词向量源码解析:(4.3)hyperwords源码解析之counts2pmi等
来源:互联网 发布:微信 请检查网络设置 编辑:程序博客网 时间:2024/06/05 15:50
PPMI和SVD都是很传统的模型。在word2vec之前很早就出现了。PPMI这种高维稀疏的单词表示一般认为比word2vec要差,尤其是在analogy问题上面。word2vec引起人注意的一个很重要的点就在于在analogy上面远远超过了传统的词向量模型。实际上,这个工具包的作者发现,当我们吧word2vec中的一些超参数引进传统的方法中,并且使用新的公式去做analogy这个任务,PPMI,SVD这种传统的模型也会去得很好的效果。实际上,word2vec,GloVe这些词向量模型本质上就是在利用共现矩阵进行训练。下面介绍如何从counts中生成PPMI矩阵。PPMI全称是positive pointwise mutual information。PMI大家应该比较熟悉,之前word2phrase中找phrase也利用的是PMI。这个东西实际上就是对共现矩阵的一个加权。用python代码几行就写好了。所以其实我们不需要用word2vec,GloVe那样复杂的训练,调参什么的,就用传统经典的bag-of-contexts加权,就能得到很高质量的词向量。而且,这个词向量很容易解释,每一维度都有清楚的含义。counts2pmi生成的是PMI矩阵,PPMI矩阵就是把PMI中的负数变成0。我们先看看counts2pmi的main函数。实际上就两步,首先从文件中读入共现矩阵,然后加权得到PMI矩阵
def main():
args = docopt("""
Usage:
counts2pmi.py [options] <counts> <output_path>
Options:
--cds NUM Context distribution smoothing [default: 1.0]
""")
counts_path = args['<counts>']
vectors_path = args['<output_path>']
cds = float(args['--cds'])//从word2vec中借鉴的超参
counts, iw, ic = read_counts_matrix(counts_path)//从文件中读取共现矩阵
pmi = calc_pmi(counts, cds)//在共现矩阵上面加权,即计算PMI矩阵
save_matrix(vectors_path, pmi)//存储计算的结果
save_vocabulary(vectors_path + '.words.vocab', iw)
save_vocabulary(vectors_path + '.contexts.vocab', ic)
我们下面再看看如何从文件中读取共现矩阵。这段代码的逻辑是先读入中心词,上下文词典,然后再读入之前文件中的counts,得到共现矩阵。这里的共现矩阵用稀疏矩阵去存。这里默认内存能够存下整个的稀疏矩阵。还有一点要说明的是,读入的counts由于不是完全排好序的,建立稀疏矩阵的代价还是比较大的。
def read_counts_matrix(counts_path):
"""
Reads the counts into a sparse matrix (CSR) from the count-word-context textual format.
"""
words = load_count_vocabulary(counts_path + '.words.vocab')//读入中心词词典
contexts = load_count_vocabulary(counts_path + '.contexts.vocab')//读入上下文词典
words = list(words.keys())
contexts = list(contexts.keys())
iw = sorted(words)//从id找到单词
ic = sorted(contexts)
wi = dict([(w, i) for i, w in enumerate(iw)])//从单词找id
ci = dict([(c, i) for i, c in enumerate(ic)])
counts = csr_matrix((len(wi), len(ci)), dtype=np.float32)//共现矩阵的维度是由中心词词典和上下文词典中单词的个数决定。
tmp_counts = dok_matrix((len(wi), len(ci)), dtype=np.float32)//dok_matrix适合做更新
update_threshold = 100000//在dok_matrix上面做更新,到了一定的阈值,更新到counts上面
i = 0
with open(counts_path) as f:
for line in f:
count, word, context = line.strip().split()
if word in wi and context in ci:
tmp_counts[wi[word], ci[context]] = int(count)//在tmp_counts上面更新
i += 1
if i == update_threshold:
counts = counts + tmp_counts.tocsr()//到了一定的阈值就更新counts
tmp_counts = dok_matrix((len(wi), len(ci)), dtype=np.float32)//初始化tmp_counts
i = 0
counts = counts + tmp_counts.tocsr()//最后把tmp_counts剩下的去更新
return counts, iw, ic//返回共现矩阵以及两个词典
下面看如何通过共现矩阵得到PMI矩阵,就是根据PMI公式算
def calc_pmi(counts, cds):
"""
Calculates e^PMI; PMI without the log().
"""
sum_w = np.array(counts.sum(axis=1))[:, 0]
sum_c = np.array(counts.sum(axis=0))[0, :]
if cds != 1:
sum_c = sum_c ** cds
sum_total = sum_c.sum()
sum_w = np.reciprocal(sum_w)
sum_c = np.reciprocal(sum_c)
pmi = csr_matrix(counts)
pmi = multiply_by_rows(pmi, sum_w)
pmi = multiply_by_columns(pmi, sum_c)
pmi = pmi * sum_total
return pmi
def multiply_by_rows(matrix, row_coefs):
normalizer = dok_matrix((len(row_coefs), len(row_coefs)))
normalizer.setdiag(row_coefs)
return normalizer.tocsr().dot(matrix)
def multiply_by_columns(matrix, col_coefs):
normalizer = dok_matrix((len(col_coefs), len(col_coefs)))
normalizer.setdiag(col_coefs)
return matrix.dot(normalizer.tocsr())
然后我们看看怎样通过pmi得到svd。同样很简单。直接调用包的接口就好,真正的代码不到十行
def main():
args = docopt("""
Usage:
pmi2svd.py [options] <pmi_path> <output_path>
Options:
--dim NUM Dimensionality of eigenvectors [default: 500]
--neg NUM Number of negative samples; subtracts its log from PMI [default: 1]
""")
pmi_path = args['<pmi_path>']
output_path = args['<output_path>']
dim = int(args['--dim'])
neg = int(args['--neg'])
explicit = PositiveExplicit(pmi_path, normalize=False, neg=neg)//得到PPMI矩阵,explicit是一个类,里面的m成员是共现矩阵
ut, s, vt = sparsesvd(explicit.m.tocsc(), dim)//sparsesvd支持csc格式的共现矩阵,得到稠密的单词,上下文表示
np.save(output_path + '.ut.npy', ut)
np.save(output_path + '.s.npy', s)
np.save(output_path + '.vt.npy', vt)
save_vocabulary(output_path + '.words.vocab', explicit.iw)//和ppmi共享词典
save_vocabulary(output_path + '.contexts.vocab', explicit.ic)
- 词向量源码解析:(4.3)hyperwords源码解析之counts2pmi等
- 词向量源码解析:(4.2)hyperwords源码解析之corpus2pairs等
- 词向量源码解析:(4.7)hyperwords源码解析之text2numpy等
- 词向量源码解析:(4.1)hyperwords源码解析
- 词向量源码解析:(4.9)hyperwords源码解析小结
- 词向量源码解析:(4.4)hyperwords源码解析之word2vecf
- 词向量源码解析:(4.5)hyperwords源码解析之representations
- 词向量源码解析:(4.6)hyperwords源码解析之evaluation
- 词向量源码解析:(4.8)hyperwords源码解析之example_test
- 词向量源码解析:(5.7)ngram2vec源码解析之counts2shuf等
- 词向量源码解析:(5.8)ngram2vec源码解析之counts2ppmi等
- 词向量源码解析:(6.6)fasttext源码解析之词向量1
- 词向量源码解析:(6.7)fasttext源码解析之词向量1
- 词向量源码解析:(2.1)word2vec源码解析
- 词向量源码解析:(2.7)word2vec源码解析小结
- 词向量源码解析:(3.1)GloVe源码解析
- 词向量源码解析:(3.6)GloVe源码解析小结
- 词向量源码解析:(5.1)ngram2vec源码解析
- NGUI_打字机效果_011
- [HDU 6200] mustedge mustedge mustedge
- 算法 第四版 2.3.20
- hdu 1874 畅通工程续(spfa)
- spark createDirectStream保存kafka offset(JAVA实现)
- 词向量源码解析:(4.3)hyperwords源码解析之counts2pmi等
- 9月12日【边缘检测、锐化】
- Vxworks下网络编程遇到sendto发送失败的问题
- web前端性能优化
- SYSTEM表空间不足处理
- windows 下 gcc/g++ 的安装
- Springboot jar包外指定配置文件及原理
- Linux系统下安装rz/sz命令完全编译和使用方法
- 代理是什么?