画像認識モデルの学習・評価・チューニング運用(fine-tune・負例・配備)
学習モデルの健康診断は再学習と出荷判定を分離し、固定評価で現行→候補best/lastの順に測る
2日前
学習モデルの「健康診断」と改善・出荷は別の行為として扱う。診断で再学習を行う場合も、まず出荷中のアーティファクトを製品と同じ閾値・固定評価セットで測り、次に比較条件を揃えた再学習候補の best と last を同じセットで測る。評価セットは診断中に再生成しない。これにより、既存モデルの現在地、再学習による差、学習途中の選択差を同じ物差しで比較できる。
製品経路の健全性は、出荷中モデルの実ブラウザ/実ランタイム経路と評価ハーネスの等価性テストで確認する。候補モデルを製品へ差し替えて確認するのは出荷工程であり、健康診断では行わない。診断のための一時成果物は配布先へコピーせず、外部同期やコミットもしない。
判定では合格本数だけでなく、検出率・誤検出率など主要値を現行と並べる。候補が全ゲートを通っても、差が測定ばらつきの範囲内なら改善とは扱わない。逆に明確な改善が見えても、健康診断の依頼だけなら出荷判断と変更作業を分離して報告する。
検証時には、起点重み、epoch 数、seed、素材、製品閾値を記録する。どれかが異なる run は世代比較に使わない。固定評価、製品経路、比較条件、外部副作用の禁止を一組にすることで、診断が意図せずモデル更新へ変質するのを防げる。
評価ゲートが製品の後処理を切って測るなら、製品ポリシー下の数値も参考値として併記する
2日前
モデルや検知器の評価軸を「素の実力」で測るために製品側の後処理(抑制・フィルタ・除外ルール)を意図的に外すことがある。正解データが後処理の前提を満たしていない場合、後処理を掛けると正解側が取りこぼされて偽の未検出が混ざるためで、この判断自体は妥当なことが多い。
問題は、その軸の数値が製品の挙動として読まれてしまうことにある。
観測された事実
物体検出の誤検出率を、製品の入れ子抑制を切って測っていた。実測すると抑制の有無で値がおよそ半分違った(あるモデルで 0.037 対 0.013、別モデルで 0.057 対 0.027)。倍率は両モデルで揃っていたため順位は狂わなかったが、絶対値は製品の誤検出量として全く使えなかった。誤検出の中身を可視化すると、大半が「対象物の内側にある要素を拾った箱」で、まさに製品の抑制が消す種類だった。
判断基準
- ゲートは厳しい側(後処理なし)で維持する。後処理を掛けると合否が緩む方向に動くなら、掛けない方が安全側である
- 同時に、製品ポリシーを掛けた数値を参考値として同じ出力に併記する。ゲートには使わない
- 参考値に閾値を付けない理由を明記する。再現ばらつきを実測していないうちに閾値を引くと、合否が実力ではなく偶然で決まる
- 参考値を毎回出力しておけば、ラウンドを重ねるうちにばらつきの実測が貯まり、後から根拠のある閾値を引ける。記録していない値は遡って取れない
併記する軸を1本に増やさない
1つの軸に複数の合否条件を載せると、別の軸が見るべき壊れ方でその軸が落ちるようになる。参考値はあくまで出力であって判定条件ではない、という分離を守る。
検証方法
参考値を足す変更は、既存のゲート数値が変更前と完全一致することで検証する。加えて、参考値が独立に書いた別実装の測定と一致すること、そして質の違う複数モデルを識別できること(同じ値にならないこと)を確認する。識別できない指標は、記録しても後で閾値の根拠にならない。
学習の起点重みはコードにも素材にもVCSにも残らない入力 — 記録しないと再学習の再現性は成立しない
2日前
モデルを逐次 fine-tune で育てている場合、出荷中モデルの品質を決めているのは素材でも epoch 数でもなく「どのチェックポイントから学習を始めたか」であることがある。起点重みはソースにも学習素材にもバージョン管理にも残らないため、「同じ素材・同じコードで回せば同じ品質が出る」という前提が静かに崩れる。
観測された事実
1クラス物体検出器で、素材とコードを完全に固定したまま起点だけ変えた比較:
- 汎用事前学習から 80 epoch: 誤検出 0.057/枚
- 出荷中モデルの起点から 25 epoch: 0.017〜0.027/枚
- 出荷中モデル自身: 0.037/枚
学習スクリプトの既定が「汎用事前学習から」だったため、手順書どおりに再学習した担当者は現行に届かず、「再学習すると退化する」という誤った結論に至った。原因はモデルでも素材でもなく、既定値と現行モデルの作られ方の食い違いだった。
判断基準
- 現行モデルと数値を比較する回は、現行モデルと同じ起点から回す。起点が違う数値どうしを並べて優劣を論じない
- 起点をゼロに戻す学習(棚卸し)は明示的な操作として分け、その回は「現行を下回るのが普通」という前提で判定基準を別に置く
- 実行した run の起点・epoch 数を成果物と一緒に必ず記録する。後から復元できない入力なので、書かなければ永久に比較不能になる
- 手順書の既定値が現行モデルの作られ方と一致しているかを定期的に照合する。既定値は「最も安全な選択」ではなく「最初に書かれた選択」であることが多い
退化を調査するときの順序
再学習の結果が悪化したら、モデルの実力やデータ品質を疑う前に実行条件が比較対象の作られ方と一致しているかを先に確認する。学習ログの冒頭に出る「事前学習パラメータの読み込み数」「1 epoch 目の損失値」は起点の判別に使える。読み込み数が全一致なら同系列のチェックポイント、一部不一致なら別タスクの汎用事前学習であり、1 epoch 目の損失が明らかに大きければ実質スクラッチである。
副作用として受け入れていること
逐次 fine-tune を続ける限り、出荷中モデルは素材とコードから再現できない個体であり続ける。過去ラウンドに含まれていた不具合(誤ったラベル変換など)も重みの中に残り続ける。再現性を取り戻すにはスクラッチで同等品質に到達できる設定を探す必要があり、それが取れるまでは「起点の記録」が唯一の防御になる。
入力が連続量なら能力評価も連続掃引で測る — 代表点の集合は帯状の穴を素通しする
3日前
対象の大きさ・距離・明るさ・速度のように、入力が連続量として変化する系の評価を、いくつかの代表ケース(固定サイズの検体、いくつかの構図)だけで組むと、連続軸上の帯状の欠損を検出できない。代表点がたまたま帯の外に落ちていれば、全軸合格のまま本番で「普通の使い方だけ動かない」が起きる。
観測した失敗
検出器の評価が、多数対象のシーン・固定サイズの実写検体・特定の2サイズの合成フレーム、という離散的な事例の集合で構成されていた。実際には対象の大きさを連続的に変えると、中間サイズの一帯だけが完全に検出不能だった。その帯が最も普通の撮影距離で、実利用でのみ表面化した。どの評価軸もその帯にサンプル点を持っていなかった。
判断基準
- 変化しうる連続量を列挙し、そのうち利用者が最も普通に使う範囲が評価に含まれているかを見る。極端値(最小・最大)だけ押さえて中間が空くのはよくある形
- 掃引軸は1つずつ独立に動かす。組合せ爆発を避けつつ、帯の有無は1軸掃引で十分に見える
- 掃引の合否は「全点通過」か「連続する2点以上の欠損なし」かを、健全なモデルの実測ばらつきを見てから決める。基準を先に決めて後から結果に合わせて緩めない
確認方法
軸に沿って等間隔で値を振り、各点の結果を並べて出力する。単発の落ち込みは系のばらつき、連続した落ち込みは構造的な欠陥として扱いを分ける。両者は原因も対処も違う。
一般化
レイテンシとペイロードサイズ、同時接続数、時刻・日付境界など、連続量やそれに準じる順序量を入力に持つ機能全般に同じ。個別事例の積み上げは「見たものは動く」しか保証しない。
ラベルなし負例は「見た目」以外で正例と分離できなければ、同じ見た目の正例まで丸ごと消す
3日前
物体検出の学習データに、正例と同じ素材から作った領域をラベルなしで置くと、モデルはそれを負例として学ぶ。このとき負例と正例が見た目で区別できないと、モデルは残った手がかり(多くはサイズや位置)で分けようとし、その手がかりの値域にある正例まで検出できなくなる。
観測した失敗
1クラス検出器の合成データで、対象の一部を切り出した断片を画面の任意位置にラベルなしで貼っていた。断片の大きさはモデル入力換算で特定の帯に集中し、シーンの3割に混入していた。結果、その帯に入る大きさの対象が、背景・位置を問わずスコアほぼ0になった。断片と対象全体は見た目が同じなので、モデルはサイズでしか分けられず、その帯の正例ごと打ち消していた。
断片の配置を「フレーム端をまたぐ位置」だけに限定したところ(実際の見切れは必ず画面の縁で起きる)、穴は消えた。負例が正例と置き方で分離可能になったため。
判断基準
- ラベルなし負例を足す前に「これは正例と何で区別できるか」を一文で言えること。言えないなら、モデルも区別できない
- 区別の根拠は見た目より幾何的な不変式(画面のどこに現れうるか、境界に接するか)のほうが安全。見た目が同じものを見た目で分けさせない
- 負例の分布が正例の分布と重なる軸(サイズ・アスペクト・輝度)を持つなら、その軸上で正例が消えていないか必ず確認する
確認方法
重なりうる軸に沿って連続的に掃引し、値ごとの検出可否を見る。特定の帯だけ落ちるなら、その帯を占める負例が混入していないか教師データ側を疑う。個別の代表ケースをいくつか通すだけでは、帯状の穴は見つからない。
一般化
検出器に限らず、分類・フィルタ・バリデータでも同じ。「これは違う」と教える例が、正しい例と決定的に分離できる特徴を持たないなら、その学習・ルールは正しい例まで巻き込む。
エッジ推論のモデル選定は「実行環境で速度を実測 → 予算に収まる最大容量」の順で決める
3日前
ブラウザや端末上で推論するモデルを選ぶとき、パラメータ数や公開ベンチマークの精度から先に候補を絞りがちだが、実行環境(wasm、モバイル GPU、量子化ランタイム)での実測を先に取らないと順位を誤る。
実例
同一系列の2サイズを候補にし、容量と演算子の安全性から大きい方を本命に選んでいた。実行環境で測ると、
- 小さい方: パラメータ 1、推論 1.0(基準)
- 大きい方: パラメータ 5.6倍、推論 4.1倍
で、大きい方は置き換え前の既存モデルより 1.8 倍遅く、採用不可だった。小さい方は既存より 2.3 倍速く、サイズも 1/3 で、精度も用途上十分だった。机上の判断のままなら遅い方を選んでいた。
入力解像度を下げて速度を取り戻す案の落とし穴
速度が足りないときの定番策だが、
- 時間は画素数に比例して下がるとは限らず、期待ほど戻らないことがある(実測で確認する)
- 小さい対象の検出は入力解像度に強く依存するため、速度を取り戻せても別の失敗(密に並んだ小物体の取りこぼし)を招く
速度回収策として採る前に、必ず実測と、小物体を含む条件での精度確認をセットで行う。
手順
- 速度予算を先に決める(既存実装の実測値を下回らない、等の具体値で)
- 候補を学習前に、事前学習済み重みのまま変換して実行環境で計測する。数十分で済み、学習に入る前に候補を落とせる
- 演算子の互換性も同時に確認する(実行できたという事実が最も確実な互換性検証)
- 予算に収まる中で最大容量を選ぶ
注意
開発機での計測値は端末実機と数倍ずれる。相対比較(候補間の順位、既存実装との比)には使えるが、絶対値の合否判定には実機計測が要る。
自前学習モデルの配布前に、学習フレームワークのライセンスが重みに及ぶかを一次情報で確認する
4日前
自前データで学習したモデルでも、学習に使ったフレームワークや起点にした事前学習重みのライセンスが「成果物(学習済み重み)にも及ぶ」と主張されていることがある。代表例が AGPL 系の検出フレームワーク(2026年時点の ultralytics/YOLO など)で、公式見解として「学習済みモデルはデフォルトで AGPL、ソース非公開の商用 SaaS 利用は別途商用ライセンス必須」と明記している。
落とし穴
- 「自分のデータで学習したから自分のもの」という直感はベンダーの主張と衝突しうる。特に公式配布の事前学習重みから fine-tune した場合は派生物としての結びつきが強い
- Web クライアント推論(ブラウザでの ONNX 実行等)は、静的配信ディレクトリへの配置時点で「重みの配布」に該当する。本番前の preview 環境でも配布は始まっている
- ONNX 等への形式変換はライセンス上の性質を変えない
確認手順
- 学習ツールチェーン全体(フレームワーク・事前学習重み・変換ツール・推論ランタイム)を列挙し、それぞれのライセンスをパッケージメタデータで確認する
- copyleft 系(GPL/AGPL)が含まれる場合は、ベンダーの公式ライセンスページで「学習済みモデルへの適用主張」を一次確認する(主張の有無と内容は時点で変わるため、判断時に再確認する)
- モデルの重みに個別ライセンス記載がない OSS は、コードのライセンスを準用できるか不明な場合があり、厳密を期すなら作者確認まで行う
判断基準
- 商用配布が必要で copyleft を避けたい場合の選択肢は (1) ベンダーの商用ライセンス購入 (2) permissive(Apache-2.0/MIT)なアーキテクチャでの再学習。学習パイプライン(素材・合成・評価)をフレームワーク非依存に資産化してあれば、載せ替えはモデル定義の差し替え+学習数回で済み、(2) のコストは小さい
- アーキテクチャ選定の段階でライセンスを選定基準に含めると後戴しがない(精度・速度だけで選ぶと配布段階で詰まる)
合成データ val の fine-tune は実目標の改善前に early stop が切る — patience 無効化と実ターゲット評価で選ぶ
4日前
合成データで train/val を組んでいるモデルを fine-tune すると、val スコアは初期からほぼ頭打ちのため、実写側(本当に改善したい対象)が良くなる前に early stopping が発動して学習が実質進まないことがある。実測では patience=12 の fine-tune が epoch 1 を best として即終了した。同じ理由で「best チェックポイント」の自動選択も合成 val 基準になり、実目標の成績と一致しない。
対処
- fine-tune 時は patience を epochs と同値にして early stopping を実質無効化する(短い epochs を自分で決める)
- チェックポイントは best と last の両方を実ターゲット評価(実写の回帰スイート、レポート写真、fixture 等)にかけ、そちらの結果で採用を決める
- 学習フレームワークの「改善が止まったら切る」は val が目標を代表している前提の機能。ドメインが乖離した fine-tune ではその前提が崩れていることを認識する
適用条件
学習データ(合成・別ドメイン)と実運用目標(実写等)が乖離している fine-tune 全般。ゼロから学習する場合は val も同時に成長するため通常どおり early stopping で問題ない。
推論側の画像縮小補間は学習時と揃える — 高品質設定が area 相当になり信頼度が一律に下がる
4日前
学習済み検出モデルを別環境(ブラウザ等)へ配備すると、同じ画像でも信頼度が学習環境より一律に低く出ることがある。原因の定番が「縮小時の補間方式の不一致」: ブラウザ canvas の描画品質を最高に設定すると面積平均(INTER_AREA 相当)になる一方、学習パイプライン(YOLO 系の標準前処理など)は bilinear(INTER_LINEAR)で縮小しており、推論入力が学習分布から外れる。実測では同一重み・同一画像で bilinear 0.67 に対し area 0.59 と、判定閾値をまたぐ差が出た。
判断基準
- 症状が「特定の対象だけ落ちる」なら教材の問題、「全体的に信頼度が低め」なら前処理差を先に疑う
- 「高品質な縮小 = 良い」ではない。推論前処理の正解は品質でなく学習分布との一致
- fine-tune の反復で症状を追う前に前処理整合を検証する。前処理側の修正はモデル不変で全対象に効き、モデル側の反復は別対象の退化(シーソー)を招きやすい
切り分け手順
- 配備先の前処理(段階縮小・letterbox・パディング色など)をオフラインの学習環境側で忠実に再現する
- 補間方式(bilinear / area / 段階縮小の有無)だけを変えた入力を作り、同一重みで信頼度を比較する
- 配備先の実測値に最も近い方式が「配備先が実際にやっていること」。学習側の方式と違えば不一致が確定
検証
修正後は配備先の実経路(E2E テスト等)で信頼度が学習環境側の値に近づいたことを確認する。境界個体(閾値付近だった対象)が閾値を安定して超えるかを見る。
モデル改善ループの逐次 fine-tune はシーソーを起こす — 出荷判定は指標全勝でなく報告事象の解消+回帰ゼロで区切る
7日前
実機フィードバックを受けて検出モデルを短サイクルで改善するとき、直前チェックポイントからの fine-tune を連鎖させるとシーソー(直近の課題に寄って既習課題が微退行)が起きる。全課題入りデータでのゼロから再学習も万能ではなく、軸ごとに一長一短になり得る(データ配分のバランス問題は残る)。
判断基準
- 小さいモデル・短サイクルの改善では、ラウンドごとに全評価軸(既存ユースケースの回帰テスト含む)を回し、シーソーを早期検知する
- 出荷判定は「全指標の同時全勝」を要求しない。ユーザーが報告した事象の解消+既存合格軸の回帰ゼロを満たす最良候補で区切り、残るテール(難易度の高い組み合わせ条件)は既知の限界として記録し次ラウンド候補に回す
- 候補間の優劣は単一スコアでなく「ユーザー可視の欠陥か」で重み付ける(例: 明示的に苦情のあった誤検出の解消 > 自己課した評価セットの数ポイント)
- 信頼度閾値の微調整は最後の仕上げとして有効(境界スコアの弱い誤検出だけを切る。正検出のスコア帯と十分に離れていることを全軸で確認してから適用)
ハードネガティブ学習は類似構造の正例を巻き添えにする — 紛らわしい正例を同時に足して境界を教える
7日前
検出器の誤検出対策として実世界の誤検出例(ハードネガティブ)を背景として学習させると、狙った誤検出は消えるが、ネガティブに含まれる構造と似た正例クラスの検出まで巻き添えで抑制されることがある(実例: PC画面のDockアイコン格子を背景学習 → 「小さな矩形の密な格子」全般が背景扱いになり、30枚並べた小カードの一括検出が退行)。
判断基準・手順
- ハードネガティブを入れるときは、そのネガティブと構造的に紛らわしい正例(例: 小サイズの対象が密に並ぶシーン)を同時に学習データへ追加し、境界(何が違うのか: 文字行の有無など)をモデルに学ばせる
- 退行検知のため、ネガティブ追加の前に既存の全ユースケース(エッジケース含む)を回帰テスト化しておく。今回は既存のE2Eテスト(30枚一括)が巻き添えを検知した
- 評価用の簡易再現シーンを自作するときは実経路との描画忠実度に注意。実経路(video要素経由の描画パイプライン)と直接描画では検出可否が変わり、偽の不合格で迷走しうる。実経路のE2Eテストを正とする
- ネガティブ素材は学習用と未見評価用に分離し、「未見ネガティブで誤検出0件」を合格基準に加えると、誤検出対策の効果を独立に検証できる
合成学習データの背景素材は「検出対象の写り込み」を検品する — 学習では無視を教え、評価では偽の誤検出を計上する
7日前
実写真から切り出した背景素材に前景オブジェクト(検出対象)を合成して検出器の学習・評価データを作るとき、背景素材自体に未ラベルの検出対象が写り込んでいると、2つの別異な弊害が出る。
- 学習側: 未ラベルの対象が「検出してはいけない背景」として扱われ、モデルに「対象を無視する」ことを教えてしまう(検出率の上限を壊す)
- 評価側: モデルが写り込み対象を正しく検出しても誤検出に計上され、真の性能より悪い値で合格判定を落とす(実例: 素材清浄化だけで誤検出/枚 0.317→0.007、検出率 96%→100%に変化)
手順・判断基準
- 背景と前景の素材は生成前にモンタージ土目視検品する。自動抽出(検出モデルでの切り出し等)は便利だが、複数対象の結合・背景混入・UIオーバーレイの焼き込みなどの不良素材を生む
- 評価で誤検出が多いときは、閾値調整の前に誤検出例を可視化して「GT汚染かモデル誤りか」を切り分ける。GT汚染なら閾値をいくら振っても解決しない
- 合成評価の合格は必要条件にすぎない。実ドメインのフレーム(モーションブラー・低解像度・類似形状のディストラクタ)での実世界チェックを別軸で行い、sim2real ギャップ(ブラーでの見逃し・白い箱の誤検出等)を次のデータ拡張(強いブラー・ディストラクタ混入・実フレーム混合)にフィードバックする
モデル学習へ投資する前に「理想検出器+実測レイテンシ」でアーキテクチャ側の成立を先に検証する
7日前
「検出モデルを学習すれば UX が改善するはず」という見込みには、2つの独立なリスクが混在している。(1) モデルの検出品質、(2) アーキテクチャ(推論レイテンシ・補間・寿命管理)の成立性。学習にはデータ作成・訓練の投資がかかるので、先に (2) だけを切り出して検証すると、投資前に Go/No-Go を判定できる。
手順
- 正解位置つきの評価シーケンス(合成モーション等)上で、学習予定モデルを「理想検出器」(GT + 現実的なノイズ: 中心ジッタ数%・ドロップアウト数%)で置換する
- 推論レイテンシは実機実測値を使い、感度(実測±50%)も振る。トラッカー補間・表示寿命など実装予定の時系列処理をそのままシミュレートする
- 表示可用率・位置精度(IoU)・ゴースト率・表示の出入りを採点し、現行構成の同一シーケンス実測と比較する
- 理想検出器でも成立しないなら学習は無駄(アーキテクチャか実行基盤を先に直す)。成立するなら残リスクはモデル品質のみに局所化され、事前合格基準(未見セット検出率等)でゲートできる
効用
- 学習インフラ・アノテーションの投資判断が「予測」でなく「アーキテクチャ側は数値で確認済み」になる
- レイテンシ予算(どの推論速度まで許容できるか)が先に決まるので、モデルサイズ選定の制約条件になる
- 理想検出器の数値は上限値であることを明記して扱う(実モデルは必ずこれを下回る)
wasm・workerを抱えるランタイム系ライブラリのバンドラ非互換は、自社配信バンドルの blob import で隔離する
7日前
onnxruntime-web のように wasm・Web Worker・GPU 初期化を内包するランタイム系ライブラリは、アプリのバンドラ(webpack/vite)経由の import で壊れることがある。バンドラがライブラリ内部の動的 import・import.meta.url・worker 生成を変形するためで、症状は環境で異なる(あるバンドラでは初期化時の TypeError、別のバンドラではセッション作成がエラーなしに無限ハング)。ハング型は「低速端末の限界」と誤診されやすいので、端末性能を疑う前に別バンドラ環境(テストランナー等)での再現を先に確認する。
判断基準・手順
- ライブラリの配布物に「glue 内包の単一ファイルバンドル」があればそれを自社アセットとして配信し、fetch→Blob→素の dynamic import(webpackIgnore / vite-ignore コメント付き)で読む。バンドラはコードに一切触れない
- blob URL 経由だとライブラリ内部の import.meta.url 相対解決が壊れるので、wasm や glue のパスは絶対 URL で明示指定する
- 検証はブラウザテストで「セッション作成が数秒で完了すること」を確認してから実機へ出す。なお vite は public 配下の module import を禁止するため、URL 直 import ではなく blob 経由にするとテスト環境と本番で同一コードパスになる
- 同一ページで別変種バンドル(例: wasm専用版と GPU 対応版)を並行ロードすることは可能。env はモジュールインスタンスごとに独立している