传统的向量空间模型(Vector Space Model)中,文档被表示成由特征词出现频率(或概率)组成的多维向量,然后计算向量间的相似度。向量空间模型依旧是现在很多文本分析模型的基础,但向量空间模型无法处理一词多义和一义多词问题。Scott Deerwester,Susan T. Dumais等人在1990年提出了LSA(Latent Semantic Analysis,潜在语义分析)又称LSI(Latent Semantic Indexing),是一种非常有效的文本建模方法,正如名称所指示,该方法意在分析文本语料所包含的潜在语义,然后将单词和文档映射到该语义空间。
LSA以矩阵奇异值分解(SVD)为基础,在了解LSA之前,需要先对奇异值分解进行简单介绍。
我们都知道,一个矩阵其实代表了一个线性变换(旋转,拉伸),可以将一个线性变换过程分解多个子过程,矩阵奇异值分解就是将矩阵分解成若干个秩一矩阵的和。
其中 σi σ i 是奇异值, uivTi u i v i T 是秩为1的矩阵,表示一个线性变换子过程。奇异值 σi σ i 反映了该子过程 uivTi u i v i T 在该线性变换 A A 中的重要程度。对上面式子进行整理,我们可以将奇异值分解过程写成如下:
其中, U U 是左奇异向量构成的矩阵,两两相互正交, S S 是奇异值构成的对角矩阵, VT V T 是右奇异向量构成的矩阵,两两相互正交。
奇异值分解具有如下数学性质:
LSA正是利用了奇异值分解的这两个性质,实现将原始的单词-文档矩阵映射到语义空间。
在LSA中,我们不再将矩阵理解成变换,而是看作文本数据的集合。文本语料中所有的单词构成了矩阵的行,每一列表示一篇文档(词袋模型表示)。假设 A A 是一个 m∗n m ∗ n 的文本数据矩阵( n<<m n << m ),表示该语料包含 m m 个单词, n n 篇文档。这 m m 个单词中肯定存在同义词等,这样一篇文档用 m m 维特征表示就显得冗余,不利于计算。利用矩阵奇异值分解:
依据奇异值分解的性质1,矩阵 A A 可以分解出n个特征值,然后依据性质2,我们选取其中较大的r个并排序,这样 USVT U S V T 就可以近似表示矩阵 A A 。对于矩阵 U U ,每一列代表一个潜语义,这个潜语义的意义由m个单词按不同权重组合而成。因为 U U 中每一列相互独立,所以r个潜语义构成了一个语义空间。 S S 中每一个奇异值指示了该潜语义的重要度。 VT V T 中每一列仍然是一篇文档,但此时文档被映射了语义空间。 VT V T 的大小远小于 A A 。
有了 VT V T ,我们就相当于有了矩阵 A A 的另外一种表示,之后我们就可以使用 VT V T 代替 A A 进行之后的工作。
借用LSA Tutorial上的例子,此时我们有单词文档矩阵如下:
这个矩阵的一行表示一个单词在哪些title中出现了(一行就是之前说的一维特征),一列表示一个title中包含哪些词,对这个矩阵进行奇异值分解,并选取奇异值最大的三项,得到下面矩阵:
表示我们将文档映射到了一个3维语义空间中,其中第一维潜语义可以表示为:
同时单词book在该语义空间中的向量表示为 (0.15,−0.27,0.04) ( 0.15 , − 0.27 , 0.04 ) 。此时title1在该语义空间中的向量表示是 (0.35,−0.32,−0.41) ( 0.35 , − 0.32 , − 0.41 ) 。然后我们反过头来看,我们可以将左奇异向量和右奇异向量都取后2维(之前是3维的矩阵),投影到一个平面上,可以得到:
在图上,每一个红色的点,都表示一个词,每一个蓝色的点,都表示一个title,这样我们可以对这些词和title进行聚类,比如stock和market可以放在一类,这也符合他们经常出现在一起的直觉,real和estate可以放在一类,dads,guide这种词就看起来有点孤立了,我们就不对他们进行合并了。对于title,T1和T3可以聚成一类,T2、T4、T5和T8可以聚成一类,所以T1和T3比较相似,T2、T4、T5和T8比较相似。按这样聚类出现的效果,可以提取文档集合中的近义词,这样当用户检索文档的时候,是用语义级别(近义词集合)去检索了,而不是之前的词的级别。这样一减少我们的检索、存储量,因为这样压缩的文档集合和PCA是异曲同工的,二可以提高我们的用户体验,用户输入一个词,我们可以在这个词的近义词的集合中去找,这是传统的索引无法做到的。
Gensim工具包提供了一系列发现文档语义结构的工具,给定一篇文档,Gensim可以产生一些列与该文档相似的文档集合,这也是作者将其命名为Gensim(gensim = “generate similar”)原因。models.lsimodel提供了LSA实现。
from gensim.test.utils import common_corpus, common_dictionary, get_tmpfile
from gensim.models import LsiModel
# 构建LSA模型
model = LsiModel(common_corpus[:3], id2word=common_dictionary) # train model
# 将文档映射到语义空间
vector = model[common_corpus[4]] # apply model to BoW document
# 更新模型
model.add_documents(common_corpus[4:]) # update model with new documents
tmp_fname = get_tmpfile("lsi.model")
model.save(tmp_fname) # save model
loaded_model = LsiModel.load(tmp_fname) # load model
其中的关键是构建LSA模型
class gensim.models.lsimodel.LsiModel(corpus=None, num_topics=200, id2word=None, chunksize=20000, decay=1.0, distributed=False, onepass=True, power_iters=2, extra_samples=100, dtype=<type 'numpy.float64'>)
关键参数:
corpus:文本语料
num_topic:保留的语义维数
id2word:ID到单词映射
该对象包括如下方法:
LsiModel.projection.u,获得左奇异向量;
LsiModel.projection.s,获得奇异值;
add_documents(),用新的语料更新模型;
get_topics(),获取所有潜语义的向量表示;
save(),保存模型到本地;
load(),从本地加载模型;
print_topic(topicno, topn=10),以string的形式输出第topicno个潜在语义的前topn个单词表示;
print_topics(num_topics=20, num_words=10),以string形式输出前num_topics个潜在语义,每个语义用num_words个单词表示;
show_topic(topicno, topn=10),获取定义第topicno个潜在语义的单词及其贡献;
知乎-奇异值的物理意义是什么
LSA Tutorial