Phystech@DataScience¶
Домашнее задание 2¶
Правила, прочитайте внимательно:
- Выполненную работу нужно отправить телеграм-боту
@thetahat_ds25_bot
. Для начала работы с ботом каждый раз отправляйте/start
. Дождитесь подтверждения от бота, что он принял файл. Если подтверждения нет, то что-то не так. Работы, присланные иным способом, не принимаются. - Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.
- Прислать нужно ноутбук в формате
ipynb
. Если вы строите интерактивные графики, их стоит прислать в формате html. - Следите за размером файлов. Бот не может принимать файлы весом более 20 Мб. Если файл получается больше, заранее разделите его на несколько.
- Выполнять задание необходимо полностью самостоятельно. При обнаружении списывания всем участникам списывания дается штраф -2 балла к итоговой оценке за семестр.
- Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.
- Обратите внимание на правила использования ИИ-инструментов при решении домашнего задания.
- Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.
- Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.
- Комментарии к решению пишите в markdown-ячейках.
- Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.
- Решение проверяется системой ИИ-проверки
ThetaGrader. Результат проверки валидируется и исправляется человеком, после чего комментарии отправляются студентам.
- Если код будет не понятен проверяющему, оценка может быть снижена.
- Никакой код из данного задания при проверке запускаться не будет. Если код студента не выполнен, недописан и т.д., то он не оценивается.
Важно!!! Правила заполнения ноутбука:
- Запрещается удалять имеющиеся в ноутбуке ячейки, менять местами положения задач.
- Сохраняйте естественный линейный порядок повествования в ноутбуке сверху-вниз.
- Отвечайте на вопросы, а также добавляйте новые ячейки в предложенных местах, которые обозначены
<...>
. - В markdown-ячейка, содержащих описание задачи, находятся специальные отметки, которые запрещается модифицировать.
- При нарушении данных правил работа может получить 0 баллов.
Перед выполнением задания посмотрите презентацию по выполнению и оформлению домашних заданий с занятия 2.
Баллы за задание:
- Задача 1 — 50 баллов
- Задача 2 — 50 баллов
- Задача 3 — 30 баллов
# Bot check
# HW_ID: phds_hw2
# Бот проверит этот ID и предупредит, если случайно сдать что-то не то
# Status: not final
# Перед отправкой в финальном решении удали "not" в строчке выше
# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, mean_squared_error, mean_absolute_error, mean_absolute_percentage_error, r2_score
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings("ignore")
import seaborn as sns
sns.set_theme(palette='Set2')
Легкая часть¶
data = pd.read_csv('breast_cancer_disbalances.csv')
data.head()
Проверьте, имеются ли в ваших данных пропуски. Если да, то удалите их.
...
Библиотека pandas
позволяет строить графики matplotlib
для своих объектов DataFrame
(подробнее). Посмотрим, как распределены значения признака Bare Nuclei
для разных классов:
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
data.groupby("Class")['Bare Nuclei'].hist(ax=axs[0], alpha=0.5)
data.groupby("Class")['Bare Nuclei'].plot(kind='kde', ax=axs[1])
axs[0].set_title('Гистограмма для Bare Nuclei', fontsize=20)
axs[1].set_title('KDE для Bare Nuclei', fontsize=20);
Чем отличаются способы построения ЯОП и гистограммы? Какую информацию о наших данных можно извлечь из каждого графика?
Ответ:
Постройте гистограммы и ядерные оценки плотности для всех признаков из датасета отдельно для каждого класса. Class
— целевая переменная. Можно это сделать, опираясь на код выше, а можно воспользоваться параметром hue
у функции sns.histplot
или другим методом, который вам нравится. Не забывайте подписывать, к чему относится каждый график.
Какие выводы вы можете сделать из полученных графиков?
Вывод: ...
Это не конец задачи! Переходите к пункту 2!
Профиль физика¶
Загрузите данные по бинарной классификации астероидов в зависимости от различных параметров с сайта.
Вашей целевой переменной будет являться столбец pha
. Более подробно ознакомить с датасетом вы можете также здесь. Можно заметить, что наш датасет сильно меньше по размерам, чем оригинал. Это сделано намеренно.
data = pd.read_csv('asteroid_cut.csv')
data.info()
Библиотека pandas
позволяет строить графики matplotlib
для своих объектов DataFrame
(подробнее). Посмотрим, как распределены значения признака rms
для разных классов:
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
data.groupby("pha")['rms'].hist(ax=axs[0], density=True)
data.groupby("pha")['rms'].plot(kind='kde', ax=axs[1])
axs[0].set_title('Гистограмма для H', fontsize=20)
axs[1].set_title('KDE для H', fontsize=20);
Чем отличаются способы построения ЯОП и гистограммы? Какую информацию о наших данных можно извлечь из каждого графика?
Ответ: ...
Постройте гистограммы и ядерные оценки плотности для указанных ниже признаков отдельно для каждого класса. pha
— целевая переменная. Можно это сделать, опираясь на код выше, а можно воспользоваться параметром hue
у функции sns.histplot
или другим методом, который вам нравится. Не забывайте подписывать, к чему относится каждый график.
features = ['epoch', 'ma', 'tp', 'rms']
...
Какие выводы вы можете сделать из полученный графиков?
Вывод: ...
2. Обучение модели¶
Продолжайте использовать выбранные вами данные.
Создайте массив признаков и массив таргета. Разбейте ваши данные на обучающую и тестовую выборки в отношении 7:3.
X = ...
y = ...
X_train, X_test, y_train, y_test = ...
Примените стандартизацию к обучающей и тестовой выборкам, используя класс StandardScaler
, натренированный на обучающей выборке.
...
Объясните, что делает StanderdScaler
и почему нельзя его нельзя обучать на тестовой выборке?
Ответ:
...
Обучите модель логистической регрессии.
...
Сделайте предсказание для тестовой выборки и оцените качества полученного предсказания, используя метрику accuracy_score
Если названия ваших классов отличаются от 0 и 1, то надо использовать pos_label
.
...
Можем ли порадоваться таким результатам? Вернемся к гистограммам и сделаем вывод, почему метрика оказалась такой большой.
Давайте посмотрим на распределение наших данных по целевой переменной по всему датасету, тренировочной и тестовой выборках.
original = <...>.value_counts() # Колонка таргета из изначального датасета
train = <...>.value_counts() # Колонка таргета из тренировочного датасета
test = <...>.value_counts() # Колонка таргета из тестового датасета
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
sns.barplot(x=original.index, y=original, ax=axes[0], palette=['blue'])
axes[0].set_title('Распределение классов в data')
axes[0].set_ylabel('Количество')
sns.barplot(x=train.index, y=train, ax=axes[1], palette=['green'])
axes[1].set_title('Распределение классов в train')
sns.barplot(x=test.index, y=test, ax=axes[2], palette=['orange'])
axes[2].set_title('Распределение классов в test')
plt.show()
Видно, что в данных есть сильный перекос — классы представлены неравномерно. Как и почему это может повлиять на наши результаты?
Ответ: ...
Есть много способов борьбы с этим. Можно искусственно сгенерировать данные нужного класса или урезать другой класс. Однако сегодня мы воспользуемся взвешенной логистической регрессией. Суть метода в том, что мы вручную поставим веса для классов, исходя из их предполагаемой природы: важность разных классов, цена ошибки в реальной жизни(например, что лучше, предсказать наличие рака, если он есть или нет?) и представленность данных.
Кросс-энтропия для взвешенной логистической регресси будет записана как:
$$ F(\theta) = \sum_{i=1}^{n} H(y_i, \widehat{y}_i) = -\sum_{i=1}^{n} w_{y_i} \left[ y_i \log(\widehat{y}_i) + (1-y_i) \log(1-\widehat{y}_i) \right] $$
где:
- $ y_i $ - истинный класс для образца $i$
- $ \widehat{y}_i = \sigma(\theta^Tx_i)$ - предсказанный класс для образца $i$
- $ w_{y_i} $ - вес класса
Давайте применим этот подход на практике. Воспользуемся параметром class_weight
. Для начала определим количество примеров каждого класса в обучающей выборке и вычислим их соотношение.
...
Применим рассчитанное соотношение классов в качестве весов:
threshold = ...
class_weights = {<класс_1>: threshold, <класс_2>: 1 - threshold}
# если использовать class_weights = 'balanced' модель сама подсчитает веса
weighted_model = LogisticRegression(class_weight=class_weights, random_state=0)
...
Посчитаем метрику accuracy_score
:
accuracy = ...
print(f"accuracy = {accuracy}")
Как изменилось качество нашей модели?
Сделайте общий вывод по задаче.
Вывод:
penguins = pd.read_csv('penguins.csv')
penguins.head()
⚠️ Warning: Лучше не нажимать на пингвинов
Здесь содержатся данные о морфололгии пингвинов трех различных видов: Adelie, Chinstrap, и Gentoo.
Оставшеися колонки: bill_length_mm
, bill_depth_mm
, flipper_length_mm
, body_mass_g
описывают морфологию каждого пингвина.
Как мы видим, в данном датасете присутствуют категориальные переменные: species
, island
и sex
, которые не могут быть оценены моделью линейной регресии. Закодируйте их: к переменным, где присутствует более трёх категорий, примените преобразование OneHotEncoder
. Обратите внимание на аргумент drop
.
cat_features = [...]
onehotencoder = ...
encoded = pd.DataFrame(onehotencoder.fit_transform(...), dtype=int)
penguins_encoded = penguins.drop(cat_features, axis=1).join(...)
penguins_encoded = penguins_encoded.rename(str, axis="columns")
penguins_encoded.head()
Выберем в качестве таргета вес пингвинов (body_mass_g
), и разделим нашу выборку на тренировочную и тестовую:
X = penguins_encoded.drop('body_mass_g', axis=1)
y = penguins_encoded['body_mass_g']
X_train, X_test, y_train, y_test = ...
Реализуйте линейную регрессию самостоятельно, используя формулы с лекции.
Вам нужно только заполнить прочерки в методах fit
и predict
.
class MyLinearRegression:
"""
Класс, реализующий линейную регрессию c помощью МНК.
"""
def __init__(self):
pass
def fit(self, X, Y):
"""
Функция обучения модели.
Предполагается модель Y = X * theta + epsilon.
где X --- регрессор (матрица размера n x d),
Y --- отклик (матрица размера n x 1),
epsilon-ы имеют нормальное распределение
Обратите внимание, здесь нет intercept_
"""
self.n, self.d = X.shape[0], X.shape[1]
self.theta = МНК-оценка
return ...
def predict(self, X):
"""
Возвращает предсказание отклика на новых объектах X.
X --- матрица объектов размера n x d
"""
y_pred = ...
return ...
Обучите вашу модель на датасете о пингвинах без добавления свободного члена.
Распечатайте коэффициенты и сравните их с коэффициентами модели из sklearn
.
MyModel = ...
...
Как соотносятся коэффициенты вашей модели и реализации из sklearn
?
Ответ: ...
Теперь обучите собственную модель c добавлением свободного члена.
...
Помимо рассмотренных на семенаре метрик, довольно часто рассматривают коэфициент детерминации $R^2$, который может быть более интуитивным.
Определяется он так:
$$
R^2 = 1 - \frac{SS_{reg}}{SS_{tot}},
$$
где $SS_{res} = \sum\limits_{i=1}^n (y_i - f_i)^2$ отражает точность нашей модели,
a $SS_{tot} = \sum\limits_{i=1}^n (y_i - \overline{y})^2$ отражает дисперсию исходных данных.
Соответсвенно чем результат ближе к еденице тем более точная у нас модель и тем лучше она предсказывает. Отрицательный же результат можем получить в случае, если наша модель предсказывает хуже, чем предсказание средним.
В sklearn
есть готовая реализация под названием r2_score
Сравните качество моделей со свободным членом и без него на тестовой выборке, используя метрики RMSE, MAE, MAPE и $R^2$.
...
Сделайте выводы
Вывод: ...
Задача 3¶
Во взвешенном методе наименьших квадратов каждому наблюдению задается некоторый известный вес $w_i$. Задача имеет вид: $$\sum\limits_{i=1}^n w_i\left(Y_i - x_i^T \theta\right)^2 \to \min\limits_\theta$$ Найдите решение задачи в матричном виде.