<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【深度學(xué)習(xí)】PyTorch:Bi-LSTM的文本生成

          共 15918字,需瀏覽 32分鐘

           ·

          2020-12-17 08:13

          作者 | Fernando López?

          編譯 | VK?

          來源 | Towards Data Science

          ?

          “寫作沒有規(guī)定。有時(shí)它來得容易而且完美;有時(shí)就像在巖石上鉆孔,然后用炸藥把它炸開一樣?!薄?dú)W內(nèi)斯特·海明威

          ?

          本文的目的是解釋如何通過實(shí)現(xiàn)基于LSTMs的強(qiáng)大體系結(jié)構(gòu)來構(gòu)建文本生成的端到端模型。

          博客分為以下幾個(gè)部分:

          • 介紹

          • 文本預(yù)處理

          • 序列生成

          • 模型體系結(jié)構(gòu)

          • 訓(xùn)練階段

          • 文本生成

          完整代碼請?jiān)L問:https://github.com/FernandoLpz/Text-Generation-BiLSTM-PyTorch

          介紹

          多年來,人們提出了各種各樣的建議來建模自然語言,但這是怎么回事呢?“建模自然語言”指的是什么?我們可以認(rèn)為“建模自然語言”是指對構(gòu)成語言的語義和語法進(jìn)行推理,本質(zhì)上是這樣,但它更進(jìn)一步。

          目前,自然語言處理(NLP)領(lǐng)域通過不同的方法和技術(shù)處理不同的任務(wù),即對語言進(jìn)行推理、理解和建模。

          自然語言處理(NLP)領(lǐng)域在過去的十年里發(fā)展非常迅速。許多模型都從不同的角度提出了解決不同NLP任務(wù)的方法。同樣,最受歡迎的模型中的共同點(diǎn)是實(shí)施基于深度學(xué)習(xí)的模型。

          如前所述,NLP領(lǐng)域解決了大量的問題,特別是在本博客中,我們將通過使用基于深度學(xué)習(xí)的模型來解決文本生成問題,例如循環(huán)神經(jīng)網(wǎng)絡(luò)LSTM和Bi-LSTM。同樣,我們將使用當(dāng)今最復(fù)雜的框架之一來開發(fā)深度學(xué)習(xí)模型,特別是我們將使用PyTorch的LSTMCell類來開發(fā)。

          問題陳述

          給定一個(gè)文本,神經(jīng)網(wǎng)絡(luò)將通過字符序列來學(xué)習(xí)給定文本的語義和句法。隨后,將隨機(jī)抽取一系列字符,并預(yù)測下一個(gè)字符。

          文本預(yù)處理

          首先,我們需要一個(gè)我們要處理的文本。有不同的資源可以在純文本中找到不同的文本,我建議你看看Gutenberg項(xiàng)目(https://www.gutenberg.org/).。

          在這個(gè)例子中,我將使用George Bird Grinnell的《Jack Among the Indians》這本書,你可以在這里找到:https://www.gutenberg.org/cache/epub/46205/pg46205.txt。所以,第一章的第一行是:

          The?train?rushed?down?the?hill,?with?a?long?shrieking?whistle,?and?then?began?to?go?more?and?more?slowly.?Thomas?had?brushed?Jack?off?and?thanked?him?for?the?coin?that?he?put?in?his?hand,?and?with?the?bag?in?one?hand?and?the?stool?in?the?other?now?went?out?onto?the?platform?and?down?the?steps,?Jack?closely?following.

          如你所見,文本包含大寫、小寫、換行符、標(biāo)點(diǎn)符號等。建議你將文本調(diào)整為一種形式,使我們能夠以更好的方式處理它,這主要降低我們將要開發(fā)的模型的復(fù)雜性。

          我們要把每個(gè)字符轉(zhuǎn)換成它的小寫形式。另外,建議將文本作為一個(gè)字符列表來處理,也就是說,我們將使用一個(gè)字符列表,而不是使用“字符串”。將文本作為字符序列的目的是為了更好地處理生成的序列,這些序列將提供給模型(我們將在下一節(jié)中詳細(xì)介紹)。

          代碼段1-預(yù)處理

          def?read_dataset(file):
          ????letters?=?['a','b','c','d','e','f','g','h','i','j','k','l','m',
          ???????????????'n','o','p','q','r','s','t','u','v','w','x','y','z','?']
          ????
          ????#?打開原始文件
          ????with?open(file,?'r')?as?f:
          ????????raw_text?=?f.readlines()
          ????????
          ????#?將每一行轉(zhuǎn)換為小寫
          ????raw_text?=?[line.lower()?for?line?in?raw_text]
          ????
          ????#?創(chuàng)建一個(gè)包含整個(gè)文本的字符串
          ????text_string?=?''
          ????for?line?in?raw_text:
          ????????text_string?+=?line.strip()
          ????????
          ?????#?。創(chuàng)建一個(gè)字符數(shù)組
          ????text?=?list()
          ????for?char?in?text_string:
          ????????text.append(char)
          ????????
          ?????#?去掉所有的符號,只保留字母
          ????text?=?[char?for?char?in?text?if?char?in?letters]
          ?
          ????return?text

          如我們所見,在第2行我們定義了要使用的字符,所有其他符號都將被丟棄,我們只保留“空白”符號。

          在第6行和第10行中,我們讀取原始文件并將其轉(zhuǎn)換為小寫形式。

          在第14行和第19行的循環(huán)中,我們創(chuàng)建了一個(gè)代表整本書的字符串,并生成了一個(gè)字符列表。在第23行中,我們通過只保留第2行定義的字母來過濾文本列表。

          因此,一旦文本被加載和預(yù)處理,例如:

          text?=?"The?train?rushed?down?the?hill."

          可以得到這樣的字符列表:

          text?=?['t','h','e','?','t','r','a','i','n','?','r','u','s','h','e','d','?','d','o','w','n',
          '?','t','h','e','?','h','i','l','l']

          我們已經(jīng)有了全文作為字符列表。眾所周知,我們不能將原始字符直接引入神經(jīng)網(wǎng)絡(luò),我們需要一個(gè)數(shù)值表示,因此,我們需要將每個(gè)字符轉(zhuǎn)換成一個(gè)數(shù)值表示。為此,我們將創(chuàng)建一個(gè)字典來幫助我們保存等價(jià)的“字符索引”和“索引字符”。

          代碼段2-字典創(chuàng)建

          def?create_dictionary(text):
          ?
          ??char_to_idx?=?dict()
          ??idx_to_char?=?dict()
          ??
          ??idx?=?0
          ??for?char?in?text:
          ????if?char?not?in?char_to_idx.keys():
          ??????
          ??????#?構(gòu)建字典
          ??????char_to_idx[char]?=?idx
          ??????idx_to_char[idx]?=?char
          ??????idx?+=?1
          ????
          ?return?char_to_idx,?idx_to_char

          我們可以注意到,在第11行和第12行創(chuàng)建了“char-index”和index-char”字典。

          到目前為止,我們已經(jīng)演示了如何加載文本并以字符列表的形式保存它,我們還創(chuàng)建了兩個(gè)字典來幫助我們對每個(gè)字符進(jìn)行編碼和解碼。

          序列生成

          序列生成的方式完全取決于我們要實(shí)現(xiàn)的模型類型。如前所述,我們將使用LSTM類型的循環(huán)神經(jīng)網(wǎng)絡(luò),它按順序接收數(shù)據(jù)(時(shí)間步長)。

          對于我們的模型,我們需要形成一個(gè)給定長度的序列,我們稱之為“窗口”,其中要預(yù)測的字符(目標(biāo))將是窗口旁邊的字符。每個(gè)序列將由窗口中包含的字符組成。要形成一個(gè)序列,窗口一次向右得到一個(gè)字符。要預(yù)測的字符始終是窗口后面的字符。我們可以在圖中清楚地看到這個(gè)過程。

          在本例中,窗口的大小為4,這意味著它將包含4個(gè)字符。目標(biāo)是作者在窗口圖像右邊的第一個(gè)字符

          到目前為止,我們已經(jīng)看到了如何以一種簡單的方式生成字符序列。現(xiàn)在我們需要將每個(gè)字符轉(zhuǎn)換為其各自的數(shù)字格式,為此,我們將使用預(yù)處理階段生成的字典。這個(gè)過程可以在下圖可視化。

          很好,現(xiàn)在我們知道了如何使用一個(gè)一次滑動(dòng)一個(gè)字符的窗口來生成字符序列,以及如何將字符轉(zhuǎn)換為數(shù)字格式,下面的代碼片段顯示了所描述的過程。

          代碼段3-序列生成

          def?build_sequences(text,?char_to_idx,?window):
          ????x?=?list()
          ????y?=?list()
          ????
          ????for?i?in?range(len(text)):
          ????????try:
          ????????????#?從文本中獲取字符窗口
          ????????????#?將其轉(zhuǎn)換為其idx表示
          ????????????sequence?=?text[i:i+window]
          ????????????sequence?=?[char_to_idx[char]?for?char?in?sequence]
          ???
          ????????????#得到target
          ????????????#?轉(zhuǎn)換到它的idx表示
          ????????????target?=?text[i+window]
          ????????????target?=?char_to_idx[target]
          ????????????
          ????????????#?保存sequence和target
          ????????????x.append(sequence)
          ????????????y.append(target)
          ????????????
          ?????????except:
          ????????????pass
          ????????
          ????x?=?np.array(x)
          ????y?=?np.array(y)
          ????
          ????return?x,?y

          太棒了,現(xiàn)在我們知道如何預(yù)處理原始文本,如何將其轉(zhuǎn)換為字符列表,以及如何以數(shù)字格式生成序列?,F(xiàn)在我們來看看最有趣的部分,模型架構(gòu)。

          模型架構(gòu)

          正如你已經(jīng)在這篇博客的標(biāo)題中讀到的,我們將使用Bi-LSTM循環(huán)神經(jīng)網(wǎng)絡(luò)和標(biāo)準(zhǔn)LSTM。本質(zhì)上,我們使用這種類型的神經(jīng)網(wǎng)絡(luò),因?yàn)樗谔幚眄樞驍?shù)據(jù)時(shí)具有巨大的潛力,例如文本類型的數(shù)據(jù)。同樣,也有大量的文章提到使用基于循環(huán)神經(jīng)網(wǎng)絡(luò)的體系結(jié)構(gòu)(例如RNN、LSTM、GRU、Bi-LSTM等)進(jìn)行文本建模,特別是文本生成[1,2]。

          ?

          所提出的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)由一個(gè)嵌入層、一個(gè)雙LSTM層和一個(gè)LSTM層組成。緊接著,后一個(gè)LSTM連接到一個(gè)線性層。

          ?

          方法

          該方法包括將每個(gè)字符序列傳遞到嵌入層,這將為構(gòu)成序列的每個(gè)元素生成向量形式的表示,因此我們將形成一個(gè)嵌入字符序列。隨后,嵌入字符序列的每個(gè)元素將被傳遞到Bi-LSTM層。隨后,將生成構(gòu)成雙LSTM(前向LSTM和后向LSTM)的LSTM的每個(gè)輸出的串聯(lián)。緊接著,每個(gè)前向+后向串聯(lián)的向量將被傳遞到LSTM層,最后一個(gè)隱藏狀態(tài)將從該層傳遞給線性層。最后一個(gè)線性層將有一個(gè)Softmax函數(shù)作為激活函數(shù),以表示每個(gè)字符的概率。下圖顯示了所描述的方法。

          到目前為止,我們已經(jīng)解釋了文本生成模型的體系結(jié)構(gòu)以及實(shí)現(xiàn)的方法。現(xiàn)在我們需要知道如何使用PyTorch框架來實(shí)現(xiàn)所有這些,但是首先,我想簡單地解釋一下bilstm和LSTM是如何協(xié)同工作的,以便稍后了解如何在代碼中實(shí)現(xiàn)這一點(diǎn),那么讓我們看看bilstm網(wǎng)絡(luò)是如何工作的。

          Bi-LSTM和LSTM

          標(biāo)準(zhǔn)LSTM和Bi-LSTM的關(guān)鍵區(qū)別在于Bi-LSTM由2個(gè)LSTM組成,通常稱為“正向LSTM”和“反向LSTM”。基本上,正向LSTM以原始順序接收序列,而反向LSTM接收序列。隨后,根據(jù)要執(zhí)行的操作,兩個(gè)LSTMs的每個(gè)時(shí)間步的每個(gè)隱藏狀態(tài)都可以連接起來,或者只對兩個(gè)LSTMs的最后一個(gè)狀態(tài)進(jìn)行操作。在所提出的模型中,我們建議在每個(gè)時(shí)間步加入兩個(gè)隱藏狀態(tài)。

          很好,現(xiàn)在我們了解了Bi-LSTM和LSTM之間的關(guān)鍵區(qū)別?;氐轿覀冋陂_發(fā)的示例中,下圖表示每個(gè)字符序列在通過模型時(shí)的演變。

          太好了,一旦Bi-LSTM和LSTM之間的交互都很清楚,讓我們看看我們是如何在代碼中僅使用PyTorch框架中的LSTMcell來實(shí)現(xiàn)的。

          那么,首先讓我們了解一下如何構(gòu)造TextGenerator類的構(gòu)造函數(shù),讓我們看看下面的代碼片段:

          代碼段4-文本生成器類的構(gòu)造函數(shù)

          class?TextGenerator(nn.ModuleList):
          ?
          ????def?__init__(self,?args,?vocab_size):
          ????????super(TextGenerator,?self).__init__()
          ????
          ????????self.batch_size?=?args.batch_size
          ????????self.hidden_dim?=?args.hidden_dim
          ????????self.input_size?=?vocab_size
          ????????self.num_classes?=?vocab_size
          ?self.sequence_len?=?args.window
          ????
          ????????#?Dropout
          ????????self.dropout?=?nn.Dropout(0.25)
          ????
          ????????#?Embedding?層
          ????????self.embedding?=?nn.Embedding(self.input_size,?self.hidden_dim,?padding_idx=0)
          ????
          ????????#?Bi-LSTM
          ????????#?正向和反向
          ????????self.lstm_cell_forward?=?nn.LSTMCell(self.hidden_dim,?self.hidden_dim)
          ????????self.lstm_cell_backward?=?nn.LSTMCell(self.hidden_dim,?self.hidden_dim)
          ????
          ????????#?LSTM?層
          ????????self.lstm_cell?=?nn.LSTMCell(self.hidden_dim?*?2,?self.hidden_dim?*?2)
          ????
          ????????#?Linear?層
          ????????self.linear?=?nn.Linear(self.hidden_dim?*?2,?self.num_classes)

          如我們所見,從第6行到第10行,我們定義了用于初始化神經(jīng)網(wǎng)絡(luò)每一層的參數(shù)。需要指出的是,input_size等于詞匯表的大?。ㄒ簿褪钦f,我們的字典在預(yù)處理過程中生成的元素的數(shù)量)。同樣,要預(yù)測的類的數(shù)量也與詞匯表的大小相同,序列長度表示窗口的大小。

          另一方面,在第20行和第21行中,我們定義了組成Bi-LSTM的兩個(gè)「LSTMCells」 (向前和向后)。在第24行中,我們定義了LSTMCell,它將與「Bi-LSTM」的輸出一起饋送。值得一提的是,隱藏狀態(tài)的大小是Bi-LSTM的兩倍,這是因?yàn)锽i-LSTM的輸出是串聯(lián)的。稍后在第27行定義線性層,稍后將由softmax函數(shù)過濾。

          一旦定義了構(gòu)造函數(shù),我們需要為每個(gè)LSTM創(chuàng)建包含單元狀態(tài)和隱藏狀態(tài)的張量。因此,我們按如下方式進(jìn)行:

          代碼片段5-權(quán)重初始化

          #?Bi-LSTM
          #?hs?=?[batch_size?x?hidden_size]
          #?cs?=?[batch_size?x?hidden_size]
          hs_forward?=?torch.zeros(x.size(0),?self.hidden_dim)
          cs_forward?=?torch.zeros(x.size(0),?self.hidden_dim)
          hs_backward?=?torch.zeros(x.size(0),?self.hidden_dim)
          cs_backward?=?torch.zeros(x.size(0),?self.hidden_dim)

          #?LSTM
          #?hs?=?[batch_size?x?(hidden_size?*?2)]
          #?cs?=?[batch_size?x?(hidden_size?*?2)]
          hs_lstm?=?torch.zeros(x.size(0),?self.hidden_dim?*?2)
          cs_lstm?=?torch.zeros(x.size(0),?self.hidden_dim?*?2)

          #?權(quán)重初始化
          torch.nn.init.kaiming_normal_(hs_forward)
          torch.nn.init.kaiming_normal_(cs_forward)
          torch.nn.init.kaiming_normal_(hs_backward)
          torch.nn.init.kaiming_normal_(cs_backward)
          torch.nn.init.kaiming_normal_(hs_lstm)
          torch.nn.init.kaiming_normal_(cs_lstm)

          一旦定義了包含隱藏狀態(tài)和單元狀態(tài)的張量,是時(shí)候展示整個(gè)體系結(jié)構(gòu)的組裝是如何完成的.

          首先,讓我們看一下下面的代碼片段:

          代碼片段6-BiLSTM+LSTM+線性層

          #?從?idx?到?embedding
          out?=?self.embedding(x)

          #?為LSTM準(zhǔn)備shape
          out?=?out.view(self.sequence_len,?x.size(0),?-1)

          forward?=?[]
          backward?=?[]

          #?解開Bi-LSTM
          #?正向
          for?i?in?range(self.sequence_len):
          ??hs_forward,?cs_forward?=?self.lstm_cell_forward(out[i],?(hs_forward,?cs_forward))
          ??hs_forward?=?self.dropout(hs_forward)
          ??cs_forward?=?self.dropout(cs_forward)
          ??forward.append(hs_forward)
          ??
          ?#?反向
          for?i?in?reversed(range(self.sequence_len)):
          ??hs_backward,?cs_backward?=?self.lstm_cell_backward(out[i],?(hs_backward,?cs_backward))
          ??hs_backward?=?self.dropout(hs_backward)
          ??cs_backward?=?self.dropout(cs_backward)
          ??backward.append(hs_backward)
          ??
          ?#?LSTM
          for?fwd,?bwd?in?zip(forward,?backward):
          ??input_tensor?=?torch.cat((fwd,?bwd),?1)
          ??hs_lstm,?cs_lstm?=?self.lstm_cell(input_tensor,?(hs_lstm,?cs_lstm))

          #?最后一個(gè)隱藏狀態(tài)通過線性層
          out?=?self.linear(hs_lstm)

          為了更好地理解,我們將用一些定義的值來解釋程序,這樣我們就可以理解每個(gè)張量是如何從一個(gè)層傳遞到另一個(gè)層的。所以假設(shè)我們有:

          batch_size?=?64
          hidden_size?=?128
          sequence_len?=?100
          num_classes?=?27

          所以x輸入張量將有一個(gè)形狀:

          #?torch.Size([batch_size,?sequence_len])
          x?:?torch.Size([64,?100])

          然后,在第2行中,x張量通過嵌入層傳遞,因此輸出將具有一個(gè)大?。?/p>

          #?torch.Size([batch_size,?sequence_len,?hidden_size])
          x_embedded?:?torch.Size([64,?100,?128])

          需要注意的是,在第5行中,我們正在reshape ?x_embedded ?張量。這是因?yàn)槲覀冃枰獙⑿蛄虚L度作為第一維,本質(zhì)上是因?yàn)樵贐i-LSTM中,我們將迭代每個(gè)序列,因此重塑后的張量將具有一個(gè)形狀:

          #?torch.Size([sequence_len,?batch_size,?hidden_size])
          x_embedded_reshaped?:?torch.Size([100,?64,?128])

          緊接著,在第7行和第8行定義了forwardbackward 列表。在那里我們將存儲Bi-LSTM的隱藏狀態(tài)。

          所以是時(shí)候給Bi-LSTM輸入數(shù)據(jù)了。首先,在第12行中,我們在向前LSTM上迭代,我們還保存每個(gè)時(shí)間步的隱藏狀態(tài)(hs_forward)。在第19行中,我們迭代向后的LSTM,同時(shí)保存每個(gè)時(shí)間步的隱藏狀態(tài)(hs_backward)。你可以注意到循環(huán)是以相同的順序執(zhí)行的,不同之處在于它是以相反的形式讀取的。每個(gè)隱藏狀態(tài)將具有以下形狀:

          #?hs_forward?:?torch.Size([batch_size,?hidden_size])
          hs_forward?:?torch.Size([64,?128])

          #?hs_backward?:?torch.Size([batch_size,?hidden_size])
          hs_backward:?torch.Size([64,?128])

          很好,現(xiàn)在讓我們看看如何為最新的LSTM層提供數(shù)據(jù)。為此,我們使用forwardbackward 列表。在第26行中,我們遍歷與第27行級聯(lián)的forwardbackward 對應(yīng)的每個(gè)隱藏狀態(tài)。需要注意的是,通過連接兩個(gè)隱藏狀態(tài),張量的維數(shù)將增加2倍,即張量將具有以下形狀:

          #?input_tesor?:?torch.Size([bathc_size,?hidden_size?*?2])
          input_tensor?:?torch.Size([64,?256])

          最后,LSTM將返回大小為的隱藏狀態(tài):

          #?last_hidden_state:?torch.Size([batch_size,?num_classes])
          last_hidden_state:?torch.Size([64,?27])

          最后,LSTM的最后一個(gè)隱藏狀態(tài)將通過一個(gè)線性層,如第31行所示。因此,完整的forward函數(shù)顯示在下面的代碼片段中:

          代碼片段7-正向函數(shù)

          def?forward(self,?x):
          ????
          ????#?Bi-LSTM
          ????#?hs?=?[batch_size?x?hidden_size]
          ????#?cs?=?[batch_size?x?hidden_size]
          ????hs_forward?=?torch.zeros(x.size(0),?self.hidden_dim)
          ????cs_forward?=?torch.zeros(x.size(0),?self.hidden_dim)
          ????hs_backward?=?torch.zeros(x.size(0),?self.hidden_dim)
          ????cs_backward?=?torch.zeros(x.size(0),?self.hidden_dim)
          ????
          ????#?LSTM
          ????#?hs?=?[batch_size?x?(hidden_size?*?2)]
          ????#?cs?=?[batch_size?x?(hidden_size?*?2)]
          ????hs_lstm?=?torch.zeros(x.size(0),?self.hidden_dim?*?2)
          ????cs_lstm?=?torch.zeros(x.size(0),?self.hidden_dim?*?2)
          ????
          ????#?權(quán)重初始化
          ????torch.nn.init.kaiming_normal_(hs_forward)
          ????torch.nn.init.kaiming_normal_(cs_forward)
          ????torch.nn.init.kaiming_normal_(hs_backward)
          ????torch.nn.init.kaiming_normal_(cs_backward)
          ????torch.nn.init.kaiming_normal_(hs_lstm)
          ????torch.nn.init.kaiming_normal_(cs_lstm)
          ????
          ????#?從?idx?到?embedding
          ????out?=?self.embedding(x)
          ????
          ????#?為LSTM準(zhǔn)備shape
          ????out?=?out.view(self.sequence_len,?x.size(0),?-1)
          ????
          ????forward?=?[]
          ????backward?=?[]
          ????
          ????#?解開Bi-LSTM
          ????#?正向
          ????for?i?in?range(self.sequence_len):
          ????????hs_forward,?cs_forward?=?self.lstm_cell_forward(out[i],?(hs_forward,?cs_forward))
          ????????hs_forward?=?self.dropout(hs_forward)
          ????????cs_forward?=?self.dropout(cs_forward)
          ????????forward.append(hs_forward)
          ????????
          ?????#?反向
          ????for?i?in?reversed(range(self.sequence_len)):
          ????????hs_backward,?cs_backward?=?self.lstm_cell_backward(out[i],?(hs_backward,?cs_backward))
          ????????hs_backward?=?self.dropout(hs_backward)
          ????????cs_backward?=?self.dropout(cs_backward)
          ????????backward.append(hs_backward)
          ????????
          ?????#?LSTM
          ????for?fwd,?bwd?in?zip(forward,?backward):
          ????????input_tensor?=?torch.cat((fwd,?bwd),?1)
          ????????hs_lstm,?cs_lstm?=?self.lstm_cell(input_tensor,?(hs_lstm,?cs_lstm))
          ????????
          ?????#?最后一個(gè)隱藏狀態(tài)通過線性層
          ????out?=?self.linear(hs_lstm)
          ????
          ????return?out

          到目前為止,我們已經(jīng)知道如何使用PyTorch中的LSTMCell來組裝神經(jīng)網(wǎng)絡(luò)。現(xiàn)在是時(shí)候看看我們?nèi)绾芜M(jìn)行訓(xùn)練階段了,所以讓我們繼續(xù)下一節(jié)。

          訓(xùn)練階段

          太好了,我們來訓(xùn)練了。為了執(zhí)行訓(xùn)練,我們需要初始化模型和優(yōu)化器,稍后我們需要為每個(gè)epoch 和每個(gè)mini-batch,所以讓我們開始吧!

          代碼片段8-訓(xùn)練階段

          def?train(self,?args):
          ??
          ??#?模型初始化
          ??model?=?TextGenerator(args,?self.vocab_size)
          ??
          ??#?優(yōu)化器初始化
          ??optimizer?=?optim.RMSprop(model.parameters(),?lr=self.learning_rate)
          ??
          ??#?定義batch數(shù)
          ??num_batches?=?int(len(self.sequences)?/?self.batch_size)
          ??
          ??#?訓(xùn)練模型
          ??model.train()
          ??
          ??#?訓(xùn)練階段
          ??for?epoch?in?range(self.num_epochs):
          ????
          ????#?Mini?batches
          ????for?i?in?range(num_batches):
          ??????
          ??????#?Batch?定義
          ??????try:
          ????????x_batch?=?self.sequences[i?*?self.batch_size?:?(i?+?1)?*?self.batch_size]
          ????????y_batch?=?self.targets[i?*?self.batch_size?:?(i?+?1)?*?self.batch_size]
          ??????except:
          ????????x_batch?=?self.sequences[i?*?self.batch_size?:]
          ????????y_batch?=?self.targets[i?*?self.batch_size?:]
          ????????
          ??????#?轉(zhuǎn)換?numpy?array?為?torch?tensors
          ??????x?=?torch.from_numpy(x_batch).type(torch.LongTensor)
          ??????y?=?torch.from_numpy(y_batch).type(torch.LongTensor)
          ??????
          ??????#?輸入數(shù)據(jù)
          ??????y_pred?=?model(x)
          ??????
          ??????#?loss計(jì)算
          ??????loss?=?F.cross_entropy(y_pred,?y.squeeze())
          ??????
          ??????#?清除梯度
          ??????optimizer.zero_grad()
          ??????
          ??????#?反向傳播
          ??????loss.backward()
          ??????
          ??????#?更新參數(shù)
          ??????optimizer.step()
          ??????
          ??????print("Epoch:?%d?,??loss:?%.5f?"?%?(epoch,?loss.item()))

          一旦模型被訓(xùn)練,我們將需要保存神經(jīng)網(wǎng)絡(luò)的權(quán)重,以便以后使用它們來生成文本。為此我們有兩種選擇,第一種是定義一個(gè)固定的時(shí)間段,然后保存權(quán)重,第二個(gè)是確定一個(gè)停止函數(shù),以獲得模型的最佳版本。在這個(gè)特殊情況下,我們將選擇第一個(gè)選項(xiàng)。在對模型進(jìn)行一定次數(shù)的訓(xùn)練后,我們將權(quán)重保存如下:

          代碼段9-權(quán)重保存

          #?保存權(quán)重
          torch.save(model.state_dict(),?'weights/textGenerator_model.pt')

          到目前為止,我們已經(jīng)看到了如何訓(xùn)練文本生成器和如何保存權(quán)重,現(xiàn)在我們將進(jìn)入這個(gè)博客的最后一部分,文本生成!

          文本生成

          我們已經(jīng)到了博客的最后一部分,文本生成。為此,我們需要做兩件事:第一件事是加載訓(xùn)練好的權(quán)重,第二件事是從序列集合中隨機(jī)抽取一個(gè)樣本作為模式,開始生成下一個(gè)字符。下面我們來看看下面的代碼片段:

          代碼片段10-文本生成器

          def?generator(model,?sequences,?idx_to_char,?n_chars):
          ??
          ??#?評估模式
          ??model.eval()
          ??
          ??#?定義softmax函數(shù)
          ??softmax?=?nn.Softmax(dim=1)
          ??
          ??#?從序列集合中隨機(jī)選取索引
          ??start?=?np.random.randint(0,?len(sequences)-1)
          ??
          ??#?給定隨機(jī)的idx來定義模式
          ??pattern?=?sequences[start]
          ??
          ??#?利用字典,它輸出了Pattern
          ??print("\nPattern:?\n")
          ??print(''.join([idx_to_char[value]?for?value?in?pattern]),?"\"")
          ??
          ??#?在full_prediction中,我們將保存完整的預(yù)測
          ??full_prediction?=?pattern.copy()
          ??
          ??#?預(yù)測開始,它將被預(yù)測為一個(gè)給定的字符長度
          ??for?i?in?range(n_chars):
          ????
          ????#?轉(zhuǎn)換為tensor
          ????pattern?=?torch.from_numpy(pattern).type(torch.LongTensor)
          ????pattern?=?pattern.view(1,-1)
          ????
          ????#?預(yù)測
          ????prediction?=?model(pattern)
          ????#?將softmax函數(shù)應(yīng)用于預(yù)測張量
          ????prediction?=?softmax(prediction)
          ????
          ????#?預(yù)測張量被轉(zhuǎn)換成一個(gè)numpy數(shù)組
          ????prediction?=?prediction.squeeze().detach().numpy()
          ????#?取概率最大的idx
          ????arg_max?=?np.argmax(prediction)
          ????
          ????#?將當(dāng)前張量轉(zhuǎn)換為numpy數(shù)組
          ????pattern?=?pattern.squeeze().detach().numpy()
          ????#?窗口向右1個(gè)字符
          ????pattern?=?pattern[1:]
          ????#?新pattern是由“舊”pattern+預(yù)測的字符組成的
          ????pattern?=?np.append(pattern,?arg_max)
          ????
          ????#?保存完整的預(yù)測
          ????full_prediction?=?np.append(full_prediction,?arg_max)
          ????
          ??print("Prediction:?\n")
          ??print(''.join([idx_to_char[value]?for?value?in?full_prediction]),?"\"")

          因此,通過在以下特征下訓(xùn)練模型:

          window?:?100
          epochs?:?50
          hidden_dim?:?128
          batch_size?:?128
          learning_rate?:?0.001

          我們可以生成以下內(nèi)容:

          Seed:
          one?of?the?prairie?swellswhich?gave?a?little?wider?view?than?most?of?them?jack?saw?quite?close?to?the
          Prediction:
          one?of?the?prairie?swellswhich?gave?a?little?wider?view?than?most?of?them?jack?saw?quite?close?to?the?wnd?banngessejang?boffff?we?outheaedd?we?band?r?hes?tller?a?reacarof?t?t?alethe?ngothered?uhe?th?wengaco?ack?fof?ace?ca??e?s?alee?bin??cacotee?tharss?th?band?fofoutod?we?we?ins?sange?trre?anca?y?w?farer?we?sewigalfetwher?d?e??we?n?s?shed?pack?wngaingh?tthe?we?the?we?javes?t?supun?f?the?har?man?bllle?s?ng?ou???y?anghe?ond?we?nd?ba?a??she?t?t?anthendwe?wn?me?anom?ly?tceaig?t?i?isesw?arawns?t?d?ks?wao?thalac?tharr?jad??d?anongive?where?the?awe?w?we?he?is?ma?mie?cack?seat?sesant?sns?t?imes?hethof?riges?we?he?d?ooushe?he?hang?out?f?t?thu?inong?bll?llveco?we?see?s?the?he?haa?is?s?igg?merin?ishe?d?t?san?wack?owhe?o?or?th?we?sbe?se?we?we?inange?t?ts?wan?br?seyomanthe?harntho?thengn??th?me?ny?we?ke?in?acor?offff??of?wan??s?arghe?we?t?angorro?the?wand?be?thing?a?sth?t?tha?alelllll?willllsse?of?s?wed?w?brstougof?bage?orore?he?anthesww?were?ofawe?ce?qur?the?he?sbaing?tthe?bytondece?nd?t?llllifsffo?acke?o?t?in?ir?me?hedlff?scewant?pi?t?bri?pi?owasem?the?awh?thorathas?th?we?hed?ofainginictoplid?we?me

          正如我們看到的,生成的文本可能沒有任何意義,但是有一些單詞和短語似乎形成了一個(gè)想法,例如:

          we,?band,?pack,?the,?man,?where,?he,?hang,?out,?be,?thing,?me,?were

          恭喜,我們已經(jīng)到了博客的結(jié)尾!

          結(jié)論

          在本博客中,我們展示了如何使用PyTorch的LSTMCell建立一個(gè)用于文本生成的端到端模型,并實(shí)現(xiàn)了基于循環(huán)神經(jīng)網(wǎng)絡(luò)LSTM和Bi-LSTM的體系結(jié)構(gòu)。

          值得注意的是,建議的文本生成模型可以通過不同的方式進(jìn)行改進(jìn)。一些建議的想法是增加要訓(xùn)練的文本語料庫的大小,增加epoch以及每個(gè)LSTM的隱藏層大小。另一方面,我們可以考慮一個(gè)基于卷積LSTM的有趣的架構(gòu)。

          參考引用

          [1] LSTM vs. GRU vs. Bidirectional RNN for script generation(https://arxiv.org/pdf/1908.04332.pdf)

          [2] The survey: Text generation models in deep learning(https://www.sciencedirect.com/science/article/pii/S1319157820303360)


          往期精彩回顧





          獲取本站知識星球優(yōu)惠券,復(fù)制鏈接直接打開:

          https://t.zsxq.com/qFiUFMV

          本站qq群704220115。

          加入微信群請掃碼:

          瀏覽 63
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色录像视频大片 | 色欲色欲一区二区三区 | 三级片在线 天天 | 亚洲 精品一区二区三区 | 久久久成人性爱 |