TensorFlow 1.x 深度学习秘籍:6~10(1)

本文涉及的产品
NLP自然语言处理_高级版,每接口累计50万次
文本翻译,文本翻译 100万字符
NLP自然语言处理_基础版,每接口每天50万次
简介: TensorFlow 1.x 深度学习秘籍:6~10

六、循环神经网络

在本章中,我们将介绍一些涵盖以下主题的秘籍:

  • 神经机器翻译-训练 seq2seq RNN
  • 神经机器翻译-推理 seq2seq RNN
  • 您只需要关注-seq2seq RNN 的另一个示例
  • 通过 RNN 学习写作莎士比亚
  • 学习使用 RNN 预测未来的比特币价值
  • 多对一和多对多 RNN 示例

介绍

在本章中,我们将讨论循环神经网络RNN)如何在保持顺序顺序重要的领域中用于深度学习。 我们的注意力将主要集中在文本分析和自然语言处理NLP)上,但我们还将看到用于预测比特币价值的序列示例。

通过采用基于时间序列的模型,可以描述许多实时情况。 例如,如果您考虑编写文档,则单词的顺序很重要,而当前单词肯定取决于先前的单词。 如果我们仍然专注于文本编写,很明显单词中的下一个字符取决于前一个字符(例如quick brown字符的下一个字母很有可能将会是字母fox),如下图所示。 关键思想是在给定当前上下文的情况下生成下一个字符的分布,然后从该分布中采样以生成下一个候选字符:

The quick brown fox句子进行预测的例子

一个简单的变体是存储多个预测,因此创建一棵可能的扩展树,如下图所示:

The quick brown fox句子的预测树的示例

但是,基于序列的模型可以在大量其他域中使用。 在音乐中,乐曲中的下一个音符肯定取决于前一个音符,而在视频中,电影中的下一个帧必定与前一帧有关。 此外,在某些情况下,当前的视频帧,单词,字符或音符不仅取决于前一个,而且还取决于后一个。

可以使用 RNN 描述基于时间序列的模型,其中对于给定输入X[i],时间为i,产生输出Y[i],将时间[0,i-1]的以前状态的记忆反馈到网络。 反馈先前状态的想法由循环循环描述,如下图所示:

反馈示例

循环关系可以方便地通过展开网络来表示,如下图所示:

展开循环单元的例子

最简单的 RNN 单元由简单的 tanh 函数(双曲正切函数)组成,如下图所示:

个简单的 tanh 单元的例子

梯度消失和爆炸

训练 RNN 十分困难,因为存在两个稳定性问题。 由于反馈回路的缘故,梯度可能会迅速发散到无穷大,或者它可能会迅速发散到 0。在两种情况下,如下图所示,网络将停止学习任何有用的东西。 可以使用基于梯度修剪的相对简单的解决方案来解决梯度爆炸的问题。 梯度消失的问题更难解决,它涉及更复杂的 RNN 基本单元的定义,例如长短期记忆LSTM)或门控循环单元GRU)。 让我们首先讨论梯度爆炸和梯度裁剪:

梯度示例

梯度裁剪包括对梯度施加最大值,以使其无法无限增长。 下图所示的简单解决方案为梯度爆炸问题提供了简单的解决方案

梯度裁剪的例子

解决梯度消失的问题需要一种更复杂的内存模型,该模型可以选择性地忘记先前的状态,只记住真正重要的状态。 考虑下图,输入以[0,1]中的概率p写入存储器M中,并乘以加权输入。

以类似的方式,以[0,1]中的概率p读取输出,将其乘以加权输出。 还有一种可能性用来决定要记住或忘记的事情:

存储单元的一个例子

长短期记忆(LSTM)

LSTM 网络可以控制何时让输入进入神经元,何时记住在上一个时间步中学到的内容以及何时让输出传递到下一个时间戳。 所有这些决定都是自调整的,并且仅基于输入。 乍一看,LSTM 看起来很难理解,但事实并非如此。 让我们用下图来说明它是如何工作的:

LSTM 单元的一个例子

首先,我们需要一个逻辑函数σ(请参见第 2 章,“回归”)来计算介于 0 和 1 之间的值,并控制哪些信息流过 LSTM 门。 请记住,逻辑函数是可微的,因此允许反向传播。 然后,我们需要一个运算符,它采用两个相同维的矩阵并生成另一个矩阵,其中每个元素ij是原始两个矩阵的元素ij的乘积。 同样,我们需要一个运算符,它采用两个相同维度的矩阵并生成另一个矩阵,其中每个元素ij是原始两个矩阵的元素ij之和。 使用这些基本块,我们考虑时间i处的输入X[i],并将其与上一步中的输出Y[i-1]并置。

方程f[t] = σ(W[f] · [y[i-1], x[t]] + b[f])实现了控制激活门的逻辑回归,并用于确定应从先前候选值C[i-1]获取多少信息。 传递给下一个候选值C[i](此处W[f]b[f]矩阵和用于逻辑回归的偏差)。如果 Sigmoid 输出为 1,则表示不要忘记先前的单元格状态C[i-1];如果输出 0, 这将意味着忘记先前的单元状态C[i-1](0, 1)中的任何数字都将表示要传递的信息量。

然后我们有两个方程:s[i] = σ(W[s] · [Y[i-1], x[i]] + b[s]),用于通过控制由当前单元产生的多少信息(Ĉ[i] = tanh(W [C] · [Y[i-1], X[i] + b[c]))应该通过运算符添加到下一个候选值C[i]中,根据上图中表示的方案。

为了实现与运算符所讨论的内容,我们需要另一个方程,其中进行实际的加法+和乘法*C[i] = f[t] * C[i-1] + s[i] * Ĉ[i]

最后,我们需要确定当前单元格的哪一部分应发送到Y[i]输出。 这很简单:我们再进行一次逻辑回归方程,然后通过运算来控制应使用哪一部分候选值输出。 在这里,有一点值得关注,使用 tanh 函数将输出压缩为[-1, 1]。 最新的步骤由以下公式描述:

现在,我了解到这看起来像很多数学运算,但有两个好消息。 首先,如果您了解我们想要实现的目标,那么数学部分并不是那么困难。 其次,您可以将 LSTM 单元用作标准 RNN 单元的黑盒替代,并立即获得解决梯度消失问题的好处。 因此,您实际上不需要了解所有数学知识。 您只需从库中获取 TensorFlow LSTM 实现并使用它即可。

门控循环单元(GRU)和窥孔 LSTM

近年来提出了许多 LSTM 单元的变体。 其中两个真的很受欢迎。 窥孔 LSTM 允许栅极层查看单元状态,如下图虚线所示,而门控循环单元GRU)将隐藏状态和单元状态和合并为一个单一的信息渠道。

同样,GRU 和 Peephole LSTM 都可以用作标准 RNN 单元的黑盒插件,而无需了解基础数学。 这两个单元都可用于解决梯度消失的问题,并可用于构建深度神经网络:

标准 LSTM,PeepHole LSTM 和 GRU 的示例

向量序列的运算

使 RNN 真正强大的是能够对向量序列进行操作的能力,其中 RNN 的输入和/或 RNN 的输出都可以是序列。 下图很好地表示了这一点,其中最左边的示例是传统的(非循环)网络,其后是带有输出序列的 RNN,然后是带有输入序列的 RNN,再是带有序列的 RNN 在不同步序列的输入和输出中,然后是在序列同步的输入和输出中具有序列的 RNN:

RNN 序列的一个例子

机器翻译是输入和输出中不同步序列的一个示例:网络将输入文本作为序列读取,在读取全文之后,会输出目标语言

视频分类是输入和输出中同步序列的示例:视频输入是帧序列,并且对于每个帧,输出中都提供了分类标签。

如果您想了解有关 RNN 有趣应用的更多信息,则必须阅读 Andrej Karpathy 发布的博客。 他训练了网络,以莎士比亚的风格撰写论文(用 Karpathy 的话说:几乎不能从实际的莎士比亚中识别出这些样本),撰写有关虚构主题的现实 Wikipedia 文章,撰写关于愚蠢和不现实问题的现实定理证明(用 Karpathy 的话:更多的幻觉代数几何),并写出现实的 Linux 代码片段(用 Karpathy 的话:他首先建模逐个字符地列举 GNU 许可证,其中包括一些示例,然后生成一些宏,然后深入研究代码)。

以下示例摘自这个页面

用 RNN 生成的文本示例

神经机器翻译 – 训练 seq2seq RNN

序列到序列(seq2seq)是 RNN 的一种特殊类型,已成功应用于神经机器翻译,文本摘要和语音识别中。 在本秘籍中,我们将讨论如何实现神经机器翻译,其结果与 Google 神经机器翻译系统。 关键思想是输入整个文本序列,理解整个含义,然后将翻译输出为另一个序列。 读取整个序列的想法与以前的架构大不相同,在先前的架构中,将一组固定的单词从一种源语言翻译成目标语言。

本节的灵感来自 Minh-Thang Luong 的 2016 年博士学位论文《神经机器翻译》。 第一个关键概念是编码器-解码器架构的存在,其中编码器将源句子转换为代表含义的向量。 然后,此向量通过解码器以产生翻译。 编码器和解码器都是 RNN,它们可以捕获语言中的长期依赖关系,例如性别协议和语法结构,而无需先验地了解它们,并且不需要跨语言进行 1:1 映射。 这是一种强大的功能,可实现非常流畅的翻译:

编解码器的示例

让我们看一个 RNN 的示例,该语句将She loves cute cats翻译成Elle Aime les chat Mignons

有两种 RNN:一种充当编码器,另一种充当解码器。 源句She loves cute cats后跟一个分隔符-目标句是Elle aime les chats mignons。 这两个连接的句子在输入中提供给编码器进行训练,并且解码器将生成目标目标。 当然,我们需要像这样的多个示例来获得良好的训练:

NMT 序列模型的示例

现在,我们可以拥有许多 RNN 变体。 让我们看看其中的一些:

  • RNN 可以是单向或双向的。 后者将捕捉双方的长期关系。
  • RNN 可以具有多个隐藏层。 选择是关于优化的问题:一方面,更深的网络可以学到更多;另一方面,更深的网络可以学到更多。 另一方面,可能需要很长的时间来训练并且可能会过头。
  • RNN 可以具有一个嵌入层,该层将单词映射到一个嵌入空间中,在该空间中相似的单词恰好被映射得非常近。
  • RNNs 可以使用简单的或者循环的单元,或 LSTM,或窥视孔 LSTM,或越冬。

仍然参考博士学位论文《神经机器翻译》,我们可以使用嵌入层来将输入语句放入嵌入空间。 然后,有两个 RNN 粘在一起——源语言的编码器和目标语言的解码器。 如您所见,存在多个隐藏层,并且有两个流程:前馈垂直方向连接这些隐藏层,水平方向是将知识从上一步转移到下一层的循环部分:

神经机器翻译的例子

在本秘籍中,我们使用 NMT(神经机器翻译),这是一个可在 TensorFlow 顶部在线获得的翻译演示包。

准备

NMT 可在这个页面上找到,并且代码在 GitHub 上。

操作步骤

我们按以下步骤进行:

  1. 从 GitHub 克隆 NMT:
git clone https://github.com/tensorflow/nmt/
  1. 下载训练数据集。 在这种情况下,我们将使用训练集将越南语翻译为英语。 其他数据集可从这里获取其他语言,例如德语和捷克语:
nmt/scripts/download_iwslt15.sh /tmp/nmt_data
  1. 考虑这里,我们将定义第一个嵌入层。 嵌入层接受输入,词汇量 V 和输出嵌入空间的所需大小。 词汇量使得仅考虑 V 中最频繁的单词进行嵌入,而所有其他单词都映射到一个常见的未知项。 在我们的例子中,输入是主要时间的,这意味着最大时间是第一个输入参数
# Embedding
 embedding_encoder = variable_scope.get_variable(
 "embedding_encoder", [src_vocab_size, embedding_size], ...)
 # Look up embedding:
 # encoder_inputs: [max_time, batch_size]
 # encoder_emb_inp: [max_time, batch_size, embedding_size]
 encoder_emb_inp = embedding_ops.embedding_lookup(
 embedding_encoder, encoder_inputs)
  1. 仍然参考这里,我们定义了一个简单的编码器,它使用tf.nn.rnn_cell.BasicLSTMCell(num_units)作为基本 RNN 单元。 这非常简单,但是要注意,给定基本的 RNN 单元,我们使用tf.nn.dynamic_rnn创建 RNN:
# Build RNN cell
 encoder_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units)
 # Run Dynamic RNN
 # encoder_outpus: [max_time, batch_size, num_units]
 # encoder_state: [batch_size, num_units]
 encoder_outputs, encoder_state = tf.nn.dynamic_rnn(
 encoder_cell, encoder_emb_inp,
 sequence_length=source_sequence_length, time_major=True)
  1. 之后,我们需要定义解码器。 因此,第一件事是拥有一个带有tf.nn.rnn_cell.BasicLSTMCell的基本 RNN 单元,然后将其用于创建一个基本采样解码器tf.contrib.seq2seq.BasicDecoder,该基本采样解码器将用于与解码器tf.contrib.seq2seq.dynamic_decode进行动态解码:
# Build RNN cell
 decoder_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units)
# Helper
 helper = tf.contrib.seq2seq.TrainingHelper(
 decoder_emb_inp, decoder_lengths, time_major=True)
 # Decoder
 decoder = tf.contrib.seq2seq.BasicDecoder(
 decoder_cell, helper, encoder_state,
 output_layer=projection_layer)
 # Dynamic decoding
 outputs, _ = tf.contrib.seq2seq.dynamic_decode(decoder, ...)
 logits = outputs.rnn_output
  1. 网络的最后一个阶段是 softmax 密集阶段,用于将顶部隐藏状态转换为对率向量:
projection_layer = layers_core.Dense(
 tgt_vocab_size, use_bias=False)
  1. 当然,我们需要定义交叉熵函数和训练阶段使用的损失:
crossent = tf.nn.sparse_softmax_cross_entropy_with_logits(
 labels=decoder_outputs, logits=logits)
 train_loss = (tf.reduce_sum(crossent * target_weights) /
 batch_size)
  1. 下一步是定义反向传播所需的步骤,并使用适当的优化器(在本例中为 Adam)。 请注意,梯度已被裁剪,Adam 使用预定义的学习率:
# Calculate and clip gradients
 params = tf.trainable_variables()
 gradients = tf.gradients(train_loss, params)
 clipped_gradients, _ = tf.clip_by_global_norm(
 gradients, max_gradient_norm)
# Optimization
 optimizer = tf.train.AdamOptimizer(learning_rate)
 update_step = optimizer.apply_gradients(
 zip(clipped_gradients, params))
  1. 现在,我们可以运行代码并了解不同的执行步骤。 首先,创建训练图。 然后,训练迭代开始。 用于评估的度量标准是双语评估研究BLEU)。 此度量标准是评估已从一种自然语言机器翻译成另一种自然语言的文本质量的标准。 质量被认为是机器与人工输出之间的对应关系。 如您所见,该值随时间增长:
python -m nmt.nmt --src=vi --tgt=en --vocab_prefix=/tmp/nmt_data/vocab --train_prefix=/tmp/nmt_data/train --dev_prefix=/tmp/nmt_data/tst2012 --test_prefix=/tmp/nmt_data/tst2013 --out_dir=/tmp/nmt_model --num_train_steps=12000 --steps_per_stats=100 --num_layers=2 --num_units=128 --dropout=0.2 --metrics=bleu
# Job id 0
[...]
# creating train graph ...
num_layers = 2, num_residual_layers=0
cell 0 LSTM, forget_bias=1 DropoutWrapper, dropout=0.2 DeviceWrapper, device=/gpu:0
cell 1 LSTM, forget_bias=1 DropoutWrapper, dropout=0.2 DeviceWrapper, device=/gpu:0
cell 0 LSTM, forget_bias=1 DropoutWrapper, dropout=0.2 DeviceWrapper, device=/gpu:0
cell 1 LSTM, forget_bias=1 DropoutWrapper, dropout=0.2 DeviceWrapper, device=/gpu:0
start_decay_step=0, learning_rate=1, decay_steps 10000,decay_factor 0.98
[...]
# Start step 0, lr 1, Thu Sep 21 12:57:18 2017
# Init train iterator, skipping 0 elements
global step 100 lr 1 step-time 1.65s wps 3.42K ppl 1931.59 bleu 0.00
global step 200 lr 1 step-time 1.56s wps 3.59K ppl 690.66 bleu 0.00
[...]
global step 9100 lr 1 step-time 1.52s wps 3.69K ppl 39.73 bleu 4.89
global step 9200 lr 1 step-time 1.52s wps 3.72K ppl 40.47 bleu 4.89
global step 9300 lr 1 step-time 1.55s wps 3.62K ppl 40.59 bleu 4.89
[...]
# External evaluation, global step 9000
decoding to output /tmp/nmt_model/output_dev.
done, num sentences 1553, time 17s, Thu Sep 21 17:32:49 2017.
bleu dev: 4.9
saving hparams to /tmp/nmt_model/hparams
# External evaluation, global step 9000
decoding to output /tmp/nmt_model/output_test.
done, num sentences 1268, time 15s, Thu Sep 21 17:33:06 2017.
bleu test: 3.9
saving hparams to /tmp/nmt_model/hparams
[...]
global step 9700 lr 1 step-time 1.52s wps 3.71K ppl 38.01 bleu 4.89

工作原理

所有上述代码已在这个页面中定义。 关键思想是将两个 RNN 打包在一起。 第一个是编码器,它在嵌入空间中工作,非常紧密地映射相似的单词。 编码器理解训练示例的含义,并产生张量作为输出。 然后只需将编码器的最后一个隐藏层连接到解码器的初始层,即可将该张量传递给解码器。 注意力学习是由于我们基于与labels=decoder_outputs的交叉熵的损失函数而发生的。

该代码学习如何翻译,并通过 BLEU 度量标准通过迭代跟踪进度,如下图所示:

Tensorboard 中的 BLEU 指标示例

神经机器翻译 – 用 seq2seq RNN 推理

在此秘籍中,我们使用先前秘籍的结果将源语言转换为目标语言。 这个想法非常简单:给源语句提供两个组合的 RNN(编码器+解码器)作为输入。 句子一结束,解码器将产生对率值,我们贪婪地产生与最大值关联的单词。 例如,从解码器产生单词moi作为第一个令牌,因为该单词具有最大对率值。 之后,会产生单词suis,依此类推:

具有概率的 NM 序列模型的示例

使用解码器的输出有多种策略:

  • 贪婪:产生对应最大对率的字
  • 采样:通过对产生的对率进行采样来产生单词
  • 集束搜索:一个以上的预测,因此创建了可能的扩展树

操作步骤

我们按以下步骤进行:

  1. 定义用于对解码器进行采样的贪婪策略。 这很容易,因为我们可以使用tf.contrib.seq2seq.GreedyEmbeddingHelper中定义的库。 由于我们不知道目标句子的确切长度,因此我们将启发式方法限制为最大长度为源句子长度的两倍:
# Helper
 helper = tf.contrib.seq2seq.GreedyEmbeddingHelper(
 embedding_decoder,
 tf.fill([batch_size], tgt_sos_id), tgt_eos_id)
 # Decoder
 decoder = tf.contrib.seq2seq.BasicDecoder(
 decoder_cell, helper, encoder_state,
 output_layer=projection_layer)
 # Dynamic decoding
 outputs, _ = tf.contrib.seq2seq.dynamic_decode(
 decoder, maximum_iterations=maximum_iterations)
 translations = outputs.sample_id
maximum_iterations = tf.round(tf.reduce_max(source_sequence_length) * 2)
  1. 现在,我们可以运行网络,输入一个从未见过的句子(inference_input_file=/tmp/my_infer_file),然后让网络翻译结果(inference_output_file=/tmp/nmt_model/output_infer):
python -m nmt.nmt \
 --out_dir=/tmp/nmt_model \
 --inference_input_file=/tmp/my_infer_file.vi \
 --inference_output_file=/tmp/nmt_model/output_infer

工作原理

将两个 RNN 打包在一起,以形成编码器-解码器 RNN 网络。 解码器产生对率,然后将其贪婪地转换为目标语言的单词。 例如,此处显示了从越南语到英语的自动翻译:

  • 用英语输入的句子:小时候,我认为朝鲜是世界上最好的国家,我经常唱歌&。 我们没有什么可嫉妒的。
  • 翻译成英语的输出句子:当我非常好时,我将去了解最重要的事情,而我不确定该说些什么。

您只需要注意力 – seq2seq RNN 的另一个示例

在本秘籍中,我们介绍了注意力方法(Dzmitry Bahdanau,Kyunghyun Cho 和 Yoshua Bengio,ICLR 2015),这是神经网络翻译的最新解决方案。 ,它包括在编码器和解码器 RNN 之间添加其他连接。 实际上,仅将解码器与编码器的最新层连接会带来信息瓶颈,并且不一定允许通过先前的编码器层获取的信息通过。 下图说明了采用的解决方案:

NMT 注意力模型的示例

需要考虑三个方面:

  • 首先,将当前目标隐藏状态与所有先前的源状态一起使用以得出注意力权重,该注意力权重用于或多或少地关注序列中先前看到的标记
  • 其次,创建上下文向量以汇总注意力权重的结果
  • 第三,将上下文向量与当前目标隐藏状态组合以获得注意力向量

操作步骤

我们按以下步骤进行:

  1. 使用库tf.contrib.seq2seq.LuongAttention定义注意力机制,该库实现了 Minh-Thang Luong,Hieu Pham 和 Christopher D. Manning(2015 年)在《基于注意力的神经机器翻译有效方法》中定义的注意力模型:
# attention_states: [batch_size, max_time, num_units]
 attention_states = tf.transpose(encoder_outputs, [1, 0, 2])
 # Create an attention mechanism
 attention_mechanism = tf.contrib.seq2seq.LuongAttention(
 num_units, attention_states,
 memory_sequence_length=source_sequence_length)
  1. 通过注意力包装器,将定义的注意力机制用作解码器单元周围的包装器:
decoder_cell = tf.contrib.seq2seq.AttentionWrapper(
 decoder_cell, attention_mechanism,
 attention_layer_size=num_units)
  1. 运行代码以查看结果。 我们立即注意到,注意力机制在 BLEU 得分方面产生了显着改善:
python -m nmt.nmt \
> --attention=scaled_luong \
> --src=vi --tgt=en \
> --vocab_prefix=/tmp/nmt_data/vocab \
> --train_prefix=/tmp/nmt_data/train \
> --dev_prefix=/tmp/nmt_data/tst2012 \
> --test_prefix=/tmp/nmt_data/tst2013 \
> --out_dir=/tmp/nmt_attention_model \
> --num_train_steps=12000 \
> --steps_per_stats=100 \
> --num_layers=2 \
> --num_units=128 \
> --dropout=0.2 \
> --metrics=bleu
[...]
# Start step 0, lr 1, Fri Sep 22 22:49:12 2017
# Init train iterator, skipping 0 elements
global step 100 lr 1 step-time 1.71s wps 3.23K ppl 15193.44 bleu 0.00
[...]
# Final, step 12000 lr 0.98 step-time 1.67 wps 3.37K ppl 14.64, dev ppl 14.01, dev bleu 15.9, test ppl 12.58, test bleu 17.5, Sat Sep 23 04:35:42 2017
# Done training!, time 20790s, Sat Sep 23 04:35:42 2017.
# Start evaluating saved best models.
[..]
loaded infer model parameters from /tmp/nmt_attention_model/best_bleu/translate.ckpt-12000, time 0.06s
# 608
src: nhưng bạn biết điều gì không ?
ref: But you know what ?
nmt: But what do you know ?
[...]
# Best bleu, step 12000 step-time 1.67 wps 3.37K, dev ppl 14.01, dev bleu 15.9, test ppl 12.58, test bleu 17.5, Sat Sep 23 04:36:35 2017

工作原理

注意是一种机制,该机制使用由编码器 RNN 的内部状态获取的信息,并将该信息与解码器的最终状态进行组合。 关键思想是,通过这种方式,有可能或多或少地关注源序列中的某些标记。 下图显示了 BLEU 得分,引起了关注。

我们注意到,相对于我们第一个秘籍中未使用任何注意力的图表而言,它具有明显的优势:

Tensorboard 中注意力的 BLEU 指标示例

更多

值得记住的是 seq2seq 不仅可以用于机器翻译。 让我们看一些例子:

  • Lukasz Kaiser 在作为外语的语法中,使用 seq2seq 模型来构建选区解析器。 选区分析树将文本分为多个子短语。 树中的非终结符是短语的类型,终结符是句子中的单词,并且边缘未标记。
  • seq2seq 的另一个应用是 SyntaxNet,又名 Parsey McParserFace(语法分析器),它是许多 NLU 系统中的关键第一组件。 给定一个句子作为输入,它将使用描述单词的句法特征的词性POS)标签标记每个单词,并确定句子中单词之间的句法关系,在依存关系分析树中表示。 这些句法关系与所讨论句子的潜在含义直接相关。

下图使我们对该概念有了一个很好的了解:

SyntaxNet 的一个例子

通过 RNN 学习写作莎士比亚

在本秘籍中,我们将学习如何生成与威廉·莎士比亚(William Shakespeare)相似的文本。 关键思想很简单:我们将莎士比亚写的真实文本作为输入,并将其作为输入 RNN 的输入,该 RNN 将学习序列。 然后将这种学习用于生成新文本,该文本看起来像最伟大的作家用英语撰写的文本。

为了简单起见,我们将使用框架 TFLearn,它在 TensorFlow 上运行。 此示例是标准分发版的一部分,可从以下位置获得。开发的模型是 RNN 字符级语言模型,其中考虑的序列是字符序列而不是单词序列。

操作步骤

我们按以下步骤进行:

  1. 使用pip安装 TFLearn:
pip install -I tflearn
  1. 导入许多有用的模块并下载一个由莎士比亚撰写的文本示例。 在这种情况下,我们使用这个页面中提供的一种:
import os
import pickle
from six.moves import urllib
import tflearn
from tflearn.data_utils import *
path = "shakespeare_input.txt"
char_idx_file = 'char_idx.pickle'
if not os.path.isfile(path): urllib.request.urlretrieve("https://raw.githubusercontent.com/tflearn/tflearn.github.io/master/resources/shakespeare_input.txt", path)
  1. 使用string_to_semi_redundant_sequences()将输入的文本转换为向量,并返回解析的序列和目标以及相关的字典,该函数将返回一个元组(输入,目标,字典):
maxlen = 25
char_idx = None
if os.path.isfile(char_idx_file):
print('Loading previous char_idx')
char_idx = pickle.load(open(char_idx_file, 'rb'))
X, Y, char_idx = \
textfile_to_semi_redundant_sequences(path, seq_maxlen=maxlen, redun_step=3,
pre_defined_char_idx=char_idx)
pickle.dump(char_idx, open(char_idx_file,'wb'))
  1. 定义一个由三个 LSTM 组成的 RNN,每个 LSTM 都有 512 个节点,并返回完整序列,而不是仅返回最后一个序列输出。 请注意,我们使用掉线模块连接 LSTM 模块的可能性为 50%。 最后一层是密集层,其应用 softmax 的长度等于字典大小。 损失函数为categorical_crossentropy,优化器为 Adam:
g = tflearn.input_data([None, maxlen, len(char_idx)])
g = tflearn.lstm(g, 512, return_seq=True)
g = tflearn.dropout(g, 0.5)
g = tflearn.lstm(g, 512, return_seq=True)
g = tflearn.dropout(g, 0.5)
g = tflearn.lstm(g, 512)
g = tflearn.dropout(g, 0.5)
g = tflearn.fully_connected(g, len(char_idx), activation='softmax')
g = tflearn.regression(g, optimizer='adam', loss='categorical_crossentropy',
learning_rate=0.001)
  1. 给定步骤 4 中定义的网络,我们现在可以使用库flearn.models.generator.SequenceGeneratornetworkdictionary=char_idx, seq_maxlen=maxleclip_gradients=5.0, checkpoint_path='model_shakespeare')生成序列:
m = tflearn.SequenceGenerator(g, dictionary=char_idx,
seq_maxlen=maxlen,
clip_gradients=5.0,
checkpoint_path='model_shakespeare')
  1. 对于 50 次迭代,我们从输入文本中获取随机序列,然后生成一个新文本。 温度正在控制所创建序列的新颖性; 温度接近 0 看起来像用于训练的样本,而温度越高,新颖性越强:
for i in range(50):
seed = random_sequence_from_textfile(path, maxlen)
m.fit(X, Y, validation_set=0.1, batch_size=128,
n_epoch=1, run_id='shakespeare')
print("-- TESTING...")
print("-- Test with temperature of 1.0 --")
print(m.generate(600, temperature=1.0, seq_seed=seed))
print("-- Test with temperature of 0.5 --")
print(m.generate(600, temperature=0.5, seq_seed=seed))

工作原理

当新的未知或被遗忘的艺术品要归功于作者时,有著名的学者将其与作者的其他作品进行比较。 学者们要做的是在著名作品的文本序列中找到共同的模式,希望在未知作品中找到相似的模式。

这种方法的工作方式相似:RNN 了解莎士比亚作品中最特殊的模式是什么,然后将这些模式用于生成新的,从未见过的文本,这些文本很好地代表了最伟大的英语作者的风格。

让我们看一些执行示例:

python shakespeare.py
Loading previous char_idx
Vectorizing text...
Text total length: 4,573,338
Distinct chars : 67
Total sequences : 1,524,438
---------------------------------
Run id: shakespeare
Log directory: /tmp/tflearn_logs/

第一次迭代

在这里,网络正在学习一些基本结构,包括需要建立有关虚构字符(DIASURYONTHRNTLGIPRMARARILEN)的对话。 但是,英语仍然很差,很多单词不是真正的英语:

---------------------------------
Training samples: 1371994
Validation samples: 152444
--
Training Step: 10719 | total loss: 2.22092 | time: 22082.057s
| Adam | epoch: 001 | loss: 2.22092 | val_loss: 2.12443 -- iter: 1371994/1371994
-- TESTING...
-- Test with temperature of 1.0 --
'st thou, malice?
If thou caseghough memet oud mame meard'ke. Afs weke wteak, Dy ny wold' as to of my tho gtroy ard has seve, hor then that wordith gole hie, succ, caight fom?
DIA:
A gruos ceen, I peey
by my
Wiouse rat Sebine would.
waw-this afeean.
SURYONT:
Teeve nourterong a oultoncime bucice'is furtutun
Ame my sorivass; a mut my peant?
Am:
Fe, that lercom ther the nome, me, paatuy corns wrazen meas ghomn'ge const pheale,
As yered math thy vans:
I im foat worepoug and thit mije woml!
HRNTLGIPRMAR:
I'd derfomquesf thiy of doed ilasghele hanckol, my corire-hougangle!
Kiguw troll! you eelerd tham my fom Inow lith a
-- Test with temperature of 0.5 --
'st thou, malice?
If thou prall sit I har, with and the sortafe the nothint of the fore the fir with with the ceme at the ind the couther hit yet of the sonsee in solles and that not of hear fore the hath bur.
ARILEN:
More you a to the mare me peod sore,
And fore string the reouck and and fer to the so has the theat end the dore; of mall the sist he the bot courd wite be the thoule the to nenge ape and this not the the ball bool me the some that dears,
The be to the thes the let the with the thear tould fame boors and not to not the deane fere the womour hit muth so thand the e meentt my to the treers and woth and wi

经过几次迭代

在这里,网络开始学习对话的正确结构,并且使用Well, there shall the things to need the offer to our heartThere is not that be so then to the death To make the body and all the mind这样的句子,书面英语看起来更正确:

---------------------------------
Training samples: 1371994
Validation samples: 152444
--
Training Step: 64314 | total loss: 1.44823 | time: 21842.362s
| Adam | epoch: 006 | loss: 1.44823 | val_loss: 1.40140 -- iter: 1371994/1371994
--
-- Test with temperature of 0.5 --
in this kind.
THESEUS:
There is not that be so then to the death
To make the body and all the mind.
BENEDICK:
Well, there shall the things to need the offer to our heart,
To not are he with him: I have see the hands are to true of him that I am not,
The whom in some the fortunes,
Which she were better not to do him?
KING HENRY VI:
I have some a starter, and and seen the more to be the boy, and be such a plock and love so say, and I will be his entire,
And when my masters are a good virtues,
That see the crown of our worse,
This made a called grace to hear him and an ass,
And the provest and stand,

更多

博客文章循环神经网络的不合理有效性描述了一组引人入胜的示例 RNN 字符级语言模型,包括以下内容:

  • 莎士比亚文本生成类似于此示例
  • Wikipedia 文本生成类似于此示例,但是基于不同的训练文本
  • 代数几何(LaTex)文本生成类似于此示例,但基于不同的训练文本
  • Linux 源代码文本的生成与此示例相似,但是基于不同的训练文本
  • 婴儿命名文本的生成与此示例类似,但是基于不同的训练文本

学习使用 RNN 预测未来的比特币价值

在本秘籍中,我们将学习如何使用 RNN 预测未来的比特币价值。 关键思想是,过去观察到的值的时间顺序可以很好地预测未来的值。 对于此秘籍,我们将使用 MIT 许可下的这个页面上提供的代码。 给定时间间隔的比特币值通过 API 从这里下载。 这是 API 文档的一部分:

We offer historical data from our Bitcoin Price Index through the following endpoint: https://api.coindesk.com/v1/bpi/historical/close.json By default, this will return the previous 31 days' worth of data. This endpoint accepts the following optional parameters: ?index=[USD/CNY]The index to return data for. Defaults to USD. ?currency=<VALUE>The currency to return the data in, specified in ISO 4217 format. Defaults to USD. ?start=<VALUE>&end=<VALUE> Allows data to be returned for a specific date range. Must be listed as a pair of start and end parameters, with dates supplied in the YYYY-MM-DD format, e.g. 2013-09-01 for September 1st, 2013. ?for=yesterday Specifying this will return a single value for the previous day. Overrides the start/end parameter. Sample Request: https://api.coindesk.com/v1/bpi/historical/close.json?start=2013-09-01&end=2013-09-05 Sample JSON Response: {"bpi":{"2013-09-01":128.2597,"2013-09-02":127.3648,"2013-09-03":127.5915,"2013-09-04":120.5738,"2013-09-05":120.5333},"disclaimer":"This data was produced from the CoinDesk Bitcoin Price Index. BPI value data returned as USD.","time":{"updated":"Sep 6, 2013 00:03:00 UTC","updatedISO":"2013-09-06T00:03:00+00:00"}}

操作步骤

这是我们进行秘籍的方法:

  1. 克隆以下 GitHub 存储库。 这是一个鼓励用户尝试使用 seq2seq 神经网络架构的项目:
git clone https://github.com/guillaume-chevalier/seq2seq-signal-prediction.git
  1. 给定前面的存储库,请考虑以下函数,这些函数可加载和标准化 USD 或 EUR 比特币值的比特币历史数据。 这些特征在dataset.py中定义。 训练和测试数据根据 80/20 规则分开。 因此,测试数据的 20% 是最新的历史比特币值。 每个示例在特征轴/维度中包含 40 个 USD 数据点,然后包含 EUR 数据。 根据平均值和标准差对数据进行归一化。 函数generate_x_y_data_v4生成大小为batch_size的训练数据(分别是测试数据)的随机样本:
def loadCurrency(curr, window_size):
   """
   Return the historical data for the USD or EUR bitcoin value. Is done with an web API call.
   curr = "USD" | "EUR"
   """
   # For more info on the URL call, it is inspired by :
   # https://github.com/Levino/coindesk-api-node
   r = requests.get(
       "http://api.coindesk.com/v1/bpi/historical/close.json?start=2010-07-17&end=2017-03-03&currency={}".format(
           curr
       )
   )
   data = r.json()
   time_to_values = sorted(data["bpi"].items())
   values = [val for key, val in time_to_values]
   kept_values = values[1000:]
   X = []
   Y = []
   for i in range(len(kept_values) - window_size * 2):
       X.append(kept_values[i:i + window_size])
       Y.append(kept_values[i + window_size:i + window_size * 2])
   # To be able to concat on inner dimension later on:
   X = np.expand_dims(X, axis=2)
   Y = np.expand_dims(Y, axis=2)
   return X, Y
def normalize(X, Y=None):
   """
   Normalise X and Y according to the mean and standard
deviation of the X values only.
   """
   # # It would be possible to normalize with last rather than mean, such as:
   # lasts = np.expand_dims(X[:, -1, :], axis=1)
   # assert (lasts[:, :] == X[:, -1, :]).all(), "{}, {}, {}. {}".format(lasts[:, :].shape, X[:, -1, :].shape, lasts[:, :], X[:, -1, :])
   mean = np.expand_dims(np.average(X, axis=1) + 0.00001, axis=1)
   stddev = np.expand_dims(np.std(X, axis=1) + 0.00001, axis=1)
   # print (mean.shape, stddev.shape)
   # print (X.shape, Y.shape)
   X = X - mean
   X = X / (2.5 * stddev)
   if Y is not None:
       assert Y.shape == X.shape, (Y.shape, X.shape)
       Y = Y - mean
       Y = Y / (2.5 * stddev)
       return X, Y
   return X
def fetch_batch_size_random(X, Y, batch_size):
   """
   Returns randomly an aligned batch_size of X and Y among all examples.
   The external dimension of X and Y must be the batch size
(eg: 1 column = 1 example).
   X and Y can be N-dimensional.
   """
   assert X.shape == Y.shape, (X.shape, Y.shape)
   idxes = np.random.randint(X.shape[0], size=batch_size)
   X_out = np.array(X[idxes]).transpose((1, 0, 2))
   Y_out = np.array(Y[idxes]).transpose((1, 0, 2))
   return X_out, Y_out
X_train = []
Y_train = []
X_test = []
Y_test = []
def generate_x_y_data_v4(isTrain, batch_size):
   """
   Return financial data for the bitcoin.
   Features are USD and EUR, in the internal dimension.
   We normalize X and Y data according to the X only to not
   spoil the predictions we ask for.
   For every window (window or seq_length), Y is the prediction following X.
   Train and test data are separated according to the 80/20
rule.
   Therefore, the 20 percent of the test data are the most
   recent historical bitcoin values. Every example in X contains
   40 points of USD and then EUR data in the feature axis/dimension.
   It is to be noted that the returned X and Y has the same shape
   and are in a tuple.
   """
   # 40 pas values for encoder, 40 after for decoder's predictions.
   seq_length = 40
   global Y_train
   global X_train
   global X_test
   global Y_test
   # First load, with memoization:
   if len(Y_test) == 0:
       # API call:
       X_usd, Y_usd = loadCurrency("USD",
window_size=seq_length)
       X_eur, Y_eur = loadCurrency("EUR",
window_size=seq_length)
       # All data, aligned:
       X = np.concatenate((X_usd, X_eur), axis=2)
       Y = np.concatenate((Y_usd, Y_eur), axis=2)
       X, Y = normalize(X, Y)
       # Split 80-20:
       X_train = X[:int(len(X) * 0.8)]
       Y_train = Y[:int(len(Y) * 0.8)]
       X_test = X[int(len(X) * 0.8):]
       Y_test = Y[int(len(Y) * 0.8):]
   if isTrain:
       return fetch_batch_size_random(X_train, Y_train, batch_size)
   else:
       return fetch_batch_size_random(X_test,  Y_test,  batch_size)
  1. 生成训练,验证和测试数据,并定义许多超参数,例如batch_sizehidden_dim(RNN 中隐藏的神经元的数量)和layers_stacked_count(栈式循环单元的数量)。 此外,定义一些参数以微调优化器,例如优化器的学习率,迭代次数,用于优化器模拟退火的lr_decay,优化器的动量以及避免过拟合的 L2 正则化。 请注意,GitHub 存储库具有默认的batch_size = 5nb_iters = 150,但使用batch_size = 1000nb_iters = 100000获得了更好的结果:
from datasets import generate_x_y_data_v4
generate_x_y_data = generate_x_y_data_v4
import tensorflow as tf  
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
sample_x, sample_y = generate_x_y_data(isTrain=True, batch_size=3)
print("Dimensions of the dataset for 3 X and 3 Y training
examples : ")
print(sample_x.shape)
print(sample_y.shape)
print("(seq_length, batch_size, output_dim)")
print sample_x, sample_y
# Internal neural network parameters
seq_length = sample_x.shape[0]  # Time series will have the same past and future (to be predicted) lenght.
batch_size = 5  # Low value used for live demo purposes - 100 and 1000 would be possible too, crank that up!
output_dim = input_dim = sample_x.shape[-1]  # Output dimension (e.g.: multiple signals at once, tied in time)
hidden_dim = 12  # Count of hidden neurons in the recurrent units.
layers_stacked_count = 2  # Number of stacked recurrent cells, on the neural depth axis.
# Optmizer:
learning_rate = 0.007  # Small lr helps not to diverge during training.
nb_iters = 150  # How many times we perform a training step (therefore how many times we show a batch).
lr_decay = 0.92  # default: 0.9 . Simulated annealing.
momentum = 0.5  # default: 0.0 . Momentum technique in weights update
lambda_l2_reg = 0.003  # L2 regularization of weights - avoids overfitting
  1. 将网络定义为由基本 GRU 单元组成的编码器/解码器。 该网络由layers_stacked_count=2 RNN 组成,我们将使用 TensorBoard 可视化该网络。 请注意,hidden_dim = 12是循环单元中的隐藏神经元:
tf.nn.seq2seq = tf.contrib.legacy_seq2seq
tf.nn.rnn_cell = tf.contrib.rnn
tf.nn.rnn_cell.GRUCell = tf.contrib.rnn.GRUCell
tf.reset_default_graph()
# sess.close()
sess = tf.InteractiveSession()
with tf.variable_scope('Seq2seq'):
   # Encoder: inputs
   enc_inp = [
       tf.placeholder(tf.float32, shape=(None, input_dim), name="inp_{}".format(t))
          for t in range(seq_length)
   ]
   # Decoder: expected outputs
   expected_sparse_output = [
       tf.placeholder(tf.float32, shape=(None, output_dim), name="expected_sparse_output_".format(t))
         for t in range(seq_length)
   ]
   # Give a "GO" token to the decoder.
   # You might want to revise what is the appended value "+ enc_inp[:-1]".
   dec_inp = [ tf.zeros_like(enc_inp[0], dtype=np.float32, name="GO") ] + enc_inp[:-1]
   # Create a `layers_stacked_count` of stacked RNNs (GRU cells here).
   cells = []
   for i in range(layers_stacked_count):
       with tf.variable_scope('RNN_{}'.format(i)):
           cells.append(tf.nn.rnn_cell.GRUCell(hidden_dim))
           # cells.append(tf.nn.rnn_cell.BasicLSTMCell(...))
   cell = tf.nn.rnn_cell.MultiRNNCell(cells)   
   # For reshaping the input and output dimensions of the seq2seq RNN:
   w_in = tf.Variable(tf.random_normal([input_dim, hidden_dim]))
   b_in = tf.Variable(tf.random_normal([hidden_dim], mean=1.0))
   w_out = tf.Variable(tf.random_normal([hidden_dim, output_dim]))
   b_out = tf.Variable(tf.random_normal([output_dim]))   
reshaped_inputs = [tf.nn.relu(tf.matmul(i, w_in) + b_in) for i in enc_inp]   
# Here, the encoder and the decoder uses the same cell, HOWEVER,
   # the weights aren't shared among the encoder and decoder, we have two
   # sets of weights created under the hood according to that function's def.
   dec_outputs, dec_memory = tf.nn.seq2seq.basic_rnn_seq2seq(
       enc_inp,
       dec_inp,
       cell
   )   
output_scale_factor = tf.Variable(1.0, name="Output_ScaleFactor")
   # Final outputs: with linear rescaling similar to batch norm,
   # but without the "norm" part of batch normalization hehe.
   reshaped_outputs = [output_scale_factor*(tf.matmul(i, w_out) + b_out) for i in dec_outputs]  
   # Merge all the summaries and write them out to /tmp/bitcoin_logs (by default)
   merged = tf.summary.merge_all()
   train_writer = tf.summary.FileWriter('/tmp/bitcoin_logs',                                     sess.graph)
  1. 现在让我们运行 TensorBoard 并可视化由 RNN 编码器和 RNN 解码器组成的网络:
tensorboard --logdir=/tmp/bitcoin_logs

以下是代码流程:

Tensorboard 中的比特币价值预测代码示例

  1. 现在让我们将损失函数定义为具有正则化的 L2 损失,以避免过拟合并获得更好的泛化。 选择的优化器是 RMSprop,其值为learning_rate,衰减和动量,如步骤 3 所定义:
# Training loss and optimizer
with tf.variable_scope('Loss'):
   # L2 loss
   output_loss = 0
   for _y, _Y in zip(reshaped_outputs, expected_sparse_output):
       output_loss += tf.reduce_mean(tf.nn.l2_loss(_y - _Y))       
  # L2 regularization (to avoid overfitting and to have a  better generalization capacity)
   reg_loss = 0
   for tf_var in tf.trainable_variables():
       if not ("Bias" in tf_var.name or "Output_" in tf_var.name):
           reg_loss += tf.reduce_mean(tf.nn.l2_loss(tf_var))
   loss = output_loss + lambda_l2_reg * reg_loss
with tf.variable_scope('Optimizer'):
   optimizer = tf.train.RMSPropOptimizer(learning_rate, decay=lr_decay, momentum=momentum)
   train_op = optimizer.minimize(loss)
  1. 通过生成训练数据并在数据集中的batch_size示例上运行优化器来为批量训练做准备。 同样,通过从数据集中的batch_size示例生成测试数据来准备测试。 训练针对nb_iters+1迭代进行,每十个迭代中的一个用于测试结果:
def train_batch(batch_size):
   """
   Training step that optimizes the weights
   provided some batch_size X and Y examples from the dataset.
   """
   X, Y = generate_x_y_data(isTrain=True, batch_size=batch_size)
   feed_dict = {enc_inp[t]: X[t] for t in range(len(enc_inp))}
   feed_dict.update({expected_sparse_output[t]: Y[t] for t in range(len(expected_sparse_output))})
   _, loss_t = sess.run([train_op, loss], feed_dict)
   return loss_t
def test_batch(batch_size):
   """
   Test step, does NOT optimizes. Weights are frozen by not
   doing sess.run on the train_op.
   """
   X, Y = generate_x_y_data(isTrain=False, batch_size=batch_size)
   feed_dict = {enc_inp[t]: X[t] for t in range(len(enc_inp))}
   feed_dict.update({expected_sparse_output[t]: Y[t] for t in range(len(expected_sparse_output))})
   loss_t = sess.run([loss], feed_dict)
   return loss_t[0]
# Training
train_losses = []
test_losses = []
sess.run(tf.global_variables_initializer())
for t in range(nb_iters+1):
   train_loss = train_batch(batch_size)
   train_losses.append(train_loss)   
   if t % 10 == 0:
       # Tester
       test_loss = test_batch(batch_size)
       test_losses.append(test_loss)
       print("Step {}/{}, train loss: {}, \tTEST loss: {}".format(t, nb_iters, train_loss, test_loss))
print("Fin. train loss: {}, \tTEST loss: {}".format(train_loss, test_loss))
  1. 可视化n_predictions结果。 我们将以黄色形象化nb_predictions = 5预测,以x形象化蓝色的实际值ix。 请注意,预测从直方图中的最后一个蓝点开始,从视觉上,您可以观察到,即使这个简单的模型也相当准确:
# Test
nb_predictions = 5
print("Let's visualize {} predictions with our signals:".format(nb_predictions))
X, Y = generate_x_y_data(isTrain=False, batch_size=nb_predictions)
feed_dict = {enc_inp[t]: X[t] for t in range(seq_length)}
outputs = np.array(sess.run([reshaped_outputs], feed_dict)[0])
for j in range(nb_predictions):
   plt.figure(figsize=(12, 3))   
   for k in range(output_dim):
       past = X[:,j,k]
       expected = Y[:,j,k]
       pred = outputs[:,j,k]       
       label1 = "Seen (past) values" if k==0 else "_nolegend_"
       label2 = "True future values" if k==0 else "_nolegend_"
       label3 = "Predictions" if k==0 else "_nolegend_"
       plt.plot(range(len(past)), past, "o--b", label=label1)
       plt.plot(range(len(past), len(expected)+len(past)), expected, "x--b", label=label2)
       plt.plot(range(len(past), len(pred)+len(past)), pred, "o--y", label=label3)   
   plt.legend(loc='best')
   plt.title("Predictions v.s. true values")
   plt.show()

我们得到的结果如下:

比特币价值预测的一个例子

工作原理

带有 GRU 基本单元的编码器-解码器层堆叠 RNN 用于预测比特币值。 RNN 非常擅长学习序列,即使使用基于 2 层和 12 个 GRU 单元的简单模型,比特币的预测确实相当准确。 当然,此预测代码并非鼓励您投资比特币,而只是讨论深度学习方法。 而且,需要更多的实验来验证我们是否存在数据过拟合的情况。

更多

预测股市价值是一个不错的 RNN 应用,并且有许多方便的包,例如:

多对一和多对多 RNN 示例

在本秘籍中,我们通过提供 RNN 映射的各种示例来总结与 RNN 讨论过的内容。 为了简单起见,我们将采用 Keras 并演示如何编写一对一,一对多,多对一和多对多映射,如下图所示:

RNN 序列的一个例子

操作步骤

我们按以下步骤进行:

  1. 如果要创建一对一映射,则这不是 RNN,而是密集层。 假设已经定义了一个模型,并且您想添加一个密集网络。 然后可以在 Keras 中轻松实现:
model = Sequential()
model.add(Dense(output_size, input_shape=input_shape))
  1. 如果要创建一对多选项,可以使用RepeatVector(...)实现。 请注意,return_sequences是一个布尔值,用于决定是返回输出序列中的最后一个输出还是完整序列:
model = Sequential()
model.add(RepeatVector(number_of_times,input_shape=input_shape)) 
model.add(LSTM(output_size, return_sequences=True))
  1. 如果要创建多对一选项,则可以使用以下 LSTM 代码段实现:
model = Sequential()
model.add(LSTM(1, input_shape=(timesteps, data_dim)))
  1. 如果要创建多对多选项,当输入和输出的长度与循环步数匹配时,可以使用以下 LSTM 代码段来实现:
model = Sequential() 
model.add(LSTM(1, input_shape=(timesteps, data_dim), return_sequences=True))

工作原理

Keras 使您可以轻松编写各种形状的 RNN,包括一对一,一对多,多对一和多对多映射。 上面的示例说明了用 Keras 实现它们有多么容易。

七、无监督学习

到目前为止,我们在本书中涵盖的所有模型都是基于监督学习范式的。 训练数据集包括输入和该输入的所需标签。 相反,本章重点介绍无监督的学习范式。 本章将包括以下主题:

  • 主成分分析
  • K 均值聚类
  • 自组织图
  • 受限玻尔兹曼机
  • 使用 RBM 的推荐系统
  • 用于情感检测的 DBN

介绍

在机器学习中,存在三种不同的学习范式:监督学习,无监督学习和强化学习。

监督学习(也称为与老师一起学习)中,向网络提供输入和各自所需的输出。 例如,在 MNIST 数据集中,手写数字的每个图像都有一个标签,表示与之关联的数字值。

强化学习(也称为与批评家学习)中,没有为网络提供所需的输出; 相反,环境会提供奖励或惩罚方面的反馈。 当其输出正确时,环境奖励网络,而当输出不正确时,环境对其进行惩罚。

无监督学习(也称为无老师学习)中,没有向网络提供有关其输出的信息。 网络接收输入,但是既不提供期望的输出,也不提供来自环境的奖励; 网络自己学习输入的隐藏结构。 无监督学习非常有用,因为正常情况下可用的数据没有标签。 它可以用于模式识别,特征提取,数据聚类和降维等任务。 在本章和下一章中,您将学习基于无监督学习的不同机器学习和 NN 技术。

主成分分析

主成分分析PCA)是用于降维的最流行的多元统计技术。 它分析了由几个因变量组成的训练数据,这些因变量通常是相互关联的,并以一组称为主成分的新正交变量的形式从训练数据中提取重要信息。 我们可以使用两种方法执行 PCA – 特征值分解奇异值分解SVD)。

准备

PCA 将n维输入数据还原为r维输入数据,其中r < n。 简单来说,PCA 涉及平移原点并执行轴的旋转,以使其中一个轴(主轴)与数据点的差异最小。 通过执行此变换,然后以高方差落下(删除)正交轴,可以从原始数据集中获得降维数据集。 在这里,我们采用 SVD 方法降低 PCA 尺寸。 考虑Xn维数据,具有p个点X[p,n]。 任何实数(p × n)矩阵都可以分解为:

X = U ∑ V^T

在这里, UV是正交矩阵(即U · U^T = V^T · V = E),大小分别为p × nn × n是大小为n × n的对角矩阵。 接下来,将矩阵切成r列,得到∑[r]; 使用UV,我们找到了降维数据点Y[r]

Y[r] = U ∑[r]

此处提供的代码已从以下 GitHub 链接进行改编

操作步骤

我们按以下步骤进行操作:

  1. 导入所需的模块。 我们肯定会使用 TensorFlow; 我们还需要numpy进行一些基本矩阵计算,并需要matplotlibmpl_toolkitseaborn进行绘图:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
%matplotlib inline
  1. 我们加载数据集-我们将使用我们最喜欢的 MNIST 数据集:
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/")
  1. 我们定义了一个TF_PCA类,它将实现我们的所有工作。 该类的初始化如下:
def __init__(self, data,  dtype=tf.float32):
        self._data = data
        self._dtype = dtype
        self._graph = None
        self._X = None
        self._u = None
        self._singular_values = None
        self._sigma = None)
  1. 给定输入数据的 SVD 用fit方法计算。 该方法定义了计算图,并执行该计算图以计算奇异值和正交矩阵U。需要self.data来输入占位符self._Xtf.svd以降序返回形状[..., p]ssingular_values)。 我们使用tf.diag将其转换为对角矩阵:
def fit(self):
        self._graph = tf.Graph()
        with self._graph.as_default():
            self._X = tf.placeholder(self._dtype, shape=self._data.shape)
            # Perform SVD
            singular_values, u, _ = tf.svd(self._X)
            # Create sigma matrix
            sigma = tf.diag(singular_values)
        with tf.Session(graph=self._graph) as session:
            self._u, self._singular_values, self._sigma = session.run([u, singular_values, sigma], feed_dict={self._X: self._data})
  1. 现在我们有了sigma矩阵,正交U矩阵和奇异值,我们通过定义reduce方法来计算降维数据。 该方法需要两个输入参数之一n_dimensionskeep_infon_dimensions参数表示我们要保留在降维数据集中的维数。 另一方面,keep_info参数确定我们要保留的信息的百分比(值为 0.8 表示我们要保留 80% 的原始数据)。 该方法创建一个切片 Sigma 矩阵的图,并计算降维数据集Y[r]
def reduce(self, n_dimensions=None, keep_info=None):
        if keep_info:
            # Normalize singular values
            normalized_singular_values = self._singular_values / sum(self._singular_values)
            # information per dimension
            info = np.cumsum(normalized_singular_values)            # Get the first index which is above the given information threshold
           it = iter(idx for idx, value in enumerate(info) if value >= keep_info)
            n_dimensions = next(it) + 1 
       with self.graph.as_default():
            # Cut out the relevant part from sigma
            sigma = tf.slice(self._sigma, [0, 0], [self._data.shape[1], n_dimensions])
            # PCA
            pca = tf.matmul(self._u, sigma)
        with tf.Session(graph=self._graph) as session:
            return session.run(pca, feed_dict={self._X: self._data})
  1. 我们的TF_PCA类已准备就绪。 现在,我们将使用它来将 MNIST 数据从尺寸为 784(28 x 28)的每个输入减少为尺寸为 3 的每个点的新数据。这里,我们仅保留了 10% 的信息以便于查看,但是通常需要保留大约 80% 的信息:
tf_pca.fit()
pca = tf_pca.reduce(keep_info=0.1)  # The reduced dimensions dependent upon the % of information
print('original data shape', mnist.train.images.shape)
print('reduced data shape', pca.shape)

以下是以下代码的输出:

  1. 现在,让我们在三维空间中绘制 55,000 个数据点:
Set = sns.color_palette("Set2", 10)
color_mapping = {key:value for (key,value) in enumerate(Set)}
colors = list(map(lambda x: color_mapping[x], mnist.train.labels))
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(pca[:, 0], pca[:, 1],pca[:, 2], c=colors)

工作原理

前面的代码对 MNIST 图像执行降维。 每个原始图像的尺寸为28 x 28; 使用 PCA 方法,我们可以将其减小到较小的尺寸。 通常,对于图像数据,降维是必要的。 之所以如此,是因为图像很大,并且包含大量的冗余数据。

更多

TensorFlow 提供了一种称为嵌入的技术,该技术是将对象映射到向量中。 TensorBoard 的嵌入式投影仪允许我们以交互方式可视化模型中的嵌入。 嵌入式投影仪提供了三种降低尺寸的方法:PCA,t-SNE 和自定义。 我们可以使用 TensorBoard 的嵌入投影器实现与上一个类似的结果。 我们需要从tensorflow.contrib.tensorboard.plugins导入projector类,以从tensorflow.contrib.tensorboard.plugins导入projector进行相同的操作。 我们可以通过三个简单的步骤来做到这一点:

  1. 加载要探索其嵌入的数据:
mnist = input_data.read_data_sets('MNIST_data')
images = tf.Variable(mnist.test.images, name='images')
  1. 创建一个metadata文件((metadata文件是制表符分隔的.tsv文件):
with open(metadata, 'w') as metadata_file:
    for row in mnist.test.labels:
        metadata_file.write('%d\n' % row)
  1. 将嵌入内容保存在所需的Log_DIR中:
with tf.Session() as sess:
    saver = tf.train.Saver([images])
    sess.run(images.initializer)
    saver.save(sess, os.path.join(LOG_DIR, 'images.ckpt'))
    config = projector.ProjectorConfig()
    # One can add multiple embeddings.
    embedding = config.embeddings.add()
    embedding.tensor_name = images.name
    # Link this tensor to its metadata file (e.g. labels).
    embedding.metadata_path = metadata
    # Saves a config file that TensorBoard will read during startup.
    projector.visualize_embeddings(tf.summary.FileWriter(LOG_DIR), config)

嵌入已准备就绪,现在可以使用 TensorBoard 看到。 通过 CLI tensorboard --logdir=log启动 TensorBoard,在 Web 浏览器中打开 TensorBoard,然后转到EMBEDDINGS选项卡。 这是使用 PCA 的 TensorBoard 投影,前三个主要成分为轴:

另见

K 均值聚类

顾名思义,K 均值聚类是一种对数据进行聚类的技术,即将数据划分为指定数量的数据点。 这是一种无监督的学习技术。 它通过识别给定数据中的模式来工作。 还记得哈利波特成名的分拣帽子吗? 书中的工作是聚类-将新生(未标记)的学生分成四个不同的类:格兰芬多,拉文克劳,赫奇帕奇和斯莱特林。

人类非常擅长将对象分组在一起。 聚类算法试图为计算机提供类似的功能。 有许多可用的聚类技术,例如“层次”,“贝叶斯”或“局部”。 K 均值聚类属于部分聚类; 它将数据划分为k簇。 每个簇都有一个中心,称为重心。 簇数k必须由用户指定。

K 均值算法以以下方式工作:

  1. 随机选择k个数据点作为初始质心(集群中心)
  2. 将每个数据点分配给最接近的质心; 可以找到接近度的不同方法,最常见的是欧几里得距离
  3. 使用当前簇成员资格重新计算质心,以使平方和的距离减小
  4. 重复最后两个步骤,直到达到收敛

准备

我们将使用 TensorFlow KmeansClustering估计器类来实现 K 均值。 它在这个链接中定义。它创建一个模型来运行 K 均值和推理。 根据 TensorFlow 文档,一旦创建了KmeansClustering类对象,就可以使用以下__init__方法实例化该对象:

__init__(
num_clusters,
model_dir=None,
initial_clusters=RANDOM_INIT,
distance_metric=SQUARED_EUCLIDEAN_DISTANCE,
random_seed=0,
use_mini_batch=True,
mini_batch_steps_per_iteration=1,
kmeans_plus_plus_num_retries=2,
relative_tolerance=None,
config=None
)

TensorFlow 文档对这些参数的定义如下:

Args:

num_clusters: The number of clusters to train.

model_dir: The directory to save the model results and log files.

initial_clusters: Specifies how to initialize the clusters for training. See clustering_ops.kmeans for the possible values.

distance_metric: The distance metric used for clustering. See clustering_ops.kmeans for the possible values.

random_seed: Python integer. Seed for PRNG used to initialize centers.

use_mini_batch: If true, use the mini-batch k-means algorithm. Or else assume full batch.

mini_batch_steps_per_iteration: The number of steps after which the updated cluster centers are synced back to a master copy. See clustering_ops.py for more details.

kmeans_plus_plus_num_retries: For each point that is sampled during kmeans++ initialization, this parameter specifies the number of additional points to draw from the current distribution before selecting the best. If a negative value is specified, a heuristic is used to sample O(log(num_to_sample)) additional points.

relative_tolerance: A relative tolerance of change in the loss between iterations. Stops learning if the loss changes less than this amount. Note that this may not work correctly if use_mini_batch=True.

config: See Estimator.

TensorFlow 支持欧几里得距离和余弦距离作为质心的量度。 TensorFlow KmeansClustering提供了各种与KmeansClustering对象进行交互的方法。 在本秘籍中,我们将使用fit()clusters()predict_clusters_idx()方法:

fit(
 x=None,
 y=None,
 input_fn=None,
 steps=None,
 batch_size=None,
 monitors=None,
 max_steps=None
)

根据 TensorFlow 文档,对于KmeansClustering估计器,我们需要向fit()提供input_fn()cluster方法返回聚类中心,predict_cluster_idx方法返回预测的聚类索引。

操作步骤

这是我们进行秘籍的方法:

  1. 和以前一样,我们从加载必要的模块开始。 我们将像往常一样需要 TensorFlow,NumPy 和 Matplotlib。 在本秘籍中,我们使用的是鸢尾花数据集,该数据集包含三个类别,每个类别有 50 个实例,其中每个类别都代表一种鸢尾花植物。 我们可以从这里下载数据作为.csv文件,也可以使用 sklearn 的数据集模块(scikit-learn) 做任务:
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# dataset Iris
from sklearn import datasets
%matplotlib inline
  1. 我们加载数据集:
# import some data to play with
iris = datasets.load_iris()
x = iris.data[:, :2] # we only take the first two features.
y = iris.target
  1. 让我们看看该数据集的外观:
# original data without clustering
plt.scatter(hw_frame[:,0], hw_frame[:,1])
plt.xlabel('Sepia Length')
plt.ylabel('Sepia Width')

以下是以下代码的输出:

  1. 我们可以看到在数据中没有明显可见的聚类。 现在我们定义input_fn,它将用于提供fit方法。 我们的输入函数返回一个 TensorFlow 常数,该常数被分配了x的值和形状,并且类型为float
def input_fn():
 return tf.constant(np.array(x), tf.float32, x.shape),None
  1. 现在我们使用KmeansClustering类; 在这里,我们已经知道类的数量为 3,因此我们将num_clusters=3设置为。 通常,我们不知道集群的数量。 在这种情况下,常用的方法是肘部法则
kmeans = tf.contrib.learn.KMeansClustering(num_clusters=3, relative_tolerance=0.0001, random_seed=2) 
kmeans.fit(input_fn=input_fn)
  1. 我们使用clusters()方法找到聚类,并使用predict_cluster_idx()方法为每个输入点分配聚类索引:
clusters = kmeans.clusters()
assignments = list(kmeans.predict_cluster_idex(input_fn=input_fn))
  1. 现在让我们可视化由 K 均值创建的聚类。 为此,我们创建一个包装器函数ScatterPlot,该函数将XY值以及每个数据点的簇和簇索引一起使用:
def ScatterPlot(X, Y, assignments=None, centers=None):
 if assignments is None:
 assignments = [0] * len(X)
 fig = plt.figure(figsize=(14,8))
 cmap = ListedColormap(['red', 'green', 'blue'])
 plt.scatter(X, Y, c=assignments, cmap=cmap)
 if centers is not None:
 plt.scatter(centers[:, 0], centers[:, 1], c=range(len(centers)), 
 marker='+', s=400, cmap=cmap) 
 plt.xlabel('Sepia Length')
 plt.ylabel('Sepia Width')

我们用它来绘制我们的clusters

ScatterPlot(x[:,0], x[:,1], assignments, clusters)

情节如下:

+标记是三个簇的质心。

工作原理

前面的秘籍使用 TensorFlow 的 K 均值聚类估计器将给定数据聚类为聚类。 在这里,由于我们知道集群的数量,我们决定保留num_clusters=3,但是在大多数情况下,如果使用未标记的数据,则永远无法确定存在多少集群。 可以使用弯头法确定最佳簇数。 该方法基于以下原则:我们应选择能减少平方误差和SSE)距离的簇数。 如果k是簇数,则随着k增加,SSE 减少,SSE = 0; 当k等于数据点数时,每个点都是其自己的簇。 我们想要一个k较低的值,以使 SSE 也较低。 在 TensorFlow 中,我们可以使用KmeansClustering类中定义的score()方法找到 SSE; 该方法将距离的总和返回到最近的聚类:

sum_distances = kmeans.score(input_fn=input_fn, steps=100)

对于鸢尾数据,如果我们针对不同的k值绘制 SSE,则可以看到对于k = 3而言,SSE 的方差最高; 之后,它开始减小,因此肘点为k = 3

更多

K 均值聚类非常流行,因为它快速,简单且健壮。 它还有一些缺点:最大的缺点是用户必须指定簇的数量。 其次,该算法不能保证全局最优。 第三,它对异常值非常敏感。

另见

自组织图

自组织映射SOM),有时也称为 Kohonen 网络胜者通吃单元WTU),是一种非常特殊的神经网络,受人脑的独特特征驱动。 在我们的大脑中,不同的感觉输入以拓扑有序的方式表示。 与其他神经网络不同,神经元并非都通过权重相互连接,而是会影响彼此的学习。 SOM 的最重要方面是神经元以拓扑方式表示学习的输入。

在 SOM 中,神经元通常放置在(1D 或 2D)晶格的节点上。 更大的尺寸也是可能的,但实际上很少使用。 晶格中的每个神经元都通过权重矩阵连接到所有输入单元。 在这里,您可以看到一个具有3 x 4(12 个神经元)和七个输入的 SOM。 为了清楚起见,仅显示将所有输入连接到一个神经元的权重向量。 在这种情况下,每个神经元将具有七个元素,从而形成大小为(12 x 7)的组合权重矩阵:

SOM 通过竞争性学习来学习。 可以将其视为 PCA 的非线性概括,因此,像 PCA 一样,可以用于降维。

准备

为了实现 SOM,让我们首先了解它是如何工作的。 第一步,将网络的权重初始化为某个随机值,或者通过从输入中获取随机样本进行初始化。 占据晶格中空间的每个神经元将被分配特定的位置。 现在,作为输入出现,与输入距离最小的神经元被宣布为 Winner(WTU)。 这是通过测量所有神经元的权重向量(W)和输入向量(X)之间的距离来完成的:

在此,d[j]是神经元j的权重与输入X的距离。 最小d值的神经元是赢家。

接下来,以一种方式调整获胜神经元及其相邻神经元的权重,以确保如果下次出现相同的输入,则相同的神经元将成为获胜者。 为了确定哪些相邻神经元需要修改,网络使用邻域函数Λ(r); 通常,选择高斯墨西哥帽函数作为邻域函数。 邻域函数在数学上表示如下:

在这里,σ是神经元的时间依赖性半径,d是其与获胜神经元的距离:

邻域函数的另一个重要属性是其半径随时间减小。 结果,一开始,许多相邻神经元的权重被修改,但是随着网络的学习,最终在学习过程中,一些神经元的权重(有时只有一个或没有)被修改。 权重变化由以下公式给出:

dW = η * Λ(X - W)

我们继续所有输入的过程,并重复给定的迭代次数。 随着迭代的进行,我们将学习率和半径减小一个取决于迭代次数的因素。

操作步骤

我们按以下步骤进行:

  1. 与往常一样,我们从导入必要的模块开始:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
  1. 接下来,我们声明一个类 WTU,它将执行所有任务。 用m x n 2D SOM 格的大小,dim输入数据中的维数以及迭代总数来实例化该类:
def __init__(self, m, n, dim, num_iterations, eta = 0.5, sigma = None):
    """
    m x n : The dimension of 2D lattice in which neurons are     arranged
    dim : Dimension of input training data
    num_iterations: Total number of training iterations
    eta : Learning rate
    sigma: The radius of neighbourhood function.
    """
    self._m = m
    self._n = n
    self._neighbourhood = []
    self._topography = []
    self._num_iterations = int(num_iterations) 
    self._learned = False
  1. __init__本身中,我们定义了计算图和会话。
  2. 如果网络未提供任何sigma值,它将采用默认值,该值通常是 SOM 晶格最大尺寸的一半:
if sigma is None:
    sigma = max(m,n)/2.0 # Constant radius
else:
    sigma = float(sigma)
  1. 接下来,在图中,我们声明权重矩阵的变量,输入的占位符以及计算获胜者并更新其及其邻居权重的计算步骤。 由于 SOM 提供了地形图,因此我们还添加了操作以获得神经元的地形位置:
self._graph = tf.Graph()
# Build Computation Graph of SOM
 with self._graph.as_default():
# Weight Matrix and the topography of neurons
    self._W = tf.Variable(tf.random_normal([m*n, dim], seed = 0))
    self._topography = tf.constant(np.array(list(self._neuron_location(m, n))))
    # Placeholders for training data
    self._X = tf.placeholder('float', [dim])
    # Placeholder to keep track of number of iterations
    self._iter = tf.placeholder('float')
    # Finding the Winner and its location
    d = tf.sqrt(tf.reduce_sum(tf.pow(self._W - tf.stack([self._X 
          for i in range(m*n)]),2),1))
    self.WTU_idx = tf.argmin(d,0)
    slice_start = tf.pad(tf.reshape(self.WTU_idx, [1]),np.array([[0,1]]))
    self.WTU_loc = tf.reshape(tf.slice(self._topography, slice_start,             [1,2]), [2])
    # Change learning rate and radius as a function of iterations
    learning_rate = 1 - self._iter/self._num_iterations
    _eta_new = eta * learning_rate
    _sigma_new = sigma * learning_rate
    # Calculating Neighbourhood function
    distance_square = tf.reduce_sum(tf.pow(tf.subtract(
    self._topography, tf.stack([self.WTU_loc for i in range(m * n)])), 2), 1)
    neighbourhood_func = tf.exp(tf.negative(tf.div(tf.cast(
distance_square, "float32"), tf.pow(_sigma_new, 2))))
    # multiply learning rate with neighbourhood func
    eta_into_Gamma = tf.multiply(_eta_new, neighbourhood_func)
    # Shape it so that it can be multiplied to calculate dW
    weight_multiplier = tf.stack([tf.tile(tf.slice(
eta_into_Gamma, np.array([i]), np.array([1])), [dim])
for i in range(m * n)])
    delta_W = tf.multiply(weight_multiplier,
tf.subtract(tf.stack([self._X for i in range(m * n)]),self._W))
    new_W = self._W + delta_W
    self._training = tf.assign(self._W,new_W)
   # Initialize All variables
   init = tf.global_variables_initializer()
   self._sess = tf.Session()
   self._sess.run(init)
  1. 我们为该类定义一个fit方法,该方法执行在该类的默认图中声明的训练操作。 该方法还计算质心网格:
def fit(self, X):
 """
 Function to carry out training
 """
 for i in range(self._num_iterations):
   for x in X:
       self._sess.run(self._training, feed_dict= {self._X:x, self._iter: i})
 # Store a centroid grid for easy retreival
 centroid_grid = [[] for i in range(self._m)]
 self._Wts = list(self._sess.run(self._W))
 self._locations = list(self._sess.run(self._topography))
 for i, loc in enumerate(self._locations):
      centroid_grid[loc[0]].append(self._Wts[i])
 self._centroid_grid = centroid_grid
 self._learned = True
  1. 我们定义一个函数来确定获胜神经元在 2D 晶格中的索引和位置:
def winner(self, x):
    idx = self._sess.run([self.WTU_idx,self.WTU_loc], feed_dict = {self._X:x})
    return idx
  1. 我们定义一些更多的辅助函数,以执行晶格中神经元的 2D 映射并将输入向量映射到 2D 晶格中的相关神经元:
def _neuron_location(self,m,n):
    """
    Function to generate the 2D lattice of neurons
    """
    for i in range(m):
       for j in range(n):
           yield np.array([i,j])
def get_centroids(self):
    """
    Function to return a list of 'm' lists, with each inner     list containing the 'n' corresponding centroid locations     as 1-D NumPy arrays.
    """
    if not self._learned:
       raise ValueError("SOM not trained yet")
    return self._centroid_grid
def map_vects(self, X):
    """
    Function to map each input vector to the relevant neuron         in the lattice
    """
    if not self._learned:
        raise ValueError("SOM not trained yet")
    to_return = []
    for vect in X:
       min_index = min([i for i in range(len(self._Wts))],
       key=lambda x: np.linalg.norm(vect -
self._Wts[x]))
       to_return.append(self._locations[min_index])
return to_return
  1. 现在我们的 WTU 类已经准备好,我们从.csv文件中读取数据并对其进行规范化:
def normalize(df):
    result = df.copy()
    for feature_name in df.columns:
       max_value = df[feature_name].max()
       min_value = df[feature_name].min()
       result[feature_name] = (df[feature_name] - min_value) / (max_value - min_value)
    return result
# Reading input data from file
import pandas as pd
df = pd.read_csv('colors.csv') # The last column of data file is a label
data = normalize(df[['R', 'G', 'B']]).values
name = df['Color-Name'].values
n_dim = len(df.columns) - 1
# Data for Training
colors = data
color_names = name
  1. 最后,我们使用我们的类执行降维并将其布置在美丽的地形图中:
som = WTU(30, 30, n_dim, 400, sigma=10.0)
som.fit(colors)
# Get output grid
image_grid = som.get_centroids()
# Map colours to their closest neurons
mapped = som.map_vects(colors)
# Plot
plt.imshow(image_grid)
plt.title('Color Grid SOM')
for i, m in enumerate(mapped):
     plt.text(m[1], m[0], color_names[i], ha='center', va='center', bbox=dict(facecolor='white', alpha=0.5, lw=0))

情节如下:

工作原理

SOM 在计算上很昂贵,因此对于非常大的数据集并没有真正的用处。 尽管如此,它们仍然易于理解,并且可以很好地找到输入数据之间的相似性。 因此,它们已被用于图像分割和确定 NLP 中的单词相似度图。

另见

受限玻尔兹曼机

受限玻尔兹曼机RBM)是两层神经网络,第一层称为可见层,第二层称为隐藏层。 它们被称为浅层神经网络,因为它们只有两层深。 它们最初是由 1986 年由保罗·斯莫伦斯基(Paul Smolensky)提出的(他称其为 Harmony Networks),后来由 Geoffrey Hinton 提出,于 2006 年提出了对比发散CD)作为训练他们的方法。 可见层中的所有神经元都与隐藏层中的所有神经元相连,但是存在限制-同一层中没有神经元可以连接。 所有神经元本质上都是二进制的:

资料来源:Qwertyus 自己的作品,CC BY-SA 3.0

RBM 可用于降维,特征提取和协作过滤。 RBM 中的训练可分为三个部分:前进,后退和比较。

准备

让我们看看制作 RBM 所需的表达式:

正向传递:可见单元(V)上的信息通过权重(W)和偏差(c)传递给隐藏的对象单元(h[0])。 隐藏单元是否可以触发取决于随机概率(σ是随机概率):

p(h[i]|v[0]) = σ(V^T · W + c)[i]

向后传递:隐藏的单元表示(h[0])然后通过相同的权重W但不同的偏置c传递回可见单元,它们在其中重构输入。 再次,对输入进行采样:

p(v[i]|h[0]) = σ(W^T · h[0] + b)[i]*

将这两个遍重复 k 步或直到达到收敛。 根据研究人员的说法,k = 1给出了很好的结果,因此我们将保持k = 1

可见向量V和隐藏向量的联合构型具有如下能量:

自由能还与每个可见向量V相关,为与具有V的所有构型具有相同概率的单个配置所需的能量:

使用对比度发散目标函数,即Mean(F(Voriginal))- Mean(F(Vreconstructed)),权重的变化由此给出:

在此,η是学习率。 对于偏差bc存在相似的表达式。

操作步骤

我们按以下步骤进行:

  1. 导入模块:
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
%matplotlib inline
  1. 声明 RBM 类,它将完成主要任务。 __init__将建立完整的图,正向和反向传递以及目标函数; 我们将使用 TensorFlow 内置的优化器来更新权重和偏差:
class RBM(object):
    def __init__(self, m, n):
        """
        m: Number of neurons in visible layer
        n: number of neurons in hidden layer
        """
        self._m = m
        self._n = n
        # Create the Computational graph
        # Weights and biases
        self._W = tf.Variable(tf.random_normal(shape=(self._m,self._n)))
        self._c = tf.Variable(np.zeros(self._n).astype(np.float32)) #bias for hidden layer
        self._b = tf.Variable(np.zeros(self._m).astype(np.float32)) #bias for Visible layer
         # Placeholder for inputs
        self._X = tf.placeholder('float', [None, self._m])
         # Forward Pass
        _h = tf.nn.sigmoid(tf.matmul(self._X, self._W) + self._c)
        self.h = tf.nn.relu(tf.sign(_h -         tf.random_uniform(tf.shape(_h))))
        #Backward pass
        _v = tf.nn.sigmoid(tf.matmul(self.h, tf.transpose(self._W)) + self._b)
        self.V = tf.nn.relu(tf.sign(_v - tf.random_uniform(tf.shape(_v))))
        # Objective Function
        objective = tf.reduce_mean(self.free_energy(self._X)) - tf.reduce_mean(
self.free_energy(self.V))
        self._train_op = tf.train.GradientDescentOptimizer(1e-3).minimize(objective)
        # Cross entropy cost
        reconstructed_input = self.one_pass(self._X)
        self.cost = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
labels=self._X, logits=reconstructed_input))
  1. 我们在RBM类中定义fit()方法。 在__init__中声明了所有操作后,训练只是在会话中调用train_op。 我们使用批量训练:
def fit(self, X, epochs = 1, batch_size = 100):
        N, D = X.shape
        num_batches = N // batch_size
        obj = []
        for i in range(epochs):
            #X = shuffle(X)
            for j in range(num_batches):
                batch = X[j * batch_size: (j * batch_size + batch_size)]
                _, ob = self.session.run([self._train_op,self.cost ], feed_dict={self._X: batch})
                if j % 10 == 0:
                    print('training epoch {0} cost {1}'.format(j,ob)) 
                obj.append(ob)
        return obj
  1. 还有其他辅助函数可计算对率误差并从网络返回重建的图像:
def set_session(self, session):
    self.session = session
def free_energy(self, V):
    b = tf.reshape(self._b, (self._m, 1))
    term_1 = -tf.matmul(V,b)
    term_1 = tf.reshape(term_1, (-1,))
    term_2 = -tf.reduce_sum(tf.nn.softplus(tf.matmul(V,self._W) +
        self._c))
    return term_1 + term_2
def one_pass(self, X):
    h = tf.nn.sigmoid(tf.matmul(X, self._W) + self._c)
    return tf.matmul(h, tf.transpose(self._W)) + self._b
def reconstruct(self,X):
    x = tf.nn.sigmoid(self.one_pass(X))
    return self.session.run(x, feed_dict={self._X: X})
  1. 我们加载 MNIST 数据集:
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
  1. 接下来,我们在 MNIST 数据集上训练RBM
Xtrain = trX.astype(np.float32)
Xtest = teX.astype(np.float32)
_, m = Xtrain.shape
rbm = RBM(m, 100)
#Initialize all variables
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    rbm.set_session(sess)
    err = rbm.fit(Xtrain)
    out = rbm.reconstruct(Xest[0:100])  # Let us reconstruct Test Data
  1. 不同周期的函数误差:

工作原理

由于其具有重建图像的能力,RBM 可用于从现有数据中生成更多数据。 通过制作一个小的助手绘图代码,我们可以看到原始和重建的 MNIST 图像:

row, col = 2, 8
idx = np.random.randint(0, 100, row * col // 2)
f, axarr = plt.subplots(row, col, sharex=True, sharey=True, figsize=(20,4))
for fig, row in zip([Xtest_noisy,out], axarr):
    for i,ax in zip(idx,row):
        ax.imshow(fig[i].reshape((28, 28)), cmap='Greys_r')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)

我们得到的结果如下:

另见

使用 RBM 的推荐系统

网上零售商广泛使用推荐系统向客户推荐产品。 例如,亚马逊会告诉您购买此商品的其他客户对什么感兴趣,或者 Netflix 根据您所观看的内容以及有相同兴趣的其他 Netflix 用户所观看的内容推荐电视连续剧和电影。 这些推荐器系统在协作筛选的基础上工作。 在协作过滤中,系统根据用户的过去行为来构建模型。 我们将使用上一个秘籍中的 RBM 构建一个使用协作过滤来推荐电影的推荐器系统。 这项工作中的一个重要挑战是,大多数用户不会对所有产品/电影进行评分,因此大多数数据都将丢失。 如果有 M 个产品和 N 个用户,则我们需要构建一个数组N x M,其中包含用户的已知等级并将所有未知值设为零。

准备

为了使用协作过滤创建推荐系统,我们需要修改数据。 作为说明,我们将使用来自这里的电影数据集。 数据由两个.dat文件组成:movies.datratings.datmovies.dat文件包含 3 列:3883 个电影的 MovieID,Title 和 Genre。 ratings.dat文件包含四列:UserID,MovieID,Rating 和 Time。 我们需要合并这两个数据文件,以便能够构建一个数组,其中对于每个用户,我们对所有 3,883 部电影都有一个评分。 问题在于用户通常不会对所有电影进行评级,因此我们仅对某些电影进行非零(标准化)评级。 其余部分设为零,因此不会对隐藏层有所贡献。

操作步骤

  1. 我们将使用在先前秘籍中创建的RBM类。 让我们定义我们的 RBM 网络; 可见单元的数量将是电影的数量,在我们的示例中为 3883(movies_df是包含movies.dat文件中的数据的数据帧):
m = len(movies_df)  # Number of visible units
n = 20  # Number of Hidden units
recommender = rbm.RBM(m,n)
  1. 我们使用 Pandas 合并和groupby命令创建了一个列表trX,该列表包含大约 1,000 个用户的规范化电影评分。 列表的大小为1000 x 3883。我们使用它来训练我们的 RBM:
Xtrain = np.array(trX) 
init = tf.global_variables_initializer()
with tf.Session() as sess:
 sess.run(init)
 recommender.set_session(sess)
 err = recommender.fit(Xtrain, epochs=10)
  1. 每个周期的跨逻辑误差减少:

  1. 网络现已接受训练; 我们使用它为索引为 150 的随机用户(可能是任何现有用户)获得推荐:
user_index = 150
x = np.array([Xtrain[user_index, :]])
init = tf.global_variables_initializer()
with tf.Session() as sess:
 sess.run(init)
 recommender.set_session(sess)
 out = recommender.reconstruct(x.astype(np.float32))
  1. 结果与现有数据帧合并,我们可以看到该用户的推荐分数:

更多

杰弗里·欣顿(Geoffrey Hinton)教授领导的多伦多大学团队赢得了 Netflix 最佳协作过滤竞赛的冠军,该协作过滤使用 RBM 来预测电影的用户收视率。 可以从他们的论文中获取其工作的详细信息

一个 RBM 的隐藏单元的输出可以馈送到另一个 RBM 的可见单元,可以重复此过程以形成 RBM 的栈。 这导致栈式 RBM 。 假定不存在其他堆叠式 RBM,则对其进行独立训练。 大量栈式 RBM 构成了深度信念网络(DBN)。 可以使用有监督或无监督的训练来训练 DBN。 您将在下一个秘籍中了解有关它们的更多信息。

用于情感检测的 DBN

在本秘籍中,我们将学习如何首先堆叠 RBM 来制作 DBN,然后训练它来检测情感。 秘籍中有趣的部分是我们采用了两种不同的学习范例:首先,我们使用无监督学习对 RBM 进行了预训练,最后,我们有了一个 MLP 层,该层是使用监督学习进行了训练的。

准备

我们使用已经在秘籍受限玻尔兹曼机中创建的 RBM 类,只需进行一次更改即可,现在无需在训练后重建图像。 取而代之的是,我们栈式 RBM 将仅将数据转发至 DBN 的最后一个 MLP 层。 这是通过从类中删除reconstruct()函数并将其替换为rbm_output()函数来实现的:

def rbm_output(self,X):
    x = tf.nn.sigmoid(tf.matmul(X, self._W) + self._c)
    return self.session.run(x, feed_dict={self._X: X})

对于数据,我们考虑了 Kaggle 面部表情识别数据,该数据可从这里获得。 此处给出的数据描述为:

数据由48 x 48像素的面部灰度图像组成。 面部已自动注册,因此面部或多或少居中,并且在每个图像中占据大约相同的空间量。 任务是根据面部表情中显示的情感将每个面孔分类为七个类别之一(0 为愤怒,1 恶心,2 为恐惧,3 为快乐,4 为悲伤,5 为惊奇,6 为中性) 。

train.csv包含两列,“情感”和“像素”。 “情感”列包含图像中存在的情感的数字代码,范围从 0 到 6(含)。 “像素”列包含每个图像用引号引起来的字符串。 该字符串的内容是按行主要顺序分隔的像素值。 test.csv仅包含“像素”列,您的任务是预测情感列。

训练集包含 28,709 个示例。 用于排行榜的公共测试集包含 3,589 个示例。 最终测试集用于确定比赛的获胜者,另外还有 3,589 个示例。

该数据集由 Pierre-Luc Carrier 和 Aaron Courville 进行,是正在进行的研究项目的一部分。 他们为研讨会的组织者提供了他们数据集的初步版本,供比赛使用。

完整的数据合而为一。 名为fer2013.csvcsv文件。 我们从中分离出训练,验证和测试数据:

data = pd.read_csv('data/fer2013.csv')
tr_data = data[data.Usage == "Training"]
test_data = data[data.Usage == "PublicTest"]
mask = np.random.rand(len(tr_data)) < 0.8
train_data = tr_data[mask]
val_data = tr_data[~mask]

我们将需要预处理数据,即将像素和情感标签分开。 为此,我们制作了两个函数dense_to_one_hot (),它对标签执行了单热编码。 第二个函数是preprocess_data(),它将单个像素分离为一个数组。 在这两个函数的帮助下,我们生成了训练,验证和测试数据集的输入特征和标签:

def dense_to_one_hot(labels_dense, num_classes):
     num_labels = labels_dense.shape[0]
     index_offset = np.arange(num_labels) * num_classes
     labels_one_hot = np.zeros((num_labels, num_classes))
     labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1
     return labels_one_hot
def preprocess_data(dataframe):
     pixels_values = dataframe.pixels.str.split(" ").tolist()
     pixels_values = pd.DataFrame(pixels_values, dtype=int)
     images = pixels_values.values
     images = images.astype(np.float32)
     images = np.multiply(images, 1.0/255.0)
     labels_flat = dataframe["emotion"].values.ravel()
     labels_count = np.unique(labels_flat).shape[0]
     labels = dense_to_one_hot(labels_flat, labels_count)
     labels = labels.astype(np.uint8)
     return images, labels

使用前面代码中定义的函数,我们以训练所需的格式获取数据。 基于本文针对 MNIST 提到的相似原理,我们构建了情感检测 DBN

操作步骤

我们按以下步骤进行:

  1. 我们需要导入标准模块 TensorFlow,NumPy 和 Pandas,以读取.csv文件和 Matplolib:
import tensorflow as tf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
  1. 训练,验证和测试数据是使用辅助函数获得的:
X_train, Y_train = preprocess_data(train_data)
X_val, Y_val = preprocess_data(val_data)
X_test, Y_test = preprocess_data(test_data)
  1. 让我们来探讨一下我们的数据。 我们绘制平均图像并找到每个训练,验证和测试数据集中的图像数量:
# Explore Data
mean_image = X_train.mean(axis=0)
std_image = np.std(X_train, axis=0)
print("Training Data set has {} images".format(len(X_train)))
print("Validation Data set has {} images".format(len(X_val)))
print("Test Data set has {} images".format(len(X_test)))
plt.imshow(mean_image.reshape(48,48), cmap='gray')

我们得到的结果如下:

  1. 我们还会看到训练样本中的图像及其各自的标签:
classes = ['angry','disgust','fear','happy','sad','surprise','neutral']
num_classes = len(classes)
samples_per_class = 7
for y,cls in enumerate(classes):
     idxs = np.flatnonzero(np.argmax(Y_train, axis =1) == y)
     idxs = np.random.choice(idxs, samples_per_class, replace=False)
     for i, idx in enumerate(idxs):
         plt_idx = i * num_classes + y + 1
         plt.subplot(samples_per_class, num_classes, plt_idx)
         plt.imshow(X_train[idx].reshape(48,48), cmap='gray') #pixel height and width
         plt.axis('off')
         if i == 0:
             plt.title(cls)
plt.show()

情节如下:

  1. 接下来,我们定义 RBM 栈; 每个 RBM 都将先前 RBM 的输出作为其输入:
RBM_hidden_sizes = [1500, 700, 400] #create 4 layers of RBM with size 1500, 700, 400 and 100
#Set input as training data
inpX = X_train
#Create list to hold our RBMs
rbm_list = []
#Size of inputs is the number of inputs in the training set
input_size = inpX.shape[1]
#For each RBM we want to generate
for i, size in enumerate(RBM_hidden_sizes):
     print ('RBM: ',i,' ',input_size,'->', size)
     rbm_list.append(RBM(input_size, size))
     input_size = size

这将生成三个 RBM:第一个 RBM 具有 2304(48×48)个输入和 1500 个隐藏单元,第二个 RBM 具有 1500 个输入和 700 个隐藏单元,最后第三个 RBM 具有 700 个输入和 400 个隐藏单元。

  1. 我们逐一训练每个 RBM。 该技术也称为贪婪训练。 在原始论文中,用于在 MNIST 上训练每个 RBM 的周期数是 30,因此在这里,增加周期也应会改善网络的表现:
# Greedy wise training of RBMs
init = tf.global_variables_initializer()
for rbm in rbm_list:
     print ('New RBM:')
     #Train a new one
     with tf.Session() as sess:
         sess.run(init)
         rbm.set_session(sess)
         err = rbm.fit(inpX, 5)
         inpX_n = rbm.rbm_output(inpX)
         print(inpX_n.shape)
         inpX = inpX_n
  1. 我们定义一个DBN类。 在类中,我们用三层 RBM 和另外两层 MLP 构建完整的 DBN。 从预训练的 RBM 中加载 RBM 层的权重。 我们还声明了训练和预测 DBN 的方法; 为了进行微调,网络尝试最小化均方损失函数:
class DBN(object):
     def __init__(self, sizes, X, Y, eta = 0.001, momentum = 0.0, epochs = 10, batch_size = 100):
         #Initialize hyperparameters
         self._sizes = sizes
         print(self._sizes)
         self._sizes.append(1000) # size of the first FC layer
         self._X = X
         self._Y = Y
         self.N = len(X)
         self.w_list = []
         self.c_list = []
         self._learning_rate = eta
         self._momentum = momentum
         self._epochs = epochs
         self._batchsize = batch_size
         input_size = X.shape[1]
         #initialization loop
         for size in self._sizes + [Y.shape[1]]:
             #Define upper limit for the uniform distribution range
             max_range = 4 * math.sqrt(6\. / (input_size + size))
             #Initialize weights through a random uniform distribution
             self.w_list.append(
             np.random.uniform( -max_range, max_range, [input_size,         size]).astype(np.float32))
             #Initialize bias as zeroes
             self.c_list.append(np.zeros([size], np.float32))
             input_size = size
         # Build DBN
         #Create placeholders for input, weights, biases, output
         self._a = [None] * (len(self._sizes) + 2)
         self._w = [None] * (len(self._sizes) + 1)
         self._c = [None] * (len(self._sizes) + 1)
         self._a[0] = tf.placeholder("float", [None, self._X.shape[1]])
         self.y = tf.placeholder("float", [None, self._Y.shape[1]])
         #Define variables and activation function
         for i in range(len(self._sizes) + 1):
             self._w[i] = tf.Variable(self.w_list[i])
             self._c[i] = tf.Variable(self.c_list[i])
         for i in range(1, len(self._sizes) + 2):
             self._a[i] = tf.nn.sigmoid(tf.matmul(self._a[i - 1], self._w[i - 1]) + self._c[i - 1])
         #Define the cost function
         cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=self.y, logits= self._a[-1]))
         #cost = tf.reduce_mean(tf.square(self._a[-1] - self.y))
         #Define the training operation (Momentum Optimizer minimizing the Cost function)
         self.train_op = tf.train.AdamOptimizer(learning_rate=self._learning_rate).minimize(cost)
         #Prediction operation
         self.predict_op = tf.argmax(self._a[-1], 1)
     #load data from rbm
     def load_from_rbms(self, dbn_sizes,rbm_list):
         #Check if expected sizes are correct
         assert len(dbn_sizes) == len(self._sizes)
         for i in range(len(self._sizes)):
             #Check if for each RBN the expected sizes are correct
             assert dbn_sizes[i] == self._sizes[i]
         #If everything is correct, bring over the weights and biases
         for i in range(len(self._sizes)-1):
             self.w_list[i] = rbm_list[i]._W
             self.c_list[i] = rbm_list[i]._c
     def set_session(self, session):
         self.session = session
    #Training method
     def train(self, val_x, val_y):
         #For each epoch
         num_batches = self.N // self._batchsize
         batch_size = self._batchsize
         for i in range(self._epochs):
             #For each step
             for j in range(num_batches):
                 batch = self._X[j * batch_size: (j * batch_size + batch_size)]
                 batch_label = self._Y[j * batch_size: (j * batch_size + batch_size)]
                 self.session.run(self.train_op, feed_dict={self._a[0]: batch, self.y: batch_label})
                 for j in range(len(self._sizes) + 1):
                     #Retrieve weights and biases
                     self.w_list[j] = sess.run(self._w[j])
                     self.c_list[j] = sess.run(self._c[j])
             train_acc = np.mean(np.argmax(self._Y, axis=1) ==
 self.session.run(self.predict_op, feed_dict={self._a[0]: self._X, self.y: self._Y}))
             val_acc = np.mean(np.argmax(val_y, axis=1) ==
 self.session.run(self.predict_op, feed_dict={self._a[0]: val_x, self.y: val_y}))
             print (" epoch " + str(i) + "/" + str(self._epochs) + " Training Accuracy: " +  str(train_acc) + " Validation Accuracy: " + str(val_acc))
     def predict(self, X):
         return self.session.run(self.predict_op, feed_dict={self._a[0]: X})
  1. 现在,我们训练实例化DBN对象并对其进行训练。 并预测测试数据的标签:
nNet = DBN(RBM_hidden_sizes, X_train, Y_train, epochs = 80)
with tf.Session() as sess:
     #Initialize Variables
     sess.run(tf.global_variables_initializer())
     nNet.set_session(sess)
     nNet.load_from_rbms(RBM_hidden_sizes,rbm_list)
     nNet.train(X_val, Y_val)
     y_pred = nNet.predict(X_test)

工作原理

RBM 使用无监督学习来学习模型的隐藏表示/特征,然后对与预训练 RBM 一起添加的全连接层进行微调。

这里的精度在很大程度上取决于图像表示。 在前面的秘籍中,我们没有使用图像处理,仅使用了 0 到 1 之间缩放的灰度图像。但是,如果我们按照以下论文所述添加图像处理,则会进一步提高精度。 因此,我们在preprocess_data函数中将每个图像乘以 100.0/255.0,然后将以下几行代码添加到主代码中:

std_image = np.std(X_train, axis=0)
X_train = np.divide(np.subtract(X_train,mean_image), std_image)
X_val = np.divide(np.subtract(X_val,mean_image), std_image)
X_test = np.divide(np.subtract(X_test,mean_image), std_image)

更多

在前面的示例中,没有进行预处理,这三个数据集的准确率大约为 40%。 但是,当我们添加预处理时,训练数据的准确率将提高到 90%,但是对于验证和测试,我们仍然可以获得约 45% 的准确率。

可以引入许多更改来改善结果。 首先,我们在秘籍中使用的数据集是只有 22,000 张图像的 Kaggle 数据集。 如果观察这些图像,则会发现仅过滤面部的步骤会改善结果。 如下文所述,另一种策略是增加隐藏层的大小而不是减小它们的大小

在识别情感方面确实非常成功的另一个更改是使用面部关键点而不是整个面部训练

使用前面的秘籍,您可以尝试这些更改并探索表现如何提高。 愿 GPU 力量与您同在!

八、自编码器

自编码器是前馈,非循环神经网络,可通过无监督学习来学习。 他们具有学习数据的紧凑表示的固有能力。 它们是深度信念网络的中心,可在图像重建,聚类,机器翻译等领域找到应用。 在本章中,您将学习和实现自编码器的不同变体,并最终学习如何堆叠自编码器。 本章包括以下主题:

  • 普通自编码器
  • 稀疏自编码器
  • 去噪自编码器
  • 卷积自编码器
  • 栈式自编码器

介绍

自编码器,也称为空竹网络自动关联器,最初由 Hinton 和 PDP 小组于 1980 年代提出。 它们是前馈网络,没有任何反馈,并且它们是通过无监督学习来学习的。 像第 3 章的多人感知机,神经网络感知机一样,它们使用反向传播算法进行学习,但有一个主要区别-目标与输入相同。

我们可以认为自编码器由两个级联网络组成-第一个网络是编码器,它接受输入x,然后使用变换h将其编码为编码信号y

y = h(x)

第二网络使用编码信号y作为其输入,并执行另一个变换f以获得重构信号r

r = f(y) = f(h(x))

我们将误差e定义为原始输入x与重构信号r之间的差,e = x - r。然后,网络通过减少均方误差MSE)进行学习,并且像 MLP 一样,该误差会传播回隐藏层。 下图显示了自编码器,其中编码器和解码器分别突出显示。 自编码器可以具有权重分配,也就是说,解码器和编码器的权重只是彼此的换位,这可以在训练参数数量较少时帮助网络更快地学习,但同时会降低编码器的自由度。 网络。 它们与第 7 章“无监督学习”的 RBM 非常相似,但有一个很大的区别-自编码器中神经元的状态是确定性的,而在 RBM 中,神经元是概率性的:

根据隐藏层的大小,自编码器分为不完整(隐藏层的神经元少于输入层)或过完整(隐藏层的神经元多于输入层)。 。 根据对损失施加的限制/约束,我们有多种类型的自编码器:稀疏自编码器,降噪自编码器和卷积自编码器。 在本章中,您将了解自编码器中的这些变体,并使用 TensorFlow 实现它们。

自编码器的明显应用之一是在降维领域[2]。 结果表明,与 PCA 相比,自编码器产生了更好的结果。 自编码器还可以用于特征提取[3],文档检索[2],分类和异常检测。

另见

  • Rumelhart, David E., Geoffrey E. Hinton, and Ronald J. Williams. Learning internal representations by error propagation. No. ICS-8506. California Univ San Diego La Jolla Inst for Cognitive Science, 1985. (http://www.cs.toronto.edu/~fritz/absps/pdp8.pdf)
  • Hinton, Geoffrey E., and Ruslan R. Salakhutdinov. Reducing the dimensionality of data with neural networks, science 313.5786 (2006): 504-507. (https://pdfs.semanticscholar.org/7d76/b71b700846901ac4ac119403aa737a285e36.pdf)
  • Masci, Jonathan, et al. Stacked convolutional auto-encoders for hierarchical feature extraction. Artificial Neural Networks and Machine Learning–ICANN 2011 (2011): 52-59. (https://www.researchgate.net/profile/Jonathan_Masci/publication/221078713_Stacked_Convolutional_Auto-Encoders_for_Hierarchical_Feature_Extraction/links/0deec518b9c6ed4634000000/Stacked-Convolutional-Auto-Encoders-for-Hierarchical-Feature-Extraction.pdf)
  • Japkowicz, Nathalie, Catherine Myers, and Mark Gluck. A novelty detection approach to classification. IJCAI. Vol. 1. 1995. (http://www.ijcai.org/Proceedings/95-1/Papers/068.pdf)

普通自编码器

Hinton 提出的普通自编码器仅包含一个隐藏层。 隐藏层中神经元的数量少于输入(或输出)层中神经元的数量。 这导致对网络中信息流产生瓶颈效应,因此我们可以将隐藏层视为瓶颈层,从而限制了要存储的信息。 自编码器中的学习包括在隐藏层上开发输入信号的紧凑表示,以便输出层可以忠实地再现原始输入:

具有单个隐藏层的自编码器

准备

此秘籍将使用自编码器进行图像重建; 我们将在 MNIST 数据库上训练自编码器,并将其用于重建测试图像。

操作步骤

我们按以下步骤进行:

  1. 与往常一样,第一步是导入所有必需的模块:
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
%matplotlib inline
  1. 接下来,我们从 TensorFlow 示例中获取 MNIST 数据-这里要注意的重要一点是,标签不是一次性编码的,仅仅是因为我们没有使用标签来训练网络。 自编码器通过无监督学习来学习:
mnist = input_data.read_data_sets("MNIST_data/")
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
  1. 接下来,我们声明一个类AutoEncoder; 该类具有init方法来为自编码器初始化权重,偏差和占位符。 我们还可以使用init方法构建完整的图。 该类还具有用于encoderdecoder,设置会话(set_session)和fit的方法。 我们在此处构建的自编码器使用简单的 MSE 作为loss函数,我们尝试使用AdamOptimizer对其进行优化:
class AutoEncoder(object):
def __init__(self, m, n, eta = 0.01):
"""
m: Number of neurons in input/output layer
n: number of neurons in hidden layer
"""
self._m = m
self._n = n
self.learning_rate = eta
# Create the Computational graph
# Weights and biases
self._W1 = tf.Variable(tf.random_normal(shape=(self._m,self._n)))
self._W2 = tf.Variable(tf.random_normal(shape=(self._n,self._m)))
self._b1 = tf.Variable(np.zeros(self._n).astype(np.float32)) #bias for hidden layer
self._b2 = tf.Variable(np.zeros(self._m).astype(np.float32)) #bias for output layer
# Placeholder for inputs
self._X = tf.placeholder('float', [None, self._m])
self.y = self.encoder(self._X)
self.r = self.decoder(self.y)
error = self._X - self.r
self._loss = tf.reduce_mean(tf.pow(error, 2))
self._opt = tf.train.AdamOptimizer(self.learning_rate).minimize(self._loss)
def encoder(self, x):
h = tf.matmul(x, self._W1) + self._b1
return tf.nn.sigmoid(h)
def decoder(self, x):
h = tf.matmul(x, self._W2) + self._b2
return tf.nn.sigmoid(h)
def set_session(self, session):
self.session = session
def reduced_dimension(self, x):
h = self.encoder(x)
return self.session.run(h, feed_dict={self._X: x})
def reconstruct(self,x):
h = self.encoder(x)
r = self.decoder(h)
return self.session.run(r, feed_dict={self._X: x})
def fit(self, X, epochs = 1, batch_size = 100):
N, D = X.shape
num_batches = N // batch_size
obj = []
for i in range(epochs):
#X = shuffle(X)
for j in range(num_batches):
    batch = X[j * batch_size: (j * batch_size + batch_size)]
    _, ob = self.session.run([self._opt,self._loss], feed_dict={self._X: batch})
    if j % 100 == 0 and i % 100 == 0:
        print('training epoch {0} batch {2} cost {1}'.format(i,ob, j)) 
obj.append(ob)
return obj

为了能够在训练后使用自编码器,我们还定义了两个工具函数:reduced_dimension提供编码器网络的输出,reconstruct重构最终图像。

  1. 我们将输入数据转换为float进行训练,初始化所有变量,然后开始计算会话。 在计算中,我们目前仅测试自编码器的重构能力:
Xtrain = trX.astype(np.float32)
Xtest = teX.astype(np.float32)
_, m = Xtrain.shape
autoEncoder = AutoEncoder(m, 256)
#Initialize all variables
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    autoEncoder.set_session(sess)
    err = autoEncoder.fit(Xtrain, epochs=10)
    out = autoEncoder.reconstruct(Xtest[0:100])
  1. 我们可以通过绘制误差与周期的关系图来验证我们的网络在训练时是否确实优化了 MSE。 为了获得良好的训练,应该使用epochs来减少误差:
plt.plot(err)
plt.xlabel('epochs')
plt.ylabel('cost')

该图如下所示:

我们可以看到,随着网络的学习,损耗/成本正在降低,到我们达到 5,000 个周期时,损耗/成本几乎在一条线上振荡。 这意味着进一步增加周期将是无用的。 如果现在要改善训练,则应该更改超参数,例如学习率,批量大小和使用的优化程序。

  1. 现在让我们看一下重建的图像。 在这里,您可以同时看到由我们的自编码器生成的原始图像和重建图像:
# Plotting original and reconstructed images
row, col = 2, 8
idx = np.random.randint(0, 100, row * col // 2)
f, axarr = plt.subplots(row, col, sharex=True, sharey=True, figsize=(20,4))
for fig, row in zip([Xtest,out], axarr):
    for i,ax in zip(idx,row):
        ax.imshow(fig[i].reshape((28, 28)), cmap='Greys_r')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)

我们得到以下结果:

工作原理

有趣的是,在前面的代码中,我们将输入的尺寸从 784 减少到 256,并且我们的网络仍可以重建原始图像。 让我们比较一下具有相同隐藏层尺寸的 RBM(第 7 章,无监督学习)的表现:

我们可以看到,自编码器重建的图像比 RBM 重建的图像更清晰。 原因是,在自编码器中,还有其他权重(从隐藏层到解码器输出层的权重)需要训练,因此保留了学习知识。 随着自编码器了解更多,即使两者都将信息压缩到相同的尺寸,它的表现也比 RBM 更好。

更多

诸如 PCA 之类的自编码器可以用于降维,但是 PCA 仅可以表示线性变换,但是我们可以在自编码器中使用非线性激活函数,从而在编码中引入非线性。 这是从 Hinton 论文复制的结果,该结果使用神经网络降低了数据的维数。 该结果将 PCA(A)的结果与栈式 RBM 作为具有 784-1000-500-250-2 架构的自编码器的结果进行了比较:

正如我们稍后将看到的,当使用栈式自编码器制作自编码器时,每个自编码器最初都经过单独的预训练,然后对整个网络进行微调以获得更好的表现。

稀疏自编码器

我们在前面的秘籍中看到的自编码器的工作方式更像是一个身份网络-它们只是重构输入。 重点是在像素级别重建图像,唯一的限制是瓶颈层中的单元数; 有趣的是,像素级重建不能确保网络将从数据集中学习抽象特征。 通过添加更多约束,我们可以确保网络从数据集中学习抽象特征。

在稀疏自编码器中,将稀疏惩罚项添加到重构误差中,以确保在任何给定时间触发瓶颈层中较少的单元。 如果m是输入模式的总数,那么我们可以定义一个数量ρ_hat(您可以在 Andrew Ng 的讲座中检查数学细节),它测量每个隐藏层单元的净活动(平均触发多少次)。 基本思想是放置一个约束ρ_hat,使其等于稀疏性参数ρ。这导致损失函数中添加了稀疏性的正则项,因此现在loss函数如下:

loss = Mean squared error + Regularization for sparsity parameter

如果ρ_hat偏离ρ,则此正则化项将对网络造成不利影响;做到这一点的一种标准方法是使用ρρ_hat之间的 Kullback-LeiberKL)差异。

准备

在开始秘籍之前,让我们进一步探讨 KL 的差异,D[KL]。 它是两个分布之间差异的非对称度量,在我们的情况下为ρρ_hat。当ρρ_hat相等时,则为零,否则,当ρ_hatρ分叉时,它单调增加。在数学上,它表示为:

这是固定ρ = 0.3D[KL]的图,我们可以看到当ρ_hat = 0.3时,D[KL] = 0;否则在两端单调增长:

操作步骤

我们按以下步骤进行:

  1. 我们导入必要的模块:
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
%matplotlib inline
  1. 从 TensorFlow 示例中加载 MNIST 数据集:
mnist = input_data.read_data_sets("MNIST_data/")
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
  1. 定义SparseAutoEncoder类,它与前面的秘籍中的AutoEncoder类非常相似,除了引入了 KL 散度损失之外:
def kl_div(self, rho, rho_hat):
 term2_num = tf.constant(1.)- rho
 term2_den = tf.constant(1.) - rho_hat
 kl = self.logfunc(rho,rho_hat) + self.logfunc(term2_num, term2_den)
 return kl
 def logfunc(self, x1, x2):
 return tf.multiply( x1, tf.log(tf.div(x1,x2)))

我们将 KL 约束添加到损失中,如下所示:

alpha = 7.5e-5
kl_div_loss = tf.reduce_sum(self.kl_div(0.02, tf.reduce_mean(self.y,0)))
loss = self._loss + alpha * kl_div_loss

在此,alpha是赋予稀疏性约束的权重。 该类的完整代码如下:

class SparseAutoEncoder(object):
 def __init__(self, m, n, eta = 0.01):
 """
 m: Number of neurons in input/output layer
 n: number of neurons in hidden layer
 """
 self._m = m
 self._n = n
 self.learning_rate = eta
 # Create the Computational graph
 # Weights and biases
 self._W1 = tf.Variable(tf.random_normal(shape=(self._m,self._n)))
 self._W2 = tf.Variable(tf.random_normal(shape=(self._n,self._m)))
 self._b1 = tf.Variable(np.zeros(self._n).astype(np.float32)) #bias for hidden layer
 self._b2 = tf.Variable(np.zeros(self._m).astype(np.float32)) #bias for output layer
 # Placeholder for inputs
 self._X = tf.placeholder('float', [None, self._m])
 self.y = self.encoder(self._X)
 self.r = self.decoder(self.y)
 error = self._X - self.r
 self._loss = tf.reduce_mean(tf.pow(error, 2))
 alpha = 7.5e-5
 kl_div_loss = tf.reduce_sum(self.kl_div(0.02,   tf.reduce_mean(self.y,0)))
 loss = self._loss + alpha * kl_div_loss 
 self._opt = tf.train.AdamOptimizer(self.learning_rate).minimize(loss)
 def encoder(self, x):
 h = tf.matmul(x, self._W1) + self._b1
 return tf.nn.sigmoid(h)
 def decoder(self, x):
 h = tf.matmul(x, self._W2) + self._b2
 return tf.nn.sigmoid(h)
 def set_session(self, session):
 self.session = session
 def reduced_dimension(self, x):
 h = self.encoder(x)
 return self.session.run(h, feed_dict={self._X: x})
 def reconstruct(self,x):
 h = self.encoder(x)
 r = self.decoder(h)
 return self.session.run(r, feed_dict={self._X: x})
 def kl_div(self, rho, rho_hat):
 term2_num = tf.constant(1.)- rho
 term2_den = tf.constant(1.) - rho_hat
 kl = self.logfunc(rho,rho_hat) + self.logfunc(term2_num, term2_den)
 return kl
 def logfunc(self, x1, x2):
 return tf.multiply( x1, tf.log(tf.div(x1,x2)))
 def fit(self, X, epochs = 1, batch_size = 100):
 N, D = X.shape
 num_batches = N // batch_size
 obj = []
 for i in range(epochs):
     #X = shuffle(X)
     for j in range(num_batches):
         batch = X[j * batch_size: (j * batch_size + batch_size)]
         _, ob = self.session.run([self._opt,self._loss], feed_dict={self._X: batch})
         if j % 100 == 0:
             print('training epoch {0} batch {2} cost {1}'.format(i,ob, j)) 
obj.append(ob)
 return obj
  1. 接下来,我们声明SparseAutoEncoder类的对象,对训练数据进行拟合,并计算重建的图像:
Xtrain = trX.astype(np.float32)
Xtest = teX.astype(np.float32)
_, m = Xtrain.shape
sae = SparseAutoEncoder(m, 256)
#Initialize all variables
init = tf.global_variables_initializer()
with tf.Session() as sess:
 sess.run(init)
 sae.set_session(sess)
 err = sae.fit(Xtrain, epochs=10)
 out = sae.reconstruct(Xtest[0:100])
  1. 让我们看看随着网络学习,均方重构损失的变化:
plt.plot(err)
plt.xlabel('epochs')
plt.ylabel('Reconstruction Loss (MSE)')

情节如下:

  1. 让我们看一下重建的图像:
# Plotting original and reconstructed images
row, col = 2, 8
idx = np.random.randint(0, 100, row * col // 2)
f, axarr = plt.subplots(row, col, sharex=True, sharey=True, figsize=(20,4))
for fig, row in zip([Xtest,out], axarr):
    for i,ax in zip(idx,row):
        ax.imshow(fig[i].reshape((28, 28)), cmap='Greys_r')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)

我们得到以下结果:

工作原理

您必须已经注意到,稀疏自编码器的主要代码与普通自编码器的主要代码完全相同,这是因为稀疏自编码器只有一个主要变化-增加了 KL 发散损耗以确保稀疏性。 隐藏的(瓶颈)层。 但是,如果比较这两个重构,则可以发现即使在隐藏层中具有相同数量的单元,稀疏自编码器也比标准编码器好得多:

训练 MNIST 数据集的原始自编码器后的重建损失为 0.022,而稀疏自编码器则为 0.006。 因此,添加约束会迫使网络学习数据的隐藏表示。

更多

输入的紧凑表示形式以权重存储; 让我们可视化网络学习到的权重。 这分别是标准自编码器和稀疏自编码器的编码器层的权重。 我们可以看到,在标准自编码器中,许多隐藏单元的权重非常大,表明它们工作过度:

另见

去噪自编码器

我们在前两个秘籍中探讨过的两个自编码器是未完成的自编码器的示例,因为与输入(输出)层相比,它们中的隐藏层具有较低的尺寸。 去噪自编码器属于过完整自编码器的类别,因为当隐藏层的尺寸大于输入层的尺寸时,它会更好地工作。

去噪自编码器从损坏的(嘈杂的)输入中学习; 它为编码器网络提供噪声输入,然后将来自解码器的重建图像与原始输入进行比较。 这个想法是,这将帮助网络学习如何去噪输入。 它不再只是按像素进行比较,而是为了进行去噪,还将学习相邻像素的信息。

准备

去噪自编码器还将具有 KL 散度惩罚项; 它在两个主要方面与先前秘籍的稀疏自编码器有所不同。 首先,n_hidden > m瓶颈层中的隐藏单元数大于输入层m中的单元数n_hidden > m。 其次,编码器的输入已损坏。 为了在 TensorFlow 中做到这一点,我们添加了invalid函数,这给输入增加了噪音:

def corruption(x, noise_factor = 0.3): #corruption of the input
    noisy_imgs = x + noise_factor * np.random.randn(*x.shape)
    noisy_imgs = np.clip(noisy_imgs, 0., 1.)
    return noisy_imgs

操作步骤

  1. 像往常一样,第一步是导入必要的模块-TensorFlow,numpy 来操纵输入数据,matplotlib 来进行绘制,等等:
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import math
%matplotlib inline
  1. 从 TensorFlow 示例中加载数据。 在本章的所有秘籍中,我们都使用标准的 MNIST 数据库进行说明,以便为您提供不同自编码器之间的基准。
mnist = input_data.read_data_sets("MNIST_data/")
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
  1. 接下来,我们定义此秘籍的主要组成部分DenoisingAutoEncoder类。 该类与我们在前面的秘籍中创建的SparseAutoEncoder类非常相似。 在这里,我们为噪点图像添加了一个占位符; 该噪声输入被馈送到编码器。 现在,当输入的是噪点图像时,重构误差就是原始清晰图像与解码器输出之间的差。 我们在此保留稀疏惩罚条款。 因此,拟合函数将原始图像和噪声图像都作为其参数。
class DenoisingAutoEncoder(object):
def __init__(self, m, n, eta = 0.01):
"""
m: Number of neurons in input/output layer
n: number of neurons in hidden layer
"""
self._m = m
self._n = n
self.learning_rate = eta
# Create the Computational graph
# Weights and biases
self._W1 = tf.Variable(tf.random_normal(shape=(self._m,self._n)))
self._W2 = tf.Variable(tf.random_normal(shape=(self._n,self._m)))
self._b1 = tf.Variable(np.zeros(self._n).astype(np.float32)) #bias for hidden layer
self._b2 = tf.Variable(np.zeros(self._m).astype(np.float32)) #bias for output layer
# Placeholder for inputs
self._X = tf.placeholder('float', [None, self._m])
self._X_noisy = tf.placeholder('float', [None, self._m])
self.y = self.encoder(self._X_noisy)
self.r = self.decoder(self.y)
error = self._X - self.r
self._loss = tf.reduce_mean(tf.pow(error, 2))
#self._loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels =self._X, logits = self.r))
alpha = 0.05
kl_div_loss = tf.reduce_sum(self.kl_div(0.02, tf.reduce_mean(self.y,0)))
loss = self._loss + alpha * kl_div_loss 
self._opt = tf.train.AdamOptimizer(self.learning_rate).minimize(loss)
def encoder(self, x):
h = tf.matmul(x, self._W1) + self._b1
return tf.nn.sigmoid(h)
def decoder(self, x):
h = tf.matmul(x, self._W2) + self._b2
return tf.nn.sigmoid(h)
def set_session(self, session):
self.session = session
def reconstruct(self,x):
h = self.encoder(x)
r = self.decoder(h)
return self.session.run(r, feed_dict={self._X: x})
def kl_div(self, rho, rho_hat):
term2_num = tf.constant(1.)- rho
term2_den = tf.constant(1.) - rho_hat
kl = self.logfunc(rho,rho_hat) + self.logfunc(term2_num, term2_den)
return kl
def logfunc(self, x1, x2):
return tf.multiply( x1, tf.log(tf.div(x1,x2)))
def corrupt(self,x):
return x * tf.cast(tf.random_uniform(shape=tf.shape(x), minval=0,maxval=2),tf.float32)
def getWeights(self):
return self.session.run([self._W1, self._W2,self._b1, self._b2])
def fit(self, X, Xorg, epochs = 1, batch_size = 100):
N, D = X.shape
num_batches = N // batch_size
obj = []
for i in range(epochs):
#X = shuffle(X)
for j in range(num_batches):
batch = X[j * batch_size: (j * batch_size + batch_size)]
batchO = Xorg[j * batch_size: (j * batch_size + batch_size)]
_, ob = self.session.run([self._opt,self._loss], feed_dict={self._X: batchO, self._X_noisy: batch})
if j % 100 == 0:
print('training epoch {0} batch {2} cost {1}'.format(i,ob, j)) 
obj.append(ob)
return obj

也可以向自编码器对象添加噪声。 在这种情况下,您将使用类self._X_noisy = self.corrupt(self._X) * 0.3 + self._X * (1 - 0.3)中定义的损坏方法,并且fit方法也将更改为以下内容:

def fit(self, X, epochs = 1, batch_size = 100):
        N, D = X.shape
        num_batches = N // batch_size
        obj = []
        for i in range(epochs):
            #X = shuffle(X)
            for j in range(num_batches):
                batch = X[j * batch_size: (j * batch_size + batch_size)]
                _, ob = self.session.run([self._opt,self._loss], feed_dict={self._X: batch})
                if j % 100 == 0:
                    print('training epoch {0} batch {2} cost {1}'.format(i,ob, j)) 
                obj.append(ob)
        return obj
  1. 现在,我们使用前面定义的损坏函数来生成一个嘈杂的图像并将其提供给会话:
n_hidden = 800
Xtrain = trX.astype(np.float32)
Xtrain_noisy = corruption(Xtrain).astype(np.float32)
Xtest = teX.astype(np.float32)
#noise = Xtest * np.random.randint(0, 2, Xtest.shape).astype(np.float32)
Xtest_noisy = corruption(Xtest).astype(np.float32) #Xtest * (1-0.3)+ noise *(0.3)
_, m = Xtrain.shape
dae = DenoisingAutoEncoder(m, n_hidden)
#Initialize all variables
init = tf.global_variables_initializer()
with tf.Session() as sess:
 sess.run(init)
 dae.set_session(sess)
 err = dae.fit(Xtrain_noisy, Xtrain, epochs=10)
 out = dae.reconstruct(Xtest_noisy[0:100])
 W1, W2, b1, b2 = dae.getWeights()
 red = dae.reduced_dimension(Xtrain)
  1. 随着网络的学习,重建损失减少:
plt.plot(err)
plt.xlabel('epochs')
plt.ylabel('Reconstruction Loss (MSE)')

情节如下:

  1. 当来自测试数据集的嘈杂图像呈现给训练网络时,重建图像如下:
# Plotting original and reconstructed images
row, col = 2, 8
idx = np.random.randint(0, 100, row * col // 2)
f, axarr = plt.subplots(row, col, sharex=True, sharey=True, figsize=(20,4))
for fig, row in zip([Xtest_noisy,out], axarr):
 for i,ax in zip(idx,row):
 ax.imshow(fig[i].reshape((28, 28)), cmap='Greys_r')
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)

我们得到以下结果:

另见

卷积自编码器

研究人员发现卷积神经网络CNN)与图像效果最佳,因为它们可以提取隐藏在图像中的空间信息。 因此,很自然地假设,如果编码器和解码器网络由 CNN 组成,它将比其余的自编码器更好地工作,因此我们有了卷积自编码器CAE)。 在第 4 章“卷积神经网络”中,说明了卷积和最大池化的过程,我们将以此为基础来了解卷积自编码器的工作原理。

CAE 是其中编码器和解码器均为 CNN 网络的一种 CAE。 编码器的卷积网络学习将输入编码为一组信号,然后解码器 CNN 尝试从中重建输入。 它们充当通用特征提取器,并学习从输入捕获特征所需的最佳过滤器。

准备

从第 4 章“卷积神经网络”中,您了解到,随着添加卷积层,传递到下一层的信息在空间范围上会减少,但是在自编码器中,重建的图像应该有输入图像的相同的大小和深度。 这意味着解码器应以某种方式对图像进行大小调整和卷积以重建原始图像。 与卷积一起增加空间范围的一种方法是借助转置的卷积层。 通过tf.nn.conv2d_transpose可以轻松地在 TensorFlow 中实现这些功能,但是发现转置的卷积层会在最终图像中产生伪像。 奥古斯都·奥德纳(Augustus Odena)等。 [1]在他们的工作中表明,可以通过使用最近邻或双线性插值(上采样)再加上卷积层来调整层的大小来避免这些伪像。 他们通过tf.image.resize_images实现了最近邻插值,取得了最佳结果; 我们将在此处采用相同的方法。

操作步骤

  1. 与往常一样,第一步包括必要的模块:
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import math
%matplotlib inline
  1. 加载输入数据:
mnist = input_data.read_data_sets("MNIST_data/")
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
  1. 定义网络参数。 在这里,我们还计算每个最大池层的输出的空间尺寸。 我们需要以下信息来对解码器网络中的图像进行升采样:
# Network Parameters
h_in, w_in = 28, 28 # Image size height and width
k = 3 # Kernel size
p = 2 # pool
s = 2 # Strides in maxpool
filters = {1:32,2:32,3:16}
activation_fn=tf.nn.relu
# Change in dimensions of image after each MaxPool
h_l2, w_l2 = int(np.ceil(float(h_in)/float(s))) , int(np.ceil(float(w_in)/float(s))) # Height and width: second encoder/decoder layer
h_l3, w_l3 = int(np.ceil(float(h_l2)/float(s))) , int(np.ceil(float(w_l2)/float(s))) # Height and width: third encoder/decoder layer
  1. 为输入(嘈杂的图像)和目标(对应的清晰图像)创建占位符:
X_noisy = tf.placeholder(tf.float32, (None, h_in, w_in, 1), name='inputs')
X = tf.placeholder(tf.float32, (None, h_in, w_in, 1), name='targets')
  1. 建立编码器和解码器网络:
### Encoder
conv1 = tf.layers.conv2d(X_noisy, filters[1], (k,k), padding='same', activation=activation_fn)
# Output size h_in x w_in x filters[1]
maxpool1 = tf.layers.max_pooling2d(conv1, (p,p), (s,s), padding='same')
# Output size h_l2 x w_l2 x filters[1] 
conv2 = tf.layers.conv2d(maxpool1, filters[2], (k,k), padding='same', activation=activation_fn)
# Output size h_l2 x w_l2 x filters[2] 
maxpool2 = tf.layers.max_pooling2d(conv2,(p,p), (s,s), padding='same')
# Output size h_l3 x w_l3 x filters[2] 
conv3 = tf.layers.conv2d(maxpool2,filters[3], (k,k), padding='same', activation=activation_fn)
# Output size h_l3 x w_l3 x filters[3]
encoded = tf.layers.max_pooling2d(conv3, (p,p), (s,s), padding='same')
# Output size h_l3/s x w_l3/s x filters[3] Now 4x4x16
### Decoder
upsample1 = tf.image.resize_nearest_neighbor(encoded, (h_l3,w_l3))
# Output size h_l3 x w_l3 x filters[3]
conv4 = tf.layers.conv2d(upsample1, filters[3], (k,k), padding='same', activation=activation_fn)
# Output size h_l3 x w_l3 x filters[3]
upsample2 = tf.image.resize_nearest_neighbor(conv4, (h_l2,w_l2))
# Output size h_l2 x w_l2 x filters[3] 
conv5 = tf.layers.conv2d(upsample2, filters[2], (k,k), padding='same', activation=activation_fn)
# Output size h_l2 x w_l2 x filters[2] 
upsample3 = tf.image.resize_nearest_neighbor(conv5, (h_in,w_in))
# Output size h_in x w_in x filters[2]
conv6 = tf.layers.conv2d(upsample3, filters[1], (k,k), padding='same', activation=activation_fn)
# Output size h_in x w_in x filters[1]
logits = tf.layers.conv2d(conv6, 1, (k,k) , padding='same', activation=None)
# Output size h_in x w_in x 1
decoded = tf.nn.sigmoid(logits, name='decoded')
loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=X, logits=logits)
cost = tf.reduce_mean(loss)
opt = tf.train.AdamOptimizer(0.001).minimize(cost)
  1. 启动会话:
sess = tf.Session()
  1. 将模型拟合给定输入:
epochs = 10
batch_size = 100
# Set's how much noise we're adding to the MNIST images
noise_factor = 0.5
sess.run(tf.global_variables_initializer())
err = []
for i in range(epochs):
 for ii in range(mnist.train.num_examples//batch_size):
 batch = mnist.train.next_batch(batch_size)
 # Get images from the batch
 imgs = batch[0].reshape((-1, h_in, w_in, 1))
 # Add random noise to the input images
 noisy_imgs = imgs + noise_factor * np.random.randn(*imgs.shape)
 # Clip the images to be between 0 and 1
 noisy_imgs = np.clip(noisy_imgs, 0., 1.)
 # Noisy images as inputs, original images as targets
 batch_cost, _ = sess.run([cost, opt], feed_dict={X_noisy: noisy_imgs,X: imgs})
 err.append(batch_cost)
 if ii%100 == 0:
 print("Epoch: {0}/{1}... Training loss {2}".format(i, epochs, batch_cost))
  1. 网络学习到的误差如下:
plt.plot(err)
plt.xlabel('epochs')
plt.ylabel('Cross Entropy Loss')

绘图如下:

  1. 最后,让我们看一下重建的图像:
fig, axes = plt.subplots(rows=2, cols=10, sharex=True, sharey=True, figsize=(20,4))
in_imgs = mnist.test.images[:10]
noisy_imgs = in_imgs + noise_factor * np.random.randn(*in_imgs.shape)
noisy_imgs = np.clip(noisy_imgs, 0., 1.)
reconstructed = sess.run(decoded, feed_dict={X_noisy: noisy_imgs.reshape((10, 28, 28, 1))})
for images, row in zip([noisy_imgs, reconstructed], axes):
 for img, ax in zip(images, row):
 ax.imshow(img.reshape((28, 28)), cmap='Greys_r')
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)

这是前面代码的输出:

  1. 关闭会话:
sess.close()

工作原理

前面的 CAE 是降噪 CAE,与仅由一个瓶颈层组成的简单降噪自编码器相比,我们可以看到它在降噪图像方面更好。

更多

研究人员已将 CAE 用于语义分割。 有趣的读物是 Badrinayanan 等人在 2015 年发表的论文 Segnet:一种用于图像分割的深度卷积编码器-解码器架构。 该网络使用 VGG16 的卷积层作为其编码器网络,并包含一层解码器,每个解码器对应一个解码器层次作为其解码器网络。 解码器使用从相应的编码器接收的最大池索引,并对输入特征图执行非线性上采样。 本文的链接在本秘籍的另请参见部分以及 GitHub 链接中给出。

另见

  1. https://distill.pub/2016/deconv-checkerboard/
  2. https://pgaleone.eu/neural-networks/2016/11/24/convolutional-autoencoders/
  3. https://arxiv.org/pdf/1511.00561.pdf
  4. https://github.com/arahusky/Tensorflow-Segmentation

栈式自编码器

到目前为止,涵盖的自编码器(CAE 除外)仅由单层编码器和单层解码器组成。 但是,我们可能在编码器和解码器网络中具有多层; 使用更深的编码器和解码器网络可以使自编码器表示复杂的特征。 这样获得的结构称为栈式自编码器(深度自编码器); 由一个编码器提取的特征将作为输入传递到下一个编码器。 可以将栈式自编码器作为一个整体网络进行训练,以最大程度地减少重构误差,或者可以首先使用您先前学习的无监督方法对每个单独的编码器/解码器网络进行预训练,然后对整个网络进行微调。 已经指出,通过预训练,也称为贪婪分层训练,效果更好。

准备

在秘籍中,我们将使用贪婪分层方法来训练栈式自编码器; 为了简化任务,我们将使用共享权重,因此相应的编码器/解码器权重将相互转换。

操作步骤

我们按以下步骤进行:

  1. 第一步是导入所有必要的模块:
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
%matplotlib inline
  1. 加载数据集:
mnist = input_data.read_data_sets("MNIST_data/")
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
  1. 接下来,我们定义类StackedAutoencoder__init__类方法包含一个列表,该列表包含从第一个输入自编码器和学习率开始的每个自编码器中的许多神经元。 由于每一层的输入和输出都有不同的尺寸,因此我们选择字典数据结构来表示每一层的权重,偏差和输入:
class StackedAutoEncoder(object):
 def __init__(self, list1, eta = 0.02):
 """
 list1: [input_dimension, hidden_layer_1, ....,hidden_layer_n]
 """
 N = len(list1)-1
 self._m = list1[0]
 self.learning_rate = eta
 # Create the Computational graph
 self._W = {}
 self._b = {}
 self._X = {}
 self._X['0'] = tf.placeholder('float', [None, list1[0]])
 for i in range(N):
 layer = '{0}'.format(i+1)
 print('AutoEncoder Layer {0}: {1} --> {2}'.format(layer, list1[i], list1[i+1]))
 self._W['E' + layer] = tf.Variable(tf.random_normal(shape=(list1[i], list1[i+1])),name='WtsEncoder'+layer)
 self._b['E'+ layer] = tf.Variable(np.zeros(list1[i+1]).astype(np.float32),name='BiasEncoder'+layer)
 self._X[layer] = tf.placeholder('float', [None, list1[i+1]])
 self._W['D' + layer] = tf.transpose(self._W['E' + layer]) # Shared weights
 self._b['D' + layer] = tf.Variable(np.zeros(list1[i]).astype(np.float32),name='BiasDecoder' + layer)
 # Placeholder for inputs
 self._X_noisy = tf.placeholder('float', [None, self._m])
  1. 我们建立一个计算图来定义每个自编码器的优化参数,同时进行预训练。 当先前的自编码器的编码器的输出为其输入时,它涉及为每个自编码器定义重建损耗。 为此,我们定义类方法pretrainone_pass,它们分别为每个栈式自编码器返回训练操作器和编码器的输出:
self.train_ops = {}
 self.out = {}
 for i in range(N):
 layer = '{0}'.format(i+1)
 prev_layer = '{0}'.format(i)
 opt = self.pretrain(self._X[prev_layer], layer)
 self.train_ops[layer] = opt
 self.out[layer] = self.one_pass(self._X[prev_layer], self._W['E'+layer], self._b['E'+layer], self._b['D'+layer])
  1. 我们建立计算图以对整个栈式自编码器进行微调。 为此,我们使用类方法encoderdecoder
self.y = self.encoder(self._X_noisy,N) #Encoder output 
self.r = self.decoder(self.y,N) # Decoder ouput
optimizer = tf.train.AdamOptimizer(self.learning_rate) 
error = self._X['0'] - self.r # Reconstruction Error
self._loss = tf.reduce_mean(tf.pow(error, 2))
self._opt = optimizer.minimize(self._loss)
  1. 最后,我们定义类方法fit,以执行每个自编码器的分批预训练,然后进行微调。 在进行预训练时,我们使用未损坏的输入,而对于微调,我们使用损坏的输入。 这使我们能够使用栈式自编码器甚至从嘈杂的输入中进行重构:
def fit(self, Xtrain, Xtr_noisy, layers, epochs = 1, batch_size = 100):
 N, D = Xtrain.shape
 num_batches = N // batch_size
 X_noisy = {}
 X = {}
 X_noisy ['0'] = Xtr_noisy
 X['0'] = Xtrain
 for i in range(layers):
 Xin = X[str(i)]
 print('Pretraining Layer ', i+1)
 for e in range(5):
 for j in range(num_batches):
 batch = Xin[j * batch_size: (j * batch_size + batch_size)]
 self.session.run(self.train_ops[str(i+1)], feed_dict= {self._X[str(i)]: batch})
 print('Pretraining Finished')
 X[str(i+1)] = self.session.run(self.out[str(i+1)], feed_dict = {self._X[str(i)]: Xin})
 obj = []
 for i in range(epochs):
 for j in range(num_batches):
 batch = Xtrain[j * batch_size: (j * batch_size + batch_size)]
 batch_noisy = Xtr_noisy[j * batch_size: (j * batch_size + batch_size)]
 _, ob = self.session.run([self._opt,self._loss], feed_dict={self._X['0']: batch, self._X_noisy: batch_noisy})
 if j % 100 == 0 :
 print('training epoch {0} batch {2} cost {1}'.format(i,ob, j)) 
 obj.append(ob)
 return obj
  1. 不同的类方法如下:
def encoder(self, X, N):
 x = X
 for i in range(N):
 layer = '{0}'.format(i+1)
 hiddenE = tf.nn.sigmoid(tf.matmul(x, self._W['E'+layer]) + self._b['E'+layer])
 x = hiddenE
 return x
def decoder(self, X, N):
 x = X
 for i in range(N,0,-1):
 layer = '{0}'.format(i)
 hiddenD = tf.nn.sigmoid(tf.matmul(x, self._W['D'+layer]) + self._b['D'+layer])
 x = hiddenD
 return x
def set_session(self, session):
 self.session = session
def reconstruct(self,x, n_layers):
 h = self.encoder(x, n_layers)
 r = self.decoder(h, n_layers)
 return self.session.run(r, feed_dict={self._X['0']: x})
def pretrain(self, X, layer ):
 y = tf.nn.sigmoid(tf.matmul(X, self._W['E'+layer]) + self._b['E'+layer])
 r =tf.nn.sigmoid(tf.matmul(y, self._W['D'+layer]) + self._b['D'+layer])
 # Objective Function
 error = X - r # Reconstruction Error
  loss = tf.reduce_mean(tf.pow(error, 2))
 opt = tf.train.AdamOptimizer(.001).minimize(loss, var_list = 
 [self._W['E'+layer],self._b['E'+layer],self._b['D'+layer]])
  return opt
def one_pass(self, X, W, b, c):
 h = tf.nn.sigmoid(tf.matmul(X, W) + b)
 return h
  1. 我们使用降噪自编码器秘籍中定义的破坏函数来破坏图像,最后创建一个StackAutoencoder并对其进行训练:
Xtrain = trX.astype(np.float32)
Xtrain_noisy = corruption(Xtrain).astype(np.float32)
Xtest = teX.astype(np.float32)
Xtest_noisy = corruption(Xtest).astype(np.float32) 
_, m = Xtrain.shape
list1 = [m, 500, 50] # List with number of neurons in Each hidden layer, starting from input layer
n_layers = len(list1)-1
autoEncoder = StackedAutoEncoder(list1)
#Initialize all variables
init = tf.global_variables_initializer()
with tf.Session() as sess:
 sess.run(init)
 autoEncoder.set_session(sess)
 err = autoEncoder.fit(Xtrain, Xtrain_noisy, n_layers, epochs=30)
 out = autoEncoder.reconstruct(Xtest_noisy[0:100],n_layers)
  1. 这里给出了随着堆叠自编码器的微调,重建误差与历时的关系。 您可以看到,由于进行了预训练,我们已经从非常低的重建损失开始了:
plt.plot(err)
plt.xlabel('epochs')
plt.ylabel('Fine Tuning Reconstruction Error')

情节如下:

  1. 现在让我们检查网络的表现。 当网络中出现嘈杂的测试图像时,这是去噪后的手写图像:

工作原理

在栈式自编码器上进行的实验表明,应以较低的学习率值进行预训练。 这样可以确保在微调期间具有更好的收敛性和表现。

更多

整章都是关于自编码器的,尽管目前它们仅用于降维和信息检索,但它们引起了很多兴趣。 首先,因为它们不受监督,其次,因为它们可以与 FCN 一起使用。 它们可以帮助我们应对维度的诅咒。 研究人员已经证明,它们也可以用于分类和异常检测。

另见

九、强化学习

本章介绍强化学习RL)-学习最少,但最有前途的学习范例。 本章包括以下主题:

  • 了解 OpenAI Gym
  • 实现神经网络智能体来玩吃豆人
  • 用 Q 学习平衡推车
  • 深度 Q 网络和 Atari 游戏
  • 用策略梯度玩 Pong 游戏

介绍

2016 年 3 月,由 Google DeepMind 制作的程序 AlphaGo 以 4 比 1 击败了世界上最好的围棋选手,十八届世界冠军李·塞多尔(Lee Sedol)。 ,具有:

208,168,199,381,979,984,699,478,633,344,862,770,286,522,
453,884,530,548,425,639,456,820,927,419,612,738,015,378,
525,648,451,698,519,643,907,259,916,015,628,128,546,089,
888,314,427, 129,715,319,317,557,736,620,397,247,064,840,935

可能的法律委员会职位。 玩和赢得围棋无法通过简单的蛮力完成。 它需要技巧,创造力,以及专业围棋选手所说的直觉。

AlphaGo 在基于 RL 算法的深度神经网络与最先进的树搜索算法相结合的帮助下实现了这一非凡的成就。 本章介绍 RL 和我们用于执行 RL 的一些算法。

因此,出现的第一个问题是什么是 RL,它与我们在前几章中探讨的有监督和无监督学习有何不同?

拥有宠物的任何人都知道,训练宠物的最佳策略是奖励其期望的行为,并惩罚其不良行为。 RL 也称为与批评者进行的学习,它是一种学习范例,其中智能体以相同的方式进行学习。 这里的智能体对应我们的网络(程序); 它可以执行一组动作a),这会导致环境的状态s)发生变化。 ,则智能体会感知其是否获得奖励或惩罚。

例如,在狗的情况下,狗是我们的智能体,狗的自愿肌肉运动是动作,地面是环境。 狗给我们骨头作为奖励,从而感觉到我们对其动作的反应:

改编自强化学习:萨顿(Sutton)和巴托(Barto)的引言即使我们的大脑在前脑底部有一组称为“基底神经节”的皮层下核,根据神经科学,它们负责选择动作,即帮助 我们决定在任何给定时间执行几个可能的动作中的哪一个。

智能体的目的是使报酬最大化并减少惩罚。 做出此决策涉及各种挑战,最重要的挑战是如何最大化未来的回报,也称为临时得分分配问题。 智能体根据某些策略(π)决定其操作; 智能体根据其与环境的交互来学习此策略(π)。 有各种策略学习算法; 我们将在本章中探索其中的一些。 智能体通过反复试验的过程来推断最优策略(π*),并且要学习最优策略,智能体需要与之交互的环境; 我们将使用提供不同环境的 OpenAI Gym。

在这里,我们仅对 RL 中涉及的基本概念进行了回顾; 我们假设您熟悉马尔可夫概念的决策过程,折现因子和值函数(状态值和动作值)。

在本章以及随后的秘籍中,我们将一集定义为游戏的一次运行,例如,解决一个数独游戏。 通常,智能体会播放许多剧集以学习一种最佳策略,该策略可使奖励最大化。

看到 RL 特工在没有任何游戏隐性知识的情况下,如何在这些游戏中学会玩游戏,不仅玩游戏,甚至击败人类,真是太神奇了。

了解 OpenAI Gym

我们将使用 OpenAI Gym 为我们的智能体提供一个环境。 OpenAI Gym 是一个开源工具包,用于开发和比较 RL 算法。 它包含各种模拟环境,可用于训练智能体和开发新的 RL 算法。

准备

首先要做的是安装 OpenAI Gym; 使用pip install gym可以完成最少的安装。 OpenAI 运动场提供了多种环境,例如 Atari,棋盘游戏以及 2D 或 3D 物理引擎。 最小安装可在 Windows 上运行,并且仅支持基本环境-算法,toy_textclassic_control-但如果您要探索其他环境,则它们将需要更多的依赖项。 OSX 和 Ubuntu 支持完整版本。 可以在 OpenAI Gym 的 GitHub 链接上阅读详细说明。

操作步骤

让我们从秘籍开始:

  1. OpenAI Gym 提供的核心接口是统一环境接口。 智能体可以使用三种基本方法与环境进行交互,即重置,逐步和渲染。 reset方法重置环境并返回观察值。 step方法将环境步进一个时间步,并返回观察,奖励,完成和信息。 render方法呈现一帧环境,例如弹出一个窗口。
  2. 要使用 OpenAI Gym,您需要先将其导入:
import gym
  1. 接下来,我们创建我们的第一个环境:
env_name = 'Breakout-v3'
env = gym.make(env_name)
  1. 我们使用reset方法启动环境:
obs = env.reset()
  1. 让我们检查一下环境的形状:
print(obs.shape)
  1. 可以使用命令actions = env.action_space检查可能的操作数量。 从此结果可以看出,对于 Breakout-v4,我们有四个可能的动作:NoOpFireLeftRight。 可以通过调用env.action_space.n命令获得操作总数。
  2. 让我们定义一个具有随机策略的智能体。 智能体会随机选择四个可能的动作中的任何一个:
def random_policy(n):
 action = np.random.randint(0,n)
 return action
  1. 接下来,我们使用obs, reward, done, info = env.step(action)允许我们的随机智能体播放 1,000 步:
for step in range(1000): # 1000 steps max
 action = random_policy(env.action_space.n)
 obs, reward, done, info = env.step(action)
 env.render()
 if done:
     img = env.render(mode='rgb_array')
     plt.imshow(img)
     plt.show()
     print("The game is over in {} steps".format(step))
     break

obs告诉智能体程序环境是什么样的; 对于我们的环境,它对应于大小为210 x 160 x 3的 RGB 图像。在每个步骤中,智能体将获得 0 或 1 奖励,根据 OpenAI Gym Wiki,其reward[-inf, inf]。 游戏结束后,环境将done返回为Trueinfo可用于调试,但智能体不使用。 env.render()命令弹出一个窗口,显示环境的当前状态。 当包含此命令时,您可以通过弹出窗口查看座席如何尝试玩耍和学习。 最好在座席接受训练时对此进行评论,以节省时间。

  1. 最后,关闭环境:
env.close()

工作原理

前面的代码实现了一个随机智能体; 智能体会随机选择以下四个动作之一:

要观察的另一件重要事情是,在这种环境中,动作空间是离散的,而观察空间是盒子类型的。 OpenAI 中关于空间(动作/观察)的术语“离散”和“框”是指允许的值。 离散空间允许固定范围的非负数,在我们的情况下为(0,1,2,3)。 另一方面,盒子空间表示一个n维盒子,因此对于吃豆人来说,任何有效观察结果都是210×160×3数字的数组。

更多

OpenAI Gym 由许多不同的环境组成,其活跃的贡献社区在其中添加了许多环境。 要获取所有现有环境的列表,可以运行以下简单代码

from gym import envs
env_ids = [spec.id for spec in envs.registry.all()]
print("Total Number of environments are", len(env_ids))
for env_id in sorted(env_ids):
    print(env_id)

目前,OpenAI Gym 共有 777 个环境。 这是吃豆人使用与之前相同的随机智能体的图像:

另见

  • 可以从这里获取有关不同环境的详细信息。
  • 这个链接中为某些环境维护了 Wiki 页面
  • 可以从这个链接获得有关安装说明和依赖项的详细信息。

实现神经网络智能体来玩吃豆人

让我们首先构建一个简单的神经网络智能体来玩“吃豆人”游戏。 我们将创建具有一组随机权重和偏差的智能体。 然后,这些特工将尝试玩游戏; 我们选择能够发挥最长平均持续时间的特工,并假设他们是最佳策略。

准备

此秘籍中的智能体没有学习任何策略; 他们根据初始权重(固定策略)做出决策。 智能体根据神经网络给出的概率来选择动作。 每个智能体做出的决定仅基于对环境的当前观察。

我们通过完全连接的神经网络来实现。 NN 的输入由环境的观察空间决定; 输出神经元的数量由可能的离散动作的数量决定。 吃豆人游戏有九种可能的离散动作-无操作,右转,左转,上转,向下转,左移,右移,上移和下移-因此我们的 NN 具有九个输出神经元。

TensorFlow 1.x 深度学习秘籍:6~10(2)https://developer.aliyun.com/article/1426773

相关文章
|
1月前
|
机器学习/深度学习 TensorFlow 算法框架/工具
深度学习之格式转换笔记(三):keras(.hdf5)模型转TensorFlow(.pb) 转TensorRT(.uff)格式
将Keras训练好的.hdf5模型转换为TensorFlow的.pb模型,然后再转换为TensorRT支持的.uff格式,并提供了转换代码和测试步骤。
85 3
深度学习之格式转换笔记(三):keras(.hdf5)模型转TensorFlow(.pb) 转TensorRT(.uff)格式
|
8天前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
垃圾识别分类系统。本系统采用Python作为主要编程语言,通过收集了5种常见的垃圾数据集('塑料', '玻璃', '纸张', '纸板', '金属'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对图像数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。然后使用Django搭建Web网页端可视化操作界面,实现用户在网页端上传一张垃圾图片识别其名称。
36 0
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
|
8天前
|
机器学习/深度学习 人工智能 算法
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
手写数字识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法。并通过对数据集进行训练,最后得到一个识别精度较高的模型。并基于Flask框架,开发网页端操作平台,实现用户上传一张图片识别其名称。
28 0
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
|
8天前
|
机器学习/深度学习 人工智能 算法
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
蔬菜识别系统,本系统使用Python作为主要编程语言,通过收集了8种常见的蔬菜图像数据集('土豆', '大白菜', '大葱', '莲藕', '菠菜', '西红柿', '韭菜', '黄瓜'),然后基于TensorFlow搭建卷积神经网络算法模型,通过多轮迭代训练最后得到一个识别精度较高的模型文件。在使用Django开发web网页端操作界面,实现用户上传一张蔬菜图片识别其名称。
43 0
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
|
24天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
71 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
1月前
|
机器学习/深度学习 人工智能 算法
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
玉米病害识别系统,本系统使用Python作为主要开发语言,通过收集了8种常见的玉米叶部病害图片数据集('矮花叶病', '健康', '灰斑病一般', '灰斑病严重', '锈病一般', '锈病严重', '叶斑病一般', '叶斑病严重'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。再使用Django搭建Web网页操作平台,实现用户上传一张玉米病害图片识别其名称。
56 0
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
|
2月前
|
机器学习/深度学习 算法 TensorFlow
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
交通标志识别系统。本系统使用Python作为主要编程语言,在交通标志图像识别功能实现中,基于TensorFlow搭建卷积神经网络算法模型,通过对收集到的58种常见的交通标志图像作为数据集,进行迭代训练最后得到一个识别精度较高的模型文件,然后保存为本地的h5格式文件。再使用Django开发Web网页端操作界面,实现用户上传一张交通标志图片,识别其名称。
104 6
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
|
1月前
|
机器学习/深度学习 移动开发 TensorFlow
深度学习之格式转换笔记(四):Keras(.h5)模型转化为TensorFlow(.pb)模型
本文介绍了如何使用Python脚本将Keras模型转换为TensorFlow的.pb格式模型,包括加载模型、重命名输出节点和量化等步骤,以便在TensorFlow中进行部署和推理。
79 0
|
2月前
|
机器学习/深度学习 人工智能 算法
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
鸟类识别系统。本系统采用Python作为主要开发语言,通过使用加利福利亚大学开源的200种鸟类图像作为数据集。使用TensorFlow搭建ResNet50卷积神经网络算法模型,然后进行模型的迭代训练,得到一个识别精度较高的模型,然后在保存为本地的H5格式文件。在使用Django开发Web网页端操作界面,实现用户上传一张鸟类图像,识别其名称。
108 12
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
|
3月前
|
机器学习/深度学习 算法 TensorFlow
深入探索强化学习与深度学习的融合:使用TensorFlow框架实现深度Q网络算法及高效调试技巧
【8月更文挑战第31天】强化学习是机器学习的重要分支,尤其在深度学习的推动下,能够解决更为复杂的问题。深度Q网络(DQN)结合了深度学习与强化学习的优势,通过神经网络逼近动作价值函数,在多种任务中表现出色。本文探讨了使用TensorFlow实现DQN算法的方法及其调试技巧。DQN通过神经网络学习不同状态下采取动作的预期回报Q(s,a),处理高维状态空间。
55 1

热门文章

最新文章