静态语言模型这个坑,我前后踩了快五年。从最早用独热编码做词表映射,到后来在业务里调 word2vec 的窗口和负采样参数,再到最近折腾 Poincaré Embedding 处理层次化数据——每次以为把原理吃透了,换一个场景就能冒出新的幺蛾子。这篇“深入浅出语言模型”第二篇,我打算把静态语言模型这条线一次讲透:独热编码、TF-IDF、word2vec、FastText、GloVe、Gaussian Embedding、Poincaré Embedding,从数学直觉、工程实现到选型边界都过一遍。适合刚接触 NLP 的初学者建立框架,也适合已经在用预训练模型、但想回头补静态嵌入这块基本功的工程师。
1. 从独热编码开始:最朴素也最容易翻车的表示方法
1.1 独热编码到底解决了什么问题
在深度学习还没普及的时候,计算机处理文本首先要解决“怎么把字/词变成数字”。最直接的思路就是建一个词表,给每个词分配一个唯一的整数 ID,然后再把这个 ID 变成一组向量。独热编码就是其中最简单的一种:假设词表里有 N 个词,每个词对应一个 N 维向量,只有当前词 ID 对应的那一维是 1,其余全是 0。比如词表是“猫”“狗”“鱼”,那么“猫”就是[1,0,0],“狗”就是[0,1,0]。
这个做法的价值在于:它把离散的符号变成了机器学习模型能吃的数值输入,而且整个过程不需要任何语料统计,给词表人工排个序就能生成。早期统计模型、朴素贝叶斯文本分类、甚至一些初版神经网络分类器,都是靠独热编码把输入文本映射成稀疏特征矩阵再喂给模型。
但它的缺陷也摆在明面上。一是维数灾难:词表稍大一点,向量维度就爆炸。一个 10 万词的词表,每个词就是 10 万维,大部分位置还都是 0,存储和计算成本都很离谱。二是完全没有语义信息:“猫”和“狗”在向量空间里没有任何关联,两个向量的余弦相似度恒为 0,模型只能靠后面叠加的复杂结构去强行学习词间关系,学习压力非常大。三是对未登录词毫无办法,词表里没有的词直接无法表示。
我当时第一次用独热编码做商品标题分类,词表只有两万多,但特征矩阵一展开就是几 GB 的稀疏矩阵,跑一次训练要等半天。后来换了 word2vec,不仅内存降了一个量级,准确率也涨了几个点。这个对比让我明白了一件事:表示学习的关键不是“能表示”,而是“表示得节省且有意义”。
1.2 为什么独热编码至今仍有存在感
既然独热编码这么多毛病,为什么现在很多系统里还能看到它的影子?因为它简单、快速、确定性高。在输入层做 Embedding 的时候,其实底层还需要一个“词 ID 到稠密向量”的映射表,而这个映射表的输入本质上就是独热编码或整数索引。你用nn.Embedding的时候,传进去的 token ID 其实就是在做“查表”,而不是真的去构造一个巨大的稀疏向量。另外,在一些类别特征极少的场景,比如情感极性只有正/负/中性,直接独热编码反而比学一个 Embedding 更可控。
所以我的观点是:独热编码不是一个需要彻底抛弃的东西,而是一个需要理解其边界的基础设施。它不承担“学习语义”的责任,只负责“让模型能读取离散符号”。所有后续的静态词向量方法,本质上都是在解决“独热编码里 1 的位置太稀疏、0 的位置没意义”这个问题。
2. TF-IDF:从词频统计到“重要性”加权的过渡带
2.1 TF-IDF 的计算逻辑和直观意义
TF-IDF 是信息检索领域的老牌方法,它由两部分组成。词频(TF)指的是一个词在文档中出现的次数,通常做一次归一化,比如除以文档总词数,避免长文档天然占便宜。逆文档频率(IDF)则是衡量一个词在整个语料中的区分度,计算公式常见形式是IDF = log(N / (df + 1)),其中 N 是文档总数,df 是包含这个词的文档数量。
把两者相乘,就得到一个词的 TF-IDF 权重。直观理解是这样的:如果一个词在文档里频繁出现,说明它对这篇文档很重要;但如果它在几乎所有文档里都出现,比如“的”“了”“我们”,那它的区分度就很低,IDF 会把它的权重压下去。“TF-IDF 值高”意味着这个词既在这篇文档里出现得多,又在整个语料里相对稀有,大概率是这篇文档的主题词。
举个例子,语料里有 1000 篇关于电子产品的评测。在一篇具体评测里,“散热”这个词出现了 20 次,但整个语料只有 30 篇文档提到“散热”,那“散热”的 TF-IDF 值就会很高。反过来,“手机”出现了 30 次,但几乎每篇文档里都有“手机”,IDF 接近 0,最终权重反而不高。这就是 TF-IDF 能用来做关键词提取的原因。
2.2 TF-IDF 在“词向量”语境下的真实角色
严格来说,TF-IDF 并不是一种词向量方法,它产生的权重和独热编码结合后才能形成“向量”。常见做法是把每个词替换成它的 TF-IDF 权重,然后拼成一个和词表等长的稀疏向量,代表一篇文档。这个文档向量在文本分类、文档相似度计算、推荐系统的“物品内容画像”里仍然很常用。
但它作为静态词表示有几个硬伤:其一,词与词之间仍然是独立的,TF-IDF 向量同样无法体现“苹果”和“香蕉”是同类;其二,它依赖于文档级别的统计,同一个词在不同文档里的权重不同,严格说它不是一个“静态词向量”,而是一个“文档+词”的联合表示;其三,它对语料的领域非常敏感,换一个领域的语料,同一个词的 IDF 可能完全不同。
我在做文本匹配时试过把 TF-IDF 向量作为特征,效果在冷启动阶段还不错,因为不需要训练,拿过来就能算相似度。但一旦语料规模上来、同义表达变多,它的天花板很快就到了。后来我把 TF-IDF 和 word2vec 做了加权融合:词向量做主表示,TF-IDF 做文档级加权,两个一结合,比单独用任意一个都稳。这种“统计方法打底+神经方法做主”的思路,在一些线上资源受限的场景里依然值得一试。
面。
3. word2vec:让词向量真正拥有“语义坐标”的里程碑
3.1 核心思想:分布假说与“预测”任务
2013 年 word2vec 横空出世,它把词向量的思路彻底改写了。它基于语言学里的分布假说:意思相近的词,经常出现在相似的上下文里。所以不需要人工标注“苹果”和“香蕉”是否同类,只需要在大规模语料里观察它们的上下文分布,就能学出它们在向量空间里的位置。
word2vec 具体做法是设计两个自监督任务。一个是 CBOW(连续词袋模型):给定上下文词,预测中心词。比如句子“今天天气真不错”,把“今天”“天气”“真”“不错”作为输入,预测中间的“真”。另一个是 Skip-gram:给定中心词,预测它的上下文词。还是这个句子,输入“真”,预测“今天”“天气”“不错”等周围词。训练完之后,神经网络隐层的权重就变成了词向量。
这个设计非常巧妙。它没有直接优化“词和词相似”,而是通过“词能不能预测上下文”这个间接目标,把语义关系编码到向量空间里。训练完成后,“苹果”和“香蕉”的向量会落在相近的位置,因为它们作为中心词时,上下文词往往是“水果”“吃”“买”“甜”这类词。同理,“国王”和“女王”的向量差,会被“男人”和“女人”的向量差拉到一个大致相等的方向,这就是经典的词类比现象。
3.2 CBOW 和 Skip-gram 的工程选型差异
这两个任务看着像孪生兄弟,实际用起来差别不小。CBOW 的训练速度更快,因为一个样本同时看了多个上下文词,更新次数更少,适合语料规模巨大但对精度要求没那么极端的场景。Skip-gram 每次只用一个中心词去预测多个上下文,训练速度慢,但对罕见词和语义精细度的刻画更好。
我给一个表格对比,方便你直接决策:
| 维度 | CBOW | Skip-gram |
|---|---|---|
| 训练速度 | 快 | 慢 |
| 对低频词效果 | 一般 | 更好 |
| 语义精细度 | 相对粗糙 | 相对更好 |
| 典型使用场景 | 海量语料、粗粒度语义 | 中小语料、相似度/类比任务 |
| 窗口敏感度 | 高 | 中等 |
我自己做电商搜索词聚类时,语料大概 5 亿条商品标题,开始用 Skip-gram 跑了一周还没收敛,后来换 CBOW,三天就跑完,聚类效果差距也不大。但做法律文书相似度时,很多专业术语出现频率很低,CBOW 学出来的向量明显不如 Skip-gram,最后老老实实换回 Skip-gram 并把最小词频阈值调低。所以选哪个不只看精度指标,还要看语料里低频词的占比和你的训练预算。
3.3 负采样、层次 Softmax 和超参经验
word2vec 原始论文里为了加速训练,提出了两种优化:层次 Softmax 和负采样。层次 Softmax 利用哈夫曼树把预测所有词的 Softmax 复杂度从 O(V) 降到 O(log V),理论上优雅,但工程实现复杂,对训练框架要求高。负采样则更简单粗暴:每次训练不用对所有词算概率,只随机采样几个负样本参与更新。实践里负采样几乎是默认选择,尤其是大规模语料。
负采样有个关键参数叫“负样本数量”。我常用的范围是 5 到 20,太少模型学不充分,太多训练变慢且边际收益递减。还有一个“采样率”参数,它的作用是丢弃高频停用词,避免“的、了、是”这类词霸占训练样本。初始采样率我一般设 1e-4 到 1e-3,如果发现学出来的向量里功能词和实义词混在一起,就把采样率调高一点。
窗口大小也很有讲究。小窗口(比如 2 到 3)更强调词的语法/近义关系,大窗口(比如 8 到 10)更强调主题/领域关系。做同义词挖掘用小窗口,做文档主题表示用大窗口。向量维度通常 100 到 300 之间,业务语料几百万级别用 100 左右就够,十亿级语料再考虑 300。
训练 epoch 不需要太多,通常 3 到 5 轮就够。我发现很多人以为训练越久越好,实际上 word2vec 多跑几轮后低频词向量会开始过拟合,相似度质量反而下降。
4. FastText:把字符拆分带进词向量训练
4.1 n-gram 子词特征到底解决了什么
FastText 是 word2vec 的升级版,核心改动在于:它不再把一个词当作不可分割的最小单元,而是把词拆成一系列字符 n-gram,再把这些 n-gram 向量加起来作为词的表示。比如“apple”,如果使用 3-gram,会拆成app、ppl、ple,再加上整个词apple本身。如果一个 n-gram 在某个词里出现,就把它对应的向量取出来求和,得到这个词的向量。
这个“子词”设计的最大价值,是解决了 word2vec 对未登录词和罕见词无能为力的问题。词表里没有的词,只要它的字符 n-gram 在训练时见过,就能通过组合这些子词向量得到一个近似的词向量。中文场景下这个优势尤其明显,因为中文的词形变化不像英文那么规则,但字符组合本身携带大量形态信息。比如“阿里”和“阿里巴巴”共享“阿里”这个子串,FastText 能捕捉到这一层关系,word2vec 则完全没有办法。
另一个实际收益是低频词的向量质量。word2vec 对出现次数极少的词,学习不充分,向量基本是噪声;FastText 因为子词之间会共享参数,低频词可以从形态相似的高频词那里借到统计信息,向量的稳定性明显更好。
4.2 训练参数与中文语料的坑
FastText 的训练参数大体继承 word2vec,但多了几个子词相关选项。最重要的两个是minn和maxn,分别控制字符 n-gram 的最小和最大长度。英文里我常用minn=3, maxn=6,中文里我倾向于minn=2, maxn=4,因为中文的信息单位更短,二字词和三字词占比高。
还有一个容易踩的坑:n-gram 字典膨胀。子词候选数量远大于词表数量,如果不做剪枝,内存会飙升。FastText 默认会做哈希桶(hash bucket)限制子词向量总数,默认 200 万。中文语料如果词表特别大,建议把 bucket 调大一点或者提高minn,否则子词哈希冲突太严重,等于随机向量叠加,效果退化。
我在一个中文评论情感分析项目里试过 FastText,模型最后只用了 10 分钟就训练完,效果和当时要跑半天的 word2vec 持平。但在做文本分类时,如果把 n-gram 最大长度调得太大,反而会引入大量只在特定词里出现的噪声子词,分类准确率不升反降。这个度需要自己在验证集上调,不能凭直觉。
4.3 对“OOV”场景的实测体验
之前处理医疗文本时,大量专业术语和缩写是词典里没有的,比如“心脑血管”的各种变体写法。word2vec 碰上这类词直接随机初始化,导致整个句子向量被这些噪声污染。FastText 因为能从“心”“脑”“血管”这些常见子串里拼凑信息,效果好了不止一个档次。
不过 FastText 也不是万能的。如果 OOV 词的字符组合本身也是低频的,比如完全没有见过的乱序缩写,那子词向量相加得到的仍然是一个接近随机的向量。还有一点要注意:FastText 的词向量方向与词本身没有理论保证,叠加后可能出现“两个完全不同的词因为字符相似而向量相近”的假象,这在反义词匹配里尤其危险。所以用 FastText 做相似度任务时,最好结合业务规则过滤明显语义相反的词对。
5. GloVe:把全局共现统计搬上桌面
5.1 原理:共现矩阵与加权最小二乘
word2vec 虽然效果好,但它的训练方式是滑窗式地逐一处理局部样本,本质上只利用了局部上下文信息。GloVe 走的是另一条路:它先统计一个全局的词-词共现矩阵,记录两个词在同一窗口内共同出现的次数,然后用一个加权最小二乘模型去拟合这个矩阵的对数共现概率。
GloVe 的损失函数可以理解为:对于词 i 和词 j,我们希望它们向量的点积能近似等于它们共现次数的对数,再减去两个偏置项。论文里更精细的设计是引入一个权重函数,对共现次数太低的词对降权,避免低频噪声主导训练。这样做的好处是训练过程能同时利用全局统计和局部窗口信息,收敛通常比 word2vec 快,向量在语义相似度和词类比任务上的表现很稳定。
直观类比一下:word2vec 像通过不断抽查局部对话来推测人物关系,GloVe 像先把所有对话里两个人同时出现的频率统计成一张大表,再用整体规律去反推人物在社交空间里的位置。两种方式各有偏科,GloVe 因为看的是全局,对语料整体结构的把握更稳。
5.2 与 word2vec 的差异和选型建议
很多初学者以为 GloVe 比 word2vec 高级,其实二者的效果在多数任务上非常接近,真正的差异在资源消耗和调参风格上。word2vec 的训练时间复杂度与语料规模线性相关,可以用增量式训练方式从海量语料里持续学习;GloVe 需要先构建共现矩阵,这个矩阵在数亿级语料上会非常庞大,内存和预处理时间都不可忽视。
选型的时候,如果语料在几千万到几亿词级别,word2vec 的工程生态更成熟,流式训练方便。如果语料已经离线准备好、规模在千万词以下,而且有充足内存去构建共现矩阵,GloVe 是一个很稳的选择。我在一个 3000 万词的新闻语料上同时训练过两者,GloVe 的收敛速度快了很多,词类比准确率也高一点,但内存峰值是 word2vec 的三倍以上。
5.3 GloVe 训练的两三个注意点
GloVe 的窗口大小对结果影响很大,论文里说对称窗口 10 是常用值,但我在中文语料上试下来,5 到 8 的效果更好。中文信息密度高,窗口太大会把很多无关词拉进共现统计,向量变得黏糊。
另一个注意点是共现矩阵的截断。共现次数低于某个阈值的词对直接丢弃,可以大幅减少矩阵非零项。阈值太低内存爆,阈值太高低频信息全丢。我一般先统计一下共现频次分布,把低于 100 的词对过滤掉,再根据内存情况调整。还有训练迭代次数不用太多,GloVe 论文用的是 50 轮以内,但我在 300 维设置下通常跑 30 轮就收敛了,再往后向量变化很小,纯浪费算力。
6. Gaussian Embedding:把“词义”画成一个分布而不是一个点
6.1 从点到分布:词义不确定性怎么建模
所有前面提到的词向量,本质上都把词映射成高维空间里的一个点。但现实中很多词的语义是不确定的。比如“苹果”可以是水果,也可以是科技品牌;“银行”既指金融机构,也指河岸。一词多义词在不同语境下应该有不同的语义重心,但一个点向量没办法表达这种“范围感”。Gaussian Embedding 的思路是:把每个词表示成一个多维高斯分布,均值向量代表词义的中心位置,协方差矩阵代表语义的模糊程度和方向。
这个想法在概念上很优雅:语义越确定的词,协方差越小,表示它的高斯分布越“瘦高”;语义越宽的词,协方差越大,分布越“扁平”。两个词的相似度不再是点之间的余弦距离,而是两个分布之间的重叠程度或相对熵。
我最初看到这个思路觉得它是花架子,但实际用在词义消歧的入门实验里,发现对“一词多义”的建模确实有效果。比如“苹果”在不同上下文里的嵌入分布,会比 word2vec 的点向量更有解释力,因为你可以在给定上下文后对分布做条件化,实现“动态”调校。
6.2 损失函数和距离度量怎么选
Gaussian Embedding 训练目标分了两派:一类用 KL 散度作为能量函数,一类用期望似然。KL 散度非对称,适合建模“词 A 是词 B 的一种情况”这种包含关系;期望似然对称,适合普通相似度任务。
距离度量的选择会直接影响业务指标。做同义词挖掘时,我建议用对称的似然距离;做词类层级关系学习时,用 KL 散度或它的对称化版本更合理。协方差的参数化也有讲究,为了降低训练难度,常见做法是让协方差矩阵为对角矩阵,或者再用一个低秩结构去近似完整协方差。完整协方差参数太多,小语料根本学不动。
我在一个电商搜索推荐项目里用 Gaussian Embedding 做过一次商品标签扩展,效果比 word2vec 好,但训练时间的开销大了一个量级。主要原因是负采样时要计算分布间的距离,矩阵运算比点积复杂得多。如果你的业务对延迟和训练成本敏感,这个方法的性价比要仔细权衡。
6.3 工程上的坑和心理预期
Gaussian Embedding 最大的坑是训练不稳定。高斯分布均值还好说,协方差矩阵如果初始化不好或者学习率太大,训练过程很容易发散,表现为 loss 突然飙升到无穷大。我踩过最惨的一次是忘记对协方差加 softplus 激活,导致协方差出现负值,整个模型的语义结构直接崩掉。建议初始化均值用预训练的 word2vec 向量,协方差初始化为较小的正数,并加上softplus之类的正数约束。
另一个问题是下游任务适配。很多现成的机器学习模型都是从向量点积或余弦相似度出发设计的,换成分布表示后,下游分类器等模型的输入接口也要跟着改。如果你的团队没有精力改下游系统,建议只在必要场景小范围试点,不要全量替换。
7. Poincaré Embedding:用双曲空间装下层级树结构
7.1 双曲空间的直觉:为什么它能“装下更多”
Poincaré Embedding 是这里面最“反直觉”的一个。它不学欧几里得空间里的点向量,而是把词嵌入到双曲空间(Poincaré 球模型)里。双曲空间有个奇特性质:它的“容量”随半径呈指数级增长,简单说就是越靠近球边界的地方,能塞下的点越多,点与点之间的几何关系也更适合表示树结构。
为什么要强调树结构?因为自然语言里有大量层级关系。比如“动物”下面是“哺乳动物”“鸟类”,“哺乳动物”下面又是“猫”“狗”。在欧几里得平面里表示一棵大树,随着层数增加,高层节点想要把它们的孩子“推开”到互不重叠的位置,需要的空间会指数膨胀。但双曲空间天生适合这种结构,它用一个低维空间就能近似嵌入一棵巨大的层级树,而且层级越深的节点,向量越靠近球边界。
7.2 距离公式与训练要点
Poincaré 球模型里的点是一个在单位球内部的向量,任意两点 u 和 v 的双曲距离公式可以写成:
d(u, v) = arcosh(1 + 2 * ||u - v||^2 / ((1 - ||u||^2) * (1 - ||v||^2)))
注意分母里的(1 - ||u||^2),这意味着当向量范数趋近 1 时,距离会快速变大。这个设计带来了一个很有意思的现象:根节点通常靠近球心,叶子节点被推向球边界,层级关系通过“范数大小”和“角度关系”一起编码。
训练时常用 ranked loss,也就是让正样本的距离尽量小,负样本的距离尽量大。负样本的采样非常关键,不能简单均匀采样,而要偏向采样“在树结构上接近但实际不是邻居”的节点,否则模型学不到层级区分度。我试过用随机负采样,结果模型只是把所有词推到一起,loss 降不下来,后来换成按层级深度分层采样,效果才正常。
7.3 适用场景和实际效果笔记
Poincaré Embedding 最典型的应用是知识图谱嵌入、词汇层级结构学习、分类体系构建。我在一个商品类目树的表示学习任务里用过它:把 3 万多个类目节点嵌到 50 维双曲空间里,效果比同维度的欧几里得嵌入有明显提升,类目之间的上下位关系判断准确率高了十几个点。
需要提醒的是,双曲空间的向量不能直接用欧几里得空间的操作去处理。比如你不能直接对两个 Poincaré 向量做加减法,也不能用普通余弦相似度去算它的相似度,必须用双曲距离。下游模型如果要接这种向量,需要单独实现双曲神经网络层,工程复杂度比普通词向量高不少。
如果你的任务本质是平面语义相似度,没有明显的层级结构,用 Poincaré Embedding 大概率不会有什么提升,甚至因为距离度量不直观而更难调试。它是个好工具,但只在结构匹配的场景里才是好工具。
8. 静态嵌入全景对比与工程选型
8.1 核心维度横向对比
把上面这些方法放在一张表里,看差异会更清楚:
| 方法 | 表示形式 | 语义来源 | 未登录词处理 | 训练复杂度 | 典型场景 |
|---|---|---|---|---|---|
| 独热编码 | 稀疏点 | 无 | 不支持 | 无训练 | 类别特征、词表映射 |
| TF-IDF | 稀疏加权点 | 文档统计 | 不支持 | 极低 | 关键词提取、冷启动相似度 |
| word2vec | 稠密点 | 局部上下文 | 不支持 | 中 | 语义相似度、词聚类 |
| FastText | 稠密点+子词 | 局部上下文+字符形态 | 支持 | 中低 | 文本分类、OOV 场景 |
| GloVe | 稠密点 | 全局共现统计 | 不支持 | 中高(内存) | 词类比、语义空间分析 |
| Gaussian Embedding | 高斯分布 | 上下文不确定性建模 | 不支持 | 高 | 词义消歧、不确定性量化 |
| Poincaré Embedding | 双曲空间点 | 层级结构建模 | 不支持 | 中高 | 知识图谱、类目树 |
8.2 选型建议:先问自己要解决什么问题
如果是做文本分类、聚类这种“只要一个还不错的输入表示”的任务,word2vec 是默认选择,FastText 会在有 OOV 和低频词问题时更优。如果是做关键词提取或文档相似度冷启动,TF-IDF 简单可靠,不需要训练。如果是处理知识图谱、上下位关系,Poincaré Embedding 性价比突出。如果业务明确要求处理一词多义的不确定性,可以试点 Gaussian Embedding,但一定要做好工程改造准备。
还有一个容易忽略的点:静态词向量和预训练语言模型的关系。现在大模型时代,很多人觉得静态嵌入已经过时。但实际业务中,静态嵌入依然有不可替代的作用:它们轻量、可离线更新、可解释性强,而且能作为大规模检索的第一层召回。我做过一个召回系统,底库有一亿条文本,先用静态向量做粗排召回,再用深度模型精排,效果和延迟都兼顾了。静态嵌入不是被淘汰,而是退到了更合适的位置。
9. 训练与调参的常见问题排查实录
9.1 训练不收敛或 loss 异常
训练 word2vec/FastText 时,最常遇到的是训练过程正常但向量质量差,表现为相似度结果不合理。首先检查语料预处理:是否统一小写、是否过滤了噪声符号、是否保留足够的上下文句子边界。其次是检查高频停用词是否被采样掉,如果没有,功能词会占据大量训练样本,实义词学不好。还有一个容易忽略的点:是否混入了大量重复文本。我在一个爬虫语料里踩过坑,重复标题占了 30%,导致“热门标题”里的一批词被错误地聚在一起。
9.2 相似度结果不合理的排查思路
如果两个明显不相关的词向量相似度接近 1,先怀疑窗口太大或者负样本太少。窗口太大让远距离的词被拉进上下文,负样本太少让判别信号不足。如果两个明显同义的词相似度很低,先怀疑它们的共现频次是否太低,低频词在 word2vec 里本来就学不好;这种情况换 FastText 或调低min_count可能有帮助。
Gaussian Embedding 出现相似度异常,先检查协方差是不是训练成奇异值了。可以用验证集监控协方差的数值范围,如果出现极端值,果断加正则化或者回退到对角协方差。Poincaré Embedding 的相似度不能用余弦,我见过有人拿双曲向量直接做余弦聚类,结果一塌糊涂,这是最容易犯的操作性错误。
9.3 训练资源不够的降级方案
如果数据量太大、内存吃紧,可以先采样一部分语料训练一个临时向量,再以它为初始化在全部语料上增量训练。这个方法我在 word2vec 上用得很顺手,收敛速度比冷启动快很多。GloVe 的共现矩阵如果太大,可以用内存映射的方式把矩阵落盘,或者把共现统计做成多机 MapReduce 的方式预处理。对于 Poincaré Embedding,双曲空间维度不用设太高,32 到 64 维往往就能装下很大规模的结构,不要一上来就选 300 维。
最后再分享一个体会:静态语言模型的每个方法,都像是在“用什么样的几何结构去表达语言”这件事上做的一次尝试。有的用点,有的用分布,有的用双曲空间,没有哪个在所有场景里都完胜。做工程的人最忌讳拿着一个方法套所有问题。先拆清楚你的数据长什么样、你的目标结构是什么,再去选嵌入方式,往往比自己闷头调参有效得多。这套基本功打扎实了,后面看动态模型、上下文表征、大模型时代的各种 trick,也会轻松很多。