PyTorch实战(1)------深度学习概述
-
- [0. 前言](#0. 前言)
- [1. 深度学习简介](#1. 深度学习简介)
- [2. 常见神经网络层](#2. 常见神经网络层)
- [3. 激活函数](#3. 激活函数)
- [4. 优化器](#4. 优化器)
- 小结
0. 前言
深度学习 (Deep Learning
, DL
) 是机器学习的一个分支,它通过模拟人脑神经网络的结构和功能,利用多层次的神经网络模型进行数据处理和学习,彻底改变了计算机用于构建现实问题自动化解决方案的方式,本节将回顾深度学习的基本概念。深度学习可以自动从大量数据中提取特征,通过复杂的非线性函数学习输入与输出之间的关系。如下图所示,常见的输入输出类型包括:
- 输入:图片;输出:文本
- 输入:文本;输出:语音
- 输入:语音;输出:转录文本

深度神经网络涉及大量的数学计算、线性代数方程、非线性函数和各种优化算法。如果我们使用 Python
等编程语言从零开始构建并训练一个深度神经网络,需要编写所有必要的方程、函数和优化算法。此外,代码还需要能够高效加载大量数据,并在合理的时间内完成训练。这意味着每次构建深度学习应用时,都需要实现许多底层细节。但和其他技术一样,我们不必从头造轮子。深度学习库已经抽象了这些细节,常用的深度学习库包括 PyTorch
、TensorFlow 和 Keras 等。
本专栏涵盖了最前沿的深度学习问题,介绍如何通过复杂的深度学习架构解决这些问题,以及如何有效使用 PyTorch
来构建、训练和评估这些复杂的模型。本专栏以 PyTorch
作为核心内容,涵盖了最先进的深度学习模型。
1. 深度学习简介
神经网络 (Neural Network
, NN
) 是机器学习 (Machine Learning
, ML
) 方法的一个子类,灵感来自于生物大脑的结构和功能,如下图所示。在神经网络中,每个计算单元(类比为神经元)通过分层的方式与其他神经元连接。当这种层数超过两层时,所形成的神经网络通常称为深度神经网络 (Deep Neural Network
, DNN
),这类模型通常称为深度学习模型。

深度学习模型因其能够学习输入数据与输出(真实标签)之间的复杂关系,通常优于其他经典的机器学习模型。近年来,深度学习得到了迅速发展,主要原因包括:
- 强大的计算设备(如
GPU
)的普及 - 海量数据的可用性
- 优秀算法的提出
现在,我们已经可以在合理的时间内训练具有数千层的深度学习模型。同时,随着数字设备的广泛使用,产生的数据量也在快速增长。因此,深度学习模型能够训练解决传统机器学习技术无法解决的复杂认知任务。
与经典机器学习模型相比,深度学习还有一个显著优势。通常,在经典的机器学习方法中,特征工程对训练模型的整体性能起着至关重要的作用。而深度学习模型不需要手动设计特征。借助大量数据,深度学习模型能够在不依赖人工设计特征的情况下,表现出优异性能,并超越传统的机器学习模型。
下图显示了深度学习模型与经典机器学习模型在不同训练数据量下的性能表现:

可以看出,在数据集规模较小时,深度学习模型的性能并不一定优于非深度学习模型。然而,随着数据规模的增加,深度神经网络开始超越非深度学习模型。
2. 常见神经网络层
深度学习模型可以基于多种不同的神经网络架构来构建,不同架构之间的主要区别在于神经网络中使用的层类型及其组合方式。常用的层类型包括:
- 全连接层 (
Fully-connected layer
):在全连接层中,如前一层的所有神经元都与后一层的所有神经元相连,如下图所示,包含两个连续的全连接层,分别包含 N 1 N_1 N1 和 N 2 N_2 N2 个神经元。全连接层是大多数深度学习分类器的基本构建单元:

- 卷积层 (
Convolutional layer
):如下图所示,卷积层使用卷积核(或滤波器)对输入进行卷积操作,卷积层是卷积神经网络 (Convolutional Neural Network
,CNN
) 的基本单元,CNN
是解决计算机视觉问题最有效的模型:

循环层 (Recurrent layer
):如下图所示,虽然它看起来与全连接层相似,但关键区别在于循环连接,递归层相较于全连接层的优势在于它具有记忆能力,这对于处理序列数据非常有用,因为它可以在处理当前输入的同时记住过去的输入:

- 反卷积层 (
DeConv layer
):卷积层的逆操作,与卷积层相反,反卷积层的工作方式如下图所示,反卷积层在空间上扩展输入数据,因此在生成或重建图像的模型中至关重要:

- 池化层 (
pooling layer
):下图展示了最大池化层 (Max-pooling
),这是最常用的池化层类型之一,最大池化层从输入的每个2x2
大小的子区域中选择最大值。池化的其他形式包括最小池化 (Min-pooling
) 和平均池化 (Average-pooling
):

基于上述层类型的一些经典架构如下图所示:

除了层的类型及其连接方式外,激活函数和优化计划等其他因素也会影响模型的行为。
3. 激活函数
激活函数 (activation function
) 对神经网络至关重要,因为它们为模型引入了非线性,如果没有激活函数,无论模型有多少层,整个神经网络都会退化成一个简单的线性模型,不同类型的激活函数本质上都是不同的非线性数学函数。常见的激活函数包括:
Sigmoid
:Sigmoid
函数表达式如下:
y = f ( x ) = 1 1 + e − x y=f(x)=\frac1{1+e^{-x}} y=f(x)=1+e−x1
函数图形如下所示,可以看出,Sigmoid
函数接收一个数值 x x x 作为输入,并输出一个在(0, 1)
范围内的值 y y y。

Tanh
:Tanh
函数的表达式如下:
y = f ( x ) = e x − e − x e x + e − x y=f(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}} y=f(x)=ex+e−xex−e−x
函数图形如下所示,可以看出,与Sigmoid
不同,Tanh
激活函数的输出y在-1
到1
之间变化,这种激活函数在同时需要正负输出的情况下非常有用。

ReLU
:ReLU
函数的表达式如下:
y = f ( x ) = m a x ( 0 , x ) y = f(x) = max (0,x) y=f(x)=max(0,x)
函数图形如下所示,可以看出,与Sigmoid
和Tanh
激活函数相比,ReLU
的一个显著特点是:当输入大于0
时,输出与输入呈线性关系,这防止了像前两种激活函数那样,梯度随着输入的增大而趋近于0
。当输入为负时,输出和梯度都是0
。

Leaky ReLU
:ReLU
会完全抑制任何负输入,输出0
。然而,有时我们也可能希望处理负输入,Leaky ReLU
提供了处理负输入的能力,它通过一个比例 k k k 来缩放负值输入,比例 k k k 是该激活函数的一个参数,数学公式表示如下:
y = f ( x ) = m a x ( k x , x ) y = f(x) = max (kx, x) y=f(x)=max(kx,x)
函数图形如下所示:

4. 优化器
我们已经讨论了神经网络的构建。为了训练神经网络,我们需要采用优化策略 (optimization schedule
)。与其他基于参数的机器学习模型一样,深度学习模型通过调整其参数来训练。模型通过前向传播计算输出,损失函数接受神经网络输出层的输出和相应的真实目标值计算损失值(误差值),模型参数通过反向传播 (backpropagation
) 过程进行调整。然后,损失通过梯度下降 (gradient descent
) 和链式法则 (chain rule of differentiation
) 反向传播到前面的各层。
每一层的参数或权重会相应地调整,以最小化损失。调整的程度由学习率 (learning rate
) 决定,学习率的取值范围是0到1。更新神经网络权重的整个过程,称为优化策略。常用优化器如下:
-
随机梯度下降 (
Stochastic Gradient Descent
,SGD
):按以下方式更新模型参数:
β = β − α ∗ δ L ( X , y , β ) δ β \beta=\beta-\alpha*\frac{\delta L(X,y,\beta)}{\delta\beta} β=β−α∗δβδL(X,y,β)其中 β \beta β 是模型的参数, X X X 和 y y y 分别是输入的训练数据和相应的标签。 L L L 是损失函数, α \alpha α 是学习率。
SGD
对于每一对训练样本 ( X , y ) (X, y) (X,y) 执行此更新。小批量梯度下降 (mini-batch gradient descent
) 是SGD
的变体,每 k k k 个样本执行一次更新,其中 k k k 是批大小,梯度会针对整个小批量的数据一起计算。批量梯度下降 (Batch Gradient Descent
),通过计算整个数据集上的梯度来执行参数更新。 -
Adagrad
:在上述优化策略中,我们为对模型的所有参数使用统一的学习率。但不同的参数可能需要以不同的速度进行更新,特别是在稀疏数据的情况下。Adagrad
引入了按参数更新的概念:
β i t + 1 = β i t − α S S G i t + ϵ ∗ δ L ( X , y , β ) δ β i t \beta_i^{t+1}=\beta_i^t-\frac{\alpha}{\sqrt{SSG_i^t+\epsilon}}*\frac{\delta L(X,y,\beta)}{\delta\beta_i^t} βit+1=βit−SSGit+ϵ α∗δβitδL(X,y,β)其中,下标 i i i 来表示第 i i i 个参数,使用上标 t t t 来表示梯度下降迭代的时间步 t t t。 S S G i t SSG_i^t SSGit 是从时间步
0
到时间步 t t t 的第 i i i 个参数的平方梯度之和。 ϵ \epsilon ϵ 是为了避免除零错误而添加的小常数。通过将全局学习率 α \alpha α 除以 S S G i t SSG_i^t SSGit 的平方根,确保对于频繁变化的参数进行较小的更新,反之亦然。 -
Adadelta
:在Adagrad
中,学习率的分母是一个随着每个时间步加入的平方项而不断增大的值,这导致了学习率会衰减到极小的值。为了解决这个问题,Adadelta
引入了只计算之前几个时间步的平方梯度和的概念。实际上,我们可以将其表示为过去梯度的移动衰减平均值:
S S G i t = γ ∗ S S G i t − 1 + ( 1 − γ ) ∗ ( δ L ( X , y , β ) δ β i t ) 2 SSG_i^t=\gamma *SSG_i^{t-1}+(1-\gamma)*(\frac{\delta L(X,y,\beta)}{\delta\beta_i^t})^2 SSGit=γ∗SSGit−1+(1−γ)∗(δβitδL(X,y,β))2其中, γ \gamma γ 是我们希望为之前平方梯度之和选择的衰减因子。通过以上方式,确保平方梯度之和不会累积到一个大值,这要归功于衰减平均值。一旦定义了平方梯度和 S S G i t SSG_i^t SSGit,我们可以使用
Adagrad
更新公式定义Adadelta
的更新步骤。通过观察
Adagrad
更新公式,可以发现均方根梯度不是一个无量纲的量,因此理想情况下不应作为学习率的系数。为了解决这个问题,我们定义了另一个移动平均值,这是针对平方参数更新的。首先,定义参数更新:
Δ β i t = β i t + 1 − β i t = − α S S G i t + ϵ ∗ δ L ( X , y , β ) δ β i t \Delta\beta_i^t=\beta_i^{t+1}-\beta_i^t=-\frac{\alpha}{\sqrt{SSG_i^t}+\epsilon}*\frac{\delta L(X,y,\beta)}{\delta\beta_i^t} Δβit=βit+1−βit=−SSGit +ϵα∗δβitδL(X,y,β)然后,可以定义参数更新的平方和:
S S P U i t = γ ∗ S S P U i t − 1 + ( 1 − γ ) ∗ ( Δ β i t ) 2 SSPU_i^t=\gamma*SSPU_i^{t-1}+(1-\gamma)*(\Delta\beta_i^t)^2 SSPUit=γ∗SSPUit−1+(1−γ)∗(Δβit)2其中, S S P U SSPU SSPU 是参数更新的平方和。得到该值后,我们就可以通过最终的
Adadelta
方程来调整Adagrad
更新公式中的维度问题:
β i t + 1 = β i t − S S P U i t + ϵ S S G i t + ϵ ∗ δ L ( X , y , β ) δ β i t \beta_i^{t+1}=\beta_i^t-\frac{\sqrt{SSPU_i^t+\epsilon}}{\sqrt{SSG_i^t+\epsilon}}*\frac{\delta L(X,y,\beta)}{\delta\beta_i^t} βit+1=βit−SSGit+ϵ SSPUit+ϵ ∗δβitδL(X,y,β)需要注意的是,最终的
Adadelta
方程不需要任何学习率。但仍然可以提供一个学习率作为乘数。因此,Adadelta
优化算法的唯一必需的超参数是衰减因子:RMSprop
:在讨论Adadelta
时,我们已经隐式地介绍了RMSprop
的内部工作原理,因为两者非常相似。唯一的区别,RMSprop
没有针对维度问题进行调整,因此更新方程仍然保持与Adagrad
相同,其中 S S G i t SSG_i^t SSGit 与Adadelta
相同。这实际上意味着,在RMSprop
中,我们需要同时指定基础学习率和衰减因子- 自适应矩估计 (
Adam
):这是另一种为每个参数计算定制学习率的优化算法。与Adadelta
和RMSprop
类似,Adam
也和Adadelta
一样使用先前平方梯度的衰减平均值。此外,它还使用先前梯度值的衰减平均值:
S G i t = γ ′ ∗ S G i t − 1 + ( 1 − γ ′ ) ∗ δ L ( X , y , β ) δ β i t SG_i^t=\gamma' *SG_i^{t-1}+(1-\gamma')*\frac{\delta L(X,y,\beta)}{\delta\beta_i^t} SGit=γ′∗SGit−1+(1−γ′)∗δβitδL(X,y,β)
S G SG SG 和 S S G SSG SSG 在数学上分别等价于梯度的一阶矩和二阶矩估计,因此这种方法被称为自适应矩估计。通常, γ \gamma γ 和 γ ′ \gamma' γ′ 接近
1
,在这种情况下, S G SG SG 和 S S G SSG SSG 的初始值可能会被推向零。为了解决这个问题,这两个量通过偏差修正重新定义:
S G i t = S G i t 1 − γ ′ SG_i^t=\frac{SG_i^t}{1-\gamma'} SGit=1−γ′SGit且:
S S G i t = S S G i t 1 − γ SSG_i^t=\frac{SSG_i^t}{1-\gamma} SSGit=1−γSSGit定义完成后,参数更新公式如下:
β i t + 1 = β i t − α S S G i t + ϵ ∗ S G i t \beta_i^{t+1}=\beta_i^t-\frac{\alpha}{\sqrt{SSG_i^t+\epsilon}}*SG_i^t βit+1=βit−SSGit+ϵ α∗SGit基本上,方程最右侧的梯度被替换为梯度的衰减平均值。需要注意的是,
Adam
优化涉及三个超参数------基础学习率、梯度的衰减率和平方梯度的衰减率。Adam
是近年来训练复杂深度学习模型最成功的优化算法之一。
优化器的选用取决于具体情况。如果我们处理的是稀疏数据,那么自适应优化器会更有优势,因为它们为每个参数提供单独的学习率更新。稀疏数据中不同参数的更新速度可能不同,因此为每个参数提供单独的学习率更新可以极大地帮助模型达到最佳解。SGD
也可能找到一个不错的解,但训练时间会显著更长。在自适应优化器中,Adagrad
的缺点是学习率分母单调递增,导致学习率逐渐消失。
RMSprop
、Adadelta
和 Adam
在各种深度学习任务中的表现非常接近。RMSprop
与 Adadelta
非常相似,唯一的区别是 RMSprop
使用基础学习率,而 Adadelta
使用先前参数更新的衰减平均值。Adam
稍有不同,它还包括梯度的一阶矩计算,并且进行了偏差修正。总的来说,在其他条件相同的情况下,Adam
可能是首选优化器。我们可以切换使用不同优化器,观察以下变化:
- 模型训练时间和收敛轨迹
- 最终模型性能
我们能够借助 PyTorch
,使用这些架构、层、激活函数和优化算法来解决不同类型的机器学习问题。
小结
深度学习 (Deep Learning
, DL
) 是机器学习的重要分支,通过模拟人脑神经网络结构和功能,利用多层次神经网络进行数据处理和学习。它能够自动从大量数据中提取特征,学习输入与输出之间的复杂关系,广泛应用于图像、文本、语音等多种输入输出场景。深度神经网络 (Deep Neural Network
, DNN
) 通过分层结构实现复杂任务,其优势在于能够处理海量数据,无需人工设计特征,并在计算设备(如 GPU
) 的支持下高效训练。常见的神经网络层包括全连接层、卷积层、循环层、反卷积层和池化层,它们组合成不同架构以解决各类问题。激活函数(如 Sigmoid
、ReLU
等)为模型引入非线性,而优化器(如 SGD
、Adam
等)则通过调整参数最小化损失函数。深度学习框架(如 PyTorch
、TensorFlow
等)封装了底层细节,简化了模型构建和训练过程,推动了深度学习在复杂认知任务中的应用。