基于本地知识库,定制一个私有GPT助手,不能再简单了

大家好呀,我是码财同行。今天来聊聊怎么搭建一个私有 GPT 助手。

背景知识

众所周知,目前大模型 LLM 的能力已经非常强大,chatgpt 已经可以很好的解决通用型问题,但是对于垂直专业领域的问题处理的还不够好。如果要利用 LLM 大模型根据已有的特定领域的知识,推理出该领域特定问题的答案,将是一种不错的应用方向。

例如,下面是一个开源医学领域的 GPT 助手:

法律领域的助手:

相对于通用大模型如 chatgpt,私有问答助手的专业性要更高。此外,如果是一些私有的领域,通用大模型是没有相关知识的,因此无法回答。

因此我们需要实现自己的私有 GPT 助手。

具体是怎么实现的呢?

可选方案

现在,我们假设自己已经有了所在特定领域的数据集,比如积累或者收集到的医疗数据、金融数据等。

经过调研和测试,现在已知的通常有三种方案。

基于大模型的全参数训练

基于大模型的全参数训练,是将我们自己的数据集和现有LLM大模型数据一起训练,出来一个新的模型。

现在很多大模型是不开源的,如 GPT、Bert 等,也有一些开源的大模型如 LLaMa、chatGLM 等。我们可以选择开源的大模型来一起训练。

这种方式是全量训练,需要大量的计算资源如 GPU,显存消耗很大,几十个G的显存是起码的要求。

这种全训练的方式有个问题,如果咱们自己的数据集不大,训练的效果基本是没有的。回答仍然还是原来模型的内容。

基于大模型的微调(fine-tunning)

目前流行的微调方法是 Lora,其基本原理是额外训练一个小模型(小的只有几M大小),叠加在原有大模型的基础上,类似于一个特定领域的补丁:

这种微调的方式在特定领域确实有不错的效果。下面是一个基于开源模型 chatGLM,使用《甄嬛传》里的台词脚本训练出的机器人,可以模仿甄嬛的语气来回答问题:

但是实际测试下来,还是遇到了比较致命的问题:微调出的问答助手会出现遗忘问题,原有大模型的通用能力很多会丧失,很多通用大模型原来能回答的问题,现在的效果都丢失了。

果然,通过这种修修补补的方式还是不行啊~

基于prompt上下文的方式

prompt上下文,也叫做提示词,意思是在要求大模型回答问题时给出一定的要求、语境、提示等,使之得到符合我们要求的答案。

我们可以把这种提示想象成球员身边的教练,有了教练的提示,球员一般也能发挥的更好。

例如一种角色扮演性质的prompt:

提示:我希望您充当面试官。我将扮演应聘者的角色,您将为我提问与职位相关的面试问题。请您只以面试官的身份回答,不要一次性写下所有对话。我只希望您与我进行面试对话。请像面试官一样一个一个问题地问我,然后等待我的回答。不要进行解释。我的第一句话是"你好"。

一种给定特定领域知识的prompt:

问题:高尔夫球的一部分是试图获得比其他人更高的得分。是或否?

知识:高尔夫球是一项精密的球杆运动,竞争选手(或高尔夫球手)使用许多类型的球杆,用最少的杆数将球打入球场上一系列洞中。目标是以最低的分数完成比赛,该分数是通过将每个洞上所用的杆数相加计算出来的。得分最低的选手赢得比赛。解释和答案是:

有了特定领域知识的提示,问答助手就能更好理解,其实就是一种形式的阅读理解:

prompt 提示的限制

通过上面的背景知识,我们了解到,通过将本地的知识以 prompt 的形式喂给大模型,它就能帮我们实现回答特定垂直领域问题的需求。

但是,这种方案有一个比较大的限制,那就是大模型 LLM 一般对输入有长度的限制,比如现在 chatgpt 普遍有 4096 个 token(类似一个英文单词,但还包括词片段等)的限制。如果我们的本地知识文档很大,就无法全部喂给大模型了。

其实,一般本地知识库里内容很多,我们的问题不一定要将所有知识库里的内容都发过去。

这里能想到的方案,就是通过搜索将我们问题中的关键字与本地知识库进行匹配,找到相关度比较高的部分,然后将这些长度有限、相关度高的信息作为大模型的 prompt 就可以了。

基于知识库搜索的 prompt 上下文的方案

如果用图来表示知识库搜索 + prompt 的方案,就如下图所示:

那么问题来了:如何通过问题在本地知识库中搜索出相关知识呢?

我们平时都能通过关键词来搜索文档,来找到关键词相关的信息。这个是我们能想到的一种解决方式。

但是现在我们拿到的是一个问题,如何能提取出关键词呢?

即使我们能提取出问题中的关键词,但是关键词在文档中也不一定能匹配出完全一样的信息,因为我们的问题很可能是与文档中的某些信息在语义上类似,但是字面上不一定一样。

举个例子,比如我们有一个文档,里面介绍了上海各种各样的美食。但是我们的问题是:

上海有什么好吃的?

语义上,好吃的美食 很接近,但是如果以 好吃的 作为关键词去硬性搜索,是找不到信息的。

在语义上寻找相似性,在自然语言处理 NLP 上有一种称为 embedding (中文翻译叫嵌入)的方式。在高纬度的语言层面我们不好比较相似性,那就将语句通过 embedding 转换为低维度的信息,在低维度层面进行比较。

Embedding

在机器学习中,计算机处理的基本都是数字类型。

那如果要训练文本、图像等,就需要一个转换:将高维数据(如文字、图像等)转化为低维数字的过程,这个过程就叫 embedding,翻译是嵌入

可以将嵌入视为一种尝试通过数组成的数组来表示某些东西本质的方法,其特性是 "相似的事物" 由相近的数表示:

用 OpenAI 提供的 embedding 模型表示这个过程,如下:

目前流行的词嵌入模型有 word2vec、glove、Deeplearning4j,OpenAI 也提供了自己的词嵌入模型 text-embedding-ada-002,可以直接通过 API 来调用(当然,要收费 O.o)

OpenAI 提供的调用返回的例子如下:

文本分割

有了 embedding ,我们就可以搜索语义相似的文档信息了。但是在实际搜索前,我们还需要将文档切分成一个个的段落。不论是将文本作为 prompt 提交给 LLM 大模型,还是提交给 embedding 模型计算向量,对于文本的长度都是有限制的。

我们可以通过一个叫做 langchain 的库来加载知识库文档。这个库的功能比较强大,可以加载各种不同的数据。比如:文件夹、Azure 存储、csv 文件、任意文件格式、任意的网页、PDF 文件、Youtube 视频文件等等。

langchain 完成文档的加载之后,就可以对文档进行分割了。

一般分割的标准是通过一些标点符号如句号、分号等来做分割。此外,还有基于 AST 语义分析的文本分割算法。目前常用的分割方式有:

  1. Langchain 的 text_splitter
  2. 阿里达摩院基于语义分割的 AliTextSplitter

由于很多知识库段落中间也会以句号、分号等来做断句,阿里的语义分割在这种情况下能保证段落的完整性,测试下来效果会好一些。

完整流程

现在,对于知识库的处理已经比较清楚了:

首先,是预处理。

  • 先做文本加载
  • 然后文本分割
  • 之后借助 embedding 模型计算分割后的文本段落,将计算出的向量结果存储起来

其次,开始做问答,将问题也发往 embedding 模型,计算其低维度的向量值,之后根据该问题的向量值在我们前面预处理中存储的知识库向量中搜索,找出相似度最高的几条信息。

有了从知识库搜索到的信息,我们就可以将这些信息组织成 prompt 的形式发往 LLM 大模型,后续的答案就交给大模型来帮我们找了。

大模型我们可以选择清华的 ChatGLM 模型或者 chatgpt。

借用开源项目langchain-ChatGLM的一张图来表示这整个流程,如下:

从知识库文档处理的角度来看,流程如下:

上面黄色的部分是搜索出的相关文档(绿色)的前后上下文,综合之后,作为 prompt 效果会更好。

实际测试

目前已经有一些开源项目实现了本地知识库的功能,如:

  • langchain-ChatGLM

  • 闻达 wenda 项目

  • 上面提到的医疗、法律方面的项目

我们使用自己游戏中的数据集,实际测试下来,影响最后实际效果的因素很多,包括:

  1. 知识库本身语料的格式问题。如果有大量的短文本占据一行,这其实没有什么意义,其表示的意思和信息很有限。因此在做本地知识库的时候,如果前后语句的语义是连贯的但又不在一行,我们需要人工将这些段落做一下整理,将短的文本凑到一行。尽量避免多行的短文本。

  2. 文本分割的效果。很多文本分割的处理方式比较粗暴,就是单纯的按照标点如句号、分号来分割,这其实打断了原本比较连贯的文本,效果不一定好。当然,如果多加上前后几句文本,效果可能会有改善,但是这个度不好控制。比较理想的方式是采用语义分割。

  3. Embedding 模型计算向量以及向量间的相似度,选择不同的模型,这个效果肯定会有差异。

  4. 大模型本身的能力,如现在 chatgpt-4 应该是傲视群雄,大模型基本是分为 chatgpt-4 和其他了。当然,使用 chatgpt 本身有信息安全问题以及费用问题。国内清华开源的 chatGLM-6b 是一个效果和性价比尚可的替代。

做了多轮测试之后,我们排除了闻达项目,效果比较差。langchain-ChatGLM 项目效果要好一些,但是也不够理想,原因也在于文本分割、embedding 模型以及大模型本身的能力有限。

为此,我们自己实现了一套简单的测试demo,结合了以下能力:

  1. Langchain 的文档加载
  2. AliTextSplitter 阿里语义分割算法
  3. OpenAI 的 text-embedding-ada-002 embedding 模型
  4. OpenAI 的 chatgpt-4 大模型(通过公司的封装接口,解决安全性及计费问题)

最终这种定制化的效果要更好。

看了这么多,一定很费脑力。来个笑话放松一下吧:

【笑话一则】地铁上有三只羊,第二站上来了一只狼,请问接下来一站还剩几只羊?答:还是三只,因为地铁上不让吃东西。

最后,请大家来个点赞、收藏、转发吧,您的鼓励是我持续创作的动力,蟹蟹!

相关推荐
咔叽布吉10 分钟前
【前端学习笔记】ES6 新特性
前端·笔记·学习
Adolf_199318 分钟前
Django 自定义路由转换器
后端·python·django
推开世界的门43 分钟前
web 中 canvas 污染 以及解决方案
前端
星离~1 小时前
css—轮播图实现
前端·css
ᝰꫝꪉꪯꫀ3611 小时前
JavaWeb——Mybatis
java·开发语言·后端·mybatis
机器之心1 小时前
跨模态大升级!少量数据高效微调,LLM教会CLIP玩转复杂文本
人工智能·后端
龙雨LongYu121 小时前
vue3+ts 我写了一个跟swagger.yml生成请求和响应实体(接口)
前端·vue.js·typescript
爱上语文2 小时前
Http 响应协议
网络·后端·网络协议·http
Stanford_11062 小时前
关于IDE的相关知识之一【使用技巧】
前端·ide·windows·微信小程序·微信公众平台·twitter·微信开放平台
_志哥_2 小时前
web开发环境下启动HTTPS服务访问
前端·javascript·https