|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. 引言
PyTorch作为当前最受欢迎的深度学习框架之一,提供了强大而灵活的张量(矩阵)操作功能。掌握PyTorch中的矩阵操作是进行深度学习研究和应用的基础。本指南将从基础概念开始,逐步深入到高级应用,帮助读者全面理解PyTorch中的矩阵操作,并通过丰富的代码示例展示其实际应用。
2. PyTorch矩阵基础
2.1 张量(Tensor)概念
在PyTorch中,矩阵是以张量(Tensor)的形式存在的。张量可以看作是多维数组,0维张量是一个标量,1维张量是向量,2维张量是矩阵,3维及以上可以称为高维张量。
- import torch
- # 创建不同维度的张量
- # 标量(0维张量)
- scalar = torch.tensor(3.1415)
- print(f"标量: {scalar}, 维度: {scalar.dim()}")
- # 向量(1维张量)
- vector = torch.tensor([1, 2, 3, 4, 5])
- print(f"向量: {vector}, 维度: {vector.dim()}")
- # 矩阵(2维张量)
- matrix = torch.tensor([[1, 2, 3], [4, 5, 6]])
- print(f"矩阵:\n{matrix}\n维度: {matrix.dim()}")
- # 3维张量
- tensor_3d = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
- print(f"3维张量:\n{tensor_3d}\n维度: {tensor_3d.dim()}")
复制代码
2.2 张量的属性
每个张量都有一些重要的属性,了解这些属性对于操作张量至关重要:
- # 创建一个示例张量
- x = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32)
- print(f"张量:\n{x}")
- print(f"形状: {x.shape}") # 或 x.size()
- print(f"数据类型: {x.dtype}")
- print(f"设备: {x.device}") # CPU或GPU
- print(f"元素个数: {x.numel()}")
- print(f"维度: {x.dim()}")
复制代码
3. 矩阵创建与初始化
3.1 直接创建矩阵
- # 从列表创建矩阵
- matrix_from_list = torch.tensor([[1, 2, 3], [4, 5, 6]])
- print(f"从列表创建的矩阵:\n{matrix_from_list}")
- # 从NumPy数组创建矩阵
- import numpy as np
- numpy_array = np.array([[1, 2, 3], [4, 5, 6]])
- matrix_from_numpy = torch.from_numpy(numpy_array)
- print(f"从NumPy数组创建的矩阵:\n{matrix_from_numpy}")
复制代码
3.2 使用特定函数创建矩阵
PyTorch提供了多种函数来创建特定类型的矩阵:
- # 创建全零矩阵
- zeros = torch.zeros(3, 4)
- print(f"全零矩阵:\n{zeros}")
- # 创建全一矩阵
- ones = torch.ones(3, 4)
- print(f"全一矩阵:\n{ones}")
- # 创建单位矩阵
- eye = torch.eye(3)
- print(f"单位矩阵:\n{eye}")
- # 创建对角矩阵
- diag = torch.diag(torch.tensor([1, 2, 3]))
- print(f"对角矩阵:\n{diag}")
- # 创建随机矩阵
- # 均匀分布
- rand = torch.rand(3, 4) # [0, 1)之间的均匀分布
- print(f"均匀分布随机矩阵:\n{rand}")
- # 标准正态分布
- randn = torch.randn(3, 4) # 标准正态分布
- print(f"标准正态分布随机矩阵:\n{randn}")
- # 指定范围内的整数随机矩阵
- randint = torch.randint(0, 10, (3, 4)) # [0, 10)之间的随机整数
- print(f"整数随机矩阵:\n{randint}")
复制代码
3.3 创建特定模式的矩阵
- # 创建上三角矩阵
- upper_tri = torch.triu(torch.ones(3, 3))
- print(f"上三角矩阵:\n{upper_tri}")
- # 创建下三角矩阵
- lower_tri = torch.tril(torch.ones(3, 3))
- print(f"下三角矩阵:\n{lower_tri}")
- # 创建特定范围的矩阵
- arange = torch.arange(0, 12).view(3, 4)
- print(f"使用arange创建的矩阵:\n{arange}")
- # 创建等间距的矩阵
- linspace = torch.linspace(0, 1, 12).view(3, 4)
- print(f"使用linspace创建的矩阵:\n{linspace}")
复制代码
4. 矩阵基本操作
4.1 索引与切片
- # 创建一个示例矩阵
- x = torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
- print(f"原始矩阵:\n{x}")
- # 获取单个元素
- element = x[1, 2] # 第2行,第3列
- print(f"单个元素 x[1, 2]: {element}")
- # 获取一行
- row = x[1, :] # 第2行
- print(f"第2行: {row}")
- # 获取一列
- col = x[:, 2] # 第3列
- print(f"第3列: {col}")
- # 获取子矩阵
- submatrix = x[0:2, 1:3] # 第1-2行,第2-3列
- print(f"子矩阵 x[0:2, 1:3]:\n{submatrix}")
- # 使用步长
- step = x[::2, ::2] # 每隔一行取一行,每隔一列取一列
- print(f"使用步长的子矩阵 x[::2, ::2]:\n{step}")
复制代码
4.2 修改矩阵元素
- # 创建一个示例矩阵
- x = torch.ones(3, 4)
- print(f"原始矩阵:\n{x}")
- # 修改单个元素
- x[1, 2] = 5
- print(f"修改单个元素后的矩阵:\n{x}")
- # 修改一行
- x[1, :] = 2
- print(f"修改一行后的矩阵:\n{x}")
- # 修改一列
- x[:, 2] = 3
- print(f"修改一列后的矩阵:\n{x}")
- # 修改子矩阵
- x[0:2, 1:3] = 4
- print(f"修改子矩阵后的矩阵:\n{x}")
复制代码
4.3 矩阵拼接与分割
- # 创建示例矩阵
- x = torch.tensor([[1, 2], [3, 4]])
- y = torch.tensor([[5, 6], [7, 8]])
- # 按行拼接(垂直方向)
- row_cat = torch.cat([x, y], dim=0)
- print(f"按行拼接:\n{row_cat}")
- # 按列拼接(水平方向)
- col_cat = torch.cat([x, y], dim=1)
- print(f"按列拼接:\n{col_cat}")
- # 使用stack堆叠
- stacked = torch.stack([x, y], dim=0)
- print(f"堆叠后的张量形状: {stacked.shape}")
- print(f"堆叠后的张量:\n{stacked}")
- # 矩阵分割
- # 创建一个可以均匀分割的矩阵
- z = torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
- # 按行分割
- row_split = torch.split(z, 2, dim=0)
- print(f"按行分割的结果:")
- for i, split in enumerate(row_split):
- print(f"分割块 {i+1}:\n{split}")
- # 按列分割
- col_split = torch.split(z, 2, dim=1)
- print(f"按列分割的结果:")
- for i, split in enumerate(col_split):
- print(f"分割块 {i+1}:\n{split}")
- # 使用chunk分割
- chunked = torch.chunk(z, 2, dim=0)
- print(f"使用chunk按行分割的结果:")
- for i, chunk in enumerate(chunked):
- print(f"分割块 {i+1}:\n{chunk}")
复制代码
5. 矩阵运算
5.1 算术运算
- # 创建示例矩阵
- x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
- y = torch.tensor([[5, 6], [7, 8]], dtype=torch.float32)
- # 加法
- add = x + y
- print(f"矩阵加法:\n{add}")
- # 减法
- sub = x - y
- print(f"矩阵减法:\n{sub}")
- # 逐元素乘法(Hadamard积)
- mul = x * y
- print(f"逐元素乘法:\n{mul}")
- # 逐元素除法
- div = x / y
- print(f"逐元素除法:\n{div}")
- # 标量运算
- scalar_mul = x * 2
- print(f"标量乘法:\n{scalar_mul}")
- scalar_add = x + 10
- print(f"标量加法:\n{scalar_add}")
复制代码
5.2 矩阵乘法
- # 创建示例矩阵
- x = torch.tensor([[1, 2], [3, 4], [5, 6]], dtype=torch.float32)
- y = torch.tensor([[7, 8, 9], [10, 11, 12]], dtype=torch.float32)
- # 矩阵乘法
- matmul = torch.matmul(x, y)
- print(f"矩阵乘法结果:\n{matmul}")
- # 使用@运算符
- matmul2 = x @ y
- print(f"使用@运算符的矩阵乘法结果:\n{matmul2}")
- # 使用torch.mm(仅适用于2D矩阵)
- mm = torch.mm(x, y)
- print(f"使用torch.mm的矩阵乘法结果:\n{mm}")
- # 批量矩阵乘法
- # 创建3D张量(批量矩阵)
- batch_x = torch.randn(10, 3, 4) # 10个3x4的矩阵
- batch_y = torch.randn(10, 4, 5) # 10个4x5的矩阵
- # 批量矩阵乘法
- batch_matmul = torch.bmm(batch_x, batch_y)
- print(f"批量矩阵乘法结果形状: {batch_matmul.shape}") # 应该是(10, 3, 5)
复制代码
5.3 其他常用矩阵运算
- # 创建示例矩阵
- x = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=torch.float32)
- # 转置
- transpose = x.t()
- print(f"矩阵转置:\n{transpose}")
- # 或者使用transpose方法
- transpose2 = x.transpose(0, 1)
- print(f"使用transpose方法的矩阵转置:\n{transpose2}")
- # 矩阵的迹(对角元素之和)
- trace = torch.trace(x)
- print(f"矩阵的迹: {trace}")
- # 矩阵的行列式
- det = torch.det(x)
- print(f"矩阵的行列式: {det}")
- # 矩阵的逆
- try:
- inv = torch.inverse(x)
- print(f"矩阵的逆:\n{inv}")
- except RuntimeError:
- print("该矩阵不可逆")
- # 矩阵的伪逆
- pinv = torch.pinverse(x)
- print(f"矩阵的伪逆:\n{pinv}")
- # 矩阵的秩
- rank = torch.linalg.matrix_rank(x)
- print(f"矩阵的秩: {rank}")
- # 特征值和特征向量
- eigenvalues, eigenvectors = torch.linalg.eig(x)
- print(f"特征值:\n{eigenvalues}")
- print(f"特征向量:\n{eigenvectors}")
- # 奇异值分解
- U, S, V = torch.linalg.svd(x)
- print(f"奇异值分解 U:\n{U}")
- print(f"奇异值 S:\n{S}")
- print(f"奇异值分解 V:\n{V}")
复制代码
6. 矩阵变形与重塑
6.1 改变形状
- # 创建一个示例矩阵
- x = torch.arange(1, 13)
- print(f"原始向量: {x}")
- # 转换为矩阵
- matrix = x.view(3, 4)
- print(f"转换为3x4矩阵:\n{matrix}")
- # 或者使用reshape
- matrix2 = x.reshape(3, 4)
- print(f"使用reshape转换为3x4矩阵:\n{matrix2}")
- # 展平矩阵
- flatten = matrix.flatten()
- print(f"展平后的向量: {flatten}")
- # 或者使用view
- flatten2 = matrix.view(-1)
- print(f"使用view展平后的向量: {flatten2}")
- # 转置并改变形状
- x = torch.randn(2, 3, 4)
- print(f"原始张量形状: {x.shape}")
- # 转置前两个维度
- transposed = x.transpose(0, 1)
- print(f"转置后的形状: {transposed.shape}")
- # 使用permute进行更复杂的维度变换
- permuted = x.permute(2, 0, 1) # 将维度从(2,3,4)变为(4,2,3)
- print(f"使用permute后的形状: {permuted.shape}")
复制代码
6.2 增减维度
- # 创建一个示例矩阵
- x = torch.tensor([[1, 2, 3], [4, 5, 6]])
- print(f"原始矩阵形状: {x.shape}")
- # 增加维度
- # 在前面增加一个维度
- unsqueeze_front = x.unsqueeze(0)
- print(f"在前面增加维度后的形状: {unsqueeze_front.shape}")
- # 在中间增加一个维度
- unsqueeze_middle = x.unsqueeze(1)
- print(f"在中间增加维度后的形状: {unsqueeze_middle.shape}")
- # 在最后增加一个维度
- unsqueeze_end = x.unsqueeze(2)
- print(f"在最后增加维度后的形状: {unsqueeze_end.shape}")
- # 减少维度
- # 创建一个可以减少维度的张量
- y = torch.tensor([[[1, 2, 3]]]) # 形状为(1, 1, 3)
- print(f"原始张量形状: {y.shape}")
- # 减少维度
- squeeze = y.squeeze()
- print(f"减少所有维度为1的维度后的形状: {squeeze.shape}")
- # 减少特定位置的维度
- squeeze_0 = y.squeeze(0)
- print(f"减少第0个维度后的形状: {squeeze_0.shape}")
复制代码
7. 广播机制
广播是PyTorch中一个强大的功能,它允许不同形状的张量进行算术运算。广播遵循一定的规则,使得运算更加灵活和高效。
7.1 广播规则
广播遵循以下规则:
1. 如果张量的维度数不同,则在较小形状的维度前面补1,直到两个张量的维度数相同。
2. 对于每个维度,如果该维度的大小相同,或者其中一个为1,或者其中一个不存在,则这两个张量在该维度上是兼容的。
3. 广播后,每个维度的大小取两个张量在该维度大小的最大值。
4. 在任何维度上,如果一个张量的大小为1,而另一个大于1,则第一个张量在该维度上被”复制”以匹配另一个张量的大小。
7.2 广播示例
- # 标量与矩阵的广播
- x = torch.tensor([[1, 2, 3], [4, 5, 6]])
- y = 2
- result = x + y
- print(f"标量与矩阵的广播:\n{result}")
- # 向量与矩阵的广播
- x = torch.tensor([[1, 2, 3], [4, 5, 6]]) # 形状(2, 3)
- y = torch.tensor([10, 20, 30]) # 形状(3,)
- result = x + y
- print(f"向量与矩阵的广播:\n{result}")
- # 矩阵与矩阵的广播
- x = torch.tensor([[1], [2], [3]]) # 形状(3, 1)
- y = torch.tensor([[10, 20]]) # 形状(1, 2)
- result = x + y
- print(f"矩阵与矩阵的广播:\n{result}")
- # 更复杂的广播示例
- x = torch.randn(2, 1, 3) # 形状(2, 1, 3)
- y = torch.randn(4, 3) # 形状(4, 3)
- # 广播后,x的形状变为(2, 4, 3),y的形状变为(1, 4, 3)
- result = x + y
- print(f"复杂广播结果的形状: {result.shape}")
复制代码
7.3 显式广播
有时候,我们需要显式地扩展张量的维度以利用广播机制:
- # 创建示例张量
- x = torch.tensor([1, 2, 3]) # 形状(3,)
- y = torch.tensor([[4], [5]]) # 形状(2, 1)
- # 显式扩展x的维度
- x_expanded = x.unsqueeze(0) # 形状(1, 3)
- print(f"扩展x的维度后: {x_expanded.shape}")
- # 使用expand进行广播
- x_broadcast = x_expanded.expand(2, 3) # 形状(2, 3)
- print(f"使用expand广播后: {x_broadcast.shape}")
- # 使用expand_as
- z = torch.zeros(2, 3)
- x_broadcast_as = x_expanded.expand_as(z)
- print(f"使用expand_as广播后: {x_broadcast_as.shape}")
- # 使用repeat进行复制(与广播不同)
- x_repeated = x_expanded.repeat(2, 1) # 形状(2, 3)
- print(f"使用repeat复制后: {x_repeated.shape}")
复制代码
8. 高级矩阵操作
8.1 聚合操作
- # 创建示例矩阵
- x = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=torch.float32)
- # 求和
- # 全部元素求和
- sum_all = torch.sum(x)
- print(f"所有元素的和: {sum_all}")
- # 按行求和
- sum_rows = torch.sum(x, dim=1)
- print(f"按行求和: {sum_rows}")
- # 按列求和
- sum_cols = torch.sum(x, dim=0)
- print(f"按列求和: {sum_cols}")
- # 均值
- mean_all = torch.mean(x)
- print(f"所有元素的均值: {mean_all}")
- mean_rows = torch.mean(x, dim=1)
- print(f"按行求均值: {mean_rows}")
- mean_cols = torch.mean(x, dim=0)
- print(f"按列求均值: {mean_cols}")
- # 最大值和最小值
- max_all = torch.max(x)
- print(f"所有元素的最大值: {max_all}")
- min_all = torch.min(x)
- print(f"所有元素的最小值: {min_all}")
- # 获取最大值和最小值的位置
- max_val, max_idx = torch.max(x, dim=1)
- print(f"每行的最大值: {max_val}")
- print(f"每行最大值的位置: {max_idx}")
- min_val, min_idx = torch.min(x, dim=0)
- print(f"每列的最小值: {min_val}")
- print(f"每列最小值的位置: {min_idx}")
- # 其他聚合操作
- # 标准差
- std_all = torch.std(x)
- print(f"所有元素的标准差: {std_all}")
- # 方差
- var_all = torch.var(x)
- print(f"所有元素的方差: {var_all}")
- # 中位数
- median_all = torch.median(x)
- print(f"所有元素的中位数: {median_all}")
复制代码
8.2 排序操作
- # 创建示例矩阵
- x = torch.tensor([[3, 1, 4], [1, 5, 9], [2, 6, 5]], dtype=torch.float32)
- # 对所有元素排序
- sorted_all, indices_all = torch.sort(x.flatten())
- print(f"所有元素排序后的值: {sorted_all}")
- print(f"所有元素排序后的索引: {indices_all}")
- # 按行排序
- sorted_rows, indices_rows = torch.sort(x, dim=1)
- print(f"按行排序后的矩阵:\n{sorted_rows}")
- print(f"按行排序后的索引矩阵:\n{indices_rows}")
- # 按列排序
- sorted_cols, indices_cols = torch.sort(x, dim=0)
- print(f"按列排序后的矩阵:\n{sorted_cols}")
- print(f"按列排序后的索引矩阵:\n{indices_cols}")
- # 获取top-k元素
- # 获取每行最大的2个元素
- topk_values, topk_indices = torch.topk(x, k=2, dim=1)
- print(f"每行最大的2个元素的值:\n{topk_values}")
- print(f"每行最大的2个元素的索引:\n{topk_indices}")
- # 获取每列最小的2个元素
- bottomk_values, bottomk_indices = torch.topk(x, k=2, dim=0, largest=False)
- print(f"每列最小的2个元素的值:\n{bottomk_values}")
- print(f"每列最小的2个元素的索引:\n{bottomk_indices}")
复制代码
8.3 条件操作
- # 创建示例矩阵
- x = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=torch.float32)
- # 使用torch.where进行条件选择
- # 条件:大于5的元素保留,否则替换为0
- result = torch.where(x > 5, x, torch.zeros_like(x))
- print(f"大于5的元素保留,否则为0:\n{result}")
- # 条件:大于5的元素替换为10,小于等于5的元素替换为-10
- result2 = torch.where(x > 5, torch.ones_like(x) * 10, torch.ones_like(x) * -10)
- print(f"大于5的元素为10,否则为-10:\n{result2}")
- # 使用条件掩码
- mask = x > 5
- print(f"条件掩码 (x > 5):\n{mask}")
- # 使用掩码选择元素
- selected_values = x[mask]
- print(f"使用掩码选择的元素: {selected_values}")
- # 使用掩码修改元素
- x_clone = x.clone() # 克隆以避免修改原始张量
- x_clone[mask] = 0 # 将满足条件的元素设为0
- print(f"使用掩码修改后的矩阵:\n{x_clone}")
- # 使用torch.clamp将值限制在特定范围内
- clamped = torch.clamp(x, min=3, max=7)
- print(f"将值限制在[3, 7]范围内:\n{clamped}")
复制代码
9. 矩阵在深度学习中的应用
9.1 线性回归
- import torch
- import torch.nn as nn
- import numpy as np
- import matplotlib.pyplot as plt
- # 设置随机种子以确保可重复性
- torch.manual_seed(42)
- # 生成一些示例数据
- # y = 2x + 1 + noise
- X = torch.linspace(0, 10, 100).reshape(-1, 1)
- y = 2 * X + 1 + torch.randn(100, 1) * 0.5
- # 定义线性回归模型
- class LinearRegression(nn.Module):
- def __init__(self):
- super(LinearRegression, self).__init__()
- self.linear = nn.Linear(1, 1) # 输入和输出都是1维
-
- def forward(self, x):
- return self.linear(x)
- # 实例化模型
- model = LinearRegression()
- # 定义损失函数和优化器
- criterion = nn.MSELoss()
- optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
- # 训练模型
- num_epochs = 100
- for epoch in range(num_epochs):
- # 前向传播
- outputs = model(X)
- loss = criterion(outputs, y)
-
- # 反向传播和优化
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- if (epoch+1) % 10 == 0:
- print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
- # 获取模型参数
- [w, b] = model.parameters()
- print(f"学习到的权重: {w.item():.4f}, 学习到的偏置: {b.item():.4f}")
- # 绘制结果
- predicted = model(X).detach().numpy()
- plt.scatter(X.numpy(), y.numpy(), label='Original data')
- plt.plot(X.numpy(), predicted, 'r', label='Fitted line')
- plt.legend()
- plt.show()
复制代码
9.2 矩阵分解与推荐系统
- import torch
- import torch.nn as nn
- import numpy as np
- # 设置随机种子以确保可重复性
- torch.manual_seed(42)
- # 创建一个简单的用户-物品评分矩阵
- # 行表示用户,列表示物品,值表示评分(0表示未评分)
- ratings = torch.tensor([
- [5, 4, 1, 1, 0],
- [4, 5, 1, 1, 0],
- [1, 1, 5, 4, 0],
- [1, 1, 4, 5, 0],
- [0, 0, 0, 0, 5],
- [0, 0, 0, 0, 4]
- ], dtype=torch.float32)
- # 创建掩码,标记哪些位置是有评分的
- mask = ratings > 0
- # 定义矩阵分解模型
- class MatrixFactorization(nn.Module):
- def __init__(self, num_users, num_items, latent_dim):
- super(MatrixFactorization, self).__init__()
- # 用户潜因子矩阵
- self.user_factors = nn.Parameter(torch.randn(num_users, latent_dim) * 0.01)
- # 物品潜因子矩阵
- self.item_factors = nn.Parameter(torch.randn(num_items, latent_dim) * 0.01)
- # 用户偏置
- self.user_biases = nn.Parameter(torch.zeros(num_users))
- # 物品偏置
- self.item_biases = nn.Parameter(torch.zeros(num_items))
- # 全局偏置
- self.global_bias = nn.Parameter(torch.zeros(1))
-
- def forward(self):
- # 预测评分矩阵
- predictions = self.global_bias + self.user_biases.unsqueeze(1) + \
- self.item_biases.unsqueeze(0) + \
- torch.matmul(self.user_factors, self.item_factors.t())
- return predictions
- # 实例化模型
- num_users, num_items = ratings.shape
- latent_dim = 2 # 潜在因子维度
- model = MatrixFactorization(num_users, num_items, latent_dim)
- # 定义损失函数和优化器
- criterion = nn.MSELoss(reduction='sum')
- optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
- # 训练模型
- num_epochs = 1000
- for epoch in range(num_epochs):
- # 前向传播
- predictions = model()
-
- # 只计算有评分位置的损失
- loss = criterion(predictions[mask], ratings[mask])
-
- # 添加L2正则化
- l2_reg = 0.01 * (torch.sum(model.user_factors**2) + torch.sum(model.item_factors**2))
- loss += l2_reg
-
- # 反向传播和优化
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- if (epoch+1) % 100 == 0:
- print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
- # 获取预测评分
- predicted_ratings = model().detach()
- print("原始评分矩阵:")
- print(ratings)
- print("\n预测评分矩阵:")
- print(predicted_ratings)
- # 为每个用户推荐评分最高的物品
- _, top_items = torch.topk(predicted_ratings, k=2, dim=1)
- print("\n为每个用户推荐的2个物品(物品索引):")
- print(top_items)
复制代码
9.3 卷积神经网络中的矩阵操作
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import torchvision
- import torchvision.transforms as transforms
- import matplotlib.pyplot as plt
- # 设置随机种子以确保可重复性
- torch.manual_seed(42)
- # 加载MNIST数据集
- transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
- train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
- test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
- train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
- test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64, shuffle=False)
- # 定义简单的CNN模型
- class SimpleCNN(nn.Module):
- def __init__(self):
- super(SimpleCNN, self).__init__()
- # 第一个卷积层:输入1通道,输出32通道,卷积核大小3x3
- self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
- # 第二个卷积层:输入32通道,输出64通道,卷积核大小3x3
- self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
- # 全连接层
- self.fc1 = nn.Linear(64 * 7 * 7, 128)
- self.fc2 = nn.Linear(128, 10)
- # 池化层
- self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
- # Dropout层
- self.dropout = nn.Dropout(0.25)
-
- def forward(self, x):
- # 第一个卷积层和池化层
- x = F.relu(self.conv1(x))
- x = self.pool(x)
-
- # 第二个卷积层和池化层
- x = F.relu(self.conv2(x))
- x = self.pool(x)
-
- # 展平操作,将多维张量转换为一维向量
- x = x.view(-1, 64 * 7 * 7)
-
- # 全连接层
- x = F.relu(self.fc1(x))
- x = self.dropout(x)
- x = self.fc2(x)
-
- return x
- # 实例化模型
- model = SimpleCNN()
- print(model)
- # 定义损失函数和优化器
- criterion = nn.CrossEntropyLoss()
- optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
- # 训练模型
- num_epochs = 5
- for epoch in range(num_epochs):
- model.train()
- running_loss = 0.0
- for i, (images, labels) in enumerate(train_loader):
- # 前向传播
- outputs = model(images)
- loss = criterion(outputs, labels)
-
- # 反向传播和优化
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- running_loss += loss.item()
-
- if (i+1) % 100 == 0:
- print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {loss.item():.4f}')
- # 测试模型
- model.eval()
- with torch.no_grad():
- correct = 0
- total = 0
- for images, labels in test_loader:
- outputs = model(images)
- _, predicted = torch.max(outputs.data, 1)
- total += labels.size(0)
- correct += (predicted == labels).sum().item()
-
- print(f'测试集上的准确率: {100 * correct / total:.2f}%')
- # 可视化卷积核
- def visualize_conv_kernels(conv_layer, title):
- kernels = conv_layer.weight.data.cpu().numpy()
- n_kernels = kernels.shape[0]
-
- fig, axes = plt.subplots(1, min(n_kernels, 8), figsize=(12, 3))
- fig.suptitle(title)
-
- for i, ax in enumerate(axes.flat):
- if i < n_kernels:
- ax.imshow(kernels[i, 0], cmap='viridis')
- ax.set_xticks([])
- ax.set_yticks([])
-
- plt.tight_layout()
- plt.show()
- # 可视化第一个卷积层的卷积核
- visualize_conv_kernels(model.conv1, "第一个卷积层的卷积核")
- # 可视化第二个卷积层的部分卷积核
- visualize_conv_kernels(model.conv2, "第二个卷积层的部分卷积核")
复制代码
10. 性能优化技巧
10.1 GPU加速
- import torch
- import time
- # 检查CUDA是否可用
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
- print(f"使用设备: {device}")
- # 创建大型矩阵
- size = 5000
- x = torch.randn(size, size)
- y = torch.randn(size, size)
- # 在CPU上执行矩阵乘法
- start_time = time.time()
- z_cpu = torch.matmul(x, y)
- cpu_time = time.time() - start_time
- print(f"CPU上矩阵乘法耗时: {cpu_time:.4f}秒")
- # 将矩阵移动到GPU(如果可用)
- if torch.cuda.is_available():
- x_gpu = x.to(device)
- y_gpu = y.to(device)
-
- # 确保GPU上的操作完成
- torch.cuda.synchronize()
-
- # 在GPU上执行矩阵乘法
- start_time = time.time()
- z_gpu = torch.matmul(x_gpu, y_gpu)
- # 确保GPU上的操作完成
- torch.cuda.synchronize()
- gpu_time = time.time() - start_time
- print(f"GPU上矩阵乘法耗时: {gpu_time:.4f}秒")
- print(f"GPU加速比: {cpu_time / gpu_time:.2f}x")
复制代码
10.2 内存优化
- import torch
- # 检查CUDA是否可用
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
- # 创建大型矩阵
- size = 10000
- x = torch.randn(size, size, device=device)
- # 计算内存使用情况
- def get_memory_usage():
- if torch.cuda.is_available():
- allocated = torch.cuda.memory_allocated() / (1024 ** 2) # MB
- cached = torch.cuda.memory_reserved() / (1024 ** 2) # MB
- return allocated, cached
- return 0, 0
- # 显示内存使用情况
- allocated, cached = get_memory_usage()
- print(f"初始内存使用 - 已分配: {allocated:.2f} MB, 已缓存: {cached:.2f} MB")
- # 使用in-place操作减少内存使用
- y = x.clone() # 创建副本
- # 非in-place操作
- start_time = time.time()
- z = y * 2 # 创建新的张量
- allocated, cached = get_memory_usage()
- print(f"非in-place操作后内存使用 - 已分配: {allocated:.2f} MB, 已缓存: {cached:.2f} MB")
- # 清理内存
- del z
- torch.cuda.empty_cache() if torch.cuda.is_available() else None
- # in-place操作
- start_time = time.time()
- y.mul_(2) # in-place操作,不创建新的张量
- allocated, cached = get_memory_usage()
- print(f"in-place操作后内存使用 - 已分配: {allocated:.2f} MB, 已缓存: {cached:.2f} MB")
- # 使用pin_memory加速CPU到GPU的传输
- if torch.cuda.is_available():
- # 创建使用pin_memory的张量
- x_pinned = torch.randn(size, size, pin_memory=True)
-
- # 测试传输速度
- start_time = time.time()
- x_gpu = x_pinned.to(device)
- torch.cuda.synchronize()
- pinned_time = time.time() - start_time
-
- # 创建不使用pin_memory的张量
- x_regular = torch.randn(size, size)
-
- # 测试传输速度
- start_time = time.time()
- x_gpu = x_regular.to(device)
- torch.cuda.synchronize()
- regular_time = time.time() - start_time
-
- print(f"使用pin_memory的传输时间: {pinned_time:.4f}秒")
- print(f"不使用pin_memory的传输时间: {regular_time:.4f}秒")
- print(f"pin_memory加速比: {regular_time / pinned_time:.2f}x")
复制代码
10.3 批处理操作
- import torch
- import time
- # 创建多个矩阵
- num_matrices = 100
- matrix_size = 100
- matrices = [torch.randn(matrix_size, matrix_size) for _ in range(num_matrices)]
- # 方法1:循环处理每个矩阵
- start_time = time.time()
- results_loop = []
- for matrix in matrices:
- result = torch.matmul(matrix, matrix.t())
- results_loop.append(result)
- loop_time = time.time() - start_time
- print(f"循环处理时间: {loop_time:.4f}秒")
- # 方法2:批处理
- start_time = time.time()
- # 将矩阵堆叠成一个3D张量
- batched_matrices = torch.stack(matrices)
- # 批量矩阵乘法
- batched_results = torch.matmul(batched_matrices, batched_matrices.transpose(1, 2))
- batched_time = time.time() - start_time
- print(f"批处理时间: {batched_time:.4f}秒")
- print(f"批处理加速比: {loop_time / batched_time:.2f}x")
- # 验证结果是否相同
- # 将批处理结果分割成单独的矩阵
- split_results = torch.split(batched_results, 1, dim=0)
- # 移除额外的维度
- split_results = [result.squeeze(0) for result in split_results]
- # 检查每个结果是否相同
- all_close = all(torch.allclose(loop_result, batch_result)
- for loop_result, batch_result in zip(results_loop, split_results))
- print(f"两种方法的结果是否相同: {all_close}")
复制代码
11. 实际案例与代码示例
11.1 图像处理中的矩阵操作
- import torch
- import torchvision
- import torchvision.transforms as transforms
- import matplotlib.pyplot as plt
- import numpy as np
- # 加载示例图像
- transform = transforms.Compose([transforms.ToTensor()])
- dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
- image, label = dataset[0] # 获取第一张图像
- # 显示原始图像
- plt.figure(figsize=(12, 8))
- plt.subplot(2, 3, 1)
- plt.imshow(np.transpose(image.numpy(), (1, 2, 0)))
- plt.title("原始图像")
- plt.axis('off')
- # 转换为灰度图像
- # 使用加权平均将RGB转换为灰度
- gray_weights = torch.tensor([0.2989, 0.5870, 0.1140]).view(3, 1, 1)
- gray_image = torch.sum(image * gray_weights, dim=0, keepdim=True)
- plt.subplot(2, 3, 2)
- plt.imshow(gray_image.squeeze().numpy(), cmap='gray')
- plt.title("灰度图像")
- plt.axis('off')
- # 图像旋转
- # 使用旋转矩阵
- angle = 45 # 旋转角度
- theta = torch.tensor(np.radians(angle), dtype=torch.float32)
- rotation_matrix = torch.tensor([
- [torch.cos(theta), -torch.sin(theta)],
- [torch.sin(theta), torch.cos(theta)]
- ])
- # 创建网格
- grid_y, grid_x = torch.meshgrid(
- torch.linspace(-1, 1, gray_image.shape[1]),
- torch.linspace(-1, 1, gray_image.shape[0])
- )
- # 将网格点转换为齐次坐标
- grid = torch.stack([grid_x.flatten(), grid_y.flatten(), torch.ones_like(grid_x.flatten())])
- # 应用旋转
- rotation_matrix_homogeneous = torch.eye(3)
- rotation_matrix_homogeneous[:2, :2] = rotation_matrix
- rotated_grid = torch.matmul(rotation_matrix_homogeneous, grid)
- # 将旋转后的网格点转换回图像坐标
- rotated_grid = rotated_grid[:2, :].view(2, gray_image.shape[0], gray_image.shape[1])
- # 采样旋转后的图像
- rotated_image = torch.nn.functional.grid_sample(
- gray_image.unsqueeze(0),
- rotated_grid.unsqueeze(0).permute(0, 2, 3, 1),
- mode='bilinear',
- padding_mode='zeros'
- )
- plt.subplot(2, 3, 3)
- plt.imshow(rotated_image.squeeze().numpy(), cmap='gray')
- plt.title("旋转45度后的图像")
- plt.axis('off')
- # 图像翻转
- flipped_h = torch.flip(image, dims=[2]) # 水平翻转
- flipped_v = torch.flip(image, dims=[1]) # 垂直翻转
- plt.subplot(2, 3, 4)
- plt.imshow(np.transpose(flipped_h.numpy(), (1, 2, 0)))
- plt.title("水平翻转")
- plt.axis('off')
- plt.subplot(2, 3, 5)
- plt.imshow(np.transpose(flipped_v.numpy(), (1, 2, 0)))
- plt.title("垂直翻转")
- plt.axis('off')
- # 图像缩放
- scaled_image = torch.nn.functional.interpolate(
- image.unsqueeze(0),
- scale_factor=0.5,
- mode='bilinear',
- align_corners=False
- )
- plt.subplot(2, 3, 6)
- plt.imshow(np.transpose(scaled_image.squeeze().numpy(), (1, 2, 0)))
- plt.title("缩小到50%")
- plt.axis('off')
- plt.tight_layout()
- plt.show()
复制代码
11.2 自然语言处理中的矩阵操作
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- # 创建简单的词嵌入矩阵
- vocab_size = 10000 # 词汇表大小
- embedding_dim = 300 # 词嵌入维度
- # 随机初始化词嵌入矩阵
- embedding_matrix = torch.randn(vocab_size, embedding_dim)
- # 定义一些示例句子和对应的词索引
- sentences = [
- "the quick brown fox jumps over the lazy dog",
- "i love deep learning and pytorch",
- "matrix operations are fundamental in deep learning"
- ]
- # 创建简单的词汇表
- word_to_idx = {}
- for sentence in sentences:
- for word in sentence.split():
- if word not in word_to_idx:
- word_to_idx[word] = len(word_to_idx)
- # 将句子转换为词索引
- sentence_indices = []
- for sentence in sentences:
- indices = [word_to_idx[word] for word in sentence.split()]
- sentence_indices.append(indices)
- print("句子和对应的词索引:")
- for sentence, indices in zip(sentences, sentence_indices):
- print(f"{sentence}: {indices}")
- # 使用词嵌入矩阵获取词向量
- def get_word_vectors(indices, embedding_matrix):
- return embedding_matrix[indices]
- # 获取每个句子的词向量
- sentence_vectors = [get_word_vectors(indices, embedding_matrix) for indices in sentence_indices]
- # 计算句子向量(通过平均词向量)
- def sentence_embedding(word_vectors):
- return torch.mean(word_vectors, dim=0)
- sentence_embeddings = [sentence_embedding(vectors) for vectors in sentence_vectors]
- print("\n句子向量形状:")
- for i, embedding in enumerate(sentence_embeddings):
- print(f"句子 {i+1}: {embedding.shape}")
- # 计算句子之间的余弦相似度
- def cosine_similarity(vec1, vec2):
- dot_product = torch.dot(vec1, vec2)
- norm_vec1 = torch.norm(vec1)
- norm_vec2 = torch.norm(vec2)
- return dot_product / (norm_vec1 * norm_vec2)
- print("\n句子之间的余弦相似度:")
- for i in range(len(sentence_embeddings)):
- for j in range(i+1, len(sentence_embeddings)):
- similarity = cosine_similarity(sentence_embeddings[i], sentence_embeddings[j])
- print(f"句子 {i+1} 和句子 {j+1}: {similarity:.4f}")
- # 注意力机制示例
- class SimpleAttention(nn.Module):
- def __init__(self, embedding_dim):
- super(SimpleAttention, self).__init__()
- self.attention_weights = nn.Parameter(torch.randn(embedding_dim))
-
- def forward(self, word_vectors):
- # 计算注意力分数
- scores = torch.matmul(word_vectors, self.attention_weights)
- # 应用softmax获取注意力权重
- attention_weights = F.softmax(scores, dim=0)
- # 计算加权和
- attended_embedding = torch.sum(word_vectors * attention_weights.unsqueeze(1), dim=0)
- return attended_embedding, attention_weights
- # 创建注意力模型
- attention_model = SimpleAttention(embedding_dim)
- # 应用注意力机制
- print("\n注意力机制示例:")
- for i, vectors in enumerate(sentence_vectors):
- attended_embedding, attention_weights = attention_model(vectors)
- print(f"\n句子 {i+1}:")
- print("词:", [sentences[i].split()[j] for j in range(len(sentence_indices[i]))])
- print("注意力权重:", attention_weights.detach().numpy())
- print("注意力权重最大的词:", sentences[i].split()[torch.argmax(attention_weights).item()])
复制代码
11.3 图神经网络中的矩阵操作
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import networkx as nx
- import matplotlib.pyplot as plt
- # 创建一个简单的图
- G = nx.karate_club_graph()
- # 获取邻接矩阵
- adjacency_matrix = nx.adjacency_matrix(G).todense()
- adjacency_matrix = torch.tensor(adjacency_matrix, dtype=torch.float32)
- # 添加自环
- adjacency_matrix += torch.eye(adjacency_matrix.shape[0])
- # 计算度矩阵
- degree_matrix = torch.diag(torch.sum(adjacency_matrix, dim=1))
- # 计算归一化的邻接矩阵
- degree_matrix_inv_sqrt = torch.inverse(torch.sqrt(degree_matrix))
- normalized_adjacency = torch.matmul(torch.matmul(degree_matrix_inv_sqrt, adjacency_matrix), degree_matrix_inv_sqrt)
- # 可视化图
- plt.figure(figsize=(10, 8))
- pos = nx.spring_layout(G)
- nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10)
- plt.title("空手道俱乐部图")
- plt.show()
- # 定义简单的GCN层
- class GCNLayer(nn.Module):
- def __init__(self, input_dim, output_dim):
- super(GCNLayer, self).__init__()
- self.linear = nn.Linear(input_dim, output_dim)
-
- def forward(self, x, adjacency):
- # x: 节点特征矩阵,形状为[num_nodes, input_dim]
- # adjacency: 归一化的邻接矩阵,形状为[num_nodes, num_nodes]
- x = self.linear(x)
- x = torch.matmul(adjacency, x)
- return F.relu(x)
- # 定义简单的GCN模型
- class GCN(nn.Module):
- def __init__(self, input_dim, hidden_dim, output_dim):
- super(GCN, self).__init__()
- self.gcn1 = GCNLayer(input_dim, hidden_dim)
- self.gcn2 = GCNLayer(hidden_dim, output_dim)
-
- def forward(self, x, adjacency):
- x = self.gcn1(x, adjacency)
- x = self.gcn2(x, adjacency)
- return x
- # 创建随机节点特征
- num_nodes = adjacency_matrix.shape[0]
- input_dim = 34 # 初始特征维度
- hidden_dim = 16
- output_dim = 2 # 最终嵌入维度,用于可视化
- # 初始化节点特征为单位矩阵
- node_features = torch.eye(num_nodes)
- # 实例化模型
- model = GCN(input_dim, hidden_dim, output_dim)
- # 前向传播
- node_embeddings = model(node_features, normalized_adjacency)
- # 可视化节点嵌入
- plt.figure(figsize=(10, 8))
- embeddings = node_embeddings.detach().numpy()
- plt.scatter(embeddings[:, 0], embeddings[:, 1], c='lightblue', s=100)
- # 添加节点标签
- for i, (x, y) in enumerate(embeddings):
- plt.text(x, y, str(i), fontsize=8, ha='center', va='center')
- plt.title("GCN学习到的节点嵌入")
- plt.xlabel("维度 1")
- plt.ylabel("维度 2")
- plt.grid(True)
- plt.show()
- # 图池化操作示例
- class GraphPooling(nn.Module):
- def __init__(self, pooling_ratio=0.5):
- super(GraphPooling, self).__init__()
- self.pooling_ratio = pooling_ratio
-
- def forward(self, x, adjacency):
- # x: 节点特征矩阵,形状为[num_nodes, feature_dim]
- # adjacency: 邻接矩阵,形状为[num_nodes, num_nodes]
-
- # 计算每个节点的投影分数
- scores = torch.sum(x, dim=1)
-
- # 根据分数选择要保留的节点
- num_nodes = x.shape[0]
- num_keep = int(num_nodes * self.pooling_ratio)
- _, top_indices = torch.topk(scores, num_keep)
-
- # 对节点特征和邻接矩阵进行池化
- pooled_x = x[top_indices]
- pooled_adjacency = adjacency[top_indices][:, top_indices]
-
- return pooled_x, pooled_adjacency, top_indices
- # 应用图池化
- pooling_layer = GraphPooling(pooling_ratio=0.5)
- pooled_features, pooled_adjacency, pooled_indices = pooling_layer(node_embeddings, normalized_adjacency)
- print(f"原始节点数: {num_nodes}")
- print(f"池化后节点数: {pooled_features.shape[0]}")
- print(f"保留的节点索引: {pooled_indices.tolist()}")
- # 可视化池化后的图
- pooled_G = G.subgraph(pooled_indices.numpy())
- plt.figure(figsize=(10, 8))
- pos = nx.spring_layout(pooled_G)
- nx.draw(pooled_G, pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10)
- plt.title("池化后的图")
- plt.show()
复制代码
12. 总结
本指南全面介绍了PyTorch中的矩阵操作,从基础概念到高级应用,涵盖了以下主要内容:
1. PyTorch矩阵基础:介绍了张量的概念和基本属性,为后续操作打下基础。
2. 矩阵创建与初始化:详细讲解了如何创建各种类型的矩阵,包括全零矩阵、全一矩阵、随机矩阵等。
3. 矩阵基本操作:包括索引、切片、修改元素、拼接和分割等基本操作。
4. 矩阵运算:涵盖了算术运算、矩阵乘法和其他常用矩阵运算,如转置、求逆、特征值分解等。
5. 矩阵变形与重塑:介绍了如何改变矩阵的形状,包括view、reshape、transpose等操作。
6. 广播机制:详细解释了PyTorch中的广播规则和应用,使不同形状的张量能够进行运算。
7. 高级矩阵操作:包括聚合操作、排序操作和条件操作等。
8. 矩阵在深度学习中的应用:通过线性回归、矩阵分解与推荐系统、卷积神经网络等示例,展示了矩阵操作在实际深度学习应用中的重要性。
9. 性能优化技巧:介绍了GPU加速、内存优化和批处理操作等技巧,帮助提高代码效率。
10. 实际案例与代码示例:通过图像处理、自然语言处理和图神经网络中的矩阵操作示例,展示了矩阵操作在不同领域的应用。
PyTorch矩阵基础:介绍了张量的概念和基本属性,为后续操作打下基础。
矩阵创建与初始化:详细讲解了如何创建各种类型的矩阵,包括全零矩阵、全一矩阵、随机矩阵等。
矩阵基本操作:包括索引、切片、修改元素、拼接和分割等基本操作。
矩阵运算:涵盖了算术运算、矩阵乘法和其他常用矩阵运算,如转置、求逆、特征值分解等。
矩阵变形与重塑:介绍了如何改变矩阵的形状,包括view、reshape、transpose等操作。
广播机制:详细解释了PyTorch中的广播规则和应用,使不同形状的张量能够进行运算。
高级矩阵操作:包括聚合操作、排序操作和条件操作等。
矩阵在深度学习中的应用:通过线性回归、矩阵分解与推荐系统、卷积神经网络等示例,展示了矩阵操作在实际深度学习应用中的重要性。
性能优化技巧:介绍了GPU加速、内存优化和批处理操作等技巧,帮助提高代码效率。
实际案例与代码示例:通过图像处理、自然语言处理和图神经网络中的矩阵操作示例,展示了矩阵操作在不同领域的应用。
通过本指南的学习,读者应该能够全面掌握PyTorch中的矩阵操作,并能够将这些知识应用到实际的深度学习项目中。矩阵操作是深度学习的基础,熟练掌握这些操作对于构建高效、准确的深度学习模型至关重要。
希望本指南能够帮助读者在PyTorch和深度学习的道路上取得更大的进步! |
|