深度学习要点:可视化卷积神经网络

AiTechYun

编辑:yuxiangyu

深度学习中最深入讨论的话题之一是如何解释和理解一个训练完成的模型,尤其是在医疗保健等高风险行业的背景下。“黑盒”这个词经常与深度学习算法联系在一起。如果我们不能解释它是如何工作的,我们如何相信模型的结果呢?

以一个为了检测癌症肿瘤而训练的深度学习模型为例。该模型告诉你它99%确定它检测到了癌症,但它并没有告诉你为什么或怎么确定的。

它是在MRI扫描(磁共振)中找到了一条重要线索,还是仅仅是扫描中的一个污点被错误地检测为肿瘤?这对患者来说是生死攸关的问题,医生经不起犯错。

在本文中,我们将探索如何将卷积神经网络(CNN)可视化。我们将了解CNN模型可视化的重要性以及将可视化的方法。我们还将研究一个可以帮助您更好地理解概念的实例示例

可视化CNN模型的重要性

正如我们在上面的癌症肿瘤示例中看到的那样,我们了解模型正在做什么以及它是如何做出预测,绝对是至关重要的。通常,下面列出的原因是深度学习实践者要记住的重点:

  1. 了解模型如何工作
  2. 协助超参数调优
  3. 找出模型的失败,并获得他们为什么失败的直觉
  4. 向消费者/最终用户或业务主管解释决策

可视化CNN模型的方法

一般来说,可视化CNN模型的方法可以根据其工作原理分为三种:

  • 初步方法 – 简单的展示一个训练完成的模型的整体结构。
  • 基于激活的方法 – 在这种方法中,我们破译单个神经元或一组神经元的激活,以了解他们在做什么的直觉。
  • 基于梯度的方法 – 这些方法倾向于在训练模型时操纵前向和反向传递形成的梯度。

我们会在下面的小节中详细介绍它们。在这里,我们将使用keras作为我们的库,用于构建深度学习模型,并使用keras-vis来可视化它们。

数据集:https://datahack.analyticsvidhya.com/contest/practice-problem-identify-the-digits/

在开始以下步骤前,确定你已经安装好必要的库,并执行:

%pylab inline
import os
import numpy as np
import pandas as pd
from scipy.miscimport imread
from sklearn.metricsimport accuracy_score

import keras
from keras.modelsimport Sequential, Model
from keras.layersimport Dense, Dropout, Flatten, Activation,Input
from keras.layersimport Conv2D, MaxPooling2D

# To stop potential randomness
seed= 128
rng= np.random.RandomState(seed)

data_dir= "../../datasets/MNIST"

train= pd.read_csv('../../datasets/MNIST/train.csv')
test= pd.read_csv('../../datasets/MNIST/Test_fCbTej3.csv')

img_name= rng.choice(train.filename)
filepath= os.path.join(data_dir,'train', img_name)

img= imread(filepath, flatten=True)

pylab.imshow(img, cmap='gray')
pylab.axis('off')
pylab.show()

temp= []
for img_namein train['filename']:
    image_path= os.path.join(data_dir,'train', img_name)
    img= imread(image_path, flatten=True)
    img= img.astype('float32')
    temp.append(img)

train_x= np.stack(temp)

train_x/= 255.0
train_x= train_x.reshape(-1,28,28,1).astype('float32')

temp= []
for img_namein test['filename']:
    image_path= os.path.join(data_dir,'test', img_name)
    img= imread(image_path, flatten=True)
    img= img.astype('float32')
    temp.append(img)

test_x= np.stack(temp)

test_x/= 255.0
test_x= test_x.reshape(-1,28,28,1).astype('float32')

train_y= keras.utils.np_utils.to_categorical(train.label.values)

split_size= int(train_x.shape[0]*0.7)

train_x, val_x= train_x[:split_size], train_x[split_size:]
train_y, val_y= train_y[:split_size], train_y[split_size:]

# define vars

epochs= 5
batch_size= 128

# import keras modules

from keras.modelsimport Sequential
from keras.layersimport Dense

# create model
model= Sequential()
model.add(Conv2D(32, kernel_size=(3,3),
                 activation='relu',
                 input_shape=(28,28,1)))
model.add(Conv2D(64, (3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax', name='preds'))


# compile the model with necessary attributes
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

trained_model= model.fit(train_x, train_y, nb_epoch=epochs, batch_size=batch_size, validation_data=(val_x, val_y))

pred= model.predict_classes(test_x)

img_name= rng.choice(test.filename)
filepath= os.path.join(data_dir,'test', img_name)

img= imread(filepath, flatten=True)

test_index= int(img_name.split('.')[0])- train.shape[0]

print ("Prediction is: ", pred[test_index])

pylab.imshow(img, cmap='gray')
pylab.axis('off')
pylab.show()

1.初步方法

1.1绘制模型架构

我们可以做的最简单的事情就是是打印或者说绘制模型。在这里,你可以打印单层神经网络的形状和每个层的参数。

在keras中,你可以实现它如下:

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param#  
=================================================================
conv2d_1 (Conv2D)            (None,26,26,32)       320      
_________________________________________________________________
conv2d_2 (Conv2D)            (None,24,24,64)       18496    
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None,12,12,64)       0        
_________________________________________________________________
dropout_1 (Dropout)          (None,12,12,64)       0        
_________________________________________________________________
flatten_1 (Flatten)          (None,9216)             0        
_________________________________________________________________
dense_1 (Dense)              (None,128)              1179776  
_________________________________________________________________
dropout_2 (Dropout)          (None,128)              0        
_________________________________________________________________
preds (Dense)                (None,10)               1290     
=================================================================
Total params:1,199,882
Trainable params:1,199,882
Non-trainable params:0

为了更具创造性和表现力 – 你也可以绘制架构图(keras.utils.vis_utils函数)。

1.2可视化过滤器

另一种方法是绘制训练模型的滤波器,以便我们可以了解这些过滤器的行为。例如,上述模型的第一层的第一个过滤器如下所示:

top_layer= model.layers [0]
plt.imshow(top_layer.get_weights()[0] [:,:,:,0] .squeeze(),cmap='gray')

一般来说,我们可以看到低级滤波器用作边缘检测器,而高级的倾向于捕捉像物品和面部这样的高级概念。

2.激活映射

2.1最大激活

要查看我们的神经网络正在做什么,我们可以在输入图像上应用滤波器,然后绘制输出。这使我们能够理解什么样的输入模式可以激活特定的过滤器。例如,可能会有一个面部过滤器,在图像中出现脸时激活。

from vis.visualizationimport visualize_activation
from vis.utilsimport utils
from kerasimport activations

from matplotlibimport pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize']= (18,6)

# Utility to search for layer index by name.
# Alternatively we can specify this as -1 since it corresponds to the last layer.
layer_idx= utils.find_layer_idx(model,'preds')

# Swap softmax with linear
model.layers[layer_idx].activation= activations.linear
model= utils.apply_modifications(model)

# This is the output node we want to maximize.
filter_idx= 0
img= visualize_activation(model, layer_idx, filter_indices=filter_idx)
plt.imshow(img[...,0])

我们可以将这个想法转移到所有的类中,并检查每个类的样子。

运行下面的脚本来检查它。

for output_idxin np.arange(10):
   # Lets turn off verbose output this time to avoid clutter and just see the output.
   img= visualize_activation(model, layer_idx, filter_indices=output_idx, input_range=(0.,1.))
   plt.figure()
   plt.title('Networks perception of {}'.format(output_idx))
   plt.imshow(img[...,0])

2.2图像遮挡

在图像分类问题中,一个自然的问题是模型是否真的识别出图像中对象的位置,或者只是使用了周边环境。基于遮挡的方法试图通过用灰色方块,系统地遮挡输入图像的不同部分,并监视分类器的输出来回答这个问题。这些例子清楚地显示了模型是在场景中定位对象,因为当物体被遮挡时,正确分类的概率显著下降。

为了理解这个概念,让我们从我们的数据集中取一个随机图像,并尝试绘制图像的热图(heatmap)。这可以让我们直观地看出图像的哪些部分对于该模型最重要,以便对实际的类进行明确的区分。

def iter_occlusion(image, size=8):
    # taken from https://www.kaggle.com/blargl/simple-occlusion-and-saliency-maps

   occlusion= np.full((size* 5, size* 5,1), [0.5], np.float32)
   occlusion_center= np.full((size, size,1), [0.5], np.float32)
   occlusion_padding= size* 2

   # print('padding...')
   image_padded= np.pad(image, ( \
   (occlusion_padding, occlusion_padding), (occlusion_padding, occlusion_padding), (0,0) \
   ),'constant', constant_values= 0.0)

   for yin range(occlusion_padding, image.shape[0]+ occlusion_padding, size):

       for xin range(occlusion_padding, image.shape[1]+ occlusion_padding, size):
           tmp= image_padded.copy()

           tmp[y- occlusion_padding:y+ occlusion_center.shape[0]+ occlusion_padding, \
             x- occlusion_padding:x+ occlusion_center.shape[1]+ occlusion_padding] \
             = occlusion

           tmp[y:y+ occlusion_center.shape[0], x:x+ occlusion_center.shape[1]]= occlusion_center

           yield x- occlusion_padding, y- occlusion_padding, \
             tmp[occlusion_padding:tmp.shape[0]- occlusion_padding, occlusion_padding:tmp.shape[1]- occlusion_padding]

i= 23 # for example
data= val_x[i]
correct_class= np.argmax(val_y[i])

# input tensor for model.predict
inp= data.reshape(1,28,28,1)

# image data for matplotlib's imshow
img= data.reshape(28,28)

# occlusion
img_size= img.shape[0]
occlusion_size= 4

print('occluding...')

heatmap= np.zeros((img_size, img_size), np.float32)
class_pixels= np.zeros((img_size, img_size), np.int16)

from collectionsimport defaultdict
counters= defaultdict(int)

for n, (x, y, img_float)in enumerate(iter_occlusion(data, size=occlusion_size)):

    X= img_float.reshape(1,28,28,1)
    out= model.predict(X)
    #print('#{}: {} @ {} (correct class: {})'.format(n, np.argmax(out), np.amax(out), out[0][correct_class]))
    #print('x {} - {} | y {} - {}'.format(x, x + occlusion_size, y, y + occlusion_size))

    heatmap[y:y+ occlusion_size, x:x+ occlusion_size]= out[0][correct_class]
    class_pixels[y:y+ occlusion_size, x:x+ occlusion_size]= np.argmax(out)
    counters[np.argmax(out)]+= 1

3.基于梯度的方法

3.1特征图

为了能够了解我们的模型关注哪个部分来进行预测,我们可以使用特征图。

使用特征图的概念非常简单 – 我们计算对于输入图像的输出分类的梯度。这会告诉我们,输出类别值的改变与输入图像中像素的微小变动之间的联系。梯度中的所有正值都告诉我们,对该像素小的改动会增加输出分类的值。因此,可视化这些与图像形状相同的梯度,应该能提供一些注意力的直觉。

直观地说,这种方法凸显了对输出贡献最大的显著的图像区域。

class_idx= 0
indices= np.where(val_y[:, class_idx]== 1.)[0]

# pick some random input from here.
idx= indices[0]

# Lets sanity check the picked image.
from matplotlibimport pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize']= (18,6)

plt.imshow(val_x[idx][...,0])


from vis.visualizationimport visualize_saliency
from vis.utilsimport utils
from kerasimport activations

# Utility to search for layer index by name.
# Alternatively we can specify this as -1 since it corresponds to the last layer.
layer_idx= utils.find_layer_idx(model,'preds')

# Swap softmax with linear
model.layers[layer_idx].activation= activations.linear
model= utils.apply_modifications(model)

grads= visualize_saliency(model, layer_idx, filter_indices=class_idx, seed_input=val_x[idx])
# Plot with 'jet' colormap to visualize as a heatmap.
plt.imshow(grads, cmap='jet')


# This corresponds to the Dense linear layer.
for class_idxin np.arange(10):
    indices= np.where(val_y[:, class_idx]== 1.)[0]
    idx= indices[0]

    f, ax= plt.subplots(1,4)
    ax[0].imshow(val_x[idx][...,0])

    for i, modifierin enumerate([None,'guided','relu']):
        grads= visualize_saliency(model, layer_idx, filter_indices=class_idx,
        seed_input=val_x[idx], backprop_modifier=modifier)
        if modifieris None:
            modifier= 'vanilla'
        ax[i+1].set_title(modifier)
        ax[i+1].imshow(grads, cmap='jet')

3.2基于梯度的GRAD-CAM

类激活地图(Class activation maps),即grad-CAM,是对模型在预测时观察到什么的另一种可视化方法。grad-CAM使用倒数第二个卷积层的输出,而不是使用与输出相关的梯度。这是为了利用存储在倒数第二层中的空间信息。

from vis.visualizationimport visualize_cam

# This corresponds to the Dense linear layer.
for class_idxin np.arange(10):
 indices= np.where(val_y[:, class_idx]== 1.)[0]
 idx= indices[0]

f, ax= plt.subplots(1,4)
 ax[0].imshow(val_x[idx][...,0])

for i, modifierin enumerate([None,'guided','relu']):
    grads= visualize_cam(model, layer_idx, filter_indices=class_idx,
    seed_input=val_x[idx], backprop_modifier=modifier)
    if modifieris None:
        modifier= 'vanilla'
    ax[i+1].set_title(modifier)
    ax[i+1].imshow(grads, cmap='jet')

结语

在本文中,我们介绍了如何可视化CNN模型,以及为什么要以一个示例来做。希望这会给你一个直觉,告诉你如何在自己的深度学习应用中建立更好的模型。

原文发布于微信公众号 - ATYUN订阅号(atyun_com)

原文发表时间:2018-03-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏深度学习思考者

Vehicle Logo Recognition System Based on Convolutional Neural Networks With a Pretraining Strategy

论文笔记Ⅰ 基于卷积神经网络的车辆标记识别系统 考虑文章中一些语法以及用词还挺好,先记录一下,留下来以后可能用到自己的paper中。 Abstract 由...

2558
来自专栏量子位

怎样构建深度学习模型?六步走,时刻小心过拟合 | 入门指南

1062
来自专栏人工智能LeadAI

keras学习笔记-黑白照片自动着色的神经网络-Alpha版

如今,上色都是人手工用Photoshop做的,一张图片要花好几个月才能完成,需要进行大量调查研究,光是其中的一张脸就需要多达20层图层。但是,基于深度神经网络的...

5485
来自专栏CreateAMind

以假乱真的生成图片的效果

昨天发的图片是训练到6小时的效果LS-GAN非常棒的效果!,今天略微调整继续训练:也出现了生成网络跟不上判别网络的情况,加快生成网络训练循环。

892
来自专栏数据科学与人工智能

【数据分析】数据分析领域中最为人称道的七种降维方法|技术专区

近来由于数据记录和属性规模的急剧增长,大数据处理平台和并行数据分析算法也随之出现。于此同时,这也推动了数据降维处理的应用。实际上,数据量有时过犹不及。有时在数据...

2126
来自专栏机器之心

学界 | 如何通过方差偏移理解批归一化与Dropout之间的冲突

3255
来自专栏锦小年的博客

Nilearn学习笔记4- 连接提取:用于直接连接的协方差

概要:给定一组时间序列(例如通过上篇博客中提到的方法在一群人的fmri数据中提取的时间序列集),功能连接组是表示不同的大脑区域之间的相互作用的连接。今天分享的是...

2287
来自专栏null的专栏

简单易学的机器学习算法——主成分分析(PCA)

一、数据降维        对于现在维数比较多的数据,我们首先需要做的就是对其进行降维操作。降维,简单来说就是说在尽量保证数据本质的前提下将数据中的维数降低。降...

3525
来自专栏大数据文摘

ResNet告诉我,我是不是世界上最美的人?

1406
来自专栏AI科技大本营的专栏

Fast.ai深度学习实战课程 Lesson7 学习笔记:CNN Architectures

本篇是AI100学院此前重点推出的《Fast.ai 深度学习实战课程》(中文字幕)第七节的学习笔记,分享者胡智豪。 如果你对深度学习感兴趣,该系列课程千万不...

4636

扫码关注云+社区

领取腾讯云代金券