| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
Tags
- 딥러닝
- 기초
- 머신러닝
- RDBMS
- Python
- 정보처리기사
- TTS
- 에이전트
- 트랜스포머
- ASR
- SQL
- LangGraph
- CLIP
- Transformer
- 생성형 인공지능
- 알고리즘
- 데이터 시각화
- 소프트웨어 개발
- 캐글
- UMAP
- python 기초
- python기초
- 객체지향
- 랭그래프
- dementional reduction
- 자연어처리
- CNN
- 힙정렬
- 데이터엔지니어
- RNN
Archives
- Today
- Total
수달이네 기술 블로그
2. CLIP모델 구현 본문
구현
import torch
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import seaborn as sns
from PIL import Image
from datasets import load_dataset
from transformers import CLIPProcessor, CLIPModel
from sklearn.metrics.pairwise import cosine_similarity
openai/clip-vit-base-patch32 · Hugging Face
모델은 HuggingFace에 올려져 있는 CLIP-ViTbase모델을 사용한다.
MODEL_NAME = "openai/clip-vit-base-patch32"
DATASET_NAME = "clip-benchmark/wds_imagenetv2"
SPLIT = "test"
SAMPLE_SIZE = 10
THUMB_SIZE = (80, 80)
OUT_PATH = "heatmap.png"
- CLIP모델과 그 성능을 평가하기 위한 ImageNetV2데이터셋을 이용할 것이다.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
- 디바이스 설정(일단cpu로 진행할것임.)
model = CLIPModel.from_pretrained(MODEL_NAME).to(device)
processor = CLIPProcessor.from_pretrained(MODEL_NAME)
model.eval()
- 모델 설정: 사전에 정한 MODEL-NAME을 통해 huggingface에서 모델을 가져온다.
- processor: 이미지와 텍스트를 모델 입력 형식에 맞게 변환해주는 전처리기.
CLIPModel(
(text_model): CLIPTextTransformer(
(embeddings): CLIPTextEmbeddings(
(token_embedding): Embedding(49408, 512)
(position_embedding): Embedding(77, 512)
)
(encoder): CLIPEncoder(
(layers): ModuleList(
(0-11): 12 x CLIPEncoderLayer(
(self_attn): CLIPAttention(
(k_proj): Linear(in_features=512, out_features=512, bias=True)
(v_proj): Linear(in_features=512, out_features=512, bias=True)
(q_proj): Linear(in_features=512, out_features=512, bias=True)
(out_proj): Linear(in_features=512, out_features=512, bias=True)
)
(layer_norm1): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
(mlp): CLIPMLP(
(activation_fn): QuickGELUActivation()
(fc1): Linear(in_features=512, out_features=2048, bias=True)
(fc2): Linear(in_features=2048, out_features=512, bias=True)
)
(layer_norm2): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
)
)
)
(final_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
)
(vision_model): CLIPVisionTransformer(
(embeddings): CLIPVisionEmbeddings(
(patch_embedding): Conv2d(3, 768, kernel_size=(32, 32), stride=(32, 32), bias=False)
(position_embedding): Embedding(50, 768)
)
(pre_layrnorm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(encoder): CLIPEncoder(
(layers): ModuleList(
(0-11): 12 x CLIPEncoderLayer(
(self_attn): CLIPAttention(
(k_proj): Linear(in_features=768, out_features=768, bias=True)
(v_proj): Linear(in_features=768, out_features=768, bias=True)
(q_proj): Linear(in_features=768, out_features=768, bias=True)
(out_proj): Linear(in_features=768, out_features=768, bias=True)
)
(layer_norm1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): CLIPMLP(
(activation_fn): QuickGELUActivation()
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
)
(layer_norm2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
)
)
(post_layernorm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
(visual_projection): Linear(in_features=768, out_features=512, bias=False)
(text_projection): Linear(in_features=512, out_features=512, bias=False)
)
- 위는 모델의 구조를 출력한 내용이다.
Text Encoder(CLIPTextTransformer)
(text_model): CLIPTextTransformer(
(embeddings): CLIPTextEmbeddings(
(token_embedding): Embedding(49408, 512)
(position_embedding): Embedding(77, 512)
)
(encoder): CLIPEncoder(
(layers): ModuleList(
(0-11): 12 x CLIPEncoderLayer(
(self_attn): CLIPAttention(
(k_proj): Linear(in_features=512, out_features=512, bias=True)
(v_proj): Linear(in_features=512, out_features=512, bias=True)
(q_proj): Linear(in_features=512, out_features=512, bias=True)
(out_proj): Linear(in_features=512, out_features=512, bias=True)
)
(layer_norm1): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
(mlp): CLIPMLP(
(activation_fn): QuickGELUActivation()
(fc1): Linear(in_features=512, out_features=2048, bias=True)
(fc2): Linear(in_features=2048, out_features=512, bias=True)
)
(layer_norm2): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
)
)
)
(final_layer_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)
)
- Embedding(임베딩)
- token_embedding: 텍스트 토큰(어휘 49408개)을 512차원의 벡터로 변환
- position_embedding: 77개의 토큰으로 위치정보 부여(Positional encoding)
- Encoder(Transformer기반) → 12개의 EncoderLayer 존재(아래의 층이 12번 반복)
- self-attention → (토큰 간 관계(문맥, 상호작용)을 학습)
- q, k, v_proj: 입력을 각각 query, key, value로 변환하는 linear층
- out_proj: 결과를 원래 차원으로 다시 매핑
- Layer_norm1,2: 레이어 정규화 → (안정적인 학습)
- 한개의 입력샘플 내부의 모든 입력값의 평균, 분산을 계산해 정규화
- MLP(Feed-Forward Network) → (복잡한 패턴 학습)
- fc1: 512차원 → 2058차원
- fc2: 2048차원 → 512차원
- GELU로 비선형 활성화
- final_layer_norm: 마지막 출력에 정규화 → 안정성
- self-attention → (토큰 간 관계(문맥, 상호작용)을 학습)
Vision Encoder(CLIPVisionTransformer)
(vision_model): CLIPVisionTransformer(
(embeddings): CLIPVisionEmbeddings(
(patch_embedding): Conv2d(3, 768, kernel_size=(32, 32), stride=(32, 32), bias=False)
(position_embedding): Embedding(50, 768)
)
(pre_layrnorm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(encoder): CLIPEncoder(
(layers): ModuleList(
(0-11): 12 x CLIPEncoderLayer(
(self_attn): CLIPAttention(
(k_proj): Linear(in_features=768, out_features=768, bias=True)
(v_proj): Linear(in_features=768, out_features=768, bias=True)
(q_proj): Linear(in_features=768, out_features=768, bias=True)
(out_proj): Linear(in_features=768, out_features=768, bias=True)
)
(layer_norm1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): CLIPMLP(
(activation_fn): QuickGELUActivation()
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
)
(layer_norm2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
)
)
(post_layernorm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
- Embedding
- ImageNetV2를 224x224로 resize후 이용할 것임.
- patch_embedding: Conv를 이용해 이미지를 분할(kernel 크기 = stride크기 = 32px)
- 왜? → 분할과 동시에 각각 임베딩까지 한번에 처리
- 동시에 (32x32x3픽셀)차원에서 768차원 벡터로 압축(충분한 차원 학습) → 차후 projection 단계에서 768→512차원으로 바꾸어 텍스트와 연결함.
- patch_embedding 이후 224 ÷ 32 = 7 즉, 7×7 = 49개의 패치 + CLS토큰 50개
- position_embedding: 위에서 출력된 패치 별로 위치 인코딩 진행.(50토큰)
- pre_layernorm: 연산 전 정규화를 해줌 (텍스트보다 이미지가 차원이 크고 변동성이 있으므로)
- Encoder(ViT) → 12개의 EncoderLayer 존재(아래의 층이 12번 반복)
- 텍스트의 transformer와 층 구조 동일
(visual_projection): Linear(in_features=768, out_features=512, bias=False)
(text_projection): Linear(in_features=512, out_features=512, bias=False)
- 이후 projection으로 두 출력의 차원을 동일시 해준다.
# 데이터셋을 불러오고, test split에서 섞어서 10개만 고름
dataset = load_dataset(DATASET_NAME)
ds = dataset[SPLIT]
subset = ds.shuffle(seed=2026).select(range(SAMPLE_SIZE))
- 데이터셋(ImageNetV2)을 불러옴
cls2label = [line.strip() for line in open("./dataset/classnames.txt", "r", encoding="utf-8").readlines()]
cls2label
# ['tench',
# 'goldfish',
# 'great white shark',
# 'tiger shark',
# ...
# ]
- 준비된 라벨셋을 불러온 후
def to_pil(x):
if isinstance(x, Image.Image):
return x.convert("RGB")
# subset에서 webp 컬럼을 꺼내 PIL 이미지 리스트를 만듦
# images = [to_pil(x) for x in list(subset("webp"))]
if isinstance(x, dict) and "bytes" in x and x["bytes"] is not None:
return Image.open(io.BytesIO(x["bytes"])).convert("RGB")
if isinstance(x, str):
return Image.open(x).convert("RGB")
raise TypeError(f'Unsupported image type: {type(x)}')
label_names = [cls2label[int(c)] for c in list(subset['cls'])]
label_texts = [f'a photo of a {name}' for name in label_names]
images = [to_pil(x) for x in list(subset['webp'])]
- 이미지를 파이썬에서 사용하는 이미지 처리 모듈 Pillow에 호환되는 객체로 변환하는 함수
with torch.no_grad():
# 이미지 임베딩
# processor가 이미지를 CLIP이 받는 파이토치 텐서 형태로 변환 (batch, 3, H, W)
inputs_image = processor(images=images, return_tensors='pt', padding=True).to(device)
# vision_model이 비전 트랜스포머를 통과시켜 출력
vision_out = model.vision_model(pixel_values=inputs_image['pixel_values'])
# pooler_output = "대표 임베딩" 같은 역할을 하는 벡터(보통 CLS 토큰 기반)
# visual_projection으로 CLIP 공통 임베딩 공간에 차원에 맞춰 projection 함
image_features = model.visual_projection(vision_out.pooler_output)
# 텍스트 임베딩
# 텍스트도 토크나이징/패딩 후 모델 통과
inputs_text = processor(text=label_texts, return_tensors='pt', padding=True).to(device)
text_out = model.text_model(
input_ids = inputs_text['input_ids'],
attention_mask = inputs_text['attention_mask']
)
text_features = model.text_projection(text_out.pooler_output)
- 이미지와 텍스트를 임베딩
# 정규화 + 유사도 계산(코사인 유사도)
# 각 벡터를 L2 정규화하면 길이가 1이 됨
# 내적(@)이 코사인 유사도
# 결과: (10, D) @ (D, 10) = (10, 10)
image_features = image_features / image_features.norm(dim=-1, keepdim=True)
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
similarity_matrix = (image_features @ text_features.T).cpu().numpy()
- 코사인 유사도 계산(대비 학습)
fig, ax = plt.subplots(figsize=(20, 12))
im = ax.imshow(similarity_matrix, aspect='auto')
plt.colorbar(im, ax=ax, fraction=0.02, pad=0.02)
ax.set_xticks(np.arange(len(label_texts)))
ax.set_xticklabels(label_texts, rotation=45, ha='right')
ax.set_yticks(np.arange(len(images)))
ax.set_yticklabels([""] * len(images))
ax.set_title('CLIP Image vs. Label Similarity (Cosine Similarity)')
ax.set_xlabel('Label Text')
ax.set_ylabel('Image')
for i in range(similarity_matrix.shape[0]):
for j in range(similarity_matrix.shape[1]):
ax.text(j, i, f'{similarity_matrix[i, j]:.2f}', ha='center', va='center', fontsize=8)
#
for i, img in enumerate(thumbnails):
imagebox = OffsetImage(img, zoom=1.0)
# AnnotationBbox를 사용해서 좌표 (-0.6, i) 위치에 썸네일 이미지를 붙임
# (-0.6, i)는 히트맵의 첫 열(0)보다 왼쪽에 배치하기 위해서
ab = AnnotationBbox(imagebox, (-0.6, i), frameon=False, xycoords='data', boxcoords='data', pad=0)
ax.add_artist(ab)
ax.set_xlim(-1.2, similarity_matrix.shape[1] - 0.5)
ax.set_ylim(similarity_matrix.shape[0] - 0.5, -0.5)
plt.tight_layout()
plt.savefig(OUT_PATH, dpi=200)
plt.show()
print(f'saved: {OUT_PATH}')
- 학습한 이미지의 유사도를 출력
'AI공부 > 멀티모달' 카테고리의 다른 글
| 6. DINO(Zero-shot Object Detection, DETR, Hungarian matching, Swim Transformer) (0) | 2026.03.18 |
|---|---|
| 5. CLIP모델과 UMAP을 이용한 차원축소 시각화 (0) | 2026.03.17 |
| 4. 차원 축소 시각화(t-SNE, UMAP, initialization: random, PCA) (0) | 2026.03.16 |
| 3. 차원 축소(PCA, t-SNE, UMAP...) (0) | 2026.03.15 |
| 0. 멀티모달 개요 (0) | 2026.03.12 |