以十億個單字進行語言模型化

2016 年 7 月 25 日 作者:Nicholas Leonard

在我們上一篇貼文中,介紹了一個結合強化學習和遞迴神經網路的視覺注意力遞迴模型。在這個 Torch 部落格貼文中,我們使用雜訊對比估計 (NCE) [2],在 Google 十億字元 (GBW) 資料集 [7] 上訓練一個以多 GPU 遞迴神經網路為基礎的語言模型 (RNNLM)。這裡呈現的成果是由 Element-Research 經過好幾個月斷斷續續的研究才完成的。龐大的資料集讓我們必須貢獻一些新穎的開源 Torch 模組、標準,甚至是一個多 GPU 張量。我們也提供指令碼,讓您可以訓練和評估您的語言模型。

如果您只對產生的範例、困惑度和學習曲線有興趣,請直接跳到結果區段

文字模型與字元語言模型

在最近幾個月中,您可能會注意到大家對生成字元等級 RNNLM(例如 char-rnn 和較新的torch-rnn)越來越有興趣。這些模型非常有趣,因為它們可用於產生以下一類的字元序列

<post>
Diablo
<comment score=1>
I liked this game so much!! Hope telling that numbers' benefits and 
features never found out at that level is a total breeze 
because it's not even a developer/voice opening and rusher runs 
the game against so many people having noticeable purchases of selling 
the developers built or trying to run the patch to Jagex.
</comment>

上面這段文字是使用reddit 留言的範本,一次產生一個字元。如您所見,產生的文字,乍看之下,其一般結構看起來不錯。標記適當打開和關閉。第一句看起來不錯:我很喜歡這個遊戲!而且與貼文的子版塊相關:Diablo。但是,讀完後面的內容,我們可以開始看出字元等級語言模型的限制。個別單字的拼寫看起來很不錯,但是下一句的意思卻很難理解(而且很長)。

在這個部落格貼文中,我們將會展示如何使用 Torch 訓練一個大規模的字詞等級語言模型以產生獨立的句子。字詞等級模型比字元等級模型有重要的優勢。請以以下序列為例(羅伯特·海萊茵的一句名言)

Progress isn't made by early risers. It's made by lazy men trying to find easier ways to do something.

在進行符號化後,詞彙級模型視此順序包含 22 個符號。反之,字元級視此順序包含 102 個符號。這個較長的順序使得字元模型的工作比詞彙模型困難,這是因為它必須考量更多符號間更多時間步長的依存性。字元語言模型的另一個問題在於它們需要學習拼寫,除了語法、語意等。無論如何,詞彙語言模型通常會比字元模型有較低的錯誤率。[8]

字元模型相較於詞彙語言模型的主要優勢在於它們有極小的詞彙表。舉例來說,GBW 資料集會包含約 800 個字元,相較於 80 萬個詞彙(在修剪低頻率符號後)。在實務上來說,這表示字元模型需要的記憶體較小,並且比它們的詞彙表對應項有更快的推論速度。另一項優勢在於它們不需要符號化作為前處理步驟。

遞迴神經網路語言模型

我們的任務是建置一個語言模型,它最大化在句子中前幾個詞彙的歷史給定下,下一個詞彙的可能性。下列圖示說明了一個簡單遞歸神經網路 (Simple RNN)語言模型的運作方式

確切的實作如下所示

h[t] = σ(W[x->h]x[t] + W[h->h]h[t1] + b[1->h])                      (1)
y[t] = softmax(W[x->y]h[t] + b[1->y])                                (2)

對於此特定範例,模型應在給定「是什麼」的情況下最大化「是」的機率,接著在給定「是」的情況下最大化「那個」,依此類推。簡易 RNN 具有內部隱藏狀態 h[t],而此狀態會綜合迄今輸入的序列,以最大化序列中剩餘字詞的機率。在內部,簡易 RNN 具有由輸入至隱藏(字詞嵌入)、隱藏至隱藏(遞迴連線)及隱藏至輸出的參數(會輸入至 softmax 的輸出嵌入)。輸入至隱藏的參數包含 LookupTable,可將每個字詞表示成向量。這些向量組成了字詞的嵌入空間。輸入 x[t]LookupTable 的內容,是與字詞 w[t] 關聯的唯一整數。此字詞的嵌入向量透過編制嵌入空間 W[x->h](我們以 W[x->h]x[t] 表示此空間)取得。隱藏至隱藏的參數透過產生 h[t] 的隱藏狀態來模擬字詞的時間相依性,給定 h[t-1]x[t]。這會在 h[t]h[t-1](及字詞 x[t])之函數時產生實際的遞迴。隱藏至輸出的層會執行仿射轉換(也就是一個 Linear 模組:W[x->y]h[t] + b[1->h]),接著再執行 softmax。這是為了在給定先前的字詞(由隱藏狀態 h[t] 體現)的情況下,估計下一個字詞的機率分佈 y[t]。標準是針對下一個字詞 w[t+1] 在給定先前字詞的情況下,最大化下一個字詞的機率:P(w[t+1]|w[1],w[2],...,w[t])

使用 rnn 套件(請參閱 簡易 RNN 範例)可輕易建置簡易 RNN,但這並非可用來建模語言的唯一模型類別。還有更進階的長短期記憶 (LSTM) 模型 [3],[4],[5],其具有特殊閘控元件,可在更長的序列中促進梯度的反向傳播。

確切的實作如下所示

i[t] = σ(W[x->i]x[t] + W[h->i]h[t1] + b[1->i])                      (3)
f[t] = σ(W[x->f]x[t] + W[h->f]h[t1] + b[1->f])                      (4)
z[t] = tanh(W[x->c]x[t] + W[h->c]h[t1] + b[1->c])                   (5)
c[t] = f[t]c[t1] + i[t]z[t]                                         (6)
o[t] = σ(W[x->o]x[t] + W[h->o]h[t1] + b[1->o])                      (7)
h[t] = o[t]tanh(c[t])                                                (8)

主要的優點是,LSTM 可以學習時間間距較長的字詞之間的相依性。由於不同的閘控元件可以在反向傳播期間保留梯度,因此它不像消失梯度問題那麼容易發生。若要建立 LM,字詞嵌入(等式 1 中的 W[x->h]x[t])會輸入 LSTM,而結果的隱藏狀態會輸入等式 2。

語言模型的誤差傳統上利用困惑度來衡量。困惑度是衡量模型在看到一連串文字時的驚訝程度。如果我們將一連串文字輸入模型,模型可以針對每一個連續的文字,以高機率預測下一個文字,那麼這個模型的困惑度就低。如果一個長度為 T 的文字序列 s 中的第 t 個字,利用索引 s[t] 來標示,其透過模型推論的機率為 y[t],那麼該字的機率為 y[t][s[t]],這段文字序列的困惑度為

                 log(y[1][s[1]) + log(y[2][s[2]) + ... + log(y[T][s[T])
PPL(s,y) = exp( -------------------------------------------------------- )
                                          -T

困惑度愈低,愈好。

載入 Google 十億字元資料集

對於我們的字元等級語言模型,我們使用 GBW 資料集。此資料集與 Penn Tree Bank 不同之處在於,它讓句子彼此獨立。因此我們的資料集會包含一組獨立且長度不一的序列。我們可以透過 dataload 套件輕鬆載入這個資料集

local dl = require 'dataload'
local train, valid, test = dl.loadGBW(batchsize)

如果硬碟中沒有發現資料,上述程式碼會自動下載資料,並回傳訓練、驗證與測試資料集。這些都是 dl.MultiSequence 實例,其建構函式如下

dataloader = dl.MultiSequence(sequences, batchsize)

sequences 參數為 Lua 表格或 tds.Vector,其中每個元素都是包含一個獨立序列的張量。例如

sequences = {
  torch.LongTensor{424,158,115,667,28,505,228},
  torch.LongTensor{389,456,188},
  torch.LongTensor{77,172,760,687,552,529}
}
batchsize = 2
dataloader = dl.MultiSequence(sequences, batchsize)

請注意,各序列長度不一。與所有 dl.DataLoader 子類別一樣,dl.MultiSequence 載入程式提供了一個方法,可以從資料集中子取樣一批 inputstargets

local inputs, targets = dataloader:sub(1, 10)

sub 方法使用 startend 索引標示要索引的子序列。在函式內,這些索引只會用於決定所要求多重序列的長度 (seqlen)。對 sub 的每一次後續呼叫都會回傳與前一次呼叫相連的多重序列。

回傳的 inputstargets 分別是 seqlen x batchsize [x inputsize] 張量,包含兩組多重序列,每組多重序列包含八個時間步長。從 inputs 開始:

print(inputs)
  0    0
 424   77
 158  172
 115  760
 667  687
  28  552
 505    0
   0  424
[torch.DoubleTensor of size 8x2]

每一個欄都是一個向量,包含多個序列,也稱為多重序列。獨立的序列會以零隔開。在下一段,我們將看到 rnn 套件如何使用這些以零遮罩的時間步長,在獨立序列之間有效地遺忘其隱藏狀態(在欄的粒度)。現在,請注意原本的 sequences,如何包含在回傳的 inputs 中,並以零隔開。

<目標><輸入> 類似,但使用 1 的遮罩來分隔序列(否則,<ClassNLLCriterion> 會抱怨)。在語言模型中,任務通常是預測下一個字,因此 <目標> 會比相應的 <輸入> 延遲一個時間步驟

print(targets)
   1    1
 158  172
 115  760
 667  687
  28  552
 505  529
 228    1
   1  158
[torch.DoubleTensor of size 8x2]

<dl.loadGBW> 呼叫回傳的 <訓練><驗證><測試> 具有與上述相同的屬性。唯一不同的是,資料集大很多(有一個十億個字)。為了除錯等目的,我們可以選擇載入訓練集的一個較小子集合。這樣載入的速度會比預設訓練集檔案快很多

local train, valid, test = dl.loadGBW({2,2,2}, 'train_tiny.th7')

上述會對所有集合使用 2 的 <批次大小>。使用 subiter 會讓迭代資料載入器更輕鬆

local seqlen, epochsize = 3, 10
for i, inputs, targets in train:subiter(seqlen, epochsize) do
   print("T = " .. i)
   print(inputs)
end

會輸出

T = 3	      
 0       0
 793470  793470
 211427    6697
[torch.DoubleTensor of size 3x2]

T = 6	 
 477149  400396
 720601  213235
 660496  368322
[torch.DoubleTensor of size 3x2]

T = 9	 
 676607   61007
 161927  767587
 248714  635004
[torch.DoubleTensor of size 3x2]

T = 10	 
 280570  130510
[torch.DoubleTensor of size 1x2]

我們也可以將上述批次傳回一個大區塊

train:reset() -- resets the internal sequence iterator
print(train:sub(1,10))
      0       0
 793470  793470
 211427    6697
 477149  400396
 720601  213235
 660496  368322
 676607   61007
 161927  767587
 248714  635004
 280570  130510
[torch.DoubleTensor of size 10x2]

請注意上述小批次如何與這個大區塊對齊。這表示資料會依序迭代。

GBW 資料集中的每個句子都封裝在 <S></S> 標記中,分別表示序列的開始和結束。每個標記都對應到一個整數。舉例而言,您可以在上述範例中看到 <S> 對應到整數 793470。現在對我們的資料集很有信心了,讓我們來看一下模型。

建立多層 LSTM

在本節中,我們會專注在實際建置多層 LSTM 的工作上。我們會在過渡到輸出層之後,從輸入層開始介紹 NCE。

<lm> 模型的輸入層是一個查詢表

lm = nn.Sequential()

-- input layer (i.e. word embedding space)
local lookup = nn.LookupTableMaskZero(#trainset.ivocab, opt.inputsize)
lm:add(lookup) -- input is seqlen x batchsize

作為 <LookupTable> 的子類別,我們使用 LookupTableMaskZero 來學習詞嵌入。最主要的差別在於它支援零索引,會作為零張量傳遞。接著,我們有實際的多層 LSTM 實作,它使用 SeqLSTM 模組

local inputsize = opt.inputsize
for i,hiddensize in ipairs(opt.hiddensize) do
   local rnn = nn.SeqLSTM(inputsize, hiddensize)
   rnn.maskzero = true
   lm:add(rnn)
   if opt.dropout > 0 then
      lm:add(nn.Dropout(opt.dropout))
   end
   inputsize = hiddensize
end

正如 rnn-benchmarks 資料庫中展示的,<SeqLSTM> 實作非常快速。接下來,我們會將 SeqLSTM 的輸出(一個 <seqlen x 批次大小 x 輸出大小> 張量)拆分成一個表,其中包含針對每個時間步驟一個 <批次大小 x 輸出大小> 張量

lm:add(nn.SplitTable(1))

問題:輸出層的瓶頸

由於賓州樹庫資料集的字彙量僅有 10000 個字,因此相較之下比較容易用於建立字元級語言模型。輸出層在訓練和推理方面仍有足夠的計算能力,特別是對於 GPU 而言。對於這些較小的字彙量,輸出層基本上就是一個 線性 接著一個 SoftMax

outputlayer = nn.Sequential()
   :add(nn.Linear(hiddensize, vocabsize))
   :add(nn.SoftMax())

不過,當使用 GBW 資料集中包含的 793471 個字詞等龐大字彙量來訓練時,輸出層很快就會成為瓶頸。例如,如果您使用 批次大小 = 128(每個批次的序列數)和 序列長度 = 50(時間反向傳播的序列大小)來訓練模型,則該層的輸出將擁有 序列長度 x 批次大小 x 字彙量大小 的形狀,也就是 128 x 50 x 793471。對於一個 FloatTensorCudaTensor,單一張量就會佔用 20GB 的記憶體!這個數字對於 gradInput(也就是對於輸入的梯度)來說可能會翻倍,而且由於 線性SoftMax 都會儲存一個 輸出 的副本,所以還會再翻倍。

除了參數及其梯度之外,上述數據概述了序列長度為 50 的 4 層 LSTM 和 2048 個單元的近似記憶體使用量。即使您設法找到一種方法將 80GB 放置在 GPU 上(或在多個 GPU 上分配),但在合理的時程中透過這個 輸出層 進行前向/後向傳遞仍然是一個問題。

解決方案:雜訊對比估計

LM 的輸出層使用 NCE 來加速訓練並減少記憶體使用量

local unigram = trainset.wordfreq:float()
local ncemodule = nn.NCEModule(inputsize, #trainset.ivocab, opt.k, unigram, opt.Z)

-- NCE requires {input, target} as inputs
lm = nn.Sequential()
   :add(nn.ParallelTable()
      :add(lm):add(nn.Identity()))
   :add(nn.ZipTable()) 

-- encapsulate stepmodule into a Sequencer
lm:add(nn.Sequencer(nn.MaskZero(ncemodule, 1)))

NCEModule 是一個更有效率的版本

nn.Sequential():add(nn.Linear(inputsize, #trainset.ivocab)):add(nn.LogSoftMax())

對於評估困惑度,此模式仍然導入 線性 + SoftMax。NCE 可用於在訓練過程中減少記憶體使用量(與上述數據比較)

連同 NCECriterionNCEModule 會導入演算法,如 [1] 所述。我不會深入探討演算法的詳細資料,因為其中牽涉到許多數學概念,這些概念在參照論文中有更詳盡的說明。其運作方式是在每個目標字(我們希望最大化其可能性)中,會從雜訊分布(通常一元分布)中抽樣 k 個字。

請記住,softmax 基本上是

                  exp(x[i])
y[i] = ---------------------------------                             (9)
       exp(x[1])+exp(x[2])+...+exp(x[n])

其中 x[i] 是輸出 線性 層的第 i 個輸出。上述分母會造成瓶頸,因為需要針對每個輸出 x[i] 計算 線性。對於字彙量為 n=797470 的字彙量而言,這將非常昂貴。NCE 在訓練過程中以常數 Z 取代等式 9 的分母,來解決這個問題

         exp(x[i])
y[i] = ------------                                                  (10)
             Z

然 780;這樣並非訓 244;時實際發生的狀況,因為透過上述反向傳播無法為 j~=ij 不等於 i)其中 x[j] 產生梯度。請注意,透過等式 9 反向傳播將為 Linear 的所有輸出 x 產生梯度(也就是所有 i)。等式 10 的另一個問題是沒有任何機制驅動 exp(x[1])+exp(x[2])+...+exp(x[n]) 逼近 Z。NCE 所採取的方式是將此問題制定成等式中可以包含 k 個雜訊範例,以確保某些(最多 k 個)負範例(也就是 x[j] 其中 j)取得梯度,以及等式 9 的分母逼近等式 10 的分母。這 k 個雜訊範例取樣自雜訊分配,也就是單詞分配。輸出層 Linear 只需要為目標和雜訊採樣的字詞運算,這也是效率提升之處。

上述 unigram 變數是一個大小為 793470 的張量,其中每個元 032;都是公司中相應字詞的頻率。使用類似 torch.multinomial 之類的函數針對這麼大的分配進行採樣,在訓 244;期間可能會造成瓶頸。因此我們在 torch.AliasMultinomial 中實作了一個更有效率的版本。後面的多項式採樣器需要比前 773;更長時間的設定時間,不過因為單詞分配是固定的,這並不成問題。

NCE 使用雜訊範例逼近正則化項 Z,其中輸出分配為 exp(x[i])/Z, 780; x[i]Linear 對字詞 i 的輸出。對於 NCE 嘗試逼近的 Softmax, Z 是所有字詞 i'exp(x[i']) 的總和。對於 NCE, Z 通常固定為 Z=1。我們的初步實驗發現,將 Z 設定為 Z=N*mean(exp(x[i]))(其中 N 是字詞數量, 780; mean 則是針對一小批字詞範例 i 估算)可以獲得更理想的 080;果,但這是因為我們未能適當初始化輸出層參數。

NCE 論文的一項值得留意的面向(儘管還有許多)是,它們常常遺漏說明這個參數初始化的重要性。設定 Z=1 只有在 NCEModule.bias 初始化為 bias[i] = -log(N) 時,才確實可行。儘管論文中未提及這一點(我聯繫上其中一位作者後得知),但 [2] 的作者使用的是這個值。

採樣每個時間步驟和每個批次列 k 個雜訊樣本,表示 NCEModule 在內部需要像 torch.baddbmm 那樣,使用某些內容來計算 output。參考文獻 [2] 實作一個較快的版本,其中僅繪製一次雜訊樣本,並在整個批次中使用(但仍適用於每個時間步驟)。這使程式碼快了一些,因為可以更有效率地使用 torch.addmm,而不是 torch.baddbmm。參考文獻 [2] 中描述的這個較快的 NCE 版本,是 NCEModule 的預設實作。採樣每個批次列可以透過 NCEModule.rownoise=true 開啟。

訓練及評估指令碼

本文提供的實驗使用三個腳本:兩個用於訓練(你只需要使用一個),一個用於評量。訓練腳本僅在使用的 GPU 量上有所不同。這兩個都對訓練語料庫訓練語言模型,並對驗證語料庫執行早期停止。評量腳本用於衡量訓練模型對測試語料庫的困惑度,或用於產生句子。

單一 GPU 訓練腳本

我們透過 noise-contrastive-estimate.lua 腳本來提供單一 GPU 的訓練腳本。在 12GB NVIDIA Titan X 上執行以下內容,在 321 個世代後,測試語料庫的困惑度應達到 65.6

th examples/noise-contrastive-estimate.lua --cuda --device 2 --startlr 1 --saturate 300 --cutoff 10 --progress --uniform 0.1 --seqlen 50 --batchsize 128 --trainsize 400000 --validsize 40000 --hiddensize '{250,250}' --k 400 --minlr 0.001 --momentum 0.9

產生的模型看起來會像這樣

nn.Serial @ nn.Sequential {
  [input -> (1) -> (2) -> (3) -> output]
  (1): nn.ParallelTable {
    input
      |`-> (1): nn.Sequential {
      |      [input -> (1) -> (2) -> (3) -> (4) -> output]
      |      (1): nn.LookupTableMaskZero
      |      (2): nn.SeqLSTM
      |      (3): nn.SeqLSTM
      |      (4): nn.SplitTable
      |    }
      |`-> (2): nn.Identity
       ... -> output
  }
  (2): nn.ZipTable
  (3): nn.Sequencer @ nn.Recursor @ nn.MaskZero @ nn.NCEModule(250 -> 793471)
}

若要減少約三分之一的記憶體用量,你可以設定動能為 0。

評量腳本

評量腳本可以用於衡量測試語料庫的困惑度或採樣獨立句子。若要評量一個已儲存的模型,你可以使用 evaluate-rnnlm.lua 腳本

th scripts/evaluate-rnnlm.lua --xplogpath /home/nicholas14/save/rnnlm/gbw:uranus:1466538423:1.t7 --cuda

你可以將 /home/nicholas14/save/rnnlm/gbw:uranus:1466538423:1.t7 替換成你自己的訓練模型的途徑。鑑於它必須使用效率較低的 Linear + SoftMax,因此評估測試語料庫的時間可能會很長,從而導致批次大小非常小(以免使用過多記憶體)。

評量腳本也可以用於從語言模型中產生樣本

th scripts/evaluate-rnnlm.lua --xplogpath /home/nicholas14/save/rnnlm/gbw:uranus:1466790001:1.t7 --cuda --nsample 200 --temperature 0.7

--nsample 旗標指定要抽樣的詞彙數。傳入語言模型的第一個詞彙是句首標籤 (<S>)。當出現句尾標籤 (</S>) 時,模型的隱藏狀態會設定為零,如此一來,每個句子都是獨立抽樣的。--temperature 旗標可以降低以使抽樣更確定。

<S> There were a number of players in the starting lineup during the season and in recent weeks , in recent years , some fans have been frustrated . </S> 
<S> WASHINGTON ( Reuters ) - The government plans to cut greenhouse gases by as much as 12 % on the global economy , a new report said . </S> 
<S> One of the most important things about the day was that the two companies had just been guilty of the same nature . </S> 
<S> " It has been as much a bit of a public service as a public organisation . </S> 
<S> In a nutshell , it 's not only the fate of the economy . </S> 
<S> It was last modified at 23.31 GMT on Saturday 22 December 2009 . </S> 
<S> He told the newspaper the prosecution had been treating the small boy as " a young man who was playing for a while . </S> 
<S> " We are astounded that our employees are not made aware of the risks and risks they are pursuing during this period of time , " he said . </S> 
<S> " I had a right to come up with the idea . </S>

多 GPU 訓練腳本

如同在前一節所觀察到的,訓練只有 250 個隱藏單元的 2 層 LSTM 不會產生最佳生成的樣本。模型需要的容量遠超過 12GB GPU 可以容納的容量。對於參數和它們的梯度,一個 4x2048 LSTM 模型需要以下事項:

這不包括不同模組需要的中間緩衝區(在 NCE 區段 中有說明)。因此,解決方案當然是將模型分配到更多 GPU。multigpu-nce-rnnlm.lua 腳本提供用於在四個 GPU 上訓練語言模型。

它使用 GPU(我們對 nn 做出了貢獻)裝飾模組以將所有運算和記憶體託管在指定的裝置上。GPU 模組不會在不同 GPU 裝置上平行執行核心。但它確實允許我們在裝置上分配大型模型。

對於我們的 LM,輸入的詞彙嵌入(即 LookupTableMaskZero)和輸出層(即 NCEModule)佔用了大部分記憶體。將第一個分配出去相當容易

lm = nn.Sequential()
lm:add(nn.Convert())

-- input layer (i.e. word embedding space)
local concat = nn.Concat(3)
for device=1,2 do
   local inputsize = device == 1 and torch.floor(opt.inputsize/2) or torch.ceil(opt.inputsize/2)
   local lookup = nn.LookupTableMaskZero(#trainset.ivocab, inputsize)
   lookup.maxnormout = -1 -- prevent weird maxnormout behaviour
   concat:add(nn.GPU(lookup, device):cuda()) -- input is seqlen x batchsize
end

基本上,嵌入空間會分成兩個表格。對於一個 2048 單位嵌入空間,一半,也就是 1024 個單位,位於兩個裝置中的每個裝置。我們使用 Concatforward 之後將它們串接回去。

對於隱藏層(即 SeqLSTM),我們只要將它們分配到輸入層使用的裝置。隱藏層使用極少的記憶體(每個大約 1GB),所以它們不是問題。我們將它們放在與輸入層相同的裝置,因為輸出層使用比較多的記憶體(作為緩衝區)。

local inputsize = opt.inputsize
for i,hiddensize in ipairs(opt.hiddensize) do
   local rnn = nn.SeqLSTM(inputsize, hiddensize)
   rnn.maskzero = true
   local device = i <= #opt.hiddensize/2 and 1 or 2
   lm:add(nn.GPU(rnn, device):cuda())
   if opt.dropout > 0 then
      lm:add(nn.GPU(nn.Dropout(opt.dropout), device):cuda())
   end
   inputsize = hiddensize
end

lm:add(nn.GPU(nn.SplitTable(1), 3):cuda())

由於 NCEModule 無法像 LookupTableMaskZero 那樣容易平行處理,因此要取得分配更為困難。我們的解決方法是在 weight 上提供一個簡單的 multicuda() 方法,用於在不同裝置上分配 gradWeight。這是透過將 weight 張量與我們自己的 : torch.MultiCudaTensor 交換,實現的。Lua 沒有嚴格的型別檢查系統,因此,您可以透過建置具有相同方法的 torch.class 表格,並製作一個假的張量。為了省時,目前的 MultiCudaTensor 版本僅支援 NCEModule 所需的操作。這種方法的優點在於它只需要對 NCEModule 進行最小的變更,並保持向後相容性,而不需冗餘程式碼或過度重構。

-- output layer
local unigram = trainset.wordfreq:float()
ncemodule = nn.NCEModule(inputsize, #trainset.ivocab, opt.k, unigram, opt.Z)
ncemodule:reset() -- initializes bias to get approx. Z = 1
ncemodule.batchnoise = not opt.rownoise
-- distribute weight, gradWeight and momentum on devices 3 and 4
ncemodule:multicuda(3,4) 

-- NCE requires {input, target} as inputs
lm = nn.Sequential()
   :add(nn.ParallelTable()
      :add(lm):add(nn.Identity()))
   :add(nn.ZipTable()) 

-- encapsulate stepmodule into a Sequencer
local masked = nn.MaskZero(ncemodule, 1):cuda()
lm:add(nn.GPU(nn.Sequencer(masked), 3, opt.device):cuda())

若要重現 [2] 中的結果,請執行下列操作:

th examples/multigpu-nce-rnnlm.lua --startlr 0.7 --saturate 300 --minlr 0.001 --cutoff 10 --progress --uniform 0.1 --seqlen 50 --batchsize 128 --trainsize 400000 --validsize 40000 --hiddensize '{2048,2048,2048,2048}' --dropout 0.2 --k 400 --Z 1 --momentum -1

與這篇論文有下列顯著差異:

  • 使用 梯度範數裁剪 [3](其 cutoff 範數為 10),以抵銷梯度爆炸和消失。
  • 使用適應性學習率行程表(這點並未在論文中說明)。我們以學習率 0.7(他們也是從此處開始)線性遞減,使它在 300 個時期後達到 0.001。
  • 我們使用 k=400 範例,而他們使用 k=100。為何如此?我沒有看到什麼重大速度下降,為何不呢?
  • 我們使用 seqlen=50 的序列長度,進行截斷 BPTT。他們使用 100(同樣的,論文中未提及)。資料集中句子的平均長度為 27,因此 50 已綽綽有餘。

我們也如同他們一樣,在 LSTM 層之間使用 dropout=0.2。這是產生的模型外觀:

nn.Serial @ nn.Sequential {
  [input -> (1) -> (2) -> (3) -> output]
  (1): nn.ParallelTable {
    input
      |`-> (1): nn.Sequential {
      |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> (6) -> (7) -> (8) -> (9) -> (10) -> (11) -> (12) -> output]
      |      (1): nn.Convert
      |      (2): nn.GPU(2) @ nn.Concat {
      |        input
      |          |`-> (1): nn.GPU(1) @ nn.LookupTableMaskZero
      |          |`-> (2): nn.GPU(2) @ nn.LookupTableMaskZero
      |           ... -> output
      |      }
      |      (3): nn.GPU(2) @ nn.Dropout(0.2, busy)
      |      (4): nn.GPU(1) @ nn.SeqLSTM
      |      (5): nn.GPU(1) @ nn.Dropout(0.2, busy)
      |      (6): nn.GPU(1) @ nn.SeqLSTM
      |      (7): nn.GPU(1) @ nn.Dropout(0.2, busy)
      |      (8): nn.GPU(2) @ nn.SeqLSTM
      |      (9): nn.GPU(2) @ nn.Dropout(0.2, busy)
      |      (10): nn.GPU(2) @ nn.SeqLSTM
      |      (11): nn.GPU(2) @ nn.Dropout(0.2, busy)
      |      (12): nn.GPU(3) @ nn.SplitTable
      |    }
      |`-> (2): nn.Identity
       ... -> output
  }
  (2): nn.ZipTable
  (3): nn.GPU(3) @ nn.Sequencer @ nn.Recursor @ nn.MaskZero @ nn.NCEModule(2048 -> 793471)
}

結果

在具有 2048 隱藏單元的 4 層 LSTM 中,[1] 在 GBW 測試集中獲得 43.2 perplexity。在驗證集的一個子集中提早停止(在 100 個訓練時期,其中 1 個時期為 128 個序列 x 40 萬個單詞/序列)之後,我們的模型能夠達到 40.61 perplexity。

這個模型在 4x12GB NVIDIA Titan X GPU 上執行。訓練需要約 40GB 的記憶體,分配橫跨 4 個 GPU 裝置,並需經過 2-3 星期的訓練。正如原始論文所示,我們不使用動能,因為它提供的益處較小,且需要 1/2 倍以上的記憶體。

訓練速度約為每秒 3800 個單詞。

學習曲線

以下是上述 4x2048 LSTM 模型的學習曲線。圖形繪製了模型的 NCE 訓練和驗證錯誤,這是錯誤輸出,但是 NCEModule。測試集錯誤沒有繪製,因為對於任何一個世代,測試集推論都需要大約 3 小時,因為測試集推論中使用了具有 batchsize=1Linear + SoftMax

可以看出,大部分的學習是發生在第一個世代。儘管如此,訓練和驗證錯誤仍然持續在訓練過程中減少。

下圖比較了小型 2x250 LSTM(沒有中斷)以及大型 4x2048 LSTM(有中斷)的驗證學習曲線(同樣地,是 NCE 錯誤)。

我覺得這張圖表令我印象深刻的是,容量較高的模型如何快速擊敗容量較低的模型。這清楚地證明了在最佳化大型語言模型時,容量的重要性。

產生句子

以下是獨立從 4 層 LSTM 採樣而來的句子,其中具有 溫度 或 0.7

<S> The first , for a lot of reasons , is the " Asian Glory " : an American military outpost in the middle of an Iranian desert . </S>
<S> But the first new stage of the project will be a new <UNK> tunnel linking the new terminal with the new terminal at the airport . </S>
<S> The White House said Bush would also sign a memorandum of understanding with Iraq , which will allow the Americans to take part in the poll . </S>
<S> The folks who have campaigned for his nomination know that he is in a fight for survival . </S>
<S> The three survivors , including a woman whose name was withheld and not authorized to speak , were buried Saturday in a makeshift cemetery in the town and seven people were killed in the town of Eldoret , which lies around a dozen miles ( 40 kilometers ) southwest of Kathmandu . </S>
<S> The art of the garden was created by pouring water over a small brick wall and revealing that an older , more polished design was leading to the creation of a new house in the district . </S>
<S> She added : " The club has not made any concession to the club 's fans and was not notified of the fact they had reached an agreement with the club . </S>
<S> The Times has learnt that the former officer who fired the fatal shots must have known about the fatal carnage . </S>
<S> Obama supporters say they 're worried about the impact of the healthcare and energy policies of Congress . </S>
<S> Not to mention the painful changes to the way that women are treated in the workplace . </S>
<S> The dollar stood at 14.38 yen ( <UNK> ) and <UNK> Swiss francs ( <UNK> ) . </S>
<S> The current , the more intractable <UNK> , the <UNK> and the <UNK> about a lot of priorities . </S>
<S> The job , which could possibly be completed in 2011 , needs to be approved in a new compact between the two companies . </S>
<S> " The most important thing for me is to get back to the top , " he said . </S>
<S> It was a one-year ban and the right to a penalty . </S>
<S> The government of president Michelle Bachelet has promised to maintain a " strong and systematic " military presence in key areas and to tackle any issue of violence , including kidnappings . </S>
<S> The six were scheduled to return to Washington on Wednesday . </S>
<S> " It 's a ... mistake , " he said . </S>
<S> The government 's offensive against the rebels and insurgents has been criticized by the United Nations and UN agencies . </S>
<S> " Our <UNK> model is not much different from many of its competitors , " said Richard Bangs , CEO of the National Center for Science in the Public Interest in Chicago . </S>
<S> He is now a large part of a group of young people who are spending less time studying and work in the city . </S>
<S> He said he was confident that while he and his wife would have been comfortable working with him , he would be able to get them to do so . </S>
<S> The summer 's financial meltdown is the worst in decades . </S>
<S> It was a good night for Stuart Broad , who took the ball to Ravi Bopara at short leg to leave England on 88 for five at lunch . </S>
<S> And even for those who worked for them , almost everything was at risk . </S>
<S> The new strategy is all part of a stepped-up war against Taliban and al-Qaida militants in northwest Pakistan . </S>
<S> The governor 's office says the proposal is based on a vision of an outsider in the town who wants to preserve the state 's image . </S>
<S> " The fact that there is no evidence to support the claim made by the government is entirely convincing and that Dr Mohamed will have to be detained for a further two years , " he said . </S>
<S> The country 's tiny nuclear power plants were the first to use nuclear technology , and the first such reactors in the world . </S>
<S> " What is also important about this is that we can go back to the way we worked and work and fight , " he says . </S>
<S> And while he has been the star of " The Wire " and " The Office , " Mr. Murphy has been a careful , intelligent , engaging competitor for years . </S>
<S> On our return to the water , we found a large abandoned house . </S>
<S> The national average for a gallon of regular gas was $ 5.99 for the week ending Jan . </S>
<S> The vote was a rare early start for the contest , which was held after a partial recount in 26 percent of the vote . </S>
<S> The first one was a show of force by a few , but the second was an attempt to show that the country was serious about peace . </S>
<S> It was a little more than half an hour after the first reports of a shooting . </S>
<S> The central bank is expected to cut interest rates further by purchasing more than $ 100 billion of commercial paper and Treasuries this week . </S>
<S> Easy , it 's said , to have a child with autism . </S>
<S> He said : " I am very disappointed with the outcome because the board has not committed itself . </S>
<S> " There is a great deal of tension between us , " said Mr C. </S>
<S> The odds that the Fed will keep its benchmark interest rate unchanged are at least half as much as they were at the end of 2008 . </S>
<S> For them , investors have come to see that : a ) the government will maintain a stake in banks and ( 2 ) the threat of financial regulation and supervision ; and ( 3 ) it will not be able to raise enough capital from the private sector to support the economy . </S>
<S> The court heard he had been drinking and drank alcohol at the time of the attack . </S>
<S> " The whole thing is quite a bit more intense . </S>
<S> This is a very important project and one that we are working closely with . </S>
<S> " We are confident that in this economy and in the current economy , we will continue to grow , " said John Lipsky , who chaired the IMF 's board of governors for several weeks . </S>
<S> The researchers said they found no differences among how men drank and whether they were obese . </S>
<S> Even though there are many brands that have low voice and no connection to the Internet , the iPhone is a great deal for consumers . </S>
<S> The £ 7m project is a new project for the city of Milton Keynes and aims to launch a new challenge for the British Government . </S>
<S> But he was not without sympathy for his father . </S>

語法看起來相當合理,特別是將其與從 單一 GPU 2x250 LSTM 所獲得的先前結果相比較時。然而,在某些情況下,語意,亦即字詞的意義,並不好。例如,對我來說至少,這個句子

<S> Easy , it 's said , to have a child with autism . </S>

如果將 容易 取代為 不容易 會更說得通。

另一方面,像這樣的句子展示了良好的語意

<S> The government of president Michelle Bachelet has promised to maintain a " strong and systematic " military presence in key areas and to tackle any issue of violence , including kidnappings . </S>`.

蜜雪兒·巴舍雷 確實是一位智利總統。在她早年的生活中,她也曾 被軍人綁架,因此在綁架問題上立場強硬似乎有其道理。

以下是某些奇怪語意的範例

<S> Even though there are many brands that have low voice and no connection to the Internet , the iPhone is a great deal for consumers . </S>

關於 加載聲音 的第一部分對我來說毫無意義。而且我無法理解 許多與網際網路無關的品牌 如何與 對於消費者來說,iPhone 是個很棒的交易 有關。但是,當然地,所有這些句子都是獨立產生的,因此語言模型需要學習隨時產生意義。這是很困難的,因為即將產生的句子沒有語境。

無論如何,我對於這些結果感到相當滿意,因為它們肯定是我到目前為止所看過最自然的合成句子。

未來工作

我目前正在製作根據一個月 reddit.com 資料為基礎的語言模型資料集。每個序列基本上是一個 reddit 提交,包含一個 標題自訂文字 (或 網址)、評分作者 和一個 留言 串。這些序列遠比構成 GBW 資料集的句子長 (平均 205 個標記),平均有 26 個標記)。目前仍在訓練中,但為了引起你的興趣,以下是一個生成資料的範例(為清楚起見,增加了縮排與換行)

<SUBMISSION>
   <AUTHOR> http://www.reddit.com/u/[deleted] </AUTHOR> 
   <SCORE> 0 </SCORE> 
   <TITLE> 
      [ WP ] You take a picture of a big bang . 
      You discover an alien that lives in the center of the planet in an unknown way . 
      You can say " what the fuck is that ? " 
   </TITLE> 
   <COMMENTS>
      <CoMMeNT> 
         <ScoRE> 2 </ScoRE> 
         <AuTHoR> http://www.reddit.com/u/Nev2k </AuTHoR>
         <BodY> 
            I have a question . 
            When i was younger , my parents had a house that had a living room in it . 
            One that was only a small portion of an entire level . 
            This was a month before i got my money . 
            If i was living in a house with a " legacy " i would make some mistakes . 
            When i was a child , i did n't know how to do shit about the house . 
            My parents got me into my own house and i never found a place to live . 
            So i decide to go to college . 
            I was so freaked out , i didnt have the drive to see them . 
            I never had a job , i was n't going anywhere . 
            I was so happy . 
            I knew i was going to be there . 
            I gave myself a job and my parents came . 
            That 's when i realized that i was in the wrong . 
            So i started to go . 
            I couldnt decide how long i wanted to live in this country . 
            I was so excited about the future . 
            I had a job . 
            I saved my money . 
            I did n't have a job . 
            I went to a highschool in a small town . 
            I had a job . 
            A job . 
            I did n't know what to do . 
            I was terrified of losing my job . 
            So i borrowed my $ 1000 in an hour . 
            I could n't afford to pay my rent . 
            I was so low on money . 
            I had my parents and i got into a free college . 
            I got in touch with my parents . 
            All of my friends were dead . 
            I was still with my family for a week . 
            I became a good parent . 
            I was a good choice . 
            When i got on my HSS i was going to go to my parents ' house . 
            I started to judge my parents . 
            I had a minor problem . 
            My parents . 
            I was so fucking bad . 
            My sister had a voice that was very loud . 
            I 'm sure my cousins were in a place where i could just hear my voice . 
            I felt like i was supposed to be angry . 
            I was so angry . 
            To cope with this . 
            My dad and i were both on break and i felt so alone . 
            I got unconscious and my mum left . 
            When I got to college , i was back in school . 
            I was a good kid . 
            I was happy . 
            And I told myself I was ready . 
            I told my parents . 
            They always talked about how they were going to be a good mom , and that I was going to be ready for that . 
            They always wanted to help me . 
            I did n't know what to do . 
            I had to . 
            I tried to go back to my dad , because I knew a lot about my mom . 
            I loved her . 
            I cared about her . 
            We cared for our family . 
            The time together was my only relationship . 
            I loved my heart . 
            And I hated my mother . 
            I chose it . 
            I cried . I cried . I cried . I cried . I cried . I cried . I cried . 
            The tears were gone . 
            I cried . I cried . I cried . I cried . I cried . I cried . I cried . I cried . I cried . I cried . 
            I do n't know how to do it . 
            I do n't know how to deal with it . 
            I ca n't feel my emotions . 
            I ca n't get out of bed . 
            I ca n't sleep . 
            I ca n't tell my friends . 
            I just need to leave . 
            I want to leave . 
            I hate myself . 
            I hate feeling like I 'm being selfish . 
            I feel like I 'm not good enough anymore . 
            I need to find a new job . 
            I hate that I have to get my shit together . 
            I love my job . 
            I 'm having a hard time .
            Why do I need to get a job ? 
            I have no job . 
            I have n't been feeling good lately . 
            I feel like I 'm going to be so much worse in the long run . 
            I feel so alone . 
            I ca n't believe I 'm so sad about going through my entire life . 
         </BodY> 
         <AuTHoR> http://www.reddit.com/u/Scarbarella </AuTHoR> 
      </CoMMeNT> 
   </COMMENTS> 
   <SUBREDDIT> http://www.reddit.com/r/offmychest </SUBREDDIT> 
   <SELFTEXT> 
      I do n't know what to do anymore . 
      I feel like I 'm going to die and I 'm going to be sick because I have no more friends . 
      I do n't know what to do about my depression and I do n't know where to go from here . 
      I do n't know how I do because I know I 'm scared of being alone . 
      Any advice would be appreciated . 
      Love . 
   </SELFTEXT> 
</SUBMISSION> 

這個特殊的範例有點令人沮喪,但那可能是 offmychest 子版塊的性質。以開啟的 <SUBMISSION> 標記為條件,儘管這個生成的序列並非完美,但卻難以置信地有人的感覺。讀完留言後,我感覺自己讀了一篇真的(有點精神分裂的)人寫的故事。模擬人類創造力的能力是我之所以如此有興趣使用 reddit 資料進行語言模型的原因之一。

以下是一個比較不令人沮喪的範例,與 命運 這個電玩遊戲有關

<SUBMISSION>
   <SUBREDDIT> http://www.reddit.com/r/DestinyTheGame </SUBREDDIT> 
   <TITLE> 
      Does anyone have a link to the Destiny Grimoire that I can use to get my Xbox 360 to play ? 
   </TITLE> 
   <COMMENTS> 
      <CoMMeNT> 
         <AuTHoR> http://www.reddit.com/u/CursedSun </AuTHoR> 
         <BodY> 
            I 'd love to have a weekly reset . 
         </BodY> 
         <ScoRE> 1 </ScoRE> 
      </CoMMeNT> 
   </COMMENTS> 
   <SCORE> 0 </SCORE> 
   <SELFTEXT> 
      I have a few friends who are willing to help me out . 
      If I get to the point where I 'm not going to have to go through all the weekly raids , I 'll have to " complete " the raid . 
      I 'm doing the Weekly strike and then doing the Weekly ( and hopefully also the Weekly ) on Monday . 
      I 'm not planning to get the chest , but I am getting my first exotic that I just got done from my first Crota raid . 
      I 'm not sure how well it would work for the Nightfall and Weekly , but I do n't want to loose my progress . 
      I 'd love to get some other people to help me , and I 'm open to all suggestions . 
      I have a lot of experience with this stuff , so I figured it 's a good idea to know if I 'm getting the right answer . 
      I 'm truly sorry for the inconvenience . 
   </SELFTEXT> 
   <AUTHOR> <OOV> </AUTHOR> 
</SUBMISSION>

對於不熟悉這電玩的讀者,像 傳說每週重設團隊副本災變任務異域武器或裝甲克羅塔團隊副本 等術語可能很奇怪。但這些都是遊戲詞彙的一部分。

這個特定的模型(具有中斷的 4x1572 LSTM)僅透過 50 個時間步驟反向傳播。我希望看到的結果是 留言 實際上回答了 標題自訂文字 提出的問題。這是一個非常困難的語意問題,我希望 Reddit 資料集有助於解決。更多內容將在下一篇 Torch 網誌文章中提供。

參考文獻

  1. A Mnih, YW Teh一個快速的簡單演算法,用於訓練神經機率語言模型
  2. B Zoph, A Vaswani, J May, K Knight用於大 RNN 字彙表的簡單、快速、雜訊對比估計
  3. R Pascanu, T Mikolov, Y Bengio訓練遞迴神經網路的難處
  4. S Hochreiter, J Schmidhuber長短期記憶
  5. A Graves, A Mohamed, G Hinton使用遞迴神經網路進行語音辨識
  6. K Greff, RK Srivastava, J KoutníkLSTM:一個搜尋空間的冒險之旅
  7. C Chelba, T Mikolov, M Schuster, Q Ge, T Brants, P Koehn, T Robinson一個十億字基準,用於衡量統計語言模型的進展
  8. A Graves使用遞迴神經網絡產生序列,table 1
Disqus 提供技術支援的留言