temperature为0为什么会输出不同的结果

temperature为0为什么会输出不同的结果
Chase Wootemperature为0为什么会输出不同的结果
今天遇到一个有意思的问题,大模型的temperature=0,为什么会有不同的结果?
理论上,温度为0时,大模型会进入“贪婪搜索”模式,只选取概率最高的那个token。当输入完全相同时,输出应当也完全相同,但是经过实践,发现结果往往有区别。
一、浮点数计算的非确定性
首先要知道,浮点数计算不符合结合律:
$$
(a+b)+c \neq a+(b+c)
$$
举个例子$(1+10^{16})-10^{16}=0$,而$1+(10^{16}-10^{16})=1$
由于Attention涉及矩阵计算,而显卡对矩阵计算做了很多算子优化,这些计算往往是并行的,这导致成千上万个线程同时计算,计算的顺序无法保证一致性。在浮点数相加时,由于精度限制,精度之外的数字会被抹零。虽然一次计算看不出什么,但是经过上万次放大后会突破阈值,产生微妙的变化,使两个token的概率排序发现改变。
如果要保证确定性,需要在代码中进行特别处理才行,但代价是模型推理速度明显下降。
二、模型架构(MoE)
MoE(Mixture of Experts)混合专家架构将请求路由到不同的专家上,再由这个专家来计算。不同的专家擅长处理不同的任务,热门任务的专家往往会处理更多请求,当然现在的MoE为了均衡负载,专家基本都是均衡的黑箱,没有那么可解释。
在生产环境中,系统往往是将很多个请求打包成一个批次,送给大模型处理。这时候就会有多个请求竞争同一个专家,而竞争的结果不可能次次相同。一个相同的请求在不同的批次中可能会被路由到不同的专家中,而专家的效果也都不相同,于是同一个请求在不同的批次中就输出了不同的结果。这里用请求粒度举例子,其实现在的MoE都是token粒度的了。
比如我们的请求是问数学问题的,其他请求问语文问题,那么我们的请求自然会被路由到数学专家。但是当所有请求都是问数学问题时,我们的请求极有可能被竞争,然后被路由到了物理专家,而数学专家和物理专家的能力不相同,最后输出了不同的结果。
这种批次级的非确定性是MoE架构不可避免的,只有在批次完全相同时,模型才会给出完全相同的输出。
三、推理平台的设置
如果使用某些推理平台来部署大模型,当我们把temperature=0传过去时,这些推理平台会自动把temperature设置为0.01或0.1,这是工程上的优化。
或者是使用了某些商家的api服务,他们会将temperature的值控制在0.1到1.0之间,而非我们设置的0。比如openai的api可能会在某些情况下,会稍微调高一点temperature,以防止卡死或死循环
四、硬件层面
厂商一般都会将模型部署在异构的集群上,用户的请求往往会落在不同型号的显卡上,从显卡架构、cuda版本、驱动版本等多方面不同,计算结果都会受这些影响波动。
现在的大模型拥有超大参数,很可能一台机器塞不下,被部署在了多台机器上,以实现多机分布式计算。这种分布式推理涉及跨设备通信,不同设备之间的数据聚合顺序和通信时延差异,都会导致不确定性。
五、Tie-breaking平局决胜
在推理时,如果两个token的概率完全相同时,模型要决定选择哪一个。虽然在概率学中,两个概率相同的可能性微乎其微,但是在浮点计算中,这种情况比想象的要频繁。
为什么会出现平局?
- 量化:很多模型压缩到INT8或FP16精度,而精度的损失会导致原本微小的差异被抹去,导致平局。
- 截断误差:在某些激活函数或归一化层,会因为位数导致输出结果在二进制层面完全一致。
多数推理框架在遇到平局时会遵循索引优先的原则,即选择词表索引最小的那个token。不同的推理框架在选择平局token时,就做出了不同的选择。
综上
temperature=0并不能完全保证结果的确定性,这是硬件架构、推理框架和实现细节共同决定的。如果是本地部署大模型,可以选择batch-invariant库,强制保证批次无关性,但是牺牲一些性能。在测试模型时,不能假设单次输出就是正确答案,多跑几次取个平均值,以此减少不确定性带来的误差。





