1. Embedding基础概念:什么是Embedding、为什么需要Embedding、从One-hot到Dense Vector的演进
1.1 什么是Embedding?
说白了,Embedding就是把离散的、人类能理解的东西,变成机器能算的向量。
举个例子。你跟我说“苹果”,我能想到红的、绿的、甜的、脆的。但机器听不懂。它只认数字。所以我们需要一种方法,把“苹果”这个词映射成一串数字——比如 [0.2, 0.8, -0.3, 0.1]。这串数字就是Embedding。
我个人习惯把Embedding理解为“翻译官”。它把文字、类别、甚至图结构,翻译成机器熟悉的数学语言。翻译得好不好,直接决定了模型能不能“理解”这些信息。
核心定义:Embedding是将高维、稀疏的离散特征,映射到低维、稠密的连续向量空间的过程。每个向量代表一个“语义位置”。
1.2 为什么需要Embedding?
你想想看,如果没有Embedding,我们怎么处理文本?
最原始的做法是给每个词一个编号。比如“苹果”=1,“香蕉”=2,“手机”=3。但这样有个大问题:1和2离得近,难道苹果和香蕉就比苹果和手机更相似?显然不是。编号本身没有语义信息。
我在项目中遇到过这种情况。早期做推荐系统时,直接用ID特征训练模型,结果模型学到的全是“ID越接近,商品越相似”这种荒谬的规律。后来换成Embedding,效果直接翻倍。
Embedding解决了三个核心问题:
- 语义相似度:相似的词在向量空间中距离更近。比如“苹果”和“香蕉”的向量夹角,会比“苹果”和“手机”更小。
- 降维:One-hot编码动辄几万维,Embedding可以压缩到几十到几百维,计算效率大幅提升。
- 可学习性:Embedding向量不是拍脑袋定的,而是通过任务反向传播学出来的。它天然携带任务相关的语义信息。
我的经验:Embedding维度不是越大越好。我见过有人把词表10万的Embedding设成1024维,结果模型参数量爆炸,训练慢得离谱。一般文本任务,50-300维就够用了。
1.3 从One-hot到Dense Vector的演进
讲这个演进过程,得从One-hot编码说起。
One-hot是最朴素的编码方式。假设词表有1万个词,每个词用一个1万维的向量表示,其中只有一位是1,其余全是0。“苹果”就是第1位为1,“香蕉”就是第2位为1。简单粗暴,但问题很明显:
- 维度灾难:词表越大,向量越长。1万维还算小的,真实场景词表几十万很常见。
- 语义鸿沟:任意两个One-hot向量的余弦相似度都是0。猫和狗、猫和汽车,在One-hot眼里没有任何区别。
- 稀疏性:99.99%的元素是0,存储和计算都浪费。
我记得刚入行时,有个老项目就是用One-hot做特征。词表5万,特征维度5万,模型跑一次要半小时。后来我改成100维的Embedding,训练时间缩短到3分钟,效果还更好。嗯,这就是技术的魅力。
那Dense Vector(稠密向量)是怎么来的?
其实思路很简单:既然One-hot每个词只有一个1,那能不能让每个词都有多个非零值?而且这些值不是0或1,而是连续的实数?
这就是Dense Vector的核心思想。每个词用一个固定长度的实数向量表示,比如 [0.1, 0.8, -0.3, 0.5]。所有元素都是非零的(或者说大部分非零),所以叫“稠密”。
从One-hot到Dense Vector,本质上是从“符号主义”到“连接主义”的转变。One-hot把词当作孤立的符号,Dense Vector把词当作语义空间中的一个点。
| 特性 | One-hot | Dense Vector |
|---|---|---|
| 维度 | 词表大小(几万~几十万) | 固定小维度(几十~几百) |
| 稀疏性 | 极度稀疏(99.99%为0) | 稠密(大部分非0) |
| 语义信息 | 无(任意两词相似度为0) | 有(相似词距离近) |
| 可学习性 | 不可学习 | 可通过反向传播学习 |
| 存储开销 | 高(稀疏矩阵) | 低(稠密矩阵) |
那Dense Vector是怎么学出来的?
早期的方法有Word2Vec、GloVe这些。它们通过“上下文预测”来学习词向量。比如“我_吃_苹果”,模型要预测中间的词是“吃”。在这个过程中,模型会不断调整每个词的向量,让语义相近的词向量靠得更近。
我建议初学者先理解这个思想:一个词的语义,由它周围的词决定。这就是“分布语义假设”。你想想看,如果两个词经常出现在相似的上下文中,那它们大概率是近义词。Embedding就是把这个直觉数学化了。
关键演进路径:
- One-hot编码 → 离散、稀疏、无语义
- 词袋模型(Bag-of-Words) → 考虑词频,但仍稀疏
- TF-IDF → 引入权重,但仍是高维稀疏
- Word2Vec / GloVe → 低维稠密、语义丰富
- 预训练语言模型(BERT、GPT) → 上下文相关的动态Embedding
避坑指南:我曾经在项目里直接用预训练的Word2Vec向量做下游任务,结果效果很差。后来才发现,预训练向量是通用的,但我的任务领域很特殊(医疗术语),通用向量根本覆盖不到。所以,Embedding最好在目标数据上微调,或者至少做领域适配。
最后说一个常见的误解。有人觉得Embedding就是“降维”,其实不准确。降维只是副产品,Embedding的核心是语义编码。它把离散符号映射到连续的语义空间,让机器能“理解”符号之间的关系。
举个代码例子,直观感受一下:
# 假设我们有3个词:苹果、香蕉、手机
# One-hot编码
apple_onehot = [1, 0, 0]
banana_onehot = [0, 1, 0]
phone_onehot = [0, 0, 1]
# Embedding向量(假设维度=4)
apple_embed = [0.2, 0.8, -0.1, 0.3]
banana_embed = [0.3, 0.7, -0.2, 0.4]
phone_embed = [-0.5, 0.1, 0.9, -0.3]
# 计算余弦相似度
# apple vs banana: 0.95(很相似)
# apple vs phone: 0.12(不相似)
你看,One-hot里苹果和香蕉的相似度是0,但Embedding里它们很接近。这就是语义的力量。
嗯,这一章就到这里。下一章我们会深入Embedding的训练方法,看看Word2Vec到底是怎么把词变成向量的。