Ahogrammer

Deep Dive Into NLP, ML and Cloud

Pythonで単語分散表現のクラスタリング

最近の自然言語処理では、単語の分散表現は当たり前のように使われています。 単語分散表現では、各単語が高次元ベクトル空間に配置され、加減乗除等の演算を行えるようになります。 これらのベクトルは、意味の近い単語に対しては同じようなベクトルになることがわかっています。

本記事では、単語分散表現のベクトルをクラスタリングし、意味が近い単語のクラスタを作ってみたいと思います。 これらのクラスタは、眺めて楽しむだけでなく、機械学習の素性として使うこともできます。

イメージ的には、以下のような感じで単語をクラスタにまとめます。 f:id:Hironsan:20171101055538g:plain

では、単語分散表現をクラスタリングして、単語のクラスタを作ってみましょう。

準備

まずは、作業用のディレクトリを作成しておきましょう。 また、必要に応じて Python の仮想環境も用意します。 以下のコマンドを実行することで、ディレクトリを用意します。

$ mkdir workspace
$ cd workspace

以降では、このディレクトリの中で作業を進めていきます。

単語分散表現のダウンロード

次に、クラスタリングに使う単語分散表現を用意します。 分散表現は自分で学習させて用意してもいいのですが、今回はクラスタリングが目的なので、手間を減らすために学習済みの分散表現を使いましょう。 以下のコマンドを実行して、分散表現をダウンロードします。

$ wget http://public.shiroyagi.s3.amazonaws.com/latest-ja-word2vec-gensim-model.zip
$ unzip latest-ja-word2vec-gensim-model.zip

こちらの分散表現は、WikipediaMeCab分かち書きして用意した文書に対して学習されています。 ちなみに、形態素解析の辞書には NEologd を用い、分散表現の次元数は50次元です。 詳細は、以下のサイトを確認してください。

これで必要なデータは揃いました。では単語分散表現をクラスタリングしてみましょう。

単語分散表現のクラスタリング

ここでは、用意した単語分散表現をクラスタリングします。 クラスタリングアルゴリズムには、直感的でよく使われている K-Means を使うことにします。 K-Means がどういうアルゴリズムなのかは以前に以下の記事を書いたので、そちらを参照してください。

まずは、必要なモジュールをインポートします。 単語分散表現の読み込みには gensim を、K-Means は scikit-learn の実装を使うことにしましょう。 インストールしていなかった場合、pip install gensim scikit-learnでインストールしてください。

from collections import defaultdict
from gensim.models.keyedvectors import KeyedVectors
from sklearn.cluster import KMeans

モジュールをインポートしたら、gensim を用いて単語分散表現を読み込みます。

model = KeyedVectors.load('word2vec.gensim.model')
max_vocab = 30000
vocab = list(model.wv.vocab.keys())[:max_vocab]
vectors = [model.wv[word] for word in vocab]

ここで、単語数は30000語に制限しています。 単語数に制限をかけているのは、単語数が多すぎると、クラスタリングの際のメモリ消費量が非常に増え、計算時間もかかるためです。 分散表現を読み込んだ後、あとで使うために語彙とベクトルを取り出しています。

取り出したベクトルに対して、K-Means でクラスタリングを行います。

n_clusters = 1000
kmeans_model = KMeans(n_clusters=n_clusters, verbose=1, random_state=42, n_jobs=-1)
kmeans_model.fit(vectors)

ここで、クラスタn_clusters1000に設定しています。 K-Means ではこのクラスタ数の設定がなかなか難しく、クラスタ数を決定する方法がいくつか提案されています。 今回は、そのような方法は用いず、適当に設定しました。

単語分散表現のクラスタリングが終了したら、クラスタリングして割り当てたクラス情報を使って単語をまとめ上げます。

cluster_labels = kmeans_model.labels_
cluster_to_words = defaultdict(list)
for cluster_id, word in zip(cluster_labels, vocab):
    cluster_to_words[cluster_id].append(word)

ここでは、各クラスタIDに属する単語をまとめています。

以上で単語分散表現のクラスタリングは終了です。結果を確認してみましょう。

結果の確認

K-Means によって生成された各クラスタの単語を見てみましょう。

for words in cluster_to_words.values():
    print(words[:10])

以下のようなクラスタが得られました。

['シャッター通り', '多様化', '車社会', '観光産業', 'かげり', '大衆化', '郊外化', '都心回帰']

['シュヴァルツシルト半径', '実視等級', '天文単位', 'プロミネンス', '矮星', 'アルゴ座', '銀河', 'ベスタ', 'アルター', '粒状斑']

['面相', '小島', '沖田総司', '武勇伝', '宮本武蔵', '神宮司', '明智', '弁慶', '護廷十三隊', '伊賀忍者']

['広瀬神社', '神明社', '西光寺', '如意寺', '吉田神社', '宇都宮二荒山神社', '稲荷山', '峰山', '小社', '鞍馬寺']

['院内交渉団体', '新党さきがけ', '維新の会', '議員団', '山岡賢次', '郵政造反組', 'みどりの風', '改進党', '新民党', '公明党']

['京都駅', '大和西大寺駅', '宇都宮駅', '向ヶ丘遊園駅', '市内電車', '津駅', '呉線', '永福町', '十三駅', '八戸駅']

['22.6%', '62%', '9.7%', '78%', '21.5%', '20.6%', '21.3%', '35.1%', '20.8%', '4.4%']

...

なかなか良さそうな感じですね! これなら、機械学習の素性として使うことができそうです。

おわりに

本記事では、日本語の単語分散表現をクラスタリングする方法を紹介しました。 クラスタリングした結果を機械学習の素性として使うことも考えられそうです。 本記事が皆様のお役に立てば幸いです。

私のTwitterアカウントでも機械学習自然言語処理に関する情報をつぶやいています。

@Hironsan

この分野にご興味のある方のフォローをお待ちしています。

参考資料