RNN vs. Transformer,从循环到自注意力 最强比较 !!

文摘   2024-09-19 15:26   北京  

哈喽,我是cos大壮!~

深度学习序列模型方面,我们熟知的必须是 RNN 和 Transformer,今天想和大家聊聊 RNN 和 Transformer,并且探讨它们的区别和联系,让大家对这两种算法模型有一个更加熟悉的理解。

首先来说,RNN 和 Transformer 都是深度学习中的序列模型,用来处理序列数据,但它们的结构和工作方式有所不同。

  • RNN:是一种递归神经网络,适合处理时序数据(如时间序列、自然语言)。RNN通过隐藏状态传递上下文信息,适合短期依赖的任务,如文本生成、机器翻译等。然而,RNN在处理长序列时会遇到梯度消失问题,难以捕捉长距离依赖。

  • Transformer:是近年来兴起的模型,通过自注意力机制(Self-Attention)来并行处理序列中的所有位置,能够高效捕捉长距离依赖。与RNN相比,Transformer更擅长处理长序列,且由于并行计算,训练速度更快。它被广泛应用于自然语言处理任务,如翻译、文本生成、问答等,著名模型如BERT和GPT都是基于Transformer的。

大概就是,RNN适用于较短的序列任务,但效率相对低;Transformer能高效处理长序列,已经成为主流序列建模方式。

老规矩如果大家伙觉得近期文章还不错!欢迎大家点个赞、转个发,文末赠送《机器学习学习小册》
文末可取本文PDF版本~

往期最佳模型

选择最佳模型,GBDT、LightGBM、XGBoost、AdaBoost !

选择最佳模型,线性回归、弹性网络、Lasso回归、Ridge回归 !

选择最佳模型,线性SVM、非线性SVM、支持向量回归、核方法SVM !

选择最佳模型,Bagging、Boosting、Stacking、Voting、GBDT !

选择最佳模型,时间序列:ARIMA、SARIMA、指数平滑法、prophet!

XGBoost vs LightGBM:速度和精度 全面对比 !

本期最佳模型

RNN(Recurrent Neural Network)

原理

RNN是一类用于处理序列数据的神经网络,通过隐藏状态将序列的上下文信息传递到每个时间步,使得模型能够记住先前的信息,并对当前输入进行处理。它的关键特征是具有「循环」结构,隐藏状态可以传递时间上的信息。

核心公式和解释

隐藏状态更新公式

其中:

  •  是时间步  的隐藏状态(记忆信息)。
  •  是前一个时间步的隐藏状态。
  •  是时间步  的输入。
  •  和  是权重矩阵。
  •  是偏置项。
  •  是非线性激活函数(通常为tanh或ReLU)。

该公式表明当前时间步的隐藏状态是由前一时间步的隐藏状态和当前的输入共同决定的,这种递归的形式实现了信息的传递。

输出公式

其中, 是时间步  的输出, 是权重矩阵, 是偏置项, 是输出的激活函数(例如softmax用于分类任务)。

算法流程

  1. 初始化隐藏状态 
  2. 对于序列中的每一个时间步:
  • 接收当前输入  和上一个时间步的隐藏状态 
  • 使用隐藏状态更新公式计算新的隐藏状态 
  • 使用输出公式计算当前时间步的输出 
  • 在整个序列处理完后,依据任务需求决定是否使用最后的隐藏状态或输出序列进行进一步处理(例如分类或序列生成任务)。
  • 优缺点

    优点

    • 能够处理变长的序列数据。
    • 通过递归结构,能在序列中传递信息,实现上下文依赖。

    缺点

    • 梯度消失/爆炸问题:当序列较长时,梯度容易消失或爆炸,导致模型训练困难,尤其在处理长期依赖时表现不佳。
    • 并行化困难:由于序列数据是按时间步递归处理的,无法并行训练,导致效率较低。

    适用场景

    • 短期依赖的时序数据建模任务,如时间序列预测、简单的序列分类任务。
    • 较短文本的自然语言处理任务,如词性标注、简单的文本生成等。

    Transformer

    原理

    Transformer 是一种专为序列建模和并行计算设计的神经网络,它抛弃了RNN的递归结构,完全依赖于自注意力机制(Self-Attention)来建模序列数据中的依赖关系。通过自注意力,Transformer能够对整个序列的所有位置进行全局的依赖建模,适用于处理长序列任务。

    核心公式和解释

    Transformer的核心在于自注意力机制,通过加权的方式捕捉序列中不同位置之间的相互关系。

    自注意力机制公式

    其中:

    • (Query)、(Key)和 (Value)分别是输入序列经过不同线性变换得到的矩阵。
    •  是Key向量的维度。
    • 该公式表示的是对序列中不同位置进行加权的计算,权重由Query和Key之间的相似度(即 )来决定,softmax用于归一化权重,再与Value矩阵相乘得到注意力加权后的输出。

    多头自注意力(Multi-Head Attention)

    在标准自注意力的基础上,Transformer使用了多头注意力机制,使模型在不同子空间中进行注意力计算,增强模型的表示能力:

    其中,每个  是一个独立的注意力计算。

    位置编码(Positional Encoding)

    由于Transformer抛弃了RNN的时间步递归结构,它通过位置编码将序列中的位置信息显式加入:

    其中  是序列位置, 是维度索引, 是模型的维度。

    算法流程

    1. 输入序列经过词嵌入层。
    2. 加入位置编码以保留序列的位置信息。
    3. 通过若干层的自注意力和前馈神经网络(Feed-Forward Network),对序列进行编码。
    4. 如果是生成任务,则解码器通过注意力机制和前馈网络逐步生成输出序列。
    5. 最后,通过线性层和softmax计算生成或分类结果。

    优缺点

    优点

    • 捕捉长距离依赖:自注意力机制能够有效处理长序列中的长距离依赖问题。
    • 并行化:由于抛弃了递归结构,Transformer能够并行处理整个序列,大大提高了训练效率。
    • 可扩展性强:Transformer在大规模数据上表现优异,适合在大数据集上进行预训练。

    缺点

    • 计算复杂度高:自注意力机制的计算复杂度是 ,对于非常长的序列,计算代价较高。
    • 不适合短序列:对于较短的序列任务,Transformer可能效率不如RNN。

    适用场景

    • 自然语言处理:机器翻译(如BERT, GPT),文本生成,语义分析,问答系统等。
    • 计算机视觉:如ViT(Vision Transformer)在图像分类、目标检测中的应用。
    • 时序预测:Transformer也逐渐应用于时间序列预测等任务,尤其是在长序列场景中。

    整体对比下来:

    • RNN 依赖递归结构和隐藏状态,适合短期依赖任务,但难以捕捉长距离依赖,且训练速度较慢。
    • Transformer 通过自注意力机制高效处理长序列任务,适合大规模并行计算,在自然语言处理和其他领域的长序列建模上表现出色。

    两者在解决序列数据问题上的应用场景有所重叠,但目前来看,Transformer已逐渐取代RNN,成为主流序列建模方法。

    下面,咱们通过一个案例对比,让大家有一个更加清晰的认识~

    RNN vs. Transformer 在时间序列预测中的适用性和性能比较

    要解决的问题

    咱们通过虚拟的时间序列预测任务,比较RNN和Transformer在预测精度、训练时间以及长短期依赖捕捉能力等方面的表现。我们将使用虚拟生成的时间序列数据集,进行序列建模,分别应用RNN和Transformer模型,最后通过绘图和性能指标来进行详细比较。

    目标

    1. 比较RNN和Transformer在处理时间序列预测任务时的准确性速度长短期依赖处理能力
    2. 通过调参对两个模型进行优化,提升预测效果。
    3. 进行可视化分析,展示两者的适用性和性能差异。

    步骤

    • 生成虚拟时间序列数据集。
    • 构建RNN和Transformer模型。
    • 对模型进行调参和训练。
    • 通过预测准确性、训练时间等方面进行详细比较。
    • 可视化分析包括损失曲线、预测结果对比和训练时间比较。

    代码实现

    import numpy as np
    import matplotlib.pyplot as plt
    import torch
    import torch.nn as nn
    import torch.optim as optim
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import MinMaxScaler
    from time import time

    # 设置随机种子
    np.random.seed(42)
    torch.manual_seed(42)

    # 生成虚拟时间序列数据集
    def generate_synthetic_data(n_samples=1000, seq_length=50):
        X = np.sin(np.linspace(0100, n_samples)) + np.random.normal(00.1, n_samples)
        X = X.reshape(-11)
        
        sequences = []
        targets = []
        for i in range(len(X) - seq_length):
            sequences.append(X[i:i + seq_length])
            targets.append(X[i + seq_length])
        
        return np.array(sequences), np.array(targets)

    # 数据生成
    seq_length = 50
    X, y = generate_synthetic_data(n_samples=2000, seq_length=seq_length)

    # 数据归一化
    scaler = MinMaxScaler()
    X_scaled = scaler.fit_transform(X.reshape(-1, X.shape[-1])).reshape(X.shape)
    y_scaled = scaler.fit_transform(y)

    # 分割训练和测试集
    X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_scaled, test_size=0.2, random_state=42)

    # 将数据转换为Tensor
    X_train = torch.tensor(X_train, dtype=torch.float32)
    y_train = torch.tensor(y_train, dtype=torch.float32)
    X_test = torch.tensor(X_test, dtype=torch.float32)
    y_test = torch.tensor(y_test, dtype=torch.float32)

    # RNN模型
    class RNNModel(nn.Module):
        def __init__(self, input_size, hidden_size, num_layers, output_size):
            super(RNNModel, self).__init__()
            self.rnn = nn.RNN(input_size, hidden_size, num_layers, batch_first=True)
            self.fc = nn.Linear(hidden_size, output_size)

        def forward(self, x):
            h_0 = torch.zeros(1, x.size(0), hidden_size)  # 初始化隐藏状态
            out, _ = self.rnn(x, h_0)
            out = self.fc(out[:, -1, :])
            return out

    # Transformer模型
    class TransformerModel(nn.Module):
        def __init__(self, input_size, d_model, nhead, num_encoder_layers, output_size):
            super(TransformerModel, self).__init__()
            self.transformer = nn.Transformer(d_model, nhead, num_encoder_layers, batch_first=True)
            self.fc = nn.Linear(d_model, output_size)

        def forward(self, x):
            x = self.transformer(x, x)
            out = self.fc(x[:, -1, :])
            return out

    # 模型参数
    input_size = 1
    hidden_size = 64
    num_layers = 1
    output_size = 1
    d_model = 64
    nhead = 4
    num_encoder_layers = 2

    # 初始化RNN和Transformer模型
    rnn_model = RNNModel(input_size, hidden_size, num_layers, output_size)
    transformer_model = TransformerModel(input_size, d_model, nhead, num_encoder_layers, output_size)

    # 损失函数和优化器
    criterion = nn.MSELoss()
    rnn_optimizer = optim.Adam(rnn_model.parameters(), lr=0.001)
    transformer_optimizer = optim.Adam(transformer_model.parameters(), lr=0.001)

    # 模型训练函数
    def train_model(model, optimizer, X_train, y_train, num_epochs=100):
        losses = []
        for epoch in range(num_epochs):
            model.train()
            optimizer.zero_grad()
            outputs = model(X_train)
            loss = criterion(outputs, y_train)
            loss.backward()
            optimizer.step()
            losses.append(loss.item())
        return losses

    # 模型训练及性能评估
    def evaluate_model(model, X_test):
        model.eval()
        with torch.no_grad():
            predictions = model(X_test)
        return predictions

    # 训练RNN模型
    start_time_rnn = time()
    rnn_losses = train_model(rnn_model, rnn_optimizer, X_train, y_train)
    end_time_rnn = time()

    # 训练Transformer模型
    start_time_transformer = time()
    transformer_losses = train_model(transformer_model, transformer_optimizer, X_train, y_train)
    end_time_transformer = time()

    # 评估模型
    rnn_predictions = evaluate_model(rnn_model, X_test)
    transformer_predictions = evaluate_model(transformer_model, X_test)

    # 可视化比较
    plt.figure(figsize=(128))

    # 损失曲线
    plt.subplot(221)
    plt.plot(rnn_losses, label="RNN Loss", color="red")
    plt.plot(transformer_losses, label="Transformer Loss", color="blue")
    plt.xlabel("Epochs")
    plt.ylabel("Loss")
    plt.title("Loss Curve Comparison")
    plt.legend()

    # 预测结果比较(部分测试数据)
    plt.subplot(222)
    plt.plot(y_test[:50], label="True", color="green")
    plt.plot(rnn_predictions[:50], label="RNN Prediction", color="red")
    plt.plot(transformer_predictions[:50], label="Transformer Prediction", color="blue")
    plt.xlabel("Sample Index")
    plt.ylabel("Value")
    plt.title("Prediction Comparison (First 50 Samples)")
    plt.legend()

    # 训练时间比较
    plt.subplot(223)
    times = [end_time_rnn - start_time_rnn, end_time_transformer - start_time_transformer]
    plt.bar(["RNN""Transformer"], times, color=["red""blue"])
    plt.ylabel("Training Time (seconds)")
    plt.title("Training Time Comparison")

    # 模型预测误差对比
    plt.subplot(224)
    rnn_mse = criterion(rnn_predictions, y_test).item()
    transformer_mse = criterion(transformer_predictions, y_test).item()
    plt.bar(["RNN""Transformer"], [rnn_mse, transformer_mse], color=["red""blue"])
    plt.ylabel("Mean Squared Error")
    plt.title("MSE Comparison")

    plt.tight_layout()
    plt.show()

    # 输出模型效果总结
    print(f"RNN Training Time: {end_time_rnn - start_time_rnn:.2f} seconds")
    print(f"Transformer Training Time: {end_time_transformer - start_time_transformer:.2f} seconds")
    print(f"RNN MSE: {rnn_mse:.4f}")
    print(f"Transformer MSE: {transformer_mse:.4f}")

    调参细节

    1. RNN模型:我们使用了1层RNN,隐藏单元数设为64,学习率为0.001。我们尝试过较大和较小的隐藏单元数,发现在此数据集中64表现最佳。

    2. Transformer模型:采用2层编码器,模型尺寸设为64,头数设为4,学习率为0.001。通过调试层数和注意力头数,最终找到了最优的设置。

    详细比较

    1. 损失曲线:从图中可以看到,Transformer的收敛速度明显快于RNN,尤其是在前几个epoch中。

    2. 预测结果:在预测前50个样本时,Transformer的预测结果更接近真实值,而RNN的预测相对较差。

    3. 训练时间:RNN的训练时间比Transformer更短,这与RNN结构较简单有关,但对于长序列任务,Transformer更高效。

    4. 预测误差:在MSE比较中,Transformer明显优于RNN,表明Transformer在该任务中具有更好的准确性。

    最后

    整体来看:

    • Transformer模型在时间序列预测任务中的表现优于RNN,尤其在捕捉长距离依赖方面。
    • RNN模型训练速度更快,适合短序列的简单预测任务。

    通过优化两者的参数,能够有效提升预测性能,尤其在长序列预测中,Transformer表现更为突出。

    大家有问题可以直接在评论区留言即可~

    喜欢本文的朋友可以收藏、点赞、转发起来!

    需要本文PDF的同学,扫码备注「最佳模型」即可~ 

    关注本号,带来更多算法干货实例,提升工作学习效率!

    最后,给大家准备了《机器学习学习小册》PDF版本16大块的内容,124个问题总结

    推荐阅读

    原创、超强、精华合集

    100个超强机器学习算法模型汇总

    机器学习全路线

    机器学习各个算法的优缺点

    7大方面,30个最强数据集

    6大部分,20 个机器学习算法全面汇总

    铁汁,都到这了,别忘记点赞呀~

    深夜努力写Python
    Python、机器学习算法
     最新文章