学习反向传播算法

参考这里,请点击

最近有个项目,需要用到深度学习,为此学了一点神经网络,而反向传播算法为神经网络中最基础也最重要的算法,特此编辑出来,记录自己的学习经历。
从最早的模式识别(Pattern Recongnition)时期开始,研究者的目标就是用可训练的多层网络取代人工特征工程。但该解决方案并没有得到广泛认可,直到上世纪80年代中期,多层架构可以通过SGD训练。只要模块是其输入金额内部权值的相对平滑函数,就可以使用反向传播步骤计算梯度。在20世纪七八十年代,几个不同的研究小组分别独立发现该思路可行且的确可用。如下图所示
这里写图片描述

从输入单元到第一个隐层H1计算如下:

对H1层的每个单元 j j j,其值 y j = f ( z j ) , z j = ∑ i ( w i j x i ) y_j=f(z_j),z_j=\sum_{i}(w_{ij}x_i) yj=f(zj),zj=i(wijxi),其中 i i i取值遍历所有输入层节点, z j z_j zj是对前一层所有节点的加权和,这里省略了偏置项。网络中使用非线性函数 f f f z j z_j zj进行非线性变换,得到改成输出 y j y_j yj

从H1到H2计算如下:

对H2层的每个单元 k k k,其值 y k = f ( z k ) , z k = ∑ j w j k y j y_k=f(z_k),z_k=\sum_{j}w_{jk}y_j yk=f(zk),zk=jwjkyj,其中 j j j遍历去所有H1层节点

从H2层到输出层计算如下

对输出层的每个单元 l l l,其值 y l = f ( z l ) , z l = ∑ k w k l y k y_l=f(z_l),z_l=\sum_{k}w_{kl}y_k yl=f(zl),zl=kwklyk,其中k取值遍历所有H2层节点

每层只取其中一个节点进行演算
每层首先计算相对于改成输出节点的误差梯度,即所有来自相对于后一层输入节点的误差梯度的加权和。之后使用链式法则将误差梯度传递至盖层输入节点,输出单元的误差梯度通过对代价函数的求导得到,假设输出单元 l l l对应的代价函数项为 E = ( y l − t l ) 2 E=(y_l-t_l)^2 E=(yltl)2,其中 t l t_l tl为期望输出值。可计算相对于 y l y_l yl的偏导数为 y l − t l y_l-t_l yltl。由于 y l = f ( z l ) y_l=f(z_l) yl=f(zl),所以代价函数相对于 z l z_l zl的偏导数为:

∂ E ∂ z l = ∂ E ∂ y l ∂ y l ∂ z l = ( y l − t l ) f ′ ( z l ) \dfrac{\partial{E}}{\partial{z_l}}=\dfrac{\partial{E}}{\partial{y_l}}\dfrac{\partial{y_l}}{\partial{z_l}}=(y_l-t_l)f^{'}(z_l) zlE=ylEzlyl=(yltl)f(zl)

从输出单元到第二个隐层H2计算如下:

对H2层的每个单元 k k k,其误差梯度为 ∂ E ∂ y k = ∑ l ∂ E ∂ z l ∂ z l ∂ y k = ∑ l w k l ∂ E ∂ z l \dfrac{\partial{E}}{\partial{y_k}}=\sum_{l} \dfrac{\partial{E}}{\partial{z_l}}\dfrac{\partial{z_l}}{\partial{y_k}}=\sum_{l}w_{kl}\dfrac{\partial{E}}{\partial{z_l}} ykE=lzlEykzl=lwklzlE,其中 l l l取值遍历所有输出层节点

同理,可得出H1层的误差梯度为:

∂ E ∂ y j = ∑ k ∂ E ∂ y k ∂ y k ∂ z k ∂ z k ∂ y j = ∑ k ∂ E ∂ y k ∂ y k ∂ z k w j k \dfrac{\partial{E}}{\partial{y_j}}=\sum_{k}\dfrac{\partial{E}}{\partial{y_k}}\dfrac{\partial{y_k}}{\partial{z_k}}\dfrac{\partial{z_k}}{\partial{y_j}}=\sum_{k}\dfrac{\partial{E}}{\partial{y_k}}\dfrac{\partial{y_k}}{\partial{z_k}}w_{jk} yjE=kykEzkykyjzk=kykEzkykwjk,其中 k k k取遍H2层所有节点。

输入层的误差梯度为:

∂ E ∂ x i = ∑ j ∂ E ∂ y j ∂ y j ∂ z j ∂ z j ∂ x i = ∑ j ∂ E ∂ y j ∂ y j ∂ z j w i j \dfrac{\partial{E}}{\partial{x_i}}=\sum_{j}\dfrac{\partial{E}}{\partial{y_j}}\dfrac{\partial{y_j}}{\partial{z_j}}\dfrac{\partial{z_j}}{\partial{x_i}}=\sum_{j}\dfrac{\partial{E}}{\partial{y_j}}\dfrac{\partial{y_j}}{\partial{z_j}}w_{ij} xiE=jyjEzjyjxizj=jyjEzjyjwij,其中 j j j取遍H1层所有节点。

通过上面的公式,可以了解到,反向传播算法的关键一点就是代价函数相对于一个模块输入的导数(或梯度),可以通过目标函数相对于该模块输出的导数反向传播求得。反向传播公式可以重复应用,将梯度从顶层输出(网络产生预测的位置)通过所有模块传递到底层(输入层)。所有这些中间梯度被计算出来后,再计算代价目标函数相对于每个模块内部权值的梯度就非常容易了。以输入层到H1层权值为例,其误差梯度为:

∂ E ∂ w i j = ∂ E ∂ y j ∂ y j ∂ z j ∂ z j ∂ w i j = ∂ E ∂ y j ∂ y j ∂ z j x i \dfrac{\partial{E}}{\partial{w_{ij}}}=\dfrac{\partial{E}}{\partial{y_j}}\dfrac{\partial{y_j}}{\partial{z_j}}\dfrac{\partial{z_j}}{\partial{w_{ij}}}=\dfrac{\partial{E}}{\partial{y_j}}\dfrac{\partial{y_j}}{\partial{z_j}}x_i wijE=yjEzjyjwijzj=yjEzjyjxi

# 创建神经网络类
# nodes为1*n矩阵,表示每一层有多少个节点
class nn():
    def __init__(self, nodes):
        self.layers = len(nodes)
        self.nodes = nodes;
        # 学习率
        self.u = 1.0;
        # 权值
        self.W = list();
        # 偏差值
        self.B = list()
        # 层值
        self.values = list();
        # 误差
        self.error = 0;
        # 损失
        self.loss = 0;

        for i in range(self.layers - 1):
            # 权值初始化,权重范围-0.5~0.5
            self.W.append(np.random.random((self.nodes[i], self.nodes[i + 1])) - 0.5)
            # B值初始化
            self.B.append(0)

        for j in range(self.layers):
            # values值初始化
            self.values.append(0)


###############################################
# 激活函数
def sigmod(x):
    return 1.0 / (1.0 + np.exp(-x))


# 前馈函数
def nnff(nn, x, y):
    layers = nn.layers
    numbers = x.shape[0]
    # 赋予初值
    nn.values[0] = x
    for i in range(1, layers):
        nn.values[i] = sigmod(np.dot(nn.values[i - 1], nn.W[i - 1]) + nn.B[i - 1])
    # 最后一层与实际的误差
    nn.error = y - nn.values[layers - 1]
    nn.loss = 1.0 / 2.0 * (nn.error ** 2).sum() / numbers
    return nn


# BP函数
def nnbp(nn):
    layers = nn.layers;
    # 初始化delta

    deltas = list();
    for i in range(layers):
        deltas.append(0)

    # 最后一层的delta为
    deltas[layers - 1] = -nn.error * nn.values[layers - 1] * (1 - nn.values[layers - 1])
    # 其他层的delta为
    for j in range(1, layers - 1)[::-1]:  # 倒过来
        deltas[j] = np.dot(deltas[j + 1], nn.W[j].T) * nn.values[j] * (1 - nn.values[j])
    # 更新W值
    for k in range(layers - 1):
        nn.W[k] -= nn.u * np.dot(nn.values[k].T, deltas[k + 1]) / (deltas[k + 1].shape[0])
        nn.B[k] -= nn.u * deltas[k + 1] / (deltas[k + 1].shape[0])
    return nn

# 对神经网络进行训练
def nntrain(nn, x, y, iterations):
    for i in range(iterations):
        nnff(nn, x, y)
        nnbp(nn)
    return nn
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值