精华内容
下载资源
问答
  • 反向传播算法代码

    2019-11-18 17:03:12
    def backpropagation(self,y, z_s, a_s): # 反向计算 dw = [] # dl/dW db = [] # dl/db deltas = [None] * len(self.weights) # delta = dl/dz known as error for each layer # insert the last layer error ...

    https://www.jianshu.com/p/9bc46738f00c 原文链接
    def feedforward(self, x): # 正向计算
    # return the feedforward value for x
    a = np.copy(x)
    z_s = []
    a_s = [a]
    for i in range(len(self.weights)):
    activation_function = self.getActivationFunction(self.activations[i])
    z_s.append(self.weights[i].dot(a) + self.biases[i])
    a = activation_function(z_s[-1])
    a_s.append(a)
    return (z_s, a_s)

    def backpropagation(self,y, z_s, a_s): # 反向计算
    dw = [] # dl/dW
    db = [] # dl/db
    deltas = [None] * len(self.weights) # delta = dl/dz known as error for each layer
    # insert the last layer error
    deltas[-1] = ((y-a_s[-1])(self.getDerivitiveActivationFunction(self.activations[-1]))(z_s[-1]))
    # Perform BackPropagation
    for i in reversed(range(len(deltas)-1)):
    deltas[i] = self.weights[i+1].T.dot(deltas[i+1])
    (self.getDerivitiveActivationFunction(self.activations[i])(z_s[i]))
    #a= [print(d.shape) for d in deltas]
    batch_size = y.shape[1]
    db = [d.dot(np.ones((batch_size,1)))/float(batch_size) for d in deltas]
    dw = [d.dot(a_s[i].T)/float(batch_size) for i,d in enumerate(deltas)]
    # return the derivitives respect to weight matrix and biases
    return dw, db

    展开全文
  • BP反向传播算法剖析代码运行过程最后结果可视化 ‘’’ 写在前面: 复习了一边BP算法,照着书上代码研究了一遍,加了些注释。代码看起来还能接受,自己照着写出来的就出现各种问题。代码的实现接近tf底层源码,能更...


    ‘’’
    写在前面:
    复习了一边BP算法,照着书上代码研究了一遍,加了些注释。代码看起来还能接受,自己照着写出来的就出现各种问题。代码的实现接近tf底层源码,能更好理解tf的实现和运行。
    ‘’’
    代码已经添加了完整的注释,具体内容可以依据《TensorFlow深度学习:龙龙老师》书籍查阅。

    代码

    # import tensorflow as tf
    from sklearn.datasets import make_moons 
    from sklearn.model_selection import train_test_split
    import matplotlib.pyplot as plt
    import numpy as np
    '''
    手工实现反向传播算法。数据集 make_moons 两个属性,两个类别
    神经网络四层:输入2节点 , 隐藏层1:25 隐藏层2:50 隐藏层3:25 输出层:2 , 激活函数sigmoid
    '''
    def draw_mse_acc(epoch_sumloss , epoch_acc):
        x=[i for i in range(len(epoch_sumloss))]
        #左纵坐标
        fig , ax1 = plt.subplots()
        color = 'red'
        ax1.set_xlabel('epoch')
        ax1.set_ylabel('loss' , color=color)
        ax1.plot(x , epoch_sumloss , color=color)
        ax1.tick_params(axis='y', labelcolor= color)
    
        ax2=ax1.twinx()
        color1='blue'
        ax2.set_ylabel('acc',color=color1)
        ax2.plot(x , epoch_acc , color=color1)
        ax2.tick_params(axis='y' , labelcolor=color1)
    
        fig.tight_layout()
        plt.show()
    
    
    def draw(X , Y):
        plt.figure(figsize=(16,10))
        plt.scatter(X[:,0] , X[:,1] , c=Y.ravel() , s=40  ,cmap=plt.cm.Spectral, edgecolors='none')
        plt.show()
    
    '''
    数据初始化
    '''
    def  data_init(N_SAMPLES, TEST_SIZE):
        #使用make_moons函数生成数据集
        X , Y = make_moons(n_samples=N_SAMPLES  ,noise=0.2 , random_state=100)
        #切分数据集
        x_train , x_test , y_train , y_test = train_test_split(X , Y , test_size = TEST_SIZE , random_state=42)   
        return X , Y , x_train , y_train , x_test , y_test
    
    
    '''
    单个网络层实现
    '''
    class Layers(object):
        def __init__(self,n_input , n_neurons , activation=None , weights = None , bias = None):
            '''
            n_input表示输入节点数
            n_neurons表示输出节点数
            activation表示激活函数
            weights表示神经网络参数权重矩阵
            bias表示偏置
            '''
            #如果未设置权重,则正态初始化权重W,同时将其压缩在[-1,1]之间
            self.weights = weights if weights is not None else np.random.randn(n_input , n_neurons) * np.sqrt(1/n_neurons) 
            self.bias = bias if bias is not None else np.random.rand(n_neurons)*0.1
            self.activation = activation
            self.out_activation = None
            self.error  = None
            self.delta = None
    
        #激活函数
        #根据初始化的激活函数,将计算的结果经过选择的激活函数处理
        def _apply_activation(self,result):
            if self.activation is  None:
                result = result
            elif self.activation == 'relu':
                result =  np.maximum(result , 0)
            elif self.activation == 'tanh':
                result =  np.tanh(result)
            elif self.activation == 'sigmoid':
                result =  1/(1+np.exp(-result))
            
            return result
    
        #前向传播
        def activate(self , x ):
            
            result  = np.dot(x,self.weights ) +self.bias
            #获得该层网络输出
            self.out_activation=self._apply_activation(result)
            return self.out_activation
    
        #计算从激活函数输出到激活函数输入的导数
        def apply_activation_derivative(self , out):
            if self.activation is None:
                #如果未使用激活函数,则其导数为1。使用ones_like函数返回一个与结果形状一样的矩阵
                result =  np.ones_like(out)
            elif self.activation == 'relu':
                #将激活函数输出的张量转换为array数组,并复制
                grad = np.array(out , copy=True)
                #根据relu激活函数的定义,计算激活函数的导数
                grad[out > 0] = 1
                grad[out <= 0] = 0
                result = grad
            elif self.activation == 'tanh':
                result = 1 - out**2
            elif self.activation == 'sigmoid':
                result =  out*(1-out)
            return result
    
    
    #构建神经网络类
    class net(object):
        def __init__(self):
            self._layers=[]
        
        def add_layer(self , layer):
            self._layers.append(layer)
    
        #网络模型前向计算
        def feed_forward(self , x):
            for layer in self._layers:    
                x = layer.activate(x)  
            return x
            
        #反向传播算法
        def backpropagation(self , X , Y , lr ):
            out = self.feed_forward(X)
            #反向循环
            for i in reversed(range(len(self._layers))):
                #获得当前层对象
                layer = self._layers[i]
                #输出层和隐藏层的梯度计算公式不一样
                if layer == self._layers[-1]:
                    layer.error = Y - out
                    layer.delta = layer.error * layer.apply_activation_derivative(out)
                else:
                    next_layer = self._layers[i+1]
                    '''
                    隐藏层误差:是与最终输出预测相关的,所以从后往前计算时,
                    使用后面一层网络的Δ乘上后面一层网络的权重参数。
                    Δ表示当下输出在该偏导方向与实际值的差距。
                    '''
                    layer.error = np.dot(next_layer.weights , next_layer.delta)
                    layer.delta = layer.error * layer.apply_activation_derivative(layer.out_activation)
    
            #网络参数更新
            #网络当下权重-网络权重Δ*该层网络的输入*学习率
            for i in range(len(self._layers)):
                layer = self._layers[i]
                #如果该网络层为输入层,则该网络层的输入即为原始数据,否则为上一层网络的输出
                o_i = np.atleast_2d(X  if i == 0 else self._layers[i-1].out_activation)
                layer.weights += layer.delta * o_i.T * lr
    
        
        #计算准确率
        def accuracy(self , predict , y):
            
            #最简单计算准确率方法
            '''right_sum = 0
            for i in range(len(predict)):
                if predict[i]==y[i]:
                    right_sum +=1
            acc = right_sum / len(predict)
            '''
            #书上的方法
            '''return np.sum(np.equal(np.argmax(predict, axis=1), y)) / y.shape[0]'''
            #我的方法
            acc = ( len(predict) -sum((predict^y)) ) / len(predict)
            return acc
    
            
        #预测
        def predict(self , dataset):
            pre_result= []
            for i in range(len(dataset)):
                pre = self.feed_forward(dataset[i])
                if pre[0] >=pre[1]:
                    re = 0
                else:
                    re = 1
                pre_result.append(re)
            # return self.feed_forward(dataset)
            return pre_result
    
    
        #训练
        def train(self , x_train , y_train , x_test , y_test , lr , epoch):
            # 训练集进行onehot编码,这一步很精妙。
            #由于只有两个类别,构建一个 行数为:训练集标签数 , 列数为:2的二维数组并以0进行填充
            y_onehot = np.zeros((y_train.shape[0] , 2))
            #由于原始数据标签是一个一维数组,使用np.arange构建一个[0,1,..,训练集标签数]一维数组
            #以该数组作为y_onehot的横坐标,y_train也是一个一维以0和1组成的数组作为纵坐标
            #对y_onehot进行赋值即可完成onehot编码
            y_onehot[ np.arange(y_train.shape[0]) , y_train.astype('int64')] = 1
    
            # y_onehot = y_train.astype('int64')
            mses = []
            accs = []
            for i in range(epoch):
                for j in range(len(x_train)):               
                    self.backpropagation(x_train[j] , y_onehot[j] , lr)
                #每计算10个数据,记录一次误差
                if i%10==0:
                    mse = np.mean(np.square(y_onehot-self.feed_forward(x_train)))
                    acc = self.accuracy(self.predict(x_test),y_test.flatten())*100
                    mses.append(mse)
                    accs.append(acc)
                    print('epoch:%s , MSE:%f'%(i , float(mse)))
                    print('accuracy : %.2f%%'% acc)
            return mses , accs
                    
    if __name__ == "__main__":
        X , Y , x_train , y_train , x_test , y_test = data_init(2000 , 0.3)
        draw(X , Y)
        #创建单层网络
        layer1 = Layers(2,25,'sigmoid')
        layer2 = Layers(25,50 ,'sigmoid')
        layer3 = Layers(50 ,25 ,'sigmoid')
        layer4 = Layers(25 ,2 ,'sigmoid')
        #获取神经网络对象
        nn = net()
        #神经网络模型添加单层网络
        nn.add_layer(layer1)
        nn.add_layer(layer2)
        nn.add_layer(layer3)
        nn.add_layer(layer4)
        #训练并计算准确率和误差
        mse , acc = nn.train(x_train , y_train , x_test , y_test , 0.01 ,1000)
        draw_mse_acc(mse , acc)
    

    运行过程

    在这里插入图片描述

    最后结果可视化

    数据分布

    在这里插入图片描述

    最后结果

    在这里插入图片描述
    代码中有详细注释,难点都已解释完全。公式参照书上介绍。

    展开全文
  • 原文链接:CHAPTER 2 How...反向传播算法最初在 1970 年代被提及,但是人们直到 David Rumelhart、Geoffrey Hinton 和 Ronald Williams 的著名的 1986 年的论文中才认识到这个算法的重要性。 反向传播的核心是一个对...

    原文链接:CHAPTER 2 How the backpropagation algorithm works

    神经网络与机器学习系列文章链接

    反向传播概述

    反向传播算法最初在 1970 年代被提及,但是人们直到 David Rumelhart、Geoffrey Hinton 和 Ronald Williams 的著名的 1986 年的论文中才认识到这个算法的重要性。

    反向传播的核心是一个对代价函数 C 关于任何权重 w 和 偏置 b 的偏导数 \partial{C}/\partial{w} 的表达式。

    这个表达式告诉我们在改变权重和偏置时,代价函数变化的快慢。

    关于代价函数的两个假设

    1. 代价函数可以被写成在每一个训练样本 x 上的代价函数 C_x 的均值 C=\frac{1}{n}\sum_{x} C_x

    2. 代价函数可以写成神经网络输出的函数。

    需要假设1的原因是,反向传播实际上是对一个独立的训练样本计算了 \partial{C_x}/\partial{w}\partial{C_x}/\partial{b}。然后通过在所有训练样本上进行平均化获得 \partial{C}/\partial{w}\partial{C}/\partial{w}

    需要假设2的原因是,要把代价函数与神经网络输出联系起来,进而与神经网络的参数联系起来。

    符号定义

    W_{jk}^{l} 是从 l-1 层的第 k 个神经元到 l 层的第 j 个神经元的权重。

    b_j^l 是第 l 层的第 j 个神经元的偏置。

    a_j^l 是第 l 层的第 j 个神经元的激活值。

    \sigma 是激活函数。

    把上面的符号向量化

    W^{l} 是权重矩阵,第 jk 列的元素是 W_{jk}^{l}

    例如第二层与第三层之间的权重矩阵是

    w^3 = { \left[      \begin{matrix}        w_{11}^3 & w_{12}^3 & w_{13}^3 & w_{14}^3\\        w_{21}^3 & w_{22}^3 & w_{23}^3 & w_{24}^3       \end{matrix} \right] }

    b^l 是偏置向量。第 j 行的元素是 b_j^l

    例如第二层的偏置向量是

    b^2 = { \left[      \begin{matrix}        b_{1}^2  \\        \\        b_{2}^2\\        \\        b_{3}^2\\        \\        b_{4}^2       \end{matrix}       \right] }

    有了这些表示 l 层的第 j 个神经元的激活值 a_j^l 就和 l-1 层的激活值通过方程关联起来了

    a^{l}_j = \sigma \left( \sum_k w^{l}_{jk} a^{l-1}_k + b^l_j \right)

    把上面式子向量化

    a^{l} = \sigma(w^l a^{l-1}+b^l)

    例如第三层的激活向量是

    { \left[  \begin{matrix}    a_{1}^3  \\    \\    a_{2}^3 \\   \end{matrix}   \right] }   =\sigma\left(   { \left[    \begin{matrix}      w_{11}^3 & w_{12}^3 & w_{13}^3 & w_{14}^3\\      w_{21}^3 & w_{22}^3 & w_{23}^3 & w_{24}^3     \end{matrix}     \right] }     { \left[     \begin{matrix}       a_{1}^2  \\       \\       a_{2}^2 \\       \\       a_{3}^2  \\       \\       a_{4}^2 \\      \end{matrix}      \right] }+      { \left[       \begin{matrix}         b_{1}^3  \\         \\         b_{2}^3 \\         \\        \end{matrix}        \right] }\right)

    a^{l} 是激活向量。第 j 行的元素是 a_j^l

    定义

    z^l \equiv w^l a^{l-1}+b^l

    a^l =\sigma(z^l)

    z^l 表示第第 l 层的带权输入。第 j 个元素是 z_j^l

    z^l_j=\sum_k w^l_{jk} a^{l-1}_k+b^l_j

    z_j^l 是第 l 层的第 j 个神经元的带权输入。

    反向传播的核心是一个对代价函数 C 关于任何权重 w 和 偏置 b 的偏导数 \partial{C}/\partial{w} 的表达式。为了计算这些值,引入一个中间量 \delta_j^l ,表示在 l 层的第 j 个神经元的误差。

    定义

    \delta^l_j \equiv \frac{\partial C}{\partial z^l_j}.

    \delta^l 是误差向量,\delta^l 的第 j 个元素是 \delta_j^l

    反向传播的四个基本方程

    \nabla_a 是求梯度运算符,\nabla_a C 结果是一个向量,其元素是偏导数 \partial C / \partial a^L_j

    \odot 是按元素乘积的运算符,{(s \odot t)}_j = s_j t_j ,例如

    \left[\begin{array}{c} 1 \\ 2 \end{array}\right]   \odot \left[\begin{array}{c} 3 \\ 4\end{array} \right] = \left[ \begin{array}{c} 1 * 3 \\ 2 * 4 \end{array} \right] = \left[ \begin{array}{c} 3 \\ 8 \end{array} \right].

    反向传播算法

    正如我们上面所讲的,反向传播算法对一个训练样本计算代价函数的梯度,C=C_x。在实践 中,通常将反向传播算法和诸如随机梯度下降这样的学习算法进行组合使用,我们会对许多训 练样本计算对应的梯度。特别地,给定一个大小为 m 的小批量数据,下面的算法在这个小批量 数据的基础上应用梯度下降学习算法:

    反向传播算法与小批量随机梯度下降算法结合的一个示意代码,完整代码参看 network.py

    def backprop(self, x, y):
            """Return a tuple ``(nabla_b, nabla_w)`` representing the
            gradient for the cost function C_x.  ``nabla_b`` and
            ``nabla_w`` are layer-by-layer lists of numpy arrays, similar
            to ``self.biases`` and ``self.weights``."""
            nabla_b = [np.zeros(b.shape) for b in self.biases]
            nabla_w = [np.zeros(w.shape) for w in self.weights]
            # feedforward
            activation = x
            activations = [x] # list to store all the activations, layer by layer
            zs = [] # list to store all the z vectors, layer by layer
            for b, w in zip(self.biases, self.weights):
                z = np.dot(w, activation)+b
                zs.append(z)
                activation = sigmoid(z)
                activations.append(activation)
            # backward pass
            delta = self.cost_derivative(activations[-1], y) * \
                sigmoid_prime(zs[-1])
            nabla_b[-1] = delta
            nabla_w[-1] = np.dot(delta, activations[-2].transpose())
            # Note that the variable l in the loop below is used a little
            # differently to the notation in Chapter 2 of the book.  Here,
            # l = 1 means the last layer of neurons, l = 2 is the
            # second-last layer, and so on.  It's a renumbering of the
            # scheme in the book, used here to take advantage of the fact
            # that Python can use negative indices in lists.
            for l in range(2, self.num_layers):
                z = zs[-l]
                sp = sigmoid_prime(z)
                delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
                nabla_b[-l] = delta
                nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
            return (nabla_b, nabla_w)
    
    复制代码
    def cost_derivative(self, output_activations, y):
            """Return the vector of partial derivatives \partial C_x /
            \partial a for the output activations."""
            return (output_activations-y)
    
    def sigmoid_prime(z):
        """Derivative of the sigmoid function."""
        return sigmoid(z)*(1-sigmoid(z))
    复制代码

    四个基本方程的证明

    我们现在证明这四个基本的方程(BP)-(BP4)。所有的这些都是多元微积分的链式法则的推论。

    证明\delta^L = \nabla_a C \odot \sigma'(z^L)

    从方程(BP1)开始,它给出了误差 \delta^l 的表达式。根据定义

    \delta^l_j \equiv \frac{\partial C}{\partial z^l_j}.

    根据关于代价函数的两个假设2 “代价函数可以写成神经网络输出的函数”,应用链式法测可知可先对神经网络输出求偏导 {\partial C}/{\partial a^L_k} 再对带权输出求偏导 {\partial a^L_k} /  {\partial z^L_j}

    \delta^L_j = \sum_k \frac{\partial C}{\partial a^L_k } \frac{\partial a^L_k}{\partial z^L_j},

    看起来上面式子很复杂,但是由于第 k 个神经元的输出激活值 a_k^l 只依赖于 当下标 k=j 时第 j 个神经元的输入权重 z_j^l。所有当 k\neq {j}\partial a^L_k / \partial z^L_j 消失了。结果我们可以简化上一个式子为

    \delta^L_j = \frac{\partial C}{\partial a^L_j} \frac{\partial a^L_j}{\partial z^L_j}.

    又因为 a^L_j = \sigma(z^L_j) 所以 \frac{\partial a^L_j}{\partial z^L_j} 可以写成 \sigma'(z^L_j),方程变为

    \delta^L_j = \frac{\partial C}{\partial a^L_j} \sigma'(z^L_j)

    这就是分量形式的(BP1),再根据\nabla_a 是求梯度运算符,\nabla_a C 结果是一个向量,其元素是偏导数 \partial C / \partial a^L_j。方程可以写成向量形式

    \delta^L ={\nabla_a {C}} \odot {\sigma'(z^L_j)}

    (BP1) 得到证明。

    证明 \delta^l = ((w^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l)

    证明(BP2),它个给出以下一层误差 \delta^{l+1} 的形式表示误差 \delta^l。为此,要以 \delta^l_j = \partial C / \partial z^l_j的形式重写 \delta^{l+1}_k = \partial C / \partial z^{l+1}_k,\delta^{l+1}\delta^l 通过 z_k^{l+1}z_j^l 联系起来,应用链式法测

    根据 z_k^{l+1} 的定义有

    z^{l+1}_k = \sum_j w^{l+1}_{kj } a^l_j +b^{l+1}_k = \sum_j w^{l+1}_{kj} \sigma(z^l_j) +b^{l+1}_k

    z_k^{l+1}z_j^{l} 做偏微分,得到

    \frac{\partial z^{l+1}_k}{\partial z^l_j} = w^{l+1}_{kj} \sigma'(z^l_j)

    注意虽然z^{l+1}z^{l} 所在的两层神经元连接错综复杂,但两层之间任意一对神经元(同一层内不连接)只有一条连接,即 z_k^{l+1}z_j^{l} 之间只通过 w_{kj}^{l+1} 连接。所以z_k^{l+1}z_j^{l} 做偏微分的结果很简单,只是 w^{l+1}_{kj} \sigma'(z^l_j)。把这个结果带入 \delta_j^l

    \delta^l_j = \sum_k w^{l+1}_{kj }  \delta^{l+1}_k \sigma'(z^l_j)

    这正是以分量形式写的(BP2)。

    写成向量形式

    \delta^l = ((w^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l)

    举例

    { \left[  \begin{matrix}    \delta_{1}^l  \\    \\    \delta_{2}^l \\    \\    ...    \\   \delta_{j}^l   \end{matrix}   \right] }   = { \left[      \begin{matrix}        w_{11}^{l+1} & w_{21}^{l+1} & w_{31}^{l+1} & ... &w_{k1}^{l+1} \\        \\        w_{12}^{l+1} & w_{22}^{l+1} & w_{32}^{l+1} & ... & w_{k2}^{l+1} \\        \\        ...        \\        w_{j1}^{l+1} & w_{j2}^{l+1} & w_{j1}^{l+1} & ... & w_{kj}^{l+1}       \end{matrix}       \right] }    { \left[    \begin{matrix}      \delta_{1}^{l+1}  \\      \\      \delta_{2}^{l+1} \\      \\      \delta_{3}^{l+1} \\      \\      ...      \\     \delta_{k}^{l+1}     \end{matrix}     \right] }     \odot     { \left[     \begin{matrix}       \sigma'(z_1^l)  \\       \\       \sigma'(z_2^l) \\       \\       \sigma'(z_3^l) \\       \\       ...       \\      \sigma'(z_k^l)      \end{matrix}      \right] }

    (BP2) 得到证明。

    证明 \frac{\partial C}{\partial b^l_j} =\delta^l_j.

    根据 z_j^l 定义

    z^l_j=\sum_k w^l_{jk} a^{l-1}_k+b^l_j

    \delta_j^l 定义

    \delta^l_j \equiv \frac{\partial C}{\partial z^l_j}.

    因此

    \frac{\partial C}{\partial b^l_j} = \frac{\partial C}{\partial z^l_j}\frac{\partial z^l_j}{\partial b^l_j}

    又因为

    \frac{\partial z^l_j}{\partial b^l_j} = 1

    所以

    \frac{\partial C}{\partial b^l_j} = \frac{\partial C}{\partial z^l_j}\frac{\partial z^l_j}{\partial b^l_j} = \frac{\partial C}{\partial z^l_j}\cdot 1= \frac{\partial C}{\partial z^l_j}=\delta^l_j

    \frac{\partial C}{\partial b^l_j} =\delta^l_j

    写成向量形式

    \frac{\partial C}{\partial b^l} =\delta^l

    (BP3) 得到证明。

    证明 \frac{\partial C}{\partial w^l_{jk}} = a^{l-1}_k \delta^l_j

    根据 z_j^l 定义

    z^l_j=\sum_k w^l_{jk} a^{l-1}_k+b^l_j

    \delta_j^l 定义

    \delta^l_j \equiv \frac{\partial C}{\partial z^l_j}.

    又因为

    \frac{\partial z^l_j}{\partial w^l_{jk}} = a_k^{l-1}

    所以

    \frac{\partial C}{\partial w^l_{jk}} = \frac{\partial C}{\partial z^l_j}\frac{\partial z^l_j}{\partial w^l_{jk}} = \delta^l_j a_k^{l-1}

    把式子向量化

    \frac{\partial C}{\partial w^l} = \delta^l (a^l)^T

    举例

    \frac{\partial C}{\partial w^l} = { \left[  \begin{matrix}    \delta_{1}^l  \\    \\    \delta_{2}^l \\    \\    ...    \\   \delta_{j}^l   \end{matrix}   \right] }{ \left[      \begin{matrix}        a_{1}^{l-1} & a_{2}^{l-1} & ... &a_{k}^{l-1}       \end{matrix}       \right] }

    (BP4) 得到证明。

    一个直观的图:

    到此关于反向传播的四个方程已经全部证明完毕。

    其他学者反向传播四个方程的证明(他写的更简明扼要些):CSDN: oio328Loio

    反向传播:全局观

    如上图所示,假设我们对 w_{jk}^l 做一点微小的扰动 \Delta w_{jk}^l, 这个扰动会沿着神经网络最终影响到代价函数 C, 代价函数的 \Delta C 改变和 \Delta w_{jk}^l 按照下面公式联系起来

    \Delta C \approx \frac{\partial C}{\partial w^l_{jk}} \Delta w^l_{jk}

    可以想象影响代价函数的一条路径是

    \Delta C \approx \frac{\partial C}{\partial a^L_m}   \frac{\partial a^L_m}{\partial a^{L-1}_n}   \frac{\partial a^{L-1}_n}{\partial a^{L-2}_p} \ldots   \frac{\partial a^{l+1}_q}{\partial a^l_j}   \frac{\partial a^l_j}{\partial w^l_{jk}} \Delta w^l_{jk}

    为了计算 C 的全部改变,我们需要对所有可能的路径进行求和,即

    \Delta C \approx \sum_{mnp\ldots q} \frac{\partial C}{\partial a^L_m}   \frac{\partial a^L_m}{\partial a^{L-1}_n}   \frac{\partial a^{L-1}_n}{\partial a^{L-2}_p} \ldots   \frac{\partial a^{l+1}_q}{\partial a^l_j}   \frac{\partial a^l_j}{\partial w^l_{jk}} \Delta w^l_{jk}

    因为

    \frac{\partial C}{\partial w^l_{jk}}=\frac{\Delta C}{\Delta w^l_{jk}}

    根据上面的三个式子可知

    \frac{\partial C}{\partial w^l_{jk} } = \sum_{mnp\ldots q} \frac{\partial C}{\partial a^L_m}   \frac{\partial a^L_m}{\partial a^{L-1}_n}   \frac{\partial a^{L-1}_n}{\partial a^{L-2}_p} \ldots   \frac{\partial a^{l+1}_q}{\partial a^l_j}   \frac{\partial a^l_j}{\partial w^l_{jk}}

    上面的公式看起来复杂,这里有一个相当好的直觉上的解释。我们用这个公式计算 C 关于网络中一个权重的变化率。而这个公式告诉我们的是:两个神经元之间的连接其实是关联于一个变化率因子,这仅仅是一个神经元的激活值相对于其他神经元的激活值的偏导数。路径的变化率因子就是这条路径上众多因子的乘积。整个变化率 \partial C / \partial w^l_{jk} 就是对于所有可能从初始权重到最终输出的代价函数的路径的变化率因子的和。针对某一路径,这个过程解释如下,

    如果用矩阵运算对上面式子所有的情况求和,然后尽可能化简,最后你会发现,自己就是在做反向传播!可以将反向传播想象成一种计算所有可能路径变化率求和的方式。或者,换句话说,反向传播就是一种巧妙地追踪权重和偏置微小变化的传播,抵达输出层影响代价函数的技术。

    如果你尝试用上面的思路来证明反向传播,会比本文的反向传播四个方程证明复杂许多,因为按上面的思路来证明有许多可以简化的地方。其中可以添加一个巧妙的步骤,上面方程的偏导对象是类似 a_q^{l+1} 的激活值。巧妙之处是改用加权输入,例如 z_q^{l+1} ,作为中间变量。如果没想到这个主意,而是继续使用激活值 a_q^{l+1} ,你得到的证明最后会比前文给出的证明稍稍复杂些。

    其实最早的证明的出现也不是太过神秘的事情。因为那只是对简化证明的艰辛工作的积累!


    参考文献

    [1] Michael Nielsen. CHAPTER 2 How the backpropagation algorithm works[DB/OL]. neuralnetworksanddeeplearning.com/chap2.html, 2018-06-21.

    [2] Zhu Xiaohu. Zhang Freeman.Another Chinese Translation of Neural Networks and Deep Learning[DB/OL]. github.com/zhanggyb/nn…, 2018-06-21.

    [3] oio328Loio. 神经网络学习(三)反向(BP)传播算法(1)[DB/OL]. blog.csdn.net/hoho1151191…, 2018-06-25.

    展开全文
  • #反向传播算法定义损失函数和激活函数 def loss(network_y, real_y): ''' 返回函数的编导,损失函数使用 MSE L = 1/2 (netowork_y - real_y)^2 delta_L = network_y - real_y 损失函数使用了均方误差作为该...
    import numpy as np
    #反向传播算法定义损失函数和激活函数
    
    def loss(network_y, real_y):
        '''
        返回函数的编导,损失函数使用 MSE
        L = 1/2 (netowork_y - real_y)^2
        delta_L = network_y - real_y
        损失函数使用了均方误差作为该神经网络的损失函数,因此求导后的输入分别为网络的预测值和真实输出值,
        输出为两者的差。激活函数采用 Sigmoid 函数
        '''
        return (network_y - real_y)
    def sigmoid(z):
        """
        激活函数使用 sigmoid
        :param z:
        :return:
        """
        return 1.0 / (1.0 + np.exp(-z))
    
    def sigmoid_der(z):
        """digmoid 函数的导数 dervation of sigmoid"""
        return sigmoid(z) * (1 - sigmoid(z))
    
    #反向传播算法的具体实现
    """backprop() 函数的输入为 x 和 y ,其中x 为(3,1)的矩阵, y 为(2,1)的矩阵。
    根据反向传播算法的四个基本公式(BP1 —BP4)的计算中需要知道每一层的神经元的激活值和加权输入值,因此在进行向前椽传播时,
    使用activation 记录每一层的激活值和 zs 记录每一层的加权输入值
    首先从L 层进行计算,因此先计算输出层的误差,然后计算损失函数关于输出层L 中的偏置和权值参数的偏导。
    最后循环为range(2,num_laylwes), 意思时从网络的倒数第二层开始,往前计算第 l 层的损失值
    以及损失函数关于第 l 层的偏置和权值的偏导。最后输出一次反向传播后, 得到关于损失函数的所有参数的偏导
    """
    def backprop(x, y):
        """反向传播算法的实现"""
        """初始化网网络参数的导数,权值 w  的偏导和偏置 b 的偏导"""
        delta_w = [np.zeros(w.shape) for w  in weights]
        delta_b = [np.zeros(b.shape) for b in biases]
        """向前传播 feed forward"""
        activation = x # 把输入的数据作为第一次激活值
        activations = [x] #存储网络的激活值
        zs = []     #存储网络的加权输入值 ( z = wx +b)
        for w,b in zip(weights, biases):
            z = np.dot(w, activation) + b
            activation = sigmoid(z)
            activations.append(activation)
            zs.append(z)
    
        """反向传播 back propagation"""
        #BP1 计算输出层误差
    
        delta_L = loss(activation[-1], y)*sigmoid_der(zs[-1])
        #BP3 损失函数在输出层关于偏置的偏导
        delta_b[-1] = delta_L
        #BP4 损失函数在输出层关于权值的偏导
    
        delta_w[-1] = np.dot(delta_L, activations [-2].transpose())
    
        delta_l = delta_L
        for l in range(2, num_layers):
            # BP2 计算第1层误差
            z = zs [-1]
            sp = sigmoid_der(z)
            delta_l = np.dot(weights[-l + 1].transpose(), delta_l) *sp
            # BP3 损失函数在 l 层关于偏置的偏导
            delta_b[-1] = delta_l
            # BP4 损失函数 在 l 层关于权值的偏导
            delta_w[-1] = np.dot(delta_l, activations[-l-1].transpose())
        return (delta_w, delta_b)
    """ 定义神经网络的模型架构[ input , hidden, output ] 
    根据网络模型的大小,利用高斯分布函数的权值参数 weights 和 偏置参数 biases 产生 均值为 0 , 方差为1 
    的随机值。
    
    """
    network_sizes = [3, 4, 2]
    #初始化该神经网络的参数
    sizes = network_sizes
    num_layers = len (sizes)
    biases = [np.random.randn(h, 1) for h in sizes[1:]]
    weights = [np.random.randn(y, x) for y, x in zip(sizes[:-1], sizes[1:])]
    
    # 产生训练的数据
    
    training_x = np.random.rand(3).reshape(3, 1)
    training_y = np.array([0, 1]).reshape(2, 1)
    print("training data x :{},training data y :{}".format(training_x,training_y))
    backprop(training_x, training_y)
    

     

    展开全文
  • 反向传播算法是训练神经网络的...本文通过理论和代码相结合的方式详细讲述了反向传播算法的原理和实现。作者:Great Learning Teamdeephub.ai翻译组译神经网络什么是反向传播?反向传播是如何工作的?损失函数为什...
  • 浅析反向传播算法

    2018-01-30 22:32:06
    反向传播算法概念 反向传播算法细节 代码细节 代码分析 反向传播算法概念 反向传播算法广泛用在机器学习、人工智能等领域,目的在于将各个结点的权值w和偏差值b等参数调节到最优状态(最优状态:使训练数据...
  • BP算法 反向传播算法 简易理解 先提出一个问题:怎样拟合一条线性函数?? 1. 什么是BP反向传播算法反向传播算法 2. 为什么要用反向传播算法? 3. 代码实现
  • 反向传播算法代码实现 代码实现 import numpy as np import math import tensorflow as tf import matplotlib.pyplot as plt def grad_sigmoid(n): # 求sigmoid梯度 return np.array(tf.sigmoid(1.0 - tf....
  • BP——反向传播算法公式推导及代码

    千次阅读 多人点赞 2019-03-14 23:51:19
    BP——反向传播算法计算过程详解人工神经网络的结构前向传播激活函数反向传播梯度下降具体计算过程 本文主要参考吴恩达和李宏毅的深度学习视频,然后自己做的笔记 反向传播计算部分参考李宏毅的视频讲解,要是有...
  • 使用pytorch实现正向传播与反向传播算法 1、预先知识学习 1、pytorch数据结构-Tensor 参考自:https://zhuanlan.zhihu.com/p/48982978 在深度学习中,Tensor实际上就是一个多维数组(multidimensional array),其...
  • python 代码实现反向传播算法

    千次阅读 2018-12-28 15:47:56
    实现反向传播算法可以在python2以及python3中运行,在我的测试环境中可以运行.我并没有详细去测试每一个python版本. 算法中使用的数据是mnist数据集. 下面是算法代码 forward_neural_network.py #!/usr/bin/...
  • 引言 根据上篇文章介绍的反向传播算法理论,我们今天来手动计算一下。 参考了国外的一篇文章:a-step-by-step-backpropagation-example
  • 反向传播算法为什么需要反向传播算法反向传播图解理论层面:链式法则前向通道后向通道反向传播代码 作为一个逻辑很差又贼容易懵逼的菜鸟,不做笔记和经常回顾真的学不到东西,脑子一团浆糊。 之前明明已经手推了反向...
  • **反向传播算法: **将输出层的误差传播到隐藏层的神经元中,然后更新权重。 反向传播算法的实现过程分为两个步骤: 1.正向传播:由输入层经过隐藏层传播到输出层,计算损失函数和误差 2.反向传播:在反向传播中,...
  • 其中在实现过程中,将会用代码的形式呈现梯度消失的现象,本文中的符号请参考我的前文《反向传播算法的公式推导》,代码将由github托管,有帮助的朋友欢迎star。 如有谬误,请联系指正。转载请注明出处。 联系方式:...
  • 反向传播的目的是计算成本函数C对网络中任意w或b的偏导数。...反向传播算法中Sigmoid函数代码演示: # 实现 sigmoid 函数 return 1 / (1 + np.exp(-x)) def sigmoid_derivative(x): # sigmoid 导数的计算 retu
  • 反向传播算法

    2018-03-28 21:09:24
    看了很多有关神经网络的资料,一直对于反向传播算法总是不理解,对于其过程也是觉得很复杂,让人想放弃,写一篇博客来从头到尾来撸一遍反向传播,让这个黑盒子变透明。自己上手推导了两遍,接下来撸一遍代码,推荐一...
  • 代价函数 反向传播算法 将输出层的误差反向传播到前面的隐藏层 ...验证反向传播算法正确之后,要关掉梯度检测的代码 随机初始化 若将神经网络中的参数全初始化为0,那么在之后的梯度下降过程...
  • 理清反向传播算法背景定义全连接网络前向运算链式求导反向传播算法 背景 去年看了《神经网络与深度学习》的前几章,了解了反向传播算法的一些皮毛,当时想自己实现一下,但是由于事情多,就放下了。现在有时间,也...
  • 神经网络反向传播算法代码实现 可以直接运行
  • 读到了一篇讲解特别友好的博客,很适合新手学习了解后向传播算法的概念。 但是原作者没有附上代码,所以我便照着教程写了份代码附上。 原教程:反向传播——通俗易懂 源码: import numpy as np i1=0.05#输入神经元...
  • backpropation算法python代码实现讲解 批量梯度更新 backpropagation算法 backpropagation算法步骤 backpropation算法python代码实现讲解 具体神经网络参见第一个笔记 批量梯度更新 class ...
  • Backpropagation算法1.算法介绍反向传播算法(Backpropagation)是...下面将结合代码,详细阐述反向传播算法在MLP中的应用过程。2.算法实现步骤2.1 读取csv该步骤代码与前面代码一致,不再重复给出。2.2 划分数据为k...
  • 代码实践:MLP的反向传播算法

    万次阅读 2019-04-10 21:23:18
    # 反向传播算法 def update_parameters ( gredient , parameters , LearnRate ) : # w: = w -learningRate *dw # w: = b -learningRate *db Layer_num = len ( parameters ) // 2 for L in ...
  • 零基础入门深度学习(3) - 神经网络和反向传播算法 代码待调试通 import random from numpy import * def sigmoid(inX): return 1.0 / (1 + exp(-inX)) class Node(object): def __init__(self, layer_index,...
  • 反向传播的目的是计算成本函数C对网络中任意w或b的偏导数。一旦我们有了这些偏导数,我们将通过一些常数 α的乘积和该数量相对于成本函数的偏导数来更新网络中的权重和...图示演示:反向传播算法中Sigmoid函数代码...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 561
精华内容 224
关键字:

反向传播算法代码