Hugging Face
Hugging Face的model hubHugging Face的model hub是一个汇聚了海量预训练机器学习模型的中央仓库可以把它理解为一个AI模型的“应用商店”一、Automodel加载模型下面是一个模型的代码结构实例以它为例我们来说明如何从Hugging Face配置预训练好的大模型上面是MiniCPM-Llama3-V-2_5 / config.json这里有两个特别说明1.architectures: [MiniCPMV]architectures一个身份标签是一个类名列表通常只有一项定义了模型的架构名称作用这是一个声明告诉使用者或HF框架“我这个模型在逻辑上属于MiniCPMV这个类型。”局限性它只提供了名字。如果HF官方库内置了这个名字比如LlamaForCausalLM框架就能自动找到代码。但如果这个名字是自定义的如这里的MiniCPMVHF的官方库根本不知道这个类长什么样光看名字会报错。2. auto_map: {AutoConfig: configuration_minicpm.MiniCPMVConfig,AutoModel: modeling_minicpmv.MiniCPMV,AutoModelForCausalLM: modeling_minicpmv.MiniCPMV}auto_map具体地址/地图是一个映射字典告诉 HF 框架“如果你要加载我这个模型请去指定的 Python 文件里寻找对应的类。”AutoConfig指向配置文件configuration_minicpm.py中的配置类。AutoModel和AutoModelForCausalLM指向模型主文件modeling_minicpmv.py中的模型类。举个例子用下面的代码加载这个模型from transformers import AutoModel # 你只给了模型文件夹路径 model AutoModel.from_pretrained(./my_minicpm_folder)此时程序内部发生了什么呢我们按步骤拆解第 1 步读取config.json程序打开文件夹读取了这个配置文件。第 2 步查询auto_map优先执行程序发现配置里有auto_map字段并且包含了AutoModel: modeling_minicpmv.MiniCPMV。路径解析modeling_minicpmv.MiniCPMV表示“去当前文件夹下的modeling_minicpmv.py这个文件中找到名为MiniCPMV的类”。实际执行HF 框架会动态导入这个 Python 文件并提取出MiniCPMV类。第 3 步实例化这个类程序使用提取出来的MiniCPMV类加上其他参数hidden_size4096等创建出你的模型对象。如果去掉auto_map会发生什么对比说明如果把这个auto_map删掉只保留architectures: [MiniCPMV]那么当你运行上述加载代码时HF 框架会去它的官方内置模型库transformers库安装目录下的models文件夹找名为MiniCPMV的类。因为MiniCPMV不在官方库里程序会直接抛出错误二、官方定义好的类(LlamaForCausalLM)加载模型内部流程比如说我们通过transformers.LlamaForCausalLM.from_pretrained...来创建模型流程 1LlamaForCausalLM.from_pretrained(./my_model)硬编码路径程序执行非常线性直接定位类Python 直接导入transformers库中的LlamaForCausalLM类。读取配置仅作为参数进入该类的from_pretrained方法读取config.json文件。请注意此时读配置是为了获取hidden_size、num_layers等初始化参数而不是为了“找类”因为类已经确定了。加载权重根据config.json里的torch_dtype和结构去文件夹里找.safetensors或.bin权重文件映射到当前的LlamaForCausalLM对象上。返回模型。三、自己定义的类加载模型内部流程#自己定义的类LlavaLlamaAttForCausalLM #这里LlamaForCausalLM是transformers库定义好的UniNaVIDMetaForCausalLM是我们自己定义的 class LlavaLlamaAttForCausalLM(LlamaForCausalLM, UniNaVIDMetaForCausalLM): config_class LlavaConfig def __init__(self, config): super(LlamaForCausalLM, self).__init__(config) self.model LlavaAttLlamaModel(config) self.lm_head nn.Linear(config.hidden_size, config.vocab_size, biasFalse) self.post_init() #LlavaLlamaAttForCausalLM调用from_pretrained model LlavaLlamaAttForCausalLM.from_pretrained( model_args.model_name_or_path, configconfig, cache_dirtraining_args.cache_dir, **bnb_model_from_pretrained_args) 最终的model就是一个LlavaLlamaAttForCausalLM实例化对象LlavaLlamaAttForCausalLM自己没有写from_pretrained但它继承了LlavaLlamaAttForCausalLM- LlamaForCausalLM - LlamaPreTrainedModel - PreTrainedModel而from_pretrained定义在 Transformers 的PreTrainedModel里所以子类可以直接调用。PreTrainedModel的from_pretrained函数中会执行 model cls(config, ...)这里的cls很重要。这里调用的是LlavaLlamaAttForCausalLM.from_pretrained(...)所以 cls 就是 LlavaLlamaAttForCausalLM最终会调用它自己的 __init__self.model LlavaAttLlamaModel(config)self.lm_head nn.Linear(...)我们通过LlavaLlamaAttForCausalLM.from_pretrained创建的model和LlamaForCausalLM.from_pretrained创建的的model主要差异体现在顶层模型类变了顶层模型训练和推理时你直接调用的完整模型外壳。比如普通 LLaMA 里有两层概念LlamaForCausalLM # 顶层模型└── self.model LlamaModel # 底层 Transformer 主干└── self.lm_head # 输出头把 hidden state 变成词表 logitsLlamaModel主要负责 Transformer 主干计算embedding、decoder layers、attention、MLP、norm最后输出 hidden states。LlamaForCausalLM在它外面加了“做语言模型任务需要的东西”主要包括#1.持有底层主干 self.model LlamaModel(config) #2.加一个语言建模头 lm_head 它把每个 token 位置的隐藏向量变成词表预测分数 self.lm_head nn.Linear(config.hidden_size, config.vocab_size, biasFalse) #3.实现 forward 的完整任务逻辑 outputs self.model(...) hidden_states outputs[0] logits self.lm_head(hidden_states) if labels is not None: loss CrossEntropyLoss(...) #4.适配生成 generate LlamaForCausalLM 继承了 GenerationMixin所以可以调用 model.generate(...)