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

とりあえず、参考資料
とりあえず、仕組みとか
やりたいことに近いのはこっちかも
学習モデル
わかりやすいまとめ

とりあえず、この辺から自分にできそうなこととしては、
- 文書を形態素解析して分解したものを用意する
- 既存のword2vecを使って各々をベクトル化
- それをもとにdoc2vecでモデル作成
- そのモデルを使って文書をベクトル化
- 階層的クラスタリングで分類
階層的クラスタリングで、階層数を指定できるのかはわからんけど。
それができれば理想的。

形態素解析のやり方忘れてんな。
この辺見ておく。分かち書きするんなら、こんな感じか?
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])
とりあえず、ちゃんと分かち書きになったデータを出せそう。

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')
でいける。

文書量が多すぎると、階層的クラスタリングでメモリを食いすぎる( データ量の二乗でメモリを使う模様 ) なので、使うデータが多すぎる場合は、文書の長さで脚切りする必要がある。
以下は単語数が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)

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

特徴となるワードがほしいので、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)
ただ、あまりうまくはいかないっぽい

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