[Elasticsearch] 검색 score

HI·2022년 5월 18일
0

검색 score 계산 및 커스텀 방법


BM25

elasticsearch는 version 7이후를 기준으로 BM25로 score계산을 한다.
키워드가 문서에 얼마나 자주 나타나는지, 모든 문서에서 자주 등장하는지 특정 문서에서만 자주 등장하는지를 계산하여 키워드의 중요도에 따른 검색 결과를 뽑아내는데 최적화 되어있다.
BM25는 TF-IDF를 기반으로 설계되었다.

그 외의 알고리즘으로

  • classic
    예전 디폴트 TF-IDF 알고리즘 기반,
    지금은 "similarity": "classic"로 사용할 수 없음. 밑에 나올 script로 적용 가능하다.
  • boolean
    full-text ranking이 필요하지 않을 때 선택할 수 있다. 오직 쿼리 텀이 매치되었는지를 기준으로 scoring한다. score는 boost만큼 부여한다. 그래서 boost를 넣지 않으면 모든 score는 1이다.

1. similarity로 간단하게 score 알고리즘 선택하기

boolean

"NAME" : {
          "type" : "text",
          "term_vector" : "with_positions_offsets",
          "analyzer" : "analyzer",
          "search_analyzer" : "search_analyzer",
          "similarity": "boolean"
        }

Name필드에선 score계산이 되지 않도록 위에 언급한 boolean을 적용하였다.
text타입만 similarity적용이 가능하다. 적용하지 않은 필드는 디폴트 BM25로 계산된다.

[참고] https://www.elastic.co/guide/en/elasticsearch/reference/current/similarity.html

적절도 부스트(Boost) 적용하기

"similarity":"boolean" 를 준 필드는 score가 모두 1이다.
검색 쿼리 작성시 해당 필드에 boost로 score를 계산하려 한다.

{
         "match_phrase": {
           "NAME": {
             "query": "simple rest apis distributed nature",
             "boost": 2
           }
         }
}

꼭 boolean이 아니고 BM25에도 boost 부여 가능하다.
다만, boost를 주면 검색 속도의 문제는 있을 수 있어보인다.

검색 템플리트(seasrch template)로 복잡한 쿼리를 원하는 대로 재활용

POST _scripts/demo_search_template
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "bool": {
          "should": [
            {
              "match": {
                "content": {
                  "query": "{{query_string}}"
                }
              }
            },
            {
              "match": {
                "content": {
                  "query": "{{query_string}}",
                  "operator": "and"
                }
              }
            },
            {
              "match_phrase": {
                "content": {
                  "query": "{{query_string}}",
                  "boost": 2
                }
              }
            }
          ]
        }
      }
    }
  }
}

<template 사용>

GET _search/template
{
    "id": "demo_search_template", 
    "params": {
        "query_string": "simple rest apis distributed nature"
    }
}
  • id에 템플릿 이름을 넣는다.
  • query string에 원하는 검색어를 입력한다.

2. similarity module

similarity module은 setting에 원하는 score계산 similarity를 정의하고 mapping 의 필드에 맞는 similarity를 적용한다.

2.1 제공하는 알고리즘 파라미터 수정하여 정의하기

  • 여러 type의 알고리즘과 각 파라미터를 변경하여 다양한 score계산이 가능하다.

ex) DFR score계산

[setting]

PUT /index
{
  "settings": {
    "index": {
      "similarity": {
        "my_similarity": {
          "type": "DFR",
          "basic_model": "g",
          "after_effect": "l",
          "normalization": "h2",
          "normalization.h2.c": "3.0"
        }
      }
    }
  }
}

[mapping에 원하는 필드에 사용]

PUT /index/_mapping
{
  "properties" : {
    "title" : { 
    "type" : "text", 
    "similarity" : "my_similarity" 
    }
  }
}

DFR

BasicModel, AffterEffect, Normalization 이 3가지 요소로 DFR이 구성된다.
각 파라미터값은 아래를 참고하여 설정가능하다.
[참고] https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-similarity.html#_available_similarities

[elasticsearch relevancy/score/ 검색 작동] https://qbox.io/blog/practical-guide-elasticsearch-scoring-relevancy

2.2 script로 similarity 정의하기

  "similarity": {
        "scripted_tfidf": {  //7이전의 tfidf 로 score계산하려면 script를 이용해 만든다.
          "type": "scripted",
          "script": {
            "source": "double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;"
          }
        },
        "scripted_tf": {   //tf만(필드에서만 빈도) score계산하고 싶으면.
          "type": "scripted",
          "script": {
            "source": "double tf = Math.sqrt(doc.freq); return query.boost * tf"
          }
        }
      },

이렇게 계산할 방법을 painless script로 정의한뒤 mapping의 해당 필드에 정의한 similarity를 설정하면 된다.

[참고] painless similarity
https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-similarity-context.html

 "NAME": {
        "type": "text",
        "term_vector": "with_positions_offsets",
        "analyzer": "analyzer",
        "search_analyzer": "search_analyzer",
        "similarity": "script_tf"
      }

scripted_tfidf는 tf-idf 알고리즘이고 scripted_tf은 이를 변경한 tf로만 score계산하는 방법이다.

  • 이렇게 여러가지 type의 알고리즘의 각 파라미터값을 변경해서 커스텀할 수 있고,
  • painless script를 통해 tfidf, tf 처럼 어떤 파라미터를 받을지, 사용자 지정하여 커스텀 할 수도 있다.

[참고]
similarity로 score를 수정하고, function_score 구현하기
https://ksk-developer.tistory.com/27

function_score : score를 조작할 수 있도록 해준다. 기본적으로 tf-idf와 bm25를 사용한다. 이 디폴트 알고리즘으로 원하는 결과를
받지 않는다면, function_score로 값을 조정해보자!

다양한 방법의 score 조정(function_score, boosting, 스코어 결합)
https://kazaana2009.tistory.com/6

위의 여러가지 score 계산을 통한 정렬 구현 (무신사)
https://velog.io/@choi-yh/%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84-%EB%AC%B4%EC%8B%A0%EC%82%AC-%EA%B2%80%EC%83%89-%EC%B6%94%EC%B2%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%A0%95%EB%A6%AC

https://kazaana2009.tistory.com/6
https://heesutory.tistory.com/30
https://m.blog.naver.com/olpaemi/222003279473

[bm25, tf-idf]
https://velog.io/@mayhan/Elasticsearch-%EC%9C%A0%EC%82%AC%EB%8F%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98

profile
hi

0개의 댓글