複数データの同時入力
重みパラメータ $W$ を最適化するためには、たくさんのデータ $\boldsymbol{x}$ を入力し、ネットワークから出力値を得て、実測値との差分から損失関数を計算する必要があります。前回記事 で定義した layer()関数は1度に1個のデータしか渡せなかったので、複数データをまとめて入力 できるように layer() を改良してみましょう。
前回記事で扱ったように、上図の $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$ の縦に並んだ成分(入力ベクトル)に対応しています(下図参照)。
ちなみに当サイトではベクトルや行列の成分を 0 番から数えていますが、これは NumPy の配列インデックスに対応させるためです。
それでは、layer()関数を再定義してみましょう。下図にあるように、2次元配列の中に入力ベクトル (1次元配列) を縦積みして、入力総和ベクトルと出力ベクトルも同じ形式で揃えるようにします。
$U=WX$ を転置すると、
\[U^T=(WX)^T=X^TW^T\]
となるので、実装するときは
\[U^T=\begin{bmatrix}x_{00} & x_{10} & x_{20}\\ x_{01} & x_{11} & x_{21}\\x_{02} & x_{12} & x_{22}\end{bmatrix}
\begin{bmatrix}w_{00} & w_{10}\\ w_{01} & w_{11}\\w_{02} & w_{12}\end{bmatrix}\]
の形でデータを処理します。
# Multi_Input
# In[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(xm, wm.T) # 入力総和を計算
if u.ndim == 1:
u = u[:, np.newaxis] # uが1次元配列なら縦ベクトルに変形
return func(u) # 入力総和を活性化関数に通して出力を得る
シグモイド関数 を組込んだ層の出力を確認しておきます。
# In[2]
# [.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 で転置してデータの形状を整えます。次のサンプルコードで例を示します。
# In[3]
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$ のランダムな数値を割り当てることにします。
# In[4]
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 を用いたデータ処理について学んでおくことをおすすめします。
とてもわかりやすい記事をありがとうございます。
複数入力のニューラルネットワークの構造がよくわからず躓いていたところで、とても助かりました。
一つ質問させてください。
入力が複数の場合、出力を一つにすることは可能なのでしょうか?
例として二層のニューラルネットワークを考えます。
入力を4つの時系列データ(10,4)、ニューロンの数を20とします。
一層目 重みw1(20,10)*入力x(10,4)→出力y1(20,4)
二層目 重みw2(1,20)*入力y1(20,4)→出力y2(1,4)
となり、入力と出力が同数になってしまいます。
回答いただけると幸いです。
よろしくお願い致します。
ニューラルネットワークは1つのデータに対して、1つの予測値(数値あるいはベクトル)が対応します。ご質問の例であれば、4つのデータを a, b, c, d とした場合、それぞれ個別にネットワークに入力すると、
データ a を入力 ⇒ データ a に対する予測値
データ b を入力 ⇒ データ b に対する予測値
データ c を入力 ⇒ データ c に対する予測値
データ d を入力 ⇒ データ d に対する予測値
のような結果を得るわけですが、a, b, c, d をまとめて入れることで、このような作業を1回で済ませられます。
a, b, c, d を入力 ⇒ a, b, c, d それぞれに対する予測値
したがって、出力数(予測値の数)は入力数と同じく4つとなるはずです。
下記は誤植と思われますので、ご確認ください。
In[1] プログラムの上の文で、X=[x0, x1, x2] → X=[x0 x1 x2]