LLM无标签自我提升:CoT多路采样与自一致性投票微调流水线 简介这份PDF收录了Google与伊利诺伊大学香槟分校团队关于“大型语言模型能否自我提升”的研究论文适合自然语言处理、大模型推理与自监督学习方向的研究者、算法工程师及高年级学生阅读用于理解无标注数据条件下提升模型推理能力的思路与实验设计。包内为1个PDF文件整体约532KB便于在电脑或平板上直接查阅原文、图表与参考文献。目前已有367人学习下载。论文提出用Chain-of-Thought提示与自一致性生成高置信度推理增强答案再以这些自生成解答微调预训练LLM在GSM8K、DROP、OpenBookQA、ANLI-A3等任务上给出540B模型的具体提升数据同时通过消融研究说明“基于推理的微调”对自我提升的关键作用。读者可据此了解自我学习、迭代优化与无监督数据利用的实验框架为后续复现、选题或构建自主进化系统提供参考。1. 零标签涨 7.7 个点这份 PDF 讲的是模型怎么用自己的答案喂自己先说一个反直觉的结论不额外标注一条数据只把模型自己多次推理、投票选出来的「高置信度答案」回收做微调GSM8K 上的准确率就能从 74.4% 提到 82.1%。这篇 PDF 讲的正是这件事标题直译过来就是「大型语言模型能够自我提升」。对日常用 ChatGPT 这类工具做数学题、多跳问答、逻辑判断的从业者来说它的价值不在于结论多漂亮而在于把「无标签自我提升」拆成了一条能照着搭的流水线Chain-of-Thought 多路采样、自一致性多数投票筛样本、混合格式回灌微调。这份材料适合两类人一类是想在自有数据上继续压推理准确率的 AI 工程师另一类是想搞明白 ChatGPT 使用里那些「让它多想几遍再回答」的技巧到底有没有工程依据的实践者。2. 方法骨架拆解CoT 多路采样、答案投票、混格式回灌三步走整套方法没有引入任何新网络结构全部收益来自「怎么造训练数据」。给定一个只有问题、没有答案的数据集 D和一个预训练好的模型 M流程是先用少样本 CoT 示例提示 M对每道题以大于 0 的温度采样 m 条推理路径再用多数投票找出最一致的答案最后把所有通向这个答案的推理路径留下来按混合格式拼成训练样本回灌给 M 做微调。得到的模型在贪心解码和多路解码两种评测下都有提升。这件事的工程含义比学术含义更值得琢磨。传统自训练是给无标签数据打伪标签然后分类头继续学这里打标签的不是一个分类器而是一整个「采样 投票」的推理过程标签也不只是一个答案而是连同推理链一起带回来的。换句话说模型交出去的是答案收回来的是「解题过程」微调时学的是过程而不只是结论。这也是论文反复强调的一点对推理链本身做微调才是自我提升成立的关键只学答案收益会小很多。2.1 先固定 CoT 示例模板再谈采样动手之前必须先定死提示模板因为模板决定了后面解析器怎么写。论文用的是少样本 CoT每道题前面挂几条「问题 分步推导 The answer is X」的示例让模型模仿这个格式输出。我一般会把示例控制在 4 到 8 条覆盖不同题型示例里的推理步骤宁可朴素也不要跳跃因为模型会连推导风格一起模仿。如果示例里出现「显然」「易得」这类跳步写法采样出来的路径大概率也是跳步的答案抽取脚本就得跟着遭殃。模板定死之后不要再改。采样阶段改了模板等于换了一个分布投票结果和后面微调的数据就不同源了。这一点在复现时最容易翻车为了「提升一点效果」中途调了示例最后发现提升全部来自模板变化和自我提升没关系。2.2 多路解码与答案抽取解析器写不好后面全白搭采样阶段的关键参数是温度 T 和路径数 m。T 太小m 条路径几乎一模一样投票没有意义T 太大路径质量崩掉投票选出来的「共识」也是错的。论文的做法是 T 取一个中等偏上的值m 取几十条量级让同一个问题能覆盖到多条不同的解法。工程上我一般先用 m16、T0.7 跑一小批看投票分布再往上加。答案抽取是最枯燥也最要命的一环。模型输出的路径是自由文本必须用正则把最终答案抠出来抠不出来或者抠错的样本要整条丢掉不能猜。下面这段是我常用的抽取加投票骨架import re from collections import defaultdict # 只认 The answer is X 这种收尾避免把中间步骤的数字误当答案 NUM_RE re.compile(ranswer is\s*\$?\s*(-?[\d,\.]), re.I) def extract_answer(path_text: str): 从一条 CoT 路径中抽取最终答案抽不到返回 None m NUM_RE.search(path_text) if not m: return None return m.group(1).replace(,, ).rstrip(.) def vote_and_collect(qid, paths, min_votes2): 按答案分桶返回票数最高的答案和通向它的所有路径 buckets defaultdict(list) for p in paths: ans extract_answer(p) if ans is not None: # 解析失败直接丢弃不做兜底 buckets[ans].append(p) if not buckets: return None ans, keep max(buckets.items(), keylambda kv: len(kv[1])) if len(keep) min_votes: # 票数不够视为低置信度整题丢弃 return None return {qid: qid, answer: ans, paths: keep}extract_answer里的正则只匹配「answer is」后面的数字这是有意的一旦放开去匹配任意数字模型在中间步骤里算出的中间值会被误当成最终答案投票分布立刻失真。min_votes这个阈值是置信度门槛低于它的题不进训练集——宁可少几百条样本也不要把噪声喂进去。实践里min_votes取 2 到 3 就够再高会把长尾难题全筛掉模型反而学不到难样本。2.3 多数投票筛「高置信度」样本混格式回灌投票完成后通向多数答案的所有路径都会被保留而不是只留一条。这一点值得单独强调只留一条最「标准」的路径训练数据会变得极其同质模型学不到推理的多样性保留多条等于告诉模型「这个问题有几种写法都能得到同一个答案」泛化会好一些。接下来是混合格式。论文里的做法是把同一批推理路径用不同的排列方式拼成训练样本——有的是「问题 → 推理 → 答案」有的是「问题 → 答案 → 推理」。前者训练的是从条件推结论后者训练的是先给结论再补依据。两种顺序混在一起目的是让模型不要死记某一种输出模式。这一步在复现时经常被简化掉但论文的消融显示格式选择对最终效果有实际影响建议不要省。3. 动手复现把三步压成一条能跑的采样-投票-微调流水线这一章按顺序把可执行的部分落下来。整套流程不需要额外的标注成本唯一要准备的是一个只有问题的数据集和一份能跑推理的模型权重。硬件上采样阶段的显存开销和推理时一样微调阶段的显存取决于你要不要全参微调——如果只是验证流程跑不跑得通先拿小模型 LoRA 走一遍再换大底座。3.1 数据侧只留 question把标签列直接删掉第一步是把数据集处理成「只有问题」的形态。这件事听着简单实操里最常见的错误是删标签删得不彻底——评测集的标签还留在训练文件的某个字段里导致模型的「自我提升」实际上是偷看了答案。我一般会在预处理阶段显式只保留两个字段唯一 id 和问题文本其它列一律 drop。import json def build_question_only(src_path, dst_path): kept 0 with open(src_path, encodingutf-8) as fin, \ open(dst_path, w, encodingutf-8) as fout: for line in fin: row json.loads(line) # 白名单式保留杜绝标签顺着别的字段漏进来 item {qid: row[id], question: row[question].strip()} fout.write(json.dumps(item, ensure_asciiFalse) \n) kept 1 return kept用白名单而不是黑名单是为了防止数据集里那些名字奇怪的辅助字段比如solution、hint、rationale被漏掉。这一步多花十分钟后面能省掉一整轮「为什么效果虚高」的排查。采样前还要把 CoT 示例拼进提示。示例本身建议单独存成文件和问题文本在运行时拼接而不是预处理时烤进数据里——这样调示例不用重跑预处理也方便做「换模板」的对照实验。3.2 采样—投票—构造训练集三合一脚本下面这段是整条流水线的主干把采样、抽取、投票、格式化四步串起来。为了可读性模型调用部分抽象成一个generate函数实际接自家推理服务或本地权重都行。from concurrent.futures import ThreadPoolExecutor def build_self_training_set(items, generate, cot_examples, m16, temperature0.7, min_votes2, workers32): items: [{qid, question}]; generate(prompt, m, temperature) - [str] def work(item): prompt cot_examples \nQ: item[question] \nA: # 一次请求采 m 条省掉 m 次往返的调度开销 paths generate(prompt, mm, temperaturetemperature) return vote_and_collect(item[qid], paths, min_votes) samples [] with ThreadPoolExecutor(max_workersworkers) as pool: for res in pool.map(work, items): if res is None: # 解析失败或票数不足跳过 continue samples.append(res) return samples def to_training_records(sample, question, cot_firstTrue): 把一条高置信度样本展开成若干条训练记录 out [] for p in sample[paths]: body p.split(The answer is)[0].strip() if cot_first: out.append(fQ: {question}\nA: {p.strip()}) else: # 答案前置先给结论再补推理逼模型学「结论-依据」的顺序 out.append(fQ: {question}\nA: The answer is {sample[answer]}.\n{body}) return out几个参数值得说明。m16是起点不是终点m 越大投票越稳但采样成本线性上涨实践中先在小验证集上试 8、16、32 三档看准确率什么时候饱和。temperature0.7负责制造多样性太低投票没意义太高答案质量下降。min_votes2是置信度下限配合后面的过滤能显著降低训练集噪声比例。workers只是并发度和效果无关按你的推理服务 QPS 调。跑完之后建议统计三个数原始题量、进入训练集的题量、平均每题保留的路径数。如果保留率低于三成说明温度或min_votes设置有问题先别急着训练。3.3 微调侧样本格式、长度控制与评测对齐训练样本拼好之后微调本身反而是最不需要创造力的部分。格式上按 2.3 说的混两种顺序比例我一般取 1:1让模型同时见到「推理在前」和「结论在前」。长度上要注意截断CoT 路径普遍偏长如果训练时把 max_length 设得太小被截掉的往往是结论句模型学到的就只剩半截推理。评测侧必须和训练侧对齐。论文里同时报了贪心解码和多路解码两种结果你自己验证时也要两种都跑只看贪心解码容易低估多路投票带来的收益只看多路解码又容易高估模型本身的能力。另外评测集的答案要单独存绝不能和训练数据放在同一个目录下避免任何形式的意外泄漏。一个容易被忽略的细节是微调后的模型在域外任务上也会变好论文里报的 AQUA、StrategyQA、MNLI 都属于这类所以验证集不要只放同分布数据。如果只有同域验证集你会看到一个很好看但没什么参考价值的曲线。4. 超参与消融温度、路径数 m、混合格式该怎么定论文的消融部分是这份 PDF 里最实用的章节因为它回答的是「哪些设计是必需的哪些是可选的」。结论可以概括成一句话对推理链做微调是必需的格式和采样参数是放大器。下面按三个维度拆。4.1 采样温度与路径数 m先保多样性再谈收敛温度和路径数是一对耦合参数不能分开调。温度低、m 大采出来的路径高度同质投票几乎必然一致看起来「置信度很高」实际上模型并没有探索到别的解法温度高、m 小投票结果随机性太大同一道题跑两次可能得到不同答案训练集不稳定。我的调法是从一个小规模网格开始T 取 0.5、0.7、1.0m 取 8、16、32在几百道题的验证子集上看「投票一致率」和「一致样本的正确率」两个指标。理想区域是一致率在六到八成之间——太低了样本不够太高了说明模型没有真正被激发出多样性。找到这个区域之后再固定 T、加大 m 提升稳定性。4.2 混合格式混的是什么什么时候该关掉混合格式的收益来自打破输出顺序的固定模式。但它有个副作用答案前置的样本会让模型更倾向于先给结论这在需要严格展示推理过程的场景里是减分项。如果你的下游应用要求「必须先推导再给答案」答案前置那部分样本就要么去掉要么降比例。另一个边界是任务类型。数学题这类有唯一数值答案的任务格式混合收益明显开放式生成任务里「答案前置」很容易变成模型编造结论再硬凑理由这时候建议只保留推理前置的格式。4.3 把消融结论翻成调参决策表下面这张表是我自己复现时的决策依据左列是要调的旋钮右列是对应的判断标准和常见取值。具体数值以你手上的验证集为准不要照抄。参数作用起始值调整信号采样温度 T控制路径多样性0.7投票一致率过高就上调答案质量崩就下调路径数 m投票稳定性16一致率随 m 提升明显就继续加饱和就停最少票数 min_votes训练集置信度门槛2保留率低于三成就降到 1噪声大就提到 3格式混合比例控制输出顺序偏好1:1下游要求先推理就降答案前置占比保留路径策略学多样性还是学单一路径全部保留数据量太少时可只留票数最高的 2 到 3 条读这张表的方式是先调温度和 m 把保留率压到合理区间再调 min_votes 控噪声最后才动格式比例。顺序反了会白折腾因为前面两个参数直接决定了训练集的规模和分布后面的调整都建立在这上面。论文里还报了一组消融结论是如果不做推理链、只学答案提升会大幅缩水。这条对做工程的人最有价值它意味着这套方法不是「伪标签自训练」的换皮收益确实来自推理过程本身。如果你复现出来发现提升很小第一个该检查的就是训练样本里到底有没有完整的推理链。5. 避坑与排查从采样到微调的五条血泪记录5.1 采样与解析阶段的三个坑现象投票一致率异常高九成以上的题都是同一个答案。原因采样温度设得太低或者推理服务在服务端做了贪心裁剪m 条路径其实是同一条。解决先打日志看同一道题的 m 条输出是否逐字符相同相同就说明多样性没打开再查服务端的采样参数有没有被默认值覆盖。现象准确率虚高但换一批题就崩。原因预处理时标签列没删干净模型在提示里看到了答案。解决按 3.1 的白名单方式重建数据集并且把训练文件和评测文件放到不同目录跑之前用脚本 grep 一遍标签字段名。现象大量样本被丢弃保留率不到一成。原因答案抽取正则太窄模型输出里常见的是「答案是 108」「 $108」这类变体只匹配一种写法会漏掉大半。解决抽取出错时先人工看 50 条失败样本的收尾写法把正则扩到 3 到 5 种常见模式同时保留「抽不到就丢弃」的原则不要写兜底逻辑去猜。5.2 训练与评测阶段的两个坑现象微调后同分布准确率涨了域外任务反而掉了。原因训练集里全是同一类题的高置信度样本分布过窄模型被过拟合到了单一题型。解决训练集要覆盖多个任务、多种题型混合比例按题量而不是按文件如果只能拿到单一任务的数据降低训练轮数别让模型把这批数据背下来。现象训练 loss 正常下降评测却不如微调前。原因训练样本被截断结论句落在 max_length 之外模型学到的是不完整的推理。解决统计训练样本的长度分布把 max_length 设到 95 分位以上实在压不下来就在拼样本时把结论句前置保证它不会被截掉。6. 进阶路径让模型自己出题、自己写 CoT 模板流程跑通之后最值得往下挖的是怎么进一步减少人工介入。论文给了两个方向都不是花架子。6.1 让模型自己写 CoT 示例少样本 CoT 的示例是人挑的这本身就是一笔人工成本。论文试过让模型自己生成示例模板再拿去做零样本推理GSM8K 上做到 74.2%对比直接零样本提示的 43.0% 和朴素多路投票的 70.1%差距非常明显。工程上的做法是先用手写的少量示例让模型对一批题生成推理从中挑格式最规整、步骤最完整的几条作为模板再拿这套模板去跑全量采样。挑样本这一步不需要人看答案对错只看格式成本很低。6.2 让模型自己扩题第二个方向是从有限的输入问题里生成新的问题扩充训练集规模。这一步的风险比上一步大扩出来的题如果偏离原分布太远投票筛出来的样本会把模型带偏。我的做法是给扩题加两道闸——一是要求新题必须复用原题的实体和数值结构二是扩出来的题先走一遍投票保留率明显低于原题的直接整批丢掉。6.3 验证自提升是否真的发生判断「自我提升」有没有真的发生不能只看一个数字。我一般会跑三组对照原始模型、原始模型 多路投票不微调、微调后的模型。如果第三组只比第二组高一点点说明收益主要来自投票而不是微调这时候要回头查训练样本质量如果第三组在贪心解码下也明显高于第一组才说明模型本身的能力被改写了。这个对照实验花不了多少算力但能省掉很多自欺欺人的结论。最后说个我自己的教训。第一次复现这套流程时我看到验证集涨了就急着换大底座结果换完发现效果全没了。回头查日志才发现小模型那次「涨点」是因为投票一致率高得离谱本质上等于把温度调成了 0。从那以后我每次跑自提升都强制先打三张表同一题 m 条输出的重复率、训练集保留率、三种解码设置下的对照结果三张表都正常了才允许往下走。希望帮到你。本文还有配套的精品资源点击获取