目录
- 项目综述
- 1.导入必备的工具包
- 2.处理数据,满足训练要求
- 2.1 统计常用的字符
- 2.2 进行规范化处理,去除重音符号
- 2.3 将文件读取到内存中
- 2.4 构建人名国家和具体人名的对应关系
- 2.5 one-hot编码
- 3.构建RNN模型
- 3.1 构建传统RNN模型
- 3.2 构建传统LSTM模型
- 3.3 构建传统GRU模型
- 3.4 测试输出
- 4.构建训练函数并进行训练
- 4.1辅助函数
- 4.1.1 返回指定的类别函数
- 4.1.2 随机产生训练数据函数
- 4.1.3 耗时计算函数
- 4.2构建传统RNN训练函数
- 4.3LSTM训练函数
- 4.4GRU训练函数
- 4.5 训练过程的日志打印函数
- 5.构建评估函数并进行验证
- 5.1 传统RNN评估函数
- 5.2 LSTM评估函数
- 5.3 GRU评估函数
- 5.4 预测函数
项目综述
- 该任务并没有分训练集和验证集,数据集为随机产生的,这样的优点是即使所给名字不在数据集里也可将其转化成模型可接受的张量;
- 分训练集和验证集要考虑两者数据的平衡,对于不在数据集里无法将其转化成模型可接受的张量。
1.导入必备的工具包
#从io中导入文件打开方式
from io import open
#帮助使用正则化表达式进行子目录的查询
import glob
import os
#用于获得常见字母及字符规范化
import string
import unicodedata
#导入随机工具
import random
#导入时间和数学工具包
import time
import math
#导入torch工具,构建模型
import torch
import torch.nn as nn
#引入制图工具包
import matplotlib.pyplot as plt
导入工具包:
Win+R输入cmd进入到CMD窗口下;
执行:pip install xxx -i https://pypi.tuna.tsinghua.edu.cn/simple
其中xxx为所要安装的工具包
2.处理数据,满足训练要求
下载数据集:数据集地址
注意:
数据集要将最后一个空白去除,即
不然会将其当作一个数据(之后被选中时会报错)
2.1 统计常用的字符
#获取所有常用字符(字母和标点)
#string.ascii_letters表示26个小写字母和26个小大写
all_letters=string.ascii_letters+" .,;'"
#表示52个字母(先小写后大写)再加上空格、句号、逗号、分号、单引号n_letters=len(all_letters) #57
2.2 进行规范化处理,去除重音符号
def unicodeToAscii(s):return ''.join(c for c in unicodedata.normalize('NFD',s)#以下是判断是否去除成功且在all_letters中if unicodedata.category(c)!='Mn' #'Mn'表示有标记字符and c in all_letters)
2.3 将文件读取到内存中
data_path="./data/names/"
def readLines(filename):#打开指定文件并读取所有内容,使用strip()去除两侧空白符,然后以'\n'进行切分lines=open(filename,encoding='utf-8').read().strip().split('\n')return [unicodeToAscii(line) for line in lines]#形成名字列表#测试:
filename=data_path+"Portuguese.txt"
results=readLines(filename)
print(results[:20])
2.4 构建人名国家和具体人名的对应关系
#构建一个人名类别与具体人名对应的字典
category_lines={} #即形如{“English”:["Lily",...],"Chinese":["Zhang",...],...}
#构建所有类别的列表
all_categories=[] #所有国家名#遍历所有文件,使用glob.glob中可以利用正则表达式的便利
for filename in glob.glob(data_path+"*.txt"):#获取每个文件的文件名:os.path.basename(filename)获取:国家.txt,如Chinese.txt#os.path.splitext()将其分为Chinese和.txt两部分#[0]表示去Chinese(第一个)category=os.path.splitext(os.path.basename(filename))[0]#将获取的每个国家名放入所有类别的列表中all_categories.append(category)#读取每个文件的内容,形成名字的列表lines=readLines(filename)#按照对应类别,将名字列表写入category_lines字典中#最终形成键值:国家名,value值:名字列表category_lines[category]=lines#测试:
n_categories=len(all_categories)#表示有18个国家的人
print(n_categories)
print(category_lines['Italian'][:10])#打印出Italian中的前10个人名
2.5 one-hot编码
什么是one-hot编码
枚举enumerate函数
#使得每一个人名都有对应的张量,该张量维度为x*1*y
#x层:人名所含字母个数;即每一个字母都用1*y的张量表示
# 1行;
# y列:所有可能用到的字符个数
def lineToTensor(line):#初始化一个全零张量,维度为(len(line),1,n_letters)tensor=torch.zeros(len(line),1,n_letters)#遍历每个人名中的每一个字符,搜索其对应的索引值,将该索引值所对应的全零张量中某一位赋值为1#比如:abc:第一层中第一个为1,其余全为0;第二层中第二个元素为1;第三层中第三个元素为1for k,letter in enumerate(line):tensor[k][0][all_letters.find(letter)]=1return tensor#测试:
line="abc"
tensor=lineToTensor(line)
print(tensor)
3.构建RNN模型
压缩只能在维度为1是才可压缩,若所在维度不为1则不可压缩。(扩充同上)
torch.unsqueeze函数
squeeze、unsqueeze函数
x=torch.tensor([0,1,2,3]) #一维
y1=torch.unsqueeze(x,0) #形状1*4
y2=torch.unsqueeze(x,1) #形状4*1
print(y1)
print(y1.shape)
print(y2)
print(y2.shape)
3.1 构建传统RNN模型
#构建传统RNN模型
class RNN(nn.Module):def __init__(self,input_size,hidden_size,output_size,num_layers=1):#input_size:输入张量维度、hidden_size:隐藏层维度、output_size:输出维度super(RNN,self).__init__() # 调用父类(nn.Module)的__init__self.input_size=input_size #代表RNN输入的最后一个维度self.hidden_size=hidden_size #代表RNN隐藏层的最后一个维度self.output_size=output_size #代表RNN网络最后线性层的输出维度self.num_layers=num_layers #代表RNN网络层数#实例化nn.RNNself.rnn=nn.RNN(input_size,hidden_size,num_layers)#nn.Linear和nn.LogSoftmax用于将RNN输出经线性化和softmax(激活函数),真正输出(即forward函数的返回)#实例化nn.Linear(全连接层),用于将nn.RNN的输出维度转化成指定输出维度self.linear=nn.Linear(hidden_size,output_size)#实例化Softmax层,用于从输出层获得类别结果self.softmax=nn.LogSoftmax(dim=-1)#dim=-1表示在最后一个维度进行(例如1*1*32即在32处进行)#传统RNN计算逻辑#forward正向传播def forward(self,input,hidden):#input:输入张量(形状1*n_letters),# hidden:隐层张量(self.num_layers*1*self.hidden_size)-由initHidden函数生成#nn.RNN要求输入为三维,input扩展一个维度input=input.unsqueeze(0)#形状:1*1*n_letters#传入RNN,若num_layers=1,则rr(输出)恒等于hn(隐层)rr,hn=self.rnn(input,hidden)#返回通过线性变换和softmax的结果,同时返回隐藏层输出(作为后续RNN输入)return self.softmax(self.linear(rr)),hn #实现真正输出#初始化一个全零隐层(即h0,三维)def initHidden(self):#self.num_layers:层数(*方向),1:批次,self.hidden_size:隐藏层维度return torch.zeros(self.num_layers,1,self.hidden_size)
Softmax层与LogSoftmax层的对比1
Softmax层与LogSoftmax层的对比2
3.2 构建传统LSTM模型
#构建LSTM模型
class LSTM(nn.Module):def __init__(self,input_size,hidden_size,output_size,num_layers=1):#input_size:输入张量维度、hidden_size:隐藏层维度、output_size:输出维度super(LSTM,self).__init__()self.input_size=input_size #代表LSTM输入的最后一个维度self.hidden_size=hidden_size #代表LSTM隐藏层的最后一个维度self.output_size=output_size #代表LSTM网络最后线性层的输出维度self.num_layers=num_layers #代表LSTM网络层数#实例化nn.LSTMself.lstm=nn.LSTM(input_size,hidden_size,num_layers)#nn.Linear和nn.LogSoftmax用于将RNN输出经线性化和softmax(激活函数)#实例化nn.Linear,用于将nn.RNN的输出维度转化成指定输出维度self.linear=nn.Linear(hidden_size,output_size)#实例化Softmax层,用于从输出层获得类别结果self.softmax=nn.LogSoftmax(dim=-1)#dim=-1表示在最后一个维度进行(例如1*1*32即在32处进行)#LSTM计算逻辑def forward(self,input,hidden,cell):#cell:细胞状态张量#input:输入张量(形状1*n_letters),hidden:隐层张量(self.num_layers*1*self.hidden)input=input.unsqueeze(0)#形状:1*1*n_letters#传入RNN,若num_layers=1,则rr(输出)恒等于hn(隐层)rr,(hn,cn)=self.lstm(input,(hidden,cell))#返回通过线性变换和softmax的结果,同时返回隐藏层输出(作为后续RNN输入)return self.softmax(self.linear(rr)),hn,cn #实现真正输出#初始化一个全零隐层和细胞状态(即h0和c0,三维)def initHiddenAndCell(self):#self.num_layers:层数(*方向),1:批次,self.hidden_size:隐藏层维度h0=c0=torch.zeros(self.num_layers, 1, self.hidden_size)return h0,c0
3.3 构建传统GRU模型
class GRU(nn.Module):def __init__(self,input_size,hidden_size,output_size,num_layers=1):#input_size:输入张量维度、hidden_size:隐藏层维度、output_size:输出维度super(GRU,self).__init__()self.input_size=input_size #代表GRU输入的最后一个维度self.hidden_size=hidden_size #代表GRU隐藏层的最后一个维度self.output_size=output_size #代表GRU网络最后线性层的输出维度self.num_layers=num_layers #代表GRU网络层数self.gru=nn.GRU(input_size,hidden_size,num_layers)#nn.Linear和nn.LogSoftmax用于将GRU输出经线性化和softmax(激活函数),真正输出(即forward函数的返回)#实例化nn.Linear(全连接层),用于将nn.RNN的输出维度转化成指定输出维度self.linear=nn.Linear(hidden_size,output_size)#实例化Softmax层,用于从输出层获得类别结果self.softmax=nn.LogSoftmax(dim=-1)#dim=-1表示在最后一个维度进行(例如1*1*32即在32处进行)#传统GRU计算逻辑def forward(self,input,hidden):#input:输入张量(形状1*n_letters),hidden:隐层张量(self.num_layers*1*self.hidden)#nn.RNN要求输入为三维,input扩展一个维度input=input.unsqueeze(0)#形状:1*1*n_letters#传入RNN,若num_layers=1,则rr(输出)恒等于hn(隐层)rr,hn=self.gru(input,hidden)#返回通过线性变换和softmax的结果,同时返回隐藏层输出(作为后续RNN输入)return self.softmax(self.linear(rr)),hn #实现真正输出#初始化一个全零隐层(即h0,三维)def initHidden(self):#self.num_layers:层数(*方向),1:批次,self.hidden_size:隐藏层维度return torch.zeros(self.num_layers,1,self.hidden_size)
3.4 测试输出
#输入张量为每个人名对应的张量,其最后一维都是n_letters(57)
input_size=n_letters
#定义隐层最后一维尺寸大小(神经元个数)
hidden_size=128
#输出尺寸为国家类别种数(18)
output_size=n_categories
#num_layers= #默认为1,可需改#假设以一个字母B作为输入
#lineToTensor('B')输出为三维张量;squeeze(0)降低一个维度(该维度需为1,才可降低)(定义的三类模型输入为二维,每一个类内都会进行扩充维度)
input1=lineToTensor('B').squeeze(0)
#初始化一个三维隐藏层和一个细胞状态张量
hidden=cell=torch.zeros(1,1,hidden_size)##调用三类模型
rnn=RNN(input_size,hidden_size,output_size)
lstm=LSTM(input_size,hidden_size,output_size)
gru=GRU(input_size,hidden_size,output_size)rnn_output,next_hidden=rnn(input1,hidden)
print("rnn:",rnn_output)
print("rnn_shape:",rnn_output.shape)
print('*******************')lstm_output,next_hidden1,cell=lstm(input1,hidden,cell)
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)
这里只是对一个字母进行测试,如果是n个字母,则需要修改参数
4.构建训练函数并进行训练
torch.topkh函数
x=torch.arange(1,6)#x为1-5的列表
res=torch.topk(x,3)#输出x中最大的三个数及其对应索引
print(x)
print(res)#索引从0开始
torch.add()函数
x=torch.randn(4)#随机生成符合正态分布的4个数
y=torch.randn(3,1)#随机生成符合正态分布的3*1的向量
print(x)
print(y)
a=torch.add(x,y)
b=torch.add(x,y,alpha=10)
print(a)
print(b)
torch.add(x,y):即x和y张量相加,x的每一个值与y的每一个值相加(a的第一行为x的每一个值分别于y的第一个值相加…)
torch.add(x,y,alpha=10):y先乘10再进行torch.add(x,y),即相当于torch.add(x,10y)
4.1辅助函数
4.1.1 返回指定的类别函数
#从输出结果中得到所指定的类别
def categoryFromOutput(output):top_n,top_i=output.topk(1)#获取output中的值和所对应的索引category_i=top_i[0].item()#从top_i获取索引值#返回索引值对应国家,和索引值return all_categories[category_i],category_i
测试:
category,category_i=categoryFromOutput(gru_output)#取GRU模型输出
print('category:',category)
print('category_i:',category_i)
即,对于GRU模型输出结果,取最大值后,判断‘B’(输入)为国家类别中第13个国家Portuguese中的名字(准确率低)
4.1.2 随机产生训练数据函数
def randomTrainingExample():#在所有国家类别中随机选择一个国家,并在该国家里随机选择一个名字category=random.choice(all_categories)line=random.choice(category_lines[category])#将选择的类别和人名封装成张量#all_categories.index(category)返回category在all_categories列表中的索引值#torch.long长整型张量category_tensor=torch.tensor([all_categories.index(category)],dtype=torch.long)line_tensor=lineToTensor(line)#人名转化成onehot张量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)
即随机生成10个国家、10个国家里的名字,并将其转换成张量(对于国家名张量category_tensor,其为一维,方括号内为其在国家名列表中的位置;人名张量见one-hot编码部分)
4.1.3 耗时计算函数
#计算模型所耗时间
def timeSince(since):#since为开始时间,返回为耗费时间now=time.time()#获取当前时间s=now-since#获取耗费时间(单位:秒)m=math.floor(s/60)#获取分钟数s=s-m*60#获取剩余秒数#输出:*m *sreturn '%dm %ds' % (m,s)#测试
since=time.time()-10*60
period=timeSince(since)
print(period)
4.2构建传统RNN训练函数
详解torch.NLLLoss
#构建传统RNN训练函数
#定义损失函数:nn.NLLLoss()函数,因为其和最后使用的nn.LogSoftmax()函数内部计算逻辑匹配
criterion=nn.NLLLoss()#学习率(经验值)
learning_rate=0.005
#训练函数
def trainRNN(category_tensor,line_tensor):#category_tensor:代表训练数据的标签#line_tensor:代表训练数据的特征hidden=rnn.initHidden()#初始化RNN隐藏层张量#将模型结构中的梯度归零(关键的一步)rnn.zero_grad()#遍历训练数据line_tensor中的每一个字符,传入RNN中,并迭代更新hiddenfor i in range(line_tensor.size()[0]):#line_tensor.size():line_tensor的尺寸(名字字母个数*1*57);#line_tensor.size()[0]:取出名字字母个数#line_tensor[i]表示名字中第i个字母对应的张量#hidden迭代更新;output输出最后一个时间步的输出(即最后一层的输出)output,hidden=rnn(line_tensor[i],hidden)#RNN输出output是三维张量,为满足category_tensor,需要进行降维操作# #output.squeeze(0)即为了将output变为1*18(即1*n的形式),对应标签category_tensor的一维(详情见详解torch.NLLLoss)loss=criterion(output.squeeze(0),category_tensor)#进行反向传播loss.backward()#计算出每一步的梯度值#显示更新模型中的所有参数(手动更新)for p in rnn.parameters():#将参数的张量标识与参数的梯度进行乘法运算#p.data表示p的数值;p.data.add_()表示用计算出的结果覆盖p的值#(-learning_rate,p.grad.data)即p=p-learning_rate*梯度#p.data.add_(-learning_rate,p.grad.data)#该种形式系统格式报错p.data.add_(p.grad.data, alpha=-learning_rate)#返回RNN最终的输出结果output,和模型损失lossreturn output,loss.item()
4.3LSTM训练函数
#构建LSTM训练函数(多了细胞状态)
def trainLSTM(category_tensor,line_tensor):#category_tensor:代表训练数据的标签#line_tensor:代表训练数据的特征hidden,cell=lstm.initHiddenAndCell()#将模型结构中的梯度归零(关键的一步)lstm.zero_grad()#遍历训练数据line_tensor中的每一个字符,传入并迭代更新hidden与cellfor i in range(line_tensor.size()[0]):#line_tensor.size():line_tensor的尺寸(名字字母个数*1*57);#line_tensor.size()[0]:取出名字字母个数#line_tensor[i]表示名字中第i个字母对应的张量#hidden迭代更新;output输出最后一个时间步的输出(即最后一层的输出)output,hidden,cell=lstm(line_tensor[i],(hidden,cell))#RNN输出output是三维张量,为满足category_tensor,需要进行降维操作#output.squeeze(0)即为了将output变为1*18(即1*n的形式),对应标签category_tensor的一维loss=criterion(output.squeeze(0),category_tensor)#进行反向传播loss.backward()#计算出每一步的梯度值#显示更新模型中的所有参数for p in lstm.parameters():#将参数的张量标识与参数的梯度进行乘法运算#p.data表示p的数值;p.data.add_()表示用计算出的结果覆盖p的值#(-learning_rate,p.grad.data)即p-learning_rate*梯度#p.data.add_(-learning_rate,p.grad.data)p.data.add_(p.grad.data, alpha=-learning_rate)#返回RNN最终的输出结果output,和模型损失lossreturn output,loss.item()
4.4GRU训练函数
#构建GRU训练函数
def trainGRU(category_tensor,line_tensor):#category_tensor:代表训练数据的标签#line_tensor:代表训练数据的特征hidden=gru.initHidden()#将模型结构中的梯度归零(关键的一步)gru.zero_grad()#遍历训练数据line_tensor中的每一个字符,传入并迭代更新hiddenfor i in range(line_tensor.size()[0]):#line_tensor.size():line_tensor的尺寸(名字字母个数*1*57);#line_tensor.size()[0]:取出名字字母个数#line_tensor[i]表示名字中第i个字母对应的张量#hidden迭代更新;output输出最后一个时间步的输出(即最后一层的输出)output,hidden=gru(line_tensor[i],hidden)#RNN输出output是三维张量,为满足category_tensor,需要进行降维操作#output.squeeze(0)即为了将output变为1*18(即1*n的形式),对应标签category_tensor的一维loss=criterion(output.squeeze(0),category_tensor)#进行反向传播loss.backward()#计算出每一步的梯度值#显示更新模型中的所有参数for p in gru.parameters():#将参数的张量标识与参数的梯度进行乘法运算#p.data表示p的数值;p.data.add_()表示用计算出的结果覆盖p的值#(-learning_rate,p.grad.data)即p-learning_rate*梯度#p.data.add_(-learning_rate,p.grad.data)p.data.add_(p.grad.data, alpha=-learning_rate)#返回最终的输出结果output,和模型损失lossreturn output,loss.item()
4.5 训练过程的日志打印函数
#构建训练过程的日志打印函数
n_iters=10000#训练的迭代次数
print_every=50#结果打印间隔(防止将代码卡死当作训练中)
plot_every=10#绘制损失曲线的制图间隔def train(train_type_fn):# train_type_fn代表三种训练模型(即trainRNN、trainLSTM、trainGRU)all_losses=[]#初始化存储每个制图间隔损失的列表start=time.time()#获取开始时间戳current_loss=0#设置初始间隔的损失值为0# 迭代训练n_iters次for iter in range(1,n_iters+1):#随机产生一组训练数据和标签category, line, category_tensor, line_tensor=randomTrainingExample()#开始训练output,loss=train_type_fn(category_tensor, line_tensor)#累加损失值current_loss+=loss#若达到打印间隔if iter % print_every==0:#从该迭代步输出获取类别和索引值guess,guess_i=categoryFromOutput(output)#判断:与真实类别标签比较;若相同则为True,否则为False并打印出类别correct='True' if guess==category else 'False (%s)'% category#打印信息:iter(当前迭代次数),iter/n_iters*100(当前迭代次数百分比,%d%%表示*%,后两个%一个为转义字符,表示%),timeSince(start)(当前训练所耗费时间),# loss(每一个打印间隔的损失值),line(真实类别),guess(猜测类别),correct(正确与否)print('%d %d%% (%s) %.4f %s / %s %s' % (iter,iter/n_iters*100,timeSince(start),loss,line,guess,correct))#print(iter)#若达到制图间隔if iter % plot_every==0:#将过去plot_every次迭代的平均损失添加到all_lossesall_losses.append(current_loss/plot_every)#间隔损失重置为0(使得all_losses均为每plot_every轮的平均值)current_loss=0#返回总损失列表和训练耗时return all_losses,int(time.time()-start)
测试:
#调用train函数,分别传入RNN、LSTM、GRU训练函数
#获取各自的损失列表和训练时间
all_losses1,period1=train(trainRNN)
all_losses2,period2=train(trainLSTM)
all_losses3,period3=train(trainGRU)# #绘制损失对比曲线
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)
plt.show()
如果报错见数据集处理
一些输出:
以下是训练次数10万的图结果:
- 损失函数对比曲线分析:
模型训练的损失降低快慢代表模型收敛程度,由图可知,传统RNN的模型收敛情况量好,然后是GRU,最后是LSTM。这是因为:我们当前处理的文本数据是人名,他们的长度有限,且长距离字母间基本无特定关联,因此无法发挥改进模型LSTM和GRU的长距离捕捉语义关联的优势。所以在以后的模型选用时,要通过对任务的分析以及实验对比,选择最合适的模型。
- 训练耗时对比图分析:
模型训练的耗时长短代表模型的计算复杂度,由图可知,也正如我们之前的理论分析,传统RNN复杂度最低,耗时几乎只是后两者的一半,然后是GRU,最后是复杂度最高的LSTM。
结论: 模型选用一般应通过实验对比,并非越复杂或越先进的模型表现越好,而是需要结合自己的特定任务,从对数据的分析和实验结果中获得最佳答案.
5.构建评估函数并进行验证
5.1 传统RNN评估函数
def evaluateRNN(line_tensor):hidden=rnn.initHidden()for i in range(line_tensor.size()[0]):output,hidden=rnn(line_tensor[i],hidden)return output.squeeze(0)#降维(二维)
5.2 LSTM评估函数
def evaluateLSTM(line_tensor):hidden,cell=lstm.initHiddenAndCell()for i in range(line_tensor.size()[0]):output,hidden,cell=lstm(line_tensor[i],hidden,cell)return output.squeeze(0)
5.3 GRU评估函数
def evaluateGRU(line_tensor):hidden=gru.initHidden()for i in range(line_tensor.size()[0]):output,hidden=gru(line_tensor[i],hidden)return output.squeeze(0)
5.4 预测函数
#构建预测函数
def predict(input_line,evaluate,n_predictions=3):#input_line表示输入的人名;n_predictions表示取出前top个#打印输入名print('\n> %s' % input_line)#表示以下操作相关张量不进行梯度更新(梯度更新在训练阶段进行)with torch.no_grad():#人名转换张量并预测output=evaluate(lineToTensor(input_line))#获取前top个的值和索引topv,topi=output.topk(n_predictions,1,True)#用于存储预测结果predictions=[]for i in range(n_predictions):#将第i个的值取出value=topv[0][i].item()#将第i个的索引值取出category_index=topi[0][i].item()print('(%.2f) %s' % (value,all_categories[category_index]))#将结果存放到predications中predictions.append([value,all_categories[category_index]])
测试:
for evaluate_fn in [evaluateRNN,evaluateLSTM,evaluateGRU]:print('*'*10)predict('Lipengyang',evaluate_fn)
参考资料