Embedding 模型本質(zhì)是一個(gè)編碼器神經(jīng)網(wǎng)絡(luò)(通?;?Transformer),將文本壓縮成固定長(zhǎng)度的向量
輸入文本
↓
┌─────────────────┐
│ Tokenizer │ "蘋果是水果" → [101, 679, 3221, 3717, 1963, 102]
└────────┬────────┘
↓
┌─────────────────┐
│ Transformer │ 多層 Self-Attention 提取語義特征
│ Encoder 層 │
│ (BERT結(jié)構(gòu)) │
└────────┬────────┘
↓
┌─────────────────┐
│ 池化層 │ 取 [CLS] token 或平均池化
│ (Pooling) │
└────────┬────────┘
↓
┌─────────────────┐
│ L2 歸一化 │ 向量長(zhǎng)度歸一化為 1
└────────┬────────┘
↓
[0.023, -0.156, 0.891, ..., 0.034] ← 768/1024/1536 維向量
一、Tokenizer
RAG 中的 Tokenizer(分詞/切塊)策略主要決定如何將原始文檔切分成適合檢索的片段(chunk),直接影響召回質(zhì)量。
1、Tokenizer的常見策略有一下幾種:
- 1、按固定 token 數(shù)或字符數(shù)切分,可設(shè)置重疊窗口(overlap)避免語義斷裂。
[chunk1: 0-512 tokens]
[chunk2: 400-912 tokens] ← overlap=112
[chunk3: 800-1312 tokens]
優(yōu)點(diǎn): 簡(jiǎn)單高效,易于實(shí)現(xiàn)
缺點(diǎn): 可能在句子/段落中間截?cái)?,破壞語義完整性
- 2、遞歸字符切分(Recursive Character Splitting)
按優(yōu)先級(jí)依次嘗試分隔符:\n\n → \n → 。 → → 字符,盡量保持語義完整。
代表: LangChain 的 RecursiveCharacterTextSplitter
優(yōu)點(diǎn): 比固定切分更自然,尊重段落/句子邊界
缺點(diǎn): chunk 大小不均勻
- 3、語義感知切分(Semantic-aware Chunking)
3.1 基于句子嵌入的語義切分
將文本按句子分割,計(jì)算相鄰句子的語義相似度
相似度驟降處作為切分點(diǎn)
代表: LlamaIndex SemanticSplitterNodeParser
3.2 基于 NLP 的句子邊界切分
使用 NLP 工具(spaCy、jieba)識(shí)別句子/詞語邊界
按句子為單位組合,控制 chunk 大小
- 4、結(jié)構(gòu)化文檔切分(Structure-aware Chunking)
根據(jù)文檔本身結(jié)構(gòu)切分,保留層級(jí)關(guān)系:
| 文檔類型 | 切分依據(jù) |
|---|---|
| Markdown | 標(biāo)題層級(jí)(H1/H2/H3) |
| HTML | <section>, <p>, <div> |
| 段落、頁面、表格 | |
| 代碼 | 函數(shù)、類、模塊 |
優(yōu)點(diǎn): 語義最完整,chunk 有明確的業(yè)務(wù)含義
- 5、父子切分(Parent-Child / Hierarchical Chunking)
存儲(chǔ)大 chunk(parent),索引小 chunk(child):
檢索時(shí)用小 chunk 精準(zhǔn)匹配
返回時(shí)擴(kuò)展到父級(jí)大 chunk 保證上下文完整
[父 chunk: 整個(gè)段落 1000 tokens]
├── [子 chunk1: 128 tokens] ← 用于向量檢索
├── [子 chunk2: 128 tokens]
└── [子 chunk3: 128 tokens]
- 6、命題切分(Proposition Chunking)
用 LLM 將段落分解為原子性的、自包含的陳述句(proposition),每個(gè) proposition 獨(dú)立可理解。
優(yōu)點(diǎn): 檢索精度極高
缺點(diǎn): 預(yù)處理成本高(需要調(diào)用 LLM)
- 7、Late Chunking(延遲切分)
先對(duì)整個(gè)文檔做 embedding,再切分,讓每個(gè) chunk 的向量帶有全局上下文信息,解決"切分后語義孤立"問題。
2、策略選型建議
| 場(chǎng)景 | 推薦策略 |
|---|---|
| 快速原型 | 固定大小 + overlap |
| 通用文檔 | 遞歸字符切分 |
| 結(jié)構(gòu)化文檔(Markdown/HTML) | 結(jié)構(gòu)感知切分 |
| 高精度問答 | 父子切分 / 命題切分 |
| 長(zhǎng)文檔全局理解 | Late Chunking |
| 中文文檔 | 結(jié)合 jieba 的句子邊界切分 |
3、核心調(diào)參指標(biāo):
- chunk_size:通常 256~1024 tokens,取決于 embedding 模型上下文窗口
- chunk_overlap:通常 10%~20% 的 chunk_size
- 切分后建議對(duì)每個(gè) chunk 添加元數(shù)據(jù)(來源、標(biāo)題、頁碼),增強(qiáng)檢索后的可解釋性
二、Transformer Encoder
一句話:把一段話變成一組數(shù)字,讓電腦能"比較"兩段話是不是在說同一件事。
你和朋友聊天時(shí),你能馬上聽出"我餓了"和"想吃飯"是一個(gè)意思。但電腦只認(rèn)數(shù)字,所以需要一個(gè)翻譯器,把文字翻譯成數(shù)字,且保證意思相近的話,翻譯出來的數(shù)字也相近。
Transformer Encoder 就是這個(gè)翻譯器。
整個(gè)過程只有 4 步
第 1 步:拆字
電腦不認(rèn)識(shí)"蘋果手機(jī)推薦"這個(gè)字符串,需要先拆成小塊:
"蘋果手機(jī)推薦"
↓ 拆開
["蘋果", "手機(jī)", "推薦"]
↓ 每個(gè)詞給一個(gè)編號(hào)
[ 678, 312, 891 ]
就像給每個(gè)詞發(fā)了一張編號(hào)卡,編號(hào)是查字典得來的。
第 2 步:給每個(gè)詞一個(gè)初始描述
光有編號(hào)沒用,需要給每個(gè)詞一個(gè)"畫像"——用 768 個(gè)數(shù)字描述它。
"蘋果" → [0.5, 0.1, 0.3, 0.8, 0.2, ... ] 共768個(gè)數(shù)字
"手機(jī)" → [0.3, 0.8, 0.6, 0.1, 0.4, ... ] 共768個(gè)數(shù)字
"推薦" → [0.7, 0.2, 0.5, 0.9, 0.3, ... ] 共768個(gè)數(shù)字
但此時(shí)有個(gè)大問題: 每個(gè)詞的畫像是"死"的。不管"蘋果"出現(xiàn)在"蘋果手機(jī)"還是"蘋果好吃"里,畫像都一樣。它還不理解上下文。
第 3 步:讓詞和詞之間"聊天"(Self-Attention)
這是最核心的一步。
打個(gè)比方:你新來一家公司,你只知道自己的崗位(初始畫像)。你需要和周圍同事聊天,了解部門整體在做什么,才能真正理解自己在這個(gè)團(tuán)隊(duì)里的角色。
Self-Attention 做的就是這件事:讓每個(gè)詞去問其他所有詞——"你和我什么關(guān)系?"
場(chǎng)景: "蘋果 手機(jī) 推薦"
"手機(jī)"開始問:
→ 問"蘋果": 你和我什么關(guān)系?
"蘋果"回答: 我是你的品牌修飾詞,關(guān)系很密切! (分?jǐn)?shù): 80分)
→ 問"推薦": 你和我什么關(guān)系?
"推薦"回答: 我是動(dòng)作,你是對(duì)象,有關(guān)系 (分?jǐn)?shù): 50分)
→ 問自己: 你和我什么關(guān)系?
自己回答: 我就是我 (分?jǐn)?shù): 90分)
然后按分?jǐn)?shù)高低,把其他詞的信息"吸收"過來更新自己的畫像:
新的"手機(jī)"畫像 = 自身信息×40% + "蘋果"信息×35% + "推薦"信息×25%
更新后,"手機(jī)"的畫像里就融入了"蘋果"的信息,電腦就知道這里說的是蘋果品牌的手機(jī)了。
每個(gè)詞都這樣做一遍。一輪之后,所有詞都"理解"了上下文。
為什么要做 12 輪?
一輪只能理解最直接的關(guān)系。多輪之后理解越來越深:
第 1-2 輪: 知道"蘋果"和"手機(jī)"是挨在一起的
第 3-5 輪: 理解"蘋果手機(jī)"是一個(gè)品牌產(chǎn)品
第 6-9 輪: 理解整句話是"有人在找蘋果手機(jī)的推薦"
第 10-12輪: 形成高度抽象的語義理解,能和"iPhone哪款好"匹配上
類比:你在新公司第一天只認(rèn)識(shí)隔壁同事。一個(gè)月后認(rèn)識(shí)了整個(gè)部門。半年后理解了公司的戰(zhàn)略方向。每一輪"聊天"都讓理解更深一層。
多頭注意力是什么?
每一輪聊天不只聊一個(gè)話題,同時(shí)聊 12 個(gè)話題:
話題1: 誰修飾誰? → "蘋果"修飾"手機(jī)"
話題2: 誰是動(dòng)作對(duì)象? → "推薦"的對(duì)象是"手機(jī)"
話題3: 誰和誰意思相近? → "推薦" ≈ "建議"
話題4: 誰挨著誰? → "蘋果"緊挨"手機(jī)"
... 共12個(gè)話題同時(shí)進(jìn)行
每個(gè)話題聊出來的結(jié)果拼在一起,信息更全面。
第 3.5 步:FFN(深度消化)
每次"聊天"之后,每個(gè)詞需要單獨(dú)"消化"吸收到的信息:
"手機(jī)"剛吸收了"蘋果"和"推薦"的信息
↓ 經(jīng)過FFN"消化"
"手機(jī)"把新信息和自己已有的知識(shí)融合
→ 形成更成熟的語義理解
類比:開完會(huì)(Self-Attention)后回到工位整理筆記(FFN),把討論內(nèi)容轉(zhuǎn)化成自己的理解。
第 4 步:合并成一個(gè)句子向量
12 輪之后,每個(gè)詞都有了充分理解上下文的向量。最后需要合并成一個(gè)向量代表整句話:
"蘋果" → [0.31, 0.67, ...]
"手機(jī)" → [0.58, 0.44, ...]
"推薦" → [0.37, 0.71, ...]
↓
池化層(Pooling)
↓
句子向量 → [0.42, 0.61, ...] 768個(gè)數(shù)字
這 768 個(gè)數(shù)字,就是"蘋果手機(jī)推薦"這句話在電腦眼里的"身份證號(hào)"。
總結(jié)
| 步驟 | 做了什么 | 大白話 |
|---|---|---|
| 拆字 | 文本變Token | 把句子拆成詞 |
| 初始向量 | Token變768維向量 | 給每個(gè)詞一張初始畫像 |
| Self-Attention ×12輪 | 詞與詞交互 | 讓每個(gè)詞看看周圍詞,理解上下文 |
| FFN | 深度變換 | 開完會(huì)后整理筆記 |
| Pooling | 多個(gè)向量→1個(gè) | 把所有詞的理解合并成句子的理解 |
本質(zhì)就是:拆詞 → 給初始畫像 → 反復(fù)交流更新畫像 → 合并成一個(gè)代表整句話的數(shù)字
三、池化層
經(jīng)過 Transformer Encoder 的 12 層處理后,每個(gè) Token 都有了自己的 768 維向量。但做檢索時(shí),我們需要一個(gè)向量代表整段話。
池化層的工作就是:把多個(gè)詞向量,壓縮成一個(gè)句子向量。
Encoder 輸出:
[CLS] → [0.31, 0.67, 0.45, ...] 768維
蘋果 → [0.45, 0.52, 0.38, ...] 768維
手機(jī) → [0.58, 0.44, 0.61, ...] 768維
推薦 → [0.37, 0.71, 0.29, ...] 768維
[SEP] → [0.12, 0.33, 0.55, ...] 768維
│
池化層(Pooling)
│
▼
句子向量 → [?, ?, ?, ...] 768維 ← 怎么算這個(gè)?
不同的池化策略,"怎么算"的方式不同,效果也不同。
四種主流池化策略
- CLS Pooling(取 [CLS] 向量)
做法: 直接取 [CLS] 這個(gè)特殊 Token 的向量作為句子向量,其他詞全部丟棄。
[CLS] → [0.31, 0.67, 0.45, ...] ← 直接用這個(gè)
蘋果 → [0.45, 0.52, 0.38, ...] ← 丟棄
手機(jī) → [0.58, 0.44, 0.61, ...] ← 丟棄
推薦 → [0.37, 0.71, 0.29, ...] ← 丟棄
[SEP] → [0.12, 0.33, 0.55, ...] ← 丟棄
句子向量 = [0.31, 0.67, 0.45, ...]
原理: BERT 在預(yù)訓(xùn)練時(shí),專門訓(xùn)練 [CLS] Token 去匯聚整句話的語義。經(jīng)過 12 層 Self-Attention,[CLS] 已經(jīng)"看過"所有詞,理論上包含了全句信息。
優(yōu)點(diǎn):
- 計(jì)算最快,只取一個(gè)向量
- BERT 原生設(shè)計(jì)就是用 [CLS] 做句子級(jí)任務(wù)
缺點(diǎn):
- [CLS] 的預(yù)訓(xùn)練目標(biāo)是 NSP(下一句預(yù)測(cè)),不是語義相似度
- 實(shí)測(cè)在語義檢索任務(wù)上效果不如 Mean Pooling
- 信息過度集中在一個(gè) Token 上,容易丟失細(xì)節(jié)
適用: 分類任務(wù)(情感分析、文本分類)
- Mean Pooling(平均池化)
做法: 把所有 Token 的向量逐維取平均值。
[CLS] → [0.31, 0.67, 0.45, ...]
蘋果 → [0.45, 0.52, 0.38, ...]
手機(jī) → [0.58, 0.44, 0.61, ...]
推薦 → [0.37, 0.71, 0.29, ...]
[SEP] → [0.12, 0.33, 0.55, ...]
第1維平均: (0.31+0.45+0.58+0.37+0.12) / 5 = 0.366
第2維平均: (0.67+0.52+0.44+0.71+0.33) / 5 = 0.534
第3維平均: (0.45+0.38+0.61+0.29+0.55) / 5 = 0.456
...
句子向量 = [0.366, 0.534, 0.456, ...]
帶 Attention Mask 的版本(實(shí)際使用)
實(shí)際文本長(zhǎng)度不一樣,短文本會(huì)用 [PAD] 填充到統(tǒng)一長(zhǎng)度。Padding 位置不應(yīng)該參與計(jì)算:
Token: [CLS] 蘋果 手機(jī) 推薦 [SEP] [PAD] [PAD] [PAD]
Mask: [ 1, 1, 1, 1, 1, 0, 0, 0 ]
只對(duì) Mask=1 的位置求平均:
句子向量 = (CLS + 蘋果 + 手機(jī) + 推薦 + SEP) / 5
優(yōu)點(diǎn):
- 每個(gè)詞都參與,信息保留最完整
- 不依賴某個(gè)特殊 Token 的訓(xùn)練質(zhì)量
- 在語義檢索任務(wù)上效果最好
缺點(diǎn):
- 所有詞權(quán)重相同,"的""了"這類虛詞也參與,會(huì)稀釋關(guān)鍵詞信息
適用: RAG 語義檢索(業(yè)界主流方案)
- Max Pooling(最大池化)
做法: 在每一維上,取所有 Token 中的最大值。
第1維 第2維 第3維
[CLS] → 0.31 0.67 0.45
蘋果 → 0.45 0.52 0.38
手機(jī) → 0.58 0.44 0.61
推薦 → 0.37 0.71 0.29
[SEP] → 0.12 0.33 0.55
取每列最大值:
第1維: max(0.31,0.45,0.58,0.37,0.12) = 0.58 ← 來自"手機(jī)"
第2維: max(0.67,0.52,0.44,0.71,0.33) = 0.71 ← 來自"推薦"
第3維: max(0.45,0.38,0.61,0.29,0.55) = 0.61 ← 來自"手機(jī)"
句子向量 = [0.58, 0.71, 0.61, ...]
原理: 每一維取最大值,相當(dāng)于保留了"最顯著的特征"。如果某個(gè)詞在某個(gè)語義維度上激活很強(qiáng),就保留它。
優(yōu)點(diǎn):
- 對(duì)關(guān)鍵詞敏感,突出特征明顯的詞
- 不容易被虛詞和 Padding 稀釋
缺點(diǎn):
- 丟失了詞序和比例信息
- 只取極端值,對(duì)細(xì)膩語義差異不敏感
- 一個(gè)"噪聲"詞的異常激活可能影響整體
適用: 關(guān)鍵詞匹配、短文本檢索
- Weighted Mean Pooling(加權(quán)平均池化)
做法: 給不同層/位置的 Token 分配不同權(quán)重,再加權(quán)平均。
4.1 按層加權(quán)(越深層權(quán)重越大)
BERT 有12層,每層都有輸出:
Layer 1 的"手機(jī)"向量 × 權(quán)重 0.02
Layer 2 的"手機(jī)"向量 × 權(quán)重 0.03
...
Layer 11 的"手機(jī)"向量 × 權(quán)重 0.15
Layer 12 的"手機(jī)"向量 × 權(quán)重 0.20 ← 最后一層權(quán)重最大
原理: 深層語義更抽象更適合檢索,給更大權(quán)重
4.2 按位置加權(quán)(越靠后權(quán)重遞增/遞減)
Token: [CLS] 蘋果 手機(jī) 推薦 [SEP]
權(quán)重: 0.1 0.2 0.3 0.3 0.1
句子向量 = 0.1×CLS + 0.2×蘋果 + 0.3×手機(jī) + 0.3×推薦 + 0.1×SEP
4.3 按注意力分?jǐn)?shù)加權(quán)(Attention-weighted)
利用最后一層 Self-Attention 中 [CLS] 對(duì)各詞的注意力權(quán)重:
[CLS] 對(duì)各詞的注意力:
蘋果: 0.30 ← 模型認(rèn)為"蘋果"很重要
手機(jī): 0.35 ← "手機(jī)"最重要
推薦: 0.25 ← 其次
[SEP]: 0.10
句子向量 = 0.30×蘋果 + 0.35×手機(jī) + 0.25×推薦 + 0.10×SEP
優(yōu)點(diǎn):
- 重要的詞貢獻(xiàn)更大,更精準(zhǔn)
- 靈活性強(qiáng),可以根據(jù)任務(wù)調(diào)整權(quán)重
缺點(diǎn):
- 權(quán)重需要額外學(xué)習(xí)或手工設(shè)計(jì)
- 實(shí)現(xiàn)復(fù)雜度高
適用: 對(duì)精度有極高要求的場(chǎng)景
四種策略對(duì)比
| 策略 | 計(jì)算方式 | 信息保留 | 檢索效果 | 計(jì)算成本 |
|---|---|---|---|---|
| CLS Pooling | 取[CLS]向量 | 低(只取一個(gè)Token) | 一般 | 最低 |
| Mean Pooling | 所有Token平均 | 高(每個(gè)詞都參與) | 好 | 低 |
| Max Pooling | 每維取最大值 | 中(只保留極端值) | 中等 | 低 |
| Weighted Mean | 加權(quán)平均 | 最高(按重要性分配) | 最好 | 較高 |
主流 Embedding 模型都用什么池化?
| 模型 | 池化策略 | 備注 |
|---|---|---|
| Sentence-BERT | Mean Pooling | 開創(chuàng)性工作,驗(yàn)證了Mean優(yōu)于CLS |
| OpenAI text-embedding-ada-002 | 未公開(推測(cè)Mean) | 閉源模型 |
| BGE 系列(智源) | CLS Pooling | 但經(jīng)過專門的對(duì)比學(xué)習(xí)訓(xùn)練CLS |
| GTE 系列(阿里) | Mean Pooling | 中文效果好 |
| E5 系列(微軟) | Mean Pooling | 多語言支持好 |
| Jina Embeddings | Mean Pooling | 長(zhǎng)文本支持好 |
實(shí)際建議
大多數(shù) RAG 場(chǎng)景 → 直接用 Mean Pooling,簡(jiǎn)單且效果最好
關(guān)鍵詞檢索場(chǎng)景 → 可以嘗試 Max Pooling
追求極致精度 → 用 Weighted Mean 或選擇已經(jīng)訓(xùn)練好的模型(如 BGE)
最重要的一點(diǎn): 池化策略的選擇遠(yuǎn)不如 Encoder 本身的訓(xùn)練質(zhì)量重要。一個(gè)經(jīng)過對(duì)比學(xué)習(xí)精心訓(xùn)練的模型,用 CLS 也能超過未經(jīng)訓(xùn)練的 Mean Pooling。選好模型比選池化策略更關(guān)鍵。
四、歸一化
每個(gè) Transformer Block 中出現(xiàn)兩次:
輸入
│
▼
┌──────────────────────────┐
│ Self-Attention │
└────────────┬─────────────┘
│
Add & LayerNorm ← 第1次
│
▼
┌──────────────────────────┐
│ Feed-Forward (FFN) │
└────────────┬─────────────┘
│
Add & LayerNorm ← 第2次
│
▼
傳入下一層
這里的 "Add & LayerNorm" 其實(shí)是兩步操作:殘差連接(Add) + 層歸一化(LayerNorm)。
殘差連接(Add)
輸出 = 子層輸出 + 原始輸入
用大白話說:
你參加了一個(gè)培訓(xùn)班(Self-Attention / FFN)
不用殘差: 你把以前學(xué)的全忘了,只記得培訓(xùn)班的內(nèi)容
用殘差: 你保留了以前學(xué)的,再疊加培訓(xùn)班的新知識(shí)
原始輸入: [0.5, 0.3, 0.8] ← 你已有的知識(shí)
子層輸出: [0.1, 0.4, -0.2] ← 培訓(xùn)班學(xué)到的新東西
殘差相加: [0.6, 0.7, 0.6] ← 新舊結(jié)合
作用:
- 保證信息不會(huì)在深層網(wǎng)絡(luò)中完全丟失
- 解決深層網(wǎng)絡(luò)"梯度消失"問題(訓(xùn)練時(shí)梯度可以通過殘差通道直接回傳)
- 沒有殘差連接,12 層 Encoder 根本訓(xùn)練不起來