Intelligence Artificielle angularforall.com

- Hugging Face : utiliser les modèles IA open source

Hugging-Face Transformers Nlp Open-Source Transformers-Js Api-Inference Ia-Locale Modeles-Ia Machine-Learning Javascript Browser-Ai Bert Ia-Generative Node-Js
Hugging Face : utiliser les modèles IA open source

Explorez Hugging Face pour utiliser les modèles NLP open source : API Inference côté back-end et transformers.js pour faire tourner les modèles dans le navigateur.

L'écosystème Hugging Face

Hugging Face est la plateforme de référence pour les modèles de machine learning open source. Elle héberge plus de 700 000 modèles NLP, vision, audio et multimodaux — accessibles via API cloud, dans le navigateur avec Transformers.js, ou localement.

ComposantDescriptionUsage JS/TS
Model Hub500k+ modèles pré-entraînés (BERT, Mistral, Whisper…)Parcourir et télécharger
Inference APIAPI REST cloud — appel sans hébergementfetch() ou @huggingface/inference
Transformers.jsInférence WebAssembly/ONNX dans le navigateur@xenova/transformers
Inference EndpointsDéploiement serverless dédié, auto-scalingREST API sur URL custom
SpacesDémos Gradio/Streamlit hébergéesEmbed ou API
Datasets200k+ datasets pour l'entraînementDownload via CLI/API

API Inference hébergée — client avec retry

L'API Inference permet d'appeler n'importe quel modèle HF sans infrastructure. Clé gratuite sur huggingface.co/settings/tokens. Limite : ~1000 requêtes/jour gratuit. Pour la production : Inference Endpoints (payant).

// hf-client.ts — Client typé avec retry et warm-up
const HF_API_URL = 'https://api-inference.huggingface.co/models';

interface HFOptions {
    waitForModel?: boolean;  // attendre que le modèle charge (cold start)
    useCache?: boolean;      // utiliser le cache HF (défaut: true)
}

async function hfInference<T>(
    model: string,
    inputs: string | object,
    options: HFOptions = {},
    maxRetries = 3
): Promise<T> {
    const token = process.env.HF_API_TOKEN; // jamais côté client

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        const response = await fetch(`${HF_API_URL}/${model}`, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'X-Wait-For-Model': options.waitForModel ? 'true' : 'false',
                'X-Use-Cache': options.useCache === false ? 'false' : 'true',
            },
            body: JSON.stringify({ inputs }),
        });

        if (response.status === 503) {
            // Modèle en cours de chargement (cold start ~20-30s)
            const { estimated_time } = await response.json();
            if (attempt < maxRetries) {
                const waitMs = (estimated_time || 20) * 1000;
                console.log(`Modèle en chargement — attente ${waitMs/1000}s...`);
                await new Promise(r => setTimeout(r, waitMs));
                continue;
            }
        }

        if (response.status === 429) {
            // Rate limit — backoff exponentiel
            const delay = Math.pow(2, attempt) * 2000;
            await new Promise(r => setTimeout(r, delay));
            continue;
        }

        if (!response.ok) {
            const error = await response.json();
            throw new Error(`HF API Error ${response.status}: ${error.error}`);
        }

        return response.json() as Promise<T>;
    }

    throw new Error('Max retries atteint');
}

// Analyse de sentiment avec le SDK officiel
import { HfInference } from '@huggingface/inference';  // npm install @huggingface/inference

const hf = new HfInference(process.env.HF_API_TOKEN);

// Classification de texte
const sentiment = await hf.textClassification({
    model: 'cardiffnlp/twitter-roberta-base-sentiment-latest',
    inputs: 'Angular 17 est incroyable pour les SPAs !',
});
// [{ label: 'positive', score: 0.96 }]

// Génération de texte
const generated = await hf.textGeneration({
    model: 'mistralai/Mistral-7B-Instruct-v0.2',
    inputs: '<s>[INST] Explique les Signals Angular en 3 points [/INST]',
    parameters: { max_new_tokens: 300, temperature: 0.3 }
});

Transformers.js — inférence dans le navigateur

@xenova/transformers (maintenant @huggingface/transformers) exécute les modèles ONNX directement dans le navigateur via WebAssembly — zéro dépendance serveur, 100% privé.

npm install @xenova/transformers
# ou version officielle HF (2024) :
npm install @huggingface/transformers
// sentiment.service.ts — Service Angular avec transformers.js
import { Injectable, signal } from '@angular/core';
import { pipeline, Pipeline, env } from '@xenova/transformers';

// Configurer le cache des modèles
env.allowLocalModels = false;            // utiliser HF Hub uniquement
env.useBrowserCache = true;              // mise en cache IndexedDB

@Injectable({ providedIn: 'root' })
export class SentimentService {
    private pipelineInstance: Pipeline | null = null;
    readonly loading = signal(false);
    readonly modelLoaded = signal(false);
    readonly progress = signal(0);

    async loadModel(): Promise<void> {
        if (this.pipelineInstance) return; // déjà chargé

        this.loading.set(true);
        try {
            // Callback de progression pour l'UX
            const progressCallback = (data: { progress?: number; status: string }) => {
                if (data.progress) {
                    this.progress.set(Math.round(data.progress));
                }
            };

            // Modèle quantisé — 4× plus léger (82 Mo → 23 Mo)
            this.pipelineInstance = await pipeline(
                'sentiment-analysis',
                'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
                { quantized: true, progress_callback: progressCallback }
            );

            this.modelLoaded.set(true);
        } finally {
            this.loading.set(false);
            this.progress.set(100);
        }
    }

    async analyze(text: string): Promise<{ label: string; score: number }[]> {
        if (!this.pipelineInstance) await this.loadModel();
        return this.pipelineInstance!(text) as any;
    }
}

Tâches NLP — guide complet des pipelines

import { pipeline } from '@xenova/transformers';

// 1. Classification de texte / sentiment
const classifier = await pipeline(
    'text-classification',
    'Xenova/distilbert-base-uncased-finetuned-sst-2-english'
);
const result = await classifier('Angular Signals are amazing!');
// [{ label: 'POSITIVE', score: 0.9998 }]

// 2. Question Answering — extraction de réponse depuis un contexte
const qa = await pipeline('question-answering');
const answer = await qa({
    question: 'Qui a créé Angular ?',
    context: 'Angular est un framework TypeScript open-source créé par Google en 2016.'
});
// { answer: 'Google', start: 62, end: 68, score: 0.9987 }

// 3. Résumé automatique
const summarizer = await pipeline('summarization', 'Xenova/distilbart-cnn-6-6');
const summary = await summarizer(longText, { max_new_tokens: 150, min_new_tokens: 40 });
// [{ summary_text: '...' }]

// 4. Traduction multi-langues
const translator = await pipeline('translation', 'Xenova/opus-mt-fr-en');
const translated = await translator('Bonjour le monde !', { tgt_lang: 'en' });
// [{ translation_text: 'Hello World!' }]

// 5. Génération de texte (GPT-style)
const generator = await pipeline('text-generation', 'Xenova/gpt2');
const generated = await generator('Angular est un framework', {
    max_new_tokens: 100,
    temperature: 0.7,
    do_sample: true,
    repetition_penalty: 1.2
});

// 6. Reconnaissance d'entités nommées (NER)
const ner = await pipeline('token-classification', 'Xenova/bert-base-NER');
const entities = await ner('Apple a été fondée par Steve Jobs à Cupertino.');
// [{ entity: 'ORG', word: 'Apple', score: 0.99, ... }]

// 7. Transcription audio (Whisper)
const transcriber = await pipeline('automatic-speech-recognition', 'Xenova/whisper-tiny');
const transcription = await transcriber(audioArray);
// { text: '...' }

// 8. Génération d'embeddings (pour RAG)
const embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
const embeddings = await embedder('Angular Signals', { pooling: 'mean', normalize: true });
// Float32Array[384] — vecteur d'embedding

Modèles recommandés par tâche

TâcheModèle Transformers.jsModèle API Inference
Sentiment (EN)Xenova/distilbert-base-uncased-finetuned-sst-2-englishcardiffnlp/twitter-roberta-base-sentiment-latest
Sentiment (FR)Xenova/camembert-base-sentimentcmarkea/distilcamembert-base-sentiment
Résumé (EN)Xenova/distilbart-cnn-6-6facebook/bart-large-cnn
Traduction FR→ENXenova/opus-mt-fr-enHelsinki-NLP/opus-mt-fr-en
QA (FR)etalab-ia/camembert-base-squadFR-fquad-piaf
Génération codeXenova/codegen-350M-monobigcode/starcoder2-3b
Transcription audioXenova/whisper-tinyopenai/whisper-large-v3
EmbeddingsXenova/all-MiniLM-L6-v2sentence-transformers/all-mpnet-base-v2
Comment trouver le bon modèle : sur huggingface.co/models → filtrer par Task (sentiment-analysis, translation…) + Language (French) → trier par "Most Downloads". Vérifier que le modèle a un espace de démo fonctionnel avant de l'intégrer.

Intégration Angular avec Web Worker

L'inférence avec Transformers.js est CPU-intensive — toujours l'exécuter dans un Web Worker pour ne pas bloquer l'UI.

// src/app/workers/nlp.worker.ts — Inférence hors thread principal
/// <reference lib="webworker" />
import { pipeline, env } from '@xenova/transformers';

env.allowLocalModels = false;
env.useBrowserCache = true;

// Cache des pipelines par tâche (ne pas recharger à chaque appel)
const pipelineCache = new Map<string, any>();

async function getPipeline(task: string, model: string) {
    const key = `${task}:${model}`;
    if (!pipelineCache.has(key)) {
        const pipe = await pipeline(task, model, {
            quantized: true,
            progress_callback: (data: any) => {
                // Envoyer la progression au thread principal
                self.postMessage({ type: 'progress', progress: data.progress });
            }
        });
        pipelineCache.set(key, pipe);
    }
    return pipelineCache.get(key);
}

self.onmessage = async ({ data }) => {
    const { id, task, model, inputs, options = {} } = data;
    try {
        const pipe = await getPipeline(task, model);
        const result = await pipe(inputs, options);
        self.postMessage({ type: 'result', id, result });
    } catch (error: any) {
        self.postMessage({ type: 'error', id, error: error.message });
    }
};

// nlp-worker.service.ts — Service Angular pour communiquer avec le worker
import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class NlpWorkerService {
    private worker = new Worker(
        new URL('./workers/nlp.worker', import.meta.url),
        { type: 'module' }
    );
    private pendingRequests = new Map<string, (value: any) => void>();
    readonly progressUpdates = new Subject<number>();

    constructor(private zone: NgZone) {
        this.worker.onmessage = ({ data }) => {
            this.zone.run(() => {
                if (data.type === 'progress') {
                    this.progressUpdates.next(data.progress);
                } else if (data.type === 'result') {
                    const resolve = this.pendingRequests.get(data.id);
                    resolve?.(data.result);
                    this.pendingRequests.delete(data.id);
                }
            });
        };
    }

    run(task: string, model: string, inputs: any, options = {}): Promise<any> {
        return new Promise((resolve, reject) => {
            const id = crypto.randomUUID();
            this.pendingRequests.set(id, resolve);
            this.worker.postMessage({ id, task, model, inputs, options });
        });
    }
}

Déployer sur Inference Endpoints

Pour la production, Hugging Face Inference Endpoints offre un déploiement serverless auto-scaling — évite le cold start de l'API gratuite.

// Appel à un Inference Endpoint dédié
// URL format: https://api-inference.huggingface.co/models/{model_id}
// ou endpoint custom: https://{endpoint-id}.{region}.aws.endpoints.huggingface.cloud

async function callEndpoint(endpointUrl: string, inputs: any) {
    const response = await fetch(endpointUrl, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${process.env.HF_API_TOKEN}`,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ inputs }),
    });

    if (!response.ok) throw new Error(`Endpoint error: ${response.status}`);
    return response.json();
}

// Pour la classification en batch — 10× plus efficace que les appels individuels
const batchResults = await callEndpoint(MY_ENDPOINT_URL, [
    'Premier texte à analyser',
    'Deuxième texte à analyser',
    'Troisième texte à analyser',
    // ... jusqu'à 32 textes par batch
]);

Bonnes pratiques et optimisation

  • Singleton par pipeline : charger une seule fois, réutiliser — le chargement prend 5-30 secondes selon le modèle et la connexion
  • Web Worker obligatoire pour Transformers.js — l'inférence CPU-intensive bloque l'UI thread principal
  • Modèles quantisés (quantized: true) pour le navigateur — 4× plus légers, qualité quasi identique
  • Progress callback pour informer l'utilisateur pendant le téléchargement du modèle
  • Cache IndexedDB (env.useBrowserCache = true) — évite de re-télécharger à chaque session
  • Retry avec backoff sur les erreurs 503 (cold start) — attendre estimated_time secondes avant de réessayer
  • Batch processing — passer plusieurs textes en tableau pour réduire les appels API
  • Jamais d'appel API Inference directement depuis le navigateur — clé API exposée dans le JS

Partager