为什么这个例子中OpenCV3.1 NormalBayesClassifier的错误率这么高?

Why is OpenCV3.1 NormalBayesClassifier's error rate so high in this example?

我正在尝试使用 OpenCV3.1 的 NormalBayesClassifier 解决一个我可以轻松生成训练数据的简单问题。我决定将输入数字分类为偶数或奇数。显然这可以直接计算出 100% 的准确率,但重点是锻炼 OpenCV 的 ML 能力以熟悉它。

所以,我的第一个问题是 - NormalBayesClassifier 不是解决这个问题的合适模型是否有理论上的原因?

如果不是,第二个问题,为什么我的错误率这么高? cv::ml::StatModel::calcError() 给我 30% - 70% 的输出。

第三,降低错误率的最佳方法是什么?

这是演示该问题的最小独立片段:

(为了清楚起见,classification/output 对于偶数应该是 0,对于奇数应该是 1

#include <ml.h>
#include <iomanip>

int main() {

   const int numSamples = 1000;
   cv::RNG rng = cv::RNG::RNG((uint64) time(NULL));

   // construct training sample data

   cv::Mat samples;
   samples.create(numSamples, 1, CV_32FC1);

   for (int i = 0; i < numSamples; i++) {
      samples.at<float>(i) = (int)rng(10000);
   }

   // construct training response data

   cv::Mat responses;
   responses.create(numSamples, 1, CV_32SC1);

   for (int i = 0; i < numSamples; i++) {
      int sample = (int) samples.at<float>(i);
      int response = (sample % 2);
      responses.at<int>(i) = response;
   }

   cv::Ptr<cv::ml::TrainData> data = cv::ml::TrainData::create(samples, cv::ml::ROW_SAMPLE, responses);

   data->setTrainTestSplitRatio(.9);

   cv::Ptr<cv::ml::NormalBayesClassifier> classifier = cv::ml::NormalBayesClassifier::create();

   classifier->train(data);

   float errorRate = classifier->calcError(data, true, cv::noArray());

   std::cout << "Bayes error rate: [" << errorRate << "]" << std::endl;

   // construct prediction inputs
   const int numPredictions = 10;

   cv::Mat predictInputs;
   predictInputs.create(numPredictions, 1, CV_32FC1);

   for (int i = 0; i < numPredictions; i++) {
      predictInputs.at<float>(i) = (int)rng(10000);
   }

   cv::Mat predictOutputs;
   predictOutputs.create(numPredictions, 1, CV_32SC1);

   // run prediction

   classifier->predict(predictInputs, predictOutputs);

   int numCorrect = 0;

   for (int i = 0; i < numPredictions; i++) {
      int input = (int)predictInputs.at<float>(i);
      int output = predictOutputs.at<int>(i);
      bool correct = (input % 2 == output);

      if (correct)
         numCorrect++;

      std::cout << "Input = [" << (int)predictInputs.at<float>(i) << "], " << "predicted output = [" << predictOutputs.at<int>(i) << "], " << "correct = [" << (correct ? "yes" : "no") << "]"  << std::endl;
   }

   float percentCorrect = (float)numCorrect / numPredictions * 100.0f;

   std::cout << "Percent correct = [" << std::fixed << std::setprecision(0) << percentCorrect << "]" << std::endl;
}

示例 运行 输出:

Bayes error rate: [36]
Input = [9150], predicted output = [1], correct = [no]
Input = [3829], predicted output = [0], correct = [no]
Input = [4985], predicted output = [0], correct = [no]
Input = [8113], predicted output = [1], correct = [yes]
Input = [7175], predicted output = [0], correct = [no]
Input = [811], predicted output = [1], correct = [yes]
Input = [699], predicted output = [1], correct = [yes]
Input = [7955], predicted output = [1], correct = [yes]
Input = [8282], predicted output = [1], correct = [no]
Input = [1818], predicted output = [0], correct = [yes]
Percent correct = [50]

在您的代码中,您为算法提供了一个特征,即要分类的数字。这还不够,除非您多次提供相同数字的多个示例。如果你想让学习算法学习一些关于奇数和偶数的东西,你需要考虑分类器可以使用什么样的特征来学习。大多数机器学习技术都需要您首先进行仔细的特征工程。

既然你想尝试机器学习,我建议如下:

  1. 对于每个数字,创建 5 个特征,一个用于对每个数字进行编码。因此,5 将是 00005 或 f1=0、f2=0、f3=0、f4=0、f5=0,而 11098 将是 f1=1、f2=2、f3=0、f4=9、f5=8。
  2. 如果您的数字大于此数字,则只能保留最后 5 位数字。
  3. 训练你的分类器
  4. 使用相同的编码进行测试。您希望从分类器中了解到只有最后一位数字对于确定奇数与偶数很重要

如果您想更多地使用它,可以将数字编码为二进制格式。这将使分类器更容易​​了解是什么使数字为奇数或偶数。