In [3]:
import matplotlib.pyplot as plt
from matplotlib.image import imread
from graphviz import Digraph
f = Digraph(format="png")
f.attr(rankdir='LR')
f.attr('node', shape='circle')
f.node('x1','')
f.node('x2','')
f.node('s1','')
f.node('s2','')
f.node('s3','')
f.node('y1','')
f.node('y2','')
with f.subgraph(name='cluster_1') as c:
c.node('x1')
c.node('x2')
c.attr(color='white')
c.attr(label='入力層')
with f.subgraph(name='cluster_2') as c:
c.node('s1')
c.node('s2')
c.node('s3')
c.attr(color='white')
c.attr(label='中間層')
with f.subgraph(name='cluster_3') as c:
c.node('y1')
c.node('y2')
c.attr(color='white')
c.attr(label='出力層')
f.edge('x1', 's1', len='1.00')
f.edge('x1', 's2', len='10')
f.edge('x1', 's3', len='10')
f.edge('x2', 's1', len='10')
f.edge('x2', 's2', len='10')
f.edge('x2', 's3', len='10')
f.edge('s1', 'y1', len='10')
f.edge('s2', 'y1', len='10')
f.edge('s3', 'y1', len='10')
f.edge('s1', 'y2', len='10')
f.edge('s2', 'y2', len='10')
f.edge('s3', 'y2', len='10')
f.render("../docs/neural_network")
img = imread('../docs/neural_network.png')
plt.figure(figsize=(6,4))
plt.imshow(img)
ax = plt.gca() # get current axis
# 枠線非表示
#ax.spines["right"].set_color("none") # 右消し
#ax.spines["left"].set_color("none") # 左消し
#ax.spines["top"].set_color("none") # 上消し
#ax.spines["bottom"].set_color("none") # 下消し
## 目盛を非表示にする
ax.tick_params(axis='x', which='both', top='off', bottom='off', labelbottom='off')
ax.tick_params(axis='y', which='both', left='off', right='off', labelleft='off')
plt.show()
パーセプトロンは以下式で表すことができた。
$$ y = \begin{cases} & \ 0 \; (b + w_{1}x_{1} + w_{1}x_{2} \leq 0) \\ & \ 1 \; (b + w_{1}x_{1} + w_{1}x_{2} > 0) \end{cases} $$バイアスはニューロンの発火のしやすさ、重みは各信号の重要性をコントロールしている。 上記の式を簡略化すると以下2つの式となる。
$$ y = h(b + w_{1}x_{1} + w_{1}x_{2}) $$$$ h(x) = \begin{cases} & \ 0 \; (x \leq 0) \\ & \ 1 \; (x > 0) \end{cases} $$入力信号の総和がh(x)という関数で変換されて出力yとなる。
In [4]:
import matplotlib.pyplot as plt
from matplotlib.image import imread
from graphviz import Digraph
f = Digraph(format="png")
f.attr(rankdir='LR')
f.attr('node', shape='circle')
with f.subgraph(name='cluster_0') as c:
c.edge('a', 'y', label='h()')
c.attr(style='rounded')
f.edge('1', 'a', label='b')
f.edge('x1', 'a', label='w1')
f.edge('x2', 'a', label='w2')
f.render("../docs/activation_function")
img = imread('../docs/activation_function.png')
plt.figure(figsize=(6,4))
plt.imshow(img)
ax = plt.gca() # get current axis
# 枠線非表示
#ax.spines["right"].set_color("none") # 右消し
#ax.spines["left"].set_color("none") # 左消し
#ax.spines["top"].set_color("none") # 上消し
#ax.spines["bottom"].set_color("none") # 下消し
## 目盛を非表示にする
ax.tick_params(axis='x', which='both', top='off', bottom='off', labelbottom='off')
ax.tick_params(axis='y', which='both', left='off', right='off', labelleft='off')
plt.show()
In [5]:
# xは実数のみ
import numpy as np
def step_function_from_num(x):
if x > 0:
return 1
else:
return 0
# 配列対応の場合
def step_function_from_array(x):
y = x > 0
return y.astype(np.int)
print(step_function_from_array(np.array([-0.1, 0, 1])))
In [6]:
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=np.int)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
In [7]:
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.array([-1.0, 1.0, 2.0])
print(sigmoid(x))
In [8]:
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
In [9]:
x = np.arange(-5.0, 5.0, 0.1)
y_step = step_function(x)
y_sig = sigmoid(x)
plt.plot(x, y_step, '--')
plt.plot(x, y_sig)
plt.ylim(-0.1, 1.1)
plt.show()
In [10]:
def relu(x):
return np.maximum(0, x)
print(relu(np.array([-2, 0, 5])))
x = np.arange(-6.0, 6.0, 0.1)
y = relu(x)
plt.plot(x, y)
plt.ylim(-0.5, 5)
plt.show()
In [11]:
import numpy as np
A = np.array([1, 2, 3, 4])
print(A)
print(np.ndim(A)) # 次元数
print(A.shape) # 4行,
print(A.shape[0]) # 添字0次元のデータ数
In [12]:
import numpy as np
B = np.array([[1, 2], [3, 4], [5, 6]])
print(B)
print(np.ndim(B)) # 次元数
print(B.shape) # 3行2列
print(B.shape[0]) # 添字0次元のデータ数
In [13]:
# 行列の内積
A = np.array([[1,2], [3,4]])
print(A.shape)
B = np.array([[5,6], [7,8]])
print(B.shape)
print(np.dot(A, B))
In [14]:
# 行列の内積
A = np.array([[1,2,3], [4,5,6]])
print(A.shape)
B = np.array([[1,2], [3,4], [5,6]])
print(B.shape)
print(np.dot(A, B))
In [15]:
# 行列の内積
A = np.array([[1,2], [3,4], [5,6]])
print(A.shape)
B = np.array([7,8])
print(B.shape)
print(np.dot(A, B))
In [16]:
# 入力
X = np.array([1, 2])
print(X.shape)
# 重み
W = np.array([[1,3,5], [2,4,6]])
print(W)
print(W.shape)
# ニューラルネットワークの内積
Y = np.dot(X, W)
print(Y)
第1層目の1番目のニューロンへ信号伝達は以下式となる。
$$ a_{1}^{(1)} = w_{1\;1}^{(1)} + w_{1\;2}^{(1)} + b_{1}^{(1)} $$行列の内積を用いると以下式で表される。
$$ A_{1}^{(1)} = XW^{(1)} + B^{(1)} $$$$ X = (x1 \; x2) $$$$ B^{(1)} = (b_{1}^{(1)} \; b_{2}^{(1)} \; b_{3}^{(1)}) $$$$ W_{1} = \begin{pmatrix} w_{1\;1}^{(1)} & w_{2\;1}^{(1)} & w_{3\;1}^{(1)} \\ w_{1\;2}^{(1)} & w_{2\;2}^{(1)} & w_{3\;2}^{(1)} \\ \end{pmatrix} $$実装は以下。
In [17]:
# 入力層から第1層
import numpy as np
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
print(W1.shape)
print(X.shape)
print(B1.shape)
A1 = np.dot(X, W1) + B1
print(A1)
活性化関数としてシグモイド関数を利用した場合は以下となる。 重み付き和はa、活性化関数で変換された信号をzとする。活性化関数はh()とする。
In [18]:
Z1 = sigmoid(A1)
print(A1)
print(Z1)
In [19]:
# 第1層から第2層
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
print(Z1.shape)
print(W2.shape)
print(B2.shape)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
In [20]:
# 第2層から出力層
def identity_function(x):
return x
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)
print(Y)
In [21]:
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
network['b2'] = np.array([0.1, 0.2])
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
network['b3'] = np.array([0.1, 0.2])
return network
def forward(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = identity_function(a3)
return y
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y)
In [22]:
# ソフトマックス関数
import matplotlib.pyplot as plt
from matplotlib.image import imread
from graphviz import Digraph
f = Digraph(format="png")
f.attr(rankdir='LR')
f.attr('node', shape='circle')
f.edge('a1', 'y1', len='10')
f.edge('a2', 'y1', len='10')
f.edge('a3', 'y1', len='10')
f.edge('a1', 'y2', len='10')
f.edge('a2', 'y2', len='10')
f.edge('a3', 'y2', len='10')
f.edge('a1', 'y3', len='10')
f.edge('a2', 'y3', len='10')
f.edge('a3', 'y3', len='10')
f.render("../docs/neural_network")
img = imread('../docs/neural_network.png')
plt.figure(figsize=(6,4))
plt.imshow(img)
ax = plt.gca() # get current axis
# 枠線非表示
#ax.spines["right"].set_color("none") # 右消し
#ax.spines["left"].set_color("none") # 左消し
#ax.spines["top"].set_color("none") # 上消し
#ax.spines["bottom"].set_color("none") # 下消し
## 目盛を非表示にする
ax.tick_params(axis='x', which='both', top='off', bottom='off', labelbottom='off')
ax.tick_params(axis='y', which='both', left='off', right='off', labelleft='off')
plt.show()
In [23]:
# ソフトマックス関数の実装
a = np.array([0.3, 2.9, 4.0])
exp_a = np.exp(a)
print(exp_a)
sum_exp_a = np.sum(exp_a)
print(sum_exp_a)
y = exp_a / sum_exp_a
print(y)
In [24]:
# 関数として定義
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
指数関数の計算を行なう際、大きな値になってしまいオーバーフローを起こすおそれがある。ソフトマックス中の分子分母両者の指数関数の計算において定数を減算することによって桁あふれを防ぐ。(結果は変わらない) 定数は入力信号の最大値を用いることが一般的。
$$ y_{k} = \frac{exp(a_{k})}{\sum_{i=1}^{n}exp(a_{i})} \\ = \frac{exp(a_{k})}{\sum_{i=1}^{n}exp(a_{i})} \\ = \frac{Cexp(a_{k})}{C\sum_{i=1}^{n}exp(a_{i})} \\ = \frac{exp(a_{k} + logC)}{\sum_{i=1}^{n}exp(a_{i} + logC)} \\ = \frac{exp(a_{k} + C')}{\sum_{i=1}^{n}exp(a_{i} + C')} \\ $$
In [25]:
# オーバーフローの再現と改善の検証
a = np.array([1010, 1000, 990])
print(np.exp(a) / np.sum(np.exp(a)))
c = np.max(a)
print(a - c)
print(np.exp(a - c) / np.sum(np.exp(a - c)))
In [26]:
# ソフトマックス関数(改善)の実装
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
In [29]:
import sys, os
sys.path.append(os.pardir)
from src.mnist import load_mnist
# normalize:入力画像の正規化(255から0~1)
# flatten:入力画像を平らに変換(1×28×28から784個の配列)
# one_hot_label:ラベルのone_hot表現(2であれば[0,0,1,0,0,0,0,0,0,0])
(x_train, t_train), (x_test, t_test) = \
load_mnist(flatten=True, normalize=False)
print(x_train.shape) # 訓練画像
print(t_train.shape) # テスト画像
print(x_test.shape) # 訓練ラベル
print(t_test.shape) # テストラベル
In [35]:
# MNIST画像の表示
import sys, os
sys.path.append(os.pardir)
import numpy as np
from src.mnist import load_mnist
from PIL import Image
def img_show(img):
pil_img = Image.fromarray(np.uint8(img))
pil_img.show()
(x_train, t_train), (x_test, t_test) = \
load_mnist(flatten=True, normalize=False)
img = x_train[0]
label = t_train[0]
print(label)
print(img.shape)
img = img.reshape(28, 28)
print(img.shape)
img_show(img)
推論を行なうニューラルネットワークを実装する。入力層を784個(画像サイズより)、出力層を10個(推定する数字のクラス0~9)、隠れ層を2層(第一層を50個、第二層を100個のニューロン。50、100個は任意に設定した)とする。
load_mnist中でnormalizeを行っており、画像の各ピクセル値を255で除算している。その結果、0.0~1.0の範囲に収まるように変換されている。 データをある決まった範囲に変換する処理を正規化(normalization)と言う。このようなニューラルネットワークの入力データに対して決まった変換を行なうことは前処理(pre-processiong)と呼ばれる。(入力画像データに対して前処理として正規化を行った)
In [47]:
import pickle
def get_data():
(x_train, t_train), (x_test, t_test) = \
load_mnist(normalize=True, flatten=True, one_hot_label=False)
return x_test, t_test
# 学習結果のサンプルパラメータの読込
def init_network():
with open("../src/sample_weight.pkl", 'rb') as f:
network = pickle.load(f)
return network
def predict(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = softmax(a3)
return y
In [48]:
# 推定
x, t = get_data()
# 学習済みパラメータ取得
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
# 推定
y = predict(network, x[i])
# 最も確率が高いクラスを推定ラベルとして取得
p = np.argmax(y)
# 推定ラベルと正解ラベルの突き合わせ
if p == t[i]:
accuracy_cnt += 1
# 正解率の表示
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
In [52]:
# バッチによる実装
x, t = get_data()
# 学習済みパラメータ取得
network = init_network()
batch_size = 100
accuracy_cnt = 0
for i in range(0, len(x), batch_size):
x_batch = x[i:i+batch_size]
y_batch = predict(network, x_batch)
p = np.argmax(y_batch, axis=1)
accuracy_cnt += np.sum(p == t[i:i+batch_size])
# 正解率の表示
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))