OpenAI Embedding 学习总结
一、什么是 Embedding(嵌入)?
Embedding 是将文本转换为数值向量的技术,它能够:
- 将文本映射到高维向量空间
- 保留文本的语义信息
- 使相似的文本在向量空间中距离更近
应用场景
- 语义搜索
- 文档相似度计算
- 推荐系统
- 文档聚类
- 问答系统
二、LangChain 中的 Embedding
1. 基础嵌入模型
LangChain 提供了多种嵌入模型的统一接口,常用模型包括:
- OpenAI Embeddings :
text-embedding-3-small,text-embedding-3-large - 千帆 Embeddings :
Embedding-V1 - 其他本地模型
核心方法
| 方法 | 说明 |
|---|---|
embed_query(text) |
将单个查询文本转换为向量 |
embed_documents(texts) |
将多个文档文本批量转换为向量 |
三、示例代码与输出
示例 1:基础嵌入模型使用
python
import dotenv
import numpy as np
from langchain_community.embeddings import QianfanEmbeddingsEndpoint
from numpy.linalg import norm
dotenv.load_dotenv()
# 计算两个向量的余弦相似度
def cosine_similarity(vec1, vec2):
dot_product = np.dot(vec1, vec2)
norm_vec1 = norm(vec1)
norm_vec2 = norm(vec2)
return dot_product / (norm_vec1 * norm_vec2)
# 创建文本嵌入模型
embeddings = QianfanEmbeddingsEndpoint(model="Embedding-V1")
# 嵌入单个查询
query_vector = embeddings.embed_query("你好,我是一名学生")
print("查询向量维度:", len(query_vector))
# 批量嵌入文档
documents_vectors = embeddings.embed_documents([
"你好,我是一名学生",
"你好,我是一名老师",
"你好,我是一名学生", # 重复文本
])
print("文档向量数量:", len(documents_vectors))
# 计算相似度
sim1 = cosine_similarity(documents_vectors[0], documents_vectors[1])
sim2 = cosine_similarity(documents_vectors[0], documents_vectors[2])
print("向量1和向量2的相似度:", sim1)
print("向量1和向量3的相似度:", sim2)
预期输出:
makefile
查询向量维度: 1024
文档向量数量: 3
向量1和向量2的相似度: 0.9567
向量1和向量3的相似度: 1.0000
输出分析:
- 向量维度为 1024(千帆 Embedding-V1 模型)
- 相同文本的向量相似度为 1.0(完全相同)
- 相似文本的向量相似度接近 1.0(语义相近)
四、CacheBackedEmbeddings(缓存嵌入)
是什么?
CacheBackedEmbeddings 是 LangChain 提供的带缓存功能的嵌入包装器,它能够:
- 缓存已计算过的文本向量,避免重复调用 API
- 节省时间和成本
- 支持多种存储后端(本地文件、Redis 等)
有什么用?
| 优势 | 说明 |
|---|---|
| 节省成本 | 避免对相同文本重复调用付费 API |
| 提升速度 | 从缓存读取比调用 API 快得多 |
| 离线可用 | 缓存后的文本可离线使用 |
| 一致性 | 相同文本始终得到相同的向量 |
核心参数
python
CacheBackedEmbeddings.from_bytes_store(
underlying_embeddings, # 底层嵌入模型
document_store, # 存储后端
namespace, # 命名空间(区分不同缓存)
query_embedding_cache=True # 是否缓存查询向量
)
五、示例代码与输出(带缓存)
示例 2:使用 CacheBackedEmbeddings
python
import dotenv
import numpy as np
from langchain_community.embeddings import QianfanEmbeddingsEndpoint
from langchain_classic.embeddings import CacheBackedEmbeddings
from langchain_classic.storage import LocalFileStore
from numpy.linalg import norm
dotenv.load_dotenv()
def cosine_similarity(vec1, vec2):
dot_product = np.dot(vec1, vec2)
norm_vec1 = norm(vec1)
norm_vec2 = norm(vec2)
return dot_product / (norm_vec1 * norm_vec2)
# 创建基础嵌入模型
embeddings = QianfanEmbeddingsEndpoint(model="Embedding-V1")
# 创建带缓存的嵌入模型
embeddings_with_cache = CacheBackedEmbeddings.from_bytes_store(
embeddings, # 基础嵌入模型
LocalFileStore("./cache/"), # 本地文件存储
namespace=embeddings.model, # 使用模型名作为命名空间
query_embedding_cache=True, # 缓存查询向量
)
# 第一次调用 - 会请求 API 并缓存
query_vector = embeddings_with_cache.embed_query("你好,我是一名学生")
print("查询向量维度:", len(query_vector))
# 第二次调用相同文本 - 从缓存读取,不请求 API
query_vector_cached = embeddings_with_cache.embed_query("你好,我是一名学生")
# 批量嵌入(部分重复,会使用缓存)
documents_vectors = embeddings_with_cache.embed_documents([
"你好,我是一名学生", # 已缓存
"你好,我是一名老师", # 新文本
"你好,我是一名学生", # 已缓存
])
print("文档向量数量:", len(documents_vectors))
# 计算相似度
sim1 = cosine_similarity(documents_vectors[0], documents_vectors[1])
sim2 = cosine_similarity(documents_vectors[0], documents_vectors[2])
print("向量1和向量2的相似度:", sim1)
print("向量1和向量3的相似度:", sim2)
预期输出:
makefile
查询向量维度: 1024
文档向量数量: 3
向量1和向量2的相似度: 0.9567
向量1和向量3的相似度: 1.0000
缓存目录结构:
bash
./cache/
└── Embedding-V1/
├── <hash1> # "你好,我是一名学生" 的缓存
└── <hash2> # "你好,我是一名老师" 的缓存
六、余弦相似度原理
公式
css
cosine_similarity(A, B) = (A · B) / (||A|| × ||B||)
说明
- A · B: 两个向量的点积
- ||A||: 向量 A 的长度(模)
- 取值范围 : [-1, 1]
1.0: 完全相同方向0.0: 正交(无关)-1.0: 完全相反方向
在文本嵌入中的应用
- 值越接近 1,文本语义越相似
- 值越接近 0,文本语义越不相关
七、最佳实践
- 生产环境务必使用缓存:可节省 50%+ 的 API 调用
- 选择合适的存储后端 :
- 本地开发:
LocalFileStore - 生产环境:
Redis或数据库
- 本地开发:
- 命名空间管理:使用模型名或项目名作为 namespace
- 批量处理 :优先使用
embed_documents而非多次调用embed_query