译者注
内容有部分增加与补充,阅读原文请点击这里
原作者的文章其实更利于读者对卷积本身的理解,但是实际上作者对卷积的现实意义的理解并没有解释的十分清楚,甚至可能不利于堵着的理解,也正因为如此我在翻译过程中可能对原文进行了比较大的改动,希望这对你有帮助.
实际上上卷积神经网络是来自神经学的研究,其计算过程实际上模拟了视觉神经系统的运算过程.这一部分内容其翻阅其他文章.
TensorFlow中该部分的内容请参考我的博客:卷积函数 和 池化函数
介绍
我曾经很懊悔过去一段时间没时间真正去理解深度学习。 过去一段时间通过话题上的研究论文和文章,感觉这是一个非常复杂的话题。 我试着理解神经网络及其各种类型,但它似乎很困难。
然后有一天,我决定要一步一个脚印,从头构建基础。 我决定要打破这些技术应用的步骤,做手工(计算)的步骤,直到我理解它们是如何工作的。 是时候采取如此大的努力了,但收获同样是惊人的。
现在,我不仅可以了解深度学习的范围,甚至我想出更好的方法,毕竟我的基础已经达到了。
今天,我要与你分享我的学习心得。 我将向您展示如何理解卷积神经网络。 我将带您亲历的我曾经的旅程,并通过这使你深刻的理解cnn是如何工作的。
在这篇文章中我将讨论卷积神经网络背后的架构,这是为了解决图像识别和分类问题。
我假设你对经网络是如何工作的有一个基本的了解神。 如果你不确定你的知识储备,那么 这篇文章 对你会有帮助(英文原文)。
1 机器如何“看到”一个图像?
人类的大脑是一个非常强大的机器。 我们看到每秒钟(捕捉)多个图像并处理处理它们,却又从未意识到这其中其实已经进行了极其复杂的运算,而对于机器而言显然没有这么容易。 其中图像处理的第一步是理解:如何表示一幅图像使得机器也可以读图
其表示方法为,每一个图像点(像素)的以矩阵的形式进行存储。 如果你改变订单或颜色的像素,图像也会改变。 让我们举一个例子。 让我们说,你想要存储和读取一幅写有数字4的图片。
机器会打“阅读”图像像素的矩阵和存储每个像素的颜色代码代表的位置。 在下面的描述中,1号是白色的,256是最黑暗的阴影绿色(实际上应该是黑色,但是为了便于表示每个像素的编号都以一种颜色,绿色的深浅程度进行表示)。
一旦确定了图像的存储格式,下一个挑战是我们的神经网络理解的安排和模式。
2 我们如何帮助一个神经网络来识别图像?
一个号码是由像素排列的不同组合表示。
首先,假设我们尝试使用一个传统的完全连接网络(全连接神经网络:fully connected network 译者注)来识别它吗,效果会怎样呢?
完全连接网络将这张照片作为数组通过压缩和考虑像素值作为特征来预测数字图像。 结果如下:
这是一个表示数字4。 我们已经完全失去了像素的空间排列,其结果好像是人类无法理解的。
而实际上我们应该做些什么呢? 我们应该做的是从图像中提取特征,并保存其空间分布,这样才算是使机器学会了“看懂”图像。那么我们到底应该怎么做呢?或许下面的案例可以帮助我们理解机器“看懂”图像的方法。’
案例1:
这里我们使用了一个权重乘以最初的像素值。
它为肉眼识别变得容易,这是一个4。 但再次发送这张照片到完全连接网络,其就会变一维数组。 这种方法无法保存图像的空间排列。
看来这种方式并不能帮助机器学习“看懂”图像
案例2:
通过上面的案例我们可以看到,一维化的图像完全破坏了它的空间排列。我们需要设计一种方法,将图像发送到一个网络,而不需要将图像拉平,并保留其空间布局。
让我们尝试一次获取图像的两个像素值,而不是只计算一个。这将为网络提供一个很好的视角来了解相邻的两个像素是怎样的。现在我们每次取两个像素,同样的我们也要取两个权重值来进行计算。
我希望你们注意到,图像现在从4列排列开始变成了一个3列排列。因为我们每次移动两个像素(像素在每个移动中得到共享),所有图像变得更小了。另外,要意识到的一个重要事实是,,因此这里只考虑水平排列,我们要连续使用两个连续的水平像素,而当我们考虑垂直元素时,我们将会使用两个在垂直方向上连续的权值(译者加)。
这是一种从图像中提取特征的方法。 我们观察图像处理后的不同部分,其中右边不像原来那样清晰了。 这是由两个原因造成的:
- 左边和右边的角落图像像素只乘了权重一次。
- 左侧部分颜色仍然很深,因为权重值较高,而右侧部分颜色由于较低的权重而略有下降。
现在我们有两个问题,我们有两个解决方案来解决这些问题。
案例3:
遇到的问题是图像的左和右角落正在通过的权重只有一次。 解决方案使左右边缘像素和图片中的其它像素一样能够与权重进行多次相乘。
我们有一个简单的解决方案来解决这个问题: 图像两边加上数值全部为零的列。
如图所示,通过添加数值为零的列,边缘信息得以保留,图像的大小也随之变大。 当我们不想要图像大小减小时,我们可以使用这种方法.
同样的我们也可以通过补零来使输入输出的图像大小一致,这种两种不同的卷积方式,在TensorFlow中都可以通过简单的参数控制来实现,输入输出图像的大小计算方法,在下文中会提及(译者加)
案例4:
我们试图解决的问题是,一个更小的重量值右边角落里减少像素值从而使我们的神经网络难以真的读读图。 我们能做的是使用多个权重值并在计算后把它们放在一起。
权重(0.3)给了我们一个输出的形式
人后权重(0.1,5)会给我们另外一个输出的形式
这两个图像的组合版本将给我们一个非常清晰的图片。因此,我们所做的是简单地使用多个权重,而不是只使用一个权重来尝试保留关于图像的更多信息。而本例子中的最终输出将是上述两个图像的组合版本。
例5:
直到现在我们使用的权重一起处理水平像素。 但在大多数情况下,我们需要在水平和垂直方向保留图像的空间排列。 我们可以用权重的二维矩阵在水平和垂直方向与像素同时相乘。 同时,请记住,因为我们两个水平和垂直运动的权重,输出是一个像素在水平和垂直方向维度都更低的图像,也就是输出的图像比输入图像小了。
特别感谢杰里米·霍华德的鼓舞我创建这些视觉效果。
上面的案例中,我们到底做了什么呢?
上面我们所做的是,试图从图像中提取特征,并保留图像的空间排列。 理解图像的极其重要一点就是的了解像素排列。 而,上面我们所做的其实就是卷积神经网络所做的事情。 我们可以把输入图像用我们定义的权值矩阵(卷积核)进行卷积操作,从而获取我们想要的结果。
这种方法有另一个好处是,它减少了参数的数量
3。 定义一个卷积神经网络
我们需要三个基本组件来定义一个基本卷积网络。
- 卷积的层
- 池层(可选)
- 输出层
让我们看看这些详细
3.1 卷积层
在这一层,就是我们看到的在案例五即之后之后的例子。 假设我们有一个图像的大小6 * 6。 我们定义了一个权重矩阵中提取图像的某些特性
我们已经初始化了体重3 * 3矩阵。 这个权重应当对每个像素都进行运算,给一个卷积的输出
6 * 6的图像现在转换成一个4 * 4的形象。 把权重矩阵像画笔画一堵墙。想象一下重量矩阵就像油漆刷画墙一样。画笔首先水平地画出墙壁中的一行,然后向下,在水平地画下第二行,然后向下画出第三行,直到整面墙壁都被粉刷完毕。而当权重矩阵沿着图像移动时,像素值再次被使用。 再次当权重矩阵沿着图像像素才一次被使用。 这个特性允许在卷积神经网络中进行参数共享。
让我们看看真实的效果。
权重矩阵的行为像一个过滤器的图像从原始图像中提取特定的信息矩阵。 重量组合可能是提取边缘,而另一个可能一个特定的颜色,而另一个可能会模糊不需要的噪声,通过不同的卷积核操作,我们通过计算机实现了对图像不同特征的提取或者其他操作(译者加)
权重是这样学习的:最小化类似于一个MLP的损失函数。因此,从原来的图像中提取出一些特征来帮助网络进行正确的预测。当我们有多个卷积层时,初始层会提取更多的泛型特征,而随着网络的深入,权重矩阵提取的特征变得越来越复杂,同时也更适合于图像的识别与处理。
步长和填充(补零)的概念
在我们之前的案例中,过滤器或权重矩阵,在整个图像移动的一次只移动 一个 像素。 其移动的像素数量,我们称之为步长,下图是步长为 2 的例子
正如你所看到的图像大小的继续减少当我们增加步长的值。 而在图像中增加为零的维度则帮助我们解决了这个问题。 如下:
我们可以看到图像的初始形状是如何保留在我们用零填充图像。 这就是所谓的 相同的填充 由于输出图像具有相同的大小作为输入。
通过这种方式,我们保留了更多的来自图像边界的信息,并保留了图片的大小。
多个过滤器和激活映射
要记住的一件事是,深度尺寸的权重和输入图像的深度尺寸一样。 权重延伸到整个输入图像的深度。 因此,与单个权重卷积矩阵与一个输出卷积结果的深度维度。 同时因为在大多数情况下我们有相同的多个卷积核一起应用,那么我们将会获取的图像数量也会随着卷积核数量的增加而增加。
每个过滤器的输出是堆叠在一起形成的深度尺寸卷积图像。 假设我们有一个输入图像的大小32 * 32 * 3。 我们应用10个大小为5 * 5 * 3的卷积核对其进行卷积(方式为不填充数值为0的维度)。那么卷积层的 输出会维度将会是28 * 28 * 10。
你可以想象这是- - -
3.2 池层
有时候图片太大,我们需要减少可训练的参数。这时候我们可以在不同的卷积层之间添加池层。 池的唯一目的是减少图像的空间大小。 池是独立在每个深度尺寸,因此图像的深度保持不变。 最常见的池层一般采用最大池化(另外一种方式为平均池化)。
同时池化层也强化了神经网络的鲁棒性(抗干扰性),使得目标图像在图像位置中的轻微形变对神经网络最终预测的影响变小(译者加)
这里我们已经大步,而池大小也是2。 最大池化操作应用于每个深度维度的卷积的输出。 正如你所看到的,4 * 4卷积后输出已成为2 * 2马克斯池操作。
让我们看看最大池化的实际应用。
正如你所看到的我已经复杂的图像,应用最大池化。 最大池化后的图像仍然保留信息,这是一个汽车在一条街上。 如果你仔细看,其尺寸图像已经减半。 这可以在很大程度上减少运算。
类似的其他形式的池化比如平均池化或L2范数池化。
输出尺寸
上面的内容可能会使你混淆每一层的输出尺寸。 所以我决定使用下面的内容让你能够识别输出尺寸。 在卷积层中,有三个关键控制着输出尺寸的大小
- 过滤器的数量 ——输出音量的深度就等于滤波器应用的数量。 每个滤波器(卷积核)可以输出一个图片,卷积核增加,输出图片的数量增加
- 步长 ——控制着卷积核向下移动的像素值。 高步值长时我们跨过的像素值,因此产生较小的输出量。
- 补零 ——这有助于我们保持输入图像的大小。 如果只在原始图像周围添加一个补零的层数,并且步长为一,那么输出将保留原始图像的大小。
我们可以应用一个简单的公式来计算输出尺寸。 输出图像的空间大小可以计算(W-F + 2 p / S)+ 1。 这里,W是输入图片大小,F是卷积核的大小,P是填充应用的数量和S是步长的数量。 假设我们有一个输入图像的大小32 * 32 * 3,我们应用10过滤器的大小3 * 3 * 3,与单步和补零。
W = 32,F = 3,P = 0和S = 1。 输出深度等于过滤器应用的数量即10。
输出音量的大小将(32-3 + 0)/ 1 + 1 = 30。 因此,输出音量将30 * 30 * 10。
3.3 输出层
以,图像识别为例,多层的卷积和池化之后,我们需要输出一个类别的形式。 卷积和池层只是从原始图像中提取了特征, 然而,想要生成最终的输出,我们需要在卷积层之后假如一个全连接层,并使全连接层的输出等于我们的实际的图像分类的类别。 卷积层最后的图像而我们只需要输出图像是否属于一个特定的类。 输出层有一个像分类交叉熵那样的损失函数,来计算预测中的误差。一旦转发完成,反向传播就开始更新错误和减少损失的权重和偏差。
4 把它们放在一起,整个网络如何看起来像什么?
CNN现在可以看到由各种卷积和汇聚层。 让我们来看看网络的样子。
- 我们首先通过一个输入图像卷积层。 获得复杂的输出作为一个激活地图。 卷积过滤器应用于层从输入图像中提取相关特征进一步通过。
- 每个过滤器应当给予不同的特性来帮助预测正确的类。 以防我们需要保留图像的大小,我们使用相同的填充(补零),其他明智有效填充,因为它有助于减少使用的数量特征。
- 池层然后进一步减少参数的数量
- 几个卷积和池层添加之前的预测。 卷积层帮助提取特征。 网络中我们更深更具体的特性提取相比浅网络特征提取更通用。
- CNN如前所述的输出层是一个完全连接层,其他层的输入在这里被变成一维数据,发送输出转换成类的数量,所期望的网络。
- 然后生成的输出通过输出层和输出层的误差比较。 其中定义的一个损失函数是完全连接输出层计算均方损失。 然后计算误差的梯度。
- 错误然后backpropagated更新过滤器(权重)和偏差值。
- 一个训练周期完成在一个向前和向后传递。
5 使用在KERAS CNN分类图像
让我们尝试以一个例子,我们输入一些猫和狗的图片,我们试着对这些图像进行分类成各自的动物种类。 这是一个典型的图像识别和分类的问题。 这台机器需要做什么是需要看到图片和理解的各种特性是否一只猫或狗。这些特性可能是提取边缘,或提取出猫的胡须等等。卷积层的作用就是提取这些特征。 数据集可以点击这里
这些都是一些图像数据集的例子。
我们将首先需要调整这些图片让他们都在相同的形状。 这是我们通常需要在处理图像时,由于在捕捉图像,是不可能捕获所有相同大小的图像。
简单的你的理解我刚刚使用一个卷积层和一个池层,一般不会发生当我们试图做出预测。
# import各种包
import os
import numpy as np
import pandas as pd
import scipy
import sklearn
import keras
from keras.models import Sequential
import cv2
from skimage import io
%matplotlib inline
# 定义文件路径
cat=os.listdir("/mnt/hdd/datasets/dogs_cats/train/cat")
dog=os.listdir("/mnt/hdd/datasets/dogs_cats/train/dog")
filepath="/mnt/hdd/datasets/dogs_cats/train/cat/"
filepath2="/mnt/hdd/datasets/dogs_cats/train/dog/"
# 加载图片
images=[]
label = []
for i in cat:
image = scipy.misc.imread(filepath+i)
images.append(image)
label.append(0) #for cat images
for i in dog:
image = scipy.misc.imread(filepath2+i)
images.append(image)
label.append(1) #for dog images
# 确定所有图像的大小
for i in range(0,23000):
images[i]=cv2.resize(images[i],(300,300))
# 将图像转换为数组
images=np.array(images)
label=np.array(label)
# 定义超参数
filters=10
filtersize=(5,5)
epochs =5
batchsize=128
input_shape=(300,300,3)
# 将目标变量转换为所需的大小
from keras.utils.np_utils import to_categorical
label = to_categorical(label)
# 定义模型
model = Sequential()
model.add(keras.layers.InputLayer(input_shape=input_shape))
model.add(keras.layers.convolutional.Conv2D(filters, filtersize, strides=(1, 1), padding='valid', data_format="channels_last", activation='relu'))
model.add(keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(units=2, input_dim=50,activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(images, label, epochs=epochs, batch_size=batchsize,validation_split=0.3)
model.summary()
在这个模型中,我只使用了一个卷积和池层,可训练的参数是219,801。如果我在这种情况下使用了一个MLP,我将会有多少输出结果呢?您可以通过添加更多的卷积和池层来进一步减少参数的数量。我们添加的更多的卷积层网络结构和训练将会更复杂,但是同样的结果也会更好。
最后指出
我希望通过这篇文章能让你们对卷积神经网络有一个直观的认识。我没有深入研究CNN的复杂数学。如果你喜欢理解同样的东西——请继续收看,你会有更多的选择。尝试建立你自己的CNN网络,了解它是如何运作的,并对图像做出预测。请让我知道您的发现和使用评论部分的方法。