【深度学习】零基础介绍循环神经网络(RNN)

发布于:2024-12-19 ⋅ 阅读:(12) ⋅ 点赞:(0)

零基础介绍

循环神经网络(RNN, Recurrent Neural Network)是一种特殊的神经网络,用于处理和分析序列数据(比如时间序列、文本、语音等)。它的特别之处在于,它能够“记住”之前的信息并用来处理当前的数据,这使得它比普通的前馈神经网络(比如卷积神经网络 CNN)更适合处理时间序列数据。

很多实际问题中,当前的输入往往和之前的数据有关系,比如:

语音识别:一个单词的发音会受到前后音节的影响。
语言翻译:一个句子的理解不仅要看当前的词,还要结合前面的词。
股票预测:当前股票的价格会受到过去价格的影响。

RNN的工作原理:

在这里插入图片描述
RNN的优缺点:

优点:
处理序列数据:RNN非常适合处理和预测序列数据,可以记住历史的信息,理解当前输入与过去输入之间的关系。
灵活性高:可以处理任意长度的序列,不需要固定输入长度。

缺点:
梯度消失/爆炸问题:在长时间序列中,RNN会遇到“梯度消失”或者“梯度爆炸”问题,即网络在训练时,梯度可能会变得非常小或非常大,导致学习过程困难。
训练速度慢:由于需要考虑历史信息,RNN的训练过程通常比普通神经网络慢。

RNN的变种:
为了克服RNN的一些缺点,研究者提出了一些变种模型,最常见的有:

长短期记忆网络(LSTM, Long Short-Term Memory):通过引入“门控机制”,LSTM能够更好地处理长时间依赖问题,避免梯度消失问题。
门控循环单元(GRU, Gated Recurrent Unit):GRU是LSTM的一个简化版本,同样可以有效处理长时间依赖问题,但结构更简单。

语言处理技术基本介绍

分词算法

在这里插入图片描述

词法分析工具

在这里插入图片描述

文本分类与聚类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

情感分析

在这里插入图片描述
在这里插入图片描述

自然语言处理

词向量

在这里插入图片描述
在这里插入图片描述

词向量学习模型

1. 神经网络语言模型

在这里插入图片描述

2. CBOW 和 skip-gram

在这里插入图片描述

3. 层次化softmax方法

在这里插入图片描述

4. 负采样方法

在这里插入图片描述

RNN介绍

传统的前馈神经网络(Feedforward Neural Network,简称 FNN),其中最常见的一种形式就是 多层感知器(Multilayer Perceptron,简称 MLP)

自然语言处理(NLP, Natural Language Processing)

在这里插入图片描述
MLP是典型的前馈神经网络,在输入层和输出层之间没有任何循环结构。它的每个输入都独立处理,无法处理序列数据和时间依赖关系。而RNN能够通过隐藏状态和递归结构处理时序数据,捕捉序列中的上下文信息。

MLP适合处理独立的输入数据(如静态图像分类、非时序特征等),而RNN更适合处理有时间依赖的序列数据(如文本生成、机器翻译、语音识别等)。
在这里插入图片描述

引入RNN:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
于是引入 LSTM

RNN的变种:LSTM

STM引入了三个门控(输入门、遗忘门和输出门)来控制信息的流动:
遗忘门(Forget Gate):决定上一时刻的记忆单元状态有多少需要被“忘记”。
输入门(Input Gate):决定当前输入有多少需要被写入记忆单元。
输出门(Output Gate):决定当前的记忆单元状态对输出的贡献
在这里插入图片描述
在这里插入图片描述

1. Forget Gate

在这里插入图片描述

2. Input Gate

在这里插入图片描述

3. Update Memory

在这里插入图片描述

4. Output Gate

在这里插入图片描述

在这里插入图片描述

RNN的变种:GRU

GRU是LSTM的一种简化版本,只有两个门(更新门和重置门):
更新门(Update Gate):控制当前隐藏状态的更新程度。
重置门(Reset Gate):决定当前输入与过去的隐藏状态结合的程度。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Attention 注意力机制

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

RNN的应用

1. 序列到类别

在这里插入图片描述

2. 同步序列到序列

在这里插入图片描述

(1)中文分词

在这里插入图片描述

(2)命名实体识别

在这里插入图片描述

3. 异步序列到序列

在这里插入图片描述

(1)机器翻译

在这里插入图片描述

(2)对话系统

在这里插入图片描述

4. 看图说话

在这里插入图片描述

5. 自动摘要

在这里插入图片描述

6. 自动写诗

在这里插入图片描述

7. 自动作曲

在这里插入图片描述
在这里插入图片描述

RNN代码

一个简单的RNN代码

每10个训练周期会打印出损失值,训练完成后,图表会显示模型的预测结果和真实的正弦曲线对比。
(使用均方误差损失函数(MSELoss)来计算模型的误差,优化器选择了Adam。)

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

# 定义一个简单的RNN模型
class SimpleRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleRNN, self).__init__()
        self.hidden_size = hidden_size
        
        # 定义RNN层
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
        
        # 定义输出层
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # 初始化隐藏状态(初始时刻为0)
        h0 = torch.zeros(1, x.size(0), self.hidden_size).to(x.device)
        
        # 通过RNN层得到输出和最终的隐藏状态
        out, hn = self.rnn(x, h0)
        
        # 获取最后一个时刻的输出
        out = out[:, -1, :]
        
        # 通过输出层得到预测结果
        out = self.fc(out)
        return out

# 超参数设置
input_size = 1       # 每个时间步的输入特征数(例如,序列中的每个元素)
hidden_size = 5      # 隐藏层的大小
output_size = 1      # 输出的特征数
learning_rate = 0.01 # 学习率
num_epochs = 100     # 训练的轮数

# 构造一个简单的序列输入(比如一个简单的正弦波或者线性序列)
x_train = torch.linspace(0, 10, steps=100).view(-1, 1, 1)  # 100个时间步,每个时间步的输入是一个1维数据
y_train = torch.sin(x_train).view(-1, 1)  # 正弦函数作为目标输出

# 创建RNN模型
model = SimpleRNN(input_size, hidden_size, output_size)

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# 训练模型
for epoch in range(num_epochs):
    model.train()
    
    # 前向传播
    outputs = model(x_train)
    
    # 计算损失
    loss = criterion(outputs, y_train)
    
    # 反向传播
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    if (epoch+1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# 预测
model.eval()
with torch.no_grad():
    predicted = model(x_train)

# 可视化结果
import matplotlib.pyplot as plt

plt.plot(x_train.numpy(), y_train.numpy(), label='True')
plt.plot(x_train.numpy(), predicted.numpy(), label='Predicted')
plt.legend()
plt.show()

电影评论情感分析

使用IMDb数据集,它包含大量的电影评论,并且每条评论都已经标注为“正面”或“负面”情感。

import torch
import torch.nn as nn
import torch.optim as optim
from torchtext.datasets import IMDB
from torchtext.data import Field, BucketIterator
import random
import numpy as np

SEED = 1234
random.seed(SEED)
torch.manual_seed(SEED)
torch.backends.cudnn.deterministic = True

# 定义Field
TEXT = Field(sequential=True, lower=True, include_lengths=True)
LABEL = Field(sequential=False, use_vocab=True, is_target=True)

# 加载IMDb数据集
train_data, test_data = IMDB.splits(TEXT, LABEL)

# 构建词汇表
TEXT.build_vocab(train_data, max_size=25000, vectors="glove.6B.100d", unk_init=torch.Tensor.normal_)
LABEL.build_vocab(train_data)

# 创建数据迭代器
BATCH_SIZE = 64

train_iterator, test_iterator = BucketIterator.splits(
    (train_data, test_data),
    batch_size=BATCH_SIZE,
    device=torch.device("cuda" if torch.cuda.is_available() else "cpu"),
    sort_within_batch=True,
    sort_key=lambda x: len(x.text),
)

class RNN_Sentiment(nn.Module):
    def __init__(self, input_dim, embedding_dim, hidden_dim, output_dim, n_layers, dropout):
        super(RNN_Sentiment, self).__init__()
        
        self.embedding = nn.Embedding(input_dim, embedding_dim)
        self.rnn = nn.RNN(embedding_dim, hidden_dim, n_layers, batch_first=True, dropout=dropout)
        self.fc = nn.Linear(hidden_dim, output_dim)
        self.dropout = nn.Dropout(dropout)

    def forward(self, text, text_lengths):
        embedded = self.embedding(text)
        
        # RNN expects packed sequences
        packed_embedded = nn.utils.rnn.pack_padded_sequence(embedded, text_lengths.cpu(), batch_first=True, enforce_sorted=False)
        
        packed_output, hidden = self.rnn(packed_embedded)
        
        # Get the last hidden state (for classification)
        hidden = self.dropout(hidden[-1])
        
        output = self.fc(hidden)
        
        return output

# 定义超参数
INPUT_DIM = len(TEXT.vocab)
EMBEDDING_DIM = 100
HIDDEN_DIM = 256
OUTPUT_DIM = 1
N_LAYERS = 2
DROPOUT = 0.5

model = RNN_Sentiment(INPUT_DIM, EMBEDDING_DIM, HIDDEN_DIM, OUTPUT_DIM, N_LAYERS, DROPOUT)

# 使用预训练的GloVe词向量初始化嵌入层
model.embedding.weight.data.copy_(TEXT.vocab.vectors)

# 如果有UNK和PAD的token,它们的向量应该是随机初始化
model.embedding.weight.data[TEXT.vocab.stoi[TEXT.pad_token]] = torch.zeros(EMBEDDING_DIM)
model.embedding.weight.data[TEXT.vocab.stoi[TEXT.unk_token]] = torch.randn(EMBEDDING_DIM)

# 将模型转移到GPU(如果可用)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)

# 定义损失函数和优化器
optimizer = optim.Adam(model.parameters())
criterion = nn.BCEWithLogitsLoss()

# 将损失函数放到GPU(如果可用)
criterion = criterion.to(device)

# 训练模型
def train(model, iterator, optimizer, criterion):
    model.train()
    epoch_loss = 0
    epoch_acc = 0
    
    for batch in iterator:
        text, text_lengths = batch.text
        labels = batch.label
        
        optimizer.zero_grad()
        
        predictions = model(text, text_lengths).squeeze(1)
        
        loss = criterion(predictions, labels)
        acc = binary_accuracy(predictions, labels)
        
        loss.backward()
        optimizer.step()
        
        epoch_loss += loss.item()
        epoch_acc += acc.item()
    
    return epoch_loss / len(iterator), epoch_acc / len(iterator)

# 计算准确率
def binary_accuracy(preds, y):
    rounded_preds = torch.round(torch.sigmoid(preds))
    correct = (rounded_preds == y).float()
    acc = correct.sum() / len(correct)
    return acc

# 开始训练
N_EPOCHS = 5
for epoch in range(N_EPOCHS):
    train_loss, train_acc = train(model, train_iterator, optimizer, criterion)
    print(f'Epoch {epoch+1}, Train Loss: {train_loss:.3f}, Train Accuracy: {train_acc*100:.2f}%')

def evaluate(model, iterator, criterion):
    model.eval()
    epoch_loss = 0
    epoch_acc = 0
    
    with torch.no_grad():
        for batch in iterator:
            text, text_lengths = batch.text
            labels = batch.label
            
            predictions = model(text, text_lengths).squeeze(1)
            
            loss = criterion(predictions, labels)
            acc = binary_accuracy(predictions, labels)
            
            epoch_loss += loss.item()
            epoch_acc += acc.item()
    
    return epoch_loss / len(iterator), epoch_acc / len(iterator)

# 测试模型
test_loss, test_acc = evaluate(model, test_iterator, criterion)
print(f'Test Loss: {test_loss:.3f}, Test Accuracy: {test_acc*100:.2f}%')

  1. 数据预处理:使用torchtext加载和处理IMDb数据集。
  2. 模型设计:使用RNN进行文本情感分类,经过词向量嵌入、RNN层处理和全连接层输出。
  3. 训练和评估:通过计算损失函数和准确率来训练模型并评估其性能。

与之前的CNN比较:
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到