【Word2Vec】単語の選び方による分類の精度の検討


概要

株式会社サイシードのインターンシップでWord2Vecを利用した単語の分類を行いました。
Word2Vecを用いた単語の分類はQiitaでも様々な記事で紹介されています。
類義語の検索、特徴量ベクトルを用いた計算にとどまらず、単語や文書の分類などへの応用にも利用されています。
Word2Vecを使う大きなモチベーションのひとつに単語や文書の分類があります。
しかし、実際にWord2Vecを使ったグルーピングはどのくらいの精度が期待できるのでしょうか。
この記事では単語集合の"粒度"に着目し、Word2Vecによる単語の分類の精度を検討します。

手順

  • Word2VecをWikipediaデータで学習
  • 異なる単語集合を作成
  • 可視化し分類の精度を検討

環境

Python 3.5.1
mecab 0.996
OS: Amazon Linux AMI release 2015.09

準備

Wikipediaデータを用いてWord2Vecを学習します。

Wikipediaデータを用いた学習

以下の記事を参考にしてWikipediaデータを準備しました。
word2vecをwikipediaコーパスで学習
【Python】Word2Vecの使い方

Wikipediaデータを取得

$ curl https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2 -o jawiki-latest-pages-articles.xml.bz2

WikiExtractor.pyを使ってwikipediaデータをWord2Vecで使える形式に変換

$ git clone https://github.com/attardi/wikiextractor
$ python3 wikiextractor/WikiExtractor.py jawiki-latest-pages-articles.xml.bz2

textディレクトリ下に各ページのテキストが保存されており、以下のコマンドで一つにまとめる。

$ cat text/*/* > wiki.txt

データは2.7GBほどです。
mecabを使って分かち書きする。

$ mecab -Owakati wiki.txt -o wiki_wakati.txt

nkfコマンドを使ってUFT-8に文字形式を揃える。

$ nkf -w --overwrite wiki_wakati.txt

Word2Vecを学習する。

from gensim.models import word2vec
data = word2vec.Text8Corpus("data_wakati.txt")
model = word2vec.Word2Vec(data, size=100, window=5, min_count=5, workers=4)
model.save('wiki.model')
#model = word2vec.Word2Vec.load("wiki.model")

単語集合の”粒度”による分類の精度の検討

ここでは、単語集合の”粒度”を人間の観点でどのくらい関連度の高い単語の集合かという意味で考えます。
例えば、単語と類似度が高い単語を上位から選んだ単語集合は粒度が小さく、
全く異なるジャンルの単語を集めた単語集合は粒度が大きいことになります。
様々な粒度の単語集合を作成し、分類の精度を検討します。

粒度が大きい単語集合

異なるジャンルの単語を集めた単語集合を作成します。
以下の記事のコードを参考に、単語をジャンルごとに人間の観点で分類し、色分けします。参考記事で使われている単語集合を本記事でも使用しています。
それらの単語の特徴量ベクトルを2次元に次元圧縮しプロットします。次元圧縮にはPCAを使っています。
Pythonでword2vecを自在に操って高次元ベクトルを可視化
【word2vec】会社のクチコミを自然言語処理した結果を可視化してみる

def draw_colored_scatter(words):
    word_model = []
    for j in range(0, len(words)):
        word_model.append(model[words[j][0]])
    pca = PCA(n_components=2)
    coords = pca.fit_transform(word_model)
    x = [v[0] for v in coords]
    y = [v[1] for v in coords]

    fig, ax = plt.subplots(figsize=(10, 7))

    for i in range(0, len(words)):
        ax.scatter(x[i], y[i], c=words[i][1])
        ax.annotate(words[i][0], (coords[i][0], coords[i][1]), fontproperties=fp)
    plt.show()
words = []
words.append(["生物","r"])
words.append(["数学","r"])
words.append(["現代文","r"])
words.append(["世界史","r"])
words.append(["物理","r"])
words.append(["豚肉","c"])
words.append(["鶏肉","c"])
words.append(["キャベツ","c"])
words.append(["チーズ","c"])
words.append(["牛乳","c"])
words.append(["卵","c"])
words.append(["白菜","c"])
words.append(["じゃがいも","c"])
words.append(["ニンジン","c"])
words.append(["国土交通省","y"])
words.append(["厚生労働省","y"])
words.append(["外務省","y"])
words.append(["総務省","y"])
words.append(["バス停","m"])
words.append(["電車","m"])
words.append(["新幹線","m"])
words.append(["バス","m"])
words.append(["タクシー","m"])
words.append(["車","m"])
words.append(["自転車","m"])
draw_colored_scatter(words)

プロットされた結果は自分が分類したジャンルごとに分かれています。このようにかなり異なるジャンルの単語どうしであると直観と分類されたモデルがよく似ることがわかります。
反対に粒度が小さい単語集合でプロットしてみます。

粒度が小さい場合

おおよそ似たカテゴリの食品が近くにプロットされていますが、明らかなクラスタは見えません。
そこで、どのくらい異なるジャンルの単語を選ぶと分類の精度が向上するのか検討してみます。

単語集合を変え、分類の精度が上がる集合を見つける

ここでは、単語の”粒度”を具体的に分類語彙表の分類から考えます。

  • 参考図書:「分類語彙表 増補改訂版」、国立国語研究所編、2004年

分類語彙表では各単語が類・部門・中項目などで分類されています。

そこでこの分類表に基づいて単語を選び、クラスタと分類表での分類が一致するような単語集合を実現する粒度を検討します。
この記事では、「1.43 食料」のカテゴリを選び、粒度を粗くしながら単語ベクトルをプロットしてみます。

同じ分類項目の単語集合

「分類番号1.4300:食料」の単語集合から単語を選びプロットします。類義語として記されている単語を同じ色にしています。

クラスタは見えていません。

同じ中項目の単語集合

「1.43 食料」の単語集合から単語を選んでいます。同じ分類番号の単語を同じ色にしています。

似ている単語が近くにプロットされている場合もありますがクラスタはできていません。

同じ部門の単語集合

「1.4 生産物および用具」の単語集合から単語を選んでいます。同じ中項目の単語を同じ色にしています。

おおよそクラスタリングできています。2次元にプロットするときにクラスタが分かる粒度は分類語彙表での部門での分け方以上であることがわかりました。

結論

Word2Vecによる類似度計算を行う際には、単語の選び方の粒度により精度が変わってくることが分かりました。ジャンルが大きく異なる単語であればかなり精度よく分類が可能ですが、近い単語であるほど、分類は難しくなっていきます。
今回分類語彙表を用いて単語の粒度と分類の精度を検討したところ、本手法で特徴ベクトルを可視化する場合、「部門」がクラスタを確認できる最低限の粒度でした。今回特徴ベクトルをPCAを用いて次元圧縮しましたが、そもそもPCAを用いて2次元に圧縮することで正確に特徴量を示せていない可能性があります。今後単語の分類を行う際、次元圧縮手法も検討する必要があります。