自然言語処理の研究を始めるにあたって LDA に触れておこうと思ったので,ついでに簡単にまとめてみます.
この分野はまだ学び始めたばかりなので,もし間違いがあれば指摘していただけると嬉しいです.
実行環境には Google Colaboratory を使用しました.
LDA について
トピックモデルは,テキストデータの集合から得られる抽象的なトピックを見つけ出すための,確率モデルの一種です.LDA (Latent Dirichlet Allocation) はそのようなトピックモデルの一つで,あるドキュメントを適当なトピックに分類するために使われます.ドキュメント毎のトピックが占める割合,及びトピック毎の単語が占める割合をそれぞれ算出します.
LDA でできることを簡単に述べると,
- 文書の集合はどんなトピックに分類できるのか
- ある文書にはどんなトピックがどの程度含まれているか
- あるトピックはどの単語がどの程度構成しているのか
のようなことがわかります.
LDA についてはこのあたりの記事で紹介されています.
トピックモデル(LDA)で初学者に分かりづらいポイントについての解説
LDA の入力データについて
テキストデータを LDA で使用するためのデータの加工方法として,BOW (Bag of Words) や tf-idf があります.これらは以下の記事で紹介されています.
実際に使ってみる
ここでは以下の記事を参考にしながら,英語のニュース見出しのクラスタリングをやっていこうと思います.
https://towardsdatascience.com/topic-modeling-and-latent-dirichlet-allocation-in-python-9bf156893c24
データの準備
今回使用するデータはここからダウンロードします(要:kaggle へのサインイン)
https://www.kaggle.com/therohk/million-headlines/data
データを Python で利用できるように読み込みます.
import pandas as pd
data = pd.read_csv('abcnews-date-text.csv', error_bad_lines=False);
data_text = data[['headline_text']]
data_text['index'] = data_text.index
documents = data_text
データの中身がどうなっているのか少し見てみると,およそ110万件のドキュメント(ニュース見出し)があって,そのうち上から5件が上の表のようになっていることがわかります.
print(len(documents))
print(documents[:5])
1103663
headline_text | index | |
---|---|---|
0 | aba decides against community broadcasting lic... | 0 |
1 | act fire witnesses must be aware of defamation | 1 |
2 | a g calls for infrastructure protection summit | 2 |
3 | air nz staff in aust strike for pay rise | 3 |
4 | air nz strike to affect australian travellers | 4 |
前処理
次の手順でデータを処理します.
- ドキュメントのトークン化:具体的には,テキストを文単位に,文を単語単位にそれぞれ分割します.大文字の単語を小文字に修正し,句読点を取り除きます.
- 3文字未満の単語の除去
- ストップワードの除去:ストップワードとは,頻出あるいは一般的すぎて,利用価値が低いと考えられる単語のことをいいます.ex. the, a, for
- 単語を見出し語化 (lemmatize) :具体的には,三人称の単語は一人称に,過去時制及び未来時制の動詞は現在時制に変更されます.
- 単語の語幹抽出 (stemming)
処理には gensim と nltk を使います.
import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS
from nltk.stem import WordNetLemmatizer, SnowballStemmer
from nltk.stem.porter import *
import numpy as np
np.random.seed(2018)
import nltk
nltk.download('wordnet')
lemmatize の例を確認してみます.
print(WordNetLemmatizer().lemmatize('went', pos='v'))
go
stemming の例を確認してみます(結果は省略).
stemmer = SnowballStemmer('english')
original_words = ['caresses', 'flies', 'dies', 'mules', 'denied','died', 'agreed', 'owned',
'humbled', 'sized','meeting', 'stating', 'siezing', 'itemization','sensational',
'traditional', 'reference', 'colonizer','plotted']
singles = [stemmer.stem(plural) for plural in original_words]
pd.DataFrame(data = {'original word': original_words, 'stemmed': singles})
lemmatizeとstemming をする関数を定義し,前に述べた手順で前処理をするための関数を作成します.
def lemmatize_stemming(text):
return stemmer.stem(WordNetLemmatizer().lemmatize(text, pos='v'))
def preprocess(text):
result = []
for token in gensim.utils.simple_preprocess(text):
if token not in gensim.parsing.preprocessing.STOPWORDS and len(token) > 3:
result.append(lemmatize_stemming(token))
return result
ドキュメントを一つ選択して前処理の前後比較をしてみましょう.
doc_sample = documents[documents['index'] == 4310].values[0][0]
print('original document: ')
words = []
for word in doc_sample.split(' '):
words.append(word)
print(words)
print('\n\ntokenized and lemmatized document: ')
print(preprocess(doc_sample))
original document:
['rain', 'helps', 'dampen', 'bushfires']
tokenized and lemmatized document:
['rain', 'help', 'dampen', 'bushfir']
きちんと動作していることが確認できたので,全ドキュメントの前処理をします.完了には少し時間がかかると思います.
processed_docs = documents['headline_text'].map(preprocess)
長くなってしまうので,今回はここまでとします.
次回は,LDA の入力データ (BOW, TF-IDF) の作成と,LDA モデルの作成について書こうと思います.
次回 -> LDA (Latent Dirichlet Allocation) を使って英文記事をクラスタリングする(分類編)