硕士校招生进入大模型领域工作,选预训练还是SFT?

引言

大语言推理模型常见的两个痛点:一是“答不对”,二是“答不规范”。前者意味着推理链条断裂或迁移失效,后者则让后续评估与系统对接步步惊心。

在这篇 How‑to 指南里,我们用 GSM8K 的标准数据与明确的评估规则,让 Qwen/Qwen2.5-Math-1.5B 通过 SFT 后,能力迁移到不同的数据集上, 学会“思考→作答”的结构化输出:不仅更常答对,也更懂规范。结果来自实测,正确率由 1.56% 提升到 62.9%,格式遵循由 18.9% 提升到 100%.


为什么零样本推理和“格式遵循”都很难?

  • 零样本推理难在“迁移”:模型虽见过大量文本,但缺少对“分步算术、单位处理、等式化简”的系统化经验,容易在多步推理或细节规范上出错。
  • 格式遵循难在“约束学习”:即使模型知道答案,若不按系统约定的输出协议(例如必须有 <think>...</think> <answer>...</answer>),评估与下游解析都会失败。
  • 两者耦合:不遵循格式会直接“判零分”,即使答案正确;而缺少分步推理(think)又会影响最终答案(answer)的稳定性。

一个好比喻:把模型想象成一个聪明但散漫的学生。零样本时,它能“蒙”对少数题;SFT 就像班主任的“规范化带教”,教它先写草稿()再交最终答案(),且必须按卷面格式来——这样既能提高质量,也能让阅卷更可靠。


核心概念与评估口径

  • 监督微调(SFT):用标注样本(这里是 GSM8K 的“问题 + 推理过程 + 最终答案”)对模型做下一 token 预测训练。通过标签掩码,只对“推理与答案”部分计算损失,指导模型输出完整的思维链与答案。
  • Prompt 架构:我们采用 R1 风格模板,强制输出 <think><answer> 标签,保证格式可解析:
  • <think> ... </think>:推理过程
  • <answer> ... </answer>:最终答案
  • 训练目标(Loss Target):标准自回归语言模型(next-token LM)损失,但只在“回应(think+answer)”区间计算,避免模型学习到重复的“系统提示与问题”的 token 序列。
  • 评估指标:
  • “推理准确率”:按每条样本的 答案是否正确(数学同值、LaTeX 等价、数值等价,详见 grader)计分 0/1,最后取平均。
  • “格式遵循率”:按每条样本 是否包含合法的 <think>...</think> <answer>...</answer> 标签计分 0/1,最后取平均。

我们在 alignment/drgrpo_grader.py 中实现了严格的格式检查与宽容但可靠的数学等价判断(符号化、数值化与 LaTeX 解析的组合),是本文准确率与格式遵循的核心评估逻辑。


方案与架构

本文的流水线架构如下:从 GSM8K JSONL 到 Prompt 构造,再到 SFT 训练与 vLLM 推理评估,最后汇总指标(准确率/格式遵循)。

flowchart LR
  A["GSM8K JSONL 数据集"] --> B["R1PromptTemplate 构造 Prompt"]
  B --> C["SFT 训练 (next-token LM)"]
  C --> D["保存 Checkpoint"]
  D --> E["vLLM 推理评估"]
  E --> F["r1_zero_reward_fn 计算格式与正确性"]
  F --> G["输出指标: 准确率 & 格式遵循"]

关键路径对应的源码均在 alignment 目录下:dataset.py(数据加载)、r1_prompt.py(模板)、sft.py(训练)、evaluate.py(评估)、drgrpo_grader.py(格式与答案打分)。


可复现配置与注意事项

  • 模型与精度:默认使用 Qwen/Qwen2.5-Math-1.5B,dtype 默认 bfloat16(见 alignment/args.py)。
  • 设备与显存:alignment/sft.py 通过 accelerate 的 infer_auto_device_map 自动切分模型到多卡(--sft_device),并设置 --max_sft_gpu_memory_use(默认 31GiB/卡)。评估使用 vLLM 独立进程(--eval_device)。
  • 随机性:--seed 默认 42。不同环境(驱动、库版本、显存压力)与随机种子可能导致轻微波动。
  • 批次与累积:--batch_size--gradient_accumulation_steps 控制有效批次大小,训练日志会打印累计后的损失。
  • 提示模板:alignment/prompts/r1_zero.prompt 强制 <think>/<answer> 标签,保证格式可检。

实践步骤:从数据到评估与训练

  1. 下载 GSM8K(train/test)到本地 data 目录(可根据你的项目目录调整):
cd dataset
wget https://raw.githubusercontent.com/openai/grade-school-math/master/grade_school_math/data/train.jsonl
wget https://raw.githubusercontent.com/openai/grade-school-math/master/grade_school_math/data/test.jsonl

建议将文件移动/复制为仓库约定路径:data/gsm8k/train.jsonldata/gsm8k/test.jsonl(alignment/args.py 默认如此)。

  1. 微调前的零样本评估(使用 vLLM + R1 模板 + 严格格式与答案打分):
uv run -m alignment.evaluate
  1. 执行 SFT 训练并在测试集上评估(训练中每个 epoch 结束都会评估并保存 checkpoint):
uv run -m alignment.sft

代码讲解:数据加载与样本构造(包含 / 标签)

首先用 R1 模板将问题转为 Prompt,将“推理过程 + 最终答案”打包为监督信号。alignment/dataset.py 与 r1_prompt.py 如下:

# alignment/dataset.py
from torch.utils.data import Dataset
import json
from .r1_prompt import R1PromptTemplate

class Gsm8kDataset(Dataset):
    def __init__(self, data_path: str, promt_template_path: str):
        template = R1PromptTemplate(promt_template_path)
        self.data = []
        self.label = []
        self.ground_truth = []
        with open(data_path, "r") as f:
            lines = f.readlines()
        for line in lines:
            qa = json.loads(line)
            question = qa["question"]
            answer_think = qa["answer"]
            think, answer = answer_think.split("####")
            think, answer = think.strip(), answer.strip()
            self.data.append(template.gen_prompt(question))
            self.label.append(template.gen_response(think, answer))
            self.ground_truth.append(answer)

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx], self.label[idx], self.ground_truth[idx]
# alignment/r1_prompt.py(关键:强制 <think>/<answer> 标签)
class R1PromptTemplate:
    def __init__(self, template_path: os.PathLike):
        with open(template_path, "r") as f:
            self.template = f.read().strip()

    def gen_prompt(self, question: str) -> str:
        return self.template.replace(r"{question}", question)

    def gen_response(self, think: str, answer: str) -> str:
        return think + "</think>" + " <answer>" + answer + " </answer>"

模板文件 alignment/prompts/r1_zero.prompt:

A conversation between User and Assistant... <think> reasoning process here </think> <answer> answer here </answer>.
User: {question}
Assistant: <think>

这保证了训练时模型学习“先写 <think> 再写 <answer>”,评估时也能稳定抽取并验证答案。


代码讲解:SFT 训练循环与标签掩码(只对回应部分计算损失)

在 alignment/sft.py,我们将 prompt + completion 拼接,对 prompt 区间的 label 置为 -100,从而让交叉熵只在“回应(think + answer)”上回传梯度:

# alignment/sft.py(节选)
model.train()
for epoch in range(args.epochs):
    for i, batch in enumerate(train_data_loader):
        prompts, completions, _ = batch

        full_texts = [p + c + tokenizer.eos_token for p, c in zip(prompts, completions)]
        inputs = tokenizer(
            full_texts,
            return_tensors="pt",
            padding=True,
            truncation=True,
            max_length=args.max_seq_len,
        ).to(model.device)

        # 计算每个样本的 prompt token 长度
        prompt_tokens = tokenizer(list(prompts), add_special_tokens=False)
        prompt_lengths = [len(ids) for ids in prompt_tokens.input_ids]

        labels = inputs.input_ids.clone()
        for idx in range(len(prompts)):
            prompt_len = prompt_lengths[idx]
            labels[idx, :prompt_len] = -100  # 对 prompt 部分不计 loss

        # Mask padding tokens
        labels[labels == tokenizer.pad_token_id] = -100

        outputs = model(
            input_ids=inputs.input_ids,
            attention_mask=inputs.attention_mask,
            labels=labels,
        )
        loss = outputs.loss
        loss = loss / args.gradient_accumulation_steps
        loss.backward()

        if (i + 1) % args.gradient_accumulation_steps == 0:
            print(f"Epoch {epoch}, Iteration {i}, Loss: {loss.item() * args.gradient_accumulation_steps}")
            optimizer.step()
            optimizer.zero_grad()

    # 评估:将策略权重加载到 vLLM 并在测试集上跑评估
    load_policy_into_vllm_instance(model, eval_model)
    evaluate_math(
        eval_model,
        args.prompt_template_path,
        args.sft_test_data,
        args.batch_size,
        log_sample=True,
    )
    model.save_pretrained(args.checkpoint_path)

这段逻辑实现了标准的 SFT:用“回应”作为监督目标,配合累积梯度与分卡策略,保证 1.5B 规模模型在可控显存与吞吐下完成训练。


代码讲解:评估与指标计算(准确率与格式遵循)

评估由 alignment/evaluate.py 驱动,调用 r1_zero_reward_fn 计算每条样本的格式与答案得分:

# alignment/evaluate.py(核心)
from collections.abc import Callable
from vllm import LLM, SamplingParams
from .drgrpo_grader import r1_zero_reward_fn

def evaluate_vllm(vllm_model: LLM,
                  reward_fn: Callable[[str, str], dict[str, float]],
                  prompts: list[str],
                  ground_truths: list[str],
                  eval_sampling_params: SamplingParams,
                  log_sample: bool) -> dict:
    outputs = vllm_model.generate(prompts, eval_sampling_params)
    generated_texts = [output.outputs[0].text for output in outputs]
    rewards = [reward_fn(generated_text, ground_truth)
               for generated_text, ground_truth in zip(generated_texts, ground_truths)]

    avg_format_rewards = sum([r["format_reward"] for r in rewards]) / len(prompts)
    avg_answer_rewards = sum([r["answer_reward"] for r in rewards]) / len(prompts)
    avg_all_rewards = sum([r["reward"] for r in rewards]) / len(prompts)
    print(f"avg_format_rewards: {avg_format_rewards}")
    print(f"avg_answer_rewards: {avg_answer_rewards}")
    print(f"avg_all_rewards: {avg_all_rewards}")
    return {
        "avg_format_rewards": avg_format_rewards,
        "avg_answer_rewards": avg_answer_rewards,
        "avg_all_rewards": avg_all_rewards,
    }

奖励函数严格要求格式标签,并对答案进行多重等价校验:

# alignment/drgrpo_grader.py(节选)
def r1_zero_reward_fn(response, ground_truth, fast=True):
    # 格式严格:必须包含 </think> <answer> ... </answer>
    if "</think> <answer>" in response and "</answer>" in response:
        model_answer = response.split("<answer>")[-1].replace("</answer>", "")
        # 允许 \boxed{...} 的答案形式并抽取
        if "\\boxed" in model_answer:
            model_answer = extract_answer(model_answer)
            if model_answer is None:
                return {"format_reward": 1.0, "answer_reward": 0.0, "reward": 0.0}
        # 字符串、数值、LaTeX、SymPy 等价综合判断
        is_correct = grade(model_answer, str(ground_truth), fast)
        if is_correct:
            return {"format_reward": 1.0, "answer_reward": 1.0, "reward": 1.0}
        else:
            return {"format_reward": 1.0, "answer_reward": 0.0, "reward": 0.0}
    else:
        # 不符合格式:直接判 0 分(同时 answer_reward 也为 0)
        return {"format_reward": 0.0, "answer_reward": 0.0, "reward": 0.0}

因此:

  • 格式遵循率 = 所有样本的 format_reward 平均值。
  • 推理准确率 = 所有样本的 answer_reward 平均值(在格式正确的前提下计算答案是否等价)。

数据读取与 Ground Truth 解析(alignment/evaluate.py):

# alignment/evaluate.py(节选)
def get_gsm8k_test_data(test_data_path: os.PathLike) -> list[dict]:
    data = []
    with open(test_data_path, "r") as f:
        for line in f.readlines():
            obj = json.loads(line.strip())
            ts = obj["answer"].split("####")
            if len(ts) != 2:
                print(f"invalid answer: {obj['answer']}")
                continue
            data.append({"question": obj["question"],
                         "think": ts[0].strip(),
                         "answer": ts[1].strip()})
    return data

结果与讨论

在同一评估口径下,我们在 GSM8K 上得到如下对比(报告值):

指标微调前(零样本)SFT 微调后
推理准确率1.56%62.9%
格式遵循率18.9%100%

说明:

  • 上述结果是在 alignment.evaluate 与 alignment.sft 的评估框架下得到的报告值。不同硬件/软件环境与随机种子可能引起轻微差异。
  • “格式遵循率”达到 100% 的关键是训练时的模板约束与监督信号覆盖 <think>/<answer>,并在奖励函数中严格判定标签存在性。

常见问题与优化建议

  • 训练不收敛或损失震荡:适当降低学习率(如 1e‑5 → 5e‑6)、增大 gradient_accumulation_steps,或提高 max_seq_len 以覆盖完整推理链。
  • 显存不足:增加 --sft_device 的卡数或调大 --max_sft_gpu_memory_use,必要时裁剪序列长度或使用更小 batch。
  • 评估速度慢:调小 SamplingParamsmax_tokens;但过小的生成长度会影响完整 <think>/<answer> 输出与正确率。
  • 格式仍偶发不合规:检查模板与数据是否一致(是否始终以 <think> 开头),并确保 SFT 标签覆盖足量样本。

结论

通过对 Qwen/Qwen2.5-Math-1.5BGSM8K 上的监督微调,我们实现了两条主线的同步提升:

  • 推理链条更稳,零样本正确率从 1.56% 提升到 62.9%
  • 输出规范更强,格式遵循率从 18.9% 提升到 100%

这背后的关键是“结构化输出模板 + 只对回应部分计损失 + 严格但宽容的评估规则”。如果你也在做数学推理或其他需要“思考‑答案二段式”输出的任务,强烈建议复用本文的架构与代码。

行动号召:现在就下载 GSM8K,跑一遍 uv run -m alignment.evaluateuv run -m alignment.sft,观察你本地的改进幅度吧! 可以参考 llm-from-scratch 仓库中 alignment 模块,对照进行学习。

开放问题:在你的场景里,是否遇到过“答案正确但格式导致系统解析失败”的案例?你是如何设计模板与评估逻辑来避免它的?欢迎在评论区分享你的经验与挑战。

如果你也想系统学习AI大模型技术,想通过这项技能真正达到升职加薪,就业或是副业的目的,但是不知道该如何开始学习*_,因为网上的资料太多太杂乱了,如果不能系统的学习就相当于是白学。
为了帮助大家打破壁垒,快速了解大模型核心技术原理,学习相关大模型技术。从原理出发真正入局大模型。在这里我和MoPaaS魔泊云联合梳理打造了系统大模型学习脉络,这份 LLM大模型资料 分享出来:包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等, 😝有需要的小伙伴,可以 扫描下方二维码免费领取🆓**⬇️⬇️⬇️

在这里插入图片描述

【大模型全套视频教程】

教程从当下的市场现状和趋势出发,分析各个岗位人才需求,带你充分了解自身情况,get 到适合自己的 AI 大模型入门学习路线。

从基础的 prompt 工程入手,逐步深入到 Agents,其中更是详细介绍了 LLM 最重要的编程框架 LangChain。最后把微调与预训练进行了对比介绍与分析。

同时课程详细介绍了AI大模型技能图谱知识树,规划属于你自己的大模型学习路线,并且专门提前收集了大家对大模型常见的疑问,集中解答所有疑惑!

在这里插入图片描述

深耕 AI 领域技术专家带你快速入门大模型

跟着行业技术专家免费学习的机会非常难得,相信跟着学习下来能够对大模型有更加深刻的认知和理解,也能真正利用起大模型,从而“弯道超车”,实现职业跃迁!

在这里插入图片描述

【精选AI大模型权威PDF书籍/教程】

精心筛选的经典与前沿并重的电子书和教程合集,包含《深度学习》等一百多本书籍和讲义精要等材料。绝对是深入理解理论、夯实基础的不二之选。

在这里插入图片描述

【AI 大模型面试题 】

除了 AI 入门课程,我还给大家准备了非常全面的**「AI 大模型面试题」,**包括字节、腾讯等一线大厂的 AI 岗面经分享、LLMs、Transformer、RAG 面试真题等,帮你在面试大模型工作中更快一步。

【大厂 AI 岗位面经分享(92份)】

图片

【AI 大模型面试真题(102 道)】

图片

【LLMs 面试真题(97 道)】

图片

【640套 AI 大模型行业研究报告】

在这里插入图片描述

【AI大模型完整版学习路线图(2025版)】

明确学习方向,2025年 AI 要学什么,这一张图就够了!

img

👇👇点击下方卡片链接免费领取全部内容👇👇

在这里插入图片描述

抓住AI浪潮,重塑职业未来!

科技行业正处于深刻变革之中。英特尔等巨头近期进行结构性调整,缩减部分传统岗位,同时AI相关技术岗位(尤其是大模型方向)需求激增,已成为不争的事实。具备相关技能的人才在就业市场上正变得炙手可热。

行业趋势洞察:

  • 转型加速: 传统IT岗位面临转型压力,拥抱AI技术成为关键。
  • 人才争夺战: 拥有3-5年经验、扎实AI技术功底真实项目经验的工程师,在头部大厂及明星AI企业中的薪资竞争力显著提升(部分核心岗位可达较高水平)。
  • 门槛提高: “具备AI项目实操经验”正迅速成为简历筛选的重要标准,预计未来1-2年将成为普遍门槛。

与其观望,不如行动!

面对变革,主动学习、提升技能才是应对之道。掌握AI大模型核心原理、主流应用技术与项目实战经验,是抓住时代机遇、实现职业跃迁的关键一步。

在这里插入图片描述

01 为什么分享这份学习资料?

当前,我国在AI大模型领域的高质量人才供给仍显不足,行业亟需更多有志于此的专业力量加入。

因此,我们决定将这份精心整理的AI大模型学习资料,无偿分享给每一位真心渴望进入这个领域、愿意投入学习的伙伴!

我们希望能为你的学习之路提供一份助力。如果在学习过程中遇到技术问题,也欢迎交流探讨,我们乐于分享所知。

*02 这份资料的价值在哪里?*

专业背书,系统构建:

  • 本资料由我与MoPaaS魔泊云的鲁为民博士共同整理。鲁博士拥有清华大学学士美国加州理工学院博士学位,在人工智能领域造诣深厚:

    • 在IEEE Transactions等顶级学术期刊及国际会议发表论文超过50篇
    • 拥有多项中美发明专利。
    • 荣获吴文俊人工智能科学技术奖(中国人工智能领域重要奖项)。
  • 目前,我有幸与鲁博士共同进行人工智能相关研究。

在这里插入图片描述

内容实用,循序渐进:

  • 资料体系化覆盖了从基础概念入门核心技术进阶的知识点。

  • 包含丰富的视频教程实战项目案例,强调动手实践能力。

  • 无论你是初探AI领域的新手,还是已有一定技术基础希望深入大模型的学习者,这份资料都能为你提供系统性的学习路径和宝贵的实践参考助力你提升技术能力,向大模型相关岗位转型发展

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

抓住机遇,开启你的AI学习之旅!

在这里插入图片描述

### 大模型预训练与监督微调 #### 预训练阶段 大型语言模型的预训练通常采用自回归或自编码的方式完成。例如,GPT系列模型通过单向的语言建模目标来学习文本表示[^1]。这意味着,在给定前一部分文字的情况下,模型能够预测后续的文字内容。这种机制使得GPT模型能够在大规模无标注数据上进行有效的预训练。 相比之下,其他类型的预训练方法可能涉及掩码语言建模(Masked Language Modeling, MLM)。这种方法通过对输入序列中的某些词进行随机遮蔽并让模型预测这些被遮蔽的部分,从而增强其双向上下文理解能力[^3]。 #### 监督微调(Supervised Fine-Tuning) 在完成了基础的大规模预训练之后,为了使模型适应特定的任务需求或者领域特性,可以对其进行有监督的学习调整——即所谓的监督微调(Supervised Fine-Tuning, SFT)[^2]。在此过程中,利用带有标签的小样本集进一步优化网络参数,以便更好地解决实际应用场景下的问题。 下面是一个简单的Python代码示例展示如何加载预训练好的transformer模型,并执行分类任务上的finetune操作: ```python from transformers import BertForSequenceClassification, AdamW, BertTokenizerFast import torch # 加载预训练Bert模型和分词工具 model_name = 'bert-base-uncased' tokenizer = BertTokenizerFast.from_pretrained(model_name) model = BertForSequenceClassification.from_pretrained(model_name) optimizer = AdamW(model.parameters(), lr=5e-5) def train_step(input_ids, attention_mask, labels): outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels) loss = outputs.loss loss.backward() optimizer.step() # 假设我们已经有了tokenized_inputs以及对应的label列表 for epoch in range(num_epochs): for batch in dataloader: input_ids = batch['input_ids'] attention_mask = batch['attention_mask'] labels = batch['labels'] train_step(input_ids, attention_mask, labels) ``` 上述脚本展示了基于HuggingFace库实现的一个基本流程:先初始化所需组件;接着定义损失函数计算方式;最后迭代整个训练过程直至收敛为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值