pytorch实践:名字分类的RNN模型


前言

任务是这样的,我们需要设计一个模型,针对不同的名字,判断他到底是哪个国家。
比如:
bob 英国的
bingbing Wang 中国的

一、先上代码

#根据名字分类国家
import numpy as np
import torch
from torch.utils.data import DataLoader,Dataset
import matplotlib.pyplot as plt
import gzip
import csv
import time

class NameDataset(Dataset):
    def __init__(self,is_train_set=True):
        filename='names_train.csv.gz'if is_train_set else'names_test.csv.gz'
        #多种不同的方式读取数据,gzip格式如下
        with gzip.open(filename,'rt') as f:
            reader=csv.reader(f)
            rows=list(reader)#name,lauguage对
        self.names=[row[0] for row in rows]
        self.len=len(self.names)
        self.countries=[row[1] for row in rows]
        #set 集合,去除重复元素 sort排序 list变成列表
        self.country_list=list(sorted(set(self.countries)))
        #country变成一个字典
        self.country_dict=self.getCountryDict()

        self.country_num=len(self.country_list)#索引总数

    #save countries and its index in list and dictionary
    def __getitem__(self, index):
        #名字string country索引
        return self.names[index],self.country_dict[self.countries[index]]

    def __len__(self):
        return self.len

    #convert list to dictionary
    def getCountryDict(self):
        country_dict=dict()#空字典
        for idx,country_name in enumerate(self.country_list,0):
            country_dict[country_name]=idx
        return country_dict

   #表示方便
    def idx2country(self,index):
        return self.country_list[index]

    def getCountriesNum(self):
        return self.country_num




class RNNclassifier(torch.nn.Module):
    def __init__(self,input_size,hidden_size,output_size,n_layers=1,bidirectional=True):
        super(RNNclassifier,self).__init__()
        #GRN layer 参数
        self.hidden_size=hidden_size
        self.n_layers=n_layers
        self.n_directions=2 if bidirectional else 1
        #(seqlen,batchsize)(seqlen,batchsize,hiddensize)
        #嵌入字典的大小,每个嵌入向量的大小
        self.embed=torch.nn.Embedding(input_size,hidden_size)#嵌入向量维度=隐藏层维度
        self.gru=torch.nn.GRU(hidden_size,hidden_size,n_layers,bidirectional=bidirectional)
        self.fc=torch.nn.Linear(hidden_size*self.n_directions,output_size)

    #创建全0隐藏
    def _init_hidden(self,batch_size):
        hidden=torch.zeros(self.n_layers*self.n_directions,batch_size,self.hidden_size)
        return create_tensor(hidden)

    def forward(self,input,seq_lengths):
       #input shape B X S ->S X B
        input=input.t()#embedding 需要的维度
        batch_size=input.size(1)

        hidden=self._init_hidden(batch_size)

        embedding=self.embed(input)

        #pack them up简化gru计算,输入的未emb的数据必须根据序列长度排序,把0去掉
        gru_input=torch.nn.utils.rnn.pack_padded_sequence(embedding,seq_lengths)

        output,hidden=self.gru(gru_input,hidden)
        if self.n_directions==2:
            hidden_cat=torch.cat([hidden[-1],hidden[-2]],dim=1)
        else:
            hidden_cat=torch.cat(hidden[-1])
        #最后的隐层输出hN
        fc_output=self.fc(hidden_cat)
        return fc_output

def name2list(name):
    arr= [ord(c) for c in name]
    return arr,len(arr)

# name to tensor
def make_tensors(names,countries):
    #name分字符ASCLL,返回arr,len(arr)
    sequences_and_lengths=[name2list(name) for name in names]
    #分开拿出来
    #ascll姓名列表
    name_sequences=[sl[0] for sl in sequences_and_lengths]
    #长度列表
    seq_lengths=torch.LongTensor([sl[1] for sl in sequences_and_lengths])
    #countries转化为long
    countries=countries.long()

    #make tensor of name B x S padding填充
    seq_tensor=torch.zeros(len(name_sequences),seq_lengths.max()).long()#bug1
    #转化为长整形long(),才可以被embed接受
    for idx,(seq,seq_len) in enumerate(zip(name_sequences,seq_lengths)):
        seq_tensor[idx,:seq_len]=torch.LongTensor(seq)

    #sort by length to use pack_padded_sequence排序
    seq_lengths,perm_idx=seq_lengths.sort(dim=0,descending=True)
    #排序的索引
    seq_tensor=seq_tensor[perm_idx]
    countries=countries[perm_idx]
    
    return create_tensor(seq_tensor),\
           create_tensor(seq_lengths),\
           create_tensor(countries)

def create_tensor(tensor):
    if USE_GPU:
        device=torch.device("cude:0")
        tensor=tensor.to(device)
    return tensor

def trainModel():
    total_loss=0
    for i,(names,countries) in enumerate(trainloader,1):
        inputs,seq_lengths,target=make_tensors(names,countries)

        outputs=classifier(inputs,seq_lengths)
        loss=criterion(outputs,target)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    total_loss+=loss.item()
    if i%10 ==0:
        print(f'[{time_since(start)}]Epoch{epoch}',end='')
        print(f'[{i*len(inputs)}/{len(trainset)}]',end='')
        print(f'loss={total_loss/i*len(inputs)}')
    return total_loss

def testModel():
    correct=0
    total=len(testset)
    print("evaluating trained model...")
    with torch.no_grad():
        for i,(names,countries) in enumerate(testloader,1):
            inputs,seq_len,target=make_tensors(names,countries)
            output=classifier(inputs,seq_len)
            pred=output.max(dim=1,keepdim=True)[1]
            correct+=pred.eq(target.view_as(pred)).sum().item()
        precent='%.2f'%(100*correct/total)
        print(f'test set:accuracy {correct}/{total} {precent}%')
    return correct/total

def time_since(since):
    s=time.time()-since
    m=math.floor(s/60)
    s-=m*60
    return '%dm %ds'%(m,s)



if __name__=='__main__':
    # parameter
    HIDDEN_SIZE = 100  # 隐藏层维度
    BATCH_SIZE = 256  # batch大小
    N_LAYER = 2  # gru维度
    N_EOPCHS = 30  # 循环次数
    N_CHARS = 128  # 字典大小,ASLL码表示
    USE_GPU = False  # 不用GPU

    trainset = NameDataset(is_train_set=True)
    trainloader = DataLoader(trainset, batch_size=BATCH_SIZE, shuffle=True)

    testset = NameDataset(is_train_set=False)
    testloader = DataLoader(testset, batch_size=BATCH_SIZE, shuffle=False)

    N_COUNTRY = trainset.getCountriesNum()  # 输出的维度

    classifier=RNNclassifier(N_CHARS,HIDDEN_SIZE,N_COUNTRY,N_LAYER)
    if USE_GPU:
        device = torch.device("cude:0")
        classifier.to(device)

    criterion = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(classifier.parameters(), lr=0.01)

    start=time.time()
    print("training for %d epochs..."%N_EOPCHS)
    acc_list=[]
    for epoch in range(1,N_EOPCHS+1):
        print('epoch',epoch)
        trainModel()
        acc=testModel()
        acc_list.append(acc)

    #画图
    epoch=np.arange(1,len(acc_list)+1,1)
    acc_list=np.array(acc_list)
    plt.plot(epoch,acc_list)
    plt.xlabel('epoch')
    plt.ylabel('accuracy')
    plt.grid()
    plt.show()


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值