gensim中word2vec python源码理解(一)
2018-01-16 09:42
1131 查看
gensim中word2vec python源码理解(一)使用Hierarchical Softmax方法构建单词表
gensim中word2vec python源码理解(二)Skip-gram模型训练
本文主要谈一谈对gensim包中封装的word2vec python源码中,使用Hierarchical Softmax构建单词表部分代码的理解。
由于之前阅读的论文是对使用Hierarchical Softmax的Skip-gram模型进行拓展,因此在阅读代码的时候重点阅读了Hierarchical Softmax构建单词表的方法,以及Skip-gram模型的训练方法。对于negative sampling方法和CBOW模型的实现方法,则会继续对代码进行研究。
进入类的初始化方法
在传入的训练句子不为空的情况下,主要调用两个方法:
下面依次介绍三个方法的功能:
代码内容阅读(有省略):
代码内容阅读(有省略):
加载新的词汇表:
添加新的单词更新模型:
计算采样阈值
代码内容阅读(有省略):
从代码中可以看出,Hierarchical Softmax方法和negative sampling方法对应两种构建词表的方法,分别是
在构建单词表完成后,每个单词对应的都是类Vocab的一个实例,构建哈夫曼树完成之后,二叉树中每个内节点对应的也是一个Vocab类的实例,其left和right属性分别保存了其左右孩子,points保存根节点到达该节点的路径(由经过的内节点的序号构成),codes保存该节点的二进制编码。
至此,构建单词表完成。
gensim中word2vec python源码理解(二)Skip-gram模型训练
本文主要谈一谈对gensim包中封装的word2vec python源码中,使用Hierarchical Softmax构建单词表部分代码的理解。
由于之前阅读的论文是对使用Hierarchical Softmax的Skip-gram模型进行拓展,因此在阅读代码的时候重点阅读了Hierarchical Softmax构建单词表的方法,以及Skip-gram模型的训练方法。对于negative sampling方法和CBOW模型的实现方法,则会继续对代码进行研究。
init
初始化一个model(实际上是Word2Vec类的实例化对象):model = Word2Vec(sentences, size=100, window=5, min_count=5, workers=4)
进入类的初始化方法
__init__,对里面的属性值进行初始化。
在传入的训练句子不为空的情况下,主要调用两个方法:
self.build_vocab(sentences, trim_rule=trim_rule) self.train( sentences, total_examples=self.corpus_count, epochs=self.iter, start_alpha=self.alpha, end_alpha=self.min_alpha )
build_vocab
该方法是从句子序列中构建单词表,其中每个句子都是字符串组成的列表。依次调用了三个方法:scan_vocab,
scale_vocab,
finalize_vocab
下面依次介绍三个方法的功能:
scan_vocab:对句子中的单词进行初始化
代码内容阅读(有省略):
sentence_no = -1 #保存扫描完成的句子数量 total_words = 0 #保存出现的单词总数(不去重) min_reduce = 1 vocab = defaultdict(int) #将单词表初始化为一个字典 checked_string_types = 0 #扫描每个句子 for sentence_no, sentence in enumerate(sentences): #取出语料中每个句子和其在语料库中的编号no for word in sentence: vocab[word] += 1 #记录每个词出现的次数 total_words += len(sentence) #记录扫描过的句子里的单词总数 if self.max_vocab_size and len(vocab) > self.max_vocab_size: #如果对于最大单词数有限制且当前超出限制 #将语料库中小于min_reduce(初始值为1)的单词都删除 utils.prune_vocab(vocab, min_reduce, trim_rule=trim_rule) min_reduce += 1 #不断增大min_reduce,直到单词表长度不大于max_vocab_size self.corpus_count = sentence_no + 1 #保存语料数(句子数) self.raw_vocab = vocab #保存单词表 return total_words #返回单词总数
scale_vocab:应用
min_count的词汇表设置(丢弃不太频繁的单词)和
sample(控制更频繁单词的采样)。
代码内容阅读(有省略):
加载新的词汇表:
if not update: #加载一个新的词汇表 retain_total, retain_words = 0, [] #保留总数,保留的单词 #获得单词及其出现的数量,raw_vocab是scan_vocab中保存的单词表dict for word, v in iteritems(self.raw_vocab): #判断当前单词是否被丢弃,trim_rule为修剪规则,默认为none if keep_vocab_item(word, v, min_count, trim_rule=trim_rule): retain_words.append(word) #添加单词 retain_total += v #添加词数 if not dry_run: #为每个单词构建一个Vocab类,传入词频、下标 self.wv.vocab[word] = Vocab(count=v, index=len(self.wv.index2word)) self.wv.index2word.append(word) else: #不符合条件则丢弃 drop_unique += 1 drop_total += v
添加新的单词更新模型:
else: new_total = pre_exist_total = 0 new_words = pre_exist_words = [] for word, v in iteritems(self.raw_vocab):#遍历更新的单词表 if keep_vocab_item(word, v, min_count, trim_rule=trim_rule): #判断当前单词是否被丢弃 if word in self.wv.vocab: #如果单词存在在之前的单词表中 pre_exist_words.append(word) #添加至先前存在的单词list pre_exist_total += v#添加词频 if not dry_run: self.wv.vocab[word].count += v#更新原单词表的词频 else: #如果单词不存在在之前的单词表中(新单词) new_words.append(word) new_total += v if not dry_run: #为单词构建一个Vocab类 self.wv.vocab[word] = Vocab(count=v, index=len(self.wv.index2word)) self.wv.index2word.append(word)#给单词添加下标 else:#不符合条件则丢弃 drop_unique += 1 drop_total += v
计算采样阈值
# 预先计算每个词汇项目的采样阈值 if not sample: # no words downsampled 没有单词被 4000 downsample,阈值等于单词总数 threshold_count = retain_total elif sample < 1.0: # traditional meaning: set parameter as proportion of total threshold_count = sample * retain_total else: # new shorthand: sample >= 1 means downsample all words with higher count than sample threshold_count = int(sample * (3 + sqrt(5)) / 2) downsample_total, downsample_unique = 0, 0 for w in retain_words: v = self.raw_vocab[w]#v是当前单词出现的次数 word_probability = (sqrt(v / threshold_count) + 1) * (threshold_count / v) if word_probability < 1.0: downsample_unique += 1 downsample_total += word_probability * v else: #如果没有设置sample值的话,word_probability一定>1 word_probability = 1.0 downsample_total += v if not dry_run: self.wv.vocab[w].sample_int = int(round(word_probability * 2**32)) #设置一个采样值,round返回浮点数x的四舍五入值。
finalize_vocab:根据最终词汇表设置建立表格和模型权重。
代码内容阅读(有省略):
if not self.wv.index2word: self.scale_vocab() if self.sorted_vocab and not update: self.sort_vocab() #按照词频降序排列,使得词频大的词下标更小 if self.hs: # 添加每个单词的Huffman编码信息 self.create_binary_tree() if self.negative: # 负采样 self.make_cum_table() if self.null_word: # create null pseudo-word for padding when using concatenative L1 (run-of-words) # this word is only ever input – never predicted – so count, huffman-point, etc doesn't matter word, v = '\0', Vocab(count=1, sample_int=0) v.index = len(self.wv.vocab) self.wv.index2word.append(word) self.wv.vocab[word] = v # set initial input/projection and hidden weights if not update:#如果不是添加新词以更新,则重置权重矩阵 self.reset_weights() else: self.update_weights()
从代码中可以看出,Hierarchical Softmax方法和negative sampling方法对应两种构建词表的方法,分别是
create_binary_tree和
make_cum_table。
create_binary_tree
Hierarchical Softmax方法,使用存储的词汇单词及其词频创建一个二进制哈夫曼树。频繁的词编码更短。# build the huffman tree heap = list(itervalues(self.wv.vocab)) #将字典中的value以列表形式返回,其value是Vocab类的实例 heapq.heapify(heap) for i in xrange(len(self.wv.vocab) - 1): #保存内节点 min1, min2 = heapq.heappop(heap), heapq.heappop(heap)#取出最小的两个 #放入两个小值节点的父节点,下标从单词表长度向后取,count值取两个孩子节点的count之和,设置左右孩子 heapq.heappush( heap, Vocab(count=min1.count + min2.count, index=i + len(self.wv.vocab), left=min1, right=min2) )#最终只剩一个根节点在堆栈中 # recurse over the tree, assigning a binary code to each vocabulary word #在树上递归,为每个词汇词分配一个二进制代码,保存到达该节点的路径上经过的内节点 if heap: max_depth, stack = 0, [(heap[0], [], [])] #定义一个最大深度,一个堆栈,放入根节点 while stack: node, codes, points = stack.pop() #node节点对应一个Vocab类的实例(也就是一个节点),code对应该节点的编码,points对应到达该节点经过的节点 if node.index < len(self.wv.vocab): #如果取出的节点下标小于单词表的长度,即该词在单词表内,取出的是叶节点 # 叶节点=>从根存储它的路径 node.code, node.point = codes, points max_depth = max(len(codes), max_depth) else: #否则,取出的是内节点=>继续递归 # inner node => continue recursion #保存路径经过的节点 points = array(list(points) + [node.index - len(self.wv.vocab)], dtype=uint32) # 把左右孩子节点放入栈中 stack.append((node.left, array(list(codes) + [0], dtype=uint8), points)) stack.append((node.right, array(list(codes) + [1], dtype=uint8), points))
在构建单词表完成后,每个单词对应的都是类Vocab的一个实例,构建哈夫曼树完成之后,二叉树中每个内节点对应的也是一个Vocab类的实例,其left和right属性分别保存了其左右孩子,points保存根节点到达该节点的路径(由经过的内节点的序号构成),codes保存该节点的二进制编码。
reset_weights
重置隐藏层的权重#syn0表示词向量矩阵 #单词数为行,向量维数为列, empty 会创建一个没有使用特定值来初始化的数组 self.wv.syn0 = empty((len(self.wv.vocab), self.vector_size), dtype=REAL) # 对于每个单词分别为其初始化一个向量,而不是立即在RAM中实现巨大的随机矩阵 for i in xrange(len(self.wv.vocab)): #对于单词表中的每一个单词 #初始化单词向量 self.wv.syn0[i] = self.seeded_vector(self.wv.index2word[i] + str(self.seed)) if self.hs: #syn0表示二叉树的内节点向量矩阵,全部初始化为0向量 self.syn1 = zeros((len(self.wv.vocab), self.layer1_size), dtype=REAL) if self.negative: self.syn1neg = zeros((len(self.wv.vocab), self.layer1_size), dtype=REAL) self.wv.syn0norm = None self.syn0_lockf = ones(len(self.wv.vocab), dtype=REAL) # zeros suppress learning
至此,构建单词表完成。
相关文章推荐
- python︱gensim训练word2vec及相关函数与功能理解
- Word2Vec的Python版Gensim的使用
- Python版的Word2Vec -- gensim 学习手札 中文词语相似性度量 V1.1
- python 环境下gensim中的word2vec的使用笔记
- Windows下安装python版的Word2Vec工具包gensim教程(基于Anaconda)
- gensim实现python对word2vec的训练和计算
- Python版的Word2Vec -- gensim 学习手札 中文词语相似性度量 V1.1
- 【python gensim使用】word2vec词向量处理英文语料
- 【python gensim使用】word2vec词向量处理中文语料
- 【word2vec】之 训练模型结果的结构探究 模型改造 python gensim
- 【python gensim使用】word2vec词向量处理英文语料
- 【python gensim使用】word2vec词向量处理中文语料
- gensim实现python对word2vec的训练和计算
- Python实现mysql数据输出到word表格中(源码)
- word2vec我的理解
- python word2vec
- Python Tensorflow下的Word2Vec代码解释
- 重写gensim.word2vec的文本相似度匹配函数(wmdistance)
- word2vec个人理解
- 理解word2vec的训练过程