포스트

2026-05-27 강의 정리

멀티모달 임베딩 모델 활용 (CLIP 등)

2026-05-27 강의 정리

목차

  1. 문제와 접근
  2. CLIP 모델 로드
  3. 이미지와 후보 텍스트 입력
  4. zero-shot 이미지 분류
  5. 이미지 임베딩 추출
  6. 텍스트 임베딩 추출
  7. 정규화와 cosine similarity
  8. 핵심 개념 총정리
  9. 자주 나오는 실수/주의사항
  10. 후보 문장도 모델 입력의 일부로 관리하기

1. 문제와 접근

CLIP의 zero-shot 분류는 주어진 후보 설명 중 이미지와 가장 잘 맞는 것을 고르는 흐름으로 살펴본다. 모델을 호출하는 방법과 함께 후보 문장 구성, 임베딩 정규화, 점수 해석을 구분한다. 출력 순위가 어떤 입력 조건에서 나온 결과인지 설명할 수 있는 것이 확인 목표다.

1
2
3
image encoder -> image embedding
text encoder  -> text embedding
cosine similarity
1
2
3
4
5
6
7
8
import torch
import numpy as np
from pathlib import Path
from PIL import Image
from transformers import CLIPProcessor, CLIPModel

DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
IMAGE_DIR = Path("data/images")

2. CLIP 모델 로드

1
2
3
4
5
6
7
8
MODEL_NAME = "openai/clip-vit-base-patch32"

model = CLIPModel.from_pretrained(
    MODEL_NAME,
    use_safetensors=True,
).to(DEVICE).eval()

processor = CLIPProcessor.from_pretrained(MODEL_NAME)

3. 이미지와 후보 텍스트 입력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
img = Image.open(IMAGE_DIR / "shapes_red_blue.png").convert("RGB")

candidates = [
    "a red square and a blue ellipse on white background",
    "a quarterly revenue bar chart",
    "a dashboard with KPI cards",
    "a photo of dog",
]

inputs = processor(
    text=candidates,
    images=img,
    return_tensors="pt",
    padding=True,
).to(DEVICE)

4. zero-shot 이미지 분류

여기서 softmax 값은 입력한 후보 목록 안에서 정규화한 점수다. 후보를 추가하거나 표현을 바꾸면 값도 달라질 수 있으므로, 특정 수치를 곧바로 분류 정답률로 해석하지 않고 같은 후보 집합에서 비교한다.

이미지와 후보 문장의 유사도를 계산해서 가장 잘 맞는 설명을 고른다.

1
2
3
4
5
6
7
with torch.no_grad():
    out = model(**inputs)

probs = out.logits_per_image.softmax(dim=1)[0].cpu().numpy()

for c, p in zip(candidates, probs):
    print(f"{p}, {c}")

CLIP은 별도 분류 head 없이도 후보 텍스트와 이미지의 유사도로 zero-shot 분류를 수행할 수 있다.


5. 이미지 임베딩 추출

vision encoder의 pooled output을 projection해서 512차원 벡터로 만든다.

1
2
3
4
5
6
7
8
9
def encode_image_clip(image) -> np.ndarray:
    inputs = processor(images=image.convert("RGB"), return_tensors="pt").to(DEVICE)
    with torch.no_grad():
        vision_out = model.vision_model(pixel_values=inputs["pixel_values"])
        pooled = vision_out.pooler_output
        projected = model.visual_projection(pooled)

    feat = projected[0].cpu().numpy()
    return feat / np.linalg.norm(feat)
1
2
3
e = encode_image_clip(Image.open(IMAGE_DIR / "shapes_red_blue.png"))
e.shape
# -> (512,)

6. 텍스트 임베딩 추출

text encoder의 pooled output을 projection해서 같은 512차원 공간으로 보낸다.

1
2
3
4
5
6
7
8
9
10
11
12
def encode_text_clip(text: str) -> np.ndarray:
    inputs = processor(text=[text], return_tensors="pt", padding=True).to(DEVICE)
    with torch.no_grad():
        text_out = model.text_model(
            input_ids=inputs["input_ids"],
            attention_mask=inputs.get("attention_mask"),
        )
        pooled = text_out.pooler_output
        projected = model.text_projection(pooled)

    feat = projected[0].cpu().numpy()
    return feat / np.linalg.norm(feat)

7. 정규화와 cosine similarity

정규화는 이미지와 텍스트 양쪽에 같은 기준으로 적용한다. 인덱스를 만들 때와 질의할 때 사용한 모델·전처리·벡터 처리 조건이 일치하는지 확인해야 점수 차이를 입력 내용의 차이로 해석할 수 있다.

벡터를 정규화하면 내적이 cosine similarity가 된다.

1
2
3
4
image_emb = encode_image_clip(img)
text_emb = encode_text_clip("a red square and a blue ellipse on white background")

score = image_emb @ text_emb
1
2
3
4
5
정규화 전:
  cosine = dot(a, b) / (||a|| * ||b||)

정규화 후:
  cosine = dot(a, b)

8. 핵심 개념 총정리

개념역할
CLIPProcessor이미지/텍스트를 모델 입력 텐서로 변환
CLIPModel이미지/텍스트 공동 임베딩 모델
logits_per_image이미지와 후보 텍스트 유사도
vision_model이미지 인코더
text_model텍스트 인코더
projection같은 512차원 공간으로 변환
L2 normalization내적을 cosine similarity로 사용

9. 자주 나오는 실수/주의사항

실수 1: 이미지 RGB 변환 누락

1
image.convert("RGB")

실수 2: eval/no_grad 누락

1
2
3
model.eval()
with torch.no_grad():
    out = model(**inputs)

실수 3: 정규화하지 않고 내적 비교

1
feat = feat / np.linalg.norm(feat)

실수 4: CLIP 토큰 길이 제한

1
CLIP 텍스트 인코더는 긴 문서 임베딩용이 아니라 짧은 캡션/쿼리에 적합하다.

10. 후보 문장도 모델 입력의 일부로 관리하기

분류 결과가 기대와 다르면 이미지뿐 아니라 후보 설명을 함께 확인한다. 색상·형태·대상을 표현한 방식이 균형적인지 보고, 정답에 가까운 설명만 유난히 구체적으로 작성한 비교는 피한다.

다음 실험에서는 유사한 후보를 추가한 경우와 어떤 후보에도 맞지 않는 이미지를 나누어 준비한다. 항상 가장 높은 후보를 반환하는 규칙을 사용할지, 별도의 보류 조건이 필요한지는 이런 입력에서의 결과를 보고 판단한다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

인기 태그