Python 手动实现三层神经网络(梯度下降法)
最近在学习人工神经网络,由于是第一次入门这个,还是遇到了一些困难。要想真正了解BP网络的原理,最好是有代码手动实现其过程。当我在网上查阅相关代码的时候,发现许多代码对新手不太友好,要么过于简洁,要么用了框架。针对上面的这种情况,我觉定把我这段时间学到的代码及原理进行整理一下。
关于梯度下降法的原理,还是有必要在这里简单介绍一下,如果看不太懂的话建议先去复习原理部分。
梯度下降法简单的来说就是通过损失函数对梯度求导,来获得其梯度。而梯度有什么用呢? 从数学上我们知道,梯度是函数上升的方向,那么我们知道了梯度,取他的相反的值,就可以朝着函数下降的方向,得到函数的极小值,从而获得一个相对的最优解。
废话不多说了,先给出代码
import numpy as np
N,D_in,H,D_out = 64,1000,100,10 # 定义输入数据为64个 1000个输入神经元,100隐层神经元,输出10个神经元
# 初始权值参数
w_1 = np.random.randn(D_in,H)
w_2 = np.random.randn(H,D_out)
learning_rate = 1e-6
# 生成数据
x = np.random.randn(N,D_in)
y = np.random.randn(N,D_out)
# 循环迭代
for i in range(500):
# 前向传播
h = x.dot(w_1) # N*H
h_relu = np.maximum(h,0) # relu 激活函数 ,h_relu 激活函数输出值 N*H
y_pred = h_relu.dot(w_2) # N*D
# 计算误差
loss = np.square( y_pred - y ).sum()
if (i+1)% 50 ==0:
print('第{}次,loss值为{}'.format(i,loss))
# 反向传播
# 先计算各种梯度
grad_y_pred = 2.0*(y_pred - y) # 64*10 L对预测输出的导数
grad_w_2 = h_relu.T.dot(grad_y_pred) # 10*64*64*10=10*10 L对W2的导数
grad_h_relu = grad_y_pred.dot(w_2.T) # L对隐层输出的导数
grad_h = grad_h_relu.copy() # 复制grad_h_relu
grad_h[h<0] = 0 # relu激活函数求导
grad_w_1 = x.T.dot(grad_h)
# 跟新权重
w_1 -= learning_rate*grad_w_1
w_2 -= learning_rate*grad_w_2
下面对代码部分进行简要解释:
首先在这里说明一下,bp网络的梯度求解方法,可以通过derta直接算出来,也可以通过链导法则算出来。这个代码是基于链导法则的,每次求出一个导数,然后通过链导法则相乘得到下一个的导数,直至乘到L对W的导数,算出其导数就很容易对它进行跟新了。
grad_h[h<0] = 0 # relu激活函数求导
#这里是由于激活函数是用的ReLU 函数,对它求导只会留下大于0的部分,小于0的部分取0,大于0 的部分就是其本身