引言
作为一名数据科学家,我一直有一个梦想——***科技公司在与我相关的领域不断推出新产品。
如果你观看了Apple公司***的iPhone X发布会,你会发现iPhone X具有非常酷的特性,比如FaceID、动态表情、增强现实,这些特性都使用了机器学习。作为一名骇客,我决定亲自上手探索一下如何建立那样的系统。
进一步调查后我发现了一个很有趣的工具,那就是Apple官方面向开发者推出的机器学习框架工具CoreML。它可以在iPhone、Macbook、Apple TV、Apple watch等任何一个苹果设备上使用。
另一个有趣的发现是Apple在***的iPhone手机上设计了一个定制GPU,以及一个带有神经引擎的A11先进仿生学处理芯片,以便用于优化机器学习。
随着核心组件计算引擎功能日益强大,iPhone将开辟机器学习的新途径,CoreML在未来的意义将越来越重要。
读完这篇文章,大家将会了解Apple CoreML是什么以及为何它势头正猛。我们也将通过开发一款iPhone上的垃圾短信分类app来与大家一起一探CoreML的实现细节。
同时,我们也会通过客观评价CoreML的利弊来结束本篇文章。
文章目录:
- CoreML是什么?
- 建立系统
- 案例学习:实现一个iPhone上的垃圾短信分类app
- 使用CoreML的利弊
01、CoreML是什么?
今年,Apple公司在每年一次的全球开发者大会WWDC(类似于谷歌的I/O会议)上大肆宣传CoreML。为了更好地理解CoreML的作用,我们需要了解一些背景。
CoreML的背景
有趣的是,这并不是Apple公司***次发布移动端机器学习框架。去年它就发布了一些同样的框架库:
- Accelerate 框架和基本神经网络子程序(BNNS)——高效利用CPU并使用卷积神经网络进行预测。
- Metal Performance Shaders CNN(MPSCNN)——高效利用GPU并使用卷积神经网络进行预测。
这两个框架库的区别在于,一个针对CPU优化而另一个针对GPU。这是因为有时在inference(推断)过程中CPU比GPU计算更快,而在training(训练)过程中几乎每次都是GPU计算更快。
但为了提高性能,框架会非常接近底层硬件,使得这些混合框架对开发者造成混乱,从而很难编程。
走进CoreML
CoreML 会在之前提到的两个库上面提供一个抽象层,并且还会提供一个简单的接口,以达到同样的效率。另一个好处是,在我们的app运行时,CoreML充分照顾到了CPU与GPU之间的上下文切换。
换句话说,假如我们有一个耗内存的任务,它涉及文本处理(自然语言处理),CoreML将自动在CPU运行;而如果我们有一个计算繁重的任务,例如图像识别,它将使用GPU;当app包含这两种功能的时候,它又会自动切换从而使得两者都得到***化的利用。
CoreML还将提供什么?
CoreML顶层还附带了三个库:
- Vision:这个库提供了高性能图像分析与计算机视觉技术,用于人脸识别、特征检测以及图像与视频中的场景识别。
- Foundation(NLP):顾名思义,它提供了自然语言处理的一些功能
- Gameplay Kit:用于游戏开发的库,此外它还提供了AI,并运用决策树。
以上提到的所有库都可以用一些简单的接口轻松使用,可用于完成一系列任务。通过上述的库,CoreML最终框架图如下:
注意,上述设计给iOS应用程序提供了一个很好的模块化结构。你可以使用不同层进行不同的任务,也可以用多种方式使用它们(例如,在app中使用NLP进行图像分类)。了解更多:Vision、Foundation与GameplayKit。好了,现在我们有了足够的理论知识,是时候实践一下了!
“微信排版限制,需要代码的同学请看文末的原文链接自行查找”
02、建立系统
为了充分使用CoreML,你需要遵循如下要求:
1.OS:MacOS(Sierra 10.12或以上)
2.Python 2.7和pip:点击下载mac上的python。打开终端,输入如下代码安装pip:
- sudo easy_install pip
3.coremltools:这个包有助于将你的模型从python转换成CoreML能理解的格式。在终端输入如下代码进行安装:
- sudo pip install -U coremltools
4.Xcode 9:这是用于构建Apple设备上应用程序的默认软件。点此下载。下载Xcode之前,你需要使用Apple ID进行登陆。
登陆之后,你需要验证你的apple ID。你将会收到与注册Apple ID的设备相同的通知。
点击“允许”并输入网站显示的6位密码。
当你完成这一步,你将会看到一个下载选项。你可以在那儿下载Xcode。现在,我们建立好了系统,准备好了的话就让我们进入实现部分!
03、案例学习:实现一个iPhone上的垃圾短信分类app
在本次开发中,我们将着重于在两个重要途径上来使用CoreML的能力。让我们开始吧!
将你的机器学习模型转换成CoreML格式
CoreML其中一个优势,或者我应该说它的创造者作出的明智的决定是,支持在sklearn、caffe、xgboost等其他流行框架中训练好的机器学习模型的转换。
数据科学社区并不会不尝试CoreML试行,因为他们可以在他们最喜欢的环境中进行实验、训练他们的模型,然后轻松导入并在iOS/MacOS的app上使用。
下面是即时可用的CoreML支持的框架:
Mlmodel是什么?
为了使转换过程简单,Apple设计了它自己的开放格式来代表跨框架机器学习模型,即mlmodel。这个模型文件包含了模型各层的描述、输入、输出、类标签、任何需要对数据进行的预处理。它还包含了已学习的参数(权重及偏差)。
转换流程如下:
- 在你最喜欢的框架中训练模型
- 使用python模块coremltools将模型转换为.mlmodel格式
- 在app中使用模型
在本次例子中,我们将在sklearn中训练一个垃圾短信分类器,然后将该模型转给CoreML。
关于垃圾短信数据集
SMS垃圾短信数据集 v.1是一个公开的SMS标注短信数据集,用于手机垃圾短信研究。它包含了5574份真实无编码的英文短信,这些短信都标注了合法(做作)或者垃圾短信。
你可以在此下载该数据集。
建立基础模型
我们使用sklearn中的LinearSVC建立基础模型。同时,我们提取短信文本的TF-IDF值作为模型特征。TF-IDF是自然语言处理中的一种方法,它基于唯一标识文档的词来分类文档。如果你想要学习更多NLP和tf-idf的知识,你可以阅读这篇文章。代码如下:
- import numpy as npimport pandas as pd#Reading in and parsing dataraw_data = open('SMSSpamCollection.txt', 'r')sms_data = []for line in raw_data: split_line = line.split("\t") sms_data.append(split_line)
- #Splitting data into messages and labels and training and testsms_data = np.array(sms_data)X = sms_data[:, 1]y = sms_data[:, 0]
- #Build a LinearSVC modelfrom sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.svm import LinearSVC
- #Build tf-idf vector representation of datavectorizer = TfidfVectorizer()vectorized_text = vectorizer.fit_transform(X)text_clf = LinearSVC()text_clf = text_clf.fit(vectorized_text, y)
我们的模型建立好了,让我们用一份垃圾短信测试一下:
- #Test the modelprint text_clf.predict(vectorizer.transform(["""XXXMobileMovieClub: To use your credit, click the WAP link in the next txt message or click here>> http://wap. xxxmobilemovieclub.com?n=QJKGIGHJJGCBL"""]))
有趣,我们的模型效果很好!让我们添加交叉验证:
- #Cross - Validationfrom sklearn.model_selection import cross_val_scorecross_score = cross_val_score(text_clf, vectorized_text, y, cv=10)print cross_scoreprint "mean:",np.mean(cross_score)
- 现在已建好模型,为使它适用于CoreML,我们需要把它转换成.mlmodel格式。用之前安装的coremltools工具包来实现。以下代码能将我们的模型转换成.mlmodel格式。
- import coremltools
- #convert to coreml model
- coreml_model = coremltools.converters.sklearn.convert(text_clf, "message", "spam_or_not")
- #set parameters of the model
- coreml_model.short_description = "Classify whether message is spam or not"
- coreml_model.input_description["message"] = "TFIDF of message to be classified"
- coreml_model.output_description["spam_or_not"] = "Whether message is spam or not"
- #save the model
- coreml_model.save("SpamMessageClassifier.mlmodel")
这是如何运作的呢?
首先我们运用coremltools Python工具包。再选择一个转换器对模型进行转换,本例中用converters.sklearn,因为要转换的模型是用sklearn工具建立的。然后在.convert()括号内声明模型对象、输入变量名称、输出变量名称。接下来设置模型参数来添加更多关于输入、输出的信息,***用.save()保存已转换成CoreML格式的模型文件。
双击模型文件,会用Xcode打开。
如你所见,该模型文件显示了很多信息,关于模型的类型、它的输入、输出,输入输出的类型等。我已在上图中用红色标记。你可以将这些描述和转换成.mlmodel时所提供的一一对比。
将自己的模型引入CoreML就是这么简单。现在你的模型已经在苹果生态系统里了,接下来真正好玩的开始啦!
注意:这一步的完整代码文件请看这里。进一步了解coremltools请看这里,提供的不同种类的转换器请看这里。
将该模型用于我们的app
既然已经训练好模型并引入CoreML中,让我们用该模型开发一个iPhone垃圾信息分类app吧!
我们将在模拟器上运行app。模拟器这一软件能显示app的界面及运行情况,像在iPhone上真正运行那样。这样节省了大量时间,因为用iPhone运行app之前,我们就可以测试代码、调试。看一下最终产品长什么样子吧:
下载工程
我已经为我们的app制作了一个简单基础的UI放在GitHub上。用以下命令加载并运行:
- git clone https://github.com/mohdsanadzakirizvi/CoreML-on-iPhone.git
- cd CoreML-on-iPhone/Practice\ App/
- open coreml\ test.xcodeproj/
这会在Xcode打开我们的项目。
在Xcode窗口中我用红色标示了三个重要区域:
- 左上角的播放按钮用来启动app在模拟器运行。
- 播放按钮的正下方列出了与我们项目相关的文件和文件夹。这是项目导航栏,方便你找项目里的文件和文件夹。
- 播放按钮旁边写着iPhone 8,表示你想用模拟器仿真的目标设备。你可以点击它,在下拉列表里选择iPhone 7。
让我们开始运行app吧,看看会发生什么。点击左上角的播放按钮让模拟器运行app。在框中随便键入些文字,点击预测按钮。发生了什么?
到现在,我们的app什么也没做,只是原样输出框中键入的文字。
向你的app中添加一个训练好的模型
相当简单:
- 将你的.mlmodel模型文件拖入到Xcode窗口工程导航栏中。
- 做好后,会弹出一个含有几项选择的窗口,默认缺省,点击“结束”。
- 当你像这样拖拽文件到Xcode时,自动在工程中生成该文件的参考路径。这样你能轻松地在代码中获取该文件。
编译模型
在能够用我们的模型进行推测之前,需要让Xcode在建立阶段中编译模型。以下是具体步骤:
在工程导航栏中选择有蓝色标识的文件
会在右手边打开工程设置。点击Compile Sources(编译源)并选择+标识。
在新出现的窗口中选择 SpamMessageClassifier.mlmodel文件,点击新增。
现在每次运行app,Xcode就会编译我们的机器学习模型,使它能用来做预测。
在代码中创建模型
任何为苹果设备开发的app都用swift编程。你不需要学swift但如果以后你有兴趣深入,你可以跟着这个教程学。
在工程导航栏中选择 ViewController.swift。这一文件包含大部分控制app功能的代码。
第24行的 predictSpam() 函数会做最多的工作。删除第25行,向函数中添加以下代码:
- let enteredMessage = messageTextField.text!
- if (enteredMessage != ""){
- spamLabel.text = ""
- }
- //Fetch tfidf representation of text
- let vec = tfidf(sms: enteredMessage)
- do {
- //Get prediction on the text
- let prediction = try SpamMessageClassifier().prediction(message: vec).spam_or_not
- print (prediction)
- if (prediction == "spam"){
- spamLabel.text = "SPAM!"
- }
- else if(prediction == "ham"){
- spamLabel.text = "NOT SPAM"
- }
- }
- catch{
- spamLabel.text = "No Prediction"
- }
以上代码检查用户是否向框内输入了任何信息。如果有,调用tfidf()函数计算文本的tfidf值。然后生成一个SpamMessageClassifier 对象实例,再调用.prediction() 函数。这与sklearn中的 .predict() 函数相同。然后基于预测展示恰当的信息。
但为什么需要tfidf()?
记住我们基于文本的tf-idf表征来训练模型,因此我们的模型需要相同形式的输入。一旦获得键入的文本框的信息,就调入tfidf()函数来做同样的事。来写这一步的代码吧,复制下列代码放在predictSpam()函数后:
- //MARK: Functionality code
- func tfidf(sms: String) -> MLMultiArray{
- //get path for files
- let wordsFile = Bundle.main.path(forResource: "wordlist", ofType: "txt")
- let smsFile = Bundle.main.path(forResource: "SMSSpamCollection", ofType: "txt")
- do {
- //read words file
- let wordsFileText = try String(contentsOfFile: wordsFile!, encoding: String.Encoding.utf8)
- var wordsData = wordsFileText.components(separatedBy: .newlines)
- wordsData.removeLast() // Trailing newline.
- //read spam collection file
- let smsFileText = try String(contentsOfFile: smsFile!, encoding: String.Encoding.utf8)
- var smsData = smsFileText.components(separatedBy: .newlines)
- smsData.removeLast() // Trailing newline.
- let wordsInMessage = sms.split(separator: " ")
- //create a multi-dimensional array
- let vectorized = try MLMultiArray(shape: [NSNumber(integerLiteral: wordsData.count)], dataType: MLMultiArrayDataType.double)
- for i in 0..
- let word = wordsData[i]
- if sms.contains(word){
- var wordCount = 0
- for substr in wordsInMessage{
- if substr.elementsEqual(word{
- wordCount += 1
- }
- }
- let tf = Double(wordCount) / Double(wordsInMessage.count)
- var docCount = 0
- for sms in smsData{
- if sms.contains(word) {
- docCount += 1
- }
- }
- let idf = log(Double(smsData.count) / Double(docCount))
- vectorized[i] = NSNumber(value: tf * idf)
- } else {
- vectorized[i] = 0.0
- }
- }
- return vectorized
- } catch {
- return MLMultiArray()
- }
- }
以上代码得到文本框内输入信息的tfidf表征,为此读取SMSSpamCollection.txt原始数据库并返回同样信息。一旦你保存项目然后再次运行模拟器,你的app就会运行良好。
4、CoreML优缺点
像每个发展中的库一样,CoreML有优点也有缺点。让我们说清楚。
优点:
- 对在移动设备上运行性能进行优化,最小化内存和能量消耗。
- 在移动设备上运行保证了用户隐私,不再需要将数据发给服务器做预测。
- 在移动设备上运行意味着甚至在没联网的时候都可以做预测,此外对用户来说反应时间更短。
- 能自己决定在CPU还是GPU上运行(或者都有)。
因为它可以用CPU,所以你能在iOS模拟器上运行它(iOS模拟器不支持GPU)。
提供了很多模型,因为它能从其他主流机器学习框架中引入模型:
- 支持向量机(SVM)
- 树集成,如随机森林、提升树
- 线性回归和逻辑回归
- 神经网络:前向反馈、卷积、循环
缺点:
- 只支持有监督模型,不支持无监督模型和强化学习。
- 不支持模型在设备上再训练,只能做预测。
- 如果CoreML不支持某种层,你就不可以使用。目前还不能用自己的层扩展CoreML。
- CoreML工具只支持少量训练工具的特定版本(竟然不支持tensorflow)。
- 不能看中间层的输出,只能得到预测结果。
- 只支持回归和分类(不支持聚类、排序、降维等)。
结语
本文中,我们学习了CoreML及应用它开发iPhone机器学习app。CoreML是一个较新的库,因此有自己的优点和缺点。有一个非常有用的优点是它在本地设备上运行,因此速度更快,保证数据隐私。但同时,它功能不全面,对数据科学家的需求考虑还不够周全。期待后续版本会改进。
如果你在某个步骤遇到困难,本文所有代码都在GitHub上。