RNN循环神经网络
循环神经网络()是一种专门处理序列数据的神经网络。与传统的前馈神经网络不同,RNN具有循环结构,能够处理和记住前面时间步的信息,使其特别适用于时间序列数据或有时序依赖的任务。我们要明确什么是序列数据,时间序列数据是指在不同时间点上收集到的数据,这类数据反映了某一事物、现象等随时间的变化状态或程度。这是时间序列数据的定义,当然这里也可以不是时间,比如文字序列,但总归序列数据有一个特点——后面的数据跟
一.RNN简介
1.RNN介绍
循环神经网络(Recurrent Neural Network, RNN)是一种专门处理序列数据的神经网络。与传统的前馈神经网络不同,RNN具有“循环”结构,能够处理和记住前面时间步的信息,使其特别适用于时间序列数据或有时序依赖的任务。
我们要明确什么是序列数据,时间序列数据是指在不同时间点上收集到的数据,这类数据反映了某一事物、现象等随时间的变化状态或程度。这是时间序列数据的定义,当然这里也可以不是时间,比如文字序列,但总归序列数据有一个特点——后面的数据跟前面的数据有关系。
2.RNN的应用
3.自然语言处理
自然语言处理(Nature language Processing, NLP)研究的主要是通过计算机算法来理解自然语言。对于自然语言来说,处理的数据主要就是人类的语言,例如:汉语、英语、法语等,该类型的数据不像我们前面接触过的结构化数据、或者图像数据可以很方便的进行数值化。

二.词嵌入层
词嵌入层的作用就是将文本转换为向量。
词嵌入层在 RNN 中的作用有输入表示、降低维度和捕捉语义相似性。
词嵌入层首先会根据输入的词的数量构建一个词向量矩阵,例如: 我们有 100 个词,每个词希望转换成 128 维度的向量,那么构建的矩阵形状即为: 100*128,输入的每个词都对应了一个该矩阵中的一个向量。

词嵌入流程:

其步骤如下:
三.循环网络层
1.RNN的网络结构
RNN(Recurrent Neural Network)是一种专为处理序列数据设计的神经网络结构,其核心特点是利用循环连接(隐藏层的输出反馈到输入)来捕捉时间或顺序上的动态信息。
例如: "我爱你", 这串文本就是具有序列关系的,"爱" 需要在 "我" 之后,"你" 需要在 "爱" 之后, 如果颠倒了顺序,那么可能就会表达不同的意思。
为了表示出数据的序列关系,需要使用循环神经网络(Recurrent Nearal Networks, RNN) 来对数据进行建模,RNN 是一个作用于处理带有序列特点的样本数据。
一般单层神经网络:

2.RNN模型的作用
RNN的核心作用是建模序列数据的时序依赖关系,适用于需要上下文信息的任务。尽管其基础版本存在局限性,但通过LSTM/GRU等改进和结合注意力机制,RNN家族在序列建模中仍有重要地位。如文本分类, 意图识别, 机器翻译等.
这里举一个例子方便理解:
第一步: 用户输入了"What time is it ?", 我们首先需要对它进行基本的分词, 因为RNN是按照顺序工作的, 每次只接收一个单词进行处理.
![]()
第二步: 首先将单词"What"输送给RNN, 它将产生一个输出O1.

第三步: 继续将单词"time"输送给RNN, 但此时RNN不仅仅利用"time"来产生输出O2, 还会使用来自上一层隐层输出O1作为输入信息.

第四步: 重复这样的步骤, 直到处理完所有的单词

第五步: 最后,将最终的隐层输出O5进行处理来解析用户意图

3.RNN网络原理
计算当前时刻的输出:网络的输出yt是当前时刻的隐藏状态经过一个线性变换得到的。

上述公式中:
词汇表映射:
输出yt是一个向量,该向量经过全连接层后输出得到最终预测结果Ypred,Ypred中每个元素代表当前时刻生成词汇表中某个词的得分(或概率,通过激活函数如softmax)。词汇表有多少个词,Ypred就有多少个元素值,最大元素值对应的词就是当前时刻预测生成的词。
4.RNN:API的使用

参数意义是:
将RNN实例化就可以将数据送入进行处理。
输入数据和输出结果
将RNN实例化就可以将数据送入其中进行处理,处理的方式如下所示:

RNN层的代码实现:
import torch
x = torch.randn(size=[5,32,128])
print(x)
h0 = torch.zeros(size=[1,32,256])
print(h0)
rnn = torch.nn.RNN(input_size=128,hidden_size=256,num_layers=1)
print(rnn)
y,h1 = rnn(x,h0)
print(h1)
print(y)

接下来介绍一个案例来应用RNN
文本生成任务是一种常见的自然语言处理任务,输入一个开始词能够预测出后面的词序列。本案例将会使用循环神经网络来实现周杰伦歌词生成任务。
整体流程是:
import time
import jieba
import torch.utils.data
import torch
# TODO 1.构建词表
def build_vocab():
# 分别储存所有杭分此列表和去重后每个分词
all_words = []
unique_words = []
for line in open(r'/data/jaychou_lyrics.txt', mode='r', encoding='utf-8'):
words = jieba.lcut(line)
all_words.append(words)
for word in words:
if word not in unique_words:
unique_words.append(word)
# 去重后单词数量
unique_words_cnt = len(unique_words)
# 构建小词表: 每个单词对应一个索引
unique_words_to_idx = {word: idx for idx, word in enumerate(unique_words)}
print(f'词表大小:{unique_words_cnt}')
all_words_idx = []
for words in all_words:
temp = []
for word in words:
temp.append(unique_words_to_idx[word])
temp.append(unique_words_to_idx[' '])
all_words_idx.extend(temp)
return unique_words, unique_words_cnt, unique_words_to_idx, all_words_idx
# TODO 2.构建数据集
class MyDataset(torch.utils.data.Dataset):
def __init__(self, all_words_idx, number_chars):
self.all_words_idx = all_words_idx
# 属性2:每个句子的长度
self.number_chars = number_chars
self.all_words_idx_cnt = len(self.all_words_idx)
self.number = self.all_words_idx_cnt // self.number_chars
def __len__(self):
return self.number
def __getitem__(self, index):
start = min(max(0, index), self.all_words_idx_cnt - 1 - self.number_chars)
end = start + self.number_chars
x = self.all_words_idx[start:end]
y = self.all_words_idx[start + 1:end + 1]
x = torch.tensor(x)
y = torch.tensor(y)
return x, y
# TODO 3.构建神经网络模型
class MyModel(torch.nn.Module):
# 重写__init__方法
def __init__(self, unique_words_cnt):
# 调用父类方法
super().__init__()
# todo 定义网络结构
# 创建一个嵌入层
self.embedding = torch.nn.Embedding(num_embeddings=unique_words_cnt, embedding_dim=128)
# 创建一个rnn层
self.rnn = torch.nn.RNN(input_size=128, hidden_size=256, num_layers=1)
# 创建一个全连接层
self.out = torch.nn.Linear(in_features=256, out_features=unique_words_cnt)
# 重写forward方法
def forward(self, x, h):
# todo 获取嵌入层输出
ebd_x = self.embedding(x)
# todo 获取rnn层输出
# TODO ebd_x[batch_size, seq_len, input_size]->(seq_len,batch_size, hidden_size)
rnn_out, h = self.rnn(ebd_x.transpose(0, 1), h)
# todo 全连接层: 只能处理2维张量,此处需要把rnn_out转成2维张量
out = self.out(rnn_out.reshape(-1, rnn_out.shape[-1]))
# todo 返回结果
return out, h
# 定义初始化隐藏状态方法
def init_hidden(self, batch_size):
# 创建初始隐藏状态
return torch.zeros(size=[1, batch_size, 256])
# TODO 4.模型训练
def train_model(unique_word_to_idx, train_loader, model, epochs, batch_size):
# 1.获取数据(本次已经传参)
# 2.获取模型(本次已经传参)
# 3.创建损失函数对象
loss_fn = torch.nn.CrossEntropyLoss()
# 4.创建优化器对象
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)
# 5.训练模型
for epoch in range(epochs):
total_loss, sample_cnt, start = 0.0, 0, time.time()
for x, y in train_loader:
# todo 正(前)向传播:从输入到输出: 预测值和损失值
# 先获取隐藏状态
h = model.init_hidden(x.size(0))
# 然后预测获取预测值和隐藏状态
y_pred, h = model(x, h)
# 计算损失值
loss = loss_fn(y_pred, y.transpose(0, 1).reshape(-1))
# 累加损失值和批次数量
total_loss += loss.item()
sample_cnt += 1
# todo 反向传播:从输出到输入: 梯度计算和参数更新
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 走到此处,说明一轮结束: 计算每轮损失值
epoch_loss = total_loss / sample_cnt
print(f"第{epoch + 1}轮,运行时间{time.time() - start:.2f}秒,损失值为:{epoch_loss:.2f}")
# 6.保存训练好的模型参数字典
torch.save(model.state_dict(), 'model')
# TODO 5.模型预测
def eval_model(start_word, length, unique_words_cnt, unique_words, unique_word_to_idx):
# 1.获取起始词对应的索引,并封装成列表
word_idx = unique_word_to_idx[start_word]
words = [word_idx]
# 2.创建新模型
model = MyModel(unique_words_cnt)
model.load_state_dict(torch.load('model'))
# 3.模型预测
# 提前获取隐藏状态
h = model.init_hidden(batch_size=1)
for i in range(length):
# 模型预测
ypred, h = model(torch.tensor([[word_idx]]), h)
# 获取预测值索引
word_idx = torch.argmax(ypred).item()
words.append(word_idx)
# print(words)
# 4.将索引列表转成词列表
words = [unique_words[idx] for idx in words]
print(''.join(words))
# 程序主入口
if __name__ == '__main__':
# TODO 1.构建词表
unique_words, unique_words_cnt, unique_word_to_idx, all_words_idx = build_vocab()
# TODO 2.构建数据集
number_chars = 32
dataset = MyDataset(all_words_idx, number_chars)
# print(f'数据集大小: {len(dataset)}') # 自动调用__len__方法
# print(dataset[0]) # 自动调用了__getitem__方法: (tensor([ 0, 1, 2, 3, 40]), tensor([ 1, 2, 3, 40, 0]))
batch_size = 5
train_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
# TODO 3.构建神经网络模型
model = MyModel(unique_words_cnt)
print(model)
# TODO 4.模型训练
epochs = 10
# train_model(unique_word_to_idx, train_loader, model, epochs, batch_size)
# TODO 5.模型预测
# 注意: start_word必须在词表中有,否则报错
start_word = '星星'
length = 99
eval_model(start_word, length, unique_words_cnt, unique_words, unique_word_to_idx)
train后的结果为:

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)