通过上一篇建模流程的介绍,我们知道,数据准备是整个建模过程的开端,数据的质量和准备程度直接影响模型的训练效果和预测精度,有经验的数据工程师都知道,数据准备阶段工作是否完善,决定了我们整个建模性能的天花板,后续的模型搭建与优化只是去不断逼近这个天花板的过程。所以,我们这个PyTorch教程,就从如何使用PyTorch进行数据准备开始。

数据准备阶段包括数据的读取、预处理、划分和转换等步骤。在后续章节中,我们将一一介绍这些过程。

在本节内容中,我们将对PyTorch中的核心数据结构——Tensor进行深入介绍。不要疑惑,之所以不直接进入正题去介绍如何使用PyTorch准备数据,因为Tensor太重要了,是整个PyTorch的基石,事实上,无论是神经网络还是其他机器学习算法,无论是CV(计算机视觉),还是NLP(自然语言处理),都依赖于对数据的有效表示和操作,而Tensor就是为此而生,Tensor正是PyTorch中最终用于承载数据的数据结构。详细来说,Tensor在Pytorch 中的重要性主要体现在以下几个方面:

  1. 通用性:Tensor 可以表示多种类型的数据,从标量、向量、矩阵到高维张量。这使得我们能够在一个统一的框架下处理各种类型的数据。

  2. 高性能:Pytorch 提供了大量针对 Tensor 的操作,例如加法、乘法、变换等。这些操作已经高度优化,可以在 CPU 或 GPU 上快速执行。通过使用 Tensor,我们可以充分利用现代硬件的计算能力,提高模型训练和推理的速度。

  3. 自动求导:Tensor 支持自动求导功能,这意味着在构建神经网络时,我们不需要手动计算梯度。Pytorch 会自动跟踪 Tensor 的计算历史,并在需要时计算梯度。这大大简化了模型训练过程,使我们能够更快速地迭代和优化模型。

  4. 易用性:Pytorch 提供了丰富的 API,使得处理 Tensor 变得简单直观。此外,Pytorch 还提供了与 NumPy 无缝集成的能力,使得数据处理和转换更加便捷。

import torch
import numpy as np
1 Tensor的创建和初始化

在PyTorch中,Tensor是一个多维数组,可以用于存储和操作数字数据。Tensor可以表示标量、向量、矩阵和更高维度的数组。在本章节中,我们将介绍如何创建和初始化Tensor。

pyTorch中提供了许许多多创建和初始化Tensor的方法,首先说明,我们真不需要全部去掌握,绝大多数都不会使用到,亦或者不同方法间存在功能相似的情况,例如pytorch中还提供有X.new_系列方法,用于创建与X相似的Tensor,但我们完全可以使用torch._like系列方法进行替代,又何必花精力去多记那么多方法呢?毕竟,人生苦短……通过对PyTorch官方文档中Tensor部分内容的梳理总结,我将创建和初始化Tensor的主要方法(不常用的不在总结范围内)总结为5大类:

  1. 根据数据源创建Tensor

  2. 根据恒定值或随机值创建Tensor

  3. 根据数值范围和间隔创建Tensor

  4. 根据其他Tensor的属性创建Tensor

  5. 根据特殊矩阵或张量结构创建Tensor

同时,也总结了大多数的方法都有一些共性的参数,下面是一些常见的参数:

  1. dtype: 指定张量的数据类型,如 torch.float32、torch.int64 等。如果不指定,则默认为 torch.float32。

  2. device: 指定张量所在的设备,如 'cpu'、'cuda:0' 等。如果不指定,则默认为 CPU。

  3. requires_grad: 指定张量是否需要梯度。如果不指定,则默认为 False。

  4. shape: 指定张量的形状,如 (3, 4) 表示一个 3×43×4 的二维张量。不同的方法可能对形状的指定有不同的方式,如使用整数、元组、列表等。

  5. initializer: 一些方法允许指定张量的初始值,如均匀分布、正态分布等。不同的方法可能对初始值的指定有不同的方式,如使用随机种子、指定均值和标准差等。

需要注意的是,并不是所有方法都支持所有的参数,不同的方法可能只支持其中的一部分参数。此外,一些方法可能还有一些额外的参数,如卷积神经网络的卷积核大小、步长等。列出常见参数,只是方便我们归纳学习,掌握规律。

接下来,我们通过示例,逐个介绍这几类方法并通过代码示例展示参数。

1.1 根据数据源创建Tensor

这类方法是将包括Python列表、元组、NumPy数据在内的其他类型的数据转化为Tensor,这类方法中常有方法有torch.tensor(),torch.from_numpy(),torch.as_tensor()等。这些方法的区别在于它们所接受的参数类型和返回的张量是否共享内存。

1.1.1 torch.tensor()

这个方法可以接受各种 Python 对象(如 Python 列表、元组、NumPy 数组等)来创建张量,返回一个新的张量。可以使用 dtype 和 device 参数指定张量的数据类型和存储设备。注意,torch.tensor()创建Tensor时会在内存中复制一份原始数据,即原始数据不会与生成的Tensor共享内存。主要参数有:

  • data:输入数据,可以是列表、元组、numpy.ndarray 等。

  • dtype(可选):指定输出张量的数据类型。

  • device(可选):指定输出张量所在的设备。

  • requires_grad(可选):指定输出张量是否需要梯度。

  • pin_memory(可选):如果设置为 True,则将张量存储到固定内存中,可以加速数据传输。

  • memory_format(可选):指定输出张量的内存格式。

import torch
import numpy as np

# 使用 Python 列表创建张量
t1 = torch.tensor([1, 2, 3])
print(t1)

out:
tensor([1, 2, 3])

# 使用 NumPy 数组创建张量
arr = np.array([[1, 2], [3, 4]])
t2 = torch.tensor(arr, dtype=torch.float64)
print(t2)

out:
tensor([[1., 2.],
[3., 4.]], dtype=torch.float64)

# 指定 dtype 和 device 创建张量
t3 = torch.tensor([1.0, 2.0, 3.0], device='cpu')
print(t3)

out:
tensor([1., 2., 3.])

# 指定输出张量所在的设备
torch.tensor([1, 2, 3], device='cuda:0')

out:
tensor([1, 2, 3], device='cuda:0')

# 指定输出张量是否需要梯度
torch.tensor([1, 2, 3], dtype=torch.float32, requires_grad=True)

out:
tensor([1., 2., 3.], requires_grad=True)

# 存储到固定内存中,可以加速数据传输
torch.tensor([1, 2, 3], pin_memory=True)

out:
tensor([1, 2, 3])
1.1.2 torch.from_numpy()

这个方法接受一个 NumPy 数组来创建张量,返回一个新的张量。它和 torch.tensor() 的区别在于,它会共享内存,即如果原始的 NumPy 数组发生了变化,新的张量也会随之改变。torch.from_numpy() 方法只有一个必选参数 numpy_array,用于指定要转换为 tensor 的 numpy 数组。

除了 numpy_array 参数之外,torch.from_numpy() 方法还有以下可选参数:

  • dtype:指定输出张量的数据类型。

  • device:指定输出张量所在的设备。

  • requires_grad:指定输出张量是否需要梯度。

其中,dtype 参数的默认值是从 numpy_array 推断出来的,而 device 和 requires_grad 的默认值与当前默认设备和梯度设置有关。

# 创建 NumPy 数组
arr = np.array([1, 2, 3])

# 使用 from_numpy() 方法创建张量,并修改原始的 NumPy 数组
t = torch.from_numpy(arr)
arr[0] = 0

# 查看张量和原始数组的值
print('Tensor:', t)
print('Numpy:', arr)

Tensor: tensor([0, 2, 3])
Numpy: [0 2 3]

可以看到,修改原始数组的值后,张量的值也随之改变。

1.1.3 torch.as_tensor()

这个方法接受各种 Python 对象(如 Python 列表、元组、NumPy 数组等)来创建张量,返回一个新的张量。它和 torch.tensor() 的区别在于,如果输入的对象是一个 NumPy 数组或 PyTorch 张量,它会尝试共享内存,即返回的张量会和输入的对象共享同一块内存。但是,如果输入的对象是Python 列表、元组,则不会共享内存,这点我觉得挺坑的。

torch.as_tensor() 方法只有一个必选参数 data,用于指定要转换为 tensor 的数据,可以是 numpy 数组、Python 列表、元组或标量等。

除了 data 参数之外,torch.as_tensor() 方法还有以下可选参数:

  • dtype:指定输出张量的数据类型。

  • device:指定输出张量所在的设备。

  • requires_grad:指定输出张量是否需要梯度。

其中,dtype 和 device 参数的默认值与当前默认设备和数据类型设置有关,而 requires_grad 的默认值为 False。

# 创建 list
lst = [1, 2, 3]

# 使用 as_tensor() 方法创建张量,并修改原始的 NumPy 数组
t = torch.as_tensor(lst)
lst[0] = 0

# 查看张量和原始list的值

print('Tensor:', t)
print('list:', lst)

out:
Tensor: tensor([1, 2, 3])
list: [0, 2, 3]
1.2 根据恒定值或随机值创建Tensor