TF Keras Обработка текста – Модель классификации

Вопрос или проблема

Я пытаюсь составить скрипт, который классифицирует комментарии на адекватные и неадекватные. Я ранее задавал вопрос здесь с полным кодом, но думаю, что изолировал проблему в настройке модели, поэтому я его удалил и надеюсь, что это более упрощенный и понятный вариант. Пример, который я пытаюсь воспроизвести, – это классический комментарий IMDB, где комментарии либо положительные, либо отрицательные, но в моем случае – адекватные или нет. Мой токенизатор и очистка текста, а также дополнение работают хорошо, и у меня есть обучающий набор данных, который корректно возвращает токенизированные последовательности. Я думаю, что моя основная ошибка заключается в следующем:

model = tf.keras.Sequential([
        tf.keras.layers.Embedding(10000, 300),
        tf.keras.layers.GlobalAveragePooling1D(),
        tf.keras.layers.Dense(1, activation = 'sigmoid')])

model.summary()

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

es = tf.keras.callbacks.EarlyStopping(monitor="val_accuracy", mode="max")

callbacks = [es]
history = model.fit(train_seqs, train_df['adq'].values,
                    batch_size = BATCH_SIZE,
                    epochs = EPOCHS,
                    verbose = 2,
                    validation_split = 0.2,
                    callbacks = callbacks)

model.evaluate(test_seqs, test_df['adq'].values)

Главная проблема с выводом заключается в том, что когда я запускаю модель для предсказания комментариев без классификации, модель возвращает одно и то же значение для каждого отдельного комментария. Я провел некоторые исследования, и люди предлагали нормализовать пакет, и я пробовал добавить слой нормализации пакета в свою модель, но это не помогло. Можете ли вы, пожалуйста, взглянуть и показать мне, где я ошибаюсь? Большое спасибо за вашу помощь!

Вот весь мой скрипт согласно моему комментарию ниже:

import pandas as pd
import tensorflow as tf
import pickle
import string
import re

NUM_WORDS = 10000
SEQ_LEN = 512
EMBEDDING_SIZE = 300
BATCH_SIZE = 70
EPOCHS = 20
HIGHEST_PROTOCOL = 3
THRESHOLD = 0.60

train_df = pd.read_csv(r'C:\Users\peter\OneDrive\Documents\IMDBtrain.csv')
test_df = pd.read_csv(r'C:\Users\peter\OneDrive\Documents\IMDBtest.csv')

def clean_text(text, remove_stopwords=True):
    '''Очистить текст с опцией удаления стоп-слов'''

    # Привести слова к нижнему регистру и разбить их
    text = text.lower().split()

    # При желании, удалить стоп-слова
    if remove_stopwords:
        stops = ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', 'your', 'yours',
                 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', 'her', 'hers', 'herself',
                 'its', 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themslves', 'what', 'which',
                 'who', 'whom', 'this', 'that', 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be',
                 'been', 'be', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a',
                 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at',
                 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before',
                 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over',
                 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why',
                 'how', 'all', 'any', 'both', 'each', 'few', 'most', 'more', 'other', 'some', 'such', 'no',
                 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will',
                 'just', 'don', 'should', 'now']
        text = [w for w in text if not w in stops]

    text = " ".join(text)

    # Очистить текст
    text = re.sub(r"<br />", " ", text)
    text = re.sub(r"[^a-z]", " ", text)
    text = re.sub(r"   ", " ", text) # Удалить лишние пробелы
    text = re.sub(r"  ", " ", text)

    # Вернуть список слов
    return(text)

train_df["text"] = train_df["text"].apply(lambda x: clean_text(x))
test_df["text"] = test_df["text"].apply(lambda x: clean_text(x))
train_df = train_df.sample(frac = 1).reset_index(drop = True)
test_df = test_df.sample(frac = 1).reset_index(drop = True)

tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words = NUM_WORDS, oov_token = '<UNK>')
tokenizer.fit_on_texts(train_df['text'])

train_seqs = tokenizer.texts_to_sequences(train_df['text'])
test_seqs = tokenizer.texts_to_sequences(test_df['text'])

train_seqs = tf.keras.preprocessing.sequence.pad_sequences(train_seqs, maxlen = SEQ_LEN, padding = 'post')
test_seqs = tf.keras.preprocessing.sequence.pad_sequences(test_seqs, maxlen = SEQ_LEN, padding = 'post')

model = tf.keras.Sequential([
        tf.keras.layers.Embedding(NUM_WORDS, EMBEDDING_SIZE),
        tf.keras.layers.GlobalAveragePooling1D(),
        tf.keras.layers.Dense(1, activation = 'sigmoid')])

model.summary()

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

es = tf.keras.callbacks.EarlyStopping(monitor="val_accuracy", mode="max")

callbacks = [es]
history = model.fit(train_seqs, train_df['adq'].values,
                    batch_size = BATCH_SIZE,
                    epochs = EPOCHS,
                    validation_split = 0.2,
                    callbacks = callbacks)

model.evaluate(test_seqs, test_df['adq'].values)

model.save('model.ps1')
with open('tokenizer.pickle', 'wb') as handle:
    pickle.dump(tokenizer, handle, protocol = pickle.HIGHEST_PROTOCOL)

del model
del tokenizer

loaded_model = tf.keras.models.load_model('model.ps1')

with open('tokenizer.pickle', 'rb') as f:
    loaded_tokenizer = pickle.load(f)

def prepare_predict_data(tokenizer, comments):
    seqs = tokenizer.texts_to_sequences(comments)
    seqs = tf.keras.preprocessing.sequence.pad_sequences(seqs, maxlen = SEQ_LEN, padding = 'post')
    return seqs

comments_to_pred = pd.read_csv(r'C:\Users\peter\OneDrive\Documents\IMDBload.csv')
my_comments = comments_to_pred.to_numpy().tolist()

my_seqs = prepare_predict_data(loaded_tokenizer, my_comments)
preds = loaded_model.predict(my_seqs)
pred_df = pd.DataFrame(columns = ['text', 'adq'])
pred_df['text'] = my_comments
pred_df['adq'] = preds
print(pred_df.head(20))
pred_df['adq'] = pred_df['adq'].apply(lambda x: 'pos' if x > THRESHOLD else 'neg')
#print(pred_df.head(40))
```

Вы применяете глобальное усреднение к своему слою встраивания. Убедитесь, что это делается по словам, а не по оси встраивания. Если выходные данные вашего встраивания имеют форму (Пакет, Слова, 300), вам нужно применить GlobalAveragePool1D к оси 1 (слова).

Однако одна проблема может заключаться в том, что, усредняя по словам, вы искажаете данные из-за дополнения вашего ввода. Если вы дополните 10-словеый ввод до 25, а затем возьмете среднее на всех 25, где 15 из 25 – нули, вы будете искажать усреднение почти случайно (случайно в зависимости от длины ввода), что ваша не-рекуррентная модель не сможет понять. Вам следует добавить дополнительный слой Lambda, чтобы усреднять только по длине ввода и передавать длину ввода в качестве дополнительного входного значения.

Ответ или решение

Конечно! Давайте разберём вашу модель и поймём, как оптимизировать её для классификации комментариев как адекватных или неадекватных. Ниже приведены ключевые моменты, которые могут помочь улучшить результаты вашей модели.

Проблема с выходными данными

Если ваша модель возвращает одно и то же значение для всех комментариев, это может быть связано с несколькими факторами:

  1. Проблема с обработкой данных: Когда вы используете GlobalAveragePooling1D, он объединяет информацию со всех слов в последовательности, включая нулевые значения, вставленные в pad_sequences. Таким образом, в среднем это может давать вам слишком много нуля, если вы работаете с последовательностями разной длины, что, в свою очередь, затрудняет обучение модели.

  2. Неоптимальные параметры модели: Размерность встраивания и число эпох могут быть не оптимальными для вашего набора данных.

Решение

1. Устранение проблемы с GlobalAveragePooling1D

Чтобы избежать проблемы объединения с нулевыми значениями после паддинга, вы можете использовать Masking или Lambda слой для маскировки паддингов или же воспользоваться Keras Functional API вместо Sequential API. Вот пример, как это реализовать:

from tensorflow.keras.layers import Input, Embedding, GlobalAveragePooling1D, Dense, Masking
from tensorflow.keras.models import Model

inputs = Input(shape=(SEQ_LEN,))
embedding = Embedding(NUM_WORDS, EMBEDDING_SIZE)(inputs)
masked = Masking(mask_value=0)(embedding)
pooled = GlobalAveragePooling1D()(masked)
outputs = Dense(1, activation='sigmoid')(pooled)

model = Model(inputs, outputs)

2. Использование более сложной архитектуры

Вы можете добавить больше слоёв и сложности в вашу модель для повышения её способности к обучению:

from tensorflow.keras.layers import Dropout

model = tf.keras.Sequential([
    Embedding(NUM_WORDS, EMBEDDING_SIZE, input_length=SEQ_LEN),
    GlobalAveragePooling1D(),
    Dense(128, activation='relu'),
    Dropout(0.5),
    Dense(1, activation='sigmoid')
])

3. Изменение параметров обучения

Обратите внимание на параметры, такие как размер пакета и количество эпох. Иногда большая размерность пакета может приводить к тому, что модель не обучается достаточно эффективно:

history = model.fit(train_seqs, train_df['adq'].values,
                    batch_size=32,  # уменьшите размер пакета
                    epochs=10,  # попробуйте уменьшить количество эпох
                    validation_split=0.2,
                    callbacks=callbacks)

4. Проверка Подготовленных Данных

Обратите внимание, что ваша функция обработки комментариев и токенизация корректно работают. Убедитесь, что ваши настраиваемые функции действительно очищают текст и что нет никаких остатков, которые могут влиять на токенизацию.

5. Визуализация и анализ

Используйте визуализацию истории обучения, чтобы понять, происходит ли переобучение. Если точность на обучающем наборе высока, а на проверочном – низка, возможно, стоит уменьшить сложность модели или использовать регуляризацию.

import matplotlib.pyplot as plt

plt.plot(history.history['accuracy'], label='accuracy')
plt.plot(history.history['val_accuracy'], label='val_accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

Заключение

Проблема, с которой вы столкнулись, скорее всего, связана с обработкой входных данных и архитектурой вашей модели. Улучшив эти аспекты, вы должны увидеть более вариативные и точные результаты модели. Если у вас будут ещё вопросы или вам потребуется дополнительная помощь, не стесняйтесь задавать!

Оцените материал
Добавить комментарий

Капча загружается...