Embedding介绍
Embedding是向量的意思,向量可以理解为平面坐标中的一个坐标点(x,y),在编程领域,一个二维向量就是一个大小为float类型的数组。也可以用三维坐标系中的向量表示一个空间中的点。在机器学习中,向量通常用于表示数据的特征。
向量分数越高,代表位置越接近,匹配的数据越相似。
文本向量化
而文本向量化是指,利用大模型可以吧一个字,一个词或一段话映射为一个多维向量,比如我们可以直接在LangChain4j中调用向量模型来对一句话进行向量化。
案例代码:
java
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);
}
}
代码执行结果为:
bash
Embedding { vector = [-0.019351093, -0.02137422...] }
1536
从结果可以知道"你好,我叫gorgor"这句话经过大模型向量化之后,得到一个长度为1536的float数组。注意,1536是固定的,不会随着句子长度而变化。
那么我们通过这种向量模型得到一句话对应的向量有什么作用呢?非常有用,因为我们可以基于向量来判断两句话之间的相似度,进而可以实现超越elasticsearch的高级搜索。
向量相似度
我们可以使用余弦相似度来计算向量的相似度,余弦相似度是一种用于衡量向量之间相似度的指标,可以用于文本向量之间的相似度,在计算机中用来判断文本之间的距离。
例如:
"机器学习"表示为 [1,2,3]
"深度学习"表示为[2,3,3]
"英雄联盟"表示为[9,1,3]
示例代码(余弦相似度):
java
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);
}
}
代码执行结果:
java
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依赖
XML
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-redis</artifactId>
<version>${langchain4j.version}</version>
</dependency>
然后需要注意的是,普通的Redis是不支持向量存储和查询的,需要额外的redisearch模块,我这边是直接使用docker来运行一个带有redisearch模块的redis容器的,命令为:
java
docker run -p 6379:6379 redis/redis-stack-server:latest
注意端口6379不要和你现有的Redis冲突了。
然后就可以使用以下代码把向量存到redis中了:
java
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());
}
}
}
代码执行结果为
bash
深度学习,分数为:0.94541862607
机器学习,分数为:0.943170130253
英雄联盟,分数为:0.9012748003005
从这就更容易看出向量的好处,能够基于向量快速的得到和文本相似的文本,这样就能非常适合用来做RAG,也就是检索增强生成。