word2vec、doc2vec的使用

一、word2vec原理

《个人的总结》:
1、因为直接使用softmax进行预测,对于分类数过大的时候,速度太慢,所以拆分成多个二分类Hierarchical Softmax。
2、使用层次化的softmax方法预测:cbow与skip-gram都是在一幅图上跑的,对于每个类别,它在图上就指定好一条路线。然后从映射层中得到一个向量,让这个向量跑这条路线,就能得到一个序列,有了这个预测序列和原序列(路线),就能计算交叉熵损失了。对交叉熵求偏导,利用梯度下降法,就能更新该路线上的非叶子结点的参数,以及更新映射层传来的向量。
3、分析树形图:
非叶子结点(根结点)权重参数的意义:因为多分类的需要设定的,可以得到逐层概率的序列,再加上原目标序列,于是可以用交叉熵了。
叶子结点的意义:只是代表路线结束,没有参数。
4、在用negative samping 处理skip-gram 模型时,原作者改变了损失函数。实际变成了多个预测一个的CBOW,只是映射层不进行累加了,理由如下:
(1)为w取了|Context(w)|次负采样【本应该是为每个Context(w)的词语负采样一次的】。
(2)更新时,不更新w,而是更新Context(w)的一个。
(3)与中间节点进行相乘的向量,居然是一个Context(w)向量,而不是w。
5、基于层次化softmax预测的cbow模型:
(1)我这边觉得孙老师的论文,不应该重新建立一个用层次softmax预测的cbow模型。这是因为所有的词向量信息都存在非叶子结点处了,而孙老师想“包袱”词的义原更好预测“包袱”,则必须也要基于原预测模型,否则新建层次softmax树会失去原词向量的信息。
(2)总的来说,就是表达:层次化softmax的cbow模型树,词向量信息隐藏在非叶子结点中。如果建立新树,将会抛弃原来的非叶子结点信息,即抛弃了原来的词向量信息。故,我觉得应该继续在第一次的cbow模型树上进行修正。
(3)假如按照我的理解:非叶子结点隐藏有词向量的信息。那样子反观孙老师的论文提到的:“cbow模型会同步更新上下文词向量和词向量。“而他的方案是固定词向量,只改变义原向量。
等价于:softmax层次树中,只更新输入层向量,不更新中间结点词向量。(今天下午,我们看的伪代码,就是只更新这两种向量嘛。)
他这样的原理就是:预测模型(softmax层次树)不变,只修正输入层(义原向量)。
(4)因为非叶子结点作分类(0、1类)的功能,所以它能知道,当前输入的向量想找哪个目标词(实际就是告诉它走哪边)。于是我表达说“非叶子结点是有隐性的词向量”。

实现细节

参考1 参考2
两个模型:CBOW、Skip-gram。
因为softmax复杂度太大,所以提出两种近似学习方法:Hierarchical Softmax、Negative Sampling。
w2v也必须建立图,才能理解好,不然你处理不了的。

如何初始化?

Negative-Sampling(NEG)基于skip-gram模型,但实际上是优化另一个目标函数。

softmax 避免数值过大的办法

参考来源

二、gensim的官方总启api

参考来源
w2v的使用:
1、加载模型。
vectormodel = ‘wiki.zh.text.model’ # 模型文件
model = gensim.models.Word2Vec.load(vectormodel) # 导入训练集
2、找相似度最高的词。
result = model.most_similar(u”计算机”,topn=50000)# topn是返回最相似的个数,默认返回10个。
for e in result:
print e[0], e[1]# 输出:自动化 0.674171924591
3、计算两次的相似度
print model.similarity(u”计算机”, u”自动化”)# 输出:0.67417196002404789
4、在一堆词的找出最不相关的词。
print model.doesnt_match(u”早餐 晚餐 午餐 中心”.split())# 输出:中心
5、查模型中全部的词。

tmp = model.vocab
print type(tmp)# 输出:<type 'builtin_function_or_method'>
print len(tmp)# 4G微博语料是:1077923个词。
for v in tmp:
    print v

6、输出400维的词向量

v = u"男人"
model[v]  # 输出的是400位的向量,本来就是:np.array型,浮点数。

7、应该是unicode码 我平时训练出来的模型才能识别

v = u"月"
v2 = "月"
print v in model,v2 in model # 输出 True False ,也就是v 才是对的。

8、使用C语言的方法,即load_word2vec_format 可能会快许多。此方法只允许查询,不能再训练。参考

model = Word2Vec.load_word2vec_format("wiki.en.text.vector", binary=False) 
model = Word2Vec.load_word2vec_format('/tmp/vectors.bin.gz', binary=True)# binary=True 代表压缩、二进制存储。加载时无需解压的。
或者(gensim官网的例子,但没有实践,不知上面的方式与下面的有什么区别):

>>> from gensim.models.keyedvectors import KeyedVectors
>>> word_vectors = KeyedVectors.load_word2vec_format('/tmp/vectors.txt', binary=False)  # C text format 普通文本格式
>>> word_vectors = KeyedVectors.load_word2vec_format('/tmp/vectors.bin', binary=True)  # C binary format 二进制格式,如压缩包。

两种方式起对应作用的 via `wv.save_word2vec_format()` and `KeyedVectors.load_word2vec_format()`

genism官方实例指导

参考来源

class gensim.models.word2vec.Word2Vec(sentences=None, size=100, alpha=0.025, window=5, min_count=5, max_vocab_size=None, sample=0.001, seed=1, workers=3, min_alpha=0.0001, sg=0, hs=0, negative=5, cbow_mean=1, hashfxn=<built-in function hash>, iter=5, null_word=0, trim_rule=None, sorted_vocab=1, batch_words=10000)

主要的4个参数:min_count 最低词频、size 词向量大小、seed 随机种子、workers 并行数。
1、min_count:如果在训练集中词频达不到这个数值,就会被舍弃。
2、size:神经网络层的大小,也是输出词向量的维度。如果你想设置较大的值,你需要更大的训练语料才比较精准。
3、seed:随机种子。初始化词向量使用。此外,如果你想准确重现,你还需要限制线程数为1,避免操作系统线程调度的抖动。如果是python3,需要设置PYTHONHASHSEED 调节哈希种子。
4、workers:并行数目。前提你安装了Cython 。

1、输入格式:list,utf8编码。主要特点就是按句号再切分了。如两个句子:[[‘first_head’, ‘sentence’], [‘second_head’, ‘sentence’]]
2、内存:对训练集数据是读取完一句,训练参数后,就马上释放内存,用来存放下一句。权重参数存储格式是NumPy arrays,建立3个矩阵(为什么是3个,暂不知道,可能叶子节点用掉1个,非叶子节点等也要用掉一些)。
3、评估:model.accuracy(‘/tmp/questions-words.txt’) 使用这个数据集
4、恢复训练:也就是加载以前的模型,使用新的训练集再次训练。不过不能使用 load_word2vec_format() 基于C语言的模型,因为他们已经删掉词典树了,只有词向量结果。
model = gensim.models.Word2Vec.load(‘/tmp/mymodel’)
model.train(more_sentences)
5、使用模型用例

model.most_similar(positive=['woman', 'king'], negative=['man'], topn=1)
[('queen', 0.50882536)]
model.doesnt_match("breakfast cereal dinner lunch";.split())
'cereal'
model.similarity('woman', 'man')
0.73723527
model['computer']  # raw NumPy vector of a word
array([-0.00449447, -0.00310097,  0.02421786, ...], dtype=float32)

用词向量实现各种各样有趣的东西

参考来源
a.计算两个词语相似度,如图1计算asp与net的相似度为0.6215127
b.列出所有相似词语列表,如图2为“PHP”的结果。
c.寻找对应关系:如图3: 男人-男孩 女人-? 如图4:内蒙-呼和浩特 河北-?
d.删选不合群的词语,可以用来做特征选择:如图5所示:

三、doc2vec的使用

参考1 参考2【工作室师兄的汽车品牌情感项目代码】

def labelizeContent(content, label_type):  # 这是常用的加label函数了,你在网上搜也是差不多这样的。
    labelized = []
    for i, v in enumerate(content):
        label = '%s_%s' % (label_type, i)
        labelized.append(TaggedDocument(v, [label])) #TaggedDocument 与 LabeledSentence是一样效果的,后者是基于前者的。
    return labelized

X_train = [sen.split() for sen in X_train]  # 句子里面的空格需要被删掉,也就是一个句子要变成一个str的list.
X_train = labelizeContent(X_train, 'TRAIN')
X_test = [sen.split() for sen in X_test]
X_test = labelizeContent(X_test, 'VAL')
X_unlabeled = [sen.split() for sen in X_unlabeled]
X_unlabeled = labelizeContent(X_unlabeled, 'TEST')

doc2vec还可以细分为:DM模型、DBOW模型

model_dm = Doc2Vec(min_count=1, window=10, size=size, sample=1e-3, negative=5, workers=3)
model_dbow = Doc2Vec(min_count=1, window=10, size=size, sample=1e-3, negative=5, dm=0, workers=3)

归结全部的语料

方式1

data = np.concatenate((x_train, x_test, unsup_reviews))

方式2

data = X_train[:]
data.extend(X_test[:])
data.extend(X_unlabeled[:])

用全部数据建立词典

model_dm.build_vocab( data )
model_dbow.build_vocab(data)

进行多次重复训练,每一次都需要对训练数据重新打乱,以提高精度

方式1

for epoch in range(10):
    all_reviews = data[:]
    random.shuffle(all_reviews)# shuffle是洗牌的意思。  random.seed(123456),如果上面加了这个语句,那就是固定顺序了,随机将会无效。 import random 这样导入,不是np的。
    model_dm.train(all_reviews)
    model_dbow.train(all_reviews)

方式2

for epoch in range(epoch_num):
        perm = np.random.permutation(all_train_reviews.shape[0]) # 这里用的是np的随机,不同上面random包。 如果前面加了numpy.random.seed(seed=1),随机将会失效。
        model_dm.train(all_train_reviews[perm])
        model_dbow.train(all_train_reviews[perm])

读取向量

方式1

def getVecs(model, corpus, size):
    vecs = [np.array(model.docvecs[z.tags[0]]).reshape((1, size)) for z in corpus]  # tags[0]是根据标签找向量,reshape的意思是重新按照这个矩阵大小排列。例如 a = np.arange(6).reshape((3, 2)) 输出([[0, 1],[2, 3],[4, 5]])
    return np.concatenate(vecs)

方式2.获取句子向量:
1.直接根据句子ID获取句子向量;2.累加每个词向量求和为该句子向量(实测效果更好),但此时特征数只有一维。

def getVecs(model, corpus):
    # vecs = [np.array(model.docvecs[z.tags[0]]).reshape((1, size)) for z in corpus]
    # return np.concatenate(vecs)
    vecs = []
    for text in corpus:
        tmp = [model[w] for w in text.words]
        tmp = np.array(tmp)
        vecs.append( tmp.sum(axis=0) )
    return np.array(vecs)

训练数据向量

train_vecs_dm = getVecs(model_dm, X_train) # getVecs 的第二个参数 X_train = labelizeContent(X_train, 'TRAIN') 是经过上面步骤打上标签了的list。
train_vecs_dbow = getVecs(model_dbow, X_train)
train_vecs = train_vecs_dm + train_vecs_dbow    # 这里可以两个模型向量相加,也可以横向拼接,即 train_vecs = np.hstack((train_vecs_dm, train_vecs_dbow))

验证数据向量

test_vecs_dm = getVecs(model_dm, X_test)
test_vecs_dbow = getVecs(model_dbow, X_test)
test_vecs = test_vecs_dm + test_vecs_dbow

测试数据向量

unlabeled_vecs_dm = getVecs(model_dm, X_unlabeled)
unlabeled_vecs_dbow = getVecs(model_dbow, X_unlabeled)
unlabeled_vecs = unlabeled_vecs_dm + unlabeled_vecs_dbow

四、制作方法

【已实践过了】
参考1 参考2
(总启网址。第二个总启网址,在一个连接里面有代码。)
下载语料:任意一个总启网址,里面有动态最新版的下载链接,省略后条目,可以进入该下载网站看看。

步骤一[总启1]:首先用 process_wiki.py处理这个XML压缩文件,执行:
python process_wiki.py zhwiki-latest-pages-articles.xml.bz2 wiki.zh.text
步骤二[总启1]:将wiki.zh.text中的繁体字转化位简体字:
opencc -i wiki.zh.text -o wiki.zh.text.jian -c zht2zhs.ini
步骤三[总启2,有改动]:用jieba进行分词:
python separate_words.py wiki.zh.text.jian wiki.zh.text.jian.seg
注意:如果你接着还进行总启2的去除非中文字符,这讲会导致词典容量不够大、质量不够好。这已经实践过了。
步骤四[总启1]:进行训练语料:
python train_word2vec_model.py wiki.zh.text.jian.seg wiki.zh.text.model wiki.zh.text.vector

其余拓展的知识点:【可看可不看】
步骤一、提取正文、转化繁体为简体:
http://licstar.net/archives/tag/wikipedia-extractor
(主要学繁转简。提取正文,我可以使用其他手段去除杂字符,实际中没有用到,而且我用的繁转简的语句也不是这样。)

https://github.com/attardi/wikiextractor
(安装wikiextractor,提取正文:先下载zip,然后doc进去,使用python setup.py install 就能安装了)
python WikiExtractor.py 解压后的目标文件名 -b1000M -o extracted >output.txt
(意思是运行本dos路线下的WikiExtractor.py 来处理解压后的目标文件,以1000M 为单位切分文件默认是 500K。由于最后生成的正文文本不到600M,参数设置的大可以保证最后的抽取结果全部存在一个文件里。)
上面语句得到:wiki_00 (这个文件是此前使用 Wikipedia Extractor 得到的)

https://code.google.com/archive/p/opencc/wikis/Install.wiki
进行繁体字转化成简体字。

http://www.52nlp.cn/用mecab打造一套实用的中文分词系统
http://blog.csdn.net/mindmb/article/details/7898528
进行分词。(在总启网址有一个jieba的分词代码。只需要改一下代码ascii编码为utf8就行了。)

如何把python默认的ascii码,改成默认utf8等其他编码。处理后,你导出的东西,就默认为utf8等编码了,就不会是ascii编码。

参考来源

import sys
reload(sys)
print sys.getdefaultencoding() # 输入出正在使用的系统默认编码。
sys.setdefaultencoding("utf8")

猜你喜欢

转载自blog.csdn.net/qimiejia5584/article/details/78975810