форкнуто от main/is_dnn
Родитель
c18567b465
Сommit
779e92c5fd
@ -0,0 +1,313 @@
|
||||
{
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": [],
|
||||
"gpuType": "T4"
|
||||
},
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"accelerator": "GPU"
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4W5M3DBiisqY"
|
||||
},
|
||||
"outputs": [],
|
||||
"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))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import os\n",
|
||||
"import random\n",
|
||||
"import numpy as np\n",
|
||||
"import tensorflow as tf\n",
|
||||
"from tensorflow import keras\n",
|
||||
"from tensorflow.keras import layers\n",
|
||||
"from tensorflow.keras.datasets import imdb\n",
|
||||
"from tensorflow.keras.preprocessing.sequence import pad_sequences\n",
|
||||
"from sklearn.metrics import classification_report, roc_curve, auc, roc_auc_score\n",
|
||||
"\n",
|
||||
"print(\"TensorFlow version:\", tf.__version__)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "k7GXIRZQtYzM"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"k = 5\n",
|
||||
"seed = 4 * k - 1\n",
|
||||
"\n",
|
||||
"vocabulary_size = 5000\n",
|
||||
"index_from = 3\n",
|
||||
"\n",
|
||||
"(X_train, y_train), (X_test, y_test) = imdb.load_data(\n",
|
||||
" path=\"imdb.npz\",\n",
|
||||
" num_words=vocabulary_size,\n",
|
||||
" skip_top=0,\n",
|
||||
" maxlen=None,\n",
|
||||
" seed=seed,\n",
|
||||
" start_char=1,\n",
|
||||
" oov_char=2,\n",
|
||||
" index_from=index_from\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Размеры: X_train={}, y_train={}, X_test={}, y_test={}\".format(\n",
|
||||
" X_train.shape, y_train.shape, X_test.shape, y_test.shape))\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "pbNyjOJItin3"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 3) Отобразить один отзыв в виде индексов и в виде текста; вывести длину и метку\n",
|
||||
"# Сначала подготовим словарь id->word\n",
|
||||
"word_to_id = imdb.get_word_index()\n",
|
||||
"word_to_id = {key: (value + index_from) for key, value in word_to_id.items()}\n",
|
||||
"word_to_id[\"<PAD>\"] = 0\n",
|
||||
"word_to_id[\"<START>\"] = 1\n",
|
||||
"word_to_id[\"<UNK>\"] = 2\n",
|
||||
"word_to_id[\"<UNUSED>\"] = 3\n",
|
||||
"id_to_word = {value: key for key, value in word_to_id.items()}\n",
|
||||
"\n",
|
||||
"some_index = 0 # индекс примера в X_train; при необходимости изменить\n",
|
||||
"review_indices = X_train[some_index]\n",
|
||||
"print(\"Отзыв (список индексов):\", review_indices)\n",
|
||||
"review_text = ' '.join(id_to_word.get(i, \"<UNK>\") for i in review_indices)\n",
|
||||
"print(\"\\nОтзыв (текст):\\n\", review_text)\n",
|
||||
"print(\"\\nДлина отзыва (число индексов):\", len(review_indices))\n",
|
||||
"print(\"Меткa класса (y):\", y_train[some_index],\n",
|
||||
" \"- название класса:\", (\"Positive\" if y_train[some_index] == 1 else \"Negative\"))\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "SVmnorZgt6Bv"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 4) Максимальная и минимальная длина отзыва в обучающем множестве\n",
|
||||
"max_len = len(max(X_train, key=len))\n",
|
||||
"min_len = len(min(X_train, key=len))\n",
|
||||
"print(\"Максимальная длина отзыва (в индексах):\", max_len)\n",
|
||||
"print(\"Минимальная длина отзыва (в индексах):\", min_len)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "qIRCcZdNuG3f"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 5) Предобработка — приведение к единой длине\n",
|
||||
"max_words = 500\n",
|
||||
"X_train_prep = pad_sequences(X_train, maxlen=max_words, value=0, padding='pre', truncating='post')\n",
|
||||
"X_test_prep = pad_sequences(X_test, maxlen=max_words, value=0, padding='pre', truncating='post')\n",
|
||||
"print(\"Форма X_train_prep:\", X_train_prep.shape)\n",
|
||||
"print(\"Форма X_test_prep:\", X_test_prep.shape)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nxrBn8g2uKFY"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 6) Проверка: максимальная и минимальная длина после предобработки\n",
|
||||
"print(\"После предобработки: длина\", X_train_prep.shape[1])"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Uvac7sCEuRPS"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 7) Показать тот же отзыв после предобработки и сравнить\n",
|
||||
"prep_review_indices = X_train_prep[some_index]\n",
|
||||
"print(\"Предобработанный отзыв (индексы):\", prep_review_indices)\n",
|
||||
"prep_review_text = ' '.join(id_to_word.get(i, \"<PAD>\") for i in prep_review_indices if i != 0)\n",
|
||||
"print(\"\\nПредобработанный отзыв (текст, без <PAD>):\\n\", prep_review_text)\n",
|
||||
"print(\"\\nДлина предобработанного отзыва:\", len(prep_review_indices))"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "0TM-1v-DuWtH"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 8) Вывести массивы и размерности\n",
|
||||
"print(\"X_train_prep shape:\", X_train_prep.shape)\n",
|
||||
"print(\"X_test_prep shape: \", X_test_prep.shape)\n",
|
||||
"print(\"y_train shape:\", y_train.shape)\n",
|
||||
"print(\"y_test shape: \", y_test.shape)\n",
|
||||
"# Показать первые 3 примера предобработанных входов и меток\n",
|
||||
"for i in range(3):\n",
|
||||
" print(f\"\\nПример {i} (индексы):\", X_train_prep[i][:300], \"...\")\n",
|
||||
" print(f\"Метка:\", y_train[i])"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "XxQ_xW3oubMs"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 9) Создание и обучение модели\n",
|
||||
"vocab_size = vocabulary_size\n",
|
||||
"embedding_dim = 32\n",
|
||||
"input_length = max_words\n",
|
||||
"\n",
|
||||
"model = keras.Sequential([\n",
|
||||
" layers.Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=input_length),\n",
|
||||
" layers.LSTM(64),\n",
|
||||
" layers.Dropout(0.3),\n",
|
||||
" layers.Dense(1, activation='sigmoid')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
|
||||
"model.summary()\n",
|
||||
"\n",
|
||||
"# Обучение с выделением валидации\n",
|
||||
"history = model.fit(\n",
|
||||
" X_train_prep, y_train,\n",
|
||||
" epochs=4,\n",
|
||||
" batch_size=64,\n",
|
||||
" validation_split=0.2,\n",
|
||||
" verbose=1\n",
|
||||
")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "uM62eg4duyBd"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# 10) Оценка качества на тестовой выборке\n",
|
||||
"# Оценка метрик\n",
|
||||
"eval_results = model.evaluate(X_test_prep, y_test, verbose=1)\n",
|
||||
"print(\"Результаты оценки на тесте (loss, accuracy):\", eval_results)\n",
|
||||
"\n",
|
||||
"# Получение \"сырых\" предсказаний и бинарных меток\n",
|
||||
"y_score = model.predict(X_test_prep)\n",
|
||||
"y_pred = [1 if y_score[i,0] >= 0.5 else 0 for i in range(len(y_score))]\n",
|
||||
"\n",
|
||||
"# Отчёт о качестве классификации\n",
|
||||
"print(\"\\nClassification report:\\n\")\n",
|
||||
"print(classification_report(y_test, y_pred, labels=[0,1], target_names=['Negative','Positive']))\n",
|
||||
"\n",
|
||||
"# ROC-кривая и AUC\n",
|
||||
"fpr, tpr, thresholds = roc_curve(y_test, y_score)\n",
|
||||
"roc_auc = auc(fpr, tpr)\n",
|
||||
"print(\"\\nAUC ROC (ручной вычисление):\", roc_auc)\n",
|
||||
"print(\"AUC ROC (sklearn):\", roc_auc_score(y_test, y_score))\n",
|
||||
"\n",
|
||||
"# Построение ROC-кривой (в Colab отобразится график)\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"plt.figure()\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()"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "332eGXS9vJNm"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"# ищем индекс отзыва, длина которого > 500\n",
|
||||
"long_index = None\n",
|
||||
"for i, review in enumerate(X_train):\n",
|
||||
" if len(review) > 500:\n",
|
||||
" long_index = i\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
"print(\"Найден индекс длинного отзыва:\", long_index)\n",
|
||||
"print(\"Исходная длина:\", len(X_train[long_index]))\n",
|
||||
"\n",
|
||||
"# исходные индексы\n",
|
||||
"orig = X_train[long_index]\n",
|
||||
"\n",
|
||||
"# преобразование индексов в текст\n",
|
||||
"def to_text(indices):\n",
|
||||
" return ' '.join(id_to_word.get(i, \"<UNK>\") for i in indices)\n",
|
||||
"\n",
|
||||
"print(\"\\nИсходный отзыв (первые 50 индексов):\")\n",
|
||||
"print(orig[:50])\n",
|
||||
"print(\"\\nТекст (первые 50 токенов):\")\n",
|
||||
"print(to_text(orig[:50]))\n",
|
||||
"\n",
|
||||
"print(\"\\nИсходный отзыв (последние 50 индексов):\")\n",
|
||||
"print(orig[-50:])\n",
|
||||
"print(\"\\nТекст (последние 50 токенов):\")\n",
|
||||
"print(to_text(orig[-50:]))\n",
|
||||
"\n",
|
||||
"# предобработанный вариант\n",
|
||||
"prep = X_train_prep[long_index]\n",
|
||||
"\n",
|
||||
"print(\"\\nПредобработанный отзыв (длина):\", len(prep))\n",
|
||||
"\n",
|
||||
"print(\"\\nПосле предобработки (первые 50 индексов):\")\n",
|
||||
"print(prep[:50])\n",
|
||||
"print(\"\\nТекст (первые 50 токенов после обрезания):\")\n",
|
||||
"print(to_text(prep[:50]))\n",
|
||||
"\n",
|
||||
"print(\"\\nПосле предобработки (последние 50 индексов):\")\n",
|
||||
"print(prep[-50:])\n",
|
||||
"print(\"\\nТекст (последние 50 токенов после обрезания):\")\n",
|
||||
"print(to_text(prep[-50:]))\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "XHUmdTXl_vKb"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,193 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""lw4.ipynb
|
||||
|
||||
Automatically generated by Colab.
|
||||
|
||||
Original file is located at
|
||||
https://colab.research.google.com/drive/10ShUhpE59sy5SmF1R--Qv5yyzRzMz1ic
|
||||
"""
|
||||
|
||||
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))
|
||||
|
||||
import os
|
||||
import random
|
||||
import numpy as np
|
||||
import tensorflow as tf
|
||||
from tensorflow import keras
|
||||
from tensorflow.keras import layers
|
||||
from tensorflow.keras.datasets import imdb
|
||||
from tensorflow.keras.preprocessing.sequence import pad_sequences
|
||||
from sklearn.metrics import classification_report, roc_curve, auc, roc_auc_score
|
||||
|
||||
print("TensorFlow version:", tf.__version__)
|
||||
|
||||
k = 5
|
||||
seed = 4 * k - 1
|
||||
|
||||
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=seed,
|
||||
start_char=1,
|
||||
oov_char=2,
|
||||
index_from=index_from
|
||||
)
|
||||
|
||||
print("Размеры: X_train={}, y_train={}, X_test={}, y_test={}".format(
|
||||
X_train.shape, y_train.shape, X_test.shape, y_test.shape))
|
||||
|
||||
# 3) Отобразить один отзыв в виде индексов и в виде текста; вывести длину и метку
|
||||
# Сначала подготовим словарь id->word
|
||||
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["<PAD>"] = 0
|
||||
word_to_id["<START>"] = 1
|
||||
word_to_id["<UNK>"] = 2
|
||||
word_to_id["<UNUSED>"] = 3
|
||||
id_to_word = {value: key for key, value in word_to_id.items()}
|
||||
|
||||
some_index = 0 # индекс примера в X_train; при необходимости изменить
|
||||
review_indices = X_train[some_index]
|
||||
print("Отзыв (список индексов):", review_indices)
|
||||
review_text = ' '.join(id_to_word.get(i, "<UNK>") for i in review_indices)
|
||||
print("\nОтзыв (текст):\n", review_text)
|
||||
print("\nДлина отзыва (число индексов):", len(review_indices))
|
||||
print("Меткa класса (y):", y_train[some_index],
|
||||
"- название класса:", ("Positive" if y_train[some_index] == 1 else "Negative"))
|
||||
|
||||
# 4) Максимальная и минимальная длина отзыва в обучающем множестве
|
||||
max_len = len(max(X_train, key=len))
|
||||
min_len = len(min(X_train, key=len))
|
||||
print("Максимальная длина отзыва (в индексах):", max_len)
|
||||
print("Минимальная длина отзыва (в индексах):", min_len)
|
||||
|
||||
# 5) Предобработка — приведение к единой длине
|
||||
max_words = 500
|
||||
X_train_prep = pad_sequences(X_train, maxlen=max_words, value=0, padding='pre', truncating='post')
|
||||
X_test_prep = pad_sequences(X_test, maxlen=max_words, value=0, padding='pre', truncating='post')
|
||||
print("Форма X_train_prep:", X_train_prep.shape)
|
||||
print("Форма X_test_prep:", X_test_prep.shape)
|
||||
|
||||
# 6) Проверка: максимальная и минимальная длина после предобработки
|
||||
print("После предобработки: длина", X_train_prep.shape[1])
|
||||
|
||||
# 7) Показать тот же отзыв после предобработки и сравнить
|
||||
prep_review_indices = X_train_prep[some_index]
|
||||
print("Предобработанный отзыв (индексы):", prep_review_indices)
|
||||
prep_review_text = ' '.join(id_to_word.get(i, "<PAD>") for i in prep_review_indices if i != 0)
|
||||
print("\nПредобработанный отзыв (текст, без <PAD>):\n", prep_review_text)
|
||||
print("\nДлина предобработанного отзыва:", len(prep_review_indices))
|
||||
|
||||
# 8) Вывести массивы и размерности
|
||||
print("X_train_prep shape:", X_train_prep.shape)
|
||||
print("X_test_prep shape: ", X_test_prep.shape)
|
||||
print("y_train shape:", y_train.shape)
|
||||
print("y_test shape: ", y_test.shape)
|
||||
# Показать первые 3 примера предобработанных входов и меток
|
||||
for i in range(3):
|
||||
print(f"\nПример {i} (индексы):", X_train_prep[i][:300], "...")
|
||||
print(f"Метка:", y_train[i])
|
||||
|
||||
# 9) Создание и обучение модели
|
||||
vocab_size = vocabulary_size
|
||||
embedding_dim = 32
|
||||
input_length = max_words
|
||||
|
||||
model = keras.Sequential([
|
||||
layers.Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=input_length),
|
||||
layers.LSTM(64),
|
||||
layers.Dropout(0.3),
|
||||
layers.Dense(1, activation='sigmoid')
|
||||
])
|
||||
|
||||
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
|
||||
model.summary()
|
||||
|
||||
# Обучение с выделением валидации
|
||||
history = model.fit(
|
||||
X_train_prep, y_train,
|
||||
epochs=4,
|
||||
batch_size=64,
|
||||
validation_split=0.2,
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# 10) Оценка качества на тестовой выборке
|
||||
# Оценка метрик
|
||||
eval_results = model.evaluate(X_test_prep, y_test, verbose=1)
|
||||
print("Результаты оценки на тесте (loss, accuracy):", eval_results)
|
||||
|
||||
# Получение "сырых" предсказаний и бинарных меток
|
||||
y_score = model.predict(X_test_prep)
|
||||
y_pred = [1 if y_score[i,0] >= 0.5 else 0 for i in range(len(y_score))]
|
||||
|
||||
# Отчёт о качестве классификации
|
||||
print("\nClassification report:\n")
|
||||
print(classification_report(y_test, y_pred, labels=[0,1], target_names=['Negative','Positive']))
|
||||
|
||||
# ROC-кривая и AUC
|
||||
fpr, tpr, thresholds = roc_curve(y_test, y_score)
|
||||
roc_auc = auc(fpr, tpr)
|
||||
print("\nAUC ROC (ручной вычисление):", roc_auc)
|
||||
print("AUC ROC (sklearn):", roc_auc_score(y_test, y_score))
|
||||
|
||||
# Построение ROC-кривой (в Colab отобразится график)
|
||||
import matplotlib.pyplot as plt
|
||||
plt.figure()
|
||||
plt.plot(fpr, tpr)
|
||||
plt.grid()
|
||||
plt.xlabel('False Positive Rate')
|
||||
plt.ylabel('True Positive Rate')
|
||||
plt.title('ROC')
|
||||
plt.show()
|
||||
|
||||
# ищем индекс отзыва, длина которого > 500
|
||||
long_index = None
|
||||
for i, review in enumerate(X_train):
|
||||
if len(review) > 500:
|
||||
long_index = i
|
||||
break
|
||||
|
||||
print("Найден индекс длинного отзыва:", long_index)
|
||||
print("Исходная длина:", len(X_train[long_index]))
|
||||
|
||||
# исходные индексы
|
||||
orig = X_train[long_index]
|
||||
|
||||
# преобразование индексов в текст
|
||||
def to_text(indices):
|
||||
return ' '.join(id_to_word.get(i, "<UNK>") for i in indices)
|
||||
|
||||
print("\nИсходный отзыв (первые 50 индексов):")
|
||||
print(orig[:50])
|
||||
print("\nТекст (первые 50 токенов):")
|
||||
print(to_text(orig[:50]))
|
||||
|
||||
print("\nИсходный отзыв (последние 50 индексов):")
|
||||
print(orig[-50:])
|
||||
print("\nТекст (последние 50 токенов):")
|
||||
print(to_text(orig[-50:]))
|
||||
|
||||
# предобработанный вариант
|
||||
prep = X_train_prep[long_index]
|
||||
|
||||
print("\nПредобработанный отзыв (длина):", len(prep))
|
||||
|
||||
print("\nПосле предобработки (первые 50 индексов):")
|
||||
print(prep[:50])
|
||||
print("\nТекст (первые 50 токенов после обрезания):")
|
||||
print(to_text(prep[:50]))
|
||||
|
||||
print("\nПосле предобработки (последние 50 индексов):")
|
||||
print(prep[-50:])
|
||||
print("\nТекст (последние 50 токенов после обрезания):")
|
||||
print(to_text(prep[-50:]))
|
||||
|
После Ширина: | Высота: | Размер: 21 KiB |
@ -0,0 +1,328 @@
|
||||
## Лабораторная работа №4 ИС. Распознавание последовательностей.
|
||||
|
||||
#### Выполнили: Ишутина Е. И., Голубев Т. Л.
|
||||
|
||||
Проведена работа с отзывами с IMDb в виде последовательностей индексов и в человекочитаемом виде. Выполнена предобработка: все тексты приведены к единой длине путём дополнения / усечения, в результате чего получены матрицы одинаковой размерности, пригодные для подачи в нейронную сеть.
|
||||
|
||||
На основании предобработанных данных была построена рекуррентная модель, включавшая слои Embedding, LSTM, Dropout и полносвязный классификатор, и проведено её обучение с использованием доли обучающей выборки в качестве валидационной и проверка точности работы модели.
|
||||
|
||||
Перед началом работы импортированы все нужные модули:
|
||||
|
||||
```python
|
||||
# Блок импортов и общих настроек
|
||||
import os
|
||||
import random
|
||||
import numpy as np
|
||||
import tensorflow as tf
|
||||
from tensorflow import keras
|
||||
from tensorflow.keras import layers
|
||||
from tensorflow.keras.datasets import imdb
|
||||
from tensorflow.keras.preprocessing.sequence import pad_sequences
|
||||
from sklearn.metrics import classification_report, roc_curve, auc, roc_auc_score
|
||||
|
||||
print("TensorFlow version:", tf.__version__)
|
||||
```
|
||||
* `layers` предоставляет готовые типы слоёв (Embedding, LSTM, Dense и др.).
|
||||
* `imdb` загружает предобработанный набор IMDb в виде последовательностей индексов.
|
||||
* `pad_sequences` приводит последовательности к фиксированной длине путём дополнения или усечения.
|
||||
* `sklearn.metrics` функции для оценки качества классификации (отчёт, ROC, AUC).
|
||||
|
||||
#### *1. В среде Google Colab создать новый блокнот (notebook). Настроить блокнот для работы с аппаратным ускорителем GPU.*
|
||||
|
||||
```python
|
||||
# 1) Проверка наличия GPU (в Colab после выбора Runtime -> Change runtime type -> GPU)
|
||||
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))
|
||||
|
||||
```
|
||||
Вывод дает понять, что подключен аппаратный ускоритель на основе графического процессора (GPU):
|
||||
|
||||
```python
|
||||
Found GPU at: /device:GPU:0
|
||||
```
|
||||
#### *2. Загрузить набор данных IMDb, содержащий оцифрованные отзывы на фильмы, размеченные на два класса: позитивные и негативные. При загрузке набора данных параметр seed выбрать равным (4k–1), где k – номер бригады. Вывести размеры полученных обучающих и тестовых массивов данных.*
|
||||
|
||||
```python
|
||||
k = 5
|
||||
seed = 4 * k - 1
|
||||
|
||||
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=seed,
|
||||
start_char=1,
|
||||
oov_char=2,
|
||||
index_from=index_from
|
||||
)
|
||||
|
||||
print("Размеры: X_train={}, y_train={}, X_test={}, y_test={}".format(
|
||||
X_train.shape, y_train.shape, X_test.shape, y_test.shape))
|
||||
|
||||
```
|
||||
Функция загрузила по 25 000 примеров в обучающую и тестовую выборки, что и было выведено как формы массивов, потому что набор IMDb в keras был заранее организован в таких размерах. В выводе отображались одномерные массивы, поэтому после запятой не было указано никакой второй размерности.
|
||||
|
||||
```python
|
||||
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
|
||||
17464789/17464789 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step
|
||||
Размеры: X_train=(25000,), y_train=(25000,), X_test=(25000,), y_test=(25000,)
|
||||
```
|
||||
|
||||
#### *3. Вывести один отзыв из обучающего множества в виде списка индексов слов. Преобразовать список индексов в текст и вывести отзыв в виде текста. Вывести длину отзыва. Вывести метку класса данного отзыва и название класса (1 – Positive, 0 – Negative).*
|
||||
|
||||
```python
|
||||
# словарь id->word
|
||||
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["<PAD>"] = 0
|
||||
word_to_id["<START>"] = 1
|
||||
word_to_id["<UNK>"] = 2
|
||||
word_to_id["<UNUSED>"] = 3
|
||||
id_to_word = {value: key for key, value in word_to_id.items()}
|
||||
|
||||
some_index = 0
|
||||
review_indices = X_train[some_index]
|
||||
print("Отзыв (список индексов):", review_indices)
|
||||
review_text = ' '.join(id_to_word.get(i, "<UNK>") for i in review_indices)
|
||||
print("\nОтзыв (текст):\n", review_text)
|
||||
print("\nДлина отзыва (число индексов):", len(review_indices))
|
||||
print("Меткa класса (y):", y_train[some_index],
|
||||
"- название класса:", ("Positive" if y_train[some_index] == 1 else "Negative"))
|
||||
```
|
||||
Преобразование индексов в текст выполнялось через обратный словарь `id_to_word`, сформированный из `word_to_id` с учётом сдвига `index_from` и добавления служебных токенов. Для каждого индекса находилось соответствующее слово, а при отсутствии — подставлялся <UNK>. Полученные токены объединялись в строку, что давало текстовое отображение отзыва.
|
||||
|
||||
Токен — это минимальная единица текста, с которой работает модель: слово, символ, подслово или специальный маркер. В датасете IMDb токеном является слово, которому присвоен числовой индекс.
|
||||
|
||||
Числа в тензоре — это индексы токенов, то есть номера слов в словаре IMDb.
|
||||
Каждое число соответствует одному слову или служебному токену:
|
||||
|
||||
* 1 — START
|
||||
* 2 — UNK
|
||||
* 0 — PAD
|
||||
|
||||
остальные числа — реальные слова корпуса, закодированные целыми числами.
|
||||
|
||||
```python
|
||||
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb_word_index.json
|
||||
1641221/1641221 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step
|
||||
Отзыв (список индексов): [1, 48, 25, 181, 8, 106, 6, 147, 2, 20, 79, 1070, 7, 4, 2, 2063, 14, 2, ... 989, 143, 8, 4, 2, 15, 1838, 2, 10, 10, 13, 447, 175, 786, 7, 12]
|
||||
```
|
||||
```
|
||||
Отзыв (текст):
|
||||
<START> if you want to watch a real <UNK> movie get hold of the <UNK> formula this <UNK> film must have cost all of 50 to make it features a <UNK> thin script <UNK> bad sets lighting and camera work and a stop motion paper <UNK> monster that is utterly laughable it looks like they sometimes used a guy in a rubber suit and or a <UNK> <UNK> for the monster but all were equally dreadful br br the actors all speak their lines as though theyve never seen them before and are reading off a <UNK> the special effects are way beyond lousy and the only sad thing is that they dropped the really <UNK> original title <UNK> <UNK> which <UNK> up exactly what you get for the full 90 minutes br br this is what happens when you <UNK> the bottom of the <UNK> so hard you break through to the <UNK> that lies <UNK> br br i loved every minute of it
|
||||
|
||||
|
||||
Длина отзыва (число индексов): 165
|
||||
Меткa класса (y): 0 - название класса: Negative
|
||||
```
|
||||
|
||||
#### *4. Вывести максимальную и минимальную длину отзыва в обучающем множестве*
|
||||
|
||||
```python
|
||||
max_len = len(max(X_train, key=len))
|
||||
min_len = len(min(X_train, key=len))
|
||||
print("Максимальная длина отзыва (в индексах):", max_len)
|
||||
print("Минимальная длина отзыва (в индексах):", min_len)
|
||||
```
|
||||
Видно,что отзывы сильно различаются по объёму, и работать с ними в исходном виде невозможно. Нейронная сеть принимает тензоры фиксированной формы. Без выравнивания обучение невозможно, т.к. батч не может содержать последовательности разной длины.
|
||||
|
||||
```python
|
||||
Максимальная длина отзыва (в индексах): 2494
|
||||
Минимальная длина отзыва (в индексах): 11
|
||||
```
|
||||
#### *5. Провести предобработку данных. Выбрать единую длину, к которой будут приведены все отзывы. Короткие отзывы дополнить спецсимволами, а длинные обрезать до выбранной длины.*
|
||||
|
||||
```python
|
||||
max_words = 500
|
||||
X_train_prep = pad_sequences(X_train, maxlen=max_words, value=0, padding='pre', truncating='post')
|
||||
X_test_prep = pad_sequences(X_test, maxlen=max_words, value=0, padding='pre', truncating='post')
|
||||
print("Форма X_train_prep:", X_train_prep.shape)
|
||||
print("Форма X_test_prep:", X_test_prep.shape)
|
||||
```
|
||||
Все отзывы приведены к фиксированной длине 500. Это значение было выбрано как компромисс: оно достаточно большое, чтобы сохранить основное содержание средних и длинных отзывов, и достаточно маленькое, чтобы не перегружать память и ускорить обучение. При приведении длины короткие отзывы были дополнены токеном <PAD>, а слишком длинные — обрезаны справа.
|
||||
|
||||
```python
|
||||
Форма X_train_prep: (25000, 500)
|
||||
Форма X_test_prep: (25000, 500)
|
||||
```
|
||||
|
||||
#### *6. Повторить п. 4.*
|
||||
|
||||
```python
|
||||
print("После предобработки: длина", X_train_prep.shape[1])
|
||||
```
|
||||
|
||||
```python
|
||||
После предобработки: длина 500
|
||||
```
|
||||
|
||||
|
||||
#### *7. Повторить п. 3. Сделать вывод о том, как отзыв преобразовался после предобработки.*
|
||||
|
||||
```python
|
||||
prep_review_indices = X_train_prep[some_index]
|
||||
print("Предобработанный отзыв (индексы):", prep_review_indices)
|
||||
prep_review_text = ' '.join(id_to_word.get(i, "<PAD>") for i in prep_review_indices if i != 0)
|
||||
print("\nПредобработанный отзыв (текст, без <PAD>):\n", prep_review_text)
|
||||
print("\nДлина предобработанного отзыва:", len(prep_review_indices))
|
||||
```
|
||||
|
||||
В исходном отзыве оказалось 165 токенов, что меньше выбранной фиксированной длины 500, поэтому при предобработке произошло только дополнение слева токеном PAD, а обрезания не было.
|
||||
|
||||
```
|
||||
Предобработанный отзыв (индексы): [ 0 0 ... 175 786 7 12]
|
||||
|
||||
Предобработанный отзыв (текст, без <PAD>):
|
||||
<START> if you want to watch a real <UNK> movie get hold of the <UNK> formula this <UNK> film must have cost all of 50 to make it features a <UNK> thin script <UNK> bad sets lighting and camera work and a stop motion paper <UNK> monster that is utterly laughable it looks like they sometimes used a guy in a rubber suit and or a <UNK> <UNK> for the monster but all were equally dreadful br br the actors all speak their lines as though they've never seen them before and are reading off a <UNK> the special effects are way beyond lousy and the only sad thing is that they dropped the really <UNK> original title <UNK> <UNK> which <UNK> up exactly what you get for the full 90 minutes br br this is what happens when you <UNK> the bottom of the <UNK> so hard you break through to the <UNK> that lies <UNK> br br i loved every minute of it
|
||||
|
||||
Длина предобработанного отзыва: 500
|
||||
```
|
||||
Чтобы убедиться в корректности преобразования, найден и выведен отзыв с длиной больше 500, к которому применено обрезание (код для поиска опущен, длинные тензоры укорочены).
|
||||
|
||||
```
|
||||
Исходный отзыв (первые 50 индексов):
|
||||
[1, 2, 34, 1308, ... 532, 2, 18]
|
||||
Исходный отзыв (последние 50 индексов):
|
||||
[4, 323, 198, 89, ... 495]
|
||||
|
||||
После предобработки (первые 50 индексов):
|
||||
[ 1 2 34 1308 ... 532 2 18]
|
||||
После предобработки (последние 50 индексов):
|
||||
[ 2 501 ... 10 32]
|
||||
```
|
||||
Как видно, последние индексы теперь выглядят по-другому, то есть произошло обрезание. При выводе появляются пробелы, связанные с тем, что Python пытается выровнять столбцы у `tf.Tensor`.
|
||||
|
||||
#### *8. Вывести предобработанные массивы обучающих и тестовых данных и их размерности.*
|
||||
|
||||
|
||||
```python
|
||||
print("X_train_prep shape:", X_train_prep.shape)
|
||||
print("X_test_prep shape: ", X_test_prep.shape)
|
||||
print("y_train shape:", y_train.shape)
|
||||
print("y_test shape: ", y_test.shape)
|
||||
|
||||
for i in range(3):
|
||||
print(f"\nПример {i} (индексы):", X_train_prep[i][:300], "...")
|
||||
print(f"Метка:", y_train[i])
|
||||
```
|
||||
Слишком длинные последовательности индексов были укорочены.
|
||||
|
||||
```python
|
||||
X_train_prep shape: (25000, 500)
|
||||
X_test_prep shape: (25000, 500)
|
||||
y_train shape: (25000,)
|
||||
y_test shape: (25000,)
|
||||
|
||||
Пример 0 (индексы): [0 0 0 ... 0 0 0 0]
|
||||
Метка: 0
|
||||
|
||||
Пример 1 (индексы): [ 0 0 0 ... 11 585 2 11 940 11 4 2 720 11 4 4691]
|
||||
Метка: 1
|
||||
|
||||
Пример 2 (индексы): [ 0 0 ... 406 1522 29 186 8 412 27 113]
|
||||
Метка: 1
|
||||
```
|
||||
|
||||
|
||||
#### *9. Реализовать модель рекуррентной нейронной сети, состоящей из слоев Embedding, LSTM, Dropout, Dense, и обучить ее на обучающих данных с выделением части обучающих данных в качестве валидационных. Вывести информацию об архитектуре нейронной сети. Добиться качества обучения по метрике accuracy не менее 0.8.*
|
||||
|
||||
В этом пункте была реализована рекуррентная нейронная сеть для задачи бинарной классификации.
|
||||
|
||||
* Embedding преобразовал числовые индексы слов в плотные векторные представления размерности embedding_dim=32. Благодаря этому слова, имеющие близкий контекст, получали похожие векторы, что облегчало обучение модели.
|
||||
|
||||
* LSTM (Long Short-Term Memory) — рекуррентный слой с 64 единицами, он учитывал последовательный порядок слов, позволял модели «помнить» информацию из начала текста при обработке его конца.
|
||||
|
||||
* Dropout — слой регуляризации с вероятностью 0.3, случайным образом отключавший часть нейронов во время обучения.
|
||||
|
||||
* Dense — полносвязный слой с одним нейроном и сигмоидной активацией, который выдавал вероятность положительного класса (1 — Positive, 0 — Negative).
|
||||
|
||||
Обучение проводилось с использованием 80% данных для тренировки и 20% для валидации (validation_split=0.2).
|
||||
|
||||
```python
|
||||
vocab_size = vocabulary_size
|
||||
embedding_dim = 32
|
||||
input_length = max_words
|
||||
|
||||
model = keras.Sequential([
|
||||
layers.Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=input_length),
|
||||
layers.LSTM(64),
|
||||
layers.Dropout(0.3),
|
||||
layers.Dense(1, activation='sigmoid')
|
||||
])
|
||||
|
||||
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
|
||||
model.summary()
|
||||
|
||||
# Обучение с выделением валидации
|
||||
history = model.fit(
|
||||
X_train_prep, y_train,
|
||||
epochs=4,
|
||||
batch_size=64,
|
||||
validation_split=0.2,
|
||||
verbose=1
|
||||
)
|
||||
```
|
||||
|
||||
#### *10. Оценить качество обучения на тестовых данных: вывести значение метрики качества классификации на тестовых данных, вывести отчет о качестве классификации тестовой выборки, построить ROC-кривую по результату обработки тестовой выборки и вычислить площадь под ROC-кривой (AUC ROC).*
|
||||
|
||||
|
||||
```python
|
||||
eval_results = model.evaluate(X_test_prep, y_test, verbose=1)
|
||||
print("Результаты оценки на тесте (loss, accuracy):", eval_results)
|
||||
|
||||
# Получение "сырых" предсказаний и бинарных меток
|
||||
y_score = model.predict(X_test_prep)
|
||||
y_pred = [1 if y_score[i,0] >= 0.5 else 0 for i in range(len(y_score))]
|
||||
|
||||
print("\nClassification report:\n")
|
||||
print(classification_report(y_test, y_pred, labels=[0,1], target_names=['Negative','Positive']))
|
||||
|
||||
# ROC-кривая и AUC
|
||||
fpr, tpr, thresholds = roc_curve(y_test, y_score)
|
||||
roc_auc = auc(fpr, tpr)
|
||||
print("\nAUC ROC (ручной вычисление):", roc_auc)
|
||||
print("AUC ROC (sklearn):", roc_auc_score(y_test, y_score))
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
plt.figure()
|
||||
plt.plot(fpr, tpr)
|
||||
plt.grid()
|
||||
plt.xlabel('False Positive Rate')
|
||||
plt.ylabel('True Positive Rate')
|
||||
plt.title('ROC')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
```python
|
||||
782/782 ━━━━━━━━━━━━━━━━━━━━ 8s 10ms/step - accuracy: 0.8603 - loss: 0.3458
|
||||
Результаты оценки на тесте (loss, accuracy): [0.34332218766212463, 0.8626000285148621]
|
||||
782/782 ━━━━━━━━━━━━━━━━━━━━ 6s 8ms/step
|
||||
|
||||
Classification report:
|
||||
|
||||
precision recall f1-score support
|
||||
|
||||
Negative 0.83 0.92 0.87 12500
|
||||
Positive 0.91 0.81 0.85 12500
|
||||
|
||||
accuracy 0.86 25000
|
||||
macro avg 0.87 0.86 0.86 25000
|
||||
weighted avg 0.87 0.86 0.86 25000
|
||||
|
||||
|
||||
AUC ROC (ручной вычисление): 0.9381251584
|
||||
AUC ROC (sklearn): 0.9381251584
|
||||
```
|
||||

|
||||
|
||||
Идеальное значение площади под графиком — единица (100% угадывание положительных классов), а худший случай — 0,5 («вслепую»). Значение 0,93 показывает хорошую точность предсказаний.
|
||||
|
||||
#### *11. Сделать выводы по результатам применения рекуррентной нейронной сети для решения задачи определения тональности текста.*
|
||||
|
||||
В ходе лабораторной работы была построена рекуррентная нейронная сеть с LSTM для задачи бинарной классификации отзывов IMDb. Модель показала высокие показатели качества: точность на тестовой выборке достигла ≈86%, а площадь под ROC-кривой (AUC ROC ≈0.93) подтверждает способность модели надёжно различать положительные и отрицательные отзывы.
|
||||
Загрузка…
Ссылка в новой задаче