CoreML 模型在应用内更新后分类没有不同

CoreML model is not classifying differently after updating it in-app

我正在为我的论文创建一个应用程序,到目前为止,我已经成功地创建了一个可更新的图像分类模型,我使用 Keras 对其进行了训练并使用 coremltools 对其进行了转换。 (我将提供用于训练和转换模型的代码,因为我认为它与我的问题有关,以及我用来在应用程序内更新模型的代码。)我可以成功地对图像进行分类并且模型有效正如预期的那样,但更新部分没有。在我对图像执行更新并尝试使用 "updated" 模型对同一图像进行分类后,我得到了相同的分类。连一点信心上的差别都没有。

模型生成和转换在 Python 中完成。应用程序是用 Swift.

编写的

这是创建模型的代码:

from keras.preprocessing.image import ImageDataGenerator 
from keras.models import Sequential 
from keras.layers import Conv2D, MaxPooling2D 
from keras.layers import Activation, Dropout, Flatten, Dense, BatchNormalization
from keras import backend as K 

img_width, img_height = 224, 224

train_data_dir = 'data/train/'
validation_data_dir = 'data/validate/'
nb_train_samples = 500
nb_validation_samples = 40
epochs = 20
batch_size = 25

if K.image_data_format() == 'channels_first': 
    input_shape = (3, img_width, img_height) 
else: 
    input_shape = (img_width, img_height, 3) 

model = Sequential() 
model.add(Conv2D(32, (2, 2), input_shape=input_shape)) 
model.add(Activation('relu')) 
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(BatchNormalization())

model.add(Conv2D(64, (2, 2))) 
model.add(Activation('relu')) 
model.add(MaxPooling2D(pool_size=(2, 2))) 
model.add(BatchNormalization())

model.add(Flatten()) 
model.add(Dense(64)) 
model.add(Activation('relu')) 
model.add(Dropout(0.5)) 
model.add(Dense(2))
model.add(Activation('softmax'))

model.summary()

model.compile(loss = 'categorical_crossentropy', 
                optimizer = 'sgd', 
                metrics = ['accuracy']) 

train_datagen = ImageDataGenerator()

test_datagen = ImageDataGenerator() 

train_generator = train_datagen.flow_from_directory(train_data_dir, 
                              target_size =(img_width, img_height), 
                     batch_size = batch_size, class_mode ='categorical') 

validation_generator = test_datagen.flow_from_directory( 
                                    validation_data_dir, 
                   target_size =(img_width, img_height), 
          batch_size = batch_size, class_mode ='categorical') 

model.fit_generator(train_generator, 
    steps_per_epoch = nb_train_samples // batch_size,
    epochs = epochs, validation_data = validation_generator, 
    validation_steps = nb_validation_samples // batch_size) 

model.save('ModelsH5/catndog_small.h5')

以下是将其转换为 CoreML 并使其可更新的代码:

import coremltools
from coremltools.models.neural_network import SgdParams
from coremltools.models import MLModel

model = "catndog_small"
coreml_model_path = 'ModelsML/' + model + '.mlmodel'

neuralnetwork_spec = coremltools.utils.load_spec(coreml_model_path)
builder = coremltools.models.neural_network.NeuralNetworkBuilder(spec=neuralnetwork_spec)
builder.inspect_layers(last=3)
builder.inspect_input_features()

neuralnetwork_spec.description.metadata.author = 'Ervins Balodis'
neuralnetwork_spec.description.metadata.license = 'No license'
neuralnetwork_spec.description.metadata.shortDescription = ('Cat and Dog Classifier converted from a Keras model')

model_spec = builder.spec
builder.make_updatable(['dense_2'])
builder.set_categorical_cross_entropy_loss(name='lossLayer', input='classLabelProbs')

from coremltools.models.neural_network import SgdParams
builder.set_sgd_optimizer(SgdParams(lr=0.1, batch=1))
builder.set_epochs(10)

model_spec.isUpdatable = True
model_spec.specificationVersion = coremltools._MINIMUM_UPDATABLE_SPEC_VERSION

model_spec.description.trainingInput[0].shortDescription = 'Image for training and updating the model'
model_spec.description.trainingInput[1].shortDescription = 'Set the value as Cat or Dog and update the model'

updateableModel = 'ModelsML/' + model + '_updateable' + '.mlmodel'
coremltools.utils.save_spec(model_spec, updateableModel)

这是从应用内更新模型的代码:

func createTrainingData(imageArray: [UIImage], outputLabel: String) -> MLArrayBatchProvider{
        var featureProviders = [MLFeatureProvider]()
        let inputName = "image"
        let outputName = outputLabel
        var trainingImages = [UIImage]()
        trainingImages.append(theImage!)

        for image in trainingImages {
            let inputValue = try? MLFeatureValue(cgImage: image.cgImage!,
                                            pixelsWide: 224,
                                            pixelsHigh: 224,
                                            pixelFormatType: kCVPixelFormatType_OneComponent8,
                                            options: nil)

            let outputValue = MLFeatureValue(string: String(outputName))

            let dataPointFeatures: [String: MLFeatureValue] = [inputName: inputValue!,
                                                               outputName: outputValue]

            if let provider = try? MLDictionaryFeatureProvider(dictionary: dataPointFeatures) {
                featureProviders.append(provider)
            }
            else{
                print("Failed to append feature provider.")
            }
        }

        return MLArrayBatchProvider(array: featureProviders)
    }

    func updateModel(at url: URL,
                     with trainingData: MLArrayBatchProvider,
                     completionHandler: @escaping (MLUpdateContext) -> Void) {
        // Create an Update Task.
        guard let updateTask = try? MLUpdateTask(forModelAt: url,
                                            trainingData: trainingData,
                                            configuration: nil,
                                            completionHandler: completionHandler)
            else {
                print("Couldn't create an MLUpdateTask.")
                return
        }
        updateTask.resume()
    }

    func performUpdate(){
        //Might need to change from a batch to a single image
        let updateImages: [UIImage] = [theImage!]
        let imageBatch = createTrainingData(imageArray: updateImages, outputLabel: "dog") // temp outputLabel
        updateModel(at: globalCompiledModel!, with: imageBatch, completionHandler:{ param in
            print("Model updated!")
        })
    }

我对 globalCompiledModel 持怀疑态度。更新模型后,您需要重新加载它,即从保存更新模型的 URL 创建一个新实例。我也看不到您在代码中的哪个位置保存更新后的模型。

(好的,从技术上讲,您 没有 来保存更新后的模型,但是您 需要使用来自之后 MLUpdateTaskContext。)