Phystech@DataScience¶
Валидация и метрики качества¶
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sklearn
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold,\
ShuffleSplit,\
StratifiedKFold,\
StratifiedShuffleSplit,\
GroupKFold,\
GroupShuffleSplit
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
import seaborn as sns
sns.set_theme(style='dark', font_scale=1.3)
import warnings
warnings.filterwarnings('ignore')
# Bot check
# HW_ID: phds_sem5
# Бот проверит этот ID и предупредит, если случайно сдать что-то не то.
# Status: not final
# Перед отправкой в финальном решении удали "not" в строчке выше.
# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную.
# Никакие значения в этой ячейке не влияют на факт сдачи работы.
Пульсары — это космические объекты, излучающие в различных диапазонах длины волны. Согласно современным астрофизическим теориям, пульсары представляют собой вращающиеся нейтронные звезды, обладающие магнитным полем, наклоненным относительно оси вращения.
Пульсары представляют значительный научный интерес. Поэтому есть потребность в автоматической идентификации интересных объектов для ускорения процесса изучения. На практике почти все обнаружения вызваны радиочастотными помехами и шумом, что затрудняет поиск достоверных сигналов. В последнее время для автоматического обнаружения пульсаров начали применять машинное обучение. Актуальная задача сегодня — бинарная классификация объектов-кандидатов на предмет того, являются ли они пульсарами.
В используемом датасете есть как примеры ложных обнаружений, так и примеры реальных пульсаров, подтвержденные учеными. Данные получены в результате The High Time Resolution Universe Pulsar Survey I.
Мы будем работать только с файлом pulsar_data_train.csv
.
data = pd.read_csv("pulsar_data_train.csv")
Сигнал от пульсаров представляет собой периодические импульсы.
Для описания объекта используется integrated profile (интегральный профиль) — агрегирование сигнала за разные периоды вращения (ссылка, слайд 11). У всех пульсаров разные интегральные профили, и обычно эта характеристика мало меняется во времени. В данном датасете интегральный профиль также усреднен по частоте.
Кроме того, импульс приходит в разное время на разных частотах. Задержка от частоты к частоте вызвана наличием ионизированной межзвездной среды и называется дисперсией. Не путать с дисперсией в теории вероятностей.
С дисперсией связана еще одна характеристика объекта — DM-SNR кривая. Подробнее о ней можно почитать в специализированных материалах.
Обе характеристики, integrated profile и DM-SNR кривая, представляют собой одномерные функции. При создании датасета значения функций были посчитаны в конечном количестве $n$ точек. Фактически, были получены реализации выборки двух случайных величин.
Пусть $P = (p_1, \ldots p_n)$ — массив значений integrated profile. Для него можно посчитать следующие величины:
- Выборочное среднее $\bar p = \frac{1}{n}\sum \limits_{i=1}^n p_i$;
- Выборочное стандартное отклонение $\sqrt{\frac{1}{n}\sum \limits_{i=1}^n (p_i - \bar p)^2}$;
- Выборочный коэффициент асимметрии (skewness);
- Выборочный коэффициент эксцесса (kurtosis).
Для $D = (d_1, \ldots d_n)$, массива значений DM-SNR, аналогично.
Именно эти значения по массивам $P$ и $D$ являются признаками в данном датасете.
Про коэффициенты эксцесса и асимметрии можно посмотреть здесь и здесь, формулы для выборочных коэффициентов асимметрии и эксцесса можно найти в английской версии этих статей.
Статья с подробным описанием процесса генерации данных.
data.head()
data.info()
2. Предобработаем данные.¶
В этом датасете в некоторых столбцах есть пропуски в данных. Об этом говорит то, что значение Non-Null Count в таблице выше не равно количеству строк для некоторых столбцов. Чтобы не усложнять себе жизнь, пока просто не будем брать эти столбцы для анализа.
Посчитаем число пропусков в каждой колонке:
data.isna().sum()
Удалите все соответствующие строчки:
data = <...>
Теперь возьмем только некоторые столбцы в качестве независимых переменных и выделим зависимую. Столбец target_class
отвечает за целевую переменную — индикатор того, является ли объект пульсаром.
needed_columns = [' Mean of the integrated profile',
' Standard deviation of the integrated profile',
' Skewness of the integrated profile', ' Mean of the DM-SNR curve',
' Excess kurtosis of the DM-SNR curve'] # используем только эти столбцы
X = data[needed_columns]
y = data["target_class"]
y.value_counts()
3. Графики¶
Проведем визуальный анализ данных. Построим оценки плотности по каждому признаку отдельно для каждого класса. Какие признаки лучше всего разделяют классы? Как это отразится на результатах модели?
sns.set_theme(font_scale=0.8)
plot = sns.PairGrid(data, x_vars=needed_columns, y_vars=needed_columns,
hue="target_class", diag_sharey=False)
plot.map_diag(sns.kdeplot)
plot.map_lower(sns.scatterplot, alpha=0.2)
plot.add_legend();
Ответ:
4. Разделим датасет¶
Случайно разделим выборку на обучающую и тестовую части в соотношении 4:1.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train.shape, X_test.shape, y_train.shape, y_test.shape
5. Подберем оптимальные гиперпараметры¶
Будем подбирать гиперпараметры для модели решающего дерева.
Задайте сетку гиперпараметров (определите, какие гиперпараметры вам нужны на основании материалов предыдущего занятия) для перебора.
Выберите стратегию кросс-валидации.
Выберите метод поиска оптимальных гиперпараметров —
GridSearchCV
илиRandomizedSearchCV
— раскомментируйте нужный блок кода.Выберите оптимизируемую метрику (параметр
scoring
вGridSearchCV
,RandomizedSearchCV
, список названий доступных метрик приведен ниже).
Объясните каждый свой выбор.
sklearn.metrics.get_scorer_names()
Ответ:
# словарь параметров
parameters_grid = {
<...>
}
# задаем стратегию кросс-валидации
cv_strategy = <...>
# задаем имя метрики для максимизации, str
scorer_name = <...>
# задаем модель
model = DecisionTreeClassifier()
# # определяем поиск по сетке
# search = RandomizedSearchCV(
# # модель для обучения
# estimator=model,
# # сетка значений гиперпараметров
# param_distributions=parameters_grid,
# # сколько комбинаций признаков будет проверено
# n_iter=<...>
# # метрика качества, можно задать строкой
# scoring=scorer_name,
# # GridSearchCV, RandomizedSearchCV отлично параллелятся на несколько ядер
# # n_jobs=-1 означает, что мы используем все доступные ядра
# n_jobs=-1,
# # стратегия кросс-валидации
# cv=cv_strategy,
# # сообщения с логами обучения: больше значение - больше сообщений
# verbose=10,
# # значение, присваиваемое scorer в случае ошибки при обучении
# error_score='raise'
# )
# # определяем поиск по сетке
# search = GridSearchCV(
# # модель для обучения
# estimator=model,
# # сетка значений гиперпараметров
# param_grid=parameters_grid,
# # метрика качества, можно задать строкой
# scoring=scorer_name,
# # GridSearchCV, RandomizedSearchCV отлично параллелятся на несколько ядер
# # n_jobs=-1 означает, что мы используем все доступные ядра
# n_jobs=-1,
# # стратегия кросс-валидации
# cv=cv_strategy,
# # сообщения с логами обучения: больше значение - больше сообщений
# verbose=10,
# # значение, присваиваемое scorer в случае ошибки при обучении
# error_score='raise'
# )
%%time
# выполняем поиск по сетке
# обучаем, конечно, не тренировочной части данных
search.fit(X_train, y_train)
Итак, мы выполнили поиск гиперпараметров.
Выведите значения гиперпараметров лучшей модели:
<...>
Выведите время обучения лучшей модели:
<...>
Визуализируйте лучшее дерево с помощью sklearn.tree.plot_tree()
, что можно о нем сказать?
best_tree = <...>
Какое значение оптимизируемой метрики получилось достичь? Выведите значение соответствующего атрибута выбранного вами алгоритма поиска
<...>
Посчитайте precision, recall, f1-score, ROC-AUC на тестовой выборке при использовании лучшей модели. Что можно сказать о полученных результатах?
<...>
Выводы: