import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = (8, 8)
# Для кириллицы на графиках
font = {'family': 'Verdana',
'weight': 'normal'}
plt.rc('font', **font)
try:
from ipywidgets import interact, IntSlider, fixed, FloatSlider
except ImportError:
print u'Так надо'
Boosting, в отличие от bagging'а - это последовательный способ построения композиции базовых моделей.
Мы постоянно работаем с одним и тем же набором данных, но на каждом шаге строим новую базовую модель, которая учитывает ошибки предыдущей модели.<br>По большому счету, бустинг-алгоритмы отличаются лишь тем, как в них заложен учет этих самых ошибок.
Например в методе AdaBoost каждому объекту присваивается вес, который изменяется в зависимости от того, ошиблась ли на нем очередная композиция базовых алгоритмов или нет. Так же веса имеются и у самих базовых моделей, которые штрафуют их за плохие предсказания. Для задачи классификации этот процесс можно проиллюстрировать следующим образом:
Введем следующие обозначения:
Конечное предсказание получается из взвешенной комбинации предсказания базовых моделей: $$ T(x^*) = sign(\sum\limits^{K}_{k=1}\alpha_kt_k(x^*)) $$
Наша цель - минимизировать количество ошибок на всей выборке ..
$$ E_T = \frac{1}{N}\sum\limits_{i=1}^N [T(x_i) \neq y_i] $$.. которые мы мажорируем экспонентой =)
$$ E_T = \frac{1}{N}\sum\limits_{i=1}^N [T(x_i) \neq y_i] \leq \frac{1}{N}\sum\limits_{i=1}^N e^{(-y_i\sum_k\alpha_kt_k(x_i))} $$Если мы посчитаем ошибки $E_1, E_2, E_3,...$ на каждом шаге, то это даст нам правило для обновления весов объектов. (Упражнение)<br> А если мы посчитаем ошибки производную $E_t$ по $\alpha_t$, то это даст нам правило для обновления весов базовых моделей. (Упражнение)<br>
Алгоритм обучения Discrete AdaBoost:
Можно вывести аналогичный алгоритм для задачи регрессии.
Обратите внимание что базовые модели как правило явлюятся слабыми (weak learners), т.е. их качество должно едва ли превышать бросание монетки. На рисунке выше - это логические правила на одном из признаков, что равносильно дереву решений с глубиной 1.
Раз все понятно, то перейдем к игрушечной практике
from sklearn import datasets
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
Рассмотрим такой набор данных
X = np.array([[-2, -1], [-2, 1], [2, -1], [2, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]])
y = np.array([-1,-1,-1,-1,1,1,1,1])
plt.scatter(X[:, 0], X[:, 1], c=y, s=500)
Вопрос: Cколько базовых классификаторов достаточно, чтобы правильно классифицировать эти данные?
Запомнили ответ, а теперь посмотрим
ada = AdaBoostClassifier(n_estimators=3, algorithm='SAMME',
base_estimator=DecisionTreeClassifier(max_depth=1))
ada.fit(X, y)
def plot_decision(model, rows=1, columns=3):
fig, ax = plt.subplots(nrows=rows, ncols=columns, figsize=(15,4))
ax = ax.ravel()
xx1, xx2 = np.meshgrid(np.arange(X[:,0].min()-1, X[:,0].max()+1, 0.1),
np.arange(X[:,1].min()-1, X[:,1].max()+1, 0.1))
yy = model.staged_predict(np.c_[xx1.ravel(), xx2.ravel()])
for i, y_hat in enumerate(yy):
y_hat = y_hat.reshape(xx1.shape)
ax[i].set_title('iteration %d' % (i+1))
ax[i].contourf(xx1, xx2, y_hat, cmap=plt.cm.Paired)
ax[i].scatter(X[:, 0], X[:, 1], c=y, s=300)
plot_decision(ada)
Ответ: 3! (для дискретного алгоритма) Почему?!<br> Для вещественного и правда достаточно двух
ada_real = AdaBoostClassifier(n_estimators=3, algorithm='SAMME.R',
base_estimator=DecisionTreeClassifier(max_depth=1))
ada_real.fit(X, y)
plot_decision(ada_real)
from sklearn.datasets import make_moons
def ada_demo(n_est=1):
ada = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1,), n_estimators=n_est, learning_rate=0.1)
ada.fit(X, y)
plt.figure(figsize=(7,5))
xx1, xx2 = np.meshgrid(np.arange(-1.5, 2.5, 0.1),
np.arange(-1, 1.5, 0.1))
y_hat = ada.predict(np.c_[xx1.ravel(), xx2.ravel()])
y_hat = y_hat.reshape(xx1.shape)
plt.title('iteration = %d' % n_est )
plt.contourf(xx1, xx2, y_hat, cmap=plt.cm.Paired)
plt.scatter(X[:, 0], X[:, 1], c=y)
plt.show()
X, y = make_moons(noise=0.1)
plt.figure(figsize=(7,5))
plt.scatter(X[:, 0], X[:, 1], c=y)
interact(ada_demo, n_est=IntSlider(min=1, max=150, value=1, step=1))
Основная идея градиентного бустинга заключается в том, что каждая следующая базовая модель настраивается на "остатки" предыдущих базовых моделей. По своей структуре метод похож на алгоритм градиентного спуска - отсюда и такое название.
По своей структуре метод похож на алгоритм градиентного спуска - отсюда и такое название.
Пусть дана дифференцируемая функция потерь $L(T_k(x), y)$ (для любой задачи - регрессии или классификации) <br> Функционал качества - $Q(T, y) = \sum_iL(T_k(x_i), y_i) = \sum_iL(T_{k-1}(x_i) + \alpha t_{k}(x_i), y_i)$
На секунду представим, что $t_{k}(x)$ - это просто вектор значений. Тогда задачу оптимизации $Q(T, y)$ можно решать простым градиентным методом:
Тогда $t_{k}(x) = \arg\min\limits_{t} \sum\limits_i(t_{k}(x_i) - g_i)^2$, $\alpha$ определяется в одномерное задаче оптимизации $ \sum_iL(T_{k-1}(x_i) + \alpha t_{k}(x_i), y_i) \rightarrow \min\limits_\alpha$
Наибольший успех, а потому и популярность, получил градиентный бустинг на деревьях решений. Именно с этой его реализацией мы сейчас поработаем
from sklearn.ensemble import GradientBoostingRegressor
def grad_demo(n_est=1):
X = np.random.uniform(1, 100, 500)
y = np.log(X) + np.random.normal(0, .3, 500)
plt.scatter(X, y)
gbr = GradientBoostingRegressor(n_estimators=n_est, learning_rate=0.15)
gbr_full = GradientBoostingRegressor(n_estimators=200, learning_rate=0.15)
gbr.fit(X.reshape(-1,1), y)
gbr_full.fit(X.reshape(-1,1), y)
x_range = np.linspace(X.min(), X.max(), 100).reshape((-1,1))
for y_hat in gbr.staged_predict(x_range):
plt.plot(x_range, y_hat, alpha=0.4, c='g')
y_hat = gbr_full.predict(x_range)
plt.title('Estimators %d' % n_est)
plt.plot(x_range, y_hat, c='r')
plt.show()
X = np.random.uniform(1, 100, 500)
y = np.log(X) + np.random.normal(0, .3, 500)
plt.scatter(X, y)
from sklearn.ensemble import GradientBoostingRegressor
# Обучите модель и изобразите предсказания на каждом шаге
# Посмотрите, как влияет скорость обучения learning_rate на предсказания
Один из самых важных параметров алгоритма бустинга является количество базовых моделей.<br> Слишком большое количество моделей может привести к переобучению, а слишком малое - к недообучению.<br> Как бы вы определяли оптимальное количество базовых моделей?
Рассмотрите следующий набор данных. По этой таблице предлагается построить модель, предсказывающую стоимоть дома в калифорнии по остальным прихнакам.
!head california.dat