移动人工智能项目(一)

原文:annas-archive.org/md5/24329c3bf9f3c672f8b5b2fa4cd1802e

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

我们正见证着人工智能AI)的革命,这要归功于深度学习的突破。移动人工智能项目使你能够通过应用 AI 技术来设计自然语言处理(NLP)、机器人技术和计算机视觉的应用,参与这一革命。

本书将教你如何在移动应用中利用 AI 的力量,并学习 NLP、神经网络、深度学习和移动视觉 API 的核心功能。本书包含一系列项目,涵盖自动推理、人脸识别、数字助手、自动文本生成和个性化新闻与故事等任务。你将学习如何利用 NLP 和机器学习算法使应用更具预测性、主动性,并能在较少的人为干预下做出自主决策。在最后几章中,你将使用如 TensorFlow Lite、CoreML 和 Snapdragon 神经处理引擎NPE)等流行库,跨 Android 和 iOS 平台进行实践。

到本书结束时,你将能够开发出令人兴奋且直观的移动应用程序,为用户提供定制化和个性化的体验。

本书适合的人群

移动人工智能项目适合机器学习专家、深度学习工程师、AI 工程师以及希望将 AI 技术集成到移动平台和应用中的软件工程师。

本书内容简介

第一章,人工智能概念与基础,涵盖了构建移动端或网页端 AI 应用所需的主要概念和高级理论。我们将讨论人工神经网络ANNs)和深度学习的基础知识,它们构成了当前 AI 研究和趋势的核心。我们将理解构建 AI 应用所需的所有基本术语,开启我们的 AI 应用构建之旅。

第二章,创建房地产价格预测移动应用程序,是本书的实用入门部分。我们将介绍全书中使用的所有必要工具和库,并考虑如何搭建深度学习环境。我们将通过展示如何使用 TensorFlow 构建一个房地产价格预测应用,并将其部署到移动端和网页端,来介绍这些内容。为此,我们将使用人工神经网络(ANNs)和 TensorFlow。

第三章,实现深度网络架构识别手写数字,聚焦于机器视觉相关的基本理论、术语和概念。我们将通过实践应用机器视觉,直观理解卷积神经网络(CNNs)的工作原理。我们将学习如何构建实际应用于机器视觉的应用程序。本章将注重直观理解和应用,而非理论分析。

第四章,构建机器视觉移动应用分类花卉物种,让我们能够将前几章的学习转化为构建一个物体识别应用,并进一步定制该应用,用来分类超过 100 种花卉物种,并展示它们的维基页面。我们将学习如何重新训练现有的深度网络架构,以适应物体和图像分类的定制用例。

第五章,使用 TensorFlow 构建 ML 模型预测汽车损伤,专注于图像修复的无监督任务。它讨论了用于这些任务的深度网络和库。我们将探讨深度学习中用于解决这些任务的技术,并逐一执行这些任务,之后将设置并运行来自安卓和 iOS 应用的图像修复。

第六章,基于 PyTorch 的 NLP 与 RNN 实验,重点介绍了递归神经网络RNNs)的工作原理及人工智能(AI)和自然语言处理(NLP)的应用。我们将深入探讨如何实际解决 AI 中的 NLP 用例。

第七章,基于 TensorFlow 的移动端语音转文本与 WaveNet 模型,在这一章中,我们将学习如何使用 WaveNet 模型将音频转换为文本。然后,我们将构建一个模型,利用安卓应用将音频转化为文本。

第八章,使用 GAN 实现手写数字识别,在这一章中,我们将构建一个安卓应用,检测手写数字并通过对抗学习推断数字。我们将使用修改版国家标准技术研究院MNIST)数据集进行数字分类。我们还将探讨生成对抗网络GANs)的基础知识。

第九章,使用 LinearSVC 进行文本情感分析,在这一章中,我们将构建一个 iOS 应用,利用用户输入对文本和图像进行情感分析。我们将使用为相同目的而构建的现有数据模型,利用 LinearSVC 将这些模型转换为核心机器学习ML)模型,方便我们在应用中使用。

第十章,下一步是什么?,讨论了流行的基于机器学习的云服务,以及在构建第一个基于机器学习的移动应用时应从哪里开始,还提供了一些进一步阅读的参考资料。

为了充分利用本书的内容

你只需要具备扎实的机器学习知识和任何编程语言的经验,就能开始本书的学习。

下载示例代码文件

您可以从您的账户在www.packt.com下载本书的示例代码文件。如果您是在其他地方购买的此书,您可以访问www.packt.com/support并注册,以便将文件直接通过电子邮件发送给您。

您可以通过以下步骤下载代码文件:

  1. 请在www.packt.com登录或注册。

  2. 选择“支持”选项卡。

  3. 点击“代码下载和勘误表”。

  4. 在搜索框中输入书名,并按照屏幕上的指示操作。

下载文件后,请确保使用最新版本的以下工具解压或提取文件夹:

  • WinRAR/7-Zip for Windows

  • Zipeg/iZip/UnRarX for Mac

  • 7-Zip/PeaZip for Linux

本书的代码包也托管在 GitHub 上:github.com/PacktPublishing/Mobile-Artificial-Intelligence-Projects。如果代码有更新,它将在现有的 GitHub 存储库中进行更新。

我们还提供了来自我们丰富书籍和视频目录的其他代码包,您可以在**github.com/PacktPublishing/**上找到它们。赶快去看看吧!

下载彩色图片

我们还提供了一个 PDF 文件,包含本书中使用的截图/图表的彩色图片。您可以在这里下载:www.packtpub.com/sites/default/files/downloads/9781789344073_ColorImages.pdf

使用的约定

本书中使用了若干文本约定。

CodeInText:表示文本中的代码字、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 用户名。以下是一个例子:“首先,让我们导入math库,以便我们可以使用exponential函数。”

代码块的格式如下:

def sigmoid ( x ):    
    return 1 / ( 1 + e **- x )

任何命令行输入或输出都如下所示:

pip install tensorflow

粗体:表示新术语、重要词汇或屏幕上看到的词汇。例如,菜单或对话框中的单词会以这种方式出现在文本中。以下是一个例子:“使用右上角的New下拉菜单来创建一个新的Python 3笔记本。”

警告或重要提示如下所示。

小贴士和技巧会这样显示。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请在邮件主题中提到书名,并通过customercare@packtpub.com与我们联系。

勘误表:尽管我们已尽最大努力确保内容的准确性,但错误仍可能发生。如果您在本书中发现错误,请报告给我们。请访问www.packt.com/submit-errata,选择您的书籍,点击“勘误提交表单”链接,并输入相关细节。

盗版:如果你在互联网上遇到任何我们作品的非法复制品,无论形式如何,我们将不胜感激,如果你能提供该地址或网站名称。请通过copyright@packt.com与我们联系,并附上该材料的链接。

如果你有兴趣成为作者:如果你在某个领域有专长,并且有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com

评价

请留下评价。阅读并使用本书后,不妨在你购买的站点上留下评价吗?潜在读者可以看到并根据你的公正意见做出购买决策,我们在 Packt 也能了解你对我们产品的看法,而我们的作者可以看到你对他们书籍的反馈。谢谢!

欲了解更多关于 Packt 的信息,请访问packt.com

第一章:人工智能的概念和基础

本章作为整本书的序章以及书中相关概念的引导。我们将在足够高的层次上理解这些概念,以便我们能够理解本书中的构建内容。

我们将通过比较人工智能、机器学习和深度学习来理解人工智能AI)的整体结构及其构建块,因为这些术语可以互换使用。接着,我们将浏览人工神经网络(ANNs)的历史、演变和原理。然后,我们将深入了解 ANNs 和深度学习的基本概念和术语,这些将在全书中使用。之后,我们将简要了解 TensorFlow Playground,以巩固我们对 ANNs 的理解。最后,我们将以关于在哪里获取人工智能和 ANN 原理的更深理论参考的思考来结束本章,具体如下:

  • 人工智能与机器学习与深度学习

  • 人工智能的演变

  • 人工神经网络(ANNs)的机制

  • 生物神经元

  • 人工神经元的工作原理

  • 激活函数与成本函数

  • 梯度下降、反向传播和 softmax

  • TensorFlow Playground

人工智能与机器学习与深度学习

人工智能(AI)并不是一个新术语,因为我们在线阅读了大量相关文章,并且有许多基于该主题的电影。所以,在我们进一步讨论之前,先退一步,从实践者的角度理解人工智能及其常见的术语。我们将清晰地区分机器学习、深度学习和人工智能,因为这些术语经常被交替使用:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/d60ec346-e3a6-4851-bab9-25e39508cbcd.png

人工智能是可以嵌入到机器中的能力,使得机器能够执行具有人的智能特征的任务。这些任务包括看见和识别物体、听见并区分声音、理解和领会语言以及其他类似的任务。

机器学习ML)是人工智能(AI)的一个子集,涵盖了使这些类人任务成为可能的技术。因此,从某种意义上讲,机器学习是实现人工智能的手段。

本质上,如果我们不使用机器学习来实现这些任务,那么我们实际上是在试图编写数百万行复杂的代码,包括循环、规则和决策树。

机器学习赋予了机器在没有明确编程的情况下进行学习的能力。因此,机器学习不再是为每个可能的场景硬编码规则,而是通过提供任务执行方式和不应执行方式的示例来进行学习。然后,机器学习系统根据这些数据进行训练,以便它能自我学习。

ML 是一种人工智能方法,通过它我们可以完成诸如分组或聚类、分类、推荐、预测和数据预测等任务。常见的例子包括垃圾邮件分类、股市预测、天气预报等。

深度学习 是机器学习中的一种特殊技术,它模仿了人类大脑的生物结构,并通过神经网络来完成类似人类的任务。这是通过使用人工神经网络(ANNs)——一种通过算法堆叠解决问题的技术——来构建像大脑一样的神经网络,从而以类人甚至更高效的能力解决问题。

这些层通常被称为深度网络(深层架构),每一层都有一个可以训练解决的特定问题。目前,深度学习领域正处于前沿技术,应用包括自动驾驶、Alexa 和 Siri、机器视觉等。

在本书中,我们将执行使用这些深度网络(deepnets)构建的任务和应用,并通过构建我们自己的深度网络架构来解决使用案例。

人工智能的发展

要理解我们当前在人工智能领域所能做到的事情,我们需要对模仿人类大脑的概念的起源有一个基本的了解,并且了解这个概念是如何发展到如今,通过机器我们能够轻松解决视觉和语言任务,且具有类似人类的能力。

一切始于 1959 年,当时哈佛的几位科学家 Hubel 和 Wiesel 通过监测猫的大脑初级视觉皮层,进行猫视觉系统的实验。

初级视觉皮层 是大脑中位于后脑勺的一群神经元,负责处理视觉信息。它是大脑接收来自眼睛输入信号的第一部分,类似于人类大脑如何处理视觉信息。

科学家们从向猫展示复杂的图像(例如鱼、狗和人类)开始,并观察猫的初级视觉皮层。令他们失望的是,初期没有从初级视觉皮层得到任何反应。因此,令他们惊讶的是,在某次实验中,当他们移除幻灯片时,黑暗的边缘形成,导致一些神经元在初级视觉皮层中激活。

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/8abe3823-b8a6-47b1-a11a-ac88a98fd276.png

他们偶然发现,初级视觉皮层中的这些个体神经元或脑细胞会对不同特定方向的条形或黑暗边缘做出反应。这导致了一个理解:哺乳动物的大脑每个神经元处理的信息量非常小,随着信息从一个神经元传递到另一个神经元,越复杂的形状、边缘、曲线和阴影逐渐被理解。因此,这些持有非常基础信息的独立神经元需要一同激活,才能理解一个完整而复杂的图像。

之后,关于如何模仿哺乳动物大脑的进展曾一度停滞,直到 1980 年,福岛提出了神经认知网络(neocognitron)。神经认知网络 的灵感来自于一个想法:我们应该能够通过许多非常简单的表现形式,创造出越来越复杂的表现形式——就像哺乳动物的大脑一样!

以下是福岛(Fukushima)提出的神经认知网络(Neocognitron)工作原理的示意图:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/739593e1-54e0-414c-944d-30ddee2d1473.png

他提出,为了识别你的祖母,初级视觉皮层中有许多神经元被激活,每个细胞或神经元理解你祖母最终图像的一个抽象部分。这些神经元以顺序、并行和协同的方式工作,最后会激活一个“祖母细胞”或神经元,只有在看到你的祖母时它才会被激活。

快进到今天(2010-2018),在 Yoshua Bengio、Yann LeCun 和 Geoffrey Hinton 的贡献下,他们被广泛称为深度学习之父。他们为我们今天所工作的 AI 领域做出了巨大的贡献。他们催生了机器学习的一种全新方法,其中特征工程被自动化。

不直接告诉算法它应该寻找什么,而是通过给它提供大量的例子让它自己搞清楚这一点,是最新的发展。这个原理的类比就像是教一个孩子区分苹果和橙子。我们会向孩子展示苹果和橙子的图片,而不仅仅是描述这两种水果的特征,比如形状、颜色、大小等。

以下图示展示了机器学习和深度学习的区别:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/bdfe861e-68f8-4f48-bdf4-a2be37b1ae07.png

这是传统机器学习与使用神经网络(深度学习)进行机器学习的主要区别。在传统机器学习中,我们提供特征和标签,而在使用人工神经网络时,我们让算法自己解码特征。

我们生活在一个激动人心的时代,这个时代我们与深度学习之父共同度过,甚至在 Stack Exchange 等地方的在线交流中,我们还能看到 Yann LeCun 和 Geoffrey Hinton 的贡献。这就像是生活在尼古拉斯·奥托(内燃机的发明者)时代,并且向他写信,正是他启动了我们至今仍在演变的汽车革命。而未来 AI 的潜力将使汽车革命相形见绌。确实是一个令人兴奋的时代!

人工神经网络的机制

在本节中,我们将了解构建我们自己 AI 项目所需的基本要素。我们将掌握深度学习技术中常用的术语。

本节旨在提供高层次的基本理论,帮助你获得足够的洞察力,从而能够构建自己的深度神经网络,调整它们,并理解构建最先进的神经网络所需的条件。

生物神经元

我们之前讨论过生物大脑如何成为人工神经网络(ANN)的灵感来源。大脑由数百亿个独立的单元或细胞组成,这些单元被称为神经元

以下图示展示了一个神经元,它有多个输入信号进入,称为树突。还有一个从细胞体输出的信号,称为轴突

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/78c9025b-fe58-45e9-8559-f948bf0fa9f3.png

树突将信息带入神经元,而轴突则允许处理后的信息从神经元流出。但实际上,有成千上万的树突将输入以微小电荷的形式传递给神经元。如果这些由树突携带的微小电荷对神经元主体的总电荷产生影响,或超过某个阈值,那么轴突将会触发。

现在我们了解了生物神经元的工作原理,接下来我们将理解人工神经元是如何工作的。

人工神经元的工作原理

就像生物大脑一样,人工神经网络(ANN)由独立的单元组成,这些单元被称为神经元。像生物神经元一样,人工神经元也有一个进行计算的主体,并且有许多输入馈送到细胞体或神经元:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/823f3760-3563-4a25-bea7-485f97fd83ea.png

例如,假设我们有三个输入到神经元。每个输入携带一个 0 或 1 的二进制值。我们有一个从主体流出的输出,它也携带一个 0 或 1 的二进制值。对于这个示例,神经元决定我今天是否应该吃蛋糕。也就是说,如果我应该吃蛋糕,神经元应输出 1;如果不该吃蛋糕,则输出 0:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/3646d7fd-ccd3-47cc-a909-2ade266b8aa1.png

在我们的示例中,三个输入代表了决定我是否应该吃蛋糕的三个因素。每个因素都有一个重要性权重;例如,第一个因素是 昨天我做了有氧运动,权重为 2。第二个因素是 昨天我去了健身房,权重为 3。第三个因素是 这是一个吃蛋糕的场合,权重为 6。

神经元的主体对输入进行一些计算,比如将所有这些输入相加并检查它们是否超过某个阈值:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/ace25c8d-9dd4-434e-b9e1-c8914bf0d60d.png

所以,对于这个示例,我们设定阈值为 4。如果输入权重的总和超过阈值,则神经元输出 1,表示我可以吃蛋糕。

这可以表示为一个方程式:

** https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/6d3e02c1-3c31-484c-af2a-516841aeed6f.png**

这里适用以下内容:

  • Xi 是第一个输入因素,昨天我做了有氧运动

  • Wi 是第一个输入因素 Xi 的权重。在我们的示例中,Wi = 2

  • Xii 是第二个输入因素,昨天我去了健身房

  • Wii 是第二个输入因素 Xii 的权重。在我们的示例中,Wii = 3

  • Xiii 是第三个输入因素,这是一个吃蛋糕的场合

  • Wiii 是第三个输入因素 Xiii 的权重。在我们的示例中,Wiii= 6

  • 阈值 为 4。

现在,让我们用这个神经元来决定在三种不同的情况下我是否可以吃蛋糕。

情境 1

我想吃蛋糕,昨天我去了健身房,但我没有做有氧运动,也不是吃蛋糕的场合:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/b48ff741-766f-4233-8e04-ddcadf19ae0e.jpg

这里适用以下内容:

  • Xi 是第一个输入因素,昨天我做了有氧运动。现在,Xi = 0,因为这是假的。

  • Wi 是第一个输入因素 Xi 的权重。在我们的例子中,Wi = 2

  • Xii 是第二个输入因素 我昨天去了健身房。现在,Xii = 1,因为这是正确的。

  • Wii 是第二个输入因素 Xii 的权重。在我们的例子中,Wii = 3。

  • Xiii 是第三个输入因素,这时是吃蛋糕的时机。现在,Xiii = 0,因为这个条件是错误的。

  • Wiii 是第三个输入因素 Xiii 的权重。在我们的例子中,Wiii = 6。

  • threshold 是 4。

我们知道神经元计算以下方程:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/f1cfb10c-000e-46eb-b95e-eca2430d94a0.png

对于场景 1,方程将转化为以下形式:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/eea9930f-44c4-49bc-ab8a-1175b1b98d9b.png

这等于这个:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c72373a8-0c4a-4348-a339-a1bb34dee2ac.png

3 ≥ 4 是假的,所以它输出 0,这意味着我不应该吃蛋糕。

场景 2

我想吃蛋糕,而且今天是我的生日,但我没有做有氧运动,也没有去健身房:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c3b90c6b-70fb-4205-ae37-f004f3859436.png

这里,以下条件成立:

  • Xi 是第一个输入因素,我昨天做了有氧运动。现在,Xi = 0,因为这个因素是错误的。

  • Wi 是第一个输入因素 Xi 的权重。在我们的例子中,Wi = 2

  • Xii 是第二个输入因素 我昨天去了健身房。现在,Xii = 0,因为这个因素是错误的。

  • Wii 是第二个输入因素 Xii 的权重。在我们的例子中,Wii = 3。

  • Xiii 是第三个输入因素,这时是吃蛋糕的时机。现在,Xiii = 1,这个因素为真。

  • Wiii 是第三个输入因素 Xiii 的权重。在我们的例子中,Wiii = 6。

  • threshold 是 4。

我们知道神经元计算以下方程:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/2f874d55-53b7-4829-a9be-6b010448f7ba.png

对于场景 2,方程将转化为以下形式:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/5a277032-ef63-4868-b8d3-0e9101a89af3.png

这将给我们以下输出:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/b326d771-dc1f-4461-acc1-976ccc80bcaa.png

6 ≥ 4 为真,所以它输出 1,这意味着我可以吃蛋糕。

场景 3

我想吃蛋糕,我昨天做了有氧运动并去了健身房,但这也不是吃蛋糕的时机:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/9719159c-ae4d-4d84-bd40-40e2b9419f06.png

这里,以下条件成立:

  • Xi 是第一个输入因素 我昨天做了有氧运动。现在,Xi = 1,因为这个因素为真。

  • Wi 是第一个输入因素 Xi 的权重。在我们的例子中,Wi = 2

  • Xii 是第二个输入因素,我昨天去了健身房。现在,Xii = 1,因为这个因素为真。

  • Wii 是第二个输入因素 Xii 的权重。在我们的例子中,Wii = 3。

  • Xiii 是第三个输入因素,这时是吃蛋糕的时机。现在,Xiii = 0,因为这个因素是错误的。

  • Wiii 是第三个输入因素 Xiii 的权重。在我们的例子中,Wiii = 6。

  • threshold 是 4。

我们知道神经元计算以下方程:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/d1697414-06e7-4cf0-8b74-ac1759983538.png

对于场景 3,方程将转化为以下形式:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/0e09f54b-fd7a-4f76-9c68-da19d034fefc.png

这给我们以下方程:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/920fd23a-247e-47fe-a27f-827f2a80209e.png

5 ≥ 4 为真,所以它触发 1,这意味着我可以吃蛋糕。

从之前的三个场景中,我们看到一个单一的人工神经元是如何工作的。这个单元也叫做感知机。感知机本质上处理二进制输入,计算总和,然后与阈值进行比较,最终给出二进制输出。

为了更好地理解感知机(perceptron)是如何工作的,我们可以将前面的方程式转化为一个更通用的形式,以便解释。

假设为了简化起见,只有一个输入因素:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/bec404cd-2afa-4e53-9147-ccf45328046c.png

假设阈值 = b。我们的方程式如下:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/9b74e4cc-32e8-4628-bef0-98d523e06c5d.png

它现在变成了这样:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/41971681-10e2-4ff7-a7b5-a7906f788a70.png

它也可以写作!,然后输出1否则*0**。

这里适用以下规则:

  • w是输入的权重

  • b是阈值,称为偏置

这条规则总结了感知机神经元的工作原理。

就像哺乳动物的大脑一样,人工神经网络由许多这样的感知机组成,它们被堆叠并分层在一起。在下一部分,我们将了解这些神经元如何在人工神经网络中协同工作。

人工神经网络(ANN)

就像生物神经元一样,人工神经元也并不是孤立存在的。它们存在于一个由其他神经元组成的网络中。基本上,神经元通过互相传递信息来存在;一些神经元的输出是其他神经元的输入。

在任何人工神经网络(ANN)中,第一个层被称为输入层。这些输入是真实值,比如我们之前示例中的带权重的因素(w.x)。从输入层传递的总和值会传播到下一层的每个神经元。该层的神经元进行计算并将它们的输出传递到下一层,以此类推:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/d40f5cee-3ac8-47b8-8d00-5c206d33504a.png

接收来自所有前一层神经元的输入并将其输出传递给下一层所有神经元的层叫做密集层。由于这一层连接着前一层和下一层的所有神经元,它也常被称为全连接层

输入和计算从一层流向下一层,最终结束于输出层,它给出了整个人工神经网络的最终估计值。

输入层和输出层之间的层被称为隐藏层,因为这些隐藏层内神经元的值对从业者来说是未知的,完全是一个黑箱。

随着层数的增加,你会增加网络的抽象程度,进而提高网络解决更复杂问题的能力。当隐藏层超过三层时,就被称为深度网络(deepnet)。

所以,如果这是一个机器视觉任务,那么第一层隐藏层将寻找边缘,下一层会寻找角落,再下一层寻找曲线和简单的形状,以此类推:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/a0dcb8f4-d1c7-41a7-b083-14c5f9e374e1.png

因此,问题的复杂性可以决定所需的层数;更多的层会导致更多的抽象。这些层可以非常深,达到 1,000 层或更多,也可以非常浅,只有大约六层。增加隐藏层的数量不一定能得到更好的结果,因为抽象可能是多余的。

到目前为止,我们已经看到如何将人工神经元堆叠在一起形成神经网络。但我们已经看到,感知机神经元只能接受二进制输入,并且只给出二进制输出。但在实践中,基于感知机思想做事情会出现问题。这个问题就是通过激活函数来解决的。

激活函数

我们现在知道,人工神经网络(ANN)是通过堆叠单个计算单元(称为感知机)来创建的。我们也已经看到感知机是如何工作的,并将其总结为输出 1,如果**。

也就是说,它根据权重w和偏置b的值,输出10

让我们看一下下面的图表,以了解为什么仅仅输出10会出现问题。以下是一个简单感知机的图表,只有一个输入,x

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/b59c88c9-dbea-4dc0-8ef5-99ade5565357.png

为了简单起见,假设!,其中适用以下情况:

  • w 是输入的权重,xb 是偏置。

  • a 是输出,可以是10

在这里,当z的值发生变化时,输出a会在某个点从0变为1。正如你所看到的,输出a的变化是突然和剧烈的:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/0609f818-9b0f-4e5e-a00a-f2a2399e8303.jpg

这意味着,对于某些小的变化,https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/3f95d5b7-7f37-417f-8d51-d3e966dd1a9f.png,我们会得到输出a的剧烈变化。如果每个感知机都有如此剧烈的变化,那么它会导致网络不稳定,因此网络无法学习。

因此,为了使网络更高效、更稳定,我们需要减缓每个感知机学习的方式。换句话说,我们需要消除从01的突变,转而进行更渐进的变化:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/558d13a2-28ee-4578-a130-9118cf059004.jpg

这是通过激活函数来实现的。激活函数是应用于感知机的函数,使其不再输出01,而是输出介于01之间的任何值。

这意味着每个神经元可以通过使用更小的变化来更慢地学习,并且能够以更大的细节进行学习,https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/660fdf4e-3332-4f80-a224-575de3824318.png。激活函数可以被看作是转换函数,用于将二进制值转换为介于给定最小值和最大值之间的更小的值序列。

有多种方法可以将二进制输出转化为一系列值,分别是 Sigmoid 函数、tanh 函数和 ReLU 函数。我们现在将快速浏览这些激活函数。

Sigmoid 函数

Sigmoid 函数是一个数学函数,对于任何输入,它都会输出一个介于 0 和 1 之间的值:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c0adb54a-93c5-414f-988e-c067850e6eb4.png

这里,https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/60bb9873-524f-45a9-84ba-8ac74caac355.pnghttps://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/f5076aa3-efc1-47e4-a41f-6ee338e6bfd8.png

让我们通过一些简单的代码更好地理解 Sigmoid 函数。如果你没有安装 Python,没关系:我们现在将使用一个在线替代方案,网址是 www.jdoodle.com/python-programming-online。我们将在 第二章 创建一个房地产价格预测移动应用 中从零开始进行完整的设置。目前,让我们继续使用这个在线替代方案。

一旦我们加载了页面 www.jdoodle.com/python-programming-online,我们可以逐步浏览代码并理解 Sigmoid 函数:

  1. 首先,让我们导入 math 库,这样我们就可以使用指数函数:
from   math   import  e
  1. 接下来,让我们定义一个名为 sigmoid 的函数,基于之前的公式:
def sigmoid ( x ):    
    return 1 / ( 1 + e **- x )
  1. 假设我们的 z 非常小,比如 -10,因此该函数将输出一个非常小且接近 0 的数字:
sigmoid(-10) 
4.539786870243442e-05
  1. 如果 z 非常大,比如 10000,那么该函数将输出最大可能值 1:
sigmoid(10000)  
1.0

因此,Sigmoid 函数将任何值 z 转换为介于 0 和 1 之间的值。当 Sigmoid 激活函数在神经元上使用时,而不是传统的感知机算法,我们得到的就是所谓的 Sigmoid 神经元

Tanh 函数

与 Sigmoid 神经元类似,我们可以应用一个名为 tanh(z) 的激活函数,它将任何值转换为介于 -1 和 1 之间的值。

使用这个激活函数的神经元被称为tanh 神经元

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/2161bec9-1aae-4b92-91f7-21d0ee1ca56d.png

ReLU 函数

然后有一个激活函数,称为 修正线性单元ReLU(z),它将任何值 z 转换为 0 或大于 0 的值。换句话说,它将小于 0 的任何值输出为 0,而将大于 0 的任何值输出为其本身:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/af0454a7-3151-4abf-a1af-da3be88ab020.png

简要总结我们目前的理解,感知机是传统的、过时的神经元,在实际应用中很少使用。它们非常适合帮助我们简单理解底层原理;然而,由于输出值的剧烈变化,它们存在快速学习的问题。

我们使用激活函数来减少学习速度,并确定 zhttps://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/2469dc05-58c0-4c3f-848c-ab28a70b8385.png 的细微变化。让我们总结一下这些激活函数:

  • sigmoid 神经元 是使用 sigmoid 激活函数的神经元,它将输出转化为介于 0 和 1 之间的值。

  • tanh 神经元 是使用 tanh 激活函数的神经元,它将输出转化为介于 -1 和 1 之间的值。

  • ReLU 神经元 是使用 ReLU 激活函数的神经元,它将输出转化为 0 或任何大于 0 的值。

Sigmoid 函数在实际中被使用,但与 tanh 和 ReLU 函数相比较慢。tanh 和 ReLU 函数是常用的激活函数。ReLU 函数也被认为是最先进的,通常是构建人工神经网络(ANN)时首选的激活函数。

以下是常用的激活函数列表:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/67a40589-04e2-4744-b539-5d814a516ef2.jpg

在本书中的项目中,我们将主要使用 sigmoid、tanh 或 ReLU 神经元来构建人工神经网络(ANN)。

代价函数

快速回顾一下,我们已经了解了基本的感知机工作原理及其局限性。然后,我们看到激活函数如何克服了感知机的缺陷,从而诞生了今天使用的其他神经元类型。

现在,我们将探讨如何判断神经元的输出是否错误。任何类型的神经元要学习,都需要知道何时输出了错误的值以及错误的幅度。衡量神经网络错误的最常见方法是使用代价函数。

代价函数 定量化了神经元输出与我们需要的输出之间的差异。常用的两种代价函数是均方误差和交叉熵。

均方误差

均方误差MSE)也被称为二次代价函数,因为它使用平方差来衡量误差的大小:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/9f61fab8-dc30-44bd-9b79-a6b70a6ca878.png

这里,适用以下公式:

  • a 是来自人工神经网络(ANN)的输出

  • y 是期望的输出

  • n 是使用的样本数

代价函数相当直接。例如,考虑一个只有一个样本的单神经元(n=1)。如果期望的输出是 2 (y=2),而神经元的输出是 3 (a=3),则 MSE 如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c18a1923-6add-41b7-a4ae-43a0943bdb46.png

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c7b65bad-5cd3-4ce3-b65a-70b26c4d8bbb.png

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/d14ec931-b364-4688-97ed-3661cdae3063.png

类似地,如果期望的输出是 3 (y=3),而神经元输出是 2 (a=2),那么 MSE 如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/1cbaaf60-0a6c-4b58-9fb8-cbbbc36a0acc.png

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/4d35819d-c83d-4b5a-8f39-c195161ac485.png

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/39b26616-25dc-4fdf-b431-4b1762ed882a.png

因此,均方误差(MSE)量化了神经元所犯错误的大小。MSE 的问题之一是,当网络中的数值变得很大时,学习会变得很慢。换句话说,当权重(w)和偏差(b)或 z 很大时,学习会变得非常慢。请记住,我们讨论的是人工神经网络(ANN)中的成千上万个神经元,这就是为什么当学习变得缓慢并最终停滞时,不再有任何进一步的学习。

交叉熵

交叉熵 是一种基于导数的函数,因为它使用了一个特别设计的方程的导数,该方程如下所示:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/d7e1d48b-f68b-4431-974a-dba3b24e7c85.png

交叉熵使得当预期输出和实际输出之间的差距较大时,网络可以更快地学习。换句话说,错误越大,它越能帮助网络加速学习。我们将通过一些简单的代码来理解这一点。

和之前一样,如果你没有安装 Python,可以使用在线替代工具,网址为 www.jdoodle.com/python-programming-online。我们将在第二章《创建一个房地产价格预测 移动应用》中讲解安装和设置。请按照以下步骤,了解网络如何通过交叉熵学习:

  1. 首先,让我们导入 math 库,以便使用 log 函数:
from numpy import log  
  1. 接下来,让我们定义一个名为 cross_entropy 的函数,基于前面的公式:
def cross_entropy(y,a): 
    return -1 *(y*log(a)+(1-y)*log (1-a))
  1. 例如,考虑一个只有一个样本的单个神经元(n=1)。假设预期输出是 0 (y=0),而神经元的输出是 0.01 (a=0.01):
cross_entropy(0, 0.01)

输出结果如下:

0.010050335853501451

由于预期输出和实际输出值非常小,结果的成本也非常小。

同样地,如果预期输出和实际输出值非常大,那么结果的成本仍然很小:

cross_entropy(1000,999.99) 

输出结果如下:

0.010050335853501451

同样地,如果预期输出与实际输出之间的差距很大,那么结果的成本也会很大:

cross_entropy(0,0.9) 

输出结果如下:

2.3025850929940459

因此,预期输出与实际输出之间的差异越大,学习速度就越快。通过使用交叉熵,我们可以获得网络的误差,同时,权重和偏差的大小并不重要,有助于网络更快地学习。

梯度下降

到目前为止,我们已经涵盖了基于激活函数使用的不同类型的神经元。我们已经讨论了如何使用成本函数来量化神经元输出的误差。现在,我们需要一个机制来解决这个误差。

网络能够学习输出接近预期或期望输出值的机制被称为 梯度下降。梯度下降是机器学习中常用的一种方法,用于寻找最低成本。

为了理解梯度下降,让我们使用我们迄今为止一直在使用的单个神经元方程:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c8dd1e82-570f-42cd-920e-4395ad993c09.png

这里,以下公式适用:

  • x是输入

  • w是输入的权重

  • b是输入的偏置

梯度下降可以表示如下:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/a9278133-1d21-4301-b560-08a75e14b516.png

最初,神经元通过为wb分配随机值来开始。从那时起,神经元需要调整wb的值,以便降低或减少误差或成本(交叉熵)。

对交叉熵(成本函数)求导,会导致wb朝着最低成本的方向逐步变化。换句话说,梯度下降尝试找到网络输出与预期输出之间的最佳线。

权重会根据一个叫做学习率的参数进行调整。学习率是调整神经元权重的值,以使输出更接近预期输出。

请记住,这里我们只用了一个单一的参数;这是为了让事情更容易理解。实际上,考虑到降低成本,会有成千上万的参数需要考虑。

反向传播——一种让神经网络学习的方法

太好了!我们已经走了很长一段路,从观察生物学神经元,到研究神经元类型,再到确定准确性并纠正神经元的学习。只剩下一个问题:整个神经网络如何一起学习?

反向传播是一种非常聪明的方式,使梯度下降能够遍及整个网络的所有层。反向传播利用微积分中的链式法则,使得信息可以在网络中前后传递:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/2347ee42-e9b4-4236-8b0c-8dafa257cf50.jpg

原则上,输入参数和权重的信息会通过网络传播,猜测预期输出,然后整体的不准确性通过网络的各层进行反向传播,以便调整权重并重新猜测输出。

这个学习的单一循环叫做训练步骤迭代。每次迭代都是在输入训练样本的一批数据上进行的。每批样本中的样本数叫做批量大小。当所有的输入样本都经历了一次迭代或训练步骤,那么这就叫做一个周期

举个例子,假设有 100 个训练样本,每次迭代或训练步骤中,网络使用 10 个样本来学习。那么,我们可以说批量大小是 10,并且它需要 10 次迭代才能完成一个周期。如果每个批次的样本是独特的,也就是说每个样本至少被网络使用一次,那么这就是一个周期。

这种预测输出和成本在网络中前后传播的方式就是网络如何学习的过程。

在我们的实践环节中,我们将重新讨论训练步骤、周期、学习率、交叉熵、批量大小等内容。

Softmax

我们已经到达本章的最后一个概念性主题。我们已经讨论了神经元类型、成本函数、梯度下降,最后是应用梯度下降跨越网络的机制,使得在多次迭代中学习成为可能。

之前,我们看到了 ANN 的输入层和密集层或隐藏层:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/a8dbb075-44b2-41c1-9380-a3ea3f8dd76d.jpg

Softmax是一种特殊的神经元,用于输出层中描述各输出的概率:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/4f904f31-079f-4eee-b8da-a970c52f121c.png

为了理解 softmax 方程及其概念,我们将使用一些代码。像以前一样,现在你可以使用任何在线 Python 编辑器来跟随代码。

首先,从math库中导入指数方法:

     from math import exp 

为了这个例子,假设这个网络被设计为分类三个可能的标签:ABC。假设从前面的层有三个信号输入到 softmax(-1,1,5):

    a=[-1.0,1.0,5.0]

解释如下:

  • 第一个信号表明输出应该是A,但信号较弱,其值为-1。

  • 第二个信号表明输出应该是B,并且略强,值为 1。

  • 第三个信号最强,表明输出应该是C,其值为 5。

这些表示的值是对预期输出的置信度度量。

现在,我们来查看 softmax 中第一个信号的分子,猜测输出是A

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/a354238f-684e-4657-b6b2-326b60172ab6.png

在这里,M是表示输出应该是A的输出信号强度:

exp(a[0]) # taking the first element of a[-1,1,5] which represents A

0.36787944117144233

接下来,这是 softmax 中第二个信号的分子,猜测输出是B

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/a354238f-684e-4657-b6b2-326b60172ab6.png

在这里,M是表示输出应该是B的输出信号强度:

exp(a[0]) # taking the second element of a[-1,1,5] which represents B

2.718281828459045

最后,这是 softmax 中第二个信号的分子,猜测输出是C

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/dc4b8b76-9465-424c-95f0-a7814745c5c7.png

在这里,M是表示输出应该是C的输出信号强度:

exp(a[2]) 
# taking the third element of a[-1,1,5] which represents C

148.4131591025766

我们可以观察到,表示的置信度值总是大于 0,并且结果会被指数级放大。

现在,让我们解释 softmax 函数的分母,它是每个信号值的指数和:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/75d8dee4-c88e-40b4-9a49-dc4e732088e8.png

让我们为 softmax 函数写一些代码:

sigma = exp ( a [ 0 ]) + exp ( a [ 1 ]) + exp ( a [ 2 ]) 
sigma

151.49932037220708

因此,第一个信号正确的概率如下:

exp(a[0])/sigma

0.0024282580295913376

这意味着A的概率不到 1%。

同样,第三个信号正确的概率如下:

exp(a[2])/sigma

0.9796292071670795

这意味着预期输出是C的概率超过 97%。

本质上,softmax 接受一个加权信号,表示某个类别预测的置信度,并输出一个介于 0 到 1 之间的概率分数,针对所有这些类别。

很棒!我们已经掌握了实现项目所需的基本高层理论。接下来,我们将通过探索 TensorFlow Playground 来总结我们对这些概念的理解。

TensorFlow Playground

在我们开始使用 TensorFlow Playground 之前,让我们快速回顾一下基本概念。这将帮助我们更好地理解 TensorFlow Playground。

神经网络的灵感来源于生物大脑,而大脑中的最小单元是 神经元

感知器(Perceptron) 是一种基于生物神经元概念的神经元。感知器基本上处理二进制输入和输出,这使得它在实际应用中不太可行。此外,由于其二进制性质,它对输入的微小变化会产生剧烈的输出变化,因此学习速度过快,无法提供细节。

激活函数 用于解决感知器的问题。这催生了其他类型的神经元,这些神经元处理介于 0 到 1、-1 到 1 等区间之间的值,而不仅仅是 0 或 1。

人工神经网络(ANNs) 由这些堆叠在各层中的神经元组成。它包括输入层、密集层或全连接层,以及输出层。

代价函数,如均方误差(MSE)和交叉熵(cross entropy),是衡量神经元输出误差大小的方式。

梯度下降 是一种机制,通过它,神经元可以学习输出更接近期望或目标输出的值。

反向传播 是一种非常聪明的方法,它使梯度下降在网络中的所有层中得以实现。

每次反向传播或预测输出与成本在网络中的迭代过程称为 训练步骤

学习率 是在每次训练步骤中调整神经元权重的值,以使输出更接近期望的输出。

Softmax 是一种特殊的神经元,它接受一个加权信号,表示某个类别预测的置信度,并为所有这些类别输出一个介于 0 到 1 之间的概率值。

现在,我们可以访问 TensorFlow Playground:Playground.tensorflow.org。TensorFlow Playground 是一个在线工具,用于可视化人工神经网络(ANN)或深度网络的运行,是一个很好的地方,可以直观地重复我们在概念上学到的内容。

现在,废话不多说,让我们开始使用 TensorFlow Playground。页面加载完成后,您将看到一个仪表板,可以创建自己的神经网络来解决预定义的分类问题。以下是默认页面及其各个部分的截图:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/b9d0dbd1-00ed-4a54-9bfb-2740326d4a34.png

让我们来看一下这个截图中的各个部分:

  • 第一部分:数据部分显示了选择预构建问题的选项,用于构建和可视化网络。第一个问题是选择的,基本上是区分蓝点和橙点。在下面,还有将数据分为训练集和测试集的控制项。还有一个参数用于设置批次大小。批次大小是每次训练步骤中输入到网络进行学习的样本数量。

  • 第二部分:特征部分表示输入参数的数量。在这种情况下,选择了两个特征作为输入特征。

  • 第三部分:隐藏层部分是我们可以创建隐藏层以增加复杂度的地方。这里还有控制项可以增加或减少每个隐藏层或全连接层中的神经元数量。在这个示例中,有两个隐藏层,分别包含 4 个和 2 个神经元。

  • 第四部分:输出部分是我们可以看到损失或代价图表的地方,同时还可视化网络分离红点和蓝点的学习效果。

  • 第五部分:这是调整网络调优参数的控制面板。它有一个小部件可以启动、暂停和刷新网络的训练。在它旁边是一个计数器,显示经过的训练轮数。接下来是学习率,表示权重调整的常数。然后是选择在神经元中使用的激活函数。最后,有一个选项可以指示可视化的问题类型,即分类或回归。在这个示例中,我们正在可视化一个分类任务。

  • 目前我们将忽略正则化和正则化率,因为我们尚未以概念性的方式讲解这些术语。我们将在书中的后续部分介绍这些术语,当时会更容易理解它们的目的。

现在我们准备开始使用 TensorFlow Playground。我们将从第一个数据集开始,调整以下调优参数:

  • 学习率 = 0.01

  • 激活函数 = Tanh

  • 正则化 = 无

  • 正则化率 = 0

  • 问题类型 = 分类

  • 数据 = Circle

  • 训练数据与测试数据的比例 = 50%

  • 批次大小 = 10

  • 特征 = X[1] 和 X[2]

  • 两个隐藏/全连接层;第一个层有 4 个神经元,第二个层有 2 个神经元

现在通过点击仪表盘左上角的播放按钮开始训练。从播放/暂停按钮向右移动,我们可以看到已经经过的训练轮数。在大约 200 轮时,暂停训练并观察输出部分:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/ee92aba1-9adb-4172-a92b-0fa2bbd9c4b0.jpg

仪表盘的关键观察点如下:

  • 我们可以在仪表盘的右侧看到网络的性能图。测试和训练损失分别是网络在测试和训练过程中的代价。如前所述,目标是最小化代价。

  • 在下方,您将看到一个可视化图,展示了网络如何将蓝色点从橙色点中分离或分类。

  • 如果我们将鼠标指针悬停在任何神经元上,我们可以看到该神经元已学会如何将蓝色点和橙色点分开。话虽如此,我们来仔细看看第二层的两个神经元,看看它们在这个任务中学到了什么。

  • 当我们将鼠标悬停在第二层的第一个神经元上时,我们可以看到这个神经元已经很好地学会了当前的任务。相比之下,第二层的第二个神经元学到的任务知识较少。

  • 这引出了从神经元中出来的虚线:它们是神经元的对应权重。蓝色虚线表示正权重,而橙色虚线表示负权重。它们通常被称为张量

另一个关键观察是,第二层中的第一个神经元输出的张量信号比第二个神经元强。这表明该神经元在将蓝色和橙色点分离的整体任务中的影响力,而且当我们看到它与最终结果的可视化对比时,这一点非常明显。

现在,牢记我们在本章中学到的所有术语,我们可以通过改变参数来进行试验,看看这如何影响整个网络。甚至可以添加新的层和神经元。

TensorFlow Playground 是一个很好的地方,可以重温 ANNs 的基本原理和核心概念。

总结

到目前为止,我们已经在高层次上涵盖了基本概念,这足以让我们欣赏本书中将要做的实际操作。拥有概念性的理解已经足够让我们开始构建 AI 模型,但更深入的理解也是非常有用的。

在下一章中,我们将设置构建 AI 应用程序的环境,并创建一个小型的 Android 和 iOS 移动应用,能够使用基于 Keras 和 TensorFlow 构建的模型来预测房价。

进一步阅读

这里有一份资源列表,可以作为参考,以帮助我们更好地理解并深入探索 AI 和深度学习的概念:

第二章:创建一个房地产价格预测移动应用

在上一章,我们讲解了理论基础;而本章将会介绍所有工具和库的设置。

首先,我们将设置环境,构建一个 Keras 模型,用于通过房地产数据预测房价。接着,我们将使用 Flask 构建一个 RESTful API 来提供此模型。然后,我们将为 Android 设置环境,并创建一个应用,该应用将调用此 RESTful API,根据房地产的特征预测房价。最后,我们将在 iOS 上重复相同的操作。

本章的重点是设置、工具、库,并应用在第一章中学到的概念,人工智能概念与基础。此用例设计简洁,但足够灵活,可以适应类似的用例。通过本章的学习,你将能轻松创建用于预测或分类任务的移动应用。

本章将涵盖以下内容:

  • 设置人工智能环境

  • 使用 Keras 和 TensorFlow 构建 ANN 模型进行预测

  • 将模型作为 API 提供

  • 创建一个 Android 应用来预测房价

  • 创建一个 iOS 应用来预测房价

设置人工智能 环境

首先需要做的事情是安装 Python。我们将在本书中使用 Python 进行所有的人工智能AI)任务。有两种方式可以安装 Python,一种是通过www.python.org/downloads/提供的可执行文件下载安装,另一种是通过 Anaconda 安装。我们将采用后一种方式,也就是使用 Anaconda。

下载并安装 Anaconda

现在,让我们访问 Anaconda 的官方安装页面(conda.io/docs/user-guide/install/index.html#regular-installation),并根据你的操作系统选择合适的安装选项:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/7ad40de0-48bf-4274-a70a-48405c216193.jpg

按照文档中的说明操作,安装过程需要一些时间。

安装完成后,让我们测试一下安装情况。打开命令提示符,输入conda list命令。你应该能看到一个包含所有已安装库和包的列表,这些库和包是通过 Anaconda 安装的:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/eaf4227c-4431-4666-9847-a5810508a2dc.png

如果没有得到这个输出,请参考我们之前查看的官方文档页面,并重试。

Anaconda 的优点

让我们讨论使用包管理工具的一些优点:

  • Anaconda 允许我们创建环境以安装库和包。这个环境完全独立于操作系统或管理员库。这意味着我们可以为特定项目创建自定义版本的库的用户级环境,从而帮助我们以最小的努力在不同操作系统之间迁移项目。

  • Anaconda 可以拥有多个环境,每个环境都有不同版本的 Python 和支持库。这样可以避免版本不匹配,并且不受操作系统中现有包和库的影响。

  • Anaconda 预装了大多数数据科学相关任务所需的包和库,包括一个非常流行的交互式 Python 编辑器——Jupyter Notebook。在本书中,我们将大量使用 Jupyter Notebook,特别是在需要交互式编码任务时。

创建 Anaconda 环境

我们将创建一个名为ai-projects的环境,使用 Python 版本 3.6。所有的依赖项都将安装在这个环境中:

conda create -n ai-projects python=3.6 anaconda

现在,继续并接受你看到的提示,你应该看到如下的输出:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/0e2e117d-df7b-4639-a8b6-c7e28ff6b533.png

在我们开始安装依赖项之前,我们需要使用activate ai-projects命令激活我们刚刚创建的环境,如果你使用的是 bash shell,可以输入source activate ai-projects。提示符会发生变化,表明环境已被激活:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/6ea56944-b878-4bff-996f-a04e75176c77.png

安装依赖项

首先,让我们安装 TensorFlow。它是一个开源框架,用于构建人工神经网络ANN):

pip install tensorflow

你应该看到以下输出,表示安装成功:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/b31ba28b-8559-4a06-872c-9d6f6b0a3c23.jpg

我们还可以手动检查安装情况。在命令行输入python打开 Python 提示符。进入 Python 提示符后,输入import tensorflow并按Enter。你应该看到以下输出:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c27ee804-673f-4076-aaf8-9cdbe2fa926c.png

输入exit()返回到默认命令行,记住我们仍然在ai-projects conda 环境中。

接下来,我们将安装 Keras,它是 TensorFlow 的一个封装器,使得设计深度神经网络更加直观。我们继续使用pip命令:

pip install keras

安装成功后,我们应该看到以下输出:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/c0c4df7c-c974-497b-8602-3630003cd1b9.jpg

要手动检查安装情况,在命令行输入python打开 Python 提示符。进入 Python 提示符后,输入import keras并按Enter。你应该看到以下输出,没有错误。请注意,输出中提到 Keras 正在使用 TensorFlow 作为其后端:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/aaa69964-684b-40a2-86f6-8612d33bd511.jpg

太好了!我们现在已经安装了创建我们自己神经网络所需的主要依赖项。接下来,让我们构建一个 ANN 来预测房地产价格。

使用 Keras 和 TensorFlow 构建用于预测的 ANN 模型

现在我们已经安装了必要的库,让我们创建一个名为aibook的文件夹,在其中创建另一个名为chapter2的文件夹。将本章的所有代码移动到chapter2文件夹中。确保 conda 环境仍然处于激活状态(提示符将以环境名称开头):

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/2f8d52ea-805c-46a4-8937-ef6c14f333d2.jpg

一旦进入chapter2文件夹,输入jupyter notebook。这将在浏览器中打开一个交互式 Python 编辑器。

在右上角使用“New”下拉菜单创建一个新的 Python 3 笔记本:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/e30c7760-1f13-4aa1-ab80-9b62b18f8643.png

我们现在准备使用 Keras 和 TensorFlow 构建第一个 ANN 模型,用于预测房地产价格:

  1. 导入我们为此练习所需的所有库。使用第一个单元格导入所有库并运行它。这里是我们将使用的四个主要库:
    • pandas:我们用它来读取数据并将其存储在数据框中

    • sklearn:我们用它来标准化数据和进行 k 折交叉验证

    • keras:我们用它来构建顺序神经网络

    • numpy:我们使用numpy进行所有数学和数组操作

让我们导入这些库:

import numpy
import pandas as pd
from keras.models import Sequential
from keras.layers import Dense
from keras import optimizers
from keras.wrappers.scikit_learn import KerasRegressor
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
  1. 使用pandas加载房地产数据:
dataframe = pd.read_csv("housing.csv", sep=',', header=0)
dataset = dataframe.values
  1. 要查看特征变量、目标变量以及数据的几行,请输入以下内容:
dataframe.head()

这个输出将是几行dataframe,如以下截图所示:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/251b1699-eadc-4873-a7ae-87182e276c88.png

数据集有八列,每列的详细信息如下:

  • BIZPROP:每个城镇的非零售商业用地比例

  • ROOMS:每个住宅的平均房间数

  • AGE:建于 1940 年之前的自有住房单位的比例

  • HIGHWAYS:通往放射状高速公路的可达性指数

  • TAX:每$10,000 的全额财产税率

  • PTRATIO:每个城镇的师生比

  • LSTAT:低社会阶层人口的百分比

  • VALUE:拥有者自住住房的中位数价值,单位为千美元(目标变量)

在我们的应用场景中,我们需要预测 VALUE 列,因此我们需要将数据框分为特征和目标值。我们将使用 70/30 的分割比例,即 70%的数据用于训练,30%的数据用于测试:

features = dataset[:,0:7]
target = dataset[:,7]

此外,为了确保我们能重现结果,我们为随机生成设置一个种子。这个随机函数在交叉验证时用于随机抽样数据:

# fix random seed for reproducibility 
seed = 9 
numpy.random.seed(seed)

现在我们准备构建我们的人工神经网络(ANN):

  1. 创建一个具有简单且浅层架构的顺序神经网络。

  2. 创建一个名为simple_shallow_seq_net()的函数,定义神经网络的架构:

def simple_shallow_seq_net():
   # create a sequential ANN 
    model = Sequential() 
    model.add(Dense(7, input_dim=7, kernel_initializer='normal', activation='sigmoid')) 
    model.add(Dense(1, kernel_initializer='normal')) 
    sgd = optimizers.SGD(lr=0.01) 
    model.compile(loss='mean_squared_error', optimizer=sgd) 
    return model
  1. 该函数执行以下操作:
model = Sequential()
  1. 创建一个顺序模型——顺序模型是一个通过线性堆叠的层构建的 ANN 模型:
model.add(Dense(7, input_dim=7, kernel_initializer='normal', activation='sigmoid'))
  1. 在这里,我们向这个顺序网络添加了一个具有七个神经元的稠密层或全连接层。此层接受具有7个特征的输入(因为有七个输入或特征用于预测房价),这由input_dim参数指示。此层所有神经元的权重都使用随机正态分布初始化,这由kernel_initializer参数指示。同样,此层所有神经元使用 sigmoid 激活函数,这由activation参数指示:
model.add(Dense(1, kernel_initializer='normal'))
  1. 添加一个使用随机正态分布初始化的单神经元层:
sgd = optimizers.SGD(lr=0.01)
  1. 设置网络使用标量梯度下降SGD)进行学习,通常作为optimizers指定。我们还表明网络将在每一步学习中使用学习率(lr)为0.01
model.compile(loss='mean_squared_error', optimizer=sgd)
  1. 指示网络需要使用均方误差MSE)代价函数来衡量模型的误差幅度,并使用 SGD 优化器从模型的错误率或损失中学习:
return model

最后,该函数返回一个具有定义规范的模型。

下一步是设置一个用于可重现性的随机种子;此随机函数用于将数据分成训练和验证集。使用的方法是 k-fold 验证,其中数据随机分为 10 个子集进行训练和验证:

seed = 9 
kfold = KFold(n_splits=10, random_state=seed)

现在,我们需要适应这个模型以预测数值(在这种情况下是房价),因此我们使用KerasRegressorKerasRegressor是一个 Keras 包装器,用于访问sklearn中的回归估计器模型:

estimator = KerasRegressor(build_fn=simple_shallow_seq_netl, epochs=100, batch_size=50, verbose=0)

注意以下事项:

  • 我们将simple_shallow_seq_net作为参数传递,以指示返回模型的函数。

  • epochs参数表示每个样本需要至少经过网络100次。

  • batch_size参数表示在每次学习周期中,网络同时使用50个训练样本。

下一步是训练和跨验证数据子集,并打印 MSE,这是评估模型表现的度量:

results = cross_val_score(estimator, features, target, cv=kfold) 
print("simple_shallow_seq_model:(%.2f) MSE" % (results.std()))

这将输出 MSE - 如您所见,这个值相当高,我们需要尽可能地降低它:

simple_shallow_seq_net:(163.41) MSE

保存这个模型以备后用:

estimator.fit(features, target)
estimator.model.save('simple_shallow_seq_net.h5')

很好,我们已经建立并保存了第一个用于预测房地产价格的神经网络。我们接下来的努力是改进这个神经网络。在调整网络参数之前,首先尝试的是在标准化数据并使用它时提高其性能(降低 MSE):

estimators = [] 
estimators.append(('standardize', StandardScaler())) 
estimators.append(('estimator', KerasRegressor(build_fn=simple_shallow_seq_net, epochs=100, batch_size=50, verbose=0))) 
pipeline = Pipeline(estimators)

在上述代码中,我们创建了一个流水线来标准化数据,然后在每个学习周期中使用它。在以下代码块中,我们训练和交叉评估神经网络:

results = cross_val_score(pipeline, features, target, cv=kfold) 
print("simple_std_shallow_seq_net:(%.2f) MSE" % (results.std()))

这将输出比以前好得多的 MSE,因此标准化和使用数据确实产生了差异:

simple_std_shallow_seq_net:(65.55) MSE

保存这个模型与之前略有不同,因为我们使用了pipeline来拟合模型:

pipeline.fit(features, target) 
pipeline.named_steps['estimator'].model.save('standardised_shallow_seq_net.h5')

现在,让我们调整一下我们的网络,看看能否获得更好的结果。我们可以从创建一个更深的网络开始。我们将增加隐藏层或全连接层的数量,并在交替的层中使用sigmoidtanh激活函数:

def deep_seq_net(): 
    # create a deep sequential model 
    model = Sequential() 
    model.add(Dense(7, input_dim=7, kernel_initializer='normal', activation='sigmoid')) 
    model.add(Dense(7,activation='tanh')) 
    model.add(Dense(7,activation='sigmoid')) 
    model.add(Dense(7,activation='tanh')) 
    model.add(Dense(1, kernel_initializer='normal')) 
    sgd = optimizers.SGD(lr=0.01) 
    model.compile(loss='mean_squared_error', optimizer=sgd) 
    return model

下一个代码块用于标准化训练数据中的变量,然后将浅层神经网络模型拟合到训练数据中。创建管道并使用标准化数据拟合模型:

estimators = [] 
estimators.append(('standardize', StandardScaler())) estimators.append(('estimator', KerasRegressor(build_fn=deep_seq_net, epochs=100, batch_size=50, verbose=0))) 
pipeline = Pipeline(estimators)

现在,我们需要在数据的各个子集上交叉验证拟合模型并打印均方误差(MSE):

results = cross_val_score(pipeline, features, target, cv=kfold) 
print("simple_std_shallow_seq_net:(%.2f) MSE" % (results.std()))

这将输出一个比我们之前创建的浅层网络更好的 MSE:

deep_seq_net:(58.79) MSE

保存模型以便后续使用:

pipeline.fit(features, target) 
pipeline.named_steps['estimator'].model.save('deep_seq_net.h5')

因此,当我们增加网络的深度(层数)时,结果会更好。现在,让我们看看当我们加宽网络时会发生什么,也就是说,增加每一层中神经元(节点)的数量。我们定义一个深而宽的网络来解决这个问题,将每一层的神经元数增加到21。此外,这次我们将在隐藏层中使用relusigmoid激活函数:

def deep_and_wide_net(): 
    # create a sequential model 
    model = Sequential() 
    model.add(Dense(21, input_dim=7, kernel_initializer='normal', activation='relu')) 
    model.add(Dense(21,activation='relu')) 
    model.add(Dense(21,activation='relu')) 
    model.add(Dense(21,activation='sigmoid')) 
    model.add(Dense(1, kernel_initializer='normal')) 
    sgd = optimizers.SGD(lr=0.01) 
    model.compile(loss='mean_squared_error', optimizer=sgd) 
    return model

下一个代码块用于标准化训练数据中的变量,然后将深而宽的神经网络模型拟合到训练数据中:

estimators = [] 
estimators.append(('standardize', StandardScaler())) 
estimators.append(('estimator', KerasRegressor(build_fn=deep_and_wide_net, epochs=100, batch_size=50, verbose=0))) 
pipeline = Pipeline(estimators)

现在,我们需要在数据的各个子集上交叉验证拟合模型并打印均方误差(MSE):

results = cross_val_score(pipeline, features, target, cv=kfold) 
print("deep_and_wide_model:(%.2f) MSE" % (results.std()))

这次,MSE 再次优于我们之前创建的网络。这是一个很好的例子,展示了更深的网络和更多的神经元如何更好地抽象问题:

deep_and_wide_net:(34.43) MSE

最后,保存网络以便后续使用。保存的网络模型将在下一节中使用,并通过 REST API 提供服务:

pipeline.fit(features, target) 
pipeline.named_steps['estimator'].model.save('deep_and_wide_net.h5')

到目前为止,我们已经能够利用各种网络架构构建一个用于预测的序列神经网络。作为练习,尝试以下内容:

  • 尝试调整网络的形状;玩玩网络的深度和宽度,看看它如何影响输出结果

  • 尝试不同的激活函数(keras.io/activations/

  • 尝试不同的初始化器,这里我们只使用了随机正态初始化器(keras.io/initializers/

  • 我们在这里使用的数据是为了演示该技术,因此可以尝试在其他数据集上使用上述技术进行预测的不同用例(data.world/datasets/prediction

我们将在第四章,构建一个用于分类花卉物种的机器视觉移动应用程序中了解更多关于优化器和正则化器的知识,这些是你可以用来调整网络的其他参数。我们创建 ANN 模型的完整代码作为 Python 笔记本文件,名为sequence_networks_for_prediction.ipynb

将模型作为 API 提供服务

现在我们已经创建了一个预测模型,接下来需要通过 RESTful API 来服务这个模型。为此,我们将使用一个轻量级的 Python 框架 Flask:flask.pocoo.org/

如果我们的 conda 环境中尚未安装 Flask 库,首先安装它:

pip install Flask

构建一个简单的 API 来添加两个数字

现在我们将构建一个非常简单的 API,以掌握 Flask 库和框架。这个 API 将接受一个包含两个数字的 JSON 对象,并返回这两个数字的和作为响应。

从 Jupyter 主页面打开一个新的 notebook:

  1. 导入我们需要的所有库,并创建一个应用实例:
from flask import Flask, request 
app = Flask(__name__)
  1. 使用 route() 装饰器创建 RESTful API 的首页:
@app.route('/') 
def hello_world(): 
 return 'This is the Index page'
  1. 使用 route() 装饰器创建一个 POST API 来添加两个数字。这个 API 接受一个包含要添加的数字的 JSON 对象:
@app.route('/add', methods=['POST']) 
def add(): 
    req_data = request.get_json() 
    number_1 = req_data['number_1'] 
    number_2 = req_data['number_2'] 
    return str(int(number_1)+int(number_2))

保存 Python notebook,并使用文件菜单将其下载为 Python 文件。将 Python 文件放置在与模型文件相同的目录中。

启动一个新的命令终端,进入包含此 Python 文件和模型的文件夹。确保激活 conda 环境,并运行以下命令启动一个服务器来运行简单的 API:

  • 如果你在使用 Windows,输入以下命令:
set FLASK_APP=simple_api
  • 如果你不是在使用 Windows,输入以下命令:
export FLASK_APP=simple_api

然后输入以下命令:

flask run

当服务器启动时,你应该看到以下输出:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/77ddebb5-0ed8-49cd-8ae8-66da25492323.jpg

打开浏览器,将此地址粘贴到 URL 栏中以访问首页:http://127.0.0.1:5000/。以下是输出:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/43df027c-5e68-4e2e-aa8e-5c3233c7e2be.png

接下来,我们将使用 curl 来访问添加两个数字的 POST API。打开一个新的终端,并输入以下 curl 命令来测试 /add API。此示例中要添加的数字是 12,并作为 JSON 对象传递:

curl -i -X POST -H "Content-Type: application/json" -d "{\"number_1\":\"1\",\"number_2\":\"2\"}" http://127.0.0.1:5000/add

如果没有错误,我们将收到一个包含数字和的响应:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/cec53b9e-bbf0-43d0-b2ad-18bfda3a0dab.png

简单 API 的完整代码可以在名为 simple_api.ipynb 的 Python notebook 文件中找到,也可以在名为 simple_api.py 的 Python 文件中找到。

构建一个 API 来预测使用保存的模型进行房地产价格预测

现在我们已经了解了 Flask 的工作原理,我们需要实现一个 API 来服务我们之前构建的模型。启动一个新的 Jupyter Notebook 并按照以下步骤操作:

  1. 导入所需的 Python 模块并创建一个 Flask 应用实例:
from flask import Flask, request 
from keras.models import load_model
from keras import backend as K

import numpy 
app = Flask(__name__)
  1. 使用 route() 装饰器为 RESTful API 创建 Index page
@app.route('/') 
def hello_world(): 
    return 'Index page'
  1. 创建一个 POST API 来预测房价,使用 route() 装饰器。该 API 接受一个包含预测房价所需所有特征的 JSON 对象:
@app.route('/predict', methods=['POST']) 
def add(): 
    req_data = request.get_json() 
     bizprop = req_data['bizprop'] 
    rooms = req_data['rooms'] 
    age = req_data['age'] 
    highways = req_data['highways'] 
    tax = req_data['tax'] 
    ptratio = req_data['ptratio'] 
    lstat = req_data['lstat'] 
    # This is where we load the actual saved model into new variable. 
    deep_and_wide_net = load_model('deep_and_wide_net.h5') 
    # Now we can use this to predict on new data 
    value = deep_and_wide_net.predict_on_batch(numpy.array([[bizprop, rooms, age  ,  highways   , tax   ,  ptratio  ,   lstat]], dtype=float)) 
    K.clear_session()

    return str(value)

保存 Python notebook,并使用文件菜单将其下载为 Python 文件。将 Python 文件放置在与模型文件相同的目录中。

启动新的命令终端并转到包含此 Python 文件和模型的文件夹。确保激活 conda 环境并运行以下内容以启动运行简单 API 的服务器:

  • 如果您使用的是 Windows,请输入以下内容:
set FLASK_APP=predict_api
  • 如果您不使用 Windows,请使用以下内容:
export FLASK_APP= predict_api

然后输入以下内容:

flask run

接下来,我们将使用 curl 访问预测房价的 POST API。打开新的终端并输入以下 curl 命令以测试 /predict API。我们可以将要用作模型输入的特征作为 JSON 对象传递:

curl -i -X POST -H "Content-Type: application/json" -d "{\"bizprop\":\"1\",\"rooms\":\"2\",\"age\":\"1\",\"highways\":\"1\",\"tax\":\"1\",\"ptratio\":\"1\",\"lstat\":\"1\"}" http://127.0.0.1:5000/predict

这将输出根据提供的特征预测的房价:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/19cdc927-bbf0-4479-b019-3ee9ddec2f73.jpg

就这样!我们刚刚构建了一个 API 来提供我们的预测模型,并使用 curl 进行了测试。预测 API 的完整代码以 Python 笔记本 predict_api.ipynb 和 Python 文件 simple_api.py 的形式提供。

接下来,我们将看到如何制作一个移动应用程序,该应用程序将使用托管我们模型的 API。我们将首先创建一个使用预测 API 的 Android 应用程序,然后在 iOS 应用程序上重复相同的任务。

创建一个 Android 应用程序来预测房价

在本节中,我们将通过 Android 应用程序的 RESTful API 消耗模型。本节的目的是演示模型如何被 Android 应用程序消耗和使用。在这里,我们假设您熟悉 Java 编程的基础知识。相同的方法也可以用于任何类似的用例,甚至是 Web 应用程序。本节涵盖以下步骤:

  • 下载并安装 Android Studio

  • 创建一个具有单个屏幕的新 Android 项目

  • 设计屏幕布局

  • 添加接受输入功能

  • 添加消耗模型提供的 RESTful API 功能

  • 附加说明

下载并安装 Android Studio

Android Studio 是用于 Android 应用开发的开发环境和沙盒。所有我们的 Android 项目都将使用 Android Studio 制作。我们可以使用 Android Studio 创建、设计和测试应用程序,然后再发布它们。

前往官方 Android Studio 下载页面,developer.android.com/studio/,并选择与您操作系统匹配的版本。在本例中,我们使用的是 Windows 可执行文件:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/16d203e0-62e1-4319-87dd-91084a31fc4c.png

下载可执行文件后,运行以开始安装过程。您将看到逐步安装菜单选项。选择“下一步”并继续安装过程。在安装步骤中,大多数选项都将选择默认设置。

创建一个具有单个屏幕的新 Android 项目

现在我们已经安装了 Android Studio,我们将创建一个简单的应用程序来根据某些输入估算房地产的价格。

一旦启动 Android Studio,它将提供一个菜单来开始创建项目。点击“开始一个新的 Android Studio 项目”选项:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/4131112d-51a2-4203-a771-42bd378e4f3d.png

下一个对话框是选择应用名称和项目位置。选择你想要的,并点击下一步:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/d9aae155-2d19-4601-878a-fd8008993202.png

接下来,选择应用程序要运行的目标版本:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/1ba1f6d5-aa22-4139-b019-a890b09584de.png

然后选择一个应用屏幕;在这种情况下,选择一个空白活动:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/88b0a687-fec0-4f7f-af95-54a2a79ccb4e.png

选择屏幕或活动名称以及相应的布局或活动屏幕的设计名称:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/80508382-73a6-420d-b677-60fe007b8dc0.png

在构建完成后,项目应该会在几秒钟内加载。在项目结构中,有三个主要文件夹:

  • manifests:这个文件夹包含了用于权限和应用版本管理的 manifest 文件。

  • java:这个文件夹包含了所有的 Java 代码文件(java|app|chapter2|realestateprediction|MainActivity.java)。

  • res:这个文件夹包含了应用程序中使用的所有布局文件和媒体文件(res|layout|activity_main.xml):

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/5821d790-164b-41e5-81d8-0e771d0a4d36.png

设计屏幕的布局

让我们设计一个屏幕,接受我们创建的模型的输入因素。屏幕将有七个输入框来接受这些因素,一个按钮和一个输出文本框来显示预测结果:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/0b6aa2a2-7cdf-432b-b49c-5a1a058e6afd.jpg

浏览到 res 文件夹中的 layout 文件夹,并选择 activity_layout.xml 文件在编辑面板中打开。选择底部的 Text 选项以查看现有的布局 XML:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/073f2eb7-11ec-4426-899a-2693b6247880.png

现在,替换现有的 XML 代码,使用新的设计模板。请参考 Android 文件夹中的 activity_layout.xml 代码文件以查看完整的设计模板。以下仅是 XML 代码模板的骨架参考:

*<?*xml version="1.0" encoding="utf-8"*?>* <ScrollView 
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fillViewport="true">   
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         > 
         <TextView
             android:id="@+id/bizprop"/>
         <EditText
             android:id="@+id/bizprop-edit"/>
         <TextView
             android:id="@+id/rooms"/>
         <EditText
             android:id="@+id/rooms-edit"/>
         <TextView
             android:id="@+id/age"/>
         <EditText
             android:id="@+id/age-edit"/>
         <TextView
             android:id="@+id/highways"/>
         <EditText
             android:id="@+id/highways-edit"/>
         <TextView
             android:id="@+id/tax"/>
         <EditText
             android:id="@+id/tax-edit"/>
         <TextView
             android:id="@+id/ptratio"/>
         <EditText
             android:id="@+id/ptratio-edit"/>
         <TextView
             android:id="@+id/lstat"/>
         <EditText
             android:id="@+id/lstat-edit"/>
         <Button
             android:id="@+id/button"/>
         <TextView
             android:id="@+id/value"/>
     </RelativeLayout>
 </ScrollView>

在这里,我们设计了一个布局来接受七个因素作为输入,具体如下:

  • BIZPROP:每个城镇非零售商业用地的比例

  • ROOMS:每个住宅的平均房间数

  • A****GE:1940 年之前建成的业主自住单元的比例

  • HIGHWAYS:通往辐射状高速公路的可达性指数

  • TAX:每$10,000 的全值财产税率

  • PTRATIO:每个城镇的学生与教师比例

  • LSTAT:低社会阶层人口的比例

还有一个按钮和一个文本框用于显示输出。当点击按钮时,预测值将显示出来。

要查看活动的设计,可以在顶部菜单栏的run菜单中选择运行应用程序选项。第一次运行时,环境会提示你创建一个虚拟设备来测试你的应用程序。你可以创建一个Android 虚拟设备AVD)或使用传统的方法,即使用 USB 线将你的 Android 手机连接到 PC,这样你就可以直接在设备上运行输出:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/e518d696-637a-457a-be1a-121b5203a0f1.png

当应用在设备或 AVD 模拟器上启动时,你应该能看到滚动布局的设计:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/1c8e556b-4586-4f6b-9e1e-cea5afd364c7.png

添加一个功能来接受输入

现在,我们需要接受输入并创建一个映射来保存这些值。然后,我们将把这个映射转换为一个 JSON 对象,以便它可以作为数据传递给POST API 请求。

浏览到MainActivity.java文件并在 Android Studio 的编辑面板中打开它。声明以下类变量:

 private EditText bizprop, rooms, age, highways, tax, ptratio, lstat;
 private Button estimate;
 private TextView value;

你会看到一个名为onCreate()的函数已经创建。将以下代码添加到onCreate()函数中,以初始化布局元素:

 bizprop = (EditText) findViewById(R.id.*bizprop_edit*);
 rooms = (EditText) findViewById(R.id.*rooms_edit*);
 age = (EditText) findViewById(R.id.*age_edit*);
 highways = (EditText) findViewById(R.id.*highways_edit*);
 tax = (EditText) findViewById(R.id.*tax_edit*);
 ptratio = (EditText) findViewById(R.id.*ptratio_edit*);
 lstat = (EditText) findViewById(R.id.*lstat_edit*);
 value =  (TextView) findViewById(R.id.*value*);
 estimate = (Button) findViewById(R.id.*button*);

现在,向 Java 类中添加另一个名为makeJSON()的函数。这个函数接受来自编辑框的值,并返回我们需要传递的 JSON 对象,以供 API 调用使用:

public JSONObject makeJSON() {

     JSONObject jObj = new JSONObject();
     try {

         jObj.put("bizprop", bizprop.getText().toString());
         jObj.put("rooms",  rooms.getText().toString());
         jObj.put("age",  age.getText().toString());
         jObj.put("tax",  tax.getText().toString() );
         jObj.put("highways",  highways.getText().toString());
         jObj.put("ptratio",  ptratio.getText().toString());
         jObj.put("lstat", lstat.getText().toString());

     } catch (Exception e) {
         System.*out*.println("Error:" + e);
     }

     Log.*i*("", jObj.toString());

     return jObj;
 }

添加一个功能来调用提供模型的 RESTful API

现在,我们需要在按钮点击时提交数据给 API。为此,我们需要以下辅助函数:

  • ByPostMethod: 接受一个 URL 作为String并返回一个InputStream作为响应。这个函数接受我们使用 Flask 框架创建的服务器 URL 字符串,并返回来自服务器的响应流:
InputStream ByPostMethod(String ServerURL) {

     InputStream DataInputStream = null;
     try {
         URL url = new URL(ServerURL);

         HttpURLConnection connection = (HttpURLConnection)
         url.openConnection();
         connection.setDoOutput(true);
         connection.setDoInput(true);
         connection.setInstanceFollowRedirects(false);
         connection.setRequestMethod("POST");
         connection.setRequestProperty("Content-Type", "application/json");
         connection.setRequestProperty("charset", "utf-8");
         connection.setUseCaches (false);
         DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
         dos.writeBytes(makeJSON().toString());
         *//flushes data output stream.* dos.flush();
         dos.close();
         *//Getting HTTP response code* int response = connection.getResponseCode();
         *//if response code is 200 / OK then read Inputstream
         //HttpURLConnection.HTTP_OK is equal to 200* if(response == HttpURLConnection.*HTTP_OK*) {
             DataInputStream = connection.getInputStream();
         }

     } catch (Exception e) {
         Log.*e*("ERROR CAUGHT", "Error in GetData", e);
     }
     return DataInputStream;

 }
  • ConvertStreamToString: 这个函数接受InputStream并返回响应的String。前一个函数返回的输入流将被此函数处理为字符串对象:
String ConvertStreamToString(InputStream stream) {

     InputStreamReader isr = new InputStreamReader(stream);
     BufferedReader reader = new BufferedReader(isr);
     StringBuilder response = new StringBuilder();

     String line = null;
     try {

         while ((line = reader.readLine()) != null) {
             response.append(line);
         }

     } catch (IOException e) {
         Log.*e*("ERROR CAUGHT", "Error in ConvertStreamToString", e);
     } catch (Exception e) {
         Log.*e*("ERROR CAUGHT", "Error in ConvertStreamToString", e);
     } finally {

         try {
             stream.close();

         } catch (IOException e) {
             Log.*e*("ERROR CAUGHT", "Error in ConvertStreamToString", e);

         } catch (Exception e) {
             Log.*e*("ERROR CAUGHT", "Error in ConvertStreamToString", e);
         }
     }
     return response.toString();
 }
  • DisplayMessage: 这个函数更新文本框内容,显示响应,即预测值:
public void DisplayMessage(String a) 
{

     value.setText(a);
 }

需要注意的是,在 Android 上进行网络调用时,最佳实践是将其放在单独的线程中,以避免阻塞主用户界面UI)线程。因此,我们将编写一个名为MakeNetworkCall的内部类来实现这一点:

private class MakeNetworkCall extends AsyncTask<String, Void, String> {

     @Override
     protected void onPreExecute() {
         super.onPreExecute();
         DisplayMessage("Please Wait ...");
     }

     @Override
     protected String doInBackground(String... arg) {

         InputStream is = null;
         String URL = "http://10.0.2.2:5000/predict";
         Log.*d*("ERROR CAUGHT", "URL: " + URL);
         String res = "";

         is = ByPostMethod(URL);

         if (is != null) {
             res = ConvertStreamToString(is);
         } else {
             res = "Something went wrong";
         }
         return res;
     }

     protected void onPostExecute(String result) {
         super.onPostExecute(result);

         DisplayMessage(result);
         Log.*d*("COMPLETED", "Result: " + result);
     }
 }

请注意,我们使用了http://10.0.2.2:5000/predict而不是http://127.0.0.1:5000/predict。这是因为在 Android 中,当我们使用模拟器时,它通过10.0.2.2访问本地主机,而不是127.0.0.1。由于示例是在模拟器中运行的,因此我们使用了10.0.2.2

最后,我们需要添加一个功能,当按钮被点击时调用 API。因此,在oncreate()方法中,在按钮初始化后插入以下代码。这将启动一个后台线程,在按钮点击时访问 API:

estimate.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         Log.*i*("CHECK", "CLICKED");

         new MakeNetworkCall().execute("http://10.0.2.2:5000/predict", "Post");
     }
 });

我们需要在AndroidManifest.xml文件中添加使用互联网的权限。将以下代码放入<manifest>标签内:

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

别忘了运行你的 Flask 应用。如果你还没有运行,请确保在激活的 conda 环境中运行它:

set FLASK_APP=predict_api

flask run

这就是在 Android 上运行和测试应用所需的所有代码。现在,在模拟器中运行应用,输入屏幕上的信息,点击 ESTIMATE VALUE 按钮,你将立即获得结果:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/fe3b6dbc-7420-4f10-9f37-995f866d0c02.png

额外说明

这是一个演示,展示了如何在 Android 设备上使用构建的 AI 模型。话虽如此,仍有许多其他任务可以添加到现有的应用程序中:

  • 改进 UI 设计

  • 添加输入检查以验证输入的数据

  • 托管 Flask 应用程序(Heroku、AWS 等)

  • 发布应用程序

所有这些任务都与我们的核心 AI 主题无关,因此可以作为读者的练习来处理。

创建一个用于预测房价的 iOS 应用程序

在本节中,我们将通过 iOS 应用程序通过 RESTful API 来使用模型。本节的目的是展示如何通过 iOS 应用程序使用和消费模型。这里假设您已经熟悉 Swift 编程。相同的方法可以用于任何类似的用例。以下是本节所涵盖的步骤:

  • 下载并安装 Xcode

  • 创建一个单屏幕的 iOS 项目

  • 设计屏幕的布局

  • 添加接受输入的功能

  • 添加功能以消费提供模型的 RESTful API

  • 附加说明

下载并安装 Xcode

您需要一台 Mac(macOS 10.11.5 或更高版本)来开发本书中实现的 iOS 应用程序。此外,需要安装 Xcode 的最新版本才能运行这些代码,因为它包含了设计、开发和调试任何应用所必需的所有功能。

要下载最新版本的 Xcode,请按以下步骤操作:

  1. 打开 Mac 上的 App Store(默认在 Dock 中)。

  2. 在搜索框中输入Xcode,它位于右上角。然后按下回车键。

  3. 搜索结果中的第一个就是 Xcode 应用程序。

  4. 点击“获取”,然后点击“安装应用程序”。

  5. 输入您的 Apple ID 和密码。

  6. Xcode 将被下载到您的/Applications目录中。

创建一个单屏幕的 iOS 项目

Xcode 包含多个内置应用程序模板。我们将从一个基本模板开始:单视图应用程序。从/Applications目录中打开 Xcode 以创建一个新项目。

如果您第一次启动 Xcode,它可能会要求您同意所有用户协议。按照这些提示操作,直到 Xcode 安装并准备好在您的系统上启动。

启动 Xcode 后,以下窗口将会出现:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/7a6aa306-1b07-4f5a-a055-31e05a850377.png

点击“创建一个新的 Xcode 项目”。将会打开一个新窗口,显示一个对话框,允许我们选择所需的模板:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/dbdc672b-af4c-4db8-b9b2-fba77d240fea.png

选择模板后,会弹出一个对话框。在这里,您需要为您的应用程序命名,您可以使用以下值。您还可以选择一些附加选项来配置您的项目:

  • 产品名称:Xcode 将使用您输入的产品名称来命名项目和应用程序。

  • 团队:如果没有填写值,请将团队设置为“无”。

  • 组织名称:这是一个可选字段。您可以输入您的组织名称或您的名字。您也可以选择将此选项留空。

  • 组织标识符:如果您有组织标识符,请使用该值。如果没有,请使用com.example

  • 捆绑标识符:此值是根据您的产品名称和组织标识符自动生成的。

  • 语言:Swift。

  • 设备:通用。一个在 iPhone 和 iPad 上都能运行的应用程序被认为是通用应用程序。

  • 使用核心数据:我们不需要核心数据。因此,它保持未选中。

  • 包含单元测试:我们需要包含单元测试。因此,这个选项将被选中。

  • 包含 UI 测试:我们不需要包含任何 UI 测试。因此,这个选项保持未选中。

现在,点击下一步。一个对话框将出现,您需要选择一个位置来保存您的项目。保存项目后,点击创建。您的新项目将由 Xcode 在工作区窗口中打开。

设计屏幕的布局

让我们设计一个屏幕,用于接收我们创建的模型因子的输入。该屏幕将有七个输入框用于接收因子,一个按钮,以及一个输出文本框来显示预测结果:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/589a76d7-b37b-4dc7-9362-302898467a83.jpg

让我们来处理应用所需的故事板。什么是故事板?故事板展示了内容的屏幕及其之间的过渡。它为我们提供了应用程序 UI 的视觉表现。我们可以使用所见即所得WYSIWYG)编辑器,在这里我们可以实时看到更改。

要打开故事板,请在项目导航器中选择Main.storyboard选项。这将打开一个画布,我们可以在其中设计屏幕。现在我们可以添加元素并设计画布:

https://github.com/OpenDocCN/freelearn-dl-pt6-zh/raw/master/docs/mobi-ai-proj/img/675b1bb5-e59c-4d18-a961-81fc301bccbf.png

也可以使用编码来代替拖放方法。为此,从定义作为输入使用的文本字段开始,放在ViewController类中:

@interface ViewController ()<UITextFieldDelegate>
{
 UITextField* bizropfeild,*roomsfeild,*agefeild,*highwaysfeild,*taxfeild,*ptratiofeild,*lstatfeild;
}
@end

然后,在CreateView方法中,我们实现每个文本字段的设计。以下是前两个文本字段的示例;其余文本字段可以采用相同的方法。完成的项目代码可以在chapter2_ios_prediction文件夹中找到。

首先,创建一个标题文本字段,估算房地产价值

UILabel *headerLabel = [[UILabel alloc]initWithFrame:CGRectMake(10, 20, self.view.frame.size.width-20, 25)];
 headerLabel.font = [UIFont fontWithName:@"SnellRoundhand-Black" size:20]; //custom font
 headerLabel.backgroundColor = [UIColor clearColor];
 headerLabel.textColor = [UIColor blackColor];
 headerLabel.textAlignment = NSTextAlignmentCenter;
 headerLabel.text=@"Estimate the value of real estate";
 [self.view addSubview:headerLabel];

 UIView *sepratorLine =[[UIView alloc]initWithFrame:CGRectMake(0, 50, self.view.frame.size.width, 5)];
 sepratorLine.backgroundColor=[UIColor blackColor];
 [self.view addSubview:sepratorLine];

接下来,创建另一个文本字段,输入房地产详细信息

UILabel *detailLabel = [[UILabel alloc]initWithFrame:CGRectMake(10, 55, self.view.frame.size.width-20, 25)];
 detailLabel.font = [UIFont fontWithName:@"SnellRoundhand-Black" size:18]; //custom font
 detailLabel.backgroundColor = [UIColor clearColor];
 detailLabel.textColor = [UIColor blackColor];
 detailLabel.textAlignment = NSTextAlignmentLeft;
 detailLabel.text=@"Enter real estate details";
 [self.view addSubview:detailLabel];

接下来,创建一个字段,用于输入非零售业务面积比例:

 UILabel *bizropLabel = [[UILabel alloc]initWithFrame:CGRectMake(5, 85, self.view.frame.size.width-150, 35)];
 bizropLabel.font = [UIFont fontWithName:@"TimesNewRomanPSMT" size:12]; //custom font
 bizropLabel.backgroundColor = [UIColor clearColor];
 bizropLabel.numberOfLines=2;
 bizropLabel.textColor = [UIColor blackColor];
 bizropLabel.textAlignment = NSTextAlignmentLeft;
 bizropLabel.text=@"Bizrope, The proportion of non-retail business acres per town";
 [self.view addSubview:bizropLabel];

 bizropfeild = [[UITextField alloc] initWithFrame:CGRectMake(self.view.frame.size.width-140, 85, 130, 35)];
 bizropfeild.delegate=self;
 bizropfeild.layer.borderColor=[UIColor blackColor].CGColor;
 bizropfeild.layer.borderWidth=1.0;
 [self.view addSubview:bizropfeild];

现在创建一个字段,用于输入每个住宅的平均房间数:

UILabel *roomsLabel = [[UILabel alloc]initWithFrame:CGRectMake(5, 125, self.view.frame.size.width-150, 35)];
 roomsLabel.font = [UIFont fontWithName:@"TimesNewRomanPSMT" size:12]; //custom font
 roomsLabel.backgroundColor = [UIColor clearColor];
 roomsLabel.numberOfLines=2;
 roomsLabel.textColor = [UIColor blackColor];
 roomsLabel.textAlignment = NSTextAlignmentLeft;
 roomsLabel.text=@"ROOMS, the average number of rooms per dwelling";
 [self.view addSubview:roomsLabel];

 roomsfeild = [[UITextField alloc] initWithFrame:CGRectMake(self.view.frame.size.width-140, 125, 130, 35)];
 roomsfeild.delegate=self;
 roomsfeild.layer.borderColor=[UIColor blackColor].CGColor;
 roomsfeild.layer.borderWidth=1.0;
 [self.view addSubview:roomsfeild];

然后,创建一个按钮来调用 API:

UIButton *estimateButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
 [estimateButton addTarget:self action:@selector(estimateAction)
 forControlEvents:UIControlEventTouchUpInside];
 estimateButton.layer.borderColor=[UIColor blackColor].CGColor;
 estimateButton.layer.borderWidth=1.0;
 [estimateButton setTitle:@"Estimate" forState:UIControlStateNormal];
 [estimateButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
 estimateButton.frame = CGRectMake(self.view.frame.size.width/2-80, 375, 160.0, 40.0);
 [self.view addSubview:estimateButton];

添加接受输入的功能

在这里,所有来自文本字段的输入都被打包成一个NSString对象,并用于请求的POST正文中:

NSString *userUpdate =[NSString stringWithFormat:@"bizprop=%@&rooms=%@&age=%@&highways=%@&tax=%@&ptratio=%@&lstat=%@",bizropfeild.text,roomsfeild.text,agefeild.text,highwaysfeild.text,taxfeild.text,ptratiofeild.text,lstatfeild.text];

添加一个功能来调用提供模型的 RESTful API

现在我们需要使用NSURLSession对象,通过活动屏幕中的输入来调用 RESTful API:

//create the Method "GET" or "POST"
 [urlRequest setHTTPMethod:@"POST"];
 //Convert the String to Data
 NSData *data1 = [userUpdate dataUsingEncoding:NSUTF8StringEncoding];
 //Apply the data to the body
 [urlRequest setHTTPBody:data1];
 NSURLSession *session = [NSURLSession sharedSession];
 NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }

最后,显示从 API 接收到的响应中的输出:

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
 if(httpResponse.statusCode == 200)
 {
 NSError *parseError = nil;
 NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
 NSLog(@"The response is - %@",responseDictionary);
UILabel *outputLabel = [[UILabel alloc]initWithFrame:CGRectMake(5, 325, self.view.frame.size.width-150, 35)];
 outputLabel.font = [UIFont fontWithName:@"TimesNewRomanPSMT" size:12]; //custom font
 outputLabel.backgroundColor = [UIColor clearColor];
 outputLabel.numberOfLines=2;
 outputLabel.textColor = [UIColor blackColor];
 outputLabel.textAlignment = NSTextAlignmentLeft;
 outputLabel.text = [responseDictionary valueForKey:@""];
 [self.view addSubview:outputLabel];
 }

该应用现在可以运行,并准备在模拟器上进行测试。

附加说明

这展示了我们如何在 iOS 设备上使用 AI 模型。话虽如此,现有应用中还有很多可以添加的任务:

  • 改进 UI 设计

  • 添加输入检查以验证输入的数据

  • 托管 Flask 应用(Heroku、AWS 等)

  • 发布应用

所有这些任务与我们的核心 AI 主题无关,因此可以作为读者的练习来处理。安卓和 iOS 应用的完整代码和项目文件分别命名为chapter2_android_predictionchapter2_ios_prediction

总结

在本章中,我们探索了基本的顺序网络,并在移动设备上使用了它。在下一章,我们将探讨一种特殊类型的网络——卷积神经网络CNN)。CNN 是最常用于机器视觉的网络类型。下一章的目标是熟悉机器视觉,并构建我们自己的定制 CNN。

第三章:实现深度神经网络架构来识别手写数字

在前面的章节中,我们已经了解了必要的概念,并设置了启动我们人工智能(AI)之旅所需的工具。我们还构建了一个小型预测应用程序,以便熟悉我们将要使用的工具。

在本章中,我们将讨论 AI 的一个更有趣且更受欢迎的应用——计算机视觉,或称机器视觉。我们将从上一章继续,逐步过渡到构建卷积神经网络CNN),这是计算机视觉中最流行的神经网络类型。本章还将涵盖第一章中承诺的人工智能概念和基础内容,但与之不同的是,本章将采取非常实践的方式。

本章将涵盖以下主题:

  • 构建一个前馈神经网络来识别手写数字

  • 神经网络的其余概念

  • 构建一个更深层的神经网络

  • 计算机视觉简介

构建一个前馈神经网络来识别手写数字,版本一

在这一节中,我们将运用前两章所学的知识来解决一个包含非结构化数据的问题——图像分类。我们的思路是,通过当前的设置和我们熟悉的神经网络基础,深入解决计算机视觉任务。我们已经看到,前馈神经网络可以用于使用结构化数据进行预测;接下来,我们就用它来分类手写数字图像。

为了解决这个任务,我们将利用MNSIT数据库,并使用手写数字数据集。MNSIT 代表的是修改后的国家标准与技术研究院(Modified National Institute of Standards and Technology)。这是一个大型数据库,通常用于训练、测试和基准化与计算机视觉相关的图像任务。

MNSIT 数字数据集包含 60,000 张手写数字图像,用于训练模型,还有 10,000 张手写数字图像,用于测试模型。

从现在开始,我们将使用 Jupyter Notebook 来理解和执行这项任务。所以,如果你还没有启动 Jupyter Notebook,请启动它并创建一个新的 Python Notebook。

一旦你的 Notebook 准备好,第一件要做的,和往常一样,是导入所有必需的模块:

  1. 导入numpy并设置seed以确保结果可复现:
import numpy as np np.random.seed(42)
  1. 加载 Keras 依赖项和内置的 MNSIT 数字数据集:
import keras from keras.datasets import mnist  
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import SGD
  1. 将数据分别加载到训练集和测试集中:
(X_train, y_train), (X_test, y_test)= mnist.load_data()
  1. 检查训练图像的数量以及每张图像的大小。在这个案例中,每张图像的大小是 28 x 28 像素:
X_train.shape
(60000, 28, 28)
  1. 检查因变量,在这种情况下,包含 60,000 个带有正确标签的案例:
y_train.shape
(60000,)
  1. 检查前 100 个训练样本的标签:
y_train [0 :99]  
array([5, 0, 4, 1, 9, 2, 1, 3, 1, 4, 3, 5, 3, 6, 1, 7, 2, 8, 6, 9, 4, 0, 9,
       1, 1, 2, 4, 3, 2, 7, 3, 8, 6, 9, 0, 5, 6, 0, 7, 6, 1, 8, 7, 9, 3, 9,
       8, 5, 9, 3, 3, 0, 7, 4, 9, 8, 0, 9, 4, 1, 4, 4, 6, 0, 4, 5, 6, 1, 0,
       0, 1, 7, 1, 6, 3, 0, 2, 1, 1, 7, 9, 0, 2, 6, 7, 8, 3, 9, 0, 4, 6, 7,
       4, 6, 8, 0, 7, 8, 3], dtype=uint8)
  1. 检查测试图像的数量以及每张图像的大小。在本例中,每张图像的大小是 28 x 28 像素:
X_test.shape
(10000, 28, 28)
  1. 检查测试数据中的样本,这些基本上是 28 x 28 大小的二维数组:
X_test[0]
array([[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,      
          .
          .
,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 121, 254, 207,
         18,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0]], dtype=uint8)
  1. 检查因变量,在本例中是 10,000 个带有正确标签的案例:
y_test.shape
(10000,)
  1. 测试集中第一个样本的正确标签如下:
y_test[0]
7
  1. 现在,我们需要对数据进行预处理,将其从 28 x 28 的二维数组转换为归一化的 784 个元素的一维数组:
X_train = X_train.reshape(60000, 784).astype('float32')
X_test = X_test.reshape(10000, 784).astype('float32')
X_train/=255
X_test /=255
  1. 检查预处理数据集的第一个样本:
X_test[0]
array([ 0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        .
        .
        .
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0.47450981,  0.99607843,  0.99607843,  0.85882354,  0.15686275,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0.47450981,  0.99607843,
        0.81176472,  0.07058824,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ,  0\.        ,
        0\.        ,  0\.        ,  0\.        ,  0\.        ], dtype=float32)
  1. 下一步是对标签进行一热编码;换句话说,我们需要将标签(从零到九)的数据类型从数字转换为类别型:
n_classes=10
y_train= keras.utils.to_categorical(y_train ,n_classes)
y_test= keras.utils.to_categorical(y_test,n_classes)
  1. 查看已经进行过一热编码的标签的第一个样本。在这种情况下,数字是七:
y_test[0]
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.])
  1. 现在,我们需要设计一个简单的前馈神经网络,输入层使用sigmoid激活函数和 64 个神经元。我们将在输出层添加一个softmax函数,通过给出分类标签的概率来进行分类:
model=Sequential()
model.add(Dense(64,activation='sigmoid', input_shape=(784,)))
model.add(Dense(10,activation='softmax'))  
  1. 我们可以通过summary()函数查看我们刚刚设计的神经网络的结构,这是一个简单的网络,具有 64 个神经元的输入层和 10 个神经元的输出层。输出层有 10 个神经元,我们有 10 个分类标签需要预测/分类(从零到九):
model.summary()
_______________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
dense_1 (Dense)              (None, 64)                50240     
_______________________________________________________________
dense_2 (Dense)              (None, 10)                650       
=================================================================
Total params: 50,890
Trainable params: 50,890
Non-trainable params: 0
_________________________________________________________________
  1. 接下来,我们需要配置模型,以便使用优化器、损失函数和度量标准来判断准确性。在这里,使用的优化器是标量梯度下降法(SGD),学习率为 0.01。使用的损失函数是代数均方误差(MSE),用于衡量模型正确性的度量标准是accuracy,即概率分数:
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01),metrics['accuracy'])  
  1. 现在,我们准备好训练模型了。我们希望它每次迭代使用 128 个样本,通过网络进行学习,这由batch_size指示。我们希望每个样本在整个网络中至少迭代 200 次,这由epochs指示。同时,我们指定了用于训练和验证的数据集。Verbose控制控制台上的输出打印:
model.fit(X_train,y_train,batch_size=128,epochs=200,
verbose=1,validation_data =(X_test,y_test))  
  1. 在 60,000 个样本上进行训练,然后在 10,000 个样本上进行验证:
Epoch 1/200
60000/60000 [==============================] - 1s - loss: 0.0915 - acc: 0.0895 - val_loss: 0.0911 - val_acc: 0.0955
Epoch 2/200
.
.
.
60000/60000 [==============================] - 1s - loss: 0.0908 - acc: 
0.8579 - val_loss: 0.0274 - val_acc: 0.8649
Epoch 199/200
60000/60000 [==============================] - 1s - loss: 0.0283 - acc: 0.8585 - val_loss: 0.0273 - val_acc: 0.8656
Epoch 200/200
60000/60000 [==============================] - 1s - loss: 0.0282 - acc: 0.8587 - val_loss: 0.0272 - val_acc: 0.8658
<keras.callbacks.History at 0x7f308e68be48>
  1. 最后,我们可以评估模型以及模型在测试数据集上的预测效果:
model.evaluate(X_test,y_test)
9472/10000 [===========================>..] - ETA: 0s
[0.027176343995332718, 0.86580000000000001]

这可以解释为错误率(MSE)为 0.027,准确率为 0.865,这意味着它在测试数据集上预测正确标签的次数占 86%。

构建一个前馈神经网络来识别手写数字,第二版

在上一部分中,我们构建了一个非常简单的神经网络,只有输入层和输出层。这个简单的神经网络给了我们 86%的准确率。让我们看看通过构建一个比之前版本更深的神经网络,是否能进一步提高这个准确率:

  1. 我们将在一个新的笔记本中进行这项工作。加载数据集和数据预处理将与上一部分相同:
import numpy as np np.random.seed(42)
import keras from keras.datasets import mnist 
from keras.models import Sequential 
from keras.layers import Dense
from keras.optimizers import SG
#loading and pre-processing data
(X_train,y_train), (X_test,y_test)= mnist.load_data()
X_train= X_train.reshape( 60000, 784). astype('float32')
X_test =X_test.reshape(10000,784).astype('float32')
X_train/=255
X_test/=255
  1. 神经网络的设计与之前版本稍有不同。我们将在网络中加入一个包含 64 个神经元的隐藏层,以及输入层和输出层:
model=Sequential()
model.add(Dense(64,activation='relu', input_shape=(784,)))
model.add(Dense(64,activation='relu'))
model.add(Dense(10,activation='softmax'))

  1. 同时,我们将为输入层和隐藏层使用relu激活函数,而不是之前使用的sigmoid函数。

  2. 我们可以如下检查模型设计和架构:

model.summary()
_______________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 64)                50240     
_______________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
_______________________________________________________________
dense_3 (Dense)              (None, 10)                650       
=================================================================
Total params: 55,050
Trainable params: 55,050
Non-trainable params: 0
_________________________________________________________________
  1. 接下来,我们将配置模型,使用派生的categorical_crossentropy代价函数,而不是我们之前使用的 MSE。同时,将学习率从 0.01 提高到 0.1:
model.compile(loss='categorical_crossentropy',optimizer=SGD(lr=0.1), 
metrics =['accuracy']) 
  1. 现在,我们将像之前的例子一样训练模型:
model.fit(X_train,y_train,batch_size=128,epochs=200,verbose=1,validation_data =(X_test,y_test))
  1. 在 60,000 个样本上训练,并在 10,000 个样本上验证:
Epoch 1/200
60000/60000 [==============================] - 1s - loss: 0.4785 - acc: 0.8642 - val_loss: 0.2507 - val_acc: 0.9255
Epoch 2/200
60000/60000 [==============================] - 1s - loss: 0.2245 - acc: 0.9354 - val_loss: 0.1930 - val_acc: 0.9436
.
.
.
60000/60000 [==============================] - 1s - loss: 4.8932e-04 - acc: 1.0000 - val_loss: 0.1241 - val_acc: 0.9774
<keras.callbacks.History at 0x7f3096adadd8>

如你所见,和我们在第一版中构建的模型相比,准确率有所提高。

构建更深的神经网络

在本节中,我们将使用本章所学的概念,构建一个更深的神经网络来分类手写数字:

  1. 我们将从一个新的笔记本开始,然后加载所需的依赖:
import numpy as np np.random.seed(42)
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
*# new!*
from
keras.layers.normalization *# new!*
import
BatchNormalization
*# new!*
from keras import regularizers
*# new!* 
from keras.optimizers
import SGD
  1. 我们现在将加载并预处理数据:
(X_train,y_train),(X_test,y_test)= mnist.load_data()
X_train= X_train.reshape(60000,784).
astype('float32')
X_test= X_test.reshape(10000,784).astype('float32')
X_train/=255
X_test/=255
n_classes=10
y_train=keras.utils.to_categorical(y_train,n_classes)
y_test =keras.utils.to_categorical(y_test,n_classes)

  1. 现在,我们将设计一个更深的神经网络架构,并采取措施防止过拟合,以提供更好的泛化能力:
model=Sequential()
model.add(Dense(64,activation='relu',input_shape=(784,)))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(64,activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(10,activation='softmax'))
model.summary()
_______________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 64)                50240     
_______________________________________________________________
batch_normalization_1 (Batch (None, 64)                256       
_______________________________________________________________
dropout_1 (Dropout)          (None, 64)                0         
_______________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
_______________________________________________________________
batch_normalization_2 (Batch (None, 64)                256       
_______________________________________________________________
dropout_2 (Dropout)          (None, 64)                0         
_______________________________________________________________
dense_3 (Dense)              (None, 10)                650       
=================================================================
Total params: 55,562
Trainable params: 55,306
Non-trainable params: 256_______________________________________________________________
  1. 这次,我们将使用adam优化器来配置模型:
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
  1. 现在,我们将设定训练模型200个周期,批量大小为128
model.fit(X_train, y_train, batch_size= 128, epochs= 200, verbose= 1, validation_data= (X_test,y_test))
  1. 在 60,000 个样本上训练,并在 10,000 个样本上验证:
Epoch 1/200
60000/60000 [==============================] - 3s - loss: 0.8586 - acc: 0.7308 - val_loss: 0.2594 - val_acc: 0.9230
Epoch 2/200
60000/60000 [==============================] - 2s - loss: 0.4370 - acc: 0.8721 - val_loss: 0.2086 - val_acc: 0.9363
.
.
.
Epoch 200/200
60000/60000 [==============================] - 2s - loss: 0.1323 - acc: 0.9589 - val_loss: 0.1136 - val_acc: 0.9690
<keras.callbacks.History at 0x7f321175a748>

计算机视觉简介

计算机视觉可以定义为人工智能的一个子集,我们可以教计算机“看”。我们不能仅仅给机器添加一个相机让它“看”。为了让机器像人类或动物一样真正感知世界,它依赖于计算机视觉和图像识别技术。阅读条形码和人脸识别就是计算机视觉的应用实例。计算机视觉可以描述为人类大脑中处理眼睛感知信息的部分,别无其他。

图像识别是计算机视觉在人工智能领域中一个有趣的应用。从机器通过计算机视觉接收的输入由图像识别系统解读,依据其所见,输出会被分类。

换句话说,我们用眼睛捕捉周围的物体,这些物体/图像在大脑中被处理,使我们能够直观地感知周围的世界。计算机视觉赋予机器这种能力。计算机视觉负责从输入的视频或图像中自动提取、分析并理解所需的信息。

计算机视觉有多种应用,主要用于以下场景:

  • 增强现实

  • 机器人技术

  • 生物特征识别

  • 污染监测

  • 农业

  • 医学图像分析

  • 法医

  • 地球科学

  • 自动驾驶汽车

  • 图像恢复

  • 流程控制

  • 字符识别

  • 遥感

  • 手势分析

  • 安全与监控

  • 人脸识别

  • 交通

  • 零售

  • 工业质量检测

计算机视觉的机器学习

使用适当的机器学习理论和工具非常重要,这对于我们开发涉及图像分类、物体检测等各种应用将非常有帮助。利用这些理论创建计算机视觉应用需要理解一些基本的机器学习概念。

计算机视觉领域的会议

一些值得关注的会议,了解最新的研究成果和应用,如下所示:

  • 计算机视觉与模式识别会议CVPR)每年举行,是最受欢迎的会议之一,涵盖从理论到应用的研究论文,跨越广泛的领域。

  • 国际计算机视觉大会ICCV)是每两年举行一次的另一大会议,吸引着一些最优秀的研究论文。

  • 计算机图形学特别兴趣小组SIGGRAPH)和交互技术,虽然更多集中在计算机图形学领域,但也有几篇应用计算机视觉技术的论文。

其他值得注意的会议包括神经信息处理系统NIPS)、国际机器学习大会ICML)、亚洲计算机视觉大会ACCV)、欧洲计算机视觉大会ECCV)等。

总结

本章中,我们构建了一个前馈神经网络,识别手写数字,并分为两个版本。然后,我们构建了一个神经网络,用于分类手写数字,最后简要介绍了计算机视觉。

在下一章中,我们将构建一个机器视觉移动应用程序,用于分类花卉品种并检索相关信息。

深入阅读

若要深入了解计算机视觉,请参考以下 Packt 出版的书籍:

  • 计算机视觉中的深度学习 由 Rajalingappaa Shanmugamani 编写

  • 实用计算机视觉 由 Abhinav Dadhich 编写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值