告别机械化的“硬语言”,让AI学会创作中国古诗

前言

计算机能够理解“硬语言”——编程语言的语法规则是确定的、无歧义的,编译器可以按照机械化的规则解析每一行代码的含义。然而,当我们试图让计算机理解汉语或英语这样的“软语言”时,传统方法便捉襟见肘了。汉语的含义和形式灵活多变,新词层出不穷,同一个词语在不同语境下可能表达截然不同的意思。

如何让计算机不仅理解语言,还能像人类一样“创作”语言?这正是本文要深入探讨的核心问题。

一、自然语言处理:让计算机“学会”人类语言

1.1 什么是自然语言处理

自然语言处理(Natural Language Processing, NLP)的目标是让计算机理解人类语言,进而完成对人类有帮助的事情。与编程语言的“硬”特性不同,自然语言是一种“软语言”——它的含义和形式会灵活变化,并且会不断出现新的词语或新的含义。这让NLP成为人工智能领域最具挑战性也最富有魅力的方向之一。

要让计算机理解自然语言,使用常规方法是无法办到的。传统的前馈神经网络假设输入之间相互独立,对于文本这种具有内在序列结构的数据,显得力不从心。

1.2 词表示方法的演进

在深入RNN之前,我们需要先理解一个核心问题:计算机如何“表示”一个词的语义?

(1)基于同义词词典的方法

早期NLP采用构建同义词词典的方式。通过将同义词或近义词归入同一类别,并根据“整体-部分”或“上位-下位”关系构建层级的树状图,形成一个庞大的“单词网络”,以此教会计算机单词之间的关系,从而计算出单词的“相似度”。

这种方法存在明显缺陷:

  • 需要人工逐个定义单词之间的相关性,费时费力;

  • 新词不断出现,语言不断变化,词典维护成本极高;

  • 表现力有限,难以覆盖语言的丰富性。

(2)基于计数的方法

随着大数据时代的到来,研究人员开始从海量文本语料中自动提取语言的“本质”。最基础的做法就是统计“词频”:

  • 分词

    :对文本内容进行切分,找出基本单元;

  • 词关联ID

    :为每个单词分配唯一ID,构建“词表”;

  • 词向量化

    :用固定长度的向量表示单词,也称为词的“分布式表示”。

具体做法是:统计每个词周围出现了哪些词、出现了多少次(称为“上下文”),将这些词频统计结果构成向量,这个向量就可以表示当前词,称为“词向量”(word vector)。所有词对应的向量汇总起来形成一个矩阵,被称为“共现矩阵”(co-occurrence matrix)。

然而,这种方法对所有词进行向量化表示的计算复杂度极高,难以在大规模语料上实用。

(3)基于推理的方法

基于推理的方法另辟蹊径:希望在已知上下文的前提下,“推测”当前位置的词是什么。利用神经网络接收上下文信息作为输入,通过模型计算,输出各个单词可能出现的概率,从而根据上下文预测该出现的单词。这正是Word2Vec等现代词嵌入技术的核心思想。

1.3 词嵌入:自然语言的向量化革命

词嵌入(Word Embedding)是将词映射到向量的技术,也是让计算机“理解”单词含义的关键一步。词向量是用于表示单词意义的向量,可以看作词的特征向量。

为什么词嵌入比传统方法更优?传统词袋模型存在三大缺陷:

  • 高维稀疏性

    :词汇表规模达数十万时,向量维度爆炸且90%以上元素为0;

  • 语义缺失

    :无法捕捉词语之间的语义关联性;

  • 词序忽略

    :完全丢失“狗咬人”与“人咬狗”的语义差异。

而词嵌入将词映射为稠密、低维的连续向量,使语义相近的词在向量空间中距离更近。以五维词向量为例,每个词都用一串浮点数表示:

词向量

[0.3307, 0.4814, 1.4244, -0.3161, 0.0859]

[0.9491, -0.1030, 2.5101, 0.1955, -0.0147]

[0.2709, -0.2052, -0.6052, -0.5227, 0.9971]

另一种表示方式是独热向量(one-hot vector)。独热向量很容易构建,但它不是一个好的选择:

  • 任意两个不同词的独热向量之间的余弦相似度为0,无法编码词之间的相似性;

  • 随着词汇量的增大,独热向量的大小会线性增长,消耗大量存储与计算资源。

二、RNN的数学原理与架构解析

2.1 为什么需要RNN

文本是连续的,具有序列特性——词序的改变往往意味着意义的根本变化。比如“狗咬人”和“人咬狗”表达的是完全不同的意思。

然而,前馈型神经网络(feedforward neural network)假设输入之间相互独立,信息仅在一个方向上传播:从输入层到隐藏层再到输出层。虽然这种网络结构简单易懂,但它有一个致命问题——不能很好地处理时间序列数据。更确切地说,单纯的前馈网络无法充分学习时序数据中蕴含的依赖关系。

2.2 RNN的核心原理

循环神经网络(Recurrent Neural Network, RNN)专门设计用于识别序列数据中的模式,其核心在于循环连接机制:每个时间步的输出不仅依赖于当前输入,还依赖于前一个时间步的隐藏状态(Hidden State)。

RNN层具有环路,通过这个环路,数据可以在层内循环传递。向时序数据 $(x_0, x_1, \dots, x_t)$ 输入层中,相应的会输出 $(h_0, h_1, \dots, h_t)$。

将RNN的环路按时间步展开后,可以看到:

  • 各个时刻的RNN层接收当前输入 $x_t$ 和前一时刻的输出 $h_{t-1}$(隐藏状态);

  • 根据这两部分信息计算当前时刻的输出 $h_t$。

核心数学公式

ht=tanh⁡(ht−1Wh+xtWx+b)ht=tanh(ht−1Wh+xtWx+b)

其中:

  • $h_t$ 是当前时刻的隐藏状态(相当于网络的“记忆”);

  • $x_t$ 是当前时刻的输入向量;

  • $W_h$ 是与前一时刻隐藏状态运算的权重矩阵;

  • $W_x$ 是与当前输入运算的权重矩阵;

  • $b$ 是偏置项;

  • $\tanh$ 是激活函数,输出范围在[-1, 1]之间。

RNN层的输出 $h_t$ 既作为当前时刻的输出,又传递给下一时刻作为“记忆”。这种设计让RNN能够“记住”之前的信息,并用这些信息来影响当前时间步的输出。

2.3 RNN的参数共享机制

RNN的一个关键特性是参数共享:在不同时间步之间,模型使用相同的权重矩阵 $W_h$ 和 $W_x$。这意味着无论序列有多长,模型的总参数量保持不变。这种设计显著减少了模型复杂度,并确保模型能够处理任意长度的序列。

2.4 RNN的局限性

传统RNN并非完美无缺。在处理长序列时,RNN面临两个核心问题:

  • 梯度消失

    :当序列很长时,通过时间反向传播(BPTT)的梯度会逐渐衰减,导致远距离依赖信息无法有效传递,模型难以捕捉长期依赖关系;

  • 梯度爆炸

    :在反向传播过程中,梯度可能指数级增长,导致网络权重不稳定,训练难以收敛。

为了解决这些问题,研究人员提出了LSTM(长短期记忆网络)和GRU(门控循环单元)等改进架构。LSTM通过引入遗忘门、输入门和输出门三个门控机制来控制信息流,同时维护独立的“细胞状态”以存储长期信息。GRU则是LSTM的简化版本,将输入门和遗忘门合并为“更新门”,参数量更少、训练更快。

三、RNN的PyTorch实现

3.1 torch.nn.RNN API详解

在PyTorch中,torch.nn.RNN类用于构建循环神经网络。其关键参数如下:

参数

含义

默认值

input_size

输入特征的数量

必填

hidden_size

隐藏层的特征数量

必填

num_layers

RNN的层数(堆叠层数)

1

nonlinearity

激活函数类型,可选'tanh'或'relu'

'tanh'

batch_first

是否将batch_size放在输入张量的第一位

False

dropout

除最后一层外各层应用dropout的概率

0

bidirectional

是否使用双向RNN

False

调用方式

import torch
import torch.nn as nn

# 初始化RNN层
rnn = nn.RNN(input_size=8, hidden_size=16, num_layers=2)

# 准备输入数据
# input的形状: [seq_len, batch_size, input_size]
input = torch.rand(1, 3, 8)  # 序列长度1,批量大小3,特征数8

# 初始化隐藏状态
# hx的形状: [num_layers, batch_size, hidden_size]
hx = torch.randn(2, 3, 16)   # 2层RNN,批量大小3,隐藏维度16

# 前向传播
output, hn = rnn(input=input, hx=hx)

print(output.shape)  # torch.Size([1, 3, 16])  输出序列
print(hn.shape)      # torch.Size([2, 3, 16])  最终隐藏状态

参数解释

  • num_layers=2表示堆叠两个RNN层,第二个RNN接收第一个RNN的输出并计算最终结果;

  • 当batch_first=False(默认)时,输入形状为(seq_len, batch_size, input_size);设置batch_first=True后,形状变为(batch_size, seq_len, input_size);

  • 输出output包含每个时间步的隐藏状态,形状与输入序列长度对应;hn只保留最后一个时间步的隐藏状态。

3.2 词嵌入层详解

在将文本输入RNN之前,需要先将文字转换为数值向量。PyTorch提供了torch.nn.Embedding模块来实现词嵌入:

embed = nn.Embedding(num_embeddings=len(vocab), embedding_dim=embedding_dim)
  • num_embeddings:词表的大小(词汇量);

  • embedding_dim:词向量的维度,即每个词被映射成的向量长度。

词嵌入矩阵实际上是一个可训练的参数矩阵,形状为[num_embeddings, embedding_dim]。通过词索引查表即可获得对应的词向量:

word_vec = embed(torch.tensor(idx))  # 通过索引获取词向量

四、实战案例:基于RNN的古诗生成器

理论部分讲完了,接下来我们用PyTorch完整实现一个古诗生成模型。这个项目将串联前面讲解的所有知识点:文本预处理→词嵌入→RNN序列建模→模型训练→文本生成。

4.1 数据预处理

数据预处理是整个项目的基石。我们需要将原始的诗歌文本转换成模型可以处理的数值形式。

import torch
import torch.nn as nn
import jieba
import re
from torch.utils.data import Dataset, DataLoader
import torch.optim as optim

def process_poems(file_path):
    """
    古诗数据预处理函数
    Args:
        file_path: 古诗文件路径
    Returns:
        sequences: 每首诗转换后的索引序列列表
        word2idx: 词到索引的映射字典
        vocab: 词表列表
    """
    poems = []          # 保存处理后的诗(字符列表形式)
    char_set = set()    # 保存所有不重复的字

    with open(file_path, "r", encoding="utf-8") as f:
        for line in f:
            # 逐行处理:去掉标点符号与两侧空白
            # 这里我们去掉常见的标点符号:逗号、句号、问号、感叹号、冒号
            line = re.sub(r"[,。、?!:]", "", line).strip()

            # 按字分割并添加到字符集合中(用于构建词表)
            char_set.update(list(line))

            # 按字符存储(保留顺序)
            poems.append(list(line))

    # 构建词表:所有不重复的字符 + 未知词标记<UNK>
    vocab = list(char_set) + ["<UNK>"]

    # 创建词到索引的映射字典
    word2idx = {word: idx for idx, word in enumerate(vocab)}

    # 将每首诗转换为索引序列
    sequences = []
    for poem in poems:
        seq = [word2idx.get(word, word2idx["<UNK>"]) for word in poem]
        sequences.append(seq)

    return sequences, word2idx, vocab

# 调用预处理函数
sequences, word2idx, vocab = process_poems("data/poems.txt")

数据集下载:https://pan.baidu.com/s/13qx0ximJZUTX8HWQ-kd7AA?pwd=7spw

代码解读

  1. 首先读取诗歌文件,逐行处理;

  2. 使用正则表达式re.sub删除所有中文标点符号;

  3. 将每首诗按字符分割成列表,同时收集所有不重复的字符构建词表;

  4. 添加<UNK>标记用于处理训练集中未出现的新词;

  5. 最终输出索引序列,便于模型读取。

4.2 构建训练数据集

对于语言模型任务,我们需要构建“输入-目标”对:给定前seq_len个字符作为输入,预测下一个字符作为目标。

例如,对于诗句“兰叶春葳蕤桂华秋皎洁欣欣此生意自尔为佳节谁知林栖”,如果设定seq_len=24:

  • 输入

    :前24个字符“兰叶春葳蕤桂华秋皎洁欣欣此生意自尔为佳节谁知林栖”

  • 目标

    :后24个字符“叶春葳蕤桂华秋皎洁欣欣此生意自尔为佳节谁知林栖者”

class PoetryDataset(Dataset):
    """
    自定义诗歌数据集
    继承自torch.utils.data.Dataset,实现__len__和__getitem__方法
    """
    def __init__(self, sequences, seq_len):
        """
        Args:
            sequences: 所有诗的索引序列列表
            seq_len: 序列长度(每个训练样本包含的字符数)
        """
        self.seq_len = seq_len
        self.data = []

        # 对每首诗,滑动窗口生成训练样本
        for seq in sequences:
            for i in range(0, len(seq) - self.seq_len):
                # 输入:从i开始的seq_len个字符
                # 目标:从i+1开始的seq_len个字符(即预测下一个字符)
                self.data.append((seq[i:i+self.seq_len], seq[i+1:i+1+self.seq_len]))

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        x = torch.LongTensor(self.data[idx][0])
        y = torch.LongTensor(self.data[idx][1])
        return x, y

# 创建数据集实例,序列长度设为24
dataset = PoetryDataset(sequences, 24)

4.3 搭建RNN模型

模型架构采用经典的三层结构:词嵌入层 → RNN层 → 全连接层

class PoetryRNN(nn.Module):
    """
    基于RNN的古诗生成模型
    架构:词嵌入层 -> RNN层 -> 全连接输出层
    """
    def __init__(self, vocab_size, embedding_dim=128, hidden_size=256, num_layers=1):
        """
        Args:
            vocab_size: 词表大小(词汇量)
            embedding_dim: 词嵌入维度,将每个词映射为embedding_dim维向量
            hidden_size: RNN隐藏层维度
            num_layers: RNN堆叠层数
        """
        super().__init__()

        # 1. 词嵌入层:将词索引转换为稠密向量
        # 形状变换:[batch, seq_len] -> [batch, seq_len, embedding_dim]
        self.embed = nn.Embedding(
            num_embeddings=vocab_size, 
            embedding_dim=embedding_dim
        )

        # 2. RNN层:处理序列数据,捕捉时序依赖关系
        self.rnn = nn.RNN(
            input_size=embedding_dim,   # 输入特征数 = 词嵌入维度
            hidden_size=hidden_size,    # 隐藏状态维度
            num_layers=num_layers       # RNN堆叠层数
        )

        # 3. 全连接层:将RNN输出映射为词表大小的概率分布
        # 形状变换:[seq_len, batch, hidden_size] -> [seq_len, batch, vocab_size]
        self.linear = nn.Linear(
            in_features=hidden_size, 
            out_features=vocab_size
        )

    def forward(self, input, hx=None):
        """
        前向传播
        Args:
            input: 输入序列,形状 [seq_len, batch_size]
            hx: 初始隐藏状态,形状 [num_layers, batch_size, hidden_size]
        Returns:
            output: 输出概率分布,形状 [seq_len, batch_size, vocab_size]
            hidden: 最终隐藏状态,形状 [num_layers, batch_size, hidden_size]
        """
        # Step 1: 词嵌入
        embed = self.embed(input)  # [seq_len, batch, embedding_dim]

        # Step 2: RNN处理
        output, hidden = self.rnn(embed, hx)  # output: [seq_len, batch, hidden_size]

        # Step 3: 全连接层输出
        output = self.linear(output)  # [seq_len, batch, vocab_size]

        return output, hidden

# 设备配置:优先使用GPU加速
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"当前使用设备: {device}")

# 实例化模型
model = PoetryRNN(
    vocab_size=len(vocab),   # 词表大小
    embedding_dim=256,       # 词嵌入维度
    hidden_size=512,         # 隐藏层维度
    num_layers=2             # RNN层数
).to(device)

模型架构解读

  • 词嵌入层

    :将离散的词索引转换为连续的稠密向量,让模型能够学习词之间的语义关系;

  • RNN层

    :核心序列处理模块,通过循环连接保持“记忆”,捕捉诗歌中的韵律和语义依赖;

  • 全连接层

    :将RNN的输出映射为词表大小的向量,经过softmax后即为各词的概率分布。

4.4 模型训练

训练语言模型本质上是一个多分类任务:在每个时间步,模型需要从整个词表中选择正确的下一个词。

def train(model, dataset, lr, epoch_num, batch_size, device):
    """
    模型训练函数
    Args:
        model: 待训练的模型
        dataset: 训练数据集
        lr: 学习率
        epoch_num: 训练轮数
        batch_size: 批量大小
        device: 计算设备(CPU/GPU)
    """
    model.train()  # 设置为训练模式(启用dropout等)

    # 创建数据加载器,自动进行批次划分和随机打乱
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

    # 损失函数:交叉熵损失
    # 适用于多分类问题,内部结合了LogSoftmax和NLLLoss
    loss_fn = nn.CrossEntropyLoss()

    # 优化器:Adam
    # Adam结合了动量和自适应学习率的优点,是深度学习的默认选择
    optimizer = optim.Adam(model.parameters(), lr=lr)

    for epoch in range(epoch_num):
        loss_accumulate = 0  # 累加当前epoch的损失

        for batch_count, (x, y) in enumerate(dataloader):
            # 将数据移动到指定设备
            x, y = x.to(device), y.to(device)
            # x.shape: [batch, seq_len]
            # y.shape: [batch, seq_len]

            # ----- 前向传播 -----
            # 需要将x转置为[seq_len, batch]格式(因为RNN默认batch_first=False)
            output, _ = model(x.transpose(0, 1))
            # output.shape: [seq_len, batch, vocab_size]

            # ----- 计算损失 -----
            # CrossEntropyLoss期望输入形状为[batch, vocab_size]或[seq_len*batch, vocab_size]
            # 因此将output转置为[batch, seq_len, vocab_size]再计算
            loss_value = loss_fn(output.transpose(0, 1).transpose(1, 2), y)

            # ----- 反向传播 -----
            optimizer.zero_grad()  # 清空梯度
            loss_value.backward()  # 计算梯度
            optimizer.step()       # 更新参数

            # 累加损失用于监控
            loss_accumulate += loss_value.item()

        # 打印训练进度
        print(f"Epoch {epoch:02d} | Loss: {loss_accumulate/len(dataloader):.6f}")

# 开始训练
train(
    model=model,
    dataset=dataset,
    lr=1e-3,           # 学习率
    epoch_num=20,      # 训练20轮
    batch_size=32,     # 批量大小
    device=device
)

训练细节解析

  • 交叉熵损失

    (CrossEntropyLoss):衡量模型预测的概率分布与真实标签分布之间的差异。在分类问题中,交叉熵损失能够准确反映模型的预测质量。

  • Adam优化器

    :Adam能够根据训练过程动态调整学习率——训练初期梯度噪声较大时进行较大幅度调整,训练稳定后进行精细微调。这使Adam成为深度学习的首选优化器。

  • 维度转置

    :由于RNN默认batch_first=False,输入需要是[seq_len, batch]格式,因此调用x.transpose(0, 1)进行转置。

4.5 文本生成

模型训练完成后,就可以让它“创作”诗歌了。生成过程采用自回归方式:每生成一个字符后,将其作为输入继续生成下一个字符,直到达到预设长度。

def generate_poem(model, word2idx, vocab, start_token, line_num=4, line_length=7):
    """
    生成古诗
    Args:
        model: 训练好的模型
        word2idx: 词到索引的映射
        vocab: 词表
        start_token: 起始字符
        line_num: 生成的行数
        line_length: 每行诗句的字数
    Returns:
        生成的诗句字符串
    """
    model.eval()  # 设置为评估模式(禁用dropout和batch norm)

    poem = []                      # 记录生成结果
    current_line_length = line_length  # 当前句剩余需要生成的字符数

    # 获取起始字符的索引
    start_idx = word2idx.get(start_token, word2idx["<UNK>"])

    # 如果起始字符在词典中,添加到结果列表
    if start_idx != word2idx["<UNK>"]:
        poem.append(vocab[start_idx])
        current_line_length -= 1

    # 初始化输入张量和隐藏状态
    input_tensor = torch.LongTensor([[start_idx]]).to(device)  # [1, 1]
    hidden = None  # 初始隐藏状态为None,RNN内部会自动初始化为全零

    with torch.no_grad():  # 禁用梯度计算,加速推理并节省显存
        for _ in range(line_num):          # 遍历每一行
            for _ in range(2):             # 每行两句
                while current_line_length > 0:  # 生成一句中的每个字
                    # 前向传播
                    output, hidden = model(input_tensor, hidden)
                    # output.shape: [1, 1, vocab_size]

                    # 计算概率分布并采样
                    # squeeze(0)去掉序列维度,softmax转换为概率
                    prob = torch.softmax(output[0, 0], dim=-1)
                    # 从概率分布中随机采样(增加生成多样性)
                    next_token = torch.multinomial(prob, 1)

                    # 记录生成的字符
                    poem.append(vocab[next_token.item()])

                    # 更新输入为刚生成的字符
                    input_tensor = next_token.unsqueeze(0)  # [1, 1]
                    current_line_length -= 1

                # 一句结束,重置计数器并添加标点
                current_line_length = line_length
                poem.append(",")  # 单句结尾添加逗号

            poem.append("\n")  # 每行结束后换行
            # 注意:这里需要重置current_line_length,但上面已经重置了

    # 将列表转换为字符串并返回
    return "".join(poem)

# 测试生成
generated_poem = generate_poem(
    model, word2idx, vocab,
    start_token="春",      # 以"春"字开篇
    line_num=4,           # 生成4行
    line_length=7         # 每行7字(七言绝句)
)
print(generated_poem)

生成策略解析

  • 自回归生成

    :模型每次只预测下一个字符,然后将预测结果作为下一时间步的输入,如此循环直至生成完整诗歌;

  • 随机采样

    :使用torch.multinomial从概率分布中随机采样,而非总是选择概率最高的字符。这样做可以增加生成结果的多样性,避免重复输出同一内容;

  • 温度系数调优

    (可选改进):在实际应用中,可以通过softmax(logits / temperature)来控制生成的“创造性”。temperature越高,生成结果越多样(但也可能更不连贯);temperature越低,生成结果越保守(但也可能更稳定)。

五、进阶优化方向

5.1 使用LSTM/GRU替代标准RNN

前文提到,标准RNN存在梯度消失问题。一个简单的改进是用LSTM或GRU替换RNN层:

# 使用LSTM替代RNN
self.lstm = nn.LSTM(
    input_size=embedding_dim,
    hidden_size=hidden_size,
    num_layers=num_layers
)

# 或使用GRU(更轻量,训练更快)
self.gru = nn.GRU(
    input_size=embedding_dim,
    hidden_size=hidden_size,
    num_layers=num_layers
)

LSTM vs GRU 选择指南

  • LSTM

    :通过遗忘门、输入门和输出门三个门控机制控制信息流,并维护独立的细胞状态,能够稳定捕捉长距离依赖,适合长序列任务;

  • GRU

    :LSTM的简化版本,合并输入门和遗忘门为更新门,参数量减少约25-30%,训练速度快30-40%,在多数任务上性能与LSTM相当。

5.2 双向RNN

如果任务需要同时利用前文和后文信息(如命名实体识别),可以使用双向RNN:

self.rnn = nn.RNN(
    input_size=embedding_dim,
    hidden_size=hidden_size,
    num_layers=num_layers,
    bidirectional=True  # 启用双向
)
# 注意:bidirectional=True时,hidden_size会翻倍

5.3 模型调优技巧

  • 增加模型深度

    :通过增加num_layers参数,堆叠多层RNN可以增强模型的表达能力;

  • 使用Dropout正则化

    :在RNN层间添加dropout防止过拟合;

  • 梯度裁剪

    :使用torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=5)防止梯度爆炸;

  • 学习率调度

    :使用torch.optim.lr_scheduler在训练过程中动态调整学习率。

六、总结

本文从自然语言处理的基础概念出发,系统讲解了词嵌入技术和循环神经网络(RNN)的核心原理,并完整实现了一个基于PyTorch的古诗生成系统。总结要点如下:

1. 词嵌入是NLP的基石。相比传统独热编码,稠密词向量能够捕捉词语间的语义相似性,是实现计算机“理解”语言的关键技术。

2. RNN通过循环机制解决序列建模问题。其核心公式 $h_t = \tanh(h_{t-1}W_h + x_tW_x + b)$ 精妙地实现了信息的传递与记忆。尽管存在梯度消失/爆炸的局限性,但RNN及其变体LSTM、GRU仍是序列处理任务的基石。

3. 古诗生成是一个经典的序列生成任务。通过“词嵌入层 → RNN层 → 全连接层”的标准架构,配合滑动窗口构建训练样本,可以训练出具有一定创作能力的“AI诗人”。

4. 自回归生成策略与随机采样是文本生成的核心技巧,在保证语义连贯性的同时增加了生成结果的多样性。

随着大模型时代的到来,Transformer架构凭借其并行计算能力和全局依赖捕捉能力,在许多NLP任务中取代了RNN。但RNN轻量、高效、易于理解的特点,使其在教育、边缘计算和资源受限场景中依然具有不可替代的价值。理解RNN的原理,也为学习更复杂的Transformer模型打下了坚实的基础。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐