• pytorch基础学习(6)


    1. nn.RNN:
      1. 数据处理:每次向网络中输入batch个样本,每个时刻处理的是该时刻的batch个样本
      2. 输入3句话,每句话10个单词,每个单词用100维的向量表示,那么seq_len=10,batch=3,feature_len=100。
      3. RNN的前向传播公式:
        1. x_t@w_(xh)+h_t@w_(hh)
        2. [batch,feature_len] @ [hidden_len,feature_len]^T+ [batch, hidden_len]@ [hidden_len , hidden_len]^T
         
        1. hidden_len是一个可以自定的超参数    
        2. x_t@w_(xh)是对当前时刻处理的Tensor x_t 的线性变换
        3. h_t@w_(hh)是对当前时刻取得的隐藏记忆单元的线性变换
        4. 注意nn.RNN构造时传入的是feature_len和hidden_len,至于有多少个特征(seq_len)、一次输入多少样本(batch)都是可以在运行时候动态决定的。
      4. RNN的构造:
        1. from torch import nn
        2. # 表示feature_len=100(如每个单词用100维向量表示), hidden_len=10(隐藏单元的尺寸)
        3. rnn = nn.RNN(100, 10)
        4. # 使用nn.RNN构造时传入的三个参数是feature_len、hidden_len、num_layers,默认空间层数=1
      5. nn.RNN的forward方法:
        1. out, h_t=forward(x, h_0)
        2. """
        3. x是一次性将所有时刻特征喂入的,而不需要每次喂入当前时刻的x_t,所以其shape是[seq_len,batch,feature_len]。
        4. h_0是第一个时刻空间上所有层的记忆单元的Tensor,要考虑循环网络空间上的层数,所以这里输入的shape是[num_layer,batch,hidden_len]。
        5. 返回值有两部分:
        6. h_t是最后一个时刻空间上所有层的记忆单元,所以它和h_0的shape是一样的,即[num_layer,batch,hidden_len]。
        7. 返回的out是每一个时刻上空间上最后一层的输出,所以它的shape是[seq_len,batch,hidden_len]
        8. """
      6. 使用例子:
        1. 一层:
          1. # 表示feature_len=100, hidden_len=20, 层数=1
          2. rnn = nn.RNN(100, 20, 1)
          3. # 输入3个样本序列(batch=3), 序列长为10(seq_len=10), 每个特征100维度(feature_len=100)
          4. x = torch.randn(10, 3, 100)
          5. # 传入RNN处理, 另外传入h_0, shape是<层数, batch, hidden_len=20>
          6. out, h = rnn(x, torch.zeros(1, 3, 20))
        2. 多层的只要构造时设置第三个参数大于1,对于每一层都有在时间线上的共享参数:
          1. from torch import nn
          2. # 表示feature_len=100, hidden_len=20, 层数=2
          3. rnn = nn.RNN(100, 20, num_layers=2)
          4. """
          5. 从l1层开始接受的输入都是下面层的输出,也就是说接受的输入的特征数不再是feature_len而是hidden_len了,所以这里参数weight_ih_l1的shape应是[hidden_len,hidden_len]:
          6. nn.RNN的最底下一层l0将外部的feature_len转化为隐藏记忆单元的内部表示即hidden_len,而其它层都是输入hidden_len输出hidden_len的。
          7. """
        3. 包含参数初始化的例子:
          1. # RNN层
          2. self.rnn = nn.RNN(
          3. input_size=1,  # 即feature_len=1
          4.     hidden_size=hidden_len,  # 隐藏记忆单元尺寸
          5.     num_layers=1,  # 层数,这里就用单层RNN
          6.     batch_first=True  # 在喂入数据时,按照[batch,seq_len,feature_len]的格式
          7. )
          8. # 对RNN层的参数做初始化
          9. for p in self.rnn.parameters():
          10.     nn.init.normal_(p, mean=0.0, std=0.001)
          11. # 输出层,直接用一个线性变换把每个时刻记忆单元的hidden_len输出为所需的feature_len=1
          12. self.linear = nn.Linear(hidden_len, 1)
          13. """生成样本数据"""
          14. # 在0~3之间随机取开始的时刻点
          15. k = np.random.randint(3, size=1)[0]
          16. # 取点的区间是[k, k+10],均匀地取num_points个点
          17. time_steps = np.linspace(k, k + 10, num_points)
          18. # 在这num_points个时刻上生成函数值数据
          19. data = np.sin(time_steps)
          20. # 喂入模型得到输出
          21. out, h = model(x, h)  # h是上次循环得到的h
          22. # 因为h在循环中被一次次嵌套,这里不要为上一个网络求梯度,而只求当前的,所以detach一下
          23. h = h.detach()
      7. 要注意,使用nn.RNN时,输入Tensor的seq_len并不是一个在构造时需要的参数,也就是说,训练的时候可以用某个seq_len训练,而测试的时候可以用另一个seq_len来测试。比如在这个例子中,训练时是用0-48这49个点,预测1-49这49个点(seq_len=49)。而测试时每次传入一个点(seq_len=1),预测下一个点。PyTorch会自动调整循环网络的结构来适应输入。
    2. nn.RNNCell:
      1. 将序列上的每个时刻分开来处理。
      2. 如果要处理的是3个句子,每个句子10个单词,每个单词用长100的向量,那么送入nn.RNN的Tensor的shape就是[10,3,100]。但如果使用nn.RNNCell,则将每个时刻分开处理,送入的Tensor的shape是[3,100],但要将此计算单元运行10次。显然这种方式比较麻烦,但使用起来也更灵活。
      3. 构造方法和nn.RNN类似,依次传入feature_len和hidden_len,因为这只是一个计算单元,所以不涉及层数。
      4. 前向计算的输入输出和nn.RNN是不一样的,具体是:
        1. h_t = forward(x_t, h_{t-1})
        2. """
        3. 当前时刻的输入x_t,所以其shape是[batch,feature_len]。
        4. h_{t-1}是这个时刻运行之前记忆单元的Tensor,也就是前一时刻的单元输出,所以这里输入的shape是[batch,hidden_len]。
        5. 返回值h_t是这个时刻运行之后记忆单元的Tensor,也就是下一时刻(如果有)的单元输入,所以它和h_{t-1}的shape是一样的,即[batch,hidden_len]。
        6. """
      5. 使用nn.RNNCell没法像nn.RNN那样直接求得网络的输出out,如果需要,可以将最后一层每个时刻i ii该单元的输出h_i组合起来:
        out = torch.stack([h1,h2,...,ht])
      6. 使用例子:
        1. 一层:
          1. # 表示feature_len=100, hidden_len=20
          2. cell = nn.RNNCell(100, 20)
          3. # 所有时刻的输入, 一共有10个时刻, 即seq_len=10
          4. xs = [torch.randn(3, 100) for i in range(10)]
          5. # 初始化隐藏记忆单元, batch=3, hidden_len=20
          6. h = torch.zeros(3, 20)
          7. # 对每个时刻的输入, 传入这个nn.RNNCell计算单元, 还要传入上一时h, 以进行前向计算
          8. for xt in xs:
          9.     h = cell(xt, h)
        2. 两层:
          1. # 第0层和第1层的计算单元
          2. cell_l0 = nn.RNNCell(100, 30)  # feature_len=100, hidden_len_l0=30
          3. cell_l1 = nn.RNNCell(30, 20)  # hidden_len_l0=30, hidden_len_l1=20
          4. # 第0层和第1层使用的隐藏记忆单元(图中黄色和绿色)
          5. h_l0 = torch.zeros(3, 30)  # batch=3, hidden_len_l0=30
          6. h_l1 = torch.zeros(3, 20)  # batch=3, hidden_len_l1=20
          7. # 原始输入, batch=3, feature_len=100
          8. xs = [torch.randn(3, 100) for i in range(4)]  # seq_len=4, 即共4个时刻
          9. for xt in xs:
          10.     h_l0 = cell_l0(xt, h_l0)
          11.     h_l1 = cell_l1(h_l0, h_l1)
  • 相关阅读:
    MySQL识别不了中文怎么办?(适合新手)
    03-背景属性
    Linux上oracle和mysql的启动,关闭,重启
    (26)STM32——内部温度传感器笔记
    vue非常实用的几行代码【日期处理、字符串处理、数组处理、颜色操作】
    Spring的注解开发-依赖注入相关注解
    数据仓库应该用什么方案——数据仓库实施方案概述
    【C进阶】指针笔试题解析
    idea中往git上上传项目步骤
    基于stm32的恒功率无线充电
  • 原文地址:https://blog.csdn.net/weixin_45647721/article/details/128166067