SGD

# 卷积层

$\newcommand{\blue}[1]{\color{Cyan}#1} \begin{bmatrix} 1 & 2 & 3 & 0\\ 0 & 1 & 2 & 3\\ 3 & 0 & 1 & 2\\ 2 & 3 & 0 & 1 \end{bmatrix} * \begin{bmatrix} 2 & 0 & 1\\ 0 & 1 & 2\\ 1 & 0 & 2 \end{bmatrix} =\begin{bmatrix} 15 & 16\\ 6 & 15 \end{bmatrix} \\ \begin{bmatrix} 15 & \\ & \end{bmatrix} =\begin{bmatrix} \blue{1} & \blue2 & \blue3 & 0\\ \blue0 & \blue1 & \blue2 & 3\\ \blue3 & \blue0 & \blue1 & 2\\ 2 & 3 & 0 & 1 \end{bmatrix} \odot \begin{bmatrix} 2 & 0 & 1\\ 0 & 1 & 2\\ 1 & 0 & 2 \end{bmatrix} \\ \begin{bmatrix} & 16 \\ & \end{bmatrix} =\begin{bmatrix} 1 & \blue2 & \blue3 & \blue0\\ 0 & \blue1 & \blue2 & \blue3\\ 3 & \blue0 & \blue1 & \blue2\\ 2 & 3 & 0 & 1 \end{bmatrix} \odot \begin{bmatrix} 2 & 0 & 1\\ 0 & 1 & 2\\ 1 & 0 & 2 \end{bmatrix} \\ \begin{bmatrix} & \\ 6 & \end{bmatrix} =\begin{bmatrix} 1 & 2 & 3 & 0\\ \blue0 & \blue1 & \blue2 & 3\\ \blue3 & \blue0 & \blue1 & 2\\ \blue2 & \blue3 & \blue0 & 1 \end{bmatrix} \odot \begin{bmatrix} 2 & 0 & 1\\ 0 & 1 & 2\\ 1 & 0 & 2 \end{bmatrix} \\ \begin{bmatrix} & \\ & 15 \end{bmatrix} =\begin{bmatrix} 1 & 2 & 3 & 0\\ 0 & \blue1 & \blue2 & \blue3\\ 3 & \blue0 & \blue1 & \blue2\\ 2 & \blue3 & \blue0 & \blue1 \end{bmatrix} \odot \begin{bmatrix} 2 & 0 & 1\\ 0 & 1 & 2\\ 1 & 0 & 2 \end{bmatrix}$

$\newcommand{\grey}[1]{\color{Grey}#1} \begin{bmatrix} \grey0 & \grey0 & \grey0 & \grey0 & \grey0 & \grey0\\ \grey0 & 1 & 2 & 3 & 0 & \grey0\\ \grey0 & 0 & 1 & 2 & 3 & \grey0\\ \grey0 & 3 & 0 & 1 & 2 & \grey0\\ \grey0 & 2 & 3 & 0 & 1 & \grey0\\ \grey0 & \grey0 & \grey0 & \grey0 & \grey0 & \grey0 \end{bmatrix} * \begin{bmatrix} 2 & 0 & 1\\ 0 & 1 & 2\\ 1 & 0 & 2 \end{bmatrix} =\begin{bmatrix} 7 & 12 & 10 & 2\\ 4 & 15 & 16 & 10\\ 10 & 6 & 15 & 6\\ 8 & 10 & 4 & 3 \end{bmatrix}$

$\newcommand{\grey}[1]{\color{Grey}#1} \begin{bmatrix} \grey0 & \grey0 & \grey0 & \grey0 & \grey0 & \grey0\\ \grey0 & 1 & 2 & 3 & 0 & \grey0\\ \grey0 & 0 & 1 & 2 & 3 & \grey0\\ \grey0 & 3 & 0 & 1 & 2 & \grey0\\ \grey0 & 2 & 3 & 0 & 1 & \grey0\\ \grey0 & \grey0 & \grey0 & \grey0 & \grey0 & \grey0 \end{bmatrix} * \begin{bmatrix} 2 & 0 & 1\\ 0 & 1 & 2\\ 1 & 0 & 2 \end{bmatrix} =\begin{bmatrix} 7 & 2\\ 8 & 3 \end{bmatrix}$

$OH = \frac{H+2P-FH}{S}+1\\ OW = \frac{W+2P-FW}{S}+1$

# 池化层

$\newcommand{\blue}[1]{\color{Cyan}#1} \newcommand{\red}[1]{\color{Magenta}#1} \newcommand{\orange}[1]{\color{Orange}#1} \newcommand{\green}[1]{\color{Lime}#1} \begin{bmatrix} \blue1 & \blue2 & \red3 & \red0\\ \blue0 & \blue1 & \red2 & \red3\\ \orange3 & \orange0 & \green1 & \green2\\ \orange2 & \orange3 & \green0 & \green1 \end{bmatrix} \Rightarrow \begin{bmatrix} \blue2 & \red3\\ \orange4 & \green5 \end{bmatrix}$

• 池化层没有要学习的参数，只是取最大或平均值
• 数据经过池化层后通道数不发生变换，各通道是独立进行的
• 池化层对微小的变化具有鲁棒性（这样图像就算有微小的位移也不会造成影响）

# 卷积层的实现

im2col
x = np.random.rand(1,3,7,7)
print(col1.shape)
OUTPUT:
(9, 75)

class Convolution:
def __init__(self, W, b, stride=1, pad=0):
self.W = W
self.b = b
self.stride = stride

# 中间数据（backward时使用）
self.x = None
self.col = None
self.col_W = None

# 权重和偏置参数的梯度
self.dW = None
self.db = None

def forward(self, x):
FN, C, FH, FW = self.W.shape
N, C, H, W = x.shape
out_h = 1 + int((H + 2*self.pad - FH) / self.stride)
out_w = 1 + int((W + 2*self.pad - FW) / self.stride)

col = im2col(x, FH, FW, self.stride, self.pad)
col_W = self.W.reshape(FN, -1).T

out = np.dot(col, col_W) + self.b
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

self.x = x
self.col = col
self.col_W = col_W

return out

def backward(self, dout):
FN, C, FH, FW = self.W.shape
dout = dout.transpose(0,2,3,1).reshape(-1, FN)

self.db = np.sum(dout, axis=0)
self.dW = np.dot(self.col.T, dout)
self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

dcol = np.dot(dout, self.col_W.T)
dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

return dx

# 池化层的实现

class Pooling:
def __init__(self, pool_h, pool_w, stride=1, pad=0):
self.pool_h = pool_h
self.pool_w = pool_w
self.stride = stride

self.x = None
self.arg_max = None

def forward(self, x):
N, C, H, W = x.shape
out_h = int(1 + (H - self.pool_h) / self.stride)
out_w = int(1 + (W - self.pool_w) / self.stride)

col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
col = col.reshape(-1, self.pool_h*self.pool_w)

arg_max = np.argmax(col, axis=1)
out = np.max(col, axis=1)
out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

self.x = x
self.arg_max = arg_max

return out

def backward(self, dout):
dout = dout.transpose(0, 2, 3, 1)

pool_size = self.pool_h * self.pool_w
dmax = np.zeros((dout.size, pool_size))
dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
dmax = dmax.reshape(dout.shape + (pool_size,))

dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)

return dx

# 构建CNN

• Conv
• ReLU
• Pooling
• Affine
• ReLU
• Affine
• Softmax
import pickle
import numpy as np
from collections import OrderedDict

class SimpleConvNet:
"""简单的ConvNet

conv - relu - pool - affine - relu - affine - softmax

Parameters
----------
input_size : 输入大小（MNIST的情况下为784）
hidden_size_list : 隐藏层的神经元数量的列表（e.g. [100, 100, 100]）
output_size : 输出大小（MNIST的情况下为10）
activation : 'relu' or 'sigmoid'
weight_init_std : 指定权重的标准差（e.g. 0.01）
指定'relu'或'he'的情况下设定“He的初始值”
指定'sigmoid'或'xavier'的情况下设定“Xavier的初始值”
"""
def __init__(self, input_dim=(1, 28, 28),
hidden_size=100, output_size=10, weight_init_std=0.01):
filter_num = conv_param['filter_num']
filter_size = conv_param['filter_size']
filter_stride = conv_param['stride']
input_size = input_dim[1]
conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1
pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))

# 初始化权重
self.params = {}
self.params['W1'] = weight_init_std * \
np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
self.params['b1'] = np.zeros(filter_num)
self.params['W2'] = weight_init_std * \
np.random.randn(pool_output_size, hidden_size)
self.params['b2'] = np.zeros(hidden_size)
self.params['W3'] = weight_init_std * \
np.random.randn(hidden_size, output_size)
self.params['b3'] = np.zeros(output_size)

# 生成层
self.layers = OrderedDict()
self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
self.layers['Relu1'] = Relu()
self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
self.layers['Relu2'] = Relu()
self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])

self.last_layer = SoftmaxWithLoss()

def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)

return x

def loss(self, x, t):
"""求损失函数
参数x是输入数据、t是教师标签
"""
y = self.predict(x)
return self.last_layer.forward(y, t)

def accuracy(self, x, t, batch_size=100):
if t.ndim != 1 : t = np.argmax(t, axis=1)

acc = 0.0

for i in range(int(x.shape[0] / batch_size)):
tx = x[i*batch_size:(i+1)*batch_size]
tt = t[i*batch_size:(i+1)*batch_size]
y = self.predict(tx)
y = np.argmax(y, axis=1)
acc += np.sum(y == tt)

return acc / x.shape[0]

"""求梯度（数值微分）

Parameters
----------
x : 输入数据
t : 教师标签

Returns
-------
具有各层的梯度的字典变量
"""
loss_w = lambda w: self.loss(x, t)

for idx in (1, 2, 3):

"""求梯度（误差反向传播法）

Parameters
----------
x : 输入数据
t : 教师标签

Returns
-------
具有各层的梯度的字典变量
"""
# forward
self.loss(x, t)

# backward
dout = 1
dout = self.last_layer.backward(dout)

layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)

# 设定

def save_params(self, file_name="params.pkl"):
params = {}
for key, val in self.params.items():
params[key] = val
with open(file_name, 'wb') as f:
pickle.dump(params, f)

with open(file_name, 'rb') as f:
for key, val in params.items():
self.params[key] = val

for i, key in enumerate(['Conv1', 'Affine1', 'Affine2']):
self.layers[key].W = self.params['W' + str(i+1)]
self.layers[key].b = self.params['b' + str(i+1)]
import numpy as np
import matplotlib.pyplot as plt

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

# 处理花费时间较长的情况下减少数据
x_train, t_train = x_train[:2000], t_train[:2000]
x_test, t_test = x_test[:1000], t_test[:1000]

max_epochs = 20

network = SimpleConvNet(input_dim=(1,28,28),
conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
hidden_size=100, output_size=10, weight_init_std=0.01)

trainer = Trainer(network, x_train, t_train, x_test, t_test,
epochs=max_epochs, mini_batch_size=100,
evaluate_sample_num_per_epoch=1000)
trainer.train()

# 保存参数
network.save_params("params.pkl")
print("Saved Network Parameters!")

# 绘制图形
markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=2)
plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=2)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

import numpy as np
import matplotlib.pyplot as plt

def filter_show(filters, nx=8, margin=3, scale=10):
"""
c.f. https://gist.github.com/aidiary/07d530d5e08011832b12#file-draw_weight-py
"""
FN, C, FH, FW = filters.shape
ny = int(np.ceil(FN / nx))

fig = plt.figure()
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)

for i in range(FN):
ax = fig.add_subplot(ny, nx, i+1, xticks=[], yticks=[])
ax.imshow(filters[i, 0], cmap=plt.cm.gray_r, interpolation='nearest')
plt.show()

network = SimpleConvNet()
# 随机进行初始化后的权重
filter_show(network.params['W1'])

# 学习后的权重