演算法複雜度包含兩個方面,時間複雜度和空間複雜度,也就是常稱的計算複雜度和記憶體空間占用兩方面。下面先說主題模型的計算複雜度,再說記憶體占用。
本來這個問題挺簡單的,但是有搞統計的同學不知道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++)
}}
由上面**可以看出,最外層是乙個迭代次數ni
ter , 內層是文件個數
m ,也即nd
,再內層是相應文件的長度(不重複單詞的個數)。由於每一篇文件的長度不同,因此取平均值l¯
。然後再看一下sampling()**:
int model::sampling(int m, int n)
// cumulate multinomial parameters
for (int k = 1; k < k; k++)
double u = ((double)random() / rand_max) * p[k - 1];
for (topic = 0; topic < k; topic++)
}nw[w][topic] += 1;
nd[m][topic] += 1;
nwsum[topic] += 1;
ndsum[m] += 1;
return topic;
}
可以看到核心計算過程也就是在為p[k]賦值的過程,而k一共有k個。所以最終的複雜度也就是:o(
nite
rndk
l¯) 。
那麼計算biterm topic model也就很簡單了。由於該模型的主題分布不在文件上,而是在biterm上,而biterm的個數為nd
l¯(l
¯−1)
/2。因此時間複雜度就是o(
nite
rknd
l¯(l
¯−1)
/2) 。
對於hdlda模型來說,其抽樣步驟比較繁瑣。具體**為:
printf("sampling %d iterations!\n", niters);
int last_iter = liter;
for (liter = last_iter + 1; liter <= niters + last_iter; liter++)
}for (int
m = 0; m
< m; m++)
}for (int
m = 0; m
< m; m++)
}}
可以看到每乙個sampling step在外層都共享了ni
tern
d 次迴圈(其中用nd
代表m )。而sampling_step_one()接下來的迴圈次數為:
ptrndata->contents[m]->length
這與lda**部分相似。
而sampling_step_two()和sampling_step_three()則各自迴圈下面次數,也就是每篇正文的跟帖條數:
ptrndata->comments[m]->c
由於不是每篇正文的跟帖數目都是相同的,因此這裡也取平均值,記作c¯
。接下來再看看每乙個抽樣步驟中的時間複雜度。sampling_step_one()的**是這樣的:
int model::sampling_step_one(int m, int n)
// cumulate multinomial parameters
for (int k = 1; k < k; k++)
// scaled sample because of unnormalized p
double u = ((double)rand() / rand_max) * p[k - 1];
for (topic = 0; topic < k; topic++)
}if (topic == k)
ndk[m][topic] += 1;
nkv[topic][w] += 1;
ndksum[m] += 1;
nkvsum[topic] += 1;
return topic;
}
可以看到其中核心計算步驟迭代了k次,與lda和biterm相同。因此對於sampling_step_one()步來說,其具有複雜度ni
tern
dl¯ 。
對於sampling_step_two_x()和sampling_step_two_y()來說,其**非常相似,節省篇幅起見,下面只給出step_two_x()的**:
int model::sampling_step_two_x(int m, int c)
}for (int k = 0; k < k; k++)
}p[k] += left;
p[k] = exp(p[k]);
}// cumulate multinomial parameters
for (int k = 1; k < k; k++)
double u = ((double)rand() / rand_max) * p[k - 1];
for (topic = 0; topic < k; topic++)
}if (topic == k)
for (int i = 0; i < ptrndata->comments[m]->commlines[c]->length; i++)
}return topic;
}
可以看出這一步中,計算時間複雜度為c¯
lc¯ ,其中lc
¯ 為跟帖的平均長度。同理sampling_step_two_y()也具有相同的複雜度。
而sampling_step_three()的複雜度為lc
¯ 。因此hdlda的時間複雜度為:ni
tern
d(kl
¯+2k
c¯lc
¯+c¯
lc¯)
空間複雜度就是程式在計算時,需要放在記憶體中用於儲存中間結果的矩陣、向量等資料型別所佔的空間。一般省略掉無關重要的變數,如標記變數等。而只關注與演算法核心相關的記憶體空間占用(好像更貼近偽**)。
在lda中,演算法需要維護三個矩陣:θ,
ϕ 和字典對映矩陣。它們分別為文件在主題上的分布矩陣,規模為nd
∗k;主題在詞上的分布,規模為 w∗
k 和文檔次的編號對映矩陣,規模為nd
∗l¯ 。因此其空間複雜度就是nd
k+wk
+nd∗
l¯了。
biterm因為沒有了文件的概念,沒有了矩陣
θ , 而
ϕ 又變成了主題在biterm上的分布,因而複雜度相對於lda有一點調整,是k+
wk+n
dl¯(
l¯−1
)/2 。
hdlda所需要的不重要變數特別多,但是如果不計入核心演算法的話,有
ϕ 矩陣,規模為wk
;θ矩陣,規模為nd
k ;
ψ 矩陣,規模為jw
,其中k 表示formal topic 的個數,
j表示leisure topic 的個數;以及文件儲存矩陣ptrndata(結構體),規模為nd
c¯lc
¯ 因此空間複雜度為wk
+ndk
+jw+
ndc¯
lc¯ 。
發現好簡單啊。
複雜度分析 時間複雜度 空間複雜度
執行效率是演算法的乙個重要的考量指標,演算法的執行效率用時間 空間複雜度來衡量。今天我們來學習一下複雜度的分析。通常我們可以通過執行程式來獲得演算法的真正的執行時間,這種方法我們可以稱為事後統計法,但這種方法得到的是具體的資料,測試結果很依賴測試環境,而且受資料規模影像最大。因此,我們需要乙個不需要...
演算法的複雜度 時間複雜度與空間複雜度
通常,對於乙個給定的演算法,我們要做 兩項分析。第一是從數學上證明演算法的正確性,這一步主要用到形式化證明的方法及相關推理模式,如迴圈不變式 數學歸納法等。而在證明演算法是正確的基礎上,第二步就是分析演算法的時間複雜度。演算法的時間複雜度反映了程式執行時間隨輸入規模增長而增長的量級,在很大程度上能很...
演算法複雜度 時間複雜度和空間複雜度
1 時間複雜度 1 時間頻度 乙個演算法執行所耗費的時間,從理論上是不能算出來的,必須上機執行測試才能知道。但我們不可能也沒有必要對每個演算法都上機測試,只需知道哪個演算法花費的時間多,哪個演算法花費的時間少就可以了。並且乙個演算法花費的時間與演算法中語句的執行次數成正比例,哪個演算法中語句執行次數...