资讯动态

BERT中文情感分析实战:1万语料3轮迭代达81.2%准确率

发布时间:2026/10/9 3:01:57 来源:尧图企业网站定制
简介这是一份面向课程设计与 NLP 入门者的 BERT 情感分析实战资源基于 Python 与 TensorFlow 实现中文文本三分类正向、无情感、负向。项目利用一万余条语料训练语言模型并迭代 3 次在三千余条测试集上验证准确率达 81.2%召回率 76%F1 值 78.5%可作为课程设计、毕业设计或情感分析入门的完整参考方案。压缩包共 41 个文件约 2.64MB包含 16 个 py 脚本模型训练、特征提取、分类预测等、10 个 txt 语料与说明、4 个 md 文档、4 个 xml 配置以及 ui 界面文件、ipynb 笔记本和 docx 实验报告覆盖从数据预处理、模型微调到 GUI 演示的完整链路。目前已有 364 人学习下载。读者可据此复现 BERT 中文情感分类流程理解预训练模型微调、语料组织与评估指标计算并借助界面文件与报告文档快速搭建可演示的情感分析系统。1. 拆开这个 BERT 情感分析包1 万语料、3 轮迭代、81.2% 准确率是怎么跑出来的做中文情感分析的同学大概率都经历过这个阶段用 SnowNLP 或者情感词典跑一版发现微博评论、电商评价里的反讽、双重否定、网络新词根本压不住准确率卡在 70% 上下上不去。这个编号 100010702 的课程设计包走的是另一条路——用 BERT 预训练模型做微调正向、无情感、负向三分类1 万多条语料训练迭代 3 次在 3000 多条测试集上拿到准确率 81.2%、召回率 76%、F1 值 78.5%。这个数字放在课程设计里算扎实的不是那种拿 demo 数据刷出来的虚高指标。包里给的东西挺全modeling.py、optimization.py、tokenization.py是 BERT 原版核心三件套run_classifier.py和run_classifier_with_tfhub.py是分类入口extract_features.py抽特征create_pretraining_data.py和run_pretraining.py负责预训练流程另外还有app_ui.py、gui.ui做界面data目录下塞了train.txt、dev.txt、test1.txt、weibo.txt、tt.txt等一堆语料文件。适合谁适合正在做 NLP 课程设计、想拿一份能跑通的中文 BERT 微调代码当底子的人也适合已经会调transformers但想看看原版 TensorFlow 实现长什么样的同学。下面按「资源是什么 → 怎么用 → 坑在哪」的顺序拆。2. 环境与数据准备从 requirements.txt 到三分类语料格式2.1 依赖安装与版本对齐这个包基于 TensorFlow 1.x 时代的 BERT 原版代码requirements.txt里大概率锁的是tensorflow1.11、numpy、six这类老依赖。直接pip install -r requirements.txt在 Python 3.8 以上环境里翻车概率很高因为 TF 1.x 对高版本 Python 支持很差。我一般会单独建一个 Python 3.7 的虚拟环境先把 TF 版本钉死。# 建独立环境别污染主环境 conda create -n bert_sentiment python3.7 -y conda activate bert_sentiment # 装 TF 1.15这是 1.x 最后一个稳定版兼容性最好 pip install tensorflow1.15.0 pip install numpy1.18.5 six1.15.0逻辑说明TF 1.15 是 1.x 系列收尾版本对tf.contrib支持完整而原版 BERT 代码大量依赖tf.contrib。参数上numpy别装太新1.20 以后移除了np.float等别名老代码会报AttributeError。如果你机器上已经有 TF 2.x别想着直接跑tf.contrib在 2.x 里彻底没了改起来工作量比重新建环境大。2.2 三分类语料的格式要求BERT 分类任务对数据格式有硬要求每行一条样本文本\t标签标签从 0 开始连续编号。这个包里data/train.txt应该就是这个格式。三分类对应关系常见做法是0 负向、1 无情感、2 正向但具体得看run_classifier.py里DataProcessor子类的get_labels()返回顺序别自己猜。# 检查语料格式是否符合 BERT 输入要求 import codecs def check_corpus(path): label_set set() with codecs.open(path, r, utf-8) as f: for i, line in enumerate(f): parts line.strip().split(\t) if len(parts) ! 2: print(f第 {i} 行格式异常: {line[:50]}) continue text, label parts label_set.add(label) if not text: print(f第 {i} 行文本为空) print(f标签集合: {sorted(label_set)}) check_corpus(data/train.txt)逻辑说明split(\t)后必须正好两段多一段少一段都会让后续InputExample构造失败。参数上标签必须是字符串形式的数字BERT 内部会int(label)转换。如果标签集合不是{0,1,2}而是{负向,无,正向}得先做映射否则label_map找不到键直接抛KeyError。这一步看着简单但语料里混进一个空格分隔的行训练时才会报错提前查能省不少时间。2.3 训练/验证/测试集划分摘要里说 1 万多条训练、3000 多条测试这个比例大概是 7:3 或 8:2。包里train.txt、dev.txt、test1.txt应该已经分好了。如果没有用sklearn的train_test_split按标签分层切保证三类的比例在训练集和测试集里一致否则某一类样本太少召回率会很难看。from sklearn.model_selection import train_test_split texts, labels [], [] with codecs.open(data/all.txt, r, utf-8) as f: for line in f: t, l line.strip().split(\t) texts.append(t); labels.append(l) # stratify 保证分层random_state 固定可复现 X_train, X_test, y_train, y_test train_test_split( texts, labels, test_size0.2, stratifylabels, random_state42) with codecs.open(data/train.txt, w, utf-8) as f: for t, l in zip(X_train, y_train): f.write(f{t}\t{l}\n)逻辑说明stratifylabels是关键参数它让切分后各类别占比与原始一致。random_state42固定随机种子保证每次切分结果相同方便复现 81.2% 这个指标。如果不分层万一测试集里负向样本特别少召回率会虚低你调半天模型以为是模型问题其实是数据划分的锅。3. 微调训练全流程run_classifier.py 的参数怎么设3.1 下载预训练权重与配置目录原版 BERT 代码需要一个BERT_BASE_DIR里面放bert_config.json、vocab.txt和bert_model.ckpt.*三个文件。中文场景要用bert-base-chinese或chinese_L-12_H-768_A-12。这个包本身不带权重文件体积太大得自己下。目录结构长这样chinese_L-12_H-768_A-12/ ├── bert_config.json ├── bert_model.ckpt.data-00000-of-00001 ├── bert_model.ckpt.index ├── bert_model.ckpt.meta └── vocab.txtbert_config.json里hidden_size是 768num_hidden_layers是 12num_attention_heads是 12vocab_size是 21128。这几个参数决定了模型大小别乱改改了权重就加载不上。vocab.txt是中文词表21128 个 token如果你的语料里有大量词表外的网络新词会被切成[UNK]这是后面准确率上不去的一个隐藏原因。3.2 启动微调训练run_classifier.py是主入口核心参数就那几个但每个都影响结果。python run_classifier.py \ --task_namesentiment \ --do_traintrue \ --do_evaltrue \ --data_dir./data \ --vocab_file./chinese_L-12_H-768_A-12/vocab.txt \ --bert_config_file./chinese_L-12_H-768_A-12/bert_config.json \ --init_checkpoint./chinese_L-12_H-768_A-12/bert_model.ckpt \ --max_seq_length128 \ --train_batch_size32 \ --learning_rate2e-5 \ --num_train_epochs3.0 \ --output_dir./output逻辑说明task_name必须和run_classifier.py里注册的DataProcessor名字对上这个包大概率自定义了一个SentimentProcessor你得去代码里确认注册名。max_seq_length128对短文本情感分析够用微博、评论一般不超过 128 个字设太大显存吃不消且拖慢速度。learning_rate2e-5是 BERT 微调的经典值太大容易把预训练学到的知识冲掉太小收敛慢。num_train_epochs3.0对应摘要里的「迭代 3 次」这个轮数在 1 万条数据上是合理的再多容易过拟合。参数怎么改如果显存不够把train_batch_size降到 16 或 8同时把learning_rate相应调小到 1e-5因为 batch 小了梯度噪声大。如果 F1 上不去先别急着加轮数检查max_seq_length是不是截断了长文本情感分析里转折词往往在句子后半段截断会丢关键信息。3.3 评估指标解读训练完output目录下会生成eval_results.txt里面是eval_accuracy、eval_precision、eval_recall、eval_f1。摘要给的 81.2% 准确率、76% 召回率、78.5% F1这三个数放一起看有个信息准确率比召回率高 5 个点说明模型偏向保守预测为正向/负向时比较准但漏掉了一些真实情感样本。三分类里「无情感」这一类通常是重灾区因为它的边界最模糊很多中性评论被误判成正向或负向。# 看混淆矩阵定位哪一类拖后腿 from sklearn.metrics import confusion_matrix import numpy as np # 假设 preds 和 labels 是模型输出的预测和真实标签 cm confusion_matrix(labels, preds) print(cm) # 行是真实列是预测对角线是正确数逻辑说明混淆矩阵能直接看出「无情感」被误判成了什么。如果cm[1][0]和cm[1][2]都很大说明中性样本被大量分到两端这时候要么补充中性语料要么在损失函数里给中性类加权。光看一个 F1 值定位不到问题混淆矩阵才是排查的起点。4. 避坑与排查跑不通、指标低、显存爆的常见问题4.1 报错ModuleNotFoundError: No module named tf.contrib现象一运行run_classifier.py就报找不到tf.contrib或者AttributeError: module tensorflow has no attribute contrib。原因装的是 TF 2.x而原版 BERT 代码基于 TF 1.xtf.contrib在 2.x 里被移除。很多人图省事直接pip install tensorflow默认装最新版必翻车。解决降级到 TF 1.15或者用tensorflow.compat.v1加tf.disable_v2_behavior()做兼容但后者改动量大。最省事就是建 Python 3.7 TF 1.15 的独立环境别在主环境里折腾。4.2 训练 loss 不降或震荡现象训练几个 step 后 loss 在 0.9 附近晃不往下走或者忽高忽低。原因学习率设太大或者init_checkpoint没加载成功模型从随机初始化开始训。还有一种可能是语料标签没从 0 开始连续编号label_map映射错位。解决先确认 checkpoint 加载日志里有没有restoring parameters字样没有就是路径写错了。学习率从 2e-5 降到 1e-5 试。检查标签集合是不是{0,1,2}如果出现{1,2,3}这种num_labels会算错输出层维度对不上。4.3 显存不足ResourceExhaustedError现象跑着跑着报 OOM或者一开始就分配不了显存。原因max_seq_length或train_batch_size太大。BERT base 模型本身参数 1.1 亿加上优化器状态显存占用不小。解决max_seq_length从 128 降到 64短文本够用train_batch_size从 32 降到 16。如果还不行开梯度累积用小 batch 模拟大 batch 效果。另外run_classifier.py里如果有tf.logging输出太多也会占一点但影响不大。4.4 评估指标远低于 81.2%现象自己跑出来准确率只有 70% 出头跟摘要里的 81.2% 差一截。原因常见有三个——语料没对齐训练集和测试集分布差异大、max_seq_length截断丢信息、预训练权重用错用了英文bert-base-uncased而不是中文权重。解决先确认vocab.txt是中文的里面应该有大量汉字。再看测试集是不是和训练集同分布如果训练用微博语料、测试用电商评论掉点正常。最后检查do_lower_case参数中文场景一般设False设True会把某些字符搞乱。4.5 GUI 界面跑不起来现象app_ui.py或gui.ui启动报错提示缺PyQt5或tkinter。原因界面依赖没装或者.ui文件需要pyuic5转成.py才能用。解决pip install PyQt5然后用pyuic5 gui.ui -o gui.py生成 Python 文件。如果app_ui.py里直接import gui确保生成的gui.py在同目录。界面这块不是核心跑不通不影响模型训练可以先放一放。5. 进阶技巧用 extract_features.py 做特征复用与指标验证5.1 抽取 [CLS] 向量做下游任务extract_features.py这个脚本容易被忽略但它其实很有用。BERT 每个输入序列的第一个 token 是[CLS]它的输出向量被设计成聚合整句语义可以直接拿来当句子特征。如果你想在这个情感分析模型基础上做迁移比如换成二分类或者加一个意图识别头不用重新微调整个 BERT抽[CLS]向量接个浅层分类器就行。python extract_features.py \ --input_file./data/test1.txt \ --output_file./output/test_features.jsonl \ --vocab_file./chinese_L-12_H-768_A-12/vocab.txt \ --bert_config_file./chinese_L-12_H-768_A-12/bert_config.json \ --init_checkpoint./output/model.ckpt-XXXX \ --layers-1 \ --max_seq_length128 \ --batch_size8逻辑说明--layers-1表示取最后一层-2取倒数第二层。常见做法是取最后四层拼接或取倒数第二层因为最后一层有时过拟合训练任务。--init_checkpoint指向你微调后的 checkpoint不是原始预训练权重这样抽出来的特征才带情感任务的偏向。输出是 JSONL 格式每行一个样本的向量维度 768。参数怎么改batch_size根据显存调抽特征不反向传播显存占用比训练小可以适当开大。max_seq_length要和训练时一致否则位置编码对不上特征会偏。5.2 用混淆矩阵和分类报告验证 81.2%摘要给的三个指标是聚合值想知道模型到底行不行得拆开看每一类。用sklearn的classification_report能一次看到三类的 precision、recall、f1。from sklearn.metrics import classification_report # preds 是模型预测标签列表labels 是真实标签列表 print(classification_report(labels, preds, target_names[负向, 无情感, 正向], digits4))逻辑说明target_names的顺序要和标签编号对应0 负向、1 无情感、2 正向。digits4保留四位小数方便和摘要里的 76%、78.5% 对比。如果「无情感」的 recall 明显低于另外两类说明中性样本是短板可以考虑对这类样本过采样或者在run_classifier.py的 loss 计算里加类权重。5.3 一个我踩过的坑checkpoint 选错导致指标虚高有次我图省事评估时init_checkpoint指到了训练中途保存的某个 checkpoint而不是最终那个结果准确率比预期高了 3 个点当时还挺高兴后来发现是那个 checkpoint 在验证集上过拟合了换到真正独立的测试集上直接掉到 75%。从那以后我每次评估都强制走一遍流程先确认 checkpoint 的 global step 是最大的那个再确认测试集没有在训练过程中被当验证集用过最后才看指标。这个习惯帮我避开了好几次「假高分」。另外提醒一句run_classifier.py默认会在训练结束后用最优 checkpoint 做一次评估但那个「最优」是按验证集选的如果你验证集和测试集划分有重叠指标会虚高。独立测试集一定要单独留出来别混用。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑