web-dev-qa-db-ja.com

LSTMでのWord埋め込みを使用してテキスト分類の過剰適合を防止する

目的:

  • ユーザーが入力した質問(質問回答システムなど)を使用してクラスラベルを識別します。
  • Big PDFファイルから抽出されたデータであり、ユーザー入力に基づいてページ番号を予測する必要があります。
  • 主にポリシードキュメントで使用されます。ユーザーはポリシーについて質問があり、特定のページ番号を表示する必要があります。

以前の実装:弾性検索を適用しましたが、ユーザーが「必要」のようなテキストを入力するため、精度は非常に低くなります==「したい」


データセット情報:データセットには、各行がテキスト(または段落)およびラベル(ページ番号として)として含まれています。ここではデータセットのサイズは小さく、500行しかありません。

現在の実装:

  • KerasおよびバックエンドのLSTMでの単語埋め込み(グローブ)の適用はテンソルフローです
  • 応用ドループアウト
  • 適用されるアクティビティ
  • 適用されたL2 W_regularizer(0.1から0.001まで)
  • 10から600までの異なるnb_Epochを適用
  • グローブデータのEMBEDDING_DIMを100から300に変更

NLPを適用

  • 小文字に変換
  • 英語のストップワードを削除
  • ステミング
  • 番号を削除
  • 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)
11
Somnath Kadam

ドロップアウトとBNは、フィードフォワードNNで非常に効果的です。ただし、RNNで問題が発生する可能性があります(このトピックについては多くの論文が公開されています)

RNNモデルをより一般化する最良の方法は、データセットのサイズを増やすことです。あなたのケース(約200セルのLSTM)では、トレーニングするために、ラベル付けされたサンプルを100,000以上注文したいと思うでしょう。

8
MaxB

一部のレイヤーの埋め込みサイズやユニット数などのパラメーターを単純に減らすだけでなく、LSTMの再発ドロップアウトを調整することもできます。

LSTMは非常に簡単に適合しすぎるようです(そのため、私は読みました)。

次に、各dropoutレイヤーのパラメーターとしてのLSTMrecurrent_dropoutの使用を Kerasのドキュメント で確認できます。

任意の数値の例:

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)

その他の原因が間違っているか、データが不十分である可能性があります:

  • テストと検証データを一緒に入れ替えて、新しいトレインと検証セットを作成してみましたか?

  • トレーニングデータには何文ありますか?あなたは小さなセットで試していますか?セット全体を使用するか、dadaの拡張を試してください(新しい文とその分類を作成しますが、これはテキストでは非常に扱いにくい場合があります)。

6
Daniel Möller

あなたが説明することは、過剰適合のように聞こえます。データに関する詳細情報がない場合は、より強力な正則化手法を試すことをお勧めします。 @Danielは、使用していないドロップアウトパラメータdropoutおよびrecurrent_dropoutを使用することをすでに提案しています。また、ドロップアウトレイヤーの比率を増やして、W_regularizerパラメーターでより強い正則化を使用することもできます。

ダニエルの提案を試したかどうか、結果はどのようなものかなど、他のオプションを開くこともできます。

3
ginge

(正則化の平均としての)敵対的なトレーニング方法は、検討する価値があるかもしれません。 半教師付きテキスト分類の敵対的トレーニング方法

0
MARK