Passer au contenu principal
Il s’agit d’un notebook interactif. Vous pouvez l’exécuter localement ou utiliser les liens ci-dessous :

Synthèse avec Chain of Density

Résumer des documents techniques complexes tout en préservant les détails essentiels est une tâche difficile. La technique de synthèse Chain of Density (CoD) offre une solution en affinant les résumés de façon itérative pour les rendre plus concis et plus riches en informations. Ce guide montre comment implémenter CoD avec Weave pour suivre et évaluer l’application.
Tableau de bord d’évaluation Weave avec les résultats de synthèse Chain of Density, les métriques et les comparaisons de performances

Qu’est-ce que la méthode de synthèse Chain of Density ?

arXiv Chain of Density (CoD) est une technique de synthèse itérative qui produit des résumés de plus en plus concis et riches en informations. Son fonctionnement repose sur les étapes suivantes :
  1. Partir d’un résumé initial
  2. Affiner le résumé de manière itérative pour le rendre plus concis tout en préservant les informations clés
  3. Augmenter la densité des entités et des détails techniques à chaque itération
Cette approche est particulièrement utile pour résumer des articles scientifiques ou des documents techniques, lorsqu’il est essentiel de préserver des informations détaillées.

Pourquoi utiliser Weave ?

Dans ce tutoriel, nous allons utiliser Weave pour implémenter et évaluer un pipeline de synthèse Chain of Density pour des articles arXiv. Vous apprendrez à :
  1. Suivre votre pipeline LLM : utilisez Weave pour journaliser automatiquement les entrées, les sorties et les étapes intermédiaires de votre processus de synthèse.
  2. Évaluer les sorties du LLM : créez des évaluations rigoureuses et directement comparables de vos synthèses à l’aide des outils intégrés de Weave.
  3. Créer des opérations composables : combinez et réutilisez les opérations Weave dans différentes parties de votre pipeline de synthèse.
  4. Intégrer facilement : ajoutez Weave à votre code Python existant avec très peu de surcoût.
À la fin de ce tutoriel, vous aurez créé un pipeline de synthèse CoD qui exploite les capacités de Weave pour le serving de modèles, l’évaluation et le suivi des résultats.

Configurer l’environnement

Tout d’abord, configurons notre environnement et importons les bibliothèques nécessaires :
!pip install -qU anthropic weave pydantic requests PyPDF2 set-env-colab-kaggle-dotenv
Pour obtenir une clé API Anthropic :
  1. Créez un compte sur https://www.anthropic.com
  2. Accédez à la section API dans les paramètres de votre compte
  3. Générez une nouvelle clé API
  4. Stockez la clé API en lieu sûr dans votre fichier .env
import io
import os
from datetime import datetime, timezone

import anthropic
import requests
from pydantic import BaseModel
from PyPDF2 import PdfReader
from set_env import set_env

import weave

set_env("WANDB_API_KEY")
set_env("ANTHROPIC_API_KEY")

weave.init("summarization-chain-of-density-cookbook")
anthropic_client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
Nous utilisons Weave pour suivre notre expérience, et le modèle Claude d’Anthropic pour générer du texte. L’appel weave.init(<project name>) initialise un nouveau projet Weave pour cette tâche de synthèse.

Définir le modèle ArxivPaper

Nous allons créer une classe ArxivPaper simple pour représenter nos données :
# Définir le modèle ArxivPaper
class ArxivPaper(BaseModel):
    entry_id: str
    updated: datetime
    published: datetime
    title: str
    authors: list[str]
    summary: str
    pdf_url: str

# Créer un exemple d'ArxivPaper
arxiv_paper = ArxivPaper(
    entry_id="http://arxiv.org/abs/2406.04744v1",
    updated=datetime(2024, 6, 7, 8, 43, 7, tzinfo=timezone.utc),
    published=datetime(2024, 6, 7, 8, 43, 7, tzinfo=timezone.utc),
    title="CRAG -- Comprehensive RAG Benchmark",
    authors=["Xiao Yang", "Kai Sun", "Hao Xin"],  # Tronqué par souci de concision
    summary="Retrieval-Augmented Generation (RAG) has recently emerged as a promising solution...",  # Tronqué
    pdf_url="https://arxiv.org/pdf/2406.04744",
)
Cette classe regroupe les métadonnées et le contenu d’un article ArXiv, qui serviront d’entrée à notre pipeline de synthèse.

Charger le contenu du PDF

Pour travailler avec le contenu intégral du document, nous allons ajouter une fonction permettant de charger et d’extraire le texte des PDF :
@weave.op()
def load_pdf(pdf_url: str) -> str:
    # Télécharger le PDF
    response = requests.get(pdf_url)
    pdf_file = io.BytesIO(response.content)

    # Lire le PDF
    pdf_reader = PdfReader(pdf_file)

    # Extraire le texte de toutes les pages
    text = ""
    for page in pdf_reader.pages:
        text += page.extract_text()

    return text

Implémenter la synthèse Chain of Density

Implémentons maintenant la logique principale de la synthèse CoD à l’aide des opérations Weave :
Visualisation de trace Weave avec exécution du pipeline de synthèse Chain of Density
# Synthèse par Chain of Density
@weave.op()
def summarize_current_summary(
    document: str,
    instruction: str,
    current_summary: str = "",
    iteration: int = 1,
    model: str = "claude-3-sonnet-20240229",
):
    prompt = f"""
    Document: {document}
    Current summary: {current_summary}
    Instruction to focus on: {instruction}
    Iteration: {iteration}

    Generate an increasingly concise, entity-dense, and highly technical summary from the provided document that specifically addresses the given instruction.
    """
    response = anthropic_client.messages.create(
        model=model, max_tokens=4096, messages=[{"role": "user", "content": prompt}]
    )
    return response.content[0].text

@weave.op()
def iterative_density_summarization(
    document: str,
    instruction: str,
    current_summary: str,
    density_iterations: int,
    model: str = "claude-3-sonnet-20240229",
):
    iteration_summaries = []
    for iteration in range(1, density_iterations + 1):
        current_summary = summarize_current_summary(
            document, instruction, current_summary, iteration, model
        )
        iteration_summaries.append(current_summary)
    return current_summary, iteration_summaries

@weave.op()
def final_summary(
    instruction: str, current_summary: str, model: str = "claude-3-sonnet-20240229"
):
    prompt = f"""
    Given this summary: {current_summary}
    And this instruction to focus on: {instruction}
    Create an extremely dense, final summary that captures all key technical information in the most concise form possible, while specifically addressing the given instruction.
    """
    return (
        anthropic_client.messages.create(
            model=model, max_tokens=4096, messages=[{"role": "user", "content": prompt}]
        )
        .content[0]
        .text
    )

@weave.op()
def chain_of_density_summarization(
    document: str,
    instruction: str,
    current_summary: str = "",
    model: str = "claude-3-sonnet-20240229",
    density_iterations: int = 2,
):
    current_summary, iteration_summaries = iterative_density_summarization(
        document, instruction, current_summary, density_iterations, model
    )
    final_summary_text = final_summary(instruction, current_summary, model)
    return {
        "final_summary": final_summary_text,
        "accumulated_summary": current_summary,
        "iteration_summaries": iteration_summaries,
    }
Voici ce que fait chaque fonction :
  • summarize_current_summary : Génère une itération unique de la synthèse à partir de l’état actuel.
  • iterative_density_summarization : Applique la technique CoD en appelant summarize_current_summary plusieurs fois.
  • chain_of_density_summarization : Orchestre l’ensemble du processus de synthèse et renvoie les résultats.
En utilisant les décorateurs @weave.op(), nous permettons à Weave de suivre les entrées, les sorties et l’exécution de ces fonctions.

Créer un Weave Model

Maintenant, encapsulons notre pipeline de synthèse dans un Weave Model :
Interface de configuration d’un Weave Model pour la synthèse Chain of Density, avec les paramètres et la configuration du modèle
# Weave Model
class ArxivChainOfDensityPipeline(weave.Model):
    model: str = "claude-3-sonnet-20240229"
    density_iterations: int = 3

    @weave.op()
    def predict(self, paper: ArxivPaper, instruction: str) -> dict:
        text = load_pdf(paper.pdf_url)
        result = chain_of_density_summarization(
            text,
            instruction,
            model=self.model,
            density_iterations=self.density_iterations,
        )
        return result
Cette classe ArxivChainOfDensityPipeline encapsule notre logique de synthèse sous la forme d’un Weave Model, avec plusieurs avantages clés :
  1. Suivi automatique des expériences : Weave capture les entrées, les sorties et les paramètres pour chaque run du modèle.
  2. Gestion des versions : les modifications apportées aux attributs ou au code du modèle sont automatiquement versionnées, ce qui crée un historique clair de l’évolution de votre pipeline de synthèse au fil du temps.
  3. Reproductibilité : la gestion des versions et le suivi permettent de reproduire facilement tout résultat ou toute configuration antérieure de votre pipeline de synthèse.
  4. Gestion des hyperparamètres : les attributs du modèle (comme model et density_iterations) sont clairement définis et suivis sur différents runs, ce qui facilite l’expérimentation.
  5. Intégration avec l’écosystème Weave : l’utilisation de weave.Model permet une intégration fluide avec d’autres outils Weave, comme les évaluations et les fonctionnalités de serving.

Mettre en place des métriques d’évaluation

Pour évaluer la qualité de nos résumés, nous allons mettre en place des métriques d’évaluation simples :
import json

@weave.op()
def evaluate_summary(
    summary: str, instruction: str, model: str = "claude-3-sonnet-20240229"
) -> dict:
    prompt = f"""
    Summary: {summary}
    Instruction: {instruction}

    Evaluate the summary based on the following criteria:
    1. Relevance (1-5): How well does the summary address the given instruction?
    2. Conciseness (1-5): How concise is the summary while retaining key information?
    3. Technical Accuracy (1-5): How accurately does the summary convey technical details?

    Your response MUST be in the following JSON format:
    {{
        "relevance": {{
            "score": <int>,
            "explanation": "<string>"
        }},
        "conciseness": {{
            "score": <int>,
            "explanation": "<string>"
        }},
        "technical_accuracy": {{
            "score": <int>,
            "explanation": "<string>"
        }}
    }}

    Ensure that the scores are integers between 1 and 5, and that the explanations are concise.
    """
    response = anthropic_client.messages.create(
        model=model, max_tokens=1000, messages=[{"role": "user", "content": prompt}]
    )
    print(response.content[0].text)

    eval_dict = json.loads(response.content[0].text)

    return {
        "relevance": eval_dict["relevance"]["score"],
        "conciseness": eval_dict["conciseness"]["score"],
        "technical_accuracy": eval_dict["technical_accuracy"]["score"],
        "average_score": sum(eval_dict[k]["score"] for k in eval_dict) / 3,
        "evaluation_text": response.content[0].text,
    }
Ces fonctions d’évaluation utilisent le modèle Claude pour évaluer la qualité des résumés générés selon des critères de pertinence, de concision et de précision technique.

Créer un jeu de données Weave et lancer une évaluation

Pour évaluer notre pipeline, nous allons créer un jeu de données Weave et lancer une évaluation :
Interface de configuration d’un jeu de données Weave pour l’évaluation, avec sélection du jeu de données et options de configuration
# Créer un jeu de données Weave
dataset = weave.Dataset(
    name="arxiv_papers",
    rows=[
        {
            "paper": arxiv_paper,
            "instruction": "Quelle était l'approche pour expérimenter différents mélanges de données ?",
        },
    ],
)

weave.publish(dataset)
Pour notre évaluation, nous utiliserons une approche de type LLM-as-a-judge. Cette technique consiste à utiliser un modèle de langage pour évaluer la qualité des résultats générés par un autre modèle ou système. Elle s’appuie sur les capacités de compréhension et de raisonnement du LLM pour fournir des évaluations nuancées, en particulier pour les tâches où les métriques traditionnelles peuvent atteindre leurs limites. arXiv
Tableau de bord d’évaluation Weave avec les résultats de synthèse Chain of Density, les métriques et les comparaisons de performances
# Définir la fonction scorer
@weave.op()
def quality_scorer(instruction: str, output: dict) -> dict:
    result = evaluate_summary(output["final_summary"], instruction)
    return result
python
# Lancer l'évaluation
evaluation = weave.Evaluation(dataset=dataset, scorers=[quality_scorer])
arxiv_chain_of_density_pipeline = ArxivChainOfDensityPipeline()
results = await evaluation.evaluate(arxiv_chain_of_density_pipeline)
Ce code crée un jeu de données à partir de notre article ArXiv d’exemple, définit un scorer de qualité et exécute une évaluation de notre pipeline de synthèse.

Conclusion

Dans cet exemple, nous avons montré comment mettre en œuvre un pipeline de synthèse Chain of Density pour des articles arXiv avec Weave. Nous avons montré comment :
  1. Créer des opérations Weave pour chaque étape du processus de synthèse
  2. Encapsuler le pipeline dans un Weave Model pour faciliter le suivi et l’évaluation
  3. Mettre en œuvre des métriques d’évaluation personnalisées à l’aide d’opérations Weave
  4. Créer un jeu de données et exécuter une évaluation du pipeline
L’intégration fluide de Weave nous permet de suivre les entrées, les sorties et les étapes intermédiaires tout au long du processus de synthèse, ce qui facilite le débogage, l’optimisation et l’évaluation de notre application LLM. Vous pouvez étendre cet exemple pour traiter des jeux de données plus volumineux, mettre en œuvre des métriques d’évaluation plus sophistiquées ou l’intégrer à d’autres flux de travail LLM. Voir le Report complet sur W&B