{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": [
"# [Phystech@DataScience](https://thetahat.ru/courses/ph-ds-2025-spr)\n",
"## Домашнее задание 11\n",
"\n",
"**Правила, прочитайте внимательно:**\n",
"\n",
"* Выполненную работу нужно отправить телеграм-боту `@thetahat_pds_bot`. Для начала работы с ботом каждый раз отправляйте `/start`. Дождитесь подтверждения от бота, что он принял файл. Если подтверждения нет, то что-то не так. **Работы, присланные иным способом, не принимаются.**\n",
"* Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.\n",
"* Прислать нужно **ноутбук в формате `ipynb`**. Если вы строите интерактивные графики, их стоит прислать в формате html.\n",
"* Следите за размером файлов. **Бот не может принимать файлы весом более 20 Мб.** Если файл получается больше, заранее разделите его на несколько.\n",
"* Выполнять задание необходимо полностью самостоятельно. **При обнаружении списывания всем участникам списывания дается штраф -2 балла к итоговой оценке за семестр.**\n",
"* Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.\n",
"* Обратите внимание на правила использования ИИ-инструментов при решении домашнего задания.\n",
"* **Код из рассказанных на занятиях ноутбуков** можно использовать без ограничений.\n",
"* Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.\n",
"* Комментарии к решению пишите в markdown-ячейках.\n",
"* Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.\n",
"* Решение проверяется системой ИИ-проверки
**ThetaGrader**. Результат проверки валидируется и исправляется человеком, после чего комментарии отправляются студентам.\n",
"* Если код будет не понятен проверяющему, оценка может быть снижена.\n",
"* Никакой код из данного задания при проверке запускаться не будет. *Если код студента не выполнен, недописан и т.д., то он не оценивается.*\n",
"\n",
"Важно!!! Правила заполнения ноутбука:\n",
"* Запрещается удалять имеющиеся в ноутбуке ячейки, менять местами положения задач.\n",
"* Сохраняйте естественный линейный порядок повествования в ноутбуке сверху-вниз.\n",
"* Отвечайте на вопросы, а также добавляйте новые ячейки в предложенных местах, которые обозначены `<...>`.\n",
"* В markdown-ячейках, содержащих описание задачи, находятся специальные отметки, которые запрещается модифицировать.\n",
"* При нарушении данных правил работа может получить 0 баллов.\n",
"\n",
"**Баллы за задание:**\n",
"\n",
"Легкая часть (достаточно на \"хор\"):\n",
"* Задача 1 — 60 баллов\n",
"* Задача 2 — 60 баллов\n",
"\n",
"Сложная часть (необходимо на \"отл\"):\n",
"* Задача 3 — 30 баллов\n",
"* Задача 4 — 30 баллов\n",
"\n",
"\n",
"\n",
"-----"
]
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"# Bot check\n",
"\n",
"# HW_ID: phds_hw11\n",
"# Бот проверит этот ID и предупредит, если случайно сдать что-то не то.\n",
"\n",
"# Status: not final\n",
"# Перед отправкой в финальном решении удали \"not\" в строчке выше.\n",
"# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную.\n",
"# Никакие значения в этой ячейке не влияют на факт сдачи работы."
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"id": "Ny8CbNN5zEkR"
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import seaborn as sns\n",
"import torch\n",
"from torch import nn, optim\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from IPython.display import clear_output\n",
"\n",
"from sklearn.metrics import mean_absolute_percentage_error, r2_score\n",
"from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet\n",
"from sklearn.preprocessing import LabelBinarizer, StandardScaler\n",
"from sklearn.model_selection import train_test_split\n",
"\n",
"import warnings\n",
"warnings.filterwarnings('ignore')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "usIK4Lrd021K"
},
"source": [
"---\n",
"## Легкая часть\n",
"### Задача 1"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4ZfWEaJu02wz"
},
"source": [
"#### *Профиль биология*\n",
"\n",
"Мы будем исследовать датасет по экспрессиям различных генов (RNA-seq), используемых для предсказания возраста пациентов."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 273
},
"id": "inV4Tx-zG2pp",
"outputId": "9570e905-0457-4994-e8ee-c29b9600653d"
},
"outputs": [],
"source": [
"df = pd.read_csv(\"Rnaseq_age_reg.csv\")\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "HJboUl86B9IU"
},
"source": [
"Разбейте датасет на признаки и таргет, где в качестве таргета будет использоваться столбец `Age`, а признаки - все остальные. В том числе разбейте на подвыборки для обучения и теста."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "_DhNs7STG2lg",
"outputId": "6ec2d089-9623-4604-8b45-6c75c1b8c36a"
},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Переходите к общей части.**"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### *Профиль физика*"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_csv(\"physics_data.csv\", index_col=0)\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Разбейте датасет на признаки и таргет, где в качестве таргета будет использоваться столбец Eat, а признаки - все остальные. В том числе разбейте на подвыборки для обучения и теста."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "C2tp1fIoHUoi"
},
"source": [
"### Общая часть\n",
"\n",
"Отмасштабируйте данные:"
]
},
{
"cell_type": "code",
"execution_count": 174,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "LPBCrYQECQ0t"
},
"source": [
"Обучите модель линейной регрессии и посмотрите на значения метрик на тесте. Что вы можете сказать про результат обучения?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "0eRQz-AzG2hS",
"outputId": "9cc62d52-da7e-4e1a-efc6-1e1ad8d7bbfc"
},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "tAEwwjTCCsML"
},
"source": [
"Обучите линейные модели с регуляризациями, которые мы проходили ранее. Для каждой из моделей постройте графики зависимости метрик r2 и MAPE от коэфициента регуляризации. Можно пользоваться кодом из домашнего задания по регуляризации. Сильно ли улучшился результат? "
]
},
{
"cell_type": "code",
"execution_count": 176,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Выберите оптимальный, на вашь взгляд, параметр для L1-регуляризации, обучите модель Lasso-регрессии, выведите ещё раз метрики r2 и MAPE и проведите отбор признаков: уберите из датасета все те, для которых коэффициент регрессии оказался нулевым."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "3gLBvPQsDI0X"
},
"source": [
"Теперь обучите простейшую нейронную сеть на уменьшенном датасете, сравните результат с результатами обучения других моделей.\n",
"\n",
"**Подсказка** Используйте nn.Sequential()"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {
"id": "qbTTQzitEYZL"
},
"outputs": [],
"source": [
"model = "
]
},
{
"cell_type": "code",
"execution_count": 97,
"metadata": {
"id": "Ijtz-8UrE7mM"
},
"outputs": [],
"source": [
"optim_func = <...>\n",
"optimizer = <...>"
]
},
{
"cell_type": "code",
"execution_count": 98,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "u94Sntm2E7L1",
"outputId": "a5d9913d-5468-4812-c004-2942b3c473b2"
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "f14nTbjyFY3a",
"outputId": "12b632d3-4a3d-4235-89bb-5931faf2d9bf"
},
"outputs": [],
"source": [
"with torch.no_grad():\n",
" y_pred = model(torch.FloatTensor(<Тестовая выборка>)).numpy()\n",
"\n",
"print(f'R2: {round(r2_score(y_test, y_pred), 2)} \\nMAPE: {round(mean_absolute_percentage_error(y_test, y_pred), 2)}')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "B_SYdVr1IGR5"
},
"source": [
"**Вопрос:** объясните полученный результат.\n",
"\n",
"<...>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "W_YpL1ipFYm1"
},
"source": [
"Сравните все модели, поясняя полученные результаты и значения метрик.\n",
"\n",
"**Вывод:**"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "yukG_GKL1_hS"
},
"source": [
"---\n",
"### Задача 2\n",
"#### *Профиль физика*\n",
"\n",
"Скачайте [датасет](https://www.kaggle.com/datasets/omidbaghchehsaraei/identification-of-two-modes-of-z-boson?resource=download), описывающий распады Z-бозонов двух типов: `Zee`и `Zmumu`. Создайте и обучите нейросеть, разделяющую эти два класса.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 206
},
"id": "ZODZ-xSva-Oc",
"outputId": "de436221-0473-4cd1-be0c-69c70e84c532"
},
"outputs": [],
"source": [
"data = pd.read_csv('Z_boson.csv')\n",
"data.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4E3mrI6BNF4r"
},
"source": [
"Удалите столбцы `Unnamed: 0`, `Run` и `Event`, так как это не физические величины. Удалите строки, где есть пропуски, если таковые имеются."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "xoCSmenbM5zU",
"outputId": "6acd1b99-9634-4be4-8b73-48c9ec78fbe0"
},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "QzhmUYMtSWnx"
},
"source": [
"Также можно как-нибудь взглянуть на признаки. Возможно, не все они вносят вклад в разделение классов. Не забудьте преобразовать таргет (столбец `class`) к формату 0 и 1. Вам может пригодиться `sklearn.preprocessing.LabelBinarizer`.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 891
},
"id": "66R0_0cYPfah",
"outputId": "00c0e1b0-125c-4d86-f0d4-2627d0460cb7"
},
"outputs": [],
"source": [
"plt.figure(figsize=(20, 10))\n",
"sns.set_theme(font_scale=2.0)\n",
"sns.pairplot(data, hue=\"class\", palette=\"deep\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Какие признаки вы бы использовали для разделения людей по классам? Выберите эти столбцы и создайте наборы train и test с помощью функции train_test_split, а также выделите набор данных для валидации при обучении."
]
},
{
"cell_type": "code",
"execution_count": 142,
"metadata": {
"id": "wgja_o782UCh"
},
"outputs": [],
"source": [
"# исходя из графиков, отберём признаки для обучения\n",
"selected_features = [<...>]\n",
"X = data[selected_features]"
]
},
{
"cell_type": "code",
"execution_count": 143,
"metadata": {},
"outputs": [],
"source": [
"# таргет преобразуем из строк \"Zee\", \"Zmumu\" к 0 и 1\n",
"<...>\n",
"\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)\n",
"X_val, X_test, y_val, y_test = train_test_split(X_test, y_test, train_size=0.3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Переходите к общей части.**"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "uehpM9ZNRRKo"
},
"source": [
"### Профиль биология\n",
"\n",
"Скачайте [датасет](https://www.kaggle.com/datasets/sooyoungher/smoking-drinking-dataset), описывающий влияние курения и алкоголя на человека. Создайте и обучите нейросеть, разделяющую эти два класса."
]
},
{
"cell_type": "code",
"execution_count": 151,
"metadata": {
"id": "yroWNuBZt37x"
},
"outputs": [],
"source": [
"df = pd.read_csv(\"smoking_driking_dataset_Ver01.csv\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 349
},
"id": "-oKe27HiuM2R",
"outputId": "064958d3-b64d-4eb2-b2da-90180d2244dd"
},
"outputs": [],
"source": [
"df.describe()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "SYdgrbqsuSbT",
"outputId": "2cce2366-9380-4273-90b7-b36f0b2f3585"
},
"outputs": [],
"source": [
"df.columns, df.shape"
]
},
{
"cell_type": "code",
"execution_count": 154,
"metadata": {
"id": "-1c1VgqDuXKN"
},
"outputs": [],
"source": [
"column_names = df.columns[1:-2]# нам не нужны гендер и таргеты сейчас"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "6cAFyKpTuqSg",
"outputId": "7d7bfae4-05d6-4733-a5da-349b1f81125d"
},
"outputs": [],
"source": [
"column_names"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "9f4jgeTtk59Q"
},
"source": [
"Попробуем классифицировать людей с плохими привычками и без них, чтобы облегчить нам задачу. Для этого создадим дополнительный столбец в таблице, который будет содержать информацию о том, имеет ли человек вредные привычки или нет."
]
},
{
"cell_type": "code",
"execution_count": 156,
"metadata": {
"id": "nY9FBycck5UU"
},
"outputs": [],
"source": [
"def smoking_preprocessing(x):\n",
" if x == 3 or x == 2:\n",
" return 1\n",
" else:\n",
" return 0\n",
"\n",
"def drinking_preprocessing(x):\n",
" if x == 'Y':\n",
" return 1\n",
" else:\n",
" return 0"
]
},
{
"cell_type": "code",
"execution_count": 157,
"metadata": {
"id": "Rq1LLGtWl3ki"
},
"outputs": [],
"source": [
"df['SMK_stat_type_cd'] = df['SMK_stat_type_cd'].apply(func = smoking_preprocessing)\n",
"df['DRK_YN'] = df['DRK_YN'].apply(func = drinking_preprocessing)"
]
},
{
"cell_type": "code",
"execution_count": 158,
"metadata": {
"id": "k7HT3JWEm597"
},
"outputs": [],
"source": [
"df['bad_habits'] = df['DRK_YN']+df['SMK_stat_type_cd']-df['DRK_YN']*df['SMK_stat_type_cd']"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "G9-eDwLSnPbd"
},
"source": [
"Теперь смотрим на разделение по привычкам"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 868
},
"id": "Xt2lMxcVnPbf",
"outputId": "6b9096dd-01bc-4ace-f2a3-ce378936f5ca"
},
"outputs": [],
"source": [
"graph = sns.PairGrid(df.iloc[:1000], hue='bad_habits', vars = column_names[:7])\n",
"graph.map_upper(sns.scatterplot)\n",
"graph.map_lower(sns.kdeplot)\n",
"graph.map_diag(sns.kdeplot)\n",
"graph.add_legend()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 873
},
"id": "8lbzL5H3nPbf",
"outputId": "640905b4-d15b-40e7-f8bd-e00e78dbb60b"
},
"outputs": [],
"source": [
"graph = sns.PairGrid(df.iloc[:1000], hue='bad_habits', vars = column_names[7:15])\n",
"graph.map_upper(sns.scatterplot)\n",
"graph.map_lower(sns.kdeplot)\n",
"graph.map_diag(sns.kdeplot)\n",
"graph.add_legend()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 853
},
"id": "wJKDZTVYnPbf",
"outputId": "ab236638-8068-4fca-eeba-8fd5b6a63a3b"
},
"outputs": [],
"source": [
"graph = sns.PairGrid(df.iloc[:1000], hue='bad_habits', vars = column_names[15:])\n",
"graph.map_upper(sns.scatterplot)\n",
"graph.map_lower(sns.kdeplot)\n",
"graph.map_diag(sns.kdeplot)\n",
"graph.add_legend()"
]
},
{
"cell_type": "code",
"execution_count": 159,
"metadata": {
"id": "ciXaf81rI0OA"
},
"outputs": [],
"source": [
"target_1 = df.pop('SMK_stat_type_cd')\n",
"target_2 = df.pop('DRK_YN')\n",
"target_3 = df.pop('bad_habits')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "gLKIv5opel5K"
},
"source": [
"Какие признаки вы бы использовали для разделения людей по классам? Выберите эти столбцы и создайте наборы train и test с помощью функции train_test_split, а также выделите набор данных для валидации при обучении."
]
},
{
"cell_type": "code",
"execution_count": 160,
"metadata": {
"id": "yA_t_GwRekzM"
},
"outputs": [],
"source": [
"# исходя из графиков, отберём признаки для обучения (нас интересуют вредные привычки)\n",
"selected_features = [<...>]\n",
"X = data[selected_features]"
]
},
{
"cell_type": "code",
"execution_count": 161,
"metadata": {
"id": "bcUqqdZxcwN-"
},
"outputs": [],
"source": [
"# данных очень много, поэтому для экономии времени автор ноутбука отводит на обучение всего треть датасета\n",
"X_train, X_test, y_train, y_test = train_test_split(X, target_3, train_size=0.3)\n",
"X_val, X_test, y_val, y_test = train_test_split(X_test, y_test, train_size=0.3)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xuPQOf5HAUIA"
},
"source": [
"## Общая часть"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Как вы помните, в задаче классификации предсказывается вероятность. На основании этой вероятности можно делать вывод о принадлежности объекта к тому или иному классу. Причём не всегда используется порог $P=0.5$. Например, если классы несбалансированы, это значение можно варьировать на интервале (0, 1). Предоставляем вам возможность самим выбрать этот порог и поэкспериментировать."
]
},
{
"cell_type": "code",
"execution_count": 162,
"metadata": {
"id": "dPrD9dlrEKLL"
},
"outputs": [],
"source": [
"class_lim_proba = <...> # критерий принадлежности к тому или иному классу"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "dW6vXt5FHnW3"
},
"source": [
"Стандартизируйте данные"
]
},
{
"cell_type": "code",
"execution_count": 163,
"metadata": {
"id": "gS-c8qg5-jqT"
},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "ayu3JfBzsqC6"
},
"source": [
"Напишем функцию для отрисовки кривых обучения. На одном графике расположим значение функции потерь на трейне и валидации, а на другом — значение метрики качества на ваш выбор, также для трейна и валидации."
]
},
{
"cell_type": "code",
"execution_count": 164,
"metadata": {
"id": "YJFrPQRAsjGz"
},
"outputs": [],
"source": [
"def plot_learning_curves(history):\n",
" '''\n",
" Функция для отображения лосса и метрики во время обучения.\n",
" '''\n",
" clear_output(wait=True)\n",
"\n",
" fig = plt.figure(figsize=(20, 7))\n",
" fontsize = 15 # размер шрифта\n",
"\n",
" plt.subplot(1,2,1)\n",
" plt.title('Лосс', fontsize=fontsize)\n",
" plt.plot(history['loss_train'], label='train')\n",
" plt.plot(history['loss_val'], label='val')\n",
" plt.ylabel('лосс', fontsize=fontsize)\n",
" plt.xlabel('эпоха', fontsize=fontsize)\n",
" plt.legend()\n",
"\n",
" plt.subplot(1,2,2)\n",
" plt.title('Метрика', fontsize=fontsize)\n",
" plt.plot(history['metric_train'], label='train')\n",
" plt.plot(history['metric_val'], label='val')\n",
" plt.ylabel('Значение метрики', fontsize=fontsize)\n",
" plt.xlabel('эпоха', fontsize=fontsize)\n",
" plt.legend()\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Напишите функцию метрики, которую вы будете использовать, например accuracy."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def metric(y_true, y_pred):\n",
" <...>\n",
" return metric"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "-KRqsqFXrOj_"
},
"source": [
"#### Создание модели.\n",
"В семинаре вы у промежуточных слоёв задавали `in_features = out_features = 1`, а в данном случае вам надо будет создать нейросеть из нескольких слоёв, поставив только у последнего из них `out_features = 1`.\n",
"\n",
"Какой должна быть размерность входа первого слоя?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "0TgKtRfVrLpb",
"outputId": "a1a2f363-525d-4233-a38b-d225419e50d6"
},
"outputs": [],
"source": [
"model = <...>\n",
"\n",
"model"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "EllEx8_atGPX"
},
"source": [
"#### Обучение\n",
"\n",
"В качетсве функции потерь возьмите [бинарную кросс-энтропию](https://pytorch.org/docs/stable/generated/torch.nn.BCELoss.html), а шаг градиентного спуска установите равным 0.5. Можете взять и другие loss и `lr`, если хотите поэкспериментировать."
]
},
{
"cell_type": "code",
"execution_count": 148,
"metadata": {
"id": "OIka-Rs1uMHY"
},
"outputs": [],
"source": [
"optim_func = <...>\n",
"optimizer = <...>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 385
},
"id": "7Xz9XaHl_7eK",
"outputId": "16943c02-774d-4210-da66-a5e2150ed4d6"
},
"outputs": [],
"source": [
"batch_size = 2000 # этот\n",
"num_iter = 200 # и этот параметры можете также поварьировать\n",
"history = {\n",
" 'loss_train': [],\n",
" 'loss_val': [],\n",
" 'metric_train': [],\n",
" 'metric_val': [],\n",
"}\n",
"\n",
"for i in range(num_iter):\n",
" \n",
" # Так как размер выборки слишком велик, то будем обучать лишь на части данных\n",
" indexes_train = np.random.choice(np.arange(len(X_train)), batch_size, replace=False)\n",
" local_X_train = X_train[indexes_train]\n",
" local_y_train = y_train[indexes_train]\n",
"\n",
" indexes_val = np.random.choice(np.arange(len(X_val)), batch_size//10, replace=False)\n",
" local_X_val = <...>\n",
" local_y_val = <...>\n",
"\n",
" # Forward pass: предсказание модели по данным X_train\n",
" y_pred_train = <...>\n",
" with torch.no_grad():\n",
" y_pred_val = <...>\n",
"\n",
"\n",
" # Вычисление оптимизируемой функции (MSE) по предсказаниям\n",
" loss_train = <...>\n",
" with torch.no_grad():\n",
" loss_val = <...>\n",
"\n",
" # Backward pass: вычисление градиентов оптимизируемой функции\n",
" # по всем параметрам модели\n",
" <...>\n",
"\n",
" # Оптимизация: обновление параметров по формулам соответствующего\n",
" # метода оптимизации, используются вычисленные ранее градиенты\n",
" <...>\n",
"\n",
" # Зануление градиентов\n",
" <...>\n",
"\n",
" # Считаем метрику на эпохе (здесь посчитана accuracy, можете реализовать любую другую за доп. баллы)\n",
" metric_train = np.sum((y_pred_train.detach().numpy() >= class_lim_proba).reshape(-1) == local_y_train) / len(local_y_train)\n",
" metric_val = np.sum((y_pred_val.detach().numpy() >= class_lim_proba).reshape(-1) == local_y_val) / len(local_y_val)\n",
"\n",
" # Сохраняем результаты эпохи\n",
" history['loss_train'].append(loss_train.item())\n",
" history['loss_val'].append(loss_val)\n",
" history['metric_train'].append(metric_train)\n",
" history['metric_val'].append(metric_val)\n",
"\n",
" # График Метрики + Лосса для трейна и валидации каждую итерацию\n",
" plot_learning_curves(history)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "DkW1-Qjd_-dN"
},
"source": [
"Тестирование"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "TkZtkmbD4PHI",
"outputId": "0bc0c23a-c963-4692-fc44-43a8e72e8e92"
},
"outputs": [],
"source": [
"with torch.no_grad():\n",
" y_pred_test = <...>\n",
" loss_test = <...>\n",
" metric_test = <считается по аналогии с тем, как на обучении>\n",
"print(f\" Test Loss: {loss_test} \\n Test metric: {metric_test}\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "AKaxR5ClAEzS"
},
"source": [
"**Выводы:**"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pBtvk1ax2BBD"
},
"source": [
"---\n",
"## Сложная часть\n",
"### Задача 3"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "LXJEXwzvEPQv"
},
"source": [
"В этой задаче мы будем вручную реализовывать усложнение для линейной регрессии, которое вы рассматривали на семинаре. \n",
"\n",
"Запрещено использовать torch.nn (саму библиотеку torch использовать можно и нужно). Чтобы иметь перед глазами оставим здесь формулы:\n",
"\n",
"$$\\widehat{y}(x) = w_1u(x) + b_1,$$\n",
"\n",
"$$u(x) = \\sigma(w_0x + b_0),$$\n",
"\n",
"$$\\sigma(x) = \\text{ReLU}(x) = \\begin{equation*}\\begin{cases}x, \\; x \\ge 0, \\\\ 0, \\; \\text{иначе,} \\end{cases} \\end{equation*}$$\n",
"\n",
"$w_0, b_0 \\in \\mathbb{R}$ — обучаемые параметры первого слоя, $w_1, b_1 \\in \\mathbb{R}$ — обучаемые параметры второго слоя, $\\sigma(x)$ — функция активации, в данном случае мы выбрали `ReLU`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Реализуйте функцию активации:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def act_func(x):\n",
" return <...>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Задайте оптимизируемую функцию / функцию ошибки / лосс — [MSE](https://miptstats.github.io/courses/ad_fivt/linreg_sklearn.html#3.-Тестирование-и-оценка-качества):"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\n",
" MSE(\\widehat{y}, y) = \\frac{1}{n}\\sum_{i=1}^n\\left(\\widehat y_i - y_i\\right)^2\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def optim_func(y_pred, y_true):\n",
" return <...>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "2OvXcixqGrlF"
},
"source": [
"Обучите вашу модель на данных профиля, которые вы выбрали в 1-й задаче. Выберите **1** признак, на котором проводите обучение."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ApGCfCxbI45M"
},
"outputs": [],
"source": [
"# Инициализация параметров\n",
"w0 = <...>\n",
"b0 = <...>\n",
"w1 = <...>\n",
"b1 = <...>\n",
"\n",
"# Количество итераций\n",
"num_iter = 1000\n",
"\n",
"# Скорость обучения для параметров\n",
"lr_w = 0.01\n",
"lr_b = 0.05\n",
"\n",
"for i in range(num_iter):\n",
"\n",
" # Forward pass: предсказание модели\n",
" y_pred = <...>\n",
"\n",
" # Вычисление оптимизируемой функции (MSE)\n",
" loss = <...>\n",
" # Bakcward pass: вычисление градиентов\n",
" loss.backward()\n",
"\n",
" # Оптимизация: обновление параметров\n",
" <...>\n",
"\n",
" # Зануление градиентов\n",
" <...>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "_FlR8uG2ISKG"
},
"source": [
"**Вывод:**"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "A2YpEPPyz1nx"
},
"source": [
"---\n",
"### Задача 4\n",
"\n",
"Рассмотрим двуслойную нейронную сеть, которая принимает на вход $x\\in\\mathbb{R}$ и возвращает $y\\in\\mathbb{R}$. Выход первого слоя возвращает $u \\in\\mathbb{R}^2$. После первого слоя используется функция активации $\\sigma(x) = \\frac{1}{1 + \\exp(-x)}$, после второго слоя функция активации не используется (или используется тождественная). Тем самым нашу нейронку можно представить в виде\n",
"\n",
"$$\\widehat{y}(x) = \\sum_{h=1}^2 w_{2h}u_h(x) + b_2,$$\n",
"\n",
"$$u_h(x) = \\sigma(w_{1h}x + b_{1h}),$$\n",
"\n",
"$$\\text{где} \\; h \\in \\{1, 2\\}.$$\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "s-W7vzhJ0e6o"
},
"source": [
"**1.** Нарисуйте схематически данную нейронную сеть. Сколько у нее обучаемых параметров?\n",
"\n",
"..."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Q8m6kFek0bb0"
},
"source": [
"**2.** Пусть нам дана обучающая выборка $(X_1, Y_1), ..., (X_n, Y_n)$, где $X_i \\in \\mathbb{R}$ и $Y_i \\in \\mathbb{R}$. Нейронная сеть обучается по этой выборке, минимизируя заданную функцию $L$ — функцию ошибки. Положим, что $L$ — это MSE:\n",
"$$\\text{MSE} = L(X, Y) = \\frac{1}{n}\\sum_{i=1}^n \\big(Y_i - \\widehat{y}(X_i)\\big)^2.$$\n",
"\n",
"Наша задача — найти оптимальные параметры нашей модели для минимизации $L(X, Y)$ на заданном наборе данных. Мы будем решать эту задачу с помощью градиентного спуска. Для этого нам понадобится выписать производные по всем параметрам сети. Конечно, в данном случае довольно просто выписать все производные напрямую. Однако мы воспользуемся следующей хитростью: мы будем считать производные поэтапно, причем начнем с конца вычислительной цепочки и, используя формулу производной сложной функции, последовательно посчитаем все необходимые производные. Этот процесс называется методом **обратного распространения ошибки (backpropagation)**.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "8Qh4_3N60XuA"
},
"source": [
"**2.1.** Начнем с производной MSE по выходам сети:\n",
"$$\\frac{\\partial\\:\\text{MSE}}{\\partial \\widehat{y}(X_i)} = \\; ...$$"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "9pVY18ws0R0r"
},
"source": [
"**2.2** Возьмем производные выходов сети по параметрам последнего слоя\n",
"\n",
"$$\\frac{\\partial \\widehat{y}(X_i)}{\\partial w_{2h}} = \\; ...$$\n",
"\n",
"$$\\frac{\\partial \\widehat{y}(X_i)}{\\partial b_2} = \\; ...$$\n",
"\n",
"Также выпишем производные выходов сети по входам последнего слоя:\n",
"\n",
"$$\\frac{\\partial \\widehat{y}(X_i)}{\\partial u_h(X_i)} = \\; ...$$\n",
"\n",
"Теперь выпишем производные MSE по параметрам и входам последнего слоя. Для этого вспомните правило производной сложной функции из математического анализа. Обратите внимание на то, что нам не нужно прописывать все производные до конца, достаточно заполнить пропуски в записи ниже:\n",
"\n",
"$$\\frac{\\partial\\:\\text{MSE}}{\\partial w_{2h}} = \\sum_{i=1}^n \\frac{\\partial\\:\\text{MSE}}{\\partial ...} \\frac{\\partial ...}{\\partial w_{2h}}$$\n",
"\n",
"$$\\frac{\\partial\\:\\text{MSE}}{\\partial b_2} = \\sum_{i=1}^n \\frac{\\partial\\:\\text{MSE}}{\\partial ...} \\frac{\\partial ...}{\\partial b_2}$$\n",
"\n",
"$$\\frac{\\partial\\:\\text{MSE}}{\\partial u_h} = \\sum_{i=1}^n \\frac{\\partial\\:\\text{MSE}}{\\partial ...} \\frac{\\partial ...}{\\partial u_h}$$"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "CUu7vmpZ0No9"
},
"source": [
"**2.3.** Теперь будем разбираться с производными по параметрам первого слоя.\n",
"\n",
"Для начала нам пригодится производная функции активации, запишите ее так, чтобы ответе осталась функция от $\\sigma(x)$:\n",
"\n",
"$$\\frac{\\partial\\:\\sigma(x)}{\\partial x} = \\; ...$$\n",
"\n",
"Теперь возьмем производные выходов первого слоя по его параметрам:\n",
"\n",
"$$\\frac{\\partial u_h(X_i)}{\\partial w_{1h}} = \\; ...$$\n",
"\n",
"$$\\frac{\\partial u_h(X_i)}{\\partial b_{1h}} = \\; ...$$\n",
"\n",
"Наконец, выпишем производные MSE по параметрам первого слоя. Так же как и раньше достаточно заполнить пропуски в записи ниже:\n",
"\n",
"$$\\frac{\\partial\\:\\text{MSE}}{\\partial w_{1h}} = \\; \\sum_{i=1}^n \\frac{\\partial\\:\\text{MSE}}{\\partial ...} \\frac{\\partial ...}{\\partial w_{1h}}$$\n",
"\n",
"$$\\frac{\\partial\\:\\text{MSE}}{\\partial b_{1h}} = \\; \\sum_{i=1}^n \\frac{\\partial\\:\\text{MSE}}{\\partial ...} \\frac{\\partial ...}{\\partial b_{1h}}$$"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "aYH-3D7bz_KN"
},
"source": [
"**3.** Пусть обучающая выборка очень большая. Что нужно делать в таком случае? Запишите, как нужно поменять правило обновления параметров.\n",
"\n",
"..."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "lUkTCQNFz3-q"
},
"source": [
"**Вывод:**\n",
"\n",
"..."
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [
"pBtvk1ax2BBD",
"A2YpEPPyz1nx"
],
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 0
}