这篇博客主要是想和大家分享一下我学习卷积神经网络可视化之后的总结和心得。学习完卷积神经网络的大致流程之后,会感觉到它和其他深度学习网络一样,像个“黑盒子”。我们只知道它有几层,每层有多少个通道,是如何连接的。但是我们不知道他们是怎么处理接收的数据。想进一步了解,却又不想接触底层的算法?其实“眼见为实”可以让我们对卷积神经网络有更充分的认识。         话不多说,本文将通过三个不同的视角来让卷积神经网络”可视化“

目录

(1)可视化卷积神经网络的中间输出(中间激活)

(2)可视化卷积神经网络的过滤器

(3)可视化类激活的热力图


(1)可视化卷积神经网络的中间输出(中间激活):这里的可视化中间输出是可以让我们看清每一层的每一个通道输出的图片都是什么样的

(2)可视化卷积神经网络的过滤器:通过可视化过滤器,我们可以了解是什么样的视觉模式和视觉概念最容易让过滤器接受。

(3)可视化图像中类激活的热力图:通过可视化热力图,我们可以了解卷积神经网络在识别分类图像的依据是什么(图像的哪个部分或者特征),从而定位到图像中的物体(位置信息)。

对于第一种可视化方法,我们采用一个小的卷积神经网络进行分析(只有简单的几层,方便理解)。后面两种我们采用经典的卷积神经网络模型VGG16进行分析。

(1)可视化卷积神经网络的中间输出(中间激活)

对于给定的输入数据,我们将展示各个卷积层和池化层的输出特征图(激活函数的输出)。

首先让我们先来看看此次用到的小型模型的结构吧。(如果想实践又懒得敲代码,我将这个简单的模型代码放在文章的最下面啦)

有了模型,我们需要一个”实验品“。就选只小猫吧。我们通过代码将其可视化看看(如下图)

#显示测试图像
from keras.preprocessing import image
img_path = '/home/hjl/Desktop/D/kaggle_original_data/dogs-vs-cats/train/cat.365.jpg'
img = image.load_img(img_path,target_size=(150,150))
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor,axis=0)
img_tensor /= 255.
plt.imshow(img_tensor[0])
plt.show()

步骤一:将模型实例化,提取前8层的特征图输出。并将第九通道(随便选的,好看一点哈哈哈)可视化(如下图)

from keras import models
layer_output = [layer.output for layer in model.layers[:8]] #提取前八层输出
activation_model = models.Model(inputs=model.input,outputs=layer_output)
activations = activation_model.predict(img_tensor) # 返回8个层的输出组成的Nummpy数组所组层的列表
plt.matshow(activations[0][0,:,:,9],cmap='viridis')

展示一个这个通道似乎是对猫(有三只猫)的轮廓进行检测(轮廓检测器)。

步骤二:接下来我们将3层的通道(只取前64个,超过了就不展示了)都显示出来看看。经过处理,效果将变得十分直观。

layers_name = [layer.name for layer in model.layers[:8]] # 前八层的名字
images_per_row = 16 #平铺在一张大图像的列数
for layer_name, layer_activation in zip(layers_name,activations):n_feature = layer_activation.shape[-1]size = layer_activation.shape[1] # 一张图像的长/宽cols = n_feature // images_per_rowdisplay_grid = np.zeros((size*cols,size*images_per_row)) #平铺在一张大图像的行数for col in range(cols):for row in range(images_per_row):channel_image = layer_activation[0,:,:,col*images_per_row+row]#接下来几行是对图像美化,方便观看channel_image -= channel_image.mean()channel_image /= channel_image.std()channel_image *= 64channel_image += 128channel_image = np.clip(channel_image,0,255).astype('uint8')display_grid[col * size:(col + 1) * size, row * size:(row + 1) * size] = channel_imagescale = 1./ sizeplt.figure(figsize=(scale * display_grid.shape[1],scale * display_grid.shape[0]))plt.grid(False)plt.imshow(display_grid,aspect='auto',cmap='viridis')

直到最后一层(第八层),是这样的。

我们可以观察到一些现象从而应正我们之前所学的结论。

(1)第一层,大多是检测轮廓。这一层包含了原始图像中的所有信息。随着层数增高,我们越来越看不懂(变得抽象了)比如最后一层的其中一张可能是一张识别“猫耳朵”的数据。层数越深,关于原始图像的信息就越少,关于分类的信息就更多。

(2)层数越到后面,图像中的”空位“就越多。这里我们称为“稀疏度”越来越高。也就是说输入图像找不到关于这些过滤器所编码的模式了。

阶段总结:

随着层数加深,提取的特征也越来越抽象。层数越高,包含输入信息越来越少,目标信息越来越多。这种感觉就像是在过滤,过滤对目标检测没有用的信息。而卷积神经网络就像是信息蒸馏管道。这和我们人类的视觉对物体的反映其实很像,我们第一眼一般是记住物体的轮廓,而不是细节。

(2)可视化卷积神经网络的过滤器

从(1)中我们已经了解到每一层,每一个管道的输出是什么样子的了。那么(2)中可视化过滤器是什么意思呢?我们知道通道输出的模样,但是我们不知道他们是如何判断的,是什么样的视觉模式让通道响应最剧烈?所以,可视化过滤器,就是可视化它的“最大响应图像”。我们将使用VGG16模型,这里如果想要实践可能会遇到VGG16导入的一些报错,可以参考我的这篇博客Keras导入VGG16模型(notop与top版本都有)时遇到文件缺失的报错(内附下载链接)_新手村霸的博客-CSDN博客

我们将通过梯度下降法作用于卷积神经网络输入的图像值,让过滤器的响应最大化。如果想要了解梯度下降可以参考我的这篇博客(我为自己代言哈哈哈)梯度下降法原理解析(大白话+公式推理)_新手村霸的博客-CSDN博客

步骤一:构建损失函数,计算梯度,通过随机梯度下降让损失最大化(没错就是最大化,这样才表明过滤器响应最大),得到可以生成过滤器可视化的函数。

#导入模型
model = VGG16(weights='imagenet',include_top='False')# 这是一个将张量转换为有效图像的实用函数
def deprocess_image(x):x -= x.mean()x /= (x.std() + 1e-5)x *= 0.1x += 0.5x = np.clip(x,0,1)x *= 255x = np.clip(x,0,255).astype('uint8')return x# 生成过滤器可视化函数
def generate_pattern(layer_name, filter_index, size=64):layer_output = model.get_layer(layer_name).output loss = K.mean(layer_output[:,:,:,filter_index]) #构建损失函数grads = K.gradients(loss,model.input)[0] #图像梯度grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5) #对图像梯度进行标准化,1e-5是防止除数为0interate = K.function([model.input],[loss,grads]) #创建函数input_img_data = np.random.random((1,size,size,3)) * 20 + 128. #这里建立输入带有噪声的图像step = 1. #布长为1for i in range(40): # 进行40次迭代(梯度上升)loss_value, gards_value = interate([input_img_data])input_img_data += gards_value * stepimg = input_img_data[0]return deprocess_image(img)

我们可以尝试看一下‘block3_conv1’的第0个通道的最大响应模式图

plt.imshow(generate_pattern('block3_conv1',0))

看起来像是波尔卡点图案,别着急。

步骤二:我们将剩下的过滤器的最大响应图也展示出来看看。

layer_name = 'block1_conv1'
size = 64
margin = 5
results = np.zeros((8 * size + 7 * margin, 8 * size + 7 * margin,3)) # 空图像,用于保存结果
#这里我们将所有图片放在一个网格里,方便观察
for i in range(8): #遍历网格的行for j in range(8): #遍历网格的列filter_img = generate_pattern(layer_name, i + (j * 8), size=size)#将结果放到results的第(i,j)个方块中horizental_start = i * size + i * marginhorizental_end = horizental_start + sizevertical_start = j * size + j * marginvertical_end = size + vertical_startresults[horizental_start:horizental_end,vertical_start:vertical_end,:] = filter_img
results /= 255.plt.figure(figsize=(20,20))
plt.imshow(results)

(左上图1)第一层(block1_conv1)的过滤器对应的响应图,这对应简单的方向边缘和颜色。

block2_conv1可能对应的就是边缘组合而成的简单纹理,更高层(block5_conv1,左上图2)可能就对应于‘羽毛’,‘眼睛’之类的。这些图像就是能让过滤器响应最大化的图像啦。

(3)可视化类激活的热力图

最后一个可视化部分就最直接啦。它可以让我们知道图像的哪一部分让卷积神经网络做出了最终的决策(就决定是你啦)。这还有助于对卷积神经网络的决策过程进行尝试,特别是出现了分类错误的情况下。它还可以对图像中的目标进行定位。

这种技术叫类激活图(CAM)可视化。举个栗子,在猫狗分类中,CAM可视化可以生成类别为“猫”的热力图,表示图像的各个部分与猫的相似程度。当然狗也可以哈哈哈

CAM大白话理解就是:用“每个通道对类别的重要程度”对“输入图像对不同通道的激活强度”的空间图进行加权,从而得到了“输入图像对类别的激活强度”。(多读几遍简单理解一下哈)

步骤一:为VGG16模型预处理一张图像(我选了一张非洲大象的图片)

from keras.applications.vgg16 import VGG16,preprocess_input,decode_predictions
from keras.preprocessing import image
import numpy as npimg_path = '/home/hjl/Desktop/elephant.jpg'
model = VGG16(weights='/home/hjl/.keras/datasets/vgg16_weights_tf_dim_ordering_tf_kernels.h5')img = image.load_img(img_path,target_size=(224,224))
x = image.img_to_array(img)
x = np.expand_dims(x,axis=0)
x = preprocess_input(x)

        步骤二:输入数据,并且预测前三个类别,并找到概率最高的识别结果对应的元素编号

preds = model.predict(x)
print(decode_predictions(preds,top=3)[0])
#元素编号
print(np.argmax(preds[0]))
# 结果如下
[('n02504458', 'African_elephant', 0.7770794), ('n01871265', 'tusker', 0.21156743), ('n02504013', 'Indian_elephant', 0.010981791)]
#元素编号
386

        步骤三:使用CAM算法生成热力图

from keras import backend as K
african_elephant_output = model.output[:,386]
last_conv_layer = model.get_layer('block5_conv3')grads = K.gradients(african_elephant_output,last_conv_layer.output)[0] # 非洲象类别对于block5_conv3输出特征图的梯度
pooled_grads = K.mean(grads,axis=(0,1,2))# 形状为(512,)的向量,每个元素是特定特征图通道的梯度平均大小
iterate = K.function([model.input],[pooled_grads,last_conv_layer.output[0]])
pooled_grads_value, conv_layer_output_value = iterate([x])
for i in range(512):conv_layer_output_value[:,:,i] *= pooled_grads_value[i]#将特征图数组的每个通道乘以“这个通道对大象类别”的重要程度heatmap = np.mean(conv_layer_output_value,axis=-1)#得到特征图的逐通道平均值就是热力图啦
#特征图的后处理
heatmap = np.maximum(heatmap,0)
heatmap /= np.max(heatmap)
plt.matshow(heatmap)

        步骤四:将热力图与原图合并

 

它似乎对大象的耳朵的激活强度比较大,这就是它区别与其他类似类别(比如其他地方的大象)的关键目标。

不知道有多少人看完了呢。希望卷积神经网络的可视化能加深你对卷积神经网络的认知。感谢阅读哈。

“看得见的”卷积神经网络(图文并茂+代码解读)(卷积神经网络可视化)相关推荐

  1. [人工智能-深度学习-24]:卷积神经网络CNN - CS231n解读 - 卷积神经网络基本层级

    作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:[人工智能-深度学习-23]:卷积神经网络CNN - CS231n解读 - 卷积神经网络基本层级_ ...

  2. 【OpenPCDet】稀疏卷积SPConv-v1.2代码解读(5)

    [普通稀疏卷积] 了解完子流形3D稀疏卷积我们再来看spconv中对于普通3D稀疏卷积的处理过程.这要回到spconv_ops.cc文件中,从getIndicePairs的普通3D稀疏卷积分支讲起. ...

  3. siris 显著性排序网络代码解读(training过程)Inferring Attention Shift Ranks of Objects for Image Saliency

    阅前说明 前面已经出现的代码用 - 代替. 本文仅解析train部分的代码(inference的部分会后续更新). 不对网络结构做过多解释,默认已经熟悉 mrcnn 的结构以及读过这篇论文了. 另:i ...

  4. 一文看懂膨胀(空洞)卷积(含代码)

    前言:本文的个别内容.图片出自各个博客,但是因时间较久目前找不到原作者链接,如有需要,烦请各位原作者联系我. 目录 一.什么是膨胀卷积?为什么要用膨胀卷积 二.膨胀卷积的特点(优点) 三.膨胀卷积特点 ...

  5. 卷积神经网络代码 Matlab,卷积神经网络matlab程序

    cnn卷积神经网络用什么语言来写pascial . 200+这个是hintonmatlab代码的C++改写版.convnetjs-Star,SAE,首选的肯定是LIBSVM这个库;RBM#47.Dee ...

  6. 数学公式、可视化图齐齐上阵,神经网络如何一步步走向最优化「看得见」!...

    继在<要做好深度学习任务,不妨先在损失函数上「做好文章」>一文中为大家深入浅出地介绍了损失函数的相关知识后,Deep Learning Demystified 编辑.数据科学家 Harsh ...

  7. cnn神经网络可以用于数据拟合吗_使用Keras搭建卷积神经网络进行手写识别的入门(包含代码解读)...

    本文是发在Medium上的一篇博客:<Handwritten Equation Solver using Convolutional Neural Network>.本文是原文的翻译.这篇 ...

  8. 卷积神经网络(CNN)加速器ip设计—1.HLS代码解读

    原作者项目:https://github.com/dhm2013724/yolov2_xilinx_fpga 加速器整体结构 上图是加速器中所有函数的调用关系,可以看到卷积层,池化层,重拍序层都采用乒 ...

  9. 深度学习笔记 第四门课 卷积神经网络 第二周 深度卷积网络:实例探究

    本文是吴恩达老师的深度学习课程[1]笔记部分. 作者:黄海广[2] 主要编写人员:黄海广.林兴木(第四所有底稿,第五课第一二周,第三周前三节).祝彦森:(第三课所有底稿).贺志尧(第五课第三周底稿). ...

最新文章

  1. 我的WINCE4.2历程(10)
  2. Android复习03(Get请求访问网络[详解]、获取文章列表)
  3. ORACLE TEXT DATASTORE PREFERENCE(七)
  4. 安卓开发笔记(二十二):读取本地(内置)html文件并实现和Javascript交互
  5. bootstrap 模态窗口按钮位置_Bootstrap模态框(modal)垂直居中
  6. 【C/C++】一篇文章教你区分数组指针指针数组
  7. 界址点圆圈怎么生成_手机联系人怎么加入黑名单
  8. mac版CAD 2021/CAD 2022许可检出超时怎么解决?
  9. ping TCP端口的实用小工具tcping
  10. mikumikudance
  11. Project: cosmo ical4j
  12. Kanzi常用操作4
  13. element tree不刷新视图_我不告诉你的话,你不会知道iPad原来也有这么多窍门,学起来...
  14. [QNX Hypervisor 2.2用户手册]12.2 术语(二)
  15. c语言通过epoll来实现http协议的web服务器
  16. 古籍、中国通史、诗经、辞、、四书五经、诸子百家、四大名著、唐诗、宋词、明清小说、四库全书
  17. Redis - 启动
  18. 新能源与自动驾驶汽车市场
  19. Centos下netstat的使用
  20. Htm 转换 安卓java_Android开发 Html工具类详解

热门文章

  1. 分布式应用程序是什么
  2. 提供天猫运营方面的自动化软件 、数据采集软件
  3. Pycharm配置运行参数
  4. 7-14 电话聊天狂人分数 25
  5. Camtasia Studio录屏软件使用
  6. 要不要启用苹果wapi_M1版MacBook要不要买?李楠称苹果后续有大招
  7. Redis集群数据同步与选举
  8. 假期,更想念我的宝宝和老婆了。
  9. SRGAN 图像超分辨率重建(Keras)
  10. PPT真人出镜如何录制?ppt和真人怎么同时出镜?