词向量与嵌入,文字是怎么变成数字的

2026-04-15AI 助手

之前参加了一个 Hackathon,做的是中文短文本分类。当时我跟队友说,我们直接用 TF-IDF 做特征提取,然后丢给 SVM 不就行了?队友看了我一眼说:"你没做过 NLP 吧?"

被他说中了。

后来我才发现,TF-IDF 这种基于词频的方法有一个根本问题:它把每个词当作独立的符号,完全忽略了词与词之间的语义关系。"苹果"和"香蕉"在 TF-IDF 眼里是两个毫无关联的词,但人知道它们都是水果。

词嵌入(Word Embedding)解决的就是这个问题:把词映射到一个连续的向量空间里,语义相近的词在空间中挨在一起。

从 One-Hot 开始的问题

最早的文字表示方法是 One-Hot 编码。假设你有 10 万个词的词表,每个词用一个 10 万维的向量表示,只有当前词对应的维度是 1,其他全是 0。

"苹果" = [0, 0, 0, 1, 0, ..., 0] "香蕉" = [0, 1, 0, 0, 0, ..., 0]

这个做法有两个致命问题。

第一,维度灾难。10 万维的向量,存储和计算都是灾难。如果你有 100 万篇文章,每篇文章用 1000 个词的 One-Hot 向量表示,那你的特征矩阵是 100 万 × 10 万,对内存来说是天文数字。

第二,语义鸿沟。任何两个不同的词,One-Hot 向量之间的内积都是 0 — 因为它们只在不同的维度上是 1。这意味着"苹果"和"香蕉"的距离跟"苹果"和"汽车"的距离完全一样。计算机完全不知道哪些词是相关的。

降维是解决第一个问题的常见思路。你用 SVD 或者 PCA 把 One-Hot 降到几百维,第二个问题也会有所缓解——因为降维过程中相似的词会被投影到相近的位置。但这不是最好的办法。

Word2Vec 的关键洞见

Mikolov 在 2013 年提出的 Word2Vec 有一个很聪明的出发点:一个词的含义可以由它周围的词来定义

这句话是英国语言学家 Firth 在 1957 年说的原话,原文是 "You shall know a word by the company it keeps"。Word2Vec 把这个语言学观点变成了一个可以训练的神经网络。

Word2Vec 有两种架构。

CBOW(Continuous Bag of Words):用上下文预测中心词。输入是"我__苹果"中"我"和"苹果"的 One-Hot,预测中间的词。猜猜中间是什么?可能是"吃",也可能是"买"。上下文的窗口大小决定了模型能学到的信息量。

Skip-gram:用中心词预测上下文。输入是"吃",预测它周围可能出现什么词——"我"、"苹果"、"一个"、"了"等等。

Skip-gram 在小型数据集上比 CBOW 表现好,但训练速度慢。CBOW 更快,但对罕见词的处理不如 Skip-gram。

Word2Vec 的训练结束后,我们会得到一个权重矩阵——它就是词向量表。每个词对应一个 300 维(常见配置)的向量。这 300 个维度的值不是人能理解的(不像 HSL 那样每个维度有明确的视觉含义),但它们确实编码了语义信息。

那个经典的向量运算

Word2Vec 最让人印象深刻的是它学到的向量能做类比推理:

vec("国王") - vec("男人") + vec("女人") ≈ vec("女王")

当时看到这个结果的惊讶我到现在还记得。一个在无标注文本上训练出来的模型,竟然学会了"国王是男性统治者,去掉男性部分加上女性部分,就得到女性统治者"这种逻辑。

用数学语言说:向量 King 和 Queen 的差,与向量 Man 和 Woman 的差,在方向上是相近的。换句话说,模型学到了"性别"这个语义维度。

但别对这个太兴奋。2016 年有人系统性地测过这个类比任务,发现准确率其实在 60-75% 之间。一些明显的类比(比如"东京-日本+中国"应该等于"北京")会失败,因为模型没有真的理解"首都"这个概念——它只是统计了文本中词的同现模式。

而且词向量的偏见问题更严重。训练语料里工程师这个词如果跟"男性"相关的词出现频率高,词向量就会学到"工程师≈男性"的关联。2017 年 Bolukbasi 等人的研究明确指出了这一点,后来也催生了一系列"去偏见"方法。但这是后话了。

GloVe:加了全局统计的改进

Word2Vec 只利用局部上下文窗口的信息。GloVe(Global Vectors)在此基础上加了一个重要的补充。

GloVe 的核心是构建一个"词-词共现矩阵"。意思是:统计在整个语料库中,每个词和另一个词同时出现在一个上下文窗口里的次数。

这个矩阵非常大(词表大小 × 词表大小),但 GloVe 不直接用它做特征,而是通过矩阵分解来学习词向量。它的损失函数设计得很巧妙——直接对共现概率比建模。

具体来说,GloVe 要学的不是"词A和词B同时出现了多少次",而是"词A和词B之间的关联比词A和词C之间的关联强多少"。这个比值比绝对计数更稳定,也更反映语义。

GloVe 的一个实际好处是:它训练出来的词向量在词汇类比任务上的表现通常比 Word2Vec 更稳定。但代价是构建共现矩阵需要一次性统计整个语料库,对超大语料来说内存开销很大。

中文词嵌入的特殊问题

上面说的这些方法对英文表现都不错,但在中文里会遇到一些额外的问题。

分词问题。 英文天然有空格分词,中文需要先切词。"中国人民银行"应该是一个词还是三个词?不同的分词器结果不同,词向量的质量因此受影响。我试过用 jieba 分词的词向量和用 HanLP 分词训练的对比,在语义相似度任务上能差 5-8 个百分点。

未登录词。 你训练的词表不可能覆盖所有词。新词、网络流行语、专业术语,在训练好的词向量里找不到对应向量。解决方案是用 subword 信息(比如 FastText 的做法)——把词拆成字符级别的 n-gram,即使没见过的词也能拼出它的向量。

字 vs 词。 中文单字本身就有含义("人"、"大"、"小"),而英文的字母没有独立含义。所以中文可以用字向量代替词向量。字向量的好处是不需要分词,但会丢失词级别的语义。通常做法是把字向量和词向量拼接起来用。

我到实践中遇到的问题

第一个项目里我用 Word2Vec 做电商评论的情感分类。训练语料是 50 万条商品评论。训练出来的词向量看起来不错,"质量"和"品质"离得很近,"快递"和"物流"也挨在一起。

但有两个问题让我卡了很久。

第一,否定词和它修饰的词向量距离太远了。"不好"和"好"在向量空间里可能是很远的点,因为"不"出现在"好"前面,窗口太小就看不到这个关系。解决办法是设置更大的上下文窗口(比如 window=10),这样模型能学到长距离依赖。但窗口太大又会引入噪声。

第二,稀有词的问题。出现次数少于 5 次的词,训练出来的向量质量很差——因为它们缺乏足够的上下文样本。我的处理方式是:对这些低频词不做词向量训练,而是直接用字符级别的 n-gram 替代。

上下文嵌入的演进

Word2Vec 和 GloVe 有一个共同的限制:每个词只有一个固定的向量。"苹果"这个词在"吃苹果"和"苹果公司"里含义不同,但 Word2Vec 只会给它一个向量。

2018 年的 BERT 和后来的 GPT 改变了这一点。它们不做"词嵌入",而是做"上下文嵌入" — 同一个词在不同的句子里会有不同的向量表示。BERT 说"苹果"在"吃苹果"里的向量和在"苹果公司"里的向量应该是不同的。

但这又引出了新的问题:BERT 的嵌入维度是 768(base)/1024(large),而且每个位置都要计算,对大段文本来说计算量很大。所以工业界的常见做法是混合使用:先用轻量的 Word2Vec 做初筛,再用 BERT 做精细的语义匹配。

如果你在看一些 AI 工具(比如 AI 摘要和 AI 翻译),它们背后的文本理解几乎都是基于上下文嵌入做的。词嵌入只是第一个台阶,在这之上还有注意力机制、Transformer 等更复杂的结构。AI 文本摘要工具就是用这些技术从长文本中提取核心信息——但也需要词嵌入这种底层表示作为基础。

写在最后

词嵌入把离散的文字符号变成了连续的数字向量。这个转变让深度学习可以在文本数据上发挥作用。从 One-Hot 到 Word2Vec,从 GloVe 到 BERT,每一次迭代都在解决上一个方法的问题,但每个方法也都有自己做不到的事。

如果你刚开始接触 NLP,不要一上来就跳进 Transformer 的汪洋大海。从词嵌入开始理解"文字怎么变成数字"这件事,后面的架构才会显得自然。