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

Custom local models

원문 보기 위키 갱신

출처: 문서

본문

사용자 지정 로컬 모델

2.9부터 도입 {: .label .label-purple }

사용자 지정 모델을 로컬에서 사용하려면 OpenSearch 클러스터에 업로드하면 돼요.

모델 지원

OpenSearch는 다음과 같은 유형의 로컬 모델을 지원해요:

  • 텍스트 임베딩 모델
  • 스파스 인코딩 모델
  • 크로스 인코더 모델
  • 질의응답 모델

CentOS 7 운영 체제에서는 로컬 모델 실행이 지원되지 않아요. 또한 모든 로컬 모델이 모든 하드웨어와 운영 체제에서 실행될 수 있는 것은 아니에요. {: .important}

모델 준비하기

모든 모델에 대해 모델 zip 파일 안에 토크나이저 JSON 파일을 반드시 제공해야 해요.

스파스 인코딩 모델의 경우 ML Commons가 스파스 벡터를 후처리할 수 있도록 출력 형식이 {"output":<sparse_vector>}인지 확인하세요.

자체 데이터셋으로 스파스 모델을 파인튜닝한다면 자체 스파스 토크나이저 모델을 사용하고 싶을 수도 있어요. 쿼리에서 토크나이저 모델을 사용할 때 쿼리 성능이 향상되기 때문에, 토크나이저 모델 zip 파일에 자체 IDF JSON 파일을 제공하는 것이 좋아요. 또는 OpenSearch에서 제공하는 범용 IDF from MSMARCO를 사용할 수도 있어요. IDF 파일을 제공하지 않으면 각 토큰의 기본 가중치가 1로 설정되는데, 이는 스파스 신경 검색 성능에 영향을 줄 수 있어요.

모델 형식

OpenSearch에서 모델을 사용하려면 모델을 이식 가능한 형식으로 내보내야 해요. OpenSearch는 TorchScript와 ONNX 형식만 지원해요.

OpenSearch에 업로드하기 전에 모델 파일을 zip으로 저장해야 해요. ML Commons가 모델을 업로드할 수 있도록 하려면 업로드하기 전에 TorchScript 파일을 압축하세요. 예시가 필요하다면 TorchScript 모델 파일을 다운로드해 보세요.

또한 모델 zip 파일의 SHA256 체크섬을 계산해야 하는데, 모델을 등록할 때 이 체크섬을 제공해야 해요. 예를 들어 UNIX에서는 다음 명령어로 체크섬을 구할 수 있어요:

shasum -a 256 sentence-transformers_paraphrase-mpnet-base-v2-1.0.0-onnx.zip

모델 크기

대부분의 딥러닝 모델은 100 MB를 넘기 때문에 단일 문서에 담기 어려워요. OpenSearch는 모델 파일을 더 작은 청크로 분할해 모델 인덱스에 저장해요. OpenSearch 클러스터에 ML 노드나 데이터 노드를 할당할 때는 ML 추론을 수행할 때 충분한 메모리를 확보할 수 있도록 ML 노드 크기를 적절히 설정해야 해요.

사전 준비 사항

사용자 지정 모델을 OpenSearch에 업로드하려면 OpenSearch 클러스터 외부에서 모델을 준비해야 해요. Hugging Face의 사전 학습 모델을 사용하거나, 필요에 맞게 새 모델을 학습시킬 수 있어요.

클러스터 설정

이 예제는 전용 ML 노드 없이 간단한 설정을 사용하며, ML이 아닌 노드에서 모델을 실행할 수 있게 해줘요.

전용 ML 노드가 있는 클러스터에서는 성능을 향상시키려면 "only_run_on_ml_node": "true"를 지정하세요. 자세한 내용은 ML Commons 클러스터 설정을 참고하세요.

이 기본 로컬 설정이 동작하도록 하려면 다음 클러스터 설정을 지정하세요:

PUT _cluster/settings
{
  "persistent": {
    "plugins.ml_commons.allow_registering_model_via_url": "true",
    "plugins.ml_commons.only_run_on_ml_node": "false",
    "plugins.ml_commons.model_access_control_enabled": "true",
    "plugins.ml_commons.native_memory_threshold": "99"
  }
}

{% include copy-curl.html %}

URL에서 모델을 등록할 때는 소스가 신뢰할 수 있는지 확인하세요. 신뢰할 수 없는 소스에서 모델을 로드하면 보안 위험이 발생할 수 있어요. 자세한 내용은 신뢰할 수 없는 모델에 대한 PyTorch 보안 가이드라인을 참고하세요. {: .warning}

1단계: 모델 그룹 등록하기

모델을 등록할 때는 다음과 같은 옵션이 있어요:

  • model_group_id를 사용해 기존 모델 그룹에 모델 버전을 등록할 수 있어요.
  • model_group_id를 사용하지 않으면 ML Commons가 새 모델 그룹과 함께 모델을 생성해요.

모델 그룹을 등록하려면 다음 요청을 보내세요:

POST /_plugins/_ml/model_groups/_register
{
  "name": "local_model_group",
  "description": "A model group for local models"
}

{% include copy-curl.html %}

응답에는 이 모델 그룹에 모델을 등록할 때 사용할 모델 그룹 ID가 담겨 있어요:

{
 "model_group_id": "wlcnb4kBJ1eYAeTMHlV6",
 "status": "CREATED"
}

모델 그룹에 대해 더 알아보려면 모델 접근 제어를 참고하세요.

2단계: 로컬 모델 등록하기

1단계에서 만든 모델 그룹에 로컬 모델을 등록하려면 Register Model API 요청을 보내세요. Register Model API 파라미터에 대한 설명은 모델 등록을 참고하세요.

function_name은 모델 유형에 해당해요. 텍스트 임베딩 모델은 이 파라미터를 TEXT_EMBEDDING으로 설정하고, 스파스 인코딩 모델은 SPARSE_ENCODING 또는 SPARSE_TOKENIZE로, 크로스 인코더 모델은 TEXT_SIMILARITY로, 질의응답 모델은 QUESTION_ANSWERING으로 설정하세요. 이 예제에서는 텍스트 임베딩 모델을 등록하므로 function_name을 TEXT_EMBEDDING으로 설정해요.

1단계에서 받은 모델 그룹 ID를 넣고 다음 요청을 보내세요:

POST /_plugins/_ml/models/_register
{
  "name": "huggingface/sentence-transformers/msmarco-distilbert-base-tas-b",
  "version": "1.0.1",
  "model_group_id": "wlcnb4kBJ1eYAeTMHlV6",
  "description": "This is a port of the DistilBert TAS-B Model to sentence-transformers model: It maps sentences & paragraphs to a 768 dimensional dense vector space and is optimized for the task of semantic search.",
  "function_name": "TEXT_EMBEDDING",
  "model_format": "TORCH_SCRIPT",
  "model_content_size_in_bytes": 266352827,
  "model_content_hash_value": "acdc81b652b83121f914c5912ae27c0fca8fabf270e6f191ace6979a19830413",
  "model_config": {
    "model_type": "distilbert",
    "embedding_dimension": 768,
    "framework_type": "sentence_transformers",
    "all_config": "{\"_name_or_path\":\"old_models/msmarco-distilbert-base-tas-b/0_Transformer\",\"activation\":\"gelu\",\"architectures\":[\"DistilBertModel\"],\"attention_dropout\":0.1,\"dim\":768,\"dropout\":0.1,\"hidden_dim\":3072,\"initializer_range\":0.02,\"max_position_embeddings\":512,\"model_type\":\"distilbert\",\"n_heads\":12,\"n_layers\":6,\"pad_token_id\":0,\"qa_dropout\":0.1,\"seq_classif_dropout\":0.2,\"sinusoidal_pos_embds\":false,\"tie_weights_\":true,\"transformers_version\":\"4.7.0\",\"vocab_size\":30522}"
  },
  "created_time": 1676073973126,
  "url": "https://artifacts.opensearch.org/models/ml-models/huggingface/sentence-transformers/msmarco-distilbert-base-tas-b/1.0.1/torch_script/sentence-transformers_msmarco-distilbert-base-tas-b-1.0.1-torch_script.zip"
}

{% include copy-curl.html %}

OpenSearch Dashboards에서는 all_config 필드 내용을 삼중 따옴표(""")로 감싸면 필드 내부의 따옴표가 자동으로 이스케이프되고 가독성도 좋아진다는 점을 참고하세요:

POST /_plugins/_ml/models/_register
{
  "name": "huggingface/sentence-transformers/msmarco-distilbert-base-tas-b",
  "version": "1.0.1",
  "model_group_id": "wlcnb4kBJ1eYAeTMHlV6",
  "description": "This is a port of the DistilBert TAS-B Model to sentence-transformers model: It maps sentences & paragraphs to a 768 dimensional dense vector space and is optimized for the task of semantic search.",
  "function_name": "TEXT_EMBEDDING",
  "model_format": "TORCH_SCRIPT",
  "model_content_size_in_bytes": 266352827,
  "model_content_hash_value": "acdc81b652b83121f914c5912ae27c0fca8fabf270e6f191ace6979a19830413",
  "model_config": {
    "model_type": "distilbert",
    "embedding_dimension": 768,
    "framework_type": "sentence_transformers",
    "all_config": """{"_name_or_path":"old_models/msmarco-distilbert-base-tas-b/0_Transformer","activation":"gelu","architectures":["DistilBertModel"],"attention_dropout":0.1,"dim":768,"dropout":0.1,"hidden_dim":3072,"initializer_range":0.02,"max_position_embeddings":512,"model_type":"distilbert","n_heads":12,"n_layers":6,"pad_token_id":0,"qa_dropout":0.1,"seq_classif_dropout":0.2,"sinusoidal_pos_embds":false,"tie_weights_":true,"transformers_version":"4.7.0","vocab_size":30522}"""
  },
  "created_time": 1676073973126,
  "url": "https://artifacts.opensearch.org/models/ml-models/huggingface/sentence-transformers/msmarco-distilbert-base-tas-b/1.0.1/torch_script/sentence-transformers_msmarco-distilbert-base-tas-b-1.0.1-torch_script.zip"
}

{% include copy.html %}

OpenSearch는 등록 작업의 태스크 ID를 반환해요:

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

작업 상태를 확인하려면 태스크 ID를 태스크 조회에 넣어 호출하세요:

GET /_plugins/_ml/tasks/cVeMb4kBJ1eYAeTMFFgj

{% include copy-curl.html %}

작업이 완료되면 상태가 COMPLETED로 바뀌어요:

{
  "model_id": "cleMb4kBJ1eYAeTMFFg4",
  "task_type": "REGISTER_MODEL",
  "function_name": "TEXT_EMBEDDING",
  "state": "COMPLETED",
  "worker_node": [
    "XPcXLV7RQoi5m8NI_jEOVQ"
  ],
  "create_time": 1689793598499,
  "last_update_time": 1689793598530,
  "is_async": false
}

반환된 model_id는 모델을 배포할 때 필요하니 잘 기억해 두세요.

3단계: 모델 배포하기

배포 작업은 모델 인덱스에서 모델 청크를 읽어 온 뒤 모델 인스턴스를 만들어 메모리에 로드해요. 모델이 클수록 더 많은 청크로 분할되고 메모리에 로드되는 데 더 오래 걸려요.

등록된 모델을 배포하려면 3단계에서 받은 모델 ID를 다음 요청에 넣어 보내세요:

POST /_plugins/_ml/models/cleMb4kBJ1eYAeTMFFg4/_deploy

{% include copy-curl.html %}

응답에는 배포 작업 상태를 확인할 때 사용할 수 있는 태스크 ID가 담겨 있어요:

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

이전 단계와 마찬가지로 Get ML Task API를 호출해 작업 상태를 확인하세요:

GET /_plugins/_ml/tasks/vVePb4kBJ1eYAeTM7ljG

{% include copy-curl.html %}

작업이 완료되면 상태가 COMPLETED로 바뀌어요:

{
  "model_id": "cleMb4kBJ1eYAeTMFFg4",
  "task_type": "DEPLOY_MODEL",
  "function_name": "TEXT_EMBEDDING",
  "state": "COMPLETED",
  "worker_node": [
    "n-72khvBTBi3bnIIR8FTTw"
  ],
  "create_time": 1689793851077,
  "last_update_time": 1689793851101,
  "is_async": true
}

클러스터나 노드가 재시작되면 모델을 다시 배포해야 해요. 자동 재배포를 설정하는 방법은 모델 배포 설정을 참고하세요. {: .tip}

4단계(선택): 모델 테스트하기

Predict API를 사용해 모델을 테스트하세요.

텍스트 임베딩 모델이라면 다음 요청을 보내세요:

POST /_plugins/_ml/_predict/text_embedding/cleMb4kBJ1eYAeTMFFg4
{
  "text_docs":[ "today is sunny"],
  "return_number": true,
  "target_response": ["sentence_embedding"]
}

{% include copy-curl.html %}

응답에는 제공한 문장에 대한 텍스트 임베딩이 담겨 있어요:

{
  "inference_results" : [
    {
      "output" : [
        {
          "name" : "sentence_embedding",
          "data_type" : "FLOAT32",
          "shape" : [
            768
          ],
          "data" : [
            0.25517133,
            -0.28009856,
            0.48519906,
            ...
          ]
        }
      ]
    }
  ]
}

스파스 인코딩 모델이라면 다음 요청을 보내세요:

POST /_plugins/_ml/_predict/sparse_encoding/cleMb4kBJ1eYAeTMFFg4
{
  "text_docs":[ "today is sunny"]
}

{% include copy-curl.html %}

응답에는 토큰과 가중치가 담겨 있어요:

{
  "inference_results": [
    {
      "output": [
        {
          "name": "output",
          "dataAsMap": {
            "response": [
              {
                "saturday": 0.48336542,
                "week": 0.1034762,
                "mood": 0.09698499,
                "sunshine": 0.5738209,
                "bright": 0.1756877,
                ...
              }
          }
        }
    }
}

5단계: 검색에 모델 사용하기

벡터 검색에 모델을 사용하는 방법은 AI 검색 방법을 참고하세요.

질의응답 모델

질의응답 모델은 주어진 컨텍스트에서 질문에 대한 답을 추출해요. ML Commons는 text 형식의 컨텍스트를 지원해요.

질의응답 모델을 등록하려면 다음 형식으로 요청을 보내세요. function_name은 QUESTION_ANSWERING으로 지정해요:

POST /_plugins/_ml/models/_register
{
    "name": "question_answering",
    "version": "1.0.0",
    "function_name": "QUESTION_ANSWERING",
    "description": "test model",
    "model_format": "TORCH_SCRIPT",
    "model_group_id": "lN4AP40BKolAMNtR4KJ5",
    "model_content_hash_value": "e837c8fc05fd58a6e2e8383b319257f9c3859dfb3edc89b26badfaf8a4405ff6",
    "model_config": { 
        "model_type": "bert",
        "framework_type": "huggingface_transformers"
    },
    "url": "https://github.com/opensearch-project/ml-commons/blob/main/ml-algorithms/src/test/resources/org/opensearch/ml/engine/algorithms/question_answering/question_answering_pt.zip?raw=true"
}

{% include copy-curl.html %}

그다음 모델을 배포하는 요청을 보내세요:

POST _plugins/_ml/models/{model_id}/_deploy

{% include copy-curl.html %}

질의응답 모델을 테스트하려면 다음 요청을 보내세요. question과 답을 생성할 관련 context가 필요해요:

POST /_plugins/_ml/_predict/question_answering/{model_id}
{
  "question": "Where do I live?"
  "context": "My name is John. I live in New York"
}

{% include copy-curl.html %}

응답은 컨텍스트를 바탕으로 답을 제공해요:

{
  "inference_results": [
    {
      "output": [
        {
          "result": "New York"
        }
    }
}

더 알아보기 (Learn more)