Jak dodać model CoreML do pakietu Swift?
Próbuję napisać pakiet Swift, który używa modelu CoreML. Nie jestem zaznajomiony z tworzeniem pakietów Swift i nie mogłem sprawić, by to działało. Oto, co zrobiłem na podstawie różnych postów, które do tej pory przeczytałem:
- Utwórz pusty pakiet
$ mkdir MyPackage $ cd MyPackage
$ swift package init $ swift build
$ swift test
Otwórz
Package.swiftplik za pomocą XCodePrzeciągnij i upuść
MyModel.mlmodelplik do folderuSources/MyPackage
Kiedy klikam MyModel.mlmodelplik w XCode, pod nazwą klasy wyświetla się następujący komunikat:
Model is not part of any target. Add the model to a target to enable generation of the model class.
Podobnie, jeśli użyję polecenia swift buildw terminalu, otrzymam następujący komunikat:
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
- Aby rozwiązać ten problem, dodałem
MyModeldo zasobów docelowych w plikuPackage.swift:
.target(
name: "MyPackage",
dependencies: [],
resources: [.process("MyModel.mlmodel")]),
Jeśli teraz skorzystam z polecenia $ swift build, nie mam już ostrzeżenia i otrzymuję komunikat:
[3/3] Merging module MyPackage
Ale kiedy sprawdzam MyModel.mlmodelplik w XCode, pod nazwą klasy wyświetla się następujący komunikat:
Model class has not been generated yet.
- Aby rozwiązać ten problem, użyłem następującego polecenia w terminalu:
$ cd Sources/MyPackage $ xcrun coremlcompiler generate MyModel.mlmodel --language Swift .
Spowoduje to wygenerowanie MyModel.swiftpliku obok pliku mlmodel.
- Model wpiąłem w kod
MyPackage.swift:
import CoreML
@available(iOS 12.0, *)
struct MyPackage {
var model = try! MyModel(configuration: MLModelConfiguration())
}
- Na koniec w pliku testowym
MyPackageTests.swifttworzę wystąpienie 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),
]
}
Otrzymuję następujący błąd (wygląda na to, że nie znaleziono modelu CoreML):
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Musiałem coś przeoczyć ... Mam nadzieję, że mój opis był wystarczająco jasny i szczegółowy. Dziękuję za pomoc!
Odpowiedzi
Ta linia jest prawdopodobnie tam, gdzie jest źle: var model = try! MyModel(configuration: MLModelConfiguration())
Ponieważ dodałeś plik mlmodel do pakietu, nie został on jeszcze skompilowany. Nie jestem ekspertem od pakietów Swift, ale nie wierzę, że Xcode automatycznie kompiluje teraz ten model. Możesz to zobaczyć na własne oczy, otwierając skompilowany pakiet aplikacji - czy zawiera on plik mlmodel czy folder mlmodelc?
Może być konieczne dodanie do opakowania mlmodelc, a nie mlmodel. Możesz to stworzyć, wykonując:
$ xcrun coremlcompiler compile MyModel.mlmodel .
Następnie w swojej aplikacji musisz załadować model w następujący sposób:
let url = YourBundle.url(forResource: "MyModel", withExtension: "mlmodelc")!
let model = try! MyModel(contentsOf: url, configuration: MLModelConfiguration())
gdzie YourBundlejest odniesienie do pakietu zawierającego plik mlmodelc (który, jak sądzę, jest pakietem dla pakietu Swift).
Rozwiązanie opisane poniżej zadziałało. Mam nadzieję, że to prawda.
Konwersja MLModel
MLModel nie może być używany bezpośrednio w pakiecie Swift. Najpierw należy go przekształcić.
$ cd /path/to/folder/containg/mlmodel $ xcrun coremlcompiler compile MyModel.mlmodel .
$ xcrun coremlcompiler generate MyModel.mlmodel . --language Swift
Pierwsze xcrunpolecenie skompiluje model i utworzy folder o nazwie MyModel.mlmodelc. Drugie xcrunpolecenie wygeneruje MyModel.swiftplik.
Dodaj model do pakietu Swift
Uważamy, że pakiet Swift już istnieje i znajduje się w /path/to/MyPackage/.
- Skopiuj
MyModel.mlmodelcfolder iMyModel.swiftplik do folderu/path/to/MyPackage/Sources/MyPackage - Dodaj do
MyModel.mlmodelczasobów docelowych w plikuPackage.swift:
.target(
name: "MyPackage",
dependencies: [],
resources: [.process("MyModel.mlmodelc")]),
Utwórz wystąpienie MyModel
W kodzie Swift po prostu utwórz wystąpienie MyModel:
let model = try? MyModel(configuration: MLModelConfiguration())
lub:
let url = Bundle.module.url(forResource: "MyModel", withExtension: "mlmodelc")!
let model = try? MyModel(contentsOf: url, configuration: MLModelConfiguration())
Rozwiązywanie problemów
Type 'MLModel' has no member '__loadContents'Najpierw pojawił się błąd. Wygląda na to, że jest to błąd związany z XCode 12. Po prostu skomentowałem dwie funkcje, które spowodowały problem.
Zobacz tutaj i tutaj, aby uzyskać więcej informacji.