AI 情感聊天机器人之旅 —— 多轮对话存在的问题与数据积累

本文主要是介绍AI 情感聊天机器人之旅 —— 多轮对话存在的问题与数据积累,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在 QA、逻辑推理等领域,多跳问答比单跳问答难得多。在聊天机器人场景中亦是如此,模型需要结合历史对话和用户当前的输入内容生成合适的响应。然而,现有的指令数据大都是单轮或者两轮的对话(截止这篇文章落笔的日期 2023-09-10),模型在对话轮数较少时,还能很好地遵循指令、记住历史信息以及输出合适的内容。但对话轮数多了后,模型的输出往往会变得不可控,例如越来越长、格式出现错误、遗忘历史信息、指令遵循能力变弱(人设不符)。一方面是因为上下文变长、加上 Lost in the middle 等 prompt 层面的影响;另一方面是模型在多轮对话下的对齐能力较弱(多轮对话数据的训练数据质量较差,且在 long context 场景下“锻炼”得不够)。

在处理多跳问题和数学推理等较难任务时,除了在模型层面做优化外,往往会使用 CoT 系列的 prompt 工程,例如“Let’s think step by step”的方式。我理解它们能提升 LLM 推理能力的一部分原因在于将任务拆解,输出中间步骤,对于从左到右的生成式模型来说,这些补齐的中间步骤的生成为输入空间带来了更细致的条件约束,而这些条件约束引导模型输出更为准确的结果

Prompt 对于模型的生成有着至关重要的影响,在另外一个将对话内容抽取摘要和标题的项目中,对 prompt 做一些细微的调整,例如仅删除个别词语,先后两次模型的 greedy 输出的相似度相差就非常大。如果将模型的输出过程比作一条长长的道路,前面每一步的偏差都会通往不同的终点。有的道路一帆风顺,而有的道路布满荆棘、高山险阻、沟壑纵横。“Let’s think step by step” 就像路途中的补给站,当你来到这里,补给站中的人给你指向了一条缓慢但平坦的道路,虽然要多走几步路,但更容易通往正确的终点。CoT 等后续发展的一系列思维链相关的 prompt 工程都通过人为添加或者引导模型自行生成中间“道路”的方式,来“安稳”抵达期望的“终点”。

多轮对话存在的问题

我们的 AI 情感聊天机器人在对话轮数多了之后,效果会明显下降。甚至产品反馈,当超过 50 轮对话后,模型会出现百分百的重复回复(大量重复的短语或近似的语义)。我的推测是:

  • 对话到后期,用户与模型都陷入到“无话可说”的地步,用户没有“强烈”地转换话题。同时,模型在几轮雷同回复的条件约束下很难有“新颖”的表达方式。
  • 对数据的理解不够透彻、对数据的质量没有把控,尤其在 3000 - 3500 token(超过 3500 token 会被截断)范围内的数据质量没有肉眼审核过,如果这部分的数据大都是雷同的,那么模型学到的或许就不是先后顺序,而是类似 ICL 演示(few-shot)的并列关系,从而去模仿历史对话的表达方式和类比生成,这样就很容易重复。关于这部分的内容,后续会将相关的研究写一篇新的博客。
  • 统一数据格式(包括 prompt 的格式)时没有做好规范,导致模型的输出不“规范”,甚至有些“离经叛道”。按照上文中提到的关于 CoT 的描述,模型在行进途中的几步就走偏一步,走着走着就来到了“地图”上没有标识的地区(SFT 阶段没有训练好,不仅损坏了模型原本的知识,还让模型不知道该怎么合理地生成符合用户指令的响应)。

结合上述思考和缓解重复问题时的实践,提出一个猜想:随着聊天的进行,更多的 context 会有更强的 conditional 限制,使得模型的输出空间囿于小而窄的空间(实际上该表述并不准确,还需要加入一些限定,例如自研模型、训练数据质量受限,本质上还是模型训练的问题)。

但在使用 gpt-4-1106-preview 作为聊天模型时,很少出现或几乎不会出现多轮重复的问题。它完全按照我们的设想,在遵循用户当前轮的指令(聊天的话题与内容)的前提下,从历史对话中撷取所需的信息,然后生成满足用户需求的响应。一个很直观的感受是,gpt4 能很好地 get 到我们的意思,甚至很多时候我们都不知道该如何表达自己的问题,它也能给出我们所需的答案。因此,一个理解力强的模型,在情感聊天场景中已经具备足够的下限,我们只需要改写其输出,让其少点“AI 味”,多点“人情味”,除了用改写的方式,通过调整 prompt 也能实现,例如:

  • 论文《Guidling Large Language Models via Directional Stimulus Prompting》提出了一个名为“定向刺激 prompt”(Directional Stimulus Prompting)的新型 prompt 框架,先训练一个经过微调、强化学习后的小型模型,然后使用该小型模型根据用户的查询来生成对应的刺激(文本),将其添加到 prompt 中来引导黑盒大语言模型朝着所需的输出方向前进。
  • 论文《EmotionPrompt:Leveraging Psychology for Language Models Enhancement via Emotional Stimulus》的作者从心理学中汲取灵感,提出 EmotionPrompt(情感提示)来探索情商,以提高 LLM 的性能。具体来说,作者为 LLMs 设计了 11 句情感刺激句子,只需将其添加到原始 prompt 中即可。缺陷在于情绪刺激可能并不适用于其他任务,并且对不同 LLM 的效果无法保证。

数据积累

综其根本,还是模型能力的问题:模型的理解能力以及抗干扰能力(在存在冗余、干扰信息且较长的历史对话(context)中找寻能够帮助回答当前轮用户的内容)。

增强模型的指令遵循能力,寻找并清洗得到质量更好的多轮对话数据集,充分对齐模型(SFT + RLHF 的标准道路)。除此之外,也可以考虑扬长避短,将多轮对话“拍平”成单轮对话,发挥模型单轮对话的优势。那么该如何“拍平”成单轮对话呢?

最先想到的方法是减少送入模型的对话轮数,仅保留最近的 5 轮对话,将 context 控制在 2500 token 左右,与收集到的高质量对话数据集的平均 token 保持一致。缺点在于送入的历史信息较少,模型没有“记忆”。但在当时,即便送入 10 轮对话(20 句),历史对话中较为久远的信息,模型也大概率 get 不到,反而会因为大量重复的历史信息(闲聊场景中,大多数的历史对话并没有意义,甚至用户输入的指令也大都没有信息)干扰当前输出。如果 context 中干扰、冗余的信息越少,模型输出的效果越好,那么我们是可以减少送入模型的对话轮数,提升模型输出的质量,并以此积累多轮对话。

在使用 LLM 清洗数据时,发现 vicuna-v1.5 等指令遵循能力较强的模型(当然,现在可以选择能力更强的模型)可以对历史对话进行总结和评分,于是尝试提取历史对话中的核心内容,去除其中的“杂质”与“噪声”,从而规范“过去的道路”,避免其误入“藕花深处”。

  • 将历史对话全部进行摘要:将当前轮前面的所有历史对话转变成摘要,结合 Rethinking Historical Messages(后续有时间会写一篇博客,重点讲述历史对话对当前轮模型输出的影响)的研究结果,该做法可以减少历史几轮对话对当前轮模型输出的影响,《Calibrate Before Use:Improving Few-Shot Performance of Language Models》论文提到的三个偏见。其核心是可以缓解模型输出的重复问题,并且减少 ICL 对模型输出的影响。在多轮重复问题测试集上进行测试,发现该方式的确可以很好地解决模型输出重复的问题。

    但该方式的缺点在于摘要会丢失细节信息,而这些细节信息恰好是未来几轮对话的重点部分,这就会导致对话的不连贯和剧情发展的磕磕绊绊。

    当时对老板提出的一种解释是,原始的历史对话是人与模型的对话,而生成的摘要是模型的摘要,模型更容易理解自己的“语言”,而非人的“语言”,尤其在与人类对齐这方面有所欠缺的模型。此外,模型的摘要抽取能力仍然是模型自身的能力,没有受到 SFT 的影响,输出的摘要内容仍然是模型“语言”。没过多久,微软提出了 LLMLingua 方法,可以对 prompt 进行压缩,压缩后的 prompt 虽然人类不可读,但模型仍然能够识别并输出正确的结果,且可以反向还原。这说明,在对齐还没有达到一定程度(2024-02 使用 LLMLingua 测试 GPT4 时,GPT4 会在生成的响应中说明用户提供的文本存在格式错误的问题,这说明 GPT-4 已经能够很好地理解人类的语言)时,模型更容易理解自身的“语言”。

  • 将每一轮对话都进行摘要:每一轮对话并不一定有足够的信息,尤其在闲聊场景下,这会导致前后两轮的摘要内容几乎没有变化,那么模型的输出很大程度上取决于当前轮用户的输入内容,如果用户输入内容也没有太多变化,模型的输出很容易雷同,仍然存在重复问题。

  • 将久远历史对话进行摘要:不再将“多余”的历史对话删除,而是用 LLM(vicuna-13B-v1.5)从中抽取信息,以前情提要的形式添加到 system prompt 中,拼接成 system prompt + 前情提要 + 最近 5 轮历史对话的形式。上线后,平均对话轮数得到了提升。

将历史对话全部进行摘要类似 MQA、将每一轮对话都进行摘要类似 MHA,将两者折衷,每 N 轮进行一次摘要,或者当有较大变化时,进行一次摘要(类似 GQA,关于 GQA 可以参见我的另一篇博客《论文阅读:GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints》)。

核心思想是提供更好、冗余干扰信息越少的 context,提升模型每一轮输出的质量,从而积累多轮对话的训练数据

后续(23 年 12 月份)尝试对历史对话进行压缩(使用 LLMLingua),从表象上来看,对 prompt 进行压缩与抽取摘要相同,都是输入较长的 prompt,得到简短的、新的 prompt,但本质上是不同的。抽取摘要的核心还是将 prompt 进行精细加工,输出成人类可读的文本内容,而 prompt 压缩并不为人类可读而服务,压缩后的文本内容对于人类不一定可读,但模型能够正确识别,甚至可以还原回原先的 prompt,更像是压缩软件的压缩过程。该方法也可以缓解重复问题,但对模型的输出效果会有一定的影响。

这篇关于AI 情感聊天机器人之旅 —— 多轮对话存在的问题与数据积累的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/989624

相关文章

MybatisGenerator文件生成不出对应文件的问题

《MybatisGenerator文件生成不出对应文件的问题》本文介绍了使用MybatisGenerator生成文件时遇到的问题及解决方法,主要步骤包括检查目标表是否存在、是否能连接到数据库、配置生成... 目录MyBATisGenerator 文件生成不出对应文件先在项目结构里引入“targetProje

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

Oracle数据库使用 listagg去重删除重复数据的方法汇总

《Oracle数据库使用listagg去重删除重复数据的方法汇总》文章介绍了在Oracle数据库中使用LISTAGG和XMLAGG函数进行字符串聚合并去重的方法,包括去重聚合、使用XML解析和CLO... 目录案例表第一种:使用wm_concat() + distinct去重聚合第二种:使用listagg,

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1