簡単分類ツールfutabaを作ってみた
しばらく前に作ったライブラリtsubomiのサンプルとして簡単分類ツールfutabaを作ってみた。あくまでtsubomiのサンプルなので高性能というわけではないが、ちょっとしたマイニングに使うには便利かもしれない。
本ツールは事前に用意した学習データ(クラス名と素性ベクトルのペア集合)から重みベクトルを学習させ、新しい入力(素性ベクトル)を学習時に与えたクラスのいずれかに分類するという一般的な分類ツールとなっている。クラスをアイテムとみなすことでレコメンデーション、類似文検索用途でも利用が可能。
以下使いかたをメモしておく。
注:tsubomiはUnix、Linux、Macでの利用を想定しています。これらの環境なら動作すると思いますが、もし上手くいかない場合は適当にMakefileをいじってみてください。
まずgooglecodeからtsubomiを入手。
$$ svn checkout http://tsubomi.googlecode.com/svn/trunk/ tsubomi
次に、tsubomi、futabaをインストール。
$$ cd tsubomi $$ gmake $$ sudo gmake install $$ cd sample $$ gmake $$ sudo gmake install
インストールしたらサンプルの動作確認。データのフォーマットは
クラス:データの解説(省略可)(\t)素性名:素性値,...
となっている。具体例はvar/color.txtを参照のこと。
warm:red R:255,G:0,B:0 cold:green R:0,G:255,B:0 cold:blue R:0,G:0,B:255 cold:cyan R:0,G:255,B:255 warm:magenta R:255,G:0,B:255 warm:yellow R:255,G:255,B:0
色のRGB値を素性として暖色/寒色で分けるという当ブログでよく使うデータ。このデータを使って学習させる。重みベクトルはヒューリスティック(TF-IDF)によるものと、オンライン線形SVMによるものの2つを用意している。TF-IDFを使う場合は、
$$ futaba_make_tfidf < var/color.txt > var/color.tfidf $$ tsubomi_mkary -l var/color.tfidf
とする。SVMの場合は、第二引数にイテレーション回数を、第三引数に正則化項の係数を指定する。
$$ futaba_make_svm 10 0.1 < var/color.txt > var/color.svm $$ tsubomi_mkary -l var/color.svm
これで学習が済んだので、分類をしてみる。
$$ futaba_match var/color.svm < var/color.txt warm:red warm:23.562,cold:0 cold:green cold:19.635,warm:7.854 cold:blue cold:19.635,warm:7.854 cold:cyan cold:39.27,warm:15.708 warm:magenta warm:31.416,cold:19.635 warm:yellow warm:31.416,cold:19.635
正しく分類できていることが分かる。この例ではsvmを使ったがtfidfの場合も同様。なお素性ベクトルにバイアス項を考慮したい場合は明示的に指定すること(var/color2.txt,var/color3.txtを参照)。
これで解説は終わりだが、これだけでは味気ないのでライトノベルの(コンテンツマッチによる)レコメンデーションをしてみる。電撃文庫の6月の新刊を使って学習し、1-5月までの各作品に対してオススメの6月新刊(っていってももう7月分が発売してしまっているが)をレコメンドする。
まず以下のperlスクリプトで電撃文庫のタイトルと紹介文を取得する。
#!/usr/local/bin/perl use strict; use warnings; use LWP::Simple; my $month = shift @ARGV; my $url = "http://dengekibunko.dengeki.com/archive_bunko/bunko$month.php"; my $c = get($url) or die "cannot get.\n"; my $pre = "<!--data start-->"; my $post = "<!--data end-->"; while ($c =~ /$pre(.+?)$post/gmsi) { my $s = $1; $s =~ /<h3>(.+?)<\/h3>/msi; my $title = cleanup($1); $s =~ /<p>(.+?)<\/p>/msi; my $snipet = cleanup($1); print "$title\t$snipet\n"; } sub cleanup { my $s = shift; $s =~ s/<.+?>//gmsi; $s =~ s/[\t\n]/ /gmsi; $s =~ s/ +/ /gmsi; return $s; }
これをget_dengeki.plとして保存。以下のようにしてデータを取得する。
$$ ./get_dengeki.pl 1101 > dengeki1101-1105 $$ ./get_dengeki.pl 1102 >> dengeki1101-1105 $$ ./get_dengeki.pl 1103 >> dengeki1101-1105 $$ ./get_dengeki.pl 1104 >> dengeki1101-1105 $$ ./get_dengeki.pl 1105 >> dengeki1101-1105 $$ ./get_dengeki.pl 1106 > dengeki1106
次に以下のperlスクリプトでデータから素性ベクトルをつくる。素性には文字3gramを使う(形態素解析はインストールの解説とか書くのめんどくさかったので・・・)。
#!/usr/local/bin/perl use strict; use warnings; use utf8; my $gram = shift @ARGV; while (<>) { chomp; my @a = split(/\t/); my $s = $a[1]; utf8::decode($s); my @cs = split(//, $s); my $n = length($s) - $gram + 1; my %h; for (my $i = 0; $i < $n; $i++) { my $ss = "$cs[$i]$cs[$i+1]$cs[$i+2]"; $ss =~ s/[:,]/ /g; utf8::encode($ss); $h{$ss}++; } my $sep = ""; print "$a[0]\t"; foreach (keys %h) { print "$sep$_:$h{$_}"; $sep = ","; } print "\n"; }
これをmake_ngram.plとして保存。以下のようにして素性ベクトルをつくる。
$$ ./make_ngram.pl 3 dengeki1101-1105 > dengeki1101-1105.txt $$ ./make_ngram.pl 3 dengeki1106 > dengeki1106.txt
これで学習データ(dengeki1106.txt)とテストデータ(dengeki1101-1105.txt)ができたので早速レコメンドしてみる。まずは学習データを使ってtfidfで重みベクトルをつくる。
$$ futaba_make_tfidf.pl dengeki1106.txt > dengeki1106.tfidf $$ tsubomi_mkary -l dengeki1106.tfidf
学習した重みベクトルを使って1-5月分の電撃文庫それぞれに対してオススメの6月新刊を1件ずつレコメンドしてみる。
$ head dengeki1101-1105.txt | futaba_match dengeki1106.tfidf 1 嘘つきみーくんと壊れたまーちゃん10 カミオロシ:0.0425 壊れかけのムーンライト 可愛くなんかないからねっ!:0.0435 オオカミさんと亮士くんとたくさんの仲間たち 血吸村へようこそ(6):0.0534 レイヤード・サマー 俺ミーツリトルデビル!:0.041 ほうかご百物語あんこーる 竜と勇者(あいつ)と可愛げのない私4:0.0228 煉獄姫 二幕 竜と勇者(あいつ)と可愛げのない私4:0.0491 輪環の魔導師8 僕と彼女のゲーム戦争:0.03 なれる!SE3 僕と彼女のゲーム戦争:0.0208 よめせんっ!4 可愛くなんかないからねっ!:0.0205 竜と勇者(あいつ)と可愛げのない私3 竜と勇者(あいつ)と可愛げのない私4:0.2423
なんとなくそれっぽいものが出た気がする。ここでは上から10件分の結果だけ表示させたが、下の方にはソードアート・オンラインに対してアクセルワールドがレコメンドされたりもしていた。
というわけでこのツールを使うと、ちょっとした分類、レコメンデーション、類似文検索ができるよというお話でした。なお性能はSVMのほうが良い気がするが、レコメンデーションのようにクラス数が多い場合はSVMだと学習が重いのでtfidfのほうが良いと思われる。