Я використовую код sklearn , оскільки він, як правило, набагато чистіший за R
код.
Ось реалізація властивості feature_imporences GradientBoostingClassifier (я видалив деякі рядки коду, які перешкоджають концептуальному матеріалу)
def feature_importances_(self):
total_sum = np.zeros((self.n_features, ), dtype=np.float64)
for stage in self.estimators_:
stage_sum = sum(tree.feature_importances_
for tree in stage) / len(stage)
total_sum += stage_sum
importances = total_sum / len(self.estimators_)
return importances
Це досить легко зрозуміти. self.estimators_
являє собою масив, що містить окремі дерева в бустері, тому цикл for є ітерацією над окремими деревами. Є одна хитка з
stage_sum = sum(tree.feature_importances_
for tree in stage) / len(stage)
це турбота про небінарний випадок відповіді. Тут ми вміщуємо кілька дерев на кожному етапі в один спосіб. Найпростіший концептуально - зосередитись на двійковому випадку, де сума має одну суму, і це справедливо tree.feature_importances_
. Тож у двійковому випадку ми можемо переписати це все як
def feature_importances_(self):
total_sum = np.zeros((self.n_features, ), dtype=np.float64)
for tree in self.estimators_:
total_sum += tree.feature_importances_
importances = total_sum / len(self.estimators_)
return importances
Отже, на словах, підсумуйте особливості імпорту окремих дерев, а потім розділіть на загальну кількість дерев . Залишається побачити, як обчислити імпорт функцій для одного дерева.
Розрахунок важливості дерева реалізується на рівні цитону , але він все ще може бути виконаний. Ось очищена версія коду
cpdef compute_feature_importances(self, normalize=True):
"""Computes the importance of each feature (aka variable)."""
while node != end_node:
if node.left_child != _TREE_LEAF:
# ... and node.right_child != _TREE_LEAF:
left = &nodes[node.left_child]
right = &nodes[node.right_child]
importance_data[node.feature] += (
node.weighted_n_node_samples * node.impurity -
left.weighted_n_node_samples * left.impurity -
right.weighted_n_node_samples * right.impurity)
node += 1
importances /= nodes[0].weighted_n_node_samples
return importances
Це досить просто. Ітерація через вузли дерева. Поки ви не знаходитесь у листовому вузлі, обчисліть зважене зменшення чистоти вузла від розколу на цьому вузлі та віднесіть його до функції, яка була розділена на
importance_data[node.feature] += (
node.weighted_n_node_samples * node.impurity -
left.weighted_n_node_samples * left.impurity -
right.weighted_n_node_samples * right.impurity)
Потім, по закінченні, розділіть це все на загальну вагу даних (у більшості випадків - кількість спостережень)
importances /= nodes[0].weighted_n_node_samples
Варто нагадати, що домішка - це загальна назва для метрики, яку слід використовувати при визначенні того, що потрібно розщепити під час вирощування дерева. У цьому світлі ми просто підсумовуємо, скільки розщеплення на кожну функцію дозволило нам зменшити домішки у всіх розщепленнях дерева.
В умовах збільшення градієнта ці дерева завжди регресують (мінімізуючи помилку в квадраті), підходять до градієнта функції втрат.