本文始发于个人公众号:TechFlow,原创不易,求个关注
今天这篇是机器学习专题的第24篇文章,我们来聊聊回归树模型。
所谓的回归树模型其实就是用树形模型来解决回归问题,树模型当中最经典的自然还是决策树模型,它也是几乎所有树模型的基础。虽然基本结构都是使用决策树,但是根据预测方法的不同也可以分为两种。第一种,树上的叶子节点就对应一个预测值和分类树对应,这一种方法称为回归树。第二种,树上的叶子节点对应一个线性模型,最后的结果由线性模型给出。这一种方法称为模型树。
今天我们先来看看其中的回归树。
回归树模型
回归树模型的核心算法,也就是构建决策树的算法,就是我们上篇文章所讲的CART算法。如果有生疏或者是遗漏的同学,可以通过下方传送门回顾一下:
CART算法的核心精髓就是我们每次选择特征对数据进行拆分的时候,永远对数据集进行二分。无论是离散特征还是连续性特征,一视同仁。CART还有一个特点是使用GINI指数而不是信息增益或者是信息增益比来选择拆分的特征,但是在回归问题当中用不到这个。因为回归问题的损失函数是均方差,而不是交叉熵,很难用熵来衡量连续值的准确度。
在分类树当中,我们一个叶子节点代表一个类别的预测值,这个类别的值是落到这个叶子节点当中训练样本的类别的众数,也就是出现频率最高的类别。在回归树当中,叶子节点对应的自然就是一个连续值。这个连续值是落到这个节点的训练样本的均值,它的误差就是这些样本的均方差。
另外,之前我们在选择特征的划分阈值的时候,对阈值的选择进行了优化,只选择了那些会引起预测类别变化的阈值。但是在回归问题当中,由于预测值是一个浮点数,所以这个优化也不存在了。整体上来说,其实回归树的实现难度比分类树是更低的。
实战
我们首先来加载数据,我们这次使用的是scikit-learn库当中经典的波士顿房价预测的数据。关于房价预测,kaggle当中也有一个类似的比赛,叫做:house-prices-advanced-regression-techniques。不过给出的特征更多,并且存在缺失等情况,需要我们进行大量的特征工程。感兴趣的同学可以自行研究一下。
首先,我们来获取数据,由于sklearn库当中已经有数据了,我们可以直接调用api获取,非常简单:
import numpy as np
import pandas as pd
from sklearn.datasets import load_boston
boston = load_boston()
X, y = boston.data, boston.target
我们输出前几条数据查看一下:
这个数据质量很高,sklearn库已经替我们做完了数据筛选与特征工程,直接拿来用就可以了。为了方便我们传递数据,我们将X和y合并在一起。由于y是一维的数组形式是不能和二维的X合并的,所以我们需要先对y进行reshape之后再进行合并。
y = y.reshape(-1, 1)
X = np.hstack((X, y))
hstack函数可以将两个np的array横向拼接,与之对应的是vstack,是将两个array纵向拼接,这个也是常规操作。合并之后,y作为新的一列添加在了X的后面。数据搞定了,接下来就要轮到实现模型了。
在实现决策树的主体部分之前,我们先来实现两个辅助函数。第一个辅助函数是计算一批样本的方差和,第二个辅助函数是获取样本的均值,也就是子节点的预测值。
def node_mean(X):
return np.mean(X[:, -1])
def node_variance(X):
return np.var(X[:, -1]) * X.shape[0]
这个搞定了之后,我们继续实现根据阈值拆分数据的函数。这个也可以复用之前的代码:
from collections import defaultdict
def split_dataset(X, idx, thred):
split_data = defaultdict(list)
for x in X:
split_data[x[idx] < thred].append(x)
return list(split_data.values()), list