基于Mixture of Recursions的大语言模型递归推理能力微调实战

发布时间:2026/5/15 12:19:17

基于Mixture of Recursions的大语言模型递归推理能力微调实战 1. 项目概述与核心价值最近在折腾一些文本生成和代码补全的本地化方案发现了一个挺有意思的仓库raymin0223/mixture_of_recursions。这名字乍一看有点绕但如果你对开源的大语言模型LLM微调领域有所关注尤其是那些致力于提升模型推理和代码能力的项目这个仓库很可能就是你一直在找的“宝藏”。简单来说它是一个专门用于训练和评估“递归思维”或“递归推理”能力的混合数据集与工具集。所谓“递归”在这里并不是指编程中的函数递归而是指一种让模型学会“一步一步思考”的能力类似于 Chain-of-Thought思维链但更侧重于在复杂问题尤其是数学、算法、逻辑推理上引导模型进行多步骤、有层次、可回溯的深度推理。为什么这个项目值得关注在当下虽然基础大模型在通用对话上表现不俗但一到需要严谨逻辑、多步推导的硬核任务上比如解一道高中数学题、分析一段复杂代码的逻辑、或者进行科学计算模型就容易“胡言乱语”或“一步跳错满盘皆输”。mixture_of_recursions瞄准的正是这个痛点。它通过精心构建的数据和训练方法旨在赋予模型更强的“递归推理”肌肉让模型不仅能给出答案更能展示出逼近人类专家解题的思考过程。对于开发者、研究者或者任何想打造一个在 STEM科学、技术、工程、数学领域更可靠助手的人来说这个项目提供了从数据到方法的一整套可复现的 pipeline。2. 核心设计思路与方案选型2.1 “递归推理”的本质与数据构建逻辑项目的核心思想“Mixture of Recursions” (MoR) 是一种训练范式。它不同于简单的指令微调SFT或偏好对齐RLHF其重点在于塑造模型的推理结构。我们可以把模型的推理过程想象成一颗树根节点是问题叶子节点是最终答案中间的枝干就是一步步的推理步骤。普通的训练可能只关心叶子节点答案对不对而 MoR 则要求模型学会生成整棵“推理树”并且这棵树的结构递归的深度、分支的选择本身就是学习的目标之一。为了实现这一点项目的数据集构建是关键。据分析其数据 likely 混合了多种来源人工精校的推理链数据从数学竞赛如 MATH、编程题库如 Codeforces、LeetCode、科学问答如 ScienceQA中筛选问题并由人工或强模型如 GPT-4生成高质量、步骤详尽且正确的推理过程。这些数据是“黄金标准”教模型什么是好的递归推理。合成与增强数据通过程序化方法对现有问题进行变换。例如给定一个代数问题可以自动生成其解题过程中间步骤的变体或者构造一些需要“先解决子问题A再结合子问题B的结果解决主问题C”的嵌套式问题。这能大幅增加数据多样性让模型见识到各种递归模式。错误推理与纠正数据为了让模型更鲁棒数据集中可能还包含一些带有常见逻辑错误的推理链并标注了错误所在步骤及纠正后的版本。这有助于模型学会在推理过程中进行自我验证和回溯。注意数据质量是这类项目的生命线。一个常见的坑是直接使用模型生成的推理链而不加严格校验这会导致“垃圾进垃圾出”模型可能学会的是流畅但错误的推理模式。因此在自行构建或扩充数据时必须投入精力进行正确性验证。2.2 模型架构与训练策略选择项目通常不会从头训练一个模型而是基于一个优秀的开源基础模型如 Llama 3、Qwen 2.5、DeepSeek-Coder 等进行继续预训练或指令微调。选择哪个基座模型取决于目标领域通用推理与数学Llama 3、Qwen 2.5 等综合能力强的模型是好的起点。代码与算法推理DeepSeek-Coder、CodeLlama 等代码预训练模型具有天然优势因为它们对编程语法、数据结构有更深的理解。在训练策略上MoR 可能采用以下一种或多种组合监督式微调 (SFT)使用上述构建的问题递归推理链答案三元组数据进行训练。损失函数不仅计算最终答案的 token还会对推理链每一步的 token 进行计算给予推理过程足够的权重。过程监督 (Process Supervision)比普通 SFT 更进一步不仅仅在序列末尾计算损失可能对推理链中的每一个关键步骤如一个等式推导、一个结论陈述都设有监督信号确保每一步的准确性。课程学习 (Curriculum Learning)从简单的、递归深度浅的问题开始训练逐步过渡到复杂的、需要深度递归和多分支推理的问题。这符合人类的学习规律能让训练更稳定、高效。项目的价值在于它可能提供了一套整合了这些数据构建方法和训练策略的标准化代码让研究者可以更方便地复现和实验而不是从零开始搭建整个系统。3. 核心组件与实操要点解析3.1 数据集结构与处理流程要使用或借鉴这个项目首先需要理解其数据格式。一个典型的 MoR 数据样本可能如下所示JSON 格式{ problem: 一个水池有甲、乙两个进水管单开甲管10小时可注满单开乙管15小时可注满。现两管同时打开几小时后可注满水池的2/3, reasoning_chain: [ { step: 1, content: 第一步确定工作效率。甲管每小时注满水池的 1/10乙管每小时注满水池的 1/15。, type: definition }, { step: 2, content: 第二步计算合作效率。两管齐开每小时注水量为 1/10 1/15 1/6。, type: calculation }, { step: 3, content: 第三步设定目标。需要注满水池的 2/3。, type: goal }, { step: 4, content: 第四步计算所需时间。时间 工作量 / 效率 (2/3) / (1/6) 4 小时。, type: calculation, is_final: true } ], answer: 4小时, domain: math, difficulty: middle, chain_depth: 4 }处理流程要点数据清洗去除包含乱码、格式错误、或答案明显矛盾的样本。对于数学和代码问题可以使用解释器如 Python 的eval或sympy或执行器来验证最终答案的正确性。Tokenization 对齐确保推理链文本和问题文本在送入模型前经过与基座模型一致的 tokenizer 处理。特别注意中文、数学符号、代码片段的 token 化是否合理。数据增强对于数量不足的领域或难度级别可以采用回译用不同模型重写推理步骤、参数扰动修改问题中的数字、名称等方法进行增强但必须再次验证增强后数据的逻辑正确性。3.2 训练循环的关键配置与监控训练部分的代码通常是基于 PyTorch 和 Hugging FaceTransformers库构建的。关键配置和监控点包括损失函数通常使用标准的交叉熵损失。但需要确保在计算损失时labels掩码正确地将推理链和答案部分包含在内而忽略 padding 部分。有时会对推理链部分的 token 损失赋予一个权重因子如 0.7对答案部分赋予另一个权重如 0.3以强调过程学习。学习率调度采用带热身的线性衰减或余弦衰减调度器是常见选择。对于继续预训练学习率通常设置得较低如 5e-6 到 1e-5对于指令微调可以稍高如 1e-5 到 2e-5。梯度累积与裁剪由于递归推理数据序列可能较长导致单卡 batch size 受限使用梯度累积来模拟更大的 batch size 是必要的。同时设置梯度裁剪如max_grad_norm1.0以防止训练不稳定。评估指标除了传统的验证集损失必须设计针对推理能力的评估指标。例如步骤准确性使用另一个强模型或规则评估生成推理链中每一步的正确性。答案匹配率最终答案的准确率。推理链流畅度/一致性通过模型自评或 N-gram 重复率等指标间接评估。项目可能会集成像LLM-Eval或InstructEval这样的评估框架。实操心得在训练初期务必频繁地比如每 100 个 step在验证集上生成样例人工检查模型生成的推理链。观察它是开始学会分步骤了还是在胡编乱造。早期的人工检查比任何自动指标都更能发现问题如数据格式错误、标签对齐问题。4. 从零开始复现与定制化训练流程假设我们想基于 Qwen2.5-7B 模型和一部分数学数据复现 MoR 训练的核心流程。以下是详细步骤。4.1 环境准备与依赖安装首先准备一个具有足够显存至少 24GB用于 7B 模型全参数微调的 GPU 环境。使用 Conda 创建环境并安装依赖。# 创建并激活环境 conda create -n mor_train python3.10 -y conda activate mor_train # 安装 PyTorch (请根据你的 CUDA 版本调整) pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装 Hugging Face 核心库及其他依赖 pip install transformers datasets accelerate peft bitsandbytes pip install scipy sentencepiece tiktoken # 可能需要的 tokenizer 依赖 pip install wandb # 用于实验跟踪可选但推荐4.2 数据准备与格式化假设我们有一个名为math_recursion.jsonl的数据文件每行是前面提到的 JSON 格式。我们需要将其转换为 Hugging Face Dataset 对象并处理成模型输入的格式。from datasets import load_dataset, Dataset import json # 1. 加载数据 def load_jsonl(file_path): data [] with open(file_path, r, encodingutf-8) as f: for line in f: data.append(json.loads(line)) return data raw_data load_jsonl(math_recursion.jsonl) dataset Dataset.from_list(raw_data) # 2. 定义格式化函数将推理链拼接成指令 def format_instruction(example): problem example[problem] # 将推理链的每一步用换行符连接 reasoning_text \n.join([fStep {s[step]}: {s[content]} for s in example[reasoning_chain]]) # 构建指令文本。这里采用 Alpaca 风格可根据基座模型调整。 formatted_input fBelow is a problem that requires reasoning. Provide a step-by-step reasoning chain first, then give the final answer. Problem: {problem} Reasoning: {reasoning_text} Final Answer: {example[answer]} example[formatted_text] formatted_input return example # 应用格式化 dataset dataset.map(format_instruction) # 3. 数据集拆分 dataset dataset.train_test_split(test_size0.1, seed42) train_dataset dataset[train] eval_dataset dataset[test]4.3 模型加载与训练配置这里我们使用 Qwen2.5-7B并采用全参数微调。为了节省显存可以使用bitsandbytes库进行 4-bit 量化加载。from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer import torch from peft import LoraConfig, get_peft_model, TaskType # 如果想用 LoRA 微调 model_name Qwen/Qwen2.5-7B-Instruct tokenizer AutoTokenizer.from_pretrained(model_name, trust_remote_codeTrue) # 设置 padding token if tokenizer.pad_token is None: tokenizer.pad_token tokenizer.eos_token # 使用 4-bit 量化加载模型大幅减少显存占用 model AutoModelForCausalLM.from_pretrained( model_name, torch_dtypetorch.bfloat16, # 使用 BF16 精度 device_mapauto, load_in_4bitTrue, # 4-bit 量化 bnb_4bit_compute_dtypetorch.bfloat16, bnb_4bit_use_double_quantTrue, bnb_4bit_quant_typenf4, trust_remote_codeTrue ) # 定义数据整理函数 def data_collator(features): texts [f[formatted_text] for f in features] # Tokenize设置 padding 和 truncation batch tokenizer( texts, paddingTrue, truncationTrue, max_length2048, # 根据你的数据长度调整 return_tensorspt ) # 对于因果语言模型labels 就是 input_ids batch[labels] batch[input_ids].clone() return batch # 配置训练参数 training_args TrainingArguments( output_dir./qwen_mor_finetuned, num_train_epochs3, per_device_train_batch_size2, # 根据显存调整 per_device_eval_batch_size2, gradient_accumulation_steps8, # 有效 batch size 2 * 8 16 warmup_steps100, logging_steps50, eval_steps200, save_steps500, evaluation_strategysteps, save_strategysteps, load_best_model_at_endTrue, metric_for_best_modeleval_loss, greater_is_betterFalse, learning_rate2e-5, weight_decay0.01, fp16False, # 使用 bfloat16 时关闭 fp16 bf16True, # 启用 bfloat16 训练A100/3090/4090等显卡支持 tf32True, # 在支持 TF32 的卡上启用 gradient_checkpointingTrue, # 进一步节省显存 report_towandb, # 可选 run_nameqwen-mor-v1 )4.4 执行训练与生成验证创建 Trainer 并开始训练。trainer Trainer( modelmodel, argstraining_args, train_datasettrain_dataset, eval_dataseteval_dataset, data_collatordata_collator, # 可以自定义 compute_metrics 函数 ) trainer.train()训练结束后进行简单的生成测试def generate_reasoning(model, tokenizer, problem_text): prompt fBelow is a problem that requires reasoning. Provide a step-by-step reasoning chain first, then give the final answer. Problem: {problem_text} Reasoning: inputs tokenizer(prompt, return_tensorspt).to(model.device) outputs model.generate( **inputs, max_new_tokens512, temperature0.7, top_p0.9, do_sampleTrue, pad_token_idtokenizer.pad_token_id, eos_token_idtokenizer.eos_token_id ) result tokenizer.decode(outputs[0], skip_special_tokensTrue) # 只输出生成的部分 generated_part result.split(Reasoning:)[-1].strip() return generated_part # 测试 test_problem 一个数的40%比它的15%多50这个数是多少 reasoning_output generate_reasoning(model, tokenizer, test_problem) print(模型生成的推理链和答案) print(reasoning_output)5. 常见问题、排查技巧与效果优化在实际操作中你肯定会遇到各种问题。下面是一些典型问题及其解决思路。5.1 训练过程中的问题问题1损失Loss不下降或震荡剧烈。检查数据首先确认数据质量。随机采样一些训练样本检查formatted_text的格式是否正确推理链是否连贯。一个常见错误是数据清洗不干净混入了大量无效或格式错误的样本。检查学习率学习率可能过高。尝试降低学习率例如从 2e-5 降到 5e-6并增加 warmup 步数。检查梯度启用TrainingArguments中的gradient_accumulation_steps并确保per_device_train_batch_size不是太小至少为1。监控梯度范数如果出现 NaN 或极大值启用梯度裁剪max_grad_norm1.0。精度问题如果使用fp16混合精度训练在有些模型上可能导致不稳定。可以尝试切换到bf16如果硬件支持或者直接使用fp32全精度但更耗显存。问题2模型生成的内容重复或逻辑混乱。推理链数据质量模型可能在学习有缺陷的推理模式。检查数据集中推理链是否存在循环论证、跳跃过大或事实错误。生成参数在推理时调整temperature(降低以减少随机性如 0.3-0.7)、top_p(0.8-0.95) 和repetition_penalty(1.1-1.2)。训练不足或过拟合如果训练周期太短模型可能没学会推理结构如果周期太长可能在训练集上过拟合丧失泛化能力。通过验证集损失和人工评估来寻找最佳 epoch。5.2 评估与效果优化如何有效评估“递归推理”能力自动评估始终是难点。一个实用的组合策略是答案正确率使用精确匹配或模糊匹配对于数字答案计算最终答案的准确率。这是硬指标。步骤分解评估编写规则或使用轻量级模型检查生成文本中是否包含“Step 1”、“首先”、“接着”等分步关键词以及步骤数量是否合理。人工评估集构建一个包含 100-200 个涵盖不同难度和领域问题的测试集定期让模型生成结果并由人工从“推理逻辑正确性”、“步骤清晰度”、“答案正确性”三个维度打分如 1-5 分。这是最可靠但最耗时的方法。效果优化技巧数据混合不要只使用数学数据。混合代码推理、科学常识推理、逻辑谜题等数据可以让模型的递归推理能力更通用。比例需要实验调整初期可以以数学为主如60%代码和逻辑各占20%。渐进式训练实施课程学习。先训练所有数据 1 个 epoch然后筛选出模型表现差如答案错误的难题在后续 epoch 中增加这些难题的采样权重。后处理与提示工程在推理时可以在 prompt 中明确要求模型“逐步思考”、“输出每一步的推理过程”、“在最后以‘因此最终答案是’的格式给出答案”。清晰的指令能显著提升输出的结构化程度。集成验证对于关键应用可以训练多个不同随机种子或不同数据子集下的模型在推理时让它们各自生成然后通过投票或选择最一致的推理路径作为最终输出提升稳定性。5.3 资源与效率考量显存不够怎么办量化如示例中使用load_in_4bitTrue这是最有效的方法。还可以考虑load_in_8bit。参数高效微调使用 LoRA 或 QLoRA。只需训练极少量参数通常是注意力层的投影矩阵能大幅降低显存需求和加快训练速度。将上面代码中的模型加载和训练部分替换为 Peft 配置即可。梯度检查点启用gradient_checkpointingTrue用计算时间换显存。模型并行对于超大模型可能需要使用device_map”auto”或手动指定模型不同层到不同 GPU 上。训练速度太慢确保使用了bf16/fp16混合精度训练。增大per_device_train_batch_size并相应减少gradient_accumulation_steps前提是显存允许。使用更快的存储如 NVMe SSD来减少数据加载瓶颈。考虑使用 DeepSpeed 的 ZeRO 优化器阶段 2 或 3 进行分布式训练。这个项目提供的不仅仅是一个数据集或脚本它更代表了一种系统化提升模型深度推理能力的工程思路。从高质量的数据构造到有针对性的训练目标设计再到严谨的评估每一步都至关重要。在实际操作中最大的挑战往往不是跑通代码而是如何获取和清洗高质量、多样化的递归推理数据以及如何设计出能够准确评估模型推理过程而不仅仅是结果的方法。这需要耐心、细致的实验和大量的人工校验工作。

相关新闻