网络搭建参考:手撕 CNN 经典网络之 AlexNet(理论篇)-CSDN博客
在实际工程应用中,构建并训练一个大规模的卷积神经网络是比较复杂的,需要大量的数据以及高性能的硬件。如果通过训练好的典型网络稍加改进,用少量数据进行训练并加以应用
4.1 什么是迁移学习
TransferLearning,把一个领域的知识迁移到另一个领域。基于共享参数的迁移学习研究如何找到源数据和目标数据的空间模型之间的共同参数或先验分布。通过修改一个通过完整训练的深度卷积神经网络模型最后几层连接层,再使用针对特定问题而建立的小数据集进行训练,使其能够适用于一个新的问题。
4.2 AlexNet
该模型主要由卷积层、池化层和全连接层组成,并引入了一些被广泛应用的特性和技巧。AlexNet包含一个输入层,一个输出层,一个卷积层,3个下采样(池化)层,2个全连接层。其中,从输入层到卷积层1开始,之后的每一层都被分成两个相同的结构进行计算,因为AlexNet将计算平均分配到了两块GPU进行。具体的架构细节,在下面给出
4.3 基于AlexNet实现迁移学习
Matlab有自带的封装好的工具箱可以使用,但是问题来了,作为几年前免费版的使用者,我的MATLAB不允许我进行下载,这该怎么办?花钱是不可能的,用工具箱写一个得了,强化下理解。我们上面说过, AlexNet 有 8 层权重层,包括 5 层卷积层和 3 层全连接层(FC 层),并引入了一些重要的创新,包括激活函数、Dropout 正则化和重叠池化。它通过增加网络的深度和宽度,结合 GPU 加速,极大提升了 CNN 的能力。当我们训练好了类似的模型,就可以对尾部的几层进行替换。
4.4 AlexNet的搭建
4.4.1 卷积层(C1)
该层的处理流程是:卷积-->ReLU-->局部响应归一化(LRN)-->池化。
卷积:输入是227x227x3,使用96个11x11x3的卷积核进行卷积,padding=0,stride=4,根据公式:(input_size + 2 * padding - kernel_size) / stride + 1=(227+2*0-11)/4+1=55,得到输出是55x55x96。 尝试使用LeNet的红绿灯数据进行训练,但是量大,怕GPU跑坏了,所以尝试改进使用手写数字进行识别。
ReLU:将卷积层输出的FeatureMap输入到ReLU函数中。
局部响应归一化:局部响应归一化层简称LRN,是在深度学习中提高准确度的技术方法。一般是在激活、池化后进行。LRN对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。归一化的模型如下:
a为归一化之前的神经元,b为归一化之后的神经元;N是卷积核的个数,也就是生成的FeatureMap的个数;k,α,β,n是超参数,论文中使用的值是k=2,n=5,α=0.0001,β=0.75。
我们根据架构搭建出这个模型,包括5层卷积层,添加为3个全连接层,添加了Softmax层和分类层
池化:使用3x3,stride=2的池化单元进行最大池化操作(max pooling)。注意这里使用的是重叠池化,即stride小于池化单元的边长。根据公式:(55+2*0-3)/2+1=27,每组得到的输出为27x27x48。
4.4.2 卷积层(C2)
该层的处理流程是:卷积-->ReLU-->局部响应归一化(LRN)-->池化。
卷积:两组输入均是27x27x48,各组分别使用128个5x5x48的卷积核进行卷积,padding=2,stride=1,根据公式:(input_size + 2 * padding - kernel_size) / stride + 1=(27+2*2-5)/1+1=27,得到每组输出是27x27x128。
ReLU:将卷积层输出的FeatureMap输入到ReLU函数中。
局部响应归一化:使用参数k=2,n=5,α=0.0001,β=0.75进行归一化。每组输出仍然是27x27x128。
池化:使用3x3,stride=2的池化单元进行最大池化操作(max pooling)。注意这里使用的是重叠池化,即stride小于池化单元的边长。根据公式:(27+2*0-3)/2+1=13,每组得到的输出为13x13x128。
4.4.3 卷积层(C3)
该层的处理流程是: 卷积-->ReLU
卷积:输入是13x13x256,使用384个3x3x256的卷积核进行卷积,padding=1,stride=1,根据公式:(input_size + 2 * padding - kernel_size) / stride + 1=(13+2*1-3)/1+1=13,得到输出是13x13x384。
ReLU:将卷积层输出的FeatureMap输入到ReLU函数中。将输出其分成两组,每组FeatureMap大小是13x13x192,分别位于单个GPU上。
4.4.4 卷积层(C4)
该层的处理流程是:卷积-->ReLU
卷积:两组输入均是13x13x192,各组分别使用192个3x3x192的卷积核进行卷积,padding=1,stride=1,根据公式:(input_size + 2 * padding - kernel_size) / stride + 1=(13+2*1-3)/1+1=13,得到每组FeatureMap输出是13x13x192。
ReLU:将卷积层输出的FeatureMap输入到ReLU函数中。
4.4.5 卷积层(C5)
该层的处理流程是:卷积-->ReLU-->池化
卷积:两组输入均是13x13x192,各组分别使用128个3x3x192的卷积核进行卷积,padding=1,stride=1,根据公式:(input_size + 2 * padding - kernel_size) / stride + 1=(13+2*1-3)/1+1=13,得到每组FeatureMap输出是13x13x128。
ReLU:将卷积层输出的FeatureMap输入到ReLU函数中。
池化:使用3x3,stride=2的池化单元进行最大池化操作(max pooling)。注意这里使用的是重叠池化,即stride小于池化单元的边长。根据公式:(13+2*0-3)/2+1=6,每组得到的输出为6x6x128。
4.4.6 全连接层(FC6)
该层的流程为:(卷积)全连接 -->ReLU -->Dropout (卷积)
全连接:输入为6×6×256,使用4096个6×6×256的卷积核进行卷积,由于卷积核尺寸与输入的尺寸完全相同,即卷积核中的每个系数只与输入尺寸的一个像素值相乘一一对应,根据公式:(input_size + 2 * padding - kernel_size) / stride + 1=(6+2*0-6)/1+1=1,得到输出是1x1x4096。既有4096个神经元,该层被称为全连接层。
ReLU:这4096个神经元的运算结果通过ReLU激活函数中。
Dropout:随机的断开全连接层某些神经元的连接,通过不激活某些神经元的方式防止过拟合。4096个神经元也被均分到两块GPU上进行运算。
4.4.7 全连接层(FC7)
该层的流程为:(卷积)全连接 -->ReLU -->Dropout
全连接:输入为4096个神经元,输出也是4096个神经元(作者设定的)。
ReLU:这4096个神经元的运算结果通过ReLU激活函数中。
Dropout:随机的断开全连接层某些神经元的连接,通过不激活某些神经元的方式防止过拟合。
4096个神经元也被均分到两块GPU上进行运算。
4.4.8 输出层(Output layer)
该层的流程为:(卷积)全连接 -->Softmax
全连接:输入为4096个神经元,输出是1000个神经元。这1000个神经元即对应1000个检测类别。
Softmax:这1000个神经元的运算结果通过Softmax函数中,输出1000个类别对应的预测概率值。
设计出来的C1池化后出来的不太一样,现在没有确定其影响
4.5 训练效果
训练的时候需要注意,最后的全连接层是需要设我们需要的输出的
下面给出MATLAB代码
%% 步骤1:加载交通灯数据样本
imds = imageDatastore('Traffic Light Samples', ...
'IncludeSubfolders', true, ...
'LabelSource', 'foldernames');
% 将数据集划分为 70% 训练集和 30% 验证集
[imdsTrain, imdsValidation] = splitEachLabel(imds, 0.7);
% 图像大小调整到 AlexNet 的输入尺寸
inputSize = [227 227 3];
augimdsTrain = augmentedImageDatastore(inputSize, imdsTrain, ...
'OutputSizeMode', 'resize');
augimdsValidation = augmentedImageDatastore(inputSize, imdsValidation, ...
'OutputSizeMode', 'resize');
%% 步骤2:定义 AlexNet 风格的架构并加入局部响应归一化(LRN)
layers = [
imageInputLayer([227 227 3], 'Name', 'input', 'Normalization', 'rescale-zero-one') % 将输入归一化到 [0, 1]
% 第一卷积层 + 局部响应归一化 + ReLU + 最大池化
convolution2dLayer(11, 96, 'Stride', 4, 'Padding', 'same', 'Name', 'conv1')
crossChannelNormalizationLayer(5, 'Name', 'lrn1') % LRN 层,邻域深度为 5
reluLayer('Name', 'relu1')
maxPooling2dLayer(3, 'Stride', 2, 'Name', 'pool1')
% 第二卷积层 + 局部响应归一化 + ReLU + 最大池化
convolution2dLayer(5, 256, 'Padding', 'same', 'Name', 'conv2')
crossChannelNormalizationLayer(5, 'Name', 'lrn2') % LRN 层
reluLayer('Name', 'relu2')
maxPooling2dLayer(3, 'Stride', 2, 'Name', 'pool2')
% 第三卷积层 + ReLU
convolution2dLayer(3, 384, 'Padding', 'same', 'Name', 'conv3')
reluLayer('Name', 'relu3')
% 第四卷积层 + ReLU
convolution2dLayer(3, 384, 'Padding', 'same', 'Name', 'conv4')
reluLayer('Name', 'relu4')
% 第五卷积层 + 最大池化
convolution2dLayer(3, 256, 'Padding', 'same', 'Name', 'conv5')
reluLayer('Name', 'relu5')
maxPooling2dLayer(3, 'Stride', 2, 'Name', 'pool5')
% 全连接层
fullyConnectedLayer(4096, 'Name', 'fc6')
reluLayer('Name', 'relu6')
dropoutLayer(0.5, 'Name', 'dropout1')
fullyConnectedLayer(4096, 'Name', 'fc7')
reluLayer('Name', 'relu7')
dropoutLayer(0.5, 'Name', 'dropout2')
fullyConnectedLayer(numel(categories(imdsTrain.Labels)), 'Name', 'fc8')
softmaxLayer('Name', 'softmax')
classificationLayer('Name', 'output')];
%% 步骤3:配置训练选项
options = trainingOptions('sgdm', ...
'MiniBatchSize', 15, ...
'MaxEpochs', 5, ...
'InitialLearnRate', 0.001, ...
'LearnRateSchedule', 'piecewise', ...
'LearnRateDropFactor', 0.1, ...
'LearnRateDropPeriod', 20, ...
'Shuffle', 'every-epoch', ...
'ValidationData', augimdsValidation, ...
'ValidationFrequency', 3, ...
'Plots', 'training-progress', ...
'ExecutionEnvironment', 'auto', ...
'Verbose', true);
%% 步骤4:训练网络
net = trainNetwork(augimdsTrain, layers, options);
%% 步骤5:测试与评估
YPredicted = classify(net, augimdsValidation); % 使用网络对验证集进行预测
accuracy = mean(YPredicted == imdsValidation.Labels); % 计算分类准确率
disp(['Validation Accuracy: ', num2str(accuracy)]);
当然这个结构有点庞大,所以运行需要比较长的时间和硬件资源,所以我们也可以进行删减,迁移学习得到的效果如下: