前言 算法实现的过程,我感觉就是把数学推导公式翻译成代码的过程,关于详细的算法思想介绍,已经写在了上一篇博客中,需要参考的可以点这一个,这里重点是实现BP算法。 一、代码实现
我不啰嗦了,直接上代码了,因为看了理论之后,很容易就能读懂代码,而且每一行代码我都加了详细的注释。

"""
BP算法的简单实现,这里只有三层网络,目的在于说明其执行过程
调试时可以控制输入的迭代次数和学习率,这样可以动态地看执行效果
当迭代次数过大时,会出现过拟合情况,亲测
"""
import numpy as np
def sigmoid(x): #设置激活函数
    return 1 / (1 + np.exp(-x))
def sigmoidDerivationx(y): #计算激活函数的偏微分
    return y * (1 - y)
if __name__ == "__main__":
    alpha = 0.05 # 学习率,一般在(0,0.1)上取值
    numIter = 100000 # 迭代次数
    w1 = [[0.15, 0.20], [0.25, 0.30]]  # 输入层的权重
    w2 = [[0.40, 0.45], [0.50, 0.55]]  # 权重矩阵的维数和输入输出维数有关系 要满足矩阵相乘的条件
    b1 = 0.35 # 初始化偏置 可以已知,也可设置为未知
    b2 = 0.60
    # 你心里应该清楚,实际生产中,输入与输出都是从对应的数据集中获取得到的,这里仅作为演示
    x = [0.05,0.10] # 初始化输入
    y = [0.01,0.99] # 初始化对应的输出label
    z1 = np.dot(w1,x) + b1
    a1 = sigmoid(z1) # 激活函数,第一层激励值
    z2 = np.dot(w2,x) + b2
    a2 = sigmoid(z2) # 同上,第二层激励值
    for n in range(numIter): # 开始迭代
        # 反向传播 使用代价函数为C=1 / (2n) * sum[(y-a2)^2]
        # 最后一层的梯度delta
        delta2 = np.multiply(-(y - a2),np.multiply(a2,1-a2))# 注意delta2这两项乘积的由来,第一个参数就是代价函数对a2求的偏导(实际上得到的就是真实输出值与预测输出值之间的误差而已),第二个参数是激励值的导数,二者相乘最后得梯度的变化值
        # 若不明白这个过程,可以直接记住结论,感兴趣的可以参考相关的详细证明过程
        # 非最后一层的梯度delta(即隐含层),算法与最后一层不一样,因为其没有输出预测值,我们用下一层误差的加权和来代替真实值与预测值的差
        delta1 = np.multiply(np.dot(np.array(w2).T, delta2), np.multiply(a1, 1 - a1))
        # 关键是要明白计算的过程与理论基础
        # 计算完权重的变化后(即delta),更新权重,delta也可以称为梯度的变化
        for i in range(len(w2)):
            w2[i] = w2[i] - alpha * delta2[i] * a1 # 有时候也会除以n 用均值来更新,这里没除,用的是梯度变化总体和来更新的,这两种更新方式可能都会遇到 ,注意一下,各有适应场景。一般来说如果不知道数据集规模,那就用总体和来更新,如果知道了数据集规模,可以除n用均值来更新。
        for i in range(len(w1)):
            w1[i] = w1[i] - alpha * delta1[i] * np.array(x)
        # 继续前向传播,算出误差值
        z1 = np.dot(w1, x) + b1 # 用新的权重值再求一遍激励
        a1 = sigmoid(z1)
        z2 = np.dot(w2, a1) + b2
        a2 = sigmoid(z2)
        print(str(n) + " result:" + str(a2[0]) + ", result:" + str(a2[1]))# 输出迭代后的预测值
        print(str(n) + "  error1:" + str(y[0] - a2[0]) + ", error2:" +str(y[1] - a2[1])) # 输出误差,可以通过改变迭代次数来查看效果

二、相关分析
麻雀虽小五脏俱全,可以通过调试上面代码的参数,动态的观看执行过程以及误差的变化情况。
这个数据集是自己创造的,很小很小,没有挑战性。在看神经网络与深度学习这本书的时候,上面有一个识别手写数字的demo,这个demo也有反向传播实现的版本,是基于经典的mnist数据集来实现的。作者用的是python2.7的环境,而且对于初学者来说,彻底读懂需要下点功夫,我学习的时候,用python3.6重新修改了一遍,而且在源代码的基础上增加了自己的理解注释,已经上传到了我的github,需要的可以参考:https://github.com/GritCoder/BP