【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的官方文档


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
1月前
|
机器学习/深度学习 数据采集 存储
时间序列预测新突破:深入解析循环神经网络(RNN)在金融数据分析中的应用
【10月更文挑战第7天】时间序列预测是数据科学领域的一个重要课题,特别是在金融行业中。准确的时间序列预测能够帮助投资者做出更明智的决策,比如股票价格预测、汇率变动预测等。近年来,随着深度学习技术的发展,尤其是循环神经网络(Recurrent Neural Networks, RNNs)及其变体如长短期记忆网络(LSTM)和门控循环单元(GRU),在处理时间序列数据方面展现出了巨大的潜力。本文将探讨RNN的基本概念,并通过具体的代码示例展示如何使用这些模型来进行金融数据分析。
218 2
|
3月前
|
机器学习/深度学习 API 异构计算
7.1.3.2、使用飞桨实现基于LSTM的情感分析模型的网络定义
该文章详细介绍了如何使用飞桨框架实现基于LSTM的情感分析模型,包括网络定义、模型训练、评估和预测的完整流程,并提供了相应的代码实现。
|
11天前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
|
23天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于贝叶斯优化CNN-LSTM网络的数据分类识别算法matlab仿真
本项目展示了基于贝叶斯优化(BO)的CNN-LSTM网络在数据分类中的应用。通过MATLAB 2022a实现,优化前后效果对比明显。核心代码附带中文注释和操作视频,涵盖BO、CNN、LSTM理论,特别是BO优化CNN-LSTM网络的batchsize和学习率,显著提升模型性能。
|
1月前
|
机器学习/深度学习 存储 自然语言处理
从理论到实践:如何使用长短期记忆网络(LSTM)改善自然语言处理任务
【10月更文挑战第7天】随着深度学习技术的发展,循环神经网络(RNNs)及其变体,特别是长短期记忆网络(LSTMs),已经成为处理序列数据的强大工具。在自然语言处理(NLP)领域,LSTM因其能够捕捉文本中的长期依赖关系而变得尤为重要。本文将介绍LSTM的基本原理,并通过具体的代码示例来展示如何在实际的NLP任务中应用LSTM。
70 4
|
1月前
|
机器学习/深度学习 存储 自然语言处理
深度学习入门:循环神经网络------RNN概述,词嵌入层,循环网络层及案例实践!(万字详解!)
深度学习入门:循环神经网络------RNN概述,词嵌入层,循环网络层及案例实践!(万字详解!)
|
3月前
|
自然语言处理 C# 开发者
Uno Platform多语言开发秘籍大公开:轻松驾驭全球用户,一键切换语言,让你的应用成为跨文化交流的桥梁!
【8月更文挑战第31天】Uno Platform 是一个强大的开源框架,允许使用 C# 和 XAML 构建跨平台的原生移动、Web 和桌面应用程序。本文详细介绍如何通过 Uno Platform 创建多语言应用,包括准备工作、设置多语言资源、XAML 中引用资源、C# 中加载资源以及处理语言更改。通过简单的步骤和示例代码,帮助开发者轻松实现应用的国际化。
41 1
|
3月前
|
机器学习/深度学习 算法 数据挖掘
基于WOA优化的CNN-LSTM的时间序列回归预测matlab仿真
本项目采用MATLAB 2022a实现时间序列预测,利用CNN与LSTM结合的优势,并以鲸鱼优化算法(WOA)优化模型超参数。CNN提取时间序列的局部特征,LSTM处理长期依赖关系,而WOA确保参数最优配置以提高预测准确性。完整代码附带中文注释及操作指南,运行效果无水印展示。
|
3月前
|
机器学习/深度学习
【机器学习】面试题:LSTM长短期记忆网络的理解?LSTM是怎么解决梯度消失的问题的?还有哪些其它的解决梯度消失或梯度爆炸的方法?
长短时记忆网络(LSTM)的基本概念、解决梯度消失问题的机制,以及介绍了包括梯度裁剪、改变激活函数、残差结构和Batch Normalization在内的其他方法来解决梯度消失或梯度爆炸问题。
144 2
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
TensorFlow 中的循环神经网络超厉害!从理论到实践详解,带你领略 RNN 的强大魅力!
【8月更文挑战第31天】循环神经网络(RNN)在人工智能领域扮演着重要角色,尤其在TensorFlow框架下处理序列数据时展现出强大功能。RNN具有记忆能力,能捕捉序列中的长期依赖关系,适用于自然语言处理、机器翻译和语音识别等多个领域。尽管存在长期依赖和梯度消失等问题,但通过LSTM和GRU等改进结构可以有效解决。在TensorFlow中实现RNN十分简便,为处理复杂序列数据提供了有力支持。
35 0