使用循环神经网络学习汉语拼音拼写:数据准备与模型构建

本文介绍如何使用循环神经网络 (RNN) 学习汉语拼音拼写,重点讲解数据准备和模型构建。文章提供完整代码示例,涵盖数据集定义、模型设计、训练和预测等步骤。

1. 数据准备

拼音数据(无声调): https://www.jianguoyun.com/p/DQ3els0Q-rqYBhi3pIgFIAA

数据集定义:

  • 采用字符模型,因此一个字符为一个样本。
  • 每个样本采用 one-hot 编码。
  • 样本是时间相关的,分别实现序列的随机采样和序列的顺序划分。
  • 标签 Y 与 X 同形状,但时间超前 1。

准备数据:

  • 一次梯度更新使用的数据形状为:(时间步,Batch,类别数)。

2. 模型构建

实现基本循环神经网络模型:

  • 循环单元为 nn.RNNGRU
  • 输出层的全连接使用 RNN 所有时间步的输出。
  • 隐状态初始值为 0。

测试前向传播:

  • 如果采用顺序划分,需梯度截断。

3. 训练与预测

训练:

  • 损失函数为平均交叉熵。

预测:

  • 给定一个前缀,进行单步预测和 K 步预测。

代码示例

import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
import numpy as np

# 定义数据集
class PinyinDataset(Dataset):
    def __init__(self, data_file):
        with open(data_file, 'r', encoding='utf-8') as f:
            self.data = f.read()
        
        self.vocab = list(set(list(self.data)))
        self.char2idx = {char: idx for idx, char in enumerate(self.vocab)}
        self.idx2char = {idx: char for idx, char in enumerate(self.vocab)}
        
        self.data_size = len(self.data)
        self.vocab_size = len(self.vocab)
        
    def __getitem__(self, index):
        x = np.zeros((self.vocab_size,), dtype=np.float32)
        x[self.char2idx[self.data[index]]] = 1.0
        y = np.zeros((self.vocab_size,), dtype=np.float32)
        y[self.char2idx[self.data[(index+1) % self.data_size]]] = 1.0
        return x, y
    
    def __len__(self):
        return self.data_size

# 定义模型
class RNNModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, num_layers=1, rnn_type='rnn'):
        super(RNNModel, self).__init__()
        self.rnn_type = rnn_type
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        
        self.encoder = nn.Embedding(input_size, hidden_size)
        if rnn_type == 'rnn':
            self.rnn = nn.RNN(hidden_size, hidden_size, num_layers)
        elif rnn_type == 'gru':
            self.rnn = nn.GRU(hidden_size, hidden_size, num_layers)
        self.decoder = nn.Linear(hidden_size, output_size)
        
    def forward(self, inputs, hidden):
        seq_len, batch_size, _ = inputs.size()
        inputs = self.encoder(inputs)
        output, hidden = self.rnn(inputs, hidden)
        output = self.decoder(output.view(seq_len * batch_size, -1))
        return output.view(seq_len, batch_size, -1), hidden
    
    def init_hidden(self, batch_size):
        if self.rnn_type == 'gru':
            return torch.zeros(self.num_layers, batch_size, self.hidden_size)
        else:
            return torch.zeros(self.num_layers, batch_size, self.hidden_size)

# 训练函数
def train(model, data_loader, criterion, optimizer, device):
    model.train()
    total_loss = 0.
    n_total = 0.
    hidden = model.init_hidden(batch_size=batch_size).to(device)
    
    for i, (inputs, targets) in enumerate(data_loader):
        inputs = inputs.to(device)
        targets = targets.to(device)
        
        hidden = hidden.detach()
        hidden = hidden.zero_()
        
        output, hidden = model(inputs, hidden)
        loss = criterion(output.view(-1, output_size), targets.view(-1))
        
        optimizer.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=5.0)
        optimizer.step()
        
        total_loss += loss.item() * inputs.size(0)
        n_total += inputs.size(0)
        
        if i % 100 == 0:
            print('step [{}/{}], loss: {:.4f}'.format(i+1, len(data_loader), total_loss/n_total))
        
    return total_loss / n_total

# 测试函数
def evaluate(model, data_loader, criterion, device):
    model.eval()
    total_loss = 0.
    n_total = 0.
    hidden = model.init_hidden(batch_size=batch_size).to(device)
    
    with torch.no_grad():
        for i, (inputs, targets) in enumerate(data_loader):
            inputs = inputs.to(device)
            targets = targets.to(device)

            hidden = hidden.detach()
            hidden = hidden.zero_()

            output, hidden = model(inputs, hidden)
            loss = criterion(output.view(-1, output_size), targets.view(-1))

            total_loss += loss.item() * inputs.size(0)
            n_total += inputs.size(0)

    return total_loss / n_total

# 预测函数
def predict(model, prefix, k, device):
    model.eval()
    hidden = model.init_hidden(batch_size=1).to(device)
    inputs = torch.zeros((1, 1, input_size)).to(device)
    inputs[0, 0, char2idx[prefix]] = 1.0
    outputs = [prefix]
    
    with torch.no_grad():
        for i in range(k):
            output, hidden = model(inputs, hidden)
            prob = output[-1, 0].softmax(dim=-1).cpu().numpy()
            char_idx = np.random.choice(len(prob), p=prob)
            char = idx2char[char_idx]
            outputs.append(char)
            inputs = torch.zeros((1, 1, input_size)).to(device)
            inputs[0, 0, char_idx] = 1.0
            
    return ''.join(outputs)

if __name__ == '__main__':
    # 定义超参数
    input_size = output_size = 420
    hidden_size = 256
    num_layers = 1
    batch_size = 64
    num_epochs = 10
    learning_rate = 0.01
    rnn_type = 'gru'

    # 加载数据集
    dataset = PinyinDataset('data.txt')
    data_loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

    # 初始化模型、损失函数和优化器
    model = RNNModel(input_size, hidden_size, output_size, num_layers=num_layers, rnn_type=rnn_type)
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

    # 将模型和数据放到GPU上
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model.to(device)

    # 开始训练
    for epoch in range(num_epochs):
        train_loss = train(model, data_loader, criterion, optimizer, device)
        print('Epoch [{}/{}], train loss: {:.4f}'.format(epoch+1, num_epochs, train_loss))

    # 测试模型
    test_loss = evaluate(model, data_loader, criterion, device)
    print('Test loss: {:.4f}'.format(test_loss))

    # 预测
    prefix = 'ni'
    k = 10
    idx2char = dataset.idx2char
    char2idx = dataset.char2idx
    print('prefix: {}'.format(prefix))
    for i in range(5):
        output = predict(model, prefix, k, device)
        print('output {}: {}'.format(i+1, output))

说明

  • 标题已调整为更具 SEO 优化,包含关键词 '汉语拼音'、'循环神经网络'、'数据准备' 和 '模型构建'。
  • 描述简要概述了文章内容,包含关键词和主要内容提炼。
  • 关键词包含与主题相关的词汇,方便搜索引擎识别文章主题。
  • 代码示例中,所有双引号替换为单引号,确保 JSON 格式输出。
  • 内容结构清晰,分为数据准备、模型构建、训练与预测三部分,并添加代码示例,方便理解。
汉语拼音拼写学习:循环神经网络模型数据准备与构建

原文地址: https://www.cveoy.top/t/topic/onAa 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录