# BentoML

**BentoML** est un framework moderne et open-source pour **construire, déployer et faire évoluer des applications d'IA**. Il comble le fossé entre l'expérimentation en ML et le déploiement en production, vous permettant d'emballer n'importe quel modèle issu de n'importe quel framework en un service API prêt pour la production en quelques minutes. Exécutez BentoML sur le cloud GPU de Clore.ai pour un hébergement d'applications IA rentable.

***

## Qu'est-ce que BentoML ?

BentoML facilite la transformation d'un modèle entraîné en un service API évolutif :

* **Agnostique au framework :** PyTorch, TensorFlow, JAX, scikit-learn, HuggingFace, XGBoost, LightGBM, et plus encore
* **Bento :** Un artefact autosuffisant et reproductible (modèle + code + dépendances)
* **Runner :** Unité d'inférence de modèle évolutive avec mise en lot automatique
* **Service :** Définition de service HTTP/gRPC de type FastAPI
* **BentoCloud :** Plateforme de déploiement gérée optionnelle
* **Docker en priorité :** Chaque Bento peut être containerisé avec une seule commande

**Principales fonctionnalités :**

* Micro-batching adaptatif pour l'optimisation du débit
* Validation entrée/sortie intégrée avec Pydantic
* Spécification OpenAPI générée automatiquement
* Métriques Prometheus intégrées
* Prise en charge des réponses en streaming (LLMs)

***

## Prérequis

| Exigence | Minimum               | Recommandé      |
| -------- | --------------------- | --------------- |
| VRAM GPU | 8 Go                  | 16–24 Go        |
| GPU      | N'importe quel NVIDIA | RTX 4090 / A100 |
| RAM      | 8 Go                  | 16 Go           |
| Stockage | 20 Go                 | 40 Go           |
| Python   | 3.9+                  | 3.11+           |

***

## Étape 1 — Louez un GPU sur Clore.ai

1. Connectez-vous à [clore.ai](https://clore.ai).
2. Cliquez **Place de marché** et choisissez une instance GPU avec ≥ 16 Go de VRAM.
3. Définir l'image Docker : nous utiliserons une build personnalisée (voir l'étape 2).
4. Définir les ports ouverts : `22` (SSH) et `3000` (service BentoML).
5. Cliquez **Louez**.

***

## Étape 2 — Dockerfile

BentoML n'a pas d'image Docker GPU officielle, donc nous en construisons une :

```dockerfile
FROM pytorch/pytorch:2.1.2-cuda12.1-cudnn8-runtime

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    git wget curl \
    openssh-server \
    libgl1 libglib2.0-0 \
    && rm -rf /var/lib/apt/lists/*

# Configurer SSH
RUN mkdir /var/run/sshd && \
    echo 'root:clore123' | chpasswd && \
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# Installer BentoML et les bibliothèques ML courantes
RUN pip install --upgrade pip && \
    pip install \
        bentoml \
        transformers \
        accelerate \
        diffusers \
        Pillow \
        numpy \
        scipy \
        tritonclient[all]

WORKDIR /workspace

EXPOSE 22 3000

CMD service ssh start && tail -f /dev/null
```

### Construire et pousser

Construisez l'image et poussez-la vers votre compte Docker Hub (remplacez `VOTRE_NOM_UTILISATEUR_DOCKERHUB` par votre nom d'utilisateur réel) :

```bash
docker build -t VOTRE_NOM_UTILISATEUR_DOCKERHUB/bentoml-gpu:latest .
docker push VOTRE_NOM_UTILISATEUR_DOCKERHUB/bentoml-gpu:latest
```

{% hint style="info" %}
BentoML ne fournit pas d'image Docker GPU officielle sur Docker Hub. Les `bentoml/bento-server` images sur Docker Hub servent à déployer des Bentos pré-packagés et n'incluent pas le support CUDA. Construisez l'image à partir du Dockerfile ci‑dessus pour des déploiements compatibles GPU sur Clore.ai.
{% endhint %}

***

## Étape 3 — Connexion via SSH

```bash
ssh root@<clore-host> -p <assigned-ssh-port>
```

Vérifier BentoML :

```bash
bentoml --version
# Attendu : bentoml, version 1.x.x
```

***

## Étape 4 — Votre premier service BentoML

### Classificateur de texte simple

Créez un fichier de service :

```bash
mkdir -p /workspace/my-service
cat > /workspace/my-service/service.py << 'EOF'
import bentoml
from bentoml.io import JSON, Text
import numpy as np

# Définir un Runner (l'unité de modèle)
class TextClassifierRunnable(bentoml.Runnable):
    SUPPORTED_RESOURCES = ("gpu", "cpu")
    SUPPORTS_CPU_MULTI_THREADING = True
    
    def __init__(self):
        import torch
        from transformers import pipeline
        
        self.classifier = pipeline(
            "text-classification",
            model="distilbert-base-uncased-finetuned-sst-2-english",
            device=0 if torch.cuda.is_available() else -1,
        )
    
    @bentoml.Runnable.method(batchable=True, batch_dim=0)
    def classify(self, texts: list[str]) -> list[dict]:
        results = self.classifier(texts)
        return results

# Créer le Runner
classifier_runner = bentoml.Runner(
    TextClassifierRunnable,
    name="text_classifier",
    max_batch_size=32,
    max_latency_ms=100,
)

# Définir le Service
svc = bentoml.Service(
    name="text_classifier_service",
    runners=[classifier_runner],
)

@svc.api(input=Text(), output=JSON())
async def classify(text: str) -> dict:
    """Classer le sentiment du texte d'entrée."""
    results = await classifier_runner.classify.async_run([text])
    return results[0]
EOF
```

### Démarrer le service

```bash
cd /workspace/my-service

bentoml serve service:svc \
    --host 0.0.0.0 \
    --port 3000 \
    --reload
```

{% hint style="info" %}
Le `--reload` le drapeau active le rechargement à chaud pendant le développement. Supprimez-le en production pour la stabilité.
{% endhint %}

***

## Étape 5 — Accéder au service

Ouvrez l'interface Swagger générée automatiquement :

```
http://<clore-host>:<public-port-3000>
```

Ou testez via `curl`:

```bash
curl -X POST http://<clore-host>:<public-port-3000>/classify \
    -H "Content-Type: text/plain" \
    -d "This GPU cloud service is amazing!"
```

Réponse attendue :

```json
{"label": "POSITIVE", "score": 0.9986}
```

***

## Étape 6 — Service de classification d'images

### Service de modèle vision

```python
# /workspace/vision-service/service.py
import bentoml
from bentoml.io import Image, JSON
from PIL import Image as PILImage
import numpy as np

class ImageClassifierRunnable(bentoml.Runnable):
    SUPPORTED_RESOURCES = ("gpu",)
    SUPPORTS_CPU_MULTI_THREADING = False
    
    def __init__(self):
        import torch
        import torchvision.transforms as transforms
        from torchvision.models import resnet50, ResNet50_Weights
        
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        weights = ResNet50_Weights.DEFAULT
        self.model = resnet50(weights=weights).to(self.device)
        self.model.eval()
        self.preprocess = weights.transforms()
        self.categories = weights.meta["categories"]
    
    @bentoml.Runnable.method(batchable=True, batch_dim=0)
    def predict(self, images: list) -> list[dict]:
        import torch
        
        batch = torch.stack([self.preprocess(img) for img in images]).to(self.device)
        
        with torch.no_grad():
            predictions = self.model(batch).softmax(dim=1)
        
        results = []
        for pred in predictions:
            top5 = pred.topk(5)
            results.append({
                "predictions": [
                    {"label": self.categories[idx], "score": round(score.item(), 4)}
                    for score, idx in zip(top5.values, top5.indices)
                ]
            })
        return results


image_runner = bentoml.Runner(
    ImageClassifierRunnable,
    name="image_classifier",
    max_batch_size=16,
)

svc = bentoml.Service(
    name="image_classifier_service",
    runners=[image_runner],
)

@svc.api(input=Image(), output=JSON())
async def classify(image: PILImage.Image) -> dict:
    """Classer une image avec ResNet50."""
    results = await image_runner.predict.async_run([image])
    return results[0]
```

```bash
bentoml serve service:svc --host 0.0.0.0 --port 3000
```

Tester avec une image :

```bash
curl -X POST http://<clore-host>:<public-port-3000>/classify \
    -H "Content-Type: image/jpeg" \
    --data-binary @/path/to/image.jpg
```

***

## Étape 7 — Service LLM en streaming

Pour les modèles de langage avec réponses en streaming :

```python
# /workspace/llm-service/service.py
import bentoml
from bentoml.io import JSON, Text
from typing import AsyncGenerator

class LLMRunnable(bentoml.Runnable):
    SUPPORTED_RESOURCES = ("gpu",)
    SUPPORTS_CPU_MULTI_THREADING = False
    
    def __init__(self):
        from transformers import AutoModelForCausalLM, AutoTokenizer
        import torch
        
        model_name = "microsoft/phi-2"
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model = AutoModelForCausalLM.from_pretrained(
            model_name,
            torch_dtype=torch.float16,
            device_map="auto"
        )
    
    @bentoml.Runnable.method(batchable=False)
    def generate(self, prompt: str, max_tokens: int = 200) -> str:
        import torch
        
        inputs = self.tokenizer(prompt, return_tensors="pt").to("cuda")
        
        with torch.no_grad():
            outputs = self.model.generate(
                **inputs,
                max_new_tokens=max_tokens,
                do_sample=True,
                temperature=0.7,
                pad_token_id=self.tokenizer.eos_token_id,
            )
        
        return self.tokenizer.decode(outputs[0], skip_special_tokens=True)


llm_runner = bentoml.Runner(LLMRunnable, name="llm")

svc = bentoml.Service("llm_service", runners=[llm_runner])

@svc.api(input=JSON(), output=Text())
async def generate(body: dict) -> str:
    prompt = body.get("prompt", "")
    max_tokens = body.get("max_tokens", 200)
    return await llm_runner.generate.async_run(prompt, max_tokens)
```

***

## Étape 8 — Enregistrer et construire un Bento

Un **Bento** est un artefact empaqueté et reproductible :

```python
# /workspace/build_bento.py
import bentoml

# Enregistrer le modèle dans le magasin de modèles BentoML
import torch
from torchvision.models import resnet50, ResNet50_Weights

model = resnet50(weights=ResNet50_Weights.DEFAULT)
model.eval()

saved_model = bentoml.pytorch.save_model(
    name="resnet50",
    model=model,
    labels={"framework": "pytorch", "task": "image-classification"},
    metadata={"accuracy": 0.80, "dataset": "ImageNet"}
)
print(f"Model saved: {saved_model.tag}")
```

```bash
python /workspace/build_bento.py

# Lister les modèles sauvegardés
bentoml models list

# Construire un Bento (requiert bentofile.yaml)
bentoml build
```

### bentofile.yaml

```yaml
service: "service:svc"
labels:
  owner: "ml-team"
  stage: "production"
include:
  - "*.py"
python:
  packages:
    - torch
    - torchvision
    - transformers
    - Pillow
    - numpy
docker:
  python_version: "3.11"
  cuda_version: "12.1"
  system_packages:
    - libgl1
```

```bash
bentoml build

# Lister les bentos construits
bentoml list

# Containeriser
bentoml containerize image_classifier_service:latest \
    --image-tag VOTRE_NOM_UTILISATEUR_DOCKERHUB/my-bento:latest
```

***

## Surveillance et métriques

BentoML expose des métriques Prometheus à `/metrics`:

```bash
curl http://<clore-host>:<public-port-3000>/metrics
```

Métriques clés :

```
# Taux de requêtes
bentoml_service_request_total{endpoint="classify", http_status_code="200"}
# Latence
bentoml_service_request_duration_seconds{endpoint="classify"}
# Débit du Runner  
bentoml_runner_request_total{runner_name="image_classifier"}
```

***

## Configuration du batching adaptatif

```python
# Ajuster finement le comportement du batching
image_runner = bentoml.Runner(
    ImageClassifierRunnable,
    name="image_classifier",
    max_batch_size=64,          # Nombre max de requêtes par lot
    max_latency_ms=50,          # Temps d'attente max avant envoi
)
```

***

## Dépannage

### Le service ne démarre pas

```
ERROR - Échec de l'initialisation du runner
```

**Solutions :**

* Vérifier la disponibilité de CUDA : `python -c "import torch; print(torch.cuda.is_available())"`
* Vérifier la VRAM GPU : `nvidia-smi`
* Vérifier que le téléchargement du modèle est terminé (recherchez la progression du téléchargement dans les logs)

### Le port 3000 n'est pas accessible

```bash
# Assurez-vous que le service écoute sur 0.0.0.0 (et non localhost)
bentoml serve service:svc --host 0.0.0.0 --port 3000
```

### Latence élevée sur la première requête

C'est normal — la première requête déclenche le chargement du modèle (warm-up). Toutes les requêtes suivantes seront rapides. Ajoutez un appel d'endpoint de warm‑up après le démarrage :

```bash
# Échauffer après le démarrage
sleep 10 && curl -s -o /dev/null http://localhost:3000/healthz
```

### Erreurs d'importation

```
ModuleNotFoundError: No module named 'transformers'
```

**Solution :**

```bash
pip install transformers accelerate
```

***

## Recommandations GPU Clore.ai

BentoML est un framework de serving — les exigences GPU dépendent entièrement du modèle que vous déployez. Voici à quoi vous attendre pour les charges de travail courantes :

| GPU       | VRAM  | Prix Clore.ai | Débit LLM (7B Q4) | Diffusion (SDXL) | Vision (ResNet50) |
| --------- | ----- | ------------- | ----------------- | ---------------- | ----------------- |
| RTX 3090  | 24 Go | \~0,12 $/h    | \~80 tok/s        | \~4 img/min      | \~400 req/s       |
| RTX 4090  | 24 Go | \~0,70 $/h    | \~140 tok/s       | \~8 img/min      | \~700 req/s       |
| A100 40GB | 40 Go | \~1,20 $/h    | \~110 tok/s       | \~6 img/min      | \~1200 req/s      |
| A100 80GB | 80 Go | \~2,00 $/h    | \~130 tok/s       | \~7 img/min      | \~1400 req/s      |

**Conseils d'utilisation :**

* **Service API LLM (7B–13B) :** RTX 3090 (\~0,12 $/h) — rapport prix‑performance optimal
* **API de génération d'images :** RTX 3090 ou RTX 4090 selon les besoins de débit
* **Grands modèles (34B–70B Q4) :** A100 40GB (\~1,20 $/h) — convient confortablement
* **Service multi‑modèles en production :** A100 80GB pour marge mémoire

{% hint style="info" %}
Le **micro-batching adaptatif** de BentoML est particulièrement efficace sur les A100 — le planificateur matériel gère le batching efficacement, extrayant plus de débit par dollar qu'un service naïf par requête unique. Pour des API à fort trafic, l'A100 40GB offre souvent un meilleur ROI que deux RTX 4090.
{% endhint %}

***

## Ressources utiles

* [Documentation officielle de BentoML](https://docs.bentoml.com)
* [BentoML GitHub](https://github.com/bentoml/BentoML)
* [Exemples BentoML](https://github.com/bentoml/BentoML/tree/main/examples)
* [Communauté BentoML sur Discord](https://l.bentoml.com/join-slack-space)
* [Galerie BentoML](https://www.bentoml.com/gallery)
* [Démarrage rapide : servir des LLMs](https://docs.bentoml.com/en/latest/get-started/quickstart.html)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-fr/mlops-et-deploiement/bentoml.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
