资讯动态

Word2vec与Gensim实战:加载预训练模型并测试词向量相似度

发布时间:2026/10/17 10:46:56 来源:尧图企业网站定制
自然语言处理项目里第一步通常都是把纯文本变成模型能吃的数字特征。我一开始做文本分类时用过字频、TF-IDF效果只能说能用可一旦涉及同义替换、语义相近的新词特征完全不给力。后来接触到 Word2vec 模型配合 Gensim 库去加载和使用词向量算是把这个问题撕开了一个口子。今天这篇是 Gensim 库使用系列里 Word2vec 模型部分的第一篇主要做两件事用比较容易懂的话介绍模型的核心思路然后直接上手加载一个别人训练好的预训练模型跑相似词测试。学完之后你可以自己写几十行代码让模型告诉你哪些词和“电脑”关系最近也能做“警察-罪犯医生”这类词向量加减法。适合刚入门自然语言处理、或者想快速验证词向量效果的人。1. 先搞懂 Word2vec 到底在学习什么为了避免一上来就写代码先花几分钟把原理部分捋清楚。因为加载完预训练模型后你大概率会遇到“为什么这两个词相似度这么高”“为什么这个词不在词表里”这类问题如果没有原理打底很容易被结果带偏。1.1 词的数字化从 One-Hot 到分布式表示计算机不认识“猫”和“狗”这样的词它只认数字和向量。传统方法是 one-hot 编码先给词表里每个词分配一个唯一编号然后把这个编号翻译成向量。例如词表是 10 万个词第 1 个词可能是[1, 0, 0, ...]第 2 个词是[0, 1, 0, ...]。向量长度等于词表大小每个词只有一位是 1其余全是 0。这种表示的优点是逻辑简单但缺点非常致命维度爆炸词表越大向量越长。10 万维的向量存 10 万词就是上百亿个浮点数实际项目里根本没法直接用。语义真空两个词不管意思多接近它们对应的 one-hot 向量都只有一个位置不同其他全是 0也就是正交的。用向量内积计算相似度的话“猫”和“狗”的相似度是 0和“猫”与“汽车”的相似度没有任何区别。为了解决这个问题大家开始用低维稠密向量来表示词。这种向量一般只有 100 到 300 维每一维的值不是一个非 0 即 1 的开关而是一个可正可负的浮点数。于是规则变了向量里没有一个维度能单独解释成“是否是动物”但多个维度组合起来可以让意思相近的词在向量空间中距离更近。这就是分布式表示Distributed Representation也是 Word2vec 模型输出的核心。我一般用一个不太严谨但很好理解的类比one-hot 像是给每个词发一个独立的编号编号之间没有任何关联分布式表示像是给每个词填一份性格问卷问卷里有很多候选问题词会根据自己的语义特征在每一题上打分。两个词答案越接近语义就越像。Word2vec 输出的正是这种“性格问卷”式的词向量。1.2 两条训练路线CBOW 和 Skip-gramWord2vec 的训练思路来自一个很朴素的语言学假设一个词的含义是由它周围的词决定的。比如在“我 ___ 苹果”这样的句子里空位处填“吃”还是“玩”我们知道“吃”更合理这是因为你观察过很多语料里“苹果”总是和“吃”“咬”“尝”这类词一起出现。模型需要学习的就是从这些共现关系里把词的含义压缩成向量。为了学到这种关系Word2vec 提供了两个方向相反的任务CBOWContinuous Bag-of-Words拿上下文词去预测中心词。训练时把中心词前后窗口内的词向量取平均或求和去预测中心词本身。它像做填空题看到“我 _ 苹果”猜中间是什么词。Skip-gram反过来拿中心词去预测它周围的上下文词。训练时把中心词作为输入预测窗口内的每一个上下文词。它更像做扩写题看到“吃”这个词猜它身边可能会出现“苹果”“饭”“药”这些词。两类模型在训练目标上互为逆向但使用场景稍有区别。CBOW 训练速度更快对高频词更友好Skip-gram 对低频词和语义细节的刻画更好因此在大规模语料上Skip-gram 通常表现更稳。使用的时候常见的做法是设vector_size100~300向量维度window5上下文窗口大小min_count1~5忽略低频词。这些参数在加载预训练模型时要特别注意因为它们直接决定了词向量的维度和词表覆盖范围。顺便说一句训练过程中真正用的是神经网络吗是但它的结构很浅核心任务就是让模型在预测词语时输出正确结果。训练完成之后输出层会被扔掉只保留输入词的向量映射表。也就是说最终我们拿到的是副产品一个把词映射成向量的查找表而并不是一个可以用来做分类的深度网络。把“训练模型”理解成“学习词向量”可能更准确。1.3 为什么通常先加载预训练模型而不是自己训练其实很多讨论 Word2vec 的教程一上来就会教怎么准备语料、怎么训练、怎么调参这容易让人形成一种错觉我得先有几十 GB 语料才能用 Word2vec。真实情况是在很多应用场景里加载别人训练好的预训练模型是更快的路子。原因有几个。第一高质量词向量语料要求量大、干净、领域匹配。通用领域要做得像社区里那些广泛流传的预训练向量一样往往需要数十亿甚至更大规模的文本普通个人或团队很难复现。第二训练非常耗时耗资源。虽然 Word2vec 在 CPU 上也能跑但在百万词表、大语料上训练几轮仍然要数小时甚至更久。第三很多下游任务只需要一个通用的语义初始化。比如做文本分类、推荐召回、关键词扩展加载通用预训练词向量已经足够自己从头训练反而没必要。不过“加载预训练”也不是完全没有成本。你要先确认三件事词向量维度是否适合下游模型词表是否覆盖你的业务词汇训练语料的领域是否与你的文本接近。新闻语料上训练出来的词向量直接拿到医疗或法律文本上对专有名词和行业术语的表示能力会明显下降。这个后续排查部分会再展开。2. 加载预训练模型之前先弄清文件格式和 Gensim 的接口第 1 节讲的是模型原理这一节开始切换到工具层面。我在使用 Gensim 库时最大的感受是它把加载词向量的操作封装得极简但使用门槛其实转移到了“文件格式”和“数据来源”上。2.1 Gensim 库在整个流程里扮演什么角色Gensim 是一个以文本处理为核心的 Python 库内置了 Word2vec、Doc2vec、主题模型等常见算法以及大量语料处理工具。它最大的特点是流式处理和内存友好训练语料可以被封装成可迭代对象不用一次性把所有文本都塞进内存。在“加载预训练模型”这个任务里Gensim 承担的是解析和查询两件事。解析指的是读取各种格式的向量文件把文本格式或二进制格式的词向量还原成一个内存里的KeyedVectors对象查询指的是对外提供most_similar、similarity、doesnt_match这样的方法方便直接测试向量质量。有些人喜欢直接用自己的深度学习框架加载词向量从技术上说没问题只是你需要自己写层与层之间的嵌入初始化、自己维护词表、自己处理 OOVout-of-vocabulary词表外词。用 Gensim 库的好处是这些工作被封装在统一接口里对原型验证阶段非常友好。2.2 常见预训练文件格式与识别技巧在下载预训练模型时我们最常碰到两类格式Word2vec C 工具格式也叫 word2vec 文本格式或二进制格式。文件第一行通常是“词表大小 向量维度”例如3000000 300剩下部分每一行是一个词和它的向量数值。文本格式的向量数值以空格分隔肉眼可以直接看二进制格式的向量部分则是一串浮点数二进制数据用文本编辑器打开会出现乱码但第一行依然是可读的文本。Gensim 原生模型格式。这种文件是 Gensim 自己用save()方法保存的如果保存的是完整模型里面不仅有词向量还有训练参数、状态信息等如果保存的是KeyedVectors则主要是词向量矩阵。这类文件不能用load_word2vec_format来读要用Word2Vec.load或KeyedVectors.load。所以拿到一个.bin文件先别急着指定binaryTrue去加载。更稳妥的判断方式是先用文本方式打开文件头部或者用程序读第一行观察它是否满足“词表大小 向量维度”的格式。那种完全不可读的乱码开头不代表是二进制词向量也可能是一个经过压缩或序列化的模型文件。为了便于记忆我做了一张简单对照表文件表现大概率格式推荐加载方法第一行是“词表大小 词向量维度”后面每行是“词 数值数值...”word2vec 文本格式load_word2vec_format(path, binaryFalse)第一行是“词表大小 词向量维度”随后内容是乱码word2vec 二进制格式load_word2vec_format(path, binaryTrue)用文本编辑器打开整体乱码且体积较大Gensim 原生保存文件Word2Vec.load(path)或KeyedVectors.load(path)这是一个很实用的小抄我加载各种预训练文件时都是按这个思路判别的。2.3 环境准备安装 Gensim 并确认版本开始测试前先把环境准备好。建议使用 Python 3.8 或更高版本。安装 Gensim 库只需要一行命令pip install gensim如果只想升级可以加-U参数。装完以后在交互式命令行里验证import gensim print(gensim.__version__)我在实际使用中版本差异带来的坑主要集中在 API 参数上。比如旧版本里size参数用来指定向量维度新版本则改名为vector_size有些老教程还会写model.most_similar新版本更推荐先拿到model.wv再通过model.wv.most_similar调用。如果你看到代码运行时报TypeError或者找不到参数第一反应应该是查一下当前 Gensim 版本对应的 API 文档而不是怀疑模型文件坏了。另外加载词向量文件时底层依赖数值计算库安装 Gensim 时会自动装上。如果你想测试后面提到的可视化部分还需要准备绘图工具和降维工具。环境准备部分其实不需要太多重点是把“能import gensim”这件事跑通。3. 实操加载预训练模型并完成第一轮相似词测试现在进入正题。假设你已经下载好了一个预训练词向量文件例如某通用新闻语料训练出来的词向量文件名叫word2vec.bin大小在几百 MB 到几个 GB 不等。下面所有代码把路径换成你自己的实际路径即可。3.1 用 KeyedVectors 加载 Word2vec 格式文件在 Gensim 库中词向量查询的主要对象是KeyedVectors。这个名字很容易理解一个把词语和向量关联起来的键值映射表。加载 word2vec C 工具格式的代码是from gensim.models import KeyedVectors path pretrained/word2vec.bin kv KeyedVectors.load_word2vec_format( path, binaryTrue, unicode_errorsignore ) print(词表规模:, len(kv)) print(向量维度:, kv.vector_size)第一行是导入对象第二行指定文件路径然后调用load_word2vec_format。参数binaryTrue告诉 Gensim 这是一个二进制格式的文件如果你的文件是文本格式就把这个参数改成False。unicode_errorsignore的作用是遇到无法解码的字符时忽略而不是报错对英文预训练词向量尤其管用因为很多大词表里会混入奇怪的 Unicode 字符。加载成功后会得到一个KeyedVectors实例。len(kv)返回词表中包含的词数kv.vector_size返回每个词的向量维度。这两个值很重要它们决定了下游模型输入层的尺寸。如果下载的文件特别大可以先只加载一部分试试接口。例如kv KeyedVectors.load_word2vec_format(path, binaryTrue, limit500000)limit参数表示只读取前 50 万个词。这在小内存机器上非常实用先验证流程通不通再决定要不要加载完整词表。3.2 跑第一个测试查询最相似的词加载完成后最直观的测试就是most_similar。它返回与目标词余弦相似度最高的若干词每个结果是一个(word, score)的元组score越接近 1 表示两个向量方向越接近语义越相似。for word, score in kv.most_similar(computer, topn10): print(f{word}: {score:.4f})这里我用computer作为测试词。跑出来的前几个词大概率会是“computers”“laptop”“desktop”“computing”这类语义相关的词。当然具体结果取决于你加载的预训练模型语料如果你用的是中文语料训练的词向量测试词就应该是中文词比如“电脑”。要注意返回结果默认不会包含查询词自己topn控制返回多少个候选词。我看到很多初学者会纠结于相似度得分的高低。其实在词向量里0.5 到 0.8 的相似度已经算比较高的水平不同语料和不同维度会有差异不必强行要求两个近义词的分数接近 1。你需要关注的更多是排序前几个词是否在语义上确实相关。3.3 类比推理测试词向量的加减法更考验质量相似词测试只能说明“它们比较接近”。要真正检验词向量的语义判断能力更常做的是类比推理也就是让向量做加减法result kv.most_similar( positive[king, woman], negative[man], topn1 ) print(result)这段代码的含义是把king的向量加上woman的向量再减去man的向量和这个结果向量最接近的词会被返回。如果模型效果理想得到的会是queen这个词。这里的逻辑是词向量里“王室”和“性别”被分解成不同的语义维度“国王”与“王后”的差别主要由性别维度贡献而加减操作能把这个维度单独提取出来。这不是什么魔法它只是说明向量加法能够捕捉语义组合关系。你完全可以用自己的词去试例如把positive和negative换成一组业务词看看模型会不会返回预期中的组合。只要观测几个组合结果就能快速判断这个预训练模型在目标语言上的质量。在实际测试时我通常会组合多个正面词和负面词比如positive[computer, keyboard], negative[screen]这样能看出模型有没有掌握“外设-部分”这类关系。需要提醒的是类比推理结果会受到训练语料影响词频低或语义模糊的词容易得出完全意料之外的结果这并不代表代码写错。3.4 另一种加载姿势加载 Gensim 原生模型文件上面用的是load_word2vec_format适合加载外部工具导出的词向量文件。如果别人传给你的模型是 Gensim 自己保存的完整 Word2vec 模型代码会有些不同from gensim.models import Word2Vec model Word2Vec.load(pretrained/my_model.model) wv model.wv print(wv.most_similar(computer, topn5))这里的区别在于Word2Vec.load恢复的是一个包含训练参数、词表、向量在内的完整模型对象所以查询词向量之前要先通过model.wv拿到KeyedVectors。之后所有的查询方法都一样。完整的 Word2vec 模型还保留着继续训练的能力所以如果你后续要基于自己的领域语料做增量训练这种加载方式更合适。原生模型文件和 word2vec C 工具文件有个容易混淆的地方文件后缀可能都是.bin或.model但底层结构不一样加载方式也不能互换。判断口诀就是外部词向量格式用load_word2vec_formatGensim 自身保存的模型用load。如果不确定两种方法都试一下总有一种能正常打开。3.5 单独算两个词的相似度验证语义关系如果你不想看完整列表只想确认两个词之间的关系可以直接用similarity方法score kv.similarity(coffee, tea) print(score)这个方法内部就是先取出两个词的向量再计算余弦相似度。当我需要快速验证一个预训练模型是否包含某个语义群体时我会先挑选几组“应该高度相似”和“不应该相似”的词对批量计算相似度。如果“咖啡”和“茶”之间的分数高于“咖啡”和“桌子”说明模型的基本语义结构是正常的。也可以用index_to_key看词表内容例如words [kv.index_to_key[i] for i in range(10)] print(words)index_to_key是当前 Gensim 推荐的属性名旧版本里叫index2word。如果你看到老代码使用index2word给它加个兼容判断就好。这些细节虽然小但在排查代码和阅读旧项目时很能节约时间。4. 加载与测试过程中的常见问题附带排查思路前几节代码看着简单真到自己跑起来各种报错会接踵而至。这里把我遇到过的问题和排查方法整理成几类。4.1 binary 参数设错文件永远加载不进去load_word2vec_format最常见的报错就是 UnicodeDecodeError或者加载后出现大量无意义的“词”。这通常不是文件损坏而是你把binary参数设置反了。文本格式的文件里每个词和向量都用可见字符存储二进制格式里数值是紧凑的二进制浮点数打开会看到乱码。当你在二进制文件上设置binaryFalseGensim 会尝试按文本方式逐行读取结果读出来的全是乱码行不是报错就是读取结果异常反过来在文本格式上设置binaryTrue它会尝试把一行数据直接解析成二进制也必然出错。排查方法其实很简单用文本编辑器或者命令行工具打开文件看一眼头部。如果有第一行“词表数 维度”且后面能看到词和数字则是文本格式如果第一行之后全是乱码则大概率是二进制格式。还有一个更省事的办法先试binaryFalse报错再换binaryTrue两个总有一个能对上。4.2 加载大模型内存不够怎么办预训练词向量文件动辄几个 GB加载到内存时实际占用会进一步膨胀。因为文本文件读取时需要先转换成浮点数数组Gensim 底层还要构建映射表内存峰值往往是文件大小的两三倍。普通笔记本很容易在加载阶段被直接卡死或报MemoryError。如果你不是非要全量词表最直接的办法是加limit参数。比如只加载前 50 万高频词很多场景下已经能覆盖大多数文本。如果需要用小语料熟悉流程这也是最省内存的选择。如果limit仍然不够可以在文件来源上下手找更小的预训练模型比如用 100 维而不是 300 维的向量或者把文本格式转成二进制格式文件体积会小很多。再往上走就是对大文件采用分片读取的方式逐段构建向量表但这属于比较高阶的处理方式初学阶段用limit就够了。另外提醒一句如果你的模型是用 Gensim 的save保存的加载时可以尝试Word2Vec.load(path, mmapr)它能把部分数据映射到磁盘而不是全部塞进内存对超大模型有一些帮助。但load_word2vec_format目前不支持这种内存映射方式它会把数据处理完再构造成KeyedVectors所以加载时要留出足够的内存。4.3 查询词不在词表里KeyError 和 OOV加载模型之后调用most_similar(word)时抛出了KeyError: word word not in vocabulary。这其实不是加载失败了而是你的查询词在预训练模型的词表里不存在。对于预训练模型来说词表是训练语料的子集低频词在训练时会被min_count参数过滤掉所以词表外词out-of-vocabulary简称 OOV非常常见。排查时先做三件事第一检查查询词是否有多余空格、标点、大小写不一致第二尝试做 normalize把英文统一转小写、去掉前后空白第三把词表里和你目标词相近的词打印出来看看模型里是否用了不同的写法。例如有些英文模型会把“cannot”写成一个词而你把文本切成“can not”两个词自然查不到。如果你用的是中文词向量OOV 问题更突出。中文文本没有天然空格同一个词在不同分词器下可能被切分成不同粒度的词。预训练模型用的是某个分词标准生成的词表你测试时也必须用同一套分词逻辑。如果你只是随便拿一个词去查命中的概率会低很多。如果词确实不在词表里技术上有几种处理方式用词形还原、用 subword 信息或者换一个词表更大的预训练模型。在 Gensim 的KeyedVectors上最直接的做法是先确认词是否在词表中再查询if word in kv: print(kv.most_similar(word)) else: print(OOV)这个小判断能帮你把众多“反常”结果快速归类。4.4 相似度结果离谱问题往往不在代码有时候代码运行正常但most_similar返回的前几个词跟你预想的完全不相干。我的第一反应不是去调代码而是去审视模型和语料是否匹配。第一个常见原因是领域错位。通用新闻语料训练出的模型对网络流行语、专业术语、口语表达的表示能力会明显不足。比如你用医疗文档里的“房颤”去查相似词得到的结果很可能是一堆你不认识或者不想关的医学词甚至可能因为语料中这个词出现太少而直接被过滤掉。第二个原因是查询词本身是多义词或语义粒度不匹配。比如英文词“bank”同时有“银行”和“河岸”两个含义向量会把多个含义揉合到一个坐标里相似词结果自然显得混乱。这不是模型的 bug而是分布式表示的固有缺点一个词只有一个向量装不下多个含义。第三个原因是模型词表内部已经做过文本规范化。有些预训练模型会把短语用下划线连起来比如new_york作为一个词你想查「new york」两个词结果必然是 OOV有些模型则把数字和符号做了特殊处理比如 123 和 123.0 不一定对应同一个词。处理这类问题时要先了解模型的词表形态再去设计查询词。4.5 中文词向量使用前一定要先分词最后单列一条说给中文场景因为这是新手最容易漏掉的地方。中文不像英文那样天然有空格分词所以无论你是想要训练还是想要加载别人的预训练中文词向量前提都是语料已经被切分成了词序列。我给一个常见错误示例。你把一句话“我喜欢自然语言处理”直接传给某个模型并把整句作为一个查询词那几乎必然报 OOV。正确方式是把这句话用中文分词工具切成“我 喜欢 自然语言处理”然后分别查询词向量。很多开源中文预训练词向量在发布时已经说明了它用的是哪种分词标准跟着标准走才能让测试结果有参考价值。如果是自己训练模型分词一致性更关键。同一个文本第一遍用 A 分词器第二遍用 B 分词器产生的结果并不是同一种词序列训练出来的词表会非常碎片化后续应用也容易出问题。建议在整个项目中锁定一种分词方案并且让预测阶段和训练阶段使用同一套预处理代码。中文词向量还有一些额外细节全角半角字符、繁简体、数字单位等都会导致同一个词在词表里变成不同形式。加载模型后先把“你好”和“hello”这类高频词查一遍能帮你尽早发现预处理层面的不一致。5. 用加载好的模型还能做哪些事到这里基础的加载和测试已经跑通了。很多人会问我费劲加载了一个预训练模型除了看相似词还能干什么下面说几个很实用的扩展。5.1 找出不属于同一类的词doesnt_match除了找最相似的词Gensim 库还提供了一个反向操作给出一组词找出其中语义最不匹配的那个。odd kv.doesnt_match([breakfast, lunch, dinner, paperclip]) print(odd)前三个词都属于用餐场景paperclip回形针显然是混进来的那个。这个方法内部仍然是计算词向量之间的平均向量再找出与平均向量距离最远的词。它非常适合做文本质量检测和脏数据清洗比如判断一个商品标题里的词语是否连贯或者识别句子中混入的无关词。5.2 把词向量接入下游任务词向量最基础的使用方式是把词转成固定维度的向量然后作为特征喂给下游模型。获取单个词向量很简单vec kv[computer] print(vec.shape)vec是一个数值数组可以直接参与向量运算或者拼接成分类模型的输入特征。对于整篇文档常见的做法是取文档中所有词向量的平均值得到一个文档向量再输给分类器。需要注意的是简单平均会损失词序信息。如果你的任务对词序敏感比如情感分析中的“不好”与“好不”简单平均并不能很好区分。此时可以配合 TF-IDF 加权平均或者用更复杂的序列模型去处理。但对于文本聚类、关键词扩展这类不需要精细词序的任务平均词向量通常已经够用。5.3 简单可视化让词向量“看得见”词向量维度通常是 100 到 300没法直接画在平面上。我们可以用 PCA 或者 t-SNE 这类降维算法把高维向量压到二维坐标再散点图展示。做法是选一组感兴趣的词取出向量然后降维。比如我拿服饰类词语来试直接散点图上会看到颜色词聚到一起品牌词聚到一起材质词聚到一起。这说明词向量捕捉到了不同语义类别之间的差异。可视化不是目的但它能非常直观地验证模型质量也有助于向非技术同事解释词向量到底在做什么。这里给个小建议先对向量做标准化处理再做降维效果会好很多。否则高频词和低频词的向量范数差异可能会主导投影方向导致最终图上只按词频分开而不是按语义分开。这个细节是我反复对比后才发现的。5.4 结合领域语料做增量更新最后一个扩展场景你有一个通用预训练模型同时又有一批领域内的专属语料。与其从头训练不如尝试做增量训练。当然这里的前提是你加载的是一个完整的 Gensim Word2Vec 模型而不是单独的KeyedVectors。流程大概是加载完整模型后用领域语料构建词表并更新已有向量然后继续训练若干轮。Gensim 库提供了类似build_vocab和train的接口但在做增量训练前要想清楚一个问题增量训练会不会破坏原本学到的通用语义如果领域语料占比太小模型很容易被通用语料信息覆盖如果占比太大又可能产生严重的灾难性遗忘。我的建议是先拿一小批领域语料做小规模实验对比增量前后的相似词列表再决定投入资源。这里的细节比较多我准备放到本系列的后续文章中专门展开这次先把加载和基础测试讲透。根据我自己这些年的使用经验第一次跑通加载预训练模型很容易真正有价值的是拿业务词做交叉验证。别只看most_similar(苹果)返回了“香蕉”就觉得模型靠谱你应该再试一组你行业里的语义关系看看模型是不是真的把“售后”和“客服”这类业务词聚类在一起。如果结果混乱大概率不是代码问题而是模型语料与你的场景不匹配这时候先换模型比反复调topn参数有效得多。希望这篇能帮你把 Word2vec 模型的第一块敲门砖搬稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑