作者 | Dipanjan(DJ)Sarkar
来源 | Medium
编辑 | 代码医生团队
介绍
人工智能(AI)不再仅限于研究论文和学术界。业内不同领域的企业和组织正在构建由AI支持的大规模应用程序。这里要考虑的问题是,“我们是否相信AI模型做出的决策?”和“机器学习或深度学习模型如何做出决策?”。解释机器学习或深度学习模型一直是整个数据科学生命周期中经常被忽视的任务,因为数据科学家或机器学习工程师会更多地参与实际推动生产或建立和运行模型。
但是除非正在构建一个有趣的机器学习模型,否则准确性并不是唯一重要的!业务利益相关者和消费者经常会问到用于解决现实问题的任何机器学习模型的公平性,问责制和透明度等难题!
在本文中,将研究用于解释计算机视觉中使用的深度学习模型的概念,技术和工具,更具体地说 - 卷积神经网络(CNN)。将采用实践方法,使用Keras和TensorFlow 2.0实现深度学习模型,并利用开源工具来解释这些模型所做出的决策!简而言之本文的目的是找出 - 深度学习模型真正看到了什么?
卷积神经网络
用于计算机视觉问题的最流行的深度学习模型是卷积神经网络(CNN)!
CNN通常由多个卷积和池化层组成,这有助于深度学习模型从图像等可视数据中自动提取相关特征。由于这种多层架构,CNN学习了强大的特征层次结构,即空间,旋转和平移不变。
CNN模型中的关键操作如上图所示。任何图像都可以表示为像素值的张量。卷积层有助于从该图像中提取特征(形成特征图)。网络中的较浅层(更接近输入数据)学习非常通用的特征,如边缘,角落等。网络中更深的层(更靠近输出层)学习与输入图像有关的非常具体的特征。下图有助于总结任何CNN模型的关键方面。
由于只关心CNN模型如何感知图像,因此不会从头开始训练任何CNN模型。相反将在示例中利用迁移学习的功能和预先训练的CNN模型。
像VGG-16这样的预训练模型已经在具有大量不同图像类别的大型数据集(ImageNet)上进行了预训练。考虑到这一事实,该模型应该已经学习了强大的功能层次结构。因此该模型已经学习了属于1,000个不同类别的超过一百万个图像的特征的良好表示,可以作为适合于计算机视觉问题的新图像的良好特征提取器。
解读CNN模型 - 深度学习模型真正看到了什么?
这是有趣的部分,真的可以通过一个看似黑盒子的CNN模型来解除呈现的不透明度,并尝试理解幕后真正发生的事情以及模型在看到图像时真正看到了什么?有许多技术和工具可用于解释基于视觉的深度学习模型所做出的决策。本文中介绍的一些主要技术如下所示。
看看这些技术中的每一种,并解释一些使用Keras和TensorFlow构建的基于CNN的深度学习模型。
SHAP Gradient Explainer
这种技术试图结合来自Integrated Gradients,SHapley Additive exPlanations(SHAP)和SmoothGrad的众多想法。该技术试图使用预期梯度(集成梯度的扩展)来解释模型决策。这是一种功能归因方法,专为基于Shapley值扩展到无限玩家游戏的可微模型而设计。将在此处使用此框架来实现此技术。
https://github.com/slundberg/shap
集成梯度值与SHAP值略有不同,需要单个参考值进行集成。然而在SHAP Gradient Explainer中,预期梯度将积分重新表示为期望,并将该期望与来自背景数据集的采样参考值相结合。因此该技术使用整个数据集作为背景分布而不是单个参考值。尝试在一些示例图像上实现这一点。首先加载一些基本依赖项和模型可视化函数实用程序。
import kerasfrom keras.applications.vgg16 import VGG16from keras.applications.vgg16 import preprocess_input, decode_predictionsfrom matplotlib.colors import LinearSegmentedColormapimport numpy as npimport shapimport keras.backend as Kimport json shap.initjs() # utility function to visualize SHAP values in larger image formats# this modifies the `shap.image_plot(...)` functiondef visualize_model_decisions(shap_values, x, labels=None, figsize=(20, 30)): colors = [] for l in np.linspace(1, 0, 100): colors.append((30./255, 136./255, 229./255,l)) for l in np.linspace(0, 1, 100): colors.append((255./255, 13./255, 87./255,l)) red_transparent_blue = LinearSegmentedColormap.from_list("red_transparent_blue", colors) multi_output = True if type(shap_values) != list: multi_output = False shap_values = [shap_values] # make sure labels if labels is not None: assert labels.shape[0] == shap_values[0].shape[0], "Labels must have same row count as shap_values arrays!" if multi_output: assert labels.shape[1] == len(shap_values), "Labels must have a column for each output in shap_values!" else: assert len(labels.shape) == 1, "Labels must be a vector for single output shap_values." # plot our explanations fig_size = figsize fig