Blog

2018.08.21 - 번역 - Made To Measure: How to Use the Ranking Evaluation API in Elasticsearch ...

drscg 2019. 1. 7. 14:33

The Ranking Evaluation API that's been added to Elasticsearch is a new, experimental REST API that lets you quickly evaluate the quality of search results for a typical query set. This can be useful either while developing new search queries, incremental improvements of the query templates of an an existing system, or as a basic monitoring tool to detect changes in the search quality of a system in production.

Elasticsearch에 새로 추가된 Ranking Evaluation API는 일반적인 query set에 대한 품질을 빠르게 평가할 수 있는 실험적인 REST API이다. 이것은 새로운 search query 개발, 기존 시스템의 query template의 증분 개선, 제품에서 시스템의 search 품질의 변화를 감지할 수 있는 기본적인 모니터링 tool로 유용하다.

A recent blog post about relevance tuning already gave some initial motivation for why Ranking Evaluation is useful and gave a first glimpse at the new API. This post will show you how to use the new Ranking Evaluation API in Elasticsearch to measure search performance on real-life data and will show some common usage examples. The goal is to give you some hands-on experience using the API to initially develop a simple search query and measure its ranking quality. Then you’ll gradually improve it, learning about some of the details of the new API along the way.

이미 최근의 relevance tuning에 대한 게시물에서 Ranking Evaluation이 유용한 이유에 대한 몇 가지 기초적인 동기를 부여했고, 새로운 API를 처음 보았다. 이 게시물에서 Elasticsearch의 새로운 Ranking Evaluation API를 사용하여 실제 data에서 search 성능을 측정하는 방법을 알아보고 몇 가지 일반적인 사용 예를 살펴보자. 처음에 간단한 search query를 개발하고, 그것의 ranking 품질을 측정하여 API를 사용하는 경험을 갖는 것이 목표이다. 그 다음에 새로운 API의 세부 사항에 대해 단계적으로 배우면서, 점차적으로 그것을 개선해 나갈 것이다.

Setting Up the Demo Project

Before we start diving into the API itself, let us take a quick look at a demo project that will help you setting up the dataset we are going to play with. We will use a small subset of documents from the English Wikipedia that Wikimedia provides publicly for download. If you are interested in how to index data from Wikipedia in general, take a look at this Loading Wikipedia's Search Index for Testing blog post. For now, we keep things simple. I have already prepared the scripts that will create the index you are going to use. Let’s start by cloning the project itself:

API 자체에 대해 알아보기 전에, 테스트할 data 집합을 설정하는데 도움이 될 demo project을 간략히 살펴보자. 우리는 Wikipedia가 download를 위해 공개적으로 제공하는 영문 Wikipedia의 작은 일부 document를 사용할 것이다. Wikipedia로 부터 data를 index하는 방법에 관심이 있다면, 테스트를 위한 Wikipedia의 Search Index Loading이라는 게시물을 살펴보자. 지금은 간단하게 하자. 이미 사용할 index를 생성할 script를 준비해 뒀다. project 자체를 clone하여 시작하자.

git clone https://github.com/cbuescher/rankEvalDemo.git

Inside the main folder you can find a directory called bulkdata which contains the documents that we are going to index. In order to index this data, start a local instance of Elasticsearch 6.3 now. Note: For other versions, please check if there is a corresponding branch in the demo project. API syntax might change, since this API is still experimental at the time of this writing, so you need to adapt the following examples if necessary.

main folder 안에, index할 document를 포함하고 있는 bulkdata 라는 directory가 있다. 이 data를 index하기 위해, local에서 Elasticsearch를 시작하자. 다른 버전을 사용하려면, demo project에 해당 branch가 있는지 확인하자. 이 API는 이 글을 쓰고 있는 시점에 여전히 실험 중이므로, API 문법은 변경될 수 있으니, 필요하다면, 다음 예제를 수정해야 한다.

Next, we are going to create an index named "enwiki_rank" that contains the demo documents. Assuming Elasticsearch is reachable at the default of localhost:9200 (otherwise you’ll need to adapt the script slightly), run the ./setup.sh script, which will create a new index called “enwiki_rank” and then index the above mentioned bulk files, which should usually take just a few seconds.

다음으로, demo document를 포함하고 있는 "enwiki_rank" 라는 index를 생성하자. Elasticsearch가 기본값인 localhost:9200 이라고 가정(그렇지 않으면, script를 약간 수정해야 한다)하면, ./setup.sh script를 실행하여, “enwiki_rank”라는 새로운 index를 생성하고, 위에서 언급한 bulk file을 index하자. 일반적으로 몇 초 정도 소요된다.

Let’s take a quick look at the data by using the Kibana Console:

Kibana Console을 이용하여, data를 잠깐 살펴보자.

GET /enwiki_rank/_count

Response:

response는 다음과 같다.

{
  "count": 1882    
}

So we have about 1900 documents in the index to play with. A quick inspection of the mapping shows that there is quite a bit of information available for each Wikipedia document:

index에는 약 1,900개의 document가 있다. mapping을 빠르게 확인해 보면, 각 Wikipedia document에서 활용할 수 있는 상당히 많다는 것을 알 수 있다.

GET /enwiki_rank/page/_mapping

Besides the text content field itself (aptly called “text”), we find fields like “title”, “opening_text” and “category” in various analysis variants, as well as several additional metadata fields like incoming and outgoing links, redirects or popularity measures. We don’t need to go through all of them now, instead take a quick look at one example document (the page about John F. Kennedy) to get an idea about a typical document:

text content field외에도, 다양한 분석 변수에서 “title”, “opening_text”, “category” 뿐만 아니라 들어오고 나가는 links, redirections, popularity measures 같은 여러가지 추가 metadata field를 볼 수 있다. 지금 그들 모두를 검토할 필요는 없고, 대신 일반적인 document에 대한 idea를 얻기 위해, 하나의 예제 document(John F. Kennedy에 대한 page)만 빠르게 살펴보자.

GET /enwiki_rank/page/5119376

The returned document is huge, but at least take a look at some of the content fields like “title”, “text”, “opening_text” and “redirect” (which lists titles of pages redirecting to this one).

반환되는 document는 거대하지만, 최소한 “title”, “text”, “opening_text”, “redirect” 같은 일부 content field를 살펴보자.

Creating a Simple Query

Let’s start some searching now. Imagine your task is to develop some general purpose search frontend for this kind of data. Currently, you have little idea of how the documents are structured and which kind of queries to translate your users input to. You start your journey by imagining a typical user who wants to find information about president Kennedy, but unfortunately types “JFK” into the search box. You now have to make a choice how your search system translates this into an Elasticsearch query. Since we don’t know any better yet, let’s start simple by querying the “all” field (the catch-all copy-to field in the Wikipedia mappings. Note: this is NOT the Elasticsearch “_all” field, which is disabled by default in the mappings as of version Elasticsearch 6.0). Because documents are very large, and at this point we only need their titles, let us use source filtering to select only the title field:

이제 몇 가지 search를 시작해 보자. 여러분의 일이 이런 종류의 data에서 어떤 일반적인 목적의 frontend search를 개발하는 것이라 가정해 보자. 현재, 여러분은 document가 어떻게 구성되어 있는지, 사용자의 입력을 처리할 query에 대해 거의 알지 못한다. Kennedy 대통령에 대한 정보를 찾으려 하지만, 불행하게도, search box에 "JFK" 라고 입력하는 일반적인 사용자를 가정하고 시작하는 것이다. 이제 여러분은 search system이 이것을 Elasticsearch query로 바꾸는 방법을 선택해야 한다. 아직 잘 모르기 때문에, 간단하게 "all" field(Wikipedia mapping에서 catch-all copy-to field, Note: 이 field는 Elasticsearch의 "_all" field가 아니며, 이 field는 Elasticsearch 6.0 을 기준으로 mapping에서 기본적으로 비활성화isabled by default기되어 있다)에 대해 query하는 것으로 시작하자. document가 매우 크고 그 title만 필요하기 때문에, title filed만 선택하도록 source filtering을 사용하자.

GET enwiki_rank/page/_search?filter_path=hits.hits._source.title
{
  "query" : {
    "match" : {
      "all" : "JFK"
    }
  }
}

The response might be a bit disappointing. It contains many documents about the JFK airport and related locations, a football club, a Boston train station, but only the "JFK (disambiguation)" page and the page called "Reactions to the assassination of John F. Kennedy" are somehow related to Kennedy, the president. Maybe we can try to be more specific by searching on the “title”, “opening_text” and a fuzzy version of the redirect page title called “redirect.title.near_match”:

response는 다소 실망스러울 수 있다. JFK 공항, 관련된 장소, 축구정, Boston 기차역 등에 대한 많은 document를 포함하고 있지만, "JFK (disambiguation)" page와 "Reactions to the assassination of John F. Kennedy" 이라는 page만이 Kennedy 대통령과 어느 정도 관련이 있다. 아마도 “title”, “opening_text”과 “redirect.title.near_match” 라는 redirect page title의 fuzzy version을 search하여 더 구체화할 수 있을 것이다.

GET enwiki_rank/page/_search?filter_path=hits.hits._source.title
{
  "query": {
    "multi_match": {
      "query": "JFK",
      "type": "best_fields",
      "fields": [
        "title",
        "opening_text",
        "redirect.title.near_match"
      ]
    }
  }
}

The result already looks more promising:

결과가 조금 더 나아졌다.

"hits": [{ [...]
  "title": "JFK (disambiguation)" [...]
  "title": "JFK Medical Center (Edison, New Jersey)" [...]
  "title": "AirTrain JFK" [...]
  "title": "John F. Kennedy" [...]
  "title": "John F. Kennedy International Airport" [...]                
  "title": "JFK Express" [...]
  "title": "Sutphin Boulevard–Archer Avenue–JFK Airport (Archer Avenue Lines)" [...]
  "title": "JFK (soundtrack)" [...]
  "title": "Howard Beach–JFK Airport (IND Rockaway Line)" [...]
  "title": "Skaboy JFK" [...]                
}]

In addition to the disambiguation page, we find the article about president John F. Kennedy and the soundtrack of the movie JFK, but the rest of the result still doesn’t look great.

disambiguation page외에, John F. Kennedy 대동령과 영화 JFK의 soundtrack에 대한 기사가 있자먼 여전히 결과의 나머지는 그리 좋지 않다.

We could continue like this: looking at some data, improving the query bit by bit and checking the results, until we are satisfied. But there are many disadvantages to this. First, you will need to look through the list of results every time you change anything in your query and make a judgement call whether the results are better or worse than the previous ones. This is more or less a gut feeling. While it is sometimes clear which documents should be returned on top, it is much harder to judge the “somewhat okay” and the “really bad” cases in a consistent way over time. And then there is your colleague or boss that in many cases will have a slightly different opinion on which search results should be leading the list, so once they start tuning the query, everything moves into a different direction. A structured way of evaluating your search ranking is clearly needed.

디음과 같이 계속 할 수 있다. 우리가 만족할 때가지 약간의 data를 보고, query를 조금씩 개선하고, 결과를 확인하는 것. 하지만 이 방법에는 다음과 같은 많은 단점이 있다. 먼저, query에서 어떤 것을 변경할 때 마다, 결과 목록을 검토하여, 결과가 이전보다 더 나아졌는지 나빠졌는지 판단해야 한다. 이것은 다소 직감적이다. 어떤 document를 상위로 return해야 할지 어떤 때는 명확하지만, 시간이 흐르면서 "약간 괜찮네"와 "진짜 아니야" 라는 사례를 일관되게 판단하는 것이 훨씬 더 어렵다. 그리고 , 어떤 search 결과가 목록의 위에 올라와야 하는지에 대해, 상사나 동료가 약간 다른 의견을 가지는 경우가 많으므로, query를 tuning하기 시작하면 모든 것이 어긋나게 된다. search 순위를 평가하는 체계적인 방법이 명확하게 필요하다.

Good, Better, Best

This is where the new Ranking Evaluation API comes into play. It provides a way of attaching a set of document ratings to each of the typical search cases relevant for your system. By doing so, it offers a repeatable way of calculating various evaluation measures (that are well known in information retrieval literature) on top of those ratings to guide you in your decisions on how to optimize your system.

여기에서 새로운 Ranking Evaluation API가 적용된다. 시스템과 관련된 각각의 일반적인 search 사례에 document ratings의 집합을 붙이는 방법을 제공한다. 이렇게 함으로써, 시스템을 최적화하는 방법을 결정하는데 도움이 되도록 상위의 해당 ratings에 대한 다양한 평가 측정치를 계산하는 반복가능한 방법을 제공한다.

To make this more concrete, let us take a look at how such document ratings might look like. The Wikimedia foundations Discovery department has been working on improving their site search for a long time, and they struggled with the same kind of problems as the ones described above. This is why they launched their own service Discernatron with the goal to collect human judgments of search result relevance by letting users rate the quality of results on a scale from 0 (irrelevant) to 3 (relevant). User with a Wikimedia account can login at https://discernatron.wmflabs.org and check out the tool. It’s really worth a look and you might even be able to help improving the search quality of the largest knowledge base of our time by rating a few documents yourself. Just in case you don’t want to do that right now, here is what the user interface looks like:

이를 보다 구체적으로 설명하기 위해, 그러한 document ratings가 어떻게 보이는지 살펴보자. Wikimedia 재단의 Discovery 부서는 오랫동안 site search를 개선하기 위해 노력해 왔으며, 위에서 언급한 것과 동일한 종료의 문제로 고생했다. 이러한 이유로, 사용자들이 결과의 품질을 0(관련없음)에서 3(관련있음)까지 평가하도록 하여, search 결과의 관련성에 대한 사람들의 판단을 수집하기 위하여, 그들은 자체 서비스인 Discernatron를 시작했다. 사용자들은 https://discernatron.wmflabs.org에서 Wikimedia 계정으로 로그인하여, tool을 확인할 수 있다. 그것은 진정 가치 있는 것이고, 스스로 몇 개의 document를 평가함으로써, 이 시대의 가장 거대한 지식 기반의 search 품질을 개선하는데 도움이 될 수 있다. 지금 바로 그렇게 하지 싶지 않은 경우, 아래의 사용자 interface를 참고하자.

discernatron.png

The full dataset is available for users with a Wikimedia account (also optionally in JSON format), currently containing more than 6000 ratings associated with about 150 user queries. The document ratings for the rest of this demo are all based on this data to save of the trouble of having to come up with our own relevance ratings.

전체 data 집합(옵션으로 JSON format도)은 Wikimedia 계정을 가진 사용자들이 활용할 수 있는데, 현재 150개의 사용자 query와 관련된 6,000개 이상의 ratings를 가지고 있다. 이 demo의 나머지 document ratings는 모두 이 data를 기반으로 하여, 자체 관련성 등급을 산출해야 하는 어려움을 덜어준다.

Basic Request and Response

So let’s get back to our search for “JFK” that we started to build a query for. Luckily, there are already about 60 document ratings for this query in the Discernatron dataset. The demo project contains a slightly older, reduced dataset containing the document ratings. When you inspect that file, the first three columns contain the query, page title and average rating for the document in question. We can see that the ratings for the result “John F. Kennedy” is most relevant (score 3.0) while things like the JFK movie (score 2.25) or the airport (score 2.25) are rated lower and articles about the movie soundtrack (score 1.0) or the football team JFK Olimps (score 0.75) are judged as rather irrelevant with respect to the query. We can use this data to create out first request to the Ranking Evaluation API. Because the list of ratings is quite long, the full ratings array is abbreviated here, but you can find the full request in the demo project.

자, query를 작성하기 시작한 "JFK"에 대한 search로 돌아가 보자. 운좋게도, 이미 이 data 집합에는 이 query에 대한 약 60개의 document ratings가 있다. demo project에는 document ratings를 포함하고 있는 약간 더 오래된 축소된 data 집합을 포함하고 있다. 해당 file을 확인해 보면, 첫번째 3 column은 질문에 대한 document의 query, page title, 평균 ratings를 포함하고 있다. “John F. Kennedy” 결과에 대한 등급이 가장 관련(score 3.0)있고, 반면에 영화 JFK(score 2.25)나 공항(score 2.25)은 덜 관련 있고, 영화 soundtrack(score 1.0)이나 축구팀 JFK Olimps(score 0.75)에 대한 게시물은 query와 다소 무관한 것으로 나타난 것을 볼 수 있다. 등급 목록이 상당히 길기 때문에, 전체 ratings 배열은 여기에서 생략했지만, demo project에서 전체 request를 볼 수 있다.

POST /enwiki_rank/_rank_eval
{
  "requests": [{
    "id": "JFK_query",
    "request": {
      "query": {
        "multi_match": {
          "query": "JFK",
          "type": "best_fields",
          "fields": [
            "title",
            "opening_text",
            "redirect.title.near_match"
          ]
        }
      }
    },
    "summary_fields": [
      "title"
    ],
    "ratings": [{
        "_id": "3054546",
        "rating": 1,
        "_index": "enwiki_rank"
      },
      {
        "_id": "5119376",
        "rating": 3,
        "_index": "enwiki_rank"
      }, [...]
    ]
  }],
  "metric": {
    "precision": {
      "relevant_rating_threshold": 2,
      "k": 5
    }
  }
}

Let’s examine this request part by part. The largest part of the request is an array called "requests". It defines the set of different search cases that are part of the evaluation. We start simple by only evaluating the “JFK” search at the moment, but will extend that later. We give this use-case an id and specifying the query from above to be executed. The summary_fields parameter defines a filter on the document fields that we want to see in the response. For the sake of this demo it is sufficient to see the document title. The "ratings" section contains all the relevance judgements that we extracted from the Discernatron dataset by mapping the page title to out internal document id. Here e.g. the document with "_id": "5119376" refers to the John F. Kennedy article and gets the highest score of 3.0.

이 request를 부분별로 확인해 보자. request의 가장 큰 부분은 "requests" 배열이다. 이것은 평가의 일부분인 디양한 search 사례의 집합을 정의한다. 현재 "JFK" search만 평가하는 하는 것으로 단순하게 시작하지만 나중에 그것을 확장할 것이다. 이 사용사례에 id 를 부여하고 위에서 실행한 query 를 지정한다. summary_fields 매개변수는 response에서 볼 수 있는 document의 field에 대한 filter를 정의한다. 이 demo를 위해서는 document의 title을 보는 것으로 충분하다. "ratings" section은 page title을 내부 document id에 mapping하여, data 집합에서 추출한 관련성 판단이 포함된다. 예를 들어, 여기에서 "_id": "5119376" document는 John F. Kennedy 게시물을 가리키며, 가장 높은 score 3.0 을 가진다.

The following, smaller section defines the evaluation metric itself. We choose Precision, or more specifically, Precision at K, which is a metric that takes the first K returned search results and calculates the fraction of relevant documents. Since we use rating on a scale from zero to three, we also need to define from which ratings on this scale we consider as “relevant”. We choose a rating of 2 and above to be “relevant” by setting the relevant_rating_threshold accordingly. For the sake of brevity, we also choose to evaluate the query only on the top 5 documents. This means that when those top 5 results contain three documents with score two or higher, the precision will be 3 / 5 = 0.6. You can check this now by running the full request in the Kibana Console. This is the abbreviated response:

이어지는 더 작은 section은 평가 metric 자체를 정의한다. 우리는 Precision, 더 정확히 표현하면, Precision at K 을 선택했는데, 이는 처음으로 K를 반환하는 search 결과를 가져와 관련된 document의 비율을 계산하는 metric이다. 0에서 3까지의 ratings를 사용하기 때문에, 이것에서 어느 ratings를 “관련있는” 것으로 간주할지를 정의해야 한다. 따라서, 위에서 "관련있는" 것이 되도록, relevant_rating_threshold 를 설정하여, ratings 2를 선택한다. 또한 간략하도록, 상위 5개의 document에서만 query를 평가하도록 선택한다. 즉, 상위 5개의 document에 score 2 이상의 document가 3개가 포함되면, 정확도는 3 / 5 = 0.6 이 된다. 이제 Kibana Console에서 전체 request를 실행하여 확인할 수 있다. 다음은 요약된 response이다.

{
  "quality_level": 0.6,
  "details": {
    "JFK_query": {
      "quality_level": 0.6,
        "unknown_docs": [
        {
        "_index": "enwiki_rank",
        "_id": "26509772"
        }
       ],
      "hits": […],
      "metric_details": {
        "precision": {
          "relevant_docs_retrieved": 3,
          "docs_retrieved": 5
        }
      }
    }
  }
}

As we can see, apart from the total result, a precision score of 0.6, the details section contains additional information about each evaluated search case, in this case only the “JFK_query”. The metric_details section shows how the score was calculated. As expected, we found 3 relevant documents out of 5 retrieved. But what about the other two documents? Either the document had a rating of less than 2, or some of the results returned by the query were not rated at all. It can be useful to present especially these unrated documents to the user and ask for a rating. The unknown_docs section contains those documents for exactly this purpose. In addition, the hits section contains all the search hits returned for the particular query under evaluation. The difference between a usual search and "hits" is that each hit contain its rating (or null if there is none) and usually _sourceis omitted. Document fields can be included (e.g. because we want to present them to a user when asking for a rating) by explicitly specifying them in the request's summary_fields.

위에서 보듯이, 전체 결과에서 0.6인 precision score를 제외하면, details section에는 각각의 평가된 search 사례에 대한 추가적인 정보가 포함되어 있다. 이 경우에는 “JFK_query”만 있다. metric_details section은 score가 계산되는 방법을 보여준다. 예상대로 5건 중 3건의 관련된 document를 가져왔다. 그렇다면 다른 2건의 document는? document의 ratings가 2보다 적거나, query에 의해 반환된 결과가 전혀 평가되지 않았다. 특히 사용자에게 이런 등급 미지정 document를 제공하고 ratings를 요청하는 것은 유용하다. unknown_docs section에는 정확히 이런 목적을 위한 해당 document가 포함되어 있다. 또한, hits section에는 평가중인 특정 query에 대해 반환된 모든 search 결과가 포함된다. 일반적인 search와 "hits"의 차이점은 각 hit는 rating (없는 경우에는 null)이 포함되며, 일반적으로 _source 는 생략된다. document의 field는 request의 summary_fields 에 그것을 명시적으로 지정하여 포함(예를 들어, ratings를 요청할 때, 사용자에게 그것을 표시하려는 경우)할 수 있다.

Improving Queries Incrementally

Now that we know what the basic requests and responses look like, let us use this to improve our query some more. By looking at the title of the results, we can see that the three relevant documents we get back are “John F. Kennedy”, “JFK (disambiguation)" and “John F. Kennedy International Airport”. Not too bad, but we might get even better. Poking around in the Wikipedia document definition, we find a field called popularity_score, which seems to be higher for popular articles like “John F. Kennedy” than for niche articles like "JFK Medical Center (Edison, New Jersey)". Let us see if we can use this to boost search results with a function score query using this request:

이제, 기본적인 request와 response를 알았으니, query를 좀 더 개선하여 이를 사용해 보자. 결과의 title을 살펴보면, “John F. Kennedy”, “JFK (disambiguation)", “John F. Kennedy International Airport”의 3개의 관련된 document를 볼 수 있다. 나쁘지 않지만, 더 나아질 수 있다. Wikipedia document 정의를 살펴보면, popularity_score 라는 field를 볼 수 있는데, 이는 "JFK Medical Center (Edison, New Jersey)" 같은 틈새 게시물보다 “John F. Kennedy” 같은 인기있는 게시물애 더 높은 것으로 보인다. 다음 request를 사용하여,이 field를 이용하여, 다음 request 처럼,  function score query 로 search 결과를 향상시킬 수 있는지 알아보자.

POST /enwiki_rank/_rank_eval
{
  "requests": [{
    "id": "JFK_query",
    "request": {
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query": "JFK",
              "type": "best_fields",
              "fields": [
                "title",
                "opening_text",
                "redirect.title.near_match"
              ]
            }
          },
          "functions": [{
            "field_value_factor": {
              "field": "popularity_score",
              "missing": 1
            }
          }],
          "boost_mode": "multiply"
        }
      }
    },
    "summary_fields": [
      "title"
    ],
    "ratings": [… ]
  }],
  "metric": {
    "precision": {
      "k": 5,
      "relevant_rating_threshold": 2
    }
  }
}

We now even get 4 out of 5 relevant documents in the result, which leads to a precision score of 0.8. Well done. But what if this is just improving results for this particular query. After all, out users are not just interested in the results for typing in “JFK” into the search box. Time to put some more typical searches in the mix.

결과에는 5개의 관련된 document 중 4개를 표시되어, precision score가 0.8이 된다. 잘 됐다. 그러나, 이것이 이 특정 query의 결과만 향상시키는 것이라면? 결국, 사용자는 검색 창에 "JFK"를 입력한 경우에 대한 결과에만 관심을 가지는 것이 아니다. 좀 더 일반적인 search를 해야할 시간이다.

Extending the Set of Evaluated Queries

Looking at the user queries in first column of the document ratings, we can discover more cases which we have ratings for. Typically you would try using the whole data if possible, but for now, let's just add two cases to the evaluation. The first search we’ll add (“the great beer flood”) relates to a curious historical incident called the London beer flood, where in 1814, after a brewery accident, more than 1 million liters of beer were running into the streets. The second search case we’ll add is about “naval flags” and we’ll assume users are interested in things like maritime flags or other kinds of naval ensigns.

document ratings의 첫번째 column에 있는 사용자 query를 살펴보면, ratings를 가진 더 많은 경우를 볼 수 있다. 일반적으로 가능한 한 전체 data를 사용하려 하겠지만, 평가에 2가지 경우만 추가해 보자. 추가할 첫번째 search는 1814년 맥주 공장 사고 후, 백만 리터 이상의 맥주가 거리로 흘러나온 London beer flood라는 기이한 역사적 사건과 관련이 있는 (“the great beer flood”) - 거대한 맥주 홍수 - 이다. 추가할 두번째 search는 “naval flags” 에 대한 것인데, 사용자들은 maritime flags(선박의 깃발)이나 다른 종류의 naval ensigns(해군 깃발)에 관심이 있다고 가정할 것이다.

We want to use the same query as the one above, but we don’t want to repeat it three times. We can avoid this by using the templates section, that allows sharing queries common to all search use cases and to parametrize them later. The query that we previously defined in the requests section gets its own template id (“my_template”) and uses a placeholder ("{{query_string}}") inside the query that is later replaced by the actual user query. If you ever used search templates before, this will look familiar. In the requests sections we can now refer to this template by its id using the template_id and providing the parameter replacements in the params object:

위에서와 동일한 query를 사용하려 하지만, 3번 반복하지는 않으려 한다. 모든 search 사례에 공통적인 query를 공유할 수 있고, 나중에 매개변수화 할 수 있는 templates section을 사용하여, 이를 피할 수 있다. requests section에서 이전에 정의한 query는 자신의 template id(“my_template”)를 가져오고, 나중에 실제 사용자 query로 대체되는 query 내에 placeholder ("{{query_string}}")를 사용한다. 이전에 search templates 을 사용해 본적이 있다면, 이것은 익숙할 것이다. 이제, requests section에서, template_id를 사용허여 그 id로 이 template을 참조할 수 있고, params object에서 매개변수 교체를 제공한다.

POST /enwiki_rank/_rank_eval
{
  "templates": [{
    "id": "my_template",
    "template": {
      "source": {
        "query": {
          "function_score": {
            "query": {
              "multi_match": {
                "query": "{{query_string}}",
                "type": "best_fields",
                "fields": [
                  "title.near_match",
                  "opening_text.plain",
                  "redirect.title.near_match"
                ]
              }
            },
            "functions": [{
              "field_value_factor": {
                "field": "popularity_score",
                "missing": 1,
                "modifier": "none"
              }
            }],
            "boost_mode": "multiply"
          }
        }
      }
    }
  }],
  "requests": [{
      "id": "JFK_query",
      "template_id": "my_template",
      "params": {
        "query_string": "JFK"
      },
      "ratings": […]
    },
    {
      "id": "the_great_beer_flood_query",
      "template_id": "my_template",
      "params": {
        "query_string": "the great beer flood"
      },
      "ratings": […]
    },
    {
      "id": "naval_flags_query",
      "template_id": "my_template",
      "params": {
        "query_string": "naval flags"
      },
      "ratings": […]
    },
    "metric": {
      "precision": {
        "k": 5,
        "relevant_rating_threshold": 2
      }
    }
  }
}
Read Less

When we run the full request, the result might be a bit surprising at first: the combined average "quality_level" is now only 0.26. While the “JFK” query still performs well (a precision of 0.8), the other two queries show a precision of 0, meaning they don’t have any relevant document in their results. This leads to an average precision of only 0.26. What seems to be wrong? If we look at the results in the details, we see that e.g. the “naval flags” query returns the “Union Jack” as first search hit, and the “great beer flood” query features things like “Great White Shark” or “Bob Dylan” among its search results. Maybe the influence of the popularity_score field we added to the query to get better results with the “JFK” example is too high? Let’s just try it out by taking the function_scorequery out again. This already increases precision to 0.46. Still not great, but at least each search now contains some relevant results: while precision for the “JFK” query dropped a bit to 0.6, we now also get 0.6 on the “naval flags” query and at least 0.2 with the “beer flood” query:

full request를 처음 실행하면, 처음에는 약간 놀랄 수 있다. 지금 조합된 평균 "quality_level"이 겨우 0,26이다. “JFK” query는 여전히 양호(precision 0.8)한데, 다른 2개의 query는 precision 0 이다. 이는 그 결과에 관련있는 document가 전혀 없다는 것을 의미한다. 이로 인해, 평균 precision이 겨우 0.26이 되었다. 무엇이 잘못되었을까? details 에서 결과를 살펴보면, 예를 들어 “naval flags” query는 “Union Jack”을 첫번째 search hit로 반환하고, great beer flood” query는 search 결과 중 “Great White Shark” 이나 “Bob Dylan” 같은 결과를 제공함을 볼 수 있다. 혹시 “JFK” 예제에서 더 낳은 결과를 위해 query에 추가한 popularity_score field의 영향이 너무 큰가? function_score query를 다시 해 보자. 이것은 precision을 0.46으로 증가시킨다. 아직 좋지는 않지만, 최소한 각 search에는 몇 가지 관련있는 결과를 포함하고 있다. “JFK” query의 precision이 0.6으로 약간 떨어졌지만, “naval flags” query에서는 0.6, “beer flood” query에서는 최소 0.2이다.

{
  "quality_level": 0.4666666666666666,
  "details": {
    "JFK_query": {
      "quality_level": 0.6,
      [...]
    },
    "naval_flags_query": {
      "quality_level": 0.6,
      [...]
    },
    "the_great_beer_flood_query": {
      "quality_level": 0.2,
      [...]
    }
  }
}

Start Exploring

In the end, finding a good query template usually means finding a compromise that works reasonably well across all search use cases in the test set. After all, a query template that returns some good results for most use cases is better than a query template that gets a perfect score for one particular use case but returns nothing useful for all the others.

결국 좋은 query template을 찾는는 것은 일반적으로 테스트 집합의 많은 search 사례에서 합리적으로 잘 동작하는 절충안을 찾는 것이다. 결국, 대부분의 사례에 대해 좋은 결과를 반환하는 query template이 어떤 특정 사례에 대해 완벽한 score를 얻지만 다른 모든 것에는 아무 것도 반환하지 못하는 query template보다 더 낫다. 

There are many aspects we haven’t touched yet, but that are worth exploring on your own. For example, we used the precision metric throughout this blog post because its relatively easy to explain and calculate. However, it doesn’t take the positions of the rated documents in the result set into account — a relevant result in the first position counts the as a relevant document in the last position of the top results. The Discounted Cumulative Gain (DCG) metric is much better suited for these cases. You can try it out by simply exchanging the “metric” section in the examples above with:

아직 우리가 다루지 못한 부분이 많이 있지만, 여러분 스스로 그것을 탐구해 볼만하다. 예를 들어, 이 게시물에서 설명과 계산이 비교적 쉽기 때문에, precision metric을 사용했다. 그러나, 결과 집합에서 연관된 document의 위치는 고려하지 않았다. 첫번째 위치에서 관련 결과는 상위 결과의 마지막 위치에서 관련 document로 계산된다. Discounted Cumulative Gain (DCG) metric은 이런 경우에 훨씬 더 적합니다. 위의 예에서 “metric” section을 다음과 같이 교환하면 된다.

"metric": {
  "dcg": {
    "normalize": true,
    "k": 5
  }
}

This will calculate the normalized variant of the metric on the top 5 results. Start by using it with just one search query first and observe how the evaluation score changes when document with a high rating are returned further up the result list.

이렇게 하면, 상위 5개의 결과에서 metric의 정규화된 variant가 계산된다. 먼저 하나의 search query만 사용하여 높은 ratings의 document가 결과 목록의 더 위로 반환될 때, 평가 score가 어떻게 변화하는지 살펴보자.

Using the Ranking Evaluation API won’t magically make your search queries return only relevant results out of a sudden, but it will help you a lot in guiding your decisions while prototyping and maintaining a search system later on. No reasonably complex search application should be run in production without any kind of quality testing, be simple unit test, A/B tests or periodically running one of the metrics the Ranking Evaluation API offers. Coming up with relevance judgements for specific search cases might seem a bit of work first, but once you have your basic test set, you can iterate much faster and with more confidence on making changes, be it to your queries, analysis, or other parts of your system.

Ranking Evaluation API를 사용한다고 갑자기 search query가 관련있는 결과만 반환하지는 읺지만, 나중에 search 시스템을 prototyping하고 유지하는 동안 여러분의 의사 결정애 많은 도움이 될 것이다. 품질 테스트, 간단한 unit 테스트, A/B 테스트 또는 주기적으로 Ranking Evaluation API가 제공하는 metrics 중 하나를 실행없이는 제품에 합리적이고 복작한 search application도 없다. 특정 search 사례에 대한 관련성 판단을 내리는 것은 처음에는 약간의 작업이라 보이겠지만, 기본 테스트 집합을 가지게 되면, 훨씬 더 빠르고 신뢰할 수 있는 방식으로 시스템의 query, analysis, 다른 부분에 적용할 수 있다.

원문 : Made To Measure: How to Use the Ranking Evaluation API in Elasticsearch