Skip to content

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的核心,掌握张量操作是深度学习的基础:

  1. 创建张量:了解各种创建方法和数据类型
  2. 形状操作:熟练使用view、reshape、transpose等
  3. 数学运算:掌握基本运算和矩阵运算
  4. 广播机制:理解不同形状张量的运算规则
  5. 设备管理:合理使用CPU和GPU资源

这些基础操作将在后续的神经网络构建中频繁使用,建议多加练习!

本站内容仅供学习和研究使用。