一、机器学习
机器学习就是通过对数据进行分析,来改进优化算法。
机器学习有三个要数:数据、学习算法、模型。
数据:机器学习的样本。如在对某支股票的股价预测中,该股过去一段时间的涨跌价格就是数据。
算法:有线性回归、逻辑回归、以及深度学习中的卷积神经网络、循环神经网络等算法。我们如果只是在App中使用模型,不自己训练模型,可以不需要了解这些算法。
模型:即机器从样本数据中找出的规律,根据这个规律,用于对新数据的判断。
机器学习过程如下: 样本数据 --> 算法 --> 模型 预测数据 --> 模型 --> 预测结果
二、Core ML
Core ML是苹果公司于2017年推出的一种离线机器学习框架,其中ML是Machine Learning的缩写,即机器学习。App 可以使用 Core ML API 对用户数据进行预测。苹果为了保护用户数据的私密性和 App 的响应速度,机器学习模型被严格限定在用户设备上,无需任何网络连接。
Core ML 可以自动为模型生成可供调用的 API,其作用相当于机器学习模型与App之间的中间媒介,关系如下图所示:
另外,可以使用 Xcode 内置的 Create ML App 来构建和训练模型。使用 Create ML 训练的模型是 Core ML 格式(文件后缀是 .mlmodel),并能直接在 App 中使用。
当然,使用 Core ML Tools 将其他格式的模型转换成 Core ML 格式,供App使用。
三、使用Core ML
1、获取模型
最简单的获取方式是在苹果官网下载,具体是在Model模块中。目前该模块下有以下模型:
FCRN-DepthPrediction:深度估计(根据一幅图像来预测深度)
MNIST:涂鸦分类 (对单个手写数字进行分类 (支持数字 0-9))
UpdatableDrawingClassifier:涂鸦分类( K-近邻算法(KNN))
MobileNetV2:图像分类
Resnet50:图像分类(残差神经网络)
SqueezeNet:图像分类 (小型深度神经网络)
DeeplabV3:图像分割
YOLOv3:对象检测 (对相机取景框内或图像中 80 种不同类型的对象进行定位和分类)
YOLOv3-Tiny: 对象检测 (实时)(对相机取景框内或图像中 80 种不同类型的对象进行定位和分类)
PoseNet:姿态估计
BERT-SQuAD:问答 (查找文本段落相关问题的答案)
当以上模型不能够满足需求的时候,就需要自己训练模型了,苹果提了转换器 Core ML Tools ,该转换器是基于Python实现的,可用它把训练出来的模型转为适配Core ML的模型。
2、导入模型(以Resnet50为例)
模型后缀名是mlpackage,直接将Resnet50.mlpackage拖入工程即可。打开模型,可以看到改模型的大小、支持版本、预测准确率、分类标签、作者、版本等信息。如下图:
其中Preview可以在工程中预览预测结果,如添加一张金毛图片,其预测结果如下:
金毛的概率87%,拉布拉多的概率6%,网球的概率1%。识别还是比较准确的。
而Predictions项则说明如何使用这个模型,如下图中输入Input是图片,大小224*224,输出Output是预测后各标签的概率,其中classLabel是概率最高的标签。
3、项目中使用(以Resnet50为例)
import "Resnet50.h" 引入 Resnet50,按模型要求调用api即可。
核心代码如下:
Resnet50 *resnet50Model = [[Resnet50 alloc] init];
NSError *error = nil;
Resnet50Output *output = [resnet50Model predictionFromImage:imageRef error:&error];
self.resultLabel.text = [NSString stringWithFormat:@"预测结果:%@",output.classLabel];
预测结果如下:
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;//预测的图片
@property (nonatomic, strong) UILabel *resultLabel;//预测结果
@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:@"预测结果:%@",output.classLabel];
} else {
NSLog(@"Error is %@", error.localizedDescription);
}
UIImagePickerController *imagePickerVC = picker;
[imagePickerVC dismissViewControllerAnimated:YES completion:^{
}];
}
#pragma mark -图片处理-
//image转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;
}
//将一个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