前言
大概在一個多月前筆者參加了一場線上“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部分組成。
- 該結構最底層是有由 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進行模型轉換蹬昌。
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ā)者對人工智能領域的關注。