到目前為止,我們的序列學(xué)習(xí)任務(wù)的工作示例是語言建模,我們的目標(biāo)是在給定序列中所有先前標(biāo)記的情況下預(yù)測下一個標(biāo)記。在這種情況下,我們只希望以左向上下文為條件,因此標(biāo)準(zhǔn) RNN 的單向鏈接似乎是合適的。然而,還有許多其他序列學(xué)習(xí)任務(wù)上下文,在這些上下文中,在向左和向右上下文的每個時間步調(diào)整預(yù)測是非常好的。例如,考慮詞性檢測。在評估與給定詞相關(guān)的詞性時,為什么我們不應(yīng)該考慮兩個方向的上下文?
另一項(xiàng)常見任務(wù)(通常在針對感興趣的實(shí)際任務(wù)微調(diào)模型之前用作預(yù)訓(xùn)練練習(xí))是屏蔽文本文檔中的隨機(jī)標(biāo)記,然后訓(xùn)練序列模型以預(yù)測缺失標(biāo)記的值。請注意,根據(jù)空白后面的內(nèi)容,缺失標(biāo)記的可能值會發(fā)生顯著變化:
我是___。
我___餓了。
我___餓了,我能吃半頭豬。
在第一句話中,“快樂”似乎是一個可能的候選者?!安弧焙汀胺浅!边@兩個詞在第二句中似乎說得通,但“不”與第三句似乎格格不入。
幸運(yùn)的是,一種簡單的技術(shù)可以將任何單向 RNN 轉(zhuǎn)換為雙向 RNN (Schuster 和 Paliwal,1997)。我們簡單地實(shí)現(xiàn)兩個單向 RNN 層,它們以相反的方向鏈接在一起并作用于相同的輸入(圖 10.4.1)。對于第一個 RNN 層,第一個輸入是x1最后的輸入是 xT,但是對于第二個 RNN 層,第一個輸入是 xT最后的輸入是x1. 為了產(chǎn)生這個雙向 RNN 層的輸出,我們只需將兩個底層單向 RNN 層的相應(yīng)輸出連接在一起。
圖 10.4.1雙向 RNN 的架構(gòu)。
正式地為任何時間步長t,我們考慮一個小批量輸入 Xt∈Rn×d(示例數(shù)量: n,每個示例中的輸入數(shù)量:d) 并令隱藏層激活函數(shù)為?. 在雙向架構(gòu)中,這個時間步長的前向和后向隱藏狀態(tài)是H→t∈Rn×h 和H←t∈Rn×h,分別在哪里h是隱藏單元的數(shù)量。前向和后向隱藏狀態(tài)更新如下:
(10.4.1)H→t=?(XtWxh(f)+H→t?1Whh(f)+bh(f)),H←t=?(XtWxh(b)+H←t+1Whh(b)+bh(b)),
權(quán)重在哪里 Wxh(f)∈Rd×h,Whh(f)∈Rh×h,Wxh(b)∈Rd×h,andWhh(b)∈Rh×h, 和偏見bh(f)∈R1×h和 bh(b)∈R1×h都是模型參數(shù)。
接下來,我們連接前向和后向隱藏狀態(tài) H→t和 H←t獲得隱藏狀態(tài) Ht∈Rn×2h送入輸出層。在具有多個隱藏層的深度雙向 RNN 中,此類信息作為輸入傳遞到下一個雙向?qū)?。最后,輸出層?jì)算輸出 Ot∈Rn×q(輸出數(shù)量: q):
(10.4.2)Ot=HtWhq+bq.
這里,權(quán)重矩陣 Whq∈R2h×q和偏見 bq∈R1×q是輸出層的模型參數(shù)。雖然從技術(shù)上講,兩個方向可以有不同數(shù)量的隱藏單元,但在實(shí)踐中很少做出這種設(shè)計(jì)選擇。我們現(xiàn)在演示雙向 RNN 的簡單實(shí)現(xiàn)。
import torch from torch import nn from d2l import torch as d2l
from mxnet import np, npx from mxnet.gluon import rnn from d2l import mxnet as d2l npx.set_np()
from jax import numpy as jnp from d2l import jax as d2l
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
import tensorflow as tf from d2l import tensorflow as d2l
10.4.1。從零開始實(shí)施
要從頭開始實(shí)現(xiàn)雙向 RNN,我們可以包含兩個RNNScratch具有獨(dú)立可學(xué)習(xí)參數(shù)的單向?qū)嵗?/p>
class BiRNNScratch(d2l.Module): def __init__(self, num_inputs, num_hiddens, sigma=0.01): super().__init__() self.save_hyperparameters() self.f_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.b_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.num_hiddens *= 2 # The output dimension will be doubled
class BiRNNScratch(d2l.Module): def __init__(self, num_inputs, num_hiddens, sigma=0.01): super().__init__() self.save_hyperparameters() self.f_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.b_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.num_hiddens *= 2 # The output dimension will be doubled
class BiRNNScratch(d2l.Module): num_inputs: int num_hiddens: int sigma: float = 0.01 def setup(self): self.f_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.b_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.num_hiddens *= 2 # The output dimension will be doubled
class BiRNNScratch(d2l.Module): def __init__(self, num_inputs, num_hiddens, sigma=0.01): super().__init__() self.save_hyperparameters() self.f_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.b_rnn = d2l.RNNScratch(num_inputs, num_hiddens, sigma) self.num_hiddens *= 2 # The output dimension will be doubled
前向和后向 RNN 的狀態(tài)分別更新,而這兩個 RNN 的輸出被連接起來。
@d2l.add_to_class(BiRNNScratch) def forward(self, inputs, Hs=None): f_H, b_H = Hs if Hs is not None else (None, None) f_outputs, f_H = self.f_rnn(inputs, f_H) b_outputs, b_H = self.b_rnn(reversed(inputs), b_H) outputs = [torch.cat((f, b), -1) for f, b in zip( f_outputs, reversed(b_outputs))] return outputs, (f_H, b_H)
@d2l.add_to_class(BiRNNScratch) def forward(self, inputs, Hs=None): f_H, b_H = Hs if Hs is not None else (None, None) f_outputs, f_H = self.f_rnn(inputs, f_H) b_outputs, b_H = self.b_rnn(reversed(inputs), b_H) outputs = [np.concatenate((f, b), -1) for f, b in zip( f_outputs, reversed(b_outputs))] return outputs, (f_H, b_H)
@d2l.add_to_class(BiRNNScratch) def forward(self, inputs, Hs=None): f_H, b_H = Hs if Hs is not None else (None, None) f_outputs, f_H = self.f_rnn(inputs, f_H) b_outputs, b_H = self.b_rnn(reversed(inputs), b_H) outputs = [jnp.concatenate((f, b), -1) for f, b in zip( f_outputs, reversed(b_outputs))] return outputs, (f_H, b_H)
@d2l.add_to_class(BiRNNScratch) def forward(self, inputs, Hs=None): f_H, b_H = Hs if Hs is not None else (None, None) f_outputs, f_H = self.f_rnn(inputs, f_H) b_outputs, b_H = self.b_rnn(reversed(inputs), b_H) outputs = [tf.concat((f, b), -1) for f, b in zip( f_outputs, reversed(b_outputs))] return outputs, (f_H, b_H)
10.4.2。簡潔的實(shí)現(xiàn)
使用高級 API,我們可以更簡潔地實(shí)現(xiàn)雙向 RNN。這里我們以一個 GRU 模型為例。
class BiGRU(d2l.RNN): def __init__(self, num_inputs, num_hiddens): d2l.Module.__init__(self) self.save_hyperparameters() self.rnn = nn.GRU(num_inputs, num_hiddens, bidirectional=True) self.num_hiddens *= 2
Using the high-level APIs, we can implement bidirectional RNNs more concisely. Here we take a GRU model as an example.
class BiGRU(d2l.RNN): def __init__(self, num_inputs, num_hiddens): d2l.Module.__init__(self) self.save_hyperparameters() self.rnn = rnn.GRU(num_hiddens, bidirectional=True) self.num_hiddens *= 2
Flax API does not offer RNN layers and hence there is no notion of any bidirectional argument. One needs to manually reverse the inputs as shown in the scratch implementation, if a bidirectional layer is needed.
Using the high-level APIs, we can implement bidirectional RNNs more concisely. Here we take a GRU model as an example.
10.4.3。概括
在雙向 RNN 中,每個時間步的隱藏狀態(tài)同時由當(dāng)前時間步之前和之后的數(shù)據(jù)確定。雙向 RNN 主要用于序列編碼和給定雙向上下文的觀察估計(jì)。由于長梯度鏈,雙向 RNN 的訓(xùn)練成本非常高。
10.4.4。練習(xí)
如果不同方向使用不同數(shù)量的隱藏單元,形狀將如何Ht改變?
設(shè)計(jì)具有多個隱藏層的雙向 RNN。
多義現(xiàn)象在自然語言中很常見。例如,“銀行”一詞在“我去銀行存款”和“我去銀行坐下”的語境中有不同的含義。我們?nèi)绾卧O(shè)計(jì)一個神經(jīng)網(wǎng)絡(luò)模型,以便在給定上下文序列和一個詞的情況下,返回該詞在上下文中的向量表示?哪種類型的神經(jīng)架構(gòu)更適合處理多義詞?
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4785瀏覽量
101250 -
pytorch
+關(guān)注
關(guān)注
2文章
808瀏覽量
13383
發(fā)布評論請先 登錄
相關(guān)推薦
基于遞歸神經(jīng)網(wǎng)絡(luò)和前饋神經(jīng)網(wǎng)絡(luò)的深度學(xué)習(xí)預(yù)測算法
PyTorch教程之循環(huán)神經(jīng)網(wǎng)絡(luò)
![<b class='flag-5'>PyTorch</b>教程之循環(huán)<b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
PyTorch教程之從零開始的遞歸神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)
![<b class='flag-5'>PyTorch</b>教程之從零開始的<b class='flag-5'>遞歸</b><b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>實(shí)現(xiàn)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
PyTorch教程9.6之遞歸神經(jīng)網(wǎng)絡(luò)的簡潔實(shí)現(xiàn)
![<b class='flag-5'>PyTorch</b>教程9.6之<b class='flag-5'>遞歸</b><b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>的簡潔實(shí)現(xiàn)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
PyTorch教程10.3之深度遞歸神經(jīng)網(wǎng)絡(luò)
![<b class='flag-5'>PyTorch</b>教程10.3之深度<b class='flag-5'>遞歸</b><b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
PyTorch教程10.4之雙向遞歸神經(jīng)網(wǎng)絡(luò)
![<b class='flag-5'>PyTorch</b>教程<b class='flag-5'>10.4</b>之<b class='flag-5'>雙向</b><b class='flag-5'>遞歸</b><b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
PyTorch教程16.2之情感分析:使用遞歸神經(jīng)網(wǎng)絡(luò)
![<b class='flag-5'>PyTorch</b>教程16.2之情感分析:使用<b class='flag-5'>遞歸</b><b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
使用PyTorch構(gòu)建神經(jīng)網(wǎng)絡(luò)
遞歸神經(jīng)網(wǎng)絡(luò)是循環(huán)神經(jīng)網(wǎng)絡(luò)嗎
遞歸神經(jīng)網(wǎng)絡(luò)與循環(huán)神經(jīng)網(wǎng)絡(luò)一樣嗎
rnn是遞歸神經(jīng)網(wǎng)絡(luò)還是循環(huán)神經(jīng)網(wǎng)絡(luò)
PyTorch神經(jīng)網(wǎng)絡(luò)模型構(gòu)建過程
遞歸神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)方法
遞歸神經(jīng)網(wǎng)絡(luò)和循環(huán)神經(jīng)網(wǎng)絡(luò)的模型結(jié)構(gòu)
![<b class='flag-5'>遞歸</b><b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>和循環(huán)<b class='flag-5'>神經(jīng)網(wǎng)絡(luò)</b>的模型結(jié)構(gòu)](https://file1.elecfans.com/web2/M00/FB/AF/wKgaomaOSc6ARPuXAABcNn5proc493.png)
評論