0

Abstract: When I drag in an .mlModel into Xcode, Xcode generates a class interface for the model.

  • All the model Inputs I use accept common initializer arguments,
  • return unique Input classes, which are passed to the model's predict(input:UniqueInput) method and
  • those return a unique ModelOutput instance
  • which has a common lazy var dependent: String property.

Problem: The unique classes generated every time I drag in a new .mlModel requires me to rewrite code every time.

Goal: to take advantage of the common arguments and dependent property in order to minimize as much as possible the amount of code I need to rewrite when I drag in new .mlModels.

Current Failing Attempt I tried to have the autogenerated model input conform to a ModelVersion1 protocol so that newly generated model inputs would have a Type in common. However protocols with initializers require that the initializer implementation has the required keyword for classes which I don't have control over in autoGenerated classes.

func createRunningDataFrame() {
    var transactions: [Transaction] = []
    let model = try? MITO_1(configuration: MLModelConfiguration())
    URLRequest.streamFor(ticker: "MITO") { (runningFrame: DataFrame, price, ticker) in

      // ----------------------------> key line ----------------------
        guard let dependent: Double = try? model?.prediction(input: runningFrame.modelInput()!).dependent.double,
      // <-------------------------------------------------------------

        let direction = Direction(dependent: dependent) else { return }
        URLRequest.transact(direction, price, ticker) { transaction in
            transactions.append(transaction)
        }
    }
}

Used Code

(Auto generated)

class MITO_1 {
    func prediction(input: MITO_1Input) throws -> MITO_1Output {
        return try self.prediction(input: input, options: MLPredictionOptions())
    }
}


class MITO_1Input { 
    init(daysOfWeek: String, tradingsHours: String, quarters: String) {
        self.daysOfWeek = daysOfWeek
        self.tradingsHours = tradingsHours
        self.quarters = quarters
    }
}

class MITO_1Output : MLFeatureProvider {
    lazy var dependent: String = { [unowned self] in 
        return self.provider.featureValue(for: "dependent")!.stringValue
    }()
}

DataFrame

extension DataFrame {
    
    func modelInput<T: ModelVersion1>() -> T? {
        guard let daysOfWeek = self.cell(for: "daysOfWeek"),
        let tradingsHours = self.cell(for: "tradingsHours"),
        let quarters = self.cell(for: "quarters") else { return nil }
        return T(
            daysOfWeek: daysOfWeek,
            tradingsHours: tradingsHours,
            quarters: quarters
        )
    }    
}

Conformance ERROR: Initializer requirement 'init(daysOfWeek:tradingsHours:quarters:)' can only be satisfied by a 'required' initializer in non-final class 'MITO_1Input'

extension MITO_1Input: ModelVersion1 {}

protocol ModelVersion1: AnyObject {
    init(daysOfWeek: String, tradingsHours: String, quarters: String)
}

String

extension String { 
    var double: Double? {
        Double(self)
    }
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
ScottyBlades
  • 12,189
  • 5
  • 77
  • 85

1 Answers1

1

Copy the auto-generated class to your own Swift file. Disable the auto-generation. Modify the Swift file to suit your needs.

Note that the auto-generated file is there just for convenience. It wraps the MLModel API. You can use this API directly, so you don't have to use the auto-generated stuff.

Matthijs Hollemans
  • 7,706
  • 2
  • 16
  • 23