본문 바로가기
WIKI 기술 지식 베이스

Advanced LTR functionality

원문 보기 위키 갱신

출처: 문서

본문

고급 LTR 기능

OpenSearch Learning to Rank(LTR)는 추가 기능을 제공해요. 이러한 기능을 다루기 전에 OpenSearch LTR에 대한 기초적인 이해를 먼저 갖추는 것이 좋아요.

재사용 가능한 피처

피처 빌드는 피처 목록을 업로드하는 작업을 포함해요. 여러 세트에 걸쳐 공통 피처를 반복해서 정의하지 않으려면 재사용 가능한 피처 라이브러리를 유지 관리할 수 있어요.

예를 들어, 피처 세트에서 title 필드 쿼리를 자주 사용한다면 feature API를 사용해 재사용 가능한 title 쿼리를 만들 수 있어요:

    POST _ltr/_feature/titleSearch
    {
        "feature":
        {
            "params": [
            "keywords"
            ],
            "template": {
            "match": {
                "title": "{{keywords}}"
            }
            }
        }
    }

{% include copy-curl.html %}

일반적인 CRUD 연산이 적용되므로 다음 연산을 사용해 피처를 삭제할 수 있어요:

DELETE _ltr/_feature/titleSearch

{% include copy-curl.html %}

개별 피처를 가져오려면 다음 요청을 사용할 수 있어요:

GET _ltr/_feature/titleSearch

{% include copy-curl.html %}

이름 접두사로 필터링된 모든 피처의 목록을 보려면 다음 요청을 사용할 수 있어요:

GET /_ltr/_feature?prefix=t

{% include copy-curl.html %}

피처 세트를 생성하거나 업데이트하려면 다음 요청을 사용해 titleSearch 피처를 참조할 수 있어요:

POST /_ltr/_featureset/my_featureset/_addfeatures/titleSearch

{% include copy-curl.html %}

이렇게 하면 titleSearch 피처가 my_featureset 피처 세트 내의 다음 순서 위치에 추가돼요.

파생 피처

파생 피처는 다른 피처를 기반으로 만들어지는 피처예요. 이러한 피처는 Lucene expressions으로 표현할 수 있으며 "template_language": "derived_expression"으로 식별돼요.

또한 파생 피처는 피처 세트 생성에서 설명한 것처럼 Number 타입의 쿼리 시점 변수를 받을 수 있어요.

스크립트 피처

스크립트 피처는 파생 피처의 한 유형이에요. 이러한 피처는 feature_vector에 접근할 수 있지만, Lucene expressions이 아니라 native 또는 Painless OpenSearch 스크립트로 구현돼요.

이러한 피처를 식별하려면 "template_language": "script_feature""를 설정해요. 커스텀 스크립트는 피처 세트 생성에서 설명한 것처럼 Java Map을 통해 feature_vector에 접근할 수 있어요.

스크립트 기반 피처는 OpenSearch 클러스터의 성능에 영향을 줄 수 있으므로, 고성능 쿼리가 필요하다면 사용하지 않는 것이 좋아요. {: .warning}

스크립트 피처 파라미터

스크립트 피처는 LTR 컨텍스트 안에서의 native 또는 Painless 스크립트예요. 자세한 내용은 Painless 스크립팅 언어를 참조하세요. 이러한 스크립트 피처는 OpenSearch 스크립트 문서에서 설명하는 것처럼 파라미터를 받을 수 있어요. LTR 스크립트로 작업할 때는 파라미터 값과 이름을 재정의할 수 있어요. 파라미터화 우선순위는 오름차순으로 다음과 같아요:

  • 파라미터 이름과 값이 LTR 스크립트 파라미터를 거치지 않고 소스 스크립트에 직접 전달돼요. 이러한 파라미터는 쿼리 시점에 설정할 수 없어요.
  • 파라미터 이름이 sltr 쿼리와 소스 스크립트 양쪽에 모두 전달되므로, 쿼리 시점에 스크립트 파라미터 값을 재정의할 수 있어요.
  • LTR 스크립트 파라미터 이름에서 native 스크립트 파라미터 이름으로의 간접 참조를 사용하면, 기반이 되는 native 스크립트의 파라미터 이름과 다른 이름을 LTR 피처 정의에서 사용할 수 있어요. 이 덕분에 LTR 컨텍스트 안에서 스크립트를 정의하고 사용하는 방식에 유연성이 생겨요.

예를 들어, title 매치와 그 밖의 조정 가능한 요소를 모두 고려하면서 검색 결과에서 영화 순위를 커스터마이징할 수 있게 설정하려면 다음 요청을 사용할 수 있어요:

POST _ltr/_featureset/more_movie_features
{
  "featureset": {
    "features": [
      {
        "name": "title_query",
        "params": [
          "keywords"
        ],
        "template_language": "mustache",
        "template": {
          "match": {
            "title": "{{keywords}}"
          }
        }
      },
      {
        "name": "custom_title_query_boost",
        "params": [
          "some_multiplier",
          "ltr_param_foo"
        ],
        "template_language": "script_feature",
        "template": {
          "lang": "painless",
          "source": "(long)params.default_param * params.feature_vector.get('title_query') * (long)params.some_multiplier * (long) params.param_foo",
          "params": {
            "default_param": 10,
            "some_multiplier": "some_multiplier",
            "extra_script_params": {
              "ltr_param_foo": "param_foo"
            }
          }
        }
      }
    ]
  }
}

{% include copy-curl.html %}

다중 피처 스토어

피처 스토어는 단일 인덱스와 캐시로 뒷받침되는 피처, 피처 세트, 모델을 포함하는 하나의 독립적인 LTR 시스템에 해당해요. 피처 스토어는 일반적으로 Wikipedia나 Wiktionary 같은 단일 검색 문제 또는 애플리케이션을 나타내요. OpenSearch 클러스터에서 여러 피처 스토어를 사용하려면 제공되는 API를 사용해 생성하고 관리할 수 있어요.

예를 들어, 먼저 wikipedia 피처 스토어를 생성해요:

PUT _ltr/wikipedia

{% include copy-curl.html %}

그런 다음 해당 스토어에 피처 세트를 생성해요:

POST _ltr/wikipedia/_featureset/attempt_1
{
  "featureset": {
    "features": [
      {
        "name": "title_query",
        "params": [
          "keywords"
        ],
        "template_language": "mustache",
        "template": {
          "match": {
            "title": "{{keywords}}"
          }
        }
      }
    ]
  }
}

{% include copy-curl.html %}

피처를 로깅할 때는 다음 예제 구조에서 보듯이 쿼리의 sltr 섹션에 있는 store 파라미터로 피처 스토어를 지정할 수 있어요. store 파라미터를 제공하지 않으면 기본 스토어를 사용해 피처 세트를 조회해요.

{
  "sltr": {
    "_name": "logged_featureset",
    "featureset": "attempt_1",
    "store": "wikipedia",
    "params": {
      "keywords": "star"
    }
  }
}

{% include copy-curl.html %}

피처 세트를 삭제하려면 다음 연산을 사용할 수 있어요:

DELETE _ltr/wikipedia/_featureset/attempt_1

{% include copy-curl.html %}

모델 캐싱

Model Caching 플러그인은 컴파일된 모델을 위한 내부 캐시를 사용해요. 모델이 다시 컴파일되도록 강제하려면 피처 스토어의 캐시를 지우면 돼요:

POST /_ltr/_clearcache

{% include copy-curl.html %}

특정 스토어에 대한 클러스터 전체 캐시 통계를 얻으려면 다음 요청을 사용해요:

GET /_ltr/_cachestats

{% include copy-curl.html %}

다음 노드 설정을 사용하면 내부 캐시의 특성을 제어할 수 있어요:

# limit cache usage to 12 megabytes (defaults to 10mb or max_heap/10 if lower) ltr.caches.max_mem: 12mb
# Evict cache entries 10 minutes after insertion (defaults to 1hour, set to 0 to disable) ltr.caches.expire_after_write: 10m
# Evict cache entries 10 minutes after access (defaults to 1hour, set to 0 to disable) ltr.caches.expire_after_read: 10m

{% include copy.html %}

추가 로깅

피처 로깅에서 설명한 것처럼, 로깅 확장을 사용해 각 문서와 함께 피처 값을 반환할 수 있어요. native 스크립트의 경우 로깅된 피처와 함께 추가적인 임의의 정보도 반환할 수 있어요.

native 스크립트의 경우 extra_logging 파라미터가 스크립트 파라미터에 주입돼요. 이 파라미터는 Supplier<Map<String,Object>>로, 로깅 fetch 단계 동안에만 null이 아닌 Map<String,Object>을 제공해요. 이 맵에 추가하는 값은 로깅된 피처와 함께 반환돼요:

{
    @Override
    public double runAsDouble() {
    ...
        Map<String,Object> extraLoggingMap = ((Supplier<Map<String,Object>>) getParams().get("extra_logging")).get();
        if (extraLoggingMap != null) {
            extraLoggingMap.put("extra_float", 10.0f);
            extraLoggingMap.put("extra_string", "additional_info");
        }
    ...
    }
}

{% include copy-curl.html %}

추가 로깅 맵에 접근하면 로깅된 피처와 함께 추가 항목으로 반환돼요. 추가 로깅 정보를 포함한 로깅된 피처의 형식은 다음 예제와 비슷하게 나타나요:

  {
    "log_entry1": [
        {
            "name": "title_query",
            "value": 9.510193
        },
        {
            "name": "body_query",
            "value": 10.7808075
        },
        {
            "name": "user_rating",
            "value": 7.8
        },
        {
            "name": "extra_logging",
            "value": {
                "extra_float": 10.0,
                "extra_string": "additional_info"
            }
        }
    ]
}

{% include copy-curl.html %}

피처 점수 캐싱

기본적으로 Feature Score Caching 플러그인은 모델 추론과 피처 점수 로깅 두 가지 모두에 대해 피처 점수를 계산해요. 예를 들어, 상위 100개 문서를 리스코어하고 피처 점수와 함께 상위 10개 문서를 반환하는 쿼리를 작성하면, 플러그인은 모델 추론을 위해 상위 100개 문서의 피처 점수를 계산한 다음, 상위 10개 문서의 점수를 계산해 로깅해요.

다음 쿼리는 이러한 동작을 보여줘요:

POST tmdb/_search
{
    "size": 10,
    "query": {
        "match": {
            "_all": "rambo"
        }
    },
    "rescore": {
        "window_size" : 100,
        "query": {
            "rescore_query": {
                "sltr": {
                    "params": {
                        "keywords": "rambo"
                    },
                    "model": "my_model"
                }
            }
        }
    },
    "ext": {
        "ltr_log": {
            "log_specs": {
                "name": "log_entry1",
                "rescore_index": 0
            }
        }
    }
}

{% include copy-curl.html %}

일부 환경에서는 모델 추론용 피처 점수를 캐시에 저장해 두고 로깅에 재사용하는 편이 더 빠를 수 있어요. 피처 점수 캐싱을 활성화하려면 다음 예제에서 보듯이 피처 점수 로깅의 대상이 되는 sltr 쿼리에 cache: "true" 플래그를 추가해요:

{
   "sltr":{
      "cache":true,
      "params":{
         "keywords":"rambo"
      },
      "model":"my_model"
   }
}

{% include copy-curl.html %}

통계

Stats API를 사용하면 플러그인의 전체 상태와 통계를 조회할 수 있어요. 이렇게 하려면 다음 요청을 보내요:

GET /_plugins/_ltr/stats

{% include copy-curl.html %}

응답에는 클러스터 정보, 구성된 스토어, 다양한 플러그인 구성 요소의 캐시 통계, 요청 횟수가 포함돼요:

{
   "_nodes":{
      "total":1,
      "successful":1,
      "failed":0
   },
   "cluster_name":"es-cluster",
   "stores":{
      "_default_":{
         "model_count":10,
         "featureset_count":1,
         "feature_count":0,
         "status":"green"
      }
   },
   "status":"green",
   "nodes":{
      "2QtMvxMvRoOTymAsoQbxhw":{
         "cache":{
            "feature":{
               "eviction_count":0,
               "miss_count":0,
               "hit_count":0,
               "entry_count":0,
               "memory_usage_in_bytes":0
            },
            "featureset":{
               "eviction_count":0,
               "miss_count":0,
               "hit_count":0,
               "entry_count":0,
               "memory_usage_in_bytes":0
            },
            "model":{
               "eviction_count":0,
               "miss_count":0,
               "hit_count":0,
               "entry_count":0,
               "memory_usage_in_bytes":0
            }
         },
         "request_total_count": 0,
         "request_error_count": 0
      }
   }
}

{% include copy-curl.html %}

다음 요청을 보내면 필터를 사용해 단일 통계를 조회할 수 있어요:

GET /_plugins/_ltr/stats/{stat}

{% include copy-curl.html %}

다음 표에서 사용 가능한 stat 값을 확인할 수 있어요.

Stat 설명
stores 구성된 피처 스토어에 대한 정보
status 플러그인의 전체 상태
cache 다양한 플러그인 구성 요소(피처, 피처 세트, 모델)에 대한 노드별 캐시 통계
request_total_count 실행된 LTR 쿼리 수의 노드별 카운터
request_error_count 실패한 LTR 쿼리 수의 노드별 카운터

다음 요청을 보내면 정보를 클러스터 내 단일 노드로 한정할 수 있어요:

GET /_plugins/_ltr/{nodeId}/stats
GET /_plugins/_ltr/{nodeId}/stats/{stat}

TermStat 쿼리

실험적 기능 {: .label .label-red }

TermStatQuery는 실험 단계에 있으며, 코드가 발전함에 따라 도메인 특화 언어(DSL)가 변경될 수 있어요. 안정적인 용어 통계 접근이 필요하다면 [ExplorerQuery]{.title-ref}를 참고하세요.

TermStatQuery는 기존의 ExplorerQuery를 재설계한 버전이에요. 용어를 지정하는 더 명확한 방법을 제공하고, 실험을 위한 유연성도 더 높아요. 이 쿼리는 [ExplorerQuery]{.title-ref}와 동일한 데이터를 제공하지만, 원하는 데이터를 가져오기 위해 커스텀 Lucene 표현식을 지정할 수 있어요. 다음 예시를 참고하세요:

POST tmdb/_search
{
    "query": {
        "term_stat": {
            "expr": "df",
            "aggr": "max",
            "terms": ["rambo", "rocky"],
            "fields": ["title"]
        }
    }
}

{% include copy-curl.html %}

expr 파라미터는 Lucene 표현식을 지정하는 데 사용돼요. 이 표현식은 용어 단위로 실행돼요. 표현식은 단순한 통계 타입이거나 (tf * idf) / 2처럼 여러 통계 타입을 조합한 커스텀 수식일 수 있어요. Lucene 표현식 컨텍스트에서 사용할 수 있는 통계 타입은 다음 표에 정리되어 있어요.

타입 설명
df 용어의 직접 문서 빈도예요. 예를 들어, rambo가 여러 문서에 걸쳐 세 개의 영화 제목에 등장한다면 값은 3이 돼요.
idf log((NUM_DOCS+1)/(raw_df+1)) + 1 수식을 사용해 계산한 역문서 빈도(IDF)예요.
tf 문서에서의 용어 빈도예요. 예를 들어, rambo가 같은 문서의 영화 시놉시스에 세 번 등장한다면 값은 3이 돼요.
tp 문서에서의 용어 위치예요. 하나의 용어에 대해 여러 위치가 반환될 수 있으므로 pos_aggr 파라미터의 동작을 확인해 보는 게 좋아요.
ttf 인덱스 전체에서 용어의 총 용어 빈도예요. 예를 들어, rambo가 모든 문서의 overview 필드에서 총 100번 언급된다면 값은 100이 돼요.

aggr 파라미터는 expr에서 수집한 통계에 적용할 집계 타입을 지정해요. 예를 들어, rambo와 rocky 용어를 지정하면 쿼리가 두 용어 모두에 대한 통계를 수집해요. 단 하나의 값만 반환할 수 있기 때문에 어떤 통계 계산을 사용할지 결정해야 해요. 사용할 수 있는 집계 타입은 min, max, avg, sum, stddev예요. 쿼리는 다음 카운트도 함께 제공해요: matches(현재 문서에서 매칭된 용어 수)와 unique(쿼리에 전달된 고유 용어 수).

terms 파라미터는 통계를 수집하려는 용어들의 배열을 지정해요. 단일 용어만 지원되며, 구문(phrase)이나 span 쿼리는 지원하지 않아요. 필드가 토크나이즈되어 있다면 배열 안의 하나의 문자열에 여러 용어를 담아 전달할 수 있어요.

fields 파라미터는 지정된 terms를 확인할 필드를 지정해요. analyzer를 지정하지 않으면 각 필드에 설정된 search_analyzer가 사용돼요.

선택적 파라미터는 다음 표에 정리되어 있어요.

타입 설명
analyzer 지정하면 각 필드에 설정된 search_analyzer 대신 이 애널라이저가 사용돼요.
pos_aggr 각 용어는 여러 위치를 가질 수 있기 때문에, 이 파라미터로 용어 위치에 적용할 집계를 지정할 수 있어요. aggr 파라미터와 동일한 값을 지원하며 기본값은 avg예요.

스크립트 주입

스크립트 주입은 스크립팅 컨텍스트에 용어 통계를 주입할 수 있는 기능이에요. ScriptFeatures를 사용할 때는 terms, fields, analyzer 파라미터를 담은 term_stat 객체를 전달할 수 있어요. 그러면 termStats라는 이름으로 주입된 변수를 통해 커스텀 스크립트 안에서 원시 값에 접근할 수 있어요. 기반이 되는 모든 데이터에 접근할 수 있기 때문에 고급 피처 엔지니어링이 가능해져요.

매칭된 토큰 수에 접근하려면 [params.matchCount.get]{.title-ref}을 사용하세요. 고유 토큰 수에 접근하려면 [params.uniqueTerms]{.title-ref}를 사용하세요.

term_stat 파라미터를 스크립트 정의에 하드코딩할 수도 있고, 쿼리 시점에 설정되도록 파라미터를 전달할 수도 있어요. 예를 들어, 다음 예시 쿼리는 하드코딩된 term_stat 파라미터를 사용하는 스크립트 피처로 피처 세트를 정의해요:

POST _ltr/_featureset/test
{
   "featureset": {
     "features": [
       {
         "name": "injection",
         "template_language": "script_feature",
         "template": {
           "lang": "painless",
           "source": "params.termStats['df'].size()",
           "params": {
             "term_stat": {
                "analyzer": "!standard",
                "terms": ["rambo rocky"],
                "fields": ["overview"]
             }
           }
         }
       }
     ]
   }
}

{% include copy-curl.html %}

애널라이저 이름을 로컬에서 지정할 때는 반드시 느낌표(!)를 접두사로 붙여야 해요. 그렇지 않으면 파라미터 조회 값으로 처리돼요. {: .note}

파라미터 조회를 설정하려면 값을 가져올 파라미터의 이름을 전달하면 돼요. 다음 예시 요청을 참고하세요:

POST _ltr/_featureset/test
{
   "featureset": {
     "features": [
       {
         "name": "injection",
         "template_language": "script_feature",
         "template": {
           "lang": "painless",
           "source": "params.termStats['df'].size()",
           "params": {
             "term_stat": {
                "analyzer": "analyzerParam",
                "terms": "termsParam",
                "fields": "fieldsParam"
             }
           }
         }
       }
     ]
   }
}

{% include copy-curl.html %}

또는 term_stat 파라미터를 쿼리 시점 파라미터로 전달할 수도 있어요. 다음 요청을 참고하세요:

POST tmdb/_search
{
    "query": {
        "bool": {
            "filter": [
                {
                    "terms": {
                        "_id": ["7555", "1370", "1369"]
                    }
                },
                {
                    "sltr": {
                        "_name": "logged_featureset",
                        "featureset": "test",
                        "params": {
                          "analyzerParam": "standard",
                          "termsParam": ["troutman"],
                          "fieldsParam": ["overview"]
                        }
                }}
            ]
        }
    },
    "ext": {
        "ltr_log": {
            "log_specs": {
                "name": "log_entry1",
                "named_query": "logged_featureset"
            }
        }
    }
}

{% include copy-curl.html %}

더 알아보기 (Learn more)