벡터 검색은 보통 쿼리에서 시작됩니다. 하지만 쿼리가 없다면 어떻게 해야 할까요?
기업은 고객 지원 티켓, 법률 문서, 뉴스 피드, 연구 논문 등 방대한 양의 문서를 축적하며, 올바른 질문을 던지기 위해서는 먼저 그 문서에 어떤 내용이 담겨 있는지 정확히 파악해야 합니다 레이블이나 학습 데이터가 없는 상태에서 수천 건의 문서를 수동으로 검토하는 것은 현실적으로 불가능합니다. 무엇을 검색해야 할지 모를 때는 기존 검색 방식이 도움이 되지 않습니다.
이 게시물에서는 이러한 탐색 문제를 해결하는 Elasticsearch 기반의 비지도형 문서 클러스터링 및 시간적 스토리 추적 방식을 안내합니다. 이 과정을 마치고 나면, 여러 날에 걸쳐 전개되는 스토리의 흐름을 다음과 같이 추적할 수 있습니다.

다룰 내용:
- 쿼리 없이 주제를 탐색할 때, (검색용 임베딩이 아닌) 클러스터링 임베딩을 사용하는 것이 왜 중요한가요?
- Elasticsearch의 k-최근접 이웃(kNN)과 배치(Batched)
msearch을(를) 활용하여, 밀도 조사 중심 분류 방식이 문서를 주제별로 그룹화하는 방법. - 모델 학습 없이도 주제를 파악할 수 있도록,
significant_text이(가) 클러스터에 자동 레이블 지정을 수행하는 방법. - 시간적 스토리 체인이 일별 클러스터를 연결하여, 주제가 매일 어떻게 진화하는지를 보여 줍니다.
이 게시물은 실행 가능한 Jupyter Notebook에서 생성되었습니다. 본문 전체에 걸쳐 표시되는 인라인 출력은 파이프라인의 실제 결과입니다. 제공되는 노트북을 복제하여 직접 실행해 보세요.
이 파이프라인은 BBC 뉴스 및 The Guardian에서 제공하는 2025년 2월 기사 약 8,500개를 테스트 코퍼스로 사용합니다. 뉴스는 시간적 흐름이 명확하기 때문에 활용하기 편리하지만, 이러한 패턴은 법률 검토, 규정 준수 모니터링, 연구 종합, 고객 지원 분류 등 문서 탐색이 중요한 모든 곳에 적용될 수 있습니다.
스택:
- Jina v5 클러스터링 임베딩: 주제 그룹화를 위한 작업별 LoRA(Low-Rank Adaptation) 어댑터. Jina가 Elastic과 함께하게 되었습니다. Elastic Inference Service(EIS)에서 Jina 모델을 네이티브로 사용할 수 있습니다.
- Elasticsearch: 확장 가능한 kNN,
significant_text레이블 지정, 및 벡터 스토리지. - DiskBBQ: 근사 최근접 이웃(ANN) 검색 가속화를 위해 더 나은 이진 양자화(BBQ)와 계층적 K-평균 분할을 결합한 디스크 기반 벡터 인덱스 포맷입니다. 이 인덱스 분할은 벡터 검색 내부에서 이루어지며, 이 게시물에서 사용된 밀도 조사 클러스터링 알고리즘과는 별개입니다.
bbq_disk은(는) 양자화된 벡터를 디스크에 저장하고 힙(Heap)에는 분할 메타데이터만 유지합니다. 덕분에bbq_hnsw과(와) 비교했을 때 높은 재현율을 유지하면서도 리소스 요구 사양을 획기적으로 낮출 수 있습니다. - 전역 클러스터링 + 일별 시간적 연결: 탐색 및 스토리 진화.
준비 사항:
- Elasticsearch 배포(Elastic Cloud, Elasticsearch Serverless 또는 Elastic Self-Managed 8.18+/9.0+)
bbq_disk을(를) 사용하려면 8.18 이상이 필요합니다. 선택 사항인 다양성 기반 검색기 섹션은 9.3 이상 또는 서버리스가 필요합니다. - Jina API 키Jina API 키: 무료 티어에는 1,000만 토큰이 포함되어 있으며, 이는 핵심 클러스터링 파이프라인(약 425만 토큰)을 감당할 수 있습니다. 선택 사항인 검색과 클러스터링 비교에는 두 번째 임베딩 패스를 사용합니다.
- Guardian API 키 (무료).
설정
필요한 패키지 설치:
선택 사항(이 리포지토리에서 스크래핑 도우미를 실행하는 경우에만):
그런 다음 프로젝트 루트에 있는 .env 파일에 API 키를 구성하세요.
이 노트북은 load_dotenv(override=True)을(를) 호출하므로, 로컬 .env 값이 우선적으로 적용됩니다.
1부: 탐색 클러스터링 - 왜 임베딩을 클러스터링해야 할까요?
대부분의 벡터 검색은 쿼리와 관련 문서 간의 일치 여부를 판단하도록 학습된 검색용 임베딩을 사용합니다. 검색에는 완벽하지만, 탐색에는 적합하지 않습니다. 쿼리 없이도 코퍼스에 어떤 주제가 존재하는지 찾으려면 유사한 문서를 함께 그룹화하는 임베딩이 필요합니다.
Jina v5는 작업별 LoRA(Low-Rank Adaptation) 어댑터를 통해 이 문제를 해결합니다. LoRA는 모델의 기본 가중치 대부분을 고정한 채, 대상 내부 레이어에 소규모의 저순위 업데이트를 추가합니다. 이를 통해 모델 전체를 재학습시키지 않고도 특정 작업에 맞춰 모델의 동작을 변화시킬 수 있습니다. 동일한 기본 모델이라도 task 매개변수 설정에 따라 서로 다른 임베딩을 생성합니다.
| 작업 | 학습 용도 | 사용 사례 |
|---|---|---|
| retrieval.passage | 쿼리-문서 일치 | 검색, Retrieval-Augmented Generation(RAG) |
| 클러스터링 | 주제 그룹화(긴밀한 클러스터에 최적화됨) | 탐색, 분류 |
클러스터링 어댑터는 동일한 주제의 문서들은 임베딩 공간에서 더 가깝게, 서로 다른 주제의 문서들은 더 멀리 배치되도록 학습되었습니다. 아래의 시각적 비교 자료를 통해 그 차이를 명확히 확인할 수 있습니다.
검색과 클러스터링: 시각적 비교
그 차이를 확인하기 위해, 두 가지 작업 유형을 각각 모두 적용하여 문서 샘플을 임베딩했습니다. 클러스터링은 원본 데이터인 1024차원의 임베딩 공간에서 수행됩니다. UMAP(Uniform Manifold Approximation and Projection)은 시각화를 위해 이러한 임베딩을 2차원으로 투영하는 용도로만 사용됩니다. UMAP은 데이터의 지역적 이웃 구조를 보존하므로, 클러스터 간의 분리를 비교하는 데 유용합니다.
아래는 동일한 480개의 문서 샘플에 두 가지 작업 유형을 각각 모두 적용하여 임베딩한 후, UMAP을 통해 2차원으로 투영한 결과입니다. 클러스터링 패널에서 더 긴밀하고, 더 명확히 분리된 색상 그룹을 찾습니다.

검색용 임베딩(왼쪽)은 주제를 넓게 퍼뜨리는 반면, 클러스터링용 임베딩(오른쪽)은 동일한 문서에서도 더 긴밀하고 명확하게 구분된 그룹을 형성합니다.
클러스터링 임베딩은 더 긴밀하고 시각적으로도 훨씬 뚜렷하게 구별되는 그룹을 생성합니다. 검색 임베딩은 주제를 더 고르게 분산시켜 검색(세밀한 유사도)에 이상적이지만, 탐색의 경우 긴밀한 주제별 클러스터가 중요합니다.
이것이 바로 본 단계별 안내 나머지 과정에서 task="clustering"을(를) 사용하는 이유입니다.
데이터셋 로딩
코퍼스는 2025년 2월의 두 뉴스 소스를 결합합니다.
- BBC 뉴스: RealTimeData/bbc_news_alltime HuggingFace 데이터셋 활용.
- The Guardian: Guardian Open Platform API활용.
여러 소스의 데이터를 사용하면, 클러스터링이 소스만의 특정 스타일이 아닌 주제를 제대로 찾아내는지 검증하는 데 도움이 됩니다.
클러스터링 작업을 통한 임베딩
모든 문서에 대해 Jina v5 API는 task="clustering" (으)로 호출됩니다. 임베딩은 디스크에 캐시되므로 이후 실행에서는 API를 완전히 건너뜁니다.
API 호출은 간단합니다. task 매개변수는 일반적인 임베딩 사용 방식과 구분되는 가장 핵심적인 차이점입니다.
아래 실행 시간은 캐시 적중이 적용되었습니다. API를 처음 실행할 때 걸리는 시간은 코퍼스 크기에 따라 달라집니다.
단일 Elasticsearch 인덱스로 색인
탐색 클러스터링을 위해, 한 달 전체 데이터를 하나의 인덱스(docs-clustering-all)에 저장합니다. 일일 분할은 시간적 스토리 연결을 위해 나중에 이루어집니다.
인덱스 매핑 시 벡터 필드에 bbq_disk (을)를 사용합니다.
1024차원의 float32 벡터는 용량이 4KB입니다. bbq_disk(은)는 계층적 K-평균을 사용하여 벡터를 작은 클러스터로 나누고, 이를 이진 양자화하여 저장한 뒤, 재계산을 위해 전체 정밀도 벡터를 디스크에 보관합니다. 오직 분할 메타데이터만 힙에 상주하므로, 방대한 코퍼스를 처리할 때도 메모리 사용량을 낮게 유지할 수 있습니다. 더 많은 힙을 감당할 수 있는 워크로드의 경우, bbq_hnsw(은)는 리소스 비용이 더 높지만 더 빠른 검색이 가능한 계층적으로 탐색 가능 작은 세계(HNSW) 그래프를 생성합니다.
dense_vector 필드 타입은 다양한 양자화 전략을 지원합니다. 여기서 사용된 1024차원 벡터와 같은 고차원 임베딩에는 bbq_disk 및 bbq_hnsw 방식이 가장 적합합니다.
클러스터링: 밀도 조사 중심 분류
HDBSCAN과 같은 전통적인 클러스터링 알고리즘은 N×d 크기의 전체 벡터 행렬을 메모리에 올려 둘 수 있고, 전체 데이터를 반복적으로 훑으며 업데이트를 수행할 수 있다는 가정하에 작동합니다. 1,024차원의 문서 8,495개라면 충분히 감당할 수 있는 크기(35MB 수준)입니다. 하지만 추가적인 인프라 없이는 수백만 건의 문서로 확장하기 어려운 접근 방식입니다.
이 알고리즘은 개념적으로 Voronoi 할당과 노이즈 플로어를 사용하는 KMeans++ 초기화 방식과 유사하지만, Elasticsearch kNN 검색을 연산 프리미티브로 사용하여 거의 모든 작업을 서버 측에서 처리합니다.
- 전체 문서의 5%를 밀도 조사용으로 샘플링합니다(무작위 샘플, 최소 50개).
- 배치 처리된
msearchkNN을 통해 밀도를 조사합니다. 각 조사 지점에서 kNN 쿼리를 실행해 주변의 평균 유사도를 기록합니다. 높은 평균 유사도는 임베딩 공간의 밀집 영역을 의미합니다.msearch(은)는 단일 HTTP 호출로 여러 검색 요청을 보낼 수 있는데, 이 기능이 여기서 매우 중요합니다. 밀도 조사 과정에서 수백 개의 kNN 쿼리가 생성되는데, 이를 배치로 처리함으로써 요청당 발생하는 오버헤드를 방지할 수 있기 때문입니다. - 다양성을 갖춘 고밀도 시드 선택: 밀도 중앙값 이상의 후보들을 밀도 내림차순으로 정렬한 뒤, 모든 기존 시드와의 코사인 유사도가 분리 임계값보다 낮은 경우에만 탐욕적으로 채택합니다. 이것이 유일한 클라이언트 측 연산입니다.(문서 8,000개 기준 약 0.01초 소요)
msearchkNN을 통해 모든 문서를 중심 기준으로 분류: 각 시드가 중심 역할을 수행하며, kNN 검색을 통해 유사도 임계값 이상의 인접 문서를 찾아냅니다. 각 문서는 가장 높은 점수를 반환한 중심에 할당됩니다. 규모가 작은 클러스터들은 해체되어 노이즈로 처리됩니다.
고난도 작업은 Elasticsearch가 처리합니다. 즉, msearch(은)는 밀도 조사에, msearch(은)는 분류에, 그리고 significant_text(은)는 레이블 지정에 활용됩니다. 이 코퍼스(총 8,495개 문서)의 경우, 5%의 밀도 조사용 샘플을 통해 425개의 kNN 조사 쿼리가 실행됩니다. msearch(은)는 이를 9개의 HTTP 호출(배치 크기 50)로 배치 처리함으로써, 개별 조사마다 발생할 수 있는 요청 오버헤드를 방지합니다. 여기에 bbq_disk의 ANN 조회가 결합되어, 클러스터링 단계를 빠르고 확장 가능하게 유지해 줍니다. 클러스터링 단계에서는 속도를 위해 num_candidates 값을 최소화하여 kNN 쿼리를 실행합니다. 반면, 프로덕션 검색 쿼리에서는 지연 시간이 늘어나더라도 재현율을 높이기 위해 더 큰 num_candidates 값을 사용해야 합니다.
클러스터의 크기는 엄격한 k 제한이 아니라, 각 중심 주변의 임베딩 공간 밀도에 따라 자연스럽게 결정됩니다. 밀집된 주제 영역은 더 큰 클러스터를 형성하며, 지엽적인 주제 영역은 더 작은 클러스터를 형성합니다.
KMeans나 HDBSCAN을 사용하지 않는 이유는 무엇일까요?
KMeans는 구형 클러스터를 가정하며 전체 N×d 행렬을 메모리에 저장해야 합니다. 반면, 메모리에 수용 가능한 크기의 코퍼스라면 HDBSCAN이 강력한 대안이 됩니다. 이는 클러스터의 모양에 구애받지 않으며, 밀도 시맨틱이 잘 정립되어 있습니다.
밀도 조사 중심 접근 방식은 다른 지엽적인 영역을 공략합니다. 바로 저장 공간, 검색, 클러스터링을 하나의 시스템 내에서 통합 처리하려 하거나, 데이터 규모 때문에 클라이언트 측에서의 행렬 연산이 실용적이지 못한 경우입니다. Elasticsearch kNN을 컴퓨팅 기본 요소로 사용하고, 임의의 클러스터 크기를 처리하며, 거의 모든 계산을 서버 측에서 수행합니다.
노이즈 비율에 대한 이해
약 28%의 노이즈 발생률은 의도적으로 설계된 것이며, 오류 모드가 아닙니다. 구성된 similarity_threshold 기준에 따라 어떤 밀집 클러스터에도 적합하지 않은 문서는, 어설픈 매칭을 강행하는 대신 미할당 상태로 남겨 둡니다. 이는 일종의 품질 검사기 역할을 합니다. 사설 칼럼, 단신, 혹은 일회성 기사들은 응집력 있는 그룹을 형성할 만큼의 주제적 밀도가 낮기 때문에 자연스럽게 클러스터링을 거부하게 됩니다.
이 임계값은 조정이 가능합니다. similarity_threshold(을)를 낮추면 더 공격적인 클러스터링이 이루어지며 (더 많은 문서가 할당되지만 군집의 결속력은 느슨해짐) 반대로 높이면 클러스터가 더 긴밀해지는 대신 노이즈 비율이 증가합니다. 다양한 뉴스 콘텐츠가 섞인 이번 코퍼스의 경우, 약 30%의 노이즈 비율은 운용상 적절한 수치입니다. 실제 배포 환경에서는 도메인별 품질 기준에 맞춰 임계값을 조정해야 합니다.
significant_text를 활용한 자동 레이블
이제 각 클러스터에 사람이 읽을 수 있는 레이블을 지정해야 합니다. Elasticsearch의 significant_text 집계는 백그라운드 세트(전체 코퍼스)와 비교했을 때, 포그라운드 세트(클러스터) 내에서 유독 자주 등장하는 용어들을 찾아냅니다.
내부적으로는 머신 러닝이나 거대 언어 모델(LLM) 호출 없이, 절대 빈도와 상대 빈도의 변화를 균형 있게 고려하는 통계적 휴리스틱(기본값으로 JLH 점수 사용)을 활용합니다 영국 정치에 관한 클러스터라면 starmer, labour, downing 같은 용어들이 등장할 것입니다. 이 용어들이 전체 뉴스 코퍼스에 비해 해당 클러스터 내에서 압도적으로 높은 비중을 차지하기 때문입니다.
이번 전체 프로세스에서 레이블은 docs-clustering-all을(를) 기준으로 직접 계산됩니다. 따라서 포그라운드와 백그라운드 모두 한 달 전체 분량에서 추출됩니다. 2부의 레이블링 작업에는 일별 인덱스 패턴(docs-clustering-*)이 사용됩니다. 이 와일드카드 패턴은 쿼리가 모든 일치 인덱스를 동시에 쿼리할 수 있게 하여, significant_text이(가) 더 넓은 백그라운드 바탕으로 더 명확한 대비 효과를 얻을 수 있도록 합니다.
최소한의 쿼리 형태는 다음과 같습니다.
significant_text 또한 품질 검사기 역할도 합니다. 유미의한 용어를 생성하지 않는 클러스터는 구별되는 어휘가 없습니다. 이는 일관성 없는 그룹으로, 오해의 소지가 있는 레이블을 지정하기보다는 다시 노이즈로 해체되어야 합니다.
가벼운 결정론적 정제 단계를 거쳐 노이즈가 섞인 레이블 용어(숫자 토큰, 일반 단어 등)를 제거하며, 필요한 경우 대표적인 헤드라인을 대신 사용합니다. 이를 통해 레이블의 가독성을 높이면서도 Elasticsearch 레이블을 네이티브로 유지할 수 있습니다.
클러스터 시각화
아래 시각화는 전체 클러스터링 과정에서 발견된 결과물입니다. 클러스터링된 문서와 노이즈 문서의 날짜별 구성 현황, 한 달 전체 데이터의 UMAP 투영도, 그리고 클러스터가이 소스가 아닌 주제를 기준으로 형성되었음을 보여 주는 소스 혼합 차트를 확인할 수 있습니다.

2025년 2월 전체 기간에 걸친 클러스터링된 문서와 노이즈 문서의 일일 분포.

한 달 전체 분량의 UMAP 투영: 색상으로 구분된 각 섬은 주제 클러스터를 나타내며, 회색 점들은 노이즈입니다.

클러스터링된 문서만 표시: 노이즈를 제거하면 주제별 구조가 더욱 명확하게 드러납니다.

하나의 클러스터(프리미어 리그 축구)를 다른 모든 데이터와 대비시켜 강조한 집중 보기입니다.

클러스터별 소스 혼합: 모든 주요 클러스터에 BBC와 Guardian 기사가 공통적으로 포함되어 있습니다. 이를 통해 소스 기반이 아닌 주제 기반으로 그룹화되었음을 확인할 수 있습니다.
UMAP 상의 각 색상별 섬은 클러스터를 나타냅니다. 이는 오직 임베딩 유사도만을 기반으로 발견된, 동일한 주제에 관한 기사 그룹입니다. 회색으로 표시된 노이즈 지점은 어떤 클러스터에도 명확히 속하지 않는 기사들입니다.(주로 짧은 사설, 또는 단발성 뉴스인 경우가 많음)
소스 분류 차트를 통해 각 클러스터에 BBC News와 The Guardian의 기사가 모두 포함되어 있음을 확인할 수 있습니다. 이 클러스터링 시스템은 소스가 아닌 주제를 찾아내고 있으며, 이는 비지도형 탐색이 생성해야 하는 가장 이상적인 결과입니다.
다양성 기반 검색기(diversify retriever)를 사용하여 클러스터 범위를 탐색
일반 kNN은 클러스터의 중심(밀집된 핵심)과 가장 유사한 문서를 반환합니다. 그러나 실제 클러스터는 하위 주제를 다룹니다. 다양성 기반 검색기는 최대 한계 관련성(MMR)을 사용하여, 클러스터 중심과 관련성이 높으면서도 서로 다른 문서들을 찾아냅니다.
핵심 매개변수는 λ(람다)입니다.
- λ = 1.0 → 순수한 관련성(일반 kNN과 동일)입니다.
- λ = 0.0 → 순수한 다양성 (최대한 분산된 결과).
- λ=0.5 → 균형: 주제와 관련이 있지만 다른 관점을 다룹니다.
버전 참고: 다양성 기반 검색기는 Elastic Cloud Serverless 및 Self-Managed Elasticsearch 9.3+ 이상 버전에서 사용할 수 있습니다. 이전 버전을 사용하더라도 클러스터링 및 시간적 연결 섹션의 내용은 그대로 따라 할 수 있습니다. 이 탐색 단계에서만 다양성 기반 검색기가 필요합니다.
최소한의 검색기 요청 형태는 다음과 같습니다.
다양성 수준에서 type, field, query_vector 매개변수는 필수입니다. field(은)는 결과 간 유사도를 측정할 때 어떤 dense_vector 필드를 사용할지 MMR에 지시하며, query_vector(은)는 정확도 점수 산정의 기준점을 제공합니다.
이를 통해 단순히 '이 클러스터의 중심 내용이 무엇인가?'를 파악하는 수준을 넘어, '이 클러스터가 실제로 무엇을 다루고 있는가?' 라는 질문에 답할 수 있습니다.
일반 kNN 결과는 주제의 한 가지 관점에 대해서만 클러스터를 이룹니다. 즉, 중심 및 서로 간의 유사도가 가장 높은 문서들만 한데 모이게 됩니다. 다양성 기반 검색기는 동일한 클러스터의 서로 다른 패싯, 즉 하위 주제, 서로 다른 소스, 다양한 관점을 보여 줍니다.
다양성 지표가 이를 수치적으로 증명합니다. 다양성 기반 검색기 결과의 평균 쌍별 유사성이 더 낮게 나타나는데, 이는 검색된 문서가 더 넓은 범위를 다룬다는 것을 의미합니다.
이는 다음과 같은 경우에 유용합니다.
- 클러스터가 실제로 포괄하는 범위가 어디까지인지 이해하려면, 단순히 중심부뿐만 아니라 그 외곽 경계까지 살펴봐야 합니다.
- 요약 생성. 다양한 대표 문서가 LLM에 더 나은 자료를 제공합니다.
- 사람이 직접 검토하거나 후속 레이블 지정에 사용할 대표 사례들을 찾아냅니다.
- 품질 검사. 다양한 결과가 일관성이 없어 보이면 클러스터를 분할해야 할 수도 있습니다.
2부: 시간적 스토리 체인
일자별 스토리 추적
1부에서는 주제 탐색을 위해 한달 전체를 전역으로 클러스터링했습니다. 시간적 흐름을 파악하기 위해, 동일한 밀도 조사 중심 분류를 일별 인덱스에 대해 매일 독립적으로 실행하면, 인접한 날짜 간에 클러스터가 서로 연결됩니다. 참고로 일별 클러스터는 1부의 전역 클러스터와는 별개입니다. 각 날짜마다 그날의 콘텐츠에 맞게 조정된 자체 클러스터 할당과 레이블이 생성됩니다.
연결 방식: 샘플링 후 쿼리
A일의 각 클러스터에 대하여:
- 몇 개의 대표 문서들을 샘플링합니다.
- B일의 인덱스를 대상으로 kNN 검색을 실행합니다.
- B일의 각 클러스터에 검색 결과가 몇 건이나 도달하는지 계산합니다.
- 검색 결과의 비율이 임계값(kNN fraction ≥ 0.4)을 초과하면, 두 클러스터 사이의 링크를 기록합니다.
이 과정은 빠르며(전체 문서가 아닌 클러스터당 몇 개의 문서만 쿼리하기 때문) Elasticsearch의 네이티브 kNN을 사용하므로 외부 도구가 필요하지 않습니다.
kNN 비율이 100%라는 것은 소스 클러스터에서 샘플링된 모든 문서가 동일한 타겟 클러스터에 도달했음을 의미하며, 이는 일자 간에 형성될 수 있는 가장 강력한 링크입니다. 위에서 확인된 대부분의 링크는 축구 관련 내용입니다. 이는 타당한 결과인데, 프리미어 리그 보도는 매일 발생하며 주제의 일관성 또한 매우 높기 때문입니다.
score | operator | gedling → league | striker | season(으)로 이어지는 링크는 지엽적인 로컬 축구 클러스터(Gedling은 비리그 구단임)가 다음 날 더 광범위한 프리미어 리그 클러스터로 흡수되는 과정을 보여 주는 사례입니다. 이는 매일 다른 세부 단위로 다시 클러스터링이 이루어질 때 나타나는 자연스러운 현상입니다.
스토리 체인 구축
스토리 체인은 연속된 날짜에 걸쳐 서로 연결된 클러스터들의 시퀀스입니다.
각각의 쌍별 링크를 통해 월요일의 '영국 정치' 클러스터가 화요일의 클러스터와 연결된다는 사실을 알 수 있습니다. 월요일에 시작된 이야기가 한 주 동안 어떻게 전개되고, 금요일에 이르러 어떻게 잦아드는지, 체인을 통해 스토리의 전체 흐름을 파악할 수 있습니다.
체인은 kNN 비율 0.4 이상의 링크를 바탕으로 탐욕적 방식으로 구축됩니다. 이는 소스 클러스터에서 샘플링된 문서 중 최소 40%가 단일 타겟 클러스터에 매칭되었음을 의미합니다. 가장 이른 시점의 클러스터부터 시작하여, 알고리즘은 항상 가장 강력한 출력 링크를 따라갑니다.
가장 긴 스토리 체인은 우크라이나-러시아 관련 보도를 19일 연속으로 추적했는데, 2025년 2월의 지속적인 지정학적 긴장 수위를 고려하면 당연한 결과라 할 수 있습니다. 두 번째로 긴 체인은 한 달 중 19일 동안 이어지는 프리미어 리그 축구 소식을 추적합니다. 더 짧은 체인으로는 시상식 시즌(영화/시상식, 6일), 식스 네이션스 럭비(10일), 그리고 영국 정치 리더십 관련 보도(7일) 등이 포착되었습니다. 각 체인은 알고리즘이 일자별 인덱스 간의 임베딩 유사도만을 바탕으로 순수하게 찾아낸 스토리의 흐름을 나타냅니다.
Sankey: 스토리 흐름 시각화
Sankey 다이어그램은 링크의 폭으로 연결 강도를 표현하는 흐름 시각화 도구입니다. 여기에서 각 수직 밴드는 하루를, 각 노드는 일별 클러스터(문서 수에 따른 크기)를 나타내며, 각각의 색상 경로는 시간을 따라 이어지는 하나의 스토리 체인을 추적합니다. 링크의 폭은 kNN 중첩 강도를 나타냅니다. 링크가 굵을수록 샘플링된 더 많은 문서가 타겟 클러스터에 도달했음을 의미합니다. 체인마다 색상이 일치하므로, 왼쪽에서 오른쪽으로 이어지는 하나의 색상 경로는 하나의 이야기 진행을 나타냅니다.
예를 들어, (가장 긴 경로 중 하나로 시각화된) 우크라이나-러시아 체인은 2월 초부터 셋째 주까지 계속해서 이어지며, 링크의 폭이 지속적으로 굵게 유지되는 것은 일자별로 강력한 주제적 연속성이 있었음을 보여 줍니다.

2025년 2월 한 달간 전개되는 시간적 스토리 체인. 각 색상별 경로는 며칠 동안 지속되는 스토리를 나타내며, 링크 폭은 k-NN 중첩 강도를 보여 줍니다.
이 접근 방식이 제공하는 이점
이 단계별 안내에서는 Elasticsearch를 활용하여 비지도형 문서 클러스터링 파이프라인의 전 과정을 살펴보았습니다.
- 임베딩 클러스터링: Jina v5의 작업별 어댑터는 단순한 쿼리-문서 매칭을 넘어, 주제별 그룹화에 최적화된 임베딩을 생성합니다.
- 전역 탐색 클러스터링: 한 달 전체를 하나의 인덱스에 클러스터링하면 일별 주제 탐색을 극대화할 수 있습니다.
- 밀도 조사 중심 분류: 5%를 샘플링하고,
msearchkNN을 통해 밀도를 조사하며, 밀도가 높은 다양한 시드를 선택하고, 모든 문서를 이 중심에 맞춰 분류합니다. 연산량이 많아 무거운 작업은 Elasticsearch가 처리하며, 시드 선택 과정만 클라이언트 측에서 실행됩니다.(약 0.01초 소요) significant_text레이블 지정: 유의성 검정을 통해 ML 모델이나 수동 주석 없이도 의미 있는 클러스터 레이블을 생성합니다. 유의미한 용어를 생성하지 않는 클러스터는 일관성이 없으며 노이즈로 강등됩니다. 이는 시스템에 내장된 품질 검사기입니다.- 시간적 스토리 연결: 일일 인덱스와 샘플 및 쿼리 교차 인덱스 kNN은 시간이 지남에 따라 스토리가 어떻게 진화하는지 추적합니다.
핵심 사항:
- 임베딩 작업 유형 설정이 핵심입니다. 클러스터링 임베딩을 사용하면 주제별 그룹이 훨씬 더 긴밀하게 형성됩니다.
- Elasticsearch는 kNN 검색을 통해 저장 계층 및 클러스터링 엔진 역할을 수행할 수 있습니다.
- 밀도 조사 중심 분류는 거의 모든 연산을 서버 측에서 처리하며, 임베딩 공간의 밀도에 따라 자연스러운 크기의 클러스터를 생성합니다.
significant_text빠르고 해석 가능하며 자동 레이블링과 품질 검사 모두에 효과적입니다.
이 방식이 유용한 경우:
- 타임스탬프가 찍힌 텍스트는 있지만, 레이블이 지정된 학습 데이터 없이 주제를 탐색하고 싶을 때 유용합니다.
- 저장 공간, 벡터 검색, 레이블 지정 및 시간적 연결을 하나의 스택으로 해결하고 싶을 때 적합합니다.
탐색할 확장 기능:
- 다중 기간 클러스터링(주간, 월간 단위 집계)
- 클러스터 할당을 점진적으로 수행하여 실시간 데이터 수집이 가능합니다.
- significant_text 용어를 시드로 활용해 LLM이 클러스터 요약문을 생성합니다.
- 규모가 커지면, 샘플링된 KMeans 중심은 밀도 기반 클러스터링을 위한 웜 스타트 시드로 사용될 수 있어, 조사 단계 비용을 줄여 줍니다.
직접 사용해 보기
자신만의 타임스탬프 기반의 문서 코퍼스를 넣어 보세요. 날짜 정보가 포함된 텍스트 모음이라면 무엇이든 이 파이프라인에서 바로 작동합니다. 전체 노트북과 지원 코드는 제공된 리포지토리에서 확인할 수 있습니다.
- 무료 Elastic Cloud 체험판 시작하기: 단 몇 분 만에
bbq_disk기능이 지원되는 관리형 클러스터를 실행할 수 있습니다. - Elasticsearch Serverless 사용해 보기: 클러스터 관리가 필요 없으며, 자동으로 확장하고, 이 단계별 안내의 모든 기능을 지원합니다.




