前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深度学习要点:可视化卷积神经网络

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

作者头像
AiTechYun
发布2018-04-19 15:09:23
2.3K0
发布2018-04-19 15:09:23
举报
文章被收录于专栏:ATYUN订阅号ATYUN订阅号

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/

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

代码语言:javascript
复制
%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中,你可以实现它如下:

代码语言:javascript
复制
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可视化过滤器

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

代码语言:javascript
复制
top_layer= model.layers [0]
plt.imshow(top_layer.get_weights()[0] [:,:,:,0] .squeeze(),cmap='gray')

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

2.激活映射

2.1最大激活

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

代码语言:javascript
复制
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])

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

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

代码语言:javascript
复制
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)。这可以让我们直观地看出图像的哪些部分对于该模型最重要,以便对实际的类进行明确的区分。

代码语言:javascript
复制
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特征图

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

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

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

代码语言:javascript
复制
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使用倒数第二个卷积层的输出,而不是使用与输出相关的梯度。这是为了利用存储在倒数第二层中的空间信息。

代码语言:javascript
复制
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模型,以及为什么要以一个示例来做。希望这会给你一个直觉,告诉你如何在自己的深度学习应用中建立更好的模型。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ATYUN订阅号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 可视化CNN模型的重要性
  • 可视化CNN模型的方法
  • 1.初步方法
    • 1.1绘制模型架构
      • 1.2可视化过滤器
      • 2.激活映射
        • 2.1最大激活
          • 2.2图像遮挡
          • 3.基于梯度的方法
            • 3.1特征图
              • 3.2基于梯度的GRAD-CAM
              • 结语
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档