PyTorch 张量
该页面由 Jupyter Notebook 生成,原文件于 Github
import torch
torch.__version__
'2.5.1+cu124'
张量用于表示数据,是机器学习的基本组成部分。
- 图片可以是三维张量,如
[height, width, channel]
,如经典的 lena 图片用张量表示:
import numpy as np
from PIL import Image
# 使用 pillow 打开图片, 转换为 numpy 矩阵, 再转换为 torch 张量
img = torch.from_numpy(np.array(Image.open("imgs/lena.jpg")))
img.shape
torch.Size([500, 500, 3])
Tensors说明文档:https://pytorch.org/docs/stable/tensors.html
- Scalar,标量是一个单独的数字,用张量的术语来说是一个零维张量。
scalar = torch.tensor(3.0)
# 维度同样可以通过 tensor.dim() 获取
print(f"scalar为{scalar}, 维度为{scalar.ndim}, 常量通过item方法获取{scalar.item()}数字")
scalar为3.0, 维度为0, 常量通过item方法获取3.0数字
- Vector,向量是一个一维张量,类似于数组。
vector = torch.tensor([1.0, 2.0, 3.0])
print(f"vector为{vector}, 维度为{vector.ndim}, 通过shape属性获取形状{vector.shape}")
vector为tensor([1., 2., 3.]), 维度为1, 通过shape属性获取形状torch.Size([3])
- Matrix,矩阵是一个二维张量。
matrix = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
print(f"{matrix}, \n维度为{matrix.ndim}, 通过shape属性获取形状{matrix.shape}")
tensor([[1., 2., 3.],
[4., 5., 6.]]),
维度为2, 通过shape属性获取形状torch.Size([2, 3])
总结:
结构 | 表示 | 维度 |
---|---|---|
scalar | 一个数字 | 0 |
vector | 一组数字 | 1 |
matrix | 一个矩阵 | 2 |
tensor | 若干维度 | 0维表示scalar,每一维表示一个vector |
实际上在机器学习中很少会手动创建张量,更多是随机生成。
# 创建指定大小的随机张量
random_tensor = torch.rand(size=(3, 4))
random_tensor, random_tensor.dtype
(tensor([[0.3467, 0.2830, 0.2079, 0.1919], [0.8412, 0.7202, 0.9083, 0.1247], [0.0241, 0.2289, 0.6623, 0.6389]]), torch.float32)
zeros = torch.zeros(size=(3, 4))
ones = torch.ones(size=(3, 4))
zeros, zeros.dtype
ones, ones.dtype
(tensor([[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]), torch.float32)
# 创建一个从0到9的张量的两种方法
# zero_to_ten1 = torch.range(0, 10) # 将弃用
zero_to_ten2 = torch.arange(start=0, end=10, step=1)
# zero_to_ten1, zero_to_ten1.dtype
zero_to_ten2, zero_to_ten2.dtype
(tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), torch.int64)
#创建一个形状一样的向量
same_shape = torch.zeros_like(input=zero_to_ten2)
same_shape, same_shape.dtype
(tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), torch.int64)
Tensor 的 DataTypes:https://pytorch.org/docs/stable/tensors.html#data-types
有些数据类型是特定于CPU,而有些更适合GPU。同时确保精度问题,可以选用不同精度的浮点数类型。
float32_tensor = torch.tensor([3.0, 6.0, 9.0],
dtype=None, # 默认为None,即torch。Float32或传递的任何数据类型
device=None, # 默认为None,使用默认的张量类型
requires_grad=False) # 如果为True,则记录对张量执行的操作
float32_tensor.shape, float32_tensor.dtype, float32_tensor.device
(torch.Size([3]), torch.float32, device(type='cpu'))
可以修改张量的数据类型:
float64_tensor = float32_tensor.type(torch.float64)
float64_tensor.dtype
torch.float64
在进行带张量的操作时,除了张量的 Shape 要匹配之外,还需要注意张量的 dtype 和 device。
tensor.shape
:获取 Shape。tensor.dtype
:获取 dtype。tensor.device
:获取 device。
张量的加减乘操作如下:
test_tensor = torch.tensor([1, 2, 3])
test_tensor + 10, test_tensor * 10, test_tensor - 1, test_tensor # 在不赋值的时候是不变的
(tensor([11, 12, 13]), tensor([10, 20, 30]), tensor([0, 1, 2]), tensor([1, 2, 3]))
也可以通过函数实现:
torch.add(test_tensor, 10), torch.mul(test_tensor, 10), torch.sub(test_tensor, 1)
(tensor([11, 12, 13]), tensor([10, 20, 30]), tensor([0, 1, 2]))
注意,矩阵乘法遵循其规则,与形状相关。
$$ M_{m\times n} = M_{m\times k} @ M_{k\times n} $$@
在 Python 中是矩阵乘法
tensor = torch.tensor([1, 2, 3])
tensor * tensor, tensor @ tensor, torch.matmul(tensor, tensor)
# torch.matmul是矩阵乘法,且比 @ 操作更快
(tensor([1, 4, 9]), tensor(14), tensor(14))
- $[1, 2, 3] * [1, 2, 3] = [1 * 1, 2 * 2, 3 * 3] = [1, 4, 9]$
- $[1, 2, 3] @ [1, 2, 3] = 1 * 1 + 2 * 2 + 3 * 3 = 14$
torch.mm()
是 torch.matmul()
的缩写。另外提供一些操作进行矩阵变换:
torch.transpose(input, dim0, dim1)
,input
是输入矩阵,dim0
和dim1
是要交换的维度。torch.T
:转置矩阵。
x = torch.arange(0, 100, 10)
print(x)
print(f"最小值: {x.min()}")
print(f"最大值: {x.max()}")
# print(f"Mean: {x.mean()}") # 会报错
print(f"均值: {x.type(torch.float32).mean()}") # 没有float数据类型将无法工作
print(f"总和: {x.sum()}")
tensor([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90])
最小值: 0
最大值: 90
均值: 45.0
总和: 450
一些方法,如torch.mean(),要求张量位于torch.float32(最常见)或其他特定数据类型中,否则操作将失败。
print(x)
print(f"Index where max value occurs: {x.argmax()}")
print(f"Index where min value occurs: {x.argmin()}")
tensor([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90])
Index where max value occurs: 9
Index where min value occurs: 0
因为深度学习模型(神经网络)都是关于以某种方式操纵张量的。因为矩阵乘法的规则,如果有形状不匹配,就会遇到错误。这些方法帮助你确保你的张量的正确元素与其他张量的正确元素混合在一起。
方法 | 描述 |
---|---|
torch.reshape(input, shape) 或 torch.Tensor.reshape() |
在兼容的情况下把 input 重塑成 shape 的形状 |
Tensor.view(shape) | 以不同的形状返回原始张量的视图,但与原始张量共享相同的数据 |
torch.stack(tensors, dim=0) | 沿着一个新的维度 dim 连接一系列张量,所有张量必须是相同的大小 |
torch.squeeze(input) | 挤压 input ,删除值为1的所有维度 |
torch.unsqueeze(input, dim) | 在 dim 处添加值为1的维度并返回 |
torch.permute(input, dims) | 返回原始输入的视图,其维度重新排列 |
tensor.reshape()
:
import torch
x = torch.arange(1., 9.)
x_reshaped = x.reshape(1, 8) # 重塑
print(f"x.shape: {x.shape}, x_reshaped.shape: {x_reshaped.shape}")
x.shape: torch.Size([8]), x_reshaped.shape: torch.Size([1, 8])
tensor.view()
:改变视图也会改变原来的张量。
x_viewed = x.view(2, 4) # 重塑
print(f"x.shape: {x.shape}, x_viewed.shape: {x_viewed.shape}")
# 修改 x_viewd, x 同步变化
x_viewed[:, 0] = 5
print(x)
print(x_viewed)
x.shape: torch.Size([8]), x_viewed.shape: torch.Size([2, 4])
tensor([5., 2., 3., 4., 5., 6., 7., 8.])
tensor([[5., 2., 3., 4.],
[5., 6., 7., 8.]])
用该函数改变一个张量的视图实际上只会创建同一个张量的新视图。
如果想要将新张量在自身之上堆叠五次,可以使用 torch.stack()
来实现。
x_stacked = torch.stack([x, x, x, x], dim=0)
x_stacked
tensor([[5., 2., 3., 4., 5., 6., 7., 8.], [5., 2., 3., 4., 5., 6., 7., 8.], [5., 2., 3., 4., 5., 6., 7., 8.], [5., 2., 3., 4., 5., 6., 7., 8.]])
同时可以移除单维度:
print(x_reshaped.shape)
x_squzzed = x_reshaped.squeeze()
print(x_squzzed.shape)
torch.Size([1, 8])
torch.Size([8])
与 torch.squeeze()
相反,可以使用 torch.unsqueeze()
在特定索引处添加一个维度值 1:
print(x_squzzed.shape)
x_unsquzzed = x_squzzed.unsqueeze(dim=0)
print(x_unsquzzed.shape)
torch.Size([8])
torch.Size([1, 8])
torch.permute(input, dims)
重排张量的维度:
img = torch.rand(size=(128, 256, 3))
img_permuted = img.permute(2, 0, 1)
img.shape, img_permuted.shape
(torch.Size([128, 256, 3]), torch.Size([3, 128, 256]))
import torch
x = torch.arange(1, 10).reshape(1, 3, 3)
print(f"{x}, {x.shape}")
print(f"x[0]: \n{x[0]}")
print(f"x[0][0]: {x[0][0]}")
print(f"x[0][0][0]: {x[0][0][0]}")
tensor([[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]]), torch.Size([1, 3, 3])
x[0]:
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
x[0][0]: tensor([1, 2, 3])
x[0][0][0]: 1
可以使用 :
来指定此维度中的所有值,使用逗号 ,
来添加另一个维度。
NumPy 和 PyTorch 数据结构互转:
- Numpy Array -> PyTorch Tensor:
torch.from_numpy(ndarray)
。 - PyTorch Tensor -> Numpy Array:
torch.Tensor.numpy()
。
torch.rand()
方法可以生成一个给定大小而值随机的张量,但是每次生成都会不一样。如果需要每次随机都一样,需要固定下随机数种子。
import torch
import random
RANDOM_SEED=42
torch.manual_seed(seed=RANDOM_SEED)
random_tensor_A = torch.rand(3, 4)
torch.random.manual_seed(seed=RANDOM_SEED)
random_tensor_B = torch.rand(3, 4)
print(f"Tensor A:\n{random_tensor_A}\n")
print(f"Tensor B:\n{random_tensor_B}\n")
print(f"A == B?")
print(random_tensor_A == random_tensor_B)
Tensor A:
tensor([[0.8823, 0.9150, 0.3829, 0.9593],
[0.3904, 0.6009, 0.2566, 0.7936],
[0.9408, 0.1332, 0.9346, 0.5936]])
Tensor B:
tensor([[0.8823, 0.9150, 0.3829, 0.9593],
[0.3904, 0.6009, 0.2566, 0.7936],
[0.9408, 0.1332, 0.9346, 0.5936]])
A == B?
tensor([[True, True, True, True],
[True, True, True, True],
[True, True, True, True]])
导入 PyTorch:
import torch
torch.cuda.is_available()
True
设置设备类型:
# Set device type
device = "cuda" if torch.cuda.is_available() else "cpu"
device
'cuda'
检查设备数:
torch.cuda.device_count()
1
通过调用 to(device)
将张量(和模型)放在特定的设备上。
GPU 可以提供比 CPU 更快的数值计算,但有时候某些操作不支持在 GPU 中执行,所以需要将张量进行移动。
张量移动到 GPU 侧:
tensor = torch.tensor([1, 2, 3])
print(tensor, tensor.device)
tensor_on_gpu = tensor.to(device)
print(tensor_on_gpu)
tensor([1, 2, 3]) cpu
tensor([1, 2, 3], device='cuda:0')
张量移动到 CPU 侧:通过使用 tensor.CPU()
tensor_back_on_cpu = tensor_on_gpu.cpu()
print(tensor_back_on_cpu)
# 上面的代码返回CPU内存中GPU张量的副本,原始张量仍然在GPU上。
print(tensor_on_gpu)
tensor([1, 2, 3])
tensor([1, 2, 3], device='cuda:0')