ImageNet是一种数据集,而不是神经网络模型。斯坦福大学教授李飞飞为了解决机器学习中过拟合和泛化的问题而牵头构建的数据集。该数据集从2007年开始手机建立,直到2009年作为论文的形式在CVPR 2009上面发布。直到目前,该数据集仍然是深度学习领域中图像分类、检测、定位的最常用数据集之一。
基于ImageNet有一个比赛,从2010年开始举行,到2017年最后一届结束。该比赛称为ILSVRC,全称是ImageNet Large-Scale Visual Recognition Challenge,每年举办一次,每次从ImageNet数据集中抽取部分样本作为比赛的数据集。ILSVRC比赛包括:图像分类、目标定位、目标检测、视频目标检测、场景分类。在该比赛的历年优胜者中,诞生了AlexNet(2012)、VGG(2014)、GoogLeNet(2014)、ResNet(2015)等耳熟能详的深度学习网络模型。“ILSVRC”一词有时候也用来特指该比赛使用的数据集,即ImageNet的一个子集,其中最常用的是2012年的数据集,记为ILSVRC2012。因此有时候提到ImageNet,很可能是指ImageNet中用于ILSVRC2012的这个子集。ILSVRC2012数据集拥有1000个分类(这意味着面向ImageNet图片识别的神经网络的输出是1000个),每个分类约有1000张图片。这些用于训练的图片总数约为120万张,此外还有一些图片作为验证集和测试集。ILSVRC2012含有5万张图片作为验证集,10万张图片作为测试集(测试集没有标签,验证集的标签在另外的文档给出)。
图1:imagenet数据集
图2:resnet50网络结构
如图所示,resnet50网络从input->stage0->stage1->stage2->stage3->stage4->output
stage0比较简单,输入由(3,224,224)经过64个大小为(7,7)、步长为2的卷积核,此时输出通道数为64(卷积层输出的特征图数目等于卷积核的数量 )。接着经过BN层(Batch Normalization),批量归一化层是一种常用于深度神经网络中的正则化技术,BN层可以加速神经网络的训练过程,可以使得网络中的梯度在反向传播过程中更加稳定。RELU是激活函数。最后经过最大池化层,其中kernel大小为3×3,步长为2。最后输出的尺寸为224/2/2=56。
总体来说,经过stage0,形状由(3,224,224)->(64,56,56)。
训练的时候需要对输入数据进行预处理,代码如下:
input_size = 224
traindir = os.path.join(args.data, 'train')
valdir = os.path.join(args.data, 'val')
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
# torchvision.set_image_backend('accimage')
train_dataset = datasets.ImageFolder(
traindir,
transforms.Compose([
transforms.Resize(256),
transforms.RandomCrop(input_size),
transforms.RandomHorizontalFlip(),
# AutoAugment(policy=AutoAugmentPolicy.IMAGENET),
transforms.ToTensor(),
normalize,
]))
train_loader = torch.utils.data.DataLoader(
train_dataset, batch_size=args.batch_size, shuffle=True,
num_workers=args.workers, pin_memory=True)
val_loader = torch.utils.data.DataLoader(
datasets.ImageFolder(valdir, transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(input_size),
transforms.ToTensor(),
normalize,
])),
batch_size=args.batch_size, shuffle=False,
num_workers=args.workers, pin_memory=True)
其中的args.data是数据集的路径。按照input size=224进行输入。
代码如下:
criterion = nn.CrossEntropyLoss().cuda(args.gpu)
optimizer = torch.optim.SGD(model.parameters(), lr=args.lr, momentum=0.9, weight_decay=args.weight_decay)
scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[10, 20, 30, 90, 120], gamma=0.1)
初始化学习率lr=0.01,采用交叉熵损失函数。
3.2.1 量化工具MOCA(方法1)
这是一套基于mqbench的量化工具研发的,适用于光计算中的量化工具以及部署模型onnx的转换,这里的onnx模型中的算子是自定义的光计算硬件可执行的算子,适用于8/4/3/2bit的定点的量化工具。
这里需要下载moca工具,然后安装:
python setup.py develop
并修改代码:
moca/QuantForCV/mqbench/mqbench/prepare_by_platform.py
这里可是设置量化的类型配置和bit位数。
moca/QuantForCV/mqbench/application/imagenet_example/main.py
这里是在main.py中设置,其需要执行在omac中执行的层有哪些,omac_exec_layers是一个dict可以设置,4bit还是2bit量化。在训练的时候需要注意,先加载预训练模型。
执行下面命令,实现QAT训练。还可以引入硬件感知量化。
CUDA_VISIBLE_DEVICES=0 python -u main.py -a resnet50 --train_data /root/imagenet/
--val_data /root/imagenet/ --epochs 2 --lr 1e-4 -b 16 --backend onnx_omac
--model_path /root/.cache/torch/hub/checkpoints/MEALV2_ResNet50_224_cutmix.pth
> resent50.log 2>&1&
3.2.2 APOT算法量化(方法2)
本文首先提出了Additive Powers-of-Two(APoT)加法二次幂量化,一种针对钟形和长尾分布的神经网络权重,有效的非均匀性量化方案。通过将所有量化数值限制为几个二次幂相加,这APoT量化有利于提高计算效率,并与权重分布良好匹配。其次,本文通过参数化Clipping函数以生成更好的更新最佳饱和阈值的梯度。最后,提出对权重归一化来调整权重的输入分布,使其在量化后更加稳定和一致。实验结果表明,本文提出的方法优于最先进的方法,甚至可以与全精度模型竞争,因此证明了本文提出的APoT量化的有效性。例如,本文在 ImageNe t上的 3bit 量化 ResNet-34 仅下降了 0.3% 的 Top-1 和 0.2% Top-5 的准确性。
根据PoT的公式,bitwidth加大,只会增加零值附近的表示精度,很浪费。所以原文把数据表示成多个PoT的加法。公式如下:
例如量化一个4bit模型,b=4, k=2, n=2得到2个PoT,如下:
所以得到16个离散值如下{0.0000, 0.3333, 0.6667, 0.0833, 0.0208, 1.0000, 0.7500, 0.6875, 0.1667,0.5000, 0.2500, 0.1875, 0.0417, 0.3750, 0.1250, 0.0625}
以及对权重进行归一化操作, 权重归一化为裁剪(Clip)和投影(projection)提供了相对一致且稳定的输入分布,这便于在训练过程中更平滑地优化不同层和迭代。此外,将权重的平均值设为零可以使得量化更加对称。权重归一化公式如下,主要是通过权重值减均值除方差完成,使得归一化后的权重分布满足均值为0方差为1。
APoT量化的伪代码如下:
项目代码参考:https://github.com/yhhhli/APoT_Quantization
w = [-1, 0, 1], a采用uint
w2a2: acc@Top1=72.602% acc@Top5=90.866%
w2a3: acc@Top1=73.434% acc@Top5=91.568%
w3a3: acc@Top1=75.488% acc@Top5=92.532%