有道 QAnything 背后的故事 --- 关于 RAG 的一点经验分享
近日,我们开源了有道自研的 RAG(Retrieval Augmented Generation) 引擎 QAnything。该引擎允许用户上传 PDF、图片、Word、Excel、PowerPoint 等多种格式的文档,并实现类似于 ChatGPT 的互动问答功能,其中每个答案都能精确追溯到相应的文档段落来源。QAnything 支持纯本地部署,上传文档数量无上限,问答准确率高。
QAnything 自开源以来,迅速吸引了开发者社区的广泛关注,并很快登上了 GitHub trending 榜单。短短一个月内,下载次数已达数万次,其中,我们的语义嵌入排序模型 BCEmbedding 更是达到了惊人的 60 万次下载。根据社区的热情反馈,我们决定分享 QAnything 背后的研发故事、技术路线选择以及我们的经验,希望能够为社区带来启发。
QAnything 的起源
与市场上的其他 Retrieval Augmented Generation (RAG) 产品相比,QAnything 引擎的研发轨迹略显不同。它不是一开始就被设定为一个具体的项目目标,而是在项目进展中,通过不断的探索和实践,逐步成形的。这个过程虽然经历了一些波折,但正是这些经历,让我们在 RAG 领域积累了丰富的实践经验。
从文档翻译到文档问答
QAnything 的研发团队最初专注于文档翻译。2022 年我们启动了一个为期一年的文档翻译的升级的项目,到 2023 年 3 月份上线,效果提升显著。正好那时候 ChatGPT 和类似技术正在兴起,我们意识到这正是将我们现有技术扩展至文档问答的绝佳时机。因此,我们毫不犹豫地为我们的文档翻译服务增添了问答功能,该功能能够根据文档内容自动推荐问题并提供答案,于 5 月份正式推出。
(视频链接:https://www.bilibili.com/video/BV1fw4m1Z7QX/)
之所以能够轻松地扩展到文档问答,是因为有道在文档翻译领域的深厚积累。我们的文档翻译服务因其卓越的性能而闻名,这主要得益于两大核心技术:先进的翻译引擎和精准的文档解析/OCR 技术。多年来,在翻译和 OCR 领域的持续探索和创新,为我们构建 Retrieval Augmented Generation (RAG) 系统提供了坚实的基础。
首先,核心技术方面,我们的翻译模型基于 Transformer 架构,这与当前研究领域的大型语言模型(LLM)紧密相连,实质上并无显著区别。所谓 LLM,就是很大的 Transformer 模型,就是我们天天在研究的东西。ChatGPT 出来后,我们之所以能迅速掌握并扩展我们的模型,例如开发了针对教育场景的“子曰”大模型,这一切都得益于我们对 Transformer 模型的深入理解和应用。
接着,关于 RAG 系统,它不仅仅是外部数据和 LLM 的简单叠加。鉴于用户文档的多样性,特别是 PDF 文件中复杂的图文混排,仅仅提取文本往往会带来信息的失真。例如,将具有逻辑连贯性的文本分割成多个片段,或者将图表数据错误地融入文本,这些都会严重影响信息的准确性。正因如此,我们长期致力于文档解析技术的研发,能够精确地识别和分析文档中的每一部分,无论是段落、图表、公式还是其他元素,确保将用户的查询以最适合机器处理的方式进行组织和检索。
借助有道翻译庞大的用户基础,我们得以在实际应用中不断完善和优化我们的系统。日均活跃用户数达百万级别的大数据反馈,为我们提供了宝贵的实践经验,使我们能够持续提升系统性能,满足用户对高质量翻译和问答服务的需求。
从文档问答到速读
有道速读(https://read.youdao.com) 是我们算法研究员从自己的需求出发做的产品。有道翻译桌面端虽然已经上线了文档问答,但是它主要是面向大众设计的,适合通用的文档。我们经常读论文,希望有一些论文相关的更个性一点的功能。而有道翻译用户量太大了,不方便随意改动。
我们做有道速读,一开始主要是面向论文阅读做的。这也是我们新技术的试验田,迭代快一点。我们看到了 wordtune 出了个段落摘要和对照的功能,用着特别爽,但是很贵,我们就把那个功能与 RAG 整合在一起,又能摘要读段落,又能问答,方便溯源。论文一般都会讲自己方法多好,我们就把其他人对这篇论文的评价信息也给整合起来了,做了论文口碑,把一篇论文的优势和局限更客观的展示出来。在内部做研发的过程中,有个研究人员希望能自动写综述,我们就在速读上加上了自动综述的功能,对每一篇论文,把前后引用的论文全部抓来,自动做问答,然后整理成报告。
有道速读可以看作是 RAG 在某个垂直领域的应用。因为里面的口碑、综述、文章解读等功能,都可以认为是先设置一个模版,有一堆问题(或者自动生成的),然后通过自问自答的方式,生成关键信息,最后再总结润色成文,这一切过程都是全自动的。
速读给了我们一个训练场,让我们调试应用新技术,这个过程也学到了很多,团队进步很大。当然,速读现在也不只局限于论文阅读了。
从速读到 Qanything
速读主要是单篇问答(6 月份刚上线时候只支持单篇,现在也支持多篇问答了,和 QAnything 主要区别是速读更偏重阅读的场景,QAnything 偏重问答的场景,底层引擎是一样的),且只支持 pdf 的格式。QAnything 是支持多文档问答的,不限文档格式。
做 Qanything 有个契机。去年 7 月份的时候,网易的 IT 集团想升级他们的客服系统,找到我们,问能否基于他们的 IT 文档做一个自动问答机器人,因为他们看到了我们的文档问答效果,觉得做的不错。于是我们就拿着他们的文档和历史的问答数据快速实验了一下,发现经过我们的系统后,70%的转人工的次数都可以被省下来,由 AI 来回答。
客服这个场景,用户的文档格式非常多样,回答问题需要综合各种文档的内容。于是我们在这个场景需求的推动下,做了多文档问答。我们给这个多文档问答系统取了一个大气的名字,叫 Qanything,中文名字叫“万物皆可问”。
QAnything,也是我们的愿景。QAnything 的前两个字母是 Q 和 A,也是问答的意思,后面是 anything,希望什么都可以放进去,什么东西都可以提问。
在去年 8 月份的时候,除了内部客户要,有道智云的外部 B 端客户也需要这样的多文档问答系统,还需要私有化。于是我们就做了大模型的小型化适配,做了私有化的版本,可以直接跑在游戏本上的。整个系统是完整的,可直接使用,也可以通过 API 调用。给了客户,卖了点钱。
(视频链接:https://www.bilibili.com/video/BV1FC411s7gQ/)
从 QAanyhing 到升学咨询
我们一直将 qanything 的体验页挂在网上,主要是为了做 有道智云 toB 生意的时候给外部用户体验的,也没怎么宣传。去年 9 月份的时候,突然有一天,我们的精品课事业部(有道领世)的人找上门来,说希望合作 QAnything。原来,他们不知道通过哪里的渠道知道了我们的 QAnything,去体验了下,发现效果很好。比他们自己用 langchain/lamma index+chatgpt 搭建了很久的系统,效果要好很多。
有道领世在高中升学领域深耕多年,积累了海量的升学数据资料,有几万份的文档,还有大量的数据存储在数据库里。我们的任务是通过 QAnything,结合这样的积累的数据,打造出一个私人 AI 规划师,针对每个家长和学生,提供个性化、更加全面、专业、及时的升学规划服务。
一开始,我们把全部数据直接塞入我们 QAnything 系统,升学百科问答只有 45%的准确率。经过一段时间的反复迭代优化,我们把确确率提升到了 95%。目前系统可以解答用户关于高考政策、升学路径、学习生活以及职业规划等各种问题。未来随着不断地数据补充和更新,准确率会一直上涨。
有道 AI 升学规划师产品做出来后,我们都为它的体验感到惊艳。
(视频链接:https://www.bilibili.com/video/BV1Bt421b7am/)
Qanything 开源
今年 1 月份,我们整理了下我们的 QAnything 的代码和模型,将适合开源的部分开放出来了。我们做这事,希望能和社区一起,共同推动 RAG 技术应用的发展。最近这个月,社区给了我们很对反馈,也让我们受益良多。
QAnything 架构解析
这次开源包括了模型和系统等所有必要的模块。模型方面包括 ocr 解析、embedding/rerank,以及大模型。系统方面包括向量数据库、mysql 数据库、前端、后端等必要的模块。整个引擎的功能完整,用户可以直接下载,不需要再搭配其他的模块即可使用。系统可扩展性也非常好,只要硬盘内存足够,就可以一直建库,支持无上限的文档。
QAnything 的整体架构图
系统的工作流程主要包含三个环节:
索引(indexing):文本索引的构建包括以下步骤:文档解析、文本分块、Embedding 向量化和创建索引。先将不同格式的原始文件解析转换为纯文本,再把文本切分成较小的文本块。通过 Embedding 为每一个文本块生成一个向量表示,用于计算文本向量和问题向量之间的相似度。创建索引将原始文本块和 Embedding 向量以键值对的形式存储,以便将来进行快速和频繁的搜索。
检索(Retrieval):使用 Embedding 模型将用户输入问题转换为向量,计算问题的 Embedding 向量和语料库中文本块 Embedding 向量之间的相似度,选择相似度最高的前 K 个文档块作为当前问题的增强上下文信息。
生成(Generation):将检索得到的前 K 个文本块和用户问题一起送进大模型,让大模型基于给定的文本块来回答用户的问题。
Bcembedding 模型
Embedding 是 RAG 系统里面最关键的模块。为啥要自己训练 embedding 模型?一开始我们也是直接去尝试 openai 的 ada embedding 以及开源的 embedding 模型。但是我们很快发现,这样做有很大弊端。首先,在我们的业务场景下,外部的 embedding 效果并不如宣传的那么好。openai 的 embedding 接口除了效果不好外,还很慢。我们后来自研了 embedding,因为是放在自己服务器上,调用起来比 openai 接口快一百倍。其次,很多开源的 embedding 模型在 mteb 等地方刷傍刷的很高,但是那些刷榜的分值并不完全能反映真实的效果。第三,我们业务场景有很多混合语言的情况,比如库里面放的是英文的文档,用户用中文去问答。这种跨语种的能力,现有模型支持不好。第四,单纯的 embedding 在检索排序上天花板比较低,所以我们在 embedding 的基础上又做了 rerank,共享同样的底座,head 不一样。
为啥我们自己训练的模型会比 openai 的效果好?我们认为可能是通才和专才的区别。openai 是通才,但是它的效果远未达到万能的地步,大家不必迷信。在我们的场景下(客服问答以及一些 toB 客户的场景),openai 的 ada2 embedding 的检索准确率只有 60%,而经过训练的 bcembedding 检索准确率可以达到 95%。
我们自研的BCEmbedding,总的来讲有两个特色:
中英双语和跨语种能力
我们收集开源数据集(包括摘要、翻译、语义改写、问答等),来实现模型通用的基础语义表征能力。为了实现一个模型就可以实现中英双语、跨语种的检索任务,我们依赖网易有道多年积累的强大的翻译引擎,对数据进行处理,获得中英双语和跨语种数据集。实现一个模型就可以完成双语和跨语种任务。
多领域覆盖我们分析现有市面上常见或可能的应用场景,收集了包括:教育、医疗、法律、金融、百科、科研论文、客服(faq)、通用 QA 等场景的语料,使得模型可以覆盖尽可能多的应用场景。同样的依靠网易有道翻译引擎,获得多领域覆盖的中英双语和跨语种数据集。实现一个模型就可以支持多业务场景,用户可以开箱即用。
我们在训练的过程中,发现一个有意思的现象,数据标签的构建对模型的效果影响非常大。相信大家一定听过“难例挖掘”的概念,在机器学习中模型性能上不去时候,经常是因为一些例子比较难,模型训练时候见的比较少,多挖掘一些难例给模型,就能够提升模型的性能。但是在 embedding 训练的时候,我们发现难例挖掘反而会降低模型的性能。我们猜测原因是 embedding 模型本身的能力有限,不应该给过难的任务。我们想要让模型做多领域覆盖,多语种、跨语种覆盖(还要覆盖代码检索和工具检索),这已经给 Embedding 增加很多负担了,应该想想怎么给 Embedding“减负”。
因为 Embedding 模型是 dual-encoder,query 和 passage 在“离线”地语义向量提取时没有信息交互,全靠模型将 query 和 passages“硬”编码到语义空间中,再去语义检索。而 rerank 的阶段,cross-encoder 可以充分交互 query 和 passage 信息,潜力大的多。所以我们定了目标,embedding 尽可能提高召回,rerank 尽可能提高精度。
我们在 Embedding 模型训练中,不使用难负样例挖掘,只在 Reranker 中使用。以下是我们的几点看法,供参考。
我们在训练 Embedding 模型时发现,过难的负样本对模型训练有损害,训练过程中会使模型“困惑”,影响模型最终性能[19]。Embedding 模型算法本身性能上限有限,很多难负样本只有细微差异,“相似”程度很高。就像让一个小学生强行去学习微积分,这种数据对 Embedding 训练是“有毒”的。
在大量的语料库中,没有人工校验的自动化难负样例挖掘,难免会“挖到正例”。语料库很大,里面经常会混有正例,利用已有 Embedding 模型去挖掘正例,经常会挖到正例,毒害模型训练。应该有不少调参工程师有这种惨痛经历。
其实所谓的“正例”和“难负样例”应该是根据你业务的定义来的。RAG 场景下,之前人们认为的难负样例可能就成为了正例。比如要回答“小明喜欢吃苹果吗?”,RAG 场景下召回“小明喜欢吃苹果”和“小明不喜欢吃苹果”都是符合目标的,而学术定义的语义相似这两句话又是难负样例。
所以回归我们业务目标和好检索器的“评判标准”,Embedding 模型应该能尽量召回相关片段,不要将精排 Reranker 要干的事强压在 Embedding 身上,“越俎代庖”终究会害了它。
检索排序效果评测方式 LlamaIndex(https://github.com/run-llama/llama_index)是一个著名的大模型应用的开源框架,在 RAG 社区中很受欢迎。最近,LlamaIndex 博客(https://blog.llamaindex.ai/boosting-rag-picking-the-best-embedding-reranker-models-42d079022e83)对市面上常用的 embedding 和 reranker 模型进行 RAG 流程的评测,吸引广泛关注。
为了公平起见,我们复刻 LlamaIndex 博客评测流程,将 bce-embedding-base_v1 和 bce-reranker-base_v1 与其他 Embedding 和 Reranker 模型进行对比分析。在此,我们先明确一些情况,LlamaIndex 博客(https://blog.llamaindex.ai/boosting-rag-picking-the-best-embedding-reranker-models-42d079022e83)的评测只使用了 llama v2(https://arxiv.org/abs/2307.09288)这一篇英文论文来进行评测的,所以该评测是在纯英文、限定语种(英文)、限定领域(人工智能)场景下进行的。
<p align=center>LlamaIndex 博客评测复刻</p>
如上表所示,
在没有 Reranker 模块的设置下,bce-embedding-base_v1 显著优于其他常见的开源和闭源英文 embedding 模型。
在相同 reranker 配置下(竖排对比),bce-embedding-base_v1 也都是优于其他开源、闭源 embedding 模型。
在相同的 embedding 配置下(横排对比),利用 reranker 模型可以显著提升检索效果,印证前面所述二阶段检索的优势。bce-reranker-base_v1 比其他常见的开源、闭源 reranker 模型具备更好的精排能力。
综上,bce-embedding-base_v1 和 bce-reranker-base_v1 的组合可以实现最好的效果。
多领域、多语种和跨语种 RAG 效果正如上所述的 LlamaIndex 博客(https://huggingface.co/datasets/maidalun1020/CrosslingualMultiDomainsDataset)评测有些局限,为了兼容更真实更广的用户使用场景,评测算法模型的 领域泛化性,双语和跨语种能力,我们按照该博客的方法构建了一个多领域(计算机科学,物理学,生物学,经济学,数学,量化金融等领域)的中英双语种和中英跨语种评测数据,CrosslingualMultiDomainsDataset(https://huggingface.co/datasets/maidalun1020/CrosslingualMultiDomainsDataset)。
为了使我们这个数据集质量尽可能高,我们采用 OpenAI 的 gpt-4-1106-preview 用于数据生成。为了防止数据泄漏,评测用的英文数据我们选择了 ArXiv 上 2023 年 12 月 30 日最新的各领域英文文章;中文数据选择 Semantic Scholar 相应领域高质量的尽可能新的中文文章。
多领域、多语种和跨语种 RAG 评测
我们针对市面上最强的常用开源、闭源 embedding 和 reranker 模型,进行系统性评测分析,结果如上图所示。
竖排对比,bce-embedding-base_v1 的表现和之前一样,具备很好的效果,语种支持和领域覆盖都很不错。最新的 openai-embed-3 和 bge-m3 表现出顽强的性能,具备良好的多语种和跨语种能力,具备良好的领域泛化性。Cohere 和 e5 的多语种 embedding 模型同样表现出不错的效果。而其他单语种 embedding 模型表现却不尽如人意(JinaAI-v2-Base-zh 和 bge-large-zh-v1.5 稍好一些)。
横排对比,reranker 模块可以显著改善检索效果。其中 CohereRerank 和 bge-reranker-large 效果相当,bce-reranker-base_v1 具备比前二者更好的精排能力。
综上,bce-embedding-base_v1 和 bce-reranker-base_v1 的组合可以实现最好的检索效果(93.46/77.02),比其他开源闭源最好组合(bge-m3-large+bge-reranker-large, 89.94/70.17),hit rate 提升 3.53%,mrr 提升 6.85%。
Rerank 的必要性
为啥需要 rerank,上面数字可能还不直观。我们在开源的 github 上放了一张图,意思是 QAnything 在知识库的数据越多,准确率越高。而一般搭建的 RAG,如果没有 rerank 这一环节,在数据输入多了以后,效果反而下降了。
我们在做升学问答的时候,遇到一个有趣的现象:我们分批往 RAG 知识库中灌入数据,每加一批数据都做一次评测,观察随着数据量变大,问答效果的变化情况:
baseline:第一批数据加入后问答正确率有 42.6%,此时有一些问题没回答上来是因为确实缺少相关资料。我们继续加数据…
迎来上涨:第二批加了更多数据,覆盖知识范围更广。准确率提升到了 60.2%,提升非常明显,看来加数据确实还是挺有用的。
坏消息:当加入第三批数据的时候,我们最担心的事情还是发生了。正确率急剧下降,跌了将近 8 个百分点。
不是所的 RAG 系统都能保证:数据越多,效果越好。随着数据的增多,数据之间可能会有相互干扰,导致检索退化的问题,影响问答的质量。
这个现象在最近的一篇论文:The Power of Noise: Redefining Retrieval for RAG Systems (arXiv:2401.14887v2)也有一些解释,对于 RAG 系统,如果喂给大模型的输入是相近容易混淆的话,对正确性的影响是最大的。
以我们遇到的一个 case 为例,大连医科大学怎么样?这个问题在 v2 版本(加入第三批数据前)是能回答对的,v3 版本(加入第三批数据后)回答错了。看了一下送到 LLM 的文本片段,居然全部都是大连理工大学相关的信息。
主要原因是第三批加入的某些文档中恰好有 "大连理工大学 xxx 怎么样?" 的句子,和 query "大连医科大学怎么样?" 表面上看起来确实非常像,Embedding 给它打了比较高的分。
而类似大连医科大学师资介绍这样的片段相关性就稍微低了些。LLM 输入 token 有限制,前面两个最相关但是实际并不能回答 query 问题的片段就已经占满了 token 的窗口,只能把他俩送进 LLM 里。结果可想而知,啥都不知道。
文本片段与 query 的相似性和文本片段是否包含 query 的答案(相关性)是两回事。RAG 中一个非常重要的矛盾点在于检索召回的片段比较多,但是 LLM 输入 token 是有限制,所以必须把能回答 query 问题的片段(和问题最相关)给 LLM。 Embedding 可以给出一个得分,但是这个得分描述的更多的是相似性。Embedding 本质上是一个双编码器,两个文本在模型内部没有任何信息交互。只在最后计算两个向量的余弦相似度时才进行唯一一次交互。所以 Embedding 检索只能把最相似的文本片段给你,没有能力来判断候选文本和 query 之间的相关性。但是相似又不等于相关。
如下图所示,从某种程度上,Embedding 其实就是在算两个文本块中相似字符的个数占比,它分不清 query 中的重点是大连医科大学,在它看来每个字符的重要性都是一样的。感兴趣的话可以计算一下下图中红字部分的占比,和最后余弦相似度的得分基本是吻合的。
Rerank 本质是一个 Cross-Encoder 的模型。Cross-Encoder 能让两个文本片段一开始就在 BERT 模型各层中通过 self-attention 进行交互。它能够用 self-attention 判断出来这 query 中的重点在于大连医科大学,而不是怎么样?。所以,如下图所示,大连医科大学怎么样?这个 query 和大连医科大学创建于 1947 年…更相关。
加上两阶段检索后,重新跑一下实验:
在数据不变的情况,两阶段检索问答准确率从 52.8%提升到 65.9%,这个结果再次证明了一阶段检索中存在数据互相干扰的情况。两阶段检索可以最大化的挖掘出数据的潜力,我们继续加数据,效果能够稳定提升。如下图所示,两阶段检索最大的意义不是在某一个实验上面提升了 10 个点。它最大的意义在于让“数据越多,效果越好”变成了现实。 在实际使用中,因为 rerank 比 embedding 慢得多,所以一般用两阶段检索。速度慢不是 cross-encoder 的模型比 bi-encoder 的模型速度慢。关键在于,bi-encoder 可以离线计算海量文本块的向量化表示,把它们暂存在向量数据库中,在问答检索的时候只需要计算一个 query 的向量化表示就可以了。拿着 query 的向量表示去库里找最相似的文本即可。但是 cross-encoder 需要实时计算两个文本块的相关度,如果候选文本有几万条,每一条都需要和 query 一起送进 BERT 模型中算一遍,需要实时算几万次。这个成本是非常巨大的。所以,我们可以把检索过程分为两个阶段:召回(粗排)和重排:
第一个阶段的目标是尽可能多的召回相似的文本片段,这个阶段的文本得分排序不是特别靠谱,所以候选的 topK 可以设置大一些,比如 topK=100;
第二个阶段的目标是对 100 个粗排的候选文本片段进行重新排序,用 cross-encoder 计算 100 个候选文本和 query 的相关度得分;两阶段检索结合可以兼顾效果和效率。
LLM 模型微调
我们的开源项目 QAnything 引入了一款 7B 参数规模的大型语言模型 Qwen-7B-QAnything,该模型是在 Qwen-7B 基础上,通过使用我们团队精心构建的中英文高质量指令数据进行微调得到的。随着开源大型语言模型(LLM)基座模型的能力不断增强,我们通过在这些优秀的基座模型上进行后续训练,包括继续预训练、指令微调(SFT)和偏好对齐等工作,以更有效地满足 RAG 应用对大模型的特定需求,从而实现高性价比的模型优化。
为什么要微调?
RAG 技术结合了知识检索与生成模型,通过从外部知识源检索信息,并将这些信息与用户问题整合成完整的 Prompt 输入到大模型中,以便根据这些参考信息回答问题。然而,当面对含有专业术语或通俗缩写的开放性问题时,直接使用开源 Chat 模型可能会导致模型回答不准确。 此外,为了最大化利用大模型的上下文窗口,RAG 应用倾向于保留尽可能多的检索信息,这可能会使得模型的注意力分散,降低其遵循指令的能力,进而引发回答中的重复内容、关键信息丢失等问题。为了提高大模型在参考信息不足时的诚实度,加入与用户问题关联度低的负样本进行微调训练变得必要。
在选择基座模型时,我们寻找能够支持中英文、至少具备 4K 上下文窗口的模型,且能在单块 GPU 上部署,优先考虑 7B 以下参数规模的模型以便于未来在消费级硬件上部署。Qwen-7B,一个阿里云研发的 70 亿参数的通用大模型,以其在多个基准测试中的卓越表现成为我们的选择。该模型通过在超过 2.4 万亿 tokens 的数据上预训练,包含了丰富的中英文、多语言、编程、数学等领域数据,确保了广泛的覆盖面。考虑到 7B 参数规模的限制,我们在指令微调时采用了结构化指令模板,以增强模型在实际应用中的指令遵循能力。
QAnything 的 prompt
如何微调?
指令微调数据构造
我们为 Qwen-7B-QAnything 模型构造了丰富的指令微调数据集,涵盖了多种类型的数据,包括基于参考信息的结构化问答数据(单文档/多文档的事实问答、多文档的归纳总结/推理类问答、信息抽取)、多轮对话查询重写、段落摘要、开放域问答、中英文翻译以及跨学科问答等。
2.指令微调模型训练
尽管与大模型的预训练相比,指令微调成本较低,但在微调数据不完整或比例不平衡的初期探索阶段,采用全参数微调的代价依然较高。为了尽可能降低实验成本并快速验证微调效果,我们首先采用 LoRA 方法进行微调探索,待实验条件稳定后,再转向全参数微调。我们的 LoRA 微调配置如下:使用 8 张 A40 显卡的单机环境,初始学习率设为 3e-5,每张卡的批量大小为 2,采用 16 步的梯度累积,同时利用 bfloat16 精度训练以避免溢出并增强稳定性。此外,我们采用 QLoRA + DeepSpeed Zero2 + FlashAttention 配置以节约训练所需的显存。QLoRA 通过 4 比特量化技术压缩预训练语言模型,使用 NormalFloat4 数据类型存储基模型权重,冻结基模型参数,并以低秩适配器(LoRA 参数)形式添加少量可训练参数。在微调阶段,QLoRA 将权重从 NormalFloat4 数据类型反量化为 bfloat16 进行前向和后向传播,仅更新 bfloat16 格式的 LoRA 参数权重梯度。与 LoRA 原论文不同,针对指令微调数据规模达到百万级别的情况,我们在所有线性层添加低秩适配器,并发现增加 lora_rank 和 lora_alpha 参数能显著提升微调效果。因此,我们为 Qwen-7B 模型微调采取了特定的 LoRA 参数配置,以实现最佳效果。
3. 指令微调模型问答效果评估
我们参考了这篇文章:Benchmarking Large Language Models in Retrieval-Augmented Generation,使用开源 Benchmark 的事实型文档问答测试集,对微调过后的 LLM 做质量评估。中、英文测试集分别包含 300 条,Context Noise Ratio (0~0.8)表示 LLM 输入 Context 中不相关噪声片段的比例。回答准确率指标结果说明:Qwen-7B-Chat (QwenLM 2023)表示论文中的结果,Qwen-7B-Chat表示使用开源 Chat 模型和结构化指令模版的结果,Qwen-7B-QAnything 表示 QAnything 开源项目微调模型的结果。模型评估时使用了 top_p 采样。结果表明 Qwen-7B-QAnything 对检索外部知识源包含不相关信息的鲁棒性更好。
公开数据集的 benchmark 的评测结果
此外,团队内部针对业务场景构造 700 条问答对作为评测集,覆盖多种文档类型和问题,其中相关参考信息由 BCE Embedding 和 Rerank 模型检索重排序得到,参考答案由 GPT4 生成,结合人工修正得到。结合 Ragas 评测框架实现对 LLM 的自动化评测。评测指标采用 [answer_correctness],通过计算 LLM 回答内容 answer 和参考答案的 factual correctness 和 semantic similarity 加权和得到,其中 factual correctness(权重系数 0.75)利用 GPT4 根据 answer 和参考答案生成 TP/FN/FP 表述计算得分,semantic similarity(权重系数 0.25)利用 BCE Embedding 计算 answer 和参考答案的语义相似度。以下是评测结果及部分示例。
本地部署
QAnything 开源项目为本地部署的大型语言模型(LLM)提供了三种推理框架后端选项:FasterTransformer、vLLM 和 Huggingface Transformers。这些选项满足了不同用户对于部署 LLM 的需求,实现了在高性能与通用性之间的平衡。
FasterTransformer (https://github.com/NVIDIA/FasterTransformer) 是由 NVIDIA 开源的一个高性能 LLM 推理框架,专为 NVIDIA GPU 优化。它的优点在于支持大型模型的 INT8-Weight-Only 推理,能在保持模型精度的同时减少推理延时和 GPU 显存使用,提高了在相同 GPU 配置下的多并发吞吐性能。FasterTransformer 的模型权重转换与具体 GPU 型号无关,提供了一定的部署灵活性,但需要 GPU 具备一定的计算能力(FP16 推理支持计算能力 7.0 及以上,INT8-Weight-Only 支持 7.5 及以上)。此外,FasterTransformer 作为 Triton Inference Server 的后端 (https://github.com/triton-inference-server/fastertransformer_backend) 实施 LLM 推理,支持 Linux/Windows 11 WSL2 部署。NVIDIA 还基于 FasterTransformer 和 TensorRT 开发了新的推理框架 TensorRT-LLM (https://github.com/NVIDIA/TensorRT-LLM),进一步提高了推理性能,但这也意味着与 NVIDIA GPU 的绑定更紧密,牺牲了一定的灵活性和通用性。
vLLM (https://github.com/vllm-project/vllm) 是由 UC Berkeley LMSYS 团队开发的另一款高性能 LLM 推理框架,其利用 PagedAttention 技术优化 KV Cache 管理,并结合并行采样和连续批处理请求调度管理,支持 NVIDIA 和 AMD GPU,提高了系统吞吐性能。通过 Huggingface Transformers 训练的模型可以轻松部署为 Python 服务,展现出良好的系统灵活性。vLLM 通过 AWQ 和 GPTQ 等算法支持 INT4-Weight-Only 推理,节省显存同时减少推理延时,但可能会轻微影响模型精度和生成质量。QAnything 利用 FastChat (https://github.com/lm-sys/FastChat)提供的接口使用 vLLM 后端,提供了兼容 OpenAI API 的调用接口,默认采用 bfloat16 推理,对 GPU 和算力有一定要求。
Huggingface Transformers (https://github.com/huggingface/transformers)是由 Huggingface 团队开发的一个通用性强、灵活性高的 Transformer 模型库,与 PyTorch 等深度学习框架配合,支持模型训练和 Python 服务部署。虽然在多数情况下,其推理性能可能不及 FasterTransformer 和 vLLM,但它兼容不同算力等级的 GPU。QAnything 通过复用 FastChat(https://github.com/lm-sys/FastChat)提供的接口使用 Huggingface Transformers 后端,实现了兼容 OpenAI API 的调用,采用 load_in_8bit 配置加载模型以节省显存,同时使用 bfloat16 进行推理。
关于开源
自从「QAnything」项目开放源代码以来,受到了开发社区的热烈欢迎和广泛认可。截至 2024 年 2 月 29 日,项目在 GitHub 上已经积累近 5000 个星标,这反映出了其流行度和用户对其价值的高度评价。
欢迎点击下面的链接下载试用:
QAnything github: https://github.com/netease-youdao/QAnythingQAnything gitee: https://gitee.com/netease-youdao/QAnything欢迎大家在 GitHub 上为「QAnything」加星助力,方便收到新版本更新的通知!
评论