ML inference search request processor
출처: 문서
본문
ML inference 검색 요청 프로세서
2.16에 도입됨 {: .label .label-purple }
ml_inference 검색 요청 프로세서는 등록된 머신러닝(ML) 모델을 호출해서, 모델 출력으로 쿼리를 재작성하기 위해 사용해요.
사전 조건
ml_inference 검색 요청 프로세서를 사용하기 전에, OpenSearch 클러스터에서 호스팅되는 로컬 ML 모델이 있거나 ML Commons 플러그인을 통해 OpenSearch 클러스터에 연결된 외부 호스팅 모델이 있어야 해요. 로컬 모델에 대한 자세한 내용은 OpenSearch 내에서 ML 모델 사용하기를 참고하세요.
외부 호스팅 모델에 대한 자세한 내용은 외부 호스팅 모델에 연결하기를 참고하세요.
{: .note}
구문
다음은 ml-inference 검색 요청 프로세서의 구문이에요:
{
"ml_inference": {
"model_id": "<model_id>",
"function_name": "<function_name>",
"full_response_path": "<full_response_path>",
"query_template": "<query_template>",
"model_config": {
"<model_config_field>": "<config_value>"
},
"model_input": "<model_input>",
"input_map": [
{
"<model_input_field>": "<query_input_field>"
}
],
"output_map": [
{
"<query_output_field>": "<model_output_field>"
}
]
}
}
{% include copy-curl.html %}
구성 파라미터
다음 표는 ml-inference 검색 요청 프로세서의 필수 및 선택 파라미터를 정리한 거예요.
| 파라미터 | 데이터 타입 | 필수/선택 | 설명 |
|---|---|---|---|
model_id |
String | 필수 | 프로세서가 사용하는 ML 모델의 ID예요. |
query_template |
String | 선택 | new_document_field를 포함하는 새 쿼리를 만들 때 사용하는 쿼리 문자열 템플릿이에요. 검색 쿼리를 새 쿼리 타입으로 재작성할 때 자주 사용해요. |
function_name |
String | 외부 호스팅 모델은 선택 로컬 모델은 필수 |
프로세서에 설정된 ML 모델의 함수 이름이에요. 로컬 모델의 경우 유효한 값은 sparse_encoding, sparse_tokenize, text_embedding, text_similarity예요. 외부 호스팅 모델의 경우 유효한 값은 remote예요. 기본값은 remote예요. |
model_config |
Object | 선택 | ML 모델의 커스텀 설정 옵션이에요. 외부 호스팅 모델의 경우 이 값을 설정하면 기본 커넥터 파라미터를 재정의해요. 로컬 모델의 경우 model_config를 model_input에 추가해서 등록 시 설정한 모델 구성을 재정의할 수 있어요. 자세한 내용은 model_config 오브젝트를 참고하세요. |
model_input |
String | 외부 호스팅 모델은 선택 로컬 모델은 필수 |
모델이 기대하는 입력 필드 형식을 정의하는 템플릿이에요. 로컬 모델 타입마다 사용하는 입력 세트가 다를 수 있어요. 외부 호스팅 모델의 경우 기본값은 "{ \"parameters\": ${ml_inference.parameters} }이에요. |
input_map |
Array | 필수 | 쿼리 문자열 필드를 모델 입력 필드에 어떻게 매핑할지 지정하는 배열이에요. 배열의 각 요소는 "<model_input_field>": "<query_input_field>" 형식의 맵이고, 문서 필드에 대한 모델 호출 한 번에 대응해요. 외부 호스팅 모델에 입력 매핑을 지정하지 않으면 모든 문서 필드가 입력으로 모델에 직접 전달돼요. input_map 크기는 모델이 호출되는 횟수(Predict API 요청 수)를 나타내요. |
<model_input_field> |
String | 필수 | 모델 입력 필드 이름이에요. |
<query_input_field> |
String | 필수 | 모델 입력으로 사용되는 쿼리 필드의 이름 또는 JSON 경로예요. |
output_map |
Array | 필수 | 모델 출력 필드를 쿼리 문자열의 새 필드에 어떻게 매핑할지 지정하는 배열이에요. 배열의 각 요소는 "<query_output_field>": "<model_output_field>" 형식의 맵이에요. |
<query_output_field> |
String | 필수 | 모델 출력(model_output으로 지정)이 저장되는 쿼리 필드의 이름이에요. |
<model_output_field> |
String | 필수 | 모델 출력에서 query_output_field에 저장될 필드의 이름 또는 JSON 경로예요. |
full_response_path |
Boolean | 선택 | model_output_field에 필드 이름 대신 필드까지의 전체 JSON 경로가 들어 있다면 이 파라미터를 true로 설정하세요. 그러면 필드 값을 얻기 위해 모델 출력 전체를 파싱해요. 로컬 모델의 기본값은 true이고 외부 호스팅 모델의 기본값은 false예요. |
ignore_missing |
Boolean | 선택 | true이고 input_map이나 output_map에 정의된 입력 필드 중 하나라도 없으면 이 프로세서는 무시돼요. 그렇지 않으면 필드가 없을 때 실패가 발생해요. 기본값은 false예요. |
ignore_failure |
Boolean | 선택 | 오류가 발생해도 프로세서가 실행을 계속할지 여부를 지정해요. true이면 이 프로세서는 무시되고 검색은 계속돼요. false이면 실패가 발생할 때 검색이 취소돼요. 기본값은 false예요. |
max_prediction_tasks |
Integer | 선택 | 쿼리 검색 중에 실행될 수 있는 동시 모델 호출의 최대 개수예요. 기본값은 10이에요. |
description |
String | 선택 | 프로세서에 대한 간단한 설명이에요. |
tag |
String | 선택 | 프로세서의 식별자 태그예요. 같은 타입의 프로세서들을 구분할 수 있어서 디버깅에 유용해요. |
input_map과 output_map 매핑은 복잡한 데이터 구조를 지정하기 위한 표준 JSON path 표기법을 지원해요.
{: .note}
프로세서 사용하기
다음 단계를 따르면 파이프라인에서 프로세서를 사용할 수 있어요. 프로세서를 생성할 때는 모델 ID, input_map, output_map을 반드시 제공해야 해요. 프로세서를 사용하는 파이프라인을 테스트하기 전에 모델이 성공적으로 배포됐는지 확인하세요. 모델 상태는 Get Model API로 확인할 수 있어요.
로컬 모델의 경우 모델 입력 형식을 지정하는 model_input 필드를 반드시 제공해야 해요. model_config에 있는 입력 필드는 모두 model_input에 추가하세요.
외부 호스팅 모델의 경우 model_input 필드는 선택 사항이고, 기본값은 "{ \"parameters\": ${ml_inference.parameters} }이에요.
셋업
my_index라는 이름의 인덱스를 만들고 문서 두 개를 인덱싱하세요:
POST /my_index/_doc/1
{
"passage_text": "I am excited",
"passage_language": "en",
"label": "POSITIVE",
"passage_embedding": [
2.3886719,
0.032714844,
-0.22229004
...]
}
{% include copy-curl.html %}
POST /my_index/_doc/2
{
"passage_text": "I am sad",
"passage_language": "en",
"label": "NEGATIVE",
"passage_embedding": [
1.7773438,
0.4309082,
1.8857422,
0.95996094,
...]
}
{% include copy-curl.html %}
생성한 인덱스에서 검색 파이프라인 없이 term 쿼리를 실행하면, 쿼리는 쿼리에 지정된 term이 정확히 포함된 문서를 검색해요. 다음 쿼리는 쿼리 텍스트가 인덱스의 어떤 문서와도 일치하지 않기 때문에 결과를 반환하지 않아요:
GET /my_index/_search
{
"query": {
"term": {
"passage_text": {
"value": "happy moments",
"boost": 1
}
}
}
}
모델을 사용하면 검색 파이프라인이 모델 추론을 바탕으로 검색 결과를 향상시키거나 바꾸기 위해 term 값을 동적으로 재작성할 수 있어요. 즉, 모델이 검색 쿼리에서 초기 입력을 받아 처리한 뒤, 모델 추론을 반영하도록 쿼리 term을 업데이트하기 때문에 검색 결과의 관련성이 개선될 수 있어요.
예제: 외부 호스팅 모델
다음 예제에서는 외부 호스팅 모델을 사용하는 ml_inference 프로세서를 설정해요.
1단계: 파이프라인 생성하기
이 예제는 term 쿼리 값을 재작성하는 외부 호스팅 감성 분석 모델용 검색 파이프라인을 만드는 방법을 보여줘요. 이 모델은 inputs 필드를 필요로 하고, label 필드에 결과를 출력해요. function_name을 지정하지 않으면 기본값인 remote가 사용되는데, 이는 외부 호스팅 모델을 뜻해요.
term 쿼리 값은 모델 출력을 기반으로 재작성돼요. 검색 요청의 ml_inference 프로세서는 모델 입력으로 쓸 쿼리 필드 값을 가져오는 input_map과, 모델 출력을 쿼리 문자열에 할당하는 output_map이 필요해요.
이 예제에서는 다음 term 쿼리에 ml_inference 검색 요청 프로세서를 사용해요:
{
"query": {
"term": {
"label": {
"value": "happy moments",
"boost": 1
}
}
}
}
다음 요청은 앞서 본 term 쿼리를 재작성하는 검색 파이프라인을 생성해요:
PUT /_search/pipeline/ml_inference_pipeline
{
"description": "Generate passage_embedding for searched documents",
"request_processors": [
{
"ml_inference": {
"model_id": "<your model id>",
"input_map": [
{
"inputs": "query.term.label.value"
}
],
"output_map": [
{
"query.term.label.value": "label"
}
]
}
}
]
}
{% include copy-curl.html %}
외부 호스팅 모델에 Predict API 요청을 보낼 때는 필요한 필드와 파라미터가 대개 parameters 오브젝트 안에 담겨 있어요:
POST /_plugins/_ml/models/cleMb4kBJ1eYAeTMFFg4/_predict
{
"parameters": {
"inputs": [
{
...
}
]
}
}
따라서 외부 호스팅 감성 분석 모델을 사용하려면 다음 형식으로 Predict API 요청을 보내면 돼요:
POST /_plugins/_ml/models/cywgD5EB6KAJXDLxyDp1/_predict
{
"parameters": {
"inputs": "happy moments"
}
}
{% include copy-curl.html %}
모델은 입력을 처리하고 입력 텍스트의 감성을 기반으로 예측을 생성해요. 이 경우 감성은 긍정적이에요:
{
"inference_results": [
{
"output": [
{
"name": "response",
"dataAsMap": {
"label": "POSITIVE",
"score": "0.948"
}
}
],
"status_code": 200
}
]
}
외부 호스팅 모델의 input_map을 지정할 때는 dot path인 parameters.inputs를 제공하는 대신 inputs 필드를 바로 참조할 수 있어요:
"input_map": [
{
"inputs": "query.term.label.value"
}
]
2단계: 파이프라인 실행하기
검색 파이프라인을 만들고 나면, 같은 term 쿼리를 검색 파이프라인과 함께 실행할 수 있어요:
GET /my_index/_search?search_pipeline=my_pipeline_request_review
{
"query": {
"term": {
"label": {
"value": "happy moments",
"boost": 1
}
}
}
}
{% include copy-curl.html %}
쿼리 term 값은 모델 출력을 기반으로 재작성돼요. 모델이 쿼리 term의 감성을 긍정적이라고 판단했기 때문에, 재작성된 쿼리는 다음과 같아요:
{
"query": {
"term": {
"label": {
"value": "POSITIVE",
"boost": 1
}
}
}
}
응답에는 label 필드 값이 POSITIVE인 문서가 포함돼요:
{
"took": 288,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.00009405752,
"hits": [
{
"_index": "my_index",
"_id": "3",
"_score": 0.00009405752,
"_source": {
"passage_text": "I am excited",
"passage_language": "en",
"label": "POSITIVE"
}
}
]
}
}
예제: 로컬 모델
다음 예제는 로컬 모델을 사용하는 ml_inference 프로세서를 설정해서 term 쿼리를 k-NN 쿼리로 재작성하는 방법을 보여줘요.
1단계: 파이프라인 생성하기
다음 예제는 huggingface/sentence-transformers/all-distilroberta-v1 로컬 모델용 검색 파이프라인을 만드는 방법을 보여줘요. 이 모델은 사전 학습된 sentence transformer 모델이며,
OpenSearch 클러스터에서 호스팅돼요.
Predict API로 이 모델을 호출하면 요청은 다음과 같아요:
POST /_plugins/_ml/_predict/text_embedding/cleMb4kBJ1eYAeTMFFg4
{
"text_docs": [
"today is sunny"
],
"return_number": true,
"target_response": [
"sentence_embedding"
]
}
이 스키마를 바탕으로 model_input을 다음과 같이 지정해요:
"model_input": "{ \"text_docs\": ${input_map.text_docs}, \"return_number\": ${model_config.return_number}, \"target_response\": ${model_config.target_response} }"
input_map에서는 query.term.passage_embedding.value 쿼리 필드를 모델이 기대하는 text_docs 필드에 매핑해요:
"input_map": [
{
"text_docs": "query.term.passage_embedding.value"
}
]
임베딩으로 변환할 필드를 JSON 경로로 지정했기 때문에 full_response_path를 true로 설정해야 해요. 그러면 입력 필드를 얻기 위해 전체 JSON 문서가 파싱돼요:
"full_response_path": true
query.term.passage_embedding.value 필드의 텍스트가 임베딩 생성에 사용될 거예요:
{
"text_docs": "happy passage"
}
Predict API 요청은 다음과 같은 응답을 반환해요:
{
"inference_results": [
{
"output": [
{
"name": "sentence_embedding",
"data_type": "FLOAT32",
"shape": [
768
],
"data": [
0.25517133,
-0.28009856,
0.48519906,
...
]
}
]
}
]
}
모델은 $.inference_results.*.output.*.data 필드에 임베딩을 생성해요. output_map은 이 필드를 쿼리 템플릿의 쿼리 필드에 매핑해요:
"output_map": [
{
"modelPredictionOutcome": "$.inference_results.*.output.*.data"
}
]
로컬 모델로 ml_inference 검색 요청 프로세서를 설정할 때는 function_name을 명시적으로 지정해야 해요. 이 예제에서 function_name은 text_embedding이에요. 유효한 function_name 값에 대한 내용은 구성 파라미터를 참고하세요.
다음은 로컬 모델을 사용하는 ml_inference 프로세서의 최종 구성이에요:
PUT /_search/pipeline/ml_inference_pipeline_local
{
"description": "searchs reviews and generates embeddings",
"request_processors": [
{
"ml_inference": {
"function_name": "text_embedding",
"full_response_path": true,
"model_id": "<your model id>",
"model_config": {
"return_number": true,
"target_response": [
"sentence_embedding"
]
},
"model_input": "{ \"text_docs\": ${input_map.text_docs}, \"return_number\": ${model_config.return_number}, \"target_response\": ${model_config.target_response} }",
"query_template": """{
"size": 2,
"query": {
"knn": {
"passage_embedding": {
"vector": ${modelPredictionOutcome},
"k": 5
}
}
}
}""",
"input_map": [
{
"text_docs": "query.term.passage_embedding.value"
}
],
"output_map": [
{
"modelPredictionOutcome": "$.inference_results.*.output.*.data"
}
],
"ignore_missing": true,
"ignore_failure": true
}
}
]
}
{% include copy-curl.html %}
2단계: 파이프라인 실행하기
요청에 파이프라인 이름을 넣어서 다음 쿼리를 실행하세요:
GET /my_index/_search?search_pipeline=ml_inference_pipeline_local
{
"query": {
"term": {
"passage_embedding": {
"value": "happy passage"
}
}
}
}
{% include copy-curl.html %}
응답을 보면 프로세서가 k-NN 쿼리를 실행했고, 문서 1이 더 높은 스코어로 반환된 것을 확인할 수 있어요:
{
"took": 288,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0.00009405752,
"hits": [
{
"_index": "my_index",
"_id": "1",
"_score": 0.00009405752,
"_source": {
"passage_text": "I am excited",
"passage_language": "en",
"label": "POSITIVE",
"passage_embedding": [
2.3886719,
0.032714844,
-0.22229004
...]
}
},
{
"_index": "my_index",
"_id": "2",
"_score": 0.00001405052,
"_source": {
"passage_text": "I am sad",
"passage_language": "en",
"label": "NEGATIVE",
"passage_embedding": [
1.7773438,
0.4309082,
1.8857422,
0.95996094,
...
]
}
}
]
}
}