Skip to content

张量 Tensor:一切数据的载体

张量(Tensor)是 PyTorch 中最基本的数据结构。模型参数是张量,输入数据是张量,中间计算结果还是张量。理解张量,重点是理解它的三要素:形状(shape)、数据类型(dtype)、设备(device)

创建张量的 4 种方式

以下代码可在任意 Python 环境运行(终端执行 python 进入交互模式,或写入 .py 文件后 python 文件名.py):

python
import torch

# 1. 从 Python 列表创建
a = torch.tensor([[1.0, 2.0], [3.0, 4.0]])

# 2. 创建特殊值张量(调试和初始化常用)
zeros = torch.zeros(2, 3)      # 全 0,形状 [2, 3]
ones = torch.ones(2, 3)        # 全 1
rand = torch.rand(2, 3)        # [0,1) 均匀分布随机数
seq = torch.arange(0, 10, 2)   # [0, 2, 4, 6, 8]

# 3. 基于已有张量创建(自动继承 dtype 和 device,推荐)
b = torch.zeros_like(a)        # 与 a 同形状同类型的全 0 张量

# 4. 从 NumPy 数组转换(打通科学计算生态)
import numpy as np
c = torch.from_numpy(np.array([1, 2, 3]))
back = c.numpy()               # 转回 NumPy

为什么推荐 zeros_like 显式写 torch.zeros(2, 3, dtype=torch.float32) 时 dtype 容易和模型不匹配报错;*_like 自动对齐,少踩一类坑。

张量三要素之一:shape(形状)

形状是深度学习代码的第一公民。 每个张量都有 shape,代表各维度上的大小:

python
x = torch.rand(32, 3, 28, 28)
print(x.shape)   # torch.Size([32, 3, 28, 28])
print(x.dim())   # 4,维度数(也叫阶/rank)
print(x.numel()) # 802816,元素总数

这个 [32, 3, 28, 28] 是图像批次的典型形状,读法是从外到内:

32 张图片 → 每张 3 通道(RGB) → 每通道 28 行 28 列像素
[ batch,  channel, height, width ]        # 简称 NCHW 格式

而文本批次的典型形状是 [batch, 序列长度][batch, 序列长度, 词向量维度]看到任何张量,先问自己每个维度代表什么,shape 想清楚了,代码就对了一大半。

改变形状的常用操作

python
x = torch.rand(32, 3, 28, 28)

# 展平:把后三个维度压成一维,常用于图像进入全连接层之前
flat = x.flatten(start_dim=1)   # [32, 2352],batch 维保持不动

# reshape:任意重排(元素总数不变)
y = x.reshape(32, -1)           # -1 表示自动推算,等价于上面的 flatten

# 交换/升/降维
t = x.permute(0, 2, 3, 1)       # [32, 28, 28, 3],调整维度顺序
img = x[0]                      # 取下标,[3, 28, 28]——索引减少一维
img1 = x[0:1]                   # 取切片,[1, 3, 28, 28]——维度数不变!
uns = x.unsqueeze(1)            # 在 1 号位插入一个 1 维:[32, 1, 3, 28, 28]

坑预警:x[0]x[0:1] 的区别。 前者形状降一维 [3,28,28],后者保持 [1,3,28,28]。需要保持 batch 维时(模型永远期望 batch 输入),用切片或 unsqueeze(0)。单个样本推理报错,八成是这个。

view 与 reshape 的区别(面试高频)

view 要求张量内存连续,reshape 不连续时会先自动拷贝。日常建议:不确定就用 reshape;追求性能、明确连续时用 view。报错 view size is not compatible with input tensor's size 时,先 x.contiguous() 再 view,或直接换 reshape。

张量三要素之二:dtype(数据类型)

python
a = torch.tensor([1, 2, 3])              # int64
b = torch.tensor([1.0, 2.0, 3.0])        # float32(默认浮点类型)
c = b.to(torch.float16)                  # 半精度,训练大模型省显存常用
d = b.to(torch.bfloat16)                 # AI 领域主流半精度格式

a + b   # 报错!dtype 不匹配,PyTorch 不会把 int 和 float 隐式混合运算

深度学习里 95% 的参数张量是 float32torch.float 是其别名),混合精度训练时才出现 float16/bfloat16。标签(分类任务的正确答案)通常是 int64torch.long)——后面实战中损失函数对 dtype 的要求会再次强调这点。

转换写法:x.to(torch.float32) 或创建时指定 torch.zeros(2, 2, dtype=torch.float32)

张量三要素之三:device(设备)

张量可以住在 CPU 也可以住在 GPU 上,两个张量运算前必须在同一设备上

python
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
x = torch.rand(3, 3).to(device)   # 搬到 GPU(没有 GPU 时这行无害)
python
# 验证 GPU 是否可用,终端执行:
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU only')"

坑预警: CPU 张量和 GPU 模型(或反之)相遇会报 Expected all tensors to be on the same device。规则很简单:模型和数据,一起 .to(device)。另外把 GPU 张量转回 NumPy 前必须先 .cpu()

基础运算

python
a = torch.rand(2, 3)
b = torch.rand(2, 3)

print(a + b)          # 逐元素加(广播规则同 NumPy)
print(a * b)          # 逐元素乘
print(a @ b.T)        # 矩阵乘(@ 运算符),[2,3]@[3,2] → [2,2]
print(torch.sigmoid(a))  # 激活函数等数学函数
print(a.max(), a.sum())  # 归约运算

矩阵乘法 @ 的形状规则是排查网络报错的核心功夫:[m, k] @ [k, n] → [m, n],中间维度必须相等。全连接层本质就是矩阵乘,后面搭模型时会反复用到。

原地操作与内存

python
a.add_(1)      # 带下划线后缀 = 原地修改 a 本身
a = a + 1      # 创建新张量

训练循环中偶尔用原地操作提升性能,但对需要求梯度的张量做原地操作会报错(破坏了计算图)。新手阶段记住:拿不准就不用带 _ 的函数。

小结

要素属性为什么重要
形状.shape80% 的 bug 是形状问题;先想清每个维度含义
类型.dtype参数 float32、标签 int64,不匹配就报错
设备.device运算双方必须同设备;模型和数据一起 .to(device)

下一篇我们看 PyTorch 真正的魔法:自动求导 Autograd