複数データの同時入力

複数データの同時入力

複数データの同時入力

 重みパラメータ $W$ を最適化するためには、たくさんのデータ $\boldsymbol{x}$ を入力し、ネットワークから出力値を得て、実測値との差分から損失関数を計算する必要があります。前回記事 で定義した layer()関数は1度に1個のデータしか渡せなかったので、複数データをまとめて入力 できるように layer() を改良してみましょう。

 ミニサイズのニューラルネットワーク(Artificial Neural Network) 改訂版

 前回記事で扱ったように、上図の $b$ 層への入力総和は
 
\[\boldsymbol{u}=W \boldsymbol{x}
=\begin{bmatrix}w_{00} & w_{01} & w_{02}\\
w_{10} & w_{11} & w_{12}\end{bmatrix}
\begin{bmatrix}x_0\\x_1\\x_2\end{bmatrix}\]
で表されます。$x_2$ は常に $1$ の値をとるダミー入力です。行列の演算規則から、$w_{02}$ と $w_{12}$ はバイアスにかかる重みとなります。ネットワークに $3$ 個のデータ
 
\[\boldsymbol{x_0}=\begin{bmatrix}x_{00}\\x_{10}\\x_{20}\end{bmatrix},\quad
\boldsymbol{x_1}=\begin{bmatrix}x_{01}\\x_{11}\\x_{21}\end{bmatrix},\quad
\boldsymbol{x_2}=\begin{bmatrix}x_{02}\\x_{12}\\x_{22}\end{bmatrix}\]
をまとめて入力することを考えます。$\boldsymbol{x_0},\ \boldsymbol{x_1},\ \boldsymbol{x_2}$ を横に並べた行列
 
\[X=[\boldsymbol{x_0},\ \boldsymbol{x_1},\ \boldsymbol{x_2}]=\begin{bmatrix}
x_{00} & x_{01} & x_{02}\\
x_{10} & x_{11} & x_{12}\\
x_{20} & x_{21} & x_{22}\end{bmatrix}\]
を定義すると、行列演算の性質から、それぞれのデータに対応する入力総和は $U=WX$ で計算することができます(行列はベクトルを並べたもの考えることができます)。
 
\[U=\begin{bmatrix}
u_{00} & u_{01} & u_{02}\\
u_{10} & u_{11} & u_{12}\end{bmatrix}
=\begin{bmatrix}
w_{00} & w_{01} & w_{02}\\
w_{10} & w_{11} & w_{12}\end{bmatrix}
\begin{bmatrix}
x_{00} & x_{01} & x_{02}\\
x_{10} & x_{11} & x_{12}\\
x_{20} & x_{21} & x_{22}\end{bmatrix}\]
 行列 $U$ の縦に並んだ成分(入力総和ベクトル)は、行列 $X$ の縦に並んだ成分(入力ベクトル)に対応しています(下図参照)。

 Python 入力行列(input matrix)と入力総和行列(input sum matrix)対応図

 ちなみに当サイトではベクトルや行列の成分を 0 番から数えていますが、これは NumPy の配列インデックスに対応させるためです。

 それでは、layer()関数を再定義してみましょう。下図にあるように、2次元配列の中に入力ベクトル (1次元配列) を縦積みして、入力総和ベクトルと出力ベクトルも同じ形式で揃えるようにします。

 Python データの表示形式を揃える

# リストM10-A-1

import numpy as np

# 恒等関数
def identity(x):
    return x

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

# ニューラルネットワークの層
def layer(xm, wm, func = identity):
    dummy = np.ones((xm.shape[0], 1))  # ダミー入力
    xm = np.append(xm, dummy, axis = 1)  # 入力ベクトルにダミー入力を追加
    u = np.dot(wm, xm.T).T  # 入力総和を計算
    if u.ndim == 1:
        u = u[:, np.newaxis]  # uが1次元配列なら縦ベクトルに変形
    return func(u)  # 入力総和を活性化関数に通して出力を得る

 シグモイド関数を組込んだ層の出力を確認しておきます。

# リストM10-A-1-(1)

# [.1 .2],[.3 .4],[.5 .6]を同時入力
X = np.array([[.1, .2],
              [.3, .4],
              [.5, .6]])

# 重み行列を設定
W = np.array([[0.5, -1.0,  1.5],
              [2.0,  3.0, -1.5]])

# Xを入力して出力を得る
Y = layer(X, W, sigmoid)
Y = np.round(Y, 3)

print(Y)
[[0.794 0.332]
 [0.777 0.574]
 [0.76  0.786]]

 

データの形状変更

 機械学習を実践する場合、色々な形状の入力データを取り込む必要が生じます。たとえば、以前の記事で扱った気温と湿度のデータのように、$x_0$ と $x_1$ が別々の 1次元配列に収められていることもよくあります。このようなデータを layer()関数に取り込む場合、numpy.vstack() などで 2つの配列を縦軸 (axis=0) 方向に連結してから、.T で転置してデータの形状を整えます。次のサンプルコードで例を示します。

# リストM10-A-1-(2)

np.random.seed(10)

# x0=[0.77 0.02 0.63 0.75 0.5]
x0 = np.random.rand(5)
x0 = np.round(x0, 2)

# x1=[0.22 0.2  0.76 0.17 0.09]
x1 = np.random.rand(5)
x1 = np.round(x1, 2)

my_data = np.vstack((x0, x1)).T
print(my_data)
[[0.77 0.22]
 [0.02 0.2 ]
 [0.63 0.76]
 [0.75 0.17]
 [0.5  0.09]]

 このデータを layer() に通してみましょう。重み行列の各要素には、np.random.rand() を使って $-1$ ~ $1$ のランダムな数値を割り当てることにします(実践においても、重みは調整するパラメータなので、初期値は適当で構いません)。

# リストM10-A-1-(3)

np.random.seed(10)

# 重み行列を設定
W = np.random.uniform(-1, 1, size = (2, 3))

# 層にmy_dataとWを渡して出力を得る
Y = layer(my_data, W, func = sigmoid)
Y = np.round(Y, 3)

print(Y)
[[0.616 0.458]
 [0.522 0.368]
 [0.47  0.44 ]
 [0.625 0.456]
 [0.611 0.425]]

 実際問題として、機械学習ではデータの形状を自在に変更できるスキルを高い水準で求められるので、機械学習と並行して NumPy や pandas を用いたデータ処理について学んでおくことをおすすめします。