言語処理におけるグラフ理論とネットワーク分析|実践的自然言語処理入門 #6

f:id:lib-arts:20190511200356p:plain

#1ではBoW、#2ではtf-idf、#3ではWord2Vec、#4ではcos類似度と文書分類、#5ではネガポジ分析について取り扱いました。

BoWと形態素解析|実践的自然言語処理入門 #1 - lib-arts’s diary

特徴語抽出とtf-idf|実践的自然言語処理入門 #2 - lib-arts’s diary

分散表現とWord2vec|実践的自然言語処理入門 #3 - lib-arts’s diary

cos類似度と文書分類|実践的自然言語処理入門 #4 - lib-arts’s diary

極性辞書を用いたネガポジ分析|実践的自然言語処理入門 #5 - lib-arts’s diary
#6では言語処理におけるグラフ理論とネットワーク分析について取り扱います。
以下目次になります。

1. 言語処理におけるグラフ理論とネットワーク分析
2. 文書分類タスクにおけるネットワーク分析とPythonによる実装
3. まとめ

 

1. 言語処理におけるグラフ理論とネットワーク分析
グラフ理論とネットワーク分析はよくセットで聞きますが、それぞれどのように整理し言語処理の文脈でどのように捉えると良いでしょうか。まず基礎知識として下記の記事が前提になりますので、自信のない方は下記を先にご確認ください。

結局のところ、グラフ理論とは物事の可視化に便利で用いられます。可視化の前提として計算機的にどのように表すかについては隣接行列(Adjacency Matrix)を理解しておけば一旦は十分です。また、ネットワーク分析に関しては基本的には隣接行列(計算機における数値での表現)とグラフを用いた表現(ノードとエッジを用いて可視化したもの)との関連さえ抑えた上で計算結果を可視化する手法だと理解しておくのが良いです。ネットワーク分析単体で成り立っているというよりは隣接行列をどのように計算するかが実は重要なため、隣接行列をどのように計算するかに重きを置いて考えておくと良いと思います。

さて、グラフ理論とネットワーク分析に関して整理を行いましたので、次は言語処理における文脈で再度捉え直したいと思います。ネットワーク分析において重要なのは隣接行列をどのように作るかなので、言語処理においてどのような隣接行列を作るかという視点で見ると良さそうです。隣接行列について考える際にG=(V,E)をまず思い出しましょう。隣接行列は行も列も同じV(Vertex)を用いた行列です。
言語処理の文脈で行列といえばBoW(Bag of Words)ですが、BoWは単語(W)と文書(D)の行列のためV(Vertex;ノード)を表現することができません。それではこのBoWをどのように隣接行列に変換できるのでしょうか。ヒントは#4でまとめた文書類似度の計算になります。

上記の計算において得られた文書間類似度は閾値を設けることで隣接行列の形式に変換することができます。このように言語処理の文脈においては文書か単語に着目し、それぞれの類似度を出すというのがネットワーク分析を行うにあたっての基本アプローチになります。
文書単位だけではなく、単語に関しても可能です。

#3のWord2Vecのmost_similar関数は似た単語の抽出を行っています。したがってこの情報を元に隣接行列を作成し、同様にネットワーク分析を行うことができます。

 

2. 文書分類タスクにおけるネットワーク分析とPythonによる実装
1節では「言語処理におけるネットワーク分析」に関する基本的な考え方について触れたので、2節では1節の最後の方で言及した#4の文書間類似度をベースにネットワーク分析の実装を行なっていきます。
基本的に類似度の計算までは同様で、最後のグラフの可視化においてはNetworkXというライブラリを用いて可視化を行います。

# モジュールインポート
from gensim import corpora, matutils
from janome.tokenizer import Tokenizer
import numpy as np
import scipy.sparse
import networkx as nx
import matplotlib.pyplot as plt

# ストップワードの設定、Janomeインスタンスの生成
stop_words = ["平和", "人間"] #全文書に出てくる特徴的でない単語はストップワードとして除く
tokenizer = Tokenizer()

# Janomeでの処理スクリプト(名詞のみ、ストップワードは除く、2文字以上の単語、数字は使用しない)
def token_generator(text):
    for text_line in text.split('\n'):
        for token in tokenizer.tokenize(text_line):
            if token.part_of_speech.split(',')[0] == '名詞' and token.surface not in stop_words:
                if len(token.surface) > 1 and not(token.surface.isdigit()):
                    yield token.surface

# データの読み込み&形態素解析
text_processed = [ ]
num_files = ファイル数
for i in range(num_files): # ファイルは1からの連番で用意している前提で記述しています
    file_path = "./data/data_file"+str(i+1)+".txt"
    with open(file_path) as f:
        txt = f.read()
            text_processed.append(list(token_generator(txt)))

# 辞書の作成と保存
dictionary = corpora.Dictionary(text_processed)
dictionary.save('./dictionary.dict')

#BoW matrixの作成&保存
corpus = [dictionary.doc2bow(doc) for doc in text_processed]
doc_matrix = matutils.corpus2csc(corpus).transpose()
scipy.sparse.save_npz('./category_matrix.npz', doc_matrix)

# cos_similarityの作成
cos_sim = np.zeros([num_files, num_files])
var_mat = doc_matrix.dot(doc_matrix.transpose()).toarray()
for i in range(num_files):
    for j in range(num_files):
        cos_sim[i,j] = var_mat[i,j]/(np.sqrt(var_mat[i, i])*np.sqrt(var_mat[j, j]))
print(cos_sim[:6,:6])

# 隣接行列の作成&グラフの描画
G=nx.Graph()
for i in range(num_files):
    for j in range(num_files):
        if i != j and cos_sim[i,j]>0.2: # 閾値を設けて隣接行列を作る
            G.add_edge(str(i),str(j),weight=cos_sim[i,j])

nx.draw(G, with_labels=True, font_weight='bold')
plt.show()

上記を実行すると下記のような図を得ることができます。

f:id:lib-arts:20190511200356p:plain
このようにして文書間の関係をネットワーク分析に基づいて可視化を行うことができます。


3. まとめ
#6では自然言語処理におけるネットワーク分析において、文書分類のグラフによる可視化を行いました。
このようにグラフ理論系の話は知っておくとなんだかんだ便利なので、一通り抑えておくと便利だと思います。