C++ 中的卷積神經(jīng)網(wǎng)絡(luò) (CNN)
點擊上方“小白學(xué)視覺”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時間送達

有很多卷積神經(jīng)網(wǎng)絡(luò)文章解釋了 CNN 是什么以及它的用途是什么,而本文將用 C++ 編寫一個 CNN 和一個名為 mlpack 的庫來對MNIST數(shù)據(jù)集進行分類。
你們可能會問為什么 C++ 在 Python 中很容易使用大量庫,你們現(xiàn)在可能已經(jīng)看到一些特斯拉汽車,這些類型的系統(tǒng)需要從它們的環(huán)境中進行實時推理,而 Python 非常適合原型設(shè)計,但不提供實時當(dāng)使用它部署如此龐大的模型時會更新。
它是一個用 C++ 編寫的機器學(xué)習(xí)庫,它利用其他一些底層庫來提供快速且可擴展的尖端機器學(xué)習(xí)和深度學(xué)習(xí)方法。
我們要使用的數(shù)據(jù)包含在一個 CSV 文件中,由 0 到 9 的數(shù)字圖像組成,其中列包含標(biāo)簽,行包含特征,但是當(dāng)我們要將數(shù)據(jù)加載到矩陣中時,數(shù)據(jù)將被轉(zhuǎn)置,并且提到哪個特征的標(biāo)簽也將被加載,所以我們需要注意這一點。
using namespace mlpack;using namespace mlpack::ann;// Namespace for the armadillo library(linear algebra library).using namespace arma;using namespace std;// Namespace for ensmallen.using namespace ens;
然后我們將聲明一個輔助函數(shù)將模型輸出轉(zhuǎn)換為行矩陣,以匹配我們加載的行矩陣形式的標(biāo)簽。
arma::Row<size_t> getLabels(arma::mat predOut){arma::Row<size_t> predLabels(predOut.n_cols);for(arma::uword i = 0; i < predOut.n_cols; ++i){predLabels(i) = predOut.col(i).index_mat() + 1;}return predLabels;}
在這一部分下面,代碼將出現(xiàn)在main函數(shù)中,但它的編寫并不是為了讓代碼易于解釋?,F(xiàn)在我們將聲明一些我們需要的明顯訓(xùn)練參數(shù),將解釋那些突出的參數(shù)。
constexpr double RATIO = 0.1; // ratio to divide the data in train and val set.constexpr int MAX_ITERATIONS = 0; // set to zero to allow infinite iterations.constexpr double STEP_SIZE = 1.2e-3;// step size for Adam optimizer.constexpr int BATCH_SIZE = 50;constexpr size_t EPOCH = 2;mat tempDataset;data::Load("train.csv", tempDataset, true);mat tempTest;data::Load("test.csv", test, true);
參數(shù) MAX_ITERATIONS 設(shè)置為 0,因為這允許我們在一個 epoch 中無限迭代,以便在訓(xùn)練階段后期使用提前停止。作為旁注,當(dāng)此參數(shù)未設(shè)置為 0 時,也可以使用提前停止。
讓我們處理和刪除描述每一行中包含的內(nèi)容的列,如我在數(shù)據(jù)部分所述,并為訓(xùn)練、驗證和測試集的標(biāo)簽和特征創(chuàng)建一個單獨的矩陣。
= tempDataset.submat(0, 1, tempDataset.n_rows - 1, tempDataset.n_cols - 1);mat test = tempTest.submat(0, 1, tempTest.n_rows - 1, tempTest.n_cols - 1);mat train, valid;data::Split(dataset, train, valid, RATIO);const mat trainX = train.submat(1, 0, train.n_rows - 1, train.n_cols - 1);const mat validX = valid.submat(1, 0, valid.n_rows - 1, valid.n_cols - 1);const mat testX = test.submat(1, 0, test.n_rows - 1, test.n_cols - 1);const mat trainY = train.row(0) + 1;const mat validY = valid.row(0) + 1;const mat testY = test.row(0) + 1;
我們將使用負對數(shù)似然損失,在 mlpack 庫中,它的標(biāo)簽從 1 而不是 0 開始,因此我們在標(biāo)簽中添加了 1。
現(xiàn)在讓我們看一下我們將要定義的簡單卷積架構(gòu)。

FFN<NegativeLogLikelihood<>, RandomInitialization> model;model.Add<Convolution<>>(1, // Number of input activation maps.6, // Number of output activation maps.5, // Filter width.5, // Filter height.1, // Stride along width.1, // Stride along height.0, // Padding width.0, // Padding height.28, // Input width.28 // Input height.);model.Add<ReLULayer<>>();model.Add<MaxPooling<>>(2, // Width of field.2, // Height of field.2, // Stride along width.2, // Stride along height.true);model.Add<Convolution<>>(6, // Number of input activation maps.16, // Number of output activation maps.5, // Filter width.5, // Filter height.1, // Stride along width.1, // Stride along height.0, // Padding width.0, // Padding height.12, // Input width.12 // Input height.);model.Add<ReLULayer<>>();model.Add<MaxPooling<>>(2, 2, 2, 2, true);model.Add<Linear<>>(16 * 4 * 4, 10);model.Add<LogSoftMax<>>();
其他細節(jié)的展示:
ens::Adam optimizer(STEP_SIZE, // Step size of the optimizer.BATCH_SIZE, // Batch size. Number of data points that are used in each iteration.0.9, // Exponential decay rate for the first moment estimates.0.999, // Exponential decay rate for the weighted infinity norm estimates.1e-8, // Value used to initialise the mean squared gradient parameter.MAX_ITERATIONS, // Max number of iterations.1e-8, // Tolerance.true);model.Train(trainX,trainY,optimizer,ens::PrintLoss(),ens::ProgressBar(),ens::EarlyStopAtMinLoss(EPOCH),ens::EarlyStopAtMinLoss([&](const arma::mat& /* param */){double validationLoss = model.Evaluate(validX, validY);std::cout << "Validation loss: " << validationLoss<< "." << std::endl;return validationLoss;}));
正如你們可以看到在驗證準(zhǔn)確性上使用 EarlyStopAtMinLoss,這就是將參數(shù) MAX_ITERATIONS 設(shè)置為 0 以讓我們定義無限迭代的原因。
mat predOut;model.Predict(trainX, predOut);arma::Row<size_t> predLabels = getLabels(predOut);double trainAccuracy = arma::accu(predLabels == trainY) / ( double )trainY.n_elem * 100;model.Predict(validX, predOut);predLabels = getLabels(predOut);double validAccuracy = arma::accu(predLabels == validY) / ( double )validY.n_elem * 100;std::cout << "Accuracy: train = " << trainAccuracy << "%,"<< "\t valid = " << validAccuracy << "%" << std::endl;mat testPredOut;model.Predict(testX,testPredOut);arma::Row<size_t> testPred = getLabels(testPredOut)double testAccuracy = arma::accu(testPredOut == testY) /( double )trainY.n_elem * 100;std::cout<<"Test Accuracy = "<< testAccuracy;
本文代碼鏈接:https://github.com/Aakash-kaushik
交流群
歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學(xué)影像、GAN、算法競賽等微信群(以后會逐漸細分),請掃描下面微信號加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進入相關(guān)微信群。請勿在群內(nèi)發(fā)送廣告,否則會請出群,謝謝理解~

