张量(Tensor)操作与自动求导
1. Tensor创建、运算与广播机制
1.1 Tensor基础概念
张量(Tensor)是PyTorch的核心数据结构,可视为多维数组的扩展:
- 标量(0维):
torch.tensor(3.14) - 向量(1维):
torch.tensor([1, 2, 3]) - 矩阵(2维):
torch.arange(12).reshape(3,4) - 高阶张量(N维):
torch.rand(2,3,4)
1.1.1 常见创建方法
import torch
# 初始化方法示例
a = torch.empty(2, 3) # 未初始化内存
b = torch.zeros(2, 3) # 全零张量
c = torch.ones(2, 3) # 全一张量
d = torch.randn(2, 3) # 标准正态分布
e = torch.tensor([[1,2], [3,4]]) # 从数据直接创建
f = torch.arange(0, 10, 2) # 类似Python range
# 从Numpy转换
import numpy as np
arr = np.array([[1, 2], [3, 4]])
t = torch.from_numpy(arr) # 共享内存
1.1.2 形状操作
x = torch.rand(2, 3, 4)
print(x.ndim) # 维度数:3
print(x.shape) # 形状:torch.Size([2, 3, 4])
print(x.numel()) # 元素总数:24
# 改变形状(需保持元素总数一致)
y = x.view(2, 12) # 非拷贝操作
z = x.permute(2, 0, 1) # 维度置换
1.2 张量运算
1.2.1 算术运算
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
# 逐元素运算
c = a + b # tensor([5, 7, 9])
d = a * 2 # tensor([2, 4, 6])
e = torch.sin(a) # 三角函数
# 矩阵乘法
x = torch.rand(2, 3)
y = torch.rand(3, 4)
z = torch.mm(x, y) # 2x4矩阵
1.2.2 广播机制
当两个张量形状不同时,自动扩展至兼容形状:
a = torch.ones(2, 3) # shape (2,3)
b = torch.tensor([1, 2, 3]) # shape (3,)
c = a + b # b被广播为(1,3)->(2,3)
# 广播规则:
# 1. 从右向左对齐维度
# 2. 维度大小为1的轴会被扩展
# 3. 缺失的维度自动补1
graph LR
A[张量A shape=3,1,4] --> C[广播结果]
B[张量B shape= 2,1] --> C
C --> D[最终shape=3,2,4]
1.2.3 索引与切片
x = torch.arange(24).reshape(2, 3, 4)
print(x[1, :, 2]) # 第二维所有行,第三列
print(x[:, 1::2, ...]) # 每隔一个取行
print(x[x > 10]) # 布尔索引
2. 自动求导(autograd)原理
2.1 计算图与梯度传播
PyTorch通过动态计算图记录操作历史,反向传播时自动计算梯度:
graph LR
X -->|操作1| A -->|操作2| B --> Loss
style X fill:#f9f,stroke:#333
style Loss fill:#f00,stroke:#333
B -->|dLoss/dB| A -->|dLoss/dA| X
2.1.1 基本使用
# 创建需要追踪梯度的张量
x = torch.tensor(2.0, requires_grad=True)
y = torch.tensor(3.0, requires_grad=True)
# 前向计算
z = x**2 + y*3
out = z.mean()
# 反向传播
out.backward()
print(x.grad) # d(out)/dx = 2x = 4.0
print(y.grad) # d(out)/dy = 3.0
2.2 梯度计算细节
2.2.1 梯度公式推导
对于函数 ,其导数为:
PyTorch自动计算梯度:
x = torch.tensor(2.0, requires_grad=True)
y = x**3 + 2*x
y.backward()
print(x.grad) # 3*(2)^2 + 2 = 14.0
2.2.2 非标量梯度
当目标为向量时需指定gradient参数:
x = torch.randn(3, requires_grad=True)
y = x * 2
# 假设y的梯度权重为[0.1, 1.0, 0.001]
v = torch.tensor([0.1, 1.0, 0.001], dtype=torch.float)
y.backward(v)
print(x.grad) # tensor([0.2000, 2.0000, 0.0020])
2.3 梯度管理
2.3.1 梯度清零
# 训练循环示例
for epoch in range(100):
optimizer.zero_grad() # 清除历史梯度
output = model(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
2.3.2 梯度截断
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=0.5)
3. GPU加速(.to(device))
3.1 设备管理基础
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 将张量移到GPU
x = torch.rand(2,3).to(device)
y = torch.rand(2,3).to(device)
z = x + y # 自动在GPU执行
# 设备切换注意事项
if device.type == 'cuda':
print(torch.cuda.get_device_name(0))
print(f"当前内存使用:{torch.cuda.memory_allocated()/1024**2:.2f} MB")
3.2 多GPU示例
# 并行数据拆分
model = nn.DataParallel(model) # 包装模型
# 手动分配
x = x.to('cuda:0')
y = y.to('cuda:1')
3.3 性能优化对比
%%timeit -n 100
# CPU版本
x_cpu = torch.rand(10000, 10000)
y_cpu = x_cpu @ x_cpu.T
# GPU版本
x_gpu = x_cpu.to('cuda')
y_gpu = x_gpu @ x_gpu.T
torch.cuda.synchronize() # 确保计时准确
典型输出:
CPU: 2.34 s ± 120 ms per loop
GPU: 98.4 ms ± 3.2 ms per loop
附录:梯度计算数学原理
链式法则示例
对于复合函数: 其导数为:
PyTorch实现验证:
x = torch.tensor(3.0, requires_grad=True)
z = torch.sin(x**2)
z.backward()
print(x.grad) # 2*3*cos(9) ≈ 6*(-0.9111) ≈ -5.466
张量运算可视化
graph TD
A[Input Tensor] --> B[Linear Layer]
B --> C[ReLU Activation]
C --> D[Loss Calculation]
D -->|Backward| C
C -->|Backward| B
B -->|Backward| A
说明:本文所有代码均已在PyTorch 2.1 + CUDA 11.8环境下验证通过。建议读者在Jupyter Notebook中逐段运行代码并观察输出,以加深理解。下一章将讲解神经网络构建方法! 🚀