人工神經網絡(ANN) 簡稱神經網絡(NN),能模擬生物神經系統對物體所做出的交互反應,是由具備適應性的簡單單元(稱爲神經元)組成的普遍並行互連網絡。html
以下圖所示,來自其它神經元的信號,$x_1, x_2, ... , x_n $,傳遞過來做爲輸入信號,並經過帶權重 ($w_1, w_2, ... , w_n$) 的鏈接 (connection) 繼續傳遞,網絡
而後神經元的總輸入值 $\sum w_i x_i$ 與閾值 $\theta$ 做比較,最後通過激活函數$\,f\,$產生神經元的輸出: $y = f\left(\,\sum \limits_{i=1}^n {w_i x_i} - \theta \right)$機器學習
理想中,階躍函數可做爲激活函數,將輸入值映射爲輸出值 「0」 和 「1;實際中,經常使用 Sigmoid 函數做激活函數, $f(x)=\,\dfrac{1}{1+e^{-x}}$,以下圖所示:函數
OpenCV 中使用的激活函數是另外一種形式,$f(x)=\beta \,\dfrac{1-e^{-\alpha x}}{1+e^{-\alpha x}}$post
當 α = β = 1 時,$f(x)=\dfrac{1-e^{-x}}{1+e^{x}}$,該函數把可能在較大範圍內變化的輸入值,「擠壓」 到 (-1, 1) 的輸出範圍內學習
具體的設置函數以下,param1 --> α,param2 --> βui
// 設置激活函數,目前只支持 ANN_MLP::SIGMOID_SYM virtual void cv::ml::ANN_MLP::setActivationFunction(int type, double param1 = 0, double param2 = 0);
感知機由兩層神經元組成,輸入層接收外界輸入信號,而輸出層則是一個 M-P 神經元。url
實際上,感知機可視爲一個最簡單的「神經網絡」,用它可很容易的實現邏輯與、或、非等簡單運算。spa
常見的神經網絡,可分爲三層:輸入層、隱含層、輸出層。輸入層接收外界輸入,隱層和輸出層負責對信號進行加工,輸出層輸出最終的結果。.net
如下圖爲例:每層神經元與下一層神經元全互連,而同層神經元之間不鏈接,也不存在跨層鏈接,這樣的結構稱爲「多層前饋神經網絡」(multi-layer feedforward neural networks)
OpenCV 中,設置神經網絡層數和神經元個數的函數爲 setLayerSizes(InputArray _layer_sizes),則上圖對應的 InputArray 可由以下代碼來構成
// (a) 3層,輸入層神經元個數爲 4,隱層的爲 6,輸出層的爲 4 Mat layers_size = (Mat_<int>(1,3) << 4,6,4); // (b) 4層,輸入層神經元個數爲 4,第一個隱層的爲 6,第二個隱層的爲 5,輸出層的爲 4 Mat layers_size = (Mat_<int>(1,4) << 4,6,5,4);
如何設置隱層神經元的個數還是個未決的問題,實際中多采用「試錯法」來調整
static Ptr<ANN_MLP> cv::ml::ANN_MLP::create(); // 建立空模型
// 設置神經網絡的層數和神經元數量 virtual void cv::ml::ANN_MLP::setLayerSizes(InputArray _layer_sizes); // 設置激活函數,目前只支持 ANN_MLP::SIGMOID_SYM virtual void cv::ml::ANN_MLP::setActivationFunction(int type, double param1 = 0, double param2 = 0); // 設置訓練方法,默認爲 ANN_MLP::RPROP,較經常使用的是 ANN_MLP::BACKPROP // 若設爲 ANN_MLP::BACKPROP,則 param1 對應 setBackpropWeightScale()中的參數,param2 對應 setBackpropMomentumScale() 中的參數 virtual void cv::ml::ANN_MLP::setTrainMethod(int method, double param1 = 0, double param2 = 0); virtual void cv::ml::ANN_MLP::setBackpropWeightScale(double val); // 默認值爲 0.1 virtual void cv::ml::ANN_MLP::setBackpropMomentumScale(double val); // 默認值爲 0.1 // 設置迭代終止準則,默認爲 TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01) virtual void cv::ml::ANN_MLP::setTermCriteria(TermCriteria val);
// samples - 訓練樣本; layout - 訓練樣本爲 「行樣本」 ROW_SAMPLE 或 「列樣本」 COL_SAMPLE; response - 對應樣本數據的分類結果
virtual bool cv::ml::StatModel::train(InputArray samples,int layout,InputArray responses);
// samples,輸入的樣本書數據;results,輸出矩陣,默認不輸出;flags,標識,默認爲 0
virtual float cv::ml::StatModel::predict(InputArray samples, OutputArray results=noArray(),int flags=0) const;
下面是 OpenCV 3.3 中,在「支持向量機」的例程上作的修改,使用 BP 神經網絡,實現了和 SVM 相同的分類功能。
OpenCV 中的 支持向量機 (Support Vector Machine),可參見另外一篇博文 OpenCV 之 支持向量機 (一)
1 #include "opencv2/core/core.hpp" 2 #include "opencv2/imgproc/imgproc.hpp" 3 #include "opencv2/imgcodecs/imgcodecs.hpp" 4 #include "opencv2/highgui/highgui.hpp" 5 #include "opencv2/ml/ml.hpp" 6 7 using namespace cv; 8 9 int main() 10 { 11 // 512 x 512 零矩陣 12 int width = 512, height = 512; 13 Mat img = Mat::zeros(height, width, CV_8UC3); 14 15 // 訓練樣本 16 float train_data[6][2] = { { 500, 60 },{ 245, 40 },{ 480, 250 },{ 160, 380 },{400, 25},{55, 400} }; 17 float labels[6] = {0,0,0,1,0,1}; // 每一個樣本數據對應的輸出 18 Mat train_data_mat(6, 2, CV_32FC1, train_data); 19 Mat labels_mat(6, 1, CV_32FC1, labels); 20 21 // BP 模型建立和參數設置 22 Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::create(); 23 24 Mat layers_size = (Mat_<int>(1,3) << 2,6,1); // 2維點,1維輸出 25 bp->setLayerSizes(layers_size); 26 27 bp->setTrainMethod(ml::ANN_MLP::BACKPROP,0.1,0.1); 28 bp->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM); 29 bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 10000, /*FLT_EPSILON*/1e-6)); 30 31 // 保存訓練好的神經網絡參數 32 bool trained = bp->train(train_data_mat,ml::ROW_SAMPLE,labels_mat); 33 if (trained) { 34 bp->save("bp_param"); 35 } 36 37 // 建立訓練好的神經網絡 38 // Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::load("bp_param"); 39 40 // 顯示分類的結果 41 Vec3b green(0, 255, 0), blue(255, 0, 0); 42 for (auto i=0; i<img.rows; ++i) { 43 for (auto j=0; j<img.cols; ++j) { 44 Mat sample_mat = (Mat_<float>(1, 2) << j, i); 45 Mat response_mat; 46 bp->predict(sample_mat,response_mat); 47 float response = response_mat.ptr<float>(0)[0]; 48 if (response > 0.5) { 49 img.at<Vec3b>(i, j) = green; 50 } else if (response < 0.5) { 51 img.at<Vec3b>(i, j) = blue; 52 } 53 } 54 } 55 56 // 畫出訓練樣本數據 57 int thickness = -1; 58 int lineType = 8; 59 circle(img, Point(500, 60), 5, Scalar(255, 255, 255), thickness, lineType); 60 circle(img, Point(245, 40), 5, Scalar(255, 255, 255), thickness, lineType); 61 circle(img, Point(480, 250), 5, Scalar(255, 255, 255), thickness, lineType); 62 circle(img, Point(160, 380), 5, Scalar(0, 0, 255), thickness, lineType); 63 circle(img, Point(400, 25), 5, Scalar(255, 255, 255), thickness, lineType); 64 circle(img, Point(55, 400), 5, Scalar(0, 0, 255), thickness, lineType); 65 66 imwrite("result.png", img); // 保存訓練的結果 67 imshow("BP Simple Example", img); 68 69 waitKey(0); 70 }
運行結果以下所示:
注意:OpenCV 3.0 以上版本,相較以前的版本,其中有關機器學習的部分作了較大改動,本人也是踩了一些坑才獲得預期的效果。
1) 代碼 #25,必須在 setActivationFunction() 以前,不然訓練後的結果多爲 nan
2) 代碼 #46,response_mat 爲預測的結果。若輸出向量爲 1 列,則如 #47 所示,可直接取出預測結果;若輸出向量爲 n 列,則可取平均值或者最大值。
同時,根據平均值或最大值,代碼 #48 處的閾值也要相應的改變。
float response = 0; for (auto i=0;i<n;++i) { response += response_mat.ptr<float>(0)[i]; }
3) 代碼 #39,若已經訓練好神經網絡的參數,並將其保存到文件 bp_param 中。
則可將 #22 ~ #35 所有註釋掉,再反註釋掉 #38,這樣,直接加載訓練好的神經網絡,即可以使用了。
<機器學習> 周志華 第5章
<統計學習方法> 李航 第1章
OpenCV 3.0 Tutorials -- Neural Networks