iOS中的人工智能(Core ML)

前言

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

什么是人工智能奏赘?

人工智能總體介紹

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

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

但為什么人工智能智能會比人類學習更智能溃肪?因為數(shù)據(jù)和人相比人腦免胃,更準確更快。人類在思考問題的過程中惫撰,會因為某些極端條件羔沙、前后因果、以及一些細節(jié)問題沒考慮進去等而導致一些問題厨钻,然而機器學習可以依據(jù)大量的數(shù)據(jù)為食物撬碟,不斷的填充自己的肚子,從而可以考慮到很多極端情況以及一些細節(jié)問題等莉撇。除此之外呢蛤,計算機的運行速度是人類大腦無法相比的。吳軍博士在《智能時代》一書中對大數(shù)據(jù)的優(yōu)勢進行了以下總結:“在無法確定因果關系時棍郎,數(shù)據(jù)為我們提供了解決問題的新方法其障,數(shù)據(jù)中所包含的信息可以幫助我們消除不確定性,而數(shù)據(jù)之間的相關性在某種程度上可以取代原來的因果關系涂佃,幫助我們得到想要的答案励翼,這便是大數(shù)據(jù)的核心」架”
再簡單說下神經(jīng)網(wǎng)絡汽抚。可以簡單理解成神經(jīng)元是神經(jīng)網(wǎng)絡的成員伯病,每個神經(jīng)元都有自己的功能造烁。如在花和草之間,前一個神經(jīng)元識別出花午笛,后一個神經(jīng)元在花中識別出玫瑰花惭蟋。前一個神經(jīng)元的識別結果再傳遞個后一個神經(jīng)元。前者是后者的輸出药磺,這就是神經(jīng)分層的大致比喻告组。圍棋AlphaGo理論上就是一個大型的神經(jīng)網(wǎng)絡,在圍棋比賽中癌佩,它能預測到接下來的若干種結果木缝,這種預測就是基于神經(jīng)分層的原理便锨。

機器學習

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

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

機器學習有三個要素:

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

Core ML基本介紹

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

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

Core ML應用步驟分析

1、拿到模型

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

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

將模型導入到項目中攀隔。然后點擊會出現(xiàn)下圖所示狀態(tài)皂贩。大小(Size)是 App 性能的一個重要指標昆汹,輸入(Input)輸出(Output)決定了如何使用這個模型明刷。下圖的輸入是一張圖片,輸出有兩個值满粗,一個是最有可能的圖片物體結果遮精,為 String 類型;另一個是所有可能的物體類型以及對應的可能性败潦,為 String 對應 Dobule 的 Dictionary 類型本冲。點擊下圖的Resret50字樣可以跳轉到生成的代碼鏈接中。


模型信息
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應用步驟分析中贤旷,導入模型那一步驟我們知道广料,ResNet50這個模型需要輸入的是一個 224 * 224 的Image 圖片模型;輸出則是預測歸類標簽等信息幼驶。上面方法中的 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是有寬和高限制的,因此輸入時阳惹,需要轉換為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 模型轉化工具介紹

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

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

2舌缤、轉換訓練好的模型

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

import coremltools

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

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

確定轉化的模型是否正常(檢測模型能否識別一張狗的圖片)友驮,可以直接運行如下命令漂羊。如果能正確輸出結果驾锰,預測結果應含有 dog卸留,并且預測的正確可能性比較高,則說明模型轉換沒問題椭豫。

XXX.mlmodel.predict('data': myTestData)
3耻瑟、定制化轉化的模型

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

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

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

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

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

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

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

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

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

結語

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

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胯舷,一起剝皮案震驚了整個濱河市刻蚯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桑嘶,老刑警劉巖芦倒,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異不翩,居然都是意外死亡兵扬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門口蝠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來器钟,“玉大人,你說我怎么就攤上這事妙蔗“涟裕” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昙啄。 經(jīng)常有香客問我穆役,道長,這世上最難降的妖魔是什么梳凛? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任耿币,我火速辦了婚禮,結果婚禮上韧拒,老公的妹妹穿的比我還像新娘淹接。我一直安慰自己,他們只是感情好叛溢,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布塑悼。 她就那樣靜靜地躺著,像睡著了一般楷掉。 火紅的嫁衣襯著肌膚如雪厢蒜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天烹植,我揣著相機與錄音斑鸦,去河邊找鬼。 笑死刊橘,一個胖子當著我的面吹牛鄙才,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播促绵,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼攒庵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了败晴?” 一聲冷哼從身側響起浓冒,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尖坤,沒想到半個月后稳懒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡慢味,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年场梆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纯路。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡或油,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驰唬,到底是詐尸還是另有隱情顶岸,我是刑警寧澤腔彰,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站辖佣,受9級特大地震影響霹抛,放射性物質發(fā)生泄漏。R本人自食惡果不足惜卷谈,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一杯拐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雏搂,春花似錦藕施、人聲如沸寇损。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矛市。三九已至芙沥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浊吏,已是汗流浹背而昨。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留找田,地道東北人歌憨。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像墩衙,于是被迫代替她去往敵國和親务嫡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容