# 第5章 分類モデルを開発してみよう¶

## 5.1 事前準備¶

[自分へのメモ] パッケージやライブラリは必要になったときに　import することにする。

## 5.2 Layerクラスの開発¶

In [6]:
import numpy as np

class Layer:

def __init__(self, prev_layer_size, layer_size, activation_fn):
np.random.seed(1)
self.W = np.random.randn(prev_layer_size, layer_size) / np.sqrt(prev_layer_size) # Xavier initialization
self.b = np.zeros((layer_size))
self.activation_fn = activation_fn

def forward(self, A_prev):
Z = np.dot(A_prev, self.W) + self.b
A = self.activation_fn(Z)
return A


## 5.3 順伝播を体験しよう¶

### Layer 0 → Layer 1

In [7]:
np.random.seed(1)
batch_size = 2
input_layer_size = 2
A0 = np.random.randn(batch_size, input_layer_size)
print(A0.shape)
print('Output of Layer 0: \n', A0)

(2, 2)
Output of Layer 0:
[[ 1.62434536 -0.61175641]
[-0.52817175 -1.07296862]]

In [8]:
relu_fn = lambda Z: np.maximum(Z, 0)   # ReLU Function

layer1 = Layer(2, 3, relu_fn)
print('Weights of Layer 1:\n', layer1.W.shape, '\n', layer1.W)
print('bias of Layer 1:\n', layer1.b.shape, '\n', layer1.b)

Weights of Layer 1:
(2, 3)
[[ 1.14858562 -0.43257711 -0.37347383]
[-0.75870339  0.6119356  -1.62743362]]
bias of Layer 1:
(3,)
[0. 0. 0.]

In [9]:
# forward propagation
A1 = layer1.forward(A0)

print('Output of Layer 1:\n', A1.shape, '\n', A1)

Output of Layer 1:
(2, 3)
[[2.32984139 0.         0.38894247]
[0.20741445 0.         1.94344353]]


### Layer 1 → Layer 2

In [10]:
layer2 = Layer(3, 4, relu_fn)

print('Weights of Layer 2: \n', layer2.W.shape, '\n', layer2.W)
print('bias of layer2: \n', layer2.b.shape, '\n', layer2.b)

Weights of Layer 2:
(3, 4)
[[ 0.93781623 -0.35319773 -0.3049401  -0.61947872]
[ 0.49964333 -1.32879399  1.00736754 -0.43948301]
[ 0.18419731 -0.14397405  0.84414841 -1.18942279]]
bias of layer2:
(4,)
[0. 0. 0. 0.]

In [11]:
# forward propagation
A2 = layer2.forward(A1)
print('Outputs of Layer 2:\n', A2.shape, '\n', A2)

Outputs of Layer 2:
(2, 4)
[[2.25660524 0.         0.         0.        ]
[0.5524937  0.         1.57730579 0.        ]]


### Layer 2 → Layer 3

In [14]:
from scipy.special import softmax

softmax_fn = lambda Z: softmax(Z, axis=1)

In [15]:
layer3 = Layer(4, 3, softmax_fn)

print('Weights of Layer 3:\n', layer3.W.shape, '\n', layer3.W)
print('bias of Layer 3:\n', layer3.b.shape, '\n', layer3.b)

Weights of Layer 3:
(4, 3)
[[ 0.81217268 -0.30587821 -0.26408588]
[-0.53648431  0.43270381 -1.15076935]
[ 0.87240588 -0.38060345  0.15951955]
[-0.12468519  0.73105397 -1.03007035]]
bias of Layer 3:
(3,)
[0. 0. 0.]

In [16]:
# forward propagation
A3 = layer3.forward(A2)
print('Outputs of Layer 3:\n', A3.shape, '\n', A3)

Outputs of Layer 3:
(2, 3)
[[0.85589266 0.06865854 0.0754488 ]
[0.79748189 0.05958264 0.14293548]]


## 5.4 SimpleClassifier クラスの開発¶

In [37]:
# SimpleClassifier
class SimpleClassifier:
def __init__(self, input_layer_size, output_layer_size, hidden_layers_sizes, activation_fn=relu_fn):
layer_sizes = [ input_layer_size, *hidden_layers_sizes ]
self.layers = [ Layer(layer_sizes[i], layer_sizes[i+1], activation_fn) for i in range(len(layer_sizes) - 1) ]
output_layer = Layer(layer_sizes[-1], output_layer_size, softmax_fn)
self.layers.append(output_layer)

def forward(self, A0):
A = A0
for layer in self.layers:
A = layer.forward(A)
Y_hat = A
return Y_hat

def predict(self, X):
Y_hat = self.forward(X)
return Y_hat

def evaluate_accuracy(self, X, Y):
predict = np.argmax(self.predict(X), axis=1)
actual = np.argmax(Y, axis=1)
num_corrects = len(np.where(predict==actual)[0])  # np.where() 条件に合う要素のインデックスを返す
accuracy = num_corrects / len(X)
return accuracy


## 5.5 画像分類を体験しよう¶

docker イメージをビルドする際の　Dockerfile の中で次のように記述されていて、pip でmnistがインストールされている。 ここではこのmnistパッケージを利用する。

# install python3 packages
RUN pip install \
'mnist==v0.2.2' \
'japanize-matplotlib==v1.1.2'


In [20]:
import mnist

X_train, Y_train = mnist.train_images(), mnist.train_labels()
X_test, Y_test = mnist.test_images(), mnist.test_labels()
X_train, X_test = X_train / 255.0, X_test / 255.0

In [22]:
print(X_train.shape, Y_train.shape)
print(X_test.shape, Y_test.shape)

(60000, 28, 28) (60000,)
(10000, 28, 28) (10000,)

In [27]:
%matplotlib inline
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1,1, figsize=(4,4))
ax.imshow(X_test[0], cmap=plt.cm.gray)
ax.axis('off')
ax.text(0.5, -0.1, str(Y_test[0]), fontsize=24, ha='center', transform=ax.transAxes)

plt.show()

In [28]:
# list 5.18 画像データをフラットに変換する
# p.102

X_train_flat = X_train.reshape(-1, 28* 28)
X_test_flat = X_test.reshape(-1, 28*28)

print(X_train_flat.shape, X_test_flat.shape)

(60000, 784) (10000, 784)

In [31]:
# list 5.18 正解ラベルを one hot vector 化する
# p.102

num_classes = 10   # 0, 1, ..., 9 に分類するので 10 classes classification

Y_train_ohe = np.eye(num_classes)[Y_train]
Y_test_ohe = np.eye(num_classes)[Y_test]

print(Y_train_ohe.shape, Y_test_ohe.shape)

(60000, 10) (10000, 10)

In [38]:
# list 5.22 MNISTの分類モデルを作成する
# p.103

mnist_classifier = SimpleClassifier(X_test_flat.shape[1], num_classes, [64, 32]) # 784 --> 10, hidden_unit=64,32

In [39]:
# list 5.23 推論と分類
# p.104

Y_hat = mnist_classifier.predict(X_test_flat[0:1])   # 最初の検証用画像データを用いて推論してみる
print(Y_hat[0])
print('分類結果: ', np.argmax(Y_hat[0]))   # 2 <--間違い (まだ学習していないので、あてずっぽうと同じ)

[0.10292778 0.06061532 0.1338951  0.10314735 0.10864224 0.09773565
0.11141222 0.08672163 0.09830012 0.09660259]


In [42]:
# list 5.24 全テストデータを分類して精度を算出する
# p.105

accuracy = mnist_classifier.evaluate_accuracy(X_test_flat, Y_test_ohe)
print(f'正解率: {accuracy*100:.2f} %')

正解率: 8.63 %


## 5.6 活性化関数によるモデルの表現力の変化を体験しよう¶

Layer 0 (2 nodes) → Layer 1 (2 nodes) → Layer 2 (1 node)

Layer 1 の活性化関数 (activation function) を次の3種類に変更して実験する。

1. $y = x$
2. $y = 0.5 x + 1$
3. $y = ReLU(x) = \begin{cases} x \quad x \geqq 0 \\ 0 \quad x < 0 \\ \end{cases}$

Layer 2 の活性化関数は無し ($y=x$)とする。

In [53]:
# list 5.25 実験用のプログラム
# p.105-106
# [自分へのメモ] 独自のコードに直している

# case 1
classifier1 = SimpleClassifier(2, 1, [2])
classifier1.layers[1].activation_fn = lambda x: x   # y = x
classifier1.layers[0].activation_fn = lambda x: x   # y = x

# case 2
classifier2 = SimpleClassifier(2,1,[2])
classifier2.layers[1].activation_fn = lambda x: x   # y = x
classifier2.layers[0].activation_fn = lambda x: 0.5 * x + 1

# case 3
classifier3 = SimpleClassifier(2,1,[2])
classifier3.layers[1].activation_fn = lambda x: x
classifier3.layers[0].activation_fn = lambda x: np.maximum(x, 0)

xmin = -5
xmax = 5
nx = 100

xs = np.linspace(xmin, xmax, nx)
X_input = np.array([ np.array([x, 1]) for x in xs ])

y1 = classifier1.predict(X_input)
y2 = classifier2.predict(X_input)
y3 = classifier3.predict(X_input)

In [62]:
print(y3.shape)
print(y3[0:10])

(100, 1)
[[-0.60741706]
[-0.58851575]
[-0.56961444]
[-0.55071313]
[-0.53181182]
[-0.51291051]
[-0.4940092 ]
[-0.4751079 ]
[-0.45620659]
[-0.43730528]]

In [63]:
# 1次元ベクトルに変換する
y3_flat = np.squeeze(y3)
print(y3_flat.shape)
print(np.squeeze(y3_flat[0:10]))

(100,)
[-0.60741706 -0.58851575 -0.56961444 -0.55071313 -0.53181182 -0.51291051
-0.4940092  -0.4751079  -0.45620659 -0.43730528]

In [56]:
%matplotlib inline
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1,1,figsize=(6,6))
ax.set_xticks(np.arange(xmin, xmax+1, 1))
ax.set_yticks(np.arange(xmin, xmax+1, 1))
ax.set_xlabel('x1')
ax.set_ylabel('y')
ax.plot(xs, np.squeeze(y1),c='r',label='y1')
ax.plot(xs, np.squeeze(y2),c='b',label='y2')
ax.plot(xs, np.squeeze(y3),c='g',label='y3')

ax.legend()
plt.show()