[譯] iOS 11:機器學習人人有份

歐長坤發表於2019-03-02

WWDC 2017 使一件事情變得非常清楚,那就是:Apple 正在全力以赴地支援「裝置上的機器學習」了。

他們希望 App 的開發者們能夠儘可能的簡單的加入他們的行列中。

Apple 去年釋出了可以用於建立基本的卷積神經網的 Metal CNN 和 BNNS 框架。今年,Metal 得到了進一步擴充套件,增加了一個全新的計算機視覺框架,以及 Core ML:一個能夠輕鬆地將機器學習整合到 App 中的工具包。

Core ML framework
Core ML framework

在這片文章中,我將就 iOS 11 和 macOS 10.13 中這些新推出的機器學習的內容,分享我自己的一些想法和經驗。

Core ML

Core ML 在 WWDC 上獲得了極大的關注度,原因很簡單:大部分開發者希望能夠在他們的 App 中使用這個框架。

Core ML 的 API 非常簡單。你只能用它做這些事情:

  1. 載入一個訓練好的模型
  2. 做出預測
  3. 收益!!!

這看起來好像很有限,但實際上你一般只會在 App 中載入模型和做出預測這兩件事。

在 Core ML 之前,載入訓練好的模型是非常困難的 —— 實際上,我寫過一個框架來減輕這種痛苦。所以現在我對這一個簡單的兩步過程感到非常高興。

模型被包含在了一個 .mlmodel 的檔案中。這是一種新的開原始檔格式,用於描述模型中的 layer、輸入輸出、標籤,以及需要在資料上產生的任何預處理過程。它還包括了所有的學習引數(權重和偏置)。

使用模型所需的一切都在這一個檔案裡面了。

你只需要將 mlmodel 檔案放入你的專案中,Xcode 將會自動生成一個 Swift 或 Objective-C 的包裝類,使你能簡單的使用這個模型。

舉個例子,如果你把檔案 ResNet50.mlmodel 新增到你的 Xcode 專案中,那麼你就可以這麼寫來例項化這個模型:

let model = ResNet50()複製程式碼

然後做出預測:

let pixelBuffer: CVPixelBuffer = /* your image */if let prediction = try? model.prediction(image: pixelBuffer) {
  print(prediction.classLabel)
}複製程式碼

這差不多就是所有要寫的東西了。你不需要編寫任何程式碼來載入模型,或者將其輸出轉換成可以從 Swift 直接使用的內容 —— 這一切都將由 Core ML 和 Xcode 來處理。

注意: 要了解背後發生了什麼,可以在 Project Navigator 裡選擇 mlmodel 檔案,然後點選 Swift generated source 右邊的箭頭按鈕,就能夠檢視生成的幫助程式碼了。

Core ML 將決定自己到底是在 CPU 上執行還是 GPU 上執行。這使得它能夠充分的利用可以用的資源。Core ML 甚至可以將模型分割成僅在 GPU 上執行的部分(需要大量計算的任務)以及 CPU 上的其他部分(需要大量記憶體的任務)。

Core ML 使用 CPU 的能力對於我們開發者來說另一個很大的好處是:你可以從 iOS 模擬器執行它,從而執行那些對於 Metal 來說做不到,同時在單元測試中也不太好的任務。

Core ML 支援什麼模型?

上面的 ResNet50 例子展示的是一個影象分類器,但是 Core ML 可以處理幾種不同型別的模型,如:

  • 支援向量機 SVM
  • 諸如隨機森林和提升樹的決策樹整合
  • 線性迴歸和 logistic 迴歸
  • 前饋神經網、卷積神經網、遞迴神經網

所有這些模型都可以用於迴歸問題和分類問題。此外,你的模型可以包含這些典型的機器學習預處理操作,例如獨熱編碼(one-hot encoding)、特徵縮放(feature scaling)、缺失值處理等等。

Apple 提供了很多已經訓練好的模型可供下載,例如 Inception v3、ResNet50 和 VGG16 等,但你也可以使用 Core ML Tools 這個 Python 庫來轉換自己的模型。

目前,你可以轉換使用 Keras、Caffe、scikit-learn、XGBoost 和 libSVM 訓練的模型。轉換工具只會支援具體指定的版本,比如 Keras 支援 1.2.2 但不支援 2.0。辛運的是,該工具是開源的,所以毫無疑問它將來會支援更多的訓練工具包。

如果這些都不行,你還是可以隨時編寫自己的轉換器。mlmodel 檔案格式是開源且可以直接使用的(由 Apple 制定釋出的一種 protobuf 格式)

侷限

如果你想在你的 App 上馬上執行一個模型, Core ML 很不錯。然而使用這樣一個簡單的 API 一定會有一些限制。

  • 僅支援有監督學習的模型,無監督學習和增強學習都是不行的。(不過有一個「通用」的神經網路型別支援,因此你可以使用它)
  • 裝置上不能進行訓練。你需要使用離線工具包來進行訓練,然後將它們轉換到 Core ML 格式。
  • 如果 Core ML 不支援某種型別的 layer,那麼你就不能使用它。在這一點上,你不能使用自己的 kernel 來擴充套件 Core ML。在使用 TensorFlow 這樣的工具來構建通用計算圖模型時,mlmodel 檔案格式可能就不那麼靈活了。
  • Core ML 轉換工具只支援特定版本的數量有限的訓練工具。例如,如果你在 TensorFLow 中訓練了一個模型,則無法使用此工具,你必須編寫自己的轉換指令碼。正如我剛才提到的:如果你的 TensorFlow 模型具有一些 mlmodel 不支援的特性,那麼你就不能在 Core ML 上使用你的模型。
  • 你不能檢視中間層的輸出,只能獲得最後一層網路的預測值。
  • 我感覺下載模型更新會造成一些問題,如果你不想每次重新訓練模型的時候都重寫一個新版本的 App,那麼 Core ML 不適合你。
  • Core ML 對外遮蔽了它是執行在 CPU 上還是 GPU 上的細節 —— 這很方便 —— 但你必須相信它對你的 App 能做出正確的事情。即便你真的需要,你也不能強迫 Core ML 執行在 GPU 上。

如果你能夠忍受這些限制,那麼 Core ML 對你來說就是正確的選擇。

否則的話,如果你想要完全的控制權,那麼你必須使用 Metal Performance Shader 或 Accelerate 框架 —— 甚至一起使用 —— 來驅動你的模型了!

當然,真正的黑魔法不是 Core ML,而是你的模型。如果你連模型都沒有,Core ML 是沒有用的。而設計和訓練一個模型就是機器學習的難點所在……

一個快速示例程式

我寫了一個使用了 Core ML 的簡單的示例專案,和往常一樣,你可以在 GitHub 上找到原始碼

The demo app in action
The demo app in action

這個示例程式使用了 MobileNet 架構來分類圖片中的貓。

最初這個模型是用 Caffe 訓練得出的。我花了一點時間來搞清楚如何將它轉換到一個 mlmodel 檔案,但是一旦我有了這個轉換好的模型,便很容易整合到 App 中了(轉換指令碼包含在 GitHub 中)。

雖然這個 App 不是很有趣 —— 它只輸出了一張靜態圖片的前五個預測值 —— 但卻展示了使用 Core ML 是多麼的簡單。幾行程式碼就夠了。

注意: 示例程式在模擬器上工作正常,但是裝置上執行就會崩潰。繼續閱讀來看看為什麼會發生這種情況 ;-)

當然,我想知道發生了什麼事情。事實證明 mlmodel 實際上被編譯進應用程式 bundle 的 mlmodelc 資料夾中了。這個資料夾裡包含了一堆不同的檔案,一些二進位制檔案,一些 JSON檔案。所以你你可以看到 Core ML 是如何將 mlmodel 在實際部署到應用中之前進行轉換的。

例如,MobileNet Caffe 模型使用了批量歸一化(Batch Normalization)層,我驗證了這些轉換也存在於 mlmodel 檔案中。但是在編譯的 mlmodelc 中,這些批量歸一化 layer 似乎就被移除了。這是個好訊息:Core ML 優化了該模型。

儘管如此,它似乎可以更好的優化該模型的結構,因為 mlmodelc 仍然包含一些不必要的 scaling layer。

當然,我們還處在 iOS 11 beta 1 的版本,Core ML 可能還會改進。也就是說,在應用到 Core ML 之前,還是值得對模型進一步優化的 —— 例如,通過「folding」操作對 layer 進行批量歸一化(Batch Normalization) —— 但這是你必須對你的特性模型進行測量和比較的東西。

還有其他一些你必須檢查的:你的模型是否在 CPU 和 GPU 上執行相同。我提到 Core ML 將選擇是否在 CPU 上執行模型(使用 Accelerate 框架)或 GPU(使用 Metal )。事實證明,這兩個實現可能會有所不同 —— 所以你兩個都需要測試!

例如,MobileNet 使用所謂的「depthwise」卷積層。原始模型在 Caffe 中進行訓練,Caffe 通過使正常卷積的 groups 屬性等於輸出通道的數量來支援 depthwise 卷積。所得到的 MobileNet.mlmodel 檔案也一樣。這在 iOS 模擬器中工作正常,但它在裝置上就會崩潰!

發生這一切的原因是:模擬器使用的是 Accelerate 框架,但是該裝置上使用的卻是 Metal Performance Shaders。由於 Metal 對資料進行編碼方式的特殊性, MPSCNNConvolution 核心限制了:不能使 groups 數等於輸出通道的數量。噢嚯!

我向 Apple 提交了一個 bug,但是我想說的是:模型能在模擬器上執行正常並不意味著它在裝置上執行正常。一定要測試!

有多快?

我沒有辦法測試 Core ML 的速度,因為我的全新 10.5 寸 iPad Pro 下個星期才能到(呵呵)。

我感興趣的是我自己寫的 Forge 庫和 Core ML (考慮到我們都是一個早期的測試版)之間執行 MobileNets 之間的效能差異。

敬請關注!當我有資料可以分享時,我會更新這一節內容。

Vision

下一個要討論的事情就是全新的 Vision 框架。

你可能已經從它的名字中猜到了,Vision 可以讓你執行計算機視覺任務。在以前你可能會使用 OpenCV,但現在 iOS 有自己的 API 了。

Happy people with square faces
Happy people with square faces

Vision 可以執行的任務有以下幾種:

  • 在影象中尋找人臉。然後對每個臉給出一個矩形框。
  • 尋找面部的詳細特徵,比如眼睛和嘴巴的位置,頭部的形狀等等。
  • 尋找矩形形狀的影象,比如路標。
  • 追蹤視訊中移動的物件。
  • 確定地平線的角度。
  • 轉換兩個影象,使其內容對齊。這對於拼接照片非常有用。
  • 檢測包含文字的影象中的區域。
  • 檢測和識別條形碼。

Core Image 和 AVFoundation 已經可以實現其中的一些任務,但現在他們都整合在一個具有一致性 API 的框架內了。

如果你的應用程式需要執行這些計算機視覺任務之一,再也不用跑去自己實現或使用別人的庫了 - 只需使用 Vision 框架。你還可以將其與 Core Image 框架相結合,以獲得更多的影象處理能力。

更好的是:你可以使用 Vision 驅動 Core ML,這允許你使用這些計算機視覺技術作為神經網路的預處理步驟。例如,你可以使用 Vision 來檢測人臉的位置和大小,將視訊幀裁剪到該區域,然後在這部分的面部影象上執行神經網路。

事實上,任何時候當你結合影象或者視訊使用 Core ML 時,使用 Vision 都是合理的。原始的 Core ML 需要你確保輸入影象是模型所期望的格式。如果使用 Vision 框架來負責調整影象大小等,這會為你節省不少力氣。

使用 Vision 來驅動 Core ML 的程式碼長這個樣子:

// Core ML 的機器學習模型
let modelCoreML = ResNet50()複製程式碼
// 將 Core ML 連結到 Vision
let visionModel = try? VNCoreMLModel(for: modelCoreML.model)複製程式碼
let classificationRequest = VNCoreMLRequest(model: visionModel) {
  request, error iniflet observations = request.results as? [VNClassificationObservation] {
    /* 進行預測 */
  }
}

let handler = VNImageRequestHandler(cgImage: yourImage)
try? handler.perform([classificationRequest])複製程式碼

請注意,VNImageRequestHandler 接受一個請求物件陣列,允許你將多個計算機視覺任務連結在一起,如下所示:

try? handler.perform([faceDetectionRequest, classificationRequest])複製程式碼

Vision 使計算機視覺變得非常容易使用。 但對我們機器學習人員很酷的事情是,你可以將這些計算機視覺任務的輸出輸入到你的 Core ML 模型中。 結合 Core Image 的力量,批量影象處理就跟玩兒一樣!

Metal Performance Shaders

我最後一個想要討論的話題就是 Metal —— Apple 的 GPU 程式設計 API。

我今年為客戶提供的很多工作涉及到使用 Metal Performance Shaders (MPS) 來構建神經網路,並對其進行優化,從而獲得最佳效能。但是 iOS 10 只提供了幾個用於建立神經網路的基本 kernel。通常需要編寫自定義的 kernel 來彌補這個缺陷。

所以我很開心使用 iOS 11,可用的 kernel 已經增長了許多,更好的是:我們現在有一個用於構建圖的 API 了!

Metal Performance Shaders
Metal Performance Shaders

注意: 為什麼要使用 MPS 而不是 Core ML?好問題!最大的原因是當 Core ML 不支援你想要做的事情時,或者當你想要完全的控制權並獲得最大執行速度時。

MPS 中對於機器學習來說的最大的變化是:

遞迴神經網路。你現在可以建立 RNN,LSTM,GRU 和 MGU 層了。這些工作在 MPSImage 物件的序列上,但也適用於 MPSMatrix 物件的序列。這很有趣,因為所有其他 MPS layer 僅處理影象 —— 但顯然,當你使用文字或其他非影象資料時,這不是很方便。

更多資料型別。以前的權重應該是 32 位浮點數,但現在可以是 16 位浮點數(半精度),8 位整數,甚至是 2 進位制數。卷積和 fully-connected 的 layer 可以用 2 進位制權重和 2 進位制化輸入來完成。

更多的層。到目前為止,我們不得不採用普通的常規卷積、最大池化和平均池化,但是在 iOS 11 MPS 中,你可以進行擴張卷積(Dilated Convolution)、子畫素卷積(Subpixel Convolution)、轉置卷積(Transposed Convolution)、上取樣(Upsampling)和重取樣(Resampling)、L2 範數池化(L2-norm pooling)、擴張最大池化(dilated max pooling),還有一些新的啟用函式。 MPS 還沒有所有的 Keras 或 Caffe layer 型別,但差距正在縮小...

更方便。使用 MPSImages 總是有點奇怪,因為 Metal 每次以 4 個通道的片段組織資料(因為影象由 MTLTexture 物件支援)。但是現在,MPSImage 有用於讀取和寫入資料的方法,這些資料不會讓你感到困惑。

MPSCNNConvolutionDescriptor 還有一個新方法,可以讓你在 layer 上設定批量歸一化引數。這意味著你不再需要將批量歸一化到卷積層中,而 MPS 會為你處理這些事情。非常方便!

效能改進。現有的核心變得更快。這總是好訊息。 ?

圖 API。這是我最關心的訊息。手動建立所有 layer 和(臨時)影象總是令人討厭的。現在你可以描述一個圖,就像你在Keras 中一樣。 MPS 將自動計算出影象需要多大,如何處理填充,如何設定 MPS 核心的 offset 等等。甚至可以通過融合不同的 layer 來優化整個圖。

看起來所有的 MPS 核心都可以使用 NSSecureCoding 進行序列化,這意味著你可以將圖儲存到檔案中,然後將其還原。並且使用這個圖來推斷現在只是一個單一的方法呼叫。它不像 Core ML 那麼簡單,但使用 MPS 絕對比以前好用得多。

有一件事情我目前還不太清楚,那就是我不知道你是否可以編寫自己的 kernel 並在這個圖中使用。在我客戶的工作中,我發現通常需要使用 Metel Shading 語言編寫的自定義著色器來進行預處理步驟。據我所知,似乎沒有一個「MPSNNCustomKernelNode」類。這還要再多研究一下!

結論:用於機器學習的 Metal Performance Shaders 已經在 iOS 11 中變得更加強大,但是大多數開發人員應該轉而使用 Core ML(對於那些使用MPS的來說)。

注意:新的圖 API 使我的 Forge 庫基本上過時了,除非你希望在 App 中繼續支援 iOS 10。我將盡快將示例應用移植到新的圖 API 上,然後將寫一個更詳細的部落格文章。

雜項

還有一些其他的更新:

Accelerate 框架: 似乎 Accelerate 框架中的 BNNS 並沒有獲得太多功能上的更新。它終於有了 Softmax 層,但 MPS 卻沒有新的 layer 型別。也許無關緊要:使用 CPU 進行深層神經網路可能不是一個好主意。也就是說,我喜歡 Accelerate,它有很多好玩的東西。而今年,它確實獲得了對稀疏矩陣的更多支援,很棒。

自然語言處理: Core ML不僅僅只能處理影象,它還可以處理大量不同型別的資料,包括文字。 使用的 API NSLinguisticTagger 類已經存在了一段時間,但是與 iOS 11 相比變得更加有效了。NSLinguisticTagger 現在已經能進行語言鑑別,詞法分析,詞性標註,詞幹提取和命名實體識別。

我沒有什麼 NLP 的經驗,所以我沒辦法比較它與其他 NLP 框架的區別,但NSLinguisticTagger 看起來相當強大。 如果要將 NLP 新增到 App 中,此 API 似乎是一個好的起點。

都是好訊息嗎?

Apple 向我們開發者提供所有的這些新工具都非常的好,但是大多數 Apple API 都有一些很重要的問題:

  1. 閉源
  2. 有侷限
  3. 只有在新 OS 釋出時候才會更新

這三個東西加在一起意味著蘋果的 API 總會落後於其他工具。如果 Keras 增加了一個很炫酷的新的 layer 型別,那麼在 Apple 更新其框架和作業系統之前,你都沒辦法將它和 Core ML 一起使用了。

如果某些 API 得到的計算結果並不是你想要的,你沒辦法簡單的進去看看到底是 Core ML 的問題還是模型的問題,再去修復它 —— 你必須繞開 Core ML 來解決這個問題(並不總是可能的);要麼就只能等到下一個 OS 釋出了(需要你所有的使用者進行升級)。

當然我不希望 Apple 放棄他們的祕密武器,但是就像其他大多數機器學習工具開源一樣,為什麼不讓 Core ML 也開源呢? ?

我知道這對於 Apple 來說不可能馬上發生,但當你決定在 App 中使用機器學習時,要記住上面的這些內容。

Matthijs Hollemans 於 2017 年 6 月 11 日

我希望這篇文章對你有所幫助!歡迎通過 Twitter @mhollemans 或 Email matt@machinethink.net 聯絡我。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章