以下是文章的中文逐字翻译:
使用 logprobs 进行分类和问答评估
本笔记本演示了如何在聊天补全 API 中使用 logprobs
参数。启用 logprobs
后,API 会返回每个输出 token 的对数概率,以及每个 token 位置上最可能的几个 token 及其对数概率。相关的请求参数是:
logprobs
:是否返回输出 token 的对数概率。如果为 true,则返回消息内容中每个输出 token 的对数概率。top_logprobs
:一个介于 0 和 5 之间的整数,指定每个 token 位置上返回的最可能的 token 数量,每个 token 都附带一个对数概率。如果使用此参数,则必须将logprobs
设置为 true。
输出 token 的对数概率表示给定上下文的序列中每个 token 发生的可能性。为简化起见,对数概率是 log(p)
,其中 p
= 基于上下文中先前 token 在特定位置发生的 token 的概率。关于 logprobs
的一些要点:
- 较高的对数概率表明该 token 在该上下文中的可能性较高。这使用户能够评估模型对其输出的置信度或探索模型考虑过的替代响应。
- 对数概率可以是任何负数或
0.0
。0.0
对应于 100% 的概率。 - 对数概率允许我们将序列的联合概率计算为各个 token 对数概率的总和。这对于对模型输出进行评分和排名很有用。另一种常见方法是取句子中每个 token 的平均对数概率来选择最佳生成。
- 我们可以检查分配给不同候选 token 的
logprobs
来了解模型认为哪些选项是合理的或不合理的。
虽然 logprobs
有广泛的用例,但本笔记本将重点关注其在以下方面的应用:
- 分类任务
- 大型语言模型在许多分类任务中表现出色,但准确衡量模型对其输出的置信度可能具有挑战性。
logprobs
提供了与每个类别预测相关的概率,使用户能够设置自己的分类或置信度阈值。
- 检索(问答)评估
logprobs
可以帮助我们对检索应用程序进行自我评估。在问答示例中,模型输出了一个人为构造的has_sufficient_context_for_answer
布尔值,该值可以作为答案是否包含在检索内容中的置信度分数。此类评估可以减少基于检索的幻觉并提高准确性。
-
自动补全 *
logprobs
可以帮助我们在用户键入时决定如何建议单词。 -
Token 高亮显示和输出字节 * 用户可以通过启用
logprobs
附带的内置分词轻松创建 token 高亮显示器。此外,bytes
参数包含每个输出字符的 ASCII 编码,这对于重现表情符号和特殊字符特别有用。 -
计算困惑度 *
logprobs
可用于帮助我们评估模型对结果的整体置信度,并帮助我们比较不同模型运行结果的置信度。
0. 导入和工具
from openai import OpenAI
from math import exp
import numpy as np
from IPython.display import display, HTML
import os
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))
def get_completion(
messages: list[dict[str, str]],
model: str = "gpt-4",
max_tokens=500,
temperature=0,
stop=None,
seed=123,
tools=None,
logprobs=None, # whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the content of message..
top_logprobs=None,
) -> str:
params = {
"model": model,
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature,
"stop": stop,
"seed": seed,
"logprobs": logprobs,
"top_logprobs": top_logprobs,
}
if tools:
params["tools"] = tools
completion = client.chat.completions.create(**params)
return completion
1. 使用 logprobs
评估分类任务的置信度
假设我们要创建一个系统,将新闻文章分类到一组预定义的类别中。如果没有 logprobs
,我们可以使用聊天补全来完成此操作,但要评估模型做出分类的确定性要困难得多。
现在,通过启用 logprobs
,我们可以确切地看到模型对其预测的置信度,这对于创建准确且值得信赖的分类器至关重要。例如,如果所选类别的对数概率很高,则表明模型对其分类非常有信心。如果它很低,则表明模型不太自信。这在模型分类不符合您的预期,或者模型输出需要人工审查或验证的情况下特别有用。
我们将从一个向模型提供四个类别的提示开始:技术、政治、体育和艺术。然后,模型根据文章标题将其分类到这些类别中。
CLASSIFICATION_PROMPT = """您将获得一篇新闻文章的标题。
请将文章分类到以下类别之一:技术、政治、体育和艺术。
仅返回类别名称,不要返回其他任何内容。
请确保您的输出是上述四个类别之一。
文章标题: {headline}"""
让我们看三个示例标题,并首先从标准的聊天补全输出开始,不带 logprobs
headlines = [
"科技巨头发布最新智能手机型号,配备先进的照片编辑功能。",
"当地市长启动计划,以改善城市公共交通。",
"网球冠军在交响乐团首演中展示隐藏的天赋",
]
for headline in headlines:
print(f"\n标题: {headline}")
API_RESPONSE = get_completion(
[{"role": "user", "content": CLASSIFICATION_PROMPT.format(headline=headline)}],
model="gpt-4o",
)
print(f"类别: {API_RESPONSE.choices[0].message.content}\n")
标题: 科技巨头发布最新智能手机型号,配备先进的照片编辑功能。
类别: 技术
标题: 当地市长启动计划,以改善城市公共交通。
类别: 政治
标题: 网球冠军在交响乐团首演中展示隐藏的天赋
类别: 艺术
在这里,我们可以看到每个标题的选定类别。但是,我们无法了解模型对其预测的置信度。让我们再次运行相同的提示,但启用 logprobs
,并将 top_logprobs
设置为 2(这将显示每个 token 的 2 个最可能的输出 token)。此外,我们还可以输出每个输出 token 的线性概率,以便将对数概率转换为更易于解释的 0-100% 的尺度。
for headline in headlines:
print(f"\n标题: {headline}")
API_RESPONSE = get_completion(
[{"role": "user", "content": CLASSIFICATION_PROMPT.format(headline=headline)}],
model="gpt-4o-mini",
logprobs=True,
top_logprobs=2,
)
top_two_logprobs = API_RESPONSE.choices[0].logprobs.content[0].top_logprobs
html_content = ""
for i, logprob in enumerate(top_two_logprobs, start=1):
html_content += (
f"<span style='color: cyan'>输出 token {i}:</span> {logprob.token}, "
f"<span style='color: darkorange'>对数概率:</span> {logprob.logprob}, "
f"<span style='color: magenta'>线性概率:</span> {np.round(np.exp(logprob.logprob)*100,2)}%<br>"
)
display(HTML(html_content))
print("\n")
标题: 科技巨头发布最新智能手机型号,配备先进的照片编辑功能。
输出 token 1: 技术, 对数概率: 0.0, 线性概率: 100.0%
输出 token 2: 技术, 对数概率: -18.75, 线性概率: 0.0%
标题: 当地市长启动计划,以改善城市公共交通。
输出 token 1: 政治, 对数概率: -3.1281633e-07, 线性概率: 100.0%
输出 token 2: 政, 对数概率: -16.0, 线性概率: 0.0%
标题: 网球冠军在交响乐团首演中展示隐藏的天赋
输出 token 1: 艺术, 对数概率: -0.028133942, 线性概率: 97.23%
输出 token 2: 体育, 对数概率: -4.278134, 线性概率: 1.39%
正如前两个标题所示,gpt-4o-mini 对其分类充满 100% 的信心,因为内容显然分别侧重于技术和政治。然而,第三个标题结合了体育和艺术相关的主题,导致置信度略有下降至 97%,但仍然显示出对其分类的强烈信心。
logprobs
对于分类任务非常有用。它们允许我们设置置信度阈值,或者在所选输出的对数概率不够高时输出多个潜在的 token。例如,在创建推荐引擎来标记文章时,我们可以自动分类超过特定阈值的标题,并将不太确定的标题发送给人工审查。
2. 检索置信度评分以减少幻觉
为了减少幻觉,并提高我们基于 RAG 的问答系统的性能,我们可以使用 logprobs
来评估模型对其检索的置信度。
假设我们使用 RAG 构建了一个用于问答的检索系统,但正在处理对我们问题的幻觉答案。注意: 我们将在本示例中使用硬编码的文章,但请参阅 cookbook 中的其他条目,了解有关使用 RAG 进行问答的教程。
# 检索到的文章
ada_lovelace_article = """奥古斯塔·艾达·金,洛夫莱斯伯爵夫人(娘家姓拜伦;1815年12月10日 – 1852年11月27日)是一位英国数学家和作家,主要以其在查尔斯·巴贝奇提出的机械通用计算机分析机上的工作而闻名。她是第一个认识到该机器除了纯粹计算之外还有其他应用的人。
艾达·拜伦是诗人乔治·戈登·拜伦勋爵和改革家安妮·艾莎·米尔班克·拜伦夫人的唯一合法子女。洛夫莱斯的所有同父异母兄弟姐妹,拜伦勋爵的其他子女,都是与其他女性非婚生子女。艾达出生一个月后,拜伦与妻子分居,并永远离开了英国。艾达八岁时,他在希腊去世。她的母亲担心她的成长,并鼓励艾达对数学和逻辑的兴趣,以防止她发展出她父亲认为的疯狂。尽管如此,艾达仍然对他很感兴趣,并将她的两个儿子命名为拜伦和戈登。在她去世时,她应她的要求被安葬在他身边。尽管童年时期经常生病,艾达仍勤奋地学习。她于1835年与威廉·金结婚。金于1838年被封为洛夫莱斯伯爵,艾达因此成为洛夫莱斯伯爵夫人。
她的教育和社会活动使她能够接触到安德鲁·克罗斯、查尔斯·巴贝奇、大卫·布鲁斯特爵士、查尔斯·惠斯通、迈克尔·法拉第和作家查尔斯·狄更斯等科学家,她利用这些联系来进一步深造。艾达将她的方法描述为“诗意的科学”,并将自己描述为“分析师(与形而上学家)”。
十八岁时,她的数学才能使她与另一位英国数学家查尔斯·巴贝奇建立了长期的工作关系和友谊,巴贝奇被称为“计算机之父”。她尤其对巴贝奇在分析机上的工作感兴趣。洛夫莱斯于1833年6月通过他们共同的朋友,也是她的私人导师玛丽·萨默维尔第一次见到他。
在1842年至1843年间,艾达翻译了军事工程师路易吉·梅纳布雷(后来的意大利总理)关于分析机的文章,并为其增加了一套详细的七个注释,简称为“注释”。
洛夫莱斯的注释在计算机早期历史中很重要,特别是第七个注释包含了许多人认为的第一个计算机程序——即为机器执行而设计的算法。其他历史学家拒绝这种观点,并指出巴贝奇在1836/1837年间的个人笔记中包含了分析机的第一个程序。她还对计算机的能力超越纯粹的计算或数字处理产生了远见,而许多其他人,包括巴贝奇本人,则只关注这些能力。她的“诗意的科学”的心态促使她对分析机提出问题(如她的注释所示),考察个人和社会如何将技术作为协作工具。
"""
# 可以根据文章轻松回答的问题
easy_questions = [
"艾达·洛夫莱斯是什么国籍?",
"洛夫莱斯夫人的第七个注释中一项重要的发现是什么?",
]
# 文章中未完全涵盖的问题
medium_questions = [
"洛夫莱斯是否与查尔斯·狄更斯合作过",
"洛夫莱斯与查尔斯·巴贝奇共同构建了哪些概念",
]
现在,我们可以让模型回答问题,然后评估其响应。具体来说,我们将要求模型输出一个布尔值 has_sufficient_context_for_answer
。然后,我们可以评估 logprobs
,以了解模型对其答案包含在提供的内容中的置信度。
PROMPT = """您检索到这篇文章:{article}。问题是:{question}。
在回答问题之前,请考虑您是否在文章中有足够的信息来充分回答问题。
您的输出应该仅仅是布尔值 true 或 false,表示您是否有足够的信息在文章中回答问题。
只回答一个词,布尔值 true 或 false。您必须输出单词 'True' 或 'False',仅此而已。
"""
html_output = ""
html_output += "文章中清楚回答的问题"
for question in easy_questions:
API_RESPONSE = get_completion(
[
{
"role": "user",
"content": PROMPT.format(
article=ada_lovelace_article, question=question
),
}
],
model="gpt-4o-mini",
logprobs=True,
)
html_output += f'<p style="color:green">问题: {question}</p>'
for logprob in API_RESPONSE.choices[0].logprobs.content:
html_output += f'<p style="color:cyan">has_sufficient_context_for_answer: {logprob.token}, <span style="color:darkorange">对数概率: {logprob.logprob}, <span style="color:magenta">线性概率: {np.round(np.exp(logprob.logprob)*100,2)}%</span></p>'
html_output += "文章中仅部分涵盖的问题"
for question in medium_questions:
API_RESPONSE = get_completion(
[
{
"role": "user",
"content": PROMPT.format(
article=ada_lovelace_article, question=question
),
}
],
model="gpt-4o",
logprobs=True,
top_logprobs=3,
)
html_output += f'<p style="color:green">问题: {question}</p>'
for logprob in API_RESPONSE.choices[0].logprobs.content:
html_output += f'<p style="color:cyan">has_sufficient_context_for_answer: {logprob.token}, <span style="color:darkorange">对数概率: {logprob.logprob}, <span style="color:magenta">线性概率: {np.round(np.exp(logprob.logprob)*100,2)}%</span></p>'
display(HTML(html_output))
文章中清楚回答的问题
问题: 艾达·洛夫莱斯是什么国籍?
has_sufficient_context_for_answer: True, 对数概率: -3.1281633e-07, 线性概率: 100.0%
问题: 洛夫莱斯夫人的第七个注释中一项重要的发现是什么?
has_sufficient_context_for_answer: True, 对数概率: -7.89631e-07, 线性概率: 100.0%
文章中仅部分涵盖的问题问题: 洛夫莱斯是否与查尔斯·狄更斯合作过
has_sufficient_context_for_answer: False, 对数概率: -0.008654992, 线性概率: 99.14%
问题: 洛夫莱斯与查尔斯·巴贝奇共同构建了哪些概念
has_sufficient_context_for_answer: True, 对数概率: -0.004082317, 线性概率: 99.59%
对于前两个问题,我们的模型以(接近)100% 的置信度断言文章有足够的内容来回答这些问题。
另一方面,对于那些在文章中回答得不太清楚的棘手问题,模型对其拥有足够内容的置信度较低。这是一个很好的保护措施,可以帮助确保我们检索到的内容足够。
这种自我评估可以帮助减少幻觉,因为您可以将答案限制在一定范围内,或者在 sufficient_context_for_answer
的对数概率低于某个阈值时重新提示用户。此类方法已被证明可以显著减少基于 RAG 的问答中的幻觉和错误(示例)。
3. 自动补全
logprobs
的另一个用例是自动补全系统。在不完全端到端创建自动补全系统的情况下,让我们演示一下 logprobs
如何帮助我们决定在用户键入时如何建议单词。
首先,让我们想出一个示例文句:“我最不喜欢的电视节目是《绝命毒师》。” 假设我们希望它在我们键入句子时动态推荐下一个单词或 token,但仅在模型对下一个单词非常有把握时才这样做。为了演示这一点,让我们将句子分解为连续的组件。
sentence_list = [
"我的",
"我最不",
"我最不喜欢的",
"我最不喜欢的电视",
"我最不喜欢的电视节目",
"我最不喜欢的电视节目是",
"我最不喜欢的电视节目是《绝命毒师》",
]
现在,我们可以让 gpt-4o-mini
作为自动补全引擎,并提供模型接收到的任何上下文。我们可以启用 logprobs
,并查看模型对其预测的置信度。
high_prob_completions = {}
low_prob_completions = {}
html_output = ""
for sentence in sentence_list:
PROMPT = """完成这个句子。你扮演自动补全的角色。只需尽力完成句子,确保它只是一句话:{sentence}"""
API_RESPONSE = get_completion(
[{"role": "user", "content": PROMPT.format(sentence=sentence)}],
model="gpt-4o-mini",
logprobs=True,
top_logprobs=3,
)
html_output += f'<p>句子: {sentence}</p>'
first_token = True
for token in API_RESPONSE.choices[0].logprobs.content[0].top_logprobs:
html_output += f'<p style="color:cyan">预测的下一个 token: {token.token}, <span style="color:darkorange">对数概率: {token.logprob}, <span style="color:magenta">线性概率: {np.round(np.exp(token.logprob)*100,2)}%</span></p>'
if first_token:
if np.exp(token.logprob) > 0.95:
high_prob_completions[sentence] = token.token
if np.exp(token.logprob) < 0.60:
low_prob_completions[sentence] = token.token
first_token = False
html_output += "<br>"
display(HTML(html_output))
句子: 我的
预测的下一个 token: 我的, 对数概率: -0.08344023, 线性概率: 91.99%
预测的下一个 token: 我的狗, 对数概率: -3.3334403, 线性概率: 3.57%
预测的下一个 token: ap, 对数概率: -3.5834403, 线性概率: 2.78%
句子: 我最不
预测的下一个 token: 我, 对数概率: -0.1271426, 线性概率: 88.06%
预测的下一个 token: 喜欢的, 对数概率: -2.1271427, 线性概率: 11.92%
预测的下一个 token: 我的, 对数概率: -9.127143, 线性概率: 0.01%
句子: 我最不喜欢的
预测的下一个 token: 我, 对数概率: -0.052905332, 线性概率: 94.85%
预测的下一个 token: 食物, 对数概率: -4.0529056, 线性概率: 1.74%
预测的下一个 token: 颜色, 对数概率: -5.0529056, 线性概率: 0.64%
句子: 我最不喜欢的电视
预测的下一个 token: 节目, 对数概率: -0.57662326, 线性概率: 56.18%
预测的下一个 token: 我, 对数概率: -0.82662326, 线性概率: 43.75%
预测的下一个 token: 节目, 对数概率: -8.201623, 线性概率: 0.03%
句子: 我最不喜欢的电视节目
预测的下一个 token: 是, 对数概率: -0.70817715, 线性概率: 49.25%
预测的下一个 token: 我, 对数概率: -0.70817715, 线性概率: 49.25%
预测的下一个 token: 是, 对数概率: -4.833177, 线性概率: 0.8%
句子: 我最不喜欢的电视节目是
预测的下一个 token: 我, 对数概率: -0.47896808, 线性概率: 61.94%
预测的下一个 token: 一个, 对数概率: -1.7289681, 线性概率: 17.75%
预测的下一个 token: 这个, 对数概率: -2.9789681, 线性概率: 5.08%
句子: 我最不喜欢的电视节目是《绝命毒师》
预测的下一个 token: 因为, 对数概率: -0.034502674, 线性概率: 96.61%
预测的下一个 token: ,, 对数概率: -3.7845027, 线性概率: 2.27%
预测的下一个 token: 因为, 对数概率: -5.0345025, 线性概率: 0.65%
让我们看看高置信度的自动补全:
high_prob_completions
{'我最不喜欢的电视节目是《绝命毒师》': '因为'}
这些看起来很合理!我们可以对这些建议感到放心。在你写完“我最不喜欢的电视”之后,写“节目”的可能性很大!现在让我们看看模型不太自信的自动补全建议:
low_prob_completions
{'我最不喜欢的电视': '节目', '我最不喜欢的电视节目': '是'}
这些也很合乎逻辑。仅凭前缀“我最不喜欢的”就很难猜到用户会说什么,而且作者最喜欢的电视节目是什么,几乎是任何人都可以猜测的。
所以,使用 gpt-4o-mini
,我们可以使用 logprobs
创建一个动态自动补全引擎的基础!
4. 高亮显示器和字节参数
让我们快速介绍一下如何使用 logprobs
创建一个简单的 token 高亮显示器,以及使用字节参数。首先,我们可以创建一个函数来计算和高亮显示每个 token。虽然这不使用对数概率,但它使用了启用 logprobs
所附带的内置分词。
PROMPT = """英语中最长的单词是什么?"""
API_RESPONSE = get_completion(
[{"role": "user", "content": PROMPT}], model="gpt-4o", logprobs=True, top_logprobs=5
)
def highlight_text(api_response):
colors = [
"#FF00FF", # 品红
"#008000", # 绿色
"#FF8C00", # 暗橙色
"#FF0000", # 红色
"#0000FF", # 蓝色
]
tokens = api_response.choices[0].logprobs.content
color_idx = 0 # 初始化颜色索引
html_output = "" # 初始化 HTML 输出
for t in tokens:
token_str = bytes(t.bytes).decode("utf-8") # 将字节解码为字符串
# 将彩色 token 添加到 HTML 输出
html_output += f"<span style='color: {colors[color_idx]}'>{token_str}</span>"
# 移动到下一个颜色
color_idx = (color_idx + 1) % len(colors)
display(HTML(html_output)) # 显示 HTML 输出
print(f"总 token 数: {len(tokens)}")
highlight_text(API_RESPONSE)
英语中最长的单词是常被认为是“pneumonoultramicroscopilicovolicationosis,”一个指代由吸入非常细的硅酸盐或石英粉尘引起的肺部疾病的术语。然而,值得注意的是这个词是为了其长度而创造的,而不是为了实际使用。还有一些蛋白质或其他化合物的化学名称可以长得多,但它们通常不在日常语言中使用。
总 token 数: 95
接下来,让我们使用字节参数来重构一个句子。启用 logprobs
后,我们同时获得每个 token 和 token 字符串的 ASCII(十进制 utf-8)值。这些 ASCII 值在处理包含表情符号或特殊字符的 token 时非常有用。
PROMPT = """输出蓝心表情符号及其名称。"""
API_RESPONSE = get_completion(
[{"role": "user", "content": PROMPT}], model="gpt-4o", logprobs=True
)
aggregated_bytes = []
joint_logprob = 0.0
# 迭代 token,聚合字节并计算联合对数概率
for token in API_RESPONSE.choices[0].logprobs.content:
print("Token:", token.token)
print("Log prob:", token.logprob)
print("Linear prob:", np.round(exp(token.logprob) * 100, 2), "%")
print("Bytes:", token.bytes, "\n")
aggregated_bytes += token.bytes
joint_logprob += token.logprob
# 将聚合后的字节解码为文本
aggregated_text = bytes(aggregated_bytes).decode("utf-8")
# 断言解码后的文本与消息内容相同
assert API_RESPONSE.choices[0].message.content == aggregated_text
# 打印结果
print("Bytes array:", aggregated_bytes)
print(f"Decoded bytes: {aggregated_text}")
print("Joint prob:", np.round(exp(joint_logprob) * 100, 2), "%")
Token: 这里
Log prob: -0.054242473
Linear prob: 94.72 %
Bytes: [72, 101, 114, 101]
Token: 是
Log prob: -0.0044352207
Linear prob: 99.56 %
Bytes: [32, 105, 115]
Token: 这个
Log prob: -2.1008714e-06
Linear prob: 100.0 %
Bytes: [32, 116, 104, 101]
Token: 蓝色
Log prob: -0.0013290489
Linear prob: 99.87 %
Bytes: [32, 98, 108, 117, 101]
Token: 心
Log prob: 0.0
Linear prob: 100.0 %
Bytes: [32, 104, 101, 97, 114, 116]
Token: 表情符号
Log prob: 0.0
Linear prob: 100.0 %
Bytes: [32, 101, 109, 111, 106, 105]
Token: 和
Log prob: -0.038287632
Linear prob: 96.24 %
Bytes: [32, 97, 110, 100]
Token: 它
Log prob: 0.0
Linear prob: 100.0 %
Bytes: [32, 105, 116, 115]
Token: 的
Log prob: -1.569009e-05
Linear prob: 100.0 %
Bytes: [32, 110, 97, 109, 101]
Token: :
Log prob: -0.11313002
Linear prob: 89.3 %
Bytes: [58, 10, 10]
Token: 💙
Log prob: -0.09048584
Linear prob: 91.35 %
Bytes: [240, 159, 146]
Token:
Log prob: 0.0
Linear prob: 100.0 %
Bytes: [153]
Token: 蓝色
Log prob: -0.023958502
Linear prob: 97.63 %
Bytes: [32, 66, 108, 117, 101]
Token: 心
Log prob: -6.2729996e-06
Linear prob: 100.0 %
Bytes: [32, 72, 101, 97, 114, 116]
Bytes array: [72, 101, 114, 101, 32, 105, 115, 32, 116, 104, 101, 32, 98, 108, 117, 101, 32, 104, 101, 97, 114, 116, 32, 101, 109, 111, 106, 105, 32, 97, 110, 100, 32, 105, 116, 115, 32, 110, 97, 109, 101, 58, 10, 10, 240, 159, 146, 153, 32, 66, 108, 117, 101, 32, 72, 101, 97, 114, 116]
Decoded bytes: 这里是蓝心表情符号和它的名字:
💙 蓝色 心
Joint prob: 72.19 %
在这里,我们看到虽然第一个 token 是 \xf0\x9f\x92'
,但我们可以获取它的 ASCII 值并将其附加到字节数组中。然后,我们可以轻松地将此数组解码为完整的句子,并通过断言语句验证解码后的字节与我们的完成消息相同!
此外,我们可以获得整个完成的联合概率,它是每个 token 对数概率的指数乘积。这告诉我们给定提示的完成有多“可能”。由于我们的提示非常明确(要求特定的表情符号及其名称),因此此输出的联合概率很高!如果我们要求随机输出,我们将看到一个低得多的联合概率。这也可以作为开发人员在提示工程中的一个好策略。
5. 计算困惑度
在评估模型对结果的置信度时,计算困惑度可能很有用,困惑度是衡量不确定性的指标。困惑度可以通过对平均对数概率的负值取指数来计算。通常,较高的困惑度表示不确定的结果,而较低的困惑度表示更自信的结果。因此,困惑度既可用于评估单个模型运行的结果,也可用于比较模型运行之间结果的相对置信度。虽然高置信度并不保证结果的准确性,但它可以作为一个有用的信号,与其他评估指标配对,以更好地理解您的提示的行为。
例如,假设我想使用 gpt-4o-mini
来了解更多关于人工智能的信息。我可以问一个关于近期历史的问题和一个关于未来的问题:
prompts = [
"用一句话简述,人工智能在过去十年中是否有所增长?",
"用一句话简述,您对人工智能的未来有何看法?",
]
for prompt in prompts:
API_RESPONSE = get_completion(
[{"role": "user", "content": prompt}],
model="gpt-4o-mini",
logprobs=True,
)
logprobs = [token.logprob for token in API_RESPONSE.choices[0].logprobs.content]
response_text = API_RESPONSE.choices[0].message.content
response_text_tokens = [token.token for token in API_RESPONSE.choices[0].logprobs.content]
max_starter_length = max(len(s) for s in ["Prompt:", "Response:", "Tokens:", "Logprobs:", "Perplexity:"])
max_token_length = max(len(s) for s in response_text_tokens)
formatted_response_tokens = [s.rjust(max_token_length) for s in response_text_tokens]
formatted_lps = [f"{lp:.2f}".rjust(max_token_length) for lp in logprobs]
perplexity_score = np.exp(-np.mean(logprobs))
print("Prompt:".ljust(max_starter_length), prompt)
print("Response:".ljust(max_starter_length), response_text, "\n")
print("Tokens:".ljust(max_starter_length), " ".join(formatted_response_tokens))
print("Logprobs:".ljust(max_starter_length), " ".join(formatted_lps))
print("Perplexity:".ljust(max_starter_length), perplexity_score, "\n")
Prompt: 用一句话简述,人工智能在过去十年中是否有所增长?
Response: 是的,人工智能在过去十年中有了显著增长,在各个领域的性能和应用都得到了提升。
Tokens: 是的 , 人工智能 有了 显著 增长 在 各个 的 领域 , 性能 和 应用 都 得到了 提升 。
Logprobs: -0.00 0.00 -0.00 0.00 -0.00 -0.73 -0.00 -0.01 -0.02 -0.00 0.00 -0.02 -0.66 -0.03 -0.62 -0.47 -0.02 -0.39 -0.01 -0.20 -0.00
Perplexity: 1.1644170003987546
Prompt: 用一句话简述,您对人工智能的未来有何看法?
Response: 人工智能的未来在各个领域都具有巨大的变革性进步潜力,但也需要仔细考虑伦理和社会影响。
Tokens: 人工智能 的未来 在 各个 领域 都 具有 巨大的 变革性 进步 潜力 , 但也 需要 仔细 考虑 伦理 和 社会性 影响 。
Logprobs: -0.02 -0.00 0.00 -0.00 0.00 -0.05 -0.35 -0.01 -0.02 -0.64 -0.43 -0.25 -0.16 -0.51 -0.02 -0.43 -0.08 -0.07 -0.97 -0.02 -0.48 -0.00 -0.00 -0.48 -0.01 -0.58 -0.00
Perplexity: 1.2292170270768858
在此示例中,gpt-4o-mini
对关于近期历史的更确定的问题返回了较低的困惑度分数,而对关于不久的未来的更具推测性的评估返回了较高的困惑度分数。同样,虽然这些差异不能保证准确性,但它们有助于为我们解释模型结果和未来使用它们指明方向。
6. 结论
很好!我们能够使用 logprobs
参数构建一个更强大的分类器,评估我们的 RAG 问答系统,并对我们 token 的每个“字节”进行编码和解码!logprobs
为我们的补全输出添加了有用的信息和信号,我们很期待看到开发人员如何将其集成以改进应用程序。
7. 可能的扩展
此 cookbook 未涵盖 logprobs
的许多其他用例。我们可以使用 logprobs
进行:
- 审核
- 关键词选择
- 改进提示和输出的可解释性
- Token 修复
- 以及更多!