美文网首页机器学习程序员机器学习与数据挖掘
小白也能看懂的BP反向传播算法之Further into Bac

小白也能看懂的BP反向传播算法之Further into Bac

作者: 六尺帐篷 | 来源:发表于2018-01-26 14:01 被阅读136次

    本文相关代码可以从Backpropagation下载

    在上一篇文章小白也能看懂的BP反向传播算法之Let's practice Backpropagation,我们计算了一个带sigmoid函数的嵌套网络的反向传播!从这篇文章开始,我们正式进入实际的神经网络的反向传播!本文将以一个两层的神经网络结构为例子,并且利用矩阵的方法实现神经网络的反向传播训练算法!

    Lets get started!!!

    神经网络的结构如下:


    image.png

    上图的神经网络包括两层网络。第一层是输入层,包括三个神经元,第二层也就是输出层,包括了两个神经元。标准的神经网络中,sigmoid层也就是激活函数,是输出层的一部分,这里为了反向传播时计算微分更直观,就将其分开!

    对于不了解基本神经网络的同学可以参考,对于不了解激活函数的同学可以参考‘神经网络’初探

    我们下面来分析这个神经网络。首先,三个输入值被输入到输入层的三个节点中,因此我们的输入,用矩阵表示,应该是三维的。然后输入层将和各自的权重相乘,得到输出层,这里和权重的相乘,可以简化成矩阵的乘法运算。然后再输入到sigmoid函数中,进行激活计算,得到一个0-1之间的输出值。最后输出到cost function中,进行误差的计算,这里的cost function可以选取不同的计算函数,这里我们用交叉熵函数作为代价函数, cross-entropy 。单纯对于研究反向传播来说,我们都可以不需要知道这些一层层的函数是干嘛的,因为我们反向传播要求的只是微分而已。只要这些函数是可微的,不管结构在复杂,无非是链式求导的时候多求几个微分而已!反向传播的本质就是在微分的计算!

    Aim

    误差当然是越小越好,所以我们训练网络的目标是将cost function的值减小,这和我们之前几篇文章将输出结果增加正好是相反的,其实也很简单,只需要在更新的时候,减去步长和微分的乘积就行,将之前的+变成-!具体可以参考梯度下降法
    这里我们要更新的是权重的值,所以更新的方法如下:


    image.png

    这里的Wij代表,第i个输入节点到第j的输出节点的权重!
    只需要求出costfunction关于每个权重的微分即可!

    首先,我们自然要先进行正向传播,也就是正向计算最后的输出cost function!

    Forward Propagation

    首先,我们将输入矩阵化,就是一个1*3的矩阵:


    image.png

    如果我们有多个样本的输入值,比如有n个输入,那么输入矩阵就可以写成n*3的矩阵!

    权重矩阵如下:


    image.png

    然后输入层到输出层的计算,就可以简化成,两个矩阵的相乘:


    image.png

    正好得到一个 1x2 的矩阵,对应输出层的两个神经元,符合我们的预期,然后我们给第一个输出层的神经元标记y1,给第二个神经元标记为y2。


    image.png

    然后再进行激活函数的计算


    image.png

    Cost function
    得到输出层的输出并进行激活函数计算之后,就要输入到cost function中计算errors!。这里我们采用的交叉熵代价函数, cross-entropy

    image.png

    这里p是预期的值,q是我们经过神经网络计算得到的预测值,具体交叉熵函数的意义,可以参考 cross-entropy

    image.png
    而我们只要知道我们要将C的值降低,利用反向传播算法,降低C的输出,所以我们就要求得C的微分,首先我们把C展开:
    image.png

    然后将我们网络中计算得到的输出层的输出带入进去:


    image.png

    这样我们就分析完了怎么进行这个神经网络的正向传播!

    Backpropagation

    反向传播之前,我们先回顾一下,每一层的输出结果

    激活函数层的输出结果


    image.png

    输出层的输出结果


    image.png

    权重矩阵


    image.png

    输入层


    image.png

    明确了每层的值之后,我们要切记,我们反向传播所需的就是关于权重的微分,也就是


    image.png

    也就是我们要想办法求出C关于各个权重的微分!
    求微分的基本思路和之前是一样的,不管网络的结构多复杂,根本都是利用链式法则,一层层的从输出求导到输入!这里,我们会采取矩阵的算法来进行微分的求解,这可以让我们的求解方法更适合于编写程序,并且更直观!

    首先我们看输出C是关于sigmoid层的输出y0的函数,然后y0又是关于输出层的输出y的函数,y同时又是输入层x与权重相乘而得来的。所以,基本就明确了,我们需要先求取C关于y0的微分,再求取y0关于y的微分,然后求取y关于w的微分,最后又链式法则相乘在一起,就得到了C关于w的微分!

    • 首先从cost function到sigmoid layer


      image.png

    我们可以很容易写出微分:


    image.png

    写成矩阵的形式


    image.png
    • 从sigmoid层到输出层的微分,就是求取sigmoid函数的微分


      image.png

    变成矩阵的形式就是:


    image.png
    • 从输出层到输入层的微分就是关于权重的微分,我们先看y关于权重的形式


      image.png

    从这个形式不难得出关于权重的微分就是:


    image.png

    这样我们就可以运用链式法则,求取C关于权重W的微分了:


    image.png

    将每个微分的值带入:


    image.png

    将六个微分全部求取出来就是:


    image.png

    不难写成矩阵的形式:


    image.png

    这里T代表矩阵的转置,X代表矩阵的乘法,圆圈加点代表矩阵对应元素相乘,也就是element-wise product。

    最后,我们就可以得到完整的权重更新的法则:


    image.png

    根据以上计算出的更新法则,编写python代码就很直观了

    import numpy as np
    
    
    def sigmoid(x):
        return 1/(1+np.exp(-x))
    
    
    def derivative_sigmoid(x):
        return np.multiply(sigmoid(x), (1-sigmoid(x)))
    
    
    # initialization
    # X : 1*3
    X = np.matrix("2, 4, -2")
    # W : 3*2
    W = np.random.normal(size=(3, 2))
    # label
    ycap = [0]
    # number of training of examples
    num_examples = 1
    # step size
    h = 0.01
    # forward-propogation
    y = np.dot(X, W)
    y_o = sigmoid(y)
    # loss calculation
    loss = -np.sum(np.log(y_o[range(num_examples), ycap]))
    print(loss)     # outputs 3.6821105514(for you it would be different due to random initialization of weights.)
    # backprop starts
    temp1 = np.copy(y_o)
    # implementation of derivative of cost function with respect to y_o
    temp1[range(num_examples), ycap] = 1 / -(temp1[range(num_examples), ycap])
    temp = np.zeros_like(y_o)
    temp[range(num_examples), ycap] = 1
    # derivative of cost with respect to y_o
    dcost = np.multiply(temp, temp1)
    # derivative of y_o with respect to y
    dy_o = derivative_sigmoid(y)
    # element-wise multiplication
    dgrad = np.multiply(dcost, dy_o)
    dw = np.dot(X.T, dgrad)
    # weight-update
    W -= h * dw
    # forward prop again with updated weight to find new loss
    y = np.dot(X, W)
    yo = sigmoid(y)
    loss = -np.sum(np.log(yo[range(num_examples), ycap]))
    print(loss)     # 3.45476397276 outpus (again for you it would be different!)
    

    运行程序,就会看到,进行反向传播,C的值也就是代价函数减少了!(由于初始权重是随机生成的,所以每次运行结果就不尽相同,但可以确定的,反向传播后的输出结果相对之前一定是减小的)

    待续

    这篇文章将会在此结束!我们已经成功将反向传播的计算扩展到真实的两层的神经网络中,并且将计算过程矩阵化!下一篇就是反向传播算法的终结篇,将会实现一个多层的神经网络的反向传播,并且运用动态规划算法对反向传播中微分的计算进行优化!

    本文相关代码可以从Backpropagation下载

    相关文章

      网友评论

        本文标题:小白也能看懂的BP反向传播算法之Further into Bac

        本文链接:https://www.haomeiwen.com/subject/anmuaxtx.html