毎日テキストマイニング

180日間、毎日テキストマイニングをするブログです

2018/8/11【49日目】word2vecを使ったk-means分類を行う

word2vecを使った分類

昨日まででword2vecを使って、単語を分散表現を作ることができました。単語をベクトル化したことでk-menas分類が使えるそうです。

k-meansの説明は「k-means わかりやすい」で検索したら、次のサイトが出てきましたのでこちらを参考にしてください。

tech.nitoyon.com

それでは早速、チュートリアルを見ていきます。

実装

チュートリアルだと時間測定やコメントが書いてあり、コードがごちゃごちゃしていますが、実際のコードは5行だけです。

from sklearn.cluster import KMeans

word_vectors = model.syn0
num_clusters = word_vectors.shape[0] / 5

kmeans_clustering = KMeans( n_clusters = num_clusters )
idx = kmeans_clustering.fit_predict( word_vectors )

このままだと動かないところがありますので、下記に修正します。

from sklearn.cluster import KMeans

word_vectors = model.wv.syn0
num_clusters = int(word_vectors.shape[0] / 5)

kmeans_clustering = KMeans(n_clusters = num_clusters)
idx = kmeans_clustering.fit_predict(word_vectors)

kmeansをimportして、k-meansを学習させています。modelというのは昨日の段階で作ったword2vecを実行済みのモデルです。model.wv.syn0というのが学習したものを配列として返すものだと思われます。 中見てみるとこんな感じです。

word_vectors
array([[ 0.09036727, -0.08819977,  0.16606072, ...,  0.02174987,
         0.0238141 , -0.09225295],
       [ 0.02887462, -0.04785182, -0.03468372, ..., -0.00712097,
         0.05275642,  0.09389295],
       [ 0.06902038,  0.02627148,  0.02220457, ...,  0.04232957,
         0.07388142, -0.01084791],(以下、略)

num_clustersというのが、学習するサイズを指定している場所の予感がします。k-meansが学習できたら、idxに代入しています。idxの中身はこんな感じです。

idx
array([1716, 1364, 2013, ...,  724, 1438, 1115], dtype=int32)

k-meansが学習された数値が入っているみたいですね。数値だけだとわからないので、model.wv.index2wordと一緒に辞書にまとめれば元の単語がわかるみたいです。

word_centroid_map = dict(zip( model.wv.index2word, idx ))
word_centroid_map
{'the': 1716,
 'and': 1364,
 'a': 2013,
 'of': 2911,
 'to': 830,
 'is': 1675,

このtheが示してる1716が何の数値かは不明です。出現が多い順に高いけわけでもないみたいです。 分類した中身を見るにはリストに入れて、出力すれば良いそうです。チュートリアルのPython2系では辞書型に添字をつけるとvalueが帰ってきたそうなのですが、Python3系ではリストを明示しないとダメだそうです。コードの修正はこんな感じです。

for cluster in range(0,20):
    print("\nCluster %d" % cluster)
    words = []
    for i in range(0,len(word_centroid_map.values())):
        valuew = word_centroid_map.values()
        if( list(word_centroid_map.values())[i] == cluster ):
            words.append(list(word_centroid_map.keys())[i])
    print(words)

実行結果。

Cluster 0
['see', 'hear']

Cluster 1
['mclaglen', 'vargas']

Cluster 2
['tough', 'noble', 'heroic', 'ruthless', 'bumbling', 'petty', 'macho', 'slimy', 'cunning', 'hardened', 'heartless', 'brute', 'cowardly', 'reckless', 'yuppie', 'malicious', 'sociopath', 'unorthodox', 'benevolent', 'brutish', 'pacifist', 'benign', 'ruthlessly']

Cluster 3
['snippets', 'passages', 'fragments', 'asides']

Cluster 4
['lana', 'dern', 'madeline', 'kahn', 'linney', 'rossellini']

Cluster 5
['bolivia', 'casualty']

Cluster 6
['exquisite', 'sublime', 'mesmerizing', 'sensational', 'shakespearean', 'spellbinding']
(以下、省略)

似ている単語が分類されているようです。 わかりにくい分類もありますが、下記はわかりやすい分類になっています。

  • Cluster 0 ['see', 'hear']
  • Cluster 14 ['equal', 'sexuality', 'gender', 'ethnic']
  • Cluster 18 ['dragons', 'pokemon', 'dungeons']

何でこんな分類ができるのか謎ですが、面白いですね。

Python2系とPython3系での違いのせいで、エラーがかなり多かったですが、かなり勉強になりました。明日からAKBの呟きに応用したいと思います。

今日の結果

今日のAKBの呟きは110件でした。何が起こったんですかね? 画像から読み取れるのは握手会とじゃんけん大会の本戦があったようですね。 f:id:rimt:20180812230852p:plain

{'嬉しい': 11, '楽しい': 8, '可愛い': 8, '珍しい': 5, '正しい': 4, 'っぽい': 4, 'よろしい': 3, '弱い': 3, 'いい': 3, 'ない': 2, '無い': 2, '早い': 2, '五月蝿い': 2, 'よい': 2, '良い': 1, '遠い': 1, '辛い': 1, '眩しい': 1, '切ない': 1, '美しい': 1, 'うれしい': 1, '悔しい': 1, '優しい': 1, 'おしい': 1, 'おもしろい': 1, 'つらい': 1, 'もどかしい': 1})
'アクシュカイ': 57, '部': 40, '今日': 36, '会': 25, '浴衣': 24, '写真': 24, '大会': 23, '日': 19, '明日': 16, '本戦': 15, 'ちゃん': 15, 'レーン': 15, 'ん': 14, '応援': 13, '皆さん': 12, '嬉しい': 11, 'さん': 11, '当日': 11, '券': 11, '目': 11, '人': 11, 'お願い': 10, '笑': 10, '動画': 10, 'ステージ': 10,
'アクシュカイ': 57, 'する': 51, '部': 40, 'ける': 40, '今日': 36, '会': 25, '浴衣': 24, '写真': 24, '大会': 23, '日': 19, '来る': 17, '明日': 16, 'くださる': 16, '本戦': 15, 'ちゃん': 15, 'レーン': 15, 'ん': 14, '応援': 13, '皆さん': 12, '負ける': 12, '嬉しい': 11, 'さん': 11, '当日': 11, '券': 11, '目': 11, '人': 11, 'てる': 11, 'なる': 11, '頑張る': 11,