基于Embedding实现超越ES的高级搜索

目前,很多企业会利用大模型来做企业内部知识库系统,比如智能客服系统,做法是把企业内部的整理的产品手册、常见问题手册,做成智能知识库系统,客户可以直接向知识库系统用自然语言提问,知识库系统能理解客户的问题并基于内部知识给出客户想要的答案,此时就会用到文本向量化。

什么是向量

一个二维向量可以理解为平面坐标轴中的一个坐标点(x,y),在编程领域,一个二维向量就是一个大小为二的float类型的数组。

文本向量化

所谓文本向量化是指,利用大模型可以把一个字、一个词或一段话映射为一个多维向量,比如我们可以直接在LangChain4j中来调用向量模型来对一句话进行向量化体验:

package com.timi;import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.model.output.Response;public class _05_Vector {public static void main(String[] args) {OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();Response<Embedding> embed = embeddingModel.embed("你好,我叫司马懿");System.out.println(embed.content().toString());System.out.println(embed.content().vector().length);}
}

代码执行结果为:

Embedding { vector = [-0.00991016, -0.009099855, ...] }
1536

从结果可以知道"你好,我叫司马懿"这句话经过OpenAiEmbeddingModel向量化之后得到的一个长度为1536的float数组。注意,1536是固定的,不会随着句子长度而变化。

那么,我们通过这种向量模型得到一句话对应的向量有什么作用呢?非常有用,因为我们可以基于向量来判断两句话之间的相似度。

向量相似度

前面提到,向量相当于就是坐标点,如果两个坐标点靠得近,那么就表示这两个向量相似,所以,如果两句话对应的向量相似,那么就表示这两句话语义比较相似,当然这中间最关键的就是向量模型,因为向量是它生成的,向量模型也是经过大量机器学习训练之后产生的,向量模型效果越好,就表示它对于自然语言理解的程度越好,同时也就表示它生成出来的向量越准备,越能反映出语义的相似度。

比如可以通过计算两个坐标的余弦相似度(代码是ChatGPT生成的):

import java.util.*;public class CosineSimilarity {// 计算两个向量的点积public static double dotProduct(double[] vectorA, double[] vectorB) {double dotProduct = 0;for (int i = 0; i < vectorA.length; i++) {dotProduct += vectorA[i] * vectorB[i];}return dotProduct;}// 计算向量的模public static double vectorMagnitude(double[] vector) {double magnitude = 0;for (double component : vector) {magnitude += Math.pow(component, 2);}return Math.sqrt(magnitude);}// 计算余弦相似度public static double cosineSimilarity(double[] vectorA, double[] vectorB) {double dotProduct = dotProduct(vectorA, vectorB);double magnitudeA = vectorMagnitude(vectorA);double magnitudeB = vectorMagnitude(vectorB);if (magnitudeA == 0 || magnitudeB == 0) {return 0; // 避免除以零} else {return dotProduct / (magnitudeA * magnitudeB);}}public static void main(String[] args) {// 示例向量double[] vectorA = {1, 2, 3};double[] vectorB = {3, 2, 1};// 计算余弦相似度double similarity = cosineSimilarity(vectorA, vectorB);System.out.println("Cosine Similarity: " + similarity);}
}

向量数据库

对于向量模型生成出来的向量,我们可以持久化到向量数据库,并且能利用向量数据库来计算两个向量之间的相似度,或者根据一个向量查找跟这个向量最相似的向量。

在LangChain4j中,EmbeddingStore表示向量数据库,它有20个实现类:

  1. AstraDbEmbeddingStore
  2. AzureAiSearchEmbeddingStore
  3. CassandraEmbeddingStore
  4. ChromaEmbeddingStore
  5. ElasticsearchEmbeddingStore
  6. InMemoryEmbeddingStore
  7. InfinispanEmbeddingStore
  8. MemoryIdEmbeddingStore
  9. MilvusEmbeddingStore
  10. MinimalEmbeddingStore
  11. MongoDbEmbeddingStore
  12. Neo4jEmbeddingStore
  13. OpenSearchEmbeddingStore
  14. PgVectorEmbeddingStore
  15. PineconeEmbeddingStore
  16. QdrantEmbeddingStore
  17. RedisEmbeddingStore
  18. VearchEmbeddingStore
  19. VespaEmbeddingStore
  20. WeaviateEmbeddingStore

其中有我们熟悉的几个数据库都可以用来存储向量,比如Elasticsearch、MongoDb、Neo4j、Pg、Redis。

那么我们使用Redis来演示对于向量的增删查改,首先添加依赖:

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-redis</artifactId><version>${langchain4j.version}</version>
</dependency>

然后需要注意的是,普通的Redis是不支持向量存储和查询的,需要额外的redisearch模块,我这边是直接使用docker来运行一个带有redisearch模块的redis容器的,命令为:

docker run -p 6379:6379 redis/redis-stack-server:latest

注意端口6379不要和你现有的Redis冲突了。

然后就可以使用以下代码把向量存到redis中了:

RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder().host("127.0.0.1").port(6379).dimension(1536).build();// 生成向量
Response<Embedding> embed = embeddingModel.embed("我是司马懿");// 存储向量
embeddingStore.add(embed.content());

dimension表示要存储的向量的维度,所以为1536,如果你不是使用OpenAiEmbeddingModel得到的向量,那么维度可能会不一样。

可以使用以下命令来清空:

redis-cli FT.DROPINDEX embedding-index DD

匹配向量

在上面我们存储了"我是司马懿"的向量到Redis,接下来我们来使用“我的名字叫司马懿”来查找,看能不能查找到“我是司马懿”,最好新建一个类,新增和查询分为两个类比较方便:

package com.timi;import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.store.embedding.EmbeddingMatch;
import dev.langchain4j.store.embedding.redis.RedisEmbeddingStore;import java.util.List;public class _05_Vector_Search {public static void main(String[] args) {OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder().host("127.0.0.1").port(6379).dimension(1536).build();// 生成向量Response<Embedding> embed = embeddingModel.embed("我的名字叫司马懿");List<EmbeddingMatch<TextSegment>> result = embeddingStore.findRelevant(embed.content(), 4);for (EmbeddingMatch<TextSegment> embeddingMatch : result) {System.out.println(embeddingMatch.score());}}
}

代码执行结果为:

0.9765566289425

这就是"我是司马懿"和“我的名字叫司马懿”两句话之间的语义相似度分数,假如我们换成“今天天气很好”,得到的分数为

0.8937962949275

看上去似乎分数差别不大,但是要注意小数位其实是很多的,也就是这个分数精度是比较高的,所以两个分数还是有一定距离的。

我们不妨再来演示一种场景:

// 生成向量
TextSegment textSegment1 = TextSegment.textSegment("客服电话是400-8558558");
TextSegment textSegment2 = TextSegment.textSegment("客服工作时间是周一到周五");
TextSegment textSegment3 = TextSegment.textSegment("客服投诉电话是400-8668668");
Response<Embedding> embed1 = embeddingModel.embed(textSegment1);
Response<Embedding> embed2 = embeddingModel.embed(textSegment2);
Response<Embedding> embed3 = embeddingModel.embed(textSegment3);// 存储向量
embeddingStore.add(embed1.content(), textSegment1);
embeddingStore.add(embed2.content(), textSegment2);
embeddingStore.add(embed3.content(), textSegment3);

我往向量数据库中添加了三条客户相关的知识,并且通过TextSegment把原始文本也存入了Redis,相当于Redis中现在存储了三条原始文本以及对应的向量,然后我们来查询:

// 生成向量
Response<Embedding> embed = embeddingModel.embed("客服电话多少");// 查询
List<EmbeddingMatch<TextSegment>> result = embeddingStore.findRelevant(embed.content(), 5);
for (EmbeddingMatch<TextSegment> embeddingMatch : result) {System.out.println(embeddingMatch.embedded().text() + ",分数为:" + embeddingMatch.score());
}

代码执行结果为:

客服电话是400-8558558,分数为:0.9529553055763
客服投诉电话是400-8668668,分数为:0.9520588517189
客服工作时间是周一到周五,分数为:0.9305278658864999

从这就更容易看出向量的好处,能够基于向量快速的得到和文本相似的文本,这样就能非常适合用来做RAG,也就是检索增强生成。

本节总结

本节我们学习了什么是文本向量化、向量数据库、以及文本相似度等概念,下一节我们将通过完成一个智能客服系统来掌握向量等技术在RAG中的使用,以及掌握到底什么是RAG。

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

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

相关文章

统计信号处理基础 习题解答11-1

题目 观测到的数据具有PDF 在μ给定的条件下&#xff0c;是相互独立的。均值具有先验PDF&#xff1a; 求μ的 MMSE 和 MAP 估计量。另外&#xff0c;当和时将发生什么情况? 解答 和两者都是独立高斯分布&#xff0c;与例题10.1一致&#xff0c;直接套用&#xff08;10.11&am…

Nvidia Jetson/RK3588+AI双目立体相机,适合各种割草机器人、扫地机器人、AGV等应用

双目立体视觉是基于视差原理&#xff0c;依据成像设备从不同位置获取的被测物体的图像&#xff0c;匹配对应点的位置偏移&#xff0c;得到视差数据&#xff0c;进而计算物体的空间三维信息。为您带来高图像质量的双目立体相机&#xff0c;具有高分辨率、低功耗、远距离等优点&a…

ubuntu丢失网络/网卡的一种原因解决方案

现象 开机进入ubuntu后发现没有网络&#xff0c;无论是在桌面顶部状态栏的快捷键 还是 系统设置中&#xff0c;都没有”有线网“和”无线网“的选项&#xff0c;”代理“的选项是有的使用数据线连接电脑和手机&#xff0c;手机开启”通过usb共享网络“&#xff0c;还是没有任何…

【python】scikit-learn安装失败No matching distribution found for scikit-learn

报错如下&#xff1a; (yolo) xiaoxinxiaoxin:~/Documents/PaDiM-Anomaly-Detection-Localization-master-main$ pip install scikit-learn ERROR: Could not find a version that satisfies the requirement scikit-learn (from versions: none) ERROR: No matching distribu…

Nginx 1.26.1最新版部署笔记

Nginx是一个高性能的 HTTP 和反向代理服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。 以下是 Nginx 的一些核心功能和特点&#xff1a; 高性能的 Web 服务器&#xff1a; Nginx 被设计为处理高并发连接&#xff0c;具有非常高的性能和稳定性。反向代理&#xff1a; …

Text2SQL中反思纠错的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

二叉树之深度优先((Depth-First Search, DFS)

产生背景 深度优先搜索(Depth-First Search, DFS)算法的产生背景主要有以下几个方面: 图论研究 图论是计算机科学和数学中一个重要的分支,涉及对图形结构的分析和研究。早期的图论研究者,如欧拉和 Tarjan,就提出了一些基于深度优先的图遍历策略。这些工作奠定了深度优先搜索…

分享3个AI工具-包括自学AI文档和AI搜索和智能体

文章目录 通往AGI之路-自学神器秘塔AI扣子 通往AGI之路-自学神器 这是是一个有关AI知识的开源文档。 但是&#xff0c;我认为这是小白学习AI的最强王者&#xff0c;每一个想学习AI、想使用AI的人都可以把它设为首页&#xff0c;从它开始。 飞书文档&#xff1a;通往AGI之路 …

Python 面试【★★★★】

欢迎莅临我的博客 &#x1f49d;&#x1f49d;&#x1f49d;&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

golang跨平台GUI框架fyne介绍与使用详解,开放案例

golang跨平台GUI框架fyne介绍与使用详解 Fyne 是一个使用 Go 编写的易于使用的 UI 工具包和应用程序 API。 它旨在构建使用单一代码库在桌面和移动设备上运行的应用程序。 通过批量调用身份证实名和三网手机实名和银行卡核验等接口&#xff0c;完成fyne框架的基本使用介绍 主要…

CVPR 2024 | 双手协作双物体的数据集TACO:引领可泛化手物交互的新方向

论文题目&#xff1a; TACO: Benchmarking Generalizable Bimanual Tool-ACtion-Object Understanding 论文链接&#xff1a; https://arxiv.org/pdf/2401.08399.pdf 项目主页&#xff1a; https://taco2024.github.io/ 视频链接&#xff1a; https://www.youtube.com/watch…

完全离线的本地问答模型LocalGPT如何实现无公网IP远程连接提问

文章目录 前言环境准备1. localGPT部署2. 启动和使用3. 安装cpolar 内网穿透4. 创建公网地址5. 公网地址访问6. 固定公网地址 前言 本文主要介绍如何本地部署LocalGPT并实现远程访问&#xff0c;由于localGPT只能通过本地局域网IP地址端口号的形式访问&#xff0c;实现远程访问…

技术驱动的音乐变革:AI带来的产业重塑

&#x1f4d1;引言 近一个月来&#xff0c;随着几款音乐大模型的轮番上线&#xff0c;AI在音乐产业的角色迅速扩大。这些模型不仅将音乐创作的门槛降至前所未有的低点&#xff0c;还引发了一场关于AI是否会彻底颠覆音乐行业的激烈讨论。从初期的兴奋到现在的理性审视&#xff0…

石家庄高校大学智能制造实验室数字孪生可视化系统平台项目验收

智能制造作为未来制造业的发展方向&#xff0c;已成为各国竞相发展的重点领域。石家庄高校大学智能制造实验室积极响应国家发展战略&#xff0c;结合自身优势&#xff0c;决定引进数字孪生技术&#xff0c;构建一个集教学、科研、生产于一体的可视化系统平台。 数字孪生可视化…

Trie字符串统计

Trie字符串统计 维护一个字符串集合&#xff0c;支持两种操作&#xff1a; I x 向集合中插入一个字符串 x&#xff1b;Q x 询问一个字符串在集合中出现了多少次。 共有 N个操作&#xff0c;所有输入的字符串总长度不超过 105&#xff0c;字符串仅包含小写英文字母。 输入格式…

launch 中可执行文件 type

<node pkg"waypoint_generator" name"waypoint_generator" type"waypoint_generator_ms" output"screen">pkg 指定了包名&#xff0c;它告诉 ROS 应该在哪个包中查找可执行文件。 name 指定了节点名&#xff0c;这个名称在 ROS …

Java中的Path类使用详解及最佳实践

Java中的Path类使用详解及最佳实践 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨Java中的Path类&#xff0c;这是Java标准库中用于操作文件…

免费内网穿透、配置超级简单

巴比达内网穿透 曾经那些所谓的内网穿透服务&#xff0c;给我带来的只有无尽的烦恼。有的像&#xff0c;毫无规律地每天更改固定访问地址和端口。有一次&#xff0c;我正在进行一个重要的项目投标&#xff0c;需要及时与团队成员共享文件和沟通。可就在关键时刻&#xff0c;网络…

endswith()方法——是否以指定子字符串结尾

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 endswith()方法用于检索字符串是否以指定子字符串结尾。如果是则返回True&#xff0c;否则返回False。endswith()方法的语法格式如下&…

启智畅想:AI集装箱箱号识别系统,解决方案提供商

AI集装箱箱号识别系统 当前,智能卡口管理行业正处于快速发展的阶段。随着物联网、大数据、人工智能等技术的不断进步,智能卡口管理系统已经能够实现对集装箱运输的全程跟踪、监控和管理,大大提高了管理效率和安全性。然而,市场上现有的智能卡口管理系统仍然存在一些痛点问题,如…