欢迎来到《深度学习保姆教程》系列的第九篇!在前面的几篇中,我们已经介绍了Python、numpy及pytorch的基本使用,进行了梯度及神经网络的实践并学习了激活函数和激活函数,在上一个教程中我们学习了优化算法。今天,我们将开始使用pytorch构建我们自己的神经网络。
欢迎订阅专栏进行系统学习:
目录
PyTorch 的 nn 模块提供了构建神经网络的高级接口。它封装了层、损失函数和优化算法,使得构建和训练复杂模型变得更加容易。
1.理解nn模块:
nn 模块提供了一系列类和函数,用于构建各种神经网络组件:
- 层(Layers):定义对输入数据执行的计算操作(例如,Linear、Conv2d、RNN)。
- 损失函数(Loss functions):量化预测输出和实际输出之间的差异(例如,CrossEntropyLoss、MSELoss)。
- 容器(Containers):将层组织成顺序、并行或更复杂的结构(例如,Sequential、ModuleList、ModuleDict)。
- 初始化(Initialization):初始化模型参数(例如,kaiming_normal_、xavier_uniform_)。
(1)使用 nn.Sequential 创建神经网络
nn.Sequential 容器是一种创建前馈神经网络的简单方法。它定义了一个模块的线性堆栈。
import torch
import torch.nn as nn
# 定义一个简单的神经网络
model = nn.Sequential(
nn.Linear(input_size, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, output_size)
)
(2)自定义神经网络
虽然 nn.Sequential 很方便,但您可以通过继承 nn.Module 来创建更复杂的架构。这允许您自定义前向传播逻辑,并对模型结构拥有更多控制权。
import torch
import torch.nn as nn
# 定义一个名为MyModel的神经网络模型类,继承自nn.Module
class MyModel(nn.Module):
# 构造函数,初始化模型参数
def __init__(self, input_size, hidden_size, output_size):
# 调用父类nn.Module的构造函数进行初始化
super(MyModel, self).__init__()
# 定义第一个全连接层(线性层),输入大小为input_size,输出大小为hidden_size
self.fc1 = nn.Linear(input_size, hidden_size)
# 定义第二个全连接层(线性层),输入大小为hidden_size,输出大小为output_size
self.fc2 = nn.Linear(hidden_size, output_size)
# 前向传播函数,定义数据如何通过网络
def forward(self, x):
# 对第一个全连接层的输出应用ReLU激活函数
x = torch.relu(self.fc1(x))
# 将经过激活后的输出传递给第二个全连接层
x = self.fc2(x)
# 返回最终的输出
return x
# 实例化MyModel模型,传入输入层、隐藏层和输出层的大小
model = MyModel(input_size, hidden_size, output_size)
2 创建神经网络层
神经网络层是处理信息的基本组件。它们对输入数据执行计算,将其转换为适合后续层的表示形式。让我们探索一些常见的层类型。
(1)线性层(全连接层)
线性层,也称为全连接层,将一层中的每个神经元连接到下一层中的每个神经元。它们执行矩阵乘法后加上偏置。
import torch.nn as nn
linear_layer = nn.Linear(in_features=10, out_features=20)
(2)卷积层
卷积层对于处理网格状数据(如图像)至关重要。它们应用滤波器来提取特征。
import torch.nn as nn
conv_layer = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
(3)池化层
池化层降低输入的维度,同时保留重要信息。
import torch.nn as nn
max_pool = nn.MaxPool2d(kernel_size=2, stride=2)
avg_pool = nn.AvgPool2d(kernel_size=2, stride=2)
(4)循环层
循环层处理序列数据。它们维护内部状态以捕捉来自先前步骤的信息。
import torch.nn as nn
rnn = nn.RNN(input_size=10, hidden_size=20, num_layers=1)
lstm = nn.LSTM(input_size=10, hidden_size=20, num_layers=1)
gru = nn.GRU(input_size=10, hidden_size=20, num_layers=1)
(5)其他层类型
- 归一化层(Normalization layers):归一化输入数据(例如,BatchNorm、LayerNorm)。
- Dropout 层(Dropout layers):通过随机丢弃神经元来防止过拟合。
- 嵌入层(Embedding layers):将分类数据转换为密集向量。
(6)将层组合成神经网络
多个层组合在一起可以创建复杂的架构。例如,卷积神经网络通常由卷积层、池化层和全连接层组成。
import torch.nn as nn
# 定义一个名为MyCNN的神经网络模型类,继承自nn.Module
class MyCNN(nn.Module):
def __init__(self):
super(MyCNN, self).__init__()
# 定义第一个卷积层
self.conv1 = nn.Conv2d(3, 16, 3, padding=1)
# 定义最大池化层
self.pool = nn.MaxPool2d(2, 2)
# 定义第一个全连接层
self.fc1 = nn.Linear(16 * 7 * 7, 120)
# 定义第二个全连接层
self.fc2 = nn.Linear(120, 84)
# 定义第三个全连接层
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# 应用卷积、ReLU激活函数和池化
x = self.pool(torch.relu(self.conv1(x)))
# 将特征图展平成一维向量
x = torch.flatten(x, 1)
# 应用第一个全连接层和ReLU激活函数
x = torch.relu(self.fc1(x))
# 应用第二个全连接层和ReLU激活函数
x = torch.relu(self.fc2(x))
# 应用第三个全连接层
x = self.fc3(x)
return x
关键考虑因素
- 层的深度和宽度(Layer depth and width):尝试不同数量的层和神经元。
- 超参数调整(Hyperparameter tuning):优化诸如核大小、步幅和填充等参数。
- 计算效率(Computational efficiency):考虑不同层的计算成本。
3 构建顺序模型
顺序模型旨在处理具有时间或空间顺序的数据,例如时间序列数据、文本和音频。它们捕捉序列中元素之间的依赖关系。
理解顺序数据
- 时间序列数据(Time series data):在特定时间点记录的观测值。
- 文本数据(Text data):单词或字符的序列。
- 音频数据(Audio data):音频样本的序列。
(1)循环神经网络(RNNs)
RNNs 是处理顺序数据的基础架构。它们在网络中引入循环,允许信息跨时间步长持续存在。
- 隐藏状态(Hidden state):维护关于过去输入的信息。
- 梯度消失问题(Vanishing gradient problem):难以学习长期依赖关系。
import torch.nn as nn
# 简单的 RNN
rnn = nn.RNN(input_size=10, hidden_size=20, num_layers=1)
(2)长短期记忆网络(LSTMs)
LSTMs 通过引入记忆单元和门来解决梯度消失问题。
- 记忆单元(Memory cell):存储长时间的信息。
- 门(Gates):控制信息流入和流出记忆单元的流动。
import torch.nn as nn
lstm = nn.LSTM(input_size=10, hidden_size=20, num_layers=1)
(3)门控循环单元(GRUs)
GRUs 是 LSTMs 的简化版本,参数更少。
- 更新门(Update gate):控制保留多少之前的隐藏状态。
- 重置门(Reset gate):控制忘记多少过去的信息。
import torch.nn as nn
gru = nn.GRU(input_size=10, hidden_size=20, num_layers=1)
挑战和注意事项
- 梯度消失/爆炸(Vanishing/exploding gradients):可能阻碍训练,特别是对于长序列。
- 计算成本(Computational cost):RNNs 训练起来可能计算成本较高。
- 过拟合(Overfitting):防止模型记住训练数据。
4 自定义神经网络模块
虽然 PyTorch 的 nn 模块提供了一套丰富的预建层,但在某些情况下,需要创建针对特定问题域或架构创新的自定义组件。
(1)理解自定义模块的需求
- 特定问题的操作(Problem-specific operations):某些任务可能需要标准层未涵盖的操作。
- 架构实验(Architectural experimentation):自定义模块允许灵活地探索新架构。
- 性能优化(Performance optimization):手工实现有时可能更高效。
创建自定义模块
要创建自定义模块,您需要继承 torch.nn.Module
并实现 forward
方法。该方法定义了对输入张量执行的计算。
import torch
import torch.nn as nn
# 定义一个名为MyCustomLayer的自定义神经网络层类,继承自nn.Module
class MyCustomLayer(nn.Module):
def __init__(self, in_features, out_features):
super().__init__()
# 初始化权重参数,使用标准正态分布随机初始化
self.weight = nn.Parameter(torch.randn(in_features, out_features))
# 初始化偏置参数,使用零初始化
self.bias = nn.Parameter(torch.zeros(out_features))
def forward(self, x):
# 执行线性变换:x @ weight + bias
return x @ self.weight + self.bias
将自定义模块纳入神经网络
一旦定义了自定义模块,您就可以像使用其他层一样在神经网络架构中使用它。
model = nn.Sequential(
MyCustomLayer(10, 20),
nn.ReLU(),
nn.Linear(20, 5)
)
高级自定义
- 参数共享(Parameter sharing):创建在不同层之间共享参数的模块。
- 动态架构(Dynamic architectures):根据输入数据构建具有可变结构的模型。
- 混合模型(Hybrid models):结合自定义模块与预训练层。
总结
通过掌握自定义模块的创建,您可以解锁设计高度专业化和创新的神经网络架构的潜力。nn 模块是一个强大的工具,但将其与对神经网络概念的理解相结合,才能构建有效的模型。