【pythonで自然言語処理】日本語版・word2vecで分散表現した単語を2次元に可視化する

スクレイピングでデータを集めてみたんだけど、10000行とかデータが多すぎて分析が面倒だよ。
なんかいい方法ないかな?

すべてのデータを見るなんて現実的ではないよね。
そんな事やっているのは、De○Aのkaggleの金メダリストくらいだよ〜。
今回は、日本語の類似単語を図に出す方法を解説するよ。
単語を図にプロットするとなんとなく傾向をつかめて良いよ♪

概要

意味的に近い単語の分布を知りたい。そんなときは、2次元の分布図を作成するのが有効です。

スクレイピングなどで集めた大量のテキストデータを可視化するときに役立ちます。

この記事では、janomeによる日本語の形態素分析、word2vecによる分散表現、PCA(主成分分析)による次元圧縮(N次元から2次元へ)、単語を2次元の図にプロットする方法を解説します。

なお、word2vecによる分散表現についてはUdemyの自然言語処理の講座が参考になります。

この記事のword2vecの部分は、Udemyの自然言語処理の講座を参考にしています。

Udemy 「自然言語処理とチャットボット: AIによる文章生成と会話エンジン開発」はこちらから

実装例をもとに分散表現から2次元にプロットまで解説

処理の流れ

処理の流れは以下の通りになります。

  1. 分布図の作成に必要な日本語の文章が入ったファイルを読み込む
  2. 日本語の文章を形態素分析して分割する
  3. gensimのword2vecを使って分散表現(N次元の配列)にする
  4. 分散表現により、意味的に近い単語がわかるようになる
  5. PCA(主成分分析)を用いてN次元の分散表現を2次元に圧縮する
  6. 圧縮した分散表現を使って2次元のグラフにプロットする

ここからは、ここの項目について解説します。

全体的なコードは、記事の後半に記載しています。

日本語のテキストファイルを読み込む

単語の分布図の元になる文章をファイルから読み込みます。

#  テキストファイルの読み込み(なお、前処理は考慮していない)
with open("プログラミングに間する不明点まとめ.txt", mode="r", encoding="utf-8") as f:  
    wagahai = f.read()

テキストファイルの中身は、このような文章が入っています。

テキストファイルの中身の例

これまでいろんな仕事をやってきました。

この年になって、将来が不安で手に職をつけたいと思いプログラマーを目指したいと思いまいた。

といってもプログラミングについて、漠然としたイメージしか無いため、かっこいいとか給料いいとか。

janomeによる形態素分析

以下の部分では、janomeを使って形態素分析をしています。

セパレーターは、「。」を指定しています。

なお、この記事の例では、前処理(青空文庫にあるルビなどの削除)は考慮していません。

seperator = "。"  # 日本語だと「。」にすることが多い
wagahai_list = wagahai.split(seperator)  # セパレーターを使って文章をリストに分割する

# janomeのTokenizerを呼び出す
t = Tokenizer()


wagahai_words = []
for sentence in wagahai_list:
    wagahai_words.append(t.tokenize(sentence, wakati=True
    

ポイント

日本語の多くは、セパレーターに「。」を設定する。

本来は、不要な文字を削除するなどの前処理が必要。

gensimのword2vecを使って分散表現(N次元の配列)にする

word2vecを使って分散表現することにより、意味的に近い単語がわかるようになります。

分散表現はN次元の配列の実数値になります。

# size : 中間層のニューロン数・数値に応じて配列の大きさが変わる。数値が多いほど精度が良くなりやすいが、処理が重くなる。
# min_count : この値以下の出現回数の単語を無視
# window : 対象単語を中心とした前後の単語数
# iter : epochs数
# sg : skip-gramを使うかどうか 0:CBOW 1:skip-gram
model = word2vec.Word2Vec(wagahai_words,
                          size=200,
                          min_count=5,
                          window=5,
                          iter=20,
                          sg = 0)

#学習結果を確認します
print(model.wv.vectors.shape)  # 分散表現の形状 Word2Vecのsizeの設定が反映されているのがわかります
print(model.wv.vectors)  #実際の分散表現、size次元の配列になっていることがわかるかと思います。

#類似している(ベクトルの距離的に近い)単語を出力してみる
#以下の例では単語「earthquake」に近い単語を出力することができます。
print(model.most_similar(positive=['プログラミング'], topn=20))


PCA(主成分分析)を用いてN次元の分散表現を2次元に圧縮する

現時点では、N次元(今回の例では200次元)の配列データになっているため、図にプロットするためには、2次元まで圧縮する必要があります。圧縮する方法の一つが主成分分析です。


#ここではすべての単語を出力する設定にしている
words = []
for i in range(len(model.wv.index2word)):
    word = model.wv.index2word[i] 
    words.append([word,"b"])


length = len(words)
data = []
 
j = 0

while j < length:
    data.append(model[words[j][0]])
    j += 1



#主成分分析により2次元に圧縮する
pca = PCA(n_components=2)
pca.fit(data)
data_pca= pca.transform(data)

ポイント

この例では、すべての単語をプロットしているが、いらない単語も多数プロットされてしまっています。

本来は、名詞だけなど品詞を限定して出力したほうが良いでしょう。

圧縮した分散表現を使って2次元のグラフにプロットする

主成分分析により2次元まで圧縮しました。

これでやっと、2次元の図にプロットできるようになりました。

次は、matplotlibを使って図にプロットします。

fig=plt.figure(figsize=(20,12),facecolor='w')


#日本語が使えるフォントの場所を指定する
#フォント指定しないと文字化けするため
font_path = './ipamp.ttf'
font_prop = FontProperties(fname=font_path)


plt.rcParams["font.size"] = 10
i = 0
while i < length_data:
    #点プロット
    plt.plot(data_pca[i][0], data_pca[i][1], ms=5.0, zorder=2, marker="x", color=words[i][1])
 
    #文字プロット
    plt.annotate(words[i][0], (data_pca[i][0], data_pca[i][1]), size=12, fontproperties = font_prop)
 
    i += 1


plt.show()

実際にプロットすると以下のような2次元の分布図が作成されます。

うーーん、きったねー図だね・・・
もう少しキレイな図にならないものかね・・・・

単語のすべてをプロットしているから汚くなるよ。
もう少し、単語を限定したほうが良さそうだ。
ストップワード(「私」などの意味がない単語のこと)を出力しないなどの対応が必要だね

図の補足

今回は、形態素に分割された単語のすべてをプロットしています。

出力する単語を限定してプロットすると見やすくなります。

全体的なコード

これまで実装方法を部分的なコードで解説しましたが、全体的なコードを掲載します。


#!/usr/bin/env python
# coding: utf-8

# # 日本語版word2vecによる分散表現と2次元グラフに単語のプロットする
# 
# ・word2vecによる分散表現に変換する
# ・分散表現をした単語をPCAを使って2次元に圧縮する
# ・圧縮した分散表現を2次元の図にプロットする
# ・参考にした参考動画(Udemy自然言語処理とチャットボット: AIによる文章生成と会話エンジン開発講座より「word2vecによる分散表現」)
# https://www.udemy.com/course/ai-nlp-bot/?deal_code=JPA8DEAL2PERCENTAGE&aEightID=s00000016735001
# 

# ## コーパスの前処理
# ・ファイルから文章データを取り出す
# ・文章ごとに単語に分割する

# In[2]:


import re
import pickle
from janome.tokenizer import Tokenizer
import warnings
warnings.simplefilter('ignore')

#  テキストファイルの読み込み(なお、前処理は考慮していない)
with open("プログラミングに間する不明点まとめ.txt", mode="r", encoding="utf-8") as f:  
    wagahai = f.read()


seperator = "。"  # 日本語だと「。」にすることが多い
wagahai_list = wagahai.split(seperator)  # セパレーターを使って文章をリストに分割する

# janomeのTokenizerを呼び出す
t = Tokenizer()


wagahai_words = []
for sentence in wagahai_list:
    wagahai_words.append(t.tokenize(sentence, wakati=True)) 
    
#ここで、pickleに保存します。
with open('jp_word2vec.pickle', mode='wb') as f:  # pickleに保存
    pickle.dump(wagahai_words, f)


# ## gensimのword2vecを用いた学習をする
# 今回はword2vecのためにライブラリgensimを使います。  
# 
# gensimについては、以下を参照 
# https://radimrehurek.com/gensim/
# 
# 以下では、word2vecを用いてコーパスの学習を行い、学習済みのモデルを作成します。

# In[3]:


from gensim.models import word2vec

with open('jp_word2vec.pickle', mode='rb') as f:
    wagahai_words = pickle.load(f)
    
    
# size : 中間層のニューロン数・数値に応じて配列の大きさが変わる。数値が多いほど精度が良くなりやすいが、処理が重くなる。
# min_count : この値以下の出現回数の単語を無視
# window : 対象単語を中心とした前後の単語数
# iter : epochs数
# sg : skip-gramを使うかどうか 0:CBOW 1:skip-gram
model = word2vec.Word2Vec(wagahai_words,
                          size=200,
                          min_count=5,
                          window=5,
                          iter=20,
                          sg = 0)


# In[4]:


#学習結果を確認します
print(model.wv.vectors.shape)  # 分散表現の形状 Word2Vecのsizeの設定が反映されているのがわかります
print(model.wv.vectors)  #実際の分散表現、size次元の配列になっていることがわかるかと思います。


# In[5]:


print(len(model.wv.index2word))  # 語彙の数
print(model.wv.index2word[:30])  # 最初の30単語を表示します。頻出単語の順番で出力されるようです。


# In[ ]:


#print(model.wv.vectors[0])  # 最初のベクトル
#print(model.wv.__getitem__("maskhttp"))  # 最初の単語「の」のベクトル


# In[6]:


#類似している(ベクトルの距離的に近い)単語を出力してみる
model.most_similar(positive=['プログラミング'], topn=20)


# ## 単語を2次元の図にプロットします
# ・PCAを用いてN次元の分散表現を2次元に圧縮する
# ・2次元に圧縮した分散表現を2次元の図にプロットします

# In[16]:


import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from sklearn.decomposition import PCA


#単語ができる
words = []
for i in range(len(model.wv.index2word)):
    word = model.wv.index2word[i] 
    words.append([word,"b"])


length = len(words)
data = []
 
j = 0

while j < length:
    data.append(model[words[j][0]])
    j += 1




#主成分分析により2次元に圧縮する
pca = PCA(n_components=2)
pca.fit(data)
data_pca= pca.transform(data)
 
length_data = len(data_pca)

#プロットの設定
#fig=plt.figure(figsize=(10,6),facecolor='w')
fig=plt.figure(figsize=(20,12),facecolor='w')


#日本語が使えるフォントの場所を指定する
#フォント指定しないと文字化けがするため
font_path = './ipamp.ttf'
font_prop = FontProperties(fname=font_path)


plt.rcParams["font.size"] = 10
i = 0
while i < length_data:
    #点プロット
    plt.plot(data_pca[i][0], data_pca[i][1], ms=5.0, zorder=2, marker="x", color=words[i][1])
 
    #文字プロット
    plt.annotate(words[i][0], (data_pca[i][0], data_pca[i][1]), size=12, fontproperties = font_prop)
 
    i += 1

plt.show()


# 実際にプロットしてみると文字が重なって見づらくなってしまった。
# ストップワードなどちゃんと設定しないとだめだね。。。。


まとめ

自然言語のkaggleのコンペや、スクレイピングで集めたデータからユーザーの動向を調査するのに可視化は大事ですね。

可視化にword2vecによる分散表現を取り入れてみてはいかがでしょうか?

pythonを使った人工知能、チャットボットが作ってみたい方のテックキャンプ

スポンサーリンク
PR




PR




シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク
PR