• 反向传播


    image-20220327123627307

    一、正向传播

    正向传播(FP - Forward Propagation)

    前一层的输出作为后一层的输入的逻辑结构,每一层神经元仅与下一层的神经元全连接,通过增加神经网络的层数虽然可为其提供更大的灵活性,让网络具有更强的表征能力,也就是说,能解决的问题更多,但随之而来的数量庞大的网络参数的训练,一直是制约多层神经网络发展的一个重要瓶颈。

    1564414334950

    正向传播推导过程如下:

    \[layer_0 = X \\ \]

    根据第一层权重计算第一层结果:

    \[layer_1 = sigmoid(layer_0 \times W_1) \]

    根据第二层权重计算当前样本的预测输出:

    \[layer_2(out) = sigmoid(layer_1 \times W_2)) = y' \]

    二、反向传播

    反向传播(Backpropagation algorithm)全称“误差反向传播”,是在深度神经网络中,根据输出层输出值,来反向调整隐藏层权重的一种方法。

    • 为什么不直接使用梯度下降而使用反向传播方式更新权重呢?
      梯度下降应用于有明确求导函数的情况,或者可以求出误差的情况(比如线性回归),我们可以把它看做没有隐藏层的网络。但对于多个隐藏层的神经网络,输出层可以直接求出误差来更新参数,但隐藏层的误差是不存在的,因此不能对它直接应用梯度下降,而是先将误差反向传播至隐藏层,然后再应用梯度下降。

    2.1 图解反向传播

    问题:Tom在超市买了2个苹果,3个橙子,其中苹果每个10元,橙子每个15元,消费税10%,请计算应该支付的金额

    image-20220327125454892

    问题:Tom在超市买了2个苹果,每个10元,消费税10%,请计算苹果价格上涨会在多大程度上影响支付金额(即求“支付金额关于苹果的价格的导数”)。设苹果的价格为x,支付金额为L,则相当于求 \(\frac{\partial L}{\partial x}\) 。这个导数的值表示当苹果的价格稍微上涨时,支付金额会增加多少。

    image-20220327130022356

    苹果价格的导数为2.2,同理,苹果个数导数为11,消费税导数为20,可以解释为:苹果价格、苹果个数或消费税增加相同的值,分别对支付金额产生2.2倍、11倍、20倍的影响

    考虑函数 y = f(x) , 输出为E,反向传播的计算顺序是,将信号E乘以节点的局部导数(偏导数),传递给前面的节点,这样可以高效地求出导数的值。

    image-20220327130356402

    加法节点反向传播:

    image-20220327131005359

    乘法节点反向传播:

    image-20220327131036958

    链式求导法则:

    image-20220327130917136

    image-20220327130933313

    案例:苹果、橙子价格和个数以及税率如下图所示,利用反向传播算法,在方框处计算填入导数

    image-20220327131318071

    2.2 代码实现

    from __future__ import unicode_literals
    import numpy as np
    import matplotlib.pyplot as mp
    
    
    class ANNModel():
    
        def __init__(self):
            # 随机初始化权重[-1 1)
            self.w0 = 2 * np.random.random((2, 4)) - 1  # 2*4
            self.w1 = 2 * np.random.random((4, 1)) - 1  # 4*1
            self.lrate = 0.1
    
        # sigmiod 函数
        def active(self, x):
            return 1 / (1 + np.exp(-x))
    
        # sigmoid函数导函数
        def backward(self, x):
            return x * (1 - x)
    
        # 单层网路前向传播
        def forward(self, x, w):
            return np.dot(x, w)
    
        def fit(self, x):
    
            for j in range(10000):
                # 前向传播
                l0 = x
                l1 = self.active(self.forward(l0, self.w0))
                l2 = self.active(self.forward(l1, self.w1))
                # 损失
                l2_error = y - l2
                if (j % 100) == 0:
                    print("Error:" + str(np.mean(np.abs(l2_error))))
                # 反向传播
                l2_delta = l2_error * self.backward(l2)
                self.w1 += l1.T.dot(l2_delta * self.lrate)
                l1_error = l2_delta.dot(self.w1.T)
                l1_delta = l1_error * self.backward(l1)
                self.w0 += l0.T.dot(l1_delta * self.lrate)
    
        def predict(self, x):
            l0 = x
            l1 = self.active(self.forward(l0, self.w0))
            l2 = self.active(self.forward(l1, self.w1))
            result = np.zeros_like(l2)
            result[l2 > 0.5] = 1
            return result
    
    x = np.array([
        [3, 1],
        [2, 5],
        [1, 8],
        [6, 4],
        [5, 2],
        [3, 5],
        [4, 7],
        [4, -1]])
    y = np.array([0, 1, 1, 0, 0, 1, 1, 0]).reshape(-1, 1)
    
    n = 500
    l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
    b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
    grid_x = np.meshgrid(np.linspace(l, r, n),
                         np.linspace(b, t, n))
    flat_x = np.column_stack((grid_x[0].ravel(), grid_x[1].ravel()))
    model = ANNModel()
    model.fit(x)
    flat_y = model.predict(flat_x)
    grid_y = flat_y.reshape(grid_x[0].shape)
    mp.figure('Classification', facecolor='lightgray')
    mp.title('Classification', fontsize=20)
    
    mp.xlabel('x', fontsize=14)
    mp.ylabel('y', fontsize=14)
    mp.tick_params(labelsize=10)
    mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap='gray')
    mp.scatter(x[:, 0], x[:, 1], c=y.ravel(), cmap='brg', s=80)
    mp.show()
    

    2.3 使用反向传播神经网络逼近函数

    import math
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 神经网络结构
    class ApproachNetwork:
        learning_rate = 0.05  # 学习率
    
        # W1:输入层与隐层之间的权重
        # B1:隐含层神经元的阈值
        # W2:隐含层与输出层之间的权重
        # B2:输出层神经元的阈值
    
        def __init__(self, hidden_size=100, output_size=1):
            self.params = {'W1': np.random.random((1, hidden_size)),
                           'B1': np.zeros(hidden_size),
                           'W2': np.random.random((hidden_size, output_size)),
                           'B2': np.zeros(output_size)}
    
        # 增加产生数据的函数
        @staticmethod
        def generate_data(fun, is_noise=True, axis=np.array([-1, 1, 100])):
            """
             产生数据集
            :param fun: 这个是你希望逼近的函数功能定义,在外面定义一个函数功能方法,把功能方法名传入即可
            :param is_noise: 是否需要加上噪点,True是加,False表示不加
            :param axis: 这个是产生数据的起点,终点,以及产生多少个数据
            :return: 返回数据的x, y
            """
            np.random.seed(0)
            x = np.linspace(axis[0], axis[1], axis[2])[:, np.newaxis]
            x_size = x.size
            y = np.zeros((x_size, 1))
            if is_noise:
                noise = np.random.normal(0, 0.1, x_size)
            else:
                noise = None
    
            for i in range(x_size):
                if is_noise:
                    y[i] = math.sin(x[i]) + noise[i]
                else:
                    y[i] = math.sin(x[i])
    
            return x, y
    
        @staticmethod
        # sigmiod函数作为激活函数
        def sigmoid(x_):
            return 1 / (1 + np.exp(-x_))
    
        # sigmoid函数导函数
        def sigmoid_grad(self, x_):
            return (1.0 - self.sigmoid(x_)) * self.sigmoid(x_)
    
        # 输出层predict
        def predict(self, x_):
            W1, W2 = self.params['W1'], self.params['W2']
            b1, b2 = self.params['B1'], self.params['B2']
    
            a1 = np.dot(x_, W1) + b1
            z1 = self.sigmoid(a1)
            a2 = np.dot(z1, W2) + b2
    
            return a2
    
        # 均方差MSE作为损失函数
        def loss(self, x_, t):
            y_ = self.predict(x_)
            return y_, np.mean((t - y_) ** 2)
    
        # 计算BP网络梯度和更新权重
        def gradient(self, x, t):
            W1, W2 = self.params['W1'], self.params['W2']
            b1, b2 = self.params['B1'], self.params['B2']
            grads = {}
    
            batch_num = x.shape[0]
    
            # forward 前向传播
            a1 = np.dot(x, W1) + b1
            z1 = self.sigmoid(a1)
            a2 = np.dot(z1, W2) + b2
    
            # backward 反向传播
            dy = (a2 - t) / batch_num
            grads['W2'] = np.dot(z1.T, dy)
            grads['B2'] = np.sum(dy, axis=0)
    
            dz1 = np.dot(dy, W2.T)
            da1 = self.sigmoid_grad(a1) * dz1
            grads['W1'] = np.dot(x.T, da1)
            grads['B1'] = np.sum(da1, axis=0)
    
            return grads
    
        # 训练主函数
        def train_with_own(self, x_, y_, max_steps=100):
            for k in range(max_steps):
                grad = self.gradient(x_, y_)
                for key in ('W1', 'B1', 'W2', 'B2'):
                    self.params[key] -= self.learning_rate * grad[key]
                pred, loss = network.loss(x_, y_)
    
                if k % 500 == 0:  # 每500步,刷新输出当前图形
                    # 动态绘制结果图,可以看到训练过程如何慢慢的拟合数据点
                    plt.cla()
                    plt.scatter(x, y)
                    plt.plot(x, pred, 'r-', lw=5)
                    plt.text(0.5, 0, 'Loss=%.4f' % abs(loss), fontdict={'size': 20, 'color': 'red'})
                    plt.pause(0.1)
    
            # 关闭动态绘制模式
            plt.ioff()
            plt.show()
    
    
    if __name__ == '__main__':
        network = ApproachNetwork()
    
        x, y = network.generate_data(False, axis=np.array([-3, 3, 100]))
    
        # 使用 自编代码 训练
        # 最大循环步骤为3000
        network.train_with_own(x, y, 3000)
    

    image-20220329142059881

  • 相关阅读:
    堆栈(线性表)
    链表 -- 循环链表(线性表)
    链表 -- 单向链表(线性表)
    排序算法--归并算法(强分治)
    sqlhelper
    sqlite与sqlserver区别
    常用sql集锦
    外出实施问题总结
    JS深入理解系列(一):编写高质量代码
    通用分页(Jquery版)
  • 原文地址:https://www.cnblogs.com/wkfvawl/p/16062637.html
Copyright © 2020-2023  润新知