Предотвратить чрезмерную подгонку классификации текста с помощью встраивания Word с LSTM

Задача :

  • Определение ярлыка класса с использованием вопроса, введенного пользователем (например, системы ответов на вопросы).
  • Данные, извлеченные из файла Big PDF, необходимо предсказать номер страницы на основе ввода данных пользователем.
  • В основном используется в документе политики, где у пользователя есть вопросы о политике и ему необходимо указать конкретный номер страницы.

Предыдущая реализация: применен эластичный поиск, но с очень меньшей точностью, потому что пользователь вводит любой текст вроде «Мне нужно» == «хочу»


Информация о наборе данных: набор данных содержит каждую строку как текст (или абзац) и метку (как номер страницы). здесь размер набора данных небольшой, у меня всего 500 строк.

Текущая реализация:

  • Прикладное встраивание слов (Glove) с LSTM в Keras и бэкэнд - это Tensor-flow
  • Прикладной дроуплаут
  • Прикладная деятельность
  • Применен L2 W_regularizer (от 0,1 до 0,001)
  • Применяли разные нб_эпоч от 10 до 600
  • Значение EMBEDDING_DIM изменено с 100 на 300 данных перчатки.

Применял НЛП для,

  • Преобразовать в нижний регистр
  • Удалить стоп-слово английского языка
  • Стемминг
  • Удалить числа
  • Удалить URL и IP-адрес

Результат: точность данных испытаний (или данных проверки) составляет 23%, а данных поездов - 91%.


Код:

import time
from time import strftime

import numpy as np
from keras.callbacks import CSVLogger, ModelCheckpoint
from keras.layers import Dense, Input, LSTM, ActivityRegularization
from keras.layers import Embedding, Dropout,Bidirectional
from keras.models import Model
from keras.preprocessing.sequence import pad_sequences
from keras.preprocessing.text import Tokenizer
from keras.regularizers import l2
from keras.utils import to_categorical

import pickle
from DataGenerator import *

BASE_DIR = ''
GLOVE_DIR = 'D:/Dataset/glove.6B'  # BASE_DIR + '/glove.6B/'

MAX_SEQUENCE_LENGTH = 50
MAX_NB_WORDS = 20000
EMBEDDING_DIM = 300
VALIDATION_SPLIT = 0.2

# first, build index mapping words in the embeddings set
# to their embedding vector
np.random.seed(1337)  # for reproducibility

print('Indexing word vectors.')

t_start = time.time()

embeddings_index = {}

if os.path.exists('pickle/glove.pickle'):
    print('Pickle found..')
    with open('pickle/glove.pickle', 'rb') as handle:
        embeddings_index = pickle.load(handle)
else:
    print('Pickle not found...')
    f = open(os.path.join(GLOVE_DIR, 'glove.6B.300d.txt'), encoding='utf8')
    for line in f:
        values = line.split()
        word = values[0]
        coefs = np.asarray(values[1:], dtype='float32')
        embeddings_index[word] = coefs
    f.close()
    with open('pickle/glove.pickle', 'wb') as handle:
        pickle.dump(embeddings_index, handle, protocol=pickle.HIGHEST_PROTOCOL)

print('Found %s word vectors.' % len(embeddings_index))

# second, prepare text samples and their labels
print('Processing text dataset')

texts = []  # list of text samples
labels = []  # list of label ids
labels_index = {}  # dictionary mapping label name to numeric id

(texts, labels, labels_index) = get_data('D:/PolicyDocument/')

print('Found %s texts.' % len(texts))

# finally, vectorize the text samples into a 2D integer tensor
tokenizer = Tokenizer(nb_words=MAX_NB_WORDS)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)

word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))
data = pad_sequences(sequences, maxlen=MAX_SEQUENCE_LENGTH)

labels = to_categorical(np.asarray(labels))
print('Shape of data tensor:', data.shape)
print('Shape of label tensor:', labels.shape)

# split the data into a training set and a validation set
indices = np.arange(data.shape[0])
np.random.shuffle(indices)
data = data[indices]
labels = labels[indices]
num_validation_samples = int(VALIDATION_SPLIT * data.shape[0])

x_train = data[:-num_validation_samples]
y_train = labels[:-num_validation_samples]
x_val = data[-num_validation_samples:]
y_val = labels[-num_validation_samples:]

# prepare embedding matrix
num_words = min(MAX_NB_WORDS, len(word_index))
embedding_matrix = np.zeros((num_words + 1, EMBEDDING_DIM))
print('Preparing embedding matrix. :', embedding_matrix.shape)
for word, i in word_index.items():
    embedding_vector = embeddings_index.get(word)
    if embedding_vector is not None:
        # words not found in embedding index will be all-zeros.
        embedding_matrix[i] = embedding_vector

# load pre-trained word embeddings into an Embedding layer
# note that we set trainable = False so as to keep the embeddings fixed
embedding_layer = Embedding(embedding_matrix.shape[0],
                            embedding_matrix.shape[1],
                            weights=[embedding_matrix],
                            input_length=MAX_SEQUENCE_LENGTH,
                            mask_zero=True,
                            trainable=False)

print('Training model.')

csv_file = "logs/training_log_" + strftime("%Y-%m-%d %H-%M", time.localtime()) + ".csv"
model_file = "models/Model_" + strftime("%Y-%m-%d %H-%M", time.localtime()) + ".mdl"
print("Model file:" + model_file)
csv_logger = CSVLogger(csv_file)

# train a 1D convnet with global maxpooling
sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
embedded_sequences = embedding_layer(sequence_input)

rate_drop_lstm = 0.15 + np.random.rand() * 0.25
num_lstm = np.random.randint(175, 275)
rate_drop_dense = 0.15 + np.random.rand() * 0.25

x = LSTM(num_lstm, return_sequences=True, W_regularizer=l2(0.001))(embedded_sequences)
x = Dropout(0.5)(x)
x = LSTM(64)(x)
x = Dropout(0.25)(x)
x = ActivityRegularization(l1=0.01, l2=0.001)(x)
preds = Dense(len(labels_index), activation='softmax')(x)

model = Model(sequence_input, preds)
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['acc'])

model_checkpoint = ModelCheckpoint(model_file, monitor='val_loss', verbose=0, save_best_only=True,
                                   save_weights_only=False, mode='auto')

model.fit(x_train, y_train,
          batch_size=1,
          nb_epoch=600,
          validation_data=(x_val, y_val), callbacks=[csv_logger, model_checkpoint])

score = model.evaluate(x_val, y_val, verbose=0)
print('Test score:', score[0])
print('Test accuracy:', score[1])

t_end = time.time()
total = t_end - t_start
ret_str = "Time needed(s): " + str(total)
print(ret_str)

person Somnath Kadam    schedule 08.05.2017    source источник
comment
и вопрос ... как предотвратить переоснащение? Это не совсем вопрос программирования ...   -  person dv3    schedule 08.05.2017
comment
Вы когда-нибудь внимательно изучали содержание своего набора тестов и / или валидации?   -  person kafman    schedule 11.05.2017
comment
Вы делаете очень частые обновления, передавая batch_size = 1. Попробуйте изменить его на такие значения, как 32, 64 и т. Д. Слишком частые обновления могут нарушить работу вашей сети.   -  person enterML    schedule 11.05.2017
comment
Пожалуйста, четко укажите вопрос.   -  person onur güngör    schedule 13.05.2017
comment
Хорошие баллы @Nain, я также изменил batch_size с 1 на 64   -  person Somnath Kadam    schedule 15.05.2017
comment
Привет @ onurgüngör, я постарался выложить сюда все, что реализовал. Укажите здесь любые конкретные моменты, которые вам могут понадобиться.   -  person Somnath Kadam    schedule 15.05.2017


Ответы (4)


Dropout и BN очень эффективны с NN с прямой связью. Однако они могут вызвать проблемы с RNN (по этой теме опубликовано много статей).

Лучший способ сделать вашу модель RNN более обобщенной - это увеличить размер набора данных. В вашем случае (LSTM с примерно 200 ячейками) вы, вероятно, захотите иметь порядка 100000 или более маркированных образцов для обучения.

person bobcat    schedule 17.05.2017

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

Кажется, что LSTM довольно легко переоснащаются (так что я читал).

Затем вы можете увидеть в документации Keras использование dropout и recurrent_dropout в качестве параметров каждого LSTM слоя.

Пример с произвольными числами:

x = LSTM(num_lstm, return_sequences=True, W_regularizer=l2(0.001), recurrent_dropout=0.4)(embedded_sequences)
x = Dropout(0.5)(x)
x = LSTM(64,dropout=0,5, recurrent_dropout=0,3)(x)

Другие причины могут быть неправильными или недостаточными данными:

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

  • Сколько предложений у вас в обучающих данных? Вы пытаетесь использовать небольшие наборы? Используйте весь набор или попробуйте Дада-аугментацию (создание новых предложений и их классификации - но это может быть очень сложно с текстом).

person Daniel Möller    schedule 12.05.2017
comment
Спасибо @Daniel, проверю. Я пробовал перетасовать с помощью np.random.shuffle (индексы). У меня 50 файлов, и каждый файл содержит 10 предложений. Как мне создать новое предложение, имеющее отношение к текущему обучению? - person Somnath Kadam; 15.05.2017
comment
Получена ошибка ниже, когда в LSTM помещается recurrent_dropout = 0.4, в настоящее время используется keras 1.0 AssertionError: аргумент ключевого слова не понят: recurrent_dropout - person Somnath Kadam; 16.05.2017

То, что вы описываете, очень похоже на переоснащение. Без дополнительной информации о данных лучше всего попробовать более сильные методы регуляризации. @Daniel уже предложил вам использовать параметры исключения, которые вы не использовали - dropout и recurrent_dropout. Вы также можете попробовать увеличить коэффициент для выпадающих слоев, используя более сильную регуляризацию с параметром W_regularizer.

Можно открыть другие варианты с дополнительной информацией, например, попробовали ли вы предложение Даниэля и каковы были результаты.

person ginge    schedule 15.05.2017

Возможно, стоит изучить состязательные методы обучения (как средство регуляризации). Состязательные методы обучения для полуавтоматической классификации текста

person MARK    schedule 08.09.2017