Word2Vec + LSTM 良好的训练和验证但测试不佳
Word2Vec + LSTM Good Training and Validation but Poor on Test
目前我正在训练我的 Word2Vec + LSTM 以进行 Twitter 情绪分析。我使用预训练的 GoogleNewsVectorNegative300 词嵌入。我使用预训练的 GoogleNewsVectorNegative300 的原因是当我使用自己的数据集训练自己的 Word2Vec 时性能更差。问题是为什么我的训练过程中验证 acc 和 loss 分别停留在 0.88 和 0.34。然后,我的混淆矩阵似乎也不对。这是我在拟合模型之前完成的几个过程
文本预处理:
- 下壳
- 删除主题标签、提及、URL、数字、将单词更改为数字、非 ASCII 字符、转发“RT”
- 展开收缩
- 用反义词代替否定词
- 删除标点符号
- 删除停用词
- 词形还原
我将 train:test 的数据集拆分为 90:10,如下所示:
def split_data(X, y):
X_train, X_test, y_train, y_test = train_test_split(X,
y,
train_size=0.9,
test_size=0.1,
stratify=y,
random_state=0)
return X_train, X_test, y_train, y_test
导致训练的拆分数据有 2060 个样本,其中 708 个正面情绪 class、837 个负面情绪 class 和 515 个中性情绪 class
然后,我在所有训练数据上实现了文本增强,即 EDA(Easy Data Augmentation),如下所示:
class TextAugmentation:
def __init__(self):
self.augmenter = EDA()
def replace_synonym(self, text):
augmented_text_portion = int(len(text)*0.1)
synonym_replaced = self.augmenter.synonym_replacement(text, n=augmented_text_portion)
return synonym_replaced
def random_insert(self, text):
augmented_text_portion = int(len(text)*0.1)
random_inserted = self.augmenter.random_insertion(text, n=augmented_text_portion)
return random_inserted
def random_swap(self, text):
augmented_text_portion = int(len(text)*0.1)
random_swaped = self.augmenter.random_swap(text, n=augmented_text_portion)
return random_swaped
def random_delete(self, text):
random_deleted = self.augmenter.random_deletion(text, p=0.5)
return random_deleted
text_augmentation = TextAugmentation()
导致训练的数据扩充有 10300 个样本,其中 3540 个正面情绪 class、4185 个负面情绪 class 和 2575 个中性情绪 class
然后,我将序列标记如下:
# Tokenize the sequence
pfizer_tokenizer = Tokenizer(oov_token='OOV')
pfizer_tokenizer.fit_on_texts(df_pfizer_train['text'].values)
X_pfizer_train_tokenized = pfizer_tokenizer.texts_to_sequences(df_pfizer_train['text'].values)
X_pfizer_test_tokenized = pfizer_tokenizer.texts_to_sequences(df_pfizer_test['text'].values)
# Pad the sequence
X_pfizer_train_padded = pad_sequences(X_pfizer_train_tokenized, maxlen=100)
X_pfizer_test_padded = pad_sequences(X_pfizer_test_tokenized, maxlen=100)
pfizer_max_length = 100
pfizer_num_words = len(pfizer_tokenizer.word_index) + 1
# Encode label
y_pfizer_train_encoded = df_pfizer_train['sentiment'].factorize()[0]
y_pfizer_test_encoded = df_pfizer_test['sentiment'].factorize()[0]
y_pfizer_train_category = to_categorical(y_pfizer_train_encoded)
y_pfizer_test_category = to_categorical(y_pfizer_test_encoded)
产生 8869 个唯一单词和 100 个最大序列长度
最后,我使用预训练的 GoogleNewsVectorNegative300 词嵌入将其拟合到我的模型中,但只使用权重和 LSTM,我再次将训练数据分成 10% 以进行验证,如下所示:
# Build single LSTM model
def build_lstm_model(embedding_matrix, max_sequence_length):
# Input layer
input_layer = Input(shape=(max_sequence_length,), dtype='int32')
# Word embedding layer
embedding_layer = Embedding(input_dim=embedding_matrix.shape[0],
output_dim=embedding_matrix.shape[1],
weights=[embedding_matrix],
input_length=max_sequence_length,
trainable=True)(input_layer)
# LSTM model layer
lstm_layer = LSTM(units=128,
dropout=0.5,
return_sequences=True)(embedding_layer)
batch_normalization = BatchNormalization()(lstm_layer)
lstm_layer = LSTM(units=128,
dropout=0.5,
return_sequences=False)(batch_normalization)
batch_normalization = BatchNormalization()(lstm_layer)
# Dense model layer
dense_layer = Dense(units=128, activation='relu')(batch_normalization)
dropout_layer = Dropout(rate=0.5)(dense_layer)
batch_normalization = BatchNormalization()(dropout_layer)
output_layer = Dense(units=3, activation='softmax')(batch_normalization)
lstm_model = Model(inputs=input_layer, outputs=output_layer)
return lstm_model
# Building single LSTM model
sinovac_lstm_model = build_lstm_model(SINOVAC_EMBEDDING_MATRIX, SINOVAC_MAX_SEQUENCE)
sinovac_lstm_model.summary()
sinovac_lstm_model.compile(loss='categorical_crossentropy',
optimizer=Adam(learning_rate=0.001),
metrics=['accuracy'])
sinovac_lstm_history = sinovac_lstm_model.fit(x=X_sinovac_train,
y=y_sinovac_train,
batch_size=64,
epochs=20,
validation_split=0.1,
verbose=1)
训练结果:
评价结果:
我真的需要一些建议或见解才能使我的测试准确无误
在不检查所有内容的情况下,一些 high-order 可能会限制您的结果的事情:
GoogleNews
向量是根据 2012 年及更早的 media-outlet 新闻故事训练的。 2020 年以后的推文使用了一种截然不同的语言风格。我不一定期望那些来自不同时代和 domain-of-writing 的预训练向量能够非常擅长为您需要的单词建模。 well-trained word2vec 模型(使用大量现代推文数据,具有良好的 preprocessing/tokenization 和参数化选择)很有可能会更好地工作,因此您可能需要重新考虑该选择。
GoogleNews
训练文本预处理,而据我所知从来没有 fully-documented,似乎没有压平所有大小写,也没有删除停用词,也没有涉及词形还原。它没有将明显的否定变异为反义词,但它确实执行了一些 single-words 的统计组合,变成了多重标记。因此,您的某些步骤可能会导致您的标记与该集合的向量的一致性降低——甚至会丢弃信息,例如单词的屈折变化,而这些信息本可以被有益地保留下来。确保你采取的每一步都是值得的 - 请注意,在推文上使用相同的 word2vec 训练预处理和后续步骤构建的足够的现代 word2vec 模型将完美匹配词汇表。
word2vec 模型和任何更深层的神经网络通常都需要大量数据才能很好地训练,并避免过度拟合。即使忽略来自 GoogleNews
的 9 亿个参数,您也在尝试从初始的仅 2060 tweet-sized 文本集(可能是 100KB 的数据)训练约 130k 个参数——至少 520KB 的状态。从某种意义上说,学习可泛化事物的模型往往是数据的压缩,并且比训练数据大得多的模型会带来严重过度拟合的风险。 (您用同义词替换单词的机械过程可能并没有真正为模型提供 word-vector 同义词之间的相似性尚未提供的任何信息。)因此:考虑缩小您的模型,并获得更多的训练数据 - 可能即使来自您主要分类兴趣之外的其他领域,只要 use-of-language 相似。
目前我正在训练我的 Word2Vec + LSTM 以进行 Twitter 情绪分析。我使用预训练的 GoogleNewsVectorNegative300 词嵌入。我使用预训练的 GoogleNewsVectorNegative300 的原因是当我使用自己的数据集训练自己的 Word2Vec 时性能更差。问题是为什么我的训练过程中验证 acc 和 loss 分别停留在 0.88 和 0.34。然后,我的混淆矩阵似乎也不对。这是我在拟合模型之前完成的几个过程
文本预处理:
- 下壳
- 删除主题标签、提及、URL、数字、将单词更改为数字、非 ASCII 字符、转发“RT”
- 展开收缩
- 用反义词代替否定词
- 删除标点符号
- 删除停用词
- 词形还原
我将 train:test 的数据集拆分为 90:10,如下所示:
def split_data(X, y):
X_train, X_test, y_train, y_test = train_test_split(X,
y,
train_size=0.9,
test_size=0.1,
stratify=y,
random_state=0)
return X_train, X_test, y_train, y_test
导致训练的拆分数据有 2060 个样本,其中 708 个正面情绪 class、837 个负面情绪 class 和 515 个中性情绪 class
然后,我在所有训练数据上实现了文本增强,即 EDA(Easy Data Augmentation),如下所示:
class TextAugmentation:
def __init__(self):
self.augmenter = EDA()
def replace_synonym(self, text):
augmented_text_portion = int(len(text)*0.1)
synonym_replaced = self.augmenter.synonym_replacement(text, n=augmented_text_portion)
return synonym_replaced
def random_insert(self, text):
augmented_text_portion = int(len(text)*0.1)
random_inserted = self.augmenter.random_insertion(text, n=augmented_text_portion)
return random_inserted
def random_swap(self, text):
augmented_text_portion = int(len(text)*0.1)
random_swaped = self.augmenter.random_swap(text, n=augmented_text_portion)
return random_swaped
def random_delete(self, text):
random_deleted = self.augmenter.random_deletion(text, p=0.5)
return random_deleted
text_augmentation = TextAugmentation()
导致训练的数据扩充有 10300 个样本,其中 3540 个正面情绪 class、4185 个负面情绪 class 和 2575 个中性情绪 class
然后,我将序列标记如下:
# Tokenize the sequence
pfizer_tokenizer = Tokenizer(oov_token='OOV')
pfizer_tokenizer.fit_on_texts(df_pfizer_train['text'].values)
X_pfizer_train_tokenized = pfizer_tokenizer.texts_to_sequences(df_pfizer_train['text'].values)
X_pfizer_test_tokenized = pfizer_tokenizer.texts_to_sequences(df_pfizer_test['text'].values)
# Pad the sequence
X_pfizer_train_padded = pad_sequences(X_pfizer_train_tokenized, maxlen=100)
X_pfizer_test_padded = pad_sequences(X_pfizer_test_tokenized, maxlen=100)
pfizer_max_length = 100
pfizer_num_words = len(pfizer_tokenizer.word_index) + 1
# Encode label
y_pfizer_train_encoded = df_pfizer_train['sentiment'].factorize()[0]
y_pfizer_test_encoded = df_pfizer_test['sentiment'].factorize()[0]
y_pfizer_train_category = to_categorical(y_pfizer_train_encoded)
y_pfizer_test_category = to_categorical(y_pfizer_test_encoded)
产生 8869 个唯一单词和 100 个最大序列长度
最后,我使用预训练的 GoogleNewsVectorNegative300 词嵌入将其拟合到我的模型中,但只使用权重和 LSTM,我再次将训练数据分成 10% 以进行验证,如下所示:
# Build single LSTM model
def build_lstm_model(embedding_matrix, max_sequence_length):
# Input layer
input_layer = Input(shape=(max_sequence_length,), dtype='int32')
# Word embedding layer
embedding_layer = Embedding(input_dim=embedding_matrix.shape[0],
output_dim=embedding_matrix.shape[1],
weights=[embedding_matrix],
input_length=max_sequence_length,
trainable=True)(input_layer)
# LSTM model layer
lstm_layer = LSTM(units=128,
dropout=0.5,
return_sequences=True)(embedding_layer)
batch_normalization = BatchNormalization()(lstm_layer)
lstm_layer = LSTM(units=128,
dropout=0.5,
return_sequences=False)(batch_normalization)
batch_normalization = BatchNormalization()(lstm_layer)
# Dense model layer
dense_layer = Dense(units=128, activation='relu')(batch_normalization)
dropout_layer = Dropout(rate=0.5)(dense_layer)
batch_normalization = BatchNormalization()(dropout_layer)
output_layer = Dense(units=3, activation='softmax')(batch_normalization)
lstm_model = Model(inputs=input_layer, outputs=output_layer)
return lstm_model
# Building single LSTM model
sinovac_lstm_model = build_lstm_model(SINOVAC_EMBEDDING_MATRIX, SINOVAC_MAX_SEQUENCE)
sinovac_lstm_model.summary()
sinovac_lstm_model.compile(loss='categorical_crossentropy',
optimizer=Adam(learning_rate=0.001),
metrics=['accuracy'])
sinovac_lstm_history = sinovac_lstm_model.fit(x=X_sinovac_train,
y=y_sinovac_train,
batch_size=64,
epochs=20,
validation_split=0.1,
verbose=1)
训练结果:
评价结果:
我真的需要一些建议或见解才能使我的测试准确无误
在不检查所有内容的情况下,一些 high-order 可能会限制您的结果的事情:
GoogleNews
向量是根据 2012 年及更早的 media-outlet 新闻故事训练的。 2020 年以后的推文使用了一种截然不同的语言风格。我不一定期望那些来自不同时代和 domain-of-writing 的预训练向量能够非常擅长为您需要的单词建模。 well-trained word2vec 模型(使用大量现代推文数据,具有良好的 preprocessing/tokenization 和参数化选择)很有可能会更好地工作,因此您可能需要重新考虑该选择。GoogleNews
训练文本预处理,而据我所知从来没有 fully-documented,似乎没有压平所有大小写,也没有删除停用词,也没有涉及词形还原。它没有将明显的否定变异为反义词,但它确实执行了一些 single-words 的统计组合,变成了多重标记。因此,您的某些步骤可能会导致您的标记与该集合的向量的一致性降低——甚至会丢弃信息,例如单词的屈折变化,而这些信息本可以被有益地保留下来。确保你采取的每一步都是值得的 - 请注意,在推文上使用相同的 word2vec 训练预处理和后续步骤构建的足够的现代 word2vec 模型将完美匹配词汇表。word2vec 模型和任何更深层的神经网络通常都需要大量数据才能很好地训练,并避免过度拟合。即使忽略来自
GoogleNews
的 9 亿个参数,您也在尝试从初始的仅 2060 tweet-sized 文本集(可能是 100KB 的数据)训练约 130k 个参数——至少 520KB 的状态。从某种意义上说,学习可泛化事物的模型往往是数据的压缩,并且比训练数据大得多的模型会带来严重过度拟合的风险。 (您用同义词替换单词的机械过程可能并没有真正为模型提供 word-vector 同义词之间的相似性尚未提供的任何信息。)因此:考虑缩小您的模型,并获得更多的训练数据 - 可能即使来自您主要分类兴趣之外的其他领域,只要 use-of-language 相似。