簡単分類ツールfutabaを作ってみた

しばらく前に作ったライブラリtsubomiのサンプルとして簡単分類ツールfutabaを作ってみた。あくまでtsubomiのサンプルなので高性能というわけではないが、ちょっとしたマイニングに使うには便利かもしれない。
本ツールは事前に用意した学習データ(クラス名と素性ベクトルのペア集合)から重みベクトルを学習させ、新しい入力(素性ベクトル)を学習時に与えたクラスのいずれかに分類するという一般的な分類ツールとなっている。クラスをアイテムとみなすことでレコメンデーション、類似文検索用途でも利用が可能。
以下使いかたをメモしておく。

参考:
CSAを使った全文検索ライブラリtsubomiを公開してみる - EchizenBlog-Zwei

注:tsubomiはUnixLinuxMacでの利用を想定しています。これらの環境なら動作すると思いますが、もし上手くいかない場合は適当に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月分が発売してしまっているが)をレコメンドする。

http://dengekibunko.dengeki.com/archive_bunko/index.php

まず以下の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のほうが良いと思われる。