{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "0heumA8kzQk3" }, "source": [ "# Phystech@DataScience\n", "## Домашнее задание 5\n", "\n", "**Правила, прочитайте внимательно:**\n", "\n", "* Выполненную работу нужно отправить телеграм-боту `@miptstats_pds_bot`. Для начала работы с ботом каждый раз отправляйте `/start`. **Работы, присланные иным способом, не принимаются.**\n", "* Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.\n", "* Прислать нужно ноутбук в формате `ipynb`.\n", "* Выполнять задание необходимо полностью самостоятельно. **При обнаружении списывания все участники списывания будут сдавать устный зачет.**\n", "* Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.\n", "* Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.\n", "* Комментарии к решению пишите в markdown-ячейках.\n", "* Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.\n", "* Если код будет не понятен проверяющему, оценка может быть снижена.\n", "* Никакой код из данного задания при проверке запускаться не будет. *Если код студента не выполнен, недописан и т.д., то он не оценивается.*\n", "* **Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.**\n", "\n", "**Правила оформления теоретических задач:**\n", "\n", "* Решения необходимо прислать одним из следующих способов:\n", " * фотографией в правильной ориентации, где все четко видно, а почерк разборчив,\n", " * отправив ее как файл боту вместе с ноутбуком *или*\n", " * вставив ее в ноутбук посредством `Edit -> Insert Image` (фото, вставленные ссылкой, не принимаются);\n", " * в виде $\\LaTeX$ в markdown-ячейках.\n", "* Решения не проверяются, если какое-то требование не выполнено. Особенно внимательно все проверьте в случае выбора второго пункта (вставки фото в ноутбук). Неправильно вставленные фотографии могут не передаться при отправке. Для проверки попробуйте переместить `ipynb` в другую папку и открыть его там.\n", "* В решениях поясняйте, чем вы пользуетесь, хотя бы кратко. Например, если пользуетесь независимостью, то достаточно подписи вида \"*X и Y незав.*\"\n", "* Решение, в котором есть только ответ, и отсутствуют вычисления, оценивается в 0 баллов.\n", "\n", "**Баллы за задание:**\n", "\n", "Легкая часть (достаточно на \"хор\"):\n", "* Задача 1 — 60 баллов\n", "* Задача 2 — 60 баллов\n", "\n", "Сложная часть (необходимо на \"отл\"):\n", "* Задача 3 — 30 баллов\n", "* Задача 4 — 30 баллов\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-----" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "id": "eNC_WGS1zIKT" }, "outputs": [], "source": [ "# Bot check\n", "\n", "# HW_ID: phds_hw5\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": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": { "id": "3gLBvPQsDI0X" }, "source": [ "Теперь обучите простейшую нейронную сеть на уменьшенном датасете, сравните результат с результатами обучения других моделей." ] }, { "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://drive.google.com/file/d/1QqYK7Go96vR4l66MShlUuXxY0_FaBcYB/view?usp=sharing), описывающий распады Z-бозонов двух типов: `Zee`и `Zmumu`. Создайте и обучите нейросеть, разделяющую эти два класса.\n", "\n", "[Описание датасета](https://www.kaggle.com/datasets/omidbaghchehsaraei/identification-of-two-modes-of-z-boson?resource=download)\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://miptstats.github.io/courses/ad_mipt.html), описывающий влияние курения и алкоголя на человека. Создайте и обучите нейросеть, разделяющую эти два класса.\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", " # Считаем метрику на эпохе (здесь посчитана acuracy, можете реализовать любую другую за доп. баллы)\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": [ "Обучите вашу модель на датасете с семинарского задания. Сравните полученный результат с результатом семинара." ] }, { "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 }