Skip to content

实现第一个神经网络

PyTorch - 实现你的第一个神经网络

Section titled “PyTorch - 实现你的第一个神经网络”

PyTorch 擅长创建和训练神经网络。在本章中,我们将指导你构建一个带有单个隐藏层的简单神经网络,用于执行二分类任务(预测两种结果之一)。

我们将按照以下步骤使用 PyTorch 实现我们的第一个神经网络:

首先,我们需要导入 PyTorch 库及其神经网络模块(nn)。

import torch
import torch.nn as nn

尽早设置计算设备(CPU 或 GPU)是一个好的习惯。这能让你的代码更具可移植性。

# 判断是否有 GPU 可用,否则使用 CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'Using device: {device}')

接下来,我们定义输入层、隐藏层和输出层的大小,以及训练的批量大小。

# 定义网络参数
n_in, n_h, n_out, batch_size = 10, 5, 1, 10 # 输入特征数量, 隐藏层单元数量, 输出单元数量, 批量大小

神经网络通过将输入数据映射到相应的目标输出进行学习。我们将创建一些随机的输入数据(x)和二分类目标数据(y)。

# 创建虚拟输入和目标张量(数据)
# 输入数据: 随机值, 形状为 (批量大小, 输入特征数量)
x = torch.randn(batch_size, n_in, device=device)
# 目标数据: 二进制值 (0 或 1), 形状为 (批量大小, 输出单元数量)
# 对于二分类任务,目标值应为浮点类型以用于损失计算。
y = torch.tensor([
[1.0],
[0.0],
[0.0],
[1.0],
[1.0],
[1.0],
[0.0],
[0.0],
[1.0],
[1.0]
], device=device)

注意:我们将数据移动到所选的 device 上,以确保计算按预期进行。

我们将使用 nn.Sequential 创建一个简单的前馈网络。它由一个线性层、一个 ReLU 激活函数、另一个线性层组成,此处没有最后的激活函数,因为我们将使用 BCEWithLogitsLoss,它在数值上更稳定。

# 创建一个序列模型
model = nn.Sequential(
nn.Linear(n_in, n_h), # 第一个线性层(从输入到隐藏层)
nn.ReLU(), # ReLU 激活函数
nn.Linear(n_h, n_out) # 第二个线性层(从隐藏层到输出)
).to(device) # 将模型移动到选定的设备
print("Model architecture:")
print(model)

在使用 nn.BCEWithLogitsLoss 时,建议 nn.Linear(n_h, n_out) 之后不加最终的 nn.Sigmoid() 激活函数,因为它以数值上更稳定的方式结合了 Sigmoid 激活和二元交叉熵损失(Binary Cross Entropy loss)。

对于二分类,nn.BCEWithLogitsLoss 是一个合适的损失函数。我们将使用随机梯度下降(SGD)作为我们的优化器。Adam 也是一个非常流行且通常有效的选择。

# 构建损失函数
# BCEWithLogitsLoss 适用于二分类,并期望模型输出原始 logits(对数几率)。
criterion = nn.BCEWithLogitsLoss()
# 构建优化器(此处为随机梯度下降)
# Adam 也是一个非常流行且有效的优化器:torch.optim.Adam(model.parameters(), lr=0.01)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

训练过程包括多次迭代(轮次,epochs)数据,执行前向传播,计算损失,执行反向传播(backward pass),并更新模型的权重。

# 训练循环(梯度下降)
epochs = 100 # 增加轮次以更好地演示收敛
print("\nStarting training...")
for epoch in range(epochs):
# 前向传播:通过将 x 传递给模型计算预测的 y
y_pred_logits = model(x)
# 计算并打印损失
# y_pred_logits 是原始分数(logits),criterion 会隐式处理 sigmoid 激活。
loss = criterion(y_pred_logits, y)
if (epoch + 1) % 10 == 0 or epoch == 0: # 每 10 个轮次和第一个轮次打印一次
print(f'Epoch: {epoch+1}/{epochs}, Loss: {loss.item():.4f}')
# 清零梯度:在计算新梯度之前清除之前的梯度。
# 如果不清零梯度,它们将累积,这通常不是期望的行为。
optimizer.zero_grad()
# 反向传播:计算损失相对于模型参数的梯度。
loss.backward()
# 更新权重:根据计算出的梯度调整模型参数。
optimizer.step()
print("Training finished.")

训练循环中的关键步骤:

  • 前向传播:输入数据通过网络以获得预测结果。
  • 损失计算:量化预测结果与实际目标值之间的差异。
  • 清零梯度:清除前一次迭代的梯度。
  • 反向传播:计算损失相对于每个模型参数的梯度。
  • 优化器更新:使用计算出的梯度更新模型参数(权重)以最小化损失。

在训练过程中,你会看到损失通常会下降,这表明模型正在学习。具体的损失值会因随机初始化而有所不同。

# Example Output (values will differ due to random initialization):
# Using device: cuda (or cpu)
# Model architecture:
# Sequential(
# (0): Linear(in_features=10, out_features=5, bias=True)
# (1): ReLU()
# (2): Linear(in_features=5, out_features=1, bias=True)
# )
#
# Starting training...
# Epoch: 1/100, Loss: 0.7512
# Epoch: 10/100, Loss: 0.7225
# Epoch: 20/100, Loss: 0.6953
# Epoch: 30/100, Loss: 0.6698
# Epoch: 40/100, Loss: 0.6459
# Epoch: 50/100, Loss: 0.6235
# Epoch: 60/100, Loss: 0.6025
# Epoch: 70/100, Loss: 0.5829
# Epoch: 80/100, Loss: 0.5645
# Epoch: 90/100, Loss: 0.5472
# Epoch: 100/100, Loss: 0.5310
# Training finished.

本示例演示了一个基础的神经网络。在实际应用中,你会使用更大的数据集、可能更复杂的架构,以及数据归一化、验证集和更复杂的超参数调优等技术。如需进一步学习,请查阅 PyTorch 关于 torch.nn、torch.optim 以及使用 Dataset 和 DataLoader 进行数据处理的文档。