Bootstrap

【作业九】RNN-SRN-Seq2Seq

点击查看作业内容


目录

1 实现SRN

(1)使用numpy实现

(2)在(1)的基础上,增加激活函数tanh

(3)使用nn.RNNCell实现

(4)使用nn.RNN实现

 2 使用RNNCell实现“序列到序列”

3 实现“编码器-解码器”-Seq2Seq

4 简单总结nn.RNNCell、nn.RNN

(1)nn.RNNCell

(2)nn.RNN

5 谈一谈对“序列”、“序列到序列”的理解

6 总结本周理论课和作业,写心得体会


1 实现SRN

        先回顾一下什么是SRN。

        SRN全称Simple Recurrent Network,即简单循环网络。SRN在传统的前馈神经网络的基础上,增加了一个隐藏层,这个隐藏层的输出会反馈到自身,形成一个循环。这样,网络就能够在时间上保持状态,即“记忆”之前的输入信息。()

(1)使用numpy实现

'''
@Function: 使用numpy实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import numpy as np
# 初始化输入序列
inputs = np.array([[1.,1.],[1.,1.],[2.,2.]])
print(f"inputs is:\n{inputs}")
# 初始化存储器
state_t = np.zeros(2)
print(f"states_t is:\n{state_t}")
# 初始化权重参数,这里所有权重都为1,bias = 0
w1, w2, w3, w4, w5, w6, w7, w8 = 1., 1., 1., 1., 1., 1., 1., 1.
U1, U2, U3, U4 = 1., 1., 1., 1.
print('============================================')
for t,input_t in enumerate(inputs):
    print(f"第{t+1}时刻:")
    print(f'input_t is: {input_t}')
    print(f'state_t is: {state_t}')
    # 隐藏层的输入=当前输入 + 前一时刻的状态
    input_h1 = np.dot([w1,w3],input_t) + np.dot([U2,U4],state_t)
    input_h2 = np.dot([w2,w4],input_t) + np.dot([U1,U3],state_t)
    state_t = input_h1, input_h2 # 更新状态(无激活 输入=输出):直接将计算得到的隐藏层输入赋值给state_t
    # 输入为隐藏层的输出,计算最终输出
    output_y1 = np.dot([w5, w7], state_t)
    output_y2 = np.dot([w6, w8], state_t)
    print(f"outputs is: {output_y1}、{output_y2}")
    print('============================================')

运行结果:

inputs is:
[[1. 1.]
 [1. 1.]
 [2. 2.]]
states_t is:
[0. 0.]
============================================
第1时刻:
input_t is: [1. 1.]
state_t is: [0. 0.]
outputs is: 4.0、4.0
============================================
第2时刻:
input_t is: [1. 1.]
state_t is: (2.0, 2.0)
outputs is: 12.0、12.0
============================================
第3时刻:
input_t is: [2. 2.]
state_t is: (6.0, 6.0)
outputs is: 32.0、32.0
============================================

可见与手动计算的结果一致 。

图源) 

(2)在(1)的基础上,增加激活函数tanh

'''
@Function: 使用numpy实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import numpy as np
# 初始化输入序列 3个对应三个时刻
inputs = np.array([[1.,1.],[1.,1.],[2.,2.]])
print(f"inputs is:\n{inputs}")
# 初始化存储器
state_t = np.zeros(2)
print(f"states_t is:\n{state_t}")
# 初始化权重参数,这里所有权重都为1,bias = 0
w1, w2, w3, w4, w5, w6, w7, w8 = 1., 1., 1., 1., 1., 1., 1., 1.
U1, U2, U3, U4 = 1., 1., 1., 1.
print('============================================')
for t,input_t in enumerate(inputs):
    print(f"第{t+1}时刻:")
    print(f'input_t is: {input_t}')
    print(f'state_t is: {state_t}')
    # 隐藏层的输入=当前输入 + 前一时刻的状态
    input_h1 = np.dot([w1,w3],input_t) + np.dot([U2,U4],state_t)
    input_h2 = np.dot([w2,w4],input_t) + np.dot([U1,U3],state_t)
    # state_t = input_h1, input_h2 # 更新状态(无激活 输出=输入):直接将计算得到的隐藏层输入赋值给state_t
    state_t = np.tanh(input_h1),np.tanh(input_h2) # 更新状态(有激活 输出=tanh(输入) )
    # 输入为隐藏层的输出,计算最终输出
    output_y1 = np.dot([w5, w7], state_t)
    output_y2 = np.dot([w6, w8], state_t)
    print(f"outputs is: {output_y1}、{output_y2}")
    print('============================================')

运行结果:

inputs is:
[[1. 1.]
 [1. 1.]
 [2. 2.]]
states_t is:
[0. 0.]
============================================
第1时刻:
input_t is: [1. 1.]
state_t is: [0. 0.]
outputs is: 1.9280551601516338、1.9280551601516338
============================================
第2时刻:
input_t is: [1. 1.]
state_t is: (0.9640275800758169, 0.9640275800758169)
outputs is: 1.9984510891336251、1.9984510891336251
============================================
第3时刻:
input_t is: [2. 2.]
state_t is: (0.9992255445668126, 0.9992255445668126)
outputs is: 1.9999753470497836、1.9999753470497836
============================================

 对比(1)(2)输出 结果可以看到无激活函数时,输出值随着时间步的增加而线性增长,从4.0增加到32.0。有激活函数时,输出值在经过激活函数后,增长速度明显减缓。

(3)使用nn.RNNCell实现

'''
@Function: 使用nn.RNNCell实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import torch
batch_size = 1
seq_len = 3  # 序列长度(多少时间步)
input_size = 2  # 输入序列维度
hidden_size = 2  # 隐藏层维度
output_size = 2  # 输出层维度

# # 创建RNNCell实例
cell = torch.nn.RNNCell(input_size=input_size,hidden_size=hidden_size)
# 初始化参数 https://zhuanlan.zhihu.com/p/342012463
'''
RNN的weight和bias封装在parameters中,且需要对weight和bias分开初始化
'''
for name,param in cell.named_parameters():
    if name.startswith('weight'): # 初始化weight
        torch.nn.init.ones_(param)
    else: # 初始化bias
        torch.nn.init.zeros_(param)

# 线性层-> 将隐藏状态映射到输出
linear = torch.nn.Linear(hidden_size,output_size)
# 初始化线性层的权重为1
linear.weight.data = torch.Tensor([[1,1],[1,1]])
# 初始化线性层的偏置为0
linear.bias.data = torch.Tensor([0.0])

# 定义输入序列
'''
三维,形状(3, 1, 2)
第一维:序列长度(seq_len),这里是3
第二维:批次大小(batch_size),这里是1
第三维:输入特征的维度(input_size),这里是2
'''
seq = torch.Tensor([[[1,1]],[[1,1]],[[2,2]]])
# 初始化隐藏状态和输出为0
hidden = torch.zeros(batch_size,hidden_size)
output = torch.zeros(batch_size,output_size)
# 遍历序列中的每一个时间步
for idx,input in enumerate(seq):
    print('===========================')
    print(f"第{idx+1}时刻:")
    print(f'Input :{input}')
    print(f'hidden :{hidden}')
    hidden = cell(input,hidden) # 使用RNNCell处理当前输入,并更新隐藏状态
    output = linear(hidden) # # 使用线性层将隐藏状态转换为输出
    print(f"output :{output}")

运行结果:

===========================
第1时刻:
Input :tensor([[1., 1.]])
hidden :tensor([[0., 0.]])
output :tensor([[1.9281, 1.9281]], grad_fn=<AddmmBackward0>)
===========================
第2时刻:
Input :tensor([[1., 1.]])
hidden :tensor([[0.9640, 0.9640]], grad_fn=<TanhBackward0>)
output :tensor([[1.9985, 1.9985]], grad_fn=<AddmmBackward0>)
===========================
第3时刻:
Input :tensor([[2., 2.]])
hidden :tensor([[0.9992, 0.9992]], grad_fn=<TanhBackward0>)
output :tensor([[2.0000, 2.0000]], grad_fn=<AddmmBackward0>)

(4)使用nn.RNN实现

'''
@Function: 使用nn.RNN实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import torch
# 设置批处理大小
batch_size = 1
# 设置序列长度
seq_len = 3
# 输入序列的维度
input_size = 2
# 隐藏层的维度
hidden_size = 2
# 输出层的维度
output_size = 2
# RNN层的数量
num_layers = 1

# 创建RNN实例
cell = torch.nn.RNN(input_size=input_size, hidden_size=hidden_size, num_layers=num_layers)
# 初始化参数
for name, param in cell.named_parameters():
    if name.startswith('weight'):
        torch.nn.init.ones_(param)
    else:
        torch.nn.init.zeros_(param)

# 线性层 将隐藏状态映射到输出
linear = torch.nn.Linear(hidden_size, output_size)
# 初始化线性层的权重为1
linear.weight.data = torch.Tensor([[1,1],[1,1]])
# 初始化线性层的偏置为0
linear.bias.data = torch.Tensor([0.0])

# 创建输入序列
'''
三维,形状(3, 1, 2)
第一维:序列长度(seq_len),这里是3
第二维:批次大小(batch_size),这里是1
第三维:输入特征的维度(input_size),这里是2
'''
inputs = torch.Tensor([[[1,1]],[[1,1]],[[2,2]]])
# 初始化隐藏状态为0,这里需要考虑RNN层的数量
hidden = torch.zeros(num_layers, batch_size, hidden_size)
# 通过RNN处理输入序列,并更新隐藏状态
out, hidden = cell(inputs, hidden)

# 打印第一个时间步的输入、隐藏状态和输出
print("第1时刻:")
print(f'Input : {inputs[0]}')
print(f'hidden: {[0 , 0]}')
print(f'Output: {linear(out[0])}')
print('======================================')
# 打印第二个时间步的输入、隐藏状态和输出
print("第2时刻:")
print(f'Input : {inputs[1]}')
print(f'hidden: {out[0]}')
print(f'Output: {linear(out[1])}')
print('=======================================')
# 打印第三个时间步的输入、隐藏状态和输出
print("第3时刻:")
print(f'Input : {inputs[2]}')
print(f'hidden: {out[1]}')
print(f'Output: {linear(out[2])}')

【这里不使用循环来打印的原因是因为RNN是一个封装好的循环神经网络层,处理整个序列的前向传播,并返回每个时间步的输出和最后一个时间步的隐藏状态,而RNNCell 是一个基本的循环神经网络单元,只处理单个时间步的输入,所以在(3)中需要循环调用】 

运行结果:

第1时刻:
Input : tensor([[1., 1.]])
hidden: [0, 0]
Output: tensor([[1.9281, 1.9281]], grad_fn=<AddmmBackward0>)
======================================
第2时刻:
Input : tensor([[1., 1.]])
hidden: tensor([[0.9640, 0.9640]], grad_fn=<SelectBackward0>)
Output: tensor([[1.9985, 1.9985]], grad_fn=<AddmmBackward0>)
=======================================
第3时刻:
Input : tensor([[2., 2.]])
hidden: tensor([[0.9992, 0.9992]], grad_fn=<SelectBackward0>)
Output: tensor([[2.0000, 2.0000]], grad_fn=<AddmmBackward0>)

 2 使用RNNCell实现“序列到序列”

b站循环神经网络讲解--刘二大人  实现视频P12中的教学案例 hello--->ohlol

 

'''
@Function: 实现序列到序列:给出hello -->预测ohlol
@Author: lxy
@Date: 2024/11/24
'''
import torch
import torch.nn as nn
import torch.optim as optim

# 定义参数
input_size = 4 # 输入特征的维度:对应的是 one-hot 编码的大小(4个字符)
hidden_size = 4 # 隐藏层维度
batch_size = 1  # 批次大小(这里是1,表示一次只处理一个字符)

# 字符到索引的映射
idx2char = ['e', 'h', 'l', 'o']

# 输入和标签数据
x_data = [1, 0, 2, 2, 3] # 对应字符 ['h', 'e', 'l', 'l', 'o']
y_data = [3, 1, 2, 3, 2] # 对应目标字符 ['o', 'h', 'l', 'o', 'l']

# 将字符映射到one-hot编码
one_hot_lookup = [
    [1, 0, 0, 0],  # 'e' 对应 [1, 0, 0, 0]
    [0, 1, 0, 0],  # 'h' 对应 [0, 1, 0, 0]
    [0, 0, 1, 0],  # 'l' 对应 [0, 0, 1, 0]
    [0, 0, 0, 1]   # 'o' 对应 [0, 0, 0, 1]
]
# 将输入数据 x_data 转换为 one-hot 编码形式
x_one_hot = [one_hot_lookup[x] for x in x_data]

# 将输入和标签转换为PyTorch张量
inputs = torch.Tensor(x_one_hot).view(-1, batch_size, input_size)  # (序列长度, 批次大小, 输入维度)
labels = torch.LongTensor(y_data).view(-1, 1)  # (序列长度, 1),每个标签对应一个字符的索引

# 定义RNN模型
class Model(nn.Module):
    def __init__(self, input_size, hidden_size, batch_size):
        super(Model, self).__init__()
        self.batch_size = batch_size
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.rnncell = nn.RNNCell(input_size=self.input_size, hidden_size=self.hidden_size)

    def forward(self, input, hidden):
        # RNNCell 处理每个输入,并返回新的隐藏状态
        hidden = self.rnncell(input, hidden)
        return hidden

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

# 初始化模型、损失函数和优化器
net = Model(input_size, hidden_size, batch_size)
criterion = nn.CrossEntropyLoss()  # 交叉熵损失函数
optimizer = optim.Adam(net.parameters(), lr=0.1)  # 使用 Adam 优化器

# 训练模型
for epoch in range(15):
    loss = 0
    optimizer.zero_grad()
    hidden = net.init_hidden() # 初始化隐藏状态
    print('Predicted string: ', end='')
    # 遍历输入序列和标签
    for input, label in zip(inputs, labels):
        hidden = net(input, hidden)  # 将输入传入网络并获取新的隐藏状态
        loss += criterion(hidden, label)  # 计算损失并累加
        _, idx = hidden.max(dim=1)   # 获取预测的字符索引(最大值)
        print(idx2char[idx.item()], end='')  # 输出对应的字符
    loss.backward()
    optimizer.step()
    print(', Epoch [%d/15] loss=%.4f' % (epoch + 1, loss.item()))

运行结果:

Predicted string: lllll, Epoch [1/15] loss=7.3776
Predicted string: lllll, Epoch [2/15] loss=6.0633
Predicted string: lhlll, Epoch [3/15] loss=4.9989
Predicted string: ohlol, Epoch [4/15] loss=4.0542
Predicted string: ohlol, Epoch [5/15] loss=3.3809
Predicted string: ohlol, Epoch [6/15] loss=3.0262
Predicted string: ohlol, Epoch [7/15] loss=2.7807
Predicted string: ohlol, Epoch [8/15] loss=2.5577
Predicted string: ohlol, Epoch [9/15] loss=2.3574
Predicted string: ohlol, Epoch [10/15] loss=2.2286
Predicted string: ohlol, Epoch [11/15] loss=2.1521
Predicted string: ohlol, Epoch [12/15] loss=2.0685
Predicted string: ohlol, Epoch [13/15] loss=1.9959
Predicted string: ohlol, Epoch [14/15] loss=1.9489
Predicted string: ohlol, Epoch [15/15] loss=1.9217

         可见模型的预测从初期的随机性lllll到 ohlol逐步改进,最终在第 4 个 epoch 开始预测正确的字符顺序。随着训练进行,损失逐渐下降,模型逐步学会了如何更准确地预测下一个字符。实现从hello到ohlol的转换。

3 实现“编码器-解码器”-Seq2Seq

Seq2Seq的Pytorch实现  ----b站配套讲解

seq2seq的PyTorch实现_哔哩哔哩_bilibili  ---代码实战

跟着问题学13——seq2seq详解及代码实战-CSDN博客

Seq2Seq原理详解 - 早起的小虫子 - 博客园

         Seq2seq模型通常用于将一个序列转换为另一个序列。这种模型由两部分组成:编码器(encoder)和解码器(decoder)。编码器将输入序列编码成一个固定长度的向量,解码器则从这个向量生成输出序列。

'''
@功能: 使用PyTorch实现Seq2Seq编码器-解码器: 将输入的英语单词翻译成西班牙语
@作者: lxy
@日期: 2024/11/24
'''

import torch
import numpy as np
import torch.nn as nn
import torch.utils.data as Data

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# S: 表示解码输入的开始符号
# E: 表示解码输出的结束符号
# ?: 用于填充空白序列,当当前批次的数据长度不足n_step时使用

letter = [c for c in 'SE?abcdefghijklmnopqrstuvwxyz']
letter2idx = {n: i for i, n in enumerate(letter)}

# 示例数据: 英语单词与对应的西班牙语翻译
seq_data = [
    ['hello', 'hola'],  # 'hello' -> 'hola'(英语到西班牙语)
    ['cat', 'gato'],    # 'cat' -> 'gato'
    ['good', 'bueno'],  # 'good' -> 'bueno'
]

# Seq2Seq 参数
n_step = max([max(len(i), len(j)) for i, j in seq_data])  # 最大长度(=5)
n_hidden = 128  # 隐藏层维度
n_class = len(letter2idx)  # 类别数(即字符集大小)
batch_size = 3  # 批次大小

# 函数:构建训练数据
def make_data(seq_data):
    enc_input_all, dec_input_all, dec_output_all = [], [], []

    for seq in seq_data:
        for i in range(2):
            seq[i] = seq[i] + '?' * (n_step - len(seq[i]))  # 如 'man??', 'women'

        # 编码输入:将字符转为索引,并在末尾添加结束符'E'
        enc_input = [letter2idx[n] for n in (seq[0] + 'E')]  # ['m', 'a', 'n', '?', '?', 'E']
        # 解码输入:在开头添加起始符'S'
        dec_input = [letter2idx[n] for n in ('S' + seq[1])]  # ['S', 'w', 'o', 'm', 'e', 'n']
        # 解码输出:在末尾添加结束符'E'
        dec_output = [letter2idx[n] for n in (seq[1] + 'E')]  # ['w', 'o', 'm', 'e', 'n', 'E']

        # 将每个输入转为独热编码
        enc_input_all.append(np.eye(n_class)[enc_input])
        dec_input_all.append(np.eye(n_class)[dec_input])
        dec_output_all.append(dec_output)  # 解码输出不进行独热编码

    # 返回Tensor格式数据
    return torch.Tensor(enc_input_all), torch.Tensor(dec_input_all), torch.LongTensor(dec_output_all)

# 获取训练数据
enc_input_all, dec_input_all, dec_output_all = make_data(seq_data)

# 自定义数据集
class TranslateDataSet(Data.Dataset):
    def __init__(self, enc_input_all, dec_input_all, dec_output_all):
        self.enc_input_all = enc_input_all
        self.dec_input_all = dec_input_all
        self.dec_output_all = dec_output_all

    def __len__(self):  # 返回数据集的大小
        return len(self.enc_input_all)

    def __getitem__(self, idx):
        return self.enc_input_all[idx], self.dec_input_all[idx], self.dec_output_all[idx]

# 数据加载器
loader = Data.DataLoader(TranslateDataSet(enc_input_all, dec_input_all, dec_output_all), batch_size, True)

# Seq2Seq模型
class Seq2Seq(nn.Module):
    def __init__(self):
        super(Seq2Seq, self).__init__()
        # 编码器:RNN模型
        self.encoder = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5)
        # 解码器:RNN模型
        self.decoder = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5)
        # 全连接层,用于输出分类
        self.fc = nn.Linear(n_hidden, n_class)

    def forward(self, enc_input, enc_hidden, dec_input):
        # enc_input:输入的编码数据 [batch_size, n_step+1, n_class]
        # dec_input:输入的解码数据 [batch_size, n_step+1, n_class]
        enc_input = enc_input.transpose(0, 1)  # 转置为 [n_step+1, batch_size, n_class]
        dec_input = dec_input.transpose(0, 1)  # 转置为 [n_step+1, batch_size, n_class]

        # 编码器输出:h_t 是最后的隐藏状态
        _, h_t = self.encoder(enc_input, enc_hidden)
        # 解码器输出:outputs 是解码过程中的所有输出
        outputs, _ = self.decoder(dec_input, h_t)

        # 通过全连接层输出最终结果
        model = self.fc(outputs)  # [n_step+1, batch_size, n_class]
        return model

# 实例化模型
model = Seq2Seq().to(device)
criterion = nn.CrossEntropyLoss().to(device)  # 交叉熵损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # Adam优化器

# 训练过程
for epoch in range(5000):
    for enc_input_batch, dec_input_batch, dec_output_batch in loader:
        # 初始化隐藏状态
        h_0 = torch.zeros(1, batch_size, n_hidden).to(device)

        # 将数据移至设备
        enc_input_batch, dec_input_batch, dec_output_batch = (
            enc_input_batch.to(device), dec_input_batch.to(device), dec_output_batch.to(device))

        # 训练模型,获取预测结果
        pred = model(enc_input_batch, h_0, dec_input_batch)

        # 计算损失
        pred = pred.transpose(0, 1)  # [batch_size, n_step+1, n_class]
        loss = 0
        for i in range(len(dec_output_batch)):
            loss += criterion(pred[i], dec_output_batch[i])  # 计算每一批次的损失

        # 每1000次迭代输出一次损失
        if (epoch + 1) % 1000 == 0:
            print('Epoch:', '%04d' % (epoch + 1), 'lost =', '{:.6f}'.format(loss))

        # 反向传播并优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

# 测试函数:翻译单词
def translate(word):
    enc_input, dec_input, _ = make_data([[word, '?' * n_step]])
    enc_input, dec_input = enc_input.to(device), dec_input.to(device)
    hidden = torch.zeros(1, 1, n_hidden).to(device)  # 初始化隐藏状态
    output = model(enc_input, hidden, dec_input)

    # 获取最大概率的预测值
    predict = output.data.max(2, keepdim=True)[1]
    decoded = [letter[i] for i in predict]
    translated = ''.join(decoded[:decoded.index('E')])  # 直到'E'为止

    return translated.replace('?', '')  # 去掉填充符号

# 测试翻译效果
print('测试')
for seq in seq_data:
    word = seq[0]  # 获取英语单词
    translated_word = translate(word)  # 获取翻译结果
    print(f'{word} -> {translated_word}')

运行结果;

Epoch: 1000 lost = 0.001182
Epoch: 2000 lost = 0.000338
Epoch: 3000 lost = 0.000147
Epoch: 4000 lost = 0.000075
Epoch: 5000 lost = 0.000041
测试
hello -> hola
cat?? -> gato
good? -> bueno

lost逐渐减小,这表明模型在不断优化,误差在不断减小 。

4 简单总结nn.RNNCell、nn.RNN

(1)nn.RNNCell

  • 单步处理RNNCell 是最基本的RNN单元,只处理单个时间步的数据。要处理整个序列,需要在外部循环中逐个时间步地调用RNNCell
  • 主要参数:input_size (int): 输入特征的维度。hidden_size (int): 隐藏状态的维度。

                        rnn_cell = nn.RNNCell(input_size, hidden_size)

  • 返回结果:当前时间步的输出 +更新后的隐藏状态

(2)nn.RNN

  • 批量处理RNN 是一个完整的RNN层,可以一次性处理整个序列。
  • 主要参数:

        input_size (int): 输入特征的维度。

        hidden_size (int): 隐藏状态的维度。

        num_layers (int, optional): RNN层的数量,默认为1。

        batch_first (bool, optional): 输入和输出张量的形状,默认为False。

        rnn = nn.RNN(input_size, hidden_size, num_layers=1, batch_first=True)

  • 返回结果: 整个序列的输出 +  最后一个时间步的隐藏状态
  •    🗣 batch_first,默认是 False,输入数据的格式为(seq_len, batch, input_size)。
         当batch_first=True时,输入序列的格式应该为( batch,seq_len, input_size)

当num_layers=1时: 

 当num_layers=3时:

 

5 谈一谈对“序列”、“序列到序列”的理解

序列:一组按照一定顺序排列的元素(数字、元素、字符等)。每个元素在序列中的位置是固定的,并且具有一定的顺序性。

序列到序列:一种深度学习模型,通过深度学习的方式,将一个序列转化为另一个序列。通过编码器提取输入序列的特征,再通过解码器生成输出序列。

6 总结本周理论课和作业,写心得体会

        

   通过实现作业的任务真正理解了RNN的原理,同时继卷积神经网络的Conv2d之后又认识到了循环神经网络中的nn.RNNCell和nn.RNN。另一个比较大的收获就是编码器和解码器的实现,第一次听说是在读“Attention is all you need”这篇论文中见到的(不过当时只是简单了解理论),这次作业中实现编码是用的one-hot编码(比较简单),还有更高级的词嵌入(Word Embeeding)方式。根据“编码器-解码器”架构的设计, 可以使用两个循环神经网络来设计一个序列到序列(Seq2Seq)学习的模型。

(部分笔记) 

;