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

it's an endless world.

グロースをデザインするひと

ナイーブベイズを利用した自動カテゴリ判定器の開発

statistics python

この記事はGoodpatch Advent Calendar 2016、21日目の記事です。
先日退職ブログを書いたばかりですが「今年も書いて良いよ」と言われ図々しく書いてます。

昨日はえんぴのQAについての記事でした。
qiita.com


私の記事ではタイトルの通り、ナイーブベイズを利用した自動カテゴリ判定器を先日作った話をしたいと思います。

参考にした記事

以下、こちらの記事にかなりお世話になっております。先人すばらしい。
qiita.com

ナイーブベイズって何?

昨年のこのアドベントカレンダーベイズ理論の話をしましたが、その中でも語っているやつです。

migi.hatenablog.com

実は、これはGmailなどのスパムフィルターにも使われている技術です。

  1. 「出会い」や「寂しい」といったメールに含まれている単語それぞれに、その単語が含まれていた時にそのメールがスパムメールである確率を出す
  2. それが一定以上の確率であればスパム認定
  3. ユーザの「このメールはスパムだ」「このメールはスパムではない」ボタンを押した時のアクションを見てベイズ更新
  4. さらに精度が高いフィルターに

この仕組みを「ナイーブベイズ分類器」といいます。ここでの「ナイーブ」の意味は「単純」ぐらいの意味です。特に日本語的なナイーブなものではないです。

これ。

ベイズ理論のこの考え方を利用して「ある文章がどんなカテゴリなのか?」を判定するものを作りました。

ざっくり言うと「恋愛っぽい単語が含まれていたら恋愛カテゴリと判定する」というものです。

なんで作ったの?

グッドパッチで関わっていたあるプロジェクトで「いま運営しているメディアサイトに160,000件くらいの記事があるんだけど、これをカテゴリにわけたい」と言われました。

そのサイトではこれまでカテゴリ分けをしておらず。

160,000件ですよ、160,000件。じゅうろくまんけん。

仮に手動で1件1件やるのであれば、その作業に1件10秒しかかけなかったとしても1,600,000秒=26,666分=444時間かかるわけで。
444時間。一人日8時間換算で55.5人日。1人でやるなら3ヶ月くらいかかりますね

これはやってられん、ということで自動でやってくれるものを作りました。

詳しく教えれ

正解かどうかがわかりやすいように、この記事ではヤフーニュースに上がっているニュースタイトルで判定してみましょう。

このような形で、ニュースタイトルとそのカテゴリを用意しておきます。

id タイトル カテゴリ
1 スマスマ16.3% ビストロ有終 エンタメ
2 設楽ノンストップ欠席 胃腸炎 エンタメ
3 トランプ相場 沈む中国踏み台 経済
4 独のXマス市に車突入12人死亡 国際
5 島根遺棄 死亡の男を書類送検 地域
6 露大使射殺「アレッポ」叫ぶ 国際
7 ASKA 音楽活動を再開へ エンタメ
8 茨城初のプロ球団設立へ準備 スポーツ
9 トランプ氏 選挙人過半数確保 国際
10 病院入り口に男性遺体 神奈川 地域
11 サイバー防衛 ネット演習検討 IT
12 箱根 問われる学生選抜の意義 スポーツ
13 神戸西バイパス 全面開通へ 地域
14 入浴剤飲み49人死亡 ロシア 国際
15 露大使死亡 銃撃はトルコ警官 国際
16 独のXマス市に車突入 9人死亡 国際
17 ASKA氏不起訴 警視庁に衝撃 国内
18 芥川・直木賞 候補10作決まる 国内
19 義父の頭にハサミ 中2逮捕 地域
20 紅白 大トリに嵐が有力 エンタメ

手順(1) 教師となるデータを揃える

先ほどの例で言うと、まずは「恋愛っぽい」を定義する必要があります。

ここでは、ヤフーニュースのカテゴリにあたる「国内・国際・経済・エンタメ・スポーツ・IT・ライフ・地域」っぽさをそれぞれ定義します。

そのためにすること。
そのための考え方。

それは「カテゴリ名を検索エンジンにかけて、上位に出てきた記事に書かれているテキスト内の単語を集めればそれっぽさの定義になるのでは」というものです。

ここではBingのAPIを使ってそれぞれのカテゴリ名で検索してヒットしたページ上位30件内に含まれているテキストを集めました。*1

このように各カテゴリ毎に集めたテキストを今回の自動カテゴリ判定機の「教師」とします。

手順(2) 文章を単語に切り分ける

さて、手順(1)で各カテゴリ別のテキストの集まりはできました。

次に、それぞれのカテゴリの中でどんな単語がよく使われているかを計算していきます。

そこで使うのがmecab
mecabを使うと、例えばこの記事のタイトルである「ナイーブベイズを利用した自動カテゴリ判定器の開発」というテキストは次のように切り分けられます。

ナイーブベイズ	名詞,一般,*,*,*,*,*
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
利用	名詞,サ変接続,*,*,*,*,利用,リヨウ,リヨー
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
自動	名詞,一般,*,*,*,*,自動,ジドウ,ジドー
カテゴリ	名詞,一般,*,*,*,*,カテゴリ,カテゴリ,カテゴリ
判定	名詞,サ変接続,*,*,*,*,判定,ハンテイ,ハンテイ
器	名詞,接尾,一般,*,*,*,器,キ,キ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
開発	名詞,サ変接続,*,*,*,*,開発,カイハツ,カイハツ

助詞や助動詞は今回はあまり必要なさそうなので、名詞・形容詞・動詞あたりだけをここから切り出しました。

ちなみにmecabを使うときは素の状態で使うのではなく、現代語にも広く対応した次の辞書を使うことをオススメします。

github.com

明日はニーアオートマタの体験版の配信日ですね。

明日	名詞,副詞可能,*,*,*,*,明日,アシタ,アシタ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
ニーアオートマタ	名詞,一般,*,*,*,*,*
の	助詞,連体化,*,*,*,*,の,ノ,ノ
体験版	名詞,固有名詞,一般,*,*,*,体験版,タイケンバン,タイケンバン
の	助詞,連体化,*,*,*,*,の,ノ,ノ
配信	名詞,サ変接続,*,*,*,*,配信,ハイシン,ハイシン
日	名詞,接尾,一般,*,*,*,日,ビ,ビ
です	助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
ね	助詞,終助詞,*,*,*,*,ね,ネ,ネ
。	記号,句点,*,*,*,*,。,。,。

このように「ニーアオートマタ」なんかもちゃんと切り分けてくれます。

手順(3) 単語のtf-idf値を計算する

ここで1つ注意があります。

それは「かならずしも、でてくる頻度が高い単語がそのカテゴリを表す単語ではない」ということです。

例えば、「今日」という単語。
これはカテゴリ関係なくどんな文章でもよく出てくる単語ですよね。
逆に「他のカテゴリには出てこないけど、そのカテゴリには出てくる単語」はけっこう重要そうですよね。

この考え方は一般的にあるらしくtf-idfと呼ばれています。
tf-idf - Wikipedia

単語の出現頻度(tf、Term Frequency)と逆文書頻度(idf、Inverse Document Frequency)のとしてのtf-idf値でその単語の重要性を量ります。
より詳しくはこちら。

yukinoi.hatenablog.com

実際に計算したところ、今回の例では国際カテゴリではこのような単語のtf-idf値が高いとでていました。

単語 tf-idf値
国際 1.92836162294
2016年 1.37820817935
1.02771762819
お知らせ 1.00330584285
中国 0.974552824172
一覧 0.846653071552
大使 0.843146415783
ニュース 0.841631941833
更新 0.837028009644
国際郵便 0.792708972889
死亡 0.780085248635
12/20 0.773339699122
トランプ 0.758564567714
情報 0.72689067367
ロシア 0.723163899222
問い合わせ 0.720312233752
トルコ 0.711138885593
サービス 0.701684243827
国連 0.674910954938

手順(4) 類似度を計算する

ここまでが下準備でこれからが本番です。

と言っても後にすることはもうそんなにないです。

類似度の計算は今回はコサイン類似度を見ています。

高校の数学でやったベクトル同士の角度を計算するやつの応用です。
高校数学と違って、2次元ベクトルではなく、単語の種類数だけ次元数があるベクトルでの計算ですが。*2

ベクトルのひとつは、それぞれのカテゴリです。
それと照らし合わせるもうひとつのベクトルは、ニュースタイトルをmecabで分けた単語です。

つまり「スマスマ16.3% ビストロ有終」であればこのようなベクトルが1つできます。

{'スマスマ': 1, 'ビストロ': 1, '有終': 1}

それと、手順(3)で作った各カテゴリのベクトルとを照らし合わせてコサインが1に近いものを類似度が高い、と判断する手法です。

8つのカテゴリと照らし合わせてもっともコサイン類似度が高いものをそのカテゴリである可能性が高いとして判定します。

手順(5) 結果発表

結果、こうなりました。

id タイトル 元のカテゴリ 計算したカテゴリ 正否
1 スマスマ16.3% ビストロ有終 エンタメ エンタメ
2 設楽ノンストップ欠席 胃腸炎 エンタメ エンタメ
3 トランプ相場 沈む中国踏み台 経済 国際
4 独のXマス市に車突入12人死亡 国際 国際
5 島根遺棄 死亡の男を書類送検 地域 国際
6 露大使射殺「アレッポ」叫ぶ 国際 国際
7 ASKA 音楽活動を再開へ エンタメ エンタメ
8 茨城初のプロ球団設立へ準備 スポーツ スポーツ
9 トランプ氏 選挙人過半数確保 国際 国際
10 病院入り口に男性遺体 神奈川 地域 地域
11 サイバー防衛 ネット演習検討 IT IT
12 箱根 問われる学生選抜の意義 スポーツ スポーツ
13 神戸西バイパス 全面開通へ 地域 地域
14 入浴剤飲み49人死亡 ロシア 国際 国際
15 露大使死亡 銃撃はトルコ警官 国際 国際
16 独のXマス市に車突入 9人死亡 国際 国際
17 ASKA氏不起訴 警視庁に衝撃 国内 エンタメ
18 芥川・直木賞 候補10作決まる 国内 国内
19 義父の頭にハサミ 中2逮捕 地域 地域
20 紅白 大トリに嵐が有力 エンタメ エンタメ

17/20。正答率85%という良い感じの結果になりました。

ミスってるやつも、例えばid:17はむしろエンタメじゃないかと。
id:3はまぁ、しょうがない感じだなと。
id:5が国際あつかいされてるのはすみません。おもに島根県の人、すみません。

あと、100%の正答率を期待していた方には申し訳ないです。
でも、機械学習の世界には過学習という言葉があり、学習させすぎると良くないという考え方もありますよ、と言い訳しておきます。
過剰適合 - Wikipedia

そんなこんなで

これまでの人生でPythonをほぼ触ってこなかった&大学で機械学習を専門にしていたわけでもなかった私でもここまで作れたのは、楽しいし嬉しい経験でした。

ソース一式はこちらにおいてあるので興味ある方はどうぞ。
github.com

さて、明日の記事は

非エンジニアであるところのプロジェクトマネージャーのまりおが今年もこのアドベントカレンダーに記事を書いてくれるそうです。お楽しみに。書いてくれました。

qiita.com

最近のPebble社のニュース、マジで残念や……。

おまけ

アドベントカレンダー書くときに毎年毎年言っている気がしますが、あさって12/23(金・祝)は私の誕生日だったりします。
こないだの退職エントリのときにみなさんからいっぱいプレゼントいただきましたが、懲りずにまたクレクレ。
https://www.amazon.co.jp/registry/wishlist/3I7LF4VEPKBA2?sort=priority&view=nullwww.amazon.co.jp

退職祝い&フリーランス祝いを贈りそびれた!って方も

*1:実際には「国内」だけでは意図しないページも多そうだったので、「国内」「国内ニュース」とそれぞれのカテゴリで「◯◯ニュース」みたいな言葉でも検索しています。

*2:ちなみに、上記の「国際」カテゴリには6,230種類の単語があったので6,230次元のベクトルの計算になります