keynet-inference


Namekeynet-inference JSON
Version 0.4.1 PyPI version JSON
download
home_pageNone
SummaryInference utilities for keynet - Triton Inference Server integration
upload_time2025-11-05 11:24:45
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT
keywords deployment inference keynet machine-learning triton
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # keynet-inference

MLflow와 Triton Inference Server를 연동하여, Python 함수를 손쉽게 추론 API로 배포하고 실행하기 위한 라이브러리입니다.

## 설치

```bash
pip install keynet-inference
```

## 주요 개념

`keynet-inference`는 추론 파이프라인을 구성하는 몇 가지 핵심 컴포넌트를 제공합니다.

- **`@keynet_function(name)`**: 일반 Python 함수를 Keynet 추론 워크플로우의 **진입점(entrypoint)**으로 만들어주는 데코레이터입니다. 이 데코레이터는 다음과 같은 역할을 자동으로 처리합니다.

  - 함수 실행에 필요한 환경변수 로드
  - 사용자 입력을 `UserInput` 싱글톤 객체에 설정
  - 함수의 시그니처 및 반환 값 검증

- **`UserInput`**: 추론 실행 시 사용자가 전달한 입력 파라미터에 안전하게 접근할 수 있는 **싱글톤(Singleton) 객체**입니다. `UserInput.get("param_name", default_value)` 형태로 사용하며, 환경변수와 분리되어 사용자 입력만을 명확하게 관리할 수 있습니다.

- **`TritonPlugin`**: MLflow 모델 레지스트리에 등록된 모델을 사용하여 Triton Inference Server에 추론 요청을 보내는 클라이언트입니다. 복잡한 Triton API 호출을 단순화하여, 모델 이름과 입력 데이터만으로 쉽게 예측을 수행할 수 있습니다.

- **`Storage`**: S3, MinIO 등 오브젝트 스토리지와의 파일 상호작용을 추상화한 인터페이스입니다. 추론에 필요한 이미지나 데이터를 다운로드하거나, 결과 파일을 업로드하는 작업을 간편하게 처리합니다.

## 사용 예제

### 전체 추론 파이프라인

다음은 `Storage`에서 이미지를 다운로드하고, 전처리한 뒤 Triton으로 추론하고, 결과를 다시 `Storage`에 업로드하는 전체 워크플로우 예제입니다.

> **참고**: 아래 예제에서 `main` 함수는 `args` 파라미터를 받지만, 실제 사용자 입력값은 `UserInput.get()` 메소드를 통해 접근하는 것을 권장합니다. `@keynet_function` 데코레이터가 `args`를 사용하여 `UserInput` 싱글톤을 내부적으로 초기화하므로, 코드의 명확성과 일관성을 위해 `UserInput`을 사용하세요.

```python
import numpy as np
from PIL import Image
from io import BytesIO
from keynet_inference import keynet_function, UserInput, TritonPlugin, Storage

# 1. @keynet_function으로 추론 함수 정의
# "image-detection"은 이 함수의 고유 이름으로 사용됩니다.
@keynet_function("image-detection")
def main(args: dict):
    # Storage 및 TritonPlugin 클라이언트 초기화
    # 관련 설정(예: 엔드포인트, 자격 증명)은 환경변수에서 자동으로 로드됩니다.
    storage = Storage()
    triton = TritonPlugin()

    # 2. 사용자 입력 파라미터 가져오기
    # UserInput.get()을 사용하여 안전하게 입력에 접근합니다.
    image_url = UserInput.get("image_url")
    threshold = UserInput.get("threshold", 0.5)  # 기본값 0.5 설정

    # 3. 입력 데이터 처리 (Storage에서 다운로드)
    image_buffer = storage.get(image_url)
    image = Image.open(image_buffer).convert("RGB")

    # 4. 모델 추론을 위한 전처리
    input_tensor = preprocess(image, size=(640, 640))

    # 5. Triton Inference Server로 추론 요청
    outputs = triton.predict(
        df={"input_0": input_tensor}
    )

    # 6. 추론 결과 후처리
    result_image_buffer = postprocess(outputs["output_0"], image, threshold)

    # 7. 결과물을 Storage에 업로드
    url = storage.put(result_image_buffer)

    # 8. 결과 반환 (JSON 직렬화 가능해야 함)
    return {"url": url}

def preprocess(image: Image.Image, size: tuple) -> np.ndarray:
    """이미지를 모델 입력 형식에 맞게 전처리합니다."""
    resized = image.resize(size)
    array = np.array(resized, dtype=np.float32) / 255.0
    array = array.transpose(2, 0, 1)  # HWC -> CHW
    return np.expand_dims(array, axis=0)

def postprocess(outputs: np.ndarray, original_image: Image.Image, threshold: float) -> BytesIO:
    """추론 결과를 원본 이미지에 시각화하고 이미지 버퍼를 반환합니다."""
    # 예시: threshold를 사용한 바운딩 박스 필터링 및 그리기 로직
    # (실제 구현은 모델 출력에 따라 달라집니다)
    # ...

    buffer = BytesIO()
    original_image.save(buffer, format="PNG")
    buffer.seek(0)
    return buffer
```

## Python 버전 지원

Python 3.9, 3.10, 3.11, 3.12

## 라이선스

MIT License

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "keynet-inference",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "deployment, inference, keynet, machine-learning, triton",
    "author": null,
    "author_email": "hbjs <hbjs97@naver.com>",
    "download_url": "https://files.pythonhosted.org/packages/f3/4a/fb7a1d1ad92ad98ecc494e2bd59a082905b9e3b6702ff13ab6a800a4f085/keynet_inference-0.4.1.tar.gz",
    "platform": null,
    "description": "# keynet-inference\n\nMLflow\uc640 Triton Inference Server\ub97c \uc5f0\ub3d9\ud558\uc5ec, Python \ud568\uc218\ub97c \uc190\uc27d\uac8c \ucd94\ub860 API\ub85c \ubc30\ud3ec\ud558\uace0 \uc2e4\ud589\ud558\uae30 \uc704\ud55c \ub77c\uc774\ube0c\ub7ec\ub9ac\uc785\ub2c8\ub2e4.\n\n## \uc124\uce58\n\n```bash\npip install keynet-inference\n```\n\n## \uc8fc\uc694 \uac1c\ub150\n\n`keynet-inference`\ub294 \ucd94\ub860 \ud30c\uc774\ud504\ub77c\uc778\uc744 \uad6c\uc131\ud558\ub294 \uba87 \uac00\uc9c0 \ud575\uc2ec \ucef4\ud3ec\ub10c\ud2b8\ub97c \uc81c\uacf5\ud569\ub2c8\ub2e4.\n\n- **`@keynet_function(name)`**: \uc77c\ubc18 Python \ud568\uc218\ub97c Keynet \ucd94\ub860 \uc6cc\ud06c\ud50c\ub85c\uc6b0\uc758 **\uc9c4\uc785\uc810(entrypoint)**\uc73c\ub85c \ub9cc\ub4e4\uc5b4\uc8fc\ub294 \ub370\ucf54\ub808\uc774\ud130\uc785\ub2c8\ub2e4. \uc774 \ub370\ucf54\ub808\uc774\ud130\ub294 \ub2e4\uc74c\uacfc \uac19\uc740 \uc5ed\ud560\uc744 \uc790\ub3d9\uc73c\ub85c \ucc98\ub9ac\ud569\ub2c8\ub2e4.\n\n  - \ud568\uc218 \uc2e4\ud589\uc5d0 \ud544\uc694\ud55c \ud658\uacbd\ubcc0\uc218 \ub85c\ub4dc\n  - \uc0ac\uc6a9\uc790 \uc785\ub825\uc744 `UserInput` \uc2f1\uae00\ud1a4 \uac1d\uccb4\uc5d0 \uc124\uc815\n  - \ud568\uc218\uc758 \uc2dc\uadf8\ub2c8\ucc98 \ubc0f \ubc18\ud658 \uac12 \uac80\uc99d\n\n- **`UserInput`**: \ucd94\ub860 \uc2e4\ud589 \uc2dc \uc0ac\uc6a9\uc790\uac00 \uc804\ub2ec\ud55c \uc785\ub825 \ud30c\ub77c\ubbf8\ud130\uc5d0 \uc548\uc804\ud558\uac8c \uc811\uadfc\ud560 \uc218 \uc788\ub294 **\uc2f1\uae00\ud1a4(Singleton) \uac1d\uccb4**\uc785\ub2c8\ub2e4. `UserInput.get(\"param_name\", default_value)` \ud615\ud0dc\ub85c \uc0ac\uc6a9\ud558\uba70, \ud658\uacbd\ubcc0\uc218\uc640 \ubd84\ub9ac\ub418\uc5b4 \uc0ac\uc6a9\uc790 \uc785\ub825\ub9cc\uc744 \uba85\ud655\ud558\uac8c \uad00\ub9ac\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.\n\n- **`TritonPlugin`**: MLflow \ubaa8\ub378 \ub808\uc9c0\uc2a4\ud2b8\ub9ac\uc5d0 \ub4f1\ub85d\ub41c \ubaa8\ub378\uc744 \uc0ac\uc6a9\ud558\uc5ec Triton Inference Server\uc5d0 \ucd94\ub860 \uc694\uccad\uc744 \ubcf4\ub0b4\ub294 \ud074\ub77c\uc774\uc5b8\ud2b8\uc785\ub2c8\ub2e4. \ubcf5\uc7a1\ud55c Triton API \ud638\ucd9c\uc744 \ub2e8\uc21c\ud654\ud558\uc5ec, \ubaa8\ub378 \uc774\ub984\uacfc \uc785\ub825 \ub370\uc774\ud130\ub9cc\uc73c\ub85c \uc27d\uac8c \uc608\uce21\uc744 \uc218\ud589\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.\n\n- **`Storage`**: S3, MinIO \ub4f1 \uc624\ube0c\uc81d\ud2b8 \uc2a4\ud1a0\ub9ac\uc9c0\uc640\uc758 \ud30c\uc77c \uc0c1\ud638\uc791\uc6a9\uc744 \ucd94\uc0c1\ud654\ud55c \uc778\ud130\ud398\uc774\uc2a4\uc785\ub2c8\ub2e4. \ucd94\ub860\uc5d0 \ud544\uc694\ud55c \uc774\ubbf8\uc9c0\ub098 \ub370\uc774\ud130\ub97c \ub2e4\uc6b4\ub85c\ub4dc\ud558\uac70\ub098, \uacb0\uacfc \ud30c\uc77c\uc744 \uc5c5\ub85c\ub4dc\ud558\ub294 \uc791\uc5c5\uc744 \uac04\ud3b8\ud558\uac8c \ucc98\ub9ac\ud569\ub2c8\ub2e4.\n\n## \uc0ac\uc6a9 \uc608\uc81c\n\n### \uc804\uccb4 \ucd94\ub860 \ud30c\uc774\ud504\ub77c\uc778\n\n\ub2e4\uc74c\uc740 `Storage`\uc5d0\uc11c \uc774\ubbf8\uc9c0\ub97c \ub2e4\uc6b4\ub85c\ub4dc\ud558\uace0, \uc804\ucc98\ub9ac\ud55c \ub4a4 Triton\uc73c\ub85c \ucd94\ub860\ud558\uace0, \uacb0\uacfc\ub97c \ub2e4\uc2dc `Storage`\uc5d0 \uc5c5\ub85c\ub4dc\ud558\ub294 \uc804\uccb4 \uc6cc\ud06c\ud50c\ub85c\uc6b0 \uc608\uc81c\uc785\ub2c8\ub2e4.\n\n> **\ucc38\uace0**: \uc544\ub798 \uc608\uc81c\uc5d0\uc11c `main` \ud568\uc218\ub294 `args` \ud30c\ub77c\ubbf8\ud130\ub97c \ubc1b\uc9c0\ub9cc, \uc2e4\uc81c \uc0ac\uc6a9\uc790 \uc785\ub825\uac12\uc740 `UserInput.get()` \uba54\uc18c\ub4dc\ub97c \ud1b5\ud574 \uc811\uadfc\ud558\ub294 \uac83\uc744 \uad8c\uc7a5\ud569\ub2c8\ub2e4. `@keynet_function` \ub370\ucf54\ub808\uc774\ud130\uac00 `args`\ub97c \uc0ac\uc6a9\ud558\uc5ec `UserInput` \uc2f1\uae00\ud1a4\uc744 \ub0b4\ubd80\uc801\uc73c\ub85c \ucd08\uae30\ud654\ud558\ubbc0\ub85c, \ucf54\ub4dc\uc758 \uba85\ud655\uc131\uacfc \uc77c\uad00\uc131\uc744 \uc704\ud574 `UserInput`\uc744 \uc0ac\uc6a9\ud558\uc138\uc694.\n\n```python\nimport numpy as np\nfrom PIL import Image\nfrom io import BytesIO\nfrom keynet_inference import keynet_function, UserInput, TritonPlugin, Storage\n\n# 1. @keynet_function\uc73c\ub85c \ucd94\ub860 \ud568\uc218 \uc815\uc758\n# \"image-detection\"\uc740 \uc774 \ud568\uc218\uc758 \uace0\uc720 \uc774\ub984\uc73c\ub85c \uc0ac\uc6a9\ub429\ub2c8\ub2e4.\n@keynet_function(\"image-detection\")\ndef main(args: dict):\n    # Storage \ubc0f TritonPlugin \ud074\ub77c\uc774\uc5b8\ud2b8 \ucd08\uae30\ud654\n    # \uad00\ub828 \uc124\uc815(\uc608: \uc5d4\ub4dc\ud3ec\uc778\ud2b8, \uc790\uaca9 \uc99d\uba85)\uc740 \ud658\uacbd\ubcc0\uc218\uc5d0\uc11c \uc790\ub3d9\uc73c\ub85c \ub85c\ub4dc\ub429\ub2c8\ub2e4.\n    storage = Storage()\n    triton = TritonPlugin()\n\n    # 2. \uc0ac\uc6a9\uc790 \uc785\ub825 \ud30c\ub77c\ubbf8\ud130 \uac00\uc838\uc624\uae30\n    # UserInput.get()\uc744 \uc0ac\uc6a9\ud558\uc5ec \uc548\uc804\ud558\uac8c \uc785\ub825\uc5d0 \uc811\uadfc\ud569\ub2c8\ub2e4.\n    image_url = UserInput.get(\"image_url\")\n    threshold = UserInput.get(\"threshold\", 0.5)  # \uae30\ubcf8\uac12 0.5 \uc124\uc815\n\n    # 3. \uc785\ub825 \ub370\uc774\ud130 \ucc98\ub9ac (Storage\uc5d0\uc11c \ub2e4\uc6b4\ub85c\ub4dc)\n    image_buffer = storage.get(image_url)\n    image = Image.open(image_buffer).convert(\"RGB\")\n\n    # 4. \ubaa8\ub378 \ucd94\ub860\uc744 \uc704\ud55c \uc804\ucc98\ub9ac\n    input_tensor = preprocess(image, size=(640, 640))\n\n    # 5. Triton Inference Server\ub85c \ucd94\ub860 \uc694\uccad\n    outputs = triton.predict(\n        df={\"input_0\": input_tensor}\n    )\n\n    # 6. \ucd94\ub860 \uacb0\uacfc \ud6c4\ucc98\ub9ac\n    result_image_buffer = postprocess(outputs[\"output_0\"], image, threshold)\n\n    # 7. \uacb0\uacfc\ubb3c\uc744 Storage\uc5d0 \uc5c5\ub85c\ub4dc\n    url = storage.put(result_image_buffer)\n\n    # 8. \uacb0\uacfc \ubc18\ud658 (JSON \uc9c1\ub82c\ud654 \uac00\ub2a5\ud574\uc57c \ud568)\n    return {\"url\": url}\n\ndef preprocess(image: Image.Image, size: tuple) -> np.ndarray:\n    \"\"\"\uc774\ubbf8\uc9c0\ub97c \ubaa8\ub378 \uc785\ub825 \ud615\uc2dd\uc5d0 \ub9de\uac8c \uc804\ucc98\ub9ac\ud569\ub2c8\ub2e4.\"\"\"\n    resized = image.resize(size)\n    array = np.array(resized, dtype=np.float32) / 255.0\n    array = array.transpose(2, 0, 1)  # HWC -> CHW\n    return np.expand_dims(array, axis=0)\n\ndef postprocess(outputs: np.ndarray, original_image: Image.Image, threshold: float) -> BytesIO:\n    \"\"\"\ucd94\ub860 \uacb0\uacfc\ub97c \uc6d0\ubcf8 \uc774\ubbf8\uc9c0\uc5d0 \uc2dc\uac01\ud654\ud558\uace0 \uc774\ubbf8\uc9c0 \ubc84\ud37c\ub97c \ubc18\ud658\ud569\ub2c8\ub2e4.\"\"\"\n    # \uc608\uc2dc: threshold\ub97c \uc0ac\uc6a9\ud55c \ubc14\uc6b4\ub529 \ubc15\uc2a4 \ud544\ud130\ub9c1 \ubc0f \uadf8\ub9ac\uae30 \ub85c\uc9c1\n    # (\uc2e4\uc81c \uad6c\ud604\uc740 \ubaa8\ub378 \ucd9c\ub825\uc5d0 \ub530\ub77c \ub2ec\ub77c\uc9d1\ub2c8\ub2e4)\n    # ...\n\n    buffer = BytesIO()\n    original_image.save(buffer, format=\"PNG\")\n    buffer.seek(0)\n    return buffer\n```\n\n## Python \ubc84\uc804 \uc9c0\uc6d0\n\nPython 3.9, 3.10, 3.11, 3.12\n\n## \ub77c\uc774\uc120\uc2a4\n\nMIT License\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Inference utilities for keynet - Triton Inference Server integration",
    "version": "0.4.1",
    "project_urls": {
        "Bug Tracker": "https://github.com/WIM-Corporation/keynet/issues",
        "Homepage": "https://github.com/WIM-Corporation/keynet"
    },
    "split_keywords": [
        "deployment",
        " inference",
        " keynet",
        " machine-learning",
        " triton"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1c132ad3c68acb12cf2ae3d943a890932b244c1576f33ff48aed30c17827d8e1",
                "md5": "fd5c0525111df07e8b83ff1bb731ce91",
                "sha256": "31381f9433587b603906229ada4d2341d9eead74c671b993b049c356639dd29c"
            },
            "downloads": -1,
            "filename": "keynet_inference-0.4.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "fd5c0525111df07e8b83ff1bb731ce91",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 35752,
            "upload_time": "2025-11-05T11:24:44",
            "upload_time_iso_8601": "2025-11-05T11:24:44.306841Z",
            "url": "https://files.pythonhosted.org/packages/1c/13/2ad3c68acb12cf2ae3d943a890932b244c1576f33ff48aed30c17827d8e1/keynet_inference-0.4.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f34afb7a1d1ad92ad98ecc494e2bd59a082905b9e3b6702ff13ab6a800a4f085",
                "md5": "67119280f86c305058decd857b3f2ef9",
                "sha256": "948de8fc4299fe76cfdaddf5bff9c48c63b62027a903a6b3b3420fc79546099c"
            },
            "downloads": -1,
            "filename": "keynet_inference-0.4.1.tar.gz",
            "has_sig": false,
            "md5_digest": "67119280f86c305058decd857b3f2ef9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 58805,
            "upload_time": "2025-11-05T11:24:45",
            "upload_time_iso_8601": "2025-11-05T11:24:45.758347Z",
            "url": "https://files.pythonhosted.org/packages/f3/4a/fb7a1d1ad92ad98ecc494e2bd59a082905b9e3b6702ff13ab6a800a4f085/keynet_inference-0.4.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-05 11:24:45",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "WIM-Corporation",
    "github_project": "keynet",
    "github_not_found": true,
    "lcname": "keynet-inference"
}
        
Elapsed time: 2.15729s