rnn,lstm 拙见

首先来简谈一下梯度消失和梯度爆炸。这个概念很常见,梯度消失指在反向传播过程中出现由于某一层的导数是0导致之后所有层的导数都是0从而导致权重参数得不到改变,从而导致训练结果不好。导数是针对激活函数,所以这个主要取决于激活函数,当然还和网络结构有关系。经典的例子就是残差网络ResNet,在这个网络中因为有跳跃连接所以参数是共享的,只要有一层参数发生变话所有层都会有一定改变。从而解决了梯度消失的问题。

梯度爆炸指进行反向传播时导数随着梯度的增加而变得差别很大,导致权重变化过大,导致网络结构不稳定,训练效果不好。这个与激活函数有关系,优化器,学习率等有关。

Rnn,循环神经网络:

rnn解决了dnn的时间序列依赖问题。dnn很简单就是线形层堆叠。这样的结构导致对于有先后顺序的序列没有识别先后的能力。rnn就是加入了上下文,可以让下一个输入中加入先前输入的一些特性。保证了时间序列之间存在依赖。

源码:

class SimpleRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleRNN, self).__init__()
        self.hidden_size = hidden_size
        self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
        self.h2o = nn.Linear(hidden_size, output_size)
        self.tanh = nn.Tanh()

    def forward(self, input, hidden):
        combined = torch.cat((input, hidden), 1)
        hidden = self.tanh(self.i2h(combined))
        output = self.h2o(hidden)
        return output, hidden

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

# 超参数
input_size = 1
hidden_size = 50
output_size = 1
learning_rate = 0.001
num_epochs = 50

# 模型实例化
model = SimpleRNN(input_size, hidden_size, output_size)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# 训练模型
 hidden = model.init_hidden()
for epoch in range(num_epochs):
    for batch_X, batch_y in dataloader:
      
        optimizer.zero_grad()
        
        loss = 0
        for t in range(batch_X.size(1)):
            output, hidden = model(batch_X[:, t], hidden)
            loss += criterion(output, batch_y[:, t])
        
        loss.backward()
        optimizer.step()
    
    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

self.i2h = nn.Linear(input_size + hidden_size, hidden_size)

self.h2o = nn.Linear(hidden_size, output_size)

这个input_size + hidden_size这个就是精髓,输入的数据要加上上一层的一些数据,就是在这里体现。hidden_size这个就是代表上一层数据的维度。combined = torch.cat((input, hidden), 1)这个是在做数据处理把输入数据和上一层隐层状态给弄成一维数组。hidden = self.tanh(self.i2h(combined))这个是得到新的hidden值,可以看出来这个hidden值是指的经过激活函数之后的值。return output, hidden返回结果和这个新的hidden值。hidden = model.init_hidden()这是对hidden进行初始化。output, hidden = model(batch_X[:, t], hidden)这里就是为什么要返回hidden,这里就是在更新hidden把新的hidden传入这个模型。

Lstm(长短期记忆网络):

这个进一步解决了rnn的问题,注意到这个rnn是存在一个问题就是他每一步都存放了前一个值得一些信息,这样子做,会导致记忆很多不重要的信息,重要的信息占比会少,导致整个效果不好。lstm就是改善了这个问题,对前一个值中的一些信息做一些筛选,保留有用的信息,去除掉一些没有用得信息,他定义了三个门来解决这个问题,输入门,输出门,遗忘门。

ht-1是上一个cell这里把上一个值得一些信息定义为cell,xt是输入得信息。b是偏执。

遗忘门是得到一个值位于0-1之间作用是确定cell中又多少信息是需要被保留的。比如ft为0.8代表上一个cell中80%的信息被保留。同理输入门是确定输入中有多少信息是被保存的。细胞更新状态就是当前传递给下一个的cell是多少。第二个输出们操作就是这一层的输出真实值。

ok上述解释非常完美,上源码:

import torch
import torch.nn as nn
import torch.optim as optim

class CustomLSTM(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(CustomLSTM, self).__init__()
        self.hidden_size = hidden_size
        
        # 遗忘门参数
        self.W_f = nn.Parameter(torch.Tensor(hidden_size, hidden_size + input_size))
        self.b_f = nn.Parameter(torch.Tensor(hidden_size))
        
        # 输入门参数
        self.W_i = nn.Parameter(torch.Tensor(hidden_size, hidden_size + input_size))
        self.b_i = nn.Parameter(torch.Tensor(hidden_size))
        
        # 候选记忆细胞参数
        self.W_C = nn.Parameter(torch.Tensor(hidden_size, hidden_size + input_size))
        self.b_C = nn.Parameter(torch.Tensor(hidden_size))
        
        # 输出门参数
        self.W_o = nn.Parameter(torch.Tensor(hidden_size, hidden_size + input_size))
        self.b_o = nn.Parameter(torch.Tensor(hidden_size))
        
        self.init_weights()

    def init_weights(self):
        for param in self.parameters():
            nn.init.uniform_(param, -0.08, 0.08)

    def forward(self, x, hidden, cell):
        combined = torch.cat((hidden, x), 1)
        
        f_t = torch.sigmoid(torch.matmul(combined, self.W_f.T) + self.b_f)
        i_t = torch.sigmoid(torch.matmul(combined, self.W_i.T) + self.b_i)
        C_tilde = torch.tanh(torch.matmul(combined, self.W_C.T) + self.b_C)
        C_t = f_t * cell + i_t * C_tilde
        o_t = torch.sigmoid(torch.matmul(combined, self.W_o.T) + self.b_o)
        h_t = o_t * torch.tanh(C_t)
        
        return h_t, C_t

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

# 设置模型参数
input_size = 10
hidden_size = 20
batch_size = 5
sequence_length = 15

# 创建模型实例
model = CustomLSTM(input_size, hidden_size)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 输入数据
inputs = torch.randn(sequence_length, batch_size, input_size)
targets = torch.randn(batch_size, hidden_size)

# 训练过程
num_epochs = 100
hidden, cell = model.init_hidden(batch_size)
for epoch in range(num_epochs):
    
    model.zero_grad()
    
    for t in range(sequence_length):
        hidden, cell = model(inputs[t], hidden, cell)
    
    loss = criterion(hidden, targets)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

f_t = torch.sigmoid(torch.matmul(combined, self.W_f.T) + self.b_f) i_t = torch.sigmoid(torch.matmul(combined, self.W_i.T) + self.b_i) C_tilde = torch.tanh(torch.matmul(combined, self.W_C.T) + self.b_C) C_t = f_t * cell + i_t * C_tilde o_t = torch.sigmoid(torch.matmul(combined, self.W_o.T) + self.b_o) h_t = o_t * torch.tanh(C_t)

整个对应的是函数实现。nn.Parameter(torch.Tensor(hidden_size))这是偏执。hidden, cell = model(inputs[t], hidden, cell)整个跟rnn同理

需要注意的是,这个整个lstm这只是一层,所以你会看到这些定义的层之间没有什么输入输出限制,因为这里这么多层只是为了得到一层的输出。

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值