Comprenez le fonctionnement de Spark

Plongez dans l’architecture de Spark

Pour bien comprendre comment Spark fonctionne, il faut d’abord explorer son architecture. Heureusement, puisque nous avons dĂ©jĂ  vu ensemble les diffĂ©rents composants du calcul distribuĂ©, vous allez tout de suite vous y retrouver ! Il suffit de donner un nouveau nom Ă  chacun des concepts, et hop, je vous prĂ©sente l’équipe de travail Spark. Cette Ă©quipe est composĂ©e de trois acteurs principaux : le Driver, les Executors, et le Cluster Manager. Chacun joue un rĂŽle clĂ© pour que tout fonctionne de maniĂšre fluide et efficace. 🚀 

Vous vous souvenez du nƓud maĂźtre, qui coordonne et distribue les tĂąches? Chez Spark, il s'appelle le Driver. Il agit comme le cerveau de l’application, transformant votre code en un plan d’exĂ©cution, qui prend la forme d’un graphe orientĂ© acyclique , ou Directed Acyclic Graph (DAG). Nous reviendrons plus prĂ©cisĂ©ment sur ce point dans la section suivante.

Allez, assez parlĂ© du Driver, passons aux Executors. Ce sont les travailleurs du cluster (les nƓuds esclaves), exĂ©cutant les tĂąches assignĂ©es et retournant les rĂ©sultats au Driver. 

Enfin, le petit dernier, vous l’aurez sĂ»rement un peu moins attendu, j’ai nommĂ© le Cluster Manager. Souvenez-vous, dans la premiĂšre partie de ce cours, je vous disais:

Le nƓud maĂźtre doit donc surveiller constamment la mĂ©moire disponible et ajuster les allocations en consĂ©quence.

Et bien, pour Spark, ce n’est pas au noeud maĂźtre de se soucier de cela, mais au Cluster Manager. Il joue le rĂŽle de gestionnaire des ressources, allouant la mĂ©moire et le CPU nĂ©cessaires aux Executors et au Driver, tout en surveillant l’état du cluster. Ensemble, ces composants permettent Ă  Spark de parallĂ©liser les calculs et de traiter efficacement des volumes massifs de donnĂ©es.

Voici donc ce qui se passe Ă  chaque fois que Spark distribue un calcul pour vous:

  1. Le Driver reçoit votre programme et le transforme en un DAG.

  2. Le Cluster Manager alloue les ressources nécessaires (mémoire, CPU) aux Executors.

  3. Les Executors exécutent les tùches assignées par le Driver et renvoient les résultats.

  4. Le Driver collecte les résultats et les retourne à votre programme.

Avec PySpark, votre point d'entrĂ©e pour manipuler cette architecture est l’objet SparkSession. Voici comment le configurer :

from pyspark.sql import SparkSession

# Créer une SparkSession avec une configuration
spark = SparkSession.builder \
    .appName("MonAppSpark") \  # Nom de l'application
    .master("local[*]") \      # Mode d'exĂ©cution (local ou cluster) (ici, local avec tous les cƓurs disponibles) (vous pouvez mettre un chiffre Ă  la place de l’étoile pour indiquer le nombre de coeurs Ă  utiliser si vous prĂ©fĂ©rez)
    
    # Configuration de la mémoire
    .config("spark.driver.memory", "4g") \  # Mémoire allouée au driver
    .config("spark.executor.memory", "8g") \  # Mémoire allouée à chaque executor
# Vous pouvez ajouter Ă  cela autant de points de config que vous souhaitez.
# Quand vous avez mis toutes les points de config souhaités, créer ou récupérer la session Spark
    .getOrCreate()

Pas de souci si vous ne connaissez pas encore les RDD ! On va découvrir ça ensemble.

Découvrez le DAG et les RDD, les piliers de la performance

Maintenant que vous connaissez les acteurs principaux de l’architecture Spark, plongeons dans son fonctionnement interne. Spark repose sur deux concepts clĂ©s : le DAG (Directed Acyclic Graph) et les RDD (Resilient Distributed Datasets). Ces deux Ă©lĂ©ments sont les piliers qui permettent Ă  Spark d’ĂȘtre aussi rapide et efficace. 🚀

Le DAG (ou graphe orientĂ© acyclique) est le plan d’exĂ©cution que Spark crĂ©e pour organiser les tĂąches de votre programme.

Voici un exemple de DAG:

Schéma composé de trois blocs numérotés (1 en vert, 2 en jaune, 3 en rose) reliés par des flÚches indiquant une séquence ou un flux de processus : le bloc 1 pointe vers le bloc 2, qui pointe ensuite vers le bloc 3.
Exemple d'un DAG (ou graphe orienté acyclique)

Le graphe acyclique est reprĂ©sentĂ© par les rectangles et les flĂšches que vous voyez ici. On peut Ă©galement parler de nƓuds et d'arĂȘtes. On voit bien qu’il y a un sens Ă  respecter, le graphe est donc orientĂ©. Et le plus important
 on ne peut pas tourner en rond ! Bah oui, vous ne voudriez pas que vos nƓuds fassent la mĂȘme chose en boucle sans s’arrĂȘter đŸ˜±. Du coup, tout s’arrĂȘte quand on arrive Ă  l'Ă©tape 3 😌. Le graphe doit absolument ĂȘtre sans cycle (acyclique) pour que tout se passe bien. Au final, c’est comme une recette de cuisine : chaque Ă©tape est une tĂąche Ă  accomplir, et les flĂšches reprĂ©sentent l’ordre dans lequel ces tĂąches doivent ĂȘtre exĂ©cutĂ©es.

Pourquoi est-il si important ?

  • Il permet Ă  Spark d’optimiser l’exĂ©cution des tĂąches en Ă©vitant les calculs redondants.

  • Il garantit que les tĂąches sont exĂ©cutĂ©es dans le bon ordre, sans cycles ni boucles infinies (d’oĂč le terme acyclique).

  • Il facilite la tolĂ©rance aux pannes : si une tĂąche Ă©choue, Spark peut la rejouer Ă  partir du DAG.

Les RDD sont la structure de donnĂ©es fondamentale de Spark. Ils reprĂ©sentent des collections d’objets distribuĂ©es sur le cluster et immutables (c’est-Ă -dire qu’ils ne peuvent pas ĂȘtre modifiĂ©s aprĂšs leur crĂ©ation). Chaque RDD est divisĂ© en partitions, ce qui permet Ă  Spark de parallĂ©liser les calculs.

Schéma expliquant la répartition des partitions et des executors dans un processus de calcul distribué. Quatre partitions contenant des mots sont assignées à deux executors. Une note précise que chaque executor traite deux partitions.
Schéma des RDD divisés en partitions

Et voici comment faire des partitions avec votre RDD :

from pyspark import SparkContext, SparkConf
conf = SparkConf().setAppName(“appName”).setMaster(“Local”)
sc = SparkContext(conf=conf)

rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], numSlices=4)  # Crée 4 partitions
rdd_repartitioned = rdd.repartition(2)  # Réduit à 2 partitions

A ce stade, nous comprenons donc que Spark va utiliser le DAG comme une recette de cuisine lui permettant de savoir quoi faire, étape par étape, pour chaque partition du RDD.

Avant cela cependant, pour faire vos RDD, vous aurez surtout besoin de charger vos donnĂ©es avec Spark. Allez, voyons ça ensemble ! 📈

Chargez vos données avec Spark

Que vous travailliez avec des fichiers CSV, JSON, Parquet ou mĂȘme des bases de donnĂ©es SQL, Spark a tout prĂ©vu pour vous đŸ€“. Ces formats couvrent la grande majoritĂ© des besoins, que vos donnĂ©es soient structurĂ©es ou semi-structurĂ©es, et ils sont donc tous inclus dans Spark.

Pour l'essentiel, voici comment vous pouvez facilement charger vos données avec Spark:

Vos données CSV:

# Charger un fichier CSV
csv_df = spark.read.csv("chemin/vers/fichier.csv", header=True, inferSchema=True)

Vos données JSON:

# Charger un fichier JSON
json_df = spark.read.json("chemin/vers/fichier.json")

Vos données Parquet:

# Charger un fichier Parquet
parquet_df = spark.read.parquet("chemin/vers/fichier.parquet")

Vos données SQL:

csv_df.createOrReplaceTempView("ma_table")
result = spark.sql("SELECT colonne, AVG(valeur) FROM ma_table GROUP BY colonne")
result.show()

 Maintenant que vos donnĂ©es sont chargĂ©es dans un DataFrame, il est temps de leur donner vie grĂące aux transformations et aux actions qui façonneront votre DAG. PrĂȘt Ă  explorer ces opĂ©rations clĂ©s pour orchestrer l'exĂ©cution de votre workflow Spark ? Allons-y ! 🚀

Comprendre les Transformations et Actions

Plus prĂ©cisĂ©ment, les transformations sont les opĂ©rations qui dĂ©finissent les Ă©tapes du DAG. Elles dĂ©crivent comment les donnĂ©es doivent ĂȘtre modifiĂ©es ou transformĂ©es, mais elles ne sont pas exĂ©cutĂ©es immĂ©diatement. Chaque transformation crĂ©e un nouveau RDD (ou Dataframe / Dataset) Ă  partir d'un RDD existant. Et oui, souvenez-vous, un RDD est immuable et donc non modifiable par une transformation ! đŸ€“

Voici quelques exemples de transformations :

  • map: Applique une fonction Ă  chaque Ă©lĂ©ment du RDD.

  • filter: Filtre les Ă©lĂ©ments selon une condition.

  • groupBy: Regroupe les Ă©lĂ©ments par clĂ©.

  • join: Combine deux RDD en fonction d'une clĂ© commune.

Les actions sont les opérations qui déclenchent l'exécution du DAG. Elles demandent à Spark de calculer un résultat et de le retourner au programme. Les actions ne font pas partie des étapes du DAG, mais elles sont le point de départ de son exécution.

Exemples d'actions :

  • count: Compte le nombre d'Ă©lĂ©ments dans le RDD.

  • collect: RĂ©cupĂšre tous les Ă©lĂ©ments du RDD sur le Driver.

  • saveAsTextFile: Sauvegarde les Ă©lĂ©ments du RDD dans un fichier.

  • show: Affiche les Ă©lĂ©ments du RDD (ou DataFrame).

Mais pourquoi le DAG ne s'exécute-t-il pas immédiatement ?

Parce qu’il est comme vous, fin moi, fin
 bref, fainĂ©ant quoi ! Spark utilise une Ă©valuation dite “paresseuse” (lazy evaluation). Autrement dit, si on ne lui dit pas clairement et explicitement de s’activer
, bah il 
 rĂ©flĂ©chit đŸ€”. En effet, Spark passe son temps Ă  essayer d'optimiser le DAG en regroupant les transformations et en Ă©vitant le plus possible les calculs inutiles. Donc sa fainĂ©antise nous est bien utile, alors que la nĂŽtre
 😕 😉.

Voici un petit code illustrant tout ce que nous venons d’énoncer:

# Créer un RDD à partir d'une liste de nombres
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

# Appliquer des transformations
rdd_mapped = rdd.map(lambda x: x * 2)       # Transformation 1 : Multiplier chaque élément par 2
rdd_filtered = rdd_mapped.filter(lambda x: x > 10)  # Transformation 2 : Filtrer les éléments > 10
rdd_grouped = rdd_filtered.groupBy(lambda x: x % 2)  # Transformation 3 : Grouper par pair/impair

# Appeler une action
result = rdd_grouped.count()  # Action : Compter le nombre de groupes

Chaque Ă©tape (map, filter, groupBy) est un nƓud dans le DAG, et l'action count dĂ©clenche l'exĂ©cution du DAG.

Manipulez des images avec PySpark

Que diriez-vous d’un petit exercice? Super idĂ©e ! 🚀 (Oui, je sais
 😉) Rien de mieux qu’un petit exercice pratique pour se familiariser avec PySpark. Et pour l’occasion, prenons l’un des jeux de donnĂ©es les plus connus de Kaggle, Ă  savoir: Dogs vs. Cats. 📾✹

Voici nos objectifs:

  • Charger des images de chats et de chiens.

  • Extraire des caractĂ©ristiques Ă  l’aide d’un modĂšle de machine learning prĂ©-entraĂźnĂ© (transfert learning).

  • EntraĂźner un modĂšle de classification automatique pour prĂ©dire si l’image contient un chat ou un chien.

Notez les cellules sous les sous-titres “calcul distribuĂ©â€. Et bien c’est lĂ  que la “magie” de Spark entre en jeu. Et je reviendrai plus en dĂ©tails sur ce qui se passe dans les parties suivantes de ce cours. Mais voilĂ , vous avez rĂ©alisĂ© votre premier calcul distribuĂ© !

Reverrons le code ensemble dans ce screencast :

Vous voyez, rien de compliquĂ© ! Si vous avez l’habitude de faire du machine learning en python, et bien vous n’avez quasiment rien Ă  changer par rapport Ă  ce que vous savez dĂ©jĂ  faire. C’est l’un des avantages de PySpark, c’est de vous permettre de faire du calcul distribuĂ© sans que vous ayez Ă  y rĂ©flĂ©chir plus que ça.

Allez, pour cĂ©lĂ©brer l’occasion, je vous propose de refaire exactement la mĂȘme chose mais avec vos scanners mĂ©dicaux. PrĂȘt ?

À vous de jouer

Vous vous souvenez de l'hĂŽpital qui souhaite centraliser l’analyse des scans mĂ©dicaux. Et bien, il vient de vous contacter pour faire un premier poc pour prouver votre capacitĂ© Ă  catĂ©goriser automatiquement ces scans. Au passage, faites vous la main en identifiant les diffĂ©rentes Ă©tapes du DAG et changez le nombre de partitions pour voir ce qui change.

Allez, au travail !

Voici les étapes pour réussir :

  1. Trouvez un dataset. Vous pouvez utiliser un dataset public de scanners médicaux comme Chest X-Ray Images (pour des radiographies de la poitrine) par exemple.

  2. Téléchargez le dataset et chargez une petite partie des images dans votre environnement de travail.

  3. Utilisez PySpark pour distribuer le chargement des images ainsi que toute autre Ă©tape du travail qui pourrait constituer un goulot d’étranglement lorsque le volume d’image augmentera.

  4. Vous pouvez utiliser un modĂšle simple comme une rĂ©gression logistique ou un arbre de dĂ©cision avec les caractĂ©ristiques extraites. Pensez Ă  diviser les donnĂ©es en jeux d’entraĂźnement et de test. 

Voici un indice : vous aurez besoin de cet import supplémentaire =>

from pyspark.ml.feature import PCA
# écrivez votre code ici pour compresser les images

Maintenant, relancer le notebook que vous avez rĂ©alisĂ© plus tĂŽt pour classifier automatiquement des scanners mĂ©dicaux et essayer d’identifier les diffĂ©rentes Ă©tapes du DAG. Changer le nombre de partitions et voyez ce qui change.

Pourquoi ne pas essayer de compresser vos images à l’aide d’une ACP avant de les sauvegarder ? Là aussi, voyez ce qui change en termes de DAG.

En résumé

  • L’architecture de Spark repose sur trois acteurs principaux : le Driver (coordination), les Executors (exĂ©cution des tĂąches), et le Cluster Manager (gestion des ressources).

  • Le DAG est le plan d’exĂ©cution de Spark, qui permet d’organiser les tĂąches de maniĂšre efficace et d’optimiser les calculs.

  • Les RDD sont la structure de donnĂ©es fondamentale de Spark, immuables et distribuĂ©es en partitions pour faciliter le traitement parallĂšle.

  • Spark utilise des transformations (dĂ©finition des Ă©tapes) et des actions (dĂ©clenchement des calculs) avec une Ă©valuation paresseuse pour maximiser les performances.

Et voilĂ , vous maĂźtrisez maintenant les rouages internes de Spark ! Prochaine Ă©tape : vos gains de performances ! 🚀 

Et si vous obteniez un diplĂŽme OpenClassrooms ?
  • Formations jusqu’à 100 % financĂ©es
  • Date de dĂ©but flexible
  • Projets professionnalisants
  • Mentorat individuel
Trouvez la formation et le financement faits pour vous