μλ νμλκΉ
μ λ μμ μμ Flask APIκ° μλ TensorFlow 2.0 νλ‘μ νΈκ° μμΌλ―λ‘ APIμμ μ΄λ―Έ μνλ λ°μ΄ν° μ¬μ μ²λ¦¬λ‘ HTTP νΈμΆμ ν΅ν΄ λͺ¨λΈμ μμ²ν μ μμ΅λλ€. λ컀 컨ν μ΄λμμ Flask/TensorFlow μ ν리μΌμ΄μ μ μ€ννκΈ° μν΄ Gunicornμ μ ννμ΅λλ€. μ¬νκ²λ Gunicornμ΄ λ§λλ μμ μ νλ‘μΈμ€λ Gunicornμ μν΄ μ£½μ λκΉμ§ 컨ν μ΄λμ 맀λ¬λ € μμ΅λλ€. μλ²κ° μ€μ§ μκ³ μμ²μ ν μ μμ΅λλ€. λν λμΌν Gunicorn μ€μ μ΄ λ΄ νΈμ€νΈ μ»΄ν¨ν°μ λ컀 μΈλΆμμ μλ²½νκ² μλν©λλ€.
Docker λ‘κ·Έ(κ·Έλ₯ κ±°κΈ°μ λ©μΆκ³ μ€λ μκ°μ΄ μ§λλ©΄ μκ° μ΄κ³Ό μ€λ₯κ° μΈμλ¨)
[2019-10-03 18:03:05 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2019-10-03 18:03:05 +0000] [1] [INFO] Listening at: http://127.0.0.1:8000 (1)
[2019-10-03 18:03:05 +0000] [1] [INFO] Using worker: sync
[2019-10-03 18:03:05 +0000] [8] [INFO] Booting worker with pid: 8
2019-10-03 18:03:08.126584: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-10-03 18:03:08.130017: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 3392000000 Hz
2019-10-03 18:03:08.130306: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x55fbb23fb2d0 executing computations on platform Host. Devices:
2019-10-03 18:03:08.130365: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Host, Default Version
λ컀 νμΌ:
FROM python
RUN pip install gunicorn
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD [ "gunicorn", "--chdir", "src", "api:app" ]
api.py:
from flask import Flask, request
import inference
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def predict():
if request.method == 'GET':
return 'POST a json payload of {"imageBase64": "base64base64base64"} to this address to predict.'
try:
result = inference.run(request.json['imageBase64'])
return result
except Exception as e:
return {'error': str(e)}, 500
if __name__ == "__main__":
app.run()
else:
print('\n * Server ready!')
inference.py
# Import packages
from __future__ import absolute_import, division, print_function, unicode_literals
import os
import tensorflow as tf
from tensorflow import keras
import PIL
import numpy as np
from io import BytesIO
import base64
import json
print("TensorFlow version is ", tf.__version__)
# Set variables
##########################################################################################
##########################################################################################
model_name = 'catsdogs'
base_dir = os.path.join(os.path.dirname(__file__), '..')
model_dir = os.path.join(base_dir, 'models')
##########################################################################################
##########################################################################################
# Load model
model = keras.models.load_model(os.path.join(model_dir, model_name + '.h5'))
# Load metadata
with open(os.path.join(model_dir, model_name + '_metadata.json')) as metadataFile:
metadata = json.load(metadataFile)
# Split metadata
labels = metadata['training_labels']
image_size = metadata['image_size']
# Exported function for inference
def run(imgBase64):
# Decode the base64 string
image = PIL.Image.open(BytesIO(base64.b64decode(imgBase64)))
# Pepare image
image = image.resize((image_size, image_size), resample=PIL.Image.BILINEAR)
image = image.convert("RGB")
# Run prediction
tensor = tf.cast(np.array(image), tf.float32) / 255.
tensor = tf.expand_dims(tensor, 0, name=None)
result = model.predict(tensor, steps=1)
# Combine result with labels
labeledResult = {}
for i, label in enumerate(labels):
labeledResult[label] = float(result[0][labels[label]])
return labeledResult
λλ μ€λ«λμ μ΄κ²μ λν ν΄κ²°μ± μ μ°Ύμκ³ μ무 κ²λ μκ°ν΄ λ΄μ§ λͺ»νμ΅λλ€. μ΄λ€ λμμ΄λΌλ λλ¨ν κ°μ¬νκ² μ΅λλ€.
κ°μ¬ ν΄μ!
Docker μ€μ μ΄ μ»¨ν μ΄λμ μ¬μ© κ°λ₯ν μ΅λ λ©λͺ¨λ¦¬λ₯Ό μ νν©λκΉ?
κ°μ κ²½ν. λλ Gunicornμ΄ λΉλλ°μ κ²μ΄λΌκ³ μκ°νμ§ μμ΅λλ€. 컨ν
μ΄λμ bash μ
Έμμ python3 api.py
λ₯Ό μ€νν λλ λμΌν μ€λ₯κ° λ°μν©λλ€.
@tlaanemaa @mackdelanyκ° λ§ν κ²μ νμΈν μ μμ΅λκΉ?
μ΄λ΄. μ΄λ κ² μ¬λΌμ Έμ μ£μ‘ν©λλ€.
λ΄ μ€μ μ Dockerμ RAMμ μ½κ° μ ννκ³ μμ§λ§ μ νμ μ κ±°ν΄λ λμΌν μΌμ΄ λ°μνμ΅λλ€.
gunicorn μμ΄ api νμΌμ μ€νν΄λ³΄κ³ λ€μ λ³΄κ³ νκ² μ΅λλ€.
κ°μ¬ ν΄μ!
@tlaanemaa κ·Έκ²μ λν μμμ΄ μμ΅λκΉ?
@benoitc ν€μΌ
μ£μ‘ν©λλ€. μ λ λ€λ₯Έ μΌμ λͺ°λν΄ μμ΄μ μ΄κ²μ λν΄ λ μ΄μΌκΈ°ν μκ°μ΄ μμμ΅λλ€.
λλ μ€λ μ΄κ²μ μ°λ₯΄λ €κ³ λ
Έλ ₯νκ³ λΉμ μκ² λμμ¬ κ²μ
λλ€
κ·Έλμ 컨ν
μ΄λμ gunicorn μμ΄ μ±μ μ€νν΄ λ³΄μλλ° ν¨κ³Όκ° μμμ΅λλ€.
μλλ λ΄ Dockerfileμ CMD λΉνΈμ
λλ€.
곡μ₯:
CMD [ "python", "src/api.py" ]
λ‘κ·Έ:
2019-12-02 11:40:45.649503: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-12-02 11:40:45.653496: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2208000000 Hz
2019-12-02 11:40:45.653999: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x55f969cf6a40 executing computations on platform Host. Devices:
2019-12-02 11:40:45.654045: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Host, Default Version
TensorFlow version is 2.0.0
* Serving Flask app "api" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
μλνμ§ μμ:
CMD [ "gunicorn", "--chdir", "src", "api:app" ]
λ‘κ·Έ:
[2019-12-02 11:39:22 +0000] [1] [INFO] Starting gunicorn 20.0.4
[2019-12-02 11:39:22 +0000] [1] [INFO] Listening at: http://127.0.0.1:8000 (1)
[2019-12-02 11:39:22 +0000] [1] [INFO] Using worker: sync
[2019-12-02 11:39:22 +0000] [9] [INFO] Booting worker with pid: 9
2019-12-02 11:39:24.041188: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-12-02 11:39:24.046495: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2208000000 Hz
2019-12-02 11:39:24.047129: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x5623e18b5200 executing computations on platform Host. Devices:
2019-12-02 11:39:24.047183: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Host, Default Version
λν μνλ κ²½μ° λλ¬λ³Ό μ μλλ‘ μ μ₯μλ₯Ό μ΄μμ΅λλ€.
λμμ΄ λ μ μμ΅λλ€
Listening at: http://127.0.0.1:8000 (1)
λ¬Έμ λ gunicornμ΄ μ»¨ν μ΄λ λ΄λΆμ localhostλ₯Ό μμ λκΈ°νλ―λ‘ μΈλΆμμ μ°κ²°ν μ μλ€λ κ²μ λκΉ?
νλΌμ€ν¬ μ±μ΄ λμΌν μμ
μ μννκ³ μλνκΈ° λλ¬Έμ κ·Έλ κ² μκ°νμ§ μμ΅λλ€.
λν gunicorn λ²μ μ tensorflow λ²μ μ κΈ°λ‘νμ§ μμ΅λλ€. μ΄λ μ½λμ ν΄λΉ λ‘κ·Έ νμμ λ¬Έμ κ° λ°μν¨μ μμν©λλ€. gunicorn μμ΄ μ€νν λ νλΌμ€ν¬λ§ μ€ννλ©΄ κΈ°λ‘λ©λλ€.
TensorFlow version is 2.0.0
λλ²κ·Έ μμ€μμ 무μμ λ§ν©λκΉ?
@tlaanemaa Docker λ°λͺ¬ λ€νΈμνΉμ΄ μ΄λ»κ² ꡬμ±λμ΄ μμ΅λκΉ? @CaselITμ μ견μ λ°λ₯΄λ©΄ ν΄λΌμ΄μΈνΈκ° Docker λ€νΈμν¬λ₯Ό ν΅ν΄ Gunicorn ν¬νΈμ μ°κ²°ν μ μλ κ² κ°μ΅λλ€.
-b 0.0.0.0:8000
μΈμλ‘ Gunicornμ μμν΄ λ³Ό μ μμ΅λκΉ?
λλ λ¬Έμ κ° λ€νΈμν¬μ μλ€κ³ μκ°νμ§ μμ΅λλ€. μ μ΄λ λ‘κ·Έμμ tensorflow κ°μ Έμ€κΈ° νμ μ€λ λ‘κ·Έ λΌμΈμ λλ¬νμ§ μκΈ° λλ¬Έμ μλ²κ° μ ν μμλμ§ μλ κ²μ²λΌ 보μ΄κΈ° λλ¬Έμ λλ€.
κ·ΈλΌμλ λΆκ΅¬νκ³ λλ λΉμ μ μ μμ μλνμ§λ§ κ·Έκ²μ λμκ² μ€λ₯λ₯Ό μ 곡ν©λλ€
CMD [ "gunicorn", "-b", "0.0.0.0:8000", "--chdir", "src", "api:app" ]
_ν΅λ무_
usage: gunicorn [OPTIONS] [APP_MODULE]
gunicorn: error: unrecognized arguments: -d
μ§μ μ¬μ©ν΄λ³΄κ³ μΆλ€λ©΄ Registry.gitlab.com/tlaanemaa/image-classifierμμ 컨ν μ΄λ μ΄λ―Έμ§λ₯Ό μ¬μ©ν μ μμ΅λλ€.
@tlaanemaa μ
λ°μ΄νΈλ Dockerfile
, μ΄λ―Έμ§ λΉλ λͺ
λ Ή λ° μ»¨ν
μ΄λ μ€ν λͺ
λ Ήμ λ€μ κ²μν μ μμ΅λκΉ?
@javabrett νμ€ν
docker build -t tlaanemaa/image-classifier .
_κ²μ λΉμμ Dockerfile:_
FROM python:3.7
RUN pip install gunicorn
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD [ "gunicorn", "-b", "0.0.0.0:8000", "--chdir", "src", "api:app" ]
dockerμ μ 체 λ‘κ·Έλ 무μμ λκΉ? λ§μ§λ§μΌλ‘ μ¬μ© μ€μΈ λͺ λ Ήμ€μ λΆμ¬λ£μ μ μμ΅λκΉ?
μ΄ λ¬Έμ λ₯Ό λλ²κ·Ένλ λμ νΌν μ μλ μμ μ μννμ§ μλ ν μ§κΈμ Porttainer μμ΄ μ€νν μ μμ΅λκΉ?
μ΄κ²μ Macμ© Docker Desktop 2.1.0.5μμ μλν©λλ€.
docker build -t tlaanemaa/image-classifier .
docker run -it --rm -p 8000:8000 tlaanemaa/image-classifier
POST
μμ²μ μλ½ν©λλ€.
μ 체 μΆλ ₯κ³Ό κ²°κ³Όλ₯Ό μ€ννκ³ κ²μνμμμ€.
λλ κ·Έκ²μ μλνκ³ μ§κΈ μλν©λλ€.
-b
νλκ·Έκ° λ¬Έμ λ₯Ό ν΄κ²°νμ μ μμ΅λκΉ?
μ λ§ κ°μ¬ν©λλ€!
μ§κΈ ν₯λ―Έλ‘μ΄ μ μ POST μμ²μ ν λ thposeλ λΉ λ₯΄μ§λ§ GET μμ²μ λ§€μ° λ리λ€λ κ²μ λλ€. μ μ λμ GET μμ²μ μννλ©΄ μ΄λ¬ν μμ²μ λΉ¨λΌμ§μ§λ§ POSTλ λ§€μ° λλ €μ§κ³ μμ μ μκ°μ΄ μ΄κ³Όλ©λλ€. ν΄λΉ POSTμ μλ΅νλ©΄ POSTλ λ€μ λΉ λ₯΄κ³ GETμ λ립λλ€. λΉ λ₯΄κ² 1νλ₯Ό ν μ μμ κ² κ°κ³ μ ννλ λ° μκ°μ΄ 걸립λλ€ :D
λ€μμ μμ μ μκ° μ΄κ³Όλ‘ μΈν΄ GETμ΄ λΉ λ₯΄κ³ POSTκ° λλ¦° κ²½μ°μ λ‘κ·Έμ λλ€.
[2020-01-10 09:34:46 +0000] [1] [CRITICAL] WORKER TIMEOUT (pid:72)
[2020-01-10 09:34:46 +0000] [72] [INFO] Worker exiting (pid: 72)
[2020-01-10 09:34:47 +0000] [131] [INFO] Booting worker with pid: 131
TensorFlow version is 2.0.0
2020-01-10 09:34:48.946351: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2020-01-10 09:34:48.951124: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2208000000 Hz
2020-01-10 09:34:48.951612: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x56481dbabd80 executing computations on platform Host. Devices:
2020-01-10 09:34:48.951665: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Host, Default Version
* Server ready!
λν μΌλΆ μν©μμλ * Server ready!
λ‘κ·Έκ° λ컀 λ‘κ·Έμμ λνλμ§ μλ κ² κ°μ΅λλ€. κ·Έκ²λ μ€ν΄μ μμ§κ° μμμ μ μμ΅λλ€. κ·Έ μμΈμ΄ 무μμΈμ§ νμ€νμ§ μμ΅λλ€.
Dockerμ νμ¬ μλ²λ λ¨μΌ/λκΈ°ν μ€λ λλ‘ κ΅¬μ±λμ΄ μ¬μ© μ€/μ°¨λ¨νκΈ°κ° μ½μ§ μμΌλ―λ‘ μ΄λ₯Ό λ³Ό μ μμ΅λλ€. --workers=2 --threads=4 --worker-class=gthread
μ κ°μ μΈμλ₯Ό μΆκ°ν΄ 보μΈμ.
@javabrett κ°μ¬ν©λλ€
ν΄κ²°νμ΅λλ€!
κ°μ λ¬Έμ κ°μμμ΅λλ€. μ§κΈκΉμ§ λ΄κ° λ΄ μμ μ λ‘κ·Έμμ μΆμΈ‘ ν μ, κ·Έκ²μμ²λΌ 보μ΄λ tensorflow
μ¬μ© gevent
, λΉμ μ μ¬μ©ν μ μμ΅λλ€ gevent
μμ κ°μ μκ°μ gunicorn
. --workers
λ° --threads
νλκ·Έλ μλ¬΄λ° μ°¨μ΄κ° μμ§λ§ --worker-class=gevent
μμ --worker-class=gthread
νλ©΄ λ¬Έμ κ° ν΄κ²°λμμ΅λλ€. @javabrett κ°μ¬
μλ νμΈμ! geventμ κ΄λ¦¬μμ΄μ μ΄ νλ‘μ νΈμ κΈ°μ¬μλ‘μ μ λ geventμ gunicornμ΄ ν¨κ» μ μλνλ€κ³ λ¨νΈνκ² λ§ν μ μμ΅λλ€. λ€μν λΌμ΄λΈλ¬λ¦¬κ° κ°μν μ μμ§λ§ μ΄λ gunicornμ΄λ geventμ μλͺ»μ΄ μλλλ€. κ·Έλ μ§ μμ κ²½μ° μ λ¬Έμ λ₯Ό μ¬μμμ€. κ°μ¬ ν΄μ!
κ°μ₯ μ μ©ν λκΈ
κ°μ λ¬Έμ κ°μμμ΅λλ€. μ§κΈκΉμ§ λ΄κ° λ΄ μμ μ λ‘κ·Έμμ μΆμΈ‘ ν μ, κ·Έκ²μμ²λΌ 보μ΄λ
tensorflow
μ¬μ©gevent
, λΉμ μ μ¬μ©ν μ μμ΅λλ€gevent
μμ κ°μ μκ°μgunicorn
.--workers
λ°--threads
νλκ·Έλ μλ¬΄λ° μ°¨μ΄κ° μμ§λ§--worker-class=gevent
μμ--worker-class=gthread
νλ©΄ λ¬Έμ κ° ν΄κ²°λμμ΅λλ€. @javabrett κ°μ¬