Bootstrap

【NLP7-使用RNN模型构建人名分类器】

使用RNN模型构建人名分类器

1、目标

了解有关人名分类问题和有关数据

掌握适用RNN构建人名分类器实现过程

2、问题

以一个人名为输入,适用模型帮助我们判断它最有可能是来自哪一个国家。

再国际化业务中,用户注册过程中,会根据用户填写名字直接给他分配可能的国家或地区选项,以及该国家或地区的国旗,限制手机号码位数等等

3、数据
4、步骤

①、导入必备工具包

②、对data文件中的数据进行处理,满足训练要求

③、构建RNN模型(包括RNN、LSTM、GRU)

④、构建训练函数并进行训练

⑤、构建评估函数并进行预测

5、代码实现
5.1、导入必备工具包
from io import open #从io中导入文件打开方法
import glob
import os
import string
import unicodedata  #用于获得常见字母及字符规范化
import random
import time
import math
import torch
import torch.nn as nn
import matplotlib.pyplot as plt

5.2、数据处理

在 names 文件夹中有 18个 .txt 文件,且都是以某种语言名 .txt 命名。 每个 txt 文件中含有很多姓氏名,每个姓氏名独占一行,有些语言使用的是 Unicode 码(含有除了26 英文字母以外的其他字符),我们需要将其统一成 ASCII 码。

对data文件中的数据进行处理,满足训练要求

# string.ascii_letters 是大小写各26字母
all_letters = string.ascii_letters + ".,;"
# 字符的种类数
n_letters = len(all_letters)
#print("n_letters",n_letters)

#2.1字符规范化之unicode转Ascli函数,主要去掉一些语言中的重音标记,如Coté变成Cote
def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD',s)
        if unicodedata.category(c) !='Mn'
        and c in all_letters
    )

s='Coté'
a=unicodeToAscii(s)
#print(a)

结果:

n_letters 55
all_letters

‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,;’

Cote

data_path = "D:/data/code_and_data/names/"
#2.2打开指定的文件并读取所有的内容,使用strip()去除掉两侧空白符,然后以'\n'为换行符进行
def readLines(filename):
    lines = open(filename,encoding='utf-8').read().strip().split('\n')
    return [unicodeToAscii(line) for line in lines]

filename = data_path + "Chinese.txt"
result = readLines(filename)
print(result[:20])

[‘Ang’, ‘AuYong’, ‘Bai’, ‘Ban’, ‘Bao’, ‘Bei’, ‘Bian’, ‘Bui’, ‘Cai’, ‘Cao’, ‘Cen’, ‘Chai’, ‘Chaim’, ‘Chan’, ‘Chang’, ‘Chao’, ‘Che’, ‘Chen’, ‘Cheng’, ‘Cheung’]

#2.3构建人名类别列表与人名对应关系字典
category_lines ={}
#构建所有类别的列表
all_categories=[]

#遍历所有文件,利用glob.glob中可以利用正则表达式的便利
for filename in glob.glob(data_path + '*.txt'):
    #获取每个文件的文件名,起始就是得到名字的类别
    category = os.path.splitext(os.path.basename(filename))[0]
    #逐一将其装入所有类别的列表中
    all_categories.append(category)
    #读取每个文件的内容,形成名字的列表
    lines = readLines(filename)
    #按照对应的类别,将名字列表写入到category_lines字典中
    category_lines[category] = lines
    
# 语言种类数
n_categories = len(all_categories)
print("n_category:",n_categories)

print(category_lines['Italian'][:15])

n_category: 18

[‘Abandonato’, ‘Abatangelo’, ‘Abatantuono’, ‘Abate’, ‘Abategiovanni’, ‘Abatescianni’, ‘Abba’, ‘Abbadelli’, ‘Abbascia’, ‘Abbatangelo’, ‘Abbatantuono’, ‘Abbate’, ‘Abbatelli’, ‘Abbaticchio’, ‘Abbiati’]

#2.4 将人名转化为对应onehot张量
def lineToTensor(line):
    # 初始化一个全0的张量,张量的形状是(len(line),1,n_letters)
    tensor = torch.zeros((len(line), 1, n_letters))
    for li,letter in enumerate(line):
        tensor[li][0][all_letters.find(letter)]=1
    return tensor
line = "Bai"
line_tensor = lineToTensor(line)
print("line_tensor:",line_tensor)

line_tensor: tensor([[[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]]])

5.3、构建RNN模型(包括传统RNN、LSTM、GRU)
class RNN(nn.Module):
    def __init__(self,input_size,hidden_size,output_size,num_layers=1):
        """
        初始化函数
        :param input_size: 输入最后的一个维度
        :param hidden_size:隐层最后的一个维度
        :param output_size:最后线性层的输出维度
        :param num_layers:层数
        """
        super(RNN,self).__init__()
        self.input_size=input_size
        self.hidden_size = hidden_size
        self.output_size=output_size
        self.num_layers = num_layers
       #实例化预定义的RNN,三个参数input_size,hidden_size,num_layers
        self.rnn = nn.RNN(input_size,hidden_size,num_layers)
        #实例化全连接线性层,作用是RNN输出维度转化成指定的输出维度
        self.linear = nn.Linear(hidden_size,output_size)
        #实例化NN中预定义的softmax层,用于从输出层中获得类似的结果
        self.softmax=nn.LogSoftmax(dim=-1)

    def forward(self,input1,hidden):
        """
        完成传统RNN中的主要逻辑
        :param input1:输入张量,形状是1 * n_letters
        :param hidden:隐层张量,形状是self.num_layers * 1 * self.hidden_size
        输入到RNN中的张量要求是三维张量,所以需要用unsqueeze()函数扩充维度
        """
        input1 = input1.unsqueeze(0)
        #将input1和hidden输入到RNN的实例化对象中,如果num_layers=1,rr恒等于hn
        rr,hn = self.rnn(input1,hidden)
        #将从RNN中获得的结果通过线性层的变换和softmax层的处理,最终返回
        return self.softmax(self.linear(rr)),hn

    def initHidden(self):
        return torch.zeros(self.num_layers,1,self.hidden_size)

class LSTM(nn.Module):
    def __init__(self,input_size,hidden_size,output_size,num_layers=1):
        """
        初始化函数的参数与传统RNN相同
        :param input_size:输入张量x中最后一个维度
        :param hidden_size:隐藏层张量的最后一个维度
        :param output_size:线性层最后的输出维度
        :param num_layers:LSTM网络曾是
        """
        super(LSTM,self).__init__()
        self.input_size=input_size
        self.hidden_size=hidden_size
        self.output_size=output_size
        self.num_layers = num_layers
        #实例化lstm对象
        self.lstm = nn.LSTM(input_size,hidden_size,num_layers)
        #实例化线性层,作用是将LSTM网络的输出维度转换成指定的输出维度
        self.linear = nn.Linear(hidden_size,output_size)
        #实例化nn中预定义的softmax,作用从输出层的张量中得到类别结果
        self.softmax = nn.LogSoftmax(dim=-1)

    def forward(self,input1,hidden,c):
        #注意:LSTM网络的输入有3个张量,尤其不要忘记细胞状态c
        input1 = input1.unsqueeze(0)
        #将3个参数输入到LSTM对象中
        rr,(hn,cn) = self.lstm(input1,(hidden,c))
        #最后将3个张量结果全部返回,同事rr要经过线性层和softmax的处理
        return self.softmax(self.linear(rr)),hn,c

    def initHiddenAndC(self):
        #注意:对于LSTM来说,初始化的时候要同事对初始化hidden和细胞状态c
        #hidden和c形状保持一致
        c=hidden=torch.zeros(self.num_layers,1,self.hidden_size)
        return hidden,c

class GRU(nn.Module):
    def __init__(self,input_size,hidden_size,output_size,num_layers=1):
        """

        :param input_size: 输入张量最后一个维度
        :param hidden_size: 隐藏层最后一个维度
        :param output_size: 指定的线性层输出维度
        :param num_layers: GRU网络层数
        """
        super(GRU,self).__init__()
        self.hidden_size = hidden_size
        self.output_size=output_size
        self.num_layers=num_layers

       #实例化GRU对象
        self.gru = nn.GRU(input_size,hidden_size,num_layers)
        #实例化线性层的对象
        self.linear =nn.Linear(hidden_size,output_size)
        #定义softmax对象,作用是从输出张量中得到类别分类
        self.softmax=nn.LogSoftmax(dim=-1)

    def forward(self,input1,hidden):
        input1 = input1.unsqueeze(0)
        rr,hn = self.gru(input1,hidden)
        return self.softmax(self.linear(rr)),hn

    def initHidden(self):
        return torch.zeros(self.num_layers,1,self.hidden_size)
#参数
input_size = n_letters
n_hidden =128
output_size = n_categories
input1 = lineToTensor('B').squeeze(0)
hidden=c=torch.zeros(1,1,n_hidden)

rnn=RNN(input_size,n_hidden,output_size)
lstm =LSTM(input_size,n_hidden,output_size)
gru = GRU(input_size,n_hidden,output_size)

rnn_output,next_hidden = rnn(input1,hidden)
print('rnn:',rnn_output)
print('rnn_shape:',rnn_output.shape)
print('*********')

lstm_output,next_hidden1,c = lstm(input1,hidden,c)
print('lstm:',lstm_output)
print('lstm_shape:',lstm_output.shape)
print('*********')

gru_output,next_hidden2 = gru(input1,hidden)
print('gru:',gru_output)
print('gru_shape:',gru_output.shape)

rnn: tensor([[[-3.0732, -2.8641, -2.9219, -2.8854, -2.8782, -2.9283, -2.8300,
-2.9144, -3.0047, -2.9106, -2.8039, -2.9344, -2.7619, -2.8207,
-2.9091, -2.9073, -2.9314, -2.7948]]], grad_fn=)
rnn_shape: torch.Size([1, 1, 18])


lstm: tensor([[[-2.8951, -2.9179, -2.8396, -2.8640, -2.8961, -2.8581, -2.8517,
-2.9630, -2.9880, -2.9863, -2.8035, -2.8468, -2.9108, -2.9249,
-2.8169, -2.8440, -2.8695, -2.9789]]], grad_fn=)
lstm_shape: torch.Size([1, 1, 18])


gru: tensor([[[-2.9311, -2.8722, -2.8480, -2.8923, -2.9130, -2.7590, -2.8493,
-2.7777, -2.8802, -2.8320, -2.8620, -2.9206, -2.9406, -2.8921,
-3.0007, -2.9350, -3.0032, -2.9554]]], grad_fn=)
gru_shape: torch.Size([1, 1, 18])

5.4、构建训练函数并进行训练

准备训练 RNN 在训练前,我们把求所属语言类别的索引值方法封装成函数category_from_output。该函数输入: output ( RNN 网络输出的 output )。该函数输出:语言类别、语言类别索引值

#4.1从输出结果中获得指定类别函数
def categoryFromOutput(output):
    """
    需要调用took()函数,得到最大的值和索引,作为我们的类别信息
    :param output: 从输出结果中得到指定的类别
    :return:
    """
    top_n,top_i = output.topk(1)
    #从top_i中取出索引的值
    category_i=top_i[0].item()
    return all_categories[category_i],category_i

#torch.topk 排名前3的值
# x=torch.arange(1,6)
# print(x)
# res = torch.topk(x,3)
# print(res)

category,category_i = categoryFromOutput(gru_output)
print('category:',category)
print('category_i:',category_i)

category: French
category_i: 5

#4.2随机产生训练数据
def randomTrainingExample():
    #random.choice随机产生训练数据
    category = random.choice(all_categories)
    #通过category_lines字典取出category类别对应的名字列表
    line = random.choice(category_lines[category])
    #将类别封装成tensor
    category_tensor = torch.tensor([all_categories.index(category)],dtype=torch.long)
    #将随机渠道的名字通过函数lineTensor()转换成onehot张量
    line_tensor=lineToTensor(line)
    return category,line,category_tensor,line_tensor


for i in range(10):
    category,line,category_tensor,line_tensor = randomTrainingExample()
    print('category=',category,'/line=',line,'/category_tensor =',category_tensor)
print('line_tensor=',line_tensor)

# a=torch.randn(4)
# print(a)
# b=torch.randn(4,1)
# print(b)
# print('__________________')
# c=torch.add(a,b)
# print(c)
# d= torch.add(a,b,alpha=10)
# print(d)

line_tensor= tensor([[[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]],
[[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]]])

5.5、模型训练
#定义损失函数,nn.NLLLoss,因为和RNN最后一层的nn.LogSoftmax()逻辑匹配
criterion = nn.NLLLoss()

#设置学习率0.005
learning_rate = 0.005
def trainRNN(category_tensor,line_tensor):
    """
    :param category_tensor:训练数据的标签
    :param line_rensor:训练数据的特征
    :return:
    """
    hidden = rnn.initHidden()
    #关键一步:将模型结构中的梯度归零
    rnn.zero_grad()
    #循环遍历训练数据line_tensor中每一个字符,传入RNN中,并且迭代更新hidden
    for i in range(line_tensor.size()[0]):
        output,hidden=rnn(line_tensor[i],hidden)
    #因为RNN的输出是三维张量,为了满足category_tensor,需要进行降维操作
    loss = criterion(output.squeeze(0),category_tensor)
    #进行反向传播
    loss.backward()
    #显示的更新模型中所有参数
    for p in rnn.parameters():
        #将参数的张量表示与参数的梯度乘以学习率的结果相加以此来更新参数
        p.data.add_(-learning_rate,p.grad.data)
    return output,loss.item()

def trainLSTM(category_tensor,line_tensor):
    #初始化隐藏层张量以及初始化细胞状态
    hidden,c = lstm.initHiddenAndC()
    #将LSTM网络的梯度归零
    lstm.zero_grad()
    #便利所有的输入时间步x1
    for i in range(line_tensor.size()[0]):
        #注意LSTM每次输入包含3个张量
        output,hidden,c = lstm(line_tensor[i],hidden,c)
    #将预测张量和目标标签张量输入损失函数中
    loss = criterion(output.squeeze(0),category_tensor)
    #进行反向传播
    loss.backward()
    #进行参数的显示更新
    for p in lstm.parameters():
        p.data.add_(-learning_rate, p.grad.data)
    return output, loss.item()

def trainGRU(category_tensor,line_tensor):
    #注意GRU网络初始化的时候只需要初始化一个隐藏层的张量
    hidden = gru.initHidden()
    #首先将GRU网络的梯度进行清零
    gru.zero_grad()
    #遍历所有的输入时间步的xi
    for i in range(line_tensor.size()[0]):
        output,hidden = gru(line_tensor[1],hidden)
    #将预测的张量值和真实的张量标签传入损失函数中
    loss = criterion(output.squeeze(0),category_tensor)
    #进行反向传播
    loss.backward()
    #进行参数的显示更新
    for p in gru.parameters():
        p.data.add_(-learning_rate, p.grad.data)
    return output, loss.item()
#4.4 构建时间计算函数
def timeSince(since):
    #当前时间
    now = time.time()
    #计算时间差
    s = now - since
    #向下取整
    m = math.floor(s/60)
    #得到秒数
    s -= m*60
    #返回指定格式的耗时
    return '%dm %ds' % (m,s)

since = time.time() - 10*60

period = timeSince(since)
print(period)
#4.5构建人名类别列表与人名对应关系字典
category_lines ={}
#构建所有类别的列表
all_categories=[]

#遍历所有文件,利用glob.glob中可以利用正则表达式的便利
for filename in glob.glob(data_path + '*.txt'):
    #获取每个文件的文件名,起始就是得到名字的类别
    category = os.path.splitext(os.path.basename(filename))[0]
    #逐一将其装入所有类别的列表中
    all_categories.append(category)
    #读取每个文件的内容,形成名字的列表
    lines = readLines(filename)
    #按照对应的类别,将名字列表写入到category_lines字典中
    category_lines[category] = lines

n_categories = len(all_categories)
print("n_category:",n_categories)

print(category_lines['Italian'][:15])

#构建训练过程的日志打印函数

#设置训练的迭代次数
n_iters =100000
#设置结果的打印间隔
print_every =500
#设置绘制损失曲线上的制图间隔
plot_every =100

def train(train_type_fn):
    #train_type_fn 代表选择哪种模型来训练函数,比如选择trainRNN
    #初始化存储每个制图间隔损失的列表
    all_losses =[]
    #获取训练开始的时间
    start = time.time()
    #设置初始间隔的损失值等于0
    current_loss =0
    #迭代训练
    for iter in range(1,n_iters + 1):
        #通过randomTrainingExample 函数随机获取一组训练数据和标签
        category,line,category_tensor,line_tensor = randomTrainingExample()
        #将训练特征和标签张量传入训练函数中,进行模型的训练
        output,loss = train_type_fn(category_tensor,line_tensor)
        #累加损失值
        current_loss += loss
        #到了迭代次数的打印间隔
        if iter % print_every==0:
            #取该迭代步的output通过函数categoryFromOutput()获取对应的类别和索引
            guess,guess_i = categoryFromOutput(output)
            #correct ='True' if guess == category else 'False(%s)' %category
            correct = '✓' if guess == category else '✗ (%s)' % category
            #打印若干信息
            print('%d %d%% (%s) %.4f %s /%s %s' % (iter, iter / n_iters*100, timeSince(start),loss,line, guess, correct))

        #如果到了迭代次数的制图间隔
        if iter % plot_every ==0:
            all_losses.append(current_loss/plot_every)
            current_loss=0
    #返回对应的总损失列表,并返回训练的耗时
    return all_losses,int(time.time()-start)

# 开始训练传统RNN、LSTM、GRU模型并制作对比图
all_losses1, period1 = train(trainRNN)
all_losses2, period2 = train(trainLSTM)
all_losses3, period3 = train(trainGRU)

# import matplotlib.pyplot as plt
# plt.figure()
# plt.plot(all_losses3)


#绘制损失对比曲线,训练耗时对比柱状图
plt.figure(0)
plt.plot(all_losses1,label="RNN")
plt.plot(all_losses2,color="red", label="LSTM")
plt.plot(all_losses3,color="orange",label="GRU")
plt.legend(loc="upper left")

#创建台布
plt.figure(1)
x_data=["RNN","LSTM","GRU"]
y_data=[period1,period2,period3]
plt.bar(range(len(x_data)),y_data,tick_label=x_data)

96000 96% (4m 48s) 2.3996 Doan /Korean ✗ (Vietnamese)
96500 96% (4m 49s) 2.0551 Wawrzaszek /Russian ✗ (Polish)
97000 97% (4m 50s) 2.1911 Grosz /Scottish ✗ (German)
97500 97% (4m 52s) 2.7591 Dioletis /Polish ✗ (Greek)
98000 98% (4m 53s) 2.5264 Soler /Portuguese ✗ (Spanish)
98500 98% (4m 55s) 3.2327 Woodford /Greek ✗ (English)
99000 99% (4m 56s) 2.7493 Kool /Korean ✗ (Dutch)
99500 99% (4m 58s) 2.3158 Slezak /English ✗ (Czech)
100000 100% (4m 59s) 2.9676 Hajicek /Spanish ✗ (Czech)

5.6、构建评估函数并进行预测
#构建传统RNN评估模型
def evaluateRNN(line_tensor):
    """评估函数,仅有一个参数,line_tensor代表名字的张量标识
    初始化一个隐藏层的张量"""
    hidden=rnn.initHidden()
    #将评估数据line_tensor中的每一个字符诸葛传入RNN
    for i in range(line_tensor.size()[0]):
        output,hidden = rnn(line_tensor[i],hidden)
    #获得输出结果
    return output.squeeze(0)

#构建LSTM评估函数
def evaluateLSTM(line_tensor):
    #评估函数,针对LSTM模型,仅有一个参数,line_tensor代表名字的张量表示
    #初始化隐藏张量和细胞状态张量
    hidden,c = lstm.initHiddenAndC()
    #将评估数据line_tensor中的每一个字符传日LSTM中
    for i in range(line_tensor.size()[0]):
        output,hidden,c = lstm(line_tensor[i],hidden,c)
    #返回整个LSTM的输出OUTPUT,同时输出结果
    return output.squeeze(0)

#构建GRU评估函数
def evaluateGRU(line_tensor):
    #评估函数,正对GRU模型,仅有一个参数,line_tensor代表名字的张量表示
    hidden=gru.initHidden()
    #将评估数据line_tensor的每个字符逐个传入gru中
    for i in range(line_tensor.size()[0]):
        output,hidden = gru(line_tensor[i],hidden)
    #返回整个GRU的输出output,同时完成降维的操作
    return output.squeeze(0)

#输入参数
line='Bai'
line_tensor = lineToTensor(line)

#调用
rnn_output=evaluateRNN(line_tensor)
lstm_output=evaluateLSTM(line_tensor)
gru_output=evaluateGRU(line_tensor)
print("rnn_output:",rnn_output)
print("lstm_output:",lstm_output)
print("gru_output:",gru_output)

rnn_output: tensor([[ -1.0590, -0.9345, -8.5922, -9.3960, -9.2901, -11.0836, -10.0137,
-8.7807, -12.1847, -6.2418, -3.0617, -4.8388, -7.7011, -10.0194,
-9.7471, -10.4256, -7.1433, -1.6005]], grad_fn=)
lstm_output: tensor([[ -4.3500, -0.6932, -8.3036, -9.0551, -8.7640, -7.8228, -8.2456,
-10.0315, -7.9138, -5.6639, -3.9022, -1.5099, -7.2387, -8.0729,
-8.5073, -8.0819, -7.6443, -1.4316]], grad_fn=)
gru_output: tensor([[-5.3601, -1.2050, -4.9560, -4.8309, -4.7093, -5.6521, -4.2407, -6.0482,
-4.0262, -4.9310, -4.4449, -1.1360, -5.0372, -5.5387, -5.4965, -4.2032,
-4.8689, -1.3616]], grad_fn=)

#构建预测函数
def predict(input_line,evaluate_fn,n_predictions=3):
    """预测函数,输入参数input_line代表输入的字符串名字
        evaluate_fn 代表模型函数 RNN LSTM GRU
        n_predictions代表需要取最有可能的top个"""
    print('\n> %s' % input_line)

    #注意:所有预测模型都不能改变模型参数
    with torch.no_grad():
        #适用输入的人名转化成张量,然后调用评估模型函数得到预测的结果
        output = evaluate_fn(lineToTensor(input_line))
        #从预测的结果中取出top3个最大值及其索引
        topv,topi = output.topk(n_predictions,1,True)
        #初始化列表
        predictions =[]
        #遍历3个最可能的结果
        for i in range(n_predictions):
            #首先从topv中取出概率值
            value = topv[0][i].item()
            #然后从topi中取出索引值
            category_index = topi[0][i].item()
            #打印概率值及其对应的吱声国家名称
            print('(%.2f) %s' % (value,all_categories[category_index]))
            #将结果封装成列表格式,添加到最终的结果列表中
            predictions.append([value,all_categories[category_index]])
        return predictions

for evaluate_fn in [evaluateRNN,evaluateLSTM,evaluateGRU]:
    print("-"*18)
    predict('Dovesky',evaluate_fn)
    predict('Jackson', evaluate_fn)
    predict('Satoshi', evaluate_fn)

Dovesky
(-0.71) Czech
(-0.73) Russian
(-4.60) Polish
Jackson
(-0.23) Scottish
(-1.86) English
(-3.58) Czech
Satoshi
(-0.47) Arabic
(-1.21) Japanese
(-3.22) Russian


Dovesky
(-0.65) Russian
(-1.61) Czech
(-1.99) Polish
Jackson
(-1.20) Scottish
(-1.43) Russian
(-2.29) English
Satoshi
(-0.54) Japanese
(-1.65) Arabic
(-2.23) Polish


Dovesky
(-2.01) Russian
(-2.15) Japanese
(-2.32) Greek
Jackson
(-2.00) Dutch
(-2.15) Japanese
(-2.36) English
Satoshi
(-2.17) Japanese
(-2.26) English
(-2.38) Scottish

;