Phystech@DataScience¶

Валидация и метрики качества¶

In [ ]:
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')
In [ ]:
# Bot check

# HW_ID: phds_sem5
# Бот проверит этот ID и предупредит, если случайно сдать что-то не то.

# Status: not final
# Перед отправкой в финальном решении удали "not" в строчке выше.
# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную.
# Никакие значения в этой ячейке не влияют на факт сдачи работы.

1. Cкачаем датасет о предсказании пульсаров.¶

Пульсары — это космические объекты, излучающие в различных диапазонах длины волны. Согласно современным астрофизическим теориям, пульсары представляют собой вращающиеся нейтронные звезды, обладающие магнитным полем, наклоненным относительно оси вращения.

Пульсары представляют значительный научный интерес. Поэтому есть потребность в автоматической идентификации интересных объектов для ускорения процесса изучения. На практике почти все обнаружения вызваны радиочастотными помехами и шумом, что затрудняет поиск достоверных сигналов. В последнее время для автоматического обнаружения пульсаров начали применять машинное обучение. Актуальная задача сегодня — бинарная классификация объектов-кандидатов на предмет того, являются ли они пульсарами.

В используемом датасете есть как примеры ложных обнаружений, так и примеры реальных пульсаров, подтвержденные учеными. Данные получены в результате The High Time Resolution Universe Pulsar Survey I.

Мы будем работать только с файлом pulsar_data_train.csv.

In [ ]:
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. Для него можно посчитать следующие величины:

  1. Выборочное среднее $\bar p = \frac{1}{n}\sum \limits_{i=1}^n p_i$;
  2. Выборочное стандартное отклонение $\sqrt{\frac{1}{n}\sum \limits_{i=1}^n (p_i - \bar p)^2}$;
  3. Выборочный коэффициент асимметрии (skewness);
  4. Выборочный коэффициент эксцесса (kurtosis).

Для $D = (d_1, \ldots d_n)$, массива значений DM-SNR, аналогично.

Именно эти значения по массивам $P$ и $D$ являются признаками в данном датасете.

Про коэффициенты эксцесса и асимметрии можно посмотреть здесь и здесь, формулы для выборочных коэффициентов асимметрии и эксцесса можно найти в английской версии этих статей.

Статья с подробным описанием процесса генерации данных.

In [ ]:
data.head()
In [ ]:
data.info()

2. Предобработаем данные.¶

В этом датасете в некоторых столбцах есть пропуски в данных. Об этом говорит то, что значение Non-Null Count в таблице выше не равно количеству строк для некоторых столбцов. Чтобы не усложнять себе жизнь, пока просто не будем брать эти столбцы для анализа.

Посчитаем число пропусков в каждой колонке:

In [ ]:
data.isna().sum()

Удалите все соответствующие строчки:

In [ ]:
data = <...>

Теперь возьмем только некоторые столбцы в качестве независимых переменных и выделим зависимую. Столбец target_class отвечает за целевую переменную — индикатор того, является ли объект пульсаром.

In [ ]:
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. Графики¶

Проведем визуальный анализ данных. Построим оценки плотности по каждому признаку отдельно для каждого класса. Какие признаки лучше всего разделяют классы? Как это отразится на результатах модели?

In [ ]:
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.

In [ ]:
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, список названий доступных метрик приведен ниже).

Объясните каждый свой выбор.

In [ ]:
sklearn.metrics.get_scorer_names()

Ответ:

In [ ]:
# словарь параметров
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'
# )
In [ ]:
%%time
# выполняем поиск по сетке
# обучаем, конечно, не тренировочной части данных
search.fit(X_train, y_train)

Итак, мы выполнили поиск гиперпараметров.

Выведите значения гиперпараметров лучшей модели:

In [ ]:
<...>

Выведите время обучения лучшей модели:

In [ ]:
<...>

Визуализируйте лучшее дерево с помощью sklearn.tree.plot_tree(), что можно о нем сказать?

In [ ]:
best_tree = <...>

Какое значение оптимизируемой метрики получилось достичь? Выведите значение соответствующего атрибута выбранного вами алгоритма поиска

In [ ]:
<...>

Посчитайте precision, recall, f1-score, ROC-AUC на тестовой выборке при использовании лучшей модели. Что можно сказать о полученных результатах?

In [ ]:
<...>

Выводы:

In [ ]: