尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

中文词向量训练实战:基于维基百科的word2vec与doc2vec完整方案

中文词向量训练实战:基于维基百科的word2vec与doc2vec完整方案 简介词向量是自然语言处理的基础设施它将离散的词语映射为稠密的语义向量使机器学习模型能够理解文本之间的相似性与关联性。在中文场景下通用预训练词向量资源稀缺且常受分词版本与领域偏置影响。基于分布式假设word2vec通过上下文共现学习词义而doc2vec进一步为句子或文档生成定长向量两者在文本匹配、语义检索、推荐召回和聚类等任务中具有广泛应用价值。本文以维基百科为语料系统介绍从数据清洗、繁简转换、jieba分词到构建word2vec与doc2vec模型的完整流程并给出关键参数配置、效果验证及工程落地中的常见坑点为中文NLP项目提供一套可直接借鉴的预训练模型构建方案。 中文词向量的落地方案我一直觉得是很多NLP项目里最容易被轻视的一环。这两年大模型火归火但实际做检索、聚类、相似度计算、推荐召回这类任务时一套训练好的word2vec或doc2vec模型依然是性价比极高的基础设施。尤其是中文场景网上能直接下载的优质预训练词向量并不算多很多还带着陈旧的分词版本或者领域偏置。所以我自己用中文维基百科的语料完整走了一遍训练流程把word2vec和doc2vec模型都训练好打包成zip分享了出来。这篇文章就是完整记录这套模型的来龙去脉包括数据怎么处理、参数怎么调、模型怎么用、有哪些坑算是一份可以直接抄作业的参考。这个zip里不是只有一个孤零零的模型文件而是包含了两套东西一套是word2vec中文词向量模型另一套是doc2vec句向量/文档向量模型外加加载脚本和简单的相似度检索demo。适合谁用呢如果你是做中文文本匹配、短文本分类、关键词扩展、语义搜索或者说要做baseline对比的这套模型可以直接帮你省掉从零训练的时间。需要注意的是这并非一个面向SOTA效果的精调模型而是一个覆盖面广、通用性较强的预训练基础模型用来做初始化特征或者离线相似度计算非常合适。1. 为什么选维基百科语料而不是百度百科或新闻语料很多人一上来就问训练词向量到底用什么语料最好。我的答案一直是看你要干什么。如果你做的是医疗领域那新闻语料训练的模型拿去处理病历文本效果会惨不忍睹。但如果目标是做一个通用的、覆盖大部分中文词汇和表达的基础模型那维基百科几乎是首选没有之一。中文维基百科的语料质量非常高覆盖了科学、历史、地理、文化、人物、技术等几乎所有常见领域。相比新闻语料维基百科的句子通常结构完整、信息密度高、用词规范这就给word2vec的学习提供了一个非常好的环境。要知道word2vec的核心假设是一个词的含义由它周围的词决定即分布式假设。语料里如果全是口水话或者重复句式模型学到的东西就会很偏。维基百科的句子长短适中上下文关系紧密训练出来的词向量语义方向性非常好。百度百科的训练语料我也试过内容量和覆盖面其实更大但是有两个问题一是抓取门槛高需要自己写爬虫反爬机制很麻烦。二是百度百科的词条内容里夹杂了大量表格、模板、维护性文字清洗成本比维基百科高不少。如果你去对比两个语料训练出来的模型在大多数语义类比任务上维基百科训练的结果并没有明显劣势很多维度上甚至更干净。我当时用的是zhwiki-latest-pages-articles.xml.bz2这个dump文件也就是维基百科的全量词条快照。这个文件一般几个GB级别解压之后更大里面是结构化的XML格式要用专门的解析工具来处理。这里有个关键操作维基百科的dump文件是一个很大的XML直接用正则去抠文本是可行的但效率很低而且容易漏。推荐使用WikiExtractor这个开源工具来处理它能直接输出清洗后的纯文本每个词条一个文件或者合并到一个大文件里。整个处理链路是这样的下载最新的zhwiki-latest-pages-articles.xml.bz2用WikiExtractor.py抽取正文文本过滤掉模板、图片链接、参考资料等干扰信息用OpenCC做繁简转换把粤语、文言文等非现代标准汉语的内容做过滤对文本进行分句、分词、去停用词在WikiExtractor处理完后会有大量繁体和简体混合的内容这直接影响后续分词。不用OpenCC统一转成简体的话同一个词在繁简两种形态下会被切成两个token等于把语料活生生地割裂了模型学出来的向量也会有问题。这一步绝对不能省。我在第一次训练时就偷懒跳过繁简转换结果跑出来的模型用手机查相似词排在前面的居然是手機这个繁体写法这种结果在我后续代码里几乎没法直接使用只能在交付前重新训练。2. 分词与停用词处理不重视细节模型质量直接降一个档次中文和英文一个很大的区别就是中文分词跑不掉。word2vec处理的是词的序列你给它什么粒度它就学什么粒度。分词工具我用的是jieba这是目前中文NLP社区里用得最多的依赖包稳定且可控。如果是做特定领域文本可以考虑用pkuseg或者LTP但从通用角度来说jieba的词典覆盖度和可控性最均衡。分词的直接决定了你模型的词表。比如自然语言处理这个词组jieba可能切成了自然语言和处理两个token。这种粒度问题各有优劣切成多个token模型的词表更小统计意义上每个词的语料量更高训练出的向量更稳定把整个词组作为一个token保留则模型在表示专有表达时更准确但是需要足够多的共现次数来支撑。我们用维基百科这种大规模语料默认采用jieba的精确模式保留分词后的词组形式不做进一步压缩。分词之后我做了停用词过滤。这里就值得说道说道了。很多人直接拿网上下载的停用词表一顿乱删把的、了、是、在这种高频功能词直接去掉。这个大方向没错但有一个地方要小心维基百科语料本身是百科性质的很多词条名称是专有名词比如地名、人名、学术名词这些是不应该当作停用词删掉的。网上的通用停用词表基本不会包含这些词问题不大。真正需要注意的是标点符号和纯数字。我在清洗脚本里还加入了一个规则过滤掉所有只包含数字和字母的无意义token。像2019年这种词到底保不保留我建议保留因为年份、数字在文本相似度计算里是有实际作用的尤其在百科文本里年份和时间词经常是关键的定位信息。所以我的过滤条件只是去掉纯数字或者数字单个量词这种噪音token而不是一刀切把所有含数字的词都删掉。分词结束后我统计了一遍词频分布。维基百科语料训练出的模型词表大小通常在几十万量级高频词的覆盖非常充足。如果你用同样的流程跑出来的词表明显过小那大概率是清洗环节出了问题比如文本抽取不完整或者分句逻辑写错了。3. word2vec与doc2vec的训练细节参数不是拍脑袋定的训练工具我选的是gensim这个库虽然性能和工程化程度比不过一些工业级实现但胜在API友好、文档齐全适合做研究和中小型项目落地。如果你要训练超大规模的词向量可以考虑用fastText官方工具或者spaCy的管道但gensim对于几GB的中文语料跑起来完全够用。在gensim里word2vec的核心参数就那几个vector_size、window、min_count、workers、epochs、negative、sample。vector_size向量的维度。我最终选的是300维。这个值不是一个绝对标准200维也能用但300维是社区验证过的一个平衡点。维度太低语义信息不够维度太高不仅训练慢还会引入噪声。中文维基百科这个规模的语料300维完全养得起。window上下文窗口大小。我选的5也就是前后各看5个词。窗口越小词向量越关注句法关系窗口越大越关注主题语义。如果你做的是短文本分类窗口可以适当调大到10如果你做的是词汇类比、句法分析窗口5是个很稳的选择。min_count最低词频阈值。我设的是5也就是一个词在整个语料中出现次数低于5次就直接丢弃。这样做的原因是低频词的向量估计往往不靠谱训练样本太少学出来的向量基本是噪声。丢弃低频词还能显著减小词表大小、加快训练速度。negative负采样数量。这个参数在gensim里默认是5我保持了这个默认值。负采样是word2vec加速训练的核心技巧它不需要对词表中所有词做softmax而是随机抽取几个负样本做二分类。负采样数量不是越大越好5到10之间是个合理区间太多了会引入过多的这俩词没关系的信号反而干扰训练。sample高频词下采样阈值。我设的是1e-4。下采样的逻辑是像的、是这种词在语料中出现频率极高但它们提供的上下文信号信息量极低。下采样会以一定概率随机丢弃这些高频词让模型的训练更加聚焦在信息量丰富的词上。这个参数对训练速度和模型质量都有正面收益1e-4到1e-5之间是常见配置。epochs训练轮数。我选了5轮。word2vec并不需要像深度学习模型那样训练几十个epoch它是单层网络结构训练太久反而会过拟合到语料中的噪声。5轮在这个语料规模上loss已经收敛得足够好了。训练word2vec选Skip-gram还是CBOW也是个老生常谈的问题。我最终用的是Skip-gram。理由很简单Skip-gram对低频词的向量学习更友好虽然训练速度比CBOW慢但在中文维基百科这种大规模语料下模型质量优先。如果你着急出结果且对低频词不敏感CBOW也能用但Skip-gram在整体语义质量上确实更胜一筹。doc2vec的训练则完全是另一套逻辑。doc2vec的核心思想是给每个文档学习一个向量表示这个向量可以用于文档级相似度计算。gensim里的Doc2Vec实现了两种训练方法PV-DMDistributed Memory和PV-DBOWDistributed Bag of Words。PV-DM的核心是模仿word2vec的CBOW思路在预测中心词时除了考虑上下文的词向量还额外拼接了文档向量。这样训练出来的文档向量包含了丰富的词序信息效果通常更好但缺点是训练时间长、参数多。PV-DBOW则忽略词序只根据文档向量去预测文档中随机抽样的词训练速度更快语义信息更聚焦于主题。我的选择是用PV-DM去训练但加了dbow_words1这个参数让它同时训练word向量这样doc2vec模型也能当word2vec用。doc2vec训练时有一个必须注意的点epochs要设得比word2vec更大。gensim老版本里doc2vec默认epochs只有40后来改来改去甚至有默认值不一致的情况。doc2vec的文档向量是通过多次遍历文档逐步收敛的epochs太少文档向量根本没学到位。我最终设的是20轮。还有一个参数是dm_concat设为1的时候会把上下文向量直接拼接而不是求平均继承了词序信息但同时会大幅增加计算量。我保持默认0用平均值模式因为在这个应用场景下平均模式已经足够稳定。4. 模型效果评测直接上手验证语义质量模型训练完不能只看loss就宣布成功。我习惯做三个维度的验证词汇类比、相似词检索、文档相似度。词汇类比是word2vec最经典的能力验证方式。比如中国-北京日本这个经典的类比任务。如果用most_similar方法去计算中国 - 北京 日本的余弦相似度返回的前几个词如果包含东京说明模型学习到了国家-首都这一语义关系。我跑了一下自己训练的模型这个类比的结果非常干脆东京排第一后面跟着的也是日本的大城市语义方向性很清晰。再比如男人-女人国王这个语法类比训练好的模型会返回女王。这类类比的本质是两个词向量做减法得到一个语义方向把这个方向加到另一个词向量上在向量空间中找一个最接近的目标。如果模型没有学好返回的词会乱七八糟。我在实际测试中这个模型的语法类比成功率还是相当高的说明语料质量和参数配置是靠谱的。相似词检索可以直接用.most_similar(深度学习)来看返回结果。训练好的模型给出的结果往往让会你意外地合理。机器学习、人工智能、神经网络这些词会靠前排列因为它们所在的语言环境——共现上下文——高度一致。doc2vec模型的验证稍微复杂一点。我的做法是从测试语料中取若干段文本计算它们之间的余弦相似度。比如拿一段关于自然语言处理的百科内容和一段关于中国历史的内容做对比理论上这两段文本的相似度应该低于同主题文本的相似度。doc2vec模型对这种主题级别的区分度表现得非常明显。另外doc2vec可以直接用model.dv.most_similar(doc_id)检索相似的文档这在做去重、推荐、聚类预筛选时非常有用。这里有个我自己踩过的坑doc2vec训练时gensim要求每个文档必须带一个唯一的TaggedDocument标签标签可以是数字也可以是字符串。如果你用的是字符串标签保存模型后重新加载时docvecs的索引会基于你训练时给的标签生成但如果在加载之后再往里面新增文档向量索引对齐问题就会冒出来。所以我在封装的时候明确要求使用者这个模型训练阶段的标签是数字编号新文档如果要加入模型做增量训练也要保持数字标签的格式不能混用字符串。这个问题在中文社区里讨论不多但实际使用中会经常碰见。5. 使用这套模型时最容易踩的坑模型交付出去用户反馈的常见问题其实都集中在那几个点。第一个是模型加载后的词表缺失问题。用gensim加载word2vec模型后如果你查询一个分词器分出来的词在模型里不存在会直接报KeyError。这非常容易让初学者懵住。原因往往是这个词在训练语料中出现频率太低被min_count过滤掉了或者分词工具在预测时切出来的粒度与训练时不一致。我的建议是封装一个get_vector函数查询前先判断词是否在wv.index_to_key里不在就返回一个全零向量或者随机向量。全零向量有一个问题就是它是零范数后续做归一化时除以零会报错所以实际工程中对OOV词我用的是一个小范围随机向量加掩码标记至少保证后续计算不会崩。第二个是词向量的静态本质。word2vec训练出来的词向量是静态的——一个词只有一个向量它没有多义词区分能力。比如苹果这个词既可以是水果也可以是手机品牌但在word2vec里它只有一个向量。这在很多场景下会带来误差。如果你做的是短文本分类或者关键词匹配这种误差可以接受但如果你做的是情感分析这种对语义细微差别敏感的任务建议在一层模型之上再叠加微调流程。第三个是中文分词粒度对最终效果的影响。这个词向量是基于jieba分词训练的如果你在实际使用时用的是别的分词器那表示同一个意思的词可能被切成不同的token比如自然语言处理jieba常见切法是自然语言/处理另一个分词器可能切成自然/语言/处理。你查询自然语言处理这个词直接用另一个分词器的结果去模型里找很可能找不到因为模型里存的是自然语言和处理两个分开的token。解决办法是要么在你的管道里也用jieba分词要么在模型前面加一层分词对齐逻辑。第四个坑是关于KeyedVectors的使用方式。gensim老版本里大家习惯直接model[词]来取向量新版本里这个方式虽然还能用但官方更推荐使用model.wv[词]。如果你是用我的zip包里的脚本这些细节我已经帮你处理好了。但如果你自己从gensim加载模型写代码注意模型的版本兼容性。旧版gensim保存的词向量格式和新版之间偶尔会有兼容问题如果加载时报错多半是gensim版本不一致导致的升级或降级gensim版本可以解决。6. 从word2vec到fastText什么时候该换算法这个话题虽然不在模型本身范围内但既然在中文词向量这个背景下我一定会提一嘴。fastText和word2vec的核心差别在于fastText在训练时会把每个词拆成字符级别的n-gram比如中国会被拆成中、国、中国以及带边界符号的字符组合然后词向量是这些n-gram向量的加总。这个设计带来的直接好处是对于训练语料里没出现过的词fastText也能根据它的字符子串拼出一个词向量解决了OOV问题。那么什么时候选word2vec什么时候选fastText呢我的判断维度是任务类型。如果你做的是语义相似度、关键词检索、文本聚类这类需要词的含义空间的任务word2vec更合适因为它生成的向量更平滑、语义浓缩更充分。如果你做的是命名实体识别、序列标注、拼写纠错这类需要关注词形态信息的任务fastText的效果会更好。还有一个区别是fastText训练出来的模型文件往往比word2vec大很多因为额外存储了n-gram向量。我当时打包的时候考虑过要不要再加一个fastText模型最终因为体积问题砍掉了。如果你对OOV问题特别敏感可以自行用相同的语料流程跑一个fastText模型出来gensim里对fastText的支持也是开箱即用的。7. 实际工程中doc2vec模型还能这样扩展很多人拿到doc2vec模型后不知道怎么用觉得它不如word2vec直观。实际上doc2vec的应用场景非常广一个典型的场景是短文本聚类。假设你有一堆用户反馈文本每一段都是几句话你想把它们自动归类为功能问题体验问题价格问题等。传统做法是TF-IDF加KMeans但TF-IDF的致命弱点是特征维度太高、语义信息割裂。用doc2vec把每段反馈映射成一个300维的向量然后对向量做聚类效果会好很多。因为doc2vec学到的向量天然编码了语义相似度两个表达方式不同但表达同一含义的句子它们的向量会很接近。另一个实用场景是推荐系统的召回。电商场景里每件商品的标题和描述可以拼成一段文本用doc2vec训练一个商品向量。用户浏览了一个商品就把这个商品的doc2vec向量拿到商品池里做最近邻搜索召回一批向量相似的商品。这个方案在冷启动阶段远比行为协同过滤管用因为它不依赖用户历史行为只看商品本身的文本语义。我见过不少中小型团队就是靠这个思路在没有丰富用户行为数据的情况下把推荐召回做起来的。doc2vec输出的是定长向量这也是它比传统词袋模型更适合做机器学习特征的重要原因。不管是接一个逻辑回归、随机森林还是接一个简单的神经网络doc2vec向量都能直接作为输入特征。而TF-IDF那种高维稀疏特征在传统机器学习模型里虽然也能跑但内存开销和过拟合风险都更大。8. 关于训练流程自动化与复现的一些建议整个流程虽然看起来繁琐但实际上每一步都是可以脚本化的。我第一次跑全流程时踩了不少坑后来总结了一套固定的流水线下载数据、解析XML、繁简转换、过滤噪音、分句、分词、训练word2vec、训练doc2vec、压缩打包。这个过程在普通笔记本上也能跑通只是耗时不同。word2vec在CPU上训练大概需要几个小时doc2vec的时间会更多一些。如果你有GPU环境gensim其实对GPU支持有限这一块反而快不起来但好在两个模型都不算大CPU完全能接受。自动化还有一个隐藏的好处语料可以持续更新。维基百科每个月都有新的dump发布跑一次流程就能得到最新版词向量。我自己现在每次发布新版本模型时都会把自己的清洗脚本一起放出来方便大家一起改进。最后再分享一个小技巧。如果你在most_similar的结果里发现某个词的相似结果明显有问题比如查询篮球却返回了几个完全不相关的词不要急着去调参数先去看这个词在语料里的实际上下文。用model.wv.contexts或者直接检索原文看看是不是文本清洗环节引入了脏数据比如把一个词条的内容解析成了无意义的字符序列。语料里的脏数据是词向量质量最大的杀手参数调优反而救不回来。本文还有配套的精品资源点击获取
返回列表