• 【pytorch】关于Embedding和GRU、LSTM的使用详解


    1. Embedding的使用

    pytorch中实现了Embedding,下面是关于Embedding的使用。

    torch.nn包下的Embedding,作为训练的一层,随模型训练得到适合的词向量。

    建立词向量层

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

    找到对应的词向量放进网络:词向量的输入应该是什么样子

    实际上,上面通过随机初始化建立了词向量层后,建立了一个“二维表”,存储了词典中每个词的词向量。每个mini-batch的训练,都要从词向量表找到mini-batch对应的单词的词向量作为RNN的输入放进网络。那么怎么把mini-batch中的每个句子的所有单词的词向量找出来放进网络呢,输入是什么样子,输出是什么样子?

    首先我们知道肯定先要建立一个词典,建立词典的时候都会建立一个dict:word2id:存储单词到词典序号的映射。假设一个mini-batch如下所示:

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

    显然,这个mini-batch有3个句子,即batch_size=3

    第一步首先要做的是:将句子标准化,所谓标准化,指的是:大写转小写,标点分离,这部分很简单就略过。经处理后,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有两个隐藏层向量。

  • 相关阅读:
    html页面禁止自动填充浏览器记住的密码
    未能加载文件或程序集“System.Data.SQLite.DLL”或它的某一个依赖项
    DotNetMQ的一个小demo
    js 排列 组合 的一个简单例子
    js 生成笛卡尔积
    290.单词模式。给定一种 pattern(模式) 和一个字符串 str ,判断 str 是否遵循相同的模式。(c++方法)
    递归与迭代的联系以及优缺点(以c++为例)
    leetcode344 反转字符串 c++实现
    面试题:编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 ""。(c++实现)
    LeetCode题库13. 罗马数字转整数(c++实现)
  • 原文地址:https://www.cnblogs.com/duye/p/10590146.html
Copyright © 2020-2023  润新知