動機
ニュース記事等のトピック推定については腐る程で出回ってるからちょっと違った切り口からトピックモデルを実践したかった
"janome"というめちゃくちゃ日本語前処理の便利なツールを見つけたから使ってみたかった
コーパス
名大会話コーパスを用いた
前処理
入力文を分かち書きにする
janome.tokenizer.Tokenizer()を用いる。引数でmmap=TrueとすることでNEologd辞書となる。文字種の統一(数字→0など)、記号の排除
NumericReplaceFilter() ← 漢数字を0にする
RegexReplaceCharFilter('\d+', '0') ← 数字を0にする
RegexReplaceCharFilter(u'>', '') ← 「>」を排除
...英文字を全て小文字化
LowerCaseFilter()名詞のみを抽出する
POSKeepFilter(['名詞'])漢数字を0にする
janomeの組み込み関数にはないので自分で定義する必要がある
class NumericReplaceFilter(TokenFilter): def apply(self, tokens): for token in tokens: parts = token.part_of_speech.split(',') if (parts[0] == '名詞' and parts[1] == '数'): token.surface = '0' token.base_form = '0' token.reading = 'ゼロ' token.phonetic = 'ゼロ' yield token
- 1文字のものを排除する
OneCharacterReplaceFilter()で読み出すが、これもjanomeの組み込みにはなかったので定義する必要があった。
class OneCharacterReplaceFilter(TokenFilter): def apply(self, tokens): for token in tokens: if len(token)==1: continue yield token
上記の処理関数を統合してjanome.analyzerに渡して前処理機は完成だ。あと個人的にストップワードをいくつか省きたかったからそれを省くコードも下には含まれている。これだけのコードで基本的な前処理が行えてしかも追加のフィルターも簡単に定義できてしまうのがjanomeの魅力だと感じた。
def janome_meishi(sentence): # janomeモデルの定義 token_filters = [ NumericReplaceFilter(), # 漢数字を0にする POSKeepFilter(['名詞']), # 名詞のみを抽出する LowerCaseFilter(), # 英字は小文字にする ExtractAttributeFilter('base_form'),# 原型のみを取得する OneCharacterReplaceFilter() # 1文字のものを除外する ] char_filters = [UnicodeNormalizeCharFilter(), RegexReplaceCharFilter('\d+', '0'), ... RegexReplaceCharFilter(u'Inf', u''), RegexReplaceCharFilter(u'>', u'') ] tokenizer = Tokenizer(mmap=True) analyzer = Analyzer(char_filters, tokenizer, token_filters) text = [w for w in analyzer.analyze(sentence)]
LDA
BoWの作成
出現回数が1回以下のものと0.4割以上の文書で現れる単語は省いたBoWを作成した
dictionary = gensim.corpora.Dictionary(text_data) dictionary.filter_extremes(no_below=1, no_above=0.4)
LDAの学習
corpus = [dictionary.doc2bow(words) for words in text_data] lda = gensim.models.ldamodel.LdaModel(corpus=corpus, num_topics=2, id2word=dictionary, random_state=19)