正在加载中...

展开本页目录
算法教程LIME-局部线性解释

LIME-局部线性解释

No.142 · 在线教程

LIME-局部线性解释 的真实核心主要位于:

LIME-局部线性解释

1. 方法概述

LIME-局部线性解释 的真实核心主要位于:

  • core/calculator.py
  • ui/lime_params_widget.py
  • ui/lime_results_widget.py
  • utils/lime_exporter.py

从真实代码看,这个模块并没有导入官方 lime 包,而是自己实现了一套 tabular LIME 风格流程,包括:

  1. 训练一个黑盒模型;
  2. 围绕待解释样本生成邻域扰动样本;
  3. 在预处理后的特征空间中计算距离与核权重;
  4. 用加权 Ridge 拟合局部线性代理模型;
  5. 输出 top-k 局部特征权重与 fidelity

设数据集为

$$ \mathcal{D}=\{(x_i,y_i)\}_{i=1}^{N} \tag{1} $$

其中 \(x_i\) 为表格特征,\(y_i\) 为目标变量。模块最终解释的是某个指定样本 \(x_0\) 在局部邻域内的模型行为。

2. 数据约束、任务识别与预处理

2.1 数据与目标列要求

代码首先要求:

  1. 数据非空;
  2. 至少 5 行;
  3. target_column 非空且存在;
  4. 目标列删去缺失后仍有数据;
  5. 目标列之外至少有 1 列特征;
  6. instance_index 在清洗后数据范围内。

当前界面默认把最后一列设为目标列候选的默认值。

2.2 auto 任务识别

task="auto",则代码并不是读取用户显式标签,而是按目标列启发式判断:

$$ \text{task}= \begin{cases} \text{classification}, & y \text{ 非数值}\\ \text{classification}, & y \text{ 为数值且唯一值较少、近似整数}\\ \text{regression}, & \text{otherwise} \end{cases} \tag{2} $$

其中“唯一值较少”的阈值在代码里是 <= 20

因此若目标列是整数编码且取值不多,auto 很容易被识别成分类任务。

2.3 分类目标的标签编码

分类任务时,代码对目标列执行 LabelEncoder

$$ y^{(\text{enc})}=\mathrm{LabelEncode}(y) \tag{3} $$

并要求至少有 2 个不同类别。

2.4 训练 / 测试划分

模型训练前先执行

$$ \mathcal{D}_{\text{train}}\cup\mathcal{D}_{\text{test}}=\mathcal{D} \tag{4} $$

其中:

  • test_size 来自界面;
  • 分类任务优先使用 stratify=y
  • 若分层切分失败,代码会自动回退为 stratify=None
  • 若回退后训练集只剩 1 个类别,则报错终止。

2.5 表格预处理

真实预处理不是 sklearn 的 ColumnTransformer,而是自写 _TabularPreprocessor

对数值列 \(x_j\),先用中位数填补缺失,再做 z-score 标准化:

$$ \tilde x_{ij}=\frac{x_{ij}^{(\text{fill})}-\mu_j}{\sigma_j} \tag{5} $$

其中:

  • 缺失值用训练集该列中位数填补;
  • \(\mu_j,\sigma_j\) 也都来自训练集;
  • 若 \(\sigma_j\le 0\) 或非法,则强制置为 1。

对类别列,代码先补上特殊类别:

  • __missing__
  • __unknown__

再用 pd.get_dummies() 做 one-hot 编码。于是送入模型与局部代理的最终特征向量可写成

$$ z_i=\Phi(x_i)\in\mathbb{R}^{p'} \tag{6} $$

这里 \(p'\) 是 one-hot 展开之后的维数。

3. 黑盒模型与真实实现口径

3.1 支持的黑盒模型

代码只支持内置模型族:

  1. 分类任务:
    • LogisticRegression
    • RandomForestClassifier
    • GradientBoostingClassifier
  2. 回归任务:
    • Ridge
    • RandomForestRegressor
    • GradientBoostingRegressor

3.2 auto 任务下模型名可能被静默回退

这一点很重要:当任务是 auto 时,UI 会同时展示 LogisticRegressionRidge。但底层 _create_model() 是按识别后的真实任务分支的,所以:

  1. 若最终识别为分类,而用户选了 Ridge,代码会回退到 RandomForestClassifier
  2. 若最终识别为回归,而用户选了 LogisticRegression,代码会回退到 RandomForestRegressor

因此论文说明里不能把“下拉框显示什么模型名”直接等同于“最终一定训练了什么模型”。

4. 邻域采样、核权重与局部代理

4.1 数值特征邻域采样

对待解释样本 \(x_0\),数值特征的邻域采样规则为:

$$ x_{ij}^{(\text{nbr})}=x_{0j}+\varepsilon_{ij}, \qquad \varepsilon_{ij}\sim\mathcal{N}(0,\sigma_j^2) \tag{7} $$

其中 \(\sigma_j\) 使用训练集该数值列的标准差。

若某列标准差为 0,则该列在所有邻域样本中保持常数不变。

同时第 1 个邻域样本会被强制设成原样本本身,因此邻域集合中一定包含 \(x_0\)。

4.2 类别特征邻域采样

类别特征不做高斯扰动,而是按训练集经验分布抽样:

$$ x_{ij}^{(\text{nbr})}\sim \hat p_j(c) \tag{8} $$

其中 \(\hat p_j(c)\) 是训练集中该类别出现的频率。同样,第 1 个邻域样本会保留原样本的类别取值。

4.3 黑盒输出

LIME 拟合的目标不是原始标签,而是黑盒模型在邻域中的输出。记黑盒输出为 \(f(x)\),代码真实口径为

$$ f(x)= \begin{cases} \hat y(x), & \text{回归}\\ \Pr(y=c^\star\mid x), & \text{分类} \end{cases} \tag{9} $$

其中分类任务中的 \(c^\star\) 为:

$$ c^\star= \begin{cases} \text{用户指定类别}, & \text{若 } \texttt{class\_to\_explain} \text{ 有效}\\ \arg\max_c \Pr(y=c\mid x_0), & \text{否则} \end{cases} \tag{10} $$

所以分类解释针对的是“某一类的预测概率”,而不是最终硬分类标签。

4.4 核宽度与核权重

在预处理后的空间中,代码使用欧氏距离:

$$ d_i=\|z_i-z_0\|_2 \tag{11} $$

若用户没有显式给出 kernel_width,则默认值为

$$ \sigma_{\text{kernel}}=0.75\sqrt{p'} \tag{12} $$

邻域权重定义为

$$ w_i=\exp\!\left(-\frac{d_i^2}{\sigma_{\text{kernel}}^2}\right) \tag{13} $$

这里分母中没有再额外出现 2,因此应按当前代码如实描述。

4.5 局部代理模型

代理模型不是 sklearn 自带的 Ridge(sample_weight=...),而是自写 _WeightedRidge。其目标函数为

$$ \min_{\beta_0,\beta}\sum_{i=1}^{m}w_i\Big(f(z_i)-(\beta_0+z_i^\top\beta)\Big)^2+\alpha\|\beta\|_2^2 \tag{14} $$

其中 \(\alpha=1.0\) 固定写死在代码里。

最终各特征的局部解释权重就是 \(\beta\) 的各分量。导出时会同时保留:

  1. weights_all
  2. 按 \(|\beta_j|\) 排序后取前 num_features 个的 local_weights

4.6 Fidelity

局部拟合优度 fidelity 使用加权 \(R^2\):

$$ R_w^2=1-\frac{\sum_i w_i\big(f(z_i)-\hat f(z_i)\big)^2}{\sum_i w_i\big(f(z_i)-\bar f_w\big)^2} \tag{15} $$

其中

$$ \bar f_w=\frac{\sum_i w_i f(z_i)}{\sum_i w_i} \tag{16} $$

这也是结果页显示的 fidelity(weighted R2)

5. 输出结果与评价指标

5.1 测试集模型指标

回归任务导出:

$$ R^2=1-\frac{\sum_i(y_i-\hat y_i)^2}{\sum_i(y_i-\bar y)^2} \tag{17} $$

$$ \mathrm{MAE}=\frac{1}{n}\sum_i |y_i-\hat y_i| \tag{18} $$

$$ \mathrm{RMSE}=\sqrt{\frac{1}{n}\sum_i(y_i-\hat y_i)^2} \tag{19} $$

分类任务导出:

$$ \mathrm{Accuracy}=\frac{1}{n}\sum_i \mathbf{1}(\hat y_i=y_i) \tag{20} $$

并额外输出:

  • precision_macro
  • recall_macro
  • f1_macro

只有在 predict_proba 存在且是二分类时,代码才会额外计算 roc_auc

5.2 结果字典与 Excel 工作表

这套实现的结果结构与很多别的模块不同:

  • raw_data 不是完整原始表,而是 {file_path, data_shape, data_head, target_column}
  • processed_data 也不是完整处理后矩阵,而是一些元信息

export_lime_results() 实际导出:

  • Parameters
  • Data_Head
  • Instance
  • Model_Metrics
  • Local_Weights
  • Weights_All
  • Processed_Info
  • Chart_Index
  • Charts

因此:

  1. 导出的 Data_Head 只有前 10 行;
  2. 不会导出完整训练集 / 测试集矩阵;
  3. 也不会导出全部邻域扰动样本。

5.3 图表与自动导出

当前只生成一张图:

  • charts/local_weights_bar.png

即 top-k 局部权重水平条形图。

结果页在计算完成后会自动新建一个时间戳目录并导出 LIME_results.xlsx;当前界面上的“导出 Excel”按钮虽然定义了,但在现有流程中始终保持隐藏状态,实际主路径仍是自动导出。

5.4 输出路径与复现脚本

默认结果目录形如:

  • results/LIME-局部线性解释分析结果_时间戳/

其中固定导出文件名为:

  • LIME_results.xlsx

结果页还会自动写出:

  • repro_lime.py

并把原输入文件复制到 repro_inputs/ 目录下,以便后续复跑。

6. 实现说明与注意事项

根据当前真实代码,论文说明中应明确以下边界:

  1. 这不是官方 lime 包的直接封装,而是基于 LIME 思想的自写 tabular 近似实现。
  2. 数值扰动使用训练集标准差高斯采样,类别扰动使用经验分布采样。
  3. 分类任务解释的是指定类别或预测类别的概率,不是最终离散标签。
  4. auto 任务识别是启发式规则,不是严格的任务元信息读取。
  5. auto 模式下,若所选模型名与检测出的任务不匹配,底层可能静默回退为随机森林。
  6. 导出的 Data_HeadProcessed_Info 都不是完整原始数据或完整处理后矩阵,这一点在论文说明里必须写清。

7. 论文写作建议

7.1 方法描述模板

论文正文可写为:本文采用基于 LIME 思想的局部加权线性近似解释方法,对黑盒模型在目标样本邻域内的预测机制进行分析。需要说明的是,当前项目使用的是自定义的 tabular 近似实现,而非官方 lime 包,因此扰动采样、核权重与代理回归形式均遵循本项目代码中的实现口径。

7.2 结果解释模板

结果部分可写为:对目标样本而言,局部解释权重的正负号分别表示该特征在局部邻域内对预测输出的促进作用或抑制作用,权重绝对值越大,说明该特征在当前样本附近越重要。同时,fidelity(weighted R2) 可作为局部代理模型拟合可信度的量化指标,数值越高表示局部线性近似越能复现原模型行为。

7.3 图表题注模板

  • 表题可写为:目标样本的 LIME 局部解释权重排序结果。
  • 图注可写为:目标样本局部特征贡献条形图。
  • 表题可写为:局部代理模型拟合优度与原始特征取值说明表。

8. 单篇终审补充

8.1 图题与表题对齐建议

当前 LIME 模块的真实工作簿包含:

  • Parameters
  • Data_Head
  • Instance
  • Model_Metrics
  • Local_Weights
  • Weights_All
  • Processed_Info
  • Chart_Index
  • Charts

这套输出里最容易写错的是把它当成“完整样本解释结果表”。当前 Data_Head 只有前 10 行预览,Processed_Info 也只是元信息,不是完整处理后矩阵。因此论文里真正适合作为核心解释结果引用的是 InstanceLocal_WeightsModel_Metrics

当前真实图只有一张:

  • charts/local_weights_bar.png

因此图题建议写成“目标样本局部解释权重条形图”。不要在论文里声称程序还自动导出了邻域扰动散点图、代理模型残差图等图形,因为当前实现没有这些图。

8.2 终审说明

这篇文档最需要避免的误写,是把 LIME 解释结果理解成全局特征重要性。当前 Local_WeightsWeights_All 都是围绕“目标样本邻域”构建的局部线性代理结果,解释范围只对该目标样本附近有效。

另外,当前 repro 脚本已经采用 repro_inputs/... 相对路径,结果目录中可见如 repro_inputs/window1_xai_local_input.csv 的输入引用。这篇可按新框架写可复现性。

8.3 全量强化补充

本篇终审补充绑定的真实算法目录为 具体的算法3/可解释与AutoML/LIME-局部线性解释,本次采用的代表性结果目录为 具体的算法3/可解释与AutoML/LIME-局部线性解释/results/LIME-局部线性解释分析结果_20260329_170636

主结果工作簿为 具体的算法3/可解释与AutoML/LIME-局部线性解释/results/LIME-局部线性解释分析结果_20260329_170636/LIME_results.xlsx,实测工作表为:

  • Parameters
  • Data_Head
  • Instance
  • Model_Metrics
  • Local_Weights
  • Weights_All
  • Processed_Info
  • Chart_Index
  • Charts

这里应继续强调 Data_Head 只是数据预览,Processed_Info 只是处理元信息;真正的局部解释证据集中在 InstanceLocal_WeightsWeights_AllModel_Metrics。不要把这一份结果写成“全局解释报告”。

当前目录真实图文件只有一张:

  • 具体的算法3/可解释与AutoML/LIME-局部线性解释/results/LIME-局部线性解释分析结果_20260329_170636/charts/local_weights_bar.png

因此这一轮真实图证据只能写成“目标样本局部权重条形图”,不能附会成邻域散点图、代理残差图或全局重要度图。

复现实物方面,该目录实际包含:

  • 具体的算法3/可解释与AutoML/LIME-局部线性解释/results/LIME-局部线性解释分析结果_20260329_170636/repro_lime.py
  • 具体的算法3/可解释与AutoML/LIME-局部线性解释/results/LIME-局部线性解释分析结果_20260329_170636/repro_inputs/window1_xai_local_input.csv

脚本里明确写成 INPUT_FILE = 'repro_inputs/window1_xai_local_input.csv',输出目录按当前结果目录解析。因此该轮复现链路已经是目录内相对路径口径。

12. 软件实现核查补充(2026-07)

  • 当前主结果目录应写作 具体的算法3/可解释与AutoML/LIME-局部线性解释/results/LIME-局部线性解释分析结果_20260329_170636
  • 正文应围绕 InstanceLocal_WeightsWeights_AllProcessed_InfoChart_IndexCharts 来写。
  • 图证应对应 local_weights_bar.png,并把单样本局部解释和全局解释区分开。
  • 复现脚本应按 repro_lime.py + repro_inputs/window1_xai_local_input.csv 的口径说明。