您的位置:首页 > 理论基础 > 计算机网络

卷积神经网络之一:基本原理

2015-07-05 14:24 405 查看

动机

卷积神经网络是多层感知机的一个变种,其思想源自Hubel和Wiesel对猫的视觉皮层的研究工作。研究表明,视觉皮层存在由一种叫“感受野”细胞构成的复杂分布。这些细胞局部图像敏感,它们以某种方式覆盖整个视觉域,从而能够更好地挖掘图像中目标的空间关系信息。

总结起来卷积神经网络的特性体现在两个方面:1. 神经元间的连接是非全连接的。2. 同一层中某些神经元之间的连接的权重共享。它的非全连接权值共享的网络结构降低了网络模型的复杂度,减少了权值的数量。



图1 非全连接与局部权重共享

上左图是一个全连接网络,假设输入一个有103×103像素的图像,经过一个106个隐含层神经元,则隐含层神经元与图像像素点就有103×103×106=1012个连接,也就是
4000
1012个权值参数。上右图是一个局部连接网络,106个隐含层神经元与图像像素同位置附近的10×10的窗口相连接,则此时的参数个数为10×10×106=108个参数。其权值参数的个数比全连接减少了四个数量级。

对于上图中被标注为红色节点的净输入,就等于所有与红线相连接的像素点特征值与红色线表示的权值之积的累加。这样的计算过程就称为卷积。另外,右图中相同颜色线上标注的权重值是相同的,这就是权值共享

更为直观的非全连接权值共享示意图如下:



图2 非全连接(上图中,上层节点只与下层对应位置以及左右相邻的两个节点进行连接。)



图3 权重共享(上图中,layer m-1的左边1-3节点与layer m的左边第1个节点连接边上的权重值相同,这便是权重共性的含义,但是layer m-1的左边2-4节点与layer m的左边第2个节点的权重值不一定与layer m-1的左边1-3节点与layer m的左边第1个节点连接边上的权重值相同,同时不同的feature_map之间对应位置的连接也不共享参数;另外需要强调的是视觉野与相邻的视觉野之间是存在重叠区域的)

CNN的结构

卷积神经网络是一个多层的神经网络,每层由多个二维平面(feature map)组成,而每个平面由多个独立神经元组成。其结构包括如下形式的约束:

特征提取。每一个神经元从上一层的局部区域得到输入,然后提取局部特征。

特征映射。每一个计算层都是由多个特征映射(多个feature map)组成的,每个特征映射都是平面形式的。平面中每个神经元在约束下共享相同的连接权值集(与上一层对应视觉野范围内节点的所有连边),这样减少了参数数量个减轻了计算负担。

子抽样。卷积层后面是一个局部子抽样的计算层,由此特征映射(feature map)的分辨率降低,以降低对变形的敏感度。子抽样层对应与池化过程,此处会有一个池化窗口,池化窗口是不重叠的。

下图是一个卷积神经网络的例子:



图4 卷积神经网络示意图(C层代表卷积层,S层代表池化层(采样层),S4层之后是一个将高维特征一维化过程,再将多个channel的特征拼接起来作为多层感知机的输入。

卷积层为特征提取层,每个神经元的输入与前一层的局部感受野相连,并提取该局部的特征,一旦该局部特征被提取后,它与其他特征间的位置关系也随之确定下来。

采样层是特征映射层,网络的每个计算层由多个特征映射组成,每个特征映射为一个平面,平面上所有神经元的权值相等。特征映射结构采用影响函数核小的sigmoid函数作为卷积网络的激活函数,使得特征映射具有位移不变性。

卷积与池化的代码理解

为了更为清晰的认识卷积和池化过程,这里借用Theano来说明,选用的例子与代码来自于基于Theano的DeepLearning 0.1 documentation 。为了简单化,对原始代码做了改写,同时提供了一个debug函数用于查看运算过程中的维度变化(debug与run函数的效果是一样的),代码说明请见注释。

代码的显示输出为:



# -*- coding:utf8 -*-

import numpy as np
import theano
import pylab
from PIL import Image
from theano import tensor as T
from theano.tensor.nnet import conv
from theano.tensor.signal import downsample
from theano.tensor import squeeze
from theano import shared

rng = np.random.RandomState(23455)

class ConvLayer(object):
def __init__(self,rng, X, c_size):
self.X = X  #四维张量
#############################################
wbound = np.sqrt(np.prod(c_size[1:])) # np.prod(c_size[1:]) = channel * 视觉野的高 * 视觉野的宽
W_values=np.asarray(rng.uniform(low = -1.0 / wbound,high = 1.0 / wbound,size=c_size), theano.config.floatX)
W = shared(value=W_values,name='W',borrow=True)
#################
b = shared(value=np.zeros((c_size[0],),dtype=theano.config.floatX),name='b',borrow=True)
self.W=W
self.b=b
#############################################
self.output = T.nnet.sigmoid(conv.conv2d(self.X, self.W) + b.dimshuffle('x', 0, 'x', 'x'))
self.params=[self.W,self.b]

class SampleLayer(object):
def __init__(self, X, pool_size):
self.X = X
print pool_size
self.pooled_X = downsample.max_pool_2d(X, pool_size, ignore_border = False)
self.output = squeeze(self.pooled_X)
pass

def run():
##############################################
rng = np.random.RandomState(23455)
w_shp = (2, 3, 9, 9) #2代表feature map的个数,每个feature map的尺寸为9*9, 3为输入图片的channel数(或者理解为上一层的feature map数)
X = T.tensor4(name='X')
convlayer = ConvLayer(rng, X, w_shp)
f = theano.function([X], convlayer.output)
##############################################
img = Image.open(open('3wolfmoon.jpg'))
#img的shape为(639,516,3),图片的像素大小为639*516,3为channel数,对应于图片的RGB值,这里除以256是为了将特征归一化。
img = np.asarray(img, dtype='float64') / 256.
#img.transpose(2, 0, 1)是交换数据的维度,从(639,516,3)变为(3,639,516)
#reshape(1, 3, 639, 516)相当于对img升维,在img.transpose(2, 0, 1)的基础上插入一维,得到(1, 3, 639, 516)
img_ = img.transpose(2, 0, 1).reshape(1, 3, 639, 516)
filtered_img = f(img_)
##############################################
pool_size = (2, 2)
samplelayer = SampleLayer(X, pool_size)
s = theano.function([X], samplelayer.output)
##############################################
pool_img = s(filtered_img)
##############################################
#在第一行第一列绘制原图
pylab.subplot(2, 3, 1); pylab.axis('off'); pylab.imshow(img)
pylab.gray();#显示灰度图,注释掉此行所得到的图片会是有颜色的
#在第一行第二列绘制经过卷积后的第一个feature map
pylab.subplot(2, 3, 2); pylab.axis('off'); pylab.imshow(filtered_img[0, 0, :, :])
#在第一行第三列绘制经过卷积后的第二个feature map
pylab.subplot(2, 3, 3); pylab.axis('off'); pylab.imshow(filtered_img[0, 1, :, :])
#在第二行第一列绘制原图
pylab.subplot(2, 3, 4); pylab.axis('off'); pylab.imshow(img)
#在第二行第二列绘制经过卷积后的第一个feature map,再经过最大池化采样后的图
pylab.subplot(2, 3, 5); pylab.axis('off'); pylab.imshow(pool_img[0, 1, :, :])
#在第二行第三列绘制经过卷积后的第二个feature map,再经过最大池化采样后的图
pylab.subplot(2, 3, 6); pylab.axis('off'); pylab.imshow(pool_img[0, 1, :, :])
pylab.show()

def debug():
####################################
sW = T.tensor4()
sigmoidf = T.nnet.sigmoid(sW)
####################################
tW = T.tensor4()
tX = T.tensor4()
convf = conv.conv2d(tX, tW)
####################################
pX = T.tensor4()
pool_size = (2, 2)
ds = downsample.max_pool_2d(pX, pool_size)
####################################
X = Image.open(open('3wolfmoon.jpg'))
X = np.asarray(X, dtype='float64') / 256.  # X是一个三维array,shape为(height = 639,width = 516,channel = 3),同时对数值进行归一化处理,channel对应于一个像素的RGB值
X_ = X.transpose(2, 0, 1).reshape(1, 3, 639, 516) #将channel移到第一维,同时在之上再增加一维,X_是一个四维张量,张量的维度为(1, 3, 639, 516)
####################################
#卷积
rng = np.random.RandomState(23455)
w_shp = (2, 3, 9, 9) #feature_map = 2, channel = 3, height = 9,width = 9,feature_map同时对应于滤波器的个数
wbound = np.sqrt(np.prod(w_shp[1:]))# np.prod(w_shp[1:]) = 3 * 9 * 9
W = np.asarray(rng.uniform(low = -1.0 / wbound,high = 1.0 / wbound,size = w_shp), dtype = np.float32)
b = np.zeros((w_shp[0],),dtype=np.float32) # b.shape = (2, )#w_shp[0] = feature_map = 2,对应于各个滤波器的偏置
output = sigmoidf.eval({sW : convf.eval({tX : X_, tW : W}) + b.reshape((1, b.shape[0], 1, 1))})# output.shape = (1, 2, 631, 508), 631= 639 - 9 + 1, 508 = 516 - 9 + 1
####################################
#池化
pds = ds.eval({pX : output}) #pds.shape = (1, 2, 316, 254)
poutput = np.squeeze(pds)
####################################
#可视化
pylab.subplot(2, 3, 1); pylab.axis('off'); pylab.imshow(X) #绘制原图
pylab.gray();
pylab.subplot(2, 3, 2); pylab.axis('off'); pylab.imshow(output[0, 0, :, :]) #绘制第一个特征图
pylab.subplot(2, 3, 3); pylab.axis('off'); pylab.imshow(output[0, 1, :, :]) #绘制第二个特征图
pylab.subplot(2, 3, 4); pylab.axis('off'); pylab.imshow(X)
pylab.subplot(2, 3, 5); pylab.axis('off'); pylab.imshow(poutput[1, :, :]) #绘制第一个特征图的池化后的结果
pylab.subplot(2, 3, 6); pylab.axis('off'); pylab.imshow(poutput[1, :, :]) #绘制第二个特征图的池化后的结果
pylab.show()
pass

if __name__ == "__main__":
run()
debug()
pass


需要说明的几点:

1. w_shp = (2, 3, 9, 9)的第2维参数必须要与输入图片的channel保持一致

2. 关于各层输出维度的说明:

a. 输入层:输入维度为(1,3,639, 516),视觉野的大小为9*9,feature map的个数为2,那么由此可以推断出,卷积层输出为(1,2,639-9+1,516-9+1)=(1,2,631,508)

b. 池化层:池化采样的窗口大小为(2, 2),则池化层的输出维度为(1,2,⌈631/2⌉,⌈508/2⌉)=(1,2,315,254)

参考资料

1.Theano下的deep learning 教程:http://www.deeplearning.net/tutorial/lenet.html#lenet

2.http://blog.csdn.net/zouxy09/article/details/8781543

3.http://blog.csdn.net/celerychen2009/article/details/8973218

4.http://blog.csdn.net/tiandijun/article/details/21822641

5.http://blog.csdn.net/shouhuxianjian/article/details/40863779

6.http://blog.csdn.net/whiteinblue/article/details/25281459

7.http://blog.csdn.net/whiteinblue/article/details/22159847

8.http://blog.csdn.net/u014568921/article/details/45222623

9.http://blog.csdn.net/nan355655600/article/details/17690029

10.http://blog.csdn.net/qiaofangjie/article/details/18042407

11.http://blog.csdn.net/llx1990rl/article/details/41864523

12.http://blog.csdn.net/wds555/article/details/44100581

13.http://blog.csdn.net/happyer88/article/details/46762823

14.http://blog.csdn.net/bemachine/article/details/15026507

15.http://blog.csdn.net/hlx371240/article/details/41677805

16.http://blog.csdn.net/Liuxz_x/article/details/43343559

17.http://blog.csdn.net/t0903/article/details/46271709

18.http://blog.csdn.net/achaoluo007/article/details/40958239

19.http://blog.csdn.net/u010457344/article/details/39586045

20.http://www.cnblogs.com/ronny/p/ann_03.html

21.http://www.cnblogs.com/fengfenggirl/p/cnn_implement.html

22.http://www.cnblogs.com/loujiayu/p/3545155.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: