Py学习  »  Python

机器学习可解释性Python库,令人讨厌

Ai学习的老章 • 8 月前 • 158 次点击  


大家好,我是Ai学习的老章

偶尔推点老本行相关文章——机器学习系列

作者 Tobias Pitters[1]。Tobias 是 shap 项目维护者,现任可解释人工智能初创公司 CloudExplain[2] 工程师。

机器学习面试准备路线图(2025版)机器学习模型的可解释性算法汇总!

SHAP 是最广泛使用的模型可解释性库之一,这有其充分理由——它让解释复杂模型变得直观甚至美观。我自 2023 年 10 月开始参与 SHAP 开发,并于 2024 年 1 月成为维护者。在此期间,我贡献了许多改进和修复,至今仍热爱其底层数学的强大、易于集成的特性以及惊艳的可视化效果(我认为这正是 SHAP 最初如此受欢迎的重要原因)。

但作为维护者也意味着要直面系统崩溃、运行迟缓或用户遭遇挫折的环节。经过一年半时间,我整理出一份亟待改进的痛点清单——既为了项目健康发展,也为了所有使用者。

1. 解释过程可能变得缓慢

在笔记本环境中处理数千条观测数据时一切正常,你可以自由选择最适合的解释器。但一旦涉及更多特征和更大样本量,速度就会急剧下降。对于具有数十万条观测数据的大型模型,解释过程可能耗时数小时。

这种缓慢部分源于 SHAP 值计算方式的固有特性,但更多是由实现选择导致的,比如有限的并行化和大量纯 Python 循环。最近我们合并了一个将部分循环转移到 Cython 的修复方案,使 KernelExplainer 的速度提升了约 5%(参见这个 PR[3] 或这个 PR[4])。我认为仍有很大改进空间,只是我们尚未将其列为优先事项。

2. DeepExplainer 的困扰

DeepExplainer 原本是解释 TensorFlow 或 PyTorch 模型的自然选择,它采用 DeepLIFT 风格逐层反向传播重要性值。遗憾的是,从 TensorFlow 2.4 版本开始,这一过程变得异常困难。许多神经网络层在内部由乘法、激活函数和加法等基础运算组合而成。过去我们只需重写这些底层运算的反向传播逻辑,就能自动适用于 LSTM 等高层结构,无需额外工作即可为各类层计算 SHAP 值。但随着 TensorFlow 转向即时执行模式并隐藏这些内部实现,这条捷径已然失效。如今我们必须为每个需要支持的层单独实现定制逻辑。

这导致 LayerNorm 等标准化层以及 LSTM、Attention 层等复杂结构不再受 DeepExplainer 支持。

3. TreeExplainer——速度快但难以修改

树模型在表格数据预测任务中仍被广泛使用且表现优异。幸运的是,SHAP 值可以通过解析方法从树模型中计算得出,而我们的实现由于采用 C 语言而具有极高的运行效率。但遗憾的是,目前团队缺乏精通 C 语言的维护者,且这部分代码年代久远难以修改。我们还遇到过内存错误——这种因内存处理不当引发的底层问题,所幸已被社区修复。我怀疑仍存在其他潜在问题,因此希望能通过用 Rust 重写代码或寻找专人接管这部分代码库,使其进入更易维护的状态。在与可解释 AI 专家讨论 Rust 实施方案时,我们一致认为这是可行的改进方向:既能借助 Rust 的内存安全设计避免问题,又能保持与当前版本相近的性能表现。

另外,您知道吗?我们其实有 TreeExplainer 的 GPU 版本[5] 。遗憾的是,目前仅支持通过源码安装(克隆代码库后运行 pip install -e .)且需配备 GPU 设备。我理想中的方案是提供额外安装标记(pip install shap[gpu])或预编译的 GPU 轮包(pip install shap-gpu)。虽然需要升级构建基础设施,但其他软件包的成功案例证明这完全可行。

4. 因上游包导致测试失败和代码损坏

SHAP 支持众多机器学习框架——包括 scikit-learn、TensorFlow、PyTorch、LightGBM、XGBoost、CatBoost、transformers 库中的部分文本模型,甚至 NGBoost 模型。这意味着我们必须与所有框架保持兼容,并通过测试确保这一点。但问题在于,支持如此多软件包的同时还要兼顾多个 Python 版本,导致我们的 CI 流水线(自动化测试与集成工作流)经常失败。

每当这些库中的任何内容发生变化,都可能导致我们的代码失效。粗略估计,我花费在 SHAP 上约 30%的时间都用于修复上游变更。这些问题往往晦涩难懂且难以调试。例如,我们曾遇到测试套件在 Python 3.10 上失败,但在 Python 3.11 及更高版本上却运行正常。失败的测试与 transformers 代码相关。经过数小时调试后发现,我们必须将流水线专门升级到 Python 3.10.12 版本,因为 transformers 开始使用了一个仅在 Python 3.10.12 中向后移植的警告过滤参数(3.10.0-3.10.11 版本均未提供)。这种在补丁版本中添加功能的做法并不常见,让我们措手不及(参见 Python 发布说明[6] )。
针对这种情况的解决方案并不多,我们可以改进日志记录、添加类型提示,但仍会有大量问题无法覆盖。

5. 绘图问题

我喜爱 SHAP 的图表功能,相信这是它成功的重要因素之一。其多样性令人印象深刻,几乎无可挑剔——除了那些缺失但难以添加到遗留绘图代码库中的功能。说到"遗留",我确实是这个意思——有些绘图函数在内部就是如此命名的,比如这里[7] ,或这里[8] ,还有那个 waterfall_legacy 函数[9] (这个函数现在已不建议使用)。

若能清理这些代码并简化通常超过 300 行的单个绘图函数就太好了。此外,部分图表是用 JavaScript 生成的,这本身没问题,但这些代码相当陈旧且难以测试。我也希望能重写这部分代码  但这需要更全面的绘图测试。我们确实已有相关测试,但图表仍是代码库中测试覆盖最薄弱的部分。如果有完善的测试,我们不仅能修复一些错误,还能扩展绘图功能,让用户获得更多控制权,比如调整调色板、间距和高度等参数。

6. 其他问题

一些较小的杂项问题或缺失功能:

  • JAX 是一个用于构建深度学习网络的库,可与 PyTorch 或 TensorFlow 相媲美,只是采用了更函数式的编程方法。它正在快速发展,我很想在 Deep-和 GradientExplainer 中支持它。不过在开发大型新功能之前,我们还有许多更紧迫的问题需要解决,这些新功能本身就会带来各种问题。
  • 作为维护者,我最讨厌 SHAP 的 6 件事之一就是缺乏完善的类型标注——这个功能在其他库中让我受益匪浅。对我们而言这是双重困境:一方面自然是时间不足难以实现,另一方面我们许多函数具有高度多态性,允许接收多种不同类型的输入,这也使得类型提示对用户而言变得难以阅读。
  • 我真的很希望实现夜间构建,并支持科学 Python 社区[10]提出的其他功能,比如按需懒加载代码。

乐观的前景

我不想显得悲观或让你觉得这是听天由命的放弃,我相信并致力于让这个软件包不断完善,许多贡献者也同样如此。诚实地面对并承认当前的不足是第一步,但看到 SHAP 带来的价值,就让人觉得值得持续投入。当出现一个 PR 解决了我们长期存在的问题,甚至是我们未曾意识到的问题,或是为 SHAP 构建了全新功能时,我都感到欣喜。看到这如何将人们凝聚在一起,推动事物向前发展,真是太好了。我也热爱这个软件包帮助解释了黑盒机器学习算法,将可解释 AI 带入主流,推动更广泛的机器学习应用,并支持我们更深入地理解复杂算法的工作原理。它让我深入探索可解释 AI,并接触了以前从未涉足的编码领域。

原文:# 作为维护者,我讨厌 SHAP 的 6 个方面 https://mindfulmodeler.substack.com/p/6-things-i-hate-about-shap-as-a-maintainer?utm_source=post-email-title&publication_id=1078760&post_id=173436591&utm_campaign=email-post-title&isFreemail=true&r=u9e1n&triedRedirect=true&utm_medium=email


参考资料
[1] 

Tobias Pitters: https://github.com/CloseChoice

[2] 

CloudExplain: https://www.cloudexplain.eu/

[3] 

这个 PR: https://github.com/shap/shap/pull/3944

[4] 

这个 PR:  https://github.com/shap/shap/pull/3983

[5] 

TreeExplainer 的 GPU 版本: https://github.com/shap/shap/blob/master/shap/explainers/_gpu_tree.py

[6] 

Python 发布说明: https://www.python.org/downloads/release/python-31012/

[7] 

这里: https://github.com/shap/shap/blob/8bcd29f3f49d1315411892417ab59cb7c1227151/shap/init.py#L49-L50

[8] 

这里:  https://github.com/shap/shap/blob/8bcd29f3f49d1315411892417ab59cb7c1227151/shap/init.py#L61

[9] 

waterfall_legacy 函数: https://github.com/shap/shap/blob/8bcd29f3f49d1315411892417ab59cb7c1227151/shap/plots/_waterfall.py#L376

[10] 

科学 Python 社区: https://scientific-python.org/specs/

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/187431