<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[정확도 - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[정확도 - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/kr/search-labs/blog/category/relevance</link>
    </image>
    <link>https://www.elastic.co/kr/search-labs/blog/category/relevance</link>
    <atom:link href="https://www.elastic.co/kr/search-labs/rss/category/relevance.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[kr]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 12:01:23 GMT</lastBuildDate>
  <item>
    <title><![CDATA[최소 점수를 사용하여 시맨틱 정밀도 보장]]></title>
    <description><![CDATA[최소 점수 임계값을 사용하여 시맨틱 정밀도를 향상하세요. 이 글에서는 시맨틱 검색 및 하이브리드 검색에 대한 구체적인 예시를 제공합니다. ]]></description>
    <content:encoded><![CDATA[<p>시맨틱 검색은 검색 정확도 측면에서 무궁무진한 가능성을 열어주었습니다. ELSER, E5, Jina Embedding v4와 같은 고품질의 희소 및 밀집 모델은 키워드 일치가 아닌 단어의 의미를 기반으로 관련성 높은 결과를 제공합니다. 그러나 시맨틱 검색은 때때로 검색 결과의 끝부분이나 인덱스에 관련 결과가 부족한 쿼리에 대해 관련성이 떨어지는 결과를 반환할 수 있습니다. 이러한 희소 및 밀집 모델의 속성으로 인해 사용자가 혼란을 겪거나 대규모 언어 모델(LLM)에서 귀중한 토큰이 낭비될 수 있습니다.</p><p>이 글에서는 최소 점수 매개변수를 사용하여 시맨틱 검색 결과의 정밀도를 높이는 방법을 알아봅니다. 이 블로그 게시물에 제공된 예시를 테스트해 보고 싶다면 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/ensuring-semantic-precision-with-minimum-score/ensuring_semantic_precision_with_minimum_score.ipynb">관련 Jupyter 노트북</a>으로 이동하세요.</p><h2>배경: 정밀도와 재현율</h2><p>검색 정확도에서 <em>정밀도</em>와 <em>재현율</em>은 핵심 개념입니다. 아직 익숙하지 않은 독자라면 관련 내용을 알아보기를 강력히 권장합니다. 다음은 간략한 내용입니다.</p><ul><li><p><strong>정밀도: </strong>사용자와 관련된 반환된 검색 결과의 비율.</p></li><li><p><strong>재현율: </strong>검색 결과 집합에 포함된 코퍼스 내 모든 관련 문서의 비율.</p></li></ul><p>다시 말해 정밀도는 <strong>오직</strong> 관련성 있는 결과만 반환하는 것이고, 재현율은 관련성 있는 <strong>모든</strong> 결과를 반환하는 것입니다. 짐작하시겠지만, 이 두 가지는 종종 상충되는 요구 사항입니다. 시맨틱 검색은 재현율은 매우 높지만, 정밀도는 떨어지는 경향이 있습니다. 이 속성을 해결하는 방법을 알아보려면 계속 읽어보세요.</p><h2>최소 점수 매개변수 소개</h2><p>'min_score' 매개변수를 사용하면 최소 점수를 설정하여 정밀도를 향상할 수 있으며, 이 매개변수는 정의된 임계값보다 낮은 점수를 가진 일치 항목을 제거하여 결과 집합을 잘라냅니다. 다음은 간단한 예시입니다.</p>GET search-movies/_search
{
  "retriever": {
    "linear": {
      "min_score": 4,
      "retrievers": [
        ...
      ]
    }
  }
}<h2>점수 정규화</h2><p>최소 점수를 설정하는 것도 좋지만, 모든 시맨틱 모델이 정적 임계값에 적합한 점수를 반환하는 것은 아닙니다. 예를 들어 ELSER는 제한이 없는 점수를 반환합니다. <a href="https://huggingface.co/intfloat/e5-small#faq">일부</a> 밀집 모델 점수는 밀집도가 높으며 특정 쿼리의 컨텍스트에서만 의미가 있습니다.</p><p>대부분의 시맨틱 검색 사례의 경우 'min_score'를 적용하기 전에 정규화 접근 방식을 사용하는 것이 좋습니다. 정규화는 문서 점수가 정의된 간격 내에 있도록 보장합니다. Elasticsearch 검색기는 'l2_norm'과 'minmax'라는 두 가지 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever#linear-retriever-normalizers">정규화 도구</a>를 제공합니다. 가장 일반적으로 사용되는 것은 'minmax'입니다. 이해하기 쉽고 많은 시나리오에서 잘 작동하기 때문입니다. 'minmax'의 주요 속성은 다음과 같습니다.</p><ul><li><p>문서 점수는 0–1점 사이로 분포됩니다.</p></li><li><p>가장 높은 점수를 받은 문서는 항상 1점으로 처리됩니다.</p></li><li><p>가장 낮은 점수를 받은 문서는 항상 0점으로 처리됩니다.</p><ul><li><p>이로 인해 키워드 검색에 적합하지 않을 수 있습니다. 자세한 내용은 '하이브리드 검색' 섹션을 참조하세요.</p></li></ul></li></ul><p>다음은 <code>min_score</code> 을(를) 사용하여 정규화된 시맨틱 쿼리의 예입니다. 검색 결과 목록을 100개부터 시작하는 더 긴 목록으로 반환할 수 있도록 순위 창 크기를 500으로 늘렸습니다.</p>GET search-movies/_search
{
  "size": 100,
  "_source": [
    "title", "overview"
  ],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        }
      ]
    }
  }
}<p>이 크기는 실제 프로덕션 환경에서 일반적으로 사용되는 값보다 크게 설정되었습니다. 이는 검색 결과의 품질을 검사하고 결과를 조정하기 위한 것입니다.</p><h2>선형 검색기를 사용한 하이브리드 검색</h2><p>하이브리드 검색의 경우 가장 간단한 접근 방식은 모든 점수를 정규화하고 가중치를 부여한 다음 최소 점수를 적용하는 것입니다. 합이 1인 가중치를 선택하면 총점이 0–1 범위 내에 유지됩니다. 이렇게 하면 최종 점수를 쉽게 파악하고 <code>min_score</code> 을(를) 조정할 수 있습니다. 다음은 그 예입니다.</p>GET search-movies/_search
{
  "size": 100,
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "weight": 0.6,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        },
        {
          "weight": 0.4,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "multi_match": {
                  "query": "superhero movie",
                  "fields": ["overview","keywords", "title"],
                  "type": "cross_fields",
                  "minimum_should_match": "2"
                }
              }
            }
          }
        }
      ]
    }
  }
}<h2>RRF를 사용한 하이브리드 검색</h2><p>BM25에서는 종종 <code>AND</code> 연산자나 <code>minimum_should_match</code> 와(과) 같은 다른 수단을 통해 정밀도를 제어하기도 합니다. 또한 단일하고 정확하며 희귀한 용어로 구성된 쿼리는 자연스럽게 검색 결과 수가 적고, 그 결과가 모두 관련성이 높은 경우가 많습니다. 이는 다음과 같은 결과로 이어질 수 있습니다.</p><ul><li><p>BM25 검색기에서 결과 뒷부분에 있는 결과는 절대 BM25 점수가 최고 점수에 근접하더라도 낮은 정규화 점수를 받게 됩니다.</p></li><li><p>매우 낮은 BM25 점수를 시맨틱 점수에 더하면 총점이 시맨틱 점수와 거의 같아집니다.</p></li><li><p>BM25 점수 기여도가 부족하면 <code>min_score threshold</code> 에 의해 문서가 폐기될 수 있습니다.</p></li></ul><p>이에 대한 해결책으로 상호 순위 융합(RRF)을 사용하여 BM25와 시맨틱 결과를 결합할 수 있습니다. RRF는 서로 다른 검색 알고리즘의 점수를 비교하는 문제를 해결하기 위해 각 결과 집합에서의 위치에 초점을 맞춥니다. 이 시나리오에서는 <code>min_score</code> 이(가) 시맨틱 검색기에만 적용됩니다.</p>GET search-movies/_search
{
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "rrf": {
      "rank_window_size": 500,
      "retrievers": [
        {
          "linear": {
            "rank_window_size": 500,
            "min_score": 0.25,
            "retrievers": [
              {
                "normalizer": "minmax",
                "retriever": {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "overview_vector",
                        "query": "superhero movie"
                      }
                    }
                  }
                }
              }
            ]
          }
        },
        {
          "standard": {
            "query": {
              "multi_match": {
                "query": "superhero movie",
                "fields": ["overview", "keywords","title"],
                "type": "cross_fields",
                "minimum_should_match": "2"
              }
            }
          }
        }
      ]
    }
  }
}<h2>결론</h2><p><code>min_score</code>을(를) 사용하여 시맨틱 검색 알고리즘의 높은 재현율로 인해 발생하는 결과 집합의 오탐 수를 줄이는 방법을 보여주었습니다. 검색기에 대해 자세히 알아보려면 이 <a href="https://www.elastic.co/search-labs/blog/elasticsearch-retrievers">블로그 게시물</a>과 <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">Elasticsearch 설명서</a>를 참조하세요.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/semantic-precision-minimum-score</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/semantic-precision-minimum-score</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[하이브리드 검색]]></category>
    <dc:creator><![CDATA[Mattias Brunnert]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4a3fba607900049/6a170e0fcdacbf8fe17d2a7a/8b3b5910abfe16d48d309341a0027008b16c4340-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[판단 목록을 사용하여 검색 쿼리의 관련성을 평가합니다.]]></title>
    <description><![CDATA[Elasticsearch에서 확장 가능한 검색 테스트를 위해 검색 쿼리 관련성을 객관적으로 평가하고 리콜과 같은 성능 메트릭을 개선하기 위해 판단 목록을 구축하는 방법을 살펴보세요.]]></description>
    <content:encoded><![CDATA[<p>검색 엔진을 개발하는 개발자들은 종종 같은 문제를 겪습니다. 비즈니스 팀이 특정 검색 결과에 만족하지 못하는데, 기대했던 문서가 검색 결과 상단이 아니라 3~4번째에 표시되기 때문입니다.</p><p>그러나 모든 케이스를 수동으로 테스트할 수 없기 때문에, 한 가지 문제를 해결하면 실수로 다른 쿼리가 손상될 수 있습니다. 하지만 특정 쿼리를 변경했을 때 다른 쿼리에 파급 효과를 미치는지 사용자 또는 QA 팀이 어떻게 테스트할 수 있을까요? 더 중요한 것은 변경 사항이 실제로 쿼리를 개선했는지 어떻게 확인할 수 있을까요?</p><h2>체계적인 평가를 향해</h2><p>이때 판단 목록이 유용하게 작용합니다. 변경할 때마다 수동 및 주관적 테스트에 의존하는 대신, 비즈니스 사례와 관련된 고정된 쿼리 세트와 해당 결과를 정의할 수 있습니다.</p><p>이 세트가 기준이 됩니다. 변경 사항을 구현할 때마다 검색이 실제로 개선되었는지 여부를 평가합니다.</p><p>이 접근 방식의 가치는 다음과 같습니다:</p><ul><li><p><strong>불확실성 제거</strong>: 변경 사항이 다른 쿼리에 영향을 미치는지를 데이터가 알려줍니다.</p></li><li><p><strong>수동 테스트 중지</strong>: 판단 세트가 기록되면 테스트가 자동으로 진행됩니다.</p></li><li><p><strong>변경 지원</strong>: 변경의 이점을 뒷받침하는 명확한 메트릭을 표시할 수 있습니다.</p></li></ul><h2>판단 목록을 만드는 방법</h2><p>가장 쉽게 시작하는 방법 중 하나는 대표 검색어를 선택하고 관련 문서를 수동으로 고르는 것입니다. 이 목록을 작성할 때 두 가지 방법을 사용할 수 있습니다.</p><ul><li><p><strong>이진 판단:</strong> 쿼리와 연관된 각 문서에 <em>관련 있음</em>(보통 점수 1)과 <strong>관련 없음</strong>(0)이라는 간단한 태그가 붙습니다.</p></li><li><p><strong>등급별 판단:</strong> 여기서는 문서마다 다른 수준의 점수가 매겨집니다. 예를 들어, <a href="https://en.wikipedia.org/wiki/Likert_scale">Likert 척도</a>와 유사하게 0~4 척도를 설정해 0은 '전혀 관련 없음', 4는 '매우 관련 있음'으로 정의하고, 그 사이에 '관련 있음', '어느 정도 관련 있음'과 같은 단계를 둘 수 있습니다.</p></li></ul><p>이진 판단은 '이 문서가 결과에 포함되어야 하는가?'처럼 검색 의도에 명확한 한계가 있을 때 잘 작동합니다.</p><p>등급별 판단은 결과가 애매한 영역이 있을 때 더 유용합니다. 어떤 결과는 다른 결과보다 더 좋으므로 '매우 좋음', '좋음', '쓸모없음' 결과를 얻고, 결과의 순서와 사용자의 피드백을 중요시하는 메트릭을 사용할 수 있습니다. 그러나 등급 척도는 검토자마다 점수 수준을 다르게 사용해 판단의 일관성이 떨어질 수 있다는 단점도 갖고 있습니다. 또한 등급별 메트릭은 점수가 높으면 가중치를 더 많이 부여하기 때문에 작은 변화(예: 4점 대신 3점으로 평가)로도 검토자가 의도한 것보다 훨씬 더 큰 변화를 일으킬 수 있습니다. 이렇게 주관적인 요소가 추가되면 시간이 지남에 따라 등급별 판단이 더 복잡하고 관리하기 어려워집니다.</p><h2>문서를 직접 분류해야 하나요?</h2><p>반드시 그렇지는 않습니다. 판단 목록을 만드는 데는 여러 가지 방법이 있고 각각 고유한 장점과 단점이 있기 때문입니다.</p><ul><li><p><strong>명시적 판단:</strong> 여기서 중소기업은 각 쿼리/문서를 검토하고 관련성 여부(또는 방법)를 수동으로 결정합니다. 이는 품질과 제어 측면에서 장점을 제공하지만 확장성이 떨어집니다.</p></li><li><p><strong>암묵적 판단:</strong> 이 방법은 클릭, 이탈률, 구매 등 실제 사용자 행동을 기반으로 관련 문서를 추론합니다. 이 방식을 사용하면 데이터를 자동으로 수집할 수 있지만 편향적일 수 있습니다. 예를 들어, 사용자들은 관련성이 없더라도 검색 결과 상단에 있는 항목을 더 자주 클릭하는 경향이 있습니다.</p></li><li><p><strong>AI 생성 판단:</strong> 이 마지막 옵션은 모델(예: LLM)을 사용하여 쿼리와 문서를 자동으로 평가하며, 흔히 <a href="https://en.wikipedia.org/wiki/LLM-as-a-Judge">LLM 배심원단</a>이라고도 합니다. 빠르고 쉽게 확장할 수 있지만, 데이터의 품질은 사용 중인 모델의 품질과 LLM 학습 데이터가 비즈니스 <a href="http://interests.as/">관심사</a>와 얼마나 잘 부합하는지에 달려 있습니다. 인간의 평가와 마찬가지로, LLM 배심원단 역시 편향이나 불일치를 도입할 수 있으므로 신뢰할 수 있는 소규모의 판단과 비교하여 결과를 검증하는 것이 중요합니다. LLM 모델은 본질적으로 확률적이기 때문에 <a href="https://www.ibm.com/think/topics/llm-temperature">온도</a> 파라미터를 0으로 설정해도 같은 결과에 대해 서로 다른 등급을 부여하는 경우가 많습니다.</p></li></ul><p>다음은 판단 세트를 만드는 가장 좋은 방법을 선택하기 위한 몇 가지 권장 사항입니다.</p><ul><li><p>가격, 브랜드, 언어, 스타일, 제품 세부정보 등 사용자만 제대로 판단할 수 있는 일부 기능의 중요도를 결정합니다. 중요한 사항이라면 최소한 <em>판단 목록</em>의 일부에 대한 <strong>명시적인 판단</strong>이 필요합니다.</p></li><li><p>검색 엔진 트래픽이 이미 충분하여 클릭, 전환, 체류 시간 등의 지표를 활용해 사용 추세를 파악할 수 있는 경우, <strong>암묵적 판단</strong>을 사용하세요. 하지만 편향(예: 사용자는 하위 순위의 결과가 더 관련성이 높더라도 상위 순위의 결과를 더 자주 클릭하는 경향이 있음)을 방지하기 위해 명시적인 판단 기준과 대조하여 신중하게 해석해야 합니다.</p></li></ul><p>이를 해결하기 위해, 위치 편향 제거 기술이 클릭 데이터를 조정하거나 가중치를 조정하여 사용자의 진정한 관심을 더 잘 반영합니다. 몇 가지 접근 방식은 다음과 같습니다.</p><ul><li><p><strong>결과 순서 변경</strong>: 일부 사용자를 대상으로 검색 결과 순서를 변경하여 순위가 클릭에 미치는 영향을 추정합니다.</p></li><li><p><strong>클릭 모델</strong>에는<a href="https://wiki.math.uwaterloo.ca/statwiki/index.php?title=a_Dynamic_Bayesian_Network_Click_Model_for_web_search_ranking">동적 베이지안 네트워크 </a><a href="https://wiki.math.uwaterloo.ca/statwiki/index.php?title=a_Dynamic_Bayesian_Network_Click_Model_for_web_search_ranking"><strong>DBN</strong></a>, <a href="https://rsrikant.com/papers/kdd10.pdf">사용자 브라우징 모델 </a><a href="https://rsrikant.com/papers/kdd10.pdf"><strong>UBM</strong></a>이 포함됩니다. 이러한 통계 모델은 스크롤, 체류 시간, 클릭 순서 및 결과 페이지로 돌아가는 등의 패턴을 사용하여 클릭이 단순한 위치가 아니라 실제 관심을 반영할 확률을 추정합니다.</p></li></ul><h2>예시: 영화 평점 앱</h2><h3>필수 구성 요소</h3><p>이 예시를 실행하려면 Elasticsearch 8.x 클러스터가 <a href="https://www.elastic.co/downloads/elasticsearch">로컬</a>이나 <a href="https://www.elastic.co/cloud/cloud-trial-overview">Elastic Cloud Hosted</a>(호스팅 또는 서버리스)를 실행하고, <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis">REST API</a> 또는 Kibana에 접근할 수 있어야 합니다.</p><p>사용자가 영화에 대한 의견을 업로드하고 볼 영화를 검색할 수 있는 앱을 생각해 보세요. 사용자들이 직접 작성한 글이기 때문에 오타가 있거나, 표현 방식이 다양할 수 있습니다. 따라서 검색 엔진은 이러한 다양성을 해석하고 사용자에게 유용한 결과를 제공할 수 있어야 합니다.</p><p>전반적인 검색 동작에 영향을 주지 않고 쿼리를 반복적으로 수정할 수 있도록, 비즈니스 팀에서 가장 빈번하게 발생하는 검색어를 기반으로 다음과 같은 이진 판단 기준을 만들었습니다.</p><p>쿼리</p><p>DocID</p><p>텍스트</p><p>디카프리오의 연기</p><p>doc1</p><p>디카프리오의 '레버넌트: 죽음에서 돌아온 자'에서의 연기는 놀라웠다.</p><p>디카프리오의 연기</p><p>doc2</p><p>인셉션에서는 레오나르도 디카프리오가 그의 가장 상징적인 연기를 보여줍니다.</p><p>디카프리오의 연기</p><p>doc3</p><p>브래드 피트는 이 범죄 스릴러에서 탄탄한 연기를 선보입니다.</p><p>디카프리오의 연기</p><p>doc4</p><p>놀라운 시각 효과와 함께 액션으로 가득한 모험을 경험할 수 있다.</p><p>눈물을 흘리게 하는 슬픈 영화</p><p>doc5</p><p>가슴 아픈 사랑과 상실에 대한 이야기로 몇 시간 동안 눈물을 흘렸습니다.</p><p>눈물을 흘리게 하는 슬픈 영화</p><p>doc6</p><p>휴지가 꼭 필요한 슬픈 영화</p><p>눈물을 흘리게 하는 슬픈 영화</p><p>doc7</p><p>웃음을 자아내는 경쾌한 코미디 영화</p><p>눈물을 흘리게 하는 슬픈 영화</p><p>doc8</p><p>액션과 흥분으로 가득 찬 SF 대작.</p><p>인덱스 생성:</p>PUT movies
{
  "mappings": {
    "properties": {
      "text": {
        "type": "text"
      }
    }
  }
}<p>대량 요청:</p>POST /movies/_bulk
{ "index": { "_id": "doc1" } }
{ "text": "DiCaprio performance in The Revenant was breathtaking." }
{ "index": { "_id": "doc2" } }
{ "text": "Inception shows Leonardo DiCaprio in one of his most iconic roles." }
{ "index": { "_id": "doc3" } }
{ "text": "Brad Pitt delivers a solid performance in this crime thriller." }
{ "index": { "_id": "doc4" } }
{ "text": "An action-packed adventure with stunning visual effects." }
{ "index": { "_id": "doc5" } }
{ "text": "A heartbreaking story of love and loss that made me cry for hours." }
{ "index": { "_id": "doc6" } }
{ "text": "One of the saddest movies ever made -- bring tissues!" }
{ "index": { "_id": "doc7" } }
{ "text": "A lighthearted comedy that will make you laugh." }
{ "index": { "_id": "doc8" } }
{ "text": "A science-fiction epic full of action and excitement." }<p>아래는 앱이 사용하는 Elasticsearch 쿼리입니다.</p>GET movies/_search
{
 "query": {
   "match": {
     "text": {
       "query": "DiCaprio performance",
       "minimum_should_match": "100%"
     }
   }
 }
}<h3>판단에서 지표로</h3><p>판단 목록은 그 자체로 많은 정보를 제공하지 않으며, 단지 쿼리 결과를 예상할 수 있을 뿐입니다. 그러나 검색 성능을 측정하기 위해 객관적인 메트릭을 계산할 때가 되면 판단 목록이 그 진가를 발휘합니다.</p><p>요즘 가장 많이 사용되는 메트릭은 다음과 같습니다.</p><ul><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-precision"><strong>정확도</strong></a><strong>: </strong>전체 검색 결과 중 실제 관련성이 있는 결과의 비율을 측정합니다.</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall"><strong>리콜</strong></a><strong>: </strong>검색 엔진이 찾은 x개의 결과 중에서 관련성 있는 결과의 비율을 측정합니다.</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_discounted_cumulative_gain_dcg"><strong>할인 누적 이득(DCG):</strong></a>결과 순위의 품질을 측정하며, 가장 관련성이 높은 결과가 최상위에 있어야 한다고 판단합니다.</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_mean_reciprocal_rank"><strong>평균 상호 순위(MRR):</strong></a> 첫 번째 관련 결과의 위치를 측정합니다. 목록에서 높은 위치에 있을수록 점수가 높아집니다.</p></li></ul><p>동일한 영화 평점 앱을 예로 들어 리콜 메트릭을 계산하여 쿼리에서 누락된 정보가 있는지 확인해 보겠습니다.</p><p>Elasticsearch에서는 <em>판단 목록</em>을 사용하여 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval">순위 평가 API</a>를 통해 메트릭을 계산할 수 있습니다. 이 API는 판단 목록, 쿼리, 평가하려는 메트릭을 입력으로 받아 쿼리 결과와 판단 목록을 비교한 값을 반환합니다.</p><p>두 개의 쿼리에 대한 판단 목록을 실행해 보겠습니다.</p>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}<p>'디카프리오' 쿼리와 '슬픈 영화' 쿼리, 두 가지 요청을 _rank_eval에 사용하겠습니다. 각 요청에는 쿼리와 해당 판단 목록(평가)이 포함됩니다. 평가에 포함되지 않은 문서는 판단이 없는 것으로 간주되므로 모든 문서에 등급을 매길 필요는 없습니다. 계산을 수행할 때 리콜은 평가에서 관련성이 있는 것으로 간주되는 문서인 '관련 세트'만 고려합니다.</p><p>이 경우, '디카프리오' 쿼리의 리콜은 1이고 '슬픈 영화'는 0입니다. 즉, 첫 번째 쿼리에서는 모든 관련 결과를 얻을 수 있었지만 두 번째 쿼리에서는 아무런 결과도 얻지 못했습니다. 따라서 평균 리콜은 0.5입니다.</p>{
 "metric_score": 0.5,
 "details": {
   "dicaprio-performance": {
     "metric_score": 1,
     "unrated_docs": [],
     "hits": [
       {
         "hit": {
           "_index": "movies",
           "_id": "doc1",
           "_score": 2.4826927
         },
         "rating": 1
       },
       {
         "hit": {
           "_index": "movies",
           "_id": "doc2",
           "_score": 2.0780432
         },
         "rating": 1
       }
     ],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 2,
         "relevant_docs": 2
       }
     }
   },
   "sad-movies": {
     "metric_score": 0,
     "unrated_docs": [],
     "hits": [],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 0,
         "relevant_docs": 2
       }
     }
   }
 },
 "failures": {}
}<p>쿼리의 모든 단어가 문서에 100% 포함되어야 한다고 요구하면 <strong>minimum_should_match</strong> 매개변수를 너무 엄격하게 적용해 관련성 있는 결과를 놓치고 있는 것일지도 모릅니다. 쿼리에서 단어가 하나만 발견되면 문서가 관련성이 있는 것으로 간주되도록 <strong>minimum_should_match</strong> 매개변수를 제거해 보겠습니다.</p>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}<p>보시다시피, 두 쿼리 중 하나에서 <strong>minimum_should_match</strong> 매개 변수를 제거하면 두 쿼리 모두에서 평균 호출 횟수가 1이 됩니다.</p>{
  "metric_score": 1,
  "details": {
    "dicaprio-performance": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc1",
            "_score": 2.0661702
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc3",
            "_score": 0.732218
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc2",
            "_score": 0.6271719
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    },
    "sad-movies": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc7",
            "_score": 2.1307156
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc5",
            "_score": 1.3160692
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc6",
            "_score": 1.190063
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    }
  },
  "failures": {}
}<p>요약하면, minimum_should_match: 100% 절을 제거하면 두 쿼리 모두에 대해 완벽한 리콜을 얻을 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaf4f08a8a2915180/6a170df61949f76cbfe7aaba/24d055da4348c63827ba7046fe8cafb6f47cadd8-546x628.png" alt="" /><p>됐습니다! 성공하셨죠?</p><p>그렇게 어렵지 않아요!</p><p>리콜을 개선함으로써 더 폭넓은 결과를 확보할 수 있게 됩니다. 그러나 각 조정에는 상충되는 부분이 있습니다. 따라서 다양한 메트릭을 사용해 변경 사항을 평가하는 완전한 테스트 케이스를 정의해야 합니다.</p><p>판단 목록과 메트릭을 사용하면 변경 사항을 적용할 때 근거 데이터가 생기므로, 감에 의존해 판단하는 일을 피할 수 있습니다. 검증은 더 이상 수동적이고 반복적이지 않으며, 한 가지 사용 사례뿐만 아니라 여러 사용 사례에서 변경 사항을 테스트할 수 있습니다. 번역:또한 A/B 테스트를 통해 어떤 설정이 사용자와 비즈니스 사례에 가장 적합한지 실제 환경에서 검증할 수 있어, 기술적 메트릭에서 출발해 실제 메트릭으로 다시 연결되는 선순환을 완성할 수 있습니다.</p><h2>판단 목록 사용에 대한 최종 권장 사항</h2><p>판단 목록을 활용하는 것은 단순히 측정하는 것뿐만 아니라, 자신 있게 반복할 수 있는 프레임워크를 만드는 것이기도 합니다. 이를 위해 다음 권장 사항을 따르세요:</p><ol><li><p><strong>작은 시작이라도, 시작하세요</strong>. 각각 50개의 판단 목록이 있는 10,000개의 쿼리가 필요하지 않습니다. 비즈니스에 가장 중요한 쿼리를 5~10개 식별하고 결과의 상단에 표시할 문서를 정의하기만 하면 됩니다. 이것으로 이미 기반이 갖춰집니다. 일반적으로 상위 쿼리와 결과가 없는 쿼리부터 시작하는 것이 좋습니다. Precision과 같이 쉽게 구성할 수 있는 메트릭으로 테스트를 시작한 후, 복잡성을 높여 나갈 수 있습니다.</p></li><li><p><strong>사용자 검증을 통해 타당성을 검증하세요.</strong> 프로덕션 환경에서 A/B 테스트를 통해 수치를 보완하세요. 이렇게 하면 메트릭에서 좋아 보이던 변경 사항이 실제 영향을 발휘하고 있는지 알 수 있습니다.</p></li><li><p><strong>목록을 계속 유지하세요.</strong> 비즈니스 사례는 진화할 것이며 중요한 쿼리도 진화할 것입니다. 정기적으로 판단을 업데이트하여 새로운 필요를 반영하세요.</p></li><li><p><strong>워크플로의 일부로 만드세요.</strong> 판단 목록을 개발 파이프라인에 통합하세요. 각 구성 변경, 동의어 또는 텍스트 분석이 기본 목록에 대해 자동으로 검증되는지 확인하세요.</p></li><li><p><strong>기술 지식을 전략과 연결하세요.</strong> 정확도나 리콜과 같은 기술적 메트릭을 측정하는 데서 멈추지 마세요. 평가 결과를 활용하여 비즈니스 성과에 전달하세요.</p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/judgment-lists-search-query-relevance-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/judgment-lists-search-query-relevance-elasticsearch</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[Elastic 내부]]></category>
    <dc:creator><![CDATA[Jhon Guzmán]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcadfd2fb1cc95b4c/6a170df7acf0887798be9bd0/25478d0ffb228afd5d65d82312998ec1c299c565-700x490.png" length="0" type="image/png"/>
    <pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[번거로움 없는 하이브리드 검색: 검색기를 사용한 하이브리드 검색 간소화]]></title>
    <description><![CDATA[선형 및 RRF 검색기를 위한 다중 필드 쿼리 형식을 사용해 Elasticsearch에서 하이브리드 검색을 간소화하는 방법을 살펴보고, Elasticsearch 인덱스에 대한 사전 지식 없이도 쿼리를 생성하는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/what-is/hybrid-search">하이브리드 검색은</a> <a href="https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch#lexical-search---sparse-retrieval">어휘 검색의</a> 정확성과 속도에 시맨틱 <a href="https://www.elastic.co/what-is/semantic-search">검색의</a> 자연어 기능을 결합한 강력한 검색 방식으로 널리 알려져 있습니다. 그러나 실제로 적용하는 것은 까다로울 수 있으며, 종종 인덱스에 대한 깊은 지식이 필요하고 간단한 구성이 아닌 장황한 쿼리를 구성해야 합니다. 이 블로그에서는 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">선형 및 RRF 검색기를 위한 다중 필드 쿼리 형식이</a> 어떻게 하이브리드 검색을 더 간단하고 접근하기 쉽게 만들어 일반적인 골칫거리를 없애고 그 모든 기능을 더 쉽게 활용할 수 있게 해주는지 살펴봅니다. 또한 다중 필드 쿼리 형식을 통해 인덱스에 대한 사전 지식 없이도 하이브리드 검색 쿼리를 수행할 수 있는 방법을 검토합니다.</p><h2>점수 범위 문제</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3521a6558cd477ca/6a17f174445de94d3c4d0225/c8b49153c47d2cdc233c0d2e440db04711d48ca5-1600x1600.jpg" alt="" /><p>하이브리드 검색이 어려운 주요 이유 중 하나인 다양한 점수 범위를 살펴보기 위해 무대를 설정해 보겠습니다. 우리의 오랜 친구 <a href="https://www.elastic.co/elasticon/conf/2016/sf/improved-text-scoring-with-bm25">BM25는</a> 무한한 점수를 만들어냅니다. 즉, BM25는 0에 가까운 점수부터 (이론적으로) 무한대까지 다양한 점수를 생성할 수 있습니다. 반대로 <code>dense_vector</code> 필드에 대한 쿼리는 0과 1 사이의 점수를 생성합니다. 이 문제를 더욱 악화시키는 <code>semantic_text</code> 은 임베딩을 색인하는 데 사용되는 필드 유형을 난독화하므로 색인 및 추론 엔드포인트 구성에 대한 자세한 지식이 없으면 쿼리의 점수 범위를 알기 어려울 수 있습니다. 이는 어휘 검색 결과와 의미 검색 결과를 통합하려고 할 때 문제가 되는데, 의미 검색 결과가 더 관련성이 높더라도 어휘 검색 결과가 의미 검색 결과보다 우선할 수 있기 때문입니다. 이 문제에 대한 일반적인 해결책은 결과를 인터리빙하기 전에 점수를 정규화하는 것입니다. 이를 위한 두 가지 도구, <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever">선형</a> 검색기와 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/rrf-retriever">RRF</a> 검색기가 있습니다.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09ed6bf3d25066bd/6a17f1750b0bedbd70dd3686/264481268c8b6ac259e3c257b85431b513f16672-1077x586.png" alt="선형/알고리즘을 사용한 검색 결과와 사용하지 않은 검색 결과 비교" /><p><strong>RRF</strong> 검색기는 문서 순위를 관련성 측정값으로 사용하고 점수를 버리는 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF 알고리즘을</a> 적용합니다. 점수는 고려되지 않으므로 점수 범위 불일치는 문제가 되지 않습니다.</p><p><strong>리니어</strong> 리트리버는 선형 조합을 사용하여 문서의 최종 점수를 결정합니다. 여기에는 문서에 대한 각 구성 요소 쿼리의 점수를 가져와서 정규화한 다음 합산하여 총 점수를 생성하는 작업이 포함됩니다. 수학적으로 이 연산은 다음과 같이 표현할 수 있습니다:</p>Total Score = 𝚺(N(Sx))<p>여기서 <code>N</code> 은 정규화 함수이고 SX 는 쿼리 X 의 점수입니다. 정규화 함수는 각 쿼리의 점수를 동일한 범위를 사용하도록 변환하기 때문에 여기서 핵심적인 역할을 합니다. 리니어 리트리버에 대해 자세히 알아보려면 <a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">여기를</a> 참조하세요.</p><h2>분석하기</h2><p>사용자는 이러한 도구를 사용하여 효과적인 하이브리드 검색을 구현할 수 있지만 색인에 대한 약간의 지식이 필요합니다. 두 개의 필드가 있는 인덱스를 쿼리하는 리니어 리트리버의 예를 살펴보겠습니다:</p>PUT linear_retriever_example
{
  "mappings": {
    "properties": {
      "semantic_text_field": { &lt;1&gt;
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elasticsearch"
      },
      "text_field": { &lt;2&gt;
        "type": "text"
      }
    }
  }
}<p>1. <code>semantic_text_field</code> 는 텍스트 임베딩 모델인 <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">E5를</a> 사용하는 <code>semantic_text</code> 필드입니다.</p><p><code>text_field</code> 는 표준 <code>text</code> 필드입니다.</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "match": { &lt;1&gt;
                  "semantic_text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}<p>1. <code>semantic_text</code> 필드에서 <code>match</code> 쿼리를 사용하며, <a href="https://www.elastic.co/search-labs/blog/semantic-search-match-knn-sparse-vector#we-made-match-happen-in-semantic-search!">Elasticsearch 8.18/9.0에서 지원이 추가되었습니다</a>.</p><p>
쿼리를 구성할 때 <code>semantic_text_field</code> 은 텍스트 임베딩 모델을 사용하므로 이 쿼리에 대한 모든 쿼리는 0에서 1 사이의 점수를 생성한다는 점을 염두에 두어야 합니다. 또한 <code>text_field</code> 은 표준 <code>text</code> 필드이므로 이 필드로 쿼리하면 무제한 점수가 생성된다는 점도 알아야 합니다. 적절한 연관성을 가진 결과 집합을 만들려면 쿼리 점수를 결합하기 전에 정규화하는 리트리버를 사용해야 합니다. 이 예에서는 각 쿼리의 점수를 0과 1 사이의 값으로 정규화하는 <code>minmax</code> 정규화와 함께 선형 검색기를 사용합니다.</p><p>이 예제의 쿼리 구성은 두 개의 필드만 관련되어 있기 때문에 매우 간단합니다. 그러나 더 많은 필드와 다양한 유형이 추가되면 매우 빠르게 복잡해질 수 있습니다. 이는 효과적인 하이브리드 검색 쿼리를 작성하려면 쿼리 대상 인덱스에 대한 심층적인 지식이 필요한 경우가 많으므로 구성 요소 쿼리 점수를 조합하기 전에 적절하게 정규화해야 한다는 것을 보여줍니다. 이는 하이브리드 검색의 광범위한 채택을 가로막는 장벽이 됩니다.</p><h3>쿼리 그룹화</h3><p>예제를 확장해 보겠습니다: 하나의 <code>text</code> 필드와 두 개의 <code>semantic_text</code> 필드를 쿼리하려면 어떻게 해야 할까요? 다음과 같이 쿼리를 작성할 수 있습니다:</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_1",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_2",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}<p>겉으로 보기에는 좋아 보이지만 잠재적인 문제가 있습니다. 이제 <code>semantic_text</code> 필드 매치가 전체 점수의 ⅔를 차지합니다:</p>Total Score = N(semantic_text_field_1 score) + N(semantic_text_field_2 score) + N(text_field score)<p>이는 불균형한 점수를 만들기 때문에 원하지 않을 수도 있습니다. 필드가 3개만 있는 이 예제에서는 그 영향이 눈에 띄지 않을 수 있지만, 더 많은 필드가 쿼리되면 문제가 됩니다. 예를 들어, 대부분의 인덱스에는 시맨틱 필드보다 훨씬 더 많은 어휘 필드가 포함되어 있습니다(예 <code>dense_vector</code>, <code>sparse_vector</code>, 또는 <code>semantic_text</code>). 위의 패턴을 사용하여 어휘 필드 9개와 의미 필드 1개가 있는 인덱스를 쿼리한다면 어떨까요? 어휘 일치 항목이 점수의 90% 을 차지하여 의미론적 검색의 효과를 무색하게 만들었습니다.</p><p>이 문제를 해결하는 일반적인 방법은 쿼리를 어휘 및 의미 범주로 그룹화하고 두 범주에 균등하게 가중치를 부여하는 것입니다. 이렇게 하면 어느 한 카테고리가 총점을 지배하는 것을 방지할 수 있습니다.</p><p>이를 실천에 옮겨 보겠습니다. 이 예제에서 리니어 리트리버를 사용할 때 그룹화된 쿼리 접근 방식은 어떤 모습일까요?</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "linear": {
              "retrievers": [
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_1",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                },
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_2",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                }
              ]
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}<p>와, 점점 장황해지네요! 전체 쿼리를 살펴보기 위해 위아래로 여러 번 스크롤해야 했을 수도 있습니다! 여기서는 두 가지 수준의 정규화를 사용하여 쿼리 그룹을 생성합니다. 수학적으로는 다음과 같이 표현할 수 있습니다:</p>Total Score = N(N(semantic_text_field_1 score) + N(semantic_text_field_2 score)) + N(text_field score)<p>이 두 번째 정규화 수준은 <code>semantic_text</code> 필드와 <code>text</code> 필드에 대한 쿼리의 가중치가 균등하게 적용되도록 합니다. 이 예제에서는 어휘 필드가 하나뿐이므로 <code>text_field</code> 에 대한 두 번째 수준 정규화를 생략하여 <em>더</em> 자세한 설명을 생략했습니다.</p><p>이 쿼리 구조는 이미 다루기 힘든데다 세 개의 필드만 쿼리하고 있습니다. 더 많은 필드를 쿼리할수록 숙련된 검색 실무자라도 관리하기가 점점 더 어려워집니다.</p><h2>다중 필드 쿼리 형식</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5d9007d279f0da29/6a17f17714d90c39cf79b6f5/dd04e1686076a574b717c1460acfe4eb79299208-1600x1600.jpg" alt="" /><p>이 모든 것을 간소화하기 위해 Elasticsearch 8.19, 9.1 및 <a href="https://www.elastic.co/cloud/serverless">서버리스에서 선형</a> <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">및 RRF 검색기에 대한 다중 필드 쿼리 형식을 추가했습니다.</a> 이제 위와 동일한 쿼리를 수행할 수 있습니다:</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>쿼리가 55줄에서 단 9줄로 줄어듭니다! Elasticsearch는 인덱스 매핑을 자동으로 사용합니다:</p><ul><li><p>쿼리되는 각 필드의 유형을 결정합니다.</p></li><li><p>각 필드를 어휘 또는 의미론적 범주로 그룹화합니다.</p></li><li><p>최종 점수에서 각 카테고리에 균등하게 가중치를 부여합니다.</p></li></ul><p>이를 통해 누구나 인덱스나 사용된 추론 엔드포인트에 대한 세부 정보를 몰라도 효과적인 하이브리드 검색 쿼리를 실행할 수 있습니다.</p><p>RRF를 사용하는 경우 순위가 관련성의 프록시로 사용되므로 <code>normalizer</code> 을 생략할 수 있습니다:</p>GET rrf_retriever_example/_search
{
  "retriever": {
    "rrf": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo"
    }
  }
}<h2>필드별 부스팅</h2><p>리니어 리트리버를 사용할 때 필드별 부스트를 적용하여 특정 필드에서 경기의 중요도를 조정할 수 있습니다. 예를 들어 <code>semantic_text</code> 필드 2개와 <code>text</code> 필드 2개 등 4개의 필드를 쿼리한다고 가정해 보겠습니다:</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field_1", "text_field_2" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>기본적으로 각 필드는 해당 그룹(어휘 또는 의미)에서 동일하게 가중치가 부여됩니다. 점수 분석은 다음과 같습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdc6e197e5ff7b68d/6a17f179505ac32834ad8c46/ba31c76189e3a1e5b1638437ccf0528aafec2598-1600x549.png" alt="쿼리 그룹 및 필드 점수 비교" /><p>즉, 각 필드는 총 점수의 25% %입니다.</p><p><code>field^boost</code> 구문을 사용하여 모든 필드에 필드별 부스트를 추가할 수 있습니다. <code>semantic_text_field_1</code> 와 <code>text_field_1</code> 에 2의 부스트를 적용해 보겠습니다:</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1^2", "semantic_text_field_2", "text_field_1^2", "text_field_2" ]
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>이제 점수 분석은 다음과 같습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccc9ed7d6e865a5e/6a17f17abe6086a31d004882/de20e555d52f914bf483a048d056f54f4fece757-1600x549.png" alt="참조 및 하이브리드 검색으로 변경된 필드 가중치" /><p>각 쿼리 그룹은 여전히 동일하게 가중치가 적용되지만 이제 그룹 내 필드 가중치가 변경되었습니다:</p><ul><li><p><code>semantic_text_field_1</code> 시맨틱 쿼리 그룹 점수는 66%, 총 점수는 33% 입니다.</p></li><li><p><code>text_field_1</code> 는 어휘 쿼리 그룹 점수 66%, 총 점수 33% 입니다.</p></li></ul><p>ℹ️ 필드별 부스트가 적용되어도 총 점수 범위는 변경되지 않습니다. 이는 점수 정규화의 의도된 부작용으로, 어휘 및 의미론적 쿼리 점수가 서로 직접 비교 가능한 상태로 유지되도록 합니다.</p><p>ℹ️ 필드별 부스팅은 Elasticsearch 9.2+의 RRF 리트리버와 함께 사용할 수도 있습니다.</p><h3>와일드카드 해상도</h3><p><code>fields</code> 매개변수에 <code>*</code> 와일드카드를 사용하여 여러 필드를 일치시킬 수 있습니다. 위의 예를 계속 이어서, 이 쿼리는 기능적으로<code>emantic_text_field_1</code>, <code>semantic_text_field_2</code>, <code>text_field_1</code> 을 명시적으로 쿼리하는 것과 동일합니다:</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_*", "*_field_1" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p><code>*_field_1</code> 패턴이 <code>text_field_1</code> 및 <code>semantic_text_field_1</code> 와 모두 일치한다는 점이 흥미롭습니다. 이는 자동으로 처리되며, 각 필드가 명시적으로 쿼리된 것처럼 쿼리가 실행됩니다. <code>semantic_text_field_1</code> 이 두 패턴과 모두 일치해도 괜찮습니다. 모든 필드 이름 일치 항목은 쿼리 실행 전에 중복이 제거됩니다.</p><p>와일드카드는 다양한 방법으로 사용할 수 있습니다:</p><ul><li><p>접두사 일치(예: <code>*_text_field</code>)</p></li><li><p>인라인 매칭(예: <code>semantic_*_field</code>)</p></li><li><p>접미사 일치(예: <code>semantic_text_field_*</code>)</p></li></ul><p><code>*_text_field_*</code> 와 같이 여러 와일드카드를 사용하여 위의 조합을 적용할 수도 있습니다.</p><h3>기본 쿼리 필드</h3><p>다중 필드 쿼리 형식을 사용하면 전혀 모르는 인덱스를 쿼리할 수도 있습니다. <code>fields</code> 매개변수를 생략하면 <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules">index.query.default_field 인덱스 설정으로</a> 지정된 모든 필드를 쿼리합니다:</p>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}<p>기본적으로 <code>index.query.default_field</code> 은 <code>*</code> 으로 설정되어 있습니다. 이 와일드카드는 용어 쿼리를 지원하는 인덱스의 모든 필드 유형(대부분)으로 확인합니다. 예외는 있습니다:</p><ul><li><p><code>dense_vector</code> 필드</p></li><li><p><code>rank_vector</code> 필드</p></li><li><p>지오메트리 필드: <code>geo_point</code>, <code>shape</code></p></li></ul><p>이 기능은 타사에서 제공하는 색인에 대해 하이브리드 검색 쿼리를 수행하려는 경우에 특히 유용합니다. 다중 필드 쿼리 형식을 사용하면 간단한 방법으로 적절한 쿼리를 실행할 수 있습니다. <code>fields</code> 매개변수만 제외하면 해당되는 모든 필드가 쿼리됩니다.</p><h2>결론</h2><p>점수 범위 문제로 인해 효과적인 하이브리드 검색을 구현하기가 어려울 수 있으며, 특히 쿼리되는 인덱스나 사용 중인 추론 엔드포인트에 대한 인사이트가 제한적인 경우 더욱 그렇습니다. 선형 및 RRF 검색기를 위한 다중 필드 쿼리 형식은 자동화된 쿼리 그룹화 기반의 하이브리드 검색 방식을 간단하고 접근하기 쉬운 API로 패키징하여 이러한 어려움을 덜어줍니다. 필드별 부스팅, 와일드카드 해상도 및 기본 쿼리 필드와 같은 추가 기능을 통해 다양한 사용 사례를 포괄하는 기능을 확장할 수 있습니다.</p><h2>지금 바로 다중 필드 쿼리 형식을 사용해 보세요.</h2><p><a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project">무료</a> 체험 판으로 완전 <a href="https://www.elastic.co/cloud/serverless">관리형 Elasticsearch 서버리스</a> 프로젝트에서 다중 필드 쿼리 형식의 선형 및 RRF 검색기를 확인해 보세요. 8.19 &amp; 9.1부터 스택 버전으로도 사용할 수 있습니다.</p><p>명령 한 번으로 로컬 환경에서 몇 분 안에 시작할 수 있습니다:</p>curl -fsSL https://elastic.co/start-local | sh<p></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</guid>
    <category><![CDATA[하이브리드 검색]]></category>
    <category><![CDATA[정확도]]></category>
    <dc:creator><![CDATA[Mike Pellegrini]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89e066372c549595/6a17f17c3e9e457c38ba1583/4494f98ae3958bbdbc6171df9677fc4d65ec5640-1536x1024.png" length="0" type="image/png"/>
    <pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[맥락을 위한 검색 - 1부: 하이브리드 검색과 맥락 엔지니어링의 진화]]></title>
    <description><![CDATA[하이브리드 검색과 컨텍스트 엔지니어링이 어휘 기반에서 어떻게 진화하여 차세대 에이전트 AI 워크플로우를 지원하는지 살펴보세요.]]></description>
    <content:encoded><![CDATA[<h2>새로운 에이전트 AI 세상</h2><p>다른 많은 사람들과 마찬가지로 저도 AI 기능이 발전하는 속도에 아찔함과 놀라움을 동시에 느낍니다. 대규모 언어 모델(LLM)과 벡터 검색을 통해 우리는 더 이상 키워드를 찾아 헤매지 않아도 되는 시맨틱 혁명을 맞이하게 되었습니다. 그런 다음 LLM은 채팅 인터페이스를 사용하여 자연어 요청을 방대한 지식 기반을 쉽게 사용할 수 있는 요약으로 변환하는 응답으로 변환하는 새로운 데이터 상호 작용 방법을 보여주었습니다. 우리는 지금 (이미!) 수신 요청을 의미론적으로 이해하고, 수행해야 할 단계를 추론한 다음, 해당 목표를 달성하기 위해 반복적으로 작업을 실행할 수 있는 도구를 선택할 수 있는 '에이전트 AI' 워크플로우의 형태로 자동화된 LLM 기반 로직의 시작을 알 수 있습니다.</p><p>에이전트 AI의 잠재력으로 인해 우리는 주로 '프롬프트 엔지니어링'을 사용하여 생성형 AI 상호작용을 형성하는 것에서 벗어나 에이전트 도구가 응답을 생성할 때 고려해야 하는 가장 관련성이 높고 효율적인 추가 정보를 얻을 수 있도록 돕는 방법, 즉 '맥락 엔지니어링'이 다음 개척 분야로 진화해야 합니다. 하이브리드 검색은 관련 컨텍스트를 표시하는 가장 강력하고 유연한 수단이며, Elastic의 검색 AI 플랫폼은 서비스 중인 데이터를 컨텍스트 엔지니어링에 활용할 수 있는 완전히 새로운 방법을 열어줍니다. 이 글에서는 LLM이 정보 검색의 세계를 어떻게 변화시켰는지 두 가지 각도에서 살펴본 다음, 더 나은 결과를 위해 어떻게 협력할 수 있는지에 대해 논의해 보겠습니다. 다뤄야 할 내용이 꽤 많습니다...</p><h2>1부: LLM이 검색을 바꾼 방법</h2><p>LLM이 정보에 액세스하고 검색하는 방식을 어떻게 변화시켰는지에 대한 관점에서 시작하겠습니다.</p><h3>어휘 유산</h3><p>우리는 모두 오랫동안 다소 제한적인 어휘 검색의 세계에서 (최선을 다해) 살아왔습니다. 검색은 새로운 프로젝트를 조사하거나 시작할 때마다 가장 먼저 찾는 도구로, 최근까지만 해도 어휘 검색 엔진이 이해할 수 있는 방식으로 쿼리를 표현하는 것은 전적으로 사용자의 몫이었습니다. 어휘 검색은 콘텐츠가 비정형인지 정형인지에 관계없이 문서 말뭉치에서 찾은 키워드에 어떤 형태의 쿼리 용어를 일치시키는 데 의존합니다. 어휘 검색이 문서를 히트로 반환하려면 해당 키워드와 일치하거나 동의어 목록이나 사전과 같이 개념적 연결을 위해 제어된 어휘가 있어야 합니다.</p>POST my-index/_search
{
  "size": 10,
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
}<p><em>어휘 </em><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query"><em>다중 일치</em></a><em> 쿼리</em>예제</p><p>적어도 검색 엔진은 관련성 점수가 있는 히트를 반환하는 기능이 있습니다. 검색 엔진은 색인된 데이터를 효과적으로 타겟팅할 수 있는 다양한 쿼리 구문 옵션과 사용자의 쿼리 구문 의도에 따라 결과를 점수화하는 기본 제공 관련성 알고리즘을 제공합니다. 검색 엔진은 수십 년간 발전해 온 관련성 순위 알고리즘의 이점을 활용하여 검색어와의 관련성에 따라 점수를 매기고 정렬된 결과를 제공할 수 있는 효율적인 데이터 검색 플랫폼이 되었습니다. SQL을 데이터 검색의 주요 방법으로 사용하는 데이터베이스 및 기타 시스템은 여기서 불리한 점이 있습니다. 데이터베이스 쿼리에는 관련성 개념이 없기 때문에 결과를 알파벳순 또는 숫자순으로 정렬하는 것이 최선입니다. 좋은 소식은 이러한 키워드로 모든 히트(리콜)를 얻을 수 있지만, 검색한 <em>이유</em> (정확도)에 비해 반드시 유용한 순서로 검색되는 것은 아니라는 점입니다. 이는 곧 살펴보겠지만 중요한 포인트입니다...</p><h3>(시맨틱) 용을 입력합니다.</h3><p>키워드 검색의 대안으로 정보를 벡터로 표현할 수 있는 가능성은 <a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">꽤 오래전부터</a> 연구되어 왔습니다. 벡터는 용어와 가중치를 숫자로 표현하기 때문에 학습 도메인에서 용어가 서로 어떻게 연관되는지에 대한 언어 모델의 이해를 바탕으로 개념을 수학적으로 가깝게 만들 수 있기 때문에 키워드만 사용하는 콘텐츠 매칭 모드에서 벗어날 수 있다는 점에서 많은 가능성을 가지고 있습니다. 범용 벡터 검색이 오래 지연된 것은 모델이 대부분 특정 도메인에 국한되어 있고, 용어가 다양한 맥락에서 나타낼 수 있는 다양한 개념을 충분히 이해하기에 충분히 크지 않았기 때문이었습니다.</p><p>벡터 검색이 실용화되기 시작한 것은 몇 년 전, 훨씬 더 많은 양의 데이터를 학습할 수 있는 대규모 언어 모델(LLM)이 등장하면서( <a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)">트랜스포머와</a> <a href="https://en.wikipedia.org/wiki/Attention_(machine_learning)">주의력을</a> 사용해) LLM의 크기와 깊이 덕분에 벡터가 의미론적 의미를 실제로 포착할 수 있는 충분한 뉘앙스를 저장할 수 있게 되었을 때였습니다. 이해의 깊이가 갑자기 증가함에 따라 LLM은 이전에는 잠겨 있던 수많은 자연어 처리(NLP) 기능을 제공할 수 있게 되었으며, 가장 영향력 있는 기능은 아마도 지금까지의 시퀀스 내용을 바탕으로 시퀀스에서 가장 가능성이 높은 다음 용어를 추론하는 기능일 것입니다. 추론은 제너레이티브 AI에 인간에 가까운 텍스트 생성 능력을 부여하는 과정입니다. AI가 생성한 텍스트는 학습 데이터 내에서 용어가 어떻게 연관되어 있는지에 대한 LLM의 이해를 기반으로 하며, 요청의 문구를 사용하여 용어가 나타날 수 있는 다양한 문맥을 명확히 구분합니다.</p><p>생성형 AI는 마법과도 같지만, 품질과 정확성에서 오류를 일으키는 LLM에는 흔히 환각이라고 불리는 한계가 <em>있습니다</em>. 환각은 LLM이 사실에 근거한 답변을 할 수 있는 정보에 접근할 수 없거나 올바른 맥락으로 안내되지 않을 때 발생하므로, 도움이 되는 대신 자신감 있고 그럴듯하게 들리는 답변을 지어내게 됩니다. 그 원인 중 하나는 LLM이 다양한 정보의 넓은 도메인 내에서 언어 사용법을 학습하지만, 특정 시점에 학습을 중단해야 하므로 이해에 적시성 요소가 있어 모델이 학습을 중단한 시점까지만 정확한 정보를 알 수 있다는 점입니다. 환각의 또 다른 요인은 모델이 일반적으로 비공개 데이터(공개 인터넷에서 사용할 수 없는 데이터)에 대해 알지 못한다는 점이며, 이러한 데이터에 특정 용어와 명명법이 포함되어 있는 경우 특히 중요합니다.</p><h3>벡터 데이터베이스</h3><p>LLM은 텍스트 임베딩이라는 기술을 사용하여 콘텐츠를 모델 공간에 벡터화하는데, 이는 학습을 기반으로 모델의 세계관 내에 콘텐츠의 의미적 의미를 <a href="https://www.elastic.co/search-labs/blog/hybrid-search-multiple-embeddings">임베딩하거나</a> 매핑하는 것을 말합니다. 임베드할 콘텐츠를 준비하고 처리하는 데에는 <a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">청킹과</a> 토큰화(및 <a href="https://www.kaggle.com/code/danishmahdi/subword-tokenization-bpe-wordpiece-and-unigram">하위 단어 토큰화</a>) 등 몇 가지 단계가 있습니다. 그 결과 일반적으로 벡터 공간 내에서 해당 콘텐츠의 의미에 대한 모델의 이해를 나타내는 고밀도 벡터 집합이 생성됩니다. 청킹은 임베딩을 생성하기 위한 모델의 처리 제약 조건에 콘텐츠를 맞추는 동시에 문장 및 단락 표시기와 같은 의미적 구성을 사용하여 관련 텍스트를 청크로 그룹화하기 위한 정확하지 않은 프로세스입니다.</p><p>청킹이 필요하면 개별 청크가 같은 문서의 다른 청크와 완전히 연결되지 않기 때문에 임베디드 문서에서 약간의 의미 손실이 발생할 수 있습니다. 신경망의 고유한 불투명성은 이러한 손실을 더욱 악화시킬 수 있습니다. LLM은 학습 중에 만들어진 용어와 개념 간의 연결이 비결정적이며 인간이 해석할 수 없는 진정한 '블랙박스'입니다. 이는 설명 가능성, 반복성, 무의식적 편견, 잠재적으로 신뢰와 정확성 상실 등의 문제로 이어집니다. 하지만 쿼리할 때 특정 키워드에 얽매이지 않고 아이디어를 의미적으로 연결할 수 있는 기능은 매우 강력합니다:</p>POST my-index/_search 
{
  "size": 10, 
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
} <p><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-semantic-query"><em>시맨틱</em></a><em> 쿼리 예시</em></p><p>벡터 데이터베이스는 검색 엔진이 아니라 데이터베이스라는 점에서 고려해야 할 문제가 하나 더 있습니다! <a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">벡터 유사성 검색이</a> 수행되면 쿼리 용어가 인코딩되어 모델의 벡터 공간 내에서 일련의 (임베딩) 좌표 집합을 찾습니다. 그런 다음 이러한 좌표를 과녁으로 사용하여 과녁에 '가장 가까운 이웃'인 문서를 찾습니다. 즉, 문서의 순위(또는 결과 내 배치)는 쿼리 좌표에서 해당 문서 좌표의 계산된 유사성 <em>거리에</em> 따라 결정됩니다. 어떤 방향으로 랭킹을 우선시해야 하며, 가능한 컨텍스트 중 사용자의 의도에 가장 가까운 컨텍스트는 무엇인가요? 제가 비유한 이미지는 영화 <a href="https://www.youtube.com/watch?v=x3h7xz558EY&amp;start=3&amp;end=86">스타게이트의</a> 한 장면으로, 교차하는 6개의 좌표점이 목적지(과녁)를 알려주지만 사용자의 주관적인 의도를 나타내는 출발점의 좌표인 '7번째 기호'를 모르면 목적지에 도달할 수 없는 상황입니다. 따라서 벡터의 상대적 순위가 계속 확장되고 차별화되지 않은 유사성 영역에 기반하는 대신, 표현 구문과 관련성 점수를 통해 쿼리의 주관적 의도를 고려하면 눈금이 매겨진 주관적 관련성의 <em>원통형과</em> 유사한 결과를 얻을 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb49ae330d3de1fc1/6a17053ee8fbce089639fb46/1ddfaae0c1496d08d7d30419e6d2aeaeacfc0ea2-1600x544.png" alt="주관적 관련성이 점수로 표시된 원통입니다." /><p>LLM의 추론 기능은 쿼리에 대해 가장 가능성이 높은 컨텍스트를 <em>식별하는</em> 데 도움이 될 수 있지만, 문제는 <em>도움이 없으면</em> 수신 쿼리의 좌표는 모델이 원래 학습된 방식에 <em>의해서만</em> 결정될 수 있다는 점입니다.</p><p>어떤 면에서 벡터 유사도는 엄격한 키워드 검색과는 정반대의 극단이라고 할 수 있는데, 용어 불일치 문제를 극복할 수 있다는 것이 강점이지만 <a href="https://medium.com/data-science/vector-embeddings-are-lossy-heres-what-to-do-about-it-4f9a8ee58bb7">거의 결함에</a> 가깝다고 할 수 있습니다: LLM은 관련 개념을 구분하기보다는 통합하는 경향이 있습니다. 벡터 유사도는 콘텐츠를 의미론적으로 일치시키는 능력을 향상시키지만, 모델에서 충분히 명확하지 않은 정확한 키워드와 특정 세부 사항을 간과할 수 있기 때문에 정확성을 보장하지는 않습니다. 벡터 유사도 검색은 그 자체로도 강력하지만, 벡터 데이터베이스에서 검색한 결과를 다른 검색 방법의 결과와 연관시킬 수 있는 방법이 필요합니다.</p><h3>순위 재조정 기술</h3><p>이제 결과 집합의 점수를 다시 매기거나 통합된 순위 순서로 정규화하는 리랭킹이라는 일반적인 기법을 언급할 때입니다. 재랭크가 필요한 이유는 여러 소스의 결과 또는 순위/채점 메커니즘이 다른 검색 방법(또는 전혀 없는 SQL!) 때문일 수도 있고, 의미론적이지 않은 소스의 결과를 사용자의 쿼리에 의미론적으로 맞추기 위해 재랭크가 사용될 수도 있습니다. 재랭크는 2단계 작업으로, 어떤 <em>초기 검색</em> 방법(예를 들어 SQL, 어휘 검색, 벡터 검색)의 순서를 다른 채점 방법으로 다시 지정합니다.</p><p><a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr">학습을 통한 순위 지정(LTR)</a> 및 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">상호 순위 융합(RRF</a> ) 등 여러 가지 접근 방식을 사용할 수 있습니다. LTR은 검색 결과 기능(좋아요, 평점, 클릭 등)을 캡처하고 이를 사용하여 결과를 점수화하고 부스트 또는 편향시키는 데 유용합니다. RRF는 다양한 쿼리 양식에서 반환된 결과를 병합하는 데 적합합니다(예 어휘 및 벡터 데이터베이스 검색)을 하나의 결과 목록으로 통합합니다. Elastic은 또한 <a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">선형 재순위화</a> 방법을 사용하여 점수를 조정할 수 있는 유연성도 제공합니다.</p><p>그러나 가장 효과적인 재순위 조정 기법 중 하나는 <a href="https://www.elastic.co/docs/solutions/search/ranking/semantic-reranking">시맨틱 재순위</a> 조정으로, LLM의 시맨틱 이해를 사용하여 쿼리와 결과의 벡터 임베딩을 함께 분석한 다음 관련성 점수/채점을 적용하여 최종 순위를 결정합니다. 물론 시맨틱 재랭크에는 재랭크 모델에 대한 연결이 필요하며, Elasticsearch는 기본 제공 모델(Elastic<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-rerank"></a> Rerank), <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/eland/machine-learning">가져온 타사</a> <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-cohere">모델 또는 Cohere나</a> <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-googlevertexai">Google Vertex AI</a> 같은 외부 호스팅 서비스를 활용하는 <strong>재랭크</strong> 엔드포인트를 생성할 수 있는 <a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference">추론 API를</a> 제공합니다. 그런 다음 <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">검색</a> 쿼리 추상화 구문을 통해 다시 순위를 매길 수 있습니다:</p>POST my-index/_search 
{
  "size": 10,
  "retriever": {
    "text_similarity_reranker": {
      "retriever": {
        "rrf": {
          "retrievers": [
            {
              "standard": {
                "query": {
                  "multi_match": {
                    "query": "machine learning applications",
                    "fields": ["title", "content"]
                  }
                }
              }
            },
            {
              "knn": {
                "field": "semantic-content-field",
                "k": 10,
                "num_candidates": 100,
                "query_vector_builder": {
                  "text_embedding": {
                    "model_id": "my-text-embedding-model",
                    "model_text": "machine learning applications"
                  }
                }
              }
            }
          ],
          "rank_window_size": 50,
          "rank_constant": 20
        }
      }
    },
    "field": "content",
    "inference_id": "my-reranker",
    "inference_text": "machine learning applications",
    "rank_window_size": 20
  }
}<p><em>다단계 리트리버 순위 재조정 작업 예시</em></p><p>멋지지 않나요? 서로 다른 소스의 결과에 대해 재랭킹을 수행하여 모든 유형의 콘텐츠에 대한 의미론적 이해에 근접할 수 있습니다... 의미론적 재랭킹은 처리 시간뿐만 아니라 계산 비용이 많이 들 수 있으므로 제한된 수의 결과에 대해서만 실현 가능하게 수행할 수 있으므로 초기 결과를 검색하는 <em>방법이</em> 중요합니다.</p><h3>컨텍스트 검색 방법의 중요성</h3><p>주관적 의도는 결과의 정확성을 결정하고 관련성을 점수화할 때 중요한 요소입니다. 쿼리 수행에 대한 사용자의 의도를 고려할 수 있는 기능(유연한 구문 또는 2단계 재랭킹을 통해 표현됨)이 없으면 모델 공간 내에 이미 인코딩된 기존 컨텍스트 중에서 선택할 수 밖에 없습니다. 일반적으로 이러한 컨텍스트 부족 문제를 해결하는 방법은 <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">검색 증강 생성(RAG)</a>과 같은 기술을 사용하는 것입니다. RAG가 작동하는 방식은 상황에 맞는 데이터에 대한 사전 쿼리에서 반환된 추가 관련 용어를 포함하여 쿼리의 좌표를 효과적으로 이동하는 것입니다. 따라서 추가 컨텍스트를 제공하는 엔진과 <em>검색을</em> 수행하는 초기 방법이 컨텍스트의 정확성에 더욱 중요해집니다!</p><p>다양한 컨텍스트 검색 방법과 이러한 방법이 RAG 작업에 어떤 도움이 되거나 해가 되는지 살펴보겠습니다:</p><ul><li><p><strong>검색 엔진이 없는 하이브리드 검색 검색은 여전히 주관적인 연관성이 부족합니다.</strong> RAG를 제공하는 플랫폼이 주로 SQL 기반인 경우(대부분의 '데이터 레이크' 플랫폼 포함), 초기 검색 단계에서 정확도 점수가 부족합니다. 많은 데이터 레이크 플랫폼이 자체 버전의 하이브리드 검색(검색이 아닌)을 제공하며, 일반적으로 SQL 기반 검색과 벡터 데이터베이스 결과에 시맨틱 리랭크 및 RRF와 같은 리랭크 기법을 결합합니다. 단순 정렬은 주관적 순위를 매기기에는 분명히 불충분하지만, 2단계 시맨틱 재랭크 작업의 기초로 사용하더라도 1단계 검색으로서의 SQL은 검색 시 결과를 점수화하는 방법 없이 '상위 k' 히트에 대해서만 시맨틱 재랭크가 수행될 때 문제가 됩니다 - 실제로 <em>최상의</em> 결과가 상위 결과라고 보장할 수 있는 방법은 무엇일까요?</p></li><li><p><strong>벡터 유사성만으로는 RAG에 충분하지</strong> 않습니다. 이는 임베딩의 손실, 순진한 청킹 방법, 유사성 계산 방식, 주관적 의도라는 중요한 요소가 누락된 문제 등 복합적인 문제 때문이었습니다. RAG의 주요 목표 중 하나는 생성 AI의 상호작용을 객관적인 진실에 근거하여 환각을 방지하고 학습 중에 알지 못했던 개인 정보를 LLM에 알려주는 것입니다. RAG를 통해 제공되는 추가 컨텍스트를 사용하여 당면한 질문에 답하는 데 가장 중요한 연결과 세부 사항을 고려하도록 LLM을 제한하고 지시할 수 있습니다. 이를 위해서는 의미론적 접근 방식과 어휘적 접근 방식을 <em>모두</em> 사용해야 합니다.</p></li><li><p><strong>파일 기반 grep/레거시 RAG.</strong> 에이전트 AI <a href="https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents">세계에서는</a> 외부 검색 플랫폼이 아닌 RAG용 grep 및 정규식을 통해 로컬 파일에 액세스하는 크게 확대된 컨텍스트 창을 사용하는 것을 지적하는 의견도 있습니다. 훨씬 더 큰 컨텍스트 창을 사용할 수 있게 되면 LLM은 관련 정보를 수집하기 위해 단편적인 정보와 여러 검색 방법/플랫폼에 의존하지 않고 자신의 사고 공간 내에서 개념을 연결할 수 있게 될 것입니다. 이론적으로는 전체 문서가 문서 세그먼트보다 더 완전한 그림을 제공하지만, 이는 소규모 데이터 도메인(예: <a href="https://en.wikipedia.org/wiki/Vibe_coding">바이브코딩을</a> 위해 파일을 제공할 때)에서만 작동할 수 있으며, 그 경우에도 초기 검색 방식은 키워드만 일치하는 모든 문서를 스캔하는 것입니다.</p></li></ul><p><strong>검색은 검색 그 이상입니다</strong></p><p>검색 엔진은 가능한 한 빠르고 유연하게 쿼리를 수행하도록 특별히 설계되었습니다. 내부적으로는 다양한 종류의 데이터를 해당 데이터 유형에 맞는 방식으로 저장하고 검색하기 위해 특수 데이터 구조를 활용합니다. Elasticsearch는 비정형/전체 텍스트 어휘 검색(일치, 구문, 근접, 다중 일치), 빠른 키워드(정확히 일치) 검색 및 필터링, 숫자 범위, 날짜, IP 주소 등 거의 모든 유형의 데이터에 대해 최적화된 저장과 쿼리를 제공하며, 문서 구조를 저장하는 방식이 매우 유연합니다(예. 중첩되거나 평평해진 문서). Elasticsearch는 또한 희소 벡터 유형과 고밀도 벡터 유형을 모두 저장하고 쿼리할 수 있는 기본 벡터 데이터베이스이며, 검색 충실도를 유지하면서 벡터화된 콘텐츠와 관련된 속도, 확장성 및 비용을 개선하는 혁신적인 방법(예: <a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization(BBQ)</a> &amp; <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>)을 계속 모색하고 있습니다. 또한 Elasticsearch 플랫폼은 기본으로 데이터 복원력과 고가용성을 제공하며, <a href="https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/searchable-snapshots">검색 가능한 스냅샷과</a> 같은 데이터 수명 주기 관리 기능을 통해 자주 액세스하지 않거나 장기 보존 데이터를 비용 효율적인 개체 스토리지에 보관하면서도 여전히 완벽하게 검색할 수 있습니다.</p><h3>하이브리드 검색은 모든 면에서 최고입니다.</h3><p><a href="https://www.elastic.co/what-is/hybrid-search">하이브리드 검색</a> (하이브리드 검색뿐만 아니라!) 는 기존 어휘 검색의 강점과 LLM의 의미론적 이해 및 벡터 유사도 검색을 결합합니다. 이러한 시너지를 통해 검색 엔진이 제공하는 유연한 쿼리 구문 옵션, 의도 중심 구문 옵션 및 관련성 점수, 멀티모달 데이터 검색, 필터링, 집계, 편향성 등 검색 엔진이 제공하는 모든 기능을 통해 <em>검색</em> 단계에서 관련성이 높은 결과를 타겟팅할 수 있습니다. <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> 및 다단계 <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">검색기와</a> 같은 검색 구문을 사용하면 기존 검색과 시맨틱 검색, 필터, 여러 순위 재조정 기술을 하나의 요청에 유연하게 결합할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6ee9512910856ee3/6a17053fcdacbf2ea97d291e/f25180cb430414b99ae553d3b8eb161dbccea4d4-1920x1080.png" alt="하이브리드 검색이 작동하는 방식" /><p>하이브리드 검색의 가장 큰 장점 중 하나는 쿼리가 여러 가지 데이터 유형에 대해 동시에 특수 구문을 사용할 수 있다는 점입니다. 이러한 다양한 쿼리 구문은 결과를 <em>찾는</em> 데만 사용할 수 있을 뿐만 아니라 결과에 <em>대한</em> 필터나 집계로도 사용할 수 있습니다. 예를 들어, 다른 구문과 자주 결합되는 가장 일반적인 쿼리 유형 중 하나는 <a href="https://www.elastic.co/docs/explore-analyze/geospatial-analysis">지리공간 분석입니다</a>. 특정 지점에서 지정된 거리 내의 지리적 좌표가 있는 결과를 쿼리하거나, 지역별 결과 집계 또는 구역 내/외의 이동을 추적하고 경고하기 위한 집계를 요청하는 등의 작업을 수행할 수 있습니다. 하이브리드 검색을 사용하면 구문을 유연하게 조합하여 가장 정확한 방식으로 결과를 타겟팅하고 컨텍스트에 가장 가까운 콘텐츠를 검색할 수 있습니다.</p><h2>인터미션</h2><p>이 첫 번째 파트에서는 벡터 검색이 데이터를 검색하는 방식을 어떻게 변화시켰는지 이야기하고, 데이터와 상호 작용하는 데 사용하는 쿼리 메커니즘에 LLM이 가져온 변화의 무대를 마련합니다. LLM이 맥락을 잃지 않고 이해할 수 있도록 여러 부분으로 나눠서 설명해야 한다고 가정해 보겠습니다... ;-) <a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">2부: 에이전트 AI와 컨텍스트 엔지니어링의 필요성에서</a> <em>이것이 중요한 이유에</em> 대해 자세히 알아보고, 3부에서는 하이브리드 검색에 대한 논의로 돌아가겠습니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai</guid>
    <category><![CDATA[하이브리드 검색]]></category>
    <category><![CDATA[정확도]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc49e39872984c8eb/6a1705410e2e49e42c419fd5/7e59a0671aa9ea32d68188a693936a66ebf48625-1000x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 12 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[하이브리드 검색 재조명: Elasticsearch의 리니어 리트리버를 소개합니다!]]></title>
    <description><![CDATA[리니어 리트리버가 가중치 점수와 최소 최대 정규화를 활용하여 보다 정확하고 일관된 랭킹을 위해 하이브리드 검색을 향상시키는 방법을 알아보고 그 사용법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/kr/search-labs/blog/elasticsearch-retrievers-ga-8.16.0">이전 블로그</a> 게시물에서 복잡한 랭킹 파이프라인을 생성할 수 있도록 처음부터 다시 설계된 검색어 프레임워크를 소개했습니다. 또한 상호 순위 융합(RRF) 검색기가 서로 다른 쿼리의 결과를 병합하여 하이브리드 검색을 가능하게 하는 방법도 살펴봤습니다. RRF는 구현하기 쉽지만, 실제 점수를 무시하고 순전히 상대적인 순위에만 초점을 맞춘다는 한계가 있습니다. 따라서 미세 조정과 최적화가 어렵습니다.</p><h2>리니어 리트리버를 만나보세요!</h2><p>이 게시물에서는 하이브리드 검색을 지원하는 최신 기능인 <a href="https://www.elastic.co/kr/docs/solutions/search/retrievers-overview#retrievers-overview-types"><code>linear</code></a> <a href="https://www.elastic.co/kr/docs/solutions/search/retrievers-overview#retrievers-overview-types">리트리버를</a> 소개합니다! <code>rrf</code> 과 달리 <code>linear</code> 검색기는 문서와 일치하는 모든 쿼리에 대해 가중 합계를 계산합니다. 이 접근 방식은 결과 집합 내에서 각 문서의 상대적 중요도를 유지하면서 각 쿼리가 최종 점수에 미치는 영향을 정밀하게 제어할 수 있습니다. 그 결과, 하이브리드 검색을 보다 직관적이고 유연하게 미세 조정할 수 있는 방법을 제공합니다.</p><p>최종 점수가 계산될 리니어 리트리버를 정의합니다:</p><p>간단합니다:</p>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5
               },


           ]
        }
     }
}<p>얼마나 간단하고 직관적인지 아시나요? (그리고 <code>rrf</code> 와 정말 비슷합니다!) 이 구성을 사용하면 상대 순위에만 의존하는 <code>rrf</code> 과 달리 각 쿼리 유형이 최종 순위에 기여하는 정도를 정밀하게 제어할 수 있습니다.</p><p>한 가지 주의할 점은 사용된 유사성 측정지표에 따라 <code>knn</code> 점수가 엄격하게 제한될 수 있다는 점입니다. 예를 들어 코사인 유사도 또는 단위 정규화된 벡터의 도트 곱을 사용하면 점수는 항상 <code>[0, 1]</code> 범위 내에 있습니다. 반면 <code>bm25</code> 점수는 예측 가능성이 낮고 범위가 명확하게 정의되어 있지 않습니다.</p><h2>점수 확장: kNN 대 BM25</h2><p>하이브리드 검색의 한 가지 문제점은 검색기마다 다른 척도로 점수를 산출한다는 점입니다. 예를 들어 다음 시나리오를 생각해 보세요:</p><p>쿼리 A 점수:</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>doc4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>100</p><p>1.5</p><p>1</p><p>0.5</p><p>쿼리 B 점수:</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>doc4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>0.63</p><p>0.01</p><p>0.3</p><p>0.4</p><p><code>kNN</code> 점수는 0에서 1 사이의 범위인 반면 <code>bm25</code> 점수는 매우 다양합니다. 이러한 차이로 인해 결과 결합을 위한 정적 최적 가중치를 설정하기가 까다롭습니다.</p><h2>구원의 정규화: MinMax 정규화 도구</h2><p>이 문제를 해결하기 위해 다음 공식을 사용하여 각 쿼리에 대해 독립적으로 점수를 <code>[0, 1]</code> 범위로 확장하는 <code>minmax</code> 정규화기(선택 사항)를 도입했습니다:</p><p>이렇게 하면 쿼리 결과 집합 내에서 각 문서의 상대적 중요도가 유지되므로 서로 다른 검색기의 점수를 쉽게 결합할 수 있습니다. 정규화를 사용하면 점수는 다음과 같이 됩니다:</p><p>쿼리 A 점수:</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>doc4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>1.00</p><p>0.01</p><p>0.005</p><p>0.000</p><p>쿼리 B 점수:</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>doc4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>1.00</p><p>0.000</p><p>0.465</p><p>0.645</p><p>이제 모든 점수는 <code>[0, 1]</code> 범위에 속하며, 절대 점수 대신 결과의 (쿼리 대비) 중요도를 파악하고 쿼리 간 일관성을 유지하므로 가중치 합계를 최적화하는 것이 훨씬 더 간단해졌습니다.</p><h2>리니어 리트리버 예시 </h2><p>이제 예제를 통해 위의 내용이 어떻게 보이는지, <code>linear</code> 리트리버가 <code>rrf</code> 의 몇 가지 단점을 어떻게 해결하는지 살펴보도록 하겠습니다. RRF는 상대적인 순위에만 의존하며 실제 점수 차이는 고려하지 않습니다. 예를 들어 다음과 같은 점수가 주어집니다:</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>doc4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>100</p><p>1.5</p><p>1</p><p>0.5</p><p>RRF 점수</p><p>0.03226</p><p>0.03252</p><p>0.03200</p><p>0.03125</p><p>rrf는 문서의 순위를 매깁니다:</p><p>그러나 doc1은 다른 문서보다 <code>bm25</code> 점수가 훨씬 높은데, <code>rrf</code> 은 상대적인 순위만 보기 때문에 이를 포착하지 못합니다. 정규화와 결합된 <code>linear</code> 리트리버는 점수와 그 차이를 모두 정확하게 설명하여 보다 의미 있는 순위를 생성합니다:</p><p></p><p>doc1</p><p>doc2</p><p>doc3</p><p>doc4</p><p>knn</p><p>0.347</p><p>0.35</p><p>0.348</p><p>0.346</p><p>bm25</p><p>1</p><p>0.01</p><p>0.005</p><p>0</p><p>위에서 볼 수 있듯이 doc1의 높은 순위와 <code>score</code> 의 <code>bm25</code> 이 적절히 고려되어 최종 점수에 반영되었습니다. 또한 모든 점수는 이제 <code>[0, 1]</code> 범위 내에 있으므로 훨씬 더 직관적인 방식으로 비교하고 결합할 수 있으며 오프라인 최적화 프로세스도 구축할 수 있습니다.</p><h2>모든 것을 종합하기</h2><p>정규화를 통해 <code>linear</code> 검색기를 최대한 활용하려면 검색 요청은 다음과 같이 표시되어야 합니다:</p>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5,
                   "normalizer": "minmax"
               },


           ]
       }
   }
}<p>이 접근 방식은 <code>linear</code> 리트리버의 유연성과 직관적인 채점 방식을 유지하면서 MinMax 정규화를 통해 일관된 점수 확장을 보장하는 두 가지 장점을 결합한 것입니다.</p><p>모든 리트리버와 마찬가지로 <code>linear</code> 리트리버는 설명 기능, 일치 항목 강조 표시, 필드 축소 등을 지원하여 계층적 리트리버 트리의 모든 레벨에 통합할 수 있습니다.</p><h2>리니어 리트리버를 선택해야 하는 시기 및 리니어 리트리버가 차이를 만드는 이유</h2><p><code>linear</code> 리트리버:</p><ul><li><p>단순한 순위가 아닌 실제 점수를 활용하여 상대적 중요도를 유지합니다.</p></li><li><p>다양한 쿼리의 가중치 기여도를 사용하여 미세 조정할 수 있습니다.</p></li><li><p>정규화를 사용하여 일관성을 향상시켜 하이브리드 검색을 더욱 강력하고 예측 가능하게 만듭니다.</p></li></ul><h2>결론</h2><p><code>linear</code> 리트리버는 이미 Elasticsearch 서버리스와 8.18 및 9.0 릴리즈에서 사용할 수 있습니다! 더 많은 예제와 구성 매개변수는 문서에서도 확인할 수 있습니다. 직접 사용해보고 하이브리드 검색 환경을 개선하는 방법을 알아보세요. 여러분의 피드백을 기다리겠습니다. 즐거운 검색 되세요!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[하이브리드 검색]]></category>
    <dc:creator><![CDATA[Panagiotis Bailis]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte58a751cbcc1153d/6a17e3c76317305c4f585a10/7a07e27e3095463ff93b4cb7f8a0cf3b8e44eab0-1777x1000.png" length="0" type="image/png"/>
    <pubDate>Wed, 28 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Quepid를 사용하여 판정 목록을 생성하기]]></title>
    <description><![CDATA[Quepid에서 협업적인 인간 평가자 프로세스를 사용해 평가 목록을 만들고, 벤치마크를 사용하여 관련성을 조정하는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/judgment-lists">판단 목록을</a> 만드는 것은 검색 결과 품질을 최적화하는 데 중요한 단계이지만 복잡하고 어려운 작업일 수 있습니다. 판단 목록은 해당 결과에 대한 관련성 등급과 짝을 이루는 큐레이션된 검색어 집합으로, 테스트 컬렉션이라고도 합니다. 이 목록을 사용하여 계산된 메트릭은 검색 엔진의 성능을 측정하는 벤치마크 역할을 합니다. 판단 목록을 만드는 프로세스를 간소화하기 위해 <a href="https://opensourceconnections.com/">오픈소스 커넥션</a> 팀은 <a href="https://quepidapp.com/">Quepid를</a> 개발했습니다. 판단은 명시적일 수도 있고 사용자의 암묵적인 피드백을 기반으로 할 수도 있습니다. 이 블로그에서는 모든 평가 목록의 기초가 되는 명시적 평가를 효과적으로 수행할 수 있도록 큐피드에서 공동 작업 환경을 설정하는 방법을 안내합니다.</p><p>Quepid는 검색 품질 평가 프로세스에서 검색 팀을 지원합니다:</p><ul><li><p>쿼리 집합 구축</p></li><li><p>판단 목록 만들기</p></li><li><p>검색 품질 지표 계산</p></li><li><p>계산된 검색 품질 지표를 기반으로 다양한 검색 알고리즘/랭커를 비교하세요.</p></li></ul><p>블로그에서는 영화 대여점을 운영하며 검색 결과 품질을 개선하는 것이 목표라고 가정해 보겠습니다.</p><h2>필수 구성 요소</h2><p>이 블로그에서는 <a href="https://github.com/o19s/es-tmdb">es-tmdb 리포지토리의</a> 데이터와 매핑을 사용합니다. 데이터는 <a href="https://www.themoviedb.org/">영화 데이터베이스에서</a> 가져온 것입니다. 따라 하려면 매핑을 사용하여 tmdb라는 인덱스를 설정하고 데이터를 인덱싱하세요. 이를 위해 로컬 인스턴스를 설정하든 Elastic Cloud 배포를 사용하든 상관없으며 둘 다 잘 작동합니다. 이 블로그에서는 Elastic Cloud 배포를 가정합니다. 데이터 인덱싱 방법에 대한 정보는 <a href="https://github.com/o19s/es-tmdb/blob/master/README.md">es-tmdb 리포지토리의 README에서</a> 찾을 수 있습니다.</p><p>제목 필드에서 <code>rocky</code> 에 대한 간단한 일치 쿼리를 수행하여 검색할 데이터가 있는지 확인합니다:</p>GET tmdb/_search
{
 "query": {
   "match": {
     "title": "rocky"
   }
 }
}<p>8개의 결과가 표시되어야 합니다.</p>{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 8,
     "relation": "eq"
   }
…
}<h2>Quepid에 로그인</h2><p><a href="https://github.com/o19s/quepid">Quepid는</a> 사용자가 검색 결과 품질을 측정하고 이를 개선하기 위해 오프라인 실험을 실행할 수 있는 도구입니다.</p><p>공개적으로 사용 가능한 무료 호스팅 버전( <a href="https://app.quepid.com">https://app.quepid.com</a>)을 사용하거나, 두 가지 방법으로 Quepid를 사용할 수 있습니다, 를 클릭하거나 액세스 권한이 있는 컴퓨터에서 Quepid를 설정하세요. 이 게시물은 무료 호스팅 버전을 사용한다고 가정합니다. 사용자 환경에서 Quepid 인스턴스를 설정하려면 <a href="https://github.com/o19s/quepid/wiki/Installation-Guide">설치 가이드를</a> 따르세요.</p><p>어떤 설정을 선택하든 아직 계정이 없는 경우 계정을 만들어야 합니다.</p><h2>Quepid 케이스를 설정하는 방법</h2><p>Quepid는 "케이스를 중심으로 구성됩니다." 케이스는 연관성 튜닝 설정 및 검색 엔진과의 연결 설정 방법과 함께 쿼리를 저장합니다.</p><ul><li><p>처음 사용하는 경우 첫 번째 <strong>관련성 사례 만들기를</strong> 선택합니다.</p></li><li><p>복귀 사용자는 최상위 메뉴에서 <strong>관련성 사례를</strong> 선택하고 <strong>+ 사례 만들기를</strong> 클릭할 수 있습니다.</p></li></ul><p>예를 들어, "영화 검색 기준," 기준 검색을 측정하고 개선하려는 경우와 같이 사례의 이름을 설명적으로 지정합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte2913d344c42cd24/6a17e6477b54f906b48b38bd/8f9e480d9aae0d706cfc5371e41f19c706dd452a-594x251.png" alt="Quepid 케이스 설정하기" /><p><strong>계속을</strong> 선택하여 이름을 확인합니다.</p><p>다음으로 Quepid에서 검색 엔진으로 연결을 설정합니다. Quepid는 Elasticsearch를 비롯한 다양한 검색 엔진에 연결할 수 있습니다.</p><p>구성은 Elasticsearch 및 Quepid 설정에 따라 달라집니다. Quepid를 Elastic Cloud 배포에 연결하려면, Elastic Cloud 배포에 대해 CORS를 활성화 및 구성하고 API 키를 준비해야 합니다. 자세한 지침은 <a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud">Quepid 문서에 있는 해당 방법에</a> 나와 있습니다.</p><p>Elasticsearch 엔드포인트 정보(<code>https://YOUR_ES_HOST:PORT/tmdb/_search</code>)와 연결에 필요한 추가 정보( <strong>고급</strong> 구성 옵션의 Elastic Cloud 배포의 경우 API 키)를 입력하고, <strong>ping을</strong> 클릭하여 연결을 테스트한 후 <strong>계속을</strong> 선택하여 다음 단계로 이동합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcc068309bdc094ae/6a17e6480b0bed14cddd356a/267339dfaecae2740eb2ee2739bdc971608bdb5f-588x1169.png" alt="Quepid로 Elasticsearch 엔드포인트를 설정하는 방법" /><p>이제 케이스에 표시할 필드를 정의합니다. 나중에 인간 평가자가 특정 쿼리에 대한 문서의 관련성을 평가하는 데 도움이 되는 모든 항목을 선택합니다.</p><p><code>title</code> 을 <em>제목 필드로</em> 설정하고 <code>_id</code> 을 <em>ID 필드로</em> 그대로 둔 다음 <code>overview, tagline, cast, vote_average, thumb:poster_path</code> 을 <em>추가 표시 필드에 추가합니다</em>. 마지막 항목은 결과에서 영화의 작은 썸네일 이미지를 표시하여 우리와 인간 평가자를 시각적으로 안내합니다.</p><p><strong>계속</strong> 버튼을 선택하여 표시 설정을 확인합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc910bdf5aa7680b3/6a17e64ae8fbce710b3a1906/02c58aae8c2ebb6d31f538b27462b4c65428fdc3-594x493.png" alt="Quepid 인간 평가자를 위한 사례에 표시할 필드를 정의합니다." /><p>마지막 단계는 케이스에 검색어를 추가하는 것입니다. 입력란에 <em>스타워즈</em>, <em>해리슨 포드</em>, <em>최고의 액션 영화라는</em> 세 가지 검색어를 하나씩 추가하고 <strong>계속을</strong> 클릭합니다.</p><p>케이스에는 실제 사용자 쿼리를 나타내고 다양한 유형의 쿼리를 설명하는 쿼리가 포함되어 있는 것이 이상적입니다. 현재로서는 <em>스타워즈는</em> 영화 제목에 대한 모든 쿼리를, <em>해리슨 포드는</em> 출연진에 대한 모든 쿼리를, <em>최고의 액션 영화는</em> 특정 장르의 영화를 검색하는 모든 쿼리를 나타내는 쿼리라고 상상할 수 있습니다. 이를 일반적으로 쿼리 집합이라고 합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt46122bb45e1063e4/6a17e64b505ac36510ad8af8/baccfe96766319aa7255e9bff08913ac87d1517f-595x326.png" alt="Quepid 사례에 검색 쿼리 추가하기" /><p>프로덕션 시나리오에서는 <a href="https://opensourceconnections.com/blog/2022/10/13/how-to-succeed-with-explicit-relevance-evaluation-using-probability-proportional-to-size-sampling/">확률 비례 크기 샘플링과</a> 같은 통계 기법을 적용하여 이벤트 추적 데이터에서 쿼리를 샘플링하고, 이렇게 샘플링된 쿼리를 Quepid로 가져와 빈도에 따라 헤드(빈번한 쿼리)와 테일(드문 쿼리)의 쿼리를 포함시킵니다. 즉, 빈번하지 않은 쿼리는 제외하지 않고 빈번한 쿼리에 편향성을 부여합니다.</p><p>마지막으로 <strong>마침을</strong> 선택하면 정의된 세 가지 쿼리가 표시되는 대소문자 인터페이스로 이동합니다.</p><h2>쿼리 및 정보 요구 사항</h2><p>판단 목록이라는 중요한 목표에 도달하기 위해서는 인간 평가자가 주어진 쿼리에 대한 검색 결과(일반적으로 문서)를 판단해야 합니다. 이를 쿼리/문서 쌍이라고 합니다.</p><p>때로는 쿼리를 보면 사용자가 원하는 것이 무엇인지 쉽게 알 수 있습니다. 쿼리( <code>harrison ford</code> )의 의도는 배우 해리슨 포드가 출연한 영화를 찾는 것입니다. <code>action</code>? 사용자의 의도가 액션 장르에 속하는 영화를 찾는 것이라고 말하고 싶을 수도 있습니다. 하지만 어떤 것일까요? 가장 최근의 것, 가장 인기 있는 것, 사용자 평가에 따른 최고의 것? 아니면 사용자가 '액션'이라는 제목의 모든 영화를 찾고 싶을까요? <a href="https://www.themoviedb.org/search/movie?query=Action">영화 데이터베이스에는 '액션'이라는 제목의 영화가 최소 12개(!)</a> 있으며, 제목에 붙은 느낌표의 개수가 주로 다릅니다.</p><p>의도가 불분명한 쿼리에 대해 두 사람의 평가자가 해석을 달리할 수 있습니다. 정보 필요를 입력합니다: <a href="https://en.wikipedia.org/wiki/Information_needs">정보</a> 욕구란 정보에 대한 의식적 또는 무의식적 욕구를 말합니다. 정보 요구 사항을 정의하면 인간 평가자가 쿼리에 대한 문서를 판단하는 데 도움이 되므로 판단 목록을 작성하는 과정에서 중요한 역할을 합니다. 전문 사용자 또는 주제별 전문가가 정보 요구 사항을 지정하는 데 적합합니다. 검색 결과가 충족해야 하는 것은 사용자의 요구이므로 사용자의 관점에서 정보 요구사항을 정의하는 것이 좋습니다.</p><p>'영화 검색 기준' 사례의 쿼리에 필요한 정보입니다:</p><ol><li><p><strong>스타워즈</strong>: 사용자가 스타워즈 프랜차이즈의 영화나 프로그램을 찾고자 합니다. 스타워즈에 관한 다큐멘터리가 관련성이 있을 수 있습니다.</p></li><li><p><strong>해리슨 포드</strong>: 사용자가 배우 해리슨 포드가 출연한 영화를 찾고자 합니다. 해리슨 포드가 내레이터와 같은 다른 역할을 맡은 영화도 관련성이 있을 수 있습니다.</p></li><li><p><strong>최고의 액션 영화</strong>: 사용자는 액션 영화, 가급적이면 사용자 평균 투표 수가 높은 영화를 찾고 싶어합니다.</p></li></ol><h2>Quepid에서 정보 요구를 정의하는 방법</h2><p>Quepid에서 정보 요구 사항을 정의하려면 케이스 인터페이스에 액세스하세요:</p><p>1. 쿼리(예:<em>스타 워즈</em>)를 열고 <em>메모 토글을</em>선택합니다.</p><p>2. 첫 번째 필드에 필요한 정보를 입력하고 두 번째 필드에 추가 메모를 입력합니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte57395f64f6e5fab/6a17e64d6864a40fd6b68771/e01d3d5242a350d8797faa665eb3170039f5dfa2-1483x559.png" alt="Quepid에서 정보와 쿼리 요구사항 정의" /><p>3. <strong>저장을</strong> 클릭합니다.</p><p>소수의 쿼리의 경우 이 프로세스를 사용해도 괜찮습니다. 그러나 사례를 3개에서 100개 쿼리로 확장하는 경우(Quepid 사례는 보통 50~100개 쿼리 범위인 경우가 많습니다.) Quepid 외부(예: 스프레드시트)에서 정보 요구 사항을 정의한 다음 <strong>가져오기를</strong> 통해 업로드하고 <strong>정보 요구 사항을</strong> 선택해야 할 수 있습니다.</p><h2>Quepid에서 팀을 만들고 사례 공유하기</h2><p>협업적 판단은 관련성 평가의 품질을 향상시킵니다. 팀을 설정하려면 다음과 같이 하세요:</p><p>1. 1. 최상위 메뉴에서 <strong>Teams로</strong> 이동합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45c51d1a0e05a80d/6a17e64fe8fbcede793a190f/797706e8d130b474a95d30b6fa22ecaf36f98c03-613x58.png" alt="Quepid에서 팀을 생성하기" /><p>2. <strong>새로 추가를</strong> 클릭하고 팀 이름(예: "검색 관련성 평가자")을 입력한 다음 <strong>만들기를</strong> 클릭합니다.</p><p>3. 이메일 주소를 입력하고 <strong>사용자 추가를</strong> 클릭하여 구성원을 추가합니다.</p><p>4. 케이스 인터페이스에서 <strong>케이스 공유를</strong> 선택합니다.</p><p>5. 적절한 팀을 선택하고 확인합니다.</p><h2>Quepid에서 판단 목록 만들기</h2><p>Quepid의 Book을 사용하면 여러 평가자가 쿼리/문서 쌍을 체계적으로 평가할 수 있습니다. 생성하려면</p><p>1. 사건 인터페이스에서 <strong>판결로</strong> 이동하여 <strong>+ 책 만들기를</strong> 클릭합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3543c00e0b834b3f/6a17e650e9ea87f57fa9c598/6a077f26225961150b7414463d7db04f090b68d6-896x365.png" alt="Quepid에서 판단 목록을 만듭니다." /><p>2. 설명이 포함된 이름으로 책을 구성하고, 팀에 할당하고, 채점 방법(예: DCG@10)을 선택하고, 채점 전략(단일 또는 복수 채점자)을 설정합니다. 책에 대해 다음 설정을 사용합니다:</p><ul><li><p><strong>이름</strong>: "영화 검색 0-3 스케일"</p></li><li><p><strong>이 책을 공유할 팀</strong>: 만든 팀과 함께 확인란을 선택합니다.</p></li><li><p><strong>득점자</strong>: DCG@10</p></li></ul><p>3. 3 <strong>. 책 만들기를 클릭</strong>합니다 .</p><p>이름은 설명적이며 검색 대상('영화')에 대한 정보와 심사 점수('0~3')를 포함합니다. 선택한 점수 DCG@10은 검색 지표가 계산되는 방식을 정의합니다. 'DCG'는 <a href="https://en.wikipedia.org/wiki/Discounted_cumulative_gain">할인 누적</a> 수익의 약자이며 '@10'은 메트릭을 계산할 때 고려되는 상위 결과의 수입니다.</p><p>이 경우 정보 획득을 측정하고 이를 포지션 가중치와 결합하는 메트릭을 사용하고 있습니다. 사용 사례에 더 적합한 다른 검색 지표가 있을 수 있으며 올바른 지표를 <a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric">선택하는 것 자체가</a> 어려운 일입니다.</p><h2>쿼리/문서 쌍으로 목록 채우기</h2><p>연관성 평가를 위해 쿼리/문서 쌍을 추가하려면 다음 단계를 따르세요:</p><p>1. 케이스 인터페이스에서 "판정으로 이동합니다."</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0e98583138535cc3/6a17e6520b0bed4f51dd3571/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" alt="Quepid에서 쿼리/문서 쌍으로 목록을 채웁니다." /><p>2. 생성한 책을 선택합니다.</p><p>3. " 책 채우기" 를 클릭하고 "책에 대한 쿼리/문서 쌍 새로 고침을 선택하여 확인합니다."</p><p>이 작업은 각 쿼리에 대한 상위 검색 결과를 기반으로 쌍을 생성하여 팀에서 평가할 수 있도록 준비합니다.</p><h2>인간 평가자 팀이 판단하도록 하세요. </h2><p>지금까지 완료된 단계는 상당히 기술적이고 관리적인 작업이었습니다. 이 필수적인 준비가 완료되었으니 이제 심사위원단에게 맡기면 됩니다. 기본적으로 심사위원의 임무는 주어진 쿼리에 대한 특정 문서의 관련성을 평가하는 것입니다. 이 프로세스의 결과는 판정된 쿼리 문서 쌍에 대한 모든 관련성 레이블이 포함된 판정 목록입니다. 다음으로 이 프로세스와 이에 대한 인터페이스에 대해 자세히 설명합니다.</p><h3>Human Rating 인터페이스 개요</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt553afb07d8080421/6a17e654505ac30514ad8afc/be3016091b49655dab3354d84e6dc638f3468390-1283x664.png" alt="Quepid에서 인간 평가자가 문서와 쿼리의 정보를 어떻게 평가하는지 설명합니다." /><p>Quepid의 인간 평가 인터페이스는 효율적인 평가를 위해 설계되었습니다.</p><ul><li><p><strong>쿼리:</strong> 검색어를 표시합니다.</p></li><li><p><strong>정보 필요:</strong> 사용자의 의도를 표시합니다.</p></li><li><p><strong>채점 가이드라인:</strong> 일관된 평가를 위한 지침을 제공합니다.</p></li><li><p><strong>문서 메타데이터:</strong> 문서에 대한 관련 세부 정보를 표시합니다.</p></li><li><p><strong>평가 버튼:</strong> 평가자가 해당 키보드 단축키를 사용하여 평가를 할당할 수 있습니다.</p></li></ul><h3>인간 평가 인터페이스 사용하기</h3><p>인간 평가자로서 저는 책 개요를 통해 인터페이스에 액세스합니다:</p><p>1. 케이스 인터페이스로 이동하여 <strong>판결을</strong> 클릭합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0e98583138535cc3/6a17e6520b0bed4f51dd3571/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" alt="Quepid Human Rating 인터페이스 사용 " /><p>2. <strong>더 많은 판단이 필요합니다!</strong> 를 클릭합니다.</p><p>시스템은 아직 평가되지 않았으며 추가 판단이 필요한 쿼리/문서 쌍을 표시합니다. 이는 도서의 선택 전략에 따라 결정됩니다:</p><ul><li><p><em>단일 평가자</em>: 쿼리/문서 쌍당 단일 평가.</p></li><li><p><em>여러 평가자</em>: 쿼리/문서 쌍당 최대 3개의 평가.</p></li></ul><h3>평가 쿼리/문서 쌍</h3><p>몇 가지 예를 살펴보겠습니다. 이 가이드를 따라가다 보면 다른 영화가 표시될 가능성이 높습니다. 그러나 평점 원칙은 동일하게 유지됩니다.</p><p>첫 번째 예는 영화 ' <em>해리슨 포드</em>'에 대한 쿼리 '히어로즈'입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta248e787b5e0a670/6a17e656ec0f89612d5a65ee/c1e14b0d8b04dd579471932dbe4ff72ae5692a02-981x571.png" alt="쿼리/문서 쌍으로 Quepid에서 책을 채우는 방법." /><p>먼저 쿼리를 살펴본 다음 필요한 정보를 확인한 다음 주어진 메타데이터를 기반으로 영화를 판단합니다.</p><p>이 영화는 해리슨 포드가 출연하기 때문에 검색어와 관련된 결과입니다. 주관적으로 최신 영화가 더 관련성이 높다고 생각할 수 있지만 이는 정보 요구 사항의 일부가 아닙니다. 따라서 이 문서의 등급은 3점 만점에 3점인 '완벽'으로 평가합니다.</p><p>다음 예는 영화 '포드 대 페라리'에 대한 쿼리 ' <em>해리슨 포드</em>'입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5d86aac918a1e96/6a17e657414c64833e945152/052af7894506d7a765af156ba8e26ceec3559973-981x789.png" alt="Quepid에서 '해리슨 포드'를 검색했을 때 영화 '포드 대 페라리'가 예시로 나옵니다." /><p>동일한 관행에 따라 쿼리, 정보 요구 사항, 그리고 문서의 메타데이터가 정보 요구 사항과 얼마나 잘 일치하는지 살펴봄으로써 이 쿼리/문서를 판단합니다.</p><p>이는 좋지 않은 결과입니다. 이 결과는 검색어 중 하나인 'ford'가 제목과 일치하기 때문일 수 있습니다. 하지만 해리슨 포드는 이 영화에서 다른 어떤 역할도 맡지 않았습니다. 따라서 이 문서의 등급은 0점인 '미흡'으로 평가합니다.</p><p>세 번째 예는 ' <em>최고의 액션 영화</em>'라는 쿼리에 대한 영화 '액션 잭슨'입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2cee335ddd1e6a92/6a17e6596df731d1f40a0ed7/247ab862fbc7435537709f8c96619cb331133d09-985x606.png" alt="최고의 액션 영화 쿼리에 대한 영화 '액션 잭슨'의 예:" /><p>액션 영화처럼 보이므로 정보에 대한 요구는 적어도 부분적으로 충족됩니다. 그러나 투표 평균은 10점 만점에 5.4점입니다. 그래서 이 영화는 저희 컬렉션에서 최고의 액션 영화가 아닐 수도 있습니다. 따라서 심사위원으로서 저는 이 문서를 등급 척도에서 1점인 '보통'으로 평가합니다.</p><p>이 예는 특히 쿼리/문서 쌍을 Quepid로 평가하는 프로세스를 높은 수준에서 그리고 일반적으로 설명합니다.</p><h2>인간 평가자의 모범 사례</h2><p>표시된 예시를 보면 명확한 판단을 내리는 것이 간단해 보일 수 있습니다. 하지만 신뢰할 수 있는 인적 평가 프로그램을 구축하는 것은 쉬운 일이 아닙니다. 이 과정은 데이터의 품질을 쉽게 손상시킬 수 있는 문제들로 가득 차 있습니다:</p><ul><li><p>인간 평가자는 반복적인 작업으로 인해 피로를 느낄 수 있습니다.</p></li><li><p>개인적인 취향에 따라 판단이 왜곡될 수 있습니다.</p></li><li><p>도메인 전문 지식의 수준은 판사마다 다릅니다.</p></li><li><p>평가자는 종종 여러 가지 책임을 맡고 있습니다.</p></li><li><p>문서의 인지된 관련성이 쿼리에 대한 실제 관련성과 일치하지 않을 수 있습니다.</p></li></ul><p>이러한 요인으로 인해 일관성이 없고 품질이 낮은 판단이 내려질 수 있습니다. 하지만 걱정하지 마세요. 이러한 문제를 최소화하고 보다 강력하고 신뢰할 수 있는 평가 프로세스를 구축하는 데 도움이 되는 입증된 모범 사례가 있습니다:</p><ul><li><p><strong>일관된 평가:</strong> 쿼리, 정보 요구 사항, 문서 메타데이터를 순서대로 검토하세요.</p></li><li><p><strong>가이드라인을 참조하십시오:</strong> 채점 가이드라인을 사용하여 일관성을 유지하세요. 채점 가이드라인에는 언제 어떤 등급을 적용할지 예시를 들어 심사 과정을 설명할 수 있습니다. 첫 번째 판정 후 인간 평가자들과 체크인하는 것은 까다로운 엣지 사례와 추가 지원이 필요한 부분을 파악하는 데 좋은 관행으로 입증되었습니다.</p></li><li><p><strong>옵션을 활용합니다:</strong> 확실하지 않은 경우 "나중에 판단하겠습니다" 또는 "알 수 없음," 필요한 경우 설명을 제공합니다.</p></li><li><p><strong>휴식을 취하세요:</strong> 규칙적인 휴식은 판단력을 유지하는 데 도움이 됩니다. 퀘피드는 인간 평가자가 심사를 마칠 때마다 색종이를 터뜨려 규칙적인 휴식을 취하도록 도와줍니다.</p></li></ul><p>이러한 단계를 수행하면 Quepid에서 판단 목록을 만드는 체계적이고 협업적인 접근 방식을 구축하여 검색 연관성 최적화 노력의 효율성을 높일 수 있습니다.</p><h2>다음 단계</h2><p>이제 어디로 가야 하나요? 판단 목록은 검색 결과 품질을 개선하기 위한 하나의 기초 단계에 불과합니다. 다음 단계는 다음과 같습니다:</p><h3>메트릭 계산 및 실험 시작</h3><p>판단 목록을 사용할 수 있게 되면 판단을 활용하고 <a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric/">검색 품질 지표를</a> 계산하는 것은 자연스러운 과정입니다. 판단이 가능한 경우 현재 사례에 대해 구성된 메트릭을 자동으로 계산합니다. 지표는 '채점자'로 구현되며, 지원되는 지표에 원하는 지표가 포함되지 않은 경우 직접 지표를 제공할 수 있습니다!</p><p>케이스 인터페이스로 이동하여 <strong>득점자 선택으로</strong> 이동하고 <em>DCG@10을</em> 선택한 다음 <strong>득점자 선택을</strong> 클릭하여 확인합니다. 이제 Quepid는 쿼리당 DCG@10을 계산하고 전체 쿼리의 평균을 계산하여 케이스의 검색 결과 품질을 정량화합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0d059c959b842139/6a17e65be8fbceb29b3a1913/0ff3b9918342071744d681a43d542102e927abd3-1163x551.png" alt="Quepid에서 검색 결과 품질을 정량화하는 방법 " /><p>이제 검색 결과 품질이 정량화되었으므로 첫 번째 실험을 실행할 수 있습니다. 실험은 가설을 세우는 것에서 시작됩니다. 스크린샷의 세 가지 검색어를 평가한 후 살펴보면 검색 품질 지표 측면에서 세 검색어의 실적이 매우 다르다는 것을 알 수 있습니다. <em>'스타워즈'</em> 는 꽤 잘 수행되고 ' <em>해리슨 포드</em> '도 괜찮아 보이지만 가장 큰 잠재력은 ' <em>최고의 액션 영화</em>'에 있습니다.</p><p>이 쿼리를 확장하면 결과를 확인할 수 있으며, 문서가 일치하는 이유와 점수에 영향을 미치는 요인을 자세히 살펴볼 수 있습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt35227761f4524a1e/6a17e65cb1e11325c579f25a/c45c6cae085a492198c0f8b7060a1a7204e3724e-1131x691.png" alt="Quepid에서 다양한 검색 지표에 대한 성능을 확인하기 위해 여러 가지 쿼리를 실험해 보고 있습니다." /><p>"쿼리 설명"을 클릭하고 "구문 분석" 탭에 들어가면 쿼리가 <em>캐스팅</em>, <em>개요</em> 및 <em>제목의</em> 세 가지 필드에서 검색하는 DisjunctionMaxxQuery임을 확인할 수 있습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0966653b9ab3cc9c/6a17e65efaa913171f93c84c/4a1e1bb2a9cd28e9c48e0ba16357d17ed9d3a5cf-894x557.png" alt="쿼리 파싱 설명" /><p>일반적으로 검색 엔지니어는 검색 플랫폼에 대한 몇 가지 도메인별 정보를 알고 있습니다. 이 경우 <em>장르</em> 필드가 있다는 것을 알 수 있습니다. 이를 쿼리에 추가하여 검색 품질이 개선되는지 확인해 보겠습니다.</p><p>케이스 인터페이스에서 <strong>관련성 조정을</strong> 선택하면 열리는 <strong>쿼리 샌드박스를</strong> 사용합니다. 검색하는 <em>장르</em> 필드를 추가하여 탐색해 보세요:</p>{
  "query": {
    "multi_match": {
      "query": "#$query##",
      "type": "best_fields",
      "fields": [
        "title^10",
        "overview",
        "cast",
        "genres"
      ]
    }
  }
}<p>내 검색 다시 실행을 클릭하세요! 그리고 결과를 확인합니다. 변경되었나요? 안타깝게도 그렇지 않습니다. 이제 탐색할 수 있는 옵션이 많아졌습니다. 기본적으로 Elasticsearch가 제공하는 모든 쿼리 옵션이 있습니다:</p><ul><li><p>장르 필드의 필드 가중치를 높일 수 있습니다.</p></li><li><p>투표 평균에 따라 문서 등급을 높이는 기능을 추가할 수 있습니다.</p></li><li><p>장르가 많이 일치하는 경우에만 투표 평균에 따라 문서를 끌어올리는 더 복잡한 쿼리를 만들 수 있습니다.</p></li><li><p>…</p></li></ul><p>이러한 모든 옵션을 Quepid에서 탐색할 때 가장 좋은 점은 개선하려는 하나의 쿼리뿐만 아니라 모든 쿼리에 대한 효과를 정량화할 수 있다는 점입니다. 따라서 실적이 저조한 하나의 검색어를 개선하기 위해 다른 검색어의 검색 결과 품질을 희생할 수 없습니다. 빠르고 저렴하게 반복하고 위험 부담 없이 가설의 가치를 검증할 수 있으므로 오프라인 실험은 모든 검색 팀의 기본 역량으로 자리 잡았습니다.</p><h3>평가자 간 신뢰성 측정</h3><p>작업 설명, 정보 요구 사항, Quepid가 제공하는 것과 같은 인간 평가자 인터페이스가 있어도 인간 평가자는 동의하지 않을 수 있습니다.</p><p>의견 불일치 자체가 나쁜 것은 아니며, 오히려 의견 불일치를 측정하면 해결해야 할 문제가 드러날 수 있습니다. 관련성은 주관적일 수 있고, 쿼리가 모호할 수 있으며, 데이터가 불완전하거나 부정확할 수 있습니다. <a href="https://en.wikipedia.org/wiki/Fleiss%27_kappa">Fleiss의 카파는</a> 평가자 간의 합의에 대한 통계적 척도이며, Quepid에 사용할 수 있는 예제 노트북이 있습니다. 찾으려면 최상위 탐색 메뉴에서 <strong>노트북을</strong> <strong>선택하고 예제</strong> 폴더에서 <strong>Fleiss Kappa.ipynb 노트북을 선택합니다.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92d52b30f1fb19c1/6a17e660abe0f2224bdfe9bb/f0669ae96371368ef4d84bb28669560ef09d755c-624x61.png" alt="Quepid의 노트북에서 평가자 간의 일치도에 대한 Fleiss’ Kappa 통계 측정을 찾는 방법" /><h2>결론</h2><p>가장 복잡한 검색 연관성 문제도 해결할 수 있도록 지원하고 계속 발전하고 있습니다. <a href="https://github.com/o19s/quepid/blob/main/CHANGELOG.md#800----2024-02-14">버전 8부터는 판단 생성</a> 프로세스를 확장하려는 팀에게 특히 유용한 AI 생성 판단을 지원합니다.</p><p>Quepid 워크플로우를 사용하면 확장 가능한 판단 목록을 효율적으로 생성하여 궁극적으로 사용자의 요구를 진정으로 충족하는 검색 결과를 얻을 수 있습니다. 판단 목록을 설정하면 검색 관련성을 측정하고, 개선 사항을 반복하며, 더 나은 사용자 경험을 제공하기 위한 강력한 기반을 마련할 수 있습니다.</p><p>앞으로 나아가면서 관련성 튜닝은 지속적인 과정이라는 점을 기억하세요. 판단 목록을 사용하면 진행 상황을 체계적으로 평가할 수 있지만, 실험, 메트릭 분석 및 반복적인 개선과 함께 사용할 때 가장 강력한 효과를 발휘합니다.</p><h2>추가 읽기</h2><ul><li><p>Quepid 문서:</p><ul><li><p><a href="https://quepid-docs.dev.o19s.com/2/quepid/32/relevancy-is-a-team-sport">관련성은 팀 스포츠입니다</a></p></li><li><p><a href="https://quepid-docs.dev.o19s.com/2/quepid/18/quepid-for-human-raters">인간 평가자를 위한 큐피드</a></p></li><li><p><a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud">Quepid를 Elastic Cloud에 연결하는 방법</a></p></li></ul></li><li><p><a href="https://github.com/o19s/quepid">퀘피드 깃허브 리포지토리</a></p></li><li><p><a href="https://opensourceconnections.com/blog/2020/07/07/meet-pete-the-e-commerce-search-product-manager/">이커머스 검색 개선에 관한 블로그 시리즈, Pete를 만나보세요.</a></p></li><li><p><a href="https://opensourceconnections.com/slack">관련성 슬랙</a>: #quepid 채널 가입하기</p></li></ul><p><a href="https://opensourceconnections.com/"><strong>오픈 소스 커넥션과</strong></a> 협력하여 검색 및 AI 기능을 혁신하고 팀이 이를 지속적으로 발전시킬 수 있도록 역량을 강화하세요. 전 세계 고객들이 검색 품질, 팀 역량, 비즈니스 성과에서 지속적으로 획기적인 개선을 달성하며 입증된 실적을 보유하고 있습니다. 자세한 내용은 <a href="https://opensourceconnections.com/contact/">지금</a> 바로 문의하세요.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/quepid-judgement-lists</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/quepid-judgement-lists</guid>
    <category><![CDATA[정확도]]></category>
    <dc:creator><![CDATA[Daniel Wrigley]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte6779c72c7a42a5a/6a17e6623e9e45acf0ba146b/307c1774bd31f92bb4aa7b69e1a6796240465100-1600x914.png" length="0" type="image/png"/>
    <pubDate>Mon, 26 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ML을 사용하여 필터 및 패싯 생성]]></title>
    <description><![CDATA[ML 모델을 사용하여 검색 환경에서 필터 및 패싯 생성을 자동화할 때의 장단점을 기존의 하드코딩 방식과 비교하여 살펴봅니다.]]></description>
    <content:encoded><![CDATA[<p>필터와 패싯은 검색 결과를 구체화하는 데 사용되는 메커니즘으로, 사용자가 관련 콘텐츠나 제품을 더 빠르게 찾을 수 있도록 도와줍니다. 기존 접근 방식에서는 규칙을 수동으로 정의합니다. 예를 들어 영화 카탈로그에서는 장르와 같은 속성이 필터와 패싯에 사용하도록 미리 정의되어 있습니다. 반면, AI 모델을 사용하면 영화의 특성에서 새로운 속성을 자동으로 추출할 수 있어 보다 역동적이고 개인화된 프로세스를 구현할 수 있습니다. 이 블로그에서는 각 방법의 장단점을 살펴보고, 각 방법의 적용 사례와 문제점을 강조합니다.</p><h2>필터와 패싯 비교</h2><p>시작하기 전에 필터와 패싯이 무엇인지 정의해 보겠습니다. <strong>필터는</strong> 결과 집합을 제한하는 데 사용되는 미리 정의된 속성입니다. 예를 들어 마켓플레이스에서는 검색이 수행되기 전에도 필터를 사용할 수 있습니다. 사용자는 <strong>"비디오 게임"</strong> 과 같은 카테고리를 선택한 다음 <strong>"PS5"</strong> 과 같이 전체 데이터베이스가 아닌 보다 구체적인 하위 집합으로 검색을 구체화할 수 있습니다. 이렇게 하면 보다 관련성 높은 결과를 얻을 가능성이 크게 높아집니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a77b38aae238938/6a170b821949f72a52e7aa51/5ed8868fa5017d034e1273e35c884a5430afdf3c-1600x937.png" alt="필터" /><p><strong>패싯은</strong> 필터와 유사하게 작동하지만 검색을 수행한 후에만 사용할 수 있습니다. 즉, 검색이 결과를 반환하고 이를 기반으로 새로운 세분화 옵션 목록이 생성됩니다. 예를 들어 PS5 콘솔을 검색할 때 저장 <strong>용량</strong>, <strong>배송비</strong>, <strong>색상</strong> 등의 측면이 표시되어 사용자가 이상적인 제품을 선택하는 데 도움이 될 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt166e356b80d423ef/6a170b840e2e494ca341a10f/c5633fcc5b6fbb916110faf32144d8d43572e33a-1600x937.png" alt="패싯 " /><p>이제 필터와 패싯을 정의했으니, 기존 방식과 머신 러닝(ML) 기반 방식이 구현과 사용에 미치는 영향에 대해 논의해 보겠습니다. 각 방법에는 검색 효율성에 영향을 미치는 장점과 과제가 있습니다.</p><h2>필터 및 패싯에 대한 전통적인 접근 방식</h2><p>이 접근 방식에서는 필터와 패싯이 미리 정의된 규칙에 따라 수동으로 정의됩니다. 즉, 카탈로그 구조와 사용자 요구를 고려하여 검색을 구체화하는 데 사용할 수 있는 속성이 미리 정해지고 계획되어 있습니다.</p><p>예를 들어 마켓플레이스에서 "전자제품" 또는 "패션" 같은 카테고리에는 브랜드, 형식, 가격대 등의 특정 필터가 있을 수 있습니다. 이러한 규칙은 정적으로 생성되므로 검색 환경의 일관성을 보장하지만 새로운 제품이나 카테고리가 등장할 때마다 수동으로 조정해야 합니다.</p><p>이 접근 방식은 표시되는 필터와 패싯에 대한 예측 가능성과 제어 기능을 제공하지만, 동적으로 세분화해야 하는 새로운 트렌드가 발생할 경우 제한적일 수 있습니다.</p><p><strong>장점:</strong></p><ul><li><p><strong>예측 가능성 및 제어:</strong> 필터와 패싯을 수동으로 정의할 수 있으므로 관리가 더 쉬워집니다.</p></li><li><p><strong>낮은 복잡성:</strong> 모델을 훈련할 필요가 없습니다.</p></li><li><p><strong>유지 관리의 용이성:</strong> 규칙이 미리 정의되어 있으므로 신속하게 조정 및 수정할 수 있습니다.</p></li></ul><p><strong>단점</strong>:</p><ul><li><p><strong>새 필터에는 재색인 작업이 필요합니다:</strong> 새 속성을 필터로 사용해야 할 때마다 문서에 이 정보가 포함되어 있는지 확인하기 위해 전체 데이터 집합을 다시 색인해야 합니다.</p></li><li><p><strong>동적 적응이 부족합니다:</strong> 필터는 정적이며 사용자 행동의 변화에 따라 자동으로 조정되지 않습니다.</p></li></ul><h3>필터/패싯 구현 - 고전적인 접근 방식</h3><p><strong>개발 도구인 Kibana에서는</strong> <strong>고전적인 접근 방식을</strong> 사용하여 필터/패싯 데모를 만들어 보겠습니다.</p><p>먼저 인덱스를 구성하기 위한 매핑을 정의합니다:</p>PUT videogames
{
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "brand": { "type": "keyword" },
      "storage": { "type": "keyword" },
      "price": { "type": "float" },
      "description": { "type": "text" }
    }
  }
}<p><strong>브랜드</strong> 및 <strong>저장</strong> 필드는 <strong>키워드로</strong> 설정되어 집계<strong>(패싯)</strong>에서 바로 사용할 수 있습니다. <strong>가격</strong> 필드는 <strong>플로트</strong> 유형으로 <strong>가격 범위를</strong> 생성할 수 있습니다.</p><p>다음 단계에서는 제품 데이터가 색인화됩니다:</p>POST videogames/_bulk
{ "index": { "_id": 1 } }
{ "name": "Play Station 5", "brand": "Sony", "storage": "1TB", "price": 499.99, "description": "Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games." }
{ "index": { "_id": 2 } }
{ "name": "Xbox Series X", "brand": "Microsoft", "storage": "1TB", "price": 499.99, "description": "Fastest, most powerful Xbox console ever. Play thousands of titles: Every game looks and plays better on Xbox Series X. At the heart of Series X is the Xbox Velocity. Architecture, which combines a custom SSD and built-in software to significantly reduce load times in and out of game. Switch between multiple games in an instant with Quick Resume. Explore new worlds and experience the action like never before with an unparalleled 12 teraflops of graphics processing power. Enjoy 4K gaming at up to 120 frames per second, premium advanced 3D sound, and more. 4K at 120 FPS: requires compatible content and display X version - with disc drive" }
{ "index": { "_id": 3 } }
{ "name": "Nintendo Switch", "brand": "Nintendo", "storage": "512GB", "price": 299.99, "description": "SHARPER, VIBRANT VISUALS. The new 7-inch screen on the Nintendo Switch OLED takes your gaming to the next level: vibrant colors with sharp contrasts for every moment. INTEGRATED GAMEPLAY. Enjoy the console's many multiplayer modes and connect with other players. Online or locally, the fun on the Nintendo Switch is guaranteed. ENJOY IMMERSION FOR LONGER. In addition to delivering an unparalleled experience, thanks to its improved audio, the Nintendo Switch has a rechargeable battery while you play. From 4.5 hours to 9 hours of battery life. INCLUDES SUPER MARIO BROS. WONDER. Transform your world with the phenomenal flowers in this new Mario game, full of amazing adventures, power-ups and new abilities. NINTENDO SWITCH ONLINE SUBSCRIPTION. Access online games, play with friends and enjoy the exclusive benefits of the Nintendo Switch Online subscription." }
{ "index": { "_id": 4 } }
{ "name": "Steam Deck", "brand": "Valve", "storage": "512GB", "price": 399.99, "description": "You can save games, apps, photos and videos without worrying about space. High-Level Performance: The 4-core processor and graphics ensure a dynamic experience and fast responses. High-Definition Images: Smooth transitions and sharp images provide complete immersion in the game. Wireless Connectivity: Wi-Fi technology allows you to play wherever you want, without wires or cables limiting your fun" }
{ "index": { "_id": 5 } }
{ "name": "Nintendo Switch Lite", "brand": "Nintendo", "storage": "512GB", "price": 299.99, "description": "MADE TO BE PORTABLE. Nintendo Switch Lite is designed specifically for portable gaming. The console lets you jump into your favorite games wherever you are. COMPACT AND LIGHTWEIGHT. With its sleek, lightweight design, this console is ready to hit the road wherever you are. COMPATIBLE GAMES. The Nintendo Switch Lite system plays the library of Nintendo Switch games that work in handheld mode. A WORLD OF COLOR TO CHOOSE FROM. Available in a variety of vibrant and unique colors, Nintendo Switch Lite lets you bring even more personality wherever you go." }<p>이제 브랜드, 스토리지 및 가격대별로 결과를 그룹화하여 클래식 패싯을 검색해 보겠습니다. 쿼리에서 size:0이 정의되었습니다. 이 시나리오에서는 쿼리에 해당하는 문서를 포함하지 않고 집계 결과만 검색하는 것이 목표입니다.</p>POST videogames/_search
{
  "size": 0,
  "aggs": {
    "brands": {
      "terms": { "field": "brand" }
    },
    "storage_sizes": {
      "terms": { "field": "storage" }
    },
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 300 },   
          { "from": 300, "to": 500 },  
          { "from": 500 }  
        ]
      }
    }
  }
}<p>응답에는 <strong>브랜드</strong>, <strong>스토리지</strong>, <strong>가격에</strong> 대한 카운트가 포함되어 필터와 패싯을 만드는 데 도움이 됩니다.</p>"aggregations": {
   "brands": {
     "doc_count_error_upper_bound": 0,
     "sum_other_doc_count": 0,
     "buckets": [
       {
         "key": "Microsoft",
         "doc_count": 1
       },
       {
         "key": "Nintendo",
         "doc_count": 1
       },
       {
         "key": "Sony",
         "doc_count": 1
       },
       {
         "key": "Valve",
         "doc_count": 1
       }
     ]
   },
   "storage_sizes": {
     "doc_count_error_upper_bound": 0,
     "sum_other_doc_count": 0,
     "buckets": [
       {
         "key": "1TB",
         "doc_count": 2
       },
       {
         "key": "512GB",
         "doc_count": 2
       }
     ]
   },
   "price_ranges": {
     "buckets": [
       {
         "key": "*-300.0",
         "to": 300,
         "doc_count": 1
       },
       {
         "key": "300.0-500.0",
         "from": 300,
         "to": 500,
         "doc_count": 3
       },
       {
         "key": "500.0-*",
         "from": 500,
         "doc_count": 0
       }
     ]
   }
 }<h2>필터 및 패싯에 대한 머신 러닝/AI 기반 접근 방식</h2><p>이 접근 방식에서는 인공 지능(AI) 기술을 포함한 머신 러닝(ML) 모델이 데이터 속성을 분석하여 관련 필터와 패싯을 생성합니다. ML/AI는 미리 정의된 규칙에 의존하는 대신 인덱싱된 데이터 특성을 활용합니다. 이를 통해 새로운 패싯과 필터를 동적으로 검색할 수 있습니다.</p><p><strong>장점</strong>:</p><ul><li><p><strong>자동 업데이트:</strong> 수동으로 조정할 필요 없이 새로운 필터와 패싯이 자동으로 생성됩니다.</p></li><li><p><strong>새로운 속성 발견:</strong> <strong>이전에는 고려하지 않았던 </strong>데이터 특성을 필터로 식별하여 검색 환경을 더욱 풍부하게 만들 수 있습니다.</p></li><li><p><strong>수동 작업 감소:</strong> AI가 사용 가능한 데이터에서 학습하므로 팀에서 필터링 규칙을 지속적으로 정의하고 업데이트할 필요가 없습니다.</p></li></ul><p><strong>단점:</strong></p><ul><li><p><strong>유지 관리의 복잡성:</strong> 모델을 사용하려면 생성된 필터의 일관성을 보장하기 위해 사전 검증이 필요할 수 있습니다.</p></li><li><p><strong>ML 및 AI 전문 지식이 필요합니다:</strong> 이 솔루션은 모델 성능을 미세 조정하고 모니터링할 수 있는 자격을 갖춘 전문가가 필요합니다.</p></li><li><p><strong>관련 없는 필터의 위험:</strong> 모델이 제대로 보정되지 않은 경우 사용자에게 유용하지 않은 패싯을 생성할 수 있습니다.</p></li><li><p><strong>비용:</strong> ML 및 AI를 사용하려면 타사 서비스가 필요할 수 있으므로 운영 비용이 증가할 수 있습니다.</p></li></ul><p>잘 보정된 모델과 잘 만들어진 프롬프트가 있더라도 생성된 패싯은 검토 단계를 거쳐야 한다는 점에 유의할 필요가 있습니다. 이 검증은 수동 또는 모더레이션 규칙에 따라 이루어질 수 있으며, 콘텐츠가 적절하고 안전한지 확인합니다. 반드시 단점이 있는 것은 아니지만, 사용자에게 제공하기 전에 패싯의 품질과 적합성을 확인하는 것은 중요한 고려 사항입니다.</p><h3>필터/패싯 구현 - AI 접근 방식</h3><p>이 데모에서는 AI 모델을 사용하여 자동으로 제품 특성을 분석하고 관련 속성을 제안합니다. 잘 구조화된 프롬프트를 통해 카탈로그에서 정보를 추출하고 이를 필터와 패싯으로 변환합니다. 아래에서 프로세스의 각 단계를 설명합니다.</p><p>처음에는 <strong>추론 API를</strong> 사용하여 ML 서비스와의 통합을 위해 엔드포인트를 등록할 것입니다. 아래는 <strong>OpenAI 서비스와의</strong> 통합 예시입니다.</p>PUT _inference/completion/generate_filter_ia
{
   "service": "openai",
   "service_settings": {
       "api_key": "your-key",
       "model_id": "gpt-4o-mini"
   }
}<p>이제 프롬프트를 실행하고 모델에서 생성된 새 필터를 가져오는 파이프라인을 정의합니다.</p>PUT /_ingest/pipeline/generate_filter_ai
{
   "processors": [
     {
       "script": {
         "source": """ctx.prompt = "You are an expert in data organization for search and product categorization. Your task is to analyze the following product and identify the best dynamic facets that can be used in an e-commerce search experience. Product: " + ctx.name + "description: " + ctx.description + "Instructions: - Analyze the product name and description. - Extract only the dynamic facets (technological features or product characteristics that can be inferred from the description, try to create max 3 facets by characteristics found). Put the values into an array. Using key and value, e.g. dynamic_facets: [{ \"name\": \"Gaming Experience\", \"value\": \"Haptic Feedback\" },{ \"name\": \"Gaming Experience\", \"value\": \"Adaptive Triggers\" } - Return only a JSON."
         """
       }
     },
     {
       "inference": {
         "model_id": "generate_filter_ia",
         "input_output": {
           "input_field": "prompt",
           "output_field": "result"
         }
       }
     },
     {
       "gsub": {
         "field": "result",
         "pattern": "```json",
         "replacement": ""
       }
     },
     {
       "json" : {
         "field" : "result",
         "strict_json_parsing": false,
         "add_to_root" : true
       }
     },
     {
       "remove": {
         "field": "result"
       }
     },
     {
       "remove": {
         "field": "prompt"
       }
     }
   ]
}<p>"PlayStation 5" 제품에 대한 이 파이프라인의 시뮬레이션을 다음 설명과 함께 실행합니다:</p><p><em>놀라운 게임: 놀라운 그래픽에 감탄하고 새로운 PS5의 기능을 경험해 보세요.</em></p><p><em>놀라운 몰입감: 햅틱 피드백, 적응형 트리거, 3D 오디오 기술을 지원하여 더욱 깊이 있는 게임 환경을 경험하세요.</em></p><p><em>슬림한 디자인: PS5 디지털 에디션으로 게이머는 세련되고 컴팩트한 디자인에 강력한 게임 기술을 즐길 수 있습니다.</em></p><p><em>1TB의 저장 공간: 1TB의 내장 SSD 스토리지로 좋아하는 게임을 준비해 두고 플레이하세요.</em></p><p><em>이전 버전과의 호환성 및 게임 부스트: PS5 콘솔은 4,000개 이상의 PS4 게임을 플레이할 수 있습니다. 게임 부스트를 사용하면 최고의 PS4 콘솔 게임에서 더욱 빠르고 부드러운 프레임 속도를 즐길 수 있습니다.</em></p><p>이 시뮬레이션에서 생성된 프롬프트 출력을 관찰해 보겠습니다.</p>{
 "docs": [
   {
     "doc": {
       "_index": "index",
       "_version": "-3",
       "_id": "1",
       "_source": {
         "name": "Play Station 5",
         "result": """```json
{
 "dynamic_facets": [
   { "name": "Storage Capacity", "value": "1TB SSD" },
   { "name": "Graphics Technology", "value": "Stunning Graphics" },
   { "name": "Audio Technology", "value": "3D Audio" }
 ]
}
```""",
         "description": "Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games.",
         "model_id": "generate_filter_ia",
         "prompt": """You are an expert in data organization for search and product categorization. Your task is to analyze the following product and identify the best dynamic facets that can be used in an e-commerce search experience. Product: Play Station 5description: Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games.Instructions: - Analyze the product name and description. - Extract only the dynamic facets (technological features or product characteristics that can be inferred from the description, try create max 3 facets by characteristics found). Put the values like arrays. Using key and value, e.g. dynamic_facets: [{ "name": "Gaming Experience", "value": "Haptic Feedback" },{ "name": "Gaming Experience", "value": "Adaptive Triggers" } - Return only a JSON."""
       },
       "_ingest": {
         "timestamp": "2025-03-19T22:14:32.0161803Z"
       }
     }
   }
 ]
}<p>이제 새 인덱스에 새 필드인 <strong>dynamic_facets가</strong> 추가되어 AI가 생성한 패싯을 저장합니다.</p>PUT videogames_1
{
 "mappings": {
   "properties": {
     "name": { "type": "text" },
     "brand": { "type": "keyword" },
     "storage": { "type": "keyword" },
     "price": { "type": "float" },
     "description": { "type": "text" },
     "dynamic_facets": { "type": "nested",
     "properties": { "name": { "type": "keyword" },
                     "value": { "type": "keyword" } } }
   }
 }
}<p><strong>재색인 API를</strong> 사용하여 <strong>비디오게임</strong> 인덱스를 <strong>비디오게임_1로</strong> 재색인하고, 이 과정에서 <strong>생성_필터_ai</strong> 파이프라인을 적용합니다. 이 파이프라인은 인덱싱 중에 동적 패싯을 자동으로 생성합니다.</p>POST _reindex?wait_for_completion=false
{
 "source": {
   "index": "videogames"
 },
 "dest": {
   "index": "videogames_1",
   "pipeline": "generate_filter_ai"
 }
}<p>이제 검색을 실행하여 새 필터를 가져옵니다:</p>GET videogames_1/_search
{
 "size": 0,
 "query": {
   "match": {
     "name": "nintendo"
   }
 },
 "aggs": {
   "dynamic_facets": {
     "nested": {
       "path": "dynamic_facets"
     },
     "aggs": {
       "facets": {
         "terms": {
           "field": "dynamic_facets.name"
         },
         "aggs": {
           "facets": {
             "terms": {
               "field": "dynamic_facets.value"
             }
           }
         }
       }
     }
   }
 }
}<p>결과:</p>"aggregations": {
   "dynamic_facets": {
     "doc_count": 3,
     "facets": {
       "doc_count_error_upper_bound": 0,
       "sum_other_doc_count": 0,
       "buckets": [
         {
           "key": "Frame Rate",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "120 FPS",
                 "doc_count": 1
               }
             ]
           }
         },
         {
           "key": "Gaming Resolution",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "4K",
                 "doc_count": 1
               }
             ]
           }
         },
         {
           "key": "Graphics Processing Power",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "12 Teraflops",
                 "doc_count": 1
               }
             ]
           }
         }
       ]
     }
   }
 }<p>패싯의 구현을 상징하기 위해 아래는 간단한 프런트엔드입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb0d6aa40caf7a91a/6a170b86ab7f0839afdb9eb6/12b6d9d4f4d0985848d92841545fd22b7253ae6d-1600x1288.png" alt="패싯 구현" /><p>제시된 UI 코드는 <a href="https://gist.github.com/andreluiz1987/06d9ec1b381e942e9def0e969bd811a0">여기에</a> 있습니다.</p><h2>결론</h2><p>필터와 패싯을 만드는 두 가지 접근 방식 모두 장점과 우려되는 점이 있습니다. 수동 규칙을 기반으로 하는 고전적인 접근 방식은 제어와 비용 절감 효과를 제공하지만 지속적인 업데이트가 필요하고 새로운 제품이나 기능에 동적으로 적응하지 못합니다.</p><p>반면, AI 및 머신러닝 기반 접근 방식은 패싯 추출을 자동화하여 검색을 더욱 유연하게 만들고 수동 개입 없이 새로운 속성을 발견할 수 있게 해줍니다. 그러나 이 접근 방식은 구현 및 유지 관리가 더 복잡할 수 있으며 일관된 결과를 보장하기 위해 보정이 필요할 수 있습니다.</p><p>기존 방식과 AI 기반 방식 중 어떤 방식을 선택할지는 비즈니스의 요구와 복잡성에 따라 달라집니다. 데이터 속성이 안정적이고 예측 가능한 간단한 시나리오의 경우, 기존 접근 방식이 더 효율적이고 유지 관리가 쉬우며 인프라 및 AI 모델을 통해 불필요한 비용을 피할 수 있습니다. 반면에 ML/AI를 사용하여 패싯을 추출하면 검색 환경을 개선하고 필터링을 더욱 지능적으로 만들어 상당한 가치를 더할 수 있습니다.</p><p>중요한 것은 자동화가 투자를 정당화하는지, 아니면 기존 솔루션이 이미 비즈니스 요구 사항을 효과적으로 충족하는지 평가하는 것입니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/filters-facets-using-ml</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/filters-facets-using-ml</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[ML 연구]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4084864dcdaa25d3/6a170b880c485781f901aaa9/6f196643d573614fe5124705c7e4db9bfce004b0-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 03 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[동의어 API를 사용하여 동의어를 자동화하고 업로드하는 방법]]></title>
    <description><![CDATA[LLM을 사용해 자동으로 동의어를 식별하고 생성하는 방법을 알아보고, 프로그래밍 방식으로 Elasticsearch 동의어 API에 용어를 로드할 수 있는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p>효율적인 사용자 경험을 제공하기 위해서는 검색 결과의 품질을 개선하는 것이 필수적입니다. 검색을 최적화하는 한 가지 방법은 동의어를 통해 쿼리된 용어를 자동으로 확장하는 것입니다. 이를 통해 쿼리를 보다 폭넓게 해석하여 언어의 다양성을 포괄하고 결과 매칭을 개선할 수 있습니다.</p><p>이 블로그에서는 대규모 언어 모델(LLM)을 사용해 자동으로 동의어를 식별하고 생성하여 이러한 용어를 프로그래밍 방식으로 Elasticsearch의 동의어 API에 로드할 수 있는 방법을 살펴봅니다.</p><h2>동의어는 언제 사용하나요?</h2><p>동의어를 사용하면 벡터 검색에 비해 더 빠르고 비용 효율적인 솔루션이 될 수 있습니다. 임베딩에 대한 깊은 지식이나 복잡한 벡터 수집 프로세스가 필요하지 않으므로 구현이 더 간단합니다.</p><p>또한 벡터 검색은 임베딩 인덱싱 및 검색을 위해 더 큰 저장 용량과 메모리를 필요로 하기 때문에 리소스 소비가 더 적습니다.</p><p>또 다른 중요한 측면은 검색 지역화입니다. 동의어를 사용하면 현지 언어와 관습에 따라 용어를 조정할 수 있습니다. 이는 임베딩이 지역 표현이나 국가별 용어와 일치하지 않을 수 있는 상황에서 유용합니다. 예를 들어 일부 단어나 약어는 지역에 따라 다른 의미를 가질 수 있지만 현지 사용자에게는 자연스럽게 동의어로 취급됩니다. 브라질에서는 이런 일이 매우 흔합니다. "아바칵시" 와 "아나나스" 는 같은 과일(파인애플)이지만 북동부의 일부 지역에서는 두 번째 용어가 더 일반적으로 사용됩니다. 마찬가지로 동남부에서 잘 알려진 "팡 프랑세스" 는 북동부에서는 "팡 카레카" 로 알려져 있을 수 있습니다.</p><h2>LLM을 사용하여 동의어를 생성하는 방법은 무엇인가요?</h2><p>동의어를 자동으로 구하려면 용어의 문맥을 분석하고 적절한 변형을 제안하는 LLM을 사용할 수 있습니다. 이 접근 방식을 사용하면 동의어를 동적으로 확장할 수 있으므로 고정된 사전에 의존하지 않고도 더 광범위하고 정확한 검색을 보장할 수 있습니다.</p><p>이 데모에서는 LLM을 사용하여 이커머스 제품의 동의어를 생성합니다. 많은 검색에서 쿼리된 용어의 변형으로 인해 결과가 거의 또는 전혀 반환되지 않습니다. 동의어를 사용하면 이 문제를 해결할 수 있습니다. 예를 들어 ' "스마트폰" '을 검색하면 다양한 모델의 휴대폰이 표시되어 사용자가 원하는 제품을 찾을 수 있습니다.</p><h3>필수 구성 요소</h3><p>시작하기 전에 환경을 설정하고 필요한 종속성을 정의해야 합니다. Elastic에서 제공하는 솔루션을 사용해 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">Docker에서 로컬로 Elasticsearch와 Kibana를 실행할</a> 것입니다. 코드는 Python v3.9.6으로 작성되며 다음과 같은 종속성이 있습니다:</p>pip install openai==1.59.8 elasticsearch==8.15.1<h3>제품 색인 생성</h3><p>처음에는 동의어가 지원되지 않는 제품 색인을 생성합니다. 이렇게 하면 쿼리의 유효성을 검사한 다음 동의어가 포함된 인덱스와 비교할 수 있습니다.</p><p>인덱스를 생성하기 위해 Kibana DevTools에서 다음 명령을 사용하여 제품 데이터 세트를 일괄 로드합니다:</p>POST _bulk
{"index": {"_index": "products", "_id": 10001}}
{"category": "Electronics", "name": "iPhone 14 Pro"}
{"index": {"_index": "products", "_id": 10007}}
{"category": "Electronics", "name": "MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10013}}
{"category": "Electronics", "name": "Samsung Galaxy Tab S8"}
{"index": {"_index": "products", "_id": 10037}}
{"category": "Electronics", "name": "Apple Watch Series 8"}
{"index": {"_index": "products", "_id": 10049}}
{"category": "Electronics", "name": "Kindle Paperwhite"}
{"index": {"_index": "products", "_id": 10067}}
{"category": "Electronics", "name": "Samsung QLED 4K TV"}
{"index": {"_index": "products", "_id": 10073}}
{"category": "Electronics", "name": "HP Spectre x360 Laptop"}
{"index": {"_index": "products", "_id": 10079}}
{"category": "Electronics", "name": "Apple AirPods Pro"}
{"index": {"_index": "products", "_id": 10115}}
{"category": "Electronics", "name": "Amazon Echo Show 10"}
{"index": {"_index": "products", "_id": 10121}}
{"category": "Electronics", "name": "Apple iPad Air"}
{"index": {"_index": "products", "_id": 10127}}
{"category": "Electronics", "name": "Apple AirPods Max"}
{"index": {"_index": "products", "_id": 10151}}
{"category": "Electronics", "name": "Sony WH-1000XM4 Headphones"}
{"index": {"_index": "products", "_id": 10157}}
{"category": "Electronics", "name": "Google Pixel 6 Pro"}
{"index": {"_index": "products", "_id": 10163}}
{"category": "Electronics", "name": "Apple MacBook Air"}
{"index": {"_index": "products", "_id": 10181}}
{"category": "Electronics", "name": "Google Pixelbook Go"}
{"index": {"_index": "products", "_id": 10187}}
{"category": "Electronics", "name": "Sonos Beam Soundbar"}
{"index": {"_index": "products", "_id": 10199}}
{"category": "Electronics", "name": "Apple TV 4K"}
{"index": {"_index": "products", "_id": 10205}}
{"category": "Electronics", "name": "Samsung Galaxy Watch 4"}
{"index": {"_index": "products", "_id": 10211}}
{"category": "Electronics", "name": "Apple MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10223}}
{"category": "Electronics", "name": "Amazon Echo Dot (4th Gen)"}<h3>LLM 와 동의어 생성</h3><p>이 단계에서는 LLM을 사용하여 동의어를 동적으로 생성합니다. 이를 위해 OpenAI API를 통합하여 적절한 모델과 프롬프트를 정의할 것입니다. LLM은 제품 카테고리와 이름을 수신하여 동의어가 문맥과 관련이 있는지 확인합니다.</p>import json
import logging

from openai import OpenAI

def call_gpt(prompt, model):
    try:
        logging.info("generate synonyms by llm...")
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7,
            max_tokens=1000
        )
        content = response.choices[0].message.content.strip()
        return content
    except Exception as e:
        logging.error(f"Failed to use model: {e}")
        return None

def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms<p>생성된 제품 색인에서 "Electronics" 카테고리의 모든 항목을 검색하여 해당 이름을 LLM으로 보냅니다. 예상 출력은 다음과 같습니다:</p>{
  "iPhone 14 Pro": ["iPhone", "smartphone", "mobile", "handset"],
  "MacBook Pro 16-inch": ["MacBook", "Laptop", "Notebook", "Ultrabook"],
  "Samsung Galaxy Tab S8": ["Tab", "Tablet", "Slate", "Pad"],
  "Bose QuietComfort 35 Headphones": ["Headphones", "earphones", "earbuds", "headset"]
}<p>생성된 동의어를 사용하면 동의어 API를 사용하여 Elasticsearch에 동의어를 등록할 수 있습니다.</p><h3>동의어 API로 동의어 관리하기</h3><p>동의어 API는 시스템 내에서 직접 동의어 집합을 효율적으로 관리할 수 있는 방법을 제공합니다. 각 동의어 세트는 동의어 규칙으로 구성되며, 여기서 단어 그룹은 검색에서 동등한 것으로 취급됩니다.</p><p><strong>동의어 집합 생성 예시</strong></p>PUT _synonyms/my-synonyms-set
{
  "synonyms_set": [
    {
      "id": "rule-1",
      "synonyms": "hello, hi"
    },
    {
      "synonyms": "bye, goodbye"
    }
  ]
}<p>
이렇게 하면 "hello" 및 "hi" 가 동등한 것으로 취급되는 "my-synonyms-set,", "bye" 및 "goodbye라는 집합이 만들어집니다."</p><h2>제품 카탈로그에 동의어 생성 구현하기</h2><p>다음은 동의어 집합을 구축하고 이를 Elasticsearch에 삽입하는 방법입니다. 동의어 규칙은 LLM에서 제안한 동의어 매핑을 기반으로 생성됩니다. 각 규칙에는 슬러그 형식의 제품 이름에 해당하는 ID와 LLM에서 계산한 동의어 목록이 있습니다.</p>import json
import logging

from elasticsearch import Elasticsearch
from slugify import slugify

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)

def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       response = es.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Error create synonyms: {str(e)}")
       return None<p>다음은 동의어 집합을 생성하기 위한 요청 페이로드입니다:</p>{
   "synonyms_set":[
      {
         "id": "iphone-14-pro",
         "synonyms": "iPhone, smartphone, mobile, handset"
      },
      {
         "id": "macbook-pro-16-inch",
         "synonyms": "MacBook, Laptop, Notebook, Computer"
      },
      {
         "id": "samsung-galaxy-tab-s8",
         "synonyms": "Tablet, Slate, Pad, Device"
      },
      {
         "id": "garmin-forerunner-945",
         "synonyms": "Forerunner, smartwatch, fitness watch, GPS watch"
      },
      {
         "id": "bose-quietcomfort-35-headphones",
         "synonyms": "Headphones, Earphones, Headset, Cans"
      }
   ]
}<p>클러스터에 동의어 집합이 생성되면 정의된 집합을 사용하여 동의어를 지원하는 새 인덱스를 생성하는 다음 단계로 넘어갈 수 있습니다.</p><p>LLM에서 생성한 동의어와 동의어 API에서 정의한 동의어 세트 생성이 포함된 전체 Python 코드는 아래와 같습니다:</p>import json
import logging

from elasticsearch import Elasticsearch
from openai import OpenAI
from slugify import slugify

logging.basicConfig(level=logging.INFO)

client = OpenAI(
   api_key="your-key",
)

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)


def call_gpt(prompt, model):
   try:
       logging.info("generate synonyms by llm...")
       response = client.chat.completions.create(
           model=model,
           messages=[{"role": "user", "content": prompt}],
           temperature=0.7,
           max_tokens=1000
       )
       content = response.choices[0].message.content.strip()
       return content
   except Exception as e:
       logging.error(f"Failed to use model: {e}")
       return None


def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms


def get_products(category):
   query = {
       "size": 50,
       "_source": ["name"],
       "query": {
           "bool": {
               "filter": [
                   {
                       "term": {
                           "category.keyword": category
                       }
                   }
               ]
           }
       }
   }
   response = es.search(index="products", body=query)

   if response["hits"]["total"]["value"] &gt; 0:
       product_names = [hit["_source"]["name"] for hit in response["hits"]["hits"]]
       return product_names
   else:
       return []


def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       es_client = get_client_es()
       response = es_client.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Erro update synonyms: {str(e)}")
       return None


if __name__ == '__main__':
   category = "Electronics"
   products = get_products("Electronics")
   llm_synonyms = generate_synonyms(category, products)
   mount_synonyms(llm_synonyms)<h3>동의어 지원으로 색인 생성</h3><p><code>products</code> 인덱스의 모든 데이터가 재색인되는 새 인덱스가 생성됩니다. 이 인덱스는 앞서 만든 <code>products-synonyms-set</code> 을 적용하는 <code>synonyms_filter</code> 을 사용합니다.</p><p>다음은 동의어를 사용하도록 구성된 인덱스 매핑입니다:</p>PUT products_02
{
  "settings": {
    "analysis": {
      "filter": {
        "synonyms_filter": {
          "type": "synonym",
          "synonyms_set": "products-synonyms-set",
          "updateable": true
        }
      },
      "analyzer": {
        "synonyms_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "synonyms_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "ID": {
        "type": "long"
      },
      "category": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "standard",
        "search_analyzer": "synonyms_analyzer"
      }
    }
  }
}<h3><code>products</code> 색인 재색인하기</h3><p>이제 <strong>재색인 API를</strong> 사용하여 <code>products</code> 인덱스의 데이터를 동의어 지원을 포함하는 새로운 <code>products_02</code> 인덱스로 마이그레이션합니다. 다음 코드는 Kibana 개발자 도구에서 실행되었습니다:
</p>POST _reindex
{
  "source": {
    "index": "products"
  },
  "dest": {
    "index": "products_02"
  }
}<p>마이그레이션이 완료되면 <code>products_02</code> 인덱스가 채워지고 구성된 동의어 집합을 사용하여 검색을 검증할 준비가 됩니다.</p><h3>동의어로 검색 유효성 검사</h3><p>두 색인 간의 검색 결과를 비교해 보겠습니다. 두 인덱스에서 동일한 쿼리를 실행하고 동의어가 결과를 검색하는 데 사용되고 있는지 확인합니다.</p><h4><code>products</code> 색인에서 검색(동의어 제외)</h4><p>Kibana를 사용해 검색을 수행하고 결과를 분석합니다. 분석 &gt; 검색 메뉴에서 생성한 인덱스의 데이터를 시각화할 수 있는 데이터 보기를 만듭니다.</p><p>Discovery에서 데이터 보기를 클릭하고 이름과 인덱스 패턴을 정의합니다. "<strong>products</strong>" 인덱스의 경우 "<strong>products</strong>" 패턴을 사용합니다. 그런 다음 이 과정을 반복하여 "<strong>products_02"</strong>패턴을 사용하여 "<strong>products_02 " 인덱스에 대한</strong> 새 데이터 뷰를 만듭니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte826fd932cfeb9df/6a17fdffec0f8912aa5a6841/3ad4a6891a3905e96532a312932fdf3a8216aec2-1600x599.png" alt="" /><p>데이터 보기를 구성했으면 Analytics &gt; Discovery로 돌아가 유효성 검사를 시작할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba3729c60068e8a0/6a17fe01e9ea87ba2aa9c82a/422c4b2b51abae6580cad25085d1b8a365fc6b9e-1294x850.png" alt="" /><p>여기서 DataView 제품을 선택하고 "태블릿" 이라는 용어를 검색한 후 "Kindle Paperwhite" 및 "Apple iPad Air" 와 같은 제품이 있다는 것을 알고 있음에도 불구하고 결과가 표시되지 않습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c6a383dd4cb0157/6a17fe02577262671d1bce0c/e4ae3a785fdd93f48d7c7d204185ded149126f2c-1600x862.png" alt="" /><h4><code>products_02</code> 색인에서 검색(동의어 지원)</h4><p>동의어를 지원하는 "<strong>products_synonyms</strong>" 데이터 뷰에서 동일한 쿼리를 수행했을 때 제품이 성공적으로 검색되었습니다. 이는 구성된 동의어 세트가 올바르게 작동하여 검색된 용어의 다양한 변형이 예상 결과를 반환하는지 확인합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt986b4706e2f70014/6a17fe043e9e454edbba16d3/e609749c39e90d5c82fa846af6124679dd62bcb8-1600x526.png" alt="" /><p>Kibana 개발자 도구에서 직접 동일한 쿼리를 실행하여 동일한 결과를 얻을 수 있습니다. Elasticsearch 검색 API를 사용해 products_02 인덱스를 검색하기만 하면 됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe829a3c4d7aac60/6a17fe05e8fbce03d73a1bd7/504d0d1f96dcfbceb309063dc0716bcee64ad2f8-1600x870.png" alt="" /><h2>결론</h2><p>Elasticsearch에서 동의어를 구현함으로써 제품 카탈로그 검색의 정확도와 범위가 개선되었습니다. 핵심적인 차별화 요소는 사전 정의된 목록이 필요 없이 상황에 따라 자동으로 동의어를 생성하는 <strong>LLM을</strong> 사용했다는 점입니다. 이 모델은 제품 이름과 카테고리를 분석하여 이커머스와 관련된 동의어를 확보했습니다.</p><p>또한 동의어 <strong>API는</strong> 사전 관리를 간소화하여 동의어 집합을 동적으로 수정할 수 있도록 했습니다. 이러한 접근 방식을 통해 검색은 더욱 유연해지고 다양한 사용자 쿼리 패턴에 적응할 수 있게 되었습니다.</p><p>이 프로세스는 새로운 데이터와 모델 조정을 통해 지속적으로 개선할 수 있어 점점 더 효율적인 연구 환경을 보장합니다.</p><h2>참고 자료</h2><p><strong>로컬에서 Elasticsearch 실행</strong></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html</a></p><p><strong>동의어 API</strong></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-synonyms-automate</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-synonyms-automate</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[기본]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f0247b9bc1d1ccd/6a17fe07ec0f891c745a6845/05a3cfeaa387561d5334ca3f1609035ddfff7481-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 27 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch에서 지연 상호작용 모델 확장하기 - 2부]]></title>
    <description><![CDATA[이 문서에서는 디스크 공간 사용량을 줄이고 계산 효율성을 개선하는 등 대규모 프로덕션 워크로드에 대비하여 지연 상호작용 벡터를 준비하는 기술을 살펴봅니다.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">ColPali에 대한 이전 블로그</a>에서는 Elasticsearch로 시각적 검색 애플리케이션을 생성하는 방법을 살펴보았습니다. 주로 ColPali와 같은 모델이 애플리케이션에 가져다주는 가치에 초점을 맞췄지만, 이러한 모델은 E5와 같은 바이인코더를 사용하는 벡터 검색에 비해 성능상의 단점이 있습니다.</p><p><a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">1부</a>의 예시를 바탕으로, 이 블로그는 다양한 기법과 Elasticsearch의 강력한 벡터 검색 도구를 사용하여 대규모 프로덕션 작업에 적합한 지연 상호작용 벡터를 준비하는 방법을 탐구합니다.</p><p>전체 코드 예시는 <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/colpali">GitHub</a>에서 확인하실 수 있습니다.</p><h2>후기 상호작용 모델의 과제</h2><p>ColPali는 인덱스에 있는 문서에 대해 페이지당 1000개 이상의 벡터를 생성합니다.</p><p>이로 인해 지연 상호작용 벡터를 사용할 때 두 가지 문제가 발생합니다.</p><ol><li><p>디스크 공간: 이러한 모든 벡터를 디스크에 저장하면 상당한 저장 공간 사용량이 발생하며, 이는 확장 시 비용이 많이 들 수 있습니다.</p></li><li><p>계산: <code>maxSimDotProduct()</code> 비교를 사용하여 문서 순위를 매길 때는 각 문서에 대한 이러한 모든 벡터를 쿼리의 N 벡터와 비교해야 합니다.</p></li></ol><p>이러한 문제를 해결하기 위한 몇 가지 기술을 살펴보겠습니다.</p><h2>지연 상호작용 모델을 최적화하는 기술</h2><h3>비트 벡터</h3><p>디스크 공간을 줄이기 위해 이미지를 비트 벡터로 압축할 수 있습니다. 다음과 같은 간단한 Python 함수를 사용하여 멀티 벡터를 비트 벡터로 변환할 수 있습니다.</p>def to_bit_vectors(embeddings: list) -&gt; list:
    return [
        np.packbits(np.where(np.array(embedding) &gt; 0, 1, 0))
        .astype(np.int8)
        .tobytes()
        .hex()
        for embedding in embeddings
    ]<p>이 함수의 핵심 개념은 간단합니다. 0보다 큰 값은 1이 되고, 0보다 작은 값은 0이 됩니다. 그 결과 0과 1로 이루어진 배열이 생성되며, 이를 16진수 스트링으로 변환하여 비트 벡터를 나타냅니다.</p><p>인덱스 매핑을 위해 <code>element_type</code> 매개변수를 <code>bit</code>로 설정했습니다.</p>mappings = {
    "mappings": {
        "properties": {
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>인덱스에 새로운 비트 벡터를 모두 작성한 후, 다음 코드를 사용하여 비트 벡터의 순위를 매길 수 있습니다.</p>query = "What do companies use for recruiting?"
query_vector = to_bit_vectors(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimInvHamming(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcaf0c8f999d68701/6a17f46696142a6f46eb1c56/51b989446e4099745971e1eac27d147a78d13e0a-1600x480.png" alt="" /><p>약간의 정확도를 희생하는 대신 비트 마스크, SIMD 등과 같은 최적화를 활용할 수 있는 해밍 거리(<code>maxSimInvHamming(...)</code>)를 사용할 수 있습니다. <a href="https://www.elastic.co/search-labs/blog/bit-vectors-in-elasticsearch">비트 벡터와 해밍 거리에 대한 자세한 내용은 블로그</a>에서 확인하세요.</p><p>대안으로, 쿼리 벡터를 비트 벡터로 변환하지 않고 완전한 충실도의 지연 상호작용 벡터로 검색할 수 있습니다.</p>query = "What do companies use for recruiting?"
query_vector = create_col_pali_query_vectors(query)
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2fa6c89fb451b6e0/6a17f468af47b65da9cde0d9/89f3c795c6dc44b2f7d46801320288316b47e1b2-1600x488.png" alt="비트 벡터를 사용하여 지연 상호작용 모델을 최적화한 결과" /><p>이는 비대칭 유사도 함수를 사용하여 벡터를 비교합니다.</p><p></p><p>두 비트 벡터 사이의 일정한 해밍 거리에 대해 생각해 보겠습니다. 문서 벡터 <em>D</em>가 있다고 가정해 봅시다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4b6ece1ec4dedac/6a17f4694b055d248143234e/366a70a23e37d403788327b7aefc873fd4482f5e-1235x86.png" alt="" /><p>그리고 쿼리 벡터 <em>Q</em>가 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7721df9c8f0d84f1/6a17f46b3e03d77cf54f2dcb/978ec31e0afbc0eaebf548a015b628c0cad84625-1247x84.png" alt="" /><p></p><p>간단한 이진 양자화는 벡터 <em>D</em>를 <code>10101101</code>로, <em>Q</em>를 <code>11111011</code>로 변환합니다. 해밍 거리를 찾으려면 직접적인 비트 연산이 필요하며, 이는 매우 빠릅니다. 이 경우 해밍 거리는 <code>01010110</code>이며 비트 수는 4입니다. 따라서 점수 계산은 해당 해밍 거리의 역수가 됩니다. 유사한 벡터일수록 해밍 거리가 작아지므로 이를 반전시키면 유사한 벡터일수록 더 높은 점수를 받을 수 있습니다. 구체적으로 여기서는 1/4 = <code>0.25</code>가 됩니다.</p><p>그러나 각 차원의 크기가 어떻게 줄어드는지 주목하세요. <code>1</code> 은 <code>1</code>입니다. 그래서 <em>Q</em>의 경우, <code>0.01</code>과 <code>0.79</code> 사이의 차이점이 사라집니다. <code>&gt;0</code>에 따라 단순히 양자화하기 때문에 Q 벡터가 양자화되지 않은 곳에서 작은 트릭을 수행할 수 있습니다. 이렇게 하면 매우 빠른 비트 단위 연산을 할 수는 없지만, D가 여전히 양자화되어 있기 때문에 스토리지 비용을 낮게 유지할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blted50315518e2d599/6a17f46c414c6463709452d3/508e8498ba969534d2a0131d431c75735fc27cb9-1399x611.png" alt="" /><p>간단히 말해서, 이는 <em>Q</em>에 제공된 정보를 유지하여 거리 추정 품질을 향상시키고 저장 공간을 최소화합니다.</p><p>비트 벡터를 사용하면 디스크 공간과 쿼리 시 계산 부하를 크게 줄일 수 있습니다. 하지만 할 수 있는 일은 더 많습니다.</p><h3>평균 벡터</h3><p>수십만 개의 문서에 걸쳐 검색을 확장하려면 비트 벡터가 제공하는 성능상의 이점만으로는 충분하지 않습니다. 이러한 유형의 워크로드에 맞게 확장하려면 벡터 검색을 위해 Elasticsearch의 HNSW 인덱스 구조를 활용해야 합니다.</p><p>ColPali는 문서당 약 천 개의 벡터를 생성하는데, 이는 HNSW 그래프에 추가하기에는 너무 많습니다. 따라서 벡터의 수를 줄여야 합니다. 이를 위해 ColPali에 의해 생성된 모든 문서 벡터의 평균을 계산하여 이미지를 임베드할 때 문서의 의미에 대한 단일 표현을 만들 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc053425fbfcf8de8/6a17f46f148009faf1b488aa/7c3b4dffb70bd95f67deb35f8c01e73c5286c2ab-1476x1102.png" alt="모든 지연 상호작용 벡터에 대한 평균 벡터" /><p>현재로서는 Elastic 자체 내에서 이 작업이 불가능하며, Elasticsearch에서 벡터를 수집하기 전에 벡터를 전처리해야 합니다. </p><p>Logstash 또는 수집 파이프라인으로 이 작업을 수행할 수 있지만 여기서는 간단한 Python 함수를 사용하겠습니다:</p>def to_avg_vector(vectors):
    vectors_array = np.array(vectors)
    
    avg_vector = np.mean(vectors_array, axis=0)
    
    norm = np.linalg.norm(avg_vector)
    if norm &gt; 0:
        normalized_avg_vector = avg_vector / norm
    else:
        normalized_avg_vector = avg_vector

    return normalized_avg_vector.tolist()<p>또한 내적 유사도를 사용할 수 있도록 벡터를 정규화하고 있습니다.</p><p>모든 ColPali 벡터를 평균 벡터로 변환한 후, 이를 dense_vector 필드에 인덱싱할 수 있습니다.</p>mappings = {
    "mappings": {
        "properties": {
            "avg_vector": {
                "type": "dense_vector",
                "dims": 128,
                "index": True,
                "similarity": "dot_product"
            },
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>이로 인해 지연 상호작용 벡터와 함께 더 많은 정보를 저장하게 되므로 전체 디스크 사용량이 증가할 것이라는 점을 고려해야 합니다. 추가로, 수십억 개의 벡터에 대한 검색을 확장할 수 있도록 HNSW 그래프를 보유하기 위해 추가 RAM을 사용할 것입니다. RAM 사용량을 줄이기 위해 우리는 인기 있는 <a href="https://www.elastic.co/search-labs/blog/optimized-scalar-quantization-elasticsearch">BBQ 기능</a>을 활용할 수 있습니다. 그 결과, 기존 방식으로는 불가능했던 대규모 데이터 세트에 대한 빠른 검색 결과를 얻을 수 있습니다.</p><p>이제 knn 쿼리로 검색하면 가장 관련성 높은 문서를 찾을 수 있습니다.</p>query = "What do companies use for recruiting?"
query_vector = to_avg_vector(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "knn": {
        "field": "avg_vector",
        "query_vector": query_vector,
        "k": 10,
        "num_candidates": 100
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf4ac6b7fd444f935/6a17f471a29299d76ad02dc0/a500f9907020f032c3b1c7a48ed8c759dc2a1dd4-1600x498.png" alt="" /><p>불행히도 이전의 베스트 매칭은 3위로 떨어졌습니다.</p><p>이 문제를 해결하기 위해 다단계 검색을 수행할 수 있습니다. 첫 번째 단계에서는 knn 쿼리를 사용하여 수백만 개의 문서에서 쿼리에 가장 적합한 후보를 검색하고 있습니다. 두 번째 단계에서는 ColPali 지연 상호작용 벡터의 충실도가 높은 상위 k(여기서는 10)만 다시 순위를 매깁니다. </p>query = "What do companies use for recruiting?"
col_pali_vector = create_col_pali_query_vectors(query)
avg_vector = to_avg_vector(col_pali_vector)
es_query = {
  "_source": False,
  "retriever": {
    "rescorer": {
      "retriever": {
        "knn": {
          "field": "avg_vector",
          "query_vector": avg_vector,
          "k": 10,
          "num_candidates": 100
        }
      },
      "rescore": {
        "window_size": 10,
        "query": {
          "rescore_query": {
            "script_score": {
              "query": {
                "match_all": {}
              },
              "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                  "query_vector": col_pali_vector
                }
              }
            }
          }
        }
      }
    }
  },
  "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt265edd5f37be40b7/6a17f4733e9e45583dba15e6/797f490860af87fcf29f34668a5c4511419c6fd0-1600x501.png" alt="지연 상호작용 모델 최적화를 위해 평균 벡터를 사용한 결과" /><p>여기서는 8.18에서 도입된 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.18/retriever.html#rescorer-retriever">재점수 검색기</a>를 사용하여 결과를 다시 순위 지정합니다. 점수를 재지정한 후 다시 가장 좋은 매치가 첫 번째 위치에 있음을 확인합니다. </p><p>참고: 프로덕션 애플리케이션에서는 최대 시뮬레이션 함수가 여전히 비교적 성능이 좋으므로 10보다 훨씬 높은 k를 사용할 수 있습니다.</p><h3>토큰 풀링</h3><p>토큰 풀링은 중복 정보(예: 흰색 배경 패치)를 풀링하여 멀티 벡터 임베딩의 시퀀스 길이를 줄입니다. 이 기법은 페이지의 신호 대부분을 보존하면서 임베딩 수를 줄입니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3d643e6ac908f640/6a17f4754b055d3783432352/09eae0b768f4b450e555d7e53e57561bd52de2c0-1412x1056.png" alt="지연 상호작용 모델 최적화를 위한 토큰 풀링" /><p>토큰 풀링은 클러스터링 알고리즘을 사용하여 문서 내에서 유사한 토큰 임베딩을 클러스터로 그룹화하는 방식으로 작동합니다. 그런 다음, 각 클러스터 내 벡터의 평균을 계산하여 단일한 집계 표현을 생성합니다. 이 집계된 벡터는 그룹 내의 원래 토큰을 대체하여 문서 신호의 상당한 손실 없이 총 벡터 수를 줄입니다.</p><p>ColPali 논문에서는 대부분의 데이터 세트에 대해 초기 풀 팩터 값을 3으로 제안하고 있는데, 이는 원래 성능의 97.8%를 유지하면서 총 벡터 수를 66.7%로 줄입니다. </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d78a15c6944a1ea/6a17f477414c64a2929452d9/343cc9eaf54af8c7a125d4115838f3c7d5659de0-1600x1007.png" alt="지연 상호작용 모델 최적화를 위한 풀 요소" /><p>그러나 주의해야 합니다. 매우 밀도가 높고 텍스트가 많으며 공백이 거의 없는 문서를 포함하는 "Shift" 데이터 세트는 풀 요소가 증가함에 따라 성능이 급격히 저하됩니다.</p><p>풀링된 벡터를 생성하려면 colpali_engine 라이브러리를 사용할 수 있습니다.</p>from colpali_engine.compression.token_pooling import HierarchicalTokenPooler

pooler = HierarchicalTokenPooler(pool_factor=3) # test on your data for a good pool_factor

def pool_vectors(embedding: list) -&gt; list:
    tensor = torch.tensor(embedding).unsqueeze(0)
    pooled = pooler.pool_embeddings(tensor)
    return pooled.squeeze(0).tolist()<p>이제 차원이 약 66.7% 감소한 벡터를 가지고 있습니다. 평소처럼 인덱싱하고 <code>maxSimDotProduct()</code> 함수로 검색할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc460c14031814ede/6a17f4794202292bf629f72d/2412c5db7d79a590b96d42fe01f140c42f010612-1600x481.png" alt="지연 상호작용 모델 결과" /><p>검색 결과의 정확도가 약간 떨어지는 대신, 우수한 검색 결과를 얻을 수 있습니다.</p><p>힌트: pool_factor 값을 높이면(100~200) 평균 벡터 솔루션과 여기서 논의한 솔루션 사이의 중간 지점을 찾을 수도 있습니다. 문서당 약 5~10개의 벡터가 있을 때, 중첩된 필드에 인덱스를 만들어 HNSW 인덱스를 활용하는 것이 현실적이 됩니다.</p><h2>코스 인코더 vs. 지연 상호작용 vs. 바이 인코더</h2><p>지금까지 배운 바에 따르면, ColPali 또는 ColBERT와 같은 지연 상호작용 모델은 다른 AI 검색 기술과 비교할 때 어떤 위치에 있을까요?</p><p>최대 시뮬레이션 함수는 크로스 인코더에 비해 저렴하지만, 쿼리-문서 쌍마다 두 벡터를 비교하는 바이 인코더를 사용한 벡터 검색보다 여전히 더 많은 비교와 계산이 필요합니다. </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfd97f3bba3e5b17/6a17f47be31791b0052d5943/75e1fc9e601aa7a6e88f137565c1919166c7a71c-1480x458.png" alt="코스 인코더 vs. 지연 상호작용 모델 vs. 바이 인코더" /><p>이러한 이유로, 지연 상호작용 모델은 일반적으로 상위 k 검색 결과의 순위를 재조정하는 데에만 사용하는 것을 권장합니다. 또한 이를 필드 유형 이름인 rank_vectors로 캡처합니다.</p><p>그렇다면 크로스 인코더는 어떨까요? 쿼리 시점에 실행 비용이 저렴하기 때문에 지연 상호작용 모델이 더 나은 것일까요? 종종 그렇듯이 답은 '상황에 따라 다르다'입니다. 크로스 인코더는 일반적으로 더 높은 품질의 결과를 생성하지만, 쿼리 문서 쌍이 트랜스포머 모델을 통해 완전한 패스를 수행해야 하기 때문에 많은 계산 자원이 필요합니다. 또한 벡터 인덱싱이 필요하지 않고 상태 비저장 방식으로 작동할 수 있다는 장점도 있습니다. 그 결과는 다음과 같습니다.</p><ul><li><p>디스크 공간 사용량 감소</p></li><li><p>더욱 간소화된 시스템</p></li><li><p>더 높은 검색 결과 품질</p></li><li><p>더 높은 대기 시간으로 인해 순위 재지정을 깊이 수행할 수 없음</p></li></ul><p>반면, 지연 상호작용 모델은 이 계산 일부를 인덱싱할 때 분산시켜 쿼리 비용을 낮출 수 있습니다. 그 대가로 벡터를 인덱싱해야 하므로 인덱스 파이프라인이 더 복잡해지고 이러한 벡터를 저장하는 데 더 많은 디스크 공간이 필요합니다.</p><p>특히 ColPali의 경우, 이미지에서 정보를 분석하는 것은 많은 데이터를 포함하기 때문에 매우 비용이 많이 듭니다. 이 경우 쿼리 시점에 이 정보를 평가하는 것은 너무 리소스 집약적이고 느리기 때문에 ColPali와 같은 후기 상호 작용 모델을 사용하는 것이 유리합니다. </p><p>ColBERT와 같은 지연 상호작용 모델의 경우, 대부분의 크로스 인코더(예: elastic-rerank-v1)처럼 텍스트 데이터를 기반으로 작동하기 때문에 디스크 공간 절약 및 간편성 측면에서 크로스 인코더를 사용하는 것이 더 유리할 수 있습니다.</p><p>사용 사례에 대한 장단점을 비교하고 Elasticsearch가 제공하는 다양한 도구를 실험하여 최고의 검색 애플리케이션을 구축하는 것이 좋습니다.</p><h2>결론</h2><p>이 블로그에서는 Elasticsearch에서 대규모 벡터 검색을 위한 ColPali와 같은 지연 상호작용 모델을 최적화하는 다양한 기술을 탐구했습니다. 지연 상호작용 모델은 검색 효율성과 순위 품질 사이에서 뛰어난 균형을 제공하지만, 저장 공간 및 계산과 관련된 문제점도 야기합니다.</p><p>이러한 과제를 해결하기 위해 다음을 살펴보았습니다.</p><ul><li><p><strong>비트 벡터</strong>를 사용하여 해밍 거리 또는 비대칭 최대 유사도와 같은 효율적인 유사도 계산을 활용하면서 디스크 공간을 크게 줄일 수 있습니다.</p></li><li><p>여러 임베딩을 하나의 밀집 표현으로 압축하기 위해 <strong>평균 벡터</strong>를 사용하여 HNSW 인덱싱을 통한 효율적인 검색을 가능하게 합니다.</p></li><li><p><strong>토큰 풀링</strong>을 통해 의미적 무결성을 유지관리하면서 중복된 임베딩을 지능적으로 병합하여 쿼리 시 계산 오버헤드를 줄입니다.</p></li></ul><p>Elasticsearch는 사용자의 요구에 따라 검색 애플리케이션을 맞춤 설정하고 최적화할 수 있는 강력한 툴킷을 제공합니다. 검색 속도, 순위 품질 또는 저장 공간 효율성 중 무엇을 우선시하든, 이러한 도구와 기술을 사용하면 실제 응용 분야에 필요한 성능과 품질의 균형을 맞출 수 있습니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[벡터 데이터베이스]]></category>
    <dc:creator><![CDATA[Peter Straßer,Benjamin Trent]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt97a536033e6b0a56/6a17f47dfbc5f88c86491c0d/c780b78a07573f2df1cfef8b29a7109f839b0ab3-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>