精华内容
下载资源
问答
  • 2022-02-13 09:07:07

    随着深度学习的快速发展,卷积神经网络应用于各个方面,图像的分类、目标检测、图像分割等等。很多人,包括自己也是,在刚学习这些内容的时候只是看了相关理论后,然后就把数据集扔进网络开始“炼丹”。但在训练和检测过程中,虽然得到最终的分类或检测效果,却并不知道中间到底发生了什么,神经网络就犹如黑盒一样。所以想把卷积网络的处理过程进一步可视化,更好的理解中间到底发生了什么。也是作为个人学习笔记,欢迎大家讨论学习。

    这里先以图像分类为例(目标检测以后有计划再进行整理)。分类网络代码参考b站up主Bubbliiiing(个人很多东西也是和这位大佬学习的,所以关于网络方面的讲解我这里不过多再说,大家可看这位大佬的视频:https://www.bilibili.com/video/BV143411B7wg?spm_id_from=333.999.0.0)

    分类主干网络我选用的是经典的VGG16做猫狗分类。下面接入正题。
    我们可以通过卷积核对图像逐步提取特征。这里我使用的是pytorch来举例,
    先读取图片:

    img = plt.imread(r'./cat.jpg')
    plt.imshow(img)
    plt.show()
    

    读入的图片为numpy形式,可以用torch.from_numpy(img),转换为tensor形式
    读入的图片为numpy形式,可以用torch.tensor(img,dtype=torch.float32) / 255,转换为tensor形式。此时的图片shape为(H,W,C),需要利用**permute(2, 0, 1).unsqueeze(0)**进行维度的转换。其中unsqueeze(0)就是在最前面加一个维度,即batch_size这一维度。那么此时的图像shape将变为【1,3,H, W】才可进行卷积

    设计一个卷积核:nn.Conv2d(3,3,3,1,1)【为了简单为例方便展示,我这里输出还是3通道,卷积核的权重可以自己定义,但这里我使用的默认的,只是为了演示一下

     conv1 = nn.Conv2d(3,3,3,1,1)
     conv1_img = conv1(img)
     # 下述代码是为了显示卷积后的图像
     img1 = conv1_img.squeeze(0).permute(1,2,0)*255  # 转化维度,并且转化为0~255,数值类型转为uint8
     img1 = img1.detach().numpy()  # tensor 变为numpy
     img1 = img1.astype('uint8')
    

    卷积后的图像如下【每次卷积后的效果是不同的,因为Conv2d权重不是固定值】:
    卷积后的效果

    然后我们在来看下每个通道卷积后提取特征的效果
    每个通道卷积后的效果
    可以看到每个通道提取的纹理特征是不一样的,网络在训练过程会不断的更新卷积核的权重,等最终提取到的特征能满足绝大多数猫的特征的时候就可以了,然后将这个权重保存下来就是我们最终得到的权重文件了。

    接下来我们再看一下图像进过已经训练好的VGG16分类网络【有关VGG16网络讲解网上有很多资料,我这里不在讲了】
    我们先来看下分类效果:
    分类效果图
    同样的,我们可以看一下网络最终在每个通道提取的特征是什么样的【通道太多,我只展现部分的】,黄色部分都是经过激活函数激活后得到的,可以认为是该通道这一区域有识别的物体。

    特征图

    可以计算每个通道提取的特征平均值,然后筛选出哪个通道平均特征值最大,再将该通道和原始图像叠加后看看效果图
    特征图
    叠加后的效果图
    再通过nn.Linear()进行分类,这个分类后的结果是hard target形式,类似这种形式[ 13.6133, -13.4983],预测的是二分类,假如13.6133代表预测的猫,-13.4983代表预测的狗,因为猫得到的值最大,所以得到的分类结果是猫【注意这还不是概率值,需要再经过softmax进行soft操作得到概率值】。然后可以再通过np.max()函数筛选出最大的概率值即可。

    实现卷积可视化,有助于我们进一步看到这个“黑盒”中到底发生了什么,进一步理解卷积神经网络。

    文章写的比较粗糙,可能还有些地方没有写到,望谅解。

    更多相关内容
  • 卷积神经网络可视化(基于keras)

    万次阅读 2022-02-15 15:52:41
    神经网络的模型是个“黑盒”,即模型学到的内容很难用人能够理解的方式来提取和表现,虽然对于某些类型的深度学习模型来说,这种表述部分正确,但对卷积神经网络来说绝对不是这样,卷积神经网络学到的表示非常适合...

    在通常的认知中,神经网络的模型是一个“黑盒”,即模型学到的内容很难用人能够理解的方式来提取和表现,虽然对于某些类型的深度学习模型来说,这种表述部分正确,但对卷积神经网络来说绝对不是这样,卷积神经网络学到的表示非常适合可视化,很大程度上是因为它们是视觉概念的表示。到现在为止,人们开发了多种技术来对这些表示进行可视化和理解,这里介绍3种最容易理解也是最有效的方法。

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

    1 可视化卷积神经网络的中间输出

    可视化中间激活,是指对于给定输入,展示网络中各个卷积层和池化层输出的特征图(层的输出通常被称为该层的激活,即激活函数的输出)。这让我们可以看到输入如何被分解为网络学到的不同过滤器。我们希望在三个维度对特征图进行可视化:宽度、高度和深度(通道)。每个通道都对应相对独立的特征,所以将这些特征图可视化的正确方法是将每个通道的内容分别绘制成二维图像。
    我们使用这篇博客保存的模型来进行中间输出的可视化:keras深度学习之猫狗分类二(数据增强)
    加载该模型,打印其网络架构:

    if __name__=='__main__':
        #加载保存的模型
        model=models.load_model('cats_and_dogs_1.h5')
        model.summary()
    ``
    
    ```bash
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    conv2d (Conv2D)              (None, 148, 148, 32)      896
    _________________________________________________________________
    max_pooling2d (MaxPooling2D) (None, 74, 74, 32)        0
    _________________________________________________________________
    conv2d_1 (Conv2D)            (None, 72, 72, 64)        18496
    _________________________________________________________________
    max_pooling2d_1 (MaxPooling2 (None, 36, 36, 64)        0
    _________________________________________________________________
    conv2d_2 (Conv2D)            (None, 34, 34, 128)       73856
    _________________________________________________________________
    max_pooling2d_2 (MaxPooling2 (None, 17, 17, 128)       0
    _________________________________________________________________
    conv2d_3 (Conv2D)            (None, 15, 15, 128)       147584
    _________________________________________________________________
    max_pooling2d_3 (MaxPooling2 (None, 7, 7, 128)         0
    _________________________________________________________________
    flatten (Flatten)            (None, 6272)              0
    _________________________________________________________________
    dropout (Dropout)            (None, 6272)              0
    _________________________________________________________________
    dense (Dense)                (None, 256)               1605888
    _________________________________________________________________
    dense_1 (Dense)              (None, 1)                 257
    =================================================================
    Total params: 1,846,977
    Trainable params: 1,846,977
    Non-trainable params: 0
    _________________________________________________________________
    

    显示第一层输出的某个通道图像:

    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    from PIL import Image
    from tensorflow.keras import layers
    from tensorflow.keras import models
    from tensorflow.keras import optimizers
    import matplotlib.pyplot as plt
    from tensorflow.keras.preprocessing import image as kimage
    
    if __name__=='__main__':
        #加载保存的模型
        model=models.load_model('cats_and_dogs_1.h5')
        model.summary()
    
        #加载一张猫的图像
        img=kimage.load_img(path='./dataset/training_set/cats/cat.1700.jpg',target_size=(150,150))
        img_tensor=kimage.img_to_array(img)
        img_tensor=img_tensor.reshape((1,)+img_tensor.shape)
        img_tensor/=255.
        plt.imshow(img_tensor[0])
        plt.show()
    
        #提取前8层的输出
        layer_outputs=[layer.output for layer in model.layers[:8]]
        activation_model=models.Model(inputs=model.input,outputs=layer_outputs)
    
        #以预测模式运行模型 activations包含卷积层的8个输出
        activations=activation_model.predict(img_tensor)
        print(activations[0].shape)#(1, 148, 148, 32)
    
        first_layer_activation = activations[0]
        plt.matshow(first_layer_activation[0, :, :, 9], cmap='viridis')
        plt.show()
    

    原图为:
    在这里插入图片描述
    第一层的输入第9个通道的特征图为:
    在这里插入图片描述
    将每个中间激活的所有通道可视化

    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    from PIL import Image
    from tensorflow.keras import layers
    from tensorflow.keras import models
    from tensorflow.keras import optimizers
    import matplotlib.pyplot as plt
    from tensorflow.keras.preprocessing import image as kimage
    import numpy as np
    
    if __name__=='__main__':
        #加载保存的模型
        model=models.load_model('cats_and_dogs_1.h5')
        model.summary()
    
        #加载一张猫的图像
        img=kimage.load_img(path='./dataset/training_set/cats/cat.1700.jpg',target_size=(150,150))
        img_tensor=kimage.img_to_array(img)
        img_tensor=img_tensor.reshape((1,)+img_tensor.shape)
        img_tensor/=255.
        plt.imshow(img_tensor[0])
        plt.show()
    
        #提取前8层的输出
        layer_outputs=[layer.output for layer in model.layers[:8]]
        activation_model=models.Model(inputs=model.input,outputs=layer_outputs)
    
        #以预测模式运行模型 activations包含卷积层的8个输出
        activations=activation_model.predict(img_tensor)
        print(activations[0].shape)#(1, 148, 148, 32)
    
        first_layer_activation = activations[0]
        plt.matshow(first_layer_activation[0, :, :, 9], cmap='viridis')
        plt.show()
        #清空当前图像
        #plt.clf()
    
        #将每个中间激活的所有通道可视化
        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] 
                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')
            plt.show()
            
    

    显示的各通道图像如下所示:
    第1个:
    在这里插入图片描述
    第2个:
    在这里插入图片描述
    第3个:
    在这里插入图片描述
    第4个:
    在这里插入图片描述
    第5个:
    在这里插入图片描述
    第6个:
    在这里插入图片描述
    第7个:
    在这里插入图片描述
    第8个:
    在这里插入图片描述
    从上面显示的各个卷积层和池化层的输出特征图,我们能够得到如下几点:

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

    深度神经网络有一个重要普遍特征:随着层数的加深,层所提取的特征变得越来越抽象。更高的层激活包含关于特定输入的信息越来越少,而关于目标的信息越来越多(本例中即图像的类别:猫或狗)。深度神经网络可以有效地作为信息蒸馏管道(information distillation pipeline),输入原始数据(本例中是 RGB 图像),反复对其进行变换,将无关信息过滤掉(比如图像的具体外观),并放大和细化有用的信息(比如图像的类别)。

    2 可视化神经网络的过滤器

    想要观察卷积神经网络学到的过滤器,另一种简单的方法是显示每个过滤器所响应的视觉模式。这可以通过在输入空间中进行梯度上升来实现:从空白输入图像开始,将梯度下降应用于卷积神经网络输入图像的值,其目的是让某个过滤器的响应最大化。得到的输入图像是选定过滤器具有最大响应的图像。
    这个过程很简单:我们需要构建一个损失函数,其目的是让某个卷积层的某个过滤器的值最大化;然后,我们要使用随机梯度下降来调节输入图像的值,以便让这个激活值最大化。
    在这里我们使用在ImageNet上训练的vgg16网络模型进行可视化滤波器。
    通过如下代码可以查看block3_conv1 层第 0 个过滤器响应的是波尔卡点(polka-dot)图案。

    from tensorflow.keras.applications import VGG16
    from tensorflow.keras import backend as K
    import matplotlib.pyplot as plt
    import numpy as np
    import tensorflow as tf
    tf.compat.v1.disable_eager_execution()
    
    #为过滤器的可视化定义损失张量
    model = VGG16(weights='imagenet',
                  include_top=False)
    
    model.summary()
    
    layer_name = 'block3_conv1'
    filter_index = 0
    
    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)
    
    #给定numpy输入值,得到numpy输出值
    iterate = K.function([model.input], [loss, grads])
    
    #通过随机梯度下降让损失最大化
    input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.
    step = 1. 
    for i in range(40):
        loss_value, grads_value = iterate([input_img_data])
        input_img_data += grads_value * step
    
    #将张量转换为有效图像的实用函数
    def deprocess_image(x):
        x -= x.mean() 
        x /= (x.std() + 1e-5)
        x *= 0.1
        x += 0.5 
        x = np.clip(x, 0, 1)
        # x *= 255 
        # x = np.clip(x, 0, 255)
        # x/=255.
        return x
    
    #生成过滤器可视化的函数
    #构建一个损失函数,将该层第 n 个过滤器的激活最大化
    def generate_pattern(layer_name, filter_index, size=150):
        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) 
        iterate = K.function([model.input], [loss, grads]) 
        input_img_data = np.random.random((1, size, size, 3)) * 20 + 128. 
        step = 1.
        for i in range(40): 
            loss_value, grads_value = iterate([input_img_data])
            input_img_data += grads_value * step
        img = input_img_data[0]
        return deprocess_image(img)
    
    #block3_conv1 层第 0 个过滤器响应的是波尔卡点(polka-dot)图案
    plt.imshow(generate_pattern('block3_conv1', 0))
    plt.show()
    

    在这里插入图片描述
    接下来我们把多个卷积层的每个层前64个过滤器的模式显示出来,代码如下:

    from tensorflow.keras.applications import VGG16
    from tensorflow.keras import backend as K
    import matplotlib.pyplot as plt
    import numpy as np
    import tensorflow as tf
    tf.compat.v1.disable_eager_execution()
    
    #为过滤器的可视化定义损失张量
    model = VGG16(weights='imagenet',
                  include_top=False)
    
    model.summary()
    
    layer_name = 'block3_conv1'
    filter_index = 0
    
    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)
    
    #给定numpy输入值,得到numpy输出值
    iterate = K.function([model.input], [loss, grads])
    
    #通过随机梯度下降让损失最大化
    input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.
    step = 1. 
    for i in range(40):
        loss_value, grads_value = iterate([input_img_data])
        input_img_data += grads_value * step
    
    #将张量转换为有效图像的实用函数
    def deprocess_image(x):
        x -= x.mean() 
        x /= (x.std() + 1e-5)
        x *= 0.1
        x += 0.5 
        x = np.clip(x, 0, 1)
        # x *= 255 
        # x = np.clip(x, 0, 255)
        # x/=255.
        return x
    
    #生成过滤器可视化的函数
    #构建一个损失函数,将该层第 n 个过滤器的激活最大化
    def generate_pattern(layer_name, filter_index, size=150):
        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) 
        iterate = K.function([model.input], [loss, grads]) 
        input_img_data = np.random.random((1, size, size, 3)) * 20 + 128. 
        step = 1.
        for i in range(40): 
            loss_value, grads_value = iterate([input_img_data])
            input_img_data += grads_value * step
        img = input_img_data[0]
        return deprocess_image(img)
    
    #block3_conv1 层第 0 个过滤器响应的是波尔卡点(polka-dot)图案
    # plt.imshow(generate_pattern('block3_conv1', 0))
    # plt.show()
    
    # 生成某一层中所有过滤器响应模式组成的网格
    #查看如下5个层的过滤器模式
    layer_names=['block1_conv1','block2_conv1','block3_conv1','block4_conv1','block5_conv1']
    for layer_name in layer_names:
        #显示通道中的前64个滤波器
        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) 
                horizontal_start = i * size + i * margin 
                horizontal_end = horizontal_start + size
                vertical_start = j * size + j * margin
                vertical_end = vertical_start + size
                results[horizontal_start: horizontal_end,vertical_start: vertical_end, :] = filter_img
        plt.figure(figsize=(20, 20)) 
        plt.imshow(results)
        plt.show()
    

    block1_conv1层过滤器模式为:
    在这里插入图片描述

    block2_conv1层过滤器模式为:
    在这里插入图片描述

    block3_conv1层过滤器模式为:
    在这里插入图片描述

    block4_conv1层过滤器模式为:
    在这里插入图片描述

    block5_conv1层过滤器模式为:
    在这里插入图片描述
    这些过滤器可视化包含卷积神经网络的层如何观察世界的很多信息:卷积神经网络中每一层都学习一组过滤器,以便将其输入表示为过滤器的组合。这类似于傅里叶变换将信号分解为一组余弦函数的过程。随着层数的加深,卷积神经网络中的过滤器变得越来越复杂,越来越精细。

    1. 模型第一层(block1_conv1)的过滤器对应简单的方向边缘和颜色(还有一些是彩色边缘)。
    2. block2_conv1 层的过滤器对应边缘和颜色组合而成的简单纹理。
    3. 更高层的过滤器类似于自然图像中的纹理:羽毛、眼睛、树叶等。

    3 可视化类激活的热力图

    我还要介绍另一种可视化方法,它有助于了解一张图像的哪一部分让卷积神经网络做出了最终的分类决策。这有助于对卷积神经网络的决策过程进行调试,特别是出现分类错误的情况下。这种方法还可以定位图像中的特定目标。
    这种通用的技术叫作类激活图(CAM,class activation map)可视化,它是指对输入图像生成类激活的热力图。类激活热力图是与特定输出类别相关的二维分数网格,对任何输入图像的每个位置都要进行计算,它表示每个位置对该类别的重要程度。举例来说,对于输入到猫狗分类卷积神经网络的一张图像,CAM 可视化可以生成类别“猫”的热力图,表示图像的各个部分与“猫”的相似程度,CAM 可视化也会生成类别“狗”的热力图,表示图像的各个部分与“狗”的相似程度。
    我们将使用的具体实现方式是“Grad-CAM: visual explanations from deep networks via gradient-based localization” 这篇论文中描述的方法。这种方法非常简单:给定一张输入图像,对于一个卷积层的输出特征图,用类别相对于通道的梯度对这个特征图中的每个通道进行加权。直观上来看,理解这个技巧的一种方法是,你是用“每个通道对类别的重要程度”对“输入图像对不同通道的激活强度”的空间图进行加权,从而得到了“输入图像对类别的激活强度”的空间图。
    为模型预处理一张输入图像,在这里采用非洲象图像:
    在这里插入图片描述
    预测代码如下:

    from tensorflow.keras.preprocessing import image
    from tensorflow.keras.applications.vgg16 import preprocess_input, decode_predictions
    from tensorflow.keras.applications import VGG16
    import numpy as np
    
    img_path = './creative_commons_elephant.jpg' 
    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)
    
    model = VGG16(weights='imagenet')
    
    preds = model.predict(x)
    print('Predicted:', decode_predictions(preds, top=3)[0])
    print(np.argmax(preds[0]))
    

    预测结果为:

    Predicted: [('n02504458', 'African_elephant', 0.90988594), ('n01871265', 'tusker', 0.085724816), ('n02504013', 'Indian_elephant', 0.00434713)]
    

    对这张图像预测的前三个类别分别为:

    • 非洲象(African elephant,92.5% 的概率)
    • 长牙动物(tusker,7% 的概率)
    • 印度象(Indian elephant,0.4% 的概率)

    网络识别出图像中包含数量不确定的非洲象。预测向量中被最大激活的元素是对应“非洲象”类别的元素,索引编号为 386。

    为了展示图像中哪些部分最像非洲象,我们来使用 Grad-CAM 算法。
    测试图像的“非洲象”类激活热力图,代码为:

    from tensorflow.keras.preprocessing import image
    from tensorflow.keras.applications.vgg16 import preprocess_input,decode_predictions
    import numpy as np
    from tensorflow.keras.applications.vgg16 import VGG16
    import matplotlib.pyplot as plt
    from tensorflow.keras import backend as K
    import cv2
    import tensorflow as tf
    tf.compat.v1.disable_eager_execution()
    
    model = VGG16(weights='imagenet')   # 包含最后的全连接层
    
    
    img_path = 'creative_commons_elephant.jpg'
    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('predicted: ', decode_predictions(preds, top=3)[0])
    print(np.argmax(preds[0]))
    
    elephant_output = model.output[:, 386]
    last_conv_layer = model.get_layer('block5_conv3')
    grads = K.gradients(elephant_output, last_conv_layer.output)[0]
    pooled_grads = K.mean(grads, axis=(0, 1, 2))
    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)
    plt.show()
    
    img = cv2.imread(img_path) 
    heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) 
    heatmap = np.uint8(255 * heatmap) 
    heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET) 
    superimposed_img = heatmap * 0.4 + img 
    cv2.imwrite('elephant_cam.jpg', superimposed_img)
    

    得到的图像为:
    在这里插入图片描述

    在这里插入图片描述

    这种可视化方法回答了两个重要问题:

    • 网络为什么会认为这张图像中包含一头非洲象?
    • 非洲象在图像中的什么位置?
      尤其值得注意的是,小象耳朵的激活强度很大,这可能是网络找到的非洲象和印度象的不
      同之处。
    展开全文
  • 针对人工提取高分辨率距离像(High Range Resolution Profile, HRRP)优良特征比较困难的问题,研究了基于一维卷积神经网络(Convolutional Neural Network, CNN)的HRRP识别方法。利用CNN具有分层学习特征的能力,...
  • 卷积可视化代码

    2019-03-02 18:13:35
    一维卷积可视化的代码,代码基于matlab,输出动态图片
  • 目录标题中间特征层可视化导入ImageNet VGG16网络加载任一图片构建多特征层输出模型所有中间层的显示参考 中间特征层可视化 导入ImageNet VGG16网络 导入基础包 import numpy as np import matplotlib.pyplot as plt...

    中间特征层可视化

    导入ImageNet VGG16网络

    导入基础包

    import numpy as np
    import matplotlib.pyplot as plt
    import tensorflow as tf
    

    导入ImageNet VGG16网络

    VGG16_model = tf.keras.applications.VGG16(include_top=True)
    VGG16_model.summary()
    

    加载任一图片

    随便网上找一张图,比如使用率最高的猫星人
    在这里插入图片描述
    读取本地图片

    def prepocess(x):
        x = tf.io.read_file(x)
        x = tf.image.decode_jpeg(x, channels=3)
        print(x.shape)
        x = tf.image.resize(x, [224,224])
        x = tf.cast(x, dtype=tf.float32)/255.
        return x
    
    img_path='Cat.jpg'
    img=prepocess(img_path)
    plt.figure()
    plt.imshow(img)
    

    构建多特征层输出模型

    对模型中16层特征层建立输出layers和多输出model模型

    from tensorflow.keras import models
    layer_outputs = [layer.output for layer in VGG16_model.layers[:16]] #前16层输出
    activation_model = models.Model(inputs=VGG16_model.input, outputs=layer_outputs) #构建能够输出前16层的模型
    

    图片输入网络,输出各中间层的特征

    input_image=tf.expand_dims(img, 0) # 扩维
    activations = activation_model.predict(input_image) #12组特征层输出
    activations[0].shape #0对应summary表中的输入层
    

    (1, 224, 224, 3)

    在这里插入图片描述
    举例展示第1卷积层的第1和第2特征层

    plt.matshow(activations[1][0,:,:,0], cmap='viridis') #第1卷积层的第1特征层输出
    plt.matshow(activations[1][0,:,:,1], cmap='viridis') #第1卷积层的第0特征层输出
    

    可以看出第1特征层主要捕获了猫星人的轮廓特征,第2特征层主要捕获毛脸部的特征
    在这里插入图片描述
    在这里插入图片描述

    所有中间层的显示

    layer_names = []
    for layer in VGG16_model.layers[:16]:
        layer_names.append(layer.name) #特征层的名字
    
    images_per_row=16
    
    for layer_name, layer_activation in zip (layer_names[1:16], activations[1:16]):
        n_feature = layer_activation.shape[-1] # 每层输出的特征层数
        size = layer_activation.shape[1]  #每层的特征大小
        n_cols = n_feature//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): #平铺每行
                # print(layer_activation.shape)
                # print(col*images_per_row+row)
                channel_image = layer_activation[0,:,:,col*images_per_row+row] # 写入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')
                # print(channel_image.shape)
                # print(display_grid[col*size:(col+1)*size, row*size:(row+1)*size].shape)
                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')
    
    

    康康结果,确实符合随着层数的加深,激活变得越来越抽象,以及激活的稀疏度(sparsity)随着层数的加深而增大的预期结果。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    参考

    《Deep Learning with Python》

    展开全文
  • 卷积神经网络特征图可视化方法及其意义


    特征图可视化方法

    1. tensor->numpy->plt.save

    以VGG网络可视化为例,参考代码见链接

    • 不同层的特征图比较
    modulelist = list(vgg.features.modules())
    
    def to_grayscale(image):
        # mean value
        image = torch.sum(image, dim=0)
        image = torch.div(image, image.shape[0])
        return image
    
    def layer_outputs(image):
        outputs = []
        names = []
        for layer in modulelist[1:]:
            outputs.append(layer(image))
            names.append(str(layer))
            
        output_im = []
        for i in outputs:
            temp = to_grayscale(i.squeeze(0))
            output_im.append(temp.data.cpu().numpy())
    
        fig = plt.figure()
        plt.rcParams["figure.figsize"] = (30, 50)
    
        for i in range(len(output_im)):
            a = fig.add_subplot(8, 4, i+1)
            imgplot = plt.imshow(output_im[i])
            plt.axis('off')
            a.set_title(names[i].partition('(')[0], fontsize=30)
    
        plt.savefig('layer_outputs.jpg', bbox_inches='tight')
    
    • 指定层的不同通道特征图比较
    def filter_outputs(image, layer_to_visualize):
        if layer_to_visualize < 0:
            layer_to_visualize += 31
        output = None
        name = None
        for count, layer in enumerate(modulelist[1:]):
            image = layer(image)
            if count == layer_to_visualize: 
                output = image
                name = str(layer)
        
        filters = []
        output = output.data.squeeze()
        for i in range(output.shape[0]):
            filters.append(output[i, :, :])
            
        fig = plt.figure()
        plt.rcParams["figure.figsize"] = (10, 10)
    
        for i in range(int(np.sqrt(len(filters))) * int(np.sqrt(len(filters)))):
            fig.add_subplot(np.sqrt(len(filters)), np.sqrt(len(filters)), i+1)
            imgplot = plt.imshow(filters[i].cpu())
            plt.axis('off')
    

    2. register_forward_pre_hook函数实现特征图获取

    采用register_forward_pre_hook(hook_func: Callable[..., None])函数获取特征图,括号中的参数是一个需要自行实现的函数名,其参数 module, input, output 固定,分别代表模块名称、一个tensor组成的tuple输入和tensor输出;随后采用torchvision.utils.make_gridtorchvision.utils.save_image将特征图转化为 PIL.Image 类型,存储为png格式图片并保存。保存图片的尺寸与特征图张量尺寸一致。关于上述函数的详细解释可参考博文
    其中由于hook_func参数固定,故定义get_image_name_for_hook函数为不同特征图命名,并定义全局变量COUNT表示特征图在网络结构中的顺序。具体实现如下。

    COUNT = 0  # global_para for featuremap naming
    IMAGE_FOLDER = './save_image'
    INSTANCE_FOLDER = None
    
    
    def hook_func(module, input, output):
        image_name = get_image_name_for_hook(module)
        data = output.clone().detach().permute(1, 0, 2, 3)
        
        # torchvision.utils.save_image(data, image_name, pad_value=0.5)
        from PIL import Image
        from torchvision.utils import make_grid
        grid = make_grid(data, nrow=8, padding=2, pad_value=0.5, normalize=False, range=None, scale_each=False)
        ndarr = grid.mul_(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to('cpu', torch.uint8).numpy()
        im = Image.fromarray(ndarr)
        # wandb save from jpg/png file
        wandb.log({f"{image_name}": wandb.Image(im)})
        # save locally
        # im.save(image_path)
    
    
    def get_image_name_for_hook(module):
        os.makedirs(INSTANCE_FOLDER, exist_ok=True)
        base_name = str(module).split('(')[0]
        image_name = '.'  # '.' is surely exist, to make first loop condition True
        
        global COUNT
        while os.path.exists(image_name):
            COUNT += 1
            image_name = '%d_%s' % (COUNT, base_name)
        return image_name
    
    
    if __name__ == '__main__':
        # clear output folder
        if os.path.exists(IMAGE_FOLDER):
            shutil.rmtree(IMAGE_FOLDER)
            
        # TODO: wandb & model initialization
        
        model.eval()
        # layers to log
        modules_for_plot = (torch.nn.LeakyReLU, torch.nn.BatchNorm2d, torch.nn.Conv2d)
        for name, module in model.named_modules():
            if isinstance(module, modules_for_plot):
                module.register_forward_hook(hook_func)
                
        index = 1
        for idx, batch in enumerate(val_loader):
            # global COUNT
            COUNT = 1
            INSTANCE_FOLDER = os.path.join(IMAGE_FOLDER, f'{index}_pic')
            # forward
            images_val = Variable(torch.from_numpy(batch[0]).type(torch.FloatTensor)).cuda()
            outputs = model(images_val)
    

    3. 反卷积可视化

    参考文献:Visualizing and Understanding Convolutional Networks
    对特征图 tensor 张量进行反池化-反激活-反卷积得到与原始输入图片尺寸一致的特征图。

    • 反卷积为卷积核转置后进行卷积操作(实为转置卷积);
    • 反激活与激活操作相同,直接调用ReLU函数(保证输出值非负即可);
    • 反池化操作为利用池化过程中记录的激活值位置信息(Switches)复原特征图尺寸,其余位置赋零值。

    特征图可视化的意义

    1. 改进训练网络结构

    图(b)包含过多低频、高频信息,很少有中频信息;图(d)中存在较多混叠伪影。因此对神经网络进行如下改进:

    1. 将卷积核尺寸从11×11缩小为7×7
    2. 将卷积层步长从4缩减为2

    改进后对应特征层输出如图(c)和图(e)所示,特征提取结果更为鲜明,无效特征(dead feature map)减少,且特征图更加清晰,混影减少。

    2. 删除冗余节点实现模型压缩

    可视化结果里有一些纯黑的特征图(下图红色方框标出),即所谓的 dead feature map,且不同的输入数据下固定卷积层的 dead feature map 位置相同。这些 dead feature map 没有办法提供有效信息,又因它们位置固定,因此可以将对应的卷积核从网络中剔除,起到模型压缩的作用。

    展开全文
  • 在pyexamples生产个"test_simple.pdf"文件 1.4 绘制自己的网络结构 在PlotNeuralNet-master文件夹(项目根目录)创建my_project文件夹,并进入该文件夹: mkdir my_project cd my_project 创建并编辑my_arch.py(这...
  • 【keras】一维卷积神经网络多分类

    万次阅读 多人点赞 2020-06-20 13:05:05
    刚刚接触到深度学习,前2个月的时间里,我用一维卷积神经网络实现了对于一维数据集的分类和回归。由于在做这次课题之前,我对深度学习基本上没有过接触,所以期间走了很多弯路。 在刚刚收到题目的要求时,我选择...
  • 卷积神经网络可以看作个黑箱子,打破黑箱子就需要处理中间过程。对于神经网络,底层卷积核提取的特征是基础的特征,对于人来说比较好理解,但是层数越高,特征就越抽象,对人来说也就越来越难理解。采用可视化就...
  • 深度学习笔记:卷积神经网络可视化--特征图

    万次阅读 多人点赞 2022-01-27 14:52:26
    特征图的可视化,是指对于给定输入图像,展示模型处理后的各中间层(包括卷积层和池化层等)输出的特征图(各中间层的激活函数的输出代表...所以将这些特征图可视化的正确方法是将每个通道的内容分别会支持成二图像。
  • 基于一维卷积神经网络的滚动轴承故障识别

    千次阅读 热门讨论 2021-01-25 15:14:37
    基于一维卷积神经网络的滚动轴承故障识别 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录基于一维卷积神经网络的滚动轴承故障识别一、数据预处理二、模型搭建三、使用步骤1.引入库2....
  • 卷积神经网络可视化

    千次阅读 2017-05-09 15:29:25
    t-SNE(数据降维)可视化技术(给她个高维数据,根据数据的结构和关系自动进行内聚) ConvNets逐渐的将不同的图片分类,我们可以将不同的图片嵌入到二的空间,这样相似的图片就会在一起.有很多嵌入的方法,t-...
  • 卷积神经网络可视化与可解释性

    千次阅读 2018-05-05 14:59:24
    卷积网络可视化与可解释性相关资料的一些整理,不断更新中~ 主要作用 进一步理解CNN 帮助我们分析训练好的网络 学会如何生成热力图,如下图: 博客 Distill 非常推荐的个网站 Global Average ...
  • 卷积神经网络及其特征图可视化

    千次阅读 2020-02-27 22:16:39
    参考链接:https://www.jianshu.com/p/362b637e2242 参考链接:https://blog.csdn.net/dcrmg/article/details/81255498/ 参考链接:https://zhuanlan.zhihu.com/p/59917842 参考链接:... 卷积与...
  • 最近两年深层的卷积神经网络,进展非常惊人,在计算机视觉方面,识别精度不断的突破,CVPR上的关于CNN的文献大堆。比如调整了某个参数,CNN结果精度飙升,但如果别人问你,为什么这样调参精度会飙升呢,你所设计的...
  • python学习之卷积神经网络可视化

    千次阅读 2019-07-01 23:07:33
    我们将介绍另可视化技术,它有助于理解给定图像的哪些部分引导convnet做出最终的分类决策。这有助于“调试”convnet的决策过程,特别是在分类错误的情况下。它还允许您在图像中定位特定对象。 这类技术被称为...
  • 三、构建卷积神经网络 四、编译&&运行 五、模型评估 通过卷积操作缩小了图像的内容,将模型注意力集中在图像特定的、明显的特征上。 这种计算对于计算机视觉来说是非常理想的,因为通常情况下,能够像...
  • 基于多通道的一维卷积神经网络故障诊断

    千次阅读 热门讨论 2021-04-07 15:38:09
    、数据准备 本次采用的PHM 2009 Challenge Data的全套齿轮箱数据集进行实验分析,包括齿轮、轴承以及轴的故障。其实验平台如下图所示: 本次选用50HZ的转速下低负载通道1的振动信号数据进行构建数据集,每类样本...
  • 卷积神经网络的特征图可视化秘籍——PyTorch实现可视化的定义及步骤PyTorch实现以预训练好的VGG16为例进行可视化关键代码剖析如果是自行搭建的网络,如何索引网络层?继续使用序号索引不使用序号,直接索引模型内部...
  • 卷积神经网络CNN的可视化

    千次阅读 2019-12-26 14:23:03
    通常我们认为深度学习模型是“黑盒”的,即模型学到的表示...1)可视化卷积神经网络的中间输出(中间激活) 有助于理解卷积网络神经连续的层如何对输入进行变换,也有助于初步了解卷积神经网络每个过滤器的含义。 ...
  • PyTorch可视化理解卷积神经网络

    千次阅读 2019-01-21 11:25:39
    造就机器能够获得在这些视觉方面取得优异性能可能是源于种特定类型的神经网络——卷积神经网络(CNN)。如果你是个深度学习爱好者,你可能早已听说过这种神经网络,并且可能已经使用一些深度学习...
  • pytorch 提取卷积神经网络的特征图可视化

    万次阅读 多人点赞 2019-08-10 17:33:14
      先看效果图(第张是原图,后面的都是相应的特征图,这里使用的网络是resnet50,需要注意的是下面图片显示的特征图是经过放大后的图,原图是比较小的图,因为太小不利于我们观察): 2、完整代码 import os ...
  • 介绍生物医学信号在研究和数据科学领域始终发挥着重要作用。就卷积神经网络 (CNN) 而言,这种特殊算法在定义深度学习 (DL) 等最复杂和最高级算法的架构方面发挥着重要作用。谈到深度学习,...
  • 卷积神经网络的特征图可视化

    千次阅读 热门讨论 2019-04-11 15:50:49
    原图: Keras代码: ...from keras.models import Sequential ...因为第卷积设置了一共有9个卷积核,所以对应就会有9张特征图 而网络的输出则是9张特征图合在一起,形成总的特征图:
  • 池化层和卷积层很类似,也是用卷积核在图上移动。唯一的不同就是池化层中核和图片窗口的操作不再是线性的。 最大池化和平均池化是最常见的池化函数。最大池化选取当前核覆盖的图片窗口中最大的数,而平均池化则...
  • 卷积神经网络的Tensorboard可视化

    千次阅读 2019-06-10 14:24:41
    Tensorboard是TensorFlow自带的可视化工具,因为想知道神经网络的中间环节到底是如何变化的,比如损失函数的变化过程、参数的分布、参数更新的过程、卷积核的样子等等,所以需要用到Tensorboard来帮助我。...
  • 12、理解与可视化卷积神经网络

    千次阅读 2018-09-02 18:00:44
    12.1 可视化卷积神经网络学习到的东西 12.1.1可视化激活和第层权重 12.1.2 找到对神经元有最大激活的图像 12.1.3用 t-SNE 嵌入代码 12.1.4遮挡部分图像 12.1.5可视化数据梯度及其他文献 12.1.6基于CNN代码...
  • 卷积神经网络种特殊类型的人工神经网络,广泛应用于图像识别。这种架构的成功始于 2015 年,当时凭借这种方法赢得了 ImageNet 图像分类挑战。 这些方法非常强大并且能够很好地进行预测,但同时它们也难以解释。...
  • 1.keras可视化深度网络各层特征;2.CNN原理

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,816
精华内容 5,126
关键字:

一维卷积神经网络可视化

友情链接: denglujiemian.rar