使用PPMI改进共现矩阵
共现矩阵的元素表示两个单词同时出现的次数,这里的次数并不具备好的性质,举个例子,有短语叫the car,因为the是个常用词,如果以两个单词同时出现的次数为衡量相关性的标准,与drive 相比,the和car的相关性更强,这是不对的。
点互信息(Pointwise Mutual Information,PMI):表达式如下,P(x)表示x发生的概率,P(y)表示y发生的概率,P(x,y)表示x和y同时发生的概率。PMI的值越高,表明x与y相关性越强。
用共现矩阵重写PMI表达式:将共现矩阵表示为C,将单词X和Y的共现次数表示为C(x,y),将单词x和y的出现次数分别表示为C(x)、C(y),将语料库的单词数量记为N。表达式如下。
正的点互信息(Positive PMI,PPMI):当两个单词的共现次数为0时, log0=-∞。为解决这个问题,实践上会使用下述正的点互信息。可以将单词间的相关性表示为大于等于0的实数。
共现矩阵转化为PPMI矩阵的函数实现:代码中防止 np.log2(0)=-inf 而使 用了微小值 eps。
分析一下源码,函数里加一个print:
M = np.zeros_like(C, dtype=np.float32)N = np.sum(C)S = np.sum(C, axis=0)total = C.shape[0] * C.shape[1]print(C)print(M)print(N)print(S)print(total)
输出如下,可见N是把共现矩阵C中所有数相加;M是维度和C相同的全为0的数组,用来存PPMI矩阵;S是记录每个词和别的词共现的次数,total是等于7*7,这个是输出进展情况时候用的,用来判断当前进度。
[[0 1 0 0 0 0 0][1 0 1 0 1 1 0][0 1 0 1 0 0 0][0 0 1 0 1 0 0][0 1 0 1 0 0 0][0 1 0 0 0 0 1][0 0 0 0 0 1 0]][[0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0.]]14[1 4 2 2 2 2 1]49
下面这句代码,S[j]和S[i]指的是i和j和别的词共现的次数,这里就知道了,代码的实现和PMI表达式定义其实还是有差别的,代码是在共现范围内判断两个词的相关性。举个例子,a和b共现了x次,b和其他人现了y次,a和其他人现了j次,群体中人的个数是n,那在这个群体里b和a现的程度就是(x * n)/(y * j)
。(‘现’理解成XO)。
pmi = np.log2(C[i, j] * N / (S[j]*S[i]) + eps)
完整代码:
def ppmi(C, verbose=False, eps = 1e-8):'''生成PPMI(正的点互信息):param C: 共现矩阵:param verbose: 是否输出进展情况:return:'''M = np.zeros_like(C, dtype=np.float32)N = np.sum(C)S = np.sum(C, axis=0)total = C.shape[0] * C.shape[1]cnt = 0for i in range(C.shape[0]):for j in range(C.shape[1]):pmi = np.log2(C[i, j] * N / (S[j]*S[i]) + eps)M[i, j] = max(0, pmi)if verbose:cnt += 1if cnt % (total//100 + 1) == 0:print('%.1f%% done' % (100*cnt/total))return M
例子:
text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size)
W = ppmi(C)np.set_printoptions(precision=3) # 有效位数为3位
print('covariance matrix')
print(C)
print('-'*50)
print('PPMI')
print(W)
输出:PPMI矩阵各个元素均为大于等于0的实数。
covariance matrix
[[0 1 0 0 0 0 0][1 0 1 0 1 1 0][0 1 0 1 0 0 0][0 0 1 0 1 0 0][0 1 0 1 0 0 0][0 1 0 0 0 0 1][0 0 0 0 0 1 0]]
--------------------------------------------------
PPMI
[[0. 1.807 0. 0. 0. 0. 0. ][1.807 0. 0.807 0. 0.807 0.807 0. ][0. 0.807 0. 1.807 0. 0. 0. ][0. 0. 1.807 0. 1.807 0. 0. ][0. 0.807 0. 1.807 0. 0. 0. ][0. 0.807 0. 0. 0. 0. 2.807][0. 0. 0. 0. 0. 2.807 0. ]]
随着语料库词汇量增加,各个单词向量的维数也会增加,这个矩阵很多元素都是0,表明向量中的绝大多数元素并不重要,对于这些问题,一个常见的方法是向量降维。