主题模型
定义
主题模型(topic model)是以非监督学习的方式对文集的隐含语义结构(latent semantic structure)进行聚类(clustering)的统计模型。
主题模型主要被用于自然语言处理(Natural language processing)中的语义分析(semantic analysis)和文本挖掘(text mining)问题,例如按主题对文本进行收集、分类和降维;也被用于生物信息学(bioinfomatics)研究 [2] 。隐含狄利克雷分布Latent Dirichlet Allocation, LDA)是常见的主题模型。
主题模型历史
简单案例引入
通过建立判断是否为算法大牛:
特征:
- 穿条纹衬衫
- 曾在BAT就职
- 做过大型项目
于主题模型对比:
知识储备:SVD——奇异值分解
1、特征值
2、SVD分解
通过案例手推SVD
步骤二:求解特征值
步骤三:求解特征向量
python实现
import numpy as np
A = np.array([[2, 4], [1, 3], [0, 0], [0, 0]])
print(np.linalg.svd(A))
3、SVD与PCA
PCA理解和含义
- PCA的问题其实是一个基的变换,使得变换后的数据有着最大的方差。
- 我们用于机器学习的数据(主要是训练数据),方差大才有意义,不然输入的数据都是同一个点,那方差就为0了,这样输入的多个数据就等同于一个数据了。
- 对原始的空间中顺序地找一组相互正交的坐标轴,第一个轴是使得方差最大的,第二个轴是在与第一个轴正交的平面中使得方差最大的,第三个轴是在与第1、2个轴正交的平面中方差最大的,这样假设在N维空间中,我们可以找到N个这样的坐标轴,我们取前r个去近似这个空间,这样就从一个N维的空间压缩到r维的空间了,但是我们选择的r个坐标轴能够使得空间的压缩使得数据的损失最小。
SVD推导PCA
SVD其实是两个方向上的PCA
PCA是对AA ^T的分解
而SVD是:U的列是AAT的特征向量,V的列是AT*A的特征向量
也可以通过SVD分解公式,利用求得得V求解 U(如上面手推求解答的案例)
PLSA——概率隐性语义分析
Probabilistic Latent Semantic Analysis
1、SVD
参考博客:https://blog.csdn.net/chlele0105/article/details/12983833
大矩阵A来描述这一百万篇文章和五十万词的关联性。这个矩阵中,每一行对应一篇文章,每一列对应一个词。
三个矩阵有非常清楚的物理含义。第一个矩阵X是对词进行分类的结果,每一列表示一类主题,其中的每个非零元素表示一个主题与一篇文章的相关性,数值越大越相关。最后一个矩阵Y中的每一列表示100个语义类/词类,每个语义类/词类与500,000个词的相关性。中间的矩阵则表示文章主题和语义类/词类之间的相关性。因此,我们只要对关联矩阵A进行一次奇异值分解,就可以同时完成了近义词分类和文章的分类。(同时得到每类文章和每类词的相关性)。
2、LSA
与上面1不同的是行列转置:行代表单词在每篇文档中出现的次数,列代表一篇文档中出现词语的分布。
依据奇异值分解的性质1,矩阵A可以分解出n个特征值,然后依据性质2,我们选取其中较大的r个并排序,这样USVT就可以近似表示矩阵AA。对于矩阵U,每一列代表一个潜语义,这个潜语义的意义由m个单词按不同权重组合而成。因为U中每一列相互独立,所以r个潜语义构成了一个语义空间。S中每一个奇异值指示了该潜语义的重要度。VT中每一列仍然是一篇文档,但此时文档被映射了语义空间。V^T的大小远小于A。
有了VT,我们就相当于有了矩阵AA的另外一种表示,之后我们就可以使用VT代替A进行之后的工作。
流程
(1)分析文档集合,建立词汇-文本矩阵A。
(2)对词汇-文本矩阵进行奇异值分解。
(3)对SVD分解后的矩阵进行降维
(4)使用降维后的矩阵构建潜在语义空间
LSA案例:
奇异值分解:
表示我们将文档映射到了一个3维语义空间中,其中第一维潜语义可以表示为
在图上,每一个红色的点,都表示一个词,每一个蓝色的点,都表示一个title,这样我们可以对这些词和title进行聚类,比如stock和market可以放在一类,这也符合他们经常出现在一起的直觉,real和estate可以放在一类,dads,guide这种词就看起来有点孤立了,我们就不对他们进行合并了。对于title,T1和T3可以聚成一类,T2、T4、T5和T8可以聚成一类,所以T1和T3比较相似,T2、T4、T5和T8比较相似。按这样聚类出现的效果,可以提取文档集合中的近义词,这样当用户检索文档的时候,是用语义级别(近义词集合)去检索了,而不是之前的词的级别。这样一减少我们的检索、存储量,因为这样压缩的文档集合和PCA是异曲同工的,二可以提高我们的用户体验,用户输入一个词,我们可以在这个词的近义词的集合中去找,这是传统的索引无法做到的。
LSA的优缺点
-
优点
1)低维空间表示可以刻画同义词,同义词会对应着相同或相似的主题。
2)降维可去除部分噪声,是特征更鲁棒。
3)充分利用冗余数据。
4)无监督/完全自动化。
5)与语言无关。 -
缺点
1)LSA可以处理向量空间模型无法解决的一义多词(synonymy)问题,但不能解决一词多义(polysemy)问题。因为LSA将每一个词映射为潜在语义空间中的一个点,也就是说一个词的多个意思在空间中对于的是同一个点,并没有被区分。
2)SVD的优化目标基于L-2 norm 或者 Frobenius Norm 的,这相当于隐含了对数据的高斯分布假设。而 term 出现的次数是非负的,这明显不符合Gaussian
假设,而更接近Multi-nomial
分布。(需要进一步研究为什么)
3)特征向量的方向没有对应的物理解释。
4)SVD的计算复杂度很高,而且当有新的文档来到时,若要更新模型需重新训练。
5)没有刻画term出现次数的概率模型。
6)对于count vectors 而言,欧式距离表达是不合适的(重建时会产生负数)。
7)维数的选择是ad-hoc的。
8)LSA具有词袋模型的缺点,即在一篇文章,或者一个句子中忽略词语的先后顺序。
9)LSA的概率模型假设文档和词的分布是服从联合正态分布的,但从观测数据来看是服从泊松分布的。因此LSA算法的一个改进PLSA使用了多项分布,其效果要好于LSA。
LSA——python实现
gensim
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个潜在语义的单词及其贡献;
from gensim.test.utils import common_corpus,common_dictionary,get_tmpfile
from gensim.models import LsiModel
#构建模型
print(common_corpus)
'''
#[[(0, 1), (1, 1), (2, 1)], [(0, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)], [(2, 1), (5, 1),......
输出的为bag-of-word后的元组,(id,count)
#语料为:common_texts = [
['human', 'interface', 'computer'],
['survey', 'user', 'computer', 'system', 'response', 'time'],
['eps', 'user', 'interface', 'system'],
['system', 'human', 'system', 'eps'],
['user', 'response', 'time'],
['trees'],
['graph', 'trees'],
['graph', 'minors', 'trees'],
['graph', 'minors', 'survey']
]
'''
model = LsiModel(common_corpus[:3],id2word=common_dictionary,num_topics=3)
#将文档映射到语义空间
vector = model[common_corpus[4]]
#更新模型
model.add_documents(common_corpus[4:])
tmp_fname = get_tmpfile("lsi.model")
model.save(tmp_fname) # save model
loaded_model = LsiModel.load(tmp_fname) # load model
umatri = loaded_model.projection.u
print(umatri)
print(umatri.shape)
ss = umatri = loaded_model.projection.s
print(ss)
allt = loaded_model.get_topics()
print(allt)
t1 = loaded_model.print_topic(1,topn=