ゼロから作るDeepLearning 第3章 ニューラルネットワーク でとったノート
目次
ゼロから作るDeep Learning 第3章 ニューラルネットワークを読んで取ったノートです.ご参考になれば幸いです.
本はこちら.
一つ目の式をより丁寧に書くと,
: 活性化関数 (activation function)
活性化関数は入力信号の総和がどのように活性化するかということを決定する.
In [1]:
def step_function(x):
return int(x > 0)
np.ndarrayを入力できるように書き換え
In [2]:
def step_function(x):
return (x > 0).astype(np.int64)
In [3]:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.size'] = 13
plt.rcParams['font.family'] = 'Helvetica'
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
fig, ax = plt.subplots(dpi=100, facecolor='white')
ax.plot(x, y)
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_ylim(-0.1, 1.1)
fig.tight_layout()
In [4]:
def sigmoid(x):
return 1 / (1 + np.exp(-x))
In [5]:
x = np.array([-1.0, 1.0, 2.0])
sigmoid(x)
Out[5]:
array([0.26894142, 0.73105858, 0.88079708])
In [6]:
plt.close()
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
fig, ax = plt.subplots(dpi=100, facecolor='white')
ax.plot(x, y)
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_ylim(-0.1, 1.1)
fig.tight_layout()
In [7]:
def relu(x):
return np.maximum(0, x)
In [8]:
A = np.array([1, 2, 3, 4])
A
Out[8]:
array([1, 2, 3, 4])
In [9]:
np.ndim(A)
Out[9]:
1
In [10]:
A.shape
Out[10]:
(4,)
In [11]:
A.shape[0]
Out[11]:
4
In [12]:
B = np.array([[1, 2], [3, 4], [5, 6]])
B
Out[12]:
array([[1, 2],
[3, 4],
[5, 6]])
In [13]:
np.ndim(B)
Out[13]:
2
In [14]:
B.shape
Out[14]:
(3, 2)
In [15]:
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
np.dot(A, B)
Out[15]:
array([[19, 22],
[43, 50]])
In [16]:
A = np.array([[1, 2, 3], [4, 5, 6]])
B = np.array([[1, 2], [3, 4], [5, 6]])
A.shape, B.shape
Out[16]:
((2, 3), (3, 2))
In [17]:
np.dot(A, B)
Out[17]:
array([[22, 28],
[49, 64]])
In [18]:
C = np.array([[1, 2], [3, 4]])
A.shape, C.shape
Out[18]:
((2, 3), (2, 2))
In [19]:
try:
np.dot(A, C)
except ValueError as e:
print('{0}\nとエラーメッセージが表示される.'.format(e))
shapes (2,3) and (2,2) not aligned: 3 (dim 1) != 2 (dim 0)
とエラーメッセージが表示される.
In [20]:
A = np.array([[1, 2], [3, 4], [5, 6]])
B = np.array([7, 8])
A.shape, B.shape
Out[20]:
((3, 2), (2,))
In [21]:
np.dot(A, B)
Out[21]:
array([23, 53, 83])
In [22]:
X = np.array([1, 2])
W = np.array([[1, 3, 5], [2, 4, 6]])
W, X.shape, W.shape
Out[22]:
(array([[1, 3, 5],
[2, 4, 6]]),
(2,),
(2, 3))
In [23]:
np.dot(X, W)
Out[23]:
array([ 5, 11, 17])
これを行列で表すと
これらをNumpyで実装.
入力層から第1層
In [24]:
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])
X.shape, W1.shape, B1.shape
Out[24]:
((2,), (2, 3), (3,))
In [25]:
A1 = np.dot(X, W1) + B1
A1
Out[25]:
array([0.3, 0.7, 1.1])
In [26]:
Z1 = sigmoid(A1)
Z1
Out[26]:
array([0.57444252, 0.66818777, 0.75026011])
第1層から第2層
In [27]:
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
Z1.shape, W2.shape, B2.shape
Out[27]:
((3,), (3, 2), (2,))
In [28]:
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
第2層から出力層
In [29]:
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)
Y
Out[29]:
array([0.31682708, 0.69627909])
ここでのidentity_function
は恒等関数.
出力層への伝達で使われる活性化関数は,
In [30]:
def init_network():
network = dict(
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]]),
b1 = np.array([0.1, 0.2, 0.3]),
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]]),
b2 = np.array([0.1, 0.2]),
W3 = np.array([[0.1, 0.3], [0.2, 0.4]]),
b3 = np.array([0.1, 0.2]),
)
return network
In [31]:
def forward(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
# 入力層から第1層
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
# 第1層から第2層
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
# 第2層から出力層
a3 = np.dot(z2, W3) + b3
y = identity_function(a3)
return y
In [32]:
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
y
Out[32]:
array([0.31682708, 0.69627909])
ソフトマックス関数を実装.
In [33]:
a = np.array([0.3, 2.9, 4.0])
exp_a = np.exp(a)
exp_a
Out[33]:
array([ 1.34985881, 18.17414537, 54.59815003])
In [34]:
sum_exp_a = np.sum(exp_a)
sum_exp_a
Out[34]:
74.1221542101633
In [35]:
y = exp_a / sum_exp_a
y
Out[35]:
array([0.01821127, 0.24519181, 0.73659691])
関数化すると,
In [36]:
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
オーバーフローしてしまう例
In [37]:
a = np.array([1010, 1000, 990])
np.exp(a) / np.sum(np.exp(a))
<ipython-input-37-23103c906500>:2: RuntimeWarning: overflow encountered in exp
np.exp(a) / np.sum(np.exp(a))
<ipython-input-37-23103c906500>:2: RuntimeWarning: invalid value encountered in true_divide
np.exp(a) / np.sum(np.exp(a))
Out[37]:
array([nan, nan, nan])
そこで先程の式変形の要領で改善してみる.
In [38]:
c = np.max(a)
a - c
Out[38]:
array([ 0, -10, -20])
In [39]:
np.exp(a - c) / np.sum(np.exp(a - c))
Out[39]:
array([9.99954600e-01, 4.53978686e-05, 2.06106005e-09])
In [40]:
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 [41]:
a = np.array([0.3, 2.9, 4.0])
y = softmax(a)
y
Out[41]:
array([0.01821127, 0.24519181, 0.73659691])
In [42]:
np.sum(y)
Out[42]:
1.0
y[0]
: 1.8 %の確率y[1]
: 24.5 %の確率y[2]
: 73.7 %の確率
と解釈することができ,確率的(統計的)な対応が可能!
注意点
- ソフトマックス関数を適用しても大小関係は変わらない.
- 指数関数が単調増加関数であることに起因
- 実際の問題で分類を行う際ソフトマックス関数は省略されることが一般的.
In [43]:
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(''))) # 上の階層のモジュールをインポートすることができないので.
In [44]:
from dataset.mnist import load_mnist
In [45]:
(X_train, t_train), (X_test, t_test) = load_mnist(flatten=True, normalize=False)
In [46]:
from PIL import Image
def img_show(img):
# numpy.ndarray to PIL用データオブジェクト
pil_img = Image.fromarray(np.uint8(img))
return pil_img # jupyter notebookなので書き換えた.
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
img = x_train[0]
label = t_train[0]
label
Out[46]:
5
In [47]:
img.shape
Out[47]:
(784,)
In [48]:
# 一辺の大きさ
a = int('{0:.0f}'.format(np.sqrt(img.shape[0])))
img = img.reshape(a, a)
img.shape
Out[48]:
(28, 28)
In [49]:
img_show(img)
Out[49]:
In [50]:
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('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 [51]:
from tqdm.notebook import tqdm
x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in tqdm(range(len(x))):
y = predict(network, x[i])
p = np.argmax(y)
if p == t[i]:
accuracy_cnt += 1
print('Accuracy: {0:.4f}'.format(accuracy_cnt/len(x)))
Accuracy: 0.9352
正規化 (normalization) という前処理 (pre-processing) を行っている.
In [52]:
x, _ = get_data()
network = init_network()
W1, W2, W3 = network['W1'], network['W2'], network['W3']
x.shape
Out[52]:
(10000, 784)
In [53]:
x[0].shape
Out[53]:
(784,)
In [54]:
W1.shape, W2.shape, W3.shape
Out[54]:
((784, 50), (50, 100), (100, 10))
さっきの実装は一枚ずつ予測していた.→ バッチ処理したい!
- バッチ処理: まとめて計算する.
- 一枚あたりの処理時間を減らせる.
In [55]:
x, t = get_data()
network = init_network()
batch_size = 100 # バッチの数
accuracy_cnt = 0
for i in tqdm(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: {:.4f}'.format(accuracy_cnt / len(x)))
Accuracy: 0.9352