LLMs 修复自身错误的能力如何?

发布于2024年12月5日
在 GitHub 上更新

Keras和TPU上的聊天机器人竞技场实验

👉您可以在阅读时体验Keras聊天机器人竞技场。
点击此处在新标签页中打开。👈

目录
   1. 引言
   2. 实验
   3. Keras聊天机器人竞技场技术:Spaces、Gradio、TPU、JAX和Keras
      3.1 为什么选择TPU?
      3.2 为什么选择JAX和Keras?
      3.3 分片模型?
      3.4 哪些模型?
   4. 结果
      4.1 可靠性
      4.2 完整对话——修复错误
      4.3 更多错误修复
   5. 总结

1. 引言

我对LLM解决大问题不感兴趣,恰恰相反。我希望它们能够处理琐碎的工作,如果第一次没有做对,一个简单的英语句子就足以修复它。简而言之,我想要一个助手,就像旧科幻电影中的电脑一样,只是没有“对不起,戴夫,恐怕我不能那样做”的部分😅。

这篇论文探讨了这样一种用于编码的工具。撇开其富有创意的标题声明(不,人工智能还没有击败Kaggle特级大师),论文作者所做的是将各种Kaggle问题手动分解为微任务,让LLM为它们生成代码,并迭代直到单元测试通过。一个微任务的例子可能是,对于图像分类问题,需要“弄清楚输入数据的格式,并将其重新格式化为包含‘id’、‘image_filename’和‘class’列的CSV文件”。

我喜欢这种方法,因为这是我未来希望与人工智能合作项目的方式。让AI生成无聊的代码片段,比如数据重新格式化,这样我就可以专注于有趣的部分:正确地构建问题并设计出解决方案的步骤。

但是,这个交互式编码助手必须能够理解简单的英语反馈并修复其代码中的错误。凭借LLM从知识和上下文推断信息的能力,这可能是一个非常高效的计算机接口。但是,如果LLM的怪癖,如幻觉或缺乏形式逻辑,成为障碍,我们最终可能会得到“人工愚蠢”而不是人工智能。

所以我决定用今天的LLM进行一次小测试。一个超级简化的测试,看看当您指出错误时,LLM修复自身错误的效率如何。

2. 实验

以下是场景描述

系统提示

你是一个移动设备上的语音助手。你的工作是使用这个Python API将用户请求转换为API调用

action.add_calendar_entry(title, date="YYYY-MM-DD", time="HH:MM", duration=m) # duration in minutes
action.remove_calendar_entry(title, date, time)

你可以使用30分钟作为新事件的默认持续时间。对每个请求,你都必须用一行可执行代码进行响应。

对话提示 预期输出
在11月11日下午5点与弗雷德安排一个会议 action.add_calendar_entry("与弗雷德的会议", date="2023-11-11", time="17:00", duration=30)
当前年份是2024年。 action.add_calendar_entry("与弗雷德的会议", date="2024-11-11", time="17:00", duration=30)
我当天晚上8点要去听一场摇滚音乐会。
添加一个日历条目。
action.add_calendar_entry("摇滚音乐会", date="2024-11-11", time="20:00", duration=30)
持续时间设置为3小时。 action.add_calendar_entry("摇滚音乐会", date="2024-11-11", time="20:00", duration=60*3)
第二天早上8点,和保罗开个会。半小时。 action.add_calendar_entry("与保罗的会议", date="2024-11-12", time="08:00", duration=30)
取消与弗雷德的会议。 action.remove_calendar_entry("与弗雷德的会议", "2024-11-11", "17:00")

就是这样。非常简单,但LLM能处理吗?当它们犯错时,你只需告诉它们是什么错误,就能期待它们修复吗?

为了测试这个,我需要一个能够同时快速与多个聊天机器人交互的环境,以下是我的设置方式。

3. Keras聊天机器人竞技场技术:Spaces、Gradio、TPU、JAX和Keras

为了进行这个场景实验,我希望能够同时进行两次对话,使用不同的LLM,并暂停其中一方,同时要求另一方修复其输出中的错误。以下是它的样子。它是使用Gradio on Spaces构建的,并使用Keras、JAX和TPU。

screenshot

在我们回到与LLM聊天的严肃问题之前,先介绍一下它的构建方式。

3.1 为什么选择TPU?

因为它们推理速度快,内存大。一个TPU v5e 2x4拥有8个核心,每个核心16GB内存,总计128GB内存。有了这么大的内存,我们可以一次性加载多个LLM,只要我们将它们分片到所有核心,并可以在用户界面中随意切换。在这个实验中,我成功加载了5个约80亿参数的模型(再多一个就会内存溢出)和3个约20亿参数的模型,总共有7个LLM同时驻留在内存中,采用bfloat16格式。

3.2 为什么选择JAX和Keras?

JAX是TPU的首选ML环境,这得益于其强大的XLA编译器。Keras现在可以在JAX(以及PyTorch和TensorFlow)之上原生运行,是我最喜欢的建模环境,它的姊妹库KerasHub中有很多预训练的LLM可供选择。它甚至可以从Hugging Face加载选定的非Keras检查点,这对于比较很有用。我之前在这里写过关于此的内容:Keras中的Llama 3.2

3.3 模型分片?

我还使用Keras,因为它是模型并行化方面最用户友好的API。在这里,我想尽可能多地将模型加载到TPU内存中。为此,模型必须跨所有8个TPU核心的内存进行分片。幸运的是,它们中的大多数都带有一个默认的布局映射,正是这样做的。例如

layout_map = keras_hub.models.Llama3Backbone.get_layout_map(device_mesh)

有关完整的加载代码以及模型并行性的更多背景信息,请参阅我之前的文章。您还会在该文章中找到一个代码片段,用于可视化模型加载后实际应用的分片。这对于调试非常有用。是的,调试和一些布局映射调整是必要的

3.4 哪些模型?

为了这个实验,我选择了参数量小于10亿的LLM,主要是考虑到它们的实用性,因为可以同时加载许多模型。此外,实验测试的内容相当简单,应该在这些较小模型的处理范围内。所有模型都经过指令微调,以便进行对话。您可以在演示的实现中查看它们的聊天模板。欢迎复制粘贴代码以满足您自己的Keras聊天机器人需求。这些模型来自Gemma、Llama3、Mistral和Vicuna系列。完整的列表请参见下面的结果表格。

4. 结果

4.1 可靠性

首先,我们先看看我们的LLM能否可靠地回答第一个问题。系统提示和第一个问题“在11月11日下午5点与弗雷德安排一个会议”重复了五次。

颜色代码

  • 一个如果模型生成预期输出(即API调用),则授予勾选标记
    action.add_calendar_entry("与弗雷德的会议", date="2023-11-11", time="17:00", duration=30)
  • 一个🍄红色毒蘑菇表示答案大部分正确但包含错误(例如:错误的日期)
  • 🔥垃圾桶着火表示响应是垃圾,没有可识别的API调用。
模型 仅第一个问题,尝试五次
Gemma 2 9B-instr ✓  ✓  ✓  ✓  ✓ 
Llama-3.1 8B-instr ✓  ✓  ✓  ✓  ✓ 
Llama 3.2 3B-instr ✓  ✓  ✓  ✓  ✓ 
Llama 3.2 1B-instr 🔥 🍄 🔥 🔥 🔥
Gemma2B-instr 🍄 🍄 🍄 🍄 ✓ 
Codegemma7B-instr ✓  ✓  ✓  ✓  ✓ 
vicuna 1.5 7b-instr ✓ 🔥 🔥 ✓  🔥
Mistral 7B-instr ✓  ✓  🍄 ✓  🍄

好消息是,一些模型每次都做对了,并且所有模型都至少成功一次地用API调用(或多或少正确)回答了问题。然而,较小的1-2B参数模型和像Vicuna这样的旧模型表现不佳。它们大部分时间都回答得很糟糕。

4.2 完整对话——修复错误

现在,让我们每次运行两个模型进行完整的对话。如果模型犯了错误,我尝试引导它回到正轨。让我们看看它是否有效。

颜色代码

  • 一个✔︎勾选标记表示生成了有效的API调用
  • 一个🍄红色毒蘑菇表示模型犯了一个错误
  • 一个🥦如果模型在被要求时能成功修复错误,则会得到绿色西兰花

表格中使用了缩写提示以节省屏幕空间。第一个问题故意不精确:会议只给出了月份、日期和时间,但没有给出年份。这是为了确保所有模型至少犯一个错误,并测试它们修复错误的能力。

对话 (完整记录) Gemma 2
9B-instr
对话 (完整记录) Llama-3.1
8B-instr
对话 (完整记录) Gemini
在线
与弗雷德安排一个会议… ✔︎ 🍄 与弗雷德安排一个会议… ✔︎ 🍄 与弗雷德安排一个会议… ✔︎ 🍄
当前年份是2024
🥦
当前年份是2024
🍄
当前年份是2024
🍄
修复API中的年份...
🥦
修复API中的年份...
🥦
我将去参加摇滚音乐会... ✔︎ 我将去参加摇滚音乐会... ✔︎ 我将去参加摇滚音乐会... ✔︎ 🍄
持续时间设置为3小时 ✔︎ 持续时间设置为3小时 ✔︎
需要持续时间...
🍄
第二天与保罗开会... ✔︎ 第二天与保罗开会... ✔︎
使用默认持续时间...
🥦
取消与弗雷德的会议 ✔︎ 取消与弗雷德的会议 ✔︎ 持续时间设置为3小时 ✔︎
第二天与保罗开会... ✔︎ 🍄
错误的第二天...
🥦
取消与弗雷德的会议 ✔︎

Gemma2 9B和Llama 3.1 8B都成功了。Llama需要一个额外的“修复”提示,但设法得到了它的西兰花。🥦.

第三列中给出了Google的Gemini(在线)运行结果以供比较。这是一个比其他两个模型大得多的模型,令人惊讶的是,它并不是最好的。它需要稍微不同的提示,因为Gemini实际上可以向您的Google日历添加条目,因此每次都必须提醒它“使用提供的API进行API调用回答”。即便如此,它还是犯了一些错误,甚至在最后一个提示中把日期弄错了。这表明,对于此任务来说,一个大型模型不一定更好。

让我们来看看小型模型:Llama 3.2 3B、Llama 3.2 1B和Gemma 2B。这项练习对这些模型来说似乎异常困难。这里需要新的符号

  • 一个🔥🔥对于包含3个或更多错误的响应,表示“垃圾”。逐一修复它们是无用的。
  • 括号中的(🍄)红色蘑菇表示反复出现的错误,每行都相同

请记住,这些是最好的运行结果。如上文“可靠性”部分所示,有些模型在五次尝试中只有一次能够通过第一个问题。

对话 (完整记录) Llama 3.2
3b-instr
对话 (完整记录) Llama 3.2
1B-instr
对话 (完整记录) Gemma
2B-instr
与弗雷德安排一个会议… ✔︎ 🍄 与弗雷德安排一个会议… ✔︎ 🍄 与弗雷德安排一个会议… ✔︎ 🍄
当前年份是2024
🥦
只需API调用...
🥦 🍄
时间错了...
🥦(🍄)
我将去参加摇滚音乐会... ✔︎
遵守日期格式...
🥦
只需API调用...
🥦
持续时间设置为3小时 ✔︎ 🍄
当前年份是2024
✔︎
当前年份是2024
🥦(🍄)
错误的API调用...
🥦 我将去参加摇滚音乐会... 🔥🔥 我将去参加摇滚音乐会... ✔︎ 🍄
和保罗开会... ✔︎ 🍄
需要持续时间...
🥦 🔥
时间错了...
🥦(🍄)
遵守日期格式...
🔥🔥
多余的括号...
🔥🔥 持续时间设置为3小时 ✔︎(🍄)
取消与弗雷德的会议 🔥🔥 持续时间设置为3小时 🔥🔥 和保罗开会... ✔︎
——放弃——
取消与弗雷德的会议 ✔︎ 🍄
API需要三个参数...
🥦(🍄)

在小型模型中,只有Gemma 2B成功完成了对话,尽管存在一个重复的错误(🍄):它无法克制自己,在请求的API调用之上添加了额外的内容。例如“好的,这是更新后的代码……”。它还不断混淆日期和时间。然而,在被要求时,它能够修复错误🥦.

最后,我们来尝试一些旧模型,例如Vicuna 1.5 7B和Mistral 7B。它们与Codegemma 7B进行了对比,Codegemma 7B应该是这项任务的理想模型,但正如您所看到的,所有这三个模型都表现不佳。

对话 (完整记录) Codegemma
7B-instr
对话 (完整记录) vicuna 1.5
7b-instr
对话 (完整记录) Mistral
7B-instr
与弗雷德安排一个会议… ✔︎ 🍄 与弗雷德安排一个会议… ✔︎ 🍄 与弗雷德安排一个会议… ✔︎ 🍄
当前年份是2024
🥦(🍄)
当前年份是2024
🥦
遵守日期格式...
🥦 🍄
年份输入错误...
(🍄) 我将去参加摇滚音乐会... ✔︎
24小时制时间...
🥦
我将去参加摇滚音乐会... ✔︎(🍄) 持续时间设置为3小时 🔥🔥
当前年份是2024
🥦 🍄
持续时间设置为3小时 ✔︎(🍄)
只需API调用...
🔥🔥
只需API调用...
🥦
和保罗开会... ✔︎(🍄) 和保罗开会... ✔︎ 🍄 我将去参加摇滚音乐会... 🍄
取消与弗雷德的会议 ✔︎ 🍄(🍄)
只有一个API调用...
🍄
你不需要那个信息...
✔︎ 🥦
API需要三个参数...
🥦 🍄(🍄) 取消与弗雷德的会议 ✔︎ 🍄 持续时间设置为3小时 ✔︎
现在是错误的事件...
🥦(🍄) 和保罗开会... ✔︎ 🍄
年份有误...
🥦🍄
取消与弗雷德的会议 ✔︎

Codegemma受到一个反复出现的顽固错误(🍄)的影响:它会把年份拼写成“20 24”,中间有一个空格,并且不会修复它。Vicuna 1.5 7B可能太旧了。它有时会开始重复自己🔥🔥,输出多个重复的API调用和其他垃圾。它在一定程度上回到了正轨,但仍有错误。最后,Mistral到处犯错,但也能够修复它们。需要大量的交互,但获得了6个西兰花🥦用于修复的错误。

4.3 更多错误修复

其中一些模型在练习中几乎没有犯错,因此也没有多少机会去修复它们并获得西兰花🥦。通过Keras聊天机器人竞技场用户界面,我们可以在它们对其他LLM造成的错误上运行,看看它们是否能修复。🍄由其他LLM造成的,看它们能否修复。

颜色编码与之前相同:绿色西兰花🥦表示成功修复错误,红色毒蘑菇🍄如果错误仍然存在,则表示“垃圾堆火灾”🔥表示多个错误。对话的完整记录在这里

模型 时间错误...
3次尝试
仅限API...
3次尝试
API错误...
3次尝试
Gemma 2 9B-instr 🥦 🥦 🥦 🥦 🥦 🥦 🍄 🍄 🍄 (1)
Llama-3.1 8B-instr 🥦 🥦 🥦 🥦 🥦 🥦 🥦 🍄 🔥
Llama 3.2 3B-instr 🥦 🥦 🍄 🍄 🥦 🍄 🍄 🔥 🍄
Llama 3.2 1B-instr 🔥🔥 🔥 🥦 🍄 🍄 🔥🔥 🔥
Gemma2B-instr 🥦 🥦 🥦 🥦 🥦 🥦 🔥🔥 🔥
Codegemma7B-instr 🥦 🥦 🥦 🥦 🥦 🥦 🍄 🍄 🍄 (1)
vicuna 1.5 7b-instr 🥦 🔥 🔥 🍄 🥦 🍄 🔥🔥 🔥
Mistral 7B-instr 🥦 🔥 🥦 🥦 🥦 🍄 🍄 🔥 🍄(1)

(1) 反复出现的错误:除了正确的API调用之外,还会输出道歉。对于Gemma来说,这可以通过要求“请仅限API调用”来可靠地修复。对于Llama来说也有效,但不可靠。

这对这些模型来说是一次很好的现实检验。Gemma 2 9B和Codegemma 7B几乎做对了,但它们会不断为一些错误道歉,而不是输出一个干净的API调用。Llama 3.1 8B紧随其后,但难以可靠地修复错误的API调用。而所有其他模型都是🔥垃圾场火灾。

5. 总结

在开始这个测试之前,我不知道会发生什么。模型正在使用的API简单得不切实际。只有两个API调用:“add_calendar_entry”和“remove_calendar_entry”。所以我认为这对于模型来说会超级容易,而且只要稍加纠正性提示,所有模型都能每次都完美通过。另一方面,我知道LLM是概率推理机器,它们并不真正听你说什么。提示只会改变输出的概率分布,有些输出就是很难得到。

现实很有趣,只有Gemma 9B一个模型几乎完美地通过了测试。以下是所有✔︎勾选标记(正确答案)、🥦西兰花(错误修复)、🍄毒蘑菇(错误)和🔥垃圾场火灾(一个答案中出现多个错误)的模型在所有测试中获得的结果总结。这不是最科学的总结结果方式,但它提供了一个很好的概览。

排序 模型 响应评级
#1 Gemma 2 9B-instr ✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎🥦🥦🥦🥦🥦🥦🥦🍄🍄🍄🍄
#2 Llama-3.1 8B-instr ✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎🥦🥦🥦🥦🥦🥦🥦🥦🍄🍄🍄🔥
#3 Codegemma7B-instr ✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎🥦🥦🥦🥦🥦🥦🥦🥦🥦🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄
#4 Mistral 7B-instr ✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🔥🔥
#5 Gemma2B-instr ✔︎✔︎✔︎✔︎✔︎✔︎🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🔥🔥🔥
#6 Llama 3.2 3B-instr ✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎✔︎🥦🥦🥦🥦🥦🍄🍄🍄🍄🍄🍄🍄🍄🔥🔥🔥🔥🔥
#7 vicuna 1.5 7b-instr ✔︎✔︎✔︎✔︎✔︎✔︎🥦🥦🥦🍄🍄🍄🍄🍄🍄🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
#8 Llama 3.2 1B-instr ✔︎✔︎🥦🥦🥦🥦🍄🍄🍄🍄🍄🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

我毫不怀疑,一些微调可以大大改善这些结果。这留待读者探索。有关Keras的更多信息,包括一个Keras LLM微调示例,请参阅这篇博客文章。您也可以随意克隆Keras聊天机器人竞技场来测试您微调后的模型。祝您玩得开心🥦!

社区

注册登录发表评论