Embedding介绍
Embedding是向量的意思,向量可以理解为平面坐标中的一个坐标点(x,y),在编程领域,一个二维向量就是一个大小为float类型的数组。也可以用三维坐标系中的向量表示一个空间中的点。在机器学习中,向量通常用于表示数据的特征。
向量分数越高,代表位置越接近,匹配的数据越相似。
文本向量化
而文本向量化是指,利用大模型可以吧一个字,一个词或一段话映射为一个多维向量,比如我们可以直接在LangChain4j中调用向量模型来对一句话进行向量化。
案例代码:
public class EmbeddingDemo{ 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("你好,我叫gorgor"); System.out.println(embed.content().toString()); System.out.println(embed.content().vector().length); } }
代码执行结果为:
Embedding { vector = [-0.019351093, -0.02137422...] } 1536
从结果可以知道“你好,我叫gorgor”这句话经过大模型向量化之后,得到一个长度为1536的float数组。注意,1536是固定的,不会随着句子长度而变化。
那么我们通过这种向量模型得到一句话对应的向量有什么作用呢?非常有用,因为我们可以基于向量来判断两句话之间的相似度,进而可以实现超越elasticsearch的高级搜索。
向量相似度
我们可以使用余弦相似度来计算向量的相似度,余弦相似度是一种用于衡量向量之间相似度的指标,可以用于文本向量之间的相似度,在计算机中用来判断文本之间的距离。
例如:
"机器学习"表示为 [1,2,3]
"深度学习"表示为[2,3,3]
"英雄联盟"表示为[9,1,3]
示例代码(余弦相似度):
package com.gorgor.langchain4j.embedding; 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 = {2,3,3}; //英雄联盟 double[] vectorC = {9,1,3}; // 计算余弦相似度 //机器学习”与“深度学习”的距离: double similarityAB = cosineSimilarity(vectorA, vectorB); System.out.println("Cosine SimilarityAB: " + similarityAB); //"机器学习”与“英雄联盟“的距离": double similarityAC = cosineSimilarity(vectorA, vectorC); System.out.println("Cosine SimilarityAB: " + similarityAC); } }
代码执行结果:
Cosine SimilarityAB: 0.9686648999069225 Cosine SimilarityAB: 0.5603318146805258
结论:“机器学习”与“深度学习”两个文本之间的余弦相似度更高,表示它们在语义上更相似。
文本向量算法
文本向量算法是指将文本数据转化为向量表示的具体算法,通常包括以下几个步骤:
- 分词:将文本划分成一个个单词或短语。
- 构建词汇表:将分词后的单词或短语建立词汇表,并为每个单词或短语赋予一个唯一的编号。
- 计算词嵌入:使用预训练的模型或自行训练的模型,将每个单词或短语映射到向量空间中。
- 计算文本嵌入:将文本中每个单词或短语的向量表示取平均或加权平均,得到整个文本的向量表示。
常见的文本向量算法包括 Word2Vec、GloVe、FastText 等。这些算法通过预训练或自行训练的方式,将单词或短语映射到低维向量空间中,从而能够在计算机中方便地处理文本数据。当然这中间最关键的就是向量模型,因为向量是它生成的,向量模型也是经过大量机器学习训练之后产生的,向量模型效果越好,就表示它对于自然语言理解的程度越好,同时也就表示它生成出来的向量越准确,越能反映出语义的相似度。
文本向量用途
文本嵌入用于测量文本字符串的相关性,通常用于:
- 搜索(结果按与查询字符串的相关性排序)
- 聚类(其中文本字符串按相似性分组)
- 推荐(推荐具有相关文本字符串的项目)
- 异常检测(识别出相关性很小的异常值)
- 多样性测量(分析相似性分布)
- 分类(其中文本字符串按其最相似的标签分类)
向量数据库
LangChain4j支持的向量数据库:
向量数据库名称 | 保存 | 过滤 | 删除 |
---|---|---|---|
In-memory | ✅ | ✅ | ✅ |
Astra DB | ✅ | ||
Azure AI Search | ✅ | ✅ | |
Azure CosmosDB Mongo vCore | ✅ | ||
Azure CosmosDB NoSQL | ✅ | ||
Cassandra | ✅ | ||
Chroma | ✅ | ||
Elasticsearch | ✅ | ✅ | ✅ |
Infinispan | ✅ | ||
Milvus | ✅ | ✅ | ✅ |
MongoDB Atlas | ✅ | ||
Neo4j | |||
OpenSearch | ✅ | ||
PGVector | ✅ | ✅ | ✅ |
Pinecone | |||
Qdrant | ✅ | ||
Redis | ✅ | ||
Vearch | ✅ | ||
Vespa | |||
Weaviate | ✅ | ✅ |
EmbeddingStore表示向量数据库,它有20个实现类:
- AstraDbEmbeddingStore
- AzureAiSearchEmbeddingStore
- CassandraEmbeddingStore
- ChromaEmbeddingStore
- ElasticsearchEmbeddingStore
- InMemoryEmbeddingStore
- InfinispanEmbeddingStore
- MemoryIdEmbeddingStore
- MilvusEmbeddingStore
- MinimalEmbeddingStore
- MongoDbEmbeddingStore
- Neo4jEmbeddingStore
- OpenSearchEmbeddingStore
- PgVectorEmbeddingStore
- PineconeEmbeddingStore
- QdrantEmbeddingStore
- RedisEmbeddingStore
- VearchEmbeddingStore
- VespaEmbeddingStore
- WeaviateEmbeddingStore
其中我们熟悉的有Elasticsearch、MongoDb、Pg、Redis,令人惊喜的是MySQL 9.0也开始支持面向Ai的向量数据库了。
案例实现
下面案例就以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中了:
public class EmbeddingDemo { 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(); TextSegment textSegment1 = TextSegment.textSegment("机器学习"); TextSegment textSegment2 = TextSegment.textSegment("深度学习"); TextSegment textSegment3 = TextSegment.textSegment("英雄联盟"); // 生成向量 Response<Embedding> embed1 = embeddingModel.embed("机器学习"); Response<Embedding> embed2 = embeddingModel.embed("深度学习"); Response<Embedding> embed3 = embeddingModel.embed("英雄联盟"); // 存储向量 embeddingStore.add(embed1.content(),textSegment1); embeddingStore.add(embed2.content(),textSegment2); embeddingStore.add(embed3.content(),textSegment3); // 生成向量 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()); } } }
代码执行结果为
深度学习,分数为:0.94541862607 机器学习,分数为:0.943170130253 英雄联盟,分数为:0.9012748003005
从这就更容易看出向量的好处,能够基于向量快速的得到和文本相似的文本,这样就能非常适合用来做RAG,也就是检索增强生成。