スクリーンショット_2019-02-12_9

議事録アナロジー2

市の予算や、議員に関する特性を市の議事録などの公開データから抽出する方法を検討していきます。前回最近の機械学習自然言語処理について整理しました。そのなかから特別なラベルがなくても学習を進めることができる教師なし学習のWordEmbeddingを試してみます。

WordEmbeddingでできること

WordEmbbedingでは単語ごとにユニークな番号をふって、その単語の前語の単語から出現確率を、その単語の特徴として数百次元のベクトルとして表現します。 同じような文脈で使われる言葉は似たようなベクトルとして表現されるという意味を数値かするということが可能になっています。しかもその意味は人間が個別に教えるわけではなく多量の文章を入力することでそのなかで使われている単語の関係からプログラムが抽出していきます。研究者のなかで英語のWikiを大量に入力させた機械学習のモデルが公開されています(残念ながら公開されている学習済みのモデルはほとんど英語、フランス語、中国語などです)。その公開されているモデルをつかった例としてそれぞれ'baby', 'computers','beautiful'に近い単語を5つまで表示させるといかのような出力を得ることができます。英語の文法も教えてないのに、きっちり意味を理解しているようにみえます。 すごいですね。(これを実行するプログラムは下記で公開されています)

get_knn(vocab, 5, 'baby')
['babies', 'boy', 'girl', 'newborn', 'pregnant']
get_knn(vocab, 5, 'computers')
['computer', 'phones', 'pcs', 'machines', 'devices']
get_knn(vocab, 5, 'beautiful')
['lovely', 'gorgeous', 'wonderful', 'charming', 'beauty']

日本語をTokenに切り出す

さて、日本語でも同じように機械学習を任意の文章で実行したいと思ったときに、まず最初につまづくのは日本語はスペースで区切られていないので英語を対象としたプログラムがそのまま動くわけではないというところです。先人の研究者が日本語用のTokenizerを公開してくれていますのでそちらを利用するようにしましょう。

以下のように、文章を入力するとそれを単語に区切ってかつ品詞まで教えてくれます。日本人の努力がつまっています。

% mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd/ 
私は馬鹿な男だなー本当に。

私	名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
馬鹿	名詞,形容動詞語幹,*,*,*,*,馬鹿,バカ,バカ
な	助動詞,*,*,*,特殊・ダ,体言接続,だ,ナ,ナ
男	名詞,一般,*,*,*,*,男,オトコ,オトコ
だ	助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ
なー	助詞,終助詞,*,*,*,*,なー,ナー,ナー
本当に	副詞,一般,*,*,*,*,本当に,ホントウニ,ホントーニ
。	記号,句点,*,*,*,*,。,。,。
EOS

以下のような簡単なpythonプログラムで文書全体を単語のスペース区切りに変換できます。

# -*- coding: utf-8 -*-
import MeCab
def wakati(text):
   t = MeCab.Tagger("-Owakati")
   m = t.parse(text)
   return m

def main(file):
   f = open(file)
   data = f.read()
   f.close()
   text = data
   res = wakati(text)
   f = open(file + "corpus-ja.txt", "w")
   f.write(res)
   f.close()
   print ("corpus-ja generated")

main('yotsukaido/giji30_08_27.txt')

まずはサンプルを市議会議事録でうごかしてみよう

まずは下記から四街道市議会の議事録をコピーしてきます http://ssp.kaigiroku.net/tenant/yotsukaido/SpTop.html

それを上記のMecabを使って単語化したのち、下記のTutorialを参考にEmbeddingモデルとしてTrainingします。 

文章を1000単語ごとのセンテンスに区切るところが日本語の場合工夫する必要があり、下記のように変更しています。

f = open(testfile,encoding='utf-8')
words = f.read().split()
f.close()

max_sentence_length = 1000
if max_sentence_length:
    data = []
    subcount = (int)(len(words)/max_sentence_length)
    for si in range(subcount) :
        subset = words[si*max_sentence_length:(si+1)*max_sentence_length]
        data.append( subset )

text8 = data

#-- sample output
# tokens: 1000 ['決算', 'は', '、', '歳入', '総額']
# tokens: 1000 ['税', 'の', '普通', '交付', '税']
# tokens: 1000 ['もの', 'で', 'あり', 'ます', '。']
# tokens: 1000 ['議', '議', '事', '日', '程']

単語ごとのカウントを取ると以下のようになっています。それらしいデータですが、一部単語が漢字で区切られてしまうところがあり原因をみてみるともともとの議事録の書式が、一般的でないところで空白がはいっていたりします。お役所の文書固有の等幅割当などの特殊ルールを今後考慮しないといけなさそうです。。

        '館内': 2,
         '省略': 9,
         'プロ': 2,
         '資格': 7,
         '過半数': 1,
         'この': 403,
         '特殊': 3,
         '農産': 1,
         '論議': 1,
         '主任': 3,
         '報告': 164,
         '発': 5,
         '重要': 37,
         '助言': 5,
         '197': 2,
         '長屋': 1,
         '企業': 14,
         'にわたる': 2,
         'どんどん': 4,
         '歳月': 1,
         '140': 3,
         'ただいま': 13,
         '佐渡': 40,
         'ねらい': 1,
         '掘削': 6,
         '数カ月': 2,
         '後継': 2,
         '忘れ': 3,
         'IT': 1,
         'とり': 8,
         '招く': 1,
         '新和': 3,
         '壇上': 43,
         '見直し': 42,
         '終始': 1,
         '振動': 3,
         '身体': 7,
         '互選': 1,
         'いつ': 10,
         'に当たり': 3,
         '管': 11,
         '裏': 3,
         '匹敵': 1,
         '包括': 22,
         '崩れ': 2,
         '傷': 1,
         '新陳代謝': 1,
         'あそび': 2,
         '居宅': 7,
         '人': 291,
         '増強': 1,

機械学習ではこれらの単語を300次元のベクトルに変換する重み行列を学習させます。学習してもしていなくても適当な行列さえ用意すればベクトルには変換できて、そのベクトル同士の内積(コサイン類似度)を出すとベクトルの向きが似ている単語が抽出できるという理屈です。(理屈はシンプルですが、数学をまじめにやっていなかった私は以下のチュートリアルコード1行読み進むのに1時間以上かかりました、、)

def norm_vecs_by_row(x):
    return x / (mx.nd.sum(x * x, axis=1) + 1e-10).sqrt().reshape((-1, 1))

def get_k_closest_tokens(vocab, embedding, k, word):
    word_vec = norm_vecs_by_row(embedding[[word]])
    vocab_vecs = norm_vecs_by_row(embedding[vocab.idx_to_token])
    dot_prod = mx.nd.dot(vocab_vecs, word_vec.T)
   ...

トレーニング前後のモデルで類似の単語をだしてみよう!

まず学習前の結果です

get_k_closest_tokens(vocab, embedding, 10, "四街道")
get_k_closest_tokens(vocab, embedding, 10, "子育て")
get_k_closest_tokens(vocab,embedding,10, "戸田")

# 出力
closest tokens to "四街道": べく, 付, 街道, 発議, 規制, 2, 着手, 人員, 根拠, 茨城
closest tokens to "子育て": 昨日, 同意, 滞納, 命令, 拡充, 放置, 構想, たい, おいしい, 増設
closest tokens to "戸田": 防災, 緩和, 暫時, 末, 流れ, 辺, 水準, 程度, 候補, 急激

ザッツランダムという漢字の結果ですね。四街道⇒茨城とか子育て⇒放置 とかドキッとする表現もありますが まーランダムですね。ちなみに"戸田"さんというのは議員名で議長をやっていてよく議事録にでてきたのでサンプルとして使用させていただきました。

つぎに学習後の結果です

get_k_closest_tokens(vocab, embedding, 10, "四街道")
get_k_closest_tokens(vocab, embedding, 10, "子育て")
get_k_closest_tokens(vocab,embedding,10, "戸田")

# 出力
closest tokens to "四街道": 市, 市議会, 第, TOP, 平成, 閲覧, 検索, 埋立, 09, 市内
[[0.46421304]]
closest tokens to "子育て": 日本一, 待機, 子ども, 若い, 世代, 取り巻く, 悩み, 子供, 女性, 掲げ
[[0.56040186]]
closest tokens to "戸田": 由紀子, ○, 議長, さん, 。, 〔, 以上, 〕, ます, 当局
[[0.9712401]]

おー! それらしい結果がでてきました。 子育てに関して市議会ではなされた内容の概要が抽出できているとも言えそうです。ただしコサイン類似度はそこまで高いわけではないのでこの結果をどのように利用していくかはまだまだ検討が必要そうです。 きりがいいので今日はここまでとします。次回はキャッチーな単語同士の足し算、引き算にトライしてみたいと思います。

この記事が気に入ったらサポートをしてみませんか?