Простая агрегация в ансамблях
При борьбе с переобучением (overfitting) базовых моделей используются простые агрегирующие функции . Рассмотрим основные типы таких агрегаций для задач регрессии и классификации.
Регрессия
При решении задачи регрессии прогнозы базовых алгоритмов можно усреднять:
Как вариант, среднее можно заменить на вычисление медианы прогнозов. Это имеет смысл при наличии выбросов в данных и использовании неустойчивых к выбросам моделей. Тогда, даже если одна из моделей начнёт выдавать аномально низкий или высокий прогноз, это не повлияет на итоговый прогноз ансамблем.
Другим и гораздо более часто используемым вариантом усреднения является взвешеннное среднее:
Взвешенное среднее лучше, когда базовые модели сильно различаются по точности. В этом случае целесообразно задать больший вес более точной модели.
При взвешенном усреднении веса могут быть не фиксированными константами, а функциями, зависящими от вектора признаков:
Например, веса могут считаться как SoftMax преобразование от линейных функций от признаков. В этом случае разные модел и будут более предпочтительными в разных участках признакового пространства. Этот подход называется смесь экспертов (mixture of experts).
Классификация
При агрегации прогнозов классификаторов необходимо различать три случая: когда классификатора выдают вероятности классов, метки классов и рейтинги классов (дискриминантные функции).
Классификаторы выдают вероятности классов
В этом случае каждый классификатор выдаст вектор вероятностей классов, которые мы можем усреднить в качестве предсказанного распределения классов всем ансамблем.
Вероятность класса в этом случае будет средним по предсказанным вероятностям этого класса для всех классификаторов:
Аналогично регрессии, равномерное усреднение можно заменить взвешенным.
Классификаторы выдают метки классов
В этом случае в качестве агрегирующей функции можно взять голосование по большинству (majority vote), то есть назначать тот класс, за который проголосовало большинство базовых классификаторов.
Если классификаторы неравнозначны между собой (за счёт сильных различий в точности), то лучше использовать взвешенное голосование, при котором голоса более точных классификаторов учитываются с большим весом.
Бинарная классификация
Когда классов всего два, то можно предсказывать положительный класс, когда этот класс предсказывают
-
все базовые классификаторы (правило AND),
-
хотя бы один из классификаторов (правило OR),
-
по крайней мере K классификаторов (правило K-out-of-N).
Последнее правило обобщает стратегии AND и OR при K=N и K=1 соответственно. Правило OR используется при обнаружении аномалий отдельными классификаторами: если хотя бы один базовый классификатор увидел что-то необычное в объекте, то он считается аномалией.
Классификаторы выдают рейтинги
Как мы знаем, каждый классификатор внутри себя рассчитывает рейтинги классов при построении прогноза. Соответственно, можно извлекать не окончательные метки, а вектора рейтингов классов для соответствующих базовых классификаторов , после чего их усреднять:
получая вектор рейтингов для ансамбля:
Далее, как обычно, прогнозируется класс, обладающий максимальным рейтингом:
Этот подход работает только для классификаторов из одного семейства, у которых рейтинги изменяются в одной шкале. Для моделей разных типов так делать нельзя, поскольку тогда ранжирование будет доминироваться классификатором с рейтингами, принимающими максимально широкий диапазон значений.
Корректировка для классификаторов разных типов
Классификаторы разных типов выдают рейтинги в разном диапазоне, поэтому усреднять их нельзя, как показано в примере ниже:
100 | 70 | 34 | -25 | |
15 | 0 | -14 | -10 | |
0.05 | 0.6 | 0.2 | 0.15 |
Рейтинги первой модели имеют максимальный разброс значений, поэтому при усреднении именно они будут в основном определять прогноз. Чтобы такого не происходило, перед усреднением рейтингов их необходимо привести к единой шкале.
Для этого применяется ранговое преобразование, в котором новый рейтинг класса считается как количество других классов, которые рассматриваемый класс доминирует (принимая более высокое значение дискриминантной функции). Преобразованный рейтинг называется рейтингом Бриера (Brier score).
В примере выше модели ранжируют классы следующим образом:
модель | ранжирование классов |
---|---|
Поэтому рейтинги Бриера будут:
3 | 2 | 1 | 0 | |
3 | 2 | 0 | 1 | |
0 | 3 | 2 | 1 |
Рейтинги Бриера принадлежат уже одинаковой шкале значений поэтому их можно усреднять. В результате усреднения получим следующие рейтинги для ансамбля:
6/3 | 7/3 | 3/6 | 2/6 |
Далее, как обычно, назначается класс с максимальным средним рейтингом. Это будет класс 2. Заметим, что прогноз отличается от прогноза голосованием по большинству, когда был назначен класс 1. Класс 2 победил новым способом, поскольку класс 2 считается вероятным всеми тремя классификаторами, а класс 1 считается самым маловероятным третьей моделью .
Как и раньше, если классификаторы сильно различаются по точности, их рейтинги Бриера можно усреднять не равномерно, а с весами, назначая более высокий вес более точным классификаторам.
Пример запуска в Python
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import brier_score_loss
X_train, X_test, Y_train, Y_test = get_demo_classification_data()
# Инициализируем базовые модели и проверим их качество
log_model = LogisticRegression() # инициализация модели
log_model.fit(X_train, Y_train) # обучение модели
Y_hat = log_model.predict(X_test) # построение прогнозов
print(f'Точность LogisticRegression: {100*accuracy_score(Y_test, Y_hat):.1f}%')
tree_model = DecisionTreeClassifier() # инициализация дерева
tree_model.fit(X_train, Y_train) # обучение модели
Y_hat = tree_model.predict(X_test) # построение прогнозов
print(f'Точность DecisionTree: {100*accuracy_score(Y_test, Y_hat):.1f}%')
# Инициализируем ансамбль, усредняющий метки классов
ensemble = VotingClassifier(estimators=[('logistic regression', log_model),
('decision tree', tree_model)],
voting='hard', # усредняем метки классов
weights=[0.5,0.5]) # веса учёта базовых моделей
ensemble.fit(X_train, Y_train) # обучение базовых моделей ансамбля
Y_hat = ensemble.predict(X_test) # построение прогнозов
print(f'Точность VotingClassifier: {100*accuracy_score(Y_test, Y_hat):.1f}%')
# Инициализируем ансамбль, усредняющий вероятности классов
ensemble = VotingClassifier(estimators=[('logistic regression', log_model),
('decision tree', tree_model)],
voting='soft', # усредняем вероятности классов
weights=[0.5,0.5]) # веса учёта базовых моделей
ensemble.fit(X_train, Y_train) # обучение базовых моделей ансамбля
Y_hat = ensemble.predict(X_test) # построение прогнозов
print(f'Точность VotingClassifier: {100*accuracy_score(Y_test, Y_hat):.1f}%')
P_hat = ensemble.predict_proba(X_test) # можно предсказывать вероятности классов
loss = brier_score_loss(Y_test, P_hat[:,1]) # мера Бриера на вероятности положительного класса
print(f'Мера Бриера ошибки прогноза вероятностей: {loss:.2f}')
Больше информации. Полный код.
from sklearn.neighbors import KNeighborsRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import VotingRegressor
from sklearn.metrics import mean_absolute_error
X_train, X_test, Y_train, Y_test = get_demo_classification_data()
# Инициализируем базовые модели и проверим их качество
knn = KNeighborsRegressor(n_neighbors=100) # инициализация модели
log_model.fit(X_train, Y_train) # обучение модели
Y_hat = log_model.predict(X_test) # построение прогнозов
print(f'Средний модуль ошибки (MAE): {mean_absolute_error(Y_test, Y_hat):.2f}')
tree_model = DecisionTreeRegressor() # инициализация дерева
tree_model.fit(X_train, Y_train) # обучение модели
Y_hat = tree_model.predict(X_test) # построение прогнозов
print(f'Средний модуль ошибки (MAE): {mean_absolute_error(Y_test, Y_hat):.2f}')
# Инициализируем усредняющий ансамбль
ensemble = VotingRegressor(estimators=[('K nearest neighbours', knn),
('decision tree', tree_model)],
weights=[0.5,0.5]) # веса учёта базовых моделей
ensemble.fit(X_train, Y_train) # обучение базовых моделей ансамбля
Y_hat = ensemble.predict(X_test) # построение прогнозов
print(f'Средний модуль ошибки (MAE): {mean_absolute_error(Y_test, Y_hat):.2f}')