解密Prompt系列9. 模型复杂推理-思维链COT基础和进阶玩法
终于写了一篇和系列标题沾边的博客,这一篇真的是解密prompt!我们会讨论下思维链(chain-of-Thought)提示词究竟要如何写,如何写的更高级。COT其实是Self-ASK,ReACT等利用大模型进行工具调用方案的底层逻辑,因此在Agent调用章节之前我们会有两章来讲思维链
先打预防针,COT当前的研究多少存在一些玄学成分,部分COT的研究使用的模型并非SOTA模型,以及相同的COT模板在不同模型之间可能不具备迁移性,且COT的效果和模型本身能力强相关,哈哈可以去围观COT小王子和Claude友商的Prompt决战]。本章只是为大家提供一些思维链设计的思路,以及给Agent调用做一些铺垫。
思维链的核心是为了提高模型解决复杂推理问题的能力,包括但不限于符号推理,数学问题,决策规划等等,Chain-of-Thought让模型在得到结果前,模拟人类思考推理的过程生成中间的推理步骤。适用于以下场景
- 有挑战的任务
- 解决任务本身需要多步推理
- 模型规模对任务效果的提升相对有限
COT基础用法
Few-shot COT
Chain of Thought Prompting Elicits Reasoning in Large Language Models
开篇自然是COT小王子的成名作,也是COT的开山之作,单看引用量已经是一骑绝尘。

论文的核心是通过Few-shot的方案,来引导模型生成中间推理过程,并最终提高模型解决复杂问题的能力。核心逻辑很Simple&Naive
- 通过在Few-shot样本中加入推理过程,可以引导模型在解码过程中,先给出推理过程,再得到最终答案
- 类似中间推理过程的加入,可以显著提高模型在常识推理,数学问题,符号推理等复杂推理问题上的模型表现。

先看效果,见上图,可以得到几个insights
- COT带来的效果提升具有涌现性,只在100B左右的大模型上才出现显著更优的效果,但作者没有给出模型规模的影响原因
- COT带来的效果提升在复杂问题例如GSM8K上表现更显著
让我们直观来看下COT的Few-shot模板构建,以及对解码的影响。这里我们使用Belle的数学COT指令样本采样10个构建了一组few-shot思维链指令,如下,

测试数学能力嘛,小学奥数经典的汽车相遇问题走一个,问题:两辆汽车从相距500千米的两城同时出发,相向而行.一辆摩托车以每小时80千米的速度在两辆汽车之间不断往返联络.已知这两辆汽车的速度分别是每小时40千米和60千米,求两汽车相遇时,摩托车共行驶了多少千米?题目不复杂500/(40+60)*80 = 400千米
以下分别使用ChatGPT和ChatGLM在Greedy-search模式下进行解码,分别是直接提问和加入few-shot-cot后再提问
| prompt | chatgpt | chatglm-6b |
|---|---|---|
| 直接提问 | 设两汽车相遇时间为\(t\)小时,则两汽车相遇时的距离为\((40+60)t=100t\)千米。摩托车在这\(t\)小时内不断往返,相当于在两汽车之间来回行驶了\(2\times 80t=160t\)千米。因此,摩托车共行驶了\(160t\)千米。根据题意,两汽车相遇时的距离为500千米,因此\(t=\frac{500}{100}=5\)小时。代入可得,摩托车共行驶了\(160\times 5=\boxed{800}\)千米。 | 设两汽车相遇时,摩托车行驶了 \(x\) 小时。根据题意,两汽车相向而行,它们的相对速度为 \(40+60=100\) 千米/小时。因此,两汽车相遇时,它们行驶的距离之和为:\(500\times(100+40)=50000\)因为两汽车相遇时,摩托车一直在行驶,所以摩托车行驶的距离等于两汽车相遇时它们行驶的距离之和:\(x\times80=50000\)解得:\(x=4000\)因此,两汽车相遇时,摩托车共行驶了 \(4000\) 千米。 |
| few-shot COT | 设摩托车共行驶了x千米,则两辆汽车相遇时,摩托车往返的次数为x/500。在每次往返中,摩托车行驶的距离为80千米,所以摩托车共行驶的距离为:x = (80千米) × (x/500) = 0.16x千米两辆汽车相遇时,它们的总路程为500千米,所以它们相遇所用的时间为:t = 500千米 / (40千米/小时 + 60千米/小时) = 5小时根据摩托车的速度和时间,可以得到:x = 80千米/小时 × 5小时 = 400千米因此,摩托车共行驶了400千米。 | 设两汽车相遇时,摩托车行驶了 \(x\) 小时。由题意可知,两汽车相遇时,它们已经行驶了 500 千米,所以有:\(40x + 60x = 500\)解得 \(x=50/9\),即两汽车相时,摩托车行驶了 50/9 小时。因此,摩托车共行驶了:\(80 \times (50/9) = 400\) |
加入COT之后chatgpt和chatglm都双双答对了,虽然有运气成分,且推理过程还显得有点智障吧。但你会发现不加入few-shot COT模型同样会进行推理再给出答案,而非像论文中给出的模型会直接直接给一个答案没有任何推理。原因是论文使用的是text-davinci-002是没有经过RLHF的旧版本模型。我们在一些需要推理的多项选择问题上也进行了测试,不管是使用few-shot COT还是加入COT指令样本进行微调,确实都能带来一定程度上推理/数学能力的提升。
论文还进行了一些消融实验包括
- 准确率提升是否来自数学公式的引入?
论文尝试了few-shot部分只加入公式并不能显著提升效果。个人感觉这个消融实验不太完整,因为很多复杂MWP问题模型很难映射到单一的公式,而先进行问题分解后再逐步映射到中间公式,在我们的场景里是可以提升效果的。这里推荐两个数据集:APE210K和Math23K分别是猿辅导和腾讯开源的数学世界问题转化成公式的数据集,大家可以自己试试看
- 准确率提升是否因为few-shot-cot帮助模型更好的召回相应的知识?
论文尝试了先给出答案再进行推理的prompt模板,发现效果显著变差。说明few-shot-COT只是激活模型给出推理,推理过程本身才是模型效果提升的核心。我们在一些需要推理的多项选择问题上也发现,先推理再回答选项的效果显著优于先回答选项再给出推理过程
zero-shot COT
Large Language Models are Zero-Shot Reasoners
除了使用Few-shot-COT样本可以让模型给出推理过程,单纯用指令也可以让模型给出思维链,且能进一步提升模型复杂问题推理能力。论文中效果最好的激活思维链的指令是"Let's think step by step"哈哈哈,我当时论文看到这里感觉空气都凝固了

zero-shot-COT在论文中需要两步解码,第一步用Let's think step by step让模型给出推理过程,第二步把推理过程和问题一起输入模型,让模型给出结果。但实际使用时你会发现,基本上第一步解码模型就会在推理结束时给出结果,而把带结果的输入再输入模型,极大概率第二步模型会给出相同的答案,所以其实zero-shot-COT也可以直接一步到位。
咱主打一个和相遇问题死磕,还是摩托车和货车相遇,咱使用几组不同的激活词再来问一遍
| 激活词 | chatgpt | chatglm |
|---|---|---|
| 请一步步的推理分析解决以下问题 | 设两汽车相遇时间为t小时,则两汽车相遇时的距离为(40+60)t=100t千米。由于两汽车相向而行,所以它们的相对速度为40+60=100千米/小时。因此,摩托车在t小时内往返的距离为2×80t=160t千米。根据题意,摩托车在两汽车相遇时停止往返,因此摩托车共行驶的距离为160t千米。将上述两个式子联立,解得t=2.5小时。因此,摩托车共行驶的距离为160×2.5=400千米。 | 设两汽车相遇时,摩托车行驶了 \(x\) 小时。根据题意,两汽车相向而行,它们的相对速度为 \(40+60=100\) 千米/小时。因此,两汽车相遇时,它们之间的距离为 \(500-x\) 千米。根据题意,摩托车以每小时 \(80\) 千米的速度在两辆汽车之间不断往返联络,因此摩托车每行驶 \(x\) 小时,会在两汽车相遇时行驶 \(80x\) 千米。因此,可以列出方程:\(80x = 500-x\)解方程可得:\(x = 50\)因此,两汽车相遇时,摩托车共行驶了 \(80x=3000\) 千米。 |
| 请逐步推理回答问题 | 设两汽车相遇时间为t小时,则两汽车相遇时的距离为(40+60)t=100t千米。根据题意,摩托车以80千米/小时的速度在两汽车之间不断往返,因此摩托车在t小时内往返的路程为2×80×t=160t千米。所以,摩托车共行驶了160t千米。将两式相等,得到:100t=500t=5此,两汽车相遇时,摩托车共行驶了160×5=<<160*5=800>>800千米。 | 设两汽车相遇时,摩托车行驶了 \(x\) 小时。根据题意,两汽车相向而行,因此它们相遇的时间可以用以下方程计算:\(t = \frac{500}{40 + 60} = 10\)因为两汽车相遇时,摩托车一直在行驶,所以摩托车行驶的时间也是 \(x\) 小时。根据题意,摩托车的速度是每小时 \(80\) 千米,因此摩托车行驶的距离可以用以下方程计算:\(x \times 80 = 500 - t\)将 \(t\) 的值代入上式,得到:\(x \times 80 = 500 - 10 = 490\)因此,两汽车相遇时,摩托车共行驶了 \(490\) 千米。 |
2组提示词,只有第一组让chatgpt给出了正确的答案,整体上提示词的效果很玄学,我们在不同问题上例如多项选择,数学计算也试过很多不同的方案,没找到啥规律。论文作者也尝试了不同的指令词来验证zero-shot-COT对指令词的依赖程度,见下表。整体上只要指令词有引导模型逐步推理的含义在,对模型效果都是有提升的。我个人也是看到这里才觉得zero-shot-COT可能确实有一定的合理性,因为指令提供的上文语义确实和模型推理的解码语义存在一定的相关性。

效果上,论文在MultiArith和GSM8k上和few-shot-cot进行了对比,整体上比few-shot略差,但是要显著超越只使用指令的baseline。不过需要注意,这里的评测模型还是是text-davinci-002,是没有经过RLHF只做了SFT的版本,并不是当前的最强模型,因此下图的效果提升放到GPT4上会打不小的折扣。毕竟GPT-4使用few-shot-COT在GSM8k上准确率已经奔着90%+去了。在模型大小上,zero-shot-COT同样具有规模效应,只在大模型上才表现出超越常规指令的效果

COT进阶用法
以上不论是few-shot还是zero-shot COT都还是基于模型自身给出推理过程,而人工不会过多干预推理过程。在进阶用法中会对推理过程做进一步的人工干预来引导解码步骤,进一步提升解码准确率,且以下的进阶方案是可以组合使用的。
Self-Consistency
SELF-CONSISTENCY IMPROVES CHAIN OF THOUGHT REASONING IN LANGUAGE MODELS
self-consistency是在few-shot-cot的基础上,用Ensemble来替换Greedy Search,来提高解码准确率的一种解码策略,论文显示加入self-consistency,可以进一步提升思维链的效果GSM8K (+17.9%)。
在使用大模型进行固定问题回答例如多项选择,数学问题时,我们往往会采用Greedy-Search的方式来进行解码,从而保证模型解码生成固定的结果,不然的话使用随机解码,我采样4次,模型把ABCD都选了一遍,那这题模型到底是答对了还是答错了??但每一步都选Top Token的局部最优的解码方案很显然不是全局最优的,而self-consistency其实提供了一种无监督的Ensemble方案,来对模型随机解码生成的多个回复“投票”出一个更准确答案,如下图

self-consistency的基础假设很人性化:同一个问题不同人也会给出不同的解法,但正确的解法们会殊途同归得到相同的正确答案。以此类比模型解码,同一问题不同随机解码会得到不同的思维链推理过程,期望概率最高的答案,准确率最高。那核心就变成针对多个解码输出,如何对答案进行聚合。论文对比了以下几种方案

给定指令prompt和问题question,模型通过随机解码会生成一组\(a_1,a_2,...a_m\)答案候选,以及对应的思维链路径\(r_1,r_2,...r_m\)。效果最好的两种聚合方案分别是
- major vote:直接对解码后的结果投票大法投出一个出现概率最高的答案。该说不说大道至简,最简单的方案往往是最好的, 论文后面的结果都是基于投票法给出的
- normalized weighted sum: 计算\((r_i, a_i)\)路径的概率,既模型输出的每一个token条件解码概率求和,并对解码长度K进行归一化。虽然这里略让人惊讶,本以为加权结果应该会更好,可能一定程度也说明模型的解码概率在答案的正确性上其实不太有区分度。
\]
针对解码参数论文还做了一些测试
- 随机参数:self-consistency支持不同的随机解码策略,在参数设定上需要平衡解码的多样性和准确率,例如temperature太低会导致解码差异太小,投票投了个寂寞,太高又会影响最终的准确率。看测试可能top-p=40, temperature=0.5是一个不错的测试起点

- 采样次数:major vote的效果很依赖候选样本数,论文中很豪横采样了40次,地主家的儿子也不敢这么玩...看效果采样5次以上就能超过Greedy解码,具体解码次数看你家有多少余粮吧...

Least-to-Most
LEAST-TO-MOST PROMPTING ENABLES COMPLEX REASONING IN LARGE LANGUAGE MODELS
如果说上面的Self-Consisty多少有点暴力出奇迹,那Least-to-Most明显更优雅一些。思路很简单,在解决复杂问题时,第一步先引导模型把问题拆分成子问题;第二步逐一回答子问题,并把子问题的回答作为下一个问题回答的上文,直到给出最终答案,主打一个循序渐进的解决问题。也可以理解为通过few-shot来引导模型给出更合理,更一致的推理思路,再根据这个思路在解决问题。

设计理念很好,但我最好奇的是few-shot-COT要如何写,才能引导模型针对不同场景进行合理的问题拆解。这里我们还是看下针对数学问题的few-shot应该如何构建的,论文中的few-shot-prompt是纯手工写制作,这里我采用chatgpt来生成再人工调整。从APE21K中随便抽了3个问题,注意不要太简单,已经有论文证明,few-shot-COT样本的推理步骤越多效果越好。这里我拆解问题的Prompt(未调优)是"对以下数学问题进行问题拆解,分成几个必须的中间解题步骤并给出对应问题, 问题:",来让ChatGPT生成中间的解题步骤作为few-shot-cot模板
- Problem Reducing 问题拆解

还是同一道相遇问题,通过Reduce prompt,ChatGPT输出:要解答摩托车共行驶了多少千米?我们需要回答以下问题:"两辆汽车相遇需要多长时间?","摩托车在这段时间内共行驶了多少千米?
以上的问题拆解不是非常稳定,有时会包括最终的问题,有时只包括中间的解题步骤,为了保险起见,你可以在解析的问题后面都再加一个原始的问题。
- Sequentially Solve 子问题有序回答
把Reduce步骤的子问题解析出来,按顺序输入chatgpt,先回答第一个子问题

再把第一个子问题和回答一起拼接作为上文,这里使用对话history也可以,拼接只是为了直观展示

Least-to-Most最值得借鉴的还是它问题拆分的思路,这在后面被广泛借鉴,例如Agent调用如何拆分每一步的调用步骤,以及如何先思考再生成下一步Action,在这些方案里都能看到Least-to-Most的影子。
想看更全的大模型相关论文梳理·微调及预训练数据和框架·AIGC应用,移步Github >> DecryptPropmt
解密Prompt系列9. 模型复杂推理-思维链COT基础和进阶玩法的更多相关文章
- 解密Prompt系列6. lora指令微调扣细节-请冷静,1个小时真不够~
上一章介绍了如何基于APE+SELF自动化构建指令微调样本.这一章咱就把微调跑起来,主要介绍以Lora为首的低参数微调原理,环境配置,微调代码,以及大模型训练中显存和耗时优化的相关技术细节 标题这样写 ...
- 解密Prompt系列2. 冻结Prompt微调LM: T5 & PET & LM-BFF
这一章我们介绍固定prompt微调LM的相关模型,他们的特点都是针对不同的下游任务设计不同的prompt模板,在微调过程中固定模板对预训练模型进行微调.以下按时间顺序介绍,支持任意NLP任务的T5,针 ...
- 解密Prompt系列3. 冻结LM微调Prompt: Prefix-Tuning & Prompt-Tuning & P-Tuning
这一章我们介绍在下游任务微调中固定LM参数,只微调Prompt的相关模型.这类模型的优势很直观就是微调的参数量小,能大幅降低LLM的微调参数量,是轻量级的微调替代品.和前两章微调LM和全部冻结的pro ...
- 解密Prompt系列4. 升级Instruction Tuning:Flan/T0/InstructGPT/TKInstruct
这一章我们聊聊指令微调,指令微调和前3章介绍的prompt有什么关系呢?哈哈只要你细品,你就会发现大家对prompt和instruction的定义存在些出入,部分认为instruction是promp ...
- 解密prompt系列5. APE+SELF=自动化指令集构建代码实现
上一章我们介绍了不同的指令微调方案, 这一章我们介绍如何降低指令数据集的人工标注成本!这样每个人都可以构建自己的专属指令集, 哈哈当然我也在造数据集进行时~ 介绍两种方案SELF Instruct和A ...
- C#语法糖系列 —— 第二篇:聊聊 ref,in 修饰符底层玩法
自从 C# 7.3 放开 ref 之后,这玩法就太花哨了,也让 C# 这门语言变得越来越多范式,越来越重,这篇我们就来聊聊 ref,本质上来说 ref 的放开就是把 C/C++ 指针的那一套又拿回来了 ...
- .NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- Java 加解密技术系列文章
Java 加解密技术系列之 总结 Java 加解密技术系列之 DH Java 加解密技术系列之 RSA Java 加解密技术系列之 PBE Java 加解密技术系列之 AES Java 加解密技术系列 ...
- [Asp.net MVC]Asp.net MVC5系列——在模型中添加验证规则
目录 概述 在模型中添加验证规则 自定义验证规则 伙伴类的使用 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net MVC5 ...
- [Asp.net MVC]Asp.net MVC5系列——添加模型
目录 概述 添加模型 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net MVC5系列——添加视图 概述 在本节中我们将追加 ...
随机推荐
- C#中抽象方法与虚方法的区别详解及示例
1. 抽象方法与虚方法的区别 先说两者最大的区别:抽象方法是需要子类去实现的.虚方法是已经实现了的,可以被子类覆盖,也可以不覆盖,取决于需求.因为抽象类无法实例化,所以抽象方法没有办法被调用,也就 ...
- Solon v2.2.7 发布,支持 Java 8 到 Java 20
Solon 是一个高效的 Java 应用开发框架:更快.更小.更简单.也是一个有自己接口标准规范的开放生态. 150来个生态插件,覆盖各种不同的应用开发场景: 相对于 Spring Boot 和 Sp ...
- 关于Docker compose值IP与域名的映射 之 extra_host
公司的所有项目都是采用Docker容器化部署,最近有一个项目需要使用定时任务调用第三方Api,正式web环境服务器的网络与第三方网络是通畅的,但是当将代码发布到正式环境,调用接口却显示 System. ...
- Git Commit Message 应该怎么写?
原文链接: Git Commit Message 应该怎么写? 最近被同事吐槽了,说我代码提交说明写的太差.其实都不用他吐槽,我自己心里也非常清楚.毕竟很多时候犯懒,都是直接一个 -m "f ...
- NetCore 使用 Swashbuckle 搭建 SwaggerHub
什么是SwaggerHub? Hub 谓之 中心, 所以 SwaggerHub即swagger中心. 什么时候需要它? 通常, 公司都拥有多个服务, 例如商品服务, 订单服务, 用户服务, 等等, 每 ...
- python---滚动条操作
""" 1.让元素滚动到可见区域后,再操作.(大部分的网页自己会滚,直接找元素---下一页) drive.find_element("id",&quo ...
- 运输问题和指派问题—R实现
运输问题和指派问题 运输问题经常出现在计划货物配送和从某些供给地区到达需求地区之间的服务中,特别是每个供给地区(起点)的货物可获得量是有限的,每个需求地区(目的地)的货物需求量是已知的.运输问题中最常 ...
- [Tomcat/Java EE/Linux]Tomcat启动异常:StandardServer.await: create[localhost:8005]: java.net.BindException: 无法指定被请求的地址
1 问题背景 部门新成员小J在一台虚拟机(ip:192.168.191.96)内安装部署部门的数据治理产品(含: 20余个微服务模块 + 1套(用户)基础管理系统BMS). 小J启动BMS的Tomca ...
- 深入理解 python 虚拟机:字节码教程(2)——控制流是如何实现的?
深入理解 python 虚拟机:字节码教程(2)--控制流是如何实现的? 在本篇文章当中主要给大家分析 python 当中与控制流有关的字节码,通过对这部分字节码的了解,我们可以更加深入了解 pyth ...
- odoo 开发入门教程系列-QWeb简史
QWeb简史 到目前为止,我们的房地产模块的界面设计相当有限.构建列表视图很简单,因为只需要字段列表.表单视图也是如此:尽管使用了一些标记,如<group>或<page>,但在 ...