《自然语言处理:基于预训练模型的方法》第三章 PyTorch基础

现代深度学习系统的模型结构变得越来越复杂,若要从头开始则极其耗时耗力,而且非常容易出错。幸好,看似纷繁复杂的深度学习模型,都可以分解为一些同构的简单网络结构,通过将这些简单网络结构连接在一起,就可构成复杂的模型。因此,很多深度学习库应运而生,它们可以帮助用户快速搭建一个深度学习模型,并完成模型的训练(也称学习或优化)、预测和部署等功能。

本书使用的是PyTorch开源深度学习库,它由Facebook人工智能研究院(Facebook's AI Research,FAIR)于2017年推出,可以使用Python语言调用。严格来讲,PyTorch是一个基于张量(Tensor)的数学运算工具包,提供了两个高级功能:

1)具有强大的GPU(图形处理单元,也叫显卡)加速的张量计算功能

2)能够自动进行微分运算,从而可以使用基于梯度的方法对模型参数进行优化

基于这些特点,它特别适合作为一个灵活、高效的深度学习平台。

与其他深度学习库相比,PyTorch具有如下优点:

  • 框架简洁
  • 入门简单,容易上手
  • 支持动态神经网络构建
  • 与Python语言无缝结合
  • 调试方便

因此,PyTorch获得了越来越多的用户,尤其是研究人员的青睐。

本节将简要介绍PyTorch的基本功能,主要包括基本的数据存储结构----张量,张量的基本操作以及通过反向传播技术自动计算梯度。

安装:

pip install torch

1. 张量的基本概念

所谓张量(Tensor),就是多维数组。当维度小于或等于2时,张量又有一些更熟悉的名字。例如,2维张量又被称为矩阵(Matrix),1维张量又被称为向量(Vector),而0维张量又被称为标量(Scalar),其实就是一个数值。

使用张量,可以方便地存储各种各样的数据,如2维表格数据数据可以使用2维张量,即矩阵存储,而多张表格就可以使用3维张量表示和存储。一副灰度图像(每个像素使用一个整数灰度值表示)也可以使用矩阵存储,而通常一副彩色图像(每个像素使用三个整数表示,分别代表红,绿,蓝的值)就可以使用3维张量表示和存储。

import torch
torch.empty(2,3) #创建一个形状(Shape)为(2,3)的空张量(未初始化)

torch.rand(2,3) # 创建一个形状为(2,3)的随机张量,每个值从[0,1)之间的均匀分布中采用

 

torch.randn(2,3) # 创建一个形状为(2,3)的随机张量,每个值从标准正态分布(均值为0,方差为1)中采用

torch.zeros(2,3,dtype=torch.long) # 创建一个形状为(2,3)的0张量,其中dtype设置张量的数据类型,此处为整数

torch.zeros(2,3,dtype=torch.double) # 创建一个形状为(2,3)的0张量,其中dtype设置张量的数据类型,此处为双精度浮点数

torch.tensor([[1.0,3.8,2.1],[8.6,4.0,2.4]]) #通过python列表创建张量

torch.arange(10)#生成包含0至9,共10个数字的张量

2.张量的基本运算

创建了张量后,即可以对其进行运算或操作,如加减乘除四则混合运算等。PyTorch中的加减乘除是按元素进行运算的,即将参加运算的两个张量按对应的元素进行加减乘除,如下所示。

import torch
x = torch.tensor([1,2,3],dtype=torch.double)
y = torch.tensor([4,5,6],dtype=torch.double)
print(x+y)

print(x-y)

 

print(x*y)

 

print(x/y)

 更多的运算方式可以通过torch中的函数实现,如向量点积(torch.dot)、矩阵相乘(torch.mm)、三角函数和各种数学函数等。具体示例如下:

x.dot(y) # 向量x和y的点积

x.sin() # 对x按元素求正弦值

 

x.exp() #对x按元素求e^x

除了以上常用的数学运算,PyTorch还提供了更多的张量操作功能,如聚合操作(Aggregation)、拼接(Concatenation) 操作、比较操作、随机采样和序列化等,详细的功能列表和使用方法可以参考PyTorch官方文档。

其中,当对张量进行聚合(如求平均、求和、最大值和最小值等)或拼接操作时,还涉及一个非常重要的概念,即维(Dim)或轴(Axis)。如对于一个张量,可以直接使用mean函数求其平均值。

x = torch.tensor([[1,2,3],[4,5,6]],dtype=torch.double)
x.mean()

 课件,直接调用mean函数获得的是全部6个数字的平均值。然而,有时需要对某一行或某一列求平均值,此时就需要使用维的概念。对于一个n维张量,其维分别是dim=0,dim=1,\cdots,dim=n-1。在做张量的运算操作时,dim设定了哪个维,就会遍历这个维去做运算(也叫作沿着该维于运算)其他维顺序不变。仍然是调用mean函数,当设定的维不同时,其结果也是不同的。

x.mean(dim=0)# 按第1维(列)求平均

x.mean(dim=1) # 按第2维(行)求平均

 以上演示了张量仅为2维(矩阵)的情况,当维度大于2时,其运算形式是怎么样的呢?可以使用一个简单的规则描述,即“当dim=n时,则结果n+1维发生变化时,其余维不变”。如在上面的例子中,当dim=0时,则张量形状由原来的(2,3)变为(1,3);当dim=1时,则张量形状由原来的(2,3)变为(2,1)。不过细心的读者可能会发现,以上示例的运算结果形状并非(1,3)或(2,1)的矩阵,而分别是两个向量。为了使结果保持正确的维度,聚合操作还提供了keepdim参数,默认设置为False,需要显式地设置为True。

x.mean(dim=0,keepdim=True)

x.mean(dim=1,keepdim=True)

 拼接(torch.cat)操作也是类似的,通过制定维,获得不同的拼接结果。如:

x = torch.tensor([[1,2,3],[4,5,6]],dtype=torch.double)
y = torch.tensor([[7,8,9],[10,11,12]],dtype=torch.double)
torch.cat((x,y),dim=0)

torch.cat((x,y),dim=1)

 可见,拼接操作的运算规则也同样为“当dim=n时,则结果的n+1维发生变化,其余维不变”。如在上面的例子中,当dim=0时,则由原来两个形状为(2,3)的张量,拼接成一个(4,3)的张量;当dim=1时,则由原来两个形状为(2,3)的张量,拼接成一个形状为(2,6)的张量。

3.自动微分

除了能显著提高执行速度,PyTorch还提供了自动计算梯度的功能(也叫作自动微分),使得无须人工参与,即可自动计算一个函数关于一个变量在某一取值下的导数。

通过该功能,就可以使用基于梯度的方法对参数(变量)进行优化(也叫学习或训练)。使用PyTorch计算梯度非常容易,仅需要执行tensor.backward()函数,就可以通过反向传播算法(Back Propogation)自动完成。

需要注意的一点是,为了计算一个函数关于某一变量的导数,PyTorch要求显式地设置该变量(张量)是可求导的,否则默认不能对该变量求导。具体设置方法是在张量生成时,设置requires_grad=True。

因此,计算z=(x+y)\times (y-2)的代码经过简单修改,就可以计算当x=2,y=3时,\frac{\mathrm{d} z}{\mathrm{~d} x}\frac{\mathrm{d} z}{\mathrm{~d} y}的值。

x = torch.tensor([2.],requires_grad=True)
y = torch.tensor([3.],requires_grad=True)
z = (x+y)*(y-2)
print(z)

z.backward() 
print(x.grad,y.grad)

 

 也可手工求解,即\frac{\mathrm{d} z}{\mathrm{~d} x}=y-2\frac{\mathrm{d} z}{\mathrm{~d} y}=x+2y-2则当x=2,\frac{\mathrm{d} z}{\mathrm{~d} x}\frac{\mathrm{d} z}{\mathrm{~d} y}的值分别为1和6,与以上PyTorch代码计算的结果一致。

4.调整张量形状

参与运算的张量需要蛮须一定的形状,比如两个矩阵相乘,前一个矩阵的第二维应该和后一个矩阵的第二维相同。为了做到这一点,有时需要对张量的形状进行调整。PyTorch一共提供了4种调整张量形状的函数,分别为view、reshape、transpose和permute。下面分别加以介绍

view函数的参数用于设置新的张量形状,因此需要保证张量的总的个数不变。

x = torch.tensor([1,2,3,4,5,6])

print(x,x.shape) # 打印x的内容和形状(6)

x.view(2,3) # 将x的形状调整为(2,3)

 

 

x.view(3,2)# 将x的形状调整为(3,2)

x.view(-1,3) # -1位置的大小可以通过其他维的大小推断出,此处为2

 

transpose(转置)函数用于交换张量中两个维度,参数分别为相应的维。

x = torch.tensor([[1,2,3],[4,5,6]],dtype=torch.double)
x.transpose(0,1) #交换第1维和第2维

 

不过,transpose函数只能同时交换两个维度,若要交换更多的维度,需要多次调用该函数。更便捷的实现方式是直接调用permute函数,其需要提供全部的维度信息作为的参数,即便有些维度无须交换也需要提供。

 

x = torch.tensor([[[1,2,3],[4,5,6]]],dtype=torch.double)
print(x,x.shape)

x = x.permute(2,0,1)
print(x,x.shape)

 

5.广播机制

在上面介绍的张量运算中,都是假设两个参与运算的张量形状相同。在有些情况下,即使两个张量的形状不同,也可以通过广播机制(Broadcasting Mechanism)执行按元素计算。

具体的执行规则是,首先,对其中一个或同事对两个张量元素进行复制,使得这两个张量的形状相同;然后,在扩展之后的张量上再执行按元素运算。通常是沿着长度为1的维度进行扩展,下面通过一个具体的例子进行说明。

 生成两个张量,形状分别为(3,1)和(1,2)。显然,它们不能直接执行按元素运算。因此,执行按元素运算之前,需要将它们扩展(广播)为形状为(3,2)的张量。具体扩展的方法为将x的第一列复制到第2列,将y的第1行复制到第2、3行。如下所示,可以直接进行加法运算,PyTorch会自动执行广播和按元素相加。

 6.索引与切片

规则与python语言基本一致,即索引值是从0开始的切片[m:n]的范围是从m开始,至n的前一个元素结束。

与python语言不同的是,PyTorch可以对张量的任意一个维度进行索引或切片。

 

 7.降维与升维

有时为了适配某些运算,需要对一个张量进行降维或升维。如很多神经网络模块在调用时,需要同时输入一个批次,即多个样例。如果此时只输入1个输入样例,则需要将某一个维度提升,以适配该模块的调用要求。

具体地,所谓升维,就是通过torch.unsqueeze(input, dim, out=None)函数,对输入张量的dim位置插入维度1,并返回一个新的张量。与索引相同,dim的值也可以为负数。

降维恰好相反,使用torch.squeeze(input, dim=None, out=None)函数,如果不指定dim时,张量中形状为1的所谓都将被除去。如输入形状为(A,1,B,1,C,1,D)的张量,那么输出形状就为(A,B,C,D)。当给定dim时,那么降维操作只在给定维度上。例如,输入形状为(A,1,B),squeeze(input, dim=0)函数将会保持张量不变。只有用squeeze(input, dim=1)函数时,形状才会变成(A,B)

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值