项目综述
- 该任务并没有分训练集和验证集,数据集为随机产生的,这样的优点是即使所给名字不在数据集里也可将其转化成模型可接受的张量;
- 分训练集和验证集要考虑两者数据的平衡,对于不在数据集里无法将其转化成模型可接受的张量。
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编码
#使得每一个人名都有对应的张量,该张量维度为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;第三层中第三个元素为1
for k,letter in enumerate(line):
tensor[k][0][all_letters.find(letter)]=1
return 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 #