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

换脸原理,使用GAN网络再造ZAO应用:使用卷积网络提升图像识别率

2020-04-07 12:09 1151 查看

上一节我们使用全连接网络来对图片内容进行识别,正确率大概在50%左右,准确率不高的原因在于,网络没有对像素点在二维平面上的分布规律加以考量。如果对神经网络引入卷积运算,网络就能具备识别像素点在空间上分布的规律,我们先看看什么是卷积操作。

卷积操作简单来说是对两个矩阵进行点和点的乘机后求和,例如下面运算:

运算将两个矩阵中的元素依次相乘后求和,也就是0.6*1+0.2*1+0.6*1+0.1*0±0.2*2±0.3*0±0.5*-1±0.1*-1±0.3*-1=2.3
左边矩阵其实可以对应于图片像素点组成的二维数组,因此左边矩阵的维度完全可以比右边矩阵大,假设左边矩阵是10行10列,右边矩阵保持3行3列不变,那么右边矩阵就可以通过“扫描”的方式进行卷积运算,右边矩阵也称为卷积运算的kernel。

例如从第0行第0个元素开始从它的右边选择3列,从它下面选择3行所形成的矩阵与右边矩阵做卷积得一个结果,然后从第0行第1列元素开始从右边选择3列,从下标选择3行然后进行卷积运算,如此依次进行,如下面动图所示:

注意看每次卷积运算时,每次挪动一个元素,这种挪动的幅度叫做stride,如果stride == 2,那么每次横线或纵向移动两个元素后才进行卷积操作。我们也注意到卷积运算后所得结果相比于原来矩阵有所“缩水”,为了保证卷积后的结果与卷积前一样大,我们可以先在运算前对左边矩阵进行“填充”,也就是使用数值0增加左边矩阵的行数和列数,使得增加后矩阵在卷积运算后大小与原来一样,如下图所示:

上图中下面蓝色部分对应卷积运算的左边矩阵,阴影部分对应kernel的大小,从图中看kernel的维度是[3,3],我们先用0填充蓝色部分对应的句子,也就是上图中的白色方块,绿色部分是卷积运算后所得结果,我们看到绿色部分的大小与白色部分相同。

卷积操作时常有的图像处理算法,不同的kernel经过卷积操作后能将图片中的某些信息抽取出来,例如下图所示的两个kernel能分别将图片中物体的水平边缘和竖直边缘抽取出来:

卷积神经网络的目的,其实就是找出一系列kernel,这些kernel能够从图片中抽取特定信息从而能帮助网络识别图片中的物体。我们看keras给我们提供的卷积网络层构造接口:

conv_layer1 = Conv2D(filters = 2, kernel_size = (3,3), stride = 1,
padding = 'same])(input_layer)

代码中input_layer对应前面描述卷积操作中的左边矩阵,filters指定右边矩阵的个数,kernel_size指定右边矩阵的维度,如果有2个kernel,那么就有两个计算结果,这两个结果会“重叠“在一起,例如经过一次卷积后所得结果是维度为[5,5]的矩阵,那么两个kernel完成卷积后所得结果就是维度为[2,5,5]的三维矩阵。值得注意的是我们在前面例子中的kernel是一个3*3的矩阵,这是相对于左边也是二维矩阵而言,通常情况下,由于图片是RGB格式,因此每个像素点由三个值组成,所以对于输入规格为32*32的图片,它对应三维矩阵[32, 32, 3]。

这就相当于3个32*32的二维矩阵叠在一起,此时右边kernel也会相应变成[3,3,3]形式,也就是kernel也变成3层,因此卷积时就不再是两个3*3矩阵之间的元素相乘后求和,而是两个3*3*3的立方体对应元素之间相乘后再求和。这里kernel的”高度“在上面Conv2D调用中没有显示出来,kernel_size只规定了长河宽,但是该调用会根据输入input_layer的高度来自动设置kernel的高度。

由此我们在构建网络时,可以设置两个卷积层来识别输入图片相关代码如下所示:

input_layer = Input(shape=(32, 32, 3)) #输入图片规格为32*32*3
'''
第一层卷积层有10个kenerl大小为4*4注意卷积层会根据输入图片的高度3自动调整kernel高度也为3,
因此实际上kernel的体积变成4*4*3
'''
conv1 = Conv2D(filters = 10, kernel_size = (3, 3), strides = 1, padding = 'same')(input_layer)
'''
再增添一层卷积层加大图片识别力度,注意到上层卷积层有10个kernel,因此conv1的规格为[32, 32, 10]
'''
conv2 = Conv2D(filters = 20, kernel_size = (3,3), strides = 1, padding = 'same')(conv1)
'''
第二层卷积层由于有20个kenel,因此conv2的规格为[32, 32, 20],下面代码将它’压扁‘为[32*32*20]的一维向量
'''
flatten_layer = Flatten()(conv2)
#对数据进行10种分类
output_layer = Dense(units = 10, activation = 'softmax')(flatten_layer)
model = Model(input_layer, output_layer)
model.summary()

上面代码运行后结果为:

Layer (type) Output Shape Param #

input_1 (InputLayer) (None, 32, 32, 3) 0

conv2d_1 (Conv2D) (None, 32, 32, 10) 280

conv2d_2 (Conv2D) (None, 32, 32, 20) 1820

flatten_1 (Flatten) (None, 20480) 0

dense_1 (Dense) (None, 10) 204810

Total params: 206,910
Trainable params: 206,910
Non-trainable params: 0

上面的输出不是很好理解,我们需要好好分析一下。输入层后跟着的第一个卷积层,kernel的长和宽是3,注意输入层输入的图片高度为3,因此卷积层自动将kernel高度也拉伸为3,于是一个kernel拥有3*3*3=27个分量,最后在机器学习中,两个矩阵相乘后往往还喜欢在结果上加上一个称为bias的参数,卷积运算有点类似于直线方程:a*x+b,a可以看做是前面例子中左边的矩阵,x相当于右边kernel,b就是我们这里所说的bias,由此一个kernel其实对应27+1=28个分量,由于有10个kernel,因此总共有280个分量。

同理第二层卷积层kernel的长和宽都是3,但是它的高度根据输入数据的高度而调整为10,因此一个kernel具备的分量数为3*3*10=90,再加上1个bias参数就是91个分量,由于有20个kenel,因此输出结果为91*20=1820个参数。

这里我们还需要了解经过卷积运算后输出的数据规格为(input_height/stride,input_width/strides, filters),这是在使用padding=‘same’,也就是在卷积运算前使用0对输入数据进行填充的情况下,运算后所得结果的规格,根据该公式我们可以确认conv1的规格为(32/1,32/1,10),conv2对应规格为(32/1,32/1,20),卷积运算过程容易理解,但运算后所得结果的规格变化很容易让人陷入迷惑。

深度学习本质上是一种高强度大规模的数值运算,想象在一条高速公路上大量汽车快速前进的情形,很有发生碰撞车祸,同理在大规模的数值计算过程中也会产生某种异常现象,那就是网络中有些参数突然急剧碰撞变得很大,这样会严重影响网络最终结果的准确性。

为了防止这种异常的出现,深度学习引入了两种手段,一种叫批量正规化,其实就是把卷积层输出的结果再次进行一些特定的数值计算,这就相当于在车流量大的高速路上放置限速标志防止车速过快而发生意外,因此我们对卷积层的输出做如下加工运算:

其中的x就是卷积层输出结果中的各个分量,r和B是两个需要在网络中训练的参数,这类似于对高速公路上的车流进行限速。keras框架提供了一个简单接口让我们迅速实现上面功能,那就是BatchNormalization()。

第二种方法叫dropout,其实就是随机将网络层间的连接参数清零,这种方法虽然简单但效果非常好,keras提供的接口是Dropout(rate = 0.25),它表示将四分之一的网络层参数随机清零,所有这些内容结合在一起最终形成的网络代码如下:

input_layer = Input((32,32,3))

x = Conv2D(filters = 32, kernel_size = 3, strides = 1, padding = 'same')(input_layer)
x = BatchNormalization()(x)
x = LeakyReLU()(x)

x = Conv2D(filters = 32, kernel_size = 3, strides = 2, padding = 'same')(x)
x = BatchNormalization()(x)
x = LeakyReLU()(x)

x = Conv2D(filters = 64, kernel_size = 3, strides = 1, padding = 'same')(x)
x = BatchNormalization()(x)
x = LeakyReLU()(x)

x = Conv2D(filters = 64, kernel_size = 3, strides = 2, padding = 'same')(x)
x = BatchNormalization()(x)
x = LeakyReLU()(x)
x = Flatten()(x)

x = Dense(128)(x)
x = BatchNormalization()(x)
x = LeakyReLU()(x)
x = Dropout(rate = 0.5)(x)

x = Dense(NUM_CLASSES)(x)
output_layer = Activation('softmax')(x)

model = Model(input_layer, output_layer)

通过上面网络对输入图片进行识别,所得结果如下图:

从中我们可以看到使用了卷积网络层后,网络对图片的识别率从以前的50%提升到73%,这是一个相当显著的提升!

请关注公众号,让我们共同学习进步

更详细的讲解和代码调试演示过程,请点击链接

  • 点赞 6
  • 收藏
  • 分享
  • 文章举报
tyler_download 博客专家 发布了351 篇原创文章 · 获赞 310 · 访问量 42万+ 他的留言板 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: