1. 程式人生 > >C++使用matlab卷積神經網路庫MatConvNet來進行手寫數字識別

C++使用matlab卷積神經網路庫MatConvNet來進行手寫數字識別

環境:WIN10(64 bit)+VS2010(64 bit)+Matlab2015b(64 bit)


我們的目的是將MatConvNet自帶的手寫數字識別DEMO移植到一個簡單的WIN32 DEMO中使用,主要過程有以下幾個步驟:

(1)配置MatConvNet,然後將手寫數字識別DEMO編譯成c++ shared library。

(2)編寫呼叫程式碼並測試結果。

配置編譯MatConvNet以及MNIST例子

首先從上面的地址中下載MatConvNet的原始碼並解壓,然後開啟Matlab2015b,將原始碼的matlab資料夾設定為當前路徑,如下圖所示:


執行mex -setup命令,此時matlab會顯示可用的編譯器,效果如下:


然後選擇我們想用的編譯器即可Microsoft Visual C++ 2010 (C)(直接用滑鼠點),然後輸入編譯命令:vl_compilenn,執行,完成後可以看到多了一個名叫mex的資料夾,如下圖所示:


這樣就配置完成了,下面我們來編譯examples下的mnist例子,切換matlab的當前路徑到mnist資料夾,開啟cnn_mnist.m檔案,直接執行,如下圖所示:


上面其實就是在下載對應的訓練資料(資料存放在data\mnist目錄下,總共有4個檔案:t10k-images-idx3-ubyte,t10k-labels-idx1-ubyte,train-images-idx3-ubyte和train-labels-idx1-ubyte)。當然也可以把對應資料先下載下來,然後放到指定的目錄,就可以跳過這一步啦。

等待一段時間後(預設有20個epoch,可以修改cnn_mnist_init.m檔案中net.meta.trainOpts.numEpochs 的值來減少epoch數量,減少等待時間),訓練完成後,開啟data\mnist-baseline-simplenn資料夾可以看到訓練的結果如下:


然後執行下面兩條命令:

[net, ~]=cnn_mnist();
save('..\\..\\data\\mnist-baseline-simplenn\\net.mat', '-struct', 'net');

執行後可以看到mnist-baseline-simplenn目錄下多了一個net.mat檔案,後面我們需要用到imdb.mat和net.mat。

下面我們就需要編寫一個自己的test_mnist.m檔案,然後用它來生成c++ shared library。

這裡需要注意一下,vl_setupnn這個函式其實就是把matlab資料夾下的所有子資料夾的路徑加到matlab的路徑變數中,類似VS裡設定標頭檔案目錄的概念,所以為了簡便,我們直接把matlab下的.m檔案和matlab\mex下的.mex64檔案拷貝到一個資料夾裡,我直接放在D盤下的test_mnist資料夾下,並新建了一個空的檔案test_mnist.m,如下圖所示:


將下面的程式碼拷貝到test_mnist.m檔案中:

function numeric = test_mnist(img, net_path, imdb_path)
net = load(net_path);
imdb = load(imdb_path);
net.layers{1, end}.type = 'softmax';
%resize inuput img
img = imresize(img, [28 28], 'bicubic');
img = single(img);
img = bsxfun(@minus, img, imdb.images.data_mean);
res = vl_simplenn(net, img);
[~, ~, n] = size(res(end).x(1, 1, :))
tmp = 0.0;
for i = 1 : n
    res(end).x(1, 1, i)
    if tmp < res(end).x(1, 1, i)
        numeric = i - 1;
        tmp = res(end).x(1, 1, i);
    end
end

注意:vl_simplenn函式返回的是一個多維的陣列,最後一個維度儲存的是分別識別為0~9的概率值,最大概率值就是最後的結果。

現在開始編譯C++ shared library,有兩種方式,第一種是用命令的方式編譯,另外一種是用Matlab自帶的視覺化工具Library Compiler,我們採用第一種方式,輸入如下命令:

mcc -W cpplib:dnrecognize -T link:lib test_mnist

dnrecognize表示生成的library name,test_mnist指定的要編譯的matlab檔案。

編譯完成後,在當前目錄下會生成不少檔案,我們主要關注三個(dnrecognize.dll,dnrecognize.h,dnrecognize.lib):

好了,我們需要在C++中呼叫的庫已經準備好了,現在我們新建一個WIN32的工程TestMnist來測試。

配置如下:

Include header file directory:

.\test_mnist;
C:\Program Files\MATLAB\R2015b\extern\include;
C:\Program Files\MATLAB\R2015b\extern\include\win64

Library directory:

.\test_mnist;
C:\Program Files\MATLAB\R2015b\extern\lib\win64\microsoft


編寫WIN32部分的程式碼

這部分可以按自己的想法來搭建,我簡單實現了一個背景黑色的視窗,然後可以用滑鼠在上面畫數字,左鍵按下就可以手寫一個數字,右鍵擦出,中鍵識別手寫數字,介面和效果如下:

cnn_mnist函式引數傳入時,一定要注意在matlab中矩陣是按列優先儲存的,切記切記。

對本人劣質程式碼有興趣的下載地址如下:

http://download.csdn.net/detail/jieleiping/9500490

存在的問題:

(1)似乎手寫的數字8就沒正確過?不知道什麼原因。

(2)程式執行過程中,有很多的異常資訊,不知道為什麼,但是程式執行時正確的。如果有誰知道原因,請一定要告訴我,謝謝。錯誤資訊如下:

TestMnist.exe 中的 0x00007ffaf3531f28 處最可能的異常: Microsoft C++ 異常: 記憶體位置 0x154feb18 處的 std::logic_error。
TestMnist.exe 中的 0x00007ffaf3531f28 處最可能的異常: Microsoft C++ 異常: 記憶體位置 0x0b4ec570 處的 matrix::serialize::EndOfFile。
TestMnist.exe 中的 0x00007ffaf3531f28 處最可能的異常: Microsoft C++ 異常: 記憶體位置 0x0b4e7d60 處的 CryptoPP::AES_PHM_Decryption::InvalidCiphertextOrKey。
TestMnist.exe 中的 0x00007ffaf3531f28 處最可能的異常: Microsoft C++ 異常: 記憶體位置 0x0014e218 處的 fl::filesystem::PathNotFound。
TestMnist.exe 中的 0x00007ffaf3531f28 處最可能的異常: Microsoft C++ 異常: 記憶體位置 0x0014cf10 處的 xsd_binder::MalformedDocumentError。

題外話,如果需要將程式釋出出去,似乎特別的麻煩。

一開始我認為把DLL帶上就可以了(包括VS的庫,test_mnist.dll以及R2015b\runtime\win64目錄下的mclmcrrt9_0.dll),但是放到其他沒有相應Matlab環境的電腦上無法執行。一百度說是在沒有安裝Matlab的電腦上執行Matlab,需要在對應電腦上安裝MCR。

其實MATLAB\R2015b\toolbox\compiler\deploy\win64目錄下MCRInstaller.exe這個檔案就是對應的MCR安裝檔案。(實際上也可以在用matlab compiler編譯c++ shared library時指定附加MCR,這裡就不詳述了)。