敢えてRubyで学ぶ「ゼロから作るDeep Learning」第4章 偏微分での二層ニューラルネット

CPUがガリガリ回る計算が遅い偏微分での二層ニューラルネットを実装。次回は誤差伝搬法で高速実装の予定

### [基本関数](https://qiita.com/michihito_t/items/681f898b0fb2412c2ff7)

### [数値偏微分](https://qiita.com/michihito_t/items/581113d69ee5500f324b)

### 二層のニューラルネット

```ruby:two_layer_neuralnet.rb
require 'numo/narray'
require './functions'
require './numerical_gradient'

class TwoLayerNeuralNet
  # 初期化
  def initialize(input_size, hidden_size, output_size, weight_init_std = 0.01)
    @params = {}
    @params['w1'] = weight_init_std * Numo::DFloat.new(input_size, hidden_size).rand_norm
    @params['b1'] = Numo::DFloat.zeros(hidden_size)
    @params['w2'] = weight_init_std * Numo::DFloat.new(hidden_size, output_size).rand_norm
    @params['b2'] = Numo::DFloat.zeros(output_size)
  end

  # self.paramsで参照できるように
  def params
    @params
  end

  # ニューラルネットの行列計算
  def predict(x)
    w1, w2 = self.params['w1'], self.params['w2']
    b1, b2 = self.params['b1'], self.params['b2']

    a1 = x.dot(w1) + b1
    z1 = sigmoid(a1)
    a2 = z1.dot(w2) + b2
    softmax(a2)
  end

  # 誤差伝搬関数
  def loss(x, t)
    y = self.predict(x)
    cross_entropy_error(y, t)
  end

  # 精度計算
  def accuracy(x, t)
    y = predict(x)
    y = y.max_index(1) % 10

    y.eq(t).cast_to(Numo::UInt32).sum / y.shape[0].to_f
  end

  # 該当の数値微分、最適化
  def numerical_gradients(x,t)
    loss_w = lambda {|w| loss(x, t) }

    grads = {}
    grads['w1'] = numerical_gradient(loss_w, self.params['w1'])
    grads['b1'] = numerical_gradient(loss_w, self.params['b1'])
    grads['w2'] = numerical_gradient(loss_w, self.params['w2'])
    grads['b2'] = numerical_gradient(loss_w, self.params['b2'])

    grads
  end
end
```

### 実行関数

```ruby:exec_numerical_gradent_neuralnet.rb
require 'numo/narray'
require './functions'
require './numerical_gradient'
require './two_layer_neural_net'
require 'datasets'
require 'mini_magick'

train_size = 60000
test_size = 10000
batch_size = 100
iters_num = 10000
learning_rate = 0.1

train = Datasets::MNIST.new(type: :train)
x_train = Numo::NArray.concatenate(train.map{|t| t.pixels }).reshape(train_size,784)
t_train = Numo::NArray.concatenate(train.map{|t| t.label })
test = Datasets::MNIST.new(type: :test)
x_test = Numo::NArray.concatenate(test.map{|t| t.pixels }).reshape(test_size,784)
t_test = Numo::NArray.concatenate(test.map{|t| t.label })

train_loss_list = []
train_acc_list = []
test_acc_list = []
keys = ['w1', 'b1', 'w2', 'b2']

iter_per_epoch = [train_size / batch_size,1].max

network = TwoLayerNeuralNet.new(input_size=784, hidden_size=50, output_size=10)

iters_num.times do |i|
  batch_mask = Array.new(batch_size){ rand test_size }
  x_batch = x_train[batch_mask, true]
  t_batch = t_train[batch_mask]

  grad = network.numerical_gradients(x_batch, t_batch)

  keys.each do |key|
    network.params[key] -= learning_rate * grad[key]
  end

  loss = network.loss(x_batch, t_batch)
  train_loss_list.append(loss)

  if i % iter_per_epoch == 0
    train_acc = network.accuracy(x_train, t_train)
    test_acc = network.accuracy(x_test, t_test)
    train_acc_list.append(train_acc)
    test_acc_list.append(test_acc)
    p "train acc, test acc | " + train_acc.to_s + ", " + test_acc.to_s
  end
end
```