| 項目 | 方針 |
|---|---|
| リサイズ | アスペクト比を保って 224×224 に収め,余白は黒で埋める(元画像は幅 449〜900・高さ 279〜1025 でばらつく) |
| 正規化 | ImageNet の mean / std(事前学習の統計に合わせる) |
| マスク外領域 | 黒 (0, 0, 0)(実データで確認済み.TEST_01).リサイズ時のパディングも黒で統一する |
色を変える拡張は既定で無効にする. 舌の色そのものが正解ラベルであり,color jitter・彩度変更・ホワイトバランス変更はラベルを壊す.
| 使う | 使わない(既定) |
|---|---|
| 左右反転・微小回転・微小スケール・ランダムクロップ | 色相・彩度・明度ジッタ・グレースケール化・強い正規化 |
※ 撮影条件差の吸収のために色系の拡張を試す場合は,独立した実験として効果を測る.
| 項目 | 初期設定 |
|---|---|
| アーキテクチャ | ConvNeXt-Tiny(timm・ImageNet 事前学習) |
| 出力 | 5 クラス |
| 学習方式 | 全層ファインチューニング(データが少ないと判断した場合は段階的凍結を試す) |
| 最適化 | AdamW + cosine annealing(warmup 付き) |
| 早期終了 | 検証セットの macro F1 を基準にする |
まず 1 本を通すことを優先し,比較は timm での差し替えで後から行う.
設定 YAML の model・train 節で与える(SPEC_01「設定ファイル」).既定値は次のとおり.
| 項目 | 既定値 | 備考 |
|---|---|---|
model.name | convnext_tiny | timm のモデル名 |
model.pretrained | true | ImageNet 事前学習の重み(初回はサーバがダウンロードする).合成データの試験では false |
train.image_size | 224 | アスペクト比を保って長辺を合わせ,余白を黒で埋める(レターボックス) |
train.batch_size | 32 | — |
train.max_epochs | 40 | — |
train.patience | 8 | 検証の macro F1 がこのエポック数改善しなければ打ち切る |
train.lr | 1e-4 | AdamW |
train.weight_decay | 0.05 | AdamW |
train.warmup_epochs | 2 | 線形 warmup の後 cosine で減衰 |
train.inner_val_ratio | 0.15 | 交差検証の各 fold で,学習側から早期終了用の検証データを取る割合(下記) |
train.num_workers | 4 | DataLoader |
train.amp | true | CUDA のときだけ混合精度 |
train.class_weighting | none | 損失のクラス重み(下記「クラス不均衡」→「対策の設定」) |
train.sampler | random | 学習データの抜き出し方(同上) |
cuda(CUDA_VISIBLE_DEVICES で割り当てられた GPU),無ければ CPUmax_epochs の終わりで 0 になる cosine>)で判定し,同点なら先のエポックを残すconvnext_tiny の事前学習の重みは timm の既定タグ convnext_tiny.in12k_ft_in1k(ImageNet-12k で事前学習し ImageNet-1k で微調整したもの)が使われる1 つのジョブで 5 つの fold を順に学習・評価し,1 つの実験ディレクトリ runs/<実験ID>/ にまとめて書き出す.「ややあり込み」と「ややあり除外」は別のジョブ(別の実験 ID)として回す.
data.severities に関わらず同じ分割.上記「程度(ややあり)の扱い」)train.inner_val_ratio の患者を検証用に取る(患者単位・9 グループ層化.data.severities を適用する前の全程度から取る.seed は split.seed + fold 番号.学習の seed も同じ)data.severities を学習用と検証用にだけ適用する(held_out には適用しない.下記)held_out は data.severities に関わらず全程度の画像で評価し,次の部分集合ごとに指標を出す.これで 2 つの条件のジョブを同じ土俵で比べられる.
| 部分集合 | 内容 | 用途 |
|---|---|---|
all | held_out の全画像 | ややあり込みの全体性能 |
common | held_out のうち strong と normal | 2 条件の比較(両条件とも学習に含まれる部分) |
mild | held_out のうち mild | 境界例の扱い |
common と all)mild では加えて予測の内訳を出す: 淡紅と予測された割合・元のクラスと予測された割合・その他のクラスと予測された割合(クラス別と全体)pooled)と,混同行列の全 fold の合計も出すmild には淡紅の正解が無いため,5 クラスで平均すると淡紅への予測が F1 = 0 として平均を下げてしまう).クラス別の指標も正解に現れるクラスだけを出す.淡紅への流れ込みは mild の予測の内訳で見るpredictions.csv に保存し,回収しないSPEC_01「実験の出力」の名前に従う.
| ファイル | 内容 | 回収 |
|---|---|---|
config.yaml | 解決済み設定・git ハッシュ・seed | する |
metrics.json | fold ごと・fold 平均の指標(上記の部分集合ごと),各 fold の最良エポック,所要時間(下記) | する |
history.csv | fold・エポックごとの学習損失・検証損失・検証 macro F1・学習率・そのエポックの所要秒数 | する |
confusion_matrix.csv | 全 fold 合計の混同行列(common と all) | する |
log.txt | ログ | する |
figures/*.png | 学習曲線・混同行列 | する |
split.json・predictions.csv | 分割と個票の予測(患者 ID を含む) | しない |
checkpoints/fold<k>.pt | 各 fold の最良の重み(サーバには常に保存する) | --with-checkpoints のときだけ |
所要時間の記録(2026-09-28 ユーザー要望): metrics.json に次を含める.モデル間・条件間で計算コストも比べられるようにするため.
すべての分割は患者単位で行う. 同一患者の画像が複数の分割に跨ってはならない.
現在のデータセットは 1 患者 1 枚であることを確認済みのため(2026-09-27.TEST_01),画像単位の分割が患者単位の分割と一致する.ただし実装は常に patient_id 単位で分割し,将来同一患者の複数枚が追加されても正しく動くようにする.
| 局面 | 方式 |
|---|---|
| 日常の試行 | 患者単位の単一分割(train / val / test).クラス分布を層化する |
| 結論を出すとき | 患者単位の 5-fold 交差検証 |
patient_id と seed から決定的に生成し,config.yaml に記録する<label>_<severity> の 9 グループ)で行う.「ややあり除外」の条件で mild を抜いた後も,各分割のクラス比が保たれるようにするため(下記「程度(ややあり)の扱い」).最小のグループは 51 枚で,比率 0.15 なら各分割に 8 枚程度入る
label と severity で決める.同じ患者に複数の severity がある場合(将来の複数枚)は,最も強いもの(strong > mild > normal)を使うStratifiedKFold(shuffle あり・seed 指定.層化ラベルは層化グループ)を使う.評価側を held_out と呼ぶ(fold 内でさらに val を取るかは Step 4・5 で決める).患者数が fold 数に満たないグループがあれば停止するruns/ にのみ保存する.SPEC_01「回収ホワイトリスト」)「ややあり」(mild)は,臨床上は淡紅(正常)と判断されることが多い境界例である(2026-09-27 ユーザー確認).学習に入れるか否かで結果が変わりうるため,入れる場合と入れない場合の両方で実験する.
設定 data.severities(SPEC_01「設定ファイル」)で使う程度を選ぶ.
| 条件 | data.severities | 使う画像 | 枚数 |
|---|---|---|---|
| ややあり込み | [strong, mild, normal](既定) | すべて | 1085 |
| ややあり除外 | [strong, normal] | 「あり」と淡紅(正常) | 754 |
label は元のまま(5 クラス).「ややあり」を淡紅に付け替える扱いは行わないnormal のみ,他の 4 クラスは strong が残るため)data.severities に関わらず,manifest 全体(1085 枚)で 1 回だけ行う.条件ごとの除外は,分割の後に各分割(train / val / test)から該当する画像を取り除いて適用する| 評価 | 対象 | 目的 |
|---|---|---|
| 条件間の比較 | test のうち strong と normal(両条件に共通する部分) | 同じデータで 2 つの条件のモデルを比べる |
| ややあり込みモデルの層別評価 | test 全体と,mild の部分 | 境界例でどれだけ誤るか(「評価指標」の層別評価) |
| ややあり除外モデルに mild を推論させる | test のうち mild | mild の画像が 淡紅と予測される割合と元のクラスと予測される割合を出す.臨床で淡紅と判断されやすいという知見がデータ上でも成り立つかを確かめる |
mild の画像に対して淡紅と予測することを「誤り」と断定しない.上表の割合として報告し,解釈は人間が行う実データのクラス分布は最大 / 最小 = 380 / 104 ≒ 3.7 倍(TEST_01).中程度の不均衡であり,初回は対策を入れずに素の結果を見る.混同行列でどのクラスが落ちているかを確認してから,クラス重み付き損失等を独立した実験として追加し,効果を切り分けて評価する.
程度ラベル(severity)を含めた 9 クラス分類は採らない(最小クラス 51 枚・不均衡 6.6 倍となり学習が不安定になる.目的は TIASjudge の 5 分類の置き換えであるため).
素の結果(TEST_03)で紫・淡白の再現率が低かったため,次の 2 つを設定で選べるようにする.既定はどちらも「対策なし」で,既定のままなら従来と同じ学習になる.
| キー | 値 | 内容 |
|---|---|---|
train.class_weighting | none(既定)・inverse・sqrt_inverse | 損失のクラス重み.クラス c の学習画像数を n_c,全体を N,クラス数を K として,inverse は w_c ∝ N / (K・n_c),sqrt_inverse はその平方根.重みは学習データでの平均が 1 になるよう正規化する(Σ_c (n_c / N)・w_c = 1.損失の大きさを対策なしと揃え,学習率の意味を変えないため) |
train.sampler | random(既定)・balanced | balanced は各画像を 1 / n_c の確率で重複ありで抜き出し,どのクラスもほぼ同じ頻度で学習に出す.1 エポックに抜き出す枚数は学習画像数と同じ |
data.severities を適用した後)**から数える.検証用・held_out の枚数は使わないCrossEntropyLoss(weight=w) の既定は重みの和で割るため,上の正規化が効かなくなる.正規化で揃うのは学習データ全体での平均であり,1 つのミニバッチの損失はクラス比の偏りに応じて対策なしとずれうるsampler: balanced でも,学習時に端数のバッチを捨てる規則(「学習の設定」)は従来どおりかかるclass_weighting と sampler: balanced は同時に指定できるが,偏りを二重に補正することになるため実験では組み合わせないmetrics.json に,使った対策(トップレベルの imbalance)と,fold ごとのクラス別枚数・クラス重み・1 回の抜き出しで各クラスが選ばれる確率(各 fold の class_balance.使わない側は null)を記録するhistory.csv の学習損失は重み付きの値になる(検証損失は重みなし).対策なしの run と学習損失を直接比べない効果は,対策なしの結果と同じ seed・同じ fold どうしの対応比較で評価する(common の macro F1 と,クラス別の再現率・適合率・混同行列).少ないクラスの再現率が上がっても,他クラスの適合率が下がる入れ替わりが起きうるため,全体の数字だけで判断しない.
ラベルが舌の色そのものであり,データが少ないため,舌領域の色の統計量を特徴にした古典的な分類器を CNN と同じ分割・同じ評価で比べる(tias-train color-cv.結果は TEST_05).GPU は使わない.
元解像度の画像の舌領域の画素だけから取る(背景は RGB がすべて 0 の画素.レターボックス縮小は補間で背景の黒が縁に混ざるため使わない).長辺が color.max_side(既定 512.null で縮小しない)を超える画像は最近傍法で縮小する(新しい色を作らないため).画素を CIE L*a*b*(D65)に変換し,次の 107 次元を作る.
| 特徴 | 次元 |
|---|---|
| L*・a*・b*・C*(彩度 √(a*² + b*²))・h(色相角 atan2(b*, a*).度,(−180, 180])それぞれの平均・標準偏差(母標準偏差.ddof=0)・10/25/50/75/90 パーセンタイル | 5 × 7 = 35 |
| a*-b* の 2 次元ヒストグラム(a* 0, 50・b* −10, 30 を 8 × 8 に等分.舌領域の画素数に対する割合) | 64 |
| L* のヒストグラム(20, 100 を 8 等分.割合) | 8 |
設定の color 節で指定する(CNN の設定には不要.color 節が無い設定で color-cv を実行すると停止する).
| キー | 値 | 内容 |
|---|---|---|
color.classifier | logistic・hist_gb | 標準化+多クラスのロジスティック回帰,または scikit-learn の HistGradientBoostingClassifier |
color.logistic.c_values | 正の数のリスト(既定 0.01・0.1・1・10・100) | 正則化の強さ C の候補 |
color.hist_gb.learning_rates × max_depths | 正の数・正の整数または null のリスト | 学習率と木の深さの候補(組み合わせ) |
color.max_side | 正の整数または null(既定 512) | 特徴抽出の前に縮小する長辺の上限 |
split.json が完全に一致する)train.class_weighting のクラス重み(「クラス不均衡」)を sample_weight として使う.train.sampler: balanced は未対応で,指定すると停止するhistory.csv・学習曲線・重み(checkpoints/)は出さない.metrics.json の environment.model_name は color_logistic・color_hist_gb,最良エポックは null で,fold ごとの採用した候補(selected_params)と候補ごとの検証 macro F1(candidates),特徴名(color_classifier),特徴抽出の所要秒数(timing.feature_extraction_seconds)を加える| 指標 | 役割 |
|---|---|
| macro F1 | 主指標(クラス不均衡下で少数クラスを無視できないため) |
| accuracy | 参考(クラス分布に引っ張られる) |
| クラス別 precision / recall | どのクラスが弱いかの特定 |
| 混同行列 | 誤りの方向(隣接クラスとの混同か,飛んだ誤りか)の確認 |
severity ごと(strong / mild)にクラス別 recall を出す.「ややあり」(mild)は境界例であり誤りが集中すると予想されるため,全体の指標だけでは改善の効果が見えない既存 TIASjudge のルールベース診断を同一テストセットで実行し,同じ指標で並べる.実装の取り込み方(移植 / 別リポジトリ参照)は未定とし,ベースライン実装に着手する時点で決める.
config.yaml に git コミットハッシュとともに記録するtorch.use_deterministic_algorithms)は速度とのトレードオフのため求めない.代わりに同一設定で複数 seed を回して振れ幅を確認する