Не обращаясь к формулам, констатируем, что ошибку модели можно выразить через 3 компоненты: $\text{Error} = \text{Bias}^2 + \text{Variance} + \text{Noise}$
Для иллюстрации рассмотрим следующую картинку
На эти компоненты можно влиять несколькими способами, например, подбирая гиперпараметры моделей.
Вопрос: Как меняются Bias и Variance если повышать глубину дерева решений?
А еще, можно строить комбинации (ансамбли) моделей!
На лекции в кратце были рассмотрены два способа\алгоритма построения ансамблей.
Bagging - это параллельный способ построения ансамбля.
Теоретически, такой подход должен уменьшать Variance составляющую ошибки.
Самый известный представитель этого метода - модель случайного леса (RandomForest). В данном случае, на каждом сэмпле базовой моделью является дерево решений.
Если вам нужно за минимальное время построить достаточно точную и устойчивую модель - это ваш вариант.
Вопрос: Какая доля объектов в среднем попадает в один bootstrap сэмпл?
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
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)
dtree = DecisionTreeClassifier(random_state=1)
dtree.fit(X_train, y_train)
# Копипаст с предыдущего семинара
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)
# Выведем распределение выроятностей предсказаний
# Your code here
# Теперь попробуем Случайный лес
# Your code here
rf = RandomForestClassifier()
# Рисуем предсказания
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)
# Распределение вероятнсстей
# Your code here
# Посмотрим, из чего складываются предсказания
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)
# Сравните roc-кривые для дерева и леса на тесте
# Your code here
X = np.random.uniform(1, 100, 500)
y = np.log(X) + np.random.normal(0, .3, 500)
plt.scatter(X, y)
# Обучите модель, изобразите индивидуальные предсказания деревьев
# И устредненное предсказание леса
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 - это последовательный способ построения ансамбля.
Мы постоянно работаем с одним и тем же набором данных, но на каждом шаге строим новую базовую модель, которая учитывает ошибки предыдущей модели.
Важное ограничение накладывается на базовые модели: они должны быть НЕМНОГО лучше, чем подбрасывание монетки (weak models). Иначе нас ждет мгновенный overfitting.
Теоретически, такой подход должен уменьшать Bias составляющую ошибки.
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingRegressor
X, y = make_moons(noise=0.1)
plt.scatter(X[:, 0], X[:, 1], c=y)
# Обучаем градиентный бустинг на деревьях
gbt = GradientBoostingClassifier(n_estimators=12, max_depth=2, learning_rate=0.3, subsample=1)
gbt.fit(X, y)
# 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)
Вопрос: Какие недостатки\преимущества есть у ансамблевых методов?
Большое количество признаков в данных - не всегда хорошо.
Избавляться от размерности можно методами отбора признаков (Feature Selection) и методами уменьшения разрмености (Feature Reduction)
Методы деляться на три группы:
Сколько информации $x$ сообщает об $y$. $$NormalizedMI(y,x) = \frac{MI(y,x)}{H(y)}$$
from sklearn.metrics import normalized_mutual_info_score as nmi
# Размеберем некоторые примеры
Вопрос: А что делать если у нас не категориальные а вещественные признаки?
При данном подходе из модели последовательно удаляются признаки с наименьшим коэффициентом
from sklearn.datasets import make_regression
from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
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)
plt.scatter(y, X[:,1])
model = LinearRegression()
rfe = RFE(model, n_features_to_select=1, verbose=1)
rfe.fit(X,y)
rfe.support_
Методы Feature Reduction производят преобразования признакового пространства, при этом пытаясь сохранить какие-то свойства данных.
PCA (Principal Component Analysis) - делаем такое линейное преобразование признаков, чтобы каждая следующая комплнента содержала в себе наибольшую изменчивость в данных.
Мы уже делали PCA на семинарах (в самом начале, где еще было задание с лицами). Повторимся..
from sklearn.decomposition import PCA
from sklearn.datasets import load_digits
digits = load_digits()
X = digits.images
y = digits.target
plt.imshow(X[2,:], cmap='Greys', interpolation='none')
# Перейдем от изображений к матрице объект-признак
# И сделаем PCA в помощью готовой функции в sklearn и SVD