卷积神经网络(CNN)相比于传统的多层感知机(MLP),最大的特点在于卷积层的引入。卷积层通过局部感知和权重共享的机制,有效地减少了网络参数的数量,使得网络更容易训练。本篇文章将通过经典的LeNet网络结构,从代码实现的角度,带大家一步一步地构建一个卷积神经网络。
什么是LeNet
LeNet是由Yann LeCun等人在1998年提出的卷积神经网络,也是最早出现的卷积神经网络之一,主要用于解决手写数字识别问题。LeNet在手写数字识别上的成功,使得卷积神经网络的应用受到了极大的关注,并为后来的卷积网络发展奠定了良好的基础。
LeNet的结构如图所示:
- 包含两个卷积层(Conv Layer)
- 两个下采样层(Pooling Layer)
- 三个全连接层(Fully Connected Layer)
输入图像尺寸为32x32,经过两次卷积和池化后,进入三个全连接层,最终输出对应0到9的10个数字的分类结果。
LeNet网络结构
LeNet的网络结构如下:
1. 输入:32x32的图像
2. 第一卷积层:6个5x5的卷积核,步长为1
3. 第一池化层:2x2的最大池化层
4. 第二卷积层:16个5x5的卷积核
5. 第二池化层:2x2的最大池化层
6. 三个全连接层:120个、84个和10个神经元
代码实现
1. 导入必要的库
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torchsummary import summary
import matplotlib.pyplot as plt
from tqdm import tqdm
2. 定义LeNet网络结构
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
self.fc1 = nn.Linear(in_features=16*4*4, out_features=120)
self.fc2 = nn.Linear(in_features=120, out_features=84)
self.fc3 = nn.Linear(in_features=84, out_features=10)
def forward(self, x):
x = torch.relu(self.conv1(x))
x = torch.max_pool2d(x, kernel_size=2, stride=2)
x = torch.relu(self.conv2(x))
x = torch.max_pool2d(x, kernel_size=2, stride=2)
x = x.view(-1, 16*4*4)
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x
net = LeNet()
3. 模型摘要
summary(net, (1, 28, 28))
4. 准备数据集
transform = transforms.Compose([
transforms.Resize((32, 32)),
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=32, shuffle=True)
testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=32, shuffle=False)
5. 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.02, momentum=0.9)
6. 训练模型
num_epochs = 10
train_loss = []
train_accuracy = []
for epoch in range(num_epochs):
running_loss = 0.0
correct = 0
total = 0
for inputs, labels in tqdm(trainloader):
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
epoch_loss = running_loss / len(trainloader)
epoch_accuracy = 100 * correct / total
train_loss.append(epoch_loss)
train_accuracy.append(epoch_accuracy)
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}, Accuracy: {epoch_accuracy:.2f}%')
7. 可视化损失和准确率
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_loss, label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(train_accuracy, label='Training Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.legend()
plt.show()
8. 测试模型
net.eval()
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in testloader:
outputs = net(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Test Accuracy: {100 * correct / total:.2f}%')
总结
在本篇文章中,我们介绍了经典的LeNet卷积神经网络,包括其网络结构和具体的代码实现。通过这次实现,我们不仅复习了卷积神经网络的基础知识,还掌握了如何在代码中实现一个完整的卷积神经网络模型。同学们可以在此基础上,调整参数或改进网络结构,进一步提高模型的性能。希望这篇文章对大家有所帮助,祝大家学习愉快!
如果有任何问题或建议,欢迎在评论区留言讨论。