はじめに
Apitoreで公開しているWord2VecのAPIを使って、任意の文書の代表語(文書ラベル)を作れないか検討してみました。アルゴリズムを考えてやってみましたがうまくいかなかったので、記録として残しておくことにします。
従来手法
文書とその文書につける正解ラベルを学習データとして準備して、機械学習で解決するのが従来手法ですね。「文書分類」や「タグ付け」などのキーワードで検索すると色々と出てきます。あとは単純にTFIDF (Term Frequency, Inverse Document Frequency) を計算するのもアリです。文書集合を一気にラベリングするとしたら、k-meansなどのクラスタリング手法を用いてクラスタリングし、そのクラスタの代表語をつけるというのもアリです。またLDA (Latent Dirichlet Allocation) などのトピックモデリングを使うのも良いですね。いずれにせよ、精度が良ければ何でも良いです。・・・が、そんなにうまくいかないですね。最近だと深層学習を使うと思いますが、詳しくないので割愛します。Qiitaで見つけた記事によると95%の精度が出ているようですが、学習データで評価しているようにみえるので実際の精度はもっと低いでしょう。
環境
- kuromoji-ipadic-neologd
- DF (Document Frequency) は日本語Wikipediaで作成、上記形態素解析で単語分割、記事=ドキュメント
- word2vec
アルゴリズム
文書は単語の集合なので、単語から文書の特徴ベクトルが作れます。実際、Word2Vecの発展系でParagraph Vectorというものが提案されています。こちらのブログが分かりやすいので引用しておきます。 今回、私が考えたアルゴリズムは以下になります。文書からN語の文書ラベルを作成します。
- 文書の構成単語のtfidfを求める
- 構成単語をword2vecし、tfidfで重み付ける
- すべてのword vectorを加算し、paragraph vectorとする
- tfidf上位N-1語を文書ラベルとし、N-1語のword vectorをparagraph vectorから減算する
- 残ったparagraph vectorに一番近い単語1語を文書ラベルに追加する
以上のアルゴリズムから分かるとおり、基本的にTFIDFでラベルを作ります。工夫した点は、TFIDFで表現しきれなかった特徴をword2vecで補おうとした点です。
検証結果
ためしに、Wikipediaにある「アークザラッド」の記事の一部を入力してみます。文書ラベルは10 (9語がtfidf上位、1語が補完語)。出力にある"distance"の値は、文書全体のparagraph vectorと各文書ラベルのword vectorとのcosine距離です。
"input":"本作は、主人公のアークを中心に物語が展開されるファンタジー調のシミュレーションRPGである。スピーディーなストーリー展開、声優によるキャラクターボイス演出、無数のやりこみ要素が特徴である。キャッチコピーは「光と音のRPG」。『ポポロクロイス物語』『ワイルドアームズ』とともにSCE三大RPGとして宣伝された[1]。ゲーム内の音楽はT-SQUAREの安藤まさひろ。本作は後述のようにシリーズ化もされているが、本作におけるストーリー自体も続編を意識した内容となっており、物語は未完のままエンディングを迎える。これは、PlayStation発売から半年の時期でRPGを出さなければいけないという前提で開発していたにもかかわらず開発が遅れていたため、その時点で出来上がっていた部分までをとりあえず一本のソフトとして出したという事情があるため[2]。そのためゲームクリア時にセーブしたデータはキャラクターのレベル、入手したアイテムなど多くの要素を次回作にコンバートする(引き継ぐ)ことができる(「アークザラッドIIコンバート」も参照)。『II』の発売前後にはザ・ベスト版が週間売上チャートのトップ10内でロングヒットした。また、ゲームアーカイブスや携帯アプリ用ゲームとして移植され、本作を題材にした漫画や小説も刊行された。さらに、1999年には直接の続編『アークザラッドII』を原作にしてテレビアニメ化もされている。"
"distances":[{"word":"RPG","distance":0.7172316312789917},{"word":"する","distance":0.5406280159950256},{"word":"T-SQUARE","distance":0.4660545289516449},{"word":"PlayStation","distance":0.6229082942008972},{"word":"シミュレーションRPG","distance":0.6894579529762268},{"word":"WA","distance":0.3161240220069885},{"word":"TVアニメ","distance":0.5569940209388733},{"word":"SCE","distance":0.5506512522697449},{"word":"本","distance":0.5027557611465454},{"word":"藤成通","distance":0.7799928188323975}]
少し見づらいですが、tfidfでは「RPG」「する」「T-SQUARE」「PlayStation」「シミュレーションRPG」「WA」「TVアニメ」「SCE」「本」と概ね特徴的な単語は取れています。 ※個人的には「コンバート」とか取れてほしかったです。 それで肝心の補完語は・・・「藤成通」・・・なんでしょう?調べてみましたが、名古屋のどこかの通りですかね? 続いて、今流行りの「逃げ恥」を行ってみましょう!
"input":"大学院を出ながらも就職難で派遣社員になった森山みくりは、いわゆる派遣切りに遭い、無職の身となってしまう。求職中の娘を見かねた父は、家事代行サービスを利用していた元部下・津崎平匡が折りよく代行の会社を替えようとしていたところを頼み込んで、週1回の仕事を取り付けてくる。気難しい性格で、あまり他人に構われることを好まない津崎だったが、みくりとは適度な距離感を保って良好な関係を築く。だが、定年を機に田舎へ引っ越すという願望を両親が叶えることになり、現状を維持したいみくりは津崎に「就職としての結婚」を持ちかけ、その提案にメリットを感じた津崎は了承し、2人は契約結婚という道を選ぶ。"
"distances":[{"word":"津崎","distance":0.5836473703384399},{"word":"くり","distance":0.397651344537735},{"word":"する","distance":0.5922220349311829},{"word":"家事代行","distance":0.6124579906463623},{"word":"なる","distance":0.55706387758255},{"word":"派遣切り","distance":0.5810062885284424},{"word":"構う","distance":0.7273969650268555},{"word":"契約結婚","distance":0.5345048904418945},{"word":"求職","distance":0.5992125868797302},{"word":"広島県尾道市美ノ郷町三成","distance":0.8804238438606262}]
逃げ恥は一度も見ていないのでさっぱりわかりませんが、TFIDFによると「津崎」「くり」「する」「家事代行」「なる」「派遣切り」「構う」「契約結婚」「求職」だそうです。「くり」はたぶん「みくり」が途中で切れてしまったんでしょう。入力文章の特徴は捉えていそうです。 ※そもそも入力文章があまりよく理解できないのですけど・・・笑。 補完語は・・・「広島県尾道市美ノ郷町三成」・・・地名ですね。逃げ恥のロケ地でもなさそうです。
考察
まず断っておく必要があるのは、そもそも今回計算したParagraph Vectorは私が勝手に作ったParagraph Vectorでこちらで解説されているものとは全く異なります。 それで結果についての考察ですが、考えてみれば当然でした。今回作ったParagraph Vectorはいろんな単語のword vectorの重ね合わせです。word vectorを加算すればparagraph vectorになるというだけです。意味を考慮しているわけではありません。Paragraph vectorとword vectorを比較すること自体がナンセンスなのです。 なんとかword2vecを使って文書ラベルを作れないかなと検討しましたが、構成単語のword vectorをクラスタリングするという、ありがちなアイデアしか浮かびませんでした。
おわりに
今回は技術開発の失敗例を紹介しました。とりあえずやってみて失敗するというのも勉強になるので良いですね。結果が残念なので、今回紹介した技術をAPIで公開するのはやめておきます。代わりのAPIを開発していますので、近々ご紹介致します。