Chapter 1 —— Python基础
Python有“解释器”和“脚本文件”两种运行模式
Numpy
实际上,Numpy主要的处理也是通过C/C++实现的
导入Numpy:import numpy as np
np.array([...])
传入python list构造numpy的array,可以做element-wise(对应元素)运算
np.array([[1, 2], [3, 4]])
可以构造多维数组,可以通过.shape
查询维度
np.arange(0, 6, 0.1)
以0.1为单位,生成从0到6的数据
一维数组->向量,二维数组->矩阵,三维数组->张量
广播
numpy的广播原则:如果两个数组的后缘维度(从末尾开始算起的维度)的轴长度相符或其中的一方的长度为1,则认为它们是广播兼容的。广播会在缺失和(或)长度为1的维度上进行
通过扩展,不同形状的数组也能运算
1
2
3
4
5
>>> A = np.array([[1, 2], [3, 4]])
>>> B = np.array([10, 20])
>>> A*B
array([[10, 40],
[30, 80]])
访问元素
可以用for row in X
遍历X的行元素,也可以直接下标访问
X = X.flatten()
可以将多维数组转换为维度(追加)
从np.array中抽取元素:
1
2
3
4
5
6
7
8
>>> print(X)
[51 55 14 19 0 4]
>>> X[np.array([0, 2, 4])]
array([51, 14, 0])
>>> X > 15
array([True, True, False, True, False, False], dtype=bool)
>>> X[X>15]
array([51, 55, 19])
Matplotlib
绘制图形的库,使用Matplotlib可以轻松地绘制图形和实现数据的可视化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 6, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)
plt.plot(x, y1, label="sin")
plt.plot(x, y2, label="cos", linestyle="--")
plt.xlabel("x")
plt.ylabel("y")
plt.title("1.6 sin & cos")
plt.legend() # 显示label内容
plt.show()
还可以通过imshow()
显示图像,位于matplotlib.image
模块
1
2
3
4
5
6
import matplotlib.image as img
path = "/Users/mariochan/Desktop/Paper.jpg"
x = img.imread(path)
plt.imshow(x)
plt.show()
Chapter 2 —— 感知机
感知机基础
感知机(perceptron),本节中可以理解为人工神经元或者朴素感知机
如果输入乘以权重的和高于某个界限值$\theta$,这里称为阈值,那么“神经元被激活”,输出为1: \(y=\begin{cases} 1, & \text{$w_1x_1+w_2x_2>\theta$} \\ 0, & \text{otherwise} \end{cases}\) 可以结合简单的逻辑电路实现:AND gate、NAND gate、OR gate等:
1
2
3
4
5
6
7
def AND(x1, x2):
w1, w2, theta = 0.5, 0.5, 0.7
tmp = w1*x1 + w2*x2
if tmp <= theta:
return 0
else:
return 1
特别的,我们可以对感知机的表达式作修改: \(y=\begin{cases} 1, & \text{$w_1x_1+w_2x_2+b>0$} \\ 0, & \text{otherwise} \end{cases}\) 此时,我们将$b$称为偏置( $b=-\theta$ ),将$w1,w2$称为权重,以下为numpy写法
1
2
3
4
5
6
7
8
9
10
import numpy as np
def AND(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.7
tmp = np.sum(w*x)+b
if tmp <= 0:
return 0
else:
return 1
权重是控制输入信号的重要性参数,而偏置是神经元被激活的难易程度参数
NAND gate与AND gate只要权重和偏置同时取负就可以实现!
1
2
3
4
5
6
7
8
9
10
import numpy as np
def NAND(x1, x2):
x = np.array([x1, x2])
w = np.array([-0.5, =0.5]) # !!!
b = 0.7 # !!!
tmp = np.sum(w*x)+b
if tmp <= 0:
return 0
else:
return 1
朴素感知机局限性
无法实现XOR(结合空间划分p30),这本质上是因为感知机只能表示由一条线性划分的空间(线性空间,反之称为非线形空间)
多层感知机 —— multi-layered perceptron
多层感知机可以通过叠加层表示XOR等
利用已有门电路的组合
XOR = OR + NAND + AND
1
2
3
4
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
return AND(x1, x2)
Chapter 3 —— 神经网络
神经网络:输入层、中间层(隐藏层)、输出层
激活函数 —— Activation Function
\[y=\begin{cases} 1, & \text{$w_1x_1+w_2x_2+b>0$} \\ 0, & \text{otherwise} \end{cases}\]我们将上述感知机表达式转换为: \(y=h(w_1x_1+w_2x_2+b), \quad h(x) = \begin{cases} 1, & \text{$x>0$} \\ 0, & \text{otherwise} \end{cases}\) 此时$h(x)$即为激活函数(Activation Function),这里可以看看p41的计算过程图
一般而言,“朴素神经网络”指的是单层网络,指的是激活函数使用了阶跃函数(一旦输入超过阈值,就会切换输出)模型
“多层感知机”是指神经网络,即使用sigmoid函数等平滑的激活函数的多层网络
阶跃函数
实现
1
2
3
4
5
6
def step_function(x):
y = x > 0
return y.astype(int)
def step_function2(x):
return np.array(x > 0, dtype=int)
example:
1
2
3
4
5
6
7
8
9
10
>>> import numpy as np
>>> x = np.array([-1.0, 1.0, 2.0])
>>> x
array([-1., 1., 2.])
>>> y = x > 0
>>> y
array([False, True, True], dtype=bool)
>>> y = y.astype(np.int)
>>> y
array([0, 1, 1])
astype()
可以转换Numpy数组的类型
图形
示例:
1
2
3
4
5
6
7
8
9
10
11
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=int)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 限定y轴范围
plt.show()
Sigmoid
expression: $h(x) = \frac{1}{1+e^{-x}}$
实现
利用了广播特性
1
2
3
4
5
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.array([-1.0, 1.0, 2.0])
print(sigmoid(x))
如果我们将上一节的画图函数的阶跃函数替换成Sigmoid函数,differences:
- 我们可以得到一条更为平滑的曲线,而不是急剧变化
- Sigmoid的可以得到的结果是连续的,而阶跃函数得到的结果是离散的
相同点:
- 值域[0, 1],结构均是“小输入接近0(为0),大输入接近1(为1)”
- 均为非线形函数
为什么激活函数不能使用线形函数
不管如何加深层数,总是存在与之等效的“无隐藏层的神经网络”。example: 对于$h(x) = cx$,我们尝试叠加3层,即$y = h(h(h(x))) = c^3x$,如果我们令$a=c^3, f(x) = ax$,那么多层网络实际上没有意义。所以为了发挥叠加层所带来的优势,激活函数必须使用非线形函数。
ReLU函数
ReLU(Rectified Linear Unit):$h(x) = \begin{cases} x, &
\text{$x>0$} \
0, & \text{otherwise}
\end{cases}$
1
2
def relu(x):
return np.maximum(0, x)
多维数组运算
np.ndim()
获得当前数组的维度
A.shape
获得当前数组各个维度的大小
1
2
3
4
5
6
7
8
9
10
>>> import numpy as np
>>> A = np.array([1, 2, 3, 4])
>>> print(A)
[1 2 3 4]
>>> np.ndim(A)
1
>>> A.shape
(4,)
>>> A.shape[0]
4
矩阵运算
维度为2的数组称为矩阵,np.dot()
为点积
1
2
3
4
5
6
7
8
9
>>> A = np.array([[1, 2], [3, 4]])
>>> A.shape
(2, 2)
>>> B = np.array([5, 6], [7, 8])
>>> B.shape
(2, 2)
>>> np.dot(A, B)
array([[19, 22],
[43, 50]])
输出层的激活函数一般用$\sigma()$ (sigma) 表示,不同于隐藏层的$h()$,对于回归问题的$\sigma()$采用identity_funtion()
,二元分类问题使用softmax()
3层神经网络
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import numpy as np
def sigmoid(x):
return 1 / (1+np.exp(-x))
def identity_function(x): # 恒等函数
return x
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])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
A2 = np.dot(Z1, W2)
Z2 = sigmoid(A2)
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3)
Z3 = identity_function(A3)
print(Z3)
更常见的做法,我们使用init_network()
将权重和偏置进行初始化,然后存在字典network
中,然后再在forward()
中编写输入转换为输出的过程。
forward表示的是前向,指的是从输入到输出方向的传递处理
Softmax函数
expression: $y_k = \frac{exp(a_k)}{\sum^n_{i=1}exp(a_k )}$
1
2
3
4
5
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
改进
指数overflow
1
2
3
4
5
6
import numpy as np
a = np.array([1010, 1000, 990])
# overflow
# b = np.exp(a) / np.sum(np.exp(a))
# print(b)
解决方法:给每个数字加上(减去)一个常数,一般选择最大值: \(y_k = \frac{exp(a_k)}{\sum^n_{i=1}exp(a_k )}=\frac{Cexp(a_k)}{C\sum^n_{i=1}exp(a_k )} \\ =\frac{exp(a_k+logC)}{\sum^n_{i=1}exp(a_k+logC)}=\frac{exp(a_k+C')}{\sum^n_{i=1}exp(a_k+C')}\)
1
2
3
4
c = np.max(a)
a = a-c
b = np.exp(a) / np.sum(np.exp(a))
print(b)
特征
softmax的和总是1,我们可以把softmax的输出解释为“概率”
sofrmax值最大对应神经元最大
一般而言,神经网络在推理阶段只将最大神经元对应的类别作为识别结果,这样看的话计算softmax其实是无意义的开销,所以一般会被忽略
前向传播 —— forward propagation
求解机器学习的步骤:学习参数、利用学习到的参数进行推理
手写数字识别
load_mnist
函数以“(训练图像,训练标签),(测试图像,测试标签)”的形式返回读入的MNIST数据。此外,还能像load_mnist(normalize=True, flatten=True, one_hot_label=False)
这样设置参数,详情看p71
one-hot representation将标签表达为[0, 0, 0, 1, 0, 0, 0],仅正确解标签为1
load_mnist函数内置pickle功能,能将运行中的对象保存为文件,后期可以快速调用
识别精度(accuracy):分类正确率统计
正则化(normalization):将数据限定在某个范围内
预处理(pre-processing):对输入数据进行某种转换
数据白化(whitening):将数据整体的分布形状均匀化
批(batch):打包式输入数据
np.argmax(arr, axis=1)
按第1维找最大值对应的下标
Chapter 4 —— 神经网络的学习
特征:从数据中学习
感知机收敛定理:通过有限次数的学习,线性可分问题是可解的,但是非线形可分问题无法通过(自动)学习来解决
特征量:可以从输入数据中准确提取本质数据的转换器
- input -> 人想到的算法 -> output
- input -> 人想到的特征量(如SIFT、HOG)-> 机器学习(如SVM、KNN)-> output
- input -> 神经网络(深度学习)-> output
因此,深度学习也被称为端对端机器学习(end-to-end machine learning)
训练数据与测试数据
使用训练数据寻找最优参数,然后使用测试数据评价模型的实际能力。为的是评价模型的泛化能力。此外训练数据也可以称为监督数据。
过拟合(over fitting)
损失函数
损失函数(loss function)可以使用任意函数,但一般使用均方误差和交叉熵误差。
均方误差
mean squared error:$E=\frac{1}{2}\sum_k(y_k-t_k)^2 $,其中$y_k$表示输出,$t_k$表示监督数据,$k$表示数据维数
只有正确解标签为1,其他标签为0,称为one-hot表示
1
2
3
4
5
6
7
def mean_squared_error(y, t):
return 0.5 * np.sum((y-t)**2)
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
print(mean_squared_error(np.array(y), np.array(t)))
交叉熵误差
cross entropy error:$E=-\sum_kt_k\times logy_k$
我们会加上一个微小偏差$delta$,这样当正确解对应的输出为0的时候,不会出现$-inf$导致后续输出有误
1
2
3
def cross_entropy_error(y, t):
delta = 1e-7 # 微小偏差
return -np.sum(t * np.log(y + delta))
mini-batch
对于所有数据的交叉熵误差总和:$E=\frac{1}{N}\sum_n\sum_kt_{nk}\times logy_{nk}$
如果训练代价较大,随机抽样作为训练数据,称为mini-batch学习
np.random.choice()
可以在指定范围内抽取数字
1
2
3
4
5
6
7
8
9
10
11
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000, 10)
train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
print(batch_mask)
mini-batch版交叉熵误差的实现
1
2
3
4
5
6
7
8
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t * np.log(y + 1e-7)) / batch_size # t为one-hot表示时
# t为标签形式时: return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
为什么不能用识别精度来评价
无法更新梯度,无法获得连续性反馈
sigmoid函数的斜率永不为0,very import
数值微分
numerical differentiation,numerical_diff(f, x)
,还有一个隐式参数$h$表示自变量变化量。
问题与改进:
- $h$这个值不能随意取,太大的话误差大,太小的话float精度丢失,可以选取$10^{-4}$
- 由于$h$实际上并不接近0,我们计算斜率会存在误差,我们可以通过计算(x-h)到(x+h)的差分来部分抵消这种影响,这种差分叫做中心差分(普通的叫做前向查分)
1
2
3
def numerical_diff(f, x):
h = 1e-4 # 0.001
return (f(x+h) - f(x-h)) / (2 * h)
example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def numerical_diff(f, x):
h = 1e-4 # 0.001
return (f(x+h) - f(x-h)) / (2 * h)
def function_1(x):
return 0.01*x**2 + 0.1*x
def getTangent(f, x):
k = numerical_diff(f, x)
b = f(x) - k * x
return lambda t: k * t + b
x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
f2 = getTangent(function_1, 5)
t = f2(x)
plt.xlabel("x")
plt.ylabel("function_1 Val")
plt.xlim(0.0, 20.0)
plt.ylim(0.0, 6.0)
plt.plot(x, y)
plt.plot(x, t)
plt.show()
print(numerical_diff(function_1, 5))
print(numerical_diff(function_1, 10))
对应的,通过数学分析得到的导数称为解析性求导
梯度
gradient:指示的是各点处的函数值减小最多的方向
np.zeros_like(x)
会生成一个形状和x相同、所有元素都为0的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def numerical_gradient(f, x): # 数值微分法
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
print(x, grad)
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = tmp_val + h
fxh1 = f(x) # 计算f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # 计算f(x-h)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val # 还原值
return grad
梯度法 —— gradient method
鞍点(saddle point):梯度为0
寻找最小值:梯度下降法(gradient descent method),寻找最大值:梯度上升法(gradient ascent method)
在深度学习中,主要指的是梯度下降法
学习率(learning rate)$\eta$:表示更新量 \(x = x-\eta\frac{\partial f}{\partial x}\)
1
2
3
4
5
6
7
8
def gradient_decent(f, init_x, lr=0.01, step_num=100): # lr学习率,step_num梯度法重复次数
x = init_x
for i in range(step_num):
grad = numerical_gradient(f, x)
x -= lr * grad # 上升的反方向
return x
Test:
1
2
3
4
5
6
def function_2(x):
return x[0]**2 + x[1]**2
init_x = np.array([-3.0, 4.0])
final_x = gradient_decent(function_2, init_x, lr=0.1)
print(final_x)
关于学习率
过大:容易发散
过小:基本没更新就结束了,除非step_num同步增大
诸如学习率这样的参数称为超参数(hyper parameter)
神经网络的梯度
np.random.randn()
:符合高斯分布的初始化
lambda表示法:lambda parameters: expression
总结
P109-p119
Step:
- mini-batch
- 计算梯度
- 更新参数
- 重复上述步骤
由于mini-batch,所以又称为随机梯度下降法(stockastic gradient descent),常用SGD(三个首字母)标识
我们每隔一个epoch就记录一些衡量指标(如识别精度)
epoch:所有训练数据都被使用过一次的更新次数
如果测试数据和训练数据的识别精度变化重合,可以理解为没有过拟合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
from common.functions import *
from common.gradient import numerical_gradient
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
# 初始化权重
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
def predict(self, x):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
return y
# x:输入数据, t:监督数据
def loss(self, x, t):
y = self.predict(x)
return cross_entropy_error(y, t)
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
# x:输入数据, t:监督数据
def numerical_gradient(self, x, t):
loss_W = lambda W: self.loss(x, t)
grads = {}
grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
return grads
def gradient(self, x, t):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
grads = {}
batch_num = x.shape[0]
# forward
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
# backward
dy = (y - t) / batch_num
grads['W2'] = np.dot(z1.T, dy)
grads['b2'] = np.sum(dy, axis=0)
da1 = np.dot(dy, W2.T)
dz1 = sigmoid_grad(a1) * da1
grads['W1'] = np.dot(x.T, dz1)
grads['b1'] = np.sum(dz1, axis=0)
return grads
Chapter 5 —— 误差反向传播网络
Computational Graph, Chain Rule
简单层实现
加法层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class AddLayer:
def __init__(self):
pass
def forward(self, x, y):
out = x + y
return out
def backward(self, dout):
dx = dout * 1
dy = dout * 1
return dx, dy
乘法层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MulLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x
self.y = y
out = x * y
return out
def backward(self, dout):
dx = dout * self.y
dy = dout * self.x
return dx, dy
激活函数层的实现
ReLU层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ReLU:
def __init__(self):
self.mask = None # 由True/False组成的Numpy数组,将正向传播时的输入x的数据小于等于0的地方保存为True
def forward(self, x):
self.mask = (x <= 0)
out = x.copy()
out[self.mask] = 0
return x
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
Sigmoid层
\[\frac{\partial L}{\partial y}y(1-y)\]y represents output value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Sigmoid:
def __init__(self):
self.out = None
def forward(self, x):
out = 1 / (1 + np.exp(-x))
self.out = out
return out
def backward(self, dout):
dx = dout * (1.0 - self.out) * self.out
return dx
Affine/Softmax层的实现
Affine层
\[Y = X*W+B\quad -> \quad \frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} * W^T \\ \frac{\partial L}{\partial W} = X^T * \frac{\partial L}{\partial Y} \\ \frac{\partial L}{\partial B} = \frac{\partial L}{\partial Y}的第一个轴方向上的和\]在几何学中,正向传播进行的矩阵乘积运算被称为“仿射变换”,因此这里进行的处理实现为”Affine层”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Affine:
def __init__(self, W, b):
self.W = W
self.b = b
self.x = None
self.dW = None
self.db = None
def forward(self, x):
self.x = x
out = np.dot(x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
return dx
Softmax-with-Loss层
神经网络进行的阶段分为推理(inference)和学习,推理一般不使用Softmax层
这里的Loss采用Cross entropy error
最后推出反向传播结果为$y_i-t_i$,Softmax层反向传播的结果为输出与监督标签的差分
如果误差大,反向传播给前面的层的值大,那么学习内容也大
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SoftmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None
self.t = None # one-hot vector
def forward(self, x, t):
self.t = t
self.y = softmax(x)
self.loss = cross_entropy_error(self.y, self.t)
def backward(self, dout=1):
batch_size = self.t.shape[0]
dx = (self.y - self.t) / batch_size # 得到单个数据的误差
return dx
误差反向传播法的实现
OrderedDict
有序字典,可以记住插入顺序,在反向传播的时候只需reverse
就行了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient
from collections import OrderedDict
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
# 初始化权重
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
# 生成层
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
self.lastLayer = SoftmaxWithLoss()
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
# x:输入数据, t:监督数据
def loss(self, x, t):
y = self.predict(x)
return self.lastLayer.forward(y, t)
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
if t.ndim != 1 : t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
# x:输入数据, t:监督数据
def numerical_gradient(self, x, t):
loss_W = lambda W: self.loss(x, t)
grads = {}
grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
return grads
def gradient(self, x, t):
# forward
self.loss(x, t)
# backward
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
# 设定
grads = {}
grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
return grads
梯度确认gradient check
通过数值微分与误差反向传播法求出的结果对比
Comments powered by Disqus.