分歧问题:深入探讨人工智能的复杂性可解释性·进阶篇
🧩

分歧问题:深入探讨人工智能的复杂性可解释性·进阶篇

(2/3)·当两种解释技术对你同一笔黄金EA模型给出相反结论,信任该交给谁?

含代码示例实战向 第 2/3 篇
很多交易者把 SHAP 或 LIME 跑出来的特征权重当成圣旨,却没意识到同一模型换种解释法结论就打架。在 MT5 里训好的行情模型,盲目信单一解释可能让你在镑美波动里亏掉手续费都找不回原因。

◍ 用置换重要性与LIME拆解模型权重

训练完线性回归模型后,直接看系数容易误判,因为多重共线性会让权重符号失真。这里用 PermutationImportance 做置换检验:它打乱单个特征后看验证集均方误差的抬升幅度,抬升越多说明该特征越不可替代。 eli5 的 show_weights 会把每个预测因子的贡献排序列出,配合 sklearn 的 mean_squared_error 可量化打乱前后的误差差。若某因子置换后 MSE 从 0.012 跳到 0.031,那么它的边际信息量就明显高于置换后 MSE 仅到 0.014 的因子。 LIME 则从单个样本切入:用局部 surrogate 模型逼近原模型在该点附近的决策面。explainer.explain_instance 针对 scaled_data 第 test 行给出局部贡献表,能在 notebook 里直接看哪些特征把该笔预测往上推、哪些往下拉。外汇与贵金属波动受宏观脉冲影响,这类局部解释对识别过拟合样本有帮助,但高频行情下解释稳定性偏低,仅作概率参考。

MQL5 / C++
epochs=class="num">100,
verbose=class="num">0,
callbacks=[early_stopping]
)
class="kw">import eli5
from sklearn.metrics class="kw">import mean_squared_error
from eli5.sklearn class="kw">import PermutationImportance
from eli5.permutation_importance class="kw">import get_score_importances
permutation = PermutationImportance(lm).fit(x_test,y_test)
eli5.show_weights(permutation,feature_names = predictors)
class="kw">import lime
from lime class="kw">import lime_tabular
explainer = lime_tabular.LimeTabularExplainer(training_data = np.array(x_train),mode=&class="macro">#x27;regression&class="macro">#x27;,feature_names=predictors)
exp = explainer.explain_instance(data_row=scaled_data.iloc[test],predict_fn=lm.predict)
exp.show_in_notebook(show_table=True)

「白盒与黑盒解释器的偏差陷阱」

知道模型底层结构时,白盒解释器能更忠实地还原行为,且计算效率高于黑盒。黑盒解释器(如 LIME、SHAP 的 Explainer 通用接口)与模型无关,但对模型形式做了简化假设,一旦假设被违反,解释就不可靠。 SHAP 黑盒图里每一行宽度由异常值决定,不反映特征重要性;要看特征在图中的垂直位置,越靠上越重要。点若蓝粉混在一起,往往暗示该特征与别的特征存在交互效应。 SHAP 算法假定特征独立,忽略数据集中交互作用,这是硬伤——违反该假设解释就会失真。图 6 黑盒与图 7 TreeExplainer 白盒对比中,最重要的 4 个特征若出现分歧,就是黑盒偏差的实际证据。 下面代码先跑黑盒 Explainer,再跑专为树模型设计的 TreeExplainer,两者 beeswarm 图可直接在 MT5 外接 Python 环境复现验证。外汇与贵金属模型解释同样受此偏差影响,高风险场景需优先用白盒。

MQL5 / C++
class="kw">import shap
explainer = shap.Explainer(xgb.predict,scaled_data.loc[test:test_end,predictors])
shap_values = explainer(scaled_data.loc[test:test_end,predictors])
shap.plots.beeswarm(shap_values)
tree_explainer = shap.TreeExplainer(xgb)
tree_shap_values = explainer(x_test)
shap.plots.beeswarm(tree_shap_values)

特征重要性解释里的分歧到底长什么样

做模型解释时,最容易被忽略的坑是:不同解释器给同一模型算出的特征重要性并不一致。先把真正的分歧拆开看——特征排列顺序不同、前 n 个重要特征集合不一致、系数符号翻转、以及你关心的两个特征相对位置对调,这四类都算硬分歧,尤其当系数从正变负,基本说明模型内部贡献方向有争议,得回头查。 但不是所有数字不一样都叫分歧。不同算法算出的绝对重要性值本来就没法直接比,只要叙事结论一致(比如都说「认知度评级」最关键),就不算冲突。模型越复杂,估算越难,解释值有波动是预期内的,不是警报。 分歧的来源很具体。全局和局部解释不能直接放一起比;黑盒解释器为了通用性做了简化假设,假设不成立就会偏,和白盒对不上。更隐蔽的是特征输入顺序:SHAP 对顺序敏感,互信息不敏感。两个相同的 XGBRegressor,只改特征送入顺序,SHAP 下「身高」重要性突然跳升,前三虽不变但大多数特征上下乱跳。 eli5 的排列重要性实现也对顺序敏感,sklearn 的同技术却不敏感。实测两个相同 XGBRegressor 仅改输入顺序,eli5 下 kickPower_rating 系数从正变负,最重要特征都换了。若你用 bootstrap 或子集重采样,得知道这现象正常。 XGB 自带特征重要性评估函数可当参照。实测中认知度评级才是真最重要,但一小撮黑盒解释完全忽略前三,nearsPro 多次登顶其实不重要——别迷信「多次靠前就真重要」。黑盒和白盒在此模型上互相矛盾,但俩都错,纠结谁更忠实是浪费时间。玻璃盒树模型能认出真最重要特征,但非树模型未必能用;且它也曾误判 yearsPro 有信息性,说明解释器也可能一起认同错误信息。外汇与贵金属模型若接这类解释流程,高风险在于把估计当真相。

◍ 用 MT5 脚本抓数据再拿三个解释器互殴

把行情和指标导出来是第一步。下面这段 MQL5 脚本在 OnStart 里一口气挂了 MA(20)、RSI(60)、CCI(10)、AO、BBANDS(120) 和 ATR(14),请求 10000 根 K 线外加 50 根缓冲,把读数写进数组等后面塞 CSV。 拿到 CSV 后,用 InterpretML 做训练测试分割,分别跑了线性模型、XGB 和深度神经网络。深神经网用 alibi 的 ALE 解释,动量振荡指标(AO)的 ALE 曲线随数值增大斜率向下,说明它倾向提示价格下跌概率上升;而 RSI 的 ALE 接近水平线,基本没信息量。 换 SHAP 蜂群图,AO 又被标成最重要特征,但行宽受异常值影响不能当真。互信息排名里前四位是 BBANDS 120 的 MID/HIGH/LOW 和 MA 20(值 1.739 / 1.731 / 1.716 / 1.526),跟 SHAP 完全对不上。最后莫里斯敏感性分析说 MA 最重要、AO 次之,在这个样本里反而最接近真实重要性。 多个解释器同台是双刃剑:能防单一方法的偏见,但也必然制造分歧。外汇和贵金属市场高波动、高杠杆,这种特征重要性的矛盾没法靠拍脑袋消解,你对自己选哪个解释器的信心,本质上取决于数据集和模型,而不是某张图好看。

MQL5 / C++
class=class="str">"cmt">//---Our handlers for our indicators
class="type">int ma_handle;
class="type">int rsi_handle;
class="type">int cci_handle;
class="type">int ao_handle;
class="type">int bbands_handle;
class="type">int atr_handle;
class=class="str">"cmt">//---Data structures to store the readings from our indicators
class="type">class="kw">double ma_reading[];
class="type">class="kw">double rsi_reading[];
class="type">class="kw">double cci_reading[];
class="type">class="kw">double ao_reading[];
class="type">class="kw">double bb_high_reading[];
class="type">class="kw">double bb_low_reading[];
class="type">class="kw">double bb_mid_reading[];
class="type">class="kw">double atr_reading[];
class=class="str">"cmt">//---File name
class="type">class="kw">string file_name = "The Dissagrement Problem Data.csv";
class=class="str">"cmt">//---Amount of data requested
class="type">int size = class="num">10000;
class="type">int size_fetch = size + class="num">50;
class="type">void OnStart()
  {
   class=class="str">"cmt">//---Setup our technical indicators
   ma_handle = iMA(_Symbol,PERIOD_CURRENT,class="num">20,class="num">0,MODE_EMA,PRICE_CLOSE);
   rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,class="num">60,PRICE_CLOSE);
   cci_handle = iCCI(_Symbol,PERIOD_CURRENT,class="num">10,PRICE_CLOSE);
   ao_handle = iAO(_Symbol,PERIOD_CURRENT);
   bbands_handle = iBands(_Symbol,PERIOD_CURRENT,class="num">120,class="num">0,class="num">0.2,PRICE_CLOSE);
   atr_handle = iATR(_Symbol,PERIOD_CURRENT,class="num">14);
class=class="str">"cmt">//---Set the values as series
   CopyBuffer(ma_handle,class="num">0,class="num">0,size_fetch,ma_reading);
   ArraySetAsSeries(ma_reading,true);
   CopyBuffer(rsi_handle,class="num">0,class="num">0,size_fetch,rsi_reading);
   ArraySetAsSeries(rsi_reading,true);
   CopyBuffer(cci_handle,class="num">0,class="num">0,size_fetch,cci_reading);
   ArraySetAsSeries(cci_reading,true);

「把多指标快照落盘成 CSV」

抓完指标缓冲后,下一步是把 AO、布林带(120 周期)和 ATR(14 周期)一并写进文件,方便拿去给小布做离线特征工程。 先挨个 CopyBuffer 把句柄数据搬进数组,再用 ArraySetAsSeries(...,true) 把序列倒序,保证 ao_reading[0] 对应最新一根 K 线;布林带的三条线分别用缓冲区索引 0/1/2 取中轨、上轨、下轨。

FileOpen 用 FILE_WRITEFILE_ANSIFILE_CSV 加逗号分隔打开目标文件,循环从 i=-1 到 size:首轮写表头,含 Time/Open/High/Low/Close 及 MA20、RSI60、CCI10、AO、BBANDS120 三轨、ATR14 共 14 列;之后每根 K 用 iTime/iOpen 等函数取 OHLC 再拼指标数组写行。

外汇与贵金属波动剧烈、杠杆高,这套导出仅用于历史复盘与概率统计,实盘信号仍要结合当下风控。

MQL5 / C++
   CopyBuffer(ao_handle,class="num">0,class="num">0,size_fetch,ao_reading);
   ArraySetAsSeries(ao_reading,true);
   CopyBuffer(bbands_handle,class="num">0,class="num">0,size_fetch,bb_mid_reading);
   ArraySetAsSeries(bb_mid_reading,true);
   CopyBuffer(bbands_handle,class="num">1,class="num">0,size_fetch,bb_high_reading);
   ArraySetAsSeries(bb_high_reading,true);
   CopyBuffer(bbands_handle,class="num">2,class="num">0,size_fetch,bb_low_reading);
   ArraySetAsSeries(bb_low_reading,true);
   CopyBuffer(atr_handle,class="num">0,class="num">0,size_fetch,atr_reading);
   ArraySetAsSeries(atr_reading,true);
   class=class="str">"cmt">//---Write to file
    class="type">int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");
for(class="type">int i=-class="num">1;i<=size;i++){
      if(i == -class="num">1){
         FileWrite(file_handle,"Time","Open","High","Low","Close","MA class="num">20","RSI class="num">60","CCI class="num">10","AO","BBANDS class="num">120 MID","BBANDS class="num">120 HIGH","BBANDS class="num">120 LOW","ATR class="num">14");
      }
      
      else{
         FileWrite(file_handle,iTime(_Symbol,PERIOD_CURRENT,i),
                              iOpen(_Symbol,PERIOD_CURRENT,i),
                              iHigh(_Symbol,PERIOD_CURRENT,i),
                              iLow(_Symbol,PERIOD_CURRENT,i),
                              iClose(_Symbol,PERIOD_CURRENT,i),
                              ma_reading[i],
                              rsi_reading[i],
                              cci_reading[i],
                              ao_reading[i],
                              bb_mid_reading[i],
                              bb_high_reading[i],
                              bb_low_reading[i]
把解释对账交给小布盯盘
这些多解释器分歧诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同 XAI 方法的对比视图,你只需判断哪条逻辑贴合盘面。

常见问题

全局解释描述模型在整个历史行情上的平均行为,局部解释只针对某一根 K 线或某笔信号附近的特征贡献,后者对剥头皮策略更实用。
不一定。无关法通用但可能平滑掉树模型分裂细节,特定模型法更贴结构却容易过拟合到训练分布,外汇高风险下建议交叉验证。
可以,AIGC 模块会对接终端导出的模型,并行跑多种解释并高亮贡献方向矛盾的特征,省去你手写对账脚本。
因为解释技术底层假设不同,且市场数据非稳态,学术界倾向按场景自拟缓解框架,本文案例即给出一种落地思路。
警惕未来函数与跨品种同期平移,回测窗和解释窗必须严格隔离,否则解释出的特征重要性只是巧合。