word2vec 英単語ベクトル可視化

2021.3.27 修正
word2vecのmodel作成の際の引数がsizeとiterでエラーを吐いてしまいました。
オフィシャルに変更があったのか分かりませんが、修正を追記しました。




##学習素材
旅行Reviewサイト、Tripadvisorの"Hikone Castle"に関する閲覧可能な150レビューを.（ピリオド）で区切った状態で.txt.で保存
> https://www.tripadvisor.com/Attraction_Review-g1023563-d1236815-Reviews-Hikone_Castle-Hikone_Shiga_Prefecture_Kinki.html

txtファイル
https://drive.google.com/file/d/1DePVQHnsUtf-RlsZ07F5HdltsbNippDs/view?usp=sharing

##word2vecに渡す文章リスト
If you can dream it, you can do it.
It always seems impossible until it’s done.

という文章であれば

[
[If,you,can,dream,it,you,can,do,it],
[It,always,seems,impossible,until,it's,done]
]

上記のように単語が切れた状態で保存が必要

## マッピングオブジェクト
今回はレビュー1144文を読み込む。登場する単語全てをベクトル化すると二次元プロットする際に単語で埋め尽くされかねない。上位単語を抽出できるように{単語：回数}になるマッピングオブジェクトを用意。マッピングオブジェクトのままだと使いにくかったのでpandasに渡す。

## stop wordの削除
今回は I my me as is am のような単語のランキングは省きたいので、nltkからstopwordのリストを拝借して削除対象とした。

## 単語のベクトル化と次元圧縮
200次元で各単語をベクトル化して、その後、主成分分析で次元圧縮してプロット


# 実際のコード

一括表示ですが、変数を取り出しながらみてみると良いと思います。

```python
# stopwordsの参照先
import nltk
from nltk.corpus import stopwords
# データフレームの作成
import pandas as pd
# Word2Vec
from gensim.models import Word2Vec
# 主成分分析
from sklearn.decomposition import PCA
# 可視化
import matplotlib.pyplot as plt

# 特定記号の削除・小文字化・リストの取得まで行う関数を設定
def sentence_preprocesser(s,delete_targets):
    """
    s:strings
    delete_targets:list
    文章s中のdelete_targetsを削除
    削除後のsentenceを小文字・リスト化してreturn
    """
    # delete_targetsのi番目があれば削除
    for i in range(len(delete_targets)):
        s = s.replace(delete_targets[i],"")
    # 小文字化    
    s=s.lower()
    # リスト化
    words = list(s.split())
    
    return words

# listの要素をdict型内で参照してキーが存在すれば+1,存在しなければ追加する関数
def dict_key_counter(lis,table):
    """
    lis:list
    table:dict
    
    list要素がtableの中に
    キーとして存在
    table[list要素] に対して +1
    キーとして存在しない
    table[list要素]=0 としてキーを作成した上で +1
    """
    
    for word in lis:
        table[word] = table.get(word, 0) +1
        
    return table

with open("pro1.txt", mode="r", encoding="utf-8") as f:hikone = f.read()
hikone_list = hikone.split("\n")
print("分析対象とした文の数:{}".format(len(hikone_list)))

# 全頻度をstockしていくマッピングオブジェクト
frequent_counter = {}
# word2vecに学習させる文
word2vec_sentence = []

for i in range(len(hikone_list)):
    sentence = hikone_list[i]
    delete_targets = [".",",","?","!","(",")","-","@"]
    # 前処理関数に通して、指定記号の削除・小文字化・リスト化まで
    words = sentence_preprocesser(sentence,delete_targets)
    # word2vecに学習させる文
    word2vec_sentence.append(words)
    
    # frequent_counterに各入力文章に対する 回数:頻度 のdictをストックする
    dict_key_counter(words,frequent_counter)

# frequent_counter中のstop_wordsを削除する
stop_words = stopwords.words("english")
for i in range(len(stop_words)):
    remove = frequent_counter.pop(stop_words[i],None)

# マッピングオブジェクトをデータフレームにする
df = pd.DataFrame(frequent_counter.values(), index=frequent_counter.keys())
# 初期のコラム名を変更
df = df.rename(columns={0:"count"})
# 頻度が高い順に並び替え
df = df.sort_values("count", ascending = False)
print("使用された単語の総数：{}".format(len(df.index)))


# count30以上を抽出
df_top = df[df["count"] >= 30]
df_top.plot.bar()

# castleが突出して多いのでcastleを除く
df_top2 = df_top[df_top["count"] <= 400]
df_top2.plot.bar()

df_top = df_top.T
df = df.T
# 読み込んだ文章を学習させてモデルを作成
model = Word2Vec(word2vec_sentence, size= 200, window=5, min_count=10, iter=20, sg=1)
model.save("hikone.model")
model = Word2Vec.load("hikone.model")

# count30以上の単語のベクトルを取得
wordvecs = []
for i in range(len(df_top.columns)):
    vec = model.wv[df_top.columns[i]]
    wordvecs.append(vec)
# １単語につき、size = 200 に基づいた200次元ベクトルで表現
    
# 主成分分析
pca = PCA(n_components=2)
pca.fit(wordvecs)

# 射影
X_2d = pca.transform(wordvecs)

# 可視化
plt.figure(figsize=(10, 10))
for i in range(len(df_top.columns)):
    plt.plot(X_2d[i][0], X_2d[i][1], marker='>')
    plt.annotate(df_top.columns[i],(X_2d[i][0], X_2d[i][1]))
    

	
plt.show()
```

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/8ef08e57-9c01-6807-baac-c5bfb13a8a2c.png)
![download-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/3df9fca9-b8ad-6613-8338-6b0b91675213.png)
![download-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/d90eb31c-6d97-c961-648c-445dfc29b91a.png)

## 感想
inside,stairs,steep,steps,climb辺りが右に固まっている。
キツい階段でもあるのかなぁ〜ということが分かる。

その中にやや異質なinsideという単語。
![download.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/5ee2933e-9226-82f4-c0fe-76c191299a3c.jpeg)
彦根城には天守閣に通じる勾配がエグいことになっている階段があるそうだ。
insideがこれと結びついているのならばword2vecさすがという印象？


# 修正

word2vecのmodel作成の際の引数がsizeとiterでエラーを吐いてしまいました。
オフィシャルに変更があったのか分かりませんが、修正を追記しました。

＜修正前＞

```python
# 読み込んだ文章を学習させてモデルを作成
model = Word2Vec(word2vec_sentence, size=200, window=5, min_count=10, iter=20, sg=1)
```
![スクリーンショット 2021-03-27 14.48.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/5b4fdf5f-4b6c-9292-006d-0a8241b75f53.png)

＜修正後＞

```python
# 読み込んだ文章を学習させてモデルを作成
model = Word2Vec(word2vec_sentence,vector_size=200,window=5,min_count=10,epochs=20,sg=1)
```

size→vector_size
iter→epochs

ちなみに今回はWord2Vecの所にカーソルを合わせて、shift+tabで引数のヘルプを参照することで修正箇所に気づくことが出来ました。