GRU-Attention 模型在中文新闻分类中的实践与优化
1. GRU-Attention模型为何适合中文新闻分类中文新闻文本分类是个挺有意思的任务。我最早接触这个领域时试过各种传统机器学习方法比如朴素贝叶斯和SVM效果总是不尽如人意。后来转向深度学习发现GRU网络特别适合处理这种序列数据尤其是中文这种上下文关联性强的语言。GRU门控循环单元可以看作是LSTM的简化版它把LSTM的三个门输入门、遗忘门、输出门简化为两个门更新门和重置门。这种设计让模型训练起来更快而且在实际应用中我发现对于中文新闻这种中等长度的文本GRU的表现往往不比LSTM差。但GRU有个问题它平等对待所有时间步的输出。这在新闻分类中不太合理因为一篇文章里总有那么几个关键词对分类特别重要。比如科技新闻里的人工智能、算法体育新闻里的比赛、进球这些词。这时候就需要Attention机制来帮忙了。Attention机制就像我们人类阅读时的注意力一样能够自动聚焦在文本的关键部分。我在实际项目中做过对比实验加入Attention后模型在科技新闻分类上的准确率能提升3-5个百分点。特别是对于那些专业术语多的领域效果更明显。2. 构建GRU-Attention模型的完整流程2.1 数据预处理的关键细节中文新闻数据预处理有几个坑我踩过不少次。首先是分词直接用jieba默认词典效果可能不够好。我的经验是收集一些领域术语构建自定义词典。比如处理财经新闻时要把GDP、CPI这些专业术语加进去。停用词处理也很关键。常见的停用词表可能不适合新闻场景比如记者、报道这类词在很多新闻里都会出现但它们对分类帮助不大。我通常会根据词频统计自己整理一个新闻专用的停用词表。词向量方面我建议使用预训练的中文词向量。我用过腾讯AI Lab开源的800万词向量效果不错。如果数据量特别大也可以考虑自己用Word2Vec或FastText训练。这里有个小技巧把新闻标题和正文分开处理因为标题中的词往往更重要。2.2 模型搭建的代码实现下面是我常用的GRU-Attention模型实现使用Keras框架from tensorflow.keras.layers import Input, GRU, Dense, Dropout from tensorflow.keras.models import Model from tensorflow.keras.layers import Layer import tensorflow.keras.backend as K class AttentionLayer(Layer): def __init__(self, **kwargs): super(AttentionLayer, self).__init__(**kwargs) def build(self, input_shape): self.W self.add_weight(nameatt_weight, shape(input_shape[-1], 1), initializernormal) self.b self.add_weight(nameatt_bias, shape(input_shape[1], 1), initializerzeros) super(AttentionLayer, self).build(input_shape) def call(self, x): et K.squeeze(K.tanh(K.dot(x, self.W) self.b), axis-1) at K.softmax(et) at K.expand_dims(at, axis-1) output x * at return K.sum(output, axis1) def build_model(max_len, vocab_size, embedding_dim, embedding_matrix): input_layer Input(shape(max_len,)) embedding Embedding(vocab_size, embedding_dim, weights[embedding_matrix], trainableFalse)(input_layer) gru_out GRU(128, return_sequencesTrue)(embedding) attention_out AttentionLayer()(gru_out) dropout Dropout(0.5)(attention_out) output Dense(5, activationsoftmax)(dropout) # 假设有5个新闻类别 model Model(inputsinput_layer, outputsoutput) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy]) return model这个实现有几个注意点我使用了自定义的Attention层这样更灵活Embedding层通常设为不可训练除非数据量很大在Attention层后加了Dropout防止过拟合输出层的神经元数量要等于新闻类别数3. 训练过程中的优化技巧3.1 学习率调整策略训练GRU-Attention模型时学习率的设置很关键。我一般会采用学习率衰减策略。比如初始学习率设为0.001然后每3个epoch衰减一次。Keras里有现成的回调函数可以用from tensorflow.keras.callbacks import ReduceLROnPlateau reduce_lr ReduceLROnPlateau(monitorval_loss, factor0.5, patience3, min_lr1e-6)另外早停(Early Stopping)也很重要。新闻数据通常噪声比较大模型容易过拟合。我会设置patience为5即验证集loss连续5次不下降就停止训练。3.2 Batch Size的选择Batch Size对模型效果影响很大。我的经验是数据量小10万条用32或64数据量中等10-50万条用128或256数据量大50万条可以用512不过要注意Batch Size太大会占用大量显存。我用NVIDIA 2080Ti显卡时batch size256时显存就快满了。3.3 处理类别不平衡问题新闻数据经常出现类别不平衡。比如体育新闻可能比科技新闻多很多。我常用的解决方法有在损失函数中使用类别权重对少数类别过采样在模型评估时看F1值而不是准确率在Keras中实现类别权重很简单class_weight {0: 1.5, 1: 1.2, 2: 1.0, 3: 0.8, 4: 1.3} # 根据各类别样本数设置 model.fit(..., class_weightclass_weight)4. 模型效果分析与调优4.1 评估指标的选择新闻分类不能只看准确率。我通常会看以下几个指标准确率Accuracy整体分类正确率精确率Precision预测为某类的样本中实际正确的比例召回率Recall实际为某类的样本中被正确预测的比例F1值精确率和召回率的调和平均特别是对于那些容易混淆的类别比如科技和数码要单独看它们的精确率和召回率。4.2 常见问题及解决方法在实践中我遇到过几个典型问题问题1模型对某些类别表现特别差解决方法检查这些类别的样本量是否足够查看混淆矩阵看是否和其他类别混淆严重增加这些类别的样本或调整类别权重问题2验证集loss波动大解决方法减小学习率增加Batch Size检查数据预处理是否有问题问题3训练集表现好但验证集差解决方法增加Dropout比例减少GRU单元数增加L2正则化4.3 注意力权重的可视化Attention机制的一个好处是可以可视化注意力权重看看模型关注了哪些词。我常用的可视化代码def visualize_attention(text, model, tokenizer, max_len): tokens tokenizer.texts_to_sequences([text]) padded pad_sequences(tokens, maxlenmax_len, paddingpost) attention_layer model.get_layer(attention_layer) func K.function([model.input], [attention_layer.output]) attention_weights func([padded])[0] words jieba.lcut(text) plt.figure(figsize(10,5)) plt.bar(range(len(words)), attention_weights[0][:len(words)]) plt.xticks(range(len(words)), words, rotation90) plt.show()这个可视化可以帮助我们理解模型的决策过程发现一些有趣的现象。比如我发现模型经常会给新闻标题中的词更高的注意力权重这很符合人类的阅读习惯。