models.lsimodel - 隐含语义分析

Reading time ~1 minute

python 隐含语义分析LSA(或者LSI)模块。

通过奇异值分解(SVD)的方式进行实现。SVD可以在任何时间添加新值进行更新(实时、增量、高效利用内存的训练)

该模块实际上包含了许多算法,用来分解大语料,允许构建LSI模块:

  • 语料比RAM大:对于语料的size来说 (尽管依赖特征集的size),只需要常量的内存空间
  • 语料是流式的:文档只能按顺序访问,非随机访问
  • 语料不能被临时存储:每个文档只能被查看一次,必须被立即处理(one-pass算法)
  • 对于非常大的语料,可以通过使用集群进行分布式运算

English Wikipedia的性能测试(2G的语料position,3.2M文档,100k特征,在最终的TF-IDF矩阵中0.5G的非零实体),请求 top 400 LSI因子:

算法 单机串行 分布式
one-pass merge算法 5h 14m 1h 41m
multi-pass stochastic算法(2阶迭代) 5h 39m N/A[注]

注:

serial = Core 2 Duo MacBook Pro 2.53Ghz, 4GB RAM, libVec

distributed = cluster of four logical nodes on three physical machines, each with dual core Xeon 2.0GHz, 4GB RAM, ATLAS

[注]stochastic算法可以设成分布式,此由于数据分布在集群节点上,时大量的时间花费在读取/解压输入磁盘文件、额外的网络传输,所以看起来变慢了。


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)

1
2
3
4
5
6
7
8
9
Bases:  [gensim.interfaces.TransformationABC](http://radimrehurek.com/gensim/interfaces.html#gensim.interfaces.TransformationABC)

该类允许构建和维护一个LSI的模型。

主要的方法有:

- 1.构造函数:用来初始化潜语义空间
- 2.[]方法:它返回任何输入文件在潜语义空间的表示
- 3.add_documents(): 使用新的文档进行增量模型更新

左奇异矩阵被存储在lsi.projection.u中,奇异值在lsi.projection.s。右奇异矩阵可以在lsi[training_corpus]的输出中被重新构造。

模型持久化可以通过它的load/save方法来完成.

https://github.com/piskvorky/gensim/wiki/Recipes-&-FAQ#q4-how-do-you-output-the-u-s-vt-matrices-of-lsi

num_topics: 请求因子的数目 (潜维度)

在模型被训练后,你可以估计主题,使用 topics = self[document] 字典。你也可以使用self.add_documents 来添加新的训练文档,因此,训练可以在任何时间被stop和resume,LSI变换可以在任何时候进行。

如果你指定了一个corpus,它可以被用来训练模型。方法 add_documents 以及相应的参数chunksize和decay描述。

关闭onepass,强制使用multi-pass stochastic算法。

power_iters和extra_samples影响了stochastic multi-pass算法的精准度,它可以在内部使用(onepass=True), 或者前端算法(onepass=False)。增加迭代数可以改次精确度,但是性能会降低。

打开distributed可以使用分布式计算。

示例:  »> lsi = LsiModel(corpus, num_topics=10)  »> print(lsi[doc_tfidf]) # project some document into LSI space  »> lsi.add_documents(corpus2) # update LSI on additional documents  »> print(lsi[doc_tfidf])

[3] http://nlp.fi.muni.cz/~xrehurek/nips/rehurek_nips.pdf


add_documents(corpus, chunksize=None, decay=None)

更新奇异值分解,重视新文档语料。

一次训练只处理chunksize大于的文档chunk。chunksize的大小,需要在增速(更大的chunksize) vs 低内存footprint(更小的chunksize)之间权衡。如果分布式模式是打开的,每个chunk都被发送到一个不同的worker/computer。

若设置decay < 1.0,会造成在输入文档流中偏向新数据,通过给定少量的emphasis给老数据。这将允许LSA逐渐“忘记”老的文档,并将更多偏向给新数据。

classmethod load(fname, *args, **kwargs)

1
2
3
加载之前保存的对象文件。

可以被还原成mmap大数组.

print_debug(num_topics=5, num_words=10)

1
2
3
打印日志给num_topics个主题以最重要的信息。

不同于print_topics(),对于一个指定的主题来说,words信息很重要。这可以产生一个可读性良好的主题描述。

print_topic(topicno, topn=10)

1
返回对应主题号单个主题字符串。可以参见show_topic()的参数。

见:

lsimodel.print_topic(10, topn=5) ‘-0.340 * “category” + 0.298 * “$M$” + 0.183 * “algebra” + -0.174 * “functor” + -0.168 * “operator”’


print_topics(num_topics=5, num_words=10)

1
show_topics()的别名函数,可以打印top5主题的日志。

save(fname, *args, **kwargs)

1
2
3
将model保存成文件。

大的内部array将被保存成独立的文件,使用fname作为前缀.

show_topic(topicno, topn=10)

1
2
3
4
5
6
返回一个指定的主题(=左奇异矩阵)字符串, 0 <= topicno < self.num_topics。

只返回topn的话语,它会最接近该主题的方向(正或者负)

>>> lsimodel.show_topic(10, topn=5)
[(-0.340, "category"), (0.298, "$M$"), (0.183, "algebra"), (-0.174, "functor"), (-0.168, "operator")]

show_topics(num_topics=-1, num_words=10, log=False, formatted=True)

1
2
3
4
5
返回num_topics 的最大主题(缺省返回所有)。对于每个主题,可以展示给num_words最重要的word信息(缺省10个word)

如果formatted=True,该主题将返回一个列表;若为False,则返回一个(weight,word)的2-tuples的list。

如果log为True,也会输出该结果到日志上。

class gensim.models.lsimodel.Projection(m, k, docs=None, use_svdlibc=False, power_iters=2, extra_dims=100)

1
2
3
4
5
Bases:  gensim.utils.SaveLoad

从一个corpus文档中构造一个(U,S)的projection。该projection可以接着通过self.merge()将另一个Projection进行merge时进行更新动作。

该类负责“核心数学计算”,通过高层的LsiModel类操作,可以处理包含处理corpora的接口,将大的corpora分割成chunks,或将它们进行merge等。

empty_like()

classmethod load(fname, mmap=None)

1
加载之前保存的一个文件对象。

merge(other, decay=1.0)

1
2
3
merge另一个projection。

other: 它的内容会在处理过程中销毁,如果你在后面还需要用到它,可以考虑传给该函数一个other的copy。

save(fname, separately=None, sep_limit=10485760, ignore=frozenset([]))

1
2
3
保存文件对象.

同其它。

gensim.models.lsimodel.ascarray(a, name=’’)


gensim.models.lsimodel.asfarray(a, name=’’)


gensim.models.lsimodel.clip_spectrum(s, k, discard=0.001)

1
2
3
4
5
给定本征值s,它将返回:需要保存的因子个数,从而避免假值(tiny, numerically instable)。

它将忽略spectrum的尾部,relative combined mass < min(discard, 1/k).

返回值被k截断(=不会返回多于k个值)

gensim.models.lsimodel.print_debug(id2token, u, s, topics, num_words=10, num_neg=None)


gensim.models.lsimodel.stochastic_svd(corpus, rank, num_terms, chunksize=20000, extra_dims=None, power_iters=0, dtype=<type ‘numpy.float64’>, eps=1e-06)

1
2
3
4
5
6
7
8
9
10
11
在一个sparse输入上,运行SVD。

(U,S): 返回值。输入数据流语料上的左SV和奇异值。语料本身可能比RAM大(通过vector迭代)。

这将返回比请求的top rank因子还要少,因为输入本身的rank就很低。extra_dims(过采样)和指定的power_iters(power次迭代)参数将影响分解。

该算法使用2+power_iters传给输入数据。这种情况下,你只能承担一个single的输入,在LsiModel中将onepass=True,并且直接避免使用该函数。

该分解算法基于:Halko, Martinsson, Tropp. Finding structure with randomness, 2009

如果corpus使用的是一个scipy.sparse矩阵,那么假设整个corpus满足主内存大小,并选择一个不同的代码路径(效率更高).