OpenCVでSVMを使った機械学習&推定

OpenCVは本当に便利ですね.
さまざまなツールが内包されていますが,その中でも機械学習に関する部分をいじってみようと思い,SVMに手をつけました.
やっていることはサンプルコードの域を脱していませんが,自分で書いてみることで使い方がわかってきました.
テーマは,「ぼけた画像(ピントが合っていない)とそうでない画像を自動的に区別する」です.
ためしに作ってみたものは性能的にイマイチでしたが,一応使い方をまとめておきます.

作ったもの

以下の機能をもつプログラムです.
・データセット(特徴量,ぼけているかどうかの判定)を読み込んで,ぼけているかどうかの判定基準を学習する
・学習した判定基準にしたがい,新たに読み込んだ特長量から画像のぼけ判定をする
ここにコミットしました.
https://github.com/glass5er/opencv-ml-svm
画像も載せたいですが,著作権的なことに触れるとまずいので,データセットだけコミットしました.

プログラムの流れ

学習フェイズ

まず,データセットを読み込みます.
最終的にsvmに渡せれば何でも良いですので,CSVファイルから読み出す形にしました.
必要なのは以下です.

  • 判定フラグ(整数値)
  • 特徴量データ(実数)

データの個数に決まりはありませんが,学習シナリオごとに個数が異なるのはNGです.

  //  training data buffer  //
  std::vector< int > flagset;
  std::vector< std::vector<double> > dataset;
  read_csv(fname_train.c_str(), flagset, dataset);
  const int lines_tr = dataset.size();
  const int segments = dataset[0].size();
<||
読み出したデータを行列に格納し,学習します.
train一発で学習できるのは便利です.
>|cpp|
  //  dataset -> matrix  //
  cv::Mat flagmat(lines_tr, 1, CV_32SC1);
  cv::Mat datamat(lines_tr, segments, CV_32FC1);
  for(int i=0; i<lines_tr; i++) {
    flagmat.at<int>(i,0) = flagset[i];
    for(int j=0; j<segments; j++) {
      datamat.at<float>(i,j) = dataset[i][j];
    }
  }
  //  training (SVM)  //
  CvSVM svm;
  svm.train(datamat, flagmat);
推定フェイズ

学習のときと同じように,CSVからデータを読み込んでいます.
当然ながら,特徴量の個数は学習時と同じでないといけません.

  //  prediction data buffer  //
  std::vector< int > flag_tmp;
  std::vector< std::vector<double> > data_tmp;
  read_csv(fname_pred.c_str(), flag_tmp, data_tmp);
  const int samples = data_tmp.size();

  //  each sample -> predict //
  cv::Mat sample(1, segments, CV_32FC1);
  for(int i=0; i<samples; i++) {
    for(int j=0; j<segments; j++) {
      sample.at<float>(0,j) = data_tmp[i][j];
    }
    //  prediction (SVM)  //
    float res = svm.predict(sample);
    cout << "pred result[" << i << "] = " << res << endl;
  }

準備した特徴量

以下のように,画像の書く画素について周辺1画素,あるいは2画素内での
差分絶対値の最小値をとり,そのヒストグラムを特徴量としました.
(こんなので判別できるなら機械学習いらないでしょうけど,お試しということで.)

推定結果

学習シナリオ192個(ぼけているx96個,ぼけていないx96個)を使い,学習した基準に対し,
自身が正しく判定されるかを確かめました.
つまり,学習と同じデータを推定にも使ってみました.
その結果,

  • ぼけている画像 : 正解80/96, 不正解16/96
  • ぼけていない画像 : 正解76/96, 不正解20/96

という,あまりよろしくない識別率が出ました.
特徴量しだいでどうにでもなる部分だと思うので,再考してみようと思います.

最後に

SVMは,ツールとしては非常に簡単に扱えました.
ただ,どう使えば性能を最大限に引き出せるかは,まだまだ修行が必要みたいです.
最初は別の機械学習アルゴリズムであるBoostingで同じことをやろうとしていましたが,
今回の特徴量とはあまりにも相性が悪かったのでやめました.
次はBoostingで何かやってみたいと思います.