Hugging Face详解:从模型下载到中文文本分类微调
前面的文章已经从零实现了 Transformer、Mini-GPT 和 RoPE。理解这些底层原理之后,一个很自然的问题是:
实际项目中,难道每次都要从头实现网络、训练 Tokenizer,再用海量数据预训练模型吗?
通常不需要。
很多任务可以从一个已经预训练好的模型出发,只添加或替换任务头,再使用自己的数据进行微调。Hugging Face 正是把“寻找模型、下载权重、统一调用、处理数据、训练评估和分享结果”串起来的一套生态。
不过,初学者很容易把下面这些概念混在一起:
- Hugging Face 网站。
- Hugging Face Hub。
transformersPython 库。datasetsPython 库。pipeline()。AutoTokenizer与AutoModel。Trainer。- Spaces 与在线推理服务。
本文会先把它们拆开,再通过一个中文情感分类项目完整走通:
选择预训练模型
-> 加载 Tokenizer
-> 文本转成模型输入
-> 加载任务模型
-> 手动完成一次前向推理
-> 构造 Dataset
-> 批量 Tokenize
-> 动态 Padding
-> Trainer 微调与评估
-> 保存到本地
-> 重新加载并推理
-> 可选上传到 Hub
1. Hugging Face 到底是什么
Hugging Face 不是单独一个库,也不只是一个模型下载网站。可以从三个层面理解它。
1.1 社区与平台
Hugging Face Hub 是一个围绕机器学习资产构建的协作平台。常见资产包括:
- Models:模型配置、Tokenizer、权重和 Model Card。
- Datasets:训练、验证与测试数据,以及 Dataset Card。
- Spaces:可以在线运行的机器学习演示或应用。
可以把 Hub 粗略理解为“面向机器学习项目的代码与资产仓库平台”,但模型仓库不只有代码,还包含体积很大的参数文件、任务元数据、许可证和在线推理信息。
1.2 Python 库生态
| 库 | 主要作用 |
|---|---|
transformers |
加载和使用 Transformer 模型、Tokenizer、Trainer |
datasets |
加载、处理、缓存和流式读取数据集 |
huggingface_hub |
搜索、下载、上传和管理 Hub 仓库 |
tokenizers |
高性能 Tokenizer 实现 |
accelerate |
设备管理、混合精度和分布式训练 |
evaluate |
加载和计算评估指标 |
safetensors |
安全、快速地保存和加载张量 |
peft |
LoRA 等参数高效微调方法 |
本文的主线使用前五项中的核心能力,并把 evaluate 作为可选工具介绍。
1.3 一组统一约定
Hugging Face 很重要的一点不是“收集了很多模型”,而是让大量不同架构能够使用相似接口:
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)
模型可以从 BERT 换成 RoBERTa,代码骨架仍然相似。差异主要由仓库中的 config.json、Tokenizer 配置与权重文件描述。
2. Hub、Transformers 和 PyTorch 的关系
这三个概念处在不同层次:
Hugging Face Hub
保存与分发配置、Tokenizer、权重、说明文档
Transformers
根据配置构造模型类并下载权重,提供统一高级 API
PyTorch
执行张量运算、自动微分和参数更新
当我们执行:
model = AutoModelForSequenceClassification.from_pretrained(model_id)
背后大致发生:
- 根据
model_id定位 Hub 仓库或本地目录。 - 读取
config.json。 - 从配置识别模型架构。
- 实例化对应的 PyTorch
nn.Module。 - 下载并加载模型权重。
- 返回可以正常前向传播的模型对象。
因此,Transformers 没有替代 PyTorch。它是在 PyTorch 之上提供模型实现、配置系统、预训练权重和训练工具。
3. 模型仓库里有什么
一个典型的 Transformers 模型仓库可能包含:
README.md
config.json
model.safetensors
tokenizer.json
tokenizer_config.json
special_tokens_map.json
vocab.txt / vocab.json / merges.txt
generation_config.json
不同模型的文件并不完全相同。
3.1 README.md
在模型仓库中,它通常会渲染成 Model Card,应说明:
- 模型适合做什么。
- 不适合做什么。
- 训练数据与训练方法。
- 评估结果。
- 已知偏差与限制。
- 许可证。
- 使用示例。
下载数高不代表模型适合你的任务。选择模型时,Model Card 应该是第一阅读入口。
3.2 config.json
它描述模型结构和任务相关配置,例如:
{
"model_type": "bert",
"hidden_size": 768,
"num_hidden_layers": 12,
"num_attention_heads": 12,
"vocab_size": 21128,
"num_labels": 2
}
实际字段由模型架构决定。
3.3 权重文件
常见格式包括:
model.safetensors
pytorch_model.bin
大型模型还可能被切成多个 shard,并使用索引文件记录每个参数所在位置。
3.4 Tokenizer 文件
Tokenizer 与模型必须匹配。它们共同决定:
- 词表。
- 子词切分规则。
- 特殊 token。
- padding 方向。
- 最大长度等默认行为。
模型权重和 Tokenizer 随意混用,轻则效果异常,重则 token id 超出词表范围。
4. 三个容易混淆的词:架构、Checkpoint 和任务
4.1 架构 Architecture
架构描述模型骨架,例如:
BERT
RoBERTa
GPT-2
T5
ViT
4.2 Checkpoint
Checkpoint 是某个架构在特定训练阶段保存的参数和配置。例如:
google-bert/bert-base-chinese
它是一个具体 Hub 仓库 ID,不等于“所有 BERT”。
4.3 任务 Task
同一 Backbone 可以连接不同任务头:
BERT Backbone
-> Sequence Classification Head 文本分类
-> Token Classification Head 命名实体识别
-> Question Answering Head 抽取式问答
-> Masked LM Head 掩码语言模型
这就是为什么 Transformers 提供多种 AutoModelFor... 类。
5. 安装与环境准备
建议为项目创建独立虚拟环境,再安装稳定发布版本:
python -m venv .venv
Windows PowerShell:
.\.venv\Scripts\Activate.ps1
安装 PyTorch 时应根据 CPU 或 CUDA 环境使用 PyTorch 官方给出的命令。安装 Hugging Face 相关库:
pip install transformers datasets accelerate safetensors
如果还要使用官方评估指标:
pip install evaluate
检查版本:
import torch
import transformers
import datasets
print("torch:", torch.__version__)
print("transformers:", transformers.__version__)
print("datasets:", datasets.__version__)
print("cuda available:", torch.cuda.is_available())
5.1 为什么要记录版本
Hugging Face 生态更新较快,不同大版本可能调整参数名、默认值或返回类型。真实项目应记录依赖:
pip freeze > requirements-lock.txt
教程代码无法替代项目自己的版本锁定。遇到 API 报错时,首先核对:
本地安装版本
官方文档所选版本
示例代码对应版本
6. 第一层 API:用 pipeline() 快速推理
pipeline() 是最方便的推理入口。以中文情感分析为例:
from transformers import pipeline
model_id = "uer/roberta-base-finetuned-jd-binary-chinese"
classifier = pipeline(
task="text-classification",
model=model_id,
)
results = classifier([
"这个耳机音质清晰,佩戴也很舒服。",
"包装破损,使用两天就无法开机。",
])
print(results)
首次运行通常会下载模型,之后优先从本地缓存读取。
6.1 不要猜测 LABEL_0 的含义
有些模型会输出:
LABEL_0
LABEL_1
不能想当然地认为 LABEL_1 一定表示正面。正确做法是查看 Model Card 和模型配置:
print(classifier.model.config.id2label)
模型作者若没有正确填写标签映射,使用者还需要结合训练数据说明确认语义。
6.2 pipeline 不是模型本身
pipeline 是一个推理工作流封装,大致包含三个阶段:
preprocess
文本 -> Tokenizer -> 模型输入张量
forward
张量 -> 模型 -> logits
postprocess
logits -> Softmax -> 标签与分数
它适合:
- 快速验证模型。
- 编写原型。
- 常规批量推理。
需要精确控制张量、损失函数或训练过程时,应继续下潜到 AutoTokenizer 和 AutoModel。
7. pipeline 中的任务字符串
常见任务包括:
| 任务 | 常见 task |
|---|---|
| 文本分类 | text-classification |
| 文本生成 | text-generation |
| 命名实体识别 | token-classification |
| 抽取式问答 | question-answering |
| 掩码填空 | fill-mask |
| 摘要 | summarization |
| 翻译 | translation 或模型对应任务 |
| 图像分类 | image-classification |
| 自动语音识别 | automatic-speech-recognition |
模型必须与任务兼容。不能把仅有 BERT Masked LM 头的 Checkpoint 当作自回归文本生成模型,也不能认为任何文本模型都能直接做分类。
7.1 显式指定模型
下面的写法虽然短:
classifier = pipeline("text-classification")
但它会选择一个默认模型。教学实验可以使用,正式项目最好显式指定:
classifier = pipeline(
"text-classification",
model="组织名/模型名",
revision="明确的分支、标签或提交哈希",
)
这样更容易复现,也能避免默认模型改变带来的行为漂移。
8. 第二层 API:AutoTokenizer
Tokenizer 把字符串转换成模型能够接收的整数张量:
from transformers import AutoTokenizer
model_id = "google-bert/bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_id)
encoded = tokenizer(
"Hugging Face 让模型复用更方便",
return_tensors="pt",
)
for name, tensor in encoded.items():
print(name, tensor.shape)
print(tensor)
对 BERT 类模型,常见输出包括:
input_ids
attention_mask
token_type_ids 某些模型有,某些没有
8.1 input_ids
Tokenization 的概念流程为:
原始文本
-> 规范化
-> 子词切分
-> 加特殊 token
-> 查词表得到 token id
可以直接查看:
tokens = tokenizer.tokenize("我正在学习Hugging Face")
ids = tokenizer.convert_tokens_to_ids(tokens)
print(tokens)
print(ids)
也可以把完整编码转换回 token:
print(tokenizer.convert_ids_to_tokens(encoded["input_ids"][0]))
8.2 特殊 token
BERT 类 Tokenizer 常在序列两侧加入类似:
[CLS] 文本 token ... [SEP]
不同架构可能使用不同特殊 token。不要在业务代码里硬编码它们,应使用 Tokenizer 的配置:
print(tokenizer.cls_token, tokenizer.cls_token_id)
print(tokenizer.sep_token, tokenizer.sep_token_id)
print(tokenizer.pad_token, tokenizer.pad_token_id)
8.3 attention_mask
在常见文本模型中:
1 表示真实 token
0 表示 Padding token
模型利用它避免把补齐部分当成有效内容。
9. Padding 与 Truncation
一个 batch 中的句子长度往往不同,但张量需要组成规则矩形。
batch = tokenizer(
[
"这个产品很好用。",
"外观不错,但是续航时间没有达到宣传效果。",
],
padding=True,
truncation=True,
max_length=64,
return_tensors="pt",
)
9.1 padding=True
把当前 batch 中较短的样本补到最长样本的长度:
样本 A:8 token
样本 B:15 token
组成 batch 后都变成 15 token
9.2 padding="max_length"
所有样本都补到指定的 max_length。优点是形状固定,缺点是短句很多时会浪费计算。
9.3 truncation=True
当 token 数超过允许长度时截断。必须意识到:
截断不会“压缩理解”被删除的文本,它会直接丢弃超出部分。
如果关键信息出现在长文末尾,需要考虑滑动窗口、分块、长上下文模型或任务专用聚合策略。
10. 动态 Padding 为什么更节省计算
假设整个数据集最长为 512,但某个 batch 中最长只有 83。
固定 Padding:
[B, 512]
动态 Padding:
[B, 83]
Self-Attention 的主要计算与序列长度平方相关:
$$
O(T^2)
$$
因此避免不必要的 Padding 往往能节省明显的显存和计算。
Transformers 提供:
from transformers import DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
它在组 batch 时才按照当前 batch 的最长序列进行补齐。
11. AutoModel 与 AutoModelFor... 有什么区别
11.1 AutoModel
from transformers import AutoModel
backbone = AutoModel.from_pretrained(model_id)
通常只加载基础 Backbone,返回隐藏状态。例如 BERT 常见输出形状:
last_hidden_state: [B, T, H]
它适合:
- 提取文本表示。
- 自己设计任务头。
- 研究中间隐藏状态。
11.2 AutoModelForSequenceClassification
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(
model_id,
num_labels=2,
)
它在 Backbone 上增加文本分类头,输出:
logits: [B, num_labels]
11.3 常用 AutoModel 类
| 类 | 任务 |
|---|---|
AutoModel |
基础隐藏表示 |
AutoModelForSequenceClassification |
句子或文本分类 |
AutoModelForTokenClassification |
每个 token 分类 |
AutoModelForQuestionAnswering |
抽取答案起止位置 |
AutoModelForMaskedLM |
预测被遮住的 token |
AutoModelForCausalLM |
自回归生成下一个 token |
AutoModelForSeq2SeqLM |
翻译、摘要等序列到序列生成 |
12. 手动完成一次分类推理
现在不使用 pipeline,把内部过程展开。
import torch
from transformers import (
AutoModelForSequenceClassification,
AutoTokenizer,
)
model_id = "uer/roberta-base-finetuned-jd-binary-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)
texts = [
"物流很快,商品质量也很好。",
"用了几分钟就坏了,非常失望。",
]
inputs = tokenizer(
texts,
padding=True,
truncation=True,
return_tensors="pt",
)
model.eval()
with torch.inference_mode():
outputs = model(**inputs)
logits = outputs.logits
probabilities = torch.softmax(logits, dim=-1)
predicted_ids = probabilities.argmax(dim=-1)
for text, class_id, probs in zip(
texts,
predicted_ids.tolist(),
probabilities.tolist(),
):
label = model.config.id2label[class_id]
print(text)
print(label, probs)
12.1 为什么模型输出 logits 而不是概率
分类头通常输出任意实数:
$$
z=[z_1,z_2,\ldots,z_C]
$$
Softmax 才把它们转换为概率:
$$
P(y=c\mid x)
=\frac{e^{z_c}}{\sum_{j=1}^{C}e^{z_j}}
$$
训练时交叉熵会在数值稳定的实现中直接接收 logits,所以不要在传给交叉熵之前手动 Softmax。
12.2 outputs 为什么可以点号访问
Transformers 模型通常返回 ModelOutput 的具体子类,既具有命名字段,又能在一定程度上像元组一样使用:
print(outputs.logits)
若传入 labels,分类模型通常还会返回:
print(outputs.loss)
13. 一次带标签的前向传播
labels = torch.tensor([1, 0])
model.train()
outputs = model(**inputs, labels=labels)
print(outputs.loss)
print(outputs.logits.shape)
内部概念流程为:
文本
-> Tokenizer
-> input_ids / attention_mask
-> Transformer Backbone
-> 分类头
-> logits [B, C]
-> 与 labels 计算 CrossEntropyLoss
只调用这一步不会更新参数。完整训练还需要:
optimizer.zero_grad()
outputs.loss.backward()
optimizer.step()
Trainer 会把这些训练循环工作统一封装起来。
14. Hugging Face Datasets 是什么
datasets 提供统一的数据加载和处理接口:
from datasets import load_dataset
dataset = load_dataset("数据集仓库ID")
print(dataset)
返回值经常是 DatasetDict:
DatasetDict({
train: Dataset(...)
validation: Dataset(...)
test: Dataset(...)
})
每个 split 都包含列名、特征类型和行数。
14.1 从本地文件加载
CSV:
dataset = load_dataset(
"csv",
data_files={
"train": "data/train.csv",
"validation": "data/validation.csv",
},
)
JSON Lines:
dataset = load_dataset(
"json",
data_files={"train": "data/train.jsonl"},
)
纯文本:
dataset = load_dataset(
"text",
data_files={"train": "data/train.txt"},
)
14.2 从 Python 数据创建
教学项目可以直接使用:
from datasets import Dataset
train_dataset = Dataset.from_dict({
"text": ["非常好用", "质量太差"],
"label": [1, 0],
})
15. 使用 map() 批量 Tokenize
原始数据只有文本和标签:
text: "这个产品很好用"
label: 1
定义预处理函数:
def tokenize_batch(examples):
return tokenizer(
examples["text"],
truncation=True,
max_length=128,
)
批量应用:
tokenized_dataset = dataset.map(
tokenize_batch,
batched=True,
)
处理后会多出模型输入列:
text
label
input_ids
attention_mask
token_type_ids 视模型而定
15.1 batched=True 是什么
不使用批处理时,函数每次接收一条样本:
{"text": "一条文本", "label": 1}
使用 batched=True 后,每次接收一批列数据:
{
"text": ["文本A", "文本B", ...],
"label": [1, 0, ...],
}
Fast Tokenizer 能够高效处理一批字符串,通常比 Python 逐条调用更快。
15.2 map() 通常返回新数据集
Datasets 的处理方法通常不会原地修改原对象:
tokenized_dataset = dataset.map(...)
不要忘记接收返回值。
15.3 为什么预处理阶段不固定 Padding
这里有意不写:
padding="max_length"
我们保留不同长度的 input_ids,等到 Data Collator 组成 batch 时再动态 Padding。
16. Data Collator 在哪里工作
数据处理可以分成两层:
Dataset.map
每条样本:文本 -> 长度不一的 token id
DataCollatorWithPadding
每个 batch:多条样本 -> 补齐成规则张量
例如三条序列长度分别为:
7, 11, 9
组成 batch 后:
input_ids.shape = [3, 11]
attention_mask.shape = [3, 11]
labels.shape = [3]
Data Collator 只整理 batch,不负责模型前向和参数更新。
17. 用迁移学习完成中文情感分类
下面使用:
Backbone: google-bert/bert-base-chinese
任务: 二分类
标签: NEGATIVE / POSITIVE
教学数据规模很小,只用于确认代码和数据流正确:
train_texts = [
"物流很快,包装完整,商品也很好用。",
"音质清晰,佩戴几个小时也很舒服。",
"屏幕显示细腻,系统运行十分流畅。",
"客服回复及时,问题很快得到解决。",
"续航表现不错,一整天使用没有压力。",
"做工粗糙,刚拆开就发现了划痕。",
"电池掉电特别快,完全无法正常使用。",
"商品与描述不符,体验非常糟糕。",
"包装已经破损,而且缺少配件。",
"运行频繁卡顿,还出现了自动关机。",
]
train_labels = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
真实项目应使用足够大、标签可靠且与目标分布一致的数据,并保留独立测试集。
18. 标签映射为什么必须写清楚
id2label = {
0: "NEGATIVE",
1: "POSITIVE",
}
label2id = {
"NEGATIVE": 0,
"POSITIVE": 1,
}
加载模型时传入:
model = AutoModelForSequenceClassification.from_pretrained(
model_id,
num_labels=2,
id2label=id2label,
label2id=label2id,
)
这样保存模型后,推理结果能够直接返回可读标签,而不是含义不明确的 LABEL_0。
19. 为什么分类头会提示“部分权重未初始化”
google-bert/bert-base-chinese 是预训练 Backbone,并不是已经完成当前二分类任务的模型。
当使用 AutoModelForSequenceClassification 加载时:
BERT Backbone 参数
<- 从预训练 Checkpoint 加载
新分类头参数
<- 随机初始化
因此出现类似“classifier weights were not initialized”的提示通常是正常的。它是在提醒:
新任务头还没有训练,必须微调后才能用于当前标签预测。
如果加载的是已经完成相同分类任务的 Checkpoint,却出现大量 Backbone 权重不匹配,则需要检查模型类和 Checkpoint 是否选错。
20. TrainingArguments 管理什么
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir="outputs/hf-chinese-sentiment",
learning_rate=2e-5,
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
num_train_epochs=3,
weight_decay=0.01,
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="accuracy",
save_total_limit=2,
logging_steps=1,
report_to="none",
seed=42,
)
常用参数含义:
| 参数 | 作用 |
|---|---|
output_dir |
Checkpoint 与训练输出目录 |
learning_rate |
优化器学习率 |
per_device_train_batch_size |
每个设备上的训练 batch size |
num_train_epochs |
遍历训练集次数 |
weight_decay |
权重衰减 |
eval_strategy |
何时评估 |
save_strategy |
何时保存 Checkpoint |
load_best_model_at_end |
训练后恢复最佳 Checkpoint |
metric_for_best_model |
选择最佳模型的指标 |
report_to |
实验记录后端;none 表示关闭 |
20.1 有效 batch size
如果有 $N$ 个设备,每个设备 batch size 为 $B$,梯度累积步数为 $G$,则每次参数更新对应的有效 batch size 约为:
$$
B_{effective}=N\times B\times G
$$
显存不足时,可以减小 per_device_train_batch_size,再使用:
gradient_accumulation_steps=4
但梯度累积不能消除单条超长序列带来的显存压力。
21. 评估函数 compute_metrics
二分类准确率可以直接实现,避免额外下载指标脚本:
import numpy as np
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
accuracy = (predictions == labels).mean()
return {"accuracy": float(accuracy)}
Trainer 在评估阶段会:
验证集 batch
-> 模型前向
-> 收集 logits 和 labels
-> 调用 compute_metrics
-> 得到 eval_accuracy
分类任务不应只看 Accuracy。类别不平衡时还应关注:
- Precision。
- Recall。
- F1。
- 混淆矩阵。
- 每一类的样本数。
22. 创建 Trainer
from transformers import Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset["train"],
eval_dataset=tokenized_dataset["validation"],
processing_class=tokenizer,
data_collator=data_collator,
compute_metrics=compute_metrics,
)
再执行:
trainer.train()
metrics = trainer.evaluate()
print(metrics)
22.1 Trainer 帮我们做了什么
它封装了典型训练循环:
DataLoader 取 batch
-> 移动到正确设备
-> model(**batch)
-> 读取 loss
-> backward
-> 梯度累积或裁剪
-> optimizer.step
-> learning rate scheduler.step
-> 清梯度
-> 日志、评估和保存
22.2 Trainer 会隐藏原理吗
工具会隐藏重复代码,但不应该隐藏理解。
至少应知道:
- batch 中有哪些键。
- 模型
forward()接收哪些参数。 labels如何产生 loss。- 优化器何时更新。
- 评估指标基于什么数据计算。
- 最佳模型按哪个指标选择。
理解这些以后,Trainer 是减少样板代码;不理解时,它就容易变成难以调试的黑箱。
23. 完整项目代码
完整脚本位于:
source/code/nlp-blog/huggingface_text_classification.py
运行:
python source/code/nlp-blog/huggingface_text_classification.py
第一次运行需要联网下载 google-bert/bert-base-chinese。下载大小、训练速度和显存占用取决于模型与环境。
脚本执行流程:
设置随机种子
-> 构造本地 DatasetDict
-> 加载 AutoTokenizer
-> dataset.map 批量分词
-> 创建 DataCollatorWithPadding
-> 加载带分类头的 BERT
-> 创建 TrainingArguments
-> 创建 Trainer
-> train + evaluate
-> save_pretrained
-> pipeline 从本地目录重新加载
-> 输出新文本预测
24. 保存模型、Tokenizer 与配置
final_dir = "outputs/hf-chinese-sentiment/final"
trainer.save_model(final_dir)
tokenizer.save_pretrained(final_dir)
保存后的目录通常包含:
final/
config.json
model.safetensors
tokenizer.json
tokenizer_config.json
special_tokens_map.json
vocab.txt
training_args.bin
24.1 为什么必须一起保存 Tokenizer
模型的 embedding 行号对应特定词表。只保存权重,不保存匹配的 Tokenizer,会让以后无法可靠复现输入 token id。
24.2 从本地目录重新加载
from transformers import pipeline
classifier = pipeline(
"text-classification",
model=final_dir,
tokenizer=final_dir,
)
print(classifier("这个键盘手感很好。"))
from_pretrained() 中的“pretrained”不意味着一定联网。它既可以接收 Hub ID,也可以接收本地目录。
25. Checkpoint 与最终模型的区别
训练中间目录可能包含:
checkpoint-10/
checkpoint-20/
它们常包含:
- 模型权重。
- 优化器状态。
- 学习率调度器状态。
- 随机数状态。
- Trainer 状态。
这些状态用于恢复训练。
最终推理目录通常只需要:
- 模型配置与权重。
- Tokenizer 或 Processor。
- 推理需要的生成配置。
- 标签映射和说明文档。
不要把“保存可推理模型”与“保存可无缝续训 Checkpoint”当成完全相同的需求。
26. 上传到 Hugging Face Hub
上传属于远程写操作,需要先创建账号和访问令牌。不要把令牌写进源代码或提交到 Git。
命令行登录:
hf auth login
使用 Trainer 上传:
trainer.push_to_hub(
commit_message="Fine-tune Chinese sentiment classifier"
)
也可以直接上传目录:
from huggingface_hub import HfApi
api = HfApi()
api.upload_folder(
folder_path=final_dir,
repo_id="your-name/chinese-sentiment-demo",
repo_type="model",
)
26.1 上传前应准备什么
至少应补充 Model Card:
模型用途
基础模型
训练数据来源与许可证
标签定义
训练超参数
评估方法与结果
局限、偏差与不适用场景
推理示例
本篇的小型手写数据只是教学样例,不适合直接发布为“可用于生产”的情感模型。
27. 下载缓存在哪里
默认情况下,Hub 文件会进入本地缓存。再次加载相同 revision 时,可以复用已有文件而不重复下载。
查看环境变量:
import os
print(os.getenv("HF_HOME"))
print(os.getenv("HF_HUB_CACHE"))
可以在运行前指定缓存根目录:
PowerShell:
$env:HF_HOME = "D:\hf-cache"
或者为单次加载指定:
tokenizer = AutoTokenizer.from_pretrained(
model_id,
cache_dir="D:/hf-cache",
)
27.1 离线模式
文件已经完整缓存后,可以启用离线模式:
$env:HF_HUB_OFFLINE = "1"
或在代码中要求只读取本地文件:
model = AutoModelForSequenceClassification.from_pretrained(
model_id,
local_files_only=True,
)
如果缓存缺少必要文件,离线加载仍然会失败。
28. CPU、GPU、dtype 与 device_map
28.1 普通小模型
手动推理时:
device = torch.device(
"cuda" if torch.cuda.is_available() else "cpu"
)
model.to(device)
inputs = {name: value.to(device) for name, value in inputs.items()}
模型和输入必须位于同一设备。
28.2 pipeline 指定 GPU
classifier = pipeline(
"text-classification",
model=model_id,
device=0,
)
device=0 通常表示第一张 CUDA GPU,device=-1 表示 CPU。
28.3 大模型自动分配
对于较大模型,可以结合 Accelerate:
model = AutoModelForCausalLM.from_pretrained(
model_id,
dtype="auto",
device_map="auto",
)
但这不表示任何模型都能塞进有限显存。仍需考虑:
- 权重占用。
- KV Cache。
- 激活值。
- batch size 与序列长度。
- 量化精度。
29. 文本生成:从 pipeline 到 generate
29.1 使用 pipeline
from transformers import pipeline
generator = pipeline(
"text-generation",
model="openai-community/gpt2",
)
result = generator(
"Machine learning is",
max_new_tokens=30,
do_sample=True,
temperature=0.8,
top_p=0.9,
)
print(result[0]["generated_text"])
这里使用英文 Prompt,是因为 GPT-2 的训练语言与 Tokenizer 更适合英文。选模型时必须检查语言适配性。
29.2 手动调用 generate
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id)
inputs = tokenizer(
"Machine learning is",
return_tensors="pt",
)
output_ids = model.generate(
**inputs,
max_new_tokens=30,
do_sample=True,
temperature=0.8,
top_p=0.9,
)
text = tokenizer.decode(
output_ids[0],
skip_special_tokens=True,
)
print(text)
29.3 max_new_tokens 与 max_length
max_new_tokens
只限制新生成的 token 数
max_length
限制输入 token + 新 token 的总长度
控制生成长度时,max_new_tokens 通常更直观。
29.4 聊天模型需要 Chat Template
聊天模型往往不是简单拼接:
system + user + assistant
它们使用训练时约定的特殊格式。应优先调用对应 Tokenizer 的:
tokenizer.apply_chat_template(...)
格式不匹配时,即使权重正确,模型表现也可能明显下降。
30. Model Card 应该怎么看
选择模型时建议依次检查:
- Task:模型是否真的为当前任务训练。
- Language:语言和领域是否匹配。
- License:是否允许你的研究或商业用途。
- Base model:模型从哪个 Checkpoint 微调而来。
- Dataset:训练数据是否可靠、是否接近目标分布。
- Metrics:在哪个测试集、使用什么指标得到结果。
- Limitations:已知偏差、风险和失败场景。
- Files:权重格式、大小与必要配置是否完整。
- Revision:项目是否需要锁定提交哈希。
同一个排行榜分数不一定能迁移到你的数据。最可靠的方法仍然是在自己保留的测试集上评估。
31. 安全与可复现性
31.1 谨慎使用 trust_remote_code=True
有些自定义模型需要执行仓库中的 Python 代码:
model = AutoModel.from_pretrained(
model_id,
trust_remote_code=True,
)
这意味着你明确允许远程仓库代码在本机运行。使用前应:
- 检查仓库作者和代码。
- 锁定
revision到可信提交。 - 在隔离环境中测试。
- 不在含敏感凭据的环境中随意执行未知代码。
如果模型不需要该参数,就不要为了消除报错盲目开启。
31.2 锁定 revision
revision = "完整提交哈希"
tokenizer = AutoTokenizer.from_pretrained(
model_id,
revision=revision,
)
model = AutoModelForSequenceClassification.from_pretrained(
model_id,
revision=revision,
)
同一模型仓库的 main 分支可能更新。锁定 revision 有助于让未来运行读取相同文件。
31.3 不要泄露访问令牌
访问令牌不应:
- 写进 Python 文件。
- 写进 Notebook 输出。
- 提交到 Git。
- 放在公开日志或截图中。
应使用 Hugging Face CLI 登录、环境变量或受控的 Secret 管理系统。
32. Spaces 是什么
Spaces 用于托管机器学习演示或应用。常见选择包括:
- Gradio。
- Streamlit。
- 静态 HTML。
- Docker。
一个简单 Gradio 应用可能是:
import gradio as gr
from transformers import pipeline
classifier = pipeline(
"text-classification",
model="your-name/chinese-sentiment-demo",
)
def predict(text):
return classifier(text)
demo = gr.Interface(
fn=predict,
inputs="text",
outputs="json",
)
demo.launch()
需要区分:
Model Repo
保存模型资产
Space Repo
保存并运行应用
Inference Endpoint / Provider
提供托管推理能力
它们解决的问题不同,价格、硬件和可用性也可能不同。
33. 常见错误
33.1 模型与 Tokenizer 不匹配
tokenizer = AutoTokenizer.from_pretrained(model_a)
model = AutoModel.from_pretrained(model_b)
除非明确知道二者兼容,否则应从同一个仓库或同一套发布资产加载。
33.2 任务模型类选错
文本分类应优先使用:
AutoModelForSequenceClassification
只使用 AutoModel 不会自动产生类别 logits。
33.3 忘记 truncation=True
长文本超过模型支持长度时会报维度错误或索引错误。设置截断后还要评估信息损失。
33.4 在整个数据集上固定 Padding 到最大长度
这会制造大量无效 token。优先考虑 DataCollatorWithPadding 的动态 Padding。
33.5 把 logits 当概率
logits 不要求位于 $[0,1]$,总和也不要求为 1。推理展示概率时再调用 Softmax。
33.6 训练时先手动 Softmax
交叉熵应接收 logits。提前 Softmax 会改变数值性质,并与模型内置损失的预期不一致。
33.7 忘记 model.eval()
手动推理时应关闭 Dropout 等训练行为:
model.eval()
with torch.inference_mode():
outputs = model(**inputs)
pipeline 会处理常规推理模式,但手动调用时需要自己负责。
33.8 模型与输入不在同一设备
如果模型在 GPU、输入在 CPU,会出现 device mismatch。二者必须一起移动。
33.9 根据标签编号猜语义
始终检查:
model.config.id2label
并与 Model Card 和数据集标签说明对照。
33.10 使用测试集调超参数
正确职责是:
train 更新参数
validation 选择超参数和最佳 Checkpoint
test 最后一次无偏评估
33.11 小数据训练成功就宣称模型可用
训练 loss 下降只说明模型拟合了训练目标。还需要检查验证集、测试集、真实分布、偏差和失败样例。
33.12 盲目相信 device_map="auto"
它负责设备分配,不会自动解决所有显存、速度或量化问题,也不能替代容量估算。
33.13 直接执行未知远程代码
不要为了加载模型就无条件设置 trust_remote_code=True。先审查代码并锁定 revision。
33.14 只保存模型,遗漏 Tokenizer
最终发布目录应同时包含匹配的预处理配置和标签映射。
34. Hugging Face 没有替你解决什么
Hugging Face 能减少工程样板,但不会自动解决:
- 任务定义是否正确。
- 数据标签是否可靠。
- 训练集是否泄露测试信息。
- 许可证是否适合使用场景。
- 模型偏差是否可接受。
- 线上延迟和吞吐是否达标。
- 长文本截断是否丢失关键信息。
- 评估集是否代表真实用户分布。
模型调用变简单以后,数据、评估和安全反而更值得认真对待。
35. 推荐学习路线
可以按照下面顺序逐层深入:
1. 用 pipeline 跑通一个已有模型
2. 阅读该模型的 Model Card 和 config
3. 用 AutoTokenizer 查看 input_ids 与 attention_mask
4. 用 AutoModelFor... 手动完成前向传播
5. 理解 logits、Softmax 和标签映射
6. 用 Dataset.from_dict 构造小数据
7. 用 map 批量 Tokenize
8. 用 DataCollatorWithPadding 组成 batch
9. 用 Trainer 微调并评估
10. save_pretrained 后从本地重载
11. 加入独立测试集和完整指标
12. 再学习 Accelerate、PEFT、量化和部署
调试时优先打印:
print(dataset)
print(dataset.features)
print(tokenized_dataset["train"][0])
print(data_collator([tokenized_dataset["train"][0]]))
print(model.config)
print(model.config.id2label)
比起只盯着训练进度条,先确认数据字段、形状和标签含义往往更有效。
36. 总结
Hugging Face 的主线可以浓缩为:
Hub
-> 找到模型、数据集与说明
AutoTokenizer / AutoProcessor
-> 把原始输入转成模型张量
AutoModelFor...
-> 按任务加载正确架构和预训练权重
pipeline
-> 封装 preprocess、forward、postprocess
Datasets + Data Collator
-> 加载、批处理并整理训练数据
Trainer
-> 训练、评估、日志和 Checkpoint
save_pretrained / push_to_hub
-> 保存、复用与分享
需要真正掌握的核心包括:
- Hugging Face 是平台、库和约定组成的生态,不是单个模型。
- Hub ID 指向具体 Checkpoint,不等于模型架构名称。
- Tokenizer 必须与模型匹配。
pipeline是高级推理封装,不是新的神经网络结构。AutoModel返回 Backbone 表示,AutoModelFor...带任务头。- 分类模型输出 logits,概率需要通过 Softmax 得到。
Dataset.map负责样本预处理,Data Collator 负责组成动态 Padding batch。Trainer封装训练循环,但数据、损失、指标和标签含义仍需自己理解。- 模型保存时应连同 Tokenizer、配置与标签映射一起保存。
- Model Card、许可证、revision 和远程代码安全与模型效果同样重要。
最后,用一句话概括:
Hugging Face 把研究社区已有的模型资产变成统一、可复用的工程组件,而使用者仍然要为任务定义、数据质量、评估结论和安全边界负责。
完整项目代码:source/code/nlp-blog/huggingface_text_classification.py
参考资料
- Hugging Face Transformers Quickstart: https://huggingface.co/docs/transformers/quicktour
- Hugging Face Pipeline Guide: https://huggingface.co/docs/transformers/main/en/pipeline_tutorial
- Hugging Face AutoClass Guide: https://huggingface.co/docs/transformers/main/en/autoclass_tutorial
- Hugging Face Text Classification Guide: https://huggingface.co/docs/transformers/main/en/tasks/sequence_classification
- Hugging Face Padding and Truncation: https://huggingface.co/docs/transformers/main/en/pad_truncation
- Hugging Face Datasets Quickstart: https://huggingface.co/docs/datasets/main/en/quickstart
- Hugging Face Datasets Processing Guide: https://huggingface.co/docs/datasets/main/en/process
- Hugging Face Hub Model Cards: https://huggingface.co/docs/hub/en/model-cards
- Hugging Face Hub Cache Guide: https://huggingface.co/docs/huggingface_hub/en/guides/manage-cache
- Hugging Face Hub Upload Guide: https://huggingface.co/docs/huggingface_hub/en/guides/upload
