本文已参与「新人创作礼」活动,一起开启掘金创作之路。
本文是英文原著翻译,并结合了其他文献的一个有益集成。中文在英文简述的后面。
We’ll follow the general machine learning workflow step-by-step 第三部分:
- Data cleaning and formatting 数据清洗和格式化
- Exploratory data analysis 探索性的数据分析
- Feature engineering and selection 字段的加工和选择
- Hide and filter Sensitive Feature 数据脱敏
- Compare several machine learning models on a performance metric 模型的评估和选择
- Perform hyperparameter tuning on the best model 用于模型优化的超参数调整
- Evaluate the best model on the testing set 评估测试集
- Interpret the model results 模型解释
- Draw conclusions and document work 记录工作和报告结果
Extrac from
机器学习模型经常被批评为黑盒子:我们将数据放在一边,然后得到答案 - 通常是非常准确的答案 - 但另一方面却解释不了为什么模型能给出准确的答案。在本系列的第三部分展示了一个完整的机器学习解决方案,我们将深入研究我们开发的模型,以尝试理解它如何进行预测以及它可以告诉我们有关解释模型的问题。我们将讨论机器学习项目中最重要的部分:记录我们的工作并展示结果。
该系列的第一部分包括数据清理,探索性数据分析,字段工程和字段选择。第二部分介绍了缺失值,实现和比较机器学习模型,使用随机搜索和交叉验证进行超参数调整,以及评估模型。
该项目的所有代码都在GitHub上。对应这篇文章的第三个Jupyter笔记本就在这里。我鼓励任何人分享,使用和构建此代码!
提醒一下,我们正在通过监督回归机器学习问题。 利用纽约市建筑能源数据,我们开发了一个模型,可以预测建筑物的能源的星级得分。 我们建立的最终模型是Gradient Boosted回归量,它能够将测试数据上的能量的星级得分预测到9.1分(1-100分)。
模型解释 (Model Interpretation)
梯度增强回归量位于模型可解释性中间的中间位置:整个模型很复杂,但它由数百个决策树组成,这些决策树本身是可以理解的。 我们将研究三种方法来理解我们的模型如何进行预测:
- 字段重要性/feature importance
- 可视化单个决策树
- LIME:本地可解释的模型 - 不可知解释(LIME: Local Interpretable Model-Agnostic Explainations)
前两种方法特定于树的集合,而第三种 - 正如您可能从名称中猜到的 - 可以应用于任何机器学习模型。 LIME是一个相对较新的软件包,代表了解释机器学习预测的持续努力中令人兴奋的一步。
字段重要性 (Feature Importances)
字段重要性尝试显示每个字段与预测目标任务的相关性。 字段重要性的技术细节很复杂(它们测量平均减少杂质,或者包括字段的误差减少/they measure the mean decrease impurity, or the reduction in error from including the feature).但我们可以使用相对值来比较哪些字段最相关。 在Scikit-Learn中,我们可以从任何基于树的学习者集合中提取重要的字段
使用模型作为我们训练的模型,我们可以使用 model.feature_importances_
找到最重要或相关的字段。 然后我们可以将它们放入pandas DataFrame
中并显示或绘制前十个最重要的字段:
import pandas as pd
# model is the trained model
importances = model.feature_importances_
# train_features is the dataframe of training features
feature_list = list(train_features.columns)
# Extract the feature importances into a dataframe
feature_results = pd.DataFrame({'feature': feature_list,
'importance': importances})
# Show the top 10 most important
feature_results = feature_results.sort_values('importance',
ascending = False).reset_index(drop=True)
feature_results.head(10)
Site EUI(能源使用强度)和天气规范化场地电力强度(Weather Normalized Site Electricity Intensity)是迄今为止最重要的特征,占总重要性的66%以上。 在前两个功能之后,重要性显着下降,这表明我们可能不需要保留数据中的所有64个字段以实现高性能。 (在Jupyter笔记本中,我尝试一下仅使用前10个字段,发现模型不太准确。)
基于这些结果,我们最终可以回答我们的一个初步问题:建筑物能源的星级得分的最重要指标是Site EUI
和天气规范化场地电力强度(Weather Normalized Site Electricity Intensity
)。 虽然我们确实需要注意不要过多地阅读字段重要性,但它们是开始理解模型以及如何进行预测的有用方法。
可视化单个决策树 (Visualizing a Single Decision Tree)
虽然整个梯度增强回归量可能难以理解,但任何一个单独的决策树都非常直观。 我们可以使用Scikit-Learn
函数export_graphviz
可视化森林中的任何树。 我们首先从集合中提取树,然后将其保存为dot文件:
from sklearn import tree
# Extract a single tree (number 105)
single_tree = model.estimators_[105][0]
# Save the tree to a dot file
tree.export_graphviz(single_tree, out_file = 'images/tree.dot',
feature_names = feature_list)
使用Graphviz可视化软件,我们可以从命令行将dot文件转换为png:
dot -Tpng images/tree.dot -o images/tree.png
结果是完整的决策树
这有点难以阅读!尽管这棵树的深度只有6(层数),但很难遵循。 我们可以修改对export_graphviz
的调用,并将我们的树限制为更合理的深度2:
树中的每个节点(框)都有四条信息:
- 问题是关于数据点的一个字段的值:这确定我们是从右边还是左边离开节点
- mse是节点误差的度量
- 样本是节点中的示例(数据点)的数量
- 该值是节点中所有样本的目标估计值
(叶节点只有2.-4。因为它们代表最终估计,叶节点也没有任何子节点)。
决策树通过从称为根的顶部节点开始并沿着树向下工作来对数据点进行预测。在每个节点,询问数据点是或否。例如,上述节点的问题是:建筑物的Site EUI是否小于或等于68.95?如果答案为是,则将建筑物放置在右子节点中,如果答案为否,则建筑物将转到左子节点。
在树的每一层重复该过程,直到数据点被放置在树的底部的叶节点中(叶子节点从小树图像中裁剪)。叶节点中所有数据点的预测值j就是value
。如果叶节点中存在多个数据点(sample
),则它们都获得相同的预测。随着树的深度增加,训练集上的误差将减少,因为有更多叶节点或示例可以更精细地划分。但是,太深的树会过度拟合(overfit)训练数据,并且无法推广到新的测试数据。
在第二篇文章中,我们调整了许多模型超参数,它们控制每棵树的各个方面,例如树的最大深度和叶节点中所需的最小样本数。这些都对underfit和overfit的平衡产生了重大影响,并且可视化单个决策树使我们能够看到这些设置如何工作。
虽然我们无法检查模型中的每一棵树,但查看一个树让我们了解每个学习者如何进行预测。这种基于流程图的方法看起来很像人类做出决策,一次回答一个关于单个值的问题。基于决策树的集合结合了许多单个决策树的预测,以便创建具有较小方差的更准确的模型。树木的集合往往非常准确,也很容易解释。
本地可解释模型 - 不可知解释(LIME)(Local Interpretable Model-Agnostic Explanations (LIME))
我们将探索的最后一个工具,试图了解我们的模型如何“思考”是模型解释领域的新进入。 LIME旨在通过使用简单模型(如线性回归)在数据点附近创建近似模型来解释任何机器学习模型中的单个预测(完整细节可在本文中找到)。
在这里,我们将使用LIME来检查模型完全错误的预测,看看它可能告诉我们模型为什么会出错。
首先,我们需要找到我们的模型最错误的观察结果。 我们通过使用模型进行训练和预测并提取模型具有最大错误的示例来实现此目的:
from sklearn.ensemble import GradientBoostingRegressor
# Create the model with the best hyperparamters
model = GradientBoostingRegressor(loss='lad', max_depth=5, max_features=None,
min_samples_leaf=6, min_samples_split=6,
n_estimators=800, random_state=42)
# Fit and test on the features
model.fit(X, y)
model_pred = model.predict(X_test)
# Find the residuals
residuals = abs(model_pred - y_test)
# Extract the most wrong prediction
wrong = X_test[np.argmax(residuals), :]
print('Prediction: %0.4f' % np.argmax(residuals))
print('Actual Value: %0.4f' % y_test[np.argmax(residuals)])
Prediction: 12.8615
Actual Value: 100.0000
接下来,我们创建LIME explainer对象,将我们的训练数据,模式,训练标签和我们数据中的字段名称传递给它。 最后,我们要求解释器对象解释错误的预测,并将其传递给观察和预测函数。
import lime
# Create a lime explainer object
explainer = lime.lime_tabular.LimeTabularExplainer(training_data = X,mode = 'regression',
training_labels = y,
feature_names = feature_list)
# Explanation for wrong prediction
exp = explainer.explain_instance(data_row = wrong,
predict_fn = model.predict)
# Plot the prediction explaination
exp.as_pyplot_figure();
解释这一预测的图片如下:
以下是如何解释上面的图片:y轴上的每个条目表示变量的一个值,红色和绿色条表示此值对预测的影响。 例如,顶部条目表示Site EUI大于95.90,从预测中减去约40个点。 第二个条目是天气归一化场地电力强度(the Weather Normalized Site Electricity Intensity)小于3.80,这增加了预测的10个点。 最终预测是截距项加上每个单独贡献的总和。
我们可以通过调用explaininer.show_in_notebook方法()
来查看相同的信息:
这通过显示每个变量对预测的贡献来显示左侧模型的推理过程。右侧的表格显示了数据点变量的实际值。
对于此示例,模型预测大约为12,实际值为100!虽然最初这个预测可能令人费解,但从解释来看,我们可以看到这不是一个极端的猜测,而是根据数据点的值给出的合理估计。Site EUI相对较高,我们预计能源的星级得分较低(因为EUI与得分强烈负相关),这是我们模型共享的结论。在这种情况下,逻辑是错误的,因为建筑物的满分为100。
当模型出错时,这可能令人沮丧,但这些解释有助于我们理解模型不正确的原因。此外,根据解释,我们可能想调查为什么尽管有如此高的Site EUI,建筑物仍然有一个完美的分数。也许我们可以在不调查模型的情况下学习一些新的问题。像这样的工具并不完美,但它们在帮助我们理解模型方面还有很长的路要走,这反过来可以让我们做出更好的决策。
记录工作和报告结果 (Documenting Work and Reporting Results)
任何技术项目中经常被忽视的部分是文档和报告。我们可以在世界上做最好的分析,但如果我们没有清楚地传达结果,那么它们就不会有任何影响!
当我们记录数据科学项目时,我们会采用所有版本的数据和代码并对其进行打包,以便我们的项目可以被其他数据科学家复制或构建。重要的是要记住代码的阅读频率高于编写代码,并且我们希望确保我们的工作对于其他人和我们自己都是可以理解的,如果我们几个月后再回来的话。这意味着在代码中添加有用的注释并解释您的推理。我发现Jupyter Notebook是一个很好的文档工具,因为它们允许一个接一个地解释和编码。
Jupyter笔记本也可以成为将结果传达给他人的好平台。使用笔记本扩展,我们可以隐藏最终报告中的代码,因为虽然很难相信,但并不是每个人都希望在文档中看到一堆Python代码!
就个人而言,我很简洁地总结了我的工作,因为我喜欢详细介绍所有细节。但是,在您呈现和相应地定制消息时,了解您的观众非常重要。考虑到这一点,这是我在项目中的30秒打包内容:
- 使用纽约市能源数据,可以建立一个模型,可以将建筑物的能源的星级得分预测在9.1分以内。
- Site EUI和天气归一化电力强度(Weather Normalized Electricity Intensity)是预测能源之星得分的最相关因素。
最初,这个项目是初创公司给我的试用期的工作。对于最终报告,他们希望看到我的工作和结论,所以我开发了一个Jupyter笔记本来交。但是,我没有直接转换为Jupyter
中的PDF
,而是将其转换为Latex.tex文件,然后我编辑了在texStudio中渲染为最终版本的PDF
。 Jupyter
的默认PDF输出具有不错的外观,但只需几分钟的编辑就可以显着改善。此外,Latex
是一个功能强大的文档准备系统,了解基础知识是很好的。
在最后一天,our work is only as valuable as the decisions it enables,并且能够呈现结果是一项至关重要的技能。此外,通过正确记录工作,我们允许其他人重现我们的结果,给我们反馈,以便我们成为更好的数据科学家,并在我们未来的工作基础上继续发展。
结论 (Conclusions)
在这一系列帖子中,我们已经完成了一个完整的端到端机器学习项目。我们首先清理数据,进入模型构建,最后研究如何解释机器学习模型。提醒一下,机器学习项目的总体结构如下:
- 数据清理和格式化
- 探索性数据分析
- 字段工程和选择
- 处理敏感数据
- 根据性能指标比较几种机器学习模型
- 在最佳模型上执行超参数调整
- 评估测试集上的最佳模型
- 尽可能地解释模型结果
- 得出结论并撰写详尽记录的报告
虽然确切的步骤因项目而异,并且机器学习通常是迭代而非线性过程,但本指南应该在您处理未来的机器学习项目时为您提供良好的服务。我希望这个系列让你有信心能够实现自己的机器学习解决方案,但请记住,我们都不是自己做的!如果您需要任何帮助,有许多令人难以置信的支持性社区,您可以在那里寻求建议。
一如既往,我欢迎反馈和建设性的批评,可以通过我的Twitter账号联系我@koehrsen_will。
参考文献
- Hands-On Machine Learning with Scikit-Learn and Tensorflow (Jupyter Notebooks for this book are available online for free)!
- An Introduction to Statistical Learning
- Kaggle: The Home of Data Science and Machine Learning
- Datacamp: Good beginner tutorials for practicing data science coding
- Coursera: Free and paid courses in many subjects
- Udacity: Paid programming and data science courses