现在要造轮子了,就不得不深入学习一下huggingface的transformers和diffusers库了。本文主要讲如何利用huggingface的代码造论文字(定制自己的模型并集成发布),而不会过于关注如何调用huggingface的代码。
自定义模型(自定义的model代码还没被集成到transformers库中)时,除了上图中常见的config和权重文件,还有以下2步:
configuration_xxx.py # 继承PretrainedConfig
modeling_xxx.py # 继承PreTrainedModel
xxx_utils.py # 一些模型generation的工具函数/类 (可选)
tokenization_xxx.py # 继承PreTrainedTokenizer
当我们向transformers官方库PR
我们的Model相关的文件后,这些Model相关的文件就会存放transformers库的src中,我们的Model Hub即使没有这几个文件,也可以直接使用Autoxxx直接调用啦。
- 设置config配置文件, 将model代码与模型config和weights放到同一目录下发布: 让 transformers 调用自定义代码,设置方式如下:设置
config.json
和tokenizer_config.json
中的auto_map
参数。
// config.json
"auto_map": {
"AutoConfig": "configuration_xxx.XXXConfig",
"AutoModelForCausalLM": "modeling_xxx.XXXLMHeadModel"
},
// tokenizer_config.json
"auto_map": {
"AutoTokenizer": [
"tokenization_xxx.XTokXXenizer",
null
]
},
接下来我们将按照下面几部分展开如何造轮子:
PretrainedConfig : config 的参数类型
PreTrainedTokenizer : tokenizer 的参数类型
PreTrainedModel : model 的参数类型
Accelerator : 通用训练器
Trainer : LLM/MLLM训练器
TrainingArguments : 训练参数的集合
Data Collator : 数据batch填充(padding)或截断(truncation)
1. Transformers库
1.1 Model相关
PretrainedConfig
如果要造轮子,你的模型的Config类就要继承PretrainedConfig基类(不能直接实例化基类PretrainedConfig。使用时,需要再model_path
路径下(hub权重仓库),将config保存为一个config.json
的文件,这样以后使用的时候就可以通过from_pretrained
函数加载了。
config.json
文件包含了用于配置预训练模型参数的选项,不同的模型类型和架构可能会有不同的选项。下面是config.json中的一些常见参数:
auto_map
:造轮子最重要的一个参数,总的来说,AutoModel/AutoTokenizer
等Auto类的加载,其实是去读取了config.json/tokenizer_config.json
文件中的automap
,其中会指定对应结构类地址名称
。
architectures: 模型所使用的架构。
attention_probs_dropout_prob: 注意力矩阵中的dropout概率。
auto_map: {
"AutoConfig": "configuration_xxx.XXXConfig",
"AutoModelForCausalLM": "modeling_xxx.XXXModelLM",
"AutoModel": "modeling_xxx.XXXModelLM"
},
bos_token_id: 开始标记的ID。
decoder_start_token_id: 解码器的起始标记ID。
dropout: Dropout的概率。
eos_token_id: 终止标记的ID。
hidden_act: 激活函数的类型,如gelu、relu等等。
hidden_dropout_prob: 隐藏层中的dropout概率。
hidden_size: 模型隐藏层的大小。
initializer_range: 参数初始化的范围。
intermediate_size: 每个隐藏层中间层的大小。
is_decoder: 是否为解码器。
is_encoder_decoder: 是否是编码器-解码器架构。
layer_norm_eps: LayerNorm层中epsilon的值。
n_head: 头部的数量。
n_layers: 模型中的总层数。
num_attention_heads: 每个隐藏层中的自注意头的数量。
num_hidden_layers: 模型的隐藏层数量。
pad_token_id: 填充标记的ID。
tie_word_embeddings: 是否将编码器和解码器的词嵌入层绑定。
tokenizer_class: 使用的分词器的类。
transformer_type: Transformer模型的类型。
transformers_version: Transformers库的版本号。
type_vocab_size: 类型词汇表的大小。
use_cache: 是否使用缓存。
vocab_size: 词汇表的大小。
对于 PretrainedModel
初始化提供的参数是 PretrainedConfig
类型的参数。它主要为不同的任务,提供了不同的重要参数。主要实现了从本地文件或目录或从库提供的预训练模型config(从 HuggingFace 的 AWS S3 存储库下载)加载/保存config参数的常用方法。
- 所有Config类中都存在的通用属性是:
hidden_size
、num_attention_heads
和num_hidden_layers
。文本模型进一步实现:vocab_size
。
完整参数如下:
通用参数
vocab_size (int) — The number of tokens in the vocabulary, which is also the first dimension of the embeddings matrix (this attribute may be missing for models that don’t have a text modality like ViT).
hidden_size (int) — The hidden size of the model.
num_attention_heads (int) — The number of attention heads used in the multi-head attention layers of the model.
num_hidden_layers (int) — The number of blocks in the model.
序列生成相关
max_length (int, optional, defaults to 20) — Maximum length that will be used by default in the generate method of the model.
min_length (int, optional, defaults to 0) — Minimum length that will be used by default in the generate method of the model.
do_sample (bool, optional, defaults to False) — Flag that will be used by default in the generate method of the model. Whether or not to use sampling ; use greedy decoding otherwise.
num_beams (int, optional, defaults to 1) — Number of beams for beam search that will be used by default in the generate method of the model. 1 means no beam search.
diversity_penalty (float, optional, defaults to 0.0) — Value to control diversity for group beam search. that will be used by default in the generate method of the model. 0 means no diversity penalty. The higher the penalty, the more diverse are the outputs.
temperature (float, optional, defaults to 1.0) — The value used to module the next token probabilities that will be used by default in the generate method of the model. Must be strictly positive.
top_k (int, optional, defaults to 50) — Number of highest probability vocabulary tokens to keep for top-k-filtering that will be used by default in the generate method of the model.
top_p (float, optional, defaults to 1) — Value that will be used by default in the generate method of the model for top_p. If set to float < 1, only the most probable tokens with probabilities that add up to top_p or higher are kept for generation.
epetition_penalty (float, optional, defaults to 1) — Parameter for repetition penalty that will be used by default in the generate method of the model. 1.0 means no penalty.
length_penalty (float, optional, defaults to 1) — Exponential penalty to the length that is used with beam-based generation. It is applied as an exponent to the sequence length, which in turn is used to divide the score of the sequence. Since the score is the log likelihood of the sequence (i.e. negative), length_penalty > 0.0 promotes longer sequences, while length_penalty < 0.0 encourages shorter sequences.
bad_words_ids (List[int], optional) — List of token ids that are not allowed to be generated that will be used by default in the generate method of the model. In order to get the tokens of the words that should not appear in the generated text, use tokenizer.encode(bad_word, add_prefix_space=True).
tokenizer相关
bos_token_id (int, optional) — The id of the beginning-of-stream token.
pad_token_id (int, optional) — The id of the padding token.
eos_token_id (int, optional) — The id of the end-of-stream token.
PyTorch相关
tie_word_embeddings (bool,可选,默认为True)— 是否应绑定模型的输入和输出词嵌入。请注意,这仅在模型具有输出词嵌入层时才相关。
torch_dtype (str, optional) — The dtype of the weights. This attribute can be used to initialize the model to a non-default dtype (which is normally float32) and thus allow for optimal storage allocation. For example, if the saved model is float16, ideally we want to load it back using the minimal amount of memory needed to load float16 weights. Since the config object is stored in plain text, this attribute contains just the floating type string without the torch. prefix. For example, for torch.float16 `torch_dtype is the "float16" string.
PretrainedConfig 中重要的方法:
- push_to_hub:依然是上传到 HF hub
- from_dict:把一个 dict 类型转到 PretrainedConfig 类型
- from_json_file:把一个 json 文件转到 PretrainedConfig 类型,传入的是文件路径
- to_dict:转成 dict 类型
- to_json_file:保存到 json 文件
- to_json_string:转成 json 字符串
from_pretrained
:从预训练模型配置文件中直接获取配置(可以是HF模型,也可以是本地模型), 原理是就是从config.json
中加载config。trust_remote_code
:默认情况下,trust_remote_code 设置为 True,这意味着将下载来自 Hugging Face 模型中心的在线资源的配置文件,通常这些配置文件是由官方提供的,且是可信的。当设置为False时,则表示您不信任从huggingface远程下载的配置文件,希望加载本地的配置文件。就需要提供一个本地文件路径,以明确指定要加载的配置文件。
PreTrainedTokenizer(Fast)
造轮子通常直接复用前人训练好的BPE Tokenizer,没有太多要改的地方,唯一可能要变动的就是增加一些vocabulary中不存在的新token,并resize模型的embedding层权重。
两个基类PreTrainedTokenizer
和PreTrainedTokenizerFast
,都依赖于 包含通用方法的 PreTrainedTokenizerBase
和SpecialTokensMixin
,Tokenizer类的3个关键功能:
- Tokenizing (分词), converting tokens strings to ids and back, and encoding/decoding (str和input_ids的转换).
- Adding new tokens to the vocabulary 且不依赖底层算法 (如BPE, SentencePiece…).
- Managing special tokens (如
mask
,beginning-of-sentence
,end-of-sequence
,padding-token
,image-token (llava中的image占位符)
, etc.) 添加它们到tokenizer的属性中,以便于访问,并确保它们在tokenizing的过程中不会被拆分。
PreTrainedTokenizer是从tokenizer_config.json
, tokenizer.json
, special_tokens_map.json
这三个文件中加载,加载流程:1. 优先加载tokenizer.json来获取分词规则和词表。2. 使用tokenizer_config.json设置额外的配置参数。3. 如果存在special_tokens_map.json,会根据该文件映射特殊标记。(要区分的3个东西: 字段=bos_token
、符号=<|startoftext|>
、TokenID=126080
)
tokenizer.json
:定义了词表和分词规则(BPE等),是Tokenizer的核心文件。包含 vocab(每个词汇对应一个唯一的token ID),merges(如果是基于BPE,会有合并的词汇及其唯一token ID),special_tokens(特殊标记,如<pad>
、<unk>
、<eos>
等)。tokenizer_config.json
:定义了Tokenzier的配置,比如是否使用special token、分词方式等。包含do_lower_case(是否将文本转为小写),bos_token / eos_token(开始和结束标记),pad_token(填充标记)等。special_tokens_map.json
:映射特殊token(如<pad>
、<unk>
)到词汇表中的实际标记。包含 pad_token:(<pad>
)、unk_token(<unk>
)、cls_token(<cls>
)。
PreTrainedTokenizer 中比较重要的几个参数:
bos_token (str or tokenizers.AddedToken, optional) — A special token representing the beginning of a sentence. Will be associated to self.bos_token and self.bos_token_id.
eos_token (str or tokenizers.AddedToken, optional) — A special token representing the end of a sentence. Will be associated to self.eos_token and self.eos_token_id.
unk_token (str or tokenizers.AddedToken, optional) — A special token representing an out-of-vocabulary token. Will be associated to self.unk_token and self.unk_token_id.
sep_token (str or tokenizers.AddedToken, optional) — A special token separating two different sentences in the same input (used by BERT for instance). Will be associated to self.sep_token and self.sep_token_id.
pad_token (str or tokenizers.AddedToken, optional) — A special token used to make arrays of tokens the same size for batching purpose. Will then be ignored by attention mechanisms or loss computation. Will be associated to self.pad_token and self.pad_token_id.
cls_token (str or tokenizers.AddedToken, optional) — A special token representing the class of the input (used by BERT for instance). Will be associated to self.cls_token and self.cls_token_id.
mask_token (str or tokenizers.AddedToken, optional) — A special token representing a masked token (used by masked-language modeling pretraining objectives, like BERT). Will be associated to self.mask_token and self.mask_token_id.
PreTrainedTokenizer 中重要的方法:
PreTrainedTokenizer类也可以直接从tokenizer.json
加载所有的token和id的映射
。
-
add_tokens
:输入str的token list
,添加一些新的token。注意,添加新token需要使用resize_token_embeddings
确保 token embedding矩阵与tokenizer是匹配的。 -
add_special_tokens
:输入special_tokens的dict
,添加特殊tokens,比如之前的eos
,pad
等,与之前普通的tokens是不大一样的,但要确保该token不在词汇表里。 -
encode
和decode
:string
转input_ids数组
,input_ids数组
转string
。(encode 和 convert_tokens_to_ids(tokenize(text)) 等价,decode 和 convert_tokens_to_string(convert_ids_to_tokens(token_ids)) 等价) -
tokenize
:把string转成input_ids序列,即分词 str → list[str]。 -
tokenzier.add_token() 、 add_special_token()有什么区别? 从让transformers模型新增token角度,这两都可以实现,没有区别。都是将特殊token加入模型词汇表vocab后可以防止tokenzier在切词时将该[token]切开,在文本编码时保留该[token]为一个整体。
# Let's see how to increase the vocabulary of Bert model and tokenizer
tokenizer = BertTokenizerFast.from_pretrained("google-bert/bert-base-uncased")
model = BertModel.from_pretrained("google-bert/bert-base-uncased")
num_added_toks = tokenizer.add_tokens(["new_tok1", "my_new-tok2"])
print("We have added", num_added_toks, "tokens")
# 注意:resize_token_embeddings 期望接收新词汇表的完整大小,即分词器的长度。
model.resize_token_embeddings(len(tokenizer))
# Let's see how to add a new classification token to GPT-2
tokenizer = GPT2Tokenizer.from_pretrained("openai-community/gpt2")
model = GPT2Model.from_pretrained("openai-community/gpt2")
special_tokens_dict = {"cls_token": "<CLS>"}
num_added_toks = tokenizer.add_special_tokens(special_tokens_dict)
print("We have added", num_added_toks, "tokens")
# 注意:resize_token_embeddings 期望接收新词汇表的完整大小,即分词器的长度。
model.resize_token_embeddings(len(tokenizer))
assert tokenizer.cls_token == "<CLS>"
PreTrainedModel
PretrainedModel主要负责管理模型的配置,模型的参数加载、下载和保存。PretrainedModel继承自 nn.Module
, ModuleUtilsMixin
, GenerationMixin
, PushToHubMixin
, PeftAdapterMixin
。Transformers的大部分模型都会继承PretrainedModel基类,在初始化时需要提供给它一个 config: PretrainedConfig
。
PreTrainedModel 中重要的方法:
- push_to_hub:将模型传到HF hub
- get_input_embeddings:获得输入的词嵌入
- init_weights:设置参数初始化,如果需要自己调整参数初始化的,在 _init_weights ,_initialize_weights 中设置。
- save_pretrained:把模型和配置参数保存在文件夹中
保存完后,便可以通过 from_pretrained 再次加载模型了 - from_pretrained :加载预训练的基础模型。在加载时,它会调用模型路径中的一系列文件。
使用的配置文件加载流程:1. 解析config.json,构建基础模型的结构。2. 加载权重文件(safetensors或.bin格式)到模型中。3. 如果有分片,则根据index.json加载完整的权重。
config.json
:定义模型的结构配置,例如层数、隐藏单元数量、注意力头数等。常见字段:
hidden_size: 隐藏层的大小。
num_attention_heads: 注意力头的数量。
num_hidden_layers: 模型的层数。
max_position_embeddings: 最大序列长度。
generation_config.json
:定义生成任务的配置,例如生成文本时的最大长度、温度等。常见字段:
max_length: 生成文本的最大长度。
temperature: 生成时的多样性控制参数。
top_k/top_p: 生成时的采样策略。
-
model.safetensors
或model-xxxx-of-xxxx.safetensors
:保存模型的权重。权重文件可能被分片存储,文件名中会标注分片信息,例如:model-00001-of-00004.safetensors: 表示这是四个权重分片中的第一个。 -
model.safetensors.index.json
:如果权重文件被分片存储,该文件记录了每个分片的索引和分布信息。
1.2 Training相关
TrainingArguments
Huggingface的各种Arguments类都使用了@dataclass
装饰,主要用于存储数据的类自动为类生成 __init__
、__repr__
、__eq__
等方法,从而减少样板代码:
field
是 dataclasses 模块中的一个函数,用于定义数据类中的变量。它允许你为变量指定default默认值
、type类型
、metadata元数据
等信息。
from dataclasses import dataclass, field
@dataclass
class TrainingArguments:
do_train: bool = field(default=False, metadata={"help": "Whether to run training."})
do_eval: bool = field(default=False, metadata={"help": "Whether to run eval on the dev set."})
do_predict: bool = field(default=False, metadata={"help": "Whether to run predictions on the test set."})
train_args = TrainingArguments(do_train=true)
print(train_args.do_train)