見出し画像

Core MLモデルをクラウドにデプロイして配信する

アプデ不要でCore MLモデルだけを更新する

アプリをApp Store上でアップデートすることなくモデルを更新できる"Model deployments"という機能がある。

Core MLモデルをiCloud上に配備し、アプリは必要に応じてiCloud経由でモデルを取得する、というもの。iOS 14.0以上で利用可能。

WWDC20のセッションより

なんとなく良さそうではあるが、実際に実装してみると、使い勝手が「思ってたのと違う」点や、挙動が怪しい点があった。

この機能はWWDC20で発表されたものだが、それ以降の続報はないし、ネット上にもあまり情報がない。挙動の怪しさも相まって、実プロダクトで運用している例はほぼ存在しないのではないか…?

以下、手元で試してみたメモ。

実装方法

実装手順は、WWDC20のセッション  "Use model deployment and security with Core ML" で丁寧に解説されているのでここでは詳細は割愛する。

大まかな流れとしては、以下のようになる。

【アプリ側】

  • MLModelCollectionのbeginAccessingメソッドを使用してModel Collection(MLModelCollectionオブジェクト)を取得する

  • MLModelCollectionオブジェクトから所望のモデルのMLModelCollection.Entryオブジェクトを取得する

  • MLModelCollection.EntryオブジェクトからモデルのURLを取得する

let _ = MLModelCollection.beginAccessing(identifier: modelCollectionName) { [self] result in
    var modelURL: URL?
                                                                      
    switch result {
    case .success(let collection):
        modelURL = collection.entries[modelName]?.modelURL
    case .failure(let error):
        print("Model collection error: \(error)")
    }

    if let modelURL = modelURL {
        ...
    } else {
        ...
    }
}

【クラウド側】

  • Model Collectionを作成する

    • 各モデルのIDを登録する

  • モデルの.mlarchiveをデプロイする

使ってみてわかったこと

セッションの情報をざっくり読んだだけの時点ではわからなかったのだが、自分で手を動かしてみると色々と気付きがあった。

モデルコレクションの制約

  • 後からモデルの種類(Model Assets)を変更することはできない

    • Model Assetsを増やしたり減らしたりIDを変更したりできない

  • 一度STOPしたDeploymentsを再開することはできない

  • 複数のModel Assetsを持つModel Collectionをデプロイする場合、すべてのModel Assetsを登録しないとデプロイできない

    • コレクションのうち一つのモデル(Model Asset)だけ更新したい、みたいなことができない。更新しないモデルもmlarchiveファイルをアップロードする必要がある。

初期モデルをあらかじめプロジェクトに組み込んでおくのは必須ではなさそう

WWDC20のセッションのサンプルではFlowerClassifier.mlmodelというCore MLモデルをあらかじめプロジェクトに組み込み、FlowerClassifierというXcodeが自動生成するクラスを使用して実装していた。

これを見て、初期モデルをあらかじめプロジェクトに組み込んでおかないといけないのかと思ったが、それはあくまで自動生成クラスを使用する場合の話。Core MLの自動生成クラスを使用せず、MLModelのイニシャライザで初期化する場合はあらかじめモデルをアプリバンドル内に組み込んでおく必要はない

挙動が怪しい点

デプロイしてクラウド上ではactiveになってもアプリ側でモデルが取得できない

これは結構ハマった。モデルコレクションを登録、モデルアーカイブをデプロイして、ステータスも"Active"となっているのに、

そしてアプリ側では MLModelCollection.beginAccessing(identifier:completionHandler:) の結果が .success となっているのに、entriesの中身が空なのだ。

この問題は、寝て起きたら解決していた…おそらく、iCloudのダッシュボード上でActiveとなっていても、実際にアプリ側で取得できるようになるまでは時間差があるのだろう。

"[coreml] MLModelCollection: experimentId unexpectedly not returned"

なるエラーメッセージのようなものが、beginAccessing実行時にコンソールに流れる。

上述の「クラウドにデプロイしたはずのモデルがアプリ側で取得できない」問題が発生したときにはこれが何かの問題を示唆しているのだと思いググったのだが、解決策は一切出てこなかった。

その後、モデルの取得に成功してからも、結局このログは出続けている。無視することにした。

MLModelCollectionオブジェクトのdeploymentIDが取得できていない

entriesの中身がちゃんと入ってきている場合でも、ここが空だった。

case .success(let collection):
    print("Collection: \(collection.identifier), \(collection.deploymentID)")

(出力)

Collection: YOLOCollection, 

beginAccessingの結果が返ってこないケースが頻出する

初回起動時(初めて当該Model CollectionにbeginAccessingするとき)、あるいはiCloudへのデプロイ直後、beginAccessingメソッドが完了しないことが多々あった。多々ある、というより自分の手元では100%そうなった。

エラーメッセージも返ってこないので結構困る。実プロダクトで利用する場合はタイムアウト処理を自分で実装したほうがいいかもしれない。

beginAccessingはアプリ起動の度に呼んでいいのか?endAccessingする必要がある?

endAccessingのドキュメントには、アプリがモデルコレクションにアクセスする必要がなくなったときに呼べ、と書いてある。

Use this method when your app no longer needs access to a model collection.

モデルを使用する限りは呼ぶ必要はなさそうだ、と読み取った。

しかし、前述のbeginAccessingの結果が返ってこないケースで、その前にendAccessingを呼ぶようにすると比較的安定して結果が返ってくるようになった。

  • beginAccessingしたら、取得完了後すぐ、遅くともアプリ終了後、最悪でも次にbeginAccessingする前にendAccessingを呼ばないといけないのかもしれない。

    • であれば、isAccessing みたいなメソッドがほしい…

Model Collectionの更新通知を受け取る

MLModelCollectionクラスには、didChangeNotificationという通知名が定義されている。

@available(iOS 14.0, *)
public class let didChangeNotification: NSNotification.Name

これを監視しておけば、デプロイ後にモデルが利用可能になったタイミングを検知できるのかもしれない。(まだ試していない)

iCloudのModel Deploymentsの仕組みを使わなくてもいいのでは?

ここまでいろいろ書いたとおり、Model deploymentsはまだ実プロダクトに投入するには不安な点が多々ある。

今回は試していないが、暗号化(Model Encryption)や、特定のデバイスをターゲティングする(Targeted deployments)機能込みで使う場合はこのiCloudのModel deploymentsが便利になってくるけど、モデルアーカイブをどこかに配備してダウンロードできるようにしたいだけであれば、たとえばS3に置いておくような運用の方が柔軟性があって良いかもしれない。

(2023.12.24追記) deprecatedに

visionOSでも使えるのかな、とAPIリファレンスを見に行くと、

なんとDeprecatedに…

Use Background Assets or URLSession instead.

とあり、至極まっとうな着地点だと思った。本記事末尾で、「こんなに不安定なら、(あまり多くの人が使ってなくて十分に枯れていない)Core ML独自の仕組みを使うより、普遍的な仕組みでモデルをダウンロードする方がいいのでは」というようなことを書いたが、実際にそうなったようだ。

続きをみるには

残り 0字

文章やサンプルコードは多少荒削りかもしれませんが、ブログや書籍にはまだ書いていないことを日々大量に載せています。たったの400円で、すぐに購読解除してもその月は過去記事もさかのぼって読めるので、少しでも気になる内容がある方にはオトクかと思います。

技術的なメモやサンプルコード、思いついたアイデア、考えたこと、お金の話等々、頭をよぎった諸々を気軽に垂れ流しています。

最後まで読んでいただきありがとうございます!もし参考になる部分があれば、スキを押していただけると励みになります。 Twitterもフォローしていただけたら嬉しいです。 https://twitter.com/shu223/