91精品国产综合久久四虎久久_国产成人午夜高潮毛片_99er视频精品免费观看_2020亚洲熟女在线观看_日本女优人体写真_国内黄色毛片_年轻的老师中文版在线_丰满女邻居做爰_久久久久久精品成人免费图片

用Apache MXNet構(gòu)建一個(gè)循環(huán)神經(jīng)網(wǎng)絡(luò)
開發(fā)一個(gè)RNN模型詳細(xì)的分步教程,用這個(gè)模型可以預(yù)測(cè)給定前置詞或字符時(shí)下一個(gè)單詞或字符是什么的概率。

之前的教程里,我們使用一種叫卷積神經(jīng)網(wǎng)絡(luò)(CNN)的深度學(xué)習(xí)技術(shù)來對(duì)文本圖片進(jìn)行分類。盡管CNN是一種強(qiáng)大的技術(shù),但它卻不能從序列型輸入(如語音和文字)中學(xué)習(xí)到時(shí)間性的特征。另外,CNN使用一個(gè)固定長(zhǎng)度的卷積核來學(xué)習(xí)空間的特征。這種類型的神經(jīng)網(wǎng)絡(luò)被叫做前饋神經(jīng)網(wǎng)絡(luò)。而循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)則可以學(xué)習(xí)到時(shí)間特征,而且比前饋神經(jīng)網(wǎng)絡(luò)有更廣泛的應(yīng)用。

在本教程里,我們將會(huì)開發(fā)一個(gè)循環(huán)神經(jīng)網(wǎng)絡(luò),用它來在給定一個(gè)前置詞或字符的情況下預(yù)測(cè)下一個(gè)詞或字符是什么的概率。幾乎我們所有人的智能手機(jī)上都有一個(gè)預(yù)測(cè)鍵盤,它能在我們快速輸入的時(shí)候建議下一個(gè)詞。循環(huán)神經(jīng)網(wǎng)絡(luò)就能讓我們構(gòu)建一個(gè)像SwiftKey這樣的非常先進(jìn)的預(yù)測(cè)系統(tǒng)。

我們會(huì)先講解一下前饋神經(jīng)網(wǎng)絡(luò)的一些局限。接著,我們會(huì)用前饋神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)一個(gè)基本的RNN模型。這個(gè)模型可以提供RNN工作機(jī)理的一個(gè)很好的展示。在這之后,我們會(huì)用MXNet的Gluon API提供的LSTM和GRU層來構(gòu)建一個(gè)非常強(qiáng)大的RNN模型。我們會(huì)用這個(gè)模型來生成文字。

我們將介紹下面這些內(nèi)容:

1. 前饋神經(jīng)網(wǎng)絡(luò)的局限;

2.RNN和LSTM背后的原理;

3.安裝帶有Gluon API的MXNet;

4.準(zhǔn)備用于訓(xùn)練的數(shù)據(jù)集;

5.使用前饋神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)一個(gè)基本RNN;

6.使用Gluon API實(shí)現(xiàn)一個(gè)能自動(dòng)生成文本的RNN。

為了能更好地理解本教程,你需要對(duì)循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)、激活函數(shù)、梯度下降和反向傳播有基本的了解。你還應(yīng)該了解Python以及NumPy庫(kù)。

前饋神經(jīng)網(wǎng)絡(luò)和循環(huán)神經(jīng)網(wǎng)絡(luò)的比較

雖然諸如卷積神經(jīng)網(wǎng)絡(luò)這樣的前饋神經(jīng)網(wǎng)絡(luò)在語句和文本分類上有很好的準(zhǔn)確度,但它還是沒法記憶長(zhǎng)間距依賴(即隱藏層狀態(tài))。記憶可以被看成是隨著時(shí)間更新的時(shí)間性狀態(tài)。前饋神經(jīng)網(wǎng)絡(luò)沒法解釋上下文,因?yàn)樗鼪]有存儲(chǔ)時(shí)間性狀態(tài),也就是它沒有記憶。CNN只能從它卷積核范圍附近的鄰居(圖片或文字)那里學(xué)習(xí)到空間特征。圖1顯示了在相同的樣本數(shù)據(jù)里,卷積神經(jīng)網(wǎng)絡(luò)提取的空間特征和RNN提取的時(shí)間上下文。在CNN里,字符O和V的聯(lián)系被丟掉了。因?yàn)樗麄冊(cè)诓煌木矸e空間上下文里。而在RNN里,字符L、O、V、E之間的聯(lián)系被捕捉到了。

cnnvsrnn-eb3f50d3970d5b4db67e68cedaf4f60b

圖1 CNN的空間上下文和RNN的時(shí)間上下文。圖片由Manu Jeevan提供

前饋網(wǎng)絡(luò)無法理解上下文,因?yàn)樗鼪]有“記憶”狀態(tài)。所以它無法對(duì)于序列性或時(shí)間性的數(shù)據(jù)(像語言這種有明確順序的數(shù)據(jù))進(jìn)行建模。對(duì)前饋神經(jīng)網(wǎng)絡(luò)的一個(gè)抽象表示如圖2所示。

ffn_rnn-dca5cfb94412e9a36adafc9bef2f8b6f

圖2 前饋神經(jīng)網(wǎng)絡(luò)。圖片由Manu Jeevan提供

RNN則更靈活。它的神經(jīng)元接受加權(quán)過的輸入,同時(shí)生成加權(quán)后的輸出(WO)和加權(quán)后的隱藏層狀態(tài)(WH)。這個(gè)隱藏層狀態(tài)的作用就相當(dāng)于存儲(chǔ)上下文的記憶。如果RNN是表征一個(gè)人的電話通話,加權(quán)的輸出就是說出的那些詞,而加權(quán)的隱藏層狀態(tài)就是說話的上下文。

RNN背后的原理

在這一節(jié),我們將會(huì)通過用前饋神經(jīng)網(wǎng)絡(luò)構(gòu)建一個(gè)展開版的基本RNN,來解釋一下前饋神經(jīng)網(wǎng)絡(luò)和RNN的相似性。這個(gè)基本RNN只有一個(gè)簡(jiǎn)單的隱藏狀態(tài)矩陣(記憶),很容易理解和實(shí)現(xiàn)。

假定我們必須根據(jù)前3個(gè)字符去預(yù)測(cè)一串字符的第4個(gè)字符。為了這一目的,我們?cè)O(shè)計(jì)一個(gè)如圖3所示的簡(jiǎn)單的前饋神經(jīng)網(wǎng)絡(luò)。

unRolled_rnn-dc900dc366dcc7d5edb759c87b601348

圖3 展開的RNN。圖片由Manu Jeevan提供

這是一個(gè)簡(jiǎn)單的前饋神經(jīng)網(wǎng)絡(luò)。其中,權(quán)重WI(綠色箭頭)和WH(紅色箭頭)在一些層間共享同一組值。這是一個(gè)展開版的基本RNN,通常針對(duì)多對(duì)一的RNN應(yīng)用場(chǎng)景,因?yàn)樗怯枚鄺l輸入(本例中,前3個(gè)字符)來預(yù)測(cè)1個(gè)字符。這個(gè)RNN可以用下面的MXNet代碼來設(shè)計(jì)實(shí)現(xiàn)。

class UnRolledRNN_Model(Block):

? ? def __init__(self, vocab_size, num_embed, num_hidden, **kwargs):

? ? ? ? super(UnRolledRNN_Model, self).__init__(**kwargs)

? ? ? ? self.num_embed = num_embed

? ? ? ? self.vocab_size = vocab_size

? ? ? ? # use name_scope to give child Blocks appropriate names.

? ? ? ? # It also allows sharing Parameters between Blocks recursively.

? ? ? ? with self.name_scope():

? ? ? ? ? ? self.encoder = nn.Embedding(self.vocab_size, self.num_embed)

? ? ? ? ? ? self.dense1 = nn.Dense(num_hidden, activation=’relu’, flatten=True)

? ? ? ? ? ? self.dense2 = nn.Dense(num_hidden, activation=’relu’, flatten=True)

? ? ? ? ? ? self.dense3 = nn.Dense(vocab_size, flatten=True)

? ? def forward(self, inputs):

? ? ? ? emd = self.encoder(inputs)

? ? ? ? # print( emd.shape )

? ? ? ? # since the input is shape (batch_size, input(3 characters) )

? ? ? ? # we need to extract 0th, 1st, 2nd character from each batch

? ? ? ? character1 = emd[:, 0, :]

? ? ? ? character2 = emd[:, 1, :]

? ? ? ? character3 = emd[:, 2, :]

? ? ? ? # green arrow in diagram for character 1

? ? ? ? c1_hidden = self.dense1(character1)

? ? ? ? # green arrow in diagram for character 2

? ? ? ? c2_hidden = self.dense1(character2)

? ? ? ? # green arrow in diagram for character 3

? ? ? ? c3_hidden = self.dense1(character3)

? ? ? ? # yellow arrow in diagram

? ? ? ? c1_hidden_2 = self.dense2(c1_hidden)

? ? ? ? addition_result = F.add(c2_hidden, c1_hidden_2)? # Total c1 + c2

? ? ? ? addition_hidden = self.dense2(addition_result)? # the yellow arrow

? ? ? ? addition_result_2 = F.add(addition_hidden, c3_hidden)? # Total c1 + c2

? ? ? ? final_output = self.dense3(addition_result_2)

? ? ? ? return final_output

vocab_size = len(chars) + 1? # the vocabsize

num_embed = 30

num_hidden = 256

# model creation

simple_model = UnRolledRNN_Model(vocab_size, num_embed, num_hidden)

# model initilisation

simple_model.collect_params().initialize(mx.init.Xavier(), ctx=context)

trainer = gluon.Trainer(simple_model.collect_params(), ‘adam’)

loss = gluon.loss.SoftmaxCrossEntropyLoss()

基本上,這個(gè)神經(jīng)網(wǎng)絡(luò)是一個(gè)詞向量層(emb),后面接著3個(gè)全連接層:

1.全連接層1(帶有權(quán)重WI),它接收輸入;

2.全連接層2(帶有權(quán)重WH),中間層;

3.全連接層3(帶有權(quán)重WO),這一層產(chǎn)生輸出。其中也用了MXNet的數(shù)組加法來把產(chǎn)生的輸出與輸入進(jìn)行組合。

你可以查看這個(gè)博客來了解詞向量層以及它的功能。全連接層1、2和3學(xué)到了一套權(quán)重,并用它來根據(jù)給出的前3個(gè)字符預(yù)測(cè)第4個(gè)字符。

我們?cè)谶@個(gè)模型里使用二元交叉熵?fù)p失函數(shù)。這個(gè)模型可以被折疊回來,用圖4所示的方式來簡(jiǎn)潔地表示。

RNN-4bb69188f2e1d36e82a4a9aac5450b84

圖4 RNN的簡(jiǎn)潔版表示。圖片由Manu Jeevan提供

圖4可以幫助解釋模型背后的數(shù)學(xué)部分,如下所示:

hidden_state_at_t = tanh(WI x input + WH x previous_hidden_state + bias)

基本RNN有一些缺陷。例如,我們有下面一個(gè)很長(zhǎng)的文檔,其中包含下面這些句子:“我出生在法國(guó),在世界大戰(zhàn)期間”,“所以我會(huì)說法語”?;綬NN就無法理解“出生在法國(guó)”是“我會(huì)說法語”的上下文,因?yàn)檫@兩個(gè)部分在這個(gè)句子里相隔很遠(yuǎn)。

RNN也沒有能力(至少實(shí)際是如此)忘記語句之間的不相關(guān)的上下文?;綬NN對(duì)越靠近的隱藏狀態(tài)給予更多的重要性,因?yàn)樗鼰o法給指定的(t-k)步隱藏狀態(tài)更多的偏好。其中,t是當(dāng)前的時(shí)間步,而k是一個(gè)大于0的數(shù)。這是因?yàn)橛靡粋€(gè)很長(zhǎng)的句子來訓(xùn)練基本RNN可能會(huì)在反向傳播時(shí)導(dǎo)致梯度消失(即梯度很小)或梯度爆炸(即梯度非常大)。

基本上,反向傳播算法會(huì)沿著神經(jīng)網(wǎng)絡(luò)計(jì)算圖的相反方向上對(duì)梯度進(jìn)行連乘。因此當(dāng)隱藏層矩陣的特征值非常小或非常大的時(shí)候,梯度就會(huì)變得非常不穩(wěn)定??梢栽?a >這里找到對(duì)RNN里這個(gè)問題的更詳細(xì)的解釋。

除了多對(duì)一的RNN之外,還有其他類型的RNN可以實(shí)現(xiàn)基于記憶的應(yīng)用,包括非常流行的序列到序列的RNN(見圖5)。在這個(gè)序列到序列的RNN模型里,序列的長(zhǎng)度是3,每個(gè)輸入都對(duì)應(yīng)到一個(gè)單獨(dú)的輸出。這可以幫助讓模型的訓(xùn)練更快,因?yàn)槲覀冊(cè)诿總€(gè)時(shí)間步都計(jì)算損失值(預(yù)測(cè)值和真實(shí)值之間的差距)。和上文只在最終計(jì)算一個(gè)損失不同,我們可以看到損失1、2等。因此訓(xùn)練模型時(shí)就能得到更好的反饋(反向傳播)。

loss-39488e1b648d9bb24918d65248055c16

圖5 序列到序列RNN。圖片由Manu Jeevan提供

長(zhǎng)短期記憶模型(LSTM

為了解決基本RNN的這些問題,兩位德國(guó)學(xué)者(Sepp Hochreiter和Juergen Schmidhuber)提出了長(zhǎng)短期記憶模型(LSTM, Long Short-Term Memory)。這是一個(gè)更復(fù)雜的模型,可以解決基本RNN的梯度消失或爆炸的問題??梢栽?a href="index.html">這里和這里看到對(duì)于基本版LSTM的非常漂亮的演示圖。從更高層面看,我們可以把一個(gè)LSTM單元看成一個(gè)小型的神經(jīng)網(wǎng)絡(luò)。它可以決定需要保留(記憶)前面哪些時(shí)間步里的信息量。

實(shí)現(xiàn)一個(gè)LSTM

現(xiàn)在讓我們?cè)囍鴺?gòu)建一個(gè)簡(jiǎn)單的字符預(yù)測(cè)器。

準(zhǔn)備環(huán)境

如果你能使用AWS云,那么可以省掉很多安裝的工作。只要使用Amazon SageMaker的預(yù)配置的深度學(xué)習(xí)環(huán)境即可。如果能這樣做,你可以直接跳過下面的步驟1到5。

如果你使用一個(gè)Conda的環(huán)境,記得要先在conda環(huán)境里安裝pip工具。在激活你的conda環(huán)境后輸入:conda install pip。這會(huì)幫你在后面的步驟里避免很多坑。

下面是安裝和設(shè)置的方法:

  1. 安裝Anaconda這個(gè)Python庫(kù)管理器。用Anaconda來安裝Python的各種庫(kù)會(huì)非常容易。你可以用這個(gè)curl命令來下載Anaconda并安裝:curl -O?https://repo.continuum.io/archive/Anaconda3-4.2.0-Linux-x86_64.sh chmod 777 Anaconda3-4.2.0-Linux-x86_64.sh ./Anaconda3-4.2.0-Linux-x86_64.sh
  2. 安裝scikit-learn。這是一個(gè)通用的科學(xué)計(jì)算庫(kù)。我們會(huì)用它來對(duì)我們的數(shù)據(jù)進(jìn)行預(yù)處理??梢杂眠@個(gè)命令來安裝:conda install scikit-learn
  3. 安裝Jupyter Notebook,命令是:conda install jupyter
  4. 獲取MXNet,這是一個(gè)開源的深度學(xué)習(xí)庫(kù)。這里用的Python notebook在MXNet的0.12.0版本上通過了測(cè)試??梢杂孟旅娴拿畎惭bMXNet:pip install mxnet-cu90(如果你有GPU)。沒有GPU的話,就用pip install mxnet –pre來安裝??梢栽?a >這里看到MXNet的文檔。

在激活anaconda的環(huán)境后,在里面運(yùn)行下面的命令:source activate mxnet。

上面的命令匯總?cè)缦拢?/p>

curl -O https://repo.continuum.io/archive/Anaconda3-4.2.0-Linux-x86_64.sh

chmod 777 Anaconda3-4.2.0-Linux-x86_64.sh?

./Anaconda3-4.2.0-Linux-x86_64.sh?

conda install pip

pip install opencv-python

conda install scikit-learn

conda install jupyter

pip install mxnet-cu90

  1. 你可以在這里下載本文教程的MXNet notebook。里面有我們寫好并測(cè)試通過的代碼。你可以調(diào)整超參數(shù)并嘗試不同的神經(jīng)網(wǎng)絡(luò)架構(gòu),好好玩一下吧!

準(zhǔn)備數(shù)據(jù)集

我們會(huì)用Friedrich Nietzsche的工作成果作為我們的數(shù)據(jù)集。

你可以在這里下載此數(shù)據(jù)集。當(dāng)然你也可以用其他的數(shù)據(jù)集,比如你自己的聊天記錄,或是你從這個(gè)地方下載的數(shù)據(jù)集。如果是用你自己的聊天記錄,你就可以為你自己的風(fēng)格寫一個(gè)定制化的可預(yù)測(cè)的文本編輯器了。在這里,我們還是用Nietzche的數(shù)據(jù)集。

數(shù)據(jù)集文件 (nietzsche.txt)包括600901個(gè)字符,來自86個(gè)唯一的字符。我們需要把這個(gè)文本轉(zhuǎn)換成一系列的數(shù)字串。

# total of characters in dataset

chars = sorted(list(set(text)))

vocab_size = len(chars)+1

print(‘total chars:’, vocab_size)

# maps character to unique index e.g. {a:1,b:2….}

char_indices = dict((c, i) for i, c in enumerate(chars))

# maps indices to character (1:a,2:b ….)

indices_char = dict((i, c) for i, c in enumerate(chars))

# mapping the dataset into index

idx = [char_indices for c in text]

針對(duì)展開版的RNN準(zhǔn)備數(shù)據(jù)

轉(zhuǎn)換的目的是把數(shù)據(jù)轉(zhuǎn)換成一系列的輸入和輸出組。來自輸入串的每3個(gè)字符串都會(huì)被存下來作為模型的3字符輸入,而第4個(gè)字符作為輸出,從而能用來訓(xùn)練我們的預(yù)測(cè)模型。例如,我們把字符串I_love_mxnet轉(zhuǎn)換成如圖6所示的輸入和輸出組。

unroll_input-c58235a9cc523734363518b96c3c97e0

圖6 “I_love_mxnet”的輸入和輸出組。圖片由Manu Jeevan提供

做這個(gè)轉(zhuǎn)換的代碼如下:

# input for neural network(our basic rnn has 3 inputs, n samples)

cs = 3

c1_dat = [idx[i] for i in range(0, len(idx)-1-cs, cs)]

c2_dat = [idx[i+1] for i in range(0, len(idx)-1-cs, cs)]

c3_dat = [idx[i+2] for i in range(0, len(idx)-1-cs, cs)]

# the output of rnn network (single vector)

c4_dat = [idx[i+3] for i in range(0, len(idx)-1-cs, cs)]

# stacking the inputs to form (3 input features )

x1 = np.stack(c1_dat[:-2])

x2 = np.stack(c2_dat[:-2])

x3 = np.stack(c3_dat[:-2])

# the output (1 X N data points)

y = np.stack(c4_dat[:-2])

col_concat = np.array([x1, x2, x3])

t_col_concat = col_concat.T

print(t_col_concat.shape)

我們還要把訓(xùn)練數(shù)據(jù)分成32組一批的批次。這樣每個(gè)訓(xùn)練輸入實(shí)例的尺寸就是32 X 3。批次化可以幫助加速模型的訓(xùn)練。

# Set the batchsize as 32, so input is of form 32 X 3

# output is 32 X 1

batch_size = 32

def get_batch(source, label_data, i, batch_size=32):

? ? bb_size = min(batch_size, source.shape[0] – 1 – i)

? ? data = source[i: i + bb_size]

? ? target = label_data[i: i + bb_size]

? ? # print(target.shape)

? ? return data, target.reshape((-1, ))

Gluon RNN準(zhǔn)備數(shù)據(jù)

上一節(jié)我們?yōu)榻o定前3個(gè)字符來預(yù)測(cè)第4個(gè)字符做的模型準(zhǔn)備了數(shù)據(jù)。這一節(jié)里,我們會(huì)擴(kuò)展這個(gè)算法,為給定的任意前n-1個(gè)字符,預(yù)測(cè)第n個(gè)字符。要做的和為展開版的RNN準(zhǔn)備數(shù)據(jù)差不多,除了要改變輸入的大小。此數(shù)據(jù)集也應(yīng)該被轉(zhuǎn)化成這個(gè)形狀(輸入X的長(zhǎng)度,批次大?。,F(xiàn)在讓我們把樣本數(shù)據(jù)分成如圖7所示的多個(gè)批次。

batch3-761ebdc4aa63dda408189d70f87216bc

圖7 批次化輸入。圖片由Manu Jeevan提供

我們把輸入的串轉(zhuǎn)化成了每批次3個(gè)輸入,每個(gè)輸入長(zhǎng)度為4。經(jīng)過這個(gè)轉(zhuǎn)換后,我們丟失了一些相鄰字符間的時(shí)間關(guān)系。比如,字符O和V、M和X。其中字符V是在O后面的,但是它們被分到了不同的批次里面。我們對(duì)數(shù)據(jù)進(jìn)行批次化的唯一目的就是加快訓(xùn)練的過程。下面是實(shí)現(xiàn)批次化的代碼:

# prepares rnn batches

# The batch will be of shape is (num_example * batch_size) because of RNN uses sequences of input ? ? x

# for example if we use (a1,a2,a3) as one input sequence , (b1,b2,b3) as another input sequence and (c1,c2,c3)

# if we have batch of 3, then at timestep ‘1’? we only have (a1,b1.c1) as input, at timestep ‘2’ we have (a2,b2,c2) as input…

# hence the batchsize is of order?

# In feedforward we use (batch_size, num_example)

def rnn_batch(data, batch_size):

? ? “””Reshape data into (num_example, batch_size)”””

? ? nbatch = data.shape[0] // batch_size

? ? data = data[:nbatch * batch_size]

? ? data = data.reshape((batch_size, nbatch)).T

? ? return data

圖8顯示了另外一個(gè)批次的例子,其中批次里的樣本數(shù)為2個(gè),每個(gè)長(zhǎng)度是6。

batch4-00db77326f87aee0b92fbdb6f541ef8a

圖8 批次的形狀轉(zhuǎn)換。圖片由Manu Jeevan提供

在給定批次里改變輸入的長(zhǎng)度是非常簡(jiǎn)單的。例如,如果想從一個(gè)批次大小為2的批次里生成一個(gè)長(zhǎng)度為3的輸入,用下面的代碼就能實(shí)現(xiàn)。

#get the batch

def get_batch(source, i, seq):

? ? seq_len = min(seq, source.shape[0] – 1 – i)

? ? data = source[i: i + seq_len]

? ? target = source[i + 1: i + 1 + seq_len]

? ? return data, target.reshape((-1,))

在這里的訓(xùn)練里,我們會(huì)使用輸入長(zhǎng)度為100,并把它作為超參數(shù)??梢愿M(jìn)一步地調(diào)優(yōu)這個(gè)超參數(shù)來得到更好的結(jié)果。

Gluon來定義RNN模型

接下來,我們定義一個(gè)類,其中可以構(gòu)造兩種RNN模型,用于我們的樣本。一種是GRU(門控循環(huán)單元模型,Gated Recurrent Unit)和LSTM(長(zhǎng)短期記憶單元模型)。GRU是一個(gè)簡(jiǎn)化版的LSTM,且性能差不多。你能在這里看到一個(gè)對(duì)它們的比較研究。下面的代碼片就是這些模型的定義。

class GluonRNNModel(gluon.Block):

? ? “””A model with an encoder, recurrent layer, and a decoder.”””

? ? def __init__(self, mode, vocab_size, num_embed, num_hidden,

?? ? ? ? ? ? ? ? num_layers, dropout=0.5, **kwargs):

? ? ? ? super(GluonRNNModel, self).__init__(**kwargs)

? ? ? ? with self.name_scope():

? ? ? ? ? ? self.drop = nn.Dropout(dropout)

? ? ? ? ? ? self.encoder = nn.Embedding(vocab_size, num_embed,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? weight_initializer=mx.init.Uniform(0.1))

? ? ? ? ? ? if mode == ‘lstm’:

? ? ? ? ? ? ? ? #? we create a LSTM layer with certain number of hidden LSTM cell and layers

? ? ? ? ? ? ? ? #? in our example num_hidden is 1000 and num of layers is 2

? ? ? ? ? ? ? ? #? The input to the LSTM will only be passed during the forward pass (see forward function below)

? ? ? ? ? ? ? ? self.rnn = rnn.LSTM(num_hidden, num_layers, dropout=dropout,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? input_size=num_embed)

? ? ? ? ? ? elif mode == ‘gru’:

? ? ? ? ? ? ? ? #? we create a GRU layer with certain number of hidden GRU cell and layers

? ? ? ? ? ? ? ? #? in our example num_hidden is 1000 and num of layers is 2

? ? ? ? ? ? ? ? #? The input to the GRU will only be passed during the forward pass (see forward function below)

? ? ? ? ? ? ? ? self.rnn = rnn.GRU(num_hidden, num_layers, dropout=dropout,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? input_size=num_embed)

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? #? we create a vanilla RNN layer with certain number of hidden vanilla RNN cell and layers

? ? ? ? ? ? ? ? #? in our example num_hidden is 1000 and num of layers is 2

? ? ? ? ? ? ? ? #? The input to the vanilla will only be passed during the forward pass (see forward function below)

? ? ? ? ? ? ? ? self.rnn = rnn.RNN(num_hidden, num_layers, activation=’relu’, dropout=dropout,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? input_size=num_embed)

? ? ? ? ? ? self.decoder = nn.Dense(vocab_size, in_units=num_hidden)

? ? ? ? ? ? self.num_hidden = num_hidden

? ? #? define the forward pass of the neural network

? ? def forward(self, inputs, hidden):

? ? ? ? emb = self.encoder(inputs)

? ? ? ? #? emb, hidden are the inputs to the hidden?

? ? ? ? output, hidden = self.rnn(emb, hidden)

? ? ? ? #? the ouput from the hidden layer to passed to drop out layer

? ? ? ? output = self.drop(output)

? ? ? ? #? print(‘output forward’,output.shape)

? ? ? ? #? Then the output is flattened to a shape for the dense layer ?

? ? ? ? decoded = self.decoder(output.reshape((-1, self.num_hidden)))

? ? ? ? return decoded, hidden

? ? # Initial state of RNN layer

? ? def begin_state(self, *args, **kwargs):

? ? ? ? return self.rnn.begin_state(*args, **kwargs)

這個(gè)類的構(gòu)造函數(shù)里面創(chuàng)建了用于前向傳播的神經(jīng)元們。這個(gè)構(gòu)造函數(shù)接收三種類型RNN層的參數(shù):LSTM、GRU和基本RNN。前向傳播方法會(huì)在訓(xùn)練時(shí)被使用,生成訓(xùn)練數(shù)據(jù)的損失值。

前向傳播函數(shù)開始時(shí)會(huì)為輸入的字符創(chuàng)建一個(gè)詞向量層。 你可以看一下我們之前的這篇博客來更多的了解詞向量的知識(shí)。詞向量層的輸出則作為RNN的輸入。RNN層會(huì)返回一個(gè)輸出層以及隱藏層的狀態(tài)。中間的dropout層則會(huì)防止模型完全記住輸入和輸出的映射關(guān)系,從而避免過擬合。RNN的輸出再經(jīng)過一個(gè)解碼器(全連接層)完成下一個(gè)字符的預(yù)測(cè),并在訓(xùn)練階段計(jì)算損失值。

我們定義了一個(gè)“開始狀態(tài)”函數(shù),它可以為模型初始化隱藏層的狀態(tài)。

訓(xùn)練這個(gè)網(wǎng)絡(luò)

在定義完網(wǎng)絡(luò)后,我們就開始訓(xùn)練這個(gè)網(wǎng)絡(luò),讓它進(jìn)行學(xué)習(xí)。

def trainGluonRNN(epochs, train_data, seq=seq_length):

? ? for epoch in range(epochs):

? ? ? ? total_L = 0.0

? ? ? ? hidden = model.begin_state(func=mx.nd.zeros, batch_size=batch_size, ctx=context)

? ? ? ? for ibatch, i in enumerate(range(0, train_data.shape[0] – 1, seq_length)):

? ? ? ? ? ? data, target = get_batch(train_data, i, seq)

? ? ? ? ? ? hidden = detach(hidden)

? ? ? ? ? ? with autograd.record():

? ? ? ? ? ? ? ? output, hidden = model(data, hidden)

? ? ? ? ? ? ? ? L = loss(output, target) # this is total loss associated with seq_length

? ? ? ? ? ? ? ? L.backward()

? ? ? ? ? ? grads = [i.grad(context) for i in model.collect_params().values()]

? ? ? ? ? ? # Here gradient is for the whole batch.

? ? ? ? ? ? # So we multiply max_norm by batch_size and seq_length to balance it.

? ? ? ? ? ? gluon.utils.clip_global_norm(grads, clip * seq_length * batch_size)

? ? ? ? ? ? trainer.step(batch_size)

? ? ? ? ? ? total_L += mx.nd.sum(L).asscalar()

? ? ? ? ? ? if ibatch % log_interval == 0 and ibatch > 0:

? ? ? ? ? ? ? ? cur_L = total_L / seq_length / batch_size / log_interval

? ? ? ? ? ? ? ? print(‘[Epoch %d Batch %d] loss %.2f’, epoch + 1, ibatch, cur_L)

? ? ? ? ? ? ? ? total_L = 0.0

? ? ? ? model.save_params(rnn_save)

每個(gè)周期的開始都是對(duì)隱藏單元初始化成零。在對(duì)批次訓(xùn)練時(shí),我們會(huì)把這個(gè)隱藏單元從計(jì)算圖里分割出來,這樣在反向傳播進(jìn)行梯度計(jì)算時(shí),就不會(huì)在超過序列長(zhǎng)度時(shí)(我們這里是100)還進(jìn)行梯度計(jì)算。如果我們不進(jìn)行分割,梯度就會(huì)被傳送到這個(gè)開始的隱藏層狀態(tài)(t=0)。在分割后,我們計(jì)算了損失值,并用反向傳播函數(shù)來進(jìn)行損失值的反向傳播,從而進(jìn)行權(quán)重的調(diào)優(yōu)。我們還通過把梯度和輸入長(zhǎng)度和批次大小相乘來對(duì)梯度進(jìn)行正規(guī)化。

文本生成

在模型用數(shù)據(jù)進(jìn)行訓(xùn)練后,我們就能生成與訓(xùn)練數(shù)據(jù)類似的隨機(jī)文本。針對(duì)輸入長(zhǎng)度100進(jìn)行200個(gè)周期訓(xùn)練后的模型的權(quán)重文件可以在這里下載。下載后,你可以用model.load_params函數(shù)導(dǎo)入這些模型權(quán)重。

下面,我們使用這個(gè)模型來生成類似于nietzsche的文字。為了生成任意長(zhǎng)度的文本,我們使用模型的輸出作為下一次生成的輸入。

為了生成文本,我們先初始化隱藏層的狀態(tài)。

?hidden = model.begin_state(func=mx.nd.zeros, batch_size=batch_size, ctx=context)

這里我們沒必要在分割這個(gè)初始隱藏層狀態(tài)了,因?yàn)槲覀儾粫?huì)再通過反向傳播來調(diào)整權(quán)重。

接著我們把輸入的向量改變一下尺寸再傳給這個(gè)RNN模型。

?sample_input = mx.nd.array(np.array([idx[0:seq_length]]).T, ctx=context)

隨后使用argmax來計(jì)算網(wǎng)絡(luò)的輸出,生成輸出字符“c”。

output, hidden = model(sample_input, hidden)

? ? ? ? index = mx.nd.argmax(output, axis=1)

? ? ? ? index = index.asnumpy()

? ? ? ? count = count + 1

再把生成的字符“c”添加到輸入串的最后,并把輸入串的第一個(gè)字符刪掉。

new_string = new_string + indices_char[index[-1]]

input_string = input_string[1:] + indices_char[index[-1]]

下面是生成任意長(zhǎng)度的類似nietzsche的文本的全部代碼。

import sys

# a nietzsche like text generator

def generate_random_text(model, input_string, seq_length, batch_size, sentence_length):

? ? count = 0

? ? new_string = ”

? ? cp_input_string = input_string

? ? hidden = model.begin_state(func=mx.nd.zeros, batch_size=batch_size, ctx=context)

? ? while count < sentence_length:

? ? ? ? idx = [char_indices for c in input_string]

? ? ? ? if(len(input_string) != seq_length):

? ? ? ? ? ? print(len(input_string))

? ? ? ? ? ? raise ValueError(‘there was a error in the input ‘)

? ? ? ? sample_input = mx.nd.array(np.array([idx[0:seq_length]]).T, ctx=context)

? ? ? ? output, hidden = model(sample_input, hidden)

? ? ? ? index = mx.nd.argmax(output, axis=1)

? ? ? ? index = index.asnumpy()

? ? ? ? count = count + 1

? ? ? ? new_string = new_string + indices_char[index[-1]]

? ? ? ? input_string = input_string[1:] + indices_char[index[-1]]

? ? print(cp_input_string + new_string)

如果你查看生成的文本,你就會(huì)發(fā)現(xiàn)模型已經(jīng)學(xué)到使用開閉引號(hào)(“”)。模型學(xué)到了有限的文本結(jié)構(gòu),看起來比較像nietzsche了。

你還可以用你自己的聊天記錄來訓(xùn)練模型,然后預(yù)測(cè)你會(huì)敲出的下一個(gè)字符。

在下一篇文章里,我們會(huì)學(xué)習(xí)生成模型,特別是生成對(duì)抗網(wǎng)絡(luò)。這是一個(gè)從給定的數(shù)據(jù)集學(xué)習(xí),并能生成新數(shù)據(jù)的強(qiáng)大的模型。

注意:雖然RNN模型可以被用來生成文本,但嚴(yán)格意義上它并不是一個(gè)好的生成模型。這個(gè)PDF文檔清晰地展示了用于文本分類的生成模型和判別模型的區(qū)別。

這篇博文是O’ReillyAmazon的合作產(chǎn)物。請(qǐng)閱讀我們的編輯獨(dú)立聲明

Suresh_Rathnaraj

Suresh Rathnaraj是人工智能公司AI Solutions的聯(lián)合創(chuàng)始人,該公司為企業(yè)構(gòu)建AI解決方案。 他是一名機(jī)器學(xué)習(xí)工程師,擁有計(jì)算機(jī)科學(xué)碩士學(xué)位。 他非常熱衷于深度學(xué)習(xí),并認(rèn)為它可以徹底改變整個(gè)科技行業(yè)。 業(yè)余時(shí)間里他會(huì)打橄欖球并練習(xí)瑜伽??梢栽贚inkedIn上找到他。

Manu_Jeevan

Manu Jeevan是人工智能公司AI Solutions的聯(lián)合創(chuàng)始人,該公司為企業(yè)構(gòu)建人工智能解決方案。他是一名自學(xué)成才的數(shù)據(jù)科學(xué)家,喜歡用人工智能和機(jī)器學(xué)習(xí)來解決復(fù)雜的業(yè)務(wù)問題。 可以在LinkedIn上找到他。

Whirl (source: Pixabay)