AI 大模型文件里到底装了什么:671B 参数、几十 GB 的文件,拆开给你看
AI 大模型文件里到底装了什么:671B 参数、几十 GB 的文件,拆开给你看
适合对 AI 大模型好奇、想知道模型文件内部结构的开发者。
本文用 Python 实际加载和分析模型文件,展示参数、权重、配置的结构。
背景:模型文件是个黑盒
下载一个开源大模型(如 DeepSeek-V3、Qwen2.5),你会得到一堆文件:
model-00001-of-00006.safetensors model-00002-of-00006.safetensors ... config.json tokenizer.json这些文件里到底装了什么?671B 参数是什么意思?为什么一个模型要几十 GB?
今天拆开看。
模型文件的组成
一个完整的模型包含 4 类文件:
| 文件 | 作用 | 大小占比 |
|---|---|---|
| 模型权重(.safetensors/.bin) | 神经网络的所有参数 | 95%+ |
| 配置文件(config.json) | 模型架构参数 | < 0.01% |
| 词表文件(tokenizer.json) | Token 和 ID 的映射 | < 1% |
| 其他文件 | 许可证、README 等 | < 0.01% |
1. 配置文件(config.json)
importjsonwithopen("config.json")asf:config=json.load(f)# DeepSeek-V3 的配置(示例)print(json.dumps(config,indent=2))输出示例:
{"model_type":"deepseek_v3","hidden_size":7168,"num_hidden_layers":61,"num_attention_heads":128,"num_key_value_heads":128,"intermediate_size":18432,"vocab_size":129280,"max_position_embeddings":163840,"rms_norm_eps":1e-6,"rope_theta":10000.0}关键参数解读:
| 参数 | 含义 | DeepSeek-V3 值 |
|---|---|---|
| hidden_size | 每个 token 的向量维度 | 7168 |
| num_hidden_layers | Transformer 层数 | 61 |
| num_attention_heads | 注意力头数 | 128 |
| intermediate_size | FFN 中间层维度 | 18432 |
| vocab_size | 词表大小 | 129280 |
| max_position_embeddings | 最大上下文长度 | 163840 |
参数量估算:
# 简化估算公式defestimate_params(config):h=config["hidden_size"]# 7168L=config["num_hidden_layers"]# 61V=config["vocab_size"]# 129280I=config["intermediate_size"]# 18432# 每层参数 ≈ 4 * h^2 + 2 * h * I(Attention + FFN)params_per_layer=4*h**2+2*h*I# 总参数 ≈ 层数 * 每层 + embedding + outputtotal=L*params_per_layer+2*V*hreturntotal params=estimate_params(config)print(f"估算参数量:{params/1e9:.1f}B")# 约 671B2. 模型权重(safetensors 文件)
fromsafetensorsimportsafe_open# 加载第一个权重文件withsafe_open("model-00001-of-00006.safetensors",framework="numpy")asf:# 列出所有参数名keys=f.keys()print(f"参数数量:{len(keys)}")# 查看前 10 个参数名forkeyinlist(keys)[:10]:tensor=f.get_tensor(key)print(f"{key}: shape={tensor.shape}, dtype={tensor.dtype}")输出示例:
参数数量: 3256 model.embed_tokens.weight: shape=(129280, 7168), dtype=float16 model.layers.0.self_attn.q_proj.weight: shape=(7168, 7168), dtype=float16 model.layers.0.self_attn.k_proj.weight: shape=(7168, 7168), dtype=float16 model.layers.0.self_attn.v_proj.weight: shape=(7168, 7168), dtype=float16 model.layers.0.self_attn.o_proj.weight: shape=(7168, 7168), dtype=float16 model.layers.0.mlp.gate_proj.weight: shape=(18432, 7168), dtype=float16 model.layers.0.mlp.up_proj.weight: shape=(18432, 7168), dtype=float16 model.layers.0.mlp.down_proj.weight: shape=(7168, 18432), dtype=float16 model.layers.0.input_layernorm.weight: shape=(7168,), dtype=float32 model.layers.0.post_attention_layernorm.weight: shape=(7168,), dtype=float32参数命名规则:
model.layers.{层号}.{模块}.{参数类型}| 模块 | 含义 | 参数形状 |
|---|---|---|
| self_attn.q_proj | Query 投影 | (hidden_size, hidden_size) |
| self_attn.k_proj | Key 投影 | (hidden_size, hidden_size) |
| self_attn.v_proj | Value 投影 | (hidden_size, hidden_size) |
| self_attn.o_proj | Output 投影 | (hidden_size, hidden_size) |
| mlp.gate_proj | FFN 门控 | (intermediate_size, hidden_size) |
| mlp.up_proj | FFN 上投影 | (intermediate_size, hidden_size) |
| mlp.down_proj | FFN 下投影 | (hidden_size, intermediate_size) |
| input_layernorm | 输入归一化 | (hidden_size,) |
3. 为什么模型这么大
defcalculate_model_size(config):"""计算模型存储大小"""h=config["hidden_size"]L=config["num_hidden_layers"]V=config["vocab_size"]I=config["intermediate_size"]# 每层参数数量attn_params=4*h*h# Q, K, V, Offn_params=3*h*I# gate, up, downnorm_params=2*h# 2 个 LayerNormlayer_params=attn_params+ffn_params+norm_params# 总参数total_params=L*layer_params+V*h# 层 + embedding# FP16 存储(2 字节/参数)size_fp16=total_params*2/(1024**3)# GB# INT4 量化存储(0.5 字节/参数)size_int4=total_params*0.5/(1024**3)# GBprint(f"总参数:{total_params/1e9:.1f}B")print(f"FP16 存储:{size_fp16:.1f}GB")print(f"INT4 量化:{size_int4:.1f}GB")returntotal_params# DeepSeek-V3config={"hidden_size":7168,"num_hidden_layers":61,"vocab_size":129280,"intermediate_size":18432}calculate_model_size(config)# 输出:# 总参数: 671.0B# FP16 存储: 1243.1 GB# INT4 量化: 310.8 GB所以 DeepSeek-V3 全精度需要 1.2TB 存储,INT4 量化后需要约 311GB。
4. 词表文件(tokenizer.json)
withopen("tokenizer.json")asf:tokenizer_data=json.load(f)# 词表大小vocab=tokenizer_data.get("model",{}).get("vocab",{})print(f"词表大小:{len(vocab)}")# 查看一些 tokensample_tokens=list(vocab.items())[:20]fortoken,token_idinsample_tokens:print(f" '{token}' → ID{token_id}")输出示例:
词表大小: 129280 '!' → ID 0 '"' → ID 1 '#' → ID 2 '$' → ID 3 '我' → ID 452 '是' → ID 374 'Python' → ID 15339词表的作用:把文本拆成 token(ID),模型处理的是 ID 序列,输出的也是 ID 序列,再通过词表转回文本。
5. 模型加载过程
fromtransformersimportAutoModelForCausalLM,AutoTokenizerimporttorch# 加载 tokenizer(词表)tokenizer=AutoTokenizer.from_pretrained("./model_dir")# 加载模型权重model=AutoModelForCausalLM.from_pretrained("./model_dir",torch_dtype=torch.float16,# 用 FP16 节省显存device_map="auto"# 自动分配到 GPU)# 查看模型结构print(model)# 输出:# DeepSeekForCausalLM(# (model): DeepSeekModel(# (embed_tokens): Embedding(129280, 7168)# (layers): ModuleList(# (0): DeepSeekDecoderLayer(...)# (1): DeepSeekDecoderLayer(...)# ...# )# (norm): RMSNorm()# )# (lm_head): Linear(7168, 129280, bias=False)# )6. 量化是怎么回事
量化就是用更少的比特数存储每个参数,减小模型体积和显存需求。
| 精度 | 每个参数占空间 | 671B 模型大小 | 质量损失 |
|---|---|---|---|
| FP32 | 4 字节 | 2.5 TB | 无 |
| FP16 | 2 字节 | 1.2 TB | 极小 |
| INT8 | 1 字节 | 620 GB | 小 |
| INT4 | 0.5 字节 | 311 GB | 中等 |
| INT2 | 0.25 字节 | 155 GB | 较大 |
# 量化示例(使用 bitsandbytes)fromtransformersimportBitsAndBytesConfig quant_config=BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_compute_dtype=torch.float16)model=AutoModelForCausalLM.from_pretrained("./model_dir",quantization_config=quant_config,device_map="auto")# 查看显存占用print(f"显存占用:{model.get_memory_footprint()/1e9:.1f}GB")踩坑记录
坑 1:safetensors 和 .bin 格式混淆
症状:下载的模型既有 .safetensors 又有 .bin,不知道用哪个。
原因:旧版用 PyTorch 的 .bin 格式,新版用 safetensors(更安全)。
解决:优先用 .safetensors,它是 safetensors 库的格式,加载更安全(不会执行恶意代码)。
坑 2:模型文件不完整
症状:加载时报 “unexpected key” 或 “missing key”。
原因:下载中断,文件不完整。
解决:检查文件 hash,或者重新下载。
坑 3:显存不够加载整个模型
症状:加载 70B 模型时报 CUDA out of memory。
解决:
- 用量化(INT4/INT8)
- 用
device_map="auto"自动分层到 CPU 和 GPU - 用 llama.cpp 等 CPU 推理工具
坑 4:不同框架的权重格式不通用
症状:HuggingFace 格式的模型在 llama.cpp 里用不了。
原因:不同框架的权重命名和排列方式不同。
解决:用转换工具(如convert-hf-to-gguf.py)转换格式。
坑 5:config.json 里的参数和实际权重不匹配
症状:config 写了 32 层,但权重文件只有 30 层的参数。
原因:模型经过剪枝或微调后 config 没更新。
解决:以实际权重文件为准,config 只是参考。
总结
3 条核心经验:
模型 = 配置 + 权重 + 词表。配置定义架构,权重存储知识,词表做文本↔ID 转换。
模型大小 = 参数量 × 精度。671B 参数在 FP16 下需要 1.2TB,INT4 量化后只需 311GB。量化是让大模型能在消费级硬件上跑的关键。
safetensors 是目前最好的权重格式。比 .bin 更安全、加载更快,优先使用。
你对大模型内部结构有什么疑问?评论区交流。