「耳をすませば」の月島雫のセリフは分類できるか?

はじめに

最近、自然言語処理とかちゃんと勉強始めたので、まずはやってみようということで文書分類をやってみた。
本当だったらもっと一般的な題材(ニュースカテゴリ分類やスパムフィルタとか)からやるべきだろうけど、前々から気になってた「映画のキャラクターのセリフは分類できるか?」をやってみることに。何か特徴的な言動(語尾が「〜だわさ」とか「〜だっちゃ」とか)があれば高精度で分類できそうだけど、普通の会話でやるとどうなんだろうと。

ということで、ジブリ映画「耳をすませば」のセリフを用いて、主人公月島雫のセリフを分類可能かどうかを試す。この映画を選んだ理由としては、最近「コクリコ坂から」を見に行って似た感じの「耳をすませば」を見なおしたときに思いついたから(ミーハー)。
今回の目的は、文書情報から素性を取り出して、SVMで学習・予測をする一連の流れを実際にやってみること。素性としては「その単語が含まれているかどうか」。精度向上とかは考えていません。

これができれば、聖司くんbotとか作って雫っぽい発言の人に「雫!大好きだ!!」ってtweetできるんでないのと思う(有用性皆無)。

準備

MeCablibsvmのインストール(Windows)

形態素に分解したいのでMeCabと、分類するためにlibsvmをインストール。

MeCabここからダウンロード&インストール。
libsvmここの「Download LIBSVM」からzipのほうをダウンロード。中にwindowsディレクトリがあって、すでにビルド済みなものがあった。

セリフからの素性抽出

映画を再生しながら全セリフを人力で取り出すことを考えてたけど、「耳をすませば セリフ」とかでググったら普通にでてきたので、それを使わせてもらう。(ここ)
形式がすべて、

名前「セリフ」

となっているので、扱いやすそう。

素性としては、ある単語が含まれる(1)か否か(0)でやってみる(2値ベクトル)。というのは、元のセリフ集を見てみると1つ1つがそんなに長くないので、単語の頻度なbag-of-wordsは意味ないと思ったから。
ということで、名前とセリフを分解して、さらにセリフをMeCab形態素分解してセリフ中にでてくる単語をリストアップ→IDを振る。

//ID0はダミー
#ID	単語
0	$
1	
2	・
3	。
4	、
5	!
6	の
7	て
8	?
9	た
10	だ
11	ん
12	に
13	ない
14	よ
...

標準の辞書&MeCabで分解しただけなので、特徴的な単語じゃないもの(と要らない句読点とか)まで含んでますが、どうせ分類の寄与率はなくなってくれると信じてそのままやります。

次に、各セリフについて上の単語リストを用いてlibsvm用のトレーニングデータを作ります。
形式は、大雑把に(単語IDは適当)

#雫のセリフの場合
雫「ヤッホー、がんばってねー!」
↓
+1 5:1 19:1 25:1 150:1 255:1

#雫以外のセリフの場合
夕子「雫、好きな人いる?」
↓
-1 25:1 30:1 500:1 605:1 1402:1

libsvmは「クラス 特徴番号:値 特徴番号:値 ...」のようになってるので、その形式にする。
(セリフ中、歌を歌う部分で「雫・夕子」のようになっているが、これは雫に含めていない)

できたトレーニングデータはこんな感じ。

//train.txt
1 0:1 1:1 3:1 817:1 
-1 0:1 1:1 3:1 24:1 66:1 916:1 
1 0:1 1:1 3:1 266:1 
-1 0:1 1:1 3:1 440:1 
-1 0:1 1:1 3:1 4:1 17:1 91:1 98:1 157:1 174:1 562:1 1183:1 1361:1 
1 0:1 1:1 3:1 10:1 11:1 50:1 81:1 284:1 
-1 0:1 1:1 3:1 12:1 13:1 25:1 31:1 34:1 65:1 131:1 1151:1 1561:1 
1 0:1 1:1 5:1 8:1 16:1 138:1 1219:1 
-1 0:1 1:1 3:1 4:1 11:1 52:1 234:1 696:1 1283:1 1350:1 
-1 0:1 1:1 3:1 440:1 
...

SVMで学習

ということで、そもそもの目的の「セリフの分類」をやってみる。

//モデルの作成
//  -tは、カーネルの種類(0は線形カーネル、デフォルトはRBFカーネル)
//  -vは、クロスバリデーションの指定(5 fold cross validationしてみた)
$ svm-train -t 0 -v 5 train.txt mimi.model
..*..*
optimization finished, #iter = 4438
nu = 0.464092
obj = -355.293648, rho = -0.014204
nSV = 754, nBSV = 298
Total nSV = 754
...*..*
optimization finished, #iter = 5089
nu = 0.461264
obj = -346.001786, rho = -0.346949
nSV = 754, nBSV = 292
Total nSV = 754
...*.*
optimization finished, #iter = 4237
nu = 0.465348
obj = -348.012405, rho = -0.491231
nSV = 764, nBSV = 292
Total nSV = 764
..*.*
optimization finished, #iter = 3966
nu = 0.441773
obj = -332.220380, rho = -0.148556
nSV = 748, nBSV = 289
Total nSV = 748
..*.*
optimization finished, #iter = 3717
nu = 0.440539
obj = -330.145876, rho = -0.397205
nSV = 721, nBSV = 285
Total nSV = 721
Cross Validation Accuracy = 67.5926%

成功率67%!思っていたより高い。
最初の予想としては、セリフからなんて分類できないと思っていたので、結構いい感じに分類できてるのかもしれない。

あと、最初、カーネルの種類としてデフォルトのRBFカーネルを使ったらCross Validation Accuracyが56.1%で、線形カーネルよりもかなり低かった。これはなんでなんだろう。

まとめ

今回は、文書分類の練習として耳すまのセリフ分類をやってみた。
素性として、時系列などは考えず、「セリフにその単語があるか否か」でやった結果、「そこそこ分類できた」ということができたと思う。ただ、一般的に何%ぐらいできればいいなどの感覚がないので、きちんと評価できないことがわかった。あと、どういう評価結果を出せばいいのかわからない。。

やってみて、いくつか疑問点がでてきたので、以下に列挙。
libsvmの使い方
svm-trainの交差検定を利用して分類精度を見てみたけど、これ使い方あってるかわからない。ちゃんとドキュメント読む。

■データの偏りは大丈夫?
データとして、全セリフ(1298個)から単語リスト(1567単語)を作ってそれを使ったけど、これは多いのか少ないのか。気になったのは、単語の質。日常的なシーンが多いから一般的な単語とかでてきていいと思ったけど、実際はどうなのか。別な人物とか別な映画でやってみてもいいかも。

■「、」や「!」、「の」や「が」などを除かずにやってよかったのか?
上記のようなストップワードを除かずにやったけど、この場合取り除いたほうがよかった気がする。でも、どの程度影響与えるかはやってみないとわからないかな。

■なぜ使用するカーネルにより大きく結果が変わったのか?
SVMがあんまりわかってないので、そんなに変わるの?って感じだったけど、10%以上変わってびっくりした。文書な分類は線形カーネルが結果がよくなるけど、それはなんでかわからない。先輩に聞いてみたら特徴ベクトルが疎だからうんたらかんたらじゃないかと教えてもらったので、ちょっと調べてみる。