Word2Vec学習済みモデルを用いた文書ベクトルの2次元可視化

##概要

- Word2Vecで[特許文章版 分散表現モデル](https://qiita.com/niship2/items/3b2592fd32bf557909c9)を作成された方がいたので、この学習済みモデルを使って(ロードして)テキスト情報のベクトル化を行い、単語ベクトル化→文書ベクトルの生成→2次元可視化を行うためのコードを作成しました。

- 膨大なデータ量をインプットとする学習モデルの生成は、個人のPCスペックでは難しいですが、こうして学習済みモデルを公開していただけると色々試せるのでありがたいです。


##環境
OS: macOS Catalina
言語: python3.6.6

##コード
こちらが作成したコードです。日本語形態素解析ではMeCabを用いています。Word2Vecモデルは300次元で、それを2次元に削減するのにscikit-learnのTSNEを使っています。

```python:patent_w2v_JP.py
from sklearn.manifold import TSNE
from gensim.models import word2vec
from statistics import mean
import string
import re
import MeCab
import pandas as pd
import numpy as np

#インプットデータの読み込み
df = pd.read_csv('input.csv', encoding="utf-8")
#タイトルと要約を結合
df['text'] = df['title'].str.cat(df['abstract'], sep=' ')

#ストップワードのリスト（もしあればこのリストに追記する）
stop_words = []

#MeCabトークナイザー
def mecab_token_list(text):
    token_list = []
    tagger = MeCab.Tagger()
    tagger.parse('') 
    node = tagger.parseToNode(text)
    while node:
        pos = node.feature.split(",")
        if not node.surface in stop_words: #ストップワードは除外
            if pos[6] != '*': #見出語があればそれを追加
                token_list.append(pos[6])
            else: #なければ表層語を追加
                token_list.append(node.surface)
        node = node.next
    return list(token_list)

#数字を除去
df['text_clean'] = df.text.map(lambda x: re.sub(r'\d+', '', x))
#英文字は全て小文字に統一
df['text_clean'] = df.text_clean.map(lambda x: x.lower())
#MeCab Tokenize
df['text_tokens'] = df.text_clean.map(lambda x: mecab_token_list(x))

#Word2Vecモデルの読み込み
model = word2vec.Word2Vec.load("patent_w2v_d300_20191125.model")

#データフレームを300次元の配列に初期化
doc_vec = np.zeros((df.shape[0], 300))
#各文書における出現単語のモデルカバー率を格納するリストを用意
coverage = []
#各文書の平均ベクトルを配列に格納
for i,doc in enumerate(df['text_tokens']): #形態素解析後のテキスト情報を文書順に処理
    feature_vec = np.zeros(300) #300次元分を0に初期化
    num_words = 0
    no_count = 0
    for word in doc: #各文書の単語ごとに処理
        try: #単語ベクトルを加算していく処理
            feature_vec += model.wv[word]
            num_words += 1
        except: #分析モデルの対象外の単語はパスする
            no_count += 1
    #得られた単語ベクトルの和を単語数で割って平均値を計算し、文書ベクトルとして格納
    feature_vec = feature_vec / num_words
    doc_vec[i] = feature_vec
    #文書ごとの単語カバー率を計算し格納
    cover_rate = num_words / (num_words + no_count)
    coverage.append(cover_rate)

#単語の平均カバー率を表示
mean_coverage = round(mean(coverage)*100, 2)
print("Word cover-rate: " + str(mean_coverage) + "%")

#t-SNEによる次元削減
tsne= TSNE(n_components=2, init='pca', verbose=1, random_state=2000, perplexity=50, learning_rate=200, method='exact', n_iter=1000)
embedding = tsne.fit_transform(doc_vec)
#DataFrameに格納
embedding = pd.DataFrame(embedding, columns=['x', 'y'])
embedding["id"]= df.id
embedding["year"]= df.year
embedding["title"]= df.title
embedding["abstract"]= df.abstract

#CSVファイルとして出力
embedding.to_csv("output.csv", encoding="utf_8")

```

##インプットデータの構成
- インプットデータは特許文書を想定し、下記のようなCSV形式でid, year(出願年など), title(発明の名称), description(本文)といったデータが含まれていることを前提とします。このあたりは適宜アレンジしていただければと思います。

|     id       |       title    |       year    |   description |
|:------------:|:--------------:|:-------------:|:-------------:|
| 1             | （発明の名称） | （出願年など）     | （本文）|
| 2             |  ・・・ | ・・・     | ・・・|

- インプットデータ(input.csv)とWord2Vecモデルとこのpythonスクリプトは全て同一ディレクトリに入っていることを前提としています。このあたりは構成に応じて適宜アレンジしてください。

##留意点
- Word2Vecの単語ベクトルから文書ベクトルを生成する方法としては、各文書に含まれる単語ベクトルの平均値を文書ベクトルとする方法を採用しています。

- 一応、出現単語の当該モデルにおけるカバー率をコンソールにて表示するように設定しています。これは、各文書における単語カバー率を平均値によって算出しています。

- 他の目的で作成したコードをベースとしているため、サンプルデータで動作確認はしていますが、実際の特許文献のデータセットでは試せていません。[J-PlatPat](https://www.j-platpat.inpit.go.jp)とかで取得すれば良いのかな。

##2次元可視化のイメージ

matplotlibなどを使えばpythonのみで可視化までできるようですが、参考までにGIS(地理情報システム)を転用して可視化するとこのような表現になります。
各プロットが文書データで、2次元平面での集中度に応じてヒートマップ化しています。こうした表現は、分類や分析のベースとして有用かと思います。
<img width="450" alt="sample_w2v.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122865/de02dfc3-2c7d-6deb-b717-97afa4371a70.png">