1.PyTorch
1.1 什么是PyTorch?
PyTorch是Torch的python版本,是由Facebook开源的神经网络框架,专门针对 GPU 加速的深度神经网络(DNN)编程。Torch 是一个经典的对多维矩阵数据进行操作的张量(Tensor )库,在机器学习和其他数学密集型应用中有广泛应用。与Tensorflow的静态计算图不同,PyTorch的计算图是动态的,可以根据计算需要实时改变计算图。
1.2 PyTorch包含的功能
-
张量计算引擎(tensor computation)
Tensor 计算引擎,类似 numpy 和 matlab,基本对象是Tensor(类比 numpy 中的 ndarray 或 matlab 中的 array)。除提供基于 CPU 的常用操作的实现外,PyTorch还提供了高效的 GPU 实现,这对于深度学习至关重要。
-
自动求导机制(autograd),自动微分(automatic differentiation)
由于深度学习模型日趋复杂,因此,对自动求导的支持对于学习框架变得必不可少。PyTorch采用了动态求导机制。作为对比,Theano,Tensorflow 采用静态自动求导机制。
-
神经网络的高层库(NN)
PyTorch还提供了高层的API实现常用的网络结构,如全连接、卷积、RNN 等。同时,PyTorch还提供了常用的optimizer。
导入库:
# 导入需要的库
import torch
# 虽然它被称为PyTorch,但是代码中使⽤torch⽽不是pytorch
2.张量
2.1 什么是张量?
在数学中:
一个单独的数可以称为标量,一列或者一行数组可以称为向量,一个二维数组称为矩阵,矩阵中的每一个元素都可以被行和列的索引唯一确定,如果数组的维度超过2, 那么我们可以称该数组为张量(Tensor)。
在PyTorch中:
张量属于一种数据结构,它可以是一个标量、一个向量、一个矩阵,甚至是更高维度的数组,所以PyTorch中Tensor和NumPy库中的数组( ndarray )非常相似,在使用时也会经常将PyTorch中的张量和NumPy中的数组相互转化。在深度网络中,基于PyTorch的相关计算和优化都是在Tensor的基础上完成的。
张量的本质是类(成员如下):
- data: 被包装的Tensor
- dtype: 张量的数据类型,如torch.float
- shape: 张量的形状,如(64, 3, 224, 224)
- device: 张量所在设备,GPU/CPU,是加速的关键
- requires_grad: 指示是否需要梯度
- grad: data的梯度
- grad_fn: 创建Tensor的Function,是自动求导的关键
- is_leaf: 指示是否是叶子结点(张量)
2.2 张量的数据类型
不同的Tensor类型之间可以相互转换
Data type | dtype | CPU tensor | GPU tensor |
---|---|---|---|
32-bit 浮点型 | torch.float32 or torch.float | torch.FloatTensor | torch.cuda.FloatTensor |
64-bit 浮点型 | torch.float64 or torch.double | torch.DoubleTensor | torch.cuda.DoubleTensor |
16-bit 半精度浮点型 | torch.float16 or torch.half | torch.HalfTensor | torch.cuda.HalfTensor |
8-bit无符号整型 | torch.uint8 | torch.ByteTensor | torch.cuda.ByteTensor |
8-bit有符号整型 | torch.int8 | torch.CharTensor | torch.cuda.CharTensor |
16-bit有符号整型 | torch.int16 or torch.short | torch.ShortTensor | torch.cuda.ShortTensor |
32-bit有符号整型 | torch.int32 or torch.int | torch.IntTensor | torch.cuda.IntTensor |
64-bit有符号整型 | torch.int64 or torch.long | torch.LongTensor | torch.cuda.LongTensor |
2.3 numpy和Tensor的区别
对比项 | numpy | Tensor |
---|---|---|
相同点 | 可以定义多维数组,进行切片、改变维度、数学运算等 | 可以定义多维数组,进行切片、改变维度、数学运算等 |
不同点 | 1、产生的数组类型为numpy.ndarray; 2、会将ndarray放入CPU中进行运算; 3、导入方式为import numpy as np,后续通过np.array([1,2])建立数组; 4、numpy中没有x.type()的用法,只能使用type(x)。 | 1、产生的数组类型为torch.Tensor; 2、会将tensor放入GPU中进行加速运算(如果有GPU); 3、导入方式为import torch,后续通过torch.tensor([1,2])或torch.Tensor([1,2])建立数组; 4、Tensor中查看数组类型既可以使用type(x),也可以使用x.type()。但是更加推荐采用x.type()。 |
2.4 创建张量
函数 | 功能 |
---|---|
Tensor(*sizes) | 基础构造函数 |
tensor(data,) | 类似np.array的构造函数 |
ones(*sizes) | 全1的Tensor |
zeros(*sizes) | 全0的Tensor |
eye(*sizes) | 对角线为1,其他为0 |
arange(s,e,step) | 从s到e,步长为step |
linspace(s,e,steps) | 从s到e,均匀切分成steps份 |
rand / randn(*sizes) | 均匀/标准分布 |
normal(mean,std) / uniform(from,to) | 正态分布/均匀分布 |
randperm(m) | 随机排列 |
tensor.new_* / torch.*_like | 创建一个相同size大小,用*类型去填充(如tensor.new_ones就是使用全一的数据去填充)的张量,具有相同的torch.dtype和torch.device |
2.4.1 使用tensor()函数创建张量
# 使用torch.tensor()函数生成张量,对于已有的数据(列表,数组等)也可以通过该函数构造张量
A = torch.tensor([[1.0,1.0],[2,2]])
# 获取张量的形状
# 属性
print(A.shape)
# 方法
print(A.size())
# 计算张量中所含元素的个数
A.numel()
# 指定张量的数据类型和是否要计算梯度,注意只有浮点数才可以计算梯度
B = torch.tensor((1,2,3), dtype=torch.float32, requires_grad=True)
2.4.2 使用Tensor()类创建张量
# 使用torch.Tensor()类创建张量,可以指定生成张量的形状
# 创建具有特定大小的张量
print(torch.Tensor(2,3))
# torch.Tensor(1)返回一个大小为1的张量,它是初始化的随机值
print(torch.Tensor(1))
# torch.tensor(1)返回一个固定值1
print(torch.tensor(1))
# 使用预先存在的数据创建张量
A = torch.Tensor([1,2,3,4])
# 同样类似的还有torch.FloatTensor,IntTensor等
B = torch.FloatTensor(2,3)
C = torch.DoubleTensor(2,3)
2.4.3 Tensor与numpy的相互转换
# 张量与NumPy的相互转换
# 利用numpy数组生成张量
import numpy as np
F = np.ones((3,3))
# numpy转tensor
# 使用torch.as_tensor()函数,浅拷贝
Ftensor = torch.as_tensor(F)
# 使用torch.from_numpy()函数,浅拷贝
Ftensor = torch.from_numpy(F)
# 使用torch.tensor()函数,深拷贝
Ftensor = torch.tensor(F)
# tensor转numpy
# 使用张量的.numpy()将张量转化为numpy数组
Ftensor.numpy()
2.4.4 随机数生成张量
# 随机数生成张量
# 设置随机数种子
torch.manual_seed(123)
# 通过指定均值和标准差生成随机数
A = torch.normal(mean=0.0, std=torch.arange(1,5.0))
# 可以分别指定每个随机数服从的均值
B = torch.normal(mean = torch.arange(1,5.0), std=torch.arange(1,5.0))
# 在区间[0,1)上生成服从均匀分布的张量
print(torch.rand(3,4))
# 生成与一个张量维度相同的随机数张量
C = torch.ones(2,3)
D = torch.rand_like(C)
# 生成服从标准正态分布的随机数
print(torch.randn(3,3))
print(torch.randn_like(C))
# 将0~10(不包括10)之间的整数随机排序
torch.randperm(10)
2.4.5 批量生成张量
# 使用torch.arange()生成张量
# 在torch.arange()中,参数start指定开始,参数end指定结束,参数step则指定步长
torch.arange(start=0, end = 10, step=2)
# 在范围内生成固定数量的等间隔张量,steps表示分割的点数,默认为100
torch.linspace(start=1, end=10, steps=5)
# 生成以对数间隔的点
torch.logspace(start=0.1, end=1.0, steps=5)
2.4.6生成特定的张量
# 生成特定的张量
# 全0张量
torch.zeros(3,3)
# 全1张量
torch.ones(3,3)
# 单位张量
torch.eye(3)
# 空张量
torch.empty(3,3)
# 使用0.25作为填充的张量
torch.full((3,3),fill_value = 0.25)
# 创建一个与B相同大小和类型的全 1 张量
print(torch.ones_like(B))
# 使用torch.zeros_like()函数生成与B维度相同的全0张量
print(torch.zeros_like(B))
# 使用torch.rand_like()函数生成与B维度相同的随机张量
print(torch.rand_like(B))
2.5 Tensor与图像之间的转换
图像转张量
import torch
import cv2
import numpy as np
from torchvision import transforms
def test():
# print(torch.__version__)
# print(torch.cuda.is_available())
im = cv2.imread("./qi_e.png")
# 对图像数据做预处理
transform = transforms.Compose([
transforms.ToPILImage(), # 将NumPy数组转换为PIL图像
transforms.Resize((224, 224)), # 调整图像大小
transforms.ToTensor(), # 转换为张量
])
img = transform(im)
img = img.cuda()
print(img)
print(img.device)
if __name__ == "__main__":
test()
张量转图像
# 将张量转换为 NumPy 数组
numpy_image = image_tensor.numpy().transpose(1, 2, 0) # (H, W, C)
# 将图像转换回 BGR 格式(OpenCV 使用 BGR)
bgr_image = cv2.cvtColor(numpy_image, cv2.COLOR_RGB2BGR)
2.6 Tensor的索引
PyTorch的索引与NumPy数组的索引类型,可以按位置索引,可以切片等
A = torch.randn(3, 4)
print("查看第1行结果:", A[0])
print("查看第2列结果:", A[:,1])
print("查看第2行最后两个元素:", A[1, -2:])
2.7 Tensor的基本运算
A = torch.arange(6.0).reshape(2,3)
B = torch.linspace(10,20,steps=6).reshape(2,3)
# print("A:",A)
# print("B:",B)
# 矩阵相乘,×乘,行乘以列
print(A @ B)
print(A.matmul(B))
AB = torch.matmul(A, B)
print(AB)
# 矩阵逐元素相乘,点乘
print(A * B)
# 逐元素相除
print(A / B)
# 逐元素整除
print(B//A)
# 逐元素相加
print(A + B)
# 逐元素相减
print(A - B)
# 张量的幂
print(torch.pow(A, 3))
print(A ** 3)
# 张量的指数
print(torch.exp(A))
# 张量的对数
print(torch.log(A))
# 张量的平方根
print(torch.sqrt(A))
print(A**0.5)