numpy手撸神经网络(手写公式推导)

任务: 根据前三个人是否看电影的意愿,预测小强是否去看电影

在这里插入图片描述
在这里插入图片描述
手推正向传播和反向传播的公式:

在这里插入图片描述

我们直接根据前4行数据,可以很明确的分析出只要如花去看电影,小明必然去看电影,下面我们根据上图的推导,用程序实现这一预测过程

import numpy as np


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


def main():
    x = np.array([[0,0,1],
                  [1,1,1],
                  [1,0,1],
                  [0,1,1]])
    y = np.array([[0,1,1,0]]).T
    np.random.seed(1)  # 随机种子,使得每次随机产生的weight都是一样的
    weights = 2 * np.random.random((3,1)) - 1  # 随机生成(-1,1)的3*1的向量
    learning_rate = 10
    # 训练网络
    for it in range(1000):
        # 根据三个人的意愿,计算第四个人的意愿
        z = np.dot(x, weights)
        # 利用sigmoid函数,将第四个人的意愿转换到(0,1)
        y_hat = sigmoid(z)
        # 对比实际值与预测值,计算误差,实际上是MSE的对y_hat的导数
        error = y_hat - y
        # 计算梯度,也即激活函数的导数
        slope = y_hat * (1 - y_hat)
        delta = error * slope  # 计算增量
        # 更新三个人对第四个人的权重
        weights -= learning_rate * x.T.dot(delta)
    print(weights)


if __name__ == '__main__':
    main()

在这里插入图片描述
通过一个神经元预测,我们可以看到如花对小强的影响最大(正相关),而小倩和小明对小强的影响是负相关。

重构代码

import numpy as np


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


# 正向传播,根据输入值以及当前权重计算预测值
def fp(x, weights):
    # 根据三个人的意愿,计算第四个人的意愿
    z = np.dot(x, weights)
    # 利用sigmoid函数,将第四个人的意愿转换到(0,1)
    y_hat = sigmoid(z)
    return y_hat


# 根据真实值以及预测值计算权重的增量
def bp(y, y_hat):
    # 对比实际值与预测值,计算误差,实际上是MSE的对y_hat的导数
    error = y_hat - y
    # 计算梯度,也即激活函数的导数
    slope = y_hat * (1 - y_hat)
    delta = error * slope  # 计算增量
    return delta


def main():
    x = np.array([[0,0,1],
                  [1,1,1],
                  [1,0,1],
                  [0,1,1]])
    y = np.array([[0,1,1,0]]).T
    np.random.seed(1)  # 随机种子,使得每次随机产生的weight都是一样的
    weights = 2 * np.random.random((3, 1)) - 1  # 随机生成(-1,1)的3*1的向量
    learning_rate = 10
    # 训练网络
    for it in range(1000):
        y_hat = fp(x, weights)
        delta = bp(y, y_hat)
        # 更新三个人对第四个人的权重,朝着梯度的反方向更新
        weights -= learning_rate * x.T.dot(delta)
    print(weights)


if __name__ == '__main__':
    main()

下面我们根据已经训练好的模型(即weight),预测三个人中只有一个人去,对小强的影响

	print(fp([[1, 0, 0]],weights))  # 如花去
    print(fp([[0, 1, 0]], weights))  # 小倩去
    print(fp([[0, 0, 1]], weights))  # 小明去

在这里插入图片描述
很明显,基本上如花去,小强是必去的

上面预测结果是由如花一个人决定的,因此可以使用一个神经元进行预测,然而下面的例子则不能使用一个神经元进行预测

在这里插入图片描述
分析一下,可以发现小强是否去看电影,取决于如花和大美两个人,是她们意愿进行亦或后得到小强是否去看电影,然而 线性模型不能解决异或问题,此时我们引入 隐藏层,得到如下神经网络:

在这里插入图片描述
权重的矩阵表示:

在这里插入图片描述

关于前向传播(Forward Propagation)

在这里插入图片描述
其中:

  1. z z z 表示所有的输入值与对应权值的乘积和偏置值的和,即
    z = w ∗ x + b z=w*x+b z=wx+b
  2. ∂ z ∂ w = − 1 \frac{\partial z}{\partial w}=-1 wz=1 表示 z z z 对权值的偏导数就等于对应的输入值
  3. 同理, z z z 对输入的值求偏导的结果就是对应的权值

关于反向传播(Backward Propagation)

在这里插入图片描述
在这里插入图片描述
其中:

  1. l l l 表示位于最末端的某个神经元的输出与真实值的距离(即损失
  2. ∂ a ∂ z \frac{\partial a}{\partial z} za 表示激活函数的输出值对输入值的导数, a a a表示激活函数输出值, z z z表示激活函数输入值,若 a = σ ( z ) a=\sigma(z) a=σ(z),则 ∂ a ∂ z = σ ′ ( z ) \frac{\partial a}{\partial z}=\sigma'(z) za=σ(z)
  3. ∂ z ′ ∂ a \frac{\partial z'}{\partial a} az 表示中间某时刻,输入值与对应权值的乘积和偏置值的和对某个输入的偏导数,其结果就是一个权重 z ′ z' z 表示中间某时刻,输入值与对应权值的乘积和偏置值的和, a a a 表示中间某时刻的一个输入值,由图可知 a a a 同时也表示上一个神经元的输出

于是我们得到 ∂ l ∂ z \frac{\partial l}{\partial z} zl计算公式

在这里插入图片描述
可画出下图进行理解:

在这里插入图片描述

当图中最后一层不是输出层时,我们递归的求 ∂ l ∂ z \frac{\partial l}{\partial z} zl,直到到达输出层

在这里插入图片描述
当图中最后一层就是输出层时

在这里插入图片描述
在这里插入图片描述

最后将上面两个式子代入 ∂ l ∂ z \frac{\partial l}{\partial z} zl的计算公式即可

到这里,我们计算出了 ∂ l ∂ z \frac{\partial l}{\partial z} zl,而 ∂ l ∂ w = ∂ l ∂ z ∗ ∂ z ∂ w \frac{\partial l}{\partial w}=\frac{\partial l}{\partial z}*\frac{\partial z}{\partial w} wl=zlwz ∂ z ∂ w = a \frac{\partial z}{\partial w}=a wz=a

我们更新权值的表达式为: w = w − l r ∗ ∂ l ∂ w w=w-lr*\frac{\partial l}{\partial w} w=wlrwl,所以到这里反向传播的更新权值的工作就完成了。

本例手推公式:

在这里插入图片描述

更多详细的公式推导参考:反向传播算法详细推导

由此,我们写出代码:

import numpy as np


np.random.seed(1)  # 随机种子,使得每次随机产生的weight都是一样的
w1 = 2 * np.random.random((3, 4)) - 1  # 表示3个输入,4个神经元
w2 = 2 * np.random.random((4, 1)) - 1  # 表示4个神经元,1个输出


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


def derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))


# 前向传播,根据输入值以及当前权重,计算第四个人的意愿值
def fp(x):
    z1 = np.dot(x, w1)  # 汇聚第一层的输入 (8, 4) = (8, 3) * (3, 4)
    l1 = sigmoid(z1)  # 经过第一层激活函数
    z2 = np.dot(l1, w2)  # 汇聚第二层的输入 (8, 1) = (8, 4) * (4, 1)
    l2 = sigmoid(z2)  # 经过第二层激活函数
    return l1, l2


# 反向传播,根据第四个人实际的意愿值以及预测的值,计算增量,以便于更新权重
# l1,l2指第一层和第二层神经网络输出
def bp(y, x, l1, l2):
    l2_error = l2 - y  # 实际上是MSE对第二层神经元输出的导数 (8, 1)
    l2_slope = l2 * (1 - l2)  # 实际上是sigmoid函数的输出对输入的导数 (8, 1)和(8, 1)的向量每一位对应相乘
    l2_delta = np.dot(l1.T, l2_error * l2_slope)  # (4, 1) = (4, 8) * (8, 1)

    l1_error = np.dot(l2_error * l2_slope, w2.T)  # 实际上是MSE对第一层神经元输出的导数 (8, 4) = (8, 1) * (1, 4)
    l1_slope = l1 * (1 - l1)  # (8, 4)
    l1_delta = np.dot(x.T, l1_error * l1_slope)  # (3, 4) = (3, 8) * (8, 4)

    return l1_delta, l2_delta


def main():
    global w1
    global w2
    x = np.array([[0, 0, 1],
                  [0, 1, 1],
                  [1, 0, 1],
                  [1, 1, 1],
                  [0, 0, 0],
                  [0, 1, 0],
                  [1, 0, 0],
                  [1, 1, 0]])
    y = np.array([[0, 1, 1, 0, 0, 1, 1, 0]]).T

    learning_rate = 10
    # 训练网络
    for it in range(1000):
        l1, l2 = fp(x)  # 前向传播,得到第一层和第二层的输出
        l1_delta, l2_delta = bp(y, x, l1, l2)  # 反向传播,得到增量,用于更新权重
        w2 -= learning_rate * l2_delta
        w1 -= learning_rate * l1_delta


if __name__ == '__main__':
    main()

预测当如花去、大美不去看电影时,小强去的概率:

	print(fp([[1, 0, 0]])[1])

在这里插入图片描述
很显然,训练的模型满足异或关系。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bugcoder-9905

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

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

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

打赏作者

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

抵扣说明:

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

余额充值