《深度学习入门:基于Python的理论与实现》读书笔记:第5章 误差反向传播算法

目录

第5章 误差反向传播算法

5.1 计算图

5.1.1 用计算图求解

5.1.2 局部计算

5.1.3 为何用计算图解题

5.2 链式法则

5.2.1 计算图的反向传播

5.2.2 什么是链式法则

5.2.3 链式法则和计算图

5.3 反向传播

5.3.1 加法节点的反向传播 

5.3.2 乘法节点的反向传播

5.3.3 苹果的例子

5.4 简单层的实现

5.4.1 乘法层的实现

5.4.2 加法层的实现

5.5 激活函数层的实现

5.5.1 ReLu层

5.5.2 Sigmoid层

 5.6 Affine/Softmax层的实现

5.6.1 Affine层

5.6.2 批版本的Affine层

5.6.3 Softmax-with-Loss层 

5.7 误差反向传播法的实现

5.7.1 神经网络学习的全貌图

5.7.2 对应误差反向传播法的神经网络的实现 

5.7.3 误差反向传播法的梯度确认

5.7.4 使用误差反向传播法的学习

5.8 小结

 


第5章 误差反向传播算法

5.1 计算图

计算图将计算过程用图形表示出来,这里说的图形是数据结构图,通过多个节点和边表示(连接节点的直线称为“边”)。

5.1.1 用计算图求解

用计算图解题的情况下,需要按如下流程进行:

1.构建计算图;

2.在计算图上,从左向右进行计算。

这里的第2步“从左向右进行计算”是一种正方向上的传播,简称为正向传播,正向传播是从计算图出发点到结束点的传播。既然有正向传播这个名称,当然也可以考虑反向的传播。实际上,这种传播称为反向传播,反向传播将在接下来的导数计算中发挥重要作用。

5.1.2 局部计算

计算图的特征是可以通过传递“局部计算”获得最终结果。“局部”这个词的意思是“与自己相关的某个小范围”。局部计算是指,无论全局发生了什么,都能只根据与自己相关的信息输出接下来的结果。

计算图可以集中精力于局部计算,无论全局的计算有多么复杂,各个步骤所要做的就是对象节点的局部计算。虽然局部计算非常简单,但是通过传递它的计算结果,可以获得全局的复杂计算的结果。

5.1.3 为何用计算图解题

如图所示,反向传播使用与正方向相反的箭头(粗线)表示,传递“局部导数” ,将导数的值写在箭头的下方。

这里只求了关于苹果的价格的导数,不过“支付金额关于消费税的导数”“支付金额关于苹果的个数的导数”等也都可以用同样的方式算出来。并且,计算中途求得的导出的结果(中间传递的导数)可以被共享,从而可以高效地计算多个导数。综上,计算图的优点是,可以通过正向传播和反向传播高效地计算各个变量的导数值。

5.2 链式法则

5.2.1 计算图的反向传播

5.2.2 什么是链式法则

如果某个函数由复合函数表示,则该复合函数的导数可以用构成复合函数的各个函数的导数的乘积表示。

5.2.3 链式法则和计算图

如图所示,计算图的反向传播从右到左传播信号。反向传播的计算顺序是,先将节点的输入信号乘以节点的局部导数(偏导数),然后再传递给下一个节点。

5.3 反向传播

5.3.1 加法节点的反向传播 

5.3.2 乘法节点的反向传播

加法的反向传播只是将上游的值传给下游,并不需要正向传播的输入信号,但是,乘法的反向传播需要正向传播时的输入信号值。因此,实现乘法节点的反向传播,要保存正向传播的输入信号。

5.3.3 苹果的例子

5.4 简单层的实现

这里,我们把要实现的计算图的乘法节点称为“乘法层”,加法节点称为“加法层” 。

5.4.1 乘法层的实现

层的实现中有两个共同的方法(接口)forward()和backward()。forward()对应正向传播,backward()对应反向传播。

class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None

    def forward(self, x, y):
        self.x = x
        self.y = y
        out = x * y

        return out

    def backward(self, dout):
        dx = dout * self.y
        dy = dout * self.x

        return dx, dy

    

上面就是MulLayer的实现。

使用这个乘法层的话,上图中的正向传播可以像下面这样实现:

apple = 100
apple_num = 2
tax = 1.1

# layer
mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)

print(price)
220.00000000000003

此外,关于各个变量的导数可由backward()求出。

# backward
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)
print(dapple, dapple_num, dtax)
2.2 110.00000000000001 200

5.4.2 加法层的实现

接下来,我们实现加法节点的加法层,如下所示:

class AddLayer:
    def __init__(self):
        pass
    
    def forward(self, x, y):
        out = x + y
        return out
    
    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1
        return dx, dy

现在,我们使用加法层和乘法层,实现购买2个苹果和3个橘子的例子。

apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1

# layer
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)
orange_price = mul_orange_layer.forward(orange, orange_num)
all_price = add_apple_orange_layer.forward(apple_price, orange_price)
price = mul_tax_layer.forward(all_price, tax)

# backward
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print('price:', int(price))
print('dApple:', dapple)
print('dApple_num:', int(dapple_num))
print('dOrange:', dorange)
print('dOrange_num:', int(dorange_num))
print('dTax:', dtax)
price: 715
dApple: 2.2
dApple_num: 110
dOrange: 3.3000000000000003
dOrange_num: 165
dTax: 650

5.5 激活函数层的实现

5.5.1 ReLu层

激活函数ReLU由下式表示:

求出y关于x的导数为:

class Relu:
    def __init__(self):
        self.mask = None
        
    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0
    
        return out
    
    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout
        
        return dx

ReLU层的作用就像电路中的开关一样。正向传播时,有电流通过的话,就将开关设为ON;没有电流通过的话,就将开关设为OFF。反向传播时,开关为ON的话,电流会直接通过;开关为OFF的话,则不会有电流通过。

5.5.2 Sigmoid层

接下来,我们来实现sigmoid函数。

 反向传播的流程:

步骤1

步骤2

 步骤3

步骤4

class Sigmoid:
    def __init__(self):
        self.out = None
        
    def forward(self, x):
        out = 1 / (1 + np.exp(-x))
        self.out = out
        
        return out
    
    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out
        
        return dx

 5.6 Affine/Softmax层的实现

5.6.1 Affine层

5.6.2 批版本的Affine层

class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b
        self.x = None
        self.dW = None
        self.db = None
    
    def forward(self, x):
        self.x = x
        out = np.dot(x, self.W) + self.b
        
        return out
    
    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)
        
        return dx

5.6.3 Softmax-with-Loss层 

神经网络中进行的处理有推理和学习两个阶段。神经网络的推理通常不使用Softmax层,当神经网络的推理只需要给出一个答案的情况下,因为此时只对得分最大值感兴趣,所以不需要Softmax层。不过,神经网络的学习阶段则需要Softmax层。

 神经网络学习的目的就是通过调整权重参数,使神经网络的输出接近监督标签。因此,必须将神经网络的输出与监督标签的误差高效地传递给前面的层。

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None
        self.t = None
    
    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        
        return self.loss
    
    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size
        
        return dx

5.7 误差反向传播法的实现

5.7.1 神经网络学习的全貌图

5.7.2 对应误差反向传播法的神经网络的实现 

import os
import sys
sys.path.append(os.pardir)
import numpy as np
from layers import *
from gradient import numerical_gradient
from collections import OrderedDict


class TwoLayerNet:
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        # 初始化权重
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size,)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)

        # 生成层
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'],self.params['b2'])
        self.lastLayer = SoftmaxWithLoss()

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)

        return x

    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)

    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1 : t = np.argmax(t, axis=t)
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy

    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)

        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

        return grads

    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastlayer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        grads = {}
        grads['W1'] = self.layers['Affine1'].dW
        grads['b1'] = self.layers['Affine1'].db
        grads['W2'] = self.layers['Affine2'].dW
        grads['b2'] = self.layers['Affine2'].db

        return grads

5.7.3 误差反向传播法的梯度确认

数值微分的优点是实现简单,因此,一般情况下不太容易出错。而误差反向传播法的实现很复杂,容易出错。所以,经常会比较数值微分的结果和误差反向传播法的结果,以确认误差反向传播法的实现是否正确。确认数值微分求出的梯度结果和误差反向传播法求出的结果是否一致(严格地讲,是非常相近)的操作称为梯度确认。

import sys
import os
sys.path.append(os.pardir)
import numpy as np
from mnist import load_mnist
from two_layers import TwoLayerNet

(x_train, t_train), (x_test, t_test) = load_mnist(normailize=True, one_hot_label True)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
x_batch = x_train[:3]
t_batch = t_train[:3]

grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backrop = network.gradient(x_batch, t_batch)

for key in grad_numerical.keys():
    diff = np.average(np.abs(grad_backprop[key] - grad_numerical[key]))
    print(key + ":" + str(diff))
    

 5.7.4 使用误差反向传播法的学习

import sys
import os
sys.path.append(os.pardir)
import numpy as np
from mnist import load_mnist
from two_layer_net import TwoLayerNet


(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    # 通过误差反向传播法求梯度
    grad = network.gradient(x_batch, t_batch)

    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]

        loss = network.loss(x_batch, t_batch)
        train_loss_list.append(loss)

        if i % iter_per_epoch == 0:
            train_acc = network.accuracy(x_train, t_train)
            test_acc = network.accuracy(x_test, t_test)
            train_acc_list.append(train_acc)
            test_acc_list.append(test_acc)
            print(train_acc, test_acc)

5.8 小结

本章我们介绍了将计算过程可视化的计算图,并使用计算图,介绍了神经网络中的误差反向传播法,并以层为单位实现了神经网络中的处理。我们学过的层有ReLU层、Softmax-with-Loss层、Affine层、Softmax层等,这些层中实现了forward和backward方法,通过将数据正向和反向地传播,可以高效地计算权重参数的梯度。通过使用层进行模块化,神经网络中可以自由地组装层,轻松构建出自己喜欢的网络。

1.通过使用计算图,可以直观地把握计算过程;

2.计算图的节点是由局部计算构成的,局部计算构成全局计算;

3.计算图的正向传播进行一般的计算,通过计算图的反向传播,可以计算各个节点的导数;

4.通过神经网络的组成元素实现为层,可以高效地计算梯度(反向传播法);

5.通过比较数值微分和误差反向传播法的结果,可以确认误差反向传播法的实现是否正确(梯度确认)。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 深度学习是一种机器学习技术,可以通过模拟人类大脑的神经网络结构来实现智能决策和预测。Python是一种广泛使用的编程语言,也是深度学习中使用最多的语言之一。 如果你想入门深度学习并使用Python进行实现,可以参考一些经典的教材和资源,例如《Python深度学习》(Francois Chollet著)、《深度学习入门:基于Python理论实现》(斋藤康毅著)等。这些教材通常会介绍深度学习的基础理论Python的基本语法和深度学习框架(如TensorFlow、Keras等)的使用方法,同时也会提供一些实例代码和练习题帮助你快速上手。 此外,你也可以通过在线课程和MOOC平台学习深度学习Python编程。例如,Coursera、Udacity和edX等平台都提供了相关课程,可以根据自己的需求和兴趣进行选择。 ### 回答2: 深度学习入门:基于Python理论实现,是一本介绍深度学习的较为全面的教程。本书主要介绍了人工神经网络,包括基于反向传播算法的多层感知器、卷积神经网络、循环神经网络等基本模型以及它们的实现方法,同时还介绍了一些高级话题,如深度强化学习、生成模型等等。 在本书中,作者通过大量的编程实例来演示深度学习的应用。这些实例包括用深度学习算法进行手写数字识别、图像分类、语音识别和自然语言处理等任务。由于Python是目前流行的机器学习工具之一,因此这本书的实现过程都使用了Python编程语言。 具体来说,本书的主要内容包括人工神经网络基础知识、多层感知器模型、卷积神经网络模型、循环神经网络模型、生成模型、 强化学习、深度学习框架等方面,同时还包括很多深度学习的应用案例。作者采用了基础理论、数学公式、实例程序和实验数据等不同形式的阐释方法,使读者既能够理解深度学习的基本原理,也能够掌握它的实现方法。 此外,本书还提供了大量的参考文献和网上资源,使读者可以进一步深入学习和研究深度学习。在阅读本书的同时,读者可以根据作者提供的代码和数据,通过实际操作来进一步巩固理论知识和应用技能。 总之,深度学习入门:基于Python理论实现是一本非常实用的深度学习教材,可以帮助初学者更好地了解深度学习的基本概念和方法,提高实际应用的技能。 ### 回答3: 深度学习是一种人工智能技术,可用于训练计算机识别和理解大量数据。《深度学习入门:基于Python理论实现》这本书是入门者学习深度学习的必读之书。以下是本书的内容概述。 本书的第一部分介绍了深度学习的基础概念和理论,包括神经网络、反向传播算法、损失函数等。介绍了基本的深度学习模型,如前馈神经网络、卷积神经网络和循环神经网络。此外,还介绍了优化算法和正则化技术。 在第二部分中,作者使用Python编程语言实现了各种深度学习模型,使用的是许多广泛使用的深度学习框架,如TensorFlow和PyTorch。学习者获得从头开始编写深度学习算法的经验,同时实际应用中必备的PyTorch和TensorFlow经验。 在第三部分中,本书涵盖了几个应用案例,包括图像分类、语音识别和自然语言处理。幸运的是,这些案例通过代码演示展示,确保即使您没有实际应用经验也能操作成功。 总的来说,《深度学习入门:基于Python理论实现》是一本适合想要学习深度学习的初学者的绝佳书籍。其提供了深度学习的基本理论和核心技术,同时应用Python编程语言演示了实现技术。由此学习者可以建立深度学习专业的技术栈和能力,在人工智能领域有更广阔的发展空间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值