Core ML Swift 統合ガイド
iOSアプリでCore MLモデルを読み込み、設定し、実行します。このスキルはSwift側のモデルロード、予測、MLTensor、プロファイリング、デプロイをカバーします。iOS 26+とSwift 6.3をターゲットとし、特に明記がない限りiOS 14まで下位互換性があります。
スコープの境界: Python側のモデル変換、最適化(量子化、パレット化、プルーニング)、フレームワーク選択は
apple-on-device-aiスキルに記載されています。このスキルはSwift統合のみを担当します。
完全なコードパターン(アクターベースのキャッシュ、バッチ推論、画像前処理、テストを含む)については、references/coreml-swift-integration.md を参照してください。
目次
- モデルの読み込み
- モデル設定
- 予測の実行
- MLTensor(iOS 18+)
- MLMultiArrayの操作
- 画像の前処理
- マルチモデルパイプライン
- Vision統合
- パフォーマンスプロファイリング
- モデルデプロイ
- メモリ管理
- よくある間違い
- レビューチェックリスト
- 参考資料
モデルの読み込み
自動生成クラス
.mlpackage または .mlmodelc をXcodeにドラッグすると、型指定された入出力を持つSwiftクラスが自動生成されます。可能な限りこれを使用してください。
import CoreML
let config = MLModelConfiguration()
config.computeUnits = .all
let model = try MyImageClassifier(configuration: config)
手動読み込み
モデルが実行時にダウンロードされた場合やバンドル外に保存されている場合に、URLから読み込みます。
let modelURL = Bundle.main.url(
forResource: "MyModel", withExtension: "mlmodelc"
)!
let model = try MLModel(contentsOf: modelURL, configuration: config)
非同期読み込み(iOS 16+)
メインスレッドをブロックせずにモデルを読み込みます。大きなモデルではこちらを推奨します。
let model = try await MLModel.load(
contentsOf: modelURL,
configuration: config
)
ランタイムでのコンパイル
.mlpackage または .mlmodel をデバイス上で .mlmodelc にコンパイルします。サーバーからダウンロードしたモデルに便利です。
let compiledURL = try await MLModel.compileModel(at: packageURL)
let model = try MLModel(contentsOf: compiledURL, configuration: config)
コンパイル済みURLはキャッシュしてください。起動のたびに再コンパイルすると時間を浪費します。compiledURL を永続的な場所(例:Application Support)にコピーします。
モデル設定
MLModelConfiguration は計算ユニット、GPUアクセス、モデルパラメータを制御します。
計算ユニット決定表
| 値 | 使用するユニット | 選択するタイミング |
|---|---|---|
.all | CPU + GPU + Neural Engine | デフォルト。システムに任せます。 |
.cpuOnly | CPU | バックグラウンドタスク、オーディオセッション、GPUがビジーな場合。 |
.cpuAndGPU | CPU + GPU | GPUが必要だが、モデルにANE非対応のオペレーションがある場合。 |
.cpuAndNeuralEngine | CPU + Neural Engine | 互換性のあるモデルに最適なエネルギー効率。 |
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
// 低優先度のバックグラウンド推論を許可
config.computeUnits = .cpuOnly
設定プロパティ
let config = MLModelConfiguration()
config.computeUnits = .all
config.allowLowPrecisionAccumulationOnGPU = true // 高速だがわずかに精度低下
予測の実行
自動生成クラスを使用
生成されたクラスは型指定された入出力構造体を提供します。
let model = try MyImageClassifier(configuration: config)
let input = MyImageClassifierInput(image: pixelBuffer)
let output = try model.prediction(input: input)
print(output.classLabel) // "golden_retriever"
print(output.classLabelProbs) // ["golden_retriever": 0.95, ...]
MLDictionaryFeatureProviderを使用
入力が動的である場合やコンパイル時に不明な場合に使用します。
let inputFeatures = try MLDictionaryFeatureProvider(dictionary: [
"image": MLFeatureValue(pixelBuffer: pixelBuffer),
"confidence_threshold": MLFeatureValue(double: 0.5),
])
let output = try model.prediction(from: inputFeatures)
let label = output.featureValue(for: "classLabel")?.stringValue
非同期予測(iOS 17+)
let output = try await model.prediction(from: inputFeatures)
バッチ予測
スループット向上のために、複数の入力を1回の呼び出しで処理します。
let batchInputs = try MLArrayBatchProvider(array: inputs.map { input in
try MLDictionaryFeatureProvider(dictionary: ["image": MLFeatureValue(pixelBuffer: input)])
})
let batchOutput = try model.predictions(from: batchInputs)
for i in 0..<batchOutput.count {
let result = batchOutput.features(at: i)
print(result.featureValue(for: "classLabel")?.stringValue ?? "unknown")
}
ステートフル予測(iOS 18+)
予測間で状態を維持するモデル(シーケンスモデル、LLM、オーディオアキュムレータ)には MLState を使用します。状態を作成し、各予測呼び出しに渡します。
let state = model.makeState()
// 各予測は内部モデル状態を引き継ぎます
for frame in audioFrames {
let input = try MLDictionaryFeatureProvider(dictionary: [
"audio_features": MLFeatureValue(multiArray: frame)
])
let output = try await model.prediction(from: input, using: state)
let classification = output.featureValue(for: "label")?.stringValue
}
状態は Sendable ではありません。単一のアクターまたはタスクからのみ使用してください。model.makeState() を呼び出して、並行ストリーム用の独立した状態を作成します。
MLTensor(iOS 18+)
MLTensor は、前処理/後処理のためのSwiftネイティブな多次元配列です。操作は遅延実行されます。結果を具体化するには .shapedArray(of:) を呼び出します。
import CoreML
// 作成
let tensor = MLTensor([1.0, 2.0, 3.0, 4.0])
let zeros = MLTensor(zeros: [3, 224, 224], scalarType: Float.self)
// 形状変更
let reshaped = tensor.reshaped(to: [2, 2])
// 数学演算
let softmaxed = tensor.softmax()
let normalized = (tensor - tensor.mean()) / tensor.standardDeviation()
// MLMultiArrayとの相互運用
let multiArray = try MLMultiArray([1.0, 2.0, 3.0, 4.0])
let fromMultiArray = MLTensor(multiArray)
let backToArray = tensor.shapedArray(of: Float.self)
MLMultiArrayの操作
MLMultiArray は、画像以外のモデル入力および出力の主要なデータ交換型です。自動生成クラスが配列型の特徴量を期待する場合に使用します。
// 3次元配列を作成: [batch, sequence, features]
let array = try MLMultiArray(shape: [1, 128, 768], dataType: .float32)
// 値の書き込み
for i in 0..<128 {
array[[0, i, 0] as [NSNumber]] = NSNumber(value: Float(i))
}
// 値の読み取り
let value = array[[0, 0, 0] as [NSNumber]].floatValue
// ゼロコピー相互運用のためのデータポインタから作成
let data: [Float] = [1.0, 2.0, 3.0]
let fromData = try MLMultiArray(dataPointer: UnsafeMutableRawPointer(mutating: data),
shape: [3],
dataType: .float32,
strides: [1])
高度なMLMultiArrayパターン(NLPトークン化やオーディオ特徴抽出を含む)については、references/coreml-swift-integration.md を参照してください。
画像の前処理
画像モデルは CVPixelBuffer 入力を期待します。カメラやフォトライブラリからの写真には CGImage 変換を使用します。Visionの VNCoreMLRequest はこれを自動的に処理します。手動変換は直接 MLModel 予測を行う場合にのみ必要です。
import CoreVideo
func createPixelBuffer(from cgImage: CGImage, width: Int, height: Int) -> CVPixelBuffer? {
var pixelBuffer: CVPixelBuffer?
let attrs: [CFString: Any] = [
kCVPixelBufferCGImageCompatibilityKey: true,
kCVPixelBufferCGBitmapContextCompatibilityKey: true,
]
CVPixelBufferCreate(kCFAllocatorDefault, width, height,
kCVPixelFormatType_32ARGB, attrs as CFDictionary, &pixelBuffer)
guard let buffer = pixelBuffer else { return nil }
CVPixelBufferLockBaseAddress(buffer, [])
let context = CGContext(
data: CVPixelBufferGetBaseAddress(buffer),
width: width, height: height,
bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
)
context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
CVPixelBufferUnlockBaseAddress(buffer, [])
return buffer
}
追加の前処理パターン(正規化、センタークロッピング)については、references/coreml-swift-integration.md を参照してください。
マルチモデルパイプライン
前処理や後処理に別のモデルが必要な場合、モデルをチェーンします。
// 逐次推論: 前処理モデル -> メインモデル -> 後処理モデル
let preprocessed = try preprocessor.prediction(from: rawInput)
let mainOutput = try mainModel.prediction(from: preprocessed)
let finalOutput = try postprocessor.prediction(from: mainOutput)
Xcodeで管理するパイプラインの場合は、.mlpackage 内のパイプラインモデルタイプを使用します。各サブモデルは最適な計算ユニットで実行されます。
Vision統合
Visionを使用して、Core ML画像モデルを自動画像前処理(リサイズ、正規化、色空間、向き)とともに実行します。
最新: CoreMLRequest(iOS 18+)
import Vision
import CoreML
let model = try MLModel(contentsOf: modelURL, configuration: config)
let request = CoreMLRequest(model: .init(model))
let results = try await request.perform(on: cgImage)
if let classification = results.first as? ClassificationObservation {
print("\(classification.identifier): \(classification.confidence)")
}
従来: VNCoreMLRequest
let vnModel = try VNCoreMLModel(for: model)
let request = VNCoreMLRequest(model: vnModel) { request, error in
guard let results = request.results as? [VNRecognizedObjectObservation] else { return }
for observation in results {
let label = observation.labels.first?.identifier ?? "unknown"
let confidence = observation.labels.first?.confidence ?? 0
let boundingBox = observation.boundingBox // 正規化された座標
print("\(label): \(confidence) at \(boundingBox)")
}
}
request.imageCropAndScaleOption = .scaleFill
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
try handler.perform([request])
完全なVisionフレームワークパターン(テキスト認識、バーコード検出、ドキュメントスキャン)については、
vision-frameworkスキルを参照してください。
パフォーマンスプロファイリング
MLComputePlan(iOS 17.4+)
予測を実行する前に、各操作がどの計算デバイスを使用するかを検査します。
let computePlan = try await MLComputePlan.load(
contentsOf: modelURL, configuration: config
)
guard case let .program(program) = computePlan.modelStructure else { return }
guard let mainFunction = program.functions["main"] else { return }
for operation in mainFunction.block.operations {
let deviceUsage = computePlan.deviceUsage(for: operation)
let estimatedCost = computePlan.estimatedCost(of: operation)
print("\(operation.operatorName): \(deviceUsage?.preferredComputeDevice ?? "unknown")")
}
Instruments
Instrumentsの Core ML インストルメントテンプレートを使用して、以下をプロファイリングします。
- モデルロード時間
- 予測レイテンシ(操作ごとの内訳)
- 計算デバイスのディスパッチ(操作ごとのCPU/GPU/ANE)
- メモリ割り当て
正確な結果を得るには、デバッガの外部で実行してください(Xcode: Product > Profile)。
モデルデプロイ
バンドル vs オンデマンドリソース
| 戦略 | 長所 | 短所 |
|---|---|---|
| アプリにバンドル | 即時利用可能、オフラインでも動作 | アプリのダウンロードサイズが増加 |
| オンデマンドリソース | 初期ダウンロードが小さい | 初回使用前にダウンロードが必要 |
| バックグラウンドアセット(iOS 16+) | 事前にダウンロード | より複雑な設定 |
| CloudKit / サーバー | 最大の柔軟性 | ネットワークが必要、セットアップに時間がかかる |
サイズに関する考慮事項
- App Store制限: アプリバンドルは4 GB
- セルラーダウンロード制限: 200 MB(例外をリクエスト可能)
- 50 MBを超えるモデルにはODRタグを使用
- デバイス上のコンパイルを避けるため、事前に
.mlmodelcにコンパイル
// オンデマンドリソースの読み込み
let request = NSBundleResourceRequest(tags: ["ml-model-v2"])
try await request.beginAccessingResources()
let modelURL = Bundle.main.url(forResource: "LargeModel", withExtension: "mlmodelc")!
let model = try await MLModel.load(contentsOf: modelURL, configuration: config)
// 使用が終わったら request.endAccessingResources() を呼び出す
メモリ管理
- バックグラウンドでアンロード: アプリがバックグラウンドに入ったらモデル参照を解放し、GPU/ANEメモリを解放します。フォアグラウンド復帰時に再読み込みします。
- バックグラウンドタスクには
.cpuOnlyを使用: バックグラウンド処理はGPUやANEを使用できません。.cpuOnlyを設定することで、サイレントフォールバックとリソース競合を回避します。 - モデルインスタンスを共有: 同じコンパイル済みモデルから複数の
MLModelインスタンスを作成しないでください。アクターを使用して共有アクセスを提供します。 - メモリプレッシャーを監視: 大きなモデル(100 MB超)はメモリ警告をトリガーする可能性があります。
UIApplication.didReceiveMemoryWarningNotificationを登録し、プレッシャーがかかったらキャッシュされたモデルを解放します。
ライフサイクルを考慮したロードとキャッシュ削除を行うアクターベースのモデルマネージャについては、references/coreml-swift-integration.md を参照してください。
よくある間違い
してはいけないこと: メインスレッドでモデルを読み込む。
すべきこと: MLModel.load(contentsOf:configuration:) の非同期APIを使用するか、バックグラウンドアクターで読み込む。
理由: 大きなモデルは読み込みに数秒かかり、UIがフリーズする可能性があります。
してはいけないこと: アプリを起動するたびに .mlpackage を .mlmodelc に再コンパイルする。
すべきこと: MLModel.compileModel(at:) で一度コンパイルし、コンパイル済みURLを永続的にキャッシュする。
理由: コンパイルは高コストです。Application Support に .mlmodelc をキャッシュしてください。
してはいけないこと: 特別な理由なく .cpuOnly をハードコードする。
すべきこと: .all を使用し、システムに最適な計算ユニットを選択させる。
…
[翻訳のため省略]
レビューチェックリスト
- モデルは非同期(
await)またはバックグラウンドアクターで読み込まれていますか? - 計算ユニット設定はターゲットデバイスとユースケースに適していますか?
- ステートフルモデルはアクター境界を越えて状態を共有していませんか?
- オンデマンドリソースモデルは使用後に
endAccessingResources()を呼び出していますか? - 手動のピクセルバッファ変換は適切なメモリ管理で行われていますか?
- バッチ予測は適切なサイズの配列でループを避けていますか?
- MLComputePlanはパフォーマンスの問題を診断するために使用されていますか?
- 複数のモデルが同じコンパイル済みモデルから作成されていませんか?
参考資料
- references/coreml-swift-integration.md - アクターベースのキャッシュ、バッチ推論、テストを含む完全なコードパターン
apple-on-device-aiスキル - Pythonでのモデル変換、最適化、フレームワーク選択vision-frameworkスキル - 完全なVisionフレームワークパターン- Apple Core MLドキュメント