背景
随着大模型的火爆,RAG 的检索方式也大火。随之出现了各种RAG 的变体。文本 + 向量的检索方式,就是其中最为重要的一种变体。文本检索主要用ES来实现。向量检索有各种向量库,如Milvus, Chroma。 ES在最新版中实现了向量检索,用一个工具就可以文本 + 向量 的检索。
名词解释:
ES (elastic search) 是一款开源的实现全文检索的工具。
混合检索:文本检索和向量检索同时使用,以提高检索的准确度。
ES的安装
ES在8.0 版本以后,实现了向量检索。 最新版是8.17,同时考虑到es的版本要与ik 分词器的版本一致,这里选择了8.16.1 的版本。
安装软件:
-
ES 8.16.1
从官网下载 https://www.elastic.co/downloads/elasticsearch docker 安装 docker run -d --name es -e "ES_JAVA_OPTS=-Xms512m -Xmx4024m" -e "discovery.type=single-node" -e "xpack.security.enabled=false" -e "xpack.security.transport.ssl.enabled=false" -e "ELASTIC_PASSWORD=xxx" -v es-data4:/home/xxx/es4/data -v es-plugins4:/home/xxx/es4/plugin --privileged -p 9200:9200 -p 9300:9300 elasticsearch:8.16.1 -e 设置参数 ES_JAVA_OPTS=-Xms512m -Xmx4024m 设置java 的内存大小 discovery.type=single-node 设置es为单节点 xpack.security.enabled=false xpack.security.transport.ssl.enabled=false 设置xpack 不使用ssl 证书连接 ELASTIC_PASSWORD=xxxx, 设置连接密码 -v es-data4:/home/xxx/es4/data 设置数据目录 es-plugins4:/home/xxx/es4/plugin 设置插件目录 docker source 现在很多不能用了,需要更新数据源。 或 docker run ... docker.elastic.co/elasticsearch/elasticsearch:8.16.1
-
IK分词器 8.16.1 用于es 实现分词检索
https://github.com/infinilabs/analysis-ik
- ES HEAD: chrome 插件版,用于查看es 中数据(谷歌商店下载)
xpack: es8.0 以后,需要证书才能连接。在配置中关闭ssl证书的使用。\config\elasticsearch.yml 修改 xpack.security.enabled=false xpack.security.transport.ssl.enabled=false
在docker 中加参数也可。
python 客户端
python 中实现es检索的api,主要有 elasticsearch,zdppy_elasticsearch 这2个包。下面的操作,主要以elasticsearch 这个包为主。
安装:
pip install elasticsearch
数据导入
-
字段索引类型的选择:
ES 中 文本检索 + 向量检索相关索引类型
### 文本类型
text(全文检索)
keyword(关键词)
### 数值类型
long(长整型)
integer(整型)
double(双精度)
float(单精度)
half_float(半精度)
### 日期类型
date(日期)
### 布尔类型
boolean
### 向量类型
dense_vector(密集向量)
sparse_vector(稀疏向量)
-
向量化:openai的text-embedding-ada-002, cohere的embed-multilingual-v3.0 ,jina的, 开源库有:m3e, bge
-
向量相似度计算:余弦相似度(cosine)、点积(dotProduct)、L1距离(曼哈顿距离)和L2距离(欧几里德距离)
"mappings": {
"properties": {
"title": {"type": "text","analyzer":"ik_smart"},
"content": {"type": "text","analyzer":"ik_smart"},
"embedding": {"type": "dense_vector", "dims": 768, "similarity": "cosine"}
}
}
索引中有3个字段,title,content 是text类型,ik_smart分词器; embedding 是dense_vector 向量类型, 向量长度768 ,计算相似度函数:cosine。 embedding 是把 title + content 字段相加,用m3e 向量化。
数据导入
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer("moka-ai/m3e-base")
def get_embedding(text):
vecs = model.encode(text).tolist()
return vecs
def insert_document(document):
embedding = get_embedding(document['summary'] + ' ' + document['title'])
es.index(index=index_name, document={
'title':document['title'],
'summary':document['summary'],
'embedding': embedding
})
documents = [
{"title": "Python 编程基础", "summary": "Python 是一种高级编程语言,广泛用于数据分析、机器学习等领域。"},
{"title": "Elasticsearch 教程", "summary": "Elasticsearch 是一个分布式搜索和分析引擎,支持全文搜索和向量搜索。"}
]
混合检索的实现
key_boostqu = [ {"match": {"title": {"query": qa,"boost":0.9}}}, {"match": {"content": {"query": qa,"boost":0.1}}} ]
query = {
"query": {
"bool": {
"should": key_boostqu
}
}
}
knn_query = {
'field': 'embedding',
'query_vector': get_embedding(qa),
'k': 10,
'num_candidates': 50,
'boost':9
}
results = es.search(index=index_name, query={
'bool': {
**search_query
}
},
knn=knn_query
)
es.search 中query,knn 实现文本检索 + 向量检索的混合。其中boost 参数表示权重。取值范围: 0 ~ 10. 在实际使用中,可以给向量检索,文本检索分配不同的权重。这参数需要调,来实现不同的检索排序。在实践中,向量检索权重 = 9, 标题检索权重=0.9, 正文检索权重=0.1。
rrf 参数:可以自动给文本检索,向量检索分配权重。不需要人工的给boost 分配权重。但这功能不在免费版中。需升级到白金版。
es.search(index = index_name,query={},knn ={},rank={
"rrf": {
"window_size": 100,
"rank_constant": 20
}
}
)