« 2009年03月 | メイン | 2009年05月 »

2009年04月 アーカイブ

2009年04月13日

英単語タイピングゲーム iVoca を機能アップデートしました

英単語タイピングゲーム iVoca にて、本日以下の機能をリリースしました。

  • ブックに問題一覧(単語一覧)ページを追加しました。問題内容や、各単語ごとの「習得度」や「苦手度」を確認できます。
  • 問題および解答の長さを 30文字から 60文字に拡張しました。
  • '¡'(逆さの!) や '¿'(逆さの?) を解答に使える文字に追加しました。入力はそれぞれ '!' と '?' です。スペイン語の感嘆文・疑問文に対応します。


ブック画面の右上に「問題一覧」リンクが追加されています。


問題一覧では、問題と解答の他に「出題数」「習得度」「苦手度」が各単語ごとに表示されます。
例えば「難読駅名(JR東日本 東京近郊編)」の問題一覧は以下のようになります(ユーザ登録して遊んでいない場合は出題数・習得度・苦手度は空になります)。


「習得度」には現在の習得レベルが 100点満点で表示されます。ブックのスコアは、この習得度の合計とほぼ一致するようになっています。
「苦手度」は、「その単語にどれだけ手こずったか」が星0個~5個で表示されます(多いほど苦手)。

問題一覧ではヘッダをクリックするとソートしますので、「苦手度」順に表示させて苦労した単語が確認するなど、今までに学んだことのあるブックについて、いろいろな楽しみ方や役立て方をしてもらえるようになりました。

ずいぶん前からご要望いただいていた「問題と解答の長さを長くして欲しい」にも、ようやく対応しました。

長~い問題や解答については、ご覧のようなイメージで2行で表示されます。

スローペースではありますが、iVoca では引き続きより楽しんで単語を覚えられる機能を追加していきたいと思いますので、よろしくお願いします。

2009年04月23日

Perceptron を勉強する前にオンライン機械学習ライブラリを試してみる

今度は CLUTO を試してみた話を書こうと思っていたのですけど、あまりふくらみそうにないので、保留。

オンライン学習(逐次学習)に興味があるので、まずは Perceptron 周辺を勉強し始めてます。
が、その前に動くものをさわっておこうということで、岡野原さんのオンライン機械学習ライブラリをちょっぴり試してみました。

oll プロジェクトページ(日本語)

ビルド

Linux なら ./configure & make でOK。

Windows の場合 oll.hpp の先頭のどこかに

#include <algorithm>

を追加すれば VC++ でもコンパイルできました。

サンプルデータ

サンプルデータには、プロジェクトページにも実験としてあがっている news20.binary をまずは使ってみることにしましょう。
「シャッフルし、15000例の訓練データと4996例のテストデータに分けた」とあるので、その通りにします。

$ sort -R news20.binary > news20.random
$ head -15000 news20.random > news20.train
$ tail -4996 news20.random > news20.test

実際に中谷が興味があるのは自然言語処理なので、NLP らしいデータはまた別途用意して試してみたいと思います。
まずはパーセプトロン。

学習&テスト

ビルドが通ってデータの用意もできました。
実行。

$ ./oll_train P news20.train news20.model.p -C=2.0 -b=1.0 -I=10

15000 例の学習データを用いて、Perceptron(P) で10回繰り返して学習(-I=10)、結果を news20.model.p に保存します。
学習手法は先頭の "P" を "AP" などに変更することで変えられます。そのあたり、コマンドラインの仕様はプロジェクトページで見てください。

「同じデータを10回繰り返して学習する」というあたりが、収束(の可能性)がある線形識別器ならでは、でしょうか。
Naive Bayes とかだと同じデータで10回学習なんて考えられないですから。

$ ./oll_test news20.test news20.model.p

Accuracy 94.235% (4708/4996)
(Answer, Predict): (p,p):2425 (p,n):35 (n,p):253 (n,n):2283

学習結果を用いて、テストデータを分類させてみて、その正解率を出しています。
(p|n,p|n) は positive|negative を positive|negative に分類した件数で、正解率は 94%。

プロジェクトページの例と大きく違っていないので、一応動いていることが確認できました。
これで自分で勉強&実装したものとのベンチマークがとれますね。


次は、Perceptron を理解するために、「手で Perceptron を計算」してみます。

2009年04月24日

Perceptron を手で計算して理解してみる

Perceptron の実装とか見ると、ものすごく簡単なので、本当にこれで学習できちゃうの? と不安になってしまいました(苦笑)。
こういうときは、実際にパーセプトロンが計算しているとおりに、紙と鉛筆で計算してみて、期待する結果が出てくることを確認してみたくなります。

参照する教科書は「パターン認識と機械学習・上」(PRML) の「 4.1.7 パーセプトロンアルゴリズム」。
短い節です。必要最低限のことを一通り書いてある感じかな。

計算に用いるサンプルですが、手で計算できる規模でないといけないので、論理演算の AND を試してみることにします。

簡単に勉強

ちゃんとした説明は PRML などを見て欲しいですが、とても簡単にまとめます。

2値の線形識別モデルは、N 次元空間内を (N-1) 次元の超平面(決定面)で分割することで、入力ベクトル x から得られる特徴ベクトル φ(x) が2つのクラスのどちらに属するかを分類します(2次元で言えば、座標平面を直線で分割している感じ)。

その超平面は重みベクトル w を使って、 w^T φ(x) = 0 と表します。
空間を w^T φ(x) ≧ 0 と w^T φ(x) < 0 に分割するわけですね。
重みベクトル w を決定する一般的なアプローチは、誤差関数を決め、それを w の関数と見なし、最小化する w を求める、となります。

パーセプトロンは、誤差関数としてパーセプトロン基準を用い、また最小化に確率的最急降下アルゴリズムを用いるのが特徴、と。

パーセプトロン基準は、目的変数として t ∈ {-1, +1} を用いつつ(通常は {0, 1})、誤差関数を次のように定義しています。

E_P(w) = - Σ w^T Φ(x_n) t_n

ここで Σは誤分類されたパターン全体を走るとします。
すると、w^T Φ(x_n) ≧ 0 のとき t_n は「間違って」 -1 になっているし、w^T Φ(x_n) < 0 のときは t_n は「間違って」 +1 になっているため、E_P(w) は常に0以上であることがわかります。

これを最小化するために、「誤分類された x_n 1つずつに対して」次の式を使って逐次的に最小値に近づけていきます。
イメージとしては、ニュートン法(多次元版)って感じです。
このあたり、PRML には 3.1.3 節を見ろ、と書いてますが、5.2.4 節もあわせて見るといいかもしれません。

w^(γ+1) = w^(γ) - η▽E_P(w) = w^(γ) + φ(x_n) t_n

ややこしそうに書いてますが、アバウトにぶっちゃけると「ハズレのときは、そのハズレが出てしまった特徴ベクトルを重みベクトルに足すか引くかする」ということです。

こんな方法では最小値に本当に近づくのかどうか必ずしもわからないんですが、「解が存在する場合は」この計算を有限回繰り返すことで解が得られます、というのが「パーセプトロンの収束定理」で、Perceptron のキモです。

ね、ほんとにそんなんでうまく学習できるんですか? という気になるでしょう。

手で計算してみる

論理演算 AND とは……は、さすがに略。

AND の入力ベクトルと目的変数は次のようになります(ベクトルは転置した状態で記述します)。

x_0 = (0, 0), t_0 = -1
x_1 = (1, 0), t_1 = -1
x_2 = (0, 1), t_2 = -1
x_3 = (1, 1), t_3 = +1

これを学習できるのか、手で計算してみましょう。

特徴関数 φ は x_n そのままでもいいんですが、「φ_0 はバイアスでしょ、jk」と PRML にもあるので、次のように定義します。

φ( x_0 ) = (1, 0, 0)
φ( x_1 ) = (1, 1, 0)
φ( x_2 ) = (1, 0, 1)
φ( x_3 ) = (1, 1, 1)

これより重みベクトル w も3次元、初期値は w^(0) = (0, 0, 0) となります。

さて、ここから計算していきますが、誤分類ベクトルは本来ランダムに選ぶべきところ、ここでは少々恣意的に選んでいくことにします(理由は後述)。


■Step 1

x_0 は w^T Φ(x_0) = 0 ≧ 0 となり t_0 = -1 と合致しません。
よって、「次の w 」は次の式で求まります。

w^(1) = w^(0) + φ(x_0) t_0 = (0, 0, 0) - (1, 0, 0) = (-1, 0, 0)

ますます不安……この調子で大丈夫?

■Step 2

x_3 は w^T Φ(x_3) = -1 + 0 + 0 < 0 となり t_3 = +1 と合致しません。よって
w^(2) = w^(1) + φ(x_3) t_3 = (-1, 0, 0) + (1, 1, 1) = (0, 1, 1)

■Step 3

x_1 は w^T Φ(x_1) = 0 + 1 + 0 ≧ 0 となり t_1 = -1 と合致しません。よって
w^(3) = w^(2) + φ(x_1) t_1 = (0, 1, 1) - (1, 1, 0) = (-1, 0, 1)

■Step 4

x_2 は w^T Φ(x_2) = -1 + 0 + 1 ≧ 0 となり t_2 = -1 と合致しません。よって
w^(4) = w^(3) + φ(x_2) t_2 = (-1, 0, 1) - (1, 0, 1) = (-2, 0, 0)

■Step 5

x_3 は w^T Φ(x_3) = -2 + 0 + 0 < 0 となり t_3 = +1 と合致しません。よって
w^(5) = w^(4) + φ(x_3) t_3 = (-2, 0, 0) + (1, 1, 1) = (-1, 1, 1)

■Step 6

x_1 は w^T Φ(x_1) = -1 + 1 + 0 ≧ 0 となり t_1 = -1 と合致しません。よって
w^(6) = w^(5) + φ(x_1) t_1 = (-1, 1, 1) - (1, 1, 0) = (-2, 0, 1)

■Step 7

x_3 は w^T Φ(x_3) = -2 + 0 + 1 < 0 となり t_3 = +1 と合致しません。よって
w^(7) = w^(6) + φ(x_3) t_3 = (-2, 0, 1) + (1, 1, 1) = (-1, 1, 2)

■Step 8

x_2 は w^T Φ(x_2) = -1 + 0 + 2 ≧ 0 となり t_2 = -1 と合致しません。よって
w^(8) = w^(7) + φ(x_2) t_2 = (-1, 1, 2) - (1, 0, 1) = (-2, 1, 1)


これで全ての w^T Φ(x_n) が目的変数と合致するようになり、w = (-2, 1, 1) が得られます。
いやあ本当に学習できるんですね。
めでたしめでたし。

もしよければ、各ステップごとに決定面がどのように遷移するか描いてみると面白いかもしれませんね。
w = (a, b, c)、φ = (1, x, y) とすると、w^T φ = a + bx + cy = 0 なので、座標平面の ( - a/b , - a/c ) を通る直線として表せるので。


こんな簡単な例でも、いろいろ問題点が見えてきます。


(1) 収束に時間がかかる。

明確に解があることがわかっている、これだけ低次元の例でも収束に8回かかっています。
オンライン学習ライブラリで「学習を10回繰り返す」必要があるのも大納得。


(2) 学習データの偏りに強い影響を受ける。

上の計算で(恣意的に)選ばれた誤分類ベクトルを見ると、x_3 が多いことがわかります。これは、正例が x_3 しかないためです。
これを本当にランダムに選んでいたら(そして実際の計算ではおおむねそうせざるを得ない)、収束にはもっと回数を要したでしょう。
あるいは学習データに偏りがなくても、ランダムに選んだ順番がたまたま「ずっと正例でトレーニングした後、今度はずっと負例」なんてことになったら、間違った結果が確実に出てしまうでしょう。


このあたりの問題点を少しでも解消しようとするのが Averaged Perceptron その他の方式、ということでしょうか。
なんにせよ、オンライン学習で「ランダム!」「繰り返し!」と叫ばれる理由がよくわかって、一安心です。


次回は実装。

About 2009年04月

2009年04月にブログ「nakatani @ cybozu labs」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2009年03月です。

次のアーカイブは2009年05月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。