深度自回归模型——《深度生成模型》阅读笔记(3)

书接上文

接着上一次的阅读笔记。上次的笔记主要是在GPT老师的帮助下学习了自回归模型的基本概念,并进行了建模,与原书不同的是,GPT老师给出的自回归模型是直接根据时间序列进行建模,而作者则是借助了概率进行建模。在最后,我们也加入了多层感知机模型(Multi-layer proceptron, MLP)将自回归模型进行参数化。

与书中一样,使用MLP模型参数化的自回归模型属于有限记忆,或者短距记忆(Short-Term Memory,STM)。GPT老师给出的解释是:在这个模型中,输入数据在经过隐藏层的处理后,直接通过输出层进行预测。该模型无法显式地捕捉和长期记忆之间的依赖关系,因此更适合处理短期的时间序列依赖关系。

基于循环神经网络的长距记忆

既然是针对时间序列的问题,我第一个想到的就是循环神经网络(Recurrent Neural Network, RNN),还有长短期记忆(Long Short-Term Memory,LSTM),果然书中作者也是最先使用的RNN模型来实现的长距记忆。可惜作者没有用LSTM,为了弥补遗憾,我让GPT老师分别给出了两种方法来为自回归模型建模。

GPT老师:

当基于RNN(循环神经网络)和LSTM(长短期记忆网络)模型进行自回归建模时,我们需要创建一个能够处理时间序列数据依赖关系的模型。

首先是基于RNN模型的自回归建模:

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

class RNNAutoregressiveModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(RNNAutoregressiveModel, self).__init__()
        self.hidden_size = hidden_size
        self.rnn = nn.RNN(input_size, hidden_size, nonlinearity='relu', batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x, hidden):
        out, hidden = self.rnn(x, hidden)
        out = self.fc(out)
        return out, hidden

    def init_hidden(self, batch_size):
        return torch.zeros(1, batch_size, self.hidden_size)

def rnn_autoregressive_model(Y, p, hidden_size, learning_rate, num_epochs):
    n = len(Y)
    X = np.zeros((n-p, p, 1))
    for i in range(n-p):
        X[i, :, 0] = Y[i:i+p]
    X = torch.FloatTensor(X)
    Y = torch.FloatTensor(Y[p:])

    model = RNNAutoregressiveModel(1, hidden_size, 1)

    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=learning_rate)

    for epoch in range(num_epochs):
        optimizer.zero_grad()
        hidden = model.init_hidden(X.size(0))
        output, _ = model(X, hidden)
        loss = criterion(output.view(-1), Y)
        loss.backward()
        optimizer.step()

    return model

# 示例数据
Y = np.array([1, 3, 5, 7, 9, 11])
p = 2
hidden_size = 32
learning_rate = 0.001
num_epochs = 1000

# 训练RNN模型
model = rnn_autoregressive_model(Y, p, hidden_size, learning_rate, num_epochs)

# 获取模型参数
weights = model.state_dict()

# 输出模型参数
print("RNN自回归模型参数:")
print("Weight:", weights['rnn.weight_ih_l0'].numpy())
print("Bias:", weights['rnn.bias_ih_l0'].numpy())

接下来是基于LSTM模型的自回归建模:

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

class LSTMAutoregressiveModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(LSTMAutoregressiveModel, self).__init__()
        self.hidden_size = hidden_size
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x, hidden):
        out, hidden = self.lstm(x, hidden)
        out = self.fc(out)
        return out, hidden

    def init_hidden(self, batch_size):
        return (torch.zeros(1, batch_size, self.hidden_size), torch.zeros(1, batch_size, self.hidden_size))

def lstm_autoregressive_model(Y, p, hidden_size, learning_rate, num_epochs):
    n = len(Y)
    X = np.zeros((n-p, p, 1))
    for i in range(n-p):
        X[i, :, 0] = Y[i:i+p]
    X = torch.FloatTensor(X)
    Y = torch.FloatTensor(Y[p:])

    model = LSTMAutoregressiveModel(1, hidden_size, 1)

    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=learning_rate)

    for epoch in range(num_epochs):
        optimizer.zero_grad()
        hidden = model.init_hidden(X.size(0))
        output, _ = model(X, hidden)
        loss = criterion(output.view(-1), Y)
        loss.backward()
        optimizer.step()

    return model

# 示例数据
Y = np.array([1, 3, 5, 7, 9, 11])
p = 2
hidden_size = 32
learning_rate = 0.001
num_epochs = 1000

# 训练LSTM模型
model = lstm_autoregressive_model(Y, p, hidden_size, learning_rate, num_epochs)

# 获取模型参数
weights = model.state_dict()

# 输出模型参数
print("LSTM自回归模型参数:")
print("LSTM Weight_ih:", weights['lstm.weight_ih_l0'].numpy())
print("LSTM Weight_hh:", weights['lstm.weight_hh_l0'].numpy())
print("LSTM Bias_ih:", weights['lstm.bias_ih_l0'].numpy())
print("LSTM Bias_hh:", weights['lstm.bias_hh_l0'].numpy())

在上述代码中,我们使用PyTorch库分别构建了基于RNN和LSTM的自回归模型。这些模型具有更好的记忆能力,能够更好地捕捉时间序列数据中的长期依赖关系。通过训练这些模型,可以获得更准确的时间序列预测结果。

两个代码都是采用的同样的框架,即:
Input -> RNN/LSTM -> Fully Connected Layer -> Output

区别在于,当输入数据进入RNN层,在RNN层内部,它使用了一个隐藏状态来保存过去的信息。而对于LSTM模型,在输入数据进入LSTM层时,LSTM网络通过使用门控单元来更好地捕捉长期依赖关系,并且能够保留和遗忘过去的信息。

基于RNN的自回归模型和基于LSTM的自回归模型都具有循环结构,可以通过时间展开(unfold in time)来更好地理解。在时间展开的模型中,每个时间步都有一个相同的模型副本,每个副本使用上一时间步的输出作为输入。这种结构使得模型能够捕捉时间序列数据中的长期依赖关系。

具体来说,RNN和LSTM可以被表示为如下的递归方程:

RNN:
h_t = tanh(W_xhx_t + W_hhh_(t-1) + b_h)
y_t = W_hy*h_t + b_y

LSTM:
i_t = σ(W_iix_t + b_ii + W_hih_(t-1) + b_hi)
f_t = σ(W_ifx_t + b_if + W_hfh_(t-1) + b_hf)
g_t = tanh(W_igx_t + b_ig + W_hgh_(t-1) + b_hg)
o_t = σ(W_iox_t + b_io + W_hoh_(t-1) + b_ho)
c_t = f_t * c_(t-1) + i_t * g_t
h_t = o_t * tanh(c_t)

其中,h_t表示时间步t的隐藏状态,x_t表示时间步t的输入,y_t表示时间步t的输出,i_t、f_t、g_t、o_t分别表示输入门、遗忘门、候选值和输出门,c_t表示细胞状态。W和b分别表示权重和偏置项。

通过这些递归方程,RNN和LSTM能够在时间序列数据中,持续地更新隐藏状态和单元状态,从而更好地捕捉时间序列数据中的长期依赖关系。
不过,书中作者也陈述了使用RNN参数化模型存在的问题:

  1. RNN是序列化的,所以很慢。
  2. 如果RNN的设置不合适,比如,其权重矩阵的特征值大于或小于1,则RNN会分别产生梯度爆炸或梯度消失问题,从而无法学习到长距地以来关系。

于是,作者又提出使用卷积神经网络来进行模型的参数化。

基于卷积神经网络的长距记忆

作者在书中说,卷积神经网络(Convolutional Neural Network, CNN)可以代替RNN来为长距依赖关系建模,这似乎有些不可思议。根据之前的经验,CNN一般都是用在图像处理领域,很少被用在处理时间序列的问题上,问了GPT老师,也得到了类似的答案:

GPT老师:

卷积神经网络(CNN)通常不被视为能够直接替代RNN和LSTM来建模长距离依赖关系的理想选择。虽然CNN在图像处理和空间特征提取方面表现出色,但它在处理序列数据和捕捉时间上下文方面的能力相对较弱。

RNN和LSTM的主要优势在于它们的循环结构,可以保留过去的信息并将其传递到未来的时间步。这允许它们能够更好地捕捉时间序列数据中的长距离依赖关系。相比之下,CNN是一种局部感知的模型,它在局部区域上提取特征,没有显式的时间连接。因此,CNN并没有直接建模长距离依赖关系的能力。

然而,可以使用CNN来作为RNN或LSTM的辅助模块来提取时间序列数据的空间特征。例如,可以将CNN用于预处理步骤,提取时间序列中的局部特征,然后将这些特征输入到RNN或LSTM模型中进行进一步的建模和预测。这种结合使用的方法可以在一定程度上结合CNN和RNN/LSTM的优势,提高建模能力。

总的来说,尽管CNN在处理图像等领域表现出色,但在处理序列数据和捕捉时间依赖关系方面,RNN和LSTM更为适用。它们的循环结构和记忆单元使它们能够更好地处理长距离依赖关系,适用于时间序列数据建模和预测的任务。

为了了解书中是如何实现的,还是仔细来看作者的介绍。

《深度生成模型》P17 图片

看图中下方的图注,其中提到了因果卷积,GPT老师对其的解释是:

因果卷积(causal convolution)是一种在信号处理和时间序列数据分析中常用的卷积操作。与传统的卷积操作不同,因果卷积只考虑过去时刻的输入数据对当前时刻的输出有影响,而不考虑未来时刻的输入。这种特性使得因果卷积在处理时间序列数据时能够保持因果性,即输出的每个时刻只依赖于过去时刻的输入。

在因果卷积中,对于给定的输入序列和卷积核(滤波器),卷积操作从输入序列的起始位置开始,逐步滑动卷积核,将卷积核的权重与对应位置的输入值相乘并求和,得到输出序列的每一个时刻的值。

作者在书中也提到:“一维卷积层Conv1D可以被用来计算嵌入向量,但却不可以直接为自回归模型所用。因为我们需要卷积层是符合因果的。”

这里因果卷积的应用,就很好的解决了自回归模型的时间序列问题。

感谢大家能耐着性子看到这里,下一节笔记,会进入自回归模型的实操部分,我打算还是以作者的代码为主进行学习。

  • 25
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值