MLCore加载在线模型
前言回顾
上次已经解释了如何从本地加载mlmodel模型,这次,我们着重来介绍一下如何加载在线的mlmodel模型。
思路
模型文件解析
我们以官网的MNISTClassifier.mlmodel模型为例。首先,我们打开MNISTClassifier.h头文件看下:
......
/// Model Prediction Input Type
API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0)) __attribute__((visibility("hidden")))
@interface MNISTClassifierInput : NSObject<MLFeatureProvider>
@property (readwrite, nonatomic) CVPixelBufferRef image;
......
@end
/// Model Prediction Output Type
API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0)) __attribute__((visibility("hidden")))
@interface MNISTClassifierOutput : NSObject<MLFeatureProvider>
/// Probability of each digit as dictionary of 64-bit integers to doubles
@property (readwrite, nonatomic, strong) NSDictionary<NSNumber *, NSNumber *> * labelProbabilities;
/// Most likely digit as integer value
@property (readwrite, nonatomic) int64_t classLabel;
......
@end
/// Class for model loading and prediction
API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0)) __attribute__((visibility("hidden")))
@interface MNISTClassifier : NSObject
......
@end
可以发现,无论是Input和Output都必须继承来自MLFeatureProvider协议,我们在看下MLFeatureProvider协议的相关函数:
//
// MLFeatureProvider.h
// CoreML
//
// Copyright © 2017 Apple Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreML/MLFeatureValue.h>
NS_ASSUME_NONNULL_BEGIN
/*
* Protocol for accessing a feature value for a feature name
*/
API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0))
@protocol MLFeatureProvider
@property (readonly, nonatomic) NSSet<NSString *> *featureNames;
/// Returns nil if the provided featureName is not in the set of featureNames
- (nullable MLFeatureValue *)featureValueForName:(NSString *)featureName;
@end
NS_ASSUME_NONNULL_END
看到这个协议,我们只需要理解featureNames变量和@select(featureValueForName:)即可。
这里,我们回顾一下一个模型的输入和输出需要具备什么功能:
- 输入可能有多个通道输入,并且每个通道都有一个唯一表示的名字。
- 输出可能有多个通道输出,并且每个通道都有一个唯一表示的名字。
这里,就可以很好的解释以上两个东西的含义了。
featureNames,代表这一层所包含所有输入(输出)所包含的唯一标识符,在这里,input的唯一标识符有一个,名字为"image",输出的唯一标识符有两个,名字为"classLabel"和"labelProbabilities"。
@select(featureValueForName:)的意思为,如果查询到某个变量,需要返回该变量对应的数据。好比输入的image,当传入"image"的时候,需要返回对应的图像数据。
到此,我们已经明白了MLModel的运行流程。那么,只需要仿照mlmodel相关资料,就可以模拟其在手机上的运行了。
模型加载
上面已经讲完了输入和输出,这时候,我们还没搞清楚模型到底从哪里来?
我们看下MLModel类:
@interface MLModel : NSObject
/// Construct a model with a default MLModelConfiguration object
(nullable instancetype)modelWithContentsOfURL:(NSURL *)url
error:(NSError **)error;
/// Construct a model given the location of its on-disk representation. Returns nil on error.
(nullable instancetype)modelWithContentsOfURL:(NSURL *)url
configuration:(MLModelConfiguration *)configuration
error:(NSError * _Nullable __autoreleasing *)error API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0));
@end
我们可以找到,有两个函数可以提供加载一个mlmodel模型,并且返回一个MLModel类。而从MLModel的函数可以得出,这个类,就是我们需要的推理类。到此,我们得到了可以通过用url的方式来加载一个模型的函数,但是我们都知道,iOS在编译的时候,会把mlmodel编译为mlmodelc,那我们实际要怎么完成这一步呢?继续查阅相关代码,我们找到了相关的函数:
/*!
* MLModel (MLModelCompilation)
*
* Class with utilties for performing .mlmodel compilation
*/
@interface MLModel (MLModelCompilation)
/*!
* Compile a .mlmodel for this device
*
* @param modelURL URL file path to .mlmodel file you wish to compile
* @param error Any errors are surfaced here
*
* @returns a URL to the compiled .mlmodelc directory if successful
* The model is compiled to a temporary location on disk
* You must move the compiled model to a permenant location if you wish to keep it
*
* @discussion the returned model can be loaded using:
* @code
* [MLModel modelWithContentsOfURL:error:]
* @endcode
*
*/
(nullable NSURL *)compileModelAtURL:(NSURL *)modelURL
error:(NSError **)error
API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0)) __WATCHOS_UNAVAILABLE;
@end
到此,我们已经可以尝试加载一个本地路径的mlmodel转化为MLModel类了。我们封装一下:
- (void)loadModelUrl:(NSString*)path modelName:(modelName) {
NSError* error = nil;
NSURL* fileUrl = [NSURL fileURLWithPath:path];
if (fileUrl == nil) {
return;
}
// 编译mlmodel
NSURL* tempModelUrl = nil;
if (@available(iOS 11.0, *)) {
tempModelUrl = [MLModel compileModelAtURL:fileUrl error:&error];
}
if (error || tempModelUrl == nil) {
NSLog(@"load model error with url:%p error:%p", error, tempModelUrl);
return;
}
// 将临时目录拷贝到目标目录
NSString* directorPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString* modelPath = [directorPath stringByAppendingPathComponent:modelName];
NSFileManager* fileManager = [NSFileManager defaultManager];
// 删除原始文件
if ([fileManager fileExistsAtPath:modelPath]) {
[fileManager removeItemAtPath:modelPath error:&error];
if (error) {
NSLog(@"remove file error with(%s)", [modelPath UTF8String]);
}
}
// 更新文件
[fileManager moveItemAtURL:tempModelUrl toURL:[NSURL fileURLWithPath:modelPath] error:&error];
if (error) {
NSLog(@"copy temp(%s) to modelPath(%s) error", [tempModelUrl.absoluteURL.path UTF8String], [modelPath UTF8String]);
return;
}
// modelPath即为本地mlmodelc路径,我们可以使用它来加载模型
_modelInstance = [MLModel modelWithContentsOfURL:modelPath error:&error];
// todo some thing
}
编写模型的输入
@interface MNISTClassifierInput : NSObject<MLFeatureProvider>
@property (readwrite, nonatomic) CVPixelBufferRef image;
@end
@implementation MNISTClassifierInput
@synthesize featureNames = _featureNames;
- (void)setFeatureNames(NSSet<NSString*>*)featureNames {
_featureNames = featureNames;
}
- (NSSet<NSString*>*)featureNames {
return _featureNames;
}
- (nullable MLFeatureValue *)featureValueForName:(nonnull NSString *)featureName {
for (NSString* name in _featureNames) {
if ([name isEqualToString:featureName]) {
// todo same thing
}
}
return nil;
}
@end
这是一个通用的模版,几乎所有的MLModel的输入输出都可以这么编写,只需要在@select(featureValueForName:)实现相关的数据返回,满足模型的输入和输出即可。但对于输出,我们无需去编写这样的一个类,只需要将返回值标记为id即可,类似
// prediction
id<MLFeatureProvider> output = [_enhance predictionFromFeatures:input error:error];
// 获取第一个feature的输出,实际上也可以获取你需要的输出,只需要通过字符串即可映射出来。
NSArray* featureNamesWithOutput = output.featureNames.allObjects;
NSString* featureName = featureNamesWithOutput[0];
MLFeatureValue* value = [output featureValueForName:featureName];
if (value == nil) {
return NO;
}
到此,整个流程已经串通。
网络下载mlmodel
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
[data writeToFile:filePath atomically:YES];
总结
我们来梳理下,总结下,对于一个在线模型,我们需要怎么使用MLCore进行端上增强呢?
- 下载资源,可参考"网络下载mlmodel"
- 将模型从mlmodel编译为mlmodelc,可以参考"模型加载"
- 编写输入类/接受输出类,用于MLModel的模型输入,可参考"编写模型的输入"
参考文献
https://www.jianshu.com/p/c86d14c87ead
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhifkjkg
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
excel图片置于文字下方的方法
PHP中文网 06-27 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
微信提示登录环境异常是什么意思原因
PHP中文网 04-09 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
微信人名旁边有个图标有什么用
PHP中文网 03-11