ハイライト
これは Create ML Components の上級 Session です。基礎(Transformer、Estimator の概念)は「Get to know Create ML Components」で紹介されています。本場は 2 つの上級テーマ、時系列データ処理と増分学習に焦点を当てます。
主要内容
動作分類では、モデルは動画クリップ内で jumping jack をしていると教えてくれます。実際のフィットネス App は別の質問にも答える必要があります。何回行ったかです。1 回の jumping jack は連続フレームにまたがり、単一フレームでは完全な答えは得られません。
Create ML Components はここで temporal transformer(時系列変換器)を導入します。AsyncSequence を受け取り、新しい AsyncSequence を出力します。動画フレーム、カメラフレーム、オーディオ buffer は時間とともに流れ、ウィンドウ化、ダウンサンプリング、モデル投入ができます。
Session の第 1 の例は動作繰り返しカウンターです。動画またはカメラフレームから始め、人体姿勢を抽出し、1 人を選び、姿勢をウィンドウに分け、動作回数を累積します。カウントは浮動小数点です。モデルは未完了の一部動作も含めるためです。
第 2 の例は音声分類器の学習です。従来の再学習は毎回すべての音声特徴を再抽出します。Create ML Components では feature extractor と classifier を分離し、前処理済み特徴をキャッシュできます。新データが来たら新規分だけ処理し、分類器パラメータ調整時も古い特徴を再利用できます。
詳細
AsyncSequence で動画と音声を表現する
(01:55)発表者はまず問題を分解します。動画は時間とともに到着するフレームの列であり、Swift の AsyncSequence がこの入力にぴったりです。フレーム単位処理は map を使います。複数フレームを一度に扱うときは temporal transformer を使います。
概念コード(トランスクリプトのコンポーネント名から整理。実際の初期化パラメータは省略):
let frames = videoReader.read(videoURL)
let transformedFrames = frames.map { frame in
preprocess(frame)
}
let windows = SlidingWindowTransformer(
length: 90,
stride: 90
).applied(to: transformedFrames)
キーポイント:
videoReader.read(videoURL)は動画を時間順フレーム列として読み込みます。トランスクリプトでは video reader が frames の async sequence を返すと明記されています。mapは非同期にフレーム単位で変換し、1 フレームずつ処理する場面に適しています。SlidingWindowTransformerは時系列コンポーネントで、入力は async sequence、出力は window 化された async sequence です。length: 90は各ウィンドウに 90 フレームまたは 90 姿勢を含みます。stride: 90はウィンドウが重ならないことを意味します。後のライブカメラ例では stride を小さくします。
動作繰り返しカウンターを組み立てる
(03:42)動作カウントパイプラインは 4 段階です。HumanBodyPoseExtractor、PoseSelector、SlidingWindowTransformer、HumanBodyActionCounter。
概念コード(コンポーネント名はトランスクリプト由来。呼び出し形式は組み立て順を示す):
let poseExtractor = HumanBodyPoseExtractor()
let poseSelector = PoseSelector(strategy: .rightMostJointLocation)
let slidingWindow = SlidingWindowTransformer(length: 90, stride: 90)
let actionCounter = HumanBodyActionCounter()
let repetitionCounter = poseExtractor
.appending(poseSelector)
.appending(slidingWindow)
.appending(actionCounter)
キーポイント:
HumanBodyPoseExtractorの入力は image、出力は人体姿勢の配列です。トランスクリプトでは Vision framework を使うと述べられています。- 出力は配列です。1 枚に複数人が写ることがあり、グループ学習シーンでよくあります。
PoseSelector(strategy: .rightMostJointLocation)は配列から 1 姿勢を選びます。例では right-most joint location 戦略を使います。SlidingWindowTransformer(length: 90, stride: 90)は連続姿勢を 90 個ずつのウィンドウに分けます。HumanBodyActionCounterはウィンドウ化された姿勢列を消費し、これまでの累積繰り返し回数を出力します。
オフライン動画をライブカメラに切り替える
(05:38)オフライン動画は 90 フレームごとにカウントを更新できます。ライブフィットネス App はそれを待てません。Session の変更は小さく、入力を readCamera に替え、stride を 90 から 15 に下げます。
概念コード(トランスクリプトの readCamera と stride パラメータから整理):
let cameraFrames = readCamera(configuration)
let liveWindow = SlidingWindowTransformer(
length: 90,
stride: 15
)
let liveCounter = poseExtractor
.appending(poseSelector)
.appending(liveWindow)
.appending(actionCounter)
キーポイント:
readCamera(configuration)はカメラフレームを読み込みます。トランスクリプトでは camera configuration を受け取り camera frames の async sequence を返すと説明されています。length: 90は 90 フレームウィンドウを維持し、カウンターが完全な動作片を見られるようにします。stride: 15はウィンドウを 15 フレームごとにスライドします。- カメラが 30fps なら 15 フレームは 0.5 秒で、カウントは 0.5 秒ごとに更新されます。
- stride が大きすぎるとフィードバックが遅くなります。小さくすると計算頻度が上がるため、App の目標に応じて取舍します。
音声分類器の特徴抽出と学習を分離する
(06:30)Create ML の MLSoundClassifier は音声分類器学習の最も簡単な入口のままです。より細かい制御が必要なら、底层コンポーネントに分解できます。AudioFeaturePrint feature extractor と classifier です。
(08:04)重要な手順はまず preprocess を呼ぶことです。入力を estimator 手前まで transformer で処理し、前処理済み features で学習します。
概念コード(トランスクリプトの AudioFeaturePrint、logistic regression classifier、preprocess を示す):
let featureExtractor = AudioFeaturePrint()
let classifier = LogisticRegressionClassifier()
let soundClassifier = featureExtractor.appending(classifier)
let features = try await soundClassifier.preprocess(trainingData)
let model = try await soundClassifier.fitted(to: features)
キーポイント:
AudioFeaturePrintは temporal transformer で、audio buffers の async sequence から音声特徴を抽出します。LogisticRegressionClassifierは例で選ばれた分類器です。featureExtractor.appending(classifier)は特徴抽出と分類器を音声分類タスクにまとめます。preprocess(trainingData)は学習データを features に処理し、後で再利用できます。fitted(to: features)はすでに抽出済みの特徴で学習し、再学習のたびに特徴抽出を繰り返しません。
新データとハイパーパラメータ調整で古い特徴を再利用する
(08:38)新しい学習データが来たら、新規データだけ特徴抽出し、supplemental features を古い features の後に追加します。分類器パラメータ調整時も古い features を再利用できます。
概念コード(データフローを強調。完全 API ではない):
let originalFeatures = try await soundClassifier.preprocess(originalData)
let supplementalFeatures = try await soundClassifier.preprocess(newData)
let allFeatures = originalFeatures + supplementalFeatures
let tunedClassifier = LogisticRegressionClassifier(l2Penalty: newPenalty)
let tunedSoundClassifier = featureExtractor.appending(tunedClassifier)
let tunedModel = try await tunedSoundClassifier.fitted(to: allFeatures)
キーポイント:
originalFeaturesは古いデータの前処理結果で、すでにキャッシュされています。supplementalFeaturesは新規データのみから来て、古いデータの再特徴抽出時間を節約します。allFeaturesは古い features と新規 features をマージして学習します。l2Penaltyはトランスクリプトで言及された分類器パラメータです。- 調整時に feature extractor を変えてはいけません。発表者は変更すると古い features が無効になると警告します。
バッチ増分学習、早期停止、checkpoint
(09:43)大規模データセットはメモリに入らないことがあります。Session では logistic regression classifier を updatable classifier(例:fully connected neural network classifier)に替え、バッチごとにモデルを更新します。
(10:53)発表者は Swift Algorithms package も導入し、chunks で features をバッチ化します。
概念コード(トランスクリプトの default model、chunks、update、mapFeatures、checkpoint を示す):
var model = classifier.defaultModel()
let features = try await soundClassifier.preprocess(trainingData)
for iteration in 0..<maxIterations {
for batch in features.chunks(ofCount: batchSize) {
model = try await classifier.update(model, with: batch)
}
let validationPredictions = try await model.mapFeatures(validationFeatures)
let accuracy = Metrics.classificationAccuracy(
validationPredictions,
validationAnnotations
)
if accuracy >= 0.95 {
break
}
if iteration.isMultiple(of: checkpointInterval) {
try model.write(to: checkpointURL)
}
}
try model.write(to: finalModelURL)
キーポイント:
defaultModel()はトランスクリプトの default initialized model に対応し、学習の出発点で、まだ有効な予測には使えません。preprocess(trainingData)は学習ループの前に置き、各 iteration で特徴を再抽出しないようにします。features.chunks(ofCount: batchSize)は Swift Algorithms のグループ化で、一度にメモリへ載せる学習データ量を制御します。classifier.update(model, with: batch)は増分学習の核心で、各バッチごとにモデルを更新します。mapFeatures(validationFeatures)は検証セットの予測を生成し、annotations とペアで評価します。accuracy >= 0.95は早期停止の例で、精度 95% で学習を止めます。model.write(to: checkpointURL)をループ内に置き、学習進捗を保存します。長時間学習は checkpoint から再開できます。
重要ポイント
1. フィットネス動作カウンター
- やること:カメラで jumping jack、squat、lunge の回数を数える。
- 価値がある理由:Session はすでに姿勢抽出、姿勢選択、スライディングウィンドウ、動作カウントの 4 コンポーネントを示しており、リアルタイムフィードバックに適しています。
- 始め方:
readCameraでフレーム列を取得し、stride を 15 前後に設定し、まず単人画面でカウント遅延と精度を検証する。
2. トレーニング動画再生分析ツール
- やること:トレーニング動画をインポートし、ウィンドウごとに動作回数を自動集計し、動作変化の時間帯をマークする。
- 価値がある理由:オフライン動画は 90 フレーム、stride 90 の非重複ウィンドウを使え、リアルタイムより計算負荷が低いです。
- 始め方:video reader でフレームを読み、
HumanBodyPoseExtractor、PoseSelector、SlidingWindowTransformer、HumanBodyActionCounterを接続する。
3. 継続更新可能な音声分類器
- やること:環境音、デバイス音、アラート音の分類器を学習し、新しい録音を継続的に追加する。
- 価値がある理由:
preprocessにより古い音声特徴を再利用でき、新データは supplemental features だけ追加します。 - 始め方:まず
AudioFeaturePrintで features を抽出してキャッシュし、分類器を学習する。新データが来たら新規サンプルの特徴抽出だけ実行する。
4. 大規模データセットの低メモリ学習
- やること:メモリ制約のある Mac や CI マシンで音声分類モデルを学習する。
- 価値がある理由:updatable classifier はバッチ更新をサポートし、全学習データを一度に読み込む必要がありません。
- 始め方:分類器を fully connected neural network classifier に替え、Swift Algorithms の
chunksでバッチ化し、ループ内でupdateを呼ぶ。
5. 再開可能な長時間学習タスク
- やること:学習フローに早期停止と checkpoint を追加し、無駄な学習時間を減らす。
- 価値がある理由:発表者の曲線は約 10 iteration で 95% に達し横ばいになり、それ以降の学習効果は低いです。
- 始め方:各ラウンドで検証セットの accuracy を計算し、目標閾値で
breakする。数ラウンドごとに model をディスクへ書き出す。
関連セッション
- Get to know Create ML Components — Transformer、Estimator、
appendingを理解してから、本場の時系列コンポーネントと増分学習を読む。 - Explore the machine learning development experience — モデル選択、変換、評価から性能チューニングまで、オンデバイス ML の完全な実装フローを補完する。
- Optimize your Core ML usage — デプロイ後のデバイス性能に焦点を当て、本場の学習フローと前後でつなぐ。
- Meet AsyncSequence — 本場の時系列 transformer の入出力は AsyncSequence 中心。理解するとデータフローが読みやすくなる。
コメント
GitHub Issues · utterances