PycallとNumPyで3層ニューラルネットワーク

O'Reilly Japan - ゼロから作るDeep Learning P.58ページからを参考に、フォワード処理を実装。

utils.rb

require 'bundler'
Bundler.require

require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: :np

def identity_function(x)
  x 
end

def sigmoid(x)
  1 / (1 + np.exp.(-1 * x))
end

-xが以下のエラーで動かなかったので-1 * xにした。

~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/pycall-0.1.0.alpha.20170317/lib/pycall/pyobject_wrapper.rb:174:in `method_missing': undefined method `-@' for array([ 0.3,  0.7,  1.1]):PyCall::PyObject (NoMethodError)
Did you mean?  -

neuralnet.rb

require 'bundler'
Bundler.require

require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: :np

require './utils'

def init_network
  network = {}
  network[:W1] = np.array.([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
  network[:b1] = np.array.([0.1, 0.2, 0.3])
  network[:W2] = np.array.([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
  network[:b2] = np.array.([0.1, 0.2])
  network[:W3] = np.array.([[0.1, 0.3], [0.2, 0.4]])
  network[:b3] = np.array.([0.1, 0.2])
  network
end

def forward(network, x)
  w1, w2, w3 = network[:W1], network[:W2], network[:W3]
  b1, b2, b3 = network[:b1], network[:b2], network[:b3]

  a1 = np.dot.(x, w1) + b1
  z1 = sigmoid(a1)
  a2 = np.dot.(z1, w2) + b2
  z2 = sigmoid(a2)
  a3 = np.dot.(z2, w3) + b3
  y = identity_function(a3)

  y
end

network = init_network
x = np.array.([1.0, 0.5])
y = forward(network, x)
p y # array([ 0.31682708,  0.69627909])

普通に動いて楽しい。

ソース

https://github.com/tnantoka/hello-pycall

PyCallでMatplotlibを使う

O'Reilly Japan - ゼロから作るDeep LearningのP.17ページにある単純なグラフ。

plot.rb

require 'bundler'
Bundler.require

require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: :np
pyimport 'matplotlib', as: :mp

pyimport 'matplotlib.pyplot', as: :plt

x = np.arange.(0, 6, 0.1)
y = np.sin.(x)

plt.plot.(x, y)
plt.show.()
$ bundle exec ruby plot.rb 

動いた!

参考

ソース

https://github.com/tnantoka/hello-pycall

PyCallでNumPyを使う

O'Reilly Japan - ゼロから作るDeep LearningのP.12辺りのサンプルが動くか試してみる。

PyCallのインストール

$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]

$ bundle init

# Gemfile
gem 'pycall'

$ bundle

numpy.rb

require 'bundler'
Bundler.require

require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: :np

x = np.array.([1.0, 2.0, 3.0])
y = np.array.([2.0, 4.0, 6.0])
p x + y # array([ 3.,  6.,  9.])

A = np.array.([[1, 2], [3, 4]])
p A # array([[1, 2],
    #        [3, 4]])
p A.shape # (2, 2)
p A.dtype # dtype('int64')

B = np.array.([[3, 0], [0, 6]])
p A + B # array([[ 4,  2],
        #        j[ 3, 10]])
$ bundle exec ruby numpy.rb 

普通に動いた!

参考

ソース

https://github.com/tnantoka/hello-pycall

PyCallをDockerで

Rubyist Magazine - PyCall があれば Ruby で機械学習ができる を呼んでいたら、Jupyter Notebookが簡単に触れるDocker環境が用意されているとういうことだったので試してみました。

$ docker --version
Docker version 17.03.0-ce, build 60ccb22

$ docker run -p 8888:8888 -it --rm --name iruby -v ~:/notebooks/local rubydata/pycall

何事も無く動きました。

[読書メモ] ゼロから作るDeep Learning: 第4章

O'Reilly Japan - ゼロから作るDeep Learning

学習

  • パラメータを自動で
    • 膨大な数な数。手作業でやるのは無理
  • 損失関数
    • 学習の指標
    • これが一番強い小さくなるパラメータを探す
  • パーセプトロン
    • 線形分離可能な問題は自動で学習可。収束定理。
    • 非線形分離問題は自動学習不可
  • データ駆動
    • 人の介入を避ける
    • ディープラーニングはそれがやりやすい手法
  • 5を識別する
    • 特徴量+機械学習のアプローチでは、特徴量を人が設計する
    • ディープラーニング(ニューラルネットワーク)ではそこも自動で
    • end to end machine learning
  • 過学習
    • 特定のデータセットにしか対応できない
    • 汎化能力がない
    • 訓練(教師)データとテストデータを分ける

損失関数

  • 任意の関数使える
    • 二乗和誤差や交差エントロピー誤差がよく使われる
  • 2乗和誤差
    • 出力と教師データの各要素の差を二乗してその総和を求める
  • 交差エントロピー誤差
    • 教師データを01のone hot表現にすれば出力結果のlog、自然対数を求めるだけになる
    • log(0)にならないように極小の値deltaを足す
  • ミニバッチ学習
    • 全訓練データを対象に損失関数を求めたい
    • 数が多いと無理
    • 無作為に一部を抽出してそれを対象に学習
    • 正確ではないが、全体の近似として扱う
  • バッチ版交差エントロピー
    • tが0のものは0になるので無視できる
    • 正確に対する出力だけ取ってくればよい
    • あとはそれを平均するだけ
  • なぜ損失関数が必要か
    • 認識精度を指標にすればいいのでは
    • 微分(勾配)を参考にパラメータを調整していく
    • 認識精度の微分は0になるので使えない
    • 認識精度はパラメータの微小な変化では変わらないステップ関数と同じ。
    • 損失関数ならパラメータを変えると連続的に変化するので、それを元にパラメータ調整できる

数値微分

  • ある瞬間の変化
  • 10分間に2km、1分間に0.2km…時間hを0に近づける
  • 小さすぎる値を使うを丸め誤差がおこる
  • 中心差分
    • f(x + h) - f(x)は前方差分。誤差が大きい
    • x(f+h) - x(f-h)を使う
  • 数式で解析的に解いた真の微分とは誤差があるがほとんど同じ値が得られる
  • 偏微分
    • 複数の変数からなる関数の微分
    • 片方の変数を固定してもう片方の変数を微分する
  • 勾配(gradient)
    • 全ての変数の偏微分をベクトルにまとめる
    • 各地点において関数の値を最も減らす方向を向いたベクトルになる(関数の最小値とは限らない)
  • 勾配法
    • 勾配を使って損失関数が小さくなるパラメータを探す
    • 勾配は最小値以外でも0になる。極小値や鞍点、プラトー。
    • 勾配方向への移動を繰り返して関数の値を減らしていく。勾配降下法。
    • 学習率。1回の移動量。正しく学習でいる値になっているか確認する必要がある。
    • ハイパーパラメータ。重みやバイアスのように自動で獲得されず、学習率のように人の手で設定するもの。

学習アルゴリズムの実装

  • SGD
    • 確率的勾配降下法
    • ミニバッチ × 勾配法
  • 前章のフォワード(推論)処理の実装と共通部分が多い
  • 1エポックごとにテストデータで評価
    • 10000個のデータを100個のミニバッチで学習するなら勾配法100回で全部見たことになるので100回がエポック