IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 【深度学习理论】(6) 循环神经网络 RNN -> 正文阅读

[人工智能]【深度学习理论】(6) 循环神经网络 RNN

大家好,今天和各位分享一下处理序列数据的循环神经网络RNN的基本原理,并用 Pytorch 实现 RNN 层和 RNNCell 层。

之前的博文中已经用过循环神经网络做过许多实战案例,感兴趣的可以看我这个专栏:https://blog.csdn.net/dgvv4/category_11712004.html


1. 序列的表示方法

在循环神经网络中,序列数据的 shape 通常是 [batch, seq_len, feature_len],其中 seq_len 代表特征的个数feature_len 代表每个特征的表示方法。

对于自然语言任务: 以 shape=[b, 5, 100] 为例,其中 5 代表每句话有 5 个单词,而 100 代表每个单词使用一个长度为 100 的向量来表示。

对于时间序列任务: 以 shape=[b, 100, 1] 为例,其中 100 代表每个 batch 统计了?100 天的数据,每天有 1 个气温值。

下面以语言的情感分析任务为例,向大家介绍处理序列数据的传统方法,如下图:

现在有一个句子 The flower is so beautiful 作为输入,通过 wordembedding 每个单词用一个长度为 100 的向量来表示,然后将每个单词输入至线性层提取特征,每个单词的输出结果是一个长度为 2 的向量,最后将所有单词聚合起来,经过一个线性层输出得到分类结果。

传统的序列处理方法存在许多缺陷:

(1)计算量庞大。现实生活中的单词量巨大,对每个单词生成一个线性层 x@w+b 提取特征,然后再对线性层输出结果做聚合,模型非常复杂,参数量极其庞大。

(2)没有考虑上下文语境。传统方法只是针对一句话中的每个单词做单独的分析,没有联系前后单词之间的信息。如:i do not think the flower is?beautiful?句子中,不能看到 beautiful 就说这句话一定是好评,要联系到上文的 not 再做分析。


2. RNN 原理解析

针对传统序列任务模型存在的问题,RNN做出了改进:

(1)优化参数量。通过权值共享,把每个单词的 w1、w2、w3... 用一个张量 W 来表示,一个RNN层就处理一整个句子。

(2)联系上下文语境。使用一个时序单元处理上下文信息,当前时刻的输入一定要考虑到上一时刻的输出。

下面仍以语言的情感分析任务为例,向大家介绍RNN的基本原理。

RNN单元的计算公式为:h_{t} = f_{W}(h_{t-1}, x_{t})

其中,?x_{t} 代表当前时刻的输入特征h_{t-1}? 代表上一时刻的输出,也是上一时刻聚合后的语境信息;

接下来把公式展开: ?h_{t} = tanh(W_{hh}h_{t-1} + W_{xh}x_{t})

其中,?W_{xh} 代表对当前时刻输入的特征提取,?W_{hh} 代表对之前语境信息的特征提取,然后对计算结果使用 tanh 激活函数,得到本时刻更新后的语境信息 ?h_{t}


3. RNN 的梯度推导

下面以时间序列预测任务为例,向大家介绍一下 RNN 的梯度更新方式,如下图。

取 RNN 层的最后一个语境信息 ht 作为预测结果输出。predict 代表前向传播得出的预测值target 代表真实值损失函数为预测值和真实值的均方误差MSE

前向传播:?h_{t} = tanh(W_{hh}h_{t-1} + W_{xh}x_{t})

线性变换:?y_{t} = W_{0}h_{t}

损失函数:?\frac{1}{2}(y_{t}-target)^{2}

?通过损失函数值更新每个时刻的语境的梯度信息?W_{hh}

反向传播公式:

\frac{\partial E_{t} }{\partial W_{hh} } = \sum_{i=0}^{t}\ \frac{\partial E_{t} }{\partial y_{t} } \frac{\partial y_{t} }{\partial h_{t} } \frac{\partial h_{t} }{\partial h_{i} } \frac{\partial h_{i} }{\partial W_{hh} }

分别对每个分式计算偏微分:

\frac{\partial E_{t} }{\partial y_{t}} = \frac{\partial \frac{1}{2}(y_{t}-target)^{2} }{\partial y_{t}}

\frac{\partial y_{t} }{\partial h_{t}} = \frac{\partial W_{O}h_{t} }{\partial h_{t}} = W_{O}

\frac{\partial h_{i} }{\partial W_{hh}} = \frac{\partial tanh(W_{hh}h_{i-1} + W_{xh}x_{t}) }{\partial W_{hh}}= h_{i-1}

\frac{\partial h_{t} }{\partial h_{i}} = \frac{\partial h_{t} }{\partial h_{t-1}} \frac{\partial h_{t-1} }{\partial h_{t-2}} ... \frac{\partial h_{i+1} }{\partial h_{i}} = \prod_{k=i}^{t-1} \frac{\partial h_{k+1} }{\partial h_{k}}

其中:

\frac{\partial h_{k+1} }{\partial h_{k}} = diag(tanh'(W_{hx}x_{i}+W_{hh}h_{i-1}))W_{hh}


4. 模型结构

下面向大家介绍一下 RNN 层的结构,各个输入和输出张量的 shape

首先,网络输入的 shape 为 [seq_len, batch, feature_len]。其中 seq_len 代表特征的个数batch 代表有多少个句子feature_len 代表每个特征的向量表示hidden_len 代表 RNN 单元的隐含层神经元个数

以 batch=3,seq_len=10,feature_len=100,hidden_len=20 为例,向大家介绍网络的输入和输出的特征的 shape 变化

RNN 层的公式:?x_{t}@W_{xh} + h_{t}@W_{hh}

shape 变化为:[batch, feature len] @ [hidden len, feature len]^{T} + [batch, hidden len ] @ [hidden len, hidden len]^{T}

带入具体数值:[3, 100] @ [20, 100]^{T} + [3, 20] @ [20, 20]^{T} = [3,20] + [3,20] = [3,20]

下面在Pytorch中展示单个RNN层的参数的shape

import torch
from torch import nn

# 100代表feature_len每个单词的向量表示的长度
# 20代表hidden_len经过RNN层之后每个单词的向量表示长度变成20
rnn = nn.RNN(100, 20)

# 查看RNN单元的参数
print(rnn._parameters.keys())

# 查看每个参数的shape
print('W_xh:', rnn.weight_ih_l0.shape,  
      'bias_xh:', rnn.bias_ih_l0.shape,  
      'W_hh:', rnn.weight_hh_l0.shape,  
      'bias_hh:', rnn.bias_hh_l0.shape)    

'''
输出结果:

odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0'])

W_xh: torch.Size([20, 100]) 
bias_xh: torch.Size([20]) 
W_hh: torch.Size([20, 20]) 
bias_hh: torch.Size([20])
'''

5. Pytorch 代码实现

5.1 单层 RNN 实现

首先需要实例化一个RNN层

input_size:用多少长的向量来表示一个单词。

hidden_size:经过RNN层特征提取后?x_{t}@W_{xh} + h_{t}@W_{hh},每个单词用多少长的向量表示。

num_layers:共有多少层RNN。

rnn = nn.RNN(input_size, hidden_size, num_layers)

前向传播函数

x:当前时刻的输入特征,shape = [seq_len, batch, feature_len]

h0:上一时刻的语境信息,shape =?[num_layers, batch, hidden_size]

out:最后一个时刻的输出结果,shape = [seq_len, batch, hidden_len]

h:所有时刻的语境状态,shape =?[num_layers, batch, hidden_size]

out, h = rnn(x, h0)

以 batch=3,seq_len=10,feature_len=100,hidden_len=20 为例,单个RNN层的代码如下:

import torch
from torch import nn

# input_size:代表每个单词的向量表示的长度
# hidden_size:代表特征提取后,每个单词的向量表示长度
# num_layers:代表RNN的层数
rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=1)  # 实例化单层的RNN层

# 构造输入层shape=[seq_len, batch, feature_len]
x = torch.randn(10, 3, 100)

# 构造上一时刻的语境shape=[num_layers, batch, hidden_size]
h0 = torch.randn(1, 3, 20)

# 前向传播的返回值如下
# out:代表每个时刻的h的输出结果shape=[seq_len, batch, hidden_len]
# h:代表最后一个时刻的输出结果shape=[num_layers, batch, hidden_size]
out, h = rnn(x, h0)

print('out:', out.shape, 'h:', h.shape)

'''
输出结果
out: torch.Size([10, 3, 20]) 
h: torch.Size([1, 3, 20])
'''

5.2 多层 RNN 实现

参数和上面相同,这里要注意的就是在前向传播的输出结果中,h 代表在最后一个时刻上看之前的所有语境信息,而 out 代表每个RNN层的输出结果

4层的RNN代码如下:

import torch
from torch import nn

# input_size:代表每个单词的向量表示的长度
# hidden_size:代表特征提取后,每个单词的向量表示长度
# num_layers:代表RNN的层数
rnn = nn.RNN(input_size=100, hidden_size=20, num_layers=4)  # 实例化4层的RNN层

# 构造输入层shape=[seq_len, batch, feature_len]
x = torch.randn(10, 3, 100)

# 构造初始时刻的语境shape=[num_layers, batch, hidden_size]
h0 = torch.randn(4, 3, 20)

# out:代表每个时刻的h的输出结果shape=[seq_len, batch, hidden_len]
# h:代表最后一个时刻的输出结果shape=[num_layers, batch, hidden_size]
out, h = rnn(x, h0)

print('out:', out.shape, 'h:', h.shape)
'''
out: torch.Size([10, 3, 20]) 
h: torch.Size([4, 3, 20])
'''

5.3 单层 RNNCell 实现

nn.RNN将所有句子全部都输入至RNN层中,而 nn.RNNCell 需要手动输入每个句子,并且当前时刻的输出状态不会自动进入到下一时刻。单个RNNCell结构如下。

实现过程如下:

import torch
from torch import nn

# input_size:代表每个单词的向量表示的长度
# hidden_size:代表特征提取后,每个单词的向量表示长度
rnncell = nn.RNNCell(input_size=100, hidden_size=20)  # 实例化单层的RNNcell层

# 构造输入层shape=[seq_len, batch, feature_len]
inputs = torch.randn(10, 3, 100)

# 构造初始时刻的语境shape=[batch, hidden_size]
h0 = torch.randn(3, 20)

# RNNCell的输入shape=[batch, feature_len]
for x in inputs:
    # h0:代表当前时刻的语境信息shape=[batch, hidden_len]
    h0 = rnncell(x, h0)

print('h0:', h0.shape)
'''
h0: torch.Size([3, 20])
'''

5.4 多层的 RNNCell 实现

以两层的 RNNCell 实现为例

第一个 RNNCell 层将每个单词的向量表示长度从 100 变成 20,第二个 RNNCell 层将每个单词的向量表示长度从 20 变成 15。

第一个 RNNCell 的输入当前时刻的单词和上一时刻的语境状态h0第二个 RNNCell 的输入第一个 RNNCell 的输出和上一时刻的语境状态h1

代码实现如下:

import torch
from torch import nn

# input_size:代表每个单词的向量表示的长度
# hidden_size:代表特征提取后,每个单词的向量表示长度
rnncell1 = nn.RNNCell(input_size=100, hidden_size=20)  # 实例化单层的RNNcell层
rnncell2 = nn.RNNCell(input_size=20, hidden_size=15)

# 构造输入层shape=[seq_len, batch, feature_len]
inputs = torch.randn(10, 3, 100)

# 构造初始时刻的语境shape=[batch, hidden_size]
h0 = torch.randn(3, 20)
h1 = torch.randn(3, 15)

# RNNCell的输入shape=[batch, feature_len]
for x in inputs:
    # h0:代表当前时刻的语境信息shape=[batch, hidden_len]
    h0 = rnncell1(x, h0)
    h1 = rnncell2(h0, h1)

print('h1:', h1.shape)
'''
h1: torch.Size([3, 15])
'''
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:06:59  更:2022-06-25 18:08:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 0:44:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码