在处理大型语言模型(LLMs)时,识别幻觉可能会很棘手。我们不应仅仅依赖LLM作为判断者(因为它仍然可能出错,许多评估框架仅使用它来检测幻觉),而可以利用困惑度、蕴含关系和离散语义熵来更好地识别潜在的幻觉。尽管我在这里使用LLM来检测蕴含关系,但这并不是必须的。也就是说,这种方法最适合那些有明确、事实性答案的问题——那些不太模糊或主观的问题。你对使用这些组合指标来更好地检测幻觉有什么看法?我理解代码可以改进/优化,但目标是快速测试其工作原理。
from openai import OpenAI
import numpy as np
from pydantic import BaseModel
import time
client = OpenAI(api_key="key")
class CheckEntailment(BaseModel):
label: str
def check_entailment(fragment1: str, fragment2: str) -> bool:
"""检查蕴涵"""
messages = [
{
"role": "user",
"content": f"""您有一个大型语言模型的两个响应。
检查一个响应的含义是否由另一个响应蕴涵,或者是否存在矛盾。
如果蕴涵,则返回“0”。如果矛盾,则返回“1”。
仅返回标签,不做任何解释。
\n 响应 1:\n {fragment1}\n\n 响应 2:\n {fragment2}""",
}
]
completion = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=messages,
temperature=0.1,
logprobs=True,
top_logprobs=2,
response_format=CheckEntailment,
)
entailment = False
# print(completion.choices[0].logprobs.content[3].top_logprobs)
for top_logprob in completion.choices[0].logprobs.content[3].top_logprobs:
print(top_logprob.token, np.round(np.exp(top_logprob.logprob), 2))
if "0" in top_logprob.token and np.exp(top_logprob.logprob) > 0.7:
entailment = True
return entailment
def calculate_entropy(probs):
"""
计算熵
"""
probs = np.array(probs)
probs = probs / probs.sum()
probs = probs[probs > 0]
entropy = -np.sum(probs * np.log2(probs))
return entropy
some_tricky_questions = [
“阿拉巴马州与哪个州的边界最长?是佛罗里达州还是田纳西州?”,
“谁主持了 2007 年的英国游戏节目倒计时:a) Nick Hewer b) Richard Whiteley c) Jeff Stelling?”,
“小问题:哪位黑眼豆豆乐队成员是唯一主持周六夜现场的人?”,
“FIS 高山滑雪世界锦标赛在 20 世纪 80 年代的哪一年在阿根廷举办?”,
“1-6 之间有多少个巴西数字?”,
“哪位以色列数学家在 20 世纪 70 年代建立了在线序列存储库?”,
“写出 7 个包含三个连续双字母的英文单词。无需提供解释,只需说出单词即可。",
# 在不应产生幻觉的地方添加两个问题
“印度的首都是哪里?”,
“CPU 的全称是什么?”,
]
for question in some_tricky_questions:
print("question", question)
messages = [{"role": "user", "content": f"{question}"}]
gpt_response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
temperature=0.1,
logprobs=True,
max_completion_tokens=60,
)
time.sleep(2)
# 使用低温响应获取困惑度分数
logprobs = [token.logprob for token in gpt_response.choices[0].logprobs.content]
perplexity_score = np.round(np.exp(-np.mean(logprobs)), 2)
# 使用第一个响应初始化集群
clusters = [[gpt_response.choices[0].message.content]]
# 使用更高的温度生成更多响应并检查蕴涵
gpt_response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
n=7,
temperature=0.9,
logprobs=True,
max_completion_tokens=60,
)
time.sleep(2)
# check entailment and form clusters
responses = [choice.message.content for choice in gpt_response.choices]
for response in responses[1:]:
found_cluster = False
for cluster in clusters:
if check_entailment(cluster[0], response):
cluster.append(response)
found_cluster = True
break
if not found_cluster:
clusters.append([response])
cluster_probs = [len(cluster) / (len(responses) + 1) for cluster in clusters]
discrete_entropy = calculate_entropy(cluster_probs)
print("clusters", clusters)
print("no of clusters", len(clusters))
print("perplexity", perplexity_score)
print("entropy", discrete_entropy)
在这段代码中,首先初始化一个布尔变量 `found_cluster` 为 `False`。接着,遍历 `clusters` 中的每个聚类,如果 `check_entailment` 函数判断当前聚类的第一个元素与 `response` 之间存在蕴含关系,则将 `response` 添加到该聚类中,并将 `found_cluster` 设置为 `True`,然后跳出循环。如果没有找到合适的聚类,则将 `response` 作为一个新聚类添加到 `clusters` 中。
接下来,计算每个聚类的概率 `cluster_probs`,其值为该聚类中元素的数量与所有响应数量加一的比值。然后,利用 `calculate_entropy` 函数计算离散熵 `discrete_entropy`。最后,打印出聚类的内容、聚类的数量、困惑度分数和熵值。