隠れマルコフ実装してみた。

PRML 13章読んで、隠れマルコフモデルを実装してみた。今回は Python + numpy の習作も兼ねている。

今回実装してみたアルゴリズムは以下の通り。数字は PRML の章番号。
まあなんて盛りだくさん。

テキストファイルを与えると、1行1系列とみなして、アルファベットからなる単語を抽出し、HMM で学習&文章をサンプリング&学習データのトピック推定を行う。


手頃なコーパスとして The Tale of Peter Rabbit をトピック数(隠れ変数の状態数) 20 で学習させ、サンプリングした文章が例えばこんな感じ。

basket don't ran i tool-shed under there the the flower-pot quite by after in 
go flower-pot hung on the the beans gather work rake shoes had down only
he amongst had to was cotton-tail stopped happened he rabbits began 
and once under so away much scuttered behind no he a into and and feeling 

厳しい(苦笑)。


PRML 13.2.6 に「隠れマルコフモデルはデータの生成モデルとしてはきわめて貧弱であることが多い」とあるとおり、まともな文章を出力させるためには相当の工夫が必要になるんだろう。
多分、マルコフ連鎖+バイグラムとかの方がはるかに少ない労力でもっとましな文章が得られると思う(それで満足するなら)。


トピック推定はこんな感じ。

[('once', 11), ('upon', 12), ('a', 4), ('time', 3), 
('there', 11), ('were', 10), ('four', 4), ('little', 10), ('rabbits', 12), 
('and', 19), ('their', 7), ('names', 11), ('were', 10), 
('flopsy', 12), ('mopsy', 19), ('cotton-tail', 19), ('and', 19), ('peter', 7)]


ちなみに、R ではなく Python + numpy を使ったのは、HDP-HMM に発展していきたいため。
ノンパラベイズを Gibbs で解く分には行列計算とかあんまりいらないので、R の良さがむしろ邪魔してしんどい。
最初 Ruby で実装しかけたんだけど、ガンマ関数とか各種分布とか自分で実装したくなかったので、numpy/scipy を使うことに。
まわりに Ruby 使いがいなくて、Python の方が喜ばれるかな、というものあるw


numpy を使ってみて、なかなか R と同じように使えることがわかった。
hmm.py は上に書いたようにそこそこてんこもりだけど、170 行ととても短く書けた。numpy に慣れればまだもうちょっと短くなるだろう。
できるだけループしないようにすればかなり高速だし。
とはいえ、さすがに R はベクトルや行列をネイティブサポートしている分、直感的に書ける度合いでは R が上(Python は numpy.array とネイティブ array がどうしても混在してしまうのが悩ましい。リスト内包とか使いたいし……)。


numpy を使うときにもう一つ気をつけないといけないのは、array(ndarray) と matrix の違いだろうか。
ndarray を2次元で使うときは一見 matrix と違いがないように見えるが、実は全然違う。
たとえば積演算子 * の挙動。ndarray * ndarray や ndarray * スカラーは要素ごとの積になるけど、左右どちらかが matrix だと行列の積になる。matrix で要素ごとの積をしたい場合(実装してると結構あるある!)、 .A で ndarray に変換すればいい。
また、逆行列 .I やエルミート行列 .H をサポートしているのは matrix だけ、などなど。


【追加】
書き忘れ。
今回は最尤推定を EMA で解いているわけだが、遅い。1000回回しても、まだ尤度がじりじり増えていく。
明らかに自然にオンライン化できそうだが、自分で書いてみるか、ohmm を利用させてもらうか。
【/追加】