之前的作业中,我们已经完成了一个两层的全连接神经网络的设计,但是有些简单,
并且还没有模块化,因为那里的损失函数和梯度我们是用一个函数来计算出来的。因
此,我们希望可以设计更复杂的网络,以便于我们可以完成不同类型层的设计,然后
将它们集成到不同结构的模型中。
主要内容如下:
1 基础层
1.1 全连接层
1.1.1 前向传播
1.1.2 反向传播
1.2 Relu层
1.2.1 前向传播
1.2.2 反向传播
1.3 损失函数层
2 两层神经网络
3 Solver
4 多层神经网络
4.1 多层网络训练
5 更新规则
5.1 SGD+Momenrum
5.2 自适应优化器
5.2.1 RMSProp
5.2.3 Adam
5.3 四种优化器的比较
6 网络训练
7 预测
8 总结
注意,这里我们提到的层数,是不将输入层算入的。
1 基础层
在 layers.py 文件中,定义了我们接下来所需要的层,我们来逐个进行分析。
1.1 全连接层
1.1.1 前向传播
前向传播的计算类似这样:
1
2
3
4
5
def layer_forward(x,w):
z= #中间值
out= #输出值
cache=(x,w,z,out) #反向传播计算梯度时需要的参数
return out cache
代码如下:
1
2
3
4
5
6
7
8
9
10
11
def affine_forward(x,w,b):
'''x:(N,d_1,…,d_k) x_rsp中我们会将x转化为(N,D),其中D=d_1*…*d_k; w:
(D,M); b:(M,); out:(N,M); cache:(x,w,b)'''
out=None
N=x.shape[0]
x_rsp=x.reshape(N,‐1)
out=x_rsp.dot(w)+b
cache=(x,w,b)
return out,cache
我们可以检验下计算的误差,检验代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
num_inputs=2
input_shape=(4,5,6)
output_dim=3
input_size=num_inputs*np.prod(input_shape)
weight_size=output_dim*np.prod(input_shape)
x=np.linspace(‐0.1,0.5,num=input_size).reshape(num_inputs,*input_shape)
w=np.linspace(‐0.2,0.3,num=weight_size).reshape(np.prod(input_shape),outp
ut_dim)
b=np.linspace(‐0.3,0.1,num=output_dim)
out,_=affine_forward(x,w,b)
correct_out=np.array([[ 1.49834967, 1.70660132, 1.91485297],
[ 3.25553199, 3.5141327, 3.77273342]])
print('testing affine_forward function:')
16
print('difference:',rel_error(out,correct_out))
输出结果如下:
1
2
testing affine_forward function:
difference: 9.76984772881e‐10
误差竟然达到e10,还是挺不错的。接下来我们就继续计算反向传播。
1.1.2 反向传播
反向传播接收来自上一层输出的梯度值和 cache 值,返回输入和权重的梯度,形式如
下:
1
2
3
4
5
def layer_backward(dout,cache):
x,w,z,out=cache
dx=
dw=
return dx dw
实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
def affine_backward(dout,cache):
'''dout:(N,M); x:(N,d_1,…d_k) x_rsp中我们会将x转化为(N,D),其中
D=d_1*…*d_k; w:(D,M); dx:(D,d_1,…d_k); dw:(D,M); db:(M,)'''
x,w,b=cache
dx,dw,db=None,None,None
N=x.shape[0]
x_rsp=x.reshape(N,‐1)
dx=dout.dot(w.T)
dx=dx.reshape(*x.shape)
dw=x_rsp.T.dot(dout)
db=np.sum(dout,axis=0)
return dx,dw,db
注:如果不清楚反向传播过程中如何利用利用链式法则进行计算,可以参阅我们上一
次的作业
同样,下面我们来检验下:
1
x=np.random.randn(10,2,3)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
w=np.random.randn(6,5)
b=np.random.randn(5)
dout=np.random.randn(10,5)
dx_num=eval_numerical_gradient_array(lambda x:affine_forward(x,w,b)
[0],x,dout)
dw_num=eval_numerical_gradient_array(lambda w:affine_forward(x,w,b)
[0],w,dout)
db_num=eval_numerical_gradient_array(lambda b:affine_forward(x,w,b)
[0],b,dout)
_,cache=affine_forward(x,w,b)
dx,dw,db=affine_backward(dout,cache)
print('test affine_backward function:')
print('dx error:',rel_error(dx_num,dx))
print('dw error:',rel_error(dw_num,dw))
print('db error:',rel_error(db_num,db))
输出结果如下:
1
2
3
4
test affine_backward function:
dx error: 2.50214010994e‐10
dw error: 9.11372672216e‐11
db error: 3.57180037616e‐11
bingo,误差还是很小的。这样我们就完成了一层映射层的前向和反向传播两个过程。
1.2 Relu层
1.2.1 前向传播
代码如下:
1
2
3
4
5
def relu_forward(x):
out=None
out=x*(x>=0)
cache=x
return out,cache
检验代码如下:
1
2
3
4
5
6
7
x=np.linspace(‐0.5,0.5,num=12).reshape(3,4)
out,_=relu_forward(x)
correct_out = np.array([[ 0., 0., 0., 0.,
],
[ 0., 0., 0.04545455,
0.13636364,],
[ 0.22727273, 0.31818182, 0.40909091, 0.5,
]])
print('testing relu_forward function:')
print('difference:',rel_error(out,correct_out))
输出结果如下:
1
2
testing relu_forward function:
difference: 4.99999979802e‐08
1.2.2 反向传播
代码如下:
1
2
3
4
def relu_backward(dout,cache):
dx,x=None,cache
dx=(x>=0)*dout
return dx
检验代码如下:
1
2
3
4
5
6
7
8
9
x=np.random.randn(10,10)
dout=np.random.randn(*x.shape)
dx_num=eval_numerical_gradient_array(lambda x:relu_forward(x)[0],x,dout)
_,cache=relu_forward(x)
dx=relu_backward(dout,cache)
print('testing relu_backward functin:')
print('dx error:',rel_error(dx_num,dx))
输出结果如下:
1
testing relu_backward functin:
2
dx error: 3.27561649471e‐12
到现在,我们差不多已经完成了基础层的构建。但是,实践中,一般映射层后面会接
Relu层,我们这里把两层合为一层来看看:
1
2
3
4
5
6
7
8
9
10
11
def affine_relu_forward(x,w,b):
a,fc_cache=affine_forward(x,w,b)
out,relu_cache=relu_forward(a)
cache=(fc_cache,relu_cache)
return out,cache
def affine_relu_backward(dout,cache):
fc_cache,relu_cache=cache
da=relu_backward(dout,relu_cache)
dx,dw,db=affine_backward(da,fc_cache)
return dx,dw,db
梯度检验代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
x=np.random.randn(2,3,4)
w=np.random.randn(12,10)
b=np.random.randn(10)
dout=np.random.randn(2,10)
out,cache=affine_relu_forward(x,w,b)
dx,dw,db=affine_relu_backward(dout,cache)
dx_num=eval_numerical_gradient_array(lambda x:affine_relu_forward(x,w,b)
[0],x,dout)
dw_num=eval_numerical_gradient_array(lambda w:affine_relu_forward(x,w,b)
[0],w,dout)
db_num=eval_numerical_gradient_array(lambda b:affine_relu_forward(x,w,b)
[0],b,dout)
print('testing affine_relu_forward:')
print('dx_error:',rel_error(dx_num,dx))
print('dw error:',rel_error(dw_num,dw))
print('db error:',rel_error(db_num,db))
运行结果如下:
1
testing affine_relu_forward:
2
3
4
dx_error: 1.58523685005e‐10
dw error: 3.68009862446e‐10
db error: 1.04868325611e‐10
1.3 损失函数层
在上一个作业中,我们已经实现了svm和softmax线性分类器,现在我们来实现了svm
和softmax层。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def svm_loss(x,y):
''':param x: (N,C),x[i,j]表示第i个输入是第j个的分数 :param y:(N,),
x[i]的标签 :return:loss,dx'''
N=x.shape[0]
correct_class_scores=x[np.arange(N),y]
margins=np.maximum(0,x‐correct_class_scores[:,np.newaxis]+1.0)
margins[np.arange(N),y]=0
loss=np.sum(margins)/N
num_pos=np.sum(margins>0,axis=1)
dx=np.zeros_like(x)
dx[margins>0]=1
dx[np.arange(N),y]‐=num_pos
dx/=N
return loss,dx
def softmax_loss(x,y):
probs=np.exp(x‐np.max(x,axis=1,keepdims=True))
probs/=np.sum(probs,axis=1,keepdims=True)
N=x.shape[0]
loss=‐np.sum(np.log(probs[np.arange(N),y]))/N
dx=probs.copy()
dx[np.arange(N),y]‐=1
dx/=N
return loss,dx
我们检验下我们做的是不是正确,代码如下:
num_classes,num_inputs=10,50
x=0.001*np.random.randn(num_inputs,num_classes)
y=np.random.randint(num_classes,size=num_inputs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dx_num=eval_numerical_gradient(lambda x:svm_loss(x,y)[0],x,verbose=False)
loss,dx=svm_loss(x,y)
print('testing svm_loss:')
print('loss:',loss)
print('dx error:',rel_error(dx_num,dx))
dx_num=eval_numerical_gradient(lambda x:softmax_loss(x,y)
[0],x,verbose=False)
loss,dx=softmax_loss(x,y)
print('\ntesting softmax_loss:')
print('loss:',loss)
print('dx error:',rel_error(dx_num,dx))
运行结果如下:
1
2
3
4
5
6
7
testing svm_loss:
loss: 9.00034570116
dx error: 1.40215660067e‐09
testing softmax_loss:
loss: 2.30262010084
dx error: 8.19589239336e‐09
OK,现在我们已经完成了基础层的构建,下面我们就可以通过这些层的连接来组成我
们想要的结构。
2 两层神经网络
下面我们就使用我们原来构建的基础层来搭建一个两层的神经网络。
我们定义了一个 TwoLayerNet 代的类来实现一个两层神经网络的搭建,代码如下:
1
2
3
4
5
6
class TwoLayerNet(object):
def
__init__(self,input_dim=3*32*32,hidden_dim=100,num_classes=10,weight_scal
e=1e‐3,reg=0.0):
self.params={}
self.reg=reg
self.params['W1']=weight_scale*np.random.rand(input_dim,hidden_dim)
self.params['b1']=np.zeros(hidden_dim)