2024Q1再谈商用LLM的输出随机性 V2
V2版本的修改点:
补充了一些常用的商用LLM API的测试结果,完善了OpenAI的测试结果
更新对于原因的猜测
本文后续更新大概率不会在更新在微信公众号上,如果读者阅读此文时明显晚于2024.3.24,可以考虑直接去知乎查看最新版本:
https://zhuanlan.zhihu.com/p/688676344
TLDR
目前商用LLM API的输出随机性即使在贪心解码模式下并不能被有效控制,目前以OpenAI、百度、讯飞为代表这个问题尤为明显,剩下的其他大多也是五十步笑百步。
以OpenAI为例,大概当输出token长度超过100后,OpenAI的大部分模型都会在用尽所有控制手段的情况下仍然有较显著的随机性。
商用LLM API输出随机性会随着新版模型的发布而有剧烈的变化。希望完全控制LLM输出随机性方面的开发者务必定期对自己使用的API进行评估。
可能我们最终仍需要更多的结构化输出来对抗随机性和伪随机性。
0、前言
在绝大多数人心中,LLM输出的随机性问题基本靠temperature=0的贪心解码就已经可以了,再考虑到OpenAI专门增加了seed参数和system_fingerprint,怎么想这个问题应该已经被基本控制住了才对。(由于一些原因完全确定性还是很不容易的,并不指望这个)直到我最近做一些实验之前,我也是这么觉得的。
但我发现并不是!问题甚至变得比原来更大了。所以才写此文进行提醒。
本文面向的是有一定经验的LLM应用层开发者,标准是:知道temperature=0或趋近于零意味着什么。
1、LLM API供应商的现状
1.1、OpenAI API在贪心解码下的输出随机性
1.1.1、OpenAI API已有的降低随机性和提升可控性方面的工作
OpenAI单从API参数功能上来说,算是在所有LLM供应商中随机性相关的可控性做的较多的,包括:
temperature、top_p 这样的传统decode控制参数,特别地支持temperature=0
支持输出token的 logprobs 来更细致的了解decode中间结果
支持设置seed参数来降低随机性的不可控性
支持返回 system_fingerprint 来暴露硬件和基础设施因素的影响
具体可以参考:
https://platform.openai.com/docs/api-reference/chat/create
虽然OpenAI并不保证在temperature=0、设置seed、最终命中的 system_fingerprint 一致的情况下输出结果一定是确定的,但会尽最大保证。
一般人看到这里应该会觉得,至少在这个条件下应该比较稳定了。但事实是让人大跌眼镜的。
1.1.2、OpenAI API 的输出随机性测试
我构造了一个测试场景来测试该问题:
使用英文翻译为中文的任务,输入token量大于1k,输出token量也大于1k。总体控制在4k context范围内,方便兼顾到老版本模型和其他供应商的模型。
所有测试中输入prompt完全一致。
设置temperature=0,固定seed
备注:0613版本的模型并不支持seed,也不会输出 system_fingerprint 。
对于每个模型请求至少20次,计算输出token序列结果之间的最大相同前缀的平均长度,越大说明第一个分歧token出现的越晚。
测试结果:
gpt-3.5-turbo-0613,平均公共前缀长度567
gpt-3.5-turbo-1106,平均公共前缀长度88,只看到1种system_fingerprint
gpt-3.5-turbo-0125,平均公共前缀长度33,只看到1种system_fingerprint
gpt-4-0613,平均公共前缀长度67
gpt-4-1106-preview,平均公共前缀长度52,看到3种system_fingerprint。筛选system_fingerprint相同的结果,平均前缀长度没有明显提升。
gpt-4-0125-preview,平均公共前缀长度75,看到6种system_fingerprint。筛选system_fingerprint相同的结果,平均前缀长度没有明显提升。
结果总结:
gpt-3.5-turbo-0613模型的结果最符合一般人预期的,平均相同长度超过500。
gpt-4-0613与从1106开始的所有后续模型的平均相同长度大多在30-90的范围内。对于我的测试案例,超过100token的输出时,结果基本不会完全一致。
即使system_fingerprint 相同,输出的随机性也没有改善。对于gpt-4-0125-preview,我看到了6种 system_fingerprint 。也就是说即使跟贪心解码完全一致,单纯由于system_fingerprint (推理环境)导致的随机性可能仍然是显著的。
1.2、其他供应商的测试结果
对于国内原生的LLM来说,上面的测试数据的输出长度不足1000 token,大概是800 token左右。这对于测量平均公共前缀长度的影响并不大,因为大多数公共前缀长度都较低,所以就不使用新的测试数据了。
百度
百度不支持temperature=0,也没有贪心解码模式
ERNIE-Speed-8K,temperature=0.001,平均公共前缀长度68
ERNIE-3.5-8K,temperature=0.001,平均公共前缀长度40
ERNIE-4.0-8K,temperature=0.001,平均公共前缀长度85
智谱
智谱支持do_sample=False,该设置的具体功能在文档上没有详细说明
GLM-4,do_sample=False,平均公共前缀长度220
GLM-3-Turbo,do_sample=False,平均公共前缀长度580,大部分结果是完全一致的,少部分不是。
Minimax
Minimax不支持temperature=0,也没有贪心解码模式
abab5.5-chat,temperature=0.001,平均公共前缀长度>1000,20次里19次都是完全一样的
abab6-chat,temperature=0.001,平均公共前缀长度>340,20次里有11次是完全一样的
Moonshot
moonshot-v1-8k,temperature=0,平均公共前缀长度107
讯飞
讯飞不支持temperature=0,也没有贪心解码模式
Spark-3.5,temperature=0.001 ,平均公共前缀长度32
Gemini Pro 1.0,temperature=0,平均公共前缀长度246
1.3、如何解读这个结果
首先得说:在temperature~=0的场景下输出结果的稳定性并不是LLM的重要评测指标,这也是为什么之前很少有人关注这方面的原因。虽然这会影响上层应用策略的构建,但这件事情只要应用开发者知道了就好,不要对于LLM API做出错误的假设。实际应用中面对的木桶短板比这短的还有不少,例如后文提到的伪随机性问题,还远远谈不上说这个问题会明显地普遍影响各种应用的效果。
从技术上,也不能说这种稳定性越好LLM供应商的技术能力就越强。以随机性较低的情况举例,实际可能有如下2种可能:
LLM供应商对于各种复杂的加速推理方式没有投入,用的相对简单的方案,接近于native推理的方式,自然随机性更小
LLM供应商充分应用了各种复杂的加速推理方案,并且算法和工程团队都是很有追求的人,通过各种方式仍然保持输出中没有引入太多的随机性因素。
现在只有OpenAI正经的提出了seed参数和system_fingerprint 返回值,并宣称已经在这方面投入了工作,而目前测试的随机性却仍然如此之大,只有OpenAI才是在这方面应该被批评的。
其他家都尚未在这方面进行努力,连随机性控制最基本的seed都没加,也没有必要苛责太多。
1.4、可能的原因
由于各家的效果都很不相同,可见在影响这个问题的全链条上,各家的方案都至少存在一些差异。这里无法一一分析,所以本节主要以讨论大家关注的OpenAI为主。
首先,我很难准确地判断OpenAI API这个现象的原因,这里只能是做一些大概率不准确的猜测。
从数据上来看,明显gpt-3.5-turbo-0613用的推理方案在稳定性上明显与gpt4与后续模型有着明显的不同。可见gpt-3.5-turbo-0613用的方案大概已经被OpenAI抛弃,可见该方案并非仅适用于较大参数量的模型。
OpenAI既然已经增加了seed控制,那么肯定是在能够低成本搞定的条件下尽量控制这个问题的,但后续模型在这方面都很烂。所以大概率这种方案有比较明显的好处(如降低推理成本或降低延迟),但较难抑制其带来的随机性,所以现状是这样。也有可能这个锅是Azure团队的,Azure团队对此不够重视。
我猜测这个问题可能与以下因素有关:
Top几个token的预测logit相同时,没有做确定性选择策略。(这个点我认为OpenAI已经做了)
某些动态组batch的方案不能完全保证结果与单次请求相同
分块数组的累加顺序不同叠加浮点精度所引发的问题,在一个长数组分块进行累加且累加次序根据到达时间优先进行时会有这样的问题。当然这种也可以通过强制固定顺序累加来消除随机性,代价是增加了系统内的串行度比例。
当模型被划分在多卡甚至多机上时,网络的延迟随机抖动导致消息到达次序不同,进而与上面一样可能再叠加decode策略本身或浮点计算不能完全保证计算的可交换性所导致的最终结果不同。
由于算子实现方式、CUDA runtime版本、驱动版本等导致的差异等,但这些可以通过准确设计的system_fingerprint进行观察。
完全消除随机性并非不可能,我在远比这复杂的多得多的数值模拟系统中,都能看到能够实现在给定seed的情况下把输出结果完全确定的产品。但问题在于,OpenAI和其他LLM供应商到底愿意花费多少研发成本和推理性能的损失来换取这种较高的确定性。以目前的状态来看,我只能说OpenAI和各家LLM供应商在这上的重视程度不算很高。
2、对LLM API供应商的随机性控制效果保持警惕
目前来看包括OpenAI在内的各家LLM供应商在这方面的控制都谈不上很好,稍微长一点的输出的随机性完全无法得到有效的控制。这已经让我觉得通过降低temperature来抑制随机性这个方式已经意义不大了,不如通过适当增加temperature来提升结果多样性,并依靠其他手段来降低全链路最终结果的随机性。在一些场景中也有看到贪心解码输出的结果从质量上来讲也未必是最好的。
从OpenAI的状态来看,不排除各家LLM供应商未来换用了某种推理方案之后这个问题又变得显著。至少说随着模型的量化程度提升,没有提供seed控制的模型API的随机性应该会继续增加。
所以对于这方面敏感的应用开发者来说,监控自己使用的API在这方面的随机性表现变得重要了,要不然可能会出现严重的效果误判。
3、伪随机性问题
既然谈到了随机性问题,就顺带说一下另一个相关的问题:LLM的伪随机性。我在2023.9月专门写过一篇文章来讨论这个问题 被忽视的LLM伪随机性问题,贪心解码为什么无法消除它 【2023Q3】,这里仅作简略重复。
即使在decode方案和推理的软硬件环境完全能够消除随机性的情况下,应用层为并非就能认为随机性问题已经完全消除。还存在另一类我称之为【伪随机性】的效果,即LLM的输出对于输入token序列的小扰动很敏感的问题。也就是说:输入可能只做了不影响语义的略微改变,但输出结果可能出现很大的语义差异。
也就是说decode/推理阶段的确定性算法只能保证对于同样token序列下的输出token稳定性。无法保证在输入语义不变而token序列不同的情况下输出结果的语义结果稳定性。在某些应用场景下,无法依赖输入token完全一致,但又需要在输入语义一致的情况下保持结果的语义问题性,这些场景在这方面有着严重的挑战。
这个伪随机性不光是对于输入的,在输出过程中,一旦出现不同结果之间的分歧token,即使语义上非常相似,仍然可能会导致后面的输出结果变得语义上很不同。这也是为什么前面的metric选择的是首次分歧token出现的位置。
4、随机性与伪随机性的应对
很多应用场景下,对于结果的语义稳定性的需求其实比很多开发者想象的要大,这是容易被忽视的。不过这并非本文主题,所以仅仅点到为止。
过去很多人觉得随机性问题靠temperature和seed就能完全控制,而我提到的伪随机性问题至今仍然没有被广为认知。而OpenAI API的现状则告诉我们,寄托于temperature和seed仍然是徒劳的,特别是对于长输出的场景下。例如说简单的回答可能还能稳定,但稍微加了一点分步求解或者CoT,结果上就开始体现随机性。认真的场景不得不开始考虑在整体策略流程的层面来解决这个问题。
在LLM API调用之上来抑制随机性,典型的方式就是多次采样进行投票或者平均。但这一般只是对于输出完全能够结构化的场景才能直接适用。当输出的内容中包含有需要通过自然语言的片段时,如何判定两个结果的对应字段是否语义一致就开始变得很复杂和脆弱。这方面我目前也没有完美的方案,我目前也没有感觉可以作为推荐的候选方案。
不过在大的思路上我目前的倾向是:
对于多次调用LLM的结果进行聚合不适合再依赖LLM,因为这后续的LLM仍然会有显著的伪随机性,这个问题很难消除。
多次调用LLM结果的聚合只能更多的依赖传统逻辑流程进行处理,这需要LLM的输出结果是尽可能结构化的,能输出成固定的json schema就不要用文本,能都输出成标准的数据类型就不要使用模糊的语义字符串。对于多个json结果进行聚合的稳定性是能够超过直接对多个文本结果进行聚合的。
尽量不要在一次LLM调用中输出太长的内容,越长的输出越容易遇到真随机性问题,这会放大LLM的伪随机性问题的影响。
这个场景其实是可以考虑做定制的LLM模型结构或者decode方案的,对于json中不相关的字段,调整attention mask进行相互屏蔽,以抑制伪随机性问题。
交流与合作
如果希望和我交流讨论,或参与相关的讨论群,或者建立合作,请私信联系,见 联系方式。
希望留言可以到知乎对应文章下留言。
本文于2024.3.23首发于微信公众号与知乎,V2版本发布于2024.3.24。
知乎链接 https://zhuanlan.zhihu.com/p/688676344