《个人的总结》:
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模型,但实际上是优化另一个目标函数。
参考来源
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()`
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所示:
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就行了。)
import sys
reload(sys)
print sys.getdefaultencoding() # 输入出正在使用的系统默认编码。
sys.setdefaultencoding("utf8")