CNN-3 基于残差网络实现手写数字识别

1.理解resnet

图1 残差块结构 

图2 网络结构 

根据图1和图2来理解一下resnet。

ResNet是用来解决什么问题的:ResNet 的提出主要是为了解决深度神经网络训练中出现的退化问题

退化问题:当网络层数加深时,模型的性能并不一定会提高,甚至会因为梯度消失、 梯度爆炸等问题导致模型性能下降。这种现象不仅仅是过拟合的问题,而是随着网络 加深,优化变得更加困难。传统的深层网络(如 VGG 或 AlexNet)在超过一定深度(例 如 20 层以上)时,训练过程可能会使模型的表现变得更差。

为什么ResNet可以解决这个问题归功于残差块(ResNet中的核心模块),使得神经网络能够在更深的层数下保持良好的性能。

在传统卷积网络中,梯度消失是因为梯度在反向传播时不断被链式法则中的小数值缩小。网络的每一层学习的是直接从输入映射到输出的函数 H(x),但H(x)的表达可能过于复杂,使得网络难以找到合适的权重。

残差块引入了一条快捷连接(Shortcut Connection),它使得梯度可以绕过中间的非线性层直接传递回前面层。分析如下:

残差块的输出为:

假设损失函数为L,则:

根据

,有:

这里的“1”就是捷径连接,确保

很小(梯度可能消失),梯度仍然能通过“1” 被有效传递回去。即使残差块中的 F(x)表现不好,梯度也可以通过直接连接的路径流 回到前面的层

残差块的结构:包括两个卷积层,卷积核大小为3*3

输入首先通过第一个卷积层、批归一化层、激活函数,然后经过第二个卷积层和批归一化层。之后输入与输出经过残差连接,最终ReLU激活得到输出。

快捷连接(Shortcut Connection)分为两种类型:

恒等连接(Identity Mapping):输入和输出形状相同,无需额外调整。

 投影连接(Projection Shortcut):通过 1×1卷积调整通道数或特征图大小(当输入输出通道数不匹配,或特征图被下采样时)。

代码实现残差块,就是定义了一个ResidualBlock块,继承自nn.Module类,里面包括了init函数和forward函数。

init函数:一个残差块包含了两个卷积层,所以初始化参数是输入通道数in_channles和输出通道数的列表channels_list(里面分别是第一个卷积层的输出通道和第二个卷积层的输出通道),以及步长stride。接下来就是定义需要用的函数,利用nn.Conv2d定义卷积层conv1,卷积核大小设置为3,填充为1保证特征图大小变化是因为步长stride的变化。利用函数nn.BatchNorm2d定义批归一化层bn1,定义激活函数relu。接下来定义第二个卷积层conv2,这个卷积层保持特征图大小和通道数不变,所以填充为1,步长也设置为1(保证了特征图大小不变)。定义批归一化层bn2。最后利用nn.Sequential()定义函数shortcut,用于对输入x进行调整大小和通道数。如果输入x和F(x)和通道数不一样,或者第一个卷积层的stride不为1(特征图大小发生了变化),那么就需要在nn.Sequential()中定义一个卷积层和批归一化层,其中卷积层的目的就是用来将输入x和f(x)的通道和大小一样,所以卷积函数的参数为 nn.Conv2d(in_channles,channels_list[-1],1,stride,0,bias=False),设置卷积核大小为1以及输出通道数量channels_list[-1],确保输入x的通道是和f(x)的通道数量一样;因为卷积核是1*1,填充为0,设置步长为stride就保证x经过卷积得到的特征图大小和f(x)一样。

forward函数:对于传进来的参数x,先通过in_x=x保留一个备份,之后x依次通过conv1,bn1,relu,conv2,bn2得到out,之后in_x传入shortcut函数,对其特征图大小和通道数量进行规整,最后就是out加上in_x,对其那行relu激活,将结果返回。

残差块代码:

class ResidualBlock(nn.Module):
    def __init__(self, in_channles, channels_list,stride):
        super(Residual
<think>好的,用户想要用Python实现手写数字识别,并且希望使用MNIST数据集。我之前提供的引用中提到了不同的方法,包括使用scikit-learn和PyTorch。用户可能对机器学习框架不太熟悉,所以需要分步骤指导。 首先,我需要确定用户的技术水平。问题中提到“使用Python实现”,可能用户希望有一个简单易懂的示例,适合初学者。引用[1]中提到了使用numpy和scikit-learn,这可能是一个基于传统机器学习的方法,比如逻辑回归或者SVM。而引用[2]和[3]提到了PyTorch和神经网络,特别是全连接网络残差网络。用户可能需要两种方法的对比,或者更基础的实现。 用户的问题明确要求教程或代码示例,所以应该提供具体的步骤和代码。考虑到用户可能希望从简单的方法开始,先介绍scikit-learn的示例,因为不需要深度学习框架,安装和运行更简单。然后再介绍PyTorch的方法,供有兴趣深入学习的用户参考。 需要检查代码的正确性,比如数据加载部分。在scikit-learn中,fetch_openml返回的数据可能需要reshape,因为图像是扁平化的。同时,要确保训练集和测试集的划分正确。对于PyTorch示例,需要解释数据预处理步骤,比如Normalize的参数,以及模型的结构。 另外,用户可能对MNIST数据集不熟悉,需要简要介绍。引用[3]提到了MNIST是常用的数据集,包含手写数字图片,每个图片是28x28的灰度图。这些信息可以帮助用户理解数据格式。 还要注意代码的可运行性,比如是否需要额外下载数据集,是否依赖库的版本问题。例如,scikit-learn的fetch_openml在较新版本中可能需要指定参数parser=&#39;auto&#39;,或者返回的数据结构是否有变化。可能需要提醒用户安装必要的库,如numpy、scikit-learn、matplotlib、torch等。 在解释模型训练时,要简明扼要,说明关键步骤,比如数据归一化、模型定义、损失函数和优化器的选择,以及训练循环。对于神经网络部分,可能需要解释全连接层的结构,激活函数的作用,以及如何计算准确率。 最后,生成相关问题,帮助用户进一步学习,比如优化模型、调整超参数、使用CNN等。这些相关问题应该基于用户可能的后续需求,引导他们深入学习。 总结步骤: 1. 介绍MNIST数据集。 2. 提供基于scikit-learn的简单示例(如逻辑回归)。 3. 提供基于PyTorch的神经网络示例。 4. 确保代码正确,解释关键部分。 5. 添加相关问题,扩展学习路径。</think>以下是通过Python实现MNIST手写数字识别的分步指南,包含两种主流实现方法: --- ### 一、MNIST数据集简介 MNIST包含70,000张28x28像素的手写数字灰度图,其中60,000张训练样本,10,000张测试样本。每个像素值为0-255的整数,标签为0-9的数字[^3]。 --- ### 二、基于Scikit-learn的实现(适合初学者) ```python # 环境安装:pip install numpy scikit-learn matplotlib import matplotlib.pyplot as plt from sklearn.datasets import fetch_openml from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split # 加载数据集 mnist = fetch_openml(&#39;mnist_784&#39;, parser=&#39;auto&#39;) X, y = mnist.data / 255., mnist.target.astype(int) # 归一化[^1] # 显示样例 plt.imshow(X[0].reshape(28,28), cmap=&#39;gray&#39;) plt.title(f"Label: {y[0]}") plt.show() # 划分数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) # 训练逻辑回归模型 model = LogisticRegression(max_iter=100) model.fit(X_train, y_train) # 评估模型 print(f"准确率: {model.score(X_test, y_test):.3f}") ``` --- ### 三、基于PyTorch的神经网络实现(更高准确率) ```python # 环境安装:pip install torch torchvision import torch import torch.nn as nn import torchvision.transforms as transforms from torchvision.datasets import MNIST from torch.utils.data import DataLoader # 定义神经网络 class Net(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(784, 128) self.fc2 = nn.Linear(128, 10) def forward(self, x): x = x.view(-1, 784) # 展平图像[^2] x = torch.relu(self.fc1(x)) return self.fc2(x) # 数据预处理 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) # MNIST均值标准差[^2] ]) # 加载数据 train_set = MNIST(&#39;./data&#39;, train=True, download=True, transform=transform) train_loader = DataLoader(train_set, batch_size=64, shuffle=True) # 初始化模型 model = Net() criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) # 训练循环 for epoch in range(5): for images, labels in train_loader: outputs = model(images) loss = criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() print(f&#39;Epoch {epoch+1} 完成&#39;) # 测试准确率(需加载test_set) # 完整代码建议参考PyTorch官方教程[^2] ``` --- ### 四、关键参数说明 1. 数据归一化:将像素值从[0,255]映射到[0,1]提升训练稳定性 2. 神经网络结构:包含两个全连接层,使用ReLU激活函数防止梯度消失 3. 批处理:batch_size=64在内存消耗与梯度稳定性间取得平衡
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值