关于在Python HTML中显示SHAP力图的教程
使用Flask在web上渲染SHAP力图
这是这是本教程的源代码,这样您就可以跟着学习,并且可以直接运行app.py
看看结果。
动机
SHAP图对于模型的解释性非常有用(参见在这里关于它们的精彩演讲)。
作为最近使用SHAP显示英雄联盟数据逻辑回归的项目的一部分(你可以看到项目web应用程序)在这里以及下面的截图),我努力寻找一种在页面加载时显示SHAP图的方法,而不必先保存图像。我非常习惯在Jupyter笔记本或类似的东西上使用SHAP图,但我真的想在web应用程序上显示它。
因为这对我来说是一个很大的痛点,我想巩固我所学到的并在这里写下来。我会帮我弥补一些在试镜过程中遇到的困难base64
,plt.savefig ()
我将以使用HTML iFrames的解决方案作为结束。
为了让这个结果可视化,一个示例最终结果是在Flask web应用程序中显示SHAP力图:
方法1:Base64编码
Base64是一个二进制到文本的编码方法。使用这个,我们可以定义我们的force_plot
和使用plt.savefig ()
把它存储在一个变量中缓冲区
稍后我们可以解码成ASCII码。在我看来,理解功能不是很重要,但是结果你的电脑会保存每一张图片,然后以HTML格式显示。这意味着img
标记的src
变量必须在本地定位每个保存的图像。
使用XGBoost进行Base64编码的示例
”“”
示例实现XGBoost算法和base64方法来保存SHAP力图,然后在HTML中显示。如果你在Flask中这样做,ML在app.py中,你传递SHAP力图作为一个变量,你可以在你的HTML页面中索引。”“”#火车测试分裂#让我们假设你有一些X_, y_…Xt, Xv, yt, yv = train_test_split(X_,y_, test_size=0.2, random_state=10)Dt = xgb。DMatrix (Xt,标签=欧美。值、enable_categorical = True)
Dv = xgb。DMatrix(十五、标签= yv.values)通过超贝叶斯优化优化超参数
参数= {
“埃塔”:0.5,
“max_depth”:8
“min_child_weight”:1、
“目标”:“二进制:物流”,
“冗长”:0,
“base_score”:np.mean(次),
:“eval_metric logloss”,
“colsample_bytree”:0.7434869381604485,
“伽马”:1.1053886968419446,
“reg_alpha”:49.0,
“reg_lambda”:0.9997899615065826
}#运行模型
Model = xgb。Train (params, dt, 35, [(dt, " Train "), (dv, "valid")], early_stopping_rounds=5, verbose_eval=35)#SHAP解释器值(NumPy数组)
explainer = shape . treeexplainer(模型)
shap_values = explainer.shap_values(Xv)# SHAP *力*图本身(使用这种方法,我认为你可以
#也做蜂群情节,但我没有尝试过)Shap_plot = shap_force_plot (explainer.expected_value,
shap_values[1],特性= Xv.iloc [1:],
feature_names = Xv.columns (0:20),
matplotlib=True, show=False, plot_cmap=['#77dd77', '#f99191'])#编码到base64,
#回答灵感:https://stackoverflow.com/questions/60621103/is-there-a-way-to-render-shap-or-lime-output-from-flask-to-react/60669449#60669449buf = BytesIO()
plt.savefig(缓冲区,
格式= "png",
Dpi = 150,
bbox_inch = 'tight')
dataToTake = base64.b64encode(buf.getbuffer()).decode("ascii")
返回dataToTake# HTML集成示例
这种方法存在的问题虽然这对于单个地块生成很方便,但在生产环境中并不可行,因为这意味着您将保存每个地块。
方法2:plt.savefig()
使用
plt.savefig ()
我们缩短了我们的工作(Base64不是完全必要的),但它是相同的结果,必须保存每个力图,并将其作为一个文件索引在我们的< img src = " wherever_the_plot_is_locally " >
标签。#只是使用上面相同的XGBoost模型def shap_plot(印第安纳州):
explainer = shape . treeexplainer(模型)
shap_values = explainer.shap_values(Xv)
P = shap.force_plot(解释器。期望值,shap_values[ind], Xv.iloc[[ind]])
plt.savefig(“temp.svg”)
plt.close ()
返回pshap_plot (3)是的,这更容易理解,但同样不适用于生产环境:您仍然保存每个图像。
方法3:使用.html()选项(我最终使用的)
使用这种方法,我们可以利用HTML iFrames,它可以嵌入我们的SHAP
force_plot
到主HTML中。#在Dash中工作
#从这里开始:https://stackoverflow.com/questions/54292226/putting-html-output-from-shap-into-the-dash-output-layout-callbackdef _force_plot_html (* args):
Force_plot = shape。force_plot(*args, matplotlib=False)
shap_html = f”< > {shap.getjs()} < /头> <身体> {force_plot.html()} < /身体> "
返回的html。Iframe (srcDoc = shap_html,
Style ={"width": "100%", "height": "200px",
“边境”:0})这并不能完全转化为我所使用的,即Flask。我采用了上面的想法,并将其应用到Flask环境中:
集成到Flask web应用程序的示例:
假设你有一个这样的逻辑回归模型:
Xt, Xv, yt, yv = train_test_split(X_, y_, test_size=0.2, random_state=10)model = LogisticRegression(刑罚='l2',求解器='liblinear', max_iter=900, C=0.1)。fit (Xt,欧美)解释=形状。解释器(model, Xt, feature_names= x .columns)
shap_values =解释器(Xv)基本的想法是好的
app.py
要创建_force_plot_html
函数使用讲解员
,shap_values
,印第安纳州
返回一个shap_html
srcdoc。我们会通过这个shap_html
变量到我们的HTML使用render_template
,并在HTML文件本身中显示shap_html
在嵌入的iFrame中。这是一个我们循环的例子
印第安纳州
,创建各种SHAP图,并在我们的HTML中显示为iFrames。#在app.py从烧瓶进口*进口世鹏科技电子
从shap.plots。导入draw_additive_plot从模型导入give_shap_plotapp = Flask(__name__)@app.route(“/”)def displayshap ():解释器,shap_values = give_shap_plot()Def _force_plot_html(解释器,shap_values, ind):
Force_plot = shap_values .force(shap_values[ind],
matplotlib = False)
shap_html = f”< > {shap.getjs()} < /头> <身体> {force_plot.html()} < /身体> "
返回shap_htmlShap_plots = {}对于I在范围(10):#你想要多少块
Ind = I
shap_values [i] = _force_plot_html(explainer, shap_values, ind)
返回render_template('displayshap.html', shap_plots = shap_plots)如果__name__ == '__main__':
app.run ()#退出app.py
# # #<!--HTML -->
<!--在displayshap.html -->{% for I in shap_plots: %}
< iframe srcdoc = " {{shap_plots[我]}}”风格={“宽度”:“2000 px”;“高度”:“200 px”;} > . Border: none
{% endfor %}的
shap.getjs ()
是非常重要的,因为没有它,你会在你的HTML中得到一个“可视化省略,Javascript库未加载!””的问题。缺点
这种解决方案的一些缺点是它只适用于力图.
- 我不能得到蜂群/酒吧的图像这样工作:
#可行的情节类型:
Force_plot = shap_values .force(shap_values[ind],matplotlib = False)#不起作用的情节类型:
Force_plot = sha_plot .beeswarm(shap_values)
Force_plot = shap.plot .bar(shap_values[ind])#我认为没有。html()方法是为这些写的?我不太确定谢谢你!
感谢您的阅读!我希望这对想要将SHAP合并到web应用程序中的任何人都有帮助。您可以找到本教程的源代码在这里.