ニューラルネットワークで手書き文字認識

前回の記事でニューラルネットワークを使って為替レートを予測してみましたが、 せっかくなので、それを少し改良してTensorflowのチュートリアルと同じく手書き文字認識を試してみました。

import cv2
import numpy as np
from itertools import *

digit_w=28
digit_h=28
dim_in=digit_w*digit_h
dim_out=10
hidden_count=128
learn_rate=0.005
limit=50000

#weight and bias
w1 = np.random.rand(hidden_count, dim_in)-0.5
w2 = np.random.rand(dim_out, hidden_count)-0.5
b1 = np.random.rand(hidden_count)-0.5
b2 = np.random.rand(dim_out)-0.5

# activation function
def activation(x):
    return np.maximum(0, x)
    #return 1.0/(1.0+np.exp(-x))
    
# differential
def activation_dash(x):
    return (np.sign(x) + 1) / 2
    #return (1-activation(x))@activation(x)

#forward calculation
def forward(x):
    #return w2 @ activation(w1 @ x + b1) + b2
    return np.dot(w2,activation(np.dot(w1,x)+b1))+b2
    

# backpropagation
def backward(x, diff):
    global w1, w2, b1, b2
    #v1 = (diff @ w2) * activation_dash(w1 @ x + b1)
    #v2 = activation(w1 @ x + b1)

    v1=np.dot(diff,w2)*activation_dash(np.dot(w1,x)+b1)
    v2=activation(np.dot(w1,x)+b1)

    w1 -= learn_rate * np.outer(v1, x)
    b1 -= learn_rate * v1
    w2 -= learn_rate * np.outer(diff, v2)
    b2 -= learn_rate * diff

#column and row number of mnist images
image_shape=np.zeros((10,2),dtype=int)
#number of all mnist data
data_num=np.zeros((10,1),dtype=int)

#count number of mnist data
images=[]
for idx in range(0,10):
    images.append(cv2.imread("mnist_img/mnist_train"+str(idx)+".jpg",0)) #load images
    image=images[idx]
    image_shape[idx,0]=len(image[:,1])/digit_h
    image_shape[idx,1]=len(image[1,:])/digit_w-1
    data_num[idx]=image_shape[idx,0]*image_shape[idx,1]

train_count=int(sum(data_num))
train_x=np.zeros((train_count,dim_in))
train_y=np.zeros((train_count,dim_out),dtype=int)

#load input data
cnt=0
temp_num=0
for idx in range(0,10):
    print ("load image:"+str(idx))
    train_y[temp_num:temp_num+int(data_num[idx]),idx]=1 #set output data
    temp_num+=int(data_num[idx])
    image=images[idx]
    for i in range(0,image_shape[idx,0]):
        for j in range(0,image_shape [idx,1]):
            temp=image[digit_h*i:digit_h*(i+1),digit_w*j:digit_w*(j+1)]/255.0 #cut images
            train_x[cnt]=[flatten for inner in temp for flatten in inner] #flatten matrix
            cnt+=1

#main
idxes = np.arange(train_count)
_error=limit*[0]
batch_size=100
for epoc in range(limit):
    np.random.shuffle(idxes)
    error = 0
    cnt=0
    for idx in idxes:
        y = forward(train_x[idx])
        diff = y - train_y[idx] 
        error += diff ** 2
        cnt+=1
        backward(train_x[idx], diff)
        if cnt==batch_size:
           break

    _error[epoc]=error    
    print(epoc+1,error.sum(),y)

result=train_count*[0]
for idx in range(train_count):
    result[idx]=forward(train_x[idx])

#accuracy rate
eval=np.argmax(train_y,axis=1)==np.argmax(result,axis=1)
print (float(eval.sum())/eval.size)

np.savetxt("estimated.csv",result,delimiter=",")
np.savetxt("train_y.csv",train_y,delimiter=",")
np.savetxt("error.csv",_error,delimiter=",")

np.savetxt("w1.csv",w1,delimiter=",")
np.savetxt("w2.csv",w2,delimiter=",")
np.savetxt("b1.csv",b1,delimiter=",")
np.savetxt("b2.csv",b2,delimiter=",")


文字のデータはネットを探してjpg化されたものを拾ってきました。

Index of /~roweis/data

0から9までの手書き文字がそれぞれ1枚ずつ用意されていて、 それをグレースケールで一次元のデータとして読み込んでいます。 ※値は255で正規化。

データが読み込めたら後は、学習させるだけです。

上のプログラムに記載されているノード数、学習回数、学習率で実行すると、 約94%の正答率でした。学習したデータに対する正答率なので、もう少し高くても良い気もしますが、 特に何の工夫もしていませんので、こんなものでしょうか。

やる気があれば、ディープラーニングのプログラムを組んで、もう一回試してみたいです。



Python機械学習プログラミング 達人データサイエンティストによる理論と実践 impress top gearシリーズ

機械学習ライブラリを使わずニューラルネットワークで為替レートを予測する

ニューラルネットワークといえば、pylearn2やTensrflowなどのライブラリがありますが、 使い方が独特だったりして習得に時間がかかるため、今回はこれらのようなライブラリを使わずに、 pythonの標準機能とnumpyで、為替レートの予測をやってみました。


ニューラルネットワークのプログラムは、一から作るのが面倒だったため、Wikipediaのソースを改変。

ニューラルネットワーク - Wikipedia

# -*- coding: utf-8 -*-
import numpy as np

dim_in = 10              # 入力
dim_out = 1             # 出力
hidden_count = 1024     # 隠れ層
learn_rate = 0.05      # 学習率
limit=3000             #学習回数

#学習データ読み込み
data=np.loadtxt("USDJPY_1D.csv",delimiter=",")
train_x=data[:,0:dim_in]/150.
train_y=data[:,dim_in:dim_in+dim_out]/150.
train_count=len(data)
    
# 重みパラメータ。-0.5 ? 0.5 でランダムに初期化。この行列の値を学習する。
w1 = np.random.rand(hidden_count, dim_in)-0.5
w2 = np.random.rand(dim_out, hidden_count)-0.5
b1 = np.random.rand(hidden_count)-0.5
b2 = np.random.rand(dim_out)-0.5

# 活性化関数
def activation(x):
    return np.maximum(0, x)
    #return 1.0/(1.0+np.exp(-x))
    
# 活性化関数の微分
def activation_dash(x):
    return (np.sign(x) + 1) / 2
    #return (1-activation(x))@activation(x)

# 順方向。学習結果の利用。
def forward(x):
    return w2 @ activation(w1 @ x + b1) + b2

# 逆方向。学習
def backward(x, diff):
    global w1, w2, b1, b2
    v1 = (diff @ w2) * activation_dash(w1 @ x + b1)
    v2 = activation(w1 @ x + b1)

    w1 -= learn_rate * np.outer(v1, x)  # outerは直積
    b1 -= learn_rate * v1
    w2 -= learn_rate * np.outer(diff, v2)
    b2 -= learn_rate * diff

# メイン処理
idxes = np.arange(train_count)
_error=np.zeros(limit)
for epoc in range(limit):
    np.random.shuffle(idxes)            # 確率的勾配降下法のため、エポックごとにランダムにシャッフルする
    error = 0                           # 二乗和誤差
    cnt=0
    for idx in idxes:
        y = forward(train_x[idx])       # 順方向で x から y を計算する
        diff = y - train_y[idx]         # 訓練データとの誤差
        error += diff ** 2              # 二乗和誤差に蓄積
        cnt+=1
        backward(train_x[idx], diff)    # 誤差を学習
        if cnt==np.round(train_count*0.1):
           break
    _error[epoc]=error    
    print(epoc+1,error.sum(),y)                  # エポックごとに二乗和誤差を出力。徐々に減衰して0に近づく。

#結果表示
result=np.zeros([train_count,2])
for i in range(train_count):
    result[i,0]=train_y[i]*150.
    result[i,1]=forward(train_x[i])*150.
    print(result[i,0],result[i,1])

#ファイル出力
np.savetxt("error.csv",_error,delimiter=",")
np.savetxt("result.csv",result,delimiter=",")

ニューラルネットワークの入力は過去10日分の終値、出力は翌日の終値としています。これを約6年分。 なお、どう正規化するのが良いのかわからなかったため、適当に150で割っています。 このデータ自体はMT4というFX用のソフトを使って取得しています。

3.ヒストリカル・データのCSVエクスポート - とあるMetaTraderの備忘秘録


実際に上記のプログラムで学習させた結果が以下です。上が誤差の推移、下が真値と推定値の比較です。

f:id:oki-lab:20160619003154p:plain

f:id:oki-lab:20160619003205p:plain

パッと見、良く予測できているように見えますが、実際には平均で0.5%ほど誤差があります。 流石にFXに使える程の精度ではありません。

実際にプログラムを動かす時に、学習時間の短縮のために全部のデータを学習するのではなく、 一部を使用するように以下の文を加えたのですが、何故か全データ学習した時より誤差は小さくなっていました。 時間が短縮できて、誤差も小さくなるのは素晴らしいですが、理屈が分からずじまい。

        if cnt==np.round(train_count*0.1):
           break

また、活性化関数としてシグモイド関数も試してみましたが、計算時間が長くなってしまった上に、誤差が大きくなりました。 ニューラルネットワークといえばシグモイドというイメージでしたが、今はReLUが最強?。


確かな力が身につくPython「超」入門 (確かな力が身につく「超」入門シリーズ)

Raspberry Pi3にUbuntu mateをインストール

遅ればせながらRaspberry Pi3を入手しましたので、Ubuntu mateをインストールしてみました。 Raspberry Pi 3 Model B

Windowsを使ったセットアップ方法を簡単にまとめてみます。

まず、下記のリンク先で、OSのISOをダウンロードします。

Download Ubuntu MATE | Ubuntu MATE

Win32 Disk Imagerをダウンロードします。

sourceforge.net

Win32 Disk Imagerを起動して、ダウンロードしたISOを選択し、SDカードに書き込みます。

f:id:oki-lab:20160505081753p:plain

最後に、SDカードをRaspberry Piに差し直して起動し、普段通りに初回起動のセットアップを行います。

なお、Raspberry Pi3は標準でWifi機能を搭載しており、普通のLinuxと同じように繋ぐことができます。 ただし、その分電気を食いますので、ご注意ください。

ちなみに、インストールしてみて初めて知りましたが、Minecraftがプリインストールされていました。 普通の製品版とは違うと思いますが、少し遊ぶ分には良いと思います。

f:id:oki-lab:20160505081947p:plain

一番初めのRaspberry PiはPCとしての性能は低く、実用には堪えないものでしたが、ここまで性能が高くなってくると、ウェブブラウジング程度であれば問題ないため、子供用PCとしての使用もアリかも知れません。

雑誌(VRFREEK)の付録でVR体験

週末、本屋に行ったらVRFREEKという雑誌を見かけたので、手にとって見たら、VRゴーグルが付録としてついてくるということで、即断即決で買ってきました。 完全に付録目当てなので記事に関してはノーコメント。

VRFREEK(ブイアールフリーク) 2016年 03 月号 [雑誌]: DTMマガジン 増刊

組み立ては非常に簡単で、2分程度で終わります。レンズは最初から組み込まれているので、単純に箱の部分を組み立てるだけとなっています。

f:id:oki-lab:20160321193707j:plain:w400

使い方は、VR対応のアプリを実行してレンズの反対側にスマホをセットすればOKです。この辺りはGoogleのCardboardと同じです。

いくつかのアプリを試してみた感想としては、やはりCardboardと同程度の没入感です。そのままではそれほどでもありませんが、部屋の電気を消すとそれなり、という感じです。 実はCardboardは眼鏡をかけたまま見れないという、眼鏡人にとって致命的な欠点があるので、眼鏡を掛けている人はこの雑誌の方が良いと思います。

Cardboard VR v2 2015

最後に試してみたアプリを紹介します。なお全てAndroid用となっています。

YouTube - Google Play の Android アプリ
 言わずと知れたYouTubeのアプリ。360°見渡せる動画が大量にアップロードされています。検索フィルタで360°という項目があるので、それを使えば一発で検索できます。

VR Roller Coaster - Google Play の Android アプリ
 VRといえばジェットコースターのアプリということで、一つご紹介。

VR Bike - Google Play の Android アプリ
 傾きでバイクを操作するゲーム。

Lamper VR: Firefly Rescue - Google Play の Android アプリ
 クオリティの高いアクションゲーム。向いている方向に移動するというのが独特の操作感で、慣れないと思うように動けません。

恐らく、VRゴーグルが付録になったのはこの雑誌が初めてだと思いますが、こういうのが増えていって、もっとVRが一般的になって欲しいです。

Leap MotionでMMDモデルに触れたことを検出する

前回の記事oki-lab.hatenablog.jp

Leap Motionを使ってMMDモデルに触れるようになりましたので、次はモデルに触れたことを検出できるようにしてみます。 f:id:oki-lab:20160228151446p:plain

まずは、下記のスクリプトを用意します。

using UnityEngine;
using System.Collections;

public class ModelTrigger: MonoBehaviour {
    private GameObject controller = null;
    private string[] touchInfo=new  string[3];

    // Use this for initialization
    void Start () {
        controller = GameObject.Find("TriggerController");
    }
    
    // Update is called once per frame
    void Update () {
    
    }

    void OnTriggerEnter(Collider other){
        touchInfo[0]=transform.root.gameObject.name;
        touchInfo[1]=transform.parent.gameObject.name;
        touchInfo[2]=other.gameObject.name;
        controller.SendMessage("Touch",touchInfo);
    }

    void OnTriggerExit(Collider other) {
        //Debug.Log(other.gameObject);
    }
}


using UnityEngine;
using System.Collections;

public class TriggerController : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }
    
    // Update is called once per frame
    void Update () {
    
    }

    void Touch(string[] touchInfo){
        Debug.Log(touchInfo[0]+","+touchInfo[1]+","+touchInfo[2]);
    }

}

次に、triggerControllerをLeap Motionの手のモデルのBoneにアタッチし、そのモデルのPrefabをHandControllerにアタッチし直します。

f:id:oki-lab:20160228150618p:plain

最後に、空のGameObjectを作成し、ModelTriggerのスクリプトをアタッチすれば完了です。

f:id:oki-lab:20160228151348p:plain

上のスクリプトでは、左右どちらの手のどの指がどのオブジェクトに触れたのかを、コンソール上に表示するようにしています。 これを応用すれば、特定の部位に触れた時に、所定のリアクションをさせることも出来ると思います。


Unity5入門 最新開発環境による簡単3D&2Dゲーム制作

ネットワーク機能を使ったアプリケーション間の通信 (Unity)

今まで特に注目していませんでしたが、Unity5にネットワーク機能が実装されているということで、少し試してみました。

サーバープログラム

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.Networking.NetworkSystem;

public class SocketServer : MonoBehaviour {

    // Use this for initialization
    void Start () {
        SetupServer();
    }
    
    // Update is called once per frame
    void Update () {
    
    }

    public void SetupServer(){
        NetworkServer.Listen("127.0.0.1",2000);
        NetworkServer.RegisterHandler(MsgType.Connect,OnConnected);
        NetworkServer.RegisterHandler(888,OnReceive);
    }

    public void OnConnected(NetworkMessage netMsg)
    {
        Debug.Log("Connected to client");
    }

    public void OnReceive(NetworkMessage netMsg)
    {
        Debug.Log("Received");
        var recvMsg=netMsg.ReadMessage<StringMessage>();
        Debug.Log(recvMsg.value);
    }

}

クライアントプログラム

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.Networking.NetworkSystem;

public class socketClients : MonoBehaviour {
    NetworkClient myClient;

    // Use this for initialization
    void Start () {
        SetupClient();
    }
    
    // Update is called once per frame
    void Update () {
    
    }

    class Message:MessageBase{
        public string text;
    }


    public void SetupClient(){
        myClient=new NetworkClient();
        myClient.RegisterHandler(MsgType.Connect, OnConnected);
        myClient.Connect("127.0.0.1", 2000);
    }

    public void OnConnected(NetworkMessage netMsg){
        Debug.Log("Connected to server");

        var myMsg=new StringMessage("test");
        myClient.Send(888,myMsg);
    }

    public void OnReceive(NetworkMessage netMsg)
    {
        Debug.Log(netMsg.ToString());
    }
}

基本的に以下のリンク先のドキュメントの応用です。

Unity - マニュアル: ネットワークのクライアントとサーバー

サーバーにクライアントから文字列を送るだけのプログラムですが、最低限、通信は出来ますので問題はないと思います。

上のプログラムはUnityアプリ同士のものですが、本当はC++で書かれたソケット通信用のプログラムと通信したいです。 ちょっと調べただけだと良く分からなかったので、時間があったらもう少し調べて試してみます。

TensorFlowでディープラーニングを試してみる

最近巷で話題のディープラーニングですが、手を出すには敷居が高いと考えていた方も多いと思います。
そんな中、Googleディープラーニングに対応した機械学習オープンソースのライブラリを公開しましたので、試してみました。

下準備

セットアップの方法は、こちらに記載されています。 私はUbuntuで試してみました。なお、64 bitにしか対応してないようです。

$ sudo apt-get install python-pip python-dev
$ sudo pip install --upgrade https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.5.0-cp27-none-linux_x86_64.whl

手書き数字認識

チュートリアルとして、手書き数字の認識が用意されています。Google曰く、機械学習のプログラムの"Hello World"に当たるのが、この数字の認識だそうです。

MNIST For ML Beginners
チュートリアルは上記のリンク先にありますが、チュートリアル中のコードを動かすには、input_data.pyが必要です。 リンク先からコピペするか、下記で色々まとめて、ダウンロードしておきます。

git clone https://github.com/tensorflow/tensorflow 

後はチュートリアル中に出てくるコードをコピペしていくと、ひとつのプログラムになります。

import input_data
import tensorflow as tf

#load images
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

#place holder
x = tf.placeholder(tf.float32, [None, 784])

#weight
W = tf.Variable(tf.zeros([784, 10]))
#bias
b = tf.Variable(tf.zeros([10]))

#normalize
y = tf.nn.softmax(tf.matmul(x, W) + b)

#placeholder
y_ = tf.placeholder(tf.float32, [None, 10])

#cross entropy
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))

#minimize cross_entropy using the gradient descent algorithm
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

#initialize all variables
init = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init)

#training step
for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

#correct boolean list
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
#cast to float point numbers
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

#show result
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})

以上のコードを実行すると、学習が始まり結果が出ます。(画像は勝手にダウンロードしてくれます) 特に数字をいじらなければ、約91%になります。それなりに高い数字のようにも思いますが、あまり良くない数字だそうです。これは次のチュートリアルで更に高精度になるようです。

流石にコードをコピペしただけでは、何やってるのか良く分からないと思うので、チュートリアルの文章は読む必要があります。
普通のニューラルネットワークはともかく、ディープラーニングに関しては、日本語の情報が少ないので、中々苦労しますが、色んなことが出来そうですので、このライブラリ公開を機に、手を出してみては如何でしょうか。


深層学習 (機械学習プロフェッショナルシリーズ)