2026/10/9 2:15:53

搜狗新闻语料库中文文本分类全流程:从TF-IDF基线到BERT微调

搜狗新闻语料库中文文本分类全流程:从TF-IDF基线到BERT微调 简介一份基于搜狗新闻语料库的中文文本分类实践项目同时覆盖传统机器学习与预训练模型两类路线适合人工智能、计算机、自动化等专业在校生用于课程设计、毕业设计或算法对比。资源共15个文件主体为6个脚本分别承担语料切分、数据加载、特征构造、模型训练与结果评估另含4张可视化图片准确率/损失曲线、词云图、2个文本文件词典与停用词、1份说明文档及版本管理配置整体结构简洁下载后可按说明快速复现。包体仅208KB轻量无冗余目前已有79人学习下载。项目源自高分答辩案例代码经过运行验证从数据预处理、特征工程到传统机器学习与卷积神经网络建模均有完整实现既适合入门理解中文文本分类全流程也便于在此基础上扩展新模型或调整参数用于实验对比。1. 中文文本分类为什么绕不开搜狗新闻语料库这道坎做中文文本分类最尴尬的不是模型选不好而是数据集先让一半人翻车。搜狗新闻语料库是入门这个方向时最常见的“正规军”语料规模够大、类别清晰、正文质量稳定比手工标注几百条玩具数据有意义得多。这个项目同时覆盖传统机器学习方法与预训练模型两条路线——先用TF-IDF加线性分类器打一个便宜又可靠的基线再用BERT这类预训练模型把准确率往上推几个点。对正在做课程设计、准备算法岗位面试或者想系统走一遍机器学习应用流程的人来说把这条路走通一次后面换任何中文语料处理套路基本都能复用。下面按照“语料预处理 → 传统机器学习基线 → 预训练模型微调 → 常见坑排查 → 评估与提分”的顺序展开。每一步都给出可以照抄的代码和参数也会把失败时该看什么指标说清楚。2. 搜狗新闻语料库的预处理与切分文本分类的“地基”工程2.1 原始XML解析与清洗编码、脏数据与低频噪声过滤搜狗新闻语料库的原始文件是XML格式每条新闻包含url、docno、contenttitle、content等字段。设想一种常见翻车场景直接用open()读出文本后做字符串匹配结果文档里既有HTML实体又有转义符正则匹配出来的内容断断续续。常见做法是先用xml.etree.ElementTree解析对解析失败的行直接丢弃不要试图逐条修复因为一条脏数据在训练时污染的是整个batch的梯度方向。import xml.etree.ElementTree as ET import re def parse_sogou_xml(xml_path): with open(xml_path, r, encodingutf-8) as f: content f.read() # 去掉XML声明与DOCTYPE否则解析器可能被特殊字符卡死 content re.sub(r\?xml.*?\?, , content) content re.sub(r!DOCTYPE.*?, , content, flagsre.S) root ET.fromstring(content) texts, labels [], [] for doc in root.iter(doc): title doc.findtext(contenttitle, ).strip() body doc.findtext(content, ).strip() if len(title) len(body) 50: continue # 过滤空文档和无信息量占位内容 texts.append(f{title}\n{body}) return texts这里两个正则替换是为了剥掉XML声明和DTD引用因为部分语料转存版本里带外部实体声明本地解析环境一旦受限就直接抛错。长度小于50的过滤阈值是经验值新闻语料里大量“点击查看全文”“更多精彩内容”之类的占位文本正文过短的样本没有分类价值。标签获取方式有两条路原始语料按频道目录组织常见类别有体育、财经、娱乐、科技等最省事的是直接用目录名当标签。但目录和内容经常错位比如“科技”频道里混着大量企业财报解读稿内容更接近财经。更稳妥的办法是使用项目中整理好的类别映射文件通常搜狗分类语料的标准映射是C000008对应教育、C000010对应IT科技、C000014对应军事、C000016对应体育、C000020对应财经这类格式。这个映射一旦错位后面全部白跑。2.2 标签编码与分层采样验证集不贴近真实分布等于白做标签文本要转成数值ID。常见序列化方式是用sklearn的LabelEncoderfrom sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split le LabelEncoder() y le.fit_transform(labels) # [体育,财经,...] - [0,1,...] X_train, X_temp, y_train, y_temp train_test_split( texts, y, test_size0.2, stratifyy, random_state42) X_val, X_test, y_val, y_test train_test_split( X_temp, y_temp, test_size0.5, stratifyy_temp, random_state42)这段先把数据按8:2切出训练集和临时集再把临时集按1:1切成验证集和测试集。stratifyy的作用是按类别比例分层采样保证每个类别在三个集合中的占比都和原始分布一致。很多人在这一步偷懒不写stratify结果“体育”类样本占比大随机切分后验证集里体育类偏少模型验证准确率被其他类拖低这种方差大于真实水平的现象在调参时异常迷惑。样本数量同样有讲究。验证集至少留够2000条才能看到平滑的训练曲线测试集建议保留5000条以上否则两个模型之间1%的准确率差异很难判断是真实差距还是采样运气。随机种子固定为42保证实验可复现调参时对照结果才有意义。2.3 中文分词与停用词jieba加载自定义词典的正确姿态中文和英文在分类任务上最大的差异是分词。英文天然空格分词中文必须靠分词器。jieba是最常见的选择但默认词典对领域词不友好“量子计算”“碳中和”“新能源汽车”这类词经常被切成单字。解决办法是准备一个自定义词典每行格式为“词 词频 词性”。import jieba jieba.load_userdict(user_dict.txt) def cut_text(text): words jieba.lcut(text) # 过滤单字和纯数字保留语义密度更高的词 return .join([w for w in words if len(w.strip()) 1 and not w.isdigit()])词频数字的作用是控制分词器把它当整体的倾向比如“新能源汽车”词频给到10它就很少再被拆成“新能源/汽车”。分词之后停用词表的取舍要谨慎。通用停用词表里往往包含“不”“没”“别”这类否定词主题分类任务直接删掉影响不大但如果你的分类体系里带有情感倾向或观点极性比如财经新闻里“不看好后市”被删掉“不”语义直接反转分类结果自然跑偏。所以对中文文本分类停用词表要人工review后使用不要直接拉个开源表一把梭。3. 传统机器学习路线TF-IDF特征、基线分类器与阈值调优3.1 TF-IDF向量化的四个必调参数max_features与ngram_range的取舍传统机器学习方法的核心是把文本变成数值矩阵TF-IDF是最常用的特征表示。很多人直接用默认参数跑效果不差但也不理想。搜狗新闻语料词表规模动辄几十万全量保留会带来大量低频噪声和高维稀疏矩阵线性分类器的训练时间成倍上升准确率却只涨零点几个百分点。from sklearn.feature_extraction.text import TfidfVectorizer tfidf TfidfVectorizer( max_features50000, ngram_range(1, 2), min_df2, max_df0.8, sublinear_tfTrue ) X_train_vec tfidf.fit_transform(X_train) X_val_vec tfidf.transform(X_val) X_test_vec tfidf.transform(X_test)四个参数的取舍逻辑分别是max_features50000限定词表大小保留出现频率最高的5万个词兼顾信息量与训练开销ngram_range(1, 2)把相邻两个词也作为特征“机器学习”比“机器”“学习”两个词连在一起更具备类别区分度min_df2过滤只出现一次的词这些大多是错别字和打印噪声max_df0.8过滤出现在80%以上文档里的词这类虚词对分类没有判别力。sublinear_tfTrue用1log(tf)替代原始词频削弱长文档里高频词对分类决策的过度支配。3.2 朴素贝叶斯、逻辑回归与线性SVM三类基线模型的实测对比特征准备好以后分类器怎么选搜狗新闻语料这种高维稀疏的TF-IDF特征下逻辑回归和线性SVM是公认最能打的两个。朴素贝叶斯在短文本任务上有优势但新闻正文动辄几百字多个类别共享大量词汇贝叶斯“特征独立”的假设在长文本上基本立不住实测通常比逻辑回归低2到3个百分点。我一般会把三个模型都跑一遍用验证集准确率决定决赛选手from sklearn.naive_bayes import MultinomialNB from sklearn.linear_model import LogisticRegression from sklearn.svm import LinearSVC models { nb: MultinomialNB(alpha0.3), lr: LogisticRegression(C1.0, max_iter1000), svm: LinearSVC(C1.0, max_iter2000) } for name, model in models.items(): model.fit(X_train_vec, y_train) print(f{name} val_acc: {model.score(X_val_vec, y_val):.4f})三个模型的差异值得展开。朴素贝叶斯的alpha是拉普拉斯平滑项调大到0.3能有效抑制未登录词带来的零概率问题逻辑回归的C是正则化强度的倒数特征维度高时C1.0容易过拟合需要按10倍间隔在验证集上搜索LinearSVC碰到不收敛时把max_iter提到3000以上或者设dualFalse切换到原问题求解。LinearSVC在准确率上和逻辑回归咬得很死但SVC不直接输出概率后面做置信度阈值处理时还要额外校准。综合来看逻辑回归的工程性价比最高——训练快、输出概率、调参直觉清晰。模型训练速度验证集准确率参考特点朴素贝叶斯最快86% - 88%对短文本友好长文本受独立性假设拖累逻辑回归快89% - 91%概率输出方便后续阈值调整和方法融合线性SVM快89% - 91%决策边界更硬概率输出需要额外校准3.3 置信度阈值与交叉验证把传统机器学习模型的分数再压榨一截模型跑通后往下走的关键一步是不要盲信最大概率输出。新闻分类里经常出现概率分布平坦的样本比如标题含“iPhone”正文含“发布会”的稿子在“科技”和“财经”上各给三成概率。此时一个直接有效的办法是引入置信度阈值低置信样本标为“待人工复审”而不是硬分到某一类prob lr_model.predict_proba(X_val_vec) max_prob prob.max(axis1) threshold 0.4 pred prob.argmax(axis1) low_conf max_prob threshold pred[low_conf] -1 # 置信度不足标记为待复核在这个数据集上把高置信样本单独拎出来精确率能从88%提到93%以上代价是大约5%到8%的样本进入人工通道。阈值0.4不是拍脑袋定的需要结合验证集上的混淆矩阵先看哪些类错得最集中再决定是调整特征、换分类器还是降低阈值。这里最重要的原则是“调阈值先于换模型”这也是整个机器学习应用流程里最容易跳过的一环。逻辑回归的predict_proba输出的是类别概率分布做阈值判断时直接对max值做比较即可。4. 预训练模型路线BERT与RoBERTa中文模型微调的全流程4.1 模型选型BERT-base-chinese合适还是换成RoBERTa传统机器学习做到90%上下再往上提升就得依赖预训练模型对上下文语义的建模能力。在搜狗新闻语料这类长文本上BERT-base-chinese微调后往往能到95%以上。那什么时候换RoBERTa中文预训练模型我的判断标准是看文本里是否存在大量反讽、双关、隐喻类表达。如果是情感倾向分类或观点极性判断RoBERTa的动态掩码和更大规模预训练语料带来的增益会明显放大如果只是主题分类词汇层面的信号已经足够强BERT-base的差距并不大没必要为了一个百分点付出两倍训练时间。from transformers import AutoTokenizer, AutoModelForSequenceClassification model_name bert-base-chinese # 文本语言现象复杂时可换 hfl/chinese-roberta-wwm-ext tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForSequenceClassification.from_pretrained( model_name, num_labels10 )资源受限的环境里也可以考虑hfl/chinese-roberta-wwm-ext它在中文上使用了全词掩码比原版BERT更适合词边界不明确的中文场景模型体积和推理速度与BERT相当。4.2 Tokenizer与数据集构建长度截断的正确姿势与token分布检查预训练模型用的是子词粒度不需要jieba分词BertTokenizer会自己处理中文切分。最常见的坑是长度对齐搜狗新闻正文动不动上千字BERT的序列长度上限是512默认硬截断会直接丢掉新闻末尾的关键事实。新闻语料的特征是信息密度前重后轻常见做法是保留标题和正文前256个字符def preprocess_for_bert(text, max_len256): # 新闻标题正文前256字符保留信息密度最高的部分 text text[:max_len] enc tokenizer(text, paddingmax_length, truncationTrue, max_lengthmax_len, return_tensorspt) return {k: v.squeeze(0) for k, v in enc.items()}paddingmax_length的好处是batch形状统一省去动态padding的复杂度代价是256长度的序列里有大量[PAD]标记占位注意力机制虽然不会从[PAD]中获取信息但计算量仍然浪费在这些位置上。显存充裕时把max_len提到384通常能再涨半个到一个点超过512则直接截断损失不可控。另外建议在切片后打印token长度分布确认截断没有造成大面积信息丢失——如果超过30%样本的实际有效token数接近max_len说明截断策略已经严重影响模型可见信息量。4.3 微调超参learning_rate、batch size、epochs与warmup的搭配微调阶段最常被问的超参数是学习率、batch size和epoch数。配置文件如下from transformers import Trainer, TrainingArguments training_args TrainingArguments( output_dir./checkpoints, num_train_epochs3, per_device_train_batch_size32, per_device_eval_batch_size64, learning_rate2e-5, warmup_ratio0.1, weight_decay0.01, logging_dir./logs, evaluation_strategyepoch, save_strategyepoch, load_best_model_at_endTrue, metric_for_best_modeleval_loss )learning_rate2e-5是BERT微调的标准起点比这个再大容易让预训练权重出现灾难性遗忘。warmup_ratio0.1意味着前10%的训练步数从接近0的学习率爬升到2e-5防止早期大步长冲乱预训练参数。3个epoch在新闻分类上通常够用训练到第3个epoch末尾时验证集loss基本不再下降再往上加epoch容易让模型记训练集的噪声。训练时要同时盯验证集loss和准确率。如果loss在下降但准确率不涨先怀疑标签映射是否错位如果loss不降反而震荡把learning_rate降到1e-5重试。手动搜索超参数很费时间但读tensorboard曲线比盲跑完再回来猜原因快得多。一个我常用的技巧是先用1%的数据跑2个epoch验证数据管线确认模型能正常收敛后再放大到全量。5. 文本分类避坑指南数据泄漏、标签噪声与训练不收敛的5条踩坑记录5.1 数据泄漏让验证集虚高先切分还是先向量化的顺序问题现象验证集准确率高达97%测试集却只有85%差距大到不合理。 原因很多人图省事先对全部文本做fit_transform再切训练集和验证集。TF-IDF的词表统计是从全量数据里学到的相当于验证集的信息提前参与了特征构建验证准确率虚高。这个错误隐蔽性强因为训练和验证时代码都不报错。 解决先切分、后向量化。训练集只调用fit_transform验证集和测试集永远只调transform。检查方法是在向量化后打印tfidf.vocabulary_里是否包含验证集独有的词出现则说明数据泄漏。5.2 停用词表删掉否定词主题分类的特征噪声来自清洗过度现象分类器对“不看好汽车后市”这类财经样本全部预测为“看好汽车”准确率在特定类别上崩盘。 原因用的开源停用词表里包含“不”“没”“别”等否定词。中文里否定词附着在核心词前后简单删除后语义极性直接反转。对主题分类任务影响有限但财经、娱乐、舆情类带倾向性判断的任务会出大问题。 解决加载停用词表后强制过滤掉否定词和程度副词或者干脆只保留标点和纯语气词。更稳妥的办法是统计训练集里出现频率最高的1000个词人工review后生成定制停用词表而不是直接套用英文翻译过来的通用表。5.3 类别分布不均导致“体育类通吃”宏观F1与类别加权双管齐下现象整体准确率75%看起来还过得去但“文化”类别的召回率只有20%模型把大多数样本都预测成样本量最大的“体育”类。 原因损失函数被大头类别主导小类别的分类错误对总loss贡献太小模型学会了偷懒。 解决传统机器学习路线里给逻辑回归加class_weightbalanced让损失按类别失衡量加权预训练模型路线里需要手动构造类别权重传给CrossEntropyLossfrom torch.nn import CrossEntropyLoss # 每个类别的权重按样本数比例取倒数后归一化 class_weights torch.tensor([...]).to(device) loss_fct CrossEntropyLoss(weightclass_weights)判断模型是否真正学会了小类别不能只看整体准确率要看宏观F1即所有类别F1的算术平均。一个分类器把体育类全分对、文化类全分错宏观F1也只有0.4左右。5.4 长文本截断丢掉关键信息标题首尾段的截断策略现象验证集上分类错误集中在正文较长的样本错误类型以“预测类别与标题强相关但与正文弱相关”为主。 原因BERT硬截断只保留前512个字符但新闻正文的结论性语句有时出现在文末比如“综合以上数据本季度净利润大幅下滑”这句丢失后类别判断失去关键依据。 解决对长文采用三段式截断——标题全量保留正文取前200字符和后100字符拼接def smart_truncate(title, body, max_len256): head body[:200] tail body[-100:] text f{title}\n{head}\n{tail} return text[:max_len]标题是分类信号最密集的位置正文尾部是结论性话语高频区域这两段组合起来比纯前512字符的信息利用率更高。代价是丢失正文中段的细节但对主题分类而言损失可控。5.5 BERT微调OOM与loss不收敛显存、batch size与学习率的三角关系现象per_device_train_batch_size32直接OOM报错CUDA out of memory调小batch size后loss开始震荡。 原因batch size和learning rate是联动的。learning rate2e-5是匹配batch size32的经验值改成batch size8后梯度估计的噪声变大2e-5的学习率过冲loss自然不降。 解决OOM时先降batch size到16或8同时把learning rate按比例下调到1e-5甚至5e-6。另一个办法是保持batch size32不变用梯度累积模拟大batchtraining_args TrainingArguments( per_device_train_batch_size8, gradient_accumulation_steps4, learning_rate2e-5 )梯度累积每4个step累加一次梯度并更新参数等效于batch size32显存只占原来的四分之一。这样学习率不用动收敛行为基本不变。6. 想拿高分混淆矩阵、错误样本回看与“标题正文”加权推理跑通一个模型只是起点拿高分的关键在分析和融合两个环节。先打印混淆矩阵看错在哪from sklearn.metrics import confusion_matrix, classification_report import numpy as np y_pred lr_model.predict(X_test_vec) print(classification_report(y_test, y_pred, target_namesle.classes_)) cm confusion_matrix(y_test, y_pred) # 打印每行中除对角元外最大值定位“哪两个类最容易混淆” for i, cls in enumerate(le.classes_): err_idx np.argsort(cm[i])[-2] if cm[i][err_idx] 20: print(f{cls} 最常被误分为 {le.classes_[err_idx]}数量 {cm[i][err_idx]})比如“科技”和“财经”经常被混淆说明分类器被“上市”“融资”“估值”这类共现词迷惑。回看几十条错误样本如果人类标注者也觉得模糊说明不是模型问题而是标签定义边界不清如果人类能轻松判断说明缺信号可考虑把标题单独编码后和正文向量拼接或者直接用标题预测结果和正文预测结果做加权投票。预训练模型路线里一个高性价比做法是对标题和正文分别预测再用权重融合final_score 0.6 * title_pred 0.4 * body_pred final_label final_score.argmax(axis1)标题与正文权重比为6:4时通常效果最好因为新闻标题是人工凝练的主题词监督信号比正文更稠密。我在这个数据集上反复调过几次标题权重从0.5提到0.6时整体准确率大概能提升0.5到1个百分点再往高权重的方向走又会因为标题过度概括而牺牲部分细分类别的召回率。最后不管是传统机器学习模型还是预训练模型建议把所有实验结果按“模型参数验证指标测试指标”记录成一张总表。这个项目中不同模型测试集准确率参考区间为TF-IDF加逻辑回归在90%上下BERT微调在95%上下使用RoBERTa和加权推理后能再逼近96%到97%。如果你也在做这个方向第一步先把数据管线跑通别急着上先进模型数据泄漏和标签错位这两个坑不处理好换任何模型都是白费算力。做完一次完整流程后你会对整个中文文本分类的落地门槛有更准确的判断。希望帮到你。本文还有配套的精品资源点击获取