Techniques avancées de NLP

Nous interagissons en réalité avec le NLP chaque jour — les suggestions de mots lorsque vous tapez sur le clavier de votre téléphone, les spams automatiquement filtrés par votre application de messagerie, et les sites web en langue étrangère que les logiciels de traduction vous aident à comprendre. Ce sont toutes des applications du traitement du langage naturel (NLP).

Avant l'émergence de ChatGPT, le NLP s'était déjà développé pendant des décennies, mais le seuil technique était élevé. Vous deviez comprendre une longue liste de concepts tels que la tokenisation, l'étiquetage morphosyntaxique, l'analyse syntaxique et l'étiquetage des rôles sémantiques, et aussi concevoir manuellement des caractéristiques, afin de permettre aux machines de « comprendre » un peu le langage.

Aujourd'hui, les grands modèles de langage rendent tout cela simple. Mais comprendre l'évolution technique du NLP vous permet de mieux comprendre pourquoi les grands modèles peuvent faire ces choses, et où se situent leurs limites.

Ce module vous fera passer des vecteurs de mots jusqu'aux grands modèles de langage d'aujourd'hui, en construisant un système complet de connaissances en NLP.。

Parcours d'apprentissage : vecteurs de mots → modèles pré-entraînés → les trois grands paradigmes de BERT/GPT/T5 → tâches en aval (classification, NER, traduction, résumé). Chaque étape comporte des exemples de code correspondants pour vous aider à mettre la théorie en pratique.


L'évolution des modèles de langage pré-entraînés

Le développement du NLP peut être clairement divisé en plusieurs étapes, chacune marquée par des avancées technologiques déterminantes.

Word2Vec : La révolution des plongements de mots

Avant 2013, la façon dont les ordinateurs traitaient le texte était très primitive.

Il utilisait généralement "One-Hot Encoding" : chaque mot correspond à un vecteur extrêmement long, avec une seule position à 1 et le reste à 0. Par exemple, si le vocabulaire comporte 100,000 mots, chaque mot est un vecteur à 100,000 dimensions.

Le problème de cette approche est évident : les vecteurs ne contiennent aucune information sémantique. La distance entre "chat" et "chien" est la même qu'entre "chat" et "table".

L'idée centrale de Word2Vec est :Le sens d'un mot est défini par les mots qui l'entourent.。

Exemple

# ============================================
# Démonstration des concepts de base de Word2Vec
# Utiliser la bibliothèque gensim pour entraîner un modèle simple de vecteurs de mots
# ============================================

# Installer d'abord gensim : pip install gensim

from gensim.models import Word2Vec
import numpy as np

# Préparer les données d'entraînement : quelques phrases simples
sentences = [
    ["I", "like", "eat", "apple"],
    ["I", "like", "eat", "banana"],
    ["chat", "aime", "mange", "poisson"],
    ["chien", "aime", "mange", "viande"],
    ["pomme", "est", "une sorte de", "fruit"],
    ["banane", "est", "une sorte de", "fruit"],
    ["chat", "est", "une sorte de", "animal"],
    ["chien", "est", "une sorte de", "animal"],
    ["EXEMPLE", "est", "a", Programmation, Site Web],
    [Apprentissage, Programmation, Go, "EXEMPLE"],
]

# Entraîner le modèle Word2Vec
# vector_size: dimension des vecteurs de mots
# window: taille de la fenêtre de contexte (regarder quelques mots avant et après)
# min_count: ignorer les mots dont le nombre d'occurrences est inférieur à cette valeur
# workers: nombre de threads pour l'entraînement parallèle
model = Word2Vec(
    sentences=sentences,
    vector_size=50,  # chaque mot est représenté par un vecteur à 50 dimensions
    window=3,       # regarder 3 mots avant et après
    min_count=1,    # garder tous les mots
    workers=4,
    epochs=100      # entraîner pendant 100 époques
)

# obtenir le vecteur de mot
apple_vector = model.wv[apple]
print(f'apple' vecteur de mot (10 premières dimensions): {apple_vector[:10]})
print(fDimension du vecteur de mot: {len(apple_vector)})

# calculer la similarité entre les mots
similarity = model.wv.similarity(apple, banana)
print(fsimilarité entre 'apple' et 'banana' : {similarity:.4f})

similarity = model.wv.similarity(apple, cat)
print(fsimilarité entre 'apple' et 'cat' : {similarity:.4f})

# trouver les mots les plus similaires
print("\nMots les plus similaires à 'cat' :)
for word, score in model.wv.most_similar(cat, topn=3):
    print(f" {score:.4f}")

print("\nMots les plus similaires à 'EXAMPLE' :)
for word, score in model.wv.most_similar("EXAMPLE", topn=3):
    print(f" {score:.4f}")

# arithmétique vectorielle classique : roi - homme + femme ≈ reine
# essayez dans notre petit corpus : fruit - apple + fish ≈ ?
if apple in model.wv and fish in model.wv and fruit in model.wv:
    result = model.wv.most_similar(positive=[fruit, fish], negative=[apple], topn=3)
    print("\n'fruit' - 'apple' + 'fish' ≈)
    for word, score in result:
        print(f" {score:.4f}")

Le succès de Word2Vec prouve une chose :La sémantique peut être représentée à l'aide d'espaces vectoriels.。

Mais il a une limitation : chaque mot n'a qu'un seul vecteur fixe, quel que soit le contexte. Par exemple, "打" dans "打电speech" (pour passer un appel téléphonique) et "打游戏" (pour jouer à des jeux) a des significations différentes, mais Word2Vec donne le même vecteur.

ELMo : plongements de mots contextuels

ELMo (Embeddings from Language Models), introduit en 2018, a résolu ce problème.

L'idée d'ELMo est :Il n'attribue pas un vecteur fixe à chaque mot au préalable ; au lieu de cela, il examine la phrase entière puis génère un vecteur pour ce mot.。

Le même caractère « 打 » est un vecteur dans « I打电speech » et un autre vecteur dans « I打游戏 ».

ELMo utilise un LSTM bidirectionnel (réseaux Long Short-Term Memory) pour modéliser le contexte, ce qui a été la première utilisation à grande échelle du paradigme « pré-entraînement + fine-tuning ».

GPT-1 : pré-entraînement unidirectionnel

Également en 2018, OpenAI a publié GPT-1 (Generative Pre-training Transformer).

Ses caractéristiques sont :

1. Utiliser un décodeur Transformer au lieu d’un LSTM.

2. Unidirectionnel : ne regarder que les mots précédents, prédire le mot suivant.

3. Génératif : peut continuer à écrire du texte.

GPT-1 a démontré le grand potentiel de Transformer sur les tâches de NLP.

BERT : pré-entraînement bidirectionnel

Fin 2018, Google a publié BERT (Bidirectional Encoder Representations from Transformers), qui a complètement transformé le domaine du NLP.

L'innovation centrale de BERT est :

Bidirectionnel : examiner simultanément le contexte précédent et suivant.

2. MLM (Masked Language Model) : masquer aléatoirement certains mots et laisser le modèle prédire.

3. NSP (Next Sentence Prediction) : déterminer si deux phrases sont consécutives.

BERT a obtenu les meilleurs résultats de l'époque sur 11 tâches NLP, marquant l'entrée du NLP dans l'« ère des modèles pré-entraînés ».

Le bond de GPT-3 à ChatGPT

En 2020, GPT-3 a été publié, avec un nombre de paramètres atteignant 175 milliards.

On a constaté que lorsque le modèle est suffisamment grand et les données suffisamment abondantes, un phénomène d'« émergence » se produit — le modèle acquiert soudainement des capacités qui manquent aux petits modèles, comme l'apprentissage few-shot et le raisonnement complexe.

Fin 2022, ChatGPT a été publié, utilisant RLHF (Reinforcement Learning from Human Feedback) pour mieux aligner les sorties du modèle sur les préférences humaines, et l'IA est véritablement devenue grand public.

Résumons cette histoire de l'évolution avec un tableau :

annéemodelidée principaleStatut historique
2013Word2VecUtiliser les mots autour d’un mot pour définir son sens.La révolution des vecteurs de mots : représentation vectorisée de la sémantique.
2018ELMoVecteurs de mots dépendants du contextePremière mise en œuvre de la représentation vectorielle de la polysémie.
2018GPT-1Décodeur Transformer, pré-entraînement unidirectionnel.Démontrer le potentiel des Transformers.
2018BERTEncodeur Transformer, pré-entraînement bidirectionnelLe TAL entre dans l'ère des modèles pré-entraînés
2020GPT-3175 milliards de paramètres, capacités émergentesIllustrer les possibilités infinies des grands modèles
2022ChatGPTRLHF + capacités conversationnellesL'IA atteint véritablement le grand public

BERT en profondeur

BERT est un jalon dans l'histoire du TAL, et il mérite notre compréhension approfondie.

Tâche MLM (Masked Language Model)

La tâche de pré-entraînement centrale de BERT est le MLM : remplacer aléatoirement 15% des mots d'une phrase par [MASK], et laisser le modèle prédire quel était le mot d'origine.

Par exemple, la phrase : « J'aime manger des pommes » pourrait devenir : « Je [MASK] manger des pommes », et le modèle doit prédire que [MASK] est « aime ».

Pourquoi faire cela ? Parce que cela force le modèle à utiliser simultanément les informations du contexte environnant, pas seulement celles de gauche ou de droite.

En pratique, ces 15% des mots ne sont pas tous remplacés par [MASK], mais plutôt :

1. 80% du temps, remplacer par [MASK]

2. 10% du temps, remplacer par un mot aléatoire

3. 10% du temps, garder le mot d'origine inchangé

Ceci est fait pour rendre le modèle plus robuste — il ne peut pas être certain qu'un mot a été remplacé, il doit donc réellement comprendre le contexte.

Tâche NSP (Next Sentence Prediction)

La deuxième tâche de pré-entraînement de BERT est NSP : étant donné deux phrases A et B, déterminer si B est la véritable phrase suivante après A.

Exemple positif : A="J'aime manger des pommes", B="Les pommes sont un fruit"

Exemple négatif : A="J'aime manger des pommes", B="Il fait vraiment beau aujourd'hui"

Cette tâche aide le modèle à comprendre les relations entre les phrases, ce qui est très utile pour des tâches telles que la réponse aux questions et l'inférence en langage naturel.

Cas d'utilisation de BERT

BERT est une architecture « encoder », douée pour comprendre le langage, adaptée à :

Classification de texte, analyse de sentiment, reconnaissance d'entités nommées, réponse aux questions, inférence en langage naturel, etc.

Il n'est pas très bon pour générer du texte, ce qui est le point fort de GPT.

Processus standard de fine-tuning de BERT

Utilisons la bibliothèque Hugging Face Transformers pour affiner BERT pour la classification de texte en pratique :

Exemple

# ============================================
# Affiner BERT pour la classification de texte avec Hugging Face
# Tâche : déterminer si une phrase a un sentiment positif ou négatif
# ============================================

# Installez d'abord les dépendances :
# pip install transformers datasets torch scikit-learn

import torch
from transformers import (
    BertTokenizer,
    BertForSequenceClassification,
    TrainingArguments,
    Trainer,
)
from datasets import Dataset, load_metric
import numpy as np
from sklearn.model_selection import train_test_split

# ============================================
# 1. Préparer les données
# ============================================

# Construire un jeu de données simple de classification de sentiment
textes = [
    "Ce produit est vraiment agréable à utiliser, je l'aime vraiment",
    "La qualité est trop mauvaise, très décevant",
    "Le tutoriel EXAMPLE est clairement écrit et facile à apprendre",
    La logistique est très lente, et l'emballage est également endommagé,
    Les performances de l'appareil photo de ce téléphone sont excellentes,
    Mauvaise attitude du service, je ne reviendrai pas,
    Le contenu du livre est merveilleux, recommandé pour l'achat,
    Le film est très ennuyeux, je me suis endormi en le regardant,
    La nourriture de ce restaurant est très savoureuse,
    Le jeu a trop de bugs, mauvaise expérience,
    Le tutoriel NLP d'EXAMPLE est très utile,
    L'interface de ce logiciel est bien conçue,
    L'attitude du livreur est très aimable,
    La taille des vêtements est inexacte, et la couleur est fausse aussi,
    L'environnement de l'hôtel est très calme, j'ai bien dormi,
    Le service client répond très lentement, le problème n'a pas été résolu,
]

labels = [
    1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0
]  # 1=positif, 0=négatif

# Diviser les ensembles d'entraînement et de validation
train_texts, val_texts, train_labels, val_labels = train_test_split(
    texts, labels, test_size=0.25, random_state=42
)

# Créer un objet Dataset
train_dataset = Dataset.from_dict({"text": train_texts, "label": train_labels})
val_dataset = Dataset.from_dict({"text": val_texts, "label": val_labels})

# ============================================
# 2. Charger le tokenizer et le modèle
# ============================================

# Utiliser le modèle BERT chinois
model_name = "bert-base-chinese"

tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(
    model_name,
    num_labels=2  # Tâche de classification binaire
)

# ============================================
# 3. Prétraitement des données : convertir le texte en entrée du modèle
# ============================================

def tokenize_function(examples):
    return tokenizer(
        examples["text"],
        padding="max_length",
        truncation=True,
        max_length=64
    )

# Appliquer le prétraitement
tokenized_train = train_dataset.map(tokenize_function, batched=True)
tokenized_val = val_dataset.map(tokenize_function, batched=True)

# ============================================
# 4. Définir les métriques d'évaluation
# ============================================

metric = load_metric("accuracy")

def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

# ============================================
# 5. Configurer les paramètres d'entraînement
# ============================================

training_args = TrainingArguments(
    output_dir="./example-bert-sentiment",  # Répertoire de sortie
    num_train_epochs=3,                    # Nombre d'époques d'entraînement
    per_device_train_batch_size=4,         # Taille du lot d'entraînement
    per_device_eval_batch_size=4,          # Taille du lot d'évaluation
    warmup_steps=5,                        # Étapes de préchauffage
    weight_decay=0.01,                     # Décroissance des poids
    logging_dir="./logs",                  # Répertoire des journaux
    logging_steps=10,
    evaluation_strategy="epoch",           # Évaluer une fois par époque
    save_strategy="epoch",
    learning_rate=2e-5,
    load_best_model_at_end=True,
)

# ============================================
# 6. Créer le Trainer et démarrer l'entraînement
# ============================================

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_train,
    eval_dataset=tokenized_val,
    compute_metrics=compute_metrics,
)

# Lancer l'entraînement (commenté car cela nécessite beaucoup de temps et un GPU)
# print("Start training...")
# trainer.train()

# ============================================
# 7. Utiliser le modèle entraîné pour la prédiction
# ============================================

# Ici, nous utilisons directement le modèle original pour démontrer le processus de prédiction
# En pratique, vous devez utiliser le modèle enregistré après l'entraînement avec trainer

def predict_sentiment(text, model, tokenizer):
    """Prédire le sentiment d'une phrase"""
    inputs = tokenizer(
        text,
        return_tensors="pt",
        padding=True,
        truncation=True,
        max_length=64
    )

    model.eval()
    with torch.no_grad():
        outputs = model(**inputs)
        logits = outputs.logits
        probabilities = torch.softmax(logits, dim=-1)
        prediction = torch.argmax(probabilities, dim=-1).item()

    label_map = {0: "Négatif", 1: "Positif"}
    confidence = probabilities[0][prediction].item()

    return {
        "text": text,
        "sentiment": label_map[prediction],
        "confiance": confidence,
        "negative_prob": probabilities[0][0].item(),
        "positive_prob": probabilities[0][1].item(),
    }

# Tester quelques phrases
test_texts = [
    "Le tutoriel EXAMPLE est vraiment génial !",
    "La qualité de ce produit est trop médiocre",
    "Le temps est vraiment agréable aujourd'hui",
    "Je n'aime pas vraiment ce film",
]

print("Prédiction du modèle de test (avec bert-base-chinese pré-entraîné) :")
print("-" * 50)
for text in test_texts:
    # Remarque : utilisation directe du modèle original ici, pas de fine-tuning, les résultats sont donnés à titre de démonstration uniquement
    result = predict_sentiment(text, model, tokenizer)
    print(f"Texte : {result['text']}")
    print(fSentiment : {result['sentiment']})
    print(fConfiance : {result['confidence']:.4f})
    print(fProbabilité positive : {result['positive_prob']:.4f})
    print(fProbabilité négative : {result['negative_prob']:.4f})
    print("-" * 50)

Remarque : Le code ci-dessus démontre le pipeline complet, mais en réalité, le fine-tuning de BERT nécessite un GPU et beaucoup de temps. Dans les environnements de production, vous pouvez également envisager d'utiliser des modèles plus légers (comme DistilBERT), ou d'utiliser directement l'API d'un grand modèle.


Plongée approfondie dans la série GPT

GPT (Generative Pre-trained Transformer) a emprunté une voie différente de celle de BERT.

Modèle de langage autorégressif

GPT est « autorégressif » : il génère mot après mot et, à chaque étape, utilise tous les mots précédemment générés pour prédire le mot suivant.

Par exemple, pour générer « J'aime les pommes », le processus est :

1. Entrée « J' », prédire que le mot suivant est « aime »

2. Entrée « J'aime », prédire que le mot suivant est « les »

3. Entrée « J'aime les », prédire que le mot suivant est « pommes »

Cette approche est naturellement adaptée à la génération de texte.

Architecture à décodeur uniquement

GPT n'utilise que le décodeur Transformer, tandis que BERT n'utilise que l'encodeur.

La caractéristique du décodeur est l'« auto-attention masquée » - chaque position ne peut voir que les positions à sa gauche, pas à sa droite.

Cela est logique car la génération de texte se fait de gauche à droite ; on ne peut pas regarder les mots qui n'ont pas encore été générés.

La découverte des lois d'échelle

La découverte la plus importante de la série GPT est la loi d'échelle :La performance du modèle entretient une relation de loi de puissance avec la taille du modèle, le volume de données et la quantité de calcul.。

En termes simples : plus le modèle est grand, plus il y a de données et plus l'entraînement est long, meilleurs sont les résultats. Et cette amélioration est prévisible sans plateau évident.

C'est pourquoi la série GPT continue de grossir : GPT-1 (117M) → GPT-2 (1.5B) → GPT-3 (175B).


T5 et le paradigme Seq2Seq

Il existe un troisième paradigme : la structure Encodeur-Décodeur, représentée par T5.

Cadre unifié texte-à-texte

L'idée centrale de T5 (Text-to-Text Transfer Transformer) est :Unifier toutes les tâches NLP dans un format « texte-à-texte ».。

Par exemple :

Classification de texte : Entrée « Classification : Ce film est génial » → Sortie « Positif »

Traduction : Entrée « Traduire de l'anglais vers le chinois : Hello world » → Sortie « Hello World »

Résumé : Entrée « Résumé : Ceci est un article sur... » → Sortie « Cet article traite principalement de... »

L'avantage de ce cadre unifié est que le même modèle peut gérer diverses tâches sans avoir besoin de modifier la structure du modèle pour chaque tâche.

Comment fonctionne l'encodeur-décodeur

L'encodeur est responsable de la compréhension du texte d'entrée, et le décodeur est responsable de la génération du texte de sortie.

Prenons la traduction comme exemple :

1. L'encodeur lit « Hello world » et génère une représentation « comprise »

2. À partir de cette représentation, le décodeur génère « Hello World » mot par mot

Le décodeur peut « prêter attention » à différentes positions de l'encodeur—par exemple, lorsqu'il génère Hello, il accorde plus d'attention à « Hello », et lorsqu'il génère « world », il accorde plus d'attention à « world ».


Comparaison des trois principaux paradigmes : BERT vs GPT vs T5

Comparons ces trois architectures à l'aide d'un tableau :

FonctionnalitéBERTGPTT5
ArchitectureEncodeur uniquementDécodeur uniquementEncodeur-Décodeur
AttentionBidirectionnel (voit à la fois le contexte à gauche et à droite)Unidirectionnel (ne voit que le contexte gauche)Encodeur bidirectionnel, décodeur unidirectionnel
Tâche de pré-entraînementMLM (modélisation du langage masqué)Modélisation autorégressive du langageCorruption de segments
Excelle dansTâches de compréhension : classification, NER, QATâches de génération : continuation, dialogueTâches de transformation : traduction, résumé, réécriture
Modèles représentatifsBERT、RoBERTa、ALBERTGPT-1/2/3/4、ChatGPTT5, BART, mT5
Applications typiquesAnalyse de sentiment, filtrage de spamAssistant de rédaction, chatbotTraduction automatique, résumé de documents

Les grands modèles de langage actuels utilisent principalement une architecture Decoder-only (la voie GPT), car elle est la plus performante en génération et peut être continuellement améliorée grâce aux lois d'échelle. Cependant, les deux autres architectures présentent encore des avantages pour des tâches spécifiques.


Classification de texte

La classification de texte est l'une des tâches de NLP les plus courantes et la meilleure tâche de départ pour les débutants.

Classification multiclasse en pratique

La classification multiclasse consiste à attribuer une étiquette à un texte, et l'étiquette possède plusieurs options.

Par exemple : classification d'actualités (technologie, sport, divertissement, finance), classification de tickets de service client, classification d'avis produits, etc.

Exemple

# ============================================
# Classification de texte en pratique : comparaison de plusieurs approches
# ============================================

from typing import List, Dict, Any

# ============================================
# Approche 1 : apprentissage automatique traditionnel + TF-IDF
# Adapté aux petits jeux de données, forte interprétabilité
# ============================================

def traditional_text_classification_demo():
    """Utiliser des méthodes traditionnelles pour la classification de texte"""
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.naive_bayes import MultinomialNB
    from sklearn.pipeline import Pipeline

    # Données d'entraînement
    textes = [
        "J'ai acheté une action aujourd'hui, elle a beaucoup augmenté",
        "L'investissement régulier en fonds est un bon moyen de gérer ses finances",
        "L'attaquant de cette équipe a marqué",
        "Les playoffs NBA sont vraiment passionnants",
        "L'appareil photo de ce téléphone prend de superbes photos",
        "Les tutoriels de programmation d'EXAMPLE sont très clairs",
        "L'intrigue de ce film est très touchante",
        "Les chansons de cet album sont très agréables",
    ]

    labels = ["Finance", "Finance", "Sports", "Sports", "Technologie", "Technologie", "Divertissement", "Divertissement"]

    # Créer un pipeline : TF-IDF + Naive Bayes
    pipeline = Pipeline([
        ("tfidf", TfidfVectorizer()),  # Convertir le texte en caractéristiques TF-IDF
        ("classifier", MultinomialNB()),  # Classificateur Naive Bayes
    ])

    # Entraîner
    pipeline.fit(texts, labels)

    # Test
    test_texts = [
        "L'action a chuté, je suis de mauvaise humeur",
        "Panier au buzzer lors du match de basket à la dernière seconde",
        "L'ordinateur portable tout juste sorti offre de solides performances",
        "Le nouveau tutoriel NLP d'EXAMPLE",
    ]

    predictions = pipeline.predict(test_texts)
    probabilities = pipeline.predict_proba(test_texts)

    print("Option 1 : Apprentissage automatique traditionnel + TF-IDF")
    print("-" * 50)
    for text, pred, probs in zip(test_texts, predictions, probabilities):
        print(f"Texte : {text}")
        print(f"Prédiction : {pred}")
        print(f"Probabilités de classe : {dict(zip(pipeline.classes_, probs))}")
        print()

# ============================================
# Option 2 : Utiliser le pipeline Hugging Face
# Adapté au prototypage rapide, aucun entraînement requis
# ============================================

def huggingface_pipeline_demo():
    """Utiliser le pipeline pré-entraîné de Hugging Face"""
    print("Option 2 : Pipeline Hugging Face")
    print("-" * 50)

    try:
        from transformers import pipeline

        # Utiliser le pipeline d'analyse de sentiment (anglais)
        classifier = pipeline("sentiment-analysis")

        test_texts = [
            "J'adore les tutoriels EXAMPLE !",
            "Ce film est terrible.",
        ]

        results = classifier(test_texts)

        for text, result in zip(test_texts, results):
            print(f"Texte : {text}")
            print(f"Sentiment : {result['label']}")
            print(f"Confiance: {result['score']:.4f}")
            print()

    except Exception as e:
        print(f"Il faut d'abord installer transformers: pip install transformers")
        print(f"Erreur: {e}")

# ============================================
# Option 3: Utiliser l'API d'un grand modèle (recommandé)
# Adapté à l'environnement de production, meilleures performances
# ============================================

def llm_based_classification_demo():
    """Utiliser un grand modèle pour la classification de texte (simulation)"""
    print("Option 3: Classification par grand modèle (concept de démonstration)")
    print("-" * 50)

    # En utilisation réelle, appeler des API comme OpenAI/Anthropic
    # Démontrer ici la conception de prompt pour la classification

    def classify_with_llm(text: str, categories: List[str]) -> Dict[str, Any]:
        """Fonction simulée pour la classification avec LLM"""
        # Scénario réel : appel à l'API LLM
        # prompt = f"""Veuillez classifier le texte suivant et ne renvoyer que le nom de la catégorie.
        # Texte : {text}
        # Catégories facultatives : {', '.join(categories)}"""

        # Effectuer une simple correspondance de mots-clés ici pour simuler
        category_map = {
            "Finance": ["Action", "Fonds", "Gestion de patrimoine", "Investissement"],
            "Sports": ["Basket-ball", "Football", "Match", "But"],
            "Technologie": ["Téléphone", "Ordinateur", "Programmation", "Tutoriel"],
            "Divertissement": ["Film", "Musique", "Album", "Chanson"],
        }

        for category, keywords in category_map.items():
            if any(keyword in text for keyword in keywords):
                return {
                    "category": category,
                    "confiance": 0.9,
                    "raisonnement": f"Contient les mots-clés : {', '.join([k for k in keywords if k in text])}"
                }

        return {"catégorie": "Autre", "confiance": 0.5}

    test_texts = [
        "L'action a augmenté, je suis très heureux",
        "La Coupe du monde de football a ouvert",
        "Nouveau tutoriel de programmation d'EXAMPLE",
    ]

    categories = ["Finance", "Sports", "Technologie", "Divertissement", "Autre"]

    for text in test_texts:
        result = classify_with_llm(text, categories)
        print(f"Texte : {text}")
        print(f"Catégorie : {result['category']}")
        print(f"Confiance : {result['confidence']:.4f}")
        if "reasoning" in result:
            print(f"Raison : {result['reasoning']}")
        print()

# ============================================
# Exécuter la démo
# ============================================

if __name__ == "__main__":
    traditional_text_classification_demo()
    print("=" * 50)
    llm_based_classification_demo()

Méthodes de classification Few-Shot

L'apprentissage Few-Shot est une nouvelle capacité apportée par les grands modèles—il suffit de fournir quelques exemples et le modèle peut apprendre à classifier.

Par exemple :

Texte : Ce produit fonctionne bien → Étiquette : Positif

Texte : La qualité est trop mauvaise → Étiquette : Négatif

Texte : [votre nouveau texte] → Étiquette : ?

Après avoir vu ces deux exemples, le grand modèle peut comprendre la tâche et effectuer des classifications raisonnables pour un nouveau texte.

Sélection entre grand modèle et petit modèle

Quand utiliser quelle approche ? Voici un guide de décision :

ScénarioApproche recommandéeJustification
Faible volume de données (< 1000 entrées)Apprentissage few-shot avec un grand modèleLes petits modèles ne peuvent pas l'apprendre ; les grands modèles généralisent bien
Grand volume de données (> 10000 entrées)Fine-tuner un petit modèle (par ex., BERT)Coût plus faible, inférence plus rapide
Besoin d'une validation rapideAPI de grand modèleAucun entraînement nécessaire, utilisable immédiatement
Environnement de production, débit élevéAffiner un petit modèle + distillationInférence rapide, coût maîtrisable
Les catégories changent fréquemmentIngénierie de prompt pour grand modèleAucun réentraînement nécessaire, il suffit de changer le prompt

Reconnaissance d'entités nommées (NER)

La reconnaissance d'entités nommées (NER) est la tâche consistant à identifier des entités spécifiques dans un texte, telles que des noms de personnes, des noms de lieux et des noms d'organisations.

Cadre d'étiquetage de séquences

La NER est généralement modélisée comme une tâche d'« étiquetage de séquences » : attribuer une étiquette à chaque mot indiquant s'il fait partie d'une entité.

Par exemple, pour la phrase : "Zhang SanIn Google 工do", les étiquettes pourraient être :

张三 → B-PER (début de nom de personne)

in → O (non-entité)

Google → B-ORG (début d'organisation)

Work → O

Format d'annotation BIO

Le format d'annotation le plus courant est BIO :

B-XXX : début d'entité

I-XXX : intérieur d'entité

O : non-entité

Par exemple : "Wang Xiaoming works at Microsoft in Beijing", avec les étiquettes suivantes :

Wang → B-PER

Xiao → I-PER

Ming → I-PER

à → O

Bei → B-LOC

Jing → I-LOC

's → O

Wei → B-ORG

Ruan → I-ORG

Gong → I-ORG

Si → I-ORG

en → O

cours → O

Applications pratiques

Utilisons Hugging Face pour la NER :

Exemple

# ============================================
# NER pratique : Utiliser Hugging Face pour la reconnaissance d'entités nommées
# ============================================

def ner_with_huggingface():
    """Utiliser un modèle NER pré-entraîné"""
    print(NER en action : Hugging Face)
    print("-" * 50)

    try:
        from transformers import pipeline

        # Charger le pipeline NER
        # NER en anglais
        ner_pipeline = pipeline(
            "ner",
            model="dbmdz/bert-large-cased-finetuned-conll03-english",
            grouped_entities=True  # Fusionner plusieurs tokens d'une même entité
        )

        test_text = "John Smith travaille chez Google à New York City. Il aime les tutoriels EXAMPLE."

        results = ner_pipeline(test_text)

        print(f"Texte : {test_text}")
        print("Entités reconnues :")
        for entity in results:
            print(f" - {entity['word']}: {entity['entity_group']} (confiance : {entity['score']:.4f})")
        print()

    except Exception as e:
        print(f"Il faut d'abord installer transformers: pip install transformers")
        print(f"Erreur: {e}")

# ============================================
# NER chinois : utilisation de regex + mots-clés (scénario simple)
# ============================================

def simple_chinese_ner():
    """NER chinois simple : utilisation de regex et d'un dictionnaire"""
    import re

    print("NER chinois simple : regex + dictionnaire")
    print("-" * 50)

    # Dictionnaire d'entités (serait plus grand dans des scénarios réels)
    person_names = ["Zhang San", "Li Si", "Wang Xiaoming", "Liu Dehua"]
    organisations = ["Google", "Microsoft", "Alibaba", "Tencent", "EXEMPLE"]
    lieux = ["Beijing", "Shanghai", "Shenzhen", "New York", "London"]

    # Motifs regex
    motifs = {
        "PERSON": "|".join(re.escape(name) for name in person_names),
        "ORG": "|".join(re.escape(org) for org in organizations),
        "LOC": "|".join(re.escape(loc) for loc in locations),
        "PHONE": r"1[3-9]\d{9}",  # Numéro de téléphone
        "EMAIL": r"\w+@\w+\.\w+",  # E-mail
    }

    def extract_entities(text: str):
        """Extraire les entités du texte"""
        entities = []

        for entity_type, pattern in patterns.items():
            for match in re.finditer(pattern, text):
                entities.append({
                    "text": match.group(),
                    "type": entity_type,
                    "start": match.start(),
                    "end": match.end(),
                })

        # Trier par position
        entities.sort(key=lambda x: x["start"])
        return entities

    # Test
    test_texts = [
        "Zhang San travaille chez Google, son numéro de téléphone est 13800138000",
        "Wang Xiaoming se rend chez Microsoft à Pékin en voyage d'affaires",
        "Si vous avez des questions, contactez [email protected]",
        "EXAMPLE est un très bon site d'apprentissage",
    ]

    for text in test_texts:
        entities = extract_entities(text)
        print(f"Texte : {text}")
        if entities:
            print("Entités reconnues :")
            for ent in entities:
                print(f" - {ent['text']}: {ent['type']}")
        else:
            print("Aucune entité reconnue")
        print()

# ============================================
# Grand modèle pour la NER
# ============================================

def ner_with_llm():
    """Démonstration de l'idée d'utiliser un grand modèle pour la NER"""
    print("Grand modèle pour la NER (idée de démonstration)")
    print("-" * 50)

    def extract_entities_with_llm(text: str):
        """Utilisation d'un LLM pour la NER (simulation)"""
        # Scénario réel : construire un prompt pour appeler un LLM
        # prompt = f"""Veuillez extraire les entités nommées du texte suivant.
        # Retourner uniquement au format JSON, incluant les types d'entités : PERSON (nom de personne), ORG (organisation), LOC (lieu).
        # Texte : {text}"""

        # Ici, nous effectuons une simple simulation
        entities = []

        # Correspondance simple de mots-clés pour la simulation
        if "Zhang San" in text:
            entities.append({"text": "Zhang San", "type": "PERSONNE"})
        if "Google" in text:
            entities.append({"text": "Google", "type": "ORGANISATION"})
        if "Pékin" in text:
            entities.append({"text": "Pékin", "type": "LIEU"})
        if "EXEMPLE" in text:
            entities.append({"text": "EXEMPLE", "type": "ORGANISATION"})

        return entities

    test_text = "Zhang San travaille chez Google à Pékin, et il utilise souvent EXEMPLE pour apprendre."
    entities = extract_entities_with_llm(test_text)

    print(f"Texte : {test_text}")
    print("Entités reconnues :")
    for ent in entities:
        print(f" - {ent['text']}: {ent['type']}")

# ============================================
# Exécuter la démo
# ============================================

if __name__ == "__main__":
    simple_chinese_ner()
    print("=" * 50)
    ner_with_llm()

Traduction automatique

La traduction automatique est l'une des applications les plus classiques du NLP et l'une des premières applications commercialisées.

Principes de la traduction automatique neuronale

Les premières traductions automatiques étaient fondées sur des règles, puis sur des statistiques, et aujourd'hui tout est neuronal.

La traduction automatique neuronale (NMT) utilise généralement une architecture encodeur-décodeur :

1. L'encodeur lit la phrase en langue source et génère une représentation sémantique

2. Le décodeur génère la phrase en langue cible à partir de la représentation sémantique

Le mécanisme d'attention permet au décodeur de « se concentrer » sur différentes positions de la phrase en langue source lors de la génération de chaque mot — c'est essentiel pour améliorer la qualité de la traduction.

Métrique d'évaluation : score BLEU

Comment mesurer la qualité d'une traduction ? BLEU (Bilingual Evaluation Understudy) est la métrique la plus couramment utilisée.

L'idée centrale de BLEU est la suivante :Plus le chevauchement de n-grammes entre le résultat de la traduction automatique et le résultat de la traduction humaine est élevé, meilleure est la qualité.。

Le score BLEU varie de 0 à 1, plus il est élevé, mieux c'est. Généralement :

En dessous de 0.1 : pratiquement inintelligible

0.1-0.3 : on peut comprendre le sens général

0.3-0.5 : bonne qualité de traduction

Au-dessus de 0.5 : proche d'une traduction humaine

Exemple

# ============================================
# Démo de calcul du score BLEU
# ============================================

def bleu_score_demo():
    """Démontrer le calcul du score BLEU"""
    print("Démo du score BLEU")
    print("-" * 50)

    # Remarque : En pratique, il est recommandé d'utiliser la bibliothèque sacrebleu
    # Ici, nous faisons une démonstration conceptuelle

    from collections import Counter
    import math

    def compute_ngrams(tokens, n):
        """Calculer les n-grammes"""
        return [tuple(tokens[i:i+n]) for i in range(len(tokens)-n+1)]

    def simple_bleu(reference: str, candidate: str, max_n: int = 4):
        """Calcul BLEU simple (démonstration conceptuelle)"""
        ref_tokens = reference.split()
        cand_tokens = candidate.split()

        # Calculer la pénalité de brièveté
        ref_len = len(ref_tokens)
        cand_len = len(cand_tokens)

        if cand_len == 0:
            return 0.0

        if cand_len <= ref_len:
            bp = math.exp(1 - ref_len / cand_len)
        else:
            bp = 1.0

        # Calculer la précision pour chaque n-gramme
        precisions = []
        for n in range(1, max_n + 1):
            ref_ngrams = Counter(compute_ngrams(ref_tokens, n))
            cand_ngrams = Counter(compute_ngrams(cand_tokens, n))

            if not cand_ngrams:
                precisions.append(0.0)
                continue

            # Compter le nombre de correspondances
            matches = 0
            for ngram, count in cand_ngrams.items():
                matches += min(count, ref_ngrams.get(ngram, 0))

            total = sum(cand_ngrams.values())
            precisions.append(matches / total if total > 0 else 0.0)

        # Moyenne géométrique
        if all(p == 0 for p in precisions):
            geo_mean = 0.0
        else:
            # Éviter log(0)
            log_sum = sum(math.log(p + 1e-10) for p in precisions) / max_n
            geo_mean = math.exp(log_sum)

        bleu = bp * geo_mean
        return bleu

    # Test
    reference = "the cat sat on the mat"
    candidates = [
        "the cat sat on the mat",  # Correspondance exacte
        "the cat was on the mat",  # Un mot différent
        "the cat sat on a mat",    # Article différent
        "a cat sat on the mat",
        "the dog sat on the mat",  # Une erreur de mot
        "mat the on sat cat the",  # Ordre des mots complètement mélangé
        "hello world",             # Totalement hors sujet
    ]

    print(fTraduction de référence : {reference})
    print()
    for candidate in candidates:
        bleu = simple_bleu(reference, candidate)
        print(fTraduction candidate : {candidate})
        print(fScore BLEU : {bleu:.4f})
        print()

    # Exemple chinois
    print(Exemple de traduction chinoise :)
    print("-" * 30)
    ref = J'aime manger des pommes
    cand1 = J'aime manger des pommes
    cand2 = J'adore manger des pommes
    cand3 = Des pommes, j'aime en manger

    print(fRéférence : {ref})
    print(fCandidat 1 : {cand1}, BLEU : {simple_bleu(ref, cand1):.4f})
    print(fCandidat 2: {cand2}, BLEU: {simple_bleu(ref, cand2):.4f})
    print(fCandidat 3: {cand3}, BLEU: {simple_bleu(ref, cand3):.4f})


if __name__ == "__main__":
    bleu_score_demo()

Qualité de la traduction à l'ère des LLM

Après l'avènement des grands modèles, la qualité de la traduction automatique a atteint un nouveau niveau.

Les modèles de traduction traditionnels sont entraînés sur des données parallèles bilingues, tandis que les grands modèles sont entraînés sur d'énormes quantités de texte et ont une compréhension plus profonde du langage.

Avantages des grands modèles en traduction:

1. Meilleure compréhension du contexte: peut gérer l'ambiguïté et la polysémie

2. Contrôle du style: peut spécifier le ton et le style de la traduction

3. Cohérence terminologique: peut fournir un glossaire pour assurer une traduction cohérente des noms propres

4. Capacité multilingue: un modèle peut traduire entre des dizaines ou des centaines de langues


Analyse de sentiments

L'analyse des sentiments consiste à déterminer la tendance émotionnelle d'un texte—positive, négative ou neutre.

Analyse fine des sentiments

L'analyse de sentiment simple est une classification binaire (positif/négatif) ; une analyse plus fine peut être :

1. Notation de sentiment : 1-5 étoiles, 1 est le plus négatif, 5 est le plus positif

2. Classification de sentiment : colère, joie, tristesse, surprise, etc.

3. Analyse de sentiment par aspect : non seulement elle évalue le sentiment global, mais elle analyse aussi le sentiment envers des aspects spécifiques.

Par exemple : "La nourriture dans ce restaurant est délicieuse, mais le service est trop lent."

Sentiment global : neutre à positif

Sentiment au niveau des aspects :

Nourriture → Positif

Service → Négatif

Modèles de sentiment multilingues

Les modèles de sentiment actuels peuvent gérer plusieurs langues sans nécessiter un entraînement séparé pour chaque langue.

Modèles multilingues courants :

De grands modèles tels que XLM-RoBERTa, mBERT (BERT multilingue), Llama et Claude.


Résumé de texte

Le résumé de texte est le processus consistant à raccourcir un long texte tout en préservant les informations essentielles.

Résumé extractif vs abstractif

Il existe deux principales approches de résumé :

Résumé extractif: Sélectionnez les phrases les plus importantes du texte original et assemblez-les.

Avantages : les informations sont exactes et ne sont pas inventées

Inconvénients : pas assez fluide, peut contenir des informations redondantes

Résumé abstractif: Laissez le modèle "comprendre" le texte original, puis rédigez un résumé dans ses propres mots.

Avantages : fluide, concis et peut résumer l'original

Inconvénients : peut "halluciner", en fabriquant des informations absentes du texte original

À l'ère des grands modèles, le résumé abstractif est devenu courant, mais il faut prêter attention au problème d'hallucination lors de son utilisation.

Métrique d'évaluation ROUGE

Comment évalue-t-on la qualité du résumé ? ROUGE est la métrique la plus couramment utilisée.

ROUGE a plusieurs variantes :

ROUGE-1 : correspondance basée sur les unigrammes (mots uniques)

ROUGE-2 : correspondance basée sur les bigrammes (deux mots)

ROUGE-L : basé sur la plus longue sous-séquence commune

Comme BLEU, ROUGE mesure également le chevauchement entre les résumés générés par machine et les résumés de référence humains.

Exemple

# ============================================
# Résumé de texte : métriques ROUGE + résumé extractif simple
# ============================================

def rouge_demo():
    """Démo des métriques ROUGE"""
    print("Démo des métriques ROUGE")
    print("-" * 50)

    from collections import Counter

    def compute_rouge_1(reference: str, candidate: str) -> float:
        """Calculer ROUGE-1 (F1 unigramme)"""
        ref_tokens = reference.split()
        cand_tokens = candidate.split()

        if not ref_tokens or not cand_tokens:
            return 0.0

        ref_counts = Counter(ref_tokens)
        cand_counts = Counter(cand_tokens)

        # Calculer le nombre de correspondances
        matches = 0
        for token, count in cand_counts.items():
            matches += min(count, ref_counts.get(token, 0))

        precision = matches / len(cand_tokens)
        recall = matches / len(ref_tokens)

        if precision + recall == 0:
            return 0.0

        f1 = 2 * precision * recall / (precision + recall)
        return f1

    def compute_lcs(a: list, b: list) -> int:
        """Calcule la longueur de la plus longue sous-séquence commune"""
        m, n = len(a), len(b)
        dp = [[0] * (n + 1) for _ in range(m + 1)]

        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if a[i-1] == b[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

        return dp[m][n]

    def compute_rouge_l(reference: str, candidate: str) -> float:
        """Calcule ROUGE-L (F1 de la plus longue sous-séquence commune)"""
        ref_tokens = reference.split()
        cand_tokens = candidate.split()

        if not ref_tokens or not cand_tokens:
            return 0.0

        lcs_len = compute_lcs(ref_tokens, cand_tokens)

        precision = lcs_len / len(cand_tokens)
        recall = lcs_len / len(ref_tokens)

        if precision + recall == 0:
            return 0.0

        f1 = 2 * precision * recall / (precision + recall)
        return f1

    # Test
    reference = "Le traitement du langage naturel est une branche importante de l'intelligence artificielle, qui étudie l'interaction entre les ordinateurs et le langage humain."
    candidates = [
        "Le traitement du langage naturel est une branche importante de l'intelligence artificielle, qui étudie l'interaction entre les ordinateurs et le langage humain.",  # Correspondance exacte
        "Le traitement du langage naturel étudie l'interaction entre les ordinateurs et le langage humain, et est une branche importante de l'intelligence artificielle.",  # Ordre modifié
        "Le traitement du langage naturel est une branche de l'intelligence artificielle, qui étudie l'interaction entre les ordinateurs et le langage.",  # Version abrégée
        "EXAMPLE est un site web d'apprentissage de la programmation.",  # Non pertinent
    ]

    print(f"Résumé de référence: {reference}")
    print()
    for candidate in candidates:
        rouge1 = compute_rouge_1(reference, candidate)
        rougel = compute_rouge_l(reference, candidate)
        print(f"Résumé du candidat: {candidate}")
        print(f"ROUGE-1: {rouge1:.4f}")
        print(f"ROUGE-L: {rougel:.4f}")
        print()


def simple_extractive_summarization():
    Résumé extractif simple : sélectionner les phrases importantes en fonction de la fréquence des mots
    print(Démonstration de résumé extractif simple)
    print("-" * 50)

    import re
    from collections import Counter

    def split_sentences(text: str) -> list:
        Segmentation simple de phrases chinoises
        # Diviser les phrases par des points, des points d'exclamation et des points d'interrogation
        sentences = re.split(r'[。!?]', text)
        # Filtrer les chaînes vides
        sentences = [s.strip() for s in sentences if s.strip()]
        return sentences

    def get_word_frequency(text: str) -> Counter:
        """Calcule la fréquence des mots (démo simple : simulée avec des n-grammes au niveau des caractères)"""
        # Dans des scénarios réels, un outil de segmentation de mots tel que jieba devrait être utilisé
        # Ici, nous utilisons simplement un uni-gramme
        chars = [c for c in text if c.strip()]
        return Counter(chars)

    def sentence_score(sentence: str, word_freq: Counter) -> float:
        """Calcule les scores des phrases"""
        if not sentence:
            return 0.0

        # Score simple : somme des fréquences des mots dans la phrase / longueur de la phrase
        total_score = 0.0
        for char in sentence:
            total_score += word_freq.get(char, 0)

        # Normalisation
        return total_score / (len(sentence) + 1)

    def extractive_summary(text: str, num_sentences: int = 2) -> str:
        """Résumé extractif"""
        sentences = split_sentences(text)

        if len(sentences) <= num_sentences:
            return text

        # Calculer la fréquence des mots
        word_freq = get_word_frequency(text)

        # Calculer le score de chaque phrase
        scores = [(i, sentence_score(s, word_freq)) for i, s in enumerate(sentences)]

        # Trier par score et sélectionner les meilleures
        scores.sort(key=lambda x: x[1], reverse=True)
        top_indices = [i for i, score in scores[:num_sentences]]
        top_indices.sort()  # Conserver l'ordre d'origine

        # Concaténer les résultats
        summary = "。".join(sentences[i] for i in top_indices) + "。"
        return summary

    # Texte de test
    test_text = """
Le traitement du langage naturel est une branche importante de l'intelligence artificielle. Il étudie comment permettre aux ordinateurs de comprendre et de générer le langage humain.
Cette technologie a de nombreuses applications, notamment la traduction automatique, l'analyse de sentiments, les systèmes de questions-réponses, et plus encore.
Ces dernières années, l'émergence des grands modèles de langage a apporté des avancées majeures dans le traitement du langage naturel.
Le site web EXAMPLE propose de nombreux excellents tutoriels de programmation pour aider les développeurs à apprendre de nouvelles compétences.
"""


    print("Texte original :")
    print(test_text.strip())
    print()

    summary = extractive_summary(test_text, num_sentences=2)
    print("Résumé extractif :")
    print(summary)


if __name__ == "__main__":
    rouge_demo()
    print("=" * 50)
    simple_extractive_summarization()
Autres extensions