我正在参加「启航方案」
这篇文章是接着一文拿捏点互信息(PMI)处理词分布式表明稀少性问题写的。处理分布式表明稀少性问题另一个办法是使用奇特值分化(Singular Value Decomposition,SVD)。
我把比如搬过来了。仍是本来的三个句子及其共现矩阵M
。
- 我 喜爱 天然 言语 处理。
- 我 爱 深度 学习。
- 我 喜爱 机器 学习。
SVD奇特值分化
从矩阵视点来看
公式为:
怎样分化的涉及到数学知识,我等就不必深究了。总之简略来讲便是将MM矩阵分化成一个UU,一个\Sigma,一个VTV^T三个矩阵相乘。
-
VV和UU都是正交矩阵
- 正交矩阵: 假如AAT=E(单位矩阵)A\times A^T = E(单位矩阵),那A便是正交矩阵。
- 正交矩阵都是方阵。
-
\Sigma是一个半正定mnmn阶对角矩阵,其对角线上的值便是MM矩阵分化的奇特值。
-
MM矩阵的形状是mnmn,那它的特征值最多为min(m,n)\min(m,n)个。
也便是说奇特值分化最终得到的奇特值只有这个小方阵里的对角线元素。
若在 {\Sigma} 中仅保存 dd 个 (d<min(m,n))(d<\min(m,n)) 最大的奇特值(UU和 V{V} 也只保存相应的维度),则被保存的奇特值组成的对角矩阵被称为切断奇特值分化 (Truncated Singular Value Decomposition,TSVD)。
从向量视点看
其间等式右边每一项前的系数 \sigma 便是奇特值, uu 和 vv 分别表明列向量,每一项 uvTu v^{T} 都是秩为 1 的矩阵。奇特值满足 1≥2≥…≥r>0\sigma_1 \geq \sigma_2 \geq \ldots \geq \sigma_r>0 。
这样就能够和前边的切断奇特值对上了。前边咱们说到,咱们能够挑选保存多少奇特值。一个矩阵MM分化后最多有min(m,n)\min(m,n)个奇特值。
看一下下图,是借用知乎上的图,从左到右依次是原图、奇特值选1、5、50时候的样子。
当切断奇特值矩阵挑选r=1r = 1时,
M′=1u1v1TM’ = \sigma_1 u_1 v_1^{\mathrm{T}}
当切断奇特值矩阵挑选r=5r = 5时,
M′=1u1v1T+2u2v2T+3u3v3T+4u4v4T+5u5v5TM’ = \sigma_1 u_1 v_1^{\mathrm{T}} + \sigma_2 u_2 v_2^{\mathrm{T}} + \sigma_3 u_3 v_3^{\mathrm{T}} +\sigma_4 u_4 v_4^{\mathrm{T}} +\sigma_5 u_5 v_5^{\mathrm{T}}
当切断奇特值矩阵挑选r=50r = 50时,
M′=1u1v1T+2u2v2T+…+50u50v50TM’ = \sigma_1 u_1 v_1^{\mathrm{T}}+ \sigma_2 u_2 v_2^{\mathrm{T}} + … + \sigma_{50} u_{50} v_{50}^{\mathrm{T}}
随着项数逐步增大,M′M’逐步复原MM,就像泰勒展开式相同,项数越多越挨近原图。
切断奇特值分化实际上是对矩阵 MM 的低秩近似。经过切断奇特值分化所得到的矩阵UU中的每一行,则为相应词的dd维向量表明, 该向量一般以为其具有接连、低维和稠密的性质。由于UU的各列相互正交,因此能够以为词表明的每一维表达了该词的一种独立的“潜在语义”,所以这种办法也被称作潜在语义分析(Latent Semantic Analysis,LSA)。别的,VTV^T的每一列也能够作为相应上下文的向量表明。
留意 :UU和VT\Sigma V^T是不相等的,相当于两套表明,咱们在这挑选UU作为MM的稠密表明。
代码
不管是NumPy仍是PyTorch 中都自带了SVD分化。
直接使用.linalg.svd()
办法即可。
import torch
M = torch.Tensor([[0, 2, 1, 1, 1, 1, 1, 2, 1, 3],
[2, 0, 1, 1, 1, 0, 0, 1, 1, 2],
[1, 1, 0, 1, 1, 0, 0, 0, 0, 1],
[1, 1, 1, 0, 1, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 0, 1],
[2, 1, 0, 0, 0, 1, 1, 0, 1, 1],
[1, 1, 0, 0, 0, 0, 0, 1, 0, 1],
[3, 2, 1, 1, 1, 1, 1, 2, 1, 0]])
u, s, v = torch.linalg.svd(M)
print((u @ torch.diag(s) @ v).int()) # 乘起来
torch.set_printoptions(precision=3, sci_mode=False)
print(u) # M 的 稠密表明
成果:
乘起来能够看到SVD之后的成果还能再拼回去,不是在骗你。
tensor([[ -0.500, 0.724, 0.351, 0.253, -0.025, 0.193, 0.000, -0.000, -0.000, 0.017],
\quad\quad\quad[ -0.384, 0.052, -0.463, -0.519, 0.394, 0.363, 0.000, -0.000, -0.000, 0.282],
\quad\quad\quad[ -0.218, 0.036, -0.398, 0.156, -0.168, -0.182, 0.072, -0.138, -0.802, -0.200],
\quad\quad\quad[ -0.218, 0.036, -0.398, 0.156, -0.168, -0.182, -0.501, 0.586, 0.270, -0.200],
\quad\quad\quad[ -0.218, 0.036, -0.398, 0.156, -0.168, -0.182, 0.429, -0.448, 0.531, -0.200],
\quad\quad\quad[ -0.183, -0.010, 0.228, -0.419, 0.070, -0.258, 0.529, 0.468, -0.033, -0.409],
\quad\quad\quad[ -0.183, -0.010, 0.228, -0.419, 0.070, -0.258, -0.529, -0.468, 0.033, -0.409],
\quad\quad\quad[ -0.293, -0.300, 0.152, -0.252, -0.789, 0.302, 0.000, -0.000, 0.000, 0.155],
\quad\quad\quad[ -0.208, 0.015, 0.087, -0.079, -0.027, -0.708, 0.000, 0.000, 0.000, 0.664],
\quad\quad\quad[ -0.515, -0.615, 0.226, 0.416, 0.356, 0.070, -0.000, 0.000, -0.000, -0.038]])