【pytorch】關於Embedding和GRU、LSTM的使用詳解

1. Embedding的使用

pytorch中實現了Embedding,下面是關於Embedding的使用。python

torch.nn包下的Embedding,做爲訓練的一層,隨模型訓練獲得適合的詞向量。網絡

創建詞向量層函數

embed = torch.nn.Embedding(n_vocabulary,embedding_size)code

找到對應的詞向量放進網絡:詞向量的輸入應該是什麼樣子對象

實際上,上面經過隨機初始化創建了詞向量層後,創建了一個「二維表」,存儲了詞典中每一個詞的詞向量。每一個mini-batch的訓練,都要從詞向量表找到mini-batch對應的單詞的詞向量做爲RNN的輸入放進網絡。那麼怎麼把mini-batch中的每一個句子的全部單詞的詞向量找出來放進網絡呢,輸入是什麼樣子,輸出是什麼樣子?ip

首先咱們知道確定先要創建一個詞典,創建詞典的時候都會創建一個dict:word2id:存儲單詞到詞典序號的映射。假設一個mini-batch以下所示:input

['I am a boy.','How are you?','I am very lucky.']it

顯然,這個mini-batch有3個句子,即batch_size=3class

第一步首先要作的是:將句子標準化,所謂標準化,指的是:大寫轉小寫,標點分離,這部分很簡單就略過。經處理後,mini-batch變爲:方法

[['i','am','a','boy','.'],['how','are','you','?'],['i','am','very','lucky','.']]

可見,這個list的元素成了一個個list。還要作一步:將上面的三個list按單詞數從多到少排列。標點也算單詞。至於爲何,後面會說到。

那就變成了:

batch = [['i','am','a','boy','.'],['i','am','very','lucky','.'],['how','are','you','?']]

可見,每一個句子的長度,即每一個內層list的元素數爲:5,5,4。這個長度也要記錄。

lens = [5,5,4]

以後,爲了可以處理,將batch的單詞表示轉爲在詞典中的index序號,這就是word2id的做用。轉換過程很簡單,假設轉換以後的結果以下所示,固然這些序號是我編的。

batch = [[3,6,5,6,7],[6,4,7,9,5],[4,5,8,7]]

同時,每一個句子結尾要加EOS,假設EOS在詞典中的index是1。

batch = [[3,6,5,6,7,1],[6,4,7,9,5,1],[4,5,8,7,1]]

那麼長度要更新:

lens = [6,6,5]

很顯然,這個mini-batch中的句子長度不一致!因此爲了規整的處理,對長度不足的句子,進行填充。填充PAD假設序號是2,填充以後爲:

batch = [[3,6,5,6,7,1],[6,4,7,9,5,1],[4,5,8,7,1,2]]

這樣就能夠直接取詞向量訓練了嗎?

不能!上面batch有3個樣例,RNN的每一步要輸入每一個樣例的一個單詞,一次輸入batch_size個樣例,因此batch要按list外層是時間步數(即序列長度),list內層是batch_size排列。即batch的維度應該是:

[seq_len,batch_size]

[seq_len,batch_size]

[seq_len,batch_size]

重要的問題說3遍。

怎麼變換呢?變換方法能夠是:使用itertools模塊的zip_longest函數。並且,使用這個函數,連填充這一步均可以省略,由於這個函數能夠實現填充!

batch = list(itertools.zip_longest(batch,fillvalue=PAD))
# fillvalue就是要填充的值,強制轉成list

經變換,結果應該是:

batch = [[3,6,4],[6,4,5],[5,7,8],[6,9,7],[7,5,1],[1,1,2]]

記得咱們還記錄了一個lens:

lens = [6,6,5]

batch還要轉成LongTensor

batch=torch.LongTensor(batch)

這裏的batch就是詞向量層的輸入。

詞向量層的輸出是什麼樣的?

好了,如今使用創建了的embedding直接經過batch取詞向量了,如:

embed_batch = embed (batch)

假設詞向量維度是6,結果是:

tensor([[[-0.2699,  0.7401, -0.8000,  0.0472,  0.9032, -0.0902],
         [-0.2675,  1.8021,  1.4966,  0.6988,  1.4770,  1.1235],
         [ 0.1146, -0.8077, -1.4957, -1.5407,  0.3755, -0.6805]],

        [[-0.2675,  1.8021,  1.4966,  0.6988,  1.4770,  1.1235],
         [ 0.1146, -0.8077, -1.4957, -1.5407,  0.3755, -0.6805],
         [-0.0387,  0.8401,  1.6871,  0.3057, -0.8248, -0.1326]],

        [[-0.0387,  0.8401,  1.6871,  0.3057, -0.8248, -0.1326],
         [-0.3745, -1.9178, -0.2928,  0.6510,  0.9621, -1.3871],
         [-0.6739,  0.3931,  0.1464,  1.4965, -0.9210, -0.0995]],

        [[-0.2675,  1.8021,  1.4966,  0.6988,  1.4770,  1.1235],
         [-0.7411,  0.7948, -1.5864,  0.1176,  0.0789, -0.3376],
         [-0.3745, -1.9178, -0.2928,  0.6510,  0.9621, -1.3871]],

        [[-0.3745, -1.9178, -0.2928,  0.6510,  0.9621, -1.3871],
         [-0.0387,  0.8401,  1.6871,  0.3057, -0.8248, -0.1326],
         [ 0.2837,  0.5629,  1.0398,  2.0679, -1.0122, -0.2714]],

        [[ 0.2837,  0.5629,  1.0398,  2.0679, -1.0122, -0.2714],
         [ 0.2837,  0.5629,  1.0398,  2.0679, -1.0122, -0.2714],
         [ 0.2242, -1.2474,  0.3882,  0.2814, -0.4796,  0.3732]]],
       grad_fn=<EmbeddingBackward>)

維度的前兩維和前面講的是一致的。可見多了一個第三維,這就是詞向量維度。因此,Embedding層的輸出是:

[seq_len,batch_size,embedding_size]

2 關於pytorch中的GRU

取詞向量,放進GRU。

創建GRU

gru = torch.nn.GRU(input_size,hidden_size,n_layers)
# 這裏的input_size就是詞向量的維度,hidden_size就是RNN隱藏層的維度,這兩個通常相同就能夠
# n_layers是GRU的層數

可見,並不須要指定時間步數,也即seq_len,這是由於,GRU和LSTM都實現了自身的迭代。

GRU的輸入應該是什麼樣子的?

上面的embed_batch做爲Embedding層的輸出,能夠直接放進GRU中嗎?

理論上能夠,但這樣不對!由於GRU並不知道哪些是填充的,並非每個句子都知足最大序列長度!因此咱們事先用lens記錄了長度。

將輸出embed_batch轉成pack_padded_sequence,使用torch.nn.utils.rnn. 下的pack_padded_sequence方法。

batch_packed = torch.nn.utils.rnn.pack_padded_sequence(embed_batch, lens)
 # 注意這裏的輸入lens就是前面的長度list

這個 batch_packed 就是GRU的輸入。

batch_packed 長啥樣?

不妨看一下:

PackedSequence(data=tensor([[-0.2699,  0.7401, -0.8000,  0.0472,  0.9032, -0.0902],
        [-0.2675,  1.8021,  1.4966,  0.6988,  1.4770,  1.1235],
        [ 0.1146, -0.8077, -1.4957, -1.5407,  0.3755, -0.6805],
        [-0.2675,  1.8021,  1.4966,  0.6988,  1.4770,  1.1235],
        [ 0.1146, -0.8077, -1.4957, -1.5407,  0.3755, -0.6805],
        [-0.0387,  0.8401,  1.6871,  0.3057, -0.8248, -0.1326],
        [-0.0387,  0.8401,  1.6871,  0.3057, -0.8248, -0.1326],
        [-0.3745, -1.9178, -0.2928,  0.6510,  0.9621, -1.3871],
        [-0.6739,  0.3931,  0.1464,  1.4965, -0.9210, -0.0995],
        [-0.2675,  1.8021,  1.4966,  0.6988,  1.4770,  1.1235],
        [-0.7411,  0.7948, -1.5864,  0.1176,  0.0789, -0.3376],
        [-0.3745, -1.9178, -0.2928,  0.6510,  0.9621, -1.3871],
        [-0.3745, -1.9178, -0.2928,  0.6510,  0.9621, -1.3871],
        [-0.0387,  0.8401,  1.6871,  0.3057, -0.8248, -0.1326],
        [ 0.2837,  0.5629,  1.0398,  2.0679, -1.0122, -0.2714],
        [ 0.2837,  0.5629,  1.0398,  2.0679, -1.0122, -0.2714],
        [ 0.2837,  0.5629,  1.0398,  2.0679, -1.0122, -0.2714]],
       grad_fn=<PackPaddedBackward>), batch_sizes=tensor([3, 3, 3, 3, 3, 2], grad_fn=<PackPaddedBackward>))

能夠看到,屬性batch_sizes清楚的記錄了每一個時間步上batch輸出是多少,並且去除了PAD。

此外,GRU還須要一個初始隱藏向量(注意層數和方向),嫌麻煩直接傳None也無妨。

因此輸入應該是( batch_packed , None )

GRU的輸出?

output,hidden = gru(batch_packed,None)

output:PackedSequence對象

PackedSequence(data=tensor([[ 0.0432, -0.0149, -0.0884, -0.0194, -0.0740,  0.1278],
        [-0.0436, -0.0726,  0.0568, -0.0995, -0.1992,  0.1594],
        [ 0.0582,  0.0625, -0.1639,  0.1474,  0.0077,  0.0542],
        [-0.0052, -0.0732,  0.0031, -0.1367, -0.2336,  0.2307],
        [ 0.0131,  0.0234, -0.0681,  0.0535, -0.1651,  0.1864],
        [ 0.0324,  0.1441, -0.1788,  0.1800, -0.0816,  0.1684],
        [-0.0788, -0.0148, -0.0292, -0.1348, -0.3352,  0.3045],
        [ 0.0502,  0.0436, -0.1509,  0.1481, -0.1284,  0.1523],
        [ 0.0627,  0.1626, -0.1888,  0.1341, -0.0984,  0.2627],
        [-0.1391, -0.0149,  0.0473, -0.2069, -0.4410,  0.3690],
        [ 0.1378,  0.0578, -0.2008,  0.1265, -0.0149,  0.2053],
        [ 0.0780,  0.1199, -0.2107,  0.1460, -0.0906,  0.2291],
        [-0.1019,  0.0055, -0.0304, -0.1277, -0.4149,  0.3582],
        [ 0.0906,  0.1025, -0.1646,  0.0933, -0.0953,  0.2905],
        [ 0.1004,  0.1175, -0.1911,  0.0979, -0.0877,  0.2771],
        [-0.0607,  0.0469, -0.0935, -0.1002, -0.3568,  0.3707],
        [ 0.0737,  0.1213, -0.1516,  0.0365, -0.1417,  0.3591]],
       grad_fn=<CatBackward>), batch_sizes=tensor([3, 3, 3, 3, 3, 2], grad_fn=<PackPaddedBackward>))

前三個list對應於第一時間步,mini-batch的三個樣例的輸出。依次類推。最後只有兩個,由於最後是有缺省的。

hidden:是個張量。維度[n_layers,batch_size,hidden_size]

tensor([[[-0.1057,  0.2273,  0.0964,  0.2777,  0.1391, -0.1769],
         [-0.1792,  0.1942,  0.1248,  0.0800, -0.0082,  0.0778],
         [-0.2631,  0.1654,  0.1455, -0.1428,  0.1888, -0.2379]],

        [[-0.0607,  0.0469, -0.0935, -0.1002, -0.3568,  0.3707],
         [ 0.0737,  0.1213, -0.1516,  0.0365, -0.1417,  0.3591],
         [ 0.1004,  0.1175, -0.1911,  0.0979, -0.0877,  0.2771]]],
       grad_fn=<ViewBackward>)

因此到這,爲何逆序,爲何記錄長度也就清楚了。

3 關於pytroch中的LSTM

有點累了,過會寫。差不對,就LSTM有兩個隱藏層向量。

相關文章
相關標籤/搜索