⏱️ 예상 읽기 시간: 12분
서론
시각적 문서 검색(Visual Document Retrieval) 분야에서 새로운 이정표가 세워졌습니다. NVIDIA에서 2025년 6월 27일에 공개한 llama-nemoretriever-colembed-3b-v1 모델이 ViDoRe V1, V2, 그리고 MTEB VisualDocumentRetrieval 벤치마크에서 모두 1위를 차지하며 업계의 주목을 받고 있습니다.
이 모델은 텍스트 쿼리와 이미지 문서를 동시에 처리할 수 있는 멀티모달 임베딩 모델로, 특히 RAG(Retrieval-Augmented Generation) 시스템에서 문서가 이미지 형태로 저장된 경우의 검색 성능을 획기적으로 개선했습니다.
본 포스트에서는 이 혁신적인 모델의 아키텍처부터 실제 사용법까지 상세히 분석해보겠습니다.
모델 개요 및 핵심 특징
기본 정보
- 모델명: nvidia/llama-nemoretriever-colembed-3b-v1
- 매개변수: 4.41B (44억 개)
- 아키텍처: Transformer 기반 멀티모달 임베딩
- 라이센스: NVIDIA Non-Commercial License (연구용 전용)
- 공개일: 2025년 6월 27일
핵심 특징
1. 멀티모달 입력 지원
- 쿼리: 텍스트 형태 (최대 8192 토큰)
- 문서: 텍스트 또는 이미지 형태 (PIL 이미지, 512x512 타일로 자동 분할)
2. ColBERT 스타일 임베딩
- Late interaction 방식의 멀티벡터 표현
- 토큰 레벨에서의 세밀한 매칭 가능
3. 뛰어난 벤치마크 성능
- ViDoRe V1: 0.9100 (nDCG@5)
- ViDoRe V2: 0.6352 (nDCG@5)
- MTEB Visual Document Retrieval: 0.8315 (Rank Borda)
모델 아키텍처 심화 분석
기반 모델 구성
모델은 두 개의 강력한 기반 모델을 결합하여 구성됩니다:
1. 비전 인코더: google/siglip2-giant-opt-patch16-384
- 이미지 처리를 담당하는 비전 트랜스포머
- 384x384 해상도의 패치 기반 처리
- Giant 규모의 모델로 뛰어난 시각적 이해 능력
2. 언어 모델: meta-llama/Llama-3.2-3B
- 텍스트 처리 및 멀티모달 융합 담당
- 30억 매개변수의 효율적인 언어 모델
- Qwen 모델을 활용한 추가 개선
입출력 사양
구분 | 쿼리 | 문서 |
---|---|---|
입력 타입 | 텍스트 | 텍스트 또는 이미지 |
입력 형식 | List[str] | List[str] 또는 List[PIL.Image] |
최대 길이 | 8192 토큰 | 8192 토큰 |
이미지 처리 | - | 512x512 타일로 자동 분할 |
출력 형식 | [batch_size × seq_length × embedding_dim] |
[batch_size × seq_length × embedding_dim] |
성능 벤치마크 상세 분석
ViDoRe (Visual Document Retrieval) 벤치마크
ViDoRe는 시각적 문서 검색을 위한 대표적인 벤치마크로, 다양한 도메인, 언어, 설정을 포괄합니다.
벤치마크 | 1B 모델 | 3B 모델 | 평가 지표 |
---|---|---|---|
ViDoRe V1 (2025.06.27) | 0.9050 | 0.9100 | nDCG@5 |
ViDoRe V2 (2025.06.27) | 0.6209 | 0.6352 | nDCG@5 |
MTEB Visual Document Retrieval | 0.8238 | 0.8315 | Rank Borda |
NVIDIA 검색 모델 라인업 비교
모델명 | 용도 | 특징 |
---|---|---|
llama-NemoRetriever-ColEmbed-3B-v1 | 연구용 | ViDoRe 1위, 최고 성능 |
llama-NemoRetriever-ColEmbed-1B-v1 | 연구용 | 경량화 버전 |
llama-3_2-nemoretriever-1b-vlm-embed-v1 | 상용 | 프로덕션 멀티모달 임베딩 |
NV-Embed-v2 | 연구용 | MTEB 텍스트 임베딩 1위 |
설치 및 환경 설정
필수 라이브러리 설치
# 특정 버전의 transformers 설치 (중요!)
pip install transformers==4.49.0
# Flash Attention 설치 (성능 최적화)
pip install flash-attn==2.6.3 --no-build-isolation
# 이미지 처리용 라이브러리
pip install pillow requests torch
시스템 요구사항
권장 하드웨어:
- NVIDIA A100 40GB/80GB 또는 H100 80GB
- CUDA 지원 GPU (최소 8GB VRAM)
- Linux 운영체제 (권장)
소프트웨어:
- Python 3.8+
- CUDA 11.8+
- PyTorch 2.0+
기본 사용법 및 코드 예제
모델 로딩
import torch
from transformers import AutoModel
from PIL import Image
import requests
from io import BytesIO
# 모델 로딩 (GPU 사용)
model = AutoModel.from_pretrained(
'nvidia/llama-nemoretriever-colembed-3b-v1',
device_map='cuda',
trust_remote_code=True,
torch_dtype=torch.bfloat16,
attn_implementation="flash_attention_2",
revision='50c36f4d5271c6851aa08bd26d69f6e7ca8b870c'
).eval()
print("모델 로딩 완료!")
실제 검색 예제
# 검색 쿼리 정의
queries = [
'2차 세계대전에서 독일 인구의 몇 퍼센트가 사망했나요?',
'2018년 가스 처리로부터 포집된 CO2는 몇 백만 톤인가요?',
'일본인의 평균 CO2 배출량은 얼마인가요?'
]
# 문서 이미지 URL
image_urls = [
'https://upload.wikimedia.org/wikipedia/commons/3/35/Human_losses_of_world_war_two_by_country.png',
'https://upload.wikimedia.org/wikipedia/commons/thumb/7/76/20210413_Carbon_capture_and_storage_-_CCS_-_proposed_vs_implemented.svg/2560px-20210413_Carbon_capture_and_storage_-_CCS_-_proposed_vs_implemented.svg.png',
'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/20210626_Variwide_chart_of_greenhouse_gas_emissions_per_capita_by_country.svg/2880px-20210626_Variwide_chart_of_greenhouse_gas_emissions_per_capita_by_country.svg.png'
]
# 이미지 다운로드 및 로딩
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
images = []
for url in image_urls:
response = requests.get(url, headers=headers)
image = Image.open(BytesIO(response.content))
images.append(image)
print(f"이미지 {len(images)}개 로딩 완료!")
임베딩 생성 및 검색
# 쿼리 및 문서 임베딩 생성
print("임베딩 생성 중...")
query_embeddings = model.forward_queries(queries, batch_size=8)
passage_embeddings = model.forward_passages(images, batch_size=8)
# 유사도 점수 계산
scores = model.get_scores(query_embeddings, passage_embeddings)
print("검색 결과 점수:")
print(scores)
# 예상 출력:
# tensor([[13.9970, 11.4219, 12.1225],
# [11.4157, 14.6388, 12.0341],
# [ 9.9023, 9.8857, 11.3387]], device='cuda:0')
# 결과 해석
for i, query in enumerate(queries):
best_match_idx = torch.argmax(scores[i]).item()
best_score = scores[i][best_match_idx].item()
print(f"쿼리 {i+1}: '{query}'")
print(f"최적 매칭 문서: {best_match_idx+1}, 점수: {best_score:.4f}")
print("-" * 50)
배치 처리 최적화
def batch_retrieval(queries, images, batch_size=4):
"""
대량의 쿼리와 문서를 효율적으로 처리하는 함수
"""
all_query_embeddings = []
all_passage_embeddings = []
# 쿼리 배치 처리
for i in range(0, len(queries), batch_size):
batch_queries = queries[i:i+batch_size]
batch_embeddings = model.forward_queries(batch_queries, batch_size=batch_size)
all_query_embeddings.append(batch_embeddings)
# 문서 배치 처리
for i in range(0, len(images), batch_size):
batch_images = images[i:i+batch_size]
batch_embeddings = model.forward_passages(batch_images, batch_size=batch_size)
all_passage_embeddings.append(batch_embeddings)
# 결합
query_embeddings = torch.cat(all_query_embeddings, dim=0)
passage_embeddings = torch.cat(all_passage_embeddings, dim=0)
return query_embeddings, passage_embeddings
# 사용 예제
if len(queries) > 10 or len(images) > 10:
query_emb, passage_emb = batch_retrieval(queries, images, batch_size=4)
scores = model.get_scores(query_emb, passage_emb)
고급 활용법
1. 멀티모달 RAG 시스템 구축
class MultimodalRAGRetriever:
def __init__(self, model):
self.model = model
self.document_embeddings = None
self.documents = []
def add_documents(self, documents):
"""텍스트와 이미지 문서를 임베딩 데이터베이스에 추가"""
self.documents.extend(documents)
embeddings = self.model.forward_passages(documents, batch_size=8)
if self.document_embeddings is None:
self.document_embeddings = embeddings
else:
self.document_embeddings = torch.cat([self.document_embeddings, embeddings], dim=0)
def search(self, query, top_k=5):
"""쿼리에 대해 상위 k개 문서 검색"""
query_embedding = self.model.forward_queries([query], batch_size=1)
scores = self.model.get_scores(query_embedding, self.document_embeddings)
top_indices = torch.topk(scores[0], k=top_k).indices
return [(self.documents[idx], scores[0][idx].item()) for idx in top_indices]
# 사용 예제
retriever = MultimodalRAGRetriever(model)
retriever.add_documents(images)
results = retriever.search("CO2 배출량 차트를 찾아주세요", top_k=3)
for doc, score in results:
print(f"점수: {score:.4f}")
2. 문서 클러스터링
from sklearn.cluster import KMeans
import numpy as np
def cluster_documents(images, n_clusters=3):
"""이미지 문서들을 의미적으로 클러스터링"""
# 문서 임베딩 생성
embeddings = model.forward_passages(images, batch_size=8)
# 평균 풀링으로 문서별 단일 벡터 생성
doc_vectors = embeddings.mean(dim=1).cpu().numpy()
# K-means 클러스터링
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
clusters = kmeans.fit_predict(doc_vectors)
return clusters
# 사용 예제
if len(images) >= 3:
clusters = cluster_documents(images, n_clusters=3)
print("문서 클러스터링 결과:", clusters)
벤치마크 평가 실행
ViDoRe 벤치마크 평가
# ViDoRe 벤치마크 설치
pip install git+https://github.com/illuin-tech/vidore-benchmark@e0eb9032e7e00adc8aa6f9cb35d5a9371f67485a
# transformers 버전 다운그레이드 (필수)
pip install transformers==4.49.0
# ViDoRe V1, V2 평가 실행
CUDA_VISIBLE_DEVICES=0 python3 vidore_eval.py \
--model_name_or_path nvidia/llama-nemoretriever-colembed-3b-v1 \
--savedir_datasets ./results/ \
--model_revision 50c36f4d5271c6851aa08bd26d69f6e7ca8b870c
MTEB 평가
# MTEB 설치
pip install git+https://github.com/embeddings-benchmark/mteb
# MTEB Visual Document Retrieval 평가
CUDA_VISIBLE_DEVICES=0 python3 mteb_eval.py \
--model_name_or_path nvidia/llama-nemoretriever-colembed-3b-v1
성능 최적화 팁
1. 메모리 최적화
# 모델 로딩 시 메모리 효율성 개선
model = AutoModel.from_pretrained(
'nvidia/llama-nemoretriever-colembed-3b-v1',
device_map='auto', # 자동 디바이스 매핑
torch_dtype=torch.bfloat16, # 메모리 절약
low_cpu_mem_usage=True, # CPU 메모리 절약
trust_remote_code=True
).eval()
# 그래디언트 비활성화 (추론 시)
with torch.no_grad():
embeddings = model.forward_queries(queries)
2. 배치 크기 조정
# GPU 메모리에 따른 최적 배치 크기 설정
def get_optimal_batch_size():
gpu_memory = torch.cuda.get_device_properties(0).total_memory
if gpu_memory > 40 * 1024**3: # 40GB 이상
return 16
elif gpu_memory > 20 * 1024**3: # 20GB 이상
return 8
else: # 20GB 미만
return 4
optimal_batch_size = get_optimal_batch_size()
embeddings = model.forward_queries(queries, batch_size=optimal_batch_size)
3. 이미지 전처리 최적화
def optimize_images(images, max_size=(1024, 1024)):
"""이미지 크기 최적화로 처리 속도 향상"""
optimized_images = []
for img in images:
# 큰 이미지 리사이징
if max(img.size) > max(max_size):
img.thumbnail(max_size, Image.Resampling.LANCZOS)
# RGB 모드로 변환
if img.mode != 'RGB':
img = img.convert('RGB')
optimized_images.append(img)
return optimized_images
# 사용 예제
optimized_images = optimize_images(images)
embeddings = model.forward_passages(optimized_images, batch_size=8)
실제 활용 사례
1. 기업 문서 검색 시스템
class EnterpriseDocumentSearch:
"""기업 내부 문서 검색을 위한 클래스"""
def __init__(self, model):
self.model = model
self.indexed_documents = {}
self.embeddings_db = None
def index_pdf_pages(self, pdf_path):
"""PDF 문서의 각 페이지를 이미지로 변환하여 인덱싱"""
import fitz # PyMuPDF
doc = fitz.open(pdf_path)
pages = []
for page_num in range(len(doc)):
page = doc.load_page(page_num)
pix = page.get_pixmap()
img_data = pix.tobytes("png")
image = Image.open(BytesIO(img_data))
pages.append(image)
self.indexed_documents[len(self.indexed_documents)] = {
'source': pdf_path,
'page': page_num,
'image': image
}
# 임베딩 생성 및 저장
embeddings = self.model.forward_passages(pages, batch_size=4)
if self.embeddings_db is None:
self.embeddings_db = embeddings
else:
self.embeddings_db = torch.cat([self.embeddings_db, embeddings], dim=0)
def search_documents(self, query, top_k=10):
"""자연어 쿼리로 문서 검색"""
query_embedding = self.model.forward_queries([query], batch_size=1)
scores = self.model.get_scores(query_embedding, self.embeddings_db)
top_indices = torch.topk(scores[0], k=min(top_k, len(scores[0]))).indices
results = []
for idx in top_indices:
doc_info = self.indexed_documents[idx.item()]
score = scores[0][idx].item()
results.append({
'source': doc_info['source'],
'page': doc_info['page'],
'score': score,
'image': doc_info['image']
})
return results
2. 다국어 문서 검색
def multilingual_search_demo():
"""다국어 환경에서의 검색 성능 테스트"""
# 다국어 쿼리
multilingual_queries = [
"What is the population growth rate?", # 영어
"인구 증가율은 얼마인가요?", # 한국어
"人口増加率はどのくらいですか?", # 일본어
"¿Cuál es la tasa de crecimiento poblacional?", # 스페인어
]
# 각 언어별 쿼리에 대한 검색 결과
for query in multilingual_queries:
query_embedding = model.forward_queries([query], batch_size=1)
scores = model.get_scores(query_embedding, passage_embeddings)
best_match = torch.argmax(scores[0]).item()
print(f"쿼리: {query}")
print(f"최적 매칭 문서: {best_match + 1}")
print(f"점수: {scores[0][best_match].item():.4f}")
print("-" * 60)
제한사항 및 주의사항
라이센스 제한
- 연구용 전용: 상업적 사용 불가
- NVIDIA Non-Commercial License 적용
- 프로덕션 환경에서는 상용 버전 사용 필요
기술적 제한사항
1. 하드웨어 요구사항
- 고성능 GPU 필요 (최소 8GB VRAM)
- CPU 전용 환경에서는 매우 느린 성능
2. 메모리 사용량
- 4.4B 매개변수로 인한 높은 메모리 사용량
- 대량 문서 처리 시 OOM 위험
3. 처리 속도
- 이미지 처리로 인한 상대적으로 느린 속도
- 실시간 검색에는 최적화 필요
개선 방안
# 메모리 효율적인 처리를 위한 팁
def memory_efficient_processing(queries, images, chunk_size=100):
"""메모리 효율적인 대량 처리"""
all_scores = []
for i in range(0, len(images), chunk_size):
chunk_images = images[i:i+chunk_size]
chunk_embeddings = model.forward_passages(chunk_images, batch_size=4)
for query in queries:
query_embedding = model.forward_queries([query], batch_size=1)
chunk_scores = model.get_scores(query_embedding, chunk_embeddings)
all_scores.append(chunk_scores)
# 메모리 정리
del chunk_embeddings
torch.cuda.empty_cache()
return all_scores
향후 발전 방향
1. 성능 개선 예상
- 더 큰 모델: 7B, 13B 버전 출시 예상
- 추론 최적화: TensorRT, ONNX 지원 확대
- 효율성 개선: 지식 증류를 통한 경량화
2. 기능 확장
- 동영상 지원: 비디오 문서 검색 기능
- 3D 문서: 3차원 문서 이해 능력
- 실시간 처리: 스트리밍 검색 지원
3. 생태계 확장
- 상용 버전: 프로덕션 환경용 라이센스
- 클라우드 API: 쉬운 접근성 제공
- 파인튜닝 지원: 도메인 특화 모델 개발
결론
NVIDIA의 llama-nemoretriever-colembed-3b-v1은 시각적 문서 검색 분야에서 새로운 표준을 제시한 혁신적인 모델입니다. ViDoRe와 MTEB 벤치마크에서의 압도적인 성능은 이 모델이 단순히 연구 단계를 넘어 실용적인 가치를 제공함을 보여줍니다.
주요 강점:
- 멀티모달 검색에서의 뛰어난 성능
- ColBERT 스타일의 정밀한 매칭
- 실제 활용 가능한 코드 예제와 도구
활용 권장 분야:
- 기업 문서 관리 시스템
- 멀티모달 RAG 애플리케이션
- 시각적 지식 베이스 구축
- 연구 및 학술 문서 검색
다만 연구용 라이센스와 높은 하드웨어 요구사항은 상용화에 있어 고려해야 할 요소입니다. 향후 상용 버전의 출시와 최적화된 추론 엔진의 개발이 기대되는 상황입니다.
시각적 문서 검색 기술의 발전은 우리가 정보를 찾고 활용하는 방식을 근본적으로 변화시킬 것입니다. 이 모델은 그러한 미래로 가는 중요한 이정표가 될 것으로 확신합니다.
참고 링크: