1. 先说清楚"准确度"这个词——词向量到底该怎么衡量
1.1 很多人第一步就错:拿intrinsic评测当"准确度"
"我用Word2vec训练的词向量准确度太低了",这个问题几乎每隔几天就会在技术社区看到一次。但每次有人抛出这句话,我第一反应不是去查参数,而是想反问一句:你说的"准确度"到底是什么?
是我跑出来的相似词Top-N结果看起来不合理?是king - man + woman = queen这种类比推理做不对?是拿中文相似度数据集算Spearman相关系数分数太低?还是下游的文本分类模型用这个词向量初始化后准确率上不去?
这四件事虽然都和"词向量效果"有关,但背后的优化方向完全不同。我自己早年间做某智能客服项目时,也曾抱着词向量一顿猛调,折腾了一周才发现问题根本不在于模型参数,而是评测口径选错了。
词向量的产物是一组稠密向量,它本身没有"对错",只有"像不像某种语义关系"。当我们说"准确度低"时,通常是指某个评测基准上的得分低。这些基准大体分两类:intrinsic(内在评测)和extrinsic(外在评测)。内在评测也就是直接拿词向量本身做相似度、类比推理;外在评测是把它喂给下游任务,看最终效果,比如情感分类、命名实体识别。
这两种评测之间有相关性,但不是完全一致。甚至在某些数据集上,内在评测分数高并不代表下游任务效果好,因为内在评测依赖的是人工标注的语义关系,而下游任务往往更看重领域适配性。
1.2 用两个例子快速理解评测口径差异
拿相似度评测来说,中文常见的有WordSim-353、SimLex-999这类数据集,里面是一对对词以及人工打的相似度分值,比如"轿车"和"汽车"相似度很高,"轿车"和"食物"很低。你把训练好的词向量取出来,计算每对词的Cosine相似度,再和人工分数算相关度,相关度越高说明词向量越"准"。
而类比推理评测则是另一回事。经典题目是筷子 : 碗 :: 叉子 : ?,模型要在词表里找一个词,使得vec(叉子) - vec(筷子) + vec(碗)的余弦距离最近。这类评测检验的不只是"相似",而是词汇之间可迁移的关系向量。
我的经验是:如果你用Word2vec训练出的词向量在相似度任务上分数低,先别急着骂模型,先看数据量和数据域的匹配度;如果相似度还行但类比推理不行,再看窗口大小和训练轮数;如果内在评测都还行但下游任务不行,可能就是词向量和任务之间的语义粒度不匹配。
1.3 一个反直觉结论:相似度分数高≠词向量"准确"
还有一个特别容易误导人的地方:有时候你眼巴巴看着某两个词的相似度分数很高,觉得模型学得很好,但其实模型只是学到了"它们经常一起出现",而不是"它们语义相近"。Word2vec本质上是在做共现统计的降维学习,它捕捉的是分布式假设下的语境相似性。共现相关和语义相似是两回事。
举个典型例子,在新闻语料里"房价"和"调控"的共现频率极高,它们的向量余弦相似度会很高,但如果你拿给标注人员判断,多半会说这俩词没那么"相似"。这就是为什么很多人把词向量可视化出来看,觉得一团乱——你没有区分模型学的"语境相关性"和人类直觉的"语义相似性",预期错位了。
所以训练词向量前,务必先立一个明确的评测任务。没有评测任务就谈"准确度太低",等于没有方向盘就开车,翻车是必然的。
2. 语料与预处理——九成"准确度低"的根因藏在这里
2.1 语料量级与领域匹配:不是所有语料都适合
当你排除了评测口径问题,真正动手调优时,第一站必须是语料。很多人从某个开源语料库下载了几十万条文本,直接扔进Word2Vec就开始训,训完觉得效果差,这根本不用意外。词向量是数据驱动的方法,语料的质量决定了效果天花板。
先说量级。Word2vec虽然比深度神经网络模型省资源,但也不是给个几万字小文本就能学到好东西。按我个人的实践,想得到比较稳的词向量,纯文本至少要有千万字级别。你可以算一笔账:中文里一个词平均由1.5~2.5个汉字组成,1000万字大概对应400万~700万个词例(token),这个量级勉强够训练一个100维度的词向量,让高频词有比较好的表示。如果想要覆盖中低频词,没有上亿字很难。
但光量大还不够。**语料的领域必须和你要用词向量的场景匹配。**通用新闻语料训练出的词向量,拿到医疗文本、法律文书、客服对话场景里,效果会明显缩水。因为同义词在不同领域的搭配关系完全不一样,"移植"在农业语料和医疗语料里的语境完全不同。所以,第一原则:能用目标场景的语料就别用通用语料,领域内数据永远优先。
2.2 分词的坑:错误分词会直接污染训练
中文训练Word2vec绕不开分词。分词错误带来的问题非常直接:一个本该是完整语义单位的词被打碎,模型的训练样本就变成了错误的共现上下文。
举个例子,"人工智能"如果被分成"人工"和"智能",模型会认为这俩词经常共现,还可能因此让"人工"和其他不相关的词产生奇怪关联。我在某电商评论项目里就吃过这个亏——"不好用"被分词成"不好"和"用",结果"不好"和"用"的向量高度相似,连带"挺好"和"用"也产生了莫名其妙的关联。
所以,训练词向量之前,务必按目标领域准备一份自定义词典。如果你没有合适的词典,可以先用一个基础分词器分一遍,然后把高频的、明显是完整语义单元却被切碎的词找出来,加入自定义词典重新分词。Jieba支持jieba.load_userdict(),操作成本很低。
还有一点很多人忽略:**分词器的一致性。**同样的一个句子,同一种分词器在不同版本的词典配置下结果可能不同。这会导致同一个词在语料里有时被切出来、有时没被切出来,模型学到的向量就被稀释了。固定分词器版本以及词典版本,是保证可复现训练的前提。
2.3 停用词、低频词与乱码处理策略
停用词怎么处理,网上的说法很矛盾,有人说要全删,有人说不能删。我的结论是:不能盲目全删,但要控制数量。
停用词诸如"的""了""在""和",它们几乎在每个上下文窗口里都会出现。如果不做任何处理,模型会把大把参数浪费在学习这些词的共现关系上。更糟的是,一些真正有语义的词,因为经常和停用词一起出现,向量会被"稀释"。我在训练客服语料时发现,去掉停用词之后,"退款"和"退货"的余弦相似度明显上升,因为模型终于能把注意力放在关键业务词上了。
但反过来说,有些词在特定领域里不是停用词。比如"我们""你们"在通用语料中自然是停用词,但在对话场景里,人称代词对于区分说话人视角是有意义的。因此,停用词表务必根据语料统计结果做定制,而不是直接下载一张通用词表就套。
低频词也要处理。min_count这个参数解决的正是这个问题。**词频过低意味着该词在语料中出现的次数不足以支撑一个稳定的向量表示,留着反而增加噪音。**实际调参时,我一般会把词频为1的词删掉,再根据词表规模决定是否把词频低于2~5的词也删掉。删掉低频词还有一个好处:缩小词表体积,训练速度更快,内存占用更低。
2.4 中文场景特有的预处理清单
最后整理一份中文场景比较完整的预处理流程,直接抄作业:
- 统一文本格式,清洗HTML标签、规范标点符号,全角转半角;
- 过滤超短文本、重复文本,这类数据对训练没有正向贡献;
- 中文分词,并结合领域词典做用户自定义分词;
- 去除纯数字、纯英文的token(除非它们在你的场景里有意义,比如手机型号、药品编号);
- 统计词频,过滤低频词和头部超高词频词语;
- 构建停用词表,根据语料本身统计结果定制;
- 把文本转化为tokens列表,保存成
LineSentence格式供gensim读取。
第4条值得多说一句。中文语料里经常混着一堆数字和英文,比如"iPhone14""10086",这些token要么本身是实体,要么是噪音。如果你的场景不需要它们,最好直接过滤,否则模型会拿它们当普通词,学出一些没有迁移价值的向量表示。我在某文本分类项目中,因为没过滤数字,结果"3""4""5"这些数字的词向量聚成了一团,还吸引了不少无关词绕在周围。
3. 核心参数逐个调——vector_size、window、min_count与负采样
3.1 维度与窗口怎么搭配
语料和预处理理清楚之后,再进参数调整环节。词向量的第一个核心参数是向量维度(vector_size)。维度太小,模型没有足够容量表达语义关系;维度太大,在小语料上容易过拟合,训练速度也慢。
多少算合适?我的经验公式是:**维度大致取语料token数开四次方,再根据语料复杂度上下浮动。**比如1000万个token的语料,四次方根约56,那取64~128之间比较合理。如果语料有1亿token,四次方根是100,取100~200都合适。当然这不是硬性公式,只是起步参考。我自己的通用设置是:小语料(千万级)取100维,中等语料(亿级)取200~300维,超大语料(十亿级)才考虑400维以上。
窗口(window)代表的是当前中心词能"看到"的左右上下文范围。**窗口小,模型学到的是更近邻的共现关系,句法特征更明显;窗口大,学到的是更宏观的语义主题特征。**比如"这家餐厅的菜很好吃,服务也很到位",window=5时,"好吃"能覆盖"服务""到位"这些词;window=2时,"好吃"主要只跟"菜""很"互动。
针对不同任务,窗口设置要有针对性。如果你要做情感分析、短文本匹配,这些任务对句内局部搭配敏感,window=5左右比较合适。如果你要做文本主题分类、文档检索,这类任务更依赖语义主题的一致性,window=10甚至更大更好。顺带一提,gensim里默认的window=5在大多数中文通用场景下表现中庸,但安全。
3.2 min_count、negative、sample这几个参数的真实作用
min_count前面提过,用来过滤低频词。它的副作用容易被忽略:设得太大,会把一些有价值的中频词也删掉,词表直接瘦一圈。我一般先不设min_count,训练完后统计词频分布,再决定在5和10之间取哪个值。如果语料质量高、领域集中,取3就够了;语料混杂、噪音多,取10更稳。
negative是负采样数量。训练的时候,模型不止要看到"正确"的上下文词对,还要随机抽一些"错误"词对作为反例,逼模型学会区分。负采样数量越大,每次更新的区分度越高,但训练耗时也线性上升。我实测下来,negative=5是性价比最高的起点,negative=10效果略有提升但收益边际递减,negative=15以上基本就是烧GPU玩。
sample是高频词下采样阈值。它解决的是另一个问题:像"的""了"这类高频词虽然被停用词表过滤了一部分,但仍有大量中高频但低信息量的词,它们出现太频繁,会让每个上下文窗口都塞满"常见词",干扰真正有区分度的词的训练。sample=1e-4是一个保守的起点,意思是词频超过一定比例的词会以一定概率被随机丢弃。这个参数调得好,模型效果提升比调维度还明显。
3.3 epochs与学习率衰减:训练不够和过度拟合的平衡
epochs表示模型对整个语料扫描多少遍。一个常见的误区是:语料小就多跑几遍,语料大就少跑。这句话对一半。语料小的时候多跑几遍确实能把样本利用得更充分,但跑太多遍又会导致过拟合——模型把训练语料里的噪声都背下来了,泛化能力反而下降。
我的观察是:千万级语料,epochs=5通常足够;亿级语料,epochs=3都不嫌少。**关键是看loss是否还在下降。**gensim的新版本里,训练时可以通过compute_loss=True记录loss,训练完后从model.get_latest_training_loss()读出来看。如果loss在前几个epoch大幅下降,说明还没收敛,可以加大epochs;如果loss已经平缓,再增加epochs只会浪费算力。
学习率衰减也要注意。gensim的Word2Vec默认使用alpha=0.025,并随训练推进线性衰减到min_alpha=0.001。这里有个很多人踩过的坑:**如果手动传入epochs和alpha,一定要确认官方文档里说过的"学习率衰减只发生在workers训练完成的回调中"这个机制,避免因为迭代器被重复消费导致学习率没有按预期衰减。**我在调试时发现,部分自定义语料迭代器在多次遍历时行为不同,导致明明设了min_alpha,最终模型参数却像是用恒定学习率训出来的。
3.4 一个可复用的调参起点表
为了方便没有经验的读者上手,我把常用场景的起点参数整理成了表。注意这里写的是起点,不是终点,实际效果一定要结合评测结果再微调。
| 参数 | 小规模语料(千万级) | 中等规模(亿级) | 大规模(十亿级) |
|---|---|---|---|
| vector_size | 100 | 200 | 300 |
| window | 5 | 5~8 | 8~10 |
| min_count | 3~5 | 5 | 5~10 |
| negative | 5 | 5~10 | 10 |
| sample | 1e-4 | 1e-4~1e-5 | 1e-5 |
| epochs | 5~10 | 3~5 | 2~3 |
| alpha | 0.025 | 0.025 | 0.025 |
| min_alpha | 0.001 | 0.001 | 0.0001 |
提示:词向量训练的"效果曲线"通常是先升后降的。参数调到某个值后效果不升反降,说明已经过了最优区间,应该往回调而不是继续加码。
4. 训练环节的细节——为什么同样的数据别人比我效果好
4.1 训练前检查语料流:iter与corpus的配合
gensim的Word2Vec接受一个可迭代的语料对象,但这里有个微妙的地方:**语料迭代器的实现方式会影响训练行为。**如果你传的是一个生成器,那它只能被消费一次,gensim内部会在每个epoch结束后重新获取迭代器吗?实际上,如果你自己实现了一个返回生成器的函数,传入的是函数调用而不是列表,那gensim每个epoch会通过多次调用该函数来重新获取数据流;如果直接传入一个list,则会多次遍历同一个列表。这个差别平时没事,但当你用LineSentence读取超大文件时,一定要保证语料对象可以被重复迭代。
我倾向的实现方式是把语料封装成类,实现__iter__方法,每次迭代打开文件重新读取。这样虽然I/O开销大一点,但能保证每个epoch看到的数据是一致的。如果要追求速度,可以把预处理完的tokens序列化存储,训练时直接按行读取。
另外非常关键的一点:**不要把所有分词结果一次性加载进内存再训。**一个上亿token的语料,分词后转成空格分隔的句子,光文本就有几个GB。一次性readlines()会让物理内存直接爆掉,或者触发大量swap导致训练慢得离谱。正确姿势是逐行读取、逐行yield,让gensim自己处理内存缓冲。
4.2 训练收敛的观察方法:loss曲线与相似度抽查
训练词向量最容易出现的状态是"黑盒训练"——跑完几小时,输出模型,然后才发现效果不行。我建议在训练过程中分阶段做检查,这样可以尽早发现问题,避免浪费时间。
第一步是观察loss。虽然gensim的loss不像深度学习框架里那样平滑,但大致趋势可以参考:最开始几个batch的loss较高,随后快速下降,到最后进入平缓。如果训练到后段loss还在大幅度震荡,说明学习率偏大,或者数据里有大量噪声需要继续过滤。
第二步是相似度抽查。每训练完一个epoch,抽几个种子词,比如"手机""退款""教育",打印它们的Top-5相似词。这一步很直观,能快速判断模型有没有走偏。我举个例子:有一次我在法律语料上训练词向量,第一轮epoch结束后"合同"的相似词里有"签订""条款""协议",当时我就知道模型方向是对的;而当"合同"的相似词混进来"法院""诉讼"时,说明模型开始把主题共现当语义相似处理了。
这一步不需要写复杂代码,就是几行Python脚本:
from gensim.models import Word2Vec model = Word2Vec.load("word2vec.model") test_words = ["手机", "退款", "教育", "合同"] for w in test_words: print("=" * 20, w) for sim_word, score in model.wv.most_similar(w, topn=5): print(f"{sim_word}: {score:.4f}")4.3 多线程与随机种子带来的微妙差异
workers参数设置多线程训练时,不同线程会各自持有部分参数副本,训练过程中定期同步。这里有一个不太被注意的问题:**多线程训练的结果在细节上会有不可复现性。**即使你设置了random_seed,不同线程间的参数交互顺序不同,最终向量也会有微小差异。如果项目对可复现性要求高(比如做实验对比),建议固定random_seed并尽量用单线程训练核心模型;如果只是生产环境使用,多线程的加速收益更值得。
另外一个和训练设置相关的坑是shrink_windows。gensim 4.x默认在训练过程中随机收缩窗口大小,从你设的window减到1,这是为了让近距离的词对获得更多训练权重。这个设计本身合理,但它会导致"实际上下文范围"比你设置的参数更小。所以,如果你在window=5时感觉相似度质量不理想,可以尝试调高到window=7或window=10,因为实际平均生效的窗口大约只有设值的一半。
4.4 模型诊断:Top-N相似词到底能不能说明问题
训练完之后,很多人会看most_similar的结果来判断模型好不好。这个操作可以作为快速诊断,但不能作为唯一标准。**因为most_similar本身受词频影响极大。**高频词通常和大量其他词都有较高的共现次数,所以它们的相似词列表往往偏向高频词。你可以试试查"我们""这个"这类高频词,多半会发现它们和一堆无关词"相似"。
要更准确地判断模型质量,建议同时参考几个指标:
- 低频词的相似词是否合理(低频词的向量更依赖局部上下文,噪音也多,但合理时说明模型学到了结构);
- 领域专有词的相似词是否聚焦(比如"冠心病"的相似词应该是心血管相关词汇);
- 词向量在显式的类比任务上表现如何。
4.5 一个诊断用的综合脚本
这里给出一段完整的训练加诊断脚本,方便直接参考:
import logging from gensim.models import Word2Vec from gensim.models.word2vec import LineSentence logging.basicConfig(level=logging.INFO) # 语料文件:每行一句话,词与词之间用空格分隔 sentences = LineSentence("corpus_tokens.txt") model = Word2Vec( sentences=sentences, vector_size=200, window=8, min_count=5, negative=10, sample=1e-4, epochs=5, workers=4, sg=1, compute_loss=True, seed=42, ) model.save("word2vec.model") # 打印训练loss信息 loss = model.get_latest_training_loss() print(f"训练loss: {loss}") # 诊断相似词 for w in ["手机", "健康", "合同", "算法"]: print(f"===== {w} 的相似词 =====") for sim_word, score in model.wv.most_similar(w, topn=10): print(f"{sim_word}: {score:.4f}")sg=1表示使用Skip-gram训练,sg=0则是CBOW。**Skip-gram对低频词的表示更好,CBOW训练速度更快且对高频词更友好。**如果你的任务更关注低频词或生僻词的表示,优先选择Skip-gram。
5. 评测与排错——从"感觉不对"到"定位问题"
5.1 类比推理评测:经典的king-man+woman用例
类比推理是检验词向量语义关系是否准确的经典方法,Word2vec论文里最著名的例子就是vec(king) - vec(man) + vec(woman) ≈ vec(queen)。在gensim里可以直接用most_similar做这个操作:
result = model.wv.most_similar(positive=["woman", "king"], negative=["man"], topn=1) print(result)但国内中文用户常见的情况是:跑这个用例得到的结果很离谱,于是认定"模型训坏了"。这里要泼一盆冷水:**中文语料里类比推理本身就是高难度任务。**像"国王-男人+女人=女王"这种规整的词义关系,在中文里高度依赖语料中是否有足够多"国王/女王"对举的文本。如果你的语料里根本没有几处这种表述,模型自然学不出这条关系路径。
所以,做类比推理评测时,不应该拿英文的经典词对直接搬到中文,而要从你自己的语料里抽样构造类比问题。比如在电商语料里可以构造手机: 充电器 :: 笔记本电脑 : ?,在法律语料里可以构造原告: 起诉 :: 被告: ?。只有基于语料语义结构的类比问题,才能真实反映模型在该语料上的学习效果。
5.2 相似度评测与可视化诊断
如果你手头有标注好的词对相似度数据集,可以做一次定量评测。以中文为例,可以找一些公开的词相似度评测集,里面包含几百对词以及人工评分的均值。评测时计算模型输出的Cosine相似度与人工评分的Spearman或Pearson相关系数。一般能达到0.5以上的相关系数就算及格,0.65以上算相当不错。
不过,公开评测集大多基于通用语料构建,如果你训练的是领域词向量,拿通用评测集衡量本来就不公平。更合理的做法是做个简单的领域内评测:请两三个人,从训练语料里挑50对词,分别标注"语义相近/无关"二分类,然后看模型输出的相似度能不能很好地区分这两组。这不难做,但比任何公开数据集都更能反映你的实际效果。
还可以用可视化做定性诊断。用TSNE把词向量降维到二维平面,挑一些语义类别明显的词画出来,看同类词是否聚在一起:
from sklearn.manifold import TSNE import matplotlib.pyplot as plt words = ["手机", "电脑", "平板", "苹果", "香蕉", "西瓜", "跑步", "游泳", "篮球"] word_vectors = [model.wv[w] for w in words] tsne = TSNE(n_components=2, random_state=0, perplexity=5) coords = tsne.fit_transform(word_vectors) plt.figure(figsize=(8, 8)) for i, word in enumerate(words): x, y = coords[i] plt.scatter(x, y) plt.annotate(word, (x, y), fontsize=12) plt.show()如果"手机/电脑/平板"聚在一起,"苹果/香蕉/西瓜"聚在另一块,而"跑步/游泳/篮球"在第三个区域,说明模型的基本语义布局没问题。如果这些词完全混杂在一起,那就要回到语料和参数去找问题了。
5.3 常见问题对照表:症状、原因与处理方案
最后整理一张问题对照表,这是我在多次排查词向量"准确度太低"问题时总结出来的,可以直接对照使用。
| 症状 | 常见原因 | 处理方案 |
|---|---|---|
| 所有词的相似词都是高频停用词 | 停用词过滤不彻底,高频词干扰了训练 | 定制停用词表,提高sample下采样参数 |
| 相似词呈现出明显的"词语搭配相关"而非"语义相近" | 语料领域混杂,或窗口过大 | 缩小窗口到3~5,或使用领域垂直语料 |
| 低频词向量质量差,相似词全是噪音 | 语料量不足,低频词样本太少 | 增加语料量,降低min_count,改用Skip-gram |
| 类比推理结果差 | 语料中缺少对应的语义关系样本 | 检查语料中相关词对的共现频次,补充语料 |
| 训练loss不下降或震荡 | 学习率偏大,或数据噪音多 | 降低初始alpha,加强清洗与过滤 |
| 同一份数据每次训练结果差异大 | 多线程训练的不确定性 | 固定seed,适当减少workers,甚至使用单线程 |
| 下游任务效果提升不明显 | 词向量粒度与任务需求不匹配 | 尝试微调词向量,或考虑训练面向任务的词向量 |
| 中文分词错误导致相似词怪异 | 分词器词典与领域不匹配 | 添加领域自定义词典,重新分词 |
5.4 别的方案什么时候考虑
做完所有排查后,如果词向量效果仍然不行,那就要停下来想想:是不是Word2vec本身不适合你这个场景?这不可耻,模型选型本来就是反复试出来的。
Word2vec的局限在于它只能给每个词一个固定的向量表示,多义词的所有义项被"平均"在了一个向量里。"苹果"既指水果又指公司,在Word2vec的向量空间里只能是一个混合位置,这在某些场景下不够用。如果你的任务强依赖语境消歧,比如情感分析里同一句话因语境不同含义完全不同,那Word2vec可能天生就不够用。
这时候可以考虑几个升级方向。FastText解决了OOV问题,因为它把词的子词特征也融入了训练,对中文这种字词混合语言比较友好。BERT这类预训练模型则通过注意力机制为每个词在不同上下文里生成动态向量,多义词问题迎刃而解,代价是显存和推理时间显著上升。还有一条路是训练领域自适应的静态词向量,比如在目标语料上继续用Word2vec做增量训练,从通用预训练词向量初始化,再加领域语料微调。这个方法我推荐过好几个做垂直领域项目的朋友,成本低、见效快。
6. 一些实操中的个人体会
词向量这个领域,看起来简单,跑通容易,跑好难。我踩过很多次坑之后,养成了三个固定的工作习惯,分享出来供参考。
第一,**训练前先写评测脚本,再碰参数。**哪怕只是一个简单的"种子词Top-N打印",也比黑盒训练强。没有评测机制,你永远分不清是语料问题,还是参数问题,还是评测口径问题。
第二,**调参每次只改一个变量。**很多人喜欢一次把vector_size、window、epochs全改一遍,最后效果好了但不知道谁的功劳,效果差了也不知道谁拖的后腿。慢慢调、记录每次变化,虽然慢,但积累的经验是可复用的。
第三,**把词向量训练当成迭代任务而不是一次性任务。**每跑完一轮,把相似词结果、评测分数记录下来,过一段时间回头看,能发现很多当初没注意到的规律。我几年前在某电商项目里训练的词向量,当时觉得效果平平,后来换了更大的语料重训,再对比旧模型的评测记录,才意识到当初的问题不在参数,而在于语料里混入了大量无意义的口碑水文。
词向量的世界里没有一劳永逸的配置,只有反复实验和持续积累。希望这篇反思能帮你少走一些弯路,早日训出自己满意的词向量。