RLVR 用に Yolo で崩れた手の検出モデルを作る
カテゴリ:deeplearning
ラベル
OK・NG の2値より、mild broken を加えた3値の方が報酬関数が滑らかになり学習が安定する。
- hand_ok
- hand_mild_broken
- hand_broken
比率は ok:mild:broken = 1:1:1 程度。本当はモデルの出現率に一致させるとよい。ここのバランスが崩れているとただの手を検出するだけになってしまい、手を隠すようになる。
正常な手にペナルティが入るのを絶対に避けたい場合は、正常な手のデータ枚数と多様性とを増やす。
データセットの多様性
- イラスト・アニメ・リアル
- ポーズ
- 複数人
hand_broken の多様性を確保する
- 指が多すぎる / 少なすぎる
- 指が長すぎる
- 関節が多い / 少ない
- 指の融合
- 不自然な向き
- 手首が消えている / 重なっている
報酬関数の設定
YOLO の出力はそのまま 0/1 にしない。
- 明確に壊れてるほど強い罰
- 正常手は 0 に近づく
# 検出された broken hand bbox の最大 confidence
conf = max(det.conf for det in detections if det.cls == "hand_broken")
# 報酬(連続値)
reward = -conf
# もしくは reward = 1.0 - conf
報酬 hacking 対策(手を隠す問題)
「良い手を描く」より「検出されない手を描く」方が簡単
なので以下のような手を描くようになる。
- 手を画面外に追い出す
- 物体・服・髪で隠す
- 指を塊にして手と分からなくする
- 極端に小さく描く
- ノイズっぽく潰す
シンプルな報酬関数では「手の未検出 = 報酬最大」
なので「手が存在する」こと自体を正の報酬に含める。
hand_present_conf = max(hand_detector.conf) # 単純に手が存在するか検出するモデル
broken_conf = max(broken_hand_detector.conf)
reward = (
+ α * hand_present_conf # 手が見えるほど加点
- β * broken_conf # 壊れてるほど減点
)
person detector で人物が存在するときのみ報酬を加えるようにすることも検討する。
手を小さく描いて検出を回避する問題の対策
手を描く面積が小さい場合は報酬減額(罰を追加)。人の大きさを検出してその比率で報酬量を調整する必要がある。背景に人がいる場合はとても面倒。
area = bbox_w * bbox_h / (H * W)
reward += γ * clamp(area, 0, A_max)
手のような何かを検出
seg mask / keypoint confidence を presence の proxy に使う。
- mediapipe hands
- SAM + hand class prompt
- openpose hand keypoints
実際のマルチ報酬合成
w1~w3 は手が存在することを検出して加点する項目。手が描かれた時点でプラスの報酬があるので、hand_ok を追加で加点する必要がない。
reward = (
+ w1 * hand_presence
+ w2 * hand_area_score
+ w3 * keypoint_conf
- w4 * hand_mild_broken_conf
- w5 * broken_hand_conf
)
# 手が存在しない場合は罰
if hand_presence < p_thresh:
reward = base_penalty_for_missing_hand # もしくは presence を低く罰する
重みの目安
| 項目 | 初期値 |
|---|---|
| hand presence | 1.0 |
| area | 0.3 |
| keypoint | 0.5 |
| mild_broken | 1.0 |
| broken | 1.5 |
- 重みは運用してみて適宜調整する
- 重みは正規化する
- mild_broken や broken の重みは手を手薄にしない(false negative を避ける)
非線形変換+クリッピングを使う方法もある。
# normalize inputs to [0,1]
p = hand_presence
a = clamp(hand_area / area_ref, 0, 1)
k = avg_keypoint_conf
mild = hand_mild_broken_conf
brk = broken_hand_conf
occl = occlusion_score # 0..1, 1 = heavy occlusion
# gated positive score
pos = sigmoid( 6*(0.25*p + 0.25*a + 0.25*k) - 3 ) # maps to (0,1)
pen = (0.6*brk + 0.3*mild + 0.5*occl) # weighted penalty
reward = clip( pos - pen - missing_hand_penalty*(1-p), -1, +1 )
anti-hacking curriculum
各フェーズで報酬の比率を段階的に変える。学習が安定するので絶対にやったほうがいい。
フェーズ別強化学習
| フェーズ | 目的 |
|---|---|
| Phase 1 | 手を描く癖をつける |
| Phase 2 | 壊さず描く |
| Phase 3 | 自然な形に |
最終手段:手を「消すと罰」
この施策は分布を歪めやすい。
if hand_present_conf < τ:
reward = -C