{ "cells": [ { "cell_type": "markdown", "id": "dc64c2b6", "metadata": {}, "source": [ "#### Инициализация Keras\n", "\n", "torch был заменен на jax, так как с torch рекуррентные сети не работали" ] }, { "cell_type": "code", "execution_count": 2, "id": "507915ea", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.9.2\n" ] } ], "source": [ "import os\n", "\n", "os.environ[\"KERAS_BACKEND\"] = \"jax\"\n", "import keras\n", "\n", "print(keras.__version__)" ] }, { "cell_type": "markdown", "id": "2dec3146", "metadata": {}, "source": [ "#### Загрузка данных для классификации с помощью глубоких сетей\n", "\n", "В качестве набора данных используется набор отзывов к фильмам с сайта IMDB.\n", "\n", "Набор включает 50 000 отзывов, половина из которых находится в обучающем наборе данных (x_train), а половина - в тестовом (x_valid). \n", "\n", "Метки (y_train и y_valid) имеют бинарный характер и назначены в соответствии с этими 10-балльными оценками:\n", "- отзывы с четырьмя звездами или меньше считаются отрицательным (y = 0);\n", "- отзывы с семью звездами или больше считаются положительными (y = 1);\n", "- умеренные отзывы — с пятью или шестью звездами — не включались в набор данных, что упрощает задачу бинарной классификации.\n", "\n", "Данные уже предобработаны для простоты работы с ними.\n", "\n", "unique_words - в векторное пространство включается только слова, которые встречаются в корпусе не менее 10 000 раз.\n", "\n", "max_length - максимальная длина отзыва (если больше, то обрезается, если меньше, то дополняется \"пустыми\" словами)." ] }, { "cell_type": "code", "execution_count": 3, "id": "e0043e5c", "metadata": {}, "outputs": [], "source": [ "from keras.api.datasets import imdb\n", "import os\n", "\n", "unique_words = 10000\n", "max_length = 100\n", "\n", "output_dir = \"tmp\"\n", "if not os.path.exists(output_dir):\n", " os.makedirs(output_dir)\n", "\n", "(X_train, y_train), (X_valid, y_valid) = imdb.load_data(num_words=unique_words)" ] }, { "cell_type": "markdown", "id": "1bfd826c", "metadata": {}, "source": [ "#### Приведение отзывов к длине max_length (100)\n", "\n", "padding и truncating - дополнение и обрезка отзывов начинается с начала (учитывается специфика затухания градиента в рекуррентных сетях)" ] }, { "cell_type": "code", "execution_count": 4, "id": "131e125a", "metadata": {}, "outputs": [], "source": [ "from keras.api.preprocessing.sequence import pad_sequences\n", "\n", "X_train = pad_sequences(X_train, maxlen=max_length, padding=\"pre\", truncating=\"pre\", value=0)\n", "X_valid = pad_sequences(X_valid, maxlen=max_length, padding=\"pre\", truncating=\"pre\", value=0)" ] }, { "cell_type": "markdown", "id": "3c337946", "metadata": {}, "source": [ "#### Формирование архитектуры глубокой многослойной рекуррентной двунаправленной LSTM сети\n", "\n", "Параметр return_sequences=True первого рекуррентного слоя позволяет представить вывод этого слоя в формате, подходящем для передачи во второй рекуррентный слой\n", "\n", "Первый слой (Embedding) выполняет векторизацию" ] }, { "cell_type": "code", "execution_count": 5, "id": "1e3fb0ec", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Model: \"sequential\"\n",
       "
\n" ], "text/plain": [ "\u001b[1mModel: \"sequential\"\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃ Layer (type)                     Output Shape                  Param # ┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ embedding (Embedding)           │ (None, 100, 64)        │       640,000 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ spatial_dropout1d               │ (None, 100, 64)        │             0 │\n",
       "│ (SpatialDropout1D)              │                        │               │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ bidirectional (Bidirectional)   │ (None, 100, 128)       │        66,048 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ bidirectional_1 (Bidirectional) │ (None, 128)            │        98,816 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ dense (Dense)                   │ (None, 1)              │           129 │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
       "
\n" ], "text/plain": [ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n", "┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\n", "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n", "│ embedding (\u001b[38;5;33mEmbedding\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m100\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m640,000\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ spatial_dropout1d │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m100\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "│ (\u001b[38;5;33mSpatialDropout1D\u001b[0m) │ │ │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ bidirectional (\u001b[38;5;33mBidirectional\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m100\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m66,048\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ bidirectional_1 (\u001b[38;5;33mBidirectional\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m98,816\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ dense (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1\u001b[0m) │ \u001b[38;5;34m129\u001b[0m │\n", "└─────────────────────────────────┴────────────────────────┴───────────────┘\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
 Total params: 804,993 (3.07 MB)\n",
       "
\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m804,993\u001b[0m (3.07 MB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
 Trainable params: 804,993 (3.07 MB)\n",
       "
\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m804,993\u001b[0m (3.07 MB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
 Non-trainable params: 0 (0.00 B)\n",
       "
\n" ], "text/plain": [ "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from keras.api.models import Sequential\n", "from keras.api.layers import InputLayer, Embedding, SpatialDropout1D, LSTM, Bidirectional, Dense\n", "\n", "mlstm_model = Sequential()\n", "mlstm_model.add(InputLayer(shape=(max_length,), dtype=\"float32\"))\n", "mlstm_model.add(Embedding(unique_words, 64))\n", "mlstm_model.add(SpatialDropout1D(0.2))\n", "mlstm_model.add(Bidirectional(LSTM(64, dropout=0.2, return_sequences=True)))\n", "mlstm_model.add(Bidirectional(LSTM(64, dropout=0.2)))\n", "mlstm_model.add(Dense(1, activation=\"sigmoid\"))\n", "\n", "mlstm_model.summary()" ] }, { "cell_type": "markdown", "id": "c27996b5", "metadata": {}, "source": [ "#### Обучение модели\n", "\n", "Веса модели сохраняются в каталог tmp после каждой эпохи обучения с помощью callback-параметра\n", "\n", "В дальнейшем веса можно загрузить" ] }, { "cell_type": "code", "execution_count": 6, "id": "11236198", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/4\n", "\u001b[1m196/196\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m65s\u001b[0m 329ms/step - accuracy: 0.6714 - loss: 0.5539 - val_accuracy: 0.8469 - val_loss: 0.3582\n", "Epoch 2/4\n", "\u001b[1m196/196\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m79s\u001b[0m 403ms/step - accuracy: 0.8938 - loss: 0.2680 - val_accuracy: 0.8510 - val_loss: 0.3457\n", "Epoch 3/4\n", "\u001b[1m196/196\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m85s\u001b[0m 437ms/step - accuracy: 0.9221 - loss: 0.2047 - val_accuracy: 0.8436 - val_loss: 0.3869\n", "Epoch 4/4\n", "\u001b[1m196/196\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m97s\u001b[0m 496ms/step - accuracy: 0.9459 - loss: 0.1525 - val_accuracy: 0.8372 - val_loss: 0.4265\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from keras.api.callbacks import ModelCheckpoint\n", "\n", "mlstm_model.compile(\n", " loss=\"binary_crossentropy\",\n", " optimizer=\"adam\",\n", " metrics=[\"accuracy\"],\n", ")\n", "\n", "mlstm_model.fit(\n", " X_train,\n", " y_train,\n", " batch_size=128,\n", " epochs=4,\n", " validation_data=(X_valid, y_valid),\n", " callbacks=[ModelCheckpoint(filepath=output_dir + \"/mlstm_weights.{epoch:02d}.keras\")],\n", ")" ] }, { "cell_type": "markdown", "id": "b170b40c", "metadata": {}, "source": [ "#### Загрузка лучшей модели и оценка ее качества\n", "\n", "Качество модели - 85.1 %." ] }, { "cell_type": "code", "execution_count": 7, "id": "94987771", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m782/782\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m36s\u001b[0m 46ms/step - accuracy: 0.8513 - loss: 0.3512\n" ] }, { "data": { "text/plain": [ "[0.3456658124923706, 0.8509600162506104]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mlstm_model.load_weights(output_dir + \"/mlstm_weights.02.keras\")\n", "mlstm_model.evaluate(X_valid, y_valid)" ] }, { "cell_type": "markdown", "id": "5f4ded2c", "metadata": {}, "source": [ "#### Визуализация распределения вероятностей результатов модели на валидационной выборке" ] }, { "cell_type": "code", "execution_count": 8, "id": "8965a612", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m782/782\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m35s\u001b[0m 45ms/step\n" ] }, { "data": { "image/png": "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/eMf/2jgzs0S7jrX2L9/v/VP//RP1nXXXWcNGjSoYZo1XLhrXV5ebvXp08e65ZZbrE2bNln79++31q9fbxUXFzdw52YJd50XL15sOZ1Oa/Hixdb+/futNWvWWO3atbOysrIauHOzvP3229Zf/vIX64033rAkWStWrPjJ+n379lnNmze3srOzrV27dllz5syxoqKirNWrV9dbjwSY01x11VVWZmam/XVVVZWVkJBgTZky5az1d999t5WWlhayr2/fvtaf//zneu2zKQh3rX+ssrLSatWqlbVo0aL6arFJqM06V1ZWWldffbW1YMECKyMjgwBznsJd6xdffNG65JJLrIqKioZqsUkId50zMzOtG2+8MWRfdna2dc0119Rrn03J+QSYsWPHWt27dw/Zd88991ipqan11hcfIf2fiooKFRUVKSUlxd4XGRmplJQUFRYWnvWYwsLCkHpJSk1NPWc9vlebtf6x7777TqdOnVLr1q3rq03j1XadJ0+erPj4eA0bNqwh2mwSarPWb775pnw+nzIzM+XxeHT55Zfr6aefVlVVVUO1bZzarPPVV1+toqIi+2Omffv26e2339Ytt9zSID3/WjTG++EF+5N4G9o333yjqqqqM37Sr8fj0Z49e856jN/vP2u93++vtz6bgtqs9Y+NGzdOCQkJZ/yFwQ9qs86bNm3SK6+8ouLi4gbosOmozVrv27dP69at05AhQ/T222/riy++0MMPP6xTp07psccea4i2jVObdb733nv1zTff6Nprr5VlWaqsrNSIESP0//7f/2uIln81zvV+GAwGdeLECcXGxtb59+QKDIwzdepULV26VCtWrFBMTExjt9NkHD16VEOHDtXLL7+stm3bNnY7TV51dbXi4+P10ksvqXfv3rrnnnv0l7/8RfPnz2/s1pqU9evX6+mnn9a8efP04Ycf6o033lBeXp6eeOKJxm4NvxBXYP5P27ZtFRUVpUAgELI/EAjI6/We9Riv1xtWPb5Xm7Wu8dxzz2nq1Kl69913dcUVV9Rnm8YLd53//ve/68CBA7rtttvsfdXV1ZKk6Oho7d27V5deemn9Nm2o2vyZbteunZo1a6aoqCh7X7du3eT3+1VRUSGHw1GvPZuoNuv86KOPaujQoXrwwQclST169NDx48c1fPhw/eUvf1FkJP8fXxfO9X7ocrnq5eqLxBUYm8PhUO/evVVQUGDvq66uVkFBgXw+31mP8fl8IfWSlJ+ff856fK82ay1J06ZN0xNPPKHVq1erT58+DdGq0cJd565du2rHjh0qLi62t3/9139V//79VVxcrMTExIZs3yi1+TN9zTXX6IsvvrBDoiR99tlnateuHeHlHGqzzt99990ZIaUmNFr8KsA60yjvh/V2e7CBli5dajmdTis3N9fatWuXNXz4cCsuLs7y+/2WZVnW0KFDrfHjx9v177//vhUdHW0999xz1u7du63HHnuMx6jPU7hrPXXqVMvhcFj/9V//ZX311Vf2dvTo0cY6BSOEu84/xlNI5y/ctS4pKbFatWpljRo1ytq7d6+1atUqKz4+3nryyScb6xSMEO46P/bYY1arVq2sv/3tb9a+ffustWvXWpdeeql19913N9YpGOHo0aPWRx99ZH300UeWJGv69OnWRx99ZH355ZeWZVnW+PHjraFDh9r1NY9Rjxkzxtq9e7c1d+5cHqNuaHPmzLE6dOhgORwO66qrrrK2bNlij91www1WRkZGSP2yZcusyy67zHI4HFb37t2tvLy8Bu7YXOGsdceOHS1JZ2yPPfZYwzdumHD/TJ+OABOecNd68+bNVt++fS2n02ldcskl1lNPPWVVVlY2cNfmCWedT506ZU2aNMm69NJLrZiYGCsxMdF6+OGHrW+//bbhGzfIe++9d9Z/c2vWNiMjw7rhhhvOOKZnz56Ww+GwLrnkEmvhwoX12mOEZXENDQAAmIV7YAAAgHEIMAAAwDgEGAAAYBwCDAAAMA4BBgAAGIcAAwAAjEOAAQAAxiHAAAAA4xBgAACAcQgwAADAOAQYAABgHAIMAAAwzv8HJGIdejBgktkAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "plt.hist(mlstm_model.predict(X_valid))\n", "_ = plt.axvline(x=0.5, color=\"orange\")" ] } ], "metadata": { "kernelspec": { "display_name": ".venv (3.12.10)", "language": "python", "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.12.10" } }, "nbformat": 4, "nbformat_minor": 5 }