在上篇的博文中,你学到了
如何用Keras训练卷积神经网络
今天,我们将这种经过训练的Keras模型部署到iPhone手机或者说iOS的APP中,在这里我们使用CoreML,这 是一种易于使用的Apple应用程序机器的学习框架。
回顾一下,在这个由三部分组成的系列中,我们学习了:
- 如何快速构建图像数据集
- 训练Keras和卷积神经网络
- 使用CoreML将我们的Keras模型部署到手机应用程序中
我今天的目标是向你展示使用CoreML将Keras模型部署到iphone手机中是多么简单。
有多简单呢?坦白的说,我不是一个的移动开发者,如果我能做到,我相信你也能做到。
你可以随意使用今天发布的代码作为你应用程序的起点。
但就我个人而言,我将继续这个系列的主题,并建立一个神奇宝贝图鉴(Pokedex)。它是一款存在于宠物小精灵世界中的设备(我一直是个口袋妖怪迷)。
使用Pokedex,你可以拍摄一个神奇宝贝的图片,Pokedex会自动识别你的宠物,提供有用的信息和资料,例如神奇宝贝的身高,体重,以及它可能拥有的特殊能力。
你也可以自由地将Keras模型替换为你自己的,过程非常简单明了。
使用CoreML在iOS上运行Keras模型
本文分为四个部分。
首先,我将介绍关于CoreML的背景,包括它是什么以及为什么我们使用它。
从那里开始,我们将编写一个脚本将我们训练 好的Keras模型从HDF5文件转换为序列化的CoreML模型 - 这是一个非常简单的过程。
接下来,我们将在Xcode中创建一个Swift项目。对于熟悉Xcode的人来说,这一步没有什么困难,但对于我来说,我必须使用在线资源学习(我不是移动专家,而且我已经很久没有使用Xcode了) 。
我认为我已经讲的足够细致了,除非你需要修改代码,否则没有必要去网上搜索。
有时,你可能想要注册Apple开发人员计划,我会在测试iPhone上的应用程序之前简略讲讲。
最后,我们将编译应用程序并将Keras模型部署到我们的iPhone和iOS上。
什么是CoreML,它的用途是什么?
在iPhone上制作CoreML深度学习计算机视觉应用程序,请遵循以下步骤:(1)收集图像,(2)使用Keras训练和保存模型,(3)转换模型文件coremltools,(4)导入将模型放入Xcode Swift应用程序中,(5)编写Swift代码以对摄像头进行帧的推断,(6)部署到iPhone!
CoreML是苹果公司开发的一个机器学习框架,其目标是让任何想要为iOS/iPhone开发一个机器学习移动应用程序的人都能轻松地集成机器学习应用程序。
CoreML支持Caffe,Keras,scikit-learn等等。
现在,你需要一个经过训练的,序列化的Keras模型文件来转换成CoreML(Xcode兼容)文件。这可能是。
如果你选择使用自己的自定义模型,则需要检查CoreML文档以确保支持你在网络内使用的层。
文档:https://developer.apple.com/documentation/coreml/converting_trained_models_to_core_ml
在那里,你需要的只是加载模型和运行推断的几行代码。
苹果公司的CoreML开发团队确实无法让它变得特别容易(值得给个五星好评)。
我是一名计算机视觉+深度学习专家,并不是应用程序开发人员
坦白的说:我不是一名移动应用程序开发人员(而且我也不会声称自己是)。
当然,我以前也开发过像ID My Pill和Chic Engine之类的应用程序 ,但移动开发并不是我的特长和爱好。实际上,这些应用程序是由PhoneGap/Cordova使用HTML、JavaScript和CSS创建的,没有任何Objective-C或Swift知识。
相反,我是一个通过并且通过计算机视觉的人。当涉及到移动应用程序时,我主要依赖易于使用的框架,例如PhoneGap / Cordova和(现在的)CoreML。
为了学习这篇博文的CoreML基础知识,我从网上其他专家开发人员那里收集了这个项目所需的知识。特别是下面这边文章:
链接:https://medium.freecodecamp.org/ios-coreml-vision-image-recognition-3619cf319d0b
这里的代码大部分都是基于此,只需要一两个小小的修改。感谢它,使这个项目成为可能!
通过CoreML和Python使Keras模型与iOS兼容
在本节中,我们使用pip安装coremltools包。
要安装 coremltools ,请确保您处于带有相关库(我们正在使用Keras)的Python虚拟环境中,然后输入以下命令:
pip install coremltools
从那里,通过滚动到本博客文章的“下载”部分并下载代码,获取我的转换器脚本和相关文件。
安装完成后,打开 coremlconverter .py 并按照下列步骤操作:
# import necessary packages
from keras.models import load_model
import coremltools
import argparse
import pickle
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", required=True,
help="path to trained model model")
ap.add_argument("-l", "--labelbin", required=True,
help="path to label binarizer")
args = vars(ap.parse_args())
第2-5行导入我们所需的软件包。
然后我们解析我们的命令行参数。我们有两个参数:
- --model:在磁盘上的预训练、序列化Keras模型的路径。
- --labelbin:我们的类标签binarizer的路径。这个文件是我们之前发布的训练CNN的文章中的scikit-learn的LabelBinarizer对象。如果你没有 LabelBinarizer对象,则需要修改代码以硬编码 class_labels集合 。
加载类标签和我们的Keras模型:
在 第17-19行,我们加载我们的类标签pickle文件,并将class_labels 作为列表存储 。
接下来,我们将训练好的Keras模型加载到一行(第23行)。
然后,我们从coremltools调用converter并将生成的模型保存到磁盘:
第27行 ,我们调用coremltools.converters.keras.convert函数。一定要参考文档中的关键参数说明。我们今天正在使用以下参数:
- model:我们正在转换的Keras模型。实际上,你可以在这里放置一个路径+文件名,但我选择输入模型对象(API支持两种方法)。
- input_names = “image” :引用自文档:“可以赋予Keras模型输入的可选名称。这些名称将在Core ML模型的界面中用于引用Keras模型的输入。如果未提供,Keras输入在Core ML模型中命名为[input1,input2,...,inputN]。当存在多个输入时,输入特征名称与Keras输入的顺序相同。”
- image_input_names = “image” :从文档引用:“将名称输入可以被Core ML处理为图像Keras模型(input_names参数的子集)。所有其他输入都被处理为MultiArrays(N-D数组)。“
- image_scale = 1 / 255.0 :这个参数非常重要。在训练网络之前,通常会将图像的像素强度缩放到[0,1]。如果你执行了此类缩放,请务必将 image_scale 参数设置为scale factor。在训练期间你可能已经完成了双重和三重检查、缩放以及预处理,并确保你在转换过程中反映了这些预处理步骤。
- class_labels = class_labels :在这里,我们提供了我们模型所训练的类标签集。我们从LabelBinarizer对象中获得了class_label。 如果你愿意,也可以硬编码class_labels。
- is_bgr = True :这个参数很容易忽略 。如果你的模型是使用BGR颜色通道排序进行训练的,那么将此值设置为True非常重要, 以便CoreML按预期运行。如果模型是使用RGB图像进行训练的,则可以放心地忽略此参数。如果你的图像不是BGR或RGB,请参阅文档。
我还想指出,如果您在iPhone应用程序中对查询图像执行均值减法,则可以通过参数添加红/绿/蓝/灰的偏差。例如,这对许多ImageNet模型都是必需的。 如果你需要执行此步骤,请务必参阅文档。均值减法是Python深度学习计算机视觉中常见的预处理步骤 。
我们脚本的最后一步是保存输出的CoreML protobuf模型:
Xcode预期的文件扩展名为.mlmodel 。因此,我选择使用代码而不是命令行参数来处理它,以避免可能出现的问题。
第35行将.model扩展从输入路径/文件名中删除,并将其替换为.mlmodel,将结果存储为输出。
第37行使用正确的文件名将文件保存到磁盘。
这就是这个脚本的全部内容。感谢Apple CoreML开发人员!
运行Keras到CoreML的转换脚本
我们的脚本可以通过传递两个命令行参数来执行:
- 模型的路径
- 标签binarizer的路径
准备好后,在终端中输入以下命令并根据需要查看输出:
$ python coremlconverter.py --model pokedex.model --labelbin lb.pickle
Using TensorFlow backend.
[INFO] loading class labels from label binarizer
[INFO] class labels: ['background', 'bulbasaur', 'charmander', 'mewtwo', 'pikachu', 'squirtle']
[INFO] loading model...
[INFO] converting model
0 : conv2d_1_input,
1 : conv2d_1,
2 : activation_1,
...
22 : batch_normalization_6,
23 : dense_2,
24 : activation_7,
[INFO] saving model as pokedex.mlmodel
Input name(s) and shape(s):
image : (C,H,W) = (3, 96, 96)
Neural Network compiler 0: 100 , name = conv2d_1, output shape : (C,H,W) = (32, 96, 96)
Neural Network compiler 1: 130 , name = activation_1, output shape : (C,H,W) = (32, 96, 96)
Neural Network compiler 2: 160 , name = batch_normalization_1, output shape : (C,H,W) = (32, 96, 96)
...
Neural Network compiler 21: 160 , name = batch_normalization_6, output shape : (C,H,W) = (1024, 1, 1)
Neural Network compiler 22: 140 , name = dense_2, output shape : (C,H,W) = (5, 1, 1)
Neural Network compiler 23: 175 , name = activation_7, output shape : (C,H,W) = (5, 1, 1)
然后,列出你的目录的内容:
$ ls -al
total 299240
drwxr-xr-x@ 6 adrian staff 192 Apr 11 15:07 .
drwxr-xr-x@ 5 adrian staff 160 Apr 11 15:06 ..
-rw-r--r--@ 1 adrian staff 1222 Apr 11 11:06 coremlconverter.py
-rw-r--r--@ 1 adrian staff 34715389 Apr 11 11:07 pokedex.mlmodel
-rw-r--r--@ 1 adrian staff 104214208 Mar 28 06:45 pokedex.model
drwxr-xr-x@ 4 adrian staff 128 Apr 10 08:36 xcode
...你会看到
pokedex .mlmodel,它可以直接导入到Xcode中(我们将在下一节的第4步中继续这样做)。有趣的是,你可以看到文件比原始的Keras模型小,这可能意味着CoreML在转换过程中删除了了任何优化器状态。
注意: 为了让我的Pokedex应用程序能够识别相机是面对的是“日常物品”还是神奇宝贝,我添加了一个名为“background”的类 (这样做的目的是消除误报)。然后,我使用上篇文章的代码重新训练模型。background类由从我的系统上的UKBench数据集中随机抽取的250个图像组成。
在Xcode中创建一个Swift + CoreML深度学习项目
第0步: 准备开发环境
本节的第0步是在Macintosh电脑上下载并安装Xcode。如果你的Xcode版本不是至少9.0版,那么就需要升级。在某些时候,我的Xcode要升级到9.3版本来支持我的iPhone iOS 11.3。
警告:升级Xcode可能破坏计算机上的其他开发软件或环境(比如安装了OpenCV的Python虚拟环境)。请小心使用MacInCloud之类的服务,以免破坏本地开发环境。
一旦你安装并检查了正确版本的XCode,你就可以继续下去了。
第1步:创建项目
为了规整,我在我的主目录中创建一个名为xcode的文件夹,用于存放所有的xcode项目。我创建了以下目录: 〜/ adrian / xcode 。
然后,启动Xcode并创建一个“Single View App”,如图所示。
接下来,你可以随意命名项目,我将它命名为
“pokedex”,如下所示。
第2步:删除storyboard
storyboard是一个视图控制器(可视化模型/视图/控制器架构)。我们将脱离简单应用程序的视图控制器。以编程方式创建视图。
继续并从左边的文件管理器中删除Main.storyboard。
在Xcode中删除Main.storyboard,我们不需要它来完成这个深度学习计算机视觉iOS应用程序。
然后,单击树中的高级应用程序名称(在我的案例中为
“pokedex ”)并滚动到
“Deployment info”。擦除标有
“Main Interface”的文本框的内容
第3步
:向info.plist添加一个元素
我们的应用程序访问相机,所以我们需要准备授权信息。这可以很容易地在info.plist中完成。
点击如图所示的
“+”按钮, 并添加Key + Value。该密钥必须完全符合“Privacy – Camera Usage Description”,但值可以自定义。
为我们的info.plist添加一个“Privacy – Camera Usage Description”,因为我们的CoreML应用程序必须使用iPhone摄像头。
第4步:创建应用程序窗口和根视图控制器
尽管我们删除了storyboard,也需要一个视图。在这一步,你需要将以下代码复制并粘贴到 AppDelegate .swift中 。函数已经定义好了,你只需要粘贴即可:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// create the user interface window and make it visible
window = UIWindow()
window?.makeKeyAndVisible()
// create the view controller and root view controller
let vc = ViewController()
window?.rootViewController = vc
// return true upon success
return true
}
第5步:将CoreML模型文件拖放到Xcode中
在Mac上使用Finder,点击上面创建的CoreML .mlmodel文件。
然后,将其拖放到项目树中。它会自动导入并创建相关的Swift类:
第6步: 构建ViewController
打开ViewController .swift 并导入我们需要的包或框架:
第10-12行为这个项目导入了三个必需的包。
UIKit包是开发iOS应用程序视图的通用框架,容许文本,按钮,表格视图,和导航。
AVFoundation框架是iOS上的影声媒介,我们用它从相机中捕捉。
我们使用 Vision框架为我们自定义的CoreML模型分类,但这个框架容许的远不止这些。借助Vision框架,可以执行人脸检测,面部标志检测,条形码识别,特征跟踪等。
现在我们已经导入了相关的框架,下一步创建 ViewController 类(从一个文本标签开始):
在第14行, ViewController 类是在继承UIViewController和 AVCaptureVideoDataOutputSampleBufferDelegate时定义的 。真长,让人回想起我在Java编程的日子!
在这门课上,我们首先要定义一个 UILabel ,它将保存我们的类标签和相关性的概率百分比文本。 16-23行处理这一步骤。
接下来,我们将重写viewDidLoad函数:
viewDidLoad函数在视图加载之后调用。对于通过代码创建的视图控制器,这个过程是在loadView之后 。
在 第25行中,我们使用override 关键字,这样编译器就知道我们重写了继承的类函数。
由于我们重写了函数,因此我们需要调用第27行所示的父函数 。
从那里,我们建立捕获会话(第30行),然后将标签添加为子视图(第31行和第32行)。
我把下一个函数作为一个完整性的问题包括在内;然而,我们实际上不会对它做任何修改:
如果测试应用程序时遇到内存不足警告,你可以重写带有附加规则的didReceiveMemoryWarning函数。我们保持原样推出,继续进行下一步。
让我们试着使用iOS和Swift设置摄像头捕捉访问权限:
func setupCaptureSession() {
// create a new capture session
let captureSession = AVCaptureSession()
// find the available cameras
let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices
do {
// select a camera
if let captureDevice = availableDevices.first {
captureSession.addInput(try AVCaptureDeviceInput(device: captureDevice))
}
} catch {
// print an error if the camera is not available
print(error.localizedDescription)
}
// setup the video output to the screen and add output to our capture session
let captureOutput = AVCaptureVideoDataOutput()
captureSession.addOutput(captureOutput)
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.frame
view.layer.addSublayer(previewLayer)
// buffer the video and start the capture session
captureOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
captureSession.startRunning()
}
请记住,我不是iOS开发专家,但上面的代码块还不难。
首先,我们需要创建一个捕捉会话(第44行)并查询相机并检查是否有错误(第47-57行)。
然后,我们将
预览图输出到屏幕的previewLayer(第60-64行)并启动会话(第67和68行)。
让我们对框架进行分类,并在屏幕上绘制标签文本
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// load our CoreML Pokedex model
guard let model = try? VNCoreMLModel(for: pokedex().model) else { return }
// run an inference with CoreML
let request = VNCoreMLRequest(model: model) { (finishedRequest, error) in
// grab the inference results
guard let results = finishedRequest.results as? [VNClassificationObservation] else { return }
// grab the highest confidence result
guard let Observation = results.first else { return }
// create the label text components
let predclass = "\(Observation.identifier)"
let predconfidence = String(format: "%.02f%", Observation.confidence * 100)
// set the label text
DispatchQueue.main.async(execute: {
self.label.text = "\(predclass) \(predconfidence)"
})
}
// create a Core Video pixel buffer which is an image buffer that holds pixels in main memory
// Applications generating frames, compressing or decompressing video, or using Core Image
// can all make use of Core Video pixel buffers
guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
// execute the request
try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])
}
虽然这对Python开发人员来说可能有点陌生,但这块没什么不可思议的。
我们在73行加载CoreML模型 。
然后,我们对给定的框架进行分类,并抓取76-79行的结果 。然后,我们可以从CoreML模型中获取第一个预测结果,并将其存储为名为Observation的对象 (第82行)。
预测的类标签可以通过Observation.identifier提取(第85行)。我们还规定confidence仅显示两位小数(第86行)。我们用这两个组件设置label 的文本(89-91行)。
最后,我们建立一个视频像素缓冲区并执行请求(97-100行)。
我们已经完成了最后的函数,然后在屏幕上指定标签的位置:
func setupLabel() {
// constrain the label in the center
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// constrain the the label to 50 pixels from the bottom
label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
}
}
setupLabel中的两个设置不言而喻 ,是指我们将标签设置在底部中心。
不要忘记最后标记ViewController类结束的括号!
注册Apple开发者计划
为了将项目部署到您的iPhone,要先注册Apple开发者计划。
注册后,在iPhone上接受证书。
注册过程很快,你只需等待Xcode和iPhone同步,然后再接受证书。我最终付了100美元,但你可以查看下面这篇博客文章创建免费的开发者帐户。
链接:https://9to5mac.com/2016/03/27/how-to-create-free-apple-developer-account-sideload-apps/
测试
现在我们准备编译并测试我们的深度学习应用程序!
我建议首先通过USB部署你的应用程序。如果你想与其他人分享,如果你想和别人分享,你可以利用TestFlight,然后在App Store上发布。
我们现在要使用USB。
首先,通过USB将你的iPhone插入Mac。你可能需要用你的识别码来解锁你的iPhone,当iTunes提示你信任该设备时,选择是。
然后,在Xcode菜单栏中,选择Product > Destination > Adrian's iPhone。
然后,构建并运行,选择Product > Run 。
如果你成功了,应用程序将会在你的iPhone上自动安装和打开。此时,你可以去寻找神奇宝贝周边(卡牌,毛绒玩具或手办)。
下面是我的CoreML应用程序的实际操作:
这绝对是一个简单的应用程序,但我很自豪,手机上有这个功能,可以向朋友,口袋妖怪迷以我的读者炫耀展示。
[video width="400" height="720" mp4="http://imgcdn.atyun.com/2018/06/Running-Keras-models-on-iOS-with-CoreML-and-building-a-real-life-pokedex.mp4"][/video]
如果有更多时间,可以在UI上放置一个按钮,以便拍摄我在外面遇到的神奇宝贝。这个交给Swift和iOS专家吧!
兼容性说明: 此应用程序已在iPhone 6s,iPhone 7和iPhone X上用iOS 11.3进行了测试。我使用xCode 9.3构建应用程序。
总结
在今天的博客文章中,我们看到,利用CoreML框架获取训练好的Keras模型并将其部署到iPhone和iOS非常简单。
希望你看到苹果公司CoreML框架中的价值,它对苹果开发人员和机器学习工程师来说简直是福音书,因为它可以吸收深度神经网络,并输出一种基本与iPhone和iOS兼容的模型。
我们在今天的iPhone应用中使用了Swift。尽管Swift不像Python那样简单(在此,为防止个人偏见,我持保留态度),但鉴于CoreML非常简单,你可以很省力的仿造这个项目来构建你自己的应用程序。