OpenCV神经网络Sigmoid输出

3
我已经使用OpenCV相当长一段时间了。最近我决定测试它在机器学习方面的能力。因此,我最终实现了一个用于人脸识别的神经网络。总结我的人脸识别策略如下:
  1. 从某些面部数据库的CSV文件中读取图像。
  2. 将图像按行滚动到Mat数组中。
  3. 应用PCA进行降维。
  4. 使用PCA的投影来训练网络。
  5. 使用训练好的网络预测测试数据。

    所以,一切都很顺利,直到预测阶段。我使用最大响应输出单元对面部进行分类。通常情况下,OpenCV的sigmoid实现应该给出-1到1范围内的值,这在文档中有说明。1是最接近类的最大值。但是当我得到几乎为0的精度时,我检查了每个测试数据每个类别的输出响应。我对这些值感到惊讶:14.53, -1.7, #IND。如果应用sigmoid,我怎么会得到这些值?我做错了什么?

    为了帮助您理解问题,并为那些想知道如何应用PCA及其与NN配合使用的人分享我的代码:

读取csv:

void read_csv(const string& filename, vector& images, vector& labels, char separator = ';') 
{
    std::ifstream file(filename.c_str(), ifstream::in);
    if (!file) 
    {
        string error_message = "未提供有效的输入文件,请检查给定的文件名。";
        CV_Error(1, error_message);
    }
    string line, path, classlabel;
    while (getline(file, line)) 
    {
        stringstream liness(line);
getline(liness, path, separator); getline(liness, classlabel);
if(!path.empty() && !classlabel.empty()) { Mat im = imread(path, 0);
images.push_back(im); labels.push_back(atoi(classlabel.c_str())); } } }

按行滚动图像:

Mat rollVectortoMat(const vector<Mat> &data)
{
   Mat dst(static_cast<int>(data.size()), data[0].rows*data[0].cols, CV_32FC1);
   for(unsigned int i = 0; i < data.size(); i++)
   {
      Mat image_row = data[i].clone().reshape(1,1);
      Mat row_i = dst.row(i);                                       
      image_row.convertTo(row_i,CV_32FC1, 1/255.);
   }
   return dst;
}

将标签向量转换为标签矩阵
Mat getLabels(const vector<int> &data,int classes = 20)
{
    Mat labels(data.size(),classes,CV_32FC1);

    for(int i = 0; i <data.size() ; i++)
    {
        int cls = data[i] - 1;  
        labels.at<float>(i,cls) = 1.0;  
    }

    return labels;
}

主要内容

int main()
{

    PCA pca;

    vector<Mat> images_train;
    vector<Mat> images_test;
    vector<int> labels_train;
    vector<int> labels_test;

    read_csv("train1k.txt",images_train,labels_train);
    read_csv("test1k.txt",images_test,labels_test);

    Mat rawTrainData = rollVectortoMat(images_train);                       
    Mat rawTestData  = rollVectortoMat(images_test);                

    Mat trainLabels = getLabels(labels_train);
    Mat testLabels  = getLabels(labels_test);

    int pca_size = 500;

    Mat trainData(rawTrainData.rows, pca_size,rawTrainData.type());
    Mat testData(rawTestData.rows,pca_size,rawTestData.type());


    pca(rawTrainData,Mat(),CV_PCA_DATA_AS_ROW,pca_size);

    for(int i = 0; i < rawTrainData.rows ; i++)
        pca.project(rawTrainData.row(i),trainData.row(i));

    for(int i = 0; i < rawTestData.rows ; i++)
        pca.project(rawTestData.row(i),testData.row(i));



    Mat layers = Mat(3,1,CV_32SC1);
    int sz = trainData.cols ;

    layers.row(0) = Scalar(sz);
    layers.row(1) = Scalar(1000);
    layers.row(2) = Scalar(20);

    CvANN_MLP mlp;
    CvANN_MLP_TrainParams params;
    CvTermCriteria criteria;

    criteria.max_iter = 1000;
    criteria.epsilon  = 0.00001f;
    criteria.type     = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS;

    params.train_method    = CvANN_MLP_TrainParams::BACKPROP;
    params.bp_dw_scale     = 0.1f;
    params.bp_moment_scale = 0.1f;
    params.term_crit       = criteria;

    mlp.create(layers,CvANN_MLP::SIGMOID_SYM);
    int i = mlp.train(trainData,trainLabels,Mat(),Mat(),params);

    int t = 0, f = 0;

    for(int i = 0; i < testData.rows ; i++)
    {
        Mat response(1,20,CV_32FC1);
        Mat sample = testData.row(i);

        mlp.predict(sample,response);

        float max = -1000000000000.0f;
        int cls = -1;

        for(int j = 0 ; j < 20 ; j++)   
        {
            float value = response.at<float>(0,j);

            if(value > max)
            {
                max = value;
                cls = j + 1;
            }
        }

        if(cls == labels_test[i])
            t++;
        else
            f++;
    }


    return 0;
}

注意:我使用了AT&T的前20个类作为我的数据集。


3
请手动设置参数,例如:mlp.create(layers, CvANN_MLP::SIGMOID_SYM, 1, 1); - baci
如果它能正常工作,请提交一个错误报告,因为对于 alpha 和 beta 来说,默认值 0 没有意义。 - baci
看起来它惊人地将Sigmoid输出值纠正到了建议的范围内。现在,我正在使用其他参数以获得更好的准确性。不久我会发布关于神经网络的经验。顺便说一句,谢谢 :) - yutasrobot
1
np,已报告@ http://code.opencv.org/issues/3583 - baci
@Canberk Baci,我看到了你的问题。除了SO链接之外,你能否在那里添加一些细节,比如有问题的mlp.create(layers, CvANN_MLP::SIGMOID_SYM, 1, 1);这一行?(也许还可以解释一下为什么默认值是错误的)。如果这样的事情不需要让那些可怜的人们去解析SO噪音,那就更好了。 - berak
@berak 谢谢,我打算更新,但随时欢迎您进行修改。 - baci
1个回答

4
感谢Canberk Baci的评论,我成功解决了Sigmoid输出不一致的问题。问题似乎在于mlp的create函数的默认参数中,alpha和beta默认为0。当它们都被设置为1时,Sigmoid函数可以像文档中所述那样工作,神经网络可以预测一些东西,但当然会有误差。
至于神经网络的结果:
通过修改一些参数,如动量等,而没有任何矫正算法,我在opencv教程的CroppedYaleB的(随机抽取的936个训练、262个测试图像的)前20类数据集上获得了72%的准确率。为了提高准确性的其他因素:当我应用PCA时,我直接给出了减少的维度大小为500。这也可能会降低准确性,因为保留的方差可能低于95%或更差。所以当我有空时,我会应用以下方法来提高准确性:
1. Tan Triggs照明校正 2. 以0.95为pca大小进行训练,以保留95%的方差。 3. 修改���经网络参数(我希望我们在OpenCV库中有一个更少参数化的NN)
我分享这些,以便有人想知道如何提高NN的分类准确率。我希望这能有所帮助。
顺便提一下,您可以在此处跟踪有关此问题的信息: http://code.opencv.org/issues/3583

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