当前位置: 首页 > news >正文

网站建设与管理课程实训天猫商城网官网

网站建设与管理课程实训,天猫商城网官网,免费效果图网站,协会网站建设的优势原文#xff1a;Deep Learning with TensorFlow Second Edition 协议#xff1a;CC BY-NC-SA 4.0 译者#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】#xff0c;采用译后编辑#xff08;MTPE#xff09;流程来尽可能提升效率。 不要担心自己的形象#xff0c;只… 原文Deep Learning with TensorFlow Second Edition 协议CC BY-NC-SA 4.0 译者飞龙 本文来自【ApacheCN 深度学习 译文集】采用译后编辑MTPE流程来尽可能提升效率。 不要担心自己的形象只关心如何实现目标。——《原则》生活原则 2.3.c 一、人工神经网络 人工神经网络利用了 DL 的概念 。它们是人类神经系统的抽象表示其中包含一组神经元这些神经元通过称为轴突的连接相互通信。 Warren McCulloch 和 Walter Pitts 在 1943 年根据神经活动的计算模型提出了第一个人工神经元模型。这个模型之后是 John von NeumannMarvin MinskyFrank Rosenblatt所谓的感知器和其他许多人提出的另一个模型。 生物神经元 看一下大脑的架构灵感。大脑中的神经元称为生物神经元。它们是看起来不寻常的细胞主要存在于动物大脑中由皮质组成。皮质本身由细胞体组成细胞体包含细胞核和细胞的大部分复杂成分。有许多称为树突的分支延伸加上一个称为轴突的非常长的延伸。 在它的极端附近轴突分裂成许多分支称为终树突并且在这些分支的顶部是称为突触末端或简单的突触的微小结构连接到其他神经元的树突。生物神经元接收称为来自其他神经元的信号的短电脉冲作为回应它们发出自己的信号 图 7生物神经元的工作原理。 在生物学中神经元由以下组成 细胞体或体细胞一个或多个树突其职责是接收来自其他神经元的信号轴突反过来将同一神经元产生的信号传递给其他连接的神经元 神经元的活动在发送/接收来自其他神经元的信号活动状态和休息非活动状态之间交替。从一个相到另一个相的转变是由外部刺激引起的由树枝状晶体拾取的信号表示。每个信号具有兴奋或抑制作用在概念上由与刺激相关的权重表示。 处于空闲状态的神经元累积它收到的所有信号直到达到某个激活阈值。 人工神经元 基于生物神经元的概念出现了人工神经元的术语和思想它们已被用于构建基于 DL 的预测分析的智能机器。这是启发人工神经网络的关键理念。与生物神经元类似人工神经元由以下部分组成 一个或多个传入连接其任务是从其他神经元收集数字信号为每个连接分配一个权重用于考虑发送的每个信号 一个或多个输出连接将信号传递给其他神经元 激活函数基于一些信号确定输出信号的数值信号从和其他神经元的输入连接接受并从权重和神经元本身的激活阈值中适当地收集权重与每个接收信号相关 图 8人工神经元模型。 通过将激活函数也称为传递函数应用于输入的加权和来计算输出即神经元传输的信号。这些函数的动态范围介于 -1 和 1 之间或介于 0 和 1 之间。许多激活函数在复杂性和输出方面有所不同。在这里我们简要介绍三种最简单的形式 阶跃函数一旦我们确定阈值x例如x 10如果输入之和高于阈值该函数将返回 1否则则返回 0。线性组合不管理阈值而是从默认值中减去输入值的加权和。我们将得到二元结果该结果将由减法的正b或负-b输出表示。Sigmoid这会产生 Sigmoid 曲线这是一条具有 S 趋势的曲线。通常sigmoid 函数指的是逻辑函数的特殊情况。 从第一个人工神经元原型制作中使用的最简单的形式我们转向更复杂的形式可以更好地表征神经元的功能 双曲正切函数 径向基函数 圆锥截面函数 Softmax 函数 图 9最常用的人工神经元模型传递函数。a阶梯函数b线性函数csigmoid 函数计算值介于 0 和 1 之间dsigmoid 函数计算值介于 -1 和 1 之间。 选择适当的激活函数也是权重初始化是使网络发挥最佳表现并获得良好训练的关键。这些主题正在进行大量研究如果训练阶段正确进行研究表明在产出质量方面存在微小差异。 注意 在神经网络领域没有经验法则。这一切都取决于您的数据以及在通过激活函数后希望数据转换的形式。如果要选择特定的激活函数则需要研究函数的图形以查看结果如何根据给定的值进行更改。 ANN 如何学习 神经网络的学习过程被配置为权重优化的迭代过程因此是监督类型。由于网络在属于训练集的一组示例上的表现即您知道示例所属的类的集合因此修改权重。 目的是最小化损失函数其表示网络行为偏离期望行为的程度。然后在由除了训练集中的对象之外的对象例如图像分类问题中的图像组成的测试集上验证网络的表现。 人工神经网络和反向传播算法 常用的监督学习算法是反向传播算法。训练程序的基本步骤如下 用随机权重初始化网络对于所有训练案例请按照下列步骤操作 正向传播计算网络的误差即所需输出与实际输出之间的差值 向后传递对于所有层从输出层回到输入层 i使用正确的输入显示网络层的输出误差函数。 ii调整当前层中的权重以最小化误差函数。这是反向传播的优化步骤。 当验证集上的误差开始增加时训练过程结束因为这可能标志着阶段过拟合的开始即网络倾向于以牺牲训练数据为代价来内插训练数据的阶段。普遍性。 权重优化 因此 优化权重的有效算法的可用性构成了构建神经网络的必要工具。该问题可以通过称为梯度下降GD的迭代数值技术来解决。该技术根据以下算法工作 随机选择模型参数的初始值根据模型的每个参数计算误差函数的梯度 G.更改模型的参数使它们朝着减小误差的方向移动即沿 -G 方向移动重复步骤 2 和 3直到 G 的值接近零 误差函数 E 的梯度G提供了误差函数与当前值具有更陡斜率的方向所以为了减少 E我们必须在相反的方向上做一些小步骤-G。 通过以迭代方式多次重复此操作我们向下移动到 E 的最小值以达到G 0的点从而无法进一步进展 图 10搜索误差函数 E 的最小值。我们沿着函数 E 的梯度 G 最小的方向移动。 随机梯度下降 在 GD 优化中我们基于完整的训练集计算成本梯度因此我们有时也将其称为批量 GD。在非常大的数据集的情况下使用 GD 可能非常昂贵因为我们在训练集上只进行一次传递。训练集越大我们的算法更新权重的速度就越慢并且在收敛到全局成本最小值之前可能需要的时间越长。 最快的梯度下降方法是随机梯度下降SGD因此它被广泛应用于深度神经网络。在 SGD 中我们仅使用来自训练集的一个训练样本来对特定迭代中的参数进行更新。 这里术语随机来自这样的事实基于单个训练样本的梯度是真实成本梯度的随机近似。由于其随机性通向全局最小成本的路径并不像 GD 那样直接但如果我们可视化 2D 空间中的成本表面则可能会出现锯齿形 图 11GD 与 SGD梯度下降左图确保权重中的每次更新都在正确的方向上完成最小化成本函数的方向。随着数据集大小的增长以及每个步骤中更复杂的计算SGD右图在这些情况下是首选。这里在处理每个样本时完成权重的更新因此后续计算已经使用了改进的权重。尽管如此这个原因导致了在最小化误差函数方面的一些误导。 神经网络架构 我们连接节点的方式和存在的层数即输入和输出之间的节点级别以及每层神经元的数量定义了神经网络的架构。 神经网络中存在各种类型的架构。我们可以将 DL 架构分为四组深度神经网络DNN卷积神经网络CNN循环神经网络RNN和紧急架构EA。本章的以下部分将简要介绍这些架构。更多详细分析以及应用实例将成为本书后续章节的主题。 深度神经网络DNN DNN 是人工神经网络它们强烈地面向 DL。在正常分析程序不适用的情况下由于要处理的数据的复杂性因此这种网络是一种极好的建模工具。 DNN 是与我们讨论过的神经网络非常相似的神经网络但它们必须实现更复杂的模型更多的神经元隐藏层和连接尽管它们遵循适用于所有 ML 问题的学习原则例如作为监督学习。每层中的计算将下面层中的表示转换为稍微更抽象的表示。 我们将使用术语 DNN 将具体指代多层感知器MLP堆叠自编码器SAE和深度信任网络DBN。 SAE 和 DBN 使用自编码器AEs和 RBM 作为架构的块。它们与 MLP 之间的主要区别在于训练分两个阶段执行无监督的预训练和监督微调 图 12分别使用 AE 和 RBM 的 SAE 和 DBN。 在无监督预训练中如上图所示这些层按顺序堆叠并以分层方式进行训练如使用未标记数据的 AE 或 RBM。然后在有监督的微调中堆叠输出分类器层并通过用标记数据重新训练来优化完整的神经网络。 在本章中我们不讨论 SAE详见第 5 章优化 TensorFlow 自编码器但将坚持使用 MLP 和 DBN 并使用这两种 DNN 架构。我们将看到如何开发预测模型来处理高维数据集。 多层感知器 在多层网络中可以识别层的人工神经元以便每个神经元连接到下一层中的所有神经元确保 属于同一层的神经元之间没有连接属于非相邻层的神经元之间没有连接每层的层数和神经元数取决于要解决的问题 输入和输出层定义输入和输出并且存在隐藏层其复杂性实现网络的不同行为。最后神经元之间的连接由与相邻层对相同的矩阵表示。 每个数组包含两个相邻层的节点对之间的连接的权重。前馈网络是层内没有环路的网络。 我们将在第 3 章使用 TensorFlow 的前馈神经网络中更详细地描述前馈网络 图 13MLP 架构 深度信念网络DBNs 为了克服 MLP 中的过拟合问题我们建立了一个 DBN做了无监督预训练为输入获得了一组不错的特征表示然后微调训练集从网络获得实际预测。虽然 MLP 的权重是随机初始化的但 DBN 使用贪婪的逐层预训练算法通过概率生成模型初始化网络权重。模型由可见层和多层随机和潜在变量组成称为隐藏单元或特征检测器。 DBN 是深度生成模型它们是神经网络模型可以复制您提供的数据分布。这允许您从实际数据点生成“虚假但逼真”的数据点。 DBN 由可见层和多层随机潜在变量组成这些变量称为隐藏单元或特征检测器。前两层在它们之间具有无向的对称连接并形成关联存储器而较低层从前一层接收自上而下的有向连接。 DBN 的构建块是受限玻尔兹曼机RBM。如下图所示几个 RBM 一个接一个地堆叠形成 DBN 图 14配置用于半监督学习的 DBN 单个 RBM 由两层组成。第一层由可见神经元组成第二层由隐藏神经元组成。下图显示了简单 RBM 的结构。可见单元接受输入隐藏单元是非线性特征检测器。每个可见神经元都连接到所有隐藏的神经元但同一层中的神经元之间没有内部连接。 RBM 由可见层节点和隐藏层节点组成但没有可见 - 隐藏和隐藏 - 隐藏连接因此项受限制。它们允许更有效的网络训练可以监督或监督。这种类型的神经网络能够表示输入的大量特征然后隐藏的节点可以表示多达 2n 个特征。可以训练网络回答单个问题例如问题是或否它是猫吗直到它能够再次以二元的方式响应总共 2n 个问题它是猫吗 这是暹罗人它是白色的吗。 RBM 的架构如下神经元根据对称的二分图排列 图 15RBM 架构。 由于无法对变量之间的关系进行建模因此单个隐藏层 RBM 无法从输入数据中提取所有特征。因此一层接一层地使用多层 RBM 来提取非线性特征。在 DBN 中首先使用输入数据训练 RBM并且隐藏层表示使用贪婪学习方法学习的特征。这些第一 RBM 的学习特征即第一 RBM 的隐藏层被用作第二 RBM 的输入作为 DBN 中的另一层。 类似地第二层的学习特征用作另一层的输入。这样DBN 可以从输入数据中提取深度和非线性特征。最后一个 RBM 的隐藏层代表整个网络的学习特征。 卷积神经网络CNNs CNN 已经专门用于图像识别。学习中使用的每个图像被分成紧凑的拓扑部分每个部分将由过滤器处理以搜索特定模式。形式上每个图像被表示为像素的三维矩阵宽度高度和颜色并且每个子部分可以与滤波器组卷积在一起。换句话说沿着图像滚动每个滤镜计算相同滤镜和输入的内积。 此过程为各种过滤器生成一组特征图激活图。将各种特征图叠加到图像的相同部分上我们得到输出量。这种类型的层称为卷积层。下图是 CNN 架构的示意图 图 16CNN 架构。 虽然常规 DNN 适用于小图像例如MNIST 和 CIFAR-10但由于需要大量参数它们会因较大的图像而崩溃。例如100×100图像具有 10,000 个像素并且如果第一层仅具有 1,000 个神经元其已经严格限制传输到下一层的信息量则这意味着 1000 万个连接。另外这仅适用于第一层。 CNN 使用部分连接的层解决了这个问题。由于相邻层仅部分连接并且因为它重复使用其权重因此 CNN 的参数远远少于完全连接的 DNN这使得训练速度更快。这降低了过拟合的风险并且需要更少的训练数据。此外当 CNN 已经学习了可以检测特定特征的内核时它可以在图像上的任何地方检测到该特征。相反当 DNN 在一个位置学习一个特征时它只能在该特定位置检测到它。由于图像通常具有非常重复的特征因此 CNN 在图像处理任务例如分类和使用较少的训练示例方面能够比 DNN 更好地推广。 重要的是DNN 没有关于如何组织像素的先验知识它不知道附近的像素是否接近。 CNN 的架构嵌入了这一先验知识。较低层通常识别图像的单元域中的特征而较高层将较低层特征组合成较大特征。这适用于大多数自然图像使 CNN 在 DNN 上具有决定性的先机 图 17常规 DNN 与 CNN。 例如在上图中在左侧您可以看到常规的三层神经网络。在右侧CNN 以三维宽度高度和深度排列其神经元如在其中一个层中可视化。 CNN 的每一层都将 3D 输入音量转换为神经元激活的 3D 输出音量。红色输入层保持图像因此其宽度和高度将是图像的尺寸深度将是三个红色绿色和蓝色通道。 因此我们所看到的所有多层神经网络都有由长线神经元组成的层我们不得不将输入图像或数据平铺到 1D然后再将它们馈送到神经网络。但是当您尝试直接为它们提供 2D 图像时会发生什么答案是在 CNN 中每个层都用 2D 表示这样可以更容易地将神经元与其相应的输入进行匹配。我们将在接下来的部分中看到这方面的示例。 自编码器 AE 是具有三层或更多层的网络 其中输入层和输出具有相同数量的神经元并且那些中间隐藏层具有较少数量的神经元。对网络进行训练以便在输出中简单地为每条输入数据再现输入中相同的活动模式。 AE 是能够在没有任何监督的情况下学习输入数据的有效表示的 ANN即训练集是未标记的。它们通常具有比输入数据低得多的维度使得 AE 可用于降低维数。更重要的是AE 作为强大的特征检测器它们可用于 DNN 的无监督预训练。 该问题的显着方面在于由于隐藏层中神经元的数量较少如果网络可以从示例中学习并推广到可接受的程度则它执行数据压缩对于每个示例隐藏神经元的状态为输入和输出公共状态的压缩版本提供。 AEs 的有用应用是数据可视化的数据去噪和降维。 下图显示了 AE 通常如何工作它通过两个阶段重建接收的输入编码阶段其对应于原始输入的尺寸减小以及解码阶段其能够从编码压缩表示重建原始输入 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kNiZcHjM-1681565849691)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_01_19.jpg)] 图 18自编码器的编码和解码阶段。 作为无监督神经网络自编码器的主要特征是其对称结构。 自编码器有两个组件将输入转换为内部表示的编码器然后是将内部表示转换为输出的解码器。 换句话说 自编码器可以看作是编码器的组合其中我们将一些输入编码为代码以及解码器其中我们将代码解码/重建为其原始输入作为输出。因此MLP 通常具有与自编码器相同的架构除了输出层中的神经元的数量必须等于输入的数量。 如前所述训练自编码器的方法不止一种。第一种方法是一次训练整个层类似于 MLP。但是在计算成本函数时不像在监督学习中使用某些标记输出我们使用输入本身。因此成本函数显示实际输入和重建输入之间的差异。 循环神经网络RNNs RNN 的基本特征是网络包含至少一个反馈连接因此激活可以在循环中流动。它使网络能够进行时间处理和学习序列例如执行序列识别/再现或时间关联/预测。 RNN 架构可以有许多不同的形式。一种常见类型包括标准 MLP 加上添加的循环。这些可以利用 MLP 强大的非线性映射功能并具有某种形式的内存。其他人具有更均匀的结构可能与每个神经元连接到所有其他神经元并且可能具有随机激活函数 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9bwsXdl8-1681565849691)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_01_20.jpg)] 图 19RNN 架构。 对于简单的架构和确定性激活函数可以使用类似的 GD 过程来实现学习这些过程导致用于前馈网络的反向传播算法。 上图查看了 RNN 的一些最重要的类型和功能。 RNN 被设计成利用输入数据的顺序信息与诸如感知器长短期存储器单元LSTM或门控循环单元GRU之类的构件块之间的循环连接。后两者用于消除常规 RNN 的缺点例如梯度消失/爆炸问题和长短期依赖性。我们将在后面的章节中讨论这些架构。 前沿架构 已经提出了许多其他前沿 DL 架构 例如深度时空神经网络DST-NN多维循环神经网络MD-RNN和卷积自编码器CAE。 然而人们正在谈论和使用其他新兴网络例如 CapsNetsCNN 的改进版本旨在消除常规 CNN 的缺点用于个性化的分解机和深度强化学习。 深度学习框架 在本节中我们介绍了一些最流行的 DL 框架。简而言之几乎所有的库都提供了使用图形处理器加速学习过程的可能性在开放许可下发布并且是大学研究小组的结果。 TensorFlow 是数学软件是一个开源软件库用 Python 和 C 编写用于机器智能。 Google Brain 团队在 2011 年开发了它它可以用来帮助我们分析数据预测有效的业务成果。构建神经网络模型后在必要的特征工程之后您可以使用绘图或 TensorBoard 以交互方式执行训练。 最新版 TensorFlow 提供的主要功能包括更快的计算灵活性可移植性易于调试统一的 APIGPU 计算的透明使用易用性和可扩展性。其他好处包括它被广泛使用支持并且可以大规模生产。 Keras 是一个深度学习库位于 TensorFlow 和 Theano 之上提供了一个直观的 API受到了 Torch可能是现有的最佳 Python API的启发。 Deeplearning4j 依赖于 Keras 作为其 Python API并从 Keras 和 KerasTheano 和 TensorFlow 导入模型。 Google 的软件工程师 FrançoisChollet 创建了 Keras。它可以在 CPU 和 GPU 上无缝运行。这样可以通过用户友好性模块化和可扩展性轻松快速地进行原型设计。 Keras 可能是增长最快的框架之一因为构建 NN 层太容易了。因此Keras 很可能成为 NN 的标准 Python API。 Theano 可能是最常见的库。 Theano 是用 Python 编写的它是 ML 领域中使用最广泛的语言之一Python 也用于 TensorFlow。此外Theano 允许使用 GPU比单 CPU 快 24 倍。 Theano 允许您有效地定义优化和求值复杂的数学表达式例如多维数组。不幸的是Yoshua Bengio 于 2017 年 9 月 28 日宣布Theano 的发展将停止。这意味着 Theano 实际上已经死了。 Neon 是由 Nirvana 开发的基于 Python 的深度学习框架。 Neon 的语法类似于 Theano 的高级框架例如Keras。目前Neon 被认为是基于 GPU 的最快工具特别是对于 CNN。虽然它的基于 CPU 的实现比大多数其他库相对更差。 Torch 是 ML 的巨大生态系统提供大量算法和函数包括 DL 和处理各种类型的多媒体数据特别关注并行计算。它为 C 语言提供了出色的接口 拥有庞大的用户社区。 Torch 是一个扩展脚本语言 Lua 的库旨在为设计和训练 ML 系统提供灵活的环境。 Torch 是各种平台WindowsMacLinux 和 Android上的独立且高度可移植的框架脚本可以在这些平台上运行而无需修改。 Torch 为不同的应用提供了许多用途。 Caffe主要由伯克利远景和学习中心BVLC开发 是一个框架 因其表达速度和模块性而脱颖而出。其独特的架构鼓励应用和创新使计算更容易从 CPU 转换到 GPU。庞大的用户群意味着最近发生了相当大的发展。它是用 Python 编写的但由于需要编译的众多支持库安装过程可能很长。 MXNet 是一个支持多种语言的 DL 框架例如 RPythonC 和 Julia。这很有帮助因为如果你知道这些语言中的任何一种你根本不需要走出自己的舒适区来训练你的 DL 模型。它的后端用 C 和 CUDA 编写它能够以与 Theano 类似的方式管理自己的内存。 MXNet 也很受欢迎因为它可以很好地扩展并且可以与多个 GPU 和计算机一起使用这使它对企业非常有用。这就是为什么亚马逊将 MXNet 作为 DL 的参考库。 2017 年 11 月AWS 宣布推出 ONNX-MXNet这是一个开源 Python 包用于将开放式神经网络交换ONNX DL 模型导入 Apache MXNet。 Microsoft Cognitive ToolkitCNTK是 Microsoft Research 的统一 DL 工具包可以轻松训练 将多种 GPU 和服务器中的流行模型类型组合在一起。 CNTK 为语音图像和文本数据实现高效的 CNN 和 RNN 训练。它支持 cuDNN v5.1 进行 GPU 加速。 CNTK 还支持 PythonC C和命令行接口。 这是一个总结这些框架的表 框架支持的编程语言训练教材和社区CNN 建模能力RNN 建模能力可用性多 GPU 支持TheanoPythonC丰富的 CNN 教程和预建模型丰富的 RNN 教程和预建模型模块化架构否NeonPythonCNN 最快的工具资源最少模块化架构否TorchLuaPython资源最少丰富的 RNN 教程和预建模型模块化架构是CaffeC丰富的 CNN 教程和预建模型资源最少创建层需要时间是MXNetRPythonJuliaScala丰富的 CNN 教程和预建模型资源最少模块化架构是CNTKC丰富的 CNN 教程和预建模型丰富的 RNN 教程和预建模型模块化架构是TensorFlowPythonC丰富的 RNN 教程和预建模型丰富的 RNN 教程和预建模型模块化架构是DeepLearning4jJavaScala丰富的 RNN 教程和预建模型丰富的 RNN 教程和预建模型模块化架构是KerasPython丰富的 RNN 教程和预建模型丰富的 RNN 教程和预建模型模块化架构是 除了前面的库之外最近还有一些关于云计算的 DL 项目。这个想法是将 DL 功能带到大数据拥有数十亿个数据点和高维数据。例如Amazon Web ServicesAWSMicrosoft AzureGoogle Cloud Platform 和 NVIDIA GPU CloudNGC都提供机器和深度学习服务它们是公共云的原生。 2017 年 10 月AWS 针对 Amazon Elastic Compute CloudEC2P3 实例发布了深度学习 AMI亚马逊机器映像 。这些 AMI 预装了深度学习框架如 TensorFlowGluon 和 Apache MXNet这些框架针对 Amazon EC2 P3 实例中的 NVIDIA Volta V100 GPU 进行了优化。深度学习服务目前提供三种类型的 AMIConda AMIBase AMI 和带源代码的 AMI。 Microsoft Cognitive Toolkit 是 Azure 的开源深度学习服务。与 AWS 的产品类似它侧重于可以帮助开发人员构建和部署深度学习应用的工具。该工具包安装在 Python 2.7 的根环境中。 Azure 还提供了一个模型库其中包含代码示例等资源以帮助企业开始使用该服务。 另一方面NGC 为 AI 科学家和研究人员提供 GPU 加速容器。 NGC 采用容器化的深度学习框架如 TensorFlowPyTorch 和 MXNet经过 NVIDIA 的调整测试和认证可在参与的云服务提供商的最新 NVIDIA GPU 上运行。尽管如此还有通过各自市场提供的第三方服务。 总结 在本章中我们介绍了 DL 的一些基本主题。 DL 由一组方法组成这些方法允许 ML 系统获得多个级别上的数据的分层表示。这是通过组合简单单元来实现的每个简单单元从输入级别开始以更高和抽象级别的表示在其自己的级别上转换表示。 最近这些技术提供了许多应用中从未见过的结果例如图像识别和语音识别。这些技术普及的主要原因之一是 GPU 架构的发展这大大减少了 DNN 的训练时间。 有不同的 DNN 架构每个架构都是针对特定问题而开发的。我们将在后面的章节中更多地讨论这些架构并展示使用 TensorFlow 框架创建的应用示例。本章最后简要介绍了最重要的 DL 框架。 在下一章中我们将开始我们的 DL 之旅介绍 TensorFlow 软件库。我们将介绍 TensorFlow 的主要功能并了解如何安装它并设置我们的第一个工作再营销数据集。 二、TensorFlow v1.6 的新功能是什么 2015 年Google 开源了 TensorFlow包括其所有参考实现。所有源代码都是在 Apache 2.0 许可下在 GitHub 上提供的。从那以后TensorFlow 已经在学术界和工业研究中被广泛采用最稳定的版本 1.6 最近已经发布了统一的 API。 值得注意的是TensorFlow 1.6及更高版本中的 API 并非都与 v1.5 之前的代码完全向后兼容。这意味着一些在 v1.5 之前工作的程序不一定适用于 TensorFlow 1.6。 现在让我们看看 TensorFlow v1.6 具有的新功能和令人兴奋的功能。 Nvidia GPU 支持的优化 从 TensorFlow v1.5 开始预构建的二进制文件现在针对 CUDA 9.0 和 cuDNN 7 构建。但是从 v1.6 版本开始TensorFlow 预构建的二进制文件使用 AVX 指令这可能会破坏旧 CPU 上的 TensorFlow。尽管如此自 v1.5 以来已经可以在 NVIDIA Tegra 设备上增加对 CUDA 的支持。 TensorFlow Lite TensorFlow Lite 是 TensorFlow 针对移动和嵌入式设备的轻量级解决方案。它支持具有小二进制大小和支持硬件加速的快速表现的设备上机器学习模型的低延迟推理。 TensorFlow Lite 使用许多技术来实现低延迟例如优化特定移动应用的内核预融合激活允许更小和更快定点数学模型的量化内核以及将来利用杠杆专用机器学习硬件在特定设备上获得特定模型的最佳表现。 图 1使用 TensorFlow Lite 在 Android 和 iOS 设备上使用训练模型的概念视图 机器学习正在改变计算范式我们看到了移动和嵌入式设备上新用例的新趋势。在相机和语音交互模型的推动下消费者的期望也趋向于与其设备进行自然的类似人的交互。 因此用户的期望不再局限于计算机并且移动设备的计算能力也因硬件加速以及诸如 Android 神经​​网络 API 和 iOS 的 C API 之类的框架而呈指数级增长。如上图所示预训练模型可以转换为较轻的版本以便作为 Android 或 iOS 应用运行。 因此广泛使用的智能设备为设备智能创造了新的可能性。这些允许我们使用我们的智能手机来执行实时计算机视觉和自然语言处理NLP。 急切执行 急切执行是 TensorFlow 的一个接口它提供了一种命令式编程风格。启用预先执行时TensorFlow 操作在程序中定义立即执行。 需要注意的是从 TensorFlow v1.7 开始急切执行将被移出contrib。这意味着建议使用tf.enable_eager_execution()。我们将在后面的部分中看到一个例子。 优化加速线性代数XLA v1.5 之前的 XLA 不稳定并且具有非常有限的特性。但是v1.6 对 XLA 的支持更多。这包括以下内容 添加了对 XLA 编译器的 Complex64 支持现在为 CPU 和 GPU 添加了快速傅里叶变换FFT支持bfloat支持现已添加到 XLA 基础结构中已启用 ClusterSpec 传播与 XLA 设备的工作Android TF 现在可以在兼容的 Tegra 设备上使用 CUDA 加速构建已启用对添加确定性执行器以生成 XLA 图的支持 开源社区报告的大量错误已得到修复并且此版本已集成了大量 API 级别的更改。 但是由于我们尚未使用 TensorFlow 进行任何研究我们将在后面看到如何利用这些功能开发真实的深度学习应用。在此之前让我们看看如何准备您的编程环境。 安装和配置 TensorFlow 您可以在许多平台上安装和使用 TensorFlow例如 Linux macOS 和 Windows。此外您还可以从 TensorFlow 的最新 GitHub 源构建和安装 TensorFlow。此外如果您有 Windows 机器您可以通过原生点或 Anacondas 安装 TensorFlow。 TensorFlow 在 Windows 上支持 Python 3.5.x 和 3.6.x. 此外Python 3 附带了 PIP3 包管理器它是用于安装 TensorFlow 的程序。因此如果您使用此 Python 版本则无需安装 PIP。根据我们的经验即使您的计算机上集成了 NVIDIA GPU 硬件也值得安装并首先尝试仅使用 CPU 的版本如果您没有获得良好的表现那么您应该切换到 GPU 支持。 支持 GPU 的 TensorFlow 版本有几个要求例如 64 位 LinuxPython 2.7或 Python 3 的 3.3NVIDIACUDA®7.5 或更高版本Pascal GPU 需要 CUDA 8.0和 NVIDIA cuDNN这是 GPU 加速深度学习v5.1建议使用更高版本。有关详情请参阅此链接。 更具体地说TensorFlow 的当前开发仅支持使用 NVIDIA 工具包和软件的 GPU 计算。因此必须在您的计算机上安装以下软件才能获得预测分析应用的 GPU 支持 NVIDIA 驱动程序具有计算能力的CUDA 3.0CudNN NVIDIA CUDA 工具包包括详见此链接 GPU 加速库例如用于 FFT 的 cuFFT基本线性代数子程序BLAS的 cuBLAScuSPARSE 用于稀疏矩阵例程cuSOLVER 用于密集和稀疏的直接求解器随机数生成的 cuRAND图像的 NPP 和视频处理原语适用于 NVIDIA Graph Analytics 库的 nvGRAPH对模板化并行算法和数据结构以及专用 CUDA 数学库的推动 但是我们不会介绍 TensorFlow 的安装和配置因为 TensorFlow 上提供的文档非常丰富可以遵循并采取相应的措施。另一个原因是该版本将定期更改。因此使用 TensorFlow 网站保持自己更新将是一个更好的主意。 如果您已经安装并配置了编程环境那么让我们深入了解 TensorFlow 计算图。 TensorFlow 计算图 在考虑执行 TensorFlow 程序时我们应该熟悉图创建和会话执行的概念。基本上第一个用于构建模型第二个用于提供数据并获得结果。 有趣的是TensorFlow 在 C 引擎上执行所有操作这意味着在 Python 中甚至不会执行一些乘法或添加操作。 Python 只是一个包装器。从根本上说TensorFlow C 引擎包含以下两件事 有效的操作实现例如 CNN 的卷积最大池化和 sigmoid前馈模式操作的衍生物 TensorFlow 库在编码方面是一个非凡的库它不像传统的 Python 代码例如你可以编写语句并执行它们。 TensorFlow 代码由不同的操作组成。甚至变量初始化在 TensorFlow 中也很特殊。当您使用 TensorFlow 执行复杂操作例如训练线性回归时TensorFlow 会在内部使用数据流图表示其计算。该图称为计算图它是由以下组成的有向图 一组节点每个节点代表一个操作一组有向弧每个弧代表执行操作的数据 TensorFlow 有两种类型的边 正常它们携带节点之间的数据结构。一个操作的输出即来自一个节点的输出成为另一个操作的输入。连接两个节点的边缘带有值。特殊此边不携带值但仅表示两个节点之间的控制依赖关系例如 X 和 Y。这意味着只有在 X 中的操作已经执行时才会执行节点 Y但之前关于数据的操作之间的关系。 TensorFlow 实现定义控制依赖性以强制规定执行其他独立操作的顺序作为控制峰值内存使用的方式。 计算图基本上类似于数据流图。图 2 显示了简单计算的计算图如z d × c (a b) × c 图 2一个计算简单方程的非常简单的执行图 在上图中图中的圆圈表示操作而矩形表示计算图。如前所述TensorFlow 图包含以下内容 tf.Operation对象这些是图中的节点。这些通常简称为操作。 操作仅为 TITO张量 - 张量 - 张量。一个或多个张量输入和一个或多个张量输出。tf.Tensor对象这些是图的边缘。这些通常简称为张量。 张量对象在图中的各种操作之间流动。在上图中d也是操作。它可以是“常量”操作其输出是张量包含分配给d的实际值。 也可以使用 TensorFlow 执行延迟执行。简而言之一旦您在计算图的构建阶段中编写了高度复合的表达式您仍然可以在运行会话阶段对其进行求值。从技术上讲TensorFlow 安排工作并以​​有效的方式按时执行。 例如使用 GPU 并行执行代码的独立部分如下图所示 图 3要在 CPU 或 GPU 等设备上的会话上执行的 TensorFlow 图中的边和节点 在创建计算图之后TensorFlow 需要具有以分布式方式由多个 CPU以及 GPU如果可用执行的活动会话。通常您实际上不需要明确指定是使用 CPU 还是 GPU因为 TensorFlow 可以选择使用哪一个。 默认情况下将选择 GPU 以进行尽可能多的操作否则将使用 CPU。然而通常它会分配所有 GPU 内存即使它不消耗它。 以下是 TensorFlow 图的主要组成部分 变量用于 TensorFlow 会话之间的值包含权重和偏置。张量一组值在节点之间传递以执行操作也称为操作。占位符用于在程序和 TensorFlow 图之间发送数据。会话当会话启动时TensorFlow 会自动计算图中所有操作的梯度并在链式规则中使用它们。实际上在执行图时会调用会话。 不用担心前面这些组件中的每一个都将在后面的章节中讨论。从技术上讲您将要编写的程序可以被视为客户。然后客户端用于以符号方式在 C/C 或 Python 中创建执行图然后您的代码可以请求 TensorFlow 执行此图。整个概念从下图中变得更加清晰 图 4使用客户端主架构来执行 TensorFlow 图 计算图有助于使用 CPU 或 GPU 在多个计算节点上分配工作负载。这样神经网络可以等同于复合函数其中每个层输入隐藏或输出层可以表示为函数。要了解在张量上执行的操作需要了解 TensorFlow 编程模型的良好解决方法。 TensorFlow 代码结构 TensorFlow 编程模型表示如何构建预测模型。导入 TensorFlow 库时 TensorFlow 程序通常分为四个阶段 构建涉及张量运算的计算图我们将很快看到张量创建会话运行会话执行图中定义的操作执行数据收集和分析 这些主要阶段定义了 TensorFlow 中的编程模型。请考虑以下示例其中我们要将两个数相乘 import tensorflow as tf # Import TensorFlowx tf.constant(8) # X op y tf.constant(9) # Y op z tf.multiply(x, y) # New op Zsess tf.Session() # Create TensorFlow sessionout_z sess.run(z) # execute Z op sess.close() # Close TensorFlow session print(The multiplication of x and y: %d % out_z)# print result前面的代码段可以用下图表示 图 5在客户端主架构上执行并返回的简单乘法 为了使前面的程序更有效TensorFlow 还允许通过占位符交换图​​变量中的数据稍后讨论。现在想象一下代码段之后的可以做同样的事情但效率更高 import tensorflow as tf# Build a graph and create session passing the graph with tf.Session() as sess:x tf.placeholder(tf.float32, namex)y tf.placeholder(tf.float32, namey)z tf.multiply(x,y)# Put the values 8,9 on the placeholders x,y and execute the graph z_output sess.run(z,feed_dict{x: 8, y:9}) print(z_output)TensorFlow 不是乘以两个数字所必需的。此外这个简单的操作有很多行代码。该示例的目的是阐明如何构造代码从最简单的如在本例中到最复杂的。此外该示例还包含一些基本指令我们将在本书中给出的所有其他示例中找到这些指令。 第一行中的单个导入为您的命令导入 TensorFlow;如前所述它可以用tf实例化。然后TensorFlow 运算符将由tf和要使用的运算符的名称表示。在下一行中我们通过tf.Session()指令构造session对象 with tf.Session() as sess:提示 会话对象即sess封装了 TensorFlow 的环境以便执行所有操作对象并求值Tensor对象。我们将在接下来的部分中看到它们。 该对象包含计算图如前所述它包含要执行的计算。以下两行使用placeholder定义变量x和y。通过placeholder您可以定义输入例如我们示例的变量x和输出变量例如变量y x tf.placeholder(tf.float32, namex) y tf.placeholder(tf.float32, namey)提示 占位符提供图元素和问题计算数据之间的接口。它们允许我们创建我们的操作并构建我们的计算图而不需要数据而不是使用它的引用。 要通过placeholder函数定义数据或张量我们将很快向您介绍张量的概念需要三个参数 数据类型是要定义的张量中的元素类型。占位符的形状是要进给的张量的形状可选。如果未指定形状则可以提供任何形状的张量。名称对于调试和代码分析非常有用但它是可选的。 注意 有关张量的更多信息请参阅此链接。 因此我们可以使用先前定义的两个参数占位符和常量来引入我们想要计算的模型。接下来我们定义计算模型。 会话内的以下语句构建x和y的乘积的数据结构并随后将操作结果分配给张量z。然后它如下 z tf.multiply(x, y)由于结果已由占位符z保存我们通过sess.run语句执行图。在这里我们提供两个值来将张量修补为图节点。它暂时用张量值替换操作的输出 z_output sess.run(z,feed_dict{x: 8, y:9})在最后的指令中我们打印结果 print(z_output)这打印输出72.0。 用 TensorFlow 急切执行 如前所述在启用 TensorFlow 的急切执行时我们可以立即执行 TensorFlow 操作因为它们是以命令方式从 Python 调用的。 启用急切执行后TensorFlow 函数会立即执行操作并返回具体值。这与tf.Session相反函数添加到图并创建计算图中的节点的符号引用。 TensorFlow 通过tf.enable_eager_execution提供急切执行的功能其中包含以下别名 tf.contrib.eager.enable_eager_executiontf.enable_eager_execution tf.enable_eager_execution具有以下签名 tf.enable_eager_execution(configNone,device_policyNone )在上面的签名中config是tf.ConfigProto用于配置执行操作的环境但这是一个可选参数。另一方面device_policy也是一个可选参数用于控制关于特定设备例如 GPU0上需要输入的操作如何处理不同设备例如GPU1 或 CPU上的输入的策略。 现在调用前面的代码将启用程序生命周期的急切执行。例如以下代码在 TensorFlow 中执行简单的乘法运算 import tensorflow as tfx tf.placeholder(tf.float32, shape[1, 1]) # a placeholder for variable x y tf.placeholder(tf.float32, shape[1, 1]) # a placeholder for variable y m tf.matmul(x, y)with tf.Session() as sess:print(sess.run(m, feed_dict{x: [[2.]], y: [[4.]]}))以下是上述代码的输出 8. 然而使用急切执行整体代码看起来更简单 import tensorflow as tf# Eager execution (from TF v1.7 onwards): tf.eager.enable_eager_execution() x [[2.]] y [[4.]] m tf.matmul(x, y)print(m)以下是上述代码的输出 tf.Tensor([[8.]], shape(1, 1), dtypefloat32) 你能理解在执行前面的代码块时会发生什么吗好了在启用了执行后操作在定义时执行Tensor对象保存具体值可以通过numpy()方法作为numpy.ndarray访问。 请注意在使用 TensorFlow API 创建或执行图后无法启用急切执行。通常建议在程序启动时调用此函数而不是在库中调用。虽然这听起来很吸引人但我们不会在即将到来的章节中使用此功能因为这是一个新功能尚未得到很好的探索。 TensorFlow 中的数据模型 TensorFlow 中的数据模型由张量表示。在不使用复杂的数学定义的情况下我们可以说张量在 TensorFlow 中识别多维数值数组。我们将在下一小节中看到有关张量的更多细节。 张量 让我们看一下来自维基百科张量的形式定义 “张量是描述几何向量标量和其他张量之间线性关系的几何对象。这种关系的基本例子包括点积叉积和线性映射。几何向量通常用于物理和工程应用以及标量他们自己也是张量。“ 该数据结构的特征在于三个参数秩形状和类型如下图所示 图 6张量只是具有形状阶数和类型的几何对象用于保存多维数组 因此张量可以被认为是指定具有任意数量的索引的元素的矩阵的推广。张量的语法与嵌套向量大致相同。 提示 张量只定义此值的类型以及在会话期间应计算此值的方法。因此它们不代表或保留操作产生的任何值。 有些人喜欢比较 NumPy 和 TensorFlow。然而实际上TensorFlow 和 NumPy 在两者都是 Nd 数组库的意义上非常相似 嗯NumPy 确实有 n 维数组支持但它不提供方法来创建张量函数并自动计算导数并且它没有 GPU 支持。下图是 NumPy 和 TensorFlow 的简短一对一比较 图 7NumPy 与 TensorFlow一对一比较 现在让我们看一下在 TensorFlow 图之前创建张量的替代方法我们将在后面看到其他的进给机制 X [[2.0, 4.0],[6.0, 8.0]] # X is a list of listsY np.array([[2.0, 4.0],[6.0, 6.0]], dtypenp.float32)#Y is a Numpy arrayZ tf.constant([[2.0, 4.0],[6.0, 8.0]]) # Z is a tensor 这里X是列表Y是来自 NumPy 库的 n 维数组Z是 TensorFlow 张量对象。现在让我们看看他们的类型 print(type(X))print(type(Y))print(type(Z))#Output class list class numpy.ndarray class tensorflow.python.framework.ops.Tensor好吧他们的类型打印正确。但是与其他类型相比我们正式处理张量的更方便的函数是tf.convert_to_tensor()函数如下 t1 tf.convert_to_tensor(X, dtypetf.float32) t2 tf.convert_to_tensor(Z, dtypetf.float32)现在让我们使用以下代码查看它们的类型 print(type(t1))print(type(t2))#Output: class tensorflow.python.framework.ops.Tensor class tensorflow.python.framework.ops.Tensor太棒了关于张量的讨论已经足够了。因此我们可以考虑以术语秩为特征的结构。 秩和形状 称为秩的维度单位描述每个张量。它识别张量的维数。因此秩被称为张量的阶数或维度。阶数零张量是标量阶数 1 张量是向量阶数 2 张量是矩阵。 以下代码定义了 TensorFlow scalarvectormatrix和cube_matrix。在下一个示例中我们将展示秩如何工作 import tensorflow as tf scalar tf.constant(100) vector tf.constant([1,2,3,4,5]) matrix tf.constant([[1,2,3],[4,5,6]])cube_matrix tf.constant([[[1],[2],[3]],[[4],[5],[6]],[[7],[8],[9]]])print(scalar.get_shape()) print(vector.get_shape()) print(matrix.get_shape()) print(cube_matrix.get_shape())结果打印在这里 () (5,) (2, 3) (3, 3, 1)张量的形状是它具有的行数和列数。现在我们将看看如何将张量的形状与其阶数联系起来 scalar.get_shape() TensorShape([])vector.get_shape() TensorShape([Dimension(5)])matrix.get_shape() TensorShape([Dimension(2), Dimension(3)])cube.get_shape() TensorShape([Dimension(3), Dimension(3), Dimension(1)]) 数据类型 除了阶数和形状张量具有数据类型。以下是数据类型列表 数据类型Python 类型描述DT_FLOATtf.float3232 位浮点DT_DOUBLEtf.float6464 位浮点DT_INT8tf.int88 位有符号整数DT_INT16tf.int1616 位有符号整数DT_INT32tf.int3232 位有符号整数DT_INT64tf.int6464 位有符号整数DT_UINT8tf.uint88 位无符号整数DT_STRINGtf.string可变长度字节数组。张量的每个元素都是一个字节数组DT_BOOLtf.bool布尔DT_COMPLEX64tf.complex64由两个 32 位浮点组成的复数实部和虚部DT_COMPLEX128tf.complex128由两个 64 位浮点组成的复数实部和虚部DT_QINT8tf.qint8量化操作中使用的 8 位有符号整数DT_QINT32tf.qint32量化操作中使用的 32 位有符号整数DT_QUINT8tf.quint8量化操作中使用的 8 位无符号整数 上表是不言自明的因此我们没有提供有关数据类型的详细讨论。 TensorFlow API 用于管理与 NumPy 数组之间的数据。 因此要构建具有常量值的张量将 NumPy 数组传递给tf.constant()运算符结果将是具有该值的张量 import tensorflow as tfimport numpy as np array_1d np.array([1,2,3,4,5,6,7,8,9,10]) tensor_1d tf.constant(array_1d)with tf.Session() as sess:print(tensor_1d.get_shape())print(sess.run(tensor_1d))运行该示例我们获得以下内容 (10,)[ 1 2 3 4 5 6 7 8 9 10] 要构建具有变量值的张量请使用 NumPy 数组并将其传递给tf.Variable构造器。结果将是具有该初始值的变量张量 import tensorflow as tf import numpy as np# Create a sample NumPy array array_2d np.array([(1,2,3),(4,5,6),(7,8,9)])# Now pass the preceding array to tf.Variable() tensor_2d tf.Variable(array_2d)# Execute the preceding op under an active session with tf.Session() as sess:sess.run(tf.global_variables_initializer())print(tensor_2d.get_shape())print sess.run(tensor_2d) # Finally, close the TensorFlow session when youre done sess.close()在前面的代码块中tf.global_variables_initializer()用于初始化我们之前创建的所有操作。如果需要创建一个初始值取决于另一个变量的变量请使用另一个变量的initialized_value()。这可确保以正确的顺序初始化变量。 结果如下 (3, 3)[[1 2 3][4 5 6][7 8 9]] 为了便于在交互式 Python 环境中使用我们可以使用InteractiveSession类然后将该会话用于所有Tensor.eval()和Operation.run()调用 import tensorflow as tf # Import TensorFlow import numpy as np # Import numpy# Create an interactive TensorFlow session interactive_session tf.InteractiveSession()# Create a 1d NumPy array array1 np.array([1,2,3,4,5]) # An array# Then convert the preceding array into a tensor tensor tf.constant(array1) # convert to tensor print(tensor.eval()) # evaluate the tensor opinteractive_session.close() # close the session提示 tf.InteractiveSession()只是方便的语法糖用于在 IPython 中保持默认会话打开。 结果如下 [1 2 3 4 5] 在交互式设置中例如 shell 或 IPython 笔记本这可能会更容易因为在任何地方传递会话对象都很繁琐。 注意 IPython 笔记本现在称为 Jupyter 笔记本。它是一个交互式计算环境您可以在其中组合代码执行富文本数学绘图和富媒体。有关更多信息感兴趣的读者请参阅此链接。 定义张量的另一种方法是使用tf.convert_to_tensor语句 import tensorflow as tf import numpy as np tensor_3d np.array([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],[[9, 10, 11], [12, 13, 14], [15, 16, 17]],[[18, 19, 20], [21, 22, 23], [24, 25, 26]]]) tensor_3d tf.convert_to_tensor(tensor_3d, dtypetf.float64)with tf.Session() as sess:print(tensor_3d.get_shape())print(sess.run(tensor_3d)) # Finally, close the TensorFlow session when youre done sess.close()以下是上述代码的输出 (3, 3, 3) [[[ 0\. 1\. 2.][ 3\. 4\. 5.][ 6\. 7\. 8.]][[ 9\. 10\. 11.][ 12\. 13\. 14.][ 15\. 16\. 17.]][[ 18\. 19\. 20.][ 21\. 22\. 23.][ 24\. 25\. 26.]]] 变量 变量是用于保存和更新参数的 TensorFlow 对象。必须初始化变量以便您可以保存并恢复它以便稍后分析代码。使用tf.Variable()或tf.get_variable()语句创建变量。而tf.get_varaiable()被推荐但tf.Variable()是低标签抽象。 在下面的示例中我们要计算从 1 到 10 的数字但让我们先导入 TensorFlow import tensorflow as tf我们创建了一个将初始化为标量值 0 的变量 value tf.get_variable(value, shape[], dtypetf.int32, initializerNone, regularizerNone, trainableTrue, collectionsNone)assign()和add()运算符只是计算图的节点因此在会话运行之前它们不会执行赋值 one tf.constant(1) update_value tf.assign_add(value, one) initialize_var tf.global_variables_initializer()我们可以实例化计算图 with tf.Session() as sess:sess.run(initialize_var)print(sess.run(value))for _ in range(5):sess.run(update_value)print(sess.run(value)) # Close the session让我们回想一下张量对象是操作结果的符号句柄但它实际上并不保存操作输出的值 0 1 2 3 4 5 运行 要获取操作的输出可以通过调用会话对象上的run()并传入张量来执行图。除了获取单个张量节点您还可以获取多个张量。 在下面的示例中使用run()调用一起提取sum和multiply张量 import tensorflow as tf constant_A tf.constant([100.0]) constant_B tf.constant([300.0]) constant_C tf.constant([3.0])sum_ tf.add(constant_A,constant_B) mul_ tf.multiply(constant_A,constant_C)with tf.Session() as sess:result sess.run([sum_,mul_])# _ means throw away afterwardsprint(result)输出如下 [array(400.],dtypefloat32),array([ 300.],dtypefloat32)] 应该注意的是所有需要执行的操作即为了产生张量值都运行一次每个请求的张量不是一次。 馈送和占位符 有四种方法将数据输入 TensorFlow 程序更多信息请参阅此链接 数据集 API这使您能够从简单和可重用的分布式文件系统构建复杂的输入管道并执行复杂的操作。如果您要处理不同数据格式的大量数据建议使用数据集 API。数据集 API 为 TensorFlow 引入了两个新的抽象用于创建可馈送数据集tf.contrib.data.Dataset通过创建源或应用转换操作和tf.contrib.data.Iterator。馈送这允许我们将数据注入计算图中的任何张量。从文件中读取这允许我们使用 Python 的内置机制开发输入管道用于从图开头的数据文件中读取数据。预加载数据对于小数据集我们可以使用 TensorFlow 图中的常量或变量来保存所有数据。 在本节中我们将看到馈送机制的例子。我们将在接下来的章节中看到其他方法。 TensorFlow 提供了一种馈送机制允许我们将数据注入计算图中的任何张量。您可以通过feed_dict参数将源数据提供给启动计算的run()或eval()调用。 提示 使用feed_dict参数进行馈送是将数据提供到 TensorFlow 执行图中的最低效方法并且仅应用于需要小数据集的小型实验。它也可以用于调试。 我们还可以用馈送数据即变量和常量替换任何张量。最佳做法是使用tf.placeholder()使用 TensorFlow 占位符节点。占位符专门用作馈送的目标。空占位符未初始化因此不包含任何数据。 因此如果在没有馈送的情况下执行它它将始终生成错误因此您不会忘记提供它。以下示例显示如何提供数据以构建随机2×3矩阵 import tensorflow as tf import numpy as npa 3 b 2 x tf.placeholder(tf.float32,shape(a,b)) y tf.add(x,x)data np.random.rand(a,b) sess tf.Session() print(sess.run(y,feed_dict{x:data}))sess.close()# close the session输出如下 [[ 1.78602004 1.64606333][ 1.03966308 0.99269408][ 0.98822606 1.50157797]]通过 TensorBoard 可视化计算 TensorFlow 包含函数 允许您在名为 TensorBoard 的可视化工具中调试和优化程序。使用 TensorBoard您可以以图形方式观察有关图任何部分的参数和详细信息的不同类型的统计数据。 此外在使用复杂的 DNN 进行预测建模时图可能很复杂且令人困惑。为了更容易理解调试和优化 TensorFlow 程序您可以使用 TensorBoard 可视化 TensorFlow 图绘制有关图执行的量化指标并显示其他数据例如通过它的图像。 因此TensorBoard 可以被认为是一个用于分析和调试预测模型的框架。 TensorBoard 使用所谓的摘要来查看模型的参数一旦执行了 TensorFlow 代码我们就可以调用 TensorBoard 来查看 GUI 中的摘要。 TensorBoard 如何运作 TensorFlow 使用计算图来执行应用。在计算图中节点表示操作弧是操作之间的数据。 TensorBoard 的主要思想是将摘要与图上的节点操作相关联。代码运行时摘要操作将序列化节点的数据并将数据输出到文件中。稍后TensorBoard 将可视化汇总操作。有关更详细的讨论读者可以参考此链接。 简而言之TensorBoard 是一套 Web 应用用于检查和理解您的 TensorFlow 运行和图。使用 TensorBoard 时的工作流程如下 构建计算图/代码 将摘要操作附加到您要检查的节点 像往常一样开始运行图 运行摘要操作 执行完成后运行 TensorBoard 以显示摘要输出 file_writer tf.summary.FileWriter(/path/to/logs, sess.graph)对于步骤 2即在运行 TensorBoard 之前请确保通过创建摘要编写器在日志目录中生成摘要数据 sess.graph包含图定义启用图可视化工具 现在如果你在终端中键入$ which tensorboard如果你用 pip 安装它它应该存在 rootubuntu:~$ which tensorboard /usr/local/bin/tensorboard 你需要给它一个日志目录。当您在运行图的目录中时可以使用以下内容从终端启动它 tensorboard --logdir path/to/logs 当 TensorBoard 配置完全时可以通过发出以下命令来访问它 # Make sure theres no space before or after $ tensorboard –logdirtrace_file_name 现在您只需输入http://localhost:6006/即可从浏览器访问localhost:6006。然后它应该是这样的 图 8在浏览器上使用 TensorBoard 注意 TensorBoard 可用于谷歌浏览器或 Firefox。其他浏览器可能有效但可能存在错误或表现问题。 这已经太过分了吗不要担心在上一节中我们将结合前面解释的所有想法构建单个输入神经元模型并使用 TensorBoard 进行分析。 线性回归及更多 在本节中我们将仔细研究 TensorFlow 和 TensorBoard 的主要概念并尝试做一些基本操作来帮助您入门。我们想要实现的模型模拟线性回归。 在统计和 ML 中线性回归是一种经常用于衡量变量之间关系的技术。这是一种非常简单但有效的算法也可用于预测建模。 线性回归模拟因变量y[i]自变量x[i]和随机项b。这可以看作如下 使用 TensorFlow 的典型线性回归问题具有以下工作流程该工作流程更新参数以最小化给定成本函数参见下图 图 9在 TensorFlow 中使用线性回归的学习算法 现在让我们尝试按照前面的图通过概念化前面的等式将其重现为线性回归。为此我们将编写一个简单的 Python 程序用于在 2D 空间中创建数据。然后我们将使用 TensorFlow 来寻找最适合数据点的线如下图所示 # Import libraries (Numpy, matplotlib)import numpy as np import matplotlib.pyplot as plot# Create 1000 points following a function y0.1 * x 0.4z (i.e. # y W * x b) with some normal random distribution:num_points 1000 vectors_set []# Create a few random data points for i in range(num_points):W 0.1 # Wb 0.4 # bx1 np.random.normal(0.0, 1.0)#in: mean, standard deviationnd np.random.normal(0.0, 0.05)#in:mean,standard deviationy1 W * x1 b# Add some impurity with normal distribution -i.e. nd y1 y1 nd# Append them and create a combined vector set:vectors_set.append([x1, y1])# Separate the data point across axises: x_data [v[0] for v in vectors_set] y_data [v[1] for v in vectors_set]# Plot and show the data points in a 2D space plot.plot(x_data, y_data, ro, labelOriginal data) plot.legend() plot.show()如果您的编译器没有报错您应该得到以下图表 图 10随机生成但原始数据 好吧到目前为止我们刚刚创建了一些数据点而没有可以通过 TensorFlow 执行的相关模型。因此下一步是创建一个线性回归模型该模型可以获得从输入数据点估计的输出值y即x_data。在这种情况下我们只有两个相关参数W和b。 现在的目标是创建一个图允许我们根据输入数据x_data通过将它们调整为y_data来找到这两个参数的值。因此我们的目标函数如下 如果你还记得我们在 2D 空间中创建数据点时定义了W 0.1和b 0.4。 TensorFlow 必须优化这两个值使W趋于 0.1 和b为 0.4。 解决此类优化问题的标准方法是迭代数据点的每个值并调整W和b的值以便为每次迭代获得更精确的答案。要查看值是否确实在改善我们需要定义一个成本函数来衡量某条线的优质程度。 在我们的例子中成本函数是均方误差这有助于我们根据实际数据点与每次迭代的估计距离函数之间的距离函数找出误差的平均值。我们首先导入 TensorFlow 库 import tensorflow as tf W tf.Variable(tf.zeros([1])) b tf.Variable(tf.zeros([1])) y W * x_data b在前面的代码段中我们使用不同的策略生成一个随机点并将其存储在变量W中。现在让我们定义一个损失函数loss mean[(y - y_data)^2]这将返回一个标量值其中包含我们之间所有距离的均值。数据和模型预测。就 TensorFlow 约定而言损失函数可表示如下 loss tf.reduce_mean(tf.square(y - y_data))前一行实际上计算均方误差MSE。在不进一步详述的情况下我们可以使用一些广泛使用的优化算法例如 GD。在最低级别GD 是一种算法它对我们已经拥有的一组给定参数起作用。 它以一组初始参数值开始并迭代地移向一组值这些值通过采用另一个称为学习率的参数来最小化函数。通过在梯度函数的负方向上采取步骤来实现这种迭代最小化 optimizer tf.train.GradientDescentOptimizer(0.6) train optimizer.minimize(loss)在运行此优化函数之前我们需要初始化到目前为止所有的变量。让我们使用传统的 TensorFlow 技术如下所示 init tf.global_variables_initializer() sess tf.Session() sess.run(init)由于我们已经创建了 TensorFlow 会话我们已准备好进行迭代过程帮助我们找到W和b的最佳值 for i in range(6):sess.run(train)print(i, sess.run(W), sess.run(b), sess.run(loss))您应该观察以下输出 0 [ 0.18418592] [ 0.47198644] 0.0152888 1 [ 0.08373772] [ 0.38146532] 0.00311204 2 [ 0.10470386] [ 0.39876288] 0.00262051 3 [ 0.10031486] [ 0.39547175] 0.00260051 4 [ 0.10123629] [ 0.39609471] 0.00259969 5 [ 0.1010423] [ 0.39597753] 0.00259966 6 [ 0.10108326] [ 0.3959994] 0.00259966 7 [ 0.10107458] [ 0.39599535] 0.00259966 你可以看到算法从W 0.18418592和b 0.47198644的初始值开始损失非常高。然后算法通过最小化成本函数来迭代地调整值。在第八次迭代中所有值都倾向于我们期望的值。 现在如果我们可以绘制它们怎么办让我们通过在for循环下添加绘图线来实现 如下所示 for i in range(6):sess.run(train)print(i, sess.run(W), sess.run(b), sess.run(loss))plot.plot(x_data, y_data, ro, labelOriginal data)plot.plot(x_data, sess.run(W)*x_data sess.run(b))plot.xlabel(X)plot.xlim(-2, 2)plot.ylim(0.1, 0.6)plot.ylabel(Y)plot.legend()plot.show()前面的代码块应该生成下图虽然合并在一起 图 11在第六次迭代后优化损失函数的线性回归 现在让我们进入第 16 次迭代 0 [ 0.23306453] [ 0.47967502] 0.0259004 1 [ 0.08183448] [ 0.38200468] 0.00311023 2 [ 0.10253634] [ 0.40177572] 0.00254209 3 [ 0.09969243] [ 0.39778906] 0.0025257 4 [ 0.10008509] [ 0.39859086] 0.00252516 5 [ 0.10003048] [ 0.39842987] 0.00252514 6 [ 0.10003816] [ 0.39846218] 0.00252514 7 [ 0.10003706] [ 0.39845571] 0.00252514 8 [ 0.10003722] [ 0.39845699] 0.00252514 9 [ 0.10003719] [ 0.39845672] 0.00252514 10 [ 0.1000372] [ 0.39845678] 0.00252514 11 [ 0.1000372] [ 0.39845678] 0.00252514 12 [ 0.1000372] [ 0.39845678] 0.00252514 13 [ 0.1000372] [ 0.39845678] 0.00252514 14 [ 0.1000372] [ 0.39845678] 0.00252514 15 [ 0.1000372] [ 0.39845678] 0.00252514 好多了我们更接近优化的值对吧现在如果我们通过 TensorFlow 进一步改进我们的可视化分析以帮助可视化这些图中发生的事情该怎么办 TensorBoard 提供了一个网页用于调试图并检查变量节点边缘及其相应的连接。 此外我们需要使用变量标注前面的图例如损失函数Wby_datax_data等。然后您需要通过调用tf.summary.merge_all()函数生成所有摘要。 现在我们需要对前面的代码进行如下更改。但是最好使用tf.name_scope()函数对图上的相关节点进行分组。因此我们可以使用tf.name_scope()来组织 TensorBoard 图表视图中的内容但让我们给它一个更好的名称 with tf.name_scope(LinearRegression) as scope:W tf.Variable(tf.zeros([1]))b tf.Variable(tf.zeros([1]))y W * x_data b然后让我们以类似的方式标注损失函数但使用合适的名称例如LossFunction with tf.name_scope(LossFunction) as scope:loss tf.reduce_mean(tf.square(y - y_data))让我们标注 TensorBoard 所需的损失权重和偏置 loss_summary tf.summary.scalar(loss, loss) w_ tf.summary.histogram(W, W) b_ tf.summary.histogram(b, b)一旦您标注了图就可以通过合并来配置摘要了 merged_op tf.summary.merge_all()在运行训练之前初始化之后使用tf.summary.FileWriter() API 编写摘要如下所示 writer_tensorboard tf.summary.FileWriter(logs/, tf.get_default_graph())然后按如下方式启动 TensorBoard $ tensorboard –logdirtrace_dir_name在我们的例子中它可能类似于以下内容 $ tensorboard --logdir/home/root/LR/现在让我们转到http://localhost:6006并单击 GRAPH 选项卡。您应该看到以下图表 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qe42IjO0-1681565849695)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_02_11.jpg)] 图 12TensorBoard 上的主图和辅助节点 提示 请注意Ubuntu 可能会要求您安装python-tk包。您可以通过在 Ubuntu 上执行以下命令来执行此操作 $ sudo apt-get install python-tk # For Python 3.x, use the following $ sudo apt-get install python3-tk 真实数据集的线性回归回顾 在上一节中我们看到线性回归的一个例子。我们看到了如何将 TensorFlow 与随机生成的数据集一起使用即假数据。我们已经看到回归是一种用于预测连续而非离散输出的监督机器学习。 然而对假数据进行线性回归就像买一辆新车但从不开车。这个令人敬畏的机器需要在现实世界中使用幸运的是许多数据集可在线获取以测试您新发现的回归知识。 其中一个是波士顿住房数据集可以从 UCI 机器学习库下载。它也可以作为 scikit-learn 的预处理数据集使用。 所以让我们开始导入所有必需的库包括 TensorFlowNumPyMatplotlib 和 scikit-learn import matplotlib.pyplot as plt import tensorflow as tf import numpy as np from numpy import genfromtxt from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split接下来我们需要准备由波士顿住房数据集中的特征和标签组成的训练集。read_boston_data ()方法从 scikit-learn 读取并分别返回特征和标签 def read_boston_data():boston load_boston()features np.array(boston.data)labels np.array(boston.target)return features, labels现在我们已经拥有特征和标签我们还需要使用normalizer()方法对特征进行标准化。这是方法的签名 def normalizer(dataset):mu np.mean(dataset,axis0)sigma np.std(dataset,axis0)return(dataset - mu)/sigmabias_vector()用于将偏差项即全 1附加到我们在前一步骤中准备的标准化特征。它对应于前一个例子中直线方程中的b项 def bias_vector(features,labels):n_training_samples features.shape[0]n_dim features.shape[1]f np.reshape(np.c_[np.ones(n_training_samples),features],[n_training_samples,n_dim 1])l np.reshape(labels,[n_training_samples,1])return f, l我们现在将调用这些方法并将数据集拆分为训练和测试75% 用于训练和休息用于测试 features,labels read_boston_data() normalized_features normalizer(features) data, label bias_vector(normalized_features,labels) n_dim data.shape[1] # Train-test split train_x, test_x, train_y, test_y train_test_split(data,label,test_size 0.25,random_state 100)现在让我们使用 TensorFlow 的数据结构例如占位符标签和权重 learning_rate 0.01 training_epochs 100000 log_loss np.empty(shape[1],dtypefloat) X tf.placeholder(tf.float32,[None,n_dim]) #takes any number of rows but n_dim columns Y tf.placeholder(tf.float32,[None,1]) # #takes any number of rows but only 1 continuous column W tf.Variable(tf.ones([n_dim,1])) # W weight vector做得好我们已经准备好构建 TensorFlow 图所需的数据结构。现在是构建线性回归的时候了这非常简单 y_ tf.matmul(X, W) cost_op tf.reduce_mean(tf.square(y_ - Y)) training_step tf.train.GradientDescentOptimizer(learning_rate).minimize(cost_op)在前面的代码段中第一行将特征矩阵乘以可用于预测的权重矩阵。第二行计算损失即回归线的平方误差。最后第三行执行 GD 优化的一步以最小化平方误差。 提示 使用哪种优化器使用优化器的主要目的是最小化成本因此我们必须定义一个优化器。使用最常见的优化器如 SGD学习率必须以1 / T进行缩放才能获得收敛其中T是迭代次数。 Adam 或 RMSProp 尝试通过调整步长来自动克服此限制以使步长与梯度具有相同的比例。此外在前面的示例中我们使用了 Adam 优化器它在大多数情况下都表现良好。 然而如果您正在训练神经网络计算梯度是必须的使用实现 RMSProp 算法的RMSPropOptimizer函数是一个更好的主意因为它是在小批量设置中学习的更快的方式。研究人员还建议在训练深度 CNN 或 DNN 时使用 Momentum 优化器。 从技术上讲RMSPropOptimizer 是一种先进的梯度下降形式它将学习率除以指数衰减的平方梯度平均值。衰减参数的建议设置值为 0.9而学习率的良好默认值为 0.001。 例如在 TensorFlow 中tf.train.RMSPropOptimizer()帮助我们轻松使用它 optimizer tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost_op)现在在我们开始训练模型之前我们需要使用initialize_all_variables()方法初始化所有变量如下所示 init tf.initialize_all_variables()太棒了现在我们已经设法准备好所有组件我们已经准备好训练实际的训练了。我们首先创建 TensorFlow 会话如下所示 sess tf.Session() sess.run(init_op) for epoch in range(training_epochs):sess.run(training_step,feed_dict{X:train_x,Y:train_y})log_loss np.append(log_loss,sess.run(cost_op,feed_dict{X: train_x,Y: train_y}))训练完成后我们就可以对看不见的数据进行预测。然而看到完成训练的直观表示会更令人兴奋。因此让我们使用 Matplotlib 将成本绘制为迭代次数的函数 plt.plot(range(len(log_loss)),log_loss) plt.axis([0,training_epochs,0,np.max(log_loss)]) plt.show()以下是上述代码的输出 图 13作为迭代次数函数的成本 对测试数据集做一些预测并计算均方误差 pred_y sess.run(y_, feed_dict{X: test_x}) mse tf.reduce_mean(tf.square(pred_y - test_y)) print(MSE: %.4f % sess.run(mse))以下是上述代码的输出 MSE: 27.3749 最后让我们展示最佳拟合线 fig, ax plt.subplots() ax.scatter(test_y, pred_y) ax.plot([test_y.min(), test_y.max()], [test_y.min(), test_y.max()], k--, lw3) ax.set_xlabel(Measured) ax.set_ylabel(Predicted) plt.show()以下是上述代码的输出 图 14预测值与实际值 总结 TensorFlow 旨在通过 ML 和 DL 轻松地为每个人进行预测分析但使用它确实需要对一些通用原则和算法有一个正确的理解。 TensorFlow 的最新版本带有许多令人兴奋的新功能所以我们试图覆盖它们以便您可以轻松使用它们。总之这里简要回顾一下本章已经解释过的 TensorFlow 的关键概念 图每个 TensorFlow 计算可以表示为数据流图其中每个图构建为一组操作对象。有三种核心图数据结构tf.Graphtf.Operation和tf.Tensor 。操作图节点将一个或多个张量作为输入并产生一个或多个张量作为输出。节点可以由操作对象表示用于执行诸如加法乘法除法减法或更复杂操作的计算单元。张量它们就像高维数组对象。换句话说它们可以表示为数据流图的边缘并且是不同操作的输出。会话会话对象是一个实体它封装了执行操作对象的环境以便在数据流图上运行计算。结果在run()或eval()调用内部求值张量对象。 在本章的后面部分我们介绍了 TensorBoard它是分析和调试神经网络模型的强大工具。最后我们看到了如何在假数据集和真实数据集上实现最简单的基于 TensorFlow 的线性回归模型之一。 在下一章中我们将讨论不同 FFNN 架构的理论背景如深度信念网络DBN和多层感知器MLP。 然后我们将展示如何训练和分析评估模型所需的表现指标然后通过一些方法调整 FFNN 的超参数以优化表现。最后我们将提供两个使用 MLP 和 DBN 的示例说明如何为银行营销数据集建立非常强大和准确的预测分析模型。 三、实现前馈神经网络 自动识别手写数字是一个重要的问题可以在许多实际应用中找到。在本节中我们将实现一个前馈网络来解决这个问题。 图 3从 MNIST 数据库中提取的数据示例 为了训练和测试已实现的模型我们将使用一个名为 MNIST 的手写数字最着名的数据集。 MNIST 数据集是一个包含 60,000 个示例的训练集和一个包含 10,000 个示例的测试集。存储在示例文件中的数据示例如上图所示。 源图像最初是黑白的。之后为了将它们标准化为20×20像素的大小由于抗混叠滤波器用于调整大小的效果引入了中间亮度级别。随后在28×28像素的区域中将图像聚焦在像素的质心中以便改善学习过程。整个数据库存储在四个文件中 train-images-idx3-ubyte.gz训练集图像9912422 字节train-labels-idx1-ubyte.gz训练集标签28881 字节t10k-images-idx3-ubyte.gz测试集图像1648877 字节t10k-labels-idx1-ubyte.gz测试集标签4542 字节 每个数据库包含两个文件的 ;第一个包含图像而第二个包含相应的标签。 探索 MNIST 数据集 让我们看一下如何访问 MNIST 数据的简短示例以及如何显示所选图像。为此只需执行Explore_MNIST.py脚本。首先我们必须导入 numpy因为我们必须进行一些图像处理 import numpy as npMatplotlib 中的pyplot函数用于绘制图像 import matplotlib.pyplot as plt我们将使用tensorflow.examples.tutorials.mnist中的input_data类它允许我们下载 MNIST 数据库并构建数据集 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data然后我们使用read_data_sets方法加载数据集 import os dataPath temp/ if not os.path.exists(dataPath):os.makedirs(dataPath) input input_data.read_data_sets(dataPath, one_hotTrue)图像将保存在temp/目录中。现在让我们看看图像和标签的形状 print(input.train.images.shape) print(input.train.labels.shape) print(input.test.images.shape) print(input.test.labels.shape)以下是上述代码的输出 (55000, 784) (55000, 10) (10000, 784) (10000, 10) 使用 Python 库matplotlib我们想要可视化一个数字 image_0 input.train.images[0] image_0 np.resize(image_0,(28,28)) label_0 input.train.labels[0] print(label_0)以下是上述代码的输出 [ 0\. 0\. 0\. 0\. 0\. 0\. 0\. 1\. 0\. 0.] 数字1是数组的第八个位置。这意味着我们图像的数字是数字 7。最后我们必须验证数字是否真的是 7。我们可以使用导入的plt函数来绘制image_0张量 plt.imshow(image_0, cmapGreys_r) plt.show()图 4从 MNIST 数据集中提取的图像 Softmax 分类器 在上一节中我们展示了如何访问和操作 MNIST 数据集。在本节中我们将看到如何使用前面的数据集来解决 TensorFlow 手写数字的分类问题。我们将应用所学的概念来构建更多神经网络模型以便评估和比较所采用的不同方法的结果。 将要实现的第一个前馈网络架构如下图所示 图 5softmax 神经网络架构 我们将构建一个五层网络第一层到第四层是 Sigmoid 结构第五层是 softmax 激活函数。请记住定义此网络是为了激活它是一组正值总和等于 1。这意味着输出的第j个值是与网络输入对应的类j的概率。让我们看看如何实现我们的神经网络模型。 为了确定网络的适当大小即层中的神经元或单元的数量即隐藏层的数量和每层神经元的数量通常我们依赖于一般的经验标准个人经验或适当的测试。这些是需要调整的一些超参数。在本章的后面我们将看到一些超参数优化的例子。 下表总结了已实现的网络架构。它显示了每层神经元的数量以及相应的激活函数 层神经元数量激活函数1L 200Sigmoid2M 100Sigmoid3N 60Sigmoid4O 30Sigmoid510Softmax 前四层的激活函数是 Sigmoid 函数。激活函数的最后一层始终是 softmax因为网络的输出必须表示输入数字的概率。通常中间层的数量和大小会极大地影响的网络表现 以积极的方式因为在这些层上是基于网络推广的能力并检测输入的特殊特征以负面的方式因为如果网络是冗余的那么它会不必要地减轻学习阶段的负担 为此只需执行five_layers_sigmoid.py脚本。首先我们将通过导入以下库来开始实现网络 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import math from tensorflow.python.framework import ops import random import os接下来我们将设置以下配置参数 logs_path log_sigmoid/ # logging path batch_size 100 # batch size while performing training learning_rate 0.003 # Learning rate training_epochs 10 # training epoch display_epoch 1然后我们将下载图像和标签并准备数据集 dataPath temp/ if not os.path.exists(dataPath):os.makedirs(dataPath) mnist input_data.read_data_sets(dataPath, one_hotTrue) # MNIST to be downloaded从输入层开始我们现在将看看如何构建网络架构。输入层现在是形状[1×784]的张量 - 即[1,28 * 28]它代表要分类的图像 X tf.placeholder(tf.float32, [None, 784], nameInputData) # image shape 28*28784 XX tf.reshape(X, [-1, 784]) # reshape input Y_ tf.placeholder(tf.float32, [None, 10], nameLabelData) # 0-9 digits 10 classes第一层接收要分类的输入图像的像素与W1权重连接组合并添加到B1偏差张量的相应值 W1 tf.Variable(tf.truncated_normal([784, L], stddev0.1)) # Initialize random weights for the hidden layer 1 B1 tf.Variable(tf.zeros([L])) # Bias vector for layer 1第一层通过 sigmoid 激活函数将其输出发送到第二层 Y1 tf.nn.sigmoid(tf.matmul(XX, W1) B1) # Output from layer 1第二层从第一层接收Y1输出将其与W2权重连接组合并将其添加到B2偏差张量的相应值 W2 tf.Variable(tf.truncated_normal([L, M], stddev0.1)) # Initialize random weights for the hidden layer 2 B2 tf.Variable(tf.ones([M])) # Bias vector for layer 2第二层通过 sigmoid 激活函数将其输出发送到第三层 Y2 tf.nn.sigmoid(tf.matmul(Y1, W2) B2) # Output from layer 2第三层接收来自第二层的Y2输出将其与W3权重连接组合并将其添加到B3偏差张量的相应值 W3 tf.Variable(tf.truncated_normal([M, N], stddev0.1)) # Initialize random weights for the hidden layer 3 B3 tf.Variable(tf.ones([N])) # Bias vector for layer 3第三层通过 sigmoid 激活函数将其输出发送到第四层 Y3 tf.nn.sigmoid(tf.matmul(Y2, W3) B3) # Output from layer 3第四层接收来自第三层的Y3输出将其与W4权重连接组合并将其添加到B4偏差张量的相应值 W4 tf.Variable(tf.truncated_normal([N, O], stddev0.1)) # Initialize random weights for the hidden layer 4 B4 tf.Variable(tf.ones([O])) # Bias vector for layer 4然后通过 Sigmoid 激活函数将第四层的输出传播到第五层 Y4 tf.nn.sigmoid(tf.matmul(Y3, W4) B4) # Output from layer 4第五层将在输入中接收来自第四层的激活O 30该激活将通过softmax激活函数转换为每个数字的相应概率类别 W5 tf.Variable(tf.truncated_normal([O, 10], stddev0.1)) # Initialize random weights for the hidden layer 5 B5 tf.Variable(tf.ones([10])) # Bias vector for layer 5 Ylogits tf.matmul(Y4, W5) B5 # computing the logits Y tf.nn.softmax(Ylogits)# output from layer 5这里我们的损失函数是目标和softmax激活函数之间的交叉熵应用于模型的预测 cross_entropy tf.nn.softmax_cross_entropy_with_logits_v2(logitsYlogits, labelsY) # final outcome using softmax cross entropy cost_op tf.reduce_mean(cross_entropy)*100另外我们定义correct_prediction和模型的准确率 correct_prediction tf.equal(tf.argmax(Y, 1), tf.argmax(Y_, 1)) accuracy tf.reduce_mean(tf.cast(correct_prediction, tf.float32))现在我们需要使用优化器来减少训练误差。与简单的GradientDescentOptimizer相比AdamOptimizer具有几个优点。实际上它使用更大的有效步长算法将收敛到此步长而不进行微调 # Optimization op (backprop) train_op tf.train.AdamOptimizer(learning_rate).minimize(cost_op)Optimizer基类提供了计算损失梯度的方法并将梯度应用于变量。子类集合实现了经典的优化算法例如GradientDescent和Adagrad。在 TensorFlow 中训练 NN 模型时我们从不实例化Optimizer类本身而是实例化以下子类之一 tf.train.Optimizertf.train.GradientDescentOptimizertf.train.AdadeltaOptimizertf.train.AdagradOptimizertf.train.AdagradDAOptimizertf.train.MomentumOptimizertf.train.AdamOptimizertf.train.FtrlOptimizertf.train.ProximalGradientDescentOptimizertf.train.ProximalAdagradOptimizertf.train.RMSPropOptimizer 见此链接和tf.contrib.opt用于更多优化器。 然后让我们构建一个将所有操作封装到范围中的模型使 TensorBoard 的图可视化更加方便 # Create a summary to monitor cost tensor tf.summary.scalar(cost, cost_op) # Create a summary to monitor accuracy tensor tf.summary.scalar(accuracy, accuracy) # Merge all summaries into a single op summary_op tf.summary.merge_all()最后我们将开始训练 with tf.Session() as sess:# Run the initializersess.run(init_op)# op to write logs to TensorBoardwriter tf.summary.FileWriter(logs_path, graphtf.get_default_graph())for epoch in range(training_epochs):batch_count int(mnist.train.num_examples/batch_size)for i in range(batch_count):batch_x, batch_y mnist.train.next_batch(batch_size)_,summary sess.run([train_op, summary_op], feed_dict{X: batch_x, Y_: batch_y})writer.add_summary(summary, epoch * batch_count i)print(Epoch: , epoch)print(Optimization Finished!)print(Accuracy: , accuracy.eval(feed_dict{X: mnist.test.images, Y_: mnist.test.labels}))定义摘要和会话运行的源代码几乎与前一个相同。我们可以直接转向评估实现的模型。运行模型时我们有以下输出 运行此代码后的最终测试设置准确率应约为 97% Extracting temp/train-images-idx3-ubyte.gz Extracting temp/train-labels-idx1-ubyte.gz Extracting temp/t10k-images-idx3-ubyte.gz Extracting temp/t10k-labels-idx1-ubyte.gz Epoch: 0 Epoch: 1 Epoch: 2 Epoch: 3 Epoch: 4 Epoch: 5 Epoch: 6 Epoch: 7 Epoch: 8 Epoch: 9 Optimization Finished! Accuracy: 0.9 715 现在我们可以通过在运行文件夹中打开终端然后执行以下命令来移动到 TensorBoard $ tensorboard --logdirlog_sigmoid/ # if required, provide absolute path 然后我们在localhost上打开浏览器。在下图中我们显示了成本函数的趋势作为示例数量的函数在训练集上以及测试集的准确率 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJptXo1w-1681565849696)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_03_06.jpg)] 图 6测试集上的准确率函数以及训练集上的成本函数 成本函数随着迭代次数的增加而减少。如果没有发生这种情况则意味着出现了问题。在最好的情况下这可能只是因为某些参数未正确设置。在最坏的情况下构建的数据集中可能存在问题例如信息太少或图像质量差。如果发生这种情况我们必须直接修复数据集。 到目前为止我们已经看到了 FFNN 的实现。但是使用真实数据集探索更有用的 FFNN 实现会很棒。我们将从 MLP 开始。 实现多层感知器MLP 感知器单层 LTU 组成每个神经元连接到所有输入。这些连接通常使用称为输入神经元的特殊传递神经元来表示它们只输出它们被输入的任何输入。此外通常添加额外的偏置特征x0 1。 这种偏置特征通常使用称为偏置神经元的特殊类型的神经元来表示一直输出 1。具有两个输入和三个输出的感知器在图 7 中表示。该感知器可以同时将实例分类为三个不同的二元类这使其成为多输出分类器 图 7具有两个输入和三个输出的感知器 由于每个输出神经元的决策边界是线性的因此感知器无法学习复杂模式。然而如果训练实例是线性可分的研究表明该算法将收敛于称为“感知器收敛定理”的解决方案。 MLP 是 FFNN这意味着它是来自不同层的神经元之间的唯一连接。更具体地MLP 由一个通过输入层一个或多个 LTU 层称为隐藏层和一个称为输出层的 LTU 的最后一层组成。除输出层外每一层都包含一个偏置神经元并作为完全连接的二分图连接到下一层 图 8MLP 由一个输入层一个隐藏层和一个输出层组成 训练 MLP MLP 在 1986 年首次使用反向传播训练算法成功训练。然而现在这种算法的优化版本被称为梯度下降。在训练阶段期间对于每个训练实例算法将其馈送到网络并计算每个连续层中的每个神经元的输出。 训练算法测量网络的输出误差即期望输出和网络的实际输出之间的差异并计算最后隐藏层中每个神经元对每个输出神经元误差的贡献程度。然后它继续测量这些误差贡献中有多少来自先前隐藏层中的每个神经元依此类推直到算法到达输入层。通过在网络中向后传播误差梯度该反向传递有效地测量网络中所有连接权重的误差梯度。 在技​​术上通过反向传播方法计算每层的成本函数的梯度。梯度下降的想法是有一个成本函数显示某些神经网络的预测输出与实际输出之间的差异 图 9用于无监督学习的 ANN 的示例实现 有几种已知类型的代价函数例如平方误差函数和对数似然函数。该成本函数的选择可以基于许多因素。梯度下降法通过最小化此成本函数来优化网络的权重。步骤如下 权重初始化计算神经网络的预测输出通常称为转发传播步骤计算成本/损失函数。一些常见的成本/损失函数包括对数似然函数和平方误差函数计算成本/损失函数的梯度。对于大多数 DNN 架构最常见的方法是反向传播基于当前权重的权重更新以及成本/损失函数的梯度步骤 2 到 5 的迭代直到成本函数达到某个阈值或经过一定量的迭代 图 9 中可以看到梯度下降的图示。该图显示了基于网络权重的神经网络的成本函数。在梯度下降的第一次迭代中我们将成本函数应用于一些随机初始权重。对于每次迭代我们在梯度方向上更新权重这对应于图 9 中的箭头。重复更新直到一定次数的迭代或直到成本函数达到某个阈值。 使用 MLP 多层感知器通常用于以监督方式解决分类和回归问题。尽管 CNN 逐渐取代了它们在图像和视频数据中的实现但仍然可以有效地使用低维和数字特征 MLP可以解决二元和多类分类问题。 图 10用于分类的现代 MLP包括 ReLU 和 softmax 然而对于多类分类任务和训练通常通过用共享 softmax 函数替换各个激活函数来修改输出层。每个神经元的输出对应于相应类的估计概率。请注意信号仅在一个方向上从输入流向输出因此该架构是 FFNN 的示例。 作为案例研究我们将使用银行营销数据集。这些数据与葡萄牙银行机构的直接营销活动有关。营销活动基于电话。通常不止一次联系同一个客户以评估产品银行定期存款是是还是不否订阅。 目标是使用 MLP 来预测客户是否会订阅定期存款变量 y即二元分类问题。 数据集描述 我想在此承认有两个来源。这个数据集被用于 Moro 和其他人发表的一篇研究论文中一种数据驱动的方法来预测银行电话营销的成功决策支持系统Elsevier2014 年 6 月。后来它被捐赠给了 UCI 机器学习库可以从此链接下载。 根据数据集描述有四个数据集 bank-additional-full.csv这包括所有示例41,188和 20 个输入按日期排序从 2008 年 5 月到 2010 年 11 月。这些数据非常接近 Moro 和其他人分析的数据bank-additional.csv这包括 10% 的例子4119从 1 和 20 个输入中随机选择bank-full.csv这包括按日期排序的所有示例和 17 个输入此数据集的较旧版本输入较少bank.csv这包括 10% 的示例和 17 个输入从 3 中随机选择此数据集的较旧版本输入较少 数据集中有 21 个属性。独立变量可以进一步分类为与客户相关的数据属性 1 到 7与当前活动的最后一次联系属性 8 到 11相关。其他属性属性 12 至 15以及社会和经济背景属性属性 16 至 20被分类。因变量由 y 指定最后一个属性21 ID属性说明1age年龄数字。2job这是类别格式的职业类型具有可能的值adminblue-collarentrepreneurhousemaidmanagementretiredself-employedservicesstudenttechnicianunemployed和unknown。3marital这是类别格式的婚姻状态具有可能的值divorced或widowedmarriedsingle和unknown。4education这是类别格式的教育背景具有如下可能的值basic.4ybasic.6ybasic.9yhigh.schoolilliterateprofessional.courseuniversity.degree和unknown。五default这是类别格式的信用默认情况下可能包含noyes和unknown。6housing客户是否有住房贷款7loan类别格式的个人贷款具有可能的值noyes和unknown。8contact这是类别格式的通信类型具有可能的值cellular或telephone。9month这是类别格式的一年中最后一个通话月份具有可能的值janfebmar… nov和dec。10day_of_week这是类别格式的一周中的最后一个通话日具有可能的值montuewedthu和fri。11duration这是以秒为单位的最后一次通话持续时间数值。此属性高度影响输出目标例如如果duration 0则y no。然而在通话之前不知道持续时间。另外在通话结束后y显然是已知的。因此此输入仅应包括在基准目的中如果打算采用现实的预测模型则应将其丢弃。12campaign这是活动期间此客户的通话数量。13pdays这是上一个广告系列和客户的上次通话之后经过的天数数字 -999 表示之前未联系过客户。14previous这是之前此广告系列和此客户的通话数量数字。15poutcome上一次营销活动的结果类别failurenonexistent和success。16emp.var.rate这是就业变化率的季度指标数字。17cons.price.idx这是消费者价格指数的月度指标数字。18cons.conf.idx这是消费者信心指数的月度指标数字。19euribor3m这是 3 个月的 euribor 费率的每日指标数字。20nr.employed这是员工数的季度指标数字。21y表示客户是否拥有定期存款可能值是二元yes和no。 预处理 您可以看到数据集尚未准备好直接输入 MLP 或 DBN 分类器因为该特征与数值和分类值混合在一起。此外结果变量具有分类值。因此我们需要将分类值转换为数值以便特征和结果变量以数字形式。下一步显示了此过程。有关此预处理请参阅preprocessing_b.py文件。 首先我们必须加载预处理所需的所需包和库 import pandas as pd import numpy as np from sklearn import preprocessing然后从上述 URL 下载数据文件并将其放在方便的位置 - 比如说input 然后我们加载并解析数据集 data pd.read_csv(input/bank-additional-full.csv, sep ;)接下来我们将提取变量名称 var_names data.columns.tolist()现在基于表 1 中的数据集描述我们将提取分类变量 categs [job,marital,education,default,housing,loan,contact,month,day_of_week,duration,poutcome,y]然后我们将提取定量变量 # Quantitative vars quantit [i for i in var_names if i not in categs]然后让我们得到分类变量的虚拟变量 job pd.get_dummies(data[job]) marital pd.get_dummies(data[marital]) education pd.get_dummies(data[education]) default pd.get_dummies(data[default]) housing pd.get_dummies(data[housing]) loan pd.get_dummies(data[loan]) contact pd.get_dummies(data[contact]) month pd.get_dummies(data[month]) day pd.get_dummies(data[day_of_week]) duration pd.get_dummies(data[duration]) poutcome pd.get_dummies(data[poutcome])现在是时候映射变量来预测 dict_map dict() y_map {yes:1,no:0} dict_map[y] y_map data data.replace(dict_map) label data[y] df_numerical data[quantit] df_names df_numerical .keys().tolist()一旦我们将分类变量转换为数值变量下一个任务就是正则化数值变量。因此使用归一化我们将单个样本缩放为具有单元规范。如果您计划使用二次形式如点积或任何其他内核来量化任何样本对的相似性则此过程非常有用。该假设是在文本分类和聚类上下文中经常使用的向量空间模型的基础。 那么让我们来衡量量化变量 min_max_scaler preprocessing.MinMaxScaler() x_scaled min_max_scaler.fit_transform(df_numerical) df_temp pd.DataFrame(x_scaled) df_temp.columns df_names现在我们有原始数值变量的临时数据帧下一个任务是将所有数据帧组合在一起并生成正则化数据帧。我们将使用熊猫 normalized_df pd.concat([df_temp,job,marital,education,default,housing,loan,contact,month,day,poutcome,duration,label], axis1)最后我们需要将结果数据帧保存在 CSV 文件中如下所示 normalized_df.to_csv(bank_normalized.csv, index False)用于客户订阅评估的 TensorFlow 中的 MLP 实现 对于这个例子我们将使用我们在前面的例子中正则化的银行营销数据集。有几个步骤可以遵循。首先我们需要导入 TensorFlow以及其他必要的包和模块 import tensorflow as tf import pandas as pd import numpy as np import os from sklearn.cross_validation import train_test_split # for random split of train/test现在我们需要加载正则化的银行营销数据集其中所有特征和标签都是数字。为此我们使用 pandas 库中的read_csv()方法 FILE_PATH bank_normalized.csv # Path to .csv dataset raw_data pd.read_csv(FILE_PATH) # Open raw .csv print(Raw data loaded successfully...\n)以下是上述代码的输出 Raw data loaded successfully... 如前一节所述调整 DNN 的超参数并不简单。但是它通常取决于您正在处理的数据集。对于某些数据集可能的解决方法是根据与数据集相关的统计信息设置这些值例如训练实例的数量输入大小和类的数量。 DNN 不适用于小型和低维数据集。在这些情况下更好的选择是使用线性模型。首先让我们放置一个指向标签列本身的指针计算实例数和类数并定义训练/测试分流比如下所示 Y_LABEL y # Name of the variable to be predicted KEYS [i for i in raw_data.keys().tolist() if i ! Y_LABEL]# Name of predictors N_INSTANCES raw_data.shape[0] # Number of instances N_INPUT raw_data.shape[1] - 1 # Input size N_CLASSES raw_data[Y_LABEL].unique().shape[0] # Number of classes TEST_SIZE 0.25 # Test set size (% of dataset) TRAIN_SIZE int(N_INSTANCES * (1 - TEST_SIZE)) # Train size现在让我们看一下我们将用于训练 MLP 模型的数据集的统计数据 print(Variables loaded successfully...\n) print(Number of predictors \t%s %(N_INPUT)) print(Number of classes \t%s %(N_CLASSES)) print(Number of instances \t%s %(N_INSTANCES)) print(\n)以下是上述代码的输出 Variables loaded successfully... Number of predictors 1606 Number of classes 2 Number of instances 41188 下一个任务是定义其他参数例如学习率训练周期批量大小和权重的标准偏差。通常较低的训练率会帮助您的 DNN 学习更慢但需要集中精力。请注意我们需要定义更多参数例如隐藏层数和激活函数。 LEARNING_RATE 0.001 # learning rate TRAINING_EPOCHS 1000 # number of training epoch for the forward pass BATCH_SIZE 100 # batch size to be used during training DISPLAY_STEP 20 # print the error etc. at each 20 step HIDDEN_SIZE 256 # number of neurons in each hidden layer # We use tanh as the activation function, but you can try using ReLU as well ACTIVATION_FUNCTION_OUT tf.nn.tanh STDDEV 0.1 # Standard Deviations RANDOM_STATE 100前面的初始化是基于反复试验设置的 。因此根据您的用例和数据类型明智地设置它们但我们将在本章后面提供一些指导。此外对于前面的代码RANDOM_STATE用于表示训练的随机状态和测试分割。首先我们将原始特征和标签分开 data raw_data[KEYS].get_values() # X data labels raw_data[Y_LABEL].get_values() # y data现在我们有标签他们必须编码 labels_ np.zeros((N_INSTANCES, N_CLASSES)) labels_[np.arange(N_INSTANCES), labels] 1最后我们必须拆分训练和测试集。如前所述我们将保留 75% 的训练输入剩下的 25% 用于测试集 data_train, data_test, labels_train, labels_test train_test_split(data,labels_,test_size TEST_SIZE,random_state RANDOM_STATE) print(Data loaded and splitted successfully...\n)以下是上述代码的输出 Data loaded and splitted successfully 由于这是一个监督分类问题我们应该有特征和标签的占位符 如前所述MLP 由一个输入层几个隐藏层和一个称为输出层的最终 LTU 层组成。对于这个例子我将把训练与四个隐藏层结合起来。因此我们将分类器称为深度前馈 MLP。请注意我们还需要在每个层中使用权重输入层除外以及每个层中的偏差输出层除外。通常每个隐藏层包括偏置神经元并且作为从一个隐藏层到另一个隐藏层的完全连接的二分图前馈完全连接到下一层。那么让我们定义隐藏层的大小 n_input N_INPUT # input n labels n_hidden_1 HIDDEN_SIZE # 1st layer n_hidden_2 HIDDEN_SIZE # 2nd layer n_hidden_3 HIDDEN_SIZE # 3rd layer n_hidden_4 HIDDEN_SIZE # 4th layer n_classes N_CLASSES # output m classes 由于这是一个监督分类问题我们应该有特征和标签的占位符 # input shape is None * number of input X tf.placeholder(tf.float32, [None, n_input])占位符的第一个维度是None这意味着我们可以有任意数量的行。第二个维度固定在多个特征上这意味着每行需要具有该列数量的特征。 # label shape is None * number of classes y tf.placeholder(tf.float32, [None, n_classes])另外我们需要另一个占位符用于丢弃这是通过仅以某种可能性保持神经元活动例如p 1.0或者将其设置为零来实现来实现的。请注意这也是要调整的超参数和训练时间而不是测试时间 dropout_keep_prob tf.placeholder(tf.float32)使用此处给出的缩放可以将相同的网络用于训练使用dropout_keep_prob 1.0和评估使用dropout_keep_prob 1.0。现在我们可以定义一个实现 MLP 分类器的方法。为此我们将提供四个参数如输入权重偏置和丢弃概率如下所示 def DeepMLPClassifier(_X, _weights, _biases, dropout_keep_prob):layer1 tf.nn.dropout(tf.nn.tanh(tf.add(tf.matmul(_X, _weights[h1]), _biases[b1])), dropout_keep_prob)layer2 tf.nn.dropout(tf.nn.tanh(tf.add(tf.matmul(layer1, _weights[h2]), _biases[b2])), dropout_keep_prob)layer3 tf.nn.dropout(tf.nn.tanh(tf.add(tf.matmul(layer2, _weights[h3]), _biases[b3])), dropout_keep_prob)layer4 tf.nn.dropout(tf.nn.tanh(tf.add(tf.matmul(layer3, _weights[h4]), _biases[b4])), dropout_keep_prob)out ACTIVATION_FUNCTION_OUT(tf.add(tf.matmul(layer4, _weights[out]), _biases[out])) return out上述方法的返回值是激活函数的输出。前面的方法是一个存根实现它没有告诉任何关于权重和偏置的具体内容所以在我们开始训练之前我们应该定义它们 weights {w1: tf.Variable(tf.random_normal([n_input, n_hidden_1],stddevSTDDEV)),w2: tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2],stddevSTDDEV)),w3: tf.Variable(tf.random_normal([n_hidden_2, n_hidden_3],stddevSTDDEV)),w4: tf.Variable(tf.random_normal([n_hidden_3, n_hidden_4],stddevSTDDEV)),out: tf.Variable(tf.random_normal([n_hidden_4, n_classes],stddevSTDDEV)), } biases { b1: tf.Variable(tf.random_normal([n_hidden_1])), b2: tf.Variable(tf.random_normal([n_hidden_2])), b3: tf.Variable(tf.random_normal([n_hidden_3])), b4: tf.Variable(tf.random_normal([n_hidden_4])), out: tf.Variable(tf.random_normal([n_classes])) }现在我们可以使用真实参数输入层权重偏置和退出调用前面的 MLP 实现保持概率如下 pred DeepMLPClassifier(X, weights, biases, dropout_keep_prob)我们建立了 MLP 模型是时候训练网络了。首先我们需要定义成本操作然后我们将使用 Adam 优化器它将慢慢学习并尝试尽可能减少训练损失 cost tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logitspred, labelsy))# Optimization op (backprop) optimizer tf.train.AdamOptimizer(learning_rate LEARNING_RATE).minimize(cost_op)接下来我们需要定义用于计算分类准确率的其他参数 correct_prediction tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) accuracy tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(Deep MLP networks has been built successfully...) print(Starting training...)之后我们需要在启动 TensorFlow 会话之前初始化所有变量和占位符 init_op tf.global_variables_initializer() 现在我们非常接近开始训练但在此之前最后一步是创建 TensorFlow 会话并按如下方式启动它 sess tf.Session() sess.run(init_op)最后我们准备开始在训练集上训练我们的 MLP。我们遍历所有批次并使用批量数据拟合以计算平均训练成本。然而显示每个周期的训练成本和准确率会很棒 for epoch in range(TRAINING_EPOCHS):avg_cost 0.0total_batch int(data_train.shape[0] / BATCH_SIZE)# Loop over all batchesfor i in range(total_batch):randidx np.random.randint(int(TRAIN_SIZE), size BATCH_SIZE)batch_xs data_train[randidx, :]batch_ys labels_train[randidx, :]# Fit using batched datasess.run(optimizer, feed_dict{X: batch_xs, y: batch_ys, dropout_keep_prob: 0.9})# Calculate average costavg_cost sess.run(cost, feed_dict{X: batch_xs, y: batch_ys, dropout_keep_prob:1.})/total_batch# Display progressif epoch % DISPLAY_STEP 0:print(Epoch: %3d/%3d cost: %.9f % (epoch, TRAINING_EPOCHS, avg_cost))train_acc sess.run(accuracy, feed_dict{X: batch_xs, y: batch_ys, dropout_keep_prob:1.})print(Training accuracy: %.3f % (train_acc)) print(Your MLP model has been trained successfully.)以下是上述代码的输出 Starting training... Epoch: 0/1000 cost: 0.356494816 Training accuracy: 0.920 … Epoch: 180/1000 cost: 0.350044933 Training accuracy: 0.860 …. Epoch: 980/1000 cost: 0.358226758 Training accuracy: 0.910 干得好我们的 MLP 模型已经成功训练现在如果我们以图形方式看到成本和准确率怎么办我们来试试吧 # Plot loss over time plt.subplot(221) plt.plot(i_data, cost_list, k--, labelTraining loss, linewidth1.0) plt.title(Cross entropy loss per iteration) plt.xlabel(Iteration) plt.ylabel(Cross entropy loss) plt.legend(locupper right)plt.grid(True)以下是上述代码的输出 图 11训练阶段每次迭代的交叉熵损失 上图显示交叉熵损失在 0.34 和 0.36 之间或多或少稳定但波动很小。现在让我们看看这对整体训练准确率有何影响 # Plot train and test accuracy plt.subplot(222) plt.plot(i_data, acc_list, r--, labelAccuracy on the training set, linewidth1.0) plt.title(Accuracy on the training set) plt.xlabel(Iteration) plt.ylabel(Accuracy) plt.legend(locupper right) plt.grid(True) plt.show()以下是前面代码的输出 图 12每次迭代时训练集的准确率 我们可以看到训练准确率在 79% 和 96% 之间波动但不会均匀地增加或减少。解决此问题的一种可能方法是添加更多隐藏层并使用不同的优化器例如本章前面讨论的梯度下降。我们将丢弃概率提高到 100%即 1.0。原因是也有相同的网络用于测试 print(Evaluating MLP on the test set...) test_acc sess.run(accuracy, feed_dict{X: data_test, y: labels_test, dropout_keep_prob:1.}) print (Prediction/classification accuracy: %.3f % (test_acc))以下是上述代码的输出 Evaluating MLP on the test set... Prediction/classification accuracy: 0.889 Session closed! 因此分类准确率约为 89%。一点也不差现在如果需要更高的精度我们可以使用称为深度信任网络DBN的另一种 DNN 架构可以以有监督或无监督的方式进行训练。 这是在其应用中作为分类器观察 DBN 的最简单方法。如果我们有一个 DBN 分类器那么预训练方法是以类似于自编码器的无监督方式完成的这将在第 5 章中描述优化 TensorFlow 自编码器分类器以受监督的方式训练微调就像 MLP 中的那样。 深度信念网络DBNs 为了克服 MLP 中的过拟合问题我们建立了一个 DBN进行无监督的预训练为输入获得一组不错的特征表示然后对训练集进行微调以获得预测。网络。 虽然 MLP 的权重是随机初始化的但 DBN 使用贪婪的逐层预训练算法通过概率生成模型初始化网络权重。这些模型由可见层和多层随机潜在变量组成这些变量称为隐藏单元或特征检测器。 堆叠 DBN 中的 RBM形成无向概率图模型类似于马尔可夫随机场MRF两层由可见神经元和隐藏神经元组成。 堆叠 RBM 中的顶部两层在它们之间具有无向的对称连接并形成关联存储器而较低层从上面的层接收自上而下的定向连接 图 13RBM 作为构建块的 DBN 的高级视图 顶部两层在它们之间具有无向的对称连接并形成关联存储器而较低层从前面的层接收自上而下的定向连接。几个 RBM 一个接一个地堆叠以形成 DBN。 受限玻尔兹曼机RBMs RBM 是无向概率图模型称为马尔科夫随机场。它由两层组成。第一层由可见神经元组成第二层由隐藏神经元组成。图 14 显示了简单 RBM 的结构。可见单元接受输入隐藏单元是非线性特征检测器。每个可见神经元都连接到所有隐藏的神经元但同一层中的神经元之间没有内部连接 图 14简单 RBM 的结构 图 14 中的 RBM 由m个可见单元组成V (v[1] ... v[m])和n个隐藏单元H (h[1] ... h[n])。可见单元接受 0 到 1 之间的值隐藏单元的生成值介于 0 和 1 之间。模型的联合概率是由以下等式给出的能量函数 在前面的等式中i 1 ... mj 1 ... nbi和cj分别是可见和隐藏单元的偏差并且w[ij]是v[i]和h[j]之间的权重。模型分配给可见向量v的概率由下式给出 在第二个等式中Z是分区函数定义如下 权重的学习可以通过以下等式获得 在等式 4 中学习率由eps定义。通常较小的eps值可确保训练更加密集。但是如果您希望网络快速学习可以将此值设置得更高。 由于同一层中的单元之间没有连接因此很容易计算第一项。p(h | v)和p(v | h)的条件分布是阶乘的并且由以下等式中的逻辑函数给出 因此样本v[i] h[j]是无偏的。然而计算第二项的对数似然的计算成本是指数级的。虽然有可能得到第二项的无偏样本但使用马尔可夫链蒙特卡罗MCMC的吉布斯采样这个过程也不具有成本效益。相反RBM 使用称为对比发散的有效近似方法。 通常MCMC 需要许多采样步骤才能达到静止的收敛。运行吉布斯采样几步通常是一步足以训练一个模型这称为对比分歧学习。对比分歧的第一步是用训练向量初始化可见单元。 下一步是使用等式 5 计算所有隐藏单元同时使用可见单元然后使用等式 4 从隐藏单元重建可见单元。最后隐藏单元用重建的可见单元更新。因此代替方程式 4我们最终得到以下的权重学习模型 简而言之该过程试图减少输入数据和重建数据之间的重建误差。算法收敛需要多次参数更新迭代。迭代称为周期。输入数据被分成小批量并且在每个小批量之后更新参数具有参数的平均值。 最后如前所述RBM 最大化可见单元p(v)的概率其由模式和整体训练数据定义。它相当于最小化模型分布和经验数据分布之间的 KL 散度。 对比分歧只是这个目标函数的粗略近似但它在实践中非常有效。虽然方便但重建误差实际上是衡量学习进度的一个非常差的指标。考虑到这些方面RBM 需要一些时间来收敛但是如果你看到重建是不错的那么你的算法效果很好。 构建一个简单的 DBN 单个隐藏层 RBM 无法从输入数据中提取所有特征因为它无法对变量之间的关系进行建模。因此一层接一个地使用多层 RBM 来提取非线性特征。在 DBN 中首先使用输入数据训练 RBM并且隐藏层以贪婪学习方法表示学习的特征。 第一 RBM 的这些学习特征用作第二 RBM 的输入作为 DBN 中的另一层如图 15 所示。类似地第二层的学习特征用作另一层的输入。 这样DBN 可以从输入数据中提取深度和非线性特征。最后一个 RBM 的隐藏层代表整个网络的学习特征。前面针对所有 RBM 层描述的学习特征的过程称为预训练。 无监督的预训练 假设您要处理复杂任务您没有多少标记的训练数据。很难找到合适的 DNN 实现或架构来进行训练并用于预测分析。然而如果您有大量未标记的训练数据您可以尝试逐层训练层从最低层开始然后使用无监督的特征检测器算法向上移动。这就是 RBM图 15或自编码器图 16的工作原理。 图 15使用自编码器在 DBN 中进行无监督的预训练 当您有一个复杂的任务需要解决时无监督的预训练仍然是一个不错的选择没有类似的模型可以重复使用并且标记很少的训练数据但是大量未标记的训练数据。目前的趋势是使用自编码器而不是 RBM但是对于下一节中的示例RBM 将用于简化。读者也可以尝试使用自编码器而不是 RBM。 预训练是一种无监督的学习过程。在预训练之后通过在最后一个 RBM 层的顶部添加标记层来执行网络的微调。此步骤是受监督的学习过程。无监督的预训练步骤尝试查找网络权重 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mfg130wX-1681565849700)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_03_16.jpg)] 图 16通过构建具有 RBM 栈的简单 DBN 在 DBN 中进行无监督预训练 监督的微调 在监督学习阶段也称为监督微调中不是随机初始化网络权重而是使用在预训练步骤中计算的权重来初始化它们。这样当使用监督梯度下降时DBN 可以避免收敛到局部最小值。 如前所述使用 RBM 栈DBN 可以构造如下 使用参数W[1]训练底部 RBM第一个 RBM将第二层权重初始化为W[2] W[1]^T这可确保 DBN 至少与我们的基础 RBM 一样好 因此将这些步骤放在一起图 17 显示了由三个 RBM 组成的简单 DBN 的构造 图 17使用多个 RBM 构建简单的 DBN 现在当调整 DBN 以获得更好的预测准确率时我们应该调整几个超参数以便 DBN 通过解开和改进W[2]来拟合训练数据。综上所述我们有了创建基于 DBN 的分类器或回归器的概念工作流程。 现在我们已经有足够的理论背景来介绍如何使用几个 RBM 构建 DBN现在是时候将我们的理论应用于实践中了。在下一节中我们将了解如何开发用于预测分析的监督 DBN 分类器。 用于客户订阅评估的 TensorFlow 中的 DBN 实现 在银行营销数据集的前一个示例中我们使用 MLP 观察到大约 89% 的分类准确率。我们还将原始数据集标准化然后将其提供给 MLP。在本节中我们将了解如何为基于 DBN 的预测模型使用相同的数据集。 我们将使用 Md.Rezaul Karim 最近出版的书籍 Predictive Analytics with TensorFlow 的 DBN 实现可以从 GitHub 下载。 前面提到的实现是基于 RBM 的简单干净快速的 DBN 实现并且基于 NumPy 和 TensorFlow 库以便利用 GPU 计算。该库基于以下两篇研究论文实现 Geoffrey E. HintonSimon Osindero 和 Yee-Whye Teh 的深度信念网快速学习算法。 Neural Computation 18.720061527-1554。训练受限制的玻尔兹曼机简介Asja Fischer 和 Christian Igel。模式识别 47.1201425-39。 我们将看到如何以无监督的方式训练 RBM然后我们将以有监督的方式训练网络。简而言之有几个步骤需要遵循。主分类器是classification_demo.py。 提示 虽然在以监督和无监督的方式训练 DBN 时数据集不是那么大或高维度但是在训练时间中会有如此多的计算这需要巨大的资源。然而RBM 需要大量时间来收敛。因此我建议读者在 GPU 上进行训练至少拥有 32 GB 的 RAM 和一个 Corei7 处理器。 我们将从加载所需的模块和库开始 import numpy as np import pandas as pd from sklearn.datasets import load_digits from sklearn.model_selection import train_test_split from sklearn.metrics.classification import accuracy_score from sklearn.metrics import precision_recall_fscore_support from sklearn.metrics import confusion_matrix import itertools from tf_models import SupervisedDBNClassification import matplotlib.pyplot as plt然后我们加载前一个 MLP 示例中使​​用的已经正则化的数据集 FILE_PATH ../input/bank_normalized.csv raw_data pd.read_csv(FILE_PATH)在前面的代码中我们使用了 pandas read_csv()方法并创建了一个DataFrame。现在下一个任务是按如下方式扩展特征和标签 Y_LABEL y KEYS [i for i in raw_data.keys().tolist() if i ! Y_LABEL] X raw_data[KEYS].get_values() Y raw_data[Y_LABEL].get_values() class_names list(raw_data.columns.values) print(class_names)在前面的行中我们已经分离了特征和标签。这些特征存储在X中标签位于Y中。接下来的任务是将它们分成训练75%和测试集25%如下所示 X_train, X_test, Y_train, Y_test train_test_split(X, Y, test_size0.25, random_state100)现在我们已经有了训练和测试集我们可以直接进入 DBN 训练步骤。但是首先我们需要实例化 DBN。我们将以受监督的方式进行分类但我们需要为此 DNN 架构提供超参数 classifier SupervisedDBNClassification(hidden_layers_structure[64, 64],learning_rate_rbm0.05,learning_rate0.01,n_epochs_rbm10,n_iter_backprop100,batch_size32,activation_functionrelu,dropout_p0.2)在前面的代码段中n_epochs_rbm是预训练无监督和n_iter_backprop用于监督微调的周期数。尽管如此我们已经为这两个阶段定义了两个单独的学习率分别使用learning_rate_rbm和learning_rate。 不过我们将在本节后面描述SupervisedDBNClassification的这个类实现。 该库具有支持 sigmoidReLU 和 tanh 激活函数的实现。此外它利用 l2 正则化来避免过拟合。我们将按如下方式进行实际拟合 classifier.fit(X_train, Y_train)如果一切顺利你应该在控制台上观察到以下进展 [START] Pre-training step:Epoch 1 finished RBM Reconstruction error 1.681226 ….Epoch 3 finished RBM Reconstruction error 4.926415Epoch 5 finished RBM Reconstruction error 7.185334 …Epoch 7 finished RBM Reconstruction error 37.734962Epoch 8 finished RBM Reconstruction error 467.182892 ….Epoch 10 finished RBM Reconstruction error 938.583801 [END] Pre-training step [START] Fine tuning step:Epoch 0 finished ANN training loss 0.316619Epoch 1 finished ANN training loss 0.311203Epoch 2 finished ANN training loss 0.308707 ….Epoch 98 finished ANN training loss 0.288299Epoch 99 finished ANN training loss 0.288900 由于 RBM 的权重是随机初始化的因此重建和原始输入之间的差异通常很大。 从技术上讲我们可以将重建误差视为重建值与输入值之间的差异。然后在迭代学习过程中将该误差反向传播 RBM 的权重几次直到达到最小误差。 然而在我们的情况下重建达到 938这不是那么大即不是无限所以我们仍然可以期望良好的准确率。无论如何经过 100 次迭代后显示每个周期训练光泽的微调图如下 图 18每次迭代的 SGD 微调损失仅 100 次迭代 然而当我重复前面的训练并微调 1000 个周期时我没有看到训练损失有任何显着改善 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zHpigbKd-1681565849700)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_03_19.jpg)] 图 19每次迭代的 SGD 微调损失1000 次迭代 这是监督的 DBN 分类器的实现。此类为分类问题实现 DBN。它将网络输出转换为原始标签。在对标签映射执行索引之后它还需要网络参数并返回列表。 然后该类预测给定数据中每个样本的类的概率分布并返回字典列表每个样本一个。最后它附加了 softmax 线性分类器作为输出层 class SupervisedDBNClassification(TensorFlowAbstractSupervisedDBN, ClassifierMixin):def _build_model(self, weightsNone):super(SupervisedDBNClassification, self)._build_model(weights)self.output tf.nn.softmax(self.y)self.cost_function tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logitsself.y, labelsself.y_))self.train_step self.optimizer.minimize(self.cost_function)classmethoddef _get_param_names(cls):return super(SupervisedDBNClassification, cls)._get_param_names() [label_to_idx_map, idx_to_label_map]classmethoddef from_dict(cls, dct_to_load):label_to_idx_map dct_to_load.pop(label_to_idx_map)idx_to_label_map dct_to_load.pop(idx_to_label_map)instance super(SupervisedDBNClassification, cls).from_dict(dct_to_load)setattr(instance, label_to_idx_map, label_to_idx_map)setattr(instance, idx_to_label_map, idx_to_label_map)return instancedef _transform_labels_to_network_format(self, labels):Converts network output to original labels.:param indexes: array-like, shape (n_samples, ):return:new_labels, label_to_idx_map, idx_to_label_map to_categorical(labels, self.num_classes)self.label_to_idx_map label_to_idx_mapself.idx_to_label_map idx_to_label_mapreturn new_labelsdef _transform_network_format_to_labels(self, indexes):return list(map(lambda idx: self.idx_to_label_map[idx], indexes))def predict(self, X):probs self.predict_proba(X)indexes np.argmax(probs, axis1)return self._transform_network_format_to_labels(indexes)def predict_proba(self, X):Predicts probability distribution of classes for each sample in the given data.:param X: array-like, shape (n_samples, n_features):return:return super(SupervisedDBNClassification, self)._compute_output_units_matrix(X)def predict_proba_dict(self, X):Predicts probability distribution of classes for each sample in the given data.Returns a list of dictionaries, one per sample. Each dict contains {label_1: prob_1, ..., label_j: prob_j}:param X: array-like, shape (n_samples, n_features):return:if len(X.shape) 1: # It is a single sampleX np.expand_dims(X, 0)predicted_probs self.predict_proba(X)result []num_of_data, num_of_labels predicted_probs.shapefor i in range(num_of_data):# key : label# value : predicted probabilitydict_prob {}for j in range(num_of_labels):dict_prob[self.idx_to_label_map[j]] predicted_probs[i][j]result.append(dict_prob)return resultdef _determine_num_output_neurons(self, labels):return len(np.unique(labels))正如我们在前面的例子和运行部分中提到的那样微调神经网络的参数是一个棘手的过程。有很多不同的方法但我的知识并没有一刀切的方法。然而通过前面的组合我收到了更好的分类结果。另一个要选择的重要参数是学习率。根据您的模型调整学习率是一种可以采取的方法以减少训练时间同时避免局部最小化。在这里我想讨论一些能够帮助我提高预测准确率的技巧不仅适用于此应用也适用于其他应用。 现在我们已经建立了模型现在是评估其表现的时候了。为了评估分类准确率我们将使用几个表现指标如precisionrecall和f1 score。此外我们将绘制混淆矩阵以观察与真实标签相对应的预测标签。首先让我们计算预测精度如下 Y_pred classifier.predict(X_test) print(Accuracy: %f % accuracy_score(Y_test, Y_pred))接下来我们需要计算分类的precisionrecall和f1 score p, r, f, s precision_recall_fscore_support(Y_test, Y_pred, averageweighted) print(Precision:, p) print(Recall:, r) print(F1-score:, f)以下是上述代码的输出 Accuracy: 0.900554 Precision: 0.8824140209830381 Recall: 0.9005535592891133 F1-score: 0.8767190584424599 太棒了使用我们的 DBN 实现我们解决了与使用 MLP 相同的分类问题。尽管如此与 MLP 相比我们设法获得了稍微更好的准确率。 现在如果要解决回归问题要预测的标签是连续的则必须使用SupervisedDBNRegression()函数进行此实现。 DBN 文件夹中的回归脚本即regression_demo.py也可用于执行回归操作。 但是使用专门为回归 y 准备的另一个数据集将是更好的主意。您需要做的就是准备数据集以便基于 TensorFlow 的 DBN 可以使用它。因此为了最小化演示我使用房价高级回归技术数据集来预测房价。 调整超参数和高级 FFNN 神经网络的灵活性也是它们的主要缺点之一有很多超参数可以调整。即使在简单的 MLP 中您也可以更改层数每层神经元的数量以及每层中使用的激活函数的类型。您还可以更改权重初始化逻辑退出保持概率等。 另外FFNN 中的一些常见问题例如梯度消失问题以及选择最合适的激活函数学习率和优化器是最重要的。 调整 FFNN 超参数 超参数是不在估计器中直接学习的参数。有可能并建议您在超参数空间中搜索最佳交叉验证得分。在构造估计器时提供的任何参数可以以这种方式优化。现在问题是您如何知道超参数的哪种组合最适合您的任务当然您可以使用网格搜索和交叉验证来为线性机器学习模型找到正确的超参数。 但是对于 DNN有许多超参数可供调整。由于在大型数据集上训练神经网络需要花费大量时间因此您只能在合理的时间内探索超参数空间的一小部分。以下是一些可以遵循的见解。 此外当然正如我所说您可以使用网格搜索或随机搜索通过交叉验证为线性机器学习模型找到正确的超参数。我们将在本节后面看到一些可能的详尽和随机网格搜索和交叉验证方法。 隐藏层数 对于许多问题你可以从一个或两个隐藏层开始这个设置可以很好地使用两个隐藏层具有相同的神经元总数见下文以了解一些神经元训练时间大致相同。现在让我们看一些关于设置隐藏层数的朴素估计 0仅能表示线性可分离函数或决策1可以近似包含从一个有限空间到另一个有限空间的连续映射的任何函数2可以用任意精度表示任意决策边界具有合理的激活函数并且可以近似任何平滑映射到任何精度 但是对于更复杂的问题您可以逐渐增加隐藏层的数量直到您开始过拟合训练集。非常复杂的任务例如大图像分类或语音识别通常需要具有数十层的网络并且它们需要大量的训练数据。 不过您可以尝试逐渐增加神经元的数量直到网络开始过拟合。这意味着不会导致过拟合的隐藏神经元数量的上限是 在上面的等式中 N[i]为输入神经元的数量 N[o]为输出神经元的数量 N[s]为训练数据集中的样本数 α为任意比例因子通常为 2-10。 请注意上述等式不是来自任何研究而是来自我的个人工作经验。但是对于自动程序您将以 2 的α值开始即训练数据的自由度是模型的两倍如果训练数据的误差明显小于 10则可以达到 10。用于交叉验证数据集。 每个隐藏层的神经元数量 显然输入和输出层中神经元的数量取决于您的任务所需的输入和输出类型。例如如果您的数据集的形状为28x28则它应该具有大小为 784 的输入神经元并且输出神经元应该等于要预测的类的数量。 我们将在下一个例子中看到它如何在实践中工作使用 MLP其中将有四个具有 256 个神经元的隐藏层只有一个超参数可以调整而不是每层一个。就像层数一样您可以尝试逐渐增加神经元的数量直到网络开始过拟合。 有一些经验导出的经验法则其中最常用的是“隐藏层的最佳大小通常在输入的大小和输出层的大小之间。” 总之对于大多数问题通过仅使用两个规则设置隐藏层配置您可能可以获得不错的表现即使没有第二个优化步骤 隐藏层的数量等于一该层中的神经元数量是输入和输出层中神经元的平均值 然而就像层数一样你可以尝试逐渐增加神经元的数量直到网络开始过拟合。 权重和偏置初始化 正如我们将在下一个示例中看到的那样初始化权重和偏置隐藏层是一个重要的超参数需要注意 不要做所有零初始化一个听起来合理的想法可能是将所有初始权重设置为零但它在实践中不起作用。这是因为如果网络中的每个神经元计算相同的输出如果它们的权重被初始化为相同则神经元之间将不存在不对称的来源。小随机数也可以将神经元的权重初始化为小数但不能相同为零。或者可以使用从均匀分布中抽取的小数字。初始化偏差通常将偏差初始化为零因为权重中的小随机数提供不对称性破坏。将偏差设置为一个小的常量值例如所有偏差的 0.01确保所有 ReLU 单元都可以传播一些梯度。但是它既没有表现良好也没有表现出持续改进因此建议坚持使用零。 选择最合适的优化器 因为在 FFNN 中 目标函数之一是最小化评估成本我们必须定义一个优化器。我们已经看到了如何使用tf.train.AdamOptimizer。Tensorflow tf.train提供了一组有助于训练模型的类和函数。就个人而言我发现 Adam 优化器在实践中对我很有效而不必考虑学习率等等。 对于大多数情况我们可以利用 Adam但有时我们可以采用实现的RMSPropOptimizer函数这是梯度下降的高级形式。RMSPropOptimizer函数实现RMSProp算法。 RMSPropOptimizer函数还将学习率除以指数衰减的平方梯度平均值。衰减参数的建议设置值为0.9而学习率的良好默认值为0.001 optimizer tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost_op)使用最常见的优化器 SGD学习率必须随1/T缩放才能获得收敛其中T是迭代次数。RMSProp尝试通过调整步长来自动克服此限制以使步长与梯度的比例相同。 因此如果您正在训练神经网络但计算梯度是必需的使用tf.train.RMSPropOptimizer()将是在小批量设置中学习的更快方式。研究人员还建议在训练 CNN 等深层网络时使用动量优化器。 最后如果您想通过设置这些优化器来玩游戏您只需要更改一行。由于时间限制我没有尝试过所有这些。然而根据 Sebastian Ruder 最近的一篇研究论文见此链接自适应学习率方法的优化者AdagradAdadeltaRMSprop和Adam是最合适的并为这些情况提供最佳收敛。 网格搜索和随机搜索的超参数调整 采样搜索的两种通用候选方法在其他基于 Python 的机器学习库如 Scikit-learn中提供。对于给定值GridSearchCV详尽考虑所有参数组合而RandomizedSearchCV可以从具有指定分布的参数空间中对给定数量的候选进行采样。 GridSearchCV是自动测试和优化超参数的好方法。我经常在 Scikit-learn 中使用它。然而TensorFlowEstimator优化learning_ratebatch_size等等还不是那么简单。而且正如我所说我们经常调整这么多超参数以获得最佳结果。不过我发现这篇文章对于学习如何调整上述超参数非常有用。 随机搜索和网格搜索探索完全相同的参数空间。参数设置的结果非常相似而随机搜索的运行时间则大大降低。 一些基准测试例如此链接已经报告了随机搜索的表现略差尽管这很可能是一种噪音效应并且不会延续到坚持不懈的测试集。 正则化 有几种方法可以控制 DNN 的训练以防止在训练阶段过拟合例如L2/L1 正则化最大范数约束和退出 L2 正则化这可能是最常见的正则化形式。使用梯度下降参数更新L2 正则化表示每个权重将线性地向零衰减。L1 正则化对于每个权重 w我们将项λ|w|添加到目标。然而 也可以组合 L1 和 L2 正则化以实现弹性网络正则化。最大范数约束这强制了每个隐藏层神经元的权重向量的大小的绝对上限。可以进一步使用投影的梯度下降来强制约束。 消失梯度问题出现在非常深的神经网络通常是 RNN它将有一个专门的章节它使用激活函数其梯度往往很小在 0 到 1 的范围内。 由于这些小梯度在反向传播期间进一步增加因此它们倾向于在整个层中“消失”从而阻止网络学习远程依赖性。解决这个问题的常用方法是使用激活函数如线性单元又名 ReLU它不会受到小梯度的影响。我们将看到一种改进的 RNN 变体称为长短期记忆 又名 LSTM它可以解决这个问题。我们将在第 5 章优化 TensorFlow 自编码器中看到关于该主题的更详细讨论。 尽管如此我们已经看到最后的架构更改提高了模型的准确率但我们可以通过使用 ReLU 更改 sigmoid 激活函数来做得更好如下所示 图 20ReLU 函数 ReLU 单元计算函数f(x) max(0, x)。 ReLU 计算速度快因为它不需要任何指数计算例如 sigmoid 或 tanh 激活所需的计算。此外与 sigmoid/tanh 函数相比发现它大大加速了随机梯度下降的收敛。要使用 ReLU 函数我们只需在先前实现的模型中更改前四个层的以下定义 第一层输出 Y1 tf.nn.relu(tf.matmul(XX, W1) B1) # Output from layer 1第二层输出 Y2 tf.nn.relu(tf.matmul(Y1, W2) B2) # Output from layer 2第三层输出 Y3 tf.nn.relu(tf.matmul(Y2, W3) B3) # Output from layer 3第四层输出 Y4 tf.nn.relu(tf.matmul(Y3, W4) B4) # Output from layer 4输出层 Ylogits tf.matmul(Y4, W5) B5 # computing the logits Y tf.nn.softmax(Ylogits) # output from layer 5当然tf.nn.relu是 TensorFlow 的 ReLU 实现。模型的准确率几乎达到 98%您可以看到运行网络 Loading data/train-images-idx3-ubyte.mnist Loading data/train-labels-idx1-ubyte.mnist Loading data/t10k-images-idx3-ubyte.mnist Loading data/t10k-labels-idx1-ubyte.mnist Epoch: 0 Epoch: 1 Epoch: 2 Epoch: 3 Epoch: 4 Epoch: 5 Epoch: 6 Epoch: 7 Epoch: 8 Epoch: 9 Accuracy:0.9789 done关注 TensorBoard 分析从源文件执行的文件夹中您应该数字 $ Tensorboard --logdir log_relu # Dont put space before or after 然后在localhost上打开浏览器以显示 TensorBoard 的起始页面。在下图中我们显示了趋势对训练集示例数量的准确率 图 21训练集上的准确率函数 在大约 1000 个示例之后您可以很容易地看到在不良的初始趋势之后准确率如何开始快速渐进式改进。 丢弃优化 在使用 DNN 时我们需要另一个占位符用于丢弃这是一个需要调整的超参数。它仅通过以某种概率保持神经元活动比如p 1.0或者将其设置为零来实现。这个想法是在测试时使用单个神经网络而不会丢弃。该网络的权重是训练权重的缩小版本。如果在训练期间使用dropout_keep_prob 1.0保留单元则在测试时将该单元的输出权重乘以p。 在学习阶段与下一层的连接可以限于神经元的子集以减少要更新的权重。这种学习优化技术称为丢弃。因此丢弃是一种用于减少具有许多层和/或神经元的网络中的过拟合的技术。通常丢弃层位于具有大量可训练神经元的层之后。 该技术允许将前一层的一定百分比的神经元设置为 0然后排除激活。神经元激活被设置为 0 的概率由层内的丢弃率参数通过 0 和 1 之间的数字表示。实际上神经元的激活保持等于丢弃率的概率否则它被丢弃即设置为 0。 图 22丢弃表示 通过这种方式对于每个输入网络拥有与前一个略有不同的架构。即使这些架构具有相同的权重一些连接也是有效的有些连接不是每次都以不同的方式。上图显示了丢弃的工作原理每个隐藏单元都是从网络中随机省略的概率为p。 但需要注意的是每个训练实例的选定丢弃单元不同这就是为什么这更像是一个训练问题。丢弃可以被视为在大量不同的神经网络中执行模型平均的有效方式其中可以以比架构问题低得多的计算成本来避免过拟合。丢弃降低了神经元依赖于其他神经元存在的可能性。通过这种方式它被迫更多地了解强大的特征并且它们与其他不同神经元的联系非常有用。 允许构建丢弃层的 TensorFlow 函数是tf.nn.dropout。此函数的输入是前一层的输出并且丢弃参数tf.nn.dropout返回与输入张量相同大小的输出张量。该模型的实现遵循与五层网络相同的规则。在这种情况下我们必须在一层和另一层之间插入丢弃函数 pkeep tf.placeholder(tf.float32)Y1 tf.nn.relu(tf.matmul(XX, W1) B1) # Output from layer 1 Y1d tf.nn.dropout(Y1, pkeep)Y2 tf.nn.relu(tf.matmul(Y1, W2) B2) # Output from layer 2 Y2d tf.nn.dropout(Y2, pkeep)Y3 tf.nn.relu(tf.matmul(Y2, W3) B3) # Output from layer 3 Y3d tf.nn.dropout(Y3, pkeep)Y4 tf.nn.relu(tf.matmul(Y3, W4) B4) # Output from layer 4 Y4d tf.nn.dropout(Y4, pkeep)Ylogits tf.matmul(Y4d, W5) B5 # computing the logits Y tf.nn.softmax(Ylogits) # output from layer 5退出优化产生以下结果 Loading data/train-images-idx3-ubyte.mnist Loading data/train-labels-idx1-ubyte.mnist Loading data/t10k-images-idx3-ubyte.mnist Loading data/t10k-labels-idx1-ubyte.mnist Epoch: 0 Epoch: 1 Epoch: 2 Epoch: 3 Epoch: 4 Epoch: 5 Epoch: 6 Epoch:7 Epoch: 8 Epoch: 9 Accuracy: 0.9666 done尽管有这种实现 之前的 ReLU 网络仍然更好但您可以尝试更改网络参数以提高模型的准确率。此外由于这是一个很小的网络我们处理的是小规模的数据集当您处理具有更复杂网络的大规模高维数据集时您会发现丢弃可能非常重要。我们将在下一章中看到一些动手实例。 现在要了解丢弃优化的效果让我们开始 TensorBoard 分析。只需键入以下内容 $ Tensorboard --logdir log_softmax_relu_dropout/下图显示了作为训练示例函数的精度成本函数 图 23a丢弃优化的准确率b训练集的成本函数 在上图中我们显示成本函数作为训练样例的函数。这两种趋势都是我们所期望的随着训练样例的增加准确率会提高而成本函数会随着迭代次数的增加而减 总结 我们已经了解了如何实现 FFNN 架构其特征在于一组输入单元一组输出单元以及一个或多个连接该输出的输入级别的隐藏单元。我们已经看到如何组织网络层以便级别之间的连接是完全的并且在单个方向上每个单元从前一层的所有单元接收信号并发送其输出值适当地权衡到所有单元的下一层。 我们还看到了如何为每个层定义激活函数例如sigmoidReLUtanh 和 softmax其中激活函数的选择取决于架构和要解决的问题。 然后我们实现了四种不同的 FFNN 模型。第一个模型有一个隐藏层具有 softmax 激活函数。其他三个更复杂的模型总共有五个隐藏层但具有不同的激活函数。我们还看到了如何使用 TensorFlow 实现深度 MLP 和 DBN以解决分类任务。使用这些实现我们设法达到了 90% 以上的准确率。最后我们讨论了如何调整 DNN 的超参数以获得更好和更优化的表现。 虽然常规的 FFNN例如 MLP适用于小图像例如MNIST 或 CIFAR-10但由于需要大量参数它会因较大的图像而分解。例如100×100 图像具有 10,000 个像素并且如果第一层仅具有 1,000 个神经元其已经严格限制传输到下一层的信息量则这意味着 1000 万个连接。另外这仅适用于第一层。 重要的是DNN 不知道像素的组织方式因此不知道附近的像素是否接近。 CNN 的架构嵌入了这种先验知识。较低层通常识别图像的单元域中的特征而较高层将较低层特征组合成较大特征。这适用于大多数自然图像与 DNN 相比CNN 具有决定性的先机性。 在下一章中我们将进一步探讨神经网络模型的复杂性引入 CNN这可能对深度学习技术产生重大影响。我们将研究主要功能并查看一些实现示例。 四、CNN 实战 以前面提到的5×5输入矩阵为例CNN 的输入层由 25 个神经元5×5组成其任务是获取与每个像素对应的输入值并将其转移到下一层。 在多层网络中输入层中所有神经元的输出将连接到隐藏层完全连接层中的每个神经元。然而在 CNN 网络中上面定义的连接方案和我们要描述的卷积层是显着不同的。正如您可能猜到的这是层的主要类型在 CNN 中使用这些层中的一个或多个是必不可少的。 在卷积层中每个神经元连接到输入区域的某个区域称为感受野。例如使用3×3内核滤波器每个神经元将具有偏置并且 9 个权重3×3连接到单个感受野。为了有效地识别图像我们需要将各种不同的内核过滤器应用于相同的感受野因为每个过滤器应该识别来自图像的不同特征。识别相同特征的神经元集定义了单个特征映射。 下图显示了运行中的 CNN 架构28×28输入图像将由一个由28x28x32特征映射组成的卷积层进行分析。该图还显示了一个感受野和一个3×3内核过滤器 图 5CNN 正在运行中 CNN 可以由级联连接的若干卷积层组成。每个卷积层的输出是一组特征映射每个都由单个内核过滤器生成。这些矩阵中的每一个都定义了将由下一层使用的新输入。 通常在 CNN 中每个神经元产生高达激活阈值的输出该激活阈值与输入成比例并且不受限制。 CNN 还使用位于卷积层之后的池化层。池化层将卷积区域划分为子区域。然后池化层选择单个代表值最大池化或平均池化以减少后续层的计算时间并增加特征相对于其空间位置的稳健性。卷积网络的最后一层通常是完全连接的网络具有用于输出层的 softmax 激活函数。在接下来的几节中将详细分析最重要的 CNN 的架构。 LeNet5 LeNet5 CNN 架构由 Yann LeCun 于 1998 年发明是第一个 CNN。它是一个多层前馈网络专门用于对手写数字进行分类。它被用于 LeCun 的实验由七层组成包含可训练的权重。 LeNet5 架构如下所示 图 6LeNet5 网络 LeNet5 架构由三个卷积层和两个交替序列池化层组成。最后两层对应于传统的完全连接的神经网络即完全连接的层后面是输出层。输出层的主要功能是计算输入向量和参数向量之间的欧几里德距离。输出函数识别输入模式和我们模型的测量值之间的差异。输出保持最小以实现最佳模型。因此完全连接的层被配置为使得输入模式和我们的模型的测量之间的差异最小化。虽然它在 MNIST 数据集上表现良好但是在具有更高分辨率和更多类别的更多图像的数据集上表现下降。 注意 有关 LeNet 系列模型的基本参考请参见此链接。 逐步实现 LeNet-5 在本节中我们将学习如何构建 LeNet-5 架构来对 MNIST 数据集中的图像进行分类。下图显示了数据如何在前两个卷积层中流动使用滤波器权重在第一个卷积层中处理输入图像。这导致 32 个新图像一个用于卷积层中的每个滤波器。图像也通过合并操作进行下采样因此图像分辨率从28×28降低到14×14。然后在第二卷积层中处理这 32 个较小的图像。我们需要为这 32 个图像中的每一个再次使用滤波器权重并且我们需要该层的每个输出通道的滤波器权重。通过合并操作再次对图像进行下采样使得图像分辨率从14×14减小到7×7。此卷积层的特征总数为 64。 图 7前两个卷积层的数据流 通过3×3第三卷积层再次过滤 64 个结果图像。没有对该层应用池操作。第三卷积层的输出是128×7×7像素图像。然后将这些图像展平为单个向量长度为4×4×128 2048其用作完全连接层的输入。 LeNet-5 的输出层由 625 个神经元作为输入即完全连接层的输出和 10 个神经元作为输出用于确定图像的类别该数字在图片。 图 8最后三个卷积层的数据流 卷积滤波器最初是随机选择的。输入图像的预测类和实际类之间的差异被称为成本函数并且这使我们的网络超出训练数据。然后优化器会自动通过 CNN 传播此成本函数并更新过滤器权重以改善分类误差。这反复进行数千次直到分类误差足够低。 现在让我们详细看看如何编写我们的第一个 CNN。让我们首先导入我们实现所需的 TensorFlow 库 import tensorflow as tf import numpy as np from tensorflow.examples.tutorials.mnist import input_data设置以下参数。它们表示在训练阶段128和测试阶段256使用的样本数量 batch_size 128 test_size 256当我们定义以下参数时该值为28因为 MNIST 图像的高度和宽度为28像素 img_size 28对于类的数量值10意味着我们将为每个 0 到 9 位数设置一个类 num_classes 10为输入图像定义占位符变量X。该张量的数据类型设置为float32形状设置为[None, img_size, img_size, 1]其中None表示张量可以保存任意数量的图像 X tf.placeholder(float, [None, img_size, img_size, 1])然后我们为占位符变量X中的输入图像正确关联的标签设置另一个占位符变量Y。此占位符变量的形状为[None, num_classes]这意味着它可以包含任意数量的标签。每个标签都是长度为num_classes的向量在这种情况下为10 Y tf.placeholder(float, [None, num_classes])我们收集MNIST数据这些数据将被复制到数据文件夹中 mnist input_data.read_data_sets(MNIST-data, one_hotTrue)我们构建训练数据集trXtrY和测试网络teXteY) trX, trY, teX, teY mnist.train.images, \mnist.train.labels, \mnist.test.images, \mnist.test.labels必须重新整形trX和teX图像集以匹配输入形状 trX trX.reshape(-1, img_size, img_size, 1) teX teX.reshape(-1, img_size, img_size, 1)我们现在开始定义网络的weights。 init_weights函数在提供的形状中构建新变量并使用随机值初始化网络权重 def init_weights(shape):return tf.Variable(tf.random_normal(shape, stddev0.01))第一卷积层的每个神经元被卷积为输入张量的小子集尺寸为3×3×1。值32只是我们为第一层考虑的特征图的数量。然后定义权重w w init_weights([3, 3, 1, 32])然后输入的数量增加到32这意味着第二卷积层中的每个神经元被卷积到第一卷积层的3×3×32个神经元。w2权重如下 w2 init_weights([3, 3, 32, 64])值64表示获得的输出特征的数量。第三个卷积层被卷积为前一层的3x3x64个神经元而128是结果特征。 w3 init_weights([3, 3, 64, 128])第四层完全连接并接收128x4x4输入而输出等于625 w4 init_weights([128 * 4 * 4, 625])输出层接收625输入输出是类的数量 w_o init_weights([625, num_classes])请注意这些初始化实际上并未在此时完成。它们仅在 TensorFlow 图中定义。 p_keep_conv tf.placeholder(float) p_keep_hidden tf.placeholder(float)是时候定义网络模型了。就像网络的权重定义一样它将是一个函数。它接收X张量权重张量和丢弃参数作为卷积和完全连接层的输入 def model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden): tf.nn.conv2d()执行 TensorFlow 操作进行卷积。请注意所有尺寸的strides都设置为 1。实际上第一步和最后一步必须始终为 1因为第一步是图像编号最后一步是输入通道。padding参数设置为SAME这意味着输入图像用零填充因此输出的大小相同 conv1 tf.nn.conv2d(X, w,strides[1, 1, 1, 1],\paddingSAME)然后我们将conv1层传递给 ReLU 层。它为每个输入像素x计算max(x, 0)函数为公式添加一些非线性并允许我们学习更复杂的函数 conv1_a tf.nn.relu(conv1)然后由tf.nn.max_pool运算符合并生成的层 conv1 tf.nn.max_pool(conv1_a, ksize[1, 2, 2, 1]\,strides[1, 2, 2, 1],\paddingSAME)这是一个2×2最大池这意味着我们正在检查2×2窗口并在每个窗口中选择最大值。然后我们将 2 个像素移动到下一个窗口。我们尝试通过tf.nn.dropout()函数减少过拟合我们传递conv1层和p_keep_conv概率值 conv1 tf.nn.dropout(conv1, p_keep_conv)如您所见接下来的两个卷积层conv2和conv3的定义方式与conv1相同 conv2 tf.nn.conv2d(conv1, w2,\strides[1, 1, 1, 1],\paddingSAME)conv2_a tf.nn.relu(conv2)conv2 tf.nn.max_pool(conv2, ksize[1, 2, 2, 1],\strides[1, 2, 2, 1],\paddingSAME)conv2 tf.nn.dropout(conv2, p_keep_conv)conv3tf.nn.conv2d(conv2, w3,\strides[1, 1, 1, 1]\,paddingSAME)conv3 tf.nn.relu(conv3)完全连接的层将添加到网络中。第一个FC_layer的输入是前一个卷积的卷积层 FC_layer tf.nn.max_pool(conv3, ksize[1, 2, 2, 1],\strides[1, 2, 2, 1],\paddingSAME)FC_layer tf.reshape(FC_layer,\[-1, w4.get_shape().as_list()[0]])dropout函数再次用于减少过拟合 FC_layer tf.nn.dropout(FC_layer, p_keep_conv)输出层接收FC_layer和w4权重张量作为输入。应用 ReLU 和丢弃运算符 output_layer tf.nn.relu(tf.matmul(FC_layer, w4))output_layer tf.nn.dropout(output_layer, p_keep_hidden)结果是一个长度为 10 的向量。这用于确定图像所属的 10 个输入类中的哪一个 result tf.matmul(output_layer, w_o)return result交叉熵是我们在此分类器中使用的表现指标。交叉熵是一个连续的函数它总是正的如果预测的输出与期望的输出完全匹配则等于零。因此这种优化的目标是通过改变网络层中的变量来最小化交叉熵使其尽可能接近零。 TensorFlow 具有用于计算交叉熵的内置函数。请注意该函数在内部计算 softmax因此我们必须直接使用py_x的输出 py_x model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden)Y_ tf.nn.softmax_cross_entropy_with_logits_v2\(labelsY,logitspy_x)现在我们已经为每个分类图像定义了交叉熵我们可以衡量模型在每个图像上的表现。我们需要一个标量值来使用交叉熵来优化网络变量因此我们只需要对所有分类图像求平均交叉熵 cost tf.reduce_mean(Y_)为了最小化评估的cost我们必须定义一个优化器。在这种情况下我们将使用RMSPropOptimizer它是 GD 的高级形式。RMSPropOptimizer实现了 RMSProp 算法这是一种未发表的自适应学习率方法由 Geoff Hinton 在他的 Coursera 课程的第 6 讲中提出。 注意 您可以在此链接找到 Geoff Hinton 的课程。 RMSPropOptimizer还将学习率除以梯度平方的指数衰减均值。 Hinton 建议将衰减参数设置为0.9而学习率的良好默认值为0.001 optimizer tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)基本上通用 SGD 算法存在一个问题即学习率必须以1 / T其中T是迭代次数进行缩放以实现收敛。 RMSProp 尝试通过自动调整步长来解决这个问题以使步长与梯度相同。随着平均梯度变小SGD 更新中的系数变得更大以进行补偿。 注意 有关此算法的有趣参考可在此处找到。 最后我们定义predict_op它是模式输出中尺寸最大值的索引 predict_op tf.argmax(py_x, 1)请注意此时不执行优化。什么都没有计算因为我们只是将优化器对象添加到 TensorFlow 图中以便以后执行。 我们现在来定义网络的运行会话。训练集中有 55,000 个图像因此使用所有这些图像计算模型的梯度需要很长时间。因此我们将在优化器的每次迭代中使用一小批图像。如果您的计算机崩溃或由于 RAM 耗尽而变得非常慢那么您可以减少此数量但您可能需要执行更多优化迭代。 现在我们可以继续实现 TensorFlow 会话 with tf.Session() as sess:tf.global_variables_initializer().run()for i in range(100):我们得到了一批训练样例training_batch张量现在包含图像的子集和相应的标签 training_batch zip(range(0, len(trX), batch_size),\range(batch_size, \len(trX)1, \batch_size))将批量放入feed_dict中并在图中为占位符变量指定相应的名称。我们现在可以使用这批训练数据运行优化器。 TensorFlow 将馈送中的变量分配给占位符变量然后运行优化程序 for start, end in training_batch:sess.run(optimizer, feed_dict{X: trX[start:end],\Y: trY[start:end],\p_keep_conv: 0.8,\p_keep_hidden: 0.5})同时我们得到了打乱的一批测试样本 test_indices np.arange(len(teX)) np.random.shuffle(test_indices)test_indices test_indices[0:test_size]对于每次迭代我们显示批次的评估accuracy print(i, np.mean(np.argmax(teY[test_indices], axis1) \sess.run\(predict_op,\feed_dict{X: teX[test_indices],\Y: teY[test_indices], \p_keep_conv: 1.0,\p_keep_hidden: 1.0})))根据所使用的计算资源训练网络可能需要几个小时。我机器上的结果如下 Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes. Successfully extracted to train-images-idx3-ubyte.mnist 9912422 bytes. Loading ata/train-images-idx3-ubyte.mnist Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes. Successfully extracted to train-labels-idx1-ubyte.mnist 28881 bytes. Loading ata/train-labels-idx1-ubyte.mnist Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes. Successfully extracted to t10k-images-idx3-ubyte.mnist 1648877 bytes. Loading ata/t10k-images-idx3-ubyte.mnist Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes. Successfully extracted to t10k-labels-idx1-ubyte.mnist 4542 bytes. Loading ata/t10k-labels-idx1-ubyte.mnist (0, 0.95703125) (1, 0.98046875) (2, 0.9921875) (3, 0.99609375) (4, 0.99609375) (5, 0.98828125) (6, 0.99609375) (7, 0.99609375) (8, 0.98828125) (9, 0.98046875) (10, 0.99609375) . . . .. . (90, 1.0) (91, 0.9921875) (92, 0.9921875) (93, 0.99609375) (94, 1.0) (95, 0.98828125) (96, 0.98828125) (97, 0.99609375) (98, 1.0) (99, 0.99609375) 经过 10,000 次迭代后 模型的准确率为 99.60%这还不错 AlexNet AlexNet 神经网络是首批实现巨大成功的 CNN 之一。作为 2012 年 ILSVRC 的获胜者这个神经网络是第一个使用 LeNet-5 网络之前定义的神经网络的标准结构在 ImageNet 等非常复杂的数据集上获得良好结果。 注意 ImageNet 项目是一个大型视觉数据库设计用于视觉对象识别软件研究。截至 2016 年ImageNet 手工标注了超过一千万个图像 URL以指示图像中的对象。在至少一百万个图像中还提供了边界框。第三方图像 URL 的标注数据库可直接从 ImageNet 免费获得。 AlexNet 的架构如下图所示 图 9AlexNet 网络 在 AlexNet 架构中有八层具有可训练参数一系列五个连续卷积层后面是三个完全连接的层。每个卷积层之后是 ReLU 层并且可选地还有最大池层尤其是在网络的开始处以便减少网络占用的空间量。 所有池层都有3x3扩展区域步长率为 2这意味着您始终使用重叠池。这是因为与没有重叠的普通池相比这种类型的池提供了稍好的网络表现。在网络的开始在池化层和下一个卷积层之间总是使用几个 LRN 标准化层经过一些测试可以看出它们倾向于减少网络误差。 前两个完全连接的层拥有 4,096 个神经元而最后一个拥有 1,000 个单元对应于 ImageNet 数据集中的类数。考虑到完全连接层中的大量连接在每对完全连接的层之间添加了比率为 0.5 的丢弃层即每次忽略一半的神经元激活。在这种情况下已经注意到使用丢弃技术不仅加速了单次迭代的处理而且还很好地防止了过拟合。没有丢弃层网络制造商声称原始网络过拟合。 迁移学习 迁移学习包括建立已经构建的网络并对各个层的参数进行适当的更改以便它可以适应另一个数据集。例如您可以在大型数据集如 ImageNet上使用预先测试的网络并在较小的数据集上再次训练它。如果我们的数据集在内容上与原始数据集没有明显不同那么预先训练的模型已经具有与我们自己的分类问题相关的学习特征。 如果我们的数据集与预训练模型训练的数据集没有太大差异我们可以使用微调技术。 已在大型不同数据集上进行预训练的模型可能会捕捉到早期层中的曲线和边缘等通用特征这些特征在大多数分类问题中都是相关且有用的。但是如果我们的数据集来自一个非常特定的域并且找不到该域中预先训练好的网络我们应该考虑从头开始训练网络。 预训练的 AlexNet 我们会对预先训练好的 AlexNet 进行微调以区分狗和猫。 AlexNet 在 ImageNet 数据集上经过预先训练。 要执行这个例子你还需要安装 scipy参见此链接和 PILPillow这是 scipy 使用的读取图像pip install Pillow或pip3 install Pillow。 然后您需要下载以下文件 myalexnet_forward.py2017 版 TensorFlow 的 AlexNet 实现和测试代码Python 3.5bvlc_alexnet.npy权重需要在工作目录中caffe_classes.py类与网络输出的顺序相同poodle.pnglaska.pngdog.pngdog2.pngquail227.JPEG测试图像图像应为227×227×3 从此链接下载这些文件或从本书的代码库中下载。 首先我们将在之前下载的图像上测试网络。为此只需从 Python GUI 运行myalexnet_forward.py即可。 通过简单地检查源代码可以看到参见下面的代码片段将调用预先训练好的网络对以下两个图像进行分类laska.png和poodle.png这些图像之前已下载过 im1 (imread(laska.png)[:,:,:3]).astype(float32) im1 im1 - mean(im1) im1[:, :, 0], im1[:, :, 2] im1[:, :, 2], im1[:, :, 0]im2 (imread(poodle.png)[:,:,:3]).astype(float32) im2[:, :, 0], im2[:, :, 2] im2[:, :, 2], im2[:, :, 0]图 10要分类的图像 的权重和偏置由以下语句加载 net_data load(open(bvlc_alexnet.npy, rb), encodinglatin1).item()网络是一组卷积和池化层后面是三个完全连接的状态。该模型的输出是 softmax 函数 prob tf.nn.softmax(fc8)softmax 函数的输出是分类等级因为它们表示网络认为输入图像属于caffe_classes.py文件中定义的类的强度。 如果我们运行代码我们应该得到以下结果 Image 0 weasel 0.503177 black-footed ferret, ferret, Mustela nigripes 0.263265 polecat, fitch, foulmart, foumart, Mustela putorius 0.147746 mink 0.0649517 otter 0.00771955 Image 1 clumber, clumber spaniel 0.258953 komondor 0.165846 miniature poodle 0.149518 toy poodle 0.0984719 kuvasz 0.0848062 0.40007972717285156在前面的例子中AlexNet 给鼬鼠的分数约为 50%。这意味着该模型非常有信心图像显示黄鼠狼其余分数可视为噪音。 数据集准备 我们的任务是建立一个区分狗和猫的图像分类器。我们从 Kaggle 那里得到一些帮助我们可以从中轻松下载数据集。 在此数据集中训练集包含 20,000 个标记图像测试和验证集包含 2,500 个图像。 要使用数据集必须将每个图像重新整形为227×227×3。为此您可以使用prep_images.py中的 Python 代码。否则您可以使用本书仓库中的trainDir.rar和testDir.rar文件。它们包含 6,000 个用于训练的犬和猫的重塑图像以及 100 个重新成形的图像用于测试。 以下部分中描述的以下微调实现在alexnet_finetune.py中实现可以在本书的代码库中下载。 微调的实现 我们的分类任务包含两个类别因此网络的新 softmax 层将包含 2 个类别而不是 1,000 个类别。这是输入张量它是一个227×227×3图像以及等级 2 的输出张量 n_classes 2 train_x zeros((1, 227,227,3)).astype(float32) train_y zeros((1, n_classes))微调实现包括截断预训练网络的最后一层softmax 层并将其替换为与我们的问题相关的新 softmax 层。 例如ImageNet 上预先训练好的网络带有一个包含 1,000 个类别的 softmax 层。 以下代码片段定义了新的 softmax 层fc8 fc8W tf.Variable(tf.random_normal\([4096, n_classes]),\trainableTrue, namefc8w) fc8b tf.Variable(tf.random_normal\([n_classes]),\trainableTrue, namefc8b) fc8 tf.nn.xw_plus_b(fc7, fc8W, fc8b) prob tf.nn.softmax(fc8)损失是用于分类的表现指标。它是一个始终为正的连续函数如果模型的预测输出与期望的输出完全匹配则交叉熵等于零。因此优化的目标是通过改变模型的权重和偏置来最小化交叉熵因此它尽可能接近零。 TensorFlow 具有用于计算交叉熵的内置函数。为了使用交叉熵来优化模型的变量我们需要一个标量值因此我们只需要对所有图像分类采用交叉熵的平均值 loss tf.reduce_mean\(tf.nn.softmax_cross_entropy_with_logits_v2\(logits prob, labelsy)) opt_vars [v for v in tf.trainable_variables()\if (v.name.startswith(fc8))]既然我们必须最小化成本度量那么我们可以创建optimizer optimizer tf.train.AdamOptimizer\(learning_ratelearning_rate).minimize\(loss, var_list opt_vars) correct_pred tf.equal(tf.argmax(prob, 1), tf.argmax(y, 1)) accuracy tf.reduce_mean(tf.cast(correct_pred, tf.float32))在这种情况下我们使用步长为0.5的AdamOptimizer。请注意此时不执行优化。事实上根本没有计算任何东西我们只需将优化器对象添加到 TensorFlow 图中以便以后执行。然后我们在网络上运行反向传播以微调预训练的权重 batch_size 100 training_iters 6000 display_step 1 dropout 0.85 # Dropout, probability to keep unitsinit tf.global_variables_initializer() with tf.Session() as sess:sess.run(init)step 1继续训练直到达到最大迭代次数 while step * batch_size training_iters:batch_x, batch_y \next(next_batch(batch_size)) #.next()运行优化操作反向传播 sess.run(optimizer, \feed_dict{x: batch_x, \y: batch_y, \keep_prob: dropout})if step % display_step 0:计算批次损失和准确率 cost, acc sess.run([loss, accuracy],\feed_dict{x: batch_x, \y: batch_y, \keep_prob: 1.})print (Iter str(step*batch_size) \ , Minibatch Loss \{:.6f}.format(cost) \, Training Accuracy \{:.5f}.format(acc)) step 1print (Optimization Finished!)网络训练产生以下结果 Iter 100, Minibatch Loss 0.555294, Training Accuracy 0.76000 Iter 200, Minibatch Loss 0.584999, Training Accuracy 0.73000 Iter 300, Minibatch Loss 0.582527, Training Accuracy 0.73000 Iter 400, Minibatch Loss 0.610702, Training Accuracy 0.70000 Iter 500, Minibatch Loss 0.583640, Training Accuracy 0.73000 Iter 600, Minibatch Loss 0.583523, Training Accuracy 0.73000 ………………………………………………………………… ………………………………………………………………… Iter 5400, Minibatch Loss 0.361158, Training Accuracy 0.95000 Iter 5500, Minibatch Loss 0.403371, Training Accuracy 0.91000 Iter 5600, Minibatch Loss 0.404287, Training Accuracy 0.91000 Iter 5700, Minibatch Loss 0.413305, Training Accuracy 0.90000 Iter 5800, Minibatch Loss 0.413816, Training Accuracy 0.89000 Iter 5900, Minibatch Loss 0.413476, Training Accuracy 0.90000 Optimization Finished! 要测试我们的模型我们将预测与标签集cat 0dog 1进行比较 output sess.run(prob, feed_dict {x:imlist, keep_prob: 1.})result np.argmax(output,1)testResult [1,1,1,1,0,0,0,0,0,0,\0,1,0,0,0,0,1,1,0,0,\1,0,1,1,0,1,1,0,0,1,\1,1,1,0,0,0,0,0,1,0,\1,1,1,1,0,1,0,1,1,0,\1,0,0,1,0,0,1,1,1,0,\1,1,1,1,1,0,0,0,0,0,\0,1,1,1,0,1,1,1,1,0,\0,0,1,0,1,1,1,1,0,0,\0,0,0,1,1,0,1,1,0,0]count 0for i in range(0,99):if result[i] testResult[i]:countcount1print(Testing Accuracy str(count) %)最后我们有我们模型的准确率 Testing Accuracy 82%VGG VGG 是在 2014 年 ILSVRC 期间发明神经网络的人的名字。我们谈论的是复数网络因为创建了同一网络的多个版本每个拥有不同数量的层。根据层数n这些网络中的一个具有的权重它们中的每一个通常称为 VGG-n。所有这些网络都比 AlexNet 更深。这意味着它们由多个层组成其参数比 AlexNet 更多在这种情况下总共有 11 到 19 个训练层。通常只考虑可行的层因为它们会影响模型的处理和大小如前一段所示。然而整体结构仍然非常相似总是有一系列初始卷积层和最后一系列完全连接的层后者与 AlexNet 完全相同。因此使用的卷积层的数量当然还有它们的参数有什么变化。下表显示了 VGG 团队构建的所有变体。 每一列从左侧开始向右侧显示一个特定的 VGG 网络从最深到最浅。粗体项表示与先前版本相比每个版本中添加的内容。 ReLU 层未在表中显示但在网络中它存在于每个卷积层之后。所有卷积层使用 1 的步幅 表VGG 网络架构 请注意 AlexNet 没有具有相当大的感受野的卷积层这里所有感受野都是3×3除了 VGG-16 中有几个具有1×1感受野的卷积层。回想一下具有 1 步梯度的凸层不会改变输入空间大小同时修改深度值该深度值与使用的内核数量相同。因此VGG 卷积层不会影响输入体积的宽度和高度只有池化层才能这样做。使用具有较小感受野的一系列卷积层的想法最终总体上模拟具有较大感受野的单个卷积层这是由于这样的事实即以这种方式使用多个 ReLU 层而不是单独使用一个从而增加激活函数的非线性从而使其更具区别性。它还用于减少使用的参数数量。这些网络被认为是 AlexNet 的演变因为总体而言使用相同的数据集它们的表现优于 AlexNet。 VGG 网络演示的主要概念是拥塞神经网络越来越深刻其表现也越来越高。但是 必须拥有越来越强大的硬件否则网络训练就会成为问题。 对于 VGG使用了四个 NVIDIA Titan Blacks每个都有 6 GB 的内存。因此VGG 具有更好的表现但需要大量的硬件用于训练并且还使用大量参数例如VGG-19 模型大约为 550MB是 AlexNet 的两倍。较小的 VGG 网络仍然具有大约 507MB 的模型。 用 VGG-19 学习艺术风格 在这个项目中我们使用预训练的 VGG-19 来学习艺术家创建的样式和模式并将它们转移到图像中项目文件是style_transfer.py在本书的 GitHub 仓库中。这种技术被称为artistic style learning参见 Gatys 等人的文章 A Art Algorithm of Artistic Style。根据学术文献艺术风格学习定义如下给定两个图像作为输入合成具有第一图像的语义内容和第二图像的纹理/风格的第三图像。 为了使其正常工作我们需要训练一个深度卷积神经网络来构建以下内容 用于确定图像 A 内容的内容提取器 用于确定图像 B 样式的样式提取器 合并器将一些任意内容与另一个任意样式合并以获得最终结果 图 11艺术风格学习操作模式 输入图像 输入图像每个都是478×478像素是您在本书的代码库中也可以找到的以下图像cat.jpg和mosaic.jpg [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K8RNpZcU-1681565849704)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_04_13.jpg)] 图 12艺术风格学习中的输入图像 为了通过 VGG 模型分析 需要对这些图像进行预处理 添加额外的维度 从输入图像中减去MEAN_VALUES MEAN_VALUES np.array([123.68, 116.779, 103.939]).reshape((1,1,1,3)) content_image preprocess(cat.jpg) style_image preprocess(mosaic.jpg) def preprocess(path):image plt.imread(path)image image[np.newaxis]image image - MEAN_VALUESreturn image内容提取器和损失 为了隔离图像的语义内容我们使用预先训练好的 VGG-19 神经网络对权重进行了一些微调以适应这个问题然后使用其中一个隐藏层的输出作为内容提取器。下图显示了用于此问题的 CNN 图 13用于艺术风格学习的 VGG-19 使用以下代码加载预训练的 VGG import scipy.io vgg scipy.io.loadmat(imagenet-vgg-verydeep-19.mat)imagenet-vgg-verydeep-19.mat模型应从此链接下载。 该模型有 43 层其中 19 层是卷积层。其余的是最大池/激活/完全连接的层。 我们可以检查每个卷积层的形状 [print (vgg_layers[0][i][0][0][2][0][0].shape,\vgg_layers[0][i][0][0][0][0]) for i in range(43) if conv in vgg_layers[0][i][0][0][0][0] \or fc in vgg_layers[0][i][0][0][0][0]]上述代码的结果如下 (3, 3, 3, 64) conv1_1 (3, 3, 64, 64) conv1_2 (3, 3, 64, 128) conv2_1 (3, 3, 128, 128) conv2_2 (3, 3, 128, 256) conv3_1 (3, 3, 256, 256) conv3_2 (3, 3, 256, 256) conv3_3 (3, 3, 256, 256) conv3_4 (3, 3, 256, 512) conv4_1 (3, 3, 512, 512) conv4_2 (3, 3, 512, 512) conv4_3 (3, 3, 512, 512) conv4_4 (3, 3, 512, 512) conv5_1 (3, 3, 512, 512) conv5_2 (3, 3, 512, 512) conv5_3 (3, 3, 512, 512) conv5_4 (7, 7, 512, 4096) fc6 (1, 1, 4096, 4096) fc7 (1, 1, 4096, 1000) fc8 每种形状以下列方式表示[kernel height, kernel width, number of input channels, number of output channels]。 第一层有 3 个输入通道因为输入是 RGB 图像而卷积层的输出通道数从 64 到 512所有内核都是3x3矩阵。 然后我们应用转移学习技术以使 VGG-19 网络适应我们的问题 不需要完全连接的层因为它们用于对象识别。 最大池层代替平均池层以获得更好的结果。平均池层的工作方式与卷积层中的内核相同。 IMAGE_WIDTH 478 IMAGE_HEIGHT 478 INPUT_CHANNELS 3 model {} model[input] tf.Variable(np.zeros((1, IMAGE_HEIGHT,\IMAGE_WIDTH,\INPUT_CHANNELS)),\dtype float32)model[conv1_1] conv2d_relu(model[input], 0, conv1_1) model[conv1_2] conv2d_relu(model[conv1_1], 2, conv1_2) model[avgpool1] avgpool(model[conv1_2])model[conv2_1] conv2d_relu(model[avgpool1], 5, conv2_1) model[conv2_2] conv2d_relu(model[conv2_1], 7, conv2_2) model[avgpool2] avgpool(model[conv2_2])model[conv3_1] conv2d_relu(model[avgpool2], 10, conv3_1) model[conv3_2] conv2d_relu(model[conv3_1], 12, conv3_2) model[conv3_3] conv2d_relu(model[conv3_2], 14, conv3_3) model[conv3_4] conv2d_relu(model[conv3_3], 16, conv3_4) model[avgpool3] avgpool(model[conv3_4])model[conv4_1] conv2d_relu(model[avgpool3], 19,conv4_1) model[conv4_2] conv2d_relu(model[conv4_1], 21, conv4_2) model[conv4_3] conv2d_relu(model[conv4_2], 23, conv4_3) model[conv4_4] conv2d_relu(model[conv4_3], 25,conv4_4) model[avgpool4] avgpool(model[conv4_4])model[conv5_1] conv2d_relu(model[avgpool4], 28, conv5_1) model[conv5_2] conv2d_relu(model[conv5_1], 30, conv5_2) model[conv5_3] conv2d_relu(model[conv5_2], 32, conv5_3) model[conv5_4] conv2d_relu(model[conv5_3], 34, conv5_4) model[avgpool5] avgpool(model[conv5_4])这里我们定义了contentloss函数来测量两个图像p和x之间的内容差异 def contentloss(p, x):size np.prod(p.shape[1:])loss (1./(2*size)) * tf.reduce_sum(tf.pow((x - p),2))return loss当输入图像在内容方面彼此非常接近并且随着其内容偏离而增长时该函数倾向于为 0。 我们将在conv5_4层上使用contentloss。这是输出层其输出将是预测因此我们需要使用contentloss函数将此预测与实际预测进行比较 content_loss contentloss\(sess.run(model[conv5_4]), model[conv5_4])最小化content_loss意味着混合图像在给定层中具有与内容图像的激活非常相似的特征激活。 样式提取器和损失 样式提取器使用过滤器的 Gram 矩阵作为给定的隐藏层。简单来说使用这个矩阵我们可以破坏图像的语义保留其基本组件并使其成为一个好的纹理提取器 def gram_matrix(F, N, M):Ft tf.reshape(F, (M, N))return tf.matmul(tf.transpose(Ft), Ft)style_loss测量两个图像彼此之间的接近程度。此函数是样式图像和输入noise_image生成的 Gram 矩阵元素的平方差的总和 noise_image np.random.uniform\(-20, 20,\(1, IMAGE_HEIGHT, \IMAGE_WIDTH,\INPUT_CHANNELS)).astype(float32)def style_loss(a, x):N a.shape[3]M a.shape[1] * a.shape[2]A gram_matrix(a, N, M)G gram_matrix(x, N, M)result (1/(4 * N**2 * M**2))* tf.reduce_sum(tf.pow(G-A,2))return resultstyle_loss生长 因为它的两个输入图像a和x倾向于偏离风格。 合并和总损失 我们可以合并内容和样式损失以便训练输入noise_image来输出在层中与样式图像类似的样式其特征相似于内容图像 alpha 1 beta 100 total_loss alpha * content_loss beta * styleloss训练 最小化网络中的损失以便样式损失输出图像的样式和样式图像的样式之间的损失内容损失内容图像和输出图像之间的损失以及总变异损失尽可能低 train_step tf.train.AdamOptimizer(1.5).minimize(total_loss)从这样的网络生成的输出图像应该类似于输入图像并且具有样式图像的造型师属性。 最后我们可以准备网络进行训练 sess.run(tf.global_variables_initializer()) sess.run(model[input].assign(input_noise)) for it in range(2001):sess.run(train_step)if it%100 0:mixed_image sess.run(model[input])print(iteration:,it,cost: , sess.run(total_loss))filename out2/%d.png % (it)deprocess(filename, mixed_image)训练时间可能非常耗时但结果可能非常有趣 iteration: 0 cost: 8.14037e11 iteration: 100 cost: 1.65584e10 iteration: 200 cost: 5.22747e09 iteration: 300 cost: 2.72995e09 iteration: 400 cost: 1.8309e09 iteration: 500 cost: 1.36818e09 iteration: 600 cost: 1.0804e09 iteration: 700 cost: 8.83103e08 iteration: 800 cost: 7.38783e08 iteration: 900 cost: 6.28652e08 iteration: 1000 cost: 5.41755e08 经过 1000 次迭代后我们创建了一个新的拼接 图 14艺术风格学习中的输出图像 真是太棒了你终于可以训练你的神经网络像毕加索一样画画…玩得开心 Inception-v3 Szegedy 和其他人在 2014 年的论文“Going Deeper with Convolutions”中首次介绍了 Inception 微架构 图 15GoogLeNet 中使用的 Original Inception 模块 初始模块的目标是通过在网络的同一模块内计算1×13×3和5×5卷积来充当多级特征提取器 - 这些滤波器的输出然后在被馈送到网络中的下一层之前沿着信道维度堆叠。这种架构的原始版本称为 GoogLeNet但后续形式简称为 InceptionVN其中 N 表示 Google 推出的版本号。 您可能想知道为什么我们在同一输入上使用不同类型的卷积。答案是只要仔细研究了它的参数就不可能总是获得足够的有用特征来用单个卷积进行精确分类。事实上使用一些输入它可以更好地使用卷积小内核而其他输入可以使用其他类型的内核获得更好的结果。可能由于这个原因GoogLeNet 团队想要在他们自己的网络中考虑一些替代方案。如前所述为此目的GoogLeNet 在同一网络级别即它们并行使用三种类型的卷积层1×1层3×3层和5×5层。 这个 3 层并行局部结构的结果是它们所有输出值的组合链接到单个向量输出它将是下一层的输入。这是通过使用连接层完成的。除了三个并行卷积层之外在相同的本地结构中还添加了一个池化层因为池化操作对于 CNN 的成功至关重要。 使用 TensorFlow 探索初始化 从此链接你应该能够下载相应的模型库。 然后键入以下命令 cd models/tutorials/image/imagenet python classify_image.py 当程序第一次运行时classify_image.py从 tensorflow.org 下载经过训练的模型。您的硬盘上需要大约 200MB 的可用空间。 上面的命令将对提供的熊猫图像进行分类。如果模型正确运行脚本将生成以下输出 giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca (score 0.88493) indri, indris, Indri indri, Indri brevicaudatus (score 0.00878) lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens (score 0.00317) custard apple (score 0.00149) earthstar (score 0.00127) 如果您想提供其他 JPEG 图像可以通过编辑来完成 image_file argument: python classify_image.py --imageimage.jpg您可以通过从互联网下载图像并查看其产生的结果来测试初始阶段。 例如您可以尝试从此链接获取以下图像我们将其重命名为inception_image.jpg 图 16使用 Inception-v3 进行分类的输入图像 结果如下 python classify_image.py --imageinception_example.jpg strawberry (score 0.91541) crayfish, crawfish, crawdad, crawdaddy (score 0.01208) chocolate sauce, chocolate syrup (score 0.00628) cockroach, roach (score 0.00572) grocery store, grocery, food market, market (score 0.00264)听起来不错 CNN 的情感识别 深度学习中难以解决的一个问题与神经网络无关它是以正确格式获取正确数据。但是Kaggle 平台提供了新的问题并且需要研究新的数据集。 Kaggle 成立于 2010 年作为预测建模和分析竞赛的平台公司和研究人员发布他们的数据来自世界各地的统计人员和数据挖掘者竞争生产最佳模型。在本节中我们将展示如何使用面部图像制作 CNN 以进行情感检测。此示例的训练和测试集可以从此链接下载。 图 17Kaggle 比赛页面 训练组由 3,761 个灰度图像组成尺寸为48×48像素3,761 个标签每个图像有 7 个元素。 每个元素编码一个情感0愤怒1厌恶2恐惧3幸福4悲伤5惊讶6中立。 在经典 Kaggle 比赛中必须由平台评估从测试集获得的标签集。在这个例子中我们将训练一个来自训练组的神经网络之后我们将在单个图像上评估模型。 在开始 CNN 实现之前我们将通过实现一个简单的过程文件download_and_display_images.py来查看下载的数据。 导入库 import tensorflow as tf import numpy as np from matplotlib import pyplot as plt import EmotionUtilsread_data函数允许构建所有数据集从下载的数据开始您可以在本书的代码库中的EmotionUtils库中找到它们 FLAGS tf.flags.FLAGS tf.flags.DEFINE_string(data_dir,\EmotionDetector/,\Path to data files) images [] images EmotionUtils.read_data(FLAGS.data_dir)train_images images[0] train_labels images[1] valid_images images[2] valid_labels images[3] test_images images[4]然后打印训练的形状并测试图像 print (train images shape ,train_images.shape) print (test labels shape ,test_images.shape)显示训练组的第一个图像及其正确的标签 image_0 train_images[0] label_0 train_labels[0] print (image_0 shape ,image_0.shape) print (label set ,label_0) image_0 np.resize(image_0,(48,48))plt.imshow(image_0, cmapGreys_r) plt.show()有 3,761 个48×48像素的灰度图像 train images shape (3761, 48, 48, 1)有 3,761 个类标签每个类包含七个元素 train labels shape (3761, 7)测试集由 1,312 个48x48像素灰度图像组成 test labels shape (1312, 48, 48, 1)单个图像具有以下形状 image_0 shape (48, 48, 1)第一张图片的标签设置如下 label set [ 0\. 0\. 0\. 1\. 0\. 0\. 0.]此标签对应于快乐图像在以下 matplot 图中可视化 图 18来自情感检测面部数据集的第一图像 我们现在转向 CNN 架构。 下图显示了数据将如何在 CNN 中流动 图 19实现的 CNN 的前两个卷积层 该网络具有两个卷积层两个完全连接的层最后是 softmax 分类层。使用5×5卷积核在第一卷积层中处理输入图像48×48像素。这导致 32 个图像每个使用的滤波器一个。通过最大合并操作对图像进行下采样以将图像从48×48减小到24×24像素。然后这些 32 个较小的图像由第二卷积层处理这导致 64 个新图像见上图。通过第二次池化操作将得到的图像再次下采样到12×12像素。 第二合并层的输出是64×12×12像素的图像。然后将它们展平为长度为12×12×64 9,126的单个向量其用作具有 256 个神经元的完全连接层的输入。这将进入另一个具有 10 个神经元的完全连接的层每个类对应一个类用于确定图像的类别即图像中描绘的情感。 图 20实现的 CNN 的最后两层 让我们继续讨论权重和偏置定义。以下数据结构表示网络权重的定义并总结了到目前为止我们所描述的内容 weights {wc1: weight_variable([5, 5, 1, 32], nameW_conv1),wc2: weight_variable([3, 3, 32, 64],nameW_conv2),wf1: weight_variable([(IMAGE_SIZE // 4) * (IMAGE_SIZE // 4)\* 64,256],nameW_fc1),wf2: weight_variable([256, NUM_LABELS], nameW_fc2) }注意卷积滤波器是随机初始化的所以分类是随机完成的 def weight_variable(shape, stddev0.02, nameNone):initial tf.truncated_normal(shape, stddevstddev)if name is None:return tf.Variable(initial)else:return tf.get_variable(name, initializerinitial)以相似方式我们已经定义了偏差 biases {bc1: bias_variable([32], nameb_conv1),bc2: bias_variable([64], nameb_conv2),bf1: bias_variable([256], nameb_fc1),bf2: bias_variable([NUM_LABELS], nameb_fc2) }def bias_variable(shape, nameNone):initial tf.constant(0.0, shapeshape)if name is None:return tf.Variable(initial)else:return tf.get_variable(name, initializerinitial)优化器必须使用区分链规则通过 CNN 传播误差并更新过滤器权重以改善分类误差。输入图像的预测类和真实类之间的差异由loss函数测量。它将pred模型的预测输出和所需输出label作为输入 def loss(pred, label):cross_entropy_loss \tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2\(logitspred, labelslabel))tf.summary.scalar(Entropy, cross_entropy_loss)reg_losses tf.add_n(tf.get_collection(losses))tf.summary.scalar(Reg_loss, reg_losses)return cross_entropy_loss REGULARIZATION * reg_lossestf.nn.softmax_cross_entropy_with_logits_v2(pred, label)函数在应用 softmax 函数后计算结果的cross_entropy_loss但它以数学上仔细的方式一起完成。这就像是以下结果 a tf.nn.softmax(x) b cross_entropy(a)我们计算每个分类图像的cross_entropy_loss因此我们将测量模型在每个图像上的单独表现。 我们计算分类图像的交叉熵平均值 cross_entropy_loss tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2 (logitspred, labelslabel))为了防止过拟合我们将使用 L2 正则化其中包括向cross_entropy_loss插入一个附加项 reg_losses tf.add_n(tf.get_collection(losses)) return cross_entropy_loss REGULARIZATION * reg_losses哪里 def add_to_regularization_loss(W, b):tf.add_to_collection(losses, tf.nn.l2_loss(W))tf.add_to_collection(losses, tf.nn.l2_loss(b))注意 有关详细信息请参阅此链接。 我们已经构建了网络的权重和偏置以及优化过程。但是与所有已实现的网络一样我们必须通过导入所有必需的库来启动实现 import tensorflow as tf import numpy as np from datetime import datetime import EmotionUtils import os, sys, inspect from tensorflow.python.framework import ops import warningswarnings.filterwarnings(ignore) os.environ[TF_CPP_MIN_LOG_LEVEL] 3 ops.reset_default_graph()然后我们在您的计算机上设置存储数据集的路径以及网络参数 FLAGS tf.flags.FLAGS tf.flags.DEFINE_string(data_dir,\EmotionDetector/,\Path to data files) tf.flags.DEFINE_string(logs_dir,\logs/EmotionDetector_logs/,\Path to where log files are to be saved) tf.flags.DEFINE_string(mode,\train,\mode: train (Default)/ test) BATCH_SIZE 128 LEARNING_RATE 1e-3 MAX_ITERATIONS 1001 REGULARIZATION 1e-2 IMAGE_SIZE 48 NUM_LABELS 7 VALIDATION_PERCENT 0.1emotion_cnn函数实现我们的模型 def emotion_cnn(dataset):with tf.name_scope(conv1) as scope:tf.summary.histogram(W_conv1, weights[wc1])tf.summary.histogram(b_conv1, biases[bc1])conv_1 tf.nn.conv2d(dataset, weights[wc1],\strides[1, 1, 1, 1],\paddingSAME)h_conv1 tf.nn.bias_add(conv_1, biases[bc1])h_1 tf.nn.relu(h_conv1)h_pool1 max_pool_2x2(h_1)add_to_regularization_loss(weights[wc1], biases[bc1])with tf.name_scope(conv2) as scope:tf.summary.histogram(W_conv2, weights[wc2])tf.summary.histogram(b_conv2, biases[bc2])conv_2 tf.nn.conv2d(h_pool1, weights[wc2],\strides[1, 1, 1, 1], \ paddingSAME)h_conv2 tf.nn.bias_add(conv_2, biases[bc2])h_2 tf.nn.relu(h_conv2)h_pool2 max_pool_2x2(h_2)add_to_regularization_loss(weights[wc2], biases[bc2])with tf.name_scope(fc_1) as scope:prob0.5image_size IMAGE_SIZE // 4h_flat tf.reshape(h_pool2,[-1,image_size*image_size*64])tf.summary.histogram(W_fc1, weights[wf1])tf.summary.histogram(b_fc1, biases[bf1])h_fc1 tf.nn.relu(tf.matmul\(h_flat, weights[wf1]) biases[bf1])h_fc1_dropout tf.nn.dropout(h_fc1, prob)with tf.name_scope(fc_2) as scope:tf.summary.histogram(W_fc2, weights[wf2])tf.summary.histogram(b_fc2, biases[bf2])pred tf.matmul(h_fc1_dropout, weights[wf2]) \biases[bf2]return pred然后定义一个main函数我们将在其中定义数据集输入和输出占位符变量以及主会话以便启动训练过程 def main(argvNone):此函数中的第一个操作是加载数据集以进行训练和验证。我们将使用训练集来教授分类器识别待预测的标签我们将使用验证集来评估分类器的表现 train_images,\ train_labels,\ valid_images,\ valid_labels,\ test_imagesEmotionUtils.read_data(FLAGS.data_dir) print(Train size: %s % train_images.shape[0]) print(Validation size: %s % valid_images.shape[0]) print(Test size: %s % test_images.shape[0])我们为输入图像定义占位符变量。这允许我们更改输入到 TensorFlow 图的图像。数据类型设置为float32形状设置为[None, img_size, img_size, 1]其中None表示张量可以保存任意数量的图像每个图像为img_size像素高和img_size像素宽和1是颜色通道的数量 input_dataset tf.placeholder(tf.float32, \[None, \IMAGE_SIZE, \IMAGE_SIZE, 1],nameinput)接下来我们为与占位符变量input_dataset中输入的图像正确关联的标签提供占位符变量。这个占位符变量的形状是[None, NUM_LABELS]这意味着它可以包含任意数量的标签每个标签是长度为NUM_LABELS的向量在这种情况下为 7 input_labels tf.placeholder(tf.float32,\[None, NUM_LABELS])global_step保持跟踪到目前为止执行的优化迭代数量。我们希望在检查点中使用所有其他 TensorFlow 变量保存此变量。请注意trainableFalse这意味着 TensorFlow 不会尝试优化此变量 global_step tf.Variable(0, trainableFalse)跟随变量dropout_prob用于丢弃优化 dropout_prob tf.placeholder(tf.float32)现在为测试阶段创建神经网络。emotion_cnn()函数返回input_dataset的预测类标签pred pred emotion_cnn(input_dataset)output_pred是测试和验证的预测我们将在运行会话中计算 output_pred tf.nn.softmax(pred,nameoutput)loss_val包含输入图像的预测类pred与实际类别input_labels之间的差异 loss_val loss(pred, input_labels)train_op定义用于最小化成本函数的优化器。在这种情况下我们再次使用AdamOptimizer train_op tf.train.AdamOptimizer\(LEARNING_RATE).minimize\(loss_val, global_step)summary_op是用于 TensorBoard 可视化的 summary_op tf.summary.merge_all()创建图后我们必须创建一个 TensorFlow 会话用于执行图 with tf.Session() as sess:sess.run(tf.global_variables_initializer())summary_writer tf.summary.FileWriter(FLAGS.logs_dir, sess.graph)我们定义saver来恢复模型 saver tf.train.Saver()ckpt tf.train.get_checkpoint_state(FLAGS.logs_dir)if ckpt and ckpt.model_checkpoint_path:saver.restore(sess, ckpt.model_checkpoint_path)print (Model Restored!)接下来我们需要获得一批训练示例。batch_image现在拥有一批图像batch_label包含这些图像的正确标签 for step in xrange(MAX_ITERATIONS):batch_image, batch_label get_next_batch(train_images,\train_labels,\step)我们将批次放入dict中其中包含 TensorFlow 图中占位符变量的正确名称 feed_dict {input_dataset: batch_image, \input_labels: batch_label}我们使用这批训练数据运行优化器。 TensorFlow 将feed_dict_train中的变量分配给占位符变量然后运行优化程序 sess.run(train_op, feed_dictfeed_dict)if step % 10 0:train_loss,\summary_str \ sess.run([loss_val,summary_op],\feed_dictfeed_dict)summary_writer.add_summary(summary_str,\global_stepstep)print (Training Loss: %f % train_loss)当运行步长是 100 的倍数时我们在验证集上运行训练模型 if step % 100 0:valid_loss \sess.run(loss_val, \feed_dict{input_dataset: valid_images, input_labels: valid_labels})然后我们打印掉损失值 print (%s Validation Loss: %f \% (datetime.now(), valid_loss))在训练过程结束时模型将被保存 saver.save(sess, FLAGS.logs_dir\ model.ckpt, \global_stepstep)if __name__ __main__:tf.app.run()这是输出。如您所见在模拟过程中损失函数减少 Reading train.csv ... (4178, 48, 48, 1) (4178, 7) Reading test.csv ... Picking ... Train size: 3761 Validation size: 417 Test size: 1312 2018-02-24 15:17:45.421344 Validation Loss: 1.962773 2018-02-24 15:19:09.568140 Validation Loss: 1.796418 2018-02-24 15:20:35.122450 Validation Loss: 1.328313 2018-02-24 15:21:58.200816 Validation Loss: 1.120482 2018-02-24 15:23:24.024985 Validation Loss: 1.066049 2018-02-24 15:24:38.838554 Validation Loss: 0.965881 2018-02-24 15:25:54.761599 Validation Loss: 0.953470 2018-02-24 15:27:15.592093 Validation Loss: 0.897236 2018-02-24 15:28:39.881676 Validation Loss: 0.838831 2018-02-24 15:29:53.012461 Validation Loss: 0.910777 2018-02-24 15:31:14.416664 Validation Loss: 0.888537然而模型可以通过改变超参数或架构来改进。 在下一节中我们将了解如何在您自己的图像上有效地测试模型。 在您自己的图像上测试模型 我们使用的数据集是标准化的。所有面部都指向相机表情在某些情况下被夸大甚至滑稽。现在让我们看看如果我们使用更自然的图像会发生什么。确保脸部没有文字覆盖情感可识别脸部主要指向相机。 我从这个 JPEG 图像开始它是一个彩色图像你可以从书的代码库下载 图 21输入图像 使用 Matplotlib 和其他 NumPy Python 库我们将输入颜色图像转换为网络的有效输入即灰度图像 img mpimg.imread(author_image.jpg) gray rgb2gray(img)转换函数如下 def rgb2gray(rgb):return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])结果如下图所示 图 22灰度输入图像 最后我们可以使用此图像为网络提供信息但首先我们必须定义一个正在运行的 TensorFlow 会话 sess tf.InteractiveSession()然后我们可以回想起之前保存的模型 new_saver tf.train.\ import_meta_graph(logs/EmotionDetector_logs/model.ckpt-1000.meta) new_saver.restore(sess,logs/EmotionDetector_logs/model.ckpt-1000) tf.get_default_graph().as_graph_def() x sess.graph.get_tensor_by_name(input:0) y_conv sess.graph.get_tensor_by_name(output:0)要测试图像我们必须将其重新整形为网络的有效48×48×1格式 image_test np.resize(gray,(1,48,48,1))我们多次评估相同的图片1000以便在输入图像中获得一系列可能的情感 tResult testResult() num_evaluations 1000 for i in range(0,num_evaluations):result sess.run(y_conv, feed_dict{x:image_test})label sess.run(tf.argmax(result, 1))label label[0]label int(label)tResult.evaluate(label)tResult.display_result(num_evaluations)在几秒后会出现如下结果 anger 0.1% disgust 0.1% fear 29.1% happy 50.3% sad 0.1% surprise 20.0% neutral 0.3%最高的百分比证实happy 50.3%我们走在正确的轨道上。当然这并不意味着我们的模型是准确的。可以通过更多和更多样化的训练集更改网络参数或修改网络架构来实现可能的改进。 源代码 这里列出了实现的分类器的第二部分 from scipy import misc import numpy as np import matplotlib.cm as cm import tensorflow as tf from matplotlib import pyplot as plt import matplotlib.image as mpimg import EmotionUtils from EmotionUtils import testResultdef rgb2gray(rgb):return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])img mpimg.imread(author_image.jpg) gray rgb2gray(img) plt.imshow(gray, cmap plt.get_cmap(gray)) plt.show()sess tf.InteractiveSession() new_saver tf.train.import_meta_graph(logs/model.ckpt-1000.meta) new_saver.restore(sess, logs/model.ckpt-1000) tf.get_default_graph().as_graph_def() x sess.graph.get_tensor_by_name(input:0) y_conv sess.graph.get_tensor_by_name(output:0)image_test np.resize(gray,(1,48,48,1)) tResult testResult() num_evaluations 1000 for i in range(0,num_evaluations):result sess.run(y_conv, feed_dict{x:image_test})label sess.run(tf.argmax(result, 1))label label[0]label int(label)tResult.evaluate(label)tResult.display_result(num_evaluations)我们实现testResult Python 类来显示结果百分比。它可以在EmotionUtils文件中找到。 以下是此类的实现 class testResult:def __init__(self):self.anger 0self.disgust 0self.fear 0self.happy 0self.sad 0self.surprise 0self.neutral 0def evaluate(self,label):if (0 label):self.anger self.anger1if (1 label):self.disgust self.disgust1if (2 label):self.fear self.fear1if (3 label):self.happy self.happy1if (4 label):self.sad self.sad1if (5 label):self.surprise self.surprise1if (6 label):self.neutral self.neutral1def display_result(self,evaluations):print(anger \str((self.anger/float(evaluations))*100) %)print(disgust \str((self.disgust/float(evaluations))*100) %)print(fear \str((self.fear/float(evaluations))*100) %)print(happy \str((self.happy/float(evaluations))*100) %)print(sad \str((self.sad/float(evaluations))*100) %)print(surprise \str((self.surprise/float(evaluations))*100) %)print(neutral \str((self.neutral/float(evaluations))*100) %)总结 在本章中我们介绍了 CNN。我们已经看到 CNN 适用于图像分类问题使训练阶段更快测试阶段更准确。 最常见的 CNN 架构已经被描述LeNet-5 模型专为手写和机器打印字符识别而设计 AlexNet2012 年参加 ILSVRC; VGG 模型在 ImageNet 中实现了 92.7% 的前 5 个测试精度属于 1,000 个类别的超过 1400 万个图像的数据集;最后是 Inception-v3 模型该模型负责在 2014 年 ILSVRC 中设置分类和检测标准。 每个 CNN 架构的描述后面都是一个代码示例。此外AlexNet 网络和 VGG 示例有助于解释传输和样式学习技术的概念。 最后我们建立了一个 CNN 来对图像数据集中的情感进行分类我们在单个图像上测试了网络并评估了模型的限制和质量。 下一章将介绍自编码器这些算法可用于降维分类回归协同过滤特征学习和主题建模。我们将使用自编码器进行进一步的数据分析并使用图像数据集测量分类表现。 五、使用 TensorFlow 实现自编码器 训练自编码器是一个简单的过程。它是一个 NN其输出与其输入相同。有一个输入层后面是几个隐藏层然后在一定深度之后隐藏层遵循反向架构直到我们到达最终层与输入层相同的点。我们将数据传递到我们希望学习嵌入的网络中。 在此示例中我们使用来自 MNIST 数据集的图像作为输入。我们通过导入所有主库来开始实现 import tensorflow as tf import numpy as np import matplotlib.pyplot as plt然后我们准备 MNIST 数据集。我们使用 TensorFlow 中的内置input_data类来加载和设置数据。此类确保下载和预处理数据以供自编码器使用。因此基本上我们根本不需要进行任何特征工程 from tensorflow.examples.tutorials.mnist import input_data mnist input_data.read_data_sets(MNIST_data/,one_hotTrue)在前面的代码块中one_hotTrue参数确保所有特征都是热编码的。单热编码是一种技术通过该技术将分类变量转换为可以馈入 ML 算法的形式。 接下来我们配置网络参数 learning_rate 0.01 training_epochs 20 batch_size 256 display_step 1 examples_to_show 20输入图像的大小如下 n_input 784隐藏层的大小如下 n_hidden_1 256 n_hidden_2 128最终尺寸对应于28×28 784像素。 我们需要为输入图像定义占位符变量。该张量的数据类型设置为float因为mnist值的比例为[0,1]形状设置为[None, n_input]。定义None参数意味着张量可以包含任意数量的图像 X tf.placeholder(float, [None, n_input])然后我们可以定义网络的权重和偏置。weights数据结构包含编码器和解码器的权重定义。请注意使用tf.random_normal选择权重它返回具有正态分布的随机值 weights {encoder_h1: tf.Variable\(tf.random_normal([n_input, n_hidden_1])),encoder_h2: tf.Variable\(tf.random_normal([n_hidden_1, n_hidden_2])),decoder_h1: tf.Variable\(tf.random_normal([n_hidden_2, n_hidden_1])),decoder_h2: tf.Variable\(tf.random_normal([n_hidden_1, n_input])), }同样我们定义了网络的偏置 biases {encoder_b1: tf.Variable\(tf.random_normal([n_hidden_1])),encoder_b2: tf.Variable\(tf.random_normal([n_hidden_2])),decoder_b1: tf.Variable\(tf.random_normal([n_hidden_1])),decoder_b2: tf.Variable\(tf.random_normal([n_input])), }我们将网络建模分为两个互补的完全连接的网络编码器和解码器。编码器对数据进行编码它从 MNIST 数据集中输入图像X并执行数据编码 encoder_in tf.nn.sigmoid(tf.add\(tf.matmul(X, \weights[encoder_h1]),\biases[encoder_b1]))输入数据编码只是矩阵乘法运算。使用矩阵乘法将维度 784 的输入数据X减少到较低维度 256 这里W是权重张量encoder_h1b是偏置张量encoder_b1。通过这个操作我们将初始图像编码为自编码器的有用输入。编码过程的第二步包括数据压缩。输入encoder_in张量表示的数据通过第二个矩阵乘法运算减小到较小的大小 encoder_out tf.nn.sigmoid(tf.add\(tf.matmul(encoder_in,\weights[encoder_h2]),\biases[encoder_b2]))然后将尺寸 256 的输入数据encoder_in压缩到 128 的较小张量 这里W代表权重张量encoder_h2而b代表偏差张量encoder_b2。请注意我们使用 sigmoid 作为编码器阶段的激活函数。 解码器执行编码器的逆操作。它解压缩输入以获得相同大小的网络输入的输出。该过程的第一步是将大小为 128 的encoder_out张量转换为 256 大小的中间表示的张量 decoder_in tf.nn.sigmoid(tf.add\(tf.matmul(encoder_out,\weights[decoder_h1]),\biases[decoder_b1]))在公式中它意味着 这里W是权重张量decoder_h1大小256×128b是偏置张量decoder_b1大小 256。最终解码操作是将数据从其中间表示大小为 256解压缩到最终表示维度 784这是原始数据的大小 decoder_out tf.nn.sigmoid(tf.add\(tf.matmul(decoder_in,\weights[decoder_h2]),\biases[decoder_b2]))y_pred参数设置为decoder_out y_pred decoder_out网络将了解输入数据X是否等于解码数据因此我们定义以下内容 y_true X自编码器的要点是创建一个擅长重建原始数据的缩减矩阵。因此我们希望最小化cost函数。然后我们将cost函数定义为y_true和y_pred之间的均方误差 cost tf.reduce_mean(tf.pow(y_true - y_pred, 2))为了优化cost函数我们使用以下RMSPropOptimizer类 optimizer tf.train.RMSPropOptimizer(learning_rate).minimize(cost)然后我们准备启动会话 init tf.global_variables_initializer() with tf.Session() as sess:sess.run(init)我们需要设置批量图像的大小来训练网络 total_batch int(mnist.train.num_examples/batch_size)从训练周期开始training_epochs的数量设置为10 for epoch in range(training_epochs):循环遍历所有批次 for i in range(total_batch):batch_xs, batch_ys \mnist.train.next_batch(batch_size)然后我们运行优化程序用批量集batch_xs提供执行图 _, c sess.run([optimizer, cost],\feed_dict{X: batch_xs})接下来我们显示每个epoch步骤的结果 if epoch % display_step 0:print(„Epoch:, ‚%04d % (epoch1),„cost, „{:.9f}.format(c))print(Optimization Finished!)最后我们使用编码或解码程序测试模型 。我们为模型提供图像子集其中example_to_show的值设置为4 encode_decode sess.run(y_pred, feed_dict\{X: mnist.test.images[:examples_to_show]})我们使用 Matplotlib 比较原始图像和它们的重建 f, a plt.subplots(2, 10, figsize(10, 2))for i in range(examples_to_show):a[0][i].imshow(np.reshape(mnist.test.images[i], (28, 28)))a[1][i].imshow(np.reshape(encode_decode[i], (28, 28)))f.show()plt.draw()plt.show()当我们运行会话时我们应该有这样的输出 Extracting MNIST_data/train-images-idx3-ubyte.gz Extracting MNIST_data/train-labels-idx1-ubyte.gz Extracting MNIST_data/t10k-images-idx3-ubyte.gz Extracting MNIST_data/t10k-labels-idx1-ubyte.gz Epoch: 0001 cost 0.208461761 Epoch: 0002 cost 0.172908291 Epoch: 0003 cost 0.153524384 Epoch: 0004 cost 0.144243762 Epoch: 0005 cost 0.137013704 Epoch: 0006 cost 0.127291277 Epoch: 0007 cost 0.125370100 Epoch: 0008 cost 0.121299766 Epoch: 0009 cost 0.111687921 Epoch: 0010 cost 0.108801551 Epoch: 0011 cost 0.105516203 Epoch: 0012 cost 0.104304880 Epoch: 0013 cost 0.103362709 Epoch: 0014 cost 0.101118311 Epoch: 0015 cost 0.098779991 Epoch: 0016 cost 0.095374011 Epoch: 0017 cost 0.095469855 Epoch: 0018 cost 0.094381645 Epoch: 0019 cost 0.090281256 Epoch: 0020 cost 0.092290156 Optimization Finished!然后我们显示结果。第一行是原始图像第二行是解码图像 图 4原始和解码的 MNIST 图像 如你所见第二个与原来的不同它似乎仍然是数字二就像三个一样。我们可以增加周期数或更改网络参数以改善结果。 提高自编码器的鲁棒性 我们可以用来改善模型稳健性的成功策略是在编码阶段引入噪声。我们将去噪自编码器称为自编码器的随机版本在去噪自编码器中输入被随机破坏但相同输入的未破坏版本被用作解码阶段的目标。 直觉上 去噪自编码器做了两件事首先它试图对输入进行编码保留相关信息然后它试图消除应用于同一输入的腐败过程的影响。在下一节中我们将展示一个去噪自编码器的实现。 实现去噪自编码器 网络架构非常简单。 784 像素的输入图像被随机破坏然后通过编码网络层进行尺寸缩减。图像尺寸从 784 减少到 256 像素。 在解码阶段我们准备网络输出将图像大小返回到 784 像素。像往常一样我们开始将所有必要的库加载到我们的实现中 import numpy as np import tensorflow as tf import matplotlib.pyplot as plt from tensorflow.examples.tutorials.mnist import input_data然后我们设置基本的网络参数 n_input 784 n_hidden_1 1024 n_hidden_2 2048 n_output 784我们设置会话的参数 epochs 100 batch_size 100 disp_step 10我们构建训练和测试集。我们再次使用从tensorflow.examples.tutorials.mnist导入的input_data函数 print (PACKAGES LOADED) mnist input_data.read_data_sets(data/, one_hotTrue) trainimg mnist.train.images trainlabel mnist.train.labels testimg mnist.test.images testlabel mnist.test.labels print (MNIST LOADED)让我们为输入图像定义一个占位符变量。数据类型设置为float形状设置为[None, n_input]。None参数表示张量可以保持任意数量的图像每个图像的大小为n_input x tf.placeholder(float, [None, n_input])接下来我们有一个占位符变量用于与在占位符变量x中输入的图像相关联的真实标签。这个占位符变量的形状是[None, n_output]这意味着它可以包含任意数量的标签并且每个标签都是长度为n_output的向量在这种情况下为10 y tf.placeholder(float, [None, n_output])为了减少过拟合我们在编码和解码过程之前应用一个丢弃因此我们必须定义一个占位符以便在丢弃期间保持神经元输出的概率 dropout_keep_prob tf.placeholder(float)在这些定义中我们修正了权重和网络偏差 weights {h1: tf.Variable(tf.random_normal([n_input, n_hidden_1])),h2: tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])),out: tf.Variable(tf.random_normal([n_hidden_2, n_output])) } biases {b1: tf.Variable(tf.random_normal([n_hidden_1])),b2: tf.Variable(tf.random_normal([n_hidden_2])),out: tf.Variable(tf.random_normal([n_output])) }使用tf.random_normal选择weights和biases值它返回具有正态分布的随机值。编码阶段将来自 MNIST 数据集的图像作为输入然后通过应用矩阵乘法运算来执行数据压缩 encode_in tf.nn.sigmoid\(tf.add(tf.matmul\(x, weights[h1]),\biases[b1])) encode_out tf.nn.dropout\(encode_in, dropout_keep_prob)在解码阶段我们应用相同的过程 decode_in tf.nn.sigmoid\(tf.add(tf.matmul\(encode_out, weights[h2]),\biases[b2]))过拟合的减少是通过丢弃程序来完成的 decode_out tf.nn.dropout(decode_in,\dropout_keep_prob)最后我们准备构建预测张量y_pred y_pred tf.nn.sigmoid\(tf.matmul(decode_out,\weights[out]) \biases[out])然后我们定义一个成本度量 用于指导变量优化过程 cost tf.reduce_mean(tf.pow(y_pred - y, 2))我们将使用RMSPropOptimizer类最小化cost函数 optimizer tf.train.RMSPropOptimizer(0.01).minimize(cost)最后我们可以按如下方式初始化已定义的变量 init tf.global_variables_initializer()然后我们设置 TensorFlow 的运行会话 with tf.Session() as sess:sess.run(init)print (Start Training)for epoch in range(epochs):num_batch int(mnist.train.num_examples/batch_size)total_cost 0.for i in range(num_batch):对于每个训练周期我们从训练数据集中选择一个较小的批次集 batch_xs, batch_ys \mnist.train.next_batch(batch_size)这是焦点。我们使用之前导入的 NumPy 包中的randn函数随机破坏batch_xs集 batch_xs_noisy batch_xs \0.3*np.random.randn(batch_size, 784)我们使用这些集来提供执行图然后运行会话sess.run feeds {x: batch_xs_noisy,\y: batch_xs, \dropout_keep_prob: 0.8}sess.run(optimizer, feed_dictfeeds)total_cost sess.run(cost, feed_dictfeeds)每十个周期将显示平均成本值 if epoch % disp_step 0:print(Epoch %02d/%02d average cost: %.6f% (epoch, epochs, total_cost/num_batch))最后我们开始测试训练有素的模型 print(Start Test)为此我们从测试集中随机选择一个图像 randidx np.random.randint\(testimg.shape[0], size1)orgvec testimg[randidx, :]testvec testimg[randidx, :]label np.argmax(testlabel[randidx, :], 1)print(Test label is %d % (label))noisyvec testvec 0.3*np.random.randn(1, 784)然后我们在选定的图像上运行训练模型 outvec sess.run(y_pred,\feed_dict{x: noisyvec,\dropout_keep_prob: 1})正如我们将看到的以下plotresult函数将显示原始图像噪声图像和预测图像 plotresult(orgvec,noisyvec,outvec)print(restart Training)当我们运行会话时我们应该看到如下结果 PACKAGES LOADED Extracting data/train-images-idx3-ubyte.gz Extracting data/train-labels-idx1-ubyte.gz Extracting data/t10k-images-idx3-ubyte.gz Extracting data/t10k-labels-idx1-ubyte.gz MNIST LOADED Start Training为简洁起见我们仅报告了 100 个周期后的结果 Epoch 100/100 average cost: 0.212313 Start Test Test label is 6这些是原始图像和噪声图像如您所见数字 6 图 5原始图像和噪声图像 这是一个严格重建的图像 图 6严重重建的图像 在 100 个周期之后我们有了更好的结果 Epoch 100/100 average cost: 0.018221 Start Test Test label is 5这是原版和噪声图像 图 7原始图像和噪声图像 这是一个很好的重建图像 图 8良好的重建图像 实现卷积自编码器 到目前为止我们已经看到自编码器输入是图像。因此有必要问一下卷积架构是否可以在我们之前展示的自编码器架构上更好地工作。我们将分析编码器和解码器在卷积自编码器中的工作原理。 编码器 编码器由三个卷积层组成。特征数量从输入数据 1 变为第一卷积层的 16然后第二层从 16 到 32最后从最后一个卷积层的 32 到 64。从卷积层移动到另一个层时形状经历图像压缩 图 9编码阶段的数据流 解码器 解码器由三个依次排列的反卷积层组成。对于每个反卷积操作我们减少特征的数量以获得必须与原始图像大小相同的图像。除了减少特征数量外反卷积还可以转换图像的形状 图 10解码阶段的数据流 我们已经准备好了解如何实现卷积自编码器第一个实现步骤是加载基本库 import matplotlib.pyplot as plt import numpy as np import math import tensorflow as tf import tensorflow.examples.tutorials.mnist.input_data as input_data然后我们构建训练和测试集 mnist input_data.read_data_sets(data/, one_hotTrue) trainings mnist.train.images trainlabels mnist.train.labels testings mnist.test.images testlabels mnist.test.labels ntrain trainings.shape[0] ntest testings.shape[0] dim trainings.shape[1] nout trainlabels.shape[1]我们需要为输入图像定义占位符变量 x tf.placeholder(tf.float32, [None, dim])数据类型设置为float32形状设置为[None, dim]其中None表示张量可以保持任意数量的图像每个图像是长度为dim的向量。接下来我们为输出图像提供占位符变量。此变量的形状设置为[None, dim]与输入形状相同 y tf.placeholder(tf.float32, [None, dim])然后我们定义keepprob变量用于配置在网络训练期间使用的丢弃率 keepprob tf.placeholder(tf.float32)此外我们必须定义每个网络层中的节点数 n1 16 n2 32 n3 64 ksize 5网络总共包含六层。前三层是卷积的属于编码阶段而后三层是解卷积的是解码阶段的一部分 weights {ce1: tf.Variable(tf.random_normal\([ksize, ksize, 1, n1],stddev0.1)),ce2: tf.Variable(tf.random_normal\([ksize, ksize, n1, n2],stddev0.1)),ce3: tf.Variable(tf.random_normal\([ksize, ksize, n2, n3],stddev0.1)),cd3: tf.Variable(tf.random_normal\([ksize, ksize, n2, n3],stddev0.1)),cd2: tf.Variable(tf.random_normal\([ksize, ksize, n1, n2],stddev0.1)),cd1: tf.Variable(tf.random_normal\([ksize, ksize, 1, n1],stddev0.1)) }biases {be1: tf.Variable\(tf.random_normal([n1], stddev0.1)),be2: tf.Variable\(tf.random_normal([n2], stddev0.1)),be3: tf.Variable\(tf.random_normal([n3], stddev0.1)),bd3: tf.Variable\(tf.random_normal([n2], stddev0.1)),bd2: tf.Variable\(tf.random_normal([n1], stddev0.1)),bd1: tf.Variable\(tf.random_normal([1], stddev0.1)) }以下函数cae构建卷积自编码器传递的输入是图像_X;数据结构权重和偏置_W和_b;和_keepprob参数 def cae(_X, _W, _b, _keepprob):最初的 784 像素图像必须重新整形为28×28矩阵随后由下一个卷积层处理 _input_r tf.reshape(_X, shape[-1, 28, 28, 1])第一个卷积层是_ce1。相对于输入图像它有_input_r张量作为输入 _ce1 tf.nn.sigmoid\(tf.add(tf.nn.conv2d\(_input_r, _W[ce1],\strides[1, 2, 2, 1],\paddingSAME),\_b[be1]))在移动到第二个卷积层之前我们应用了丢弃操作 _ce1 tf.nn.dropout(_ce1, _keepprob)在下面的两个编码层中我们应用相同的卷积和丢弃操作 _ce2 tf.nn.sigmoid\(tf.add(tf.nn.conv2d\(_ce1, _W[ce2],\strides[1, 2, 2, 1],\paddingSAME),\_b[be2]))_ce2 tf.nn.dropout(_ce2, _keepprob)_ce3 tf.nn.sigmoid\(tf.add(tf.nn.conv2d\(_ce2, _W[ce3],\strides[1, 2, 2, 1],\paddingSAME),\_b[be3]))_ce3 tf.nn.dropout(_ce3, _keepprob)特征数量从 1输入图像增加到 64而原始形状图像已减少到28×28到7×7。在解码阶段压缩或编码和重新成形的图像必须为尽可能与原始图像相似。为实现这一目标我们在接下来的三个层中使用了conv2d_transpose TensorFlow 函数 tf.nn.conv2d_transpose(value, filter, output_shape, strides, paddingSAME)这种操作有时是 称为反卷积它只是conv2d的梯度。该函数的参数如下 valuefloat型和形状[batch, height, width, in_channels]的 4D 张量。filter与value和形状[height, width, output_channels, in_channels]具有相同类型的 4D 张量。in_channels维度必须与值匹配。output_shape表示去卷积操作的输出形状的 1D 张量。strides整数列表。输入张量的每个维度的滑动窗口的步幅。padding一个有效的字符串或SAME。 conv2d_transpose函数将返回与value参数类型相同的张量。第一个去卷积层_cd3具有卷积层_ce3作为输入。它返回_cd3张量其形状为(1,7,7,32) _cd3 tf.nn.sigmoid\(tf.add(tf.nn.conv2d_transpose\(_ce3, _W[cd3],\tf.stack([tf.shape(_X)[0], 7, 7, n2]),\strides[1, 2, 2, 1],\paddingSAME),\_b[bd3]))_cd3 tf.nn.dropout(_cd3, _keepprob)对于第二个去卷积层_cd2我们将反卷积层_cd3作为输入传递。它返回_cd2张量其形状为(1,14,14,16) _cd2 tf.nn.sigmoid\(tf.add(tf.nn.conv2d_transpose\(_cd3, _W[cd2],\tf.stack([tf.shape(_X)[0], 14, 14, n1]),\strides[1, 2, 2, 1],\paddingSAME),\_b[bd2]))_cd2 tf.nn.dropout(_cd2, _keepprob)第三个也是最后一个反卷积层_cd1将_cd2层作为输入传递。它返回结果_out张量其形状为(1,28,28,1)与输入图像相同 _cd1 tf.nn.sigmoid\(tf.add(tf.nn.conv2d_transpose\(_cd2, _W[cd1],\tf.stack([tf.shape(_X)[0], 28, 28, 1]),\strides[1, 2, 2, 1],\paddingSAME),\_b[bd1]))_cd1 tf.nn.dropout(_cd1, _keepprob)_out _cd1return _out然后我们将成本函数定义为y和pred之间的均方误差 pred cae(x, weights, biases, keepprob) cost tf.reduce_sum\(tf.square(cae(x, weights, biases, keepprob)\- tf.reshape(y, shape[-1, 28, 28, 1]))) learning_rate 0.001为了优化成本我们将使用AdamOptimizer optm tf.train.AdamOptimizer(learning_rate).minimize(cost)在下一步中我们配置我们的网络来运行会话 init tf.global_variables_initializer() print (Functions ready) sess tf.Session() sess.run(init) mean_img np.zeros((784))批次的大小设置为128 batch_size 128周期数是50 n_epochs 50然后我们开始循环会话 for epoch_i in range(n_epochs):对于每个周期我们得到一个批量集trainbatch for batch_i in range(mnist.train.num_examples // batch_size):batch_xs, _ mnist.train.next_batch(batch_size)trainbatch np.array([img - mean_img for img in batch_xs])我们应用随机噪声就像去噪自编码器一样来改善学习 trainbatch_noisy trainbatch 0.3*np.random.randn(\trainbatch.shape[0], 784)sess.run(optm, feed_dict{x: trainbatch_noisy \, y: trainbatch, keepprob: 0.7})print ([%02d/%02d] cost: %.4f % (epoch_i, n_epochs \, sess.run(cost, feed_dict{x: trainbatch_noisy \, y: trainbatch, keepprob: 1.})))对于每个训练周期我们随机抽取五个训练样例 if (epoch_i % 10) 0:n_examples 5test_xs, _ mnist.test.next_batch(n_examples)test_xs_noisy test_xs 0.3*np.random.randn(test_xs.shape[0], 784)然后我们在一个小子集上测试训练模型 recon sess.run(pred, feed_dict{x: test_xs_noisy,\keepprob: 1.})fig, axs plt.subplots(2, n_examples, figsize(15, 4))for example_i in range(n_examples):axs[0][example_i].matshow(np.reshape(test_xs_noisy[example_i, :], (28, 28)), cmapplt.get_cmap(gray))最后我们可以使用 Matplotlib 显示输入和学习集 axs[1][example_i].matshow(np.reshape(np.reshape(recon[example_i, ...], (784,)) mean_img, (28, 28)), cmapplt.get_cmap(gray))plt.show()执行将产生以下输出 Extracting data/train-images-idx3-ubyte.gz Extracting data/train-labels-idx1-ubyte.gz Extracting data/t10k-images-idx3-ubyte.gz Extracting data/t10k-labels-idx1-ubyte.gz Packages loaded Network ready Functions ready Start training.. [00/05] cost: 8049.0332 [01/05] cost: 3706.8667 [02/05] cost: 2839.9155 [03/05] cost: 2462.7021 [04/05] cost: 2391.9460请注意对于每个周期我们将可视化输入集和先前显示的相应学习集。正如您在第一个周期所看到的我们不知道哪些图像已被学习 图 11第一个周期图像 在第二个周期 的想法变得更加清晰 图 12第二周期图像 这是第三个周期 图 13第三周期图像 在第四个周期再好一点 图 14第四周期图像 我们可能已经停止在上一个周期但这是第五个也是最后一个周期 图 15第五周期图像 到目前为止我们已经看到了自编码器的不同实现以及改进版本。但是将此技术应用于 MNIST 数据集并不能说明其真正的力量。因此现在是时候看到一个更现实的问题我们可以应用自编码器技术。 自编码器和欺诈分析 银行保险公司和信用合作社等金融公司的欺诈检测和预防是一项重要任务。到目前为止我们已经看到如何以及在何处使用深度神经网络DNN 和卷积神经网络CNN。 现在是时候使用其他无监督学习算法如自编码器。在本节中我们将探索信用卡交易的数据集并尝试构建一个无监督的机器学习模型该模型能够判断特定交易是欺诈性的还是真实的。 更具体地说我们将使用自编码器预先训练分类模型并应用异常检测技术来预测可能的欺诈。在开始之前我们需要知道数据集。 数据集的描述 对于这个例子我们将使用来自 Kaggle 的信用卡欺诈检测数据集。数据集可以从此链接下载。由于我使用的是数据集因此引用以下出版物时最好是透明的 Andrea Dal PozzoloOlivier CaelenReid A. Johnson 和 Gianluca Bontempi。用不平衡分类的欠采样校准概率。在计算智能和数据挖掘研讨会CIDMIEEE2015 年。 该数据集包含 2013 年 9 月欧洲信用卡持有人在两天内进行的交易。共有 285,299 笔交易只有 492 笔欺诈这意味着数据集非常不平衡。正类欺诈占所有交易的 0.172%。 数据集包含数字输入变量这些变量是 PCA 转换的结果。遗憾的是由于机密性问题我们无法提供有关数据的原始特征和更多背景信息。有 28 个特征即V1V2… V27它们是使用 PCA 获得的主要成分除了Time和Amount特征。Class特征是响应变量在欺诈情况下取值1否则取0。 还有两个附加特征Time和Amount。Time列表示每笔交易与第一笔交易之间的时间以秒为单位而Amount列表示此交易中转账的金额。那么让我们看一下输入数据仅显示V1V2V26和V27如图 16 所示 图 16信用卡欺诈检测数据集的快照 问题描述 对于这个例子我们将使用自编码器作为无监督的特征学习算法该算法学习和概括训练数据共享的公共模式。在重建阶段对于具有异常模式的数据点RMSE 将更高。因此这些数据点是异常值或异常值。我们的假设是异常也等于我们所追求的欺诈性交易。 现在在评估步骤中我们可以根据验证数据选择 RMSE 的阈值并将 RMSE 高于阈值的所有数据标记为欺诈。或者如果我们认为 0.1% 的交易都是欺诈性的我们也可以根据每个数据点即 RMSE的重建误差对数据进行排名然后选择前 0.1% 为欺诈性交易。 给定类不平衡比建议使用精确回忆曲线下面积AUPRC测量精度 因为混淆矩阵精度在不平衡分类中没有意义。在这种情况下使用线性机器学习模型例如随机森林逻辑回归或支持向量机通过应用上下采样技术将是一个更好的主意。或者我们可以尝试查找数据中的异常因为我们假设数据集中只有少数欺诈案例即异常。 在处理如此严重的响应标签不平衡时我们在测量模型表现时也需要小心。只有少数欺诈性实例因此将一切预测为非欺诈的模型将达到 99% 以上的准确率。然而尽管它们具有高精度但线性 ML 模型甚至是树组合并不一定能帮助我们找到欺诈性案例。 对于这个例子我们将构建一个无监督的模型该模型将接受正面和负面数据欺诈和非欺诈的训练但不提供标签。由于我们有比欺诈更多的正常交易我们应该期望该模型在训练后学习和记忆正常交易的模式并且该模型应该能够为任何异常交易给出分数。 这种无监督的训练对于此目的非常有用因为我们没有足够的标记数据。那么让我们开始吧。 探索性数据分析 在我们实现模型之前探索数据集将提供一些见解。我们首先导入所需的包和模块包括此示例所需的其他包和模块 import pandas as pd import numpy as np import tensorflow as tf import os from datetime import datetime from sklearn.metrics import roc_auc_score as auc import seaborn as sns # for statistical data visualization import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec提示 安装 seaborn 您可以通过多种方式安装seaborn这是一个用于统计数据可视化的 Python 模块 $ sudo pip install seaborn # for Python 2.7 $ sudo pip3 install seaborn # for Python 3.x $ sudo conda install seaborn # using conda # Directly from GitHub (use pip for Python 2.7) $ pip3 install githttps://github.com/mwaskom/seaborn.git 现在我假设您已经从上述 URL 下载了数据集。下载附带一个名为creditcard.csv的 CSV 文件。 接下来让我们阅读数据集并创建一个 pandas DataFrame df pd.read_csv(creditcard.csv) print(df.shape)(284807, 31)因此数据集具有关于 300,000 个事务30 个特征和两个二元标签即 0/1。现在让我们看一下列名及其数据类型 print(df.columns)Index([Time, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, Amount, Class],dtypeobject)print(df.dtypes)Time float64 V1 float64 V2 float64 V3 float64 … V25 float64 V26 float64 V27 float64 V28 float64 Amount float64Class int64现在让我们来看看数据集 print(df.head())图 17数据集的快照 现在让我们看看所有交易的时间跨度 print(Total time spanning: {:.1f} days.format(df[Time].max() / (3600 * 24.0)))Total time spanning: 2.0 days现在让我们看看这些类的统计信息 print({:.3f} % of all transactions are fraud. .format(np.sum(df[Class]) / df.shape[0] * 100))0.173 % of all transactions are fraud.因此我们只有少数欺诈交易。这在文献中也被称为罕见事件检测并且意味着数据集高度不平衡。现在让我们绘制前五个特征的直方图 plt.figure(figsize(12,5*4)) gs gridspec.GridSpec(5, 1) for i, cn in enumerate(df.columns[:5]):ax plt.subplot(gs[i])sns.distplot(df[cn][df.Class 1], bins50)sns.distplot(df[cn][df.Class 0], bins50)ax.set_xlabel()ax.set_title(histogram of feature: str(cn)) plt.show()图 18显示前五个特征的直方图 在前面的屏幕截图中可以看出所有特征都是正偏差或负偏斜。此外数据集没有很多特征因此修剪尾部会丢失重要的信息。所以暂时让我们尽量不这样做并使用所有特征。 训练验证和测试集准备 让我们通过将数据分成训练开发也称为验证和测试集来开始训练。我们首先使用 80% 的数据作为训练和验证集。剩余的 20% 将用作测试集 TEST_RATIO 0.20 df.sort_values(Time, inplace True) TRA_INDEX int((1-TEST_RATIO) * df.shape[0]) train_x df.iloc[:TRA_INDEX, 1:-2].values train_y df.iloc[:TRA_INDEX, -1].values test_x df.iloc[TRA_INDEX:, 1:-2].values test_y df.iloc[TRA_INDEX:, -1].values现在让我们对前面的分裂进行统计 print(Total train examples: {}, total fraud cases: {}, equal to {:.5f} % of total cases. .format(train_x.shape[0], np.sum(train_y), (np.sum(train_y)/train_x.shape[0])*100))print(Total test examples: {}, total fraud cases: {}, equal to {:.5f} % of total cases. .format(test_x.shape[0], np.sum(test_y), (np.sum(test_y)/test_y.shape[0])*100)) Total train examples: 227845, total fraud cases: 417, equal to 0.18302 % of total cases. Total test examples: 56962, total fraud cases: 75, equal to 0.13167 % of total cases.归一化 为了获得更好的预测准确率我们可以考虑两种类型的标准化z 得分和 min-max 缩放 Z 得分这将每列归一化为零均值并将其标准化。这特别适用于激活函数例如 tanh其输出零两侧的值。其次这将留下极端值因此在正常化之后会有一些极端。在这种情况下这可能对检测异常值很有用。最小 - 最大缩放这确保所有值都在 0 和 1 之间即正数。如果我们使用 sigmoid 作为输出激活这是默认方法。 我们使用验证集来决定数据标准化方法和激活函数。根据实验我们发现当与 z 得分标准化一起使用时tanh 的表现略好于 sigmoid。因此我们选择了 tanh然后是 z 得分 cols_mean [] cols_std []for c in range(train_x.shape[1]):cols_mean.append(train_x[:,c].mean())cols_std.append(train_x[:,c].std())train_x[:, c] (train_x[:, c] - cols_mean[-1]) / cols_std[-1]test_x[:, c] (test_x[:, c] - cols_mean[-1]) / cols_std[-1]作为无监督特征学习算法的自编码器 在本小节中我们将看到如何使用自编码器作为无监督的特征学习算法。首先让我们初始化网络超参数 learning_rate 0.001 training_epochs 1000 batch_size 256 display_step 10 n_hidden_1 15 # number of neurons is the num features n_input train_x.shape[1]由于第一层和第二层分别包含 15 和 5 个神经元我们正在构建这样的架构网络28输入- 15 - 5 - 15 - 28输出。那么让我们构建我们的自编码器网络。 让我们创建一个 TensorFlow 占位符来保存输入 X tf.placeholder(float, [None, n_input])现在我们必须使用随机初始化创建偏差和权重向量 weights {encoder_h1: tf.Variable\(tf.random_normal([n_input, n_hidden_1])),decoder_h1: tf.Variable\(tf.random_normal([n_hidden_1, n_input])), } biases {encoder_b1: tf.Variable(tf.random_normal([n_hidden_1])),decoder_b1: tf.Variable(tf.random_normal([n_input])), }现在我们构建一个简单的自编码器。这里我们有encoder()函数它构造了编码器。我们使用tanh函数对隐藏层进行编码如下所示 def encoder(x):layer_1 tf.nn.tanh(tf.add\(tf.matmul(x, weights[encoder_h1]),\biases[encoder_b1]))return layer_1这是decoder()函数它构造了解码器。我们使用tanh函数解码隐藏层如下所示 def decoder(x):layer_1 tf.nn.tanh(tf.add\(tf.matmul(x, weights[decoder_h1]),\biases[decoder_b1]))return layer_1之后我们通过传递输入数据的 TensorFlow 占位符来构建模型。权重和偏置NN 的W和b包含我们将学习优化的网络的所有参数如下所示 encoder_op encoder(X) decoder_op decoder(encoder_op)一旦我们构建了自编码器网络就可以进行预测其中目标是输入数据 y_pred decoder_op y_true X现在我们已经进行了预测现在是时候定义batch_mse来评估表现 batch_mse tf.reduce_mean(tf.pow(y_true - y_pred, 2), 1)注意 未观测值的均方误差MSE是平方误差或偏差的平均值。从统计学的角度来看 它是估计量质量的度量它总是非负的接近于零的值更好。 如果Y^是 n 个预测的向量并且Y是被预测变量的观测值的向量则预测变量的样本内 MSE 计算如下 因此MSE 是误差平方(Y[i] - Y^[i])^2的平均值(1/n ∑[i])。 我们在这里有另一个batch_mse将返回批量中所有输入数据的 RMSE这是一个长度等于输入数据中行数的向量。如果您想要输入无论是训练验证还是测试数据这些将是预测值或欺诈分数我们可以在预测后提取出来。然后我们定义损失和优化器并最小化平方误差 cost_op tf.reduce_mean(tf.pow(y_true - y_pred, 2)) optimizer tf.train.RMSPropOptimizer(learning_rate).minimize(cost_op)每层所用的激活函数是tanh。这里的目标函数或成本测量一批中预测和输入数组的总 RMSE这意味着它是一个标量。然后每次我们想要进行批量更新时我们都会运行优化器。 太棒了我们准备开始训练了。但是在此之前让我们定义保存训练模型的路径 save_model os.path.join(data_dir, autoencoder_model.ckpt) saver tf.train.Saver()到目前为止我们已经定义了许多变量以及超参数因此我们必须初始化变量 init_op tf.global_variables_initializer()最后我们开始训练。我们在训练周期中循环所有批次。然后我们运行优化操作和成本操作来获得损失值。然后我们显示每个周期步骤的日志。最后我们保存训练有素的模型 epoch_list [] loss_list [] train_auc_list [] data_dir Training_logs/ with tf.Session() as sess:now datetime.now()sess.run(init_op)total_batch int(train_x.shape[0]/batch_size)# Training cyclefor epoch in range(training_epochs):# Loop over all batchesfor i in range(total_batch):batch_idx np.random.choice(train_x.shape[0],\batch_size)batch_xs train_x[batch_idx]# Run optimization op (backprop) and# cost op (to get loss value)_, c sess.run([optimizer, cost_op],\feed_dict{X: batch_xs})# Display logs per epoch stepif epoch % display_step 0:train_batch_mse sess.run(batch_mse,\feed_dict{X: train_x})epoch_list.append(epoch1)loss_list.append(c)train_auc_list.append(auc(train_y, train_batch_mse))print(Epoch:, %04d, % (epoch1),cost, {:.9f},.format(c),Train auc, {:.6f},.format(auc(train_y, \train_batch_mse)),print(Optimization Finished!)save_path saver.save(sess, save_model)print(Model saved in: %s % save_path)save_model os.path.join(data_dir, autoencoder_model_1L.ckpt) saver tf.train.Saver()前面的代码段很简单。每次我们从train_x中随机抽取 256 个小批量的小批量作为X的输入将其输入模型并运行优化器通过随机梯度下降更新参数 SGD Epoch: 0001, cost 0.938937187, Train auc 0.951383 Epoch: 0011, cost 0.491790086, Train auc 0.954131 … Epoch: 0981, cost 0.323749095, Train auc 0.953185 Epoch: 0991, cost 0.255667418, Train auc 0.953107 Optimization Finished! Model saved in: Training_logs/autoencoder_model.ckpt Test auc score: 0.947296我们在train_x上的估值得出的 AUC 得分约为 0.95。然而从前面的日志中很难理解训练的进展情况 # Plot Training AUC over time plt.plot(epoch_list, train_auc_list, k--, labelTraining AUC, linewidth1.0) plt.title(Training AUC per iteration) plt.xlabel(Iteration) plt.ylabel(Training AUC) plt.legend(locupper right) plt.grid(True)# Plot train loss over time plt.plot(epoch_list, loss_list, r--, labelTraining loss, linewidth1.0) plt.title(Training loss) plt.xlabel(Iteration) plt.ylabel(Loss) plt.legend(locupper right) plt.grid(True) plt.show()图 19每次迭代的训练损失和 AUC 在上图中我们可以看到训练误差有点颠簸但训练 AUC 几乎保持稳定约为 95%。这可能听起来很可疑。您还可以看到我们使用相同的数据进行训练和验证。这可能听起来很混乱但等等 由于我们正在进行无监督的训练并且模型在训练期间从未看到标签因此不会导致过拟合。此附加验证用于监视早期停止以及超参数调整。 评估模型 在训练完我们的自编码器模型和超参数后我们可以在 20% 测试数据集上评估其表现如下所示 save_model os.path.join(data_dir, autoencoder_model.ckpt) saver tf.train.Saver()# Initializing the variables init tf.global_variables_initializer()with tf.Session() as sess:now datetime.now()saver.restore(sess, save_model)test_batch_mse sess.run(batch_mse, feed_dict{X: test_x})print(Test auc score: {:.6f}.format(auc(test_y, \test_batch_mse)))在此代码中我们重用了之前制作的训练模型。test_batch_mse是我们测试数据的欺诈分数 Test auc score: 0.948843太棒了我们训练有素的模型被证明是一个高度准确的模型显示 AUC 约为 95%。现在我们已经看到了评估一些可视化分析会很棒。你们觉得怎么样让我们绘制非欺诈案件的欺诈分数MSE分布图。以下代码段执行此操作 plt.hist(test_batch_mse[test_y 0.0], bins 100) plt.title(Fraud score (mse) distribution for non-fraud cases) plt.xlabel(Fraud score (mse)) plt.show()图 20非欺诈案件的 MSE 欺诈评分 前面的屏幕截图是不可理解的所以让我们将其缩放到(0, 30)范围并再次绘制图形 # Zoom into (0, 30) range plt.hist(test_batch_mse[(test_y 0.0) (test_batch_mse 30)], bins 100) plt.title(Fraud score (mse) distribution for non-fraud cases) plt.xlabel(Fraud score (mse)) plt.show()[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4YQOSeAB-1681565849711)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-tf-2e-zh/img/B09698_05_21.jpg)] 图 21非欺诈案件的 MSE 欺诈评分放大到(0, 30)范围 现在让我们只显示欺诈类 # Display only fraud classes plt.hist(test_batch_mse[test_y 1.0], bins 100)plt.title(Fraud score (mse) distribution for fraud cases) plt.xlabel(Fraud score (mse)) plt.show()图 22欺诈案件的 MSE 欺诈评分 最后让我们看一下一些相关统计数据。例如我们使用10作为检测阈值。现在我们可以计算高于阈值的检测到的病例数高于阈值的阳性病例数高于阈值的准确率百分比即精确度并将其与测试集中欺诈的平均百分比进行比较 threshold 10 print(Number of detected cases above threshold: {}, \n\ Number of pos cases only above threshold: {}, \n\ The percentage of accuracy above threshold (Precision): {:0.2f}%. \n\ Compared to the average percentage of fraud in test set: 0.132%.format( \ np.sum(test_batch_mse threshold), \ np.sum(test_y[test_batch_mse threshold]), \ np.sum(test_y[test_batch_mse threshold]) / np.sum(test_batch_mse threshold) * 100))Number of detected cases above threshold: 198, Number of positive cases only above threshold: 18, The percentage of accuracy above threshold (Precision): 9.09%. Compared to the average percentage of fraud in test set: 0.132%总而言之对于我们的案例只有一个隐藏层的自编码器足够至少用于训练。但是您仍然可以尝试采用其他变体例如解卷积自编码器和去噪自编码器来解决同样的问题。 总结 在本章中我们实现了一些称为自编码器的优化网络。自编码器基本上是数据压缩网络模型。它用于将给定输入编码为较小维度的表示然后可以使用解码器从编码版本重建输入。我们实现的所有自编码器都包含编码和解码部分。 我们还看到了如何通过在网络训练期间引入噪声和构建去噪自编码器来提高自编码器的表现。最后我们应用第 4 章中介绍的 CNN 概念卷积神经网络上的 TensorFlow 和卷积自编码器的实现。 即使隐藏单元的数量很大我们仍然可以通过在网络上施加其他约束来使用自编码器发现数据集的有趣和隐藏结构。换句话说如果我们对隐藏单元施加稀疏约束那么即使隐藏单元的数量很大自编码器仍将在数据中发现有趣的结构。为了证明这一观点我们看到了一个真实的例子即信用卡欺诈分析我们成功应用了自编码器。 循环神经网络RNN是一类人工神经网络其中单元之间的连接形成有向循环。 RNN 利用过去的信息如时间序列预测。这样他们就可以对具有高时间依赖性的数据进行预测。这创建了网络的内部状态允许它展示动态时间行为。 在下一章中我们将研究 RNN。我们将首先描述这些网络的基本原理然后我们将实现这些架构的一些有趣示例。
http://www.tj-hxxt.cn/news/220038.html

相关文章:

  • 河南网站公司com网站是用什么做的
  • 伪装学渣无极网站公司网站建设的申请
  • 总结企业网站建设的流程手机做免费个人网站
  • 中宁建设局网站中国十大奇迹工程
  • 做网站百度收费吗汕头制作网站软件
  • 网站建设需要些什么设备wordpress投稿
  • 网站建设数据处理wordpress添加返回顶部
  • 建网站要多少钱一台大宗商品采购平台
  • 漂亮企业网站厦门专业网站设计
  • 网站建设扬州校园网站建设途径
  • 网站设计一般是什么专业网络营销理论有哪些内容
  • 文化馆网站建设方案静宁县门户网
  • 外贸怎么建立自己的网站wordpress 机械
  • 做网站诱导充值犯法吗wordpress中文免费企业模板下载
  • 番禺大石做网站地方网站总结
  • 域名备案要多久seo外链工具软件
  • 制作微信网站模板网站建设公司如何规避风险
  • 室内设计网站大全网外贸企业做网站
  • 网站 asp.net php上海高端网站建设服务公
  • 宝安网站建设制作windows优化大师收费
  • 惠普电脑网站建设策划方案做学校后台网站用什么浏览器
  • 自己做游戏app的网站网站网站做维护
  • 网站 蓝色广告策划案优秀案例
  • 有很多长尾怎么做网站内容花都网站建设设计
  • .net 网站开发书籍wordpress主题美化
  • 网络推广就是做网站吗网站seo方案撰写
  • wordpress新闻资讯模块如何使用推广优化工具
  • 垦利网站制作张家口手机台app下载
  • 目前网站开发怎么兼顾手机广西南宁网站建设排行榜
  • 爱站建没工程信息网