一个基于LSTM的字符级文本生成模型的训练+使用(pytorch)

一、​​​​代码实现

1. 配置文件  config.py

import torch
# 设备配置
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 超参数和配置
SEQ_LENGTH = 100       # 输入序列长度
BATCH_SIZE = 64        # 批大小
EMBEDDING_DIM = 256    # 嵌入层维度
HIDDEN_SIZE = 512      # LSTM隐藏层大小
NUM_LAYERS = 2         # LSTM层数
NUM_EPOCHS = 30        # 训练轮数
LEARNING_RATE = 0.001  # 学习率
TEXT_PATH = 'shakespeare.txt'  # 文本路径
MODEL_SAVE_PATH = 'text_generation_model.pth'  # 模型保存路径

 2. 数据加载、预处理  data_preprocessing.py

import numpy as np
import torch
from torch.utils.data import DataLoader, TensorDataset
from config import *

def preprocess_data():
    # 加载并预处理文本数据
    with open(TEXT_PATH, 'r', encoding='utf-8') as f:
        text = f.read()

    # 创建字符映射
    chars = sorted(list(set(text)))
    char_to_idx = {c: i for i, c in enumerate(chars)}
    idx_to_char = {i: c for i, c in enumerate(chars)}

    # 文本转索引序列
    text_indices = np.array([char_to_idx[c] for c in text])

    # 创建训练样本
    samples = []
    for i in range(0, len(text) - SEQ_LENGTH):
        samples.append(text_indices[i:i + SEQ_LENGTH + 1])
    samples = np.array(samples)

    # 分割输入和目标
    inputs = samples[:, :-1]
    targets = samples[:, 1:]

    # 转换为PyTorch数据集
    dataset = TensorDataset(
        torch.LongTensor(inputs),
        torch.LongTensor(targets)
    )
    
    return DataLoader(dataset, BATCH_SIZE, shuffle=True), char_to_idx, idx_to_char

 3. 模型定义   model.py

import torch.nn as nn
from config import DEVICE

class CharLSTM(nn.Module):
    def __init__(self, vocab_size):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, EMBEDDING_DIM)
        self.lstm = nn.LSTM(EMBEDDING_DIM, HIDDEN_SIZE, NUM_LAYERS, batch_first=True)
        self.fc = nn.Linear(HIDDEN_SIZE, vocab_size)

        # 保存超参数用于隐藏状态初始化
        self.hidden_size = HIDDEN_SIZE
        self.num_layers = NUM_LAYERS

    def forward(self, x, hidden):
        x = self.embed(x)
        out, hidden = self.lstm(x, hidden)
        out = self.fc(out)
        return out, hidden

    def init_hidden(self, batch_size):
        """初始化隐藏状态"""
        return (torch.zeros(self.num_layers, batch_size, self.hidden_size).to(DEVICE),
                torch.zeros(self.num_layers, batch_size, self.hidden_size).to(DEVICE))

4. 训练  train.py

import torch
import torch.nn as nn
from model import CharLSTM
from data_preprocessing import preprocess_data
from config import *

def train():
    # 加载数据
    dataloader, char_to_idx, _ = preprocess_data()
    vocab_size = len(char_to_idx)

    # 初始化模型并移动到GPU
    model = CharLSTM(vocab_size).to(DEVICE)
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)

    # 训练循环
    for epoch in range(NUM_EPOCHS):
        for inputs, targets in dataloader:
            # 将数据移动到GPU
            inputs = inputs.to(DEVICE)
            targets = targets.to(DEVICE)

            # 动态获取当前batch大小
            batch_size = inputs.size(0)
            hidden = model.init_hidden(batch_size)

            optimizer.zero_grad()
            outputs, hidden = model(inputs, hidden)
            loss = criterion(outputs.view(-1, vocab_size), targets.view(-1))
            loss.backward()
            optimizer.step()

            # 切断隐藏状态的历史梯度
            hidden = tuple(h.detach() for h in hidden)

        print(f'Epoch {epoch + 1}/{NUM_EPOCHS} Loss: {loss.item():.4f}')

    # 保存模型
    torch.save(model.state_dict(), MODEL_SAVE_PATH)

if __name__ == '__main__':
    train()

5. 文本生成

import torch
from model import CharLSTM
from config import *

def generate(start_seq, num_chars=500):
    # 加载字符映射
    with open(TEXT_PATH, 'r') as f:
        text = f.read()
    chars = sorted(list(set(text)))
    char_to_idx = {c: i for i, c in enumerate(chars)}
    idx_to_char = {i: c for i, c in enumerate(chars)}

    # 加载模型
    model = CharLSTM(len(chars)).to(DEVICE)
    model.load_state_dict(torch.load(MODEL_SAVE_PATH))
    model.eval()

    # 生成过程
    input_seq = torch.LongTensor([[char_to_idx[c] for c in start_seq]])
    hidden = model.init_hidden(1)  # batch_size=1

    generated = []
    for _ in range(num_chars):
        output, hidden = model(input_seq, hidden)
        prob = torch.softmax(output[0, -1], dim=-1)
        char_id = torch.multinomial(prob, 1).item()

        generated.append(idx_to_char[char_id])
        input_seq = torch.cat([input_seq[:, 1:],
                               torch.LongTensor([[char_id]]).unsqueeze(0)],
                              dim=1)

    return start_seq + ''.join(generated)

if __name__ == '__main__':
    print(generate("ROMEO:", 1000))

二、代码说明

1. 各个文件的作用 

  1. config.py:集中管理所有超参数和路径配置

  2. data_preprocessing.py:数据加载、预处理和数据集创建

  3. model.py:定义LSTM网络结构

  4. train.py:模型训练流程

  5. generate.py:文本生成功能

2. 文件执行顺序

  1. 运行train.py进行模型训练

  2. 使用generate.py生成文本

  3. 可调整config.py中的参数进行实验

3. 注意事项

  • 如果GPU内存不足,可以尝试减小 BATCH_SIZE 或 HIDDEN_SIZE

  • 如果没有GPU,代码会自动回退到CPU运行。

  • 可以使用 nvidia-smi 命令监控GPU的使用情况。

三、主要功能总结

1. 文本数据预处理

  • 功能:从文本文件中加载数据,并将其转换为模型可处理的格式。

  • 具体实现

    • 加载文本文件。

    • 构建字符到索引的映射(char_to_idx)和索引到字符的映射(idx_to_char)。

    • 将文本转换为索引序列。

    • 将数据分割为输入序列和目标序列,并创建PyTorch的 DataLoader 以便批量加载数据。

2. LSTM模型定义

  • 功能:定义一个基于LSTM的神经网络模型,用于学习文本的字符级模式。

  • 具体实现

    • 使用 nn.Embedding 将字符索引映射为稠密向量。

    • 使用 nn.LSTM 处理序列数据,捕捉文本的上下文信息。

    • 使用 nn.Linear 将LSTM的输出映射为字符概率分布。

    • 提供隐藏状态的初始化方法(init_hidden)。

3. 模型训练

  • 功能:训练LSTM模型,使其能够学习文本数据的分布。

  • 具体实现

    • 使用交叉熵损失函数(nn.CrossEntropyLoss)计算损失。

    • 使用Adam优化器更新模型参数。

    • 在每个epoch中遍历数据集,计算损失并反向传播。

    • 保存训练好的模型权重到文件(text_generation_model.pth)。

4. 文本生成

  • 功能:使用训练好的模型生成新的文本。

  • 具体实现

    • 加载训练好的模型权重。

    • 根据输入的起始字符串(如 "ROMEO"),逐步预测下一个字符。

    • 使用概率采样(torch.multinomial)生成多样性文本。

    • 将生成的字符拼接成完整的文本并返回。

5. 模块化设计

  • 功能:将代码按功能拆分为多个模块,便于维护和扩展。

  • 具体实现

    • config.py:集中管理超参数和路径配置。

    • data_preprocessing.py:处理数据加载和预处理。

    • model.py:定义LSTM模型结构。

    • train.py:实现模型训练逻辑。

    • generate.py:实现文本生成逻辑。

6. 主要技术点

  • 字符级建模:以字符为单位进行文本生成,适合生成任意文本。

  • LSTM网络:使用LSTM捕捉文本的长期依赖关系。

  • 概率采样:在生成文本时引入随机性,避免生成重复或单调的文本。

  • 模块化设计:将数据预处理、模型定义、训练和生成逻辑分离,提高代码可读性和可维护性。

7. 应用场景

  • 文本生成:生成类似莎士比亚风格的文本。

  • 语言模型:学习文本数据的分布,可用于文本补全、对话生成等任务。

  • 教育用途:帮助理解LSTM和字符级语言模型的工作原理。

8. 改进空间

  • 性能优化:有条件的可以使用更大的数据集和更深的网络提升生成质量。

  • 多样性控制:在生成过程中引入温度参数(temperature)控制生成文本的多样性。

  • GPU支持:将模型和数据迁移到GPU以加速训练和生成。

  • 更复杂的模型:有条件的可以尝试使用Transformer或GPT等更先进的模型架构。

### 循环神经网络 (RNN) 文本生成模型实现教程 #### 加载和预处理数据 为了使用 R 语言和 Keras 构建循环神经网络进行文本生成,首先需要准备并清理输入的数据集。这通常涉及读取文本文件、去除不必要的字符、分词化以及创建字符到整数映射表等操作。 ```r library(keras) text <- readLines("path_to_your_text_file.txt", warn = FALSE) cat(text, file = "cleaned_text.txt") # 清洗后的文本保存至新文件 raw_text <- tolower(readChar("cleaned_text.txt", file.info("cleaned_text.txt")$size)) chars <- unique(strsplit(raw_text, NULL)[[1]]) char_indices <- data.frame(char = chars, index = seq_along(chars)) seq_length <- 40 step <- 3 sentences <- character() next_chars <- character() for(i in 1:(nchar(raw_text)-seq_length-step)){ sentences <- c(sentences, substr(raw_text,i,(i+seq_length-1))) next_chars <- c(next_chars,substr(raw_text,(i+seq_length),(i+seq_length))) } ``` 上述代码片段展示了如何加载文本文件,并对其进行初步清洗与分割成固定长度的句子及其对应的下一个字符[^1]。 #### 定义模型架构 接下来定义一个简单的单层 RNN 结构来作为基础框架: ```r model <- keras_model_sequential() %>% layer_lstm(units=128, input_shape=c(seq_length, length(unique_chars))) %>% layer_dense(units=length(unique_chars), activation='softmax') summary(model) compile( model, loss="categorical_crossentropy", optimizer=optimizer_rmsprop(lr=0.01), metrics=list('accuracy')) ``` 这里选择了 LSTM 层代替普通的 RNN 单元因为其能够更好地捕捉长期依赖关系,在许多情况下表现更佳[^2]。 #### 训练过程 一旦完成了数据准备工作并且搭建好了合适的网络拓扑结构,则可以着手训练模型了。此阶段涉及到将之前构造好的样本送入网络中迭代更新权重参数直至收敛为止。 ```r history <- fit_generator( model, generator=batch_generator(), steps_per_epoch=floor(length(sentences)/batch_size), epochs=epochs_count, verbose=TRUE, callbacks=list(checkpoint_callback()) ) ``` `fit_generator()` 函数允许以批处理的方式逐步提供训练实例给算法学习;同时还可以设置回调函数以便监控进度或保存最佳版本的权值矩阵副本。 #### 使用训练完成的模型来进行预测/生成新的文本串 当经过充分的学习周期后,就可以利用最终得到的最佳状态下的参数集合去尝试创造全新的字符串出来啦! ```r start_index <- sample(1:nrow(char_indices), size=1) generated <- "" sentence <- raw_text[start_index:(start_index + seq_length - array(0,dim=c(1,seq_length,length(unique_chars))) for(t in 1:seq_length){ char_pos <- which(char_indices[, 'char'] == substring(sentence,t,t)) x_pred[,,t] <- as.numeric(char_pos==unique_chars) } preds <- predict(model,x_pred,verbose=0)[1,] next_char_idx <- sample(which(preds>sort(preds,decreasing=T)[sample_temperature]),1) next_char <- char_indices[next_char_idx,'char'] generated <- paste(generated,next_char,sep="") sentence <- paste(substring(sentence,-length(sentence)+2,nchar(sentence)),next_char) } print(paste("Generated text:", generated)) ``` 这段脚本会随机选取一段起始位置作为种子序列,接着按照设定的概率分布采样出后续可能出现的一个个字母直到达到指定的最大长度限制为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值