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的核心,掌握维度变换和常用操作是构建模型的基础。建议通过以下练习巩固:
- 创建一个形状为(2, 2, 1)的张量,用
squeeze()去掉多余维度,再用unsqueeze()恢复维度。 - 尝试不同维度张量的加法,观察广播机制如何自动扩展维度。
- 用
permute()将(1, 2, 3)的张量变为(3, 2, 1)。
通过实际操作,你会逐渐熟悉张量的“形状语言”,为后续学习神经网络和深度学习模型打下坚实基础!