人工ニューラルネットワーク

当サイトではアフィリエイトプログラムを利用して商品を紹介しています。

ニューラルネットワークの構造

生物のニューラルネットワークをコンピュータ上に模倣したモデルを人工ニューラルネットワーク(Artificial Neural Network)とよびます。機械学習の分野では「人工」を省略するのが一般的なので、当講座でも以降は単に ニューラルネットワーク(Neural Network)と記述します。

ニューラルネットワークは ニューロン を入力層、中間層(隠れ層)、出力層の順に層状に並べて構築します。
 
ニューラルネットワーク(Artificial Neural Network)
入力層は信号をニューラルネットワークに送ります。出力層はニューラルネットワークから信号を出力します。入力層と出力層の間には複数の中間層(隠れ層)を並べます。そして、入力層から出力層まで、第0層、第1層、第2層 … というように順序づけることにします(入力層を第1層とする方式もありますが、当サイトでは中間層から第1層と数える方式を採用します)。また、入力信号が上から下へと流れていくイメージで、ある層から見て入力層に近い層を上の層、出力層に近い層を下の層とよぶことにします。

ある1つのニューロンからの信号は、次の層のすべてのニューロンに重みをつけて渡されます。同じ層にあるニューロン同士では信号のやりとりは行われません。
 
ニューラルネットワークから 2つの層を選んで、上の層を $a$ 層、下の層を $b$ 層とします。簡単のために、$a$ 層・$b$ 層ともに 2個のニューロンで構成されているとします。
 
ミニサイズのニューラルネットワーク(Artificial Neural Network) 改訂版
$x_0,\ x_1$ は $b$ 層が $a$ 層から受け取る入力です。$x_2$ はダミー入力(疑似信号)で、常に 1 の値をとります。$w_{ji}$ は $b$ 層の $j$ 番目のニューロンが $a$ 層の $i$ 番目のニューロンから受け取る信号に掛かる重みを表しています(下図参照)。
 
Python 重みパラメータ行列(改訂版)
たとえば、$w_{01}$ は「$b$ 層の 0 番目のニューロンが $a$ 層の 1 番目のニューロンから受け取る信号に掛かる重み」を表しています。

$u_j$ は $b$ 層の $j$ 番目のニューロンの入力総和を表します。
たとえば、$u_0$ は $b$ 層の 0 番目のニューロンの入力総和であり、$a$ 層のすべてのニューロンからの入力とダミー入力の線形結合で表されます。
 \[u_0=w_{00}x_0+w_{01}x_1+w_{02}x_2\tag{1}\]
同様に $u_1$ は $b$ 層の 1 番目のニューロンの入力総和です。
 \[u_1=w_{10}x_0+w_{11}x_1+w_{12}x_2\tag{2}\]
(1) と (2) は行列形式で
 \[\begin{bmatrix}u_0\\u_1\end{bmatrix}=\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}\tag{3}\]
のようにまとめて表せます。ここで行列 $W$ とベクトル $\boldsymbol{u}$ および $\boldsymbol{x}$ を
 \[W=\begin{bmatrix}w_{00} & w_{01} & w_{02}\\ w_{10} & w_{11} & w_{12}\end{bmatrix},\quad
\boldsymbol{u}=\begin{bmatrix}u_0\\u_1\end{bmatrix},\quad\boldsymbol{x}=\begin{bmatrix}x_0\\x_1\\x_2\end{bmatrix}\tag{4}\]
によって定義すると、$b$ 層の入力総和は
 \[\boldsymbol{u}=W\boldsymbol{x}\tag{5}\]
と表すことができます。これを活性化関数 $f(u)$ に通して、この層のそれぞれのニューロンからの出力値
 \[\boldsymbol{y}=f(\boldsymbol{u})=f(W\boldsymbol{x})\tag{6}\]
を得ることができます。このように行列を使うと、各層のニューロンをまとめて1つのユニットとして扱うことができます(実装する際にはニューロンごとにではなく、層ごとに関数を定義します)。
Python で小さなニューラルネットワークを作ってみましょう。
最初にネットワークの層の機能をもつ関数を定義しておきます。

# Neural_Network

# In[1]

import numpy as np

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

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

# 層関数
def layer(xv, wm, func=identity):
    xv = np.append(xv, 1)  # 入力信号にダミーを追加
    u = np.dot(wm, xv)  # 入力総和
    return func(u)  # 活性化関数に入力総和を渡す

layer()は入力信号 xv と重み行列 wm、関数オブジェクト(活性化関数)func を受け取って、出力値を返します。func にはデフォルトで恒等関数オブジェクト identity が指定されているので、このオプション引数を省略すると、入力総和 u をそのまま出力することになります。また、layer() は自動的にダミー入力を組み込むので、xv にダミー入力を加えた配列を渡す必要はありません。
 
入力層 2 ユニット、中間層 2 ユニット、出力層 1 ユニットを備えた次のようなモデルを考えます。

ニューラルネットワークの簡単なモデル
灰色のユニットはダミー信号 $1$ を出力します。中間層と出力層を区別するために、中間層に入る信号の重み、入力総和の肩に $(1)$ を添えます。また、中間層の $k$ 番目のユニットからの出力値を $x_{k}^{(1)}$ と表すことにします。同様に、出力層に入る信号の重み、入力総和の肩に $(2)$ を添え、出力層の $k$ 番目のユニットからの出力値を $x_{k}^{(2)}$ と表します。

中間層の各ユニットに入ってくる入力総和は先ほどと同じく
 \[\begin{bmatrix}u_0^{(1)}\\u_1^{(1)}\end{bmatrix}=\begin{bmatrix}w_{00}^{(1)} & w_{01}^{(1)} & w_{02}^{(1)}\\
w_{10}^{(1)} & w_{11}^{(1)} & w_{12}^{(1)}\end{bmatrix}\begin{bmatrix}x_0\\x_1\\x_2\end{bmatrix}\tag{7}\]
と表せます。行列 $W^{(1)}$ とベクトル $\boldsymbol{u}^{(1)}$ および $\boldsymbol{x}$ を
 \[W^{1}=\begin{bmatrix}w_{00}^{(1)} & w_{01}^{(1)} & w_{02}^{(1)}\\ w_{10}^{(1)} & w_{11}^{(1)} & w_{12}^{(1)}\end{bmatrix},\quad
\boldsymbol{u}^{(1)}=\begin{bmatrix}u_0^{(1)}\\u_1^{(1)}\end{bmatrix},\quad\boldsymbol{x}=\begin{bmatrix}x_0\\x_1\\x_2\end{bmatrix}\tag{8}\]
によって定義すると、$b$ 層の入力総和は
 \[\boldsymbol{u}^{(1)}=W^{(1)}\boldsymbol{x}\tag{9}\]
となります。中間層の各ユニットから出力される値は、
 \[\boldsymbol{x}^{(1)}=f(\boldsymbol{u}^{(1)})=f(W^{(1)}\boldsymbol{x})\tag{10}\]
となります。

出力層は 1 ユニットだけなので、中間層から出力層に入ってくる入力総和は
 \[u_{0}^{(2)}=w_{00}^{(2)}\ x_{0}^{(1)}+w_{01}^{(2)}\ x_{1}^{(1)}+w_{02}^{(2)}\ x_{2}^{(1)}\tag{11}\]
のようにスカラーとなりますが、これも重みベクトル
 \[W^{(2)}=[w_{00}^{(2)}\quad w_{01}^{(2)}\quad w_{02}^{(2)}]\tag{12}\]
と、中間層からの出力ベクトル
 \[\boldsymbol{x}^{(1)}=\begin{bmatrix}x_0^{(1)}\\x_1^{(1)}\\x_2^{(1)}\end{bmatrix}\tag{13}\]
の内積
 \[u_{0}^{(2)}=W^{(2)}\boldsymbol{x}^{(1)}\tag{14}\]
で表せます。行列形式に合わせて $W^{{2}}$ は行ベクトルで表記しています。出力層から出力される値は、
 \[\boldsymbol{x}^{(2)}=f(\boldsymbol{u}^{(2)})=f(W^{(2)}\boldsymbol{x}^{(1)})\tag{15}\]
以上の準備をもとに、適当な入力信号と重み行列を設定して、ネットワークからの出力を確認してみましょう。入力層からの信号にかかる重み行列 $W^{(1)}$、入力層からの信号にかかる重み行列 $W^{(2)}$ をそれぞれ変数 wm1、wm2 に格納します。入力層、中間層、出力層からの信号を、それぞれ変数 xv0、xv1、xv2 に格納します。
 
適当な入力信号と重み行列を設定して、ネットワークからの出力を確認してみましょう。

# In[2]

# 中間層への入力の重み行列
wm1 = np.array([[1.0, 2.0, 1.5],
               [1.5, 1.0, 1.0]])

# 出力層への入力の重み行列
wm2 = np.array([1.0, -0.5, 1.5])

# 中間層への入力信号
xv0 = np.array([1.0, -0.5])

# 出力層への入力信号
xv1 = layer(xv0, wm1, sigmoid)

# ニューラルネットワークからの出力ベクトル
xv2 = layer(xv1, wm2)
xv2 = np.round(xv2, 5)

print("ニューラルネットワークからの出力値 {}".format(xv2))
# ニューラルネットワークからの出力値 1.87718

前回と同じように、$x_0$-$x_1$ 平面上にニューラルネットワークの出力値をプロットしてみましょう。

# In[3]

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 解像度の設定
xn = 33

# x0-x1平面の格子点を作成
xv0_1 = np.linspace(-8.0, 8.0, xn)
xv0_2 = np.linspace(-8.0, 8.0, xn)
X0, X1 = np.meshgrid(xv0_1, xv0_2)

# 中間層への入力の重み
wm1 = np.array([[3.0, 2.0,  2.0],
               [2.0, 3.0, -2.0]])

# 出力層への入力の重み
wm2 = np.array([1.0, 1.0, 0.1])

# ニューラルネットワークの出力を格納する変数
Z = np.zeros((xn, xn))

# ニューラルネットワークからの出力
for i in range(xn):
    for j in range(xn):
        xv0 = np.array([xv0_1[i], xv0_2[j]])
        xv1 = layer(xv0, wm1, sigmoid)
        Z[j][i] = layer(xv1, wm2)

# 曲面を描画
fig = plt.figure(figsize = (10, 6))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel("x0", size=16, labelpad=10)
ax.set_ylabel("x1", size=16, labelpad=10)
ax.set_zlabel("output", size=16)
ax.plot_surface(X0, X1, Z, color="green")
plt.show()

ニューラルネットワーク(Artificial Neural Network)出力3Dプロット
単体ニューロンに比べると複雑な出力分布になっていますね。ニューラルネットワークの中間層を増やしていけば、表現力はさらに向上し、より柔軟なモデルを構築できるようになります。

 

コメント

  1. HNaito より:

    下記は誤植と思われますので、ご確認ください。
    In[1] プログラムで、def layer(xv, wv, func = identify): → def layer(xv, wm, func = identify):

  2. HNaito より:

    In[2] プログラムの上の図に入力と出力の線を追加して、
    x0, x1,(入力) wm1_00, wm1_01, ,wm1_10, wm1_11, wm2_00, wm2_01, xv0, xv1, xv2,
    identity, sigmoid, identity, u0, u1(隠れ層の入力総和)
    を自分で書き込んで、プログラムに沿ってlayer( )では入力ベクトルに 1 を追加して、更にそれに対する重み ( wm1_02, wm1_12 あるいは wm2_02 ) も追加して、何とか 4 本の行列を使った式を書き下せました。

    • あとりえこばと より:

      申し訳ないです。
      In[2] で実装するモデルの説明が不足していました。
      詳細な説明文を加筆して、図も差し替えておきました。

  3. あとりえこばと より:

    【お知らせ】
     数式に式番号を付けました。
     また、(5) 式の具体的な表式として (3) 式を付け加えました。