news 2026/9/8 2:56:52

大模型训练像做面包?一文看懂预训练、微调与对齐全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大模型训练像做面包?一文看懂预训练、微调与对齐全流程

你是不是也遇到过这种情况:刚接触大模型时,看论文、看源码、看视频,到处都在说预训练、微调、SFT、RLHF,感觉每个词都认识,但连在一起就不知道整个流程到底在干什么。然后你去看训练框架的文档,里面全是数据预处理、优化器、学习率调度、检查点保存……越看越乱,最后只能在别人的代码里改改参数,跑起来也不知所以然。

这篇文章想换一个思路,用“烘焙”来做比喻,把 LLM 训练的完整链路重新讲一遍。“Baking a Model” 这个说法在英文技术社区里并不少见,它不是玩梗,而是因为大模型训练和烘焙在工程逻辑上有很多高度对应的地方:都是非线性过程、都极度依赖经验、最终质量都取决于原料和过程控制,而不是单点“魔法”。

读完这篇文章,你应该能收获三样东西:

  • 一张 LLM 训练全流程的“心智地图”:预训练、微调、对齐分别对应烘焙的哪个阶段,它们为什么不能跳步。
  • 一份可以照着跑的完整代码示例:用 Hugging Face 生态训练一个真正的语言模型,从数据处理到推理验证。
  • 一套判断训练过程是否健康的经验指标:什么时候 loss 下降是正常的、什么时候该停、什么时候模型“坏了”。

文章会从比喻入手,但不会停留在比喻。每个环节都会给出技术定义、关键代码和实际工程建议,毕竟比喻只是脚手架,真正要建立的还是技术直觉。

1. 为什么“烘焙”是理解 LLM 训练的最佳比喻

先给结论:用烘焙比喻大模型训练,最大的价值不是让概念变得“可爱”,而是让初学者看清单个操作在整个流程中的位置。

很多人学 LLM 训练的难点在于:每个单独的技术点都有大量资料,但很少有人讲清楚它们之间的前后依赖。比如你知道数据清洗很重要,但不知道为什么预训练阶段的数据清洗和微调阶段完全不同;你知道学习率很关键,但不理解为什么训练后期要下降,为什么同样一个超参数在预训练和微调里作用差别那么大。

这些问题用烘焙类比会立刻清晰起来。

想象你在做一款面包,整个流程是:

  1. 挑选小麦、面粉、水、酵母、盐,并称量。
  2. 把原料混合揉成面团。
  3. 第一次发酵,让面团膨胀。
  4. 排气、整形,决定最终形状。
  5. 第二次发酵。
  6. 入炉烘烤,控制温度和湿度。
  7. 出炉冷却、切片、包装。

现在把它映射到 LLM 训练上:

烘焙步骤LLM 训练对应阶段关键技术
挑选原料、称量数据收集与清洗数据质量、去重、过滤、tokenization
揉面数据组织与批次构建采样策略、batch size、序列长度
第一次发酵预训练(pretraining)自监督学习、语言建模损失、学习率调度
排气、整形模型结构设计与初始化transformer 架构、参数量、初始化方式
第二次发酵继续预训练 / domain adaptation领域数据继续训练、增量预训练
入炉烘烤监督微调(SFT)指令数据、对话数据、交叉熵损失
出炉冷却、切片对齐 / RLHF / 部署前评估奖励模型、PPO、评测集验证
试吃、调整配方迭代实验实验管理、超参数搜索、回滚

这张对照表有两点值得展开。

第一,烘焙的每个阶段都不能跳步。你不可能不发酵就直接烤,也不可能在面团还没成型时就裱花。同样,LLM 训练中跳过预训练直接微调会效果很差,因为模型根本不具备语言基础能力;反过来,你也不能指望预训练直接产出能聊天的助手,因为预训练只是“学会了字词规律”,还没有“学会听从指令”。

第二,原料质量决定了上限,工艺只决定你能多接近这个上限。面包好不好吃,最关键的其实是面粉和发酵状态,而不是最后那几分钟烤箱温度;模型最终效果的上限,同样由数据质量和数据分布决定,超参数调优只是让你逼近这个上限。这个认知非常重要,因为很多新手把大量时间花在调学习率上,却不愿意花时间检查数据质量,方向就反了。

所以,要理解 LLM 训练,不要从“训练”本身开始,而要从“原料”开始。接下来各节就按这个顺序展开。

2. 原料准备:数据收集、清洗与 Tokenization

在烘焙里,面粉的蛋白质含量、新鲜度、吸水性,直接决定了面团能不能揉出筋、面包能不能膨胀。在模型训练里,数据就是面粉

但数据不能直接扔给模型。原始文本要先经过一条流水线:

  1. 收集:从网页、书籍、代码仓库、学术论文等来源采集文本。
  2. 清洗:去 HTML 标签、去重复段落、去低质量内容、过滤有害信息。
  3. 去重:计算文本相似度,去除近似重复的数据。重复数据会严重干扰训练。
  4. 分词(Tokenization):把文本切分成 token 序列,这一步相当于“把面粉过筛”。
  5. 构建训练样本:将 token 序列截断或拼接成固定长度,组装成 batch。

其中Tokenization 是初学者最容易忽略、但影响极其深远的一步。模型看到的世界不是字符,不是单词,而是 token 的序列。同一个词在中文和英文里被切分的方式完全不同;tokenizer 词表大小、切分粒度直接影响训练效率和最终效果。

在实际代码里,用 Hugging Face 生态处理数据通常长这样:

# 文件路径:prepare_data.py from datasets import load_dataset, concatenate_datasets from transformers import AutoTokenizer import numpy as np # 1. 加载原始数据集 # 这里用中文维基作为演示数据,实际项目请根据任务替换 dataset = load_dataset("wikipedia", language="zh", date="20240101", trust_remote_code=True) # 2. 清洗:去掉空文本和过短的内容 def clean_text(example): text = example["text"] if text is None: return {"text": ""} text = text.replace("\n", " ").strip() return {"text": text} dataset = dataset.map(clean_text, num_proc=8) dataset = dataset.filter(lambda x: len(x["text"]) > 50) # 3. 加载 tokenizer tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese") # 4. 切分文本并截断/拼接为固定长度 block_size = 512 def tokenize_function(examples): return tokenizer(examples["text"], truncation=False) tokenized_dataset = dataset.map(tokenize_function, batched=True, num_proc=8, remove_columns=["text"]) def group_texts(examples): concatenated = sum(examples["input_ids"], []) total_length = (len(concatenated) // block_size) * block_size result = { "input_ids": [concatenated[i: i + block_size] for i in range(0, total_length, block_size)], "attention_mask": [ [1] * block_size for _ in range(0, total_length, block_size) ], } return result lm_dataset = tokenized_dataset.map(group_texts, batched=True, num_proc=8) print(lm_dataset["train"][0].keys())

这段代码做了什么?

  • 加载原始语料。
  • 过滤掉短文本,因为短文本往往信息量低或属于没实际内容的模板页面。
  • 使用中文 BERT 的 tokenizer 把文本切成子词片段。
  • 把所有 token 首尾拼接后,按固定长度切成块,每一块就是一条训练样本。

最后一步group_texts是实践中一个经典技巧。直接按段落切分会导致大多数样本长度不足,造成计算浪费;拼接后再切,可以最大化每个训练样本的利用率。

2.1 数据质量检查清单

很多新手训练完模型发现效果差,第一反应是模型结构有问题,实际原因却在数据。这里给一份经验清单:

  • 重复率:用 MinHash 等算法做近似去重。重复数据会让模型记忆而不是泛化。
  • 语言分布:混合多语言数据时,各语言占比要和目标使用场景匹配,而不是随意混合。
  • 污染检测:确保评测集没有出现在训练集里,否则评测结果虚高。
  • 质量过滤:参考 C4、Gopher 论文的过滤规则,用启发式规则(长度、符号密度、语言识别)筛掉低质量页面。

3. 揉面与发酵:预训练阶段到底在做什么

有了原料,下一步是揉面。在 LLM 训练里,预训练就是“把数据中的语言规律揉进模型参数里”。

这一步的技术定义是:在超大规模无标注文本上,以自监督方式训练 Transformer 模型,最常用的目标函数是自回归语言建模损失(Causal Language Modeling Loss),也就是让模型根据前面的 token 预测下一个 token。

用公式表达就是:

L = - (1/N) * Σ log P( token_i | token_1, token_2, ..., token_{i-1} )

模型看到一句话前 511 个 token,预测第 512 个 token 是什么。训练数据里有正确答案,所以这是标准的监督信号——只是“标签”来自文本本身,因此叫自监督。

很多人问:为什么预测下一个词就能学到知识?

这个问题其实和“为什么揉面能形成面筋”一样。面筋不是加进去的,是揉出来的——反复折叠、拉伸让蛋白质分子重新排列。语言模型也一样,预测下一个词看起来是一个非常简单的任务,但要在海量文本上反复做这件事,模型就不得不学会词法、句法、事实关联、逻辑推理等大量能力,因为这些能力都能帮助降低预测损失。

这就是“涌现”的朴素解释:能力不是被显式编程进去的,而是在足够大的模型上、足够多的文本上,通过预测下一个词这个简单任务被迫发展出来的。

3.1 分布式训练:单卡烤不动

预训练阶段的数据量通常是 TB 级,单张 GPU 根本装不下。工程上需要分布式训练,常见策略有:

  • 数据并行(Data Parallelism):每张卡持有完整模型副本,处理不同 batch,梯度同步。
  • 张量并行(Tensor Parallelism):把单个 Transformer 层的矩阵拆分到多张卡上。
  • 流水线并行(Pipeline Parallelism):把不同层放到不同卡上,数据像流水线一样流过。
  • ZeRO / DeepSpeed 优化:把优化器状态、梯度、参数分片存储,降低显存占用。

这已经是训练框架层面的问题了。对初学者来说,不需要立刻掌握全部细节,但要明白:预训练依赖大规模分布式并行和极高的数据工程能力,这正是个人开发者几乎不可能复现的环节。这也是为什么现在业界的标准路径不是从零预训练,而是“加载开源基座模型 + 领域微调”。

代码层面,如果用 Transformers 搭配 Accelerate,可以很直观地看到数据并行的抽象:

# 文件路径:train_pretrain.py # 一个简化的预训练训练循环示例 import torch from torch.utils.data import DataLoader from transformers import AutoModelForCausalLM, get_cosine_schedule_with_warmup from accelerate import Accelerator accelerator = Accelerator() model_path = "your-base-model-path" # 实际项目替换 model = AutoModelForCausalLM.from_pretrained(model_path) train_dataloader = DataLoader(lm_dataset["train"], batch_size=32, shuffle=True) optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5) scheduler = get_cosine_schedule_with_warmup( optimizer, num_warmup_steps=100, num_training_steps=len(train_dataloader) * 3, ) model, optimizer, train_dataloader, scheduler = accelerator.prepare( model, optimizer, train_dataloader, scheduler ) model.train() global_step = 0 for epoch in range(3): for batch in train_dataloader: batch = {k: v.to(accelerator.device) for k, v in batch.items()} outputs = model(input_ids=batch["input_ids"], labels=batch["input_ids"]) loss = outputs.loss accelerator.backward(loss) optimizer.step() scheduler.step() optimizer.zero_grad() global_step += 1 if global_step % 100 == 0: print(f"step {global_step} loss {loss.item():.4f} lr {scheduler.get_last_lr()[0]:.2e}") # 每个 epoch 结束保存一次检查点 accelerator.save_state(f"./checkpoints/epoch_{epoch}")

这里需要强调:labels=batch["input_ids"]这一行的含义是让模型把「预测下一个 token」作为目标,模型内部会自动把标签左移一位。新手经常在这里写错,导致 loss 不为负却学不到东西。

3.2 预训练时期望看到的 loss 变化

预训练过程中,loss 的典型变化模式是:前期快速下降,中期缓慢下降,后期进入平台期。如果 loss 出现以下异常,要立刻停止排查:

  • loss 为 NaN:学习率过高或数据里有异常值,立即降低学习率或检查数据。
  • loss 不下降:数据 pipeline 有问题,比如 labels 与 input_ids 错位。
  • loss 下降后反弹:学习率调度异常,或 batch size 太小导致梯度不稳定。

4. 整形与二次发酵:继续预训练与领域适配

面包第一次发酵后要排气、整形,有时还会加入不同配料。LLM 训练里对应的环节是“二次训练”:在通用基座模型的基础上,用特定领域数据继续训练。

这有几种常见叫法:

  • 继续预训练(Continued Pretraining / Domain-Adaptive Pretraining)
  • 增量预训练(Incremental Pretraining)

它的目的是让模型更熟悉某个领域的语言风格和知识分布,比如代码、医学、法律、金融。实操上和预训练几乎一样,只是数据从通用语料换成了领域语料,学习率通常调小。

为什么需要这一步?因为通用模型虽然在大量文本上训练过,但领域文本的分布和通用文本差别很大。比如代码数据的特点是结构化强、括号和缩进重要、错误信息频繁出现;法律数据则有大量固定句式,这些分布特征需要在领域数据上继续训练才能被强化。

继续预训练的关键经验:

  • 学习率要比预训练更小,通常用预训练的 1/10 甚至更低。
  • 混合通用数据,防止“灾难性遗忘”(后面会讲)。
  • 这个阶段的评估不是看对话能力,而是看困惑度(Perplexity)和下游任务表现。

5. 入炉烘烤:监督微调(SFT)让模型学会“说话”

面包经过整形后,终于可以入炉了。在 LLM 训练中,监督微调是让模型从“会预测下一个词”变成“会回答用户问题”的关键烘烤步骤

微调和预训练的区别,用一个表格就能说清楚:

维度预训练监督微调(SFT)
数据来源海量无标注文本人工标注的指令-回答对
数据量TB 级数千到百万条
目标学习语言规律和世界知识学会遵循指令、对齐人类偏好
训练时间数周到数月数小时到数天
模型状态基座模型指令微调后的助手模型

SFT 的数据格式非常重要。现在开源生态里通用的格式是对话模板,例如 ChatML:

<|im_start|>system 你是一个有帮助的助手。<|im_end|> <|im_start|>user 介绍一下大模型训练的基本流程。<|im_end|> <|im_start|>assistant 大模型训练通常包含预训练、微调和对齐三个阶段……<|im_end|>

训练时,只有 assistant 回答部分的 token 才参与 loss 计算,system 和 user 部分应该被 mask 掉。这个细节直接决定模型能不能学会“对话”而不是重复整个模板。

用代码实现时,可以用 Transformers 的DataCollatorForCompletionOnlyLM,它专门处理这种“只对回答部分计算损失”的场景:

# 文件路径:train_sft.py from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, DataCollatorForCompletionOnlyLM, ) from datasets import Dataset tokenizer = AutoTokenizer.from_pretrained("your-base-model-path") tokenizer.pad_token = tokenizer.eos_token train_data = [ { "conversation": [ {"role": "user", "content": "什么是 Transformer?"}, {"role": "assistant", "content": "Transformer 是一种基于自注意力机制的神经网络架构,最早由 Vaswani 等人于 2017 年提出。"}, ] }, # ... 实际项目请准备更多数据 ] def format_conversation(example): prompt = "" conv = example["conversation"] for i, turn in enumerate(conv): if turn["role"] == "user": prompt += f"<|im_start|>user\n{turn['content']}<|im_end|>\n" else: prompt += f"<|im_start|>assistant\n{turn['content']}<|im_end|>\n" prompt += "<|im_start|>assistant\n" # 这里需要把目标文本也拼接出来,供 DataCollator 计算 completion 位置 response = conv[-1]["content"] return {"prompt": prompt, "completion": response} formatted_data = [format_conversation(item) for item in train_data] dataset = Dataset.from_list(formatted_data) def tokenize_with_response(example): prompt_ids = tokenizer.encode(example["prompt"], truncation=True, max_length=1024) completion_ids = tokenizer.encode(example["completion"], add_special_tokens=False) input_ids = prompt_ids + completion_ids + [tokenizer.eos_token_id] labels = [-100] * len(prompt_ids) + completion_ids + [tokenizer.eos_token_id] return {"input_ids": input_ids, "labels": labels} tokenized_dataset = dataset.map(tokenize_with_response, remove_columns=["prompt", "completion"]) data_collator = DataCollatorForCompletionOnlyLM( response_template="<|im_start|>assistant\n", tokenizer=tokenizer, ) training_args = TrainingArguments( output_dir="./sft_model", per_device_train_batch_size=4, gradient_accumulation_steps=8, learning_rate=2e-5, num_train_epochs=3, logging_steps=10, save_strategy="epoch", report_to="none", ) trainer = Trainer( model=AutoModelForCausalLM.from_pretrained("your-base-model-path"), args=training_args, train_dataset=tokenized_dataset, data_collator=data_collator, ) trainer.train() trainer.save_model("./sft_model_final")

这段代码的核心在于labels列表中的-100。PyTorch 的交叉熵损失会把-100位置的 token 忽略掉,这样模型在训练时只需要预测 assistant 的回答部分,而不是去预测用户的提问。

这里的response_template="<|im_start|>assistant\n"是告诉 DataCollator 从哪个标记开始才算 completion,这样它就能自动构造出正确的labels

很多新手在微调时遇到同一个问题:模型训练完 loss 很低,但对话时只会把用户的问题和回答一起复述出来。这个现象的根源通常就是 label 没有 mask,模型把“预测用户问题”也当成了学习目标。

如果你用的是 LLaMA 系模型,官方没有 ChatML 模板,需要把response_template换成模型对应的 assistant 起始标记,同时把tokenizer.add_special_tokens处理到位。

5.1 微调数据的关键认知

SFT 的数据质量比数据量更重要。业内不少团队的实践经验是:几千条高质量、覆盖多任务的指令数据,效果往往好于几万条低质量、模板雷同的数据。

高质量 SFT 数据应满足:

  • 指令多样,覆盖真实用户使用场景。
  • 回答准确、信息密度高、逻辑清晰。
  • 避免大量“复读机式”的问答对。
  • 注意安全性和价值观对齐,不包含违规内容。

6. 出炉与试吃:评估、困惑度与人工评测

面包出炉后要先冷却、切开试吃,才知道火候对不对。LLM 训练也是如此,训练结束时必须做系统评估,而且要在训练过程中就建立评估机制。

6.1 客观指标

  • Perplexity(困惑度):语言模型对测试数据的负对数似然的指数形式。困惑度越低,说明模型对文本的预测能力越强。但要注意,困惑度低不代表对话能力强,它只是基础能力的指标。
  • Loss(训练损失):监控训练过程用。
  • 下游任务指标:比如代码生成的 pass@k、数学推理的准确率、阅读理解 EM/F1 等。

6.2 人工评测

真正的对话质量,必须靠人工评测。实践中通常会设计一个评测集,包含几百到几千条覆盖不同能力的 prompt,然后让多个评测者给模型回答打分,对比不同训练版本的优劣。

现在也有很多自动评测框架可以辅助,但最终人工抽检不可替代,尤其是对安全性和语气这类难以量化的维度。

6.3 一个简单的评估脚本

# 文件路径:evaluate_model.py from transformers import AutoModelForCausalLM, AutoTokenizer import torch model_path = "./sft_model_final" tokenizer = AutoTokenizer.from_pretrained(model_path) model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto") model.eval() def generate(prompt, max_new_tokens=200): messages = [{"role": "user", "content": prompt}] input_text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer(input_text, return_tensors="pt").to(model.device) with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=max_new_tokens, do_sample=True, temperature=0.7, top_p=0.9, ) return tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True) test_prompts = [ "用一句话解释什么是注意力机制。", "写一个 Python 函数,计算斐波那契数列。", "大模型训练分为哪几个阶段?", ] for prompt in test_prompts: print(f"Prompt: {prompt}") print(f"Answer: {generate(prompt)}") print("-" * 50)

运行后,逐个检查回答是否流畅、准确、符合指令。这里要特别提醒:评估时必须设置temperaturetop_p等采样参数,固定评估方式和参数,否则同一 prompt 每次生成结果可能不同,不利于对比版本效果。

7. 常见问题与排查方法

问题现象可能原因排查方式解决方案
训练 loss 不下降labels 与 input_ids 错位,模型在预测自己的输入而非下一 token检查数据预处理,打印一个 batch 的 input_ids 和 labels 对比修正 labels 构造逻辑,或使用 DataCollatorForCompletionOnlyLM
loss 变成 NaN学习率过高、数据中存在异常值、混合精度训练精度溢出查看 loss 变 NaN 前的日志和当时的输入数据降低学习率;检查数据;关闭 fp16 或改用 bf16
显存不足(OOM)batch size 过大、序列过长、优化器状态占用太大查看报错栈,确认是模型参数还是激活值占满显存减小 batch size、开启梯度累积、使用 LoRA/QLoRA、使用 DeepSpeed ZeRO
微调后“复读机”现象没有对 prompt 部分做 label mask打印 labels 中的 -100 位置是否正确覆盖 user 部分使用 response_template 自动 mask 回答部分
灾难性遗忘:微调后通用能力下降微调数据分布和预训练分布差异过大,或学习率过高、训练轮数过多在通用评测集上对比基座模型和微调模型混合 5%-20% 通用数据;降低学习率;控制 epoch;使用参数高效微调
评测集和训练集重叠数据泄漏,评测结果虚高计算训练集和评测集的 n-gram 重叠率构建评测集时做相似度去重排除训练数据
生成结果重复单调温度过低、top_p 过小、模型未收敛尝试不同采样参数组合调整 temperature / top_p / repetition_penalty

这里单说两个高频问题。

灾难性遗忘是微调中最常见的隐性坑。模型在领域数据上训练太久,可能会遗忘预训练阶段学到的通用能力。解决思路不是“尽量多训”,而是“控制训练强度”:降低学习率、减少 epochs、混合通用数据、优先选择 LoRA 这类参数高效微调方法。

OOM 的排查逻辑也要说清楚。很多新手第一反应是把 batch size 调小,但有时真正的问题是优化器状态太大。AdamW 优化器每个参数要维护两份额外状态,单个 float32 参数实际占用显存可能是参数本身的 3 倍以上。这时用 AdamW 的 8-bit 版本或换用 Adafactor,效果立竿见影。

8. 工程最佳实践:从“能跑”到“可维护”

训练模型和做面包一样,最大的坑往往不在配方本身,而在流程管理。下面这几条实践经验,是比任何单一技巧都重要的。

8.1 用 LoRA 降门槛

如果你在单个消费级显卡上做微调,不要直接训练全部参数,优先选择 LoRA。

LoRA 的核心思路是冻结原始权重,只训练一小部分低秩增量矩阵。这能把可训练参数量降低到原来的 1% 甚至更低,显存占用大幅下降。用 PEFT 库实现非常方便:

# 文件路径:train_lora.py from peft import LoraConfig, get_peft_model, TaskType lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, r=8, # 低秩矩阵的秩 lora_alpha=32, # 缩放系数 target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], lora_dropout=0.1, ) model = AutoModelForCausalLM.from_pretrained("your-base-model-path") model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 输出示例:trainable params: 4,194,304 || all params: 1,234,567,890 || trainable%: 0.34

LoRA 的额外好处是可插拔。训练完的增量矩阵只有几十到几百 MB,可以单独保存、加载、切换,多个 LoRA 模块可以复用到同一个基座模型上,很适合团队内部做多业务场景的模型复用。

8.2 实验管理:每次训练都要“可复现”

面包师傅会记录每次的配方、温度、时间,模型训练更应该如此。实践建议:

  • 每次实验记录:数据版本(hash 或 commit id)、代码 commit、超参数、训练日志、评测结果。
  • 使用实验管理工具(如 Weights & Biases、MLflow、TensorBoard),或至少用一个统一的实验记录表格。
  • 保存检查点时,同时保存 tokenizer 和配置文件,确保模型可以独立加载。

8.3 评估先行:还没训练就要想好怎么评测

这是最有价值的一条建议。很多人训练完模型才开始设计评测,结果发现评测集不完善,只能重新训练。正确做法是:

  • 训练开始前先构建评测集。
  • 用基座模型跑一遍生成结果作为 baseline。
  • 每次微调后在同一批 prompt 上对比。

如果没有 baseline,你无法判断模型是变好了还是变差了。很多“微调后效果变差”的结论,其实是因为根本没有对比对象。

8.4 节省资源的小技巧

  • 用梯度累积替代直接放大 batch size。
  • 优先用 bf16 而不是 fp16,bf16 在训练稳定性上更优。
  • 检查点保存不要每步都存,用save_strategy="epoch"或按步数间隔保存。
  • 训练结束后用model = model.merge_and_unload()合并 LoRA 权重,部署时零额外开销。

9. 总结:烤箱还是那台烤箱,配方和手艺才是核心

用烘焙来理解 LLM 训练,学到的不是比喻本身,而是流程思维。

  • 数据是面粉,决定模型能力的上限。
  • 预训练是揉面和发酵,把语言规律揉进参数。
  • 继续预训练是整形和二次发酵,让模型适配领域。
  • 微调是入炉烘烤,让模型学会对话和指令遵循。
  • 评估是试吃,没有评估的训练等于闭着眼睛烤面包。
  • 灾难性遗忘是烤过头,OOM 是烤箱塞太满,loss 不降是配方抄错了。

对于正在入门 LLM 训练的同学,我的实战建议是:不要急着从零预训练大模型,也不要上来就追最新的训练框架。先用中文小规模数据跑通一个最小的预训练 + 微调流程,把数据 pipeline、loss 变化、评估方法、领域适配这几个环节全部走一遍,然后再去学习分布式训练和 RLHF。有了完整的流程视角,你再去看任何一篇训练相关的论文或代码,都会清楚它在整个流程里处于哪个位置。

下一步值得深入的方向有三个:一是继续预训练时如何解决灾难性遗忘;二是 SFT 数据配比到底怎么设计才更优;三是 RLHF 中的奖励模型如何评估,它和 SFT 优化目标的差异在哪里。这三块都是通往更高质量助手模型的必经之路,也可以作为你接下来的动手实践主题。

烘焙的关键不是记住一个面包配方,而是理解每一种原料和每一步操作对成品的影响;训练模型也是如此。把这份“配方思维”建立起来,你就已经从“调参侠”迈向了真正理解大模型引擎的开发者。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 2:56:17

R-FCN源码解析:位置敏感得分图与全卷积目标检测网络实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 2:54:21

AI国风纸雕赋能足球文化传播:从LoRA微调到批量生成实践

最近在协助一个体育文化传播项目时&#xff0c;团队一直在思考一个问题&#xff1a;如何让东北超足球文化在年轻群体里“破圈”传播&#xff0c;同时又能保持一定的文化质感和艺术审美。直接拍短视频、做海报当然可以&#xff0c;但同质化严重。后来我们找到了一个比较清奇的创…

作者头像 李华
网站建设 2026/9/8 2:54:10

codex代码生成实用指南:高效掌握AI辅助代码生成的核心方法与应用技巧

AI Agent时代的科研革命 这三个工具让你的效率提升十倍 传统科研模式正在被AI彻底颠覆。过去需要几周甚至几个月完成的文献调研和综述写作&#xff0c;现在几天就能搞定。过去需要反复调试才能复现的实验&#xff0c;现在一键就能完成。这三个基于最新AI技术的科研工具&#x…

作者头像 李华
网站建设 2026/9/8 2:49:12

Word论文页眉页脚设置全攻略:分节、页码、横线一次讲透

这次我们直接讲 Word 页眉页脚设置&#xff0c;重点解决论文排版里最折磨人的那一堆细节&#xff1a;封面不要页脚、摘要和目录用罗马数字、正文从第一页开始用阿拉伯数字、每一章页眉不同、奇偶页页眉不同&#xff0c;还有那条怎么都删不掉的页眉横线。很多人的正文写得很快&a…

作者头像 李华
网站建设 2026/9/8 2:49:03

小范围AI+BI试点中数据治理与决策协同方法

导语 很多企业在启动AIBI落地时&#xff0c;都会陷入一个误区&#xff1a;想要先完成全企业全量数据治理&#xff0c;再推业务试点&#xff0c;结果往往是治理周期拉得很长&#xff0c;业务侧迟迟看不到效果&#xff0c;试点项目不了了之。实际上&#xff0c;小范围AIBI试点阶段…

作者头像 李华