增强常见问题解答搜索引擎:在 Elasticsearch 中利用 KNN 的力量

在快速准确的信息检索至关重要的时代,开发强大的搜索引擎至关重要。 随着大型语言模型和信息检索架构(如 RAG)的出现,在现代软件系统中利用文本表示(向量/嵌入)和向量数据库已变得越来越流行。 在本文中,我们深入研究了如何使用 Elasticsearch 的 K 最近邻 (KNN) 搜索和来自强大语言模型的文本嵌入,这是一个强大的组合,有望彻底改变我们访问常见问题 (FAQ) 的方式。 通过对 Elasticsearch 的 KNN 功能的全面探索,我们将揭示这种集成如何使我们能够创建尖端的常见问题解答搜索引擎,通过以闪电般的延迟理解查询的语义上下文,从而增强用户体验。

在开始设计解决方案之前,让我们了解信息检索系统中的一些基本概念。

文本表示(嵌入)

你可以通过阅读 “Elasticsearch:什么是向量和向量存储数据库,我们为什么关心?” 来了解更多的关于文本嵌入的知识。

嵌入是一条信息的数字表示,例如文本、文档、图像、音频等。 该表示捕获了所嵌入内容的语义,使其对于许多行业应用程序来说都是稳健的。

语义搜索

传统的搜索系统使用词法匹配来检索给定查询的文档。 语义搜索旨在使用文本表示(嵌入)来理解查询的上下文,以提高搜索准确性。

语义搜索的类型

  • 对称语义搜索:查询和搜索文本长度相似的搜索用例。 例如 在数据集中找到类似的问题。
  • 非对称语义搜索:查询和搜索文本长度不同的搜索用例。 例如 查找给定查询的相关段落。

向量搜索引擎(向量数据库)

向量搜索引擎是专用数据库,可用于将图像、文本、音频或视频等非结构化信息存储为嵌入或向量。 在本文中,我们将使用 Elasticsearch 的向量搜索功能。

现在我们了解了搜索系统的构建块,让我们深入了解解决方案架构和实现。

  1. 搜索解决方案的第一步是将问题-答案对索引到 Elasticsearch 中。 我们将创建一个索引并将问题和答案嵌入存储在同一索引中。 我们将根据检索的特征使用两个独立的模型来嵌入问题和答案。
  2. 我们将使用步骤 1 中使用的相同模型来嵌入查询,并形成搜索查询(3 个部分,即问题、答案、词汇搜索),将查询嵌入映射到相应的问题和答案嵌入。
  3. 我们还将为查询的每个部分提供一个提升值,以表示它们在组合中的重要性。 返回的最终结果根据分数总和乘以各自的提升值进行排名。

环境设置

要使用 docker 安装 Elasticsearch,请参阅这篇有关如何设置单节点集群的详细文章。 如果你已有集群,请跳过此步骤。如果你想详细了解如何安装 Elasticsearch,请参考文章 “如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch”。在本演示中,我们将使用 Elastic Stack 8.10.4 来进行展示。

设置你的索引。 你可以使用以下映射作为起点。我们在 Kibana 的 Dev Tools 中打入如下的命令:

PUT faq-index
{"settings": {"number_of_shards": 1,"number_of_replicas": 0},"mappings": {"properties": {"Question": {"type": "text"},"Answer": {"type": "text"},"question_emb": {"type": "dense_vector","dims": 768,"index": true,"similarity": "dot_product"},"answer_emb": {"type": "dense_vector","dims": 1024,"index": true,"similarity": "dot_product"}}}
}

模型选择

由于我们使用相当通用的语言处理数据,因此为了进行本实验,我从 MTEB 排行榜的检索(用于答案)和 STS(用于问题)部分中选择了表现最好的模型。

选定型号:

  1. 答案:BAAI/bge-large-en-v1.5(您可以使用量化版本以加快推理速度)
  2. 如有问题:thenlper/gte-base

如果你有特定领域的常见问题解答并想要检查哪种模型表现最好,你可以使用 Beir。 查看本节,其中描述了如何加载自定义数据集以进行评估。

实现

出于本实验的目的,我将使用 Kaggle 的心理健康常见问题解答数据集。

安装所需要的模块

pips install sentence_transformers

1. 装载数据

import pandas as pd
data = pd.read_csv('Mental_Health_FAQ.csv')

2. 生成嵌入

Questions

from sentence_transformers import SentenceTransformer
question_emb_model = SentenceTransformer('thenlper/gte-base')data['question_emb'] = data['Questions'].apply(lambda x: question_emb_model.encode(x, normalize_embeddings=True))

注意:我们对嵌入进行归一化,以使用点积作为相似性度量而不是余弦相似性。 该计算速度更快,并且在 Elasticsearch 密集向量场文档中得到推荐。

Answers:

answer_emb_model = SentenceTransformer('BAAI/bge-large-en-v1.5')
data['answer_emb'] = data['Answers'].apply(lambda x: answer_emb_model.encode(x, normalize_embeddings=True))

3. 索引文档

我们将使用 Elasticsearch  helper 函数。 具体来说,我们将使用 streaming_bulk API 来索引我们的文档。

首先,让我们实例化 elasticsearch python 客户端。

我们首先需要把安装好的 Elasticsearch 的证书拷贝到当前目录中:

$ pwd
/Users/liuxg/python/faq
$ cp ~/elastic/elasticsearch-8.10.4/config/certs/http_ca.crt .
$ ls
Mental Health FAQ.ipynb archive (13).zip
Mental_Health_FAQ.csv   http_ca.crt

然后我们打入如下的代码:

from elasticsearch import Elasticsearchfrom ssl import create_default_contextcontext = create_default_context(cafile=r"./http_ca.crt")
es = Elasticsearch('https://localhost:9200',basic_auth=('elastic', 'YlGXk9PCN7AUlc*VMtQj'),ssl_context=context,
)

接下来,我们需要创建一个可以输入到流式 bulk API 中的文档生成器。

index_name="faq-index"
def generate_docs():for index, row in data.iterrows():doc = {"_index": index_name,"_source": {"faq_id":row['Question_ID'],"question":row['Questions'],"answer":row['Answers'],"question_emb": row['question_emb'],"answer_emb": row['answer_emb']},}yield doc

最后,我们可以索引文档。

import tqdm
from elasticsearch.helpers import streaming_bulk
number_of_docs=len(data)
progress = tqdm.tqdm(unit="docs", total=number_of_docs)
successes = 0
for ok, action in streaming_bulk(client=es, index=index_name, actions=generate_docs()):progress.update(1)successes += okprint("Indexed %d/%d documents" % (successes, number_of_docs))

4. 查询文档

def faq_search(query="", k=10, num_candidates=10):if query is not None and len(query) == 0:print('Query cannot be empty')return Noneelse:query_question_emb = question_emb_model.encode(query, normalize_embeddings=True)instruction="Represent this sentence for searching relevant passages: "query_answer_emb = answer_emb_model.encode(instruction + query, normalize_embeddings=True)payload = {"query": {"match": {"title": {"query": query,"boost": 0.2}}},"knn": [ {"field": "question_emb","query_vector": query_question_emb,"k": k,"num_candidates": num_candidates,"boost": 0.3},{"field": "answer_emb","query_vector": query_answer_emb,"k": k,"num_candidates": num_candidates,"boost": 0.5}],"size": 10,"_source":["faq_id","question", "answer"]}response = es.search(index=index_name, body=payload)['hits']['hits']return response

按照模型页面上的说明,我们需要在将查询转换为嵌入之前将指令附加到查询中。 此外,我们使用模型的 v1.5,因为它具有更好的相似度分布。 查看型号页面上的常见问题解答以了解更多详细信息。

评估

为了了解所提出的方法是否有效,根据传统的 KNN 搜索系统对其进行评估非常重要。 让我们尝试定义这两个系统并评估所提出的系统。

  • 系统 1:非对称 KNN 搜索(查询和答案向量)。
  • 系统2:查询(BM25)、非对称KNN搜索(查询和答案向量)和对称KNN搜索(查询和问题向量)的组合。

为了评估系统,我们必须模仿用户如何使用搜索。 简而言之,我们需要从源问题生成与问题复杂性相似的释义问题。 我们将使用 t5-small-finetuned-quora-for-paraphrasing 微调模型来解释问题。

让我们定义一个可以生成释义问题的函数。

from transformers import AutoModelWithLMHead, AutoTokenizertokenizer = AutoTokenizer.from_pretrained("mrm8488/t5-small-finetuned-quora-for-paraphrasing")
model = AutoModelWithLMHead.from_pretrained("mrm8488/t5-small-finetuned-quora-for-paraphrasing")def paraphrase(question, number_of_questions=3, max_length=128):input_ids = tokenizer.encode(question, return_tensors="pt", add_special_tokens=True)generated_ids = model.generate(input_ids=input_ids, num_return_sequences=number_of_questions, num_beams=5, max_length=max_length, no_repeat_ngram_size=2, repetition_penalty=3.5, length_penalty=1.0, early_stopping=True)preds = [tokenizer.decode(g, skip_special_tokens=True, clean_up_tokenization_spaces=True) for g in generated_ids]return preds

现在我们已经准备好了释义函数,让我们创建一个评估数据集,用于测量系统的准确性。

temp_data = data[['Question_ID','Questions']]eval_data = []for index, row in temp_data.iterrows():preds = paraphrase("paraphrase: {}".format(row['Questions']))for pred in preds:temp={}temp['Question'] = predtemp['FAQ_ID'] = row['Question_ID']eval_data.append(temp)eval_data = pd.DataFrame(eval_data)#shuffle the evaluation dataset
eval_data=eval_data.sample(frac=1).reset_index(drop=True)

上面的代码生成相应的测试 Question,它们的结果如下:

最后,我们将修改 “faq_search” 函数以返回各个系统的 faq_id。

对于系统 1:

def get_faq_id_s1(query="", k=5, num_candidates=10):if query is not None and len(query) == 0:print('Query cannot be empty')return Noneelse:instruction="Represent this sentence for searching relevant passages: "query_answer_emb = answer_emb_model.encode(instruction + query, normalize_embeddings=True)payload = {"knn": [{"field": "answer_emb","query_vector": query_answer_emb,"k": k,"num_candidates": num_candidates,}],"size": 1,"_source":["faq_id"]}response = es.search(index=index_name, body=payload)['hits']['hits']return response[0]['_source']['faq_id']

对于系统 2:

def get_faq_id_s2(query="", k=5, num_candidates=10):if query is not None and len(query) == 0:print('Query cannot be empty')return Noneelse:query_question_emb = question_emb_model.encode(query, normalize_embeddings=True)instruction="Represent this sentence for searching relevant passages: "query_answer_emb = answer_emb_model.encode(instruction + query, normalize_embeddings=True)payload = {"query": {"match": {"title": {"query": query,"boost": 0.2}}},"knn": [ {"field": "question_emb","query_vector": query_question_emb,"k": k,"num_candidates": num_candidates,"boost": 0.3},{"field": "answer_emb","query_vector": query_answer_emb,"k": k,"num_candidates": num_candidates,"boost": 0.5}],"size": 1,"_source":["faq_id"]}response = es.search(index=index_name, body=payload)['hits']['hits']return response[0]['_source']['faq_id']

注意:boost 值是实验性的。 为了这个实验的目的,我根据组合中各个字段的重要性进行了划分。 搜索中每个字段的重要性完全是主观的,可能由业务本身定义,但如果不是,系统的一般经验法则是 Answer 向量 > Question 向量 > 查询。

好的! 我们一切准备就绪,开始我们的评估。 我们将为两个系统生成一个预测列,并将其与原始 faq_id 进行比较。

eval_data['PRED_FAQ_ID_S1'] = eval_data['Question'].apply(get_faq_id_s1)from sklearn.metrics import accuracy_scoreground_truth = eval_data["FAQ_ID"].values
predictions_s1 = eval_data["PRED_FAQ_ID_S1"].valuess1_accuracy = accuracy_score(ground_truth, predictions_s1)print('System 1 Accuracy: {}'.format(s1_accuracy))

eval_data['PRED_FAQ_ID_S2'] = eval_data['Question'].apply(get_faq_id_s2)predictions_s2 = eval_data["PRED_FAQ_ID_S2"].valuess2_accuracy = accuracy_score(ground_truth, predictions_s2)print('System 2 Accuracy: {}'.format(s2_accuracy))

通过所提出的系统,我们可以看到与非对称 KNN 搜索相比,准确率提高了 7-11%。

我们还可以尝试 ramsrigouthamg/t5_paraphraser,但该模型生成的问题有点复杂和冗长(尽管在上下文中)。

你还可以使用 LLM 生成评估数据集并检查系统的性能。

准确性的提高是主观的,取决于查询的质量,即 查询的上下文有多丰富、嵌入的质量和/或使用搜索的用户类型。 为了更好地理解这一点,让我们考虑两种最终用户:

  1. 想要了解有关您的产品和服务的一些事实的一般用户:在这种情况下,上述系统会做得很好,因为问题简单、直观且上下文充分。
  2. 领域/产品特定用户,例如 想要了解产品的一些复杂细节以设置系统或解决某些问题的工程师:在这种情况下,查询在词汇组成方面更具特定于领域,因此开箱即用的模型嵌入将无法捕获所有上下文。 那么,我们该如何解决这个问题呢? 系统的架构将保持不变,但可以通过使用特定领域数据(或预先训练的特定领域模型)微调这些模型来提高搜索系统的整体准确性。

结论

在本文中,我们提出并实现了结合搜索类型的常见问题解答搜索。 我们研究了 Elasticsearch 如何使我们能够结合对称和非对称语义搜索,从而将搜索系统的性能提高高达 11%。 我们还了解所提出的搜索架构的系统和资源要求,这将是考虑采用这种方法时的主要决定因素。

你可以在我的 Github 存储库中找到源笔记本。

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

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

相关文章

Java工具库——commons-lang3的50个常用方法

未来的你,我亲爱的女孩,愿此刻无忧无虑,开心,快乐… 工具库介绍 Apache Commons Lang 3(通常简称为Commons Lang 3)是Apache Commons项目中的一个Java工具库,它提供了一系列实用的工具类和方法…

Android---如何同view进行渲染

ViewRootImpl 在 Activity、window 和 View 三者关系之间起着承上启下的作用。一方面,ViewRootImpl 中通过 Binder 通信机制,远程调用 WindowSession 将 View 添加到 Window 中;另一方面,ViewRootImpl 在添加 View 之前&#xff0…

树莓派 Qt中 QCameraInfo 无法使用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、QCameraInfo 是什么?二、使用步骤1.测试代码2.解决方案2.1输入命令2.2输出 3. 成功打印了摄像头的信息 总结 前言 提示:这里可以添…

Spring的条件注解,一篇文章盘得清清楚楚明明白白

前言 在Spring中,条件注解可根据特定的条件来决定是否创建或配置Bean,这些条件可以基于类、属性、环境等因素。通过使用条件注解,我们可以在Spring容器中更加灵活地管理和控制组件的创建和注入,帮助我们更加灵活地管理和控制Bean…

51单片机-点阵屏led

代码配置 这样就能选择每一列哪个亮了 进行位选,段清零,这样就不会影响多列同时了 实现动画 1、使用文字摸提取文件,提取图案的16进制表示数组 offest作为偏移量,count作为计时。count10,偏移量加1,就相当于得到下一…

毕业设计基于SpringMVC+Mybatis+Bootstrap的电影院管理系统源码+数据库

<<电影院管理系统>> 电影院管理系统&#xff1a;SpringMVCJSPTomcatMybatisBootstrapJqueryAnimateCSSLayerJS 项目部署&#xff1a;该项目是IDEA版本&#xff0c;Maven项目 前端依赖&#xff1a; Bootstrap-3.4.1Animate.css- 4.1.1Jquery-3.6.0Layer-v3.5.1B…

自学爬虫—作业1—requests模块

视频&#xff1a; 要求&#xff1a; 肯德基地址查询&#xff0c;爬某个关键字&#xff0c;获取下面的所有page的信息&#xff0c;存到一个json或者txt。 代码&#xff1a; 关键点&#xff0c;&#xff08;1&#xff09;每一个ajax的请求第一个键值对就是所有获得的地址的总数…

Python语言: 切片的使用

切片的本质&#xff1a;通过切片来截取指定的元素&#xff0c;形成一个新的容器。 切片的具体阐释&#xff1a; 此切片非切片面包的切片&#xff0c;而是python语言中的切片。切片&#xff1a;顾名思义&#xff0c;就是把整块的东西分割开来。python语言中的切片是把一个容器截…

微信小程序 slot 不显示

问题:创建组件&#xff0c;使用带名字的slot&#xff0c;页面调用组件使用slot不显示 源码&#xff1a; 组件xml <view class"p-item br24" style"{{style}}"><slot name"right" wx:if"{{!custBottom}}"></slot>&l…

java商城免费搭建 VR全景商城 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城 短视频商城

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

maven环境变量,安装源,本地仓库配置

1. maven环境变量 我这里用的是idea自带的maven 数值为&#xff1a; D:\software\computer_software\java\IDEAJ\IDEAJ2021.2.1\IntelliJ IDEA 2021.2.1\plugins\maven\lib\maven3\bin 2. 安装源更换为阿里云&#xff08;我不知道清华源是什么网址&#xff0c;网上也没查到&am…

分布式:一文吃透分布式事务和seata事务

目录 一、事务基础概念二、分布式事务概念什么是分布式事务分布式事务场景CAP定理CAP理论理解CAPCAP的应用 BASE定理强一致性和最终一致性BASE理论 分布式事务分类刚性事务柔性事务 三、分布式事务解决方案方案汇总XA规范方案1&#xff1a;2PC第一阶段&#xff1a;准备阶段第二…

【计算机网络】什么是HTTPS?HTTPS为什么是安全的?

【面试经典题】 前言&#xff1a; HTTP最初的设计就是用于数据的共享和传输&#xff0c;并没有考虑到数据的安全性&#xff0c;如窃听风险&#xff0c;篡改风险和冒充风险。HTTPS是在 HTTP 的基础上引入了一个加密层。HTTPS通过数据加密&#xff0c;数据完整性检验和身份认证…

【Java 进阶篇】Java登录案例详解

登录是Web应用程序中常见的功能&#xff0c;它允许用户提供凭证&#xff08;通常是用户名和密码&#xff09;以验证其身份。本文将详细介绍如何使用Java创建一个简单的登录功能&#xff0c;并解释登录的工作原理。我们将覆盖以下内容&#xff1a; 登录的基本概念创建一个简单的…

安装Jdk 报错 ,Java SE Development Kit 8 Update 202(64-bit)安装完毕之前,向导被中断

具体原因没有找到&#xff0c;估计是由于jdk 没有删干净导致的&#xff0c;我的处理方法是&#xff0c;将 Java的注册表全然后手动安装 Jdk和导入注册表&#xff08;在同事那里获取jdk文件 压缩包&#xff0c;并将 java的注册表导出&#xff0c;放在自己电脑上使用。&#xff0…

ChatGPT扩展系列之ChatExcel

文章目录 ChatGPT扩展系列之ChatExcel对某一列的文字进行处理对数据进行排序对数据进行计算微软官方又推出Excel AI插件ChatGPT扩展系列之ChatExcel 自从ChatGPT很空出世之后,很多基于ChatGPT的应用便如雨后春笋般应用而生,这些应用的底层本质就是利用了ChatGPT对自然语言的…

M1本地部署Stable Diffusion

下载安装 参考博客: 在Mac上部署Stable Diffusion&#xff08;超详细&#xff0c;AI 绘画入门保姆级教程&#xff09; 安装需要的依赖库 brew install cmake protobuf rust python3.10 git wget 可能中途会存在下载报错或者下载卡主的问题,需要切国内源 brew进行替换源: …

树莓派 qt 调用multimedia、multimediawidgets、serialport、Qchats

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、测试11.命令安装出现错误 二、测试21. 安装 Qt Charts&#xff1a;2. 安装 Qt Multimedia 和 Qt MultimediaWidgets&#xff1a;3. 安装 Qt SerialPort&…

C/S架构和B/S架构

1. C/S架构和B/S架构简介 C/S 架构&#xff08;Client/Server Architecture&#xff09;和 B/S 架构&#xff08;Browser/Server Architecture&#xff09;是两种不同的软件架构模式&#xff0c;它们描述了客户端和服务器之间的关系以及数据交互的方式。 C/S 架构&#xff08…

Springboot的Container Images,docker加springboot

Spring Boot应用程序可以使用Dockerfiles容器化&#xff0c;或者使用Cloud Native Buildpacks来创建优化的docker兼容的容器映像&#xff0c;您可以在任何地方运行。 1. Efficient Container Images 很容易将Spring Boot fat jar打包为docker映像。然而&#xff0c;像在docke…