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

iOS 11 : CORE ML—淺析

特征點 cti play 同時 pooling 類別 ext 運算 caf

蘋果在 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當前版本還有很多問題,期待正式版本發布能夠解決這些問題

iOS 11 : CORE ML—淺析