지난 글에서 통합형 멀티모달 모델의 이점 중 하나로 「그림 속 한 지점을 가리키며 대화하는 것」을 적었다. 그 가리키기가 실제로 어떻게 이뤄지는지는 넘어갔는데, 이 글이 그 자리다.
말로 자리를 가리키는 일을 그라운딩(grounding)이라고 부른다. 정확히는 언어 표현을 이미지 속 영역에 대응시키는 것이고, 좌표를 다루므로 공간 그라운딩이라고 구분해 부르기도 한다. 「빨간 우산」이라는 넉 자를 받아 사진 위의 네모 하나를 내놓는 일이다.
이게 왜 따로 다룰 만한 주제인가 하면, 답이 문장이 아니라 숫자 넷이기 때문이다. 문장은 조금 어색해도 읽는 사람이 알아서 이해하지만, 숫자 넷은 20픽셀만 어긋나도 엉뚱한 것을 잘라 온다. 그리고 실무에서 어긋나는 이유의 태반은 모델이 못 봐서가 아니라 좌표계를 안 맞춰서다.
세 갈래 작업
그라운딩이라는 말 아래에 성격이 다른 작업 셋이 들어 있다.
지시 표현 이해(referring expression comprehension)는 「왼쪽에서 두 번째 빨간 우산」처럼 대상을 하나로 좁히는 문구를 받아 상자 하나를 낸다. 표현이 잘 쓰였다면 정답이 하나뿐이라 채점이 깔끔하다.
구절 그라운딩(phrase grounding)은 문장 하나를 통째로 받아 그 안의 명사구마다 상자를 낸다. 「우산을 든 사람이 개와 함께 걷는다」면 우산·사람·개 셋이다. 여기서는 빠뜨림과 중복이 새 문제로 생긴다.
영역 설명은 방향이 반대다. 상자를 주고 그 자리에 무엇이 있는지 묻는다. 검색보다는 확인에 쓴다 — 앞의 두 작업이 낸 상자가 진짜 그것인지 되짚을 때다.
실무에서 가장 많이 쓰는 것은 첫 번째다. 「이 서류에서 계약 금액이 적힌 칸」, 「이 화면에서 저장 버튼」처럼 하나를 짚어 잘라 오는 용도가 압도적으로 많다.
모델은 어떻게 좌표를 내는가
방식이 셋이고, 어느 것을 쓰느냐가 정확도와 다루기 쉬움을 함께 바꾼다.
| 방식 | 어떻게 | 장점 | 약점 |
|---|---|---|---|
| 좌표 전용 토큰 | 0~999 같은 구간마다 토큰을 따로 만들어 어휘에 넣는다 | 한 좌표가 한 토큰이라 짧고, 형식이 어긋날 일이 없다 | 어휘가 늘고, 구간 수가 곧 해상도 상한 |
| 숫자를 그냥 텍스트로 | 412 208 566 430을 평범한 숫자 토큰으로 쓴다 |
모델 구조를 안 건드린다 | 자릿수마다 토큰을 쓰고, 형식이 깨질 수 있다 |
| 별도 검출 헤드 | 언어 모델은 질의 벡터만 내고 검출기가 상자를 그린다 | 작은 대상에 강하고 좌표가 정밀하다 | 구조가 늘고 학습이 복잡하다 |
앞의 둘은 결국 좌표를 언어로 취급하는 접근이고, 셋째는 검출 문제로 되돌리는 접근이다. 범용 VLM은 대개 앞의 둘이고, 화면 조작이나 문서 처리처럼 정밀도가 필요한 특화 모델은 셋째를 붙이는 경우가 있다.
어느 방식이든 우리가 지시에 못 박아야 하는 것은 같다. 숫자 넷이 무엇을 뜻하는지와 무엇을 기준으로 한 값인지다. 좌상단과 우하단인지, 좌상단과 폭·높이인지, 픽셀인지 01인지 01000인지. 이걸 안 적으면 모델은 학습 때 가장 자주 봤던 형식을 낸다 — 그리고 그게 무엇인지는 모델마다 다르다.
좌표가 어긋나는 진짜 이유
여기가 이 글에서 가장 실무적인 부분이다. 모델이 대상을 정확히 찾았는데도 잘라 온 그림이 어긋나는 경우가 흔한데, 원인은 거의 늘 하나다. 모델이 본 그림과 우리가 가진 그림이 다르다.
비전 모델은 대개 정해진 크기의 정사각형 입력을 받는다. 가로가 긴 사진을 넣으면 비율을 유지한 채 줄인 다음 위아래에 여백을 채운다 — 이 방식을 레터박스라고 부른다. 모델은 여백까지 포함한 정사각형을 보고, 좌표도 그 정사각형 기준으로 낸다.
되돌리는 계산 자체는 짧다.
def to_source(box, src_w, src_h, in_size=896, grid=1000):
"""모델이 낸 0~grid 정규화 좌표를 원본 픽셀로 되돌린다."""
scale = min(in_size / src_w, in_size / src_h)
pad_x = (in_size - src_w * scale) / 2
pad_y = (in_size - src_h * scale) / 2
out = []
for i, v in enumerate(box): # x1, y1, x2, y2
px = v / grid * in_size # 정사각 픽셀로
px -= pad_x if i % 2 == 0 else pad_y # 여백 제거
out.append(round(px / scale)) # 배율 되돌리기
return out
문제는 계산이 아니라 이 계산을 해야 한다는 사실을 모르는 것이다. 세로가 긴 사진에서는 좌우에 여백이 붙으므로 어긋나는 축이 바뀌고, 정사각형에 가까운 사진에서는 여백이 거의 없어 그냥 배율만 나눠도 얼추 맞는다. 그래서 정사각형 샘플로 테스트하면 멀쩡하다가 실제 데이터에서 어긋난다.
몇 가지를 미리 정해 두면 이 부류의 사고가 거의 사라진다.
- 원본 크기를 응답과 함께 들고 다닌다. 좌표만 저장하고 그 좌표가 어느 크기 기준이었는지를 안 남기면 나중에 되돌릴 수가 없다
- 전처리를 한 곳에만 둔다. 리사이즈를 API 클라이언트와 서버가 각각 하면 배율이 두 번 걸린다
- 되돌린 좌표로 실제 그림을 잘라 눈으로 한 번 본다. 이 확인 한 번이 IoU 지표 열 개보다 빠르다
자주 밟는 실패
좌표계를 맞추고 나면 남는 것은 모델의 한계다. 유형이 대체로 정해져 있다.
없는 것을 가리킨다. 「빨간 우산」이 사진에 없어도 모델은 대개 무언가를 하나 골라 답한다. 지시에 「해당하는 대상이 없으면 없다고 답하라」를 넣고, 부재 응답의 형식까지 정해 줘야 한다. 이건 프롬프트로 상당 부분 잡히는 문제라 값이 싸다.
개수를 못 센다. 「왼쪽에서 두 번째」, 「셋 중 가운데」 같은 서수 표현은 정확도가 눈에 띄게 떨어진다. 대상이 많고 비슷하게 생겼을수록 심하다. 서수를 쓰지 않고 구별되는 속성으로 표현을 바꾸면 좋아진다.
「왼쪽」의 기준이 흔들린다. 보는 사람 기준인지 사진 속 인물 기준인지 모델이 늘 같은 쪽을 고르지 않는다. 지시에 「보는 사람 기준」을 명시하는 것으로 대부분 해결된다.
작은 대상을 놓친다. 입력이 정사각형으로 줄어들면서 픽셀이 사라지기 때문이다. 원본을 겹치게 타일로 나눠 각각 물어보고 좌표를 원본으로 합치는 방식이 흔한 대응이다 — 여기서도 결국 좌표 되돌리기를 두 번 해야 한다.
언제 VLM이고 언제 전용 검출기인가
찾을 대상의 종류가 미리 정해져 있고 양이 많다면 전용 검출기가 여전히 낫다. 훨씬 빠르고, 좌표가 정밀하고, 같은 입력에 같은 답을 낸다.
VLM 그라운딩이 이기는 자리는 대상을 미리 나열할 수 없을 때다. 「금액이 적힌 칸」, 「경고를 뜻하는 아이콘」, 「다른 것들과 색이 다른 상자」처럼 클래스 목록으로는 못 적고 말로만 설명되는 대상이 그렇다. 이런 요청에 라벨을 붙여 검출기를 학습시키는 비용을 생각하면, 문장 한 줄로 되는 쪽의 값이 훨씬 싸다.
둘을 겹쳐 쓰는 것도 흔하다. 검출기로 후보 상자를 잔뜩 뽑고, 그중 어느 것이 「금액이 적힌 칸」인지만 VLM에게 고르게 하는 식이다. 이때 VLM은 좌표를 만들지 않고 번호만 고르므로 위에서 본 좌표 사고가 통째로 사라진다 — 정밀도가 중요한 곳에서 자주 쓰는 조합이다.
정리
- 그라운딩은 말을 이미지 속 영역에 대응시키는 일이고, 실무에서는 「하나 짚어 잘라 오기」가 대부분이다
- 좌표를 내는 방식은 전용 토큰·평범한 숫자·별도 검출 헤드 셋이다. 앞의 둘은 좌표를 언어로 다루고 셋째는 검출 문제로 되돌린다
- 숫자 넷의 뜻과 기준은 반드시 지시에 못 박는다. 안 적으면 모델마다 다른 형식이 나온다
- 어긋남의 가장 흔한 원인은 모델이 못 봐서가 아니라 레터박스 여백을 안 빼서다. 원본 크기를 좌표와 함께 들고 다닌다
- 부재·서수·좌우 기준은 프롬프트로 상당 부분 잡힌다. 작은 대상은 타일로 나눠 묻는다
- 대상을 미리 나열할 수 있으면 전용 검출기가, 말로만 설명되면 VLM이 유리하다. 검출기가 후보를 내고 VLM이 고르는 조합이 안전하다
읽어주셔서 감사합니다. 😊

