
image.png
简介
一个典型的神经网络训练过程包括以下几点:
- 1.定义一个包含可训练参数的神经网络
- 2.迭代整个输入
- 3.通过神经网络处理输入
- 4.计算损失(loss)
- 5.反向传播梯度到神经网络的参数
- 6.更新网络的参数,典型的用一个简单的更新方法:weight = weight - learning_rate *gradient
原理
源码
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 为了实现手写数字识别(MNIST数据集)使用PyTorch,我们需要完成以下几个步骤:
# 加载和预处理MNIST数据集。
# 定义神经网络模型。
# 设置损失函数和优化器。
# 训练模型。
# 测试模型性能。
# 数据预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
# 下载训练集和测试集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# 创建DataLoader
batch_size = 64
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
# 定义简单的卷积神经网络
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# --------卷积层的作用是将输入图像中的像素值与卷积核中的像素值进行相乘,得到特征图。-----------------
# in_channels=1: 输入通道数。对于MNIST数据集,每个图像都是灰度图,因此输入通道数为1。
# out_channels=32: 输出通道数。这是卷积层产生的特征图的数量。这里设置为32。
# kernel_size=3: 卷积核(滤波器)的大小。这里是3x3的卷积核。
# stride=1: 步幅(步长)。表示卷积操作在输入上移动的步数。这里设置为1,意味着每次卷积操作会在输入上向右和向下各移动一步。
# padding=1: 填充。在输入周围添加额外的像素,以控制输出的空间维度。这里设置为1,这意味着在输入图像的上下左右各填充一个像素,使得输入和输出的高度和宽度相同
self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
# in_channels=32: 输入通道数。这个值必须等于前一层的输出通道数,即第一层卷积层的输出通道数32。
# out_channels=64: 输出通道数。这是第二层卷积层产生的特征图的数量。这里设置为64。
# kernel_size=3: 卷积核的大小仍然是3x3。
# stride=1: 步幅依然是1。
# padding=1: 填充同样是1,保持输出空间维度不变。
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
# --------最大池化层的作用是从每个2x2的区域中选择最大的值,从而减少特征图的空间维度,同时保留最重要的信息。-----------------
# kernel_size=2: 池化窗口的大小。这里是2x2的窗口。
# stride=2: 步幅(步长)。表示池化操作在输入上移动的步数。这里设置为2,意味着每次池化操作会在输入上向右和向下各移动两步。
# padding=0: 填充。在输入周围添加额外的像素,以控制输出的空间维度。这里设置为0,没有填充。
self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
# in_features=64 * 7 * 7: 输入特征的数量。这个值计算如下:
# 经过两个卷积层后,特征图的通道数变为64。
# 每个卷积层后面有一个最大池化层,将特征图的空间维度减半。
# 初始输入图像大小为28x28,经过第一次卷积和池化后变为14x14,再经过第二次卷积和池化后变为7x7。
# 因此,最终的特征图大小为64x7x7。
# out_features=128: 输出特征的数量。这里设置为128,表示第一层全连接层有128个神经元。
self.fc1 = nn.Linear(64 * 7 * 7, 128)
# in_features=128: 输入特征的数量,等于前一层全连接层的输出特征数量128。
# out_features=10: 输出特征的数量。这里设置为10,因为MNIST数据集有10个类别(数字0到9),因此最后一层全连接层的输出对应于每个类别的概率。
self.fc2 = nn.Linear(128, 10)
# ReLU激活函数: 引入非线性,增强模型的学习能力。
self.relu = nn.ReLU()
def forward(self, x):
x = self.pool(self.relu(self.conv1(x)))
x = self.pool(self.relu(self.conv2(x)))
x = x.view(-1, 64 * 7 * 7)
x = self.relu(self.fc1(x))
x = self.fc2(x)
return x
# 初始化模型、损失函数和优化器
model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
def train_model(model, train_loader, criterion, optimizer, num_epochs=5):
model.train()
for epoch in range(num_epochs):
running_loss = 0.0
for images, labels in train_loader:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}')
# 测试模型
def test_model(model, test_loader):
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100 * correct / total
print(f'Test Accuracy: {accuracy:.2f}%')
# 开始训练和测试
train_model(model, train_loader, criterion, optimizer)
test_model(model, test_loader)