LLM 엔지니어링12장 · 멀티모달 RAG 와 데이터셋 자동 생성4 / 22← 이전목차다음 →MetaContents

12장. 멀티모달 RAG 와 데이터셋 자동 생성

멀티모달 임베딩

여러 나라 사람이 모인 행사장에서 그림 카드로 소통한다고 해 봅시다. "사과"라는 말과 사과 그림을 같은 자리에 붙여 두면, 말을 몰라도 그림으로 뜻이 이어집니다. CLIP 은 그림과 그 그림을 설명하는 글이 같은 자리에 오도록, 그림과 글의 짝 수억 개로 함께 학습한 모델입니다.

그림 인코더와 글 인코더가 각각 임베딩을 만들고, 같은 공간에서 가까운 짝을 찾는 CLIP 의 구조를 보인 세 카드

왼쪽 그림 인코더는 그림을, 가운데 글 인코더는 글을 같은 길이(512)의 숫자 줄로 바꿉니다. 오른쪽처럼 두 숫자 줄은 같은 공간에 놓이므로, 7장와 같은 코사인 유사도로 "빨간 머그컵"이라는 글과 가장 가까운 그림을 찾을 수 있습니다. 여기서 같은 자리에 붙인 그림 카드와 말이 같은 공간의 그림 임베딩과 글 임베딩입니다. 다만 그림 카드와 달리 CLIP 은 학습한 짝에 없는 낯선 말에는 약합니다.

이 장에서 쓰는 두 모델

모델하는 일글의 언어크기
clip-ViT-B-32그림과 영어 글을 임베딩으로영어600MB 쯤
sentence-transformers/clip-ViT-B-32-multilingual-v1한국어 등 50여 언어의 글을 위 모델과 같은 공간으로다국어540MB 쯤

다국어 모델은 영어 CLIP 의 글 인코더를 흉내 내도록 따로 학습한 글 인코더입니다(증류). 그림은 영어 모델로, 한국어 글은 다국어 모델로 임베딩하면 둘을 견줄 수 있습니다.

같은 공간에서 견주기

Python
import numpy as np

# 설명용 숫자 줄(실제 CLIP 임베딩이 아니다). 길이를 1 로 맞춘 그림 하나와 글 둘
image = np.array([0.8, 0.6, 0.0])
texts = {"빨간 머그컵": np.array([0.7, 0.7, 0.14]), "파란 우산": np.array([0.1, 0.2, 0.97])}
for name, v in texts.items():
    v = v / np.linalg.norm(v)
    print(name, round(float(image @ v), 3))       # 길이가 1 이면 내적이 코사인 유사도

코드에 나오는 함수

코드쉬운 뜻
np.array([0.8, 0.6, 0.0])NumPy(Numerical Python, 수치 계산 파이썬) 배열을 만든다. 여기서는 설명용 숫자 줄(임베딩) 하나다
np.linalg.norm(v)linalg 는 linear algebra(선형대수), norm 은 "크기"라는 뜻이다. 숫자 줄의 길이를 잰다
v / np.linalg.norm(v)숫자 줄을 자기 길이로 나눠 길이를 1 로 맞춘다. 정규화(normalization)라고 한다
image @ v내적(dot product)이다. 같은 자리의 숫자끼리 곱해 모두 더한다
float(...)NumPy 숫자를 보통 파이썬 실수로 바꾼다
round(x, 3)소수 셋째 자리까지 반올림한다

이 코드가 뜻하는 것

두 사람이 같은 곳을 가리키는지 보려면 팔 길이는 상관없고 팔이 향한 방향만 보면 됩니다. 팔 길이가 다르면 견주기 어려우니 먼저 길이를 똑같이 맞춥니다. 여기서 팔이 가리키는 방향이 임베딩, 팔 길이를 맞추는 일이 정규화, 두 방향이 얼마나 같은지가 코사인 유사도입니다. 다만 팔은 세 방향 공간에서 움직이지만 CLIP 의 숫자 줄은 512 방향이라 눈으로 그릴 수 없습니다.

줄하는 일쉽게 말하면
image = np.array([0.8, 0.6, 0.0])그림 하나의 설명용 숫자 줄. 길이가 이미 1 이다그림이 가리키는 방향
texts = {...}글 둘의 설명용 숫자 줄두 글이 가리키는 방향
v = v / np.linalg.norm(v)글의 숫자 줄도 길이를 1 로 맞춘다팔 길이를 똑같이
image @ v길이가 1 인 두 줄의 내적, 곧 코사인 유사도두 방향이 얼마나 같은가

실행하면 빨간 머그컵 0.98, 파란 우산 0.201 이 나옵니다. 1 에 가까울수록 같은 방향이므로, 이 그림은 "빨간 머그컵" 글과 거의 같은 곳을 가리킵니다.

작게 줄여서 눈으로 보기

방향이 같고 길이만 두 배인 두 숫자 줄로, 길이를 맞추기 전과 뒤의 내적을 견줍니다. 숫자는 설명을 위해 고른 것입니다.

Python
import numpy as np

a = np.array([3.0, 4.0])
b = np.array([6.0, 8.0])
print("a 의 길이:", np.linalg.norm(a), "/ b 의 길이:", np.linalg.norm(b))
print("그냥 내적:", a @ b)
a1, b1 = a / np.linalg.norm(a), b / np.linalg.norm(b)
print("길이를 1 로 맞춘 a:", a1)
print("맞춘 뒤 내적:", round(float(a1 @ b1), 3))
출력
a 의 길이: 5.0 / b 의 길이: 10.0
그냥 내적: 50.0
길이를 1 로 맞춘 a: [0.6 0.8]
맞춘 뒤 내적: 1.0
결과읽는 법
5.0 / 10.0b 는 a 의 숫자를 모두 두 배 한 것이라 길이도 두 배다
그냥 내적: 50.0길이가 섞여 큰 수가 나온다. 이 수만으로는 얼마나 비슷한지 알 수 없다
[0.6 0.8]3 과 4 를 각각 5 로 나눈 것이다. 방향은 그대로이고 길이만 1 이다
맞춘 뒤 내적: 1.0방향이 똑같아 코사인 유사도가 가장 큰 값인 1 이 된다

주의할 점

  • 다국어 글 인코더는 영어 CLIP 을 흉내 낸 것이라 영어로 물을 때보다 약할 수 있습니다. 실습 1에서 한국어와 영어로 같은 그림을 찾아 견줍니다.
  • CLIP 의 그림 인코더는 그림을 224×224 로 줄여 봅니다. 작은 글씨나 세부 모양은 잘 못 봅니다.
← 이전 절12장 · 멀티모달이란다음 절 →12장 · 그림으로 찾고 글로 찾기