読者です 読者をやめる 読者になる 読者になる

カルマンフィルタで角度推定 (加速度センサ+ジャイロセンサ)

以前購入していた加速度センサ、ジャイロセンサの使い方を思い出す為に、それぞれの値を取得するプログラムを書きました。それだけでは面白くないので、カルマンフィルタで値を統合してみました。

機器構成はArduino nanoに加速度センサ(MMA7361)、ジャイロセンサ(ENC-03R)を接続し、更に、ArduinoRaspberry piを繋いでシリアル通信でデータのやり取りをする、というものです。Arduinoを使う理由はアナログ入力が楽だから、Raspberry piを使う理由はカルマンフィルタを使うのにpythonでプログラムを書きたかったからです。
なお、センサは秋月電子で買える以下のものです。

3軸加速度センサモジュール MMA7361(アナログ出力)
小型圧電振動ジャイロモジュール

各センサの接続先は加速度センサのx,y,zがそれぞれArduinoのA0,A1,A2、ジャイロセンサのOUT1がA3,OUT2がA4です。
加速度センサは計測範囲を広げるため、gSに3.3V入力します。

また、事前準備として、加速度センサを使うためのライブラリをインストールしておきます。

github.com

Arduino側プログラム

#include <AcceleroMMA7361.h>
#include <stdio.h>

AcceleroMMA7361 accelero;

int ax=0;
int ay=0;
int az=0;

int wx=0;
int wy=0;

char buf[50];

void setup(){
 Serial.begin(9600);
 accelero.begin(13,12,11,10,A0,A1,A2);
 accelero.setARefVoltage(3.3);
 accelero.setSensitivity(LOW);
 accelero.calibrate();
}

void loop(){
 ax=accelero.getXAccel();
 ay=accelero.getYAccel();
 az=accelero.getZAccel();
 
 wx=analogRead(3);
 wy=analogRead(4);
 
 //ax=accelero.getXRaw();
 //ay=accelero.getYRaw();
 //az=accelero.getZRaw();
 
 sprintf(buf,"%d,%d,%d,%d,%d\n",ax,ay,az,wx,wy);
 
 Serial.write(buf);
 delay(1);
}

Raspberry pi側プログラム

import serial
import numpy as np
import time

ser=serial.Serial("/dev/ttyUSB0",9600)

data=np.zeros(5)

#offset=1.35/3.3*1023
offset=np.array((445,442))

F=np.matrix(np.identity(2))
H=np.matrix(np.identity(2))
x=np.zeros((2,1))
y=np.zeros((2,1))
u=np.zeros((2,1))
dt=0.01
P=np.zeros((2,2))
Q=np.array(((1E-3,0),(0,1E-3)))
R=np.array(((1E-2,0),(0,1E-2)))
t=time.time()
a=np.zeros((2,1))

elapsedTime=0
w=np.zeros((2,1))
rawData=[]
estData=[]
t=time.time()
dt=0

firstFlag=True

while True:
    val=str(ser.readline())
    temp=val.split(",")

    try:
        for i in range(5):
            data[i]=float(temp[i])

        print (data[0],data[1],data[2],data[3],data[4])

        #For initializing time
        if (firstFlag):
            firstFlag=False
            t=time.time()

        #angular acceleration (gyro sensor)
        a[0]=(data[3]-offset[0])/1023*3.3/0.00067*np.pi/180
        a[1]=(data[4]-offset[1])/1023*3.3/0.00067*np.pi/180

        #angular velocity (gyro sensor)
        for i in range(2):
            if (np.abs(a[i])>5):
                u[i]+=a[i]*dt

        #angle (acceleration sensor)
        y[0]=np.arctan2(data[1],np.sqrt(data[0]**2+data[2]**2))
        y[1]=np.arctan2(data[0],data[2])

        #angle (gyro sensor)
        w+=u*dt*180/np.pi

        #kalman filter
        x=x+u*dt
        P=np.dot(np.dot(F,P),F.T)+Q
        K=np.dot(np.dot(P,H.T),np.linalg.inv(np.dot(np.dot(H,P),H.T)+R))
        x=x+np.dot(K,(y-np.dot(H,x)))
        P=P-np.dot(np.dot(K,H),P)

        estData.append(np.r_[x*180/np.pi,y*180/np.pi,w])
        rawData.append(data)

        dt=time.time()-t
        t=time.time()
        elapsedTime+=dt

        print (int(elapsedTime),x*180/np.pi)

        if (elapsedTime>10):
            np.savetxt("estData.csv",estData,delimiter=",")
            np.savetxt("rawData.csv",rawData,delimiter=",")
            break

    except Exception as e:
        print (e.message)



一応コメントを書いてありますが、xがカルマンフィルタによる推定値、yが加速度による推定値、wがジャイロによる推定値(積分値)です。
QやRの共分散行列の値は適当です。数値を変えてもあまり変わりませんでした。
なお、はじめにプログラムを書いた時、ジャイロによる推定値が明らかにおかしかったので、計算方法が間違っていたのかと思いましたが、 ネットで調べてみると、回路上にハイパスフィルタが入っていて、出力値が角加速度になっているようです。その為、2階積分になるようなプログラムにしています。
また、ジャイロの積分誤差が大きい為、一定値以下の数値は無視しています。

一通り接続してセンサを傾けてみた結果が下記のグラフです。
ジャイロによる値は誤差が大きいですが、加速度による推定値とカルマンフィルタによる推定値は殆ど同じです。
今回の場合だとジャイロセンサがあまり役に立っていない気もしますが、とりあえず傾きが取得できるようになりました。

f:id:oki-lab:20170425202847j:plain

格安のArduino Nano互換機を使う

海外の通販でArduino Nanoの互換機を買ってみたのですが、動かすまでに少し手間取ったのでやったことをメモ。
目標は、Raspberry Pi上でスケッチを書くための環境を整えて、ArduinoRaspberry Piとデータのやり取りができるようにすることです。
また、今回はUbuntu MATEで試しています。

1.ドライバインストール

シリアル接続用のドライバをインストールします。 今回はRaspberry Pi上で使用するので、Linux用を下記からダウンロード。

CH341SER_LINUX.ZIP下载页面-江苏沁恒股份有限公司

解凍して、readmeに書かれている通り、make、make loadを実行します。 ただ、私の環境だと上記を実行すると、 /lib/modules/4.4.38-v7がどうのこうのというエラーが出てきたので、 色々調べてみた結果、下記を実行すると、makeが通るようになりました。

sudo apt-get install raspberrypi-kernel-headers

2.Arduino IDEインストール

下記を実行して、IDEをインストールします。

sudo apt-get install arduino

3.Arduinoへ書き込み

下記を実行してIDEを立ち上げます。

sudo arduino

Arduinoを接続した後、
ツール→マイコンボード→Arduino Nano w/ATmega328を選択。
ツール→シリアルポートでArduinoが接続されているポートを選択。

適当なスケッチを書いてみて、無事書き込みが出来ればOKです。

文字にすると簡単そうですが、ドライバのmakeができずに苦労しました。 また、Arduinoは2つ買ったのですが、そのうち1個はどうしても書き込みができず、 不良品だと思って諦めました。どうせ数百円なので返品はしませんが、 海外通販を使う際は、ダメ元くらいの感覚でいた方が良さそうです。

【TensorFlow】リカレントニューラルネットワークで為替レートを予測【FX】

以前、ニューラルネットワークで為替レートを予測する記事を書きましたが、 今度はTensorFlowを使って予測してみました。
前回とは異なり、今回はリカレントニューラルネットワークというものを使ってみます。理論は殆ど理解できていないので、 ここにあるコードを書き換えました。

入力は、4時間足のデータ25個、出力は3パターンで、4時間後に値が上昇するなら、[1,0,0]、下がるなら[0,1,0]、同値なら[0,0,1]としています。 なお、入力値は0~1の値に正規化しています。

学習プログラム

import tensorflow as tf
from tensorflow.contrib import rnn
import numpy as np


# Parameters
learning_rate = 0.01
training_iters = 100000
batch_size = 100
display_step = 10

# Network Parameters
n_input = 1
n_steps = 25 # timesteps
n_hidden = 1024 # hidden layer num of features
n_classes = 3

#exchange rates
data=np.loadtxt("input.csv",delimiter=",")

# tf Graph input
x = tf.placeholder("float", [None, n_steps, n_input])
y = tf.placeholder("float", [None, n_classes])

# Define weights
weights = {
    # Hidden layer weights => 2*n_hidden because of foward + backward cells
    'out': tf.Variable(tf.random_normal([2*n_hidden, n_classes]))
}
biases = {
    'out': tf.Variable(tf.random_normal([n_classes]))
}

def BiRNN(x, weights, biases):

    # Prepare data shape to match `bidirectional_rnn` function requirements
    # Current data input shape: (batch_size, n_steps, n_input)
    # Required shape: 'n_steps' tensors list of shape (batch_size, n_input)
    
    # Permuting batch_size and n_steps
    x = tf.transpose(x, [1, 0, 2])
    # Reshape to (n_steps*batch_size, n_input)
    x = tf.reshape(x, [-1, n_input])
    # Split to get a list of 'n_steps' tensors of shape (batch_size, n_input)
    x = tf.split(x, n_steps, 0)

    # Define lstm cells with tensorflow
    # Forward direction cell
    lstm_fw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
    # Backward direction cell
    lstm_bw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)

    # Get lstm cell output
    try:
        outputs, _, _ = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x,
                                              dtype=tf.float32)
    except Exception: # Old TensorFlow version only returns outputs not states
        outputs = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x,
                                        dtype=tf.float32)

    # Linear activation, using rnn inner loop last output
    return tf.matmul(outputs[-1], weights['out']) + biases['out']

pred = BiRNN(x, weights, biases)

# Define loss and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

# Evaluate model
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

# Initializing the variables
init = tf.global_variables_initializer()

# Launch the graph
with tf.Session() as sess:
    sess.run(init)
    step = 1
    # Keep training until reach max iterations
    while step * batch_size < training_iters:
        
        np.random.shuffle(data)
        
        batch_x=data[0:batch_size,0:n_steps]
        #normalize
        batch_x=batch_x-np.min(batch_x,axis=1).reshape(batch_size,1)
        batch_x=batch_x/np.max(batch_x,axis=1).reshape(batch_size,1)

        batch_x = batch_x.reshape((batch_size, n_steps, n_input))
        batch_y=data[0:batch_size,n_steps:n_steps+n_classes]
        batch_y=batch_y.reshape((batch_size,n_classes))

        # Run optimization op (backprop)
        sess.run(optimizer, feed_dict={x: batch_x, y: batch_y})
        if step % display_step == 0:
            # Calculate batch accuracy
            acc = sess.run(accuracy, feed_dict={x: batch_x, y: batch_y})
            # Calculate batch loss
            loss = sess.run(cost, feed_dict={x: batch_x, y: batch_y})
            #print ("Iter"+str(step*batch_size)+", Training Accuracy="+str(acc))
            print ("Iter " + str(step*batch_size) + ", Minibatch Loss= " + \
                 "{:.6f}".format(loss) + ", Training Accuracy= " + \
                  "{:.5f}".format(acc))

        step += 1
    print ("Optimization Finished!")

結果

Iter 90000, Minibatch Loss= 0.746534, Training Accuracy= 0.50000
Iter 91000, Minibatch Loss= 0.750871, Training Accuracy= 0.47000
Iter 92000, Minibatch Loss= 0.738956, Training Accuracy= 0.54000
Iter 93000, Minibatch Loss= 0.803958, Training Accuracy= 0.55000
Iter 94000, Minibatch Loss= 0.784448, Training Accuracy= 0.58000
Iter 95000, Minibatch Loss= 0.764126, Training Accuracy= 0.49000
Iter 96000, Minibatch Loss= 0.750926, Training Accuracy= 0.54000
Iter 97000, Minibatch Loss= 0.836504, Training Accuracy= 0.47000
Iter 98000, Minibatch Loss= 0.828720, Training Accuracy= 0.51000
Iter 99000, Minibatch Loss= 1.095886, Training Accuracy= 0.54000
Optimization Finished!

10万回回した結果、学習データに対してすら50数%程度で、ランダムと対して変わらない結果になってしまいました。 モデルが単純すぎるのか、入力値が悪いのか、そもそも予測ができないものなのか分かりませんが、もう少し工夫してみたいです。

WindowsにTensorFlowをインストール

普段、TensorFlowはUbuntuが入ったノートパソコンで動かしていますが、 画像処理を行おうとすると、流石にスペック不足の為、 Windows10のデスクトップPCにインストールすることにしました。

インストール方法

基本的に公式サイトを見れば良いですが、 案内通りにAnacondaをインストールして下記を実行すると、エラーが発生しました。

pip install --upgrade https://storage.googleapis.com/tensorflow/windows/cpu/tensorflow-0.12.1-cp35-cp35m-win_amd64.whl
tensorflow-0.12.1-cp35-cp35m-win_amd64.whl is not a supported wheel on this platform.

AnacondaでインストールされたのがPython3.6で、公式サイトでサポートしていると謳っているのが、3.5なので、 3.6をアンインストール後、3.5をインストールして、上記コマンドを実行すると、無事インストールされました。

ちゃんとインストールされているかどうかは、公式サイトにある通り、 pythonを起動して、tensorflowをインポートできるかどうかで確認できます。

TensorFlowのTutorialを改造して画像識別

以前少しだけTensorFlowの記事を書きましたが、 久しぶりにTensorFlowを触ってみたので、成果物のご紹介。

やりたいこと

アイドルマスターシンデレラガールズのキャラクターを識別する。

やったこと

  • フリーソフトを使って画像を自動収集。
  • ここのプログラムの応用で、顔画像を切り出して、手動でタグ付け。(ファイル名を"タグ,(連番).jpg"に)
  • チュートリアルのプログラムを元に、学習プログラムを作成して、顔画像を学習。
  • 学習したパラーメタを使って検証。

学習プログラム

学習した顔画像は、64*64のリサイズにして保存しています。 また、ファイル名のカンマ以前の数字がキャラクターを識別するタグとなります。 今回は2500枚程度用意しました。

顔画像の例↓

f:id:oki-lab:20170205200131j:plain f:id:oki-lab:20170205200202j:plain

import cv2
import sys
import os.path
import glob
import numpy as np
import tensorflow as tf

image_w=64
image_h=64
dim_in=image_w*image_h
dim_out=18
limit=3000
batch_size=100

paths=glob.glob("images/*")
image_num=len(paths)

train_x=np.zeros((image_num,dim_in))
train_y=np.zeros((image_num,dim_out))

#loading images
cnt=0
for path in paths:
    image = cv2.imread(path,0)/255.0
    tag=int(os.path.basename(path).split(",")[0])
    train_x[cnt,:]=[flatten for inner in image for flatten in inner] #flatten matrix
    train_y[cnt,tag]=1
    print (tag)
    cnt+=1

#neural network
x = tf.placeholder(tf.float32, [None, dim_in]) #place holder
W = tf.Variable(tf.zeros([dim_in, dim_out])) #weight
b = tf.Variable(tf.zeros([dim_out])) #bias
y = tf.nn.softmax(tf.matmul(x, W) + b) #normalize
y_ = tf.placeholder(tf.float32, [None, dim_out]) #placeholder

#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.0005).minimize(cross_entropy)

#initialize all variables
init = tf.initialize_all_variables()

#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"))
saver=tf.train.Saver()

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

#training step
train_count=0
for i in range(limit):
    rnd=np.random.randint(image_num-batch_size)
    sess.run(train_step,feed_dict={x:train_x[rnd:rnd+batch_size,:],y_:train_y[rnd:rnd+batch_size,:]})
    train_count+=1
    print train_count,sess.run(accuracy,feed_dict={x:train_x[rnd:rnd+batch_size,:],y_:train_y[rnd:rnd+batch_size,:]})
    
print sess.run(accuracy,feed_dict={x:train_x,y_:train_y})

#save
saver.save(sess,"model.ckpt")

概要説明:
“images"フォルダに保存された顔画像を読み込み、画素情報を一次元化し、入力用テンソルに保存。
ウェイトやバイアス、損失関数や正答率用の関数を定義。
バッチサイズ分のデータを切り出して、学習。それを規定ステップ分繰り返し。
最後にモデルを保存。

評価用プログラム

import cv2
import os.path
import glob
import numpy as np
import tensorflow as tf

image_w=64
image_h=64
dim_in=image_w*image_h
dim_out=18

paths=glob.glob("images/*")
image_num=len(paths)

train_x=np.zeros((image_num,dim_in))
train_y=np.zeros((image_num,dim_out))

#loading images
cnt=0
for path in paths:
    image = cv2.imread(path,0)/255.0
    tag=int(os.path.basename(path).split(",")[0])
    train_x[cnt,:]=[flatten for inner in image for flatten in inner] #flatten matrix
    train_y[cnt,tag]=1
    print (tag)
    cnt+=1

#neural network
x = tf.placeholder(tf.float32, [None, dim_in]) #place holder
W = tf.Variable(tf.zeros([dim_in, dim_out])) #weight
b = tf.Variable(tf.zeros([dim_out])) #bias
y = tf.nn.softmax(tf.matmul(x, W) + b) #normalize
y_ = tf.placeholder(tf.float32, [None, dim_out]) #placeholder

#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"))
    
saver=tf.train.Saver()
sess=tf.Session()
saver.restore(sess,"model.ckpt")

print sess.run(accuracy,feed_dict={x:train_x,y_:train_y})

概要説明:
画像の読み込みや変数、関数の定義は同じ。
モデルを読み込み、これを使って識別した時の正答率を表示。

所感

今回は画像があまり集められなかったことと、振り分けが面倒だったことが理由で、
画像の量が少ないですが、学習用データに対する正答率は94%程度でした。
上記のプログラムはディープラーニングではなく、非常に単純なニューラルネットワークなので、
気が向いたらディープラーニング化してみたいと思います。

Unityの起動失敗と対策

初めてUnity Bug Reporterを使ってみた、というお話。

先日、デスクトップPCを買い替えたため、色々と開発環境を整えているところですが、
Unityをインストールしたところ、起動直後にクラッシュする現象が発生しました。

とりあえず調べてみて、下記の事を試してみました。

・再インストールする。
・セキュリティソフトを終了させる。
・起動オプションに「 -force-d3d11」を追加する。
・管理者として実行する。
・互換モードで実行する。
・起動時にAltキーを押す。

全部やっても解決せず、万策尽きた感じになったため、
Unity Bug Reporterにて、レポート送信してみました。

ログは勝手に送信可能な状態になってるので、メールアドレスとタイトルを
入れてSendボタンを押すだけです。

回答が来るまで時間がかかるかと思いましたが、約15時間で返ってきました。

回答要約すると、
Lavasoft Web Companionというソフトがインストールされてるので、
これをアップデートしなさい。
という内容でした。

Lavasoft Web Companionなどというソフトをインストールした覚えが無かったため、
アンインストールしたところ、無事Unityが起動するようになりました。

あまり期待していなかったのですが、非常に早く解決できたため、
Unityに関して何か問題があった場合は、Bug Reporterを活用すると
幸せになれるかもしれません。

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

前回の記事でニューラルネットワークを使って為替レートを予測してみましたが、 せっかくなので、それを少し改良して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シリーズ