来源:机器鸡

作者:瑶瑶

本文长度为10000字,建议阅读20分钟+

本文手把手教你开拓自己的app~

手把手教你用TensorFlowKeras打造美剧硅谷中的识别热狗APP

HBO热播剧《硅谷》最近推出了一款能够识别“热狗”和“不是热狗”的人工智app(就像这部剧第四季第四集里的Jian Yang展示的一样),这款app目前已在安卓和iOS上架,但仅限美国、加拿大用户下载。

当你对着食品照片拍摄后(或者你可用手机里的照片),它会见告你这个物体是「Hotdog or Not Hotdog」,这便是它全部的浸染,全体运用功能非常大略。

为了准确地识别热狗,作者开拓了一个能够直接在手机上运行的神经网络构造,并用TensorFlow,Keras和Nvidia GPU来演习它。

虽然功能很可笑,但这款运用是一个可操作性强的、利用深度学习和边缘运算(edgecomputing)的例子。
所有的AI程序都百分百在用户的设备上运行,所拍摄的照片不用离开手机就能被处理。

这使得用户享有更好的体验(无需来回云端),离线利用功能,以及更好的隐私保护。
这也使我们在拥有百万用户的情形下依然零开销运行运用,和传统的那些基于云真个AI比较为我们省下很大一部分花费。

作者开拓运用的装备(图中eGPU用于演习“不是热狗”的AI)

这款运用是由一个开拓者用一台条记本电脑和一个GPU手动编辑数据开拓的。
这个例子见告我们,以现在的科技,不须要科技公司的支持,个人开拓者及爱好者也能利用有限的资源开拓有趣的运用。
废话不多说,下面我们就来手把手教你开拓自己的app。

目录

一、App

二、从原型到产品

1、原型

2、TensorFlow,Inception构造和迁移学习

3、Keras和SqueezeNet

三、DeepDog架构

1、演习

2、在手机上运行神经网络

3、通过神经网络来改变运用程序行为

4、用户体验,开拓者体验以及AI的胆怯谷

一、App

这款运用会先让你拍张照片,然后见告你你拍的是不是热狗。
这个功能刀切斧砍,算是对ImageNet这类近期的AI运用的致敬。
虽然我们比任何人在热狗上投资的工程资源都要多,但这款app也有犯傻的时候。

看来只要有番茄酱便是热狗

相反,有时候它也能在繁芜情形下展示它机警的一壁。
Engadget曾如此宣布:“太神奇了!
我用这款app在20分钟内的体验比我用Shazam(一款能猜歌名的运用)两年来的体验还要好。

看来骗不了‘不是热狗’啊

二、从原型到产品

不知道你有没有这样的经历:当你在读Hacker News(一家关于打算机黑客和创业公司的社会化新闻网站)的时候,你会想:“他们A轮融资1000万就弄出来个这?我一个周末就能做得出来好吗!

那么这款app会让你有同样的觉得,而且它的原型确实仅用一个周末,利用谷歌云端平台的Vision API和React Native打造出来的。

但上架运用市场的终极版是我们又花了几个月的韶光(兼职)打磨出来的。
我们做了一些生手们不能理解的优化。
我们花了数周最大化了运用的准确度,AI演习韶光,测试韶光,以及迭代我们的setup和tooling,这些事情使我们的迭代开拓更高效。

其余,我们还花了一全体周末优化在iOS和安卓上的用户体验(不说了,说多了都是泪啊

)。

平时大多数技能博文和学术文章都会跳过这个部分,直接展示他们终极的方案。
但为了能给大家一个前车之鉴,我们在这里把我们试过的不可行方案缩略一下放在这里。
在这之后我们会先容末了成功了的方案。

1、原型

Google Cloud Vision文档的示例图像和相应的API输出

我们选择用React Native来搭建原型是由于,一方面它是块很好的试验田,另一方面它能帮助我们很快地支持许多设备。
事实证明我们的选择是精确的。

在这个项目后续的事情中,我们依旧保留了React Native:虽然它并没有一贯简化我们的事情量,而且我们特意限定了这款运用的设计,但终极React Native还是胜任了这项事情。

我们在制作原型时所用的- 谷歌云真个Vision API很快就被我们弃用了。
缘故原由有以下三条:

它识别热狗的准确率很一样平常。
虽然它善于识别大量物件,但你单单让它识别一种东西就很困难了。
在我们2016年试用它的时候就有许多失落败的案例。

由于它是云端做事的缘故原由,它会比在设备上运行慢得多(网!
太!
慢!
),而且不支持离线功能。
而且一旦照片离开设备就会触发一些涉及隐私和法律条款的考虑。

末了,一旦运用上线,利用谷歌云端做事的用度就会很高昂。

考虑到这些成分,我们开始考试测验现在盛行的“边缘打算”,在这里意味着先在我们自己的条记本电脑上演习神经网络,然后再将其传输并直接嵌入我们的移动设备。
这样的话,神经网络实行推理,就能直接在用户手机上运行了。

2、TensorFlow,Inception构造和转移学习

通过一次有时的机会和TensorFlow团队的Pete Warden聊过后,我们意识到了TensorFlow能够直接嵌入一个iOS设备中运行的能力,以是我们开始往这个方向上探索。
在React Native之后,TensorFlow成了我们第二个敲定下来的开拓工具。

我们只花了一天韶光,就把TensorFlow的Objective C++相机示例整合到我们的React Native的库里,利用他们的迁移学习脚本(transfer learning script)花了多一点的韶光。

迁移学习脚本能够帮助你重新演习插Inception构造,使其能够处理更详细的图片问题。
Inception是谷歌用于办理图片识别问题时搭建的一组神经构造的名称。
有的Inception是演习完毕并且设置过权重的。
绝大多数情形下,图片识别网络都在ImageNet上演习过。

ImageNet每年都会举办比赛,旨在挖掘能够最好地识别超过两万种不同物品(包括热狗)的神经网络构造。
但是就像谷歌云真个Vision API,这个比赛虽然横向纵向都有筛选,但面对两万物品的识别,算法仍有所欠缺。
这种情形下,迁移学习便是要拿出一套经由完全演习的神经网络,把它重新演习成一个能够更好完成单项详细任务的工具。

这就牵扯到一定程度的“遗忘”,要么就直接剪切stack里的全体一层,要么就逐步擦掉神经网络等分辨其他物件(比如说椅子)的能力,转而专注于识别你须要的物件(在这个运用中便是热狗了)。

所说到的这个神经网络(Inception)是用了ImageNet里1400万张图演习出来的,我们只用了几千张热狗的图片,就大大提高了它对热狗的识别能力。

迁移学习的最大好处便是,你能比从零开始更快地得到更好的结果,并且还用不着利用很多数据。
一套完全的演习不仅须要用多个GPU和上百万图片,而且要耗费几个月的韶光。
迁移学习一样平常能在几小时之内用一台条记本电脑和两三千张图搞定。

我们碰着的一大难题是,确定哪些称得上是热狗而哪些不是。

定义“什么是热狗”竟出乎猜想的难(切成一段一段的喷鼻香肠算不算?如果算的话,哪些肠可以呢?),而且这还牵扯到不同文化和地域对热狗的理解。

同样的,app这种开放式的环境意味着我们须要处理险些无限多的用户输入。
虽说有一些打算机识别问题是相对有限的(比如用X光图来检讨螺栓有无质量毛病),我们所面临的将会是自拍,风景照,和大量的食品图片。

可以说,这种开拓办法是不错的,而且也确实带来了优化结果。
但是,我们由于以下两个缘故原由弃用了这种办法:

第一,我们的演习数据一定是严重失落衡的,由于“不是热狗”的例子要比“是热狗”的例子多得多。

这就意味着,如果你用3张热狗图和97张非热狗图来演习你的算法,就算它识别的结果显示有0%的热狗和100%的非热狗,那么默认来说它的准确率仍高达97%!
这就算利用TensorFlow进行迁移学习也不能直截了当地办理。
换句话说,这基本上就见告我们必须得用深度学习模型从头来掌握演习和导入权重了。

这个时候我们决定硬着头皮上,利用Keras(一款深度学习软件库,在TensorFlow的根本上供应更加都雅便捷的抽象abstractions)重新开始。

Keras里自带了一些很厉害的演习工具, 以及一个权重类别的选项,完美符合我们这种演习数据严重失落衡的情形。
我们利用这个机会试用了诸如VGG的其他时下流行的神经构造,但一个问题迟迟没有办理:这些构造都不能很好地与iPhone兼容。

它们占了太大的内存,导致其他app崩溃。
还有一点便是它们打算处理的韶光有时须要10秒以上,这从用户体验的角度来说是很不妙的。
我们试过很多办法试图减轻这个问题,但是终极这些神经构造对付手机来说还是太大了。

3、Keras和SqueezeNet

图:SqueezeNet SqueezeNet 与AlexNet(打算机视觉架构鼻祖)

给你一个的韶光观点吧,这大概是我们项目的中间点旁边。
到这时,用户界面已经完成90%了,基本不会做什么改动了。
但是事后看来,当时的神经网络最多只做了20%。

我们对难度有了一定的把握,也有了一个不错的数据组,但终极的神经构造代码还一行都没码呢。
当时我们的神经代码还不能稳定地在手机上运行,我们的准确率乃至都要在之后的几周里才会有显著的提升。

我们所面临的最直接的问题很大略,如果Inception和VGG都太大了,那么有没有一个更大略的,演习完毕的神经网络能够让我们用来迁移学习的呢?

我们探索了Xception,Enet和SqueezeNet。
我们很快就决定利用SqueezeNet。

SqueezeNet拥有明确的定位功能,这可以作为嵌入式深度学习的办理方案。
其余,在GitHub上有演习完毕的Keras模型可以调用(耶!
开源网站!

那么这可以造成多大的差别呢?一个像VGG这样的构造须要用到差不多一亿三千八百万个参数(仿照神经元和神经元间数值的必要数字)。
Inception已经有了很大提高了,只需用2300万个参数。
SqueezeNet比较下来只须要125万个参数。

这会带来两个好处:

在演习过程中,利用一个小型网络要快得多。
内存中没有这么多参数须要map,这就意味着你可以进行同时演习(增大批量),而且神经网络会搜集(预估出数学公式)得更快些。

在开拓过程中,这个模型更小更快。
SqueezeNet只须要10MB以内的RAM,而像Inception这样的会须要100MB以上的RAM。
这个差距很大,而且还特別主要,由于有些移动设备没有100MB的RAM。
小型的神经网络运算起来也频年夜的更快捷。

当然,既然有得就有失落:

小型神经系统的“影象力”就弗成了:它处理不了繁芜的任务(比如识别两万个不同的物件),乃至连繁芜从属项(比如分辨纽约式热狗和芝加哥式热狗)都傻傻分不清。

就此推论,小型的神经网络总体来说在准确率上会逊于大型神经网络。
当试图识别ImageNet的2万种不同物件时,SqueezeNet的准确率只有58%,而VGG的是72%。

在小型神经网络上利用迁移学习也更难一些。
理论上来说,我们是可以用对付Inception和VGG一样的办法,来处理SqueezeNet的:先让它遗忘一些信息,然后重新特殊演习它识别热狗和非热狗的能力。

但在实际运用过程中,我们创造这种办法使得我们很难调度学习进度,而且得到的结果总是没有从零开始演习SqueezeNet来的好。
这个问题也有可能来源于我们项目的开放性(用户自己拍照上传图片)。

一样平常来说,小型神经网络很少会涌现过拟合(overfit)的情形,但我们在利用几个“小”构造的时候碰着过这样的问题。
过拟合意味着你的神经网络太专一了,只能识别你演习它的热狗照片而不能举一反三交融贯通。

用人类的例子来类比就彷佛一个人仅仅记住了你给他看得那张热狗照片,而不会把观点抽象化,无法意识到热狗便是一根喷鼻香肠夹在面包里,有时会佐以调味料等等。
如果你给他看一张全新的热狗照片(和原来那张不同),他会说这不是热狗。

由于一个小型神经网络常日“影象力”更差一些,那么我们不难明得,为什么对付它们来说专攻一件物品会更难一些。
但是有好几次,我们的小型神经网络的准确率一下跳到99%,然后溘然就不能识别之前演习中没有涌现过的图片了。

这种问题在我们加入足够的数据集后就消逝了。
数据集在这里意味着我们会对导入的图片做适当的随机改动(拉伸或扭曲),以是相较于一千张图里的每一张都演习一百次,我们用对这一千张图进行故意义的改动,使得神经网络不会仅仅记住图片,而是记住热狗的组成(面包,喷鼻香肠,调味料等等),与此同时又不失落灵巧变通的能力(而不是记住某一张图里的分外像素)。

Keras Blog里的数据示例

在这段韶光里,我们开始考试测验着精调神经网络构造。
特殊是我们开始利用批规范化(Batch Nomalization),并考试测验了不同的激活函数(activation functions)。

批规范化能够通过将stack里的数值”光滑化“来帮助你的神经网络学得更快。
详细为什么批规范化能有这个功能还没有被完完备全理解,但它能使你的神经网络用更少的演习达到更高的准确率,或者用同样多的演习一下子达到更高的准确率。

激活函数是一种内部函数,用来检测你的“神经元”是否被激活。
很多学术文章中仍利用ReLU(Rectified Linear Unit 线性整流函数),但我们用ELU得到了最好的结果。

在给SqueezeNet加上批量范化和ELU之后,我们从零开始,演习出了准确率在90%以上的神经网络。
但是,我们的神经网络还是相比拟较薄弱的,这意味着同样的网络会在有些时候过度拟化,但在面对实际测试时又会涌现拟化不敷的情形。
就算是在数据组里增加更多的范例并且增加演习数据也没能达到我们的预期。

以是只管这个阶段的事情成效不错,而且做出了一个完备能在iPhone上利用的app,但瞬间我们就转移到我们的第四个,也便是末了一个构造了。

三、DeepDog构造

from keras.applications.imagenet_utils import _obtain_input_shape

from keras import backend as K

from keras.layers import Input, Convolution2D, SeparableConvolution2D, \

GlobalAveragePooling2d \

Dense, Activation, BatchNormalization

from keras.models import Model

from keras.engine.topology import get_source_inputs

from keras.utils import get_file

from keras.utils import layer_utils

def DeepDog(input_tensor=None, input_shape=None, alpha=1, classes=1000):

input_shape = _obtain_input_shape(input_shape,

default_size=224,

min_size=48,

data_format=K.image_data_format(),

include_top=True)

if input_tensor is None:

img_input = Input(shape=input_shape)

else:

if not K.is_keras_tensor(input_tensor):

img_input = Input(tensor=input_tensor, shape=input_shape)

else:

img_input = input_tensor

x = Convolution2D(int(32alpha), (3, 3), strides=(2, 2), padding='same')(img_input)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(32alpha), (3, 3), strides=(1, 1), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(64 alpha), (3, 3), strides=(2, 2), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(128 alpha), (3, 3), strides=(1, 1), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(128 alpha), (3, 3), strides=(2, 2), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(256 alpha), (3, 3), strides=(1, 1), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(256 alpha), (3, 3), strides=(2, 2), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

for _ in range(5):

x = SeparableConvolution2D(int(512 alpha), (3, 3), strides=(1, 1), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(512 alpha), (3, 3), strides=(2, 2), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = SeparableConvolution2D(int(1024 alpha), (3, 3), strides=(1, 1), padding='same')(x)

x = BatchNormalization()(x)

x = Activation('elu')(x)

x = GlobalAveragePooling2D()(x)

out = Dense(1, activation='sigmoid')(x)

if input_tensor is not None:

inputs = get_source_inputs(input_tensor)

else:

inputs = img_input

model = Model(inputs, out, name='deepdog')

return model

1、设计

我们末了的构造很大一部分是受谷歌在4月17日在MobileNets论文的启示。
这篇文章称谷歌会推出一个新的神经构造,这个构造只用4百万旁边的参数就能在我们这种大略的项目上达到能与Inception相媲美的准确率。

这意味着它是SqueezeNet(对我们的意图来说过度简化的构造)和Inception/VGG之间的完美权衡。
这篇文章还提到了该神经网络调度大小和难度的功能,能够在内存和准确率之间取舍,这可办理了我们当时的心头病。

间隔app必须上线的不到一个月的时候,我们热切地想要重修文章中的结果。
然而戏剧性是,在文章发布确当天,一个来自伊斯坦布尔工业大学的学生Refik Can Malli率先在GitHub上公开供应了Keras的代码。

我们末了的构造和MobileNets的构造,或者说与传统的构造有显著的不同,尤其是:

我们在处理时候没有利用批量范化, 由于Xception的文章(特殊详尽地谈论了深度网络上的卷积)彷佛在指出批量范化实际上会使我们的这种构造准确率低落。
不该用批量范化同时还能减个人们的神经网络大小。

我们用ELU取代了ReLU。
就像我们之前的SqueezeNet实验,ELU相较ReLU供应了更高的领悟速率和准确度。
我们并没有利用PELU。
由于虽然好用,但这个激活函数彷佛在我们每次利用它时都会陷入二进制状态。
以是我们的我们网络的准确率在从一个批量到达另一个批量的时候会在0%和100%中往来来往,而不是循规蹈矩地增长。
这个问题发生的缘故原由我们也不清楚,有可能是由于implementation缺点或是用户缺点。
我们试着合并图片中的宽/高轴,但没有成效。

我们没用SELU。
在对iOS和安卓的发布(release)进行了一番调查后,得到的结果与PELU的非常类似。
我们疑惑SELU不能作为一种激活函数的捷径来单独利用。
事实上,正如文章的题目映射的,SELU是狭义SNN构造中的一部分。

我们在利用ELU的时候连续利用了批量范化。
虽然有很多迹象表明这一步是不必要的,但是我们每次运行试验的时候,如果没加批量范化就无法融汇。
这有可能是由于我们构造太小。

我们是在激活之前利用批量范化的。
虽然这是最近大家热议的主题,但我们在小型神经网络上在激活之后加入批量范化的试验都不能很好的融汇。

为了优化神经网络,我们利用了周期学习率(Cyclical Learning Rates,CLR)以及(小伙伴)Brad Kenstler的 Keras 模型)。
CLR可以踏实地找出演习时的最优学习进度(optimallearning rate)。
更主要的是,通过高下调度学习进度,它能帮助我们达到比传统优化器更高的终极准确率。
由于以上的两个缘故原由,我们决定往后都用CLR来演习神经网络。

我们以为没有必要调度MobileNets构造里 α 或 ρ 的值。
由于我们的模型足够小,在α = 1的时候足以达成目标。
ρ = 1我们的运算也足够快了。
但是,如果比较旧的移动设备或者嵌入式平台上运行的话就有必要调度了。

以是这个stack是怎么事情的?深度学习常常给大家一种“黑盒”的觉得。
但虽然很多部分确实比较神秘,但我们所用的神经网络常常会向我们透露一些信息,以戳穿它是如何事情的。
我们可以看到stack里的层以及这些层是如何激活特定的输入图片的。
这见告了我们每一层分别识别喷鼻香肠,面包,以及热狗的其他特点的能力。

2、演习

数据的质量是至关主要的。
供应的演习数据有多好,这个神经网络构造才会好。
提高演习数据的水准是我们在这个项目上最花韶光的三件事之一。

我们所做的比较关键的提升有以下这些:

获取更多图片以及更多样化的图片(长宽,背景,灯光,文化差异,透视远景,构图等等)。

将图片类型比对到预期的产品输入值(用户图片)。
我们的预测是,人们大多数情形下用户会拍摄热狗或其他其他食品的图片,或者有的时候人们会试图用随机的物品来磨练该运用,以是我们的在建立数据组的时候考虑到了这些成分。

给你的神经网络供应一些可能会以假乱真的相似图片。
和热狗最随意马虎稠浊的是其他的食品(比如汉堡,或者光是喷鼻香肠,亦或是小胡萝卜和烧熟的小番茄)。
我们的数据组中表示了这一点。

预期的图片扭曲:在手机的情形下,大多数图片的质量会比用单反的“均匀水平”要低。
手机摄像时的灯光也不尽如人意。
移动设备拍摄的照片一样平常会更暗或者有一定的角度。
大规模的数据集是办理问题的关键。

其余我们还推测如果用户手头上没有热狗,他们会在谷歌上搜索热狗的图片,然后用手机拍摄电脑频幕,而这会导致另一种扭曲(如果拍照有角度会造成图片的倾斜,如果用手机相机拍摄液晶显示器还会有光点和和明显的莫列波纹)。

这些特定的扭曲很随意马虎迷惑我们的神经网络,这和最近揭橥的一些有关卷积网络难以抵抗噪音的学术文章有异曲同工之处。
利用Keras的通道转换(channel shift)功能可以办理大部分问题。

图:Wikimedia 扭曲示例:莫列波纹和光点。

有些边边角角的问题很难察觉。
尤其是用柔焦或背景虚化得到的照片有时也会迷惑我们的神经网络。
这个问题很难办理。

第一,用柔焦拍摄的热狗照片很少(我们想想就饿了)。

第二,如果我们花大量神经网络容量来演习识别柔焦的能力是弊大于利的,由于绝大多数用移动设备拍摄的图片没有这个功能。
我们选择基本不考虑这个问题。

我们末了的数据组是15万张图片,个中只有3000张热狗的照片:热狗只有这么点,但不是热狗的东西多了去了。
这里49:1的失落衡我们用Keras类的权重设置来填补。
别的的14万7千张图中大多数是食品,有大概三千张不是食品,这能够帮助我们的网络更好地交融贯通,不被一个穿着热狗外衣的实物所迷惑。

我们数据扩容的条件如下:

我们所添加的旋转在 ±135 度之间-远大于均匀值,由于我们敲代码的时候不考虑手机的方向。

高和宽移动20%

剪切范围30%

缩放范围10%

通道转换(channel shift)20%

随机水平翻转以帮神经网络提高概括归纳的能力

这些数字都是我们基于考试测验和我们对该app实际利用理解的直觉所得,并不是严谨地通过实验得来的。

我们数据准备中的末了关键便是利用Patrick Rodriguez的Keras 多进程图像数据天生器(multiprocess image data generator)。

虽然Keras有自带的多线和多元处理的implementation,我们试验创造Patrick的数据库运行得更快(详细缘故原由我们还没空仔细研究)。
这个库帮助我们将演习韶光缩短了三分之二。

这个神经网络是在用一台2015 MacBook Pro以及外接的GPU演习的。
我们当时的eGPU用的是Nvidia GTX 980 Ti,但如果我们现在开始做的话会买一个1080 Ti。

我们当时能以128张图片一批的速率演习网络。
我们的神经网络一共演习了240次(epoch),也便是说我们运行15万张图用了240次。
这花了80个小时。

我们分以下三个阶段演习神经网络:

第一阶段运行112次演习(以一级8次演习来完成7个完全的CLR cycle)。

我们的学习率在0.005到0.03之间,基于triangular 2 方法(意思是最大学习率每16次就减半一次)。

第二阶段再运行64次演习(以一级8次演习来完成4个CLR cycle),学习率在0.0004到0.0045之间,仍基于triangular 2 方法。

第二阶段再运行64次演习(以一级8个演习来完成4个CLR cycle),学习率在0.000015到0.0002之间,仍基于triangular 2 方法。

更新:之前一版的图表有缺点信息

虽然学习率是通过运行由CLR文章所推举的线性实验所得,但实际上这些数字还是不难明得的。
每一接单的最大值都是前一阶段最小值的一半旁边,这与行业里推举的标准(如果在演习时碰着准确率的平台期,要减半你的学习率)是符合的。

为了节省韶光,我们有一部分演习是在一个Paperspace P5000运行实例(instance)的Ubantu上进行的。
在这部分演习中,我们能够将批量大小翻倍。
我们创造这样的话,每个阶段的最优学习率也差不多翻倍了。

3、在手机上运行神经网络

虽说我们已经搭建了一个相对紧凑的神经构造,而且也将其演习成了可以适应移动环境的构造了,但我们还要做不少事情才能让它很好地运行。
试图直接运行这种高等神经网络构造很快就会花费几百MB的RAM,而现在的大多数手机都没有这么多余的内存了。
抛开优化神经网络不说,你操作图片的办法,乃至加载TensorFlow本身都会对你的神经网络运行速率、RAM利用量以及濒临系统崩溃的用户体验带来影响。

这可能便是这个项目最神秘的部分了。
关于它我们险些找不到任何信息,这或许是由于目前在移动设备上运行深度学习运用的例子少之又少。
但是,我们在这里要感谢TensorFlow的团队,特殊是Pete Warden,Andrew Harp和Chad Whipkey的耐心。

通过化整我们神经网络的权重,我们把网络大小压缩到了原来的四分之三。

终极,不同于利用我们演习中任意的存储值,我们的这项优化会选出N个最常涌现的值,并将你的神经网络中所有的参数设置成这些值。
这在压缩之后可以减小神经网络的大小。

然而这对付未经压缩的app大小或内存用量是没有任何影响的。
我们并没有把这项优化放到末了的产品上,由于一方面我们的神经网络已经足够小了,另一方面,我们没有韶光量化这项化整对付app准确率的影响。

通过将TensorFlow软件库编译到iOS系统来对其进行优化。

从TensorFlow软件库中移除不必要的运算法则:TensorFlow从某种意义上而言是一台虚拟机器(virtual machine),它能够理解一个数字或者任意TensorFlow运算法则:相加,相乘,字串串接(Concatenation)等等。

你可以在编译到iOS的过程中,通过从TensorFlow软件库中移除不必要的运算法则,来省下很大一部分内存。

其他的提升也可行。
比如,笔者的另一项不相关的事情歪打正着地在安卓二进制大小上有了1MB的提升。
以是由此可以推测,在TensorFlow的iOS代码中也有地方可以改进优化。

除了在iOS上利用TensorFlow这个选项,我们还关注过Apple自带的深度学习软件库(BNNS, MPSCNN以及后来的CoreML)。
我们本可以在Keras上设计神经网络,在TensorFlow上演习,然后导出全部的数值,重新再BNNS或MPSCNNimplement(也可以直接由CoreML导入),然后将参数加载到新的implementation上去。

但是,这些新的Apple软件库的最大障碍便是它们只能在iOS 10+上利用,而我们想让app在更早版本的iOS上也能够利用。
随着iOS 10+被公众年夜众利用采纳,以及框架的更新换代,在不久的将来,我们或许就不须要再用TensorFlow了。

#import <CodePush/CodePush.h>

NSString FilePathForResourceName(NSString name, NSString extension) {

// NSString file_path = [[NSBundle mainBundle] pathForResource:name ofType:extension];

NSString file_path = [[[[CodePush.bundleURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@\公众assets\"大众] URLByAppendingPathComponent:name] URLByAppendingPathExtension:extension] path];

if (file_path == NULL) {

LOG(FATAL) << \"大众Couldn't find '\公众 << [name UTF8String] << \"大众.\公众

<< [extension UTF8String] << \"大众' in bundle.\"大众;

}

return file_path;

}

AIManager.mm hosted with ❤ by GitHub

import React, { Component } from 'react';

import { AppRegistry } from 'react-native';

import CodePush from \"大众react-native-code-push\"大众;

import App from './App';

class nothotdog extends Component {

render() {

return (

<App />

)

}

}

require('./deepdog.pdf')

const codePushOptions = { checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME };

AppRegistry.registerComponent('nothotdog', () => CodePush(codePushOptions)(nothotdog));

index.ios.js hosted with ❤ by GitHub

我们的项目还有什么不同?

实在还有很多事情我们没韶光做的或者试过没成的,这些是我们日后所要探索的想法:

更仔细地精调我们数据增长参数。

端到端,即运用程序终极确定的抽象方法,如我们的运用程序是否有2个或更多类别的热狗识别阈值是什么(我们终极有运用程序说“热狗”如果识别大于0.90,而默认值为0.5),重量更加等。

在app中添加一个反馈机制 -- 如果涌现缺点时可以许可用户吐槽,或者能够app主动优化神经网络。

利用比224 x 224像素更高的图片识别分辨率。
终极我们要利用一个大于1.0的MobileNets ρ 值。

4、用户体验 ,开拓者体验以及AI 的胆怯谷

末了,我们要说说用户体验,开拓者体验以及内置偏见给一个AI app开拓造成的巨大影响。
每一点我都可以再写一篇博文(乃至一本书),但是在这里我们就讲讲这三件事儿在我们自己的履历中非常踏实的影响。

用户体验在AI app开拓中的每一阶段都要比传统app来的关键。
目前还没有深度学习算法可以带给你完美的结果,但是有很多情形中,深度学习和用户体验的得当组合会带给你几近完美的结果。

得当的用户体验预期无可替代,尤其可以将开拓职员在设计神经网络时引向正途, 对利用软件的用户设定得当的预期,并且帮助办理无可避免的AI bug。
如果搭建AI app没有用户体验为上的理念,就好象不用随机梯度低落法(Stochastic Gradient Descent)来演习神经网络:你会终极在搭建完美AI的途中陷入胆怯谷(Uncanny Valley)的最低值。

图: New Scientist

开拓者体验同样至关主要,由于深度学习演习须要花费很长的韶光,而且你要等程序演习完才能运行。
我们建议你先看重开拓者体验,由于之后总能优化运行韶光(手动GPU并行,多元数据注入,TensorFlow管道,乃至可以重新implement你的caffe2 / pyTorch)如果有了好用的,而且掩护得很好的演习及运行神经网络的环境,纵然你用的是像TensorFlow那样相对低级的API和文档,你的开拓者体验也会得到大大的提升。

同样的道理,你很难以低廉的价格得到拥有在本地GPU环境下,来自主开拓的灵巧性。
能够在本地查看/编辑图片,并且利用你喜好的工具来编辑代码,可以大大提升开拓AI项目的质量和速率。

说到AI app的自带偏见,可能绝大多数的其他AI运用会碰着比我们更棘手的问题。
但就连我们这款大略的app都会碰着文化偏见的难题。
比如,我们的app无法识别法度模范热狗,亚洲热狗,或者其他我们没有打仗到的种类。

我们须要认识到的是,AI并不能做出比人类更好的决策,它们是由不完美的人类设计出来的,那么它们熏染上人类的偏见也是无可厚非的。

感谢所有用过并分享这款app的伙伴们!
我们这几个月来每天盯着热狗的图片,打开门看窗外满天下都是热狗了......