《PyTorch深度学习实践》学习笔记:循环神经网络(高级篇)


1. RNN分类器

数据集
在这里插入图片描述
数据集里有人名和对应的国家,我们需要训练一个模型,输入一个新的名字,模型能预测出这个名字是基于哪种语言的(18种不同的语言,18分类)。
在这里插入图片描述
在自然语言处理中,通常的方式:①先把词或字变成一个one-hot向量,one-hot向量维度高,而且过于稀疏,所以一般来说先通过嵌入层(embed)把one-hot向量转化成低维的稠密向量,②然后经过RNN层,隐层的输出不一定和最终要求的目标一致,所以要用一个线性层把输出映射成和我们的要求一致。

在上面分类器中,要求输出一个大的分类(名字属于哪一个分类),所以对于最上面部分的输出是没有要求的,也就是说并不要求所有隐藏的输出做线性的变换,而且并不知道输出的结果是什么,所以为了解决这一个问题,网络是可以变得更加简单的。
在这里插入图片描述
只需要输出最终的隐藏状态,然后最终的隐藏状态接一个线性层,然后分成18个类别,这样可以实现名字分类的任务。本节主要学习的就是处理自然语言过程的方法和流程。

整体的使用模型结构
在这里插入图片描述
Maclean这个名字,我们其实得到的是一个序列M a c l e a n,每一个字符其实就是x1,x2,x3,x4依次类推,所以看上去我们只是名字一个字段,实际上它是一个序列,而且还有问题就是序列的长短是不一样的,所以我们还要思考序列长度不一致的问题。

模型处理过程
在这里插入图片描述

2. 分类器实现

主体代码

if __name__ == '__main__':
    # N_CHARS:序列的字符数 HIDDEN_SIZE:隐藏层尺寸 N_COUNTRY:国家的类别数 N_LAYES:GRU的层数
    classifier = RNNClassication(N_CHARS,HIDDEN_SIZE,N_COUNTRY,N_LAYES)
    # 使用GPU
    if USE_GPU:
        device = torch.device("cuda:0")
        model.to(device)
    # 优化器和损失函数
    creation = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(),lr = 0.001)

    # 计时
    start = time.time()
    print("Trainginng for {} epochs...".format(N_EPOCHS))
    acc_list = []
    for epoch in range(1,N_EPOCHS):
        # 训练
        trainModel()
        # 测试
        acc = testModel()
        acc_list.append(acc)
# 计时模块
def time_since(since):
    s = time.time() - since
    m = math.floor(s/60)
    s -= m*60
    return "{} {}".format(m,s)
# 绘图模块
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()

2.1 准备数据

在这里插入图片描述
①拿到的是字符串,先转变成序列,转成字符列表,列表里面的每一个数就是名字里面的每一个字符。

②接下来做词典,可以用ASCII表,ASCII表是128个字符,我们把字典长度设置成128,求每一个字符对应的ASCII值,拼成我们想要的序列。上图中的最右表中每一个数并不是一个数字,而是一个one-hot向量。例如77,就是一个128维的向量,第77个数的值为1,其他的值都是0。对于Embedding(嵌入层)来说,只要告诉嵌入层第几个维度是1就行了,所以只需要把ASCII值放在这就行了。

数据对齐,使得数据的长度一致,找出最长的,然后其余的补0。做完填充之后,可以保证构成一个张量。
在这里插入图片描述
上述是名字的处理,下面是国家的处理,将国家转变成一个分类索引。
在这里插入图片描述

# 准备数据集
class NameDataset(Dataset):
    def __init__(self,is_train_set=True):
        filename = 'names_train.csv.gz' if is_train_set else 'names_test.csv.gz'
        with gzip.open(filename,'rt') as f:          # 使用gzip读取gz文件
            reader = csv.reader(f)                   # 使用csv读取里面内容
            rows = list(reader)                      # 存到列表中,每一个都是元组(名字,国家)
        self.names = [row[0] for row in rows]       # 将名字存到列表中
        self.len = len(self.name)                    # 记录长度
        self.countries = [row[1] for row in rows]    # 将国家存到列表中
        self.countries_list = list(sorted(set(self.countries)))  # 去重 排序 再存到列表
        self.countries_dict = self.getCountryDict()              # 设置一个字典进行查找国家
        self.countries_num = len(self.countries_list)            # 国家的类别数

    def __getitem__(self, item):
        return self.names[item],self.countries_dict[self.countries[item]]  # 字典,key是国家名,value是index

    def __len__(self):
        return self.len

    def getCountryDict(self):  # 构造国家的查询字典
        country_dict = dict()
        for idx,country_name in enumerate(self.countries_list,0):
            country_dict[country_name] = idx
        return country_dict

    def idx2country(self,index): # 根据索引返回国家的字符串
        return self.countries_list[index]

    def getCountriesNum(self):   # 返回国家的数量
        return self.countries_num

注意上述代码读取数据集为什么不用Numpy?因为读取数据集有很多种方式,如果是pickle/HDFS/HD5类型的数据,要就要用相应的包。

trainset = NameDataset(is_train_set=True)
trainloader = DataLoader(dataset=trainset,batch_size=BATCH_SIZE,shuffle=True)
testset = NameDataset(is_train_set=False)
testloader = DataLoader(dataset=testset,batch_size=BATCH_SIZE,shuffle=False)

N_COUNTRY = trainset.getCountriesNum()  # 决定模型最终输出的维度大小

2.2 准备模型

2.2.1 init过程

先看看和GRU相关的参数:hidden_size和n_layers。
在这里插入图片描述
注意Embedding层的输入、输出维度:
在这里插入图片描述
还有GRU的输入、输出维度:
在这里插入图片描述
bidrectional是双向循环神经网络。

代码如下:

# 模型
class RNNClassication(nn.Module):
    def __init__(self,input_size,hidden_size,output_size,n_layers=1,bidirectional = True):
        super(RNNClassication, self).__init__()
        self.hidden_size = hidden_size   # GRU layer
        self.n_layers = n_layers         # GRU layer
        self.n_directions = 2 if bidirectional else 1 #What is the Bi-Direction RNN/LSTM/GRU?
        # The input of Embedding Layer with shape:𝑠𝑒𝑞𝐿𝑒𝑛, 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒
        # The output of Embedding Layer with shape:𝑠𝑒𝑞𝐿𝑒𝑛, 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒, ℎ𝑖𝑑𝑑𝑒𝑛𝑆𝑖𝑧𝑒
        self.embedding = torch.nn.Embedding(input_size,hidden_size)

        # The inputs of GRU Layer with shape:
        # 𝑖𝑛𝑝𝑢𝑡: 𝑠𝑒𝑞𝐿𝑒𝑛, 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒, ℎ𝑖𝑑𝑑𝑒𝑛𝑆𝑖𝑧𝑒
        # ℎ𝑖𝑑𝑑𝑒𝑛: 𝑛𝐿𝑎𝑦𝑒𝑟𝑠 ∗ 𝑛𝐷𝑖𝑟𝑒𝑐𝑡𝑖𝑜𝑛𝑠, 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒, ℎ𝑖𝑑𝑑𝑒𝑛𝑆𝑖𝑧𝑒
        # The outputs of GRU Layer with shape:
        # 𝑜𝑢𝑡𝑝𝑢𝑡: 𝑠𝑒𝑞𝐿𝑒𝑛, 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒, ℎ𝑖𝑑𝑑𝑒𝑛𝑆𝑖𝑧𝑒 ∗ 𝑛𝐷𝑖𝑟𝑒𝑐𝑡𝑖𝑜𝑛𝑠
        # ℎ𝑖𝑑𝑑𝑒𝑛: 𝑛𝐿𝑎𝑦𝑒𝑟𝑠 ∗ 𝑛𝐷𝑖𝑟𝑒𝑐𝑡𝑖𝑜𝑛𝑠, 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒, ℎ𝑖𝑑𝑑𝑒𝑛𝑆𝑖𝑧𝑒
        self.gru = torch.nn.GRU(hidden_size,hidden_size,n_layers,bidirectional=bidirectional) # 输入输出都是hidden_size
        self.fc = torch.nn.Linear(hidden_size*self.n_directions,output_size)

    def __init_hidden(self,batch_size): # 创建全0的初始的隐层
        hidden = torch.zeros(self.n_layers*self.n_directions,batch_size,self.hidden_size)
        return create_tensor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值