我试图用OpenCV在C ++中创建一个神经网络。目的是识别道路标志。我以这种方式创建了网络,但它预测得很糟糕,因为它会返回奇怪的结果:
来自训练选择的样本图像如下所示:
有人可以帮忙吗?
trainNN() {
char* templates_directory[] = {
"speed50ver1\\",
"speed60ver1\\",
"speed70ver1\\",
"speed80ver1\\"
};
int const numFilesChars[]={ 213, 100, 385, 163};
char const strCharacters[] = { '5', '6', '7', '8' };
Mat trainingData;
Mat trainingLabels(0, 0, CV_32S);
int const numCharacters = 4;
// load images from directory
for (int i = 0; i != numCharacters; ++i) {
int numFiles = numFilesChars[i];
DIR *dir;
struct dirent *ent;
char* s1 = templates_directory[i];
if ((dir = opendir (s1)) != NULL) {
Size size(80, 80);
while ((ent = readdir (dir)) != NULL) {
string s = s1;
s.append(ent->d_name);
if(s.substr(s.find_last_of(".") + 1) == "jpg") {
Mat img = imread(s,0);
Mat img_mat;
resize(img, img_mat, size);
Mat new_img = img_mat.reshape(1, 1);
trainingData.push_back(new_img);
trainingLabels.push_back(i);
}
}
int b = 0;
closedir (dir);
} else {
/* could not open directory */
perror ("");
}
}
trainingData.convertTo(trainingData, CV_32FC1);
Mat trainClasses(trainingData.rows, numCharacters, CV_32FC1);
for( int i = 0; i != trainClasses.rows; ++i){
int const labels = *trainingLabels.ptr<int>(i);
auto train_ptr = trainClasses.ptr<float>(i);
for(int k = 0; k != trainClasses.cols; ++k){
*train_ptr = k != labels ? 0 : 1;
++train_ptr;
}
}
int layers_d[] = { trainingData.cols, 10, numCharacters};
Mat layers(1, 3, CV_32SC1, layers_d);
ann.create(layers, CvANN_MLP::SIGMOID_SYM, 1, 1);
CvANN_MLP_TrainParams params = CvANN_MLP_TrainParams(
// terminate the training after either 1000
// iterations or a very small change in the
// network wieghts below the specified value
cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, 0.000001),
// use backpropogation for training
CvANN_MLP_TrainParams::BACKPROP,
// co-efficents for backpropogation training
// (refer to manual)
0.1,
0.1);
int iterations = ann.train(trainingData, trainClasses, cv::Mat(), cv::Mat(), params);
CvFileStorage* storage = cvOpenFileStorage( "neural_network_2.xml", 0, CV_STORAGE_WRITE );
ann.write(storage,"digit_recognition");
cvReleaseFileStorage(&storage);
}
void analysis(char* file, bool a) {
//trainNN(a);
read_nn();
// load image
Mat img = imread(file, 0);
Size my_size(80,80);
resize(img, img, my_size);
Mat r_img = img.reshape(1,1);
r_img.convertTo(r_img, CV_32FC1);
Mat classOut(1,4,CV_32FC1);
ann.predict(r_img, classOut);
double min1, max1;
cv::Point min_loc, max_loc;
minMaxLoc(classOut, &min1, &max1, &min_loc, &max_loc);
int x = max_loc.x;
//create windows
namedWindow("Original Image", CV_WINDOW_AUTOSIZE);
imshow("Original Image", img);
waitKey(0); //wait for key press
img.release();
rr.release();
destroyAllWindows(); //destroy all open windows
}
奇怪的结果:对于这个输入答案是3(因为我只有4个类 - 速度限制50,60,70,80)。它对速度限制80标志是正确的。
但其余输入结果不正确。它们对于标志50,60,70是相同的.max1 = min1 = 1.02631 ......(如第一张图片所示)它很奇怪。
答案 0 :(得分:3)
我已经调整了你的代码来训练4手位置的分类器(因为那是我的图像数据)。我保持你的逻辑尽可能相似,只改变了使我的Windows机器上的图像运行绝对必要的东西。简而言之,您的代码没有任何根本性的错误 - 我没有看到您描述的失败模式。
你遗漏的一件事是read_nn()的代码。我假设只是执行以下操作:
ann.load("neural_network_2.xml");
无论如何,我的怀疑是你的神经网络根本没有融合,或者它过度拟合。也许训练数据没有足够的变化。您是否在ANN没有接受过培训的单独测试数据上运行analysis()?如果是这样,ANN是否能够至少正确地预测训练数据?
编辑:好的,我刚刚下载了您的图像数据并尝试了它并看到了相同的行为。经过一些分析,看起来你的ANN没有收敛。即使您仅为cvTermCriteria指定CV_TERMCRIT_ITER,训练操作也仅在大约250次迭代后退出。在将隐藏的图层大小从10增加到20之后,我看到了显着的改进,成功将212,72,94和143个图像的训练数据分类到了类(50,60,70和80) 。这不是很好,但它表明你走在正确的轨道上。基本上,网络体系结构的表达力不足以充分模拟您尝试解决的问题,因此网络权重永远不会收敛,并且它会尽早放弃反向提升。对于一门课程,您可能会看到一些成功,但我认为这很大程度上取决于缺乏训练数据的改组。如果它在刚刚训练了几百个非常相似的图像后停止,它可能能够设法正确地对它们进行分类。
简而言之,我建议您执行以下操作:
实际上,这是一个可以从使用卷积神经网络中获益的问题,但OpenCV的机器学习设施非常有限。最后,如果你真的想要创建人工神经网络,你可能想要研究一些更强大的工具。我个人使用Tensorflow,但我也听说过Theano的好消息。
答案 1 :(得分:0)
我只使用OpenCV实现NN进行布尔分类,但我认为对于需要对两个以上不同类进行分类的任务,这也可能适用:
“如果您使用默认的cvANN_MLP :: SIGMOID_SYM激活函数,那么输出应该在[-1,1]范围内,而不是[0,1],以获得最佳结果。”
所以,你在哪里:
*train_ptr = k != labels ? 0 : 1;
您可能想尝试:
*train_ptr = k != labels ? -1 : 1;
如果我在这里偏离轨道,请不要理会。 p>