曾经,为了搭建一个深度学习环境,你可能要在命令行里和各种依赖包斗智斗勇;而今,Cloud Studio 就像一位贴心的技术管家,将繁杂的环境配置一键化解。只需打开浏览器、轻点几下,你便能获得一个功能齐全、预装深度学习库的开发环境。无论你是在家、在咖啡馆,还是在旅途中,Cloud Studio 都能让你随时随地开展实验,畅享云端编程的乐趣。
接下来,我将使用 Cloud Studio 来学习和复现经典的 LeNet-5 模型。通过这一实践过程,我们能够深入理解卷积神经网络的基本构成,还能亲身体验在云端环境下搭建、运行机器学习实验的便捷与高效。借助 Cloud Studio,我们无需在本地繁琐地配置环境,只需在浏览器上轻点几下,就能快速启动实验环境。
首先,打开浏览器,访问 Cloud Studio 的官方网站。完成注册或登录后,你会看到一个简洁直观的控制台界面。这里的设计简洁而现代,每个选项都像是高级餐厅里的精美菜单,让你一目了然。接下来,点击“新建项目”,选择“Pytorch 2.0.0 模板”。这一步操作就好比在厨房里拿出你最爱的刀具,准备开始一场美味大餐。
进入 Notebook 编辑器后,你会发现 Cloud Studio 已经预装了大部分深度学习所需的库,包括 PyTorch。你可以在包管理器中查看这些库的版本信息。如果有特殊需求,也可以在 Notebook 中直接运行如下代码安装或更新库:
!pip install torch torchvision
大多数情况下,Cloud Studio 的预装环境已经足够应对我们的 LeNet 复现任务。接下来,我们把这个 Notebook 保存为“使用 Cloud Studio 复现 LeNet”,以便今后反复使用和不断优化。
LeNet 是深度学习史上不可忽视的里程碑之一。它由著名的深度学习先驱提出,最初用于手写数字识别任务。LeNet 不仅证明了卷积神经网络(CNN)在图像识别中的巨大潜力,更奠定了现代深度学习的基础。在这里,我们将从两个角度带你认识 LeNet:一方面通过动手复现 LeNet,理解其结构与实现过程;另一方面深入精讲 LeNet 论文中的原理、架构设计及其创新点,让你明白这篇论文为什么在当时震撼了整个领域。
在上世纪九十年代初,计算资源有限、数据规模较小,但对手写数字识别等问题却有着迫切的需求。LeNet 论文应时而生,提出了一种全新的卷积神经网络架构,用于处理图像数据。这篇论文不仅在当时取得了令人瞩目的识别效果,而且奠定了卷积神经网络在后续图像处理与识别领域中的基础地位。
论文提出的主要思想包括:
这些思想不仅为 LeNet 带来了卓越的表现,还为后来的深度学习模型指明了方向。
在讨论 LeNet 之前,我们先来设想这样一个场景:你拿到一张手写数字图片,这张图片其实就是由无数个小数字(像素)组成的矩阵。如何让计算机“看懂”这张图片,并判断里面写的是哪个数字?传统方法可能需要我们手动设计一些规则和特征,但 LeNet 则用一种全自动、端到端的方式来解决问题。它的成功不仅在于整体架构简单高效,更在于它将图像处理的各个步骤巧妙地拆分成了几个核心模块,每个模块都承担着特定的功能。
可以把输入层看作整个网络的入口。对于 LeNet 来说,输入层接收的就是一张图片的原始数据,比如 MNIST 数据集中经常使用的 32×32 像素的灰度图。这里的每个像素都有一个数值(通常代表亮度),这些数值组成了一个二维矩阵,甚至可以扩展成三维(如果是彩色图片,通常还包含颜色通道)。输入层的主要任务是把这些数据按照一定的格式传递到网络的下一层,而这一步就像是把外面的世界“翻译”成计算机可以理解的语言。
进入卷积层之后,网络就开始了“侦查”工作。卷积层其实就像是一个小小的侦查员,它会用一个叫做“卷积核”的小滤波器,在整张图片上滑动(也称为“卷积运算”)。这一步的关键思想在于“局部感受野”:每个卷积核只关注输入图像中的一小块区域,就像你用放大镜观察图片的一部分。这样做的好处有两个:
在 LeNet 中,第一卷积层通常采用 5×5 的卷积核,把 32×32 的输入图像转换成若干张特征图,每张特征图都对应着图像中某种特定的局部特征,比如某种边缘或纹理模式。
紧接着卷积层后面的是池化层。你可以把池化层想象成一位“信息精炼者”,它的作用是对卷积层提取的特征进行“压缩”和“提炼”。池化层常用的方法有最大池化和平均池化:
通过池化操作,原本尺寸较大的特征图被“压缩”成更小的版本,同时保留了最重要的信息。这一步不仅降低了数据的维度(从而减少后续层的计算量),还使得模型对图像的小幅位移和噪声变化更加鲁棒。比如,如果一张图片中的边缘因为轻微位移而位置改变,池化层依然能保证主要特征不变。
在 LeNet 中,经过第一卷积层后的特征图会被池化层降采样,通常把尺寸由 28×28 降到 14×14,这样既减少了数据量,也提高了网络处理数据的速度和稳定性。
当经过几层卷积和池化操作后,图像中的特征已经被层层提取、不断压缩。这时,我们需要把这些零散的信息整合起来,做出最终的判断——这张图片到底代表哪个数字?这就是全连接层登场的时刻。全连接层就像一个决策大脑,它将前面各个模块提取到的特征“展平”为一个长向量,然后经过一系列的非线性变换(通常通过激活函数如 ReLU 来实现),最终映射到一个输出空间。
在全连接层中,每个神经元都与前一层的所有神经元相连,这种“全连接”设计确保了模型能够捕捉到所有特征之间的复杂关系,并在最后综合所有信息作出分类决策。例如,在手写数字识别任务中,最后的全连接层可能有 10 个输出,每个输出对应一个数字的概率。模型会选择概率最高的那个数字作为最终预测结果。
这种结构设计体现了从粗到细、从局部到全局的层次化思路,正是这种思想使得 LeNet-5 能够在资源有限的条件下实现高效而准确的图像识别。与此同时,这种端到端的设计理念也大大简化了传统手工设计特征的复杂过程,使得神经网络可以直接从数据中学习,自动获取最优特征表示。
接下来,我们将详细讲解如何在 Cloud Studio 上,通过 PyTorch 模板来复现 LeNet 模型。整个案例将涵盖数据准备、模型构建、训练、评估、保存与加载,每一步都将结合上文精讲的 LeNet 理论,让你在实践中体会论文中提到的核心思想。
LeNet 最初就是为手写数字识别设计的,因此我们选择经典的 MNIST 数据集。MNIST 包含 60000 张训练图片和 10000 张测试图片,每张图片均为 28×28 的灰度图。经过简单预处理,这些图片可以完美适配 LeNet 模型。
首先打开 Cloud Studio 环境,创建一个 Pytorch 环境,并启动运行,没有的按照上一篇文章创建一个。
我们通过 torchvision 来下载并加载 MNIST 数据集,同时利用 transforms 对数据进行归一化处理。代码如下:
import torch
import torchvision
import torchvision.transforms as transforms
# 定义数据预处理:将图片转换为 Tensor,并归一化到 [-1, 1]
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
# 下载并加载训练数据和测试数据
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# 定义 DataLoader,用于批量加载数据,设置 batch_size 为 64
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)
再 Jupyter Notebook 将上述代码输入到单元块之后,点击单步执行,就可以在下方看到数据集下载的过程。
经过漫长的等待,数据集下载完成。下载完成后,可以在 data 目录下看到数据集文件。
之后,经过快递员 DataLoader 之手,将数据整齐打包后送到模型手中,确保每一批数据都能及时且高效地参与训练。
基于论文中的架构,我们使用 PyTorch 的 torch.nn 模块构建 LeNet 模型。下面的代码实现了 LeNet-5 的基本结构,也就是上面 LeNet-5 结构图的结构:
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
# 第一卷积层:输入通道 1,输出通道 6,卷积核大小 5x5
self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
# 第二卷积层:输入通道 6,输出通道 16,卷积核大小 5x5
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
# 全连接层:16 个特征图(经过两次池化后尺寸缩小为 4x4)映射到 120 个神经元
self.fc1 = nn.Linear(16 * 4 * 4, 120)
# 全连接层:120 映射到 84
self.fc2 = nn.Linear(120, 84)
# 全连接层:84 映射到 10(输出 10 个类别)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# 卷积层 1 + ReLU 激活 + 池化(下采样)
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 2)
# 卷积层 2 + ReLU 激活 + 池化
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2)
# 展平多维特征图
x = x.view(x.size(0), -1)
# 全连接层 1 + ReLU 激活
x = F.relu(self.fc1(x))
# 全连接层 2 + ReLU 激活
x = F.relu(self.fc2(x))
# 全连接层 3,输出分类结果
x = self.fc3(x)
return x
# 创建模型实例并打印模型结构
model = LeNet()
print("模型结构:\n", model)
输出的模型结构如下:
可以看到有两次卷积加池化、三层全连接。通过使用 ReLU 激活函数,模型能够引入非线性因素,使得复杂模式得以高效表达。
有了数据和模型后,我们开始模型训练。训练过程中,我们定义了交叉熵损失函数和 Adam 优化器,通过多次迭代不断调整模型参数,使得模型输出与真实标签尽可能吻合。代码如下:
import torch.optim as optim
# 定义损失函数(交叉熵损失)和优化器(Adam)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 设定训练轮数
num_epochs = 10
# 进入训练循环
for epoch in range(num_epochs):
running_loss = 0.0
for i, (images, labels) in enumerate(train_loader):
# 前向传播:输入图片得到模型输出
outputs = model(images)
# 计算损失值
loss = criterion(outputs, labels)
# 反向传播前先清空梯度
optimizer.zero_grad()
# 反向传播:自动求导计算梯度
loss.backward()
# 更新模型参数
optimizer.step()
running_loss += loss.item()
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0
print("训练完成!")
再次等待漫长的训练过程……
在 Cloud Studio 的算力加持下,训练还是很快的。没一会就训练完了。
训练过程就像是在不断“练功”,每一轮更新都是对论文中“端到端学习”理念的验证。可以看到,随着训练进行,损失(Loss)逐渐下降,模型的分类准确率也在不断提升。
训练完成后,我们需要在测试集上评估模型效果。通过统计模型对测试样本的预测准确率,可以直观地看到 LeNet 的识别能力。评估代码如下:
# 将模型设置为评估模式,关闭 dropout 等训练时的特殊操作
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()
print(f'测试集准确率:{100 * correct / total:.2f}%')
可以看到,我训练的模型测试机准确率评估为 98.97%。
PS:在评估阶段,通过关闭梯度计算,不仅能加快推理速度,还能避免训练模式下的干扰。
训练好的模型当然要保存下来,以便日后复用或进行进一步调试。通过 torch save 方法,我们可以轻松保存模型。代码如下:
# 保存模型参数到文件
torch.save(model.state_dict(), "lenet_model.pth")
print("模型保存成功!")
# 加载模型参数:重新创建模型实例并加载参数
loaded_model = LeNet()
loaded_model.load_state_dict(torch.load("lenet_model.pth"))
loaded_model.eval()
print("模型加载成功,开始推理...")
# 对部分测试数据进行推理展示
import matplotlib.pyplot as plt
import numpy as np
# 获取一批测试数据
dataiter = iter(test_loader)
images, labels = next(dataiter)
# 定义显示图片函数
def imshow(img):
img = img / 2 + 0.5 # 反归一化处理
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)), cmap='gray')
plt.show()
# 显示前 8 张图片
imshow(torchvision.utils.make_grid(images[:8]))
# 对前 8 张图片进行预测
outputs = loaded_model(images[:8])
_, predicted = torch.max(outputs, 1)
print("预测结果:", predicted.numpy())
对前 8 张图片进行预测,结果如下,全部都中了:
通过阅读 LeNet 论文,希望大家能够体会到深度学习早期突破性的设计思想。论文不仅展示了如何通过局部感受野、权重共享和下采样技术有效提取图像特征,而且证明了端到端自动学习特征的重要性。LeNet 的架构设计虽然简单,却为后来的卷积神经网络奠定了基础,启发我们在有限计算资源下构建高效、鲁棒的模型。
总的来说,这篇论文不仅让我们看到了从原始数据到最终分类的完整流程,也为理解现代深度学习模型提供了宝贵的理论和实践指导。同时,Cloud Studio 为我们学习机器学习提供了一个高效便捷的平台,让我们能够轻松搭建和运行实验,加速了知识的掌握和技能的提升。
最后,别忘了关闭 Pytorch 实例,否则还会继续计费哦!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。