Hybrid search
출처: 문서
본문
하이브리드 검색
2.11에 도입됨 {: .label .label-purple }
하이브리드 검색은 키워드 검색과 시맨틱 검색을 결합해서 검색 관련성을 높여요. 하이브리드 검색을 구현하려면 검색 시점에 실행되는 검색 파이프라인을 설정해야 해요. 검색 파이프라인은 중간 단계에서 검색 결과를 가로채서 문서 점수를 정규화하고 결합하는 처리를 적용해요.
하이브리드 검색에는 두 가지 검색 페이즈 결과 프로세서(search phase results processor)를 사용할 수 있고, 둘은 결합하는 대상이 달라요:
- normalization processor는 점수 기반이에요. 각 쿼리 절의 점수를 공통 스케일로 변환한 뒤 결합하며, 문서 간 점수 격차는 그대로 유지해요. 두 문서 점수의 차이에 최종 랭킹이 반영해야 할 정보가 담겨 있거나, 정규화 기법·조합 기법·score bounds를 통한 세밀한 제어가 필요할 때 이 프로세서를 선택하세요.
- score ranker processor는 랭킹 기반이에요. 점수 자체는 무시하고, reciprocal rank fusion(RRF)을 사용해 각 쿼리 절 결과에서의 위치에 따라 문서를 결합해요. 쿼리 절이 문서에 점수를 매기는 방식을 먼저 측정하지 않고도 동작하는 구성을 원할 때 이 프로세서를 선택하세요. 자세한 내용은 Reciprocal rank fusion를 참고하세요.
다음 예제는 normalization processor를 사용해요. 자신의 데이터와 judgment list로 두 프로세서를 비교해 보려면 Optimizing hybrid search를 참고하세요.
필수 조건
이 예제를 따라 하려면 텍스트 임베딩 모델을 설정해야 해요. 자세한 내용은 Choosing a model을 참고하세요. 이미 텍스트 임베딩을 생성했다면 3단계로 건너뛰세요.
{: .note}
하이브리드 검색 구성하기
하이브리드 검색을 구성하는 방법은 두 가지가 있어요:
- 자동화 워크플로 (빠른 설정에 권장): 최소한의 구성으로 ingest 파이프라인, 인덱스, 검색 파이프라인을 자동으로 생성해요.
- 수동 설정 (커스텀 구성에 권장): 더 큰 유연성과 제어력을 위해 각 구성 요소를 수동으로 구성해요.
자동화 워크플로
OpenSearch는 ingest 파이프라인, 인덱스, 검색 파이프라인을 자동으로 생성하는 워크플로 템플릿을 제공해요. 워크플로를 생성할 때는 설정한 모델의 모델 ID를 반드시 제공해야 해요. 하이브리드 검색 워크플로 템플릿의 기본값을 검토해서 파라미터를 업데이트해야 하는지 판단하세요. 예를 들어 모델 차원이 기본값(1024)과 다르다면 output_dimension 파라미터에 모델의 차원을 지정하세요. 기본 하이브리드 검색 워크플로를 생성하려면 다음 요청을 보내세요:
POST /_plugins/_flow_framework/workflow?use_case=hybrid_search&provision=true
{
"create_ingest_pipeline.model_id": "mBGzipQB2gmRjlv_dOoB"
}
{% include copy-curl.html %}
OpenSearch는 생성된 워크플로의 워크플로 ID를 응답으로 돌려줘요:
{
"workflow_id" : "U_nMXJUBq_4FYQzMOS4B"
}
워크플로 상태를 확인하려면 다음 요청을 보내세요:
GET /_plugins/_flow_framework/workflow/U_nMXJUBq_4FYQzMOS4B/_status
{% include copy-curl.html %}
워크플로가 완료되면 state가 COMPLETED로 바뀌어요. 워크플로는 다음 구성 요소를 생성해요:
nlp-ingest-pipeline이라는 이름의 ingest 파이프라인my-nlp-index라는 이름의 인덱스nlp-search-pipeline이라는 이름의 검색 파이프라인
이제 4, 5단계를 이어서 진행해서 인덱스에 문서를 수집하고 인덱스를 검색할 수 있어요.
수동 설정
하이브리드 검색을 수동으로 구성하려면 다음 단계를 따르세요:
1단계: ingest 파이프라인 생성하기
벡터 임베딩을 생성하려면 문서 필드의 텍스트를 벡터 임베딩으로 변환하는 text_embedding 프로세서가 포함된 ingest 파이프라인을 생성해야 해요. 프로세서의 field_map은 벡터 임베딩을 생성할 입력 필드와 임베딩을 저장할 출력 필드를 결정해요.
다음 예제 요청은 passage_text의 텍스트를 텍스트 임베딩으로 변환하고 그 임베딩을 passage_embedding에 저장하는 ingest 파이프라인을 생성해요:
PUT /_ingest/pipeline/nlp-ingest-pipeline
{
"description": "A text embedding pipeline",
"processors": [
{
"text_embedding": {
"model_id": "bQ1J8ooBpBj3wT4HVUsb",
"field_map": {
"passage_text": "passage_embedding"
}
}
}
]
}
{% include copy-curl.html %}
2단계: 수집용 인덱스 생성하기
파이프라인에 정의한 텍스트 임베딩 프로세서를 사용하려면 벡터 인덱스를 생성하면서, 이전 단계에서 만든 파이프라인을 기본 파이프라인으로 추가하세요. field_map에 정의된 필드가 올바른 타입으로 매핑되어 있는지 확인하세요. 예제를 이어서 보면, passage_embedding 필드는 모델 차원과 일치하는 차원을 가진 k-NN 벡터로 매핑해야 하고, 마찬가지로 passage_text 필드는 text로 매핑해야 해요.
다음 예제 요청은 기본 ingest 파이프라인이 설정된 벡터 인덱스를 생성해요:
PUT /my-nlp-index
{
"settings": {
"index.knn": true,
"default_pipeline": "nlp-ingest-pipeline"
},
"mappings": {
"properties": {
"id": {
"type": "text"
},
"passage_embedding": {
"type": "knn_vector",
"dimension": 768,
"method": {
"engine": "lucene",
"space_type": "l2",
"name": "hnsw",
"parameters": {}
}
},
"passage_text": {
"type": "text"
}
}
}
}
{% include copy-curl.html %}
벡터 인덱스 생성과 지원되는 메서드 사용 방법에 대한 자세한 내용은 Creating a vector index를 참고하세요.
3단계: 검색 파이프라인 구성하기
normalization-processor가 포함된 검색 파이프라인을 구성하려면 다음 요청을 사용하세요. 프로세서의 정규화 기법은 min_max로, 조합 기법은 arithmetic_mean으로 설정돼요. weights 배열은 각 쿼리 절에 할당되는 가중치를 소수 백분율로 지정해요:
PUT /_search/pipeline/nlp-search-pipeline
{
"description": "Post processor for hybrid search",
"phase_results_processors": [
{
"normalization-processor": {
"normalization": {
"technique": "min_max"
},
"combination": {
"technique": "arithmetic_mean",
"parameters": {
"weights": [
0.3,
0.7
]
}
}
}
}
]
}
{% include copy-curl.html %}
4단계: 인덱스에 문서 수집하기
이전 단계에서 만든 인덱스에 문서를 수집하려면 다음 요청들을 보내세요:
PUT /my-nlp-index/_doc/1
{
"passage_text": "Hello world",
"id": "s1"
}
{% include copy-curl.html %}
PUT /my-nlp-index/_doc/2
{
"passage_text": "Hi planet",
"id": "s2"
}
{% include copy-curl.html %}
문서가 인덱스에 수집되기 전에 ingest 파이프라인이 문서에 text_embedding 프로세서를 실행해서 passage_text 필드에 대한 텍스트 임베딩을 생성해요. 인덱싱된 문서에는 원본 텍스트가 담긴 passage_text 필드와 벡터 임베딩이 담긴 passage_embedding 필드가 포함돼요.
5단계: 하이브리드 검색으로 인덱스 검색하기
인덱스에서 하이브리드 검색을 수행하려면 키워드 검색과 시맨틱 검색의 결과를 결합하는 hybrid 쿼리를 사용하세요.
예제: neural 쿼리와 match 쿼리 결합하기
다음 예제 요청은 두 쿼리 절(neural 쿼리와 match 쿼리)을 결합하고, 이전 단계에서 만든 검색 파이프라인을 쿼리 파라미터로 지정해요:
GET /my-nlp-index/_search?search_pipeline=nlp-search-pipeline
{
"_source": {
"exclude": [
"passage_embedding"
]
},
"query": {
"hybrid": {
"queries": [
{
"match": {
"passage_text": {
"query": "Hi world"
}
}
},
{
"neural": {
"passage_embedding": {
"query_text": "Hi world",
"model_id": "aVeif4oB5Vm0Tdw8zYO2",
"k": 5
}
}
}
]
}
}
}
{% include copy-curl.html %}
또는 my-nlp-index 인덱스에 기본 검색 파이프라인을 설정할 수도 있어요. 자세한 내용은 Default search pipeline을 참고하세요.
응답에는 매칭된 문서가 포함돼요:
{
"took" : 36,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.2251667,
"hits" : [
{
"_index" : "my-nlp-index",
"_id" : "1",
"_score" : 1.2251667,
"_source" : {
"passage_text" : "Hello world",
"id" : "s1"
}
}
]
}
}
{% include copy-curl.html %}
예제: match 쿼리와 term 쿼리 결합하기
다음 예제 요청은 두 쿼리 절(match 쿼리와 term 쿼리)을 결합하고, 이전 단계에서 만든 검색 파이프라인을 쿼리 파라미터로 지정해요:
GET /my-nlp-index/_search?search_pipeline=nlp-search-pipeline
{
"_source": {
"exclude": [
"passage_embedding"
]
},
"query": {
"hybrid": {
"queries": [
{
"match":{
"passage_text": "hello"
}
},
{
"term":{
"passage_text":{
"value":"planet"
}
}
}
]
}
}
}
{% include copy-curl.html %}
응답에는 매칭된 문서들이 포함돼요:
{
"took": 11,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0.7,
"hits": [
{
"_index": "my-nlp-index",
"_id": "2",
"_score": 0.7,
"_source": {
"id": "s2",
"passage_text": "Hi planet"
}
},
{
"_index": "my-nlp-index",
"_id": "1",
"_score": 0.3,
"_source": {
"id": "s1",
"passage_text": "Hello world"
}
}
]
}
}
{% include copy-curl.html %}
데이터 필터링
하이브리드 검색은 두 가지 필터링 방식을 지원해요:
- **사전 필터링(pre-filtering)**은 문서에 점수를 매기기 전에 문서를 제거해요. 사전 필터링을 사용하려면
hybrid쿼리에 최상위filter를 추가하세요. 하이브리드 검색 결과를 필터링할 때 가장 많이 쓰이는 방식이에요. 자세한 내용은 Hybrid search with pre-filtering을 참고하세요 - **사후 필터링(post-filtering)**은 모든 점수 계산이 끝난 후에 문서를 제거해요. 사후 필터링을 사용하려면 검색 요청에
post_filter를 추가하세요. 애그리게이션을 사용하는 패싯 검색에서, 패싯에는 필터링되지 않은 쿼리가 반영되면서 표시되는 히트만 필터링되길 원할 때 이 방식을 사용하세요. 자세한 내용은 Hybrid search with post-filtering을 참고하세요.
다음 단계
- 튜토리얼을 살펴보면서 AI 검색 애플리케이션을 구축하는 방법을 알아보세요.