字符+词嵌入CNN文本分类模型准确率极低问题求助
我尝试使用CNN进行文本分类,模型采用字符级词嵌入+词嵌入的组合,经CNN层提取特征后接入全连接层,以softmax激活完成分类,损失函数为categorical_crossentropy。模型代码如下:
cnns = [ [64, 3, 2], [128, 3, -1], [256, 5, 3], [256, 5, -1], [512, 5, 3], ] nb_classes = 2 input_word = Input(shape = (default_max_len_words,), name='input_word') input_chw = Input(shape = (default_max_len_words, default_max_len_subwords), name='input_chw') embedding_word = Embedding(input_dim=size_of_word_vocab, output_dim=default_emb_dim, input_length=default_max_len_words, name='word_emb') (input_word) embedding_chw = Embedding(input_dim=size_of_char_vocab, output_dim=default_emb_dim, input_length=default_max_len_subwords, name='chw_emb') (input_chw) reduced = tf.keras.layers.Lambda(lambda x: tf.reduce_sum(x, axis=2), name='reduction')(embedding_chw) x = Add(name='adding')([embedding_word, reduced]) for f, ks, ps in cnns: x = Conv1D(filters=f, kernel_size=ks, padding='valid', activation='relu') (x) x = BatchNormalization() (x) if ps != -1: x = MaxPooling1D(pool_size=ps) (x) x = Flatten() (x) x = Dense(256, activation='relu') (x) x = Dense(128, activation='relu') (x) x = Dense(2, activation='softmax') (x) model = keras.Model(inputs=[input_word, input_chw], outputs=x, name='temp') model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['Accuracy'])
训练10轮后,模型的准确率和损失不再变化,且准确率极低,训练指标为:loss: 0.0162 - accuracy: 0.1983 - val_loss: 1.8428 - val_accuracy: 0.0814。已确认数据无NaN值,且训练前已完成数据打乱,以下是可能的问题排查方向:
标签编码与损失函数不匹配:
categorical_crossentropy要求标签为one-hot编码格式,如果你的标签是整数形式(比如0/1),应该改用sparse_categorical_crossentropy。若标签编码错误,模型会完全无法学习到有效特征,出现极低准确率。字符嵌入降维方式不合理:代码中用
tf.reduce_sum(axis=2)对字符嵌入降维,直接求和会导致不同字符的嵌入信息相互抵消,丢失字符级特征。建议替换为字符维度的全局池化,比如:reduced = tf.keras.layers.GlobalMaxPool1D()(embedding_chw)或
GlobalAveragePool1D,能更合理地提取每个词的字符级特征。CNN层特征维度坍塌:当前CNN序列使用
padding='valid',每轮Conv1D都会缩短特征序列长度,再加上多次MaxPooling,可能导致经过几层后特征序列长度变为0或极小,Flatten后无有效特征可用。建议计算每一步的特征长度:
初始长度为default_max_len_words,每轮Conv1D后长度 = 当前长度 - kernel_size + 1,Pooling后长度 = 当前长度 // pool_size。如果出现长度为负的情况,必须调整kernel_size、pool_size,或把padding改为same避免长度骤降。过拟合与梯度消失并存:训练集损失极低但验证集损失极高,说明严重过拟合;同时训练准确率也低,说明模型在训练集上也未学到有效特征,大概率存在梯度消失问题。可尝试:
- 减少CNN层的filter数量(比如从512降到256或128),降低模型复杂度
- 在Dense层后添加Dropout层(如
Dropout(0.5))抑制过拟合 - 降低Adam优化器的学习率(比如设置
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4))
输入数据预处理错误:检查
input_chw的输入格式是否正确,每个词对应的字符序列是否正确填充到default_max_len_subwords长度,是否存在大量全0填充的情况;同时确认词嵌入的输入索引是否正确,未知词的映射处理是否合理。嵌入层配置问题:确认嵌入层的
trainable参数是否为True(默认是True),如果使用预训练词向量,需检查加载是否正确,以及是否设置了合适的微调策略;另外字符词汇表size_of_char_vocab是否准确,过大的词汇表会导致嵌入层参数过多,增加训练难度。
内容的提问来源于stack exchange,提问作者Mohammad Javad Jafari

