使用 iOS 11 mlmodel 进行图像分类 - 使用 coremltools 和经过训练的 .caffemodel 转换问题

Image classification with iOS 11 mlmodel - convert issue using coremltools and trained .caffemodel

似乎我在使用 coremltool 和训练有素的 .caffemodel 时遇到了一些转换问题。我能够训练和测试 caffe 狗模型(120 个类别,20k 图像)并且它通过直接 caffe 分类的测试。不幸的是,在转换为 mlmodel 之后,它并没有给我对相同输入的有效预测。

训练模型

该模型已使用 Caffe、GoogleNet、打包到 lmdb 中的超过 120 个类别的 20k 图像集和大约 500k 次迭代进行了训练。我已经准备好图像数据库和所有其他的,并把 all files together here

分类为caffe

这个 classification examplecaffe。当我尝试 运行 针对受过训练的 caffemodel 的分类请求时 - 它工作得很好,概率很高 (80-99%),正确的结果:

分类用AppleiOS 11CoreML

不幸的是,当我试图将此 DTDogs.caffemodeldeploy.txt 打包到 Apple iOS 11 CoreML 消耗品的 .mlmodel 中时,我得到了不同的预测结果。实际上,加载和使用模型没有错误,但我无法获得有效的分类,所有预测的置信度均为 0-15%,并且标签错误。为了正确测试它,我使用了与 caffe:

直接分类时使用的完全相同的图像

我也从这里尝试 the pre-trained and pre-packed models 使用我的 iOS 应用程序 - 它们工作正常,所以这似乎是打包过程的问题。

我错过了什么?


这是 caffe 的分类示例:没有问题,正确答案 (python):

import numpy as np
import sys
import caffe
import os
import urllib2
import matplotlib.pyplot as plt
%matplotlib inline

test_folder = '/home/<username>/Desktop/CaffeTest/'
test_image_path = "http://cdn.akc.org/content/hero/irish-terrier-Hero.jpg"

# init caffe net
model_def = test_folder + 'deploy.prototxt'
model_weights = test_folder + 'DTDogs.caffemodel'
# caffe.set_mode_gpu()
net = caffe.Net(model_def, model_weights, caffe.TEST) 

# prepare transformer
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2,0,1))  
transformer.set_raw_scale('data', 255)
transformer.set_channel_swap('data', (2,1,0))
net.blobs['data'].reshape(1, 3, 256, 256)  

test_image = urllib2.urlopen(test_image_path) 
with open(test_folder + 'testImage.jpg','wb') as output:
  output.write(test_image.read())

image = caffe.io.load_image(test_folder + 'testImage.jpg')
transformed_image = transformer.preprocess('data', image)
net.blobs['data'].data[...] = transformed_image

# classify
output = net.forward()
output_prob = output['prob'][0]
output_prob_val = output_prob.max() * 100
output_prob_ind = output_prob.argmax()
labels_file = test_folder + 'labels.txt'
labels = np.loadtxt(labels_file, str, delimiter='\t')

plt.imshow(image)
print 'predicted class is:', output_prob_ind
print 'predicted probabily is:', output_prob_val
print 'output label:', labels[output_prob_ind]

这里是使用coremltools打包DTDogs.mlmodel模型的例子。我看到生成的 .mlmodel 文件比原始 .caffemodel 文件小两倍,但它可能是 coremltools (python):

进行的某种归档或压缩优化
import coremltools;
caffe_model = ('DTDogs.caffemodel', 'deploy.prototxt')
labels = 'labels.txt'
coreml_model = coremltools.converters.caffe.convert(caffe_model, class_labels = labels, image_input_names= "data")
coreml_model.short_description = "Dogs Model v1.14"
coreml_model.save('DTDogs.mlmodel')

这是在应用程序中使用 DTDogs.mlmodel 的示例。我正在使用常规图像选择器来选择用于 .caffe 分类测试 (swift) 的相同图像:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    picker.dismiss(animated: true)
    print("Analyzing Image…")

    guard let uiImage = info[UIImagePickerControllerOriginalImage] as? UIImage
        else { print("no image from image picker"); return }
    guard let ciImage = CIImage(image: uiImage)
        else { print("can't create CIImage from UIImage"); return }

    imageView.image = uiImage

    do {
        let model = try VNCoreMLModel(for: DTDogs().model)
        let classificationRequest = VNCoreMLRequest(model: model, completionHandler: self.handleClassification)
        let orientation = CGImagePropertyOrientation(uiImage.imageOrientation)
        let handler = VNImageRequestHandler(ciImage: ciImage, orientation: Int32(orientation.rawValue))
        try handler.perform([classificationRequest])
    } catch {
        print(error)
    }
}

通常在这些情况下发生的情况是 Core ML 传递到模型中的图像格式不正确。

对于 Caffe 模型,您通常需要在调用 caffe.convert() 时设置 is_bgr=True,并且您通常必须传入将从中减去的 RGB 平均值输入图像,也可能是缩放值。

换句话说,Core ML 需要做与您的 transformer 在 Python 脚本中做的相同的事情。

像这样:

coreml_model = coremltools.converters.caffe.convert(
    caffe_model, class_labels = labels, image_input_names= "data",
    is_bgr=True, image_scale=255.)

我不确定是否需要 image_scale=255.,但值得一试。 :-)