この記事で分かること
- Pythonを使った形態素解析の方法が分かる
- Pythonを使った辞書、コーパスの作り方が分かる
- Pythonを使って初歩的な自然言語処理の方法が分かる
- Pythonを使って頻度情報を可視化する方法が分かる
Pythonで自然言語を分類し可視化する概要
PythonでLDAというトピックモデルを使った自然言語処理の方法を説明します。
ここでは、指定した複数の文書(自然言語が並ぶ文章のこと)に対して、LDA分析を行い、LDAトピックモデル、辞書、コーパスを算出します。
また、LDAトピックモデルをWord Cloudという図を表示させ、文書中の単語の出現頻度を可視化します。
Pythonで自然言語を分類し可視化する前提環境
自然言語処理の方法の前提環境は、下の記事で説明している私の環境と同じ環境で確認しています。
また、レンタルサーバなどのLinuxサーバを使っている場合は、下の記事でプログラミングする環境の作り方を説明しています。
もし、まだプログラムする環境ができていない方は、先にこちらの記事を読んで、プログラミングできる環境をつくることをおすすめします。
Pythonで自然言語を分類し可視化するサンプルプログラム
この章では、複数の文書(自然言語が並ぶ文章)に対して、自然言語処理を行い、結果を確認するサンプルプログラムを説明します。
自然言語を分類し可視化するサンプルプログラムの作成
自然言語処理の中でも、複数の文書(自然言語が並ぶ文章)に対して、LDAトピックモデルをWord Cloudという図を表示させ、文書中の単語の出現頻度を可視化することを行います。サンプルプログラムは次の順番で説明します。
- ライブラリのインポート
- LDA解析に使うパラメータを指定
- treetaggerのインスタンスを取得する
- 動詞と名詞だけ抽出
- 形態素に分解した配列を作成
- 辞書とコーパス作成
- LDAモデルの作成
- LDAモデルと辞書とコーパスの作成
- トピックモデルの画像作成
- 自然言語処理一連の処理
1.ライブラリのインポート
ここでは、全体の処理に必要なライブラリのインポートを行っています。
- 形態素解析のツールであるtreetaggerのPythonラッパーのtreetaggerwrapper。
- トピック分析を行う為のライブラリgensim。
- 数値演算を行うライブラリnumpy
- ワードクラウドという図を描画する為のライブラリwordcloud。
- データをグラフにプロットする為のライブラリmatplotlib。
# Natural language processing
import treetaggerwrapper
import gensim
import numpy as np
# Wordcould
from wordcloud import WordCloud
#import matplotlib
import matplotlib.pylab as plt
- ライブラリ「treetaggerwrapper」をインポートする。
- ライブラリ「gensim」をインポートする。
- ライブラリ「numpy」を別名の「np」としてインポートする。
- ライブラリ「wordcloud」からモジュール「WordCloud」をインポートする。
- ライブラリ「matplotlib.pylab」 を別名「plt」としてインポートする。
2.LDA解析に使うパラメータを指定
次に、LDA解析に使うパラメータを指定します。
LDA_TOPIC_NUM:トピック数
MINIMUM_PROBABILITITY:所属確率の閾値(所属確率が0.001以上のトピックを返す)
#LDA parameter
LDA_TOPIC_NUM = 2
MINIMUM_PROBABILITITY = 0.001
- グローバル変数「LDA_TOPIC_NUM 」の値を「2」に初期化する。
- グローバル変数「MINIMUM_PROBABILITITY 」の値を「0.001」に初期化する。
3.treetaggerのインスタンスの取得
次は、「TreeTagger」のインスタンスを取得しています。「TreeTagger」は、ある英語の文章を単語とその品詞に分解してくれるツールです。品詞の分類は下記のサイトの種類があります。
この環境では、ツール「TreeTagger」が「/usr/local/src/tree-tagger」のディレクトリに配置されているものとして説明します。
def get_treetagger_instatnce():
return treetaggerwrapper.TreeTagger(TAGLANG='en',TAGDIR='/usr/local/src/tree-tagger')
- 関数「get_treetagger_instatnce」を定義宣言する。
- オブジェクト「treetaggerwrapper」の関数「TreeTagger」の戻り値を、関数「get_treetagger_instatnce」の戻り値として返却する。
4.動詞と名詞だけ抽出
tagsで渡されるタグのリストを1つずつtagに格納しながら、名詞と動詞(Be動詞は除く)だけを選別しています。最終的に戻り値docには、名詞と動詞のタグ(単語)がリストされていることになります。
def create_wordtaglist_1doc(tags):
doc = []
for tag in tags:
# The case of Noun
if tag.split()[1].startswith("N"):
doc.append(tag.split()[2])
# The case of Verb except for be verb
elif tag.split()[1].startswith("V") and not tag.split()[1].startswith("VB"):
doc.append(tag.split()[2])
return doc
関数「create_wordtaglist_1doc」を定義宣言する。
- 変数「doc」を空のリストで初期化する。
- 「tags」の要素を1つずつ変数「tag」に格納しながら次の処理を繰り返す。
- 「tag」をスペースで分割した2番目の要素が文字「N」で始まっていた場合は次の処理を実行する。
- 「tag」をスペースで分割した3番目の要素を変数「doc」に加える。
- 「tag」をスペースで分割した2番目の要素が文字「V」で始まり、かつ文字列「VB」で始まっていない場合は次の処理を実行する。
- 「tag」をスペースで分割した3番目の要素を変数「doc」に加える。
- 「tag」をスペースで分割した2番目の要素が文字「N」で始まっていた場合は次の処理を実行する。
- 「doc」を戻り値として返却する。
5.形態素に分解した配列を作成
doc_listの登録されている1つ1つの文章をdocに格納しながら、create_wordtaglist_1docから返却される1文書辺りの形態素解析結果のtagリストをdocsに保存しています。
なので、docsはリストを要素に持つリストなので、2次元のリストになります。
def create_morphological_docs(tagger,doc_list):
docs = []
for doc in doc_list:
tags = tagger.TagText(doc)
docs.append(create_wordtaglist_1doc(tags))
return docs
- 関数「create_morphological_docs」を定義宣言する。
- 変数「docs」を空のリストで初期化する。
- 変数「doc_list」の要素を1つずつ「doc」に格納しながら、次の処理を繰り返す。
- 変数「tags」 を変数「doc」を引数に指定した場合のオブジェクト「tagger」の関数「TagText」の戻り値で初期化する。
- 変数「tags」を引数に指定した時の関数「create_wordtaglist_1doc」の戻り値を変数「docs」に加える。
- 変数「docs」の値を戻り値として返却する。
6.辞書とコーパスの作成
gensimライブラリのcorpora.Dictionaryをコールして、辞書を作成してdictionaryに格納しています。
その後、dictionary.doc2bowをコールして1docあたりのコーパスをリスト化してcorpusに格納しています。
def create_dic_and_corpus(docs):
# create dictonary
dictionary = gensim.corpora.Dictionary(docs)
# create corpus
corpus = [dictionary.doc2bow(doc) for doc in docs]
return corpus,dictionary
- 関数「create_dic_and_corpus」を定義宣言する。
- 変数「docs」を引数に指定した時のライブラリ「gensim」の関数「corpora.Dictionary」の戻り値で変数「dictionary」 を初期化する。
- 変数「doc」のそれぞれの要素を引数に指定した時の関数「dictionary.doc2bow」の戻り値のリストで変数「corpus」を初期化する。
- 変数「corpus」と変数「dictionary」を戻り値として返却する。
7.LDAモデルの作成
def create_LDA_model(corpus,dictionary,topic_num):
# LDA (Latent Dirichlet Allocation) processing
tfidf = gensim.models.TfidfModel(corpus)
corpus_tfidf = tfidf[corpus]
lda = gensim.models.LdaModel(
corpus=corpus_tfidf,
id2word=dictionary,
num_topics=topic_num,
minimum_probability=MINIMUM_PROBABILITITY,
)
return lda
- 関数「create_LDA_model」を定義宣言する。
- 変数「tfidf」 をライブラリ「gensim」の関数「models.TfidfModel」からの戻り値(オブジェクト)で初期化する。
- 変数「corpus_tfidf」 をオブジェクト「tfidf」に引数で得られた変数「corpus」を指定した戻り値で初期化する。
- 変数「lda」 = ライブラリ「gensim」のメンバ関数「models.LdaModel」からの戻り値で初期化する。
- 変数「lda」の値を戻り値として返却する。
8.LDAモデルと辞書とコーパスの作成
create_morphological_docsでコーパスと辞書を作成します。その後、先程求めたコーパスと辞書を元に、create_LDA_modelでLDAモデルを計算しています。LDAモデル、辞書、コーパス、文書のリストを返却しています。
def make_lda_model(doc_list,topic_num):
docs = create_morphological_docs(get_treetagger_instatnce(),doc_list)
corpus,dictionary = create_dic_and_corpus(docs)
lda = create_LDA_model(corpus=corpus,dictionary=dictionary,topic_num=topic_num)
return lda,dictionary,corpus,docs
- 関数「make_lda_model」を定義宣言する。
- 変数「docs」 = create_morphological_docs(get_treetagger_instatnce(),doc_list)
- 変数「corpus」と変数「dictionary」を関数「create_dic_and_corpus」からの戻り値で初期化する。
- 変数「lda」を関数「create_LDA_model」の戻り値で初期化する。
- オブジェクト「lda」のメンバ「dictionary,corpus,docs」を戻り値として返却する。
9.トピックモデルの画像作成
ここでは、トピックモデルをWord Cloudで描画しています。トピックが4つよりも少ない場合は、1行のレイアウトでWord Cloudを表示し、4つ以上になると、数に応じて列を増やしています。ちなみに、Word Cloudの描画は、480*320のサイズで背景を黒に設定しています。
def make_topic_image(lda_model,corpus,dictionary):
print('lda_model.num_topics : ' + str(lda_model.num_topics))
if lda_model.num_topics < 4:
fig, axs = plt.subplots(ncols=lda_model.num_topics, nrows=1,figsize=(15,7))
else:
fig, axs = plt.subplots(ncols=4, nrows=int(math.ceil((lda_model.num_topics+1)/4)),figsize=(15,7))
axs = axs.flatten()
for i, t in enumerate(range(lda_model.num_topics)):
x = dict(lda_model.show_topic(t, 30))
WC = WordCloud(
width=480,
height=320,
background_color='black',
max_words=2000,
).fit_words(x)
axs[i].imshow(WC)
axs[i].axis('off')
axs[i].set_title('Topic '+str(t))
plt.tight_layout()
plt.savefig('wordcloud/'+'best_topic_wordcould.png')
plt.show()
- 関数「make_topic_image」を定義宣言する。
- オブジェクト「lda_model」のメンバ変数「num_topics」の内容を表示する。
- オブジェクト「lda_model」のメンバ変数「num_topics」が 値「4」よりも小さい場合は次の処理を実行する。
- 変数「fig」と変数「axs」をオブジェクト「plt」のメンバ関数「subplots」からの戻り値で初期化する。
- オブジェクト「lda_model」のメンバ変数「num_topics」が 値「4」以上の場合は次の処理を実行する。
- 変数「fig」と変数「axs」をオブジェクト「plt」のメンバ関数「subplots」からの戻り値で初期化する。
- 変数「axs」 をオブジェクト「 axs」のメンバ関数「flatten」からの戻り値で初期化する。
- オブジェクト「lda_model」のメンバ変数「num_topics」のそれぞれの要素を変数「t」に、その要素の順番を変数「i」に格納しながら、次の処理を繰り返す。
- 辞書形式に変換する関数「dict」に「lda_model.show_topic」を指定した時の戻り値で、変数「x」を初期化する。
- 変数「WC」 を関数「WordCloud」の戻り値で初期化する。
- 変数「i」番目のオブジェクト「axs」のメンバ関数「imshow」を呼び出す。
- 変数「i」番目のオブジェクト「axs」のメンバ関数「axis」を呼び出す。
- 変数「i」番目のオブジェクト「axs」のメンバ関数「set_title」を呼び出す。
- ライブラリ「plt」のメンバ変数「tight_layout」を呼び出す。
- ライブラリ「plt」のメンバ変数「savefig」を呼び出す。
- ライブラリ「plt」のメンバ変数「show」を呼び出す。
10.自然言語処理一連の処理
doc_listに5つの文書(文章)をリスト形式で格納します。これが分析対象のデータとなります。その後、doc_listを引数にLDAモデルを作成する為にmake_lda_modelのメソッドをコールしています。
これによってLDAモデル、辞書、コーパス、文書リストが得られます。最後に、LDAモデルをWord Cloudという図で描写する為に、make_topic_imageのメソッドを呼び出します。
if __name__ == '__main__':
# Target doc list
doc_list = [
u'This is Tommy.',
u'My name is Tommy.',
u'It is python code.',
u'Python is very cool.',
u'You are a specialist.'
]
# Create lda dictionary corpus
lda,dictionary,corpus,docs = make_lda_model(doc_list=doc_list,topic_num=LDA_TOPIC_NUM)
print(lda)
print(dictionary)
print(corpus)
# Making and showing topic image
make_topic_image(lda_model=lda,corpus=corpus,dictionary=dictionary)
- 変数「doc_list」 を初期化する。
- 変数「lda」と変数「dictionary」と変数「corpus」と変数「docs」を関数「make_lda_model」からの戻り値で初期化する。
- 変数「lda」の内容を表示する。
- 変数「dictionary」の内容を表示する。
- 変数「corpus」の内容を表示する。
- 変数「lda」と変数「corpus」と変数「dictionary」を引数に指定して、関数「make_topic_image」を呼び出す。
自然言語を分類し可視化するプログラムの実行結果
上記メイン関数での処理の結果、下記のような結果が得られました。単語が5種類、トピック数が2、ディケイとチャンクサイズはデフォルトの値になっています。Print文で、LDAモデル、辞書、コーパスを順に表示しているので、その結果が下記のようになります。
LdaModel(num_terms=5, num_topics=2, decay=0.5, chunksize=2000)
Dictionary(5 unique tokens: [u'python', u'specialist', u'code', u'name', u'Tommy'])
[[(0, 1)], [(0, 1), (1, 1)], [(2, 1), (3, 1)], [(2, 1)], [(4, 1)]]
lda_model.num_topics : 2
下の図がWord Cloudという図で単語の出現頻度を描写した結果です。文書数が極端に少ないので、トピックのしての特徴があまりよく分かりませんが、「名詞としての主旨としてトピック」と、「Pythonのスペシャリストとしてのトピック」の2つ分かれているような図になっているような気がします。