機器學習----matlab中的CNN
編輯推薦: |
本文來自於CSDN,介紹了CNN分類問題以及如何用matlab自帶資料集做車輛檢測等相關知識。 |
1.前言
最近需要用到卷積神經網路(CNN),在還沒完全掌握cuda+caffe+TensorFlow+python這一套傳統的深度學習的流程的時候,想到了matlab,自己查了一下documentation,還真的有深度學習的相關函式。所以給自己提個醒,在需要用到某個成熟的技術時先查一下matlab的幫助文件,這樣會減少很多時間成本。記得機器學習的大牛Andrew NG.說過在矽谷好多人都是先用matlab/octava先實現自己的想法,再轉化成其他語言。
2.配置需求
要像用matlab實現deep learning,需要更新到2017a版本。GPU加速的話,需要安裝cuda8.0, 自己GPU 的compute capacity 要3.0 以上。
3. 可以完成的任務
我們看一下matlab的新加的深度學習功能可以完成哪些任務
1. 獲取別人訓練好的CNN網路
2. 遷移學習(transfer learning and fine-tune)
3. 解決分類問題(classifiy problem)
4. 解決迴歸問題(regression problem)
5. 物體檢測(object detection)
6. 提取學習到的特徵
3.1 獲取別人訓練好的網路
matlab2017中,可以用別人訓練好的現成的網路,也可以輸入caffe中的網路。目前已知的可以用的網路包括用於分類的:Alexnet, vgg16, vgg19。已經用於物體檢測的,RCNN, FastRCNN, Faster RCNN。由於最近一直研究的是分類和迴歸問題,物體檢測的CNN在過後補全。這裡只舉一個分類的例子。
Alexnet作為2012年ImageNet的冠軍,它的提出確實影響到了CV的研究熱點,人們驚奇的發現深度網路的描述能力居然這麼強,雖然背後的數學原理一直沒能得到完美的解決,但不妨礙它強大的能力,我們看看她在matlab中是如何做分類的。首先貼出程式碼:
clear;clc;close all;
%獲取alexnet
net = alexnet;
%讀照片選物體
I= imread('peppers.png');
[cropedim, rect2]=imcrop(I);
cropedim=imresize(cropedim,[227 227]);
figure,imshow(cropedim);
% 用AlexNet分類
label = classify(net, cropedim);
% 顯示結果
figure;
imshow(I);
rectangle('position',rect2,'EdgeColor','r'
,'LineWidth',2);
text(10,20,char(label),'Color','white','FontSize',20);
用matlab自帶的照片測試一下分類的準確率,得到的結果如下
bell pepper是甜椒的意思,我們發現效果還是不錯的,感興趣的同學可以多找幾張測試圖片試一下。用
命令可以看Alnexnet的網路結構,得到以下
這是一個25層的網路,每一層都對應著詳細的說明。值得關注的是有5個卷積層(convolution layer)和三個全連線層(full connection layer)。其他的vgg16和vgg19是相同的道理,不過要看清楚網路的輸入,使用vgg19時,需要改變上邊程式碼中的兩行
net = vgg19;
cropedim=imresize(cropedim,[224 224]);
剩下的部分是一樣的。當然也可以從caffe中匯入自己訓練好的網路,自己還沒有完全掌握caffe,熟悉這部分的同學可以自己實現一下。
3.2 遷移學習(transfer learning and fine-tune)
所謂遷移學習(transfer learning)就是微調(fine-tune)別人訓練好的網路中的某些引數,使得它更適合自己的資料集。遷移學習使用的情況是:幾百到幾千個訓練樣本,想快速訓練網路。網路的訓練過程就是剛開始為各個引數賦予隨機的值,採用數值的方法(一般是梯度下降法)求讓cost function 達到最小值的各個引數的取值,這些引數主要產生於各個層之間連線時候的權值。Cost function是標定好的資料與通過網路計算出的資料的差的累加。Cost function越小說明網路的效能越好。我們看看matlab中是如何用現有的網路做遷移學習的,我們舉一個手寫體識別的例子,其中matlab自己提供了訓練集和測試集。先貼出程式碼:
%% transfer learning
%讀取訓練集和測試集
digitDatasetPath = fullfile(matlabroot,'toolbox','nnet','nndemos', ...
'nndatasets','DigitDataset');
digitData = imageDatastore(digitDatasetPath, ...
'IncludeSubfolders',true,'LabelSource','foldernames');
[trainDigitData,testDigitData] = splitEachLabel
(digitData,0.5,'randomize');
%顯示前20個訓練照片
numImages = numel(trainDigitData.Files);
idx = randperm(numImages,20);
for i = 1:20
subplot(4,5,i)
I = readimage(trainDigitData, idx(i));
imshow(I)
end
% 獲取matlab自己訓練好的網路
load(fullfile(matlabroot,'examples',
'nnet','LettersClassificationNet.mat'))
% 改變輸出層的類別個數
layersTransfer = net.Layers(1:end-3);
% 顯示新的類別個數
numClasses = numel(categories(trainDigitData.Labels));
% 把最後三層替換成新的類別
layers = [...
layersTransfer
fullyConnectedLayer(numClasses,
'WeightLearnRateFactor',20,'BiasLearnRateFactor',20)
softmaxLayer
classificationLayer];
optionsTransfer = trainingOptions('sgdm',...
'MaxEpochs',5,...
'InitialLearnRate',0.0001,...
'ExecutionEnvironment','cpu');
% 訓練網路
netTransfer = trainNetwork(trainDigitData,layers,optionsTransfer);
% 顯示測試準確率
YPred = classify(netTransfer,testDigitData);
YTest = testDigitData.Labels;
accuracy = sum(YPred==YTest)/numel(YTest);
% 顯示測試結果
idx = 501:500:5000;
figure
for i = 1:numel(idx)
subplot(3,3,i)
I = readimage(testDigitData, idx(i));
label = char(YTest(idx(i)));
imshow(I)
title(label)
end
程式碼的前邊的部分是讀取matlab中自帶的資料集和測試集,把它儲存成imageDatastore格式,這種格式只需要提供圖片的路徑資訊而不用把圖片全部讀入記憶體中,因此非常適合大規模的資料集。中間部分是修改訓練好的網路中的最後三層,原網路用來識別手寫的字母和數字有36類,而現在的任務只需要識別手寫體數字,所以把它們改成10類,在訓練時使用0.0001的學習率,共計算5輪,用cpu做訓練。程式碼的最後部分是測試新訓練好的網路,因為transfer learn是在現有的網路基礎上做引數的微調,所以訓練速度很快,我們看一下訓練效果。
由於是在cpu上做的訓練,而且是transfer learning 所以訓練的過程很快,我們發現重新訓練好的網路能達到很高的準確率。我們再看gpu上的訓練
由於引數的初始化是隨機的,因此得到的結果也是隨機的,不過可以看出gpu上做訓練明顯要快很多!GPU的第一輪計算慢是因為資料要重新初始化為gpu矩陣。最後放一張效果圖:
我們發現識別的效果還是不錯的。
3.3 分類問題(classification problem)
CNN之所以能引起廣泛關注,就是在於它最初在影象分類方面取得很大的成功,後來人們發現對於其他的分類問題,CNN也有很好的效能。上邊講的遷移學習解決的也是一種分類問題,接下來的敘述也就建立在上文的基礎上。
我們這裡要解決的分類問題,就是訓練自己的分類網路。之前的遷移學習已經說明,所謂訓練就是為每層網路之間尋找使得cost function最小的權值,這些權值剛開始是按照某種分佈隨機初始化的,我們用數值的方法求cost function的最小值。一般來說,我們用神經網路建立的是一個非常複雜的模型,我們往往能難找到這個模型的最小值,但可以找到它的極小值(區域性最小值),這些極小值已經很接近我們要找到最小值。
要訓練自己的網路,我們要先建立自己的網路,並設定一定的訓練引數。我們看一下matlabs是如何完成的。
在matlab中用來建立網路的語句如下:
layers = [ ...
imageInputLayer([imsize imsize 1])
convolution2dLayer(5,150)
reluLayer
crossChannelNormalizationLayer(5,'Alpha',0.00005,
'Beta',0.75,'K',1) %Norm layer1
convolution2dLayer(3,300,'Stride',1,
'BiasLearnRateFactor',2) %Cov2 layer
reluLayer
fullyConnectedLayer(1)
softmaxLayer
classificationLayer];
直接用陣列建立網路,這個例子是建立一個9層的分類網路,包括輸入層,卷積層1,啟用函式層1,標準化層,卷積層2,啟用函式層2,全連線層,去最大值層,分類層。至於如何選擇適合自己的網路結構,我目前還沒有搞太清楚,不過,可以現在別人的網路基礎上做修改。
用來設定修改引數的語句如下:
options = trainingOptions('sgdm', ...
'MaxEpochs',15, ...
'InitialLearnRate',1e-4, ...
'MiniBatchSize',256,...
'ExecutionEnvironment','gpu');
'OutputFcn',functions);
這些引數是CNN網路的基本引數,MaxEpoch是計算的輪數,它的值越大越容易收斂,InitialLearRate是學習率,太大模型可能不會收斂,太小則收斂的太慢。MiniBatchSize是每次處理的資料的個數,ExcutionEnviroment是訓練網路的環境,可以在CPU(‘cpu’)上做,也可在GPU(‘gpu’)上做,可以並行(‘paralle’),預設的情況是先測試gpu,如果不可用在測試gpu。在matlab上用gpu訓練網路時需要cuda8.0, 顯示卡計算能力為3.0。這些引數可以用指令gpuDevice來檢視。OutputFcn是可以在訓練過程中呼叫的某些函式。比如:它可以用來畫cost function值的變化。如何像視覺化訓練表格(上文輸出的那些)某些資料可以呼叫相應的函式,我在迴歸問題時會再說明。
設定好網路結構和訓練引數後,可以用
來訓練自己的網路,訓練資料可以是ImageDatastore型別,可以是4-D陣列,四個維度分別是長度,寬度,通道數,第幾個圖片。因為4-D陣列是一次性裝入到記憶體中的,如果資料量太大時慎用,小心記憶體不足。同樣,我們舉一個完整的例子,也是利用matlab自帶的資料集去分類手寫體。程式碼如下:
%讀取資料集並儲存成imageDatastore形式
digitDatasetPath = fullfile(matlabroot,'toolbox','nnet','nndemos',...
'nndatasets','DigitDataset');
digitData = imageDatastore(digitDatasetPath,...
'IncludeSubfolders',true,'LabelSource','foldernames');
%隨機顯示二十個訓練集中的圖片
figure;
perm = randperm(10000,20);
for i = 1:20
subplot(4,5,i);
imshow(digitData.Files{perm(i)});
end
%把資料集劃分成訓練集和測試集
trainingNumFiles = 750;
rng(1) % For reproducibility
[trainDigitData,testDigitData] = splitEachLabel(digitData,...
trainingNumFiles,'randomize');
%建立自己的網路
layers = [imageInputLayer([28 28 1]);
convolution2dLayer(5,20);
reluLayer();
maxPooling2dLayer(2,'Stride',2);
fullyConnectedLayer(10);
softmaxLayer();
classificationLayer()];
%設定訓練引數
options = trainingOptions('sgdm','MaxEpochs',20,...
'InitialLearnRate',0.0001);
%訓練網路
convnet = trainNetwork(trainDigitData,layers,options);
%測試網路
YTest = classify(convnet,testDigitData);
TTest = testDigitData.Labels;
accuracy = sum(YTest == TTest)/numel(TTest);
disp(accuracy);
我自己的訓練結果如下:
自己是在gpu上做的,所以時間較短,最後得到分類準確率發現還不錯。
3.4 迴歸問題(regression problem)
迴歸問題與分類問題的處理方式相同,我們仍然需要訓練集和測試集。在網路結構上有些不同,最後一次必須是Regression layer, 而倒數第二次必須是卷積層。迴歸問題的網路中的引數與分類問題是一樣的,這裡不再詳細說明,我們直接分析一個例子,看一下matlab是如何做分類的。這個問題同時看一下function引數的作用。這次要解決的問題是,圖片中的字母到底旋轉了多少度。資料集同樣來自matlab程式碼如下:
%讀取資料集
[trainImages,~,trainAngles] = digitTrain4DArrayData;
%顯示任意二十個結果
numTrainImages = size(trainImages,4);
figure
idx = randperm(numTrainImages,20);
for i = 1:numel(idx)
subplot(4,5,i)
imshow(trainImages(:,:,:,idx(i)))
drawnow
end
%建立迴歸網路
layers = [ ...
imageInputLayer([28 28 1])
convolution2dLayer(12,25)
reluLayer
fullyConnectedLayer(1)
regressionLayer];
%設定訓練引數
functions={...
@plotTrainingRMSE,...
@(info)stopTrainingAtThreshold(info,0)};
options = trainingOptions('sgdm', ...
'MaxEpochs',20, ...
'InitialLearnRate',1e-3, ...
'MiniBatchSize',128,...
'ExecutionEnvironment','gpu',...
'OutputFcn',functions);
%訓練網路
net = trainNetwork(trainImages,trainAngles,
layers,options);
%測試網路
[testImages,~,testAngles] = digitTest4DArrayData;
predictedTestAngles = predict(net,testImages);
%檢視擬合誤差
predictionError = testAngles - predictedTestAngles;
thr = 10;
numCorrect = sum(abs(predictionError) < thr);
numTestImages = size(testImages,4);
accuracy = numCorrect/numTestImages;
disp('accuracy');
disp(accuracy);
squares = predictionError.^2;
rmse = sqrt(mean(squares));
disp('the rmse');
disp(rmse);
%train function
function plotTrainingRMSE(info)
persistent plotObj
if info.State == "start"
figure;
plotObj = animatedline;
xlabel("Iteration")
ylabel("Training RMSE")
elseif info.State == "iteration"
addpoints(plotObj,info.Iteration,double
(info.TrainingRMSE))
drawnow limitrate nocallbacks
end
end
function stop = stopTrainingAtThreshold(info,thr)
stop = false;
if info.State ~= "iteration"
return
end
persistent TrainingRMSE
% Append accuracy for this iteration
T= info.TrainingRMSE;
% Evaluate mean of iteration accuracy and remove oldest entry
stop = T <thr;
end
得到的迴歸結果如下:
我們在訓練過程中呼叫兩個函式,plotTrainingRMSE是用來畫cost function是如何變化的,(info)stopTrainingAtThreshold(info,0)是設定訓練提前結束的條件的,可以根據表中的某些引數讓訓練在一定條件下停下來。最後,我們看一下cost function 的變換規律,如下:
目前為止,我們用cnn解決了最基本的分類問題和迴歸問題,此外,還介紹瞭如何建立網路和設定引數,後邊將補充檢測部分。
3.3 檢測問題(Detection problem)
同樣用matlab自帶資料集做車輛檢測,關於檢測的網路有RCNN, Fast RCNN, Faster RCNN, 他們大同小異,差距在於速度的快慢,我們只測試Faster RCNN
1) 讀取資料
data是個結構體型別的資料,主要是用來四個屬性分別是detector, layers, result, vehicleTraining.
其中,detector, layers, reault是提前訓練好的檢測子,網路和測試結果,我們用vehicleTraining重新訓練CNN網路,用layers來設計網路結構
2) 抽取用於訓練的影象
trainingData = data.vehicleTrainingData;
trainingData.imageFilename=fullfile(toolboxdir
('vision'),'visiondata',...
trainingData.imageFilename);
抽取出的trainingData是table格式的,matlab訓練網路RCNN網路只能用table格式。
3) 讀取網路結構
該網路是個11層的網路,訓練時我們可以設計自己的網路結構,也可以在這個網路的基礎上做訓練。
4) 設定訓練選項
options = trainingOptions('sgdm', ...
'InitialLearnRate',1e-6,...
'MaxEpochs',1,...
'ExecutionEnvironment','gpu',...
'CheckpointPath',tempdir);
這裡設定的是初始學習率為 1e-6, 迭代1輪,用GPU做訓練,在訓練時會把checkpoint的結果存下來。
5) 訓練網路
detector = trainFasterRCNNObjectDetector(trainingData,
layers,options);
用trainingData做訓練資料,訓練layers網路,在訓練過程中選擇option中的訓練引數,同樣用了GPU做訓練,其中的一步如下:
6) 結果檢測
img=imread('highway.png');
[bbox,score,label]=detect(detector,img);
detectedImg=insertShape(img,'Rectangle',bbox);
figure,imshow(detectedImg);
從Matlab自身相簿中選擇hightway這張照片,用剛才訓練出的網路監測裡邊的車輛,其中bbox是監測出的包圍盒的座標,這個可以用來返回。
這結果顯示如下: