• 循环神经网络 - 序列模型


    序列模型

    1 - 训练

    使用正弦函数和一些可加性噪声来生成序列数据,时间步为1,2,…,1000

    %matplotlib inline
    import torch
    from torch import nn
    from d2l import torch as d2l
    
    • 1
    • 2
    • 3
    • 4
    T = 1000 # 总共产生1000个点
    time = torch.arange(1,T + 1,dtype = torch.float32)
    x = torch.sin(0.01 * time) + torch.normal(0,0.2,(T,))
    d2l.plot(time,[x],'time','x',xlim=[1,1000],figsize=(6,3))
    
    • 1
    • 2
    • 3
    • 4

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-canXx8M6-1662805657790)(https://yingziimage.oss-cn-beijing.aliyuncs.com/img/202209101810997.svg)]

    接下来,我们将这个序列转换为模型的“特征-标签”(feature-label)对。基于嵌入维度τ,我们将数据映射为数据对yt = xt 和xt = [xt−τ , . . . , xt−1]。你可能已经注意到,这⽐我们提供的数据样本少了τ个,因为我们没有⾜够的历史记录来描述前τ个数据样本。⼀个简单的解决办法是:如果拥有⾜够⻓的序列就丢弃这⼏项;另⼀个⽅法是⽤零填充序列。在这⾥,我们仅使⽤前600个“特征-标签”对进⾏训练

    tau = 4
    features = torch.zeros((T - tau,tau))
    for i in range(tau):
        features[:,i] = x[i:T - tau + i]
        
    labels = x[tau:].reshape((-1,1))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    batch_size,n_train = 16,600
    # 只有前n_train个样本用于训练
    train_iter = d2l.load_array((features[:n_train],labels[:n_train]),
                               batch_size,is_train = True)
    
    • 1
    • 2
    • 3
    • 4

    在这里,我们使用一个相当简单的架构训练模型:一个拥有两个全连接层的多层感知机,ReLU激活函数和平方损失

    # 初始化网络权重的函数
    def init_weights(m):
        if type(m) == nn.Linear:
            nn.init.xavier_uniform_(m.weight)
            
    # 一个简单的多层感知机
    def get_net():
        net = nn.Sequential(nn.Linear(4,10),
                           nn.ReLU(),
                           nn.Linear(10,1))
        net.apply(init_weights)
        return net
    
    # 平方损失。注意:MSELoss计算平方误差时不带系数1/2
    loss = nn.MSELoss(reduction='none')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    现在,我们开始训练模型了

    def train(net,train_iter,loss,epochs,lr):
        trainer = torch.optim.Adam(net.parameters(),lr)
        for epoch in range(epochs):
            for X,y in train_iter:
                trainer.zero_grad()
                l = loss(net(X),y)
                l.sum().backward()
                trainer.step()
            print(f'epoch {epoch + 1},'
                 f'loss: {d2l.evaluate_loss(net,train_iter,loss):f}')
            
    net = get_net()
    train(net,train_iter,loss,5,0.01)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    epoch 1,loss: 0.074985
    epoch 2,loss: 0.060292
    epoch 3,loss: 0.057717
    epoch 4,loss: 0.056681
    epoch 5,loss: 0.058259
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2 - 预测

    由于训练损失很小,因此我们期望模型能有很好的工作效果,让我们看看这在实践中意味着什么。首先检查模型预测下一个时间步的能力,也就是单步预测(one-step-ahead prediction)

    onestep_preds = net(features)
    d2l.plot([time,time[tau:]],
            [x.detach().numpy(),onestep_preds.detach().numpy()],'time',
            'x',legend=['data','1-step preds'],xlim = [1,1000],
            figsize=(6,3))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ms3SzC7D-1662805657791)(https://yingziimage.oss-cn-beijing.aliyuncs.com/img/202209101810999.svg)]


    multistep_preds = torch.zeros(T)
    multistep_preds[:n_train + tau] = x[:n_train + tau]
    for i in range(n_train + tau,T):
        multistep_preds[i] = net(multistep_preds[i - tau:i].reshape((1,-1)))
    
    d2l.plot([time, time[tau:], time[n_train + tau:]],
            [x.detach().numpy(), onestep_preds.detach().numpy(),
            multistep_preds[n_train + tau:].detach().numpy()], 'time',
            'x', legend=['data', '1-step preds', 'multistep preds'],
            xlim=[1, 1000], figsize=(6, 3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5toJA1m7-1662805657792)(https://yingziimage.oss-cn-beijing.aliyuncs.com/img/202209101810000.svg)]

    如上⾯的例⼦所⽰,绿线的预测显然并不理想。经过⼏个预测步骤之后,预测的结果很快就会衰减到⼀个常数。为什么这个算法效果这么差呢?事实是由于错误的累积:假设在步骤1之后,我们积累了⼀些错误ϵ1 = ¯ϵ。于是,步骤2的输⼊被扰动了ϵ1,结果积累的误差是依照次序的ϵ2 = ¯ϵ + cϵ1,其中c为某个常数,后⾯的预测误差依此类推。因此误差可能会相当快地偏离真实的观测结果。例如,未来24⼩时的天⽓预报往往相当准确,但超过这⼀点,精度就会迅速下降。我们将在本章及后续章节中讨论如何改进这⼀点

    基于k = 1, 4, 16, 64,通过对整个序列预测的计算,让我们更仔细地看⼀下k步预测的困难

    max_steps = 64
    
    features = torch.zeros((T - tau - max_steps + 1,tau + max_steps))
    # 列i(i < tau)是来自x的观测,其时间步从(i + 1)到(i + T - tau - max_steps + 1)
    for i in range(tau):
        features[:,i] = x[i: i + T - tau - max_steps + 1]
        
    # 列i(i >= tau)是来自(i - tau + 1)步的预测,其时间步从(i + 1)到(i + T - tau - max_steps + 1)
    for i in range(tau,tau + max_steps):
        features[:,i] = net(features[:,i - tau:i]).reshape(-1)
        
    steps = (1,4,16,64)
    d2l.plot([time[tau + i - 1:T - max_steps + i] for i in steps],
            [features[:,(tau + i - 1)].detach().numpy() for i in steps],'time','x',
            legend=[f'{i}-step preds' for i in steps],xlim=[5,1000],
            figsize=(6,3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16


    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H4WUpSUg-1662805657792)(https://yingziimage.oss-cn-beijing.aliyuncs.com/img/202209101810001.svg)]

    上述例子清楚地说明了当我们试图预测更远的未来时,预测的质量是如何变化的。虽然“4步预测”看起来仍然不错,但超过这个跨度的任何预测几乎都是无用的

    3 - 小结

    • 内插法(在现有观测值之间进⾏估计)和外推法(对超出已知观测范围进⾏预测)在实践的难度上差别很⼤。因此,对于你所拥有的序列数据,在训练时始终要尊重其时间顺序,即最好不要基于未来的数据进⾏训练
    • 序列模型的估计需要专门的统计工具,两种较流行的选择是自回归模型和隐变量自回归模型
    • 对于时间是向前推进的因果模型,正向估计通常比反向估计更容易
    • 对于直到时间步t的观测序列,其在时间步t + k的预测输出是“k步预测”。随着我们对预测时间k值的增加,会造成误差的快速累积和预测质量的极速下降
  • 相关阅读:
    JUC-管程
    solidty-基础篇-基础语法和定义函数
    20-指针-2
    GoLong的学习之路,进阶,标准库之并发(context)补充并发三部曲,你真的明白context吗?
    聊 · Flutter
    C++中map和set的区别
    三维动画设计
    猿创征文 | 组件的定义及复用性,局部组件和全局组件(1)
    Nginx实战
    Asahi Linux 采用开源驱动,在 Apple M1 下运行首个三角形渲染
  • 原文地址:https://blog.csdn.net/mynameisgt/article/details/126798425