IOS机械学习初探

作者: o翻滚的牛宝宝o | 来源:发表于2018-08-23 14:41 被阅读163次

    背景


    机械学习是目前比较热门的技术,它的英文名称叫做Machine Learning简称ML。

    早在2017年WWDC上,Apple公司就宣布其提供的框架Core ML已经被每个主流ML平台支持,并可以将现有的训练好的模型转换成Core ML支持的模型。

    Turi Create在2017年WWDC之后被收购,其简化了定制机器学习模型的开发,降低了开发门槛。开发者无需成为机器学习专家,就可以进行机械训练,导出Core ML支持的模型。但是它使用的语言是Python。

    2018年WWDC上,apple宣布了新的ML开发工具Create ML,这次使用的是自己的语言swift并集成到Xcode中。虽然目前只完成了Turi Create的部分功能,但后续会不断扩展优化。

    本文就是介绍使用Create MLCore ML框架,体验一次机械学习从无到有的过程。

    开发环境


    想要使用Create ML需要满足2个条件:

    • 一台Mac,运行macOS 10.14 Mojave beta
    • Xcode 10.x beta

    写这篇文章的时候 Mojave 版本是10.14 beta 8,Xcode 10 beta 6。下载链接:https://developer.apple.com/download/

    创建一个自己训练的模型


    准备机械学习的数据样本

    我想做一个自动识别各种动物的神经模块,因此,我准备了一些动物的图片,作为数据样本。因为时间原因,从百度上下80张动物图片,分别为兔子、猫、狗、老虎,每个种类各20张。这里对图片有些要求,尽量清晰,并且最好只有一个动物。

    数据样本越多,训练的效果越好,不过相对的训练的时间也越长。

    图片分类.png

    准备机械学习效果验证的数据样本

    有了学习数据样本后,还需要准备一个验证样本,来验证机械学习训练结果的准确性,目录结构和之前一样,每个动物我准备了10个图片。

    测试数据.png

    开始机械训练


    用xcode10创建机械学习库

    打开xcode10,创建一个macOS版的playground。

    macOS_playground.png

    然后输入以下代码:

    import CreateMLUI
    
    let builder = MLImageClassifierBuilder()
    builder.showInLiveView()
    

    点击运行并打开辅助界面就可以看到Create ML的训练界面。

    机械训练界面.png

    导入样本数据进行训练

    将之前准备的数据样本,pets文件夹拖入右侧的虚线框就可以自动进行训练。

    pets文件夹里面有很多字文件夹,每个文件夹成为数据集,里面是同一类图片数据,字文件夹的名称就是这一类的输出结果。字文件夹名字不能用中文,会在后面集成自己APP的时候输出乱码,应该有其他方法解决。

    训练结果.png

    上面这张图片是我80个测试数据样本的训练结果,第一个表格是有3个表格头,分别是Images ProcessedElapsed TimePercent Complete 代表的是图片总个数、花费时间和完成进度。在我的电脑配置下,80个图片大约用了14秒。

    每次训练完成,系统会在样本数据中随机抽取10%的数据样本进行准确度验证,第二个表格是验证结果。我的样本数据得到了100%的验证结果。

    从上面这个训练过程可以看出,我们并不需要进行代码编写机械学习的算法,这些Create ML都帮我们实现了,并且提供了一套优秀的提取图片特征算法,我们只需要提供样本数据就能得到训练后的模型,因此的确不需要专业的机械学习知识。

    进行准确度验证

    经过以上步骤,我们能够得到一个初步的神经模型,但是我们还需要测试其是否准确,是否达到我们的要求,因此还需要进行验证。将之前准备好的pets-test文件夹(目录结构和pets一致)拖入右上角虚线框就可以进行验证。

    验证结果.png

    可以看到,这次训练的模型结果有一张图片出错,我传进去是猫的图片,它是被成了兔子。准确率只有98%。根据这个结果,我们可以选择是否使用这个模型。

    提高模型准确性

    提供准确性有很多方法,加大数据样本数量,优化样本数据等等。Create ML提供一些优化方法可以在训练之前设置,如下图:

    增加样本.png

    我们可以给图片样本增加参数,如翻转flipping, 旋转rotating, 斜切shearing或改变曝光exposure等,打上勾就会增加效果并进行排列组合增加样本数量。

    官方提供的解释.png

    我们还可以增加iterations的值提高准确性,这个应该和算法有关。不过这里需要注意的是,勾选越多就意味着样本数据越多,训练的时间也就越长。

    下面是优化过的模型结果,正确率有一定提高。

    正确率提高.png

    最后一步就是导出数据模型,我们下面将在工程中使用它。

    保存数据模型.png

    在工程中使用训练好的模型


    新建一个工程

    导出的模型是一个以.mlmodel结尾的文件,我的模型大小不太大,大概53KB左右。新建一个singleView工程,然后直接将该模型拖进来就可以直接使用。

    新建工程.png

    .mlmodel文件会自动根据项目的语言转换成OC或者swift代码,点击中间紫色图标右边的箭头就可以进入查看对应的代码。

    api代码.png

    从上图我们可以看到,主要有3个类animal,animalInput,animalOutput,分别是主类,输入类和输出类。

    animalOutput是输出结果类,里面有个属性的名字叫做classLabel就是相似度最高的结果,另外一个属性classLabelProbs就是每个结果的可能性占比,占比越高可能性越大。

    animalInput是输入类,其支持一个初始化方法- (instancetype)initWithImage:(CVPixelBufferRef)image;,可以看出需要把图片转换成CVPixelBufferRef格式然后才能使用。

    图片转化

    前面我们已经说到不能直接使用UIImage格式,而需要转换成CVPixelBufferRef格式下面是转换代码:

    - (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image{
        
        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                             nil];
    
    CVPixelBufferRef pxbuffer = NULL;
    
    CGFloat frameWidth = CGImageGetWidth(image);
    CGFloat frameHeight = CGImageGetHeight(image);
    
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
                                          frameWidth,
                                          frameHeight,
                                          kCVPixelFormatType_32ARGB,
                                          (__bridge CFDictionaryRef) options,
                                          &pxbuffer);
    
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
    
    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
    NSParameterAssert(pxdata != NULL);
    
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    
    CGContextRef context = CGBitmapContextCreate(pxdata,
                                                 frameWidth,
                                                 frameHeight,
                                                 8,
                                                 CVPixelBufferGetBytesPerRow(pxbuffer),
                                                 rgbColorSpace,
                                                 (CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);
    CGContextConcatCTM(context, CGAffineTransformIdentity);
    CGContextDrawImage(context, CGRectMake(0,
                                           0,
                                           frameWidth,
                                           frameHeight),
                       image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);
    
    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
    
    return pxbuffer;
    }
    

    这里还有一点需要主注意,从Model Evaluation Parameters的表格中可以看出,输入的图片规格需要时299 X 299,因此还需要将UIImage先转换成299 X 299大小再进行转换。

    下面是转换代码:

    - (UIImage *)scaleToSize:(CGSize)size image:(UIImage *)image {
        UIGraphicsBeginImageContext(size);
        [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
        UIImage* scaledImage =  UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return scaledImage;
    }
    

    模型的使用

    在使用前别忘了加入头文件#import "animal.h"

    我这里是使用从相册中选取一张图片,然后判断该图片是什么类型的动物,代码如下:

    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
    //获取图片
        UIImage *image = info[UIImagePickerControllerOriginalImage];
        [self dismissViewControllerAnimated:YES completion:nil];
        self.photoView.image = image;
        UIImage * newSizeImage = [self scaleToSize:CGSizeMake(299, 299) image:image];
    
        CVPixelBufferRef imageRef = [self pixelBufferFromCGImage:newSizeImage.CGImage];
    
        NSError * error = nil;
        animalOutput * outPut = [_netModel predictionFromImage:imageRef error:&error];
        if (error == nil) {
            NSNumber * num = [outPut.classLabelProbs objectForKey:outPut.classLabel];
            NSString * resultStr = [NSString stringWithFormat:@"%@ : %.2lf",outPut.classLabel,[num doubleValue]];
    
            self.resultLabel.text = resultStr;
        }
        else {
            self.resultLabel.text = error.description;
        }
    
    }
    

    最终结果

    最终结果.PNG 最终结果2.PNG 最终结果3.PNG

    相关文章

      网友评论

        本文标题:IOS机械学习初探

        本文链接:https://www.haomeiwen.com/subject/hbzpiftx.html