上一节咱们顺利从本地读取了文件内容,并且可以使用transform工具对文件进行内容处理,下面咱们继续看看如何将文件进行向量化,并且存储到向量数据库中。
Embedding 知识扩展
Embeddings是一种将高维数据映射到低维空间的技术,它能够捕捉数据之间的语义关系和相似性,使得模型能够更好地理解和处理这些数据。
在自然语言处理(NLP)中,Embeddings通常用于将文本转换为连续向量,基于分布式假设捕捉语义信息。
例如,Word2Vec、GloVe和BERT等模型通过学习上下文和语义信息,将单词映射到连续的向量空间。
Embeddings在多个领域都有广泛的应用,包括但不限于:
- 自然语言处理:Embeddings可以帮助计算机更好地理解单词之间的语义关系,这对于机器翻译、情感分析等任务非常有益.
- 图像处理:在计算机视觉领域,图像Embeddings被用于图像分类、相似性匹配等任务。通过将图像映射到低维向量空间,我们可以比较和分析图像之间的相似性.
- 推荐系统:在推荐系统中,Embeddings被用于表示用户和商品。这种表示能够捕捉用户和商品之间的潜在关系,从而提高推荐的准确性.
简单来说,embedding向量就是一个N维的实值向量,它将输入的数据表示成一个连续的数值空间中的点。
如图 通俗易懂的描述:
嵌入就相当于给文本穿上了“数字化”的外衣
,目的是让机器更好的理解和处理。Embedding 起源于 Word Embedding,从横向发展上看:
Word Embedding -> Item Embedding -> Entity Embedding -> Graph Embedding -> Position Embedding -> Segment Embedding
。从纵向发展看:
- 由静态的Word Embedding(如Word2Vec、GloVe和FastText)
- 动态预训练模型(如ELMo、BERT、GPT、GPT-2、GPT-3、ALBERT、XLNet等)。
大型语言模型可以生成上下文相关的 embedding 表示,可以更好地捕捉单词的语义和上下文信息。
Spring AI是如何支持Embedding
在 Spring AI中通过EmbeddingModel
来实现,EmbeddingModel
接口旨在简化AI和机器学习中的嵌入模型的集成。
其主要作用是将文本转换为数值向量,即嵌入。这些嵌入对于执行语义分析和文本分类等任务至关重要。EmbeddingModel
接口的设计主要追求两个目标:
- 可移植性**:**确保在不同嵌入模型间的高度适应性。开发者可以轻松地在各种嵌入技术或模型之间进行切换,只需进行极小的代码更改。这与Spring框架强调的模块化和可互换性原则一致。
- 简洁性:
EmbeddingModel
使得文本到嵌入的转换过程更为简单。通过提供embed(String text)
和embed(Document document)
等直观方法,它减少了处理原始文本和嵌入算法的复杂性。这样的设计让开发者,尤其是AI领域的新手,能够更容易地在他们的应用中使用嵌入技术,无需深入了解其背后的工作原理。
实现设计
EmbeddingModel
API基于Spring AI库中的通用AI模型API构建。因此,EmbeddingModel
接口继承了Model接口,后者提供了一系列与AI模型交互的标准方法。EmbeddingRequest
和EmbeddingResponse
类分别继承自ModelRequest
和ModelResponse
类,用于封装嵌入模型的输入和输出数据。
嵌入API随后被用于实现特定嵌入模型的高级组件,例如OpenAI
、Titan
、Azure OpenAI
、Ollama
等。
下图展示了Embedding API与Spring AI模型API以及嵌入模型之间的关系:
现在Spring AI 已经支持了很多Embedding模型,如下:
- Spring AI OpenAI Embeddings
- Spring AI Azure OpenAI Embeddings
- Spring AI Ollama Embeddings
- Spring AI Transformers (ONNX) Embeddings
- Spring AI PostgresML Embeddings
- Spring AI Bedrock Cohere Embeddings
- Spring AI Bedrock Titan Embeddings
- Spring AI VertexAI PaLM2 Embeddings
- Spring AI Mistral AI Embeddings
文本向量化(使用Ollama Embedding)
要计算向量数据库的嵌入向量,需要选择与 AI 模型匹配的嵌入模型,比如,openai的模型text-embedding-ada-002
,咱们这次使用的Embedding是llama3.1
模型进行数据向量化。
通过Ollama,可以在本地运行多种大型语言模型(LLMs),并利用这些模型生成文本嵌入。Spring AI通过OllamaEmbeddingModel
支持Ollama文本嵌入功能。
嵌入实质上是由浮点数构成的向量。两个向量之间的距离可以反映它们之间的相关性:
- 距离越近,相关性越高;
- 距离越远,相关性越低。
以下案例通过llama3.1模型实现。
项目embedding模型配置信息
spring:
ai:
ollama:
base-url: http://localhost:11434
embedding:
model: llama3.1
将文本进行向量化实现
@RestController
@AllArgsConstructor
@RequestMapping("/embedding")
public class EmbeddingController {
private final OllamaEmbeddingModel ollamaEmbeddingModel;
/**
* 将文本内容向量化
*
* @param message 文本
* @return 向量化数据
*/
@GetMapping("/text")
public Map<String, ?> embed(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
EmbeddingResponse embeddingResponse =
ollamaEmbeddingModel.embedForResponse(List.of(message));
return Map.of("embedding", embeddingResponse);
}
}
关于向量数据库
向量数据库技术是一种专门用于存储和处理向量数据的数据库系统,它以向量为基本数据类型,旨在处理和存储大规模向量数据。
向量数据库和传统关系型数据库在数据模型上的主要区别体现在数据的表示和组织方式上。
- 向量数据库将数据表示为多维空间中的矢量,强调数据之间的相似性和距离关系,这种模型适合处理大规模的非结构化数据,侧重于数据的相似性搜索和模式匹配。
- 传统关系型数据库基于表格形式,数据以行和列的结构组织,通过明确的关系和约束来关联不同的表,更适用于结构化数据,重点在于精确的数据查询、更新和事务处理。
向量数据库由于其在处理高维数据和相似性搜索方面的优势,常被用于图像识别、自然语言处理、推荐系统等领域,而关系型数据库则广泛应用于企业资源规划(ERP)、客户关系管理(CRM)等具有结构化数据和事务处理需求的系统。
文本向量存储(基于Chroma DB)
SpringAI支持了很多向量数据接入,如下:
- Apache Cassandra - Apache Cassandra 矢量存储。
- Chroma Vector Store - Chroma 矢量存储。
- Elasticsearch Vector Store - Elasticsearch 矢量存储。
- Milvus Vector Store - Milvus 矢量存储。
- MongoDB Atlas Vector Store -MongoDB Atlas矢量存储。
- Neo4j Vector Store - Neo4j 矢量存储。
- PgVector Store - PostgreSQL/PGVector 向量存储。
- Qdrant Vector Store - Qdrant 矢量存储。
- Redis Vector Store - Redis 矢量存储。
- SimpleVectorStore -持久向量存储的简单实现,适用于教育目的.
- 等等。
本次案例使用 Chroma VectorStore 来存储文档嵌入并执行相似性搜索。
Chroma 是开源嵌入数据库。它为您提供了用于存储文档嵌入、内容和元数据以及搜索这些嵌入(包括元数据过滤)的工具。
使用docker安装Chroma
# 加载镜像
docker pull chromadb/chroma
# 启动container
docker run -d --rm --name chromadb -p 8000:8000 chromadb/chroma:latest
Chroma项目配置
spring:
ai:
ollama:
chat:
options:
model: llama3.1
temperature: 0.5
base-url: http://localhost:11434
embedding:
model: llama3.1
vectorstore:
chroma:
client:
host: http://localhost
port: 8000
collection-name: "jingyu-spring-ai"
initialize-schema: true # 如果chromadb中没有collection-name,那么此处需要设置为true,否者会404报错
pom配置
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId>
</dependency>
向量化的代码很简单如下:
@RestController
@RequestMapping("/vector")
@AllArgsConstructor
public class VectorController {
private final VectorStore vectorStore;
/**
* 将向量化数据存储到向量数据库
*
* @param message 文本内容
* @return 向量化存储结果
*/
@GetMapping("/msg/vector")
public Boolean embedAndOptions(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
// 将文本内容向量化到chromaDB中
vectorStore.add(List.of(new Document(message)));
return Boolean.TRUE;
}
}
第一次使用向量化存储的API时,很诧异,为啥这么简单,竟然向量化也不需要配置,直接add()
就可以实现了。
打开其源码才发现,大善人在add()
都已经完成了,咱们来一起看一下ChromaVectorStore
的实现:
public class ChromaVectorStore implements VectorStore, InitializingBean {
// 其他代码省略
// 构造方法中自动注入了一个Embedding模型
public ChromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi, String collectionName, boolean initializeSchema) {
this.embeddingModel = embeddingModel;
this.chromaApi = chromaApi;
this.collectionName = collectionName;
this.initializeSchema = initializeSchema;
this.filterExpressionConverter = new ChromaFilterExpressionConverter();
}
public void add(List<Document> documents) {
Assert.notNull(documents, "Documents must not be null");
if (!CollectionUtils.isEmpty(documents)) {
List<String> ids = new ArrayList();
List<Map<String, Object>> metadatas = new ArrayList();
List<String> contents = new ArrayList();
List<float[]> embeddings = new ArrayList();
Iterator var6 = documents.iterator();
while(var6.hasNext()) {
Document document = (Document)var6.next();
ids.add(document.getId());
metadatas.add(document.getMetadata());
contents.add(document.getContent());
document.setEmbedding(this.embeddingModel.embed(document));
embeddings.add(JsonUtils.toFloatArray(document.getEmbedding()));
}
this.chromaApi.upsertEmbeddings(this.collectionId, new ChromaApi.AddEmbeddingsRequest(ids, embeddings, metadatas, contents));
}
}
}
向量检索
存储数据只是咱们得第一步,目的还是为了咱们能够更好的根据语义去检索到咱们得数据。
向量数据库的检索也挺简单,咱们可以只需要设置部分参数,底层会自动帮助我们做问题的Embedding,然后再到向量数据库中进行匹配查询。
向量化检索
@GetMapping("/retrieval")
public Object queryVector(@RequestParam("query") String query) {
SearchRequest searchRequest = SearchRequest.defaults()
.withQuery(query)
.withTopK(5)
.withSimilarityThreshold(SearchRequest.SIMILARITY_THRESHOLD_ACCEPT_ALL);
// .withFilterExpression();
return vectorStore.similaritySearch(searchRequest);
}
这里需要注意的有两点:
- SearchRequest中设置的参数:
query
:用于嵌入相似性比较的文本。topK
:指定要返回的相似文档的最大数量。这通常被称为“前 K”搜索,或“K 最近邻”(KNN)。similarityThreshold
:相似度检索阈值。阈值值为0.0表示接受任何相似度或禁用相似度阈值过滤。阈值值为1.0表示需要完全匹配。例如,默认情况下,如果将阈值设置为 0.75,则仅返回相似度高于此值的文档。filterExpression
:基于 ANTLR4 的外部 DSL,接受过滤器表达式作为字符串。例如,对于 country、year 和 等元数据键,可以使用如下表达式:isActivecountry == 'UK' && year >= 2020 && isActive == true.
- Chroma查询过滤语法:🧪 Chromadb Querying a Collection
以上就是本节所有的内容了,下面我将继续研究一下,在数据有限的情况下,如何通过Function Calling来获取外部数据。