基于预训练Word2Vec的多作者文本分类模型训练出现loss为nan、accuracy为0问题求助
看了你的代码和描述,马上找到了核心问题,还有几个细节需要调整,咱们一步步来解决:
核心问题:多分类任务用了二分类的模型配置
你要做的是8/9位作者的文本分类,属于多分类任务,但代码里却用了二分类的设置:
- 输出层写的是
Dense(1, activation='sigmoid'):这个只能输出0或1,根本没法区分多个作者 - 损失函数用了
binary_crossentropy:这玩意儿只适用于二分类场景,多分类用它会直接导致损失计算出错,出现nan,模型自然学不到东西,准确率就一直是0了
怎么改?
根据你标签的形式(用0、1、2...这样的整数代表不同作者),最方便的是用sparse_categorical_crossentropy损失函数,不用把标签转成one-hot编码,同时调整输出层:
# 重新定义模型的输出层 model = Sequential() model.add(embedding_layer) model.add(Conv1D(filters=128, kernel_size=5, activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(Flatten()) # 这里要改成你的作者数量,代码里你写了9个(a1到a9),所以填9;如果实际是8位就改8 model.add(Dense(9, activation='softmax')) print(model.summary()) # 编译的时候换损失函数 model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
如果你更习惯用categorical_crossentropy,那得先把标签转成one-hot编码:
from keras.utils import to_categorical # 把训练和测试标签转成one-hot格式 ytrain = to_categorical(ytrain) ytest = to_categorical(ytest) # 输出层还是Dense(9, activation='softmax'),然后编译时用这个损失 model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
其他几个潜在小问题也要注意
1. 预训练词向量里没有的词怎么处理?
你的get_weight_matrix函数里,如果词汇表中的词不在预训练Word2Vec里,embedding.get(word)会返回None,这时候对应的权重会是全0,可能影响模型效果。可以改成用随机小值初始化这些词:
import numpy as np def get_weight_matrix(embedding, vocab): vocab_size = len(vocab) + 1 weight_matrix = zeros((vocab_size, 100)) for word, i in vocab.items(): vec = embedding.get(word) if vec is not None: weight_matrix[i] = vec else: # 给没找到的词赋随机小值,比全0好 weight_matrix[i] = np.asarray(np.random.uniform(-0.05, 0.05, 100), dtype='float32') return weight_matrix
2. 作者数量要对应上
你描述里说的是8位作者,但代码里写了a1到a9共9个,得确认你的文件夹里确实是9位作者的文本,不然标签数量和样本数量不匹配,也会出问题。比如训练集每个作者10本,9个作者就是90个样本,ytrain的长度得是90,测试集每个2本就是18个样本,ytest长度18,这点要核对清楚。
3. 文本长度可以适当限制
你用max_length = max([len(s.split()) for s in train_docs])来取最长文本长度当输入长度,如果有特别长的文本,会让模型输入维度太大,训练起来慢还容易出问题。可以考虑设一个固定值,比如max_length = 500,太长的文本截断,短的补全。
改完之后再试试
调整完这些地方,重新运行代码,应该就能看到loss正常下降,准确率慢慢提升了,测试集也能得到合理的准确率啦!
内容的提问来源于stack exchange,提问作者Sona Joseph

