CCF对话式检索增强生成Top1赛后方案

向量数据库大模型云通信
前言

作者
Winnie; 马千里
说在前面
不知不觉已经坚持大模型比赛逾半年,期间学习和收获了很多。非常幸运,在最近的CCF BDCI中荣获全赛道特等奖。遂将方案分享,期望能和大家共同进步。
比赛链接
https://www.datafountain.cn/competitions/1074

赛题背景

    赛题针对对话式检索增强进行,简单来说就是给出了多轮历史对话以及最新的问题,期望选手检索相关文档,然后用大模型给出回答。相对于经典的检索增强,该赛题基于多轮对话场景。

数据描述

    赛题使用CORAL基准测试数据集,并且提供了来源于wikipedia的相关知识库,相关论文链接为:
https://arxiv.org/pdf/2410.23090
对话结构主要分为以下四种类型:

  • 线性递进型 对话随着每一轮的进行,围绕单一主题逐步深入探讨。
  • 广度拓展型 对话在深入探讨的同时,围绕一个主题逐渐拓宽讨论范围。
  • 灵活探索型 对话在进行中,围绕一个主题自由地探索不同的子话题。
  • 话题转换型 对话在展开过程中,出现主题的跳跃。

评价指标

  • 评价方法 采用ROUGE-L评价指标。计算方法可参考该文章:
    https://zhuanlan.zhihu.com/p/659637538
  • 抖个激灵 针对ROUGE-L尝试过很多偷分方式,比如拼接多次输出,但都不如直接用模型输出。
算法实现

    分析完赛题和数据,接下来我们将深入探讨实现方案。

整体流程

    结合行业经验,本次比赛我们遵循对话式检索增强最常见的实现流程:

  1. 问题改写 结合历史对话,改写用户最后的问题。改写后的问题能包含历史对话的信息,使其嵌入也更有代表性,往往能检索到更好的文档。我们的做法是简单将历史对话和问题传给微调的模型,让其进行改写,不再赘述。
  2. 检索增强 根据改写的问题检索相关文档。针对该题我们设计了一套上下文感知的混合检索技术,下一小节重点介绍。
  3. 提示词设计 得到相关文档后,将所有信息整合成提示词,传入大模型从而得到答案。后面小节会给出我们常用的提示词结构供大家参考。

上下文感知的混合检索

1.多路召回

    在完成问题改写后,我们以召回-精排的方式检索到相关文档。先做检索召回(pointwise),以多种方式召回相关文档,本题中我们最终使用了两种召回方式:

  1. 嵌入召回 使用嵌入模型,对知识库文档进行嵌入,然后根据改写问题的嵌入,找到和它最接近的一些文档。是最常见的召回方法。文本嵌入方法可以参考以下链接:
    https://huggingface.co/spaces/mteb/leaderboard
  2. 关键词召回 嵌入式方法可能会缺失对关键实体的感知。为了弥补这一点,我们在改写问题中提取关键实体,然后在文档中搜索该实体的文档进行召回。相对BM25,我们的方法更快而且能召回更多可能相关的文档。
  • 关键词召回示例代码

        
          
from joblib import Parallel, delayed  
import spacy  
def process\_ent(row):  
    nlp = spacy.load("en\_core\_web\_sm")  
    return nlp(row['rewrite'])  
def process\_ref(row):  
    if len(row['entity']) == 0:  
        return []  
    ref_pattern = '|'.join(re.escape(entity) for entity in row['entity'])  
    return df_doc.loc[df_doc['ref\_string'].str.contains(ref_pattern), 'idx'].tolist()  
# 找到关键实体(仅支持英文)  
entitys = Parallel(n_jobs=-1)(delayed(process_ent)(row) for _, row in tqdm(df.iterrows(), total=len(df)))  
df['entity'] = [list(set([e.text for e in x.ents if e.label_ in ['PERSON','ORG', 'WORK\_OF\_ART']])) for x in entitys]  
# 检索有该实体的所有文档  
ref_ids = Parallel(n_jobs=-1)(delayed(process_ref)(row) for _, row in tqdm(df.iterrows(), total=len(df)))  
df['ent\_docs'] = ref_ids  

      

2.精排截断

    召回文档过多会导致模型难以感知关键信息,所以需要对结果进行筛选。我们将召回文档和问题一起放入交叉编码模型(pairwise):

  • 实施方式 使用reranker模型,将召回的文档和问题进行一一匹配计算得到相关性得分,并根据得分截取Top5文档。
  • 算法介绍 召回时使用的双编码器Bi-Encoder对句子之间的关系一无所知。而Cross-Encoder会利用自注意力机制不断计算这两个句子之间的交互(注意力),最后接一个分类器输出一个分数(logits)代表相似度(可以经过sigmoid变成一个概率)。
  • 示意图

picture.image

3.上下文感知重排

    这是我们拿到赛道一等奖和全赛道特等奖的关键。本节算法目的是重排截取的文档,让最终文档的信息连续且互补(contextual):

  • 背景假设 我们假定在对全部的知识库做了切分之后(切分是为了让每个文本块信息密度更高),得到了文档的index。相邻的index可能是从同一片资料中切割出来的,有连续关系。
  • 实施方式 在得到五个文档后,我们从这些文档随机抽取一个子集,并遍历所有的排列方式。让排列完的列表和文档输入相似度打分,然后保留得分最高的列表,作为最终提示文档。还有很多打分方式,比如让大模型去评估每个列表是否语义连续、是否包含所需信息。
  • 特殊优化 我们做了剪枝操作来进行加速,我们会将列表按index做排序,这样可以保证不会把牛尾放在牛头之前。并且我们做了一个补全操作,比如重排文档中,只有90/92/93这三个index的文档,我们会用启发式算法自动补上91,这又提升了召回上限,几乎保证了不会缺失信息。
  • 流程图

picture.image

Prompt设计技巧

    Prompt设计是大模型竞赛重要的一环。有一些常用的技术,这里我们简单给出多轮对话下常用的Prompt格式:

  • 注意事项 本题中,召回文档较长,为了防止问题被截断,我们将问题放在中间,但会导致Loss-in-the-middle。具体排列方式可多做尝试。
  • Prompt示意

picture.image

结尾

联系
如有疑问或反馈,欢迎联系944632634@qq.com
讲在后面
希望我们的方案能够对大家学习大模型有帮助,未来也会继续分享我们在其他比赛的优质方案,希望能跟大家共同进步!

0
0
0
0
关于作者
相关资源
如何利用云原生构建 AIGC 业务基石
AIGC即AI Generated Content,是指利用人工智能技术来生成内容,AIGC也被认为是继UGC、PGC之后的新型内容生产方式,AI绘画、AI写作等都属于AIGC的分支。而 AIGC 业务的部署也面临着异构资源管理、机器学习流程管理等问题,本次分享将和大家分享如何使用云原生技术构建 AIGC 业务。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论