diff --git a/labworks/LW4/IS_LR4.ipynb b/labworks/LW4/IS_LR4.ipynb new file mode 100644 index 0000000..45039ad --- /dev/null +++ b/labworks/LW4/IS_LR4.ipynb @@ -0,0 +1 @@ +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"gpuType":"T4","mount_file_id":"1dqGOXcLwVQwLYdgcud5XOoxGoEaRIQsZ","authorship_tag":"ABX9TyN6RT6AzdQtvrIpuV+YgB7t"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"},"accelerator":"GPU"},"cells":[{"cell_type":"code","execution_count":1,"metadata":{"id":"Sk1rdDVJ_RSy","executionInfo":{"status":"ok","timestamp":1765315324986,"user_tz":-180,"elapsed":45,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}}},"outputs":[],"source":["import os\n","os.chdir('/content/drive/MyDrive/Colab Notebooks/IS_LR4')"]},{"cell_type":"code","source":["import tensorflow as tf\n","device_name = tf.test.gpu_device_name()\n","if device_name != '/device:GPU:0':\n"," raise SystemError('GPU device not found')\n","print('Found GPU at: {}'.format(device_name))"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"ltCjrjZG_mAf","executionInfo":{"status":"ok","timestamp":1765315352692,"user_tz":-180,"elapsed":10426,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"b69dc0b6-5ca3-44bd-e130-033f24915ce4"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":["Found GPU at: /device:GPU:0\n"]}]},{"cell_type":"code","source":["# загрузка датасета\n","from keras.datasets import imdb\n","vocabulary_size = 5000\n","index_from = 3\n","(X_train, y_train), (X_test, y_test) = imdb.load_data(path=\"imdb.npz\",\n"," num_words=vocabulary_size,\n"," skip_top=0,\n"," maxlen=None,\n"," seed=35,\n"," start_char=1,\n"," oov_char=2,\n"," index_from=index_from\n"," )\n","print('Shape of X train:', X_train.shape)\n","print('Shape of y train:', y_train.shape)\n","print('Shape of X test:', X_test.shape)\n","print('Shape of y test:', y_test.shape)"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"AEb6ZdYOALQl","executionInfo":{"status":"ok","timestamp":1765316082917,"user_tz":-180,"elapsed":3530,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"be205b9e-f927-4371-f860-0a7d603b0097"},"execution_count":10,"outputs":[{"output_type":"stream","name":"stdout","text":["Shape of X train: (25000,)\n","Shape of y train: (25000,)\n","Shape of X test: (25000,)\n","Shape of y test: (25000,)\n"]}]},{"cell_type":"code","source":["# создание словаря для перевода индексов в слова\n","# заргузка словаря \"слово:индекс\"\n","word_to_id = imdb.get_word_index()\n","# уточнение словаря\n","word_to_id = {key:(value + index_from) for key,value in word_to_id.items()}\n","word_to_id[\"\"] = 0\n","word_to_id[\"\"] = 1\n","word_to_id[\"\"] = 2\n","word_to_id[\"\"] = 3\n","# создание обратного словаря \"индекс:слово\"\n","id_to_word = {value:key for key,value in word_to_id.items()}\n","\n","review_indices = X_train[19]\n","print(\"Review - index:\\n\", review_indices)\n","\n","review_text = \" \".join(id_to_word.get(i, \"?\") for i in review_indices)\n","print(\"\\nReview - text:\\n\", review_text)\n","\n","print(\"\\nReview length:\", len(review_indices))\n","\n","label = y_train[19]\n","class_name = \"Positive\" if label == 1 else \"Negative\"\n","print(\"Class label:\", label, \"| Class name:\", class_name)"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"WywjkQNXClqM","executionInfo":{"status":"ok","timestamp":1765316515983,"user_tz":-180,"elapsed":85,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"31b86852-c762-47c6-b6c9-69c07a020178"},"execution_count":12,"outputs":[{"output_type":"stream","name":"stdout","text":["Review - index:\n"," [1, 608, 50, 26, 84, 37, 144, 24, 67, 14, 20, 10, 10, 300, 92, 67, 12, 48, 25, 92, 40, 2006, 42, 328, 1285, 241, 92, 40, 12, 48, 25, 188, 4154, 34, 4, 2, 342, 92, 67, 12, 48, 25, 181, 6, 622, 3783, 20, 10, 10, 4, 360, 7, 25, 521, 92, 1135, 8, 67, 736, 349, 45, 163, 45, 2812, 45, 6, 1917, 2, 7, 175, 78, 3783, 4896, 573, 8, 132, 2552, 2, 83, 4715, 312, 1285, 92, 2457, 4, 3028, 11, 3850, 364, 1317, 253, 7, 2, 2, 1022, 4106, 5, 4391, 2, 17, 73, 17, 6, 378, 7, 1139, 4139, 531, 34, 2, 3409, 5, 2, 2, 52, 8, 67, 4841, 2, 397, 157, 99, 13, 1498, 32, 4, 96, 143, 1254, 2, 643, 916, 21, 52]\n","\n","Review - text:\n"," ok there are people who should not see this movie br br 1 don't see it if you don't like satire or black humour 2 don't like it if you got offended by the 3 don't see it if you want a serious superhero movie br br the rest of you run don't walk to see mystery men it's funny it's quirky it's a delightful of every bad superhero cliche known to man occasional into junior high humour don't ruin the tongue in cheek low key fun of ben stiller and hank as well as a couple of amusing smaller parts by rush and good to see louise getting work too i laughed all the way through utterly somewhat weird but good\n","\n","Review length: 134\n","Class label: 1 | Class name: Positive\n"]}]},{"cell_type":"code","source":["print(\"Max review length:\", len(max(X_train, key=len)))\n","print(\"Min review length:\", len(min(X_train, key=len)))"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"rjanZPAMEZ4g","executionInfo":{"status":"ok","timestamp":1765316620934,"user_tz":-180,"elapsed":12,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"06e1f94b-8aee-45b0-e771-5168f941ffee"},"execution_count":13,"outputs":[{"output_type":"stream","name":"stdout","text":["Max review length: 2494\n","Min review length: 11\n"]}]},{"cell_type":"code","source":["# предобработка данных\n","from tensorflow.keras.utils import pad_sequences\n","max_words = 500\n","X_train = pad_sequences(X_train, maxlen=max_words, value=0, padding='pre', truncating='post')\n","X_test = pad_sequences(X_test, maxlen=max_words, value=0, padding='pre', truncating='post')"],"metadata":{"id":"XdwfZ6W3EnPW","executionInfo":{"status":"ok","timestamp":1765316656611,"user_tz":-180,"elapsed":917,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}}},"execution_count":14,"outputs":[]},{"cell_type":"code","source":["print(\"Max review length:\", len(max(X_train, key=len)))\n","print(\"Min review length:\", len(min(X_train, key=len)))"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"_ioXpqEbEtb7","executionInfo":{"status":"ok","timestamp":1765316681395,"user_tz":-180,"elapsed":6,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"40a46ef6-8a3c-48a0-ef7f-b0ca496b6af3"},"execution_count":15,"outputs":[{"output_type":"stream","name":"stdout","text":["Max review length: 500\n","Min review length: 500\n"]}]},{"cell_type":"code","source":["review_indices = X_train[19]\n","print(\"Review - index:\\n\", review_indices)\n","\n","review_text = \" \".join(id_to_word.get(i, \"?\") for i in review_indices)\n","print(\"\\nReview - text:\\n\", review_text)\n","\n","print(\"\\nReview length:\", len(review_indices))\n","\n","label = y_train[19]\n","class_name = \"Positive\" if label == 1 else \"Negative\"\n","print(\"Class label:\", label, \"| Class name:\", class_name)"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"vOAcIjEkE8K6","executionInfo":{"status":"ok","timestamp":1765316741347,"user_tz":-180,"elapsed":16,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"e0a4954a-086e-4096-d58d-227323934389"},"execution_count":16,"outputs":[{"output_type":"stream","name":"stdout","text":["Review - index:\n"," [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"," 0 0 1 608 50 26 84 37 144 24 67 14 20 10\n"," 10 300 92 67 12 48 25 92 40 2006 42 328 1285 241\n"," 92 40 12 48 25 188 4154 34 4 2 342 92 67 12\n"," 48 25 181 6 622 3783 20 10 10 4 360 7 25 521\n"," 92 1135 8 67 736 349 45 163 45 2812 45 6 1917 2\n"," 7 175 78 3783 4896 573 8 132 2552 2 83 4715 312 1285\n"," 92 2457 4 3028 11 3850 364 1317 253 7 2 2 1022 4106\n"," 5 4391 2 17 73 17 6 378 7 1139 4139 531 34 2\n"," 3409 5 2 2 52 8 67 4841 2 397 157 99 13 1498\n"," 32 4 96 143 1254 2 643 916 21 52]\n","\n","Review - text:\n"," ok there are people who should not see this movie br br 1 don't see it if you don't like satire or black humour 2 don't like it if you got offended by the 3 don't see it if you want a serious superhero movie br br the rest of you run don't walk to see mystery men it's funny it's quirky it's a delightful of every bad superhero cliche known to man occasional into junior high humour don't ruin the tongue in cheek low key fun of ben stiller and hank as well as a couple of amusing smaller parts by rush and good to see louise getting work too i laughed all the way through utterly somewhat weird but good\n","\n","Review length: 500\n","Class label: 1 | Class name: Positive\n"]}]},{"cell_type":"code","source":["print(\"Preprocessed training set X_train (first 3 examples):\")\n","print(X_train[:3])\n","\n","print(\"\\nPreprocessed training set X_test (first 3 examples):\")\n","print(X_test[:3])\n","\n","\n","print(\"Size of X_train:\", X_train.shape)\n","print(\"Size of y_train:\", y_train.shape)\n","print(\"Size of X_test:\", X_test.shape)\n","print(\"Size of y_test:\", y_test.shape)"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"e1-rP9DOFo0M","executionInfo":{"status":"ok","timestamp":1765317120012,"user_tz":-180,"elapsed":15,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"7346eb8b-c4c2-4b51-df86-292c841e9c0f"},"execution_count":21,"outputs":[{"output_type":"stream","name":"stdout","text":["Preprocessed training set X_train (first 3 examples):\n","[[ 0 0 0 ... 8 591 1462]\n"," [ 0 0 0 ... 28 35 585]\n"," [ 0 0 0 ... 11 2 2]]\n","\n","Preprocessed training set X_test (first 3 examples):\n","[[ 0 0 0 ... 14 356 22]\n"," [ 0 0 0 ... 301 87 22]\n"," [ 0 0 0 ... 46 7 158]]\n","Size of X_train: (25000, 500)\n","Size of y_train: (25000,)\n","Size of X_test: (25000, 500)\n","Size of y_test: (25000,)\n"]}]},{"cell_type":"code","source":["from tensorflow.keras.models import Sequential\n","from tensorflow.keras.layers import Embedding, LSTM, Dropout, Dense\n","\n","\n","model = Sequential()\n","model.add(Embedding(\n"," input_dim=vocabulary_size + index_from,\n"," output_dim=32,\n"," input_length=max_words\n","))\n","model.add(LSTM(67))\n","model.add(Dropout(0.5))\n","model.add(Dense(1, activation='sigmoid'))\n","\n","model.compile(\n"," loss='binary_crossentropy',\n"," optimizer='adam',\n"," metrics=['accuracy']\n",")\n","\n","model.build(input_shape=(None, max_words))\n","model.summary()\n","\n","# Обучение модели\n","history = model.fit(\n"," X_train,\n"," y_train,\n"," epochs=5,\n"," batch_size=64,\n"," validation_split=0.2,\n"," verbose=1\n",")"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":485},"id":"gx2Lz72DGfzA","executionInfo":{"status":"ok","timestamp":1765317634152,"user_tz":-180,"elapsed":56373,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"50a5e229-abf6-4c48-b279-586009918627"},"execution_count":22,"outputs":[{"output_type":"stream","name":"stderr","text":["/usr/local/lib/python3.12/dist-packages/keras/src/layers/core/embedding.py:97: UserWarning: Argument `input_length` is deprecated. Just remove it.\n"," warnings.warn(\n"]},{"output_type":"display_data","data":{"text/plain":["\u001b[1mModel: \"sequential\"\u001b[0m\n"],"text/html":["
Model: \"sequential\"\n","
\n"]},"metadata":{}},{"output_type":"display_data","data":{"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;34m500\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m160,096\u001b[0m │\n","├─────────────────────────────────┼────────────────────────┼───────────────┤\n","│ lstm (\u001b[38;5;33mLSTM\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m67\u001b[0m) │ \u001b[38;5;34m26,800\u001b[0m │\n","├─────────────────────────────────┼────────────────────────┼───────────────┤\n","│ dropout (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m67\u001b[0m) │ \u001b[38;5;34m0\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;34m68\u001b[0m │\n","└─────────────────────────────────┴────────────────────────┴───────────────┘\n"],"text/html":["
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n","┃ Layer (type)                     Output Shape                  Param # ┃\n","┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n","│ embedding (Embedding)           │ (None, 500, 32)        │       160,096 │\n","├─────────────────────────────────┼────────────────────────┼───────────────┤\n","│ lstm (LSTM)                     │ (None, 67)             │        26,800 │\n","├─────────────────────────────────┼────────────────────────┼───────────────┤\n","│ dropout (Dropout)               │ (None, 67)             │             0 │\n","├─────────────────────────────────┼────────────────────────┼───────────────┤\n","│ dense (Dense)                   │ (None, 1)              │            68 │\n","└─────────────────────────────────┴────────────────────────┴───────────────┘\n","
\n"]},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["\u001b[1m Total params: \u001b[0m\u001b[38;5;34m186,964\u001b[0m (730.33 KB)\n"],"text/html":["
 Total params: 186,964 (730.33 KB)\n","
\n"]},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m186,964\u001b[0m (730.33 KB)\n"],"text/html":["
 Trainable params: 186,964 (730.33 KB)\n","
\n"]},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n"],"text/html":["
 Non-trainable params: 0 (0.00 B)\n","
\n"]},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Epoch 1/5\n","\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m13s\u001b[0m 25ms/step - accuracy: 0.6426 - loss: 0.6635 - val_accuracy: 0.6048 - val_loss: 0.6939\n","Epoch 2/5\n","\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m17s\u001b[0m 24ms/step - accuracy: 0.5151 - loss: 0.7202 - val_accuracy: 0.6084 - val_loss: 0.6766\n","Epoch 3/5\n","\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m7s\u001b[0m 23ms/step - accuracy: 0.5620 - loss: 0.6804 - val_accuracy: 0.7786 - val_loss: 0.5682\n","Epoch 4/5\n","\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m10s\u001b[0m 22ms/step - accuracy: 0.7489 - loss: 0.5362 - val_accuracy: 0.7468 - val_loss: 0.5106\n","Epoch 5/5\n","\u001b[1m313/313\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m8s\u001b[0m 24ms/step - accuracy: 0.8451 - loss: 0.3959 - val_accuracy: 0.8556 - val_loss: 0.3406\n"]}]},{"cell_type":"code","source":["test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)\n","\n","print(\"Classification results\")\n","print(f\"Test accuracy: {test_accuracy:.4f}\")"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"izA_1WRRI5ba","executionInfo":{"status":"ok","timestamp":1765317817014,"user_tz":-180,"elapsed":7047,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"8445ec24-49ae-4868-c80a-31bcbb0b6baf"},"execution_count":23,"outputs":[{"output_type":"stream","name":"stdout","text":["Classification results\n","Test accuracy: 0.8519\n"]}]},{"cell_type":"code","source":["y_score = model.predict(X_test)\n","y_pred = [1 if y_score[i,0]>=0.5 else 0 for i in range(len(y_score))]\n","from sklearn.metrics import classification_report\n","print(classification_report(y_test, y_pred, labels = [0, 1], target_names=['Negative', 'Positive']))"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"S8NHPV47JkoZ","executionInfo":{"status":"ok","timestamp":1765317966588,"user_tz":-180,"elapsed":10535,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"4ea613c3-c530-4b0c-c32b-cb6871ac16f5"},"execution_count":24,"outputs":[{"output_type":"stream","name":"stdout","text":["\u001b[1m782/782\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m9s\u001b[0m 12ms/step\n"," precision recall f1-score support\n","\n"," Negative 0.85 0.86 0.85 12500\n"," Positive 0.85 0.85 0.85 12500\n","\n"," accuracy 0.85 25000\n"," macro avg 0.85 0.85 0.85 25000\n","weighted avg 0.85 0.85 0.85 25000\n","\n"]}]},{"cell_type":"code","source":["from sklearn.metrics import roc_curve, auc\n","import matplotlib.pyplot as plt\n","fpr, tpr, thresholds = roc_curve(y_test, y_score)\n","plt.plot(fpr, tpr)\n","plt.grid()\n","plt.xlabel('False Positive Rate')\n","plt.ylabel('True Positive Rate')\n","plt.title('ROC')\n","plt.show()\n","print('Area under ROC is', auc(fpr, tpr))"],"metadata":{"id":"CdHMBHqYKR7V","executionInfo":{"status":"ok","timestamp":1765318141382,"user_tz":-180,"elapsed":498,"user":{"displayName":"Мирон Романов","userId":"18135774377279153892"}},"outputId":"d5b940b0-a18e-40ba-c87c-51297487ab85","colab":{"base_uri":"https://localhost:8080/","height":489}},"execution_count":25,"outputs":[{"output_type":"display_data","data":{"text/plain":["
"],"image/png":"iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUrRJREFUeJzt3Xl4E+XaBvA7SbN0XyxdKZZ9kR2EUxYRZHNBOCqg8AGi4lGocqwoi0AFhaooB46iKIgVXMBdjiBQiiggCgJlpywtInSBUiBds77fH2kDsS00Jck0yf27rl4mk5nJk4dCbmfemVcmhBAgIiIi8hByqQsgIiIiciSGGyIiIvIoDDdERETkURhuiIiIyKMw3BAREZFHYbghIiIij8JwQ0RERB6F4YaIiIg8CsMNEREReRSGGyIiIvIoDDdEVK+kpqZCJpNZf3x8fBAbG4tHH30U586dq7K+EAKrVq3CHXfcgZCQEPj5+aFdu3aYO3cuSkpKanyfb7/9FnfffTfCw8OhUqkQExODESNGYMuWLc78eETkAjLOLUVE9UlqairGjx+PuXPnonHjxigvL8dvv/2G1NRUxMfH49ChQ9BoNAAAk8mEUaNG4YsvvkDv3r3xwAMPwM/PD9u2bcNnn32GNm3aYPPmzYiMjLTuXwiBxx57DKmpqejUqRMeeughREVFITc3F99++y327NmDHTt2oEePHlK1gIhuliAiqkc++ugjAUDs3r3bZvnUqVMFALFmzRrrsvnz5wsAYsqUKVX2s3btWiGXy8XgwYNtli9YsEAAEP/+97+F2Wyust3KlSvF77//7qBPQ0RS4GkpInILvXv3BgCcOnUKAFBWVoYFCxagRYsWSElJqbL+kCFDMG7cOGzYsAG//fabdZuUlBS0atUKb775JmQyWZXtxowZg27dujnxkxCRszHcEJFbOH36NAAgNDQUALB9+3ZcunQJo0aNgo+PT7XbjB07FgDwww8/WLcpLCzEqFGjoFAonF80EUmi+n8RiIgkduXKFRQUFKC8vBy///475syZA7Vajfvuuw8AcOTIEQBAhw4datxH5WtHjx61+W+7du2cWToRSYzhhojqpf79+9s8j4+PxyeffIKGDRsCAIqKigAAgYGBNe6j8jWtVmvz3+ttQ0Tuj+GGiOqlJUuWoEWLFrhy5QpWrFiBX375BWq12vp6ZUCpDDnV+XsACgoKuuE2ROT+OOaGiOqlbt26oX///njwwQexdu1atG3bFqNGjUJxcTEAoHXr1gCAAwcO1LiPytfatGkDAGjVqhUA4ODBg84snYgkxnBDRPWeQqFASkoKcnJy8M477wAAevXqhZCQEHz22WcwmUzVbrdy5UoAsI7T6dWrF0JDQ/H555/XuA0RuT+GGyJyC3feeSe6deuGRYsWoby8HH5+fpgyZQoyMzPx0ksvVVl/3bp1SE1NxaBBg/CPf/wDAODn54epU6fi6NGjmDp1KkQ19zD95JNPsGvXLqd/HiJyHo65ISK38cILL2D48OFITU3FU089hWnTpmHfvn14/fXXsXPnTjz44IPw9fXF9u3b8cknn6B169b4+OOPq+zj8OHDeOutt/DTTz9Z71Ccl5eH7777Drt27cKvv/4q0SckIkfg9AtEVK9UTr+we/dudO3a1eY1s9mMFi1aAAAyMzOhUChgNpuxcuVKLF++HAcPHoRer0fTpk0xYsQIPP/88/D396/2fb7++mt88MEH+OOPP6DVatGgQQPccccdePrpp9GnTx+nf04ich6GGyIiIvIoHHNDREREHoXhhoiIiDwKww0RERF5FIYbIiIi8igMN0RERORRGG6IiIjIo3jdTfzMZjNycnIQGBgImUwmdTlERERUC0IIFBUVISYmBnL59Y/NeF24ycnJQVxcnNRlEBERUR389ddfaNiw4XXX8bpwExgYCMDSnKCgIIfu22AwYNOmTRg4cCCUSqVD901Xsc+uwT67BvvsOuy1azirz1qtFnFxcdbv8evxunBTeSoqKCjIKeHGz88PQUFB/IvjROyza7DPrsE+uw577RrO7nNthpRwQDERERF5FIYbIiIi8igMN0RERORRGG6IiIjIozDcEBERkUdhuCEiIiKPwnBDREREHoXhhoiIiDwKww0RERF5FIYbIiIi8iiShptffvkFQ4YMQUxMDGQyGb777rsbbrN161Z07twZarUazZo1Q2pqqtPrJCIiIvchabgpKSlBhw4dsGTJklqtn52djXvvvRd9+/ZFRkYG/v3vf+OJJ57Axo0bnVwpERERuQtJJ868++67cffdd9d6/aVLl6Jx48Z46623AACtW7fG9u3b8Z///AeDBg1yVplEREReyWwWMAsBkxAwm2F9rDOYYRYCQgAClf8FhBAwGIy4ope2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gNdffx1r1qxBZmYmpk2bhoyMDEyePBkAsHDhQnz++ec4duwYjh8/ji+//BJRUVHV3ngwIiICvr6+2LBhA/Lz83HlypUa33f06NFYt24dVqxYYR1IXGn27NlYuXIl5syZg8OHD+Po0aNYvXo1Zs6caddnS0hIQPv27TF//nwAQPPmzfHHH39g48aNOH78OGbNmoXdu3fbbBMfH48DBw4gMzMTBQUFMBgMGD16NMLDwzF06FBs27YN2dnZ2Lp1K5599lmcPXvWrpqIPJLUg36IyPGqG4RaaeHChSI6Olr4+vqKQYMGiZUrVwoA4tKlS0II2wG/Op1OPPzwwyIuLk6oVCoRExMjEhMTbQYL79q1SwwYMEAEBAQIf39/0b59+yoDgq/19wHFf2cymcTLL78sYmNjhVKpFB06dBA//vij9fUPPvhAdOzYUfj7+4ugoCBx1113ib1791pfxzUDioUQYtmyZSIuLk7I5XLRp0+fGvtjMplEdHS0ACBOnTpVpa4NGzaIHj16CF9fXxEUFCS6desmPvjggxo/R3JysujQoUOV5Z9//rlQq9XizJkzory8XDz66KMiODhYhISEiKefflpMmzbNZrvz589b+wtA/PTTT0IIIXJzc8XYsWNFeHi4UKvVokmTJmLChAniypUrNdZE5C1kQgghbbwiIiIichyeliIiIiKPwnBDREREHoXhhoiIiDwKww0RERF5FIYbIiIi8igMN0RERORRGG6IiIjIozDcEBERkUdhuCEiIiKPwnBDREREHoXhhoiIiDwKww0RERF5lP8HBB2Rx51Rfc4AAAAASUVORK5CYII=\n"},"metadata":{}},{"output_type":"stream","name":"stdout","text":["Area under ROC is 0.925042592\n"]}]}]} \ No newline at end of file diff --git a/labworks/LW4/model.png b/labworks/LW4/model.png new file mode 100644 index 0000000..8c7a3c9 Binary files /dev/null and b/labworks/LW4/model.png differ diff --git a/labworks/LW4/report.md b/labworks/LW4/report.md new file mode 100644 index 0000000..924e97d --- /dev/null +++ b/labworks/LW4/report.md @@ -0,0 +1,339 @@ +# Отчёт по лабораторной работе №4 +Романов Мирон, Юсуфов Юнус, А-01-22 +Бригада №9 + +### Подготовка среды + +```python +import os +os.chdir('/content/drive/MyDrive/Colab Notebooks/IS_LR4') +``` + +## 1) В среде Google Colab создали новый блокнот (notebook). Настроили блокнот для работы с аппаратным ускорителем GPU + +```python +import tensorflow as tf +device_name = tf.test.gpu_device_name() +if device_name != '/device:GPU:0': + raise SystemError('GPU device not found') +print('Found GPU at: {}'.format(device_name)) +``` +``` +Found GPU at: /device:GPU:0 +``` + +## 2) Загрузили набор данных IMDb, содержащий оцифрованные отзывы на фильмы, размеченные на два класса: позитивные и негативные + +```python +# загрузка датасета +from keras.datasets import imdb +vocabulary_size = 5000 +index_from = 3 +(X_train, y_train), (X_test, y_test) = imdb.load_data(path="imdb.npz", + num_words=vocabulary_size, + skip_top=0, + maxlen=None, + seed=35, + start_char=1, + oov_char=2, + index_from=index_from + ) +``` + +``` +Shape of X train: (25000,) +Shape of y train: (25000,) +Shape of X test: (25000,) +Shape of y test: (25000,) +``` + +## 3) Вывесли один отзыв из обучающего множества в виде списка индексов слов. Преобразовали список индексов в текст и вывести отзыв в виде текста. Вывели длину отзыва. Вывели метку класса данного отзыва и название класса (1 – Positive, 0 – Negative) + +```python +# создание словаря для перевода индексов в слова +# заргузка словаря "слово:индекс" +word_to_id = imdb.get_word_index() +# уточнение словаря +word_to_id = {key:(value + index_from) for key,value in word_to_id.items()} +word_to_id[""] = 0 +word_to_id[""] = 1 +word_to_id[""] = 2 +word_to_id[""] = 3 +# создание обратного словаря "индекс:слово" +id_to_word = {value:key for key,value in word_to_id.items()} + +review_indices = X_train[19] +print("Review - index:\n", review_indices) + +review_text = " ".join(id_to_word.get(i, "?") for i in review_indices) +print("\nReview - text:\n", review_text) + +print("\nReview length:", len(review_indices)) + +label = y_train[19] +class_name = "Positive" if label == 1 else "Negative" +print("Class label:", label, "| Class name:", class_name) +``` + +``` +Review - index: + [1, 608, 50, 26, 84, 37, 144, 24, 67, 14, 20, 10, 10, 300, 92, 67, 12, 48, 25, 92, 40, 2006, 42, 328, 1285, 241, 92, 40, 12, 48, 25, 188, 4154, 34, 4, 2, 342, 92, 67, 12, 48, 25, 181, 6, 622, 3783, 20, 10, 10, 4, 360, 7, 25, 521, 92, 1135, 8, 67, 736, 349, 45, 163, 45, 2812, 45, 6, 1917, 2, 7, 175, 78, 3783, 4896, 573, 8, 132, 2552, 2, 83, 4715, 312, 1285, 92, 2457, 4, 3028, 11, 3850, 364, 1317, 253, 7, 2, 2, 1022, 4106, 5, 4391, 2, 17, 73, 17, 6, 378, 7, 1139, 4139, 531, 34, 2, 3409, 5, 2, 2, 52, 8, 67, 4841, 2, 397, 157, 99, 13, 1498, 32, 4, 96, 143, 1254, 2, 643, 916, 21, 52] + +Review - text: + ok there are people who should not see this movie br br 1 don't see it if you don't like satire or black humour 2 don't like it if you got offended by the 3 don't see it if you want a serious superhero movie br br the rest of you run don't walk to see mystery men it's funny it's quirky it's a delightful of every bad superhero cliche known to man occasional into junior high humour don't ruin the tongue in cheek low key fun of ben stiller and hank as well as a couple of amusing smaller parts by rush and good to see louise getting work too i laughed all the way through utterly somewhat weird but good + +Review length: 134 +Class label: 1 | Class name: Positive +``` + +## 4) Вывели максимальную и минимальную длину отзыва в обучающем множестве + +```python +print("Max review length:", len(max(X_train, key=len))) +print("Min review length:", len(min(X_train, key=len))) +``` + +``` +Max review length: 2494 +Min review length: 11 +``` + +## 5) Провели предобработку данных + +```python +# предобработка данных +from tensorflow.keras.utils import pad_sequences +max_words = 500 +X_train = pad_sequences(X_train, maxlen=max_words, value=0, padding='pre', truncating='post') +X_test = pad_sequences(X_test, maxlen=max_words, value=0, padding='pre', truncating='post') +``` + +## 6) Повторили п. 4 + +```python +print("Max review length:", len(max(X_train, key=len))) +print("Min review length:", len(min(X_train, key=len))) +``` + +``` +Max review length: 500 +Min review length: 500 +``` + +## 7) Повторили п. 3. Сделали вывод о том, как отзыв преобразовался после предобработки + +```python +review_indices = X_train[19] +print("Review - index:\n", review_indices) + +review_text = " ".join(id_to_word.get(i, "?") for i in review_indices) +print("\nReview - text:\n", review_text) + +print("\nReview length:", len(review_indices)) + +label = y_train[19] +class_name = "Positive" if label == 1 else "Negative" +print("Class label:", label, "| Class name:", class_name) +``` + +``` +Review - index: + [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 1 608 50 26 84 37 144 24 67 14 20 10 + 10 300 92 67 12 48 25 92 40 2006 42 328 1285 241 + 92 40 12 48 25 188 4154 34 4 2 342 92 67 12 + 48 25 181 6 622 3783 20 10 10 4 360 7 25 521 + 92 1135 8 67 736 349 45 163 45 2812 45 6 1917 2 + 7 175 78 3783 4896 573 8 132 2552 2 83 4715 312 1285 + 92 2457 4 3028 11 3850 364 1317 253 7 2 2 1022 4106 + 5 4391 2 17 73 17 6 378 7 1139 4139 531 34 2 + 3409 5 2 2 52 8 67 4841 2 397 157 99 13 1498 + 32 4 96 143 1254 2 643 916 21 52] + +Review - text: + ok there are people who should not see this movie br br 1 don't see it if you don't like satire or black humour 2 don't like it if you got offended by the 3 don't see it if you want a serious superhero movie br br the rest of you run don't walk to see mystery men it's funny it's quirky it's a delightful of every bad superhero cliche known to man occasional into junior high humour don't ruin the tongue in cheek low key fun of ben stiller and hank as well as a couple of amusing smaller parts by rush and good to see louise getting work too i laughed all the way through utterly somewhat weird but good + +Review length: 500 +Class label: 1 | Class name: Positive +``` + +``` +После предобработки длина всех отзывов была приведена к 500 словам. Отзывы с меньшим количеством слов, чем 500, были дополнены нулями. +``` + +## 8) Вывели предобработанные массивы обучающих и тестовых данных и их размерности + +```python +print("Preprocessed training set X_train (first 3 examples):") +print(X_train[:3]) + +print("\nPreprocessed training set X_test (first 3 examples):") +print(X_test[:3]) + + +print("Size of X_train:", X_train.shape) +print("Size of y_train:", y_train.shape) +print("Size of X_test:", X_test.shape) +print("Size of y_test:", y_test.shape) +``` + +``` +Preprocessed training set X_train (first 3 examples): +[[ 0 0 0 ... 8 591 1462] + [ 0 0 0 ... 28 35 585] + [ 0 0 0 ... 11 2 2]] + +Preprocessed training set X_test (first 3 examples): +[[ 0 0 0 ... 14 356 22] + [ 0 0 0 ... 301 87 22] + [ 0 0 0 ... 46 7 158]] +Size of X_train: (25000, 500) +Size of y_train: (25000,) +Size of X_test: (25000, 500) +Size of y_test: (25000,) +``` + +## 9) Реализовали модель рекуррентной нейронной сети, состоящей из слоев Embedding, LSTM, Dropout, Dense, и обучили ее на обучающих данных с выделением части обучающих данных в качестве валидационных. Вывели информацию об архитектуре нейронной сети. Добилсь качества обучения по метрике accuracy не менее 0.8. + +```python +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Embedding, LSTM, Dropout, Dense + + +model = Sequential() +model.add(Embedding( + input_dim=vocabulary_size + index_from, + output_dim=32, + input_length=max_words +)) +model.add(LSTM(67)) +model.add(Dropout(0.5)) +model.add(Dense(1, activation='sigmoid')) + +model.compile( + loss='binary_crossentropy', + optimizer='adam', + metrics=['accuracy'] +) + +model.build(input_shape=(None, max_words)) +model.summary() + +# Обучение модели +history = model.fit( + X_train, + y_train, + epochs=5, + batch_size=64, + validation_split=0.2, + verbose=1 +) +``` +![model](model.png) + +``` +Epoch 1/5 +313/313 ━━━━━━━━━━━━━━━━━━━━ 13s 25ms/step - accuracy: 0.6426 - loss: 0.6635 - val_accuracy: 0.6048 - val_loss: 0.6939 +Epoch 2/5 +313/313 ━━━━━━━━━━━━━━━━━━━━ 17s 24ms/step - accuracy: 0.5151 - loss: 0.7202 - val_accuracy: 0.6084 - val_loss: 0.6766 +Epoch 3/5 +313/313 ━━━━━━━━━━━━━━━━━━━━ 7s 23ms/step - accuracy: 0.5620 - loss: 0.6804 - val_accuracy: 0.7786 - val_loss: 0.5682 +Epoch 4/5 +313/313 ━━━━━━━━━━━━━━━━━━━━ 10s 22ms/step - accuracy: 0.7489 - loss: 0.5362 - val_accuracy: 0.7468 - val_loss: 0.5106 +Epoch 5/5 +313/313 ━━━━━━━━━━━━━━━━━━━━ 8s 24ms/step - accuracy: 0.8451 - loss: 0.3959 - val_accuracy: 0.8556 - val_loss: 0.3406 +``` + +## Вывели значение метрики качества классификации на тестовых +данных + +```python +test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0) + +print("Classification results") +print(f"Test accuracy: {test_accuracy:.4f}") +``` + +``` +Classification results +Test accuracy: 0.8519 +``` + +## Вывели отчет о качестве классификации тестовой выборки + +```python +y_score = model.predict(X_test) +y_pred = [1 if y_score[i,0]>=0.5 else 0 for i in range(len(y_score))] +from sklearn.metrics import classification_report +print(classification_report(y_test, y_pred, labels = [0, 1], target_names=['Negative', 'Positive'])) +``` + +``` + precision recall f1-score support + + Negative 0.85 0.86 0.85 12500 + Positive 0.85 0.85 0.85 12500 + + accuracy 0.85 25000 + macro avg 0.85 0.85 0.85 25000 +weighted avg 0.85 0.85 0.85 25000 +``` + +## Построили ROC-кривую по результату обработки тестовой +выборки и вычислить площадь под ROC-кривой (AUC ROC) + +```python +from sklearn.metrics import roc_curve, auc +import matplotlib.pyplot as plt +fpr, tpr, thresholds = roc_curve(y_test, y_score) +plt.plot(fpr, tpr) +plt.grid() +plt.xlabel('False Positive Rate') +plt.ylabel('True Positive Rate') +plt.title('ROC') +plt.show() +print('Area under ROC is', auc(fpr, tpr)) +``` + + +![roc](roc.png) + +``` +Area under ROC is 0.925042592 +``` +## 11) Выводы по результатам применения рекуррентной нейронной сети. + +``` +В данной лабораторной работе была реализована и обучена рекуррентная нейронная сеть с использованием слоя LSTM для задачи классификации тональности отзывов на основе данных IMDb. После этапа предобработки, состоявшего из приведения длины текстов к одному размеру и преобразования слов в числовые индексы, модель смогла качественно решать поставленную задачу. + +Результаты эксперимента показали, что точность на тестовой выборке составляет примерно 86%, то есть примерно 86% отзывов классифицируются верно. Классификационный отчёт с метриками precision, recall и f1-score продемонстрировал, что модель одинаково хорошо справляется как с положительными, так и с отрицательными отзывами. + +Анализ ROC-кривой и значения AUC ROC (примерно 0.93) подтвердил высокую эффективность модели в различении двух классов отзывов. + +В итоге, применение LSTM-рекуррентной нейронной сети оказалось удачным выбором для анализа тональности текста. Модель хорошо выявляет смысловые зависимости в последовательностях и достигает высокого качества классификации, что делает её пригодной для таких практических задач, как фильтрация, анализ эмоциональной окраски и обработка пользовательских отзывов. +``` \ No newline at end of file diff --git a/labworks/LW4/roc.png b/labworks/LW4/roc.png new file mode 100644 index 0000000..6382126 Binary files /dev/null and b/labworks/LW4/roc.png differ