从卷积神经网络的角度看世界

时间:2022-04-26
本文章向大家介绍从卷积神经网络的角度看世界,主要内容包括使用Keras方法研究卷积网络过滤器、将所有的过滤器可视化!、Convnet的梦想、寻找最大化特定类别的输入、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

原文作者:Keras

原文地址:https://blog.keras.io/how-convolutional-neural-networks-see-the-world.html

使用Keras方法研究卷积网络过滤器

注意:所有代码示例在2017年3月14日已经更新到Keras 2.0 API,需要Keras 2.0.0或更高版本才能运行它们。

在这篇文章中,我们来探索深度卷积神经网络(convnets)真正学习到了什么,以及他们如何理解我们提供的图像。我们将使用Keras来对输入进行可视化,这些输入的图像已经在ImageNet上进行训练,可以最大限度地激活VGG16架构不同层次的滤波器。所有在这篇文章中使用的代码都可以在Github上找到。

VGG16(也被称为OxfordNet)是一个由牛津大学Visual Geometry Group开发并以此命名的卷积神经网络架构。它在2014年的ILSVR(ImageNet)竞赛中获胜。尽管已经有了像Inception和ResNet这样的更优秀的进步,到目前为止,它仍然被认为是一个优秀的视觉模型。

首先,作为开始,让我们在Keras中定义VGG16模型:

from keras import applications

# build the VGG16 network
model = applications.VGG16(include_top=False,
                           weights='imagenet')

# get the symbolic outputs of each "key" layer (we gave them unique names).
layer_dict = dict([(layer.name, layer) for layer in model.layers])

请注意,我们只包括卷积层 - 不包括完全连接的层。原因是添加完全连接的层迫使您为模型使用固定的输入大小(224x224,原始ImageNet格式)。通过只保留卷积模块,我们的模型可以适应任意的输入大小。

该模型加载一组在ImageNet上预先训练的权重。

现在让我们定义一个损失函数,它将试图最大化在一个特定图层(layer_name)中对特定过滤器(filter_index)的激活。我们通过Keras中的backend函数来实现这个功能,它可以使我们的代码在TensorFlow和Theano之上运行。

from keras import backend as K

layer_name = 'block5_conv3'
filter_index = 0  # can be any integer from 0 to 511, as there are 512 filters in that layer

# build a loss function that maximizes the activation
# of the nth filter of the layer considered
layer_output = layer_dict[layer_name].output
loss = K.mean(layer_output[:, :, :, filter_index])

# compute the gradient of the input picture wrt this loss
grads = K.gradients(loss, input_img)[0]

# normalization trick: we normalize the gradient
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)

# this function returns the loss and grads given the input picture
iterate = K.function([input_img], [loss, grads])

这些都很简单。这里唯一的技巧是对输入图像像素的梯度进行归一化处理,避免了非常小的和非常大的梯度,并确保梯度上升过程是平滑的。

现在我们可以使用我们定义的Keras函数在输入空间中进行梯度上升,就我们的滤波器激活损耗而言:

import numpy as np

# we start from a gray image with some noise
input_img_data = np.random.random((1, 3, img_width, img_height)) * 20 + 128.
# run gradient ascent for 20 steps
for i in range(20):
    loss_value, grads_value = iterate([input_img_data])
    input_img_data += grads_value * step

使用TensorFlow在CPU上执行此操作需要几秒钟。

然后我们可以提取并显示生成的输入图像:

from scipy.misc import imsave

# util function to convert a tensor into a valid image
def deprocess_image(x):
    # normalize tensor: center on 0., ensure std is 0.1
    x -= x.mean()
    x /= (x.std() + 1e-5)
    x *= 0.1

    # clip to [0, 1]
    x += 0.5
    x = np.clip(x, 0, 1)

    # convert to RGB array
    x *= 255
    x = x.transpose((1, 2, 0))
    x = np.clip(x, 0, 255).astype('uint8')
    return x

img = input_img_data[0]
img = deprocess_image(img)
imsave('%s_filter_%d.png' % (layer_name, filter_index), img)

结果:

将所有的过滤器可视化!

现在开始介绍比较有趣的部分。我们可以使用相同的代码来系统地显示哪种输入(它们不是唯一的)可以使每个图层中的每个过滤器达到最大化,从而为我们提供了对卷积网络视觉空间的模块化分层分解的整洁可视化。

第一层基本上只是编码方向和颜色。然后这些方向和颜色过滤器被组合成基本的网格和点的纹理。这些纹理逐渐结合成越来越复杂的模式。

您可以将每个图层中的过滤器视为向量的基,通常是过度完成的,可用于以紧凑的方式对图层的输入进行编码。过滤器变得越来越复杂,因为他们开始从越来越大的空间范围中整合信息。

一个值得注意的发现:这些滤波器中许多是相同的,但旋转了一些非随机因素(通常是90度)。这意味着我们可以通过寻找使卷积滤波器旋转不变的方法来潜在地压缩在一个大的因子中使用的滤波器的数量。我可以看到一些可以实现的方法 - 这是一个有趣的研究方向。

令人震惊的是,即使对于比较高级别的过滤器,比如block4_conv1,旋转观察也是如此。

在最高层(block5_conv2block5_conv3),我们开始识别与网络被训练分类的对象相似的纹理,比如羽毛,眼睛等。

Convnet的梦想

另一个有趣的事情是将这些过滤器应用于照片(而不是噪点较多的全灰度输入)。这是谷歌去年推广的Deep Dreams的原则。通过选择特定的过滤器组合而不是单个过滤器,可以获得相当不错的结果。如果您对此感兴趣,您还可以查看Deep Dream example in Keras以及介绍该技术的Google博客文章

寻找最大化特定类别的输入

现在讨论另一个方面 - 如果在网络末端包含了完全连接的层,并试图最大限度地激活网络的特定输出,那么该怎么办?你会得到一个看起来像是根据输出来编码的类的图像吗?我们来试试吧。

我们可以定义下面这个非常简单的损失函数:

model = applications.VGG16(include_top=True,
                           weights='imagenet')
loss = K.mean(model.output[:, output_index])

让我们把它应用到output_index = 65(这是ImageNet中的海蛇类)。我们很快就达到了0.999的损失,这意味着有99.9%的把握认为生成的输入是一条海蛇。我们来看看生成的输入。

好吧。看起来不像海蛇。当然,这是一种侥幸,所以让我们再试一次output_index = 18(喜鹊类)。

好吧。所以我们的卷积网络对喜鹊的概念看起来不像一只喜鹊 - 最好,唯一的相似之处在于质地(羽毛,也许是一两个喙)。这是不是意味着卷积网络是个糟糕的工具?当然不是,他们服务他们的目的就好了。这意味着我们应该避免想当然地将它们拟人化,并且相信他们“理解”了狗的概念,或者是喜鹊的存在,这是因为他们能够以高精度对这些物体进行分类。它们不会,至少在任何程度上都不会搞清楚人类的思想。

那么他们真正“明白”什么?有两方面:首先,作为卷积滤波器的分层模块化网络,他们理解他们的视觉输入空间的分解;其次,他们理解这些滤波器的特定组合和一组任意标签之间的概率映射。当然,这在任何人的意义上都不符合“看”的意义,从科学的角度来看,这并不意味着我们在某种程度上解决了计算机视觉问题。不要相信炒作; 我们只是站在一个非常高的梯子的第一阶。

有人说,一个卷积网络对视觉空间的分层模块化分解所学的东西与人类视觉皮层所做的相似。这可能是真的,也可能不是真的,但是没有有力的证据可以相信。当然,人们会期望视觉皮层学到相似的东西,这样就构成了我们视觉世界的“自然”分解(就像傅里叶分解将是一个周期性音频的“自然”分解一样信号)。但是过滤器和层次结构的确切性质,以及他们所学习的过程,与我们的小卷积网络几乎没有什么共同之处。视觉皮层开始并不是卷积,虽然它是分层结构,这些图层本身被组织成皮质柱,其确切目的仍然不能被很好理解 - 这是我们的人工网络中没有发现的一个特征(虽然Geoff Hinton正在研究它)。此外,视觉感知比静态图片的分类还要多得多 - 人类感知从根本上来说是连续的和主动的,而不是静态的和被动的,并且与运动控制(例如眼睛跳动)紧密地联系在一起。

想想下次你听到一些风投或大牌CEO出现在这个消息里面,警告你们我们近期深度学习的进展所带来的生存威胁。今天我们有了比以往任何时候都更好的映射复杂信息空间的工具,这真是太棒了,但是他们终究只是工具,而不是生物,他们所做的任何事情都不能合理地称为“思考”。即使你的灵长类动物新皮质告诉你在岩石上画一个笑脸会使它“快乐”,岩石也不会感到快乐的。

也就是说,将卷积网络的学习内容可视化是相当有趣的 - 谁能想到,在一个足够大的数据集上,具有合理损失函数的简单梯度下降就足以学习这个优美的分层模块网络模式,可以如此好的解释一个复杂的视觉空间。深度学习可能不是真正意义上的智力,但它仍然比几年前任何人预料的要好得多。但愿我们能够知其所以然... ;-)

@fchollet,2016年1月

translated by lightjames 2018.02.03