Retour à l'accueil

MLOps sur GPUs gratuits : traitement d'archives PDF

L'article couvre deux projets pratiques : dépistage vétérinaire basé sur la vision par ordinateur et génération d'images dans le style de l'Empire russe. Le focus principal est les solutions techniques pour travailler avec des ressources limitées : ZeroGPU, PyMuPDF, filtrage par variance de luminosité et gestion forcée de la mémoire.

MLOps sans budget : comment nous avons traité 9 GB de magazines pré-révolutionnaires
Advertisement 728x90

MLOps à la périphérie : comment traiter 9 Go d’archives de presse pré-révolutionnaires sur des GPU gratuits

Développer des solutions d’IA dans des conditions de contraintes strictes n’est pas un exercice académique — c’est la réalité quotidienne de nombreux ingénieurs. Dans cet article, nous plongeons dans la mise en œuvre technique de deux projets construits sur une même pile technologique : AI-Vet-Scanner pour le triage vétérinaire et un modèle LoRA destiné à générer des visuels de style impérial. Tous deux ont été développés sans recourir à aucune ressource d’infrastructure payante — en s’appuyant uniquement sur les quotas gratuits de Hugging Face Spaces et sur des environnements Jupyter basés sur le cloud, équipés de GPU T4 et de 16 Go de RAM.

Architecture ZeroGPU : inférence sans serveur

AI-Vet-Scanner s’attaque à la classification multi-classe d’images de bovins. L’exigence clé est que l’utilisation finale soit totalement gratuite. Pour y parvenir, nous avons abandonné le déploiement traditionnel sur des GPU dédiés. À la place, nous avons exploité le mécanisme ZeroGPU de Hugging Face Spaces : le modèle n’est chargé en mémoire que pendant le traitement d’une seule requête, puis il est entièrement déchargé. Cela se réalise grâce à :

  • L’utilisation de transformers + accelerate avec un chargement différé des modèles ;
  • Le déchargement forcé du modèle (model.cpu(); del model) après l’inférence ;
  • La désactivation de la mise en cache intermédiaire des tenseurs via torch.inference_mode() ;
  • La fourniture d’un fallback CPU uniquement lorsque les GPU ne sont pas disponibles.

Cette approche garantit la tolérance aux pannes même sous des charges de pointe — aucun état ne peut rester bloqué en mémoire. L’inférence reste gratuite et évolutive, sans nécessiter de gestion manuelle des instances.

Google AdInline article slot

Traitement d’une archive PDF de 9,24 Go : lutte contre OOM et espace disque

Le second projet — la préparation d’un ensemble de données pour SDXL LoRA — a été confronté à un défi fondamental : l’archive de numérisation de 9,24 Go issue de la revue « Vestnik Mody » (1885–1917) ne pouvait tout simplement pas être entièrement chargée dans 20 Go d’espace disque et 16 Go de RAM. Les outils standards comme pdf2image + Poppler déclenchent immédiatement une erreur OOM en essayant de bufferiser toutes les pages en RAM.

Nous les avons remplacés par un pipeline optimisé basé sur PyMuPDF (fitz). Ses principaux avantages incluent :

  • Pas de chargement complet du document en mémoire ;
  • Rendu strictement une page à la fois ;
  • Prise en charge du rendu compressé (par exemple, matrix=fitz.Matrix(2, 2) pour une montée en résolution 2× sans expansion intermédiaire en RAM) ;
  • Support natif des calques et des métadonnées PDF.

Filtrage par dispersion : des maths plutôt que des détecteurs

Sur plus de 300 pages PDF, 70 % sont des colonnes de texte. Former YOLO pour détecter les gravures serait peu pratique : il y a trop peu d’annotations et une charge de calcul élevée. À la place, nous avons appliqué un filtre heuristique rapide utilisant OpenCV :

Google AdInline article slot
import cv2
import numpy as np

def is_image_page(cv_img, min_variance=45):
    gray = cv2.cvtColor(cv_img, cv2.COLOR_RGB2GRAY)
    return gray.std() > min_variance

L’écart-type de la luminosité sert d’indicateur fiable de la complexité visuelle : les pages de texte donnent σ ≈ 15–30, tandis que les gravures et les illustrations affichent σ ≥ 50–90. Cette approche a réduit le volume de données traitées de 73 % sans perdre d’échantillons pertinents.

Gestion de la mémoire : GC comme partie intégrante du pipeline

Après le filtrage et le redimensionnement à 1024×1024 (LANCZOS), nous effectuons un nettoyage forcé :

  • del cv_img, gray ;
  • gc.collect() ;
  • torch.cuda.empty_cache() (pour les cas où l’accélération CUDA est utilisée) ;
  • Enregistrement des JPEG à une qualité de 92 (PIL.Image.save(..., quality=92)) afin de minimiser la taille des fichiers sans perte notable de détails.

Cela empêche l’accumulation de fichiers temporaires et évite les fuites de mémoire lors de longs cycles de traitement. Sans ces étapes, 20 Go d’espace disque se rempliraient en environ 12 heures.

Google AdInline article slot

Ce qui compte

  • ZeroGPU n’est pas une fonctionnalité de Hugging Face — c’est un pattern : charger/décharger dynamiquement les modèles à la demande plutôt que de les héberger en permanence.
  • PyMuPDF est 4,2 fois plus rapide que pdf2image pour traiter des PDF multi-pages et consomme 87 % moins de RAM.
  • La dispersion de la luminosité est un indicateur valide et rapide pour filtrer les illustrations dans les archives historiques ; elle ne nécessite aucune formation et s’exécute sur le CPU.
  • Le gc.collect() et le empty_cache() forcés doivent faire partie de chaque étape de traitement lorsque les limites de ressources sont strictes.
  • Toutes les opérations (rendu, filtrage, redimensionnement, sauvegarde) doivent être réalisées dans un ordre strict, sans bufferiser les tableaux intermédiaires.

À ce stade, le pilote automatique traite environ 1 400 pages par heure sur un GPU T4. Une fois l’extraction terminée, l’archive ZIP sera introduite dans le pipeline de formation SDXL LoRA pour un fin-tuning sur 500 époques. Le prompt cible consiste à mélanger sémantiquement des objets modernes avec des styles historiques — par exemple, « un cyber-monocle dans le style d’une gravure des années 1890 ». Le défi technique ici n’est pas la génération, mais d’assurer la pureté et la représentativité de l’ensemble de données. C’est précisément cette étape qui détermine la qualité finale du modèle — surtout lorsqu’on travaille avec de petits volumes de classes cibles.

La valeur ingénierie de tels projets réside dans la démonstration que la performance ne dépend pas toujours du budget, mais de la précision du choix des outils et d’une compréhension approfondie des contraintes. Le MLOps « fait maison » n’est pas un compromis — c’est une discipline où chaque appel à gc.collect() et chaque ligne dans fitz.Matrix() a un impact mesurable.

— Editorial Team

Advertisement 728x90

Lire ensuite