Домашняя страницаzicesun.com
RNN
Судить о поле задачи на основе имени человека - очень интересная работа.Существует много методов предсказания пола английского имени и предсказания имени, таких как Наивный Байес, SVM, нейронная сеть и т. д. При предсказании , вы можете использовать Существуют также различные характеристики: последняя буква имени, две буквы (2-грамма), является ли последняя буква гласной или согласной.
Мы расскажем, как использовать RNN для предсказания пола английских имен.нажмитеdatasetСкачать набор данных
обработка данных
Набор данных, используемый в этом проекте, представляет собой набор данных на английском языке.Соотношение мужских и женских данных составляет, в целом соотношение данных более подходящее.
- женщина 5001
- мужской 2943 бара
При обработке данных нормализуйте данные:
- строчные все символы;
- Удалите все неалфавитные данные;
- Нумерация букв, каждой букве соответствует порядок, в котором отмечены символы;
- Особенности модели — отдельные буквы имени;
- Тестовые данные и данные обучения разделены в соответствии с
пропорции разделены.
модель RNN
Модель имеет три уровня, а именно уровень RNN, полностью подключенный уровень и уровень softmax.Уровень RNN принимает сетевую структуру LSTM.
class Classifier(nn.Module):
def __init__(self, input_size, hidden_size, embedding_size):
super(Classifier, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.embedding_size = embedding_size
self.embeddings = nn.Embedding(self.input_size, self.embedding_size)
nn.init.xavier_normal(self.embeddings.weight.data)
self.drop = nn.Dropout(p=0.1)
self.rnn = nn.LSTM(input_size=self.embedding_size, hidden_size=self.hidden_size,
batch_first=True)
self.out = nn.Linear(self.hidden_size, 2)
self.log_softmax = nn.LogSoftmax(dim=-1)
def forward(self, input, length):
input = self.embeddings(input)
input = self.drop(input)
input_packed = nn.utils.rnn.pack_padded_sequence(input, length, batch_first=True)
# out_packed, (ht, ct) = self.rnn(input_packed, None)
# out = nn.utils.rnn.pad_packed_sequence(out_packed, batch_first=True)
_, (ht, _) = self.rnn(input_packed, None)
out = self.out(ht)
out = self.log_softmax(out)
return out
Параметры сети РНСbatch_first=TrueПри использовании RNN (в том числе LSTM\GRU и т. д.) для задач NLP обычно требуется заполнение для коротких предложений в одном пакете.Эти символы заполнения не должны использоваться для вычисления вывода, скрытого состояния, потери.
В pytorch при обработке задач NLP первое, что нужно сделать, это дополнить последовательность данных, чтобы длина данных пакета была одинаковой, а затем отсортировать в обратном порядке в соответствии с исходной длиной последовательности.В нашей задаче нам нужно обработать последовательность имен и метки. На данный момент есть два варианта:
- После того, как последовательность имен выстроена по порядку, метки также корректируются в соответствии с порядком имен.
- После того, как последовательность имен будет упорядочена по длине, удалите ее из очереди, а затем настройте результирующую последовательность в соответствии с исходным порядком. Здесь применяется первый метод.
length = np.array([len(name) for name in names_list])
sort_idx = np.argsort(-length)
max_len = max(length)
name_tensors = torch.zeros(len(names), max_len).to(torch.long)
for i, idx in enumerate(sort_idx):
for j, e in enumerate(names_list[idx]):
name_tensors[i][j] = e
names_lengths = torch.from_numpy(length[sort_idx]).to(torch.long)
labels = labels[sort_idx].view(1, -1).squeeze(0)
тренироваться
classifier = Classifier(len(chars), 128, 128)
optimizer = optim.RMSprop(classifier.parameters(),lr=0.001)
loss_func = nn.NLLLoss()
total_loss = 0
total_step = 0
while True:
for data in train_loader:
total_step += 1
names = data[0]
labels =data[1]
name_tensors, labels, names_lengths = name_to_tensor(names, labels)
out = classifier(name_tensors, names_lengths).view(-1, 2)
# print(out)
# print(labels)
loss = loss_func(out, labels)
total_loss += loss.item()
loss.backward()
optimizer.step()
if total_step % 50 == 0:
print('%dth step, avg_loss: %0.4f'%(total_step, total_loss/total_step))
with torch.no_grad():
for data in test_loader:
names = data[0]
labels = data[1]
name_tensors, labels, names_lengths = name_to_tensor(names, labels)
out = classifier(name_tensors, names_lengths).view(-1, 2)
result = torch.argmax(out,dim=-1 )
result = (result == labels).to(torch.float)
print(torch.mean(result))
break
Полный код и экспериментальные результаты
код
# coding=utf-8
import random
from torch.utils.data import DataLoader
from torch.utils.data import Dataset
import torch.nn as nn
import numpy as np
import torch
import torch.optim as optim
################################load dataset ###################
################################################################
def load_data(path, label):
names = []
with open(path) as f:
lines = f.readlines()
for l in lines:
names.append((l.strip('\n'), label))
return names
female_names = load_data('../datasets/names_gender/eng/female.txt', 0)
male_names = load_data('../datasets/names_gender/eng/male.txt', 1)
names = female_names + male_names
random.shuffle(names)
# 将数据划分为训练集和测试集
train_dataset = names[: int(len(names)*0.9)]
test_dataset = names[int(len(names)*0.9):]
# padding的字符为0,
chars = [0] + [chr(i) for i in range(97,123)]
# print(chars)
class NameDataset(Dataset):
def __init__(self, data):
self.data = data
def __getitem__(self, index):
# 这里可以对数据进行处理,比如讲字符数值化
data = self.data[index]
name = data[0]
label = data[1]
return name, label
def __len__(self):
return len(self.data)
train_dataset = NameDataset(train_dataset)
test_dataset = NameDataset(test_dataset)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=500, shuffle=True)
# 序列按照长短顺序逆序排列
def name_to_tensor(names, labels):
names_list = []
for name in names:
char_list = []
for ch in name.lower():
try:
char_list.append(chars.index(ch))
except:
char_list.append(0)
# name_tensor = torch.from_numpy(np.array(char_list))
names_list.append(char_list)
length = np.array([len(name) for name in names_list])
sort_idx = np.argsort(-length)
max_len = max(length)
name_tensors = torch.zeros(len(names), max_len).to(torch.long)
for i, idx in enumerate(sort_idx):
for j, e in enumerate(names_list[idx]):
name_tensors[i][j] = e
names_lengths = torch.from_numpy(length[sort_idx]).to(torch.long)
labels = labels[sort_idx].view(1, -1).squeeze(0)
return name_tensors, labels, names_lengths
######################### model ###################
# #################################################################
class Classifier(nn.Module):
def __init__(self, input_size, hidden_size, embedding_size):
super(Classifier, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.embedding_size = embedding_size
self.embeddings = nn.Embedding(self.input_size, self.embedding_size)
nn.init.xavier_normal(self.embeddings.weight.data)
self.drop = nn.Dropout(p=0.1)
self.rnn = nn.LSTM(input_size=self.embedding_size, hidden_size=self.hidden_size,
batch_first=True)
self.out = nn.Linear(self.hidden_size, 2)
self.log_softmax = nn.LogSoftmax(dim=-1)
def forward(self, input, length):
input = self.embeddings(input)
input = self.drop(input)
input_packed = nn.utils.rnn.pack_padded_sequence(input, length, batch_first=True)
# out_packed, (ht, ct) = self.rnn(input_packed, None)
# out = nn.utils.rnn.pad_packed_sequence(out_packed, batch_first=True)
_, (ht, _) = self.rnn(input_packed, None)
out = self.out(ht)
out = self.log_softmax(out)
return out
classifier = Classifier(len(chars), 128, 128)
optimizer = optim.RMSprop(classifier.parameters(),lr=0.001)
loss_func = nn.NLLLoss()
total_loss = 0
total_step = 0
while True:
for data in train_loader:
total_step += 1
names = data[0]
labels =data[1]
name_tensors, labels, names_lengths = name_to_tensor(names, labels)
out = classifier(name_tensors, names_lengths).view(-1, 2)
# print(out)
# print(labels)
loss = loss_func(out, labels)
total_loss += loss.item()
loss.backward()
optimizer.step()
if total_step % 50 == 0:
print('%dth step, avg_loss: %0.4f'%(total_step, total_loss/total_step))
with torch.no_grad():
for data in test_loader:
names = data[0]
labels = data[1]
name_tensors, labels, names_lengths = name_to_tensor(names, labels)
out = classifier(name_tensors, names_lengths).view(-1, 2)
result = torch.argmax(out,dim=-1 )
result = (result == labels).to(torch.float)
print(torch.mean(result))
break
Результаты экспериментов
50th step, avg_loss: 0.4905
100th step, avg_loss: 0.4662
tensor(0.8200)
150th step, avg_loss: 0.4535
200th step, avg_loss: 0.4467
tensor(0.8020)
250th step, avg_loss: 0.4396
300th step, avg_loss: 0.4320
tensor(0.8260)
350th step, avg_loss: 0.4270
400th step, avg_loss: 0.4217
tensor(0.8300)
450th step, avg_loss: 0.4178
500th step, avg_loss: 0.4117
550th step, avg_loss: 0.4073
tensor(0.8140)
600th step, avg_loss: 0.4027
650th step, avg_loss: 0.3994
tensor(0.8260)
700th step, avg_loss: 0.3971
750th step, avg_loss: 0.3934
tensor(0.8100)
800th step, avg_loss: 0.3908
850th step, avg_loss: 0.3884
tensor(0.8160)
900th step, avg_loss: 0.3861
950th step, avg_loss: 0.3832
1000th step, avg_loss: 0.3822
tensor(0.8140)
1050th step, avg_loss: 0.3801
1100th step, avg_loss: 0.3789
tensor(0.8060)
1150th step, avg_loss: 0.3769
1200th step, avg_loss: 0.3757
tensor(0.8420)
Во время обучения самый высокий показатель правильных результатов проверки может достигать 86%.Эта модель все еще относительно грубая, используя только одну букву имени в качестве признака, модель не может полностью проанализировать взаимосвязь между буквами.Метод 2-грамм , результат может быть лучше. Это будет реализовано позже.
[1] pytorch RNN обрабатывает последовательности переменной длины