PyTorchで顔キーポイント座標回帰の誤差分析をして、左右反転augmentationを検証した
はじめに
前回までで、PyTorchを使った顔キーポイント座標回帰モデルを一通り構築しました。
今回はその続きとして、モデルがどこで失敗しているのかを確認するために、以下の分析と検証を行いました。
- prediction可視化
- worst sample分析
- 部位別誤差分析
- 左右反転augmentationの検証
単にvalidation lossやKaggleのスコアを見るだけではなく、予測結果を画像上で確認し、どの部位やどのような画像で誤差が大きくなるのかを調べていきます。
前回までにできていたこと
前回までに、以下の処理を一通り実装しました。
- Kaggle Notebookにデータセットを追加
-
training.zip/test.zipを解凍 - CSV内の画像文字列から
96×96の画像を復元 - 顔キーポイントの可視化
- PyTorch Dataset / DataLoader の作成
- CNNによる座標回帰モデルの構築
- train / validation 分割
- 座標を
0〜1に正規化して学習を安定化 - best validation model の保存
-
test.csvに対する推論 -
submission.csvの作成 - Kaggleへの提出
前回結果
| 指標 | 値 |
|---|---|
| Public Score | 3.59348 |
| Private Score | 3.56800 |
| Best Validation Loss | 約 0.00048 |
ここまでで、画像から30個の座標値を予測する基本的なCNNモデルは作成できました。
ただし、スコアだけでは「どこが苦手なのか」がわかりにくいため、今回は誤差分析を行います。
この記事でやること
この記事では、以下の流れでモデルの失敗傾向を確認します。
- validation画像に正解座標と予測座標を重ねて表示する
- 誤差が大きい画像を確認する
- 顔の部位ごとの平均誤差を比較する
- 左右反転augmentationを試す
- 改善した点・改善しなかった点を考察する
今回やったこと
1. prediction可視化
まず、validation画像に対して、
- 正解座標
- 予測座標
を重ねて描画しました。
さらに、正解と予測のズレを線で表示することで、どの方向にどれくらい外れているのかを確認できるようにしました。
これにより、lossの数値だけでは見えにくいモデルの癖を確認できます。
例えば、以下のような点を見ます。
- 目や鼻は安定しているか
- 口周りで大きくズレていないか
- 顔全体が少し平行移動していないか
- 特定の部位だけが大きく外れていないか
座標回帰では、数値上のlossだけを見るよりも、実際に画像上へ重ねて確認した方が失敗の原因を把握しやすいです。
2. worst sample分析
次に、validationデータ全体に対して誤差を計算し、誤差が大きい画像を確認しました。
errors = np.sqrt(((preds_xy - targets_xy) ** 2).sum(axis=2))
mean_errors = errors.mean(axis=1)
ここでは、各キーポイントについて予測座標と正解座標のユークリッド距離を計算し、画像ごとの平均誤差を mean_errors としました。
その後、mean_errors が大きい画像を並べて確認しました。
誤差が大きくなりやすかった画像
特に失敗しやすかったのは、以下のような画像でした。
- 笑顔
- 口が大きく変形している顔
- ぼやけた顔
- 眉毛が薄い顔
- 複数人物が写る画像
- 光の当たり方で顔の一部が見えにくい画像
比較的安定していた画像
逆に、以下のような画像では比較的安定していました。
- 正面顔
- 真顔に近い表情
- 顔のコントラストがはっきりしている画像
- 目・鼻・口の位置関係が標準的な画像
この時点で、モデルは大まかな顔の構造は捉えられているものの、表情変化や画像品質の影響を強く受けているように見えました。
部位別誤差分析
validation全体で、部位ごとの平均誤差も確認しました。
結果
部位別誤差の上位は、以下のようになりました。
mouth_left_corner: 3.72px
mouth_right_corner: 3.66px
mouth_center_bottom_lip: 3.42px
right_eyebrow_outer_end: 3.30px
mouth_center_top_lip: 3.14px
left_eyebrow_outer_end: 3.02px
nose_tip: 2.97px
...
全体として、口周りと眉毛外側の誤差が大きい傾向がありました。
考察
口周りの誤差が大きい
口周りは、今回の分析で最も誤差が大きい部位でした。
理由としては、以下のような要因が考えられます。
- 表情変化が大きい
- 口が開く・閉じるで形が大きく変わる
- 歯が見える場合と見えない場合がある
- ひげや影の影響を受けやすい
- 個人差が大きい
特に口角や唇の上下は、顔の中でも変形自由度が高い部位です。
そのため、単純なCNNでは、目や鼻に比べて安定した位置を学習しにくいのではないかと考えました。
眉毛外側も誤差が大きい
眉毛の外側も誤差が大きめでした。
これは、以下の理由が考えられます。
- 眉毛が薄い場合に境界が曖昧になる
- 髪や影と混ざりやすい
- 光で飛んで見えにくくなる
- 顔の角度によって外側の位置が不安定になる
眉毛外側は、目頭や鼻のように強い境界がある部位ではありません。
そのため、画像によっては「どこを眉毛の端と見るか」が曖昧になりやすく、誤差が大きくなったと考えられます。
目頭周辺は比較的安定していた
一方で、目頭周辺は比較的誤差が小さくなりました。
理由としては、以下のような特徴があるためだと思われます。
- 目の境界はコントラストが高い
- 鼻との位置関係が安定している
- 顔の中でも幾何的に位置が大きく変わりにくい
この結果から、モデルは顔の中でも境界がはっきりしていて、位置関係が安定している部位を比較的うまく学習できていることがわかりました。
左右反転augmentation
次に、左右反転augmentationを試しました。
顔キーポイントの座標回帰では、画像を単純に左右反転するだけではラベルが壊れてしまいます。
そのため、左右反転時には以下の処理も同時に行いました。
- x座標を反転する
- left / right のキーポイントを入れ替える
実装イメージ
if self.use_flip and np.random.rand() < self.flip_prob:
image = np.fliplr(image).copy()
keypoints = flip_keypoints(keypoints)
左右反転した画像をすべて追加してデータ数を単純に倍増させるのではなく、学習時にランダムに反転させる形にしました。
理由としては、左右反転は完全に別画像を増やす処理ではなく、元画像に近いサンプルを追加する処理だからです。
そのため、今回のように正面顔が多いデータでは、データ量を単純に増やしても、多様性の増加は限定的になる可能性があります。
また、epochごとに反転されるサンプルが変わるため、学習にある程度の揺らぎを持たせることもできます。
結果
左右反転augmentationを試した結果、
- validation loss
- 部位別誤差
ともに明確な改善は見られませんでした。
むしろ、
- 元々うまく予測できていた画像
- 正面顔
- 顔の配置が安定している画像
では、少し精度が悪化する傾向も見られました。
なぜ左右反転で改善しなかったのか
可能性1:モデルが浅く、増えた情報を受け止めきれていない
今回使っているモデルは、比較的シンプルなCNNです。
そのため、左右反転によって顔向きのバリエーションが増えても、モデル側がその情報を十分に使い切れていない可能性があります。
イメージとしては、以下のような流れです。
左右反転データが入る
↓
顔向きのバリエーションは増える
↓
しかし単純CNNには少し難しくなる
↓
平均的な位置へ寄る力が強くなる
↓
元々うまく予測できていたサンプルまで少し甘くなる
augmentationは、入れれば必ず良くなるものではありません。
モデルの表現力やデータの性質によっては、むしろ学習を少し難しくすることもあります。
可能性2:顔には左右非対称性がある
人の顔には、完全な左右対称ではない要素があります。
例えば、以下のようなものです。
- 髪の流れ
- 光や影の入り方
- 眉の形
- 表情の癖
- 顔の向き
今回のタスクは分類ではなく座標回帰です。
そのため、微妙な左右非対称性も座標予測の手がかりになっている可能性があります。
そこに対して強制的に左右反転を入れると、モデルにとっては特徴が少しぼやけることがあるのではないかと考えました。
可能性3:データセットの顔がかなり整っている
今回のデータセットは、以下のような特徴があります。
- 顔が画像の中央付近にある
- 正面顔が多い
- 画像サイズが
96×96にそろっている - 顔の位置やスケールがある程度そろっている
そのため、左右反転を行っても「新しい難しい条件」を追加したというより、すでに近い条件の画像を増やしただけになった可能性があります。
つまり、今回のデータセットでは、モデルがすでに左右両方のパターンをある程度見ていたのかもしれません。
今回の結論
今回のデータセットでは、左右反転augmentationによる明確な改善は見られませんでした。
理由としては、以下のように考えています。
- 正面顔が多く、左右反転による新情報が少なかった
- 単純CNNでは増えたバリエーションを十分に扱えなかった
- 顔の微妙な左右非対称性が座標回帰に影響していた可能性がある
今回の結果から、少なくともこのNotebookでは、左右反転augmentationは採用しない方針にしました。
次にやる予定
次は、画像品質に関するaugmentationを試す予定です。
候補としては、以下を考えています。
- brightness
- contrast
- blur
- gamma
- 軽いrotation
今回の分析から、単純な顔向きの変化よりも、
- ぼやけ
- 低コントラスト
- 薄眉
- 光の当たり方
- 口周りの変形
の方がボトルネックになっていそうでした。
そのため、次は画像品質系のaugmentationを中心に試していきます。
今回得られた学び
今回の分析で、以下のことがわかりました。
- augmentationは入れれば必ず良くなるわけではない
- 座標回帰では、画像変換とラベル変換を必ずセットで考える必要がある
- lossだけでなく、失敗例や部位別誤差を見ると次の改善方針を決めやすい
- 口周りや眉毛外側など、変形しやすい部位・境界が曖昧な部位は誤差が大きくなりやすい
- 今回のデータでは、顔向きよりも画像品質や表情変化の方が重要そうだった
まとめ
今回は、PyTorchで作成した顔キーポイント座標回帰モデルに対して、
- prediction可視化
- worst sample分析
- 部位別誤差分析
- 左右反転augmentation検証
を行いました。
その結果、モデルは目や鼻のような安定した部位は比較的うまく予測できている一方で、口周りや眉毛外側のように変形しやすい部位・境界が曖昧な部位では誤差が大きくなることがわかりました。
また、左右反転augmentationを試しましたが、今回のデータセットと単純CNNの組み合わせでは明確な改善は見られませんでした。
今回の分析によって、次に試すべき改善方針として、brightness / contrast / blur などの画像品質系augmentationが有力そうだと考えています。
単にスコアを見るだけでなく、失敗例や部位ごとの誤差を見ることで、モデル改善の方向性がかなり見えやすくなりました。