iOS中的人工智能(Core ML)

前言

大概在一個多月前筆者參加了一場線上“IT技術(shù)人成長經(jīng)驗交流會”,實際上這場大會的參加者主要是以iOS技術(shù)人員為主。小猿搜題技術(shù)負責人唐巧大神也參加了這場大會,給在場所有人做了一場分享。印象最為深刻的一個大神李嘉璇(《TensorFlow技術(shù)解析于實戰(zhàn)》作者),當時做了一個讓在場幾乎所有人都懵逼的技術(shù)分享,技術(shù)分享的主題是和TensorFlow相關(guān)的人工智能。參加交流會的多為iOS開發(fā)者,接觸人工智能的少之又少,所以在場的90%以上的人聽得一臉懵也是正常情況。所以今天筆者想簡單的總結(jié)下所謂的人工智能以及蘋果最近推出的Core ML框架。附上上次技術(shù)交流會的幾張圖,巧神、李嘉璇以及我露面的鏡頭??。
唐巧大神
李嘉璇大神
露面的我

什么是人工智能?

人工智能總體介紹

人工智能簡稱AI。提及這個詞匯的時候,通常大數(shù)據(jù)、機器學習、神經(jīng)網(wǎng)絡(luò)等詞匯也會與之一塊出來。接下來筆者帶領(lǐng)大家一一認識這幾個詞匯。
所謂的人工智能,實際就是機器依靠數(shù)據(jù)的內(nèi)在邏輯自己定義方法,通過機器模擬人類大腦思考過程,進而定義方法。機器學習實際就是實現(xiàn)人工智能的一種方法,而方法的定義是以大數(shù)據(jù)為依靠。
試想一位兒童心理學家在做一些心理學實驗。這個實驗大概可以分為三步:

  • 1、盡可能收集多的數(shù)據(jù),該數(shù)據(jù)實際就類似大數(shù)據(jù)
  • 2、分析數(shù)據(jù)。分析數(shù)據(jù)的過程實際上就類似機器學習的過程。
  • 3、得出結(jié)論。
    為了更好的理解這個過程,下圖簡單的對比了下。人的大腦學習過程和機器學習的對比。
人類學習和機器學習對比

但為什么人工智能智能會比人類學習更智能?因為數(shù)據(jù)和人相比人腦,更準確更快。人類在思考問題的過程中,會因為某些極端條件、前后因果、以及一些細節(jié)問題沒考慮進去等而導致一些問題,然而機器學習可以依據(jù)大量的數(shù)據(jù)為食物,不斷的填充自己的肚子,從而可以考慮到很多極端情況以及一些細節(jié)問題等。除此之外,計算機的運行速度是人類大腦無法相比的。吳軍博士在《智能時代》一書中對大數(shù)據(jù)的優(yōu)勢進行了以下總結(jié):“在無法確定因果關(guān)系時,數(shù)據(jù)為我們提供了解決問題的新方法,數(shù)據(jù)中所包含的信息可以幫助我們消除不確定性,而數(shù)據(jù)之間的相關(guān)性在某種程度上可以取代原來的因果關(guān)系,幫助我們得到想要的答案,這便是大數(shù)據(jù)的核心。”
再簡單說下神經(jīng)網(wǎng)絡(luò)??梢院唵卫斫獬缮窠?jīng)元是神經(jīng)網(wǎng)絡(luò)的成員,每個神經(jīng)元都有自己的功能。如在花和草之間,前一個神經(jīng)元識別出花,后一個神經(jīng)元在花中識別出玫瑰花。前一個神經(jīng)元的識別結(jié)果再傳遞個后一個神經(jīng)元。前者是后者的輸出,這就是神經(jīng)分層的大致比喻。圍棋AlphaGo理論上就是一個大型的神經(jīng)網(wǎng)絡(luò),在圍棋比賽中,它能預(yù)測到接下來的若干種結(jié)果,這種預(yù)測就是基于神經(jīng)分層的原理。

機器學習

機器學習就是通過對經(jīng)驗、數(shù)據(jù)進行分析,來改進現(xiàn)有的計算機算法,優(yōu)化現(xiàn)有的程序性能。簡單說就是:

數(shù)據(jù)->算法->模型; 需要判斷的數(shù)據(jù)->模型->給出預(yù)測

機器學習有三個要素:

  • 數(shù)據(jù):數(shù)據(jù)就是機器學習的樣本。比如在多種花中識別這些花,這些花本身的一些特征就是數(shù)據(jù),區(qū)分這些花,主要是依照它們自身的特性不同。
  • 學習算法:神經(jīng)網(wǎng)絡(luò)、邏輯回歸、隨機森林等都是機器的學習算法,所謂iOS開發(fā)工程師的我們,無需深刻理解這些算法,Core ML框架中就已經(jīng)為我們做了這些。
  • 模型:所謂的模型就是機器從樣本數(shù)據(jù)中找出的規(guī)律。根據(jù)這些規(guī)律,面對新的數(shù)據(jù),模型就能做出相應(yīng)的判斷。實際和人類學習是很相像的。

Core ML基本介紹

Core ML支持 iOS、MacOS、tvOS和 watchOS。由4部分組成。

Core ML構(gòu)造
  • 該結(jié)構(gòu)最底層是有由 Acccelerate 和 Metal Performance Shaders 組成。前者用于圖形學以及數(shù)學上的大規(guī)模計算,后者用于優(yōu)化加速圖形渲染。
  • Core ML主要有兩個職責:導入機器學習模型;生成對應(yīng)的OC或Swift代碼。
  • Vision主要用于圖片分析。NLP主要用于自然語義分析。
  • 最上層是應(yīng)用層,有了下面三層的基礎(chǔ),應(yīng)用層就可以做很多事情,如人臉識別、手寫文字理解、文字情感分析、自動翻譯等。

Core ML應(yīng)用步驟分析

1、拿到模型

最簡單的獲取方式是在蘋果官網(wǎng)下載,具體是在Model模塊中,該模塊下有Places205-GoogLeNet、ResNet50、Inception V3、 VGG6四個模型。當然也可以自己訓練模型。另外蘋果也提供了轉(zhuǎn)換器(Core ML Tools),該轉(zhuǎn)換器是基于Python實現(xiàn)的,可用它把訓練出來的模型轉(zhuǎn)為適配Core ML的模型。在文章的最后我會介紹如何使用Core ML Tools進行模型轉(zhuǎn)換。

模型轉(zhuǎn)換適配Core ML框架的過程
2、模型導入到項目

將模型導入到項目中。然后點擊會出現(xiàn)下圖所示狀態(tài)。大?。⊿ize)是 App 性能的一個重要指標,輸入(Input)輸出(Output)決定了如何使用這個模型。下圖的輸入是一張圖片,輸出有兩個值,一個是最有可能的圖片物體結(jié)果,為 String 類型;另一個是所有可能的物體類型以及對應(yīng)的可能性,為 String 對應(yīng) Dobule 的 Dictionary 類型。點擊下圖的Resret50字樣可以跳轉(zhuǎn)到生成的代碼鏈接中。


模型信息
3、生成高級代碼并編程

這一步驟請具體看下面示例的代碼。

Core ML實戰(zhàn)(基于ResNet50模型照片識別)

這個示例中我選擇蘋果官網(wǎng)提供的圖像識別ResNet50作為模型。照片的選擇主要是通過UIImagePickerController這個類實現(xiàn)。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.imagePickerController = [[UIImagePickerController alloc] init];
    self.imagePickerController.delegate = self;
    self.imagePickerController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    self.imagePickerController.allowsEditing = YES;
    self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
    self.imagePickerController.mediaTypes = @[(NSString *)kUTTypeImage];
    self.imagePickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
    [self.navigationController presentViewController:self.imagePickerController
                                            animated:YES
                                          completion:nil];
}

在UIImagePickerControllerDelegate的代理方法下,實現(xiàn)了如下代碼。

- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
    NSString *mediaType=[info objectForKey:UIImagePickerControllerMediaType];
    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]){
        CGSize thesize = CGSizeMake(224, 224);
        UIImage *theimage = [self image:info[UIImagePickerControllerEditedImage] scaleToSize:thesize];
        self.imageView.image = theimage;
        
        CVPixelBufferRef imageRef = [self pixelBufferFromCGImage:theimage.CGImage];
        Resnet50 *resnet50Model = [[Resnet50 alloc] init];
        NSError *error = nil;
        Resnet50Output *output = [resnet50Model predictionFromImage:imageRef
                                                              error:&error];
        if (error == nil) {
            self.photoNameLabel.text = output.classLabel;
        } else {
            NSLog(@"Error is %@", error.localizedDescription);
        }
    }
        
    UIImagePickerController *imagePickerVC = picker;
    [imagePickerVC dismissViewControllerAnimated:YES completion:^{
        
    }];
}

在上面Core ML應(yīng)用步驟分析中,導入模型那一步驟我們知道,ResNet50這個模型需要輸入的是一個 224 * 224 的Image 圖片模型;輸出則是預(yù)測歸類標簽等信息。上面方法中的 imageRef以及output.classLabel都是基于此模型定義的。

如果不是很理解,可以點擊模型,然后再點擊Model Class -> 模型名稱,可以查看模型生成的代碼。下面一段代碼便是此模型生成的代碼。

//
// Resnet50.h
// 
// This file was automatically generated and should not be edited.
// 

#import <Foundation/Foundation.h>
#import <CoreML/CoreML.h>
#include <stdint.h>

NS_ASSUME_NONNULL_BEGIN

/// Model Prediction Input Type
@interface Resnet50Input : NSObject<MLFeatureProvider>
/// Input image of scene to be classified as BGR image buffer, 224 pixels wide by 224 pixels high
@property (readwrite, nonatomic) CVPixelBufferRef image;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithImage:(CVPixelBufferRef)image;
@end

/// Model Prediction Output Type
@interface Resnet50Output : NSObject<MLFeatureProvider>
/// Probability of each category as dictionary of strings to doubles
@property (readwrite, nonatomic) NSDictionary<NSString *, NSNumber *> * classLabelProbs;
/// Most likely image category as string value
@property (readwrite, nonatomic) NSString * classLabel;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithClassLabelProbs:(NSDictionary<NSString *, NSNumber *> *)classLabelProbs classLabel:(NSString *)classLabel;
@end

/// Class for model loading and prediction
@interface Resnet50 : NSObject
@property (readonly, nonatomic, nullable) MLModel * model;
- (nullable instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError * _Nullable * _Nullable)error;
/// Make a prediction using the standard interface
/// @param input an instance of Resnet50Input to predict from
/// @param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
/// @return the prediction as Resnet50Output
- (nullable Resnet50Output *)predictionFromFeatures:(Resnet50Input *)input error:(NSError * _Nullable * _Nullable)error;
/// Make a prediction using the convenience interface
/// @param image Input image of scene to be classified as BGR image buffer, 224 pixels wide by 224 pixels high:
/// @param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
/// @return the prediction as Resnet50Output
- (nullable Resnet50Output *)predictionFromImage:(CVPixelBufferRef)image error:(NSError * _Nullable * _Nullable)error;
@end
NS_ASSUME_NONNULL_END

這樣就基本完成了主要代碼的編寫。不過代碼的實現(xiàn)中還有這樣兩個方法:

- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image {
    NSDictionary *options = @{
                              (NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES,
                              (NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
                              };

    CVPixelBufferRef pxbuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, CGImageGetWidth(image),
                                          CGImageGetHeight(image), kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
                                          &pxbuffer);
    if (status!=kCVReturnSuccess) {
        NSLog(@"Operation failed");
    }
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pxdata, CGImageGetWidth(image),
                                                 CGImageGetHeight(image), 8, 4*CGImageGetWidth(image), rgbColorSpace,
                                                 kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);

    CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
    CGAffineTransform flipVertical = CGAffineTransformMake( 1, 0, 0, -1, 0, CGImageGetHeight(image) );
    CGContextConcatCTM(context, flipVertical);
    CGAffineTransform flipHorizontal = CGAffineTransformMake( -1.0, 0.0, 0.0, 1.0, CGImageGetWidth(image), 0.0 );
    CGContextConcatCTM(context, flipHorizontal);

    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
                                           CGImageGetHeight(image)), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
    return pxbuffer;
}

- (UIImage*)image:(UIImage *)image scaleToSize:(CGSize)size{

    UIGraphicsBeginImageContext(size);

    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];

    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return scaledImage;
}

對于第一個方法,CVPixelBufferRef這種圖像格式的處理與UIImage, CGImageRef的處理需小心,容易造成內(nèi)存泄漏。對于第二個方法,是因為模型的Input Image是有寬和高限制的,因此輸入時,需要轉(zhuǎn)換為224 * 224大小才能夠正確識別。

這個Demo到此就完成了,如果此時拍一張你敲代碼的鍵盤,就能識別出圖片中顯示的是鍵盤。
這里順便在說一下文字感情分析類實現(xiàn)過程,有興趣的可以自行研究下。文字感情分析總共分為四個過程:1、用自然語義處理API統(tǒng)計輸入文字的單詞頻率。2、將單詞頻率輸入到 Core ML 的模型中。3、Core ML 模型根據(jù)單詞頻率判斷內(nèi)容為正面或負面情緒。4、根據(jù)情緒內(nèi)容更新 UI。其實說白了基本上和圖片識別的實現(xiàn)過程實現(xiàn)一致。

Core ML Tool 模型轉(zhuǎn)化工具介紹

之前說過了,模型可以在蘋果官網(wǎng)下載,可以自己訓練模型,也可以借助Core ML Tool將Caffee,Keras,LIBSVM,scikit-learn,xgboot等開源機器學習框架訓練出的模型轉(zhuǎn)換為 Core ML 對應(yīng)的模型。Core ML Tool模型轉(zhuǎn)換工具是基于Python實現(xiàn)的,我們可以定制轉(zhuǎn)換器以及轉(zhuǎn)換模型的參數(shù)。

#######1、安轉(zhuǎn)Core ML Tool 模型轉(zhuǎn)換工具
如果電腦沒有安裝Python,請先執(zhí)行:
brew install python
然后直接輸入以下命令:
pip install -U coremltools

2、轉(zhuǎn)換訓練好的模型

假如模型是用 caffe 訓練的,即現(xiàn)在有一個 .caffemodel 文件,以下步驟可以將其轉(zhuǎn)化為蘋果支持的 .mlmodel:

import coremltools

// 利用 core ml 中對應(yīng)的 caffee 轉(zhuǎn)化器處理 .caffemodel 模型
coreml_model = coremltools.converters.caffe.convert('XXX.caffemodel')

// 將轉(zhuǎn)化好的模型存儲為 .mlmodel 文件
coreml_model.save('XXX.mlmodel')

確定轉(zhuǎn)化的模型是否正常(檢測模型能否識別一張狗的圖片),可以直接運行如下命令。如果能正確輸出結(jié)果,預(yù)測結(jié)果應(yīng)含有 dog,并且預(yù)測的正確可能性比較高,則說明模型轉(zhuǎn)換沒問題。

XXX.mlmodel.predict('data': myTestData)
3、定制化轉(zhuǎn)化的模型

定制轉(zhuǎn)化模型的參數(shù),我們一般用 label.txt 文件來定義,直接傳入轉(zhuǎn)化中即可。

// 自定義模型的接口參數(shù)
labels = 'labels.txt'

// 將 labels 設(shè)為轉(zhuǎn)換的模型參數(shù)
coreml_model = coremltools.converters.caffe.convert('XXX.caffemodel', class_labels='labels')

定制轉(zhuǎn)化的輸入數(shù)據(jù) data 為 image 類型:

coreml_model = coremltools.converters.caffe.convert('XXX.caffemodel', class_labels='labels', image_input_name = 'data')

指定轉(zhuǎn)換模型的描述型參數(shù)(metadata),其他參數(shù)的設(shè)置類似:

// 指定作者信息
coreml_model.author = 'Apple Papa'

// 指定許可證
coreml_model.license = 'MIT'

// 指定輸入('data')描述
coreml_model.input_description['data'] = 'An image of flower'

結(jié)語

整片文章中我們認識了什么人工智能、機器學習,知道了Core ML框架結(jié)構(gòu)以及應(yīng)用步驟,并實現(xiàn)了一個簡單的照片識別實例,最后還介紹了Core ML模型轉(zhuǎn)換工具。但是這一切的一切只是簡單的入門,同上次參加經(jīng)驗交流會《TensorFlow技術(shù)解析于實戰(zhàn)》作者李嘉璇所講的那些技術(shù)相比,連九牛一毛都不算,畢竟人家講的都是一些很高深的底層實現(xiàn)以及高數(shù)中那些復(fù)雜計算公式??赡苡行┤藭J為人工智能只是一個噱頭,實際人工智能已經(jīng)滲入到我們生活的很多方面,希望這篇文章能引起更多iOS開發(fā)者對人工智能領(lǐng)域的關(guān)注。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容