5.4 卷积神经网络的可视化

卷积神经网络学到的表示非常适合可视化,很大程度上是因为它们是视觉概念的表示\color{red}视觉概念的表示。接下来介绍3种可视化方法。

  • 可视化卷积神经网络的中间输出(中间激活)\color{red}可视化卷积神经网络的中间输出(中间激活)有助于理解卷积神经网络连续的层如何对输入进行变换,也有助于初步了解卷积神经网络每个过滤器的含义。
  • 可视化卷积神经网络的过滤器\color{red}可视化卷积神经网络的过滤器有助于精确理解卷积神经网络中每个过滤器容易接受的视觉模式或视觉概念。
  • 可视化图像中类激活的热力图\color{red}可视化图像中类激活的热力图有助于理解图像的哪个部分被识别为属于某个类别,从而可以定位图像中的物体。
    在这里插入图片描述

5.4.1 可视化中间激活

5.4.1 可视化中间激活相关定义

  1. What ?——什么是中间层激活可视化?
    • 中间层激活可视化:指对于给定输入,展示网络中各个卷积层和池化层输出的特征图\color{red}展示网络中各个卷积层和池化层输出的特征图
    • 特征图:层的输出通常称为该层的激活,即线性变换与激活函数的输出
  2. Why?——为什么使用中间层激活可视化?
    它可以帮助我们看到输入图片是如何被CNN模型学习并分解为不同特征的过程\color{red}输入图片是如何被CNN模型学习并分解为不同特征的过程CNN,并进而通过可视化的方式来更好地理解、分析模型。
  3. How?——如何实现中间层激活可视化?
    • 可以从三个维度对特征图进行可视化——宽度、高度、深度(通道)\color{red}宽度、高度、深度(通道),而每个通道都对应相对独立的特征。
    • 可视化方法:将每个通道形成的矩阵绘制成二维图像(matplotlib提供了现函数,plt.matshow()/pIt.imshow())

5.4.2 可视化中间激活基本思路

  1. 研究对象
    自定义深度网络模型:共11层网络
    • 4个conv2d层,即2维卷积层
      • 随着网络深度增加,filters数目也随之增加,32–>64–>/28.……
      • filter窗口全部为3×3
      • 每一个卷积层后都跟着一个池化层
    • 4个max_pool2d层
      • 采用最大池化层
      • 池化窗口统一为2×2
    • 1个flatten层,即将张量平铺,转化为向量行
    • 2个dense层,即全连接层
      • 倒数第二个dense层激活函数为relu
      • 倒数第一个dense层激活函数为sigmoid(因为该问题为二元分类间题
        在这里插入图片描述
  2. 编程任务流
    1. 一选层\color{red}一选层:加载指定模型,并指定需要观察的卷积层
      load_model()
      model.layerslayer.output
      
    2. 二建模\color{red}二建模:将原模型输入和指定层的输出关联,创建新的模型
      models.Model(input..output...)
      
    3. 三预测\color{red}三预测:指定输入图像并预处理(一转/二扩/三归),用新建的模型进行预测,获取指定层激活输出
      new_model.predict(input..)
      
    4. 四可视化\color{red}四可视化:先对指定卷积层的激活输出预处理,再可视化
      plt.imshow()
      

5.4.3 可视化中间激活代码实现

  1. 一选层\color{red}一选层

    from tensorflow.keras import models
    from tensorflow.keras.models import load_model
    model = load_model('cats_and_dogs_small_1.h5')
    # 通过各层的属性output可以获取各层的输出张量
    layer_outputs = [layer.output for layer in model.layers[:8]]
    model.summary() # 作为提醒
    

    在这里插入图片描述

  2. 二建模\color{red}二建模

    activation_model = models.Model(inputs = model.input, outputs = layer_outputs)
    

    用一个输入张量和一个输出张量列表将模型实例化。使用KerasModel类。

    1. 使用 KerasModel类。为了提取想要查看的特征图,我们需要创建一个 Keras 模型并使用Model类,以图像批量作为输入,并输出所有卷积层和池化层的激活。
    2. Model 类允许模型有多个输出,这一点与 Sequential 模型不同。模型实例化需要两个参数:一个输入张量(或输入张量的列表)和一个输出张量(或输出张量的列表)。
    3. 查看第1层输出的张量值,输出张量的空间大小可与model.summary()比对:
  3. 三预测\color{red}三预测

    • 加载图片

      	# 加载测试图片
      	img_path = 'C:\\Users\\Administrator\\deep-learning-with-python-notebooks-master\\cats_and_dogs_small\\test\\cats\\cat.3700.jpg'
      	from tensorflow.keras.preprocessing import image
      	import numpy as np
      	img = image.load_img(img_path, target_size=(150, 150))
      	## 图片预处理三步诀
      	# 一,将image转化为数组
      	img_tensor = image.img_to_array(img)
      	#二,由3D扩展为4D
      	img_tensor = np.expand_dims(img_tensor, axis=0)
      	#三,归一化处理
      	img_tensor /= 255.
      	# 其形状为 (1, 150, 150, 3)
      	print(img_tensor.shape)
      
    • 显示测试图像(此步可省)

      import matplotlib.pyplot as plt
      plt.imshow(img_tensor[0])
      plt.show()
      

    在这里插入图片描述

    1. 第二部分处理后的图片张量作为输入,即可得到各层实际的输出张量值
      # activations中包含了各个中间层输出的张量
      activations = activation_model.predict(img_tensor)
      # 查看第1层输出的张量值
      #第0层输出
      first_layer_activation = activations[0]
      first_layer_activation.shape
      
  4. 四可视化\color{red}四可视化

    每个网络层的输出张量为4维:(批次中张量序号, 通道长,通道宽,通道序号),在本项目中,每个批次只有一个输出张量(因为只有一个输入样本)。

    • 第一层网络第1个通道矩阵
      在这里插入图片描述
      #对于输入样本,展现第一层激活函数所输出的张量中,32个通道中的第1号通道所形成的的矩阵可视化渲染图
      import matplotlib.pyplot as plt
      plt.imshow(first_layer_activation[0, :, :, 0], cmap = 'viridis')#通道编号从0开始
      

    该通道像边界检测器。

    • 将首层第 7 个通道可视化
      在这里插入图片描述
    • 对第一张图片,图像增强
      #characters = first_layer_activation[0, :, :, 0]
      characters = activations[0][0, :, :, 0]
      characters -= characters.mean()
      characters /= characters.std()
      characters *= 64
      characters += 128
      characters = np.clip(characters, 0, 255).astype('uint8')
      
      # 绘图
      plt.rcParams['font.sans-serif']=['SimHei']
      plt.rcParams['axes.unicode_minus'] = False
      
      ax1 = plt.subplot(121)
      ax1.set_title('通道原始数组可视化效果')
      ax1.imshow(activations[0][0, :, :, 0],cmap = 'viridis')
      ax2 = plt.subplot(122)
      ax2.set_title('通道处理后数组可视化效果')
      ax2.imshow(characters, cmap = 'viridis')
      

在这里插入图片描述

  1. 完整代码

    ## 一选层
    from tensorflow.keras import models
    from tensorflow.keras.models import load_model
    model = load_model('cats_and_dogs_small_1.h5')
    # 通过各层的属性output可以获取各层的输出张量
    layer_outputs = [layer.output for layer in model.layers[:8]]
    ## 二建模
    activations = models.Model(inputs = model.input, outputs = layer_outputs)
    ## 三预测
    # 加载测试图片
    img_path = 'C:\\Users\\Administrator\\deep-learning-with-python-notebooks-master\\cats_and_dogs_small\\test\\cats\\cat.3700.jpg'
    from tensorflow.keras.preprocessing import image
    import numpy as np
    img = image.load_img(img_path, target_size=(150, 150))
    ## 图片预处理三步诀
    # 一,将image转化为数组
    img_tensor = image.img_to_array(img)
    #二,由3D扩展为4D
    img_tensor = np.expand_dims(img_tensor, axis=0)
    #三,归一化处理
    img_tensor /= 255.
    # 其形状为 (1, 150, 150, 3)
    print(img_tensor.shape)
    
    # activations中包含了各个中间层输出的张量
    activations = activation_model.predict(img_tensor)
    # 查看第1层输出的张量值
    #第0层输出
    first_layer_activation = activations[0]
    
    ## 四可视化
    #对于输入样本,展现第一层激活函数所输出的张量中,32个通道中的第1号通道所形成的的矩阵可视化渲染图
    import matplotlib.pyplot as plt
    plt.imshow(first_layer_activation[0, :, :, 0], cmap = 'viridis')#通道编号从0开始
    
  2. 将每个中间激活的所有通道可视化

    # 层的名称,这样你可以将这些名称画到图中
    layer_names = []
    for layer in model.layers[:8]:
    	layer_names.append(layer.name)
    images_per_row = 16
    
    # 显示特征图
    for layer_name, layer_activation in zip(layer_names, activations):
    	n_features = layer_activation.shape[-1] # 特征图中的特征个数
    	
    	size = layer_activation.shape[1] # 特征图的形状为 (1, size, size, n_features)
    	
    	n_cols = n_features // images_per_row # 在这个矩阵中将激活通道平铺
    	display_grid = np.zeros((size * n_cols, images_per_row * size))
    	# 将每个过滤器平铺到一个大的水平网格中
    	for col in range(n_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 *= 64
    		channel_image += 128
    		channel_image = np.clip(channel_image, 0, 255).astype('uint8')
    		display_grid[col * size : (col + 1) * size, # 显示网格
    					row * size : (row + 1) * size] = channel_image
    		scale = 1. / size
    		plt.figure(figsize=(scale * display_grid.shape[1],
    							scale * display_grid.shape[0]))
    	plt.title(layer_name)
    	plt.grid(False)
    	plt.imshow(display_grid, aspect='auto', cmap='viridis')
    

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

中间激活的结论

  • 第一层是各种边缘探测器的集合\color{red}第一层是各种边缘探测器的集合。在这一阶段,激活几乎保留了原始图像中的所有信息。
  • 随着层数的加深,激活变得越来越抽象,并且越来越难以直观地理解\color{red}随着层数的加深,激活变得越来越抽象,并且越来越难以直观地理解。它们开始表示更高层次的概念,比如“猫耳朵”和“猫眼睛”。层数越深,其表示中关于图像视觉内容的信息就越少,而关于类别的信息就越多。
  • 激活的稀疏度(sparsity)随着层数的加深而增大\color{red}激活的稀疏度(sparsity)随着层数的加深而增大sparsity。在第一层里,所有过滤器都被输入图像激活,但在后面的层里,越来越多的过滤器是空白的。也就是说,输入图像中找不到这些过滤器所编码的模式

随着层数的加深,层所提取的特征变得越来越抽象。更高的层激活包含关于特定输入的信息越来越少,而关于目标的信息越来越多(本例中即图像的类别:猫或狗)。这个跟人类的认知场景很相似,我们认识一个东西也是这样开始的。

深度神经网络可以有效地作为信息蒸馏管道\color{red}信息蒸馏管道(information distillation pipeline),输入原始数据(本例中是 RGB 图像),反复对其进行变换,将无关信息过滤掉(比如图像的具体外观),并放大和细化有用的信息\color{red}并放大和细化有用的信息(比如图像的类别)。
在这里插入图片描述


Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐