1. 为什么选择Transformer做新闻分类?
新闻文本分类是自然语言处理中的经典任务。过去大家常用RNN、CNN这些传统神经网络,但自从Transformer横空出世,局面就完全改变了。我第一次用Transformer做分类任务时,准确率直接比LSTM提升了8个百分点,当时就惊掉了下巴。
Transformer的核心优势在于它的注意力机制。想象你在读新闻时,不会平均关注每个字,而是会重点看关键词。比如"央行宣布降准"这句话,"央行"和"降准"才是重点。Transformer的self-attention机制正是模拟了这个过程,它能自动学习哪些词更重要。
我用一个简单例子说明注意力机制有多神奇。下面这段财经新闻:
"美联储宣布加息50个基点,导致纳斯达克指数暴跌3.2%"
传统模型可能平均处理每个词,但Transformer会给"美联储"、"加息"、"暴跌"这些词更高权重。这种特性让它特别适合处理新闻文本,因为新闻的关键信息往往集中在少数关键词上。
2. 快速搭建Transformer分类模型
2.1 数据准备
我们先准备一个典型的新闻数据集,包含7个类别:
- 教育
- 科技
- 社会
- 时政
- 财经
- 房产
- 家居
每个类别1万条数据,共7万条。数据质量很高,已经去除了乱码和特殊符号。我从实际项目经验中发现,数据清洗比模型选择更重要。曾经有个项目因为数据里有大量HTML标签,导致模型效果奇差,清洗后准确率直接涨了15%。
数据预处理主要做三件事:
- 中文分词(我用jieba)
- 去除停用词
- 文本长度统一(设为200个词)
import jieba from keras.preprocessing.text import Tokenizer # 加载停用词 stop_words = pd.read_csv("stopwords.txt")['stopword'].values # 分词函数 def cut_text(text): return " ".join([w for w in jieba.lcut(text) if w not in stop_words]) # 构建词表 tokenizer = Tokenizer(num_words=10000) tokenizer.fit_on_texts(df['cutword'])2.2 模型构建
我用的是Keras实现的Transformer编码器。相比原始Transformer,做了两点简化:
- 只用Encoder部分(分类任务不需要Decoder)
- 简化了位置编码的实现
from tensorflow.keras import layers class TransformerEncoder(layers.Layer): def __init__(self, embed_dim, dense_dim, num_heads): super().__init__() self.attention = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim) self.dense_proj = keras.Sequential([ layers.Dense(dense_dim, activation="relu"), layers.Dense(embed_dim) ]) self.layernorm1 = layers.LayerNormalization() self.layernorm2 = layers.LayerNormalization() def call(self, inputs): attn_output = self.attention(inputs, inputs) proj_input = self.layernorm1(inputs + attn_output) proj_output = self.dense_proj(proj_input) return self.layernorm2(proj_input + proj_output)完整模型结构如下:
- 输入层(最大长度200)
- 词嵌入层(维度128)
- Transformer编码器(4个头)
- 全局池化
- 全连接分类层(7个输出)
inputs = layers.Input(shape=(200,)) x = layers.Embedding(10000, 128)(inputs) x = TransformerEncoder(128, 32, 4)(x) x = layers.GlobalMaxPooling1D()(x) outputs = layers.Dense(7, activation="softmax")(x) model = keras.Model(inputs, outputs)3. 模型训练与调优技巧
3.1 训练配置
我推荐这些超参数设置:
- Batch Size: 128
- 初始学习率: 3e-5
- Epochs: 10
- 优化器: AdamW(带权重衰减)
关键技巧是使用学习率warmup:前10%的step线性增加学习率,避免初期震荡。我在多个项目中发现这能提升最终准确率1-2个百分点。
from transformers.optimization_tf import AdamWeightDecay optimizer = AdamWeightDecay( learning_rate=3e-5, weight_decay_rate=0.01 ) model.compile(optimizer=optimizer, loss="sparse_categorical_crossentropy", metrics=["accuracy"])3.2 早停与模型保存
为了防止过拟合,我设置了两个回调:
- EarlyStopping(验证loss 3轮不下降就停止)
- ModelCheckpoint(只保存最佳模型)
callbacks = [ keras.callbacks.EarlyStopping(patience=3), keras.callbacks.ModelCheckpoint("best_model.h5", save_best_only=True) ] history = model.fit( train_ds, validation_data=val_ds, epochs=10, callbacks=callbacks )3.3 效果评估
在测试集上,这个模型达到了96.2%的准确率。混淆矩阵显示最容易混淆的是"财经"和"时政"类新闻,这很合理因为它们经常有重叠词汇。
我常用的评估指标组合:
- 准确率(整体效果)
- F1分数(处理类别不平衡)
- Cohen's Kappa(考虑随机猜测)
from sklearn.metrics import classification_report y_pred = model.predict(test_ds) print(classification_report(y_true, y_pred.argmax(axis=1)))4. 模型部署实战
4.1 保存完整pipeline
部署时不仅要保存模型,还要保存tokenizer和标签编码器。我推荐用pickle保存这些组件:
import pickle # 保存模型 model.save('news_classifier.h5') # 保存tokenizer with open('tokenizer.pkl', 'wb') as f: pickle.dump(tokenizer, f) # 保存标签编码器 with open('label_encoder.pkl', 'wb') as f: pickle.dump(label_encoder, f)4.2 Flask API部署
最简单的部署方式是Flask。下面是一个完整的API实现:
from flask import Flask, request, jsonify import tensorflow as tf import pickle app = Flask(__name__) # 加载组件 model = tf.keras.models.load_model('news_classifier.h5') with open('tokenizer.pkl', 'rb') as f: tokenizer = pickle.load(f) with open('label_encoder.pkl', 'rb') as f: label_encoder = pickle.load(f) @app.route('/predict', methods=['POST']) def predict(): text = request.json['text'] # 预处理 seq = tokenizer.texts_to_sequences([text]) padded = tf.keras.preprocessing.sequence.pad_sequences(seq, maxlen=200) # 预测 pred = model.predict(padded) # 返回结果 return jsonify({ 'class': label_encoder.inverse_transform([pred.argmax()])[0], 'prob': float(pred.max()) }) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)4.3 使用Hugging Face Hub
如果想更专业的部署,可以上传到Hugging Face Model Hub:
from huggingface_hub import push_to_hub_keras push_to_hub_keras( model, "your-username/news-classifier", include_optimizer=False )上传后就可以用一行代码加载:
model = keras.models.load_from_hub("your-username/news-classifier")5. 实际应用中的经验分享
在真实项目中,我遇到过几个典型问题:
长文本处理:新闻有时很长,超过模型最大长度。我的解决方案是先分段,然后对各段预测后投票。
新词问题:遇到"元宇宙"这种新词时,tokenizer会拆分成未知标记。解决方法是用领域数据微调tokenizer。
类别不平衡:某些类别样本少。我采用过采样+类别权重组合,效果最好。
领域迁移:财经新闻模型用在科技新闻上效果下降。这时需要用少量目标领域数据微调。
一个实用的技巧是建立反馈循环:把线上预测不确定的样本(预测概率<0.7)保存下来,定期人工标注后加入训练集。这套方法让我的一个客户项目准确率从92%提升到了96.5%。
最后提醒大家,部署后一定要监控模型表现。我见过最惨的案例是某个新闻APP的分类模型,因为新闻分布变化,半年后准确率从95%跌到了60%都没人发现。建议设置准确率报警,当周环比下降超过5%时触发警告。