RRF算法原理与RAG中的应用
倒数排名融合算法(Reciprocal Rank Fusion,简称RRF)是一种在元搜索或混合搜索中常用的相关性评分算法。元搜索或混合搜索是指从多个搜索引擎或搜索源获取结果,并将这些结果融合到一个结果集中的过程。在RAG中,这种算法能够提高你的文档召回率,准率的文档召回才能使得RAG系统出色的完成了第一步。
RRF算法原理
RRF的基本思想是,每个搜索源返回的结果都有其自身的排名。这些排名可能基于各种因素,如 relevancy score、page rank、click-through rate 等。RRF算法将这些排名转化为倒数,然后将这些倒数相加,从而得到一个融合的排名分数。
RRF的具体公式如下 (i一般作为经验值取60,根据实际情况调整):
$$ RRF(k) = \frac{1}{k + i} $$
其中 k 表示一个搜索结果在其原始搜索源中的排名。这个公式的意义在于,排名越高(即 k 越小),其倒数越大,因此其在融合结果中的排名也应该越高。
RRF的优点在于其简单易用,且不需要知道原始搜索源的排名算法的具体细节。只需要知道每个搜索结果在其原始搜索源中的排名,就可以计算出其在融合结果中的排名。
然而,RRF也有其局限性。它假设所有的搜索源都同样重要,但实际上,不同的搜索源可能有不同的权重。此外,它也没有考虑到搜索结果的相关性评分,只考虑了排名。因此,如果两个搜索结果的排名相同,但一个的相关性评分远高于另一个,RRF无法区分这两个结果。
现在以大白话来解释RRF算法,保证你一看就懂:
假设有两个搜索引擎A和B,用来搜索用户提出的关于水果的问题,它们分别返回了以下两个搜索结果列表:
搜索引擎A排名列表:[apple, banana, cherry, date]
搜索引擎B排名列表:[banana, cherry, apple, date]
现在我们想要使用倒数排名融合(RRF)来融合这两个排名列表。首先,我们计算每个项目在各个排名列表中的倒数排名:
对于搜索引擎A:
- apple在A中的倒数排名为1
- banana在A中的倒数排名为2
- cherry在A中的倒数排名为3
- date在A中的倒数排名为4
对于搜索引擎B:
- banana在B中的倒数排名为1
- cherry在B中的倒数排名为2
- apple在B中的倒数排名为3
- date在B中的倒数排名为4
然后,我们将这些倒数排名进行加权平均,得到新的融合排名列表:
- apple: (1/1 + 1/3) / 2 = 0.67
- banana: (1/2 + 1/1) / 2 = 0.75
- cherry: (1/3 + 1/2) / 2 = 0.42
- date: (1/4 + 1/4) / 2 = 0.25
最终的融合排名列表为:[banana, apple, cherry, date],这就是通过倒数排名融合得到的综合排名结果。
RRF算法实战
Elasticsearch在8.12版本中支持了Vector Search。如果你的Embedding后的向量是浮点类型的向量,要么采用类似Milvus的向量数据库进行检索,现在也可以采用Elasticsearch进行检索了;如果你的Embedding后是二进制类型目前Elasticsearch也是支持的。在8.12版本中支持了HNSW算法,自动将 float32 值量化为 int8 字节值。虽然这会使磁盘使用量增加,但是HNSW 搜索所需的内存减少了 75%,大幅减少密集向量搜索所需的资源开销,在这一点上Milvus和Elasticsearch同时支持。
关于Elasticsearch 8.12的环境参考:
-
ElasticSearch 8.12.2 elasticsearch-8.12.2-linux-x86_64.tar.gz
-
Kibana 8.12.2 kibana-8.12.2-linux-x86_64.tar.gz
-
es-analysis 分词器 elasticsearch-analysis-ik-8.12.2.zip
Elasticsearch 的 kNN 搜索功能可以找到与查询向量最相似的 k 个向量。这种搜索技术在自然语言处理、产品推荐、图像或视频的相似性搜索等领域有广泛应用。
要使用 kNN 搜索,首先需要将数据转换为有意义的向量值,并将它们作为 dense_vector
字段值添加到文档中。查询也以同样维度的向量形式表示。设计时需确保文档向量与查询向量越接近,其匹配度就越高。
Elasticsearch 支持两种 kNN 方法:一是使用 knn 搜索选项或 knn 查询进行近似 kNN 搜索;二是通过带有 vector 函数的 script_score 查询进行精确但暴力计算的 kNN 搜索。通常情况下,建议使用近似方法,因为它提供了较低延迟和可接受准确性之间平衡,尽管索引速度会慢些。而精确方法虽能保证结果准确无误,但对于大型数据集来说并不易扩展。
运行一个近似 kNN 搜索时需要特别注意资源配置,并且所有矢量数据必须适合节点页面缓存以保证效率。此外,在映射中明确定义至少一个启用索引功能的 dense_vector
字段,并设置相应参数如 similarity 值(默认为余弦相似性)。还可以调整 num_candidates 参数来权衡搜索速度和结果准确性:增加该值可获得更精准结果但降低搜索速度;反之则可能牺牲一定准确性以换取更快响应时间。
现在以QA问答式数据为例,来演示如何使用RRF算法:
插入使用BM25算法的文本匹配表数据:
1from datetime import datetime
2from elasticsearch import Elasticsearch
3import json
4
5es = Elasticsearch(
6 ['http://demo.com:9200'],
7 basic_auth=('username', 'password'),
8)
9
10with open('pc_qa_library.json', 'r', encoding='utf-8') as f:
11 data = json.load(f)
12 for item in data:
13 res = es.index(index="qa-pc-doc", id=item['id'], body={
14 'qa': item['qa'],
15 'timestamp': datetime.now()
16 })
测试一下检索:
1res = es.search(index="qa-pc-doc", body={
2 "size": 5, # 限定最终的Top-k的数量
3 "query": {
4 "match": {
5 'qa': 'Linux支持吗?'
6 }
7 }
8})
9
10# print(res)
11print("总共%d条结果:" % res['hits']['total']['value'])
12for hit in res['hits']['hits']:
13 print(hit['_score'], "---------- %(qa)s" % hit["_source"])
OK,插入和查询数据都验证没问题!
插入使用向量检索的数据:
1from elasticsearch import Elasticsearch
2
3es = Elasticsearch(
4 ['http://demo.com:9200'],
5 basic_auth=('elastic', 'password'),
6)
7
8# 创建索引
9create_index_body = {
10 "mappings": {
11 "properties": {
12 "embedding": {
13 "type": "dense_vector",
14 "dims": 1536, # 这个维度目前是OpenAI text-embedding-3-small 的维度
15 # "similarity": "l2_norm" # sqrt((1 / _score) - 1)
16 "similarity": "cosine" # (2 * _score) - 1
17 },
18 "context": {
19 "type": "keyword"
20 }
21 }
22 }
23}
24
25es.indices.create(index="qa-knn", body=create_index_body)
26
27
28openai.api_key = 'sk-xxx'
29
30# 读取JSON文件
31with open('pc_qa_library.json', 'r', encoding='utf-8') as f:
32 data = json.load(f)
33 count = 0
34 for item in data:
35 resp = openai.embeddings.create(input=[item['qa']], model="text-embedding-3-small")
36 res = es.index(index="qa-pc-knn", id=item['id'], body={
37 'embedding': resp.data[0].embedding,
38 'qa': item['qa'],
39 'timestamp': datetime.now()
40 })
41 count += 1
42 print("Inserted %d documents into Elasticsearch." % count)
向量检索的方式查询试试:
1question = 'Linux支持吗?'
2q_vector = openai.embeddings.create(input=[question], model="text-embedding-3-small").data[0].embedding
3
4print(q_vector)
5
6search_body = {
7 "knn": {
8 "field": "embedding",
9 "query_vector": q_vector,
10 "k": 5, # 最终的Top-k的数量
11 "num_candidates": 350
12 },
13 "fields": ["qa"]
14}
15
16res = es.search(index="qa-pc-knn", body=search_body)
17
18print("总共%d条结果:" % res['hits']['total']['value'])
19for hit in res['hits']['hits']:
20 print(hit['_score'], "---------- %(qa)s" % hit["_source"])
现在将两种搜索方式的结果结合起来,然后采用RRF算法的到最终的结果:
1import openai
2from elasticsearch import Elasticsearch
3
4
5es = Elasticsearch(
6 ['http://nas.zouchanglin.cn:9200'],
7 basic_auth=('elastic', 'lhl123456an'),
8)
9
10openai.api_key = 'sk-xxx'
11
12question = '你这里的电脑Linux能跑吗?'
13top_k = 5
14q_vector = openai.embeddings.create(input=[question], model="text-embedding-3-small").data[0].embedding
15
16search_body = {
17 "size": top_k, # 限定最终的Top-k的数量
18 "knn": {
19 "field": "embedding",
20 "query_vector": q_vector,
21 "k": top_k, # 最终的Top-k的数量
22 "num_candidates": 512
23 },
24 "fields": ["context"]
25}
26
27res = es.search(index="qa-pc-knn", body=search_body)
28group_knn = []
29print("总共%d条结果:" % res['hits']['total']['value'])
30for hit in res['hits']['hits']:
31 # print(hit['_id'], '---------', hit['_score'], "---------- %(context)s" % hit["_source"])
32 print(hit['_id'], '---------', hit['_score'])
33 group_knn.append(hit['_id'])
34
35
36res = es.search(index="qa-pc-doc", body={
37 "size": top_k, # 限定最终的Top-k的数量
38 "query": {
39 "match": {
40 'qa': question
41 }
42 }
43})
44
45group_bm25 = []
46print("总共%d条结果:" % res['hits']['total']['value'])
47for hit in res['hits']['hits']:
48 # print(hit['_id'], '---------', hit['_score'], "---------- %(question)s: %(answer)s" % hit["_source"])
49 print(hit['_id'], '---------', hit['_score'])
50 group_bm25.append(hit['_id'])
51
52
53# 倒数排序融合(Reciprocal Rank Fusion,RRF)
54# 1/(k+rank) k取60
55group_knn_rank = []
56for i in range(1, len(group_knn)+1):
57 group_knn_rank.append({
58 group_knn[i-1]: 1/(60+i)
59 })
60print(group_knn_rank)
61
62group_bm25_rank = []
63for i in range(1, len(group_bm25)+1):
64 group_bm25_rank.append({
65 group_bm25[i-1]: 1/(60+i)
66 })
67print(group_bm25_rank)
68
69# fusion
70group_fusion = {}
71for i in range(len(group_knn_rank)):
72 for k, v in group_knn_rank[i].items():
73 if k in group_fusion:
74 group_fusion[k] += v
75 else:
76 group_fusion[k] = v
77for i in range(len(group_bm25_rank)):
78 for k, v in group_bm25_rank[i].items():
79 if k in group_fusion:
80 group_fusion[k] += v
81 else:
82 group_fusion[k] = v
83# rank
84group_fusion = sorted(group_fusion.items(), key=lambda x: x[1], reverse=True)
85print('---------------最终结果-------------------')
86for k, v in group_fusion:
87 # print(k, v)
88 res = es.get(index="qa-pc-doc", id=k)
89 content: str = res['_source']['qa']
90 content = content.replace('\n', '')
91 print(f'id={k}, score={v}, content={content}')
92 print('---------------------------------')
可以看到排序结果还是非常令人满意的:
相似问题优化
RAG中常用的一个的优化方式:有时候存在用户提问是一个非常模糊的问题,此时通常情况下我们会生成3-5个类似问题,同时进行检索:
1client = OpenAI(
2 api_key=os.environ.get("OPENAI_API_KEY")
3)
4
5completion = client.chat.completions.create(
6 model="gpt-4-0125-preview",
7 messages=[
8 {
9 "role": "system",
10 "content": f'''Generate 3 similar questions based on existing questions to ensure \
11 that the semantics remain unchanged and must be in Chinese.
12 ## Problem
13 {question}
14 ## Return JSON Format
15 ["", "", ""]
16 '''
17 }
18 ],
19 temperature=0.2,
20)
21
22questions = json.loads(completion.choices[0].message.content.strip())
23print(questions)
最后贴出50条QA Demo数据 pc_qa_library.json:
1[
2 {
3 "id": 0,
4 "qa": "问:我应该选择固态硬盘还是机械硬盘? 答:如果您追求更快的速度和更好的性能,建议选择固态硬盘。"
5 },
6 {
7 "id": 1,
8 "qa": "问:购买电脑时,哪些因素需要考虑? 答:您需要考虑处理器性能、内存容量、硬盘类型、显卡性能等因素。"
9 },
10 {
11 "id": 2,
12 "qa": "问:我可以在购买电脑时升级内存吗? 答:通常可以,在购买时选择较低配置,后期再单独购买内存进行升级。"
13 },
14 {
15 "id": 3,
16 "qa": "问:购买电脑时有哪些推荐的品牌? 答:常见的电脑品牌有惠普、戴尔、华硕、联想等,选择时可以根据个人喜好和需求来决定。"
17 },
18 {
19 "id": 4,
20 "qa": "问:购买电脑时有什么保修服务? 答:大多数电脑品牌会提供一定时期的免费保修服务,可以根据实际情况选择延长保修期。"
21 },
22 {
23 "id": 5,
24 "qa": "问:如何选择合适的显示器? 答:选择显示器时需要考虑分辨率、屏幕大小、刷新率等因素,根据自己的需求来选择。"
25 },
26 {
27 "id": 6,
28 "qa": "问:购买电脑时可以选择哪些支付方式? 答:通常支持支付宝、微信支付、银行转账等多种支付方式,具体以商家支持的方式为准。"
29 },
30 {
31 "id": 7,
32 "qa": "问:购买电脑后配送时间需要多久? 答:配送时间会根据您所在地区和配送方式而有所不同,一般在1-7个工作日内送达。"
33 },
34 {
35 "id": 8,
36 "qa": "问:我可以在电脑上安装Linux系统吗? 答:可以,在购买时选择兼容Linux系统的硬件配置,安装Linux系统通常不会有太大问题。"
37 },
38 {
39 "id": 9,
40 "qa": "问:购买电脑需要注意什么? 答:购买电脑时需要注意配置是否满足自己的需求、售后服务是否完善、价格是否合理等方面。"
41 },
42 {
43 "id": 10,
44 "qa": "问:如何选择合适的处理器? 答:选择处理器时可以考虑性能、核心数量、功耗等因素,根据自己的需求来选择合适的处理器。"
45 },
46 {
47 "id": 11,
48 "qa": "问:购买电脑时可以选择哪些配送方式? 答:通常支持快递配送、自提等多种配送方式,具体以商家提供的配送方式为准。"
49 },
50 {
51 "id": 12,
52 "qa": "问:购买电脑时有哪些性能指标需要关注? 答:您可以关注处理器型号、内存容量、显卡性能、硬盘类型等性能指标。"
53 },
54 {
55 "id": 13,
56 "qa": "问:购买电脑时可以选择哪些保修方案? 答:通常有标准保修、延长保修、意外保修等多种保修方案可供选择。"
57 },
58 {
59 "id": 14,
60 "qa": "问:如何选择合适的显卡? 答:选择显卡时可以考虑显存大小、核心频率、功耗等因素,根据自己的需求来选择合适的显卡。"
61 },
62 {
63 "id": 15,
64 "qa": "问:购买电脑时有哪些常见的配件? 答:常见的配件有键盘、鼠标、显示器、音箱等,可以根据实际需求选择购买。"
65 },
66 {
67 "id": 16,
68 "qa": "问:购买电脑时可以选择哪些操作系统? 答:常见的操作系统有Windows、Mac OS、Linux等,可以根据个人喜好选择。"
69 },
70 {
71 "id": 17,
72 "qa": "问:如何选择合适的内存? 答:选择内存时可以考虑容量、频率、延迟等因素,根据主板支持的规格来选择合适的内存。"
73 },
74 {
75 "id": 18,
76 "qa": "问:购买电脑时有哪些网络连接方式? 答:通常支持有线连接、无线连接等多种网络连接方式,可以根据需求选择。"
77 },
78 {
79 "id": 19,
80 "qa": "问:购买电脑时如何选择合适的机箱? 答:选择机箱时需要考虑散热性能、扩展性、外观设计等因素,根据自己的需求来选择。"
81 },
82 {
83 "id": 20,
84 "qa": "问:购买电脑时有哪些常见的接口? 答:常见的接口有USB接口、HDMI接口、网口、音频接口等,可以根据外设需求选择。"
85 },
86 {
87 "id": 21,
88 "qa": "问:购买电脑时可以选择哪些储存设备? 答:常见的储存设备有固态硬盘、机械硬盘、光驱等,可以根据需求选择。"
89 },
90 {
91 "id": 22,
92 "qa": "问:如何选择合适的键盘? 答:选择键盘时可以考虑键盘类型、按键手感、背光等因素,根据个人习惯来选择合适的键盘。"
93 },
94 {
95 "id": 23,
96 "qa": "问:购买电脑时可以选择哪些音频设备? 答:常见的音频设备有耳机、音箱、麦克风等,可以根据需求选择适合的音频设备。"
97 },
98 {
99 "id": 24,
100 "qa": "问:购买电脑时如何选择合适的散热系统? 答:选择散热系统时可以考虑散热效率、噪音、尺寸等因素,根据电脑配置来选择合适的散热系统。"
101 },
102 {
103 "id": 25,
104 "qa": "问:购买电脑时可以选择哪些外设? 答:常见的外设有打印机、扫描仪、摄像头等,可以根据实际需求选择适合的外设。"
105 },
106 {
107 "id": 26,
108 "qa": "问:如何选择合适的电源? 答:选择电源时可以考虑功率、效率、稳定性等因素,根据电脑配置来选择合适的电源。"
109 },
110 {
111 "id": 27,
112 "qa": "问:购买电脑时可以选择哪些电池? 答:如果是笔记本电脑,可以选择标准电池、大容量电池等不同容量的电池。"
113 },
114 {
115 "id": 28,
116 "qa": "问:购买电脑时如何选择合适的摄像头? 答:选择摄像头时可以考虑像素、拍摄效果、适配性等因素,根据需求选择合适的摄像头。"
117 },
118 {
119 "id": 29,
120 "qa": "问:如何选择合适的无线网络设备? 答:选择无线网络设备时可以考虑信号覆盖范围、传输速度、稳定性等因素,根据需求选择合适的设备。"
121 },
122 {
123 "id": 30,
124 "qa": "问:购买电脑时可以选择哪些耗材? 答:常见的耗材有墨盒、硒鼓、纸张等,可以根据打印需求选择适合的耗材。"
125 },
126 {
127 "id": 31,
128 "qa": "问:购买电脑时如何选择合适的投影仪? 答:选择投影仪时可以考虑分辨率、亮度、投影距离等因素,根据使用场景选择合适的投影仪。"
129 },
130 {
131 "id": 32,
132 "qa": "问:如何选择合适的键鼠套装? 答:选择键鼠套装时可以考虑连接方式、手感、耐用性等因素,根据个人习惯选择合适的键鼠套装。"
133 },
134 {
135 "id": 33,
136 "qa": "问:购买电脑时可以选择哪些办公软件? 答:常见的办公软件有Office套件、金山WPS等,可以根据需求选择适合的办公软件。"
137 },
138 {
139 "id": 34,
140 "qa": "问:购买电脑时如何选择合适的打印机? 答:选择打印机时可以考虑打印速度、打印质量、耗材成本等因素,根据需求选择合适的打印机。"
141 },
142 {
143 "id": 35,
144 "qa": "问:如何选择合适的扫描仪? 答:选择扫描仪时可以考虑扫描速度、扫描精度、连接方式等因素,根据需求选择合适的扫描仪。"
145 },
146 {
147 "id": 36,
148 "qa": "问:购买电脑时可以选择哪些办公设备? 答:常见的办公设备有复印机、传真机、装订机等,可以根据需求选择适合的办公设备。"
149 },
150 {
151 "id": 37,
152 "qa": "问:购买电脑时如何选择合适的耳机? 答:选择耳机时可以考虑音质、佩戴舒适度、阻抗等因素,根据需求选择合适的耳机。"
153 },
154 {
155 "id": 38,
156 "qa": "问:如何选择合适的音箱? 答:选择音箱时可以考虑音质、功率、连接方式等因素,根据需求选择合适的音箱。"
157 },
158 {
159 "id": 39,
160 "qa": "问:购买电脑时可以选择哪些网络设备? 答:常见的网络设备有路由器、交换机、网卡等,可以根据需求选择适合的网络设备。"
161 },
162 {
163 "id": 40,
164 "qa": "问:购买电脑时如何选择合适的显示器支架? 答:选择显示器支架时可以考虑承重能力、调节功能、稳定性等因素,根据显示器尺寸选择合适的支架。"
165 },
166 {
167 "id": 41,
168 "qa": "问:如何选择合适的鼠标垫? 答:选择鼠标垫时可以考虑材质、尺寸、防滑性等因素,根据鼠标类型选择合适的鼠标垫。"
169 },
170 {
171 "id": 42,
172 "qa": "问:购买电脑时可以选择哪些电脑桌椅? 答:常见的电脑桌椅有电脑椅、电脑桌、桌面升降架等,可以根据使用习惯选择合适的电脑桌椅。"
173 },
174 {
175 "id": 43,
176 "qa": "问:购买电脑时如何选择合适的电脑包? 答:选择电脑包时可以考虑尺寸、质地、舒适度等因素,根据电脑尺寸选择合适的电脑包。"
177 },
178 {
179 "id": 44,
180 "qa": "问:如何选择合适的电脑桌面壁纸? 答:选择桌面壁纸时可以根据个人喜好选择风格、颜色等,也可以自定义壁纸。"
181 },
182 {
183 "id": 45,
184 "qa": "问:购买电脑时可以选择哪些办公软件? 答:常见的办公软件有Office套件、金山WPS等,可以根据需求选择适合的办公软件。"
185 },
186 {
187 "id": 46,
188 "qa": "问:购买电脑时如何选择合适的打印机? 答:选择打印机时可以考虑打印速度、打印质量、耗材成本等因素,根据需求选择合适的打印机。"
189 },
190 {
191 "id": 47,
192 "qa": "问:如何选择合适的扫描仪? 答:选择扫描仪时可以考虑扫描速度、扫描精度、连接方式等因素,根据需求选择合适的扫描仪。"
193 },
194 {
195 "id": 48,
196 "qa": "问:购买电脑时可以选择哪些办公设备? 答:常见的办公设备有复印机、传真机、装订机等,可以根据需求选择适合的办公设备。"
197 },
198 {
199 "id": 49,
200 "qa": "问:购买电脑时如何选择合适的耳机? 答:选择耳机时可以考虑音质、佩戴舒适度、阻抗等因素,根据需求选择合适的耳机。"
201 }
202]