基于计数方法的改进
本文记录的是鱼书第3章:如何对原有的计数方法进行改进。
基于统计方法函数
下面介绍的是传统的基于统计的方法。
预处理
1 | # 常用函数 |
共现矩阵
1 | def create_to_matrix(corpus, vocab_size,window_size=1): |
相似度计算
1 | def cos_similarity(x, y, eps=1e-8): |
相似单词的降序
1 | def most_similar(query, word_to_id, id_to_word,word_matrix, top=5): |
调用函数
1 | import numpy as np |
you
goodbye: 0.7071067691154799
i: 0.7071067691154799
hello: 0.7071067691154799
say: 0.0
and: 0.0
基于【计数】存在问题
比如,我们来考虑某个语料库中the和car共现的情况:
在这种情况下,我们会看到很多...the car...
这样的短语。
因此,它们的共现次数将会很大。另外,car和drive也明显有很强的相关性。
但是,如果只看单词的出现次数,那么与drive相比,the和car的相关性更强。
这意味着,仅仅因为the是个常用词,它就被认为与car有很强的相关性
解决方法
点互信息PMI
使用点互信息Pointwise Mutual Information,PMI
;PMI值越高表示相关性越强
定义为:
$$PMI(x,y)=log_2 \frac{P(x,y)}{P{(x)}{P(y)}}$$
- $P(x)$:表示x发生的概率
- $P(x,y)$:表示x和y同时发生的概率
使用共现矩阵来重写上面的式子:
$$PMI(x,y)=log_2 \frac{P(x,y)}{P{(x)}{P(y)}} = log_2\frac{\frac{C{(x,y)}}{N}}{\frac{C{(x)}}{N} \frac{C{(y)}}{N}} = log_2 \frac{C{(x,y)} * N}{C{(x)}C{(y)}}$$
其中:N表示语料库的单词数量N
优化方案PPMI
上面基于点的互信息的方法有个缺点:当两个单词的共现次数为0时,会出现$log_2{0}= \infty$
使用正的点互信息Positive Pointwise Mutual Information,PPMI
$$PPMI(x,y) = max(0, PMI(x,y))$$
代码实现PPMI
1 | def ppmi(C, verbose=False, eps=1e-8): |
1 | import numpy as np |
1 | M |
array([[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. ]], dtype=float32)
降维-dimensionality reduction
PPMI存在问题
PPMI矩阵存在的问题:
-
维度爆炸:随着语料库词汇量的增加,各个单词向量的维度也会随着增加
-
矩阵稀疏:在PPMI矩阵中存在很多的元素都是0,这表明向量中的很多元素是不重要的
-
向量中的大多数元素为0的矩阵(向量)称为稀疏矩阵(稀疏向量)
-
从稀疏向量中找出重要的轴,用更少的维度对其重新表示;稀疏矩阵转化为密集矩阵
奇异值分解SVD-Singular Value Decomposition
SVD基本原理:
SVD可以将任意矩阵分解为3个矩阵的乘积:
$$X = USV^T$$
-
UV是列向量彼此正交的正交矩阵;U矩阵构成了一些空间的基轴(基向量),看做是"单词空间"。
-
S是除了对角线元素外其他元素均为0的对角矩阵;奇异值在对角线上降序排列
-
S中奇异值越小,对应的基轴的重要性越低;因此通过去除U中多余的列向量来近似原始矩阵
基于SVD的降维
1 | import numpy as np |
对比3大矩阵
对比原共现矩阵、PPMI矩阵、经过SVD降维后的密集U
1 | C[0] # 共现矩阵 |
array([0, 1, 0, 0, 0, 0, 0], dtype=int32)
1 | M[0] # PPMI矩阵 |
array([0. , 1.807, 0. , 0. , 0. , 0. , 0. ], dtype=float32)
1 | U[0] # PPMI矩阵的稀疏向量转成了密集向量U |
array([ 3.409e-01, -1.110e-16, -1.205e-01, -4.163e-16, -9.323e-01,
-1.110e-16, -2.426e-17], dtype=float32)
比如现在我们将这个密集向量降到二维,只要取出前面两个元素:
1 | print(U) |
[[ 3.409e-01 -1.110e-16 -1.205e-01 -4.163e-16 -9.323e-01 -1.110e-16
-2.426e-17]
[ 0.000e+00 -5.976e-01 0.000e+00 1.802e-01 0.000e+00 -7.812e-01
0.000e+00]
[ 4.363e-01 -5.551e-17 -5.088e-01 -2.220e-16 2.253e-01 -1.388e-17
-7.071e-01]
[ 1.665e-16 -4.978e-01 2.776e-17 6.804e-01 -1.110e-16 5.378e-01
7.467e-17]
[ 4.363e-01 -3.124e-17 -5.088e-01 -1.600e-16 2.253e-01 -1.302e-17
7.071e-01]
[ 7.092e-01 -3.124e-17 6.839e-01 -1.600e-16 1.710e-01 -1.302e-17
2.314e-17]
[-1.665e-16 -6.285e-01 -4.163e-17 -7.103e-01 2.220e-16 3.169e-01
-9.614e-17]]
1 | U[0, :2] |
array([ 3.409e-01, -1.110e-16], dtype=float32)
降维可视化
1 | import matplotlib.pyplot as plt |
降维进阶-Truncated SVD
如果矩阵大小是N,SVD的计算复杂度将达到$N^3$;计算量大大增加,现实中无法达到。通常使用Truncated SVD等方法。Truncated SVD通过截去奇异值较小的部分,从而实现高速化。
PTB数据集(略)
PTB语料库是以文件文本的形式提供,一行保存一个句子。实战案例略。