Huggingface Transformers
在 transformers 中,每个模型都需要三个核心 class:
- Configuration class
- Model class
- Preprocessing class
Config
PretrainedConfig
Base class for all configuration classes
|
|
LlamaConfig
|
|
|
|
ModelOutput
ModelOutput 是所有模型输出的基类。下面是其源码核心部分,可以看到 ModelOutput 其实就是一个有序的字典(OrderedDict)。
|
|
基于 ModelOutput,hf 预先定义了 40 多种不同的 sub-class,这些类是 Hugging Face Transformers 库中用于表示不同类型模型输出的基础类,每个类都提供了特定类型模型输出的结构和信息,以便于在实际任务中对模型输出进行处理和使用。每个 sub-class 都需要用装饰器 @dataclass。
CausalLMOutput
CausalLMOutput 是一个基础的输出类,通常在训练阶段或不需要缓存(cache)进行快速解码的推理场景中使用。它包含了模型前向传播后的核心结果:
loss: 当你传入labels(即正确答案)时,模型会计算并返回语言建模的损失值。这是模型训练所必需的。logits: 模型对词汇表中每个词的原始预测分数。在生成文本时,通常会通过 argmax 或采样(sampling)等策略从logits中选择下一个词元。hidden_states: (可选) 模型所有层的隐藏状态。这对于模型分析和特征提取很有用。attentions: (可选) 模型所有注意力层的注意力权重。这可以用于理解模型在不同词元上的关注程度。
|
|
CausalLMOutputWithPast
CausalLMOutputWithPast 继承了 CausalLMOutput 的所有属性,并额外增加了一个关键属性:
past_key_values: 这是它与CausalLMOutput最核心的区别。该属性用于存储注意力机制中的键(Key)和值(Value)的状态。在自回归生成(如生成一篇文章)的推理过程中,模型每生成一个新词元,就可以利用之前所有词元的past_key_values,而无需重新计算它们,从而极大地提升生成速度。这个机制通常被称为 “KV 缓存”。
|
|
这里的参数:
- loss 为
torch.FloatTensorof shape(1,) - logits 为
torch.FloatTensorof shape(batch_size, sequence_length, config.vocab_size)
为了保持代码规范,我们需要在模型的 forward 函数中对输出结果进行封装,示例如下:
|
|
MoEModelOutput
这是 MoE 模型 基础输出 的基类。它代表了 MoE Transformer 模型(不带任何特定任务头)的裸输出。
last_hidden_state:torch.FloatTensor,维度是(batch_size, sequence_length, hidden_size)。- 这是模型最后一层输出的隐藏状态序列。这是 Transformer 模型最核心的输出。
hidden_states: (可选)tuple(torch.FloatTensor)。- 包含了模型所有层的隐藏状态,以及初始的词嵌入向量。如果你在模型配置中启用了
output_hidden_states=True,这个字段才会被返回。 - 每一层的输出 shape 为
(batch_size, sequence_length, hidden_size)
- 包含了模型所有层的隐藏状态,以及初始的词嵌入向量。如果你在模型配置中启用了
attentions: (可选)tuple(torch.FloatTensor)。- 包含了模型所有注意力层的注意力权重。如果你在模型配置中启用了
output_attentions=True,这个字段才会被返回。 - 每一层的 shape 为
(batch_size, num_heads, sequence_length, sequence_length)
- 包含了模型所有注意力层的注意力权重。如果你在模型配置中启用了
router_probs/router_logits: (可选)tuple(torch.FloatTensor)。- 这是 MoE 模型特有的输出。它包含了每一层 MoE 模块中“路由器”(Router)的输出。路由器决定了每个词元(token)应该被发送到哪个专家(expert)去处理。
- 这些值对于计算 MoE 模型的“辅助损失”(Auxiliary Loss)至关重要。这个损失函数用来确保所有专家被均匀地使用,避免某些专家过载而另一些专家空闲。
- (注意:文档中
router_probs和router_logits的描述稍有混淆,但你可以理解为它们都是指路由器的原始输出,用于计算辅助损失。)
|
|
MoeModelOutputWithPast
这个类继承自 MoEModelOutput 的概念,并额外增加了一个用于 自回归生成(autoregressive generation) 的关键部分。
它包含了 MoEModelOutput 的所有字段,并增加了:
past_key_values: (可选)Cache对象。- 这就是我们常说的 KV 缓存。在生成文本时,为了提高效率,模型会缓存已经计算过的 Key 和 Value 向量。这样,在生成下一个词元时,就不需要重新计算前面所有词元的 Key-Value,从而大大加快了生成速度。
- 只有在调用模型时设置了
use_cache=True,这个字段才会被返回。
|
|
MoECausalLMOutputWithPast
MoECausalLMOutputWithPast 是最具体的一个类,专门用于 带有因果语言模型头(Causal Language Modeling Head)的 MoE 模型。当你使用像 Mixtral 这样的模型进行文本生成或微调时,你通常会直接接触到这个输出对象。
它包含了 MoeModelOutputWithPast 的所有字段,并增加了针对特定任务的输出:
loss: (可选)torch.FloatTensor,维度是(1,)。- 语言模型损失。只有当你向模型提供了
labels(即正确答案)时,这个字段才会被计算并返回。它通常是交叉熵损失(Cross-Entropy Loss),用于衡量模型预测的下一个词元与真实词元之间的差距。
- 语言模型损失。只有当你向模型提供了
logits:torch.FloatTensor,维度是(batch_size, sequence_length, config.vocab_size)。- 预测分数。这是语言模型头的原始输出,在经过 Softmax 激活函数之前。它为词汇表中的每个词元都提供了一个分数,分数越高的词元,模型认为它越有可能成为下一个词元。
aux_loss: (可选)torch.FloatTensor。- 辅助损失。这就是前面提到的,根据
router_logits计算出的 MoE 负载均衡损失。在训练过程中,这个损失会与主要的loss相加,共同指导模型的优化。
- 辅助损失。这就是前面提到的,根据
|
|
Seq2SeqModelOutput
|
|
TokenClassifierOutput
|
|
这里简单介绍以下几种,更多的可以查看官方文档和源码:
BaseModelOutput: 该类是许多基本模型输出的基础,包含模型的一般输出,如 logits、hidden_states 等。BaseModelOutputWithNoAttention: 在模型输出中不包含注意力(attention)信息。BaseModelOutputWithPast: 包含过去隐藏状态的模型输出,适用于能够迭代生成文本的模型,例如语言模型。BaseModelOutputWithCrossAttentions: 在模型输出中包含交叉注意力(cross attentions)信息,通常用于特定任务中需要跨注意力的情况,比如机器翻译。BaseModelOutputWithPastAndCrossAttentions: 同时包含过去隐藏状态和交叉注意力的模型输出。MoEModelOutput: 包含混合专家模型(Mixture of Experts)输出的模型。MoECausalLMOutputWithPast: 混合专家语言模型的输出,包括过去隐藏状态。Seq2SeqModelOutput: 序列到序列模型输出的基类,适用于需要生成序列的模型。
Model
PreTrainedModel
PreTrainedModel 是 Hugging Face Transformers 库中定义预训练模型的基类。它继承了 nn.Module,同时混合了几个不同的 mixin 类,如 ModuleUtilsMixin、GenerationMixin、PushToHubMixin 和 PeftAdapterMixin。这个基类提供了创建和定义预训练模型所需的核心功能和属性。
以下是 PreTrainedModel 中的部分代码:
|
|
在这个基类中,我们可以看到一些重要的属性和方法:
config_class:指向特定预训练模型类的配置文件,用于定义模型的配置。base_model_prefix:基本模型前缀,在模型的命名中使用,例如在加载预训练模型的权重时使用。main_input_name:指定模型的主要输入名称,通常是 input_ids。_init_weights方法:用于初始化模型权重的方法。
在这个基类中,大多数属性都被定义为 None 或空字符串,这些属性在具体的预训练模型类中会被重写或填充。接下来我们将看到如何使用 PretrainedModel 类定义 llama 模型。
LlamaPreTrainedModel
|
|
在这个例子中,首先定义了 LlamaPreTrainedModel 类作为 llama 模型的基类,它继承自 PreTrainedModel。在这个基类中,我们指定了一些 llama 模型特有的属性,比如配置类 LlamaConfig、模型前缀 model、支持梯度检查点(gradient checkpointing)、跳过的模块列表 _no_split_modules 等等。
然后,我们基于这个基类分别定义了 LlamaModel、LlamaForCausalLM 和 LlamaForSequenceClassification。这些模型的逻辑关系如下图所示:
LlamaModel
LlamaModel 是 llama 模型的主体定义类,也就是我们最常见的 pytorch 定义模型的方法、默认的输出格式为 BaseModelOutputWithPast。这里的 LlamaModel 是不带 LMHead 的,输出最后一层的 hidden states。
|
|
LlamaForCausalLM
LlamaForCausalLM 适用于生成式语言模型的 llama 模型,可以看到 backbone 就是 LlamaModel,增加了 lm_head 作为分类器,输出长度为词汇表的大小,用来预测下一个单词。输出格式为 CausalLMOutputWithPast;
|
|
LlamaForSequenceClassification
LlamaForSequenceClassification 适用于序列分类任务的 llama 模型,同样把 LlamaModel 作为 backbone,不过增加了 score 作为分类器,输出长度为 label 的数量,用来预测类别。输出格式为 SequenceClassifierOutputWithPast
|
|
对应的 GenericForSequenceClassification 实现如下:
|
|
LlamaForTokenClassification
|
|
对应的 GenericForTokenClassification 实现如下
|
|
每个子类根据特定的任务或应用场景进行了定制,以满足不同任务的需求。另外可以看到 hf 定义的模型都是由传入的 config 参数定义的,所以不同模型对应不同的配置,这也是为什么我们经常能看到有像 BertConfig,GPTConfig 这些预先定义好的类。例如我们可以很方便地通过指定的字符串或者文件获取和修改不同的参数配置:
|
|
Tokenizer
|
|
Tokenize 编码
tokenize 方法可以将一个自然语言字符串,转换成对应的 tokens
|
|
encode/decode
可以通过 encode() 函数将这两个步骤合并, decode() 函数则对应相反操作。
|
|
Tokenizer 直接处理
一般情况下,会直接通过 tokenizer 处理输入 sequences,如下所示,返回的 output 包括了 input_ids 和 attention_mask 等
|
|
tokenizer 有很多可以控制的参数:
- return_tensors=“pt”,指定返回张量类型,可选"pt"(PyTorch)、“tf”(TensorFlow)、“np”(NumPy),避免手动转换格式
- return_attention_mask: 返回注意力掩码(
1表示有效 Token,0表示[PAD]),默认True,模型靠它忽略填充部分
|
|
上面的例子一次输入一个 sample,可以同时输入多个 sample 给 tokenizer,这个时候则需要考虑 batch 对齐的相关参数
- max_length: 设定序列的最大长度(包括特殊符号),默认是模型的最大支持长度(如 BERT 为 512,GPT2 为 1024)。超过这个长度会截断,不足则填充。
- padding: 控制如何填充短文本:
- False(默认):不填充,输出长度不统一,无法批量输入模型;
- True/“longest”:填充到批次中最长序列的长度,节省计算资源;
- “max_length”:强制填充到 max_length,适合需要固定输入长度的场景(如文本分类)。
- truncation: 控制是否截断长文本,默认False(超过max_length会报错)。
|
|
输出为,可以看到对应的 input_ids 和 attention_mask
|
|
Apply_chat_template
对于 chat 模型而言,给到模型的数据往往是 chat 格式的,每个消息都包含一个 role 及其 content ,其内容是消息的实际文本:
|
|
返回的结果也是一个字符串,其中增加了很多 special tokens
<|im_start|>表示 messages 内一条消息的开始,这里没有写 system messages,执行 apply_chat_template 之后默认返回了 Qwen 的 system message<|im_end|>表示本消息的结束- 接下来是 user message
- 因为在 apply_chat_template 设置了 add_generation_prompt=True,因此还增加了
<|im_start|>assistant告诉模型此时应该由模型开始输出
|
|
实际推理例子
|
|
Processors
早期 Transformers:
- NLP:用 Tokenizer
- Vision:用 FeatureExtractor
- Audio:用 FeatureExtractor
- 多模态:用户手动拼接 问题:
- 接口碎片化
- 多模态模型难统一
- pipeline 使用复杂
Transformers 库中的 Processors 定位为多模态统一预处理入口,通常封装了:
- tokenizer: 文本
- image processor:图像
- video processor:视频
- feature extractor:音频等
|
|
Processors 本质是多模态预处理的整合器,通常会:
- 对文本:调用
Tokenizer进行分词、编码 - 对图像 / 视频:调用
ImageProcessor/VideoProcessor进行 resize、归一化等操作 - 最终将多模态输出整合为模型输入字典(如
input_ids、pixel_values等)
|
|
ProcessorMixin 是 HuggingFace Transformers 库中用于多模态模型的核心基类,它的作用是将多个处理器 (如 ImageProcessor 和 Tokenizer)封装成一个统一的接口。
|
|
ImageProcessor
专门处理图像数据,核心操作包括:
- 调整尺寸(resize)、中心裁剪(center crop);
- 归一化(normalize,基于 ImageNet 均值 / 标准差);
- 转换为张量(to tensor)。
|
|
图像处理器继承自 BaseImageProcessor 类,该类提供了 center_crop()、normalize() 和 rescale() 函数。图像处理器有两种类型。
- BaseImageProcessor是一个 Python 实现。
- BaseImageProcessorFast是一个速度更快的torchvision版本。对于一批torch.Tensor输入,它的速度最高可提升 33 倍。目前并非所有视觉模型都支持BaseImageProcessorFast 。请参阅模型 API 文档以确认其是否受支持。
每个图像处理器都继承了ImageProcessingMixin类,该类提供了from_pretrained()和save_pretrained()方法,用于加载和保存图像处理器。
加载图像处理器有两种方法:使用 AutoImageProcessor 或特定型号的图像处理器。
VideoProcessor
专门处理视频数据,核心逻辑:
- 采样视频帧(如均匀采样 16 帧);
- 对每一帧应用
ImageProcessor的操作; - 组合为视频张量(形状
[batch_size, num_frames, channels, height, width])。
|
|
Auto Classes
在很多情况下,你想要使用的架构可以从你提供给 from_pretrained() 方法的预训练模型的名称或路径中推测出来,AutoClasses 的作用就是为你完成这项工作。
比如,可以直接通过下列的方法加载模型
|
|
如果想在 AutoModel 注册一个新模型,需要定义好 NewModel 和 NewModelConfig,然后利用 AutoModel 和 AutoConfig 的 register 方法注册就可以。
|
|
一般来说,这里的 NewModelConfig 是 PretrainedConfig 的子类,NewModel 是 PretrainedModel 的子类。
AutoConfig
|
|
AutoTokenizer
| 类名 | 描述 | 适用任务 |
|---|---|---|
| AutoModel | 加载预训练的基础模型,不包含任何任务特定的头部。 | 特征提取、嵌入生成、自定义任务等 |
| AutoModelForCausalLM | 加载带有因果语言建模头部的模型,适用于生成任务。 | 文本生成、对话系统、自动补全等 |
| AutoModelForMaskedLM | 加载带有掩码语言建模头部的模型,适用于填空任务。 | 填空任务、句子补全、文本理解等 |
| AutoModelForSeq 2 SeqLM | 加载适用于序列到序列任务的模型,带有编码器-解码器架构。 | 机器翻译、文本摘要、问答系统等 |
| AutoModelForQuestionAnswering | 加载适用于问答任务的模型,带有专门的头部用于预测答案的起始和结束位置。 | 问答系统、信息检索等 |
| AutoModelForTokenClassification | 加载用于标注任务(如命名实体识别)的模型。 | 命名实体识别、词性标注等 |
| AutoModelForSequenceClassification | 加载用于序列分类任务的模型,带有分类头部。 | 文本分类、情感分析等 |
AutoModelForCausalLM
|
|
AutoModelForTokenClassification
参考资料
Author Houmin Wei
Publish February 25, 2023
LastMod August 27, 2026
License 本作品采用 CC BY-NC-ND 4.0 许可协议进行许可,转载时请注明原文链接
如果你在浏览博客的过程中发现了任何问题,欢迎在对应文章下评论。如果你有其他事情想要咨询,可以通过邮件联系我。