文章目录
PyTorch 基础
0. 概述
在开始深度学习项目之前,选择一个合适的框架是非常重要的。常见的深度学习框架包括 TensorFlow, Keras, PyTorch, Caffe, Theano, CNTK, PaddlePaddle 等。本次实验我们将选用 PyTorch,并学习 PyTorch 的背景知识和一些基本使用方法。最后,我们将基于 PyTorch 搭建一个卷积神经网络 (Convolutional Neural Network, CNN),并在数据集上进行测试。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eTXI6ZNb-1642942422380)(C:\Users\张清望\AppData\Roaming\Typora\typora-user-images\image-20220123192851747.png)]
在介绍 PyTorch 之前,不得不先介绍 Torch。Torch 是一个有大量机器学习算法支撑的科学计算框架,是一个与 Numpy 类似的张量(Tensor) 操作库,其诞生已经有十年之久,但是真正起势得益于 Facebook 开源了大量 Torch 的深度学习模块和扩展。Torch 的特点在于特别灵活。但因其采用了小众的编程语言 Lua,在目前深度学习大部分都采用以 Python 为编程语言的大环境下,流行度不高,这也就有了 PyTorch 的出现。PyTorch 使用 Python 作为开发语言,其前身就是 Torch,底层和 Torch 框架一样,但是使用 Python 重写了很多内容。
PyTorch 是一个基于 Torch 的 Python 开源机器学习库,可用于计算机视觉、自然语言处理等任务。它主要由 Facebook 的人工智能小组开发,不仅能够实现强大的 GPU 加速,同时还支持动态神经网络,这一点是现在很多主流框架如 TensorFlow 都不支持的。同学们如果有兴趣也可以在课后了解一下 Google 公司开发的 TensorFlow,并和 PyTorch 对比,在未来进行相关研究或应用时,可以选择自己喜欢的深度学习框架。
与上次实验课利用 Numpy 手动搭建神经网络相比,PyTorch 为我们提供了如下几个高级功能:
- 它具有强大的 GPU 支持的张量计算功能,能够高效实现神经网络中的各种张量运算;
- 它能够自动实现反向传播过程中的求导运算,大大降低用户的工作量;
- 我们常用的神经网络组成模块如卷积层 (convolutional layer)、池化层 (pooling layer)、全连接层 (fully-connected layer)、各种激活函数 (activation functions) 等 PyTorch 都支持,对用户来讲使用非常方便。
目前除了 Facebook 之外,Twitter 和 GMU 等许多机构都采用了 PyTorch。
下面,我们就来学习 PyTorch 的基本用法。
PyTorch官方教程 https://pytorch.org/tutorials/beginner/basics/intro.html
http://deeplizard.com/learn/playlist/PLZbbT5o_s2xrfNyHZsM6ufI0iZENK9xgG
1. 导入 PyTorch
import torch
import torchvision
import numpy as np
查看 PyTorch 版本,是否可以使用GPU,以及 CUDA 的版本。
print(torch.__version__)
print(torch.cuda.is_available())
print(torch.version.cuda)
1.7.1+cu110
True
11.0
2. 张量(Tensor)
PyTorch 中的张量 (tensor) 与数组和矩阵类似,是一种特殊的数据结构。在 PyTorch 中,我们使用 tensor 来表示模型的输入、输出以及模型的参数等。PyTorch 中的 tensor 类似于 Numpy 中的数组类型(ndarrays),不同的是 tensor 运算可以在 GPU 或其他硬件加速器上高效快速的运行,而 Numpy 数组只能在 CPU 上运行。实际上,tensor 和 Numpy 数组通常可以共享相同的底层内存,从而消除类型转换时复制数据的需求。与此同时,tensor 也支持自动求导功能 (我们将在后面详细介绍)。如果你熟悉 Numpy 数组,那也将会对 tensor API 感到非常熟悉。
下图展示了一维张量到六维张量。其中零维张量也称为标量,一维张量称为向量,二维张量就是我们一般说的矩阵。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rLw2f2U5-1642942422381)(C:\Users\张清望\AppData\Roaming\Typora\typora-user-images\image-20220123194000385.png)]
2.1 张量初始化 (Tensor Initialization)
- 初始化一个空张量
# create an empty tensor by calling the constructor of torch.Tensor class
t = torch.Tensor()
print(t)
tensor([])
- 由数据直接创建
# tensors can be created directly from data. The data type is automatically inferred.
data = [1, 2, 3, 4]
t1 = torch.Tensor(data) # constuctor create the object with the default data type (float32)
print(t1)
print(t1.dtype) # print the data type
tensor([1., 2., 3., 4.])
torch.float32
t2 = torch.tensor(data) # the datatype will be int64
print(t2)
print(t2.dtype)
tensor([1, 2, 3, 4])
torch.int64
- 由 Numpy 数组创建 (反之亦可)
# tensors can be created from Numpy arrays (and vice versa)
np_arr = np.array([1,2,3,4])
t = torch.from_numpy(np_arr) # they share the same memory
print(t)
print(t.dtype)
tensor([1, 2, 3, 4], dtype=torch.int32)
torch.int32
由 Numpy 数组创建的 tensor 与原数组共享存储空间,因此原数组一经变化,tensor 也会发生相应的改变。
np_arr[0] = 999
print(t)
tensor([999, 2, 3, 4], dtype=torch.int32)
- 由另一个 Pytorch tensor 创建
# the new tensor retains the properties (shape, datatype) of the argument tensor, unless explicitly overridden
t_ones = torch.ones_like(t2) # retains the properties of t_data
print(f"Ones Tensor: \n {t_ones} \n")
t_rand = torch.rand_like(t2, dtype=torch.float) # overrides the datatype of t_data
print(f"Random Tensor: \n {t_rand} \n")
Ones Tensor:
tensor([1, 1, 1, 1])
Random Tensor:
tensor([0.9006, 0.3040, 0.1702, 0.0847])
- 其他方式创建
# create tensors with random or constant values
shape = (2,3,)
rand_tensor = torch.rand(shape) # 构建一个随机初始化的张量
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
Random Tensor:
tensor([[0.6786, 0.6451, 0.3554],
[0.7361, 0.1580, 0.8583]])
Ones Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
Zeros Tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])
2.2 张量的属性 (Attributes)
张量的属性描述了他们的形状 (shape)、数据类型 (datatype)、和存储设备。
print(f"Shape of rand_tensor: {rand_tensor.shape}")
print(f"Datatype of rand_tensor: {rand_tensor.dtype}") # float32
print(f"Device tensor is stored on: {rand_tensor.device}") # cpu
Shape of rand_tensor: torch.Size([2, 3])
Datatype of rand_tensor: torch.float32
Device tensor is stored on: cpu
print(f"Shape of t_ones: {t_ones.shape}")
print(f"Datatype of t_ones: {t_ones.dtype}") # int64
print(f"Device tensor is stored on: {t_ones.device}") # cpu
Shape of t_ones: torch.Size([4])
Datatype of t_ones: torch.int64
Device tensor is stored on: cpu
存储设备默认是 CPU,假如存在 GPU,可以使用如下命令将数据转移至 GPU
t_ones_gpu = t_ones.cuda() # move the tensor to gpu
print(t_ones_gpu) # you will see: tensor([1, 1, 1, 1], device='cuda:0')
tensor([1, 1, 1, 1], device='cuda:0')
请注意,tensor 之间的运算要求参与运算的 tensor 在相同的 “device”上
例如:
print("rand_tensor: \n", rand_tensor)
print("ones_tensor: \n", ones_tensor)
print("rand_tensor + ones_tensor: \n", rand_tensor+ones_tensor)
rand_tensor:
tensor([[0.9118, 0.0866, 0.5149],
[0.3336, 0.4063, 0.0159]])
ones_tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
rand_tensor + ones_tensor:
tensor([[1.9118, 1.0866, 1.5149],
[1.3336, 1.4063, 1.0159]])
而以下代码运行时会报错,因为参与运算的 tensor 在不同的 “device” 上
print("t_ones: \n", t_ones_gpu)
print("t_rand: \n", t_rand)
print("t_ones + t_rand: \n", t_ones_gpu+t_rand)
t_ones:
tensor([1, 1, 1, 1], device='cuda:0')
t_rand:
tensor([0.7465, 0.8281, 0.3466, 0.7537])
RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!
2.3 高维张量
刚刚我们主要学习了一维张量,下面我们来看看高维张量。
dd = [[1,2,3],
[4,5,6],
[7,8,9]] # 2D python list
t = torch.tensor(dd) # create a rank-2 tensor (dd and t have different memory allocation)
print("t.shape: \t\t", t.shape)
print("type(t): \t\t", type(t))
print("t.reshape(1, 9): \t", t.reshape(1, 9)) # reshape 操作可以变换张量的形状,我们会在下一节详细介绍
print("t.reshape(1, 9).shape: \t", t.reshape(1, 9).shape)
print("t.shape: \t\t", t.shape) # 请注意,reshape 操作并不会更改原本的张量 t 的形状
t.shape: torch.Size([3, 3])
type(t): <class 'torch.Tensor'>
t.reshape(1, 9): tensor([[1, 2, 3, 4, 5, 6, 7, 8, 9]])
t.reshape(1, 9).shape: torch.Size([1, 9])
t.shape: torch.Size([3, 3])
dd = [[[1,2,3],[4,5,6],[7,8,9]],
[[10,11,12],[13,14,15],[16,17,18]],
[[19,20,21],[22,23,24],[25,26,27]]] # 3D python list
t = torch.tensor(dd) # create a rank-3 tensor
print("t.shape: \t\t", t.shape)
print("type(t): \t\t", type(t))
print("t.reshape(1, 9): \n", t.reshape(3, 9, 1)) # reshape 操作可以变换张量的形状,我们会在下一节详细介绍
print("t.reshape(1, 9).shape: \t", t.reshape(3, 9, 1).shape)
print("t.shape: \t\t", t.shape) # 请注意,reshape 操作并不会更改原本的张量 t 的形状
t.shape: torch.Size([3, 3, 3])
type(t): <class 'torch.Tensor'>
t.reshape(1, 9):
tensor([[[ 1],
[ 2],
[ 3],
[ 4],
[ 5],
[ 6],
[ 7],
[ 8],
[ 9]],
[[10],
[11],
[12],
[13],
[14],
[15],
[16],
[17],
[18]],
[[19],
[20],
[21],
[22],
[23],
[24],
[25],
[26],
[27]]])
t.reshape(1, 9).shape: torch.Size([3, 9, 1])
t.shape: torch.Size([3, 3, 3])
3. 张量的基本运算
本节中,我们将学习如下几种张量基本运算:
3.1 reshape 操作
3.2 元素间操作 (element-wise operations)
3.3 访问操作 (access operations)
3.4 归约操作 (reduction operations)
3.1 Reshape 操作
Reshape 操作是 PyTorch 中最重要的张量操作之一,它可以用来变换张量的形状。我们会在之后构建神经网络、编写前向传播代码(forward() 函数)时用到它。
t = torch.tensor([
[1,1,1,1],
[2,2,2,2],
[3,3,3,3]
], dtype=torch.float32)
print("t.size(): ", t.size()) # same as t.shape
print("t.shape: ", t.shape)
print("rank of tensor: ", len(t.shape)) # rank of the tensor (维度)
print("number of elements: ", t.numel()) # get the total # of elements in the tensor
t.size(): torch.Size([3, 4])
t.shape: torch.Size([3, 4])
rank of tensor: 2
number of elements: 12
print("reshape(1,12): \n\t", t.reshape(1,12),"\n")
print("reshape(2,6): \n\t", t.reshape(2,6),"\n")
print("reshape(12,1): \n\t", t.reshape(12,1),"\n")
print("reshape(2,2,3): \n\t", t.reshape(2,2,3),"\n") # we can change the rank from 2 to 3 using reshape
print("t.shape (should not change): ", t.shape) # note that the shape of t is not changed!
reshape(1,12):
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
reshape(2,6):
tensor([[1., 1., 1., 1., 2., 2.],
[2., 2., 3., 3., 3., 3.]])
reshape(12,1):
tensor([[1.],
[1.],
[1.],
[1.],
[2.],
[2.],
[2.],
[2.],
[3.],
[3.],
[3.],
[3.]])
reshape(2,2,3):
tensor([[[1., 1., 1.],
[1., 2., 2.]],
[[2., 2., 3.],
[3., 3., 3.]]])
t.shape (should not change): torch.Size([3, 4])
与 reshape 相关的一些其他操作:
- squeeze 与 unsqueeze
squeeze: 消除张量中长度为 1 的维度
unsqueeze: 在张量中插入一个长度为 1 的维度
# squeeze: remove the axes which has a length of 1
print(t.reshape(12,1).squeeze())
print(t.reshape(12,1).squeeze().shape)
print(t.reshape(1,12).squeeze())
print(t.reshape(1,12).squeeze().shape)
print(t) # 注意原本的张量 t 不会被更改
tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])
torch.Size([12])
tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])
torch.Size([12])
tensor([[1., 1., 1., 1.],
[2., 2., 2., 2.],
[3., 3., 3., 3.]])
# unsqueeze: add a dimension with the length of 1
print(t.reshape(12).unsqueeze(dim=0))
print(t.reshape(12).unsqueeze(dim=0).shape)
print("")
print(t.reshape(12).unsqueeze(dim=1))
print(t.reshape(12).unsqueeze(dim=1).shape)
print("")
print(t) # 注意原本的张量 t 不会被更改
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
torch.Size([1, 12])
tensor([[1.],
[1.],
[1.],
[1.],
[2.],
[2.],
[2.],
[2.],
[3.],
[3.],
[3.],
[3.]])
torch.Size([12, 1])
tensor([[1., 1., 1., 1.],
[2., 2., 2., 2.],
[3., 3., 3., 3.]])
- concatenation (并置)
将两个 tensor 沿某个维度拼接起来
t1 = torch.tensor([[1,2,3],
[4,5,6,],
[7,8,9]], dtype=torch.int32)
t2 = torch.tensor([[0,0,0],
[0,0,0],
[0,0,0]], dtype=torch.int32)
print(torch.cat((t1,t2), dim=0))
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]], dtype=torch.int32)
print(torch.cat((t1,t2), dim=1))
tensor([[1, 2, 3, 0, 0, 0],
[4, 5, 6, 0, 0, 0],
[7, 8, 9, 0, 0, 0]], dtype=torch.int32)
t3 = torch.tensor([[0,0],
[0,0],
[0,0]], dtype=torch.int32)
torch.cat((t1,t3), dim=1)
tensor([[1, 2, 3, 0, 0],
[4, 5, 6, 0, 0],
[7, 8, 9, 0, 0]], dtype=torch.int32)
# 注意拼接时,tensor 的维度要对应的上。由于 t1 与 t3 在 dim1 上的长度不同,因此以下代码运行时会报错:
print(t1.shape[1])
print(t3.shape[1])
print(torch.cat((t1,t3), dim=0))
3
2
RuntimeError: Sizes of tensors must match except in dimension 0. Got 3 and 2 in dimension 1 (The offending index is 1)
3.2 元素间操作 (Element-Wise Operations)
- 与标量的加减乘除操作
t1 = torch.tensor([[1,2],
[3,4]], dtype=torch.float32)
print("t1 + 2: \n", t1 + 2)
print("t1.add(2): \n", t1.add(2), "\n")
print("t1 - 2: \n", t1 - 2)
print("t1.sub(2): \n", t1.sub(2), "\n")
print("t1 * 2: \n", t1 * 2)
print("t1.mul(2): \n", t1.mul(2), "\n")
print("t1 / 2: \n", t1 / 2)
print("t1.div(2): \n", t1.div(2), "\n")
t1 + 2:
tensor([[3., 4.],
[5., 6.]])
t1.add(2):
tensor([[3., 4.],
[5., 6.]])
t1 - 2:
tensor([[-1., 0.],
[ 1., 2.]])
t1.sub(2):
tensor([[-1., 0.],
[ 1., 2.]])
t1 * 2:
tensor([[2., 4.],
[6., 8.]])
t1.mul(2):
tensor([[2., 4.],
[6., 8.]])
t1 / 2:
tensor([[0.5000, 1.0000],
[1.5000, 2.0000]])
t1.div(2):
tensor([[0.5000, 1.0000],
[1.5000, 2.0000]])
- tensor 间的加减乘除操作 (当两个 tensor 具有相同的形状)
t1 = torch.tensor([[1,2],
[3,4]], dtype=torch.float32)
t2 = torch.tensor([[1,3],
[-9,8]], dtype=torch.float32)
# element-wise computation between tensors:方式一
print("t1 + t2: \n", t1 + t2)
print("t1 - t2: \n", t1 - t2)
print("t1 * t2: \n", t1 * t2)
print("t1 / t2: \n", t1 / t2)
t1 + t2:
tensor([[ 2., 5.],
[-6., 12.]])
t1 - t2:
tensor([[ 0., -1.],
[12., -4.]])
t1 * t2:
tensor([[ 1., 6.],
[-27., 32.]])
t1 / t2:
tensor([[ 1.0000, 0.6667],
[-0.3333, 0.5000]])
# 方式二
print("t1.add(t2): \n", t1.add(t2))
print("t1.sub(t2): \n", t1.sub(t2))
print("t1.mul(t2): \n", t1.mul(t2))
print("t1.div(t2): \n", t1.div(t2))
t1.add(t2):
tensor([[ 2., 5.],
[-6., 12.]])
t1.sub(t2):
tensor([[ 0., -1.],
[12., -4.]])
t1.mul(t2):
tensor([[ 1., 6.],
[-27., 32.]])
t1.div(t2):
tensor([[ 1.0000, 0.6667],
[-0.3333, 0.5000]])
- 比较操作
# 方式1
print("t1 == t2: \n", t1 == t2, "\n")
print("t1 >= t2: \n", t1 >= t2, "\n")
print("t1 <= t2: \n", t1 <= t2, "\n")
print("t1 > t2: \n", t1 > t2, "\n")
print("t1 < t2: \n", t1 < t2, "\n")
t1 == t2:
tensor([[ True, False],
[False, False]])
t1 >= t2:
tensor([[ True, False],
[ True, False]])
t1 <= t2:
tensor([[ True, True],
[False, True]])
t1 > t2:
tensor([[False, False],
[ True, False]])
t1 < t2:
tensor([[False, True],
[False, True]])
# 方式二
print("t1.eq(t2): \n", t1.eq(t2), "\n")
print("t1.ge(t2): \n", t1.ge(t2), "\n")
print("t1.le(t2): \n", t1.le(t2), "\n")
print("t1.gt(t2): \n", t1.gt(t2), "\n")
print("t1.lt(t2: \n", t1.lt(t2), "\n")
t1.eq(t2):
tensor([[ True, False],
[False, False]])
t1.ge(t2):
tensor([[ True, False],
[ True, False]])
t1.le(t2):
tensor([[ True, True],
[False, True]])
t1.gt(t2):
tensor([[False, False],
[ True, False]])
t1.lt(t2):
tensor([[False, True],
[False, True]])
- 关于广播 (broadcast)
哪些情况下可以广播?
从 dim0 开始评估,如果该维度上两个 tensor 的长度是一样的,或其中一个 tensor 在该维度上长度为 1,则我们认为这两个 tensor 在该维度上是兼容的,可以通过广播进行元素间运算。若有任何一个维度上两个 tensor 不兼容,则不能进行元素间运算。
t3 = torch.tensor([[1,2,3]])
t4 = torch.tensor([[4],[5],[6]])
print('t3 shape: ', t3.shape)
print('t4 shape: ', t4.shape)
print(t3 + t4)
t3 shape: torch.Size([1, 3])
t4 shape: torch.Size([3, 1])
tensor([[5, 6, 7],
[6, 7, 8],
[7, 8, 9]])
# 思考:上面的单元发生了什么?
t3_broadcast = torch.cat((t3,t3,t3),dim=0)
print("t3_broadcast: \n", t3_broadcast)
t4_broadcast = torch.cat((t4,t4,t4),dim=1)
print("t4_broadcast: \n", t4_broadcast)
print(t3_broadcast+t4_broadcast)
t3_broadcast:
tensor([[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])
t4_broadcast:
tensor([[4, 4, 4],
[5, 5, 5],
[6, 6, 6]])
tensor([[5, 6, 7],
[6, 7, 8],
[7, 8, 9]])
- 其他 element-wise 操作
print(t1.abs())
print(t1.sqrt()) # square root
print(t1.neg())
print(t1.neg().abs())
tensor([[1., 2.],
[3., 4.]])
tensor([[1.0000, 1.4142],
[1.7321, 2.0000]])
tensor([[-1., -2.],
[-3., -4.]])
tensor([[1., 2.],
[3., 4.]])
3.3 访问操作 (Access Operations)
# 方式一
t = torch.tensor([
[0,1,0],
[2,0,2],
[0,3,0]
], dtype=torch.float32)
for item in t: # tensor is iterable
print(item) # rank-1 tensor
for item in t[0]:
print(item) # rank-0 tensor
tensor([0., 1., 0.])
tensor([2., 0., 2.])
tensor([0., 3., 0.])
tensor(0.)
tensor(1.)
tensor(0.)
# 方式二
print(t[0])
print(t[1])
print(t[2])
print(t[0][0] + t[1][0] + t[2][0]) # data type: tensor!
tensor([0., 1., 0.])
tensor([2., 0., 2.])
tensor([0., 3., 0.])
tensor(2.)
print((t[0][0] + t[1][0] + t[2][0]).item()) # item(): 将元素提取出来(这是一个归约操作,我们会在下一小节细讲归约操作)
2.0
3.4 归约操作 (reduction operations)
print("t.sum(): \t", t.sum()) # 对全部元素求和,并返回一个零维 tensor
print("t.prod(): \t", t.prod()) # 对全部元素求积,并返回一个零维 tensor
print("t.mean(): \t", t.mean()) # 对全部元素求均值,并返回一个零维 tensor
print("t.std(): \t", t.std()) # 对全部元素求标准方差,并返回一个零维 tensor
t.sum(): tensor(8.)
t.prod(): tensor(0.)
t.mean(): tensor(0.8889)
t.std(): tensor(1.1667)
print(t.sum().item()) # 将元素从零维 tensor 中提取出来
8.0
print(t.numel()) # 计数元素个数,并返回一个标量
print(t.sum().numel())
9
1
归约操作可单独对某个维度进行
t = torch.tensor([
[1,1,1,1],
[2,2,2,2],
[3,3,3,3]
], dtype=torch.float32)
print("t.sum(dim=0): \t", t.sum(dim=0))
print("t.sum(dim=1): \t", t.sum(dim=1), "\n")
print("t.prod(dim=0): \t", t.prod(dim=0))
print("t.prod(dim=1): \t", t.prod(dim=1), "\n")
print("t.mean(dim=0): \t", t.mean(dim=0))
print("t.mean(dim=1): \t", t.mean(dim=1), "\n")
t.sum(dim=0): tensor([6., 6., 6., 6.])
t.sum(dim=1): tensor([ 4., 8., 12.])
t.prod(dim=0): tensor([6., 6., 6., 6.])
t.prod(dim=1): tensor([ 1., 16., 81.])
t.mean(dim=0): tensor([2., 2., 2., 2.])
t.mean(dim=1): tensor([1., 2., 3.])
等一下我们在用 PyTorch 搭建神经网络时,需要用到两个特殊的规约操作:max 和 argmax。
其中 max 会返回给我们 tensor 中的最大值,而 argmax 则会返回最大值对应的索引。
# argmax method: tell us the index of the max value within a tensor
t = torch.tensor([[1,0,0,2],
[2,4,1,0],
[5,2,7,9]], dtype=torch.float32)
print(t.max())
print(t.argmax())
print(t.reshape(t.numel())[t.argmax()])
tensor(9.)
tensor(11)
tensor(9.)
max 和 argmax 也可以沿某个维度操作
# dim0
print(t.max(dim=0))
print(t.argmax(dim=0))
torch.return_types.max(
values=tensor([5., 4., 7., 9.]),
indices=tensor([2, 1, 2, 2]))
tensor([2, 1, 2, 2])
# dim1
print(t.max(dim=1))
print(t.argmax(dim=1))
torch.return_types.max(
values=tensor([2., 4., 9.]),
indices=tensor([3, 1, 3]))
tensor([3, 1, 3])
我们还可以直接使用 torch.max 函数,它会返回两个值,分别对应 dimX 上的最大值及其索引。
max_val, index = torch.max(t, dim=1) # return the max value in dim=1 and the corresponding index
print(max_val)
print(index)
tensor([2., 4., 9.])
tensor([3, 1, 3])
4. PyTorch Tensor 与 Python List 和 Aumpy Array 的转换
t1 = torch.tensor([
[1,1,1,1],
[2,2,2,2],
[3,3,3,3]
], dtype=torch.float32) # PyTorch tensor
print(type(t1.tolist())) # transform the tensor to a python list
print(type(t1.numpy())) # transform the tensor to a numpy array
print("")
t2 = torch.from_numpy(t1.numpy()) # transform a numpy array to a tensor (t2 is a tensor)
print(t2)
print("")
t1[0][0] = 999
print(t2) # t1 and t2 share the memory
<class 'list'>
<class 'numpy.ndarray'>
tensor([[1., 1., 1., 1.],
[2., 2., 2., 2.],
[3., 3., 3., 3.]])
tensor([[999., 1., 1., 1.],
[ 2., 2., 2., 2.],
[ 3., 3., 3., 3.]])
5. Tensor 综合练习
假设我们有三张图片,每张图片为RGB格式(三个通道),每个通道有3*3个像素点,具体信息如下:
图片1: R通道:像素点均取1.0 G通道:像素点均取2.0 B通道:像素点均取3.0
图片2: R通道:像素点均取1.02 B通道:像素点均取2.02 B通道:像素点均取3.0*2
图片3: R通道:像素点均取1.03 G通道:像素点均取2.03 B通道:像素点均取3.0*3
现在我们即将使用这三张图片训练一个全连接层:
1) 图片在 PyTorch 中通常用一个四维张量来表示,我们首先来构建这个四维张量。
下面,请分别写出图片1至3对应的 tensor t1,t2,t3(每张图片的维度为3,其中 dim0 对应通道,dim1 对应行,dim2 对应列)。然后,请通过两种不同方式,将三张图片组织在一起,形成一个四维张量 t_batch(其中,dim0 对应图片;dim1 对应通道;dim2 和 dim3 分别对应行和列)。[提示:例如,第一种方式可以采用 torch.unsqueeze + torch.cat 实现,第二种方式可以采用 torch.cat + reshape 实现。]
2) 请将该四维张量变换形状(reshape),并与如下权重矩阵进行运算(该层为全连接层,因此权重为二维张量)。这里我们将要用到 PyTorch 中的矩阵乘法运算 torch.mm(a, b)。
w = torch.tensor([[1,0,1,0,1,0,1,1,0,0,0,1,0,1,0,1,1,1,0,0,1,0,1,0,0,0,1],
[0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,0,0,1,0],
[1,1,0,0,0,1,0,0,1,1,0,0,1,0,1,1,1,0,0,1,0,1,1,0,1,0,1]
], dtype=torch.float32)
3) 请问运算结果是什么形状的张量?每一个维度分别对应什么?请在实验报告中回答这些问题,并展示本段代码和运行结果,进行说明。
################## Please finish the code ###################
# 列出 t1, t2, t3
# t1 = XXX
# t2 = t1*2
# t3 = t1*3
shape=(3,3,3)
t1=torch.ones(shape)
t1[1]*=2
t1[2]*=3
t2=t1*2
t3=t1*3
print(t1)
print(t2)
print(t3)
# 将 t1,t2,t3 组织起来 - 第一种方式:
# ...
# t_batch_1 = XXX
t_batch_1=torch.cat(((t1.unsqueeze(dim=0)),(t2.unsqueeze(dim=0)),(t3.unsqueeze(dim=0))),dim=0)
print(t_batch_1)
# 将 t1,t2,t3 组织起来 - 第二种方式:
# ...
# t_batch_2 = XXX
t_batch_2=torch.cat((t1,t2,t3),dim=0)
t_batch_2=t_batch_2.reshape(3,3,3,3)
# print(t_batch_2)
# 检查以上二者结果是否相同:
# t_batch = (t_batch_1 == t_batch_2)*t_batch_1
# print(t_batch)
t_batch = (t_batch_1 == t_batch_2)*t_batch_1
print(t_batch)
# 将 t_batch 变换形状,并与 w 运算,得到 t_out
# w = torch.tensor([[1,0,1,0,1,0,1,1,0,0,0,1,0,1,0,1,1,1,0,0,1,0,1,0,0,0,1],
# [0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,0,0,1,0],
# [1,1,0,0,0,1,0,0,1,1,0,0,1,0,1,1,1,0,0,1,0,1,1,0,1,0,1],
# [0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1]
# ], dtype=torch.float32)
# t_batch = t_batch.reshape(XXX,XXX)
# t_out = torch.mm(XXX,XXX)
# print(t_out)
w = torch.tensor([[1,0,1,0,1,0,1,1,0,0,0,1,0,1,0,1,1,1,0,0,1,0,1,0,0,0,1],
[0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,0,0,1,0],
[1,1,0,0,0,1,0,0,1,1,0,0,1,0,1,1,1,0,0,1,0,1,1,0,1,0,1],
[0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1]
], dtype=torch.float32)
t_batch = t_batch_1.reshape(3,27)
t_out = torch.mm(t_batch,w.T)
print(t_out)
########################### end ##############################
tensor([[[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]],
[[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]],
[[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]]])
tensor([[[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]],
[[4., 4., 4.],
[4., 4., 4.],
[4., 4., 4.]],
[[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]]])
tensor([[[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]],
[[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]],
[[9., 9., 9.],
[9., 9., 9.],
[9., 9., 9.]]])
tensor([[[[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]],
[[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]],
[[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]]],
[[[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]],
[[4., 4., 4.],
[4., 4., 4.],
[4., 4., 4.]],
[[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]]],
[[[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]],
[[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]],
[[9., 9., 9.],
[9., 9., 9.],
[9., 9., 9.]]]])
tensor([[[[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]],
[[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]],
[[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]]],
[[[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]],
[[4., 4., 4.],
[4., 4., 4.],
[4., 4., 4.]],
[[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]]],
[[[3., 3., 3.],
[3., 3., 3.],
[3., 3., 3.]],
[[6., 6., 6.],
[6., 6., 6.],
[6., 6., 6.]],
[[9., 9., 9.],
[9., 9., 9.],
[9., 9., 9.]]]])
tensor([[24., 29., 29., 23.],
[48., 58., 58., 46.],
[72., 87., 87., 69.]])