OpenCV神经网络,预测不正确

4
我正在尝试使用OpenCV和C++创建神经网络,目的是识别道路标志。我按照以下方式创建了网络,但预测效果很差,因为它返回奇怪的结果:enter image description here。训练样本图片如下所示:examples。请问有人能帮忙吗?
  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
}

奇怪的结果:对于这个输入speed80,答案是3(因为我只有4个类别-限速50、60、70、80)。对于限速80标志来说是正确的。results 但是对于其他输入,结果是不正确的。它们对于50、60、70限速标志是相同的。max1 = min1 = 1.02631...(如第一张图片所示)。这很奇怪。

也许这个链接会有用:http://blog.dlib.net/2014/02/dlib-186-released-make-your-own-object.html。 - sturkmen
使用MNIST手写数字数据集是检查您自己的实现的好方法 - 对于该数据集,不同算法应该表现得很好是众所周知的。 - MSalters
你使用了哪个传递函数?样本大小是多少?(还有你的 eta/alpha 值)你可能正在过度训练或欠拟合。为了进行简单的测试,请尝试使用神经网络创建 XOR 门,然后使用 MSalters 在 MNIST 数据集上进行工作。为节省时间,请查看 https://dev59.com/kmsy5IYBdhLWcg3w1xeU 以了解如何在 c++ 中读取 MNIST 数据。 - Lilith Daemon
2个回答

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)。这不是很好,但它表明您正在正确的轨道上。
基本上,网络结构不足以充分地建模您试图解决的问题,因此网络权重从未收敛,它就会提前放弃反向传播。对于一个类,您可能会看到一些成功,但我认为这在很大程度上取决于训练数据的缺乏洗牌。如果他只是在训练了几百张非常相似的图像后停止,那么他可能能够成功地对其进行分类。
简而言之,我建议执行以下操作:
1. 创建测试结果的方法 - 例如:创建一个函数来运行所有训练数据的预测,并理想情况下将一些图像作为验证集,以确认模型没有过度拟合训练数据。 2. 在训练之前对训练数据进行洗牌。否则,反向传播不会很容易收敛。 3. 尝试使用不同的体系结构,例如具有不同大小的多个隐藏层。
实际上,这是一个从卷积神经网络中获益巨大的问题,但OpenCV的机器学习设施非常有限。最终,如果您真的想要创建ANN,您可能需要调查一些更强大的工具。我个人使用Tensorflow,但我也听说过Theano的好处。

我甚至将训练选择的图像作为输入测试。但是它并没有起作用。 - colla
请查看我的进一步编辑 - 希望这能帮助您更进一步地努力。 - Aenimated1

0

我仅使用OpenCV实现了布尔分类的NN,但是我认为对于需要分类超过两个不同类别的任务,这也可能适用:

"如果您正在使用默认的cvANN_MLP :: SIGMOID_SYM激活函数,则输出应在[-1,1]范围内而不是[0,1],以获得最佳结果。"

因此,在您进行以下操作时:

*train_ptr = k != labels ? 0 : 1;

你可以尝试以下方法:

*train_ptr = k != labels ? -1 : 1;

如果我偏离了主题,请忽略此消息。


很遗憾,它没有起作用,但还是谢谢你的帮助。 - colla
如果您不介意的话,我可以尝试实现自己版本的分类器。我只需要用于ANN的训练和测试图像以及您感兴趣的类别。 - Carafini
好的,这是培训选择链接 https://www.dropbox.com/s/zdf840zmhvgav46/training2.rar?dl=0 班级:50、60、70、80 - colla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接