大语言模型 从主流大模型到llama权重文件解析和参数计算

大语言模型 从主流大模型到llama权重文件解析和参数计算

大语言模型 从主流大模型到llama权重文件解析和参数计算

一、大语言模型

目前我们关注的GPT、Llama都属于大语言模型。

  • Llama:已在大量数据集上完成训练,具备文本分类、生成、摘要等能力;通过“迁移学习”,可在特定任务上快速微调。
  • 大语言模型的核心特点:
  1. 更大规模的训练数据:覆盖各类知识、文本类型,提升泛化能力;
  2. 更复杂的模型结构:通常采用更深的神经网络结构;
  3. 更强的生成能力:能生成连贯、有意义的文本;
  4. 更强的泛化能力:少量数据即可适配不同任务;
  5. 零样本学习能力:无需小样本,仅通过指令即可完成任务。

二、GPT简介

GPT是OpenAI基于Transformer架构的语言模型,核心特点是自回归预训练,可用于文本生成、翻译、问答等任务。

  • ChatGPT:基于GPT系列模型的对话微调版本,通过大规模对话数据优化,提升了上下文理解与对话流畅度;
  • GPT-4:在多模态能力(文本+图像)、逻辑推理等方面升级,2023年3月发布,性能优于此前的GPT-3系列。

三、Llama简介

Llama是Meta推出的开源大语言模型(含Llama 1、Llama 2),特点是仅使用公开数据训练,性能接近GPT-3.5。

  • Llama 2:在Llama 1基础上升级,支持更长上下文、更强数据清洗;其对话版本Llama 2-Chat通过人类反馈优化(RLHF),提升了安全性与实用性。
  • Llama 2开源模型名称:
模型Llama 2-Chat
7BLlama-2-7b-chat-hf
13BLlama-2-13b-chat-hf
70BLlama-2-70b-chat-hf

四、Llama的训练

1. 训练数据

Llama仅使用公开可得数据训练,Llama 1预训练数据约含1.4T tokens

Llama 2训练数据扩展至2万亿标记,同时剔除了含私人信息的数据源。

2. 预训练

Llama 2基于Llama 1的架构,优化点包括:更强数据清洗、更长上下文长度、分组查询注意力机制。

训练损失:在2T标记的预训练后,各参数模型(7B/13B/70B)均未出现损失饱和。

五、Llama 2 Chat

Llama 2 Chat是Llama 2基础上针对对话微调的模型,通过监督微调+人类反馈强化学习(RLHF) 优化。

1. 监督微调

  • 基于预训练的Llama 2,在人工编写的指令数据集(27540条prompt+answer)上微调;
  • 合并prompt与answer,保证序列长度为4096,训练时仅对回答部分反向传播。

2. 基于人类反馈的强化学习(RLHF)

目标是对齐人类偏好与指令遵循,核心步骤:

  1. 收集人类偏好数据:通过二元比较标注模型回应的“有用性”“安全性”,构建百万级数据集;
  2. 奖励模型:训练两个独立的奖励模型(有用性RM/安全性RM),避免两者相互抵消;
  • 数据配比:有用性RM采用“Meta Helpfulness: (Meta Safety+开源)=1:1”,安全性RM采用“Meta Safety: (Meta Helpfulness+开源)=9:1”;
  1. 迭代微调:使用近端策略优化(PPO)、拒绝采样微调两种算法,提升模型的对话效果。

六、Llama 2权重文件夹内容整理

1、权重文件格式分类

Llama 2发布时提供两种权重文件格式:

  1. pth格式(原始格式):如 Llama-2-7b ,目录包含 checklist.chk 、 unconsolidated.00.pth 、 params.json 等文件;
  2. hf格式(HuggingFace格式):如 Llama-2-7b-hf ,是主流使用格式。

2、Llama-2-7b-hf权重文件夹目录

plaintext

├── config.json # 模型配置(架构、超参数、特殊标记)
├── generation_config.json # 生成器配置(文本生成方式)
├── pytorch_model-00001-of-00002.bin # 权重文件1(约9.9GB)
├── pytorch_model-00002-of-00002.bin # 权重文件2(约3.5GB)
├── pytorch_model.bin.index.json # 权重文件索引(快速加载)
├── README.md # 说明文档
├── special_tokens_map.json # 特殊标记定义(起始/结束/未知标记)
├── tokenizer.model # 分词器二进制文件
├── tokenizer.json # 分词器配置(JSON格式)
└── tokenizer_config.json # 分词器初始化配置

3、核心文件说明

  1. .bin权重文件
  • 模型训练后的参数文件,因体积(13G)超过10GB被拆分;
  • 包含神经网络的权重、偏置等可学习参数。
  1. 配置类文件
  • config.json :定义模型架构(如 LlamaForCausalLM )、嵌入维度( hidden_size )、注意力头数( num_attention_heads )等;
  • generation_config.json :控制文本生成的参数(如 bos_token_id 、 eos_token_id );
  • special_tokens_map.json :定义特殊标记(如 bos_token: 、 eos_token: )。
  1. 分词器相关文件
  • tokenizer.model / tokenizer.json :存储词表与分词规则;
  • tokenizer_config.json :分词器初始化参数。

七、Llama 2模型参数计算与权重文件解析(含代码实战)

1、Llama 2模型超参数

Llama 2不同尺寸模型的核心超参数如下:

参数数量模型大小嵌入维度 d_model层数 L中间维度 d_ff
6.7B1347683942440963211008
13.0B2603173888051204013824
32.5B6505790259266566017920
70B13795331686481928028672

2、标准Transformer解码器参数计算

以GPT-3(125M参数)为例,参数来自词嵌入层、Transformer层、输出层:

  1. 词嵌入层: V × d_model ( V 为词汇表大小)
  2. Transformer层:每层包含自注意力机制、前馈网络、层归一化,公式为:

4×dmodel2+2×df×dmodel+df+5×dmodel4×d_model² + 2×d_f×d_model + d_f + 5×d_model4×dm​odel2+2×df​×dm​odel+df​+5×dm​odel

  1. 输出层:与词嵌入层参数共享,数量为 V × d_model

3、Llama 2权重文件解析(HF格式)

HF格式的Llama 2权重以 .bin 文件存储(拆分多个文件),每个文件是zip压缩包,包含 data.pkl (序列化的参数数据)。

代码示例:解析Llama 2权重文件,用于读取HF格式的Llama 2权重文件,解析参数形状、类型并统计数量:

import pickle import zipfile from pathlib import Path from typing import IO, Any, Callable, Optional, List, Dict from dataclasses import dataclass import torch # 需提前安装PyTorch# 定义数据类型枚举(对应权重存储的类型) DT_F16 ='F16'# 半精度浮点 DT_F32 ='F32'# 单精度浮点 DT_BF16 ='DT_BF16'# 脑浮点数 DT_I32 ='I32'# 32位整数# -------------------------- 数据类定义:描述权重存储信息 --------------------------@dataclassclassLazyStorageKind:"""描述权重的存储数据类型""" data_type:str# 对应DT_F16/DT_F32等@dataclassclassLazyStorage:"""描述权重的存储位置与类型""" kind: LazyStorageKind # 数据类型 description:str# 存储路径等描述信息@dataclassclassLazyTensor:"""描述权重张量的形状、类型""" shape: List[int]# 张量形状(如[32000, 4096]) data_type:str# 数据类型 description:str# 附加描述# -------------------------- 自定义反序列化类 --------------------------classLazyUnpickler(pickle.Unpickler):""" 继承pickle.Unpickler,重写persistent_load和find_class方法, 用于解析HF格式权重文件中的序列化数据 """# 映射Torch存储类型到自定义的LazyStorageKind CLASSES ={('torch.util','_rebuild_tensor_v2'):lambda*args: args,('torch','BFloat16Storage'): LazyStorageKind(DT_BF16),('torch','HalfStorage'): LazyStorageKind(DT_F16),# Half对应F16('torch','FloatStorage'): LazyStorageKind(DT_F32),('torch','IntStorage'): LazyStorageKind(DT_I32)}def__init__(self, fp: IO[bytes], data_base_path:str, zip_file: zipfile.ZipFile):super().__init__(fp) self.data_base_path = data_base_path # 权重数据在zip包中的基础路径 self.zip_file = zip_file # zip文件对象defpersistent_load(self, pid: Any)-> Any:""" 处理持久化对象(HF权重文件中用pid标记权重存储位置) """# 从pid中解析数据类型、文件名、偏移量等信息 data_type = pid[1] filename =f"{self.data_base_path}/{pid[2]}"# 构造LazyStorage对象,描述权重的存储信息 description =f"persistent data_type={data_type} path-in-zip={filename}"return LazyStorage(kind=self.CLASSES[('torch', data_type)], description=description)deffind_class(self, module:str, name:str)-> Any:""" 重写类查找逻辑,映射Torch的存储类到自定义的LazyStorageKind """ifnot module.startswith('torch'):# 非Torch类,使用默认查找逻辑returnsuper().find_class(module, name)# Torch类,使用CLASSES映射return self.CLASSES.get((module, name),super().find_class(module, name))# -------------------------- 主函数:解析权重文件 --------------------------if __name__ =='__main__':# 1. 指定要解析的权重文件路径(替换为你的Llama 2权重文件路径) weight_path = Path(r"")#此处填你的模型根目录# 2. 以二进制模式打开权重文件(HF的.bin是zip压缩包)withopen(weight_path,'rb')as fp:# 3. 打开zip压缩包with zipfile.ZipFile(fp)as zip_file:# 4. 找到zip包中的data.pkl文件(序列化的权重数据) pickle_paths =[name for name in zip_file.namelist()if name.endswith('.pkl')]assertlen(pickle_paths)==1,"权重文件中应只有一个data.pkl"# 校验# 5. 打开data.pkl文件with zip_file.open(pickle_paths[0],'r')as pickle_fp:# 6. 使用自定义的LazyUnpickler反序列化数据 unpickler = LazyUnpickler( fp=pickle_fp, data_base_path=pickle_paths[0][:-4],# 去掉.pkl后缀,得到基础路径 zip_file=zip_file ) model_data = unpickler.load()# 反序列化得到模型权重字典# 7. 统计并打印权重参数信息 total_params =0# 总参数数量 total_bytes =0# 总字节数# 遍历权重字典中的每个参数for param_name, lazy_tensor in model_data.items():# 打印参数名、形状、类型print(f"{param_name}: shape={lazy_tensor.shape} type={lazy_tensor.data_type}")# 计算当前参数的数量 param_count =1for dim in lazy_tensor.shape: param_count *= dim # 计算当前参数的字节数(F16占2字节,F32占4字节)if lazy_tensor.data_type == DT_F16: param_bytes = param_count *2elif lazy_tensor.data_type == DT_F32: param_bytes = param_count *4else: param_bytes =0# 其他类型暂不统计# 累加总数 total_params += param_count total_bytes += param_bytes # 打印统计结果print(f"\n总参数数量: {total_params}")print(f"总字节数: {total_bytes} (约{total_bytes/1024/1024/1024:.2f}GB)")

第82行填写你自己的模型根目录喵

输出大概是这样

model.embed_tokens.weight: shape=[32000, 4096] type=F16 model.layers.0.self_attn.q_proj.weight: shape=[4096, 4096] type=F16 model.layers.0.self_attn.k_proj.weight: shape=[4096, 4096] type=F16 model.layers.0.self_attn.v_proj.weight: shape=[4096, 4096] type=F16 model.layers.0.self_attn.o_proj.weight: shape=[4096, 4096] type=F16 model.layers.0.self_attn.rotary_emb.inv_freq: shape=[64] type=F32 model.layers.0.mlp.gate_proj.weight: shape=[11008, 4096] type=F16 model.layers.0.mlp.up_proj.weight: shape=[11008, 4096] type=F16 model.layers.0.mlp.down_proj.weight: shape=[4096, 11008] type=F16 model.layers.0.input_layernorm.weight: shape=[4096] type=F16 model.layers.0.post_attention_layernorm.weight: shape=[4096] ...... model.layers.23.self_attn.q_proj.weight: shape=[4096, 4096] type=F16 model.layers.23.self_attn.k_proj.weight: shape=[4096, 4096] type=F16 model.layers.23.self_attn.v_proj.weight: shape=[4096, 4096] type=F16 model.layers.23.self_attn.o_proj.weight: shape=[4096, 4096] type=F16 model.layers.23.self_attn.rotary_emb.inv_freq: shape=[64] type=F32 model.layers.23.mlp.gate_proj.weight: shape=[11008, 4096] type=F16 model.layers.23.mlp.up_proj.weight: shape=[11008, 4096] type=F16 model.layers.23.mlp.down_proj.weight: shape=[4096, 11008] type=F16 model.layers.23.input_layernorm.weight: shape=[4096] type=F16 model.layers.23.post_attention_layernorm.weight: shape=[4096] type=F16 

参数数量=4988274176,字节数=9976551424

Could not load content