ニューラルネットワークを理解する #7 「ニューラルネットワークを作ってみよう」

 では早速作ってみましょうか。

Pythonを使って実装していきます。


アーキテクチャ

 

ハイパーパラメータ

入力ニューロン数

784

隠れ層の数

1

隠れ層のニューロン数

50

出力ニューロン数

10

隠れ層の活性化関数

シグモイド関数

出力層の活性化関数

ソフトマックス関数

損失関数

交差エントロピー誤差


実際に解く問題

よくチュートリアルで使用される手書き文字のデータセットを使って、画像に移っている数字を識別するモデルを作成してみましょう!



データのロード

TensorFlowからデータをロードしましょう。(ない人はインストールしてね)
訓練用とテスト用に分割しておきます。

import tensorflow as tf
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

実際に中身を見てみると0~256の数値の2次元配列になっていることが分かります。(8bitグレースケール: 白「0」~黒「255」の256段階)
正解ラベルの方は正解の数値になっていますね。

データの前処理

前処理についてはあまり触れてませんでしたが、機械学習では必ず行われるものです。
機械学習ではデータが命なのでいかにきれいにするかが肝になってきます。
今回のデータはそもそも結構きれいなので、ニューラルネットワークに都合がよいように変換します。
具体的には以下のような処理を施します。
  • 訓練データを0~1の間に押し込める(ニューラルネットワークでは0~1の数値が前提とされる)
  • データを1次元に変換
  • 正解ラベルをワンホットエンコーディング

では早速やっていきましょう


# 1次元に変換
x_train_fla=np.array([x.flatten() for x in x_train])
x_test_fla=np.array([x.flatten() for x in x_test])

x_train_fla.shape,x_train.shape
# ((60000, 784), (60000, 28, 28))

28かける28だった配列が1次元になっていますね。


# 0~1へ変換
x_train_fla_norm=x_train_fla/255.0
x_test_fla_norm=x_test_fla/255.0

x_train_fla_norm[0]
# こんな感じ [0.        , 0.        , 0.01176471, 0.07058824, 0.07058824,]


# One-Hot
num_classes = 10
y_train_one_hot = np.eye(num_classes)[y_train]
y_test_one_hot = np.eye(num_classes)[y_test]

y_train_one_hot[0],y_train[0]
# (array([0., 0., 0., 0., 0., 1., 0., 0., 0., 0.]), 5)

これで前処理は終了です。

ニューラルネットワークの実装

まずはネットワークの重みとバイアスの初期化です。
重みは0以外のランダムな小さな値を振り分けます。

# 実装
def init_network(input_size, hidden_size, output_size, weight_init_std=0.01):
  # 重みの初期化
  network={}
  network['params']={}
  network['params']['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
  network['params']['b1'] = np.zeros(hidden_size)
  network['params']['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
  network['params']['b2'] = np.zeros(output_size)

  return network

network=init_network(784, 50, 10, weight_init_std=0.01)

次に、予測の関数を作成しましょう。
これが実際のニューラルネットワークの計算を行う部分です。
隠れ層の活性化関数にはシグモイド関数を、出力層にはソフトマックス関数を設定しましょう。

# シグモイド関数
def sigmoid(x):
  return 1 / (1+np.exp(-x))

# softmax
def softmax(a):
  c=np.max(a)
  exp_a=np.exp(a-c)
  sum_exp_a=np.sum(exp_a)
  y= exp_a/sum_exp_a
  return y

def predict(network, x):
  W1, W2 = network['params']['W1'], network['params']['W2']
  b1, b2 = network['params']['b1'], network['params']['b2']

  a1 = np.dot(x, W1) + b1
  z1 = sigmoid(a1)
  a2 = np.dot(z1, W2) + b2
  y = softmax(a2)
  
  return y

次は損失関数を実装しましょう。
交差エントロピー誤差を設定ましょう。

# 交差エントロピー誤差
def cross_entropy_error(y,t):
  delta=1e-7
  return -np.sum(t*np.log(y+delta))

def loss(network, x, t):
  y = predict(network, x)
  return cross_entropy_error(y, t)

次に勾配を計算する関数を作成しましょう。
ここまでで説明してきた項目が出そろってきましたね。

def numerical_gradient(f, x):
  h = 1e-4 # 0.0001
  grad = np.zeros_like(x)
  
  it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
  while not it.finished:
      idx = it.multi_index
      tmp_val = x[idx]
      x[idx] = tmp_val + h
      fxh1 = f(x) # f(x+h)
      
      x[idx] = tmp_val - h 
      fxh2 = f(x) # f(x-h)
      grad[idx] = (fxh1 - fxh2) / (2*h)
      
      x[idx] = tmp_val # 値を元に戻す
      it.iternext()   
      
  return grad

def numerical_gradient_calc(network, x, t):
  loss_W = lambda W: loss(network,x, t)
  grads = {}
  grads['W1'] = numerical_gradient(loss_W, network['params']['W1'])
  grads['b1'] = numerical_gradient(loss_W, network['params']['b1'])
  grads['W2'] = numerical_gradient(loss_W, network['params']['W2'])
  grads['b2'] = numerical_gradient(loss_W, network['params']['b2'])
  
  return grads

長くなってきたので次回、実際に学習させていきましょう!

コメント