学深度学习,第一道坎不是神经网络,而是 Tensor(张量)。这篇博客帮你用 10 分钟跨过这道坎。
一、Tensor 到底是什么?
一句话:Tensor 就是多维数组,是深度学习中所有数据的载体。
| 维度 | 名称 | 举例 |
|---|---|---|
| 0维 | 标量(Scalar) | 5 |
| 1维 | 向量(Vector) | [1, 2, 3] |
| 2维 | 矩阵(Matrix) | [[1,2],[3,4]] |
| 3维 | 张量(Tensor) | 一张 RGB 图片(高×宽×3) |
| 4维 | 张量 | 一批图片(batch×高×宽×3) |
你只需要记住:在 PyTorch 和 TensorFlow 里,所有东西都是 Tensor,包括你的数据、权重、梯度。
二、PyTorch 中的 Tensor(推荐入门)
1. 创建 Tensor
python
import torch
# 从 Python 列表创建
a = torch.tensor([1, 2, 3]) # 一维向量
b = torch.tensor([[1, 2], [3, 4]]) # 二维矩阵
# 常见快捷创建
zeros = torch.zeros(3, 4) # 3×4 的全零矩阵
ones = torch.ones(2, 3) # 2×3 的全一矩阵
rand = torch.rand(2, 3) # 2×3 的随机数(0~1均匀分布)
randn = torch.randn(2, 3) # 2×3 的随机数(标准正态分布)
arange = torch.arange(0, 10, 2) # [0, 2, 4, 6, 8]
2. 查看形状(最常用的操作)
python
x = torch.randn(3, 4, 5)
print(x.shape) # torch.Size([3, 4, 5])
print(x.ndim) # 3(维度数)
print(x.numel()) # 60(元素总数 = 3×4×5)
print(x.dtype) # torch.float32(数据类型)
⚡ 90% 的 bug 都出在 shape 不对上,养成随手
.shape的习惯。
3. 改变形状(核心操作)
python
x = torch.arange(12) # [0,1,2,3,4,5,6,7,8,9,10,11]
# reshape:改变形状,元素总数不变
y = x.reshape(3, 4) # 变成 3×4 矩阵
z = x.view(3, 4) # 和 reshape 类似,但要求内存连续
# 展平(常用于连接全连接层)
flat = x.view(-1) # -1 表示自动推断,等价于 x.reshape(12)
# 增加/删除维度(非常常用)
a = torch.tensor([1, 2, 3]) # shape: [3]
b = a.unsqueeze(0) # shape: [1, 3] 加一维
c = a.unsqueeze(1) # shape: [3, 1]
d = b.squeeze(0) # shape: [3] 删掉长度为1的维
一张图理解 unsqueeze 和 squeeze:
原始: [1, 2, 3] shape: (3,)
↓ unsqueeze(0)
[[1, 2, 3]] shape: (1, 3)
↓ unsqueeze(1)
[[[1], [2], [3]]] shape: (1, 3, 1)
↓ squeeze()
[1, 2, 3] shape: (3,) ← 去掉所有长度为1的维度
三、Tensor 的运算(和 NumPy 几乎一样)
1. 基本运算
python
a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])
# 逐元素运算(不是矩阵乘法!)
a + b # [5, 7, 9]
a * b # [4, 10, 18]
a.pow(2) # [1, 4, 9]
a.mean() # 2.0
a.sum() # 6.0
2. 矩阵乘法(最容易搞混)
python
A = torch.randn(2, 3)
B = torch.randn(3, 4)
# ✅ 矩阵乘法
C = A @ B # shape: (2, 4)
C = torch.matmul(A, B) # 等价写法
# ❌ 逐元素乘法(不是矩阵乘法!)
D = A * B # 只有 shape 完全相同时才能这样用
⚠️
*是逐元素乘,@才是矩阵乘。这个坑新手必踩。
3. 广播机制(Broadcasting)
python
a = torch.tensor([[1, 2, 3]]) # shape: (1, 3)
b = torch.tensor([[10], [20]]) # shape: (2, 1)
c = a + b
# 结果 shape: (2, 3)
# [[11, 12, 13],
# [21, 22, 23]]
规则:从右往左对齐,维度相同或其中一个为 1 就能广播。
四、Tensor 与 NumPy 互相转换
python
import numpy as np
# Tensor → NumPy
t = torch.tensor([1, 2, 3])
n = t.numpy() # 共享内存,改一个另一个也变
# NumPy → Tensor
n = np.array([1, 2, 3])
t = torch.from_numpy(n) # 也是共享内存
⚡ 共享内存意味着:如果你改了 NumPy 数组,Tensor 也会变,反之亦然。需要独立副本用
.clone()或.copy()。
五、GPU Tensor(一行代码提速)
python
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
x = torch.randn(3, 4).to(device) # 送到 GPU
y = torch.randn(3, 4).to(device)
z = x @ y # GPU 上运算
z.cpu() # 拿回 CPU
原则:同一个运算中的所有 Tensor 必须在同一设备上,否则报错。
六、一个完整的小例子
python
import torch
# 模拟一批数据:8 张 32×32 的 RGB 图片
batch_size = 8
images = torch.randn(batch_size, 3, 32, 32) # (N, C, H, W)
print(f"数据形状: {images.shape}") # torch.Size([8, 3, 32, 32])
print(f"每张图片元素数: {images.numel() / batch_size}") # 3072 = 3×32×32
# 假设经过一个卷积层后
output = torch.randn(batch_size, 16, 30, 30)
# 展平后喂给全连接层
flattened = output.view(batch_size, -1) # (8, 14400)
print(flattened.shape) # torch.Size([8, 14400])
七、新手最常踩的 5 个坑
| 坑 | 现象 | 解决 |
|---|---|---|
* 当成矩阵乘 |
维度对不上报错 | 用 @ 或 matmul |
| shape broadcast 意外 | 结果维度不是预期的 | 打印 .shape 确认 |
忘记 .float() |
类型不匹配报错 | Tensor 默认浮点是 float32,整数运算要转类型 |
view() vs reshape() |
有时候一个报错一个不报 | view() 要求内存连续,不确定就用 reshape() |
| GPU/CPU 混用 | Expected all tensors to be on the same device |
统一用 .to(device) |
八、PyTorch vs TensorFlow 怎么选?
| PyTorch | TensorFlow 2.x | |
|---|---|---|
| 风格 | 动态图,Pythonic,调试方便 | 静态图优化好,部署生态强 |
| 入门难度 | ⭐⭐ 更简单 | ⭐⭐⭐ 稍复杂 |
| 学术界 | 主流 | 逐渐被 PyTorch 取代 |
| 工业部署 | TorchScript / ONNX | TFLite / TF Serving 更成熟 |
结论:2024-2026 年,入门首选 PyTorch,没争议。
总结:记住这 5 句话就够了
- Tensor = 多维数组,是深度学习的唯一数据类型
.shape是你最好的朋友,遇到问题先打印它*是逐元素乘,@才是矩阵乘unsqueeze/squeeze/view(-1)三个操作覆盖 80% 的变形需求- GPU 上跑只需要加一句
.to(device)
Tensor 不难,难的是习惯用 .shape 思考。一旦养成这个习惯,后面学线性层、卷积层、注意力机制,都是同样的逻辑。