算法复杂度包含两个方面,时间复杂度和空间复杂度,也就是常称的计算复杂度和内存空间占用两方面。下面先说主题模型的计算复杂度,再说内存占用。
时间复杂度
本来这个问题挺简单的,但是有搞统计的同学不知道CS里面的复杂度是怎么计算,就写到这里吧。本文比较三个topic model时间复杂度的计算方法,分别是LDA,Biterm,和hdLDA。
时间复杂度的计算很好掌握,就是把算法中的最基本操作作为单元操作,设定其时间复杂度为O(1)。最简单的可以是赋值、加减乘除等操作;复杂的也可以将之前文章中讲到的一次Gibbs sampling抽样(一个变量t+1时刻的值,由其它变量t时刻的值抽得),作为一个单元操作。最后看该操作总共执行了多少次,若执行了M次,则算法的时间复杂度就是O(M)。
在Topic Model中,单元操作可看作是一次Gibbs抽样(其实可以更细粒度到加减乘除运算,但是本次所对比的LDA和Biterm模型的单次抽样复杂度相同,所以不需要比较更细粒度的运算)。那么计算时间复杂度就是计算该抽样所执行的次数。所以接下来就是看一下套在单元计算外面的计算步骤。
int last_iter = liter;
for (liter = last_iter + 1; liter <= niters + last_iter; liter++) {
printf("Iteration %d ...\n", liter);
for (int m = 0; m < M; m++) {
for (int n = 0; n < ptrndata->docs[m]->length; n++) {
int topic = sampling(m, n);
z[m][n] = topic;
}
}
}
由上面代码可以看出,最外层是一个迭代次数 Niter , 内层是文档个数 M ,也即
int model::sampling(int m, int n) {
// remove z_i from the count variables
int topic = z[m][n];
int w = ptrndata->docs[m]->words[n];
nw[w][topic] -= 1;
nd[m][topic] -= 1;
nwsum[topic] -= 1;
ndsum[m] -= 1;
double Vbeta = V * beta;
double Kalpha = K * alpha;
// do multinomial sampling via cumulative method
for (int k = 0; k < K; k++) {
p[k] = (nw[w][k] + beta) / (nwsum[k] + Vbeta) *
(nd[m][k] + alpha) / (ndsum[m] + Kalpha);
}
// cumulate multinomial parameters
for (int k = 1; k < K; k++) {
p[k] += p[k - 1];
}
double u = ((double)random() / RAND_MAX) * p[K - 1];
for (topic = 0; topic < K; topic++) {
if (p[topic] > u) {
break;
}
}
nw[w][topic] += 1;
nd[m][topic] += 1;
nwsum[topic] += 1;
ndsum[m] += 1;
return topic;
}
可以看到核心计算过程也就是在为p[k]赋值的过程,而k一共有K个。所以最终的复杂度也就是: O(NiterNDKl¯) 。
那么计算Biterm topic model也就很简单了。由于该模型的主题分布不在文档上,而是在biterm上,而biterm的个数为 NDl¯(l¯−1)/2 。因此时间复杂度就是 O(