此文翻譯自 Core ML and Vision: Machine Learning in iOS 11 Tutorial
注意:此教程需要 Xcode 9 Beta1 或更新的版本、Swift 4 以及 iOS 11.
機器學習正在肆虐橫行。很多人都聽說過,但很少有人知道這是什么。
這篇《iOS 機器學習教程》會為你介紹 Core ML 和 Vision,iOS 11 中推出的兩個全新框架。
具體來說,你會學習如何借助 Places205-GoogLeNet 模型,使用新的 API 對圖像的場景進行分類。
開始
下載起始項目。它已包含了用于顯示圖片的用戶界面,并允許用戶從照片庫中選擇另一張圖片。這樣你就可以專注于實現 App 的機器學習和視覺方面。
構建并運行該項目;可以看到一張城市夜景圖,以及一個按鈕:
從“照片” App 的照片庫中選擇另一張圖片。此起始項目的 Info.plist 已經有 Privacy – Photo Library Usage Description,所以你會被提示允許使用。
圖片和按鈕之間的空隙有一個 label,將會在此顯示模型對圖片場景的分類。
iOS 機器學習
機器學習是一種人工智能,計算機會“學習”而不是被明確編程。不用編寫算法,機器學習工具通過在大量數據中尋找模式,使計算機能夠開發和優化算法。
深度學習
自20世紀50年代以來,AI 研究人員開發了許多機器學習方法。蘋果的 Core ML 框架支持神經網絡、樹組合、支持向量機、廣義線性模型、特征工程和流水線模型。但是,神經網絡最近已經取得了很多極為神奇的成功,開始于 2012 年谷歌使用 YouTube 視頻訓練 AI 來識別貓和人。僅僅五年后,谷歌正在贊助一場確定 5000 種植物和動物的比賽。像 Siri 和 Alexa 這樣的 App 也存在它們自己的神經網絡。
神經網絡嘗試用節點層來模擬人腦流程,并將節點層用不同的方式連接在一起。每增加一層都需要增加大量計算能力:Inception v3,一個對象識別模型,有48層以及大約2000萬個參數。但計算基本上都是矩陣乘法,GPU 來處理會非常有效。GPU 成本的下降使我們能夠創建多層深度神經網絡,此為深度學習。
神經網絡需要大量的訓練數據,這些訓練數據理想化地代表了全部可能性。用戶生成的數據爆炸性地產生也促成了機器學習的復興。
訓練模型意味著給神經網絡提供訓練數據,并讓它計算公式,此公式組合輸入參數以產生輸出。訓練是離線的,通常在具有多個 GPU 的機器上。
要使用這個模型,就給它新的輸入,它就會計算輸出:這叫做推論。推論仍然需要大量計算,以從新的輸入計算輸出。因為有了 Metal 這樣的框架,現在可以在手持設備上進行這些計算。
在本教程的結尾你會發現,深度學習遠非完美。真的很難建立具有代表性的訓練數據,很容易就會過度訓練模型,以至于它會過度重視一些古怪的特征。
蘋果提供了什么?
蘋果在 iOS 5 里引入了 NSLinguisticTagger
來分析自然語言。iOS 8 出了 Metal,提供了對設備 GPU 的底層訪問。
去年,蘋果在 Accelerate 框架添加了 Basic Neural Network Subroutines (BNNS),使開發者可以構建用于推理(不是訓練)的神經網絡。
今年,蘋果給了我們 Core ML 和 Vision!
- Core ML 讓我們更容易在 App 中使用訓練過的模型。
- Vision 讓我們輕松訪問蘋果的模型,用于面部檢測、面部特征點、文字、矩形、條形碼和物體。
你還可以在 Vision 模型中包裝任意的圖像分析 Core ML 模型,我們在這篇教程中就干這個。由于這兩個框架是基于 Metal 構建的,它們能在設備上高效運行,所以不需要把用戶的數據發送到服務器。
將 Core ML 模型集成到你的 App
本教程使用 Places205-GoogLeNet 模型,可以從蘋果的“機器學習”頁面下載。往下滑找到 Working with Models,下載第一個。還在這個頁面,注意一下其它三個模型,它們都用于在圖片中檢測物體——樹、動物、人等等。
注意:如果你有一個訓練過的模型,并且是使用受支持的機器學習工具訓練的,例如 Caffe、Keras 或 scikit-learn,Converting Trained Models to Core ML 介紹了如何將其轉換為 Core ML 格式。
為你的項目添加模型
下載 GoogLeNetPlaces.mlmodel 后,把它從 Finder 拖到項目導航器的 Resources 組里:
選擇該文件,然后等一會兒。Xcode 生成了模型類后會顯示一個箭頭:
點擊箭頭,查看生成的類:
Xcode 已生成了輸入和輸出類以及主類 GoogLeNetPlaces
,主類有一個 model
屬性和兩個 prediction
方法。
GoogLeNetPlacesInput
有一個 CVPixelBuffer
類型的 sceneImage
屬性。哭了,這些都是什么鬼?!不要害怕,Vision 框架會負責把我們熟悉的圖片格式轉換成正確的輸入類型。:]
Vision 框架還會把 GoogLeNetPlacesOutput
屬性轉換為自己的 results
類型,并管理對 prediction
方法的調用,所以在所有生成的代碼中,我們只會使用 model
屬性。
在 Vision Model 中包裝 Core ML Model
終于,要開始寫代碼了!打開 ViewController.swift,并在 import UIKit
下面 import 兩個框架:
import CoreML
import Vision
下一步,在 IBActions
擴展下方添加如下擴展:
// MARK: - Methods
extension ViewController {
func detectScene(image: CIImage) {
answerLabel.text = "detecting scene..."
// 從生成的類中加載 ML 模型
guard let model = try? VNCoreMLModel(for: GoogLeNetPlaces().model) else {
fatalError("can't load Places ML model")
}
}
}
我們上面的代碼做了這些事:
首先,給用戶顯示一條消息,讓他們知道正在發生什么事情。
GoogLeNetPlaces 的指定初始化方法會拋出一個 error,所以創建時必須用 try
。
VNCoreMLModel
只是用于 Vision 請求的 Core ML 模型的容器。
標準的 Vision 工作流程是創建模型,創建一或多個請求,然后創建并運行請求處理程序。我們剛剛已經創建了模型,所以下一步是創建請求。
在 detectScene(image:)
的末尾添加如下幾行:
// 創建一個帶有 completion handler 的 Vision 請求
let request = VNCoreMLRequest(model: model) { [weak self] request, error in
guard let results = request.results as? [VNClassificationObservation],
let topResult = results.first else {
fatalError("unexpected result type from VNCoreMLRequest")
}
// 在主線程上更新 UI
let article = (self?.vowels.contains(topResult.identifier.first!))! ? "an" : "a"
DispatchQueue.main.async { [weak self] in
self?.answerLabel.text = "\(Int(topResult.confidence * 100))% it's \(article) \(topResult.identifier)"
}
}
VNCoreMLRequest
是一個圖像分析請求,它使用 Core ML 模型來完成工作。它的 completion handler 接收 request
和 error
對象。
檢查 request.results
是否是 VNClassificationObservation
對象數組,當 Core ML 模型是分類器,而不是預測器或圖像處理器時,Vision 框架就會返回這個。而 GoogLeNetPlaces
是一個分類器,因為它僅預測一個特征:圖像的場景分類。
VNClassificationObservation
有兩個屬性:identifier
- 一個 String
,以及 confidence
- 介于0和1之間的數字,這個數字是是分類正確的概率。使用對象檢測模型時,你可能只會看到那些 confidence 大于某個閾值的對象,例如 30% 的閾值。
然后取第一個結果,它會具有最高的 confidence 值,然后根據 identifier 的首字母把不定冠詞設置為“a”或“an”。最后,dispatch 回到主線程來更新 label。你很快會明白分類工作為什么不在主線程,因為它會很慢。
現在,做第三步:創建并運行請求處理程序。
把下面幾行添加到 detectScene(image:)
的末尾:
// 在主線程上運行 Core ML GoogLeNetPlaces 分類器
let handler = VNImageRequestHandler(ciImage: image)
DispatchQueue.global(qos: .userInteractive).async {
do {
try handler.perform([request])
} catch {
print(error)
}
}?
VNImageRequestHandler 是標準的 Vision 框架請求處理程序;不特定于 Core ML 模型。給它 image 作為 detectScene(image:)
的參數。然后調用它的 perform
方法來運行處理程序,傳入請求數組。在這個例子里,我們只有一個請求。
perform
方法會拋出 error,所以用 try-catch 將其包住。
使用模型來分類場景
哇,剛剛寫了好多代碼!但現在只需要在兩個地方調用 detectScene(image:)
就好了。
把下面幾行添加到 viewDidLoad()
的末端和 imagePickerController(_:didFinishPickingMediaWithInfo:)
的末端:
guard let ciImage = CIImage(image: image) else {
fatalError("couldn't convert UIImage to CIImage")
}
detectScene(image: ciImage)
現在構建并運行。不需要多久就可以看見分類:
哈哈,是的,圖片里有 skyscrapers(摩天大樓)。還有一列火車。
點擊按鈕,選擇照片庫里的第一張圖片:一些樹葉上太陽光斑的特寫:
啊哈哈哈哈哈,瞇起眼睛,也許可以想象尼莫或多莉正在里面游泳?但至少你知道應該用 “a” 還是 “an”。;]
看一眼蘋果的 Core ML 示例 App
本教程的項目和 WWDC 2017 Session 506 Vision Framework: Building on Core ML 的示例項目很相似。Vision + ML Example App 使用 MNIST 分類器,可以識別手寫數字——對郵政分類自動化非常有幫助。它還使用原生 Vision 框架方法 VNDetectRectanglesRequest
,還包括 Core Image 的代碼來矯正矩形檢測的透視。
還可以從 Core ML 文檔頁面下載另一個示例項目。MarsHabitatPricePredictor 模型的輸入只是數字,因此代碼直接使用生成的 MarsHabitatPricer
方法和屬性,而不是將模型包裝在 Vision 模型中。每次都改一下參數,很容易看出模型只是一個線性回歸:
137 * solarPanels + 653.50 * greenHouses + 5854 * acres
下一步?
可以從這里下載教程的完整項目。如果模型顯示為缺失,將其替換為你下載的那個。
你現在已經有能力將現有的模型整合到你的 App 中。這里有一些資源可以更詳細地介紹:
- 蘋果的 Core ML Framework 文檔
- WWDC 2017 Session 703 介紹 Core ML
- WWDC 2017 Session 710 Core ML in depth
2016 年的:
- WWDC 2016 Session 605 What’s New in Metal, Part 2: demos show how fast the app does the Inception model classification calculations, thanks to Metal.
- 蘋果的 Basic Neural Network Subroutines 文檔
想構建自己的模型?恐怕這超出了本教程的范圍(以及我的專業知識)。但這些資源可能會幫你上手:
- RWDevCon 2017 Session 3 Machine Learning in iOS: Alexis Gallagher 做了一項絕對精彩的工作,指導你一系列流程,為神經網絡收集訓練數據(你微笑或皺眉的視頻),訓練,然后檢查它是否有效。他的結論:“不需要是數學家或大公司也可以建立有效的模型。”
-
Quartz article on Apple’s AI research paper: Dave Gershgorn’s 有關 AI 的文章都很清晰和翔實。此文做了一項杰出的工作,總結了蘋果的第一篇 AI 研究論文:研究人員使用基于
真實
圖像訓練的神經網絡來優化圖像合成
,從而有效地產生了大量高質量的新訓練數據,而沒有個人數據隱私問題。
最后,我從 Andreessen Horowitz 的 Frank Chen 那里真的學習了很多 AI 的簡史:AI and Deep Learning a16z podcast。
希望本教程對你有所幫助。隨意在下方加入討論!