https://docs.opensearch.org/latest/tutorials/vector-search/neural-search-tutorial/
기본적으로 OpenSearch는 Okapi BM25 알고리즘을 사용하여 문서 점수를 계산합니다. BM25는 키워드를 포함하는 쿼리에서는 좋은 성능을 보이지만 쿼리 용어의 의미론적 의미를 포착하지 못하는 키워드 기반 알고리즘입니다. 의미론적 검색은 키워드 기반 검색과 달리 검색 맥락에서 쿼리의 의미를 고려합니다. 따라서 의미론적 검색은 자연어 이해가 필요한 쿼리에서 좋은 성능을 보입니다.
이 튜토리얼에서는 다음 유형의 검색을 구현하는 방법을 배울 것입니다:
이 튜토리얼에서는 다음 OpenSearch 구성 요소를 사용할 것입니다:
튜토리얼을 따라가면서 이 모든 구성 요소에 대한 설명을 찾을 수 있으므로 일부에 익숙하지 않더라도 걱정하지 마세요. 앞의 목록의 각 링크는 해당 구성 요소에 대한 문서 섹션으로 이동합니다.
이 간단한 설정을 위해 OpenSearch에서 제공하는 기계 학습(ML) 모델과 전용 ML 노드가 없는 클러스터를 사용할 것입니다. 이 기본 로컬 설정이 작동하는지 확인하려면 다음 요청을 보내 ML 관련 클러스터 설정을 업데이트하세요:
PUT _cluster/settings
{
"persistent": {
"plugins.ml_commons.only_run_on_ml_node": "false",
"plugins.ml_commons.native_memory_threshold": "99"
}
}
커스텀 로컬 모델 설정의 경우 다음 요구사항에 주의하세요:
"allow_registering_model_via_url": "true" 클러스터 설정을 지정해야 합니다."only_run_on_ml_node": "true"를 지정하세요.ML 관련 클러스터 설정에 대한 자세한 정보는 "ML Commons 클러스터 설정"을 참조하세요.
이 튜토리얼은 다음 단계로 구성됩니다:
명령줄이나 OpenSearch Dashboards Dev Tools 콘솔을 사용하여 이 튜토리얼을 따라할 수 있습니다.
튜토리얼의 일부 단계에는 선택적 "테스트" 섹션이 포함되어 있습니다. 이 섹션의 요청을 실행하여 단계가 성공적으로 완료되었는지 확인할 수 있습니다.
완료 후 "정리" 섹션의 단계를 따라 생성된 모든 구성 요소를 삭제하세요.
먼저 수집 시간과 쿼리 시간 모두에서 텍스트 필드로부터 벡터 임베딩을 생성하기 위한 언어 모델을 선택해야 합니다.
이 튜토리얼에서는 Hugging Face의 DistilBERT 모델을 사용할 것입니다. 이는 벤치마킹 테스트에서 가장 좋은 결과를 보인 OpenSearch에서 사용 가능한 사전 훈련된 문장 변환기 모델 중 하나입니다(자세한 정보는 이 블로그 포스트 참조). 모델을 등록하려면 모델 이름, 버전, 차원이 필요합니다. 모델의 TorchScript 아티팩트에 해당하는 config_url 링크를 선택하여 사전 훈련된 모델 테이블에서 이 정보를 찾을 수 있습니다:
huggingface/sentence-transformers/msmarco-distilbert-base-tas-b1.0.3768벡터 인덱스를 설정할 때 필요하므로 모델의 차원을 기록해 두세요.
또는 모델에 대해 다음 옵션 중 하나를 선택할 수 있습니다:
모델 선택에 대한 정보는 "추가 읽기"를 참조하세요.
모델을 등록하고 배포하려면 등록 요청에서 모델 그룹 ID를 제공하세요:
POST /_plugins/_ml/models/_register?deploy=true
{
"name": "huggingface/sentence-transformers/msmarco-distilbert-base-tas-b",
"version": "1.0.3",
"model_format": "TORCH_SCRIPT"
}
모델 등록은 비동기 작업입니다. OpenSearch는 이 작업에 대한 작업 ID를 다시 보냅니다:
{
"task_id": "aFeif4oB5Vm0Tdw8yoN7",
"status": "CREATED"
}
OpenSearch는 URL에서 모델의 구성 파일과 모델 내용을 다운로드합니다. 모델이 10MB보다 크기 때문에 OpenSearch는 이를 최대 10MB의 청크로 분할하고 해당 청크를 모델 인덱스에 저장합니다. Tasks API를 사용하여 작업 상태를 확인할 수 있습니다:
GET /_plugins/_ml/tasks/aFeif4oB5Vm0Tdw8yoN7
OpenSearch는 등록된 모델을 모델 인덱스에 저장합니다. 모델을 배포하면 모델 인스턴스가 생성되고 모델이 메모리에 캐시됩니다.
작업이 완료되면 작업 상태가 COMPLETED가 되고 Tasks API 응답에는 배포된 모델의 모델 ID가 포함됩니다:
{
"model_id": "aVeif4oB5Vm0Tdw8zYO2",
"task_type": "REGISTER_MODEL",
"function_name": "TEXT_EMBEDDING",
"state": "COMPLETED",
"worker_node": [
"4p6FVOmJRtu3wehDD74hzQ"
],
"create_time": 1694358489722,
"last_update_time": 1694358499139,
"is_async": true
}
다음 단계들 중 몇 가지에서 이 모델을 사용하기 위해 모델 ID가 필요합니다.
OpenSearch는 언어 모델을 사용하여 텍스트를 벡터 임베딩으로 변환합니다. 수집 중에 OpenSearch는 요청의 텍스트 필드에 대한 벡터 임베딩을 생성합니다. 검색 중에는 동일한 모델을 적용하여 쿼리 텍스트에 대한 벡터 임베딩을 생성할 수 있으므로 문서에 대한 벡터 유사성 검색을 수행할 수 있습니다.
이제 모델을 배포했으므로 이 모델을 사용하여 하나의 프로세서를 포함하는 수집 파이프라인을 구성할 수 있습니다: 문서가 인덱스에 수집되기 전에 문서 필드를 변환하는 작업입니다. 이 예제에서는 텍스트로부터 벡터 임베딩을 생성하는 text_embedding 프로세서를 설정할 것입니다. 이전 섹션에서 설정한 모델의 model_id와 텍스트를 가져올 필드의 이름(text)과 임베딩을 기록할 필드의 이름(passage_embedding)을 지정하는 field_map이 필요합니다:
PUT /_ingest/pipeline/nlp-ingest-pipeline
{
"description": "An NLP ingest pipeline",
"processors": [
{
"text_embedding": {
"model_id": "aVeif4oB5Vm0Tdw8zYO2",
"field_map": {
"text": "passage_embedding"
}
}
}
]
}
이제 이미지 설명을 포함하는 text라는 필드와 텍스트의 벡터 임베딩을 포함하는 passage_embedding이라는 knn_vector 필드가 있는 벡터 인덱스를 생성할 것입니다. 또한 기본 수집 파이프라인을 이전 단계에서 생성한 nlp-ingest-pipeline으로 설정하세요:
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,
"space_type": "l2"
},
"text": {
"type": "text"
}
}
}
}
벡터 인덱스를 설정하면 나중에 passage_embedding 필드에서 벡터 검색을 수행할 수 있습니다.
이 단계에서는 인덱스에 여러 샘플 문서를 수집할 것입니다. 샘플 데이터는 Flickr 이미지 데이터셋에서 가져온 것입니다. 각 문서에는 이미지 설명에 해당하는 text 필드와 이미지 ID에 해당하는 id 필드가 포함되어 있습니다:
PUT /my-nlp-index/_doc/1
{
"text": "A West Virginia university women 's basketball team , officials , and a small gathering of fans are in a West Virginia arena .",
"id": "4319130149.jpg"
}
PUT /my-nlp-index/_doc/2
{
"text": "A wild animal races across an uncut field with a minimal amount of trees .",
"id": "1775029934.jpg"
}
PUT /my-nlp-index/_doc/3
{
"text": "People line the stands which advertise Freemont 's orthopedics , a cowboy rides a light brown bucking bronco .",
"id": "2664027527.jpg"
}
PUT /my-nlp-index/_doc/4
{
"text": "A man who is riding a wild horse in the rodeo is very near to falling off .",
"id": "4427058951.jpg"
}
PUT /my-nlp-index/_doc/5
{
"text": "A rodeo cowboy , wearing a cowboy hat , is being thrown off of a wild white horse .",
"id": "2691147709.jpg"
}
문서가 인덱스에 수집되면 text_embedding 프로세서는 벡터 임베딩을 포함하는 추가 필드를 생성하고 해당 필드를 문서에 추가합니다. 인덱싱된 예제 문서를 보려면 문서 1을 검색하세요:
GET /my-nlp-index/_doc/1
응답에는 원본 text 및 id 필드와 추가된 passage_embedding 필드를 포함하는 문서 _source가 포함됩니다:
{
"_index": "my-nlp-index",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"passage_embedding": [
0.04491629,
-0.34105563,
0.036822468,
-0.14139028,
...
],
"text": "A West Virginia university women 's basketball team , officials , and a small gathering of fans are in a West Virginia arena .",
"id": "4319130149.jpg"
}
}
이제 키워드 검색, 의미론적 검색, 그리고 두 가지의 조합을 사용하여 인덱스를 검색할 것입니다.
키워드 검색을 사용하려면 match 쿼리를 사용하세요. 결과에서 임베딩을 제외할 것입니다:
GET /my-nlp-index/_search
{
"_source": {
"excludes": [
"passage_embedding"
]
},
"query": {
"match": {
"text": {
"query": "wild west"
}
}
}
}
문서 3은 지정된 키워드를 포함하지 않기 때문에 반환되지 않습니다. rodeo와 cowboy라는 단어를 포함하는 문서들은 의미론적 의미가 고려되지 않기 때문에 낮은 점수를 받습니다.
의미론적 검색을 사용하려면 neural 쿼리를 사용하고 이전에 설정한 모델의 모델 ID를 제공하여 쿼리 텍스트에 대한 벡터 임베딩이 수집 시간에 사용된 모델과 동일한 모델로 생성되도록 하세요:
GET /my-nlp-index/_search
{
"_source": {
"excludes": [
"passage_embedding"
]
},
"query": {
"neural": {
"passage_embedding": {
"query_text": "wild west",
"model_id": "aVeif4oB5Vm0Tdw8zYO2",
"k": 5
}
}
}
}
이번에는 응답에 다섯 문서가 모두 포함될 뿐만 아니라 의미론적 검색이 의미론적 의미를 고려하기 때문에 문서 순서도 개선됩니다.
하이브리드 검색은 키워드와 의미론적 검색을 결합하여 검색 관련성을 향상시킵니다. 하이브리드 검색을 구현하려면 검색 시간에 실행되는 검색 파이프라인을 설정해야 합니다. 구성할 검색 파이프라인은 중간 단계에서 검색 결과를 가로채고 normalization-processor를 적용합니다. normalization-processor는 여러 쿼리 절의 문서 점수를 정규화하고 결합하여 선택된 정규화 및 결합 기법에 따라 문서를 다시 점수화합니다.
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
]
}
}
}
}
]
}
hybrid 쿼리를 사용하여 match와 neural 쿼리 절을 결합할 것입니다. 쿼리 매개변수의 요청에 이전에 생성한 nlp-search-pipeline을 적용해야 합니다:
GET /my-nlp-index/_search?search_pipeline=nlp-search-pipeline
{
"_source": {
"exclude": [
"passage_embedding"
]
},
"query": {
"hybrid": {
"queries": [
{
"match": {
"text": {
"query": "cowboy rodeo bronco"
}
}
},
{
"neural": {
"passage_embedding": {
"query_text": "wild west",
"model_id": "aVeif4oB5Vm0Tdw8zYO2",
"k": 5
}
}
}
]
}
}
}
OpenSearch는 wild west의 의미론적 의미와 일치하는 문서를 반환할 뿐만 아니라 이제 wild west 테마와 관련된 단어를 포함하는 문서들도 다른 문서들에 비해 상대적으로 높은 점수를 받습니다.
모든 요청에서 검색 파이프라인을 지정하는 대신 다음과 같이 인덱스의 기본 검색 파이프라인으로 설정할 수 있습니다:
PUT /my-nlp-index/_settings
{
"index.search.default_pipeline" : "nlp-search-pipeline"
}
이제 다양한 가중치, 정규화 기법, 결합 기법을 실험할 수 있습니다. 자세한 정보는 normalization-processor 및 hybrid query 문서를 참조하세요.
검색 템플릿을 사용하여 검색을 매개변수화할 수 있습니다. 검색 템플릿은 구현 세부사항을 숨기고 중첩 수준을 줄여 쿼리 복잡성을 줄입니다. 자세한 정보는 "검색 템플릿"을 참조하세요.
자동화된 워크플로우를 사용하여 의미론적 또는 하이브리드 검색을 빠르게 설정할 수 있습니다. 이 접근 방식은 모든 필요한 리소스를 자동으로 생성하고 프로비저닝합니다. 자세한 정보는 "워크플로우 템플릿"을 참조하세요.
OpenSearch는 기본 로컬 모델(huggingface/sentence-transformers/paraphrase-MiniLM-L3-v2)을 자동으로 등록하고 배포하며 수집 파이프라인과 벡터 인덱스를 생성하는 워크플로우 템플릿을 제공합니다:
POST /_plugins/_flow_framework/workflow?use_case=semantic_search_with_local_model&provision=true
매개변수를 업데이트해야 하는지 확인하기 위해 의미론적 검색 워크플로우 템플릿 기본값을 검토하세요. 예를 들어, 다른 모델을 사용하려면 요청 본문에서 모델 이름을 지정하세요:
POST /_plugins/_flow_framework/workflow?use_case=semantic_search_with_local_model&provision=true
{
"register_local_pretrained_model.name": "huggingface/sentence-transformers/msmarco-distilbert-base-tas-b"
}
OpenSearch는 생성된 워크플로우에 대한 워크플로우 ID로 응답합니다:
{
"workflow_id" : "U_nMXJUBq_4FYQzMOS4B"
}
워크플로우 상태를 확인하려면 다음 요청을 보내세요:
GET /_plugins/_flow_framework/workflow/U_nMXJUBq_4FYQzMOS4B/_status
워크플로우가 완료되면 상태가 COMPLETED로 변경됩니다. 워크플로우는 다음 단계를 실행합니다:
이제 3단계(c)에서 인덱스에 문서를 수집하고 4단계에서 데이터를 검색할 수 있습니다.
완료 후 이 튜토리얼에서 생성한 구성 요소를 클러스터에서 삭제하세요:
DELETE /my-nlp-index
DELETE /_search/pipeline/nlp-search-pipeline
DELETE /_ingest/pipeline/nlp-ingest-pipeline
POST /_plugins/_ml/models/aVeif4oB5Vm0Tdw8zYO2/_undeploy
DELETE /_plugins/_ml/models/aVeif4oB5Vm0Tdw8zYO2
DELETE /_plugins/_ml/model_groups/Z1eQf4oB5Vm0Tdw8EIP2
OpenSearch의 AI 검색을 탐색해보세요.