Papers with Code 하이브리드 검색 소개
Papers with Code는 논문과 코드, 벤치마크 결과를 한곳에서 찾을 수 있게 해 주는 AI 연구 색인 서비스로, Hugging Face가 2026년 5월부터 되살린 뒤 PostgreSQL 전문 검색과 벡터 검색을 결합한 하이브리드 검색(Hybrid Search) 을 새로 구축했습니다. 이 글은 Hugging Face의 Niels Rogge가 2026년 8월 21일 공개한 블로그를 바탕으로, 11만 편이 넘는 논문 임베딩을 어떤 인프라 위에서 만들고 서비스하는지, 그리고 그 과정에서 어떤 설계 결정을 내렸는지를 정리한 것입니다.
Papers with Code는 한동안 운영이 멈춰 있다가, Hugging Face가 Reddit 공지와 X 게시물로 부활을 알리면서 다시 문을 열었습니다. 저자는 이 프로젝트의 목표를 "열린 AI 연구를 접근 가능하고 소화하기 쉽게 만드는 것" 이라고 설명합니다. 사람이든 에이전트든 논문에 딸린 산출물을 쉽게 찾고, 분야별 SOTA(State of the Art)를 확인하고, 흥미로운 연구를 공유하며 서로의 연구 위에 쌓아 갈 수 있어야 한다는 것입니다. 궁극적으로는 다음 Transformer로 이어질 연구를 뒷받침하는 것이 목표라고 적혀 있습니다. 웹사이트뿐 아니라 pwc-cli라는 명령줄 도구와, 코딩 에이전트가 그 도구를 쓰는 법을 담은 Skill 파일까지 함께 제공하는 이유가 여기에 있습니다. pwc search "small vision language models" 한 줄로 에이전트가 논문을 찾을 수 있습니다.
이번에 소개하는 블로그의 핵심 질문은 "연구 논문을 찾는 검색은 일반 텍스트 검색과 무엇이 다른가" 입니다. 저자에 따르면 쓸 만한 논문 검색 엔진은 정확한 제목이나 arXiv 식별자를 곧바로 찾아야 하지만, 동시에 "small language models for code generation" 처럼 논문에 그 단어들이 함께 등장하지 않아도 뜻을 이해해야 합니다. "the original BERT paper" 가 특정 논문으로 이동하려는 탐색형 질의라는 것을 알아채야 하고, 불완전한 제목과 오타를 견뎌야 하며, 임베딩 모델 서비스가 잠들어 있거나 잠시 응답하지 않을 때도 빠르게 답해야 합니다. 이 요구사항 하나하나가 뒤에서 살펴볼 설계 결정으로 이어집니다.
이 글에서는 원문의 순서를 따라 하이브리드 검색의 배경, 전체 구조, 임베딩 계약, 그리고 Hugging Face Jobs, Storage Buckets, Inference Endpoints 세 서비스가 각각 맡은 역할을 차례로 살펴봅니다. 검색 시스템을 직접 운영하거나 RAG(Retrieval-Augmented Generation) 파이프라인의 검색 품질을 고민하는 분들께 참고가 될 만한 내용입니다.
키워드 검색과 벡터 검색, 그리고 둘을 합치는 하이브리드 검색
검색 시스템은 크게 두 계열로 나뉩니다. 키워드 검색(Keyword Search) 은 질의에 들어 있는 단어가 문서에 실제로 등장하는지를 보는 방식으로, 정확한 용어, 식별자, 희귀한 이름을 찾는 데 강합니다. 반면 벡터 검색(Vector Search) 은 질의와 문서를 임베딩 모델로 고정 길이 벡터로 바꾼 뒤 벡터 사이의 거리로 유사도를 재는 방식이라, 단어가 겹치지 않아도 뜻이 비슷한 문서를 찾아냅니다. 저자는 이를 "키워드 검색은 정확한 언급을 찾고, 벡터 검색은 더 흐릿하고 의미적으로 유사한 용어를 찾는다" 라고 요약합니다.
두 방식을 함께 쓰는 것이 하이브리드 검색입니다. Papers with Code 팀은 이 선택의 근거로 ML6에서 고객사에 RAG 기반 시스템을 구축했던 경험을 들며, "하이브리드 검색은 보통 키워드 검색과 벡터 검색 각각을 능가한다" 고 설명합니다. 아래 그림은 원문이 인용한 Microsoft의 Azure AI Search 블로그(2023) 실험 결과입니다. 세로축은 고품질 문서 조각이 상위 1~5개 결과 안에 포함된 질의의 비율이고, 다섯 구간 모두에서 하이브리드가 키워드 단독과 벡터 단독보다 높습니다. 같은 블로그의 표 1은 순위 품질을 재는 NDCG(Normalized Discounted Cumulative Gain)@3 기준으로 고객 데이터셋에서 다음 수치를 보고합니다.
| 검색 구성 | NDCG@3 (고객 데이터셋) |
|---|---|
| 키워드 | 40.6 |
| 벡터 (Ada-002) | 43.8 |
| 하이브리드 (키워드 + 벡터) | 48.4 |
| 하이브리드 + 의미 기반 재순위화 | 60.1 |
다만 이 값들은 Ada-002 임베딩과 512토큰 청크라는 특정 설정에서 나온 것이므로, 절대 수치보다 순서를 참고하는 편이 안전합니다.
여기서 한 단계 더 나아간 것이 리랭커(Reranker) 입니다. 교차 인코더(Cross-Encoder)라고도 부르는 이 모델은 질의와 후보 문서를 한 쌍으로 입력받아 관련도를 직접 점수화하므로 1차 검색 결과의 순서를 더 정확하게 바로잡을 수 있습니다. Pinecone의 리랭커 설명은 그 정확도의 이유를 임베딩 모델과 대비해 설명합니다. 임베딩 모델은 문서가 가질 수 있는 모든 의미를 벡터 하나로 압축해야 하고 질의를 알기 전에 문서를 인코딩하는 반면, 리랭커는 질의와 문서를 함께 입력받으므로 정보 손실이 적습니다. 대신 후보마다 모델을 한 번씩 더 실행해야 하므로 추가 연산과 지연 시간이 따라옵니다. Papers with Code는 현재 리랭커 없이 하이브리드 검색만 운영하고, 필요하면 Qwen3-Reranker 같은 모델을 뒤에 붙일 수 있다고 원문은 덧붙입니다.
저자는 한 가지 주의도 남깁니다. 하이브리드 검색이 언제나 최선은 아니며, "값싸고 빠른 기준선으로 키워드 검색부터 시작하고, 의미 검색이나 하이브리드 검색이 검색 품질을 의미 있게 끌어올린다고 확인될 때만 추가하는 것" 을 권합니다. 같은 구성을 사내 지식베이스에 적용한 사례로는 Cerebras의 pgvector와 RRF 기반 지식베이스 설계를, PostgreSQL 안에서 하이브리드 검색을 확장으로 제공하는 프로젝트로는 pgContext를 함께 참고하실 수 있습니다.
하이브리드 검색 더 알아보기
Azure AI Search: Outperforming vector search with hybrid retrieval and reranking - Microsoft (2023)
Rerankers and Two-Stage Retrieval - Pinecone
전체 구조: 오프라인 코퍼스 구축과 온라인 검색 서비스의 분리
Papers with Code의 검색은 PostgreSQL 데이터베이스를 기반으로 합니다. PostgreSQL이 기본으로 제공하는 전문 검색(Full-Text Search) 기능이 빠른 어휘 기준선 역할을 하고, 밀집 임베딩(Dense Embedding)은 pgvector 확장으로 같은 데이터베이스에 저장해 의미 기반 재현율을 보탭니다. 두 결과 목록은 상호 순위 결합(Reciprocal Rank Fusion, RRF) 알고리즘으로 하나로 합칩니다. 별도의 검색 엔진이나 벡터 데이터베이스를 도입하지 않고 PostgreSQL 하나로 두 계열을 모두 처리하는 구성입니다.
임베딩을 만드는 쪽에는 Hugging Face의 서비스 세 가지가 쓰입니다.
- Hugging Face Jobs: 논문 코퍼스 전체를 임베딩할 때 필요한, 짧게 폭발적으로 쓰는 GPU 연산을 제공합니다.
- Hugging Face Storage Buckets: 데이터베이스, 실험, Jobs 사이에서 산출물을 안전하게 넘겨 주는 영구 저장소입니다.
- Hugging Face Inference Endpoints: 실시간 질의와 소규모 증분 업데이트를 위한 저지연 임베딩을 서비스합니다.
위 그림은 이 글에서 살펴볼 전체 흐름을 보여줍니다. 상단의 오프라인 코퍼스 구축은 PostgreSQL에서 체크섬이 붙은 덤프를 내보내 스냅샷을 만들고, 이를 Storage Bucket에 올린 뒤 GPU Job이 임베딩해 매니페스트가 딸린 Parquet 파일로 내보내고, 검증을 거쳐 pgvector의 HNSW 인덱스로 적재하는 흐름입니다. 하단의 온라인 검색은 사용자 질의가 API에 도달하면 전문 검색과 벡터 검색을 동시에 실행하고 RRF로 결과를 합치는 흐름입니다. 저자는 이 분리를 "비용이 크고 처리량 중심인 작업은 Jobs로 실행하고, 영구 산출물은 Bucket에 두고, 요청 경로에는 작은 질의 임베딩 단계 하나만 보호된 Inference Endpoint 뒤에 둔다" 라고 요약합니다.
이 구조가 주는 실질적 이점은 폴백(Fallback) 입니다. 질의 임베딩 엔드포인트가 잠들어 있거나(cold), 바쁘거나, 비정상이면 검색은 즉시 전문 검색 결과만으로 응답합니다. 의미 검색이 빠져 품질은 조금 낮아질 수 있지만 사용자는 기다리지 않습니다. 원문 기준으로 이 시스템은 arXiv와 Hugging Face Daily Papers에서 가져온 11만 편 이상의 현행 논문 임베딩을 유지하고 있습니다.
임베딩 계약: 모델 이름만 고정하는 것으로는 부족하다
임베딩 파이프라인은 조용히 망가지는 경우가 많습니다. 저자가 꼽는 사례는 모델 리비전이 바뀌는 경우, 질의용 프롬프트와 문서용 프롬프트가 뒤섞이는 경우, 벡터를 잘라내는 차원이 서로 다른 경우, 초록이 갱신됐는데 저장된 벡터는 옛 초록을 기준으로 남아 있는 경우입니다. 어느 쪽도 오류를 내지 않은 채 검색 품질만 떨어뜨립니다.
Papers with Code 팀은 이를 막기 위해 임베딩 형식을 버전이 붙은 API처럼 취급하는 임베딩 계약(Embedding Contract) 을 세웠습니다. 모든 논문은 다음 형식으로 인코딩됩니다.
normalized title + "\n\n" + normalized abstract
그리고 벡터 세대(generation)마다 다음 항목을 함께 기록합니다.
- 모델 저장소와 정확한 리비전
- 출력 차원
- 입력 형식 버전
- 입력이 질의인지 문서인지
- 정규화(Normalization) 방식
- 원본 제목과 초록의 콘텐츠 해시(Content Hash)
운영에 쓰는 모델은 Qwen3-Embedding-0.6B로, 특정 리비전에 고정하고 256차원 L2 정규화 벡터를 출력하도록 설정했습니다. 모델 선정에는 임베딩 모델을 비교하는 표준 벤치마크인 MTEB(Massive Text Embedding Benchmark) 리더보드를 참고했습니다. 임베딩 모델 선택지에 관해서는 NVIDIA의 Nemotron 3 Embed 소개와 Perplexity의 pplx-embed 소개도 참고하실 수 있습니다.
Qwen3 같은 최근 임베딩 모델은 두 가지 기능을 더 제공하는데, 이 두 기능이 Papers with Code 설계에 그대로 쓰였습니다.
동적 임베딩 크기 (Dynamic Embedding Size): 품질과 속도 및 저장 비용 사이의 트레이드 오프(Trade-off)를 조절할 수 있습니다. Qwen 모델은 이를 MRL이라고 부르는데, Matryoshka Representation Learning 논문의 약자입니다. 러시아 인형처럼 큰 벡터의 앞부분만 잘라내도 의미 있는 작은 벡터가 되도록 학습하는 기법으로, 자세한 설명은 Hugging Face의 Matryoshka 임베딩 블로그에 있습니다. 이 블로그는 벡터를 자르고 나면 정규화가 풀리므로 다시 정규화해야 한다는 점도 함께 짚는데, 뒤에서 볼 Job의 처리 순서가 자르기 다음에 정규화를 두는 이유가 여기에 있습니다. 팀은 검색 속도를 위해 256차원을 택했습니다.
지시 프롬프트 (Instruction Prompt): Qwen 임베딩 모델은 문서를 임베딩할 때 쓰는 document 프롬프트와 질의를 임베딩할 때 쓰는 query 프롬프트를 구분합니다. Papers with Code는 논문에는 document 프롬프트를, 실시간 검색어에는 query 프롬프트를 적용합니다. 모델 카드에 따르면 지시문을 쓰면 대부분의 다운스트림 작업에서 쓰지 않을 때보다 1~5% 정도 성능이 오릅니다.
이 계약은 데이터베이스 내보내기에서 시작해 GPU 추론(Inference), PostgreSQL 적재, 온라인 검색까지 임베딩이 지나가는 모든 단계에 따라다닙니다. 어느 단계에서든 기록된 리비전, 차원, 프롬프트 종류, 정규화 방식, 콘텐츠 해시가 맞지 않으면 그 벡터는 검색에 쓰이지 않습니다.
Qwen3 Embedding과 Matryoshka 더 알아보기
Introduction to Matryoshka Embedding Models - Hugging Face Blog
Matryoshka Representation Learning - Kusupati et al. (NeurIPS 2022)
MTEB Leaderboard - Hugging Face Space
Hugging Face Jobs: 데이터베이스 스냅샷을 벡터 코퍼스로 바꾸는 배치 작업
코퍼스 전체를 임베딩하는 일은 전형적인 배치(Batch) 작업입니다. GPU가 비교적 짧은 시간 동안만 필요하고, 처리량이 높을수록 좋으며, 실행 사이에는 자원을 점유하지 않아야 합니다. Hugging Face Jobs는 이 형태에 맞습니다. Job은 명령어, 하드웨어 종류(flavor), 그리고 선택적으로 Docker 이미지로 정의되며, 의존성을 파일 안에 선언한 uv 스크립트를 바로 실행할 수 있습니다.
코퍼스 구축은 PostgreSQL의 반복 읽기(repeatable-read) 스냅샷에서 모든 논문의 최신 버전을 내보내는 것으로 시작합니다. 내보내기 프로그램은 카탈로그 전체를 메모리에 올리지 않고 행을 스트리밍하며, 크기가 제한된 JSONL 샤드(shard)를 쓰고, 행 개수와 SHA-256 체크섬(Checksum)을 담은 매니페스트(Manifest)를 만듭니다. 이 불변(immutable) 실행 디렉토리를 비공개 Storage Bucket에 동기화한 뒤, hf-mount로 Bucket을 l4x1 Job에 직접 마운트합니다. l4x1은 24GB VRAM을 가진 NVIDIA L4 GPU 한 장짜리 구성입니다. 작업자 입장에서 Bucket은 그냥 파일시스템으로 보입니다. hf-mount는 FUSE 또는 NFS로 Bucket과 Hub 저장소를 로컬 경로에 연결하고 파일을 처음 읽는 시점에 가져오므로, 코드가 실제로 건드린 바이트만 네트워크로 전송됩니다.
hf jobs uv run \
--flavor l4x1 \
--timeout 6h \
--volume hf://buckets/OWNER/pwc-paper-embeddings:/bucket \
embed_papers_job.py \
--input /bucket/runs/RUN_ID/input \
--output /bucket/runs/RUN_ID/output \
--model Qwen/Qwen3-Embedding-0.6B \
--revision MODEL_REVISION \
--dimensions 256 \
--allow-matryoshka
Job 안에서 실행되는 작업자는 다음 순서로 동작합니다.
- 입력 매니페스트와 모든 샤드의 체크섬을 검증합니다.
- 고정된 리비전의 모델을 불러옵니다.
- 패딩을 줄이기 위해 텍스트를 길이 순으로 정렬합니다.
- 모델 카드에 안내된 대로
encode_document를 배치 단위로 호출합니다. - GPU 메모리가 부족하면 배치 크기를 자동으로 줄입니다.
- Matryoshka 표현을 256차원으로 잘라 정규화합니다.
- float16 Parquet 샤드를 원자적으로(atomically) 씁니다.
- 처리량, 패키지 버전, 하드웨어, 최대 VRAM 사용량, 행 개수, 출력 체크섬을 기록합니다.
완료된 샤드마다 완료 마커 파일이 따로 남기 때문에, 중간에 중단된 Job을 다시 시작하면 이미 검증된 샤드는 건너뛰고 나머지만 처리합니다. 코퍼스가 크면 재시도가 기존 임베딩을 덮어쓰는 대신 이어서 작업하는 것이 중요합니다.
논문 5,000편으로 진행한 파일럿에서 Qwen Job은 L4 GPU 한 장에서 1024차원 기준 초당 약 75편을 인코딩했습니다. 같은 실행 결과를 512차원과 256차원으로도 결정적으로(deterministically) 만들어 낼 수 있었기 때문에, 추가 추론 비용 없이 저장 공간과 검색 품질의 트레이드 오프를 비교할 수 있었습니다. Matryoshka 학습의 실질적 이점이 여기서 드러납니다. 한 번 만든 1024차원 벡터의 앞 256개만 잘라 쓰면 되므로 차원별로 임베딩을 다시 계산할 필요가 없습니다.
Hugging Face Jobs 더 알아보기
Hugging Face Jobs 공식 문서
hf-mount: Hugging Face 저장소와 Bucket을 파일시스템으로 마운트하는 도구
Storage Buckets: 세 시스템을 잇는 영구 저장소
Storage Buckets는 Hugging Face Hub 위에서 제공되는 변경 가능한(mutable) S3 방식 객체 저장소로, AI 워크로드에 맞게 최적화되어 있습니다. 공식 문서에 따르면 Git 기반 저장소와 달리 버전 관리를 하지 않고(non-versioned) 덮어쓰기를 전제로 설계되어 있으며, 내부적으로는 Xet 저장 백엔드를 사용합니다. hf://buckets/... 경로로 접근할 수 있고, 별도의 저장소 연동 코드를 짜지 않아도 Jobs 안에 읽기와 쓰기가 가능한 형태로 마운트할 수 있습니다.
Papers with Code 팀에게 Bucket은 단순히 벡터를 두는 곳이 아니라, 수명 주기가 서로 다른 세 시스템 사이의 경계입니다.
- 운영 데이터베이스는 원본 레코드를 내보냅니다.
- 일시적인 Jobs는 그 레코드를 소비해 벡터를 만듭니다.
- 가져오기(import) 프로그램은 검색 인덱스에 손대기 전에 결과를 검증합니다.
산출물은 불변 실행 접두어(run prefix) 아래에 다음과 같이 정리됩니다.
runs/<run-id>/
├── input/
│ ├── manifest.json
│ └── papers-*.jsonl
└── output/
├── manifest.json
├── embeddings-*.parquet
└── embeddings-*.complete.json
Bucket 자체는 의도적으로 변경 가능하게 설계되어 있으므로, 불변성은 애플리케이션 수준의 규칙으로 지킵니다. 실행 ID는 절대 덮어쓰지 않고, 모든 산출물은 매니페스트와 체크섬으로 보호합니다. 이 규칙이 다섯 가지 이점을 만듭니다.
- 재현성(Reproducibility): 데이터베이스의 특정 세대를 정확한 코퍼스 스냅샷, 모델 리비전, 산출물 집합까지 거슬러 추적할 수 있습니다.
- 안전한 재시도: Jobs는 같은 실행 접두어 안에서 완료된 샤드부터 이어서 진행할 수 있습니다.
- 저렴한 실험: 검증된 입력 스냅샷 하나를 여러 모델이나 여러 차원 설정이 재사용할 수 있습니다.
- 통제된 배포: 세대를 가져왔다고 바로 활성화되지 않습니다. 먼저 커버리지를 검증하고 인덱스를 만듭니다.
- 간단한 롤백: 새 세대가 안정적이라고 입증될 때까지 이전 세대와 그 산출물이 그대로 남아 있습니다.
가져오기 프로그램이 스키마, 체크섬, 차원, 정규화, 논문 ID 중복 여부, 현행 콘텐츠 해시를 다시 확인한 뒤에야 벡터가 PostgreSQL에 적재됩니다. 이어서 새 세대 전용의 HNSW 인덱스를 별도로 만들고, 대상이 되는 모든 현행 논문이 빠짐없이 포함됐을 때만 원자적으로 활성 상태로 바꿉니다. HNSW(Hierarchical Navigable Small World)는 벡터를 여러 층의 그래프로 연결해 두고 상위 층에서 대략적인 위치를 찾은 뒤 하위 층으로 내려가며 이웃을 좁히는 근사 최근접 이웃(Approximate Nearest Neighbor, ANN) 알고리즘으로, 벡터 수가 많아도 밀리초 단위 검색을 가능하게 해 줍니다.
Storage Buckets 더 알아보기
Hugging Face Storage Buckets 공식 문서
Inference Endpoints: 질의 임베딩을 요청 경로에 올리기
배치 임베딩은 검색의 문서 쪽 문제를 해결합니다. 그러나 사용자의 질의는 요청이 들어오는 순간에 같은 모델 계약으로 임베딩되어야 합니다. 이를 위해 팀은 고정된 모델을 인증이 필요한 Inference Endpoint로 배포했고, 그 뒤에서는 Text Embeddings Inference(TEI)가 돌아갑니다. 엔드포인트는 질의 텍스트를 받아 모델의 query 프롬프트를 적용한 뒤 정규화된 256차원 벡터를 돌려줍니다. 원문은 이 자리에 vLLM이나 SGLang을 쓸 수도 있다고 덧붙입니다.
위 스크린샷은 실제 운영 중인 엔드포인트의 개요 화면입니다. AWS us-east-1의 NVIDIA L4 GPU 한 장에서 TEI 1.9.1 컨테이너가 Qwen/Qwen3-Embedding-0.6B의 리비전 97b0c61을 서비스하고 있고, 15분 동안 요청이 없으면 0으로 축소(scale-to-zero)되도록 설정되어 있습니다. 시간당 0.8달러짜리 인스턴스가 지난 한 시간에 220건의 요청을 처리했다는 정보도 함께 보입니다.
API는 이렇게 받은 벡터로 현재 활성화된 pgvector 세대에 대해 코사인 거리 검색을 수행합니다.
SELECT paper_id,
embedding <=> CAST(:query_vector AS halfvec(256)) AS distance
FROM paper_embeddings
WHERE generation_id = :active_generation
ORDER BY embedding <=> CAST(:query_vector AS halfvec(256))
LIMIT 50;
<=> 는 pgvector의 코사인 거리 연산자이고, halfvec(256) 은 256차원 벡터를 float16으로 저장하는 타입입니다. generation_id 조건 덕분에 여러 세대의 벡터가 한 테이블에 공존해도 검색은 활성 세대만 봅니다. HNSW 인덱스가 이 조회를 빠르게 유지하며, 5,000편 파일럿에서 측정한 수치는 다음과 같습니다.
| 지표 (256차원 Qwen HNSW 인덱스) | 값 |
|---|---|
| 정확 검색 대비 Recall@20 | 0.9955 |
| HNSW 조회 지연 시간 p50 | 1.31 ms |
| HNSW 조회 지연 시간 p95 | 2.21 ms |
| 테이블과 인덱스 저장 공간 (1024차원 대비) | 약 27% |
256차원으로 줄였는데도 정확 검색과 비교한 재현율(Recall)이 99.5%를 넘고, 저장 공간은 1024차원의 4분의 1 남짓입니다. 앞서 임베딩 계약에서 256차원을 고른 선택을 이 수치가 뒷받침합니다.
0으로 축소되는 엔드포인트와 콜드 스타트 설계
엔드포인트는 최대 복제본(replica) 1개로 설정되어 있고, 요청이 없으면 0으로 축소됩니다. 사용이 없을 때 비용을 내지 않는다는 점에서 유용한 비용 절감 수단이지만, 다시 깨어나 트래픽을 받기까지 시간이 걸리므로 콜드 스타트(Cold Start) 를 예외가 아닌 정상 상황으로 보고 애플리케이션을 설계해야 합니다.
그래서 질의 클라이언트는 의도적으로 엄격하게 동작합니다.
- 운영 환경 타임아웃 1초
- 대기 없이 즉시 거절하는(non-blocking) 동시성 제한
- 응답 벡터의 차원, 유한성(finiteness), 노름(norm) 검증
- 질의와 임베딩 세대를 키로 하는 짧은 캐시
- 반복 실패 시 작동하는 서킷 브레이커(Circuit Breaker)
- 로그에 원본 질의 텍스트를 남기지 않고 정규화된 지문(fingerprint)만 기록
엔드포인트가 확장 중이거나, 타임아웃되거나, 잘못된 형태의 벡터를 돌려주거나, 동시성 여유가 없으면 의미 검색 가지는 즉시 건너뜁니다. 사용자는 불안정한 의존성을 기다리는 대신 어휘 검색 결과를 받습니다. 저자는 Inference Endpoints가 안정적으로 동작했고, 핵심 지표를 한눈에 볼 수 있는 대시보드가 유용했다고 평가합니다.
위 대시보드는 2주 동안의 요청 통계입니다. 2XX 응답 56,704건에 5XX 응답 426건이 섞여 있고, 응답 시간 중앙값은 0.008초, p99는 0.013초로 나타납니다. 오른쪽 아래 "Running Replicas" 그래프에서 실행 중(running)과 초기화 중(initializing) 상태가 번갈아 나타나는 것이 scale-to-zero의 흔적입니다. 요청이 끊기면 복제본이 내려가고 다음 요청에 다시 올라오는 주기가 반복됩니다. 앞에서 설명한 1초 타임아웃과 어휘 검색 폴백 덕분에, 요청이 이 초기화 구간에 걸리더라도 사용자는 기다리지 않고 결과를 받습니다.
Inference Endpoints와 TEI 더 알아보기
Hugging Face Inference Endpoints 공식 문서
Text Embeddings Inference (TEI) GitHub 저장소
Inference Endpoints 자동 확장 및 Scale-to-Zero 가이드
하이브리드 결합: 가중 RRF와 결정적 식별 규칙
질의가 들어오면 어휘 가지는 가중치가 적용된 PostgreSQL 전문 검색으로 최대 50개 후보를 가져오고, 의미 가지는 pgvector에서 최대 50개 후보를 가져옵니다. 두 목록은 가중 상호 순위 결합(weighted RRF)으로 합쳐집니다.
여기서 d 는 후보 문서, \text{rank}_r(d) 는 가지 r 에서 그 문서가 받은 순위, w_r 은 가지별 가중치, k 는 순위 상수(rank constant)라고 부르는 하이퍼파라미터입니다. RRF는 서로 척도가 다른 두 시스템의 점수가 아니라 순위만 결합하기 때문에 단순하면서도 견고합니다. 어휘 가지와 의미 가지 양쪽에서 모두 상위에 오른 논문은 결합 결과에서도 상위에 오를 가능성이 높아집니다. 현재 설정은 두 가지의 가중치를 동일하게 두고 k = 60 을 씁니다. 이 값은 RRF를 처음 제안한 Cormack 등의 2009년 논문에서 온 것입니다. 논문은 파일럿 실험에서 k = 60 으로 고정한 뒤 이후 검증에서 바꾸지 않았고, "k = 60이 최적에 가까웠지만 그 선택이 결정적이지는 않았다" 고 밝히고 있습니다. 상수 k 가 하는 일은 일부 시스템만 유난히 높게 매긴 순위의 영향을 누그러뜨리는 것입니다. 하이브리드 질의를 결합할 때 Elasticsearch와 Azure AI Search가 쓰는 알고리즘도 같은 RRF이고, Elasticsearch 문서 역시 RRF에 별도의 튜닝이 필요하지 않다는 점을 장점으로 듭니다.
간단한 예를 들어보겠습니다. 어떤 논문이 어휘 검색에서 1위, 의미 검색에서 3위라면 점수는 \frac{1}{61} + \frac{1}{63} \approx 0.0323 입니다. 어휘 검색에서만 1위이고 의미 검색 후보에 없는 논문은 \frac{1}{61} \approx 0.0164 에 그칩니다. 한쪽에서 압도적으로 높은 점수를 받은 문서보다 양쪽에서 고르게 상위에 오른 문서를 앞세우는 것이 RRF의 성격이고, k 가 클수록 상위 순위 사이의 점수 차가 완만해집니다.
밀집 검색은 개념적인 질의의 재현율을 높이고, 전문 검색은 정확한 용어, 식별자, 희귀한 이름에 여전히 강합니다. 여기에 더해 팀은 결합된 순위 위에 결정적 식별 규칙(deterministic identity behavior) 을 얹어 둡니다.
- 정확한 제목과 arXiv ID는 항상 맨 위에 옵니다.
- 메소드 분류체계(method taxonomy)가 "the original BERT paper" 같은 탐색형 검색을 인식합니다.
- 불완전한 제목과 일정 범위 안의 오타는 보수적인 트라이그램(trigram) 후보로 처리합니다. 트라이그램은 문자열에서 잘라낸 연속된 세 글자를 뜻하고, 두 문자열이 공유하는 트라이그램 개수로 유사도를 잽니다. PostgreSQL의 pg_trgm 확장이 제공하는 방식입니다.
- 모호한 퍼지(fuzzy) 매치는 잘못된 결과를 억지로 내는 대신 결과를 내지 않습니다(abstain).
위 화면은 dino 검색 결과입니다. 상단에 메소드 항목 DINO가 2,651편의 논문과 함께 먼저 뜨고, 그 아래로 DINO 원 논문(Emerging Properties in Self-Supervised Vision Transformers), DINOv2, Mask DINO처럼 어휘적으로 맞는 결과와 MAE, SimSiam처럼 자기지도 학습이라는 의미로 연결되는 결과가 섞여 나옵니다. 메소드 분류체계가 인식한 DINO 항목과, 어휘 및 의미 양쪽에서 올라온 논문들이 한 화면에 함께 나옵니다.
RRF 더 알아보기
Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods - Cormack, Clarke, Buettcher (SIGIR 2009)
Reciprocal rank fusion - Elasticsearch 공식 문서
하나의 엔드포인트, 두 갈래 업데이트 경로
초기 대규모 코퍼스는 Jobs로 임베딩하지만, Papers with Code는 끊임없이 바뀝니다. 새 논문이 들어오고, 초록이 수정되고, 새 arXiv 버전이 현행 버전이 됩니다. 몇 개 안 되는 변경 행 때문에 GPU Job을 새로 실행하면 시작 비용과 오케스트레이션 부담만 늘어납니다.
대신 매시간 돌아가는 증분(incremental) 프로세스가 누락되었거나 내용이 바뀐 논문을 골라, 크기가 제한된 델타를 같은 TEI 엔드포인트에 보냅니다. 이번에는 document 프롬프트를 씁니다. 한 번의 실행은 최대 500편을 16편 단위 배치로 처리합니다. 임베딩을 쓰기 직전에 원본 행을 잠그고 콘텐츠 해시를 다시 확인하며, 추론 도중 논문이 바뀌었으면 그 벡터는 버리고 다음 실행에서 다시 처리합니다.
이렇게 세 서비스의 역할이 나뉩니다.
| 서비스 | 맡는 일 |
|---|---|
| Jobs | 전체 재구축, 새 모델 세대 도입, 대규모 백필(backfill) |
| Inference Endpoints | 실시간 질의 임베딩과 소규모 증분 문서 업데이트 |
| Buckets | 대규모 빌드 산출물 보존, 재개 가능하고 감사 가능한 빌드 |
매시간 경로는 온라인 엔드포인트를 무제한 배치 처리기로 바꾸지 않으면서도 활성 인덱스를 실제 카탈로그와 가깝게 유지합니다. 500편이라는 상한이 있기 때문에 변경이 몰려도 엔드포인트가 질의 처리를 못 할 정도로 점유되지 않습니다. 전체 재구축이나 새 모델 세대 도입, 대규모 백필처럼 규모가 큰 작업은 이 경로가 아니라 Jobs가 맡습니다.
관련 논문 추천: 저장된 벡터로 추가 비용 없이
같은 문서 임베딩이 각 논문 페이지의 관련 논문(related papers) 추천에도 쓰입니다.
원본 논문에 이미 저장된 벡터가 있으므로 관련 논문 검색은 요청 시점에 모델을 호출할 필요가 없습니다. 활성 세대에 대한 최근접 이웃(nearest neighbor) 질의 한 번이면 끝납니다. 벡터가 일시적으로 없으면 이전 arXiv 버전의 벡터를 쓰거나, 기존의 태스크 및 인용 기반 폴백으로 결과를 채울 수 있습니다. 인용 데이터는 Semantic Scholar API에서 가져오며, 팀은 이 인용 그래프를 질의하는 명령줄 도구 s2-cli도 함께 만들었습니다. s2-cli는 paperswithcode.co/chat에서 동작하는 에이전트가 사용합니다. 위 예시 화면은 멀티모달 이해와 생성을 하나의 구조로 묶은 SenseNova-U1 논문 페이지의 관련 논문입니다.
운영에서 얻은 6가지 교훈
저자는 시스템을 운영에 올리면서 배운 것을 여섯 가지로 정리합니다.
- 1. 처리량 작업과 지연 시간에 민감한 작업을 분리하라: 코퍼스 임베딩과 질의 임베딩은 같은 모델을 쓰지만 다른 인프라 문제입니다. Jobs는 처리량과 비용 상한에, Inference Endpoints는 가용성과 요청 지연 시간에 최적화되어 있습니다.
- 2. 저장소를 연산과 운영 사이의 명시적 계약으로 삼으라: Buckets는 연산과 운영 사이의 인계를 명시적으로 처리합니다. 체크섬이 붙은 산출물이 운영 인덱스에 들어가기 전에 검토 가능한 경계를 만듭니다.
- 3. 모델 이름 이상을 고정하라: 리비전, 차원, 프롬프트, 정규화, 입력 포매터가 모두 검색 결과에 영향을 줍니다. 이들을 함께 저장하고 모든 곳에서 검증해야 합니다.
- 4. 콜드 스타트를 전제로 설계하라: Scale-to-zero는 트래픽이 간헐적일 때 유용하지만, 제품에 빠른 폴백이 있을 때만 그렇습니다. 하이브리드 검색은 이 폴백을 자연스럽게 제공합니다. 어휘 검색은 그 자체로도 언제나 유용합니다.
- 5. 작은 벡터는 시스템의 기능일 수 있다: Matryoshka 임베딩 덕분에 품질, 메모리, 인덱스 크기, 지연 시간을 하나의 트레이드 오프로 평가할 수 있었습니다. 파일럿에서 256차원은 ANN 재현율을 유지하면서 1024차원보다 저장 공간을 크게 줄였습니다.
- 6. 활성화는 지루해야 한다: 새 세대는 현재 세대 옆에 가져와 독립적으로 인덱싱하고, 완전하고 현행인 커버리지를 확인한 뒤 원자적으로 활성화합니다. 롤백은 비상 재계산이 아니라 설정 변경입니다.
이 여섯 가지는 Papers with Code에 한정된 이야기가 아닙니다. PostgreSQL을 이미 쓰고 있는 팀이라면 pgvector와 RRF만으로 하이브리드 검색을 시작할 수 있고, 임베딩 생성은 GPU를 잠깐 빌려 쓰는 배치 작업으로, 질의 임베딩은 작은 엔드포인트로 분리하는 구성이 그대로 적용됩니다. 저자가 강조한 대로 시작점은 키워드 검색이고, 의미 검색은 측정으로 효과가 확인될 때 얹는 것이 순서입니다. 검색은 paperswithcode.co에서, 대화형 인터페이스는 paperswithcode.co/chat에서 직접 사용해 볼 수 있습니다.
How Hugging Face Inference Endpoints, Jobs, and Buckets Power Search on Papers with Code 블로그
Papers with Code 홈페이지
pwc-cli GitHub 저장소
s2-cli GitHub 저장소
Qwen3-Embedding-0.6B 모델 (Hugging Face)
더 읽어보기
-
하루 15,000개 질문에 답하는 Cerebras의 사내 지식 베이스 설계 노하우 (feat. pgvector, RRF, MCP)
-
Sentence Transformers로 도메인 특화 멀티 벡터 검색 모델 미세조정하기, GPU 한 장에 14.5시간
-
NVIDIA, RTEB 1위 오픈 임베딩 모델 Nemotron 3 Embed 공개 (feat. RAG, 에이전트 검색)
-
Hugging Face Skills: 코딩 에이전트에게 AI/ML 전문 지식을 부여하고 AI 워크플로우 자동화를 위한 스킬 모음 (feat. ACP)
이 글은 GPT 모델로 정리한 초안을 바탕으로 한 것으로, 원문의 내용 또는 의도와 다르게 정리된 내용이 있을 수 있습니다. 관심있는 내용이시라면 원문도 함께 참고해주세요! 읽으시면서 어색하거나 잘못된 내용을 발견하시면 댓글로 알려주시기를 부탁드립니다. ![]()
파이토치 한국 사용자 모임
에서 이런 글들을 계속 정리하고 있습니다. 회원 가입으로 주요 글들을 이메일
로, 텔레그램(Telegram)이나 Slack/Discord/Teams/Dooray/GoogleChat 등으로 새 글 알림을 받아보세요! ![]()
아래
쪽에 좋아요
를 눌러주시면 새로운 소식들을 정리하고 공유하는데 힘이 됩니다~ ![]()







