학습이 완료된 MLflow모델을 저장 한 경우, Seldon의 사전 패키지 된 MLflow 서버를 사용하여 간단히 배포 할 수 있습니다. 그리고 conda.yaml
파일을 이용하면, MLflow 서버의 초기화 중 Conda 환경을 만들 수 도 있습니다.
전제 조건
MLflow 서버를 사용하려면 다음 전제 조건이 충족되어야합니다.
- MLmodel 아티팩트 폴더에 원격으로 액세스 할 수 있어야합니다
- 모델 형식은 python_function 과 호환 가능해야합니다
MLproject
환경은 Conda를 사용하여 지정해야합니다.
MFLow에서 제공하는 모델 형태
MLFlow 에서 제공하고 있는 주요 모델 형태는 다음과 같습니다.
- Python Function (python_function)
- Keras (keras)
- PyTorch (pytorch)
- Scikit-learn (sklearn)
- Spark MLlib (spark)
- TensorFlow (tensorflow)
- XGBoost (xgboost)
Conda 환경 생성
MLflow 서버는 여러가지 머신러닝 프레임워크를 지원합니다. 그래서 모델 서버를 실행할 때 필요한 패키지들을 설치할 필요가 있습니다. MLflow 서버는 conda.yaml
파일을 이용하여 모델 서버에 실행에 필요한 환경을 만듭니다. SeldonDeployment
에 의해서 모델 서버가 생성될 때, 모델 저장 위치에 있는 conda.yaml
파일을 읽어와서 Conda 환경을 구성합니다.
모델 생성
MLflow 서버를 사용하기 위해 파이썬을 사용한 간단한 scikit-learn 모델을 생성한 후, MLFlow를 이용하여 저장하겠습니다.
Scikit-learn의 기본적인 데이터셋 중의 하나인 아이리스 꽃 데이터를 사용하여, 아이리스 꽃을 분류하는 모델을 작성해 보겠습니다. mlflow.sklearn.save_model() 메소드를 이용하여 MLflow 형식으로 모델을 저장합니다.
from joblib import dump from sklearn import datasets from sklearn import svm clf = svm.SVC(gamma='scale') iris = datasets.load_iris() X, y = iris.data, iris.target clf.fit(X, y) mlflow.sklearn.save_model(clf, path=model_path)
모델 저장하기
쿠버네티스의 퍼시스턴스 볼륨에 모델을 저장해 보겠습니다. PVC 는 앞서 생성한 seldon-models-pvc
을 사용하겠습니다. 모델을 학습시키기 위해서 쿠버네티스 잡(Job)을 사용하겠습니다. Job을 생성할 때 모델을 저장하기 위한 PVC를 마운트 해줍니다.
모델 코드 작성하기
아이리스 꽃을 분류하는 간단한 모델입니다. 모델을 저장할 위치를 --model_path
파라미터로 입력받게 하였습니다.
mlflow_sklearn_iris.py
import argparse import os import mlflow import mlflow.sklearn from sklearn import datasets from sklearn import svm def train(): parser = argparse.ArgumentParser() parser.add_argument('--model_path', default='/mnt/pv/mlflow/sklearn/iris/model', type=str) args = parser.parse_args() # if not (os.path.isdir(args.model_path)): # os.makedirs(args.model_path) # os.rmdir(args.model_path) clf = svm.SVC(gamma='scale') iris = datasets.load_iris() X, y = iris.data, iris.target clf.fit(X, y) print('Finished Training') conda_env = { 'name': 'mlflow-env', 'channels': ['defaults'], 'dependencies': [ 'python=3.7.0', 'scikit-learn=0.20.3' ] } mlflow.sklearn.save_model(clf, path=args.model_path, conda_env=conda_env) if __name__ == '__main__': train()
컨테이너 이미지를 만들기
컨테이너 이미지를 만들기 위한 Dockerfile 입니다. 파이썬을 기본 이미지로 사용하고, scikit-learn
패키지를 추가로 설치합니다.
Dockerfile
FROM python:3.7-slim RUN pip install scikit-learn==0.20.3 mlflow RUN mkdir -p /app ADD mlflow_sklearn_iris.py /app/
쿠버네티스 잡 실행하기
컨테이너 이미지를 빌드하고, 컨테이너 이미지 레지스트리에 푸시 한 다음, 쿠버네티스 잡(Job)을 생성하겠습니다.
Job을 생성할 때는 모델을 저장하기 위해서 PVC를 마운트 해줍니다. 이 일련의 작업들은 직접 실행 할 수 있습니다. 하지만 좀 더 편하게 하기 위해서 앞서 배운 Kubeflow Fairing을 사용하겠습니다.
다음은 로컬 개발 환경에서 Fairing을 사용하여 컨테이너 이미지를 만들고, 쿠버네티스 잡을 실행하는 예제입니다.
import uuid from kubeflow import fairing from kubeflow.fairing.kubernetes import utils as k8s_utils CONTAINER_REGISTRY = 'kangwoo' namespace = 'admin' job_name = f'mlflow-sklean-iris-job-{uuid.uuid4().hex[:4]}' command=["python", "mlflow_sklearn_iris.py", "--model_path", "/mnt/pv/mlflow/sklearn/iris/model"] output_map = { "Dockerfile": "Dockerfile", "mlflow_sklearn_iris.py": "mlflow_sklearn_iris.py" } fairing.config.set_preprocessor('python', command=command, path_prefix="/app", output_map=output_map) fairing.config.set_builder('docker', registry=CONTAINER_REGISTRY, image_name="mlflow-sklean-iris", dockerfile_path="Dockerfile") fairing.config.set_deployer('job', namespace=namespace, job_name=job_name, pod_spec_mutators=[k8s_utils.mounting_pvc(pvc_name='seldon-models-pvc', pvc_mount_path='/mnt/pv')], cleanup=True, stream_log=True) fairing.config.run()
fairing을 실행하면 쿠버네티스 잡이 생성되고, 학습이 완료된 모델이 지정한 경로에 저장됩니다.
MLflow을 사용하는 SeldonDeployment 로 배포 하기
SeldonDeployment 생성
SeldonDeployment 매니페스트를 작성합니다. predictor의 구현체를 MLFLOW_SERVER
로 사용합니다. modelUri
필드로 모델 저장 위치를 지정해 줍니다. pvc 의 이름이 seldon-models-pvc
이고 저장 위치가 mlflow/sklearn/iris/model
이므로, pvc://seldon-models-pvc/mlflow/sklearn/iris/model
라고 지정해 줍니다.
mlflow-sklearn-iris.yaml
apiVersion: machinelearning.seldon.io/v1alpha2 kind: SeldonDeployment metadata: name: mlflow-sklearn-iris spec: name: mlflow-sklearn-iris predictors: - graph: children: [] implementation: MLFLOW_SERVER modelUri: pvc://seldon-models-pvc/mlflow/sklearn/iris/model name: classifier name: default replicas: 1
SeldonDeployment 를 생성합니다.
다음은 admin 네임스페이스 SeldonDeployment 를 생성하는 예제입니다.
kubectl -n admin apply -f mlflow-sklearn-iris.yaml
생성한 SeldonDeployment를 조회해 보겠습니다.
kubectl -n admin get seldondeployment mlflow-sklearn-iris -o yaml
SeldonDeployment 가 정상적으로 생성되면 다음과 같은 응답 결과를 확인할 수 있습니다.
apiVersion: machinelearning.seldon.io/v1 kind: SeldonDeployment metadata: ... spec: ... status: deploymentStatus: mlflow-sklearn-iris-default-8c791aa: availableReplicas: 1 replicas: 1 serviceStatus: mlflow-sklearn-iris-mlflow-sklearn-iris-default: grpcEndpoint: mlflow-sklearn-iris-mlflow-sklearn-iris-default.admin:5001 httpEndpoint: mlflow-sklearn-iris-mlflow-sklearn-iris-default.admin:8000 svcName: mlflow-sklearn-iris-mlflow-sklearn-iris-default seldon-d9062e953c9534d009e3dc84a7d3707a: httpEndpoint: seldon-d9062e953c9534d009e3dc84a7d3707a.admin:9000 svcName: seldon-d9062e953c9534d009e3dc84a7d3707a state: Available
SeldonDeployment
의 state
가 Available
이면 예측을 요청 할 수 있습니다.
예측 실행하기
예측을 요청하기 위해서는 모델 서버에 접근해야 합니다. 모델 서버는 ingressgateway 를 통해서 접근할 수 있습니다. ingressgateway 는 모델 서버들을 구분하기 위해서 호스트 이름을 사용합니다. ingressgateway에 접근하 기 위한 주소는 앞서 정의한 CLUSTER_IP 를 사용하겠습니다.
예측을 요청할 데이터를 json 파일로 작성합니다.
iris-input.json
{ "data": { "ndarray": [ [6.8, 2.8, 4.8, 1.4], [6.0, 3.4, 4.5, 1.6] ] } }
다음은 admin 네임스페이스의 sklearn-iris SeldonDeployment 에 예측을 요청하는 예제입니다.
MODEL_NAME=mlflow-sklearn-iris NAMESPACE=admin INPUT_PATH=@./iris-input.json curl -v -H "Content-Type: application/json" http://$CLUSTER_IP/seldon/${NAMESPACE}/${MODEL_NAME}/api/v1.0/predictions -d $INPUT_PATH
정상적으로 실행되면 다음과 같은 응답 결과를 확인 할 수 있습니다.
* Trying 192.168.21.38... * TCP_NODELAY set * Connected to 192.168.21.38 (192.168.21.38) port 32380 (#0) > POST /seldon/admin/mlflow-sklearn-iris/api/v1.0/predictions HTTP/1.1 > Host: 192.168.21.38:32380 > User-Agent: curl/7.64.1 > Accept: */* > Content-Type: application/json > Content-Length: 96 > * upload completely sent off: 96 out of 96 bytes < HTTP/1.1 200 OK < x-content-type-options: nosniff < vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers < content-type: application/json;charset=utf-8 < content-length: 261 < date: Thu, 09 Apr 2020 16:02:46 GMT < x-envoy-upstream-service-time: 100 < server: istio-envoy < { "meta": { "puid": "geuhtnu2stgad08ngp9c0382oi", "tags": { }, "routing": { }, "requestPath": { "classifier": "seldonio/mlflowserver_rest:0.2" }, "metrics": [] }, "data": { "names": [], "ndarray": [1, 1] } }