Open8

文章の解析を目でやるの面倒だから、doc2vec使っていろいろやってみたい

niisanniisan

とりあえず、この辺から自分にできそうなこととしては、

  • 文書を形態素解析して分解したものを用意する
  • 既存のword2vecを使って各々をベクトル化
  • それをもとにdoc2vecでモデル作成
  • そのモデルを使って文書をベクトル化
  • 階層的クラスタリングで分類

階層的クラスタリングで、階層数を指定できるのかはわからんけど。
それができれば理想的。

niisanniisan

形態素解析のやり方忘れてんな。
https://qiita.com/jun40vn/items/78e33e29dce3d50c2df1
この辺見ておく。

分かち書きするんなら、こんな感じか?

import MeCab
tagger = MeCab.Tagger("-Owakati")

split_words = []
for text in ep_dataframe['good_point']:
  split = tagger.parse(text).split()
  split_words.append(' '.join(split))

print(split_words[0:5])

とりあえず、ちゃんと分かち書きになったデータを出せそう。

niisanniisan

Doc2Vec使ってみる

分かち書きしたんで、doc2vecを使う

!pip install gensim

from gensim.models.doc2vec import Doc2Vec, TaggedDocument

documents = [TaggedDocument(doc, [i]) for i, doc in enumerate(split_words)]
model = Doc2Vec(documents, dm=1, vector_size=150, window=10, min_count=1, workers=4)

ベクトルのサイズは結構適当。
長い文章ほど、vector_size, window, mincountは大きくしたほうが良さそう。
大きすぎても、意味ないデータができそう。

モデルの中にはベクトル化した文書が全部入っている模様。
そりゃそうか。

model.docvecs

で全部のベクトルデータをリスト取得できる。
一回作ったやつは

model.save('/path/to/save.model')

でいける。

niisanniisan

文書量が多すぎると、階層的クラスタリングでメモリを食いすぎる( データ量の二乗でメモリを使う模様 ) なので、使うデータが多すぎる場合は、文書の長さで脚切りする必要がある。

以下は単語数が10を超える場合のみ文書に含むようにした。

import MeCab
tagger = MeCab.Tagger("-Owakati")

split_words = []
for text in ep_dataframe[1]:
  split = tagger.parse(text).split()
  if (len(split) > 10):
    split_words.append(' '.join(split))

階層的クラスタリングは簡単にできる。

from scipy.cluster.hierarchy import linkage, fcluster, dendrogram

docvecs = [model.docvecs[x] for x in range(len(model.docvecs))]
step_cluster = linkage(docvecs, method='ward', metric='euclidean', optimal_ordering=False)

method='ward'だと、いい感じにクラスタリングできるっぽい。
階層的クラスタリングができてしまえば、あとはクラスタを作ってしまえばいい。
だいたいどれくらいのクラスタ数であればいいかがわかっているのであれば、クラスタ数を指定するのがいい。

cluster = fcluster(step_cluster, t=70, criterion='maxclust', depth=2, R=None, monocrit=None)
niisanniisan

このライブラリ使う場合、クラスタリングのラベル付は「1」からつけられるのに注意。

niisanniisan

特徴となるワードがほしいので、TF-IDF出してみる。

まず、各クラスタごとに文書を全部つなげる。

word_cluster = [[] for x in range(max(cluster))]

for key in range(len(cluster)):
  index = cluster[key] - 1
  words = split_words[key].split()
  word_cluster[index].extend(words)

word_cluster2 = [' '.join(x) for x in word_cluster]

これで、クラスタごとに大きな一つの文書となった感じ。
こいつをTF-IDFに通す。

from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
import numpy as np

tfidf = TfidfVectorizer()
tfidf_cluster = tfidf.fit_transform(word_cluster2).toarray()
names = np.array(tfidf.get_feature_names())
index = tfidf_cluster.argsort(axis=1)[:,::-1]
feature_words = [names[doc[:10]] for doc in index]
print(feature_words)

ただ、あまりうまくはいかないっぽい

niisanniisan

ちがうな、多分、変な文書が紛れ込んでると、そいつが外れ値みたいになって、周りに影響を与えるのかもしれない。
もしくは、クラスタリングするにしても、そんなにパターンがないかどっちかか。