Masaponto's Blog

お勉強メモ

TensorFlowと数式パーサで手書き電卓をつくった - Aizu Advent Calendar 2015

この記事はAizu Advent Calendar 2015 - Adventar 18日目の記事です。

前の人 @ababupdownba
Scala.jsを全力でオススメする - Qiita

次の人 @GUCC1_sp1ritu4l

タイトルどおり、手書き電卓を作ったのでそれについて書きます。

手書き電卓 dentaku

Webで見れます。これです

http://dentaku.herokuapp.com
masaponto/dentaku · GitHub

言語はPython 2.7.10です。
サーバにFlask0.10.1、学習にTensorFlow0.5.0を用いました。
CSSフレームワークはMaterial Design Liteを使っています。

使い方

左上のテキストボックスに数式を入力すると、パース(evalじゃないぞ)されて計算結果が下のテキストボックスに出力されます。
例えば、"30+12" と入力すると、"42"と出力されます。
うっかりコーヒーをキーボードにぶちまけて、キー入力ができなくなってしまっても安心してください。dentakuは手書き入力をサポートします!!
右側のキャンバスにマウスまたはタッチで "3" と上手に描くと、キャンバス上部に This is 3 ? と表示されると思います。
そのままYesボタンを押せば、テキストボックスに入力されます。
認識結果が間違えていた場合はNoボタンを押してください。無限に書き直すことができます。
今のところ、0 ~ 9の数字と、+、-、×、÷の演算子記号に対応しています。
Generatorと書かれたタブがありますが、それについては後述します。

TensorFlowによる手書き数字認識

手書き数字認識を行うにあたって、TensorFlow手書き数字データセットのMNISTを用いました。
学習方法はTensorFlowのエキスパート用チュートリアルと同じです。
こちらのページが参考になります。
http://qiita.com/haminiku/items/36982ae65a770565458d

また、Webアプリにするにあたってこちらのページを参考にさせていただきました。
http://d.hatena.ne.jp/sugyan/20151124/1448292129
http://qiita.com/ginrou@github/items/07b52a8520efcaebce37

演算子記号の認識

MNISTは0から9のアラビア数字の画像からなるデータセットです。
なので、MNISTのみを学習させただけでは、数字しか認識できません。
手書き電卓として機能させるには、+や-などの演算子の記号も認識させたいですよね。
という訳で、Tegaki Feature Vector Generator なるものを作りました。
dentakuのGeneratorタブをクリックしていただければ見れます。
左側のキャンバスに好きな記号を描いて、Generateボタンを押すと、右側のテキストエリアにキャンバスから生成された特徴ベクトルが表示されます。
MNISTと同様(?) 28×28次元で[0, 255]の範囲の特徴です。
生成した特徴ベクトルはDownloadボタンでcsv形式でダウンロードできます。
このデータとMNISTをくっつけて学習させれば、任意の記号を学習させられるという訳です。
今回は、+、-、× 、÷ の4つの記号をそれぞれ20個生成して学習をさせました。 データが少ないかと思っていましたが、それなりに学習、認識できてるかなと感じています。
どこかで手書き記号のデータセットが公開されていたら教えてください。

数式パーサ

数式パーサはHaskellサブセットであるFayを用いて作成したものを用いてます。
詳しくは下記のエントリを参照してください。

masaponto.hatenablog.com

おわりに

Webやディープラーニングについての知識が未熟なので、おかしい所を指摘していただければ幸いです。

多層パーセプトロンを実装してみた

こんにちは。
Aizu Advent Calendar 2014 19日目の記事です。

前の人 @takuti
会津若松〜東京間の高速バス(往復約8時間)に日帰りで4月から9月まで毎週乗った話 - きたくち

機械学習アルゴリズムである多層パーセプトロンを実装したので、それについて書きます。
間違えや、Pythonについて教えてください。
厳密性は犠牲になりました。

多層パーセプトロン

単純では

単純パーセプトロンの式はこれでした。

{ \displaystyle
\ f( g(\boldsymbol{x}) ) = f( \sum_{i=1}^{N} w_i x_i ) = f( \boldsymbol{w}^t\boldsymbol{x} )\
}

ここで簡単のため、特徴ベクトルの次元 N = 3 とします。

{ \displaystyle
\ f( g(\boldsymbol{x}) ) = f( \sum_{i=1}^{3} w_i x_i) = f( \boldsymbol{w}^t\boldsymbol{x})\
}

さらに、この式を図で表すと次のようになります。

f:id:masaponto:20150608232424p:plain

左の入力層の x1, x2, x3 が特徴ベクトルxの要素。
出力層のwが重みベクトルです。

見方としては、まず入力層にベクトルxの要素のx1, x2, x3 が入ります。
その後、出力層で特徴ベクトルxと重みベクトル w内積計算をし、
最後に活性関数が適用され結果が出るていう流れです。

多層パーセプトロン

一方、多層パーセプトロンの式はこれ (厳密ではない) です。(中間層1層、ユニット数2)

{ \displaystyle
\ y = f( \sum v_k   h(  \sum w_i x_i )  ) = f( \boldsymbol{v}^t  h( \boldsymbol{w}^t\boldsymbol{x}) )\
}

wは中間層の重み、v は出力層の重みです。
図はこれです。

f:id:masaponto:20150608232627p:plain

まさに、単純パーセプトロンの式を2回適用した感じですね。
図を見ると、多層と呼ばれる由縁がわかると思います。
図では2クラス分類の場合のため、出力ユニットがひとつです。 他クラス分類では、クラス数の数だけ出力ユニット用意します。

重み修正 ~誤差逆伝搬法 (バックプロパゲーション)~

多層パーセプトロンでの重み修正方法は誤差逆伝播法というものがよく使われます。
誤差逆伝播法はバックプロパゲーションとも呼ばれます。

詳しくは下記のリンクを参照してください。

多層パーセプトロンによる関数近似 - 人工知能に関する断創録

多層パーセプトロン-Stephen Machine Learning- - gerumaniumの日記

Pythonで多層パーセプトロンの実装例 - stMind

田舎な爺だ 多層パーセプトロンの仕組み

実装

上記のサイトを参考にPython3で実装してみました。
中間層は1層での合計3層の多層パーセプトロンです。
活性関数は中間層、出力層ともに、シグモイド関数
重み修正法はバックプロパゲーションです。

多クラス分類、公開されてるデータ・セットに対応させたコードはこちらを参照してください github.com

今回は、多層パーセプトロンを用いて、XOR問題をといてみました。 XOR問題は、線形分類不可能のため前回の単純パーセプトロンでは分類することができません。

gistb2eab035acc8198a1f4b

ちなみに、XORの真理値表はこれ。

f:id:masaponto:20141219003545p:plain

結果

中間層のユニット数2, 出力層1です。 また、学習率0.5でやってみました。

[1, 1]:[ 0.50196166]
[1, 0]:[ 0.9771374]
[0, 1]:[ 0.97709995]
[0, 0]:[ 0.01843416]

また、中間層ユニット数を5にしてみたら。精度が上がりました。

[1, 1]:[ 0.01694916]
[1, 0]:[ 0.98602664]
[0, 1]:[ 0.98414518]
[0, 0]:[ 0.0134073]

おわり

出力層のユニット数を増やすことで、多クラス分類できるそうなので、実際に何かを(識別)認識させてみたいと思います。

参考

多層パーセプトロンによる関数近似 - 人工知能に関する断創録

多層パーセプトロン-Stephen Machine Learning- - gerumaniumの日記

Pythonで多層パーセプトロンの実装例 - stMind

田舎な爺だ 多層パーセプトロンの仕組み

多層パーセプトロンの動きを可視化する - StatsFragments

単純パーセプトロンを実装してみた

こんにちは。
Aizu Advent Calendar 2014 6日目の記事です。 (意味不明だったので修正しました)

前の人 @Mopp_jp
もぷろぐ: 自作OSでのプロセス実装について (1) ~初めてのユーザプロセス~

次の人 @Mopp_jp
もぷろぐ: 自作OSでのプロセス実装について (2) ~初めてのユーザプロセス~

機械学習アルゴリズムである単純パーセプトロンを実装したので、それについて書きます。
もし間違いがあれば教えてください。

単純パーセプトロン 2クラス分類について

単純パーセプトロンは、2クラス分類のパターン認識に使うアルゴリズムです。

パターン認識についてはこちら。
パターン認識 - Wikipedia
Pattern Recognition

単純パーセプトロンを例で簡単に説明します。

キノコを毒キノコと、そうでないキノコの2クラスに分けたいとします。
たくさんあるキノコのうち、ある12個に関しては毒の有無がわかっているとします。
そこで、その12個のキノコの長さと太さを測ってグラフにプロットします。 次のようになりました。

f:id:masaponto:20141206004522p:plain

毒々しい青色の点が毒が入ってるキノコ、赤い点がそうでないキノコです。
ここで、毒と無毒の2クラスに分けるんだから、こんな感じに直線を引きたくなりますよね。

f:id:masaponto:20141206004721p:plain

この直線を使えば、残りのキノコも分類できそうです。

単純パーセプトロンは、この直線を機械的に求める方法です。

機械学習の分野では、キノコの長さと太さをベクトルで表したものを特徴ベクトル、特に毒の有無が既知なキノコのベクトルを学習データ、 緑の直線を識別境界と言います。

アルゴリズム

以下のようなアルゴリズムで識別境界を決めます

1. 識別境界をきめる関数(識別関数)をランダムに決める。  
この関数は特徴ベクトルを入力すると、それが所属するクラスを出力します。
2. この関数に学習データを1つ入力して出力を見る。  
3. 実際のクラスと異なるクラスが出力された場合、関数を修正する。  
正しかった場合はそのまま。  
3. すべての学習データにおいて (2)(3) をやる。  

まずは識別関数について説明します。

識別関数

単純パーセプトロンでは先ほどの例の識別境界を次の識別関数で求めます。

{ \displaystyle
\ f( g(\boldsymbol{x}) ) = f( \sum_{i=0}^{N} w_i x_i + v ) = f( \boldsymbol{w}^t\boldsymbol{x} + v )\
}

この関数は入力に特徴ベクトルを取り、その特徴ベクトルを持つキノコがどのクラスに属するかを返します。

xは入力の学習データです。

wは重みベクトルです。
ランダムに決められる要素をもつベクトルで、次元数は特徴ベクトルと同じです。
識別関数はこのベクトルに依存します。

vはバイアス項です。 ランダムなスカラー値です。

fは活性関数とよばれるものです。ここでは単位ステップ関数です。*1
単位ステップ関数は入力が負だったら0を、負で無かったら1を返します。
これにより、出力は0か1になり、2クラスに分けられる訳です。
今回の場合、g(x) < 0 なら0、g(x) ≧ 0なら1が出力になります。

この式を少し変形してみましょう。
以下の例のように、特徴ベクトルに要素1を追加します。

(長さ, 太さ) -> (1, 長さ, 太さ)  

そして、ベクトルwもスカラーvも乱数なので、vをベクトルwの第一要素として追加します。

(w0, w1) -> (v, w0, w1)  

すると以下のように、変形できます。

{ \displaystyle
\ f( g(\boldsymbol{x}) ) = f( \sum_{i=0}^{N} w_i x_i ) = f( \boldsymbol{w}^t\boldsymbol{x})\
}

内積のみの形で表せたので、実装が楽になりした。

関数の修正

識別関数は重みベクトルに依存しているので、実際は重みベクトルを修正していくことになります。
重みベクトルの修正には以下の式を使います。

{ \displaystyle
\ \boldsymbol{w'} = \boldsymbol{w} + \rho ( d - f( g(\boldsymbol{x}) ) ) \boldsymbol{x}
}

wは修正前の重みベクトル、w'は修正後の重みベクトルです。

xは入力の学習データです。

f(g(x))は先ほどの識別関数です。

ρは学習率と呼ばれるスカラー値で、主に0.5に設定されます。

dは学習データxの属すべきクラスです。

この式でdとf(g(x))の値が同じだったら、つまり識別関数が正しく識別できていたら、二項目の値が0になり、 重みの更新が行われないことがわかります。 上のように重みを更新していくことを"学習する"と言います。

識別関数とその修正方法を踏まえるとアルゴリズムは次のようになります。

(1). 識別関数の重みベクトルをランダムに定義する。
(2). 学習データから一つ選び、識別関数に適用する。
(3). 識別関数の返り値を重み修正法で更新する。
(4). すべての教師データにおいて、正しい値を返すまで(2),(3)を繰り返す。

実装

今回はHaskellとPython3で実装してみました。
環境はArch Linux(x64)、ghc7.10.1、python3.4 です。

コードはこちら。

Haskell
SimplePerceptron/SP.hs at master · masaponto/SimplePerceptron · GitHub

Python3

simple_perceptron.py · GitHub

実装の確認してみます。主に論理演算のAND、ORがよく使われるみたいです。

AND演算の真偽値表はこれです。

f:id:masaponto:20141206023932p:plain

ORはこれです。

f:id:masaponto:20141206024257p:plain

つまり、x1,x2を特徴ベクトルとして、演算結果を属してほしいクラスに対応させるわけです。

python3での結果以下の通り、
学習率はいずれも0.5です。

AND
f:id:masaponto:20150813152218p:plain

OR
f:id:masaponto:20150813152227p:plain

やったー。識別できてます。
いずれのグラフも、黒点がクラス0, 赤点がクラス1です。
最終的な識別関数は2変数関数(z = ax + by + c)なので、z = 0を入れてyについて解いた式を使うと二次元にプロットできます。
Haskellの場合も同じような結果になるぞ!

おわり

本当は多層パーセプトロンの話もしたかったんですが、書いたコードが怪しいことと、解説するほど勉強できてないので、今回はしません。

つぎは
@Mopp_jp
もぷろぐ: 自作OSでのプロセス実装について (2) ~初めてのユーザプロセス~

参考資料

第3回 単純パーセプトロン · levelfour/machine-learning-2014 Wiki · GitHub

テキストマイニングのための機械学習超入門 二夜目 パーセプトロン - あんちべ!

はじめてのパターン認識

はじめてのパターン認識

フリーソフトでつくる音声認識システム?パターン認識・機械学習の初歩から対話システムまで?

フリーソフトでつくる音声認識システム?パターン認識・機械学習の初歩から対話システムまで?

*1:他にも区分線形関数、シグモイド関数が用いられます。