【www.shanpow.com--网络散文】
篇一:[lenet]卷积神经网络Lenet
原文地址:http://blog.csdn.net/hjimce/article/details/47323463
作者:hjimce
卷积神经网络算法是n年前就有的算法,只是近年来因为深度学习相关算法为多层网络的训练提供了新方法,然后现在电脑的计算能力已非当年的那种计算水平,同时现在的训练数据很多,于是神经网络的相关算法又重新火了起来,因此卷积神经网络就又活了起来,再开始前,我们需要明确的是网上讲的卷积神经网络的相关教程一般指的是神经网络的前向传导过程,反向传播都是用梯度下降法进行训练。
一、理论阶段
讲解这个算法,没有打算啰嗦太多的东西,因为什么权值共享、局部感受野什么的,讲那么多,都是那些生物学的相关理论。卷积神经网络的相关博文也是一大堆,但是讲的,基本上都是抄过来抄过去,就像我之前不理解从S2层到C3层是怎么实现的,网上看了一大堆教程,没有一个解答这个问题的。我的个人感觉整个过程,就只有S2到C3是最难理解的。接着我将结合我的理解进行讲解。
1、卷积
卷积的概念这个我想只要学过图像处理的人都懂的概念了,这个不解释。我们知道对于给定的一幅图像来说,给定一个卷积核,卷积就是根据卷积窗口,进行像素的加权求和。
卷积神经网络与我们之前所学到的图像的卷积的区别,我的理解是:我们之前学图像处理遇到卷积,一般来说,这个卷积核是已知的,比如各种边缘检测算子、高斯模糊等这些,都是已经知道卷积核,然后再与图像进行卷积运算。然而深度学习中的卷积神经网络卷积核是未知的,我们训练一个神经网络,就是要训练得出这些卷积核,而这些卷积核就相当于我们学单层感知器的时候的那些参数W,因此你可以把这些待学习的卷积核看成是神经网络的训练参数W。
2、池化
刚开始学习CNN的时候,看到这个词,好像高大上的样子,于是查了很多资料,理论一大堆,但是实践、算法实现却都没讲到,也不懂池化要怎么实现?其实所谓的池化,就是图片下采样。这个时候,你会发现CNN每一层的构建跟图像高斯金字塔的构建有点类似,因此你如果已经懂得了图像金字塔融合的相关算法,那么就变的容易理解了。在高斯金子塔构建中,每一层通过卷积,然后卷积后进行下采样,而CNN也是同样的过程。废话不多说,这里就讲一下,CNN的池化:
CNN的池化(图像下采样)方法很多:Mean pooling(均值采样)、Max pooling(最大值采样)、Overlapping (重叠采样)、L2 pooling(均方采样)、Local Contrast Normalization(归一化采样)、Stochasticpooling(随即采样)、Def-pooling(形变约束采样)。其中最经典的是最大池化,因此我就解释一下最大池化的实现:
原图片
为了简单起见,我用上面的图片作为例子,假设上面的图片大小是4*4的,如上图所示,然后图片中每个像素点的值是上面各个格子中的数值。然后我要对这张4*4的图片进行池化,池化的大小为(2,2),跨步为2,那么采用最大池化也就是对上面4*4的图片进行分块,每个块的大小为2*2,然后统计每个块的最大值,作为下采样后图片的像素值,具体计算如下图所示:
也就是说我们最后得到下采样后的图片为:
这就是所谓的最大池化。当然以后你还会遇到各种池化方法,比如均值池化,也就是对每个块求取平均值作为下采样的新像素值。还有重叠采样的池化,我上面这个例子是没有重叠的采样的,也就是每个块之间没有相互重叠的部分,上面我说的跨步为2,就是为了使得分块都非重叠,等等,这些以后再跟大家解释池化常用方法。这里就先记住最大池化就好了,因为这个目前是最常用的。
3、feature maps
这个单词国人把它翻译成特征图,挺起来很专业的名词。那么什么叫特征图呢?其实一张图片经过一个卷积核进行卷积运算,我们可以得到一张卷积后的结果图片,而这张图片就是特征图。在CNN中,我们要训练的卷积核并不是仅仅只有一个,这些卷积核用于提取特征,卷积核个数越多,提取的特征越多,理论上来说精度也会更高,然而卷积核一堆,意味着我们要训练的参数的个数越多。在LeNet-5经典结构中,第一层卷积核选择了6个,而在AlexNet中,第一层卷积核就选择了96个,具体多少个合适,还有待学习。
回到特征图概念,CNN的每一个卷积层我们都要人为的选取合适的卷积核个数,及卷积核大小。每个卷积核与图片进行卷积,就可以得到一张特征图了,比如LeNet-5经典结构中,第一层卷积核选择了6个,我们可以得到6个特征图,这些特征图也就是下一层网络的输入了。我们也可以把输入图片看成一张特征图,作为第一层网络的输入。
4、CNN的经典结构
对于刚入门CNN的人来说,我们首先需要现在的一些经典结构:
(1)LeNet-5。这个是n多年前就有的一个CNN的经典结构,主要是用于手写字体的识别,也是刚入门需要学习熟悉的一个网络,我的这篇博文主要就是要讲这个网络
(2)AlexNet。
在imagenet上的图像分类challenge上大神Alex提出的alexnet网络结构模型赢得了2012届的冠军,振奋人心,利用CNN实现了图片分类,别人用传统的神经网络调参跳到半死也就那样,Alex利用CNN精度远超传统的网络。
其它的还有什么《Network In Network》,GoogLeNet、Deconvolution Network,在以后的学习中我们会遇到。比如利用Deconvolution Network反卷积网络实现图片的去模糊,牛逼哄哄。
OK,理论阶段就啰嗦到这里就好了,接着就讲解 LeNet-5, LeNet-5是用于手写字体的识别的一个经典CNN:
LeNet-5结构
输入:32*32的手写字体图片,这些手写字体包含0~9数字,也就是相当于10个类别的图片
输出:分类结果,0~9之间的一个数
因此我们可以知道,这是一个多分类问题,总共有十个类,因此神经网络的最后输出层必然是SoftMax问题,然后神经元的个数是10个。LeNet-5结构:
输入层:32*32的图片,也就是相当于1024个神经元
C1层:paper作者,选择6个特征卷积核,然后卷积核大小选择5*5,这样我们可以得到6个特征图,然后每个特征图的大小为32-5+1=28,也就是神经元的个数由1024减小到了28*28=784。
S2层:这就是下采样层,也就是使用最大池化进行下采样,池化的size,选择(2,2),也就是相当于对C1层28*28的图片,进行分块,每个块的大小为2*2,这样我们可以得到14*14个块,然后我们统计每个块中,最大的值作为下采样的新像素,因此我们可以得到S1结果为:14*14大小的图片,共有6个这样的图片。
C3层:卷积层,这一层我们选择卷积核的大小依旧为5*5,据此我们可以得到新的图片大小为14-5+1=10,然后我们希望可以得到16张特征图。那么问题来了?这一层是最难理解的,我们知道S2包含:6张14*14大小的图片,我们希望这一层得到的结果是:16张10*10的图片。这16张图片的每一张,是通过S2的6张图片进行加权组合得到的,具体是怎么组合的呢?问题如下图所示:
为了解释这个问题,我们先从简单的开始,我现在假设输入6特征图的大小是5*5的,分别用6个5*5的卷积核进行卷积,得到6个卷积结果图片大小为1*1,如下图所示:
为了简便起见,我这里先做一些标号的定义:我们假设输入第i个特征图的各个像素值为x1i,x2i……x25i,因为每个特征图有25个像素。因此第I个特征图经过5*5的图片卷积后,得到的卷积结果图片的像素值Pi可以表示成:
这个是卷积公式,不解释。因此对于上面的P1~P6的计算方法,这个就是直接根据公式。然后我们把P1~P6相加起来,也就是:
P=P1+P2+……P6
把上面的Pi的计算公式,代入上式,那么我们可以得到:
P=WX
其中X就是输入的那6张5*5特征图片的各个像素点值,而W就是我们需要学习的参数,也就相当于6个5*5的卷积核,当然它包含着6*(5*5)个参数。因此我们的输出特征图就是:
Out=f(P+b)
这个就是从S2到C3的计算方法,其中b表示偏置项,f为激活函数。
我们回归到原来的问题:有6张输入14*14的特征图片,我们希望用5*5的卷积核,然后最后我们希望得到一张10*10的输出特征图片?
根据上面的过程,也就是其实我们用5*5的卷积核去卷积每一张输入的特征图,当然每张特征图的卷积核参数是不一样的,也就是不共享,因此我们就相当于需要6*(5*5)个参数。对每一张输入特征图进行卷积后,我们得到6张10*10,新图片,这个时候,我们把这6张图片相加在一起,然后加一个偏置项b,然后用激活函数进行映射,就可以得到一张10*10的输出特征图了。
而我们希望得到16张10*10的输出特征图,因此我们就需要卷积参数个数为16*(6*(5*5))=16*6*(5*5)个参数。总之,C3层每个图片是通过S2图片进行卷积后,然后相加,并且加上偏置b,最后在进行激活函数映射得到的结果。
S4层:下采样层,比较简单,也是知己对C3的16张10*10的图片进行最大池化,池化块的大小为2*2。因此最后S4层为16张大小为5*5的图片。至此我们的神经元个数已经减少为:16*5*5=400。
C5层:我们继续用5*5的卷积核进行卷积,然后我们希望得到120个特征图。这样C5层图片的大小为5-5+1=1,也就是相当于1个神经元,120个特征图,因此最后只剩下120个神经元了。这个时候,神经元的个数已经够少的了,后面我们就可以直接利用全连接神经网络,进行这120个神经元的后续处理,后面具体要怎么搞,只要懂多层感知器的都懂了,不解释。
上面的结构,只是一种参考,在现实使用中,每一层特征图需要多少个,卷积核大小选择,还有池化的时候采样率要多少,等这些都是变化的,这就是所谓的CNN调参,我们需要学会灵活多变。
比如我们可以把上面的结构改为:C1层卷积核大小为7*7,然后把C3层卷积核大小改为3*3等,然后特征图的个数也是自己选,说不定得到手写字体识别的精度比上面那个还高,这也是有可能的,总之一句话:需要学会灵活多变,需要学会CNN的调参。
二、实战阶段
1、训练数据获取
在theano学习库中有手写字体的库,可以从网上下载到,名为:mnist.pkl.gz的手写字体库,里面包含了三个部分的数据,训练数据集train_set:50000个训练样本,验证集valid_set,我们可以用如下的代码读取这些数据,然后用plot显示其中的一张图片:
[python] view
plain copy
<span style="font-size:18px;">import cPickle
import gzip
import numpy as np
import matplotlib.pyplot as plt
f = gzip.open("mnist.pkl.gz", "rb")
train_set, valid_set, test_set = cPickle.load(f)
f.close()
tx,ty=train_set;
#查看训练样本
print np.shape(tx)#可以看到tx大小为(50000,28*28)的二维矩阵
print np.shape(ty)#可以看到ty大小为(50000,1)的矩阵
#图片显示
A=tx[8].reshape(28,28)#第八个训练样本
Y=ty[8]
print Y
plt.imshow(A,cmap="gray")#显示手写字体图片</span>
在上面的代码中我显示的是第8张图片,可以看到如下结果:
第八个样本是数字1。
2、LeNet-5实现
首先你要知道mnist.pkl.gz这个库给我们的图片的大小是28*28的,因此我们可以第一步选择5*5的卷积核进行卷积得到24*24,同时我们希望C1层得到20张特征图,等等,具体的代码实现如下;
[python] view
plain copy
import os
import sys
import timeit
import numpy
import theano
import theano.tensor as T
from theano.tensor.signal import downsample
from theano.tensor.nnet import conv
from logistic_sgd import LogisticRegression, load_data
from mlp import HiddenLayer
#卷积神经网络的一层,包含:卷积+下采样两个步骤
#算法的过程是:卷积-》下采样-》激活函数
class LeNetConvPoolLayer(object):
#image_shape是输入数据的相关参数设置 filter_shape本层的相关参数设置
def __init__(self, rng, input, filter_shape, image_shape, poolsize=(2, 2)):
"""
:type rng: numpy.random.RandomState
:param rng: a random number generator used to initialize weights
3、input: 输入特征图数据,也就是n幅特征图片
4、参数 filter_shape: (number of filters, num input feature maps,
filter height, filter width)
num of filters:是卷积核的个数,有多少个卷积核,那么本层的out feature maps的个数
也将生成多少个。num input feature maps:输入特征图的个数。
然后接着filter height, filter width是卷积核的宽高,比如5*5,9*9……
filter_shape是列表,因此我们可以用filter_shape[0]获取卷积核个数
5、参数 image_shape: (batch size, num input feature maps,
image height, image width),
batch size:批量训练样本个数 ,num input feature maps:输入特征图的个数
image height, image width分别是输入的feature map图片的大小。
image_shape是一个列表类型,所以可以直接用索引,访问上面的4个参数,索引下标从
0~3。比如image_shape[2]=image_heigth image_shape[3]=num input feature maps
6、参数 poolsize: 池化下采样的的块大小,一般为(2,2)
"""
assert image_shape[1] == filter_shape[1]#判断输入特征图的个数是否一致,如果不一致是错误的
self.input = input
# fan_in=num input feature maps *filter height*filter width
#numpy.prod(x)函数为计算x各个元素的乘积
#也就是说fan_in就相当于每个即将输出的feature map所需要链接参数权值的个数
fan_in = numpy.prod(filter_shape[1:])
# fan_out=num output feature maps * filter height * filter width
fan_out = (filter_shape[0] * numpy.prod(filter_shape[2:]) /
numpy.prod(poolsize))
# 把参数初始化到[-a,a]之间的数,其中a=sqrt(6./(fan_in + fan_out)),然后参数采用均匀采样
#权值需要多少个?卷积核个数*输入特征图个数*卷积核宽*卷积核高?这样没有包含采样层的链接权值个数
W_bound = numpy.sqrt(6. / (fan_in + fan_out))
self.W = theano.shared(
numpy.asarray(
rng.uniform(low=-W_bound, high=W_bound, size=filter_shape),
dtype=theano.config.floatX
),
borrow=True
)
# b为偏置,是一维的向量。每个输出特征图i对应一个偏置参数b[i]
#,因此下面初始化b的个数就是特征图的个数filter_shape[0]
b_values = numpy.zeros((filter_shape[0],), dtype=theano.config.floatX)
self.b = theano.shared(value=b_values, borrow=True)
# 卷积层操作,函数conv.conv2d的第一个参数为输入的特征图,第二个参数为随机出事化的卷积核参数
#第三个参数为卷积核的相关属性,输入特征图的相关属性
conv_out = conv.conv2d(
input=input,
filters=self.W,
filter_shape=filter_shape,
image_shape=image_shape
)
# 池化操作,最大池化
pooled_out = downsample.max_pool_2d(
input=conv_out,
ds=poolsize,
ignore_border=True
)
#激励函数,也就是说是先经过卷积核再池化后,然后在进行非线性映射
# add the bias term. Since the bias is a vector (1D array), we first
# reshape it to a tensor of shape (1, n_filters, 1, 1). Each bias will
# thus be broadcasted across mini-batches and feature map
# width & height
self.output = T.tanh(pooled_out + self.b.dimshuffle("x", 0, "x", "x"))
# 保存参数
self.params = [self.W, self.b]
self.input = input
#测试函数
def evaluate_lenet5(learning_rate=0.1, n_epochs=200,
dataset="mnist.pkl.gz",
nkerns=[20, 50], batch_size=500):
""" Demonstrates lenet on MNIST dataset
:learning_rate: 梯度下降法的学习率
:n_epochs: 最大迭代次数
:type dataset: string
:param dataset: path to the dataset used for training /testing (MNIST here)
:nkerns: 每个卷积层的卷积核个数,第一层卷积核个数为 nkerns[0]=20,第二层卷积核个数
为50个
"""
rng = numpy.random.RandomState(23455)
datasets = load_data(dataset)#加载训练数据,训练数据包含三个部分
train_set_x, train_set_y = datasets[0]#训练数据
valid_set_x, valid_set_y = datasets[1]#验证数据
test_set_x, test_set_y = datasets[2]#测试数据
# 计算批量训练可以分多少批数据进行训练,这个只要是知道批量训练的人都知道
n_train_batches = train_set_x.get_value(borrow=True).shape[0]#训练数据个数
n_valid_batches = valid_set_x.get_value(borrow=True).shape[0]
n_test_batches = test_set_x.get_value(borrow=True).shape[0]
n_train_batches /= batch_size#批数
n_valid_batches /= batch_size
n_test_batches /= batch_size
# allocate symbolic variables for the data
index = T.lscalar() # index to a [mini]batch
# start-snippet-1
x = T.matrix("x") # the data is presented as rasterized images
y = T.ivector("y") # the labels are presented as 1D vector of
# [int] labels
# Reshape matrix of rasterized images of shape (batch_size, 28 * 28)
# to a 4D tensor, compatible with our LeNetConvPoolLayer
# (28, 28) is the size of MNIST images.
layer0_input = x.reshape((batch_size, 1, 28, 28))
"""""构建第一层网络:
image_shape:输入大小为28*28的特征图,batch_size个训练数据,每个训练数据有1个特征图
filter_shape:卷积核个数为nkernes[0]=20,因此本层每个训练样本即将生成20个特征图
经过卷积操作,图片大小变为(28-5+1 , 28-5+1) = (24, 24)
经过池化操作,图片大小变为 (24/2, 24/2) = (12, 12)
最后生成的本层image_shape为(batch_size, nkerns[0], 12, 12)"""
layer0 = LeNetConvPoolLayer(
rng,
input=layer0_input,
image_shape=(batch_size, 1, 28, 28),
filter_shape=(nkerns[0], 1, 5, 5),
poolsize=(2, 2)
)
"""""构建第二层网络:输入batch_size个训练图片,经过第一层的卷积后,每个训练图片有nkernes[0]个特征图,每个特征图
大小为12*12
经过卷积后,图片大小变为(12-5+1, 12-5+1) = (8, 8)
经过池化后,图片大小变为(8/2, 8/2) = (4, 4)
最后生成的本层的image_shape为(batch_size, nkerns[1], 4, 4)"""
layer1 = LeNetConvPoolLayer(
rng,
input=layer0.output,
image_shape=(batch_size, nkerns[0], 12, 12),
filter_shape=(nkerns[1], nkerns[0], 5, 5),
poolsize=(2, 2)
)
# the HiddenLayer being fully-connected, it operates on 2D matrices of
# shape (batch_size, num_pixels) (i.e matrix of rasterized images).
# This will generate a matrix of shape (batch_size, nkerns[1] * 4 * 4),
# or (500, 50 * 4 * 4) = (500, 800) with the default values.
layer2_input = layer1.output.flatten(2)
"""""全链接:输入layer2_input是一个二维的矩阵,第一维表示样本,第二维表示上面经过卷积下采样后
每个样本所得到的神经元,也就是每个样本的特征,HiddenLayer类是一个单层网络结构
下面的layer2把神经元个数由800个压缩映射为500个"""
layer2 = HiddenLayer(
rng,
input=layer2_input,
n_in=nkerns[1] * 4 * 4,
n_out=500,
activation=T.tanh
)
# 最后一层:逻辑回归层分类判别,把500个神经元,压缩映射成10个神经元,分别对应于手写字体的0~9
layer3 = LogisticRegression(input=layer2.output, n_in=500, n_out=10)
# the cost we minimize during training is the NLL of the model
cost = layer3.negative_log_likelihood(y)
# create a function to compute the mistakes that are made by the model
test_model = theano.function(
[index],
layer3.errors(y),
givens={
x: test_set_x[index * batch_size: (index + 1) * batch_size],
y: test_set_y[index * batch_size: (index + 1) * batch_size]
}
)
validate_model = theano.function(
[index],
layer3.errors(y),
givens={
x: valid_set_x[index * batch_size: (index + 1) * batch_size],
y: valid_set_y[index * batch_size: (index + 1) * batch_size]
}
)
#把所有的参数放在同一个列表里,可直接使用列表相加
params = layer3.params + layer2.params + layer1.params + layer0.params
#梯度求导
grads = T.grad(cost, params)
# train_model is a function that updates the model parameters by
# SGD Since this model has many parameters, it would be tedious to
# manually create an update rule for each model parameter. We thus
# create the updates list by automatically looping over all
# (params[i], grads[i]) pairs.
updates = [
(param_i, param_i - learning_rate * grad_i)
for param_i, grad_i in zip(params, grads)
]
train_model = theano.function(
[index],
cost,
updates=updates,
givens={
x: train_set_x[index * batch_size: (index + 1) * batch_size],
y: train_set_y[index * batch_size: (index + 1) * batch_size]
}
)
# end-snippet-1
###############
# TRAIN MODEL #
###############
print "... training"
# early-stopping parameters
patience = 10000 # look as this many examples regardless
patience_increase = 2 # wait this much longer when a new best is
# found
improvement_threshold = 0.995 # a relative improvement of this much is
# considered significant
validation_frequency = min(n_train_batches, patience / 2)
# go through this many
# minibatche before checking the network
# on the validation set; in this case we
# check every epoch
best_validation_loss = numpy.inf
best_iter = 0
test_score = 0.
start_time = timeit.default_timer()
epoch = 0
done_looping = False
while (epoch < n_epochs) and (not done_looping):
epoch = epoch + 1
for minibatch_index in xrange(n_train_batches):#每一批训练数据
cost_ij = train_model(minibatch_index)
iter = (epoch - 1) * n_train_batches + minibatch_index
if (iter + 1) % validation_frequency == 0:
# compute zero-one loss on validation set
validation_losses = [validate_model(i) for i
in xrange(n_valid_batches)]
this_validation_loss = numpy.mean(validation_losses)
print("epoch %i, minibatch %i/%i, validation error %f %%" %
(epoch, minibatch_index + 1, n_train_batches,
this_validation_loss * 100.))
# if we got the best validation score until now
if this_validation_loss < best_validation_loss:
#improve patience if loss improvement is good enough
if this_validation_loss < best_validation_loss * \
improvement_threshold:
patience = max(patience, iter * patience_increase)
# save best validation score and iteration number
best_validation_loss = this_validation_loss
best_iter = iter
# test it on the test set
test_losses = [
test_model(i)
for i in xrange(n_test_batches)
]
test_score = numpy.mean(test_losses)
print((" epoch %i, minibatch %i/%i, test error of "
"best model %f %%") %
(epoch, minibatch_index + 1, n_train_batches,
test_score * 100.))
if patience <= iter:
done_looping = True
break
end_time = timeit.default_timer()
print("Optimization complete.")
print("Best validation score of %f %% obtained at iteration %i, "
"with test performance %f %%" %
(best_validation_loss * 100., best_iter + 1, test_score * 100.))
print >> sys.stderr, ("The code for file " +
os.path.split(__file__)[1] +
" ran for %.2fm" % ((end_time - start_time) / 60.))
if __name__ == "__main__":
evaluate_lenet5()
def experiment(state, channel):
evaluate_lenet5(state.learning_rate, dataset=state.dataset)
训练结果:
参考文献:
1、http://blog.csdn.net/zouxy09/article/details/8775360/
2、http://www.deeplearning.net/tutorial/lenet.html#lenet
**********************作者:hjimce 时间:2015.8.6 联系QQ:1393852684 地址:http://blog.csdn.net/hjimce 转载请保留本行信息********************
篇二:[lenet]Deep Learning(深度学习)学习笔记整理系列之LeNet
版权声明:本文为博主原创文章,未经博主允许不得转载。
Deep Learning(深度学习)学习笔记整理系列的地址是http://blog.csdn.net/zouxy09/article/details/8781543,里面举了一个卷积例子用来说明参数.
一个典型的例子说明
一种典型的用来识别数字的卷积网络是LeNet-5(效果和paper等见这)。当年美国大多数银行就是用它来识别支票上面的手写数字的。能够达到这种商用的地步,它的准确性可想而知。毕竟目前学术界和工业界的结合是最受争议的。 LeNet-5共有7层,不包含输入,每层都包含可训练参数(连接权重)。输入图像为32*32大小。这要比Mnist数据库(一个公认的手写数据库)中最大的字母还大。这样做的原因是希望潜在的明显特征如笔画断电或角点能够出现在最高层特征监测子感受野的中心。
我们先要明确一点:每个层有多个Feature Map,每个Feature Map通过一种卷积滤波器提取输入的一种特征,然后每个Feature Map有多个神经元。
C1层是一个卷积层(为什么是卷积?卷积运算一个重要的特点就是,通过卷积运算,可以使原信号特征增强,并且降低噪音),由6个特征图Feature Map构成。特征图中每个神经元与输入中5*5的邻域相连。特征图的大小为28*28,这样能防止输入的连接掉到边界之外(是为了BP反馈时的计算,不致梯度损失,个人见解)。C1有156个可训练参数(每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器,共(5*5+1)*6=156个参数),共156*(28*28)=122,304个连接。
S2层是一个下采样层(为什么是下采样?利用图像局部相关性的原理,对图像进行子抽样,可以减少数据处理量同时保留有用信息),有6个14*14的特征图。特征图中的每个单元与C1中相对应特征图的2*2邻域相连接。S2层每个单元的4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid函数计算。可训练系数和偏置控制着sigmoid函数的非线性程度。如果系数比较小,那么运算近似于线性运算,亚采样相当于模糊图像。如果系数比较大,根据偏置的大小亚采样可以被看成是有噪声的“或”运算或者有噪声的“与”运算。每个单元的2*2感受野并不重叠,因此S2中每个特征图的大小是C1中特征图大小的1/4(行和列各1/2)。S2层有12个可训练参数和5880个连接。图:卷积和子采样过程:卷积过程包括:用一个可训练的滤波器fx去卷积一个输入的图像(第一阶段是输入的图像,后面的阶段就是卷积特征map了),然后加一个偏置bx,得到卷积层Cx。子采样过程包括:每邻域四个像素求和变为一个像素,然后通过标量Wx+1加权,再增加偏置bx+1,然后通过一个sigmoid激活函数,产生一个大概缩小四倍的特征映射图Sx+1。
所以从一个平面到下一个平面的映射可以看作是作卷积运算,S-层可看作是模糊滤波器,起到二次特征提取的作用。隐层与隐层之间空间分辨率递减,而每层所含的平面数递增,这样可用于检测更多的特征信息。
C3层也是一个卷积层,它同样通过5x5的卷积核去卷积层S2,然后得到的特征map就只有10x10个神经元,但是它有16种不同的卷积核,所以就存在16个特征map了。这里需要注意的一点是:C3中的每个特征map是连接到S2中的所有6个或者几个特征map的,表示本层的特征map是上一层提取到的特征map的不同组合(这个做法也并不是唯一的)。(看到没有,这里是组合,就像之前聊到的人的视觉系统一样,底层的结构构成上层更抽象的结构,例如边缘构成形状或者目标的部分)。
刚才说C3中每个特征图由S2中所有6个或者几个特征map组合而成。为什么不把S2中的每个特征图连接到每个C3的特征图呢?原因有2点。第一,不完全的连接机制将连接的数量保持在合理的范围内。第二,也是最重要的,其破坏了网络的对称性。由于不同的特征图有不同的输入,所以迫使他们抽取不同的特征(希望是互补的)。
例如,存在的一个方式是:C3的前6个特征图以S2中3个相邻的特征图子集为输入。接下来6个特征图以S2中4个相邻特征图子集为输入。然后的3个以不相邻的4个特征图子集为输入。最后一个将S2中所有特征图为输入。这样C3层有1516个可训练参数和151600个连接。
S4层是一个下采样层,由16个5*5大小的特征图构成。特征图中的每个单元与C3中相应特征图的2*2邻域相连接,跟C1和S2之间的连接一样。S4层有32个可训练参数(每个特征图1个因子和一个偏置)和2000个连接。
C5层是一个卷积层,有120个特征图。每个单元与S4层的全部16个单元的5*5邻域相连。由于S4层特征图的大小也为5*5(同滤波器一样),故C5特征图的大小为1*1:这构成了S4和C5之间的全连接。之所以仍将C5标示为卷积层而非全相联层,是因为如果LeNet-5的输入变大,而其他的保持不变,那么此时特征图的维数就会比1*1大。C5层有48120个可训练连接。
F6层有84个单元(之所以选这个数字的原因来自于输出层的设计),与C5层全相连。有10164个可训练参数。如同经典神经网络,F6层计算输入向量和权重向量之间的点积,再加上一个偏置。然后将其传递给sigmoid函数产生单元i的一个状态。
最后,输出层由欧式径向基函数(Euclidean Radial Basis Function)单元组成,每类一个单元,每个有84个输入。换句话说,每个输出RBF单元计算输入向量和参数向量之间的欧式距离。输入离参数向量越远,RBF输出的越大。一个RBF输出可以被理解为衡量输入模式和与RBF相关联类的一个模型的匹配程度的惩罚项。用概率术语来说,RBF输出可以被理解为F6层配置空间的高斯分布的负log-likelihood。给定一个输入模式,损失函数应能使得F6的配置与RBF参数向量(即模式的期望分类)足够接近。这些单元的参数是人工选取并保持固定的(至少初始时候如此)。这些参数向量的成分被设为-1或1。虽然这些参数可以以-1和1等概率的方式任选,或者构成一个纠错码,但是被设计成一个相应字符类的7*12大小(即84)的格式化图片。这种表示对识别单独的数字不是很有用,但是对识别可打印ASCII集中的字符串很有用。
使用这种分布编码而非更常用的“1 of N”编码用于产生输出的另一个原因是,当类别比较大的时候,非分布编码的效果比较差。原因是大多数时间非分布编码的输出必须为0。这使得用sigmoid单元很难实现。另一个原因是分类器不仅用于识别字母,也用于拒绝非字母。使用分布编码的RBF更适合该目标。因为与sigmoid不同,他们在输入空间的较好限制的区域内兴奋,而非典型模式更容易落到外边。
RBF参数向量起着F6层目标向量的角色。需要指出这些向量的成分是+1或-1,这正好在F6 sigmoid的范围内,因此可以防止sigmoid函数饱和。实际上,+1和-1是sigmoid函数的最大弯曲的点处。这使得F6单元运行在最大非线性范围内。必须避免sigmoid函数的饱和,因为这将会导致损失函数较慢的收敛和病态问题。
5)训练过程
神经网络用于模式识别的主流是有指导学习网络,无指导学习网络更多的是用于聚类分析。对于有指导的模式识别,由于任一样本的类别是已知的,样本在空间的分布不再是依据其自然分布倾向来划分,而是要根据同类样本在空间的分布及不同类样本之间的分离程度找一种适当的空间划分方法,或者找到一个分类边界,使得不同类样本分别位于不同的区域内。这就需要一个长时间且复杂的学习过程,不断调整用以划分样本空间的分类边界的位置,使尽可能少的样本被划分到非同类区域中。
卷积网络在本质上是一种输入到输出的映射,它能够学习大量的输入与输出之间的映射关系,而不需要任何输入和输出之间的精确的数学表达式,只要用已知的模式对卷积网络加以训练,网络就具有输入输出对之间的映射能力。卷积网络执行的是有导师训练,所以其样本集是由形如:(输入向量,理想输出向量)的向量对构成的。所有这些向量对,都应该是来源于网络即将模拟的系统的实际“运行”结果。它们可以是从实际运行系统中采集来的。在开始训练前,所有的权都应该用一些不同的小随机数进行初始化。“小随机数”用来保证网络不会因权值过大而进入饱和状态,从而导致训练失败;“不同”用来保证网络可以正常地学习。实际上,如果用相同的数去初始化权矩阵,则网络无能力学习。
训练算法与传统的BP算法差不多。主要包括4步,这4步被分为两个阶段:
第一阶段,向前传播阶段:
a)从样本集中取一个样本(X,Yp),将X输入网络;
b)计算相应的实际输出Op。
在此阶段,信息从输入层经过逐级的变换,传送到输出层。这个过程也是网络在完成训练后正常运行时执行的过程。在此过程中,网络执行的是计算(实际上就是输入与每层的权值矩阵相点乘,得到最后的输出结果):
Op=Fn(…(F2(F1(XpW(1))W(2))…)W(n))
第二阶段,向后传播阶段
a)算实际输出Op与相应的理想输出Yp的差;
b)按极小化误差的方法反向传播调整权矩阵。
6)卷积神经网络的优点
卷积神经网络CNN主要用来识别位移、缩放及其他形式扭曲不变性的二维图形。由于CNN的特征检测层通过训练数据进行学习,所以在使用CNN时,避免了显式的特征抽取,而隐式地从训练数据中进行学习;再者由于同一特征映射面上的神经元权值相同,所以网络可以并行学习,这也是卷积网络相对于神经元彼此相连网络的一大优势。卷积神经网络以其局部权值共享的特殊结构在语音识别和图像处理方面有着独特的优越性,其布局更接近于实际的生物神经网络,权值共享降低了网络的复杂性,特别是多维输入向量的图像可以直接输入网络这一特点避免了特征提取和分类过程中数据重建的复杂度。
流的分类方式几乎都是基于统计特征的,这就意味着在进行分辨前必须提取某些特征。然而,显式的特征提取并不容易,在一些应用问题中也并非总是可靠的。卷积神经网络,它避免了显式的特征取样,隐式地从训练数据中进行学习。这使得卷积神经网络明显有别于其他基于神经网络的分类器,通过结构重组和减少权值将特征提取功能融合进多层感知器。它可以直接处理灰度图片,能够直接用于处理基于图像的分类。
卷积网络较一般神经网络在图像处理方面有如下优点: a)输入图像和网络的拓扑结构能很好的吻合;b)特征提取和模式分类同时进行,并同时在训练中产生;c)权重共享可以减少网络的训练参数,使神经网络结构变得更简单,适应性更强。
---------------------------------------------------------------------------------------------------->上面都是俺copy来的
作者不屑于讲里面参数的计算过程,俺看了好久好久才算出来,下面是我对作者上文中参数计算的理解:
1.C1层是一个卷积层(为什么是卷积?卷积运算一个重要的特点就是,通过卷积运算,可以使原信号特征增强,并且降低噪音),由6个特征图Feature Map构成。特征图中每个神经元与输入中5*5的邻域相连。特征图的大小为28*28,这样能防止输入的连接掉到边界之外(是为了BP反馈时的计算,不致梯度损失,个人见解)。C1有156个可训练参数(每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器,共(5*5+1)*6=156个参数),共156*(28*28)=122,304个连接。---->这一段作者说的很清楚,不用多说了
2.S2层是一个下采样层(为什么是下采样?利用图像局部相关性的原理,对图像进行子抽样,可以减少数据处理量同时保留有用信息),有6个14*14的特征图。特征图中的每个单元与C1中相对应特征图的2*2邻域相连接。S2层每个单元的4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid函数计算。可训练系数和偏置控制着sigmoid函数的非线性程度。如果系数比较小,那么运算近似于线性运算,亚采样相当于模糊图像。如果系数比较大,根据偏置的大小亚采样可以被看成是有噪声的“或”运算或者有噪声的“与”运算。每个单元的2*2感受野并不重叠,因此S2中每个特征图的大小是C1中特征图大小的1/4(行和列各1/2)。S2层有12个可训练参数和5880个连接。
---->6个2*2的小方框,每个有一个参数,加上一个偏置,也就是(1+1)*6=12个可训练参数
---->对于S2层的每一个图的每一个点,连接数是(2*2+1)=5,总共是14*14*6*(2*2+1)=5880个连接
3.C3层也是一个卷积层,它同样通过5x5的卷积核去卷积层S2,然后得到的特征map就只有10x10个神经元,但是它有16种不同的卷积核,所以就存在16个特征map了。这里需要注意的一点是:C3中的每个特征map是连接到S2中的所有6个或者几个特征map的,表示本层的特征map是上一层提取到的特征map的不同组合(这个做法也并不是唯一的)。
例如,存在的一个方式是:C3的前6个特征图以S2中3个相邻的特征图子集为输入。接下来6个特征图以S2中4个相邻特征图子集为输入。然后的3个以不相邻的4个特征图子集为输入。最后一个将S2中所有特征图为输入。这样C3层有1516个可训练参数和151600个连接。
---->这里的参数和连接数最不好理解啦
---->从C3的角度看,它有16个图.把每个图对应的参数加起来就行了:
6*(3*25+1) + 6*(4*25+1) + 3*(4*25+1)+ 1*(6*25+1)=1516个可训练参数
---->上面的1516*10*10=151600个连接
4.S4层是一个下采样层,由16个5*5大小的特征图构成。特征图中的每个单元与C3中相应特征图的2*2邻域相连接,跟C1和S2之间的连接一样。S4层有32个可训练参数(每个特征图1个因子和一个偏置)和2000个连接。
---->16个2*2的小方框,每个有一个参数,加上一个偏置,也就是(1+1)*16=32个可训练参数
---->对于S4层的每一个图的每一个点,连接数是(2*2+1)=5,总共是5*5*16*(2*2+1)=2000个连接
5. C5层是一个卷积层,有120个特征图。每个单元与S4层的全部16个单元的5*5邻域相连。由于S4层特征图的大小也为5*5(同滤波器一样),故C5特征图的大小为1*1:这构成了S4和C5之间的全连接。之所以仍将C5标示为卷积层而非全相联层,是因为如果LeNet-5的输入变大,而其他的保持不变,那么此时特征图的维数就会比1*1大。C5层有48120个可训练连接。
---->120*(5*5*16+1)=48120个可训练连接
6. F6层有84个单元(之所以选这个数字的原因来自于输出层的设计),与C5层全相连。有10164个可训练参数。如同经典神经网络,F6层计算输入向量和权重向量之间的点积,再加上一个偏置。然后将其传递给sigmoid函数产生单元i的一个状态。
---->84*(120+1)=10164个可训练连接
深入理解卷积:
http://blog.csdn.net/zouxy09/article/details/9993371
http://blog.csdn.net/celerychen2009/article/details/8973218
篇三:[lenet]剖析细节,说说深度学习的最经典模型 LeNet
近几年来,卷积神经网络(Convolutional Neural Networks,简称CNN)在图像识别中取得了非常成功的应用,成为深度学习的一大亮点。CNN发展至今,已经有很多变种,其中有几个经典模型在CNN发展历程中有着里程碑的意义,它们分别是:LeNet、Alexnet、Googlenet、VGG、DRL等,接下来将分期进行逐一介绍。
在之前的文章中,已经介绍了卷积神经网络(CNN)的技术原理,细节部分就不再重复了,有兴趣的同学再打开链接看看(大话卷积神经网络),在此简单回顾一下CNN的几个特点:局部感知、参数共享、池化。
1、局部感知
人类对外界的认知一般是从局部到全局、从片面到全面,类似的,在机器识别图像时也没有必要把整张图像按像素全部都连接到神经网络中,在图像中也是局部周边的像素联系比较紧密,而距离较远的像素则相关性较弱,因此可以采用局部连接的模式(将图像分块连接,这样能大大减少模型的参数),如下图所示:
2、参数(权值)共享
每张自然图像(人物、山水、建筑等)都有其固有特性,也就是说,图像其中一部分的统计特性与其它部分是接近的。这也意味着这一部分学习的特征也能用在另一部分上,能使用同样的学习特征。因此,在局部连接中隐藏层的每一个神经元连接的局部图像的权值参数(例如5×5),将这些权值参数共享给其它剩下的神经元使用,那么此时不管隐藏层有多少个神经元,需要训练的参数就是这个局部图像的权限参数(例如5×5),也就是卷积核的大小,这样大大减少了训练参数。如下图
3、池化
随着模型网络不断加深,卷积核越来越多,要训练的参数还是很多,而且直接拿卷积核提取的特征直接训练也容易出现过拟合的现象。回想一下,之所以对图像使用卷积提取特征是因为图像具有一种“静态性”的属性,因此,一个很自然的想法就是对不同位置区域提取出有代表性的特征(进行聚合统计,例如最大值、平均值等),这种聚合的操作就叫做池化,池化的过程通常也被称为特征映射的过程(特征降维),如下图:
回顾了卷积神经网络(CNN)上面的三个特点后,下面来介绍一下CNN的经典模型:手写字体识别模型LeNet5。
LeNet5诞生于1994年,是最早的卷积神经网络之一, 由Yann LeCun完成,推动了深度学习领域的发展。在那时候,没有GPU帮助训练模型,甚至CPU的速度也很慢,因此,LeNet5通过巧妙的设计,利用卷积、参数共享、池化等操作提取特征,避免了大量的计算成本,最后再使用全连接神经网络进行分类识别,这个网络也是最近大量神经网络架构的起点,给这个领域带来了许多灵感。
LeNet5的网络结构示意图如下所示:
LeNet5由7层CNN(不包含输入层)组成,上图中输入的原始图像大小是32×32像素,卷积层用Ci表示,子采样层(pooling,池化)用Si表示,全连接层用Fi表示。下面逐层介绍其作用和示意图上方的数字含义。
1、C1层(卷积层):6@28×28
该层使用了6个卷积核,每个卷积核的大小为5×5,这样就得到了6个feature map(特征图)。
(1)特征图大小
每个卷积核(5×5)与原始的输入图像(32×32)进行卷积,这样得到的feature map(特征图)大小为(32-5+1)×(32-5+1)= 28×28
卷积过程如下图所示:
卷积核与输入图像按卷积核大小逐个区域进行匹配计算,匹配后原始输入图像的尺寸将变小,因为边缘部分卷积核无法越出界,只能匹配一次,如上图,匹配计算后的尺寸变为Cr×Cc=(Ir-Kr+1)×(Ic-Kc+1),其中Cr、Cc,Ir、Ic,Kr、Kc分别表示卷积后结果图像、输入图像、卷积核的行列大小。
(2)参数个数
由于参数(权值)共享的原因,对于同个卷积核每个神经元均使用相同的参数,因此,参数个数为(5×5+1)×6= 156,其中5×5为卷积核参数,1为偏置参数
(3)连接数
卷积后的图像大小为28×28,因此每个特征图有28×28个神经元,每个卷积核参数为(5×5+1)×6,因此,该层的连接数为(5×5+1)×6×28×28=122304
2、S2层(下采样层,也称池化层):6@14×14
(1)特征图大小
这一层主要是做池化或者特征映射(特征降维),池化单元为2×2,因此,6个特征图的大小经池化后即变为14×14。回顾本文刚开始讲到的池化操作,池化单元之间没有重叠,在池化区域内进行聚合统计后得到新的特征值,因此经2×2池化后,每两行两列重新算出一个特征值出来,相当于图像大小减半,因此卷积后的28×28图像经2×2池化后就变为14×14。
这一层的计算过程是:2×2 单元里的值相加,然后再乘以训练参数w,再加上一个偏置参数b(每一个特征图共享相同的w和b),然后取sigmoid值(S函数:0-1区间),作为对应的该单元的值。卷积操作与池化的示意图如下:
(2)参数个数
S2层由于每个特征图都共享相同的w和b这两个参数,因此需要2×6=12个参数
(3)连接数
下采样之后的图像大小为14×14,因此S2层的每个特征图有14×14个神经元,每个池化单元连接数为2×2+1(1为偏置量),因此,该层的连接数为(2×2+1)×14×14×6 = 5880
3、C3层(卷积层):16@10×10
C3层有16个卷积核,卷积模板大小为5×5。
(1)特征图大小
与C1层的分析类似,C3层的特征图大小为(14-5+1)×(14-5+1)= 10×10
(2)参数个数
需要注意的是,C3与S2并不是全连接而是部分连接,有些是C3连接到S2三层、有些四层、甚至达到6层,通过这种方式提取更多特征,连接的规则如下表所示:
例如第一列表示C3层的第0个特征图(feature map)只跟S2层的第0、1和2这三个feature maps相连接,计算过程为:用3个卷积模板分别与S2层的3个feature maps进行卷积,然后将卷积的结果相加求和,再加上一个偏置,再取sigmoid得出卷积后对应的feature map了。其它列也是类似(有些是3个卷积模板,有些是4个,有些是6个)。因此,C3层的参数数目为(5×5×3+1)×6 +(5×5×4+1)×9 +5×5×6+1 = 1516
(3)连接数
卷积后的特征图大小为10×10,参数数量为1516,因此连接数为1516×10×10= 151600
4、S4(下采样层,也称池化层):16@5×5
(1)特征图大小
与S2的分析类似,池化单元大小为2×2,因此,该层与C3一样共有16个特征图,每个特征图的大小为5×5。
(2)参数数量
与S2的计算类似,所需要参数个数为16×2 = 32
(3)连接数
连接数为(2×2+1)×5×5×16 = 2000
5、C5层(卷积层):120
(1)特征图大小
该层有120个卷积核,每个卷积核的大小仍为5×5,因此有120个特征图。由于S4层的大小为5×5,而该层的卷积核大小也是5×5,因此特征图大小为(5-5+1)×(5-5+1)= 1×1。这样该层就刚好变成了全连接,这只是巧合,如果原始输入的图像比较大,则该层就不是全连接了。
(2)参数个数
与前面的分析类似,本层的参数数目为120×(5×5×16+1) = 48120
(3)连接数
由于该层的特征图大小刚好为1×1,因此连接数为48120×1×1=48120
6、F6层(全连接层):84
(1)特征图大小
F6层有84个单元,之所以选这个数字的原因是来自于输出层的设计,对应于一个7×12的比特图,如下图所示,-1表示白色,1表示黑色,这样每个符号的比特图的黑白色就对应于一个编码。
该层有84个特征图,特征图大小与C5一样都是1×1,与C5层全连接。
(2)参数个数
由于是全连接,参数数量为(120+1)×84=10164。跟经典神经网络一样,F6层计算输入向量和权重向量之间的点积,再加上一个偏置,然后将其传递给sigmoid函数得出结果。
(3)连接数
由于是全连接,连接数与参数数量一样,也是10164。
7、OUTPUT层(输出层):10
Output层也是全连接层,共有10个节点,分别代表数字0到9。如果第i个节点的值为0,则表示网络识别的结果是数字i。
(1)特征图大小
该层采用径向基函数(RBF)的网络连接方式,假设x是上一层的输入,y是RBF的输出,则RBF输出的计算方式是:
上式中的Wij的值由i的比特图编码确定,i从0到9,j取值从0到7×12-1。RBF输出的值越接近于0,表示当前网络输入的识别结果与字符i越接近。
(2)参数个数
由于是全连接,参数个数为84×10=840
(3)连接数
由于是全连接,连接数与参数个数一样,也是840
通过以上介绍,已经了解了LeNet各层网络的结构、特征图大小、参数数量、连接数量等信息,下图是识别数字3的过程,可对照上面介绍各个层的功能进行一一回顾:
墙裂建议
Ann LeCun 在1998年发表了关于LeNet的经典论文《Gradient-Based Learning Applied to Document Recognition 》(基于梯度学习在文档识别中的应用),里面有非常详细介绍,建议阅读这篇论文,进一步巩固知识。






