今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

iOS CreateML的使用

CreateML 使用#

背景#

业务需求,想要通过拍照识别照片中指定物体的数量或者物体的种类。而这种物体的模型网上没有训练好的,需要从头开始。所以调研了苹果的 createML 的实现方案,具体操作如下:

需求是:通过拍照识别照片中指定物体的数量,实现方案大致有几种:

  • 通过第三方平台,训练数据,生成模型,提供前端使用
  • 自己搭建平台,训练数据,生成模型,提供前端使用
  • 通过苹果的 CreateML 工具,训练数据,生成模型,供 iOS 使用或转换成其他模型使用

对比可以发现,通过苹果的 CreateML 工具,可以省去搭建平台的过程。下面来看看怎么使用 CreateML。

使用 CreateML 的整体流程是:

  1. 有大量的样本
  2. 标注所有的样本
  3. 用这些样本训练生成模型
  4. 验证模型的识别率
  5. 测试模型效果
  6. 导出模型供使用
image

需求是通过拍照识别照片中指定物体的数量,所以对我来说样本就是照片,下面就来看下怎么生成 CreateML 训练需求的标注信息。

使用#

样本照片标注#

首先要有大量的样本照片,这里由于是调研测试,所以选取 20 张照片,照片来源是百度图片。。。麻烦的是照片标注,由于苹果 CreateML 训练需要指定格式的JSON文件,格式如下:


[
    {
        "image": "图片名字.jpg",
        "annotations": [
            {
                "label": "标签名字",
                "coordinates": {
                    "x": 133,
                    "y": 240.5,
                    "width": 113.5,
                    "height": 185
                }
            }
        ]
    }
 ]

所以找一个能直接导出Create ML支持的JSON格式的标注工具尤其重要。这里参考Object Detection with Create ML: images and dataset使用roboflow来进行标注,具体使用如下:

roboflow登陆后,会有引导步骤,建立 workSpace,workSpace 下新建项目,也可以邀请用户加入到同一个 workSpace,界面如下:

image

点击Create Project,需要输入项目的名字,选择项目的License,选择项目的功能,输入要标记的物体的名字,界面如下:

image

然后导入照片,比如去网上下载十几张钢管的照片,训练识别钢管(通常应该是很多很多照片,样本越多,训练出的模型就越准确这里只用于演示流程,所以选取的照片不多);注意照片上传完成后,不要点击右上角的Finish Uploading,因为此时还没有标注,注意All Images(18)Annotated(0),说明没有一张标注的。

image

双击任意一张照片,即可进入标注页面,

  1. 在标注页面,要先选择最右侧标注工具,默认是长方形框框标注;
  2. 然后选择标注的范围,注意的是使用曲线标注时,起始和结束的两个点要闭合;
  3. 然后在弹出的 Annotation Editor 框中,输入要标记的 label,然后点击保存;
  4. 如果有多个标记,则重复上面的过程;
  5. 所有标记都完成后,点击左侧的返回即可;
image

所有照片都标记好之后,点击 Finish Uploading,会弹出如下提示框,选择如何分配照片给TrainValidTest,默认是所有照片都用于Train,如下:

image image

点击 Continue,进入下一步。可以看到步骤 1 和 2 都已完成,如果想要编辑上面两个步骤,也可以移动鼠标到对应模块的右半边,会出现编辑按钮;目前所在步骤 3,Auto-Orient意思是Discard EXIF rotations and standardize pixel ordering.,如果不需要可以删除这步;Resize是把照片重新调整大小。也可以添加Preprosession Step。这个步骤的预处理的目的是用于节省最终训练时间。

image

点击 Continue,进入步骤 4,这个步骤的目的是通过生成训练集中每个图像的增强版本,为模型创建新的训练示例以进行学习。如果不需要,可以直接点击 Continue,进入下一步。

image

最后一步,就是生成结果,点击 Generate,然后等待结果出现。结果如下:

image

从上图可以看到,所有标注的照片、TrainValidationTest的数量,Start Training按钮是网站提供的在线训练功能,这里用不到;需要的是导出 CreateML 类型的标注信息,所以点击Export,在弹出的弹窗中选择要导出的格式,这个网站最好用的就是导出支持的格式很多,这里选择CreateML,然后选中download zip to computer,点击 Continue。

image

在 finder 中找到下载的xxx.createml.zip,解压,查看文件夹,内容如下,可以看到TrainValidTest都已分类好,查看Train文件夹下_annotations.createml.json,可以看到格式和CreateML需要的一致,可以直接使用。

image

CreateML 使用#

选择 Xcode -> Open Developer Tool -> Create ML,打开 CreateML 工具,如下:

image

然后进入选择项目界面,如果已有 CreateML 项目,则选择打开;没有则选择要保存的文件夹,然后点击左下角的New Document进入创建页面,创建页面如下:

image

根据需要,选择要创建的模板,这里选择Object Detection,然后输入项目名字和描述,如下:

image

这里歪个楼,看到Text ClassificationWord Tagging,想起来商店里的短信过滤 APP,可能就是用大量的垃圾短信,然后通过 CreateML 训练出模型,再导入到项目中,在项目中继续用 Core ML 来不断更新这个模型,从而针对每个人做到识别效果更好。

点击下一步,进入主界面,如下图所示,可以看到有以下几部分:

  • 上方中间的SettingTraningEvaluationPreviewOutput代表 CreateML 的几个步骤,可以切换查看,目前后面个步骤都是空的,因为还没有训练的结果。
  • 上方左侧的Train按钮是在中间部分TrainingData选择导入数据后,开始训练。
  • Data 部分分别可以用刚刚下载的文件夹中的内容,但是首先要导入Train的文件,并且等训练结果出来后,可以继续后面的步骤。
  • Parameters 部分
    • Algorithm 是选择算法,默认的FullNetworking是基于 YOLOv2 的,可以选择TransferLearningTransferLearning与样本数量相关,训练出的模型也更小。至于什么是 YOLOv2,可以参考这篇文章A Gentle Introduction to Object Recognition With Deep Learning,看完会对深度学习的几种算法有大致的理解
    • Iterations训练的迭代次数,并不是越大越好,也不能太小。
    • Batch Size,每次迭代训练的数据大小,值越大占用的内存越大。参考What is batch size in neural network?
    • Grid Size,参考gridsize,YOLO 算法中分割图片为多少块。
image

这里设置 Iterations 为 100,其余采用默认设置,然后点击Train,会进入Training的步骤,最终结果如下:

image

然后选择Testing Data,由于下载的标注中Test只有两条数据,所以可以接着用Train来作为Testing Data的数据,点击Test,然后等待结果出现,如下:

image

这个页面上的I/U 50%Varied I/U的意义可参考CreateML object detection evaluation

I/U means "Intersection over Union". It is a very simple metric which tells you how good the model is at predicting a bounding box of an object. You can read more about I/U in the "Intersection over Union (IoU) for object detection" article at PyImageSearch.
So:
I/U 50% means how many observations have their I/U score over 50%
Varied I/U is an average I/U score for a data set

也可以通过 Preview 更直观的查看识别效果,如下,点击添加文件,选择所有钢管照片

image

然后一张张查看,就可以看到每张能不能识别出

image

最后,切到 Output,点击 Get,导出模型xxx.mlmodel,如下:

image

训练出的模型 mlmodel 的使用#

新建一个项目,把训练出的模型导入到项目中,如下:

image

然后导入Vision库,加载一张照片,然后生成VNImageRequestHandler,用生成的 handler 去识别,代码如下:


class PipeImageDetectorVC: UIViewController {


    // MARK: - properties
    fileprivate var coreMLRequest: VNCoreMLRequest?
    fileprivate var drawingBoxesView: DrawingBoxesView?
    fileprivate var displayImageView: UIImageView = UIImageView()
    fileprivate var randomLoadBtn: UIButton = UIButton(type: .custom)
    
    // MARK: - view life cycle
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        setupDisplayImageView()
        setupCoreMLRequest()
        setupBoxesView()
        setupRandomLoadBtn()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    
    // MARK: - init
    fileprivate func setupDisplayImageView() {
        view.addSubview(displayImageView)
        displayImageView.contentMode = .scaleAspectFit
        displayImageView.snp.makeConstraints { make in
            make.center.equalTo(view.snp.center)
        }
    }
    
    fileprivate func setupCoreMLRequest() {
        guard let model = try? PipeObjectDetector(configuration: MLModelConfiguration()).model,
              let visionModel = try? VNCoreMLModel(for: model) else {
            return
        }
        
        coreMLRequest = VNCoreMLRequest(model: visionModel, completionHandler: { [weak self] (request, error) in
            self?.handleVMRequestDidComplete(request, error: error)
        })
        coreMLRequest?.imageCropAndScaleOption = .centerCrop
    }
    
    fileprivate func setupBoxesView() {
        let drawingBoxesView = DrawingBoxesView()
        drawingBoxesView.frame = displayImageView.frame
        displayImageView.addSubview(drawingBoxesView)
        drawingBoxesView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        self.drawingBoxesView = drawingBoxesView
    }
    
    fileprivate func setupRandomLoadBtn() {
        randomLoadBtn.setTitle("随机加载一张图片", for: .normal)
        randomLoadBtn.setTitleColor(UIColor.blue, for: .normal)
        view.addSubview(randomLoadBtn)
        let screenW = UIScreen.main.bounds.width
        let screeH = UIScreen.main.bounds.height
        let btnH = 52.0
        let btnW = 200.0
        randomLoadBtn.frame = CGRect(x: (screenW - btnW) / 2.0, y: screeH - btnH - 10.0, width: btnW, height: btnH)
        
        randomLoadBtn.addTarget(self, action: #selector(handleRandomLoad), for: .touchUpInside)
    }
    
    // MARK: - utils
    
    
    // MARK: - action
    fileprivate func handleVMRequestDidComplete(_ request: VNRequest, error: Error?) {
        let results = request.results as? [VNRecognizedObjectObservation]
        DispatchQueue.main.async {
            if let prediction = results?.first {
                self.drawingBoxesView?.drawBox(with: [prediction])
            } else {
                self.drawingBoxesView?.removeBox()
            }
        }
    }
    
    @objc fileprivate func handleRandomLoad() {
        let imageName = randomImageName()
        if let image = UIImage(named: imageName),
            let cgImage = image.cgImage,
            let request = coreMLRequest {
            displayImageView.image = image
            let handler = VNImageRequestHandler(cgImage: cgImage)
            try? handler.perform([request])
        }
    }
    
    // MARK: - other
    fileprivate func randomImageName() -> String {
        let maxNum: UInt32 = 18
        let minNum: UInt32 = 1
        let randomNum = arc4random_uniform(maxNum - minNum) + minNum
        let imageName = "images-\(randomNum).jpeg"
        return imageName
    }
}

最终效果如下,完整代码已放在 Github: CreateMLDemo

image

总结#

本篇介绍了CreateML的整体流程,从模型标注 ,到训练生成模型,再到使用模型,希望大家能对CreateML有大致的了解。后面再来研究下,已导入项目中的模型,能否更新,如何更新?以及尝试做自己的短信过滤 APP。

参考#

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。