GPT-4o 随机性揭秘:可控与不可控的奥秘

GPT-4o 中可避免与不可避免的随机性

探讨 GPT-4o 生成随机性的来源,从已知且可控的因素到不透明且不可控的机制。


GPT-4o 结果中的随机性:无可避免的现实

毫无疑问,GPT-4o 的输出存在随机性。毕竟,模型在选择每个 token 时是从概率分布中进行采样的。但我一直没有意识到,这些概率本身并不是完全确定性的。即使在使用相同的提示词、固定的随机种子(seed)以及将温度(temperature)设为 0 的情况下,GPT-4o 仍然会引入微妙却令人困惑的随机性。

这不是一个可以修复的问题,甚至可能不是 OpenAI 能够修复的问题——这篇文章的核心结论就是如此。在本文中,我们将深入分析 GPT-4o 输出中的各种随机性来源,并详细拆解其采样过程。我们将明确指出这个问题——概率值会变化,并对 OpenAI 官方有关确定性的指导进行批判性审视。


为什么确定性如此重要?

确定性(determinism)意味着相同的输入始终会产生相同的输出,就像数学函数一样。虽然大型语言模型(LLM)的创造性往往是有价值的,但在许多场景下,确定性至关重要:

  • 研究人员 需要确定性来保证实验的可复现性。
  • 开发者 需要确定性来验证测试结果。
  • 提示工程师 需要确定性来调试提示词的效果。

如果没有确定性,我们很难判断不同的输出是由于我们调整了提示词,还是仅仅因为模型的随机性导致的。


掷硬币实验:探索 GPT-4o 的非确定性

为了简化分析,我们让 GPT-4o 进行一个最基本的二元选择任务:掷硬币。我们使用 OpenAI API 向最新版本的 GPT-4o(gpt-4o-2024-08-06)发送以下请求:

import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

prompt = 'Flip a coin. Return Heads or Tails only.'

response = client.chat.completions.create(
    model='gpt-4o-2024-08-06',
    messages=[{'role': 'user', 'content': prompt}],
)

print(response.choices[0].message.content)

执行代码后,我得到了 "Heads"。你可能会得到 "Tails",或者如果运气够好,可能会看到一些更奇怪的结果。

然而,当我们多次运行这个代码时,我们发现了一个有趣的现象——GPT-4o 的“硬币”并不公平。例如,在运行 10 次后,我得到了以下结果:

Heads  
Heads  
Heads  
Heads  
Heads  
Heads  
Tails  
Heads  
Heads  
Heads  

很明显,GPT-4o 更倾向于返回 "Heads",但这并不奇怪。研究表明,人类在想象掷硬币时,80% 的时候会选择 "Heads"(Bar-Hillel, Peer, & Acquisti, 2014)。GPT-4o 可能从人类数据中学到了这种倾向。

但这只是表象,真正的问题在于:GPT-4o 的硬币抛掷结果并不是完全确定的,即使在所有可控参数固定的情况下。


GPT-4o 的概率分布并不固定

为了进一步分析,我们不直接生成完整的响应,而是请求 GPT-4o 返回 token 级别的 log 概率(logprobs),以便精确计算 "Heads" 与 "Tails" 的概率分布。

import os
import math
from openai import OpenAI
from tabulate import tabulate

client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

prompt = 'Flip a coin. Return Heads or Tails only.'

response = client.chat.completions.create(
    model='gpt-4o-2024-08-06',
    max_tokens=1,
    logprobs=True,
    top_logprobs=20,
    messages=[{'role': 'user', 'content': prompt}],
)

logprobs_list = response.choices[0].logprobs.content[0].top_logprobs

data = []
total_pct = 0.0

for logprob_entry in logprobs_list:
    token = logprob_entry.token
    logprob = logprob_entry.logprob
    pct = math.exp(logprob) * 100  # 转换为百分比
    total_pct += pct
    data.append([token, logprob, pct])

print(
    tabulate(
        data,
        headers=["Token", "Log Probability", "Percentage (%)"],
        tablefmt="github",
        floatfmt=("s", ".10f", ".10f")
    )
)
print(f"\nTotal probabilities: {total_pct:.6f}%")

运行后,可能会得到如下结果:

Token Log Probability Percentage (%)
Heads -0.0380541235 96.27%
T -3.2880542278 3.73%

惊人的是,"Heads" 的概率高达 96.27%,远远高于 "Tails"。

更重要的是,如果你多次运行这段代码,你会发现每次的概率 都不完全相同。即使所有参数(seed、temperature、system_fingerprint)都保持一致,GPT-4o 仍然会返回不同的 log 概率值。这意味着——GPT-4o 的 log 概率本身是不稳定的


为什么这无法被修复?

OpenAI 在其文档中提到:

“如果 seed、请求参数和 system_fingerprint 保持一致,那么模型输出在大多数情况下将是相同的。但由于模型的固有非确定性,仍然存在输出不同的可能性。”

我们尝试了一切可能的方法来锁定确定性,包括:

  • 将 temperature 设为 0.0(以确保始终选择最高概率的 token)。
  • 使用固定的随机种子(seed=42)(以确保随机数生成器的行为一致)。
  • 检查 system_fingerprint(以确认服务器端的模型版本和配置没有变化)。

然而,这些措施 都无法消除 GPT-4o 的随机性


Mixture-of-Experts(MoE)架构导致的不可避免随机性

GPT-4o 被广泛认为采用了 Mixture-of-Experts(MoE) 架构,即每个请求被分配给多个“专家”模型之一进行处理。Google DeepMind 的研究表明,这种架构可能会引入不可避免的随机性:

“当 token 被分配到不同的专家时,它们会相互竞争可用的计算资源,从而导致非确定性。”

换句话说,GPT-4o 的不同请求可能会被路由到不同的专家,而这些专家可能会返回不同的 log 概率值。这种分配是由 OpenAI 的基础设施动态决定的,因此是不可控的


结论:GPT-4o 的不确定性是无法规避的

GPT-4o 的非确定性 是其架构本身导致的,无法通过参数调整消除。这对研究者、开发者和提示工程师来说可能是一个重大挑战:

  • 实验无法完全复现,因为 log 概率会变化。
  • 调试变得更加复杂,因为不同的运行可能导致不同的结果。
  • 概率分析变得困难,因为 GPT-4o 的内部行为无法被完全控制或预测。

如果 OpenAI 能提供更透明的 API(例如,指示请求被分配到哪个专家),或许我们可以更好地理解这种随机性。但在目前的情况下,我们只能接受这个事实——GPT-4o 的随机性是不可避免的。

你可能感兴趣的:(java,前端,人工智能,机器学习,语言模型,chatgpt)