Hugging Face的model hub
Hugging Face的model hub:是一个汇聚了海量预训练机器学习模型的中央仓库,可以把它理解为一个AI模型的“应用商店”
一、Automodel加载模型:
下面是一个模型的代码结构实例,以它为例我们来说明如何从Hugging Face配置预训练好的大模型
上面是MiniCPM-Llama3-V-2_5 / config.json
这里有两个特别说明:
1."architectures": ["MiniCPMV"]
architectures(一个身份标签):是一个类名列表(通常只有一项),定义了模型的架构名称
作用:这是一个声明,告诉使用者或HF框架:“我这个模型在逻辑上属于MiniCPMV这个类型。”
局限性:它只提供了名字。如果HF官方库内置了这个名字(比如LlamaForCausalLM),框架就能自动找到代码。但如果这个名字是自定义的(如这里的MiniCPMV),HF的官方库根本不知道这个类长什么样,光看名字会报错。
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_size=4096等),创建出你的模型对象。
如果去掉auto_map会发生什么?(对比说明)
如果把这个auto_map删掉,只保留"architectures": ["MiniCPMV"],那么当你运行上述加载代码时,HF 框架会去它的官方内置模型库(transformers库安装目录下的models文件夹)找名为MiniCPMV的类。因为MiniCPMV不在官方库里,程序会直接抛出错误
二、官方定义好的类(LlamaForCausalLM)加载模型内部流程
比如说我们通过:
transformers.LlamaForCausalLM.from_pretrained(...)来创建模型
流程 1:LlamaForCausalLM.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, bias=False) self.post_init() #LlavaLlamaAttForCausalLM调用from_pretrained model = LlavaLlamaAttForCausalLM.from_pretrained( model_args.model_name_or_path, config=config, cache_dir=training_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 变成词表 logits
LlamaModel主要负责 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, bias=False) #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(...)