PyTorch深度学习实践代码 第十三讲

#引入torch
import torch
#引入time计时
import time
#引入math数学函数
import math
#引入numpy
import numpy as np
#引入plt
import matplotlib.pyplot as plt
#从torch的工具的数据引入数据集,数据加载器
from torch.utils.data import Dataset,DataLoader
#从torch的神经网络的数据的rnn中引入包装填充好的序列。作用是将填充的pad去掉,然后根据序列的长短进行排序
from torch.nn.utils.rnn import pack_padded_sequence
#引入gzip 压缩文件
import gzip
#引入csv模块
import csv

#隐层数是100
HIDDEN_SIZE = 100
#batch的大小时256
BATCH_SIZE = 256
#应用2层的GRU
N_LAYER = 2
#循环100
N_EPOCHS = 100
#字符数量时128
N_CHARS = 128
#不使用GPU
USE_GPU =False
#定义名字数据集的类,继承自数据集
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打开文件名,操作text文本的时候使用'rt',作为f
        with gzip.open(filename,'rt') as f:
            #阅读器是用csv的阅读器阅读文件
            reader = csv.reader(f)
            #将文件设成一个列表
            rows = list(reader)
        #自身名字是文件的第一列都是名字,提取第一列,对于r在rs中时
        self.names = [row[0] for row in rows]
        #长度是名字的长度
        self.len = len(self.names)
        #国家是第二列
        self.countries = [row[1] for row in rows]
        #将国家变成集合,去除重复的元素,然后进行排序,然后接着再变回列表
        self.country_list = list(sorted(set(self.countries)))
        #得到国家的词典,将列表转化成词典(有索引)
        self.country_dict = self.getCountryDict()
        #长度是国家的长度
        self.country_num = len(self.country_list)
    #定义 获得项目类,提供索引访问,自身,索引
    def __getitem__(self,index):
        #返回 带索引的名字,带索引的国家,代入,得到带国家的词典
        return self.names[index], self.country_dict[self.countries[index]]
    #定义长度
    def __len__(self):
        #返回长度
        return self.len
    #定义获得国家词典
    def getCountryDict(self):
        #现设一个空字典
        country_dict = dict()
         #idx表示进行多少次的迭代,country_name是国家名,用列举的方法将国家列表的数据提取出来,从0开始
        for idx,country_name in enumerate(self.country_list,0):
            #构造键值对,将国家名代入国家列表中等于1,2,3.
            country_dict[country_name] = idx
        #返回国家列表
        return country_dict
    #定义 索引返回国家字符串,自身索引
    def idx2country(self,index):
        #返回 自身,将索引代入国家列表得到字符串
        return self.country_list[index]
    #获得国家数量
    def getCountriesNum(self):
        #返回自身国家数量
        return self.country_num

#将训练集为真,代入名字数据集模型中得到训练集
trainset = NameDataset(is_train_set=True)
#将训练集,batch的大小等于batch的大小,shuffle为真将数据打乱。代入到数据加载器中。得到训练加载器
trainloader = DataLoader(trainset,batch_size=BATCH_SIZE,shuffle=True)
#将训练集为假,代入名字数据集模型中得到测试集
testset = NameDataset(is_train_set=False)
#将测试集,batch的大小等于batch的大小,shuffle为假不把数据打乱。代入到数据加载器中。得到测试加载器
testloader = DataLoader(testset,batch_size=BATCH_SIZE,shuffle=False)
#训练集的获得国家数量得到国家数量
N_COUNTRY = trainset.getCountriesNum()

#创建tensor    
def create_tensor(tensor):
    #如果使用GPU
    if USE_GPU:
        #使用第一个GPU代入到设置,得到设置
        device = torch.device("cuda:0")
        #让张量在设置里面跑
        tensor = tensor.to(device)
    #返回张量
    return tensor

#将RNN分类器分成一个类,继承自Module模块
class RNNClassifier(torch.nn.Module):
    #定义自身初始化,输入的大小,隐层的大小,输出的大小,层数是1,bidirectional为真设成双向的。
    def __init__(self,input_size,hidden_size,output_size,n_layers=1,bidirectional=True):
        #父类初始化
        super(RNNClassifier,self).__init__()
        #自身隐层等于隐层
        self.hidden_size = hidden_size
        #自身层数等于层数
        self.n_layers = n_layers
        #自身方向数量是如果bidirectional为真则是2,否则是1
        self.n_directions = 2 if bidirectional else 1
        #将输入的大小和隐层的大小代入嵌入层得到自身嵌入层
        self.embedding = torch.nn.Embedding(input_size,hidden_size)
        #隐层的大小是输入,隐层的大小是输出,层数,双向代入GRU模型中,得到gru
        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)
    #初始化h0,自身batch的大小    
    def _init_hidden(self,batch_size):
        #将层数×方向数,batch的大小,隐层的大小归零,得到h0
        hidden = torch.zeros(self.n_layers * self.n_directions,batch_size,self.hidden_size)
        #返回 创建张量的隐层
        return create_tensor(hidden)
    #定义前馈计算,自身,输入,序列的长度    
    def forward(self,input,seq_lengths):
        #将输入进行转置,B*S--S*B
        input = input.t()
        #输入的第二列是batch的大小
        batch_size = input.size(1)
        #将batch的大小代入到自身初始隐层中,得到隐层的大小
        hidden = self._init_hidden(batch_size)
        #将输入的大小代入到自身嵌入层得到嵌入层
        embedding = self.embedding(input)
        #将嵌入层和序列的长度代入pack_padded_sequence中,先将嵌入层多余的零去掉,然后排序,打包出来,得到GRU的输入。
        gru_input = pack_padded_sequence(embedding,seq_lengths)
        #将输入和隐层代入gru,得到输出和隐层
        output,hidden = self.gru(gru_input,hidden)
        #如果是双向的
        if self.n_directions == 2:
            #将隐层的最后一个和隐层的最后第二个拼接起来,按照维度为1的方向拼接起来。得到隐层
            hidden_cat = torch.cat([hidden[-1],hidden[-2]],dim=1)
        #否则
        else:
            #隐层就只有最后一个
            hidden_cat = hidden[-1]
        #将隐层代入激活函数得到输出
        fc_output = self.fc(hidden_cat)
        #返回输出
        return fc_output

#定义名字到列表
def name2list(name):
    #对于c在名字里,将c转变为ASC11值
    arr = [ord(c) for c in name]
    #返回arr和长度
    return arr, len(arr)


#定义制作张量 名字 国家
def make_tensors(names, countries):
    #将名字代入到模型中得到ASC11值,对于名字在名字中,得到序列和长度
    sequences_and_lengths = [name2list(name) for name in names]
    #将第一列取出来得到名字序列
    name_sequences = [sl[0] for sl in sequences_and_lengths]
    #将第二列转换成长tensor得到序列的长度
    seq_lengths = torch.LongTensor([sl[1] for sl in sequences_and_lengths])
    #将国家变为长整型数据
    countries = countries.long()
    #将名字序列的长度,序列长度的最大值的长整型归零。得到序列的张量
    seq_tensor = torch.zeros(len(name_sequences), seq_lengths.max()).long()
    #对于索引,序列和序列长度 在名字序列和名字长度中遍历,从零开始
    for idx, (seq, seq_len) in enumerate(zip(name_sequences, seq_lengths), 0):
        #将序列变成长张量,等于序列张量,idx是索引,第1,2,3.。。。,
        #:seq_len是按照从小到大排序的序列长度,这样就将序列复制到空序列中了。
        seq_tensor[idx, :seq_len] = torch.LongTensor(seq)

    #将序列长度按照维度为0,进行排序,下降是真,得到序列长度和索引
    seq_lengths, perm_idx = seq_lengths.sort(dim=0, descending=True)
    #将索引赋值给序列张量
    seq_tensor = seq_tensor[perm_idx]
    #将索引赋值给国家
    countries = countries[perm_idx]
    #返回序列张量,序列长度,国家。创建tensor
    return create_tensor(seq_tensor),\
        create_tensor(seq_lengths),\
        create_tensor(countries)

#定义time_since模块
def time_since(since):
    #现在的时间减去开始的时间的到时间差
    s = time.time() - since
    #Math.floor() 返回小于或等于一个给定数字的最大整数。计算分钟数
    m = math.floor(s / 60)
    #减去分钟数乘以60就是剩下的秒数
    s -= m * 60
    #返回分秒
    return '%dm %ds' % (m, s)


#定义训练模型
def trainModel():
    #损失设为0
    total_loss = 0
    #对于i,名字和国家在训练加载器中遍历,从1开始
    for i , (names,countries)in enumerate(trainloader,1):
        #将名字和国家代入到make_tensors模型中得到输入,序列长度,目标
        inputs,seq_lengths,target = make_tensors(names,countries)
        #将输入和序列长度代入到分类器中得到输出
        output = classifier(inputs,seq_lengths)
        #将输出和目标代入到损失标准器中得到损失
        loss = criterion(output,target)
        #梯度归零
        optimizer.zero_grad()
        #反向传播
        loss.backward()
        #更新
        optimizer.step()
        #损失标量相加得到总的损失    
        total_loss +=loss.item()
        #如果i能被10整除
        if i % 10 == 0:
            #以f开头表示在字符串内支持大括号内的python 表达式。将开始的时间代入time_since中得到分秒,循环次数,end是不换行加空格
            print(f'[{time_since(start)}]) Epoch {epoch}',end='')
            #f,i×输入的长度除以训练集的长度
            print(f'[{i * len(inputs)}/{len(trainset)}]',end='')
            #总损失除以i×输入的长度,得到损失
            print(f'loss={total_loss / (i * len(inputs))}')
    #返回总损失
    return total_loss
#定义测试模型    
def testModel():
    #初始正确的为0
    correct = 0
    #总长是测试集的长度
    total = len(testset)
    #打印,,,
    print("evaluating trained model ...")
    #不用梯度
    with torch.no_grad():
        #对于i,名字和国家在测试加载器中遍历,从1开始
        for i,(name,countries)in enumerate(testloader,1):
            #将名字和国家代入到make_tensors模型中得到输入,序列长度,目标
            inputs,seq_lengths,target = make_tensors(name,countries)
            #将输入和序列长度代入到分类器中得到输出
            output = classifier(inputs,seq_lengths)
            #按照维度为1的方向,保持输出的维度为真,取输出的最大值的第二个结果,得到预测值
            pred = output.max(dim=1,keepdim=True)[1]            
            #view_as将target的张量变成和pred同样形状的张量,eq是等于,预测和目标相等。标量求和
            correct += pred.eq(target.view_as(pred)).sum().item()
        #100×正确除以错误,小数点后保留两位,得到百分比
        percent = '%.2f' % (100 * correct / total)
        #测试集正确率
        print(f'Test set: Accuracy {correct}/{total} {percent}%')
    #返回正确除以总数    
    return correct / total
#封装到if语句里面
if __name__ == '__main__':
    #实例化分类器,字符的长度,隐层的大小,国家的数量,层数
    classifier = RNNClassifier(N_CHARS,HIDDEN_SIZE,N_COUNTRY,N_LAYER)
    #如果使用GPU
    if USE_GPU:
        #设置使用第一个GPU
        device = torch.device("coda:0")
        #让分类器进到设置里面跑
        classifier.to(device)
    #标准器是交叉熵损失    
    criterion = torch.nn.CrossEntropyLoss()
    #优化器是Adam。分类器的大部分参数,学习率是0.001
    optimizer = torch.optim.Adam(classifier.parameters(),lr=0.001)
    #开始时时间的时间
    start = time.time()
    #打印循环次数
    print("Training for %d epochs..."% N_EPOCHS)
    #空列表
    acc_list = []
    #对于循环在1到循环次数中。
    for epoch in range(1,N_EPOCHS + 1):
        #训练模型
        trainModel()
        #测试模型
        acc = testModel()
        #将测试结果加到列表中
        acc_list.append(acc)


#循环,起始是1,列表长度+1是终点。步长是1
epoch = np.arange(1, len(acc_list) + 1, 1)
#将数据变成一个矩阵
acc_list = np.array(acc_list)
#循环,列表
plt.plot(epoch, acc_list)
#x标签
plt.xlabel('Epoch')
#y标签
plt.ylabel('Accuracy')
#绿色
plt.grid()
#展示
plt.show()

 

你可能感兴趣的:(PyTorch深度学习实践代码 第十三讲)