読者です 読者をやめる 読者になる 読者になる

凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

Juliusの認識結果をOLLでオンライン学習させてみたら結構良かった

C++ Julius OLL 機械学習

はじめに

前回のエントリでは、OLLによるオンライン学習を試してみました。そこで今回は、Juliusによる認識結果を実際にこのオンライン学習を用いるとどれほど成果が上がるのか試してみた結果を報告します。

データ

Juliusの認識結果は@super_rtiさんがgithubに上げて下さっているものをお借りします。

train.txt と test.txt をダウンロードし、これを利用します。
前回紹介した oll_class.hpp はちょっとだけ改変しました。

下記のようなコードを書いて、オンライン学習がどれだけの成果を出してくれるのかテストをしてみます。学習には @super_rti さんからのアドバイス(下記参照)を元に、CW(Confidence Weighted)を用いています。

test.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <utility>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include "oll_class.hpp"

int main(int argc, char const* argv[])
{
	OLL<oll_tool::CW> oll;

	std::ifstream train_data("train.txt"), test_data("test.txt");
	std::vector<std::pair<int, std::string>> train_map, test_map;
	std::string line;

	// 学習用データをロード
	while ( std::getline(train_data, line) ) {
		auto it = line.begin();
		for (; (*it) != ' '; ++it);
		train_map.push_back(
			std::make_pair(
				boost::lexical_cast<int>( std::string(line.begin(), it) ),
				std::string(it+1, line.end())
			)
		);
	}

	// テスト用データをロード
	while ( std::getline(test_data, line) ) {
		auto it = line.begin();
		for (; (*it) != ' '; ++it);
		test_map.push_back(
			std::make_pair(
				boost::lexical_cast<int>( std::string(line.begin(), it) ),
				std::string(it+1, line.end())
			)
		);
	}

	// 学習
	for (const auto& x : train_map) {
		oll.add(x.first, x.second);

		// 学習用データでテスト
		int good = 0, bad = 0;
		for (const auto& x : train_map) {
			const int   success_or_failure = x.first;
			const float score = oll.test(x.second).get();
			if ( success_or_failure > 0 && score > 0 ||
			     success_or_failure < 0 && score < 0 )
				++good;
			else
				++bad;
		}
		std::cout << static_cast<float>(good) / train_map.size() << std::endl;
	}

	// 未知のデータでテスト
	int pp = 0, pn = 0, np = 0, nn = 0;
	for (const auto& x : test_map) {
		const int   success_or_failure = x.first;
		const float score = oll.test(x.second).get();
		if (success_or_failure > 0 && score > 0)
			++pp;
		else if (success_or_failure > 0 && score < 0)
			++pn;
		else if (success_or_failure < 0 && score > 0)
			++np;
		else if (success_or_failure < 0 && score < 0)
			++nn;
	}
	std::cout << boost::format("(p,p):%1% (p,n):%2% (n,p):%3%, (n,n):%4%\n") % pp % pn % np % nn;
	std::cout << static_cast<float>(pp + nn) / test_map.size() << std::endl;

	return 0;
}
コンパイル
g++-4.6 -std=c++0x test.cpp oll.cpp -o test

結果

オンライン学習の結果

まず、オンライン学習の結果です。

最初の方は真平らになっていますが、50〜60データを学習させたあたりから精度の向上が見られます。最初平らなのは、おそらく与えている素性の数がとても多い(数千素性ある)ので、一定の学習数を与えないと推定する素性が被らなく、学習に寄与しないからなのではないかと思います。とはいっても学習に与えたデータをそのままテストしているだけなので、精度が上がるのは当然っちゃぁ当然、という感じもします。

未知のデータに対するテストの結果

次に、未知の(学習させていない)データをテストしてみた結果を見てみます。

(p,p):10 (p,n):8 (n,p):1, (n,n):9
0.678571

それぞれの値は、

  • (p,p): Juliusで正しく認識され、機械学習で正解とされたもの
  • (p,n): Juliusで正しく認識されたが、機械学習では不正解とされたもの
  • (n,p): Juliusで誤認識され、かつ機械学習でも正解とされてしまったもの
  • (n,n): Juliusで誤認識されたが、機械学習では不正解とできたもの

となっています。
認識精度自体は 68% 程度ですが、注目すべきは (n,p)が 1 と、一番困るパターンがほぼ弾けた点です。(p,n)が多いのもちょっと気になりますが、概ね良好な結果が得られました。

お礼

またまた @super_rti さんのお助けを色々借りてしましました。いつもありがとうございます。

PA1では常に機械学習によるテストで「正解」という結果が返ってきてしまっていたため、57/110(=Juliusで正しく認識されたデータ数/全データ数)=0.51818 となってしまっていたみたいです。

先駆けての調査、ありがとうございました。

お借りさせて頂きました m(_ _)m