iOS(swift): Core ML的使用

10 篇文章 1 订阅
6 篇文章 0 订阅

    神经网络模型在移动端可以利用CPU加速,但是,如果算法同事那边为了更好的效果在尝试不同的模型,相应的在部署iOS移动端这块就需要不断的修改网络模型。Core ML的出现使部署移动端的任务量可以缩减到最少两行代码。

    苹果官方给出了如何利用Core ML的demo,并且给出了两个例子。一个是根绝输入相关数据预测房价,另外一个是输入图片给出分类结果(结合了Vision框架)。打开Xcode ->window->Developer Documentation,输入Core ML,得到相应的官方介绍。

结合官方文档,首选任务是如何获取一个Core ML模型,该模型文件的后缀为.mlmodel。

Core ML Tool:

      该工具是Python语言写的工具包,支持很多机器学习平台生成的模型转成mlmodel文件,如caffe、keras等,可惜不支持TensorFlow。如果使用TensorFlow,可利用GitHub上的第三方平台进行转换(tf-coreml),并且只支持部分OP操作。现在话题转移到TensorFlow模型转换成mlmodel的工具上。从该项目上下载代码后,暂时感觉有两个比较重要的功能,一个在inspect_pb.py文件里面,调用该文件里面的inspect方法(需要输入两个参数,第一个参数为pb文件路径,第二个为输出txt文件路径)。该方法是读取pb文件里所有OP操作的名字以及相应配置,并写入txt文件。读取txt文件信息你可以了解到该模型的每一次OP的名字,并且到这里tensor的shape是多少。拉到该txt文件的最下面,你还可以知道该模型每一个OP操作一共执行了多少次。如图所示:

    另外一个比较重要的文件肯定是如何利用该工具将pb文件转成mlmodel文件了。在tests文件夹的tests_tf_converter.py的最后,可以看到使用tf_converter.convert()方法,点到convert()方法里面,可以看到后很多参数,但是必须输入的是前三个,tf模型路径,输出mlmodel的路径以及输出tensor的名字,这里感觉input_name_shape_dict也是需要给出的,毕竟如果该网络不是全卷机网络,那么该输入的图片尺寸就必须是固定的,那么你就应当给出输入tensor或者placeholder的具体shape。另外,这其中有个很容易忽略的参数,如果忽略了,那么在iOS采用Core ML进行模型推理的时候会变得很麻烦(稍后会解释),该参数就是image_input_names。该参数是一个string类型,表示一个tensor的名字,给与该参数赋值表明该tensor接受的是一个image(pixelBuffer)类型,因此一般是输入tensor的名字:例如:

       模型的可视化

我们查看模型的结构,除了使用netron该神器(可以查看很多机器学习平台生成的模型结构,caffe、TensorFlow、MXNet等等),也可以使用Core ML Tool,很简单,两行代码: 

import coremltools

model =  coremltools.models.MLModel('HousePricer.mlmodel')

model.visualize_spec()

mlmodel文件的使用

通过上述步骤,应该会顺利得到mlmodel文件。这里就只是展示了官方给的demo上的mlmodel文件进行说明。首先拖入到xcode后点击MobileNet.mlmodel会看到:

首先,红框地方表示输入的一些信息,可以看到输入的是一个image,尺寸为224x224。输入的是一个字典类型和一个classLabel总集。在这里之所以标红是因为上面说的image_input_names在pb文件转成mlmodel文件时,设置该参数的问题。如果不设置,那么你可能会看到类似这种输入信息:

该MultiArray输入类型在Python中的数组处理应该比较简单,Python本身很多图片处理框架读取一张图片直接就是三维数组的形式,但是在iOS中就比较麻烦。所以在转换模型的时候,image_input_names参数一定要设置好。

     点击上图中红圈标记的地方后,会跳如mlmodel文件自动生成的一个同名swift文件。它是有三个类组成,一个是模型输入类、模型类、模型输出类。输出和输入这两个类的使用和创建还是比较简单的,他们都继承自MLFeatureProvider。而且他们的方法还都很类似,其中一个叫做MLFeatureValue,这个类很有意思。查看他的相关解释会发现,他几乎hold住了所有Core ML中相关数据类型的一个类,比如imageBufferValue,multiArrayValue等等。

这里应当注意的是,在accessing the value 这个解释下面,他的每个属性都是在他初始化传入数据格式时就已经确定,比如说初始化的时候为传入的数据为MLMutiArray,那么只有他的multiArrayValue这个属性不为空。曾经用到的时候,天真的以为初始化时传入MLMutiArray,那么通过他的imageBufferValue属性就可以得到pixelBuffer的图像格式,这样就省去了MLMutiArray到pixelBuffer格式转换的麻烦。所以当我们用到类似风格转换的神经网络图像处理时,输出来的是MLMutiArray的格式,只能老老实实的用相关方法转成image了。还好已经有大神为我们写好相关方法——CoreMLHelper,只是速度有点慢。利用CoreMLHelper项目中的转换方法,可以得出能够显示的image:

//image为从相册取出的image,imageToPixelBuffer是image->pixelBuffer的格式转换方法
            let pixBuffer = self.imageToPixelBuffer(image: image, width: 1280, height: 720)!
            guard let outModel = try?self.model.prediction(src_input__0: pixBuffer) else {
                fatalError("Visionular warning : Unexpected runtime error.")
            }
            let resultMultiArray = outModel.imgout__0
            
            let result_image = resultMultiArray.image(min:0,max:255)
            

不过,在仔细研究了Core MLtools后发现了即使输入和输出是MLMutiArray,但是通过该工具是可以在次转换的。即通过tf_coreml工具转换后,还可以继续使用苹果自家的Core ML Tool工具把格式以及一些描述信息给添加上。由于主要是输出的数据格式转换,那么这里就只给了输出的格式转换,因此还是要多关注一下Core ML Tool 具体的操作。

def export_coreml_image(image, array_shape):
    from coremltools.proto import FeatureTypes_pb2 as ft
    channels, height, width = array_shape
    if channels == 1:
        image.type.imageType.colorSpace = ft.ImageFeatureType.ColorSpace.Value('GRAYSCALE')
    elif channels == 3:
        image.type.imageType.colorSpace = ft.ImageFeatureType.ColorSpace.Value('RGB')
    else:
        raise ValueError("Channel Value %d not supported for image inputs" % channels)
    image.type.imageType.width = width
    image.type.imageType.height = height

if __name__ == '__main__':
    path = '/Users/MacBook/Desktop/v1.0.0.1_pb/Out_format_isImage.mlmodel'

    coreml_model = tf_converter.convert(tf_model_path="/Users/MacBook/Desktop/v1.0.0.1_pb/coustome.pb",
                          mlmodel_path="/Users/MacBook/Desktop/v1.0.0.1_pb/out_model.mlmodel",
                          image_input_names="src_input:0",
                          output_feature_names=['imgout:0'],
                          input_name_shape_dict={'src_input:0':[1, 720, 1280, 3]})
    spec = coreml_model.get_spec()
    toolmodel = coremltools.models.MLModel(spec)
    image_output = spec.description.output[0]
    output_array_shape = tuple(image_output.type.multiArrayType.shape)
    export_coreml_image(image_output, output_array_shape)
    toolmodel.save(path)

那么,在将转出来的模型利用xcode可以看到:

输出是image格式,第二个箭头处的description则是空白的,比如我们可用:
coreml_model.output_description[outImageName] = 'out_image'

outImageName是输出的tensor的名字,这里是imgout。好比代码中的:output_feature_names=['imgout:0']。

其他的信息设置还有:

coreml_model.author = 'John Smith'
coreml_model.license = 'BSD'
coreml_model.short_description = 'Predicts the price of a house in the Seattle area.'
# Set feature descriptions manually
model.input_description['bedroom'] = 'Number of bedrooms'
model.input_description['bathrooms'] = 'Number of bathrooms'
model.input_description['size'] = 'Size (in square feet)'
# Set the output descriptions
model.output_description['price'] = 'Price of the house'

# Save the model
model.save('HousePricer.mlmodel')

mlmodel的瘦身与选择运行设备

       在官网提供的方法中,默认直接利用的MobileNet()方法加载了mlmode模型。加入我们要选择设备运行方式呢,即:使用CPU还是GPU甚至NPU来对模型进行加速呢。苹果已经提供了相应的配置方法。这里就采用懒加载的方式在里面进行设置了:

    lazy var model : MobileNet = {
        do{
            let config = MLModelConfiguration.init()
            config.computeUnits = MLComputeUnits.cpuAndGPU
            let model = try MobileNet.init(configuration: config)
            return model
        }catch{
           fatalError("Failed to load MobileNet ML model: \(error)")
        }
    }()

通过MLModelConfiguration类可以对模型使用哪种设备来加速进行配置。MLComputeUnits有cpuOnly、cpuAndGPU、all。前面两个就比较好解释了,最后一个all是cpu、GPU、还有苹果自家芯片A11以及后代产品中自带的NPU。由此也可以看出,我们没有办法独自选择GPU或者NPU进行加速。在这里稍微提一下,如果采用带有NPU的设备,并且利用了NPU(也就是配置中选择了all,或者直接采用let mode = MobileNet()创建的model。)结果有可能不对(起码我在项目中遇到了该问题,配置选择cpuOnly或者CPUAndGPU没有问题),对于该问题,GitHub相关项目上也有人提到,很多人猜测可能是NPU中数据处理的格式类型有可能不对(比如说cpu中用到的是float32,而NPU用到的是float8等等,当然这也只是猜测)。

     一般神网训练出来的模型比较大,在预测精度降低不是那么明显的情况下,选择量化方案可大大降低模型的大小。Core ML Tool自带了该工具。用法特别简单,直接在安装了Core ML Tool工具的Python包中调用相关API即可:

import coremltools

from coremltools.models.neural_network.quantization_utils import *
urlPath = 'Path/model.mlmodel'
desPath = 'Path/model_8.mlmodel'
model = coremltools.models.MLModel(urlPath)
line_quant_model_8 = quantize_weights(model, 8, "linear")
line_quant_model_8.save(desPath)

主要是quantize_weights()该方法,第一个参数不用解释,第二个参数即量化的bit量,可以为16,8,4,甚至为2。第三个参数即量化方法,这里使用的是线性量化,其他的量化方法可点进去看一下,比如说kmeam量化方法等。

需要指出的是,在我测试的项目中,模型尽管变小了,但是计算速度并没有提高,甚至还有所下降。当然精度肯定是下降的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值