Pytorch实现基于CharRNN的文本分类与生成示例

1. 简介

Pytorch是一种深度学习框架,它是使用Python语言编写的,是一种研究人员能够更快速地训练神经网络和进行研究的工具。现在,深度学习在自然语言处理中应用已经得到广泛关注,并取得了许多的突破。本文将介绍如何使用Pytorch实现一个基于CharRNN的文本分类与生成示例。

2. CharRNN概述

CharRNN(Char-Level Recurrent Neural Network)是一种基于循环神经网络的生成模型,主要应用于文本生成和文本分类等领域。在CharRNN中,输入和输出都是字符序列,而不是如传统的单词级别模型一样。由于CharRNN能够捕捉到字符在文本中的顺序信息,因此比传统的N-gram模型等模型表现更加优异。

在CharRNN中,模型输入一个字符向量,然后通过循环层不断演化,输出下一个字符概率的一个向量。模型通过对输入字符序列的历史状态进行编码,从而实现对特定任务的学习和预测。

3. 数据预处理

3.1 数据获取

为了训练我们的CharRNN模型,我们需要有文本数据集。在这个示例中,我们将使用名为“data/names”目录下的人名数据集。本数据集由较常见的人名(如英文、法文、德文、意大利语、俄语、西班牙语)组成,并包含18个人名文件。

我们可以使用Python的os库检查当前工作目录下的“data/names”目录中是否存在人名文件:

import os

print(os.listdir('data/names'))

3.2 字符向量化

在CharRNN中,输入和输出都是字符序列。为了将字符序列进行训练,我们需要将每个字符转换为一个独立的数字标识。在本示例中,我们可以使用Python的collections.Counter函数,将所有字符向量化后保存在char_to_idx字典中。

以下是代码示例:

import collections

# 获取所有文件中的字符序列

def read_file(filename):

with open(filename, 'r', encoding='utf-8') as f:

return f.read()

# 获取数据集中的所有字符

def get_chars():

files = os.listdir('data/names')

words = []

for file in files:

path = os.path.join('data/names', file)

word = read_file(path)

words.append(word)

chars = list(''.join(words))

return chars

# 字符向量化

chars = get_chars()

char_counter = collections.Counter(chars)

char_to_idx = {char: idx for idx, char in enumerate(char_counter.keys())}

num_chars = len(char_counter)

3.3 数据集准备

在本示例中,我们的数据集由许多人名组成。为了训练我们的CharRNN模型,我们需要将这些人名进行标记。在标记之前,我们需要将这些人名划分为输入和输出序列,以便我们可以训练模型进行人名生成。

在本示例中,我们将使用一个窗口大小为10的滑动窗口来生成我们的序列。然后,我们将这些输入和输出序列保存在X和Y列表中,以便稍后用于训练模型。

以下是代码示例:

import numpy as np

# 获取输入和输出序列

def get_input_and_output_names(names):

X = []

Y = []

for name in names:

name = f' {name}' #增加一个空格来区分边界

name = list(name)

if len(name) < window_size + 1:

continue

for i in range(len(name) - window_size):

x = name[i: i + window_size]

y = name[i + window_size]

X.append([char_to_idx[c] for c in x])

Y.append(char_to_idx[y])

return X, Y

# 生成训练数据

window_size = 10

names = [read_file(os.path.join('data/names', file)) for file in os.listdir('data/names')]

X, Y = get_input_and_output_names(names)

X = np.array(X)

Y = np.array(Y)

print(X.shape, Y.shape)

4. CharRNN模型

在本示例中,我们将使用CharRNN模型进行人名生成。CharRNN模型是一种循环神经网络,可以递归地生成一系列字符,一次生成一个字符。

CharRNN模型主要有三个部分组成:

嵌入层(Embedding layer):输入字符的独热向量

循环神经网络层(Recurrent neural network layer):将前一个字符的状态和当前字符的嵌入向量进行组合

输出层(Output layer):将循环神经网络的最后一层输出进行softmax变换,生成一个字符概率分布

以下是CharRNN模型的代码实现:

import torch

import torch.nn as nn

class CharRNN(nn.Module):

def __init__(self, num_chars, embedding_size, hidden_size, num_layers, dropout):

super(CharRNN, self).__init__()

self.embedding_size = embedding_size

self.hidden_size = hidden_size

self.num_layers = num_layers

self.embedding = nn.Embedding(num_embeddings=num_chars, embedding_dim=embedding_size)

self.lstm = nn.LSTM(input_size=embedding_size, hidden_size=hidden_size, num_layers=num_layers, dropout=dropout, batch_first=True)

self.fc = nn.Linear(hidden_size, num_chars)

def forward(self, x, state=None):

x = self.embedding(x)

output, (h, c) = self.lstm(x, state)

output = self.fc(output[:, -1, :])

return output, (h, c)

5. 训练模型

5.1 准备数据

在本示例中,我们将使用Pytorch DataLoader来准备我们的训练数据。我们将从X和Y列表中创建一个训练数据集,并使用Pytorch DataLoader将数据集划分为小批量,以便我们可以批量地进行训练。

以下是代码示例:

import torch.utils.data as data_utils

# 将输入和输出的数据样本组合成数据集(DataSet)

class NameDataSet(data_utils.Dataset):

def __init__(self, X, Y):

self.X = X

self.Y = Y

def __getitem__(self, index):

return self.X[index], self.Y[index]

def __len__(self):

return len(self.X)

# 创建批量数据集(DataLoader)

batch_size = 256

train_dataset = NameDataSet(torch.Tensor(X).long(), torch.Tensor(Y).long())

train_loader = data_utils.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

5.2 定义损失函数和优化器

在训练模型之前,我们需要定义损失函数和优化器。在本示例中,我们将使用交叉熵作为损失函数,并使用Adam作为优化器。

以下是代码示例:

import torch.optim as optim

# 定义损失函数和优化器

criterion = nn.CrossEntropyLoss()

optimizer = optim.Adam(model.parameters(), lr=0.001)

5.3 开始训练

一旦我们完成了准备数据和定义损失函数和优化器,我们就可以为我们的CharRNN模型训练数据并生成人名了。在本示例中,我们将迭代150个纪元,每个纪元将输入批量数量个数据样本,进行损失函数计算和反向传播。

以下是代码示例:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 训练模型

num_epochs = 150

model = CharRNN(num_chars, embedding_size=128, hidden_size=256, num_layers=2, dropout=0.2).to(device)

for epoch in range(num_epochs):

total_loss = 0

for i, (inputs, targets) in enumerate(train_loader):

inputs, targets = inputs.to(device), targets.to(device)

model.zero_grad()

state = None

for j in range(window_size):

output, state = model(inputs[:, j:j+1], state)

loss = criterion(output, targets)

loss.backward()

optimizer.step()

total_loss += loss.item()

avg_loss = total_loss / len(train_loader)

print(f'Epoch [{epoch+1}/{num_epochs}], Average Loss: {avg_loss:.4f}')

6. 生成人名

训练CharRNN模型后,我们可以使用模型来生成新的人名。为了生成人名,我们需要传入给定前缀,然后从概率分布中随机抽取每个字符,生成一个新的字符序列。

以下是代码示例:

temperature = 0.6 # 用于调整生成人名的自信度,调整值越小生成的人名越保守

start_char = 'A'

num_chars_to_generate = 20

model.eval()

model.to('cpu')

with torch.no_grad():

x = torch.Tensor([char_to_idx[start_char]]).long()

state = None

for i in range(num_chars_to_generate):

output, state = model(x[None, :], state)

probs = nn.functional.softmax(output/temperature, dim=-1)[0]

char_idx = torch.multinomial(probs, 1)

x[0] = char_idx.squeeze().item()

print(list(char_to_idx.keys())[list(char_to_idx.values()).index(x)])

7. 总结

在本示例中,我们介绍了如何使用Pytorch实现一个基于CharRNN的文本分类与生成示例,涵盖了从数据预处理到模型训练和人名生成的完整流程。CharRNN模型通过对输入字符序列的历史状态进行编码,学习一个字符概率分布,然后根据概率分布生成一个新的字符序列。CharRNN模型表现良好,其能够生成与数据集中相似的高质量的人名。

后端开发标签