Handwritten Digit Recognition with a Back-Propagation Network
基于反向传播网络的手写数字识别
发表时间:1989;
发表期刊/会议:NIPS;
贡献:CNN的起点LeNet;
LeNet名字来源于该论文的第一作者Yann LeCun;
0 摘要
提出了一种反向传播网络在手写数字识别中的应用。数据的预处理是最少的,但是网络的架构是高度受限的,是专门为这项任务设计的。网络的输入由孤立数字的归一化图像组成。这种方法的错误率为1%,对美国邮政服务提供的邮政编码数字的拒收率约为9%。
1 简介
大型反向传播(BP)网络可以应用于真实的图像识别问题,而不需要大量复杂的预处理阶段。与之前关于该主题的大多数工作不同(Denker et al, 1989),学习网络直接使用图像,而不是特征向量,因此证明了BP网络处理大量低水平信息的能力。
之前对简单数字图像的研究(Le Cun, 1989)表明,网络的架构对网络的泛化能力有很大的影响。只有通过设计一个包含一定数量的关于问题的先验知识的网络架构,才能获得良好的泛化。基本设计原则是尽量减少必须由学习算法确定的自由参数的数量,而不会过度降低网络的计算能力。这一原则增加了正确泛化的概率,因为它导致了一个具有较少熵的专门网络架构(Denker et al, 1987; Patarnello and Carnevali, 1987; Tishby, Levin and Solla, 1989; Le Cun, 1989)。另一方面,必须在架构中设计适当的约束。
2 ZIPCODE 识别
手写数字识别应用程序,是一个相对简单的机器视觉任务:输入由黑色或白色像素组成,数字通常与背景分离得很好,并且只有10个输出类别。(处理2D真实图像,有规律性和复杂性,具有实用价值)
用于训练和测试网络的数据集是去年报道的工作中使用的数据库的超集(Denker et al, 1989)。
该数据库由9298个分段数字组成,这些数字来自于经过纽约州布法罗邮局的真实美国邮件上的手写邮政编码。
示例如图1所示:

- 这些数字是由许多不同的人书写的,使用了各种各样的尺寸、书写风格和工具,书写的程度也大不相同。
- 此外,还有一组3349个印刷数字,来自35种不同的字体。
- 训练集由7291个手写数字和2549个打印数字组成。
- 剩下的2007个手写数字和700个印刷数字被用作测试集。
3 预处理
预处理包括:获取、二值化、定位和初步分割;
其中一些步骤的预处理非常艰(比如连体字的分割);数据库中的许多模糊字符都是错误分割的结果(特别是破碎的"5"),如图2所示。

接下来:图像resize到相同大小(线性变换)、图像的灰度归一化到[-1,1];
4 网络
网络的输入是一个16 × 16的标准化图像,输出由10个单元组成:每个类一个单元。当出现属于第i类的模式时,第i个输出单元的期望输出为+1,其他输出单元的期望输出为-1。
通过检测和结合局部特征来进行形状识别具有众所周知的优点;
此外,如果一个特征检测器对图像的一部分有用,那么它很可能对图像的其他部分也有用。用一个具有局部接受域的单个神经元扫描输入图像,并将该神经元的状态存储在一个称为特征映射的层中的相应位置(见图3)。
(就是卷积过程嘛)

网络架构如图4所示:

网络有四个隐藏层,分别命名为HI、H2、H3和H4。HI和H3层是共享权重特征提取器,而H2和H4层是平均/子采样层。
- 输入图像:28 * 28;
- H1:卷积层
- 4个卷积核大小5 * 5, stride=1;
- 输出:4个24 * 24;
- H2:池化层
- 卷积核大小2 * 2, stride=2,MAX POOL;
- 输出:4个12 * 12;
- H3:卷积层
- 卷积核大小:5 * 5,stride=1;
- 输出:12个8 * 8;
- H4:池化层
- 卷积核大小2 * 2, stride=2,MAX POOL;
- 输出:12个4 * 4;
- 输出:全连接层
- 10分类;

特点:
1.相比MLP,LeNet使用了相对更少的参数,获得了更好的结果。
2.设计了maxpool来提取特征
LeNet代码:
import torch
import torch.nn as nn
import torch.nn.functional as F
class LeNet5(nn.Module):
def __init__(self, num_classes, grayscale=False):
"""
num_classes: 分类的数量
grayscale:是否为灰度图
"""
super(LeNet5, self).__init__()
self.grayscale = grayscale
self.num_classes = num_classes
if self.grayscale: # 可以适用单通道和三通道的图像
in_channels = 1
else:
in_channels = 3
# 卷积神经网络
self.features = nn.Sequential(
nn.Conv2d(in_channels, 6, kernel_size=5),
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(6, 16, kernel_size=5),
nn.MaxPool2d(kernel_size=2) # 原始的模型使用的是 平均池化
)
# 分类器
self.classifier = nn.Sequential(
nn.Linear(16*5*5, 120), # 这里把第三个卷积当作是全连接层了
nn.Linear(120, 84),
nn.Linear(84, num_classes)
)
def forward(self, x):
x = self.features(x) # 输出 16*5*5 特征图
x = torch.flatten(x, 1) # 展平 (1, 16*5*5)
logits = self.classifier(x) # 输出 10
probas = F.softmax(logits, dim=1)
return logits, probas