해당 글은 엘라스틱 스택 개발부터 운영까지라는 책 중 4장을 정리하여 쓴 글입니다.
엘라스틱서치는 전문 검색 기능을 시작으로 꾸준히 성장했으며, 다양한 검색 쿼리를 지원하고 있다.
1부에서는 쿼리 컨텍스트와 필터 컨텍스트의 차이, 쿼리 스트링과 쿼리 DSL의 차이점 그리고 유사 스코어를 계산법에 대해서 알아본 후 2부에서 전문 쿼리, 매치 쿼리, 용어 쿼리, 멀티 매치 쿼리, 범위 쿼리, 논리 쿼리, 패턴 쿼리에 대해서 알아보려고 한다.
쿼리 컨텍스트와 필터 컨텍스트
쿼리 컨텍스트 ? 질의에 대한 유사도를 계산해 이를 기준으로 정확한 결과를 먼저 보여줌.
필터 컨텍스트 ? 유사도를 계산하지 않고 일치 여부에 따른 결과만을 반환한다.
질의에 대한 유사도의 결과 반환에 따라 다르다고 나와있다. 쿼리 컨텍스트는 유사도를 계산해 정확한 결과를 먼저 보여주기 때문에 연관성에 대한 스코어 결과를 제공하지만, 필터 컨텍스트는 계산하지 않고 일치 여부만에 따른 결과만을 반환하기 때문에 '예/아니요'의 결과만 제공한다.
필터 컨텍스트는 스코어 계산을 하지 않으며, 결과에 대한 업데이트를 매번 수행할 필요가 없기 때문에 캐시를 이용할 수 있다. 이를 캐시하기 위하여 엘라스틱 서치는 기본적으로 힙 메모리의 10%를 캐시에 이용하고 있으며, 캐시를 이용한 빠른 검색을 하려면 필터 컨텍스트를 사용하면 된다.
쿼리 스트링과 쿼리 DSL
쿼리 스트링 ? 한 줄 정도의 간단한 쿼리에 사용.
쿼리 DSL ? 한줄에 넣기 힘든 복잡한 쿼리에 사용.
쿼리 DSL은 엘라스틱서치에서 제공하는 쿼리 전용 언어로 JSON 기반의 직관적인 언어이다.
쿼리 스트링은 한 줄 정도의 간단한 쿼리에 사용하기에 좋기 때문에 REST API의 URI 주소에 쿼리문을 작성하는 방식으로 실행해볼 수 있어 사용하기 쉽다 하지만, 쿼리 DSL은 복잡한 쿼리에 사용이 되기 때문에 JSON 형태로 쿼리를 작성해야 한다.
유사도 스코어 계산법
유사도 스코어 ? 질의문과 도큐먼트의 유사도를 표현한 값으로, 스코어가 높을수록 찾고자 하는 도큐먼트에 가깝다는 사실을 의미한다.
엘라스틱 서치는 다양한 스코어 알고리즘을 사용할 수 있는데 기본적으로 BM25 알고리즘을 이용해 유사도 스코어를 계산한다.
BM25 알고리즘 ? 검색, 추천에 많이 사용되는 알고리즘으로 TF(Term Frequency), IDF(Inverse Document Frequency) 개념에 문서 길이를 고려한 알고리즘이다.
엘라스틱 서치는 5.x 이전 버전에서는 TF-IDF 알고리즘을 사용했으나, 5.x부터 BM25 알고리즘을 기본적으로 사용한다고 한다.
이제 BM25 알고리즘이 어떻게 검색을 하는지에 대해서 알아보자.
검색 결과에 대한 스코어 계산이 어떻게 되어있는지 알려면 explain 옵션을 추가하여 검색을 하면 된다.
검색을 하게 되면 아래의 접은 글을 펼쳤을 때 처럼 하나의 결과에 대해서 스코어 계산이 나온다. 이제부터 해당 결과에 대해서 BM25 알고리즘을 이용하여 어떻게 스코어가 나왔는지 차근차근 알아볼 생각이다.
{
"_shard": "[kibana_sample_data_ecommerce][0]",
"_node": "i2U98WAQR_GpFIz3eHK3WA",
"_index": "kibana_sample_data_ecommerce",
"_type": "_doc",
"_id": "X36O_X4BUDdHMtaiajEJ",
"_score": 8.268259,
"_source": {
"category": [
"Men's Shoes",
"Men's Clothing"
],
"currency": "EUR",
"customer_first_name": "Robert",
"customer_full_name": "Robert Cross",
"customer_gender": "MALE",
"customer_id": 29,
"customer_last_name": "Cross",
"customer_phone": "",
"day_of_week": "Wednesday",
"day_of_week_i": 2,
"email": "robert@cross-family.zzz",
"manufacturer": [
"Elitelligence",
"Low Tide Media"
],
"order_date": "2022-02-16T11:51:22+00:00",
"order_id": 568578,
"products": [
{
"base_price": 46.99,
"discount_percentage": 0,
"quantity": 1,
"manufacturer": "Elitelligence",
"tax_amount": 0,
"product_id": 17925,
"category": "Men's Shoes",
"sku": "ZO0520005200",
"taxless_price": 46.99,
"unit_discount_amount": 0,
"min_price": 24.43,
"_id": "sold_product_568578_17925",
"discount_amount": 0,
"created_on": "2016-12-14T11:51:22+00:00",
"product_name": "Boots - tan",
"price": 46.99,
"taxful_price": 46.99,
"base_unit_price": 46.99
},
{
"base_price": 32.99,
"discount_percentage": 0,
"quantity": 1,
"manufacturer": "Low Tide Media",
"tax_amount": 0,
"product_id": 16500,
"category": "Men's Clothing",
"sku": "ZO0421104211",
"taxless_price": 32.99,
"unit_discount_amount": 0,
"min_price": 16.82,
"_id": "sold_product_568578_16500",
"discount_amount": 0,
"created_on": "2016-12-14T11:51:22+00:00",
"product_name": "Casual Cuffed Pants",
"price": 32.99,
"taxful_price": 32.99,
"base_unit_price": 32.99
}
],
"sku": [
"ZO0520005200",
"ZO0421104211"
],
"taxful_total_price": 79.98,
"taxless_total_price": 79.98,
"total_quantity": 2,
"total_unique_products": 2,
"type": "order",
"user": "robert",
"geoip": {
"country_iso_code": "SA",
"location": {
"lon": 45,
"lat": 25
},
"continent_name": "Asia"
},
"event": {
"dataset": "sample_ecommerce"
}
},
"_explanation": {
"value": 8.268259,
"description": "weight(products.product_name:pant in 594) [PerFieldSimilarity], result of:",
"details": [
{
"value": 8.268259,
"description": "score(freq=1.0), computed as boost * idf * tf from:",
"details": [
{
"value": 2.2,
"description": "boost",
"details": []
},
{
"value": 7.1974354,
"description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
"details": [
{
"value": 3,
"description": "n, number of documents containing term",
"details": []
},
{
"value": 4675,
"description": "N, total number of documents with field",
"details": []
}
]
},
{
"value": 0.52217203,
"description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
"details": [
{
"value": 1,
"description": "freq, occurrences of term within document",
"details": []
},
{
"value": 1.2,
"description": "k1, term saturation parameter",
"details": []
},
{
"value": 0.75,
"description": "b, length normalization parameter",
"details": []
},
{
"value": 5,
"description": "dl, length of field",
"details": []
},
{
"value": 7.3161497,
"description": "avgdl, average length of field",
"details": []
}
]
}
]
}
]
}
}
일단 총 스코어는 8.268259가 나왔다고 볼 수 있다.
스코어는 도큐먼트와 쿼리 간의 연관성 수치로, 값이 클수록 연관성이 높다는 것을 의미한다.
아까전에 BM25 알고리즘은 TF(Term Frequency), IDF(Inverse Document Frequency) 개념에 문서 길이를 고려한 알고리즘이다. 라고 한다. 즉, 8.268259라는 스코어가 IDF와 TF의 값 그리고 최종 스코어 계산식으로 만들어졌다고 알면 된다.
IDF ? 특정 용어가 얼마나 자주 등장했는지를 의미하는 지표이다.
TF ? 특정 용어가 하나의 도큐먼트에 얼마나 많이 등장했는지를 의미하는 지표이다.
IDF는 특정 용어가 얼마나 자주 등장했는지를 의미하는 지표로써 전체 문서에서 자주 발생하는 단어일수록 중요하지 않는 단어로 인식하고 가중치를 낮춘다고 하며, TF는 하나의 도큐먼트에서 특정 용어가 많이 나오면 중요한 용어로 인식하고 가중치를 높인다고 한다.
처음 읽으면 확실히 이해가 안되지만, 추가로 설명하자면, IDF는 전체 문서 즉, 전체 Document에서 해당 용어가 자주 나왔는지에 대한 것이고, TF는 하나의 문서, 즉 하나의 Document에서 용어가 자주 나왔는지에 대한 것이다.
현재 검색 결과에 대한 IDF는 "Pants"에 대해서 4675(N)개의 전체 문서에서 3(n)개 일치한다고 한다.
이렇게 IDF는 log(1+ (4675 - 3 + 0.5) / (3 + 0.5))로 계산되어 7.1974354가 나왔다고 보면 된다.
그리고 결과에 대한 TF는 freq, k1, b, dl, avgdl 값을 통하여 구하면 된다고 한다.
freq ? 도큐먼트 내에서 용어가 나온 횟수
k1 / b ? 알고리즘을 정규화하기 위한 가중치로 엘라스틱서치가 디폴트로 취하는 상수.
dl ? 필드 길이, 현재 도큐먼트를 토큰화했을 떄의 토큰 수
avgdl ? 전체 도큐먼트에서 평균 필드 길이다. 모든 도큐먼트를 토큰화했을 때의 도큐먼트 당 갖는 평균 토큰 수
dl이 작고 avgdl이 클수록 TF 값이 크게 나온다.
이렇게 TF는 1 / ( 1+ 1.2 * ( 1 - 0.75 + 0.75 * 5 / 7.3161497)) 로 계산하여 0.52217203이 나온다.
유사 스코어의 마지막 최종 스코어를 알아보자.
최종 스코어는 boost이다. boost는 엘라스틱이 지정한 고정값으로 2.2로 정해져있다고 한다.
이렇게 만들어진, IDF 와 TF 그리고 최종스코어인 boost를 이제 각각 곱하면, 유사 스코어가 나온다.
7.1974354 * 0.52217203 * 2.2 = 8.268259로 말이다.
다음
유사 스코어를 끝으로 1부는 끝을 내려고 한다. 2부에서는 전문 쿼리, 매치 쿼리, 용어 쿼리, 멀티 매치 쿼리, 범위 쿼리, 논리 쿼리, 패턴 쿼리에 대해서 알아보려고 한다.
회고
유사 스코어에 대해서 처음 봤을 때 BM25 알고리즘을 사용하며, 복잡한 식이 인터넷에 떠돌아다녔다. 처음 보자마자 겁을 먹었으나, 진정하고 차근차근 BM25 알고리즘의 요소들이 어떻게 되어있는지 확인하고 분석을 해보니 확실히 간단했으나 나중에 실무에 적용할 때 머리가 아플 것 같은 느낌 아닌 느낌이 들었다. 이러한 상황이 온다면 이제 한 단계 더 업해야되는 상황일 것이라고 생각한다. 즉, 피할 수 없는 상황이니 신나게 계산기 두드리면서 해야되겠다. 어서 이러한 시기가 왔으면 좋겠다. 이러한 시기 하나하나가 나를 더욱 성장시켜줄 것이기 때문이다.
'ELK > ELK 개발부터 운영까지' 카테고리의 다른 글
엘라스틱서치(elasticsearch) 집계 (0) | 2022.03.09 |
---|---|
엘라스틱서치(elasticSearch) 검색 - 2부 (0) | 2022.03.02 |
엘라스틱서치(elasticSearch) 기본 - 2부 (0) | 2022.02.23 |
엘라스틱서치(elasticSearch) 기본 - 1부 (0) | 2022.02.23 |
엘라스틱스택(ELK Stack) 이란? (0) | 2022.02.14 |