M l ops Docker

Đóng gói quá trình Train AI model và Batch Inference sử dụng Docker

Đóng gói quá trình Train AI model và Batch Inference sử dụng Docker

Trong bài trước, chúng ta đã tìm hiểu và sử dụng Docker để triển khai AI model theo kiểu online inference. Trong bài này, ta sẽ train một model khác để inference theo kiểu thứ 2, batch inference, sử dụng docker. Mình cũng sẽ thực hiện việc train model bên trong docker luôn (các bài trước đó là train model bên ngoài docker, sau đó chỉ copy model đã train vào trong docker để thực hiện online inference).

1. Tạo cấu trúc thư mục

Trước tiên, chúng ta sẽ tạo ra một thư mục để lát nữa khi build docker image, nó sẽ được copy vào trong docker image đó.

code
├── Dockerfile
├── batch_inference.py
└── train.py

Trong đó:

  • code: thư mục làm việc chính.
  • Dockerfile: File cấu hình để build docker image.
  • train.py: File code python để thực hiện train model.
  • batch_inference.py: File code python để thực hiện batch inference.

2. Tạo code python để train model và inference data

Lần này, chúng ta sẽ thực hiện train model để dự đoán giá nhà tại Boson, sử dụng tập dữ liệu boson trong thư viện scikit-learn. Đây là kiểu model Leaner Regression.

Tạo file train.py với nội dung như sau:

import json
import os

from joblib import dump
import matplotlib.pyplot as plt
import numpy as np
from sklearn import ensemble
from sklearn import datasets
from sklearn.utils import shuffle
from sklearn.metrics import mean_squared_error


# #############################################################################
# Load directory paths for persisting model and metadata

MODEL_DIR = os.environ["MODEL_DIR"]
MODEL_FILE = os.environ["MODEL_FILE"]
METADATA_FILE = os.environ["METADATA_FILE"]
MODEL_PATH = os.path.join(MODEL_DIR, MODEL_FILE)
METADATA_PATH = os.path.join(MODEL_DIR, METADATA_FILE)

# #############################################################################
# Load and split data
print("Loading data...")
boston = datasets.load_boston()

print("Splitting data...")
X, y = shuffle(boston.data, boston.target, random_state=13)
X = X.astype(np.float32)
offset = int(X.shape[0] * 0.9)
X_train, y_train = X[:offset], y[:offset]
X_test, y_test = X[offset:], y[offset:]

# #############################################################################
# Fit regression model
print("Fitting model...")
params = {'n_estimators': 500, 'max_depth': 4, 'min_samples_split': 2,
          'learning_rate': 0.01, 'loss': 'ls'}
clf = ensemble.GradientBoostingRegressor(**params)

clf.fit(X_train, y_train)
train_mse = mean_squared_error(y_train, clf.predict(X_train))
test_mse = mean_squared_error(y_test, clf.predict(X_test))
metadata = {
    "train_mean_square_error": train_mse,
    "test_mean_square_error": test_mse
}

# #############################################################################
# Serialize model and metadata
print("Serializing model to: {}".format(MODEL_PATH))
dump(clf, MODEL_PATH)

print("Serializing metadata to: {}".format(METADATA_PATH))
with open(METADATA_PATH, 'w') as outfile:  
    json.dump(metadata, outfile)

Tạo file batch_inference.py với nội dung như sau:

import os

from joblib import load
import numpy as np
from sklearn import datasets
from sklearn.utils import shuffle


MODEL_DIR = os.environ["MODEL_DIR"]
MODEL_FILE = os.environ["MODEL_FILE"]
METADATA_FILE = os.environ["METADATA_FILE"]
MODEL_PATH = os.path.join(MODEL_DIR, MODEL_FILE)
METADATA_PATH = os.path.join(MODEL_DIR, METADATA_FILE)

# #############################################################################
# Get a batch of data to inference
def get_data():
    """
    Return data for inference.
    """
    print("Loading data...")
    boston = datasets.load_boston()
    X, y = shuffle(boston.data, boston.target, random_state=13)
    X = X.astype(np.float32)
    offset = int(X.shape[0] * 0.9)
    X_train, y_train = X[:offset], y[:offset]
    X_test, y_test = X[offset:], y[offset:]
    return X_test, y_test

print("Running inference...")

X, y = get_data()


# #############################################################################
# Load model
print("Loading model from: {}".format(MODEL_PATH))
clf = load(MODEL_PATH)

# #############################################################################
# Run inference
print("Scoring observations...")
y_pred = clf.predict(X)
print(y_pred)

Mình tin chắc các bạn có thể dễ dàng hiểu được đoạn code trên. Ở đây, có một chú ý là mình sử dụng một số biến môi trường MODEL_DIR, MODEL_FILE, …. Mình không muốn hard-code những biến này vì chúng được sử dụng ở 2 nơi, train và inference model. Thay vào đó, giá trị của chúng sẽ được truyền vào lúc build docker.

3. Build Docker image để train model

Tạo file Dockerfile với nội dung như sau:

FROM jupyter/scipy-notebook
USER root
WORKDIR /code
ADD . /code

RUN pip install joblib
RUN mkdir model

# Env variables
ENV MODEL_DIR=/code/model
ENV MODEL_FILE=clf.joblib
ENV METADATA_FILE=metadata.json

# COPY train.py ./train.py
# COPY batch_inference.py ./batch_inference.py

RUN python3 train.py

Để build docker image, chạy lệnh sau:

$ docker build -t docker-model-batch-infer .

Output:

Sending build context to Docker daemon  4.608kB
Step 1/10 : FROM jupyter/scipy-notebook
 ---> 069532086d63
Step 2/10 : USER root
 ---> Running in c580ea3bbd7f
Removing intermediate container c580ea3bbd7f
 ---> efcad69c0b79
Step 3/10 : WORKDIR /code
 ---> Running in 08ee819c1e52
Removing intermediate container 08ee819c1e52
 ---> d05432266229
Step 4/10 : ADD . /code
 ---> 6b1f81f3a9f6
Step 5/10 : RUN pip install joblib
 ---> Running in 9a2c41424c59
Removing intermediate container 9a2c41424c59
 ---> 11e642103f1f
Step 6/10 : RUN mkdir /code/model
 ---> Running in 5a4068194bfa
Removing intermediate container 5a4068194bfa
 ---> 6ae6727bf9aa
Step 7/10 : ENV MODEL_DIR=/code/model
 ---> Running in f8992466e635
Removing intermediate container f8992466e635
 ---> c3491c25966e
Step 8/10 : ENV MODEL_FILE=clf.joblib
 ---> Running in 15f321f925e4
Removing intermediate container 15f321f925e4
 ---> a643969fdfd1
Step 9/10 : ENV METADATA_FILE=metadata.json
 ---> Running in 72c0b8ef67db
Removing intermediate container 72c0b8ef67db
 ---> efce67f3494c
Step 10/10 : RUN python3 train.py
 ---> Running in 10bec25fc25e
Loading data...
Splitting data...
Fitting model...
Serializing model to: /code/model/clf.joblib
Serializing metadata to: /code/model/metadata.json
Removing intermediate container 10bec25fc25e
 ---> 05d1c3a12437
Successfully built 05d1c3a12437
Successfully tagged docker-model-batch-infer:latest

Kiểm tra nội dung file metadata:

$ docker run docker-model-batch-infer cat /code/model/metadata.json

Kết quả:

{"train_mean_square_error": 1.7677391462344387, "test_mean_square_error": 6.588673999729974}

Chú ý: Model ở đây chưa được tuning để tối ưu hóa hiệu năng, các thông tin metadata khác như thuật toán, phân phối dữ liệu, phiên bản model, … cũng không được lưu lại. Nếu bạn sử dụng hướng dẫn này trong thực tế thì cần lưu ý những điểm trên.

4. Thực hiện Batch Inference

Chúng ta đã có một model đã train, được lưu thành file clf.joblib trong docker image docker-model-batch-infer. Giờ là lúc sử dụng nó để thực hiện inference.

Để thực hiện batch inference, ta sẽ khởi động docker image và chạy lệnh thực thi code kèm theo đó:

$ docker run docker-model-batch-infer python3 batch_inference.py

Kết quả:

Running inference...
Loading data...
Loading model from: /code/model/clf.joblib
Scoring observations...
[15.32448686 27.68741572 24.23789723 31.94786177 10.43966955 34.25663827
 22.05210667 11.58265489 13.36407623 42.87157933 33.03218733 15.77635169
 23.93521876 19.88239305 25.43466604 20.55132127 13.65254047 47.45491473
 17.5734174  21.51806638 22.57388848 16.97645106 16.25503893 20.57862843
 14.57438158 11.81385445 24.78353556 37.51637481 30.34664466 19.67895051
 23.22841646 25.02203256 18.65459129 30.09762517  8.96667041 13.8130382
 14.18734797 17.3840622  19.83840166 24.23822033 20.52076144 15.32433651
 25.8157052  16.47533793 19.2214524  19.87110293 21.47113681 21.56443118
 24.64517965 22.43665872 22.22261406]

5. Kết luận

Như vậy là chúng ta đã thực hiên xong việc train một ML model và sử dụng nó để inference dữ liệu bằng docker. Tuy nhiên, để mang nó vào sử dụng trong môi trường production thì chúng ta cần thực hiện thêm một số công việc nữa như là lập lịch đê thực hiện batch inference định kỳ, tuning hyper-parameter, lưu trữ metadata của model, …

Toàn bộ source code sử dụng trong bài này, các bạn có thể tham khảo trên github cá nhân của mình tại đây.

Trong các bài viết tiếp theo, mình sẽ tổng hợp lại các thuật toán Deep Learning, sau đó sẽ đi chi tiết vào một số thụât toán với các ứng dụng cụ thể. Mời các bạn đón đọc!

6. Tham khảo