搭建RAG系统
Posted by 付辉 on Monday, December 30, 2024 共2290字在「大模型辅助编程应用」中讲到RAG系统,我也一直想探究RAG的构建,如何在本地可以构建一把?可能和学习一门编程语言类似,先从打印输出 hello world 开始。但是,我没有找到一个不错的参考说明。如果不自己构建,当然可以直接使用其它构建好的RAG平台,但这样其实并不方便,最方便的还是在自己本地有一个RAG系统。
RAG系统最常见的方法通过独立训练的方式构建
从 Hugging Face 网站下载预训练的向量模型和生成模型,借助一些开源工具包便可以快速搭建一个RAG系统。
独立训练
下面给出基本流程示例,局部看懂了,全局就慢慢的懂了。
- 文章切片。按照固定的窗口大小将文章切成若干片段
- 通过OpenAI的向量接口,将每个段落转换为向量【召回模型】
- 将用户提问的问题转换为向量
- 比较问题的向量与所有段落的向量,得到相似程度并排序
- 选取与提问语义最接近的一个或几个段落作为上下文,通过OpenAI的对话接口得到最终答案【生成模块】
在这个过程中,召回模块和生成模块是解耦的,可以随意替换为其它模型或API。也就是说,这两个模块是单独训练完成的,二者之间互不关联。在独立训练类型中,召回模块的向量模型可以完全复用那些经典的模型,或者使用对比损失对自己的数据进行微调,以获得更适合业务的向量模型。
独立训练存在的问题:
- 语言模型的训练并未考虑利用召回数据,这可能导致推理过程中的数据分布存在偏差,对于参数量小的模型更容易产生幻觉
- 向量模型并没有针对语言模型所需应对的场景和领域进行优化,这可能对模型的跨域能力提出较高的要求
安装依赖 chroma
向量数据库选用 chroma 开源数据库,图示来源于官方文档,可以通过 pip 进行安装,首先需要安装 python,一般来说,安装 python 的时候会默认安装好 pip。
pip install chromadb
安装的核心在于python环境,尝试使用 pip、pip3安装都失败了,长叹一口气放松一下。说不准可能和之前安装的 miniconda 有关。这次我决定安装 Miniforge 作为包管理工具,Miniforge 开源免费。
安装官方提供的文档逐步进行安装,保存最终的提示输出。命令行还建议我修改 PYTHONPATH 环境变量为当前安装的地址,下面截取其中的部分输出展示:
To activate this environment, use:
micromamba activate /Users/fuhui/miniforge3
Or to execute a single command in this environment, use:
micromamba run -p /Users/fuhui/miniforge3 mycommand
If you'd prefer that conda's base environment not be activated on startup,
run the following command when conda is activated:
conda config --set auto_activate_base false
You can undo this by running `conda init --reverse $SHELL`? [yes|no]
安装完成后就可以开始功能验证,文档「Getting Started」的部分详细描述了验证的流程,这么一顿操作下来,也就算是和 Chroma 有了点头的交情,欢迎入门啦。关于检索数据集中的文本内容,我是有诉求的,我预期检索的是 json 格式的文本,可以理解为服务器上打印的日志内容。
不过,这个简单的例子还是有需要思考的地方:从文本到向量生成采用了什么样的模型?如何指定向量模型、采用这个模型是否符合你的业务预期、有没有更好的向量模型呢?不过,路要一步一步走,不停步就好。
下面是我在 Mac 本地执行的结果,和官方文档的返回示例存在出入。这个示例能否正常运行,取决于你安装的 python 依赖版本。我参考了stackoverflow中的问题,将依赖 onnxruntime 的版本指定为 16.3,并按照编译要求做了调整,总算是让 demo 跑起来了。
pip 对包管理的语法看起来非常便捷,python 语法虽然简单,但工程环境中我也一直没有使用过,借助大模型这次机会,我感觉自己可能会上线一个 python 服务。当然,这些有的没的,不看实力看运气。
pip install numpy<2
关于 chroma 的向量模型
chroma 是向量数据库,demo 中输入的是纯文本,从纯文本到向量的转换需要使用到向量模型,当然,它一定是内置了默认的处理模型,all-MiniLM-L6-v2 Sentence Transformers。在运行 demo 的过程中,就出现过下面这样的报错提示,认真阅读之后觉得不知所云。
return self._sess.run(output_names, input_feed, run_options)
onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Non-zero status code returned while running CoreML_12715953440113264640_1 node. Name:'CoreMLExecutionProvider_CoreML_12715953440113264640_1_1' Status Message: Error executing model: Unable to compute the prediction using a neural network model. It can be an invalid input data or broken/unsupported model (error code: -1).
看到信息中的关键字 「broken/unsupported model」,我差点就以为是 embeding 模型没有被正确安装,好在文档中给出了求证的示例,下面的代码可以正常输出,说明,内置的默认 embedding 模型是正常的。
from chromadb.utils import embedding_functions
default_ef = embedding_functions.DefaultEmbeddingFunction()
val = default_ef(["foo"])
print(val)
结合前面提到的 stackoverflow 的问题,锁定「onnxruntime.capi.onnxruntime_pybind11_state」关键字,尤其是 onnxruntime
和 pybind11
这两个依赖包,反正就是这么一折腾,程序就开心的 run 起来了。后来才后知后觉,和 python 的版本其实有很大的关系。
回归向量模型的话题,chroma 集成对接了很多三方的矢量模型,可以通过 API 的方式进行调用,具体细节可以查看 Embedding Functions。
我需要这样的能力,但我的诉求其实是调用我自己本地安装的矢量模型。我希望可以找到满足我需求的,更好的矢量化模型,最好可以通过数据对比得出一个最优解。
向量模型浅谈
向量模型有很多使用的场景,有基于编码器训练的,也有基于解码器训练的,使用的场景也各不相同。这里面其实有很多细节点,可以通过查看 chroma 使用示例来体会,尝试从多个角度去看。数据写入主要包括两部分:
- 数据文本通过向量化模型转换为向量
- 向量相似度的检索算法
文本是如何转换为向量的呢?一句话可以由很多词语组成,每个词语又可以用一个向量表示,那最终的句子如何用向量表示呢?
通过对字向量进行压缩(通常的做法是取BERT的CLS位置的向量或者堆所有字向量取平均)来作为文本段的向量表示。但是,如果文本段过长,压缩过长中必然会引入较大的语义损失。
调用模型生成向量的过程中,向量模型存在token数量限制,如果是超过token数量限制的长文本就会被截断,为了避免丢失文本信息,长文本就需要被特殊处理。但特殊处理也可能会损失语义信息。
这本身要突出了一种冲突,向量化模型需要短文本而LLM需要长文本,所以,就出现了很多手段来填补这个冲突。