使用numpy实现两层神经网络
1. 网络结构及实现思路
以下为次此依赖的的网络结构,并且后边的所有思路及编程都将遵循这个结构给出。
其中隐藏层、激活函数(采用Relu函数)、输出层的内容如下:
- h = x w 1 h=xw_1 h=xw1
- a = m a x ( 0 , h ) a=max(0,h) a=max(0,h)
- y ^ = a w 2 \hat{y}=aw_2 y^=aw2
其整体实现思路如下:
1. 定义神经网络参数
2. 实现前馈神经网络 forward pass
3. 计算损失 loss
4. 实现反向传播 backword pass
2.编程实现及公式推导
# @Time : 2020/5/7 23:25
# @Author : kingback
# @File : model_practice.py
# @Software: PyCharm
## 使用numpy实现两层神经网络
import torch
import numpy as np
#一个全连接的Relu神经网络,一个隐藏层,没有bias,用来从x预测y:
#这一实现的三大过程:
#1.前馈神经网络 forword pass
#2.计算损失 loss
#3.反向传播 backword pass
#神经网络参数定义:
N,D_in,D_out,H=64,1000,10,100
# N:代表64个人
# D_in:代表每个人输入到神经网络1000个数据点
# D_out:代表每个人从神经网络输出10个数据点
# H:代表该神经网络含有100个隐藏层
x=np.random.randn(N,D_in)
#定义输入到神经网络之前的数据矩阵,大小为64*1000
y=np.random.randn(N,D_out)
#定义从神经网络输出的的数据矩阵,大小为64*10
w_1=np.random.randn(D_in,H)
#大小为1000*100
w_2=np.random.randn(H,D_out)
#大小为100*10
#定义学习率 learning rate
learning_rate=1e-06
for it in range(500):
#forword pass
h=x.dot(w_1)
#numpy中的点乘np.dot是数学意义上的向量内积
#print(h.shape)
#打印矩阵维度信息
h_relu=np.maximum(h,0)
#定义relu 函数,是目前深度神经网络中经常使用的激活函数。
y_hat=h_relu.dot(w_2)
#大小为64*10
#计算损失compute loss
loss=np.square(y_hat-y).sum()
#估计值与真实值之间差值的平方和再取和
print(it,loss)
#计算梯度,主要是对(y_hat-y)^2求各项偏导
y_hat_grad=2*(y_hat-y)
w_2_grad=h_relu.T.dot(y_hat_grad)
h_relu_grad=y_hat_grad.dot(w_2.T)
h_grad=h_relu_grad.copy()
h_grad[h<0]=0
w_1_grad=x.T.dot(h_grad)
#更新w_1和w_2的权值
w_1=w_1-learning_rate*w_1_grad
w_2=w_2-learning_rate*w_2_grad
这里需要说明的一点就是在计算各参数的梯度问题时,是在估计值与真实值之间差值的平方的基础上进行求偏导的,我们令其为A,则有:
A
=
(
y
^
−
y
)
2
A=(\hat{y}-y)^2
A=(y^−y)2
易得:
∂
A
∂
y
^
=
2
⋅
(
y
^
−
y
)
\frac{\partial{A}}{\partial{\hat{y}}}=2\cdot(\hat{y}-y)
∂y^∂A=2⋅(y^−y)
后面几个偏导的计算就很容易写错了:
∂
A
∂
w
2
=
a
T
⋅
∂
A
∂
y
^
\frac{\partial{A}}{\partial{w_2}}=a^T\cdot\frac{\partial{A}}{\partial{\hat{y}}}
∂w2∂A=aT⋅∂y^∂A
∂
A
∂
a
=
∂
A
∂
y
^
⋅
w
2
T
\frac{\partial{A}}{\partial{a}}=\frac{\partial{A}}{\partial{\hat{y}}}\cdot w_2^T
∂a∂A=∂y^∂A⋅w2T
因为
a
=
m
a
x
(
0
,
h
)
a=max(0,h)
a=max(0,h),则有:
a
=
m
a
x
(
0
,
h
)
=
{
h
h
≥
0
0
h
<
0
a=max(0,h)=\left\{\begin{aligned}h \qquad h\geq0 \\ 0 \qquad h<0\\ \end{aligned}\right.
a=max(0,h)={hh≥00h<0
所以对h求偏导则有:
∂
A
∂
h
=
{
∂
A
∂
a
⋅
1
h
≥
0
∂
A
∂
a
⋅
0
h
<
0
\frac{\partial{A}}{\partial{h}}=\left\{\begin{aligned} {\frac{\partial{A}}{\partial{a}} } \cdot1 \qquad h\geq0 \\ {\frac{\partial{A}}{\partial{a}} }\cdot0 \qquad h<0\\ \end{aligned}\right.
∂h∂A=⎩⎪⎪⎨⎪⎪⎧∂a∂A⋅1h≥0∂a∂A⋅0h<0
最后我们再对w_1求偏导:
∂
A
∂
w
1
=
x
T
⋅
∂
A
∂
h
\frac{\partial{A}}{\partial{w_1}}=x^T\cdot\frac{\partial{A}}{\partial{h}}
∂w1∂A=xT⋅∂h∂A
有了上边的推导,相信大家很容易就理解代码了吧。嘿嘿嘿
3.结果测试
在结果中我们可以看到,在程序刚开始时和程序结束时的loss值有了明显减小:
程序开始时loss值 | 程序结束时loss值 |
---|---|
![]() | ![]() |
可以看到程序在训练了500次的情况下,我们的这个两层的神经网络效果还是比较不错的。接下来我也将会继续将该模型改写为torch版本的。