当前位置:   article > 正文

卷积神经网络实战

卷积神经网络实战

一. CNN实现(TensorFlow)

在TensorFlow中,每一个图像都有一个3D的tensor,shape为[height, width, channels]。每一个mini-batch是以一个shape为[min-batch size, height, width, channels]的4Dtensor来表示的。卷积层的权重是以shape为[f_h, f_w, f_n, f_n'],其中偏置项是以1D tensor[f_n]表示。接下来,我们一起看下,在TensorFlow中是怎么实现的:首先,代码中用sklearn中的load_sample_images()来加载图片。然后手工创建了两个7x7的卷积核,一个为水平直线,一个为竖直直线。接着让图像通过一个卷积层conv2d()(边界扩充0,stride = 2),最后画出一个特征图。如下:

  1. 1import numpy as np
  2. 2from sklearn.datasets import load_sample_images
  3. 3# Load sample images
  4. 4dataset = np.array(load_sample_images().images, dtype=np.float32)
  5. 5batch_size, height, width, channels = dataset.shape
  6. 6# Create 2 filters
  7. 7filters_test = np.zeros(shape=(77, channels, 2), dtype=np.float32)
  8. 8filters_test[:, 3, :, 0= 1 # vertical line
  9. 9filters_test[3, :, :, 1= 1 # horizontal line
  10. 10# Create a graph with input X plus a convolutional layer applying the 2 filters
  11. 11= tf.placeholder(tf.float32, shape=(None, height, width, channels))
  12. 12convolution = tf.nn.conv2d(X, filters, strides=[1,2,2,1], padding="SAME")
  13. 13with tf.Session() as sess:
  14. 14    output = sess.run(convolution, feed_dict={X: dataset})
  15. 15plt.imshow(output[0, :, :, 1]) # plot 1st image's 2nd feature map
  16. 16plt.show()

其中X是一个mini-batch(4D的tensor),filters也是一个4D的tensor,stride是一个有4个元素的1D tensor其中中间连个值为竖直和水平的stride,第一个元素和第四个元素必须是1,这个是备用的,以后可能会用在batch和channel上。padding必须设置为“VALID”或“SAME”,当设为“VALID”的时候,卷积层不进行边界扩充,但是可能会根据stride来忽略图像下面的某些行或右侧的某些列。如果设置为“SAME”,卷积层会进行对边界扩充0.这种情况下,输出神经元个数等于输入神经元个数除以stride,在下面的例子中输出层神经元为3,其中stride为5.如下:

所以,卷积层有一些参数要设置,比如:卷积核的个数,卷积核的高,卷积核的宽,以及stride,padding的类型。有时候,可以通过交叉验证来找最优的参数组合,但是这样往往是比较耗时的。后面我们会介绍一些通用的网络结构,可能会给我们一些启发,在实践中什么样的参数组合通常会得出最优的性能。


二. CNN之内存计算

卷积神经网络的一个问题就是需要大量的内存来处理数据,特别是在training阶段,因为反向传输需要保留前向传输的数据。举个例子,比方一个卷积层,其中卷积核为5x5,输出200个特征图,每一个特征图大小为150x100, stride为1,padding为"SAME",如果输入为一个150x100的RGB(3通道)图像的话,那么权重参数的个数为:

(5x5x3+1)x200 = 15200

其中+1为考虑到偏置项,相对来说,参数比全连接层要少。然而,每一个特征图包括150x100个神经元,每一个神经元需要计算5x5x3=75个权重,那么总共就有225million个浮点型数据相乘,虽然没有比全连接更糟,但是仍然是一个巨大的计算。如果特征图用32位float表示的话,那么一个卷积层将会占用:

200x150x100x32 = 96million bits

约11.4M内存,然而这只是一个样本,如果每个batch有100张图的话,那么单单这一层卷积层就要耗费超过一个G的内存。

在预测的时候,当一个卷积层计算的时候,就会将上一层所占用的内存释放掉,所以仅仅需要两个连续卷积层所占的内存即可。但是在计算的时候,每一次向前传输的数据都要为向后传输而保留,所以所需要的内存至少为全部层所占用的内存总和。

如果在training的时候,由于内存的问题导致crash,那么可以通过减少mini-batch的size来进行降低内存占有。当然也可以通过stride降维,或者减少一些层,甚至可以用16bit的float代替32bit的float或者多个设备来跑。

接下来,我们一起看一下CNN的另一个重要的构成:池化层。


三. CNN之池化层

一旦我们理解了卷积层的工作原理之后,池化层就相对来说比较简单了,池化层的目标就是为了降低计算负载,内存使用,参数数量,也可以降低过拟合的风险,而对输入进行的下采样。原理和卷积层一样,池化层的每一个神经元和上一层中有限区域(一个矩形的感受野)的神经元相连接。我们也必须定义感受野的size, stride,以及padding类型。然而不同的是,池化层的神经元没有权重,它的输出仅仅是输入区域的最大值或均值,下图显示了一个最大值的池化层,也是最常用的池化层,这个例子中用了一个2x2的池化核,stride=2,没有padding,也就是说,仅仅输出上一层中池化核位置的最大值到下一层。其他的输入都丢掉。

一般池化层在每一个输入通道上单独工作,所以经过池化层后,输入输出通道数不变。

在TensorFlow中实现池化还是蛮简单的,下面的代码创建一个2x2的池化核的池化层,stride为2,没有padding,然后应用到所有图像上。

  1. 1[...] # load the image dataset, just like above
  2. 2# Create a graph with input X plus a max pooling layer
  3. 3= tf.placeholder(tf.float32, shape=(None, height, width, channels))
  4. 4max_pool = tf.nn.max_pool(X, ksize=[1,2,2,1], strides=[1,2,2,1],padding="VALID")
  5. 5with tf.Session() as sess:
  6. 6    output = sess.run(max_pool, feed_dict={X: dataset})
  7. 7plt.imshow(output[0].astype(np.uint8)) # plot the output for the 1st image
  8. 8plt.show()

上面参数ksize包括了池化核的4D参数[batch size, height, width, channels].TensorFlow目前暂时不支持跨样本池化,所以第一个数据必须是1,由于目前也不支持跨通道池化,所以最后一个参数也得是1.当然,想创建一个均值的池化层的话,仅仅将上面的max_pool替换为avg_pool即可。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/121925
推荐阅读
相关标签
  

闽ICP备14008679号