opencv2.4.9中ann_mlp.cpp学习
2015-11-22 17:09
459 查看
多层感知机的结构
http://www.ieee.cz/knihovna/Zhang/Zhang100-ch03.pdf” title=”” />理论上已经证明,单层感知机无法拟合XOR等非线性函数,而两层感知机(hidden layer,output layer,但不包括input layer)可以拟合任意连续函数。该理论很好,但是对应一个待拟合的函数,却无法给出每层到底需要多少个神经元(neuron)。
理解opencv2.4.9中ann_mlp.cpp的难点之二,一是RPROP算法,二是权值初始化方法
一.RPROP算法
rprop全称是“resilient propagation.在BP算法中对权值更新推导过程中有如下公式:ann_mlp.cpp中关于各个参数的取值大都参考了文献Riedmiller M, Braun H. A direct adaptive method for faster backpropagation learning: The RPROP algorithm[C]//Neural Networks, 1993., IEEE International Conference on. IEEE, 1993: 586-591.
在此,贴出公式方便理解代码。
二.权值初始化方法—Nguyen-Widrow algorithm
网上有人说:Nguyen & Widrow in their paper assume that the inputs are between -1 and +1.Nguyen Widrow initialization is valid for any activation function which is finite in length. Again in their paper they are only talking about a 2 layer NN, not sure about a 5 layer one.
但是ann_mlp.cpp中将Nguyen-Widrow algorithm应用到了任意层数。
使用Nguyen-Widrow algorithm的2个步骤:
1.给与hidden layer的neuron连接权值随机赋予初值。
若前一层的hidden node数是n1,当前层的hidden node数是n2,则总连接数数是n1*n2.
权值w的个数=(n1+1)*(n2+1),所有w均服从(-1,1)之间的均匀分布。“+1”的原因是在将WX输入到sigmoid前有个偏置bias。这个连接关系可以用一个矩阵来表示,矩阵值代表w。
2.对每个hidden node,采用1-范数归一化权值。
公式如下:
三.使用MLP来分类
正如http://blog.csdn.net/xiaowei_cqu/article/details/9004331所说:“由于ml模型实现的算法都继承自统一的CvStatModel基类,其训练和预测的接口都是train(),predict()。”训练数据采用mushroom.cpp中所用到的agaricus-lepiota.data,输入数据是22维的,输出有毒还是无毒标签。
1. 设计MLP
假设采用两层感知机,input layer node number = 22,output layer node
number = 1,由于d
维感知机的VC维是d+1,证明请参照http://www.douban.com/note/323572623/,因此hidden
layer node
number=21,因此这个两层感知机必定可以将数据完全分开,我认为当感知机的层数增加时,每层的neurons个数应该要减少,在此为了简便,只采用两层感知机。实验室采用for循环选择mse最小时的hidden layer node number。
2.数据处理
由于agaricus-lepiota.data中有属性缺失问题,但是为了简单,不像CvDTree那样考虑代理分裂的之类的处理了,所有缺失属性‘?’都变成了-1。
代码如下:
#include <iostream> #include<opencv2/opencv.hpp> #include <opencv2/ml/ml.hpp> using namespace cv; using namespace std; static int mushroom_read_database(const char* filename, CvMat** data, CvMat** missing, CvMat** responses) { const int M = 1024; FILE* f = fopen(filename, "rt"); CvMemStorage* storage; CvSeq* seq; char buf[M + 2], *ptr; float* el_ptr; CvSeqReader reader; int i, j, var_count = 0; if (!f) return 0; // read the first line and determine the number of variables if (!fgets(buf, M, f)) { fclose(f); return 0; } for (ptr = buf; *ptr != '\0'; ptr++) var_count += *ptr == ','; assert(ptr - buf == (var_count + 1) * 2); // create temporary memory storage to store the whole database el_ptr = new float[var_count + 1]; storage = cvCreateMemStorage(); seq = cvCreateSeq(0, sizeof(*seq), (var_count + 1)*sizeof(float), storage); for (;;) { for (i = 0; i <= var_count; i++) { int c = buf[i * 2]; el_ptr[i] = c == '?' ? -1.f : (float)c;//'?' is replaced by -1.f } if (i != var_count + 1) break; cvSeqPush(seq, el_ptr); if (!fgets(buf, M, f) || !strchr(buf, ',')) break; } fclose(f); // allocate the output matrices and copy the base there *data = cvCreateMat(seq->total, var_count, CV_32F); *missing = cvCreateMat(seq->total, var_count, CV_8U); *responses = cvCreateMat(seq->total, 1, CV_32F); cvStartReadSeq(seq, &reader); for (i = 0; i < seq->total; i++) { const float* sdata = (float*)reader.ptr + 1; float* ddata = data[0]->data.fl + var_count*i; float* dr = responses[0]->data.fl + i; uchar* dm = missing[0]->data.ptr + var_count*i; for (j = 0; j < var_count; j++) { ddata[j] = sdata[j]; dm[j] = sdata[j] < 0; } *dr = sdata[-1]; CV_NEXT_SEQ_ELEM(seq->elem_size, reader); } cvReleaseMemStorage(&storage); delete[] el_ptr; return 1; } int main(int argc,char **argv){ CvMat *data = 0, *missing = 0, *responses = 0; const char* base_path = argc >= 2 ? argv[1] : "C:/opencv2.4.9/sources/samples/c/agaricus-lepiota.data"; if (!mushroom_read_database(base_path, &data, &missing, &responses)) { printf("\nUnable to load the training database\n\n"); return -1; } Mat X = Mat(data, true); X.convertTo(X, CV_32FC1); Mat Y = Mat(responses, true); Y.convertTo(Y, CV_32FC1); cvReleaseMat(&data); cvReleaseMat(&missing);//missing is not used in this program. cvReleaseMat(&responses); int SAMPLES = X.rows; float SPLIT = 0.8f; Mat X_train = X(Range(0, (int)(SAMPLES*SPLIT)), Range::all()); Mat Y_train = Y(Range(0, (int)(SAMPLES*SPLIT)), Range::all()); Mat X_test = X(Range((int)(SAMPLES*SPLIT), SAMPLES), Range::all()); Mat Y_test = Y(Range((int)(SAMPLES*SPLIT), SAMPLES), Range::all()); CvANN_MLP_TrainParams params( cvTermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 1000, 0.000001), CvANN_MLP_TrainParams::BACKPROP, 0.1, 0.1); float min_mse = 1e8f; int min_nnode = 0, min_err = X_test.rows; for (int nnode = 4; nnode <= X_train.cols-1; nnode++){ Mat layers = (Mat_<int>(3, 1) << X_train.cols, nnode, 1); CvANN_MLP net(layers, CvANN_MLP::SIGMOID_SYM, 0, 0); net.train(X_train, Y_train, Mat(), Mat(), params); Mat predictions(Y_test.size(), CV_32F); net.predict(X_test, predictions); //cout << predictions << endl; Mat error = predictions - Y_test; int err=0; for (int k = 0; k < error.cols; k++){ if (fabs((float)*(error.data + k)) > FLT_EPSILON)//the same threshold as CvDTree err++; } multiply(error, error, error); float mse = (float)(sum(error)[0] / error.rows); if (mse < min_mse){ min_mse = mse; min_nnode = nnode; min_err = err; } } cout << "MSE: " << min_mse << endl; cout << " nnode:" << min_nnode << endl; cout << "accuracy:" << (1 - min_err / X_test.rows) * 100 << "%\n"; system("pause"); return 0; }
代码借鉴了http://stackoverflow.com/questions/25748423/opencv-mlp-with-sigmoid-neurons-output-range。此处贴出全部代码一是方便备忘,二是方便他人重复试验。试验中发现accuracy总是100%? 另外说明:opencv2.4.9中RPROP和权值初始化时一样的,但是train函数不一样。
训练的过程中采用的训练方法是BACKPROP而不是RPROP,发现RPROP的速度随着hidden layer number的增加变得非常慢。
文中诸多不完善的地方,后续有了新的体会,再更新。。。
(转载请注明作者和出处:http://blog.csdn.net/CHIERYU 未经允许请勿用于商业用途)
相关文章推荐
- python中使用OpenCV进行人脸检测的例子
- opencv 做人脸识别 opencv 人脸匹配分析
- 使用opencv拉伸图像扩大分辨率示例
- OpenCV 2.4.3 C++ 平滑处理分析
- 利用Python和OpenCV库将URL转换为OpenCV格式的方法
- python结合opencv实现人脸检测与跟踪
- 在树莓派2或树莓派B+上安装Python和OpenCV的教程
- opencv-python学习一--人脸检测
- 在Ubuntu上安装OpenCV3.0和Python-openCV的经历
- OpenCV配置,从来没有这么简单!
- ubuntu下opencv和qt的安装配置
- OpenCV学习笔记(二十五)——OpenCV图形界面设计Qt+VS2008
- 分享一些OpenCV实现立体视觉的经验
- 关于OpenCv图像变换与基本图形检测
- "应用程序正常初始化失败"-0xc0150002 解决办法
- OpenCV->HSV色彩空间
- opencv 内存泄露
- OpenCV函数cvFindContours
- OpenCV 2.3.1图像文件的读入和显示
- opencv2 矩阵方式 resize图像缩放代码