Comment ajouter un modèle CoreML dans un package Swift?
J'essaye d'écrire un package Swift qui utilise un modèle CoreML. Je ne suis pas très familier avec la création de packages Swift et je n'ai pas pu le faire fonctionner. Voici ce que j'ai fait sur la base des différents articles que j'ai lus jusqu'à présent:
- Créer un package vide
$ mkdir MyPackage $ cd MyPackage
$ swift package init $ swift build
$ swift test
Ouvrez le
Package.swiftfichier avec XCodeFaites glisser et déposez le
MyModel.mlmodelfichier dans le dossierSources/MyPackage
Lorsque je clique sur le MyModel.mlmodelfichier dans XCode, le message suivant s'affiche sous le nom de la classe:
Model is not part of any target. Add the model to a target to enable generation of the model class.
De même, si j'utilise la commande swift builddans un terminal, j'obtiens le message suivant:
warning: found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
/Path/To/MyPackage/Sources/MyPackage/MyModel.mlmodel
- Pour résoudre ce problème, j'ai ajouté
MyModelaux ressources cibles du fichierPackage.swift:
.target(
name: "MyPackage",
dependencies: [],
resources: [.process("MyModel.mlmodel")]),
Si j'utilise maintenant la commande $ swift build, je n'ai plus l'avertissement et je reçois le message:
[3/3] Merging module MyPackage
Mais lorsque je vérifie le MyModel.mlmodelfichier dans XCode, le message suivant s'affiche sous le nom de la classe:
Model class has not been generated yet.
- Pour résoudre cela, j'ai utilisé la commande suivante dans un terminal:
$ cd Sources/MyPackage $ xcrun coremlcompiler generate MyModel.mlmodel --language Swift .
Cela a généré un MyModel.swiftfichier à côté du fichier mlmodel.
- J'ai branché le modèle dans le code
MyPackage.swift:
import CoreML
@available(iOS 12.0, *)
struct MyPackage {
var model = try! MyModel(configuration: MLModelConfiguration())
}
- Enfin, dans le fichier de test
MyPackageTests.swift, je crée une instance de MyPackage:
import XCTest
@testable import MyPackage
final class MyPackageTests: XCTestCase {
func testExample() {
if #available(iOS 12.0, *) {
let foo = MyPackage()
} else {
// Fallback on earlier versions
}
}
static var allTests = [
("testExample", testExample),
]
}
J'obtiens l'erreur suivante (il semble que le modèle CoreML n'a pas été trouvé):
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
J'ai dû manquer quelque chose ... J'espère que ma description était suffisamment claire et détaillée. Merci de votre aide!
Réponses
Cette ligne est probablement là où ça va mal: var model = try! MyModel(configuration: MLModelConfiguration())
Puisque vous avez ajouté un fichier mlmodel au package, il n'a pas encore été compilé. Je ne suis pas un expert des packages Swift, mais je ne pense pas que Xcode compile automatiquement ce modèle maintenant. Vous pouvez le voir par vous-même lorsque vous ouvrez le bundle d'applications compilées - contient-il le fichier mlmodel ou le dossier mlmodelc?
Vous devrez peut-être ajouter le mlmodelc au package, pas le mlmodel. Vous pouvez créer ceci en faisant:
$ xcrun coremlcompiler compile MyModel.mlmodel .
Ensuite, dans votre application, vous devrez charger le modèle comme suit:
let url = YourBundle.url(forResource: "MyModel", withExtension: "mlmodelc")!
let model = try! MyModel(contentsOf: url, configuration: MLModelConfiguration())
où YourBundleest une référence au bundle qui contient le fichier mlmodelc (qui je suppose est le bundle pour le package Swift).
La solution décrite ci-dessous a fonctionné pour moi. J'espère que c'est correct.
Conversion du MLModel
Le MLModel ne peut pas être utilisé directement dans le package Swift. Il doit d'abord être converti.
$ cd /path/to/folder/containg/mlmodel $ xcrun coremlcompiler compile MyModel.mlmodel .
$ xcrun coremlcompiler generate MyModel.mlmodel . --language Swift
La première xcruncommande compilera le modèle et créera un dossier nommé MyModel.mlmodelc. La deuxième xcruncommande générera un MyModel.swiftfichier.
Ajouter le modèle au package Swift
Nous considérons qu'un package Swift existe déjà et se trouve dans /path/to/MyPackage/.
- Copiez le
MyModel.mlmodelcdossier et leMyModel.swiftfichier dans le dossier/path/to/MyPackage/Sources/MyPackage - Ajoutez le
MyModel.mlmodelcdans les ressources cibles dans le fichierPackage.swift:
.target(
name: "MyPackage",
dependencies: [],
resources: [.process("MyModel.mlmodelc")]),
Instancier MyModel
Dans le code Swift, créez simplement une instance de MyModel:
let model = try? MyModel(configuration: MLModelConfiguration())
ou:
let url = Bundle.module.url(forResource: "MyModel", withExtension: "mlmodelc")!
let model = try? MyModel(contentsOf: url, configuration: MLModelConfiguration())
Dépannage
J'ai eu une Type 'MLModel' has no member '__loadContents'erreur au début. Cela semble être un bug lié à XCode 12. J'ai simplement commenté les 2 fonctions qui ont causé un problème.
Voir ici et ici pour plus d'informations.