1. 程式人生 > >iOS 11: CORE ML—淺析

iOS 11: CORE ML—淺析

作者:liujizhou

導語:在剛剛過去的WWDC上,蘋果釋出了Core ML這個機器學習框架。現在,開發者可以輕鬆的使用Core ML把機器學習功能整合到自己的應用裡,讓應用變得更加智慧,給使用者更牛逼的體驗。

蘋果在 iOS 5 裡引入了 NSLinguisticTagger 來分析自然語言。iOS 8 出了 Metal,提供了對裝置 GPU 的底層訪問。去年,蘋果在 Accelerate 框架添加了 Basic Neural Network Subroutines (BNNS),使開發者可以構建用於推理(不是訓練)的神經網路。

今年,蘋果給了我們 Core ML 和 Vision,讓iOS開發者在人工智慧上面更上一步臺階。

  • Core ML 讓我們更容易在 App 中使用訓練過的模型。
  • Vision 讓我們輕鬆訪問蘋果的模型,用於面部檢測、面部特徵點、文字、矩形、條形碼和物體。

你還可以在 Vision 模型中包裝任意的影象分析 Core ML 模型。由於這兩個框架是基於 Metal 構建的,它們能在裝置上高效執行,所以不需要把使用者的資料傳送到伺服器。

一、CORE ML是什麼?

相信很多人都聽說過機器學習,除了專業人士,應該很少有人去研究機器學習裡面的具體實現,CORE ML的出現,大大降低了iOS開發人員進入這一領域的門檻,能以最低成本開發出更加智慧的產品。

機器學習的一個重要環節就是利用海量的資料去訓練特定的模型,然後在遇到新資料的時候能夠準確預測出結果。比如,事先通過大量的物體特徵訓練一個模型,當把一個新物體輸入該模型,模型能夠準確預測出物體所屬的物種和類別;學習大量圍棋對局後,面對一個陌生的棋局,知道在哪下棋贏的概率更高。

在對機器進行訓練的時候,訓練完成後,會生成一個關於這個特定問題的資料模型,對模型輸入關於這個特定問題的新資料,模型會返回一個預測結果。Core ML實際做的事情是使用事先訓練好的模型(trained model),在Native利用MLNeuralNetworkEngine等相關模組進行預測,最終返回結果,這種在本地進行預測的方式可以不依賴網路,也可以降低處理時間。

應用和Core ML的互動流程大體如圖所示:

從圖上可以看出,真正智慧的部分其實是這個事先訓練好的模型(trained model),這個模型決定了最終判斷的結果。蘋果提供了一些轉化好的Core ML格式的模型,也可以通過蘋果提供的工具把自己在別的常用機器學習工具生成的模型轉化為Core ML格式的模型,這個工具當前頁只是支援一些常用的格式模型的轉換,如果需要轉換比較特定的格式模型,需要參考這個工具的程式碼,把你特定的模型資料轉換為蘋果規定的模型資料。

蘋果的 Core ML 框架現在已經支援前饋神經網、卷積神經網、遞迴神經網、諸如隨機森林和提升樹的決策樹整合、支援向量機、線性迴歸和 logistic 迴歸、特徵工程和流水線模型。

二、CORE ML涉及到的相關技術

Core ML是機器學習的一個基礎框架,Vision、GameplayKit都有利用Core ML做相應的處理。為了提升計算效能,蘋果充分利用了硬體的特性,最大限度優化了Core ML的效能,減少記憶體佔用和功耗。

1、Metal

Metal 是針對 iPhone 和 iPad 中 GPU 程式設計的高度優化的框架,Metal 與 OpenGL ES 相比最大的好處是顯著降低了消耗。 OpenGL 在建立緩衝區和紋理的過程中都會複製一份以避免 GPU 在使用資料的時候發生異常,而這些複製操作是非常耗時的。 為了提升效率和效能Metal在安全和效率方面選擇了後者,Metal 並不複製資源,使用Metal程式設計需要開發者自己來保證資料安全,開發者需要負責在 CPU 和 GPU 之間同步訪問。使用 Metal 時仍然有些這方面的問題需要注意。

Metal 的另外一個好處是其預估 GPU 狀態來避免多餘的驗證和編譯。在 OpenGL 中,你需要依次設定 GPU 的狀態,在每個繪製指令 (draw call) 之前需要驗證新的狀態。最壞的情況是 OpenGL 需要再次重新編譯著色器 (shader) 以反映新的狀態。 Metal 選擇了另一種方法,在渲染引擎初始化過程中,一組狀態被烘焙 (bake) 至預估渲染的路徑 (pass) 中。多個不同資源可以共同使用該渲染路徑物件,但其它的狀態是恆定的。Metal 中一個渲染路徑無需更進一步的驗證,使 API 的消耗降到最低,從而大大增加每幀的繪製指令的數量。

2、神經網路

深度學習(Deep Learning,簡稱DL)當前是相當火熱,任何一個方向和應用,都希望能夠運用到深度學習,戴上智慧的皇冠,不只是網際網路、人工智慧,生活中的各大領域都能反映出深度學習引領的巨大變革。要學習深度學習,首先需要先弄清楚人工神經網路(Artificial Neural Networks,簡稱ANN),人工神經網路的設計靈感完全來源於生物學上的神經元的資訊傳遞機制,神經網路已經發展成為一類多學科交叉的學科領域,它也隨著深度學習取得的進展受到重視和推崇。

卷積神經網路(Convolutional Neural Networks,簡稱 CNNs 或者 ConvNets)是人工神經網路的一種,已成為當前語音分析和影象識別領域的研究熱點。它的權值共享網路結構使之更類似於生物神經網路,降低了網路模型的複雜度,減少了權值的數量。優點在網路的輸入是多維影象時表現的更為明顯,使影象可以直接作為網路的輸入,避免了傳統識別演算法中複雜的特徵提取和資料重建過程。CNNs是深層神經網路領域的主力。它們已經學會對影象進行分類,對影象的識別準確率已經超過了人類。

3、Metal Performance Shaders

Metal Performance Shader是apple推出的一套通過Metal來在iOS上實現深度學習的工具,它主要封裝了MPSImage來儲存資料管理記憶體,實現了Convolution、Pooling、Fullconnetcion、ReLU等常用的卷積神經網路中的Layer。

如果自己的學習模型Core ML不支援,或者想要完全控制各個Layer的輸入和輸出,則必須使用MPS來完成,在服務端訓練好的模型引數需要進行轉換才能被MPS使用。一般的CNN網路包含可訓練引數的Layer基本上只有Convolution、Fullconnetcion、Normalization這三種layer。也就是說只要把這三種層的引數拿出來轉化為MPS需要的格式就可以給MPS使用了。

三、Core ML+Vision的應用場景

Core ML提供的是一套底層的演算法庫,Core ML 框架當前已經支援神經網路、樹組合、支援向量機、廣義線性模型、特徵工程和流水線模型等演算法模型的運算, 理論上,只要我們是基於上述這些演算法架構訓練出來的模型,Core ML都是可以支援的

你可能已經從它的名字中猜到了,Vision 可以讓你執行計算機視覺任務。在以前你可能會使用OpenCV,但現在 iOS 有自己的 API 了。Vision庫提供了很多影象處理方面的功能,可以完成人臉識別、特徵檢測、條碼識別、文字識別、並對影象和視訊中的場景進行分類等多個領域,蘋果對這些大資料量的執行也是進行了很深入的優化的,效能比較好。

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

  1. 在給定的影象中尋找人臉。

  2. 尋找面部的詳細特徵,比如眼睛和嘴巴的位置,頭部的形狀等等。

  3. 追蹤視訊中移動的物件、確定地平線的角度。

  4. 轉換兩個影象,使其內容對齊、檢測包含文字的影象中的區域。

  5. 檢測和識別條形碼。

可以使用 Vision 驅動 Core ML,在使用Core ML進行機器學習的時候,可以先用Vision框架進行一些資料的預處理。例如,你可以使用 Vision 來檢測人臉的位置和大小,將視訊幀裁剪到該區域,然後在這部分的面部影象上執行神經網路。

利用Core ML 進行機器學習的時候,輸入的影象資料要求是模型規定的格式和大小,一般我們獲取到的資料大部分都是不滿足這個要求的,如果使用 Vision 框架來負責調整影象大小、影象色度等,我們就很容易把影象資料轉換為模型要求的格式。

利用蘋果提供的這些能力,並結合我們自己的產品,應該可以創造出很多有意思的產品功能,想象空間很大,比如:

  1. 利用訓練好的模型,把低解析度的圖片轉化為高解析度圖片,能夠節省很大的流量,同時在使用者體驗上也能得到很大的提升

  2. 用於人臉檢測,通過人臉聚焦出之前跟這個好友的一些互動操作

  3. 通過學習使用者在app上的操作路徑,預測使用者的行為,比如通過預測使用者的習慣,定時給使用者發feed?

總之,有很多場景可以應用。

四、利用Core ML在影象識別方面實踐

需要 Xcode 9 Beta1 或更新的版本、以及 iOS 11環境,可以下載Demo

專案中允許使用者從照片庫中選擇一張圖片,分別選擇物體分類識別和矩形區域數字識別。

1、直接利用ML進行影象分類識別

a、將 Core ML 模型整合到你的 App

以Inceptionv3模型為例,可以從蘋果的“機器學習”頁面下載。目前蘋果提供的這幾個模型都用於在圖片中檢測物體——樹、動物、人等等。如果你有一個訓練過的模型,並且是使用受支援的機器學習工具訓練的,例如 Caffe、Keras 或 scikit-learn,Converting Trained Models to Core ML 介紹瞭如何將其轉換為 Core ML 格式。

下載 Inceptionv3.mlmodel 後,把它從 Finder 拖到專案導航器裡:

b、Xcode會把模型編譯成三個類:Inceptionv3Input、Inceptionv3Output、Inceptionv3,同時生成一個Inceptionv3.mlmodelc檔案目錄,這裡面就是需要用到已經訓練好的模型資料.

只需要新增幾行程式碼,即可實現一個簡單的物體分類智慧識別器。大大降低了人工智慧的門檻

- (NSString*)predictionWithResnet50:(CVPixelBufferRef )buffer
{
    NSError *modelLoadError = nil;
    NSURL *modelUrl = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"Resnet50" ofType:@"mlmodelc"]];
    Resnet50* resnet50 = [[Resnet50 alloc] initWithContentsOfURL:modelUrl error:&modelLoadError];

    NSError *predictionError = nil;
    Resnet50Output *resnet50Output = [resnet50 predictionFromImage:buffer error:&predictionError];
    if (predictionError) {
        return predictionError.description;
    } else {
        // resnet50Output.classLabelProbs sort
        return [NSString stringWithFormat:@"識別結果:%@,匹配率:%.2f",resnet50Output.classLabel, [[resnet50Output.classLabelProbs valueForKey:resnet50Output.classLabel]floatValue]];
    }
}

2、利用Vision識別矩形框中的數字

上面的方式,是直接利用Core ML操作模型來預測結果的,除了這種方式,我們還可以在 Vision 模型中包裝任意的影象分析 Core ML 模型。模型新增跟上面的方法一致,我們只需要通過vision把相關請求進行封裝,

- (void)predictMINISTClassifier:(UIImage* )uiImage {
    CIImage *ciImage = [CIImage imageWithCGImage:uiImage.CGImage];
    CGImagePropertyOrientation orientation = [self cgImagePropertyOrientation:uiImage];
    self.inputImage = [ciImage imageByApplyingOrientation:orientation];

    VNDetectRectanglesRequest* rectanglesRequest = [[VNDetectRectanglesRequest alloc]initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) {
        [self handleRectangles:request error:error];
    }];

    VNImageRequestHandler *handler = [[VNImageRequestHandler alloc] initWithCGImage:uiImage.CGImage orientation:orientation options:nil];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSError* error = nil;
        [handler performRequests:@[rectanglesRequest] error:&error];
    });
}

- (void)handleRectangles:(VNRequest*)request error:(NSError*)error {
    VNRectangleObservation *detectedRectangle = request.results.firstObject;
    CGSize imageSize = self.inputImage.extent.size;
    CGRect boundingBox = [self scaledCGRect:detectedRectangle.boundingBox toSize:imageSize];
    if (!CGRectContainsRect(self.inputImage.extent, boundingBox)) {
        NSLog(@"invalid detected rectangle");
        return;
    }
    CGPoint topLeft = [self scaledCGPoint:detectedRectangle.topLeft toSize:imageSize];
    CGPoint topRight = [self scaledCGPoint:detectedRectangle.topRight toSize:imageSize];
    CGPoint bottomLeft =[self scaledCGPoint:detectedRectangle.bottomLeft toSize:imageSize];
    CGPoint bottomRight = [self scaledCGPoint:detectedRectangle.bottomRight toSize:imageSize];
    CIImage *cropImage = [self.inputImage imageByCroppingToRect:boundingBox];
    NSDictionary *param = [NSDictionary dictionaryWithObjectsAndKeys:[CIVector vectorWithCGPoint:topLeft],@"inputTopLeft",[CIVector vectorWithCGPoint:topRight],@"inputTopRight",[CIVector vectorWithCGPoint:bottomLeft],@"inputBottomLeft",[CIVector vectorWithCGPoint:bottomRight],@"inputBottomRight", nil];
    CIImage* filterImage = [cropImage imageByApplyingFilter:@"CIPerspectiveCorrection" withInputParameters:param];
    filterImage = [filterImage imageByApplyingFilter:@"CIColorControls" withInputParameters:[NSDictionary dictionaryWithObjectsAndKeys:@(0),kCIInputSaturationKey,@(32),kCIInputContrastKey, nil]];
    filterImage = [filterImage imageByApplyingFilter:@"CIColorInvert" withInputParameters:nil];
    UIImage *correctedImage = [UIImage imageWithCIImage:filterImage];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.imageView.image = correctedImage;
    });
    VNImageRequestHandler *vnImageRequestHandler = [[VNImageRequestHandler alloc] initWithCIImage:filterImage options:nil];

    MNISTClassifier *model = [MNISTClassifier new];
    VNCoreMLModel *vnCoreModel = [VNCoreMLModel modelForMLModel:model.model error:nil];
    VNCoreMLRequest *classificationRequest = [[VNCoreMLRequest alloc] initWithModel:vnCoreModel completionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) {
        VNClassificationObservation *best = request.results.firstObject;

        NSString* result  = [NSString stringWithFormat:@"識別結果:%@,匹配率:%.2f",best.identifier,best.confidence];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.resultLabel.text = result;
        });
    }];
    NSError *imageError = nil;
    [vnImageRequestHandler performRequests:@[classificationRequest] error:&imageError];
}

3、執行效果

五、一些思考

1、模型是否可以通過下載的方式

從蘋果提供的幾個模型來看,他們佔用的空間都是幾十兆上下,在實際應用中,這基本是不現實的,安裝包增加幾十兆,基本是不可想象的。經過分析,Xcode對新增進去的模型做了兩件事:建立對應的類、新增模型資料檔案,這個工作我們自己也能完成

a、首先我們自己生成所需要的類,參考專案中的GoogLeNetPlaces.h GoogLeNetPlaces.m兩個檔案

b、把需要的模型資料夾GoogLeNetPlaces.mlmodelc作為資源新增到工程,實際中可以通過下載獲取

c、在生成GoogLeNetPlaces例項的時候,把模型檔案的路徑傳入

- (nullable instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError * _Nullable * _Nullable)error {
    self = [super init];
    if (!self) { return nil; }
    _model = [MLModel modelWithContentsOfURL:url error:error];
    if (_model == nil) { return nil; }
    return self;
}

MLModel的建立方式是通過介面

+ (nullable instancetype)modelWithContentsOfURL:(NSURL *)url
                                          error:(NSError **)error;

完成的,我我們只需要把模型資料的路徑指定即可。通過這種方式我們完全不需要新增Places205-GoogLeNet模型到工程中,即可用它完成物體預測,用相同的方法,其他模型也可以用這種方式完成。

2、如果需要修改線上APP的模型資料,這種需求能完成麼?

這也是能夠做到的,

a、如果只是更改模型的引數和資料,介面沒有改變(即這些類檔案沒有改變),這種情況,完全可以通過更改模型資料,達到修改外網模型的需求

b、如果模型介面有改變,或者是想換一個模型進行預測,這種情況,如果借用OCS的能力,也是能夠做到的,把生成的類程式碼轉換為OCS指令碼下發,介面和模型檔案,都通過下載新的外掛,即可完成修改模型引數甚至替換其他模型進行資料預測的需求

3、執行緒是否安全?

現在從文件上看,沒有明確說是否執行緒安全,自己實驗取樣100個執行緒並行執行,沒有發現異常情況,具體還需要等正式版釋出後,再看看是否執行緒安全

六、遇到的一些問題

  1. 現在看模型的預測準確率還比較低,很多種情況都識別不了,但願正式版出來後會提升準確率

  2. Xcode9 beta版不支援新增資源目錄,如果想再工程中新增資源目錄,必須先在Xcode8開啟工程,新增進去之後,再用Xcode9 beta開啟,這個應該是Xcode9 beata版本的bug,正式版應該能夠修復

  3. xcode9 beta版之後,導致xcode8的模擬器都不能夠用了

  4. 裝置上不能進行訓練。你需要使用離線工具包來進行訓練,然後將它們轉換到 Core ML 格式

  5. 如果 Core ML 並不是支援所有的layer。在這一點上,你不能使用自己的 kernel 來擴充套件 Core ML。在使用 TensorFlow 這樣的工具來構建通用計算圖模型時,mlmodel 檔案格式可能就不那麼靈活了。

  6. Core ML 轉換工具只支援特定版本的數量有限的訓練工具。例如,如果你在 TensorFLow 中訓練了一個模型,則無法使用此工具,你必須編寫自己的轉換指令碼。

  7. 不能檢視Core ML中間層的結果輸出,只能獲得最後一層網路的預測值,在使用模型進行預測的時候出現問題,這時候不好定位是模型的問題還是框架的問題。

  8. 如果你想要完全能夠掌控機器學習的的各個layer輸出以及決定是否執行在CPU還是GPU,那麼你必須使用 Metal Performance Shader 或 Accelerate 框架來實現完成你的模型的執行。

總之,Core ML當前版本還有很多問題,期待正式版本釋出能夠解決這些問題

參考文獻:
- Metal
- Metal開發文件
- Vision開發文件
- Metal Performance Shaders
- How do Convolutional Neural Networks work
- 深度學習 — 反向傳播(BP)理論推導
- 神經網路基礎

更多精彩內容歡迎關注騰訊 Bugly的微信公眾賬號:

騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智慧合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響使用者數最多的崩潰,精準定位功能幫助開發同學定位到出問題的程式碼行,實時上報可以在釋出後快速的瞭解應用的質量情況,適配最新的 iOS, Android 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧!