word2vec

word2vec简介

word2vec是把一个词转换为向量,变为一个数值型的数据。

主要包括两个思想:分词和负采样

使用gensim库——这个库里封装好了word2vector模型,然后用它训练一个非常庞大的数据量。

自然语言处理的应用

拼写检查——P(fiften minutes)>P(fiftenminutes),然后依据这些进行纠错

关键词检索

文本挖掘

文本分类——体育类?新闻类?

机器翻译——通过之前学习到的信息进行概率分析

客服系统——人工客服

复杂对话系统——微软小冰,长时间的交流

 语言模型

我 今天 下午 打 篮球

P(s)=p(w1,w2,w3,...,wn)=p(w1)p(w2|w1)p(w3|w1,w2)...p(wn|w1,w2,...,wn-1)

P(s)被称为语言模型,即用来计算一个句子概率的模型。

p(wi|w1,w2,...,wi-1)=p(w1,w2,...,wi-1,wi)/p(w1,w2,...,wi-1)

即预测一个词,这个词和之前所有的词都相关。

导致的问题:

  1.数据过于稀疏——一个词和之前相关的词的数量较少

  2.参数空间太大——数据过于稀疏造成。

解决办法:

  1.假设下一个词的出现依赖它前面的一个词:

  p(s)=p(w1)p(w2|w1)p(w3|w1,w2)..p(wn|w1,w2,...,wn-1)

    =p(w1)p(w2|w1)p(w3|w2)...p(wn|wn-1)

  2.假设下一个词的出现依赖它前面的两个词:

  p(s)=p(w1)p(w2|w1)p(w3|w1,w2)..p(wn|w1,w2,...,wn-1)

    =p(w1)p(w2|w1)p(w3|w1,w2)...p(wn|wn-1,wn-2)

这样的解决办法称为:n-gram模型,指定n为2,表示当前词与之前的两个词有关。

n-gram模型

通过这个表计算p(I want chinese food)=p(want|I)*p(chinese|want)*p(food|chinese)

第一个表是统计文章中每个词出现的频率;

第二个表是统计每个词与其他词一起出现的概率(n=1),比如当i时,第二个词也出现i有5词,第二次出现want有827个词,第二次出现to有0词;

第三个表求的是近似概率,p(want|I)=p(I,want)/p(I)=827/2533=0.33

那么p(I want chinese food)=p(want|I)*p(chinese|want)*p(food|chinese)就可以根据第三个表求出来。

这个模型的复杂度为:

N表示语料库的大小,n表示n-gram中的n,与前几个词有关。我们一半选择n为2和3就可以了,当n太大的时候就很难计算了,虽然现在的计算机能力能够计算n为10的情况,但是没有必要花这么的计算量提高比较小的性能,一半n为2和3就够用了。

词向量

无论是一句话还是一篇文章,都是一个词一个词组成的,这是基本单位,怎么让机器认识他们呢?

  1.one-hot转换方式,有多少个词就有多少个位数,词对应位置就是1,其余位为0,但是这样缺少词与词之间的关系;

  2.word2vec,这是我们现在用的比较好的模型。

这是word2vec的样子:

其空间结构如下:

我们可以看到had,has,have意思相近,其向量空间的位置也相近 ,need和help经常一起出现的,也会挨的比较相近。

现在我们想知道谁和青蛙挨的比较近:

比如说,‘今天的菜比较便宜’和‘今天的菜比较贱’其实表达的都是一个含义,在语言中许多相近的词,需要让计算机知道这是说的一回事儿。

下边左边是英语的数字右边是西班牙语的数字,构造出来的空间结构也是相似,这说明word2vec构造出来的模型与语言无关,而是与具体的逻辑有关。

神经网络模型

词向量模型与神经网络模型有什么关系呢?

我们把神经网络模型分成四部分:

比如我输入:‘我’,‘今天’,‘下午’,‘打’然后预测后一个词是什么

如上图所示,

input layer表示的输入层,

第二个是projection layer,即投影层,我们要把输入的词首尾相连组合在一起,这里和传统神经网络不一样,我们这里我们要求出来神经网络每个层之间的参数和输入,传统的神经网络只要求神经网络层之间的参数即可。

第三个是隐藏层

最后一个是输出层 

根据最后的输出进行优化。

神经网络模型的优点:

网咖和网吧是同一个词,对于N-gram模型会统计词频,因为词频不一样,所以导致结果不一样,而使用神经网络模型之后,我们发现网咖和网吧在向量空间非常的相近,所以会被认为是一样的。

在神经网络模型看来,狗和猫都是一样的,都是动物再跑,所以会把他们加起来。

Hierarchical softmax

即分层的softmax。

首先对于神经网络的实现有两种形式:

CBOW模型,即continuous bag of words模型,输入的是上下文词,预测的是当前一个词,

设为一个似然函数,希望这个函数越大越好:

设计这个网络需要用到哈夫曼树

b图把权值最大的放在前边,比如我们整体的语料库有1万个词,我们要在输出层预测一万个词的所有的概率,普通神经网络中我们同等对待他们。但是我们常用的词就那么那么多,我们可以着重关注词频出现次数较多的词,我们在分层softmax中,首先进行一个判断,一个二分类问题(使用sigmoid函数),逐一的进行判断。

Skip-gram模型,输入是当前的词,预测的是这个词的上下文词。

CBOW模型实例

二分类问题就是使用logistic回归。

我们用这个的二分类来判断分层softmax中往左子树走还是往右子树走的概率。

首先看一下哈夫曼树中分层softmax的概念:

举个例子:

通过上下文,我们想通过神经网络输出‘足球’,我们可以把高频词往上移动,每个节点都是二分类问题。我们预测‘足球’会走途中红线的路径。第一层的softmax往左走,第二层,往右走,第三次右走,第四次是往左走。最后我们要把所有的概率乘在一起。

CBOW求解目标 

要求接的内容包括两个部分:输入向量怎么改变,神经网络中的参数怎么改变

要优化的目标是L似然函数,和logisitic计算类似。

梯度上升求解

既然是求一个最大值的问题,那么就可以用梯度上升进行求解。

分成两部分,分别计算θ和x的更新。下边是它们的更新公式:

负采样模型

即使我们使用哈夫曼树进行解决这个问题,依然面临着问题:当语料库非常大的时候,一些不太常用的词会排到非常后的位置。时间复杂度依然非常的高,为此提出了一个新的解法,即负采样模型——Negative Sampling

给一个上下文,预测对了就是这样一个词,预测错了就不是这个词。

当我们预测‘足球’,我们正样本就是‘足球’,负样本就是非足球的所有词,负样本非常的多。

我们从负样本中进行采样,然后进行最大化即可。

词频越大,越容易被随机采样到。

使用Gensim库构造词向量

Gensim是一个比较好的开源库。

model=word2vec.Word2Vec()中有两个参数常用参数

from gensim.models import word2vec
import logging

logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')

raw_sentences=['the quick brown fox jump over the lazy dogs','yoyoyo you go home now to sleep']#输入两句话

sentences=[s.split() for s in raw_sentences]#分词
print(sentences)

#sentences表示用来进行训练的文本,min_count控制某些出现次数较低的词
model=word2vec.Word2Vec(sentences,min_count=1)

#输出'dog'和'you'的相似度
print(model.similarity('dogs','you'))

维基百科中文数据处理

维基百科里边有很大的数据,这个网站里又他们收集到的数据,正常我们下载下来的数据都是繁体的,包含大量的信息,比如包括一系列的文章。维基百科和百度百科是类似的,知识百度百科的东西我们无法下载下来。下载格式为:xml.bz2。

网站:https://dumps.wikimedia.org/zhwiki/

但是我们下载下来的内容是繁体的,opencc工具可以用来将繁体转换成中文的。

如果不想用维基百科,用其他的语料库也是可以的,比如用小说等都是可以的。

Gensim构造word2vec模型

jieba分词,每一行都进行分词,如下是使用结巴分词对维基百科的txt文档进行分词,然后将分词结果进行存储。

import jieba
import jieba.analyse
import jieba.posseg as pseg
import codecs,sys
def cut_words(sentence):
    #print sentence
    return " ".join(jieba.cut(sentence)).encode('utf-8')
f=codecs.open('wiki.zh.jian.text','r',encoding="utf8")##需要读入的文件名
target = codecs.open("zh.jian.wiki.seg-1.3ggg.txt", 'w',encoding="utf8")##需要写入的文件名
print ('open files')
line_num=1
line = f.readline()
while line:
    print('---- processing ', line_num, ' article----------------')
    line_seg = " ".join(jieba.cut(line))
    target.writelines(line_seg)
    line_num = line_num + 1
    line = f.readline()
f.close()
target.close()
exit()

 分词之后就是建模工作,建模工作就是用gensim包进行一个建模工作。

import logging
import os.path
import sys
import multiprocessing
from gensim.corpora import WikiCorpus
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
if __name__ == '__main__':

    ##sys.argv实际上是一个列表,第一个表示程序本身,后边的为程序的参数
    program = os.path.basename(sys.argv[0])
    logger = logging.getLogger(program)
    logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
    logging.root.setLevel(level=logging.INFO)
    logger.info("running %s" % ' '.join(sys.argv))
    # check and process input arguments
    if len(sys.argv) < 4:
        print (globals()['__doc__'] % locals())
        sys.exit(1)
    inp, outp1, outp2 = sys.argv[1:4]##第二个参数为分好词的txt,第三那个参数是模型保存地址,第四个参数是词向量

    ##建模过程挺长的,如果使用的是1g左右的数据,建模过程大概要1个小时左右
    ##指定Word2Vec的参数,
    model = Word2Vec(LineSentence(inp), size=400, window=5, min_count=5, workers=multiprocessing.cpu_count())
    model.save(outp1)
    model.model.wv.save_word2vec_format(outp2, binary=False)
#python word2vec_model.py zh.jian.wiki.seg.txt wiki.zh.text.model wiki.zh.text.vector
#opencc -i wiki_texts.txt -o test.txt -c t2s.json

测试模型相似度结果

然后我们用我们徐莲好的模型进行测试,输出['苹果','数学','学术','白痴','篮球']词最相近的几个词。

代码如下:

from gensim.models import Word2Vec

en_wiki_word2vec_model = Word2Vec.load('wiki.zh.text.model')

testwords = ['苹果','数学','学术','白痴','篮球']
for i in range(5):
    res = en_wiki_word2vec_model.most_similar(testwords[i])##计算这几个词最相近的几个词,默认是前十个词
    print (testwords[i])
    print (res)

 

原文地址:https://www.cnblogs.com/shixisheng/p/9533376.html