모델과 추론
모델 레지스트리의 모델 내려받기, 장비 내 추론, 추론 결과의 중앙 기록
장비에서 추론하면 생산 라인이 중앙의 응답을 기다리지 않고, 회선이 끊어져도 판정 계속. 중앙에는 결과(소용량)만 전송, 원본 영상(대용량)은 전송하지 않음. 결과는 다른 데이터와 같은 큐로 전송되므로 오프라인 동안의 추론 결과도 연결 복구 시 추론 결과 탭에 표시.
모델 내려받기(pull)
모델은 테넌트의 모델 레지스트리에 등록된 버전을 내려받음. 토큰에 models:read 스코프 필요. 방법은 세 가지, 동작은 동일. 내려받아 압축 해제, 기본으로 즉시 활성화, 버전 번호가 큰 것부터 models.keep_versions(기본 2)개만 보관하고 나머지 버전은 삭제. 이미 캐시에 있는 버전이면 다시 내려받지 않고 활성화만 수행.
디바이스 상세 모델 탭에서 모델 배포 를 누르고 모델과 버전을 지정. 배포가 pull_model 명령을 보내고, 장비가 다음 폴링 때 가져와 처리한 뒤 하트비트로 그 버전을 실행 중이라고 보고하면 확인됨(Edge Fleet). 명령 탭의 모델 내려받기 도 같은 명령을 보내지만 배포 이력에 남지 않음.
geo-mlops-edge models --pull helmet-detector --version-tag 2curl -X POST http://127.0.0.1:8600/api/v1/models/helmet-detector:pull \
-H 'content-type: application/json' \
-d '{"version": "2", "activate": true}'성공하면 다음 결과 반환(값은 예시).
{ "name": "helmet-detector", "version": "2", "framework": "yolo", "activated": true, "pruned": [] }
내려받은 모델은 data_dir/models/{이름}/{버전}/ 에 MLflow 모델 디렉터리 구조 그대로 압축 해제. 임시 폴더에 내려받은 뒤 이름을 변경하므로 중간에 중단되어도 일부만 풀린 모델이 남지 않음.
디바이스 상세 모델 탭의 이 버전으로 전환 도 이미 내려받은 버전으로 배포(같은 pull_model 명령). 캐시에 있는 버전이므로 다시 내려받지 않고 전환.
모델 명령
| 명령 | 인자 | 장비 동작 |
|---|---|---|
pull_model | name, version, activate(기본 true), deployment_id(선택) | 캐시에 없으면 내려받고, 활성화하고, keep_versions 초과분 삭제. 활성화에 실패하면 명령을 실패 로 보고(사유 포함) |
activate_model | name, version | 캐시에 있는 버전으로 전환. 내려받지 않음. 캐시에 없으면 실패 |
remove_model | name, version | 캐시에서 그 버전 삭제. 실행 중인 버전이면 실패 |
내려받았지만 돌리지 못한 버전(엑스트라 없음, 가중치 로드 실패)은 디스크에 남고, 다음 하트비트부터 state: failed 와 error 로 보고. 중앙의 모델 탭에서 로드 실패 로 보이고 자세히 로 사유 확인.
활성화 규칙
-
내려받을 때:
activate를false로 지정하지 않으면 내려받은 버전을 즉시 활성 모델로 지정. 러너를 생성하지 못하면(엑스트라 미설치 등) 명령은 실패 로 보고되고 중앙의 배포도 실패로 기록. 파일은 디스크에 남음. -
활성 포인터: 활성화에 성공한 버전은
data_dir/models/active.json에 기록. 로컬 API:activate, CLI--activate, 중앙의 배포 모두 같은 파일을 갱신. -
재시작할 때:
models.auto_activate설정에 따름.값 재시작 뒤 pointer(기본)active.json이 가리키는 버전만 로드해 활성. 파일이 없거나 그 버전이 캐시에 없으면latest로 동작latest모델 이름마다 버전 번호가 가장 큰 캐시 버전을 로드. 마지막에 로드된 모델이 활성 none아무것도 로드하지 않음 정전 뒤에도 명령을 다시 보내지 않고 추론 계속. 재시작 후
geo-mlops-edge models로 확인. -
버전 전환:
curl -X POST http://127.0.0.1:8600/api/v1/models/helmet-detector:activate \ -H 'content-type: application/json' -d '{"version": "1"}' geo-mlops-edge models --activate helmet-detector --version-tag 1장비에서 직접 전환하면 중앙의 기대 모델과 달라져 모델 탭 배지가 불일치 로 바뀜. 중앙에서 다시 적용 하거나, 바꾼 버전을 이 버전으로 전환 으로 배포해 맞춤.
장비 프로그램이 직접 돌리는 모델
자체 검출기나 규칙으로 판정하는 프로그램도 플릿 입장에서는 모델을 돌리는 것. runtime.external_models 로 보고하면 하트비트 models[] 에 source: external 로 실리고, 중앙의 모델 탭에 외부 로 표시.
from geo_mlops_sdk.contracts.inference import ModelRef
runtime.external_models.set(
ModelRef(name="pass-anomaly", version="rule-1"), framework="rule", task="pass_anomaly"
)
runtime.external_models.clear("pass-anomaly") # 배포된 모델이 넘겨받을 때
외부 모델은 배포 대상이 아님. 배포된 모델로 바꾸려면 runtime.events 의 model.activated 를 받아 전환하고, model.deactivated·model.activation_failed 면 되돌림. 외부 모델은 하트비트에서 SDK 모델 뒤에 실리므로, SDK 모델이 활성이면 중앙은 그것을 장비의 실행 모델로 봄.
러너 선택
러너는 모델 디렉터리의 MLmodel 파일에서 metadata.geo_framework 를 읽어 선택. 플랫폼 학습이 기록한 모델에는 이 값이 포함.
geo_framework | 러너 | 필요한 엑스트라 |
|---|---|---|
yolo | 내장 YOLO 러너 | yolo |
rf-detr / rfdetr | 내장 RF-DETR 러너 | rfdetr |
| 그 밖의 이름 | register_runner 로 등록한 러너 | 없음 |
엑스트라가 없으면 no runner for framework 'yolo'; install 'geo-mlops-sdk[yolo]' 처럼 설치할 엑스트라 안내.
로컬 추론 API
POST/api/v1/inference
활성 모델로 이미지 한 장 판정. 세 가지 형식 모두 지원.
curl -X POST http://127.0.0.1:8600/api/v1/inference -F [email protected]
# 모델 지정: -F model=helmet-detectorcurl -X POST http://127.0.0.1:8600/api/v1/inference \
-H 'content-type: image/jpeg' --data-binary @frame.jpgcurl -X POST http://127.0.0.1:8600/api/v1/inference \
-H 'content-type: application/json' \
-d "{\"image_b64\": \"$(base64 -w0 frame.jpg)\", \"model\": \"\", \"record\": true, \"params\": {}}"record(결과를 중앙에 기록할지 여부)와 params(러너에 전달할 값)는 JSON 형식에서만 지정 가능.
응답 예:
{
"task": "detect",
"width": 640,
"height": 480,
"detections": [
{ "cls": 0, "name": "bright", "conf": 0.894, "bbox": [0.0, 0.0, 1.0, 1.0], "polygon": [] }
]
}
bbox는[x1, y1, x2, y2], 원본 이미지 기준 0~1 로 정규화한 값.polygon은 분할 모델일 때만 채워짐. 플랫폼의 중앙 서빙과 같은 형식이므로 모델을 장비에서 실행하든 중앙에서 실행하든 수신 측 코드 수정 불필요.- 활성 모델이 없으면
409 no active model; activate one first, 이미지가 없으면400, 본문이api.max_body_bytes를 넘으면413. - 기본적으로 결과는 큐에 저장되고, 중앙의 추론 결과 탭에 모델·지연·결과와 함께 기록.
- 판정한 프레임 자체를 학습 데이터로 보내려면 frames 수집기 사용.
커스텀 러너
플랫폼이 지원하지 않는 프레임워크(ONNX, OpenVINO, 규칙 기반 검사 등)는 Runner 프로토콜을 구현해 등록. 메서드는 세 가지.
| 메서드 | 역할 |
|---|---|
load(model: LocalModel) | 가중치 등 로드. model.path, model.weights_path, model.class_names, model.metadata 사용 가능. |
predict(image: bytes, **params) -> InferenceOutput | 이미지 한 장 판정 |
close() | 장치·메모리 해제 |
아래는 이미지 평균 밝기로 판정하는 간단한 예제 러너.
# my_edge.py
import io
import sys
from pathlib import Path
from PIL import Image
from geo_mlops_sdk.contracts.inference import Detection, InferenceOutput
from geo_mlops_sdk.edge.daemon import run
from geo_mlops_sdk.edge.models import register_runner
from geo_mlops_sdk.edge.settings import EdgeSettings
class BrightnessRunner:
def load(self, model):
self.classes = model.class_names or ["bright"]
self.threshold = float(model.metadata.get("threshold", 100))
def predict(self, image: bytes, **params) -> InferenceOutput:
img = Image.open(io.BytesIO(image)).convert("L")
mean = sum(img.getdata()) / (img.width * img.height)
detections = []
if mean >= float(params.get("threshold", self.threshold)):
detections.append(Detection(cls=0, name=self.classes[0],
conf=round(mean / 255, 3),
bbox=[0.0, 0.0, 1.0, 1.0]))
return InferenceOutput(task="detect", width=img.width,
height=img.height, detections=detections)
def close(self):
pass
register_runner("brightness", BrightnessRunner) # 인자 없이 부를 수 있는 팩토리
if __name__ == "__main__":
config = Path(sys.argv[1]) if len(sys.argv) > 1 else None
sys.exit(run(EdgeSettings.load(config)))
모델 디렉터리의 MLmodel 에 프레임워크 이름 지정. 에이전트는 geo_framework 값을 소문자로 변환해 러너를 찾으므로 register_runner 의 이름은 소문자로 작성.
# data_dir/models/brightness-check/1/MLmodel
flavors:
python_function:
loader_module: none
metadata:
geo_framework: brightness
geo_task: detect
class_names: ["bright"]
python my_edge.py /etc/geo-mlops/edge.yaml 로 실행하면 캐시의 모델을 로드해 활성화.
geo-mlops-edge models
{
"items": [
{ "name": "brightness-check", "version": "1", "framework": "brightness",
"task": "detect", "classes": 1,
"path": "/var/lib/geo-mlops-edge/models/brightness-check/1", "active": true }
],
"active": "brightness-check"
}
러너의 load·predict 는 스레드 풀에서 실행, 잠금으로 한 번에 한 예측만 실행. GPU 하나에서 두 예측이 동시에 실행되어 메모리가 부족해지는 것을 방지.