標籤:oid ndk 解決 nsstring ctc rspec with iter rem
蘋果在 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)是人工神經網路的一種,已成為當前語音分析和Image Recognition領域的研究熱點。它的權值共用網路結構使之更類似於生物神經網路,降低了網路模型的複雜度,減少了權值的數量。優點在網路的輸入是多維映像時表現的更為明顯,使映像可以直接作為網路的輸入,避免了傳統識別演算法中複雜的特徵提取和資料重建過程。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庫提供了很多影像處理方面的功能,可以完成Face Service、特徵檢測、條碼識別、文字識別、並對映像和視頻中的情境進行分類等多個領域,蘋果對這些大資料量的運行也是進行了很深入的最佳化的,效能比較好。
Vision 可以執行的任務有以下幾種:
在給定的映像中尋找人臉。
尋找面部的詳細特徵,比如眼睛和嘴巴的位置,頭部的形狀等等。
追蹤視頻中移動的對象、確定地平線的角度。
轉換兩個映像,使其內容對齊、檢測包含文本的映像中的地區。
檢測和識別條碼。
可以使用 Vision 驅動 Core ML,在使用Core ML進行機器學習的時候,可以先用Vision架構進行一些資料的預先處理。例如,你可以使用 Vision 來檢測人臉的位置和大小,將視訊框架裁剪到該地區,然後在這部分的面部映像上運行神經網路。
利用Core ML 進行機器學習的時候,輸入的映像資料要求是模型規定的格式和大小,一般我們擷取到的資料大部分都是不滿足這個要求的,如果使用 Vision 架構來負責調整映像大小、映像色度等,我們就很容易把映像資料轉換為模型要求的格式。
利用蘋果提供的這些能力,並結合我們自己的產品,應該可以創造出很多有意思的產品功能,想象空間很大,比如:
利用訓練好的模型,把低解析度的圖片轉化為高解析度圖片,能夠節省很大的流量,同時在使用者體驗上也能得到很大的提升
用於臉部偵測,通過人臉聚焦出之前跟這個好友的一些互動操作
通過學慣用戶在app上的操作路徑,預測使用者的行為,比如通過預測使用者的習慣,定時給使用者發feed?
總之,有很多情境可以應用。
四、利用Core ML在Image Recognition方面實踐
需要 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個線程並行運行,沒有發現異常情況,具體還需要等正式版發布後,再看看是否安全執行緒
六、遇到的一些問題
現在看模型的預測準確率還比較低,很多種情況都識別不了,但願正式版出來後會提升準確率
Xcode9 beta版不支援添加資來源目錄,如果想再工程中添加資來源目錄,必須先在Xcode8開啟工程,添加進去之後,再用Xcode9 beta開啟,這個應該是Xcode9 beata版本的bug,正式版應該能夠修複
xcode9 beta版之後,導致xcode8的模擬器都不能夠用了
裝置上不能進行訓練。你需要使用離線工具包來進行訓練,然後將它們轉換到 Core ML 格式
如果 Core ML 並不是支援所有的layer。在這一點上,你不能使用自己的 kernel 來擴充 Core ML。在使用 TensorFlow 這樣的工具來構建通用計算圖模型時,mlmodel 檔案格式可能就不那麼靈活了。
Core ML 轉換工具只支援特定版本的數量有限的訓練工具。例如,如果你在 TensorFLow 中訓練了一個模型,則無法使用此工具,你必須編寫自己的轉換指令碼。
不能查看Core ML中介層的結果輸出,只能獲得最後一層網路的預測值,在使用模型進行預測的時候出現問題,這時候不好定位是模型的問題還是架構的問題。
如果你想要完全能夠掌控機器學習的的各個layer輸出以及決定是否運行在CPU還是GPU,那麼你必須使用 Metal Performance Shader 或 Accelerate 架構來實現完成你的模型的運行。
總之,Core ML目前的版本還有很多問題,期待正式版本發布能夠解決這些問題
iOS 11 : CORE ML—淺析