基于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.mfbz.cn/a/753361.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TIOBE 6月榜单出炉!编程语言地位大洗牌,谁才是王?

C历史上首次超越C&#xff01;&#xff01;&#xff01; TIOBE 公布了 2024 年 6 月编程语言的排行榜&#xff1a;https://www.tiobe.com/tiobe-index/ 排行榜 以下列出的语言代表了第51至第100名。由于它们之间的差异相对较小&#xff0c;编程语言仅以字母顺序列出。 ABC, A…

ubuntu16.04上搭建qt开发环境

安装qt 下载qt在linux下的安装包qt-opensource-linux-x64-5.8.0.run&#xff1b;将安装包拷贝到ubuntu16.04上&#xff1b;执行如下命令进行安装&#xff0c;注意&#xff1a;安装前先断开虚拟机的网络&#xff0c;这样就避免了注册账户的操作 基本上一路按“next”键&#xf…

使用青否数字人直播软件有哪些优势?

使用青否数字人主播主要具有以下优势&#xff1a; 1、降低直播门槛 在垂直程度较高、专业度更强的行业&#xff0c;面对相关品牌们“专业主播难培养”的问题。数字人主播的学习技能和灵活优势尽显。通过数字人直播可以借助知识库配置与AI能力&#xff0c;快速获得技术性知识&am…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 螺旋矩阵填数(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

c语言--指针

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理c语言中指针的相关知识点。 指针概念 指针存储的就是数据的地址。 直观理解: 李华家是北洋路130号1单元101 用变量处理数据: 我们去李华家拿数据。 用指针处理数据: 我们去北洋路130号1单元101拿数据…

石墨舟氮气柜的特点和使用要求介绍

石墨舟是一种在半导体、太阳能光伏等高科技产业中广泛使用的专用工具&#xff0c;主要由高纯度石墨材料制成。它的形状通常像一只船&#xff0c;因此得名“石墨舟”。石墨舟主要用于承载硅片或其他基板材料通过各种高温处理过程&#xff0c;是制造半导体器件和太阳能电池片的关…

二叉树的方法

目录 一、二叉树的定义 ​编辑 二、二叉树的创建 三、二叉树的遍历 1、前序遍历 2、中序遍历 3、后序遍历 4、层序遍历 四、二叉树遍历方法的使用 五、二叉树的操作 1、节点的个数 2、叶子节点的个数 3、第k层节点的个数 4、二叉树的高度 5、检查值为value的元素…

「2024抢先看」6款免费的ai变声器,助你开麦就变声

你是否也曾想模仿电视剧中的明星说话&#xff0c;或者想像泰勒一样有着独特的嗓音呢&#xff1f;通过免费版ai变声器&#xff0c;你可以轻松实现实时变声&#xff0c;将你的声音转换为专业且动听的声音&#xff0c;让身边的朋友对你刮目相看。在本文中&#xff0c;小编将分享20…

服务器日志事件ID4107:从自动更新 cab 中提取第三方的根目录列表失败,错误为: 已处理证书链,但是在不受信任提供程序信任的根证书中终止。

在查看Windows系统日志时&#xff0c;你是否有遇到过事件ID4107错误&#xff0c;来源CAPI2&#xff0c;详细信息在 http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/authrootstl.cab 从自动更新 cab 中提取第三方的根目录列表失败&#xff0c;…

简单的本地局域网的前后端接口联调

由于项目被赶进度了&#xff0c;急于前后端联调接口&#xff0c;但是我又没钱买服务器&#xff08;主要我也不会部署&#xff09;&#xff0c;所以我这里就紧急找一个后端的大神朋友请教了一下&#xff1a;苏泽SuZe-CSDN博客 提示&#xff1a;这里不讲后端怎么写接口、前端怎么…

C#——里氏转换详情

里氏转换 里氏转换就是派生类的对象赋值给父类对象&#xff0c;反之则不行 实例 : 先创键一个类然后继承 调用

双路视频同屏显示(拼接)-基于野火Zynq7020开发板

前情提要 米联客FDMA驱动OV5640摄像头—基于野火Zynq7020开发板 本文在此基础上&#xff0c;实现了双路视频拼接。将ov5640输出的1024600的图像数据缩放为512600&#xff0c;分两路写入ddr3&#xff0c;并且显示在1024*600的RGB屏幕中。 纯FPGA也可以按此方法实现。 总体BLOC…

【C++ 初阶路】--- 类和对象(末)

目录 一、const成员1.1 取地址及const取地址操作符重载 二、再谈构造函数2.1 构造函数体赋值2.2 初始化列表2.3 explicit关键字 三、static成员3.1 概念3.2 特性 四、友元4.1 友元函数4.2 友元类 五、内部类六、匿名对象 一、const成员 将const修饰的“成员函数”称之为const成…

Qt creator实现一个简单计算器

目录 1 界面设计 2 思路简介 3 代码 目录 1 界面设计 ​2 思路简介 3 代码 3.1 widget.h 3.2 widget.c 4 完整代码 在这里主要记载了如何使用Qt creator完成一个计算器的功能。该计算器可以实现正常的加减乘除以及括号操作&#xff0c;能实现简单的计算器功能。 1 界…

北京站圆满结束!MongoDB Developer Day上海站,周六见!

上周六 MongoDB Developer Day首站北京站 80位开发者与MongoDB一起度过了充实的一天 专题讲座➕动手实操➕专家面对面交流 从数据建模、进阶查询技巧 到Atlas搜索与向量搜索 让参会伙伴们直呼“满满的技术干货&#xff01;” 全体参会者与工作人员合影 MongoDB Developer …

【unity笔记】五、UI面板TextMeshPro 添加中文字体

Unity 中 TextMeshPro不支持中文字体&#xff0c;下面为解决方法&#xff1a; 准备字体文件&#xff0c;从Windows系统文件的Fonts文件夹里拖一个.ttf文件&#xff08;C盘 > Windows > Fonts &#xff09; 准备字库文件,新建一个文本文件&#xff0c;命名为“字库”&…

Vue组件化、单文件组件以及使用vue-cli(脚手架)

文章目录 1.Vue组件化1.1 什么是组件1.2 组件的使用1.3 组件的名字1.4 嵌套组件 2.单文件组件2.1 vue 组件组成结构2.1.1 template -> 组件的模板结构2.1.2 组件的 script 节点2.1.3 组件的 style 节点 2.2 Vue组件的使用步骤2.2.1 组件之间的父子关系2.2.2 使用组件的三个步…

低代码+定制:优化项目管理的新方案

引言 在当今快速变化的商业环境中&#xff0c;企业需要更加灵活、高效的项目管理工具。低代码平台作为一种新的开发方式&#xff0c;因其能够快速构建应用程序而受到广泛关注。与此同时&#xff0c;软件定制开发仍然是满足特定复杂需求的重要手段。在项目管理中&#xff0c;低代…

建筑可视化中使用云渲染的几大理由

在建筑行业中&#xff0c;可视化技术已成为不可或缺的一部分。无论是设计方案的展示、施工进度的模拟&#xff0c;还是最终效果的呈现&#xff0c;建筑可视化都发挥着至关重要的作用。 建筑可视化是指通过计算机技术和图形学算法&#xff0c;将建筑设计、规划和施工过程中的数据…

社交风潮塑造者:探索用户在Facebook的影响力

在当今数字化社会中&#xff0c;Facebook不仅是人们社交互动的主要平台&#xff0c;更是塑造社交风潮和文化趋势的重要力量。本文将从另一个角度深入探讨用户在Facebook上的影响力&#xff0c;探索其如何通过个人行为和互动&#xff0c;影响和改变社会的各个方面。 个人表达和内…