1. 张量基础:从数据到Pytorch的核心结构

在Pytorch中,张量(Tensor) 是存储数据的基本结构,类似于NumPy的数组,但支持GPU加速和自动求导。理解张量是掌握Pytorch的第一步。

1.1 张量的创建
  • 从列表/数值创建:直接用torch.tensor()将Python列表转换为张量。
  import torch

  # 创建1维张量
  x = torch.tensor([1, 2, 3])
  print(x)  # 输出: tensor([1, 2, 3])
  print(x.shape)  # 输出: torch.Size([3]) (形状为3)
  • 从NumPy数组创建:通过torch.from_numpy()将NumPy数组转为张量(反之用.numpy())。
  import numpy as np

  a = np.array([[1, 2], [3, 4]])  # NumPy数组
  b = torch.from_numpy(a)         # 转为Pytorch张量
  print(b)  # 输出: tensor([[1, 2], [3, 4]], dtype=torch.int64)
  • 用Pytorch内置函数创建:快速生成全0/全1/随机张量。
  # 2x3全0张量
  zeros = torch.zeros(2, 3)
  # 3x2全1张量
  ones = torch.ones(3, 2)
  # 1x4随机数张量(均匀分布)
  rand = torch.rand(1, 4)
  # 1x4随机数张量(标准正态分布)
  randn = torch.randn(1, 4)

2. 张量维度变换:调整形状的核心操作

在深度学习中,数据的维度(形状)经常需要调整。Pytorch提供了多种工具实现维度变换,以下是最常用的操作:

2.1 reshape():灵活调整形状

reshape(new_shape)可以将张量的形状变为new_shape元素总数必须保持不变
适用场景:无论原张量是否连续存储,均可直接调整形状(更安全)。

# 创建2x3的张量(6个元素)
x = torch.rand(2, 3)
print("原形状:", x.shape)  # 输出: torch.Size([2, 3])

# 转为3x2形状(元素总数6=3*2)
y = x.reshape(3, 2)
print("reshape后形状:", y.shape)  # 输出: torch.Size([3, 2])
2.2 view():高效调整连续存储的形状

view(new_shape)reshape()类似,但仅适用于原张量是连续存储的情况(如未被transpose等操作破坏)。
适用场景:当张量是连续存储时,view()reshape()更快。

# 2x3的张量转为3x2(连续存储时可用view)
x = torch.rand(2, 3)
y = x.view(3, 2)  # 与reshape(3,2)效果相同
print(y.shape)  # 输出: torch.Size([3, 2])
2.3 squeeze():去掉维度大小为1的维度

squeeze(dim=None)会移除所有维度大小为1的维度,是“压缩”操作。
若需指定移除某个维度,用dim=index

# 原张量形状: (1, 2, 1, 3)(多个维度为1)
x = torch.rand(1, 2, 1, 3)
print("原形状:", x.shape)  # 输出: torch.Size([1, 2, 1, 3])

# 去掉所有维度为1的维度
y = x.squeeze()
print("squeeze后形状:", y.shape)  # 输出: torch.Size([2, 3])

# 仅去掉第0维(大小为1)
z = x.squeeze(0)
print("squeeze第0维后形状:", z.shape)  # 输出: torch.Size([2, 1, 3])
2.4 unsqueeze():增加维度大小为1的维度

unsqueeze(dim)会在指定位置插入一个维度大小为1的新维度,是“扩展”操作。
适用场景:模型输入需要固定维度时(如卷积层要求通道维度)。

# 原张量形状: (2, 3)
x = torch.rand(2, 3)
print("原形状:", x.shape)  # 输出: torch.Size([2, 3])

# 在第0维插入维度1(变为1x2x3)
y = x.unsqueeze(0)
print("unsqueeze后形状:", y.shape)  # 输出: torch.Size([1, 2, 3])

# 在第2维插入维度1(变为2x3x1)
z = x.unsqueeze(2)
print("unsqueeze第2维后形状:", z.shape)  # 输出: torch.Size([2, 3, 1])
2.5 transpose()permute():交换维度
  • transpose(dim0, dim1):交换两个维度,返回新张量。
  • permute(dims):交换多个维度(参数为维度索引元组),更灵活。
# 原张量形状: (2, 3)
x = torch.rand(2, 3)
print("原形状:", x.shape)  # 输出: torch.Size([2, 3])

# 交换第0维和第1维 → (3, 2)
y = x.transpose(0, 1)
print("transpose后形状:", y.shape)  # 输出: torch.Size([3, 2])

# 原张量形状: (1, 2, 3)
x = torch.rand(1, 2, 3)
# 交换维度顺序为 (2, 0, 1)
y = x.permute(2, 0, 1)
print("permute后形状:", y.shape)  # 输出: torch.Size([3, 1, 2])

3. 张量常用操作:从运算到聚合

除了维度变换,张量还支持多种基础运算和聚合操作,是模型计算的核心。

3.1 基础算术运算

支持+-*/等元素级运算,操作后形状不变(除非触发广播)。

x = torch.tensor([1, 2, 3])
y = torch.tensor([4, 5, 6])
print("x + y:", x + y)    # 输出: tensor([5, 7, 9])
print("x * y:", x * y)    # 输出: tensor([4, 10, 18])
print("x / y:", x / y)    # 输出: tensor([0.2500, 0.4000, 0.5000])
3.2 矩阵乘法:matmul()

深度学习中常用矩阵乘法(而非元素级乘法),用matmul()实现。

# 2x3矩阵 × 3x4矩阵 → 2x4矩阵
x = torch.rand(2, 3)
y = torch.rand(3, 4)
z = torch.matmul(x, y)
print("matmul后形状:", z.shape)  # 输出: torch.Size([2, 4])
3.3 广播机制(Broadcasting)

当两个张量维度不同但满足条件时,Pytorch会自动扩展维度进行运算(类似NumPy的广播)。
规则:维度从右向左对齐,大小为1的维度会被扩展到匹配对方。

# 形状(2,1) + 形状(1,3) → 自动扩展为(2,3)
a = torch.tensor([[1], [2]])  # 形状(2,1)
b = torch.tensor([[3, 4, 5]])  # 形状(1,3)
c = a + b
print("c的形状:", c.shape)  # 输出: torch.Size([2, 3])
print("c的内容:\n", c)
# 结果:
# [[4, 5, 6],
#  [5, 6, 7]]
3.4 聚合操作:sum()mean()max()

对张量按维度进行求和、均值、最大/最小值等统计,是模型计算损失的关键。

x = torch.tensor([[1, 2], [3, 4]])
print("总和:", x.sum())          # 输出: 10(所有元素相加)
print("按行求和:", x.sum(dim=1))  # 输出: tensor([3, 7])(每行相加)
print("均值:", x.mean())          # 输出: 2.5(所有元素均值)
print("按列最大值:", x.max(dim=0))  # 输出: (tensor([3, 4]), tensor([1, 1]))

4. 总结与练习

张量是Pytorch的核心,掌握维度变换和常用操作是构建模型的基础。建议通过以下练习巩固:

  1. 创建一个形状为(2, 2, 1)的张量,用squeeze()去掉多余维度,再用unsqueeze()恢复维度。
  2. 尝试不同维度张量的加法,观察广播机制如何自动扩展维度。
  3. permute()将(1, 2, 3)的张量变为(3, 2, 1)。

通过实际操作,你会逐渐熟悉张量的“形状语言”,为后续学习神经网络和深度学习模型打下坚实基础!

小夜