[自然言語処理/NLP] pyvisライブラリを使って共起ネットワークを簡単に描画してみる(SageMaker使用)

2019.09.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、Mr.Moです。
共起ネットワークを描画するのにpyvisライブラリを使うと簡単にできたので下記にまとめていきます。

共起ネットワークについて

  • 共起とは?

> 共起(きょうき,英:Co-occurrence)は、ある単語がある文章(または文)中に出たとき、その文章(文)中に別の限られた単語が頻繁に出現すること。

https://ja.wikipedia.org/wiki/%E5%85%B1%E8%B5%B7

共起ネットワークは共起する単語の関係をネットワーク図で表したものです。

image.png

https://en.wikipedia.org/wiki/Co-occurrence_network

pyvisライブラリについて

ネットワーク図の視覚化に特化したライブラリのようです。

https://pyvis.readthedocs.io

チュートリアル がありデータも用意されているのですがそちらは英語のデータになるので、本記事では日本語のデータを別途用意した上で共起ネットワークを描画したいと思います。

データの準備

下記の記事を参考にデータを作っていきます。

事前準備

前提としてSageMaker上で作業をしていきます。

SageMakerでノートブックインスタンスの作成

まずはじめにSageMakerの画面を開きます。

https://ap-northeast-1.console.aws.amazon.com/sagemaker/home?region=ap-northeast-1#/notebook-instances

次にノートプックインスタンスを作成します。 下記の要領で赤枠の部分を対応して後はデフォルトのままで大丈夫です。

image.png image.png

ノートブックはconda_python3の環境で実行します。

image.png

MeCabのインストール

MeCabを使うのでインストールをしていきます。ノートブックのセルに下記のコードをコピペしておもむろに実行してください。完了まで少し時間がかかります。

参考:https://qiita.com/YuukiMiyoshi/items/00b9878a1fa32b859a43

%%bash
sudo yum install -y  bzip2 bzip2-devel gcc gcc-c++ git make wget curl openssl-devel readline-devel zlib-devel patch file

# 作業フォルダを作成して移動
mkdir -p ~/source/mecab
cd ~/source/mecab

# ソースをダウンロードして解凍して移動
wget 'https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE' -O mecab-0.996.tar.gz
tar zxvf mecab-0.996.tar.gz
cd mecab-0.996

# インストール先フォルダを作成
sudo mkdir -p /opt/mecab

# configure(コンパイルのための設定)を実行し、コンパイルしてインストール
./configure --prefix=/opt/mecab --with-charset=utf8 --enable-utf8-only
make
sudo make install

# bashの場合
echo "export PATH=/opt/mecab/bin:\$PATH" >> ~/.bashrc
source ~/.bashrc

sudo bash -c "echo \"/opt/mecab/lib\" > /etc/ld.so.conf.d/libmecab.conf"
sudo ldconfig

# 作業フォルダを作成して移動
mkdir ~/source/mecab-ipadic
cd ~/source/mecab-ipadic

# 辞書ファイルを取得して解凍して移動
wget 'https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM' -O mecab-ipadic-2.7.0-20070801.tar.gz
tar zxvf mecab-ipadic-2.7.0-20070801.tar.gz
cd mecab-ipadic-2.7.0-20070801

# 設定してmakeしてインストール
./configure --with-mecab-config=/opt/mecab/bin/mecab-config --with-charset=utf8
make
sudo make install

# 作業フォルダに移動してファイルを取得
cd ~/source
git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git

# 設定とともにインストールを実行
./mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n -y -p /opt/mecab/lib/mecab/dic/neologd

MeCabをPythonで使うのに下記モジュールが必要なのでインストールしましょう。

!pip install --upgrade pip
!pip install mecab-python3

データ準備

青空文庫からテキストデータをダウンロード

今回扱うテキストデータは青空文庫にある福沢諭吉の『学問のすすめ』を使わせていただきましょう。

<br />!wget https://www.aozora.gr.jp/cards/000296/files/47061_ruby_28378.zip
!mkdir data
!unzip 47061_ruby_28378.zip -d data

ダウンロードしたデータを少し加工します。先に加工に必要な関数などを定義しておきます。下記のコードをノートブック上で実行していっってください。

参考:https://newtechnologylifestyle.net/711-2/

import re
# テキストファイルのルビや注釈などを削除して本文のみを取得する
def convert(download_text):
    binarydata = open(download_text, 'rb').read()
    text = binarydata.decode('shift_jis')

    # ルビ、注釈などの除去
    text = re.split(r'\-{5,}', text)[2]
    text = re.split(r'底本:', text)[0]
    text = re.sub(r'《.+?》', '', text)
    text = re.sub(r'[#.+?]', '', text)
    text = re.sub(r'\r\n', '', text)
    text = re.sub(r'\u3000', '', text)
    text = text.strip()
    return text
# ストップワードをダウンロード
import urllib.request
url = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt'
urllib.request.urlretrieve(url, 'stop_word.txt')

with open('stop_word.txt', 'r', encoding='utf-8') as file:
    stopwords = [word.replace('\n', '') for word in file.readlines()]
import MeCab
mecab = MeCab.Tagger("-Owakati -d  /opt/mecab/lib/mecab/dic/neologd")

# 文章から名刺のみを抽出する
def get_meishi(sentence):
    keywords = []
    node = mecab.parseToNode(sentence)
    while node:
        word = node.surface
        feature = node.feature.split(',')
        if feature[0] == '名詞' and node.surface not in stopwords:
            keywords.append(word)
        node = node.next

    return keywords

下記のコードから実際のデータ加工を実行していきます。

download_file = './data/gakumonno_susume.txt'
text = convert(download_file)
import itertools
sentences = [get_meishi(w) for w in text.split("。")]
sentence_combinations = [list(itertools.combinations(sentence, 2)) for sentence in sentences]
sentence_combinations = [[tuple(sorted(words)) for words in sentence] for sentence in sentence_combinations]
target_combinations = []
for sentence in sentence_combinations:
    target_combinations.extend(sentence)
import collections
ct = collections.Counter(target_combinations)
ct.most_common()[:10]

image.png

準備が整いましたので、さっそく共起ネットワークを可視化してみましょう。

共起ネットワークの描画

下記のコードを実行していってください。

!pip install pyvis
# ネットワーク描画のメイン処理定義
def kyoki_word_network():
    from pyvis.network import Network
    import pandas as pd

    #got_net = Network(height="500px", width="100%", bgcolor="#222222", font_color="white", notebook=True)
    got_net = Network(height="1000px", width="95%", bgcolor="#FFFFFF", font_color="black", notebook=True)

    # set the physics layout of the network
    #got_net.barnes_hut()
    got_net.force_atlas_2based()
    got_data = pd.read_csv("kyoki.csv")[:150]

    sources = got_data['first']#count
    targets = got_data['second']#first
    weights = got_data['count']#second

    edge_data = zip(sources, targets, weights)

    for e in edge_data:
        src = e[0]
        dst = e[1]
        w = e[2]

        got_net.add_node(src, src, title=src)
        got_net.add_node(dst, dst, title=dst)
        got_net.add_edge(src, dst, value=w)

    neighbor_map = got_net.get_adj_list()

    # add neighbor data to node hover data
    for node in got_net.nodes:
        node["title"] += " Neighbors:<br>" + "<br>".join(neighbor_map[node["id"]])
        node["value"] = len(neighbor_map[node["id"]])

    got_net.show_buttons(filter_=['physics'])
    return got_net

    #got_net.show("gameofthrones.html")
# 所定の構造でCSVファイルに出力
import pandas as pd
pd.DataFrame([{'first' : i[0][0], 'second' : i[0][1], 'count' : i[1]} for i in ct.most_common()]).to_csv('kyoki.csv', index=False)
# 処理の実行
got_net = kyoki_word_network()
got_net.show("kyoki.html")

image.png

見事、共起ネットワークが可視化がされましたね!

まとめ

テキストマイニングなどでは共起関係の分析を行うことが多いと思いますので、その際に共起ネットワークを簡単に描画できるのは分析が捗りそうですね!