10 从零到上手系统学习 PyTorch - 构建简单的神经网络

10 从零到上手系统学习 PyTorch - 构建简单的神经网络

在这一小节中,我们将详细介绍如何使用 PyTorch 构建一个简单的神经网络。我们将分步骤进行,包括数据准备、模型定义、训练及评估等部分。

1. 数据准备

在构建神经网络之前,首先需要准备训练和测试数据。我们将使用 torchvision 库中的 MNIST 数据集,这是一个手写数字识别的数据集。

1.1 导入必要的库

1
2
3
4
5
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

1.2 下载和加载数据集

我们将使用 torchvision.datasets 中的 MNIST 数据集,并应用一些基本的转换,比如将图像转换为张量并进行归一化处理。

1
2
3
4
5
6
7
8
9
10
11
12
# 定义数据转换
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.5,), (0.5,)) # 归一化处理
])

# 下载训练集和测试集
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False)

2. 定义神经网络

接下来,我们将定义一个简单的全连接神经网络。我们将创建一个子类 Net 继承自 nn.Module

2.1 创建神经网络模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(28 * 28, 128) # 输入层到隐藏层
self.fc2 = nn.Linear(128, 64) # 隐藏层到隐藏层
self.fc3 = nn.Linear(64, 10) # 隐藏层到输出层
self.relu = nn.ReLU() # 激活函数

def forward(self, x):
x = x.view(-1, 28 * 28) # 将28x28的图像展平成784维的向量
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
x = self.relu(x)
x = self.fc3(x)
return x

3. 定义损失函数和优化器

选择一个适当的损失函数和优化器对于训练模型是至关重要的。对于多类别分类问题,我们通常使用 CrossEntropyLoss 作为损失函数,使用 Adam 作为优化器。

1
2
3
4
# 实例化模型、损失函数和优化器
model = Net()
criterion = nn.CrossEntropyLoss() # 损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001) # 优化器

4. 训练模型

现在,我们可以开始训练模型。我们将迭代多个 epoch,每个 epoch 中使用训练数据进行模型的更新。

4.1 训练过程

1
2
3
4
5
6
7
8
9
10
11
num_epochs = 5  # 训练轮数

for epoch in range(num_epochs):
for inputs, labels in trainloader:
optimizer.zero_grad() # 清空之前的梯度
outputs = model(inputs) # 前向传播
loss = criterion(outputs, labels) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新权重

print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

5. 评估模型

训练完成后,我们需要评估模型在测试集上的效果。我们将计算模型的准确率。

5.1 测试过程

1
2
3
4
5
6
7
8
9
10
11
correct = 0
total = 0

with torch.no_grad(): # 不需要计算梯度
for inputs, labels in testloader:
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1) # 获取预测结果
total += labels.size(0) # 更新总样本数
correct += (predicted == labels).sum().item() # 统计预测正确的样本数

print(f'Accuracy: {100 * correct / total:.2f}%')

总结

通过上述步骤,我们成功实现了一个简单的神经网络,并对其进行训练和评估。在这个过程中,我们使用了 PyTorch 的基本功能,包括数据加载、模型构建、损失计算和梯度更新等。接下来,您可以尝试调整网络结构、优化器或者其他超参数,进一步提高模型的性能。

11 从零到上手 PyTorch 的系统学习教程 - 使用 `nn.Module`

11 从零到上手 PyTorch 的系统学习教程 - 使用 `nn.Module`

在 PyTorch 中,nn.Module 是构建神经网络的核心类。它提供了一种简单的方式来定义和组织模型的各个组成部分,包括层、参数和前向传播的计算。

1. 什么是 nn.Module

nn.Module 是 PyTorch 的一个基类,所有的神经网络模型都可以从它继承。通过扩展此类,我们可以创建自定义的神经网络层。

1.1 nn.Module 的基本结构

一个 nn.Module 通常包含以下几个部分:

  • **构造函数 (__init__)**:在这里定义子模块和参数。
  • **前向传播函数 (forward)**:定义输入如何通过模型进行处理。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch
import torch.nn as nn

class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.layer1 = nn.Linear(10, 5) # 输入维度为10,输出维度为5
self.layer2 = nn.ReLU() # ReLU 激活函数

def forward(self, x):
x = self.layer1(x) # 通过第一层
x = self.layer2(x) # 通过激活函数
return x

在这个例子中,我们定义了一个名为 MyModel 的模型,它包含一个线性层和一个 ReLU 激活函数。

2. 使用 nn.Module 定义复杂模型

我们不仅可以定义简单的模型,还可以通过在 __init__ 方法中添加多个层来构建更复杂的模型。

2.1 例子:创建一个简单的全连接神经网络

1
2
3
4
5
6
7
8
9
10
11
12
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.fc1 = nn.Linear(784, 128) # 第一层:输入784(28x28)、输出128
self.fc2 = nn.Linear(128, 10) # 第二层:输入128,输出10(分类数)
self.relu = nn.ReLU() # ReLU 激活函数

def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x

2.2 使用模型

我们可以创建模型实例,并将数据输入模型进行前向传播。

1
2
3
4
5
6
7
8
9
# 创建模型实例
model = SimpleNN()

# 假设我们有一个输入为 (32, 784) 的批次,代表32个样本
input_tensor = torch.randn(32, 784)

# 进行前向传播
output = model(input_tensor)
print(output.shape) # 输出将是 (32, 10),代表32个样本的分类输出

3. 自定义层

有时我们需要定义自定义层来实现特定功能。我们可以继承 nn.Module,并在构造函数中定义层。

示例:自定义激活函数

1
2
3
class CustomActivation(nn.Module):
def forward(self, x):
return torch.maximum(x, torch.tensor(0.0)) # 仅返回非负值

将其集成到模型中时就像使用其他层一样:

1
2
3
4
5
6
7
8
9
10
class ModelWithCustomActivation(nn.Module):
def __init__(self):
super(ModelWithCustomActivation, self).__init__()
self.fc = nn.Linear(10, 5)
self.custom_activation = CustomActivation()

def forward(self, x):
x = self.fc(x)
x = self.custom_activation(x)
return x

4. 模型的训练与评估

在训练阶段,我们通常会结合优化器和损失函数来更新模型的参数。

4.1 示例:训练模型

1
2
3
4
5
6
7
8
9
10
11
# 假设我们使用的是交叉熵损失与随机梯度下降优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 假设我们有训练数据 train_loader
for inputs, labels in train_loader:
optimizer.zero_grad() # 清零之前的梯度
outputs = model(inputs) # 前向传播
loss = criterion(outputs, labels) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数

4.2 评估模型性能

在评估模型时,我们通常需要计算准确度或其他指标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 评估模型
model.eval() # 切换到评估模式
correct = 0
total = 0

with torch.no_grad(): # 关闭梯度计算以减少内存消耗
for inputs, labels in test_loader:
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1) # 获取最大概率的索引
total += labels.size(0)
correct += (predicted == labels).sum().item()

accuracy = correct / total
print(f'Accuracy: {accuracy * 100:.2f}%')

结论

使用 nn.Module 可以轻松地构建和管理复杂的神经网络模型。通过定义 __init__forward 方法,我们能够创建可复用的组件,并便于进行训练和评估。随着对 PyTorch 的深入了解,你将能构建更加复杂的模型,进一步提升你的深度学习能力。

12 从零到上手系统学习 PyTorch

12 从零到上手系统学习 PyTorch

1. 激活函数

激活函数是神经网络中用于引入非线性变换的重要组成部分。通过激活函数,神经网络能够学习到复杂的函数映射。

1.1 常见的激活函数

1.1.1 Sigmoid 函数

Sigmoid 函数是最早使用的激活函数之一,其公式为:

$$
\sigma(x) = \frac{1}{1 + e^{-x}}
$$

  • 特性

    • 输出范围为 (0, 1)。
    • 适合于二分类任务的输出层。
  • 缺点

    • 梯度消失问题。
1
2
3
4
5
6
7
import torch
import torch.nn as nn

sigmoid = nn.Sigmoid()
input_tensor = torch.tensor([-1.0, 0.0, 1.0])
output = sigmoid(input_tensor)
print(output) # 输出: tensor([0.2689, 0.5000, 0.7311])

1.1.2 ReLU 函数

ReLU(Rectified Linear Unit)是目前使用最广泛的激活函数,其公式为:

$$
f(x) = \max(0, x)
$$

  • 特性

    • 非常简单,计算效率高。
    • 在正区间梯度恒为 1。
  • 缺点

    • 死亡的 ReLU 的问题:当输入为负时,输出恒为 0。
1
2
3
4
relu = nn.ReLU()
input_tensor = torch.tensor([-1.0, 0.0, 1.0])
output = relu(input_tensor)
print(output) # 输出: tensor([0., 0., 1.])

1.1.3 Tanh 函数

Tanh (双曲正切)是另一种常用的激活函数,其公式为:

$$
\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}
$$

  • 特性

    • 输出范围为 (-1, 1)。
    • Sigmoid 函数更优,输出均值为 0。
  • 缺点

    • 仍然存在梯度消失问题。
1
2
3
4
tanh = nn.Tanh()
input_tensor = torch.tensor([-1.0, 0.0, 1.0])
output = tanh(input_tensor)
print(output) # 输出: tensor([-0.7616, 0.0000, 0.7616])

1.2 自定义激活函数

在某些情况下,我们可能希望使用自定义的激活函数。可以通过继承 nn.Module 来实现。

1
2
3
4
5
6
7
8
class CustomActivation(nn.Module):
def forward(self, input):
return input * torch.sigmoid(input) # 自定义激活

custom_activation = CustomActivation()
input_tensor = torch.tensor([-1.0, 0.0, 1.0])
output = custom_activation(input_tensor)
print(output) # 输出: tensor([-0.2689, 0.0000, 0.7311])

2. 损失函数

损失函数用于衡量模型的输出与真实标签之间的差距。通过最小化损失函数,我们可以优化模型的权重。

2.1 常见的损失函数

2.1.1 交叉熵损失

对于分类问题,CrossEntropyLoss 函数通常用于衡量分类的准确性。其公式为:

$$
L = -\sum_{i=1}^{C} y_i \log(p_i)
$$

  • C 为类别数,y 为真实标签,p 为模型预测。
1
2
3
4
5
6
criterion = nn.CrossEntropyLoss()
# 模拟输出和真实标签
output = torch.tensor([[1.0, 2.0, 0.0]]) # 未归一化的logits
target = torch.tensor([1]) # 真实标签
loss = criterion(output, target)
print(loss.item()) # 输出: 0.5500 (示例值)

2.1.2 均方误差损失

对于回归问题,常用 MSELoss (均方误差损失)来衡量模型输出与真实值之间的差距,其公式为:

$$
L = \frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y_i})^2
$$

1
2
3
4
5
6
criterion = nn.MSELoss()
# 模拟真实值和预测值
target = torch.tensor([1.0, 2.0, 3.0])
output = torch.tensor([1.5, 2.5, 3.5])
loss = criterion(output, target)
print(loss.item()) # 输出: 0.3333(示例值)

2.2 自定义损失函数

可以通过继承 nn.Module 来创建自定义损失函数。

1
2
3
4
5
6
7
8
9
class CustomLoss(nn.Module):
def forward(self, output, target):
return torch.mean((output - target) ** 2) + 0.1 * torch.mean(output) # 自定义损失

custom_loss = CustomLoss()
target = torch.tensor([1.0, 2.0, 3.0])
output = torch.tensor([1.5, 2.5, 3.5])
loss = custom_loss(output, target)
print(loss.item()) # 输出: 自定义损失的值

结论

在这部分内容中,我们详细介绍了 激活函数损失函数 的重要性及常见类型,并展示了如何在 PyTorch 中使用和自定义这些函数。通过对这些概念的掌握,我们可以更好地设计和训练神经网络模型。