编程语言和自然语言一样,不理解的词越多,对全文的理解就越差。掌握必要的基础知识,让后期看代码更加流畅。
机器学习需要掌握数据处理工具Pandas、Numpy,同理,深度学习也需要掌握相应的工具,在Pytorch中数据存储在Tensor之中,本篇将介绍它们的基本用法以及与之相关的常用函数。
查看版本信息
包含头文件
1. import torch
1.查看torch版本
1. print(torch.__version__)
2.查看CUDA版本
1. print(torch.version.cuda)
GPU相关操作
1. 查看当前是否支持GPU
1. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
2.GPU相关的数据转换
1. a = torch.Tensor([5])
2. b = a.to('cuda') # 转成在 GPU 上运行的类型
3. b = a.cuda() # 同上
4. c = b.to('cpu') # 转成在 CPU 上运行的类型
5. c = b.cpu() # 同上
3. 查看GPU状态
1. print(torch.cuda.device_count()) # 查看可用 GPU 个数
2. print(torch.cuda.current_device()) # 查看当前使用的 GPU ID
3. print(torch.cuda.get_device_capability(0)) # 查看 ID 为 0 的 CUDA 容量
4. print(torch.cuda.get_device_name(0)) # 查看设备名
4. 清空GPU缓存
1. print(torch.cuda.empty_cache()) # 清空缓存
Tensor
Tensor用于表示矩阵(多维数据),类似Numpy的ndarray,它可以使用GPU加速。从Pytorch 0.4.0之后,Variable与Tensor合并(Variable仍存在,但很少使用)。
创建Tensor时参数requires_grad默认为False,若设为True,则可实现自动求导的功能,梯度保存在其内部变量grad中,计算梯度的方法存储在grad_fn中。
1.生成Tensor
用Tensor方法将其它格式数据转换成张量,转换内容一般是list或数组格式:
1. a = torch.Tensor([[1,2],[3,4],[5,6]]) # 生成 3,2 维Tensor
2. b = torch.zeros(3,5) # 生成内容为全0的 3,5 维Tensor
3. c = torch.rand(3,5) # 生成内容为 0-1 随机数的 3,5 维Tensor
4. d = c.clone() # 将c中内容复制到 d, 新旧内容指向不同的地址空间
2.修改Tensor
用赋值的方法即可修改张量,比如将上面生成张量中位置1,1的元素赋值成50。
1. a[1,1]=50
3. 查看Tensor状态
查看Tensor数据类型,大小及元素个数
1. a = torch.Tensor([5])
2. print(a.type()) # torch.FloatTensor,默认类型为FloatTesor
3. print(a.size()) # torch.Size([1])
4. print(a.shape) # torch.Size([1])
5. print(a.numel()) # 1,查看元素个数
6. print(a.dim()) # 1, 查看维度
4.类型转换
Tensor与其它数据类型相互转换
1. a = torch.Tensor([5]) # tensor([5.])
2. b = a.numpy() # 转换成numpy.array类型 [5.]
3. c = a.item() # 转换成单个值 5.0
4. d = torch.from_numpy(b) # 转换成Tensor tensor([5.])
5. e = d.int() # 转换成 IntTensor tensor([5], dtype=torch.int32)
6. f = d.tolist() # 转换成list [5.0]
维度变换
1.增加维度
在深度学习过程中,现有的数据格式和模型要求的格式往往不同,层与层之间的数据也需要转换后才能对接,维度转换是最常用的方法。
squeeze意为压缩,即去掉维度,unsqueeze则相反,为添加维度。
1. unsqueeze(input, dim, out=None)
用于增添第dim维度为1。具体用法见以下示例:
1. a = torch.Tensor([1,2,3])
2. print(a, a.shape) # tensor([1., 2., 3.]) torch.Size([3])
3. b = torch.unsqueeze(a, 1)
4. print(b, b.shape) # tensor([[1.],[2.],[3.]]) torch.Size([3, 1])
5. c = torch.unsqueeze(a, 0)
6. print(c, c.shape) # tensor([[1., 2., 3.]]) torch.Size([1, 3])
2.减小维度
1. squeeze(input, dim=None, out=None)
用于删除第dim个维度,如果当前不包括指定的维度,则不会删除。如果不指定dim,函数将删除值为1的维度。
本例中延用上例中的数据:
1. print(torch.squeeze(c,0)) # tensor([1., 2., 3.])
2. print(torch.squeeze(b,1)) # tensor([1., 2., 3.])
3. print(torch.squeeze(b)) # tensor([1., 2., 3.])
3.转换维度
比squeeze和unsqueeze更简单的方法是直接把张量转换成指定维度,使用view函数实现,它类似于numpy的reshape,Tensor也提供reshape方法。
1. view(*shape)
将张量转换成指定的形状,示例:
1. x = torch.Tensor([1,2,3,4])
2. print(x.view(2,2)) # tensor([[1., 2.], [3., 4.]])
3. print(x.view(1,4)) # tensor([[1., 2., 3., 4.]])
4. print(x.view(1,-1)) # 设为-1时自动计算该维度大小 tensor([[1., 2., 3., 4.]])
5. print(x.view(4)) # tensor([1., 2., 3., 4.])
4.cat拼接
cat函数用于在指定维度上拼接多个张量。
1. cat(tensors, dim=0, out=None)
将tensors中的多个张量按dim指定的维度拼接成一个张量,拼接后总维数不变。
1. x = torch.Tensor([[1,2],[3,4]])
2. y = torch.Tensor([[5,6],[7,8]])
3. print(torch.cat((x,y),0))
4. # tensor([[1., 2.],
5. # [3., 4.],
6. # [5., 6.],
7. # [7., 8.]])
8. print(torch.cat((x,y),1))
9. # tensor([[1., 2., 5., 6.],
10. # [3., 4., 7., 8.]])
一般面对的数据最多三维,并以一两维居多,可将其理解为横向加或者纵向加。
5.stack拼接
与cat拼接不同的是,stack拼接后维度增加,其用法如下:
1. stack(tensors, dim=0, out=None)
示例:
1. x = torch.Tensor([1,2])
2. y = torch.Tensor([3,4])
3. print(torch.stack((x,y),dim=0))
4. # tensor([[1., 2.],
5. # [3., 4.]])
6. print(torch.stack((x,y),dim=1))
7. # tensor([[1., 3.],
8. # [2., 4.]])
从输出内容可以看到,拼接后张量变成了两维,dim=0是最常用的方法,它直接把两个张量拼在一起,当dim=1时,拼接时转换了位置。
6.transpose两维度互换
1. transpose(input, dim0, dim1)
互换dim0, dim1两个维度,具体用法如下:
1. x = torch.Tensor([[1,2],[3,4]])
2. print(torch.transpose(x,0,1))
3. # tensor([[1., 3.],
4. # [2., 4.]])
7.perumute多维度互换
permute与transpose功能类似,但更加灵活,它可以指定多个维度互换。
1. x = torch.Tensor([[1,2],[3,4]])
2. print(torch.transpose(x,0,1))
3. # tensor([[1., 3.],
4. # [2., 4.]])
用于将张量转换成dim指定的维度。
1. x = torch.rand(2,3,4)
2. print(x.shape, x.permute(2,1,0).shape)
3. # torch.Size([2, 3, 4]) torch.Size([4, 3, 2])
本例先产生了一组3维的随机数,每个维度的元素个数分别是2,3,4,然后使用permute将其第2维转成第0维,第1维不变,第0维转成第2维,从打印信息可以看到各维元素个数的变化。permute常用于对转换图片格式。
8.维度扩展
增加Tensor中元素的个数,其内容与被扩展的内容一致。
1. a = torch.Tensor([5])
2. print(a.expand(1, 2)) # tensor([[5., 5.]])
求导
下例中使用了torch.tensor而非torch.Tensor,torch.Tensor是Pytorch中的类,确切地说是FloatTensor的别名;而torch.tensor()是函数,它更加灵活,使用方法如下:
1. torch.tensor(data, dtype=None, device=None, requires_grad=False)
它可以创建各种类型的Tensor数据。
下面声明三个变量x,a,b,用它们计算变量y,之后用y.backward()求y对x,a,b三个变量的偏导数,这是深度学习中常说的“反向”过程。结果存储在变量的grad元素中,如x.grad。
1. x = torch.tensor([1.0], requires_grad=True)
2. a = torch.tensor([2.0], requires_grad=True)
3. b = torch.tensor([3.0], requires_grad=True)
4. y = a * x + b
5. y.backward()
6. print(x.grad) # 输出结果: tensor([2.])
7. print(a.grad) # 输出结果: tensor([1.])
8. print(b.grad) # 输出结果: tensor([1.])
9. print(b.data) # 输出结果: tensor([3.])
从输出结果可以看到,Tensor包含两个元素,data是它的Tensor值,grad保存的是求得的偏导数。
剥离无梯度变量
detach方法用于从普通Tensor中剥离出无梯度的变量。下面延用上例求导后有梯度的数据b。
1. print(b) # tensor([3.], requires_grad=True)
2. print(b.detach()) # tensor([3.])
3. print(b.detach_()) # tensor([3.])
detach和detach_的区别是detach获取b的data数据,而detach_则是将b中的grad_fn设置为None,requires_grad设置为False,从而改变了b的内容。
Parameter
一般模型参数都是torch.nn.parameter.Parameter类型的数据,它继承了Tensor,主要不同是requires_grad默认为True,以便于模型调参。
常在两种场景中使用Parameter,一种是fine-tune精调时冻结模型中某些层的参数;另一种是自己实现一些特殊的结构,如构建Attention时,用Parameter创建需要调节的参数。
1.创建Parameter数据
1. p = torch.nn.Parameter(torch.randn(2))
2. print(p) # tensor([1.2087, 1.2607], requires_grad=True)
2. 查看模型的Parameter参数,大小及冻结情况
Pytorch模型的基类是Module,其中维护一个名为_parameters的字典,它包含每一个子元素的名字(key)和value参数(value)。如需要冻结某层,将其require_grad设为False即可。
1. model = torch.nn.Linear(1,1)
2. for name, param in model.named_parameters():
3. print(name, param.size(), param.requires_grad)
4. # 输出结果:
5. # weight torch.Size([1, 1]) True
6. # bias torch.Size([1]) True