首页 / 资讯中心 / 文章详情

Python调用预训练模型:Transformers实战指南

Python调用预训练模型:Transformers实战指南 ★ FEATURED ARTICLE
最近不少人问我同一个问题想用大模型做点实际的东西到底该从哪入手。我的回答一直很明确——先别急着训练先学会用 Python 把现成的预训练模型正确调起来。这不是偷懒而是务实。如今的 Transformers 生态已经非常成熟Hugging Face 上几十万个模型覆盖了绝大多数常见任务文本分类、情感分析、阅读理解、摘要生成、对话问答几乎都能靠现成的 Checkpoint 跑出可用的基线。这篇博文就是写给那些刚接触 Transformers、想知道怎么把模型跑起来的读者的我会把环境准备、Pipeline 快速上手、底层调用链路、中文模型选型、显存估算以及实测中常见的坑都过一遍。全程用我自己的实战经验说话保证每一段都能直接照着操作。1. 先搞清楚一件事预训练大模型对普通开发者意味着什么1.1 Transformers 到底帮你省了什么在 Transformers 库出现之前做 NLP 任务是一件相当痛苦的事。你要先下载预训练权重自己写加载逻辑自己处理词表自己实现分词器还要手动把输入转换成模型能理解的张量格式。每个模型的结构还不一样换一个模型几乎等于重写一套代码。Transformers 把这些全部统一了不管是 BERT、RoBERTa、GPT 还是 LLaMA在它眼里都是同一种调用方式from_pretrained加载forward推理底层细节全部封装好。你不需要关心 checkpoint 文件是分片存储还是单个文件不需要关心权重是 PyTorch 格式还是 TensorFlow 格式库会自动帮你做转换。我在实际项目中感受最深的是模型即插即用这一点。比如我今天想用情感分析模型明天想用命名实体识别模型代码结构几乎完全一样只是换一个模型名称和任务头。过去这种切换需要熟悉底层实现现在只需要几十行代码。对于业务开发来说这意味着我们可以把大模型当作一个服务化组件嵌入系统而不是每次都要当研究课题去啃。1.2 该不该自己训练我的判断标准很多人一上来就想微调甚至预训练自己的模型我觉得这是最大的误区。做工程不是发论文我们追求的是在尽可能短的时间内交付可用的效果。我的判断标准很简单如果现有预训练模型在你这个领域的效果已经可以接受直接调用别折腾如果效果差一点先尝试换更大的模型、换更好的中文模型、加提示词而不是立刻微调如果确实需要微调先在小规模数据上验证收益收益不明显就放弃预训练自己的模型这件事除非你有海量数据和 GPU 资源不然基本不现实我给团队定的规矩是能调用就不训练能微调就不预训练。这样做的好处是迭代速度快风险低出了问题还能快速回退到原始 checkpoint。2. 环境准备Python、PyTorch 和 Transformers 的版本搭配2.1 最稳妥的安装组合如果你是从零开始搭环境我建议你按这个组合来Python 3.10 或 3.11PyTorch 2.xTransformers 最新稳定版。为什么推荐 3.10 而不是最新的 3.13因为很多第三方库对最新 Python 版本的适配有滞后而 3.10 是生态兼容性最好的版本。我在多个项目中遇到过 Python 3.13 下一些编译型依赖装不上的情况最后都得退回 3.10 解决。安装步骤我用虚拟环境隔离避免污染系统 Pythonpython -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install torch --index-url https://download.pytorch.org/whl/cu118 pip install transformers这里面的关键在于 PyTorch 和 CUDA 的匹配。cu118表示 CUDA 11.8如果你要用 GPU 推理需要先看自己的显卡驱动支持哪个 CUDA 版本用nvidia-smi可以查到。如果你没有独立显卡或者暂时不打算用 GPU直接装 CPU 版本的 PyTorch 就行pip install torch这个默认会装 CPU 版本Windows 上直接可用。2.2 为什么我坚持推荐先装 PyTorch 再装 Transformers因为 Transformers 的依赖项里有torch作为可选依赖如果你不先装它可能会自动帮你装一个 CPU 版本。但如果你已经有 GPU 却装到了 CPU 版后面所有推理都会在 CPU 上跑慢到你怀疑人生。我自己第一次装环境就踩过这个坑模型加载后跑一次推理花了 40 秒还以为是模型太大结果是 torch 装成了 CPU 版。装完以后你可以跑一下这个命令确认版本python -c import torch; print(torch.__version__, torch.cuda.is_available())如果输出显示True说明 CUDA 可用。如果你的环境网络不太好可以加上清华源加速pip install transformers -i https://pypi.tuna.tsinghua.edu.cn/simple2.3 一个容易忽略的点模型下载目录Transformers 默认会把模型缓存在~/.cache/huggingface目录下。如果你在服务器上跑磁盘空间不够模型文件动辄几个 GB很容易把目录撑爆。我建议在.bashrc或.zshrc中设置export HF_HOME/your/disk/large/path/huggingface这个路径可以指向一个容量大的数据盘避免模型下载到系统盘导致磁盘满了。3. Pipeline三行代码跑通第一个大模型3.1 pipeline 到底替你做了什么pipeline是 Transformers 库提供的最上层 API。很多人觉得它太黑盒了不适合入门学习。但我的看法正好相反——它是新手理解大模型能干什么最快的方式。因为它在内部帮你处理了分词、张量转换、模型推理、结果解码这一整套流程你只需要指定任务类型和模型名称就能立刻得到结果。来看一个最直观的例子中文情感分类from transformers import pipeline classifier pipeline( sentiment-analysis, modeluer/roberta-base-finetuned-jd-binary-chinese ) result classifier(这家酒店服务很好房间也很干净) print(result)输出结果类似[{label: positive, score: 0.99}]就这么简单。你在三行代码里完成了从模型加载到推理的全部流程。第一次跑的时候会下载模型权重几百 MB 到几个 GB 不等之后就快了。3.2 pipeline 支持的任务类型pipeline支持的任务类型比你想象的多下面是我常用的几个任务名称典型应用示例模型sentiment-analysis情感分类uer/roberta-base-finetuned-jd-binary-chinesetext-classification多标签文本分类IDEA-CCNL/Erlangshen-Roberta-110M-Sentimenttoken-classification命名实体识别uer/roberta-base-wwm-chinese-clue-nerquestion-answering阅读理解hfl/chinese-macbert-basetext-generation文本生成Qwen/Qwen2.5-0.5B-Instructsummarization摘要生成facebook/bart-large-cnnzero-shot-classification零样本分类facebook/bart-large-mnli你只需要把pipeline的第一个参数换成对应的任务名再指定一个合适的模型就能跑通。我记得第一次用zero-shot-classification的时候很震撼——不需要任何训练数据直接把候选标签传进去模型就能帮你分类。对于快速验证业务场景非常有用。3.3 为什么说长期只靠 pipeline 不够Pipeline 适合快速验证但不适合深度定制。它的封装太完整了你无法控制中间的细节比如分词策略、padding 方式、输出解码逻辑。而且pipeline 每次调用都要重新走一遍预处理如果你需要批量推理几百条数据性能会明显下降。我一般在两种场景下才用 pipeline一是写 Demo 给团队看效果二是做简单的线上服务快速验证。真正上线的时候我通常用底层 API 手动控制整个流程。4. 深入调用链路AutoTokenizer 与 AutoModel 背后发生了什么4.1 为什么要理解 tokenizer绕不过去的坎如果你决定走上线这条路必须理解AutoTokenizer和AutoModel的配合关系。我之前带过一个新人他用 pipeline 跑了几天觉得一切都很简单结果一让他处理长文本就出问题——模型的输入超过了最大长度限制报错一片。这就是因为不理解 tokenizer 的工作原理。Tokenizer 做的事情是把文本切成模型能理解的 token 序列再映射成数字 ID。比如人工智能这四个字可能被切成人工和智能两个 token也可能切成人工智能四个 token取决于词表。同一个词不同的模型有不同的切法。所以你加载模型的时候必须配套加载同一个模型的 tokenizer不能混用。这里有三个最重要的参数from transformers import AutoTokenizer, AutoModelForSequenceClassification import torch model_name uer/roberta-base-finetuned-jd-binary-chinese tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForSequenceClassification.from_pretrained(model_name) texts [ 这家酒店服务很好房间也很干净, 物流太慢了等了一个星期才到 ] inputs tokenizer( texts, paddingTrue, truncationTrue, max_length128, return_tensorspt ) outputs model(**inputs) logits outputs.logits probs torch.softmax(logits, dim-1) print(probs)paddingTrue会把同一批次中较短的序列补齐到与最长序列一致这样它们才能拼成一个矩阵输入模型。truncationTrue会把超过max_length的部分截断防止输入超长。return_tensorspt指的是返回 PyTorch 张量框架对齐很重要。4.2 解读模型输出从 logits 到概率模型输出的logits是一个原始分数向量维度是(batch_size, num_labels)。它不能直接当作概率来看需要经过softmax归一化。很多人第一次跑模型直接看 logits 觉得结果很奇怪——数值可能是负的也可能大于 1这太正常了。softmax 之后才能得到每个类别的概率分布。我画一条思路线帮你理解输入输出文本字符串 → tokenizer 切分 → token ID 序列 → 模型 forward → logits → softmax → 概率。这四个环节任一步出错结果都会不对。最常见的问题出在 tokenizer 这一步比如你忘记设paddingTrue而 batch 里的文本长短不一模型会直接报维度错误。4.3 生成模型的调用方式不太一样分类模型上面已经讲了生成模型则要多一个generate的步骤。以 Qwen2.5-0.5B 为例from transformers import AutoModelForCausalLM, AutoTokenizer model_name Qwen/Qwen2.5-0.5B-Instruct tokenizer AutoTokenizer.from_pretrained(model_name, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained(model_name, trust_remote_codeTrue) messages [ {role: user, content: 用一句话解释什么是深度学习} ] text tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) inputs tokenizer([text], return_tensorspt) outputs model.generate(**inputs, max_new_tokens256, do_sampleTrue, temperature0.7) response tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokensTrue) print(response)这里有几个细节值得注意。trust_remote_codeTrue告诉库你允许执行模型目录里的自定义代码很多新模型需要这个参数。apply_chat_template会把对话消息转成模型期望的输入格式这很重要因为不同的指令模型对输入格式的要求完全不同。outputs[0]是生成的 token 序列inputs.input_ids.shape[1]是原始输入的长度我们切片截掉这部分只保留模型新生成的内容。4.4 batch 处理一次跑多条文本的优化技巧实际业务中很少一次只推理一条文本。批量推理可以显著提升 GPU 利用率。但 batch 不是越大越好受限于显存。我常用的策略是手动控制 batch size循环处理。比如一条一条跑太慢一次跑 32 条又可能显存溢出那就折中选 8 或 16压测确定最优值。还有个实用技巧是让同一批次的文本长度尽量接近这样 padding 造成的浪费少推理速度会快不少。最简单的方法是在预处理时按长度排序分组。5. 中文模型选型、显存估算与 CPU 推理优化5.1 中文场景用什么模型用 Transformers 跑中文任务模型选型很重要。我按任务类型和资源消耗整理了下面这个对照表任务类型轻量推荐效果推荐适用场景文本分类bert-base-chinesehfl/chinese-roberta-wwm-ext情感、意图、主题分类命名实体识别uer/roberta-base-wwm-chinese-clue-neruer/roberta-large-wwm-chinese-clue-ner关键词抽取、实体抽取语义相似度BAAI/bge-small-zh-v1.5BAAI/bge-large-zh-v1.5检索、去重、匹配文本生成Qwen/Qwen2.5-0.5B-InstructQwen/Qwen2.5-7B-Instruct对话、写作辅助、结构化输出阅读理解hfl/chinese-macbert-basehfl/chinese-macbert-large抽取式问答我自己的经验是在没有标注数据的情况下先用轻量模型跑通流程效果不够再换更大模型。不要一上来就上 7B 模型推理慢、显存大调参成本也高。很多时候 hfl/chinese-roberta-wwm-ext 在分类任务上已经足够碾压一众新模型了而且只有 100M 左右的参数量CPU 也能扛得住。5.2 显存怎么粗算心里要有数我推荐一个粗算公式模型推理时显存占用约为参数量的 2 倍到 4 倍取决于是否开启梯度、batch size 和序列长度。以 7B 模型为例fp16 精度下权重约占 14GB再加上激活值和 KV cache单条推理大概需要 16GB 显存。如果你只有 8GB 显存就别硬上 7B 的 fp16 了可以用 4-bit 量化压到 5GB 左右或者干脆用 0.5B、1.5B 的小模型。量化是显存不够时最常用的手段。Transformers 里通过bitsandbytes加载 4-bit 模型很简单“python from transformers import AutoModelForCausalLM, BitsAndBytesConfig import torchquantization_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypetorch.float16 )model AutoModelForCausalLM.from_pretrained( Qwen/Qwen2.5-7B-Instruct, quantization_configquantization_config, device_mapauto )device_mapauto 会自动把模型层分配到可用的 GPU 上显存不够时还会落到 CPU虽然会慢一些但至少能跑起来。 ### 5.3 CPU 推理的优化思路 不是所有人都有 GPU。我自己在开发环境里也经常用 CPU 跑模型验证逻辑。CPU 推理最重要的一个词是忍——把模型降到最小把批量降到最小把 max_length 降到刚好够用。另外Transformers 的 set_parallelism 可以调用 torch 的线程优化 python import torch torch.set_num_threads(8)这个方法在 CPU 推理时能明显提速。还有一个小技巧是使用ONNX导出模型推理速度通常比 PyTorch 原生快 20% 到 50%。不过 ONNX 的导出偶尔会遇到算子兼容问题不算必须项。如果你只是入门验证先把小模型跑通再说。6. 实测踩坑配置冲突、OOM 和异常输出的排查链路6.1 aimv2 is already used by a transformers config 是怎么一回事这个报错我印象很深。报错原文类似Oserror: something is already used by a transformers config, pick another name.第一次遇到的时候我完全懵了因为我没有定义过任何叫这个名字的东西。排查到最后发现这是transformers版本和本地环境残留冲突导致的。简单解释Transformers 在加载模型配置时会读入config.json如果配置里的model_type与库内已有的注册类型重名库就不知道你想用哪个了。常见诱因是安装在环境里的旧版本 Transformers 与新模型不兼容或者你在代码里手动注册了一个同名配置transformers的自动注册机制把它和内置的模型类型混在了一起。排查链路是先升级 Transformers 到最新版本清除~/.cache/huggingface里的旧缓存再在干净的虚拟环境里重装。如果你自己在代码里写了register之类的操作检查一下是不是和库内置的类型撞名了。我曾在一个项目里为了让一个自定义模型能被from_pretrained加载给它起了个和内置模型相同的名字结果就踩了这个坑改个名字就好了。6.2 显存溢出OOM的排查顺序OOM 是新手最容易遇到的报错之一。遇到CUDA out of memory我的处理顺序是把batch_size降到 1看能不能跑。能跑说明是 batch 太大用小一点的 batch 或者用梯度累积把max_length调小。很多任务是短文本没必要保留 512 的长度128 一般够用。输入长度直接决定激活大小和 KV cache 大小检查是不是开了torch.no_grad()。推理模式记得包一层不存梯度能省很多显存还是不行就用量化或者换小模型我可以明确告诉你90% 的 OOM 都能通过前两步解决不要一上来就上量化。6.3 tokenizer 三个参数的误用padding、truncation、max_length这三个参数看着简单坑却最多。有人只设置max_length不设truncationTrue超长文本不仅不会被截断还会在后端报token length exceeded。有人只设置paddingTrue不设max_length那么 padding 是按 batch 内最长序列来的如果某条文本特别长整个 batch 都会被撑大显存被白白浪费。我见过最离谱的一次是 batch 里混了一条几千字的文本直接导致单次推理消耗了 3GB 显存明明其他文本都很短。正确的做法是先根据你的任务统计数据长度分布选一个覆盖 90% 样本的max_length然后同时打开paddingTrue和truncationTrue。这样既保证矩阵维度对齐又不会让极端长文本拖垮整个 batch。6.4 输出不符合预期时的排查顺序模型输出和你的预期不一致时不要第一时间怀疑模型能力先检查流程。我的排查顺序是先看 tokenizer 是否用对了错误地用错模型配套的 tokenizer 会产生非常离谱的结果再看输入格式生成类模型有没有正确应用 chat_template很多指令模型不按格式输入时回答质量会急剧下降然后看解码参数max_new_tokens设太短会导致生成半句话temperature设太高会导致输出发散最后再考虑换一个更合适、更大的模型。比如你用情感分析模型却传了超长文本结果变成positive并不一定代表模型判断正确可能只是截断后看到开头就说好。我之前遇到过一个简历解析任务模型一直抽不出关键信息排查半天发现是文本中英文混合而选的是一个纯中文训练的模型词表里英文几乎不存在自然什么也抽不出来。这类问题跟模型能力无关纯粹是选型错了。写在最后的一点体会做 Transformers 这一年多我最大的感受是这个库的门槛被大大低估了。不少人觉得大模型高深实际上只要理解了加载、分词、推理、解码这条主线绝大多数任务都能套用同一套模板。先跑通再深入是最快的路径。最后分享一个我很受用的小习惯所有官方文档不写清楚的模型行为我都会写一个小脚本实测一遍比如不同的temperature值对生成结果的影响、不同的max_length对分类准确率的影响。把这些测试结果记录下来你会比自己想象中更快地掌握这套工具。
阅读完成 · 觉得有帮助?
咨询建站