Genism word2vec 研读
neaural networks in https://code.google.com/p/word2vec/
初始化中的参数作用
def __init__(
self, sentences=None, size=100, alpha=0.025, window=5, min_count=5,
max_vocab_size=None, sample=1e-3, seed=1, workers=3, min_alpha=0.0001,
sg=0, hs=0, negative=5, cbow_mean=1, hashfxn=hash, iter=5, null_word=0,
trim_rule=None, sorted_vocab=1, batch_words=MAX_WORDS_IN_BATCH):
sentances 句子
sg sg = 0, 用 CBOW, 否则, 用 skip-gram
size 特征向量的维度 (通常为4的倍数)
window 这个应该就是 n - gram 的n
alpha 最初的学习率,有随着训练的持续而下降
seed 生成随机数, 为每个词创造初始向量
min_count 比这个频次小的词就不要了
max_vacab_size 限制内存和单词个数,优先裁剪频次最小的单词, 10,000,000 个单词大致需要1GB, 不限制的话设为None
sample 多高频单词进行向下采样(需研究)
workers 多核机器上加速
hs hs = 1, 用 hierarchical softmax, hs = 0 and 'negative' 不为0, 用negative sampling(需研究)
negative > 0, 用nagetive sampling, 分值为采用多少噪音单词
cbow_mean 使用 cbow 的时候, 若为0, 用上下文向量 累加, 若为1, 用上下文向量 平均
iter 多少轮, 默认为5
trim_rule 确认保留词,特别是当频次 < min_count 的时候采用
sorted_vocab = 1, 按频次排列
batch_words 一次传多少给workers
一些参数
self.layer1_size = size
流程
1 build_vocab
def build_vocab(self, sentences, keep_raw_vocab=False, trim_rule=None, progress_per=10000, update=False):
1.1 scan vocab
self.scan_vocab(sentences, progress_per=progress_per, trim_rule=trim_rule) # initial survey
对句子中存在的单词进行扫描
vocab 频次词典
vocab[word] += 1
如果vocab 长度 大于 max_vocab_size, 进行剪枝, 去掉除了 trim_rule 的频次最小词
self.corpus_count = sentence_no + 1, 这个意思是 句子编号, 即有多少个句子
self.raw_vocab = vocab
1.2 scale_vocab
self.scale_vocab(keep_raw_vocab=keep_raw_vocab, trim_rule=trim_rule, update=update)
剪枝(对低频次单词而言)和向下采样(对高频次单词而言)
def scale_vocab(self, min_count=None, sample=None, dry_run=False, keep_raw_vocab=False, trim_rule=None, update=False):
min_count => 作用于低频次单词
sample => 作用于高频次单词
keep_raw_vocab : True, 在构建完scale之后删除 dict 以释放空间
1.2.1 首次构建
遍历 raw_vocab
满足大于最小频次条件
保留数据 retain_words.append && retain_total += v
否则
drop_unique += 1
drop_total += v
original_unique_total <= 原来的词典长度(有频次, 非去重)
retain_unique_pct <= 保留词的频次占比
original_total <= 有多少种词
retain_pct <= 保留多少种词的占比
* 种为category
1.2.2 更新
如果 word 在 self.wv.vocab, 更新 pre_exist_words 和 pre_exists_total
如果 不在, 更新 new_words, new_total
再依次更新 original_unique_total, pre_exist_unique_pct, new_unique_pct, retain_words, retain_total
1.2.3 向下采样
sample
1. 没有
threshold_count = ratain_total
2. < 1. 是proportion
3. > 1 表示对sample多的words 都向下采样
遍历每一个在retain的词w和频次V
用V THRESHOLD_COUNT 计算 word_probability
如果 word_probability < 1.0
downsample_unique += 1
downsample_total += word_probability * v
else
word_probability = 1.0
downsample_total += v
1.3 finalize_vocab 构建
sort_vocab -> index2word 按频次排序
1.3.1 create_binary_tree
建立哈夫曼树
通过 vocab 建立堆 heap
heapify 进行堆排序 heapq
遍历 vocab
min1, min2 为堆顶heapq的两个元素, 通过 heappop弹出
再把融合过的元素 返回队 heappush
通过堆建立 node
1.3.2 make_cum_table
如果有 negative , 建立累积table
cum_table = zeros(vocab_size,)
遍历 vocab
train_words_pow += 对应词频 的 power 次方
遍历 vocab
cum += 对应词频 的 power 次方
cum_table[index] = cum / train_words_pow * domain
2 训练
def train(self, sentences, total_words=None, word_count=0,
total_examples=None, queue_factor=2, report_delay=1.0):
如果有negative, neg_labels = zeros(self.negative + 1)
对worker中的多线程进行分配
在每个线程中, 调用 worker_loop
2.1 worker_loop
work = REAL * size
neu1 = REAL * size
如果还有 job
sentances, alpha = job
tally, raw_tally = self._do_train_job(sentences, alpha, (work, neu1))
2.2 _do_train_job
训练一个单独的batch
2.2.1
训练 CBOW
train_batch_cbow
def train_batch_cbow(model, sentences, alpha, work=None, neu1=None):
获取 一个句子 的 log like-hood
遍历 sentance
遍历 在sentance中的word
其中 word_vocabs 包含 index pos 和 词语 word
------ sample int?
会生成一个reduce_window, 随机减少 window( n_gram ) 的宽度
start = index - n_gram + reduce_window
end = index + n_gram + 1 - reduce_window
window_pos 为 word_vocab 扫描窗口 n_gram 的数据
word2 为 window_pos 的word
word2_indeces = word2.index ... window_pos的下标
l1 是 通过 sv.syn0 进行 context 累加
如果 采用 cbow_mean l1 /= len(word2_indices)
log_prob_sentence += score_cbow_pair(model, word, word2_indices, l1)