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模型表现良好,其能够生成与数据集中相似的高质量的人名。