本文是 WWDC 2018 Session 708 和 Session 709 的讀后感,其視頻及配套 PDF 文稿鏈接如下:
What is New in Core ML, Part 1
What is New in Core ML, Part 2
本文會(huì)首先回顧 Core ML 的基本背景知識(shí),其后著重介紹 Core ML 在應(yīng)用上和工具上的更新。
Core ML 回顧
Core ML 是蘋果在2017年推出的機(jī)器學(xué)習(xí)框架。主要支持圖像分類和文本信息處理兩大功能。其基本流程是獲取模型、導(dǎo)入并生成接口、使用接口編程3個(gè)步驟。我們來詳細(xì)分析每一步:
獲取模型。2017年時(shí)主要有兩種方式:從官網(wǎng)下載和轉(zhuǎn)化第三方框架生成的模型為 Core ML 模型。為了方便轉(zhuǎn)化,蘋果推出了 Python 編寫的 Core ML Tools。2018年又推出了原生的 Create ML 框架來直接從數(shù)據(jù)生成 Core ML 模型。Core ML Tools 的優(yōu)點(diǎn)在于其語法簡(jiǎn)單直接,缺點(diǎn)在于支持的第三方框架少、生成的模型尺寸過大且不能定制化。
導(dǎo)入并生成接口。這里直接拖拽模型進(jìn)入 Xcode,Xcode 會(huì)自動(dòng)生成相應(yīng)的機(jī)器學(xué)習(xí)模型接口。無需任何手動(dòng)或其他操作,十分方便友好。美中不足的是生成的接口是固定的、無法增加定制化接口。
使用編程接口。根據(jù)生成的 API 進(jìn)行編程。2017年 Core ML 模型只支持對(duì)單一對(duì)象進(jìn)行預(yù)測(cè),無法批量預(yù)測(cè),運(yùn)行效率比較低下。
可以說2017年推出的 Core ML 框架十分易用,但其功能也十分簡(jiǎn)陋。開發(fā)者們只能在一開始模型生成上做定制化操作,且很有可能要依賴第三方框架。之后只能使用 Core ML 生成的固定模型進(jìn)行編程,十分局限:無法優(yōu)化預(yù)測(cè)效率、無法精簡(jiǎn)尺寸、無法增加新的層級(jí)、無法自定義模型。
針對(duì)這些缺陷,蘋果在今年的 Core ML 2.0 上做出了如下改進(jìn)——更小。更快。高度定制化。

新功能
Core ML 這次的新功能主要在于模型的接口生成新增了一個(gè)可以批量預(yù)測(cè)的 API。下面代碼展示了原來的 API 和 新的 API:
// 預(yù)測(cè)單一輸入
public func prediction(from: MLFeatureProvider,
options: MLPredictionOptions) throws -> MLFeatureProvider
// 預(yù)測(cè)多個(gè)輸入
public func predictions(from: MLBatchProvider,
options: MLPredictionOptions) throws -> MLBatchProvider
以前需要用 for 循環(huán)完成的操作現(xiàn)在可以用一個(gè)方法完成。不進(jìn)如此,新的批量預(yù)測(cè)方法相對(duì)于 for 循環(huán)嵌套單一預(yù)測(cè)的實(shí)現(xiàn),還用了 batch 進(jìn)行優(yōu)化。
原來的 for 循環(huán)單一預(yù)測(cè)方法需要完整地讀入每一個(gè)數(shù)據(jù),將其預(yù)處理后發(fā)送給 GPU,GPU 計(jì)算完畢后再把結(jié)果從 GPU 中取出并在程序中輸出結(jié)果。新的批量預(yù)測(cè)方法則是消除了預(yù)處理和取出的操作,將所有數(shù)據(jù)一次性發(fā)給 GPU,利用 GPU Pipeline 將其逐個(gè)計(jì)算的同時(shí)依次取出結(jié)果。另外因?yàn)?GPU 一直在運(yùn)算狀態(tài),GPU 可以對(duì)計(jì)算進(jìn)行統(tǒng)一優(yōu)化,使得相似數(shù)據(jù)的處理越來越快。這樣整體的性能就要快上不少,具體邏輯如下圖所示:

蘋果當(dāng)場(chǎng)展示了兩種方法之間的效率之差:處理40張圖片,新的批量預(yù)測(cè)方法比 for 循環(huán)的單一預(yù)測(cè)方法比要快將近5秒鐘,效率上幾乎提高了一倍。
除此之外,Core ML Tools 增加了第三方機(jī)器學(xué)習(xí)框架數(shù)量,從原來的6個(gè)增加到了11個(gè),包括了最知名的 TensorFlow、IBM Watson、MXNet,數(shù)量和質(zhì)量都有大幅提升。

性能優(yōu)化
性能優(yōu)化是 Core ML 的重頭戲,蘋果宣稱 Core ML 2 的速度提高了30%。我們來看看蘋果做了哪些工作:
量化權(quán)重。Core ML 的模型可以根據(jù)需求對(duì)權(quán)重進(jìn)行量化。權(quán)重越低,模型尺寸越小,運(yùn)轉(zhuǎn)速度越快,占用內(nèi)存資源也就越少,但是處理效果也就越差。
多尺寸支持。針對(duì)圖片處理,Core ML 現(xiàn)在只需一個(gè)模型,就能處理不同分辨率的圖片。相對(duì)于之前單一分辨率圖片的模型,該模型更加靈活,且因?yàn)樵诘讓哟罅抗蚕泶a,使得模型的體積也遠(yuǎn)遠(yuǎn)小于原來多個(gè)單獨(dú)模型體積之和。
我們來重點(diǎn)看下量化權(quán)重。2017年時(shí) Core ML 的所有模型權(quán)重都是32位,也就是說每個(gè)模型可以識(shí)別 2^32 個(gè)不同的特征值。這雖然帶來了非常之高的準(zhǔn)確度,但同時(shí)也使得 Core ML 模型非常之大(20+M)。對(duì)于 App 開發(fā)來說,尺寸大小是非常值得注意的因素。借鑒 App Thinning 的思路,蘋果針對(duì) Core ML 的模型大小進(jìn)行了優(yōu)化。現(xiàn)在開發(fā)者可以使用 Core ML Tools 對(duì)原來32位權(quán)重的模型進(jìn)行量化,根據(jù)需要,蘋果支持16位、8位、4位等權(quán)重。權(quán)重越低。模型尺寸越小,運(yùn)轉(zhuǎn)速度越快,但是處理效果也就越差。所以還是要根據(jù)實(shí)際需求進(jìn)行選擇,下圖中我們可以看到不同模型尺寸和處理效果的對(duì)比。

在權(quán)重量化上我們可以針對(duì)需求做出最小體積的模型;同時(shí)針對(duì)多尺寸圖片我們可以合并多個(gè)類似功能的模型;同時(shí) Core ML Tools 又提供了自由定制權(quán)重的 API。在多重措施的優(yōu)化之下,Core ML 的模型可以最大限度的縮小尺寸,從而帶來更合適的加載和運(yùn)算效率。
定制化
蘋果在定制化方面推出了兩種方案:定制化神經(jīng)網(wǎng)絡(luò)層和定制化模型。我們先來看定制化神經(jīng)網(wǎng)絡(luò)層。
很多 Core ML 模型的內(nèi)部實(shí)現(xiàn)是多層神經(jīng)網(wǎng)絡(luò),每一層接受上一層的輸入,然后做出相應(yīng)處理再將結(jié)果輸出給下一層。例如,識(shí)別照片中動(dòng)物是馬的過程如下圖所示:

這個(gè)神經(jīng)網(wǎng)絡(luò)每一層都是固定的、由 Core ML 框架自動(dòng)生成并優(yōu)化,我們不能做任何操作。這使得模型的功能大大受到局限:試想我們?nèi)绻谏鲜瞿P蜕梢粋€(gè)新模型,使得該模型不僅能識(shí)別馬,還能識(shí)別鳥、魚、貓、狗等各種動(dòng)物,最簡(jiǎn)單的做法就是將上述模型中判別動(dòng)物是馬的層級(jí)給替換掉。Core ML 2 現(xiàn)在提供了這種功能,具體操作步驟是:
- 獲取生成含有特定的層級(jí)的模型。一般獲取方法是依靠第三方神經(jīng)網(wǎng)絡(luò)庫(kù),比如 Keras。
- 用 Core ML Tools 將含有特定層級(jí)的模型轉(zhuǎn)化成對(duì)應(yīng)的 Core ML 模型。這其中我們要自定義特殊層轉(zhuǎn)化方法。具體代碼如下:
# 用 keras 神經(jīng)網(wǎng)絡(luò)庫(kù)生成模型,其中特殊層為 GridSampler
model = keras.model.load_model('spatial_transformer_MNIST.h5', custom_object: {'GridSampler': GridSampler})
# 自定義 Core ML 模型中對(duì)應(yīng)特殊層 GridSampler 的轉(zhuǎn)化方法
def convert_grid_sampler(keras_layer):
params = NerualNetwork_pb2.customLayerParams()
# 定義名稱和描述
params.className = 'AAPLGridSampler'
params.description = 'Custom grid sampler layer for the spatial transformer network'
# 定義層級(jí)參數(shù),這里我們只要處理圖片的長(zhǎng)和寬
params.parameters["output_height"].intValue = keras_layer.output_size[0]
params.parameters["output_width"].intValue = keras_layer.output_size[1]
return params
# 用 Core ML Tools 將 Keras 模型轉(zhuǎn)化,其中特定層 GridSampler 的轉(zhuǎn)化方法定義為 convert_grid_sampler
coreml_model = coremltools.converters.keras.convert(model, custom_conversion_functions = {'GridSampler': convert_grid_sampler})
- 將 Core ML 模型導(dǎo)入 Xcode 中,自定義特殊層的接口。其對(duì)應(yīng)類務(wù)必實(shí)現(xiàn) MLCustomLayer 協(xié)議,它是自定義神經(jīng)網(wǎng)絡(luò)層的行為協(xié)議,每個(gè)方法的具體解釋可以參照蘋果的官方文檔:MLCustomLayer。
public protocol MLCustomLayer {
public init(parameters: [String : Any]) throws
public func setWeightData(_ weights: [Data]) throws
public func outputShapes(forInputShapes: [[NSNumber]]) throws -> [[NSNumber]]
public func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) throws
}
同時(shí),上文中提到的GridSampler的具體實(shí)現(xiàn)如下圖:

當(dāng)然并不是所有模型的實(shí)現(xiàn)都是神經(jīng)網(wǎng)絡(luò)。所以蘋果還推出了定制化模型。實(shí)現(xiàn)一個(gè)定制化模型的方法十分簡(jiǎn)單,就是實(shí)現(xiàn) MLCustomModel協(xié)議:
public protocol MLCustomModel {
public init(modelDescription: MLModelDescription, parameters: [String : Any]) throws
public func prediction(from: MLFeatureProvider,
options: MLPredictionOptions) throws -> MLFeatureProvider
optional public func predictions(from: MLBatchProvider,
options: MLPredictionOptions) throws -> MLBatchProvider
}
其具體說明亦可以參考蘋果官方文檔 。
總結(jié)
Core ML 2 在2017年的基礎(chǔ)上增加了新功能,同時(shí)對(duì)模型大小和運(yùn)轉(zhuǎn)效率進(jìn)行了相應(yīng)優(yōu)化。其配套工具 Core ML Tools 也增加了可支持的機(jī)器學(xué)習(xí)框架,同時(shí)開發(fā)者可以借助工具自定義神經(jīng)網(wǎng)絡(luò)層和 Core ML 模型。除此之外,蘋果推出的 Create ML 也極大地解決了模型獲取方面的局限。目前 Core ML 已經(jīng)深度應(yīng)用于 Siri、Photos、QuickType 等原生應(yīng)用上,采用 Core ML 的第三方應(yīng)用也多達(dá)182個(gè),相信在不久的將來,Core ML 會(huì)成為所有主流 App 的標(biāo)配。