12行实现一个简易神经网络

准备工作

现实现一个具有三层的最简单的神经网络,激活函数采用sigmod函数。整体结构如下所示:
结构图

  • x是输入的样本值。设定为5*3的矩阵,即代表5个样本值,每个样本有3个特征值。
  • y是标签,为5*1的矩阵代表每一个样本的标签。且取值范围为0或1。即完成一个简单的分类问题。
  • w0、w1为中间的权重参数。L0、L1、L2表示每一层的计算结果。
  • L2即是最终与标签比对的结果

首先是数据的初始化:

x = np.array(
    [[0, 0, 1],
    [0, 1, 1],
    [1, 0, 1],
    [1, 1, 1],
    [0, 0, 1]]
)
y = np.array([
    [0],
    [1],
    [1],
    [0],
    [0],
])
np.random.seed(1)
# w0 w1取值范围为 -1 到 1
w0 = 2 * np.random.random([3, 4]) - 1
w1 = 2 * np.random.random([4, 1]) - 1

因为神经网络可以大概分为两部分:前向传播与反向传播,且主要难点在反向传播。故要对反向传播进行公式的推导。

反向传播的推导

sigmod函数的定义

首先定义好sigmod函数:

def sigmod(x, deriv = False):
    if(deriv == True):
        return x * (1 - x)
    return 1 / (1 + np.exp(-x))

该函数的deriv参数是为了方便进行反向传播的运算。我们知道sigmod函数具有一个很有意思的特征:
f ( z ) = s i g m o d ( z ) = 1 1 + e − z f(z) = sigmod(z) = \frac{1}{1+e^{-z}} f(z)=sigmod(z)=1+ez1
∂ f ∂ z = f ( z ) ( 1 − f ( z ) ) \frac{\partial f}{\partial z}=f(z)(1-f(z)) zf=f(z)(1f(z))
故当进行反向传播时需要返回sigmod函数的导数,所以定义deriv参数方便返回值的调用。

损失函数

使用均方函数定义误差,如下:
L o s s = 1 2 ( l 2 − y ) 2 Loss = \frac{1}{2}(l_2-y)^2 Loss=21(l2y)2
分别对 w 1 w_1 w1 w 0 w_0 w0求导:
d L o s s d w 1 = ( l 2 − y ) d l 2 d w 1 = ( l 2 − y ) l 2 ( 1 − l 2 ) l 1 \frac{dLoss}{dw_1} = (l_2-y)\frac{dl_2}{dw_1}=(l_2-y)l_2(1-l_2)l_1 dw1dLoss=(l2y)dw1dl2=(l2y)l2(1l2)l1
d L o s s d w 0 = ( l 2 − y ) d l 2 d w 0 = ( l 2 − y ) d l 2 d l 1 d l 1 d w 0 = ( l 2 − y ) l 2 ( 1 − l 2 ) w 1 l 0 l 1 ( 1 − l 0 ) \frac{dLoss}{dw_0} = (l_2-y)\frac{dl_2}{dw_0}=(l_2-y)\frac{dl_2}{dl_1}\frac{dl_1}{dw_0}=(l_2-y)l_2(1-l_2)w_1l_0l_1(1-l_0) dw0dLoss=(l2y)dw0dl2=(l2y)dl1dl2dw0dl1=(l2y)l2(1l2)w1l0l1(1l0)
其实就是简单的复合函数求导,难度较低。
在求得梯度之后就可以进行梯度下降算法更新 w 1 w_1 w1 w 0 w_0 w0参数,进行迭代。

代码编写

设置迭代10万次,每一万次输出一下loss值

for j in range(100000):
    l0 = x
    l1 = sigmod(np.dot(l0, w0))
    l2 = sigmod(np.dot(l1, w1))
    l2_error = (y - l2) # 均方误差的导数值 
    l2_delta = l2_error * sigmod(l2, True) # 导数相乘。对应公式
    l1_error = l2_delta.dot(w1.T) 
    l1_delta = l1_error * sigmod(l1, True)
    w1 += l1.T.dot(l2_delta) # 每次更新梯度值
    w0 += l0.T.dot(l1_delta)
    if j % 10000 == 0:
        print('Error:' + str(np.mean(np.abs(l2_error))))

总结

整体其实还是蛮抽象的,关键在于梯度的计算。
代码里面的每一个变量都能在公式里找到位置,因为最终更新公式太长了所以把一些变量分开写了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangbowj123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值