资讯详情

RNN以及其改进版(附2个代码案列)

感谢阅读

  • RNN简介
    • 传统RNN
      • 演示内部结构过程
      • 内部计算公式
      • RNN输出
      • 激活函数tanh
      • Pytorch构建传统RNN
      • 梯度计算
    • LSTM介绍
      • 遗忘门结构分析:
      • 输入门结构分析:
      • 细胞状态更新分析:
      • 输出门结构分析:
      • 结构图
      • 梯度公式
      • 加强对现实生活的理解
      • 代码示例
    • GRU介绍
      • 结构图
      • 个人对GRU的理解
      • LSTM两个地方难以比拟
  • RNN示例(人名分类问题)
    • 案例介绍
    • 下载和解释数据集
    • 导包
    • 检查常用字符的数量
    • 建立国家名称,获得国家数量
    • 读数据到内存
    • 构建数据源并迭代
      • 改进异常索引的处理
    • 构建三种RNN模型
      • 构建传统RNN
      • 构建LSTM
      • 构建GRU
    • 测试和训练三个模型
      • 测试
      • 训练
        • 传统RNN
        • LSTM
        • GRU
    • 进行预测
      • 构建预测函数
        • 传统RNN
        • LSTM
        • GRU
        • 调用
  • 注意力机制
    • 注意机制简介
      • 注意力概念
      • 注意力计算规则
      • 作用
      • 帮助理解生活场景
    • bmm运算简介
    • 代码实现
  • RNN案例 seq2seq英译法
    • seq2seq介绍
      • seq2seq模型架构
      • 模型解释
    • 数据集下载
    • 清理导包和文件
    • 思路分析
    • 数据预处理
    • 构建数据源对象并进行测试
    • 编码器和解码器
      • 构建基于GRU的编码器
        • 思路分析
        • 代码实现
      • 基于GRU和Attention的解码器
        • 结构图
        • 代码实现
    • 训练模型
      • teacher_forcing
      • 内部迭代训练函数
        • 设置参数
        • 代码实现
      • 训练
    • 模型评估与测试
      • 书写评估函数
      • 评估
  • 关于服务器调试python的说明
    • 操作前需要知道的知识点
      • nohup
      • tail
    • 首先进入python文件所在目录并编译
    • 转后台进程
    • 启动一个SSH连接看训练输出
    • 执行结果

RNN简介

RNN(Recurrent Neural Network), 中文称循环神经网络, 它通常以序列数据为输入, 通过网络内部的结构设计,有效地捕捉序列之间的关系特征, 一般以序列的形式输出

传统RNN

演示内部结构过程

在这里插入图片描述 两个黑点一起到达蓝色区域(并在之前形成整体)

内部计算公式

RNN输出

激活函数tanh

帮助调整流经网络的值, tanh函数将值压缩在-1和1之间

Pytorch构建传统RNN

def dm_run_for_hiddennum():     ''' 第一个参数:input_size(输入张量x的维度) 第二个参数:hidden_size(隐藏层的维度, 隐藏层的神经元数) 第三个参数:num_layer(隐藏层的数量) '''     rnn = nn.RNN(5, 6, 2)  # A 隐藏层数从1-->2 需要修改以下程序的地方?     ''' 第一个参数:sequencelength(输入序列的长度) 第二个参数:batch_size(批次的样本数量) 第三个参数:input_size(输入张量的维度) '''
    input = torch.randn(1, 3, 5)  # B
    ''' 第一个参数:num_layer * num_directions(层数*网络方向) 第二个参数:batch_size(批次的样本数) 第三个参数:hidden_size(隐藏层的维度, 隐藏层神经元的个数) '''
    h0 = torch.randn(2, 3, 6)  # C

    output, hn = rnn(input, h0)	  #
    print('output-->', output.shape, output)
    print('hn-->', hn.shape, hn)
    print('rnn模型--->', rnn)  # nn模型---> RNN(5, 6, num_layers=11)

    # 结论:若只有一个隐藏次 output输出结果等于hn
    # 结论:如果有2个隐藏层,output的输出结果有2个,hn等于最后一个隐藏层

梯度计算

LSTM介绍

优点:LSTM的门结构能够有效减缓长序列问题中可能出现的梯度消失或爆炸, 虽然并不能杜绝这种现象, 但在更长的序列问题上表现优于传统RNN. 缺点:由于内部结构相对较复杂, 因此训练效率在同等算力下较传统RNN低很多

遗忘门结构分析:

与传统RNN的内部结构计算非常相似, 首先将当前时间步输入x(t)与上一个时间步隐含状态h(t-1)拼接, 得到[x(t), h(t-1)], 然后通过一个全连接层做变换, 最后通过sigmoid函数进行激活得到f(t), 我们可以将f(t)看作是门值, 好比一扇门开合的大小程度, 门值都将作用在通过该扇门的张量, 遗忘门门值将作用的上一层的细胞状态上, 代表遗忘过去的多少信息, 又因为遗忘门门值是由x(t), h(t-1)计算得来的, 因此整个公式意味着根据当前时间步输入和上一个时间步隐含状态h(t-1)来决定遗忘多少上一层的细胞状态所携带的过往信息.

输入门结构分析:

我们看到输入门的计算公式有两个, 第一个就是产生输入门门值的公式, 它和遗忘门公式几乎相同, 区别只是在于它们之后要作用的目标上. 这个公式意味着输入信息有多少需要进行过滤. 输入门的第二个公式是与传统RNN的内部结构计算相同. 对于LSTM来讲, 它得到的是当前的细胞状态, 而不是像经典RNN一样得到的是隐含状态.

细胞状态更新分析:

细胞更新的结构与计算公式非常容易理解, 这里没有全连接层, 只是将刚刚得到的遗忘门门值与上一个时间步得到的C(t-1)相乘, 再加上输入门门值与当前时间步得到的未更新C(t)相乘的结果. 最终得到更新后的C(t)作为下一个时间步输入的一部分. 整个细胞状态更新过程就是对遗忘门和输入门的应用.

输出门结构分析:

输出门部分的公式也是两个, 第一个即是计算输出门的门值, 它和遗忘门,输入门计算方式相同. 第二个即是使用这个门值产生隐含状态h(t), 他将作用在更新后的细胞状态C(t)上, 并做tanh激活, 最终得到h(t)作为下一时间步输入的一部分. 整个输出门的过程, 就是为了产生隐含状态h(t).

结构图

C表示某时刻的记忆细胞 h表示某时刻的状态 f遗忘门 i更新门 o输出门 ht流向下一个时刻以及直接输出

梯度公式

现实生活列子加强理解

我们以考试为例子:我们马上要期末考试了,第一门是高数,第二门线代。这个图的Xt就是考线代,h(t-1)就是考高数的状态,c(t-1)就是考高数的记忆,ht包含了考线代结束的状态以及分数,分数那一部分作为输出从上面输出,状态传给下一门考试(比如英语)。为什么遗忘?因为并不是所有东西都是线代需要关心的,这就是遗忘门的作用。为什么要保留上一门知识,比如高数用到的运算能力是我们需要保留的。 同样的,传统RNN就是要记住所有的东西,效率就会变低。

代码示例

# 定义LSTM的参数含义: (input_size, hidden_size, num_layers)
# 定义输入张量的参数含义: (sequence_length, batch_size, input_size)
# 定义隐藏层初始张量和细胞初始状态张量的参数含义:
# (num_layers * num_directions, batch_size, hidden_size)

>>> import torch.nn as nn
>>> import torch
>>> rnn = nn.LSTM(5, 6, 2)
>>> input = torch.randn(1, 3, 5)
>>> h0 = torch.randn(2, 3, 6)
>>> c0 = torch.randn(2, 3, 6)
>>> output, (hn, cn) = rnn(input, (h0, c0))
>>> output
tensor([[[ 0.0447, -0.0335,  0.1454,  0.0438,  0.0865,  0.0416],
         [ 0.0105,  0.1923,  0.5507, -0.1742,  0.1569, -0.0548],
         [-0.1186,  0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],
       grad_fn=<StackBackward>)
>>> hn
tensor([[[ 0.4647, -0.2364,  0.0645, -0.3996, -0.0500, -0.0152],
         [ 0.3852,  0.0704,  0.2103, -0.2524,  0.0243,  0.0477],
         [ 0.2571,  0.0608,  0.2322,  0.1815, -0.0513, -0.0291]],

        [[ 0.0447, -0.0335,  0.1454,  0.0438,  0.0865,  0.0416],
         [ 0.0105,  0.1923,  0.5507, -0.1742,  0.1569, -0.0548],
         [-0.1186,  0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],
       grad_fn=<StackBackward>)
>>> cn
tensor([[[ 0.8083, -0.5500,  0.1009, -0.5806, -0.0668, -0.1161],
         [ 0.7438,  0.0957,  0.5509, -0.7725,  0.0824,  0.0626],
         [ 0.3131,  0.0920,  0.8359,  0.9187, -0.4826, -0.0717]],

        [[ 0.1240, -0.0526,  0.3035,  0.1099,  0.5915,  0.0828],
         [ 0.0203,  0.8367,  0.9832, -0.4454,  0.3917, -0.1983],
         [-0.2976,  0.7764, -0.0074, -0.1965, -0.1343, -0.6683]]],
       grad_fn=<StackBackward>)

GRU介绍

GRU(Gated Recurrent Unit)也称门控循环单元结构, 它也是传统RNN的变体, 同LSTM一样能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象.

结构图

个人对GRU的理解

鄙人不才,如有理解错误之处,还望海涵斧正,先行谢过。 其实,个人感觉GRU是LSTM的改进版,把记忆、h二者功能进行了合并,其核心是上图最后一个式子,这就决定了记忆和遗忘的强度。 我还是以上学为例子。h(t-1)相当于我们在学校期间所学的所有东西,现在我们要搞毕设(以软件工程为例子,因为不才是软件工程专业出身,其他专业不是很了解)。Xt就是我们要搞毕设.h(t)就是我们学到的东西可以提供给做毕设的,比如python语言、软件体系结构、软件工程导论等。rt是什么呢?就是各学科可以提供给做毕设的东西的比列,比如python语言提供70%,软件体系提供10%。当然也有可能有0,比如选修课中的外国历史。h(上面带波浪线)的就相当于需要新学习的东西,比如我们做毕设的时候,想搞个反向代理服务器,学校没有教我们,我们是不是要自学?

LSTM难以比拟的两个地方

1.由于参数的减少,模型训练速度将会提升,同时可解释性也略微提升了一些。 2.由于运算的改良,降低了过拟合的风险。

RNN示例(人名分类问题)

案例介绍

以一个人名为输入, 使用模型帮助我们判断它最有可能是来自哪一个国家的人名, 这在某些国际化公司的业务中具有重要意义, 在用户注册过程中, 会根据用户填写的名字直接给他分配可能的国家或地区选项, 以及该国家或地区的国旗, 限制手机号码位数等等。

数据集下载与解释

在github上的name_decalre 点我下载 数据格式说明 每一行第一个单词为人名,第二个单词为国家名。中间用制表符tab分割

导包

# 导入torch工具
import torch
# 导入nn准备构建模型
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# 导入torch的数据源 数据迭代器工具包
from  torch.utils.data import Dataset, DataLoader
# 用于获得常见字母及字符规范化
import string
# 导入时间工具包
import time
# 引入制图工具包 
import matplotlib.pyplot as plt
# 从io中导入文件打开方法
from io import open

查看常用字符数量

def data_process():
    # 获取所有常用字符包括字母和常用标点
    all_letters = string.ascii_letters + " .,;'"
    # 获取常用字符数量
    n_letters = len(all_letters)
    return n_letters


def main():
    print(data_process())
    return 0


if __name__ == '__main__':
    main()

构建国家名字,并获取国家数量

def get_country():
    # 国家名 种类数
    categorys = ['Italian', 'English', 'Arabic', 'Spanish', 'Scottish', 'Irish', 'Chinese', 'Vietnamese', 'Japanese',
                 'French', 'Greek', 'Dutch', 'Korean', 'Polish', 'Portuguese', 'Russian', 'Czech', 'German']
    # 国家名 个数
    categorynum = len(categorys)
    return categorys,categorynum


def main():
    # print(data_process())
    print(get_country())
    return 0

读数据到内存

def read_data(filename):
    """ :param filename: :return: # 思路分析 1 打开数据文件 open(filename, mode='r', encoding='utf-8') 2 按行读文件、提取样本x 样本y line.strip().split('\t') 3 返回样本x的列表、样本y的列表 my_list_x, my_list_y """
    my_list_x, my_list_y= [], []
    # 打开文件
    with  open(filename, mode='r', encoding='utf-8') as f:
        # 按照行读数据
        for line in f.readlines():
            if len(line) <= 5:
                continue
            # 按照行提取样本x 样本y
            (x, y) = line.strip().split('\t')
            my_list_x.append(x)
            my_list_y.append(y)
    # 返回样本x的列表、样本y的列表
    return my_list_x, my_list_y

构建数据源并进行迭代

def read_data(filename):
    """ :param filename: :return: # 思路分析 1 打开数据文件 open(filename, mode='r', encoding='utf-8') 2 按行读文件、提取样本x 样本y line.strip().split('\t') 3 返回样本x的列表、样本y的列表 my_list_x, my_list_y """
    my_list_x, my_list_y= [], []
    # 打开文件
    with  open(filename, mode='r', encoding='utf-8') as f:
        # 按照行读数据
        for line in f.readlines():
            if len(line) <= 5:
                continue
            # 按照行提取样本x 样本y
            (x, y) = line.strip().split('\t')
            my_list_x.append(x)
            my_list_y.append(y)
    # 返回样本x的列表、样本y的列表
    return my_list_x, my_list_y
class NameClassDataset(Dataset):

    def __init__(self, my_list_x, my_list_y):
        # 样本x
        self.my_list_x = my_list_x
        # 样本y
        self.my_list_y = my_list_y
        # 样本条目数
        self.sample_len = len(my_list_x)

    # 获取样本条数
    def __len__(self):
        return self.sample_len

    # 获取第几条 样本数据
    def __getitem__(self, index):

        # 对index异常值进行修正 [0, self.sample_len-1]
        index = min(max(index, 0), self.sample_len-1)
        # 按索引获取 数据样本 x y
        x = self.my_list_x[index]
        y = self.my_list_y[index]
        # print(x, y)
        # 样本x one-hot张量化
        tensor_x = torch.zeros(len(x), n_letters)
        # 遍历人名 的 每个字母 做成one-hot编码
        for li, letter in enumerate(x):
            # letter2indx 使用all_letters.find(letter)查找字母在all_letters表中的位置
            # 给one-hot赋值
            tensor_x[li][all_letters.find(letter)] = 1
        # 样本y 张量化
        tensor_y = torch.tensor(categorys.index(y), dtype=torch.long)
        # 返回结果
        return tensor_x, tensor_y
def dm_test_NameClassDataset():

    # 1 获取数据
    myfilename = '../data/name_classfication.txt'
    my_list_x, my_list_y = read_data(myfilename)
    # 2 实例化dataset对象
    nameclassdataset = NameClassDataset(my_list_x, my_list_y)
    # 3 实例化dataloader
    mydataloader = DataLoader(dataset=nameclassdataset, batch_size=1, shuffle=True)
    for  i, (x, y) in enumerate (mydataloader):
        print('x.shape', x.shape, x)
        print('y.shape', y.shape, y)
        break

对异常索引的处理的改良

python语言支持我们的索引,所以我们的索引也应该如此,但是程序也会复杂一些,这个代码想替换的可以把NameClassDataset的__getitem__替换为下面的代码:

    def __getitem__(self, index):

        # 对index异常值进行修正 [0, self.sample_len-1]
        if index < 0:
            index = max(-self.sample_len, index)
        else:
            index = min(self.sample_len-1, index)
        # 按索引获取 数据样本 x y
        x = self.my_list_x[index]
        y = self.my_list_y[index]
        # print(x, y)
        # 样本x one-hot张量化
        tensor_x = torch.zeros(len(x), n_letters)
        # 遍历人名 的 每个字母 做成one-hot编码
        for li, letter in enumerate(x):
            # letter2indx 使用all_letters.find(letter)查找字母在all_letters表中的位置
            # 给one-hot赋值
            tensor_x[li][all_letters.find(letter)] = 1
        # 样本y 张量化
        tensor_y = torch.tensor(categorys.index(y), dtype=torch.long)
        # 返回结果
        return tensor_x, tensor_y

构建三种RNN模型

构建传统RNN

class RNN(nn.Module):

    def __init__(self, input_size, hidden_size, output_size, num_layers=1):
        super(RNN, self).__init__()
        # 1 init函数 准备三个层 self.rnn self.linear self.softmax=nn.LogSoftmax(dim=-1)
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.num_layers = num_layers

        # 定义rnn层
        self.rnn = nn.RNN(self.input_size, self.hidden_size, self.num_layers)
        # 定义linear层(全连接线性层)
        self.linear = nn.Linear(self.hidden_size, self.output_size)
        # 定义softmax层
        self.softmax = nn.LogSoftmax(dim=-1)

    def forward(self, input, hidden):
        # 让数据经过三个层 返回softmax结果和hn
        # 数据形状 [6,57] -> [6,1,57]
        input = input.unsqueeze(1)
        # 把数据送给模型 提取事物特征
        # 数据形状 [seqlen,1,57],[1,1,128]) -> [seqlen,1,18],[1,1,128]
        rr, hn = self.rnn(input, hidden)
        # 数据形状 [seqlen,1,128] - [1, 128]
        tmprr = rr[-1]
        tmprr = self.linear(tmprr)
        return self.softmax(tmprr), hn

    def inithidden(self):
        # 初始化隐藏层输入数据 inithidden()
        return torch.zeros(self.num_layers, 1,self.hidden_size)

构建LSTM

class LSTM(nn.Module):

    def __init__(self, input_size, hidden_size, output_size, num_layers=1):
        super(LSTM, self).__init__()
        # 1 init函数 准备三个层 self.rnn self.linear self.softmax=nn.LogSoftmax(dim=-1)
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.num_layers = num_layers

        # 定义rnn层
        self.rnn = nn.LSTM(self.input_size, self.hidden_size, self.num_layers)

        # 定义linear层
        self.linear = nn.Linear(self.hidden_size, self.output_size)

        # 定义softmax层
        self.softmax = nn.LogSoftmax(dim=-1)

    def forward(self, input, hidden, c):
        # 让数据经过三

标签: 3917连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台