Pytorch入门教程01-张量(Tensor)的介绍

  • A+
所属分类:Pytorch快速入门
摘要这一篇是Pytorch快速入门的第一篇, 主要介绍Pytorch中张量的相关操作, 张量的类型, 张量的运算, 和CPU与GPU相互转换.

简介

PyTorch中的所有操作都是在张量的基础上进行的. 作为Pytorch的一份入门的资料, 我们就会首先从张量开始讲起, 主要分为下面几个部分:

  • 张量的定义
  • 张量的运算
  • 张量的切片
  • 改变张量形状
  • GPU还是CPU

 

在所有操作开始之前, 我们首先导入Pytorch库.

  1. import torch

 

张量的简单介绍

PyTorch中的所有内容都基于Tensor操作, 我们可以使用empty来创建一维, 二维, 或是三维的张量. 在这里我们会介绍一下几种初始化张量的方式:

  • 张量的创建
    • 创建空张量, torch.empty.
    • 创建随机张量, torch.rand或torch.randn.
    • 创建全0或全1张量, torch.zeros和torch.ones
    • 创建指定值的张量, torch.tensor(List)
    • Numpy与Tensor的相互转化(比较常用)
  • 张量的类型, 使用dtype查看或指定张量的类型
  • 张量是否自动计算梯度, requires_grad=True进行指定.

张量的创建

torch.empty可以初始化指定大小的张量, 如果不指定值的话, 内容为随机值. (注意, 我们可以通过x.size查看张量的大小)

  1. x = torch.empty(2)
  2. print(x.size())
  3. # >>  torch.Size([2])
  4. x = torch.empty(2,3)
  5. print(x.size())
  6. # >> torch.Size([2, 3])
  7. x = torch.empty(1,2,3)
  8. print(x.size())
  9. # >>  torch.Size([1, 2, 3])

我们也可以使用torch.rand来进行初始化, 他可以生成0-1之间的向量(服从uniform distribution). 使用torch.randn可以生成服从N(0,1)的数据.

下面我们生成2*3的随机向量.

  1. torch.rand(2,3)
  2. """
  3. tensor([[0.1180, 0.6572, 0.6190],
  4.         [0.9897, 0.8449, 0.1371]])
  5. """

如果想要初始化全0或是全1的张量, 可以使用torch.zeros(size) 和 torch.ones(size)来完成.

  1. x = torch.zeros(2, 2) # 全0张量
  2. print(x)
  3. """
  4. tensor([[0., 0.],
  5.         [0., 0.]])
  6. """
  7. y = torch.ones(2, 2) # 全1张量
  8. print(y)
  9. """
  10. tensor([[1., 1.],
  11.         [1., 1.]])
  12. """

除了上面的方法之外, 我们还可以创建指定值的张量, 我们使用 torch.tensor(list),

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

还有一种比较常用的做法是, 将numpy转换为tensor. 这两个数据类型之间是可以相互转换的. 我们看下面一个简单的例子.

  1. # numpy -> tensor
  2. x = np.ones((2,3))
  3. y = torch.from_numpy(x)
  4. print(y)
  5. """
  6. tensor([[1., 1., 1.],
  7.         [1., 1., 1.]], dtype=torch.float64)
  8. """
  9. # tensor -> numpy
  10. x = torch.zeros(2,3)
  11. y = x.numpy()
  12. print(type(y))
  13. # <class 'numpy.ndarray'>

有的时候, 我们需要指定更加详细的数据类型, 我们可以在numpy的时候就进行定义, 下面是一个简单的例子. 下面我们将特征数据定义为float32类型, label数据定义为long类型.

  1. # 特征数据
  2. X_train = torch.from_numpy(X_train.astype(np.float32))
  3. X_test = torch.from_numpy(X_test.astype(np.float32))
  4. # 标签数据
  5. y_train = torch.from_numpy(y_train.astype(np.long))
  6. y_test = torch.from_numpy(y_test.astype(np.long))

 

张量的类型

上面是介绍了张量定义的方法, 除此之外, 我们还可以使用x.dtype查看tensor的具体类型.

  1. x = torch.rand(2,2)
  2. print(x.dtype)
  3. # torch.float32

同样的, 我们在定义张量的时候, 也是可以指定他的类型的.

  1. x = torch.rand(2, 2, dtype=torch.float64)
  2. print(x)
  3. """
  4. tensor([[0.0942, 0.6157],
  5.         [0.6045, 0.1863]], dtype=torch.float64)
  6. """
  7. # check type
  8. print(x.dtype)
  9. # torch.float64

 

指定张量是否计算梯度

除此之外, 我们在定义张量的时候, 需要指定他是否可以计算梯度, 需要设置参数 requires_grad.

  1. x = torch.tensor([[1,2],[3,4]], dtype=torch.float32, requires_grad=True)
  2. print(x)
  3. """
  4. tensor([[1., 2.],
  5.         [3., 4.]], requires_grad=True)
  6. """

 

张量的运算

基础运算

张量的运算包括张量的加法(+), 减法(-), 乘法(*)和除法(/). 我们就可以直接使用常见的符号来进行运算. 同时, 也可以使用torch中的函数来完成, 下面简单看一下例子.

  1. x = torch.ones(2,2)
  2. y = torch.ones(2,2)
  3. # 加法
  4. z = torch.add(x,y)
  5. print(z)
  6. """
  7. tensor([[2., 2.],
  8.         [2., 2.]])
  9. """
  10. # 减法
  11. z = torch.sub(x, y)
  12. print(z)
  13. """
  14. tensor([[0., 0.],
  15.         [0., 0.]])
  16. """
  17. # 乘法
  18. z = torch.mul(x, y)
  19. print(z)
  20. """
  21. tensor([[1., 1.],
  22.         [1., 1.]])
  23. """
  24. # 除法
  25. z = torch.div(x, y)
  26. print(z)
  27. """
  28. tensor([[1., 1.],
  29.         [1., 1.]])
  30. """

高阶运算

除了完成上面的简单的加减乘除的运算, 我们还可以完成一些整体的运算. 例如我们可以对tensor进行求和运算. 下面我们按列进行求和.

  1. X = torch.tensor([[1., 2., 3.], [4., 5., 6.]])
  2. torch.sum(X, dim=1)
  3. """
  4. tensor([ 6., 15.])
  5. """

我们也可以保持维度, 按列求和的时候, 就是将一行的元素全部加起来, 最后应该是2*1的大小. 我们可以使用keepdim来实现求和前后维度的相同.

  1. X = torch.tensor([[1., 2., 3.], [4., 5., 6.]])
  2. torch.sum(X, dim=1, keepdim=True)
  3. """
  4. tensor([[ 6.],
  5.         [15.]])
  6. """

 

张量的切片

张量的切片就与Numpy中的操作是一样的. 我们简单看下面的一个例子.

  1. x = torch.rand(2, 3)
  2. print(x)
  3. print(x[1, 1])  # 表示第二行, 第二列的值. (2,2)
  4. print(x[:, 0])  # 第1列
  5. print(x[1, :])  # 第2行
  6. """
  7. tensor([[0.9756, 0.6967, 0.5652],
  8.         [0.1255, 0.2977, 0.6983]])
  9. tensor(0.2977)
  10. tensor([0.9756, 0.1255])
  11. tensor([0.1255, 0.2977, 0.6983])
  12. """

 

内存节省(关于Savig Memory)

再Pytorch中, 每次我们定义一个新的变量, 系统就会指定一个新的内存. 同时他不会实时进行内存的回收. 但是在机器学习的时候, 每个时刻都会有大量的系数, 所以我们需要写的时候注意关于内存的节省. 主要有下面两个方法, 分别是:

  • x[:] = expression
  • x += y

例如, 像下面这样直接对z进行赋值, 是会新建一个z来进行保存的.

  1. z = torch.zeros_like(y)
  2. print('id(z):', id(z))
  3. z = x + y # 这样赋值会重新分配内存
  4. print('id(z):', id(z))
  5. """
  6. id(z): 140029165236864
  7. id(z): 140029165234768
  8. """

但是如果像下面这样, 就是不会重新进行内存的分配的.

  1. z = torch.zeros_like(y)
  2. print('id(z):', id(z))
  3. z[:] = x + y # 这样是不会重新分配内存的
  4. print('id(z):', id(z))
  5. """
  6. id(z): 140029165234848
  7. id(z): 140029165234848
  8. """

同样, 我们在进行累加的时候, 可以使用x += y的写法, 是和 x=x+y是一样的.

  1. print(id(x))
  2. print(x)
  3. """
  4. 140029165148272
  5. tensor([[40., 38., 44., 42.],
  6.         [38., 40., 42., 44.],
  7.         [32., 33., 34., 35.]])
  8. """
  9. print(y)
  10. """
  11. tensor([[14., 13., 16., 15.],
  12.         [13., 14., 15., 16.],
  13.         [12., 12., 12., 12.]])
  14. """

使用x += y的方法进行累加, 可以看到内存地址是一样的. 如果使用x=x+y, 那么x会重新进行内存的分配.

  1. x += y
  2. print(id(x))
  3. print(x)
  4. """
  5. 140029165148272
  6. tensor([[54., 51., 60., 57.],
  7.         [51., 54., 57., 60.],
  8.         [44., 45., 46., 47.]])
  9. """

 

转换为One-hot向量

在大部分时候(即使是在分类任务中), 我们也是不需要转换为one-hot的格式的. 但是我们也是可以的使用scatter来进行转换, 例如下面的例子.

参考链接Convert int into one-hot format

  1. batch_size = 5
  2. nb_digits = 10
  3. # Dummy input that HAS to be 2D for the scatter (you can use view(-1,1) if needed)
  4. y = torch.LongTensor(batch_size,1).random_() % nb_digits
  5. # One hot encoding buffer that you create out of the loop and just keep reusing
  6. y_onehot = torch.FloatTensor(batch_size, nb_digits)
  7. # In your for loop
  8. y_onehot.zero_()
  9. y_onehot.scatter_(1, y, 1)
  10. print(y)
  11. """
  12. tensor([[2],
  13.         [1],
  14.         [7],
  15.         [4],
  16.         [9]])
  17. """
  18. print(y_onehot)
  19. """
  20. tensor([[0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
  21.         [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
  22.         [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
  23.         [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
  24.         [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])
  25. """

 

改变张量形状

使用view或是reshape修改形状

有的时候我们需要对张量的形状进行改变, 可以最基础的可以使用view或是reshape进行修改. 我们首先看一下使用view来修改张量 (需要注意的是使用view只是改变形状, 是共享内存的). 详细的可以查看链接, view方法修改tensor形状-详细说明

下面是使用view来进行修改的一个例子.

  1. x = torch.randn(4, 4)
  2. y = x.view(1, 16)  # 指定改变后的大小
  3. z = x.view(2, 8)
  4. print(x.size(), y.size(), z.size())
  5. # torch.Size([4, 4]) torch.Size([1, 16]) torch.Size([2, 8])

同样的内容, 我们可以使用reshape也来进行.

  1. x = torch.randn(4, 4)
  2. y = x.reshape(1, 16)  # 指定改变后的大小
  3. z = x.reshape(2, 8)
  4. print(x.size(), y.size(), z.size())
  5. # torch.Size([4, 4]) torch.Size([1, 16]) torch.Size([2, 8])

关于view和reshape的区别在于, It means that torch.reshape may return a copy or a view of the original tensor. (也就是使用reshape可能是返回的一个copy, 而不会共享内存). 可以参考链接, What's the difference between reshape and view in pytorch?

当然, 使用view或是reshape的时候, 可以有一个维度是不指定大小的, 使用-1来代替.

  1. x = torch.ones(3,5,5)
  2. y = x.view(3,-1)
  3. print(y.size())
  4. # torch.Size([3, 25])

 

GPU还是CPU

在实际计算中, 我们需要指定使用CPU还是GPU, 首先我们需要检查GPU是否可用.

  1. torch.cuda.is_available()

我们需要指定tensor是使用GPU还是CPU. 我通常会写成下面这个样子. 这里使用to是首先创建张量, 接着将张量移动到GPU上面.

  1. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  2. x = torch.tensor([1,2,3]).to(device)

 

  • 微信公众号
  • 关注微信公众号
  • weinxin
  • QQ群
  • 我们的QQ群号
  • weinxin
王 茂南

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: