Sklearn DecisionTreeRegressor如何实现训练区间外趋势延伸预测
问题原因
原生DecisionTreeRegressor属于分段常数模型,本身不具备外推能力:所有落在训练集特征取值范围外的样本,都会被路由到最边缘的叶子节点,直接返回该叶子节点上训练样本标签的均值。因此只要输入特征超过训练集特征最大值,无论超出幅度多大,都会返回训练集标签的最大值,这是模型本身的固有机制,不属于代码错误。
实现方案
方案1:封装现有决策树,手动增加外推逻辑
不需要更换现有模型框架,只需要在预测层加一层判断:对超出训练集特征上限的样本,用训练集最右侧区间拟合的变化趋势做线性外推即可。
可直接复用的封装代码如下:
import numpy as np from sklearn.tree import DecisionTreeRegressor class ExtrapableDTR: def __init__(self, random_state=0): self.model = DecisionTreeRegressor(random_state=random_state) self.x_upper = None self.y_upper = None self.right_slope = None def fit(self, X_train, y_train): self.model.fit(X_train, y_train) # 记录训练集特征上限及对应预测值 self.x_upper = X_train.max() self.y_upper = self.model.predict([[self.x_upper]])[0] # 取上限左侧最近的训练点,计算最后一段的变化斜率 x_near_upper = X_train[X_train < self.x_upper].max() y_near_upper = self.model.predict([[x_near_upper]])[0] self.right_slope = (self.y_upper - y_near_upper) / (self.x_upper - x_near_upper) return self def predict(self, X_test): y_pred = self.model.predict(X_test) # 筛选超出训练集范围的样本 out_range_idx = X_test.flatten() > self.x_upper # 按斜率外推计算超界样本的预测值 y_pred[out_range_idx] = self.y_upper + self.right_slope * (X_test.flatten()[out_range_idx] - self.x_upper) return y_pred # 效果测试 X_train = np.array([[100],[500],[1500],[3500]]) y_train = np.array([23, 43, 44, 55]) model = ExtrapableDTR(random_state=0) model.fit(X_train, y_train) X_test = np.array([[700], [4000], [10000]]) print(model.predict(X_test)) # 输出 [43. 57.75 90.75]
如果需要更精准的斜率,也可以直接解析决策树的分裂结构,取最右侧叶子节点对应的分裂区间计算斜率,不需要依赖训练集的原始样本点。
方案2:更换支持外推的树类模型
如果不想手动编写外推逻辑,可以替换为M5模型树:这类树模型的叶子节点存储的不是常数,而是拟合该节点样本的线性回归模型,预测范围外样本时会自动用叶子节点的线性模型做外推,符合趋势延续的需求,同时保留树模型的可解释性。
注意事项
所有外推结果都存在固有不确定性,如果训练集范围外的实际趋势和拟合的最后一段区间趋势不一致,预测结果会出现偏差,落地时需要结合业务规则做校验。
内容的提问来源于stack exchange,提问作者robert_gonzalez
相关产品推荐
相关产品推荐

