京公网安备 11010802034615号
经营许可证编号:京B2-20210330
“你的神经网络是如何生成这个结果的?”这个问题也曾让许多数据科学家陷入了困境。其实,让我们去解释一个层数较少的简单神经网络工作原理并不难,但是当我们将计算机视觉项目中的神经网络层数增加到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除外)。我们可以看到起始层对应于边缘等低级特征,而后面的层则看到汽车的车顶,排气等特征。
结语
可视化永远不会让我感到惊讶。有多种方法可以理解技术的工作原理,但可视化可以使它变得更加有趣。以下是您应该查看的几个资源:
如果您对本文有任何疑问或反馈,请与我们联系。 我很乐意参与讨论!
CDA学员免费下载查看报告全文:2026全球数智化人才指数报告【CDA数据科学研究院】.pdf
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
近日,由 CDA 数据科学研究院重磅发布的《2026 全球数智化人才指数报告》,被中国教育科学研究院官方账号正式收录, ...
2026-04-22在数字化时代,客户每一次点击、浏览、下单、咨询等行为,都在传递其潜在需求与决策倾向——这些按时间顺序串联的行为轨迹,构成 ...
2026-04-22数据是数据分析、建模与业务决策的核心基石,而“数据清洗”作为数据预处理的核心环节,是打通数据从“原始杂乱”到“干净可用” ...
2026-04-22 很多数据分析师每天盯着GMV、转化率、DAU等数字看,但当被问到“什么是指标”“指标和维度有什么区别”“如何搭建一套完整的 ...
2026-04-22在数据分析与业务决策中,数据并非静止不变的数值,而是始终处于动态波动之中——股市收盘价的每日涨跌、企业月度销售额的起伏、 ...
2026-04-21在数据分析领域,当研究涉及多个自变量与多个因变量之间的复杂关联时,多变量一般线性分析(Multivariate General Linear Analys ...
2026-04-21很多数据分析师精通描述性统计,能熟练计算均值、中位数、标准差,但当被问到“用500个样本如何推断10万用户的真实满意度”“这 ...
2026-04-21在数据处理与分析的全流程中,日期数据是贯穿业务场景的核心维度之一——无论是业务报表统计、用户行为追踪,还是风控规则落地、 ...
2026-04-20在机器学习建模全流程中,特征工程是连接原始数据与模型效果的关键环节,而特征重要性分析则是特征工程的“灵魂”——它不仅能帮 ...
2026-04-20很多数据分析师沉迷于复杂的机器学习算法,却忽略了数据分析最基础也最核心的能力——描述性统计。事实上,80%的商业分析问题, ...
2026-04-20在数字化时代,数据已成为企业决策的核心驱动力,数据分析与数据挖掘作为解锁数据价值的关键手段,广泛应用于互联网、金融、医疗 ...
2026-04-17在数据处理、后端开发、报表生成与自动化脚本中,将 SQL 查询结果转换为字符串是一项高频且实用的操作。无论是拼接多行数据为逗 ...
2026-04-17面对一份上万行的销售明细表,要快速回答“哪个地区卖得最好”“哪款产品增长最快”“不同客户类型的购买力如何”——这些看似复 ...
2026-04-17数据分析师一天的工作,80% 的时间围绕表格结构数据展开。从一张销售明细表到一份完整的分析报告,表格结构数据贯穿始终。但你真 ...
2026-04-16在机器学习无监督学习领域,Kmeans聚类因其原理简洁、计算高效、可扩展性强的优势,成为数据聚类任务中的主流算法,广泛应用于用 ...
2026-04-16在机器学习建模实践中,特征工程是决定模型性能的核心环节之一。面对高维数据集,冗余特征、无关特征不仅会增加模型训练成本、延 ...
2026-04-16在数字化时代,用户是产品的核心资产,用户运营的本质的是通过科学的指标监测、分析与优化,实现“拉新、促活、留存、转化、复购 ...
2026-04-15在企业数字化转型、系统架构设计、数据治理与AI落地过程中,数据模型、本体模型、业务模型是三大核心基础模型,三者相互支撑、各 ...
2026-04-15数据分析师的一天,80%的时间花在表格数据上,但80%的坑也踩在表格数据上。 如果你分不清数值型和文本型的区别,不知道数据从哪 ...
2026-04-15在人工智能与机器学习落地过程中,模型质量直接决定了应用效果的优劣——无论是分类、回归、生成式模型,还是推荐、预测类模型, ...
2026-04-14