多元自适应回归样条(Multivariate Adaptive Regression Splines),简称 **MARS**,是一种用于解决复杂非线性回归问题的算法。
该算法通过找到一组简单的线性函数,并将它们组合起来以获得最佳预测性能。通过这种方式,MARS 可以看作是简单线性函数的集成,并且可以在具有许多输入变量和复杂非线性关系的挑战性回归问题上取得良好的性能。
在本教程中,您将学习如何在 Python 中开发多元自适应回归样条模型。
完成本教程后,您将了解:
- 用于多元非线性回归预测建模问题的 MARS 算法。
- 如何使用 py-earth API 开发与 scikit-learn 兼容的 MARS 模型。
- 如何在回归预测建模问题上评估和使用 MARS 模型进行预测。
开始你的项目,阅读我的新书《Python 集成学习算法》,其中包含分步教程和所有示例的Python 源代码文件。
让我们开始吧。

Python 中的多元自适应回归样条 (MARS)
照片由 Sei F 拍摄,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- 多变量自适应回归样条
- MARS Python API
- MARS 回归示例
多变量自适应回归样条
多元自适应回归样条,简称 MARS,是一种为多元非线性回归问题设计的算法。
回归问题是指模型必须预测一个数值的问题。多元表示存在一个以上的(通常是几十个)输入变量,而非线性表示输入变量与目标变量之间的关系不是线性的,意味着无法用直线描述(例如,它是弯曲的)。
MARS 是一种自适应回归过程,非常适合高维问题(即大量的输入)。它可以被视为逐步线性回归的推广……
— 第 321 页,统计学习要素,2016。
MARS 算法涉及发现一组描述数据的简单分段线性函数,并将它们组合起来进行预测。从某种意义上说,该模型是线性函数的集成。
分段线性函数 (piecewise linear function) 是由较小的函数组成的函数。在这种情况下,它是一个函数,要么输出 0,要么直接输出输入值。
一个输入变量的“右函数”涉及选择一个变量的特定值,并为低于该值的所有值输出 0,为高于该值的所有值直接输出该值。
- f(x) = x if x > value, else 0
或者反过来,可以使用“左函数”,其中小于选择值的那些值直接输出,而大于选择值的那些值输出 0。
- f(x) = x if x < value, else 0
这被称为**铰链函数** (hinge function),其中选择的值或分割点是函数的“节点”(knot)。在神经网络中,它也被称为修正线性函数。
这些函数也称为“样条”(splines),因此得名该算法。
每个函数都是分段线性的,在节点 t 处有一个节点。用 [...] 的术语来说,这些是线性样条。
— 第 322 页,统计学习要素,2016。
MARS 算法生成许多这样的函数,称为一个或多个输入变量的基函数。
然后,从每个基函数的输出和目标变量中学习一个线性回归模型。这意味着每个基函数的输出都由一个系数加权。通过将模型中所有基函数的加权输出来求和来进行预测。
MARS 算法的关键在于基函数的选择方式。这涉及两个步骤:增长或生成阶段(称为前向阶段)和修剪或改进阶段(称为后向阶段)。
- 前向阶段:为模型生成候选基函数。
- 后向阶段:从模型中删除基函数。
前向阶段涉及生成基函数并将其添加到模型中。与决策树类似,训练数据集中的每个输入变量的每个值都被视为基函数的候选。
切割点是如何确定的?通过创建一个包含候选特征的线性回归模型来评估每个预测变量的每个数据点作为候选切割点,并计算相应的模型误差。
— 第 146 页,应用预测建模,2013。
函数总是成对添加,用于同一分割点的分段线性函数的左侧和右侧版本。只有当一对生成的函数能够减小整体模型的误差时,才将其添加到模型中。
后向阶段涉及一次选择要从模型中删除的函数。只有当某个函数对性能没有影响(中性)或能提高预测性能时,才将其从模型中删除。
一旦创建了完整的特征集,算法就会依次删除对模型方程没有显著贡献的单个特征。“修剪”过程评估每个预测变量,并估计包含它对模型误差率的降低程度。
— 第 148 页,应用预测建模,2013。
后向阶段的模型性能变化使用训练数据集的交叉验证进行评估,简称为广义交叉验证 (GCV)。因此,可以估计每个分段线性模型对模型性能的影响。
模型使用的函数数量是自动确定的,因为修剪过程将在不再有改进时停止。
需要考虑的两个关键超参数是生成的候选函数总数(通常设置为一个非常大的数字)以及生成的函数的次数。
… MARS 模型有两个调整参数:添加到模型中的特征的次数,以及保留的项数。后一个参数可以使用默认的修剪过程(使用 GCV)自动确定,也可以由用户设置或使用外部重采样技术确定。
— 第 149 页,应用预测建模,2013。
次数是每个分段线性函数考虑的输入变量的数量。默认情况下,它设置为 1,但可以设置为更大的值以允许模型捕获输入变量之间的复杂交互。次数通常保持较小以限制模型的计算复杂性(内存和执行时间)。
MARS 算法的一个优点是它只使用能提高模型性能的输入变量。与装袋和随机森林集成算法类似,MARS 实现了自动特征选择。
… 模型会自动进行特征选择;模型方程独立于未包含在任何最终模型特征中的预测变量。这一点不容低估。
— 第 149 页,应用预测建模,2013。
现在我们熟悉了 MARS 算法,让我们看看如何在 Python 中开发 MARS 模型。
想开始学习集成学习吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
MARS Python API
scikit-learn 库不提供 MARS 算法;相反,必须使用第三方库。
MARS 由 py-earth Python 库提供。
“Earth” 是对“Mars”(火星)的引用,也是 R 语言中提供 MARS 算法的包的名称。
py-earth Python 包是 MARS 的 Python 实现,以 R 版本命名,并提供与 scikit-learn 机器学习库完全兼容。
第一步是安装 py-earth 库。我建议使用 pip 包管理器,在命令行中使用以下命令:
1 |
sudo pip install sklearn-contrib-py-earth |
安装完成后,我们可以在 Python 脚本中加载该库并打印版本号,以确认其已正确安装。
1 2 3 4 |
# 检查 pyearth 版本 import pyearth # 显示版本 print(pyearth.__version__) |
运行脚本将加载 py-earth 库并打印库版本号。
您的版本号应与此相同或更高。
1 |
0.1.0 |
可以通过创建 Earth 类 的实例来创建具有默认模型超参数的 MARS 模型。
1 2 3 |
... # 定义模型 model = Earth() |
创建后,可以直接在训练数据上拟合模型。
1 2 3 |
... # 使用训练数据集拟合模型 model.fit(X, y) |
默认情况下,您可能不需要设置任何算法超参数。
算法会自动发现要使用的基函数的数量和类型。
基函数的最大数量由“max_terms”参数配置,它被设置为与输入变量数量成比例的较大数字,但最多为 400。
分段线性函数的次数,即每个基函数中考虑的输入变量数量,由“max_degree”参数控制,默认为 1。
拟合后,可以使用该模型对新数据进行预测。
1 2 3 4 |
... Xnew = ... # 进行预测 yhat = model.predict(Xnew) |
通过调用 summary() 函数可以创建拟合模型的摘要。
1 2 3 |
... # 打印拟合模型的摘要 print(model.summary()) |
摘要返回模型使用的基函数列表以及模型在训练数据集上通过广义交叉验证 (GCV) 估算的性能。
下面提供了一个摘要输出示例,其中我们可以看到模型有 19 个基函数,估计的 MSE 约为 25。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Earth 模型 -------------------------------------- 基函数 修剪 系数 -------------------------------------- (截距) 否 313.89 h(x4-1.88408) 否 98.0124 h(1.88408-x4) 否 -99.2544 h(x17-1.82851) 否 99.7349 h(1.82851-x17) 否 -99.9265 x14 否 96.7872 x15 否 85.4874 h(x6-1.10441) 否 76.4345 h(1.10441-x6) 否 -76.5954 x9 否 76.5097 h(x3+2.41424) 否 73.9003 h(-2.41424-x3) 否 -73.2001 x0 否 71.7429 x2 否 71.297 x19 否 67.6034 h(x11-0.575217) 否 66.0381 h(0.575217-x11) 否 -65.9314 x18 否 62.1124 x12 否 38.8801 -------------------------------------- MSE: 25.5896, GCV: 25.8266, RSQ: 0.9997, GRSQ: 0.9997 |
现在我们熟悉了使用 py-earth API 开发 MARS 模型,接下来我们来看一个实际示例。
MARS 回归示例
在本节中,我们将研究一个用于回归预测建模问题的 MARS 模型评估和使用的实际示例。
首先,我们必须定义一个回归数据集。
我们将使用 make_regression() 函数创建一个合成回归问题,包含 20 个特征(列)和 10,000 个样本(行)。下面的示例创建并总结了合成数据集的形状。
1 2 3 4 5 6 |
# 定义一个合成回归数据集 from sklearn.datasets import make_regression # 定义数据集 X, y = make_regression(n_samples=10000, n_features=20, n_informative=15, noise=0.5, random_state=7) # 汇总数据集 print(X.shape, y.shape) |
运行示例会创建数据集并总结行数和列数,这符合我们的预期。
1 |
(10000, 20) (10000,) |
接下来,我们可以在数据集上评估 MARS 模型。
我们将使用默认超参数来定义模型。
1 2 3 |
... # 定义模型 model = Earth() |
我们将使用重复 K 折交叉验证 (repeated k-fold cross-validation) 来评估模型,这通常是评估回归模型的好方法。
在这种情况下,我们将使用三次重复和 10 折。
1 2 3 |
... # 定义评估过程 cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1) |
我们将使用平均绝对误差(简称 MAE)来评估模型性能。
scikit-learn API 会将 MAE 分数变为负数,以便最大化它,这意味着分数范围从负无穷(最差)到 0(最好)。
1 2 3 |
... # 评估模型并收集结果 n_scores = cross_val_score(model, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1) |
最后,我们将模型在所有重复和交叉验证折上的平均 MAE 分数作为模型的性能进行报告。
1 2 3 |
... # 报告表现 print('MAE: %.3f (%.3f)' % (mean(n_scores), std(n_scores))) |
将所有内容整合起来,评估回归数据集上的 MARS 模型完整示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 评估用于回归的多元自适应回归样条 from numpy import mean from numpy import std from sklearn.datasets import make_regression from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedKFold from pyearth import Earth # 定义数据集 X, y = make_regression(n_samples=10000, n_features=20, n_informative=15, noise=0.5, random_state=7) # 定义模型 model = Earth() # 定义评估过程 cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型并收集结果 n_scores = cross_val_score(model, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1) # 报告表现 print('MAE: %.3f (%.3f)' % (mean(n_scores), std(n_scores))) |
运行示例会评估 MARS 模型的性能,并报告 MAE 分数的平均值和标准差。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到 MARS 算法在合成回归数据集上达到了平均 MAE 约为 4.0(忽略符号)。
1 |
MAE: -4.041 (0.085) |
我们可能希望将 MARS 用作最终模型,并用它来预测新数据。
这首先需要定义模型并在所有可用数据上进行拟合。
1 2 3 4 5 |
... # 定义模型 model = Earth() # 在整个数据集上拟合模型 model.fit(X, y) |
然后,我们可以调用 predict() 函数,并传入新的输入数据以进行预测。
1 2 3 |
... # 对单行数据进行预测 yhat = model.predict([row]) |
使用 MARS 最终模型并对单行新数据进行预测的完整示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 使用多元自适应回归样条进行回归预测 from sklearn.datasets import make_regression from pyearth import Earth # 定义数据集 X, y = make_regression(n_samples=10000, n_features=20, n_informative=15, noise=0.5, random_state=7) # 定义模型 model = Earth() # 在整个数据集上拟合模型 model.fit(X, y) # 定义单行数据 row = [-0.6305395, -0.1381388, -1.23954844, 0.32992515, -0.36612979, 0.74962718, 0.21532504, 0.90983424, -0.60309177, -1.46455027, -0.06788126, -0.30329357, -0.60350541, 0.7369983, 0.21774321, -1.2365456, 0.69159078, -0.16074843, -1.39313206, 1.16044301] # 对单行数据进行预测 yhat = model.predict([row]) # 总结预测 print('Prediction: %d' % yhat[0]) |
运行示例会拟合 MARS 模型,然后进行一次回归预测。
1 |
预测:-393 |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
论文
- 多变量自适应回归样条, 1991.
- 多元自适应回归样条简介, 1995.
书籍
API
文章
总结
在本教程中,您学习了如何在 Python 中开发多元自适应回归样条模型。
具体来说,你学到了:
- 用于多元非线性回归预测建模问题的 MARS 算法。
- 如何使用 py-earth API 开发与 scikit-learn 兼容的 MARS 模型。
- 如何在回归预测建模问题上评估和使用 MARS 模型进行预测。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
尊敬的Jason博士,
pip 安装,如下所示
仅适用于 Python 3.6 版本,https://pypi.ac.cn/project/sklearn-contrib-py-earth/#files
但是,如果您的 Python 版本大于 3.6,您可以从此网站下载 .whl 版本,https://www.lfd.uci.edu/~gohlke/pythonlibs/ 。
使用 Ctrl+F 搜索页面 sklearn_contrib_py_earth – 注意是下划线 _ 而不是 -。
选择 32 位或 64 位 Python,适用于 Python 版本 3.7、3.8 和 3.9。
将适当的 .whl 文件下载到您计算机上的一个文件夹中
安装 – 以 Python v3.8 64 位为例
测试
总之,如果您的 Python 版本大于 3.6,请访问 https://www.lfd.uci.edu/~gohlke/pythonlibs/。然后在浏览器页面中搜索 = CTRL+F sklearn_contrib_py_earth,并选择特定 Python 版本的 32 位或 64 位版本。例如,对于 Python v3.8,.whl 文件中必须包含 38。
下载适当的 .whl 文件,然后 pip 安装特定的 wheel。
谢谢你,
悉尼的Anthony
感谢分享。
我建议在撰写本文时使用 Python 3.6 进行机器学习和深度学习。
尊敬的Jason博士,
我有一个关于 Earth() 模型的问题,请。
我“浅读”了一下这个主题,它提到了节点和样条。这就是沿着 x 轴存在一个间断点,您构建一个节点/样条。
样条和节点不就是精算学专业学生/毕业生在对不连续的 x 数据进行建模时使用的东西吗?
谢谢你,
悉尼的Anthony
抱歉,我不知道精算学专业的学生学什么。
尊敬的Jason博士,
我并不期望您知道精算学专业的学生学什么。我从本科学习中知道,我学习了样条,尽管我从未学习过精算学。
这个网站 https://www.acted.co.uk/forums/index.php?threads/splines-in-emblem.8885/ 回答了这个问题。即样条曲线“…是一组连接在一起以产生一个更复杂的单一曲线…”的曲线,因此一个人可以“…使用相当简单的函数来建模复杂曲线,并以任意复杂的程度对其进行建模。
本文档显示了非线性函数的图形以及连接“不连续”片段的插值方法 http://www.ae.metu.edu.tr/~ae464/splines.pdf。
因此,我对本页使用的 Earth() 算法可能有更好的理解。
谢谢你,
悉尼的Anthony
感谢分享链接。
尊敬的Jason博士,
我打印了模型的摘要
从摘要中
R^2 RSQ = 0.9997,
这接近完美。R^2 不就是衡量准确性的指标吗?
谢谢你,
悉尼的Anthony
R^2 是一个拟合优度指标。
https://en.wikipedia.org/wiki/Coefficient_of_determination
尊敬的Jason博士,
当我打印 summary() 时,我期望它是表格形式的。你必须移动水平滑块才能在最右边看到 R^2。
谢谢你,
悉尼的Anthony
尝试将摘要打印到控制台,这样换行符 (\n) 才能被正确解释。
尊敬的Jason博士,
我明白 R^2 解释了方差,而 1-R^2 是未解释的方差。sqrt(R^2) = |R| 是相关性的幅度,不考虑方向。
上述模型的R^2为0.99997。拟合优度非常接近1。
我的问题是,能否同时拥有非常高的拟合优度但准确度很低?例如,平均绝对误差(MAE)是原始值与预测值之间差异的平均值。
换句话说,R^2可以高,MAE也可以高或低吗?
换个说法,如果R^2很高,比如0.9997,为什么还要担心MAE?
谢谢你
悉尼的Anthony
好问题。我的直觉是R^2和误差指标高度相关。例如,不行,你不可能一个分数显示性能良好而另一个显示性能较差。尽管如此,用一些人为设计的案例来证实这种直觉还是很好的(这是一个很棒的博文主题!)。
你好,Jason
感谢您提供有趣的指导!
不幸的是,安装‘pip install sklearn-contrib-py-earth’时出现了无法理解的错误消息。我没能真正看出原因。但也许只有我一个人是这样?
此致
Matthias
试试这个
亲爱的Jason
我的python版本是3.6.10。
我有这段代码:
# 检查 pyearth 版本
import pyearth
# 显示版本
print(pyearth.__version__)
# 定义模型
model = Earth()
运行这段代码时,我得到了:
import pyearth
# 显示版本
print(pyearth.__version__)
# 定义模型
model = Earth()
0.1.0
回溯(最近一次调用)
File “”, line 6, in
model = Earth()
NameError: name ‘Earth’ is not defined
我测试pyearth得到:
dir(pyearth)
Out[12]
[‘Earth’,
‘__builtins__’,
‘__cached__’,
‘__doc__’,
‘__file__’,
‘__loader__’,
‘__name__’,
‘__package__’,
‘__path__’,
‘__spec__’,
‘__version__’,
‘_basis’,
‘_forward’,
‘_knot_search’,
‘_pruning’,
‘_qr’,
‘_record’,
‘_types’,
‘_util’,
‘_version’,
‘earth’]
我哪里做错了?
看起来你没有导入“Earth”类。
也许确认一下你是否完全按照教程复制代码了。
请忽略上面的内容。当我运行你的最终代码时,问题就解决了。
谢谢
很高兴听到这个。
你好 Jason,
非常清晰和精彩的MARS/EARTH(多变量自适应回归样条/通过铰链的增强自适应回归)教程,感谢您的教程。
但是有几件事让我感到困扰:
1. 何时我们应优先选择MARS而非其他非线性集成模型,如随机森林、GBM、XGBoost等?
2. MARS的优缺点是什么?
3. 为什么现代机器学习书籍、博客、文章和教程不像XGboost等算法那样多地讨论它?
谢谢。
当MARS的表现优于你评估过的其他算法时,就使用它。没有比这更好的经验法则了。
MARS比一些算法更复杂,因此训练速度可能较慢。
很少有人了解MARS,也许这就是他们不写它的原因。
你好,Jason。感谢分享。这很有帮助。我想在这个MARS模型中尝试MultioutputRegressor。但我认为它不可调用,你知道如何使用MARS进行多输出吗?
谢谢
不客气。
我不确定,也许你可以试试看?
你好Jason,我只是问一下,它能像ARIMA或Prophet那样用于预测多步或多天ahead吗?
它可以用于时间序列预测,但它更普遍的设计是用于回归。
你好Jason,感谢您提供有趣的教程。
是否可以通过MARS进行回归并查看回归方案(模型)?
此致,
当然。也许你可以检查拟合的MARS对象并检索模型细节。
感谢您的回复!
不客气。
你好Jason,我喜欢你的博客。
我想提一下,可解释性是回归样条的一个主要优势。
另一方面,正如Kuhn在他的精彩著作中提到的,它们可能不稳定(尤其是对于较高阶的数值)。这也是我与其他非线性框架相比的经验。
我只是觉得这可以为您的精彩帖子锦上添花。
谢谢。
同意。感谢分享。
你好Jason,感谢您的博文。我有一个关于估计量的问题。既然它们是由节点分隔的线性回归,这是否意味着变量(年龄-50)的估计值20表示在50岁之后,每个单位年龄的增加,结果的平均变化是20?另一方面,既然这是一种非参数方法,估计量是否应该是中位数,或者“非参数”在这种情况下是否仅仅意味着我们需要使用GCV来确定模型拟合?
不客气。
我猜想响应函数的拐点使得响应函数非线性。例如,分析起来不像线性回归那样容易。
另外,我的MARS模型中铰链函数的输出与部分依赖图的输出不符。
例如,我得到(30 - 变量);估计值:-0.7,而部分依赖图中30之前的变量实际上是随着结果而增加的。我不确定为什么估计值是负的,而可视化是正的。
尊敬的Brownlee博士,是否可以使用pyearth进行Y为离散变量的建模,即用于解决分类问题?
它应该能提供一些结果,但该模型并非为真正的离散变量设计。因此结果可能不太好。
您好。
感谢您宝贵的课程。我有一个小问题。如果我使用对数变量(COVID-19病例数据的对数)作为因变量,而使用其他非对数变量作为自变量,我可以使用MARS处理它们吗?使用MARS处理对数转换变量有什么限制吗?
谢谢
MARS提供分段线性函数。因此,对于混合的对数和非对数变量,你正在构建类似于“log(x1) + b x2”的东西。这有时是有意义的,例如,考虑到放射性衰变,它在形式上是线性的。
您好,请问如何将交叉验证程序确定的超参数用于训练数据集,然后评估测试集上的模型性能?谢谢!