人工神经网络 (ANN) 简称神经网络(NN),能模拟生物神经系统对物体所作出的交互反应,是由具有适应性的简单单元(称为神经元) 组成的广泛并行互连网络。
1 神经元
1.1 M-P 神经元
如下图所示,来自其它神经元的信号,$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)$
1.2 激活函数 (activation function)
理想中,阶跃函数可作为激活函数,将输入值映射为输出值 "0" 和 "1
实际中,常用 Sigmoid 函数作激活函数, $f(x)=\beta \,\dfrac{1-e^{-\alpha x}}{1+e^{-\alpha x}}$
当 $\,\alpha = 1, \, \beta = 1\,$ 时,该函数把可能在较大范围内变化的输入值,"挤压" 到 (0, 1) 的输出范围内。
2 神经网络
2.1 感知机 (perceptron)
感知机由两层神经元组成,输入层接收外界输入信号,而输出层则是一个 M-P 神经元。
实际上,感知机可视为一个最简单的 "神经网络",用它可很容易的实现逻辑与、或、非等简单运算。
2.2 层级结构
常见的神经网络,可分为三层:输入层、隐含层、输出层。输入层接收外界输入,隐层和输出层负责对信号进行加工,输出层输出最终的结果。
以下图为例:每层神经元与下一层神经元全互连,而同层神经元之间不连接,也不存在跨层连接,这样的结构称为 "多层前馈神经网络"(multi-layer feedforward neural networks)
2.3 层数设置
OpenCV 中,设置神经网络层数和神经元个数的函数为 setLayerSizes(InputArray _layer_sizes),则上图对应的 InputArray 可由如下代码来构成
- // (a) 3层,输入层神经元个数为 4,隐层的为 6,输出层的为 4Mat layerSizes = (Mat_<int>(1,3) <<4,6,4);
- //(b) 4层,输入层神经元个数为 4,第一个隐层的为 6,第二个隐层的为 5,输出层的为 4Mat layerSizes = (Mat_<int>(1,4) <<4,6,5,4);
如何设置隐层神经元的个数仍是个未决的问题,实际中多采用 "试错法" 来调整
3 OpenCV 函数
1) 创建
- static Ptr cv: :ml: :ANN_MLP: :create(); // 创建空模型
2) 设置参数
- // 设置神经网络的层数和神经元数量
- virtual void cv::ml::ANN_MLP::setLayerSizes(InputArray _layer_sizes);
- // 设置激活函数,目前只支持 ANN_MLP::SIGMOID_SYM
- virtual voidcv::ml::ANN_MLP::setActivationFunction(inttype,doubleparam1 =0,doubleparam2 =0);
- // 设置训练方法,默认为 ANN_MLP::RPROP,较常用的是 ANN_MLP::BACKPROP
- // 若设为 ANN_MLP::BACKPROP,则 param1 对应 setBackpropWeightScale()中的参数,param2 对应 setBackpropMomentumScale() 中的参数
- virtual voidcv::ml::ANN_MLP::setTrainMethod(intmethod,doubleparam1 =0,doubleparam2 =0);
- virtual voidcv::ml::ANN_MLP::setBackpropWeightScale(doubleval);// 默认值为 0.1
- virtual voidcv::ml::ANN_MLP::setBackpropMomentumScale(doubleval);// 默认值为 0.1
- // 设置迭代终止准则,默认为 TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01)
- virtual voidcv::ml::ANN_MLP::setTermCriteria(TermCriteria val);
3) 训练
- // samples - 训练样本; layout - 训练样本为 "行样本" ROW_SAMPLE 或 "列样本" COL_SAMPLE; response - 对应样本数据的分类结果virtual bool cv::ml::StatModel::train(InputArray samples,int layout,InputArray responses);
4) 预测
- // samples,输入的样本书数据;results,输出矩阵,默认不输出;flags,标识,默认为 0virtual float cv::ml::StatModel::predict(InputArray samples, OutputArray results=noArray(),int flags=0) const;
4 代码示例
下面是 OpenCV 3.2 中,在 "支持向量机" 的例程上做的修改,使用 BP 神经网络,实现了和 SVM 相同的分类功能。
OpenCV 中的 支持向量机 (Support Vector Machine),可参见另一篇博文 OpenCV 之 支持向量机 (一)
- 1#include 2#include 3#include "opencv2/imgcodecs.hpp"4#include 5#include 6 7 using namespace cv;
- 8 using namespace cv: :ml;
- 9 10 int main() 11 {
- 12 // 512 x 512 零矩阵
- 13 int width = 512,
- height = 512;
- 14 Mat image = Mat: :zeros(height, width, CV_8UC3);
- 15 16 // 训练样本
- 17 float trainingData[6][2] = {
- {
- 500,
- 60
- },
- {
- 245,
- 40
- },
- {
- 480,
- 250
- },
- {
- 160,
- 380
- },
- {
- 400,
- 25
- },
- {
- 55,
- 400
- }
- };
- 18 float labels[6] = {
- 0,
- 0,
- 0,
- 1,
- 0,
- 1
- }; // 每个样本数据对应的输出
- 19 Mat trainingDataMat(6, 2, CV_32FC1, trainingData);
- 20 Mat labelsMat(6, 1, CV_32FC1, labels);
- 21 22 // BP 模型创建和参数设置
- 23 Ptr bp = ANN_MLP: :create();
- 24 25 Mat layerSizes = (Mat_ < int > (1, 3) << 2, 6, 1);
- 26 bp - >setLayerSizes(layerSizes);
- 27 28 bp - >setTrainMethod(ANN_MLP: :BACKPROP, 0.1, 0.1);
- 29 bp - >setActivationFunction(ANN_MLP: :SIGMOID_SYM);
- 30 bp - >setTermCriteria(TermCriteria(TermCriteria: :MAX_ITER, 10000,
- /*FLT_EPSILON*/
- 1e-6));
- 31 32 // 保存训练好的神经网络参数
- 33 bool trained = bp - >train(trainingDataMat, ROW_SAMPLE, labelsMat);
- 34
- if (trained) {
- 35 bp - >save("bp_param");
- 36
- }
- 37 38 // 创建训练好的神经网络
- 39 // Ptr<ANN_MLP> bp = ANN_MLP::load("bp_param");
- 40 41 // 显示分类的结果
- 42 Vec3b green(0, 255, 0),
- blue(255, 0, 0);
- 43
- for (int i = 0; i < image.rows; ++i) 44
- for (int j = 0; j < image.cols; ++j) 45 {
- 46 Mat sampleMat = (Mat_ < float > (1, 2) << j, i);
- 47 Mat responseMat;
- 48 bp - >predict(sampleMat, responseMat);
- 49 float response = responseMat.ptr < float > (0)[0];
- 50
- if (response > 0.5) 51 image.at(i, j) = green;
- 52
- else if (response < 0.5) 53 image.at(i, j) = blue;
- 54
- }
- 55 56 // 画出训练样本数据
- 57 int thickness = -1;
- 58 int lineType = 8;
- 59 circle(image, Point(500, 60), 5, Scalar(255, 255, 255), thickness, lineType);
- 60 circle(image, Point(245, 40), 5, Scalar(255, 255, 255), thickness, lineType);
- 61 circle(image, Point(480, 250), 5, Scalar(255, 255, 255), thickness, lineType);
- 62 circle(image, Point(160, 380), 5, Scalar(0, 0, 255), thickness, lineType);
- 63 circle(image, Point(400, 25), 5, Scalar(255, 255, 255), thickness, lineType);
- 64 circle(image, Point(55, 400), 5, Scalar(0, 0, 255), thickness, lineType);
- 65 66 imwrite("result.png", image); // 保存训练的结果
- 67 imshow("BP Simple Example", image);
- 68 waitKey(0);
- 69
- }
运行结果如下所示:
注意:OpenCV 3.0 以上版本,相较之前的版本,其中有关机器学习的部分做了较大改动,本人也是踩了很多坑才得到预期的效果。
1) 代码 #26,必须在 setActivationFunction() 之前,否则训练后的结果多为 nan
2) 代码 #48,responseMat 为预测的结果。若输出向量为 1 列,则如 #49 所示,可直接取出预测结果;若输出向量为 n 列,则可取平均值或者最大值。
同时,根据平均值或最大值,代码 #50 处的阈值也要相应的改变。
- floatresponse =0;
- for(inti=0;ii) {
- response += responseMat.ptr<float>(0)[i];
- }
3) 代码 #39,若已经训练好神经网络的参数,并将其保存到文件 bp_param 中。
则可将 #23 ~ #36 全部注释掉,再反注释掉 #39,这样,直接加载训练好的神经网络,便可以使用了。
参考资料:
<机器学习> 周志华 第 5 章
<统计学习方法> 李航 第 1 章
OpenCV 3.0 Tutorials -- Neural Networks
OpenCV 进阶之路:神经网络识别车牌字符 ☆Ronny 丶
【模式识别】OpenCV 中使用神经网络 CvANN_MLP xiaowei_cqu
来源: http://www.cnblogs.com/xinxue/p/5789421.html