- SignalDesk1小时前
学习目标:理解大模型项目的完整训练与推理流程 → 掌握模型文件组织、权重加载与 Checkpoint → 理解 FP32/FP16/BF16、混合精度和量化 → 掌握训练显存、计算量与 FLOPs 估算 → 理解 LLM 解码策略与推理流程 → 了解 Scaling Law、推理加速及常见工程技术 → 能够独立阅读基础模型训练与推理代码。 难度 : 重要性 : 昔日只知神通如何施展,却不识其背后的万千阵法。如今,以参数为骨,以数据为血,以算力为炉,窥模型运转之玄机,掌训练推理之法门。自此,神通不再只是纸上玄谈,亦可亲手铸就。 大模型项目是怎样组织的? 假设现在有一个大模型,我们想要用它进行问答,那么至少需要四个组成部分: Model: 网络架构 + 已训练的权重参数 Data:Tokenizer + 训练样本 + 数据预处理 Training:Forward,Loss,Backward,Optimizer Inference:输入问题,模型计算,解码,输出答案 其中模型结构与权重决定神经网络如何计算;数据决定模型学习什么;训练程序负责更新参数;推理程序负责利用训练后的模型生成结果。 这些部分可以放在一个项目里,也可以分布在不同的代码仓库和模型文件目录中。例如如果自己从头写一个 模型,一个经典的文件架构: my_llm_project/ │ ├── model.py # 定义神经网络结构 ├── config.py # 模型和训练配置 ├── dataset.py # 读取并处理训练数据 ├── train.py # 模型训练 ├── inference.py # 模型推理 │ ├── data/ │ ├── train.jsonl # 训练集 │ └── val.jsonl # 验证集 │ └── checkpoints/ └── checkpoint-1000/ # 训练检查点 model.py —— 模型的结构与权重是什么关系? 它最重要的作用是 定义神经网络的结构,以及数据进入网络后如何计算 。 例如一个最简单的神经网络,MLP: import torch.nn as nn class SimpleModel(nn.Module): def __init__(self): // 定义模型有哪些层 super().__init__() self.fc1 = nn.Linear(768, 1024) self.act = nn.GELU() self.fc2 = nn.Linear(1024, 768) def forward(self, x): //定义输入数据怎样依次经过这些层 x = self.fc1(x) x = self.act(x) x = self.fc2(x) return x 这里面的 nn.Linear(768, 1024) 实际上就是定义了一个对应形状的矩阵 W\in\mathbb R^{1024\times768} ,注意这里定义的只是架构,具体的值实际上是存在模型权重里面的。这里面的具体数值才决定了模型的能力,这也是一般下载一个模型的几十上百G的源头。 预训练模型的架构,权重等配置文件在哪? 如果从 HuggingFace 下载一个预训练好的 LLM,可能看到如下结构: pretrained_model/ │ ├── config.json ├── generation_config.json │ ├── tokenizer.json ├── tokenizer_config.json ├── special_tokens_map.json │ └── model.safetensors 文件 主要作用 config.json 描述模型架构配置 model.safetensors 保存模型权重参数 tokenizer.json 保存 Tokenizer 所需的词表及分词规则等信息 tokenizer_config.json Tokenizer 相关配置 special_tokens_map.json 特殊 Token 的定义或映射 generation_config.json 保存默认生成配置 config.json 里面保存着怎样构建网络,比如隐藏维度、Transformer Block 数量、Attention Head 数量、词表大小等。 { "model_type": "example_llm", "hidden_size": 4096, "num_hidden_layers": 32, "num_attention_heads": 32, "vocab_size": 50000 } model.safetensors 保存的是模型的权重张量。 例如某一层可能包含 model.layers.0.self_attn.q_proj.weight model.layers.0.self_attn.k_proj.weight model.layers.0.self_attn.v_proj.weight model.layers.0.mlp.up_proj.weight ... 每个名称对应一个具体的 Tensor。例如 q_proj.weight → [4096, 4096] ,这时模型结构与权重就可以对应起来。程序先创建一个形状为 [4096,4096] 的 Linear,然后将权重文件中保存的数值加载进去。 tokenizer.json 保存的是Tokenizer 所需的词表及分词规则等信息,这也很重要,因为 Tokens ID 和 Embedding 层是对应的,不同的分词器产生的 Tokens ID 不同,这会导致后面 Embedding 层对应不上,输入发生改变。 train.py —— 模型到底是怎么训练的? 现在已经有了模型结构,权重和Tokenizer。希望使用自己的数据进行 SFT,这时候就需要 dataset.py 和 train.py 。 dataset.py 负责将原始数据整理成模型需要的形式。 train.py 负责将这些数据送入模型,并根据 Loss 更新参数。一次基本的训练迭代包含: \text{Data} \rightarrow\text{Forward} \rightarrow\text{Loss} \rightarrow\text{Backward} \rightarrow\text{Optimizer Step} Checkpoint —— 训练到一半为什么需要保存文件? 训练到一半断电了,没有中途保存就完蛋了,这里需要区分两件事情: 保存一个可用于推理的模型,通常只需要模型权重、结构配置,以及配套的 Tokenizer 等文件。(就像我们在 HuggingFace 下载的预训练模型那样) 保存一个能够恢复训练的 Checkpoint,往往还需要优化器状态、学习率调度器状态、当前训练进度,有时还需要随机数生成器状态等。例如: checkpoint-7000/ │ ├── model.safetensors ├── config.json ├── optimizer.pt ├── scheduler.pt ├── trainer_state.json └── rng_state.pth inference.py —— 加载训练好的模型以后怎么生成答案? 现在模型已经训练完毕,需要推理,对于 Decoder-Only LLM,整个流程如下: \text{Text} \xrightarrow{\text{Tokenizer}} \text{Input IDs} \xrightarrow{\text{LLM}} \text{Logits} \xrightarrow{\text{Decoding}} \text{Output IDs} \xrightarrow{\text{Tokenizer Decode}} \text{Answer} 这里还有一个问题,我们知道 Transformer 会输出 Logits,但从 Logits 选择下一个 Token 的过程,究竟是怎么实现的?这就是后面要学习的 Greedy、Temperature、Top-K、Top-P 等解码策略。我们可以使用现成的 HuggingFace Transformers: #################################### 加载Tokenizer和Model #################################### import torch from transformers import AutoTokenizer, AutoModelForCausalLM model_path = "./pretrained_model" tokenizer = AutoTokenizer.from_pretrained(model_path) #加载Tokenizer model = AutoModelForCausalLM.from_pretrained(model_path) #加载Model权重,包括读取config.py获取架构,并利用.safetensors权重文件填入 #################################### 加载Tokenizer和Model #################################### #################################### 得到 #################################### model.eval() #将模型切换到 Evaluation Mode prompt = "APTAPTAPTAPT" inputs = tokenizer( prompt, return_tensors="pt" ) """ 可以利用字典进行访问,对于单条文本输入形状通常是 [1,N],1是Batch Size,N是Token数量 例如:inputs = { "input_ids": tensor([[125, 830, 42, 671]]), "attention_mask": tensor([[1, 1, 1, 1]]) } """ with torch.inference_mode(): #关闭 Autograd 梯度记录 outputs = model.generate( inputs, max_new_tokens=64, do_sample=False ) """ LLM 经过计算后会得到 Logits [B,N,V],然后用于决定下一个 Tokens,然后加入生成序列重复。generate就是封装起来了这个过程。 而且这里面还有Prefill,Decoder,KV Cache等机制 """ new_tokens = outputs[0][inputs["input_ids"].shape[1]:] """ generate() 默认返回的通常是包含输入 Prompt 与新生成 Token 的完整序列。 inputs["input_ids"].shape[1] 表示就是原始 Prompt 的 Token 数量。 所以整段就是 取得第一个样本的生成序列,然后删除原始 Prompt 对应的前 N 个 Token。即只取新生成的部分 """ answer = tokenizer.decode( new_tokens, skip_special_tokens=True ) # 将对应的 Tokens IDs 转化为 Text print(answer) 这里假设模型目录存在且兼容 AutoModelForCausalLM 。对于聊天模型,实际使用时还需要遵循它对应的 Chat Template,并将输入张量放到与模型一致的设备上。 一个大模型是怎样训练的? 完整的训练流程,大概是,先利用 DataLoader 准备好对应的 Datasets,然后就进入 Forward ,计算 Logits 后计算 Loss,然后更新梯度和 Optimizer。 最最最简单的训练代码如下: model.train() for batch in train_loader: outputs = model(batch) loss = outputs.loss optimizer.zero_grad() loss.backward() optimizer.step() DataLoader 是什么? 假设我们有 10000 条训练样本,Dataset 负责定义如何读取其中某一条数据 sample = dataset[0] ,可能会返回 input_ids, atention_mask, labels... 。但如果需要用到一群数据,一般会使用 Dataloader: from torch.utils.data import DataLoader train_loader = DataLoader( dataset, batch_size=4, #表示每次从 Dataset 中取出 4 条样本 shuffle=True #表示每轮遍历训练数据时打乱样本顺序。 ) Forward、Backward 和 Optimizer Step 分别做什么? Forward 用于计算预测结果 例如一个 LLM,Forward 过程就是先利用 Embedding 转化为向量,然后得到 Logits,最后根据 Labels 计算损失。 Backward 用于得到梯度 调用 loss.backward() ,Autograd 就会沿着前向传播构建的计算图,利用链式法则计算各个可训练参数的梯度。不过需要注意其本身不会执行普通优化器的参数更新,它只是计算并累积梯度,真正修改参数的是下一步 Optimizer。 Optimizer 真正修改模型参数 比如使用 SGD,Adam,AdamW 等优化器,通过公式进行计算参数被修改的值,会直接写出 optimizer.step() 这些优化器是什么—— SGD,Adam,AdamW? 先从最简单的 SGD 开始,核心思想就是沿着当前梯度的反方向更新参数 \theta_{t+1}=\theta_t-\e
- 情报分类:技术学习与提效
- 分类依据:内容涉及技术、AI、软件工具或工程实践
- 信息来源:服务器 / LINUX DO - 最新话题
- 发布时间:2026/10/17 21:06:23
- 暂无回复