Почему прогнозы нескольких целей иногда суммируются до 1 с помощью sklearn RandomForestRegressor? - PullRequest
1 голос
/ 06 мая 2020

При контролируемом методе обучения у нас есть функции (входы) и цели (выходы). Если у нас есть многомерные цели, которые суммируются до 1 по строкам (например, [0,3, 0,4, 0,3]), почему случайныйForestRegressor sklearn, похоже, нормализует все выходы / прогнозы, чтобы суммировать их до 1, когда данные обучения суммируются до 1?

Похоже, что где-то в исходном коде sklearn происходит нормализация результатов, если сумма обучающих данных равна 1, но я не смог его найти. Я дошел до BaseDecisionTree class , который, кажется, используется случайными лесами, но не смог увидеть там какой-либо нормализации. Я создал gist , чтобы показать, как это работает. Когда строчные суммы целевых показателей не равны 1, выходные данные регрессора не суммируются до 1. Но когда строчные суммы целевых показателей суммируются до 1, кажется, что это нормализует их. Вот демонстрационный код из сути:

import numpy as np
from sklearn.ensemble import RandomForestRegressor

# simulate data
# 12 rows train, 6 rows test, 5 features, 3 columns for target
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)))


# normalize targets to sum to 1
norm_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)))

И последнее замечание: я попытался выполнить сопоставимую подгонку в других реализациях случайного леса (H2O в Python, в R: rpart, Rborist, RandomForest) но не нашел другой реализации, которая допускает несколько выходов.

Я предполагаю, что в коде sklearn есть ошибка, которая каким-то образом смешивает классификацию и регрессию, и выходные данные нормализуются до 1, как классификация проблема.

1 Ответ

2 голосов
/ 06 мая 2020

Что здесь может ввести в заблуждение, так это то, что вы смотрите только на результирующие sum выходных значений. Причина, по которой все прогнозы в сумме дают 1, когда модель обучается с нормализованными метками, заключается в том, что она будет прогнозировать только среди этих массивов с несколькими выходами, которые она видела. И это происходит потому, что при таком небольшом количестве образцов модель переоснащается, и дерево решений де-факто действует как классификатор. применяется к DecisionTree):

from sklearn.tree import DecisionTreeRegressor

features = np.random.random((6, 5))
targets = np.random.random((6, 3))

rfr = DecisionTreeRegressor(random_state=42)
rfr.fit(features, targets)

Если теперь мы прогнозируем на новый набор случайных функций, мы будем получать прогнозы среди набора выходных данных, которые модель была обучена on:

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 s вдоль первой оси для целевых и прогнозируемых значений, мы видим, что сумма всех прогнозируемых значений существует в 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
...