百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

如何在Python代码中可视化卷积特征

toyiye 2024-07-08 22:59 11 浏览 0 评论

本文的结构如下:首先,我将向您展示VGG-16网络中若干层卷积特征的可视化,然后我们会试着理解其中的一些可视化,我将向你们展示如何快速测试某个过滤器可能检测到的模式的假设。最后,我将解释创建本文中提供的模式所必需的Python代码。

特征可视化

下面您将看到VGG-16网络中几个层的过滤器的特征可视化。在研究它们的同时,我希望您观察生成的模式的复杂性是如何随着更深的网络而增加的。

Layer 7:Conv2d(64,128)

Layer 14: Conv2d(128, 256)

Layer 30: Conv2d(512, 512)

Layer 40: Conv2d(512, 512)?—?top of the network

模式识别

让我们试着解释一些可视化的特征!

从这个开始,这会让你想起什么吗?

这张照片立刻让我想起了你在教堂里发现的拱形天花板的圆形拱门。

那么我们如何检验这个假设呢?图像是通过最大化第40层第286个feature maps的平均激活量而得到的。因此,我们简单地将神网络应用于图像,并绘制第40层中feature maps的平均激活。

我们看到了什么?如所期望的,feature maps286有强烈的尖峰!这是否意味着第40层的Filter 286负责检测拱形天花板呢?这里我要小心一点。Filter 286显然对图像中的拱形结构有响应,但请记住,这种拱形结构可能在几个不同的类别中扮演重要角色。

注意:虽然我使用层40(卷积层)来生成我们当前正在查看的图像,但我使用了第42层来生成显示每个feature map的平均激活的图。层41和42是 batch-norm和 ReLU。ReLU激活函数删除所有负值,这是选择42层而不是40的原因,否则,该图将显示大量负噪声,这使得我们很难看到我们感兴趣的正峰值。

到下一个例子。我可以看到是鸡头(或至少是鸟头)!你看到尖尖的喙和黑眼睛了吗?

测试照片:

feature map 256 显示了强烈的峰值。

下一个:

可能是filter 462 响应羽毛吗?

是的,filter 462响应了羽毛:

filter 265的猜测呢?

也许是链?

是的,似乎是对的!

然而,还有一些其他的峰值!让我们来看看分别为两个过滤器生成的特征可视化:

当快速扫描第40层的512个过滤器生成的模式时,这两张图片都没有引起注意。但是现在网络可以说:也许有点连锁。

这很酷:

我相信会看到很多像羽毛一样的结构,让我想起鸟腿,左下方可能会有类似鸟头的东西,黑眼圈和长嘴。

好的,在feature map 64上有一个峰值,但是还有更多甚至更大的峰值!让我们来看看为其他四个过滤器生成的模式,它们的feature map显示峰值:

顶部有更多的鸟腿和更多的眼睛和喙?然而,对于下图,我不知道。也许这些模式与图像的背景相关联,或者只是代表网络检测我不理解的鸟类所需的东西。我想现在这仍然是黑匣子的一部分......

最后一个,然后我们查看Python代码:

我认出了一只猫的耳朵!

是的,feature map 277有一个峰值,但是什么原因导致了强烈的峰值直接到它的右边呢?

让我们快速生成一张图片,使层40中feature map 281的最大化平均激活:

可能是猫的毛?

事实是,即使在最终的卷积层中,大多数滤波器对我来说都是绝对抽象的。

一种更严格的方法是将网络应用于许多不同类型图像的整个数据集,并跟踪在特定层中最能激发特定过滤器的图像。

还有一件事我觉得很有趣。在浏览生成的模式时,我发现许多模式似乎以不同的方向出现(有时甚至是相同的方向)。

这是有道理的!卷积在平移上是不变的,因为filters水平和垂直地在图像上滑动。但它们不是旋转不变的因为filters不旋转。因此,网络似乎需要几个不同方向的类似filters来检测不同方向的对象和模式。

Python代码

这个想法如下:我们从包含随机像素的图片开始。我们将评估模式中的网络应用于该随机图像,计算特定层中某个feature map的平均激活,然后我们从该图中计算相对于输入图像像素值的梯度。知道像素值的梯度后,我们继续以最大化所选feature map的平均激活的方式更新像素值。

让我们再次解释它:网络权重是固定的,网络不会被训练,我们试图找到一个图像,通过执行梯度下降来最大化某个feature map的平均激活对像素值进行优化。

该技术也可用于神经风格转移。

为了实现这一点,我们需要:

  1. 从随机图像开始
  2. 评估模式下的预训练神经网络
  3. 用一种很好的方式获取我们感兴趣的隐藏层的结果
  4. 用于计算梯度的损失函数和用于更新像素值的优化器

让我们从生成噪声图像作为输入开始。我们可以这样做,即以下方式:img = np.uint8(np.random.uniform(150, 180, (sz, sz, 3)))/255,其中sz是图像的高度和宽度,3是颜色通道的数量,我们除以255,因为它是类型的变量uint8可以存储的最大值。如果您想要更多或更少的噪音,请使用数字150和180。然后我们将其转换为需要使用渐变的PyTorch变量img_var = V(img[None], requires_grad=True)(这是fastai语法)。像素值需要gradients ,因为我们希望使用反向传播来优化它们。

接下来,我们需要在评估模式下预训练的网络(这意味着权重是固定的)。这可以用model = vgg16(pre=True).eval()和set_trainable(model, False)完成。

现在,我们需要一种方法来访问其中一个隐藏层的特征。我们可以在我们感兴趣的隐藏层之后截断网络,以便它成为输出层。但是,有一种更好的方法可以在PyTorch中解决这个问题,称为钩子,它可以在PyTorch Module或Tensor上注册。要理解这一点,你必须知道:

  1. Pytorch Module是所有神经网络模块的基类。
  2. 我们的神经网络中的每一层都是一个Module。
  3. 每个Module都有一个forward方法,用于计算给定输入的Module的输出。

当我们将网络应用于噪声图像时,第一层的forward方法将图像作为输入并计算其输出。这个输出是第二层forward方法的输入,以此类推。当您在某一层注册forward钩子时,该钩子在调用该层的forward方法时执行。即:当你把你的网络应用到一个输入图像时,第一层计算它的输出,然后是第二层,以此类推。当我们到达为其注册了钩子的层时,它不仅计算它的输出,还执行钩子。

这有什么用呢?假设我们对层i的feature maps感兴趣,我们在层i上注册一个forward hook,一旦调用层i的forward方法,就会将层i的feature保存在一个变量中。

下面的类是这样做的:

from fastai.conv_learner import *
from cv2 import resize
%matplotlib inline
class SaveFeatures():
 def __init__(self, module):
 self.hook = module.register_forward_hook(self.hook_fn)
 def hook_fn(self, module, input, output):
 self.features = torch.tensor(output,requires_grad=True).cuda()
 def close(self):
 self.hook.remove()

当钩子执行时,它调用hook_fn方法(参见构造函数)。hook_fn方法将层输出保存在self.features中。注意,这个张量需要梯度,因为我们想对像素值进行反向传播。

如何使用SaveFeatures对象呢?

使用activations = SaveFeatures(list(self.model.children())[i])注册层i的钩子,在将模型应用到带有model(img_var)的图像之后,可以访问activations.features中钩子为我们保存的特征。记住调用clo1se方法来释放使用的内存。

现在我们可以访问图层i的feature maps了!feature maps可以是这样的形状[1,512,7,7],其中1是批处理维度,512是filters/feature maps的数量,7是feature maps的高度和宽度。目标是最大化所选feature map j的平均激活。因此,我们定义以下损失函数:[loss = -activations.features[0, j].mean()以及optimizer = torch.optim.Adam([img_var], lr=lr, weight_decay=1e-6) 优化像素值的优化器。优化器在默认情况下最小化损失,因此我们不告诉优化器最大化损失,而是简单地将平均激活乘以-1。使用optimizer.zero_grad()重置梯度,使用loss.back()计算像素值的梯度,使用optimizer.step()改变像素值。

让我们来看一个例子:

接下来,我改变了噪声输入图像的大小。

你能观察到“链状图案”的频率似乎随着图像尺寸的增加而增加吗?我知道可能很难看出我的意思。然而,生成的图案的频率随着图像尺寸的增加而增加是有意义的,因为卷积filters具有固定的尺寸,但是它们与图像的相对大小随着图像分辨率的增加而减小。换句话说:假设所创建的模式的像素大小总是大致相同。如果我们增加图像的尺寸,生成的图案的相对尺寸会减小,图案的频率会增加。

如果我的假设是正确的,我们想要的是低分辨率示例的低频模式(甚至比上面显示的还要低),但是具有高分辨率。这有意义吗?我们怎么做呢?

我尝试从一个非常低分辨率的图像开始,即56×56像素,对像素值进行几个步骤的优化,然后将图像的大小增加一定的因子。在放大图像之后,我对像素值进行了进一步的优化,然后再次放大图像。

这样做效果更好:

我们现在有一个低频模式,分辨率更高,而且没有太多噪音。为什么这样做?当我们从低分辨率开始时,我们得到一个低频模式。在升级之后,如果我们使用随机图像以较大的图像大小开始,则放大的模式具有比优化器生成的频率更低的频率。因此,当在下一次迭代中优化像素值时,我们处于更好的起点并且似乎避免了较差的局部最小值。这有意义吗?为了进一步减少高频模式,我在升频后稍微模糊了图像,这比低频模式更能影响高频模式。

我发现按比例增加12倍可以得到很好的效果。

看看下面的Python代码。您会发现我们已经讨论了最重要的行,例如创建随机图像,注册钩子,定义优化器和损失以及优化像素值。

class FilterVisualizer():
 def __init__(self, size=56, upscaling_steps=12, upscaling_factor=1.2):
 self.size, self.upscaling_steps, self.upscaling_factor = size, upscaling_steps, upscaling_factor
 self.model = vgg16(pre=True).cuda().eval()
 set_trainable(self.model, False)
 def visualize(self, layer, filter, lr=0.1, opt_steps=20, blur=None):
 sz = self.size
 img = np.uint8(np.random.uniform(150, 180, (sz, sz, 3)))/255 # generate random image
 activations = SaveFeatures(list(self.model.children())[layer]) # register hook
 for _ in range(self.upscaling_steps): # scale the image up upscaling_steps times
 train_tfms, val_tfms = tfms_from_model(vgg16, sz)
 img_var = V(val_tfms(img)[None], requires_grad=True) # convert image to Variable that requires grad
 optimizer = torch.optim.Adam([img_var], lr=lr, weight_decay=1e-6)
 for n in range(opt_steps): # optimize pixel values for opt_steps times
 optimizer.zero_grad()
 self.model(img_var)
 loss = -activations.features[0, filter].mean()
 loss.backward()
 optimizer.step()
 img = val_tfms.denorm(img_var.data.cpu().numpy()[0].transpose(1,2,0))
 self.output = img
 sz = int(self.upscaling_factor * sz) # calculate new image size
 img = cv2.resize(img, (sz, sz), interpolation = cv2.INTER_CUBIC) # scale image up
 if blur is not None: img = cv2.blur(img,(blur,blur)) # blur image to reduce high frequency patterns
 self.save(layer, filter)
 activations.close()
 
 def save(self, layer, filter):
 plt.imsave("layer_"+str(layer)+"_filter_"+str(filter)+".jpg", np.clip(self.output, 0, 1))

使用FilterVisualizer可参考以下Python代码:

layer = 40
filter = 265
FV = FilterVisualizer(size=56, upscaling_steps=12, upscaling_factor=1.2)
FV.visualize(layer, filter, blur=5)
img = PIL.Image.open("layer_"+str(layer)+"_filter_"+str(filter)+".jpg")
plt.figure(figsize=(7,7))
plt.imshow(img)

该Python代码假设你有一个Nvidia GPU。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码