“你的神经网络是如何生成这个结果的?”这个问题也曾让许多数据科学家陷入了困境。其实,让我们去解释一个层数较少的简单神经网络工作原理并不难,但是当我们将计算机视觉项目中的神经网络层数增加到1000层时,它的可解释性就非常差了。
现实情况是,我们的用户或者终端需要可解释性——他们想知道我们的模型是如何得到最终的结果的。但是,深度神经网络的工作原理没有办法通过文字被清楚地描述出来,这个时候深度神经网络就被打上了“黑盒子”的标签,那我们该如何摆脱这个标签呢?
我们可以通过可视化来解决这个问题!当一个神经网络通过可视化的方式展示出来时,他的可解释性将会得到极大的提升,可视化可以将神经网络模型中处理数据的过程清晰展现。尤其当我们处理基于成千上万数据的卷积神经网络(CNN)时更是如此。
在本文中,我们将介绍多种用于可视化卷积神经网络的技术。 此外,我们还可以从这些可视化中加深观察信息,以调整我们的CNN模型。
内容表
1.我们为什么要使用可视化解码神经网络?
2.设置模型的体系结构
3.访问CNN的每个层
4.滤波器—可视化CNN的构成模块
5.最大化激活—可视化模型所期望的内容
6.遮挡贴图—可视化输入中的重要内容
7.显著性贴图—可视化输入特征的贡献
8.分类激活映射
9.可视化分层输出—可视化过程
我们为什么要使用可视化解码神经网络?
除可视化之外,有很多方法可以帮助我们去理解神经网络是如何工作的,那么这篇文章为什么要转向可视化这种非常规途径呢?
我们通过一个例子来回答这个问题。假设我们正在做一个对动物图片进行分类的项目,如分类雪豹与阿拉伯豹。从图片来看,我们可以使用图像背景来区分这两种动物。
两种动物的栖息地可以形成鲜明的对比。大多数雪豹图像的背景里都会有雪,而大多数阿拉伯豹图片里都会有一片茫茫沙漠。
问题来了——模型可以通过分类雪与沙漠的图像从而去分类雪豹与阿拉伯豹。那么,我们如何确保我们的模型正确地学习了这两种不同类型豹子的不同特征呢?可视化会给我们答案。
可视化有助于我们了解哪些特征正在指导模型对图像进行分类的决策。
将模型可视化有许多种方法,在本文中,我们将展示其中的一些。
设置模型的体系结构
实践是最好的学习方式之一。因此,我们立刻开始研究模型的代码。
在本文中,我们在ImageNet数据集上使用VGG16架构模型和预训练权重。首先我们先将模型程序导入并开始理解其架构。
我们将使用Keras中的'model.summary()'函数来可视化模型体系结构。在我们进入模型构建部分之前,这是非常重要的一步。我们需要确保输入和输出形状与我们的问题陈述相匹配,因此我们先可视化模型摘要。
#importing required modules
from keras.applications import VGG16
#loading the saved model
#we are using the complete architecture thus include_top=True
model = VGG16(weights='imagenet',include_top=True)
#show the summary of model
model.summary()
以下是上述代码生成的模型摘要:
我们有了模型的详细架构以及每层的可训练参数的数量。上面的输出可以多花一点时间去浏览,这样才能了解我们现有的数据情况。
我们仅训练模型层的一个子集(特征提取)是很重要的。 我们可以生成模型摘要,并确保不可训练参数的数量与我们不想训练的层匹配。
此外,我们可以使用可训练参数的总数来检查我们的GPU是否能够为训练模型分配足够的内存。 这对于我们大多数在个人电脑上工作的人来说,是一个比较熟悉的挑战!
访问CNN的每个层
既然我们知道如何获得模型的整体架构,让我们深入探索并尝试探索每个独立的层。
实际上,访问Keras模型的各个层并提取与每个层相关的参数非常容易。 这包括图层权重和其他信息,如滤波器的数量。
现在,我们将创建将图层名称映射到其相应特征和图层权重的字典:
#creating a mapping of layer name ot layer details
#we will create a dictionary layers_info which maps a layer name to its charcteristics
layers_info = {}
for i in model.layers:
layers_info[i.name] = i.get_config()
#here the layer_weights dictionary will map every layer_name to its corresponding weights
layer_weights = {}
for i in model.layers:
layer_weights[i.name] = i.get_weights()
print(layers_info['block5_conv1'])
上面的代码给出了以下输出,它由block5_conv1层的不同参数组成:
{'name': 'block5_conv1',
'trainable': True,
'filters': 512,
'kernel_size': (3, 3),
'strides': (1, 1),
'padding': 'same',
'data_format': 'channels_last',
'dilation_rate': (1, 1),
'activation': 'relu',
'use_bias': True,
'kernel_initializer': {'class_name': 'VarianceScaling',
'config': {'scale': 1.0,
'mode': 'fan_avg',
'distribution': 'uniform',
'seed': None}},
'bias_initializer': {'class_name': 'Zeros', 'config': {}},
'kernel_regularizer': None,
'bias_regularizer': None,
'activity_regularizer': None,
'kernel_constraint': None,
'bias_constraint': None}
不知你有没有注意到图层'block5_conv1'的可训练参数是否为真? 这意味着我们可以通过进一步训练模型来更新图层权重。
滤波器—可视化CNN的构成模块
滤波器是任何卷积神经网络的基本构建模块。不同的滤波器从图像中提取不同类型的特征。下面的GIF图非常清楚地说明了这一点:
如图所示,每个卷积层都由多个滤波器组成。查看我们在上一节中生成的输出 - 'block5_conv1'层由512个滤波器组成。这是对应的,是吧?
让我们绘制每个VGG16块的第一个卷积层的第一个滤波器:
layers = model.layers
layer_ids = [1,4,7,11,15]
#plot the filters
fig,ax = plt.subplots(nrows=1,ncols=5)
for i in range(5):
ax[i].imshow(layers[layer_ids[i]].get_weights()[0][:,:,:,0][:,:,0],cmap='gray')
ax[i].set_title('block'+str(i+1))
ax[i].set_xticks([])
ax[i].set_yticks([])
我们可以在上面的输出中看到不同层的滤波器。由于VGG16仅使用3×3滤波器,因此所有滤波器都具有相同的形状。
最大化激活—可视化模型所期望的内容
我们使用下面的图片来理解最大化激活的概念:
你认为对模型去识别大象来说哪些特征是重要的?下面是我想到的一些主要的特征:
那就是我们在本能情况下如何识别大象的,对吧?现在,当我们看看当我们尝试优化任意分类大象图像的模型时,我们在过程中得到了什么。
我们知道CNN中的每个卷积层都在前一层的输出中寻找相似的模式。当输入由它正在寻找的模式组成时,卷积层的激活被最大化。
在最大化激活技术中,我们更新每层的输入,以便将最大化激活的损失降到最低。
我们是如何做到这一点的?我们通过计算相对于输入的激活损失函数的梯度,然后相应得更新输入:
下面是这个操作的代码:
#importing the required modules
from vis.visualization import visualize_activation
from vis.utils import utils
from keras import activations
from keras import applications
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = (18,6)
#creating a VGG16 model using fully connected layers also because then we can
#visualize the patterns for individual category
from keras.applications import VGG16
model = VGG16(weights='imagenet',include_top=True)
#finding out the layer index using layer name
#the find_layer_idx function accepts the model and name of layer as parameters and return the index of respective layer
layer_idx = utils.find_layer_idx(model,'predictions')
#changing the activation of the layer to linear
model.layers[layer_idx].activation = activations.linear
#applying modifications to the model
model = utils.apply_modifications(model)
#Indian elephant
img3 = visualize_activation(model,layer_idx,filter_indices=385,max_iter=5000,verbose=True)
plt.imshow(img3)
我们的模型使用随机输入对印度象进行分类,生成了以下输出:
从上面的图像中,我们可以观察到该模型需要像牙齿,大眼睛和象牙这样的结构。现在,这些信息对于我们检查数据集的完整性非常重要。 因为印度象通常存在于长满树木或长草的栖息地中,所以模型可能会侧重于背景中的栖息地特征,而这是不对的。
可视化输入中的重要内容—Occlusion Maps(遮挡贴图)
激活最大化用于可视化在图像中的模型期望的输出。在另一方面,遮挡部分图,可以帮助我们找到对模型来说那个部分是重要的。
现在,为了了解遮挡图后的模型是如何工作的,我们想根据制造商对汽车进行分类的模型,如丰田,奥迪等:
你能知道是那家生产商制造了上面这辆车吗?很大程度上是不能的,因为放置公司标志的部分在图片里被遮挡了。我们是以分类为目的,所以被遮挡的这部分对我们来说很重要。
类似地,因为这样的遮挡图的出现,我们遮挡图片的某些部分,然后计算出它属于哪一类的概率。如果概率降低,则意味着图像的被遮挡部分对于该类是重要的。否则,这就是不重要的。
这里,我们根据图像每个部分的像素值做概率分配,然后将它标准化并生成热图:
import numpy as np
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Activation, Conv2D, MaxPooling2D
from keras.optimizers import Adam
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.preprocessing.image import ImageDataGenerator
from keras.activations import relu
%matplotlib inline
import matplotlib.pyplot as plt
def iter_occlusion(image, size=8):
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 y in range(occlusion_padding, image.shape[0] + occlusion_padding, size):
for x in 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]
上面的代码定义了一个函数iter_occlusion,它返回一个具有不同被遮挡部分的图像。
现在,让我们导入图像并绘制它:
from keras.preprocessing.image import load_img
# load an image from file
image = load_img('car.jpeg', target_size=(224, 224))
plt.imshow(image)
plt.title('ORIGINAL IMAGE')
现在,我们将进行下面三个步骤:
from keras.preprocessing.image import img_to_array
from keras.applications.vgg16 import preprocess_input
# convert the image pixels to a numpy array
image = img_to_array(image)
# reshape data for the model
image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2]))
# prepare the image for the VGG model
image = preprocess_input(image)
# predict the probability across all output classes
yhat = model.predict(image)
temp = image[0]
print(temp.shape)
heatmap = np.zeros((224,224))
correct_class = np.argmax(yhat)
for n,(x,y,image) in enumerate(iter_occlusion(temp,14)):
heatmap[x:x+14,y:y+14] = model.predict(image.reshape((1, image.shape[0], image.shape[1], image.shape[2])))[0][correct_class]
print(x,y,n,' - ',image.shape)
heatmap1 = heatmap/heatmap.max()
plt.imshow(heatmap)
这非常有意思。 我们现在将使用标准化的热图概率创建一个掩模并绘制它:
import http://skimage.io as io
#creating mask from the standardised heatmap probabilities
mask = heatmap1 < 0.85
mask1 = mask *256
mask = mask.astype(int)
io.imshow(mask,cmap='gray')
最后,我们将遮挡码强加在输入图像上并绘制:
import cv2
#read the image
image = cv2.imread('car.jpeg')
image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#resize image to appropriate dimensions
image = cv2.resize(image,(224,224))
mask = mask.astype('uint8')
#apply the mask to the image
final = cv2.bitwise_and(image,image,mask = mask)
final = cv2.cvtColor(final,cv2.COLOR_BGR2RGB)
#plot the final image
plt.imshow(final)
你能猜到为什么我们只看到图像的某些部分吗? 这其实是正确的 - 只有输入图像中对其输出类概率有重大贡献的那些部分才是可见的。 简而言之,这就是被遮挡图的全部内容。
可视化输入特征的贡献—显著性贴图
显著性图是另一种基于梯度的可视化技术。
显着图计算每个像素值对模型输出的影响。 这涉及计算输出相对于输入图像的每个像素的梯度。这告诉我们如何根据输入图像像素的微小变化输出类别变化。梯度的所有正值意味着像素值的微小变化将增加输出值:
这些梯度与图像形状相同(梯度是根据每个像素值计算的),为我们提供了直观的重点。让我们看看如何为任何图像生成显著性图。 首先,我们将使用以下代码段读取输入图像。
现在,我们将使用VGG16模型为图像生成显著性图:
# 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, 'predictions')
# Swap softmax with linear
model.layers[layer_idx].activation = activations.linear
model = utils.apply_modifications(model)
#generating saliency map with unguided backprop
grads1 = visualize_saliency(model, layer_idx,filter_indices=None,seed_input=image)
#plotting the unguided saliency map
plt.imshow(grads1,cmap='jet')
我们看到该模型更侧重于狗的面部部分。现在,让我们看看反向传播的结果:
#generating saliency map with guided backprop
grads2 = visualize_saliency(model, layer_idx,filter_indices=None,seed_input=image,backprop_modifier='guided')
#plotting the saliency map as heatmap
plt.imshow(grads2,cmap='jet')
引导反向传播将所有负梯度截断为0,这意味着仅更新对分类概率具有正影响的像素。
分类激活图(梯度加权)
分类激活图也是一种神经网络可视化技术,它基于根据激活图的梯度或它们对输出的贡献来权衡激活图这样的想法。
以下摘自Grad-CAM论文给出了该技术的要点:
梯度加权分类激活映射(Grad-CAM),使用任何目标概念的梯度(比如“狗”或甚至是标题的对数),流入最终的卷积层以生成粗略的定位图,突出显示重要区域中的重要区域。用于预测概念的图像。
从本质上来说,我们采用最后一层卷积层的特征映射,并使用相对于特征映射的输出的梯度对每个滤波器进行加权(乘)。 Grad-CAM涉及以下步骤:
1.获取最终卷积层的输出要素图。对于VGG16,此功能图的形状为14x14x512; 2.计算输出相对于要素图的梯度 3.将全局平均池化应用于梯度 4.将要素图与相应的池化梯度相乘 我们可以在下面看到输入图像及其对应的分类激活图:
现在,我们来给上面的图像生成分类激活图:
可视化过程—分层输出可视化
CNN的起始层通常寻找像边缘这样的低级特征。随着我们的深入,功能也会发生变化。
可视化模型的不同层的输出有助于我们看到在相应层突出显示图像的是哪些特征。此步骤对于针对我们的问题微调架构特别重要。为什么?因为我们可以看到哪些图层提供了哪种特征,然后决定我们要在模型中使用哪些图层。
例如,可视化图层输出可以帮助我们比较神经样式转移问题中不同层的性能。
让我们看看如何在VGG16模型的不同层获得输出:
#importing required libraries and functions
from keras.models import Model
#defining names of layers from which we will take the output
layer_names = ['block1_conv1','block2_conv1','block3_conv1','block4_conv2']
outputs = []
image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2]))
#extracting the output and appending to outputs
for layer_name in layer_names:
intermediate_layer_model = Model(inputs=model.input,outputs=model.get_layer(layer_name).output)
intermediate_output = intermediate_layer_model.predict(image)
outputs.append(intermediate_output)
#plotting the outputs
fig,ax = plt.subplots(nrows=4,ncols=5,figsize=(20,20))
for i in range(4):
for z in range(5):
ax[i][z].imshow(outputs[i][0,:,:,z])
ax[i][z].set_title(layer_names[i])
ax[i][z].set_xticks([])
ax[i][z].set_yticks([])
plt.savefig('layerwise_output.jpg')
上图显示了VGG16的每一层从图像中提取的不同特征(模块5除外)。我们可以看到起始层对应于边缘等低级特征,而后面的层则看到汽车的车顶,排气等特征。
结语
可视化永远不会让我感到惊讶。有多种方法可以理解技术的工作原理,但可视化可以使它变得更加有趣。以下是您应该查看的几个资源:
如果您对本文有任何疑问或反馈,请与我们联系。 我很乐意参与讨论!
数据分析咨询请扫描二维码
在当今以数据为导向的商业环境中,数据分析师的角色变得越来越重要。无论是揭示消费者行为的趋势,还是优化企业运营的效率,数据 ...
2024-11-17金融数学是一门充满挑战和机遇的专业,它将数学、统计学和金融学的知识有机结合,旨在培养能够运用数学和统计方法解决复杂金融市 ...
2024-11-16在信息时代的浪潮中,大数据已成为推动创新的重要力量。无论是在商业、医疗、金融,还是在日常生活中,大数据扮演的角色都愈发举 ...
2024-11-16随着大数据技术的迅猛发展,数据已经成为现代商业、科技乃至生活各个方面的重要资产。大数据专业的毕业生在这一变革背景下,拥有 ...
2024-11-15随着大数据技术的迅猛发展,数据已经成为现代商业、科技乃至生活各个方面的重要资产。大数据专业的毕业生在这一变革背景下,拥有 ...
2024-11-15在快速演变的数字时代,数据分析已成为多个行业的核心驱动力。无论你是刚刚踏入数据分析领域,还是寻求进一步发展的专业人士,理 ...
2024-11-15Python作为一种通用编程语言,以其简单易学、功能强大等特点,成为众多领域的核心技术驱动者。无论是初学者还是有经验的编程人员 ...
2024-11-15在当今数据驱动的世界中,数据分析已成为许多行业的基础。无论是商业决策,产品开发,还是市场策略优化,数据分析都扮演着至关重 ...
2024-11-15数据分析作为现代商业和研究领域不可或缺的一部分,吸引了越来越多的初学者。然而,自学数据分析的过程中,初学者常常会遇到许多 ...
2024-11-15在当今的数据驱动世界中,机器学习方法在数据挖掘与分析中扮演着核心角色。这些方法通过从数据中学习模式和规律来构建模型,实现 ...
2024-11-15随着数据在各个行业的重要性日益增加,数据分析师在商业和技术领域的角色变得至关重要。其核心职责之一便是通过数据可视化,将复 ...
2024-11-15数据分析师的职责不仅仅局限于解析数据和得出结论,更在于将这些复杂的信息转换为清晰、易懂且具有影响力的沟通。良好的沟通能力 ...
2024-11-15数字化转型是企业提升竞争力和实现可持续发展的关键路径。面对快速变化的市场环境,以及技术的飞速发展,企业在数字化转型过程中 ...
2024-11-15CDA数据分析师认证:CDA认证分为三个等级:Level Ⅰ、Level Ⅱ和Level Ⅲ,每个等级的报考条件如下: Le ...
2024-11-14自学数据分析可能是一条充满挑战却又令人兴奋的道路。随着数据在现代社会中的重要性日益增长,掌握数据分析技能不仅能提升你的就 ...
2024-11-14数据分析相关职业选择 数据分析领域正在蓬勃发展,为各种专业背景的人才提供了丰富的职业机会。从初学者到有经验的专家,每个人 ...
2024-11-14数据挖掘与分析在金融行业的使用 在当今快速发展的金融行业中,数据挖掘与分析的应用愈发重要,成为驱动行业变革和提升竞争力的 ...
2024-11-14学习数据挖掘需要掌握哪些技能 数据挖掘是一个不断发展的领域,它结合了统计学、计算机科学和领域专业知识,旨在从数据中提取有 ...
2024-11-14统计学作为一门基于数据的学科,其广泛的应用领域和多样的职业选择,使得毕业生拥有丰厚的就业前景。无论是在政府还是企业,统计 ...
2024-11-14在当今高速发展的技术环境下,企业正在面临前所未有的机遇和挑战。数字化转型已成为企业保持竞争力和应对市场变化的必由之路。要 ...
2024-11-13