LeNet-5网络解读及代码实现

一、介绍

    LeNet-5被大规模用于自动分类美国银行支票上的手写数字。该网络是一种卷积神经网络(CNN)。CNNs是现代最先进的基于深度学习的计算机视觉基础。这些网络建立在三个主要思想之上:局部感受野(local receptive fields)、共享权重( shared weights )和空间子抽样(spacial subsampling)。具有共享权重的局部感受野是卷积层的本质,下面描述的大多数结构以一种或另一形式使用卷积层。

    LeNet之所以是一个重要的体系结构,另一个原因是在LeNet发明之前,字符识别主要是通过手工特征工程来完成的,其次是机器学习模型来学习如何对手工特征进行分类。LeNet使得手工工程特征变得多余,因为网络自动从原始图像中学习最佳的内部表示。

二、卷积神经网络

    在传统的模式识别任务中,通常需要先人工设计特征抽取方法,从输入变量中消除不相关的变量,然后构造特征,再使用一个分类器方法进行分类。在这种情况下,全连接的多层神经网络作为分类器使用。但是,我们也可以依赖算法自己学习特征抽取的部分。以字符识别为例,我们可以以几乎原始的输入数据来作为网络的输入。但是这也有一些问题:

    首先,一般来说图片都是比较大的,一个图片通常都有好几百个变量(像素)。假如神经网络的第一层有100个神经元。那么这里就已经有数以万计的权重了。这么大的参数数量需要更复杂的系统,更多的训练集样本。此外,这么多的参数也需要更多的内存,这就已经让一些机器无法使用了。但是,最主要的问题还是这样的网络不能处理不同输入的情况。在输入到神经网络固定大小的的第一层之前,输入的图像必须是标准大小,且是图像的正中间(也就是输入的图像需要比较正式,不能偏差太大)。不幸的是,并没有一个完美的预处理方法可以达到这样的效果。因为输入的图像一般不是很正规的图像,大小、位置甚至是风格都不同。当然,如果网络足够大,这样的有较大差别的图像也是可以处理的,只是这需要很大的网络,目前这样的网络无法训练。

    其次,全连接网络也会忽视输入的拓扑特征。输入变量可以以任何顺序呈现,但却不影响输出。但实际上,图像有很强的局部特征:那些相近的变量具有很强的相关性。局部相关性也就是为什么在识别空间或者时间相关的目标的时候,先抽取并联合局部特征可以提升效果。因为相邻的变量可以被分到一个相同的小类别中。

    卷积网络联合了三个架构特征导致了转换、拉伸和扭曲的不变形:1)局部感受野(Local Receptive Fields);2)共享权重(Shared Weights);3)时间和空间的二次抽样(Spatial or Temporal Subsampling)。

    局部感受野就是接受输入的几个相邻的单元来操作。它可以追溯到20世纪60年代早期的感知机时代。局部连接在神经网络处理视觉问题中很常见。局部感受野可以抽取图像初级的特征,如边、转角等。这些特征会在后来的层中通过各种联合的方式来检测高级别特征。此外,局部初级的特征也能在全局图像中发挥作用。这个知识可以用来强制某些单元,其感受野在图像的不同位置,但是拥有相同权重。一个层里面的单元,可以通过平面的方式共享权重。这样一个平面的输出结果可以称作是feature map(这个feature map就是一个卷积核的输出结果)。一个feature map内部的单元可以做相同的操作。一个卷积层可以有多个feature map组成,因此,每个位置都可以抽取多个特征。

三、LeNet-5

我们介绍一下LeNet-5的结构。LeNet-5是用来处理手写字符的识别问题的。总共有7层。其结果如下:

     LeNet-5网络解读及代码实现_第1张图片

1. LeNet-5结构:


输入:32x32的灰度图像,也就是一个通道,那么一个图像就是一个2维的矩阵,没有RGB三个通道。
Layer1:6个大小为5x5的卷积核,步长为1。因此,到这里的输出变成了28x28x6。
Layer2:2x2大小的池化层,使用的是average pooling,步长为2。那么这一层的输出就是14x14x6。
Layer3:16个大小为5x5的卷积核,步长为1。但是,这一层16个卷积核中只有10个和前面的6层相连接。也就是说,这16个卷积核并不是扫描前一层所有的6个通道。而是只扫描其中的三个。

                                          LeNet-5网络解读及代码实现_第2张图片

这么做的原因是打破图像的对称性,并减少连接的数量。如果不这样做的话,每一个卷积核扫描一层之后是10x10,一个核大小是5x5,输入6个通道,输出16个,所以是10x10x5x5x6x16=240000个连接。但实际上只有156000连接。训练参数的数量从2400变成了1516个。

Layer4:和第二层一样,2x2大小的池化层,使用的是average pooling,步长为2。
Layer5:卷积层,120个卷积核,大小为1x1。
Layer6:全连接层,隐藏单元是84个。
Layer7:输出层,输出单元是10个,因为数字识别是0-9。

2. LeNet-5的总结如下:

LeNet-5网络解读及代码实现_第3张图片

3. LeNet-5的特点:

LeNet有一些操作,在现在看来并不是很常见。第一个就是第三层的卷几层并没有利用上一层所有的通道。这个一般很少这么做。这个一方面是减少了训练参数,一方面也是希望能检测到不同的模式。另一个是输出层之前加了一个全连接层,也就是强制要把所有的图像变成一个84维的向量,然后去分类。还有一点就是使用average pooling。这个一般现在主要是用maxpooling,因为maxpooling的效果更好。

四、Python代码实现:LeNet-5-with-Keras

    dataset can be found here: https://www.kaggle.com/c/digit-recognizer/data

# Implementation of LeNet-5 in keras 
# [LeCun et al., 1998. Gradient based learning applied to document recognition]
# Some minor changes are made to the architecture like using ReLU activation instead of 
# sigmoid/tanh, max pooling instead of avg pooling and softmax output layer 

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')

Y_train = train[['label']]
X_train = train.drop(train.columns[[0]], axis=1)
X_test = test

#Visualizing the data
sample = X_train.iloc[10, :]
sample = sample.reshape([28,28])
plt.imshow(sample, cmap='gray')

X_train = np.array(X_train)
X_test = np.array(X_test)

#Reshape the training and test set
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1)
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1)

#Padding the images by 2 pixels since in the paper input images were 32x32
X_train = np.pad(X_train, ((0,0),(2,2),(2,2),(0,0)), 'constant')
X_test = np.pad(X_test, ((0,0),(2,2),(2,2),(0,0)), 'constant')

#Standardization
mean_px = X_train.mean().astype(np.float32)
std_px = X_train.std().astype(np.float32)
X_train = (X_train - mean_px)/(std_px)

#One-hot encoding the labels
from keras.utils.np_utils import to_categorical
Y_train = to_categorical(Y_train)


import keras 
from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dense


model = Sequential()
#Layer 1
#Conv Layer 1
model.add(Conv2D(filters = 6, 
                 kernel_size = 5, 
                 strides = 1, 
                 activation = 'relu', 
                 input_shape = (32,32,1)))
#Pooling layer 1
model.add(MaxPooling2D(pool_size = 2, strides = 2))
#Layer 2
#Conv Layer 2
model.add(Conv2D(filters = 16, 
                 kernel_size = 5,
                 strides = 1,
                 activation = 'relu',
                 input_shape = (14,14,6)))
#Pooling Layer 2
model.add(MaxPooling2D(pool_size = 2, strides = 2))
#Flatten
model.add(Flatten())
#Layer 3
#Fully connected layer 1
model.add(Dense(units = 120, activation = 'relu'))
#Layer 4
#Fully connected layer 2
model.add(Dense(units = 84, activation = 'relu'))
#Layer 5
#Output Layer
model.add(Dense(units = 10, activation = 'softmax'))
model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])

model.fit(X_train ,Y_train, steps_per_epoch = 10, epochs = 42)

y_pred = model.predict(X_test)

#Converting one hot vectors to labels
labels = np.argmax(y_pred, axis = 1)

index = np.arange(1, 28001)

labels = labels.reshape([len(labels),1])
index = index.reshape([len(index), 1])

final = np.concatenate([index, labels], axis = 1)

#Prediction csv file
np.savetxt("mnist_1.csv", final, delimiter = " ", fmt = '%s')

 

你可能感兴趣的:(深度学习)