python使用elasticserch进行混合搜索构建知识库

基于python使用elasticserch

安装elasticsearch

pip install elasticsearch

连接elasticsearch

es_username = "elastic"
es_password = "123456"
es = Elasticsearch(
    ['https://0.0.0.0:9200'],
    basic_auth=(es_username, es_password),verify_certs=False
    )

创建索引

#settings: 定义了索引的设置,包括了主分片数量和副本数量。一个主分片和0副本。
#id类型为 keyword,意味着它是一个精确值字段,不会被分词。
#title 和 content: 表示标题和内容的字段,类型为 text,使用了中文分词器(ik_smart 和 ik_max_word)进行分析,并且都被配置为存储。
#title_vec 和 vectors.features: 表示向量字段的字段,类型为 dense_vector,意味着它们存储了稠密向量。这些向量字段还指定了维度(dims),相似度度量方式(similarity,这里是余弦相似度),以及是否被索引(index,设置为 True)。
#vectors.segment: 表示文档中的某一段落的字段,类型为 text,使用了中文分词器,并且被配置为存储。
index_name = "zft_lnn"
index_settings = {
    'settings': {
        'number_of_shards': 1,
        'number_of_replicas': 0
    },
    'mappings': {
        "properties": {
            "id": {
                "store": True,
                "type": "keyword"
            },  
            "title": {
                "search_analyzer": "ik_smart",
                "analyzer": "ik_max_word",
                "store": True,
                "type": "text"
            },
            "content": {
                "search_analyzer": "ik_smart",
                "analyzer": "ik_max_word",
                "store": True,
                "type": "text"
            },
            "title_vec": {
                "dims": 768,
                "similarity": "cosine",
                "index": True,
                "type": "dense_vector"
            },
            "vectors": {
                "type": "nested",
                "properties": {
                    "features": {
                        "dims": 768,
                        "similarity": "cosine",
                        "index": True,
                        "type": "dense_vector"
                    },
                    "segment": {"search_analyzer": "ik_smart",
                                "analyzer": "ik_max_word",
                                "store": True,
                                "type": "text"}
                }
            }
        }
    }
}
es.indices.create(index=index_name, body=index_settings)

写入数据

待写入数据存储在json文件中,里面是query和answer的对子。

import json

# 读取JSON文件
with open('answer.json', 'r', encoding='utf-8') as file:
    data = json.load(file)
    
count = 1
for item in data:
    title = item["query"]
    content  = item["answer"]
    index_name = "zft_bge"
    title_vec = vector_model.encode(title)
    content_vec = vector_model.encode(content)
    id = str(count)
    count += 1

    datas = {}
    datas["id"] = id
    datas["title"] = title
    datas["content"] = content
    datas["title_vec"] = title_vec
    datas["content_vec"] = content_vec

    document = {
        "id": datas["id"],
        "title": datas["title"],
        "title_vec":datas["title_vec"],
        "content":datas["content"],
        "content_vec":datas["content_vec"]
    }
    response = es.index(index=index_name,id=datas["id"], body=document)

搜索数据

关键字搜索

IK 分词器可以将中文文本切分成一个个有意义的词语,并计算这些词语的词频(term frequency,TF),用于构建倒排索引。在搜索时,Elasticsearch 会根据查询词的词频和文档中各个词语的词频来计算文档的相关性得分,从而排序返回搜索结果。

boostqu = {
          "match": {
            "title": {
              "query": qa,
              "boost": 1.5
            }
          }
        }
query = {
        "query": {
            "function_score": {
                "query": {
                    "bool": {
                        "should": boostqu
                    }
                },
                "functions": [
                    {
                        "script_score": {
                            "script": {
                                "source": "Math.sqrt(_score)/2",
                                "lang": "painless",
                            }
                        }
                    }
                ],
                "boost_mode": "replace"
            }
        },
}

results = es.search(index=index_name, body=query)
hits = results["hits"]["hits"]
for h in hits:
    print(h["_source"]["title"],h['_score'])

KNN搜索

在 Elasticsearch 中,KNN 搜索通常用于基于向量的相似性搜索,例如基于词嵌入向量或其他类型的向量来查找最相似的文档或项。

query_vector = vector_model.encode(qa)
query = {
  "query": {
    "bool": {
      "should": [
        {
          "knn": {
            "field": "title_vec",
            "query_vector": query_vector,
            "num_candidates": 10,
            "boost": 1.0
          }
        }
      ]
    }
  }
}
results = es.search(index=index_name, body=query)
hits = results["hits"]["hits"]
for h in hits:
    print(h["_source"]["title"],h['_score'])

向量搜索

query_vector = vector_model.encode(qa)
query = {
  "query": {
    # 通过 script score 来自定义查询的分值计算
    "script_score": {
      "query": {
        # 此处可以结合其他约束条件
        "match_all": {}
      },
      # 稠密向量查询的重点在此处,通过 script 的方式指定具体的计算方式
      # 此处是计算余弦相似度,也可以替换其他的相似度
      "script": {
		# 余弦相似度计算需要两个参数:本次待查询的向量 queryVector 和索引中的字段 entity_name_embedding
        "source": "cosineSimilarity(params.queryVector, 'title_vec')",
        "lang": "painless",
		# 此处将本次待查询的向量传入,注意“params”中 queryVector 和 “source”中的书写要保持一致。
        "params": {
          # 768 维的待查询向量
          "queryVector": query_vector
        }
      },
       "boost": 1.0,
    }
  },
}
results = es.search(index=index_name, body=query)
hits = results["hits"]["hits"]
for h in hits:
    print(h["_source"]["title"],h['_score'])

混合搜索

在混合搜索中,可以在不同方式的搜索加入权重,进行去权重的相加,从而得到一个更好的结果,这个权重参数更多的需要结合实际场景进行设置。

#混合搜索
def multiserch(es,vector_model,qa):
    query_vector = vector_model.encode(qa)

    key_boostqu = [{"match": {"title": {"query": qa, "boost": 1.2}}},
               {"match": {"content": {"query": qa, "boost": 0.8}}},]

    query = {
    "query": {
        "bool": {
        "should": [
            {
            "function_score": {
                "query": {
                        "bool": {
                            "should": key_boostqu
                        }
                    },
                "functions": [
                {
                    "script_score": {
                    "script": {
                        "source": "Math.sqrt(_score)/2",
                        "lang": "painless"
                    }
                    }
                }
                ],
                "boost_mode": "replace"
            }
            },
        
            {
            "script_score": {
                "query": {
                "match_all": {}
                },
                "script": {
                "source": "cosineSimilarity(params.queryVector, 'title_vec')",
                "lang": "painless",
                "params": {
                    "queryVector": query_vector
                }
                },
                "boost": 1.0
            }
            }
        ]
        }
    },
        "knn": {
        "field": "title_vec",
        "query_vector": query_vector,
        "k": 1,
        "num_candidates": 10,
        "boost": 0.5
        },
        "knn": {
        "field": "content_vec",
        "query_vector": query_vector,
        "k": 1,
        "num_candidates": 10,
        "boost": 0.5
        },
    "size": 3
    }

    results = es.search(index=index_name, body=query)
    hits = results["hits"]["hits"]
    for h in hits:
        print(h["_source"]["title"],h['_score'])
        print(h["_source"]["content"])
    content = hits[0]["_source"]["content"]
    return content
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江小皮不皮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值