反向传播(BP)算法的推导与前馈神经网络的实现

1. BP算法的推导

考虑前馈神经网络, 即整个网络中不存在反馈.



如图所示, w_ji表示当前层神经元 j 的来自上层神经元 i 的输入的权值.
设样本集为D, 输出集是outputs, 标签值和预估值分别用 t 和 o 表示. 对于回归问题, 整个网络的损失函数定义为:

E(w) = \frac{1}{2}\sum_{d{\in}D}\sum_{k{\in}outputs}(t_{kd}-o_{kd})^2
由于每个样本都是对称的, 于是考虑单个样本d的损失函数:

E_d(w) = \frac{1}{2}\sum_{k{\in}outputs}(t_{k}-o_{k})^2
记以下符号:

net_j=\sum_iw_{ji}x_{ji}: 当前层神经元 j 的输入的加权和
Downstream(j): 所有输入含有神经元 j 的下一层神经元

欲更新各w_ji, 需要求
{\Delta}w_{ji}=\frac{\partial{E_d}}{\partial{w_{ji}}}
由链式求导法则
\frac{\partial{E_d}}{\partial{w_{ji}}}=\frac{\partial{E_d}}{\partial{net_j}}\frac{\partial{net_j}}{\partial{w_{ji}}}=\frac{\partial{E_d}}{\partial{net_j}}x_{ji}
定义误差项\ \ {\delta}_j=\frac{\partial{E_d}}{\partial{net_j}}
设激活函数为sigmoid函数\sigma: {\sigma}'={\sigma}(1-{\sigma}).

1.1 输出层的梯度

对回归问题而言, 输出层不再作用激活函数. 有

{\delta}_k = \frac{\partial{E_d}}{{\partial}o_k}\frac{{\partial}o_k}{\partial{net_k}}=\frac{\partial{E_d}}{{\partial}o_k}=\frac{\partial}{{\partial}o_k}\frac{1}{2}\sum_{j{\in}outputs}(t_{j}-o_{j})^2=-(t_{k}-o_{k})
{\Delta}w_{ki}=\frac{\partial{E_d}}{\partial{w_{ki}}}={\delta}_kx_{ki}

1.2 隐藏层的梯度

隐藏层误差项的求解会借助下一层神经元, 根据输出层的求解结果计算最后一层隐藏层, 然后依次向后计算, 故称为反向传播.

\delta_h=\frac{\partial{E_d}}{\partial{net_h}}=\sum_{k{\in}Downstream(h)}\frac{\partial{E_d}}{\partial{net_k}}\frac{\partial{net_k}}{\partial{net_h}}
=\sum_{k{\in}Downstream(h)}\frac{\partial{E_d}}{\partial{net_k}}\frac{\partial{net_k}}{\partial{o_h}}\frac{\partial{o_h}}{\partial{net_h}}
=\sum_{k{\in}Downstream(h)}\delta_kw_{kh}\frac{\partial{\sigma(net_h)}}{\partial{net_h}}
=o_h(1-o_h)\sum_{k{\in}Downstream(h)}\delta_kw_{kh}
{\Delta}w_{hi}=\frac{\partial{E_d}}{\partial{w_{hi}}}=\frac{\partial{E_d}}{\partial{net_h}}\frac{\partial{net_h}}{\partial{w_{hi}}}={\delta}_hx_{hi}

1.3 BP算法流程

初始化参数. 对每个训练样本(x, t):

  1. 前向传播一次, 求每个神经元 p的输出 o_p
  2. 对每个输出层神经元\ k, 求误差项\ \delta_k:

\delta_k\ {\leftarrow}\ -(t_{k}-o_{k})

  1. 对每个隐藏层神经元\ h, 求误差项\ \delta_h:

\delta_h\ {\leftarrow}\ o_h(1-o_h)\sum_{k{\in}Downstream(h)}\delta_kw_{kh}

  1. 更新参数 w_{ji}:
    w_{ji}:=w_{ji}-\eta\Delta{w_{ji}}=w_{ji}-\eta\delta_jx_{ji}

2. 简单前馈神经网络的实现

基于mini-batch, 即每次随机选取一定数量(batch_size)的样本来更新参数.

class SimpleFeedForwardNetwork(object):
    """mini-batch based feedforward neural network"""
    def __init__(self, input_units, hidden_units, output_units=1):
        self._n_input_units = input_units
        self._n_hidden_units = copy.deepcopy(hidden_units)
        self._n_output_units = output_units

        self._weights = list()
        self._bias = list()

        # 用正态分布初始化每层连接的参数
        input_nums = [self._n_input_units] + self._n_hidden_units
        output_nums = self._n_hidden_units + [self._n_output_units]
        for in_units, out_units in zip(input_nums, output_nums):
            self._weights.append(np.random.randn(in_units, out_units))
            self._bias.append(np.random.randn(out_units))

    def fit(self, X, y, batch_size, epochs, eta):
        input_idx = np.arange(X.shape[0])
        evaluate_res = list()
        for epoch in range(epochs):
            np.random.shuffle(input_idx)
            for i in range(0, input_idx.shape[0], batch_size):
                batch_idx = input_idx[i: i+batch_size]
                batch_size = batch_idx.shape[0]

                delta_weights, delta_bias = self._backword(X[batch_idx], y[batch_idx])
                for idx, (delta_w, delta_b) in enumerate(zip(delta_weights, delta_bias)):
                    self._weights[idx] -= eta / batch_size * delta_w
                    self._bias[idx] -= eta / batch_size * delta_b

            evaluate_res.append(self.evaluate(X, y))
            sys.stdout.write('Epoch {}: {}\n'.format(epoch, evaluate_res[-1]))

    def predict(self, X):
        layer_outs = self._forward(X)
        return layer_outs[-1]

    def evaluate(self, X, y):
        y_pred = self.predict(X)
        return np.square(y - y_pred).sum()

    def _activation(self, z):
        return 1 / (1 + np.exp(-z))

    # 前向传播过程, 求各层输出
    def _forward(self, inputs):
        layer_outs = [inputs]
        for w, b in zip(self._weights[:-1], self._bias[:-1]):
            net = np.dot(layer_outs[-1], w) + b
            layer_outs.append(self._activation(net))

        layer_outs.append(np.dot(layer_outs[-1], self._weights[-1]) + self._bias[-1])
        return layer_outs

    # 反向传播过程, 更新参数
    def _backword(self, inputs, outputs):
        # 1. 前向传播求预估值
        layer_outs = self._forward(inputs)

        delta_weights = [np.zeros(w.shape) for w in self._weights]
        delta_bias = [np.zeros(b.shape) for b in self._bias]

        # 2. 计算输出层误差项和梯度
        delta = layer_outs[-1] - outputs
        delta_weights[-1] = np.dot(layer_outs[-2].T, delta)
        delta_bias[-1] = delta.sum()

        # 3. 计算隐藏层误差项和梯度
        for h in range(2, len(self._weights)+1):
            delta = np.dot(delta, self._weights[-h + 1].T) * layer_outs[-h] * (1 - layer_outs[-h])
            delta_weights[-h] = np.dot(layer_outs[-h - 1].T, delta)
            delta_bias[-h] = delta.sum()

        return delta_weights, delta_bias
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,820评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,648评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,324评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,714评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,724评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,328评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,897评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,804评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,345评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,431评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,561评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,238评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,928评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,417评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,528评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,983评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,573评论 2 359

推荐阅读更多精彩内容

  • 最近这段时间系统性的学习了BP算法后写下了这篇学习笔记,因为能力有限,若有明显错误,还请指出 目录 什么是梯度下降...
    曾梓华阅读 34,087评论 19 144
  • BP神经网络现在来说是一种比较成熟的网络模型了,因为神经网络对于数字图像处理的先天优势,特别是在图像压缩方面更具有...
    云时之间阅读 6,703评论 14 11
  • 引言 机器学习栏目记录我在学习Machine Learning过程的一些心得笔记,涵盖线性回归、逻辑回归、Soft...
    hfk阅读 4,367评论 4 18
  • 温峤来到王敦身边,他暗中观察,发现王敦已有谋反之意,当务之急是要搜集更多的情报能够回去让朝廷有所准备。于是温峤便假...
    寒七琪阅读 546评论 0 1
  • 女人决定辞职了,辞职之前找医生做了痔疮手术,从看病到住院都是一个人操持,就像很久很久以前一样,只是现在多了些牵挂,...
    唔溜56阅读 239评论 0 0