1. 数据集
样本是高中的试题,标签是试题涉及的知识点。每个题目涉及到多个知识点,也就是多标签。一共有95个类别
1.2 数据预处理
(1)开多进程进行文本的清洗和分词,主要是取出样本中的数字,标点以及其他特殊字符
(2)确定输入最大长度,使得输入的最大长度能够覆盖95%的样本,不足这个长度,就 zero pad;超过了这个长度,就截断
(3)sklearn的多标签处理工具,对多标签进行数值化
(4)设置最低词频为5, 然后构建词表
(5)加载预训练好的百度百科词向量,取出词表中词对应词向量
(6)处理多标签类别不平衡问题:计算类别的权重,用于计算加权的loss
weights = 1 / torch.log(1.01 + weights)
其中weights是每个标签的频率
2. 网络构建
2.1. 网络结构
(1)embedding层
(2)卷积层
使用4种尺寸不同的卷积核,尺寸分别为 2,3,4,5,也就是 n-gram 中的n,每种卷积核的数量为128
(3)全连接层
把卷积池化的结果拼接 —— dropout —— 全连接层 —— 输出层
在输出层前接全连接层作用:
(a)把网络加深,可以提取更丰富的特征
(b)如果输出的类别比较少,比如多分类模型中的3分类,那么输出的维度直接从几百维降到3维,可能对分类的效果产生不好的影响。因此加一个全连接层来过渡
TextCNN做多分类时,加了一个全连接层进行过渡,F1值大概提升了2个百分点
(4)dropout的使用
embedding 层 —— dropout —— 卷积池化的结果拼接 —— dropout —— 全连接层 —— dropout —— 输出层。
(5)其他注意事项
输出层是一个线性层,没有用激活函数。如果用激活函数,那应该是 sigmoid 函数,而不是 softmax 函数,因为95个类别,每一类都做一个二分类模型:是该类则为1,不是则为0。此处没有加sigmoid函数是因为后面的loss函数,把 sigmoid 和计算 binary loss 这两步同时做了,而且文档说这样计算更有效率,更稳定
2.2. 损失函数
损失函数不再是多分类的 cross-entropy loss,而是 binary cross-entropy loss(把模型的输出做 sigmoid,然后计算损失)
criterion = nn.BCEWithLogitsLoss(pos_weight=config.class_weights)
pos_weights 这个关键字参数,就是用于传入类别的权重的,从而缓解类别不平衡的问题
early stop 的监控指标选择的是 F1值(微平均,micro)
2.3 模型评价指标
(1)选择F1值(宏平均和微平均),来作为模型的评价指标
(2)没有找到适合多标签分类的准确率计算方法。用的就是sklearn的函数,对于每一个预测样本,需要95个类别的每一个类别都预测正确,才能算该样本预测正确。这个实在太难了! 这个不适合用来评估模型的效果。(缺陷,需要改进的地方)
在训练的过程中,如果监控到验证集上的 F1值有提升,那么会在测试集上做一次评估,同时保存模型。模型评估的结果,以测试集上的为准。
2.4 预测
输入一条文本 ——> 分词、pad和转化为id ——> 进行预测,得到预测标签的数值表示 ——> 转化为文本标签(mlb.inverse_transform)
3. 实验结果
1: Accuracy of model is 0.5639
2: F1-macro of model is 0.8210
3: F1-micro of model is 0.9161
4. 注意事项
(1)把学习从1e-4调大到1e-3后,模型的收敛速度明显快了很多,而且效果有了非常大的提升, 所以一开始还是应该选择比较大的学习率,再配合学习率衰减。如果一开始学习率就很小,学习的速度会非常慢,最终也难以得到较优的结果。
(2)在计算loss时加 class weights, 是为了处理类别不平衡问题。可是我多次用 pytorch 训练模型的经验告诉我,这个 class weights 不能加
加 class weights 的结果
1: Accuracy of model is 0.2472
2: F1-macro of model is 0.7500
3: F1-micro of model is 0.8459
不加 class weights 的结果
1: Accuracy of model is 0.5639
2: F1-macro of model is 0.8210
3: F1-micro of model is 0.9161