C. PYTORCH 101 AN INTRODUCTION TO DEEP LEARNING USING PyTorch
作者:禅与计算机程序设计艺术
1.简介
自2019年以来的十多年间,人工智能领域的热潮持续了多年。
在阅读本文之前,建议您已经掌握了Python编程基础以及线性代数、微积分和统计学的相关知识,并对机器学习有一定的理论了解
2.核心概念
2.1 Pytorch的安装
PyTorch安装非常简单,只需要通过Anaconda(一个开源的Python发行版)或者pip包管理器即可快速完成安装。如果您的计算机上已经安装了Anaconda,那么直接在命令提示符下输入以下命令即可完成安装:
pip install torch torchvision
如果您没有安装Anaconda,那么可以选择下载安装包手动安装。PyTorch提供两种安装方式:
为开发人员和研究人员提供的源代码编译安装方案。具体步骤如下:首先通过GitHub获取最新版本的源代码并进行克隆或下载;其次,在系统环境中正确配置相应的依赖库,并严格按照README指导进行编译和安装。
(2)已经预先打包好的wheel文件安装(通常适用于普通用户):针对运行在Linux或macOS系统的用户,可以直接从PyTorch官方网站下载相应版本的wheel包。例如,在Windows系统上使用Python 3.7时,可以通过访问https://download.pytorch.org/whl/torch_stable.html#cpu并使用pip命令进行安装。
完成安装后,则可利用python启动Python交互模式。
import torch
print(torch.__version__)
代码解读
如果能够正常打印出版本信息,则说明安装成功。
2.2 计算图(Computational Graphs)
PyTorch的基本数据架构是运算图(operational graph),它被用来模拟动态运算流程。该架构由节点实体(nodes)与连接线(edges)构成。其中每个节点实体负责特定的操作如矩阵乘法、加法等基础算子;而连接线则定义了各实体间的关联关系。例如下文所示:上文展示了一个简单的运算流程。它包含了三个操作实体A、B、C;以及四个连接线A->C、B->C、A->B、B->C。这些连接线表示了操作间的依赖关系。一般而言该运算流程具有动态特性,在输入输出及算子变化时会相应调整。在PyTorch中可以通过创建变量实例(Variable)并启用自动求导机制(Autograd)来构建这种架构模型
2.3 Tensors
在PyTorch中,张量(tensors)被视为一种核心的数据结构。它是一种多维数组(multidimensional array),其中所有元素具有相同的数据类型。这种多维数组不仅可以是二维或三维空间中的二维、三维张量(tensor),还可以扩展至更高维度的空间以适应复杂的计算需求。该库提供了大量函数和操作(functions and operations),这些工具能够显著简化张量相关的计算流程。
2.3.1 构造函数
PyTorch包含多种多样的功能模块用于生成和设置tensors。这里详细介绍了几个典型的应用场景中使用广泛的是torch.zeros、torch.ones以及torch.randn这些功能。
该函数用于生成一个包含数据的张量(Tensor),其形状、数据类型和设备可在函数调用时指定。
zero() 和 one() 函数:生成全是零或全是1的 tensor。这些参数在函数调用时可灵活指定
randn()/rand():遵循标准正态分布/等概率分布生成张量,在函数调用时可指定其数据类型与设备参数设置。
arange() 和 linspace():用于生成一系列数值的张量,在调用该函数时即可设定其形状、数据类型以及执行设备。
from_numpy():将numpy数组转换为tensor。
to_numpy():将tensor转换为numpy数组。
view() / reshape():改变tensor的形状。
permute():改变tensor的轴顺序。
scatter_() / gather():向tensor中指定的位置插入/提取值。
split() / chunk():将tensor切分成多个子tensor。
stack() / cat() / expand():将多个tensor叠加/拼接/扩展。
通过将多个函数进行组合处理的方式制造出复杂的计算图,并经过运算后生成相应的张量
2.3.2 运算操作
借助算术操作符(arithmetic operators),我们可以对 tensors 执行一系列基本数学运算操作包括加减乘除指数对数以及平方根等函数。PyTorch采用了广播机制(broadcasting mechanism)这一特性使得即使张量具有不同的形状也可以实现它们之间的计算过程实现了按位的操作逻辑。此外PyTorch不仅提供了丰富的统计指标随机数生成器以及傅里叶变换工具更涵盖了线性代数相关的功能使其在科学计算领域表现尤为突出。
2.3.3 索引操作
通过索引操作可有效提取指定位置或范围内的数据。在神经网络的训练过程中,索引操作扮演着关键角色。PyTorch提供了丰富的索引操作类型,具体包括整数型、布尔型、向量型、元组型以及指针型等多种选择。
2.3.4 操作约定
大部分运算操作都支持广播机制。具体的规则为:
当两个张量的形状不同时,会尝试使用广播机制进行匹配。
如果两个张量在某一维上的长度均为1,并且另一个张量在该维上的长度不等于1,则第一个张量会被广播至第二个张 tensor对应的维数。
当两个张量的某个维度长度都为1但形状不同时,会报错。
当一个张量的某个维度被广播之后,其他维度的长度需要保持一致。
2.4 Autograd
它是 PyTorch 中实现自动生成导数计算图的关键组件。该工具能够追踪每一个操作并生成支持求导功能的计算图结构。基于动态计算图的设计使得反向传播能够在不增加内存消耗的情况下迅速完成梯度计算。作为灵活且高效的工具,Autograd 支持嵌套多层模型构建过程中的每一个细节设计。
2.4.1 模型定义
利用 Autograd 来建立模型,涉及三个步骤:
创建一个包含可学习参数的 Module,它继承自 nn.Module。
在这个 Module 中定义前向传播函数。
将损失函数和优化器添加到模块中。
下面是一个简单的示例:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(10, 20)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(20, 10)
def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return F.log_softmax(x, dim=1)
net = Net().to('cuda') # GPU acceleration
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.01)
for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data[0].to('cuda'), data[1].to('cuda')
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
print('[%d] loss: %.3f' % (epoch + 1, running_loss))
代码解读
该示例构建了一个具有单个隐藏层的神经网络模型;其输入维度设定为10,并输出维度也被设定为10以完成分类任务;该模型采用cross-entropy loss函数与stochastic gradient descent (SGD)优化算法进行训练。
2.4.2 模型参数
模型参数指的是在机器学习过程中动态调整的变量,在使用反向传播算法进行优化时会不断更新这些参数。初次运行模型之前,在系统中所有未训练过的权重都被设定为初始随机值以启动训练过程。
模型参数指的是在机器学习过程中动态调整的变量,在使用反向传播算法进行优化时会不断更新这些参数。初次运行模型之前,在系统中所有未训练过的权重都被设定为初始随机值以启动训练过程。
可以通过调用 model.parameters() 获取所有模型参数这一操作实现。其返回结果是一个 generator object。
此外还可以通过优化某些参数来避免它们参与反向传播过程。具体来说在代码中只需将这些参数的requires_grad属性设置为False即可
这里有一个例子:
for param in net.parameters():
if param.requires_grad == True:
print(param.shape)
代码解读
在此时时刻,在网络中仅输出了可训练的参数(即那些未被优化的参数),这表明权重矩阵 fc1.weight 和 bias 被指定为其梯度要求。
2.5 迁移学习 Transfer Learning
迁移学习构成深度学习的核心关键技术。该方法可利用预训练模型参数作为工具来辅助解决新问题。此过程被称作微调(fine-tuning),此方法可显著减少所需计算资源的时间消耗。
该框架提供了丰富的预训练模型库,可作为迁移学习的基础框架。其中一些著名的示例包括 AlexNet、VGG 和 ResNet 等深度学习核心算法。这些模型均为深度学习领域的核心技术奠定了基础,并在该数据集上获得了优异的效果。它们可为多样化的图像识别应用场景提供可靠的基础参考。
以下是一个采用 ResNet50 模型作为基准模型,在经过 CIFAR-10 数据集上的微调训练来实现网络优化的例子:
base_model = models.resnet50(pretrained=True)
# Freeze all the parameters in the base model
for param in base_model.parameters():
param.requires_grad = False
# Add some new layers at the end of the base model
num_ftrs = base_model.fc.in_features
add_layers = nn.Sequential(
nn.Linear(num_ftrs, 256),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(256, num_classes)
)
# Replace the last layer with our new layers
base_model.fc = add_layers
# Use a small learning rate since we are finetuning
optimizer = optim.Adam(base_model.parameters(), lr=0.001)
# Define a new network that uses the pretrained model as the base model
model = MyNetwork(base_model)
train_model(model, device, dataloaders, dataset_sizes,
criterion, optimizer, scheduler, num_epochs)
代码解读
在该案例中,默认采用ResNet50作为基准架构,并在经过深度学习框架中的后续结构处理后,在最后两层添加了一个全连接层。这种设计思路通过模块化的方式,在CIFAR-10数据集上实现了类似于图像分类的效果。为了实现更好的泛化能力,在微调过程中采用了相对较低的学习率设置。
一旦模型训练完成,我们就可以把它保存起来,以备后用。
更多迁移学习相关的内容,可以参考官方文档:
https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html
3.神经网络模型搭建
神经网络模型作为深度学习的核心技术之一,在现代人工智能领域发挥着至关重要的作用。在本节中,我们将深入阐述神经网络模型的核心概念及其构建方法。
3.1 激活函数 Activation Function
在神经网络体系中,激活函数扮演着不可或缺的角色。不仅在神经网络的各个层次中引入非线性因素,并且有助于提升模型的能力。其重要功能不仅在于向各层传递信号时加入非线性因素,在缓解梯度消失和爆炸问题方面也发挥了关键作用。
有几种常见的激活函数:
sigmoid函数:该函数呈现出S型曲线特征,并具有明确的数学表达式:f(x)=\frac{1}{1+e^{-x}}。当输入数值较大或较小的时候,该函数的输出结果不易察觉。
双曲正切函数也被广泛称为tanh函数。其数学表达式是:对于任意实数x来说f(x)=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}。双曲正切函数的输出值位于区间[-1, 1]内,并且通常被认为是更为理想的替代选择与Sigmoid函数相比
ReLU函数简称为Rectified Linear Unit(修正线性单元),其数学表达式为f(x)=\max\{0,x\}。它是一个非线性的函数,在输出时将所有负值置零从而避免了Sigmoid函数中出现的梯度消失和梯度爆炸问题。
Softmax函数用于归一化输入数据,并将每个数值限制在区间[0,1]内使其总和等于1。其数学表达式定义为f(x_{i})=\frac{\exp(x_{i})}{\sum_{j}\exp(x_{j})}(其中i表示类别编号)。它常用于分类任务中。
Leaky ReLU函数属于生物学术语范畴,在生物科学文献中被视为一种重要的数学工具。其数学表达式定义为f(x)=max\{\alpha x,x\}, 其中\alpha是可调节的关键参数,默认设置为0.01。这种激活函数设计能够减弱输入信号的负面效果但无法完全消除这些负向影响。
该激活函数被称为Exponential Linear Unit(指数线性单元),其简称为ELU函数。其数学表达式如下所示:当x大于等于零时,f(x)=x;否则f(x)=α*(e^x - 1),其中α是一个可调节的参数,默认情况下取值为1。该激活函数既能有效处理输入中的负数值问题,在输入较大时也能保持输出稳定
该函数代表Parametric ReLU的简写形式,并由以下数学表达式定义:f(x)=\max\{0,\alpha*\cdot x\},其中\cdot表示逻辑回归领域中的符号函数。该激活函数通过赋予不同输入特征各自独特的权重系数,在不同输入空间中展现出多样化的响应模式。
Mish function: Mish function is an abbreviation for Self Regularized Non-Monotonic Neural Activation Function, also known as adaptive regularized non-monotonic neural activation function. Its mathematical expression is given by f(x)=x\cdot \tanh(\text{softplus}(x)). This activation function demonstrates exceptional capability in capturing nonlinear characteristics within neural networks and achieves performance comparable to that of ReLU and SELU activation functions.
激活函数的选择方式也十分关键,在实际应用中需根据具体需求来确定。每个激活函数都展现出各自的特性。例如在数据分布不均衡的情况下建议采用带有类别权重平衡机制的损失函数作为优化目标。
3.2 卷积层 Convolutional Layer
卷积层是卷积神经网络的核心组成部分,在图像处理任务中扮演着关键角色。该层能够提取图像中的关键特征,并通过多层堆叠实现复杂的特征表示和分类功能。
卷积层的核心概念在于通过可学习的滤波器(Convolution Kernel)执行空间域上的信号处理过程,在此过程中系统能够识别并提取具有独特模式的空间特征。通常采用尺寸为N imes N的小型滤波器矩阵(Matrix),其中参数N被定义为滤波器的空间维度。这种线性变换过程能够有效从输入数据中提取关键信息。
根据卷积的定义,卷积层的计算公式可以写成:
Z=(W \ast X)+b
其中,X是输入数据,W是卷积核,b是偏置项,*表示卷积运算,z是输出数据。
在PyTorch中,卷积层可以通过Conv2d()函数来实现:
conv = nn.Conv2d(in_channels, out_channels, kernel_size)
参数含义:
in_channels:输入通道数,即输入数据的通道数。
out_channels:输出通道数,即过滤后的特征图的通道数。
kernel_size:卷积核大小。
PyTorch支持多种类型的卷积核,常用的有:
标准卷积核:由3 imes 3、5 imes 5、7 imes 7构成的各种尺寸的卷积核。
深度卷积核:它主要用于密集型的深度特征学习任务中,在这些领域中常见于各种复杂场景下的数据处理与分析工作。例如图像分类等应用中广泛使用该技术以实现高效的特征提取与识别功能。其卷积核尺寸通常为 3\times3, 5\times5, 7\times7, 和 9\times9.
时序卷积核:主要用于序列数据分析任务中,在语音识别等场景中广泛应用。其典型配置通常采用的卷积核尺寸包括3×d、5×d和7×d结构(其中d值常取2-4)。
空洞式卷积核:在实际应用中发现该类网络架构仅对输入数据局部区域进行卷积操作(即仅受输入数据局部区域进行卷积作用),其典型的尺寸设置通常包括3×3+δ、5×5+δ以及7×7+δ三种配置方案(其中符号δ代表零填充度)。
在处理不同类型的的任务时,卷积核的选择也会有所差异;例如,在进行文本分类时,则可采用词嵌入或BERT等技术以获取相关的特征信息。
3.3 池化层 Pooling Layer
池化层的功能是对其输入的特征图进行整合处理。该过程旨在减少运算量,并抑制模型过拟合的能力。常用的手段包括最大值采样(max pooling)、平均值采样(average pooling)以及局部响应归一化(local response normalization)。
最大池化是一种特殊的池化操作。该操作通过在输入数据的空间窗口中选取最大值来生成特征图。其数学表达式如下所示:
Y_{i}=max(X_{ij}), j=1,2,\cdots,k; k=window\_size
在本节中,我们假设输入样本集\{X\}由若干二维向量构成,其中每个向量对应于一个特定的任务场景.具体来说,输入数据\mathbf{X}中的每一个样本\mathbf{X}_{i}都可以被分解为m \times n维的空间点阵形式.特别地,对于每一个空间点阵中的某个位置(i,j),其(i,j)位置处的值即为对应于该场景下某个性质的表现程度.
同样地,输出样本集\{Y\}则由若干个一维向量组成,每个向量代表了与之对应的某种属性的变化情况.具体而言,对于每一个输出样本\mathbf{Y}_{j}而言,其长度等于系统所处理的不同属性的数量.
在深度学习中, 平均池化也被称为一种特殊的池化操作. 该过程通过计算输入数据窗口内的所有元素的算术平均值来生成输出结果. 其表达式为:
Y_{i}=(\frac{1}{k}\Sigma_{j=1}^{k}X_{ij}), j=1,2,\cdots,k;
其中,在输入样本集中的每个样本点(X_{i1}, X_{i2}, \dots, X_{in})^T \in \mathbb{R}^n)都对应于输出结果集中的一个样本点(y_1, y_2, \dots, y_m)^T \in \mathbb{R}^m);对于任意两个不同的样本点(x_1^{(k)}, x_2^{(k)}, \dots, x_n^{(k)})^T)和(x_1^{(l)}, x_2^{(l)}, \dots, x_n^{(l)})^T)属于同一个类别时,则有k=l\Rightarrow y_k = y_l\Rightarrow Y_i = Y_j\Rightarrow i=j\Rightarrow X_i = X_j\Rightarrow X_i - X_j = 0\Rightarrow ||X_i - X_j||=0\Rightarrow d(X_i,X_j)=0.
局部响应规范化:该方法能够对卷积层的输出进行归一化处理,从而使得梯度的变化更加趋于稳定;其数学表达式如下所示:
Z^{l}_{ji}=\gamma_j\hat{a}^l_{ji},
其中,在第l层的位置上计算得到的结果矩阵中元素为\gamma_j\cdot Z^{l}_{ji};\hat{a}^l=\sigma(W^{l}Z^{l}+b^l)是经过卷积运算后的输出结果;这里i,j,l,k,m,n,s,t,u,v,w,x,y,z,Z,W,b,a,\sigma,\gamma,\tau,\omega,\theta,\lambda\rho\nu\xi\eta\delta\mu\epsilon\zeta\eta\theta\omega\phi\psi\chi\gamma,j,i,l,k,m,n,s,t,u,v,w,x,y,z,Z,W,b,a.
在PyTorch中,池化层可以通过MaxPool2d()和AvgPool2d()函数来实现:
pool = nn.MaxPool2d(kernel_size, stride=None, padding=0)
或
pool = nn.AvgPool2d(kernel_size, stride=None, padding=0, ceil_mode=False)
参数含义:
kernel_size:池化核的大小。
stride:步长,默认值为None,表示每次移动步长为kernel_size。
padding:填充,默认值为0,表示没有填充。
ceil_mode:该变量决定了输出尺寸是否向上或向下舍入。其默认设置为False,则表示系统将尺寸进行下舍入处理。
池化层可以提升网络的鲁棒性和性能,防止过拟合并降低泛化能力。
3.4 全连接层 Fully Connected Layer
全连接层负责处理神经网络的最后任务,在神经网络中通常位于卷积层或池化层之后,并主要应用于执行分类任务。它通过将输入数据转换为一维向量,并通过矩阵乘法运算获得输出数据。其计算公式如下:
output=\sigma(WX+b)
在其中,权重矩阵W接收输入数据X作为输入,并通过激活函数\sigma引入偏置项b。全连接层通常用于处理分类问题。
在PyTorch中,全连接层可以通过Linear()函数来实现:
linear = nn.Linear(in_features, out_features)
参数含义:
in_features:输入特征的维度。
out_features:输出特征的维度。
全连接层可以对输入数据进行高维度的映射,适用于复杂的分类任务。
3.5 循环神经网络 Recurrent Neural Networks
循环神经网络(Recurrent Neural Network, RNN)属于一种深度学习模型。
能够建模各种各样的序列数据。
包括时间序列、自然语言处理中的文本以及视频中的时空信息。
RNN能够记住之前的上下文信息,并通过这种能力帮助神经网络识别和利用长程依赖关系的信息。
RNN属于一种递归架构,在其运行过程中能够将前一时间步的输出作为当前时间步的输入信号,并通过这种方式完成序列数据建模的任务。在PyTorch框架中,实现这一功能可通过调用LSTM()和GRU()等模块来完成任务。
长短期记忆单元:作为独特的递归神经网络结构,LSTM层通过门控机制对输入数据进行选择性处理,并显著增强模型在信息捕捉与表示能力方面的性能。
在序列模型中,GRU层被描述为一种具有独特结构的RNN单元。它通过门控机制对输入数据进行处理,并不包含细胞状态这一特殊组件。这种设计使得其相比传统的RNN能够更快地捕获短期信息特征;然而,在面对需要长期记忆的问题时,则容易表现出性能上的不足。
3.6 注意力机制 Attention Mechanism
注意力机制是一种多任务学习的途径,并有助于神经网络自动关注输入数据中的重要部分。注意力机制可以帮助神经网络捕捉到数据中的全局关系,并能够有效地进行多任务学习。
Attention mechanism的原理在于将输入数据分割为多个子空间,并通过一个注意力权重矩阵来指导神经网络的注意力分配。具体而言,该机制主要包含以下三个步骤:
把输入数据划分成多个子空间。
生成注意力权重矩阵。
利用注意力权重矩阵来进行多任务学习。
在PyTorch中,可以用 nn.MultiheadAttention()函数来实现注意力机制。
该结构被称为多头注意力机制。该结构由多个子结构构成,并由每个子结构负责生成不同的注意力权重矩阵。
该结构被称为多头注意力机制。该结构由多个子结构构成,并由每个子结构负责生成不同的注意力权重矩阵。
Scaled Dot-Product Attention:这是一种最基础的注意力机制,在其运作中基于点积运算计算出注意力权重,并经过缩放处理以避免数值溢出。
GPT-2系统:该系统是一种基于Transformer架构的语言模型。该系统由多层自注意力机制构成,并具备能力去学习语言模型的语法和上下文关系。
