【35】Sequence序列网络介绍与使用(含RNN,RNNCell,LSTM,LSTMCell的调用)

简介: 【35】Sequence序列网络介绍与使用(含RNN,RNNCell,LSTM,LSTMCell的调用)

在上一篇笔记中,了解了可以使用各种编码的方式对一句文本进行编码为一个特征向量,处理的方法可以有词频处理,权重处理或者是哈希编码处理等等。那么有了特征向量就可以实现对当前的文本进行分类处理,就是简单的再使用其他的分类器。


而对于文本,在深度学习领域一般是用时序网络来解决这些问题,实现网络的end-to-end预测。这里的时序网络可以使用RNN、GRU、LSTM、Transformer等知名架构。对于这些实现网络,其处理的过程和之前的不同,这里是将每一个单词进行一个编码,可以使用pytorch提供的查找表,就是在每一个表中查找对于的编码向量。所以处理的输入维度变成了:[seq, batch, embedding]


下面,就介绍以[seq, batch, embedding]为输出的方式,使用各项接口来搭建RNN或者是LSTM网络,以后续实现时序预测与文本分类任务。


!nvidia-smi


Tue May 10 20:27:38 2022       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla T4            Off  | 00000000:17:00.0 Off |                    0 |
| N/A   39C    P8     9W /  70W |      0MiB / 15109MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  Tesla T4            Off  | 00000000:65:00.0 Off |                    0 |
| N/A   77C    P0    66W /  70W |   5472MiB / 15109MiB |     99%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   2  Quadro P400         Off  | 00000000:B4:00.0 Off |                  N/A |
| 34%   36C    P8    N/A /  N/A |    103MiB /  1997MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    1   N/A  N/A     14250      C   python                           5465MiB |
|    2   N/A  N/A      3147      G   /usr/bin/X                         94MiB |
|    2   N/A  N/A      3221      G   /usr/bin/gnome-shell                5MiB |
+-----------------------------------------------------------------------------+


开始前,导入所需要的工具包


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


1. 时间序列的表示方法


序列的信息其实比较常见,文本是序列信息,语音是序列信息,图像也可以看成是一行行像素的组合其同样可以看成是序列的信息。这里主要对文本信息进行描述:


单词的编码形式:[words, word_vec]

这里有两种现成的编码方法:word2vec、glove


Batch的编码形式:[word num, b, word vec ] 、 [b, word num, word vec]

这里,如果是对图像处理比较熟悉的可能会举得第二种编码形式比较容易理解,就是传入一批数据,每一篇都有N个词,每个词编码为一个长度为word vec的特征向量。

但是,其实第一种是比较符合时序模型的理解的,有N个单词,每个单词都有b个批次也是也就是b个序列的时间搓,然后每个对应的时间戳同样编码为一个长度为word vec的特征向量。所以,这样不要把b理解为是一个批量,而是要将其理解为一个序列的时间戳。因为对于序列来说,肯定是有多个时间戳的。


1.1 word2vec编码实现

一般来说,可以通过nn.Embedding来生成一个查找表,对于每个单词给出一个编码的特征向量。如果是按一下的方法来进行设置,那么其是随机生成的,没有进行初始化的。


import torch
import torch.nn as nn
word_to_ix = {'hello':0, 'world':1}
lookup_tensor = torch.tensor([word_to_ix['hello']], dtype=torch.long)
embeds = nn.Embedding(2, 5)
embeds(lookup_tensor)
tensor([[ 0.8536,  0.6441, -0.1695,  0.5107,  0.0300]],
       grad_fn=<EmbeddingBackward>)


那么为了可以有一个好的初始化,可以使用一个现成的方式比如word2vec与GloVe,把这些数据集下载下来(大约会有几Gb的样子),然后就会把这个数据的内容填充进这个查找表中就可以直接使用了。但是这种查表的操作,是不能优化训练的。


1.2 GloVe编码实现

还可以通过pytorch nlp提供的另外一个编码方式,下载下来大概需要2.2G,直接输入单词就可以编码为一个特征向量。

from torchnlp.word_to_vector import GloVe, BPEmb
vectors = GloVe()    # 使用GloVe对单词文本进行编码
# vectors = BPEmb()  # 使用Embedding对单词文本进行编码
vectors['hello']
# 返回的是一个100位的特征向量,实现文本单词的编码


2. RNN


假设输入的维度x形式为:[seq len, batch, feature len],比如设置为[10, 3, 100]。


也就是说,这里有10个单词,总共有3句话,其中为每个单词编码为一个20位的特征向量。但是这里每一次送入的是一个feature,也就是每一个单词对于的3个句子的特征编码,这里记录为Xt:[batch,feature len],也就是每隔送入的特征shape为[3, 100]。


那么,现在根据pytorch实现的rnn的公式为:

image.png


也就是说,这里初始化提供的h t − 1 的维度需要与句子数保持一致,也就是说其维度需要设置为:[b, hidden len]。只有这样,才能和句子每个feature也就是Xt与Wih矩阵相乘后的结果相加,以为需要维度一致才可以相加。如下图所示:

image.png


2.1 单层rnn测试

import torch.nn as nn
rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=1)
print(rnn)
print(rnn._parameters.keys())
print("Wih:{} Whh:{}".format(rnn.weight_ih_l0.shape, rnn.weight_hh_l0.shape))
print("Bih:{} Bhh:{}".format(rnn.bias_ih_l0.shape, rnn.bias_hh_l0.shape))
print()
x = torch.randn(10, 3, 100)
out, h = rnn(x, torch.zeros(1, 3, 20))
print(out.shape, h.shape)
RNN(100, 20)
odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0'])
Wih:torch.Size([20, 100]) Whh:torch.Size([20, 20])
Bih:torch.Size([20]) Bhh:torch.Size([20])
torch.Size([10, 3, 20]) torch.Size([1, 3, 20])


对于单层RNN来说, RNN这里的input_size就是每个文本/样本的特征编码,如果对于单词来说,可以使用一些编码工具word2vec将单词编码为一个特征向量,比如设置为100的特征向量;如果是对于一些序列信息,比如天气温度预测啥的,那么这个就是一个值,可以直接设置为1;


然后,这里的hidden_size相当于全连接中的一个隐藏层,可以负责升维或者是降维的操作。对于单词来说,如果编码为一个100的vec,然后隐藏层hidden_size设置为20,那么就表示对100维的特征向量进行了降维。


由于rnn需要考虑当前的隐藏信息与当前的输入,所以除了有一个矩阵W i h  对输入x t  进行降维,还需要初始化设置一个隐藏单元h 0  。这里的隐藏单元的batch维度需要与x的batch维度保持一致,否则两个矩阵之间无法相加。然后对于dim=0的维度是1,表示当前只有一层RNN,对于只需要对这1维的进行初始化即可,num_layers=k时,这里的dim=0也需要设置为k。


最后就是输出,输出的维度这里可以看见是[10, 3, 20]。由于这里的输入是[10, 3, 100],表示有10个这样的单词,但是需要知道网络是不是一次次的逐个单词喂进去的,而是一次性的全部维进去模型里面的。所以out表示的是每个隐藏单元[h0, h1,…,hi]最后一层的输出。也就是,最后返回的h是最后一个时间隐藏层的状态,所以out可以说是最后一个聚合的信息。那么,这就说明,out的最后一个隐藏单元信息与返回的隐藏单元信息h是一直所,下面来做一个验证:(out[-1] == h).all() 返回的是tensor(True),说明我们的推理是没有错的。


2.2 多层rnn测试

rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=4)
print(rnn)
print(rnn._parameters.keys())
print("Wih:{} Whh:{}".format(rnn.weight_ih_l0.shape, rnn.weight_hh_l0.shape))
print("Bih:{} Bhh:{}".format(rnn.bias_ih_l0.shape, rnn.bias_hh_l0.shape))
print()
x = torch.randn(10, 3, 100)
out, h = rnn(x, torch.zeros(4, 3, 20))
print(out.shape, h.shape)
RNN(100, 20, num_layers=4)
odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0', 'weight_ih_l1', 'weight_hh_l1', 'bias_ih_l1', 'bias_hh_l1', 'weight_ih_l2', 'weight_hh_l2', 'bias_ih_l2', 'bias_hh_l2', 'weight_ih_l3', 'weight_hh_l3', 'bias_ih_l3', 'bias_hh_l3'])
Wih:torch.Size([20, 100]) Whh:torch.Size([20, 20])
Bih:torch.Size([20]) Bhh:torch.Size([20])
torch.Size([10, 3, 20]) torch.Size([4, 3, 20])


当有单层的rnn变成多层的rnn时,也就意味着此时对于下一层RNN的输入不是原来的特征,而是经过第一层rnn处理后的特征。而此时,对于每一层的RNN,都需要一个初始化的隐藏单元hi,对于正如上诉所说的,这里dim=0的维度的设置需要与num_layers设置得相同。尽管如此,每一层的隐含单元都是与单层rnn的维度是一致的。


所以,在多层的rnn模型下,out的输出是不变的,但是h的维度会改变,因为会输出每一层的隐藏单元。如下图所示:

image.png


而此时,out同样是最后输出的集合,表示为[h0,h1,…,hi]的组合叠加;而h则是每一层最后隐藏单元的集合;前者out是横向维度上的拼接,后者h是列项维度上的拼接。那么,这就以为这out的最后一个输出与h的最后一个隐藏单元其实是一样的,这样同样再做一个测试:(h[-1] == out[-1]).all(),这里返回为tensor(True);证明猜想是正确的。


3. RNNCell


3.1 单层RNNCell

cell_1 = nn.RNNCell(100, 20)
print(cell_1)
print(cell_1._parameters.keys())
print("Wih:{} Whh:{}".format(cell_1.weight_ih.shape, cell_1.weight_hh.shape))
print("Bih:{} Bhh:{}".format(cell_1.bias_ih.shape, cell_1.bias_hh.shape))
print()
x = torch.randn(10, 3, 100)
h1 = torch.zeros(3, 20)
for xt in x:
    h1 = cell_1(xt, h1)
print(h1.shape)


RNNCell(100, 20)
odict_keys(['weight_ih', 'weight_hh', 'bias_ih', 'bias_hh'])
Wih:torch.Size([20, 100]) Whh:torch.Size([20, 20])
Bih:torch.Size([20]) Bhh:torch.Size([20])
torch.Size([3, 20])


这里由于是使用了单个rnn单元,所以最后只有一个输出,就是循环处理的隐藏层单元。对于输入的文本,会分批次的输入到rnncell中,因为rnncell一次只会处理一个单词,或者是一个单词的编码。所以,需要不断的迭代输出。最后输出与隐藏单元就是同一个内容,就是h1.


3.2 多层RNNCell

# 设置多个Rnncell
cell_1 = nn.RNNCell(80, 50)
cell_2 = nn.RNNCell(50, 20)
cell_3 = nn.RNNCell(20, 10)
# 初始化隐藏层hi
x = torch.rand([10, 3, 80])
h1 = torch.zeros([3, 50])
h2 = torch.zeros([3, 20])
h3 = torch.zeros([3, 10])
# 对于每个特征进行迭代训练
for xi in x:
    h1 = cell_1(xi, h1)
    h2 = cell_2(h1, h2)
    h3 = cell_3(h2, h3)
print(h1.shape, h2.shape, h3.shape)


torch.Size([3, 50]) torch.Size([3, 20]) torch.Size([3, 10])


对于这里不同的rnncell单元的输出是不一样的,一方面可以其到降维的作用,如果想要类似rnn的效果,那么隐藏层维度设置为一样就可以了


# 设置多个Rnncell
cell_1 = nn.RNNCell(80, 80)
cell_2 = nn.RNNCell(80, 80)
cell_3 = nn.RNNCell(80, 80)
# 初始化隐藏层hi
x = torch.rand([10, 3, 80])
h1 = torch.zeros([3, 80])
h2 = torch.zeros([3, 80])
h3 = torch.zeros([3, 80])
# 对于每个特征进行迭代训练
for xi in x:
    h1 = cell_1(xi, h1)
    h2 = cell_2(h1, h2)
    h3 = cell_3(h2, h3)
print(h1.shape, h2.shape, h3.shape)


torch.Size([3, 80]) torch.Size([3, 80]) torch.Size([3, 80])


如此设置,就可以保持输入的特征维度不变


4. LSTM


对于lstm来说,在pytorch中提供的公式如下图所示:

image.png


相比rnn,lstm对了3个门控的机制,可以有效的处理rnn梯度弥散与梯度爆炸的情况。而相比与rnn,lstm的使用其实是类似的,但是多了一个输入的参数,也就是多了一个控制单元c0,所以现在需要对两个控制单元隐藏状态h0与门控状态c0进行初始化即可。


4.1 单层lstm

import torch
import torch.nn as nn
lstm = nn.LSTM(input_size=100, hidden_size=20, num_layers=1)
print(lstm)
print(lstm._parameters.keys())
print("Wih:{} Whh:{}".format(lstm.weight_ih_l0.shape, lstm.weight_hh_l0.shape))
print("Bih:{} Bhh:{}".format(lstm.bias_ih_l0.shape, lstm.bias_hh_l0.shape))
print()
x = torch.rand([10, 3, 100])
h0 = torch.zeros([1, 3, 20])
c0 = torch.zeros([1, 3, 20])
out, (ht, ct) = lstm(x, (h0, c0))
print(out.shape, ht.shape, ct.shape)
LSTM(100, 20)
odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0'])
Wih:torch.Size([80, 100]) Whh:torch.Size([80, 20])
Bih:torch.Size([80]) Bhh:torch.Size([80])
torch.Size([10, 3, 20]) torch.Size([1, 3, 20]) torch.Size([1, 3, 20])


可以看见,两个状态h0与c0最后的维度其实是没有改变的。而对于out来说,其同样是有最后的隐藏单元的拼接,(out[-1] == ht).all()的返回同样是tensor(True),所以可以证实结论。


4.2 多层lstm

lstm = nn.LSTM(input_size=100, hidden_size=20, num_layers=4)
print(lstm)
print(lstm._parameters.keys())
print("Wih:{} Whh:{}".format(lstm.weight_ih_l0.shape, lstm.weight_hh_l0.shape))
print("Bih:{} Bhh:{}".format(lstm.bias_ih_l0.shape, lstm.bias_hh_l0.shape))
print()
x = torch.rand([10, 3, 100])
h0 = torch.zeros([4, 3, 20])
c0 = torch.zeros([4, 3, 20])
out, (ht, ct) = lstm(x, (h0, c0))
print(out.shape, ht.shape, ct.shape)


LSTM(100, 20, num_layers=4)
odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0', 'weight_ih_l1', 'weight_hh_l1', 'bias_ih_l1', 'bias_hh_l1', 'weight_ih_l2', 'weight_hh_l2', 'bias_ih_l2', 'bias_hh_l2', 'weight_ih_l3', 'weight_hh_l3', 'bias_ih_l3', 'bias_hh_l3'])
Wih:torch.Size([80, 100]) Whh:torch.Size([80, 20])
Bih:torch.Size([80]) Bhh:torch.Size([80])
torch.Size([10, 3, 20]) torch.Size([4, 3, 20]) torch.Size([4, 3, 20])


类似的,这里同样进行测试,(out[-1] == ht[-1]).all(),返回的是tensor(True)。


4.3 单层双向lstm

示意图:

image.png


公式为:

image.png


有时候希望预测的输出由前面的输入和后面的输入共同决定,从而提高准确度。Forward层和Backward层共同连接到输出层。


Forward层从1到t时刻正向计算,得到并保存每个时刻的隐藏层的输出向后传播;Backward层从t时刻向1反向传播,得到并保存每个时刻向后隐藏层的输出。最后每个时刻结合Farward和Backward层的相应输出的结果通过激活函数得到最终的结果。


import torch.nn as nn
lstm = nn.LSTM(input_size=100, hidden_size=20, num_layers=1, bidirectional=True)
print(lstm)
print(lstm._parameters.keys())
print("Wih:{} Whh:{}".format(lstm.weight_ih_l0.shape, lstm.weight_hh_l0.shape))
print("Bih:{} Bhh:{}".format(lstm.bias_ih_l0.shape, lstm.bias_hh_l0.shape))
print("rWih:{} rWhh:{}".format(lstm.weight_ih_l0_reverse.shape, lstm.weight_hh_l0_reverse.shape))
print("rBih:{} rBhh:{}".format(lstm.bias_ih_l0_reverse.shape, lstm.bias_hh_l0_reverse.shape))
print()
x = torch.rand([10, 3, 100])
h0 = torch.zeros([2, 3, 20])
c0 = torch.zeros([2, 3, 20])
out, (ht, ct) = lstm(x, (h0, c0))
print(out.shape, ht.shape, ct.shape)


LSTM(100, 20, bidirectional=True)
odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0', 'weight_ih_l0_reverse', 'weight_hh_l0_reverse', 'bias_ih_l0_reverse', 'bias_hh_l0_reverse'])
Wih:torch.Size([80, 100]) Whh:torch.Size([80, 20])
Bih:torch.Size([80]) Bhh:torch.Size([80])
rWih:torch.Size([80, 100]) rWhh:torch.Size([80, 20])
rBih:torch.Size([80]) rBhh:torch.Size([80])
torch.Size([10, 3, 40]) torch.Size([2, 3, 20]) torch.Size([2, 3, 20])


这里由于是输出的前向ht与后向h’t的权重和构成的输出,所以out的最后一个内容与ht并不是完全相同的,也就说只有最后的最后的部分才是相同的。(out[-1][:, :20] == ht[0]).all(),才返回tensor(True)。 (out[-1][:, :20] == ht[-2]).all()也返回为Ture


out[-1][:, :20].shape 与 ht[0].shape 的输出均为torch.Size([3, 20])


4.4 多层双向lstm

import torch.nn as nn
lstm = nn.LSTM(input_size=100, hidden_size=20, num_layers=4, bidirectional=True)
print(lstm)
print(lstm._parameters.keys())
print("Wih:{} Whh:{}".format(lstm.weight_ih_l0.shape, lstm.weight_hh_l0.shape))
print("Bih:{} Bhh:{}".format(lstm.bias_ih_l0.shape, lstm.bias_hh_l0.shape))
print("rWih:{} rWhh:{}".format(lstm.weight_ih_l0_reverse.shape, lstm.weight_hh_l0_reverse.shape))
print("rBih:{} rBhh:{}".format(lstm.bias_ih_l0_reverse.shape, lstm.bias_hh_l0_reverse.shape))
print()
x = torch.rand([10, 3, 100])
h0 = torch.zeros([8, 3, 20])
c0 = torch.zeros([8, 3, 20])
out, (ht, ct) = lstm(x, (h0, c0))
print(out.shape, ht.shape, ct.shape)



LSTM(100, 20, num_layers=4, bidirectional=True)
odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0', 'weight_ih_l0_reverse', 'weight_hh_l0_reverse', 'bias_ih_l0_reverse', 'bias_hh_l0_reverse', 'weight_ih_l1', 'weight_hh_l1', 'bias_ih_l1', 'bias_hh_l1', 'weight_ih_l1_reverse', 'weight_hh_l1_reverse', 'bias_ih_l1_reverse', 'bias_hh_l1_reverse', 'weight_ih_l2', 'weight_hh_l2', 'bias_ih_l2', 'bias_hh_l2', 'weight_ih_l2_reverse', 'weight_hh_l2_reverse', 'bias_ih_l2_reverse', 'bias_hh_l2_reverse', 'weight_ih_l3', 'weight_hh_l3', 'bias_ih_l3', 'bias_hh_l3', 'weight_ih_l3_reverse', 'weight_hh_l3_reverse', 'bias_ih_l3_reverse', 'bias_hh_l3_reverse'])
Wih:torch.Size([80, 100]) Whh:torch.Size([80, 20])
Bih:torch.Size([80]) Bhh:torch.Size([80])
rWih:torch.Size([80, 100]) rWhh:torch.Size([80, 20])
rBih:torch.Size([80]) rBhh:torch.Size([80])
torch.Size([10, 3, 40]) torch.Size([8, 3, 20]) torch.Size([8, 3, 20])


(out[-1][:,:20] == ht[-2]).all() 返回为True,由于是双向,所以这里第一层是正向,第二层为反向,所以这里-1表示的是反向顺序,-2表示的正向顺序。


5. LSTMCell


image.png

5.1 单层LSTMCell

lstm_cell = nn.LSTMCell(input_size=100, hidden_size=20)
print(lstm_cell)
print("Wih:{}, Whh:{}".format(lstm_cell.weight_ih.shape, lstm_cell.weight_hh.shape))
print("Bih:{}, Bhh:{}".format(lstm_cell.bias_ih.shape, lstm_cell.bias_hh.shape))
print()
x = torch.rand([10, 3, 100])
h0 = torch.zeros([3, 20])
c0 = torch.zeros([3, 20])
for xt in x:
    h0, c0 = lstm_cell(xt, (h0, c0))
print(xt.shape, h0.shape, c0.shape)
LSTMCell(100, 20)
Wih:torch.Size([80, 100]), Whh:torch.Size([80, 20])
Bih:torch.Size([80]), Bhh:torch.Size([80])
torch.Size([3, 100]) torch.Size([3, 20]) torch.Size([3, 20])


lstmcell的使用与rnncell的类似,对于h0与c0不断地循环迭代,最后得到的h0就是最后的结果。此外,这里需要注意的是初始化的维度,只需要一个[batch, feacture vec]的维度就可以了。


5.2 多层LSTMCell

lstmcell_1 = nn.LSTMCell(input_size=80, hidden_size=50)
lstmcell_2 = nn.LSTMCell(input_size=50, hidden_size=20)
lstmcell_3 = nn.LSTMCell(input_size=20, hidden_size=10)
# 随机初始化输入
x = torch.rand([10, 3, 80])
# 初始化隐藏单元
h0 = torch.zeros([3, 50])
h1 = torch.zeros([3, 20])
h2 = torch.zeros([3, 10])
# 初始化控制单元
c0 = torch.zeros([3, 50])
c1 = torch.zeros([3, 20])
c2 = torch.zeros([3, 10])
for xt in x:
    h0, c0 = lstmcell_1(xt, (h0, c0))
    h1, c1 = lstmcell_2(h0, (h1, c1))
    h2, c2 = lstmcell_3(h1, (h2, c2))
print(h0.shape, h1.shape, h2.shape)


torch.Size([3, 50]) torch.Size([3, 20]) torch.Size([3, 10])


这里类似rnncell的同样设置的方法,简单的处理过程就是对于第一层的lstm,使用lstmcell_1处理好第一个单词之后,lstmcell_1的输出h0(已更新状态)作为下一层lstm层的输入,同时也作为当前层的当前时刻的一个隐藏状态。而下一层也就是lstmcell_2获取到上一层的输出h0之后,以及初始化的隐藏层单元h1,就会更新这个隐藏层单元作为当前的一个隐藏状态,作为当前层的输出。同样的,这个输出又会进一步的交给下层lstmcell_3进行处理,此时更新好的h2就会作为当前这个这个单词文本的输出。然后,循环遍历整个输入文本,逐一处理好每一个单词,这个过程中就是不断的更新每一层的隐藏单元。最后处理完之后,最后一层的LSTMCell就会更新出也称为是输出的隐藏单元就是最后的结果。


import numpy as np
lstmcell_1 = nn.LSTMCell(input_size=80, hidden_size=80)
lstmcell_2 = nn.LSTMCell(input_size=80, hidden_size=80)
lstmcell_3 = nn.LSTMCell(input_size=80, hidden_size=80)
# 随机初始化输入
x = torch.rand([10, 3, 80])
# 初始化隐藏单元
h0 = torch.zeros([3, 80])
h1 = torch.zeros([3, 80])
h2 = torch.zeros([3, 80])
# 初始化控制单元
c0 = torch.zeros([3, 80])
c1 = torch.zeros([3, 80])
c2 = torch.zeros([3, 80])
# 仿造lstm,保留每一个文本的输出特征
out = []
for xt in x:
    h0, c0 = lstmcell_1(xt, (h0, c0))
    h1, c1 = lstmcell_2(h0, (h1, c1))
    h2, c2 = lstmcell_3(h1, (h2, c2))
    out.append(h2.detach().numpy())
output = np.array(out)
print(h0.shape, h1.shape, h2.shape)
print("output.shape:", output.shape)


torch.Size([3, 80]) torch.Size([3, 80]) torch.Size([3, 80])
output.shape: (10, 3, 80)


如果想要保持维度的不变,那么可以将隐藏单元的维度设置为一样,这样最后的输出结果就会和输入的结果是一致的。


参考资料:

部分资料来源与龙曲良老师的pytorch教程与Pytorch的官方文档


相关实践学习
在云上部署ChatGLM2-6B大模型(GPU版)
ChatGLM2-6B是由智谱AI及清华KEG实验室于2023年6月发布的中英双语对话开源大模型。通过本实验,可以学习如何配置AIGC开发环境,如何部署ChatGLM2-6B大模型。
目录
相关文章
|
10月前
|
机器学习/深度学习 算法 安全
【PSO-LSTM】基于PSO优化LSTM网络的电力负荷预测(Python代码实现)
【PSO-LSTM】基于PSO优化LSTM网络的电力负荷预测(Python代码实现)
441 0
|
12月前
|
机器学习/深度学习 算法 数据挖掘
基于WOA鲸鱼优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本项目基于MATLAB 2022a/2024b实现,采用WOA优化的BiLSTM算法进行序列预测。核心代码包含完整中文注释与操作视频,展示从参数优化到模型训练、预测的全流程。BiLSTM通过前向与后向LSTM结合,有效捕捉序列前后文信息,解决传统RNN梯度消失问题。WOA优化超参数(如学习率、隐藏层神经元数),提升模型性能,避免局部最优解。附有运行效果图预览,最终输出预测值与实际值对比,RMSE评估精度。适合研究时序数据分析与深度学习优化的开发者参考。
|
9月前
|
机器学习/深度学习 数据采集 资源调度
基于长短期记忆网络定向改进预测的动态多目标进化算法(LSTM-DIP-DMOEA)求解CEC2018(DF1-DF14)研究(Matlab代码实现)
基于长短期记忆网络定向改进预测的动态多目标进化算法(LSTM-DIP-DMOEA)求解CEC2018(DF1-DF14)研究(Matlab代码实现)
389 0
|
8月前
|
机器学习/深度学习 自然语言处理 PyTorch
21_RNN与LSTM:序列建模的经典方法
在自然语言处理领域,处理序列数据是一个核心挑战。传统的机器学习方法难以捕捉序列中的时序依赖关系,而循环神经网络(Recurrent Neural Network,RNN)及其变种长短期记忆网络(Long Short-Term Memory,LSTM)通过其独特的循环结构,为序列建模提供了强大的解决方案。本教程将深入探讨RNN和LSTM的原理、实现方法和最新应用,帮助读者全面掌握这一NLP核心技术。
668 0
|
9月前
|
机器学习/深度学习 传感器 数据采集
基于贝叶斯优化CNN-LSTM混合神经网络预测(Matlab代码实现)
基于贝叶斯优化CNN-LSTM混合神经网络预测(Matlab代码实现)
1275 0
|
12月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本项目基于MATLAB2022a/2024b开发,结合粒子群优化(PSO)算法与双向长短期记忆网络(BiLSTM),用于优化序列预测任务中的模型参数。核心代码包含详细中文注释及操作视频,涵盖遗传算法优化过程、BiLSTM网络构建、训练及预测分析。通过PSO优化BiLSTM的超参数(如学习率、隐藏层神经元数等),显著提升模型捕捉长期依赖关系和上下文信息的能力,适用于气象、交通流量等场景。附有运行效果图预览,展示适应度值、RMSE变化及预测结果对比,验证方法有效性。
|
12月前
|
机器学习/深度学习 数据采集 算法
基于GWO灰狼优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本项目基于Matlab 2022a/2024b实现,结合灰狼优化(GWO)算法与双向长短期记忆网络(BiLSTM),用于序列预测任务。核心代码包含数据预处理、种群初始化、适应度计算及参数优化等步骤,完整版附带中文注释与操作视频。BiLSTM通过前向与后向处理捕捉序列上下文信息,GWO优化其参数以提升预测性能。效果图展示训练过程与预测结果,适用于气象、交通等领域。LSTM结构含输入门、遗忘门与输出门,解决传统RNN梯度问题,而BiLSTM进一步增强上下文理解能力。
|
12月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA遗传优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本内容包含基于BiLSTM与遗传算法(GA)的算法介绍及实现。算法通过MATLAB2022a/2024b运行,核心为优化BiLSTM超参数(如学习率、神经元数量),提升预测性能。LSTM解决传统RNN梯度问题,捕捉长期依赖;BiLSTM双向处理序列,融合前文后文信息,适合全局信息任务。附完整代码(含注释)、操作视频及无水印运行效果预览,适用于股票预测等场景,精度优于单向LSTM。
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN-LSTM时间卷积神经网络时间序列预测算法matlab仿真
本内容展示了一种基于粒子群优化(PSO)与时间卷积神经网络(TCN)的时间序列预测方法。通过 MATLAB2022a 实现,完整程序运行无水印,核心代码附详细中文注释及操作视频。算法利用 PSO 优化 TCN 的超参数(如卷积核大小、层数等),提升非线性时间序列预测性能。TCN 结构包含因果卷积层与残差连接,结合 LSTM 构建混合模型,经多次迭代选择最优超参数,最终实现更准确可靠的预测效果,适用于金融、气象等领域。
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM
本研究基于MATLAB 2022a,使用GRU网络对QAM调制信号进行检测。QAM是一种高效调制技术,广泛应用于现代通信系统。传统方法在复杂环境下性能下降,而GRU通过门控机制有效提取时间序列特征,实现16QAM、32QAM、64QAM、128QAM的准确检测。仿真结果显示,GRU在低SNR下表现优异,且训练速度快,参数少。核心程序包括模型预测、误检率和漏检率计算,并绘制准确率图。
425 65
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM

热门文章

最新文章