词向量与Embedding究竟是怎么回事?
By 苏剑林 | 2016-12-03 | 276422位读者 |词向量,英文名叫Word Embedding,按照字面意思,应该是词嵌入。说到词向量,不少读者应该会立马想到Google出品的Word2Vec,大牌效应就是不一样。另外,用Keras之类的框架还有一个Embedding层,也说是将词ID映射为向量。由于先入为主的意识,大家可能就会将词向量跟Word2Vec等同起来,而反过来问“Embedding是哪种词向量?”这类问题,尤其是对于初学者来说,应该是很混淆的。事实上,哪怕对于老手,也不一定能够很好地说清楚。
这一切,还得从one hot说起...
五十步笑百步 #
one hot,中文可以翻译为“独热”,是最原始的用来表示字、词的方式。为了简单,本文以字为例,词也是类似的。假如词表中有“科、学、空、间、不、错”六个字,one hot就是给这六个字分别用一个0-1编码:
$$\begin{array}{c|c}\hline\text{科} & [1, 0, 0, 0, 0, 0]\\
\text{学} & [0, 1, 0, 0, 0, 0]\\
\text{空} & [0, 0, 1, 0, 0, 0]\\
\text{间} & [0, 0, 0, 1, 0, 0]\\
\text{不} & [0, 0, 0, 0, 1, 0]\\
\text{错} & [0, 0, 0, 0, 0, 1]\\
\hline
\end{array}$$
那么,如果表示“科学”这个词,那么就可以用矩阵
$$\begin{pmatrix}1 & 0 & 0 & 0 & 0 & 0\\
0 & 1 & 0 & 0 & 0 & 0 \end{pmatrix}$$
大家可能感觉到问题了,有多少个字,就得有多少维向量,假如有1万字,那么每个字向量就是1万维(常用的字可能不多,几千个左右,但是按照词的概念来看,常用的词可能就有十几万了)。于是就出来了连续向量表示,比如用100维的实数向量来表示一个字,这样就大大降低了维度,降低了过拟合的风险,等等。初学者是这样说的,不少专家也是这样说的。
然而事实是:放屁!放屁!放屁!重要的事情说三遍。
给大家出道题大家给明白了:给两个任意实数型的100阶矩阵让你算它们的乘积,可能没几个人能够算出来;可是,给你两个1000阶的矩阵,但其中一个是one hot型(每一行只有一个元素为1,其它都是0)的矩阵,让你相乘,你很快就能算出来了,不信你就试试。
看出问题来了吧?one hot矩阵是庞大,但是人家好算,你那个什么鬼实数矩阵,虽然维度小,但是算起来还麻烦呢(虽然这点计算量对于计算机来说算不了什么)!当然,更深刻的原因还在下面。
似非而是 #
我们真的去算一次
$$\begin{pmatrix}1 & 0 & 0 & 0 & 0 & 0\\
0 & 1 & 0 & 0 & 0 & 0 \end{pmatrix}\begin{pmatrix}w_{11} & w_{12} & w_{13}\\
w_{21} & w_{22} & w_{23}\\
w_{31} & w_{32} & w_{33}\\
w_{41} & w_{42} & w_{43}\\
w_{51} & w_{52} & w_{53}\\
w_{61} & w_{62} & w_{63}\end{pmatrix}=\begin{pmatrix}w_{11} & w_{12} & w_{13}\\
w_{21} & w_{22} & w_{23}\end{pmatrix}$$
左边的形式表明,这是一个以2x6的one hot矩阵的为输入、中间层节点数为3的全连接神经网络层,但你看右边,不就相当于在$w_{ij}$这个矩阵中,取出第1、2行,这不是跟所谓的字向量的查表(从表中找出对应字的向量)是一样的吗?事实上,正是如此!这就是所谓的Embedding层,Embedding层就是以one hot为输入、中间层节点为字向量维数的全连接层!而这个全连接层的参数,就是一个“字向量表”!从这个层面来看,字向量没有做任何事情!它就是one hot,别再嘲笑one hot的问题了,字向量就是one hot的全连接层的参数!
那么,字向量、词向量这些,真的没有任何创新了吗?有的,从运算上来看,基本上就是通过研究发现,one hot型的矩阵相乘,就像是相当于查表,于是它直接用查表作为操作,而不写成矩阵再运算,这大大降低了运算量。再次强调,降低了运算量不是因为词向量的出现,而是因为把one hot型的矩阵运算简化为了查表操作。这是运算层面的。思想层面的,就是它得到了这个全连接层的参数之后,直接用这个全连接层的参数作为特征,或者说,用这个全连接层的参数作为字、词的表示,从而得到了字、词向量,最后还发现了一些有趣的性质,比如向量的夹角余弦能够在某种程度上表示字、词的相似度。
对了,有人诟病,Word2Vec(CBOW)只是一个三层的模型,算不上“深度”学习,事实上,算上one hot的全连接层,就有4层了,也基本说得上小小的深度模型了。
从何而来 #
等等,如果把字向量当做全连接层的参数(这位读者,我得纠正,不是“当做”,它本来就是),那么这个参数你还没告诉我怎么得到呢!答案是:我也不知道怎么得来呀。神经网络的参数不是取决你的任务吗?你的任务应该问你自己呀,怎么问我来了?你说Word2Vec是无监督的?那我再来澄清一下。
严格来讲,神经网络都是有监督的,而Word2Vec之类的模型,准确来说应该是“自监督”的,它事实上训练了一个语言模型,通过语言模型来获取词向量。所谓语言模型,就是通过前$n$个字预测下一个字的概率,就是一个多分类器而已,我们输入one hot,然后连接一个全连接层,然后再连接若干个层,最后接一个softmax分类器,就可以得到语言模型了,然后将大批量文本输入训练就行了,最后得到第一个全连接层的参数,就是字、词向量表,当然,Word2Vec还做了大量的简化,但是那都是在语言模型本身做的简化,它的第一层还是全连接层,全连接层的参数就是字、词向量表。
这样看,问题就比较简单了,我也没必要一定要用语言模型来训练向量吧?对呀,你可以用其他任务,比如文本情感分类任务来有监督训练。因为都已经说了,就是一个全连接层而已,后面接什么,当然自己决定。当然,由于标签数据一般不会很多,因此这样容易过拟合,因此一般先用大规模语料无监督训练字、词向量,降低过拟合风险。注意,降低过拟合风险的原因是可以使用无标签语料预训练词向量出来(无标签语料可以很大,语料足够大就不会有过拟合风险),跟词向量无关,词向量就是一层待训练参数,有什么本事降低过拟合风险?
最后,解释一下为什么这些字词向量会有一些性质,比如向量的夹角余弦、向量的欧氏距离都能在一定程度上反应字词之间的相似性?这是因为,我们在用语言模型无监督训练时,是开了窗口的,通过前$n$个字预测下一个字的概率,这个$n$就是窗口的大小,同一个窗口内的词语,会有相似的更新,这些更新会累积,而具有相似模式的词语就会把这些相似更新累积到可观的程度。我举个例子,“忐”、“忑”这两个字,几乎是连在一起用的,更新“忐”的同时,几乎也会更新“忑”,因此它们的更新几乎都是相同的,这样“忐”、“忑”的字向量必然几乎是一样的。“相似的模式”指的是在特定的语言任务中,它们是可替换的,比如在一般的泛化语料中,“我喜欢你”中的“喜欢”,以及一般语境下的“喜欢”,替换为“讨厌”后还是一个成立的句子,因此“喜欢”与“讨厌”必然具有相似的词向量,但如果词向量是通过情感分类任务训练的,那么“喜欢”与“讨厌”就会有差异较大的词向量。
未完不待续 #
感觉还没说完,但好像也没有什么好说的了,希望这点文字有助于大家理解字、词向量这些概念,弄清楚one hot和Embedding的本质。有疑问或者有新的见解,欢迎留言提出。
转载到请包括本文地址:https://kexue.fm/archives/4122
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Dec. 03, 2016). 《词向量与Embedding究竟是怎么回事? 》[Blog post]. Retrieved from https://kexue.fm/archives/4122
@online{kexuefm-4122,
title={词向量与Embedding究竟是怎么回事?},
author={苏剑林},
year={2016},
month={Dec},
url={\url{https://kexue.fm/archives/4122}},
}
February 17th, 2017
赞
April 15th, 2017
可否理解为特定环境中使用模式相同的词的向量就是相似的。
是的,语境类似,词语就类似。
那么无论是ngram还是skip-gram什么的,就是取上下文的方式不同而已吧?如果是这样的话,样本的构造就是将一个窗口的词分别变成one-hot向量,进而排列一个one-hot矩阵即可?
你的理解是对的,但不用表示为one hot,直接表示为词的id对就行了,embedding层负责将id映射为向量(数学等价于one hot矩阵乘法)
June 5th, 2017
最后一段写的“词向量是通过情感分类任务训练的,那么“喜欢”与“讨厌”就会有截然不同的词向量。”
这个可以理解为神经网络最底层连接一个情感分类的输出层吗?而不是连接原来的词预测层。
正是如此。
July 11th, 2017
那么keras中的embedding层具体做的就是把id映射成 词向量 吗?
具体实现的方法 和 word2vec 相比 有什么不同?
请把本文反复阅读10次
September 30th, 2017
楼主你好,我没有用什么语言模型,就看了IMDB的例子代码。请问embedding的代码实现里one-hot乘的这个w字向量矩阵,是随机的一些数还是某个有语义的pre-define在keras里的向量阵?
全连接层怎么做,它就怎么做,因为它就是一个全连接层:随机初始化,然后训练过程中更新
如果我输入的是token过得纯文本,然后网络输出是不同的类预测结果。
其中的embedding层没用像word2vec和glove的预训练词向量嵌入。
那么随机初始化的这些向量经过训练更新后能理解为是一种特殊的语义向量吗?
不用任何预训练的词向量嵌入,有道理吗?
我用pretrained emeddings的结果没有random initialized embedding好,
但是random initialed很快就overfit了。
不是很明白,这是embedding的代码https://github.com/fchollet/keras/blob/master/keras/layers/embeddings.py#L11
或者我可以理解为训练更新后的weights可以作为一种词向量?
embedding层的本质就是one hot全连接,参数量很大,直接让它在训练过程中更新,自然是容易过拟合的。至于效果是不是一定会比pretrained的要好或者差,则得结合任务来看,不一定的。
至于你说的,随机初始化后经过训练得到的embedding,的确就是一种特殊的词向量了。事实上这跟word2vec的更新原理是一样的,只不过word2vec的更新目标(loss)不一样罢了。
最后,必须明确的是,使用pretrained的词向量,目的一般有两个(取其一):
1、如果在模型训练的时候,不微调词向量,那么就是为了获得标注数据集以外的泛化能力,因为无监督pretrained相对标注数据集来说,也许能补充很多标注集没有的词语;
2、如果在模型训练的时候,微调词向量(先固定词向量训练模型到饱和,然后降低学习率,放开词向量再训练),那么pretrained词向量的目的就是为了获得更好的初始化点。
October 18th, 2017
想请问下,之前训练用的一部分文本,训练好模型。预测时的文本如果跟训练文本比新增加了词汇,怎么解决,求指教
可能。。或许。。。是删掉。。。
或者随机产生一个向量。。。
November 10th, 2017
我觉得有两种方式:
1. 用unk表示不在词典中的词(训练样本中也会有这样的词,因为很多情况下词典都会过滤出现次数太少的词)
2. 一开始用一个很大的词库训练一个embedding层,之后训练和测试的时候词的embedding层参数都用这个预训练好的embedding层参数,并且设定它不会更改即可。训练和测试的词则都用那个很大的词库来表示
我觉得一般用0向量代替新词汇。不一定是预测,哪怕训练时就有可能遇到词表中没有的词,训练时就用0向量代替,让模型监督学习到这个性质就是了
December 21st, 2017
xia jiba shuo.duo xuexi.
感觉哪里有问题,说出来一起讨论,何必这样?
January 30th, 2018
博主,我想请问一下,自定义的损失也只能传入两个参数吗?现在想给不同的样本的损失添加一个权重应该怎么修改损失呀
自定义一个Input层,作为权重,然后自定义Lambda层,直接输出loss
https://kexue.fm/archives/4493/
April 23rd, 2018
为什么word2vec的隐层不容非线性激活函数呢?
一般来说,用非线性激活函数的原因是“线性运算是可以复合的,如果不加激活函数,多层神经网络就等效于两层(输入加输出)”。事实上这种说法还不够严谨。
比如“输入100维、隐层10维、输出100维”的三层神经网络,就算每一层都不加激活函数,它也不等价于“输入100维、输出100维”的两层神经网络。
word2vec正是这样的情况,本身就是压缩降维,没必要用非线性激活函数了。