什么是张量(Tensor)?
在 PyTorch 中,张量(Tensor)是最核心的数据结构,可以理解为:
张量 = 多维数组(NumPy ndarray 的增强版) + GPU支持 + 自动求导能力
不同维度的张量对应关系:
| 阶数 | 名称 | 示例 |
|---|---|---|
| 0维 | 标量 | 3 |
| 1维 | 向量 | [1,2,3] |
| 2维 | 矩阵 | [[1,2],[3,4]] |
| 3维+ | 高维张量 | 图像/视频数据 |
例如,一张 RGB 图片通常表示为:
bash
[3, H, W]
张量的创建
1. 直接创建
python
import torch
# 标量
a = torch.tensor(5)
# 向量
b = torch.tensor([1, 2, 3])
# 矩阵
c = torch.tensor([[1, 2], [3, 4]])
2. 常用初始化方法
python
# 全0
zeros = torch.zeros(2, 3)
# 全1
ones = torch.ones(2, 3)
# 随机数(0~1)
rand = torch.rand(2, 3)
# 正态分布
randn = torch.randn(2, 3)
# 单位矩阵
eye = torch.eye(3)
3. 指定数据类型
python
x = torch.tensor([1,2,3], dtype=torch.float32)
常见类型:
- torch.float32
- torch.int64
- torch.bool
4. 从 NumPy 转换
python
import numpy as np
np_array = np.array([1,2,3])
tensor = torch.from_numpy(np_array)
注意:共享内存!
python
np_array[0] = 100
print(tensor) # 会变化
张量的基本属性
python
x = torch.randn(3, 4)
| 属性 | 说明 |
|---|---|
| x.shape | 形状 |
| x.size() | 同shape |
| x.dtype | 数据类型 |
| x.device | 所在设备 |
| x.ndim | 维度数 |
示例:
python
print(x.shape) # (3,4)
print(x.ndim) # 2
张量运算
1. 基本运算
python
a = torch.tensor([1,2,3])
b = torch.tensor([4,5,6])
print(a + b)
print(a - b)
print(a * b)
print(a / b)
2. 矩阵运算
python
A = torch.randn(2, 3)
B = torch.randn(3, 4)
# 矩阵乘法
C = torch.matmul(A, B)
# 或
C = A @ B
3. 广播机制(Broadcasting)
当两个形状不同的张量进行运算时,自动把较小的张量"扩展"为相同形状,然后逐元素计算。
python
a = torch.tensor([[1,2,3],
[4,5,6]])
b = torch.tensor([1,2,3])
print(a + b)
自动扩展为:
python
[1,2,3]
→
[[1,2,3],
[1,2,3]]
4. 常见函数
python
x = torch.randn(3,3)
torch.sum(x)
torch.mean(x)
torch.max(x)
torch.min(x)
索引与切片
x = torch.tensor([[1,2,3],
[4,5,6]])
print(x[0]) # 第一行
print(x[:,1]) # 第二列
print(x[0,1]) # 单个元素
高级索引
x = torch.arange(10)
idx = torch.tensor([1,3,5])
print(x[idx])
形状操作
1. reshape
python
x = torch.arange(6)
y = x.reshape(2,3)
2. view(共享内存)
python
y = x.view(2,3)
区别:
- reshape:可能复制
- view:必须连续内存
3. transpose / permute
python
x = torch.randn(2,3)
# 转置
x.t()
# 高维交换
x = torch.randn(2,3,4)
x = x.permute(1,0,2)
4. squeeze / unsqueeze
python
x = torch.randn(1,3,1)
x.squeeze() # 去掉1维
x.unsqueeze(0) # 增加维度
设备(CPU/GPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
x = torch.tensor([1,2,3]).to(device)
或:
x = x.cuda()
x = x.cpu()
自动求导(Autograd)
这是 PyTorch 最核心的能力。
1. requires_grad
python
x = torch.tensor(2.0, requires_grad=True)
y = x * x + 3*x
y.backward()
print(x.grad) # dy/dx = 2x + 3
输出:
7
2. 计算图
PyTorch 会自动构建:
python
x → y → loss
并通过反向传播计算梯度。
3. 梯度清零
python
optimizer.zero_grad()
否则梯度会累加!
4. 禁用梯度
python
with torch.no_grad():
y = x * 2
用于:
- 推理
- 提升性能
内存与性能
1. inplace操作
x += 1
优点:
- 节省内存
缺点:
- 可能破坏计算图
2. contiguous
x = x.contiguous()
在 view() 前常用。
3. clone vs detach
y = x.clone() # 拷贝
z = x.detach() # 断开计算图
实际案例:简单线性回归
python
import torch
# 数据
x = torch.tensor([[1.0],[2.0],[3.0]])
y = torch.tensor([[2.0],[4.0],[6.0]])
# 参数
w = torch.randn(1, requires_grad=True)
# 训练
for i in range(100):
y_pred = x * w
loss = ((y_pred - y)**2).mean()
loss.backward()
with torch.no_grad():
w -= 0.1 * w.grad
w.grad.zero_()
print(w)
Tensor 内存布局与 Stride 原理
Tensor 的本质
很多人以为 Tensor 是"多维数组",其实不是。
Tensor 本质 = 一维连续内存 + shape + stride
举个例子:
python
import torch
x = torch.tensor([[1,2,3],
[4,5,6]])
逻辑结构:
[[1,2,3],
[4,5,6]]
真实内存:
[1,2,3,4,5,6]
多维结构只是"解释方式"。
什么是 stride?
核心定义
stride 表示:在某一维上移动1步,需要跳过多少个内存元素
示例
x = torch.tensor([[1,2,3],
[4,5,6]])
print(x.stride())
输出:
(3, 1)
含义:
| 维度 | stride | 含义 |
|---|---|---|
| 行 | 3 | 跳到下一行需要跨3个元素 |
| 列 | 1 | 相邻元素间隔1 |
地址计算公式(重点)
对于元素 x[i][j]:
内存位置 = base + i*3 + j*1
为什么 view 会报错?
经典错误:
RuntimeError: view size is not compatible
示例
python
x = torch.randn(2,3)
y = x.t()
y.view(6) # 报错
原因
view()要求 Tensor 在内存中是连续的(contiguous)
而 transpose 之后:
stride = (1,3)
已经不是连续布局。
contiguous 是什么?
定义
contiguous = 内存按行优先连续排列
解决方法
y = y.contiguous()
y.view(6)
本质
contiguous() = 重新拷贝一份连续内存
总结
PyTorch 张量的核心可以概括为:
三大能力:
- 多维数据表示(类似 NumPy)
- GPU 加速
- 自动求导(Autograd)
五大重点:
- 创建与初始化
- 运算与广播
- 形状变换
- 设备管理
- 自动求导