宝子们,在深度学习的神秘世界里,咱们就像一群“炼丹师”,而模型就是咱们精心炼制的“丹药”,数据集则是炼丹的“原材料”。今天,咱们就用经典的LeNet卷积神经网络模型,在MNIST手写数字数据集这个“原材料宝库”里,炼制出一颗能精准识别数字的“神奇丹药”!😎
LeNet网络结构回顾,见:深度学习图像分类六大经典网络结构全解析
一、MNIST数据集:炼丹的“珍贵原料”🎁
MNIST数据集可是深度学习界的“老牌明星原料”,它包含60000张训练图片和10000张测试图片,每张图片都是28x28像素大小的手写数字,从0到9一应俱全。这些图片就像是一群调皮的“数字小精灵”,等着咱们用LeNet这个“炼丹炉”把它们炼化成有用的“数字丹药”。
图:MNIST数据集部分手写数字示例
二、LeNet模型:炼丹的“神奇炉子”🧪
LeNet模型可是卷积神经网络的“开山鼻祖”之一,结构简单却功能强大,就像一个精心设计的“炼丹炉”,能把输入的“数字小精灵”转化为精准的识别结果。它主要由卷积层、池化层和全连接层组成,下面咱们就来详细看看这个“炼丹炉”的结构。
模型结构剖析
- 输入层:接收28x28像素的MNIST图片,就像把“数字小精灵”放进“炼丹炉”的入口。
- 卷积层C1:使用6个5x5的卷积核,对输入图片进行卷积操作,提取图片的特征,就像在“炼丹炉”里对“数字小精灵”进行初步的“提纯”。
- 池化层S2:采用2x2的最大池化,对卷积后的特征图进行降采样,减少数据量,同时保留重要特征,就像在“炼丹炉”里对“提纯”后的物质进行进一步浓缩。
- 卷积层C3:使用16个5x5的卷积核,对池化后的特征图进行再次卷积,提取更高级的特征,让“数字小精灵”的“本质”更加清晰。
- 池化层S4:同样采用2x2的最大池化,对C3层的输出进行降采样,进一步浓缩特征。
- 全连接层C5:将S4层的输出展平为一维向量,然后与120个神经元进行全连接,就像把浓缩后的物质放入一个“大容器”里进行混合。
- 全连接层F6:与84个神经元进行全连接,进一步处理特征。
- 输出层:使用10个神经元,通过SoftMax激活函数输出每个数字类别的概率,就像“炼丹炉”最终吐出一颗颗“数字丹药”,告诉我们“数字小精灵”到底属于哪个类别。
三、炼丹代码:开启“炼丹”之旅🚀
下面就是咱们用PyTorch框架实现LeNet模型,在MNIST数据集上“炼丹”的完整代码啦!👇
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
# 定义LeNet模型
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(1, 6, 5) # 卷积层C1
self.pool = nn.MaxPool2d(2, 2) # 池化层S2和S4
self.conv2 = nn.Conv2d(6, 16, 5) # 卷积层C3
self.fc1 = nn.Linear(16 * 4 * 4, 120) # 全连接层C5
self.fc2 = nn.Linear(120, 84) # 全连接层F6
self.fc3 = nn.Linear(84, 10) # 输出层
def forward(self, x):
x = self.pool(torch.relu(self.conv1(x))) # C1和S2
x = self.pool(torch.relu(self.conv2(x))) # C3和S4
x = x.view(-1, 16 * 4 * 4) # 展平
x = torch.relu(self.fc1(x)) # C5
x = torch.relu(self.fc2(x)) # F6
x = self.fc3(x) # 输出层
return x
# 数据预处理和加载
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64, shuffle=False)
# 初始化模型、损失函数和优化器
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = LeNet().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
num_epochs = 10
train_losses = []
train_accuracies = []
for epoch in range(num_epochs):
running_loss = 0.0
correct = 0
total = 0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播和优化
optimizer.zero_grad()
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(train_loader)
epoch_accuracy = 100 * correct / total
train_losses.append(epoch_loss)
train_accuracies.append(epoch_accuracy)
print(f'Epoch {epoch + 1}/{num_epochs}, Loss: {epoch_loss:.4f}, Accuracy: {epoch_accuracy:.2f}%')
# 绘制训练损失和准确率曲线
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(train_losses, label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(train_accuracies, label='Training Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.title('Training Accuracy Curve')
plt.legend()
plt.tight_layout()
plt.show()
# 测试模型
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_accuracy = 100 * correct / total
print(f'Test Accuracy: {test_accuracy:.2f}%')
代码解释
- 模型定义:创建了
LeNet
类,实现了前面剖析的LeNet模型结构。 - 数据加载:使用
torchvision
加载MNIST数据集,并进行数据预处理。 - 初始化:将模型移动到GPU(如果可用),定义损失函数为交叉熵损失,优化器为Adam优化器。
- 训练过程:循环训练10个epoch,在每个epoch中,计算损失、反向传播和优化,并记录训练损失和准确率。
- 结果可视化:绘制训练损失和准确率曲线,直观展示训练过程。
- 测试模型:在测试集上评估模型的准确率。
四、炼丹结果:收获“神奇丹药”🎉
运行上面的代码,咱们就能看到“炼丹”的过程和结果啦!在训练过程中,你会看到类似下面的输出:
Epoch 1/10, Loss: 00.2876, Accuracy: 91.52%
Epoch 2/10, Loss: 0.0752, Accuracy: 970.68%
...
Epoch 10/10, Loss: 0.0234, Accuracy: 99.21%
Test Accuracy: 99.17%
从结果可以看出,经过10个epoch的训练,模型在训练集上的准确率达到了99.21%,在测试集上的准确率也达到了99.17%,这说明咱们的“炼丹”大获成功,LeNet模型成功地把MNIST数据集里的“数字小精灵”炼化成了能精准识别数字的“神奇丹药”!🥳
五、总结:炼丹的“快乐与收获”🎊
宝子们,今天咱们用LeNet模型在MNIST数据集上“炼丹”的过程是不是超有趣?通过这个实战,咱们不仅掌握了LeNet模型的结构和实现,还学会了如何用PyTorch框架进行模型训练和评估。深度学习就像一场奇妙的“炼丹之旅”,每一次尝试都可能带来意想不到的收获。希望你们也能在这个充满挑战和惊喜的世界里,不断探索,炼制出更多更强大的“神奇丹药”!💪
好啦,今天的“炼丹”分享就到这里啦,咱们下次再见!👋