一植捎、機(jī)器學(xué)習(xí)
機(jī)器學(xué)習(xí)就是通過對數(shù)據(jù)進(jìn)行分析,來改進(jìn)優(yōu)化算法阳柔。
機(jī)器學(xué)習(xí)有三個(gè)要數(shù):數(shù)據(jù)焰枢、學(xué)習(xí)算法、模型舌剂。
數(shù)據(jù):機(jī)器學(xué)習(xí)的樣本济锄。如在對某支股票的股價(jià)預(yù)測中,該股過去一段時(shí)間的漲跌價(jià)格就是數(shù)據(jù)架诞。
算法:有線性回歸拟淮、邏輯回歸干茉、以及深度學(xué)習(xí)中的卷積神經(jīng)網(wǎng)絡(luò)谴忧、循環(huán)神經(jīng)網(wǎng)絡(luò)等算法。我們?nèi)绻皇窃贏pp中使用模型角虫,不自己訓(xùn)練模型沾谓,可以不需要了解這些算法。
模型:即機(jī)器從樣本數(shù)據(jù)中找出的規(guī)律戳鹅,根據(jù)這個(gè)規(guī)律均驶,用于對新數(shù)據(jù)的判斷。
機(jī)器學(xué)習(xí)過程如下: 樣本數(shù)據(jù) --> 算法 --> 模型 預(yù)測數(shù)據(jù) --> 模型 --> 預(yù)測結(jié)果
二枫虏、Core ML
Core ML是蘋果公司于2017年推出的一種離線機(jī)器學(xué)習(xí)框架妇穴,其中ML是Machine Learning的縮寫,即機(jī)器學(xué)習(xí)隶债。App 可以使用 Core ML API 對用戶數(shù)據(jù)進(jìn)行預(yù)測腾它。蘋果為了保護(hù)用戶數(shù)據(jù)的私密性和 App 的響應(yīng)速度,機(jī)器學(xué)習(xí)模型被嚴(yán)格限定在用戶設(shè)備上死讹,無需任何網(wǎng)絡(luò)連接瞒滴。
Core ML 可以自動為模型生成可供調(diào)用的 API,其作用相當(dāng)于機(jī)器學(xué)習(xí)模型與App之間的中間媒介,關(guān)系如下圖所示:
另外妓忍,可以使用 Xcode 內(nèi)置的 Create ML App 來構(gòu)建和訓(xùn)練模型虏两。使用 Create ML 訓(xùn)練的模型是 Core ML 格式(文件后綴是 .mlmodel),并能直接在 App 中使用世剖。
當(dāng)然定罢,使用 Core ML Tools 將其他格式的模型轉(zhuǎn)換成 Core ML 格式,供App使用旁瘫。
三引颈、使用Core ML
1、獲取模型
最簡單的獲取方式是在蘋果官網(wǎng)下載境蜕,具體是在Model模塊中蝙场。目前該模塊下有以下模型:
FCRN-DepthPrediction:深度估計(jì)(根據(jù)一幅圖像來預(yù)測深度)
MNIST:涂鴉分類 (對單個(gè)手寫數(shù)字進(jìn)行分類 (支持?jǐn)?shù)字 0-9))
UpdatableDrawingClassifier:涂鴉分類( K-近鄰算法(KNN))
MobileNetV2:圖像分類
Resnet50:圖像分類(殘差神經(jīng)網(wǎng)絡(luò))
SqueezeNet:圖像分類 (小型深度神經(jīng)網(wǎng)絡(luò))
DeeplabV3:圖像分割
YOLOv3:對象檢測 (對相機(jī)取景框內(nèi)或圖像中 80 種不同類型的對象進(jìn)行定位和分類)
YOLOv3-Tiny: 對象檢測 (實(shí)時(shí))(對相機(jī)取景框內(nèi)或圖像中 80 種不同類型的對象進(jìn)行定位和分類)
PoseNet:姿態(tài)估計(jì)
BERT-SQuAD:問答 (查找文本段落相關(guān)問題的答案)
當(dāng)以上模型不能夠滿足需求的時(shí)候策治,就需要自己訓(xùn)練模型了未檩,蘋果提了轉(zhuǎn)換器 Core ML Tools ,該轉(zhuǎn)換器是基于Python實(shí)現(xiàn)的悄雅,可用它把訓(xùn)練出來的模型轉(zhuǎn)為適配Core ML的模型台诗。
2完箩、導(dǎo)入模型(以Resnet50為例)
模型后綴名是mlpackage,直接將Resnet50.mlpackage拖入工程即可拉队。打開模型弊知,可以看到改模型的大小、支持版本粱快、預(yù)測準(zhǔn)確率秩彤、分類標(biāo)簽、作者事哭、版本等信息漫雷。如下圖:
其中Preview可以在工程中預(yù)覽預(yù)測結(jié)果,如添加一張金毛圖片鳍咱,其預(yù)測結(jié)果如下:
金毛的概率87%降盹,拉布拉多的概率6%,網(wǎng)球的概率1%谤辜。識別還是比較準(zhǔn)確的蓄坏。
而Predictions項(xiàng)則說明如何使用這個(gè)模型,如下圖中輸入Input是圖片丑念,大小224*224涡戳,輸出Output是預(yù)測后各標(biāo)簽的概率,其中classLabel是概率最高的標(biāo)簽渠欺。
3妹蔽、項(xiàng)目中使用(以Resnet50為例)
import "Resnet50.h" 引入 Resnet50,按模型要求調(diào)用api即可。
核心代碼如下:
Resnet50 *resnet50Model = [[Resnet50 alloc] init];
NSError *error = nil;
Resnet50Output *output = [resnet50Model predictionFromImage:imageRef error:&error];
self.resultLabel.text = [NSString stringWithFormat:@"預(yù)測結(jié)果:%@",output.classLabel];
預(yù)測結(jié)果如下:
4胳岂、最后貼上完整代碼
//
// ViewController.m
// CoreMLDemo
//
// Created by 胡sir on 2023/2/18.
//
#import "ViewController.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import "Resnet50.h"
@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>
@property (nonatomic, strong) UIImageView *imageView;//預(yù)測的圖片
@property (nonatomic, strong) UILabel *resultLabel;//預(yù)測結(jié)果
@property (nonatomic, strong) UIButton *startBtn;//按鈕
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.resultLabel];
[self.view addSubview:self.imageView];
[self.view addSubview:self.startBtn];
}
#pragma mark -懶加載-
- (UILabel *)resultLabel {
if (!_resultLabel) {
_resultLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, 40)];
_resultLabel.textAlignment = NSTextAlignmentCenter;
_resultLabel.textColor = [UIColor blackColor];
_resultLabel.font = [UIFont systemFontOfSize:18];
}
return _resultLabel;
}
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.resultLabel.frame), [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-240)];
}
return _imageView;
}
- (UIButton *)startBtn {
if (!_startBtn) {
_startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_startBtn.frame = CGRectMake(0, CGRectGetMaxY(self.imageView.frame), [UIScreen mainScreen].bounds.size.width, 100);
[_startBtn setTitle:@"選擇照片" forState:UIControlStateNormal];
[_startBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_startBtn addTarget:self action:@selector(startBtnClick) forControlEvents:UIControlEventTouchUpInside];
}
return _startBtn;
}
- (void)startBtnClick {
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePickerController.delegate = self;
imagePickerController.allowsEditing = YES;
// imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
// imagePickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
[self presentViewController:imagePickerController animated:YES completion:nil];
}
#pragma mark -UIImagePickerControllerDelegate-
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
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.resultLabel.text = [NSString stringWithFormat:@"預(yù)測結(jié)果:%@",output.classLabel];
} else {
NSLog(@"Error is %@", error.localizedDescription);
}
UIImagePickerController *imagePickerVC = picker;
[imagePickerVC dismissViewControllerAnimated:YES completion:^{
}];
}
#pragma mark -圖片處理-
//image轉(zhuǎn)PixelBuffer
- (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;
}
//將一個(gè)UIImage縮放變換到指定Size
-(UIImage*)image:(UIImage *)image scaleToSize:(CGSize)size {
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
@end