在监督学习方法中,我们有特征(输入)和目标(输出)。如果我们有多个维度的目标,它们按行总和为1(例如[0.3, 0.4, 0.3]),为什么当训练数据的总和为1时,scikit-learn的RandomForestRegressor似乎会将所有输出/预测归一化,使其总和为1?
看起来在scikit-learn的源代码中的某个地方,如果训练数据总和为1,它会对输出进行归一化处理,但我没能找到这个部分。我已经查看了BaseDecisionTree
类,这个类似乎被随机森林使用,但没有看到任何归一化的迹象。我创建了一个gist来展示它的工作原理。当目标的行总和不为1时,回归器的输出也不会总和为1。但当目标的行总和确实为1时,它似乎会进行归一化处理。以下是来自gist的示范代码:
import numpy as npfrom sklearn.ensemble import RandomForestRegressor# 模拟数据# 12行训练数据,6行测试数据,5个特征,3列目标features = np.random.random((12, 5))targets = np.random.random((12, 3))test_features = np.random.random((6, 5))rfr = RandomForestRegressor(random_state=42)rfr.fit(features, targets)preds = rfr.predict(features)print('preds sum to 1?')print(np.allclose(preds.sum(axis=1), np.ones(12)))# 将目标归一化为总和为1norm_targets = targets / targets.sum(axis=1, keepdims=1)rfr.fit(features, norm_targets)preds = rfr.predict(features)te_preds = rfr.predict(test_features)print('predictions all sum to 1?')print(np.allclose(preds.sum(axis=1), np.ones(12)))print('test predictions all sum to 1?')print(np.allclose(te_preds.sum(axis=1), np.ones(6)))
最后一点要注意的是,我尝试在其他随机森林实现中运行类似的拟合(Python中的H2O,R中的rpart, Rborist, RandomForest),但没有找到另一个支持多输出的实现。
我的猜测是scikit-learn的代码中存在一个错误,它在某种程度上混淆了分类和回归,导致输出像分类问题一样被归一化为1。
回答:
这里可能误导人的地方在于,你只关注了输出值的sum
结果。当模型使用归一化标签进行训练时,所有预测总和为1的原因是,它只会在它所见过的这些多输出数组中进行预测。这是因为在样本数量较少的情况下,模型会过拟合,决策树实际上像分类器一样工作。
换句话说,来看一个输出未归一化的例子(这同样适用于DecisionTree
):
from sklearn.tree import DecisionTreeRegressorfeatures = np.random.random((6, 5))targets = np.random.random((6, 3))rfr = DecisionTreeRegressor(random_state=42)rfr.fit(features, targets)
如果我们现在在一个新的随机特征集上进行预测,我们将得到模型训练过的输出集中的预测值:
features2 = np.random.random((6, 5))preds = rfr.predict(features2)print(preds)array([[0.0017143 , 0.05348525, 0.60877828], #0 [0.05232433, 0.37249988, 0.27844562], #1 [0.08177551, 0.39454957, 0.28182183], [0.05232433, 0.37249988, 0.27844562], [0.08177551, 0.39454957, 0.28182183], [0.80068346, 0.577799 , 0.66706668]])print(targets)array([[0.80068346, 0.577799 , 0.66706668], [0.0017143 , 0.05348525, 0.60877828], #0 [0.08177551, 0.39454957, 0.28182183], [0.75093787, 0.29467892, 0.11253746], [0.87035059, 0.32162589, 0.57288903], [0.05232433, 0.37249988, 0.27844562]]) #1
所以,逻辑上,如果所有训练输出的总和为1
,预测值的总和也会如此。
如果我们取目标和预测值沿第一个轴的sum
的交集,我们会看到所有预测值的总和存在于targets
中:
preds_sum = np.unique(preds.sum(1))targets_sum = np.unique(targets.sum(1))len(np.intersect1d(targets_sum, preds_sum)) == len(features)# True