2015/08/11

MeCabにWikipediaの単語を追加する

このエントリーをはてなブックマークに追加
ニュースコンテンツに含まれる用語・単語を抽出するような、いわゆるキーワード抽出がやりたいなあと思ったので、その過程で試せるMeCabとWikipediaの単語登録をやってみました。
達成目標はあんまり高くなくていい。それっぽいことができたらいいかなくらいで、さくっといろいろ動かしてみるというところを目標にしています。
参考にしているサイトや記事が2011年なので、なんとも今更って技術なのかもしれません。MeCabもWikipediaもすごいってことですね。
Windows向け、使用言語はC#です。

ざっと紹介する内容は下の通りです。
  1. MeCabをインストールする
  2. Wikipediaのページタイトルを辞書登録する(ダウンロード→CSVに変換→辞書ファイルに変換→辞書ファイルを登録する→まずは動かしてみる)
  3. Wikipediaから除外したほうがいい単語を考える

MeCabを導入してみる

MeCabをインストールします。

MeCab - Wikipedia
https://ja.wikipedia.org/wiki/MeCab

MeCabとは
文章を解析して、名詞や動詞、形容詞などの文節に区切ってくれるソフトです。
形態素解析と呼ばれます。

こういう感じです。
すもももももももものうち
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS

MeCabのWindows版をインストールします

以下のサイトからWindows版のexeファイル「mecab-0.98.exe」をダウンロードする。
http://sourceforge.net/projects/mecab/files/

「mecab-win32」→「0.98」→「mecab-0.98.exe」をダウンロードします。
インストール時に聞いてくる文字コードは、UTF-8を選択しました。

MeCabのWindows版をインストールしてみる : 走りながら考えるエンジニア
http://handsrecs2nd.seesaa.net/article/140090025.html

MeCabを使ってみる

とりあえず実行してみる。
MeCabを実行するためにバッチファイルを作ります。

"C:\Program Files (x86)\MeCab\bin\mecab.exe" in.txt -o out.txt

in.txtは、解析した文章を入れて、テキストファイルにしたものです。
バッチファイルで上を実行すると解析結果がout.txtに出力されます。

入力テキストはニュースから拝借しました。他意はありません。

人工知能 創造的な仕事奪う?(2015年8月9日(日)掲載) - Yahoo!ニュース
http://news.yahoo.co.jp/pickup/6170034

実行結果はこのようになります。
入力テキスト
人工知能研究に、新たにディープラーニングというアイデアが出現。『ターミネーター』や『攻殻機動隊』など、空想の世界が現実味を帯びてきた。
出力結果
人工 名詞,一般,*,*,*,*,人工,ジンコウ,ジンコー
知能 名詞,一般,*,*,*,*,知能,チノウ,チノー
研究 名詞,サ変接続,*,*,*,*,研究,ケンキュウ,ケンキュー
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
、 記号,読点,*,*,*,*,、,、,、
新た 名詞,形容動詞語幹,*,*,*,*,新た,アラタ,アラタ
に 助詞,副詞化,*,*,*,*,に,ニ,ニ
ディープラーニング 名詞,固有名詞,組織,*,*,*,*
という 助詞,格助詞,連語,*,*,*,という,トイウ,トユウ
アイデア 名詞,一般,*,*,*,*,アイデア,アイデア,アイデア
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
出現 名詞,サ変接続,*,*,*,*,出現,シュツゲン,シュツゲン
。 記号,句点,*,*,*,*,。,。,。
『 記号,括弧開,*,*,*,*,『,『,『
ターミネーター 名詞,固有名詞,一般,*,*,*,ターミネーター,ターミネーター,ターミネーター
』 記号,括弧閉,*,*,*,*,』,』,』
や 助詞,並立助詞,*,*,*,*,や,ヤ,ヤ
『 記号,括弧開,*,*,*,*,『,『,『
攻 名詞,固有名詞,人名,名,*,*,攻,オサム,オサム
殻 名詞,一般,*,*,*,*,殻,カラ,カラ
機動 名詞,一般,*,*,*,*,機動,キドウ,キドー
隊 名詞,接尾,一般,*,*,*,隊,タイ,タイ
』 記号,括弧閉,*,*,*,*,』,』,』
など 助詞,副助詞,*,*,*,*,など,ナド,ナド
、 記号,読点,*,*,*,*,、,、,、
空想 名詞,サ変接続,*,*,*,*,空想,クウソウ,クーソー
の 助詞,連体化,*,*,*,*,の,ノ,ノ
世界 名詞,一般,*,*,*,*,世界,セカイ,セカイ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
現実味 名詞,一般,*,*,*,*,現実味,ゲンジツミ,ゲンジツミ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
帯び 動詞,自立,*,*,一段,連用形,帯びる,オビ,オビ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
き 動詞,非自立,*,*,カ変・クル,連用形,くる,キ,キ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。 記号,句点,*,*,*,*,。,。,。
EOS


一応さくっと動きました。
これでも画期的で楽しいですが、分断され過ぎて残念。
人工知能、攻殻機動隊が残念ですね。
辞書として単語を追加してあげることで、この辺は回避できます。
しかし、単語一個一個を手作業で辞書を作るのは大変なので、Wikipediaの単語リストを拝借して登録してみます。

Wikipediaのタイトルリストから辞書を作る


Wikipediaのタイトルリストのダウンロード
Wikipediaから見出しリストをダウンロードします。
こちらからダウンロードします。
jawiki-latest-all-titles-in-ns0.gz
http://download.wikimedia.org/jawiki/latest/

wikipediaのタイトルリスト(jawiki-latest-all-titles-in-ns0.gz)をこちら(http://download.wikimedia.org/jawiki/latest/)からダウンロード。適当なディレクトリ(home/foo/bar)に解凍します。

fukushimuのメモ帳 [mecab] mecab辞書にwikipediaのタイトルリストを追加 [wikipedia][はてなキーワード]
http://fukushimu.blog.shinobi.jp/mecab/-mecab-%20mecab%E8%BE%9E%E6%9B%B8%E3%81%ABwikipedia%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E8%BF%BD%E5%8A%A0%20-wikipedia--%E3%81%AF%E3%81%A6%E3%81%AA%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89-

なお、Windowsでgzファイルを解凍するには、それなりの解凍ソフトが必要です。ご注意ください。
自分は、Lhaplusで解凍しました。
HoeHoe.com - Lhaplus
http://www7a.biglobe.ne.jp/~schezo/

Wikipedia見出しリストからCSVを作成
見出しリストは、155万行くらいの単語リストです。
こんなんです。

辞書ファイルに変換する前に、ちょっとだけ内容のケアをしてあげます。
CSVから辞書ファイル(.dic)を作成するのに、カンマが単語内に含まれているものは、別の文字に置き換えるなどの対応が必要なのです。
また、後述しますが、キーワードとしてあってもどうしようもない単語も含まれていますので、これを除外するにも、そのまま使うのではなくて、なんらかの置き換えが必要になってくるでしょう。

コンソールアプリとして、さくっとC#で書きました。

using System.IO;
using System.Text.RegularExpressions;

static void Main(string[] args) {
    // 引数の1個目をWikipediaのオリジナルデータとする
    
    if(args.Length == 0 || !File.Exists(args[0])){
        return; // 指定なしかファイルなし
    }

    string wikipedia = args[0];
    string csv = "wikipedia.csv";

    StreamReader reader = new StreamReader(wikipedia, Encoding.UTF8);
    StreamWriter writer = new StreamWriter(csv, false, Encoding.UTF8);
    try{
        while (!reader.EndOfStream) {
            string line = reader.ReadLine();
            int length = line.Length;

            double max = Math.Max(-36000, -400 * Math.Pow(length, 1.5));
            if (length > 1){
                line = line.Replace(',', '_'); // カンマを置換

                writer.WriteLine("{0},0,0,{1},名詞,固有名詞,*,*,*,*,{0},*,*,wikipedia_word,", line, max);
            }

        }
    }catch(Exception ex){
        Console.WriteLine(ex.Message);
    }finally{
        reader.Close();
        writer.Close();
    }

    Console.WriteLine("おわり!");
    Console.ReadKey();
}

カンマ区切りなので、カンマがあるワードだけ置換しておきます。

はい、これで、実行。
100万行以上あるので、計測用にタイマー仕込もうかなとも思ったけれど、動かしてみたら10秒くらいで終わりました。このままでいいことにします。
ただし、出来上がったwikipedia.csvファイルは170MBくらいある。なかなかでかいですね。

CSV(wikipedia.csv)を辞書(.dic)に変換
辞書ファイルをつくるには、MeCabをインストールしたところにある、mecab-dict-index.exeを使うらしい。

"C:\Program Files (x86)\MeCab\bin\mecab-dict-index.exe" -d "C:\Program Files (x86)\MeCab\dic\ipadic" -u wikipedia.dic -f utf8 -t utf8 wikipedia.csv

上を実行。
できた。
wikipedia.dicファイル、223MBでした。(サイズは目安です)

作った辞書をMeCabに登録する
eCabインストール先のetcフォルダにあるmecabrcファイルを書き換えます。

自分のところでは、C:\Program Files (x86)\MeCab\etc\mecabrcでした。
mecabrcは何の拡張子とかついてませんが、メモ帳かなんかで編集すればいいかと。

userdic = "(dicのあるディレクトリ)\wikipedia.dic"
を追加して上書きする。

MeCabを走らせてみる(検証その1)
さあ、実行してみましょう!
ぶっこんでみます。
使う文章は、先ほどと同じテキストです。

入力テキスト
人工知能研究に、新たにディープラーニングというアイデアが出現。『ターミネーター』や『攻殻機動隊』など、空想の世界が現実味を帯びてきた。
Wikipediaを辞書に登録した出力結果
人工知能 名詞,固有名詞,*,*,*,*,人工知能,*,*,wikipedia_word,
研究 名詞,サ変接続,*,*,*,*,研究,ケンキュウ,ケンキュー
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
、 記号,読点,*,*,*,*,、,、,、
新 名詞,一般,*,*,*,*,新,シン,シン
たに 名詞,固有名詞,*,*,*,*,たに,*,*,wikipedia_word,
ディープラーニング 名詞,固有名詞,*,*,*,*,ディープラーニング,*,*,wikipedia_word,
とい 名詞,固有名詞,*,*,*,*,とい,*,*,wikipedia_word,
う 感動詞,*,*,*,*,*,う,ウ,ウ
アイデア 名詞,固有名詞,*,*,*,*,アイデア,*,*,wikipedia_word,
が 接続詞,*,*,*,*,*,が,ガ,ガ
出現 名詞,サ変接続,*,*,*,*,出現,シュツゲン,シュツゲン
。 記号,句点,*,*,*,*,。,。,。
『 記号,括弧開,*,*,*,*,『,『,『
ターミネーター 名詞,固有名詞,*,*,*,*,ターミネーター,*,*,wikipedia_word,
』 記号,括弧閉,*,*,*,*,』,』,』
や 助詞,並立助詞,*,*,*,*,や,ヤ,ヤ
『 記号,括弧開,*,*,*,*,『,『,『
攻殻機動隊 名詞,固有名詞,*,*,*,*,攻殻機動隊,*,*,wikipedia_word,
』 記号,括弧閉,*,*,*,*,』,』,』
など 助詞,副助詞,*,*,*,*,など,ナド,ナド
、 記号,読点,*,*,*,*,、,、,、
空想 名詞,固有名詞,*,*,*,*,空想,*,*,wikipedia_word,
の 助詞,連体化,*,*,*,*,の,ノ,ノ
世界 名詞,固有名詞,*,*,*,*,世界,*,*,wikipedia_word,
が 接続詞,*,*,*,*,*,が,ガ,ガ
現実 名詞,固有名詞,*,*,*,*,現実,*,*,wikipedia_word,
味 名詞,一般,*,*,*,*,味,アジ,アジ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
帯び 動詞,自立,*,*,一段,連用形,帯びる,オビ,オビ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
き 動詞,非自立,*,*,カ変・クル,連用形,くる,キ,キ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。 記号,句点,*,*,*,*,。,。,。
EOS

いい感じ。
攻殻機動隊はいけました。だけど、wikipedia_wordが邪魔してますね。

たに 名詞,固有名詞,*,*,*,*,たに,*,*,wikipedia_word,
とい 名詞,固有名詞,*,*,*,*,とい,*,*,wikipedia_word,
こういうのが引っかかってしまうのとややこしい。
固有名詞じゃないのに固有名詞扱いされている語句がヒットしてますね。
こういうのは、あらかじめ除外しておくすることにしましょう。

Wikipediaの見出しリストの除外条件を考える


省ける語句がものすごいありそうなので、除外条件を考えます。

どっちかというと、わりときつめに条件設定しておくほうがいい気がします。
Wikipediaの追加単語に求めているのは、長い単語を優先的にくっつけてほしいというものだからですね。
短すぎる単語とか、マイナーすぎるのはわりとどうでもいい。
条件によって省かれてしまうものは、あんまり優先的な用語ではなかったと諦めてしまってもいい。

記号から始まっている
記号から始まる単語は除外。 ただし、拡張子の登録はあってもいいので.だけ許す。 [!"#$%&'()\*\+\-\.,\/:;<=>?@\[\\\]^_`{|}~]
数字は除外
ただの数字が単語としてあるので除外。 ただし0から始まるものだけ残す。007とかあるから。
_アンダーバー+(曖昧さ回避)は除外
曖昧さ回避のためのページ用のタイトルです。 いらないので除外。
_アンダーバー+([カテゴリ])は除外



括弧内でカテゴリが指定されているようです。
これで単語を登録していても無駄なので除外。

_ アンダーバー入りは除外



Wikipediaのタイトルルールなのでしょう。アンダーバーでサブタイトルを指定していうようです。
このページタイトルで単語がヒットすることは絶対にないと言えるので、アンダーバー入りは除外としてしまいます。
(曖昧さ回避)、([カテゴリ])も含めて除外対象にしてしまいます。

数字+年、数字+年代は除外しない



1030年とかが固有名詞として登録されるのはどうかと思うけれど、キーワードとして考えるなら一単語として抽出できるほうが有用な気がするので残す。

数字+年+出来事は除外



「1935年全豪テニス選手権」というタイトルがあるようです。
こういうのは除外することにします。単語として絶対ヒットしないから。

数字+月+数字+日(日付)は除外しない



固有名詞としてはもちろん不適切。違和感ありますが、西暦と同じで、キーワードとして考えるなら抽出できるほうがいいので残す。

ひらがな2文字を省く



ひらがな2文字で、Wikipediaにしかない言葉というのをあんまり期待しなくてよさそう。
もともとある辞書で網羅できているものとする。
上の出力結果でも出ているけれど、「たに」「とい」を辞書から除外する。

条件のとこだけだと、ソースはこんな感じですかね。

if (length <= 1){

} else if (line.IndexOf("_") >= 0) {
    // アンダーバー入りは除外
} else if (Regex.IsMatch(line, "^[1-9][0-9]*$")) {
    // 数字
    // 0から始まる数字は残す
} else if (Regex.IsMatch(line, "^[0-9]+[年][代]?$")) {
    // 数字+年
    writer.WriteLine("{0},0,0,{1},名詞,固有名詞,*,*,*,*,{0},*,*,wikipedia_year,", line, max);
} else if (Regex.IsMatch(line, "^[0-9]+[年][^代]+$")) {
    // 数字+年+文字
} else if (Regex.IsMatch(line, "^[0-9]+[月][0-9]+[日]$")) {
    writer.WriteLine("{0},0,0,{1},名詞,固有名詞,*,*,*,*,{0},*,*,wikipedia_date,", line, max);
    // 日付
} else if (Regex.IsMatch(line, "^[!\"#$%&'()\\*\\+\\-\\,\\/:;<=>?@\\[\\\\\\]^_`{|}~]")) {
    // 記号から始まっている(.「ドット」以外)
} else if (Regex.IsMatch(line, "^[ぁ-ん]{2}$")) {
    // ひらがな2文字
} else { 
    writer.WriteLine("{0},0,0,{1},名詞,固有名詞,*,*,*,*,{0},*,*,wikipedia_word,", line, max);
}

除外条件は、年や日付を単語に入れるかどうかなど好みで決めればいいかと。条件はあくまでご参考に。

コストについて

double max = Math.Max(-36000, -400 * Math.Pow(length, 1.5));

この謎の式は参照元のサイトにならって、そのまま使っています。どうやらコストと呼ばれるものらしいです。
値が小さいほど、出現率が高い。
つまりこの式は、長い単語ほど重宝しようという意味のようです。なんか解らないでもない。
しかし、数式自体はよく解らないのでそのまま触らないことにします。
http://mecab.googlecode.com/svn/trunk/mecab/doc/dic.html
コストは,その単語がどれだけ出現しやすいかを示しています. 小さいほど, 出現しやすいという意味になります. 似たような単語と 同じスコアを割り振り, その単位で切り出せない場合は, 徐々に小さくしていけばいいと思います。


はい、実行。
まあまあ。
20秒くらい掛かりました。
できたファイルは130MBくらいあるなあ。気にしてても仕方ないからいいや。
同じように、これで辞書ファイルをつくります。

MeCabを走らせてみる(検証その2)
そして、さっきのテキストもう一回実行してみる。

入力テキスト
人工知能研究に、新たにディープラーニングというアイデアが出現。『ターミネーター』や『攻殻機動隊』など、空想の世界が現実味を帯びてきた。
Wikipedia辞書を使った出力結果
人工知能 名詞,固有名詞,*,*,*,*,人工知能,*,*,wikipedia_word,
研究 名詞,サ変接続,*,*,*,*,研究,ケンキュウ,ケンキュー
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
、 記号,読点,*,*,*,*,、,、,、
新た 名詞,形容動詞語幹,*,*,*,*,新た,アラタ,アラタ
に 助詞,副詞化,*,*,*,*,に,ニ,ニ
ディープラーニング 名詞,固有名詞,*,*,*,*,ディープラーニング,*,*,wikipedia_word,
という 助詞,格助詞,連語,*,*,*,という,トイウ,トユウ
アイデア 名詞,固有名詞,*,*,*,*,アイデア,*,*,wikipedia_word,
が 接続詞,*,*,*,*,*,が,ガ,ガ
出現 名詞,サ変接続,*,*,*,*,出現,シュツゲン,シュツゲン
。 記号,句点,*,*,*,*,。,。,。
『 記号,括弧開,*,*,*,*,『,『,『
ターミネーター 名詞,固有名詞,*,*,*,*,ターミネーター,*,*,wikipedia_word,
』 記号,括弧閉,*,*,*,*,』,』,』
や 助詞,並立助詞,*,*,*,*,や,ヤ,ヤ
『 記号,括弧開,*,*,*,*,『,『,『
攻殻機動隊 名詞,固有名詞,*,*,*,*,攻殻機動隊,*,*,wikipedia_word,
』 記号,括弧閉,*,*,*,*,』,』,』
など 助詞,副助詞,*,*,*,*,など,ナド,ナド
、 記号,読点,*,*,*,*,、,、,、
空想 名詞,固有名詞,*,*,*,*,空想,*,*,wikipedia_word,
の 助詞,連体化,*,*,*,*,の,ノ,ノ
世界 名詞,固有名詞,*,*,*,*,世界,*,*,wikipedia_word,
が 接続詞,*,*,*,*,*,が,ガ,ガ
現実 名詞,固有名詞,*,*,*,*,現実,*,*,wikipedia_word,
味 名詞,一般,*,*,*,*,味,アジ,アジ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
帯び 動詞,自立,*,*,一段,連用形,帯びる,オビ,オビ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
き 動詞,非自立,*,*,カ変・クル,連用形,くる,キ,キ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。 記号,句点,*,*,*,*,。,。,。
EOS
ほぼほぼいい感じです!
いやー、おもしろーい!

単語の除外条件やら、キーワード抽出するためにもっと都合のいいやりかたがあると思うので、さらにここからの調整は必要かもしれません。
しかし、まずはさくっと動かすことができたのでよしとします。

以上です。ではでは。