> For the complete documentation index, see [llms.txt](https://docs.clore.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.clore.ai/guides/guides_v2-fr/mlops-et-deploiement/bentoml.md).

# BentoML

**BentoML** est un framework moderne et open source pour **créer, 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'empaqueter n'importe quel modèle de n'importe quel framework dans un service d'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 d'IA économique.

***

## Qu'est-ce que BentoML ?

BentoML facilite la prise d’un modèle entraîné et sa transformation en service d’API évolutif :

* **Indépendant du framework :** PyTorch, TensorFlow, JAX, scikit-learn, Hugging Face, XGBoost, LightGBM, et plus encore
* **Bento :** Un artefact autonome et reproductible (modèle + code + dépendances)
* **Runner :** Unité d'inférence de modèle évolutive avec micro-lots automatiques
* **Service :** Définition de service HTTP/gRPC de type FastAPI
* **BentoCloud :** Plateforme de déploiement gérée optionnelle
* **Priorité à Docker :** Chaque Bento peut être conteneurisé en une seule commande

**Fonctionnalités clés :**

* Micro-batching adaptatif pour optimiser le débit
* Validation intégrée des entrées/sorties 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      | Toute carte NVIDIA | RTX 4090 / A100 |
| RAM      | 8 Go               | 16 Go           |
| Stockage | 20 Go              | 40 Go           |
| Python   | 3.9+               | 3.11+           |

***

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

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

***

## Étape 2 — Dockerfile

BentoML ne dispose pas d'une image Docker GPU officielle, nous en construisons donc une :

```dockerfile
FROM pytorch/pytorch:2.11.0-cuda12.8-cudnn9-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 propre compte Docker Hub (remplacez `YOUR_DOCKERHUB_USERNAME` par votre nom d'utilisateur réel) :

```bash
docker build -t YOUR_DOCKERHUB_USERNAME/bentoml-gpu:latest .
docker push YOUR_DOCKERHUB_USERNAME/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 sont destinées au service de Bentos préemballés et n'incluent pas la prise en charge de CUDA. Construisez l'image à partir du Dockerfile ci-dessus pour des déploiements avec GPU sur Clore.ai.
{% endhint %}

***

## Étape 3 — Connectez-vous 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:
    """Classifie 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` L'indicateur permet le rechargement à chaud pendant le développement. Supprimez-le en production pour plus de stabilité.
{% endhint %}

***

## Étape 5 — Accéder au service

Ouvrez l'interface Swagger UI 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 de 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:
    """Classifie 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
```

Testez 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 de streaming LLM

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"Modèle enregistré : {saved_model.tag}")
```

```bash
python /workspace/build_bento.py

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

# Construire un Bento (nécessite 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

# Conteneuriser
bentoml containerize image_classifier_service:latest \
    --image-tag YOUR_DOCKERHUB_USERNAME/my-bento:latest
```

***

## Surveillance et métriques

BentoML expose les 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 l'envoi
)
```

***

## Dépannage

### Le service ne démarre pas

```
ERREUR - Échec de l'initialisation du Runner
```

**Solutions :**

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

### Port 3000 inaccessible

```bash
# Assurez-vous que le service se lie à 0.0.0.0 (et non à localhost)
bentoml serve service:svc --host 0.0.0.0 --port 3000
```

### Latence élevée lors de 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 à un point de terminaison de warm-up après le démarrage :

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

### Erreurs d'importation

```
ModuleNotFoundError: Aucun module nommé 'transformers'
```

**Solution :**

```bash
pip install transformers accelerate
```

***

## Recommandations GPU Clore.ai

BentoML est un framework de service — 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 du LLM (7B Q4) | Diffusion (SDXL) | Vision (ResNet50) |
| ---------- | ----- | ----------------------------------------- | -------------------- | ---------------- | ----------------- |
| RTX 3090   | 24 Go | 0,07–0,21 $/h                             | \~80 tok/s           | \~4 img/min      | \~400 req/s       |
| RTX 4090   | 24 Go | 0,14–0,42 $/h                             | \~140 tok/s          | \~8 img/min      | \~700 req/s       |
| A100 40 Go | 40 Go | [bare metal](https://clore.ai/bare-metal) | \~110 tok/s          | \~6 img/min      | \~1200 req/s      |
| A100 80 Go | 80 Go | [bare metal](https://clore.ai/bare-metal) | \~130 tok/s          | \~7 img/min      | \~1400 req/s      |

**Conseils d'utilisation :**

* **Service d'API LLM (7B–13B) :** RTX 3090 (0,07–0,21 $/h) — rapport qualité-prix optimal
* **API de génération d'images :** RTX 3090 ou RTX 4090 selon les besoins en débit
* **Grands modèles (34B–70B Q4) :** A100 40 Go ([bare metal](https://clore.ai/bare-metal)) — tient largement
* **Service multi-modèles en production :** A100 80 Go pour une marge mémoire

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

***

## Ressources utiles

* [Documentation officielle de BentoML](https://docs.bentoml.com)
* [GitHub BentoML](https://github.com/bentoml/BentoML)
* [Exemples BentoML](https://github.com/bentoml/BentoML/tree/main/examples)
* [Communauté Discord BentoML](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
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
