跳到主要内容

Embedding 优化技巧

问题

如何优化 Embedding 的效果和效率?有哪些实用的优化策略?

答案

一、优化方向总览

二、效果优化

1. Embedding 微调

用业务数据微调通用 Embedding 模型,可显著提升领域效果:

from sentence_transformers import SentenceTransformer, losses
from torch.utils.data import DataLoader

# 加载基础模型
model = SentenceTransformer("BAAI/bge-base-zh-v1.5")

# 准备训练数据:(query, positive_doc, negative_doc)
train_examples = [
InputExample(texts=["如何退款", "退款流程说明", "Python 入门"]),
InputExample(texts=["账号被封", "封号申诉指南", "美食推荐"]),
]

# 使用三元组损失微调
train_dataloader = DataLoader(train_examples, batch_size=16)
train_loss = losses.TripletLoss(model)

model.fit(
train_objectives=[(train_dataloader, train_loss)],
epochs=3,
warmup_steps=100,
)
微调经验
  • 数据量:几百到几千对训练数据即可见效
  • 负采样Hard Negative(语义相近但不相关的文档)比随机负样本效果好 3-5 倍
  • 评测先行:微调前先建立评测集,量化效果提升

2. 查询指令前缀

BGE、E5 等模型支持在查询前加指令前缀以区分查询和文档:

# BGE 模型:查询加前缀,文档不加
query = "为这个句子生成表示以用于检索相关文章:如何退款"
doc = "退款流程说明..." # 文档不加前缀

# E5 模型
query = "query: 如何退款"
doc = "passage: 退款流程说明..."

3. Late Chunking

先对完整文档编码,再分块提取向量(保留跨块上下文):

三、效率优化

1. Matryoshka Embedding(维度裁剪)

Matryoshka Representation Learning 让向量的前 N 维就包含足够信息:

维度MTEB 平均分存储搜索速度
153662.3基准基准
76861.550%~2x
25659.817%~6x
适用模型

OpenAI text-embedding-3 系列、nomic-embed-text、Jina-v3 等支持此特性。

2. 向量量化

将 float32 向量量化为低精度格式,大幅减少存储和计算:

量化精度空间节省Recall 损失
float3232bit基准0%
float1616bit50%< 0.1%
int88bit75%1-3%
binary1bit97%5-10%
import numpy as np

def quantize_int8(vectors: np.ndarray) -> np.ndarray:
"""将 float32 向量量化为 int8"""
vmin, vmax = vectors.min(), vectors.max()
# 线性映射到 [-128, 127]
scale = (vmax - vmin) / 255
return ((vectors - vmin) / scale - 128).astype(np.int8)

3. 二值化(Binary Embedding)

将向量转为 0/1 布尔型,用汉明距离做粗筛:

def binarize(vector: np.ndarray) -> np.ndarray:
"""正值为1,负值为0"""
return (vector > 0).astype(np.uint8)

# 粗筛:二值汉明距离
# 精排:原始向量余弦相似度

四、工程优化

批量处理

# ❌ 逐条编码:N 次 API 调用
for text in texts:
embedding = model.encode(text)

# ✅ 批量编码:1 次调用
embeddings = model.encode(texts, batch_size=64)

缓存策略

import hashlib
from functools import lru_cache

# 内存缓存(适合重复查询)
@lru_cache(maxsize=10000)
def get_embedding(text: str) -> tuple:
return tuple(model.encode(text))

# Redis 缓存(适合持久化)
def get_embedding_cached(text: str, redis_client):
key = f"emb:{hashlib.md5(text.encode()).hexdigest()}"
cached = redis_client.get(key)
if cached:
return np.frombuffer(cached, dtype=np.float32)
emb = model.encode(text)
redis_client.set(key, emb.tobytes(), ex=86400) # 缓存 24h
return emb

常见面试问题

Q1: 如何在不换模型的情况下提升检索效果?

答案(按优先级排序):

  1. 查询改写:将用户查询展开为更明确的搜索语句
  2. 添加指令前缀:BGE/E5 模型使用 query/passage 前缀
  3. 调整分块策略:更大/更小的分块、增加重叠
  4. Embedding 微调:用业务数据微调,提升领域效果
  5. Reranking:检索后用交叉编码器重排序(参考 Reranking

Q2: Matryoshka Embedding 的原理是什么?

答案

  • 训练时同时在多个维度截断点计算损失(如 256/512/768/1536 维)
  • 强制让前 N 维包含最重要的语义信息
  • 推理时可以按需截取前 N 维,在效果和效率间灵活权衡
  • 类似信息论中的"渐进式编码"

相关链接