스터디/AI

[실무] bentoML 라이브러리

_leezoee_ 2023. 3. 4. 00:50

(mlflow는 실험, 프로젝트, 모델 관리와 서빙을 두루 한다면 bentoML은 모델 서빙에 집중한 라이브러리)

 

진행순서

 

1. 모델 훈련

2. BentoService 인스턴스 만들기

3. BentoService#pack 으로 학습된 모델 아티팩트 패키징

4. BentoService#save 로 Bento 에 저장

 

pip install bentoml

 

 


 

<Iris 데이터를 활용한 SVC 모델 구현>

from sklearn import svm
from sklearn import datasets

iris = datasets.load_iris()
X, y = iris.data, iris.target

clf=svm.SVC(gamma='scale')
clf.fit(X,y)

 

 

< 분류모델 서비스 생성 1>

import pandas as pd

from bentoml import env, artifacts, api, BentoService
from bentoml.adapters import DataframeInput
from bentoml.frameworks.sklearn import SklearnModelArtifact

@env(infer_pip_packages=True)
@artifacts([SklearnModelArtifact('model')])
class IrisClassifier(BentoService):
    @api(input=DataframeInput(), batch=True)
    def predict(self, df:pd.DataFrame):
    	rerturn self.artifacts.model.predict(df)

 

< 분류모델 서비스 생성 2>

@artifact는 예측 서비스에 필요한 모델파일들을 지정.

@env 에서 파이썬패키지 지정

@api는 예측 서비스에 액세스하기위한 endpoint, 추론 API를 정의.

import pandas as pd

from bentoml import env, artifacts, api, BentoService
from bentoml.adapters import DataframeInput
from bentoml.frameworks.sklearn import SklearnModelArtifact

@env(infer_pip_packages=True)
@artifacts([SklearnModelArtifact('model')])
class IrisClassifier(BentoService):

    @api(input=DataframeInput(), batch=True)
    def predict(self, df:pd.DataFrame):
        return self.artifacts.model.predict(df)

 

 

<배포를 위한 서비스 저장>

IrisClassifier 위에 정의한 예측 서비스 클래스로 학습 된 모델을 패키징.

배포를 위해 IrisClassifier 인스턴스를 BentoML 형식으로 디스크에 저장.

 

from iris_classifier import IrisClassifier
from sklearn import svm
from sklearn import datasets

iris = datasets.load_iris()
X,y = iris.data, iris.target

clf = svm.SVC(gamma='scale')
clf.fit(X, y)

iris_classifier_service = IrisCalssifier()
iris_classifier_service.pack('model', clf)
saved_path = iris_classifier_service.save()

 

<모델 서빙>

$ bentoml serve IrisClassifier:latest

 

<추론 요청 실행>

bentoml run IrisClassifier:latest predict --input '[[5.1, 3.5, 1.4, 0.2]]'

or

curl -i \
 	--header "Content-Type:application/json" \
    --request POST \
    --data '[[5.1, 3.5, 1.4, 0.2]]' \
    http://localhost:5000/predict

 

 

<도커 컨테이너화>

$ bentoml containerize IrisClassifier:latest -t iris-classifier

 

 

 


 

 

<Fashion Mnist 데이터 텐서플로우2.0 이미지 분류 서빙 예제>

import io
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

fashion_mnist = tf.keras.datasets.fashion_mnist
(_train_images, train_labels) , (_test_images, test_labels) = fashion_mnist.load_data()
class_names = ['T-shirt/top' , 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
train_images = _train_images/255.0
test_images = _test_images/255.0

class FashionMnist(tf.keras.Model):
    def__init__(self):
        super(FashionMnist, self).__init__()
        self.cnn = tf.keras.Sequential([
            tf.keras.layers.Flatten(input_shape=(28,28)),
            tf.keras.layers.Dense(128, activation='relu'),
            tf.keras.layers.Dense(10, activation='softmax'),
        ])
        
        @staticmethod
        def image_bytes2tensor(inputs):
            with tf.device("cpu:0"):
                inputs = tf.map_fn(lambda i : tf.io.decode_png(i, channels=1), inputs, dtype=tf.unit8)
            inputs = tf.cast(inputs, tf.float32)
            inputs = (255.0 - inputs) / 255.0
            inputs = tf.reshape(inputs, [-1, 28, 28])
            return inputs
            
        @tf.function(input_signature=[tf.TensorSpec(shape=(None,), dtype=tf.string)])
        def predict_image(self, inputs):
            inputs = self.image_bytes2tensor(inputs)
            return self(inputs)
            
        def call(self, inputs):
            return self.cnn(inputs)

 

<테스트용 이미지 생성>

d_test_img = _test_images[0]
print(class_names[test_labels[0]])

plt.imshow(255.0 - d_test_img, cmap='gray')
plt.imsave("test.png", 255.0 - d_test_img, cmap='gray')

#바이트 읽기
with open("test.png", "rb") as f:
    img_bytes = f.read()
    
#저장된 이미지 확인
assert tf.reduce_mean(FashionMnist.image_bytes2tensor(tf.constant([img_bytes])) - d_tes_img) < 0.01

 

<모델 학습>

model = FashionMnist()
model.compile(optimizer = 'adam',
	loss='spare_categorical_crossentropy',
    metrics = ['accuracy']    
)
model.fit(train_images, train_labels, epochs=50)

 

 

<모델 추론 동작 확인>

predict = model.predict_image(tf.constant([img_bytes]))
klass = tf.argmax(predict, axis=1)
[class_names[c] for c in klass]

 

 

<BentoService 클래스 생성>

import bentoml
import tensorflow as tf
from bentoml.artifact import TensorflowSavedModelArtifact
from bentoml.adapters import TfTensorInput

FASHION_MNIST_CLASSES =  ['T-shirt/top' , 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

@bentoml.env(pip_dependencies = ['tensorflow','numpy', 'pilow'])
@bentoml.artifacts([TensorflowSavedModelArtifact('model')])
class FashionMnistTensorflow(bentoml.BentoService):
    @bentoml.api(input=TfTensorInput(), batch=True)
    def predict(self, inputs):
        outputs = self.artifacts.model.predict_image(inputs)
        output_classes = tf.math.argmax(outputs, axis=1)
        return [FASHION_MNIST_CLASSES[c] for c in output_classes]
        

from tensorflow_fashion_mnist import FashionMnistTensorflow

bento_svc = FashionMnistTensorflow()
bento_svc.pack("model", model)
saved_path = bento_svc.save()

 

 

<인퍼런스 서버 실행(모델 서빙)>

$ bentoml serve FashionMnistTensorflow:latest

 

 

<테스트이미지 base64 인코딩>

 

echo '{"instances":[{"b64":"'$(base64 test.png)'"}]}' > test.json

 

 

<테스트 이미지로 모델 실행>

 

curl -X POST http://localhost:5000/predict -d @test.json --header "Content-Type : application/json"