# Entraînement ML avec Jupyter

Configurer JupyterLab avec prise en charge GPU pour des expériences d'apprentissage automatique et l'entraînement de modèles.

{% hint style="success" %}
Tous les exemples peuvent être exécutés sur des serveurs GPU loués via [CLORE.AI Marketplace](https://clore.ai/marketplace).
{% endhint %}

## Exigences du serveur

| Paramètre          | Minimum     | Recommandé |
| ------------------ | ----------- | ---------- |
| RAM                | 16Go        | 32 Go+     |
| VRAM               | 8 Go        | 16 Go+     |
| Réseau             | 200 Mbps    | 500 Mbps+  |
| Temps de démarrage | 2-3 minutes | -          |

{% hint style="info" %}
JupyterLab lui-même est léger. Choisissez le GPU et la RAM en fonction des exigences de votre charge de travail d'entraînement.
{% endhint %}

## Déploiement rapide

**Image Docker :**

```
pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime
```

**Ports :**

```
22/tcp
8888/http
6006/http
```

**Environnement :**

```
JUPYTER_TOKEN=votre_token_sécurisé_ici
```

**Commande :**

```bash
pip install jupyterlab tensorboard && \
jupyter lab --ip=0.0.0.0 --port=8888 --allow-root --NotebookApp.token='votre_token_sécurisé_ici'
```

## Accéder à votre service

Après le déploiement, trouvez votre `http_pub` URL dans **Mes commandes**:

1. Aller à la **Mes commandes** page
2. Cliquez sur votre commande
3. Trouvez l' `http_pub` URL (par ex., `abc123.clorecloud.net`)

Utilisez `https://VOTRE_HTTP_PUB_URL` au lieu de `localhost` dans les exemples ci-dessous.

### Vérifiez que cela fonctionne

```bash
# Vérifier si JupyterLab est accessible
curl https://your-http-pub.clorecloud.net/

# Accéder avec le token
# https://your-http-pub.clorecloud.net/?token=votre_token_sécurisé_ici
```

{% hint style="warning" %}
Si vous obtenez HTTP 502, attendez 2-3 minutes - le service installe les dépendances.
{% endhint %}

## Location sur CLORE.AI

1. Visitez [CLORE.AI Marketplace](https://clore.ai/marketplace)
2. Filtrer par type de GPU, VRAM et prix
3. Choisir **À la demande** (tarif fixe) ou **Spot** (prix d'enchère)
4. Configurez votre commande :
   * Sélectionnez l'image Docker
   * Définissez les ports (TCP pour SSH, HTTP pour les interfaces web)
   * Ajoutez des variables d'environnement si nécessaire
   * Entrez la commande de démarrage
5. Sélectionnez le paiement : **CLORE**, **BTC**, ou **USDT/USDC**
6. Créez la commande et attendez le déploiement

### Accédez à votre serveur

* Trouvez les détails de connexion dans **Mes commandes**
* Interfaces Web : utilisez l'URL du port HTTP
* SSH : `ssh -p <port> root@<adresse-proxy>`

## Accéder à Jupyter

1. Attendre le déploiement
2. Trouver le mappage du port 8888
3. Ouvrir : `http://<proxy>:<port>?token=votre_token_sécurisé_ici`

## Image ML préconfigurée

Pour un environnement ML complet :

**Image :**

```
jupyter/pytorch-notebook:cuda12-pytorch-2.1.0
```

Ou construire personnalisé :

```dockerfile
FROM pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime

RUN pip install --no-cache-dir \
    jupyterlab \
    numpy pandas matplotlib seaborn \
    scikit-learn \
    transformers datasets accelerate \
    tensorboard wandb \
    opencv-python pillow \
    tqdm rich

EXPOSE 8888 6006

CMD ["jupyter", "lab", "--ip=0.0.0.0", "--allow-root"]
```

## Bibliothèques essentielles

### Installer dans Jupyter

```python
!pip install transformers datasets accelerate bitsandbytes
!pip install wandb tensorboard
!pip install scikit-learn xgboost lightgbm
!pip install opencv-python albumentations
```

### Créer requirements.txt

```

# Frameworks ML
torch>=2.1.0
torchvision
torchaudio

# NLP
transformers>=4.36.0
datasets
tokenizers
sentencepiece

# Entraînement
accelerate
bitsandbytes
peft
trl

# Monitoring
wandb
tensorboard

# Données
numpy
pandas
matplotlib
seaborn
scikit-learn

# Vision par ordinateur
opencv-python
pillow
albumentations
```

## Exemples d'entraînement

### Classification d'images avec PyTorch

```python
import torch
import torch.nn as nn
import torchvision
from torchvision import transforms
from torch.utils.data import DataLoader

# Vérifier le GPU
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"Mémoire: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB")

# Charger les données
transform = transforms.Compose([
    transforms.Resize(224),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

train_data = torchvision.datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform
)
train_loader = DataLoader(train_data, batch_size=64, shuffle=True, num_workers=4)

# Modèle
model = torchvision.models.resnet18(pretrained=True)
model.fc = nn.Linear(512, 10)
model = model.cuda()

# Entraînement
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
criterion = nn.CrossEntropyLoss()

for epoch in range(10):
    model.train()
    for images, labels in train_loader:
        images, labels = images.cuda(), labels.cuda()

        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    print(f"Époque {epoch+1}, Perte : {loss.item():.4f}")

# Sauvegarder le modèle
torch.save(model.state_dict(), 'model.pth')
```

### Classification de texte HuggingFace

```python
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import TrainingArguments, Trainer
from datasets import load_dataset
import numpy as np

# Charger le jeu de données
dataset = load_dataset("imdb")

# Charger le modèle
model_name = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

# Tokeniser
def tokenize(examples):
    return tokenizer(examples["text"], padding="max_length", truncation=True)

tokenized = dataset.map(tokenize, batched=True)

# Entraînement
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=3,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=64,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir="./logs",
    logging_steps=100,
    evaluation_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized["train"],
    eval_dataset=tokenized["test"],
)

trainer.train()
trainer.save_model("./best_model")
```

### Ajustement fin de LLM avec LoRA

```python
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from datasets import load_dataset
from trl import SFTTrainer
import torch

# Charger le modèle avec quantification 4 bits
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.float16,
)

model = AutoModelForCausalLM.from_pretrained(
    "mistralai/Mistral-7B-v0.1",
    quantization_config=bnb_config,
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")
tokenizer.pad_token = tokenizer.eos_token

# Configurer LoRA
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, lora_config)

# Charger le jeu de données
dataset = load_dataset("timdettmers/openassistant-guanaco")

# Entraîner
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset["train"],
    dataset_text_field="text",
    max_seq_length=512,
    tokenizer=tokenizer,
    args=TrainingArguments(
        output_dir="./lora_output",
        num_train_epochs=1,
        per_device_train_batch_size=4,
        gradient_accumulation_steps=4,
        learning_rate=2e-4,
        fp16=True,
        logging_steps=10,
        save_steps=100,
    ),
)

trainer.train()
trainer.save_model("./final_lora")
```

## Intégration TensorBoard

### Démarrer TensorBoard

```python
%load_ext tensorboard
%tensorboard --logdir ./logs --port 6006 --bind_all
```

Ou via le terminal :

```bash
tensorboard --logdir ./logs --port 6006 --bind_all &
```

### Enregistrer les métriques d'entraînement

```python
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter('./logs')

for epoch in range(epochs):
    # ... boucle d'entraînement ...
    writer.add_scalar('Loss/train', train_loss, epoch)
    writer.add_scalar('Loss/val', val_loss, epoch)
    writer.add_scalar('Accuracy/val', accuracy, epoch)

writer.close()
```

## Intégration Weights & Biases

```python
import wandb

wandb.init(project="my-project", name="experiment-1")

# Enregistrer les métriques
wandb.log({"loss": loss, "accuracy": acc})

# Enregistrer le modèle
wandb.save("model.pth")

# Terminer
wandb.finish()
```

## Gestion des données

### Télécharger des jeux de données

```python

# jeux de données HuggingFace
from datasets import load_dataset
dataset = load_dataset("squad")

# jeux de données Kaggle
!pip install kaggle
!kaggle datasets download -d username/dataset-name

# Téléchargement direct
!wget https://example.com/data.zip
!unzip data.zip
```

### Monter le stockage cloud

```python

# S3
!pip install boto3
import boto3
s3 = boto3.client('s3')
s3.download_file('bucket', 'key', 'local_path')

# Google Cloud
!pip install google-cloud-storage
from google.cloud import storage
client = storage.Client()
bucket = client.bucket('my-bucket')
blob = bucket.blob('data.zip')
blob.download_to_filename('data.zip')
```

## Sauvegarder le travail

### Enregistrer sur un stockage externe

```python

# Sauvegarder le modèle sur S3
import boto3
s3 = boto3.client('s3',
    aws_access_key_id='VOTRE_CLE',
    aws_secret_access_key='VOTRE_SECRET'
)
s3.upload_file('model.pth', 'my-bucket', 'models/model.pth')
```

### Avant de terminer la session

```bash

# Télécharger les fichiers importants
scp -P <port> root@<host>:/workspace/model.pth ./
scp -P <port> -r root@<host>:/workspace/results/ ./results/
```

## Entraînement multi-GPU

```python
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel

# Vérifier les GPUs
print(f"GPUs disponibles : {torch.cuda.device_count()}")

# DataParallel (simple)
model = nn.DataParallel(model)

# DistributedDataParallel (meilleur)

# Lancer avec : torchrun --nproc_per_node=4 train.py
dist.init_process_group("nccl")
model = DistributedDataParallel(model)
```

## Conseils de performance

### Optimisation de la mémoire

```python

# Checkpointing de gradient
model.gradient_checkpointing_enable()

# Précision mixte
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()

with autocast():
    output = model(input)
    loss = criterion(output, target)

scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
```

### Chargement des données

```python

# Chargement plus rapide des données
loader = DataLoader(
    dataset,
    batch_size=64,
    num_workers=8,      # Utiliser plusieurs workers
    pin_memory=True,    # Transfert GPU plus rapide
    prefetch_factor=2   # Précharger les lots
)
```

## Dépannage

## Estimation des coûts

Tarifs typiques du marché CLORE.AI (à partir de 2024) :

| GPU       | Tarif horaire | Tarif journalier | Session de 4 heures |
| --------- | ------------- | ---------------- | ------------------- |
| RTX 3060  | \~$0.03       | \~$0.70          | \~$0.12             |
| RTX 3090  | \~$0.06       | \~$1.50          | \~$0.25             |
| RTX 4090  | \~$0.10       | \~$2.30          | \~$0.40             |
| A100 40GB | \~$0.17       | \~$4.00          | \~$0.70             |
| A100 80GB | \~$0.25       | \~$6.00          | \~$1.00             |

*Les prix varient selon le fournisseur et la demande. Vérifiez* [*CLORE.AI Marketplace*](https://clore.ai/marketplace) *pour les tarifs actuels.*

**Économisez de l'argent :**

* Utilisez **Spot** market pour les charges de travail flexibles (souvent 30-50 % moins cher)
* Payer avec **CLORE** jetons
* Comparer les prix entre différents fournisseurs
