一些NLP
简单的例子,判断一个词的词性,是动词还是名词。用机器学习的思路,我们有一系列样本(x,y),这里 x 是词语,y 是它们的词性,我们要构建 f(x)->y
的映射,但这里的数学模型 f(比如神经网络、SVM)只接受数值型输入,而 NLP 里的词语,一般的表现形式并不是数值型,所以需要把他们转换成数值形式,或者说——嵌入到一个数学空间里。这种嵌入方式,就叫词嵌入(word embedding),而 Word2vec,就是词嵌入( word embedding) 的一种。
一般的数学模型只接受数值型输入,这里的x经常使用one-hot encoder,这种方式忽略了句子当中词语的次序,但是将每个词装换成为一个列表,词语之间的距离都为1。例如’我喜欢你’,转化成3个词:’’我’,’喜欢,’你’,三个word分别可以表示为可以表示为[1,0,0],[0,1,0],[0,0,1]
现在已经可以将句子中的词转化成为数值型数据,输入到模型中,为什么还要word2vec呢?
word2vec从大量文本语料中以无监督的方式学习语义信息,即通过一个嵌入空间使得语义上相似的单词在该空间内距离很近。比如“机器”和“机械”意思很相近,而“机器”和“猴子”的意思相差就很远了,那么由word2vec构建的这个数值空间中,“机器”和“机械”的距离较“机器”和“猴子”的距离而言是要近很多的。
word2vec是将onehot encoder后的数据,通过训练映射到新的特征空间中。原始的onehot-encoder是将所有word的间隔(距离)都设置为1,这在实际中是不复合认知的。word2vec中,相似的word的向量在距离上也是相近的,没什么关系的距离较远;同时,对于一个大文本来讲,词形成的词典大小很大,相应的表示为每个词需要的长度也很大,这些词中有用的信息很少,只有那个为1的index是有用的,表示形式很sparse,稀疏,word2vec的size一般情况下远远小于词语总数,相当于将onehot-encoder降维。
我们来看个例子,如何用 Word2vec 寻找相似词:
- 对于一句话:『她们 夸 吴彦祖 帅 到 没朋友』,如果输入 x 是『吴彦祖』,那么 y 可以是『她们』、『夸』、『帅』、『没朋友』这些词
- 现有另一句话:『她们 夸 我 帅 到 没朋友』,如果输入 x 是『我』,那么不难发现,这里的上下文 y 跟上面一句话一样
- 从而 f(吴彦祖) = f(我) = y,所以大数据告诉我们:我 = 吴彦祖(完美的结论)
Skip-gram/CBOW
一个word作为输入,预测周围的上下文,模型叫做Skip-gram模型;
一个word的上下文(窗口)作为输入,预测这个word本身,模型叫做CBOW模型
先以Skip-gram为例,一个word x作为输入,预测周围的一个word y为输出
x是上面提到的经过onehot-encoder形式的输入,y是在V(词典的size)个词上输出的概率,期望形式是y真实的onehot-encoder
在这里,跟常规的NN不同的是,Wordvec的隐层激活函数是线性的,而非sigmoid、relu这些非线性形式。
当模型训练完成之后,需要的的是神经网络的权重。
比如现在输入一个 x 的 one-hot encoder: [1,0,0,…,0],对应刚说的那个词语『吴彦祖』,则在输入层到隐含层的权重里,只有对应 1 这个位置的权重被激活,这些权重的个数,跟隐含层节点数是一致的,从而这些权重组成一个向量 vx 来表示x,而因为每个词语的 one-hot encoder 里面 1 的位置是不同的,所以,这个向量 vx 就可以用来唯一表示 x。这就是Word2vec的核心思想。
如图,这里有两个部分的参数W,分别是输入到隐层以及隐层到输出,这两部分都可以作为词向量形式,默认使用输入到隐层的参数作为 词向量。
假设共有词10000个,则one-hot encoder有10000维,对于一对一的预测,输入是10000维的onehot encoder,输出是10000维的每个词出现的概率值(一般通过softmax层进行归一化),这样维度实在是太大了。我们设置隐层,有神经元500个,那么参数w就是10000500,现在一个词可以表示为500维度。方法是训练后的参数10000500,对于输入x,找到值为1的index,提取出来对应的w[index]即可。
上面的形式是极端情况,通过一个word来预测周围的一个word或者通过上下文的一个word来预测word。上面提到过,CBOW是上下文预测word,Skip-gram是word预测上下文,常规形式如下:
区别
CBOW用于普通的数据量计算;Skip-gram用于数据量较大的情况。
测试
- 获取语料库
- 对语料进行分词(jieba分词)、去停用词
- 将分词、去停用词后的语料填充到word2vec模型中训练(使用
gensim.models.word2vec
) - 使用model[word]获得词向量,使用model.similarity(word1,word2)计算word间的相似度
一些进阶
Word2vec 本质上是一个语言模型,它的输出节点数是 V 个,对应了 V 个词语,本质上是一个多分类问题,但实际当中,词语的个数非常非常多,会给计算造成很大困难,所以需要用技巧来加速训练。
hierarchical softmax
本质是把 N 分类问题变成 log(N)次二分类
negative sampling
本质是预测总体类别的一个子集
BOW
Bag of words词袋模型。忽略了文本的语法和语序等要素(举个例子,Bow模型认为“我爱你”和“你爱我”是相同的),将其仅仅看作是若干词汇的集合。BoW 使用一组无序的单词(word)来表达一段文字或一个文档,并且文档中每个单词的出现都是独立的。
举例:
John likes to watch movies. Mary likes too.
John also likes to watch football games.
基于上述两个文档中出现的单词,构建如下一个词典 (dictionary):
{'John': 1, 'likes': 2,'to': 3, 'watch': 4, 'movies': 5,'also': 6, 'football': 7, 'games': 8,'Mary': 9, 'too': 10}
上面的词典中包含10个单词, 每个单词有唯一的索引, 那么每个文本我们可以使用一个10维的向量来表示,向量中的元素是词典中对应的词语出现的频数。如下所示:
1 | [1, 2, 1, 1, 1, 0, 0, 0, 1, 1] |
该向量与原来文本中单词出现的顺序没有关系,而是词典中每个单词在文本中出现的频数
哈夫曼树
满二叉树:除了叶节点外每一个结点都有左右子女且叶节点都处在最底层的二叉树。
完全二叉树:只有最下面的两层结点度小于2,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
平衡二叉树:又称为AVL树,它是一颗空树或它的左右两个子树的高度差的绝对值不超过1
哈夫曼树:带权路径长度达到最小的二叉树,也叫做最优二叉树。
注意到这里,哈夫曼树只是一棵最优二叉树,不一定是完全二叉树,也不一定是平衡二叉树。
fastText的霍夫曼树叶子结点对应为最终的label,可以看到,权重最大的,也就是权重最大的label,其深度最小,fastText 充分利用了这个性质,使得其速度得以提升。
fastText
fastText通过上下文预测标签(这个标签就是文本的类别,是训练模型之前通过人工标注等方法事先确定下来的),沿用了CBOW的单层神经网络的模式。
fastText模型的输入是一个词的序列(一段文本或者一句话),输出是这个词序列属于不同类别的概率。在序列中的词和词组构成特征向量,特征向量通过线性变换映射到中间层,再由中间层映射到标签。fastText在预测标签时使用了非线性激活函数,但在中间层不使用非线性激活函数。
第一个权重矩阵$w_1$可以被视作某个句子的词向量,然后被平均成一个文本表征,然后其会被馈送入一个线性分类器。这个构架和CBOW模型相似,只是中间词(middle word)被替换成了标签(label)。
模型将一系列单词作为输入并产生一个预定义类的概率分布。使用softmax方程来计算这些概率。当数据量巨大时,线性分类器的计算代价十分大,所以fastText使用了一个基于霍夫曼编码树的分层softmax方法。
常用的文本特征表示方法是词袋模型,然而词袋(BoW)是忽略词序的。作为替代,fastText使用n-gram获取额外特征来得到关于局部词顺序的部分信息。
分层softmax
分层softmax的目的是降低softmax层的计算复杂度。
把原来的softmax看做深度为1的树,词表V中的每一个词语表示一个叶子节点。如果把softmax改为二叉树结构,每个word表示叶子节点,那么只需要沿着通向该词语的叶子节点的路径搜索,而不需要考虑其它的节点。这就是为什么fastText可以解决不平衡分类问题,因为在对某个节点进行计算时,完全不依赖于它的上一层的叶子节点(即权重大于它的叶结点),也就是数目较大的label不能影响数目较小的label
具体说来,当遍历树的时候,我们需要能够计算左侧分枝或是右侧分枝的概率值。
哈夫曼树与分层softmax的关系:fasttext输出是一个哈夫曼树,模型构造了一颗哈夫曼树,计算softmax的时候,需要计算向左还是向右,这里就需要基于词频作为权重构造的哈夫曼树结构,在这里加快了训练速度。
fasttext采用了基于哈夫曼树的分层softmax方法。label样本多的,离顶点近,样本少的离顶点远,综合起来时间复杂度为O(hlog2k)
值得注意的是,此方法只是加速了训练过程,因为我们可以提前知道将要预测的词语(以及其搜索路径)。在测试过程中,被预测词语是未知的,仍然无法避免计算所有词语的概率值。
fasttext输入是简单的BOW模型加上局部的ngram(考虑词序),每个哈夫曼的节点向量都是训练的结果,也是通过梯度向下方法不断拟合。最后与相应的h(input)相乘,得出偏向左还是偏向右。
fasttext的hierarhical loss,粗略地说,是将所有的label,也就是所有的单词,放到一个哈夫曼树里面,出现频率越少的单词越接近叶子端。假设我们有一万个单词在词典里面,简单的softmax是一个10000类的分类问题;假设我们的一个目标单词在哈夫曼树上面是01001位置(比如我们用0表示左子树,1表示右子树)那么在这个样本上的分类问题是5个二分类问题:我们期望预测的单词在这条路径上每一个位置都更倾向于正确的方向。
fasttext中的ngram
常用的特征是词袋模型,在第一部分小编已经介绍过词袋模型了。词袋模型不考虑词之间的顺序,因此 fastText 还加入了 N-gram 特征。
“我爱你”:如果使用2-gram,这句话的特征还有 “我-爱”和“爱-你”,这两句话“我爱你”和“你爱我”就能区别开来了,因为“你爱我”的2-gram的特征还包括“你-爱”和“爱-我”,这样就可以区分“你爱我”和“我爱你”了。为了提高效率,实务中会过滤掉低频的 N-gram。否则将会严重影响速度。
在fastText 中一个低维度向量与每个单词都相关。隐藏表征在不同类别所有分类器中进行共享,使得文本信息在不同类别中能够共同使用。这类表征被称为词袋(bag of words)(此处忽视词序)。在 fastText中也使用向量表征单词 n-gram来将局部词序考虑在内,这对很多文本分类问题来说十分重要。
用途
文本分类
同近义词的挖掘