WebDB Forum 2013 で「どの言語でつぶやかれたのか、機械が知る方法」について発表しました。

11/27-28 に京都で開催された WebDB Forum 2013(第6回 Webとデータベースに関するフォーラム) の、サイボウズの技術報告セッションにて「どの言語でつぶやかれたのか、機械が知る方法」という題で発表させてもらいました。聞いて下さった方(ustream 中継含む)、関係各位、ありがとうございました。


発表資料はこちら。



テーマは過去に発表済みの「∞-gram ロジスティック回帰を使った短文言語判定」なので、技術的に目新しいことは特にない。実装が新しくなったり、細かい工夫はちらほらなくもないんだけど、そのあたりは基本端折ってしまったし。20分ちょっとの発表時間でモデルの話をすると大火傷を負うことは身にしみてわかっちゃったんだ……。
というわけで、「twitter やチャットといったネット上の informal な場で、諸言語が正書法からはみ出しまくっている様子」を中心にお話しした。それでも20分だとやっぱりショートバージョンになってしまうんだけどね。なんなら一時間でも二時間でも話せてしまうんだけど、モデルの話と違って途中で打ち切られても問題ないw
twitterfacebook などにあふれるテキストを使って、日本語なり英語なり、特定の言語だけを対象としたサービスや研究だけではなく、多言語をターゲットとしたものを考えるなら、今回のような話は知っておいて損はないと思う。


もともとの言語判定器は ldig という名前で公開しているのだが、こちらは扱いとしてはプロトタイプ版であり、API 的なものは整備していない。
実装も Python で、学習にまる1日かかるていたらく。


そこでぼちぼちと C++ 版を実装していたのだが、今回の発表に合わせてとりあえず最低限の体裁を整えてリポジトリにコミットしておいた。しかしドキュメントもまだないし、学習済みモデルも x64 ビルド用しか付いていない(size_t をそのまま吐いているので、プラットフォームによってフォーマットが違ってしまっている)。

ビルドは今のところ Windows のみで、 Visual C++ 2010 用のプロジェクトファイルが付いている(C++11 をちょいちょい使ってるので、それより前の VC ではビルドできない)。gcc on Linux でも微調整で通ると思うが、手を付けてない。ごめん。


なおビルドには cybozulib が必要なので、以下のリポジトリから ldigcpp のディレクトリ下にチェックアウトする必要がある。

岡野原さんの esaxx も必要だが、こちらは ldigcpp のリポジトリに同梱させていただいている。


というわけで、現状では余程の物好きでなければ手を出す気にもならないと思うが、ゆるゆると整備していく、かもしれない。
元の python 版 ldig と比較しての第一の変化はやはり速度だろうか。まる1日かかっていた学習が、20分で完了する。この速度ならパラメータを色々変えて実験してみたり、アホみたいに cross validation 回しまくったりできるので、やはり速いは正義と実感。


もう一つの変化は、ラテン・アルファベット以外の言語にも対応したこと。リポジトリにある学習済みモデルは、以下の50言語に対応したものとなっている。

特に Arabizi(とは何かについては発表資料参照) やモンゴル語(キリル文字表記)は Google 翻訳や Choromium の言語判定器も対応していないので、超ごくごく一部の人には値打あるのでは。精度はまだちょっと低いのだけどね。
本当はバスク語ガリシア語やスロバキア語やスロベニア語やネパール語マラヤーラム語や……なども用意していたのだが、さすがにツイート100件にも満たないのは除外した。


必要な 50言語コーパスは以前の ldig で作った 50万件は一部のみ再利用したが、40万件(訓練)、7.6万件(テスト)を基本新しく作りなおした。なぜそのまま再利用しなかったのかというと、旧コーパスの大部分は twitter のステータス ID を捨ててしまっていたので、配布できる形式ではなかったからだ。
今回の新コーパスは全部ステータス ID が付いているので、(ステータス ID, 言語ラベル) の組で配布できる。
が、ステータス ID から実際のツイートを取得するにはそういうスクリプトを書かないといけないんだけど、自分にはそれ必要ないので、用意する動機がない。スクリプト無しでデータだけ配布してもいいんだろうけど、そしたら「開いたら数字しか書いてないんだけど、どうしたらいいの?」というメールとツイートと issues への書き込みが確実にやってくる(苦笑)。
というわけで、こちらもそのうちおいおい。
ちなみに、twitter には API 呼び出し回数のキャップ(15分180回)があるので、40万件を取得するには約23日かかる。まあこういうのを必要としている人はそれでも全然いいから欲しいと言うだろうけど。

第4回 #DSIRNLP で Active Learning 入門について話しました

@overlast さん主宰の データ構造と情報検索と言語処理勉強会(DSIRNLP) の第4回にのこのこ参加して、Active Learning 入門なるものを発表してきました。お疲れ様でした&ありがとうございました>各位


こちらが発表資料。



入門とか偉そうに歌ったけど勉強し始めてまだ1月半もないので、実は入門しているのは中谷本人である。
動機は資料にも書いたとおり、ドメイン適応をドメイン知識のある人が低コストで行うのに Active Learning の技術が使えるのでは、というあたり。
ここまで実験した範囲でそれなりの手応えはあるものの、非常に単純なテキスト分類問題で試しただけなので、もう少し難しくて現実的なタスクでもいろいろ試してみたいと思っている。


発表資料に間に合わなくて20数回の試行で Query-By-Committee の箱ひげ図を描いてしまっていたが、50回の試行も終わったのでそちらの図をここに載せておこう。大幅に違うものにならなくてよかった。



ああ、そう言えばうっかりチャートを縦に見る話ばかりしてしまったが、本当は横にも見ないといけなかったんだった。大失敗。
つまり「 active learning を適応して得た精度を得るために、random sampling だと何倍の訓練データが必要か」。
必要なグラフは資料に載っているので、興味のある向きは是非自分で確かめてほしい。active learning の有用性が実感できるだろう。


あと、こちらは発表時間その他から断念したのだが、oracle sampling(笑) や Expected Error Reduction の指標と相関性が高くて軽量な指標があったりしないかな、と探してみたりしていた。
それ用の図も蔵出しておこう。



これは横軸が MCMI[min] (Guo+ 2007)、青/赤/黄緑がそれぞれ対応するデータの least confident / margin sampling / entropy-based の指標を縦軸に取った散布図。こうしてみると、少なくとも線形な相関は全く無さそうだな〜……とわかってしまう。難しいのう。
資料作成の寝不足がまだ解消されてないのでこのくらいで。

夏のプログラミングシンポジウムで「数式を綺麗にプログラミングするコツ」を発表してきました

8/25 に開催された夏のプログラミングシンポジウム 2013 にて、「数式を綺麗にプログラミングするコツ」というお話をさせてもらいました。運営、発表に携わった&参加者のみなさん、会場のドリコムさん、お疲れ様でした&ありがとうございました。お水おいしかったです。

こちらが発表資料。

www.slideshare.net

この発表、実は一昨年に Tokyo.SciPy #2 でやらせてもらった「数式を numpy に落としこむコツ」のブラッシュアップ版である。

変更点は R のサンプルコードの追加と、表現をよりわかりやすくリライトしたという2点であり、紹介されているサンプルも含め本質的にはほぼ同じ内容である。手抜きっぽくてごめん。
本当は他の例を追加したかったのだけど、なにぶん観測範囲が狭くて、紹介にちょうどいい例(難しすぎず、易しすぎず)を機械学習ネタ以外で見つけることが出来ず。これ以上 PRML からネタを引っ張ってくると、「ふーん、これって機械学習限定なのね」みたいな空気になってしまうんじゃあないかと。
まあ発表時間的に3例は難しいというのも。


他の例も見てみたいという方には、こちらの記事で同じく PRML 13章の隠れマルコフモデルの推論に出てくる Baum-Welch の更新式を、「コツ」にしたがって誰でも実装できる形に書き直す手順を追っかけているのでよろしければ。


言いたいことは「紙と鉛筆が最強」なので、最悪それだけでも。
こういうプログラミングがすご〜く得意な人は、ロジスティック回帰の更新式だろうが Baum-Welch だろうが、式を10秒ぐらいじーっと見つめたら、ぴらっとコードを書けてしまったりするんだけど、そんなのは誰にでもできることではないので真似しちゃあダメ。




他の方の発表もいろいろ楽しませていただいたが、中でも JFEスチール 茂森氏の発表がおもしろかった。
でもこれってプロシンっぽくないんでは〜とちょっと心配したが、twitterドメイン知識の大切さがよくわかったといったような感想が続出しているところを見て、ああなるほど、プロシンでやる意味があったんだなあと実感。


実務でデータ解析をしようと思ったら、ドメイン知識バリバリの人でないとモデルもルールも書けない&超絶泥臭い努力の積み重ねの固まりというのは、テキストマイニングシンポジウムとかに行って、企業の方の発表とか聞くとめちゃめちゃ実感させられるので、今回のプロシンで蒙を啓かれてしまった! もっとそういう話を聞きたい! という人は行ってみると幸せになれるかも。
え? ちょうど再来週に第3回のテキストマイニングシンポジウムがあるんだ。へー(ステマ……じゃあないよ、別に)

Active Learning を試す(Uncertainly Sampling 編)

教師あり学習の教師データの作成はとても大変。例えば、twitter 言語判定のために、訓練・テストデータあわせて70万件のツイートに言語ラベルを振った人もいたりいなかったり。


Active Learning(能動学習) はそんな教師データ作成のコストを抑えながらモデルの性能向上を測るアプローチの1つ。
具体的には、正解なしデータの中から「こいつの正解がわかれば、モデルが改善する(はず)」というデータを選び、Oracle と呼ばれる「問い合わせれば正解を教えてくれる何か(ヒント:人間)」にそのデータを推薦、得られた正解付きデータを訓練データに追加して、以下繰り返し。


しかし「こいつの正解がわかれば、モデルが改善」を選び出す基準なんて素人考えでも何通りも思いつくわけで、実際 Active Learning のやり口は幾通りもある。
Active Learning Literature Survey (Settle 2009) ではその戦略を大きく6つに分類している。

  • 1. Uncertainly Sampling
  • 2. Query-By-Committee
  • 3. Expected Model Change
  • 4. Expected Error Reduction
  • 5. Variance Reduction
  • 6. Density-Weighted Methods


この記事では、まず 1. Uncertainly Sampling を試す。
これは「現時点のモデルで『最も不確かなデータ』を選ぶ」、柔らかく言うと「分類予測に最も自信がないものを選ぶ」ということ。
代表的な Uncertainly Sampling として、(Settle 2009) では 3 つの方法が紹介されている。ああ、ここでは教師あり学習一般ではなく、分類問題をターゲットに説明していく。

Least Confident
「最も確率の低いラベル」の確率が最も高いデータを選ぶ
Margin Sampling
(「1番目に確率の高いラベル」−「2番目に確率の高いラベル」)が最も小さいデータを選ぶ
Entropy-based Approach
予測分布のエントロピーが最も大きいデータを選ぶ


いずれも、どれか1つのラベルの確率が 1.0 であるデータは最も選ばれにくいし、全てのラベルの確率が等しいデータは最も選ばれやすい。まあ、いずれも至極妥当な印象がある。
線形分類器でこれらの戦略を取ると、選ばれるデータ点は分類平面周辺にもっぱら集中することになる。


Active Learning は理論的な評価も一部行われてはいるが、基本はヒューリスティックなアプローチということもあり、やっぱり実際に試さないことにはよくわからない。そこでまずは簡単な例としてロジスティック回帰に適用してみる。
ちなみに、2値分類ではこの3つの手法は同じデータを選択することになる(ことに気づかず、しばらく2値分類で頑張っていたのはナイショ)ので、多クラス分類をターゲットとする。


以下の様な設定で Uncertainly Sampling に分類される3手法 Least Confident, Margin Sampling, Entropy-based Approach と、baseline としてランダムにデータを選択する Random を比較した。
まずデータは再現実験しやすいよう NLTK の Reuters コーパスにて、crude, money-fx, trade, interest, ship, wheat, corn の 7 カテゴリを選び、この中の 2 つ以上のカテゴリに同時に属する文書を除外した 2256 文書を使った。
学習器は自分で実装してもいいが、何十回も学習することを考えると高速なものがいいので scikit-learn を使う。特徴量は単語(小文字化 & lemmatized)の出現数とした。 tf-idf の結果も一応見てみたが、精度に誤差程度の差しかなかったのでシンプルな方を採用した。


ここからそれぞれの戦略に従って選んだデータを訓練データに加えていくわけだが、最初に各カテゴリに1つ以上のデータがないと scikit-learn の分類器が学習してくれないので、初期データとしてランダムに1つずつ選ぶ。ちなみに、baseline(ランダム) 以外の3手法は初期値から後の選択は決定的である(評価値が同じ場合、numpy の arg 系はインデックスの一番若いものを取る)。
選択するデータは訓練データ数が 300 になるまでとし、残りのデータの正解率を毎回算出していった。


と、設定話はこれくらいにして、いよいよお楽しみの結果。
実装したスクリプトと、1回実行した時の正解率の推移グラフを挙げておく。


初期値依存なので細かい結果は毎回違うわけだが、このグラフから読み取れる通り、

  • Margin Sampling は精度が最も高く、かつほぼ安定している
  • Least Confident は最終的に Margin Sampling に近い精度に達するが、若干不安定
  • Entropy-based Approach はかなり不安定で、その精度は他の2つどころか Random を下回ることも珍しくない


という傾向は何回実行しても同じだった。
実行する前は Entropy-based に期待していたので、この結果は正直ショック。
Margin Sampling は実は ldig で訓練データを作る際にラベル付けするデータの選択(手動)にまさにこの指標を使っていたので、この指標が一番成績がいいのは少し嬉しくはある。


データやモデルにも強く依存するので、この傾向が常に成り立つとかそんなことは言えない。なんてわざわざ釘を刺す必要もないくらいだろうが、念のため。
次は Query-By-Committee を試す予定。


追記
ちょっとわかりにくいかなーと思ったので、訓練データ 100個までの推移を拡大した図を追加。

【/追記

参考文献

R で Vanishing Component Analysis

行けなかった ICML 読み会で紹介されてた Vanishing Component Analysis (Livni+ ICML2013)、うちの社内勉強会でも光成さんが紹介してくれて、ふむふむしていたところに、試作実装がでてきて、おーおもしろいと思ったんだけど、Matlab なので試せない orz


というわけで貧乏人の Matlab (失礼) である R で実装してみた。
極力、論文の pseudo code そのままで書くという変なところに情熱を注ぎ込んでいる。*1
ところどころにある i<-i や g<-g みたいなのは、遅延評価対策。

Sm <- matrix(1:8, ncol=2, byrow=T); # data

n <- ncol(Sm); # dimension of data space
m <- nrow(Sm); # data size
e <- 0.5; # tolerance

findrangenull <- function(F,C,Sm,e) {
    Ftilda <- lapply(C, function(fi){
        fism <- apply(Sm,1,fi);
        function(x)fi(x)-sum(sapply(F, function(g)sum(fism*apply(Sm,1,g))*g(x)))
    });
    A <- sapply(Ftilda, function(f)apply(Sm,1,f));
    x <- svd(A);
    U <- x$v;
    G <- apply(U, 2, function(u){u<-u; function(x)sum(u*sapply(Ftilda, function(f)f(x)))});
    F1 <- lapply(G[x$d>e], function(g){z<-sqrt(sum(apply(Sm,1,g)**2)); function(x)g(x)/z});
    list(F=F1, V=G[x$d<=e]);
};

F <- c(function(x)1/sqrt(m));
C1 <- lapply(1:n, function(i){i<-i; function(x)x[i];});

pre <- findrangenull(F, C1, Sm, e);
F1 <- pre$F;
V <- pre$V;
while(length(pre$F)>0) {
    F <- append(F, pre$F);
    C <- list();
    for (h in F1) {
        C <- append(C, lapply(pre$F, function(g){g<-g; function(x)g(x)*h(x)}));
    };
    pre <- findrangenull(F, C, Sm, e);
    V <- append(V, pre$V);
}


VCA では多項式環上での演算が入ってくるわけだが、そこは手抜きで function の入れ子で新しい function を定義する、とやったらイテレーションが進むごとに評価すべき function の数が文字通り指数的に増えてしまって重過ぎに……。
評価する点は最初に与えたデータ点に限られるので、メモ化すれば線形に落ちて大丈夫になるはずなのだが、連想配列がない R でメモ化のコードを簡単に書く方法がパッと出てこない。いやそりゃがんばりゃいくらでも手はあるけど、そこでがんばろうという気にあんまりならない。
というわけで、R で簡単にメモ化する方法を緩募中。

*1:良くあることだが、論文の pseudo code は2ヶ所バグっているので注意

PRML Wednesday (平日読書会) と読み始める人のための参考リンク集

毎週決まった平日の夜に 「機械学習パターン認識」(PRML) を読み進めようという PRML Wednesday のキックオフにのこのこ顔を出してきた。主催の naoya_t さん&参加者のみなさん、お疲れ様でした&ありがとうございました。


ほとんど初顔の方ばかりの中で好き放題しゃべってしまい。まあ例によって反省はしていないのだけれど(苦笑)。


会であれこれ言ったこと(めんどくさいので、ここでもう一度繰り返すことはしないw)はあくまで「素人から出発して PRML をひと通り読み終わった個人が、その経験から感じたこと」であり、絶対の正解なんかではない。
気に入らなかったら「なるほど、お前の中では(ry」で片付けて欲しい。勉強なんて続かなかったら意味が無いので、自分が続けられる方法やスタンスを模索して選びとっていかないとしょうがないのだから。


PRML Wednesday にはたぶん毎回は参加しないで、時々顔を出す感じで行こうかなと思っている。まあ 2 章や 3 章はアンチョコ(後述)があって、しかも読んだことのある人間が1人でもいれば、フォローを必要とする事態にはあんまりならないと思う。
次行くのは……ロジスティック回帰とか? って、ちょっと先すぎる? じゃあ、もうちょっと手前のどこかでw ロジスティック回帰も言っときたいことはだいたい gihyo.jp の連載(これも後述)の方で書いたんだけどね。


会の冒頭の自己紹介で、全員「あなたと PRML 」を語るようにと naoya_t さんからムチャぶりされ、一瞬「なにそれ1時間ほど語っていいの?」と誰もの頭をよぎったことだろう。
残念ながらきっとそんなわけないので、「こんなん書きました」と PRML の薄い本を見せてお茶を濁したわけだが。
PRML を読んできた履歴はまさに今見ているこのブログにだいたいまとまっている。初心者時代から振り返ってみるのもこれから PRML と戦おうとしている人の参考になるかも。


naoya_t さんの PRML 読書会(初回レーン)が始まったのが4年前。その第3回から参加した。

「確率分布ってどうにも苦手」「解析と線形代数すっかり忘れてる」と微笑ましいことを書いている。まあそうは言っても「腐っても数学科」というやつで、思い出して現役の頃のように行列計算とかできるようになるのにそれほど時間はかからなかった。
が、確率分布の苦手っぷりは謙遜抜きの本物で、特に事前分布は本気でわからなくて、ずいぶん苦しんだ。
今でこそドヤ顔であれこれ語っていてウザイが、最初はそんなもんである。


完全に理解したわけではないにせよ苦しみを脱出し始めたのは、読み始めてからもう1年近くたって、読書会がようやく 10章に差し掛かった頃。
ちょうどそのすぐ後、PRML 復習レーンが始まるというので、宣伝っぽく書いたエントリがこれ。

上の参加初回エントリの 10ヶ月後に「極論、PRML は全編にわたってこの加法定理、乗法定理を繰り返し繰り返し使っているだけである」みたいなことを言えている。
逆に言うと、この程度の理解にたどり着くまでに10ヶ月かかっている。それより短い期間で一定の理解に達したら n_shuyo より頭良さそう。


これもちょうど同じ頃の話だが、とある勉強会で PRML の勉強に使っているノートを広げてて、そんなノート作ってるのかとびっくりされたことがあって。いや逆にどうやってノート作らずに勉強しているんだろうと、びっくり。

この記事でも言っているが、ガウス分布の式はこれまでにもう100回くらいは書いている気がする。あとディリクレ分布も。
その後、社内で PRML 読書会を行うことになったのだが、その時は「担当は自分の作ったノートを見てしゃべれ! 本(PRML)を持つのは禁止!」と言い渡した。


ノート以外では、とにかくコードを書くのは大好きというか全く苦にならない人なので、PRML で実装しまくっている。

これは PRML を読んだ人の中でも比較的多いほうだろう。
こんだけ実装しまくっていると、「やっぱり本当の理解のためには実装しないとダメですかねえ」的な相談をちょいちょいされるので、「うん、実装してみたらいーんじゃあない?」と気楽気軽に答えていたが、最近心を改めた。


もともと機械学習や確率・統計をやったことがない人が PRML を読んで理解するのは大変だと思う。
ただ、「いくら勉強してもわかるようにならない」というのが 1 ヶ月や 3 ヶ月くらいの話だったら、「そりゃあそうだ」としか言ってあげない。それは才能うんぬん以前に時間が足りてない。
社会人、特に家族持ちだと、まとまった勉強時間を継続して取り続けるのは本当に困難だから、何がしかの方法でちょっとでも応援出来ればと思っている。


あんましまとまってないが、あとはこれから PRML を読む人の参考になりそうなリンク集。

PRML - 機械学習の「朱鷺の杜Wiki」 / 日本語版サポート情報
http://ibisforest.org/index.php?PRML#about


PRML を読むにあたっては、初めにここにある正誤表を必ず反映させる
PRML は比較的新しい版でもかなり多くの間違いが残っている。すでに指摘済みの間違いに気づかず悩むのは、時間の無駄。
正誤表はそこそこ高い頻度で更新されているので、章が変わるたびにその章の内容をチェック&反映させるくらいがいいと思う。


同じ「朱鷺の杜Wiki」の中にある PRML のコース分けは、内容を節ごとに難易度レベル付けをした情報。

読んだことのある人にアドバイスやフォローをもらえるのが一番だが、身近にそう言うことを頼める人がいない場合はこの情報も大変参考になるだろう。
PRML は分量のある本であり、全てを読破するのは相当なエネルギーと時間が必要となってしまうので、例えばまず易しいと言われているところから手を付ける、あるいは難しいと言われているところは軽く音読だけで済ませる(出てくる用語を耳にしたことがある状態にはしておく)、といった方針で進めるという手もあるだろう。

PRML アンチョコ / 「機械学習パターン認識の学習」(PRML同人誌) の PDF 版
https://github.com/herumi/prml/raw/master/main.pdf

サイボウズ・ラボの社内 PRML 読書会で行列計算に苦しむメンバーのために、同僚の光成さんがメモとして起こしたのがこのアンチョコ。
某黒幕の手にかかって、かの暗黒通信団から「機械学習とパターン認識の学習」という書籍として出版され、ジュンク堂やアマゾンで購入可能になっているが、もともとは PDF で無料公開(CC-BY ライセンス)されているものである。
フォローされている範囲は 2〜4 章と 9 章 10 章。社内勉強会で中心的に勉強した中で、計算の重要度が高い範囲となっている。後は 5 章の補足も追加されている(書籍の第2版で追加された範囲)。
ちなみに、PDF を生成するための元 tex ファイルもこちらで一緒に公開されている( main.tex )。需要は正直なさそうだが、一応紹介。

ある程度自信のある人ならどうしてもわからないところを辞書的に見る。自信のない人は一度目はこのアンチョコを首っ引きで見てノートに数式を書き下し、二度目は見ないで自力で解く。
というのがお勧めパターンだろうか。誰ですか、今「えー2回もやるの?」って言った人。

機械学習 はじめよう:連載|gihyo.jp … 技術評論社
http://gihyo.jp/dev/serial/01/machine-learning

手前味噌で申し訳ないが、gihyo.jp で書いている機械学習連載。
ここだけのハナシ、実はこの連載は PRML 1 章から 4 章の範囲に対応している。シンクロしているわけではないのでアンチョコ的には使えないだろうけど。

PRML 4章「線形識別モデル」

この連載は、自分が門外漢の状態で PRML を読んだときに「なんでこんなことするんだ(してもいいんだ)?」と感じたことを 1 つずつ 1 つずつつぶしていっている。
ので、PRML を読む前にこの連載を読んで、引っかかりそうなところを先につぶしておき、読みながらわからないところ(計算)があったらアンチョコでチェックする、という補完する感じで使ってもらえたら、と思う。


ちなみに予定通りに行けばあと 2 回か 3 回で連載終了。PRML 網羅してくれと言われても大変すぎるのでホント勘弁。連載の1回分書くのに1ヶ月とかザラにかかるんで……。

Kneser-Ney スムージングによる文書生成

少し前に Kneser-Ney スムージングの性能を測ってみた記事 を書いたが、今回は Kneser-Ney スムージングによる n-Gram 言語モデルで文書生成を行なってみた。
スクリプトはこちら。


適当なテキストファイル(複数可)を入力すると、1行1文書の訓練データとみなして Kneser-Ney スムージング付きの n-Gram 言語モデルを学習後、文書を生成してファイルに出力する。
オプションもいくつか指定できるが、-h でヘルプを出すか、ソースを読むかしてもらえば。


与えられた文書の確率を Kneser-Ney で計算するには、ディスカウントによって生じる正規化係数の補正を求めるために N1+ などのちょいややこしい値をあらかじめ計算して保持しておかないといけないが、文書生成の場合は単語(あるいは文字)空間全体をなめるときにその補正項も求まるから、頻度さえ保持しておけば良い。このため、実装がとても簡単になっている。
確率を計算するところも再帰的に書けばとてもコンパクトになるので、言語モデルの実装は40行程度に収まっている。
なお、modified KN ではなく、素の KN になっている。これは、前回の記事で modified KN は確かに性能向上するが劇的なものではなく、コストを考えると実用的には KN で十分だろうという感触によっている。


というわけで、気になるところは実際に生成される文書だろう。
まずは困ったときの青空文庫夏目漱石「吾輩は猫である」 からルビと制御用の表記を除外したテキストを作り、上のスクリプトに入力している。
文字 7-Gram 言語モデルを構築して、生成した文書例がこちら。

「なかなかでもいいからちょっとどけたまえと云って大騒ぎさ」
「大和魂! と叫んで日本人が肺病やみのような咳をした」
「こっちがいをやる事はない。辞職をする勇気もない。薬缶は洩るに相違ない。吾輩はほかに能もない者だと思った。こんな真似をして出来得る限り早く御免蒙るに若くはないと決心した。しかし黒の子分になって鼠以外の御蔭で女郎を見せたんだがね」と迷亭も少し気に返えした。
「だっていいじゃありませんか」
「そうでございましょうがねえ」「駄目だ駄目だ」「出てこねえ」「落ちねえはずはあるまい。気の毒だと云う念が胸一杯になって鳴いていると、本当に面白いのよ。最初にはね巡査の服を脱いで、肉を脱いで石鹸で磨き上が、その内のものは湯を使でものだなどと狂ってくる。こんなものを見せびらかしたんだって、考えると女は罪な者でありました」
「偏屈になるかも知れません。こいつの間にかみんな椅子へ腰をかけて、ふわふわする」
「落第の候補者寒月君はヴァイオリンを買ったかい」
「あんな声を出してわあわあ云う。それでまず脈をとるためのこの質問に対して相談をしているか一向要領を得んと云う顔付をする。加之こう知名のつもりでやった芝居かも分らんじゃないか」と主人は無雑作に迷亭に聞く。


あー、そうだよねえ、「吾輩は猫である」ってこんなんだよねえ。
意外だったのは、"「" と "」" の対応がそこそこ取れていること。たかだか文字 7-gram だからその対応が取れる見込みなど本来全くないのだが、日本語の並びにその対応の分布が隠されているということだろうか。
とはいえ、やっぱり完全ではないし、そもそも地の文と会話文では表現も当然違ってくるわけだから、言語モデルをそれぞれ作ってあげればより良い結果が得られそうな雰囲気はある。難しくはないから、興味があればチャレンジしてみるといいかも。


もういっちょ、今度はツイート。
メンションや URL などを削った約 17000ツイートから同じく 7-gram 言語モデルを構築&文書生成した例。

そうやってつけてYahoo!オークション出まくってましたね でも、なんでだろう・・・・ ・・・・・
ソムリエ的な何かを描くので誰でもいいからRTくれた人に手書きメッセージ書く おやすみ( ;´Д`)
俺もつけようwwwwwwwwwwwww 中距離殲滅フォーメーションて、これからも楽しく拝見しております
でも嫌な気持ちになるらしいからすごいせどりツールが配信された。 最近しんどいわ、
おっすごい!→10,000ベルを必ずGET!激レア以上がザクザク とびびらせる to FUKUSHIMA 、国は金を出す
新歓行きたいものですな
そういやギョウオオオオオオオオブォオオオオオオオオオオオオオオアアアーーーーーーー(((o(*゚▽゚*)o)))
楽しみよおおおおおおおおおおおよろしくでござる〜おやすいみ〜〜〜(。&#9696;&#8255;&#9696;。) わら
なにこれwwwこれからもよろしくねヾ(*´∀`*)ノ
よろしくでーす ( ´∀`∩);:
アイドルキックオーバーロー中wwwwwwwwwwwwwwwww┏(^o^)┓男のじゃ、凄く驚いた。
教えてくれない…。
久々にニコニコでペルソナ4のシーン集見てないから後悔して死にそうだった///////////


ふつー……あ、いや、普通でいいんだった。
普通で、かつ訓練データと異なる文書が生成されている。
ただ、幅広い表現のためにはもうちょっとデータが多いほうがよさそうだ。


このスクリプトは扱う文字種を特に制限していない。
というわけで最後に英語。Jane Austen の "Emma", "Northanger Abbey", "Persuation", "Pride and Prejudice", "Sense and Sensibility" を訓練データとし、文字 9-gram 言語モデルを構築している。
生成された文書は長いものが多いので、短いものからピックアップ。

"And this was the only yesterday, and Jane on her, in all probably ceased to Emma.
The day came, and to any fears, which one; she had spent and clever; but his remark on motives.
He had not think I might have been mortification of his neighbourhood, the regiment, which ought to be with him. There were; but that it is," whispering to get it! But I am always hoped our bestowed on me.
"He is a fool?"
"It will not be very heart again about her modesty, economy, took her hearing. She liked the two esteem. That was he perhaps be brought up to all his views on Sir John, who has never allow them to their evening."
"Are you must go to the subject would do her goodwill, and by them, had ever dared no explanation, a most unwilling to inquired after you."
"I must stay to everything for happiness; attending them, he was her mind was but a mixture of him for so many farther distressed him.


日本語より1文字あたりの情報量が少なく、不自然なところもやはりないわけではないが、それなりに英語っぽいと言えるのでは。
内容は置いといて(苦笑)。