2017-04-21 112 views
0

我一直在試圖實現愚蠢的退格語言模型(描述可用here,但我相信細節與問題無關)。什麼是減緩這段Python代碼?

事情是,代碼工作併產生預期的結果,但工作速度比我預期的要慢。我想通了,是減緩一切都在這裏(而不是在訓練的一部分)的一部分:

def compute_score(self, sentence): 
    length = len(sentence) 
    assert length <= self.n 
    if length == 1: 
     word = tuple(sentence) 
     return float(self.ngrams[length][word])/self.total_words 
    else: 
     words = tuple(sentence[::-1]) 
     count = self.ngrams[length][words] 
     if count == 0: 
      return self.alpha * self.compute_score(sentence[1:]) 
     else: 
      return float(count)/self.ngrams[length - 1][words[:-1]] 

def score(self, sentence): 
""" Takes a list of strings as argument and returns the log-probability of the 
    sentence using your language model. Use whatever data you computed in train() here. 
""" 
    output = 0.0 
    length = len(sentence) 
    for idx in range(length): 
     if idx < self.n - 1: 
      current_score = self.compute_score(sentence[:idx+1]) 
     else: 
      current_score = self.compute_score(sentence[idx-self.n+1:idx+1]) 
     output += math.log(current_score) 
    return output 

self.ngrams是具有n個條目嵌套的字典。這些條目中的每一個都是形式(word_i,word_i-1,word_i-2 .... word_i-n)的詞典:該組合的計數。

self.alpha是一個常數,定義了n-1的懲罰。

self.n是程序在字典self.ngrams中查找的元組的最大長度。它被設置爲3(儘管將其設置爲2或甚至1都不會)。這很奇怪,因爲Unigram和Bigram模型在幾分之一秒內就可以正常工作。

我正在尋找的答案不是我自己的代碼的重構版本,而是一個提示,其中的一部分是計算量最大的代碼(這樣我就可以弄清楚自己如何重寫它並獲得最多解決這個問題的教育收益)。

請耐心等待,我只是一個初學者(進入編程世界兩個月)。謝謝。

UPD: 我使用了time.time()的定時具有相同數據的運行時間:

單字組= 1.9

Bigram = 3.2

笨退避(N = 2)= 15.3

笨退避(N = 3)= 21.6

(它是在一些比原先由於了time.time的更大的數據。精度差)

+1

如果代碼正常工作,並且您只是在尋求同行評審以優化它,那麼您應該詢問[codereview.se]。這個網站是關於您首先獲取代碼的問題。 –

+2

@KenWhite:Stack Overflow的主題是否與代碼生成的結果不正確有關,還是代碼複審的主題。這個問題至少和主題一樣,「爲什麼我的輸出包含額外的空格」或「爲什麼我的列表是空的」問題。我們有一個具體問題 - 運行時間大大超過預期 - 問題是尋找關於導致該問題的具體答案,而不是通用代碼改進。這個問題不需要移動。 – user2357112

+0

@ user2357112:CR是專門爲此創建的。我沒有投票結束;我提出了一個建議。創建CR是爲了優化目的而檢查工作代碼。產生不正確結果的代碼不是工作代碼,也不是產生額外空格的代碼*或空列表*,因此您對它們的引用是不相關的。如果沒有問題適合遷移到CR,爲什麼它首先存在?如果你想要討論它的存在或相關性,那就把它作爲[meta]。 (這裏也沒有*提到的實質上更長的*,只是*比預期的要慢*) –

回答

0

如果句子很長,大部分實際上運行的代碼是在這裏:

def score(self, sentence): 
    for idx in range(len(sentence)): # should use xrange in Python 2! 
     self.compute_score(sentence[idx-self.n+1:idx+1]) 

def compute_score(self, sentence): 
    words = tuple(sentence[::-1]) 
    count = self.ngrams[len(sentence)][words] 
    if count == 0: 
     self.compute_score(sentence[1:]) 
    else: 
     self.ngrams[len(sentence) - 1][words[:-1]] 

這並不意味着工作的代碼 - 它只是刪除了不重要的部分。因此

關鍵路徑的流程是:

  • 對於句子中的每個單詞:
    • 呼叫compute_score()對這個詞加上下列2這將創建一個長度爲3的新列表。您可以使用itertools.islice()來避免這種情況。
      • 構造一個三元組,並翻轉單詞。這創建了一個新的元組。您可以通過傳遞-1步驟參數來避免這種情況,因爲在此函數之外進行切片時。
      • 查看self.ngrams,一個嵌套的字典,第一個鍵是一個數字(如果這個級別是一個列表,可能會更快;只有三個鍵?),第二個是剛創建的元組。
      • 用遞歸的第一個字去掉,即建立一個新的元組(sentence[2], sentence[1]),或
      • 做另查找在self.ngrams,隱含創建另一個新的記錄(words[:-1])

總之,我覺得你有最大的問題是列表和元組的重複和嵌套的創建和銷燬。