潜在语义分析LSA(Latent Semantic Analysis )也叫作潜在语义索引LSI( Latent Semantic Indexing ) 顾名思义是通过分析文章(documents )来挖掘文章的潜在意思或语义(concepts )。LSA和传统向量空间模型(vector space model)一样使用向量来表示词(terms)和文档(documents),并通过向量间的关系(如夹角)来判断词及文档间的关系;不同的是,LSA将词和文档映射到潜在语义空间,从而去除了原始向量空间中的一些“噪音”,提高了信息检索的精确度。
用处
-
分类:
-
将词汇表中的字词按意思归类(比如将各种体育运动的名称都归成一类)
-
将文本按主题归类(比如将所有介绍足球的新闻归到体育类)
-
-
检索:用户提出提问式(通常由若干个反映文本主题的词汇组成),然后系统在数据库中进行提问式和预存的文本关键词的自动匹配工作,两者相符的文本被检出。
原理
通过对大量的文本集进行统计分析,根据上下文提取出词语含义。技术上通过SVD分解等处理,消除了同义词、多义词的影响,提高了后续处理的精度。 流程:
-
分析文档集合,建立词汇-文本矩阵A。参考上一篇文章(CountVector基础功能的复现)
-
对词汇-文本矩阵进行奇异值分解
-
对SVD分解后的矩阵进行降维
这种降维可以使用截断 SVD 来执行。截断 SVD 的降维方式是:选择奇异值中最大的个数,且只保留矩阵和的前列。在这种情况下,是一个超参数,我们可以根据想要查找的主题数量进行选择和调整。可以看做是主题数。 -
使用降维后的矩阵构建潜在语义空间
SVD
即奇异值分解,是线性代数中的一种技术。该技术将任意矩阵分解为三个独立矩阵的乘积:,其中是矩阵A奇异值的对角矩阵。
对于奇异值,它跟我们特征分解中的特征值类似,在奇异值矩阵中也是按照从大到小排列,而且奇异值的减少特别的快,在很多情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上的比例。也就是说,我们也可以用最大的k个的奇异值和对应的左右奇异向量来近似描述矩阵。即矩阵A只需要灰色的部分的三个小矩阵就可以近似描述了。
-
A': Term行 * Document列
-
U: Term行 * concept列
-
sigma : concept行 * concept列
-
VT: concept行 * Document列
由于这个重要的性质,SVD可以用于PCA降维,来做数据压缩和去噪。也可以用于推荐算法,将用户和喜好对应的矩阵做特征分解,进而得到隐含的用户需求来做推荐。同时也可以用于NLP中的算法,如LSI。
词-文档矩阵A‘
稀疏矩阵,其行代表词,其列代表文档。一般情况下,词-文档矩阵的元素是该词在文档中的出现次数,也可以是是该词的tf-idf。
矩阵U
是对词进行分类的一个结果,它的每一行表示一个词,每一列表示一个语义相近的词类,这一行中每个非零元素表示每个词在每个语义类中的重要性(相关性)
矩阵sigma
表示词的类和文章的类之间的相关性
矩阵VT
是对文本进行分类的一个结果,它的每一行表示一个主题,每一列表示一个文本,这一列每个元素表示这篇文本在不同主题中的相关性
实例如下:
k=2
复现
MyLatentSemanticAnalysis(object):
def __init__(self, A, k):
# A:The term-frequency matrix
# k:The desired number of topics (or rank)
self.A = A.T
self.k = k
def transform(self):
# yield the topic distribution per document
#SVD
U, sigma, VT = np.linalg.svd(self.A)
# Take the first k columns
self.new_U = U[:,:self.k]
# Take the first k numbers (sigma is sorted)
self.new_sigma = sigma[:self.k]
# new_sigma = np.eye(self.k)
# for i in range(self.k):
# new_sigma[i][i] = sigma[i]
# new_VT = VT[:self.k,:]
# new_A = np.dot(np.dot(new_U, new_sigma), new_VT)
def get_topics(self, n, vocab):
# return the top n words for all k topics as a list of lists
# Construct tf-matrix column number to word map
idx2word = {j:i for i,j in vocab.items()}
top_n_words_ls = []
for i in range(self.new_U.shape[1]):
# Take the top n words for topic i
sorted_word_idx_ls = sorted(enumerate(self.new_U.T[i]), key=lambda x:abs(x[1]), reverse=True)[:n]
top_n_words_ls.append([idx2word[i[0]] for i in sorted_word_idx_ls])
return top_n_words_ls
def get_importance(self):
# return the importance of all k topics as a list
return self.new_sigma
cv = MyCountVectorizer(1, True)
cv.fit(data)
term_frequency_matrix = cv.transform()
lsa = MyLatentSemanticAnalysis(term_frequency_matrix, 10)
lsa.transform()
print(lsa.get_topics(5, cv1.get_vocab()))
print(lsa.get_importance())
ps:计算复杂度高
网友评论