Transformers 库实战指南:从环境配置到模型微调
发布时间:2026/10/1 23:35:08 作者:尧图编辑部 阅读量:1,286

1. 环境准备先把“工具箱”装齐1.1 为什么要用虚拟环境第一次接触 Transformers 的人最容易犯的错就是在全局 Python 环境里直接 pip install然后被各种版本冲突折磨得失去耐心。实际上 Transformers 生态迭代非常快今天你装的是 4.36下周可能就出了 4.40而某个旧项目可能锁死在 4.30。我建议所有项目一开始就建独立虚拟环境这样无论怎么折腾都不会连累系统环境里的其他 Python 项目。python -m venv my_transformers_env source my_transformers_env/bin/activate # Windows 下是 my_transformers_env\Scripts\activate这里有个容易踩的坑装好虚拟环境后你 pip 安装的包只会进入当前环境换一个终端窗口如果忘了 activate又会掉回全局环境然后你发现自己明明装过了却 import 不到。建议把source my_transformers_env/bin/activate写成终端开机加载或者干脆用 conda 管理。conda 的好处是不仅能隔离 Python 环境还能直接指定 Python 版本比如有些老模型对 Python 3.11 支持不好conda create -n env python3.10 一下就切过去了。我个人的习惯是用 conda 创建环境但环境内的依赖管理仍然用 pip因为 transformers 生态的包在 pip 上更新最快conda 源总是慢半拍。1.2 安装深度学习框架选对 PyTorch 版本Transformers 库本身只是模型加载和调用的上层封装真正负责张量计算的是 PyTorch 或 TensorFlow 这类底层框架。目前社区主流是 PyTorch几乎新发布的模型都优先支持 PyTorch 权重所以我下面默认以 PyTorch 为例。这里的关键是你必须根据自己的硬件先确定安装哪个版本的 PyTorch。很多人上来就是pip install torch结果装到的是 CPU 版本显卡白白浪费。判断方法非常简单nvidia-smi如果这个命令报错说明你没有 NVIDIA 显卡或者驱动没装好那就老老实实用 CPU 版。如果有显卡你要看输出的 CUDA Version 是多少比如是 12.1那你就装对应 CUDA 12.1 的 PyTorch。从 PyTorch 2.x 开始官方推荐用 pip 指定 index-url 安装pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121如果不想折腾 CUDA还有一个选择是直接用 CPU 版本先跑通流程。CPU 推理慢是慢但你要区分场景如果是学习 API 调用、跑通代码CPU 完全够如果是大规模微调那就不行了后面我会专门讲 CPU 怎么优化。1.3 安装 Transformers 库核心库本身安装非常简单pip install transformers不过单装这个还不够四件套缺一不可transformers模型加载与调用、torch计算后端、datasets数据处理、accelerate多卡加速与离线加载。建议一次性装齐pip install transformers datasets accelerate如果是在国内网络环境直接 pip install 经常卡死或超时我建议用清华镜像装速度能快一个量级pip install transformers datasets accelerate -i https://pypi.tuna.tsinghua.edu.cn/simple装完之后先跑一个最简单的验证确认库和底层框架都正常import transformers print(transformers.__version__) import torch print(torch.__version__) print(torch.cuda.is_available())如果torch.cuda.is_available()返回 True说明 GPU 环境 OK。如果是 False后面大概率是你的 PyTorch 版本和驱动不匹配把上面那个 index-url 换成你自己的 CUDA 版本号重装一遍就行。2. 先理解三个关键概念2.1 预训练模型到底是什么很多人一听“预训练大模型”就觉得很神秘其实这个概念可以打一个特别接地气的比方它就像一个已经读了十年书的人。这个人已经掌握了语言的基本规律、常识和逻辑你不需要再教他认字、组词、造句你只需要告诉他你现在要看哪一本书、关注什么问题他就能很快上手。技术上来说预训练模型是在海量文本上“自学”过的深度神经网络。它学到的是语言的统计规律、语法结构、常识知识这些东西被压缩在几十亿甚至上千亿个参数里。我们拿到一个预训练模型之后有两种用法一种是直接拿来推理让模型根据输入生成输出这种叫推理inference另一种是把模型继续在自己的少量任务数据上训练一段时间让它更适应你的特定任务这种叫微调fine-tuning。所有 Transformers 库的核心功能本质上就是围绕这两个场景展开的。我自己的经验是第一次接触这个生态不要一上来就想着微调那涉及的东西太多数据格式、训练参数、分布式环境。先从推理开始把一个模型加载起来、跑通一次输出你就能自然理解模型的输入输出逻辑后面再微调就容易多了。2.2 Tokenizer模型入口的“词典工具”模型不认识英文单词也不认识中文字符它只认数字。所以你输入的任何文本在进入模型之前都要被转换成一串数字 ID做这个转换的工具就是 Tokenizer分词器。Tokenizer 的核心逻辑是维护一个词表vocabulary里面存了几万个常见的词和子词单元每个单元对应一个 ID。分词器把文本切分成词表中的单元然后查表得到 ID 序列。你可能会问为什么不按整词切因为大家说过“nlp 里没有真正的分词”同一个词在中文里容易变体比如“孩子们”你切开之后模型没见过“孩子”的单独形态怎么办所以现在的方案是非监督式的子词切分把“孩子们”切分成“孩子”“们”这俩在词表里都单独存在模型能灵活组合。Transformers 库设计了一套统一的 Tokenizer API核心方法就几个只要记住from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(bert-base-chinese) inputs tokenizer(你好世界, return_tensorspt) print(inputs[input_ids])输出是一串张量比如[[101, 2974, 7392, 4710, 102]]。其中 101 和 102 是 BERT 的特殊标记CLS 和 SEP用来标识句子开始和结束。每个模型自己带着一个专属分词器你加载模型时顺手就加载了不需要手动实现。2.3 三类主流模型架构速览Transformers 这个名字最早来自 2017 年那篇《Attention Is All You Need》论文后来社区把模型按结构分成三大类每一类擅长的任务完全不同。第一类是 Encoder-only代表是 BERT 和它的兄弟们。它像一个“阅读理解机器”擅长把一段文本编码成向量然后做分类、抽取、相似度判断这类任务。它不能生成文本但理解能力强训练和推理速度快。如果你要做文本分类、情感分析、实体识别、句子相似度优先考虑这一类。第二类是 Decoder-only代表是 GPT 系列、LLaMA、Qwen、Mistral 等。它像一个“接龙机器”每次只预测下一个最可能的词把预测到的词接在后面继续预测循环往复就生成了整段文本。现在的 ChatGPT 背后的架构就是这种。你要做聊天、续写、摘要生成就找这一类。第三类是 Encoder-Decoder代表是 T5、BART。它先把输入编码成中间表示再解码生成输出适合做机器翻译、文本摘要这类典型“从一段到另一段”的任务。你在 Transformers 里通过 AutoModelForXXX 这个类去加载对应架构的模型比如AutoModelForSequenceClassification加载 BERT 做分类AutoModelForCausalLM加载 GPT 做生成。核心就记住一点任务的类型决定了你选哪类模型。3. 核心实操加载模型并完成推理3.1 快速上手Pipeline 三行代码Transformers 库最贴心的地方是它封装了一个pipeline()接口把分词、模型加载、推理、后处理全部打包成了“三行代码”。如果你只是想快速体验一下模型效果或者验证一个想法完全不需要深入底层 API。from transformers import pipeline classifier pipeline(sentiment-analysis, modeldistilbert-base-uncased-finetuned-sst-2-english) result classifier(I love this movie!) print(result)输出结果类似[{label: POSITIVE, score: 0.9998}]pipeline会自动帮你下载模型权重和分词器文件你只要指定任务类型和模型名。任务类型常见的有 sentiment-analysis情感分析、text-generation文本生成、summarization摘要、translation翻译、ner命名实体识别等。这里我要提醒一下pipeline的模型名其实是可以不填的比如你直接pipeline(sentiment-analysis)它会用自己的默认模型。但我不建议这么干因为默认模型往往是英语模型你对中文文本做分析效果会很差。更稳妥的做法是去 Hugging Face 模型市场搜索一下有没有针对中文或者针对你的业务场景的微调模型然后指定它。3.2 手动流程Tokenizer Model 完整链路用pipeline虽然方便但如果你想真正理解 Transformers 的底层逻辑或者想控制更多细节比如修改生成参数、拿到中间隐藏层向量就必须手动走一遍完整链路。整个流程一共四步加载分词器、加载模型、文本转 ID、模型推理。先看一个文本分类的完整示例from transformers import AutoTokenizer, AutoModelForSequenceClassification import torch tokenizer AutoTokenizer.from_pretrained(nlptown/bert-base-multilingual-uncased-sentiment) model AutoModelForSequenceClassification.from_pretrained(nlptown/bert-base-multilingual-uncased-sentiment) text 这个产品真的很好用 inputs tokenizer(text, return_tensorspt, truncationTrue, max_length512) with torch.no_grad(): outputs model(**inputs) logits outputs.logits predicted_class torch.argmax(logits, dim-1).item() print(f预测类别: {predicted_class})几个关键细节我展开说一下。第一return_tensorspt表示返回 PyTorch 张量如果return_tensorstf就返回 TensorFlow 张量不传则返回 Python 列表。用 pt 肯定是最常见的。第二truncationTrue和max_length512很重要。BERT 这类模型对输入序列长度有上限通常是 512 token如果你的文本超长不截断直接装进去有的模型会报维度错误有的会静默出错。我的建议是所有调用都加上这两个参数哪怕你的句子很短因为你无法预知线上真实输入会有多长。第三model(**inputs)这一步非常关键它等价于model(input_ids..., attention_mask...)。Transformers 的设计哲学是“输入一个字典输出一个对象”输出的logits就是每个类别的原始分数你还得用softmax转成概率或者直接用argmax拿类别。第四with torch.no_grad()一定要写。推理阶段没有反向传播的需求如果不关闭梯度计算每个 batch 的中间变量都会被保存下来显存占用会翻几倍跑几次就可能 OOM。这个细节新手最容易忽略。3.3 生成类任务的关键参数文本生成Decoder-only 模型的调用方式和分类不同核心区别在于你要调用model.generate()而不是model()。我以当前社区使用量极高的 Qwen 系列中文模型为例展示一个最基础的调用from transformers import AutoTokenizer, AutoModelForCausalLM model_name Qwen/Qwen2-1.5B-Instruct tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name) prompt 用一句话总结今天的天气 inputs tokenizer(prompt, return_tensorspt) outputs model.generate( **inputs, max_new_tokens50, do_sampleTrue, temperature0.7, top_p0.9 ) result tokenizer.decode(outputs[0], skip_special_tokensTrue) print(result)这里面的生成参数每一个都有讲究我逐个说清楚。max_new_tokens控制生成的最大新 token 数。它和max_length的区别是max_length是输入加输出总长度上限而max_new_tokens只看生成部分。两者都能用但max_new_tokens更符合直觉因为你通常知道自己想让模型说多长。do_sampleTrue表示使用随机采样策略而不是贪心解码。贪心解码每次选概率最大的词容易产生重复、空洞、无聊的文本采样则引入了随机性使文本更自然。但采样也要有度过于随机容易胡言乱语所以需要后面两个参数来约束。temperature控制概率分布的锐利程度取值一般在 0.1 到 1.0 之间。温度越低输出越保守、越确定温度越高输出越随机、越有创造力。打个比方温度 0.2 像照本宣科温度 1.5 像喝醉的人在说话。普通对话场景0.7 左右是一个安全区间。top_p是核采样nucleus sampling的参数它在每一个生成步骤只从累计概率达到 top_p 的词里采样。比如 top_p0.9就是只从概率总和前 90% 的词里选。这个参数能有效过滤掉那些概率极低、明显不合适的选项同时保留多样性。说实话这些参数没有固定答案跟模型、任务、期望风格都有关。我自己的经验是摘要和翻译等任务想要稳定输出temperature 调 0.2~0.3对话和创意写作可以调 0.7~0.9超过 1.2 基本不可控。另外top_p不要和top_k同时设置两者机制类似同用一个就够了。4. 进阶用自己的数据微调模型4.1 什么时候需要微调你可能已经注意到了通用预训练模型虽然什么都会一点但真放到你的具体业务里效果往往差强人意。比如你想做一个医疗领域的智能问答一个在互联网通用语料上训练的模型可能分不清“感冒”和“流感”的专业差异甚至会一本正经地胡说八道。这时候你就需要微调。微调的核心思想是用你的少量任务数据在预训练模型的基础上继续训练让模型把通用知识迁移到你的特定场景。所谓“少量”其实也看任务几百条到几万条都能跑关键是你数据质量要好、标注要准确。不过我要提醒一句微调不是银弹。如果你的任务只是“根据输入文本做分类”换个思路直接调用现有大模型的 API比如通过 API 接口进行 few-shot 提示也能解决问题成本低得多。什么时候真的需要微调我的判断标准有三个一是你手上有几十万条以上的专属数据二是你的任务对实时性有要求没法每次请求都走大模型接口三是你的数据涉及领域专业内容且通用模型完全答不准。4.2 准备数据集Transformers 生态里有个配套库叫datasets它负责数据的加载、清洗和格式化。微调时你需要把数据整理成模型能吃的格式这一步远比想象的费事。以最经典的文本分类微调为例你的数据应该是一个 CSV 或 JSON 文件包含两列一列是文本一列是标签。加载方式如下from datasets import load_dataset dataset load_dataset(csv, data_filesmy_data.csv)但如果你要训练的是一个生成类模型比如对话、问答数据格式就不同了。以指令微调理应的典型格式为例每条样本应该是 structured 的通常是instruction、input、output三项。现在社区比较通用的做法是把指令、输入、输出拼成对话模板再交给模型训练。比如 Qwen 这类模型的训练数据是 ChatML 格式需要把用户消息和助手消息前后加上特殊标记。我这里强烈建议你在微调前先做一次简单的数据直觉检查。随机抽 20~50 条样本打印出来问自己几个问题文本有没有乱码标签有没有错的样本平衡吗领域词汇是否统一数据质量差的话训练时间基本白费模型会学到你的错误标注。4.3 用 Trainer 进行训练Transformers 库提供了一个高层 API 叫Trainer它把分布训练、梯度累积、学习率调度、日志输出、检查点保存全都封装好了。你不需要理解底层细节只要配置好参数就能训练。先看一个完整的微调示例from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer ) model_name bert-base-chinese model AutoModelForSequenceClassification.from_pretrained(model_name, num_labels2) tokenizer AutoTokenizer.from_pretrained(model_name) def tokenize_function(examples): return tokenizer(examples[text], truncationTrue, paddingmax_length, max_length128) tokenized_dataset raw_dataset.map(tokenize_function, batchedTrue) training_args TrainingArguments( output_dir./results, evaluation_strategyepoch, learning_rate2e-5, per_device_train_batch_size16, per_device_eval_batch_size16, num_train_epochs3, weight_decay0.01, save_total_limit2, fp16True, ) trainer Trainer( modelmodel, argstraining_args, train_datasettokenized_dataset[train], eval_datasettokenized_dataset[test], ) trainer.train()这里面我用到的几个参数都很关键展开说明一下。learning_rate2e-5这是微调任务推荐的学习率。预训练模型已经收敛得很好你微调时不能用一个大的学习率否则会破坏它已经学到的通用知识。一般来说1e-5 到 5e-5 这个区间是安全的具体多少看你的数据量和任务难易。per_device_train_batch_size每个设备上每批喂给模型的样本数。这个值直接决定显存占用和训练速度。显存不够就调小比如从 16 降到 8 或 4显存够就调大训练会明显更快。num_train_epochs3训练轮数。NLP 微调一般不追求多轮2~5 轮就够了。轮数过多容易过拟合模型在训练集上表现好但测试集上效果反而下降。我建议训练完看 eval loss如果 eval loss 在某个 epoch 开始回升那说明开始过拟合了在这个 checkpoint 停止就行。fp16True混合精度训练用半精度浮点数来减少显存占用并加速训练。前提是你的 GPU 支持而且是把 PyTorch 和 CUDA 版本装对了。这个开关在 RTX 20 系列之后的绝大多数显卡上都能开。还有一个细节是weight_decay这是 L2 正则化防止权重过大导致过拟合。0.01 是 BERT 论文里用的值插到别的模型一般也没问题。4.4 微调后的模型保存与使用训练完了模型和分词器都要保存下来这是很多人容易漏的一步。完整保存方式如下model.save_pretrained(./my_finetuned_model) tokenizer.save_pretrained(./my_finetuned_model)保存后会生成config.json、pytorch_model.bin和分词器文件等。你下次使用的时候直接加载这个目录即可完全不需要重新下载原始权重model AutoModelForSequenceClassification.from_pretrained(./my_finetuned_model) tokenizer AutoTokenizer.from_pretrained(./my_finetuned_model)如果你想把模型分享给同事或部署到服务器可以把本地目录整个打包上传也可以推送到 Hugging Face 模型市场。推送到 Hub 的步骤是先用huggingface-cli login验证身份然后model.push_to_hub(your_model_name)。如果是企业内部不公开还以用privateTrue私有化存储或者干脆不推送直接用内网共享存储分发文件。5. 高频问题排查与避坑实录5.1 GPU 显存不足OOM这是我在交流群里被问到最多的问题报错长这样CUDA out of memory。显存不足常见有两种原因一是模型本身就很大比如 7B 参数的模型 fp16 大约需要 14GB 显存二是你的 batch size 开太大或者推理时忘了开no_grad()。我的排查顺序是先看模型大小是否超过显存如果超过要么换小模型比如从 7B 换到 1.5B要么用加载时的low_cpu_mem_usageTrue参数如果模型尺寸远小于显存那就去调 batch size。训练时 OOM 就把 per_device_train_batch_size 减半推理时 OOM 就看是不是忘了torch.no_grad()。还有一个隐藏的技巧使用model AutoModel.from_pretrained(..., load_in_8bitTrue)或load_in_4bitTrue做量化加载。这是在显存不足的情况下继续跑大模型的常用手段虽然精度有一定损失但能跑起来才是第一优先。5.2 模型下载缓慢或总是失败Hugging Face 的模型仓库在国外国内访问经常超时。解决办法是设置镜像环境变量export HF_ENDPOINThttps://hf-mirror.com设置了之后from_pretrained和pipeline都会从这个国内镜像下载。不过要注意这个镜像的内容同步有一定延迟太新发布的模型可能要等几天才能搜到。另一个思路是提前下载好权重文件然后在代码里直接指定本地路径加载。我自己的习惯是把常用模型都放在一个固定目录里比如~/models/bert-base-chinese加载时写成from_pretrained(~/models/bert-base-chinese)这样以后无论改不改镜像都能离线加载。5.3 版本兼容性Transformers 生态有一个经典版本搭配我先列出来Transformers 4.36 配 PyTorch 2.1配 Python 3.10 或 3.11配 accelerate 0.25。如果你用的是 Transformers 4.30 配 PyTorch 1.13某些新模型特别是那些用到了新的 attention 实现或者需要 safetensors 支持的模型可能直接报错或者加载出来的权重随机初始化的而不是预训练的效果惨不忍睹。解决版本问题的思路其实很简单先判断你的 Transformers 是不是太旧如果是直接升级如果已经比较新但还是报错那多半是底层依赖不对。我遇到过的一个典型报错是ModuleNotFoundError: No module named accelerate原因是新版 Transformers 的分布式和离线加载底层依赖 accelerate而这个包没有默认装。解决方案就是pip install accelerate一句话的事。5.4 CPU 推理加速与极小模型选择很多人没有 GPU但也想学这个生态其实也能跑只要选对模型。BERT 系列最小的bert-tiny和distilbert-base-uncased在 CPU 上跑推理也就几百毫秒完全可接受。生成类模型里openai-community/gpt2这类小模型也能在 CPU 上凑合跑。我的建议是学习阶段先用 CPU 跑小模型不断熟练掌握 API 之后再上 GPU 跑大模型这样思路不会在环境问题上中断。如果你确实有 CPU 推理的性能需求还有一个简单有效的加速手段在推理时设置model.eval()然后关闭梯度必要时用torch.compile进一步加速。不过torch.compile需要较新的 PyTorch 版本而且首次运行有编译开销小模型可能体会不明显。5.5 常见问题速查表问题可能原因快速解决方案CUDA out of memorybatch size 过大或模型太大减小 batch size开启 fp16用 load_in_8bit/4bit 量化下载模型超时Hugging Face 官网连接不稳定设置 HF_ENDPOINThttps://hf-mirror.com 或本地离线加载报错找不到 accelerateTransformers 版本过新但依赖缺失pip install accelerate生成结果全是重复没有设置采样参数或温度过低do_sampleTruetemperature 0.7以上tokenizer 输出乱码加载了错误的分词器比如用英文模型的 tokenizer 处理中文确认模型是 multulingual 或中文专用模型eval loss 持续上升训练轮数过多过拟合减少 epochs或用早停策略CPU 推理慢到不能忍模型参数太大或未开启评估模式换更小的模型启用 model.eval()关闭梯度最后再分享一个我自己实践中很受用的习惯不管模型多小第一次加载的时候一定要打印一下模型的config看看它支持的max_position_embeddings、vocab_size、num_labels这些关键参数。很多莫名其妙的报错比如输入维度对不上、分类数量不对根因都能从 config 里看出来。这个工作看起来多花了一分钟但能帮你省掉后面数小时的排查时间。