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

ウォンツテック

技術的な話や会社設立に関わる話などを適当にしていきます

TensorFlowを使ってみる 5

Kerasを使ってみる

tensorflowの高機能ライブラリであるcontrib.learnを使ってみたんですが
今一わかりづらかったのでもう一つの高機能ライブラリであるKerasをさわってみます

とりあえず1層でOptimizerは最降下勾配方で最も単純なネットワークを組んでみます

gist.github.com

contrib.learnと比べて層で何をやっているかが非常に明快です。
ただし、結果としてはaccuracyが0.86くらいと前回素のtensorflowで組んだ場合の0.97と比べるとかなり低いです。

と思って調べてみたら、lrの値、learning rateという1回の微分でどの程度重みを移動させるかの値が0.01とかなり低い値を指定してしまっていました。

素のtensorflowで組んだ場合と同じ値の0.5にしたらaccuracyは0.956と精度は近づいてます。

後、精度を出すのに重要なハイパーパラメータである重みの初期値を以下のように合わせてみたんですが、精度は逆に落ちて0.952くらいとなってしまいました。

std_deviation = 0.01

def my_init(shape, name=None):
    print(shape)
    value = std_deviation * np.random.randn(*shape)
    return K.variable(value, name=name)

model.add(Dense(output_dim=100, input_dim=784, init=my_init))

Keras使ってみる 2

Kerasで畳み込みニューラルネットワークを書いてみました

gist.github.com

やはりcontrib learnより何をやっているか分かり易く、Try&Errorが容易に出来そうな気がします

結果はaccuracyが0.991となりました。

TensorFlowを使ってみる 4

畳み込みニューラルネットワークチュートリアルをやってみる

Deep MNIST for Experts  |  TensorFlow

このページの「Build a Multilayer Convolutional Network」以下をやってみました

※mnistは「ゼロから作るDeepLearning本」の方で取得したデータを利用するように変えてあります

気になった所をいくつか抜粋

def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

def main(_):
    ...
    W_conv1 = weight_variable([5, 5, 1, 32])

フィルターのtensorの順序がゼロから作る本と違う。
内部的には正しい計算をしているはずなのでいいとして、TensorFlowのConvolutionの順序は縦、横、チャネル(色)、アウトプットチャネルです。

def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

stridesに渡す配列の0と3番目の要素は必ず1とし、中の二つの値(同じにする)がstrideとなる。
この場合はstrideが1。
paddingは2種類「SAME」と「VALID」を指定出来てSAMEの場合は入力と出力のサイズが同じになるように自動でpaddingを調整してくれる。VALIDの場合は何もせず。
max_pool_2x2で定義されているstridesとpaddingも同じ。ksizeはpoolフィルタのサイズで2x2。

    W_fc1 = weight_variable([7*7*64, 1024])
    b_fc1 = bias_variable([1024])
    h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
    h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

2回のpoolフィルタで28x28x1→ 14x14x32→ 7x7x64までになったデータを一旦1024個のニューロンに出力してます(Affine変換で)
(2x2のフィルタをstride 2でフィルタしてるので元の半分になる)
今一何のためにやってるかわかりません。
データを圧縮するため?

   keep_prob = tf.placeholder(tf.float32)
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

過学習を防ぐためにいくつかのニューロンの伝搬を止めるdropout層というのが挿入されてます

    W_fc2 = weight_variable([1024, 10])
    b_fc2 = bias_variable([10])
    y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

    y_ = tf.placeholder(tf.float32, [None, 10])
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

最後は数字のクラス分けをするために10の値で出力しています(Affine変換)

ちなみにこれでmnistを学習すると

step 11000, training accuracy 1
step 11100, training accuracy 1
step 11200, training accuracy 1
step 11300, training accuracy 0.99
step 11400, training accuracy 1
step 11500, training accuracy 1
step 11600, training accuracy 1
step 11700, training accuracy 1
step 11800, training accuracy 1
step 11900, training accuracy 1
step 12000, training accuracy 1
step 12100, training accuracy 0.99
step 12200, training accuracy 1

こんな感じで0.99を超えました。


試したコード
experiment2.py · GitHub

TensorFlowを使ってみる 3

実際自分で書いた数字で試してみる

前回書いたサンプルコードを使い、実際にmacのお絵描きソフトで書いた以下の数字を認識するかやってみました。
mnistで試してるだけだとなんだか本当に合ってんだかよくわからないですからね。


f:id:sodex:20170214171049p:plain f:id:sodex:20170214171058p:plain f:id:sodex:20170214171104p:plain


gist.github.com

結果は

0.9746
[1 5 7]

となり、こんな汚い数字でも無事認識していました。

数字の取り込みはPILを使いgrayscaleで読み込み(convert("L"))、numpyでmatrixにし、flattenで1次元配列にしたあと反転(255で引く)と正規化(255で割る)を実施しています。

TensorFlowを使ってみる 2

TensorFlowのOptimizerを使ってみる

MNIST For ML Beginners  |  TensorFlow

TensorFlowのチュートリアル機械学習初学者向けのドキュメントを読んで、サンプルのコード

tensorflow/mnist_softmax.py at master · tensorflow/tensorflow · GitHub

をゼロから作るDeepLearning本でやってた2層に改修して実験

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import sys

from tensorflow.examples.tutorials.mnist import input_data

import tensorflow as tf

FLAGS = None


def main(_):
  # Import data
  mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)

  # Create the model
  x = tf.placeholder(tf.float32, [None, 784])
  std = 0.01
  W1 = tf.Variable(tf.random_normal([784, 100], stddev=std))
  b1 = tf.Variable(tf.zeros([100]))
  W2 = tf.Variable(tf.random_normal([100, 10], stddev=std))
  b2 = tf.Variable(tf.zeros([10]))
  a1 = tf.matmul(x, W1) + b1
  z1 = tf.sigmoid(a1)
  a2 = tf.matmul(z1, W2) + b2
  y = tf.nn.softmax(a2)


  y_ = tf.placeholder(tf.float32, [None, 10])
  cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
  train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

  sess = tf.InteractiveSession()
  tf.global_variables_initializer().run()

  # Train
  for _ in range(10000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

  # Test
  correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
  accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
  print(sess.run(accuracy, feed_dict={x: mnist.test.images,
                                      y_: mnist.test.labels}))

if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('--data_dir', type=str, default='/tmp/tensorflow/mnist/input_data',
                      help='Directory for storing input data')
  FLAGS, unparsed = parser.parse_known_args()
  tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)

サンプルコードだと重み変数(W)を生成する時に0で初期化してるけど、初期値全部0はよろしくないはずで、Optimizerがよしなにやってくれるんですかね。
実験コードではrandom_normalの標準偏差を0.01にしてます。

Trainingの回数を1万回でだいたいaccuracyが0.931くらいになりました。
ちなみに元のサンプルコードは1層のはずなのにaccuracyが0.923くらいとやたら精度が高い気がします。
1層だと線形で、線形だと簡単な論理演算(XOR)すら表現出来なかったはずなのにと..

※出力層でsoftmaxに渡す値の活性化関数にsigmoidを使っていましたが、これは恒等関数(何もしない)が正しかったです。
書き直したらaccuracyは0.974となりました。

TensorFlowを使ってみる 1

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

この本を読み終わったので機械学習を実験してみようと思います。

ライブラリはGoogleが公開しているTensorFlowを使います

www.tensorflow.org

日本のPFI社が開発したChanierあたりも気になっているのでそのうち触ってみたいです

TensorFlowのインストール

PythonをAnacondaでインストールしているので以下のようにインストール

$ conda create -n tensorflow python=3.5

その次に環境を切り替えます

$ source activate tensorflow

ここで、私の環境ではシェルごと落ちる問題が発生しました。
調べると

pyenvとanacondaを共存させる時のactivate衝突問題の回避策3種類 - Qiita

pyenvのactivateと衝突しているらしいので、

export PATH="$PYENV_ROOT/versions/anaconda3-2.4.0/bin/:$PATH"

をshellの設定ファイルに追記しておきます 「eval "$(pyenv init -)" 」より後に記述

プロンプトが切り替わったら以下を実行
※CPUでしか使わない人は実行するらしい

(tensorflow)/Users/sode% conda install -c conda-forge tensorflow

TensorFlowを使った簡単な計算

Basic Usage  |  TensorFlow

このサイトに載ってる簡単な計算が動くか試しておきました。

Graph

import tensorflow as tf

matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.], [2.]])
product = tf.matmul(matrix1, matrix2)

sess = tf.Session()
result = sess.run(product)

print(result)

sess.close()

TensorFlowを算数で理解する - Qiita

このサイトの説明によると、TensorFlowはNodeとedgeで構成されていて、それをGraphと呼んでいる
Graphの計算はSessionが管理している。
このコード例だとmatrix1とmatrix2がedgeでmatmul(行列積)がNode、これらを組み合わせたproductがGraphって事ですね。

計算はSessionのrunメソッドで実施し、内部的には高度な最適化がされてそう。

Interactive Session

import tensorflow as tf
sess = tf.InteractiveSession()

x = tf.Variable([1.0, 2.0])
a = tf.constant([3.0, 3.0])
x.initializer.run()

sub = tf.sub(x, a)
print(sub.eval())

sess.close()

続いての例はtensorflowのeval使ってインタラクティブに(いちいちSession.runせずに)計算させたい時に使うっぽい。

Variables

import tensorflow as tf

state = tf.Variable(0, name="counter")

one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)

init_op = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init_op)
    print(sess.run(state))

    for _ in range(3):
        sess.run(update)
        print(sess.run(state))

Variableは変数。assignもNodeの1種でSessionのrunで計算するまで評価されない。updateを評価(run)する際にassignとaddが走りstateが更新される。

Fetches

input1 = tf.constant([3.0])
input2 = tf.constant([2.0])
input3 = tf.constant([5.0])
intermed = tf.add(input2, input3)
mul = tf.mul(input1, intermed)

with tf.Session() as sess:
  result = sess.run([mul, intermed])
  print(result)

Sessionのrunは同時に複数のGraphを渡し、同時に計算する事が可能

Feeds

import tensorflow as tf

input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)
output = tf.mul(input1, input2)

with tf.Session() as sess:
  print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))

GraphのEdgeは型だけ用意しておいて評価時(run)に実体を渡してもいいよという事

rails5 + puma + nginx で AWSテスト環境を構築してみる

puma

railsのAPサーバとWebサーバですが、昨今ではrails5にpumaが標準になったり、herokuがpumaを推奨したりとpumaがおすすめらしいので私のサービスのテスト環境もpumaで構築することとしました。

github.com


ざっくりとした手順は以下のような感じです

  1. railsの設定(ここは割愛します)
  2. puma.rbの編集
  3. puma起動ファイルの編集(/etc/init.d/puma)
  4. nginxのインストール&編集

ちなみに私のAWSubuntuで構築してます

pumaのコンフィグファイル修正

rails5でアプリを作るとアプリケーションがあるディレクトリのconfig/以下にpuma.rbってのが出来ています。
以下の項目を追記します

# アプリケーションがあるディレクトリと各種ファイルを生成するディレクトリを指定する
# tmpディレクトリは存在していないが、手動で作成するか起動時のスクリプトで作成するようにする
#  socketやlogやpidファイルを格納するディレクトリも存在していないので手動かスクリプトで作成
#  存在しないと起動出来ずに落ちます
app_dir = File.expand_path("../..", __FILE__)
tmp_dir = "#{app_dir}/tmp"

# 環境変数を指定する。起動時に変数があればそれを見る。無ければテスト環境である"staging"としている
rails_env = ENV['RAILS_ENV'] || "staging"
environment rails_env

# socketでbindする。nginxからsocket経由で接続するため
bind "unix://#{tmp_dir}/sockets/puma.sock"

# ログ出力ファイルの指定
stdout_redirect "#{tmp_dir}/logs/puma.stdout.log", "#{tmp_dir}/logs/puma.stderr.log", true

# pidとstateファイルの格納
pidfile "#{tmp_dir}/pids/puma.pid"
state_path "#{tmp_dir}/pids/puma.state"

pumaの起動ファイルの作成

pumaの起動ファイルについてはサーバが再起動しても自動で立ち上がるようにinit.dに書きます
upstartなどで書いてもいいいです

各種ディレクトリの生成(無ければ)と、起動ユーザーを気をつけて、起動時の環境変数に気をつけましょう

sudo vi /etc/init.d/puma

#!/bin/bash

NAME=puma
USER=ubuntu
APP_NAME=app
APP_DIR=/home/$USER/$APP_NAME
TMP_DIR=$APP_DIR/tmp
PID_DIR=$TMP_DIR/pids
SOCKET_DIR=$TMP_DIR/sockets
LOG_DIR=$TMP_DIR/logs
PUMA_BIN=/home/$USER/.rbenv/shims/puma

RAILS_ENV=staging

sudo -u $USER -H sh -c "mkdir -p $PID_DIR"
sudo -u $USER -H sh -c "mkdir -p $SOCKET_DIR"
sudo -u $USER -H sh -c "mkdir -p $LOG_DIR"

PIDFILE=$PID_DIR/puma.pid


start() {
  cd $APP_DIR;
  sudo -u $USER -H sh -c "echo \$\$ > $PIDFILE; RAILS_ENV=$RAILS_ENV $PUMA_BIN -C $APP_DIR/config/puma.rb -d"
}

stop() {
  echo -n "Stopping puma"
  kill -s SIGTERM `cat $PIDFILE`
}

restart() {
  stop
  start
}

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    restart
    ;;
  *)
    N=/etc/init.d/$NAME
    echo "Usage: $N {start|stop|restart}" >&2
    exit 1
    ;;
esac

これで sudo chmod +x /etc/init.d/puma をして sudo /etc/init.d/puma start でpumaを立ち上げてみましょう。

ps axu|grep pumaで

ubuntu   27220  0.0  1.6 343440 16468 ?        Sl   04:26   0:00 puma 3.7.0 (unix:///home/ubuntu/app/tmp/sockets/puma.sock) [app]
ubuntu   27227  0.1 10.3 830828 105096 ?       Sl   04:26   0:02 puma: cluster worker 0: 27220 [app]
ubuntu   27230  0.1 16.0 892820 162328 ?       Sl   04:26   0:02 puma: cluster worker 1: 27220 [app]

こんな感じで立ち上がっていなかったら失敗です。
tmp, sockets, pids, logsディレクトリが存在しているかと所有者やパーミションが起動ユーザーで問題ないか確認しましょう。

ちなみにですが、bundle.jsの生成など事前に各種ファイルのprecompileはしておきましょう。

nginxのインストールと設定

nginxのインストールは以下のサイトを参考にしました

How To Deploy a Rails App with Puma and Nginx on Ubuntu 14.04 | DigitalOcean

$ sudo apt-get install nginx

続いて設定ファイルの編集

$ sudo vi /etc/nginx/sites-available/default

/etc/nginx/sites-available以下にアプリ用のファイルを新規作成してsites-enabled以下にシンボリックリンクを張ってもよいです。

upstreamのところで先ほどpumaで生成したsocketに接続する設定をしています

upstream app {
    # Path to Unicorn SOCK file, as defined previously
    server unix:/home/ubuntu/app/tmp/sockets/puma.sock fail_timeout=0;
}

server {
    listen  80;
    server_name app.jp;
    keepalive_timeout 300;

    client_max_body_size 4G;

    root /home/ubuntu/app/public;

    try_files $uri/index.html $uri @app;

    location / {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header Host $http_host;
          proxy_set_header X-Forwarded_Proto $scheme;
          proxy_redirect off;
          proxy_pass http://app;
          proxy_read_timeout 300s;
          proxy_send_timeout 300s;
    }

    # You can override error pages by redirecting the requests to a file in your
    # application's public folder, if you so desire:
    error_page 500 502 503 504 /500.html;
    location = /500.html {
            root /home/ubuntu/app/public;
    }
}

あとは sudo /etc/init.d/nginx start で起動し、上記のserver_nameで指定したドメイン(もしくはIP)に接続出来れば成功です。

Deep Learning

# Deep Learning 5

逆誤差伝搬法の数学的お勉強

高卒でもわかる機械学習 (5) 誤差逆伝播法 その1 | 頭の中に思い浮かべた時には

前回見たスライドはスライド48のあたりがイマイチ分からなかったので上記で読み直し
さらに分かりやすいです

逆誤差伝搬法は計算量を如何にして減らすかという事で、N層パーセプトロンがある場合は
N層、N-1層、N-2層と逆順に、重みの微小変化(その層の重みによる勾配法で損失関数を減少させる)をみていきます。k-1層の計算時点でk層の計算結果を利用出来るようになっているとうのがキモ


本のp.148 (5.13)の補足
 \frac{\partial L}{\partial {\bf X}} = \frac{\partial L}{\partial {\bf Y}} \cdot {\bf W}^T

この式の1要素目に着目し、図5-24のX → X・W → Yの1要素目の計算の流れを見てみると

 (x_1, x_2) \cdot (w_{11}, w_{12})^T x_1 w_{11} + x_2 w_{12} y_1

となっていてYへの出力に関しては
 \frac{\partial L}{\partial x_1} = \frac{\partial L}{\partial y_1} \frac{\partial y_1}{\partial x_1} + \frac{\partial L}{\partial y_2} \frac{\partial y_2}{\partial x_1} + \frac{\partial L}{\partial y_3} \frac{\partial y_3}{\partial x_1}

となっていて
 y_1 = x_1 w_{11} + x_2 w_{12}
 y_2 = x_1 w_{21} + x_2 w_{22}
 y_3 = x_1 w_{31} + x_2 w_{32}
を代入すると

 \frac{\partial L}{\partial x_1} = \frac{\partial L}{\partial y_1} w_{11} +  \frac{\partial L}{\partial y_2} w_{21} +  \frac{\partial L}{\partial y_3} w_{31}
 =  ( \frac{\partial L}{\partial y_1},  \frac{\partial L}{\partial y_2},  \frac{\partial L}{\partial y_3}) \cdot (w_{11}, w_{21}, w_{31})
 =  \frac{\partial L}{\partial {\bf Y}} \cdot W^T
と書ける。
W^Tは3行2列中の1列目のみ

これを他の要素( x_2)に関しても導くと 5.13 の上の式が導ける。下の式も同様な感じで導ける