为什么Embedding比以前更重要?

2026年,Embedding已经成为AI应用的基础设施。无论是RAG系统、语义搜索、推荐系统,还是内容去重、相似度计算,背后都离不开高质量的向量化技术。但很多工程师对Embedding的理解还停留在"把文本变成向量"这个粗浅层面。本文将深入讲解Embedding技术的演进、不同场景下的选型策略,以及生产环境的工程实践。—## 一、Embedding技术演进简史### 1.1 第一代:词袋模型(Bag of Words)最早的文本表示方法,将文本转为词频向量:pythonfrom sklearn.feature_extraction.text import TfidfVectorizercorpus = [ "大模型改变了AI应用开发", "RAG系统需要高质量的向量化", "Embedding是语义搜索的基础"]# TF-IDF向量化vectorizer = TfidfVectorizer()X = vectorizer.fit_transform(corpus)print(X.shape) # (3, N_词汇数)缺点:无法捕捉语义,"大模型"和"LLM"是完全不同的向量。### 1.2 第二代:Word2Vec/GloVe(词级静态向量)2013年Word2Vec的出现革命性地引入了语义向量空间:- “国王” - “男人” + “女人” ≈ “女王”- 相似词在向量空间中距离相近缺点:一词一个向量,无法处理多义词("苹果"在不同语境含义不同)。### 1.3 第三代:BERT系列(上下文动态向量)BERT引入了上下文感知的词向量,同一个词在不同句子中有不同的向量表示。但BERT的句子级向量(通常取[CLS]标记)并不擅长语义相似度任务。### 1.4 第四代:Sentence Transformers(语义向量)专为句子级语义相似度优化的模型,使用对比学习训练:pythonfrom sentence_transformers import SentenceTransformermodel = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')sentences = [ "如何提高RAG系统的准确率?", "RAG系统精度优化方法有哪些?", # 语义相似 "今天天气怎么样?" # 语义无关]embeddings = model.encode(sentences)similarities = model.similarity(embeddings, embeddings)print(similarities)# 前两句相似度接近1.0,与第三句相似度接近0### 1.5 第五代:大规模通用Embedding模型(当前主流)OpenAI text-embedding-3系列、Cohere Embed v3、阿里云通义Embedding等,用数十亿文本对训练,泛化能力极强。—## 二、2026年主流Embedding模型横评### 2.1 商业API| 模型 | 维度 | 最大Token | 中文支持 | 价格(/1M tokens) ||------|------|-----------|---------|-------------------|| text-embedding-3-large | 3072 | 8192 | 优秀 | $0.13 || text-embedding-3-small | 1536 | 8192 | 良好 | $0.02 || Cohere embed-v3 | 1024 | 512 | 良好 | $0.10 || 通义千问 text-embedding-v3 | 1024/2048 | 8192 | 极好 | ¥0.0007/千token || 智谱 embedding-3 | 2048 | 8192 | 极好 | ¥0.0005/千token |### 2.2 开源模型(本地部署)python# 使用sentence-transformers加载开源模型# 中文最佳选择from sentence_transformers import SentenceTransformer# BAAI/bge-m3:多语言,支持稠密+稀疏+多粒度检索model = SentenceTransformer("BAAI/bge-m3")# 专门优化中文的模型model_zh = SentenceTransformer("BAAI/bge-large-zh-v1.5")# 代码语义搜索专用model_code = SentenceTransformer("Salesforce/codesearch-distilroberta-base")# 多模态(文本+图像)from transformers import CLIPModel, CLIPProcessorclip_model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")### 2.3 如何选择?需要高精度 + 不在意成本 → text-embedding-3-large需要低成本 + 可接受略低精度 → text-embedding-3-small中文业务 + 成本敏感 → 通义/智谱 Embedding私有化部署 + 中文 → BAAI/bge-large-zh-v1.5代码搜索 → code-search-babbage多模态(图文混合) → CLIP或通用多模态模型—## 三、核心工程实践### 3.1 批量Embedding(生产必备)pythonimport asynciofrom openai import AsyncOpenAIfrom typing import AsyncGeneratorclient = AsyncOpenAI()async def embed_batch( texts: list[str], batch_size: int = 100, model: str = "text-embedding-3-small") -> list[list[float]]: """批量向量化,自动处理并发和重试""" all_embeddings = [] for i in range(0, len(texts), batch_size): batch = texts[i:i + batch_size] # 清理文本:去除多余空白、控制长度 cleaned_batch = [ text.strip()[:8000] # 留一些buffer for text in batch if text.strip() ] try: response = await client.embeddings.create( input=cleaned_batch, model=model, encoding_format="float" ) batch_embeddings = [item.embedding for item in sorted( response.data, key=lambda x: x.index )] all_embeddings.extend(batch_embeddings) except Exception as e: print(f"批次 {i//batch_size} 失败: {e}") # 单个文本重试 for text in cleaned_batch: try: resp = await client.embeddings.create( input=[text], model=model ) all_embeddings.append(resp.data[0].embedding) except: # 失败用零向量占位 all_embeddings.append([0.0] * 1536) return all_embeddings### 3.2 维度压缩(降本提速)text-embedding-3系列支持Matryoshka表示学习,可以直接截取前N维:python# 原始3072维 → 压缩到512维,质量损失约5%,存储和计算成本降低6倍response = await client.embeddings.create( input=texts, model="text-embedding-3-large", dimensions=512 # 指定输出维度)或者使用PCA离线压缩:pythonimport numpy as npfrom sklearn.decomposition import PCAdef train_dimension_reducer(sample_embeddings: np.ndarray, target_dim: int = 256): """训练维度压缩器(用样本数据拟合PCA)""" pca = PCA(n_components=target_dim, random_state=42) pca.fit(sample_embeddings) explained_variance = pca.explained_variance_ratio_.sum() print(f"保留方差比例: {explained_variance:.2%}") return pcadef compress_embeddings(embeddings: np.ndarray, pca: PCA) -> np.ndarray: """使用训练好的PCA压缩向量""" return pca.transform(embeddings)### 3.3 查询优化:HyDE(假设文档扩展)对于用户查询很短而知识库文档很长的情况,直接用查询向量检索效果较差。HyDE方法先让LLM生成一个假设答案,用答案的向量去检索:pythonasync def hyde_retrieve( query: str, retriever, llm, top_k: int = 5) -> list: """HyDE:用假设答案向量检索,提升召回率""" # 生成假设答案 hypothetical_answer = await llm.generate( f"假设你要回答以下问题,写一段详细的技术说明(200字):\n{query}" ) # 用假设答案做检索(而不是原始查询) results = await retriever.retrieve( hypothetical_answer, # 关键:用生成的答案而非原始查询 top_k=top_k ) return results# 与普通检索对比:# 原始查询:"RAG的chunk size怎么设置?"(14字)# 假设答案:"RAG系统中chunk size的设置需要考虑多个因素。# 过小的chunk可能导致上下文信息不完整,# 过大则会引入噪声并降低检索精度。# 通常建议将chunk size设置在256-512 tokens之间..."(150字)### 3.4 混合检索(稀疏+稠密)纯向量检索在精确词汇匹配上弱于BM25,混合检索两者取长补短:pythonfrom qdrant_client import QdrantClientfrom qdrant_client.models import SparseVector, NamedSparseVectordef hybrid_search( query: str, dense_embedding: list[float], sparse_embedding: dict[int, float], # BM25/SPLADE生成的稀疏向量 collection: str, top_k: int = 10, dense_weight: float = 0.7, sparse_weight: float = 0.3) -> list: """混合检索:向量检索 + 关键词检索""" from qdrant_client.models import Prefetch, FusionQuery, Fusion results = client.query_points( collection_name=collection, prefetch=[ Prefetch( query=dense_embedding, using="dense", limit=20 ), Prefetch( query=NamedSparseVector( name="sparse", vector=SparseVector( indices=list(sparse_embedding.keys()), values=list(sparse_embedding.values()) ) ), using="sparse", limit=20 ) ], query=FusionQuery(fusion=Fusion.RRF), # 倒数排名融合 limit=top_k ) return results.points—## 四、多模态Embedding### 4.1 图文混合知识库pythonfrom PIL import Imageimport torchfrom transformers import CLIPModel, CLIPProcessorclass MultimodalEmbedder: def __init__(self): self.model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14") self.processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14") def embed_text(self, texts: list[str]) -> np.ndarray: inputs = self.processor(text=texts, return_tensors="pt", padding=True) with torch.no_grad(): features = self.model.get_text_features(**inputs) return features.numpy() def embed_image(self, images: list[Image.Image]) -> np.ndarray: inputs = self.processor(images=images, return_tensors="pt") with torch.no_grad(): features = self.model.get_image_features(**inputs) return features.numpy() def cross_modal_similarity( self, text_query: str, images: list[Image.Image] ) -> list[float]: """用文字查询找最相关的图片""" text_emb = self.embed_text([text_query]) image_embs = self.embed_image(images) # 归一化 text_emb = text_emb / np.linalg.norm(text_emb, axis=1, keepdims=True) image_embs = image_embs / np.linalg.norm(image_embs, axis=1, keepdims=True) similarities = (text_emb @ image_embs.T)[0] return similarities.tolist()—## 五、Embedding质量评估pythonfrom sklearn.metrics.pairwise import cosine_similarityimport pandas as pddef evaluate_embedding_quality( model_name: str, test_pairs: list[tuple[str, str, float]] # (文本A, 文本B, 人工相似度标注)) -> dict: """评估Embedding模型在特定业务数据上的质量""" texts_a = [p[0] for p in test_pairs] texts_b = [p[1] for p in test_pairs] human_scores = [p[2] for p in test_pairs] # 获取向量 embs_a = get_embeddings(texts_a, model=model_name) embs_b = get_embeddings(texts_b, model=model_name) # 计算余弦相似度 model_scores = [ cosine_similarity([a], [b])[0][0] for a, b in zip(embs_a, embs_b) ] # 计算与人工标注的相关性(斯皮尔曼相关) from scipy.stats import spearmanr correlation, p_value = spearmanr(human_scores, model_scores) return { "model": model_name, "spearman_correlation": correlation, "p_value": p_value, "avg_cosine_for_similar": np.mean([s for s, h in zip(model_scores, human_scores) if h > 0.7]), "avg_cosine_for_dissimilar": np.mean([s for s, h in zip(model_scores, human_scores) if h < 0.3]) }—## 六、生产部署要点1. 向量归一化:入库时统一做L2归一化,检索时用内积代替余弦(等价但更快)2. 索引类型选择: - < 100万向量:HNSW(高精度) - > 1000万向量:IVF_PQ(量化压缩,牺牲少量精度换存储)3. 增量更新:大多数向量数据库支持增量插入,无需全量重建索引4. 分片策略:超大规模(亿级)考虑按业务域分集合,避免单集合过大Embedding技术在2026年已经非常成熟,选对模型、做好工程化,是构建高质量语义检索系统的核心前提。—本文代码示例基于openai-python 1.x、sentence-transformers 3.x、qdrant-client 1.9+

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐