问题与目标
PyTorch 的 Tensor 同时承担三个职责:组织多维数据、在 CPU 或加速设备上执行计算、记录自动微分需要的运算关系。张量的形状、类型或设备只要有一项不匹配,训练就可能在距离真正错误很远的位置中断。
本篇输入 NumPy 数组和 Python 列表,完成张量创建、索引、形状变换、设备迁移和自动求导。输出包含形状、设备和梯度,便于逐项核对。

一次 PyTorch 计算同时受形状、类型和设备约束,前向运算形成计算图后,反向结果才会进入
.grad。
核心概念
四个基本属性
import torch
values = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
print(values.shape)
print(values.dtype)
print(values.device)
print(values.requires_grad)
shape描述每个维度的大小。dtype决定数值范围、精度和内存占用。device表示数据所在的计算设备。requires_grad表示是否跟踪与该张量相关的梯度。
模型参数通常是浮点张量,多分类类别索引通常是 torch.int64。数据类型不是显示细节,而是运算契约。
创建张量时的拷贝与共享
import numpy as np
array = np.array([1.0, 2.0, 3.0], dtype="float32")
shared = torch.from_numpy(array)
copied = torch.tensor(array)
array[0] = 99.0
print(shared, copied)
from_numpy() 通常与 NumPy 数组共享 CPU 内存,torch.tensor() 则创建副本。共享可以减少拷贝,也可能让一方的修改意外影响另一方。需要隔离时明确使用 .copy() 或 .clone()。
形状操作与内存连续性
| 操作 | 作用 | 注意点 |
|---|---|---|
reshape() | 改变形状 | 可能返回视图或副本 |
view() | 按同一内存重解释 | 常要求内存布局兼容 |
transpose() | 交换两个维度 | 返回视图 |
permute() | 重排多个维度 | 图像通道转换常用 |
unsqueeze() | 添加长度为 1 的维度 | 常用于补批次或通道维 |
squeeze() | 删除长度为 1 的维度 | 最好指定维度,避免误删批次维 |
reshape() 改变的是数据组织方式,不会自动交换语义不同的维度。从 NHWC 转换到 NCHW 应使用 permute(),而不是 reshape()。
设备契约
同一次运算的模型参数和输入必须在同一设备。设备选择不应写死:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tensor = torch.ones(3, 2, device=device)
print(device, tensor.device)
.to(device) 可能返回新张量,因此需要接住返回值。将 GPU 张量转为 NumPy 前,先使用 .detach().cpu().numpy()。
自动微分与梯度累积
PyTorch 在前向运算中构建动态计算图。对标量结果调用 backward() 后,叶子参数的梯度写入 .grad。再次反向会累加而不是覆盖,这为梯度累积提供了能力,也是训练循环容易出错的地方。
可运行实现
import torch
torch.manual_seed(42)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
features = torch.tensor(
[[1.0, 2.0], [2.0, 0.5], [3.0, 1.5]],
device=device,
)
weight = torch.tensor([0.3, -0.2], device=device, requires_grad=True)
bias = torch.tensor(0.1, device=device, requires_grad=True)
target = torch.tensor([1.0, 0.5, 1.4], device=device)
prediction = features @ weight + bias
loss = ((prediction - target) ** 2).mean()
loss.backward()
print("device:", device)
print("prediction shape:", prediction.shape)
print("loss:", round(loss.item(), 6))
print("weight grad:", weight.grad.detach().cpu().numpy().round(4))
print("bias grad:", round(bias.grad.item(), 4))
weight.grad.zero_()
bias.grad.zero_()
print("cleared:", weight.grad, bias.grad)
这里的 loss 是标量,因此可直接反向传播。如果输出是多元素张量,需要先对它汇总为标量,或显式提供上游梯度。
不记录梯度的推理
with torch.inference_mode():
inference_prediction = features @ weight + bias
print(inference_prediction.requires_grad)
推理阶段不需要反向传播,关闭梯度记录可以减少内存与运算开销。inference_mode() 比单纯不调用 backward() 更明确。
常见问题与排查
Expected all tensors to be on the same device:检查模型、输入和标签的.device。- 矩阵乘法类型不一致:检查是否一边是
float32、另一边是float64。 view()报内存不连续:优先用reshape(),或确认需要后调用.contiguous()。.numpy()报错:对跟踪梯度或 GPU 上的张量使用.detach().cpu().numpy()。- 梯度比预期大整数倍:检查是否忘记在上一次更新后清空梯度。
- 用
.item()处理多元素张量:.item()只能将单元素张量转成 Python 数值。
小结
Tensor 的形状、类型、设备和梯度状态共同构成计算契约。形状决定运算能否连接,设备决定数据在哪里计算,自动微分则根据前向运算生成梯度。这四项能在每一层都说清,PyTorch 代码才不会只停留在“试到不报错”。
License: CC BY-NC 4.0
Updated 2 hours ago
Was this article helpful? Give it a like.
0 comments


