디케이 랭커 개요Compatible with Milvus 2.6.x

기존의 벡터 검색에서는 순전히 벡터 유사도, 즉 벡터가 수학적 공간에서 얼마나 가깝게 일치하는지에 따라 결과의 순위가 매겨집니다. 하지만 실제 애플리케이션에서 콘텐츠의 진정한 연관성은 의미론적 유사성 이상의 요소에 의해 좌우되는 경우가 많습니다.

다음과 같은 일상적인 시나리오를 생각해 보세요:

  • 3년 전의 유사한 기사보다 어제 기사가 더 높은 순위를 차지해야 하는 뉴스 검색

  • 30분 거리에 있는 식당보다 5분 거리에 있는 식당을 우선시하는 식당 찾기 기능

  • 검색어와 약간 덜 유사한 제품일지라도 인기 있는 제품의 순위를 높여주는 이커머스 플랫폼

이러한 시나리오는 모두 벡터 유사성과 시간, 거리 또는 인기도와 같은 다른 수치적 요소의 균형을 맞추는 공통된 요구 사항을 공유합니다.

Milvus의 디케이 랭커는 숫자 필드 값을 기반으로 검색 순위를 조정하여 이러한 요구를 해결합니다. 이를 통해 벡터 유사도와 '최신성', '근접성' 또는 데이터의 다른 숫자 속성의 균형을 맞춰 보다 직관적이고 맥락에 맞는 검색 환경을 만들 수 있습니다.

사용 참고 사항

  • 그룹화 검색에는 감쇠 순위를 사용할 수 없습니다.

  • 감쇠 순위에 사용되는 필드는 숫자(INT8, INT16, INT32, INT64, FLOAT, DOUBLE)여야 합니다.

  • 각 붕괴 순위는 하나의 숫자 필드만 사용할 수 있습니다.

  • 시간 단위 일관성: 시간 기반 감쇠 순위를 사용하는 경우 origin, scale, offset 매개변수의 단위는 수집 데이터에 사용된 단위와 일치해야 합니다:

    • 컬렉션에서 타임스탬프를 단위로 저장하는 경우 모든 매개변수에 초를 사용합니다.

    • 컬렉션에서 타임스탬프를 밀리초 단위로 저장하는 경우 모든 매개변수에 밀리초를 사용합니다.

    • 컬렉션이 타임스탬프를 마이크로초 단위로 저장하는 경우, 모든 매개변수에 마이크로초를 사용합니다.

작동 방식

디케이 랭킹은 시간이나 지리적 거리와 같은 숫자 요소를 랭킹 프로세스에 통합하여 기존의 벡터 검색을 향상시킵니다. 전체 프로세스는 다음 단계를 따릅니다:

1단계: 정규화된 유사도 점수 계산

먼저, Milvus는 일관된 비교를 위해 벡터 유사도 점수를 계산하고 정규화합니다:

  • L2JACCARD 거리 메트릭(값이 낮을수록 유사성이 높음을 나타냄)의 경우:

    normalized_score = 1.0 - (2 × arctan(score))/π
    

    거리를 0-1 사이의 유사도 점수로 변환하며, 점수가 높을수록 더 좋습니다.

  • IP, COSINEBM25 메트릭(점수가 높을수록 더 잘 일치함을 의미)의 경우: 점수는 정규화 없이 바로 사용됩니다.

2단계: 부패 점수 계산하기

다음으로 Milvus는 선택한 감쇠 순위자를 사용하여 숫자 필드 값(예: 타임스탬프 또는 거리)을 기반으로 감쇠 점수를 계산합니다:

  • 각 감쇠 순위자는 원시 숫자 값을 0-1 사이의 정규화된 관련성 점수로 변환합니다.

  • 감쇠 점수는 이상적인 지점과의 '거리'를 기준으로 항목이 얼마나 관련성이 있는지를 나타냅니다.

구체적인 계산 공식은 감쇠 랭커 유형에 따라 다릅니다. 감쇠 점수를 계산하는 방법에 대한 자세한 내용은 가우스 감쇠, 지수 감쇠, 선형 감쇠 전용 페이지를 참조하세요.

3단계: 최종 점수 계산

마지막으로 Milvus는 정규화된 유사도 점수와 감쇠 점수를 결합하여 최종 순위 점수를 산출합니다:

final_score = normalized_similarity_score × decay_score

하이브리드 검색(여러 벡터 필드를 결합)의 경우, Milvus는 검색 요청 중 정규화된 최대 유사도 점수를 취합니다:

final_score = max([normalized_score₁, normalized_score₂, ..., normalized_scoreₙ]) × decay_score

예를 들어, 하이브리드 검색에서 연구 논문이 벡터 유사성에서 0.82점, BM25 기반 텍스트 검색에서 0.91점을 받은 경우, Milvus는 0.91점을 기본 유사성 점수로 사용한 후 감쇠 계수를 적용합니다.

실제 감쇠 순위

시간 기반 감쇠를 사용하여 'AI 연구 논문'을 검색하는 실제 시나리오에서 감쇠 순위가 어떻게 적용되는지 살펴봅시다:

이 예에서 감쇠 점수는 시간이 지남에 따라 관련성이 감소하는 방식을 반영하며, 최신 논문은 1.0에 가까운 점수를 받고 오래된 논문은 더 낮은 점수를 받습니다. 이 값은 특정 감쇠 순위자를 사용하여 계산됩니다. 자세한 내용은 올바른 감쇠 순위 선택하기를 참조하세요.

논문

벡터 유사도

정규화된 유사도 점수

게시 날짜

감쇠 점수

최종 점수

최종 순위

Paper A

높음

0.85 (COSINE)

2 주 전

0.80

0.68

2

Paper B

매우 높음

0.92 (COSINE)

6 개월 전

0.45

0.41

3

Paper C

Medium

0.75 (COSINE)

1 일 전

0.98

0.74

1

종이 D

중간 높음

0.76 (COSINE)

3주 전

0.70

0.53

4

감쇠 재순위를 적용하지 않으면 순수 벡터 유사도(0.92)를 기준으로 문서 B가 가장 높은 순위를 차지합니다. 그러나 감쇠 재랭킹을 적용하면:

  • C 문서가 매우 최근(어제 게시됨)이기 때문에 중간 정도의 유사성에도 불구하고 1위로 올라갑니다.

  • 논문 B는 상대적으로 오래되었기 때문에 우수한 유사성에도 불구하고 3위로 떨어집니다.

  • 문서 D는 L2 거리(낮을수록 좋음)를 사용하므로 감쇠를 적용하기 전에 점수가 1.2에서 0.76으로 정규화됩니다.

올바른 감쇠 랭커 선택

Milvus는 각각 특정 사용 사례에 맞게 설계된 gauss, exp, linear 등 다양한 디케이 랭커를 제공합니다:

디케이 랭커

특성

이상적인 사용 사례

예시 시나리오

가우시안 (gauss)

적당히 확장되는 자연스러운 느낌의 점진적 감쇠

  • 균형 잡힌 결과가 필요한 일반 검색

  • 사용자가 직관적으로 거리감을 느낄 수 있는 애플리케이션

  • 적당한 거리가 결과에 심각한 불이익을 주지 않아야 하는 경우

레스토랑 검색에서 3km 떨어진 양질의 장소는 인근 옵션보다 순위가 낮지만 여전히 검색할 수 있습니다.

지수 (exp)

처음에는 급격히 감소하지만 긴 꼬리를 유지합니다.

  • 최신성이 중요한 뉴스 피드

  • 신선한 콘텐츠가 지배적이어야 하는 소셜 미디어

  • 가까운 항목을 선호하지만 예외적으로 먼 항목도 계속 표시되어야 하는 경우

뉴스 앱에서 어제 기사의 순위는 일주일 전 콘텐츠보다 훨씬 높지만, 관련성이 높은 오래된 기사가 여전히 표시될 수 있습니다.

선형 (linear)

명확한 컷오프를 통한 일관되고 예측 가능한 감소

  • 자연스러운 경계가 있는 애플리케이션

  • 거리 제한이 있는 서비스

  • 만료일 또는 명확한 임계값이 있는 콘텐츠

이벤트 파인더에서 2주 후 이후의 이벤트는 전혀 표시되지 않습니다.

각 감쇠 랭커가 점수를 계산하는 방법과 구체적인 감쇠 패턴에 대한 자세한 내용은 전용 문서를 참조하세요:

구현 예시

디케이 랭커는 Milvus의 표준 벡터 검색과 하이브리드 검색 작업 모두에 적용할 수 있습니다. 다음은 이 기능을 구현하기 위한 주요 코드 스니펫입니다.

감쇠 함수를 사용하기 전에 먼저 감쇠 계산에 사용할 적절한 숫자 필드(타임스탬프, 거리 등)가 포함된 컬렉션을 만들어야 합니다. 컬렉션 설정, 스키마 정의 및 데이터 삽입을 포함한 전체 작업 예제는 튜토리얼을 참조하세요: 밀버스에서 시간 기반 랭킹 구현하기를 참조하세요.

감쇠 순위 생성하기

감쇠 랭킹을 구현하려면 먼저 적절한 구성으로 Function 객체를 정의합니다:

from pymilvus import Function, FunctionType

# Create a decay function for timestamp-based decay
# Note: All time parameters must use the same unit as your collection data
decay_ranker = Function(
    name="time_decay",                  # Function identifier
    input_field_names=["timestamp"],    # Numeric field to use for decay
    function_type=FunctionType.RERANK,  # Must be set to RERANK for decay rankers
    params={
        "reranker": "decay",            # Specify decay reranker. Must be "decay"
        "function": "gauss",            # Choose decay function type: "gauss", "exp", or "linear"
        "origin": int(datetime.datetime(2025, 1, 15).timestamp()),    # Reference point (seconds)
        "scale": 7 * 24 * 60 * 60,      # 7 days in seconds (must match collection data unit)
        "offset": 24 * 60 * 60,         # 1 day no-decay zone (must match collection data unit)
        "decay": 0.5                    # Half score at scale distance
    }
)
import io.milvus.v2.service.vector.request.ranker.DecayRanker;

import java.time.ZoneId;
import java.time.ZonedDateTime;

ZonedDateTime zdt = ZonedDateTime.of(2025, 1, 25, 0, 0, 0, 0, ZoneId.systemDefault());

DecayRanker ranker = DecayRanker.builder()
        .name("time_decay")
        .inputFieldNames(Collections.singletonList("timestamp"))
        .function("gauss")
        .origin(zdt.toInstant().toEpochMilli())
        .scale(7 * 24 * 60 * 60)
        .offset(24 * 60 * 60)
        .decay(0.5)
        .build();


import {FunctionType } from "@zilliz/milvus2-sdk-node";

const decayRanker = {
  name: "time_decay",
  input_field_names: ["timestamp"],
  function_type: FunctionType.RERANK,
  params: {
    reranker: "decay",
    function: "gauss",
    origin: new Date(2025, 1, 15).getTime(),
    scale: 7 * 24 * 60 * 60,
    offset: 24 * 60 * 60,
    decay: 0.5,
  },
};

// go
# restful

파라미터

필수?

설명

값/예시

name

검색을 실행할 때 사용되는 함수의 식별자입니다. 사용 사례와 관련된 설명이 포함된 이름을 선택하세요.

"time_decay"

input_field_names

감점 점수 계산을 위한 숫자 필드입니다. 부패도 계산에 사용할 데이터 속성을 결정합니다(예: 시간 기반 부패도의 경우 타임스탬프, 위치 기반 부패도의 경우 좌표).

컬렉션에 관련 숫자 값이 포함된 필드여야 합니다. INT8/16/32/64, FLOAT, DOUBLE을 지원합니다.

["timestamp"]

function_type

생성되는 함수 유형을 지정합니다.

모든 디케이 랭커에 대해 RERANK 로 설정해야 합니다.

FunctionType.RERANK

params.reranker

Yes

사용할 리랭크 방법을 지정합니다.

부패 순위 기능을 사용하려면 "decay" 로 설정해야 합니다.

"decay"

params.function

Yes

적용할 수학적 감쇠 순위자를 지정합니다. 관련성 감소의 곡선 모양을 결정합니다.

적절한 기능을 선택하는 방법에 대한 지침은 올바른 감쇠 순위 선택 섹션을 참조하세요.

"gauss", "exp", 또는 "linear"

params.origin

감쇠 점수를 계산하는 기준점입니다. 이 값에 있는 항목은 최대 연관성 점수를 받습니다.

시간 기반 감쇠의 경우 시간 단위가 수집 데이터와 일치해야 합니다.

  • 타임스탬프의 경우: 현재 시간(예: int(time.time()))

  • 지리적 위치의 경우: 사용자의 현재 좌표

params.scale

관련성이 decay 값으로 떨어지는 거리 또는 시간. 관련성 감소 속도를 제어합니다.

시간 기반 쇠퇴의 경우 시간 단위가 수집 데이터와 일치해야 합니다.

값이 클수록 관련성이 점진적으로 감소하고 값이 작을수록 가파르게 감소합니다.

  • 시간의 경우: 기간(초)(예: 7일 동안 7 * 24 * 60 * 60 )

  • 거리의 경우: 미터(예: 5km의 경우 5000 )

params.offset

No

항목이 만점을 유지하는 origin 주위에 "감쇠 없음 영역"을 만듭니다(감쇠 점수 = 1.0).

시간 기반 감쇠의 경우 시간 단위가 수집 데이터와 일치해야 합니다.

이 범위( origin ) 내의 항목은 최대 관련성을 유지합니다.

  • 시간의 경우: 기간(초)(예: 1일의 경우 24 * 60 * 60 )

  • 거리의 경우: 미터(예: 500m의 경우 500 )

params.decay

아니요

scale 거리의 점수 값으로 커브의 가파른 정도를 제어합니다. 값이 낮을수록 가파른 감소 곡선을 만들고 값이 높을수록 완만한 감소 곡선을 만듭니다.

0에서 1 사이여야 합니다.

0.5 (기본값)

감쇠 순위자를 정의한 후 ranker 파라미터에 전달하여 검색 작업 중에 적용할 수 있습니다:

# Use the decay function in standard vector search
results = milvus_client.search(
    collection_name,
    data=[your_query_vector], # Replace with your query vector
    anns_field="vector_field",
    limit=10,
    output_fields=["document", "timestamp"],  # Include the decay field in outputs to see values
    ranker=decay_ranker,                      # Apply the decay ranker here
    consistency_level="Strong"
)
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.response.SearchResp;
import io.milvus.v2.service.vector.request.data.EmbeddedText;

SearchReq searchReq = SearchReq.builder()
        .collectionName(COLLECTION_NAME)
        .data(Collections.singletonList(new EmbeddedText("search query")))
        .annsField("vector_field")
        .limit(10)
        .outputFields(Arrays.asList("document", "timestamp"))
        .functionScore(FunctionScore.builder()
                .addFunction(ranker)
                .build())
        .build();
SearchResp searchResp = client.search(searchReq);
const result = await milvusClient.search({
  collection_name: "collection_name",
  data: [your_query_vector], // Replace with your query vector
  anns_field: "dense",
  limit: 10,
  output_fields: ["document", "timestamp"],
  rerank: ranker,
  consistency_level: "Strong",
});
// go
# restful