2023年11月6日

如何使你的补全输出与新的seed参数保持一致

简要说明: 开发者现在可以在聊天补全请求中指定seed参数来获取(基本)一致的输出。为了帮助您跟踪这些变更,我们开放了system_fingerprint字段。如果该值不同,您可能会看到由于我们系统变更导致的输出差异。请注意此功能目前处于测试阶段,仅支持gpt-4-1106-previewgpt-3.5-turbo-1106模型。

上下文

在使用我们的API时,可复现性一直是用户群体的重要诉求。例如,当获得可复现数值结果的能力时,用户就能解锁许多对数值变化敏感的用例场景。

确保输出一致性的模型级特性

Chat Completions和Completions API默认情况下是非确定性的(这意味着模型的输出可能因请求而异),但现在通过一些模型级别的控制,提供了一些实现确定性输出的方法。

这可以实现一致的完成结果,从而完全控制基于API构建的任何模型行为,对于结果复现和测试非常有用,因此您可以确切知道会得到什么,从而获得安心。

实现一致的输出

为了在API调用中获得基本确定的输出:

  • seed参数设置为任意你选择的整数值,但在不同请求中保持使用相同的值。例如:12345
  • 将所有其他参数(prompt、temperature、top_p等)在多个请求中设置为相同的值。
  • 在响应中,检查system_fingerprint字段。系统指纹是一个标识符,代表OpenAI服务器当前用于生成补全结果的模型权重、基础设施和其他配置选项的组合。每当您更改请求参数,或OpenAI更新服务模型的基础设施数值配置时(这种情况每年可能发生几次),该标识符就会发生变化。

如果seed、请求参数和system_fingerprint在您的所有请求中都匹配,那么模型输出将基本一致。由于我们模型固有的非确定性,即使请求参数和system_fingerprint匹配,响应仍有可能存在微小差异。

模型级控制确保输出一致性 - seedsystem_fingerprint

seed

如果指定了种子值,我们的系统将尽力进行确定性采样,这样使用相同种子和参数的重复请求应返回相同结果。但无法保证绝对的确定性,您应参考system_fingerprint响应参数来监测后端的变化。

system_fingerprint

此指纹代表模型运行时的后端配置。它可以与种子请求参数结合使用,以了解何时进行了可能影响确定性的后端更改。这是用户是否应该预期"几乎总是相同结果"的指标。

示例:使用固定种子生成简短摘要

在本示例中,我们将演示如何使用固定种子生成简短摘要。这在需要为测试、调试或需要稳定输出的应用场景生成一致结果时特别有用。

Python SDK

注意 请切换到最新版本的SDK(当前最新版本为1.3.3)。

!pip install --upgrade openai # Switch to the latest version of OpenAI (1.3.3 at time of writing)
import openai
import asyncio
from IPython.display import display, HTML

from utils.embeddings_utils import (
    get_embedding,
    distances_from_embeddings
)

GPT_MODEL = "gpt-3.5-turbo-1106"
async def get_chat_response(
    system_message: str, user_request: str, seed: int = None, temperature: float = 0.7
):
    try:
        messages = [
            {"role": "system", "content": system_message},
            {"role": "user", "content": user_request},
        ]

        response = openai.chat.completions.create(
            model=GPT_MODEL,
            messages=messages,
            seed=seed,
            max_tokens=200,
            temperature=temperature,
        )

        response_content = response.choices[0].message.content
        system_fingerprint = response.system_fingerprint
        prompt_tokens = response.usage.prompt_tokens
        completion_tokens = response.usage.total_tokens - response.usage.prompt_tokens

        table = f"""
        <table>
        <tr><th>Response</th><td>{response_content}</td></tr>
        <tr><th>System Fingerprint</th><td>{system_fingerprint}</td></tr>
        <tr><th>Number of prompt tokens</th><td>{prompt_tokens}</td></tr>
        <tr><th>Number of completion tokens</th><td>{completion_tokens}</td></tr>
        </table>
        """
        display(HTML(table))

        return response_content
    except Exception as e:
        print(f"An error occurred: {e}")
        return None

def calculate_average_distance(responses):
    """
    This function calculates the average distance between the embeddings of the responses.
    The distance between embeddings is a measure of how similar the responses are.
    """
    # Calculate embeddings for each response
    response_embeddings = [get_embedding(response) for response in responses]

    # Compute distances between the first response and the rest
    distances = distances_from_embeddings(response_embeddings[0], response_embeddings[1:])

    # Calculate the average distance
    average_distance = sum(distances) / len(distances)

    # Return the average distance
    return average_distance

首先,让我们尝试生成几个不同版本的关于"火星之旅"的简短摘录,不使用seed参数。这是默认行为:

topic = "a journey to Mars"
system_message = "You are a helpful assistant."
user_request = f"Generate a short excerpt of news about {topic}."

responses = []


async def get_response(i):
    print(f'Output {i + 1}\n{"-" * 10}')
    response = await get_chat_response(
        system_message=system_message, user_request=user_request
    )
    return response


responses = await asyncio.gather(*[get_response(i) for i in range(5)])
average_distance = calculate_average_distance(responses)
print(f"The average similarity between responses is: {average_distance}")
Output 1
----------
响应"NASA火星任务进入关键阶段,航天器成功进入红色星球轨道。这项始于一年前的历史性旅程已吸引全球关注,科学家和宇航员正准备首次登陆火星。该任务预计将为研究火星地质、大气层及未来维持人类生命的潜力提供宝贵见解。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量76
Output 2
----------
响应"NASA的毅力号火星车成功登陆火星,标志着探索这颗红色星球的使命取得了重大里程碑。该火星车配备了先进的科学仪器,用于寻找古代微生物生命的迹象,并采集岩石和土壤样本以便未来带回地球。这一历史性成就为近一步探索和未来可能的载人火星任务铺平了道路。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量76
Output 3
----------
响应"SpaceX昨日成功发射了首次载人火星任务,标志着太空探索的历史性里程碑。四名宇航员组成的机组将在未来六个月前往这颗红色星球,他们将在那里开展开创性的研究和实验。此次任务代表着人类在火星建立永久存在的重要一步,并为未来的星际旅行铺平了道路。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量72
Output 4
----------
响应"NASA最新的火星任务超出预期,毅力号火星车发现了关于这颗红色星球过去的诱人线索。科学家们对古老河床和沉积岩的发现感到兴奋,这增加了在火星上发现过去生命迹象的希望。随着这一激动人心的进展,将人类送上火星的梦想感觉比以往任何时候都更接近了。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量72
Output 5
----------
响应"NASA毅力号火星车成功登陆火星,开启探索任务 在太空探索的历史性时刻,NASA的毅力号火星车已成功降落在火星表面。经过七个月的旅程,探测器在杰泽罗陨石坑着陆,科学家认为该地区可能曾经存在湖泊,并可能蕴藏着远古微生物生命的迹象。 火星车的主要任务是寻找火星过去生命的证据,并采集岩石和土壤样本以供未来送回地球。毅力号配备了先进的科学仪器,包括相机、光谱仪和钻头,将开始对火星表面进行探索,为研究火星地质和潜在宜居性提供宝贵数据和见解。 这次成功着陆标志着人类探索这颗红色星球的重要里程碑,为未来载人火星任务铺平了道路。NASA的毅力号火星车有望揭开火星的奥秘,开启新的可能性
系统指纹fp_772e8125bb
提示词数量29
补全标记数量200
The average similarity between responses is: 0.1136714512418833

现在,让我们尝试使用固定的seed值123和temperature值0来运行相同的代码,并比较响应结果和system_fingerprint

SEED = 123
responses = []


async def get_response(i):
    print(f'Output {i + 1}\n{"-" * 10}')
    response = await get_chat_response(
        system_message=system_message,
        seed=SEED,
        temperature=0,
        user_request=user_request,
    )
    return response


responses = await asyncio.gather(*[get_response(i) for i in range(5)])

average_distance = calculate_average_distance(responses)
print(f"The average distance between responses is: {average_distance}")
Output 1
----------
响应"NASA毅力号火星车成功登陆火星 在一项历史性成就中,NASA的毅力号火星车已成功登陆火星表面,标志着这颗红色星球探索的重大里程碑。这辆从地球飞行了超过2.93亿英里的火星车配备了最先进的仪器,旨在寻找古代微生物生命的迹象,并收集岩石和土壤样本以供未来带回地球。此次任务代表着我们对火星认知的重大进步,也为未来人类探索这颗行星的潜力迈出了重要一步。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量113
Output 2
----------
响应"NASA的毅力号火星车成功登陆火星,标志着太空探索的历史性里程碑。该火星车配备了先进的科学仪器,用于寻找古代微生物生命的迹象并采集样本以供未来带回地球。这项任务为未来人类探索这颗红色星球铺平了道路,科学家和工程师们继续推动太空旅行的边界,拓展我们对宇宙的认知。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量81
Output 3
----------
响应"NASA的毅力号火星车成功登陆火星,标志着太空探索的历史性里程碑。该火星车配备了先进的科学仪器,用于寻找古代微生物生命的迹象并采集样本以供未来带回地球。这项任务为未来人类探索这颗红色星球铺平了道路,NASA持续推动着太空探索的边界。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量72
Output 4
----------
响应"NASA的毅力号火星车成功登陆火星,标志着太空探索的历史性里程碑。该火星车配备了先进的科学仪器,用于寻找古代微生物生命的迹象并采集样本以供未来返回地球。这项任务为未来人类探索这颗红色星球铺平了道路,科学家和工程师们继续推动太空旅行的边界,拓展我们对宇宙的认知。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量81
Output 5
----------
响应"NASA的毅力号火星车成功登陆火星,标志着太空探索的历史性里程碑。该火星车配备了先进的科学仪器,用于寻找古代微生物生命的迹象并采集样本以供未来带回地球。这项任务为未来人类探索这颗红色星球铺平了道路,科学家和工程师们继续推动着太空旅行的边界。"
系统指纹fp_772e8125bb
提示词数量29
补全标记数量74
The average distance between responses is: 0.0449054397632461

正如我们所观察到的,seed参数使我们能够生成更加一致的结果。

结论

我们演示了如何使用固定整数seed从模型中生成一致的输出。这在需要结果可复现的场景中特别有用。但需要注意的是,虽然seed能确保一致性,但不能保证输出质量。请注意,当您需要可复现的输出时,需要在Chat Completions调用中使用相同的seed整数值。您还应匹配其他参数如temperaturemax_tokens等。可复现输出的进一步扩展可以是在基准测试/评估不同提示或模型性能时使用一致的seed,以确保每个版本都在相同条件下进行评估,使比较公平且结果可靠。