Classification
이미지 embedding이 정확한 landmark class로 분류되도록 학습한다.
첫 실험은 해석이 쉬운 cross_entropy를 사용한다.
Implementation note · 2026-05-21
Sprint 2 모델 학습을 위해 landmark-assistant-model-ver2에 추가한 실제 구현 기록이다.
기존 Sprint 1 학습 경로는 보존하고, image-label classification, image-text contrastive,
hard-negative margin을 함께 쓰는 새 학습 entrypoint를 분리했다.
최초 구현은 MobileCLIP2-S4를 중심으로 시작했지만, 현재 실험과 앱 후보는 MobileCLIP2-S3/S4를 같은 구조로 비교한다.
공유 문서는 landmark-assistant/docs에서 관리한다.
landmark-assistant-model-ver2 repo의 Markdown은 코드 근처의 짧은 개발 메모 또는 중앙 문서 링크로만 둔다.
팀원이 진행 상황, 실행 기준, 설계 이유를 확인할 때는 이 문서를 기준으로 삼는다.
Sprint 1 모델은 이미지 입력에서 랜드마크를 잘 찾는 것이 중심이었다. 하지만 Sprint 2에서는
자연어 검색, 비슷한 궁궐/문/전각 구분, hard negative 학습까지 같이 다뤄야 한다.
그래서 단순 class label만 쓰지 않고, 이미지별 한/영 caption과 confusing_with를
학습 신호로 사용하도록 구현했다.
Dataset labels.json
├─ image label / training_role
├─ caption_set(text_ko, text_en)
└─ confusing_with
↓
LandmarkMultitaskDataset
↓
MobileCLIP2-S3/S4 image encoder + text encoder
↓
Loss = classification + image-text contrastive + hard-negative margin
↓
metrics.json + prediction jsonl + low-margin csv
| 파일 | 역할 |
|---|---|
src/landmark_candidate/multitask_dataset.py |
caption_set과 confusing_with를 읽어 multi-task batch를 만든다. |
src/landmark_candidate/multitask_model.py |
MobileCLIP2-S3/S4 image/text encoder, partial unfreeze, LoRA, projection head를 구성한다. |
src/landmark_candidate/losses.py |
CE/CosFace/ArcFace, multi-positive contrastive, hard-negative margin loss를 제공한다. |
src/landmark_candidate/train_multitask.py |
새 학습 entrypoint. W&B, k-fold split, 평가 출력, low-margin 분석을 수행한다. |
scripts/run_multitask_tmux.sh |
서버에서 tmux로 multi-task 실험을 실행한다. |
scripts/mine_hard_negative_candidates.py |
학습 후 prediction log에서 confusion, low-margin, nearest negative 후보를 모아 다음 라벨링 검수 목록을 만든다. |
configs/experiments/*.yaml |
partial unfreeze, LoRA, ArcFace 비교 실험 config를 분리한다. |
이미지 embedding이 정확한 landmark class로 분류되도록 학습한다.
첫 실험은 해석이 쉬운 cross_entropy를 사용한다.
같은 landmark_id의 이미지와 caption은 가깝게, 다른 class는 멀게 만든다.
자연어 검색이 image encoder fine-tuning 이후에도 깨지지 않도록 잡아주는 역할이다.
confusing_with에 적힌 비슷한 landmark가 정답보다 너무 가까워지면 penalty를 준다.
광화문/근정문/궁궐 전각처럼 헷갈리는 케이스를 추적하기 위한 장치다.
가장 먼저 돌릴 기준 실험은 partial unfreeze + CE + hard negative 구성이다.
cd /workspace/landmark-assistant-model-ver2
source .venv/bin/activate
export DATA_ROOT=/workspace/landmark-assistant-model-ver2/Dataset
GPUS=1,2,3,4 NPROC=4 bash scripts/run_multitask_tmux.sh mobileclip2_s4_partial_unfreeze_ce_hardneg 0
서버에 이미 /workspace/landmark-assistant-model 이름으로 같은 repo를 clone해 둔 경우에는
해당 경로를 계속 사용해도 된다. 중요한 기준은 GitHub repo가
HyunKN/landmark-assistant-model-ver2인지 확인하는 것이다.
LoRA 비교 실험은 아래 명령으로 분리해서 실행한다.
GPUS=1,2,3,4 NPROC=4 bash scripts/run_multitask_tmux.sh mobileclip2_s4_lora_ce_hardneg 0
한 번 학습이 끝나면 모델이 실제로 헷갈린 class pair를 다음 라벨링 작업으로 되돌려야 한다.
이를 위해 run directory의 prediction log를 읽어 review 후보를 만든다.
이 스크립트는 labels.json을 자동 수정하지 않는다. 사람이 예시 이미지를 보고
confusing_with에 넣을지 결정한다.
python scripts/mine_hard_negative_candidates.py \
--run-dir runs/<run_name> \
--low-margin-threshold 0.05
| 입력 | 역할 |
|---|---|
predictions_val.jsonl, predictions_test.jsonl |
Top-1 오답과 Top-3 안의 가까운 negative landmark를 찾는다. |
low_margin_val.csv, low_margin_test.csv |
Top-1과 Top-2 차이가 작아 fragile한 케이스를 찾는다. |
| 출력 | 사용 방법 |
|---|---|
hard_negative_candidates.json |
후보 pair, 근거 count, 예시 Top-3를 포함한다. 라벨링 검수자가 먼저 본다. |
hard_negative_candidates.csv |
스프레드시트에서 정렬·필터링하며 검수하기 위한 요약 표다. |
각 run은 runs/<run_name>/ 아래에 다음 파일을 남긴다.
| 출력 | 확인할 내용 |
|---|---|
metrics.json |
val/test Top-1, Top-3, macro F1, hard-case accuracy, text retrieval 결과. |
predictions_val.jsonl, predictions_test.jsonl |
각 이미지의 Top-3, margin, confusing_with 결과. |
predictions_text_queries.jsonl |
텍스트 카탈로그 query가 어떤 landmark prototype으로 검색되는지. |
low_margin_val.csv, low_margin_test.csv |
Top-1과 Top-2가 가까워 애매한 입력 후보. |
로컬에서는 python -m compileall src와 loss smoke test를 통과했다.
전체 학습 실행은 서버의 torch/open_clip/mobileclip/scikit-learn 환경과 실제 Dataset이 필요하므로,
서버에서 위 tmux 명령으로 확인한다.