
VLMにBBoxをSFTすると、モデルは一体何を学習しているのか?
最近、VLM(Vision Language Model)に画像を入力し、物体のBounding Boxを出力させる実験をしている。
例えば、画像内にある物体について、次のような形式で出力させる。
[
{
"name": "connector_A",
"bbox": [120, 340, 80, 50]
}
]
BBoxの形式は [x, y, width, height]。座標は画像サイズに依存しないよう、0〜1000の範囲に正規化している。
ここで、VLMをLoRAでSFT(Supervised Fine-Tuning)すればBBoxの精度を改善できるのではないか、と考えた。画像と正解BBoxのペアを大量に用意して学習させる。一見すると、普通の物体検出モデルと同じように「画像内の位置」を学習してくれそうだ。
しかし、ふと疑問が湧いた。VLMにBBoxをSFTすると、モデルは一体何を学習しているのだろうか?
SFTで学習しているのは「正解文字列」
VLMのSFTをかなり単純化すると、やっていることは言語モデルの学習と同じである。例えば正解データが [120, 340, 80, 50] だったとする。モデルは [、120、,、340、,、80、,、50、] というトークン列を順番に予測する。
つまり損失関数は基本的にCross Entropy Lossであり、「次の正解トークンをどれだけ高い確率で予測できたか」を評価している。
ここで重要なのは、BBoxの幾何学的なズレを直接評価しているわけではないという点だ。
例えばGTが [100, 100, 200, 200] だったとする。
- 予測1:
[101, 101, 200, 200] - 予測2:
[800, 800, 50, 50]
人間から見ると、予測1はほぼ正解である。一方、予測2は完全に外れている。物体検出の観点では、この2つは明らかに異なる。
しかし通常のSFTでは、100 → 101 が「座標的に少しだけズレた」ということを直接理解して損失を計算しているわけではない。あくまで正解トークンと違うトークンを出力したという問題として扱われる。つまりSFTのLoss空間と、BBox評価の空間にはズレがある。
IoUを見て学習しているわけではない
物体検出では、BBoxの評価にIoU(Intersection over Union)がよく使われる。予測BBoxとGT BBoxがどれくらい重なっているかを評価する指標だ。IoU=0.9ならかなり正確、IoU=0.0なら全く重なっていない。
YOLOなどの物体検出モデルでは、BBoxの位置やサイズに関するLossが明示的に存在する。モデルは「BBoxをもう少し右に動かした方がいい」「幅をもう少し広げた方がいい」という方向の勾配を受け取ることができる。
一方、VLMの通常のSFTではそうではない。GTが [500, 300, 100, 50] なのに [510, 300, 100, 50] と出力した場合でも、モデルに与えられるのは基本的に「500に対応する正解トークンを出せなかった」という言語モデルとしての誤差である。「10だけ右にズレています」という情報を直接Lossとして受け取っているわけではない。
ここが非常に面白い。VLMにBBoxを出力させていると、見た目は物体検出をしている。しかし学習の仕組みを見ると、実際には「BBoxを表現した文字列」を生成する学習をしている。
では、画像と座標の対応は学習できないのか?
ここまで読むと、「じゃあSFTでBBoxを学習させても意味がないのでは?」と思うかもしれない。もちろん、そんなことはない。
VLMにはVision Encoderが存在し、画像特徴がLanguage Model側に入力される。そのため、
画像特徴
↓
物体がこの辺にある
↓
対応する座標トークンを生成
という対応関係を学習すること自体は可能である。実際、Grounding系のVLMでは座標をテキストとして出力するモデルも存在する。
つまりSFTでも、**「この画像特徴のときは、この座標列を出力する」**という対応関係は学習できる。
ただし問題は、その学習効率だ。数字を通常のテキストトークンとして扱う場合、499・500・501 という座標の近さと、100・900 という座標の遠さが、BBoxの幾何学的な距離としてLossに反映されるとは限らない。モデルにとって重要なのはトークン予測である。人間が考える「座標空間」と、モデルが学習する「トークン空間」は同じではない。
loc tokenという考え方
そこで使われるのが、位置専用トークンである。例えば座標を0〜1000に正規化し、<loc_0> <loc_1> <loc_2> … <loc_999> のようなトークンを用意する。
BBoxを [120, 340, 80, 50] ではなく、<loc_120><loc_340><loc_80><loc_50> として出力する。
こうすることで、座標値を通常の数字ではなく「位置を表す専用語彙」として扱える。少なくとも、"120" という数字がTokenizerの都合で "1"+"20" や "12"+"0" のように分割される問題を避けやすい。
ただし、loc tokenを使えば自動的に <loc_120> と <loc_121> は近いとモデルが理解するわけではない。Cross Entropy Loss上では、基本的に別のクラスである。位置トークンの設計によってBBox生成を扱いやすくすることはできるが、IoU Lossと同じになるわけではない。
結局、SFTは何を学習しているのか?
自分なりの現在の理解はこうだ。
VLMのBBox SFTは、物体検出器を学習しているというより、「画像特徴から位置表現を生成する方法」を学習している。
画像のどこに物体が存在するかという情報はVision Encoderから得る。そしてLanguage Modelは、その視覚情報を <loc_120><loc_340><loc_80><loc_50> という位置表現に変換する。つまり、
画像
↓
視覚特徴
↓
位置情報の推定
↓
座標トークン列
という変換を学習している。
ただし最終的な学習目標はToken Lossである。こちらが本当に改善したい指標が、IoU・Precision・Recall・F1なのであれば、SFTの目的関数と最終評価指標にはズレがある。
そして新しい疑問が生まれる
現在、自分のタスクではGT BBoxと推論BBoxからIoUを計算できる。さらにBBoxをマッチングし、見逃し・過検知・IoU・Precision・Recall・F1までコネクタ単位で評価できる。つまりモデルの出力が良いBBoxなのか悪いBBoxなのかは評価できる。
では、この評価値を直接学習に使えないのだろうか。例えば、
- IoUが高い → 高Reward
- 見逃し → Penalty
- 過検知 → Penalty
というRewardを設計する。SFTで「BBoxの出力形式」を学習させた後、BBoxの品質そのものをRewardとして最適化する。
こうすると初めて、「座標文字列を正しく生成する」ではなく「GTに近いBBoxを生成する」という目的でVLMを学習できるのではないか。
SFTだけでBBoxを学習させようとしていたが、考えれば考えるほど、これはSFTの問題というよりReward設計の問題なのかもしれない。
そしてここまで考えて、また一つ思う。
そこまでしてVLMにBBoxを出させる必要、本当にある? YOLOでよくない?


