书名:移动端机器学习实战
ISBN:978-7-115-51684-8
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 [印度]卡斯基延•NG(Karthikeyan NG)
译 王东明 周达希
责任编辑 谢晓芳
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
本书系统地讲述如何基于TensorFlow Lite和Core ML构建Android与iOS应用程序。本书共9章。第1章介绍机器学习的基础知识以及TensorFlow Lite和Core ML框架。第2~8章介绍如何开发7款常见应用程序,分别是一款预测人物年龄和性别的应用程序,一款在照片上应用艺术风格迁移的应用程序,一款用于面部检测和条形码扫描的应用程序,一款类似于Snapchat的应用程序,一款识别手写数字的应用程序,一款流行的在线换脸应用程序,一款利用迁移学习完成食物分类的应用程序。第9章总结全书,并介绍基于机器学习的云服务。
本书适合机器学习、深度学习和人工智能等方面的专业人士阅读。
Copyright ©2018 Packt Publishing. First published in the English language under the title Machine Learning Projects for Mobile Applications.
All rights reserved.
本书由英国Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。
版权所有,侵权必究。
Karthikeyan NG是印度的一名工程和技术主管。之前他曾经在Symantec公司担任软件工程师,之后就职于两家总部位于美国的初创企业,参与过各种类型的产品开发。他在开发各种可扩展的产品方面拥有超过9年的经验,这些产品使用了Web、Mobile、机器学习(Machine Learning,ML)、增强现实以及虚拟现实技术。他是一名有抱负的企业家和技术传播者,他勇于探索新技术并使用创新理念来解决问题。同时,他还是班加罗尔大学的客座教授。
王东明,从事游戏行业十年,具有丰富的游戏开发经验,熟悉客户端游戏、网页游戏、手机游戏的开发。业余时间,喜欢学习和翻译技术类图书,积极探索各类前沿技术在游戏开发中的应用。
周达希,中国传媒大学硕士,具有5年新媒体运营经验。熟悉互联网行业的各种流行技术,致力于传播专业技术知识。
Mayur Ravindra Narkhede在数据科学和工业领域拥有大量经验。他是一名科研人员,拥有计算机科学专业学士学位,在计算机与工程科学方面拥有硕士学位,他的研究方向是人工智能。
作为一名数据科学家,他擅长构建自动化的端到端解决方案,他精通人工智能(Artificial Intelligence,AI)、机器学习(ML)和数据挖掘技术的应用,同时他对于商业功能和可能增长的需求会提出更好的解决方案。
他曾经参与制定过很多高级解决方案,比如,ML和预测模型开发在石油与天然气、金融服务、交通运输、生命科学等领域中的应用,以及资产密集型行业的大数据平台。
从AlaphGo击败围棋世界冠军李世石并一战成名之后,机器学习逐渐从计算机领域中的专业词汇走进了大众的视野。目前,机器学习已经广泛应用于各行各业,如人类视觉、自然语言处理、图像处理等。机器学习渗透到了信息化生活的方方面面,不管每个人是否注意,他们几乎都会或多或少接触到与机器学习相关的应用。
随着移动智能设备的逐渐普及和硬件水平的提升,机器学习在移动设备上的应用也越来越多。各种硬件新产品的发布也都要将AI与机器学习作为一大卖点。机器学习与移动设备的结合展现了机器学习在移动应用开发领域的魅力。
机器学习是一门复杂的交叉学科,专门研究计算机如何模拟或者实现人类的学习行为,从而不断完善自身。机器学习是人工智能领域的核心技术,它涉及的算法众多,很容易让很多入门者都知难而退。
本书旨在讲述如何在移动应用程序开发领域使用机器学习技术。本书面向机器学习的初学者。本书首先会介绍机器学习的基本概念,然后会通过移动应用程序案例和实际代码,逐步介绍相关的理论知识,从而激发初学者的兴趣。阅读完本书,读者完全可以开发出与机器学习相关的移动端应用程序,从而进一步深入理解机器学习在移动端的应用。
因为本书主要讲述机器学习的实际应用,所以与机器学习相关的理论知识涉及略少,如果要进阶,建议读者在今后持续学习。
因为本书的技术比较新颖,涉及的一些专业词汇在国内尚没有统一的翻译标准,所以术语的翻译可能与今后正规的译法略有差异。限于译者水平,疏漏在所难免,恳请读者批评指正。
王东明
机器学习(Machine Learning,ML)是一门正在高速发展的技术,它专注于计算机程序的开发,这些程序在处理新数据时可以进行修改。机器学习现在已经获得了重大进展,小到机器学习的实际应用,大到人工智能(Artificial Intelligence,AI)。
本书介绍现实生活中多个应用程序的实现,这些应用程序会展示如何使用TensorFlow Lite或者Core ML完成有效率的机器学习。我们将会学习TensorFlow及其扩展(如TensorFlow Lite)的新功能,以开发智能的应用程序(可从复杂的大型数据库中学习)。本书将会深入讨论一些高级话题,如使用卷积神经网络(Convolutional Neural Network,CNN)、循环神经网络(Recurrent Neural Network,RNN)、迁移学习这样的深度神经网络技术来构建深度应用程序,从而掌握深度学习相关的内容。
通过本书,你不但会掌握机器学习中的概念,而且将学会如何实现机器学习和深度学习。同时,在使用TensorFlow Lite和Core ML构建强大的移动端应用程序时,你还将学会如何解决问题以及应对挑战。
本书主要面对想要掌握ML与深度学习的数据科学家、ML专家、深度学习(或者AI)爱好者。读者最好具有一定的Python编程基础。
第1章介绍TensorFlow Lite和Core ML背后的基础知识。
第2章讲述如何构建一款iOS应用程序,这款应用程序可以借助已经存在的数据模型,使用相机或者用户相册中的数据预测人的年龄和性别。
第3章讨论如何将图片转换为Instagram应用程序的风格。
第4章探讨基于Firebase的ML Kit平台。
第5章展示如何使用TensorFlow Lite构建一个AR滤镜,这个滤镜将会使用在Snapchat和Instagram等应用程序上。
第6章介绍如何构建一个用于识别手写数字的Android应用程序。
第7章讲述如何构建一款可以换脸的应用程序。
第8章解释如何使用迁移学习对食物进行分类。
第9章回顾本书讨论的所有的应用程序。
如果你之前开发过移动端应用程序,那么这项技能对于阅读本书将会有极大的帮助。如果之前没有开发过移动端应用程序,那么最好学习一下Android应用程序开发语言(如Java或者Kotlin),还有iOS应用程序开发语言Swift。
如果你拥有Python编程基础,那么它将会帮助你构建自己的数据模型,但是Python技能并不是必需的。
本书介绍的应用程序都是使用MacBook Pro笔记本计算机构建的。对于大部分命令行操作,假设你已经在计算机上安装了bash shell。这些命令行可能在Windows开发环境下无法正常工作。
可以在Packt网站上使用自己的账号下载本书中全部的示例代码。如果你在其他地方购买的本书,那么可以访问Packt网站并注册,代码会通过邮件的形式直接发送给你。
可以通过下面的步骤下载文件。
(1)在Packt网站上登录或注册。
(2)选择SUPPORT选项卡。
(3)单击Code Downloads & Errata。
(4)在Search框中输入本书的名字,并参照屏幕上的提示进行操作。
一旦下载了文件,请确保使用各个平台对应版本的压缩软件解压文件夹。
本书使用的代码也可以从GitHub上下载。一旦代码有更新,GitHub仓库就会进行相应的更新。
在GitHub网站上还有一些其他图书的代码和视频。
我们还提供了一个PDF文档,里面包含了本书使用的所有屏幕截图和彩色图片,可以在Packtpub网站下载。
本书在版式上遵循以下约定。
代码段的格式如下。
def estimate_house_price(sqft, location):
price = < DO MAGIC HERE >
return price
命令行的输入和输出格式如下。
xcode-select --install
粗体:表示新的术语、重要的文字,或者需要在屏幕上让你看到的文字。比如,菜单或者对话框中的文字将会显示成粗体。例如,“在初始化界面中选中Single View App,如下图所示”。
警告或者重要的注意事项用这个图标表示。
小贴士和技巧用这个图标表示。
我们非常欢迎读者的反馈。
一般反馈:如果你对本书的任何章节有疑问,那么请在邮件的标题中写上书名,并将邮件发送到customercare@packtpub.com。
勘误:虽然我们已经尽量保证本书内容的正确性,但可能还会有一些错误。如果你发现了本书中的错误,请报告给我们,我们将会非常感谢。请访问Packt网站,找到本书,单击Errata Submission Form链接,并输入相应的细节。
打击盗版行为:如果你在网上发现本书任何形式的非法副本,请将具体链接或者网站的名称报告给我们,我们将会非常感谢。请将相关信息通过邮件发送到copyright@packt.com。
投稿:如果你精通某个领域同时想要撰写或参与撰写图书,请访问Packt网站。
当你阅读完本书后,请发表评论。你已经阅读了本书,为什么不在购书的网站上发表评论呢?潜在读者将会看到你客观的评论,并根据评论做出是否购买本书的决定,同时Packt会了解你对本书的评价,作者也会看到关于他们撰写的图书的反馈。谢谢!
要获得关于Packt的更多信息,请访问Packt网站。
感谢Saurav Satpathy帮助我完成某些章节的代码。感谢Varsha Shetty给了我编写本书的灵感,还要感谢Rhea Henriques的坚韧不拔。感谢Akshi、Tejas和Sayli以及技术审校者Mayur以及编辑团队。同时感谢开源社区中既可以用于Android又可以用于iOS平台的框架让本书顺利编写完毕。
Karthikeyan NG
本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。
要获得以上本书配套源代码,请在异步社区本书页面中单击,跳转到下载界面,按提示进行操作即可。注意,为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。
如果您是教师,希望获得教学配套资源,请在社区本书页面中直接联系本书的责任编辑。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,单击“提交勘误”,输入勘误信息,单击“提交”按钮即可(见下图)。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/ selfpublish/submission即可)。
如果您所在学校、培训机构或企业想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。
异步社区
微信服务号
当今,计算机在不断发展,设备形式正在发生巨大的改变。过去我们只能在办公室看到计算机,但是现在它可以出现在我们的家里、膝盖上、口袋里以及在手腕上。计算机市场正在变得越来越多样化,同时计算机也越来越智能化。
现在基本上所有的成年人都会携带一个设备,无论我们是否真的需要看手机,据估算,我们每天都会看至少50次智能手机。这些设备影响着我们每天的决策过程。设备上一般都装配一些仿人工智能的应用程序,如Siri、Google Assistant、Alexa或者Cortana。虚拟助手回答任何问题的能力使得这类技术越来越了解人类的需求。在后端,这类系统使用来自所有用户的综合人工智能。与虚拟助手交互得越多,得到的结果就越好。
尽管已经有了这些进步,但是我们距离通过机器创建人脑有多远呢?如果科学的进步可以发明一种控制大脑神经元的方法,那么在不久的未来我们就可以通过机器创建人脑。使用机器模拟人脑的能力可以帮我们解决一些与文本、视觉和音频相关的复杂问题。它们会模拟人脑每天执行的任务——平均来说,人脑每天会做出35 000个决定。
虽然未来我们可以模拟人脑,但是这要付出代价。现在我们找不到一个更廉价的解决方案。相对于人脑,模拟人脑的程序受限于巨大的能量消耗。人脑的功率大约为20W,而模拟人脑同等功能的程序的功率至少为106W。人脑神经元的工作频率大约是200Hz,而常见的微处理器的工作频率大概是2GHz,这比人类大脑快1000万倍。
虽然我们距离复制人脑的距离仍然很遥远,但是我们可以实现一种算法,这种算法可以基于之前的数据以及来自相似设备的数据做出精准的决策。在这里,人工智能将会派上用场。使用提前定义的算法对我们拥有的复杂数据进行模式识别,这种类型的智能(行为)可以为我们提供有用的信息。
当计算机每次可以在不需要明确指示的情况下做出决策的时候,我们就实现了机器学习(Machine Learning,ML)的功能。现在,ML的应用随处可见,比如,识别垃圾邮件,在电商网站上推荐购买商品、在社交媒体的图片中自动标注人脸等。这些例子都使用了基于历史数据的模式识别,以及数据降噪算法和高质量输出算法。当数据越来越多的时候,计算机就可以做出更精准的决策。
因为我们现在通过各种方式访问移动设备,而且我们在这些设备上花费的时间也越来越多,所以让ML模型运行在手机上也是可行的。在移动手机市场中,Android和iOS平台占据了绝大部分。所以,我们将探索TensorFlow Lite和Core ML如何运行在这两个移动平台上。
本章将包含如下内容:
ML是一个描述使用一组通用算法分析数据的过程的概念,它能提供你感兴趣的数据,而不用专门编写特定代码。
可以将ML视为黑箱,前沿科学家使用黑箱来实现一些高级功能,比如,检测癫痫病或者癌症,而你的邮件收件箱将会每天使用黑箱过滤垃圾邮件。
从更高的层面上说,ML可以分为监督式学习和非监督式学习。
对于监督式学习,我们的主要任务是编写一个函数将输入映射到输出。比如,如果有一个输入变量(x)和一个输出变量(y),那么就可以使用某个算法作为从输入到输出的映射函数:
y = f (x)
我们的目标是尽量实现映射函数,这样当有一个输入(x)的时候,我们可以预测出对应的输出变量(y)。
比如,我们有一堆水果和一些篮子。首先,我们将水果和篮子按照标签分类,如苹果、香蕉、草莓等。当将水果和篮子的标签制作好并将水果放到对应的篮子中之后,现在我们的工作就是标记新添加的水果。我们已经学习了所有的水果品种并用标签将它们标记好了。基于之前的经验,我们可以根据水果的颜色、大小和形状来标记水果的品种。
在这种情况下,只有输入数据(x),没有对应的输出变量。非监督式学习的目标是对数据的基础结构或分布情况进行建模,由此从数据中学习更多知识。
在非监督式学习中,我们一开始可能没有任何数据。看一下监督式学习中的例子,现在我们有一篮子水果,需要将它们按类分组。但是,我们事先没有任何数据,也没有接受过训练或者用标签加以区分。在这种情况下,我们需要了解输入的对象所处的领域,因为我们不知道它是水果还是其他的东西。所以,首先,要了解每次输入的全部特征。然后,当有新输入的时候,尽量用已有的特征进行匹配。最后,我们可能会将所有红色的水果放到一个篮子中,所有绿色的水果放到另外一个篮子中。这种分类方法并不精确,我们将它称为非监督式学习。
看一个线性回归的简单例子,它是由TensorFlow实现的。
根据在相同区域中其他不同大小的房屋价格来预测某个房屋的价格(见下图)。
在我们手上有两栋房屋的价格信息,一栋售价是82 000美元,另外一栋售价是55 000美元。现在,我们的任务是预测第3栋房屋的价格。我们知道房屋的价格和对应房屋的面积,这样可以将已有的数据映射到一张图上。我们根据已有的两个数据推测第3栋房屋的价格。
现在可能你想知道如何绘制直线。画一条随机的直线以接近图中标记的所有点。计算每个点到线之间的距离,并将它们加到一起。这样得到一个误差值。使用的算法应该最小化误差,因为最合适的直线拥有更小的误差值。这个过程称为梯度下降(gradient descent)。
首先将指定区域中所有房屋的价格都映射到对应的图中(见下图)。
然后,将已知的两栋房屋的价格绘制到图中(见下图)。
之后,绘制一条直线以尽量接近所有的值。这条直线与数据完美吻合。由此,我们应该可以推断出第3栋房屋的价格(见下图)。
根据第3栋房屋的面积,可以将数据映射到图中。接下来,找出连接所有点的直线。从上图中可以看到,y轴映射到了98 300美元,因此可预测第3栋房屋的价格(见下图)。这个过程叫作线性回归(linear regression)。
我们将问题以伪代码的形式展现出来。
def estimate_house_price(sqft, location):
price = 0
#In my area, the average house costs 2000 per sq.ft
price_per_sqft = 2000
if location == "vegas":
#but some areas cost a bit more
price_per_sqft = 5000
elif location == "newyork":
#and some areas cost less
price_per_sqft = 4000
#start with a base price estimate based on how big the place is
price = price_per_sqft * sqft
return price
上面就是预测房价的常见方法。可以添加很多条件,但是当地点或者其他参数越来越多的时候,代码将会变得更加复杂。对于房价预测来说,有很多因素需要考虑,如面积、位置以及附近的学校、加油站、医院、交通状况等。可以先将这个函数简单化,代码如下。
def estimate_house_price(sqft, location):
price = < DO MAGIC HERE >
return price
如何标识一条直线(而不用编写额外的条件检查)呢?一般来说,线性回归线用如下公式表达。
Y = XW +b
在这个例子中,为了便于理解,首先,将上面的公式转换为以下形式。
prediction = X • Weight + bias
其中,prediction表示预测值,Weight表示直线的斜率,bias表示截距(也就是当X=0的时候,Y的值)。
然后,构建线性模型,这时需要确定梯度下降值。根据cost函数确定均方差(mean squared error),以获得梯度下降值。
用伪代码表示cost函数,以解决房价预测的问题。
def estimate_house_price(sqft, location):
price = 0
#and this
price += sqft * 235.43
#maybe this too
price += location * 643.34
#adding a little bit of salt for a perfect result
price += 191.23
return price
值235.43
、643.34
以及191.23
看上去像是随机值,但是这些值可以用来预测新的房屋价格。是如何获得这些值的呢?我们应该使用迭代方法来获得正确的值,以减小在正确方向上的误差。
def estimate_house_price(sqft, location):
price = 0
#and this
price += sqft * 1.0
#maybe this too
price += location * 1.0
#adding a little bit of salt for a perfect result
price += 1.0
return price
因此,先从1.0开始迭代,然后在正确的方向上最小化误差。使用TensorFlow实现下面的代码。后面将会详细解释这些代码。
#import all the necessary libraries
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy
#Random number generator
randnumgen = numpy.random
#The values that we have plotted on the graph
values_X =
numpy.asarray([1,2,3,4,5.5,6.75,7.2,8,3.5,4.65,5,1.5,4.32,1.65,6.08])
values_Y =
numpy.asarray([50,60,65,78,89,104,111,122,71,85,79,56,81.8,55.5,98.3])
# Parameters
learning_rate = 0.01
training_steps = 1000
iterations = values_X.shape[0]
# tf float points - graph inputs
X = tf.placeholder("float")
Y = tf.placeholder("float")
# Set the weight and bias
W = tf.Variable(randnumgen.randn(), name="weight")
b = tf.Variable(randnumgen.randn(), name="bias")
# Linear model construction
# y = xw + b
prediction = tf.add(tf.multiply(X, W), b)
#The cost method helps to minimize error for gradient descent.
#This is called mean squared error.
cost = tf.reduce_sum(tf.pow(prediction-Y, 2))/(2*iterations)
# In TensorFlow, minimize() method knows how to optimize the values for #
weight & bias.
optimizer =
tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
#assigning default values
init = tf.global_variables_initializer()
#We can start the training now
with tf.Session() as sess:
# Run the initializer. We will see more in detail with later
#chapters
sess.run(init)
# Fit all training data
for step in range(training_steps):
for (x, y) in zip(values_X, values_Y):
sess.run(optimizer, feed_dict={X: x, Y: y})
c = sess.run(cost, feed_dict={X: values_X, Y:values_Y})
print("Step:", '%04d' % (step+1), "cost=", "
{:.4f}".format(c), \
"W=", sess.run(W), "b=", sess.run(b))
print("Successfully completed!")
# with this we can identify the values of Weight & bias
training_cost = sess.run(cost, feed_dict={X: values_X, Y:
values_Y})
print("Training cost=", training_cost, "Weight=", sess.run(W),
"bias=", sess.run(b))
# Lets plot all the values on the graph
plt.plot(values_X, values_Y, 'ro', label='house price points')
plt.plot(values_X, sess.run(W) * values_X + sess.run(b),
label='Line Fitting')
plt.legend()
plt.show()
可以在GitHub仓库中找到本章的代码。
尝试亲自使用ML模型数据集并训练模型将有助于阅读本书。这对于迅速深入理解后面章节也很有帮助。这里并不讨论基本的ML算法。相反,我们更注重实践方法。可以从GitHub仓库下载完整的代码库。
本书将会介绍两个框架——TensorFlow Lite和Core ML。这两个框架与Android和iOS紧密结合。我们将会使用TensorFlow Lite查看ML在移动设备上的基本应用。假设读者已了解TensorFlow的基础知识和基本的ML算法,因为本书并不会介绍这些内容。
就像前面说过的那样,现在基本上每个人都会随时携带一部智能手机。我们从设备的传感器中能获得大量数据。除此之外,我们还会从边缘设备中获取数据。在撰写本书的时候,这个分类下已经有将近2300万种设备,包括智能音箱、智能手表以及智能传感器。之前只能应用于昂贵设备上的高端技术现在也可以应用在廉价设备上了。设备的指数级增长为在这些设备上使用ML做好了准备。
虽然在这些设备上运行ML有很多原因,但是最主要的原因是时延性。如果你正在处理视频或者音频,你不希望一直与服务器来来回回地传递数据。另外一个优点是可以在设备离线的时候进行操作。更重要的是,数据一直都在设备上,并且是用户的本地数据。从电池/能量的消耗来说,这也是非常节能的。
虽然这种方法的优点很多,但是也有几个缺点。大部分由电池提供能量的设备的存储空间有限,计算能力不足,还有严格的内存限制。TensorFlow框架并不会解决这些问题,这也是为什么TensorFlow转换为了一种能在这些限制下高效工作的框架。TensorFlow Lite是一个轻量级的并且节省内存和节能的框架,可以运行在造型小巧的嵌入式设备中。
TensorFlow Lite框架由5个高级的组件构成。这些组件都针对移动平台进行过优化,整个架构如下图所示。
下面是TensorFlow Lite架构核心单元的介绍。
.tflite
),并将经过训练的模型保存在硬盘上。还可以在移动或者嵌入式应用程序中使用提前训练过的模型。.tflite
模型并调用解释器。这些API适用于所有平台。Java API是在C++ API上进行了一层封装,只能应用在Android系统上。可以使用C++ API实现自定义内核,这个自定义内核也可以由解释器使用。
TensorFlow Lite当前支持Android/iOS平台,也支持Linux(如Raspberry Pi)平台(见下图)。在嵌入式设备(如Raspberry Pi)中,Python API将会很有帮助。TensorFlow Lite平台同样支持Core ML模型以及iOS平台。
在iOS平台上,可以直接将预训练的TensorFlow 模型在格式上转换为Core ML模型,这样应用程序就可以直接运行在Core ML运行时中。
当只有单一模型的时候,通过格式转换可以让它既运行在Android平台上,也运行在iOS平台上(见下图)。
TensorFlow模型使用了FlatBuffers。FlatBuffers是一个跨平台、开源的序列化库。使用FlatBuffers的主要优点是在打包/解包的过程中不需要辅助表征(secondary representation)。FlatBuffers通常与逐对象的内存分配(per-object memory allocation)搭配使用。相对于Protocol Buffers而言,FlatBuffers更节约内存,因为它有助于保持较小的内存占用量。
FlatBuffers最开始是针对游戏平台开发的。出于性能原因,它也应用在其他领域。在转换过程中,TensorFlow Lite提前处理了激活和偏差的问题,让TensorFlow Lite执行得更快。解释器使用静态内存和执行计划,这可以加快载入速度。优化过的内核可以在NEON和ARM平台上运行得更快。
TensorFlow充分利用了这些设备上硅片级的创新。TensorFlow Lite支持Android NNAPI。在编写本书的时候,不少Oracle企业管理器(Oracle Enterprise Manager,OEM)已经开始使用NNAPI了。TensorFlow Lite直接使用了图形加速,也就是说,在Android上使用开放图形库(Open Graphics Library,OpenGL),在iOS上使用Metal。
为了优化性能,分层方式有一些更改。采用一种存储数字和对数字执行运算的技术。TensorFlow Lite提供的帮助有两方面。首先,只要模型越小,TensorFlow Lite就越适合小型设备。其次,很多处理器使用专门的synthe指令集,该指令集处理定点数的速度远远大于处理浮点数的速度。所以,一种非常原始的分层方式就是在训练之后简单地缩小权重和激活数量。不过这样会导致次优的准确率。
TensorFlow Lite的性能是MobileNet和Inception V3上TensorFlow性能的3倍。虽然TensorFlow Lite仅支持推断,但是它很快就会拥有一个训练模块。TensorFlow Lite支持将近50个常见的操作。
它支持MobileNet、Inception V3、RedNet50、SqueezeNet、DenseNet、Inception V4、SmartReply以及其他网络(见下图)。
图中纵轴的单位是毫秒。
借助TensorFlow Lite,可以使用一个已有模块迅速开始构建第一个基于TensorFlow Lite的应用程序(见下图)。
在实际情况下,使用TensorFlow Lite包含4步。
(1)要么使用一个已经存在的模型,要么使用自己的模型并训练它。
(2)一旦准备好模型,就需要使用转换器将其转换为.tflite
格式。
(3)在这个模型之上编写各种类型的优化操作。
(4)开始实现hello world项目。
从这里开始,直接进入代码部分。
只需要调用一行转换函数,就可以将ML模型转换为TensorFlow Lite模型。下面是一段简单的Python代码,它会将已经存在的模型转换为TensorFlow Lite格式。可以输入已经存在的模型,并将它转换为.tflite
格式。
import sys
from tf.contrib.lite import convert_savedmodel
convert_savedmodel.convert(
saved_model_directory="/tmp/your_model",
output_tflite_file="/tmp/my_model.tflite")
这段代码将从其他框架创建的模型转换为使用FlatBuffers的TensorFlow Lite格式。下面是一些转换策略。
我们实现了下列策略。
后面的章节将会结合实际应用程序探讨这些策略的细节。
我们可以从TensorFlow GitHub仓库的示例应用程序开始学习。示例是一个相机应用程序,它使用浮点数的Inception V3模型或者量化的MobileNet模型持续对图片进行分类。尝试使用Android 5.0版本或者更早的版本。
示例应用程序参见GitHub网站。
这个应用程序用于实时地按帧分类。它显示了最可能的分类,同时还会显示检测每张图片所需的时间。
通过3种方法可以让这个示例应用程序运行在设备上。
这是尝试运行示例应用程序最简单的方法。
安装了示例应用程序之后,打开它。首次打开这款应用程序的时候,它会提示你使用运行时权限访问设备相机。一旦具有了访问权限,你就可以使用这款应用程序实时识别相机视图中的对象了。在结果中,可以看到已识别对象的前3种分类,以及分类花费的时间。
按照下面的步骤可以直接通过Android Studio下载并构建TensorFlow Lite。
(1)下载并安装最新版本的Android Studio。
(2)在Android Studio的设置中,确保NDK的版本大于14,SDK的版本大于26。本书中的应用程序使用的SDK的版本是27。后面将会详细介绍如何配置。
(3)从GitHub网站下载tflitecamerademo应用程序。
(4)按照Android Studio的提示,下载所有Gradle的相关依赖项。
为了在应用程序中使用模型,需要提供一个模型。要么使用已经存在的模型,要么训练自己的模型。在这个应用程序中,使用一个已经存在的模型。
可以从GitHub网站下载已经存在的模型,也可以从GitHub网站下载压缩过的模型文件。
可以下载Inception V3浮点模型或者最新的MobileNet模型。首先,将合适的.tflite
文件复制到Android应用程序的assets
文件夹中。然后,更改Camera2BasicFragment.java
文件,该文件位于tensorflow/contrib/lite/java/demo/app/src/main/assets/中。
可以从GitHub网站下载已经存在的models.md模型。
现在,可以开始构建并运行示例应用程序。
复制TensorFlow的代码仓库。需要使用Bazel来构建APK。
git clone https://github.com/tensorflow/tensorflow
如果系统中还没有安装Bazel,那么首先要安装它。本书的编写环境基于macOS High Sierra 10.13.2。可以使用Homebrew来安装Bazel。
请按照下面的步骤安装Homebrew。
(1)因为Homebrew依赖JDK,所以首先要安装JDK。从Oracle官方网站下载并安装最新的JDK。
(2)安装Homebrew。
可以直接在Terminal中运行如下脚本。
/usr/bin/ruby -e "$(curl -fsSL \
https://github.com/Homebrew/)"
一旦Homebrew安装完毕,可以使用以下命令安装Bazel。
brew install bazel
现在,所需的软件已经安装好,可以使用下面的命令确认Bazel的版本。
bazel version
如果已经安装了Bazel,那么可以使用下面的命令升级Bazel的版本。
brew upgrade bazel
Bazel当前在Windows系统上不支持Android版本。Windows用户应该下载已经构建好的二进制文件。
构建TensorFlow Lite代码需要Android NDK。Android NDK Archives 可以从Android Developers网站下载。
SDK工具中有Android Studio,需要使用V23或者更高版本的构建工具。(运行应用程序的设备需要API的版本至少为21。)
可以在根目录的WORKSPACE
文件中更新API等级以及SDK和NDK的路径。
在根节点中更新api_level
和SDK以及NDK的位置。如果在Android Studio中打开SDK Manager,就会看到SDK的路径。比如,下面的SDK配置方式。
android_sdk_repository (
name = "androidsdk",
api_level = 27,
build_tools_version = "27.0.3",
path = "/Users/coco/Library/Android/sdk",
)
Android NDK的配置方式如下。
android_ndk_repository(
name = "androidndk",
path = "/home/coco/android-ndk-r14b/",
api_level = 19,
)
在编写本书的时候,NDK Archives使用的是android-ndk-r14b-darwin-x86_64.zip
。可以根据实际需要调整前面的参数。
现在,可以开始构建源代码了。为了构建示例应用程序,运行Bazel。
bazel build --cxxopt=--std=c++11
//tensorflow/contrib/lite/java/demo/app/src/main:TfLiteCameraDemo
由于一个Bug,Bazel现在仅支持Python 2的环境。
MobileNet是新手学习ML的一个很好的起点。这个数据库中的模型图片的大小是299×299像素。不过,相机捕捉的画面是224×224像素,需要重新调整大小以适配模型。在硬盘上每张图片会占224×224×3字节,之后这些字节会逐行转换为ByteBuffer。在这里,数字3表示每个像素的RGB值。
示例应用程序使用了TensorFlow Lite的Java API,它首先以一张图片作为输入,然后生成这张图片的输出内容。输出内容包含一个二维数组。数组的第一维包含分类索引值,第二维包含分类的置信度。根据这些值,示例应用程序在前端向用户显示排名前三的分类。
现在,我们将在iOS环境下构建相同的应用程序。这个应用程序包含与Android版本相同的功能,我们还使用相同的MobileNet分类模型。这次将会在真正的iOS设备上运行应用程序,使用设备上的拍摄功能。这个应用程序在模拟器上无法正常运行。
为了正常使用XCode,我们需要在官方网站上注册一个Apple开发者ID。这个应用程序还需要一部iPhone,因为它需要使用相机的功能。另外,还需要在指定的机器上安装配置文件。只有这样,才能在设备上构建并运行设备。
我们需要复制完整的TensorFlow仓库,但是运行这个应用程序并不需要完整的源代码。如果已经下载了代码,那么不需要重复下载。
git clone https://github.com/tensorflow/tensorflow
使用命令行工具安装XCode,代码如下。
xcode-select --install
如果你不熟悉iOS应用程序的构建方法,请参考相关教程。为了安装依赖项,首先需要安装cocoapods
。
sudo gem install cocoapods
下面是一个脚本文件,它的作用是下载运行应用程序所需的模块文件。
sh tensorflow/contrib/lite/examples/ios/download_models.sh
现在可以进入项目目录,并在命令行中安装pod。
cd tensorflow/contrib/lite/examples/ios/camera
pod install
pod update
一旦更新完成,你就会看到tflite_camera_example.xcworkspace
。然后,就可以在XCode中打开应用程序。当然,也可以使用下面的命令行完成这个操作。
open tflite_camera_example.xcworkspace
现在,可以开始在iPhone上构建并运行应用程序了。
你需要允许应用程序获得使用相机的权限。使用相机对准某个对象拍照,就会看到分类结果了。
Core ML可以帮助我们构建iOS上的ML应用程序。
Core ML使用经过训练的模型,这些模型根据新的输入数据做出预测。比如,如果基于一个地区的历史地价训练过一个模型,那么这个模型可以在已知地点和大小的情况下预测指定的地价。
Core ML是其他特定领域框架的基础。Core ML支持的主要框架包括GamePlayKit(其主要功能就是评估决策树),用于文本分析的自然语言处理(Natural Language Processing,NLP),以及基于图片分析的各种框架。
Core ML构建在加速模块、基本神经网络子例程(Basic Neural Network Subroutine,BNNS)以及Metal 性能着色器(performance shader)上,正如Core ML文档中架构图显示的那样。
Core ML应用程序构建在前面提到的3个组件之上,如下图所示。
Core ML为设备性能进行了优化,占用的内存最少,消耗的功率最小。
要在iOS上运行第一个应用程序,不需要构建自己的模型。可以使用任何一个已经存在的优秀的模型。如果已经拥有一个由第三方框架创建的模型,那么可以使用Core ML Tools Python包,或者第三方的包,比如,MXNet转换器或者TensorFlow转换器。下载这3个工具的网站如下所示。如果你的模型不支持以上3个转换器中的任何一个,也可以自己写一个转换器。
Core ML Tools Python包的下载地址参见PyPI网站。
TensorFlow转换器的下载地址参见GitHub网站。
MXNet转换器的下载地址参见 GitHub网站。
Core ML Tools Python包支持从Caffe v1、Keras 1.2.2+、scikit-learn 0.18、XGBoost 0.6以及LIBSVM 3.22进行的转换。它涵盖了SVM模型、树集成、神经网络、广义线性模型、特征工程和管道模型。
可以使用pip
命令安装Core ML工具。
pip install -U coremltools
可以使用coretools Python包将已经存在的模型转换为Core ML模型。如果要将一个简单的Caffe模型转换为Core ML模型,可以按照下面的代码进行操作。
import coremltools
my_coremlmodel =
coremltools.converters.caffe.convert('faces.caffemodel')
coremltools.utils.save_spec(my_coremlmodel, 'faces.mlmodel')
模型不同,转换步骤也有差异。可能会添加标签或者输入名称以及模型的结构。
将Core ML集成到iOS应用程序中非常简单。在Apple开发者页面下载一个已经训练好的模型,例如,下载MobileNet模型。
下载了MobileNet.mlmodel
之后,将它添加到项目的Resources
组中。视觉框架(vision framework)帮助我们将已经存在的图片格式转换为可用于输入的类型。我们可以在下图中看到模型的细节。在接下来的章节里,我们将在已存在模型的基础上创建自己的模型。
下图展示了如何在应用程序中载入模型。
在最近创建的XCode项目中打开ViewController.swift
,并导入Vision和Core ML框架。
/**
Lets see the UIImage given to vision framework for the prediction.
The results could be slightly different based on the UIImage conversion.
**/
func visionPrediction(image: UIImage) {
guard let visionModel = try? VNCoreMLModel(for: model.model) else{
fatalError("World is gonna crash!")
}
let request = VNCoreMLRequest(model: visionModel) { request, error
in
if let predictions = request.results as? [VNClassificationObservation]
{
//top predictions sorted based on confidence
//results come in string, double tuple
let topPredictions = observations.prefix(through: 5)
.map { ($0.identifier, Double($0.confidence)) }
self.show(results: topPredictions)
}
}
}
通过Core ML MobileNet模型载入相同的图片以完成预测。
/**
Method that predicts objects from image using CoreML. The only downside of
this method is, the mlmodel expects images in 224 * 224 pixels resolutions.
So we need to manually convert UIImage
into pixelBuffer.
**/
func coremlPrediction(image: UIImage) {
if let makeBuffer = image.pixelBuffer(width: 224, height: 224),
let prediction = try? model.prediction(data: makeBuffer) {
let topPredictions = top(5, prediction.prob)
show(results: topPredictions)
}
}
现在我们已经基本熟悉了TensorFlow Lite和Core ML的基础知识。本章涉及的所有代码都能在GitHub代码仓库中找到。因为这两个库都是针对移动设备研发的,所以它们都存在一定的限制。这将在后面几章的实时应用程序中深入探讨。
后面的章节将讲述如何基于特定的应用场景开发并训练特定的模型,也会介绍如何在这个基础上构建自己的移动应用程序。做好训练模型并将它应用到自己的移动应用程序上的准备吧!
[1] 1ft2 = 0.092 903m2。——编者注
在本章中,我们将构建一款iOS应用程序,检测拍摄的照片或者用户照片库中人的性别、年龄以及表情。我们将使用一个已经存在的数据模型,它基于Caffe机器学习(ML)库构建,目的就是实现上述功能。我们将会把这个模型转换为Core ML模型以方便使用。本章还将通过示例应用程序从年龄、性别和表情预测方面讨论卷积神经网络(Convolutional Neural Network,CNN)的工作原理。
这款应用程序在多种使用场景下都很实用。其中一些应用场景如下。
还有很多种应用场景。一旦提高了数据模型的准确率,我们就能找到更多的使用场景。
本章还会介绍下面几个话题:
本章将介绍一款使用Core ML模型预测照片或者照片里人物的年龄和性别的完整iOS应用程序,
Core ML不仅能让开发者在设备上安装并运行预训练模型,还有别具一格的优点。因为Core ML安装在本地设备中,所以它并不需要调用云服务来获得预测结果。它不但缩短了通信延迟,而且节约了数据带宽。Core ML的另外一个重要优点是隐私性。我们不需要将数据发给第三方服务来获得预测结果。使用离线模型的主要缺点是模型没有办法更新,所以它也没有办法根据新的输入来提升预测效果。进一步讲,一些模型可能会增加内存的占用量,因为移动设备的存储空间是有限的。
在使用Core ML的时候,当导入ML模型后,XCode将会帮助你完成剩下的工作。在这个项目中我们将基于Gil Levi和TalHassncer的几篇论文来构建iOS应用程序。论文分别是“Age and Gender Classification Using Convolutional Neural Networks ”(参见IEEE网站),IEEE研讨会上的“Analysis and Modeling of Faces and Gestures(AMFG)”,2015年波士顿的IEEE会议上的“Computer Vision and Pattern Recognition”(CVPR)。
本书中这个项目的开发环境是MacBook Pro计算机,在macOS High Sierra版本的操作系统中,XCode的版本是9.3。在社交媒体平台的应用程序上,年龄和性别预测是一个常见的功能。有很多算法可以对年龄和性别进行预测与分类,这些算法还在不断进行性能优化。在本章中,我们将使用深度CNN完成分类操作。
可以在GitHub网站上找到本章开发的应用程序。在本章中我们将在应用程序里使用Adience数据库。该数据库可以在GitHub网站中找到。
根据给定的照片预测年龄,有多种方法。早期主要使用计算面部属性测量值之间比率的方法,这些属性包含眼睛、鼻子、嘴巴等。根据面部器官的大小和距离计算出相应的属性之后,就会计算出对应的比率,年龄的分类将会使用基于规则的引擎。现在,面临一个问题:当无法获得人脸正面照的时候,这个方法就不可行,但是我们在社交平台上看到的大头照很多都不是正面照。
通过很多种方法可以预测面部特征并对面部特征进行分类。其中一个方法是高斯混合模型(Gaussian Mixture Model,GMM),它主要用于表示面部块(facial patch)的分布。其他方法是超向量以及隐马尔可夫模型(Hidden Markov Model,HMM),它表示面部块的分布。性能最好的是局部二元模式(Local Binary Pattern,LBP),以及支持向量机(Support Vector Machine,SVM)分类器。
早期的性别预测使用的是神经网络。图像增强(image intensity)和面部3D结构可用于预测性别。SVM分类器可用于图像增强。
由于本书后面所有iOS应用程序的制作都要经过一个通用的步骤,因此先介绍配置文件的签名和配置。其中一个非常流行的基准(benchmark)是FERET 基准,它使用强度、形状和特征给出近乎完美的性能解决方案。本应用程序的数据集使用了一个复杂的图形集合,这些图片的拍摄角度不同而且曝光时间的长短也不一样。另外一个很流行的基准称作户外脸部检查数据库(Labeled Faces in the Wild,LFW),它使用带AdaBoost分类器的局部二元模式(Local Binary Pattern,LBP)。
神经网络最早的应用之一是光学字符识别(Optical Character Recognition,OCR),但是当训练大型网络的时候,神经网络会面临时间、计算资源等问题。
CNN是一个前馈神经网络(Feedforward Neural Network),它受生物过程的影响。CNN与大脑神经元的工作方式类似,有像神经元一样相互连接的组织方式。这些神经元会对刺激做出反应,这些反应仅作用于视野中的特定区域,这些区域称为感受野(receptive field)。当多个神经元相互重叠的时候,它们就会覆盖整个视野。下图展示了CNN的架构。
CNN有一个输入层、一个输出层以及多个隐藏层。这些隐藏层包含池化层(pooling layer)、卷积层(convolutional layer)、归一化层(normalization layer)以及全连接层(fully connected layer)。卷积层将会对输入执行卷积操作,然后将结果传到下一层。这个过程模拟了神经元对外界刺激的反应。每个神经元都有一个对应的感受域。深度CNN已经应用在各种各样的应用程序之中,比如,面部特征检测、行为分类、语音识别等。
识别给定的图片是否包含数字0的一个简单方法是首先将包含所有数字的图片按序排好,然后逐个与给定的图片进行对比,从而确认图片是否包含数字0。这将是一个棘手并冗繁的过程,因为计算机主要执行数学计算。除非图库中有一张图片与给定图片的相似度十分高,否则我们将无法找到匹配的图片。从计算机的角度讲,可以将一张图片看作一个记录了每个位置像素值的二维数组。下图展示了包含数字0的例子。
上图中,左边的图片是图库中的一张图片,右边的图片是输入的图片,右图有一些变形,像手写的数字0。为了确认两张图片是否一致,计算机将会尝试匹配所有的像素值。然而,只要有一个点在像素级别不匹配,就无法识别出数字0。这里,我们就需要CNN的帮助了。
下图是一张包含大写字母X的图片。当在系统中输入一张新图片的时候,CNN并不知道它是否满足特征值。所以,它将会尝试在整张图片上匹配特征模型。下面展示如何构建一个过滤器。
这里应用的数学逻辑称为卷积(convolution)。为了计算图片的某个部分与特征的匹配度,将特征对应的像素值与图片中对应的像素相乘。为了得到一个最终值,首先将所有值加在一起,然后除以像素的总个数。
如果两个像素的颜色相同(用1来表示),那么1×1=1(见下图);如果两个像素的颜色不相同,那么(−1)×(−1)=1。在最终的结果里,每个匹配的像素对应的最终值都是1,每个不匹配的像素对应的最终值都是−1。
为了完成卷积的计算过程,应将特征网格移动到图像块上。如下图所示,将3×3的特征网格移动到7×7像素的图像上。这样就得到了一个5×5的数组。在最终结果的网格中,值越接近1表示越接近特征值,值越接近0表示与特征值越不匹配,值越接近−1表示越背离最终特征值。
接下来,针对其他特征值,重复执行卷积的计算过程。这就会得到过滤后的图片——每个特征值都有一个对应的过滤器。在CNN中,它称为卷积层(convolution layer),然后会把一些附加层添加在它上面。
这也就是CNN计算量大的原因。上面的例子显示了一张7×7像素的图片经过卷积运算后得到了一个5×5的数组。然而,一张正常的图片至少有128×128像素。计算量将会根据特征数量以及每个特征的像素数量的增长而线性增长。
另外一个能够提升处理效率的过程称作池化(pooling)。在池化层中,将会压缩大图,但是会保证主要特征信息一致。这使用一个窗口在图片上滑动,并找出每个窗口中的最大值。在典型的池化层中,在一侧通常使用2或者3像素的窗口(见下图),不过使用2像素的步长也可以。
经过池化过程之后,图片的大小将会缩小1/4。每个值保存的是每个窗口中的最大值。最大值也保存了每个窗口中最强的特征。池化过程表明它并不关心特征是否完全匹配,只要能满足一部分特征即可。借助池化的帮助,CNN可以识别一张图片中的特征,而不用担心这个特征在图片的哪个部分。使用这种方法,计算机也不用担心文字书写是否规范。
在池化层的最后,将一张1000万像素的图片压缩为200万像素,会显著帮助我们提升后面几个过程的处理速度。
修正线性单元(Rectified Linear Unit,ReLU)层背后的逻辑非常简单,它会将所有的负值都替换为0(见下图)。因为避免了负值的出现,所以让CNN中的数学计算更简单。
在这个过程中,图的大小没有发生改变。我们将会得到相同大小的输出,只不过负值都会被替换为0。
在生物大脑的功能中,这个概念称为侧抑制(lateral inhibition)。侧抑制指的是受到刺激的神经元抑制周围神经元的能力。我们的主要任务是找到一个局部峰值,由此找到临近的最大值。
当处理ReLU神经元的时候,局部响应归一化(Local Response Normalization,LRN)层非常有用。ReLU神经元可以被无限激活,需要使用LRN层来将它们归一化。为了达到这个目的,我们需要识别高频特征。通过应用LRN层,受刺激的神经元比周围的神经元更加敏感。LRN层通常用在ImageNet ConvNet过程中,前面的文献中已提到过。
不过,在最近要构建的实时应用程序中,LRN层的贡献非常小,所以这里不太强调这个层。
从语义上说,dropout层用于随机丢弃一些数据单元。这就意味着在向前通道中消除了下游神经元的影响,并且在向后通道中没有权重值。如果在训练过程中遗失了一些神经元,那么其他神经元会尝试预测遗失的神经元的权重值。使用这种方法,神经元会减小对特定权重的神经元的影响。我们这样做是为了避免过拟合(overfitting)。
在全连接层中,使用高等级的层作为输入,而输出结果则由投票决定。比如,我们想要确定输入的图片是否包含字母a或者b。在这一步中,输入内容以一个列表呈现,而不是一个二维数组。下图展示了全连接层的示例。
列表中的每个值都对应一个投票值,以决定输入值是否包含字母a或者b。一些值会帮助我们确认给定转入是否包含字母a,还有一些值会帮助我们识别它是否包含字母b。这些特定的值会得到比其他值更高的票数。投票值表示为值与每个输入分类之间的权重。CNN会深入到输入图像的底层,直到它找到全连接层。最终结果就是票数最多的值,该值将会作为输入的分类。
现在回到我们的应用场景。
当尝试准备年龄和性别预测的数据库时,我们可能会遇到一些问题。使用海量的社交媒体图片创建一个数据库可能需要很多私人的数据,这样做不符合我们的要求。我们可以使用大多数已经存在的模型,当然,它们有自己的局限性。类似地,拟合也需要慎重,因为这是CNN中的一个常见问题。
这里的应用程序架构包含3个卷积层、两个全连接层以及少量的神经元。
下图是CNN的流程以及整个流程中的所有组件。
颜色通道(红、绿、蓝)会由网络单独和直接处理。每当获得一张输入图片时,图片的尺寸将会缩放到256×256像素。然后,把经过剪裁的227×227像素的图片提供给网络。
本章开发的应用程序基于ImageNet分类器,它使用了120万张图片。论文“Alexnet-2012 Imagenet Classification with Deep Convolutional Neural Networks”可以在NIPS网站中找到。
接下来,按照下面的步骤定义3个子卷积层。
下面是构建这个模型使用的数据集的详情。
初始化数据集
所有层的权重都使用了随机值,并将标准偏差维持在0.01。使用之前提到的训练数据集来训练网络。训练的最终结果(用二进制数组的形式来表示)对应于真正的分类。这个结果带有与年龄组分类相适应的标签,以及与之关联的正确性别分类。
现在,回到与应用程序代码相关的部分。这里的模型使用了Caffe 深度学习框架,它由伯克利人工智能研究(Berkeley AI Research,BAIR)团队和社区共同开发。首先,要将已经存在的Caffe模型转换为应用程序中可以使用的Core ML模型。
//Downloading Age and Gender models
wget
http://www.openu.ac.il/home/hassner/projects/cnn_agegender/cnn_age_gen
der_models_and_data.0.0.2.zip
unzip -a cnn_age_gender_models_and_data.0.0.2.zip
然后,进入解压后的文件夹,将模型转换为Core ML模型。
import coremltools
folder = 'cnn_age_gender_models_and_data.0.0.2'
coreml_model = coremltools.converters.caffe.convert(
(folder + '/age_net.caffemodel', folder + '/deploy_age.prototxt'),
image_input_names = 'data',
class_labels = 'ages.txt'
)
coreml_model.save('Age.mlmodel')
对于性别模型也要进行类似的操作。为了开展工作,创建第一个Core ML应用程序。
首先,在初始化界面选择Single View App,如下图所示。
在下一页的向导界面中,为应用程序取一个合适的名称。填写剩余的字段,包括组织名称以及标识符。因为在这个应用程序中将会使用核心数据,所以需要勾选Use Core Data复选框。然后,在XCode中创建一个新的应用程序。下图展示了如何在XCode中创建新项目。
当选择了应用程序将要保存的位置之后,就可以看到刚创建的应用程序的基本信息,如下图所示。
之后,创建一个控制器,使用这个控制器可以在移动设备的相册中或者相机中选择合适的图片。
下面的代码块为图像选择器创建了控制器。
import UIKit
open class ImageClassificationController<Service:
ClassificationServiceProtocol>: UIViewController,
PhotoSourceControllerDelegate, UINavigationControllerDelegate,
UIImagePickerControllerDelegate {
/// View with image, button and labels
public private(set) lazy var mainView =
ImageClassificationView(frame: .zero)
/// Service used to perform gender, age and emotion classification
public let classificationService: Service = .init()
/// Status bar style
open override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - View lifecycle
open override func viewDidLoad() {
super.viewDidLoad()
mainView.frame = view.bounds
mainView.button.setTitle("Select a photo", for: .normal)
mainView.button.addTarget(self, action:
#selector(handleSelectPhotoTap), for: .touchUpInside)
view.addSubview(mainView)
mainView.setupConstraints()
classificationService.setup()
}
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
mainView.frame = view.bounds
}
// MARK: - Actions
/// Present image picker
@objc private func handleSelectPhotoTap() {
let sourcePicker = PhotoSourceController()
sourcePicker.delegate = self
present(sourcePicker, animated: true)
}
// MARK: - PhotoSourceControllerDelegate
public func photoSourceController(_ controller: PhotoSourceController,
didSelectSourceType sourceType: UIImagePickerControllerSourceType) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.allowsEditing = true
imagePicker.sourceType = sourceType
present(imagePicker, animated: true)
}
// MARK: - UIImagePickerControllerDelegate
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
let editedImage = info[UIImagePickerControllerEditedImage] as? UIImage
guard let image = editedImage, let ciImage = CIImage(image: image) else {
print("Can't analyze selected photo")
return
}
DispatchQueue.main.async { [weak mainView] in
mainView?.imageView.image = image
mainView?.label.text = ""
}
picker.dismiss(animated: true)
// Run Core ML classifier
DispatchQueue.global(qos: .userInteractive).async { [weak self] in
self?.classificationService.classify(image: ciImage)
}
}
}
在控制器中,一旦选择了图片,就会将这张图片传给下一页。在下一页中,会将图片进行分类。图片选择器如下图所示。
现在,添加图片源选择器,这样用户就可以在相册或者相机中选择图片了。
import UIKit
/// Delegate protocol used for `PhotoSourceController`
public protocol PhotoSourceControllerDelegate: class {
/// Sent to the delegate when a photo source was selected
func photoSourceController(_ controller: PhotoSourceController,
didSelectSourceType sourceType: UIImagePickerControllerSourceType)
}
/// Controller used to present a picker where the user can select a
/// source for a photo
public final class PhotoSourceController: UIAlertController {
/// The controller's delegate
public weak var delegate: PhotoSourceControllerDelegate?
public override func viewDidLoad() {
super.viewDidLoad()
addAction(forSourceType: .camera, title: "Snap a photo")
addAction(forSourceType: .savedPhotosAlbum, title: "Photo Album")
addCancelAction()
}
}
// MARK: - Actions
private extension PhotoSourceController {
func addAction(forSourceType sourceType:
UIImagePickerControllerSourceType, title: String) {
let action = UIAlertAction(title: title, style: .default) { [weak
self] _ in
guard let `self` = self else {
return
}
self.delegate?.photoSourceController(self, didSelectSourceType:
sourceType)
}
addAction(action)
}
func addCancelAction() {
let action = UIAlertAction(title: "Cancel", style: .cancel, handler:
nil)
addAction(action)
}
}
当用户单击Select a photo时,就会弹出一个菜单,其中包括3个选项,分别是Snap a photo选项(表示用相机拍照),Photo Album选项(表示从用户的相册中选择图片),以及Cancel选项,如下图所示。
最后一个任务是为图像选择菜单中的选项添加对应的行为。一旦选中了图片,就会调用对应的方法来从模型中获取结果。
下面的代码块用于在按钮中添加对应的行为。
extension ViewController: ClassificationServiceDelegate {
func classificationService(_ service: ClassificationService,
didDetectGender gender: String) {
append(to: mainView.label, title: "Gender", text: gender)
}
func classificationService(_ service: ClassificationService, didDetectAge
age: String) {
append(to: mainView.label, title: "Age", text: age)
}
func classificationService(_ service: ClassificationService,
didDetectEmotion emotion: String) {
append(to: mainView.label, title: "Emotions", text: emotion)
}
/// Set results of the classification request
func append(to label: UILabel, title: String, text: String) {
DispatchQueue.main.async { [weak label] in
let attributedText = label?.attributedText ?? NSAttributedString(string:
"")
let string = NSMutableAttributedString(attributedString: attributedText)
string.append(.init(string: "\(title): ", attributes: [.font:
UIFont.boldSystemFont(ofSize: 25)]))
string.append(.init(string: text, attributes: [.font:
UIFont.systemFont(ofSize: 25)]))
string.append(.init(string: "\n\n"))
label?.attributedText = string
}
}
这里给出的方法首先将会使用分类服务获得图片中人物对应的性别、年龄和表情,然后将这些信息显示在UI上。最终结果可能不是100%准确的,因为使用的模型是运行在本机上的。下图显示了应用程序的完整功能,以及图片中的相关信息。
本章讨论了如何从头开始构建完整的iOS应用程序,还介绍了如何将Caffe模型转换为Core ML模型。现在我们知道了如何将Core ML模型导入iOS应用程序中,并使用这个模型获得对应的预测结果。使用这种方式,因为不用访问互联网所以节约了网络带宽,同时所有的数据依然在本地设备上(避免了出现隐私泄露的问题)。
在下一章中,根据已经学习的知识,我们将构建一个应用程序,它可以使用神经网络对已经存在的图片进行艺术化处理。