Предсказание пола английских имен на основе RNN

глубокое обучение

Домашняя страницаzicesun.com

RNN

Судить о поле задачи на основе имени человека - очень интересная работа.Существует много методов предсказания пола английского имени и предсказания имени, таких как Наивный Байес, SVM, нейронная сеть и т. д. При предсказании , вы можете использовать Существуют также различные характеристики: последняя буква имени, две буквы (2-грамма), является ли последняя буква гласной или согласной.

Мы расскажем, как использовать RNN для предсказания пола английских имен.нажмитеdatasetСкачать набор данных

обработка данных

Набор данных, используемый в этом проекте, представляет собой набор данных на английском языке.Соотношение мужских и женских данных составляет3:5, в целом соотношение данных более подходящее. - женщина 5001 - мужской 2943 бара

При обработке данных нормализуйте данные:

  1. строчные все символы;
  2. Удалите все неалфавитные данные;
  3. Нумерация букв, каждой букве соответствует порядок, в котором отмечены символы;
  4. Особенности модели — отдельные буквы имени;
  5. Тестовые данные и данные обучения разделены в соответствии с1:9пропорции разделены.

модель 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 первое, что нужно сделать, это дополнить последовательность данных, чтобы длина данных пакета была одинаковой, а затем отсортировать в обратном порядке в соответствии с исходной длиной последовательности.В нашей задаче нам нужно обработать последовательность имен и метки. На данный момент есть два варианта:

  1. После того, как последовательность имен выстроена по порядку, метки также корректируются в соответствии с порядком имен.
  2. После того, как последовательность имен будет упорядочена по длине, удалите ее из очереди, а затем настройте результирующую последовательность в соответствии с исходным порядком. Здесь применяется первый метод.
    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 обрабатывает последовательности переменной длины