最近想在matlab环境下跑一下深度学习模型,找到了以下的一篇博客,但因为该博客所附录的资源失效,而且也可能因为版本问题导致数据集的预处理容易出bug,所以打算改成.mat格式数据上传到我的博客资源,方便下载和练习。原博客地址如下:
https://blog.csdn.net/longlongsvip/article/details/105466512
环境
Matlab: 2020b
GPU:NVIDIA Quadro P620
(注:Matlab2020b好像不支持NVIDIA安培架构显卡即不支持30系列,用3090在matlab2020b上测试过,报错找不到GPU)
MNIST手写体数据包含60000个训练样本(数字0-9),以及测试集数据10000个(数字0-9),这里不再详细叙述数据集背景,上文博客写得很详细。 我已经将数据集转换为.mat文件格式(省去上文博客中的复杂预处理步骤),其数据格式如下:
由上图可以看出,图片是以一维的形式存储的,其数据维度为1 x 784,即原28 x 28 维度的图片压缩成了一维,所以在使用的时候需要将图片恢复为二维形式(代码下面给出)。
打开matlab APP 的Deep Network Designer
打开后如下图所示:(可以选择好预训练的网络,也可以新建空白网络)
点击空白网络,根据LetNet5[1]的网络结构搭建网络(拖拽搭建)
搭建完成后点击分析,没问题后就可以导出(导出-生成代码)网络了
很明显,以上步骤是非必要的,老手可以直接写层代码来实现…
layers = [
imageInputLayer([28 28 1],"Name","imageinput")
convolution2dLayer([5 5],6,"Name","conv1","Padding","same")
tanhLayer("Name","tanh1")
maxPooling2dLayer([2 2],"Name","maxpool1","Stride",[2 2])
convolution2dLayer([5 5],16,"Name","conv2")
tanhLayer("Name","tanh2")
maxPooling2dLayer([2 2],"Name","maxpool","Stride",[2 2])
fullyConnectedLayer(120,"Name","fc1")
fullyConnectedLayer(84,"Name","fc2")
fullyConnectedLayer(10,"Name","fc")
softmaxLayer("Name","softmax")
classificationLayer("Name","classoutput")];
在进行实现深度网络前,可以先用传统机器学习的方法进行对比,有助于更好理解深度学习网络架构的优越性。
SVM作为机器学习的老大哥,在小样本,小分类数的情况下一直表现优异,但数据量庞大和分类数量比较多的情况下不一定合适。SVM在本例的评价如下:
运行时间:1星 (不管是训练还是测试都非常慢)
准确率: 1星
用到的SVM分类器为libsvm工具箱,其调用代码如下:
load handwriting.mat %载入数据集
model=svmtrain(y_train,x_train,'-c 2 -g 0.10'); %训练
[predicted_label]=svmpredict(y_test,x_test,model); %测试
相对于SVM,ANN人工神经网络更适合于处理数据量庞大的情况,但是相对与本例的LetNet-5而言,ANN忽略了输入图片的空间信息,即输入的数据是一维训练数据。设计的ANN网络结构如下:
这里我用了3层隐藏层的ANN网络,其中第一层有100个神经元,第二层60个神经元,第三层40个神经元,第四层10个神经元。ANN在本例的表现评价如下:
运行时间:2星
准确率: 3星(Accuracy:95%)
ANN的实现用matlab自带的ANN工具箱,其调用代码如下:
load handwriting.mat %载入手写体数字数据集
for i=1:size(y_train)
if y_train(i)==0
y_train(i)=10; %这里把数字0标签换成10,不然出bug
end
end
for j=1:size(y_test)
if y_test(j)==0
y_test(j)=10; %这里把数字0标签换成10,不然出bug
end
end
class=y_train;
[input,minI,maxI]=premnmx(x_train');
s = length( class) ;
output = zeros( s , 2 ) ;%构造输出矩阵
for i = 1 : s
output( i , class( i ) ) = 1 ;
end
%% 网络参数
net = newff( minmax(input) , [100 60 40 10] , { 'logsig' 'logsig' 'logsig' 'purelin' } , 'traingdx' ) ; %创建神经网络
%激活函数有'tansig' 'logsig'以及'purelin'三种
net.trainparam.show = 50 ; %显示中间结果的周期
net.trainparam.epochs = 7000 ; %最大迭代次数(学习次数)
net.trainparam.goal = 0.01 ; %神经网络训练的目标误差
net.trainParam.lr = 0.001 ; %学习速率(Learning rate)
%% 开始训练
net = train( net, input , output' ) ; %其中input为训练集的输入信号,对应output为训练集的输出结果
%% GPU训练
% gpudev=gpuDevice;%事先声明gpudev变量为gpu设备类
% gpudev.AvailableMemory;%实时获得当前gpu的可用内存
% input=single(input);%将double型的P转为single型
% output=single(output);%将double型的T转为single型
% net = train( net, input , output' , 'useGPU','only' ) ; %GPU
%% 测试
tic
testInput=tramnmx(x_test',minI,maxI);
Y=sim(net,testInput);
[s1 , s2] = size( Y ) ; %统计识别正确率
hitNum = 0 ;
predictChar=[]; %输出结果
for i = 1 : s2
[m , Index] = max( Y( : , i ) ) ;
predictChar=[predictChar;Index];
if( Index == y_test(i) )
hitNum = hitNum + 1 ;
end
end
sprintf('识别率是 %3.3f%%',100 * hitNum / s2 )
toc
通过第二节获得层参数layers后,就可以直接将该参数用于深度学习训练,当然训练前需要进行数据格式的转换,把一维数据转换为二维图片数据。LetNet5在本例的评价如下:
运行时间:3星(GPU训练)
准确率: 4星
代码如下:
Datapre.m(一维数据转换为二维)
load handwriting.mat
% 将一维数据转为二维图像数据
%% 训练集
X=x_train;
X = permute(X,[2 1]); %交换数据维度
X = X./255; %归一化
X=reshape(X,[28,28,1,size(X,2)]);
X = dlarray(X, 'SSCB');
Y=categorical(y_train);
%% 测试集
X2=x_test;
X2 = permute(X2,[2 1]); %交换数据维度
X2 = X2./255; %归一化
X2=reshape(X2,[28,28,1,size(X2,2)]);
X2 = dlarray(X2, 'SSCB');
Y2=categorical(y_test);
%% 保存数据
save XY.mat X Y X2 Y2
CNNTrain.m(网络训练)
load XY.mat
XTrain=X; %训练集
YTrain=Y; %训练集标签
XTest=X2; %测试集
Ytest=Y2; %测试集标签
layers = [
imageInputLayer([28 28 1],"Name","imageinput")
convolution2dLayer([5 5],6,"Name","conv1","Padding","same")
tanhLayer("Name","tanh1")
maxPooling2dLayer([2 2],"Name","maxpool1","Stride",[2 2])
convolution2dLayer([5 5],16,"Name","conv2")
tanhLayer("Name","tanh2")
maxPooling2dLayer([2 2],"Name","maxpool","Stride",[2 2])
fullyConnectedLayer(120,"Name","fc1")
fullyConnectedLayer(84,"Name","fc2")
fullyConnectedLayer(10,"Name","fc")
softmaxLayer("Name","softmax")
classificationLayer("Name","classoutput")];
options = trainingOptions('sgdm', ... %优化器
'LearnRateSchedule','piecewise', ... %学习率
'LearnRateDropFactor',0.2, ...
'LearnRateDropPeriod',5, ...
'MaxEpochs',20, ... %最大学习整个数据集的次数
'MiniBatchSize',128, ... %每次学习样本数
'Plots','training-progress'); %画出整个训练过程
%训练网络
trainNet = trainNetwork(XTrain, YTrain,layers,options);
save Minist_LeNet5 trainNet %训练完后保存模型
yTest = classify(trainNet, XTest); %测试训练后的模型
accuracy = sum(yTest == Ytest)/numel(yTest); %模型在测试集的准确率
disp(accuracy) %打印测试集准确率
CNNTest.m(通过JPG图片进行测试)
load('Minist_LeNet5'); %导入训练好的LeNet5网络
test_image = imread('1.jpg'); %导入手写体数字图片
shape = size(test_image);
dimension=numel(shape);
if dimension > 2
test_image = rgb2gray(test_image); %灰度化
end
test_image = imresize(test_image, [28,28]); %保证输入为28*28
test_image = imcomplement(test_image); %反转,使得输入网络时一定要保证图片 背景是黑色,数字部分是白色
test_image=double(test_image);
test_image=test_image'; %旋转
test_image=test_image./255; %归一化
result = classify(trainNet, test_image); %利用LetNet5分类
disp(result);
测试用的是自己用windows画图工具画的10个数字,如下:
CNNTest.m运行结果:
手写体数字识别.mat数据集已经上传到我的博客资源,到我的博客资源就可以下载了。
MNIST手写体数字数据集官网
http://yann.lecun.com/exdb/mnist/
参考文献
[1] Lecun Y , Bottou L . Gradient-based learning applied to document recognition[J]. Proceedings of the IEEE, 1998, 86(11):2278-2324.