自学机器学习也有一段时间了,我发现很多资料中都提到模型解释分析,说它是是机器学习中的一个重要领域。一开始我并不在意,我们已经能够通过评估指标了解模型的性能了(在分类任务中,准确率高说明模型预测正确的情况多,回归任务中,R²高说明模型解释了大部分的变异),何必还要费劲地去解释模型?
可随着学习的深入,我想更深入的了解模型到底是怎么做出预测的?为什么模型在某些情况下表现得很好,却在其他情况下却表现很差?在航空维修领域,我们通过机器学习来预测故障,不光想知道这个故障发生的概率是多少,还想知道哪些参数对这个概率影响最大,这样我们才能有针对性地换件。假如模型是个黑箱,即使它预测得很准,我可能还是会心存疑虑。光靠评估指标,并不能解决我所有的疑问。于是我又回过头来再把这个概念学习一遍。以下是我整理的一些学习笔记:
模型解释分析旨在解释和理解模型的行为及其输出的原因,为我们提供了模型透明性,可以帮助我们理解以下问题:
模型的决策依据:模型是如何得出某个预测结果的?
有些模型本身比较容易解释,比如线性回归,每个特征的系数就能说明这个特征对预测结果的影响。可是像决策树或者随机森林,虽然也能看一些特征的重要性,但感觉还是没那么直观。随着机器学习模型(尤其是深度学习模型)越来越复杂,模型变得越来越难以理解。可如果不进行解释分析,就无法真正理解模型的工作原理,也无法确保模型在未知数据上的泛化能力。
模型解释的方法有很多,既有针对单个预测的局部解释,也有针对整个模型的全局解释。比如:用SHAP值可以衡量每个特征对预测结果的贡献;还有Permutation Importance通过打乱特征值来看模型性能下降多少,来判断这个特征的重要性;还有一些基于梯度的方法,用来解释神经网络等复杂模型的特征影响,等等。下面我们先通过一个简单的例子来学习一下SHAP作为入门:
假设一个团队正在完成一个项目,团队由 3 名成员组成:A、B、C。完成项目可以获得 100 万的总收益,但每个人的贡献不一样:
那么问题来了:如何公平地分配这 100 万呢?
为了公平地分配100万元的收益,我们可以使用Shapley值来衡量每个成员的贡献。Shapley值的核心思想是通过考虑所有可能的合作顺序,计算每个玩家加入时所带来的边际贡献,然后取所有排列的平均值。计算公式为:
其中:S为不包含玩家
这里简单解释一下什么是边际贡献?即某些成员已经合作,那么新加入的成员带来的额外收益就是这个成员的边际贡献。例如:团队已经有 A 和 B,他们能赚 90 万。如果此时 C 加入,收益变为 100 万,则 C 的边际贡献为 10 万。
由于Shapley值认为所有的成员加入团队的顺序都是同等可能的,为了确保所有顺序的公平性,所以需要计算每个成员在所有可能顺序下的边际贡献,然后再取平均值。
综上,当N=3,成员只有A时,S可以是空集、{B}、{C}、{B,C}。将数值代入公式计算成员A的Shapley值为:
= [ (1*2 / 6) (40 - 0) ] + [ (1*1 / 6) (90 - 30) ] + [ (1*1 / 6) (70 - 10) ] + [ (2*1 / 6) (100 - 50) ]
= [ (2/6)*40 ] + [ (1/6)*60 ] + [ (1/6)*60 ] + [ (2/6)*50 ]
= (80/6) + (60/6) + (60/6) + (100/6) = (80+60+60+100)/6 = 300/6 = 50
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
import shap
import pandas as pd
import numpy as np
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
X = data
y = target
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练模型
model = RandomForestRegressor(random_state=42)
model.fit(X_train, y_train)
# 创建 SHAP 解释器
explainer = shap.TreeExplainer(model)
# 计算 SHAP 值
shap_values = explainer.shap_values(X_test)
DeepExplainer
或 Gradi
entExplainer;对于普通线性模型或类模型,则使用LinearExplainer
)。# 定义特征名称
feature_names = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE',
'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT']
shap.summary_plot
和 shap.dependence_plot
进行可视化:# 全局特征重要性图
shap.summary_plot(shap_values, X_test, feature_names=feature_names)
# 依赖图
shap.dependence_plot("LSTAT", shap_values, X_test, feature_names=feature_names)
微信号
minhangjiwuluntan