NLP 早期技术演进:从分词到词向量

3 阅读6分钟

📖 一句话说清全文:从中文分词到词向量,NLP 早期技术的核心逻辑就是——让机器从「认识字」进化到「理解语义」。本文 10 章,由浅入深讲透原理、公式、面试考点和工业落地。


📋 本文目录

章节内容难度
中文分词基础 & 词表匹配算法🟢
分词 DAG 有向无环图🟡
TF-IDF & BM25 核心原理(附公式推导 + 实例)🟡🔴
TF-IDF 工业落地应用🟢
TF-IDF vs BM25 优缺点对比🟡
六→七从 One-Hot 到 Word2Vec 演进🟡
神经概率语言模型(理论基石)🔴
静态词向量核心痛点🟡
延伸:句向量 & K-Means 聚类🟢🟡

一、中文分词基础与词表匹配算法 🟢

1.1 中文分词基础概述

🎯 面试官会问:中文 NLP 和英文 NLP 最大的处理差异是什么?

2018 年某电商搜索「南京市长裙」,返回了大量「南京市 + 长江大桥」的图片。因为分词器把「南京市长」切出来了——一次分词事故,直接导致关键词匹配翻车。

中文和英文最大的区别就在这:英文单词自带空格,中文是一大坨汉字糊你脸上,你自己切

英文: I / love / natural / language / processing
中文: 我爱自然语言处理 → 我 / 爱 / 自然语言 / 处理

分词就是把连续中文文本切成独立有语义的词语。它是所有中文 NLP 任务的前置必经步骤——检索、分类、情感分析,谁都绕不开。

两大硬难点

难点说明经典翻车现场
😵 歧义切分同一段话有多个合法切法南京市长江大桥 → 南京市长/江大桥?南京市/长江大桥?
🤯 新词识别网络热词随时冒,词表永远追不上绝绝子大模型RLHF遥遥领先

🕳 踩坑实录:某新闻 App 上线分词后,「特朗普」被切成「特/朗/普」三个单字,热搜榜直接崩了。最后发现词表里没这个词。

工业现状:几乎没人自己手写。业务直接上结巴(jieba) 等开源工具,好使不折腾。传统分词算法更多是面试考点——工程谁自己写谁加班 😅

1.2 基于词表的分词算法

全部依赖词表做匹配,最经典的是正向最大匹配法(FMM)

核心逻辑:从左往右扫,先挑最长的下手——吃自助餐先拿最大的盘子 🍽️

词表: ["南京", "南京市", "市长", "长江", "长江大桥", "大桥"]
文本: "南京市长江大桥"1 轮:取"南京市长江大"→ 无 → 缩短 → "南京市长江"→ 无 → ...
          直到 "南京市" → 命中!切出 ✅
第 2 轮:从"长江大桥"继续 → "长江大桥" → 命中!切出 ✅

结果:南京市 / 长江大桥 ✅

🤔 思考题:如果词表里同时有「南京」和「南京市」,FMM 会优先切哪个? A. 南京(短的) B. 南京市(长的) C. 看词表顺序 👇 评论区告诉我答案

✅ 优点❌ 缺点
逻辑简单,10 行代码搞定看词表脸色——词表拉胯一切拉胯
实现零门槛缺词或歧义立刻翻车

前缀字典优化:给前缀打标记,0=仅前缀,1=完整词。不用反复回退,直接跳。

"长江大桥" 的前缀标记:
  长→0  长江→1  长江大→0  长江大桥→1
                        ↑
                命中就切,不等了

全切分(🔥高频考点):给定词表,枚举文本所有合法切分方式。

输入:文本"abc",词表 {a, ab, bc, c}
输出:["a/bc", "ab/c"]   ← 枚举所有合法路径

没有词表就没有全切分。本质考递归 + 回溯,面试官最爱 🎯


二、分词 DAG 有向无环图 🟡

DAG 是结巴分词的底层核心数据结构。

原理:以每个汉字为节点,记录当前字往后能构成合法词的所有终点位置。

文本:南京市长江大桥

DAG 图结构:
0(南): → 1(京) → 2(市)           # "南京""南京市"
1(京): → 2(市)                     # "京市"(单字)
2(市): → 3(长) → 5(大)            # "市长"
3(长): → 4(江) → 6(桥)            # "长江""长江大桥"
4(江): → 5(大) → 6(桥)            # "江大桥"
5(大): → 6(桥)                     # "大桥"
6(桥):                              # 终点

完整流程

① 遍历文本,逐字查词表
② 每个字作起点,记录可达的终点下标
③ 建图后遍历所有路径 → 穷举全部分词结果
④ 动态规划选最优路径

DAG 是全切分的高效底层支撑——没有 DAG,结巴就是散沙 🏖️


三、TF-IDF 与 BM25 算法核心原理 🟡🔴

3.1 TF-IDF 核心原理(附公式 + 手算实例)

🎯 面试官会问:TF-IDF 的 IDF 为什么要取 log?如果文档集只有一篇文章怎么办?

TF-IDF = TF(词频) × IDF(逆文档频率)

一句话:一个词在本文出现多 + 在其他文档出现少 = 核心关键词

完整公式链

TF(t,d)=count(t,d)kdcount(k,d)TF(t, d) = \frac{\text{count}(t, d)}{\sum_{k \in d} \text{count}(k, d)}
IDF(t,D)=logN1+df(t,D)IDF(t, D) = \log \frac{N}{1 + \text{df}(t, D)}
TF-IDF(t,d,D)=TF(t,d)×IDF(t,D)TF\text{-}IDF(t, d, D) = TF(t, d) \times IDF(t, D)
符号含义直觉
count(t,d)\text{count}(t, d)tt 在文档 dd 中出现次数这个我说了多少遍
kdcount(k,d)\sum_{k \in d} \text{count}(k, d)文档总词数归一化,不被长文欺负
NN文档总数全局视野
df(t,D)\text{df}(t, D)tt 的文档数这词到底有多通用
+1+1平滑项防止 log(0)

手算一笔账 📊

文档集 D = {doc1, doc2, doc3, doc4, doc5}

词 "NLP" 在各文档的词频:
  doc1: 3次/100词    doc2: 0次/80词  
  doc3: 5次/200词    doc4: 0次/120词
  doc5: 2次/150TF("NLP", doc1) = 3/100   = 0.030
IDF("NLP", D)   = log(5/(1+3)) = log(1.25) ≈ 0.223
TF-IDF          = 0.030 × 0.2230.0067

对比停用词「的」

TF("的", doc1) = 15/100   = 0.150    ← 词频超高
IDF("的", D)   = log(5/(1+5)) = log(0.83) ≈ -0.079  ← 到处都有,IDF 极低
TF-IDF          = 0.150 × (-0.079) ≈ -0.012 ❌  → 直接过滤掉
TF-IDFTFIDF得分
"NLP"0.0300.2230.0067 ✅
"的"0.150-0.079-0.012 ❌

这就是为什么停用词天然被压——不是刻意过滤,是数学决定的 🧮

💡 原理:IDF 取 log 是基于「信息量」的直觉——一个词在越少文档中出现,携带的信息量越大。log 让分值增长变平滑,不至于某个稀有词一枝独秀。

3.2 BM25 算法核心原理(附公式 + 双栏对比)

BM25 = TF-IDF 的进化版。TF-IDF 是自行车,BM25 是电动车 🚲→🛵

核心公式

score(Q,D)=tQIDF(t)TF(t,D)(k1+1)TF(t,D)+k1(1b+bDavgdl)\text{score}(Q, D) = \sum_{t \in Q} IDF(t) \cdot \frac{TF(t, D) \cdot (k_1 + 1)}{TF(t, D) + k_1 \left(1 - b + b \cdot \frac{|D|}{avgdl}\right)}

BM25 的 IDF 变体(做过饱和处理):

IDF(t)=logNdf(t)+0.5df(t)+0.5IDF(t) = \log \frac{N - \text{df}(t) + 0.5}{\text{df}(t) + 0.5}
参数含义默认值调大后果
k1k_1词频饱和控制1.2~2.0高频词影响力↑
bb长度归一化强度0.75长文档惩罚↑

TF-IDF vs BM25 双栏对比

📌 TF-IDF                        📌 BM25
─────────────────────────────────────────────────────
score = TF × IDF                score = Σ IDF · TF_sat
无词频饱和 → 高频词无脑膨胀     有词频饱和 → 边际收益递减
无长度归一 → 长文档天然高分     长度归一 → 公平竞争
无超参可调                      k₁、b 双旋钮适配场景
效果:凑合用                     效果:行业标配 ✅

BM25 的直觉:同样一个词,出现 100 次 ≠ 出现 2 次的 50 倍重要。刷票无效,边际递减 📉

词频   TF-IDF 得分     BM25 得分
────────────────────────────
2次    2 × IDF         2 × IDF × 系数 ≈ 正常
10次   10 × IDF ≈ 5倍  被分母压住 ≈ 2.5倍  
100次  100 × IDF = 50倍  几乎饱和 ≈ (k₁+1) 倍 ≈ 锁死

🕳 踩坑实录:某搜索服务用 TF-IDF 做召回,一篇 5000 字长文反复堆砌关键词,排名常年第一。接入 BM25 后,该文章直接掉到第 7 页——专业作弊 vs 专业反作弊 🤣


四、TF-IDF 工业落地应用 🟢

场景做法一句话
🔑 关键词提取统计 TopK「老板给我提炼行业关键词」→ 跑一下
🔍 简易搜索预计算 TF-IDF,Query 分词后排序2000 年的 Google 就这样 😂
📝 抽取式摘要算单句平均分,取 Top 拼接丐版自动摘要,但真的好使
📊 相似度计算词频向量 + 余弦公式查重、推荐都靠它

余弦相似度公式

cos(A,B)=ABAB\text{cos}(\vec{A}, \vec{B}) = \frac{\vec{A} \cdot \vec{B}}{\|\vec{A}\| \|\vec{B}\|}

比如两篇文章的词频向量夹角为 15°,cos15°0.966\cos 15° \approx 0.966——高度相似 ✅


五、TF-IDF 优缺点及 BM25 改进 🟡

5.1 TF-IDF 优缺点

✅ 优点❌ 缺点
可解释性强——每个词都能追溯分词崩了它就崩
计算飞快,CPU 就够同义词?不认识。「苹果」和「apple」是两个词 🍎
无需标注没词序——「我打你」=「你打我」
可组合复用换领域重算
成本≈一杯奶茶钱长文档天生占便宜

5.2 BM25 改进(公式对比)

原始 TF vs BM25 饱和 TF

TFraw=TF(t,D)TF_{\text{raw}} = TF(t, D)
TFBM25=TF(t,D)(k1+1)TF(t,D)+k1(1b+bDavgdl)TF_{\text{BM25}} = \frac{TF(t, D) \cdot (k_1 + 1)}{TF(t, D) + k_1 \cdot \left(1 - b + b \cdot \frac{|D|}{avgdl}\right)}

TFTF \to \inftyTFBM25k1+1TF_{\text{BM25}} \to k_1 + 1高频词得分被硬上限锁死 🔒

Davgdl|D| \gg avgdl:分母增大 — 长文档被压制 ⚖️

BM25 改进总结:
✅ 文档长度归一化 → 公平竞技
✅ 词频饱和压制 → 禁止刷榜  
✅ 可调超参数 → 适配不同场景
✅ 全面替代 TF-IDF → 行业召回标配

🤔 思考题:极端情况——如果 b=0b = 0,BM25 退化成什么? A. TF-IDF B. 纯词频模型 C. 还是 BM25 提示:b=0b = 0 意味着「完全不考虑文档长度」


六、从文本雏形到词向量演进 🟡

6.1 One-Hot 编码

每个词对应稀疏向量,只有一个位置是 1:

"NLP"=[0,0,0,1,0,...,0](维度=词表大小)\text{"NLP"} = [0, 0, 0, 1, 0, ..., 0] \quad (\text{维度} = \text{词表大小})

它有多离谱 🤦‍♂️:

"猫"  = [1, 0, 0, 0, ..., 0]
"狗"  = [0, 1, 0, 0, ..., 0]
"猫" · "狗" = 0  → 内积为 0 → 完全不相关???
缺陷说明
维度 = 词表大小10 万词 → 10 万维,稀疏到爆炸
零语义「苹果」「iPhone」「水果」互相正交
零扩展性加个新词就得加一维

6.2 文本向量化核心目标

语义相近的词向量空间距离也相近\text{语义相近的词} \xrightarrow{\text{向量空间}} \text{距离也相近}

6.3 Word Embedding

Embedding = 查找表

EmbeddingRV×d,V=词表大小, d=向量维度\text{Embedding} \in \mathbb{R}^{V \times d}, \quad V = \text{词表大小},\ d = \text{向量维度}
One-Hot "NLP" × Embedding矩阵 = Embedding["NLP"]
   [0,0,1,0]        [w11 w12 w13]      [w31 w32 w33]
                     [w21 w22 w23]
                     [w31 w32 w33]     ← 查表第 3[w41 w42 w43]
                    
等价于:直接查表 ✅

这是面试高频题:「One-Hot 乘 Embedding 矩阵,到底是不是矩阵乘法?」答案是:数学上是,工程上是查表

6.4 词向量训练逻辑

① 随机初始化 Embedding
② 设计预训练任务(预测下一个词)
③ 计算损失 → 梯度下降 → 更新 Embedding
④ 重复到收敛

核心靠语言模型自回归:挖掉一个词让模型猜。猜对了就学到语义——学的是词,猜的是上下文,练的是向量 🧠


七、词向量经典训练框架 🟡

Word2Vec 三大训练方式

方法原理一句话
Skip-Gram中间词→猜上下文给「苹果」猜「很好吃」🍎
CBOW上下文→猜中间词完形填空:「_很好吃」→猜「苹果」
🌐 GloVe全局词共现概率比全图视角看词与词的关系

核心超参 🔧:

  • 窗口大小:看多远的朋友圈(默认 5)
  • 词频过滤:太生僻的词不要(默认 min_count=5)
  • 向量维度:128 够用,256 更好,512 真香
  • 迭代轮数:欠拟合 vs 过拟合的博弈

八、神经概率语言模型 🔴(NLP 理论基石)

🎯 大厂面试:2003 年 Bengio 的 NNLM 论文解决了什么核心问题?

2003 年 Bengio 经典论文 NNLM——NLP 深度学习开山之作 🔥

核心贡献

① 首次将神经网络 + 语言模型结合
② 提出「词分布式表示」= 今天的词向量
③ 解决传统 n-gram 的稀疏性和泛化性差

n-gram 的死穴

句子:「我在 798 看了一场 ____ 展览」

n-gram 见过"看了一场画展" → 能猜"画展"
但没见过"看了一场装置艺术展" → 猜不出 ❌

NNLM 知道"装置艺术""画"是类似语境 → 能猜 ✅

这就是词向量的革命性——相似词聚在一起,没见过的组合也能推 🧠


九、静态词向量的核心痛点 🟡

痛点表现扎心实例
📉 无统一评价只能靠下游验证「这个向量好不好?」「跑一下分类」
🔀 一词多义同词不同语境向量不变「苹果很好吃」vs「苹果发新机」→ 向量一样 🍎📱
🔄 反义词尴尬同语境,向量接近「好」和「坏」距离≈0
🔃 不对称A 最似B ≠ B 最似A「医生」最似「护士」,「护士」最似「患者」

这些痛点催生了 ELMo、BERT——动态词向量,但那是另一个故事了


十、延伸拓展知识点 🟢🟡

10.1 句向量构建方式

方案说明评价
🏆 词向量平均全句词向量取平均最笨但最好使,基线首选
🔧 CNN/RNN 池化卷积或循环网络提取有参数能学,但得训练
💰 预训练编码BERT/GPT 直接出效果最好,也最贵

10.2 K-Means 无监督聚类

目标函数(最小化类内距离):

J=j=1Ki=1Nxi(j)μj2J = \sum_{j=1}^{K} \sum_{i=1}^{N} \|x_i^{(j)} - \mu_j\|^2

流程

① 指定 K 值(我要聚几堆?)
② 随机挑 K 个点当老大(质心)
③ 剩下的投奔最近的老大(分配)
④ 老大重算中心(更新)
⑤ 重复③④→老大不动了→收敛 ✅
✅ 优点❌ 缺点
快——10 万条秒级K 值靠猜——「聚 5 类还是 8 类?」
不用标注初始质心选不好,结果天差地别
简单好理解离群点一个带崩全局

💎 全文总结

从分词到词向量的演进逻辑

分词 → 让机器知道"词在哪里"
TF-IDF/BM25 → 让机器知道"哪些词重要"
One-Hot/Word2Vec → 让机器知道"词是什么意思"

这些技术到今天依然是工业落地的基石,也是大厂面试的必考点。老祖宗的东西,学就完事了 💪

互动题答案 👇

  • Q1(FMM 优先切哪个):B. 长的 → 所以词表里「南京市」排在「南京」前面不影响 FMM 结果
  • Q2(b=0 退化):B. 长度归一化被关闭,只剩词频饱和逻辑,不再是标准 BM25

文中所有翻车案例均为真实改编,如有雷同,说明你也遇到过 😅