机器学习?深度学习?作为一个已经毕业几年,已经把大多数相关知识都还给老师的人来说,要实际应用这些知识到工作中,还是相当有难度的。但是!我们毕竟是iOS开发啊,有Apple罩着我们,Apple在2017年的WWDC上就给了我们一个机器学习框架Core ML,且还在不断更新完善中,今年已经升级到Core ML 2了。
本文就将详细介绍如何训练、集成从而识别物品,且实际落地到产品之中。也就是是主要演示图像分类的问题。
首先是选什么框架来来训练模型呢? 通用主流的有Keras、Caffe、TensorFlow等。既然是要集成到iOS App中,显然首选的是Apple自家的框架,Turi Create。
XCode 10上,有新的Create ML框架,可在playground上可视化训练结果。由于本人主力机器还是XCode 9, 对swift不熟悉 , 因此不采用,且Turi Create更灵活。大家也可以试试这个新框架。
如果是考虑多个平台,或者对其他训练模型更有经验,可以选择其他的,Apple也提供了工具来转换成iOS所需的mlmodel
, 也就是coremltools,可转的为:
如果是TensorFlow
则需要使用 tfcoreml 来转换。
Apple对于开发者来说,还是很周到,友好的。
训练素材方面,Turi Create
的要求并不严格,.JPEG和PNG都支持,不过为了识别的更准确些,建议素材的选择上要多一些跟实际应用场景接近的图片。本文的例子将是识别手边的实物,分别是路飞手办、索隆手办、WallE、严选运动水杯以及普通的杯子。所以在训练素材方面,walleE、杯子会全部使用Google图片,路飞、索隆会使用Google的图片以及部分iPhone实拍图片(为了试验贴近实际使用场景),严选运动水杯则全部采用线上的商品评价图来进行训练。
这里是作为例子来演示各种素材来源的效果,杯子和运动水杯之间也有交集并不是严格的不同分类,仅作效果展示,如果实际应用,建议还是更好的分类,使用更合理的训练集。
以下是部分WallE的训练图:
本文中使用的训练集数量为5个分类各20张,数量上是偏少的。训练素材上,需放在各自文件夹内,并将整个文件夹命名为mydataset,如图放置即可:
这里大家会发现还有一个other的文件夹,具体原因后文中会再介绍。
因为TuriCreate
是个python
库,所以首先需要安装的是python, 这个在Mac OS上已经自带了,以下的安装和各类操作,都将以Mac上使用为例,Windows稍微修改下即可。然后需要安装turicreate
,
pip install -U turicreate
即可。TuriCreate
依赖很多其他库,依赖库又依赖其他的库,所以直接安装可能会与当前的各种已安装包出现冲突,错误。这里使用viertualenv
来安装会方便很多,创建一个虚拟环境,可以直接安装,详细操作可见TuriCreate文档里的Installation部分。 安装后如图:
然后在训练集目录下新建一个turi.py
的文件,用IDE打开,然后就可以开动了!
turi.py
:
#0 导入需要的库
import turicreate as tc
import os
#1. 导入之前放的图片文件夹mydataset
data = tc.image_analysis.load_images('mydataset', with_path=True)
#2. 用python的lambda来提取标签(文件夹名字)
data['name'] = data['path'].apply(lambda path: os.path.basename(os.path.dirname(path)))
#3. 保存下sframe
data.save('mydataset.sframe')
#4. turicreate的可视化
data.explore()
data.show()
这里展示的为图像分类问题,因此#1使用的是image_analysis
。SFrame
是一种可伸缩表格数据,详细定义可可参考github - SFrame。
运行后可能会报warning
,这报错也就是之前提到的,TuriCreate
支持的是JPEG和PNG格式,其他不支持。
Unsupported image format. Supported formats are JPEG and PNG file: /Users/Chen/Downloads/testml/mydataset/.DS_Store
然后由#4步骤中的操作,会将数据进行可视化.
有时候explore会卡在loading过程中,可参见此issue, show()可正常使用。
在同一个目录中,可新建一个turi_train.py
,并上一个过程中保存的mydataset.sframe
来进行训练。具体代码如下: turi_train.py
:
import turicreate as tc
# 1. 导入之前生成的sframe文件
data = tc.SFrame('mydataset.sframe')
# 2. 分0.8的数据作为训练样本,0.2的作为测试样本
train_data, test_data = data.random_split(0.8)
# 3. 生成模型
model = tc.image_classifier.create(train_data, target='name')
# 4. 测试样本数据
predictions = model.predict(test_data)
# 5. 计算样本数据的精确性
metrics = model.evaluate(test_data)
print(metrics['accuracy'])
# 6. 保存model
model.save('mydataset.model')
# 7. 导出成mlmodel,以便coreML使用
model.export_coreml('mydataset.mlmodel')
其中#2的操作是为了验证我们训练出来的模型的可信度,以80%的数据来训练,剩下的作为验证样本来看看训练的数据质量怎么样。
会输出类似这样的log:
需要注意的是,每次训练的结果并不相同,尤其是验证的数据,可能为100%,也可能为80%,跟随机到的结果有关,仅供参考。
最后一行的0.95为样本测试正确率。可以通过打印predictions
和test_data['name']
来进行对比。
['cup', 'cup', 'cup', 'cup', 'cup', 'luffy', 'luffy', 'other', 'other', 'other', 'other', 'sportcup', 'sportcup', 'sportcup', 'sportcup', 'sportcup', 'sportcup', 'walle', 'walle', 'walle', 'walle', 'walle', 'walle', 'walle', 'walle', 'zoro', 'zoro', 'zoro', 'zoro', 'zoro', 'zoro', 'zoro', 'zoro']
['cup', 'cup', 'cup', 'cup', 'cup', 'luffy', 'luffy', 'other', 'other', 'other', 'other', 'sportcup', 'sportcup', 'sportcup', 'sportcup', 'sportcup', 'sportcup', 'walle', 'walle', 'walle', 'walle', 'walle', 'walle', 'luffy', 'luffy', 'zoro', 'luffy', 'luffy', 'luffy', 'zoro', 'luffy', 'zoro', 'zoro']
这个跟上一个0.95的结果非同一次训练
两者一对比,即可找到错误的判断为哪些,为了更加直观的看到具体是哪些图片出现了误识别,可打印: print test_data[test_data['name'] != predictions]['path']
然后再使用turicreate.img
来预览图片就可以自动打开错误的图片,来看是哪几种图片有问题,以及猜测可能原因。代码如下:
print test_data[test_data['name'] != predictions]['path']
wrongResults = test_data[test_data['name'] != predictions]['path']
for index in range(len(wrongResults)):
img = tc.Image(test_data[test_data['name'] != predictions]['path'][index])
img.show()
查询后发现本应该属于WallE的图:
被误识别成了luffy-路飞。可能是跟这张训练样本混淆了(以及另外3张路飞的图,也都有大面积蓝色背景):
可以看出这张图的元素是比较复杂的,样本可能还需要更多些。
真正到XCode是集成使用的,只有turi_train.py
中导出的mydataset.mlmodel
(94.2MB)。拖动文件到项目中即可:
这一步会自动生成OC的Mydataset.h
和Mydataset.m
文件。
如果需要生成swift
格式的文件,则可在 项目 - BuildSetting - 点击All - 搜索 coreml,会看到选项 CoreML Model Class Generation Language,修改即可。
在你想要使用的源文件中,先引入库:
#import <CoreML/CoreML.h>
#import <Vision/Vision.h>
#import "Mydataset.h"
关键类为VNCoreMLModel
,VNCoreMLRequest
,VNImageRequestHandler
。核心在于用VNCoreMLModel
来构建VNCoreMLRequest
,方法为: - (instancetype)initWithModel:(VNCoreMLModel *)model completionHandler:(VNRequestCompletionHandler)completionHandler
, 然后在completeHandler
里获取request
分类的结果数组,根据结果里匹配率最高的来当作识别结果。具体代码如下:
Mydataset *resnetModel = [[Mydataset alloc] init];
VNCoreMLModel *vnCoreModel = [VNCoreMLModel modelForMLModel:resnetModel.model error:nil];
VNCoreMLRequest *vnCoreMlRequest = [[VNCoreMLRequest alloc] initWithModel:vnCoreModel completionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) {
CGFloat confidence = 0.0f;
VNClassificationObservation *tempClassification = nil;
for (VNClassificationObservation *classification in request.results) {
if (classification.confidence > confidence) {
//取最大概率的模型
confidence = classification.confidence;
tempClassification = classification;
}
}
NSLog(@"识别结果:%@,匹配率:%@",tempClassification.identifier,@(tempClassification.confidence));
}];
//这里的image为输入图片
VNImageRequestHandler *vnImageRequestHandler = [[VNImageRequestHandler alloc] initWithCGImage:image.CGImage options:nil];
NSError *error = nil;
[vnImageRequestHandler performRequests:@[vnCoreMlRequest] error:&error];
if (error) {
NSLog(@"%@",error.localizedDescription);
}
这部分代码对于iOS开发来说,还是比较简单易懂的。基本的识别到这一步也就可以了。很神奇,只要这么一点点的代码和一个导入的模型,我们就完成了机器学习的工作,Apple对于开发者实在是友好。
相关阅读:
本文来自网易实践者社区,经作者陈蒙奇授权发布。