Ансамблевые методы.

Понижение размерности данных.

Шестаков А.В. Майнор по анализу данных 10/05/2016

1) Ансамблевые методы (Ensembles)

Не обращаясь к формулам, констатируем, что ошибку модели можно выразить через 3 компоненты: $\text{Error} = \text{Bias}^2 + \text{Variance} + \text{Noise}$

  • $\text{Bias}$ (Смещение) - точность модели. Высокое смещение чаще всего означает, что модель недообучена (underfitting).
  • $\text{Variance}$ (Разброс) - чувствительность модели к данным. Высокие разброс чаще всего означает, что модель переобучена (overfitting)
  • $\text{Noise}$ (Шум) - это просто шум.

Для иллюстрации рассмотрим следующую картинку

На эти компоненты можно влиять несколькими способами, например, подбирая гиперпараметры моделей.
Вопрос: Как меняются Bias и Variance если повышать глубину дерева решений?

А еще, можно строить комбинации (ансамбли) моделей!

На лекции в кратце были рассмотрены два способа\алгоритма построения ансамблей.

Bagging

Bagging - это параллельный способ построения ансамбля.

  1. Обучающая выборка сэмплируется $k$ раз с помощью bootstrap'a (выборка с возвратом)
  2. На каждом сэмпле обучается отдельная базовая модель
  3. Ответы моделей усредняются (возможно с весом)

Теоретически, такой подход должен уменьшать Variance составляющую ошибки.

Самый известный представитель этого метода - модель случайного леса (RandomForest). В данном случае, на каждом сэмпле базовой моделью является дерево решений.
Если вам нужно за минимальное время построить достаточно точную и устойчивую модель - это ваш вариант.

Вопрос: Какая доля объектов в среднем попадает в один bootstrap сэмпл?

Немного интуиции

Классификация

In [ ]:
import pandas as pd
import numpy as np

from sklearn.datasets import make_circles
from sklearn.datasets import make_moons
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import roc_curve
from sklearn.cross_validation import train_test_split
import matplotlib.pyplot as plt

plt.style.use('ggplot')

%matplotlib inline
In [ ]:
X, y = make_circles(n_samples=500, factor=0.1, noise=0.35, random_state=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

plt.scatter(X_train[:,0], X_train[:,1], c=y_train)
In [ ]:
dtree = DecisionTreeClassifier(random_state=1)

dtree.fit(X_train, y_train)
In [ ]:
# Копипаст с предыдущего семинара

x_range = np.linspace(X.min(), X.max(), 100)

xx1, xx2 = np.meshgrid(x_range, x_range)
y_hat = dtree.predict(np.c_[xx1.ravel(), xx2.ravel()])
y_hat = y_hat.reshape(xx1.shape)

plt.contourf(xx1, xx2, y_hat, alpha=0.2)
plt.scatter(X[:,0], X[:,1], c=y)
In [ ]:
# Выведем распределение выроятностей предсказаний
# Your code here
In [ ]:
# Теперь попробуем Случайный лес
# Your code here
rf = RandomForestClassifier()
In [ ]:
# Рисуем предсказания

x_range = np.linspace(X.min(), X.max(), 100)

xx1, xx2 = np.meshgrid(x_range, x_range)
y_hat = rf.predict(np.c_[xx1.ravel(), xx2.ravel()])
y_hat = y_hat.reshape(xx1.shape)

plt.contourf(xx1, xx2, y_hat, alpha=0.2)
plt.scatter(X[:,0], X[:,1], c=y)
In [ ]:
# Распределение вероятнсстей
# Your code here
In [ ]:
# Посмотрим, из чего складываются предсказания

for tree in rf.estimators_:
    y_hat = tree.predict(np.c_[xx1.ravel(), xx2.ravel()])
    y_hat = y_hat.reshape(xx1.shape)

    plt.contourf(xx1, xx2, y_hat, alpha=0.02)
plt.scatter(X[:,0], X[:,1], c=y)
In [ ]:
# Сравните roc-кривые для дерева и леса на тесте
# Your code here

Регрессия

In [ ]:
X = np.random.uniform(1, 100, 500)

y = np.log(X) + np.random.normal(0, .3, 500)
plt.scatter(X, y)
In [ ]:
# Обучите модель, изобразите индивидуальные предсказания деревьев
# И устредненное предсказание леса

plt.scatter(X, y)
rf = RandomForestRegressor(n_estimators=10)
rf.fit(X.reshape(-1,1), y)
x_range = np.linspace(X.min(), X.max(), 100)

# Your code here

Boosting

Boosting - это последовательный способ построения ансамбля.
Мы постоянно работаем с одним и тем же набором данных, но на каждом шаге строим новую базовую модель, которая учитывает ошибки предыдущей модели.

Важное ограничение накладывается на базовые модели: они должны быть НЕМНОГО лучше, чем подбрасывание монетки (weak models). Иначе нас ждет мгновенный overfitting.

Теоретически, такой подход должен уменьшать Bias составляющую ошибки.

Вновь интуиция

Классификация

In [ ]:
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingRegressor
In [ ]:
X, y = make_moons(noise=0.1)
plt.scatter(X[:, 0], X[:, 1], c=y)
In [ ]:
# Обучаем градиентный бустинг на деревьях

gbt = GradientBoostingClassifier(n_estimators=12, max_depth=2, learning_rate=0.3, subsample=1)
gbt.fit(X, y)
In [ ]:
# Cмотрим, как изменяется предсказания с каждым новым деревом

fig, ax = plt.subplots(4,3, figsize=(15,15))
ax = ax.ravel()

xx1, xx2 = np.meshgrid(np.arange(-1.5, 2.5, 0.1),
                       np.arange(-1, 1.5, 0.1))

yy = gbt.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 )
    ax[i].contourf(xx1, xx2, y_hat, cmap=plt.cm.Paired)
    ax[i].scatter(X[:, 0], X[:, 1], c=y)

Вопрос: Какие недостатки\преимущества есть у ансамблевых методов?

2) Методы понижения размерности данных.

Большое количество признаков в данных - не всегда хорошо.

  • Проклятие размерности!
  • В признаках может быть шум, а не хотим использовать шумовые взаимосвязи между признаками и прогнозируемой величиной
  • Мультиколлинеарность
  • Далеко не все признаки вносят весомый вклад в предсказание, но если и дальше их "тащить", то это может повлиять на качество
  • Неудобно смотреть на данные

Избавляться от размерности можно методами отбора признаков (Feature Selection) и методами уменьшения разрмености (Feature Reduction)

Feature Selection

Методы деляться на три группы:

  • Filter methods
    • Признаки рассматриваются независимо друг от друга
    • Изучается индивидуальный "вклад" призника в предсказываемую переменную
    • Быстрое вычисление
    • Пример?
  • Wrapper methods
    • Идет отбор группы признаков
    • Может быть оооочень медленным, но качество, обычно, лучше чем у Filter Methods
    • Stepwise feature selection for regression
  • Embedded methods
    • Отбор признаков "зашит" в модель
    • Пример?

Filter method - Mutual Information

$$MI(y,x) = \sum_{x,y} p(x,y) \ln[\frac{p(x,y)}{p(x)p(y)}]$$

Сколько информации $x$ сообщает об $y$. $$NormalizedMI(y,x) = \frac{MI(y,x)}{H(y)}$$

In [ ]:
from sklearn.metrics import normalized_mutual_info_score as nmi
In [ ]:
# Размеберем некоторые примеры

Вопрос: А что делать если у нас не категориальные а вещественные признаки?

Wrapper Methods - Recursive Feature Elimination

При данном подходе из модели последовательно удаляются признаки с наименьшим коэффициентом

In [ ]:
from sklearn.datasets import make_regression
from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
In [ ]:
X, y = make_regression(n_samples=500, n_features=4, n_informative=1, 
                       n_targets=1, tail_strength=0.5, noise=1.0, coef=False, random_state=None)
In [ ]:
plt.scatter(y, X[:,1])
In [ ]:
model = LinearRegression()
rfe = RFE(model, n_features_to_select=1, verbose=1)

rfe.fit(X,y)
In [ ]:
rfe.support_

Feature Reduction

Методы Feature Reduction производят преобразования признакового пространства, при этом пытаясь сохранить какие-то свойства данных.

PCA (Principal Component Analysis) - делаем такое линейное преобразование признаков, чтобы каждая следующая комплнента содержала в себе наибольшую изменчивость в данных.

Мы уже делали PCA на семинарах (в самом начале, где еще было задание с лицами). Повторимся..

In [ ]:
from sklearn.decomposition import PCA
from sklearn.datasets import load_digits
In [ ]:
digits = load_digits()
X = digits.images
y = digits.target
In [ ]:
plt.imshow(X[2,:], cmap='Greys', interpolation='none')
In [ ]:
# Перейдем от изображений к матрице объект-признак
# И сделаем PCA в помощью готовой функции в sklearn и SVD
In [ ]: