训练集准确率极高但测试集表现差的神经网络问题咨询
问题分析与优化建议
问题背景
我开发了一个用于情感多分类的简单神经网络,使用Kaggle上25k+带正面、中性、负面标签的推文数据集。模型在训练集上准确率达98-99%,但测试集准确率仅约60%。尝试过调整网络层数、层规模、批量大小、训练轮数及词表大小,测试性能仍无改善。想知道造成这种差异的原因,以及如何优化提升测试表现。
代码实现
df = pd.read_csv('Tweets.csv'); df = df.drop(columns=['textID', 'selected_text']) data = df['text'] labels = df['sentiment'] labels = np.unique(labels, return_inverse=True) lookup = labels[0] labels = labels[1] data = np.array(data).astype(str) tokenizer = keras_preprocessing.text.Tokenizer(num_words=10000) tokenizer.fit_on_texts(data) sequences = tokenizer.texts_to_sequences(data) one_hot_results = tokenizer.texts_to_matrix(data, mode='binary') all_tweets = one_hot_results[:20000] all_labels = labels[:20000] train_data = all_tweets[:10000] test_data = all_tweets[10000:] train_labels = all_labels[:10000] test_labels = all_labels[10000:] model = models.Sequential() model.add(layers.Dense(64, activation='relu', input_shape=(10000, ))) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(3, activation='softmax')) model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(train_data, train_labels, batch_size=512, epochs=20, validation_split=0.3) test_loss, test_acc = model.evaluate(test_data, test_labels) print("test_acc:", test_acc)
训练与测试输出
Epoch 19/20 14/14 [==============================] - 0s 14ms/step - loss: 0.0423 - accuracy: 0.9903 - val_loss: 1.9351 - val_accuracy: 0.6063 Epoch 20/20 14/14 [==============================] - 0s 14ms/step - loss: 0.0365 - accuracy: 0.9920 - val_loss: 2.0434 - val_accuracy: 0.6013 313/313 [==============================] - 1s 3ms/step - loss: 1.9856 - accuracy: 0.6086 test_acc: 0.6086000204086304
核心原因:严重过拟合+文本处理/模型适配不合理
从训练曲线和测试结果来看,模型完全记住了训练集的特征,但无法泛化到测试数据,具体诱因包括:
- 文本特征无效:用
binary模式的one-hot编码完全丢失了词序和语义关联,这种特征本身区分度极低,模型只能死记硬背训练集的词频组合,无法理解情感逻辑。 - 数据集划分错误:直接按顺序切割数据,推文可能存在时间或主题的序列性,导致训练/测试集数据分布差异极大,模型学不到通用的情感判别模式。
- 模型结构不适配:全连接Dense层擅长处理结构化数据,无法捕捉文本的上下文依赖关系,加上无正则化约束,很容易在简单特征上过度拟合。
- 训练策略激进:20轮训练已经远超模型收敛所需,后期模型一直在学习训练集的噪声细节,验证集损失持续上升就是明确信号。
优化方案(按优先级排序)
1. 重构文本特征处理
- 放弃one-hot编码,改用**词嵌入(Embedding)**层:将单词映射到低维语义空间,保留词之间的关联信息,可选择预训练GloVe/Word2Vec向量,或让模型在数据集上自行训练。
- 保留序列结构:用
pad_sequences将文本转为固定长度的序列,而非扁平的one-hot矩阵,为后续序列模型提供输入基础。
2. 修正数据集划分
- 采用分层随机划分,用
sklearn.model_selection.train_test_split并设置stratify=labels,确保训练/测试集的情感标签分布一致,避免数据偏移。
3. 调整模型结构与正则化
- 替换全连接网络为文本专用模型:基础版用
Embedding + LSTM/GRU捕捉上下文情感;进阶版用Transformer Encoder(如简化版BERT)提取复杂语义。 - 添加正则化组件:
- 在Dense层后加入
Dropout(0.3-0.5),随机丢弃神经元打断噪声依赖; - 给Dense层添加
kernel_regularizer=regularizers.l2(0.01),限制权重规模。
- 在Dense层后加入
4. 优化训练策略
- 启用早停(Early Stopping):监控验证集损失,当连续3轮无下降时停止训练并恢复最优权重,避免过度训练;
- 调整批量大小:改用32/64这类较小批量,提升模型泛化能力;
- 动态调整学习率:用
ReduceLROnPlateau回调,当验证集损失停滞时降低学习率。
5. 文本预处理优化
- 清洗推文:去除URL、@用户名、特殊符号/表情,转小写,过滤停用词,可选词干提取/词形还原,减少噪声干扰。
优化后代码示例
import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder from tensorflow import keras from tensorflow.keras import layers, regularizers, callbacks # 数据读取与预处理 df = pd.read_csv('Tweets.csv') df = df.drop(columns=['textID', 'selected_text']) data = df['text'].astype(str) labels = df['sentiment'] # 标签编码 le = LabelEncoder() labels = le.fit_transform(labels) # 文本序列处理 tokenizer = keras.preprocessing.text.Tokenizer(num_words=10000) tokenizer.fit_on_texts(data) sequences = tokenizer.texts_to_sequences(data) max_len = 50 # 设定每条推文的最大长度 padded_sequences = keras.preprocessing.sequence.pad_sequences(sequences, maxlen=max_len) # 分层划分数据集 train_data, test_data, train_labels, test_labels = train_test_split( padded_sequences, labels, test_size=0.2, stratify=labels, random_state=42 ) # 构建模型 model = keras.Sequential([ layers.Embedding(input_dim=10000, output_dim=128, input_length=max_len), layers.LSTM(64, return_sequences=False), layers.Dropout(0.3), layers.Dense(32, activation='relu', kernel_regularizer=regularizers.l2(0.01)), layers.Dense(3, activation='softmax') ]) model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 早停回调 early_stop = callbacks.EarlyStopping(patience=3, restore_best_weights=True) # 训练模型 history = model.fit( train_data, train_labels, batch_size=64, epochs=20, validation_split=0.3, callbacks=[early_stop] ) # 测试 test_loss, test_acc = model.evaluate(test_data, test_labels) print("test_acc:", test_acc)
内容的提问来源于stack exchange,提问作者mwdziak
相关产品推荐
相关产品推荐

