Hun-Bot

Vision Language Models (VLM): 이미지와 텍스트의 멀티모달 학습

Computer Vision NLP Multimodal CLIP Deep Learning

Vision Language Models (VLM): 이미지와 텍스트의 멀티모달 학습

개요

Vision Language Models(VLM)는 이미지와 텍스트를 동시에 이해하는 인공지능 모델입니다. 기존의 이미지 분류 모델이나 텍스트 분석 모델과 달리, VLM은 두 가지 모달리티(Modality) 사이의 의미적 관계를 학습하여 더욱 유연하고 강력한 표현 능력을 제공합니다.

이 글에서는 VLM의 대표 모델인 **CLIP(Contrastive Language-Image Pre-training)**을 중심으로, 멀티모달 학습의 원리부터 실무 응용까지 상세히 설명합니다.


1. 문제 정의: 전통적 AI의 한계

1.1 단일 모달리티의 문제점

기존의 인공지능 모델들은 한 가지 종류의 데이터만 처리했습니다.

이미지 분류 (Image Classification)

입력: 고양이 사진
출력: "고양이" (라벨만)
한계: 이미지가 무엇인지만 알고, 그 이미지를 텍스트로 설명하지 못함

텍스트 분류 (Text Classification)

입력: "이 영화는 정말 재미있었다"
출력: "긍정" (감정 분류)
한계: 텍스트는 이해하지만, 관련 이미지와 연결하지 못함

Cross-Modal 문제

질문: 이 사진과 이 설명이 같은 물건인가?
기존 방식: 이미지 모델과 텍스트 모델 각각 돌려야 하고,
         두 결과를 어떻게 비교할지 명확하지 않음

1.2 멀티모달 학습의 필요성

현실의 인지는 항상 멀티모달입니다.

사람이 사진을 본다 = 시각 + 언어 기억 + 맥락
ChatGPT가 이미지를 본다 = 이미지를 텍스트로 이해

멀티모달 학습은 이런 현실을 모델링합니다.

실제 사용 사례:

  • 이미지 검색 (Pinterest, Google Lens)
  • 이미지 캡셔닝 (사진에 자동으로 설명 추가)
  • 시각 질의응답 (VQA: “이 사진에서 뭐가 보이나?”)
  • 추천 시스템 (유사한 스타일의 상품 추천)

2. CLIP: Contrastive Language-Image Pre-training

2.1 핵심 아이디어

CLIP은 2021년 OpenAI가 발표한 모델로, 멀티모달 학습의 패러다임을 바꿨습니다.

CLIP의 핵심 철학:

"이미지와 텍스트를 같은 벡터 공간(Embedding Space)에 표현하고,
같은 쌍의 이미지-텍스트는 가깝게,
다른 쌍은 멀게 배치한다"

2.2 아키텍처

CLIP은 두 개의 인코더로 구성됩니다.

┌─────────────────────────────────────────────────────────────┐
│                    CLIP Architecture                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Image Encoder          Text Encoder                        │
│  (Vision Transformer)   (Transformer)                       │
│         │                      │                            │
│    [ Image ]                [Text]                          │
│    (224×224)              "A photo                          │
│         │                 of a cat"                         │
│         │                    │                              │
│         ▼                    ▼                              │
│  ┌─────────────┐       ┌─────────────┐                    │
│  │ ViT Encoder │       │ Text Encoder│                    │
│  │ (12 layers) │       │ (12 layers) │                    │
│  └─────────────┘       └─────────────┘                    │
│         │                    │                              │
│         ▼                    ▼                              │
│   Image Vector          Text Vector                        │
│   (Shape: 512)          (Shape: 512)                       │
│         │                    │                              │
│         └────────┬───────────┘                             │
│                  │                                          │
│         Cosine Similarity Computation                      │
│         (비교)                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.3 학습 과정 (Contrastive Learning)

CLIP의 학습은 **대조적 학습(Contrastive Learning)**을 사용합니다.

배치 구성:

배치에 N개의 이미지-텍스트 쌍이 있습니다.

배치: [
  (Image₁, Text₁),  ✓ 같은 쌍 (일치)
  (Image₂, Text₂),  ✓ 같은 쌍 (일치)
  ...
  (Imageₙ, Textₙ)   ✓ 같은 쌍 (일치)
]

배치 크기: N (보통 256 또는 512)

손실함수 (Contrastive Loss):

각 이미지에 대해, 모든 N개의 텍스트와 유사도를 계산합니다.

예시 (이미지 1 기준):
Image₁ vs Text₁: 0.95  ✓ 높은 유사도 (원하는 것)
Image₁ vs Text₂: 0.15  ✗ 낮은 유사도 (원하는 것)
Image₁ vs Text₃: 0.08  ✗ 낮은 유사도 (원하는 것)
...
Image₁ vs Textₙ: 0.10  ✗ 낮은 유사도 (원하는 것)

손실함수는 이 확률들을 교차 엔트로피로 계산합니다.

수학적 표현:

similarity(image_i, text_j) = cosine_similarity(I_i, T_j)

Loss = -log(exp(sim(i,i) / τ) / Σⱼ exp(sim(i,j) / τ))

여기서:
- i: 이미지 인덱스
- j: 텍스트 인덱스
- τ (tau): Temperature parameter (보통 0.07)
  → 유사도의 차이를 더 극단적으로 만들어줌

핵심:

  • 맞는 쌍(i=j)의 유사도는 높게
  • 틀린 쌍(i≠j)의 유사도는 낮게
  • 이를 동시에 최적화하는 것이 CLIP의 학습

2.4 왜 이 방식이 효과적인가?

기존의 supervised learning과의 비교:

기존 방식:
- 라벨 필요: "이 사진은 고양이" (정해진 카테고리만)
- 새로운 종류 추가: 모델 재학습 필요
- 데이터: ImageNet (1000개 카테고리만)

CLIP:
- 라벨 불필요: 이미지-텍스트 쌍만 있으면 됨
- 새로운 종류 추가: 텍스트 설명만 추가 (즉시 작동)
- 데이터: 인터넷 전체 (400M 이미지-텍스트 쌍)
  → 훨씬 더 다양한 개념 학습 가능

3. CLIP의 강점: Zero-Shot Learning

3.1 개념

CLIP의 가장 혁신적인 특징은 **제로샷 학습(Zero-Shot Learning)**입니다.

"제로샷"이란?
= 모델이 본 적 없는 데이터도 분류할 수 있음
= 학습 없이(Zero) 새로운 작업(Shot)을 바로 수행

3.2 동작 원리

예시: 이미지 분류

# 모델 준비
model = CLIP("ViT-B/32")  # 400M 데이터로 사전학습됨

# 이미지 로드
image = load_image("cat.jpg")

# 분류할 카테고리 (모델은 이것들을 본 적 없음!)
categories = [
    "a photo of a cat",
    "a photo of a dog",
    "a photo of a bird"
]

# 유사도 계산
similarities = model.compute_similarity(image, categories)
# [0.95, 0.02, 0.01]

# 결과
predicted_class = categories[argmax(similarities)]
# "a photo of a cat" → 확률 95%

이게 가능한 이유:

CLIP은 "고양이", "개", "새" 따로 배우지 않았습니다.
대신 400M 이미지-텍스트 쌍으로부터:
- "동물"의 시각적 특징들
- "털", "귀", "발" 같은 개념들
- "고양이 같은" vs "개 같은" 시각적 차이들
을 모두 학습했습니다.

따라서 새로운 카테고리도 이 지식을 조합해 이해할 수 있습니다.

3.3 Zero-Shot vs Traditional 비교

┌─────────────────────────────────────────────────────────────┐
│                 Zero-Shot Learning Flow                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Traditional (Fine-tuning 필요):                            │
│  고양이 100장 + 개 100장 → 모델 재학습 → 분류               │
│  (시간: 1시간, 데이터: 200장 필요)                         │
│                                                             │
│  CLIP Zero-Shot (즉시):                                     │
│  "a photo of a cat" 텍스트만 준비 → 즉시 분류              │
│  (시간: 1초, 데이터: 0장 필요)                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4. 실무 응용: 3가지 주요 사례

문제: 사용자가 찍은 사진과 비슷한 상품을 추천해야 한다.

전통적 방식 (어렵고 비쌈):

User Photo → 특징 추출 → Database의 모든 사진과 비교
문제: 어떤 특징을 추출할지 모호함
     (색상? 모양? 질감? 모두?)

CLIP 기반 방식:

Database의 모든 상품 사진 → CLIP Image Encoder → 벡터화
                            (한 번만 처리)

User Photo → CLIP Image Encoder → 벡터화

         벡터들 간 거리 계산 (매우 빠름)

         상위 K개의 비슷한 상품 추천

장점:

  • 매우 빠름 (벡터 간 유사도는 O(n))
  • 의미적 유사성 학습됨 (스타일, 패턴 등)
  • 텍스트 검색도 가능 (“청색 운동화” → 연관된 사진)

산업 적용:

  • 핀터레스트: 핀의 이미지로 비슷한 핀 찾기
  • 아마존: “이 신발과 비슷한 상품”
  • 구글 렌즈: 사진을 촬영해서 구매 링크 찾기

4.2 이미지 캡셔닝 (Image Captioning)

문제: 사진에 자동으로 설명 텍스트를 추가해야 한다.

전통적 방식 (CNN + RNN):

Image → CNN Feature Extraction → RNN Text Generation
"고양이가 의자에 앉아있다"

문제: 고정된 크기의 텍스트만 생성 가능
     자연스러운 문장을 만들기 어려움

CLIP + LLM 기반 (최신):

Image → CLIP Image Encoder → 이미지 벡터

                    Large Language Model (GPT, LLaMA)

"부드러운 회색 털을 가진 고양이가
 나무로 만든 갈색 의자에 편안하게 앉아있으며,
 창문 너머로 햇빛이 들어오고 있다."

장점:

  • CLIP이 이미지 이해 → LLM이 자연스러운 문장 생성
  • 훨씬 더 디테일하고 정확한 설명
  • 이미지의 맥락을 잘 이해함

코드 예시:

from transformers import CLIPVisionModel, GPT2LMHeadModel

# 이미지를 벡터로
image_features = clip_model.vision_model(image)

# LLM이 이미지 벡터를 입력받아 텍스트 생성
caption = gpt2_model.generate(
    inputs=image_features,
    max_length=50,
    temperature=0.7
)

4.3 Visual Question Answering (VQA)

문제: 이미지와 질문이 주어졌을 때, 질문에 답변해야 한다.

예시:

이미지: 식당 사진
질문: "이 식당의 분위기는 어떤가요?"
기대 답변: "따뜻하고 현대적인 분위기의 고급 식당입니다"

이미지: 도로 신호등 사진
질문: "신호등이 무슨 색인가요?"
기대 답변: "빨간색입니다"

CLIP 기반 VQA 파이프라인:

Image → CLIP Image Encoder → 이미지 벡터

Question → CLIP Text Encoder → 질문 벡터

        두 벡터를 LLM에 입력

        답변 생성 (Fine-tuned LLM 사용)

실무 사례:

  • 의료: “이 CT 스캔에서 종양이 보이는가?” → “네, T4 위치에서”
  • 자율주행: “앞의 신호등은 무슨 색인가?” → “빨간색”
  • 로봇: “파란 도구는 어디에 있는가?” → “선반의 왼쪽 상단”
  • 접근성: 시각장애인을 위한 이미지 설명

5. Fine-Tuning: CLIP 커스터마이징

5.1 왜 Fine-Tuning이 필요한가?

CLIP은 일반적인 인터넷 데이터로 학습되었으므로, 특정 도메인에서는 성능이 낮을 수 있습니다.

문제 사례:

CLIP (General):
"병충해 사진" → "곤충" (너무 일반적)

Domain-Specific CLIP (Fine-tuned):
"병충해 사진" → "총채벌레" (정확함)

5.2 Fine-Tuning 전략

데이터 양에 따라 다른 전략을 사용합니다.

데이터 많음 (100K+):

Strategy: 모든 레이어 재학습
Learning Rate: 1e-4 (낮음)
Epochs: 10~20
GPU: V100 이상, 8시간 이상 필요

코드:
for param in model.parameters():
    param.requires_grad = True  # 모든 파라미터 학습

optimizer = Adam(model.parameters(), lr=1e-4)

데이터 중간 (1K~100K):

Strategy: Text Encoder + Image Encoder만 재학습
Learning Rate: 1e-3
Epochs: 5~10
GPU: T4 (Google Colab), 30분~2시간

코드:
# Vision Transformer의 마지막 블록만
for param in model.vision_model.transformer.resblocks[-4:].parameters():
    param.requires_grad = True

optimizer = Adam(trainable_params, lr=1e-3)

데이터 적음 (<1K):

Strategy: Linear Head만 (또는 가볍게 Fine-tune)
Learning Rate: 1e-2 (높음)
Epochs: 3~5
GPU: CPU 가능, 수분

코드:
# 인코더는 고정, 새로운 분류 헤드만 학습
for param in model.parameters():
    param.requires_grad = False

# 새로운 헤드만 추가
new_head = Linear(512, num_classes)
optimizer = Adam(new_head.parameters(), lr=1e-2)

5.3 Fine-Tuning 코드 예시

import torch
import clip
from torch.utils.data import DataLoader
from torch.optim import Adam

# 1. 모델 로드
device = "cuda"
model, preprocess = clip.load("ViT-B/32", device=device)

# 2. 데이터 로더 준비
# (이미지-텍스트 쌍으로 구성된 커스텀 데이터셋)
train_loader = DataLoader(
    CustomDataset(images, texts),
    batch_size=32,
    shuffle=True
)

# 3. 학습 가능한 파라미터 설정
for param in model.parameters():
    param.requires_grad = False

for param in model.transformer.resblocks[-4:].parameters():
    param.requires_grad = True

# 4. 옵티마이저
optimizer = Adam(
    [p for p in model.parameters() if p.requires_grad],
    lr=1e-3
)

# 5. 학습 루프
for epoch in range(5):
    for batch_idx, (images, texts) in enumerate(train_loader):
        images = images.to(device)
        
        # 인코딩
        image_features = model.encode_image(images)
        text_features = model.encode_text(texts)
        
        # 정규화 (CLIP의 특징)
        image_features /= image_features.norm(dim=-1, keepdim=True)
        text_features /= text_features.norm(dim=-1, keepdim=True)
        
        # 유사도 행렬 계산
        similarity = image_features @ text_features.t()
        
        # 손실 함수 (Contrastive Loss)
        loss = contrastive_loss(similarity)
        
        # 역전파
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        if batch_idx % 100 == 0:
            print(f"Epoch {epoch}, Batch {batch_idx}, Loss: {loss:.4f}")

# 6. 모델 저장
torch.save(model.state_dict(), "clip_finetuned.pt")

6. 최신 VLM 트렌드 (2024~2025)

6.1 CLIP 이후의 진화

CLIP (2021) 이후, VLM은 급속도로 발전했습니다.

Timeline:
2021 CLIP
  ↓ (OpenAI의 기초 확립)
2023 LLaVA (CLIP + LLaMA)
  ↓ (대형 언어모델과의 결합)
2024 GPT-4V, Gemini Pro Vision
  ↓ (멀티모달 이해 고도화)
2025 Multimodal Agents
  ↓ (VLM이 행동까지 가능)

6.2 주요 모델들

LLaVA (Large Language and Vision Assistant)

구조: CLIP Image Encoder + LLaMA Language Model
특징: 이미지 이해 + 자연스러운 대화 가능
용도: Visual Chatbot ("이 사진에 대해 물어봐도 돼?")
성능: CLIP보다 높은 이해도, 하지만 느림

PaliGemma (Google’s Efficient VLM)

구조: 경량 비전 모델 + 언어 모델
특징: 빠르고 가벼움 (모바일 지원)
용도: 엣지 디바이스에서 실행
성능: 대형 모델보다 낮지만 빠름

GPT-4V / Claude Vision

구조: Proprietary (상세 공개 안 됨)
특징: 매우 높은 이해도
용도: 고도의 이미지 분석 필요한 작업
성능: 현재 가장 우수
비용: 높음 (API 기반)

6.3 발전 방향

1. 더 큰 모델

CLIP: 400M 데이터
→ 현재 모델들: 1B~10B 데이터 학습
→ 앞으로: 100B 데이터 규모

2. 멀티태스킹

CLIP: 이미지 분류만
→ 현재: 분류 + 검색 + 캡셔닝 동시
→ 앞으로: + 객체 감지 + 세그멘테이션

3. 비디오 멀티모달

CLIP: 정적 이미지
→ 현재: 비디오도 이해 시작
→ 앞으로: 비디오 + 오디오 + 텍스트

4. 행동 가능한 에이전트

현재: "이 사진을 설명해줘" (말만 함)
앞으로: "그 물건을 집어줘" (로봇이 실제로 행동)

7. 제한점과 윤리적 고려사항

7.1 기술적 제한점

1. 편향 (Bias)

문제: CLIP은 인터넷 데이터로 학습됨
     → 특정 문화/인종/성별에 편향됨

예시:
- "의사" 검색 → 백인 남성 사진 많음
- "간호사" 검색 → 여성 사진 많음

해결책:
- 데이터셋 균형 맞추기
- Fine-tuning 시 다양한 데이터 사용
- 결과 검증 및 모니터링

2. 미세한 이해의 어려움

쉬움:
- "고양이" vs "개" (명확한 차이)

어려움:
- "3명 vs 4명" (정확한 수 세기)
- "물체 A가 B 위에 있는가?" (공간 관계)
- "감정 표현" (얼굴의 미세한 표정)

3. 높은 계산 비용

CLIP 추론 비용:
- 이미지 + 텍스트 동시 처리
- 단일 모델보다 2배 느릴 수 있음
- 대규모 시스템에서는 비용 문제

7.2 윤리적 고려사항

개인정보 보호:

이미지 검색 시스템:
- 사용자의 사진 데이터베이스화
- 개인정보 보호 필수

해결:
- On-device processing (스마트폰에서 처리)
- 데이터 암호화
- 명확한 동의 절차

저작권:

CLIP의 학습 데이터:
- 인터넷의 수십억 이미지 사용
- 저작권 논쟁 발생 가능

해결:
- 라이선스 확인 필수
- 오픈소스 데이터셋 우선 사용

8. 실제 구현: Python 코드

8.1 기본 사용

import torch
import clip
from PIL import Image

# 1. 장비 설정
device = "cuda" if torch.cuda.is_available() else "cpu"

# 2. 모델 로드 (자동 다운로드)
model, preprocess = clip.load("ViT-B/32", device=device)

# 3. 이미지 준비
image = preprocess(Image.open("example.jpg")).unsqueeze(0).to(device)

# 4. 카테고리 정의
labels = [
    "a cat",
    "a dog",
    "a bird",
    "a car",
    "a person"
]

# 5. 텍스트 토큰화
text_inputs = clip.tokenize(labels).to(device)

# 6. 추론
with torch.no_grad():
    image_features = model.encode_image(image)
    text_features = model.encode_text(text_inputs)
    
    # 정규화
    image_features /= image_features.norm(dim=-1, keepdim=True)
    text_features /= text_features.norm(dim=-1, keepdim=True)
    
    # 유사도 계산
    similarity = (100.0 * image_features @ text_features.T).softmax(dim=-1)

# 7. 결과
print(f"Image classification results:")
for label, prob in zip(labels, similarity[0].cpu().numpy()):
    print(f"  {label}: {prob*100:.2f}%")

# 출력 예시:
# Image classification results:
#   a cat: 94.32%
#   a dog: 3.21%
#   a bird: 1.45%
#   a car: 0.68%
#   a person: 0.34%

8.2 배치 처리 (여러 이미지)

from torch.utils.data import DataLoader

# 이미지 배치
image_batch = torch.stack([
    preprocess(Image.open(f"image_{i}.jpg"))
    for i in range(10)
]).to(device)

# 추론
with torch.no_grad():
    image_features = model.encode_image(image_batch)  # Shape: (10, 512)
    text_features = model.encode_text(text_inputs)    # Shape: (5, 512)
    
    # 배치 유사도
    similarity = (100.0 * image_features @ text_features.T).softmax(dim=-1)
    # Shape: (10, 5)

# 각 이미지의 상위 카테고리
for i, scores in enumerate(similarity):
    top_category = labels[scores.argmax().item()]
    top_score = scores.max().item()
    print(f"Image {i}: {top_category} ({top_score*100:.2f}%)")

8.3 이미지 검색

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 데이터베이스의 모든 이미지를 벡터화 (사전 처리)
database_images = [...]  # 이미지 경로 리스트
database_features = []

for img_path in database_images:
    img = preprocess(Image.open(img_path)).unsqueeze(0).to(device)
    with torch.no_grad():
        feat = model.encode_image(img)
        feat /= feat.norm(dim=-1, keepdim=True)
    database_features.append(feat.cpu().numpy())

database_features = np.vstack(database_features)  # Shape: (N, 512)

# 쿼리 이미지
query_image = preprocess(Image.open("query.jpg")).unsqueeze(0).to(device)
with torch.no_grad():
    query_feature = model.encode_image(query_image)
    query_feature /= query_feature.norm(dim=-1, keepdim=True)

query_feature = query_feature.cpu().numpy()

# 유사도 계산
similarities = cosine_similarity(query_feature, database_features)[0]
top_k_indices = np.argsort(similarities)[-5:][::-1]

# 상위 5개 유사 이미지
print("Top 5 similar images:")
for rank, idx in enumerate(top_k_indices, 1):
    print(f"{rank}. {database_images[idx]} (similarity: {similarities[idx]:.4f})")

9. 다음 스텝: 당신이 할 수 있는 것

9.1 기초 실습

Step 1: CLIP 설치 (15분)
$ pip install clip-by-openai

Step 2: 위의 코드 실행 (10분)
- 자신의 사진으로 분류 테스트
- 여러 카테고리 시도

Step 3: 결과 분석 (15분)
- 잘 분류된 이미지
- 실패한 이미지 분석

9.2 중급: Fine-Tuning

Step 1: 도메인 데이터 수집 (데이터 크기에 따라)
- 100장 이상 목표

Step 2: CLIP Fine-tuning 코드 작성
- 위의 5.3 코드 참고

Step 3: 성능 평가
- Fine-tuning 전후 비교
- F1-score, Accuracy 계산

9.3 고급: 애플리케이션 구축

프로젝트 아이디어:

1. 농구 장면 자동 분류
   "슈팅" vs "드리블" vs "패스" 자동 인식
   
2. 의료 이미지 검색
   유사한 CT/X-ray 사례 찾기
   
3. 이커머스 이미지 검색
   사진으로 상품 찾기
   
4. 음악 앨범 아트 검색
   특정 스타일의 앨범 찾기

결론

Vision Language Models, 특히 CLIP은 인공지능이 두 가지 모달리티를 동시에 이해하는 능력을 보여줍니다.

핵심 요점:

  1. Contrastive Learning: 이미지와 텍스트를 같은 공간에 표현
  2. Zero-Shot Learning: 새로운 작업도 추가 학습 없이 수행 가능
  3. 멀티모달 이해: 이미지만, 텍스트만이 아닌 둘의 조합 이해
  4. 실무 응용: 검색, 캡셔닝, VQA, 추천 등 다양한 분야

앞으로 AI는 더욱 멀티모달 중심으로 발전할 것이며, VLM의 이해는 현대 AI 엔지니어의 필수 지식이 됩니다.


참고자료

질문 및 피드백

이 글에 대한 질문이나 피드백은 댓글로 남겨주세요. 다음 글에서는 CLIP Fine-tuning의 실제 프로젝트 사례를 다루겠습니다.

목차

댓글