用Python编写的LeNet网络在图片分类任务中的应用
LeNet是深度学习领域中的一个经典卷积神经网络,由Yann LeCun等人在1998年提出,用于手写数字的识别任务。虽然它简单,但具有很强的泛化能力,成为了后续深度学习发展的奠基石。
LeNet网络的结构由卷积层、池化层、全连接层和输出层组成。这个网络的主要特点是通过卷积和池化操作来提取图像的局部特征,并逐渐缩小特征图的尺寸,最后输入到全连接层进行分类。
下面我们将使用Python编写一个LeNet网络,来进行手写数字的分类任务。我们使用的数据集是MNIST,其中包含了60000张训练图像和10000张测试图像,每个图像都是一个28x28像素的灰度图片,对应一个0-9的数字。
首先,我们需要导入相关的库:
import torch import torch.nn as nn import torch.optim as optim
接下来,我们定义一个LeNet类,继承自nn.Module,并在其中定义网络的结构:
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(1, 6, kernel_size=5)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(6, 16, kernel_size=5)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.fc1 = nn.Linear(16*4*4, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.conv1(x)
x = torch.relu(x)
x = self.pool1(x)
x = self.conv2(x)
x = torch.relu(x)
x = self.pool2(x)
x = x.view(-1, 16*4*4)
x = self.fc1(x)
x = torch.relu(x)
x = self.fc2(x)
x = torch.relu(x)
x = self.fc3(x)
return x
我们在LeNet类的初始化函数中定义了卷积层、池化层和全连接层的参数,并在forward函数中将这些层连接起来。注意,在forward函数中,我们使用了relu激活函数,并通过view函数将卷积层输出的特征图展平为一维向量。
接下来,我们加载MNIST数据集,并进行数据预处理:
from torchvision import datasets, transforms
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
trainset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
testset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False)
我们使用transforms模块进行数据预处理,包括将数据转为Tensor格式,并进行归一化。然后使用datasets模块加载MNIST数据集,并使用DataLoader模块创建数据加载器。
接下来,我们定义优化器和损失函数。
net = LeNet() optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9) criterion = nn.CrossEntropyLoss()
我们使用SGD优化器,并设置学习率为0.01和动量为0.9。损失函数选择交叉熵。
接下来,我们开始训练模型。
for epoch in range(10):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 200 == 199:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 200))
running_loss = 0.0
我们使用10个epoch进行训练,每个epoch将训练集分成若干个batch,每个batch包含64个图像。对于每个batch,我们首先将优化器的梯度清零,然后将输入数据传入网络,得到输出并计算损失,然后进行反向传播和参数更新。
最后,我们在测试集上评估模型的性能:
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %.2f %%' % (
100 * correct / total))
我们使用torch.no_grad()来禁用梯度计算,然后对测试集的每个样本进行前向传播,得到模型的预测结果,计算预测准确的样本个数,并根据准确率评估模型的性能。
这样,我们就完成了使用Python编写LeNet网络在手写数字分类任务中的应用。通过训练和测试,我们可以得到一个具有较高准确率的模型,用于识别手写数字的图片。
