经过一次本科毕业设计的洗礼,让我逐渐从一个对Deep Learning一无所知的骚年变成一个准备入门的炼丹小白。回顾这几个月(虽然大部分时间都在划水),想做什么都是在谷歌百度,也很难找到一个完全适合我这种小白的Pytorch教程,网上的资料要么过时要么繁琐,最好的学习方式大概是官方的教程,但谁会看得进去呢?所以,我想把我这些日子的经历记录下来,如果有和我一样小白的同学们可以用来参考一下。
看完这个教程,你会基本学会如何使用Pytorch进行初级水平的搭积木。让我们开始吧!
一、Install
第一步,当然是要安装Pytorch啦,请移步官网,里面有给出具体的安装命令,包括conda以及pip的安装方式。当然这里强烈建议使用Anaconda,Anaconda配置教程详见这里。选择你机器的配置之后复制命令即可,如图所示:
二、Tensor
要开始介绍Pytorch之前,我们首先要知道它的计算的基本单位是什么,没错就是Tensor——张量,Tensor比较类似Numpy中的array,比如标量可以理解为0维Tensor,向量可以理解为1维Tensor,矩阵可以理解为2维Tensor,但二者又不完全相同,因为Tensor还附有更多的属性。它有哪些属性呢?让我们来看一下。
1 | import torch |
哇,它竟然有这么多的方法或属性,那我们列举几个比较常用的吧。
1. About Tensor’s Attribute
Tensor.requires_grad
首先第一个requires_grad
,是我认为最重要的,它可是Tensor区别于Numpy的灵魂所在,没有了它,怎么知道这个这个Tensor需不需要求导数呢?当我们创建Tensor的时候,它是默认为False的,但如果我们新建一个网络的时候,它的默认值就是True了。1
2
3
4
5
6
7
8
9tensor_a = torch.randn(5, 3)
print(tensor_a.requires_grad)
# False
tensor_b = torch.randn(5, 3, requires_grad=True)
print(tensor_b.requires_grad)
# True
embed = torch.nn.Embedding(5, 10)
print(embed.weight.requires_grad)
# True如果你看过有关Pytorch的代码,你可能会发现有个叫Variable的东西,在0.4版本之前,对Tensor,都会用Variable一裹,但现在没必要这么做了,现在的Tensor已经和Variable等价了,所以忘掉Variable吧。
Tensor.shape or Tensor.size()
当我们想查看一下Tensor的形状的时候,也就是我们想知道它的每一维是多少,我们可以用这两种方式:1
2
3
4
5
6
7
8
9tensor_a = torch.randn(5, 3)
print(tensor_a.shape)
# torch.Size([5, 3])
print(tensor_a.size())
# torch.Size([5, 3])
# 我们可以将这两种方法返回的结果看作是个tuple,因为你可以取出每一维的数值,而且它的值并不能被更改.
print(tensor_a.size()[0])
# 5
# 同时,还有其他几个方法,比如Tensor.dim()用来查看维数,Tensor.numel()用来查看所有元素个数.Tensor.view()
当我们需要喂给下游的网络一个对应维度的Tensor需要怎么做呢?这就可以用到Tensor.view()
了,我们可以在括号里指定维数。但需要注意的一点是,view后的Tensor变量其实是和前者共享一个存储空间的,这也就意味着如果前者发生了改变,后者对应位置的值也是要跟着改变的,反之亦然。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24tensor_a = torch.randn(5, 3)
'''
tensor([[-0.0519, -1.4556, 0.9473],
[ 1.1269, -1.1122, 0.8940],
[ 1.9436, -0.4815, -1.6614],
[ 0.6607, -0.2767, 0.9539],
[ 1.6680, -1.1298, -1.2454]])
'''
tensor_b = tensor_a.view(1, -1)
'''
tensor([[-0.0519, -1.4556, 0.9473, 1.1269, -1.1122, 0.8940, 1.9436, -0.4815,
-1.6614, 0.6607, -0.2767, 0.9539, 1.6680, -1.1298, -1.2454]])
'''
# 使用-1的意思就是根据前面的维度数值自动计算出这个维度位置的具体数值。
# 当我们修改tensor_a会发生什么呢?
tensor_a[0][0] = 0
print(tensor_b)
'''
tensor([[ 0.0000, -1.4556, 0.9473, 1.1269, -1.1122, 0.8940, 1.9436, -0.4815,
-1.6614, 0.6607, -0.2767, 0.9539, 1.6680, -1.1298, -1.2454]])
'''
# 看!它俩共享一个存储空间的吧。
# 当然,如果你不想他们共享存储空间,可以用clone()方法复制一个Tensor,之后再对其改变维度。
tensor_c = tensor_a.clone().view(1, -1)Tensor.item()
用来返回Python中的标量值。这个方法只适用于一个元素的Tensor,并不管是不是一维的,只要这个Tensor只有一个元素就可以。1
2
3tensor_a = torch.Tensor([1])
print(tensor_a.item())
# 1.0Tensor.squeeze() and Tensor.unsqueeze()
这两个方法是用来干嘛的呢?我们可以查一下squeeze的意思——挤压,没错,就是用来挤压Tensor维度的,当一个Tensor有维度值是1的时候,我们就可以用squeeze()来消除这一维,反之,可以用unsqueeze()增加一个维度值为1的维度。注意使用unsqueeze()的时候需要指定维度位置参数。1
2
3
4
5
6
7tensor_a = torch.randn(5, 3)
# shape: torch.Size([5, 3])
tensor_b = tensor_a.unsqueeze(1)
# shape: torch.Size([5, 1, 3])
tensor_c = tensor_b.squeeze()
# shape: torch.Size([5, 3])
# 当然在squeeze()中指定维度位置参数也是可以的,比如tensor_c = tensor_b.squeeze(1)Tensor.transpose() and Tensor.permute()
这两个方法用于对Tensor的维度进行交换,对于矩阵来说,也就达到了转置的功能。1
2
3
4
5
6
7
8
9tensor_a = torch.randn(3, 4, 5)
# 利用transpose()对0,1维度进行交换
tensor_b = tensor_a.transpose(0, 1)
# 传入一组维度,将对应维度按顺序生成新的size
tensor_c = tensor_a.permute(2, 1, 0)
print(tensor_b.shape)
# torch.Size([4, 3, 5])
print(tensor_c.shape)
# torch.Size([5, 4, 3])切片和索引
既然pytorch语法与Python高度相似,那Tensor一定也是支持切片与索引功能的。但注意,与列表不同,切片出的Tensor与原Tensor共享内存,也就意味着切片出的Tensor的数据修改将导致原Tensor的数据跟着修改。1
2
3
4
5
6
7
8
9tensor_a = torch.arange(1,5)
tensor_b = tensor_a[0:3:2]
print(tensor_a)
# tensor([1, 2, 3, 4])
print(tensor_b)
# tensor([1, 3])
tensor_b[0] = 0
print(tensor_a)
# tensor([0, 2, 3, 4])未完待续
BTW,最近我这个废物太懒了,什么也不想干,只想躺在床上玩手机。等我调整好状态再写下去吧,所以更新随缘吧…