使用 ElasticSearch 作为知识库,存储向量及相似性搜索

一、ElasticSearch 向量存储及相似性搜索

在当今大数据时代,快速有效地搜索和分析海量数据成为了许多企业和组织的重要需求。Elasticsearch 作为一款功能强大的分布式搜索和分析引擎,为我们提供了一种优秀的解决方案。除了传统的文本搜索,Elasticsearch 还引入了向量存储的概念,以实现更精确、更高效的相似性搜索。

Elasticsearch 中,我们可以将文档或数据转换为数值化向量的方法存入。每个文档被表示为一个向量,其中每个维度对应于文档中的一个特征或属性。这种向量化的表示使得文档之间的相似性计算变得可能。

使用场景:

  1. 相似文档搜索:通过将文档转换为向量,并使用向量相似性函数,如 dot productcosine similarity,可以快速找到与查询文档最相似的文档,从而实现精确且高效的相似文档搜索。

  2. 推荐系统:将用户和商品等表示为向量,可以根据用户的喜好和行为,推荐与其兴趣相似的商品。

  3. 图像搜索:将图像转换为向量表示,并使用相似性度量,可以在图像库中快速找到与查询图像相似的图像。

下面基于上篇文章使用到的 Chinese-medical-dialogue-data 中文医疗对话数据作为知识内容进行实验。

本篇实验使用 ES 版本为:7.14.0

二、Chinese-medical-dialogue-data 数据集

GitHub 地址如下:

https://github.com/Toyhom/Chinese-medical-dialogue-data

数据分了 6 个科目类型:

在这里插入图片描述

数据格式如下所示:

在这里插入图片描述

其中 ask 为病症的问题描述,answer 为病症的回答。

由于数据较多,本次实验仅使用 IM_内科 数据的前 5000 条数据进行测试。

三、Embedding 模型

Embedding 模型使用开源的 chinese-roberta-wwm-ext-large ,该模型输出为 1024 维。

huggingface 地址:

https://huggingface.co/hfl/chinese-roberta-wwm-ext-large

基本使用如下:

from transformers import BertTokenizer, BertModel
import torch# 模型下载的地址
model_name = 'D:\\AIGC\\model\\chinese-roberta-wwm-ext-large'def embeddings(docs, max_length=300):tokenizer = BertTokenizer.from_pretrained(model_name)model = BertModel.from_pretrained(model_name)# 对文本进行分词、编码和填充input_ids = []attention_masks = []for doc in docs:encoded_dict = tokenizer.encode_plus(doc,add_special_tokens=True,max_length=max_length,padding='max_length',truncation=True,return_attention_mask=True,return_tensors='pt')input_ids.append(encoded_dict['input_ids'])attention_masks.append(encoded_dict['attention_mask'])input_ids = torch.cat(input_ids, dim=0)attention_masks = torch.cat(attention_masks, dim=0)# 前向传播with torch.no_grad():outputs = model(input_ids, attention_mask=attention_masks)# 提取最后一层的CLS向量作为文本表示last_hidden_state = outputs.last_hidden_statecls_embeddings = last_hidden_state[:, 0, :]return cls_embeddingsif __name__ == '__main__':res = embeddings(["你好,你叫什么名字"])print(res)print(len(res))print(len(res[0]))

运行后可以看到如下日志:

在这里插入图片描述

四、ElasticSearch 存储向量

创建向量索引

PUT http://127.0.0.1:9200/medical_index
{"settings": {"number_of_shards": 3,"number_of_replicas": 1},"mappings": {"properties": {"ask_vector": {  "type": "dense_vector",  "dims": 1024  },"ask": {  "type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"},"answer": {  "type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"}}}
}

其中 dims 为向量的长度。

在这里插入图片描述

查看创建的索引:

GET http://127.0.0.1:9200/medical_index

在这里插入图片描述

数据存入 ElasticSearch

引入 ElasticSearch 依赖库:

pip install elasticsearch -i https://pypi.tuna.tsinghua.edu.cn/simple
from elasticsearch import Elasticsearch
from transformers import BertTokenizer, BertModel
import torch
import pandas as pddef embeddings_doc(doc, tokenizer, model, max_length=300):encoded_dict = tokenizer.encode_plus(doc,add_special_tokens=True,max_length=max_length,padding='max_length',truncation=True,return_attention_mask=True,return_tensors='pt')input_id = encoded_dict['input_ids']attention_mask = encoded_dict['attention_mask']# 前向传播with torch.no_grad():outputs = model(input_id, attention_mask=attention_mask)# 提取最后一层的CLS向量作为文本表示last_hidden_state = outputs.last_hidden_statecls_embeddings = last_hidden_state[:, 0, :]return cls_embeddings[0]def add_doc(index_name, id, embedding_ask, ask, answer, es):body = {"ask_vector": embedding_ask.tolist(),"ask": ask,"answer": answer}result = es.create(index=index_name, id=id, doc_type="_doc", body=body)return resultdef main():# 模型下载的地址model_name = 'D:\\AIGC\\model\\chinese-roberta-wwm-ext-large'# ES 信息es_host = "http://127.0.0.1"es_port = 9200es_user = "elastic"es_password = "elastic"index_name = "medical_index"# 数据地址path = "D:\\AIGC\\dataset\\Chinese-medical-dialogue-data\\Chinese-medical-dialogue-data\\Data_数据\\IM_内科\\内科5000-33000.csv"# 分词器和模型tokenizer = BertTokenizer.from_pretrained(model_name)model = BertModel.from_pretrained(model_name)# ES 连接es = Elasticsearch([es_host],port=es_port,http_auth=(es_user, es_password))# 读取数据写入ESdata = pd.read_csv(path, encoding='ANSI')for index, row in data.iterrows():# 写入前 5000 条进行测试if index >= 500:breakask = row["ask"]answer = row["answer"]# 文本转向量embedding_ask = embeddings_doc(ask, tokenizer, model)result = add_doc(index_name, index, embedding_ask, ask, answer, es)print(result)if __name__ == '__main__':main()

在这里插入图片描述

五、相似性搜索

1. 余弦相似度算法:cosineSimilarity

from elasticsearch import Elasticsearch
from transformers import BertTokenizer, BertModel
import torchdef embeddings_doc(doc, tokenizer, model, max_length=300):encoded_dict = tokenizer.encode_plus(doc,add_special_tokens=True,max_length=max_length,padding='max_length',truncation=True,return_attention_mask=True,return_tensors='pt')input_id = encoded_dict['input_ids']attention_mask = encoded_dict['attention_mask']# 前向传播with torch.no_grad():outputs = model(input_id, attention_mask=attention_mask)# 提取最后一层的CLS向量作为文本表示last_hidden_state = outputs.last_hidden_statecls_embeddings = last_hidden_state[:, 0, :]return cls_embeddings[0]def search_similar(index_name, query_text, tokenizer, model, es, top_k=3):query_embedding = embeddings_doc(query_text, tokenizer, model)print(query_embedding.tolist())query = {"query": {"script_score": {"query": {"match_all": {}},"script": {"source": "cosineSimilarity(params.queryVector, 'ask_vector') + 1.0","lang": "painless","params": {"queryVector": query_embedding.tolist()}}}},"size": top_k}res = es.search(index=index_name, body=query)hits = res['hits']['hits']similar_documents = []for hit in hits:similar_documents.append(hit['_source'])return similar_documentsdef main():# 模型下载的地址model_name = 'D:\\AIGC\\model\\chinese-roberta-wwm-ext-large'# ES 信息es_host = "http://127.0.0.1"es_port = 9200es_user = "elastic"es_password = "elastic"index_name = "medical_index"# 分词器和模型tokenizer = BertTokenizer.from_pretrained(model_name)model = BertModel.from_pretrained(model_name)# ES 连接es = Elasticsearch([es_host],port=es_port,http_auth=(es_user, es_password))query_text = "我有高血压可以拿党参泡水喝吗"similar_documents = search_similar(index_name, query_text, tokenizer, model, es)for item in similar_documents:print("================================")print('ask:', item['ask'])print('answer:', item['answer'])if __name__ == '__main__':main()

打印日志如下:

在这里插入图片描述

================================
ask: 我有高血压这两天女婿来的时候给我拿了些党参泡水喝,您好高血压可以吃党参吗?
answer: 高血压病人可以口服党参的。党参有降血脂,降血压的作用,可以彻底消除血液中的垃圾,从而对冠心病以及心血管疾病的患者都有一定的稳定预防工作作用,因此平时口服党参能远离三高的危害。另外党参除了益气养血,降低中枢神经作用,调整消化系统功能,健脾补肺的功能。感谢您的进行咨询,期望我的解释对你有所帮助。
================================
ask: 我准备过两天去看我叔叔,顺便带些人参,但是他有高血压,您好人参高血压可以吃吗?
answer: 人参有一定的调压作用,主要用来气虚体虚的患者,如果有气血不足,气短乏力,神经衰弱,神经衰弱健忘等不适症状的话,可以适当口服人参调养身体,但是对于高血压的病人,如果长期食用人参的话,可能会对血压引发一定影响,所以,比较好到医院中医科实施辨证论治调治,看如何适合食用人参。
================================
ask: 我妈妈有点高血压,比较近我朋友送了我一些丹参片,我想知道高血压能吃丹参片吗?
answer: 丹参片具备活血化瘀打通血管的作用可以致使血液粘稠度减低,所以就容易致使血管内血液供应便好防止出现血液粘稠,致使血压下降,所以对降血压是有一定帮助的,高血压患者是经常使用丹参片实施治疗的。可以预防,因为血液粘稠引来的冠心病心绞痛以及外周血管脑水肿症状。

2. 点积算法:dotProduct

计算给定查询向量和文档向量之间的点积度量。

from elasticsearch import Elasticsearch
from transformers import BertTokenizer, BertModel
import torchdef embeddings_doc(doc, tokenizer, model, max_length=300):encoded_dict = tokenizer.encode_plus(doc,add_special_tokens=True,max_length=max_length,padding='max_length',truncation=True,return_attention_mask=True,return_tensors='pt')input_id = encoded_dict['input_ids']attention_mask = encoded_dict['attention_mask']# 前向传播with torch.no_grad():outputs = model(input_id, attention_mask=attention_mask)# 提取最后一层的CLS向量作为文本表示last_hidden_state = outputs.last_hidden_statecls_embeddings = last_hidden_state[:, 0, :]return cls_embeddings[0]def search_similar(index_name, query_text, tokenizer, model, es, top_k=3):query_embedding = embeddings_doc(query_text, tokenizer, model)print(query_embedding.tolist())query = {"query": {"script_score": {"query": {"match_all": {}},"script": {"source": "dotProduct(params.queryVector, 'ask_vector')+1.0","lang": "painless","params": {"queryVector": query_embedding.tolist()}}}},"size": top_k}res = es.search(index=index_name, body=query)hits = res['hits']['hits']similar_documents = []for hit in hits:similar_documents.append(hit['_source'])return similar_documentsdef main():# 模型下载的地址model_name = 'D:\\AIGC\\model\\chinese-roberta-wwm-ext-large'# ES 信息es_host = "http://127.0.0.1"es_port = 9200es_user = "elastic"es_password = "elastic"index_name = "medical_index"# 分词器和模型tokenizer = BertTokenizer.from_pretrained(model_name)model = BertModel.from_pretrained(model_name)# ES 连接es = Elasticsearch([es_host],port=es_port,http_auth=(es_user, es_password))query_text = "我有高血压可以拿党参泡水喝吗"similar_documents = search_similar(index_name, query_text, tokenizer, model, es)for item in similar_documents:print("================================")print('ask:', item['ask'])print('answer:', item['answer'])if __name__ == '__main__':main()

在这里插入图片描述

================================
ask: 我有高血压这两天女婿来的时候给我拿了些党参泡水喝,您好高血压可以吃党参吗?
answer: 高血压病人可以口服党参的。党参有降血脂,降血压的作用,可以彻底消除血液中的垃圾,从而对冠心病以及心血管疾病的患者都有一定的稳定预防工作作用,因此平时口服党参能远离三高的危害。另外党参除了益气养血,降低中枢神经作用,调整消化系统功能,健脾补肺的功能。感谢您的进行咨询,期望我的解释对你有所帮助。
================================
ask: 我准备过两天去看我叔叔,顺便带些人参,但是他有高血压,您好人参高血压可以吃吗?
answer: 人参有一定的调压作用,主要用来气虚体虚的患者,如果有气血不足,气短乏力,神经衰弱,神经衰弱健忘等不适症状的话,可以适当口服人参调养身体,但是对于高血压的病人,如果长期食用人参的话,可能会对血压引发一定影响,所以,比较好到医院中医科实施辨证论治调治,看如何适合食用人参。
================================
ask: 我妈妈有点高血压,比较近我朋友送了我一些丹参片,我想知道高血压能吃丹参片吗?
answer: 丹参片具备活血化瘀打通血管的作用可以致使血液粘稠度减低,所以就容易致使血管内血液供应便好防止出现血液粘稠,致使血压下降,所以对降血压是有一定帮助的,高血压患者是经常使用丹参片实施治疗的。可以预防,因为血液粘稠引来的冠心病心绞痛以及外周血管脑水肿症状。

3. L1曼哈顿距离:l1norm

计算给定查询向量和文档向量之间的L1距离。

from elasticsearch import Elasticsearch
from transformers import BertTokenizer, BertModel
import torchdef embeddings_doc(doc, tokenizer, model, max_length=300):encoded_dict = tokenizer.encode_plus(doc,add_special_tokens=True,max_length=max_length,padding='max_length',truncation=True,return_attention_mask=True,return_tensors='pt')input_id = encoded_dict['input_ids']attention_mask = encoded_dict['attention_mask']# 前向传播with torch.no_grad():outputs = model(input_id, attention_mask=attention_mask)# 提取最后一层的CLS向量作为文本表示last_hidden_state = outputs.last_hidden_statecls_embeddings = last_hidden_state[:, 0, :]return cls_embeddings[0]def search_similar(index_name, query_text, tokenizer, model, es, top_k=3):query_embedding = embeddings_doc(query_text, tokenizer, model)print(query_embedding.tolist())query = {"query": {"script_score": {"query": {"match_all": {}},"script": {"source": "1 / (1 + l1norm(params.queryVector, doc['ask_vector']))","lang": "painless","params": {"queryVector": query_embedding.tolist()}}}},"size": top_k}res = es.search(index=index_name, body=query)hits = res['hits']['hits']similar_documents = []for hit in hits:similar_documents.append(hit['_source'])return similar_documentsdef main():# 模型下载的地址model_name = 'D:\\AIGC\\model\\chinese-roberta-wwm-ext-large'# ES 信息es_host = "http://127.0.0.1"es_port = 9200es_user = "elastic"es_password = "elastic"index_name = "medical_index"# 分词器和模型tokenizer = BertTokenizer.from_pretrained(model_name)model = BertModel.from_pretrained(model_name)# ES 连接es = Elasticsearch([es_host],port=es_port,http_auth=(es_user, es_password))query_text = "我有高血压可以拿党参泡水喝吗"similar_documents = search_similar(index_name, query_text, tokenizer, model, es)for item in similar_documents:print("================================")print('ask:', item['ask'])print('answer:', item['answer'])if __name__ == '__main__':main()

在这里插入图片描述

================================
ask: 我有高血压这两天女婿来的时候给我拿了些党参泡水喝,您好高血压可以吃党参吗?
answer: 高血压病人可以口服党参的。党参有降血脂,降血压的作用,可以彻底消除血液中的垃圾,从而对冠心病以及心血管疾病的患者都有一定的稳定预防工作作用,因此平时口服党参能远离三高的危害。另外党参除了益气养血,降低中枢神经作用,调整消化系统功能,健脾补肺的功能。感谢您的进行咨询,期望我的解释对你有所帮助。
================================
ask: 我准备过两天去看我叔叔,顺便带些人参,但是他有高血压,您好人参高血压可以吃吗?
answer: 人参有一定的调压作用,主要用来气虚体虚的患者,如果有气血不足,气短乏力,神经衰弱,神经衰弱健忘等不适症状的话,可以适当口服人参调养身体,但是对于高血压的病人,如果长期食用人参的话,可能会对血压引发一定影响,所以,比较好到医院中医科实施辨证论治调治,看如何适合食用人参。
================================
ask: 我妈妈有点高血压,比较近我朋友送了我一些丹参片,我想知道高血压能吃丹参片吗?
answer: 丹参片具备活血化瘀打通血管的作用可以致使血液粘稠度减低,所以就容易致使血管内血液供应便好防止出现血液粘稠,致使血压下降,所以对降血压是有一定帮助的,高血压患者是经常使用丹参片实施治疗的。可以预防,因为血液粘稠引来的冠心病心绞痛以及外周血管脑水肿症状。

4. l2 欧几里得距离:l2norm

计算给定查询向量和文档向量之间的欧几里德距离。

from elasticsearch import Elasticsearch
from transformers import BertTokenizer, BertModel
import torchdef embeddings_doc(doc, tokenizer, model, max_length=300):encoded_dict = tokenizer.encode_plus(doc,add_special_tokens=True,max_length=max_length,padding='max_length',truncation=True,return_attention_mask=True,return_tensors='pt')input_id = encoded_dict['input_ids']attention_mask = encoded_dict['attention_mask']# 前向传播with torch.no_grad():outputs = model(input_id, attention_mask=attention_mask)# 提取最后一层的CLS向量作为文本表示last_hidden_state = outputs.last_hidden_statecls_embeddings = last_hidden_state[:, 0, :]return cls_embeddings[0]def search_similar(index_name, query_text, tokenizer, model, es, top_k=3):query_embedding = embeddings_doc(query_text, tokenizer, model)print(query_embedding.tolist())query = {"query": {"script_score": {"query": {"match_all": {}},"script": {"source": "1 / (1 + l2norm(params.queryVector, doc['ask_vector']))","lang": "painless","params": {"queryVector": query_embedding.tolist()}}}},"size": top_k}res = es.search(index=index_name, body=query)hits = res['hits']['hits']similar_documents = []for hit in hits:similar_documents.append(hit['_source'])return similar_documentsdef main():# 模型下载的地址model_name = 'D:\\AIGC\\model\\chinese-roberta-wwm-ext-large'# ES 信息es_host = "http://127.0.0.1"es_port = 9200es_user = "elastic"es_password = "elastic"index_name = "medical_index"# 分词器和模型tokenizer = BertTokenizer.from_pretrained(model_name)model = BertModel.from_pretrained(model_name)# ES 连接es = Elasticsearch([es_host],port=es_port,http_auth=(es_user, es_password))query_text = "我有高血压可以拿党参泡水喝吗"similar_documents = search_similar(index_name, query_text, tokenizer, model, es)for item in similar_documents:print("================================")print('ask:', item['ask'])print('answer:', item['answer'])if __name__ == '__main__':main()

在这里插入图片描述

================================
ask: 我有高血压这两天女婿来的时候给我拿了些党参泡水喝,您好高血压可以吃党参吗?
answer: 高血压病人可以口服党参的。党参有降血脂,降血压的作用,可以彻底消除血液中的垃圾,从而对冠心病以及心血管疾病的患者都有一定的稳定预防工作作用,因此平时口服党参能远离三高的危害。另外党参除了益气养血,降低中枢神经作用,调整消化系统功能,健脾补肺的功能。感谢您的进行咨询,期望我的解释对你有所帮助。
================================
ask: 我准备过两天去看我叔叔,顺便带些人参,但是他有高血压,您好人参高血压可以吃吗?
answer: 人参有一定的调压作用,主要用来气虚体虚的患者,如果有气血不足,气短乏力,神经衰弱,神经衰弱健忘等不适症状的话,可以适当口服人参调养身体,但是对于高血压的病人,如果长期食用人参的话,可能会对血压引发一定影响,所以,比较好到医院中医科实施辨证论治调治,看如何适合食用人参。
================================
ask: 我妈妈有点高血压,比较近我朋友送了我一些丹参片,我想知道高血压能吃丹参片吗?
answer: 丹参片具备活血化瘀打通血管的作用可以致使血液粘稠度减低,所以就容易致使血管内血液供应便好防止出现血液粘稠,致使血压下降,所以对降血压是有一定帮助的,高血压患者是经常使用丹参片实施治疗的。可以预防,因为血液粘稠引来的冠心病心绞痛以及外周血管脑水肿症状。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/96108.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云计算 - 百度AIStudio使用小结

云计算 - 百度AIStudio使用小结 前言 本文以ffmpeg处理视频为例,小结一下AI Studio的使用体验及一些避坑技巧。 算力获得 免费的算力获得方式为:每日登录后运行一个项目(只需要点击运行,不需要真正运行)即可获得8小…

el-table中点击跳转到详情页的两种方法

跳转的两种写法: 1.使用keep-alive使组件缓存,防止刷新时参数丢失 keep-alive 组件用于缓存和保持组件的状态,而不是路由参数。它可以在组件切换时保留组件的状态,从而避免重新渲染和加载数据。 keep-alive 主要用于提高页面性能和用户体验,而…

Spark有两种常见的提交方式:client 模式和 cluster 模式对机器 CPU 的影响

Spark有两种常见的提交方式:client 模式和 cluster 模式。这两种方式对机器 CPU 的影响略有不同 ,请参考以下说明 Client 模式: 在 Client 模式下,Spark Driver 运行在提交任务的客户端节点上(即运行 spark-submit 命…

windows 搭建 swoole开发环境(官网已支持)

第一步下载:swoole官网下载 swoole-cli-v5.0.3-cygwin-x64.zip 只支持 64 位的系统 第二步解压到指定文件夹:E:\phpstudy_pro\WWW\swoole-cli-v5.0.3-cygwin-x64 第三步设置环境变量:把解压后的文件夹下的 bin 目录路径配置到系统的 Path 环境…

对Excel表中归类的文件夹进行自动分类

首先把excel表另存为.txt文件(注意:刚开始可能是ANSI格式,需要转成UTF-8格式);再新建一个.txt文件,重命名成.bat文件(注意:直接创建的如果是是UTF-8格式,最好转成ANSI格式&#xff0…

Mariadb高可用(四十)

目录 一、概述 (一)概念 (二)组成 (三)特点 (四)工作原理 二、实验要求 三、构建MHA (一)ssh免密登录 (二)安装mariadb数据库…

android framework之Applicataion启动流程分析(三)

现在再回顾一下Application的启动流程,总的来说,虽然进程的发起是由ATMS服务发起的,但是进程的启动还是由AMS负责,所以需要调用AMS的startProcess()接口完成进程启动流程,AMS要处理的事情很多,它将事务交给…

解读亚马逊云科技语义搜图检索方案

图像检索(包括文搜图和图搜图)是各个行业中常见的一个应用场景。比如在电商场景中,基于以图搜图做相似商品查找;在云相册场景中,基于文搜图来找寻所需的图像素材。 传统基于标签的图像检索方式,即先使用目标…

【Linux】JumpServer 堡垒机远程访问

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpS…

7.react useReducer使用与常见问题

useReducer函数 1. useState的替代方案.接收一个(state, action)>newState的reducer, 并返回当前的state以及与其配套的dispatch方法2. 在某些场景下,useReducer会比useState更加适用,例如state逻辑较为复杂, 且**包含多个子值**,或者下一个state依赖于之前的state等清楚us…

Cmake qt ,vtkDataArray.cxx.obj: File too big

解决方法: Qt4 在pro 加入“QMAKE_CXXFLAGS -BigObj” 可以解决 Qt5 在网上用“-Wa,-mbig-obj” 不能解决,最后通过“QMAKE_CXXFLAGS -Ofast -flto”解决问题。 Qt4 在pro 加入“QMAKE_CXXFLAGS -BigObj” 可以解决Qt5 在网上用“-Wa,-mbig-obj” …

数据集收集列表(opencv,机器学习,深度学习)持续更新

opencv 车牌识别数据集 opencv 手写数字识别数据集 机器学习 印第安糖尿病 Pima Indians数据集 ,下载地址 Boston波士顿房价数据集 ,下载