14、深度循环神经网络及相关模型介绍

深度循环神经网络及相关模型介绍

1. 双向循环神经网络(Bidirectional RNNs)

在传统的循环神经网络(RNN)中,给定时间步 t 的输出依赖于之前所有时间步的输出。然而,输出也可能依赖于未来的输出,这在自然语言处理(NLP)等应用中尤为明显。因为在预测一个单词或短语的属性时,可能需要考虑整个句子的上下文,而不仅仅是前面的单词。双向 RNN 有助于网络架构对序列的开头和结尾给予同等的重视,并增加训练可用的数据。

双向 RNN 由两个堆叠在一起的 RNN 组成,它们以相反的方向读取输入。例如,一个 RNN 从左到右读取单词,另一个则从右到左读取。每个时间步的输出将基于两个 RNN 的隐藏状态。

在 Keras 中,可以通过双向包装层(Bidirectional wrapper layer)来支持双向 RNN。以下是一个词性标注(POS tagging)的示例代码:

from keras.layers.wrappers import Bidirectional
model = Sequential()
model.add(Embedding(s_vocabsize, EMBED_SIZE,
input_length=MAX_SEQLEN))
model.add(SpatialDropout1D(Dropout(0.2)))
model.add(Bidirectional(LSTM(HIDDEN_SIZE, dropout=0.2, recurrent_dropout=0.2)))
model.add(RepeatVector(MAX_SEQLEN))
model.add(Bidirectional(LSTM(HIDDEN_SIZE, return_sequences=True)))
model.add(TimeDistributed(Dense(t_vocabsize)))
model.add(Activation("softmax"))

2. 有状态循环神经网络(Stateful RNNs)

RNN 可以是有状态的,这意味着它们在训练期间可以跨批次保持状态。即一批训练数据计算得到的隐藏状态将用作下一批训练数据的初始隐藏状态。不过,这需要显式设置,因为 Keras 中的 RNN 默认是无状态的,并且在每批训练后会重置状态。将 RNN 设置为有状态意味着它可以在训练序列中建立状态,甚至在进行预测时保持该状态。

使用有状态 RNN 的好处是可以减小网络规模和/或降低训练时间。缺点是需要使用反映数据周期性的批次大小来训练网络,并在每个周期后重置状态。此外,在训练网络时不应打乱数据,因为数据的呈现顺序对于有状态网络很重要。

2.1 使用 Keras 有状态 LSTM 预测电力消耗

下面通过一个示例来展示如何使用有状态和无状态的 LSTM 网络预测消费者的电力消耗,并比较它们的性能。

2.1.1 数据准备

使用来自 UCI 机器学习库的电力负荷图数据集(https://archive.ics.uci.edu/ml/datasets/ElectricityLoadDiagrams20112014),该数据集包含 370 个客户在 2011 年至 2014 年期间每 15 分钟的电力消耗信息。随机选择客户 250 的数据进行分析。

import numpy as np
import matplotlib.pyplot as plt
import os
import re
DATA_DIR = "../data"
fld = open(os.path.join(DATA_DIR, "LD2011_2014.txt"), "rb")
data = []
cid = 250
for line in fld:
    if line.startswith(";"):
        continue
    cols = [float(re.sub(",", ".", x)) for x in 
                line.strip().split(";")[1:]]
    data.append(cols[cid])
fld.close()
NUM_ENTRIES = 1000
plt.plot(range(NUM_ENTRIES), data[0:NUM_ENTRIES])
plt.ylabel("electricity consumption")
plt.xlabel("time (1pt = 15 mins)")
plt.show()
np.save(os.path.join(DATA_DIR, "LD_250.npy"), np.array(data))

从输出结果可以看出,电力消耗存在明显的每日周期性趋势,因此该问题适合使用有状态模型。根据观察,选择批次大小(BATCH_SIZE)为 96(24 小时内每 15 分钟读取一次的次数)较为合适。

2.1.2 数据处理

加载客户 250 的数据,将其缩放到 (0, 1) 范围,并将输入重塑为三维张量。

from keras.layers.core import Dense
from keras.layers.recurrent import LSTM
from keras.models import Sequential
from sklearn.preprocessing import MinMaxScaler
import numpy as np
import math
import os

DATA_DIR = "../data"
data = np.load(os.path.join(DATA_DIR, "LD_250.npy"))
data = data.reshape(-1, 1)
scaler = MinMaxScaler(feature_range=(0, 1), copy=False)
data = scaler.fit_transform(data)

NUM_TIMESTEPS = 20
X = np.zeros((data.shape[0], NUM_TIMESTEPS))
Y = np.zeros((data.shape[0], 1))
for i in range(len(data) - NUM_TIMESTEPS - 1):
    X[i] = data[i:i + NUM_TIMESTEPS].T
    Y[i] = data[i + NUM_TIMESTEPS + 1]
# reshape X to three dimensions (samples, timesteps, features)
X = np.expand_dims(X, axis=2)

sp = int(0.7 * len(data))
Xtrain, Xtest, Ytrain, Ytest = X[0:sp], X[sp:], Y[0:sp], Y[sp:]
print(Xtrain.shape, Xtest.shape, Ytrain.shape, Ytest.shape)
2.1.3 模型定义

分别定义无状态和有状态的 LSTM 模型。

NUM_TIMESTEPS = 20
HIDDEN_SIZE = 10
BATCH_SIZE = 96 # 24 hours (15 min intervals)

# 无状态模型
model = Sequential()
model.add(LSTM(HIDDEN_SIZE, input_shape=(NUM_TIMESTEPS, 1), 
    return_sequences=False))
model.add(Dense(1))

# 有状态模型
model = Sequential()
model.add(LSTM(HIDDEN_SIZE, stateful=True,
    batch_input_shape=(BATCH_SIZE, NUM_TIMESTEPS, 1), 
    return_sequences=False))
model.add(Dense(1))

model.compile(loss="mean_squared_error", optimizer="adam",
    metrics=["mean_squared_error"])
2.1.4 模型训练

无状态模型的训练代码:

BATCH_SIZE = 96 # 24 hours (15 min intervals)
# 无状态
model.fit(Xtrain, Ytrain, epochs=NUM_EPOCHS, batch_size=BATCH_SIZE,
    validation_data=(Xtest, Ytest),
    shuffle=False)

有状态模型的训练代码:

BATCH_SIZE = 96 # 24 hours (15 min intervals)
# 有状态
# need to make training and test data to multiple of BATCH_SIZE
train_size = (Xtrain.shape[0] // BATCH_SIZE) * BATCH_SIZE
test_size = (Xtest.shape[0] // BATCH_SIZE) * BATCH_SIZE
Xtrain, Ytrain = Xtrain[0:train_size], Ytrain[0:train_size]
Xtest, Ytest = Xtest[0:test_size], Ytest[0:test_size]
print(Xtrain.shape, Xtest.shape, Ytrain.shape, Ytest.shape)
for i in range(NUM_EPOCHS):
    print("Epoch {:d}/{:d}".format(i+1, NUM_EPOCHS))
    model.fit(Xtrain, Ytrain, batch_size=BATCH_SIZE, epochs=1,
        validation_data=(Xtest, Ytest),
        shuffle=False)
    model.reset_states()
2.1.5 模型评估
score, _ = model.evaluate(Xtest, Ytest, batch_size=BATCH_SIZE)
rmse = math.sqrt(score)
print("MSE: {:.3f}, RMSE: {:.3f}".format(score, rmse))

实验结果表明,有状态模型的性能略优于无状态模型。无状态模型的误差率约为 6.2%,有状态模型的误差率约为 5.9%,即它们的准确率分别约为 93.8% 和 94.1%。

2.2 RNN 其他变体

RNN 是一个活跃的研究领域,许多研究人员提出了一些特定用途的变体。

  • 添加窥视孔连接的 LSTM 变体 :允许门层查看单元状态,由 Gers 和 Schmidhuber 在 2002 年提出。
  • 耦合遗忘和输出门的 LSTM 变体 :这种变体最终演变成了门控循环单元(GRU),它将遗忘和获取信息的决策一起做出,新信息会替换被遗忘的信息。

Keras 仅提供了三种基本变体,即 SimpleRNN、LSTM 和 GRU 层。不过,这通常足以解决大多数问题。如果需要构建自定义层,可以使用 Keras 构建自定义层,也可以使用开源框架 recurrent shop(https://github.com/datalogai/recurrentshop)来构建复杂的循环神经网络。

以下是 RNN 相关内容的总结表格:
| 类型 | 特点 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- |
| 双向 RNN | 两个 RNN 以相反方向读取输入,输出基于两个 RNN 隐藏状态 | 增加训练数据,对序列首尾同等重视 | 计算复杂度增加 |
| 有状态 RNN | 跨批次保持状态 | 减小网络规模,降低训练时间 | 需要合适批次大小,不能打乱数据 |
| 添加窥视孔连接 LSTM | 门层可查看单元状态 | 可能提高时间精度建模能力 | 实现复杂度增加 |
| 耦合遗忘和输出门 LSTM(GRU) | 遗忘和获取信息决策一起做出 | 简化结构,减少参数 | 可能在某些复杂任务表现稍弱 |

下面是一个简单的流程图,展示有状态 LSTM 预测电力消耗的流程:

graph TD;
    A[数据准备] --> B[数据处理];
    B --> C[模型定义];
    C --> D[模型训练];
    D --> E[模型评估];

3. 其他深度学习模型

3.1 回归网络

在监督机器学习中,分类和回归是两个主要的子类别。之前讨论的大多是分类网络,而回归网络则是预测连续值,而非类别。例如,在前面有状态和无状态 RNN 的讨论中,预测电力消耗就是一个回归问题。

下面以预测大气中苯含量为例,展示回归网络的应用。虽然很多回归问题可以使用分类模型轻松解决,但这里我们专注于回归网络的构建。

3.1.1 回归网络特点
  • 输出为连续值:与分类网络输出类别不同,回归网络输出一个具体的数值。
  • 评估指标:常用均方误差(MSE)等作为评估指标,而不是分类中的准确率。

3.2 自编码器(Autoencoders)

自编码器是一种无监督(更准确地说是自监督)模型,用于从无标签数据中学习数据的结构。它的目标是将输入数据编码为低维表示,然后再将其解码回原始数据。

3.2.1 自编码器工作原理
  • 编码阶段 :将输入数据压缩为低维向量。
  • 解码阶段 :将低维向量重建为原始数据。
3.2.2 自编码器应用示例

构建句子的紧凑向量表示。通过训练自编码器,可以将长句子转换为固定长度的向量,便于后续处理。

3.3 复杂网络的组合

可以将之前介绍的网络组合成更大的计算图,以实现一些顺序模型无法单独完成的自定义目标。这些网络可能有多个输入和输出,并与外部组件连接。

3.3.1 组合网络示例 - 问答系统

为了实现问答系统,可以组合多个网络模块,例如:
- 问题编码器:将问题转换为向量表示。
- 文档编码器:将文档转换为向量表示。
- 匹配模块:计算问题和文档的匹配度。
- 答案生成模块:根据匹配结果生成答案。

3.4 Keras 后端 API

Keras 后端 API 允许我们构建自定义组件,以扩展 Keras 的功能。通过使用后端 API,可以实现一些复杂的操作,例如自定义损失函数、自定义层等。

3.4.1 使用后端 API 构建自定义组件步骤
  1. 导入后端 API :根据使用的后端(如 TensorFlow、Theano 等)导入相应的 API。
  2. 定义自定义组件 :使用后端 API 定义自定义损失函数、层等。
  3. 集成到模型中 :将自定义组件集成到 Keras 模型中。

3.5 生成模型

生成模型是一类不需要标签的模型,它通过学习现有对象的分布,然后从该分布中采样生成新的数据。

3.5.1 生成模型示例 - 图像特效

可以利用在 ImageNet 数据上预训练的 VGG - 16 网络,学习数据分布,从而创建有趣的视觉效果。

以下是不同类型模型的对比表格:
| 模型类型 | 输入数据 | 输出类型 | 应用场景 |
| ---- | ---- | ---- | ---- |
| 回归网络 | 特征数据 | 连续值 | 预测电力消耗、大气苯含量等 |
| 自编码器 | 无标签数据 | 重建的输入数据 | 数据降维、特征提取 |
| 组合网络 | 多个输入数据 | 自定义输出 | 问答系统、复杂任务处理 |
| 生成模型 | 现有对象数据 | 类似的新数据 | 图像特效、文本生成 |

下面是一个流程图,展示生成模型的工作流程:

graph TD;
    A[现有对象数据] --> B[学习数据分布];
    B --> C[采样生成新数据];

4. 总结

本文介绍了多种深度学习模型,包括双向循环神经网络、有状态循环神经网络及其变体,以及回归网络、自编码器、组合网络、Keras 后端 API 和生成模型。

  • 双向 RNN 通过相反方向读取输入,增加训练数据,对序列首尾同等重视。
  • 有状态 RNN 跨批次保持状态,可减小网络规模和降低训练时间,但需要合适的批次大小和数据顺序。
  • 回归网络用于预测连续值,评估指标常用均方误差。
  • 自编码器从无标签数据中学习数据结构,可用于数据降维和特征提取。
  • 组合网络可实现复杂的自定义目标,有多个输入和输出。
  • Keras 后端 API 可扩展 Keras 功能。
  • 生成模型学习数据分布,生成类似的新数据。

通过对这些模型的学习和应用,可以解决不同领域的复杂问题,推动深度学习技术的发展。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值