matlab的旋律 发表于 2020-9-1 19:00:02

四大经典轻量级网络之二:SqueezeNet

本帖最后由 matlab的旋律 于 2020-9-1 19:14 编辑

1.前言从上世纪90年代以 LeNet为开始代表的卷积神经网络,2012年又以AlexNet为起始点爆发,在深度学习领域中成为炙手可热的网络框架,特别是在机器视觉领域更是超群绝伦。此后的ZF-Net到VGG-Nets,GoogleNet再到ResNet以及DenseNet,以追求提升识别精确率为主要目的,采用手段的主要方向包括加深网络结构和增强卷积模块功能,同时这也导致了整个用于识别的网络越来越复杂,需要的内存和算力也大大增加。SqueezeNet开辟了另外一个方向:在保证模型精度不降低的前提下,最大程度的提高运算速度。它能够在ImageNet数据集上达到近似AlexNet的效果,但是参数量相比AlexNet少了50倍,同时结合模型压缩技术 Deep Compression,SqueezeNet模型文件相比AlexNet小约510倍。SqueezeNet与几乎同一时期提出的MobileNet、ShuffleNet和Xception被称为当前的四大轻量级模型,但SqueezeNet是最早在arXiv上公开的。2.网络结构SqueezeNet创新点在于提出了fire module,包括两个部分,squeeze和expand,如下图所示。其中的squeeze 在 SqueezeNet的结构中表示一个 squeeze 层,该层采用 1*1 卷积核对上一层 feature map进行卷积,其主要目的是降低feature map 的维数。expand使用的Inception结构,包括1*1和3*3卷积,然后拼接,Matlab实现的代码如下:lgraph = layerGraph();
tempLayers = [
    convolution2dLayer(,16,"Name","fire2-squeeze1x1")
    reluLayer("Name","fire2-relu_squeeze1x1")];
lgraph = addLayers(lgraph,tempLayers);
tempLayers = [
    convolution2dLayer(,64,"Name","fire2-expand3x3","Padding",)
    reluLayer("Name","fire2-relu_expand3x3")];
lgraph = addLayers(lgraph,tempLayers);

tempLayers = [
    convolution2dLayer(,64,"Name","fire2-expand1x1")
    reluLayer("Name","fire2-relu_expand1x1")];
lgraph = addLayers(lgraph,tempLayers);

tempLayers = [
depthConcatenationLayer(2,"Name","fire2-concat")];

lgraph = addLayers(lgraph,tempLayers);
lgraph = connectLayers(lgraph,"fire2-relu_squeeze1x1","fire2-expand3x3");
lgraph = connectLayers(lgraph,"fire2-relu_squeeze1x1","fire2-expand1x1");
lgraph = connectLayers(lgraph,"fire2-relu_expand1x1","fire2-concat/in1");
lgraph = connectLayers(lgraph,"fire2-relu_expand3x3","fire2-concat/in2");
plot(lgraph)%绘图
得到的流程图如下图图所示:由于两个卷积过程使用的stride都是1,因此输出的数据结构仅仅在于通道数上的差异,拼接后的数据是1*1和3*3卷积后的数据在通道上之和。其中所有的1*1和3*3卷积使用的padding方式分别为和。
整个SqueezeNet的结构使用Matlab中plot函数对深度学习网络结构的表示如下图所示:从图中可以看出SqueezeNet中一共包含了8个fire module,其中在第3和第5个fire module使用了3*3的最大池化,对应池化层的stride都为2*2,将进行下采样的池化层下移动的原因是作者认为较大的Feature Map含有更多的信息。当然这样的操作虽然会提升网络的精度,但同时也带来一个问题,也就是会导致网络的计算量增加。3.模型迁移在深度学习行业内一个普遍存在的问题就是在面对某一领域的某一特定问题时,找到足够充分的训练数据非常困难。因此,如果能够使用其他数据集训练得到的成熟模型,经过一定的修改和完善,然后可以复用在与之类似的领域,这样就可以大大的缓解数据源不足导致的问题。当前,能够解决这个问题的一个关键技术就是迁移学习。迁移学习主要过程包括:1)      使用现成的数据集训练好模型,保存为预训练模型;2)      在预训练模型中定位输出为可复用特征(feature)的层次(layer);3)      将可复用特征的层次的输出作为新模型的输入,进行重新训练。其中,这里进行预训练的数据集和迁移学习的数据集可以对应完全不同的待解问题,例如样本的尺寸相同,样本标签数目却不一样等。由于此前的预训练模型已经学习得到了数据的组织模式,因此进行迁移学习的网络只需要学习数据集中针对某一特定问题的相关性问题就可以了。简而言之,迁移学习具有的优势可以总结为:1)      适应小样本,迁移学习能够将大样本数据进行训练的模型迁移到只有小样本的数据领域,实现小样本数据的落地。2)      提高鲁棒性,通过迁移学习所得到的模型具有普适性,可以迁移到多个不同的领域而不至于产生显著的性能下降。3)      个性化定制,对应每个问题的个性化样本规模可能不大,但是在通过大数据训练的模型基础上进行迁移学习,就能很好的解决个性化的问题,也可以形象的比如为站在巨人的肩膀上。
下面使用小迈步视频课程上的一个例子,对SqueezeNet进行迁移学习用于Cap、Cube、MWBottle、NFSQ、PlayingCards、Screwdriver和Torch七大类物品的识别。对应的迁移学习步骤和代码如下:%加载Squeezenet模型并修改
net = squeezenet();

lgraph = layerGraph(net);
inputSize = net.Layers(1).InputSize;
numClasses = numel(categories(imdsTrain.Labels));

% 删除squeezenet的最后5层   
lgraph = removeLayers(lgraph, {'conv10', 'relu_conv10', ...
'pool10', 'prob', 'ClassificationLayer_predictions'});

%定义一个卷积核大小为2的卷积层         
conv10 = convolution2dLayer( ...
                1, numClasses, ...
                'Stride', 1, ...
                'Padding', 0, ...
                'Name', 'conv10');

relu_conv10 = reluLayer('Name', 'relu_conv10');

pool10 = averagePooling2dLayer( 14, 'Stride', 1, 'Padding', 0, 'Name', 'pool10');

%其中新网络的最后5层如下:
newLayers = [
                conv10
                relu_conv10
                pool10
                softmaxLayer('Name', 'softmax')
                classificationLayer('Name', 'classoutput')];

% 添加并连接到网络的最后一层 'drop9'层
lgraph = addLayers(lgraph, newLayers);
lgraph = connectLayers(lgraph, 'drop9', 'conv10');
4.实验结果
对上述迁移学习的模型进行训练,其中具体训练的参数设置可以点击阅读原文参看对应的Matlab源码。其中训练过程详细图示如下图。可以看出经过20轮训练集和验证集都达到了100%的准确率,损失函数值也收敛到了近0值,另外使用的时间仅为42秒。如下图所示,通过对测试集4张图片的识别其结果也完全正确,这些也说明轻量级网络SqueezeNet在迁移学习上高效性,以及部署到性能较低的单片机上的可行性。参考文献:博客:https://blog.csdn.net/cumttzh/article/details/79822766视频:https://www.bilibili.com/video/BV1Kt4y1D7H6?p=3论文:SQUEEZENET: ALEXNET-LEVEL ACCURACYWITH 50X FEWER PARAMETERS AND <0.5MB MODEL SIZE欢迎加入深度学习算法交流群学习交流。

matlab的旋律 发表于 2020-9-1 19:16:49

本帖最后由 matlab的旋律 于 2020-9-1 19:20 编辑

用于迁移学习测试的实例程序
%准备训练及测试用图片集
%载入图库
imds = imageDatastore('TrainData',...
    'IncludeSubfolders',true,...
    'LabelSource','foldernames');
= splitEachLabel(imds,0.7,'randomized');

%加载Squeezenet模型并修改
net = squeezenet();

lgraph = layerGraph(net);
inputSize = net.Layers(1).InputSize;
numClasses = numel(categories(imdsTrain.Labels));

% Remove last 5 layers   
lgraph = removeLayers(lgraph, {'conv10', 'relu_conv10', ...
'pool10', 'prob', 'ClassificationLayer_predictions'});

% define a conv layer with a filter size of 2         
conv10 = convolution2dLayer( ...
                1, numClasses, ...
                'Stride', 1, ...
                'Padding', 0, ...
                'Name', 'conv10');

relu_conv10 = reluLayer( ...
                'Name', 'relu_conv10');

pool10 = averagePooling2dLayer( ...
                14, ...
                'Stride', 1, ...
                'Padding', 0, ...
                'Name', 'pool10');

% Last 5 layers of the new network would be
newLayers = [
                conv10
                relu_conv10
                pool10
                softmaxLayer('Name', 'softmax')
                classificationLayer('Name', 'classoutput')];

% Add and connect to the last layer of the network 'drop9'
lgraph = addLayers(lgraph, newLayers);
lgraph = connectLayers(lgraph, 'drop9', 'conv10');
plot(lgraph)

%调整图库
pixelRange = [-30 30];
scaleRange = ;
imageAugmenter = imageDataAugmenter( ...
    'RandXReflection',true, ...
    'RandXTranslation',pixelRange, ...
    'RandYTranslation',pixelRange, ...
    'RandXScale',scaleRange, ...
    'RandYScale',scaleRange);
augimdsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain,...
    'DataAugmentation',imageAugmenter);

augimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);

%进行模型训练
options = trainingOptions('sgdm',...
    'ExecutionEnvironment','gpu',...
    'MiniBatchSize',10,...
    'MaxEpochs',20,...
    'InitialLearnRate',0.0001,...
    'ValidationData',augimdsValidation,...
    'ValidationFrequency',3,...
    'ValidationPatience',Inf,...
    'Verbose',false,...
    'Plots','training-progress',...
    'ExecutionEnvironment','gpu');    % 此处为GPU训练,可改为CPU或PCT

net = trainNetwork(augimdsTrain,lgraph,options);

%测试训练好的模型

= classify(net,augimdsValidation);
idx = randperm(numel(imdsValidation.Files),4);
figure
for i = 1:4
    subplot(2,2,i)
    I = readimage(imdsValidation,idx(i));
    imshow(I)
    label = YPred(idx(i));
    title(string(label));
end

matlab的旋律 发表于 2023-10-31 02:08:33

本帖最后由 matlab的旋律 于 2023-10-31 03:18 编辑

上述迁移学习方法是在预训练模型上根据具体需求修改最后几个层(最常见的是修最后几个全连接层和最后的输出层),然后在预训练模型参数基础重新训练模型。这种在预训练模型基础不冻结参数进行微调的迁移学习方法,具有一下四方面的优势:
[*]更快的收敛速度:不冻结层参数的迁移学习可以从已经学习到的特征表示中受益。因此,模型可以更快地从新数据中学习到更好的特征表示,并更快地收敛。
[*]避免信息丢失:冻结层参数的迁移学习在重新训练时会重新初始化网络的一部分或全部参数,这可能导致已经学习到的特征表示信息丢失。不冻结层参数的迁移学习可以避免这种信息丢失,在新任务中保留并利用已经学到的特征表示。
[*]更好的泛化能力:由于不冻结层参数的迁移学习可以使用已经学习到的特征表示,因此可以更好地泛化到新任务中。已经学习到的特征表示可以捕捉到底层数据中的一般模式,从而使得迁移学习在新任务中更加有效。
[*]更好的适应新数据的能力:不冻结层参数的迁移学习可以更好地适应新数据。对于特定任务,底层的特征提取器可能需要进行微调才能更好地适应新数据的特点,而不冻结参数的迁移学习可以灵活地调整权重。总之,不冻结层参数的迁移学习可以更快地学习新任务,避免信息丢失,具有更好的泛化能力,并且更好地适应新数据。但是也存在一下几方面的不足:

[*]过拟合风险:如果新任务与原任务的数据分布差异较大,不冻结层参数的迁移学习可能会增加过拟合的风险。因为底层的特征提取器可能会过度拟合原任务的数据,而无法有效地适应新任务的数据。
[*]学习能力受限:底层的特征提取器可能已经学习到了某些数据特征的表示,而这些特征对于新任务不一定是最优的。不冻结层参数的迁移学习无法再重新学习这些特征,而可能只是微调特征提取器的权重,从而限制了模型的学习能力。
[*]模型顶层不适用:如果底层的特征提取器与新任务的特征表示需求有较大差异,不冻结层参数的迁移学习可能无法将底层的特征表示有效地应用于新任务,导致模型顶层无法适应新任务的特征需求。
[*]训练时间较长:不冻结层参数的迁移学习需要同时更新底层特征提取器和顶层分类器/回归器的参数,因此可能需要更长的训练时间。这对于大规模数据集或计算资源有限的情况下可能会带来挑战。
       总得来说,不冻结层参数的迁移学习可能增加过拟合的风险,有限制模型学习能力的问题,并且可能无法适应新任务的特征需求。此外,训练时间也可能较长。为了避免过拟合、保持特征提取能力、保持预训练模型的适应性和提高训练效率,同时在迁移学习中可以更好地利用预训练模型的知识,针对新任务进行微调,一种方法是冻结预训练模型中部分层参数。还是以squeezenet为例给出matlab程序:%准备训练及测试用图片集
%载入图库
imds = imageDatastore('TrainData',...
    'IncludeSubfolders',true,...
    'LabelSource','foldernames');
= splitEachLabel(imds,0.7,'randomized');

%加载Squeezenet模型并修改
net = squeezenet();

lgraph = layerGraph(net);
inputSize = net.Layers(1).InputSize;
numClasses = numel(categories(imdsTrain.Labels));

% Remove last 5 layers   
lgraph = removeLayers(lgraph, {'conv10', 'relu_conv10', ...
'pool10', 'prob', 'ClassificationLayer_predictions'});

% define a conv layer with a filter size of 2         
conv10 = convolution2dLayer( ...
                1, numClasses, ...
                'Stride', 1, ...
                'Padding', 0, ...
                'Name', 'conv10');

relu_conv10 = reluLayer( ...
                'Name', 'relu_conv10');

pool10 = averagePooling2dLayer( ...
                14, ...
                'Stride', 1, ...
                'Padding', 0, ...
                'Name', 'pool10');

% Last 5 layers of the new network would be
newLayers = [
                conv10
                relu_conv10
                pool10
                softmaxLayer('Name', 'softmax')
                classificationLayer('Name', 'classoutput')];

% Add and connect to the last layer of the network 'drop9'
lgraph = addLayers(lgraph, newLayers);
lgraph = connectLayers(lgraph, 'drop9', 'conv10');

%由于lgraph声明为layerGraph类,而layerGraph的属性Layers为只读类型,因此需要使用replaceLayer实现对应冻结层的参数替换
Layers = freezeWeights(lgraph.Layers(1:16));%freezeWeights函数将对应层的学习率设置为0达到冻结该层参数的目的
for k = 1:16
    lgraph = replaceLayer(lgraph,lgraph.Layers(k).Name,Layers(k));
end
%由于冻结层的梯度不需要进行计算,从而在训练过程中冻结层的参数并不会更新,因此能够加快网络训练

plot(lgraph)

%调整图库
pixelRange = [-30 30];
scaleRange = ;
imageAugmenter = imageDataAugmenter( ...
    'RandXReflection',true, ...
    'RandXTranslation',pixelRange, ...
    'RandYTranslation',pixelRange, ...
    'RandXScale',scaleRange, ...
    'RandYScale',scaleRange);
augimdsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain,...
    'DataAugmentation',imageAugmenter);

augimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);

%进行模型训练
options = trainingOptions('sgdm',...
    'ExecutionEnvironment','gpu',...
    'MiniBatchSize',10,...
    'MaxEpochs',20,...
    'InitialLearnRate',0.0001,...
    'ValidationData',augimdsValidation,...
    'ValidationFrequency',3,...
    'ValidationPatience',Inf,...
    'Verbose',false,...
    'Plots','training-progress',...
    'ExecutionEnvironment','gpu');    % 此处为GPU训练,可改为CPU或PCT

net = trainNetwork(augimdsTrain,lgraph,options);

%测试训练好的模型

= classify(net,augimdsValidation);
idx = randperm(numel(imdsValidation.Files),4);
figure
for i = 1:4
    subplot(2,2,i)
    I = readimage(imdsValidation,idx(i));
    imshow(I)
    label = YPred(idx(i));
    title(string(label));
end训练过程的曲线如下图:

通过对比在预训练模型基础不冻结参数进行微调的迁移学习方法的训练过程曲线,可以看出,冻结预训练模型部分参数的迁移学习方法收敛更快,鲁棒性也更强。


页: [1]
查看完整版本: 四大经典轻量级网络之二:SqueezeNet