Keras 深度学习秘籍(三)
原文:
annas-archive.org/md5/1db02a22ae55dea1c646433e37acffb2译者:飞龙
第九章:神经网络研究
在本章中,我们将看看神经网络中的一些活跃研究领域。以下问题从基础研究领域到复杂的现实问题进行了分析:
-
神经网络中的过拟合
-
使用神经网络进行大规模视频处理
-
使用扭曲神经网络进行命名实体识别
-
双向递归神经网络
避免神经网络中的过拟合
让我们理解过拟合的构成因素,以及如何在神经网络中避免它。Nitesh Srivastava、Geoffrey Hinton 等人于 2014 年发布了一篇论文,www.cs.toronto.edu/~hinton/absps/JMLRdropout.pdf,该论文展示了如何避免过拟合的案例。
问题陈述
深度神经网络包含非线性隐藏层,这使它们成为可以学习输入和输出之间非常复杂关系的表达性模型。然而,这些复杂的关系将是采样噪声的结果。这些复杂的关系可能在测试数据中不存在,从而导致过拟合。为减少这种噪声,已经开发了许多技术和方法。包括在验证集上的性能开始变差时立即停止训练,引入 L1 和 L2 正则化等权重惩罚,以及软权重共享(Nowlan 和 Hinton,1992)。
解决方案
Dropout 是一种解决一些其他技术性能问题的技术,例如在多个模型之间进行平均。它还防止了过拟合,并提供了一种有效地组合大量不同神经网络架构的方法。Dropout 一词意味着在神经网络中删除单元(隐藏和可见)。通过删除一个单元,意味着将其从网络及其输入和输出连接中移除,如下图所示。
删除哪些单元的选择通常是随机的。在简单的情况下,每个单元以独立于其他单元的概率 p 保留。选择 p 的技巧可以是验证集,也可以设置为 0.5;这个值对于广泛的网络和任务来说接近最优。
然而,对于输入单元,保留的最优概率通常更接近 1 而非 0.5。
Dropout 神经网络模型:
-
一个标准的具有两个隐藏层的神经网络
-
一个应用 dropout 后产生的稀疏神经网络,左侧的网络中已删除的单元用交叉标记表示
在 TensorFlow 中如何应用 Dropout 的示例
cell = tf.nn.rnn_cell.LSTMCell(state_size, state_is_tuple=True)
cell = tf.nn.rnn_cell.DropoutWrapper(cell, output_keep_prob=0.5)
cell = tf.nn.rnn_cell.MultiRNNCell([cell] * num_layers, state_is_tuple=True)
如上所示,0.5 的 Dropout 已应用于LSTMCell,其中output_keep_prob:单位张量或介于 0 和 1 之间的浮动值,表示输出保持的概率;如果它是常数且为 1,则不会添加输出 dropout。
结果
让我们看看 dropout 策略如何影响模型的准确性:
如上所示,分类误差在使用 dropout 后显著下降。
使用神经网络进行大规模视频处理
在本文中,static.googleusercontent.com/media/research.google.com/en//pubs/archive/42455.pdf,作者探讨了卷积神经网络(CNN)如何应用于大规模视频分类。在这种应用场景中,神经网络不仅可以访问单一静态图像中的外观信息,还能处理图像的复杂时间演化。将 CNN 应用于这一领域面临着一些挑战。
很少有(或者根本没有)视频分类基准能够匹配现有图像数据集的规模和多样性,因为视频数据集比图像数据集更难以收集、注释和存储。为了获得足够的数据以训练我们的 CNN 架构,作者收集了一个新的 Sports-1M 数据集。该数据集包含来自 YouTube 的 100 万个视频,属于 487 个体育类别的分类体系。Sports-1M 数据集也对研究界开放,以支持该领域未来的研究工作。
在这项工作中,作者将每个视频视为一个由多个短小的固定大小片段组成的袋子。每个片段包含几个时间上连续的帧,因此网络的连接性可以在时间维度上扩展,以学习时空特征。作者描述了三种广泛的连接模式类别(早期融合、晚期融合和慢融合)。之后,我们将探讨一种多分辨率架构,以提高计算效率。
下图解释了各种融合技术:
各种融合技术用于组合时间上分离的帧
分辨率改进
作者使用了一个多分辨率架构,旨在通过在两个空间分辨率上分别处理两条流(称为视网膜流和上下文流)来实现妥协(参见下图)。178 × 178 帧的视频片段作为网络的输入:
多分辨率卷积神经网络(CNN)
上下文流接收下采样后的帧,分辨率为原始空间分辨率的一半(89 × 89 像素)。视网膜流接收中心 89 × 89 区域,分辨率保持原始分辨率。通过这种方式,总输入维度被减半。
特征直方图基准
除了比较不同 CNN 架构之间的差异外,作者还报告了基于特征的方法的准确性。使用标准的词袋管道在视频的所有帧上提取了几种类型的特征,随后通过 k-means 向量量化对其进行离散化,并通过空间金字塔编码和软量化将词语累积成直方图。
定量结果
Sports-1M 数据集测试集结果(200,000 个视频和 4,000,000 个片段)总结如下表。多网络方法始终显著超越基于特征的基线方法。基于特征的方法在视频的持续时间内密集计算视觉词,并生成基于完整视频级特征向量的预测,而作者的网络仅单独查看 20 个随机采样的片段:
在 Sports-1M 测试集的 200,000 个视频上的结果。Hit@k 值表示测试样本中
至少包含了前 k 个预测中的一个真实标签。
网络拓扑采用的方法尽管存在标签噪声,依然能够良好地学习;训练视频可能会出现一些错误注释,即使是正确标注的视频,也往往包含大量伪影,如文本、特效、剪辑和徽标,而我们并没有专门尝试过滤这些内容。
使用扭曲神经网络的命名实体识别
在本文中,www.cs.cmu.edu/~leili/pubs/lu-baylearn2015-twinet.pdf, 作者探讨了在自然语言中识别实体的问题。这通常是问答、对话及其他多个 NLP 用例的第一步。对于一段文本序列,命名实体识别器识别出属于预定义类别的实体,如人物和组织。
命名实体识别示例
IOB 标记系统是命名实体识别的一种标准。
IOB 标记 系统包含如下格式的标签:
-
B-{CHUNK_TYPE}:表示Beginning 块中的词 -
I-{CHUNK_TYPE}: 用于块内部的词Inside -
O:Outside 任何块 -
B-PERSON: 人物实体 -
B-GPE: 地缘政治实体
以下文本展示了句子中命名实体的示例:
John has lived in Britain for 14 years .
B-PERSON O O O B-GPE O B-CARDINAL I-CARDINAL O
然而,这个问题具有相当大的挑战,原因有二:
-
实体数据库往往不完整(考虑到新组织的不断成立)。
-
相同的短语可以根据上下文指代不同的实体(或没有实体)。
定义 Twinet
扭曲的 RNN(Twinet)使用两个并行分支。每个分支由一个递归网络层、一个非线性感知器层和一个反向递归网络层组成。分支是扭曲的:第二个分支中层的顺序被反转。所有递归层的输出在最后集中。
总结一下,递归神经网络(RNN)接受一系列输入向量 x1..T,并递归地计算隐藏状态(也称为输出标签):
h[t] = σ(U · x[t] + W · ht−1)
其中,
-
t 是 1..T
-
x[t] 是外部信号
-
W 是权重
-
h[t-1] 是时间步 t-1 的隐藏层权重。
-
h[t] 为时间步 t 计算的权重
-
U 是 tanh层,用于为时间步 t 创建权重。
σ(·) 是一种非线性激活函数。在作者使用的实验中,我们使用了修正线性单元(RELU)。
结果
Twinet 与斯坦福 NER 和伊利诺伊 NER 进行了比较,结果相当有利。此处 NER 代表命名实体识别器(Named Entity Recognizer)。
从前面的图中可以看出,精确度-召回率(Precision-Recall)以及 F1 分数都更高。
双向循环神经网络(Bidirectional RNNs)
在本节中,我们将介绍一种在自然语言处理(NLP)领域越来越受到关注的新型神经网络拓扑结构。
Schuster 和 Paliwal 在 1997 年提出了双向递归神经网络(BRNN)。BRNN 有助于增加可供网络使用的输入信息量。多层感知器(MLPs)和时间延迟神经网络(TDNNs)被认为在输入数据灵活性方面存在局限性。RNN 也要求其输入数据是固定的。更先进的拓扑结构如 RNN 也有其限制,因为无法从当前状态预测未来的输入信息。相反,BRNN 不需要其输入数据是固定的,其未来的输入信息可以从当前状态中获取。BRNN 的基本思想是将两个相反方向的隐藏层连接到相同的输出层。通过这种结构,输出层可以同时获取来自过去和未来状态的信息。
当输入的上下文信息非常重要时,BRNN 非常有用。例如,在手写识别中,通过了解当前字母前后的位置字母,可以提高识别性能。
这图展示的是双向循环神经网络(Bidirectional RNN)
TIMIT 数据集上的 BRNN
在本节中,我们将探讨双向 RNN(BRNN)如何在 TIMIT 数据集上为音素文本分类提供更高的准确度。
TIMIT 是一个包含美国英语发音者不同性别和方言的音素及词汇转录语音的语料库。每个转录元素都已标注时间。TIMIT 旨在推动声学-语音学知识和自动语音识别系统的发展:
从前面的图中可以看出,BRNN 比 MLP 在训练集和测试集上的准确度更高,能够正确识别更多的帧。BLSTM 的准确度更高。
总结
在本章中,我们讨论了一些已经进行研究以提高准确性并避免过拟合的领域。我们还探讨了一些新的领域,如视频分类。尽管本书的范围不涵盖所有研究领域的详细内容,但我们真诚建议你浏览谷歌、Facebook 和百度的研究网站,此外,还可以浏览顶级的 ACM 和 IEEE 会议,以便了解最新的研究进展。
第十章:入门 TensorFlow
TensorFlow 是 Google 提供的一个开源深度学习库。它提供了用于定义张量函数并自动计算其导数的原语。张量可以表示为多维数字数组。标量、向量和矩阵是张量的类型。TensorFlow 主要用于设计计算图、构建和训练深度学习模型。TensorFlow 库通过数据流图执行数值计算,其中节点代表数学操作,边代表数据点(通常是多维数组或在这些边之间传输的张量)。
环境设置
最好使用像 PyCharm 这样的 IDE 来编辑 Python 代码;它提供更快的开发工具和编码辅助。代码自动完成和检查使得编码和调试更快、更简单,确保你可以专注于编程神经网络的最终目标。
TensorFlow 提供了多种语言的 API:Python、C++、Java、Go 等。我们将下载一个 TensorFlow 版本,使我们能够用 Python 编写深度学习模型的代码。在 TensorFlow 安装网站上,我们可以找到使用 virtualenv、pip 和 Docker 安装 TensorFlow 的最常见方法和最新说明。
以下步骤描述了如何设置本地开发环境:
-
下载 Pycharm 社区版。
-
在 Pycharm 中获取最新版本的 Python。
-
进入“首选项”,设置 Python 解释器,并安装最新版本的 TensorFlow:
- TensorFlow 现在会出现在已安装的包列表中。点击“确定”。现在用如 hello world 这样的程序来测试你的安装:
import TensorFlow as tf
helloWorld = tf.constant("Hello World!")
sess = tf.Session()
print(sess.run(helloWorld))
TensorFlow 与 Numpy 的对比
TensorFlow 和 Numpy 都是 N 维数组库。TensorFlow 还允许我们创建张量函数并计算导数。TensorFlow 已成为深度学习的主要库之一,因为它非常高效,且可以在 GPU 上运行。
以下程序描述了如何使用 TensorFlow 和 numpy 执行类似操作,如创建形状为 (3,3) 的张量:
import TensorFlow as tf
import numpy as np
tf.InteractiveSession()
# TensorFlow operations
a = tf.zeros((3,3))
b = tf.ones((3,3))
print(tf.reduce_sum(b, reduction_indices=1).eval())
print(a.get_shape())
# numpy operations
a = np.zeros((3, 3))
b = np.ones((3, 3))
print(np.sum(b, axis=1))
print(a.shape)
上述代码的输出结果如下:
[ 3\. 3\. 3.]
(3, 3)
[ 3\. 3\. 3.]
(3, 3)
计算图
TensorFlow 基于构建计算图。计算图是一个节点网络,每个节点定义了执行某个函数的操作;这些操作可以是简单的加法或减法,也可以是复杂的多变量方程。TensorFlow 程序在构建阶段组装图,在执行阶段利用会话对象执行图中的操作。
一个操作被称为 op,可以返回零个或多个张量,这些张量可以在图中稍后使用。每个 op 都可以给定常量、数组或 n 维矩阵。
图
默认图会在导入 TensorFlow 库时实例化。构建图对象而非使用默认图在创建多个不依赖于彼此的模型时非常有用。常量和操作会被添加到 TensorFlow 图中。
在newGraph.as_default()之外应用的变量和操作将被添加到默认图中,默认图会在库被导入时创建:
newGraph = tf.Graph()
with newGraph.as_default():
newGraphConst = tf.constant([2., 3.])
会话对象
TensorFlow 中的会话封装了张量对象求值的环境。会话可以有它们自己的私有变量、队列和指定的读取器。我们应该在会话结束时使用 close 方法。
会话有三个参数,这些参数是可选的:
-
Target:要连接的执行引擎 -
graph:要启动的图对象 -
config:这是一个 ConfigProto 协议缓冲区
要执行 TensorFlow 计算的单步操作,需调用步骤函数并执行图中的必要依赖:
# session objects
a = tf.constant(6.0)
b = tf.constant(7.0)
c = a * b
with tf.Session() as sess:
print(sess.run(c))
print(c.eval())
当前活动会话中的sess.run(c)!
上述代码输出如下:
42.0, 42.0
tf.InteractiveSession()函数是保持默认会话在ipython中打开的简便方法。sess.run(c)是 TensorFlow 获取操作的一个示例:
session = tf.InteractiveSession()
cons1 = tf.constant(1)
cons2 = tf.constant(2)
cons3 = cons1 + cons2
# instead of sess.run(cons3)
cons3.eval()
变量
训练模型时,我们使用变量来保存和更新参数。变量就像内存缓冲区,包含张量。我们之前使用的所有张量都是常量张量,而不是变量。
变量由会话对象进行管理或维护。变量在会话之间持久存在,这非常有用,因为张量和操作对象是不可变的:
# tensor variablesW1 = tf.ones((3,3))
W2 = tf.Variable(tf.zeros((3,3)), name="weights")
with tf.Session() as sess:
print(sess.run(W1))
sess.run(tf.global_variables_initializer())
print(sess.run(W2))
上述代码输出如下:
[[ 1\. 1\. 1.] [ 1\. 1\. 1.] [ 1\. 1\. 1.]]
[[ 0\. 0\. 0.] [ 0\. 0\. 0.] [ 0\. 0\. 0.]]
TensorFlow 变量在赋值前必须初始化,这与常量张量不同:
# Variable objects can be initialized from constants or random values
W = tf.Variable(tf.zeros((2,2)), name="weights")
R = tf.Variable(tf.random_normal((2,2)), name="random_weights")
with tf.Session() as sess:
# Initializes all variables with specified values.
sess.run(tf.initialize_all_variables())
print(sess.run(W))
print(sess.run(R))
上述代码输出如下:
[[ 0\. 0.] [ 0\. 0.]]
[[ 0.65469146 -0.97390586] [-2.39198709 0.76642162]]
state = tf.Variable(0, name="counter")
new_value = tf.add(state, tf.constant(1))
update = tf.assign(state, new_value)
with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
print(sess.run(state))
for _ in range(3):
sess.run(update)
print(sess.run(state))
上述代码输出如下:
0 1 2 3
获取变量状态:
input1 = tf.constant(5.0)
input2 = tf.constant(6.0)
input3 = tf.constant(7.0)
intermed = tf.add(input2, input3)
mul = tf.multiply(input1, intermed)
# Calling sess.run(var) on a tf.Session() object retrieves its value. Can retrieve multiple variables simultaneously with sess.run([var1, var2])
with tf.Session() as sess:
result = sess.run([mul, intermed])
print(result)
上述代码输出如下:
[65.0, 13.0]
范围
TensorFlow 模型可能有成百上千个变量。tf.variable_scope()提供了一个简单的命名方式。
为了管理模型的复杂性并将其分解为独立的部分,TensorFlow 提供了作用域。作用域非常简单,并且在使用 TensorBoard 时非常有用。作用域也可以嵌套在其他作用域内:
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v = tf.get_variable("v", [1])
assert v.name == "foo/bar/v:0" with tf.variable_scope("foo"):
v = tf.get_variable("v", [1])
tf.get_variable_scope().reuse_variables()
v1 = tf.get_variable("v", [1])
assert v1 == v
以下示例展示了如何使用重用选项来理解get_variable的行为:
#reuse is falsewith tf.variable_scope("foo"):
n = tf.get_variable("n", [1])
assert v.name == "foo/n:0" *#Reuse is true* with tf.variable_scope("foo"):
n = tf.get_variable("n", [1])
with tf.variable_scope("foo", reuse=True):
v1 = tf.get_variable("n", [1])
assert v1 == n
数据输入
向 TensorFlow 对象输入外部数据:
a = np.zeros((3,3))
ta = tf.convert_to_tensor(a)
with tf.Session() as sess:
print(sess.run(ta))
上述代码输出如下:
[[ 0\. 0\. 0.] [ 0\. 0\. 0.] [ 0\. 0\. 0.]]
占位符和输入字典
使用tf.convert_to_tensor()输入数据是方便的,但它不能扩展。使用tf.placeholder变量(虚拟节点,用于为计算图提供数据的入口)。feed_dict是一个 Python 字典映射:
input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)
output = tf.multiply(input1, input2)
with tf.Session() as sess:
print(sess.run([output], feed_dict={input1:[5.], input2:[6.]}))
上述代码输出如下:
[array([ 30.], dtype=float32)]
自动微分
自动微分也被称为算法微分,它是一种自动计算函数导数的方法。这对于计算梯度、雅可比矩阵和海森矩阵等在数值优化等应用中非常有用。反向传播算法是自动微分反向模式的一种实现,用于计算梯度。
在以下示例中,使用mnist数据集,我们使用其中一个loss函数计算损失。问题是:我们如何将模型拟合到数据上?
我们可以使用tf.train.Optimizer来创建优化器。tf.train.Optimizer.minimize(loss, var_list)将优化操作添加到计算图中,自动微分则无需用户输入即可计算梯度:
import TensorFlow as tf
# get mnist dataset
from TensorFlow .examples.tutorials.mnist import input_data
data = input_data.read_data_sets("MNIST_data/", one_hot=True)
# x represents image with 784 values as columns (28*28), y represents output digit
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])
# initialize weights and biases [w1,b1][w2,b2]
numNeuronsInDeepLayer = 30
w1 = tf.Variable(tf.truncated_normal([784, numNeuronsInDeepLayer]))
b1 = tf.Variable(tf.truncated_normal([1, numNeuronsInDeepLayer]))
w2 = tf.Variable(tf.truncated_normal([numNeuronsInDeepLayer, 10]))
b2 = tf.Variable(tf.truncated_normal([1, 10]))
# non-linear sigmoid function at each neuron
def sigmoid(x):
sigma = tf.div(tf.constant(1.0), tf.add(tf.constant(1.0), tf.exp(tf.negative(x))))
return sigma
# starting from first layer with wx+b, then apply sigmoid to add non-linearity
z1 = tf.add(tf.matmul(x, w1), b1)
a1 = sigmoid(z1)
z2 = tf.add(tf.matmul(a1, w2), b2)
a2 = sigmoid(z2)
# calculate the loss (delta)
loss = tf.subtract(a2, y)
# derivative of the sigmoid function der(sigmoid)=sigmoid*(1-sigmoid)
def sigmaprime(x):
return tf.multiply(sigmoid(x), tf.subtract(tf.constant(1.0), sigmoid(x)))
# automatic differentiation
cost = tf.multiply(loss, loss)
step = tf.train.GradientDescentOptimizer(0.1).minimize(cost)
acct_mat = tf.equal(tf.argmax(a2, 1), tf.argmax(y, 1))
acct_res = tf.reduce_sum(tf.cast(acct_mat, tf.float32))
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
for i in range(10000):
batch_xs, batch_ys = data.train.next_batch(10)
sess.run(step, feed_dict={x: batch_xs,
y: batch_ys})
if i % 1000 == 0:
res = sess.run(acct_res, feed_dict=
{x: data.test.images[:1000],
y: data.test.labels[:1000]})
print(res)
TensorBoard
TensorFlow 具有一个强大的内置可视化工具,叫做TensorBoard。它允许开发者解释、可视化和调试计算图。为了自动在 TensorBoard 中可视化图形和指标,TensorFlow 会将与计算图执行相关的事件写入特定文件夹。
这个示例展示了之前分析的计算图:
要查看图形,请点击 TensorBoard 顶部面板中的图形标签。如果图形中有多个节点,单独查看可能会很困难。为了让我们的可视化更易访问,我们可以使用tf.name_scope并指定名称,将相关操作组织成组。