日本語形態素解析

「入門自然言語処理」読み進めシリーズ。12章の形態素解析アルゴリズムの箇所をせっせと見よう見まねでプログラム打ちながら読み進めていました。

p.484のanalyze_simpleのところで、途中_BOS_ENTRYと_EOS_ENTRYが突然出てきます。定義されてない変数ってことでエラーでそう、と思いつつp.486の結果を出すところまでプログラムを打ち込み実行しましたが、やはり上述の理由でだめでした。しばらくあれこれ試行錯誤し、

_BOS_ENTRY = {'pos':'BOS','lemma':u'BOS','length':1}
_EOS_ENTRY = {'pos':'EOS','lemma':u'EOS','length':1}

として関数内で定義したらエラー無く本のとおりの以下の結果

BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/まで/待つ/EOS
BOS/彼/の/車/で/待つ/EOS

が得られました。dict_entriesのところに加えて定義してもいいのかな、と思いましたが、辞書の各エントリのlist[0]、つまり表層形のところをどうしたらいいものか、という問題があったのでひとまず辞書エントリとは分けての定義が無難かなと判断しました。

それにしてもここで学んだ簡易な形態素解析ですが、簡易なものとはいえプログラムの流れを追うだけで今はいっぱいいっぱいいです。このレベルのアルゴリズムをすらすらプログラムに直せる実力がいち早く欲しいところです。

文字列整形式

「入門自然言語処理」p.177にて。

timeitモジュールを使って、list,set型のオブジェクトの中にある要素が存在しているかどうか、のチェックに要する処理速度を比較するところ。

本の通りにプログラムを打ち込んで実行すると、、

Traceback (most recent call last):
File "prac0321.py", line 8, in
print Timer(statement,setup_list).timeit(1000)
File "C:\Python26\lib\timeit.py", line 193, in timeit
timing = self.inner(it, self.timer)
File "", line 6, in inner
NameError: global name 'vocabrandom' is not defined

vocabrandomが定義されていない??

試行錯誤すると、この行

statement = "random.randint(0,%d) in vocab" % vocab_size * 2

において、%が*よりも優先順位が高いため、vocab_sizeを%dに代入した後、文字列全体を2倍するという挙動になってしまっていて、つまりstatementが

random.randint(0,10000) in vocabrandom.randint(0,10000) in vocab

という文字列になってしまっていたのでした。以下のように修正しO.K.

statement = "random.randint(0,%d) in vocab" % (vocab_size * 2)

しかし、肝心の処理速度の違い、setが遥かに速い理由が今ひとつピンと来ない。今の理解として、「set型は中身の検索がなぜか超速い!!」程度にして後に理解できる時が来るのを待とうと思います。

更新再開、東北地方太平洋沖地震を受けて

発生から丁度一週間。亡くなられた方のご冥福をお祈りするとともに、被災地で復興に向けて尽力している全ての方々を心から応援したいと思います。国が一丸となって長期的な支援をすることが必要でしょうから、沢山募金したい、行けるなら現地に行きたい、このような気持ちを一過性のものとせず、末永く持ち続けること、それが今の自分がするべきことなのかな、と考えております。

そして、学生、とりわけ大学院まで進学させてもらった身の自分としては、とにかく勉強が大事です。地震のニュースが気がかりで勉強不足なんです、というのは理由になりません。被災地の方は自分じゃ想像できないくらいの強い気持ちで復興に向けて取り組んでいると思います。沈んで暗くなっているより、むしろ被災地の方に元気をもらって自分のやるべきことに取り組まなくてはいけない。

今回の地震では、ツイッターを始めとするSNS、またGoogleの提供したperson finderや通行可能な道路の表示と言ったwebアプリケーション、ustreamによるTV放送など、web上のメディアが果たした役目は非常に大きかったと思います。自然言語処理の分野の研究者の方々でツイッターのつぶやきにタグ付けをして安否情報を整理する(学習して後々の震災時に自動でつぶやきを分類するようなものが作れる?)という活動が行われたりもしていました。この辺、ゆくゆくはじっくり自分なりに検証したいです。

今回、コンピューターの中の技術が、実際に人の役に立ち、回りまわってきっと多くの人の命を救うことにも繋がったに違いない、と考え、これから自分が取り組む研究や勉強も、決してこういうことと無関係ではないのかな、という思い込みにより、モチベーションを高く保っていこうと決意しました。

さて、最大事後確率推定(MAP推定)の勉強の続きを行いたいと思います。教科書を一回読んで「は?何言ってんの?」ってなっても、何度が粘って読み直していれば、「あっ!」っていう瞬間が来ますね。今ようやくその瞬間をつかみかけたところです。

ラグランジュの未定乗数法

「言語処理のための機械学習入門」の1.2節、ラグランジュの未定乗数法のところまでを一応読み終える。途中、最大化をする式(目的関数)とラグランジュ関数自体がごちゃごちゃになって??だったがあくまで最大化するのは目的関数だな、と気づいてからは何とか頭がすっきりしてきた。

p.20の例題1.8では最終的な目的関数の値(最適解)とラグランジュ関数の値がともに-1/2と等しくなっていたために両者を取り違えて混乱してしまったけど、多分たまたま値が同じだっただけかな。

現時点ではこの最適化問題についての知識が言語処理にどう使われるのかまったく分からんけど、

確率分布のパラメータ推定でも、EMアルゴリズムでも、ナイーブベイズ分類器でも、サポートベクトルマシンでも、ラグランジュの未定乗数法が使われている
(本書p.15より)

うーん期待大です。「サポートベクトルマシンでは〜」とかさらっと口に出してみたい。

tagged_words()で返されるtuple

jeitaコーパスにて、tagged_words()で返されるturpleの中身についてメモ

アルファベット、数字は

(u'not', u'\tnot\t記号-アルファベット'),
(u'notation', u'\tnotation\t記号-アルファベット'),
(u'now', u'\tnow\t記号-アルファベット'),
(u'13', u'\t13\t名詞-数'),
(u'25', u'\t25\t名詞-数'),

タプルを(w,t)として、2番目の要素を\tでsplitしたときに、読みに当たるt[0]は空。

また未知語は、

(u'トリニティ', u'\t\t未知語'),
(u'ナジャーハ', u'\t\t未知語'),
(u'ナブルス', u'\t\t未知語'),

t[0],t[1]が空。カタカナだから未知語と言えどそのままのカタカナを読みにしても良さそうだと思うけど、敢えてそうしていない理由があるんだろうな。まあいいや。

tagged_words

「入門自然言語処理」素人の読み進めシリーズです。
p.476にて

genpaku_tfd = FreqDist(t[2] for (w, t) in genpaku.tagged_words())

と言う箇所。このままだと品詞タグがちゃんと表示されなかった。あれれ。tagged_words()メソッドで返ってくるlistの形式を調べてみると、

(   u'まずしい',
    u'マズシイ\tまずしい\t形容詞-自立\t形容詞・イ段\t基本形')
(u'王子', u'オウジ\t王子\t名詞-一般')
(u'さま', u'サマ\tさま\t名詞-接尾-人名')
(u'が', u'ガ\tが\t助詞-格助詞-一般')
(u'おり', u'オリ\tおる\t動詞-自立\t五段・ラ行\t連用形')
(u'まし', u'マシ\tます\t助動詞\t特殊・マス\t連用形')
(u'た', u'タ\tた\t助動詞\t特殊・タ\t基本形')

(w,t)のtにあたる部分ではタブ区切りで読み、原形、品詞1、品詞2と入っている。ってことは\tでsplitしてindex2にあたる品詞1を取り出して...

と考えて以下のコードでO.K. 本が書かれた時点とでコーパスの形式が変わってたのかな?まっいっか。
FreqDistはプログラムはじめでimport nltkしているのでnltk.FreqDistで呼び出す形式にしてみました。

genpaku_tfd = nltk.FreqDist(t.split('\t')[2] for (w,t) in genpaku.tagged_words())

genpaku_tfd.tabulate(10)

出力は、

助動詞 動詞-自立 名詞-一般 助詞-格助詞-一般 記号-読点 記号-句点 助詞-接続助詞 助
詞-係助詞 助詞-連体化 名詞-代名詞-一般
 350  335  305  240  132  125  123  117   82   66

ちなみに教科書では沢山のコーパスを読み込んでいますが、逐一読み込みの時間が掛かるので、g0005.chasenというものだけ読み込んでいます。適当に選んだけどこれは何の話だろう。。童話っぽいが。

jeitaディレクトリ内にあった_copyright.htmlより調べると、そのタイトルは、、

なんと「ブタ飼い王子」でした。聞いたことないな。作者はアンデルセン。息抜きに今度読もう。

正規表現

むむむ。。

str1 = u"あ\nいうえお"
str2 = u"あ\n"
if re.match(str2,str1):
    print "match!"
else:
    print "not match!"

これはちゃんとmatch!
しかし、

str1 = ur"あ\nいうえお"
str2 = ur"あ\n"
if re.match(str2,str1):
    print "match!"
else:
    print "not match!"

はnot match!になる。

さらにさらに

str1 = u"あ\nいうえお"
str2 = ur"あ\n"

はmatch,

str1 = ur"あ\nいうえお"
str2 = u"あ\n"

はnot match

原因究明中。これ理解したら家帰ろうっと。


追記 同日21:26

あんまりしっくりとした解釈までたどり着かなかったので、とりあえず以下のように今日は雑にまとめ。

正規表現のための文字列ではエスケープやメタ文字関係がごちゃっとするのでrをつけてrawで。マッチを探す方の文字列はごくごく普通に、rawを付けない。ruby正規表現使ってたときはこういうことで頭をひねった記憶が無いんだけどなぁ。。まあいっか。