Langchian.js |Embedding & Vector Store👈| 数据向量化后这样储存😱
前言
书接上文 , 学习了分割多个文档对象 , 这一次要学习
- 如何将数据向量化 ? 😍
- 向量化的数据持久化储存 ? 😍
也就是说 ,下面这张图 ,要 over 了 , 🤡👈
Embedding
langchain.js 在文本处理领域 ,不仅提供我前面所学的文本分割与转换 , 也为文本的向量化提供了支持 , 这不禁让应用开发者尖叫 ~ , 所谓文本的嵌入 , 其机制就是 : 将复杂文本数据转换为具有固定维度的向量 , 在机器学习和检索任务中十分 nice ~
Embedding 就是嵌入 , 他是 Langchain.js 的一个核心组件
主要作用是 , 为各种文本嵌入模型交互而设计 , 为许多的模型提供统一的 、标准化的接口 ; 说到这里 , 我们可以思考 : 其实 langchain 框架本身就是为了提供“统一化 、标准化的接口”而生 , 它是 LLM 的上层应用框架 , 成为开发层面的老大 , 底层调用各类模型 , 我们开发者只需要熟悉固定的语法 , 痛苦都交给了 langchain 🤡
langchain 支持的嵌入式模型如下 :
Name | Description |
---|---|
Alibaba Tongyi | The AlibabaTongyiEmbeddings class uses the Alibaba Tongyi API to gene... |
Azure OpenAI | [Azure |
Baidu Qianfan | The BaiduQianfanEmbeddings class uses the Baidu Qianfan API to genera... |
Amazon Bedrock | Amazon Bedrock is a fully managed |
ByteDance Doubao | This will help you get started with ByteDanceDoubao [embedding |
Cloudflare Workers AI | This will help you get started with Cloudflare Workers AI [embedding |
Cohere | This will help you get started with CohereEmbeddings [embedding |
DeepInfra | The DeepInfraEmbeddings class utilizes the DeepInfra API to generate ... |
Fireworks | This will help you get started with FireworksEmbeddings [embedding |
Google Generative AI | This will help you get started with Google Generative AI [embedding |
Google Vertex AI | Google Vertex is a service that |
Gradient AI | The GradientEmbeddings class uses the Gradient AI API to generate emb... |
HuggingFace Inference | This Embeddings integration uses the HuggingFace Inference API to gen... |
IBM watsonx.ai | This will help you get started with IBM watsonx.ai [embedding |
Jina | The JinaEmbeddings class utilizes the Jina API to generate embeddings... |
Llama CPP | Only available on Node.js. |
Minimax | The MinimaxEmbeddings class uses the Minimax API to generate embeddin... |
MistralAI | This will help you get started with MistralAIEmbeddings [embedding |
Mixedbread AI | The MixedbreadAIEmbeddings class uses the Mixedbread AI API to genera... |
Nomic | The NomicEmbeddings class uses the Nomic AI API to generate embedding... |
Ollama | This will help you get started with Ollama [embedding |
OpenAI | This will help you get started with OpenAIEmbeddings [embedding |
Pinecone | This will help you get started with PineconeEmbeddings [embedding |
Prem AI | The PremEmbeddings class uses the Prem AI API to generate embeddings ... |
Tencent Hunyuan | The TencentHunyuanEmbeddings class uses the Tencent Hunyuan API to ge... |
TensorFlow | This Embeddings integration runs the embeddings entirely in your brow... |
TogetherAI | This will help you get started with TogetherAIEmbeddings [embedding |
HuggingFace Transformers | The TransformerEmbeddings class uses the Transformers.js package to g... |
Voyage AI | The VoyageEmbeddings class uses the Voyage AI REST API to generate em... |
ZhipuAI | The ZhipuAIEmbeddings class uses the ZhipuAI API to generate embeddin... |
参考自官网 :js.langchain.com/docs/integr…
这些模型支持嵌入式 , 即支持将文本向量化 ~
我将使用 openai 来演示 ,
- 首先加载 data 文件夹下的"少年中国说.txt"文件为 Document 对象
- 然后使用工具分割对象
- 使用嵌入式模型向量化第二步分割后的 chunk
import { load } from "dotenv";
import { OpenAIEmbeddings } from "@langchain/openai";
import { TextLoader } from "langchain/document_loaders/fs/text";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
const env = await load();
const process = {
env
}
// 1.
const loader = new TextLoader("data/少年中国说.txt");
const docs = await loader.load();
// 2.
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100, // 切片大小
chunkOverlap: 20,// 重叠部分
});
const splitDocs = await splitter.splitDocuments(docs);
// 3.
const embeddings = new OpenAIEmbeddings()
const splitDoc = splitDocs[0].pageContent
const res = await embeddings.embedQuery(splitDoc)
console.log(res)
在向量化之前 , 打印splitDocs , 输出如下:
使用OpenAIEmbeddings 嵌入式模型 , 对上述输出向量化之后 , 变成如下 :
上述代码使用嵌入式模型将文本变成了向量
会经历一下过程 :
-
预处理
分词:首先,文本需要被分割成更小的单元,如单词或子词(subword)。例如,中文文本通常会被切分成单个汉字或词语。
标准化:去除标点符号、转换为小写等操作,以确保一致性。 -
词汇表构建
模型会根据训练数据构建一个词汇表(vocabulary),其中每个词都对应一个唯一的索引。对于未出现在词汇表中的词,通常会有一个特殊的标记(如)来表示未知词。 -
词向量生成
静态词向量:早期的方法如Word2Vec、GloVe等会为每个词生成一个固定长度的向量。这些向量是通过无监督学习从大量文本中训练得到的,能够捕捉到词与词之间的语义关系。
动态词向量:现代模型如BERT、OpenAI的模型使用的是上下文敏感的词向量。这意味着同一个词在不同的句子中可能会有不同的向量表示,从而更好地捕捉其在特定上下文中的含义。 -
句子编码
平均池化:一种简单的方法是将句子中所有词向量的平均值作为句子的向量表示。
加权求和:可以对词向量进行加权求和,权重可以根据词的重要性(如TF-IDF)来确定。
Transformer架构:现代模型如BERT、OpenAI的模型使用了自注意力机制(self-attention),能够更好地捕捉句子中的长距离依赖关系,并生成整个句子的向量表示。 -
嵌入层
在神经网络中,嵌入层(Embedding Layer)负责将输入的词索引转换为对应的词向量。这个层通常是可训练的,可以在下游任务中进一步优化。 -
输出向量
最终,模型会输出一个固定长度的向量,这个向量代表了输入文本片段的语义信息。这个向量可以用于各种自然语言处理任务,如相似度计算、分类等。
以上过程参考自网络
Vector Store
向量数据库主要由 LangChain 社区维护的第三方集成 , 即在@langchain/community
包下面
关于选取那个数据 ,请查阅:js.langchain.ac.cn/docs/integr…
下面介绍两种向量数据库
- Chroma
- FaissStore
Chroma
一个专门为嵌入式向量设计的基于 SQLite 的开源数据库 , 有如下特点
- 容易用
- 轻量
- 智能
通过向量切分多个段落 , 并对每个段落独立进行 k-means 聚类 , Chroma 可以有效压缩数据 , 减少储存空间 , 提高查询效率
k-means 聚类是一种无监督学习算法。它将数据分为 k 个聚类,使得每个数据点都属于离它最近的聚类中心所属的聚类。 通过不断迭代更新聚类中心,直到达到某种收敛条件。 例如,在图像识别中,可以用 k-means 聚类对图像的颜色进行分类;在客户细分中, 可以根据客户的特征将客户分为不同的群体。
langchain.js 官网 : js.langchain.ac.cn/docs/integr…
Chroma 官网 : docs.trychroma.com/docs/overvi…
好家伙 , 只支持 python 和 ts 🤡
安装、使用 ,依照上面官网
FaissStore
Faiss 是一个用于高效相似性搜索和密集向量聚类的库。
LangChain.js 支持使用 Faiss 作为本地运行的向量存储,可以保存到文件。
它还提供从 LangChain Python 实现 读取保存的文件的能力。
我在官网上看到这段 , 从那一眼起 , 我就选择她了 , 可是让我无语的是 , 我熬夜到天亮改了一个很臭的 bug —— 使用 npm , yarn , pnpm ... , 从淘宝源到腾讯源 , 这个包总是下不下来 , 我就不断搜索 , 可惜我用的是 Edge , 全是 csdn , 直到我在 github 上搜到以下解决方案 ,非常 nice !
一言蔽之即 : 手动下载 realse 版本 , 将无法下载的文件 ,手动添加到 node_modules
愿以我之发 , 保倔友之发🤡
总结 : 不要使用诸如 Edge 之类的浏览器搜报错🤡👈
实战
package.json
{
"name": "test-app-node",
"private": true,
"version": "0.0.0",
"scripts": {
"prepare-kong-faiss": "ts-node prepare-kong-faiss.ts",
"load-kong-faiss": "ts-node load-kong-faiss.ts",
"multiQueryRetriever": "ts-node multiQueryRetriever.ts",
"LLMChainExtractor": "ts-node LLMChainExtractor.ts",
"ScoreThresholdRetriever": "ts-node ScoreThresholdRetriever.ts",
"prepare-qiu": "ts-node ./rag/prepare-qiu.ts",
"rag-2": "ts-node ./rag/index.ts",
"rag-server": "ts-node ./rag/server.ts",
"rag-client": "ts-node ./rag/client.ts"
},
"type": "module",
"dependencies": {
"@langchain/community": "^0.0.27",
"dotenv": "^16.4.7",
"express": "^4.19.2",
"faiss-node": "^0.5.1",
"langchain": "^0.1.37",
"typescript": "^5.7.3"
},
"main": "index.js",
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"ts-node": "^10.9.2"
}
}
embedding
安装好上述包后 , 使用嵌入式模型 将向量化后的数据储存在 data/vector/ 下
import { TextLoader } from "langchain/document_loaders/fs/text";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { FaissStore } from "langchain/vectorstores/faiss";
import { OpenAIEmbeddings } from "@langchain/openai";
import "dotenv/config";
const run = async () => {
const loader = new TextLoader("../data/少年中国说.txt");
const docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100,
chunkOverlap: 20,
});
const splitDocs = await splitter.splitDocuments(docs);
const embeddings = new OpenAIEmbeddings();
const vectorStore = await FaissStore.fromDocuments(splitDocs, embeddings);
const directory = "../db/vector";
await vectorStore.save(directory);
};
run();
运行上述文件后 , 生成 docstore.json 和二进制文件 faiss.index
docstore.json 中 , 即向量化后的数据 :
retriever
从向量数据库中检索 , 我提问 : 日本怎么称呼我们中国? , 将从向量数据中检索 ,
import { FaissStore } from "@langchain/community/vectorstores/faiss";
import { OpenAIEmbeddings } from "@langchain/openai";
import "faiss-node";
import dotenv from "dotenv";
dotenv.config();
async function f() {
const directory = "../db/vector";
const embeddings = new OpenAIEmbeddings(
{
modelName: "text-embedding-ada-002", //指定模型的名称
maxConcurrency: 10, //设置最大的并发数 , 意味着同负一时间最多可以并行处理10个请求 , 避免过多并发请求 , 导致系统过载和api限流
maxRetries: 3, //设置最大的重试次数 , 当api调用失败的时候 , 程序会自动重试最多三次 , 这增加请求成功的概率 , 提高了系统的可靠性
},
{
batchSize: 100, //设置批量处理的大小 , 每次调用api 最多处理100个文本片段 , 但同时也要注意api的限制和内存的使用
}
);
//加载向量储存
const vectorstore = await FaissStore.load(directory, embeddings);
//从向量数据库中创建一个检索器
const retriever = vectorstore.asRetriever(2);
//使用Runnable API进行进行检索
const res = await retriever.invoke("日本怎么称呼我们中国?");
console.log(res);
}
f();
结果如下 :
总结
学到这里 , 我已经知道知识库从自然语言到向量的过程 , 从数据角度的话 , 经历了一下过程 :
- 加载数据源
- 分割数据
- 向量化数据
- 持久化数据
逐步走向 RAG ~