PyTorch 张量基础
什么是张量?
张量(Tensor)是PyTorch中最基本的数据结构,可以理解为多维数组的泛化:
- 0维张量:标量(scalar)
- 1维张量:向量(vector)
- 2维张量:矩阵(matrix)
- 3维及以上:高维张量
python
import torch
# 不同维度的张量
scalar = torch.tensor(3.14) # 0维
vector = torch.tensor([1, 2, 3]) # 1维
matrix = torch.tensor([[1, 2], [3, 4]]) # 2维
tensor_3d = torch.zeros(2, 3, 4) # 3维创建张量
1. 从数据创建
python
import torch
import numpy as np
# 从Python列表创建
data = [[1, 2], [3, 4]]
x = torch.tensor(data)
print(f"从列表创建: {x}")
# 从NumPy数组创建
np_array = np.array([[1, 2], [3, 4]])
x = torch.from_numpy(np_array)
print(f"从NumPy创建: {x}")
# 指定数据类型
x = torch.tensor([1.0, 2.0], dtype=torch.float32)
print(f"指定类型: {x.dtype}")2. 使用内置函数创建
python
# 零张量
zeros = torch.zeros(3, 4)
print(f"零张量:\n{zeros}")
# 一张量
ones = torch.ones(2, 3)
print(f"一张量:\n{ones}")
# 单位矩阵
eye = torch.eye(3)
print(f"单位矩阵:\n{eye}")
# 随机张量
rand = torch.rand(2, 3) # [0, 1)均匀分布
randn = torch.randn(2, 3) # 标准正态分布
print(f"随机张量:\n{rand}")
print(f"正态分布:\n{randn}")
# 指定范围的随机整数
randint = torch.randint(0, 10, (3, 3))
print(f"随机整数:\n{randint}")
# 等差数列
arange = torch.arange(0, 10, 2) # start, end, step
linspace = torch.linspace(0, 1, 5) # start, end, steps
print(f"等差数列: {arange}")
print(f"线性空间: {linspace}")3. 从其他张量创建
python
x = torch.tensor([[1, 2], [3, 4]])
# 创建相同形状的张量
zeros_like = torch.zeros_like(x)
ones_like = torch.ones_like(x)
rand_like = torch.rand_like(x, dtype=torch.float)
print(f"原张量:\n{x}")
print(f"相同形状的零张量:\n{zeros_like}")张量属性
python
x = torch.randn(3, 4, 5)
print(f"形状: {x.shape}") # 或 x.size()
print(f"维度: {x.ndim}") # 或 x.dim()
print(f"元素总数: {x.numel()}")
print(f"数据类型: {x.dtype}")
print(f"设备: {x.device}")
print(f"是否需要梯度: {x.requires_grad}")数据类型
PyTorch支持多种数据类型:
python
# 整数类型
int8 = torch.tensor([1, 2, 3], dtype=torch.int8)
int16 = torch.tensor([1, 2, 3], dtype=torch.int16)
int32 = torch.tensor([1, 2, 3], dtype=torch.int32)
int64 = torch.tensor([1, 2, 3], dtype=torch.int64)
# 浮点类型
float16 = torch.tensor([1.0, 2.0], dtype=torch.float16) # 半精度
float32 = torch.tensor([1.0, 2.0], dtype=torch.float32) # 单精度
float64 = torch.tensor([1.0, 2.0], dtype=torch.float64) # 双精度
# 布尔类型
bool_tensor = torch.tensor([True, False], dtype=torch.bool)
# 类型转换
x = torch.tensor([1, 2, 3])
x_float = x.float() # 转换为float32
x_double = x.double() # 转换为float64
x_int = x_float.int() # 转换为int32
print(f"原始类型: {x.dtype}")
print(f"转换后: {x_float.dtype}")张量操作
1. 索引和切片
python
x = torch.tensor([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 基本索引
print(f"第一行: {x[0]}")
print(f"第一列: {x[:, 0]}")
print(f"特定元素: {x[1, 2]}")
# 切片
print(f"前两行: {x[:2]}")
print(f"后两列: {x[:, -2:]}")
print(f"子矩阵: {x[1:3, 1:3]}")
# 布尔索引
mask = x > 6
print(f"大于6的元素: {x[mask]}")
# 高级索引
indices = torch.tensor([0, 2])
print(f"选择特定行: {x[indices]}")2. 形状变换
python
x = torch.randn(2, 3, 4)
# 重塑形状
reshaped = x.view(6, 4) # view要求内存连续
reshaped2 = x.reshape(8, 3) # reshape更灵活
# 展平
flattened = x.flatten() # 展平为1维
flattened_partial = x.flatten(start_dim=1) # 从第1维开始展平
# 增加维度
unsqueezed = x.unsqueeze(0) # 在第0维增加维度
unsqueezed2 = x.unsqueeze(-1) # 在最后增加维度
# 减少维度
squeezed = unsqueezed.squeeze(0) # 移除第0维
# 转置
transposed = x.transpose(0, 1) # 交换第0和第1维
permuted = x.permute(2, 0, 1) # 重新排列维度
print(f"原形状: {x.shape}")
print(f"重塑后: {reshaped.shape}")
print(f"转置后: {transposed.shape}")3. 拼接和分割
python
x = torch.randn(2, 3)
y = torch.randn(2, 3)
# 拼接
cat_dim0 = torch.cat([x, y], dim=0) # 沿第0维拼接
cat_dim1 = torch.cat([x, y], dim=1) # 沿第1维拼接
# 堆叠(增加新维度)
stacked = torch.stack([x, y], dim=0)
# 分割
chunks = torch.chunk(cat_dim0, 2, dim=0) # 分割为2块
splits = torch.split(cat_dim1, 3, dim=1) # 按指定大小分割
print(f"拼接结果形状: {cat_dim0.shape}")
print(f"堆叠结果形状: {stacked.shape}")
print(f"分割块数: {len(chunks)}")数学运算
1. 基本运算
python
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float)
y = torch.tensor([[5, 6], [7, 8]], dtype=torch.float)
# 逐元素运算
add = x + y # 或 torch.add(x, y)
sub = x - y # 或 torch.sub(x, y)
mul = x * y # 或 torch.mul(x, y)
div = x / y # 或 torch.div(x, y)
# 就地运算(修改原张量)
x.add_(y) # x = x + y
print(f"就地运算后: {x}")
# 标量运算
scaled = x * 2
shifted = x + 1
print(f"加法: {add}")
print(f"乘法: {mul}")2. 矩阵运算
python
x = torch.randn(3, 4)
y = torch.randn(4, 5)
# 矩阵乘法
matmul = torch.matmul(x, y) # 或 x @ y
mm = torch.mm(x, y) # 2D矩阵乘法
# 批量矩阵乘法
batch_x = torch.randn(10, 3, 4)
batch_y = torch.randn(10, 4, 5)
batch_result = torch.bmm(batch_x, batch_y)
print(f"矩阵乘法结果形状: {matmul.shape}")
print(f"批量矩阵乘法结果形状: {batch_result.shape}")3. 统计运算
python
x = torch.randn(3, 4)
# 基本统计
mean = x.mean() # 全局均值
mean_dim = x.mean(dim=0) # 沿第0维均值
std = x.std() # 标准差
var = x.var() # 方差
# 最值
max_val = x.max()
min_val = x.min()
max_indices = x.argmax() # 最大值索引
min_indices = x.argmin() # 最小值索引
# 求和
sum_all = x.sum()
sum_dim = x.sum(dim=1) # 沿第1维求和
print(f"均值: {mean:.4f}")
print(f"按维度均值: {mean_dim}")
print(f"最大值: {max_val:.4f}")广播机制
PyTorch支持广播(Broadcasting),允许不同形状的张量进行运算:
python
# 广播规则:
# 1. 从右边开始比较维度
# 2. 维度大小相等或其中一个为1时可以广播
# 3. 缺失的维度视为1
x = torch.randn(3, 4)
y = torch.randn(4) # 会广播为 (1, 4)
z = torch.randn(3, 1) # 会广播为 (3, 4)
result1 = x + y # (3, 4) + (4,) -> (3, 4)
result2 = x + z # (3, 4) + (3, 1) -> (3, 4)
print(f"x形状: {x.shape}")
print(f"y形状: {y.shape}")
print(f"广播结果形状: {result1.shape}")
# 手动广播
y_expanded = y.expand_as(x) # 扩展为与x相同的形状
print(f"手动扩展后: {y_expanded.shape}")设备管理
python
# 检查CUDA是否可用
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")
# 创建张量时指定设备
x = torch.randn(3, 4, device=device)
# 移动张量到不同设备
x_cpu = torch.randn(3, 4)
x_gpu = x_cpu.to(device) # 移动到GPU
x_back = x_gpu.cpu() # 移回CPU
print(f"CPU张量设备: {x_cpu.device}")
print(f"GPU张量设备: {x_gpu.device}")内存管理
python
# 检查张量是否共享内存
x = torch.randn(3, 4)
y = x.view(4, 3) # view共享内存
z = x.clone() # clone创建副本
print(f"x和y共享内存: {x.storage().data_ptr() == y.storage().data_ptr()}")
print(f"x和z共享内存: {x.storage().data_ptr() == z.storage().data_ptr()}")
# 分离张量(用于梯度计算)
x = torch.randn(3, 4, requires_grad=True)
y = x.detach() # 分离,不参与梯度计算
print(f"原张量需要梯度: {x.requires_grad}")
print(f"分离后需要梯度: {y.requires_grad}")实用技巧
1. 张量初始化技巧
python
# Xavier初始化
def xavier_init(tensor):
nn.init.xavier_uniform_(tensor)
# He初始化
def he_init(tensor):
nn.init.kaiming_uniform_(tensor)
# 自定义初始化
def custom_init(tensor):
with torch.no_grad():
tensor.uniform_(-0.1, 0.1)2. 性能优化
python
# 使用合适的数据类型
x = torch.randn(1000, 1000, dtype=torch.float32) # 而不是float64
# 避免频繁的CPU-GPU传输
x = torch.randn(1000, 1000, device='cuda')
# 在GPU上进行所有计算
result = x.mm(x.t()).sum()
# 使用就地操作节省内存
x.add_(1) # 而不是 x = x + 1总结
张量是PyTorch的核心,掌握张量操作是深度学习的基础:
- 创建张量:了解各种创建方法和数据类型
- 形状操作:熟练使用view、reshape、transpose等
- 数学运算:掌握基本运算和矩阵运算
- 广播机制:理解不同形状张量的运算规则
- 设备管理:合理使用CPU和GPU资源
这些基础操作将在后续的神经网络构建中频繁使用,建议多加练习!