ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Transformers实战指南:从情感分析到语义检索的NLP全流程落地

Transformers实战指南:从情感分析到语义检索的NLP全流程落地 1. 这篇实战定位不聊原理直接落地第二篇终于来了。上一篇我系统过了一遍 Transformers 的安装、Pipeline 基础用法和 Tokenizer 的进出流程算是把门槛迈过去了。这一篇我打算换个节奏所有篇幅都留给“能直接用起来的东西”核心就一个主题——用 Python 和 Transformers 解决真实业务里的自然语言处理场景包括文本分类、命名实体识别、抽取式问答、文本摘要、语义检索这一整串任务。这些都是我在实际项目里反复做过的踩过的坑、总结出的套路全都揉进后面几章尽量让你读完能直接照着抄作业而不是又看了一篇原理综述。这篇内容适合两类人一类是已经跑通 pipeline、但不知道下一步该怎么深入的新手另一类是要在项目里快速落地 NLP 方案的工程师。前者重点看第 3、4 章的玩法后者可以直接跳到第 5、6 章的性能优化和排错清单。文章里的代码基于 transformers 4.x 和 Python 3.8我本地是 Python 3.10、transformers 4.36、torch 2.1、accelerate 0.25往下兼容到 3.8 基本没压力。很多人学 Transformers 喜欢先啃注意力机制、多头自注意力的公式再动手写代码。我的建议正好反过来先让代码跑起来拿到一个真实可用的结果回头再看公式会轻松十倍。因为绝大多数业务场景根本不需要从零实现模型Hugging Face 生态已经把这些做成了“搭积木”式的组件。真正值钱的不是你会默写 Attention而是你对组件边界的判断力——什么时候用现成 pipeline什么时候要自己微调什么时候连 Transformers 都不该选。这篇文章就是奔着这个判断力写的先交代一句如果你还没装环境pip install transformers torch datasets accelerate一条命令跑完就行版本别用 3.xAPI 差异比较大。2. 核心组件拆解Tokenizer、AutoModel 与 pipeline 的底层逻辑2.1 Tokenizer 的分词差异与 Fast 版本Tokenizer 是所有人最容易忽略、但坑最多的组件。同一个文本换一个分词器模型的输入可能完全不同。这里先分清楚两个流派WordPiece 和 BPE。BERT 用的是 WordPiece中文场景下会把句子切成一个一个汉字再判断有没有子词组合比如“人工智能”会被切成“人”、“工”、“智”、“能”四个 token英文场景则会切得更细playing可能变成play##ing。BPE 是 GPT 系列的做法按字节对合并对 OOV 词的处理更友好。你实际用的时候不用纠结算法细节但必须记住一件事模型和分词器是一一绑定的预训练时用什么分词器推理时就必须原样加载不能拿 BERT 的 tokenizer 去喂 RoBERTa 的模型也别自己写一套“更合理”的分词逻辑塞进去。我在项目里见过一个同学为了“提高中文分词准确性”先拿 jieba 切词再喂给 BERT结果效果不升反降就是这个原因——预训练阶段模型看到的都是字符级 token你强行换成词级输入分布整个变了。另外要优先用名称里带Fast的分词器也就是BertTokenizerFast、AutoTokenizer默认返回的快速版本。Fast 版基于 Rust 实现分词速度能快一个量级而且支持offset_mapping做 NER 或实体抽取时需要把 token 的位置映射回原文靠的就是这个能力。慢速版虽然也能用但在批处理和序列标注场景里会让你多写很多补丁代码。2.2 AutoModel 家族怎么选AutoModel系列是 Hugging Face 提供的一层“自动匹配”机制。你传入bert-base-chinese它自动帮你找到对应的 BERT 结构传入roberta-large就自动切到 RoBERTa 结构。但这里有个非常容易踩的坑AutoModel只会加载“基座模型”也就是不带任务头的版本。你要做文本分类得用AutoModelForSequenceClassification要做生成得用AutoModelForCausalLM或AutoModelForSeq2SeqLM做抽取式问答得用AutoModelForQuestionAnswering。用错类型最常见的报错是Some weights of the model checkpoint were not used或者直接抛出架构不匹配的异常。这里我给一个非常实用的判断表任务加载类输出句子分类/情感分析AutoModelForSequenceClassificationlogits命名实体识别AutoModelForTokenClassificationtoken 级 logits抽取式问答AutoModelForQuestionAnsweringstart/end logits摘要/翻译AutoModelForSeq2SeqLM生成序列文本向量/语义相似度AutoModel拿隐藏层做 pooling句向量我个人的习惯是如果只是做特征提取比如算句子向量就用AutoModel加 mean pooling如果是有明确 label 的任务一律用带任务头的AutoModelForXxx不要自己从 hidden state 再接一层 Linear除非你要做非常特殊的输出结构。2.3 pipeline 为什么方便以及它的局限pipeline是 Transformers 里封装最完整的一层 API。它把 tokenizer、模型、后处理全部串好了传文本进去直接出结果。情感分析一行代码就能跑from transformers import pipeline classifier pipeline( sentiment-analysis, modeluer/roberta-base-finetuned-jd-binary-chinese ) result classifier([充电很快做工精致, 屏幕有划痕退了]) print(result)输出是[{label: positive, score: 0.99}, {label: negative, score: 0.97}]这种结构开箱即用。它的内部逻辑实际上是读取模型配置 → 加载权重 → 加载配套 tokenizer → 预处理 → 前向推理 → 后处理解析。这个链路你平时不用关心但建议至少手动跑一遍tokenizer(text, return_tensorspt)再model(**inputs)理解 pipeline 到底替你做了什么。但 pipeline 也有局限。第一它默认把整段文本塞进模型超过 max length 会被截断长文本场景需要自己处理切分第二pipeline 每次调用都会走一次完整的 Python 对象调用链高并发服务里性能不是最优部署时一般要绕过 pipeline 直接上 ONNX 或 TensorRT第三不同任务的 pipeline 后处理逻辑差别很大比如 NER 的aggregation_strategy参数如果不理解结果会让你一脸懵。这些都在后面详细展开。3. 高频任务实操分类、NER、问答、摘要、语义向量3.1 文本分类情感分析的完整链路文本分类是 NLP 里最成熟、最容易落地的任务。我上面给的例子直接用了京东情感分类微调好的模型效果对电商评论场景很好。但真实项目里你更多会遇到“特定业务字段分类”的需求比如工单类型识别、客服咨询意图分类这种就得自己微调第 4 章详细说。这里先讲直接用现成模型的细节。情感分析 pipeline 默认返回的 label 是模型训练时定义的字符串不同模型的 label 含义不一样有0/1数字型的有positive/negative文本型的。你用之前一定要拿几条真实数据先试一遍别把 0 当成负面、1 当成正面我见过一个团队因为这个把结果画反了整个看板上全是错的。实际工程里文本分类还有一个隐藏坑类别不均衡。比如在工单分类里“咨询”类占 90%“投诉”类占 3%模型稍微偷懒一点就会全预测成“咨询”。这时候光看 accuracy 是没用的必须看 F1 尤其是少数类的召回率。后面第 6 章我会专门说评估指标的问题。3.2 命名实体识别聚合策略与边界问题NER 是信息抽取的基础能力做知识图谱、舆情分析、合同信息提取都要用到。以中文为例模型推荐直接用已经微调好的中文 checkpoint我在项目里常用的是基于 BERT 系列微调的版本。加载方式from transformers import pipeline ner pipeline( ner, modelhfl/rbt3, # 这里换成实际可用的微调 NER 模型 aggregation_strategysimple ) text 周杰伦在台北举办演唱会门票由大麦网发售 print(ner(text))一个关键参数是aggregation_strategy。BERT 系模型在 NER 任务上输出的是token 级标签中文里一个实体名“周杰伦”对应三个 tokenlabels 会连续输出B-PER、I-PER、I-PER。如果不加聚合策略你会拿到三个零散的结果每个都只覆盖一个字设置成simple之后pipeline 会自动把连续的同类 token 拼成一个完整的实体并给你实体在原文中的 start/end 位置。聚合策略里还有first、average、max区别在如何合并同一个实体多个 token 的置信度分数。我的建议是默认用simple它同时返回 start 和 end方便后面做文本替换或高亮。但要注意聚合后的实体边界不一定准尤其遇到中文介词或英文缩略词时实际项目里我会再写一层规则校正比如“北京市朝阳区”如果模型只抽到“朝阳区”规则层可以补上前缀“北京市”。3.3 抽取式问答context 管理与过长文本处理抽取式问答适合“给一段资料问一个问题答案就在这段资料里”的场景比如企业知识库、合同条款问答、工单历史检索。它不是让模型自由发挥而是从 context 里挑一个连续片段作为答案所以模型定位的是一段文字的起止位置。典型用法from transformers import pipeline qa_model pipeline( question-answering, modeluer/roberta-base-chinese-extractive-qa ) context 华为技术有限公司于1987年在深圳成立是全球领先的ICT基础设施提供商。 question 华为在哪一年成立 print(qa_model(questionquestion, contextcontext))输出结果包含answer、score、start、end。实战中最需要注意的是score 只是模型对该答案的置信度不是准确率。我见过很多项目拿 score 做硬性阈值但不同模型的分值分布差异极大有的模型正确答案只有 0.3有的模型错误答案能到 0.9。建议先拿人工标注的样本把 score 分布跑出来再定阈值。第二个坑是超长 context。BERT 系模型上限通常是 512 个 token一段几千字的合同根本塞不下。这时可以用 pipeline 的handle_long_impossible_answer配合doc_stride做滑窗或者自己把段落按句号切成多段分别做问答再取 score 最高的答案。后者我实测更可控因为滑窗会把一句话从中间截断影响语义完整性。3.4 文本摘要模型选择与关键参数摘要分两类抽取式从原文里摘重要的句子和生成式模型自己组织语言。Transformers 生态里生成式是主力中文场景我推荐用多语言 T5 系模型比如 mT5 的多语言摘要版本。用法from transformers import pipeline summarizer pipeline( summarization, modelcsebuetnlp/mT5_multilingual_XLSum ) text 这里放一段较长的中文新闻正文 result summarizer( text, max_length150, min_length50, num_beams4, do_sampleFalse ) print(result[0][summary_text])参数里面max_length和min_length控制摘要长度num_beams控制集束搜索宽度。beam 越大效果略好但速度明显变慢业务上 4 到 6 是甜点位。do_sample一定要设成 False否则同样的输入每次输出都不一样这在正式环境里是无法接受的。生成式摘要最大的问题是输入长度限制和幻觉。文本超过模型上限时我会先做句频统计或 TextRank从原文里筛出 top N 句作为“候选压缩文”再喂给生成模型。这个方法我管它叫“抽取式前置 生成式压缩”效果比硬截断好非常多。幻觉问题则要靠业务侧的校验比如摘要里出现原文没出现过的日期、金额数字时标记告警别指望模型自己改掉。3.5 语义向量与语义检索知识库对接的第一步最后一个高频任务不是传统 NLP 分类而是把文本变成向量做语义相似度计算和检索。这个需求最近特别多因为很多人搭 AI 应用都需要“从文档库里召回相关内容”。虽然可以直接用 BERT 的CLS向量但效果一般更推荐用专门的 sentence embedding 模型from sentence_transformers import SentenceTransformer model SentenceTransformer(moka-ai/m3e-base) sentences [如何申请退货, 退款流程怎么走] embs model.encode(sentences, normalize_embeddingsTrue) print(embs.shape)拿到向量后常见做法是存进向量数据库如 Milvus、FAISS查询时把用户问题也编码成向量做余弦相似度 top-k 召回。这里有一个新手很容易犯的错误直接用AutoModel最后一层的 hidden state 当句向量也没归一化效果往往很糟糕。原因是 BERT 这类模型不是按“句子级相似度”目标训练的最后一层向量包含大量词级信息直接平均会稀释语义。专门的 sentence embedding 模型用对比学习训练过方向就是要让语义相近的句子距离更近所以优先用它。如果只能用 BERT也可以自己写 mean pooling 并配合归一化但期望别太高。向量维度和存储成本也要提前算清楚m3e-base输出 768 维100 万条文档大约占 3GB 左右换算好再选数据库。4. 微调实战用 Trainer 训练一个属于自己的模型4.1 数据准备从 CSV 到 Dataset 的完整链路如果说第 3 章是拿来主义那微调就是让你从“用别人的模型”进化到“做自己的模型”。不要被“微调”两个字吓到它的本质就是把预训练模型当做一个已经学会中文语法的“实习生”你给他看一批你业务里的例子让他调整自己的判断标准。数据量不用特别大几千条标注样本就能有明显效果这点比从零训练友好太多了。先看数据准备的链路。假设你有一个reviews.csv两列text是评论内容label是分类标签0 负面 / 1 正面。标准的加载写法import pandas as pd from datasets import Dataset, DatasetDict from sklearn.model_selection import train_test_split df pd.read_csv(reviews.csv) train_df, eval_df train_test_split(df, test_size0.1, random_state42) train_ds Dataset.from_pandas(train_df[[text, label]]) eval_ds Dataset.from_pandas(eval_df[[text, label]]) dataset DatasetDict({train: train_ds, eval: eval_ds}) from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(hfl/chinese-roberta-wwm-ext) def tokenize(batch): return tokenizer( batch[text], truncationTrue, paddingmax_length, max_length128 ) encoded dataset.map(tokenize, batchedTrue, remove_columns[text]) encoded encoded.rename_column(label, labels) print(encoded[train].features)这里几个细节remove_columns[text]是把原始文本从 Dataset 里删掉否则后面 Trainer 会把 text 当输入特征报Expected input batch_size to match target之类的错rename_column(label, labels)是因为 Transformers 约定监督标签字段名是labelspaddingmax_length会统一把每条样本都填到 128虽然占资源但省心后面我会说怎么优化。中文场景我常用hfl/chinese-roberta-wwm-ext它是哈工大讯飞联合发布的 RoBERTa 中文版本加了全词掩码优化在下游中文任务上普遍比bert-base-chinese好一截。4.2 TrainingArguments 关键参数逐个说微调最核心的对象是TrainingArguments它几乎控制训练的一切。新手最容易瞎调的是 learning rate业界对 BERT 家族有个默认共识全参数微调用 2e-5 到 5e-5太大直接发散太小收敛很慢。我一般是 2e-5 起步。先看一个完整的配置from transformers import TrainingArguments, Trainer training_args TrainingArguments( output_dir./checkpoints, learning_rate2e-5, per_device_train_batch_size16, per_device_eval_batch_size32, num_train_epochs3, weight_decay0.01, warmup_ratio0.1, fp16True, eval_strategyepoch, # 老版本叫 evaluation_strategy save_strategyepoch, save_total_limit2, logging_steps50, load_best_model_at_endTrue, metric_for_best_modeleval_f1, report_tonone, )一个个说关键点。per_device_train_batch_size受显存限制BERT base 在 128 长度下 16 可以跑在 12GB 卡上显存不够就先降到 8 或 4不要硬撑。gradient_accumulation_steps是另一种降显存方案比如 batch_size4 加梯度累积 4 步效果等价于 batch 16但速度会慢所以我一般优先调 batch。fp16True在半精度下训练显存减半、速度翻倍A100、V100、RTX 系列都支持但要留意 loss 变成nan如果出现就把 fp16 关掉。warmup_ratio0.1表示前 10% 的 step 里学习率从 0 线性升到设定值能有效避免早期震荡。load_best_model_at_endTrue配合metric_for_best_modeleval_f1是保住最好成绩的关键否则它默认用 eval_loss 选模型很多场景下 loss 最低不代表业务指标最好。注意一个版本坑新版 transformers 把evaluation_strategy改成了eval_strategy旧参数写了会报 warning不影响运行但升级后建议直接写新的。4.3 Trainer 训练、评估、保存与加载Trainer把训练循环、梯度更新、日志、评估都包好了你不需要手写 for loop。但要注意它默认只输出 loss不输出 F1 等指标所以评估指标要自己写个函数传进去from sklearn.metrics import accuracy_score, f1_score import numpy as np def compute_metrics(eval_pred): logits, labels eval_pred preds np.argmax(logits, axis-1) return { accuracy: accuracy_score(labels, preds), f1: f1_score(labels, preds, averagemacro) } trainer Trainer( modelmodel, argstraining_args, train_datasetencoded[train], eval_datasetencoded[eval], tokenizertokenizer, compute_metricscompute_metrics, ) trainer.train() trainer.evaluate() trainer.save_model(./my_model) tokenizer.save_pretrained(./my_model)训练完模型文件model.safetensors、config.json和分词器文件会保存到./my_model。加载时直接走 pipeline 最省事from transformers import pipeline clf pipeline(text-classification, model./my_model, tokenizer./my_model) res clf(这个耳机戴久了耳朵疼) print(res)这里有个隐藏问题如果你的类别标签是从 0 到 1 的整数不配置id2label的话pipeline 输出的 label 会是 “LABEL_0” 这种占位符。训练前建议在模型里配好model.config.id2label {0: 负面, 1: 正面} model.config.label2id {负面: 0, 正面: 1}另外评估集千万别偷懒用训练集切出来的一定要留出独立验证集。我在项目里吃过亏模型在验证集上 F1 到 0.9上线后一塌糊涂后来发现因为数据采集时间窗口重叠训练集和验证集里有大量相似文本。数据泄漏是微调项目最常见的隐形杀手比调参重要得多。5. 推理性能优化与轻量部署5.1 从 PyTorch 到 ONNX Runtime十分钟上手先泼一盆冷水直接用 pipeline 做生产服务除非并发量很低否则性能都不够看。PyTorch 动态图灵活但推理开销大每条请求都要重新走一遍 Python 调度。我常用的方案是导出 ONNX 格式用 ONNX Runtime 做推理在 CPU 上通常能快 1.5 到 3 倍显存占用也更低。导出可以用optimum库一条命令搞定pip install optimum[onnxruntime] optimum-cli export onnx --model hfl/chinese-roberta-wwm-ext --task text-classification ./onnx_model导出后在 Python 里直接跑from optimum.onnxruntime import ORTModelForSequenceClassification from transformers import AutoTokenizer ort_model ORTModelForSequenceClassification.from_pretrained(./onnx_model) tokenizer AutoTokenizer.from_pretrained(hfl/chinese-roberta-wwm-ext) inputs tokenizer(这个产品非常好用, return_tensorspt) outputs ort_model(**inputs) print(outputs.logits)导出过程中最常遇到的坑是算子不兼容。老版本 transformer 结构某些算子 ONNX Runtime 支持不好报错就升级 optimum 和 onnxruntime或者尝试把opset版本调低。另一个坑是动态轴的问题ONNX 模型默认输入长度固定你训练时用max_length128推理时来了个 200 长度的文本就会报错。导出时加--dynamic-batchsize之类的参数去开启动态轴或者在推理侧统一做截断填充。我的习惯是服务入口限制max_length128超长的文本先做滑窗从源头避免动态轴复杂度。5.2 批量推理、半精度与动态截断ONNX 之外还有几个不用换格式就能提升性能的手段。第一个是批量推理。pipeline 本身就支持batch_size参数classifier pipeline(sentiment-analysis, modelmodel_path, batch_size32) results classifier(long_text_list)GPU 推理时批量 32 相比单条逐条调用吞吐量可以提升 5 到 10 倍代价是显存占用上升。批量推理有个细节不同长度的文本会统一 pad 到 batch 内最长短文本占比大的时候浪费很多计算。解决办法是给 pipeline 加paddingTrue而不是paddingmax_length让动态填充只对齐到 batch 内最长 token。第二个是半精度推理。PyTorch 侧只需要import torch model.half().to(cuda)配合torch.inference_mode()前向速度能再快一截。注意半精度在 CPU 上不支持只在 GPU 上用。第三个是torch.compilePyTorch 2.x 自带一行model torch.compile(model)在某些 GPU 上能带来可观收益但首次调用有编译开销服务场景要预热后再接流量。5.3 FastAPI 部署那些没人提醒你的细节终于说到部署。用 FastAPI 包一个推理服务是最常见的做法网上模板很多但有几个细节几乎没人提。第一模型必须在模块加载时初始化一次不能放在每个请求的函数里面加载否则一次请求就要读一遍权重卡死。正确做法是模块级变量from fastapi import FastAPI from transformers import pipeline app FastAPI() classifier pipeline(text-classification, model./my_model) app.post(/predict) def predict(text: str): return classifier(text)第二FastAPI 的异步接口和 CPU 推理是互斥的。如果你定义async def predict里面跑同步的 pipeline整个事件循环会被阻塞并发一高请求全部排队。稳妥做法是用普通的def定义端点FastAPI 会自动把它丢到线程池执行或者你手动run_in_executor。GPU 推理还要小心多线程同时往 GPU 灌 batch显存会瞬间爆掉。我在生产里一般加一个信号量限制并发数比如最多 4 个推理线程同时在跑。第三响应结构要稳定。不要直接把 pipeline 的原始 list 返回建议封装一层带code、data、message的统一响应体label 和 score 分开方便上游解析。还有正式环境记得关掉模型内部的 dropout推理时模型默认在 eval 模式但如果你手动调用model()而不是 pipeline一定要model.eval()并包在with torch.inference_mode()里否则结果每次都不一样。6. 高频踩坑实录模型下载、显存、中文编码、随机性6.1 模型下载失败与离线模型加载模型下载是使用 Transformers 时第一个大坑。Hugging Face 下载大模型时经常失败原因通常是网络不稳定或连接超时。我的经验是先设置镜像环境变量下载速度会有明显提升export HF_ENDPOINThttps://hf-mirror.com然后再正常from_pretrained下载。这个变量不用改代码只影响下载源。如果运行环境完全隔离、不能联网就提前在能上网的机器上把模型库下好整个目录拷过去然后本地加载from transformers import AutoModelForSequenceClassification, AutoTokenizer model AutoModelForSequenceClassification.from_pretrained(/data/models/bert-chinese) tokenizer AutoTokenizer.from_pretrained(/data/models/bert-chinese)离线环境还要记得设置HF_HUB_OFFLINE1否则它会反复尝试联网检查更新每次都等很久。模型文件看起来很多实际核心就是model.safetensors、config.json、vocab.txt或tokenizer.json这几个判断是否拷贝完整就盯着这几个文件看。6.2 显存不足与 OOM 的排查思路OOM 是微调和推理的第一杀手报错一般是CUDA out of memory。排查思路从大到小先看还有什么程序占着显存nvidia-smi查一眼很多时候是上一个崩掉的进程没释放kill -9掉就行。再考虑降 batch size从 32 降到 16、8这是最直接的。还要看序列长度BERT 把 512 token 全 pad 满非常费显存实际业务里大部分文本平均不到 100 token把max_length从 512 降到 128显存直接省一半以上。如果你在训练时 OOM 但不想降 batch用gradient_accumulation_steps。显存是训练时一次前向和反向需要的量单次 batch 小就不会爆梯度累积再合并更新效果等价大 batch。还有一个我常忽略的原因同时加载了多个模型。比如你在同一个脚本里加载了一个 NER 模型、一个分类模型、一个向量模型每个都占几个 GB加一起直接爆。上线前先规划好确实需要多个模型就做模型排队加载或者分进程部署。6.3 中文任务的编码与分词细节中文任务看似简单坑全在细节里。第一个是空格问题。中文文本里如果混着英文单词分词器会把空格也当成 token 的一部分导致边界错乱。数据清洗阶段要把多余空格、全角半角统一处理好。第二个是繁体简体不统一的问题如果业务数据有繁体建议先转简体再做训练否则模型很容易把同一意思的两个写法当成不同类别。第三个是标点符号BERT 分词器对中文句号、逗号有专门处理但如果你在预处理阶段把所有标点删掉反而会损害语义因为标点也是句法信息的一部分。还有一个 NER 场景特别容易踩的坑用 FastTokenizer 时拿input_ids硬算实体位置没有用offset_mapping。中文一个 token 对应一个汉字还好英文一个词可能被切成两三个 token你自己算的位置和原文对不上。正确做法是让 tokenizer 返回offset_mappingTrue它会给出每个 token 对应原文的起止位置按这个去还原实体边界。6.4 结果不稳定与评估指标失真推理结果不稳定第一个要查的是模型是否在 eval 模式下model.eval()没写Dropout 还在生效结果每次都不一样。第二个是采样参数生成任务里do_sampleTrue且temperature过高时输出会有明显随机性生产环境务必do_sampleFalse。第三个是随机种子训练之前一定transformers.set_seed(42)同时把 numpy 和 torch 的 seed 也设上否则每次训练出来的最佳权重都可能不一样实验没法对比。评估指标失真是另一个高频问题。前面提过类别不均衡这里再展开如果你的业务是“投诉”只占 3%准确率可能到 97% 但模型一个投诉都抓不住。必须同时看 precision、recall、F1尤其是 macro F1 而不是 micro F1。micro F1 会被大类别主导macro F1 对每个类一视同仁更符合这类场景。另外评估集和训练集的时间窗口要分开更重要的是评估集必须覆盖业务上线后的真实分布比如线上会出现表情符号、中英混合、营销文案训练集里全是没有表情的干净文本评估分数再高也没有参考价值。最后补一个训练时容易忽略的小细节数据里如果存在空文本、纯标点文本tokenizer 会返回全 0 的 attention_mask模型输出没有意义。清洗时先df df[df[text].str.strip().str.len() 5]把过短文本过滤掉这类样本对训练是纯噪声。7. 最后聊几句我的几条使用习惯写到这里第 6 章的坑基本覆盖了我这几年用 Transformers 踩过的绝大多数雷。最后分享几条我个人的使用习惯不一定对所有项目适用但至少能帮你少走弯路。第一条任何时候拿到一个新数据集先写一个“探索脚本”把数据量、类别分布、文本长度分布、空缺比例打印出来再决定模型和参数。我见过太多人急着训练数据里 30% 是空值都没发现训完才发现是垃圾进垃圾出。第二条微调完的模型一定要做“对抗测试”拿一些训练集里没见过的、跨领域的样本测一下比如训练集都是电商评论上线后来了天气话题的文本你要提前知道它会给出什么结果。第三条模型文件、分词器、配置、训练参数全部跟着代码走版本管理不要只存个权重文件不然三个月后你自己都复现不了当时的实验结果。Transformers 这个生态发展很快今天推荐的具体模型改天可能就有更好的替代品。但这一篇里涉及的工作流、参数理解和排查思路短期内不会被淘汰——因为这些都是自然语言处理落地时最稳定的那部分逻辑。希望这篇能帮你在自己的项目里少一点“为什么跑不通”的夜晚多一点“这个效果终于能用了”的时刻。
返回列表