繁体   English   中英

Swift - 在 Core ML Model 之间切换

[英]Swift - switch between Core ML Model

我正在尝试比较 SwiftUI 中不同MLModelsSwiftUI 为此,我必须在它们之间切换,但不能因为每个ML变量都有自己的 class,所以我得到了错误:

无法将类型“ModelOne”的值分配给类型“ModelTwo”

这是一个示例代码:

import Foundation
import CoreML
import SwiftUI

let modelone = { //declaration model 1
do {
    let config = MLModelConfiguration()
    return try ModelOne(configuration: config)
} catch {
    /*...*/
}
}()

let modeltwo = { //declaration model 2
do {
    let config = MLModelConfiguration()
    return try ModelTwo(configuration: config)
} catch {
    /*...*/
}
}()

var imageused : UIImage! //image to classify
var modelstring = ""     //string of model user chosen
var modelchosen = modelone

Button(action: { //button user decide to use model two
   modelstring = "Model Two"

}) {/*...*/}

/*...*/
func classifyphoto() {

    guard let image = imageused as UIImage?,
          let imagebuffer = image.convertToBuffer() else {
        return
        
    }
    if  modelstring == "Model Two" { //if the user chosen model two, use ModelTwo
        modelchosen = modeltwo // Error: Cannot assign value of type 'ModelOne' to type 'ModelTwo'
    } else {
        modelchosen = modelone}
    
    let output = try? modelchosen.prediction(image: imagebuffer) //prediction with model chosen
 
    if let output = output {
        let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
        _ = results.map { /*...*/
        }
    }
}

谢谢

问题是两个 model 类没有共同的 class 或共同继承的 class。 有几种方法可以实现您想要的。 根据您的示例,我认为这是最好的方法。

class MyModel {

    var model: MLModel? = nil

    init(modelName: String) {
    
        let bundle = Bundle.main
    
        if let modelURL = bundle.url(forResource: modelName, withExtension:"mlmodelc") {
    
            do {
                self.model = try MLModel(contentsOf: modelURL)
            }
            catch  {
                print("Unable to open MLModel: \(error)")
            }
        }
    }
}

class TestModel {

    class func testModels() {
        let modelOne = MyModel(modelName: "ModelOne")
        let modelTwo = MyModel(modelName: "ModelTwo")
    
        var selectedModel = modelOne
        selectedModel = modelTwo
    }
}

Swift 是一种静态类型语言,这意味着在一般情况下,您不能将一种类型的变量分配给另一种类型的变量:

var int: Int = 42
int = "Hello, world!"  // Not allowed: cannot assign String to Int

问题是modelchosenModelOne类型,因为它是用modelone初始化的,因此,您以后不能像您尝试那样将modeltwo分配给它。

要使其发挥作用,您首先必须确定ModelOneModelTwo的共同功能。 看看他们的定义。 例如,他们的.predict(image:)方法是否返回相同的类型? 看起来您正在尝试进行图像分类,因此常见的功能可能是返回描述图像的String (或潜在对象列表等)的能力。

当您确定通用功能后,您可以定义一个抽象基础 class(或协议)来描述网络的通用接口。 例如,如果两个网络都可以使用MLModelConfiuration进行初始化并用于分类,则:

class MLClassifier {
  init(from config: MLModelConfig) { 
    fatalError("not implemented")
  }

  func classify(image: ImageBuffer) -> String {
    fatalError("not implemented")
  }
}

然后,您可以使用您的两个模型派生此基础 class:

final class ModelOne: MLClassifier {
  init(from config: MLModelConfig) {
    // the specific implementation for `ModelOne`...
  }

  func classify(image: ImageBuffer) -> String {
    // the specific implementation for `ModelOne`..
  }
}

然后,您可以将变量模型选择为modelchosen类型以擦除MLClassifier的底层具体类型:

var modelchosen: MLClassifier = ModelOne(from: config1)

由于MLClassifierModelOneModelTwo的通用基础modelchosen ,您可以在需要时动态更改所选模型的类型:

// Later...
modelchosen = ModelTwo(from: config2)

类型为MLClassifier的变量modelchosen确保您可以调用.classify(image:)方法,无论具体的 model 类型是:

func classifyphoto() {
    guard let image = imageused as UIImage?,
          let imagebuffer = image.convertToBuffer() else {
        return
        
    }
    
    let output = modelchosen.classify(image: imageBuffer)
    // Update the UI...
}

综上所述,关键是识别不同类型的共同能力。 例如,如果两个模型 output 在某个时候有一个相同类型的classLabelProbs ,那么您可以将其用作公共抽象。


我使用 class inheritance 作为抽象机制编写了这个答案,但还有其他两种方法:协议和带有有效负载的枚举,协议是首选方式:

protocol MLClassifier {
  init(from config: MLModelConfig)
  func classify(image: ImageBuffer) -> String
}

// Implement the protocol for your models
struct ModelOne: MLClassifier {
  init(from config: MLModelConfig) { ... }
  func classify(image: ImageBuffer) -> String { ... }
}

// Store an instance of any `MLClassfier` using an existential
var classifier: any MLClassifier = ModelOne(from: config1)

// Later...
classifier = ModelTwo(from: config2)

作为最后的手段,您可以将所有内容包装在一个大的if-else语句中,尽管不推荐使用 event,因为它不是很可读,不是封装常见行为的好方法,并且会导致大量代码重复:

func classifyphoto() {
    guard let image = imageused as UIImage?,
          let imagebuffer = image.convertToBuffer() else {
        return
        
    }

    if modelstring == "Model Two" {
        // Use modeltwo
        let output = try? modeltwo.prediction(image: imagebuffer)
 
        if let output = output {
        let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
        _ = results.map { /*...*/ }
    } else {
        // Use modelone
        let output = try? modelone.prediction(image: imagebuffer)
 
        if let output = output {
        let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
        _ = results.map { /*...*/ }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM