Hatena::ブログ(Diary)

明日とロボット このページをアンテナに追加 RSSフィード

2011-07-06

kinectと最近傍法で物体認識

復習

設計概要

  • GUIのレイアウトは前回とほぼ変わらず以下のよう。

f:id:astrobot:20110706120722j:image

  • 変更点は、ボタン1を学習モードに割り当て、ボタン2をテストモードに割り当てた所くらい。
  • メインウィンドウのフィールドに、選択範囲内での深度マップを格納する1次元配列を用意。
    • (imageFrame.Image.Bitsを取得したdepthData配列も、表示する色を格納したcolorFrameも余計なデータが入ってるので新しく作った)
  • rec、testのそれぞれのボタンを押す際に、イメージのストリームをとめてわかりやすくする。

NN法の設計(id:nowokayさんの基本のNearestNeighbors法(NN法)でパターン認識を参考)

  • まずは、データを格納するクラスを作った。
public class Data
{
    public int index;//privateにしてゲッターセッターを追加する方が良い
    public double[] data;
    public Data(int _index, double[] _data)
    {
        data = _data;
        index = _index;
    }
}
    • doubleの配列のデータ、そして、その名前のインデックス(メインウィンドウで、命名した順に割り振られる)
  • NN法の考え方はシンプルで、テスト用に送られてきたデータをメモリ内のデータと比較し、一番近似した物を選ぶ。
public class NearestNeighbor{
    ArrayList dataArray = new ArrayList();
    MainWindow mw;

    public NearestNeighbor(MainWindow _mw)
    {
        mw = _mw;//main windowのアドレス取得
    }

    public void learn(int _index, double[] _data) {
        dataArray.Add(new Data(_index,_data));
    }

    public int trial(double[] _data) {
        int index = 0;

        double min = Double.MaxValue;//とりあえず可能な最大値

        for(int i = 0; i<dataArray.Count; i++){
            double[] tmp = ((Data)dataArray[i]).data;
            
            double sumDiff = 0;
            for(int j=0; j<tmp.Length; j++)
            {
                sumDiff += Math.Pow(tmp[j] - _data[j],2);//それぞれの点における二条誤差を足しあわせる
            }
            if(min > sumDiff)//最小の誤差のインデックスを記憶
            {
                min = sumDiff;
                index = ((Data)dataArray[i]).index;
            }

        }
        return index;
    }
}

名前登録パネル

  • 名前の登録のために以下のパネルを追加

f:id:astrobot:20110706120723j:image

  • コンボボックスに、すでに登録された名前リスト、あるいは新規名前登録の選択肢を与える。
    • 新規登録が選択されたときのみ、下部のテキストボックスをenableにする。
    • また、学習の手間を省くため、コンボボックスの初期メニュー選択は、送られてきた深度マップが最も近い名前の物を表示するようにする。

text-to-speech

  • あとは、人に見せるとき、学習した名前をテキストボックスに表示するのでは地味なので、覚えた名前をコンピュータに読ませるようにした。
  • referenceにSystem.speechを追加し、コードに以下を追加
using System.Speech.Synthesis
  • 初期化
SpeechSynthesizer synth = new SpeechSynthesizer();
synth.Volume = 100;  // ボリューム設定:0〜100
synth.Rate = -5;     // 読むスピード設定:-10〜10
  • 読ませる
synth.Speak("what do you want me to say?");

デモ

  • ムヒをkinectの前に置いて、認識範囲指定、そして、「Rec」をクリック

f:id:astrobot:20110706120729j:image

  • で、次回から「test」で認識するようになる。
    • text-to-speechで読むと同時に、画面左下のtext boxにも認識した名前を表示。

f:id:astrobot:20110706120728j:image

  • 次にかぼちゃを認識

f:id:astrobot:20110706120731j:image

  • 鍋を認識。
    • 初めはかぼちゃと勘違いしていたが、すぐに学習

f:id:astrobot:20110706120730j:image

  • やかん

f:id:astrobot:20110706120726j:image

  • やかん2
    • NN法は、メモリの中から一番近い物を選ぶため、違う物を同じやかんと命名することも可

f:id:astrobot:20110706120727j:image

  • コーヒーカップ

f:id:astrobot:20110706120725j:image

  • 鳥の置物

f:id:astrobot:20110706120724j:image

  • お茶葉箱

f:id:astrobot:20110706120732j:image

改善点

  • 認識したい物体の大きさに応じて、データを拡大縮小できるように
  • カメラからの距離で物体を学習するのではなく、物体の厚みなどを計算し、設置距離が大きく変わっても認識できるように
  • RANSACアルゴリズムなどを用いて、設置平面を求め消去する*1

心理学との関連

  • 発達心理学の分野の実験で分かっていることは、2歳以下の子供は、物の名前学習をするに当たって、shape biasを主に使っている。(Jones and Smith, 2002)*2
    • 似た形の物は同じ名前を持っているという思いこみを、色よりも大きさよりも何よりも最も利用する。
  • それ以上の子供は、更に、function biasという、同じ機能を持った物体に名前を汎化させる傾向を持つ。(Nelson, Russsel, Duke, and Jones, 2000)*3
    • それは少し難しいのでもう少し勉強したら挑戦。
    • Grabner, Gall, Goolの"What Makes a Chair a Chair?" (2011)とか面白そうなことしてる。

参考書


Kinect関連記事

*1:参考:RANSACを調べてみました

*2:Jones, S. S., & Smith, L. B. (2002). How children know the relevant properties for generalizing object names. Developmental Science 5(2), 219-232.

*3:Nelson, D. G. K., Russell, R., Duke, N., & Jones, K. (2000). Two-Year-Olds Will Name Artifacts by Their Functions. Child Development, 71(5), 1271-1288.

2011-06-28

Rでニューラルネットワークを用いた手書き数字認識

アイディア

  • 元々のアイディアはCodeZineの記事ニューラルネットワークを用いたパターン認識で公開されていたJavaアプレット
  • 学習精度を上げるために、自分で書いた文字を元に学習させる。
    • それぞれの数を10回ずつ手書き入力させ、データを用意し、それを元にニューラルネットワークを学習させる
  • Rで用意されているneuralパッケージを使用する

手書き入力データの用意

  • CodeZineさんの記事のように、手書き入力した数字を7×11のグリッドで表現

f:id:astrobot:20110629002141p:image

  • 作成したJavaアプレットを用い、RBFモデルのネットワークで学習するための、入力層のユニットは7×11の計77、出力層のユニットに0から9の数を設定したデータを用意する。
  • 0〜9間での数字をそれぞれ10回ずつ書いて、以下のような学習用のcsvファイル(data.csv)を作成
cell0cell1cell2...cell76ans0ans1ans2...ans9
0.10.10.1...0.10.90.10.1...0.1
..............................
0.10.10.1...0.10.10.90.1...0.1
..............................
    • 塗りつぶされたcellは0.9、空白だったセルは0.1で一次元配列にして保存
    • それぞれの値に対応するansの値が0.9、それ以外が0.1となるように
  • 同様に、0〜9までのテスト用のcsvファイル(test.csv)も作成

cell0cell1cell2...cell76
0.10.10.1...0.1
0.10.10.1...0.9
...............

Rを使ったRBFモデルのニューラルネットワーク

  • 今回のように高次の交互作用が存在する課題は、ユニット内の変換にラジアル基底関数(RBF)など釣り鐘式の関数を利用すると学習が容易に成立する事が知られている。
  • package neuralをRにインストール
options(CRAN="http://cran.r-project.org")
install.packages("neural")
    • 通常ならば以上のようにパッケージをインストールできるが、neuralは古くなっためか退避されていていたため、zipを直接ダウンロードしてインストールする。
データを読み込む
  • neural libraryを読み込んで、dataに学習用csvを読み込み
  • inputに7×11の配列データ、outputにそれぞれに対応する数を読み込む
library(neural)
data<-read.csv("data.csv",header=T)
input<-as.matrix(data[0:77])
output<-as.matrix(data[78:87])
neuralのGUIを使ってネットワークの分析
  • 初期値のシードを固定し、中間層を2層(5個と3個)で学習の最大繰り替えし数を300回に指定
set.seed(1)
learn0<-mlptrain(input, neurons=c(5,3), output$ans1, it=300)
learn1<-mlptrain(input, neurons=c(5,3), output$ans2, it=300)
...
learn9<-mlptrain(input, neurons=c(5,3), output$ans9, it=300)
  • すると以下のようなGUIが表示されるため、startをクリック

f:id:astrobot:20110629010708p:image

  • 自分のノートパソコンでだいたい、それぞれ10分程度。
    • 終了すると、出力ユニットボックスが赤くなるので、そうしたらexit

学習データのセーブ

  • 以下のようにセーブできる
save(learn0, file="learn0.dat") 
save(learn1, file="learn1.dat") 
...
save(learn9, file="learn9.dat") 

精度テスト

  • テスト用csvを読み込んで、学習の完了したネットワークで値予測を行う
    • それぞれの予測値を取り込み、ひとつに結合し、表示、csv出力
test<-read.csv("test.csv",header=T)
testdata<-as.matrix(test[0:77])

applied0<-mlp(testdata, learn0$weight, learn0$dist,learn0$neurons, learn0$actfns)
applied1<-mlp(testdata, learn1$weight, learn1$dist,learn1$neurons, learn1$actfns)
...
applied9<-mlp(testdata, learn9$weight, learn9$dist,learn9$neurons, learn9$actfns)

result<-cbind(applied0,applied1,applied2,applied3,applied4,applied5,applied6,applied7,applied8,applied9)
result

write.csv(result,"result.csv")
  • 結果は以下のようになった。
prediction\actual #0123456789
00.8380.0880.0870.0900.0940.0910.0910.0990.0850.302
10.0900.5940.1230.1400.0930.1030.0870.0960.0850.091
20.1420.1270.9070.0920.1090.0990.1080.0930.1090.764
30.0880.0960.1460.7680.1060.0960.0940.1080.0930.129
40.1040.1100.0910.0890.8820.1000.1460.0950.1140.106
50.0920.0990.1050.0880.1010.9370.1000.0910.1070.090
60.1080.1020.1020.2370.1220.3490.8600.0920.0900.090
70.1070.1840.1860.0940.1590.2520.0970.7600.1020.236
80.1000.0930.0890.0890.5940.0870.0880.0960.1060.093
90.0880.2970.0910.1300.1770.1020.0890.1130.0940.852
  • 8を除いた全ての値でそれなりの精度の予測ができた。
  • 改善点はこれらの作業をJAVAから全自動化

参考