Model serving: Triton, TorchServe, BentoML
- ⬜⚙️ Training pipelines: Airflow, Kubeflow, Prefect(MLOps — ML em produção)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Serving é engenharia, não afterthought
Um modelo vale zero até servir. Serving resolve três pressões em conflito: latência (p99 do SLA), throughput (requests por segundo por GPU) e custo (USD por 1k inferências). Boas escolhas de servidor, batching e hardware definem se o modelo é lucrativo ou não.
Triton Inference Server — config.pbtxt
# model_repository/churn_xgb/config.pbtxt
name: "churn_xgb"
platform: "onnxruntime_onnx"
max_batch_size: 128
input [
{ text: "features", data_type: TYPE_FP32, dims: [ 42 ] }
]
output [
{ text: "probability", data_type: TYPE_FP32, dims: [ 1 ] }
]
dynamic_batching {
preferred_batch_size: [ 16, 64 ]
max_queue_delay_microseconds: 5000
}
instance_group [
{ count: 2, kind: KIND_GPU, gpus: [ 0 ] }
]
version_policy { specific: { versions: [ 7 ] } }max_queue_delay_microseconds é o contrato com o SLA de p99: quanto mais espera, melhor o batching, mas pior a cauda de latência.
Cliente HTTP mínimo
import numpy as np
import tritonclient.http as httpclient
client = httpclient.InferenceServerClient("triton.ffv.internal:8000")
features = np.array([[0.1, 0.2, 0.3] + [0.0] * 39], dtype=np.float32)
inp = httpclient.InferInput("features", features.shape, "FP32")
inp.set_data_from_numpy(features)
res = client.infer(model_name="churn_xgb", inputs=[inp])
proba = res.as_numpy("probability")
print(proba)Por que servir modelo é engenharia, e não etapa final trivial?
BentoML — quando DX Python pesa
import bentoml
from bentoml.io import JSON
churn_runner = bentoml.sklearn.get("ffv-churn:latest").to_runner()
svc = bentoml.Service("ffv-churn-api", runners=[churn_runner])
@svc.api(input=JSON(), output=JSON())
async def predict(payload: dict) -> dict:
# validacao, feature enrichment, feature store lookup, etc.
vec = build_feature_vector(payload["cliente_id"])
proba = await churn_runner.async_run([vec])
return {"score": float(proba[0]), "model_version": "7"}Ensemble em Triton — DAG server-side
# model_repository/text_pipeline/config.pbtxt
name: "text_pipeline"
platform: "ensemble"
max_batch_size: 32
input [ { text: "raw_text", data_type: TYPE_STRING, dims: [ 1 ] } ]
output [ { text: "label", data_type: TYPE_FP32, dims: [ 3 ] } ]
ensemble_scheduling {
step [
{ model_name: "tokenizer", model_version: -1,
input_map: { key: "text", value: "raw_text" },
output_map: { key: "ids", value: "token_ids" } },
{ model_name: "encoder", model_version: -1,
input_map: { key: "ids", value: "token_ids" },
output_map: { key: "emb", value: "embedding" } },
{ model_name: "classifier", model_version: -1,
input_map: { key: "emb", value: "embedding" },
output_map: { key: "out", value: "label" } }
]
}- → dado cru
- → resposta pronta
- → agrupa
- → paraleliza
- → carrega versão
- Compute
- Conceito de arquitetura
Repare que o cliente conversa com um único nó, e tudo o que decide desempenho — lote, concorrência, versão — está do lado do servidor. É essa fronteira que transforma "o modelo está num contêiner" em serving de verdade.
- 1 · O cliente manda o dado cru. Isso é decisão de projeto, não conveniência: se cada cliente normalizar por conta própria, um deles vai divergir do treino e a acurácia cai só naquele caminho — o defeito mais difícil de achar em serving.
- 2 · O grafo roda inteiro no servidor. Pré, modelo e pós são um único destino para quem chama. Uma ida e volta de rede em vez de três, e a versão do pré-processamento anda junto da versão do modelo.
- 3 · O lote dinâmico é o que paga a placa. Requisições que chegam em poucos milissegundos umas das outras são agrupadas numa passagem só. É de onde vem a maior parte do ganho de vazão — e o custo é alguns milissegundos de espera proposital.
- 4 · Instâncias concorrentes preenchem o resto. Enquanto uma cópia do modelo espera dados, outra calcula. Ajustar esse número é o que separa uma placa a 30% de utilização de uma a 80%.
- 5 · Duas versões convivem no mesmo servidor. O repositório mantém as versões lado a lado, o que torna o experimento controlado e a volta atrás uma mudança de configuração — não um novo deploy sob pressão.
Para pipeline com 3 modelos, ensemble reduz latência em 30-50% vs orquestrar os três pelo cliente via rede.
Perguntas frequentes
❓ Como escolher servidor de inferência?
❓ Formação de lote dinâmica ajuda sempre?
❓ CPU ou GPU para servir?
Fixando
O que o agrupamento dinâmico de requisições no servidor faz?
Quando montar um encadeamento de modelos no próprio servidor, em vez de na aplicação?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…