TensorFlow-和-Keras-应用研讨会-全-
TensorFlow 和 Keras 应用研讨会(全)
原文:
annas-archive.org/md5/9b4f768de6a25886fb0e3fe70a17bf09译者:飞龙
前言
关于本书
机器学习赋予计算机像人类一样学习的能力。它正以多种形式逐渐对各行业产生变革性影响,成为为未来数字经济做准备的关键技能。
作为初学者,通过学习所需的技巧,你将开启一个机会的世界,能够在机器学习、深度学习和现代数据分析领域做出贡献,使用最新的尖端工具。
应用 TensorFlow 和 Keras 工作坊首先向你展示神经网络的工作原理。在你理解了基础知识后,你将通过调整超参数训练一些网络。为了进一步提高技能,你将学习如何选择最合适的模型来解决当前问题。在处理高级概念时,你将发现如何通过将构建基本深度学习系统所需的所有关键元素——数据、模型和预测——结合起来,来组装一个深度学习系统。最后,你将探索如何评估模型的表现,并通过模型评估和超参数优化等技术来改进模型。
在本书结束时,你将学会如何构建一个预测未来价格的比特币应用程序,并能够为其他项目构建自己的模型。
读者群体
如果你是一名数据科学家,或者是机器学习和深度学习的爱好者,正在寻求将 TensorFlow 和 Keras 模型设计、训练并部署到实际应用中,那么这个工作坊适合你。了解计算机科学和机器学习的概念,以及具备数据分析经验,将帮助你轻松理解本书中解释的主题。
关于各章节
第一章,神经网络和深度学习简介,引导我们通过使用 TensorBoard 选择一个经过 TensorFlow 训练的神经网络,并通过变化的迭代次数和学习率训练神经网络。这将为你提供如何训练一个高性能神经网络的实践经验,并让你探索其一些局限性。
第二章,现实世界中的深度学习:预测比特币价格,教我们如何从数据输入到预测,组装一个完整的深度学习系统。创建的模型将作为基准,我们可以从中进行改进。
第三章,现实世界中的深度学习:评估比特币模型,重点讲解如何评估神经网络模型。通过超参数调优,我们将提高网络的表现。然而,在改变任何参数之前,我们需要先衡量模型的表现。在本章结束时,你将能够使用不同的功能和技术来评估一个模型。
第四章,产品化,讲解了如何处理新数据。我们将创建一个能够从展示给它的模式中不断学习并做出更好预测的模型。我们将使用一个 Web 应用程序作为示例,展示如何部署深度学习模型。
约定
文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 句柄如下所示:
“在激活虚拟环境后,确保通过对 requirements.txt 文件执行 pip 来安装正确的组件。”
屏幕上看到的词汇(例如在菜单或对话框中)以相同的格式出现。
一块代码的设置如下:
$ python -m venv venv
$ venv/bin/activate
新术语和重要单词如下所示:
“在机器学习中,通常会定义两个不同的术语:参数 和 超参数。”
代码展示
跨多行的代码使用反斜杠( \ )分割。当代码执行时,Python 会忽略反斜杠,并将下一行的代码视为当前行的直接延续。
例如:
history = model.fit(X, y, epochs=100, batch_size=5, verbose=1, \
validation_split=0.2, shuffle=False)
注释是添加到代码中的,以帮助解释特定的逻辑。单行注释使用 # 符号,如下所示:
# Print the sizes of the dataset
print("Number of Examples in the Dataset = ", X.shape[0])
print("Number of Features for each example = ", X.shape[1])
多行注释使用三引号括起来,如下所示:
"""
Define a seed for the random number generator to ensure the
result will be reproducible
"""
seed = 1
np.random.seed(seed)
random.set_seed(seed)
设置您的环境
在我们详细探讨本书之前,我们需要设置特定的软件和工具。在接下来的部分,我们将展示如何做到这一点。
安装
以下部分将帮助您在 Windows、macOS 和 Linux 系统上安装 Python。
在 Windows 上安装 Python
Python 在 Windows 上的安装方法如下:
-
确保从官方安装页面的下载页
www.anaconda.com/distribution/#windows选择 Python 3.7(与 TensorFlow 2.0 兼容)。 -
确保安装适合您计算机系统的正确架构;即 32 位或 64 位。您可以在操作系统的 系统属性 窗口中找到此信息。
-
下载完安装程序后,只需双击该文件并按照屏幕上的用户友好提示进行操作。
在 Linux 上安装 Python
要在 Linux 上安装 Python,您有几个不错的选择;即命令提示符和 Anaconda。
如下使用命令提示符:
-
打开命令提示符并通过运行
python3 --version验证p\Python 3是否已安装。 -
要安装 Python 3,请运行以下命令:
sudo apt-get update sudo apt-get install python3.7 -
如果遇到问题,网上有许多资源可以帮助您排查问题。
或者,您可以通过从 www.anaconda.com/distribution/#linux 下载安装程序并按照说明进行操作来安装 Anaconda Linux。
在 macOS 上安装 Python
与 Linux 类似,您在 Mac 上安装 Python 有几种方法。安装 Python 在 macOS X 上的一种方法如下:
-
通过按 CMD + Spacebar 打开 Mac 的终端,在打开的搜索框中输入
terminal,然后按 Enter。 -
通过运行
xcode-select --install在命令行安装 Xcode。 -
安装 Python 3 的最简单方法是使用 Homebrew,可以通过命令行运行
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"来安装。 -
将 Homebrew 添加到你的
$PATH环境变量中。通过运行sudo nano ~/.profile打开命令行中的配置文件,并在文件底部插入export PATH="/usr/local/opt/python/libexec/bin:$PATH"。 -
最后一步是安装 Python。在命令行中运行
brew install python。
你也可以通过 Anaconda 安装程序来安装 Python,安装包可以从www.anaconda.com/distribution/#macos下载。
安装 pip
通过 Anaconda 安装程序安装 Python 时,pip(Python 的包管理器)会预安装。但是,如果你是直接安装的 Python,你需要手动安装pip。安装 pip 的步骤如下:
-
访问
bootstrap.pypa.io/get-pip.py并将文件保存为get-pip.py。 -
访问你保存
get-pip.py的文件夹。在该文件夹中打开命令行(Linux 用户使用 Bash,Mac 用户使用 Terminal)。 -
在命令行中执行以下命令:
python get-pip.py -
请注意,在执行此命令之前,你应该已安装 Python。
-
一旦
pip安装完成,你可以安装所需的库。要安装 pandas,只需执行pip install pandas。要安装特定版本的库,例如 pandas 的 0.24.2 版本,可以执行pip install pandas=0.24.2。
Jupyter Notebook
如果你没有通过 Anaconda 安装程序安装 Python,你需要手动安装 Jupyter。请参考jupyter.readthedocs.io/en/latest/install.html#id4中经验丰富的 Python 用户的替代方法:使用 pip 安装 Jupyter 部分。
JupyterLab
Anaconda 分发版包含 JupyterLab,它允许你运行 Jupyter Notebooks。Jupyter Notebooks 可以通过浏览器访问,并允许你在集成环境中交互式地运行代码,以及嵌入图像和文本。
安装库
pip 已随 Anaconda 一起预安装。一旦 Anaconda 安装到你的计算机上,所有必需的库都可以使用pip安装,例如,pip install numpy。或者,你也可以使用pip install –r requirements.txt安装所有必需的库。你可以在packt.live/3haRJp0找到requirements.txt文件。
练习和活动将在 Jupyter Notebooks 中执行。Jupyter 是一个 Python 库,可以像安装其他 Python 库一样安装 —— 即使用pip install jupyter,幸运的是,Anaconda 已经预装了它。要打开一个 notebook,只需在 Terminal 或命令提示符中运行命令jupyter notebook。
访问代码文件
您可以在packt.live/2DnXRLS找到本书的完整代码文件。您还可以通过使用packt.live/39dH7ml上的互动实验环境,直接在网页浏览器中运行许多活动和练习。
我们已尽力支持所有活动和练习的互动版本,但我们仍然推荐进行本地安装,以防在某些情况下无法使用此支持。
如果您在安装过程中遇到任何问题或有任何疑问,请通过电子邮件联系我们 workshops@packt.com。
第一章:1. 神经网络与深度学习简介
概述
本章将介绍神经网络的基础知识,以及如何设置深度学习编程环境。我们还将探讨神经网络的常见组件和基本操作。最后,我们将通过使用 TensorFlow 创建的一个训练过的神经网络进行探索。通过本章学习结束时,您将能够训练一个神经网络。
简介
本章旨在帮助理解神经网络能做什么,而不是深入探讨深度学习的具体原理。因此,我们不会涉及深度学习算法背后的数学概念,而是将描述构成深度学习系统的基本部分,以及神经网络在该系统中的作用。我们还将通过实例展示神经网络如何利用这些算法解决实际问题。
本章的核心是挑战您将问题看作是思想的数学表示。到本章结束时,您将能够将问题看作这些表示的集合,并识别深度学习算法如何学习这些表示。
什么是神经网络?
神经网络是由神经元组成的网络。在我们的大脑中,有一个由数十亿神经元相互连接的网络。神经元是神经系统的基本元素之一。神经元的主要功能是对事件做出反应并向其他神经元传递信息。在这种情况下,神经元的反应就是简单地激活或去激活自身。受到大脑设计的启发,人工神经网络最早由麻省理工学院的教授沃伦·麦卡洛克和沃尔特·皮茨在 1940 年代提出。
注意
如需了解更多关于神经网络的信息,请参阅《解释:神经网络》MIT 新闻办公室,2017 年 4 月 14 日,可访问news.mit.edu/2017/explained-neural-networks-deep-learning-0414。
受到神经科学进展的启发,他们提出了创建一个计算机系统来再现大脑(无论是人类的大脑还是其他大脑)的工作原理。其核心思想是构建一个作为相互连接的网络工作的计算机系统,也就是说,一个由许多简单组件组成的系统。这些组件解读数据,并相互影响如何解读这些数据。这个核心思想至今仍然存在。
深度学习在很大程度上被认为是神经网络的现代研究。可以把它看作是神经网络的现代名称。主要的区别在于,深度学习中使用的神经网络通常要大得多,意味着它们比早期的神经网络包含更多的节点和层次。深度学习算法和应用通常需要大量资源才能成功,因此使用了深度这个词来强调其规模以及大量相互连接的组件。
神经网络的成功应用
自 1940 年代神经网络诞生以来,它们一直以某种形式存在于研究中。直到最近,深度学习系统才成功地应用于大规模工业应用。
现代神经网络的支持者在语音识别、语言翻译、图像分类等领域取得了巨大成功。其当前的突出地位得益于可用计算能力的大幅增加,以及图形处理单元(GPU)和张量处理单元(TPU)的出现,这些处理单元能够执行比常规 CPU 更多的并行数学运算,并且有更多的数据可用。与 CPU 相比,GPU 专门设计用来执行特定任务(在“单指令,多线程”模式下),其执行可以并行化。
其中一个成功案例是不同 AlphaGo 算法的功耗。AlphaGo是 DeepMind 发起的一个项目,旨在开发一系列算法来击败围棋游戏。它被视为深度学习能力的典型例子。DeepMind 的团队利用强化学习实现了这一目标,其中 AlphaGo 成为了自己的老师。
这个最初一无所知的神经网络通过与自己对弈,了解哪些棋步能带来胜利。所使用的算法利用了 TPU 进行训练。TPU 是谷歌开发的一种芯片组,专门用于深度学习程序。文章《Alpha Zero: 从零开始》,deepmind.com/blog/alphago-zero-learning-scratch/,描述了用于训练不同版本 AlphaGo 算法的 GPU 和 TPU 的数量。
注意
在本书中,我们将不使用 GPU 来完成我们的任务。神经网络的工作不需要 GPU。在一些简单的例子中——如本书中提供的——所有计算都可以通过一台普通笔记本的 CPU 来完成。然而,在处理非常大的数据集时,GPU 可以提供很大帮助,因为训练一个神经网络所需的时间,否则将变得不切实际。
以下是神经网络在多个领域产生重大影响的几个例子:
文本翻译:2017 年,谷歌宣布发布了一种新的翻译服务算法,名为 Transformer。该算法由一种称为 长短期记忆(LSTM)的递归神经网络组成,该网络经过训练以使用双语文本。LSTM 是一种应用于文本数据的神经网络形式。谷歌展示了该算法与行业标准 双语评估指标(BLEU)相比,具有显著的准确性,并且在计算上也非常高效。BLEU 是一种评估机器翻译文本性能的算法。有关更多信息,请参阅谷歌研究博客,Transformer:一种新型神经网络架构用于语言理解,2017 年 8 月 31 日,可在 research.googleblog.com/2017/08/transformer-novel-neural-network.html 查阅。
自动驾驶车辆:据认为,Uber、NVIDIA 和 Waymo 正在使用深度学习模型来控制与驾驶相关的不同车辆功能。每家公司都在研究多种可能性,包括使用人类进行训练、在虚拟环境中模拟车辆驾驶,甚至创建一个类似小城市的环境,在其中基于预期和非预期事件对车辆进行训练。
注意事项
若要了解每项成就的详细信息,请参阅以下参考资料。
Uber:Uber 的新 AI 团队正在寻找通往自动驾驶汽车的最短路线,Dave Gershgorn,Quartz,2016 年 12 月 5 日,可在 qz.com/853236/ubers-new-ai-team-is-looking-for-the-shortest-route-to-self-driving-cars/ 查阅。
NVIDIA:自动驾驶汽车的端到端深度学习,2016 年 8 月 17 日,可在 devblogs.nvidia.com/deep-learning-self-driving-cars/ 查阅。
Waymo:深入了解 Waymo 为训练自动驾驶汽车而建立的秘密世界,《大西洋月刊》,Alexis C. Madrigal,2017 年 8 月 23 日,可在 www.theatlantic.com/technology/archive/2017/08/inside-waymos-secret-testing-and-simulation-facilities/537648/ 查阅。
图像识别:Facebook 和谷歌使用深度学习模型来识别图像中的实体,并自动将这些实体标记为来自联系人列表中的人物。在这两种情况下,网络都通过之前已标记的图像以及目标朋友或联系人的图像进行训练。两家公司报告称,在大多数情况下,这些模型能够高效且准确地建议朋友或联系人。
尽管其他行业还有许多例子,深度学习模型的应用仍处于起步阶段。许多成功的应用还未出现,包括你所创造的那些。
为什么神经网络效果如此好?
为什么神经网络如此强大?神经网络之所以强大,是因为它们可以用来预测任何给定函数的合理逼近。如果我们能将一个问题表示为一个数学函数,并且我们有正确表示该函数的数据,那么只要有足够的资源,深度学习模型就能逼近这个函数。这通常被称为通用逼近定理。更多信息,请参考 Michael Nielsen 的《神经网络与深度学习:神经网络可以计算任何函数的视觉证明》,该书可在以下网址找到:neuralnetworksanddeeplearning.com/chap4.html。
本书中我们不会探讨通用性原理的数学证明。然而,神经网络的两个特征应该能给你正确的直觉,帮助你理解这一原理:表示学习和函数逼近。
注意
更多信息,请参考 深度强化学习简要概述,Kai Arulkumaran、Marc Peter Deisenroth、Miles Brundage 和 Anil Anthony Bharath,arXiv,2017 年 9 月 28 日,可在以下网址找到:www.arxiv-vanity.com/papers/1708.05866/。
表示学习
用于训练神经网络的数据包含表示(也称为特征),它们解释了你试图解决的问题。例如,如果我们有兴趣从图像中识别人脸,那么从包含人脸的一组图像中,每个像素的颜色值将作为起点。然后,模型会通过将像素组合在一起来不断学习更高层次的表示,直到完成训练过程。这里展示了一个图示:

图 1.1:基于输入数据的一系列更高层次的表示
注意
图 1.1 是基于 Yann LeCun、Yoshua Bengio 和 Geoffrey Hinton 在《深度学习》一书中所提供的原始图像制作的派生图像,该书发表于 Nature, 521, 436–444 (2015 年 5 月 28 日) doi:10.1038/ nature14539。你可以在以下网址找到该论文:www.nature.com/articles/nature14539。
用正式的语言来说,神经网络是计算图,其中每一步都会根据输入数据计算出更高层次的抽象表示。每一步表示进入一个不同的抽象层次。数据通过这些层次进展,从而构建出更高层次的表示。该过程最终会完成最高层次的表示:模型试图预测的结果。
函数逼近
当神经网络学习数据的新表示时,它们通过将不同层次的神经元与权重和偏置相结合来进行学习。在每次训练周期中,它们都会使用一种叫做反向传播的数学方法调整这些连接的权重。每一轮中,权重和偏置都会得到改进,直到达到一个最优解。这意味着神经网络可以测量每次训练周期中它的错误程度,调整每个神经元的权重和偏置,并再次尝试。如果它确定某种修改比前一轮的结果更好,它会继续投资于这个修改,直到达到最优解。
基本上,在一个周期中,发生了三件事。首先是前向传播,我们使用权重、偏置和输入来计算结果。第二步,我们使用损失函数计算计算值与期望值之间的差距。最后一步是更新权重和偏置,朝着与前向传播相反的方向调整,这个过程叫做反向传播。
由于早期层次的权重和偏置与后期层次没有直接联系,我们使用一个叫做链式法则的数学工具来计算早期层次的新权重。基本上,早期层次的变化等于其下方所有层次的梯度或导数的乘积。
简而言之,这个过程是神经网络能够逼近函数的原因。然而,神经网络无法完美预测一个函数的原因有很多,其中最主要的原因包括以下几点:
-
许多函数具有随机特性(即随机性质)。
-
可能会出现对训练数据特性的过拟合。过拟合是指我们训练的模型在面对从未见过的数据时,无法很好地泛化。它只是学习了训练数据,而不是发现一些有趣的模式。
-
可能缺乏训练数据。
在许多实际应用中,简单的神经网络可以以合理的精度逼近一个函数。这类应用将是本书的重点。
深度学习的局限性
深度学习技术最适用于那些可以用正式的数学规则(例如数据表示)来定义的问题。如果一个问题很难用这种方式定义,那么深度学习可能无法提供有效的解决方案。此外,如果给定问题的数据有偏差或仅包含生成该问题的潜在函数的部分表示,深度学习技术将只能复制该问题,而无法学会解决它。
记住,深度学习算法通过学习数据的不同表示来近似给定的函数。如果数据不能恰当地表示一个函数,那么神经网络可能会错误地表示该函数。可以考虑以下类比:你正在尝试预测全国汽油价格(即燃料价格),并创建一个深度学习模型。你将信用卡账单中每日的汽油消费作为输入数据。该模型最终可能学会你的汽油消费模式,但它可能会错误地表示由于其他因素(例如政府政策、市场竞争、国际政治等)引起的汽油价格波动,这些因素在你的数据中只是每周表示一次。最终,当模型投入生产使用时,可能会得出错误的结果。
为了避免这个问题,确保用于训练模型的数据尽可能准确地代表模型试图解决的问题。
固有偏差与伦理考量
研究人员建议,在使用深度学习模型时,如果没有考虑训练数据中的固有偏差,不仅可能导致表现不佳的解决方案,还可能引发伦理上的问题。
例如,2016 年底,中国上海交通大学的研究人员创建了一个神经网络,能够仅通过面部照片正确识别犯罪分子。研究人员使用了 1,856 张中国男性的照片,其中一半是已定罪的。该模型以 89.5%的准确率识别了这些囚犯。
注意
若要了解更多信息,请参考 blog.keras.io/the-limitations-of-deep-learning.html 和 MIT Technology Review. 神经网络通过面部识别犯罪分子,2016 年 11 月 22 日,可在 www.technologyreview.com/2016/11/22/107128/neural-network-learns-to-identify-criminals-by-their-faces/ 阅读。
该论文在科学界和大众媒体中引起了极大轰动。该解决方案的一个关键问题是未能正确识别输入数据中固有的偏差。即,该研究使用的数据来自两个不同的来源:一个是罪犯数据,另一个是非罪犯数据。一些研究人员建议,他们的算法识别的是与研究中使用的不同数据来源相关的模式,而不是识别来自人脸的相关模式。虽然我们可以从技术角度讨论模型的可靠性,但主要的批评是在伦理方面:研究人员应当明确认识到深度学习算法所使用的输入数据中固有的偏见,并考虑其应用会如何影响人们的生活。 Timothy Revell. 当面部识别技术被用来“识别”罪犯时的担忧. 新科学家. 2016 年 12 月 1 日. 访问链接: www.newscientist.com/article/2114900-concerns-as-face-recognition-tech-used-to-identify-criminals/
数据集可能会出现不同类型的偏差。考虑一个例子,假设你正在构建一个可以在白天和夜间都能运行的自动监控系统。如果你的数据集仅包含白天的图像,那么你就会在模型中引入样本偏差。通过包含夜间数据,并覆盖所有可能的不同情况,例如晴天、雨天的图像,可以消除这种偏差。另一个例子是,假设在一个工作场所安装了类似的系统,用于分析工人及其活动。如果你的模型输入了成千上万的例子,图像中男人在编程,女人在做饭,那么这些数据显然反映了刻板印象。解决这个问题的方法与之前相同:将模型暴露于更加均衡分布的数据。
注意
要了解更多关于学习算法(包括深度学习)伦理的话题,请参考由 AI Now Institute 所做的研究(ainowinstitute.org/),该组织致力于理解智能系统的社会影响。
神经网络的常见组件和操作
神经网络有两个关键组件:层和节点。
节点负责特定的操作,层是节点的组合,用于区分系统的不同阶段。典型的神经网络由以下三层组成:
-
输入层:接收并解读输入数据
-
隐藏层:进行计算并在数据传递过程中修改数据
-
输出层:在此处将输出结果组装并进行评估
以下图展示了神经网络层的工作原理:

图 1.2:神经网络中最常见层的示意图
隐藏层是神经网络中最重要的层。它们被称为隐藏层,因为在这些层中生成的表示数据并不直接出现在数据中,而是从数据中学习到的。正是这些层内进行的主要计算构成了神经网络的核心。
节点是数据在网络中表示的位置。节点有两个与之相关的值:偏置和权重。这两个值影响数据在节点中的表示方式,并将数据传递给其他节点。当网络学习时,它实际上会调整这些值,以满足优化函数。
神经网络中的大部分工作发生在隐藏层中。不幸的是,目前没有明确的规则来确定网络应有多少层或节点。在实现神经网络时,你可能会花费时间实验不同的层和节点组合。建议从单层开始,并且使用一个节点数量来反映输入数据的特征数量(也就是说,数据集中有多少列)。
你可以继续添加层和节点,直到达到满意的性能——或者当网络开始对训练数据过拟合时停止。另外,注意这很大程度上依赖于数据集——如果你训练一个模型来识别手写数字,那么两个隐藏层的神经网络就足够了,但如果你的数据集更复杂,比如检测图像中的汽车和救护车等物体,那么即使是 10 层也可能不足以正确识别物体,你需要更深的网络。
同样,如果你使用一个有 100 个隐藏层的网络来训练手写数字,那么很有可能会导致模型过拟合,因为如此复杂的结构对模型来说并非必需。
现代神经网络实践通常局限于实验节点和层的数量(例如,网络的深度)以及每层执行的操作类型。在许多成功的实例中,神经网络仅通过调整这些参数就超越了其他算法。
最开始,想象数据通过输入层进入神经网络系统,然后在网络中从节点到节点流动。数据流动的路径取决于节点之间的连接方式、每个节点的权重和偏置、每层所执行的操作类型以及在这些操作完成后的数据状态。神经网络通常需要多次迭代(或周期),以不断调整节点的权重和偏置,这意味着数据会在图的不同层之间流动多次。
配置深度学习环境
在本章结束之前,我们希望你能够与一个真实的神经网络进行交互。我们将首先介绍本书中使用的主要软件组件,并确保它们正确安装。接着,我们将探索一个预训练的神经网络,并探讨在什么是神经网络?一节中讨论的一些组件和操作。
深度学习的软件组件
我们将使用以下软件组件进行深度学习:
Python 3
本书将使用 Python 3。Python 是一种通用编程语言,在科学界非常流行——因此它在深度学习中得到了广泛采用。本书不支持 Python 2,但可以用它来训练神经网络,代替 Python 3。即使你选择在 Python 2 中实现你的解决方案,也建议迁移到 Python 3,因为其现代化的特性集远比其前身更为强大。
TensorFlow
TensorFlow 是一个用于执行图形形式数学运算的库。TensorFlow 最初由 Google 开发,今天它已经成为一个开源项目,拥有众多贡献者。它的设计是为了神经网络而构建的,也是创建深度学习算法时最受欢迎的选择之一。
TensorFlow 也以其生产组件而著名。它包含 TensorFlow Serving(github.com/tensorflow/serving),这是一个用于服务深度学习模型的高性能系统。此外,训练好的 TensorFlow 模型可以在其他高性能编程语言中使用,如 Java、Go 和 C。这意味着你可以将这些模型部署在从微型计算机(即树莓派)到安卓设备等各种设备上。截止 2019 年 11 月,TensorFlow 2.0 版本是最新版本。
Keras
为了高效地与 TensorFlow 交互,我们将使用 Keras(keras.io/),这是一个具有高级 API 的 Python 包,用于开发神经网络。虽然 TensorFlow 专注于相互交互的计算图中的组件,Keras 则专注于神经网络的开发。Keras 使用 TensorFlow 作为其后台引擎,使得开发此类应用变得更加简便。
截至 2019 年 11 月,Keras 是 TensorFlow 的内置和默认 API。它位于tf.keras命名空间下。
TensorBoard
TensorBoard 是一个数据可视化工具套件,用于探索 TensorFlow 模型,并且与 TensorFlow 原生集成。TensorBoard 通过读取 TensorFlow 在训练神经网络时创建的检查点和摘要文件来工作。这些文件可以在近实时(延迟 30 秒)或训练完成后进行探索。TensorBoard 使得实验和探索神经网络的过程变得更加轻松——而且,跟踪你网络的训练过程是非常激动人心的。
Jupyter Notebook、Pandas 和 NumPy
在使用 Python 创建深度学习模型时,通常会从交互式工作开始;逐步开发一个最终变成更具结构化的软件的模型。在此过程中,经常使用三种 Python 包:Jupyter Notebooks、Pandas 和 NumPy:
-
Jupyter Notebook 创建交互式 Python 会话,使用网页浏览器作为其界面。
-
Pandas 是一个用于数据处理和分析的包。
-
NumPy 常用于数据形状处理和执行数值计算。
这些软件包在本书中偶尔使用。它们通常不构成生产系统的一部分,但在探索数据和开始构建模型时经常使用。我们将更详细地关注其他工具。
注意
Michael Heydt(2017 年 6 月,Packt Publishing)出版的《学习 Pandas》和 Dan Toomey(2016 年 11 月,Packt Publishing)出版的《学习 Jupyter》是两本关于如何使用这些技术的综合指南。它们都可以在www.packtpub.com/big-data-and-business-intelligence/learning-pandas-second-edition和www.packtpub.com/big-data-and-business-intelligence/learning-jupyter-5-second-edition找到。这些书籍是继续学习的好参考。
以下表格详细列出了成功创建本书中解释的深度学习模型所需的软件要求:
![图 1.3:创建深度学习环境所需的软件组件]
图 1.3:创建深度学习环境所需的软件组件
Anaconda 是一个免费的 Python 包分发工具,适用于 Windows、Mac 或其他平台。我们建议您按照docs.anaconda.com/anaconda/install/上的说明进行操作。标准的 Anaconda 安装将安装大多数这些组件,第一个练习将演示如何安装其他组件。
练习 1.01:验证软件组件
在我们探索一个训练好的神经网络之前,让我们先验证所需的软件组件是否齐全。我们已经包括了一个脚本,用来验证这些组件是否工作。让我们花点时间运行脚本并解决可能遇到的问题。我们现在将测试在您的工作环境中是否具备本书所需的软件组件。首先,我们建议使用 Python 的原生模块venv来创建 Python 虚拟环境。虚拟环境用于管理项目依赖关系。我们建议每个项目都有自己的虚拟环境。
-
可以使用以下命令创建 Python 虚拟环境:
$ python -m venv venv $ source venv/bin/activate后面的命令将把字符串
venv附加到命令行的开头。确保在进行项目开发时始终激活你的 Python 虚拟环境。要停用虚拟环境,请运行
$ deactivate。 -
激活虚拟环境后,请通过在
requirements.txt文件上执行pip,确保正确的组件已安装(packt.live/300skHu)。$ pip install –r requirements.txt输出如下:
![图 1.4:终端截图,显示使用 pip 安装 requirements.txt 中的依赖]()
图 1.4:终端截图,显示使用 pip 安装 requirements.txt 中的依赖
-
这将会在虚拟环境中安装本书中使用的库。如果这些库已经存在,它将不会做任何操作。如果库正在安装,将显示进度条,否则会提示“
requirement is already specified”。要检查已安装的可用库,请使用以下命令:$ pip list输出将如下所示:
![图 1.5:终端截图,显示运行 pip 列出可用库]()
图 1.5:终端截图,显示运行 pip 列出可用库
注意
这些库对于本书中的所有代码活动至关重要。
-
作为此练习的最后一步,执行脚本
test_stack.py。该文件可以在以下地址找到:packt.live/2B0JNau。它验证本书所需的所有软件包是否已安装并且可用。 -
运行以下脚本检查 Python 3、TensorFlow 和 Keras 的依赖项是否可用。使用以下命令:
$ python3 Chapter01/Exercise1.01/test_stack.py脚本返回有用的消息,说明已安装的内容以及需要安装的内容:
![图 1.6:终端截图,显示并非所有要求都已安装]()
图 1.6:终端截图,显示并非所有要求都已安装
例如,在前面的截图中,它显示 TensorFlow 2.0 未被检测到,但检测到了 Keras 2.2 或更高版本。因此,你会看到错误消息
Please review software requirements before proceeding to Lesson 2。如果所有要求都已满足,则会显示已安装 Python、TensorFlow 和 Keras,如下图所示:![图 1.7:显示所有元素已安装的终端截图]()
图 1.7:显示所有元素已安装的终端截图
-
在终端中运行以下脚本命令,获取有关如何配置 TensorBoard 的更多信息:
$ tensorboard –help输出如下:

图 1.8:--help 命令的输出
你应该能看到相关的帮助信息,解释每个命令的作用,如 图 1.8 所示。
如上图所示,脚本返回消息,通知你所有依赖项已正确安装。
注意
要访问本节的源代码,请参阅packt.live/2B0JNau。
本节目前没有在线互动示例,需要在本地运行。
一旦我们确认安装了 Python 3、TensorFlow、Keras、TensorBoard 和 requirements.txt 中列出的所有包,就可以继续进行如何训练神经网络的演示,然后使用这些相同的工具来探索训练好的网络。
探索训练好的神经网络
在本节中,我们将探索一个训练好的神经网络。我们这样做是为了理解神经网络如何解决实际问题(预测手写数字),并熟悉 TensorFlow API。在探索这个神经网络时,我们将识别出许多在前面章节中介绍过的组件,比如节点和层,但也会看到许多我们不认识的组件(比如激活函数);我们将在后续章节中进一步探索这些组件。接着,我们将演示如何训练这个神经网络,并自己动手训练同样的网络。
我们将要探索的神经网络已经过训练,能够识别数字(整数),使用的是手写数字的图像。它使用了 MNIST 数据集(yann.lecun.com/exdb/mnist/),这是一个经典的数据集,常用于探索模式识别任务。
MNIST 数据集
修改版国家标准与技术研究院(MNIST)数据集包含 60,000 张训练图像和 10,000 张测试图像。每张图像包含一个手写的数字。这个数据集源自美国政府创建的一个数据集,最初用于测试不同的计算机系统识别手写文字的方法。能够做到这一点对于提高邮政服务、税务系统和政府服务的效率非常重要。MNIST 数据集被认为对于当代方法来说过于简单。现在的研究中使用了不同且更新的数据集(例如,加拿大高级研究院(CIFAR)数据集)。然而,MNIST 数据集仍然非常有用,因为已知的模型能够高效地达到很高的准确度,从而帮助理解神经网络的工作原理。
注意
CIFAR 数据集是一个机器学习数据集,包含按不同类别组织的图像。与 MNIST 数据集不同,CIFAR 数据集包含来自多个不同领域的类别,包括动物、活动和物体。CIFAR 数据集可以在www.cs.toronto.edu/~kriz/cifar.html获取。
然而,MNIST 数据集仍然非常有助于理解神经网络的工作原理,因为已知模型能够以极高的效率达到很高的准确率。在下图中,每张图片都是一张包含单个手写数字的 20x20 像素图像。你可以在 yann.lecun.com/exdb/mnist/ 找到原始数据集。

图 1.9:MNIST 数据集训练集的摘录
使用 TensorFlow 训练神经网络
现在,让我们使用 MNIST 数据集训练一个神经网络来识别新的数字。我们将实现一个名为卷积神经网络(CNN)的特殊神经网络来解决这个问题(我们将在后续部分中更详细地讨论它们)。我们的完整网络包含三个隐藏层:两个全连接层和一个卷积层。模型由以下 TensorFlow Python 代码片段定义:
注意
\ ) to split the logic across multiple lines. When the code is executed, Python will ignore the backslash, and treat the code on the next line as a direct continuation of the current line.
model = Sequential()
model.add(Convolution2D(filters = 10, kernel_size = 3, \
input_shape=(28,28,1)))
model.add(Flatten())
model.add(Dense(128, activation = 'relu'))
model.add(Dropout(0.2))
model.add(Dense(10, activation = 'softmax'))
注意
使用 mnist.py 文件作为参考,地址为 packt.live/2Cuhj9w。在代码编辑器中打开该脚本并跟随练习。
我们在训练神经网络时只执行一次前面的代码片段。
我们将在第二章《使用 TensorFlow 和 Keras 进行真实世界的深度学习:预测比特币的价格》中详细探讨每个组件的使用。现在,我们将重点理解网络在每次运行时如何改变每一层的权重和偏置值。这些 Python 代码是数十年神经网络研究的结晶。
现在,让我们训练这个网络,评估它在 MNIST 数据集上的表现。
练习 1.02:使用 MNIST 数据集训练神经网络
在这个练习中,我们将训练一个神经网络,用于检测 MNIST 数据集中的手写数字。按照以下步骤来设置这个练习:
-
打开两个终端实例。
-
访问
packt.live/2BWNAWK。确保你的 Python 3 虚拟环境已激活,并且已安装requirements.txt中列出的所有依赖项。 -
在其中一个终端中,使用以下命令启动 TensorBoard 服务器:
$ tensorboard --logdir logs/fit输出结果如下:
![图 1.10:TensorBoard 服务器]()
$ python mnist.py当你开始运行脚本时,你会看到如下的进度条:
![图 1.11:mnist.py 脚本的结果]()
图 1.11:mnist.py 脚本的结果
-
打开浏览器并导航到启动服务器时提供的 TensorBoard URL,在步骤 3中可能是
http://localhost:6006/或类似的网址。在你运行mnist.py脚本的终端中,你将看到模型的训练轮次进度条。当你打开浏览器页面时,你会看到几个图表,包括epoch_accuracy和epoch_loss图表。理想情况下,准确度应随着每次迭代而提高,而损失应随着每次迭代而减少。你可以通过图表直观地确认这一点。 -
点击
epoch_accuracy图表,放大它,并让页面刷新(或点击refresh图标)。你将看到随着训练轮次的增加,模型的准确度逐渐提高:

图 1.12:使用 TensorBoard 可视化准确度和损失图表
我们可以看到,在大约 5 个训练轮次(或步骤)后,网络的准确度超过了 97%。也就是说,到这一点为止,网络已经正确识别了测试集中的 97% 数字。
注意
要获取此特定部分的源代码,请访问 packt.live/2Cuhj9w。
本节目前没有在线互动示例,需在本地运行。
现在,让我们测试这些网络在未见过的数据上的表现。
使用未见过的数据测试网络性能
在浏览器中访问网站 mnist-demo.herokuapp.com/ 并在指定的白色框中画一个 0 到 9 之间的数字:

图 1.13:一个用于手动绘制数字并测试两个训练网络准确度的 Web 应用程序
注意
我们使用的这个 Web 应用程序是由 Shafeen Tejani 创建的,目的是探索训练过的网络是否能够正确预测我们手写的数字。
来源:github.com/ShafeenTejani/mnist-demo。
在应用程序中,你可以看到两个神经网络的结果 —— 如下图所示,数字 1 被绘制在绘图区域的右边缘附近:

图 1.14:两个网络在估计绘制在区域边缘的值时遇到了困难
在这个示例中,我们看到数字 1 被画在绘图区域的右侧。在两个网络中,该数字是 1 的概率为 0。
MNIST 数据集的图像边缘没有数字。因此,两个网络都没有给位于该区域的像素分配相关的值。如果我们把数字画得更靠近指定区域的中心,两个网络在正确分类数字方面会表现得更好。这是因为在训练集中,我们只有图像中间部分绘制的数字。这表明神经网络的强大程度取决于用于训练的数据。如果用于训练的数据与我们要预测的目标差异很大,网络很可能会产生令人失望的结果。
活动 1.01:使用不同超参数训练神经网络
在本节中,我们将探索在练习 1.02,使用 MNIST 数据集训练神经网络中训练的神经网络,那个时候我们在 MNIST 数据集上训练了自己的卷积神经网络(CNN)。我们已经将该训练好的网络作为二进制文件放在本书的目录中。在本活动中,我们将仅介绍你可以通过 TensorBoard 完成的操作,并通过更改一些超参数来训练其他几个网络。
这里是你需要遵循的步骤:
-
通过输入相应命令打开 TensorBoard。
-
打开 TensorBoard 的准确率图表,并调整平滑滑块的值来观察变化。
-
通过改变超参数来训练另一个模型。
-
尝试减小学习率并增加训练轮数。
-
现在尝试理解超参数调优对在 TensorBoard 上生成的图表有什么影响。
-
尝试增加学习率并减少训练轮数,然后重复步骤 5。
注意:
本活动的解决方案可以在第 130 页找到。
总结
在本章中,我们使用 TensorFlow 训练的神经网络进行了 TensorBoard 探索,并用不同的训练轮数和学习率训练了我们自己修改版的网络。这让你获得了训练一个高效神经网络的实战经验,并且可以探索其一些局限性。
你认为我们能使用真实的比特币数据达到类似的准确度吗?我们将在第二章,使用 TensorFlow 和 Keras 进行实际深度学习:预测比特币价格中尝试使用常见的神经网络算法来预测未来的比特币价格。在第三章,使用 TensorFlow 和 Keras 进行实际深度学习:评估比特币模型中,我们将评估并改进该模型,最后在第四章,产品化中,我们将创建一个程序,通过 HTTP API 提供该系统的预测服务。
第二章:2. 实际深度学习:预测比特币价格
概述
本章将帮助您为深度学习模型准备数据,选择合适的模型架构,使用 Keras——TensorFlow 2.0 的默认 API,并用训练好的模型进行预测。到本章结束时,您将准备好一个模型以进行预测,我们将在接下来的章节中探索它。
引言
在第一章,神经网络和深度学习简介的基本概念基础上,现在让我们移步到一个实际情景,并确定我们是否能够构建一个预测比特币价格的深度学习模型。
我们将学习为深度学习模型准备数据的原则,以及如何选择合适的模型架构。我们将使用 Keras——TensorFlow 2.0 的默认 API,并用训练好的模型进行预测。我们将通过将所有这些组件放在一起并构建一个简陋但完整的深度学习应用的第一个版本来结束本章。
深度学习是一个正在经历激烈研究活动的领域。研究人员致力于发明新的神经网络架构,可以解决新问题或提高先前实现的架构的性能。
在本章中,我们将研究旧和新的架构。旧的架构已经被用来解决各种问题,并且通常被认为是开始新项目时的正确选择。新的架构在特定问题上显示了巨大的成功,但很难一般化。后者作为下一步探索的参考是有趣的,但在开始项目时很难是一个好选择。
以下主题讨论了这些架构的细节以及如何确定特定问题陈述的最佳架构。
选择合适的模型架构
考虑到可用的架构可能性,有两种流行的架构通常被用作多个应用的起点:卷积神经网络(CNNs)和循环神经网络(RNNs)。这些是基础网络,应该被视为大多数项目的起点。
我们还介绍了其他三种网络的描述,因为它们在领域中的相关性:长短期记忆(LSTM)网络(一种 RNN 变体);生成对抗网络(GANs);以及深度强化学习(DRL)。这些后者在解决当代问题上取得了巨大成功,但使用起来略显困难。接下来的部分将涵盖在不同问题中使用不同类型架构的情况。
卷积神经网络(CNNs)
CNNs 因处理具有网格结构的问题而声名鹊起。最初它们被创建用于分类图像,但已被用于多个其他领域,从语音识别到自动驾驶车辆。
CNN 的核心理念是将紧密相关的数据作为训练过程的元素,而不仅仅是单独的数据输入。在图像处理的背景下,这一思想尤为有效,因为位于中心的像素与其右侧和左侧的像素相关联。卷积(convolution)这一名称被用来表示以下过程的数学形式:

图 2.1:卷积过程的示意图
注
图像来源:Volodymyr Mnih,等人
您可以在以下链接找到这张图像:packt.live/3fivWLB
欲了解更多有关深度强化学习的信息,请参考通过深度强化学习实现人类级别的控制。2015 年 2 月,Nature,可在storage.googleapis.com/deepmind-media/dqn/DQNNaturePaper.pdf获取。
循环神经网络(RNN)
CNN 使用一组输入,这些输入不断调整网络相应层和节点的权重和偏置。这种方法的已知局限性在于,当确定网络权重和偏置的变化时,它的架构忽略了这些输入的顺序。
RNN 的出现正是为了解决这一问题。它们被设计用于处理顺序数据。这意味着在每一个时期(epoch),层次结构可以受到前一层输出的影响。在每个序列中的前期观测记忆在后期观测的评估中起着重要作用。
由于语音识别问题的顺序性,RNN(循环神经网络)在该领域得到了成功应用。此外,RNN 也被用于翻译问题。Google Translate 当前的算法——Transformer,使用 RNN 将文本从一种语言翻译到另一种语言。在 2018 年底,Google 推出了另一种基于 Transformer 算法的算法,称为双向编码器表示(Bidirectional Encoder Representations from Transformers)(BERT),它目前是自然语言处理(NLP)领域的最先进技术。
注
欲了解更多关于 RNN 应用的信息,请参考以下内容:
Transformer:一种用于语言理解的全新神经网络架构,Jakob Uszkoreit,Google Research Blog,2017 年 8 月,可在ai.googleblog.com/2017/08/transformer-novel-neural-network.html获取。
BERT:开源 BERT:自然语言处理的最先进预训练技术,可在ai.googleblog.com/2018/11/open-sourcing-bert-state-of-art-pre.html获取。
以下图表展示了英语单词与法语单词如何根据它们在句子中的位置关联。RNN 在语言翻译问题中非常受欢迎:

图 2.2:distill.pub 中的插图,展示了英语和法语单词的关联
注意
图片来源:distill.pub/2016/augmented-rnns/
长短期记忆(LSTM)网络
LSTM网络是 RNN 的变体,用于解决梯度消失问题。该问题是由于与当前步骤相距过远的记忆组件所导致,它们由于距离较远而接收到较低的权重。LSTM 是 RNN 的一种变体,包含一个叫做遗忘门的记忆组件。该组件可以用来评估最近的和较早的元素如何影响权重和偏置,具体取决于观察值在序列中的位置。
注意
LSTM 架构最早由 Sepp Hochreiter 和 Jürgen Schmidhuber 于 1997 年提出。目前的实现已做了若干修改。如需详细了解 LSTM 每个组件的数学原理,请参考 Christopher Olah 的文章理解 LSTM 网络,2015 年 8 月,文章可通过以下链接获取:colah.github.io/posts/2015-08-Understanding-LSTMs/。
生成对抗网络
生成对抗网络(GANs)由 Ian Goodfellow 及其同事于 2014 年在蒙特利尔大学发明。GAN 的工作原理是,通过一个神经网络优化权重和偏置,以最小化其误差,而不是仅有一个神经网络优化权重和偏置,应该有两个神经网络互相竞争完成这一目标。
注意
如需了解有关 GAN 的更多信息,请参考生成对抗网络,Ian Goodfellow 等人,arXiv,2014 年 6 月 10 日,可通过以下链接获取:arxiv.org/pdf/1406.2661.pdf。
GANs 生成新的数据(伪数据)和一个评估第一个网络生成的数据是否为真实或伪的网络。它们相互竞争,因为它们都在学习:一个学习如何更好地生成伪数据,另一个学习如何区分数据是否真实。它们在每个迭代周期中反复训练,直到收敛。那时,评估生成数据的网络无法再区分伪数据和真实数据。
GANs 已在数据具有明确拓扑结构的领域成功应用。最初,GAN 被用来生成与真实图像相似的物体、人物面孔和动物的合成图像。你将在接下来的图像中看到,在StarGAN项目中,面部表情发生了变化:

图 2.3:基于情感的面部变化,使用 GAN 算法
这是图像创建领域中 GAN 使用最为频繁的地方,但在其他领域也偶尔能在研究论文中看到其应用。
注意
图像来源:StarGAN 项目, 可在github.com/yunjey/StarGAN查阅。
深度强化学习(DRL)
原始的深度强化学习(DRL)架构由 DeepMind 提出,DeepMind 是一个总部位于英国的 Google 拥有的人工智能研究机构。DRL 网络的核心思想是它们本质上是无监督的,通过试验和错误来学习,只优化奖励函数。
也就是说,DRL 网络与其他网络不同(后者使用监督方法来优化与已知正确结果相比的错误预测),DRL 网络并不知道解决问题的正确方法。它们只是被给定一个系统的规则,然后每当它们正确执行一个功能时,都会得到奖励。这个过程需要大量的迭代,最终训练网络在多个任务上表现出色。
注意
欲了解更多关于 DRL 的信息,请参阅Human-Level Control through Deep Reinforcement Learning,Volodymyr Mnih 等,2015 年 2 月,Nature,可在以下网址查阅:storage.googleapis.com/deepmind-media/dqn/DQNNaturePaper.pdf。
DRL 模型在 DeepMind 创建了 AlphaGo(一个比职业玩家更擅长围棋的系统)之后获得了广泛关注。DeepMind 还创建了 DRL 网络,使其能够独立地以超人类水平学习玩视频游戏。
注意
想了解更多关于 DQN 的信息,可以查阅 DeepMind 创建的 DQN,它能够击败 Atari 游戏。该算法使用 DRL 解决方案不断提高奖励。
图像来源:keon.io/deep-q-learning/。
以下是神经网络架构及其应用的总结:

图 2.4:不同的神经网络架构、数据结构及其成功应用
数据归一化
在构建深度学习模型之前,数据归一化是一个重要的步骤。数据归一化是机器学习系统中的常见做法。对于神经网络,研究人员提出归一化是训练 RNN(和 LSTM)时的一个关键技术,主要是因为它可以减少网络的训练时间,并提高网络的整体性能。
注意
欲了解更多信息,请参阅Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift,Sergey Ioffe 等,arXiv,2015 年 3 月, 可在arxiv.org/abs/1502.03167查阅。
哪种归一化技术最有效取决于数据和所处理的问题。以下是一些常用的技术:
Z-Score
当数据呈正态分布(即,高斯分布)时,你可以计算每个观察值与其均值的标准差距离。这种归一化方法在识别数据点距离分布中更可能出现的情况有多远时非常有用。Z 分数的定义如下公式:

图 2.5: Z 分数公式
这里,xi 是第i个观察值,
是均值,
是序列的标准差。
注意
欲了解更多信息,请参阅标准分数文章(Z 分数:定义、公式与计算),该文章可在www.statisticshowto.datasciencecentral.com/probability-and-statistics/z-score/上找到。
点相对归一化
这种归一化方法计算给定观察值与序列第一个观察值之间的差异。这种归一化方法有助于识别与起始点相关的趋势。点相对归一化的定义为:

图 2.6: 点相对归一化公式
这里,oi 是第i个观察值,oo 是序列的第一个观察值。
注意
欲了解更多关于预测的信息,请观看如何轻松预测股票价格 – 深度学习入门 #7,Siraj Raval,可以在 YouTube 上找到,链接:www.youtube.com/watch?v=ftMq5ps503w。
最大值和最小值归一化
这种归一化方法计算给定观察值与序列中最大值和最小值之间的距离。当处理最大值和最小值不是异常值且对未来预测非常重要的序列时,这种方法非常有用。这种归一化技术可以使用以下公式应用:

图 2.7: 归一化计算公式
这里,Oi 是第i个观察值,O代表包含所有O值的向量,min(O)和max(O)函数分别代表序列的最小值和最大值。
在练习 2.01,探索比特币数据集并为模型准备数据中,我们将准备可用的比特币数据,以便用于我们的 LSTM 模型。这包括选择感兴趣的变量,选择相关的时间段,并应用前述的点相对归一化技术。
结构化你的问题
与研究人员相比,实践者在开始一个新的深度学习项目时,花在选择架构上的时间要少得多。获取能够正确代表给定问题的数据是开发这些系统时最重要的因素,其次是理解数据集的固有偏差和限制。当开始开发深度学习系统时,可以考虑以下反思问题:
-
我是否拥有正确的数据?这是训练深度学习模型时最具挑战性的问题。首先,用数学规则定义你的问题。使用精确的定义,并将问题组织为类别(分类问题)或连续的量表(回归问题)。现在,如何收集与这些度量标准相关的数据?
-
我有足够的数据吗?通常,深度学习算法在大数据集上的表现远优于小数据集。了解训练一个高性能算法所需的数据量取决于你试图解决的问题类型,但应尽可能收集尽可能多的数据。
-
我能使用预训练模型吗?如果你正在解决的问题是更一般应用的一个子集,但仍然属于相同领域,考虑使用预训练模型。预训练模型可以帮助你迅速识别问题的特定模式,而不是广泛领域中的一般特征。一个好的起点是官方的 TensorFlow 代码库(
github.com/tensorflow/models)。
当你用这些问题来构建你的问题时,你将对任何新的深度学习项目采用一种顺序化的方法。以下是这些问题和任务的代表性流程图:

图 2.8:深度学习项目开始时需要问的关键反思问题的决策树
在某些情况下,数据可能根本无法获取。根据具体情况,可能可以使用一系列技术有效地从输入数据中生成更多数据。这个过程被称为数据增强,在图像识别问题中应用得非常成功。
注意
一个好的参考是文章 使用深度神经网络分类浮游生物,可以在benanne.github.io/2015/03/17/plankton.html找到。作者展示了一系列增强少量图像数据的技术,以增加模型的训练样本数量。
一旦问题结构清晰,你就可以开始准备模型。
Jupyter Notebook
在本节中,我们将使用 Jupyter Notebook 进行编码。Jupyter Notebook 提供通过 web 浏览器进行 Python 会话的功能,让您可以交互式地处理数据。它们是探索数据集的热门工具,将在本书的练习中使用。
练习 2.01:探索比特币数据集并为模型准备数据
在本次练习中,我们将准备数据,然后将其传递给模型。准备好的数据将在本章的后续部分有助于进行预测。在准备数据之前,我们将对其进行一些可视化分析,例如查看比特币的价值何时达到最高,以及下降何时开始。
注意
我们将使用一个原本从 Yahoo Finance 网站获取的公共数据集 (finance.yahoo.com/quote/BTC-USD/history/)。该数据集经过轻微修改,并且与本章一起提供,之后会在本书的其余部分使用。
数据集可以从以下网址下载:packt.live/2Zgmm6r。
以下是完成此练习的步骤:
-
使用您的终端,导航到
Chapter02/Exercise2.01目录。激活在上一章创建的环境,并执行以下命令以启动 Jupyter Notebook 实例:$ jupyter notebook这应该会自动在您的浏览器中打开 Jupyter Lab 服务器。从那里您可以启动 Jupyter Notebook。
您应该会看到以下输出或类似内容:
![图 2.9: 启动 Jupyter Lab 实例后的终端图片]()
图 2.9: 启动 Jupyter Lab 实例后的终端图片
-
选择
Exercise2.01_Exploring_Bitcoin_Dataset.ipynb文件。这是一个 Jupyter Notebook 文件,将在新的浏览器标签页中打开。应用程序将自动为您启动一个新的 Python 交互式会话:![图 2.10: Jupyter Notebook 实例的登录页面]()
图 2.10: Jupyter Notebook 实例的登录页面
-
点击 Jupyter Notebook 文件:
![图 2.11: Jupyter Notebook 图片]()
图 2.11: Jupyter Notebook 图片
-
打开我们的 Jupyter Notebook,考虑本章提供的比特币数据集。数据集
data/bitcoin_historical_prices.csv(packt.live/2Zgmm6r) 包含了自 2013 年初以来的比特币价格详情。它包含八个变量,其中两个(date和week)描述数据的时间周期。它们可以作为索引使用——其余六个(open、high、low、close、volume和market_capitalization)可以用来理解比特币价格和价值随时间的变化:![图 2.12: 比特币历史价格数据集中的可用变量(即列)]()
图 2.12: 比特币历史价格数据集中的可用变量(即列)
-
使用打开的 Jupyter Notebook 实例,考虑这两个变量的时间序列:
close和volume。从这些时间序列开始,探索价格波动模式,也就是比特币在历史数据中不同时间段的价格变化情况。 -
导航到打开的 Jupyter Notebook 实例,
Exercise2.01_Exploring_Bitcoin_Dataset.ipynb。现在,执行Introduction标题下的所有单元格。这将导入所需的库,并将数据集导入到内存中:![图 2.13:笔记本中第一个单元格的输出时间序列图 比特币收盘价时间序列图(来自 close 变量)]()
图 2.13:笔记本中第一个单元格的输出,比特币收盘价时间序列图(来自 close 变量)
-
数据集加载到内存后,转到
探索部分。你会找到一段代码,可以生成close变量的时间序列图:bitcoin.set_index('date')['close'].plot(linewidth=2, \ figsize=(14, 4),\ color='#d35400') #plt.plot(bitcoin['date'], bitcoin['close'])输出如下:
![图 2.14:比特币收盘价时间序列图(来自 close 变量)]()
图 2.14:比特币收盘价时间序列图(来自 close 变量)
-
在下面的一个新单元格中,使用
volume变量重新生成该图。你肯定已经注意到,比特币价格变量在 2017 年暴涨,随后开始下跌:bitcoin.set_index('date')['volume'].plot(linewidth=2, \ figsize=(14, 4), \ color='#d35400')![图 2.15:比特币每日交易量总和]()
图 2.15:比特币每日交易量总和
图 2.15 显示自 2017 年以来,比特币交易在市场中显著增加。每日交易量的变化比每日收盘价的变化大得多。
-
执行探索部分的其余单元格,探索 2017 年至 2018 年的范围。
近年来,比特币价格波动已变得越来越常见。虽然这些时期可以被神经网络用于理解某些模式,但我们将排除较旧的观测数据,因为我们关注的是预测未来不久的价格。因此,只过滤 2016 年后的数据。导航到
为模型准备数据集部分,使用 pandas API 来过滤数据。Pandas 提供了一个直观的 API 来执行此操作。 -
提取最新数据并将其保存到一个变量中:
bitcoin_recent = bitcoin[bitcoin['date'] >= '2016-01-04']bitcoin_recent变量现在包含我们原始的比特币数据集副本,但仅保留 2016 年 1 月 4 日及以后的数据。使用数据归一化部分中描述的点相对归一化技术对数据进行归一化。你只需归一化两个变量——
close和volume——因为这两个是我们要预测的变量。 -
运行笔记本中的下一个单元格,以确保我们只保留 close 和 volume 变量。
在包含此章节的同一目录中,我们放置了一个名为
normalizations.py的脚本。该脚本包含了本章描述的三种归一化技术。我们将该脚本导入到 Jupyter Notebook 中,并将函数应用于我们的数据系列。 -
导航到
Preparing Dataset for a Model部分。现在,使用iso_week变量,利用 pandas 的groupby()方法将每日观测按周分组。我们现在可以直接对该周的数据系列应用归一化函数normalizations.point_relative_normalization()。我们可以使用以下代码将归一化后的输出作为新变量存储在同一个 pandas DataFrame 中:bitcoin_recent['close_point_relative_normalization'] = \ bitcoin_recent.groupby('iso_week')['close']\ .apply(lambda x: normalizations.point_relative_normalization(x)) -
close_point_relative_normalization变量现在包含了close变量的归一化数据:bitcoin_recent.set_index('date')\ ['close_point_relative_normalization'].plot(linewidth=2, \ figsize=(14,4), \ color='#d35400')这将导致以下输出:
![图 2.16:Jupyter Notebook 的图像,聚焦于应用归一化函数的部分]()
图 2.16:Jupyter Notebook 的图像,聚焦于应用归一化函数的部分
-
对
volume变量(volume_point_relative_normalization)执行相同操作。归一化后的close变量每周都包含一个有趣的方差模式。我们将使用这个变量来训练我们的 LSTM 模型:bitcoin_recent.set_index('date')\ ['volume_point_relative_normalization'].\ plot(linewidth=2, \ figsize=(14,4), \ color='#d35400')你的输出应如下所示。
![图 2.17:显示归一化变量系列的图表]()
图 2.17:显示归一化变量系列的图表
-
为了评估模型的表现,你需要测试其与其他数据的准确性。通过创建两个数据集来实现这一点:一个训练集和一个测试集。你将使用数据集的 90%来训练 LSTM 模型,10%来评估模型的表现。由于数据是连续的并且以时间序列的形式存在,因此使用最后 10%的可用周作为测试集,前 90%作为训练集:
boundary = int(0.9 * bitcoin_recent['iso_week'].nunique()) train_set_weeks = bitcoin_recent['iso_week'].unique()[0:boundary] test_set_weeks = bitcoin_recent[~bitcoin_recent['iso_week']\ .isin(train_set_weeks)]['iso_week'].unique() test_set_weeks train_set_weeks这将显示以下输出:
![图 2.18:测试集周的输出]()
图 2.18:测试集周的输出
![图 2.19:使用周数创建训练集]()
图 2.19:使用周数创建训练集
-
为每个操作创建单独的数据集:
train_dataset = bitcoin_recent[bitcoin_recent['iso_week']\ .isin(train_set_weeks)] test_dataset = bitcoin_recent[bitcoin_recent['iso_week'].\ isin(test_set_weeks)] -
最后,导航到
Storing Output部分,并将筛选后的变量保存到磁盘,如下所示:test_dataset.to_csv('data/test_dataset.csv', index=False) train_dataset.to_csv('data/train_dataset.csv', index=False) bitcoin_recent.to_csv('data/bitcoin_recent.csv', index=False)注意
要访问此特定部分的源代码,请参考
packt.live/3ehbgCi。你也可以在线运行这个示例,网址是
packt.live/2ZdGq9s。你必须执行整个 Notebook 才能获得期望的结果。
在本次练习中,我们探索了比特币数据集,并为深度学习模型做好了准备。
我们了解到,2017 年比特币的价格暴涨。这一现象发生得很缓慢,可能受到了许多外部因素的影响,而这些数据本身无法完全解释(例如,其他加密货币的出现)。在 2017 年的暴涨后,我们看到比特币在 2018 年的价值大幅下跌。
我们还使用了点相对归一化技术,将比特币数据集按周进行处理。我们这样做是为了训练一个 LSTM 网络,学习比特币价格变化的周度模式,从而预测未来一整周的价格。
然而,比特币的统计数据显示其价格在周度基础上波动较大。我们能预测比特币未来的价格吗?七天后的价格会是多少?我们将在下一节中使用 Keras 构建一个深度学习模型来探索这些问题。
使用 Keras 作为 TensorFlow 接口
我们使用 Keras 是因为它将 TensorFlow 接口简化为通用抽象,并且在 TensorFlow 2.0 中,这是该版本的默认 API。在后台,计算仍然在 TensorFlow 中执行,但我们花费更少的时间关注各个组件,如变量和操作,而更多的时间用来构建作为计算单元的网络。Keras 使得我们可以轻松地实验不同的架构和超参数,更快速地朝着高效的解决方案迈进。
从 TensorFlow 2.0.0 开始,Keras 已正式与 TensorFlow 一起作为 tf.keras 进行分发。这意味着 Keras 现在与 TensorFlow 紧密集成,并且可能会继续作为一个开源工具长时间开发。组件是构建模型时的一个重要组成部分。让我们现在深入了解这个概念。
模型组件
正如我们在第一章《神经网络与深度学习简介》中所看到的,LSTM 网络同样具有输入层、隐藏层和输出层。每个隐藏层都有一个激活函数,用于评估该层相关的权重和偏差。正如预期,网络将数据从一个层顺序地传递到另一个层,并在每次迭代(即一个 epoch)时通过输出评估结果。
Keras 提供了直观的类,表示下表中列出的每个组件:

图 2.20:Keras API 关键组件的描述
我们将使用这些组件来构建深度学习模型。
Keras 的 keras.models.Sequential() 组件表示一个完整的顺序神经网络。这个 Python 类可以单独实例化,并可以后续添加其他组件。
我们之所以对构建 LSTM 网络感兴趣,是因为这些网络在处理序列数据时表现优异,而时间序列正是一种序列数据。使用 Keras,完整的 LSTM 网络将如下实现:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM
from tensorflow.keras.layers import Dense, Activation
model = Sequential()
model.add(LSTM(units=number_of_periods, \
input_shape=(period_length, number_of_periods) \
return_sequences=False), stateful=True)
model.add(Dense(units=period_length)) \
model.add(Activation("linear"))
model.compile(loss="mse", optimizer="rmsprop")
这个实现将在第三章,使用 TensorFlow 和 Keras 进行现实世界深度学习:评估比特币模型中进一步优化。
Keras 抽象使你能够专注于让深度学习系统更高效的关键元素:确定正确的组件顺序、包括多少层和节点、以及使用哪种激活函数。所有这些选择都是通过将组件添加到实例化的 keras.models.Sequential() 类中的顺序,或通过传递给每个组件实例化的参数(例如,Activation("linear"))来确定的。最后的 model.compile() 步骤使用 TensorFlow 组件构建神经网络。
在网络构建完成后,我们使用 model.fit() 方法来训练网络。这将生成一个经过训练的模型,可以用于进行预测:
model.fit(X_train, Y_train,
batch_size=32, epochs=epochs)
X_train 和 Y_train 变量分别是用于训练的集合和用于评估损失函数(即测试网络预测数据的准确性)的小集合。最后,我们可以使用 model.predict() 方法进行预测:
model.predict(x=X_train)
上述步骤涵盖了 Keras 在处理神经网络时的范式。尽管不同的架构可以通过非常不同的方式来处理,Keras 通过使用三个组件——Network Architecture、Fit 和 Predict——简化了与不同架构的交互:

图 2.21:Keras 神经网络范式
Keras 神经网络图包括以下三个步骤:
-
神经网络架构
-
训练神经网络(或 拟合)
-
进行预测
Keras 在这些步骤中提供了更大的控制权。然而,它的重点是尽可能简化用户创建神经网络的过程,尽量在最短时间内完成。这意味着我们可以从一个简单的模型开始,然后在每个前面的步骤中增加复杂性,以使初始模型表现得更好。
我们将在接下来的练习和章节中利用这一范式。在下一个练习中,我们将创建一个最简单的 LSTM 网络。然后,在第三章,现实世界中的深度学习:评估比特币模型中,我们将不断评估和调整该网络,使其更加稳健和高效。
练习 2.02:使用 Keras 创建一个 TensorFlow 模型
在这个 Notebook 中,我们使用 Keras 作为 TensorFlow 接口来设计和编译一个深度学习模型。我们将在接下来的章节和练习中继续修改这个模型,通过尝试不同的优化技术来优化它。然而,模型的基本组件完全是在这个 Notebook 中设计的:
-
打开一个新的 Jupyter Notebook,并导入以下库:
import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) import tensorflow as tf from tensorflow import keras from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM from tensorflow.keras.layers import Dense, Activation -
我们的数据集包含每日的观测值,每个观测值会影响未来的观测值。此外,我们的目标是预测未来一周——即 7 天——的比特币价格:
period_length = 7 number_of_periods = 208 - 21 - 1 number_of_periods我们已根据数据集中可用的周数计算了
number_of_observations。由于我们将在每个 epoch 使用上周的数据来测试 LSTM 网络,因此我们将使用 208 – 21 – 1。你将得到:186 -
使用 Keras 构建 LSTM 模型。我们将批处理大小设为 1,因为我们将整个数据一次性传递。如果数据量很大,我们可以通过多个批次来传递数据,这就是为什么我们使用
batch_input_shape的原因:def build_model(period_length, number_of_periods, batch_size=1): model = Sequential() model.add(LSTM(units=period_length,\ batch_input_shape=(batch_size, \ number_of_periods, \ period_length),\ input_shape=(number_of_periods, \ period_length),\ return_sequences=False, stateful=False)) model.add(Dense(units=period_length)) model.add(Activation("linear")) model.compile(loss="mse", optimizer="rmsprop") return model这应该返回一个已经编译的 Keras 模型,可以进行训练并存储到磁盘中。
-
让我们将模型存储到模型输出的磁盘上:
model = build_model(period_length=period_length, \ number_of_periods=number_of_periods) model.save('bitcoin_lstm_v0.h5')请注意,
bitcoin_lstm_v0.h5模型尚未训练。当保存一个未经训练的模型时,你实际上只保存了模型的架构。该模型稍后可以通过使用 Keras 的load_model()函数加载,如下所示:model = keras.models.load_model('bitcoin_lstm_v0.h5')注意
要访问该部分的源代码,请参考
packt.live/38KQI3Y。你也可以在
packt.live/3fhEL89上在线运行此示例。你必须执行整个 Notebook 才能获得预期的结果。
这标志着我们 Keras 模型的创建完成,现在我们可以使用它进行预测。
注意
你在加载 Keras 库时可能会遇到以下警告:
使用 TensorFlow 后端
Keras 可以配置为使用 TensorFlow 以外的其他后端(即 Theano)。为了避免出现此消息,你可以创建一个名为keras.json的文件,并在那里配置其后端。该文件的正确配置取决于你的系统。因此,建议你访问 Keras 的官方文档keras.io/backend/。
在本节中,我们学习了如何使用 Keras(TensorFlow 的接口)构建一个深度学习模型。我们研究了 Keras 的核心组件,并使用这些组件构建了基于 LSTM 模型的比特币价格预测系统的第一个版本。
在下一部分中,我们将讨论如何将本章中的所有组件整合成一个(几乎完整的)深度学习系统。该系统将产生我们第一个预测,作为未来改进的起点。
从数据准备到建模
本节专注于深度学习系统的实现方面。我们将使用选择正确的模型架构部分中的比特币数据,以及前一部分将 Keras 作为 TensorFlow 接口使用中的 Keras 知识,将这两个组件结合在一起。本节通过构建一个从磁盘读取数据并将其作为一个整体输入到模型中的系统来结束本章。
训练神经网络
神经网络可能需要很长时间才能训练。影响训练时间的因素有很多。其中,三个因素通常被认为是最重要的:
-
网络的架构
-
网络的层数和神经元数
-
在训练过程中要使用多少数据
其他因素也可能极大地影响网络训练的时间,但在解决业务问题时,神经网络可以进行大多数优化。
我们将使用前一节中的标准化数据。请记住,我们已将训练数据存储在名为train_dataset.csv的文件中。
注意:
您可以通过访问此链接下载训练数据:packt.live/2Zgmm6r。
我们将使用pandas库将该数据集加载到内存中,以便轻松探索:
import pandas as pd
train = pd.read_csv('data/train_dataset.csv')
注意
确保根据您下载或保存 CSV 文件的位置更改路径(已突出显示)。
您将看到以下表格形式的输出:

图 2.22:显示训练数据集前五行的表格
我们将使用close_point_relative_normalization 变量的系列,这是比特币收盘价从 2016 年初以来的标准化系列——自close变量开始。
close_point_relative_normalization 变量已按周进行了标准化。每周期内的每个观察值都相对于该期的第一天的收盘价差异进行了。这一标准化步骤很重要,将有助于我们的网络更快地训练:

图 2.23:显示来自标准化变量的系列的图表。
此变量将用于训练我们的 LSTM 模型。
重塑时间序列数据
神经网络通常使用向量和张量——这两种数学对象来组织数据在多个维度上。Keras 中实现的每个神经网络都将有一个按照规定组织的向量或张量作为输入。
起初,理解如何将数据重塑为给定层期望的格式可能会令人困惑。为了避免混淆,建议从尽可能少的组件开始,逐渐添加组件。Keras 的官方文档(位于Layers部分)对了解每种层的要求至关重要。
注意
Keras 的官方文档可在keras.io/layers/core/找到。该链接直接进入了Layers部分。
NumPy 是一个流行的 Python 库,用于进行数值计算。它被深度学习社区用于操作向量和张量,并为深度学习系统准备数据。
特别是,numpy.reshape() 方法在为深度学习模型调整数据时非常重要。该模型允许操作 NumPy 数组,这些数组是 Python 对象,类似于向量和张量。
现在我们将根据 2016 年之后的周次对 close_point_relative_normalization 变量中的价格进行组织。我们将创建不同的组,每组包含 7 个观察值(每周一天),总共 208 周完整数据。这样做是因为我们有兴趣预测一周内的交易价格。
注意
我们使用 ISO 标准来确定一周的开始和结束。其他类型的组织方式完全是可能的。这种方法简单直观,但仍有改进的空间。
LSTM 网络处理三维张量。每一个维度代表了网络的一个重要特性。这些维度如下:
-
周期长度:周期长度,即一个周期内有多少个观察值
-
周期数:数据集中可用的周期数量
-
特征数:数据集中可用的特征数量
我们来自 close_point_relative_normalization 变量的数据目前是一个一维向量。我们需要将其重塑为匹配这三个维度。
我们将使用一周的周期。因此,我们的周期长度为 7 天(周期长度 = 7)。我们有 208 周完整的周数据可用。在训练期间,我们将使用这些周中的最后一周来测试我们的模型。这使得我们剩下 187 个不同的周。最后,我们将在该网络中使用单个特征(特征数 = 1);未来版本中我们会加入更多特征。
为了调整数据以匹配这些维度,我们将使用基本的 Python 属性和 numpy 库中的 reshape() 方法的组合。首先,我们使用纯 Python 创建 186 个包含 7 天的不同周组:
group_size = 7
samples = list()
for i in range(0, len(data), group_size):
sample = list(data[i:i + group_size])
if len(sample) == group_size:samples\
.append(np.array(sample)\
.reshape(group_size, 1).tolist())
data = np.array(samples)
这段代码创建了不同的周组。生成的变量数据是一个包含所有正确维度的变量。
注意
每个 Keras 层将期望其输入以特定的方式组织。然而,在大多数情况下,Keras 会自动调整数据的形状。添加新层之前,请始终参考 Keras 的层文档(keras.io/layers/core/)。
Keras LSTM 层期望这些维度按特定顺序组织:特征数、观察数和周期长度。将数据集重塑为匹配该格式:
X_train = data[:-1,:].reshape(1, 186, 7)
Y_validation = data[-1].reshape(1, 7)
上面的代码片段还选择了我们数据集中最后一周作为验证集(通过 data[-1])。我们将尝试使用前 76 周的数据预测数据集中的最后一周。下一步是使用这些变量来拟合我们的模型:
model.fit(x=X_train, y=Y_validation, epochs=100)
LSTM 是计算量大的模型。在现代计算机上,它们可能需要多达 5 分钟的时间来训练我们的数据集。大部分时间都花费在计算的开始阶段,此时算法会创建完整的计算图。开始训练后,过程会变得更快:

图 2.24:显示每个时期评估的损失函数结果的图表
注意
这比较了模型在每个时期预测的内容,然后使用称为均方误差的技术将其与真实数据进行比较。该图显示了这些结果。
乍一看,我们的网络表现似乎非常好;它从一个非常小的错误率开始,持续下降。现在我们已经降低了错误率,让我们继续进行一些预测。
进行预测
在我们的网络训练完毕后,我们可以开始进行预测。我们将为未来一个超出我们时间范围的周进行预测。
一旦我们使用 model.fit() 方法训练了我们的模型,进行预测就很简单了:
model.predict(x=X_train)
我们使用与训练数据相同的数据进行预测(变量 X_train)。如果有更多数据可用,我们可以使用那些数据——前提是将其调整为 LSTM 所需的格式。
过拟合
当神经网络在验证集上过拟合时,这意味着它学习了训练集中存在的模式,但无法将其推广到未见过的数据(例如测试集)。在下一章中,我们将学习如何避免过拟合,并创建一个既能评估网络又能提升其性能的系统:

图 2.25:显示比特币每周表现的图表
在上述图中,水平轴表示周数,垂直轴表示比特币的预测表现。现在我们已经探索了数据、准备了模型并学会了如何进行预测,让我们将这些知识付诸实践。
活动 2.01:组装一个深度学习系统
在这个活动中,我们将集合所有构建基本深度学习系统的关键部分——数据、模型和预测:
-
启动 Jupyter Notebook。
-
将训练数据集加载到内存中。
-
检查训练集,看看它是否以周期长度、周期数或特征数量的形式呈现。
-
如果不在所需格式中,将训练集转换。
-
加载先前训练过的模型。
-
使用您的训练数据集训练模型。
-
对训练集进行预测。
-
对值进行反归一化并保存模型。
最终输出将如下所示,水平轴表示天数,垂直轴表示比特币的价格:
6

图 2.26:预期输出
注意
这个活动的解决方案可以在第 136 页找到。
总结
在本章中,我们已经组装了一个完整的深度学习系统,从数据到预测。这个活动中创建的模型在被认为有用之前需要进行多项改进。然而,它作为我们不断改进的一个很好的起点。
下一章将探讨衡量我们模型性能的技术,并继续进行修改,直到我们得到一个既有用又稳健的模型。
第三章:3. 实际深度学习:评估比特币模型
概述
本章重点讲解如何评估神经网络模型。我们将修改网络的超参数以提升其性能。然而,在更改任何参数之前,我们需要先衡量模型的表现。在本章结束时,您将能够使用不同的函数和技术评估模型。您还将通过实现函数和正则化策略,学习超参数优化。
介绍
在上一章中,您已经训练了您的模型。那么,您如何检查它的性能以及它是否表现良好呢?让我们通过评估模型来找出答案。在机器学习中,通常会定义两个不同的术语:参数和超参数。参数是影响模型如何从数据中做出预测的属性,比如来自特定数据集的预测。超参数则是模型如何从数据中学习的方式。参数可以从数据中学习并动态修改。而超参数则是在训练开始之前定义的高层次属性,通常不会从数据中学习。在本章中,您将详细了解这些因素,并理解如何使用它们与不同的评估技术相结合,以提高模型的性能。
注意事项
如需更详细的机器学习概述,请参阅 Python Machine Learning(Sebastian Raschka 和 Vahid Mirjalili 著,Packt Publishing,2017 年)。
问题类别
通常,神经网络可以解决两类问题:分类和回归。分类问题关注从数据中预测正确的类别——例如,温度是热还是冷。回归问题则是关于预测连续标量中的值——例如,实际的温度值。
这两类问题的特点如下:
-
yes或no。然而,它们必须清楚地分配给每个数据元素。分类问题的一个例子是使用卷积神经网络将图像标记为car或not a car。我们在《第一章:神经网络与深度学习概述》中探索的 MNIST 示例也是一个分类问题的例子。 -
回归:这些问题的特点是具有连续变量(即标量)。它们按范围进行度量,其评估考虑网络与实际值的接近程度。一个例子是时间序列分类问题,其中使用递归神经网络预测未来的温度值。比特币价格预测问题是另一个回归问题的例子。
尽管这两类问题的模型评估结构相同,但我们使用不同的技术来评估模型的表现。在接下来的部分,我们将探讨这些评估分类或回归问题的技术。
损失函数、准确率与误差率
神经网络使用衡量网络与验证集表现的函数——即,保留出来的部分数据,在训练过程中作为验证使用。这些函数被称为损失函数。
损失函数评估神经网络预测的错误程度。然后,它们将这些错误反向传播并调整网络,改变单个神经元的激活方式。损失函数是神经网络的关键组成部分,选择正确的损失函数会对网络的表现产生重大影响。误差通过一种叫做反向传播的过程传播,这是一种将损失函数返回的误差传播到神经网络中每个神经元的技术。传播的误差影响神经元的激活方式,最终影响网络输出的结果。
许多神经网络框架,包括 Keras,默认使用这一技术。
注意
有关反向传播数学原理的更多信息,请参考《深度学习》,Ian Goodfellow 等著,MIT 出版社,2016 年。
对于回归和分类问题,我们使用不同的损失函数。对于分类问题,我们使用准确率函数(即,预测正确的次数与总预测次数的比例)。例如,如果你预测抛硬币的结果,当抛掷 n 次时,其中会出现 m 次正面,且预测正确,那么准确率将计算为 m/n。对于回归问题,我们使用误差率(即,预测值与观察值的接近程度)。
以下是一些常见的损失函数总结及其常见应用:

图 3.1:用于分类和回归问题的常见损失函数
对于回归问题,MSE 函数是最常用的选择;而对于分类问题,二元交叉熵(适用于二分类问题)和多元交叉熵(适用于多分类问题)是常见的选择。建议从这些损失函数开始,然后在神经网络演变的过程中,尝试其他函数,以期提升性能。
我们在第二章中开发的网络,《用 TensorFlow 和 Keras 进行真实世界的深度学习:预测比特币的价格》,使用均方误差(MSE)作为损失函数。在接下来的部分,我们将探讨该函数在网络训练过程中的表现。
不同的损失函数,相同的架构
在继续下一部分之前,让我们从实际操作的角度探讨一下这些问题在神经网络中的不同之处。
TensorFlow Playground(playground.tensorflow.org/)应用程序是由 TensorFlow 团队提供的,旨在帮助我们理解神经网络是如何工作的。在这里,我们可以看到一个由输入层(左侧)、隐藏层(中间)和输出层(右侧)组成的神经网络。
注:
这些图像可以在 GitHub 上的仓库中查看:packt.live/2Cl1t0H。
我们还可以选择不同的示例数据集进行实验,这些数据集位于最左侧。最后,在最右侧,我们可以看到网络的输出:

图 3.2:TensorFlow Playground 网络应用程序
通过查看此可视化中的神经网络参数,您可以了解每个参数如何影响模型的结果。此应用程序帮助我们探索上一部分中讨论的不同问题类别。当我们选择回归(右上角)时,点的颜色会在橙色和蓝色之间的颜色范围内变化:

图 3.3:TensorFlow Playground 中的回归问题示例
当我们选择分类作为问题类型时,数据集中的点只有两种颜色:蓝色或橙色。在处理分类问题时,网络根据网络错误分类的蓝色和橙色点的数量来评估其损失函数。它检查每个点在网络中颜色值右侧的偏移量,如下截图所示:

图 3.4:TensorFlow Playground 应用程序的详细信息。不同的颜色被分配给分类问题中的不同类别
点击播放按钮后,我们会注意到训练损失区域中的数字不断下降,因为网络在持续训练。这些数字在每个问题类别中非常相似,因为损失函数在神经网络中起着相同的作用。
然而,每个类别实际使用的损失函数是不同的,具体选择哪个函数取决于问题的类型。
使用 TensorBoard
评估神经网络是 TensorBoard 的强项。正如我们在第一章,神经网络与深度学习简介中解释的那样,TensorBoard 是与 TensorFlow 一起提供的一套可视化工具。除了其他功能,我们可以在每个 epoch 后探索损失函数评估的结果。TensorBoard 的一个很棒的特点是,我们可以将每次运行的结果单独组织,并比较每次运行的损失函数度量标准。然后,我们可以决定调整哪些超参数,并对网络的表现有一个大致的了解。最棒的是,这一切都能实时完成。
为了在我们的模型中使用 TensorBoard,我们将使用 Keras 的callback函数。我们通过导入 TensorBoard 的callback并将其传递给模型,在调用fit()函数时实现这一点。以下代码展示了如何在我们在第二章,TensorFlow 和 Keras 的实际深度学习:预测比特币价格中创建的比特币模型中实现这一点:
from tensorflow.keras.callbacks import TensorBoard
model_name = 'bitcoin_lstm_v0_run_0'
tensorboard = TensorBoard(log_dir='logs\\{}'.format(model_name)) \
model.fit(x=X_train, y=Y_validate, \
batch_size=1, epochs=100, verbose=0, \
callbacks=[tensorboard])
Keras 的callback函数会在每个 epoch 运行结束时被调用。在这种情况下,Keras 会调用 TensorBoard 的callback,将每次运行的结果存储到磁盘上。还有许多其他有用的callback函数,你可以使用 Keras API 创建自定义的回调函数。
注意
请参考 Keras 回调文档(keras.io/callbacks/)以获取更多信息。
在实现 TensorBoard 回调后,损失函数的度量标准现在可以在 TensorBoard 界面中查看。你可以运行 TensorBoard 进程(使用tensorboard --logdir=./logs),并在训练网络时保持它运行(通过fit())。
评估的主要图形通常称为损失。我们可以通过将已知度量标准传递给fit()函数中的 metrics 参数来添加更多度量标准。这些度量标准会在 TensorBoard 中进行可视化,但不会用于调整网络的权重。交互式图形会持续实时更新,帮助你了解每个 epoch 的进展。在下面的截图中,你可以看到一个 TensorBoard 实例,展示了损失函数的结果,以及已添加到 metrics 参数中的其他度量标准:
![图 3.5:展示损失函数结果的 TensorBoard 实例截图]
图 3.5:展示损失函数结果的 TensorBoard 实例截图
在下一节中,我们将详细讨论如何实现本节中讨论的不同度量标准。
实现模型评估度量标准
在回归和分类问题中,我们将输入数据集拆分为另外三个数据集:训练集、验证集和测试集。训练集和验证集都用于训练网络。训练集作为输入提供给网络,而验证集则被损失函数用来将神经网络的输出与真实数据进行比较,并计算预测的误差。最后,测试集在网络训练完成后用于衡量网络在未见过的数据上的表现。
注意
没有明确的规则来确定训练集、验证集和测试集如何划分。常见的方法是将原始数据集划分为 80% 的训练集和 20% 的测试集,然后将训练集再划分为 80% 的训练集和 20% 的验证集。有关此问题的更多信息,请参阅 Python 机器学习,作者 Sebastian Raschka 和 Vahid Mirjalili (Packt Publishing, 2017)。
在分类问题中,您将数据和标签作为相关但不同的数据传递给神经网络。然后,网络学习数据与每个标签之间的关系。在回归问题中,我们不是传递数据和标签,而是将感兴趣的变量作为一个参数传递,将用于学习模式的变量作为另一个参数。Keras 提供了适用于这两种情况的接口,使用的是 fit() 方法。
注意
fit() 方法可以使用 validation_split 或 validation_data 参数,但不能同时使用两者。
请参见以下代码片段,了解如何使用 validation_split 和 validation_data 参数:
model.fit(x=X_train, y=Y_ train, \
batch_size=1, epochs=100, verbose=0, \
callbacks=[tensorboard], validation_split=0.1, \
validation_data=(X_validation, Y_validation))
X_train:训练集中的特征
Y_train:训练集中的标签
batch_size:每批的大小
epochs:迭代次数
verbose:您希望的输出级别
callbacks:在每个 epoch 后调用一个函数
validation_split:如果您没有显式创建验证集,则为验证集的百分比分割
validation_data:如果您显式创建了验证集,则为验证数据
损失函数评估模型的进展,并在每次运行时调整其权重。然而,损失函数仅描述训练数据和验证数据之间的关系。为了评估模型是否正确执行,我们通常会使用第三个数据集——该数据集不用于训练网络——并将我们模型的预测与该数据集中的真实值进行比较。这就是测试集的作用。
Keras 提供了 model.evaluate() 方法,使得对已训练神经网络在测试集上的评估变得简单。以下代码展示了如何使用 evaluate() 方法:
model.evaluate(x=X_test, y=Y_test)
evaluate() 方法返回损失函数的结果以及传递给 metrics 参数的函数的结果。我们将在比特币问题中频繁使用该函数,以测试模型在测试集上的表现。
你会注意到我们之前训练的比特币模型与这个示例有些不同。这是因为我们使用了 LSTM 架构。LSTM 被设计用来预测序列。
因此,我们不使用一组变量来预测另一个单一的变量——即使这是一个回归问题。相反,我们使用来自单一变量(或变量集)的先前观察值来预测该变量(或变量集)未来的观察值。keras.fit()中的y参数与x参数包含相同的变量,但仅包含预测的序列。那么,让我们看看如何评估之前训练的比特币模型。
评估比特币模型
我们在第一章,神经网络与深度学习简介中的活动中创建了一个测试集。该测试集包含 21 周的每日比特币价格观察数据,相当于原始数据集的约 10%。
我们还使用了其余 90%的数据(即训练集,包含 187 周数据,减去 1 周用于验证集)在第二章,使用 TensorFlow 和 Keras 进行真实世界的深度学习:预测比特币价格中训练了我们的神经网络,并将训练好的网络保存在磁盘上(bitcoin_lstm_v0)。现在,我们可以使用evaluate()方法来评估测试集中的每一周数据,并查看第一个神经网络的表现。
但是,要做到这一点,我们需要提供 186 周的前期数据。我们必须这么做,因为我们的网络是通过使用恰好 186 周的连续数据来预测一周数据进行训练的(我们将在第四章,产品化中通过定期用更长时间段的数据重新训练网络来处理这种行为,当我们将神经网络部署为 Web 应用程序时)。以下代码片段实现了evaluate()方法,用于评估我们模型在测试数据集中的表现:
combined_set = np.concatenate((train_data, test_data), axis=1) \
evaluated_weeks = []
for i in range(0, validation_data.shape[1]):
input_series = combined_set[0:,i:i+187]
X_test = input_series[0:,:-1].reshape(1, \
input_series.shape[1] – 1, ) \
Y_test = input_series[0:,-1:][0]
result = B.model.evaluate(x=X_test, y=Y_test, verbose=0) \
evaluated_weeks.append(result)
在前面的代码中,我们使用 Keras 的model.evaluate()方法评估每一周的数据,然后将其输出存储在evaluated_weeks变量中。接着,我们绘制每周的 MSE 结果,如下图所示:

图 3.6:测试集每周的 MSE
我们模型的 MSE(均方误差)结果表明,我们的模型在大多数周内表现良好,除了第 2、8、12 和 16 周,其值从大约 0.005 增加到 0.02。我们模型似乎在几乎所有其他测试周内表现得都很好。
过拟合
我们第一个训练的网络(bitcoin_lstm_v0)可能正遭遇一种被称为过拟合的现象。过拟合是指模型在优化验证集时,虽然在验证集上表现得很好,但却牺牲了从我们想要预测的现象中学习到的更具泛化能力的模式。过拟合的主要问题是模型学会了如何预测验证集,但却无法预测新的数据。
我们在模型中使用的损失函数在训练过程结束时达到了非常低的水平。不仅如此,这一过程发生得很早:用于预测我们数据最后一周的 MSE 损失函数在大约第 30 个周期时降到了一个稳定的平稳值。这意味着我们的模型几乎完美地预测了第 187 周的数据,使用了前 186 周的数据。这会不会是过拟合的结果呢?
让我们再看一遍前面的图表。我们知道我们的 LSTM 模型在验证集上达到了极低的值(大约为 2.9 × 10^-6),但它在测试集上的表现也很低。然而,关键的区别在于规模。在我们的测试集中,每周的 MSE 大约是验证集的 4,000 倍(平均值)。这意味着模型在我们的测试数据中的表现远不如在验证集中的表现。这一点值得考虑。
然而,规模掩盖了我们 LSTM 模型的强大能力;即使在我们的测试集上表现较差,预测的 MSE 误差仍然非常、非常低。这表明我们的模型可能正在从数据中学习模式。
模型预测
比较 MSE 误差来衡量我们的模型是一回事,而能够直观地解释其结果是另一回事。
使用相同的模型,让我们为接下来的几周创建一系列预测,使用 186 周的数据作为输入。我们通过将 186 周的滑动窗口应用于完整的序列(即训练集加测试集),并为每个窗口做出预测来实现这一点。
以下代码片段使用Keras model.predict()方法对测试数据集的所有周进行预测:
combined_set = np.concatenate((train_data, test_data), \
axis=1) predicted_weeks = []
for i in range(0, validation_data.shape[1] + 1):
input_series = combined_set[0:,i:i+186]
predicted_weeks.append(B.predict(input_series))
在前面的代码中,我们使用model.predict()方法进行预测,然后将这些预测存储在predicted_weeks变量中。然后,我们绘制结果图,生成以下图表:

图 3.7:测试集中每周的 MSE
我们模型的结果表明,它的表现并没有那么糟糕。通过观察Predicted线(灰色)的模式,我们可以看到网络已经识别出了每周波动的模式,规范化价格在一周的中间上涨,到周末时下降。然而,仍然有很大的改进空间,因为它无法捕捉到更大的波动。除了几个星期外——与我们之前的 MSE 分析相同——大多数周的数据接近正确的值。
现在,让我们对预测结果进行反归一化,以便能够使用与原始数据相同的尺度(即美元)来分析预测值。我们可以通过实现一个反归一化函数来完成这一步,该函数利用预测数据中的日期索引来识别测试数据中的等效周。识别出该周后,函数将取该周的第一个值,并利用相同的点相对归一化技术(但反向)来反归一化预测值。以下代码段展示了如何使用反向点相对归一化技术来反归一化数据:
def denormalize(reference, series, normalized_variable=\
'close_point_relative_normalization', \
denormalized_variable='close'):
if('iso_week' in list(series.columns)):
week_values = reference[reference['iso_week'] \
== series['iso_week'].values[0]]
last_value = week_values[denormalized_variable].values[0]
series[denormalized_variable] = \
last_value * (series[normalized_variable] + 1)
return series
predicted_close = predicted.groupby('iso_week').apply(lambda x: \
denormalize(observed, x))
denormalize()函数会取测试集等效周第一天的收盘价。
我们的结果现在使用美元比较预测值与测试集。如下图所示,bitcoin_lstm_v0模型在预测未来 7 天比特币价格时似乎表现得相当好。但我们如何用可解释的术语来衡量这种表现呢?

图 3.8:每周反归一化预测
解释预测结果
我们的最后一步是为预测结果添加可解释性。前面的图表似乎显示我们的模型预测与测试数据的匹配度较高,但到底有多接近呢?
Keras 的model.evaluate()函数对理解模型在每个评估步骤中的表现非常有用。然而,考虑到我们通常使用归一化数据集来训练神经网络,model.evaluate()方法生成的指标也很难解释。
为了解决这个问题,我们可以收集模型的完整预测集,并使用来自图 3.1的另外两个更易解释的函数进行比较:MAPE 和 RMSE,分别通过mape()和rmse()实现。
注意
这些函数是使用 NumPy 实现的。原始实现来自stats.stackexchange.com/questions/58391/mean-absolute-percentage-error-mape-in-scikit-learn和stackoverflow.com/questions/16774849/mean-squared-error-in-numpy
我们可以在以下代码段中看到这些方法的实现:
def mape(A, B):
return np.mean(np.abs((A - B) / A)) * 100
def rmse(A, B):
return np.sqrt(np.square(np.subtract(A, B)).mean())
在使用这两个函数比较我们的测试集和预测值之后,我们得到了以下结果:
-
反归一化 RMSE:$596.6 美元
-
反归一化 MAPE:4.7%
这表明我们的预测与实际数据的差异平均约为$596。这表示与实际比特币价格相比,差异约为 4.7%。
这些结果有助于我们理解预测结果。我们将继续使用model.evaluate()方法来跟踪 LSTM 模型的改进,同时也会计算rmse()和mape(),并在每个版本的模型上对完整数据系列进行计算,以解释我们距离预测比特币价格有多近。
练习 3.01:创建一个主动训练环境
在本练习中,我们将为神经网络创建一个训练环境,以便于其训练和评估。这个环境对于接下来的主题尤为重要,我们将在其中寻找超参数的最佳组合。
首先,我们将启动 Jupyter Notebook 实例和 TensorBoard 实例。这两个实例可以在本练习的剩余部分保持打开。让我们开始吧:
-
在终端中,导航到
Chapter03/Exercise3.01目录,并执行以下代码以启动 Jupyter Notebook 实例:$ jupyter-lab服务器应该会自动在你的浏览器中打开。
-
打开名为
Exercise3.01_Creating_an_active_training_environment.ipynb的 Jupyter Notebook:![图 3.9:Jupyter Notebook 高亮显示了“Evaluate LSTM Model”部分]()
图 3.9:Jupyter Notebook 高亮显示了“Evaluate LSTM Model”部分
-
同时在你的终端中,通过执行以下命令启动一个 TensorBoard 实例:
$ cd ./Chapter03/Exercise3.01/ $ tensorboard --logdir=logs/确保
logs目录在仓库中是空的。 -
打开屏幕上出现的 URL,并保持该浏览器标签页打开。还要执行包含导入语句的初始单元格,以确保加载了所需的依赖项。
-
执行“Validation Data”下的两个单元格,以在 Jupyter Notebook 中加载训练和测试数据集:
train = pd.read_csv('data/train_dataset.csv') test = pd.read_csv('data/test_dataset.csv')注意
不要忘记根据文件在你系统上的保存位置更改路径(高亮部分)。
-
添加 TensorBoard 回调并重新训练模型。执行“Re-Train model with TensorBoard”下的单元格。
现在,让我们评估我们的模型在测试数据上的表现。我们的模型是用 186 周的数据来预测未来一周的数据——也就是接下来的 7 天。当我们构建第一个模型时,我们将原始数据集分为训练集和测试集。现在,我们将这两个数据集的结合版本(我们称之为“合并集”)进行滑动窗口操作,窗口大小为 186 周。在每个窗口中,我们执行 Keras 的
model.evaluate()方法,评估网络在该特定周的表现。 -
执行标题为
Evaluate LSTM Model下的单元格。这些单元格的关键概念是为测试集中的每一周调用model.evaluate()方法。这行代码是最重要的:result = model.evaluate(x=X_test, y=Y_test, verbose=0) -
每个评估结果现在都存储在
evaluated_weeks变量中。该变量是一个简单的数组,包含测试集每一周的 MSE 预测序列。继续绘制结果:![图 3.10:使用方法对测试集每一周的 MSE 结果]()
图 3.10:从 model.evaluate()方法返回的每周测试集的 MSE 结果
正如我们已经讨论过的,MSE 损失函数很难解释。为了帮助我们理解模型的表现,我们还调用
model.predict()方法,在测试集中的每一周进行预测,并将其预测结果与测试集中的真实值进行比较。 -
转到解释模型结果部分,并在
进行预测子标题下执行代码单元。请注意,我们正在调用model.predict()方法,但使用的是略有不同的参数组合。我们不再同时使用X和Y值,而只使用X:predicted_weeks = [] for i in range(0, test_data.shape[1]): input_series = combined_set[0:,i:i+186] predicted_weeks.append(model.predict(input_series))在每个窗口中,我们将为接下来的一周发布预测结果并存储。现在,我们可以将标准化的结果与测试集中的标准化值一起绘制,如下图所示:
![图 3.11:绘制从 model.predict()返回的每周标准化值 测试集的周]()
图 3.11:绘制从 model.predict()返回的每周标准化值
我们还将使用反标准化值进行相同的比较。为了反标准化我们的数据,我们必须找出测试集和预测结果之间的对应周。然后,我们可以获取该周的第一个价格值,并利用它来逆转《第二章》中的基于点的标准化方程,即《现实世界的深度学习与 TensorFlow 和 Keras:预测比特币价格》。
-
转到
反标准化预测标题,并执行该标题下的所有单元格。 -
在本节中,我们定义了
denormalize()函数,该函数执行完整的反标准化过程。与其他函数不同,这个函数接收一个 pandas DataFrame,而不是 NumPy 数组。我们这样做是为了使用日期作为索引。以下是该标题下最相关的单元格块:predicted_close = predicted.groupby('iso_week').apply(\ lambda x: denormalize(observed, x))我们的反标准化结果(如以下图所示)表明,我们的模型的预测与真实的比特币价格非常接近。那么,接近程度如何呢?
![图 3.12:绘制从 model.predict()返回的反标准化值 对于测试集中的每一周]()
图 3.12:绘制从 model.predict()返回的每周反标准化值
LSTM 网络使用 MSE 值作为其损失函数。然而,正如我们已经讨论过的,MSE 值很难解释。为了解决这个问题,我们需要实现两个函数(从
utilities.py脚本中加载),它们实现rmse()和mape()函数。这些函数通过返回与我们原始数据使用相同尺度的度量,并通过将差异按百分比进行比较,从而为我们的模型增加了可解释性。 -
转到
反标准化预测标题,并从utilities.py脚本中加载两个函数:from scripts.utilities import rmse, mape这个脚本中的函数其实非常简单:
def mape(A, B): return np.mean(np.abs((A - B) / A)) * 100 def rmse(A, B): return np.sqrt(np.square(np.subtract(A, B)).mean())每个函数都是使用 NumPy 的矢量操作实现的。它们在相同长度的向量中工作良好,旨在应用于一整套结果。
使用
mape()函数,我们现在可以理解我们的模型预测与测试集中的价格大约相差 4.7%。这相当于一个通过rmse()函数计算的 RSME,大约为 $596.6。在进入下一部分之前,返回到笔记本,找到
Re-train Model with TensorBoard这个标题。你可能已经注意到我们创建了一个名为train_model()的辅助函数。这个函数是我们模型的一个封装,负责训练(使用model.fit())我们的模型,并将结果存储在一个新的目录下。这些结果随后会被 TensorBoard 使用,用于显示不同模型的统计信息。 -
继续修改一些传递给
model.fit()函数的参数值(例如尝试调整 epochs)。然后,运行加载模型的单元格,将模型从磁盘载入内存(这将替换你训练的模型):model = load_model('bitcoin_lstm_v0.h5') -
现在,再次运行
train_model()函数,但使用不同的参数,表示一个新的运行版本。当你运行这个命令时,你将能够训练一个新的版本的模型,并在版本参数中指定该版本:train_model(model=model, X=X_train, Y=Y_train, \ epochs=10, version=0, run_number=0)注意
要访问该部分的源代码,请参考
packt.live/2ZhK4z3。你也可以在线运行这个示例,访问
packt.live/2Dvd9i3。你必须执行整个笔记本,才能得到预期的结果。
在这个练习中,我们学习了如何使用损失函数评估网络。我们了解到损失函数是神经网络的关键元素,因为它们评估网络在每一轮中的表现,并且是反向传播调整到各层和节点的起点。我们还探讨了为什么某些损失函数(例如,MSE 函数)可能难以解释,并通过使用另外两个函数——RMSE 和 MAPE,来解释我们 LSTM 模型的预测结果。
最重要的是,我们已经结束了这个练习,并创建了一个活跃的训练环境。现在我们有了一个可以持续训练深度学习模型并评估其结果的系统。当我们进入下一主题,优化网络时,这将是关键。
超参数优化
到目前为止,我们已经训练了一个神经网络,利用前 76 周的比特币价格预测接下来 7 天的比特币价格。平均而言,这个模型的预测与实际比特币价格的偏差约为 8.4%。
本节描述了提高神经网络模型性能的常见策略:
-
添加或移除层,调整节点数量
-
增加或减少训练的轮次
-
实验不同的激活函数
-
使用不同的正则化策略
我们将使用在模型评估章节末尾开发的相同主动学习环境来评估每一个修改,衡量这些策略如何帮助我们开发出更精确的模型。
层和节点 – 添加更多层
使用单一隐藏层的神经网络在许多问题上可以表现得相当不错。我们的第一个比特币模型(bitcoin_lstm_v0)就是一个很好的例子:它可以用一个 LSTM 层预测接下来的 7 天比特币价格(来自测试集),误差率约为 8.4%。然而,并非所有问题都可以用单一层来建模。
你所要预测的函数越复杂,你就越有可能需要添加更多的层。判断是否需要添加新层的一个好方法是理解它们在神经网络中的作用。
每一层都会对其输入数据创建一个模型表示。链中的早期层创建较低级的表示,而后续层创建更高级的表示。
尽管这个描述可能难以直接转化为现实世界的问题,但其实际直觉非常简单:在处理具有不同表示层次的复杂函数时,你可能希望尝试添加层。
添加更多节点
层所需的神经元数量与输入和输出数据的结构有关。例如,如果你正在处理一个二分类问题,要对一个 4 x 4 像素的图像进行分类,那么你可以尝试如下操作:
-
设置一个包含 12 个神经元的隐藏层(每个可用像素一个神经元)
-
设置一个只包含两个神经元的输出层(每个预测类别一个神经元)
通常,在添加新层的同时也会添加新神经元。然后,我们可以添加一个层,其神经元数量可以与前一层相同,或者是前一层神经元数量的倍数。例如,如果你的第一隐藏层有 12 个神经元,你可以尝试添加第二层,神经元数量为 12、6 或 24 个。
添加层和神经元可能会带来显著的性能限制。可以自由地尝试添加层和节点。通常,从一个较小的网络开始(即一个包含较少层和神经元的网络),然后根据性能提升进行扩展。
如果这听起来不够精确,你的直觉是正确的。
注意
引用Aurélien Géron(YouTube 前视频分类负责人)的话:“找到合适数量的神经元仍然有些像黑魔法。”
最后,提醒一句:你添加的层越多,超参数的调节就越多——网络训练所需的时间也会更长。如果你的模型表现良好且没有对数据过拟合,在添加新层之前,可以尝试本章节中概述的其他策略。
层和节点 – 实现
现在,我们将通过添加更多的层来修改原始 LSTM 模型。在 LSTM 模型中,我们通常将 LSTM 层按顺序添加,形成 LSTM 层之间的链条。在我们的例子中,新的 LSTM 层具有与原始层相同数量的神经元,因此我们不需要配置该参数。
我们将修改后的模型命名为 bitcoin_lstm_v1。通常的做法是根据不同的超参数配置命名每一个模型,这有助于你追踪每个不同架构的表现,同时也能在 TensorBoard 中方便地比较模型之间的差异。我们将在本章结束时比较所有不同的修改架构。
注意
在添加新的 LSTM 层之前,我们需要将第一个 LSTM 层的 return_sequences 参数设置为 True。我们这么做是因为第一个层期望接收与其输入相同的数据序列。当该参数设置为 False 时,LSTM 层输出的预测参数与原来的不兼容。
以下代码示例将第二个 LSTM 层添加到原始的 bitcoin_lstm_v0 模型中,生成 bitcoin_lstm_v1:
period_length = 7
number_of_periods = 186
batch_size = 1
model = Sequential()
model.add(LSTM(
units=period_length,
batch_input_shape=(batch_size, number_of_periods, period_length), \
input_shape=(number_of_periods, period_length), \
return_sequences=True, stateful=False))
model.add(LSTM(units=period_length,\
batch_input_shape=(batch_size, number_of_periods, \
period_length), \
input_shape=(number_of_periods, period_length), \
return_sequences=False, stateful=False))
model.add(Dense(units=period_length)) \
model.add(Activation("linear"))
model.compile(loss="mse", optimizer="rmsprop")
训练轮次
训练轮次是指网络在响应通过的数据及其导致的损失函数时调整其权重的次数。运行模型更多轮次可以让模型从数据中学到更多东西,但也有可能导致过拟合的风险。
在训练模型时,建议将训练轮次指数级增加,直到损失函数开始趋于平稳。在 bitcoin_lstm_v0 模型的情况下,它的损失函数大约在 100 轮时趋于平稳。
我们的 LSTM 模型使用的数据量较少,因此增加训练轮次不会显著影响其性能。例如,如果我们尝试在 103 轮时训练模型,几乎没有任何提升。如果所训练的模型使用的是庞大的数据量,情况就不同了。在这种情况下,大量的训练轮次对取得良好的性能至关重要。
我建议你遵循以下经验法则:用于训练模型的数据越大,所需的训练轮次就越多,以达到良好的性能。
训练轮次 - 实现
我们的比特币数据集相对较小,因此增加模型的训练轮次可能对其性能的影响很小。为了让模型训练更多轮次,我们只需要在 model.fit() 方法中更改 epochs 参数。在下面的代码片段中,你将看到如何更改模型的训练轮次:
number_of_epochs = 10**3
model.fit(x=X, y=Y, batch_size=1,\
epochs=number_of_epochs, \
verbose=0, \
callbacks=[tensorboard])
这个更改将我们的模型升级到 v2,有效地使其成为 bitcoin_lstm_v2。
激活函数
激活函数评估你需要多大程度地激活单个神经元。它们决定每个神经元将传递给网络下一个元素的值,这个值既包括来自上一层的输入,也包括损失函数的结果——或者决定一个神经元是否需要传递任何值。
注意
激活函数是神经网络研究领域中科学界关注的一个重要话题。有关当前研究概况以及激活函数如何工作的更详细审查,请参阅 Ian Goodfellow 等人所著《深度学习》,MIT 出版社,2017 年。
TensorFlow 和 Keras 提供了许多激活函数——并且偶尔会增加新的激活函数。作为介绍,以下三种是需要特别考虑的;让我们一起探讨它们。
注意
本节内容深受 Avinash Sharma V 撰写的文章 理解神经网络中的激活函数 的启发,文章可在 medium.com/the-theory-of-everything/understanding-activation-functions-in-neural-networks-9491262884e0 阅读。
线性(恒等)函数
线性函数仅基于常数值激活神经元。它们由以下方程定义:

图 3.13:线性函数的公式
在这里,c 是常数值。当 c = 1 时,神经元将按原样传递值,不需要激活函数进行任何修改。使用线性函数的问题在于,由于神经元是线性激活的,链式层现在作为一个单一的大层工作。换句话说,我们失去了构建多层网络的能力,在这些网络中,一个层的输出会影响另一个层:

图 3.14:线性函数的示意图
由于线性函数无法计算复杂特征,且不会在神经元中引入适当的非线性,因此它们通常被认为对于大多数网络来说已经过时。
双曲正切(Tanh)函数
Tanh 是一个非线性函数,其表示公式如下:

图 3.15:双曲正切函数的公式
这意味着它们对节点的影响是持续评估的。而且,由于其非线性特性,我们可以使用该函数来改变一个层如何影响链中的下一个层。当使用非线性函数时,层以不同方式激活神经元,使得从数据中学习不同的表示变得更加容易。然而,它们具有类似于 sigmoid 的模式,会重复惩罚极端节点值,从而导致一种称为梯度消失的问题。梯度消失对网络学习能力产生负面影响:

图 3.16:双曲正切函数的示意图
虽然 Tanh 是一种常见选择,但由于其计算开销较大,因此通常使用 ReLU 函数作为替代。
修正线性单元函数(ReLU)
ReLU 代表 修正线性单元。它滤除负值,仅保留正值。ReLU 函数通常被推荐作为在尝试其他函数之前的一个很好的起点。其公式如下:

图 3.17:ReLU 函数的公式
ReLU 具有非线性特性:

图 3.18:ReLU 函数的示意图
ReLU 倾向于惩罚负值。因此,如果输入数据(例如,标准化后在 -1 到 1 之间)包含负值,这些值将被 ReLU 惩罚。这可能不是预期的行为。
我们不会在网络中使用 ReLU 函数,因为我们的标准化过程会产生许多负值,导致学习模型变得更加缓慢。
激活函数 – 实现
在 Keras 中实现激活函数的最简单方法是实例化 Activation() 类并将其添加到 Sequential() 模型中。Activation() 可以用 Keras 中任何可用的激活函数来实例化(完整列表请参见 keras.io/activations/)。
在我们的案例中,我们将使用 tanh 函数。在实现激活函数后,我们将模型版本提升到 v2,使其成为 bitcoin_lstm_v3:
model = Sequential() model.add(LSTM(
units=period_length,\
batch_input_shape=(batch_size, \
number_of_periods, period_length), \
input_shape=(number_of_periods, \
period_length), \
return_sequences=True, \
stateful=False))
model.add(LSTM(units=period_length,\
batch_input_shape=(batch_size, number_of_periods, \
period_length), \
input_shape=(number_of_periods, period_length), \
return_sequences=False, stateful=False))
model.add(Dense(units=period_length)) \
model.add(Activation("tanh"))
model.compile(loss="mse", optimizer="rmsprop")
执行 compile 命令后,您的模型已经根据指定的层构建完毕,现在可以开始训练了。有许多其他激活函数值得尝试。TensorFlow 和 Keras 都在各自的官方文档中提供了已实现函数的列表。在实现自己的函数之前,可以先使用我们在 TensorFlow 和 Keras 中已经实现的函数。
正则化策略
神经网络特别容易发生过拟合。过拟合是指网络学习到训练数据的模式,但无法找到可以应用于测试数据的可泛化模式。
正则化策略指的是通过调整网络学习的方式来处理过拟合问题的技术。在接下来的章节中,我们将讨论两种常见的策略:
-
L2 正则化
-
Dropout
L2 正则化
L2 正则化(或 权重衰减)是处理过拟合模型的常用技术。在某些模型中,某些参数的变化幅度非常大。L2 正则化会惩罚这些参数,减少它们对网络的影响。
L2 正则化使用
参数来确定惩罚模型神经元的程度。我们通常将该值设置为非常低的数值(即 0.0001);否则,我们可能会完全消除给定神经元的输入。
Dropout
Dropout 是一种基于简单问题的正则化技术:如果我们随机去除一部分层中的节点,其他节点将如何适应? 结果证明,剩下的神经元会适应,学会表示那些原本由缺失神经元处理的模式。
Dropout 策略简单易实现,并且通常能有效避免过拟合。这将是我们首选的正则化方法。
正则化策略 – 实现
为了使用 Keras 实现 Dropout 策略,我们将导入Dropout()方法,并在每个 LSTM 层后立即将其添加到网络中。这个添加将使我们的网络变为bitcoin_lstm_v4。在这个代码片段中,我们将Dropout()步骤添加到我们的模型(bitcoin_lstm_v3)中,使其变为bitcoin_lstm_v4:
model = Sequential()
model.add(LSTM(\
units=period_length,\
batch_input_shape=(batch_size, number_of_periods, \
period_length), \
input_shape=(number_of_periods, period_length), \
return_sequences=True, stateful=False))
model.add(Dropout(0.2))
model.add(LSTM(\
units=period_length,\
batch_input_shape=(batch_size, number_of_periods, \
period_length), \
input_shape=(number_of_periods, period_length), \
return_sequences=False, stateful=False))
model.add(Dropout(0.2))
model.add(Dense(units=period_length))
model.add(Activation("tanh"))
model.compile(loss="mse", optimizer="rmsprop")
我们本可以使用 L2 正则化来替代 Dropout。Dropout 在每一轮中随机丢弃神经元,而 L2 正则化则惩罚那些具有高权重值的神经元。为了应用 L2 正则化,只需实例化ActivityRegularization()类,并将 L2 参数设置为一个较低的值(例如 0.0001)。然后,将它放入原本添加了Dropout()类的位置。可以自由尝试将其添加到网络中,同时保留两个Dropout()步骤,或者直接用ActivityRegularization()替换所有的Dropout()实例。
优化结果
总的来说,我们创建了四个版本的模型。三个版本,即bitcoin_lstm_v1、bitcoin_lstm_v2和bitcoin_lstm_v3,是通过应用本章中介绍的不同优化技术创建的。现在,我们需要评估哪个模型表现最好。为此,我们将使用与第一个模型相同的指标:MSE、RMSE 和 MAPE。MSE 用于比较模型在每一周预测中的误差率。RMSE 和 MAPE 则用于使模型结果更易解释。下表显示了这一点:

图 3.19:所有模型的结果
有趣的是,我们的第一个模型(bitcoin_lstm_v0)在几乎所有定义的指标中表现最好。我们将使用该模型来构建我们的 Web 应用程序,并持续预测比特币价格。
活动 3.01:优化深度学习模型
在本活动中,我们将在第二章中创建的模型上实现不同的优化策略,《真实世界的深度学习:使用 TensorFlow 和 Keras 预测比特币价格》(bitcoin_lstm_v0)。该模型在完整的反标准化测试集上实现了约 8.4%的 MAPE 性能。我们将尝试缩小这个差距,获得更准确的预测。
以下是步骤:
-
从终端启动 TensorBoard。
-
启动一个 Jupyter Notebook。
-
加载训练和测试数据,并将
lstm输入拆分为模型所需的格式。 -
在之前的练习中,我们创建了一个模型架构。复制该架构并添加一个新的 LSTM 层。编译并创建模型。
-
在步骤 4中更改 epoch 的数量,创建一个新模型。编译并创建新模型。
-
更改激活函数为
tanh或relu,并创建一个新模型。编译并训练新模型。 -
在 LSTM 层之后添加一个 dropout 层并创建新模型。保持 dropout 值为
0.2或0.3。编译并训练新模型。 -
评估并比较在本活动中训练的所有模型。
注意
该活动的解决方案可以在第 141 页找到。
总结
在本章中,我们学习了如何使用 MSE、RMSE 和 MAPE 指标来评估我们的模型。我们在第一次神经网络模型做出的 19 周预测中计算了后两个指标。通过这样做,我们了解到模型表现良好。
我们还学习了如何优化模型。我们研究了通常用于提高神经网络性能的优化技术。此外,我们实现了这些技术并创建了几个新模型,用不同的误差率预测比特币价格。
在下一章中,我们将把我们的模型转化为一个 Web 应用程序,该应用程序有两个功能:定期使用新数据重新训练我们的模型,并能通过 HTTP API 接口进行预测。
第四章:4. 产品化
概览
在本章中,你将处理新数据,并创建一个能够从展示的模式中持续学习并帮助做出更好预测的模型。我们将以网页应用程序为例,展示如何部署深度学习模型,不仅因为网页应用程序的简单性和普及性,还因为它们提供了不同的可能性,比如通过网页浏览器在移动设备上获取预测结果,并为用户制作 REST API。
介绍
本章的重点是如何将深度学习模型产品化。我们使用“产品化”这个词来定义从深度学习模型创建软件产品的过程,使其可以被其他人和应用程序使用。
我们关注的是那些使用新数据并在数据可用时进行学习的模型,这些模型能够从新数据中持续学习模式,并因此做出更好的预测。在本章中,我们将研究两种处理新数据的策略:一种是重新训练现有模型,另一种是创建一个完全新的模型。然后,我们将在比特币价格预测模型中实现后一种策略,以使其能够持续预测新的比特币价格。
在本章结束时,我们将能够部署一个可工作的网页应用程序(具有有效的 HTTP API),并根据需要进行修改。
处理新数据
模型可以使用一组数据进行一次性训练,然后用来进行预测。这类静态模型非常有用,但通常情况下,我们希望模型能够从新数据中持续学习——并在此过程中不断改进。
在本节中,我们将讨论处理新数据的两种策略,并展示如何在 Python 中实现它们。
数据和模型的分离
在构建深度学习应用程序时,最重要的两个领域是数据和模型。从架构角度来看,建议将这两个领域分开。我们认为这是一个好建议,因为这两个领域本质上包含了彼此独立的功能。数据通常需要收集、清洗、整理和标准化,而模型需要训练、评估,并能够进行预测。
根据这个建议,我们将使用两个不同的代码库来帮助构建我们的网页应用程序:Yahoo Finance API 和 Model():
-
Yahoo Finance API:可以使用以下命令通过
pip安装该 API:pip install yfinance安装完成后,我们将能够访问所有与金融领域相关的历史数据。
-
Model():这个类将我们目前所写的所有代码整合成一个单独的类。它提供了与我们先前训练的模型交互的功能,并允许我们使用去标准化的数据进行预测,这样更容易理解。Model()类是我们的模型组件。
这两个代码库在我们的示例应用程序中被广泛使用,定义了数据和模型组件。
数据组件
Yahoo Finance API 帮助检索并解析股票的历史数据。它包含一个相关方法 history(),其详细代码如下:
import yfinance as yf
ticker = yf.Ticker("BTC-USD")
historic_data = ticker.history(period='max')
这个 history() 方法从 Yahoo Finance 网站收集数据,解析数据并返回一个已准备好供 Model() 类使用的 pandas DataFrame。
Yahoo Finance API 使用参数 ticker 来确定要收集的加密货币。Yahoo Finance API 还提供了许多其他加密货币,包括以太坊(Ethereum)和比特币现金(Bitcoin Cash)等流行的加密货币。通过使用 ticker 参数,你可以更改加密货币,并训练一个与本书中创建的比特币模型不同的模型。
模型组件
Model() 类是我们实现应用程序模型组件的地方。Model() 类包含五个方法,这些方法实现了本书中的所有不同建模主题。它们分别是:
-
build():这个方法使用 TensorFlow 构建一个 LSTM 模型。该方法作为一个简单的包装器,用于手动创建的模型。 -
train():这个方法使用类实例化时提供的数据来训练模型。 -
evaluate():这个方法使用一组损失函数来评估模型。 -
save():这个方法将模型保存为本地文件。 -
predict():这个方法基于按周排序的观察输入序列进行预测并返回结果。
我们将在本章中使用这些方法来工作、训练、评估和发出预测。Model() 类是如何将 TensorFlow 的核心功能包装成 Web 应用程序的一个示例。前面提到的方法几乎可以像以前的章节一样实现,但有增强的接口。例如,以下代码中实现的 train() 方法使用来自 self.X 和 self.Y 的数据训练 self.model 中的模型:
def train(self, data=None, epochs=300, verbose=0, batch_size=1):
self.train_history = self.model.fit(x=self.X, y=self.Y, \
batch_size=batch_size, \
epochs=epochs, \
verbose=verbose, \
shuffle=False)
self.last_trained = datetime.now()\
.strftime('%Y-%m-%d %H:%M:%S')
return self.train_history
一般来说,Keras 工作流程中的每个过程(构建或设计、训练、评估和预测)都可以轻松地转化为程序中的独立部分。在我们的案例中,我们将它们转化为可以从 Model() 类调用的方法。这组织了我们的程序,并提供了一系列约束(例如关于模型架构或某些 API 参数的约束),帮助我们在稳定的环境中部署模型。
在接下来的章节中,我们将探讨处理新数据的常见策略。
处理新数据
机器学习模型的核心理念(包括神经网络)是它们可以从数据中学习模式。假设一个模型已经使用某个数据集进行训练,现在它开始进行预测。现在,假设有新的数据可用。你可以采用不同的策略,让模型利用这些新数据学习新模式并改进其预测。在本节中,我们将讨论两种策略:
-
重新训练旧模型
-
训练新模型
重新训练旧模型
在这个策略中,我们通过使用新数据来重新训练现有模型。通过这种策略,你可以持续调整模型参数,以适应新的现象。然而,后期训练期间使用的数据可能与早期数据有显著差异。这些差异可能导致模型参数发生重大变化,比如让模型学习新的模式并遗忘旧的模式。这种现象通常被称为灾难性遗忘。
注意
灾难性遗忘是一个常见的现象,影响着神经网络。深度学习研究人员多年来一直在努力解决这个问题。DeepMind(一个来自英国、隶属于谷歌的深度学习研究团队)在寻找解决方案方面取得了显著进展。文章《克服神经网络中的灾难性遗忘》,James Kirkpatrick 等人,提供了很好的参考,相关资料可以在arxiv.org/pdf/1612.00796.pdf找到。
用于第一次训练的接口(model.fit())也可以用于用新数据进行训练。以下代码段加载数据并帮助训练一个模型,指定了训练的轮数(epochs)和批量大小(batch size):
X_train_new, Y_train_new = load_new_data()
model.fit(x=X_train_new, y=Y_train_new, batch_size=1, \
epochs=100, verbose=0)
在 TensorFlow 中,当模型训练时,模型的状态会作为权重保存在磁盘上。当你使用 model.save() 方法时,该状态也会被保存。而当你调用 model.fit() 方法时,模型会使用新的数据集进行重新训练,且以之前的状态作为起点。
在典型的 Keras 模型中,这种技术可以正常使用而不会遇到问题。然而,在处理 LSTM 模型时,这种技术有一个关键限制:训练数据和验证数据的形状必须相同。例如,在第三章,使用 TensorFlow 和 Keras 进行真实世界深度学习:评估比特币模型中,我们的 LSTM 模型(bitcoin_lstm_v0)使用了 186 周的数据来预测未来的一周。如果我们尝试用 187 周的数据来重新训练模型以预测下一周,模型会抛出异常,并提供有关数据形状不正确的信息。
解决这个问题的一种方法是将数据安排成模型所期望的格式。例如,为了基于一年的数据(52 周)进行预测,我们需要配置一个模型,用 40 周的数据预测未来一周。在这种情况下,我们首先使用 2019 年初的 40 周数据训练模型,然后继续在接下来的周数中进行重新训练,直到达到第 51 周。我们使用 Model() 类在以下代码中实现了这一重新训练技术:
M = Model(data=model_data[0*7:7*40 + 7], variable='close', \
predicted_period_size=7)
M.build()
M.train()
for i in range(41, 52):
j = i - 40
M.train(model_data.loc[j*7:7*i + 7])
这种技术通常训练速度较快,并且适用于较大的序列数据。接下来的技术更容易实现,并且在较小的序列中表现良好。
训练新模型
另一种策略是每当有新数据时,创建并训练一个新模型。这种方法通常能减少灾难性遗忘,但随着数据量的增加,训练时间也会增加。其实现相当简单。
以比特币模型为例,假设我们有 2019 年 49 周的旧数据,并且在一周后,新的数据可用。我们通过以下代码片段中的old_data和new_data变量来表示这一点,并在新数据可用时实现训练新模型的策略:
old_data = model_data[0*7:7*48 + 7]
new_data = model_data[0*7:7*49 + 7]
M = Model(data=old_data,\
variable='close', predicted_period_size=7)
M.build()
M.train()
M = Model(data=new_data,\
variable='close', predicted_period_size=7)
M.build()
M.train()
这种方法非常简单,通常适用于小型数据集。这将是我们比特币价格预测应用的首选解决方案。
练习 4.01: 动态重新训练模型
在这个练习中,你需要重新训练一个模型,使其具备动态功能。每当加载新数据时,模型应能根据数据做出预测。以下是需要遵循的步骤:
首先导入 cryptonic。Cryptonic 是一本书中为实现这一节的步骤而开发的简单软件应用,使用 Python 类和模块实现了所有步骤。可以将 Cryptonic 视为创建应用程序的模板。作为本练习的 Python 模块,Cryptonic 可以在以下 GitHub 链接找到:packt.live/325WdZQ。
-
首先,我们将启动一个 Jupyter Notebook 实例,然后加载
cryptonic包。 -
使用终端,导航到
Chapter04/Exercise4.01目录,并执行以下代码来启动 Jupyter Notebook 实例:$ jupyter-lab服务器会自动在浏览器中打开,然后打开名为
Exercise4.01_Re_training_a_model_dynamically.ipynb的 Jupyter Notebook。 -
现在,我们将从
cryptonic包中导入类:Model()和 Yahoo Finance API。这些类简化了操作我们模型的过程。 -
在 Jupyter Notebook 实例中,导航到标题为
Fetching Real-Time Data的部分。我们将通过调用history()方法,从 Yahoo Finance 获取更新的历史数据:import yfinance as yf ticker = yf.Ticker("BTC-USD") historic_data = ticker.history(period='max')historic_data变量现在包含一个 pandas DataFrame,其中包含比特币汇率的历史数据,直到运行此代码的时刻为止。这非常好,并且使得在获取更多数据时重新训练我们的模型变得更容易。 -
你可以使用以下命令查看存储在
historic_data中的前三行数据:historic_data.head(3)然后,你可以查看存储在
historic_data中的数据:![图 4.1: 显示数据头部的输出]()
图 4.1: 显示数据头部的输出
数据包含与我们使用的比特币数据集几乎相同的变量。然而,大部分数据来自较早的时间段,即 2017 年到 2019 年。
-
使用 pandas API,筛选出 2019 年可用的数据,并将其存储在
model_data中。你应该能够通过使用日期变量作为筛选索引来实现这一点。确保在继续之前已经过滤好数据:start_date = '01-01-2019' end_date = '31-12-2019' mask = ((historic_data['date'] \ >= start_date) & (historic_data['date'] \ <= end_date)) model_data = historic_data[mask]在下一个单元格中运行
model_data,输出模型如下所示:![图 4.2: model_data 变量显示历史数据]()
图 4.2: model_data 变量显示历史数据
Model()类将我们在所有活动中编写的所有代码进行了汇编。我们将在本活动中使用该类来构建、训练和评估我们的模型。 -
我们将使用筛选后的数据来训练模型:
M = Model(data=model_data, \ variable='close', predicted_period_size=7) M.build() M.train() M.predict(denormalized=True) -
运行以下命令查看训练后的模型:
M.train(epochs=100, verbose=1)训练后的模型显示在以下截图中:
![图 4.3:显示我们训练好的模型的输出]()
图 4.3:显示我们训练好的模型的输出
上述步骤展示了使用
Model()类训练模型时的完整工作流。注意
完整代码请参阅
Chapter04/Exercise4.01文件夹。 -
接下来,我们将重点关注每次新数据可用时重新训练我们的模型。这样可以根据新数据调整网络权重。
为此,我们已将模型配置为使用 40 周来预测一周。现在我们想利用剩余的 11 周完整数据来创建 40 周的重叠周期。每个周期包括这 11 周中的一个,并为每个周期重新训练模型。
-
在 Jupyter Notebook 中导航到
Re-Train Old Model标题。现在,使用索引完成range函数和model_data筛选参数,按七天为一组进行数据拆分,形成重叠组。然后,重新训练我们的模型并收集结果:results = [] for i in range(A, B): M.train(model_data[C:D]) results.append(M.evaluate())A、B、C和D变量是占位符。使用整数来创建重叠的七天组,其中重叠部分为一天。将这些占位符替换为周数后,我们按照以下方式运行循环:
results = [] for i in range(41, 52): j = i-40 print("Training model {0} for week {1}".format(j,i)) M.train(model_data.loc[j*7:7*i+7]) results.append(M.evaluate())这是显示该循环结果的输出:
Training model 1 for week 41 Training model 2 for week 42 Training model 3 for week 43 Training model 4 for week 44 Training model 5 for week 45 Training model 6 for week 46 Training model 7 for week 47 Training model 8 for week 48 Training model 9 for week 49 Training model 10 for week 50 Training model 11 for week 51 -
重新训练模型后,继续调用
M.predict(denormalized=True)函数并检查结果:array([7187.145 , 7143.798 , 7113.7324, 7173.985 , 7200.346 , 7300.2896, 7175.3203], dtype=float32) -
接下来,我们将专注于每次有新数据时创建和训练一个新模型。为此,我们假设我们有 2019 年 49 周的旧数据,并且过了一周后我们有了新数据。我们用
old_data和new_data变量表示这一点。 -
导航到
New Data New Model标题,并将数据分为old_data和new_data两个变量:old_data = model_data[0*7:7*48 + 7] new_data = model_data[0*7:7*49 + 7] -
然后,首先使用
old_data训练模型:M = Model(data=old_data,\ variable='close', predicted_period_size=7) M.build() M.train()现在我们已经拥有了训练动态模型所需的所有组成部分。
注意
要访问该特定部分的源代码,请参考
packt.live/2AQb3bE。你也可以在线运行这个示例,访问
packt.live/322KuLl。你必须执行整个 Notebook 才能获得期望的结果。
在下一部分中,我们将把模型部署为 Web 应用程序,使其预测结果通过 HTTP API 在浏览器中可用。
将模型部署为 Web 应用程序
在本节中,我们将把模型部署为 Web 应用程序。我们将使用 Cryptonic Web 应用程序来部署模型,探索其架构,以便我们将来能够进行修改。我们的目的是让您使用此应用程序作为更复杂应用程序的起点——一个完全可用并可以根据需要扩展的起点。
除了熟悉 Python,本主题还假设您了解创建 Web 应用程序的相关知识。具体来说,我们假设您对 Web 服务器、路由、HTTP 协议和缓存有一定的了解。您可以在没有深入了解 Web 服务器、HTTP 协议和缓存的情况下,本地部署所演示的 Cryptonic 应用程序,但学习这些知识将使未来的开发更加轻松。
最后,Docker 用于部署我们的 Web 应用程序,因此对该技术的基本了解也是有帮助的。
在继续之前,请确保您的计算机上已安装并可以使用以下应用程序:
-
Docker(社区版)17.12.0-ce 或更高版本
-
Docker Compose (
docker-compose) 1.18.0 或更高版本
这两个组件可以从 docker.com/ 下载并安装在所有主要系统上。这些组件对于完成本次活动至关重要。确保在继续之前,它们已经安装并可在系统中使用。
应用程序架构与技术
为了部署我们的 Web 应用程序,我们将使用 图 4.4 中描述的工具和技术。Flask 是关键,因为它帮助我们为模型创建一个 HTTP 接口,使我们能够访问 HTTP 端点(例如 /predict)并以通用格式接收数据。其他组件之所以被使用,是因为它们在开发 Web 应用程序时非常流行:

图 4.4:用于部署深度学习 Web 应用程序的工具和技术
这些组件按如下图所示组合在一起:

图 4.5:本项目中构建的 Web 应用程序的系统架构
用户通过浏览器访问 Web 应用程序。然后,流量由 Nginx 路由到包含 Flask 应用程序的 Docker 容器中(默认情况下,运行在 5000 端口)。Flask 应用程序在启动时已实例化我们的比特币模型。如果指定了一个模型,它将使用该模型而不进行训练;如果没有,它会创建一个新模型,并使用来自 Yahoo Finance 的数据从头开始训练。
在模型准备好之后,应用程序会验证请求是否已缓存到 Redis 中;如果缓存存在,它会返回缓存的数据。如果没有缓存,它将继续进行预测,并在用户界面中渲染结果。
练习 4.02:部署和使用 Cryptonic
Cryptonic 被开发为一个容器化应用程序。在 Docker 术语中,这意味着该应用程序可以作为 Docker 镜像构建,然后在开发或生产环境中作为 Docker 容器部署。
在这个练习中,我们将学习如何使用 Docker 和 Cryptonic 部署应用程序。在开始之前,请从 www.docker.com/products/docker-desktop 下载 Docker Desktop,确保在开始练习之前该应用程序已在后台运行。
注意
该练习的完整代码可以在 packt.live/2AM5mLP 找到。
-
Docker 使用名为
Dockerfile的文件来描述如何构建镜像的规则以及当镜像作为容器部署时会发生什么。Cryptonic 的 Dockerfile 可在以下代码中找到:FROM python:3.6 ENV TZ=America/New_York """ Setting up timezone to EST (New York) Change this to whichever timezone your data is configured to use. """ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone COPY . /cryptonic WORKDIR "/cryptonic" RUN pip install -r requirements.txt EXPOSE 5000 CMD ["python", "run.py"] -
可以使用以下命令构建 Docker 镜像:
$ docker build --tag cryptonic:latest此命令将使
cryptonic:latest镜像可用于作为容器部署。构建过程可以在生产服务器上重复执行,或者直接部署该镜像并作为容器运行。 -
在镜像构建完成并可用后,您可以使用
docker run命令运行 Cryptonic 应用程序,如下所示的代码所示:$ docker run --publish 5000:5000 \ --detach cryptonic:latest--publish标志将本地5000端口绑定到 Docker 容器的5000端口,--detach使容器在后台以守护进程模式运行。如果您已经训练了一个不同的模型,并且希望使用该模型而不是训练新模型,您可以在
docker-compose.yml文件中修改MODEL_NAME环境变量。该变量应包含您训练并希望提供的模型的文件名(例如,bitcoin_lstm_v1_trained.h5);它也应该是一个 Keras 模型。如果您这样做,请确保将本地目录挂载到/models文件夹中。您决定挂载的目录必须包含您的模型文件。Cryptonic 应用程序还包括一些在部署您自己的模型时可能会用到的环境变量:
MODEL_NAME:允许我们提供一个经过训练的模型供应用程序使用。BITCOIN_START_DATE:确定用于比特币系列的起始日期。近年来,比特币价格的波动比早期更大。此参数过滤数据,仅保留感兴趣年份的数据。默认值为2017 年 1 月 1 日。PERIOD_SIZE:设置以天为单位的周期大小。默认值为7。EPOCHS:配置模型在每次运行时训练的轮数。默认值为300。这些变量可以在
docker-compose.yml文件中进行配置。该文件的一部分在以下代码片段中显示:version: "3" services: cache: image: cryptonic-cache:latest build: context: ./cryptonic-cache dockerfile: ./Dockerfile volumes: - $PWD/cache_data:/data networks: - cryptonic cryptonic: image: cryptonic:latest build: context: . dockerfile: ./Dockerfile ports: - "5000:5000" environment: - BITCOIN_START_DATE=2019-01-01 - EPOCH=50 - PERIOD_SIZE=7 -
部署 Cryptonic 最简单的方法是使用仓库中的
docker-compose.yml文件 (packt.live/2AM5mLP)。这个文件包含了应用程序运行所需的所有规范,包括如何连接 Redis 缓存以及使用哪些环境变量的说明。导航到
docker-compose.yml文件所在的位置后,可以通过docker-compose up命令启动 Cryptonic,示例如下代码:$ docker-compose up -d-d标志会将应用程序在后台执行。 -
部署后,Cryptonic 可以通过 Web 浏览器在端口
5000访问。该应用程序具有一个 HTTP API,在调用时会进行预测。API 有一个端点/predict,该端点返回一个包含未来一周比特币价格预测的去归一化 JSON 对象。以下是显示/predict端点的示例 JSON 输出片段:{ message: "API for making predictions.", period_length: 7, result: [ 15847.7, 15289.36, 17879.07, … 17877.23, 17773.08 ], success: true, 7 }注意
要访问此特定章节的源代码,请参考
packt.live/2ZZlZMm。本节目前没有在线互动示例,需要在本地运行。
现在可以将应用程序部署到远程服务器上,然后你可以使用它持续预测比特币价格。在接下来的活动中,你将部署一个应用程序。
活动 4.01:部署深度学习应用程序
在本节中,基于到目前为止解释的概念,尝试将模型作为本地 Web 应用程序进行部署。你需要按照以下步骤进行:
-
导航到
cryptonic目录。 -
为所需组件构建 Docker 镜像。
-
更改
docker-compose.yml中的必要参数。 -
在本地使用 Docker 部署应用程序。
预期输出如下:

图 4.6:预期输出
注意
本活动的解决方案可以在第 150 页找到。
总结
本课程结束了我们创建深度学习模型并将其作为 Web 应用程序部署的旅程。我们的最后一步包括部署一个使用 Keras 和 TensorFlow 引擎构建的比特币价格预测模型。我们通过将应用程序打包为 Docker 容器并进行部署,使得其他人可以通过其 API 使用我们模型的预测结果,以及其他应用程序。
除此之外,你还学到了很多可以改进的地方。我们的比特币模型只是一个模型可以做的示例(特别是 LSTM)。现在的挑战有两个:如何让模型随着时间推移表现得更好?你还可以为 Web 应用程序添加哪些功能,使得你的模型更易于访问?通过你在本书中学到的概念,你将能够开发模型并不断改进它们,以实现更精确的预测。
附录
第一章:神经网络与深度学习简介
活动 1.01:使用不同的超参数训练神经网络
解决方案:
-
使用终端,导航至从
packt.live/2ZVyf0C克隆的目录,并执行以下命令启动 TensorBoard:$ tensorboard --logdir logs/fit输出如下:
![图 1.15:启动 TensorBoard 实例后的终端截图]()
图 1.15:启动 TensorBoard 实例后的终端截图
-
现在,在浏览器中打开 TensorBoard 提供的 URL。你应该能看到 TensorBoard
SCALARS页面:![图 1.16:TensorBoard SCALARS 页面截图]()
图 1.16:TensorBoard SCALARS 页面截图
-
在 TensorBoard 页面中,点击
SCALARS页面并放大epoch_accuracy图表。现在,将平滑滑块移至0.6。准确率图表衡量了网络预测测试集标签的准确性。最初,网络完全错误地预测这些标签。这是因为我们用随机值初始化了网络的权重和偏置,所以它的第一次尝试只是猜测。然后,网络会在第二次运行时改变它层的权重和偏置;网络将继续通过改变权重和偏置来投资于那些给出正面结果的节点,并通过逐渐减少它们对网络的影响来惩罚那些没有效果的节点(最终达到
0)。如你所见,这是一种高效的技术,能迅速取得良好的结果。 -
要通过更改不同的超参数训练另一个模型,请在
Chapter01/Activity1.01中打开终端,激活环境。修改mnist.py中的以下几行:learning_rate = 0.0001 (at line number 47) epochs = 10 (at line number 56)mnist.py文件将如下所示:![图 1.17:mnist.py 文件截图以及需要更改的超参数]()
图 1.17:mnist.py 文件截图以及需要更改的超参数
-
现在,为这个新训练的模型重复 步骤 1-3。启动 TensorFlow,打开 TensorBoard 上看到的 URL 中的
Scalar页面,并查看epoch_accuracy图表。你将看到与之前图表的差异:![图 1.18:TensorBoard 截图,显示步骤 4 中指定的参数]()
图 1.18:TensorBoard 截图,显示步骤 4 中指定的参数
-
现在重复 步骤 4。在
Chapter01/Activity1.01中打开终端,激活环境。将mnist.py中的以下几行修改为以下值:learning_rate = 0.01 (at line number 47) epochs = 5 (at line number 56)可视化结果。你将得到如下图表:
![图 1.19:TensorBoard 图表截图]()
图 1.19:TensorBoard 图表截图
现在尝试使用你的自定义值运行模型,看看图表如何变化。
注意
请参考 mnist.py 文件,网址:packt.live/2ZVyf0C。
你可以修改神经网络中的许多其他参数。目前,先尝试调整网络的训练轮数(epochs)和学习率(learning rate)。你会发现,单独调整这两个参数就能显著改变网络的输出——但变化幅度有限。尝试通过仅调整这两个参数,看是否能在当前架构下更快地训练网络。
注意
要访问本节的源代码,请参阅packt.live/3eiFdC3。
本节目前没有在线互动示例,需要在本地运行。
使用 TensorBoard 验证你的网络训练情况。通过将初始值乘以 10 多次来调整这些参数,直到你注意到网络在改进。这个过程,即调整网络并找到更高准确度,实际上就是今天工业应用中用于改进现有神经网络模型的方式。
第二章:现实世界中的深度学习:预测比特币的价格
活动 2.01:组建深度学习系统
解决方案:
我们将继续使用 Jupyter Notebooks 和第二章之前练习中准备的数据(data/train_dataset.csv),以及我们本地保存的模型(bitcoin_lstm_v0.h5):
-
导入加载和训练深度学习模型所需的库:
import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline from tensorflow.keras.models import load_model plt.style.use('seaborn-white')注意
close_point_relative_normalization变量将用于训练我们的 LSTM 模型。我们将首先加载在之前活动中准备的数据集。我们将使用 pandas 将该数据集加载到内存中。
-
使用 pandas 将训练数据集加载到内存中,如下所示:
train = pd.read_csv('data/train_dataset.csv') -
现在,快速检查数据集,执行以下命令:
train.head()如本章所述,LSTM 网络需要三维的张量。这三维分别是周期长度、周期数和特征数。
现在,继续创建每周分组,然后重新排列结果数组以匹配这些维度。
-
随时使用提供的
create_groups()函数来执行此操作:create_groups(data=train, group_size=7)该函数的默认值是
7天。现在,确保将数据分为两组:训练集和验证集。我们通过将比特币价格数据集的最后一周分配给评估集来做到这一点。然后,我们训练网络以评估最后一周的数据。将训练数据的最后一周分开,并使用
numpy.reshape()重新调整其形状。重新调整形状很重要,因为 LSTM 模型只接受以这种方式组织的数据:X_train = data[:-1,:].reshape(1, 186, 7) Y_validation = data[-1].reshape(1, 7)现在我们的数据已经准备好用于训练。接下来,我们加载之前保存的模型,并用指定的训练轮数对其进行训练。
-
导航到
Load Our Model标题并加载我们之前训练的模型:model = load_model('bitcoin_lstm_v0.h5') -
现在,使用我们的训练数据
X_train和Y_validation来训练该模型:%%time history = model.fit( x=X_train, y=Y_validation, epochs=100)请注意,我们将模型的日志存储在一个名为
history的变量中。模型日志对于探索其训练精度的具体变化以及观察损失函数的表现非常有用:![图 2.27:Jupyter Notebook 的一部分,其中我们加载了之前的模型 并用新数据对其进行训练]()
图 2.27:Jupyter Notebook 的一部分,其中我们加载了之前的模型并用新数据进行训练
-
最后,让我们用训练好的模型进行预测。使用相同的数据
X_train,调用以下方法:model.predict(x=X_train) -
模型立即返回一个包含下 7 天预测的归一化值的列表。使用
denormalize()函数将数据转换为美元值。使用最新可用的值作为参考,来缩放预测结果:denormalized_prediction = denormalize(predictions, \ last_weeks_value)输出结果如下:
![图 2.28:未来 7 天比特币价格预测 使用我们刚刚构建的 LSTM 模型]()
图 2.28:使用我们刚构建的 LSTM 模型预测未来 7 天比特币价格
注意
我们在这张图中结合了两个时间序列:真实数据(垂直线之前)和预测数据(垂直线之后)。模型展示了与之前看到的模式相似的变化,并且暗示在接下来的 7 天内价格将会上涨。
-
当你完成实验后,使用以下命令保存你的模型:
model.save('bitcoin_lstm_v0_trained.h5')我们将保存这个训练好的网络,以备将来参考,并与其他模型的性能进行比较。
网络可能已经从我们的数据中学习到了某些模式,但它如何能在如此简单的架构和如此少的数据下做到这一点呢?LSTM 是从数据中学习模式的强大工具。然而,在接下来的课程中我们将学习到,LSTM 也可能会遭遇过拟合问题,这是一种神经网络常见的现象,表现为它们从训练数据中学习到的模式在预测现实世界中的模式时没有用处。我们将学习如何应对这种情况,并改进我们的网络,使其能够做出有用的预测。
注意
要访问此特定部分的源代码,请参考packt.live/2ZWfqub。
你也可以在线运行这个示例,网址是packt.live/3gIhhcT。你必须执行整个 Notebook 才能获得期望的结果。
第三章:现实世界深度学习:评估比特币模型
活动 3.01:优化深度学习模型
解决方案:
-
使用终端,通过执行以下命令启动 TensorBoard 实例:
$ cd ./Chapter03/Activity3.01/ $ tensorboard --logdir=logs/当 TensorBoard 在浏览器中打开时,你将看到
SCALARS页面:![图 3.20:TensorBoard 显示 SCALARS 页面的截图]()
图 3.20:TensorBoard 显示 SCALARS 页面的截图
-
打开屏幕上出现的网址,并保持该浏览器标签页打开。同时,使用以下命令启动一个 Jupyter Notebook 实例:
$ jupyter-lab这是显示 Jupyter Notebook 的截图:
![图 3.21:Jupyter Notebook]()
图 3.21:Jupyter Notebook
打开出现在不同浏览器窗口中的 URL。
-
现在,打开名为
Activity3.01_Optimizing_a_deep_learning_model.ipynb的 Jupyter Notebook,导航到 Notebook 的标题。运行单元格以导入所有必需的库。 -
设置种子以避免随机性:
np.random.seed(0)我们将像之前的活动中那样加载训练数据和测试数据。我们还将使用
split_lstm_input()工具函数将其分为训练组和测试组:![图 3.22:显示加载数据集结果的截图]()
图 3.22:显示加载数据集结果的截图
在这个 Notebook 的每个部分,我们将实现新的优化技术到模型中。每次进行优化时,我们都会训练一个新的模型,并将其训练后的实例存储在一个描述模型版本的变量中。例如,我们的第一个模型
bitcoin_lstm_v0在这个 Notebook 中被称为model_v0。在 Notebook 的最后,我们将使用 MSE、RMSE 和 MAPE 来评估所有模型。 -
要使这些模型运行,请执行参考模型部分下的单元格。
-
现在,在打开的 Jupyter Notebook 中,导航到
添加层和节点标题。你将在下一个单元格中看到我们的第一个模型。这是我们在第二章中构建的基本 LSTM 网络,《使用 TensorFlow 和 Keras 进行真实世界深度学习:预测比特币价格》。现在,我们需要为这个网络添加一个新的 LSTM 层:![图 3.23:带有添加新 LSTM 层代码的 Jupyter Notebook]()
图 3.23:带有添加新 LSTM 层代码的 Jupyter Notebook
利用你在本章中学到的知识,继续添加一个新的 LSTM 层,然后编译并训练模型。
在训练模型时,记得频繁访问正在运行的 TensorBoard 实例。你将能够看到每个模型运行并比较其损失函数的结果:
![图 3.24:不同模型的损失函数输出]()
图 3.24:不同模型的损失函数输出
TensorBoard 实例显示了许多不同的模型运行。TensorBoard 对于实时跟踪模型训练非常有用。
-
在本节中,我们关注探索不同规模的训练周期(epoch)。使用
train_model()工具函数为不同的模型版本和运行命名:train_model(model=model_v0, X=X_train, \ Y=Y_validate, epochs=100, \ version=0, run_number=0)使用不同的 epoch 参数训练模型。
此时,你要确保模型不会对训练数据发生过拟合。你需要避免这种情况,因为如果模型过拟合,它将无法预测在训练数据中已表示但在测试数据中具有不同表示的模式。
在完成对 epoch 的实验后,继续进行下一个优化技术:激活函数。
-
现在,导航到 Notebook 中的
激活函数标题。在此部分,你只需更改以下变量:activation_function = "relu”本节中我们使用了
tanh函数,但您可以尝试其他激活函数。查看keras.io/activations/上的可用列表,尝试其他选项。我们的最终选择是尝试不同的正则化策略。这通常更复杂,可能需要几次迭代才能看到任何改进,尤其是在数据较少的情况下。此外,添加正则化策略通常会增加网络的训练时间。
-
现在,导航到笔记本中的
Regularization Strategies章节。在本节中,您需要实现Dropout()正则化策略。找到合适的位置将此步骤添加到我们的模型中:![图 3.25:显示正则化策略代码的 Jupyter 笔记本]()
图 3.25:显示正则化策略代码的 Jupyter 笔记本
-
您还可以在此尝试 L2 正则化(或将两者结合)。像使用
Dropout()一样操作,但现在使用ActivityRegularization(l2=0.0001)。最后,让我们使用 RMSE 和 MAPE 来评估我们的模型。
-
现在,导航到笔记本中的
Evaluate Models章节。在本节中,我们将评估模型对测试集下一个 19 周数据的预测结果。然后,我们将计算预测序列与测试序列的 RMSE 和 MAPE。第一个图表如下所示:

图 3.26:预测序列与测试序列 #1
第二个图表如下所示:

图 3.27:预测序列与测试序列 #2
第三个图表如下所示:

图 3.28:预测序列与测试序列 #3
第四个图表如下所示:

图 3.29:预测序列与测试序列 #4
第五个图表如下所示:

图 3.30:预测序列与测试序列 #5
我们实现了与练习 2.01、探索比特币数据集,packt.live/3ehbgCi中相同的评估技术,所有内容都封装在工具函数中。只需运行本节的所有单元格,直到笔记本结束,就能看到结果。
注意
若要访问此特定章节的源代码,请参考 packt.live/2ZgAo87。
您还可以在 packt.live/3ft5Wgk 在线运行此示例。必须执行整个笔记本才能获得预期的结果。
在本次活动中,我们使用了不同的评估技术来获得更准确的结果。我们尝试了更多的训练周期,改变了激活函数,添加了正则化,并在不同场景中比较结果。利用这个机会调整前述优化技术的参数,尝试超越该模型的表现。
第四章:产品化
活动 4.01:部署深度学习应用
解决方案:
在本次活动中,我们将模型作为 Web 应用程序本地部署。这使我们可以通过浏览器连接到 Web 应用程序,或者通过应用程序的 HTTP API 使用其他应用程序。你可以在packt.live/2Zdor2S找到本活动的代码。
-
使用你的终端,导航到
cryptonic目录并构建所有所需组件的 Docker 镜像:$ docker build --tag cryptonic:latest . $ docker build --tag cryptonic-cache:latest cryptonic-cache/这两个命令构建了我们将在此应用中使用的两个镜像:
cryptonic(包含 Flask 应用)和cryptonic-cache(包含 Redis 缓存)。 -
构建完镜像后,找到
docker-compose.yml文件并用文本编辑器打开它。将BITCOIN_START_DATE参数更改为2017-01-01以外的日期:BITCOIN_START_DATE = # Use other date here -
最后一步,使用
docker-compose up在本地部署你的 Web 应用,如下所示:docker-compose up你应该能看到终端中的活动日志,其中包括模型完成的训练周期。
-
在模型训练完成后,你可以访问
http:// localhost:5000并在http://localhost:5000/predict进行预测:![图 4.7:Cryptonic 应用程序本地部署的截图]()
图 4.7:Cryptonic 应用程序本地部署的截图
注意
要访问本节的源代码,请参考packt.live/2Zg0wjd。
本节目前没有在线交互示例,需要在本地运行。








































浙公网安备 33010602011771号