ckiias/lec4-6-nlp-blstm.ipynb

30 KiB
Raw Blame History

Инициализация Keras

torch был заменен на jax, так как с torch рекуррентные сети не работали

In [1]:
import os

os.environ["KERAS_BACKEND"] = "jax"
import keras

print(keras.__version__)
3.9.2

Загрузка данных для классификации с помощью глубоких сетей

В качестве набора данных используется набор отзывов к фильмам с сайта IMDB.

Набор включает 50 000 отзывов, половина из которых находится в обучающем наборе данных (x_train), а половина - в тестовом (x_valid).

Метки (y_train и y_valid) имеют бинарный характер и назначены в соответствии с этими 10-балльными оценками:

  • отзывы с четырьмя звездами или меньше считаются отрицательным (y = 0);
  • отзывы с семью звездами или больше считаются положительными (y = 1);
  • умеренные отзывы — с пятью или шестью звездами — не включались в набор данных, что упрощает задачу бинарной классификации.

Данные уже предобработаны для простоты работы с ними.

unique_words - в векторное пространство включается только слова, которые встречаются в корпусе не менее 10 000 раз.

max_length - максимальная длина отзыва (если больше, то обрезается, если меньше, то дополняется "пустыми" словами).

In [2]:
from keras.api.datasets import imdb
import os

unique_words = 10000
max_length = 100

output_dir = "tmp"
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

(X_train, y_train), (X_valid, y_valid) = imdb.load_data(num_words=unique_words)

Приведение отзывов к длине max_length (100)

padding и truncating - дополнение и обрезка отзывов начинается с начала (учитывается специфика затухания градиента в рекуррентных сетях)

In [3]:
from keras.api.preprocessing.sequence import pad_sequences

X_train = pad_sequences(X_train, maxlen=max_length, padding="pre", truncating="pre", value=0)
X_valid = pad_sequences(X_valid, maxlen=max_length, padding="pre", truncating="pre", value=0)

Формирование архитектуры глубокой рекуррентной двунаправленной LSTM сети

Первый слой (Embedding) выполняет векторизацию

In [4]:
from keras.api.models import Sequential
from keras.api.layers import InputLayer, Embedding, SpatialDropout1D, LSTM, Bidirectional, Dense

blstm_model = Sequential()
blstm_model.add(InputLayer(shape=(max_length,), dtype="float32"))
blstm_model.add(Embedding(unique_words, 64))
blstm_model.add(SpatialDropout1D(0.2))
blstm_model.add(Bidirectional(LSTM(256, dropout=0.2)))
blstm_model.add(Dense(1, activation="sigmoid"))

blstm_model.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                     Output Shape                  Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ embedding (Embedding)           │ (None, 100, 64)        │       640,000 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ spatial_dropout1d               │ (None, 100, 64)        │             0 │
│ (SpatialDropout1D)              │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ bidirectional (Bidirectional)   │ (None, 512)            │       657,408 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 1)              │           513 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 1,297,921 (4.95 MB)
 Trainable params: 1,297,921 (4.95 MB)
 Non-trainable params: 0 (0.00 B)

Обучение модели

Веса модели сохраняются в каталог tmp после каждой эпохи обучения с помощью callback-параметра

В дальнейшем веса можно загрузить

In [5]:
from keras.api.callbacks import ModelCheckpoint

blstm_model.compile(
    loss="binary_crossentropy",
    optimizer="adam",
    metrics=["accuracy"],
)

blstm_model.fit(
    X_train,
    y_train,
    batch_size=128,
    epochs=6,
    validation_data=(X_valid, y_valid),
    callbacks=[ModelCheckpoint(filepath=output_dir + "/blstm_weights.{epoch:02d}.keras")],
)
Epoch 1/6
196/196 ━━━━━━━━━━━━━━━━━━━━ 134s 682ms/step - accuracy: 0.6565 - loss: 0.6039 - val_accuracy: 0.8432 - val_loss: 0.3756
Epoch 2/6
196/196 ━━━━━━━━━━━━━━━━━━━━ 166s 848ms/step - accuracy: 0.8841 - loss: 0.2820 - val_accuracy: 0.8425 - val_loss: 0.3577
Epoch 3/6
196/196 ━━━━━━━━━━━━━━━━━━━━ 176s 902ms/step - accuracy: 0.9148 - loss: 0.2238 - val_accuracy: 0.8459 - val_loss: 0.3929
Epoch 4/6
196/196 ━━━━━━━━━━━━━━━━━━━━ 171s 875ms/step - accuracy: 0.9375 - loss: 0.1744 - val_accuracy: 0.8434 - val_loss: 0.3572
Epoch 5/6
196/196 ━━━━━━━━━━━━━━━━━━━━ 155s 790ms/step - accuracy: 0.9466 - loss: 0.1520 - val_accuracy: 0.8385 - val_loss: 0.4029
Epoch 6/6
196/196 ━━━━━━━━━━━━━━━━━━━━ 158s 807ms/step - accuracy: 0.9584 - loss: 0.1172 - val_accuracy: 0.8337 - val_loss: 0.4419
Out[5]:
<keras.src.callbacks.history.History at 0x3455e57f0>

Загрузка лучшей модели и оценка ее качества

Качество модели - 84.6 %.

In [6]:
blstm_model.load_weights(output_dir + "/blstm_weights.03.keras")
blstm_model.evaluate(X_valid, y_valid)
782/782 ━━━━━━━━━━━━━━━━━━━━ 86s 110ms/step - accuracy: 0.8449 - loss: 0.3976
Out[6]:
[0.3929494023323059, 0.8458799719810486]

Визуализация распределения вероятностей результатов модели на валидационной выборке

In [7]:
import matplotlib.pyplot as plt

plt.hist(blstm_model.predict(X_valid))
_ = plt.axvline(x=0.5, color="orange")
782/782 ━━━━━━━━━━━━━━━━━━━━ 85s 108ms/step