3.5 tensorflow 中LSTM和GRU模块使用意境级讲解
Posted 炫云云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3.5 tensorflow 中LSTM和GRU模块使用意境级讲解相关的知识,希望对你有一定的参考价值。
文章目录
循环神经网络实现文本情感分类
目标
- 知道LSTM和GRU的使用方法及输入输出的格式
- 能够应用LSTM和GRU实现文本情感分类
1. tensorflow 中LSTM和GRU模块使用
1.1 LSTMCell
LSTMCell 的用法和 SimpleRNNCell 基本一致,区别在于 LSTM 的状态变量 List 有两个,即 [ h t , c t ] \\left[\\boldsymbol{h}_{t}, \\boldsymbol{c}_{t}\\right] [ht,ct], 需要分别初始化,其中 L i s t \\mathrm{List} List 第一个元素为 h t \\boldsymbol{h}_{t} ht, 第二个元素为 c t \\boldsymbol{c}_{t} ct 。调用 cell 完成前向运算时,返回两个元素,第一个元素为 cell 的输出,也就是 h t \\boldsymbol{h}_{t} ht, 第二个元素为 cell 的更新后的状态 List: [ h t , c t ] \\left[\\boldsymbol{h}_{t}, \\boldsymbol{c}_{t}\\right]_{} [ht,ct] 。首先新建一个状态向量长度 h = 64 h=64 h=64 的 LSTM Cell,其中状态向量 c t \\boldsymbol{c}_{t} ct 和输出向量 h t \\boldsymbol{h}_{t} ht 的长度都为h,代码如下:
x = tf.random.normal([2, 80, 100])
xt = x[:, 0, :] # 得到一个时间戳的输入
cell = layers.LSTMCell(64) # 创建 LSTM Cell
# 初始化状态 List,[h,c]
state = [tf.zeros([2, 64]), tf.zeros([2, 64])]
out, state = cell(xt, state) # 前向计算 ,和输出
# 查看返回元素的 id
id(out), id(state[0]), id(state[1]) # 为C_t
(1936930386344, 1936930386344, 1936930387048)
可以看到,返回的输出 out 和 L i s t \\mathrm{List} List 的第一个元素 h t \\boldsymbol{h}_{t} ht 的 id 是相同的,这与RNN 初衷 一致,都是为了格式的统一。
通过在时间步上展开循环运算,即可完成一次层的前向传播,写法与RNN 一 样。例如:
# 在序列长度维度上解开,循环送入 LSTM Cell 单元
for xt in tf.unstack(x, axis=1):
# 前向计算
out, state = cell(xt, state)
输出可以仅使用最后一个时间戳上的输出,也可以聚合所有时间戳上的输出向量。
1.2 LSTM
LSTM层和GRU层都是由tf.keras.layers.
提供.其中LSTM:
keras.layers.LSTM(units, activation='tanh', recurrent_activation='sigmoid',
use_bias=True, return_sequences=False, return_state=False,
go_backwards=False, stateful=False, unroll=False)
参数
- units: 正整数,输出空间的维度。
- input_dim: 输入的维度(整数)。将此层用作模型中的第一层时,此参数(或者,关键字参数
input_shape
)是必需的。 - input_length: 输入序列的长度,在恒定时指定。如果你要在上游连接
Flatten
和Dense
层,则需要此参数(如果没有它,无法计算全连接输出的尺寸)。
请注意,如果循环神经网络层不是模型中的第一层,则需要在第一层的层级指定输入长度(例如,通过input_shape
参数)。 - activation: 要使用的激活函数。如果传入
None
,则不使用激活函数(即 线性激活:a(x) = x
)。 - recurrent_activation: 用于循环时间步的激活函数.默认:分段线性近似 sigmoid (
hard_sigmoid
)。
如果传入None
,则不使用激活函数(即 线性激活:a(x) = x
)。 - dropout: 在 0 和 1 之间的浮点数。单元的丢弃比例,用于输入的线性转换。
- recurrent_dropout: 在 0 和 1 之间的浮点数。单元的丢弃比例,用于循环层状态的线性转换。
- use_bias: 布尔值,该层是否使用偏置向量。
- unit_forget_bias: 布尔值。如果为 True,初始化时,将忘记门的偏置加 1。将其设置为 True 同时还会强制
bias_initializer="zeros"
。这个建议来自 Jozefowicz et al. (2015)。 - return_sequences: 布尔值。是返回输出序列中的最后一个输出,还是全部序列的隐藏状态。
- return_state: 布尔值。除了输出之外是否返回最后一个状态。状态列表的返回元素分别是隐藏状态和单元状态。
- go_backwards: 布尔值 (默认 False)。如果为 True,则向后处理输入序列并返回相反的序列。
- stateful: 布尔值 (默认 False)。如果为 True,则批次中索引 i 处的每个样品的最后状态将用作下一批次中索引 i 样品的初始状态。
- unroll: 布尔值 (默认 False)。如果为 True,则网络将展开,否则将使用符号循环。展开可以加速 RNN,但它往往会占用更多的内存。展开只适用于短序列。
输入尺寸
3D 张量,尺寸为 (batch_size, timesteps, input_dim)
。
输出尺寸
- 如果
return_state
:返回张量列表。第一个张量为输出。剩余的张量为最后的状态,每个张量的尺寸为(batch_size, units)
。例如,对于 RNN/GRU,状态张量数目为 1,对 LSTM 为 2,即 h t h_t ht 和 C t C_t Ct。 - 如果
return_sequences
:返回 3D 张量,尺寸为(batch_size, timesteps, units)
。 - 否则,返回尺寸为
(batch_size, units)
的 2D 张量。
Masking
该层支持以可变数量的时间步对输入数据进行 masking。要将 masking 引入你的数据,请使用 Embedding 层,并将 mask_zero
参数设置为 True
。
关于在 RNN 中使用「状态(statefulness)」的说明
你可以将 RNN 层设置为 stateful
(有状态的),这意味着针对一个批次的样本计算的状态将被重新用作下一批样本的初始状态。这假定在不同连续批次的样品之间有一对一的映射。
为了使状态有效:
-
在层构造器中指定
stateful=True
。 -
为你的模型指定一个固定的批次大小,如果是顺序模型,为你的模型的第一层传递一个
batch_input_shape=(...)
参数。 -
为你的模型指定一个固定的批次大小,如果是顺序模型,为你的模型的第一层传递一个
batch_input_shape=(...)
。
如果是带有 1 个或多个 Input 层的函数式模型,为你的模型的所有第一层传递一个batch_shape=(...)
。
这是你的输入的预期尺寸,包括批量维度。它应该是整数的元组,例如
(32, 10, 100)
。 -
在调用
fit()
是指定shuffle=False
。
要重置模型的状态,请在特定图层或整个模型上调用 .reset_states()
。
关于指定 RNN 初始状态的说明
您可以通过使用关键字参数 initial_state
调用它们来符号化地指定 RNN 层的初始状态。initial_state
的值应该是表示 RNN 层初始状态的张量或张量列表。
您可以通过调用带有关键字参数 states
的 reset_states
方法来数字化地指定 RNN 层的初始状态。states
的值应该是一个代表 RNN 层初始状态的 Numpy 数组或者 Numpy 数组列表。
关于给 RNN 传递外部常量的说明
你可以使用 RNN.__call__
(以及 RNN.call
)的 constants
关键字参数将「外部」常量传递给单元。这要求 cell.call
方法接受相同的关键字参数 constants
。
这些常数可用于调节附加静态输入(不随时间变化)上的单元转换,也可用于注意力机制。
1.2 LSTM使用示例
假设数据输入为 input 形状是[10,20]
,假设embedding的形状是[100,30]
则LSTM使用示例如下:
batch_size =10 #句子的数量
seq_len = 20 #每个句子的长度
embedding_dim = 30 #每个词语使用多长的向量表示
word_vocab = 100 #词典中词语的总数
hidden_size = 12 #隐层中lstm的个数
#准备输入数据
input = np.random.randint(low=0,high=100,size=(batch_size,seq_len))
#准备embedding
embedding = layers.Embedding(word_vocab,embedding_dim)
#进行mebed操作
embed = embedding(input) #[10,20,30]
lstm = layers.LSTM(hidden_size)
output = lstm(embed)
#(batch_size, units)
print(output.shape)#(10, 12)
此时只会返回一个hidden state 值。如果input 数据包含多个时间步,则这个hidden state 是最后一个时间步的结果
1.2.1区别 cell state 和 hidden state
LSTM 网络结构
图 1 : L S T M 网 络 结 构 图1:LSTM 网络结构 图1:LSTM网络结构
图1是LSTM 的cell 单元,其中:
- h t h_{t} ht 表示第 t t t 步的 hidden state,是 t t t 时刻的数据被编码后的向量,也是LSTM对外的输出。
- C t C_{t} Ct 表示第 t 步的 cell state,是 t 时刻数据的记忆向量,代表LSTM的记忆,通常只在内部流动,不对外输出。cell state 是实现LSTM的关键。
通常我们只需拿到 hidden state 作为输LSTM的输出就够了,而不需要访问cell state,但是当想要设计复杂一点的网络的话,就需要用到cell state,比如encoder-decoder模型和Attention机制,设置return_state=True
。return_sequences=True
返回hidden state 所有时间步的结果.
lstm = layers.LSTM(hidden_size,return_state=True)
whole_seq_output, h_t,c_t = lstm(embed)
#(batch_size, units),(batch_size, units),(batch_size, units)
print(whole_seq_output.shape)#(10, 12)
print(h_t.shape)#(10, 12)
print(c_t.shape)#(10, 12)
输出所有时间步的hidden state。
lstm = layers.LSTM(hidden_size,return_sequences=True)
whole_seq_output = lstm(embed)
#(batch_size, timesteps, units)
print(whole_seq_output.shape)#(10, 20, 12)
lstm = layers.LSTM(hidden_size,return_sequences=True,return_state=True)
whole_seq_output, h_t,c_t = lstm(embed)
#(batch_size, timesteps, units),(batch_size, units),(batch_size, units)
print(whole_seq_output.shape)#(10, 20, 12)
print(h_t.shape)#(10, 12)
print(c_t.shape)#(10, 12)
whole_seq_output 输出所有时间步的hidden state。h_t最后一个时间步的 hidden state。 c_t是最后一个时间步 cell state结果。
输出如下
(10, 12)
(10, 12)
(10, 12)
(10, 12)
(10, 20, 12)
(10, 20, 12)
(10, 12)
(10, 12)
通过前面的学习,我们知道,最后一次的 h t h_t ht应该和whole_seq_output 的最后一个time step的输出是一样的
通过下面的代码,我们来验证一下:
a = whole_seq_output[:,-1,:]
print(h_t.shape)#(10, 12)
print(a.shape)#(10, 12)
a == h_t
(10, 12)
(10, 12)
<tf.Tensor: shape=(10, 12), dtype=bool, numpy=
array([[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True],
[ True, True, True, True, True, True, True, True, True,
True, True, True]])>
# define model
inputs = layers.Input(shape = (seq_len, embedding_dim))#batch_size没有
lstm, state_h, state_c = layers.LSTM(1,return_state = True)(inputs)
model = keras.Model(inputs = inputs,outputs = [lstm, state_h, state_c])
# define data and predict
test = np.random.randint(0,100,size=(seq_len, embedding_dim)).reshape([1,seq_len, embedding_dim])
print(model.predict(test))
[array([[0.7615942]], dtype=float32), # lstm: LSTM的输出
array([[0.7615942]], dtype=float32), # state_h: 最后时间步的 hidden state
array([[1.]], dtype=float32)] # state_c: 最后时间步的 cell state
最后时间步的 hidden state == LSTM的输出
1.2.2小结
- 输入和输出的类型
相对之前的tensor,这里多了个参数timesteps,其表示啥意思?举个栗子,假如我们输入有10个句子,每个句子都由20个单词组成,而每个单词用30维的词向量表示。那么batch_size=20,timesteps=10,input_dim=30,你可以简单地理解timesteps就是输入序列的长度seq_len(如上面的例子)
- units
假如units=12,就一个单词而言,你可以把LSTM内部简化看成 Y = X 1 × 30 W 30 × 12 Y=X_{1\\times30}W_{30\\times12} Y=X1×30W30×12 ,X为上面提及的词向量比如30维,W中的12就是units,也就是说通过LSTM,把词的维度由30转变成了12
1.2.3多层LSTM
对于多层神经网络,可以通过 Sequential 容器包裹多层 LSTM 层,并设置所有非末层网络 return_sequences=True,这是因为非末层的 LSTM 层需要上一层在所有时间戳的输出作为输入。例如:
net = keras.Sequential([
layers.LSTM(64, return_sequences=True), # 非末层需要返回所有时间戳输出
layers.LSTMDL之LSTM/GRU/CNN:基于tensorflow框架分别利用LSTM/GRUCNN算法对上海最高气温实现回归预测案例
Tensorflow 2 实战(kears)- 双层RNN/LSTM/GRU
TF之LSTM/GRU:基于tensorflow框架对boston房价数据集分别利用LSTMGRU算法(batch_size调优对比)实现房价回归预测案例