• OpenCV 之 神经网络 (一)


      人工神经网络(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$ 与阈值 $ heta$ 作比较,最后经过激活函数$\,f\,$产生神经元的输出: $y = fleft(\,sum limits_{i=1}^n {w_i x_i} - heta ight)$

      

    1.2  激活函数 (activation function)

      理想中,阶跃函数可作为激活函数,将输入值映射为输出值 “0” 和 “1;实际中,常用 Sigmoid 函数作激活函数, $f(x)=\,dfrac{1}{1+e^{-x}}$,如下图所示:

     

      OpenCV 中使用的激活函数是另一种形式,$f(x)=eta \,dfrac{1-e^{-alpha x}}{1+e^{-alpha x}}$

      当 α = β = 1 时,$f(x)=dfrac{1-e^{-x}}{1+e^{x}}$,该函数把可能在较大范围内变化的输入值,“挤压” 到 (-1, 1) 的输出范围内

          

      具体的设置函数如下,param1 --> α,param2 --> β

    // 设置激活函数,目前只支持 ANN_MLP::SIGMOID_SYM
    virtual void cv::ml::ANN_MLP::setActivationFunction(int type, double param1 = 0, double param2 = 0); 

    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,输出层的为 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);

       如何设置隐层神经元的个数仍是个未决的问题,实际中多采用“试错法”来调整

    3  OpenCV 函数

    1)  创建

    static Ptr<ANN_MLP> cv::ml::ANN_MLP::create();  // 创建空模型

    2) 设置参数

    // 设置神经网络的层数和神经元数量
    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);

    3)  训练

    // samples - 训练样本; layout - 训练样本为 “行样本” ROW_SAMPLE 或 “列样本” COL_SAMPLE; response - 对应样本数据的分类结果
    virtual
    bool cv::ml::StatModel::train(InputArray samples,int layout,InputArray responses);

    4)  预测

    // samples,输入的样本书数据;results,输出矩阵,默认不输出;flags,标识,默认为 0
    virtual
    float cv::ml::StatModel::predict(InputArray samples, OutputArray results=noArray(),int flags=0) const;      

    4 代码示例

      下面是 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)  代码 #46response_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

      OpenCV进阶之路:神经网络识别车牌字符       ☆Ronny丶

     【模式识别】OpenCV中使用神经网络 CvANN_MLP       xiaowei_cqu

  • 相关阅读:
    40 图 |我用 Mac M1 玩转 Spring Cloud
    # 20 图 |6000 字 |实战缓存(上篇)
    博客园,你肿么了?
    ES 终于可以搜到”悟空哥“了!
    48 张图 | 手摸手教你微服务的性能监控、压测和调优
    植树节,种个二叉树吧?
    紫霞仙子:区块链的十二连问
    太上老君的炼丹炉之分布式 Quorum NWR
    病毒入侵:全靠分布式
    为什么要“除夕”,原来是内存爆了
  • 原文地址:https://www.cnblogs.com/xinxue/p/5789421.html
Copyright © 2020-2023  润新知