第 02 章:文本表示学习:从词袋到Transformer

第 02 章:文本表示学习:从词袋到Transformer

“语言是人类思想的密码,Embedding 是解开这道密码的钥匙。”

1. 导言:计算机不懂泰语

在多语言文本分析场景中,我们面临的第一个挑战是语言的巴别塔。
用户的抱怨五花八门:

  • 英文: “Where is my package?”
  • 泰文: “พัสดุอยู่ที่ไหน?”
  • 印尼文: “Di mana paket saya?”

虽然字面完全不同,但它们的意思是一模一样的。
如果我们直接把这些字符串丢给聚类算法,算法会认为它们是完全不相关的东西。因为它只能看到字符 W-h-e-r-eพ-ั-ส-ด-ุ 的区别。

我们需要一种通用语言,把所有人类的语言翻译成计算机能理解的数学语言
这个过程,就是 文本表示 (Text Representation),或者更时髦的叫法——Embedding

Embedding 进化史

2. 核心概念:进化的三个阶段

文本表示技术经历了三个时代的跨越,每一次跨越都让机器离“理解”更近一步。

阶段一:词袋模型 (Bag of Words) —— 极其笨拙的翻译

这是最早期的做法。假设我们有一个词典 {'apple', 'banana', 'cat'}

  • 句子 “I have an apple” -> [1, 0, 0] (假设只关注关键词)
  • 句子 “I have a banana” -> [0, 1, 0]

致命缺陷

  1. 稀疏 (Sparse):词典可能有 10 万个词,你的向量就是 10 万维,全是 0。这极度浪费内存。
  2. 语义鸿沟:在数学上,[1,0,0][0,1,0] 是正交的(垂直的),没有任何相似性。但在现实中,苹果和香蕉都是水果,应该很相似才对。

阶段二:静态词向量 (Word2Vec) —— 捕捉到了语义

2013 年,Google 提出了 Word2Vec。它做了一件惊天动地的事:把词映射到低维稠密空间

  • Apple: [0.8, 0.2, 0.1]
  • Banana: [0.7, 0.3, 0.1]
  • Cat: [-0.5, 0.1, 0.9]

在这个空间里,神奇的事情发生了:

  • $\text{Apple} \approx \text{Banana}$ (向量距离很近)
  • $\vec{King} - \vec{Man} + \vec{Woman} \approx \vec{Queen}$ (居然能做加减法!)

致命缺陷
它是静态的。单词 “Bank” 在 “River Bank” (河岸) 和 “Bank Account” (银行) 中,用的是同一个向量。机器依然是个脸盲。

阶段三:动态上下文向量 (Transformer / BERT) —— 真正的理解

这是目前最先进的技术(也是大多数现代工业系统的选择)。
Transformer 模型(如 BERT, GPT)引入了 Self-Attention (自注意力机制)
在生成向量时,它会看整句话

  • 当它看到 “River Bank” 时,它会给 “Bank” 一个代表【地理位置】的向量。
  • 当它看到 “Bank Account” 时,它会给 “Bank” 一个代表【金融机构】的向量。

这就是为什么现代 Embedding 模型能听懂多国语言
OpenAI 的 Embedding 模型经过了海量多语言语料的训练。它不仅理解了上下文,还理解了跨语言的对应关系。

  • English “Package” 的向量 $\approx$ Thai “พัสดุ” 的向量。
  • 在 1536 维的空间里,它们几乎重叠在一起。

语义空间示意图

3. 技术对比:主流 Embedding 方案

在工业界,选择哪种 Embedding 方案取决于你的钱包和需求。

方案 代表模型 优点 缺点 工业界常见选择
开源小模型 BERT, RoBERTa 免费,可私有化部署,速度快 维度低 (768),语义理解能力有限,多语言支持弱
开源大模型 E5, BGE (MTEB 榜单前列) 效果极好,目前 SOTA 需要昂贵的 GPU 显存,部署维护麻烦
商业 API OpenAI, Cohere, Google PaLM 效果顶级,多语言无敌,无需运维 收费,数据隐私(需传云端)

决策思考
对于需要处理小语种(如泰语、越南语、印尼语等)的项目,开源模型的支持通常较弱。而 OpenAI 的模型在多语言对齐上具有统治级优势。虽然要花钱,但相比于自己雇人清洗数据、训练模型的成本,调用 API 反而是最省钱的。

4. 代码实战:调用 OpenAI Embedding

cluster_analysis.py 中,我们封装了一个函数来获取向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 通用实现:假设你有一个 API Key
import openai

def get_embedding(text, model="text-embedding-3-small"):
   text = text.replace("\n", " ")
   return openai.embeddings.create(input = [text], model=model).data[0].embedding

# 工业实践:使用 requests 库直接调用 REST API,并增加重试机制
def get_single_embedding_with_retry(session, text, index):
    # ... (省略重试逻辑)
    response = session.post(
        f"{OPENAI_BASE_URL}/v1/embeddings",
        json={
            "model": "text-embedding-3-small",  # OpenAI 的 Embedding 模型
            "input": text
        }
    )
    # 返回一个 list,长度为 1536
    return result['data'][0]['embedding']

得到的这个 [0.12, -0.45, ...] 长度为 1536 的数组,就是那句话的数字灵魂

5. 实践要点

  1. 文本清洗:虽然 Transformer 很强,但不要给它喂垃圾。
    • 去掉无意义的 HTML 标签 (<br>, <div>)。
    • 截断超长文本(OpenAI 通常限制 8191 tokens,但为了效果,建议只取前 512 个 token)。
  2. 加权策略
    • 在实际项目中,可以把 category (分类) 字段重复拼接到 description 前面。
    • "Logistics | Logistics | Package lost"
    • 这就相当于告诉 Attention 机制:“嘿,这是物流相关的,请重点关注物流这个词!”这是一种隐式的特征加权。
  3. 缓存 (Caching)
    • API 是要钱的!而且很慢!
    • 务必把跑过的文本 Hash 一下,存到本地文件里。下次跑同一句话,直接读缓存,别再调 API 了。这是降低成本的关键策略。

下一章预告
现在我们手里有了一堆向量。
我们该如何判断两个向量是不是“相似”的?是用尺子量距离?还是用量角器量角度?
在高维空间里,这可是个大问题。

👉 第 03 章:相似度与距离度量

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×