본문 바로가기
WIKI 기술 지식 베이스

Generating sparse vector embeddings automatically

원문 보기 위키 갱신

출처: 문서

본문

희소 벡터 임베딩 자동 생성하기

희소 벡터 임베딩을 자동으로 생성하면 신경망 희소 검색이 어휘 검색처럼 작동할 수 있어요. 이 캡슐화를 활용하려면 수집 중에 문서 텍스트에서 희소 벡터 임베딩을 생성하고 저장하는 수집 파이프라인을 설정하세요. 쿼리 시에는 일반 텍스트를 입력하면 검색을 위해 자동으로 벡터 임베딩으로 변환돼요.

신경망 희소 검색은 다음과 같이 작동해요:

  • 수집 시, 신경망 희소 검색은 희소 인코딩 모델을 사용하여 텍스트 필드에서 희소 벡터 임베딩을 생성해요.
  • 쿼리 시, 신경망 희소 검색은 두 가지 검색 모드 중 하나로 작동해요:
    • 문서 전용 모드(기본값): 수집 시 희소 인코딩 모델이 문서에서 희소 벡터 임베딩을 생성해요. 쿼리 시 신경망 희소 검색은 쿼리 텍스트를 토큰화하고 조회 테이블에서 토큰 가중치를 가져와요. 이 접근 방식은 검색 관련성이 약간 감소하는 대신 더 빠른 검색을 제공해요. 쿼리 시 토큰화는 다음 구성 요소로 수행할 수 있어요:
      • DL 모델 분석기(기본값): DL 모델 분석기는 내장 ML 모델을 사용해요. 이 접근 방식은 검색 관련성이 약간 감소하는 대신 더 빠른 검색을 제공해요.
      • 사용자 정의 토크나이저: Model API를 사용하여 사용자 정의 토크나이저를 배포하여 쿼리 텍스트를 토큰화할 수 있어요. 이 접근 방식은 신경망 희소 검색 구현 전반에 걸쳐 일관된 토큰화를 유지하면서 더 많은 유연성을 제공해요.
    • 바이인코더 모드: 희소 인코딩 모델이 문서와 쿼리 텍스트 모두에서 희소 벡터 임베딩을 생성해요. 이 접근 방식은 지연 시간이 증가하는 대신 더 나은 검색 관련성을 제공해요.

대부분의 사용 사례에서 성능과 관련성의 최상의 균형을 제공하므로 기본 문서 전용 모드와 DL 분석기를 사용하는 것이 좋아요. {: tip}

분석기와 함께 기본 문서 전용 모드는 다음과 같이 작동해요:

  1. 수집 시:

    • 등록된 희소 인코딩 모델이 희소 벡터 임베딩을 생성해요.
    • 이 임베딩은 인덱스에 토큰-가중치 쌍으로 저장돼요.
  2. 검색 시:

    • 쿼리 텍스트는 내장 DL 모델 분석기(해당 내장 ML 모델 토크나이저 사용)를 사용하여 분석돼요.
    • 토큰 가중치는 OpenSearch에 내장된 미리 계산된 조회 테이블에서 가져와요.
    • 토큰화는 희소 인코딩 모델이 기대하는 것과 일치하는데, 둘 다 동일한 토큰화 방식을 사용하기 때문이에요.

따라서 수집 시에는 ML 모델을 선택하고 적용해야 하지만, 검색 시에는 분석기(모델 아님)만 지정하면 돼요.

희소 인코딩 모델/분석기 호환성

다음 표는 문서 전용 모드에서 사용할 수 있는 모든 모델을 나열해요. 각 모델은 검색 시 사용해야 하는 호환 분석기와 쌍을 이뤄요. 언어 요구 사항(영어 또는 다국어)과 성능 요구 사항에 따라 선택하세요.

모델 분석기 BEIR 관련성 MIRACL 관련성 모델 매개변수
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v1 bert-uncased 0.490 N/A 133M
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v2-distill bert-uncased 0.504 N/A 67M
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v2-mini bert-uncased 0.497 N/A 23M
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v3-distill bert-uncased 0.517 N/A 67M
amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v3-gte bert-uncased 0.546 N/A 133M
amazon/neural-sparse/opensearch-neural-sparse-encoding-multilingual-v1 mbert-uncased 0.500 0.629 168M

예제: 분석기와 함께 기본 문서 전용 모드 사용하기

이 예제에서는 권장되는 문서 전용 모드와 DL 모델 분석기를 사용해요. 이 모드에서 OpenSearch는 수집 시 희소 인코딩 모델을 적용하고 검색 시 호환되는 DL 모델 분석기를 적용해요. 다른 모드의 예제는 신경망 희소 검색을 위한 사용자 정의 구성 사용을 참조하세요.

이 예제에서는 OpenSearch의 내장 머신러닝(ML) 모델 호스팅 및 수집 파이프라인과 함께 신경망 희소 검색을 사용할 거예요. 텍스트에서 임베딩으로의 변환이 OpenSearch 내에서 수행되므로 문서를 수집하고 검색할 때 텍스트를 사용할 거예요.

사전 요구 사항

시작하기 전에 사전 요구 사항을 완료하세요.

1단계: 수집을 위한 희소 인코딩 모델 구성

문서 전용 모드를 사용하려면 먼저 수집 시 사용할 희소 인코딩 모델을 선택하세요. 그런 다음 모델을 등록하고 배포하세요. 예를 들어 opensearch-neural-sparse-encoding-doc-v3-distill 모델을 등록하고 배포하려면 다음 요청을 사용하세요:

POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v3-distill",
  "version": "1.0.0",
  "model_format": "TORCH_SCRIPT"
}

{% include copy-curl.html %}

모델 등록은 비동기 작업이에요. OpenSearch는 등록하는 모든 모델에 대해 작업 ID를 반환해요:

{
  "task_id": "aFeif4oB5Vm0Tdw8yoN7",
  "status": "CREATED"
}

Get ML Task API를 호출하여 작업 상태를 확인할 수 있어요:

GET /_plugins/_ml/tasks/aFeif4oB5Vm0Tdw8yoN7

{% include copy-curl.html %}

작업이 완료되면 작업 상태가 COMPLETED로 변경되고 ML Tasks API 응답에 등록된 모델의 모델 ID가 포함돼요:

{
  "model_id": "<model ID>",
  "task_type": "REGISTER_MODEL",
  "function_name": "SPARSE_ENCODING",
  "state": "COMPLETED",
  "worker_node": [
    "4p6FVOmJRtu3wehDD74hzQ"
  ],
  "create_time": 1694358489722,
  "last_update_time": 1694358499139,
  "is_async": true
}

생성한 모델의 model_id를 기록해 두세요. 다음 단계에서 필요해요.

2단계: 수집 파이프라인 생성

희소 벡터 임베딩을 생성하려면 문서 필드의 텍스트를 벡터 임베딩으로 변환하는 sparse_encoding 프로세서가 포함된 수집 파이프라인을 생성해야 해요. 프로세서의 field_map은 벡터 임베딩을 생성할 입력 필드와 임베딩을 저장할 출력 필드를 결정해요.

다음 예제 요청은 passage_text의 텍스트가 희소 벡터 임베딩으로 변환되어 passage_embedding에 저장되는 수집 파이프라인을 생성해요. 요청에 등록된 모델의 모델 ID를 제공하세요:

PUT /_ingest/pipeline/nlp-ingest-pipeline-sparse
{
  "description": "An sparse encoding ingest pipeline",
  "processors": [
    {
      "sparse_encoding": {
        "model_id": "<bi-encoder or doc-only model ID>",
        "prune_type": "max_ratio",
        "prune_ratio": 0.1,
        "field_map": {
          "passage_text": "passage_embedding"
        }
      }
    }
  ]
}

{% include copy-curl.html %}

긴 텍스트를 구절로 분할하려면 sparse_encoding 프로세서 앞에 text_chunking 수집 프로세서를 사용하세요. 자세한 내용은 텍스트 청킹을 참조하세요.

3단계: 수집을 위한 인덱스 생성

파이프라인에 정의된 희소 인코딩 프로세서를 사용하려면 이전 단계에서 생성한 파이프라인을 기본 파이프라인으로 추가하여 순위 특징 인덱스를 생성하세요. field_map에 정의된 필드가 올바른 유형으로 매핑되었는지 확인하세요. 예제를 계속 진행하면 passage_embedding 필드는 rank_features로 매핑되어야 해요. 마찬가지로 passage_text 필드는 text로 매핑되어야 해요.

다음 예제 요청은 기본 수집 파이프라인으로 구성된 순위 특징 인덱스를 생성해요:

PUT /my-nlp-index
{
  "settings": {
    "default_pipeline": "nlp-ingest-pipeline-sparse"
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "text"
      },
      "passage_embedding": {
        "type": "rank_features"
      },
      "passage_text": {
        "type": "text"
      }
    }
  }
}

{% include copy-curl.html %}

디스크 공간을 절약하려면 다음과 같이 소스에서 임베딩 벡터를 제외할 수 있어요:

PUT /my-nlp-index
{
  "settings": {
    "default_pipeline": "nlp-ingest-pipeline-sparse"
  },
  "mappings": {
    "_source": {
      "excludes": [
        "passage_embedding"
      ]
    },
    "properties": {
      "id": {
        "type": "text"
      },
      "passage_embedding": {
        "type": "rank_features"
      },
      "passage_text": {
        "type": "text"
      }
    }
  }
}

{% include copy-curl.html %}

<token, weight> 쌍이 소스에서 제외되면 복구할 수 없어요. 이 최적화를 적용하기 전에 애플리케이션에 <token, weight> 쌍이 필요하지 않은지 확인하세요. {: .important}

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 %}

문서가 인덱스에 수집되기 전에 수집 파이프라인이 문서에 대해 sparse_encoding 프로세서를 실행하여 passage_text 필드에 대한 벡터 임베딩을 생성해요. 인덱싱된 문서에는 원본 텍스트가 포함된 passage_text 필드와 벡터 임베딩이 포함된 passage_embedding 필드가 포함돼요.

5단계: 데이터 검색

인덱스에서 신경망 희소 검색을 수행하려면 Query DSL 쿼리에서 neural_sparse 쿼리 절을 사용하세요.

다음 예제 요청은 neural_sparse 쿼리를 사용하여 원시 텍스트 쿼리로 관련 문서를 검색해요. 선택한 모델과 호환되는 analyzer를 지정하세요(희소 인코딩 모델/분석기 호환성 참조):

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "analyzer": "bert-uncased"
      }
    }
  }
}

{% include copy-curl.html %}

분석기를 지정하지 않으면 기본 bert-uncased 분석기가 사용돼요. 따라서 이 쿼리는 이전 쿼리와 동일해요:

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world"
      }
    }
  }
}

{% include copy-curl.html %}

응답에는 일치하는 문서가 포함돼요:

{
  "took" : 688,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 30.0029,
    "hits" : [
      {
        "_index" : "my-nlp-index",
        "_id" : "1",
        "_score" : 30.0029,
        "_source" : {
          "passage_text" : "Hello world",
          "passage_embedding" : {
            "!" : 0.8708904,
            "door" : 0.8587369,
            "hi" : 2.3929274,
            "worlds" : 2.7839446,
            "yes" : 0.75845814,
            "##world" : 2.5432441,
            "born" : 0.2682308,
            "nothing" : 0.8625516,
            "goodbye" : 0.17146169,
            "greeting" : 0.96817183,
            "birth" : 1.2788506,
            "come" : 0.1623208,
            "global" : 0.4371151,
            "it" : 0.42951578,
            "life" : 1.5750692,
            "thanks" : 0.26481047,
            "world" : 4.7300377,
            "tiny" : 0.5462298,
            "earth" : 2.6555297,
            "universe" : 2.0308156,
            "worldwide" : 1.3903781,
            "hello" : 6.696973,
            "so" : 0.20279501,
            "?" : 0.67785245
          },
          "id" : "s1"
        }
      },
      {
        "_index" : "my-nlp-index",
        "_id" : "2",
        "_score" : 16.480486,
        "_source" : {
          "passage_text" : "Hi planet",
          "passage_embedding" : {
            "hi" : 4.338913,
            "planets" : 2.7755864,
            "planet" : 5.0969057,
            "mars" : 1.7405145,
            "earth" : 2.6087382,
            "hello" : 3.3210192
          },
          "id" : "s2"
        }
      }
    ]
  }
}

희소 임베딩 소스와 관련된 디스크 및 네트워크 I/O 지연 시간을 최소화하려면 다음과 같이 쿼리에서 임베딩 벡터 소스를 제외할 수 있어요:

GET my-nlp-index/_search
{
  "_source": {
    "excludes": [
      "passage_embedding"
    ]
  },
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "analyzer": "bert-uncased"
      }
    }
  }
}

{% include copy-curl.html %}

바이인코더 모드

바이인코더 모드에서는 수집 및 쿼리 시간 모두에 사용할 바이인코더 모델을 등록하고 배포하세요:

POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "amazon/neural-sparse/opensearch-neural-sparse-encoding-v2-distill",
  "version": "1.0.0",
  "model_format": "TORCH_SCRIPT"
}

{% include copy-curl.html %}

배포 후 검색에 동일한 model_id를 사용하세요:

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "model_id": "<bi-encoder model_id>"
      }
    }
  }
}

{% include copy-curl.html %}

전체 예제는 신경망 희소 검색을 위한 사용자 정의 구성 사용을 참조하세요.

사용자 정의 토크나이저를 사용한 문서 전용 모드

사용자 정의 토크나이저와 함께 문서 전용 모드를 사용할 수 있어요. 토크나이저를 배포하려면 다음 요청을 보내세요:

POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "amazon/neural-sparse/opensearch-neural-sparse-tokenizer-v1",
  "version": "1.0.1",
  "model_format": "TORCH_SCRIPT"
}

{% include copy-curl.html %}

배포 후 쿼리에서 토크나이저의 model_id를 사용하세요:

GET my-nlp-index/_search
{
  "query": {
    "neural_sparse": {
      "passage_embedding": {
        "query_text": "Hi world",
        "model_id": "<tokenizer model_id>"
      }
    }
  }
}

{% include copy-curl.html %}

전체 예제는 신경망 희소 검색을 위한 사용자 정의 구성 사용을 참조하세요.

시맨틱 필드 사용

semantic 필드를 사용하면 신경망 희소 검색 구성이 간소화돼요. semantic 필드를 사용하려면 다음 단계를 따르세요. 자세한 내용은 시맨틱 필드 유형을 참조하세요.

1단계: 희소 인코딩 모델 등록 및 배포

먼저 1단계에 설명된 대로 희소 인코딩 모델을 등록하고 배포하세요.

2단계: 수집을 위해 시맨틱 필드가 있는 인덱스 생성

이전 단계에서 구성된 희소 인코딩 모델은 수집 시 희소 벡터 임베딩을 생성하는 데 사용돼요. semantic 필드를 사용할 때는 model_id를 수집에 사용된 모델의 ID로 설정하세요. 문서 전용 모드의 경우 search_model_id 필드에 ID를 제공하여 쿼리 시 사용할 모델을 추가로 지정할 수 있어요.

다음 예제는 희소 인코딩 모델을 사용하여 문서 전용 모드로 구성된 semantic 필드가 있는 인덱스를 생성하는 방법을 보여줘요. 긴 텍스트를 더 작은 구절로 자동 분할하려면 시맨틱 필드 구성에서 chunking을 true로 설정하세요:

PUT /my-nlp-index
{
  "mappings": {
    "properties": {
      "id": {
        "type": "text"
      },
      "passage_text": {
        "type": "semantic",
         "model_id": "_kPwYJcBmp4cG9LrUQsE",
         "search_model_id": "AUPwYJcBmp4cG9LrmQy8",
         "chunking": true
      }
    }
  }
}

{% include copy-curl.html %}

인덱스를 생성한 후 매핑을 검색하여 임베딩 필드가 자동으로 생성되었는지 확인할 수 있어요:

GET /my-nlp-index/_mapping
{
   "my-nlp-index": {
      "mappings": {
         "properties": {
            "id": {
               "type": "text"
            },
            "passage_text": {
               "type": "semantic",
               "model_id": "_kPwYJcBmp4cG9LrUQsE",
               "search_model_id": "AUPwYJcBmp4cG9LrmQy8",
               "raw_field_type": "text",
               "chunking": true
            },
            "passage_text_semantic_info": {
               "properties": {
                  "chunks": {
                     "type": "nested",
                     "properties": {
                        "embedding": {
                           "type": "rank_features"
                        },
                        "text": {
                           "type": "text"
                        }
                     }
                  },
                  "model": {
                     "properties": {
                        "id": {
                           "type": "text",
                           "index": false
                        },
                        "name": {
                           "type": "text",
                           "index": false
                        },
                        "type": {
                           "type": "text",
                           "index": false
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

{% include copy-curl.html %}

passage_text_semantic_info라는 객체 필드가 자동으로 생성돼요. 여기에는 임베딩을 저장하기 위한 rank_features 하위 필드와 모델 메타데이터를 캡처하기 위한 추가 텍스트 필드가 포함돼요.

3단계: 인덱스에 문서 수집

이전 단계에서 생성한 인덱스에 문서를 수집하려면 다음 요청을 보내세요:

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 %}

문서가 인덱스에 수집되기 전에 OpenSearch가 자동으로 텍스트를 청킹하고 각 청크에 대한 희소 벡터 임베딩을 생성해요. 임베딩이 제대로 생성되었는지 확인하려면 검색 요청을 실행하여 문서를 검색할 수 있어요:

GET /my-nlp-index/_doc/1
{
   "_index": "my-nlp-index",
   "_id": "1",
   "_version": 1,
   "_seq_no": 0,
   "_primary_term": 1,
   "found": true,
   "_source": {
      "passage_text": "Hello world",
      "passage_text_semantic_info": {
         "chunks": [
            {
               "text": "Hello world",
               "embedding": {
                  "hi": 0.5843902,
                  ...
               }
            }
         ],
         "model": {
            "name": "amazon/neural-sparse/opensearch-neural-sparse-encoding-doc-v3-distill",
            "id": "_kPwYJcBmp4cG9LrUQsE",
            "type": "SPARSE_ENCODING"
         }
      },
      "id": "s1"
   }
}

{% include copy-curl.html %}

3단계: 데이터 검색

의미론적 필드의 임베딩을 검색하려면 Query DSL 쿼리에서 neural 쿼리 절을 사용하세요.

다음 예제는 텍스트 입력을 사용하여 관련 문서를 검색하기 위해 neural 쿼리를 사용합니다. semantic 필드 이름만 지정하면 됩니다---OpenSearch가 자동으로 쿼리를 다시 작성하여 기본 임베딩 필드에 적용하고, 중첩된 객체를 적절히 처리합니다. OpenSearch가 인덱스 매핑에서 semantic 필드의 구성을 검색하므로 쿼리에 model_id를 제공할 필요가 없습니다:

GET my-nlp-index/_search
{
   "_source": {
      "excludes": [
         "passage_text_semantic_info"
      ]
   },
   "query": {
      "neural": {
         "passage_text": {
            "query_text": "Hi world"
         }
      }
   }
}

{% include copy-curl.html %}

응답에는 일치하는 문서가 포함됩니다:

{
   "took": 19,
   "timed_out": false,
   "_shards": {
      "total": 1,
      "successful": 1,
      "skipped": 0,
      "failed": 0
   },
   "hits": {
      "total": {
         "value": 2,
         "relation": "eq"
      },
      "max_score": 6.437132,
      "hits": [
         {
            "_index": "my-nlp-index",
            "_id": "1",
            "_score": 6.437132,
            "_source": {
               "passage_text": "Hello world",
               "id": "s1"
            }
         },
         {
            "_index": "my-nlp-index",
            "_id": "2",
            "_score": 5.063226,
            "_source": {
               "passage_text": "Hi planet",
               "id": "s2"
            }
         }
      ]
   }
}

또는 기본 제공 분석기를 사용하여 쿼리 텍스트를 토큰화할 수 있습니다:

GET my-nlp-index/_search
{
   "_source": {
      "excludes": [
         "passage_text_semantic_info"
      ]
   },
   "query": {
      "neural": {
         "passage_text": {
            "query_text": "Hi world",
            "semantic_field_search_analyzer": "bert-uncased"
         }
      }
   }
}

{% include copy-curl.html %}

쿼리를 더 간소화하려면 semantic 필드 구성에서 semantic_field_search_analyzer를 정의할 수 있습니다. 이렇게 하면 OpenSearch가 검색 중에 구성된 분석기를 자동으로 적용하므로 쿼리 자체에서 분석기를 생략할 수 있습니다.

신경 희소 검색 가속화

신경 희소 검색의 검색 시간 개선에 대한 자세한 내용은 신경 희소 검색 가속화를 참조하세요.

neural 쿼리와 함께 semantic 필드를 사용하는 경우 쿼리 가속이 지원되지 않습니다. 기본 rank_features 필드에 대해 직접 neural_sparse 쿼리를 실행하여 가속을 달성할 수 있습니다. {: .note}

문제 해결

이 섹션에는 신경 희소 검색 실행 중 발생하는 일반적인 문제를 해결하는 방법에 대한 정보가 포함되어 있습니다.

원격 커넥터 제한 예외

Amazon SageMaker와 같은 원격 서비스를 호출하기 위해 커넥터를 사용할 때 원격 커넥터 제한 예외로 인해 수집 및 검색 호출이 실패하는 경우가 있습니다.

OpenSearch 버전 2.15 이전의 경우 제한 예외가 원격 서비스에서 오류로 반환됩니다:

{
  "type": "status_exception",
  "reason": "Error from remote service: {\"message\":null}"
}

제한 예외를 완화하려면 커넥터의 client_config 객체에서 max_connection 설정에 지정된 최대 연결 수를 줄이세요. 이렇게 하면 최대 동시 연결 수가 원격 서비스의 임계값을 초과하지 않습니다. 또한 재시도 설정을 수정하여 수집 중 요청 급증을 방지할 수 있습니다.

다음 단계

더 알아보기 (Learn more)