1. 先搞清楚微调后评估与推理到底要做什么很多人跑通了情感分析微调的代码看到训练损失下降就以为大功告成。但真正决定模型能不能用的是训练结束后那几步评估和推理。评估不是简单地跑一下测试集看个准确率推理也不是把模型predict一下就算完。这步没做好前面几十轮的训练可能白费。微调后的评估核心是回答两个问题第一模型在没见过的数据上表现到底稳不稳定第二它有没有“学偏”——比如把所有文本都预测成积极情感虽然准确率可能不低但完全失去了判断能力。而推理则是把训练好的模型用起来这里面的坑更多怎么加载模型和分词器、如何处理单条和批量文本、怎么解读输出结果以及怎么把这一套流程封装成可用的服务。所以这部分内容适合已经用 Hugging Face Transformers 库完成了模型微调正准备验收成果和投入使用的开发者。最关键的价值在于帮你建立一套从训练到落地的完整检查清单避免因为评估不充分或推理流程有缺陷导致模型在实际场景中掉链子。2. 评估别只看准确率先看评估指标选对了没有模型训练完成后第一件事不是马上部署而是进行系统性的评估。很多新手会直接调用trainer.evaluate()然后盯着输出的eval_accuracy看这远远不够。2.1 选择与任务匹配的评估指标对于情感分析这种分类任务准确率Accuracy是一个直观的指标但它有局限性。特别是当你的数据集中正负样本比例不平衡时准确率可能会误导你。例如一个数据集中90%是正面评价模型即使把所有样本都预测为正面也能获得90%的准确率但这模型是无效的。因此至少需要结合以下指标综合判断精确率Precision在所有被模型预测为“正面”的样本中真正是“正面”的比例。这衡量了模型预测的“准度”。召回率Recall在所有真实的“正面”样本中被模型正确预测出来的比例。这衡量了模型发现正样本的“广度”。F1分数F1-Score精确率和召回率的调和平均数在两者之间寻求一个平衡。这是非常常用的综合指标。分类报告Classification ReportScikit-learn 提供的classification_report函数能一次性输出每个类别的精确率、召回率、F1分数和支持度样本数一目了然。在 Hugging Face 的TrainerAPI 中可以通过定义compute_metrics函数来定制评估过程。下面是一个针对二分类情感分析的示例from sklearn.metrics import accuracy_score, precision_recall_fscore_support import numpy as np def compute_metrics(eval_pred): 计算评估指标的函数 predictions, labels eval_pred # predictions 是模型输出的 logits未归一化的分数 # 对于二分类我们取 logits 中值更大的那个维度作为预测类别 predictions np.argmax(predictions, axis1) # 计算准确率 accuracy accuracy_score(labels, predictions) # 计算精确率、召回率、F1分数针对‘1’这个类别假设1代表正面 precision, recall, f1, _ precision_recall_fscore_support(labels, predictions, averagebinary, pos_label1) return { accuracy: accuracy, precision: precision, recall: recall, f1: f1, } # 在初始化 Trainer 时传入 from transformers import Trainer trainer Trainer( modelmodel, argstraining_args, train_datasettrain_dataset, eval_dataseteval_dataset, compute_metricscompute_metrics, # 传入自定义评估函数 # ... 其他参数 )训练完成后调用trainer.evaluate()返回的字典里就会包含你定义的这些指标。2.2 在独立测试集上进行最终评估非常重要的一点是不要用验证集eval_dataset作为最终的性能报告依据。验证集在训练过程中用于调整超参数和监控模型是否过拟合模型已经“见过”它很多次了。你需要一个完全独立的、模型从未接触过的测试集。通常的做法是在数据加载时就划分出三份训练集、验证集、测试集。训练时只用训练集和验证集。训练结束后用训练好的模型在这个“干净”的测试集上跑一次推理并用上面的compute_metrics函数计算指标。这才是模型真实能力的反映。# 假设 test_dataset 是你的测试集 test_results trainer.predict(test_dataset) print(test_results.metrics) # 输出在测试集上的各项指标2.3 进行错误分析看整体指标只是第一步更重要的是分析模型在哪里犯了错。我常用的方法是收集错例遍历测试集把模型预测错误的样本包括文本、真实标签、预测标签、预测概率保存下来存成 CSV 或 JSON 文件。人工检查抽样查看这些错例。是句子太长包含反讽有生僻词还是标注本身就有问题归纳模式尝试将错误分类比如“包含否定词的正面评价”、“强烈情绪但无关好坏的陈述”等。这能帮你理解模型的弱点并为后续的数据清洗、增强或模型调整提供方向。这个过程没有自动化的捷径但花一两个小时做一次对理解你的模型和任务有质的提升。3. 推理从单条测试到批量处理与服务化评估通过后就可以用模型进行推理了。推理流程有几个关键环节每个环节都有需要注意的细节。3.1 加载微调后的模型与分词器训练时我们通常使用AutoModelForSequenceClassification和AutoTokenizer的from_pretrained方法加载预训练模型。微调后模型权重保存在本地目录由TrainingArguments中的output_dir指定。推理时需要从这个目录加载。from transformers import AutoModelForSequenceClassification, AutoTokenizer # 指定你保存模型的路径 model_path ./my_finetuned_sentiment_model # 加载模型和分词器 model AutoModelForSequenceClassification.from_pretrained(model_path) tokenizer AutoTokenizer.from_pretrained(model_path) # 将模型设置为评估模式关闭Dropout等训练特有的层 model.eval()注意确保加载模型的环境与训练环境具有兼容的库版本特别是 Transformers 和 PyTorch/TensorFlow否则可能会报错。3.2 单条文本推理流程处理单条文本时步骤是标准化的分词 - 转换为张量 - 模型预测 - 解析结果。import torch def predict_sentiment(text): 对单条文本进行情感预测 # 1. 分词 inputs tokenizer(text, return_tensorspt, truncationTrue, paddingTrue) # return_tensors“pt” 返回PyTorch张量 # truncationTrue 处理超长文本 # paddingTrue 在这里单条文本情况下不是必须的但保持习惯 # 2. 模型推理不计算梯度 with torch.no_grad(): outputs model(**inputs) # 3. 获取预测结果 # outputs.logits 形状为 [1, num_labels] logits outputs.logits # 使用 softmax 将 logits 转换为概率 probabilities torch.nn.functional.softmax(logits, dim-1) # 获取预测的类别ID predicted_class_id logits.argmax().item() # 获取对应类别的概率 confidence probabilities[0][predicted_class_id].item() # 4. 将ID映射回标签假设0:负面1:正面 id2label {0: NEGATIVE, 1: POSITIVE} predicted_label id2label[predicted_class_id] return { text: text, predicted_label: predicted_label, confidence: confidence, probabilities: probabilities.tolist()[0] # 所有类别的概率列表 } # 测试 result predict_sentiment(This movie is absolutely fantastic, I loved every minute of it!) print(result) # 输出可能类似{text: ..., predicted_label: POSITIVE, confidence: 0.998, probabilities: [0.002, 0.998]}关键点with torch.no_grad():非常重要它能禁用梯度计算大幅减少内存占用并加速推理。输出logits后通常用argmax取最大值的索引作为预测类别。对于需要概率的场景比如看置信度再用softmax转换。记得维护一个id2label映射字典将模型输出的数字ID转换回人类可读的标签。这个映射关系在训练时由label2id定义并保存在模型配置中可以通过model.config.id2label获取。3.3 批量文本推理与性能优化在实际应用中我们更常处理批量文本。批量处理能充分利用 GPU 的并行计算能力效率远高于循环处理单条。def predict_batch_sentiments(texts, batch_size8): 批量预测情感 all_results [] for i in range(0, len(texts), batch_size): batch_texts texts[i:ibatch_size] # 1. 批量分词 inputs tokenizer(batch_texts, return_tensorspt, truncationTrue, paddingTrue, max_length128) # 2. 批量推理 with torch.no_grad(): outputs model(**inputs) # 3. 批量解析结果 probabilities torch.nn.functional.softmax(outputs.logits, dim-1) predicted_class_ids outputs.logits.argmax(dim-1) confidences probabilities[torch.arange(len(batch_texts)), predicted_class_ids] # 4. 映射标签并组装结果 id2label model.config.id2_label # 从模型配置中读取 for j in range(len(batch_texts)): all_results.append({ text: batch_texts[j], predicted_label: id2label[predicted_class_ids[j].item()], confidence: confidences[j].item() }) return all_results # 示例 batch_texts [ The product is okay, nothing special., Terrible experience, would not recommend., Excellent service and fast delivery! ] results predict_batch_sentiments(batch_texts) for r in results: print(r)性能优化建议调整batch_size这是最重要的参数。增大batch_size能提升 GPU 利用率但受限于 GPU 显存。需要通过实验找到在显存不溢出的前提下吞吐量最高的batch_size。可以从 4、8、16、32 开始尝试。使用pipelineHugging Face 提供了更高级的pipelineAPI它封装了分词、推理、后处理的全过程使用非常简单并且内部也做了批量优化。from transformers import pipeline classifier pipeline(text-classification, modelmodel_path, tokenizermodel_path, device0) # device0 指定使用GPU results classifier(batch_texts, batch_size16) # 直接批量处理 print(results)对于快速验证和简单应用pipeline是首选。如果需要更精细的控制如获取 logits、自定义后处理则使用上面手动分词和推理的方式。4. 模型保存、部署与持续监控评估和单次推理完成后如果要长期使用就需要考虑模型部署和工程化的问题。4.1 模型保存格式与优化Trainer保存的模型目录包含了很多文件pytorch_model.bin,config.json,tokenizer.json等。为了生产部署可以考虑进一步优化保存为 Safetensors 格式这是一种更安全、加载更快的模型权重格式。可以在保存时指定model.save_pretrained(“./my_model”, safe_serializationTrue)使用 ONNX 或 TorchScript 导出如果你需要在特定的推理引擎如 ONNX Runtime或移动端上运行可以考虑将模型导出为 ONNX 或 TorchScript 格式这通常能获得更好的推理性能。但这会引入额外的复杂性需要根据部署环境决定。4.2 构建简单的推理服务一个最基础的部署方式是使用 Flask 或 FastAPI 将模型包装成 HTTP API 服务。# 示例使用 FastAPI from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List import torch from transformers import AutoModelForSequenceClassification, AutoTokenizer, pipeline app FastAPI() # 启动时加载模型 model_path ./my_finetuned_sentiment_model model AutoModelForSequenceClassification.from_pretrained(model_path) tokenizer AutoTokenizer.from_pretrained(model_path) classifier pipeline(text-classification, modelmodel, tokenizertokenizer, device0) class TextRequest(BaseModel): text: str class BatchTextRequest(BaseModel): texts: List[str] app.post(/predict) async def predict_single(request: TextRequest): try: result classifier(request.text)[0] return {label: result[label], score: result[score]} except Exception as e: raise HTTPException(status_code500, detailstr(e)) app.post(/predict_batch) async def predict_batch(request: BatchTextRequest): try: results classifier(request.texts, batch_size16) return {predictions: results} except Exception as e: raise HTTPException(status_code500, detailstr(e)) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)部署注意事项资源管理API 服务中模型加载到 GPU 上是一次性的。要处理好 GPU 内存避免因并发请求过多导致内存溢出OOM。可以通过限制batch_size和实现请求队列来缓解。超时与重试在客户端调用 API 时要设置合理的超时时间并考虑网络错误的重试机制。日志与监控记录每一次预测请求和结果注意脱敏并监控服务的响应时间、成功率和 GPU 使用率。4.3 持续监控与模型迭代模型上线不是终点。你需要持续监控线上表现模型的预测分布是否与测试集时期望的分布一致有没有出现大量低置信度的预测数据漂移用户新产生的文本数据其语言风格、用词习惯是否与训练数据发生了显著变化这会导致模型性能下降。反馈闭环能否收集用户对预测结果的反馈如“这条评论的预测是否正确”将这些反馈作为新的标注数据用于后续的模型迭代和再训练。建立一个简单的监控面板定期如每周查看模型预测结果的统计信息并与基线进行比较是维持模型长期有效性的关键。5. 常见问题与排查清单在实际操作中你可能会遇到以下问题。这里提供一个排查顺序问题1评估指标异常如准确率接近100%或50%检查首先确认你的compute_metrics函数是否正确。打印出eval_pred中的predictions和labels的前几条看形状和值是否合理。检查确认验证集/测试集的标签是否正确加载。是不是不小心把训练集当测试集用了检查对于二分类检查precision_recall_fscore_support函数的average和pos_label参数设置是否正确。问题2推理速度非常慢检查是否在推理循环中忘记了with torch.no_grad():检查batch_size是否设置得过小如1没有充分利用 GPU。检查文本是否非常长导致max_length设置得很大计算量激增。可以尝试统计文本长度分布设置一个合理的截断长度。检查模型是否在 CPU 上运行确保模型和输入张量都在 GPU 上 (model.to(“cuda”),inputs {k: v.to(“cuda”) for k, v in inputs.items()})。问题3加载模型时报错如“权重不匹配”或“配置错误”检查加载模型时指定的model_path是否正确目录下是否包含pytorch_model.bin和config.json。检查当前环境的 Transformers 库版本是否与训练时一致。版本差异可能导致架构解析错误。考虑使用requirements.txt或环境镜像固定版本。检查是否在训练时修改了模型类别数num_labels但加载时使用了默认的预训练模型确保使用AutoModelForSequenceClassification.from_pretrained加载你自己的微调模型。问题4批量推理时 GPU 内存溢出OOM降低batch_size这是最直接有效的方法。启用梯度检查点在模型加载时设置model.gradient_checkpointing_enable()这会用计算时间换内存空间但可能影响推理速度。使用更小的模型如果性能允许考虑微调一个参数量更小的模型如DistilBERT。使用动态填充确保tokenizer的paddingTrue是启用的这样每个 batch 内只填充到该 batch 中最长序列的长度而不是全局最大长度。问题5Pipeline 预测结果与手动推理不一致检查pipeline和手动推理时tokenizer的参数是否完全一致特别是truncation,padding,max_length。检查pipeline默认可能已经应用了softmax而手动推理时你可能需要自己应用。对比两者的原始logits输出。我个人的经验是微调后的评估与推理其稳定性比追求极致的指标更重要。先确保整个流程评估 - 单条推理 - 批量推理 - 服务化能在你的开发环境里稳定、可重复地跑通。然后再去优化batch_size、尝试模型量化、或者集成到更复杂的系统中。很多项目卡住不是因为模型不够好而是因为忽略了这些工程细节导致结果不可靠或性能不达标。把这一套流程理顺你的模型才算真正从实验阶段走到了可用阶段。