Apache Spark et Python pour le big data et le machine learning
Apache Spark est reconnu comme un moteur généraliste, rapide et simple d'utilisation pour le traitement des big data, avec des modules intégrés pour le streaming, SQL, le machine learning (ML) et le traitement de graphes. Cette technologie est très recherchée par les data engineers, mais les data scientists y trouvent également un intérêt pour la phase d'exploration (EDA), l'extraction de variables et, bien sûr, le ML.
Dans ce tutoriel, vous utiliserez Spark avec Python via PySpark, l'API Python de Spark qui expose le modèle de programmation Spark à Python. Concrètement, vous vous concentrerez sur :
L'installation de Spark et sa mise en route peuvent représenter un défi. Dans cette section, vous suivrez des étapes pour l'installer sur votre ordinateur.
Commencez par vérifier les prérequis. Spark est écrit en Scala et s'exécute sur la machine virtuelle Java (JVM). Vérifiez donc si un Java Development Kit (JDK) est installé. Le JDK fournit une ou plusieurs implémentations de la JVM. Idéalement, choisissez la version la plus récente ; au moment de la rédaction, il s'agissait du JDK8.
Ensuite, vous pouvez télécharger Spark !
Télécharger pyspark avec pip
Vous pouvez télécharger et installer PySpark avec pip. C'est très simple, comme pour n'importe quel autre package. Exécutez la commande habituelle et laissez l'outil faire le travail :
Sinon, rendez-vous sur la page de téléchargement de Spark. Conservez les options par défaut pour les trois premières étapes et vous obtiendrez un lien téléchargeable à l'étape 4. Cliquez sur ce lien pour télécharger. Pour ce tutoriel, téléchargez Spark 2.2.0 et le package « Pre-built for Apache Hadoop 2.7 and later ».
Remarque : le téléchargement peut prendre du temps !
Télécharger Spark avec Homebrew
Vous pouvez également installer Spark avec Homebrew, un gestionnaire de paquets libre et open source. C'est particulièrement pratique sous macOS.
Exécutez simplement les commandes suivantes pour chercher Spark, obtenir des informations, puis l'installer sur votre ordinateur :
# Rechercher spark
$ brew search spark
# Obtenir des infos sur apache-spark
$ brew info apache-spark
# Installer apache-spark
$ brew install apache-sparkPropulsé par
Télécharger et configurer Spark
Décompressez ensuite l'archive apparue dans votre dossier Downloads. Cela peut se faire automatiquement en double-cliquant sur spark-2.2.0-bin-hadoop2.7.tgz ou via le Terminal avec la commande :
$ tar xvf spark-2.2.0-bin-hadoop2.7.tgzPropulsé par
Puis, déplacez le dossier extrait vers /usr/local/spark avec :
$ mv spark-2.1.0-bin-hadoop2.7 /usr/local/sparkPropulsé par
Remarque : si vous obtenez une erreur de permission lors du déplacement, préfixez la commande par sudo. La ligne devient alors $ sudo mv spark-2.1.0-bin-hadoop2.7 /usr/local/spark. Il vous sera demandé votre mot de passe, généralement celui que vous utilisez pour ouvrir votre session.
Vous êtes prêt : ouvrez le fichier README dans /usr/local/spark. Pour cela, exécutez
Vous arrivez ainsi dans le bon répertoire. Vous pouvez ensuite lister son contenu et lire le README inclus.
Utilisez d'abord $ ls pour afficher les fichiers et dossiers du dossier spark. Vous verrez un fichier README.md. Ouvrez-le avec l'une des commandes suivantes :
# Ouvrir et éditer le fichier
$ nano README.md
# Simplement lire le fichier
$ cat README.mdPropulsé par
Astuce : utilisez la touche Tab pour l'autocomplétion des noms de fichiers ; cela vous fera gagner du temps.
Ce README fournit des informations générales sur Spark, la documentation en ligne, la construction de Spark, les shells interactifs Scala et Python, des exemples, etc.
La partie la plus utile ici peut être la section sur la construction de Spark, mais elle ne concerne vraiment que les versions non précompilées. Pour ce tutoriel, vous avez téléchargé une version précompilée. Appuyez sur CTRL + X pour quitter le README et revenir au dossier spark.
Si vous avez choisi une version non construite, exécutez la commande indiquée dans le README. Au moment de la rédaction, c'était :
$ build/mvn -DskipTests clean package runPropulsé par
Notez que cette commande peut être longue.
Bases de PySpark : RDD
Maintenant que Spark et PySpark sont installés, commençons par explorer le shell interactif et ancrer quelques notions de base indispensables. Pour la suite du tutoriel, vous utiliserez PySpark dans un notebook Jupyter.
Applications Spark vs shell Spark
Le shell interactif est un environnement de type REPL (Read-Eval-Print-Loop) : tout ce que vous tapez est lu, exécuté puis affiché pour que vous puissiez enchaîner vos analyses. Cela rappelle IPython, le shell Python interactif que vous utilisez peut-être avec Jupyter. Pour en savoir plus, lisez l'article de DataCamp IPython or Jupyter.
Vous pouvez utiliser ce shell, disponible pour Python et Scala, pour tout travail interactif.
Outre le shell, vous pouvez écrire et déployer des applications Spark. Contrairement à une application, le SparkSession est déjà créé dans le shell pour vous faire gagner du temps.
Vous vous demandez peut-être : qu'est-ce que SparkSession ?
C'est le point d'entrée principal de Spark : il représente la connexion à un cluster Spark et permet de créer des RDD et de diffuser des variables sur ce cluster. Avec Spark, tout commence et finit avec ce SparkSession. Remarque : avant Spark 2.0.0, les objets de connexion principaux étaient SparkContext, SqlContext et HiveContext.
Vous en verrez plus par la suite. Concentrez-vous pour l'instant sur le shell.
Le shell Spark pour Python
Depuis le dossier spark situé en /usr/local/spark, lancez :
Du texte s'affiche, puis « Spark » apparaît, comme ceci :
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 172016, 12:39:47)[GCC 4.2.1 (Apple Inc. build 5666)(dot 3)] on darwin
Type "help", "copyright", "credits" or "license"formore information.
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
17/07/26 11:41:26 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
17/07/26 11:41:47 WARN ObjectStore: Failed to get database global_temp, returning NoSuchObjectException
Welcome to
____ __
/ __/__ ___ _____/ /__
_\ \/ _ \/ _ `/ __/ '_/
/__ / .__/\_,_/_/ /_/\_\ version 2.2.0
/_/
Using Python version 2.7.13 (v2.7.13:a06454b1afa1, Dec 17201612:39:47)
SparkSession available as 'spark'.>>>Propulsé par
Une fois à cet écran, vous pouvez commencer à expérimenter dans le shell interactif !
Astuce : si vous préférez IPython au shell Spark, définissez la variable d'environnement suivante :
exportPYSPARK_DRIVER_PYTHON="/usr/local/ipython/bin/ipython"Propulsé par
Créer des RDD
Commençons simplement en créant un RDD, brique de base de Spark. Un RDD représente des données, mais ce n'est ni un objet unique, ni une collection d'enregistrements, ni un data set classique. Il est conçu pour des données réparties sur plusieurs machines : un RDD peut être fragmenté sur des milliers de JVM, Spark partitionnant automatiquement les données pour paralléliser le traitement. Vous pouvez bien sûr ajuster le parallélisme. Un RDD est donc une collection de partitions.
Vous pouvez créer un RDD simple avec la fonction parallelize() en lui passant des données (un itérable comme une liste, ou une collection) :
Remarque : l'objet SparkSession expose l'objet SparkContext via spark.sparkContext. Pour des raisons de compatibilité ascendante, vous pouvez encore utiliser sc, par exemple rdd1 = sc.parallelize(['a',7),('a',2),('b',2)]).
Opérations sur les RDD
Maintenant que vos RDD sont créés, vous pouvez opérer en parallèle sur les données de rdd1 et rdd2. Il existe deux types d'opérations : transformations et actions.
Pour saisir intuitivement la différence, retenez que des transformations courantes sont map(), filter(), flatMap(), sample(), randomSplit(), coalesce() et repartition() tandis que des actions fréquentes sont reduce(), collect(), first(), take(), count(), saveAsHadoopFile().
Les transformations sont paresseuses : elles créent un ou plusieurs nouveaux RDD. Les actions produisent une valeur non-RDD : résultat, nombre, fichier, …
Par exemple, agrégez tous les éléments de rdd1 avec une lambda simple et renvoyez le résultat au driver :
Exécuter cette ligne renvoie : ('a', 7, 'a', 2, 'b', 2). Autre exemple de transformation : flatMapValues() sur un RDD de paires clé-valeur comme rdd2. Ici, vous appliquez flatMap à chaque valeur sans modifier les clés (lambda ci-dessous) puis vous effectuez une action en collectant le résultat avec collect().
Après ces bases dans le shell interactif, passons à des données réelles. Pour ce tutoriel, vous utiliserez le jeu de données California Housing. Il s'agit certes de « petites » données ; utiliser Spark ici est sans doute excessif : le but est pédagogique, pour illustrer l'usage de PySpark pour construire un modèle de machine learning.
Charger et explorer vos données
Même si vous connaissez un peu votre jeu de données, prenez le temps de l'explorer plus en profondeur. Avant cela, configurez votre Jupyter Notebook avec Spark et amorcez la définition du SparkContext.
PySpark dans Jupyter Notebook
Pour cette partie, vous n'utiliserez pas le shell interactif, mais construirez votre propre application dans un Jupyter Notebook. Tout est déjà installé, vous n'avez presque rien à faire pour utiliser PySpark dans Jupyter.
Lancez l'application comme d'habitude avec $ jupyter notebook. Créez un nouveau notebook, importez la bibliothèque findspark et utilisez la fonction init(). Vous fournirez ici le chemin /usr/local/spark à init() car vous savez que Spark est installé à cet emplacement.
# Import findspark import findspark
# Initialize and provide path
findspark.init("/usr/local/spark")# Or use this alternative#findspark.init()Propulsé par
Astuce : si vous ne connaissez pas votre chemin Spark, utilisez findspark.find() pour le détecter automatiquement.
Tout est en place : créons votre premier programme Spark !
Créer votre premier programme Spark
Commencez par importer SparkSession depuis pyspark.sql et initialisez-le. Rappel : dans le shell interactif, il est créé automatiquement ! Ici, vous ferez un peu plus vous-même :
Importez le module SparkSession puis construisez-le avec builder(). Définissez l'URL du master, le nom de l'application, quelques paramètres comme la mémoire exécuteur, puis getOrCreate() pour récupérer ou créer la session.
Remarque : si vous obtenez une erreur de type FileNotFoundError du style : « No such file or directory: '/User/YourName/Downloads/spark-2.1.0-bin-hadoop2.7/./bin/spark-submit' », vous devez (ré)définir votre PATH Spark. Allez au dossier personnel avec $ cd puis éditez .bash_profile avec $ nano .bash_profile.
Quittez avec CTRL + X puis validez l'enregistrement des modifications avec Y. N'oubliez pas d'appliquer les changements avec source .bash_profile.
Astuce : vous pouvez définir d'autres variables d'environnement si besoin. Voici quelques exemples :
# Valeur fixe pour le hachageexportPYTHONHASHSEED=0# Exécutable Python alternatifexportPYSPARK_PYTHON=/usr/local/ipython/bin/ipython
# Chemin de recherche des bibliothèques partagéesexportLD_LIBRARY_PATH=/usr/local/ipython/bin/ipython
# Chemin de recherche des bibliothèques privées exportPYTHONPATH=$SPARK_HOME/python/lib/py4j-*-src.zip:$PYTHONPATH:$SPARK_HOME/python/Propulsé par
Remarque : vous avez initialisé un SparkSession par défaut. Dans la plupart des cas, vous souhaiterez le configurer davantage, surtout avec de gros volumes de données. Pour en savoir plus, consultez cette page.
Charger vos données
Ce tutoriel utilise le jeu de données California Housing, issu d'un article de 1997 intitulé Sparse Spatial Autoregressions par R. Kelley Pace et Ronald Barry, publié dans Statistics and Probability Letters. Les auteurs l'ont construit à partir du recensement californien de 1990.
Chaque ligne correspond à un groupe d'îlots de recensement, plus petite unité géographique pour laquelle le U.S. Census Bureau publie des échantillons (en général de 600 à 3 000 personnes). Dans cet échantillon, un groupe compte en moyenne 1425,5 individus sur une zone géographique compacte. Vous trouverez ces informations sur cette page ou dans l'article mentionné ici.
Ces données spatiales contiennent 20 640 observations de prix de l'immobilier avec 9 variables économiques :
Longitude : distance angulaire d'un lieu vers le nord ou le sud de l'équateur terrestre pour chaque groupe ;
Latitude : distance angulaire d'un lieu vers l'est ou l'ouest de l'équateur terrestre pour chaque groupe ;
Housing median age : âge médian des habitants du groupe. Remarque : la médiane est la valeur au centre de la distribution ;
Total rooms : nombre total de pièces des logements par groupe ;
Total bedrooms : nombre total de chambres des logements par groupe ;
Population : nombre d'habitants du groupe ;
Households : unités ménages et leurs occupants par groupe ;
Median income : revenu médian des personnes du groupe ; et
Median house value : variable dépendante, valeur médiane des logements par groupe.
Vous apprendrez également que tous les groupes avec des zéros sur les variables indépendantes et dépendantes ont été exclus.
Median house value est la variable cible de votre modèle de ML.
Téléchargez les données ici. Recherchez le dossier houses.zip, téléchargez et décompressez-le pour accéder aux fichiers.
Ensuite, utilisez textFile() pour lire les données depuis le dossier téléchargé vers des RDD. Cette méthode prend l'URI du fichier (ici un chemin local) et lit une collection de lignes. Par commodité, lisez aussi le fichier .domain contenant l'en-tête, pour vérifier l'ordre des variables.
# Load in the data
rdd = sc.textFile('/Users/yourName/Downloads/CaliforniaHousing/cal_housing.data')# Load in the header
header = sc.textFile('/Users/yourName/Downloads/CaliforniaHousing/cal_housing.domain')Propulsé par
Exploration des données
Vous avez déjà glané beaucoup d'informations via la page du jeu de données, mais rien ne remplace une inspection directe avec Spark en Python.
Important : l'exécution de Spark est paresseuse : rien n'a encore été exécuté. Les variables rdd et header ne sont que des représentations. Vous devez « pousser » Spark à travailler. Utilisez collect() pour regarder header :
Astuce : attention avec collect() ! Cela peut saturer la mémoire du driver. Préférez take() pour n'afficher que quelques éléments. En général, limitez votre jeu de résultats autant que possible, comme en SQL.
Vous confirmez l'ordre des variables vu précédemment et que toutes les colonnes sont continues. Faisons travailler Spark pour vérifier cela sur les données California Housing.
Cette commande prend les 2 premiers éléments du RDD. Le résultat est logique : en lisant avec textFile(), les lignes sont lues telles quelles. Les entrées sont séparées par des virgules et les lignes aussi :
[u'-122.230000,37.880000,41.000000,880.000000,129.000000,322.000000,126.000000,8.325200,452600.000000',u'-122.220000,37.860000,21.000000,7099.000000,1106.000000,2401.000000,1138.000000,8.301400,358500.000000']Propulsé par
Il faut corriger cela. Pas besoin de découper chaque entrée tout de suite, mais les lignes doivent devenir des éléments distincts. Utilisez map() avec une lambda qui scinde la ligne sur la virgule, puis vérifiez avec take() :
Rappel : les lambdas sont des fonctions anonymes créées à l'exécution.
# Split lines on commas
rdd = rdd.map(lambda line: line.split(","))# Inspect the first 2 lines
rdd.take(2)Propulsé par
Vous obtenez :
[[u'-122.230000',u'37.880000',u'41.000000',u'880.000000',u'129.000000',u'322.000000',u'126.000000',u'8.325200',u'452600.000000'],[u'-122.220000',u'37.860000',u'21.000000',u'7099.000000',u'1106.000000',u'2401.000000',u'1138.000000',u'8.301400',u'358500.000000']]Propulsé par
Vous pouvez aussi utiliser :
# Inspect the first line
rdd.first()# Take top elements
rdd.top(2)Propulsé par
Si vous avez l'habitude de Pandas ou des data frames en R, vous vous attendiez peut-être à un en-tête ; il n'y en a pas. Pour vous faciliter la vie, passez des RDD aux DataFrames, préférables quand ils sont disponibles, notamment en Python où ils sont plus performants.
Mais quelle est la différence ?
Utilisez des RDD pour des transformations/actions bas niveau sur des données non structurées, sans schéma ni accès par nom de colonne, et quand vous n'avez pas besoin des gains de performance des DataFrames. Privilégiez les RDD si vous manipulez les données avec des constructions fonctionnelles plutôt que des expressions spécifiques au domaine.
En résumé, passons aux DataFrames pour utiliser des expressions de haut niveau, effectuer des requêtes SQL et profiter de l'accès colonne par colonne.
Allons-y.
Première étape : créer un SchemaRDD (RDD de Row) avec un schéma. Comme pour un DataFrame, vous voulez des lignes et des colonnes, avec des types.
Utilisez encore map() et une lambda pour associer chaque entrée à un champ nommé d'une Row. Par exemple, dans la première ligne :
[u'-122.230000',u'37.880000',u'41.000000',u'880.000000',u'129.000000',u'322.000000',u'126.000000',u'8.325200',u'452600.000000']Propulsé par
La lambda construit une Row dans un SchemaRDD en nommant l'élément d'indice 0 « longitude », etc.
Avec ce SchemaRDD, convertissez facilement le RDD en DataFrame via toDF().
# Import the necessary modules from pyspark.sql import Row
# Map the RDD to a DF
df = rdd.map(lambda line: Row(longitude=line[0],
latitude=line[1],
housingMedianAge=line[2],
totalRooms=line[3],
totalBedRooms=line[4],
population=line[5],
households=line[6],
medianIncome=line[7],
medianHouseValue=line[8])).toDF()Propulsé par
Avec le DataFrame df, inspectez-le avec first(), take(), mais aussi head() et show() :
Vous voyez immédiatement une différence nette avec le RDD :
Astuce : utilisez df.columns pour obtenir la liste des colonnes.
Les colonnes semblent correctes, mais qu'en est-il des types ? Spark tente d'inférer un schéma à la lecture, mais y est-il arrivé ? Utilisez df.dtypes ou df.printSchema() pour le savoir.
# Print the data types of all `df` columns# df.dtypes# Print the schema of `df`
df.printSchema()Propulsé par
Comme la première ligne n'est pas exécutée, vous obtenez :
Toutes les colonnes sont encore en chaîne de caractères … Décevant !
Pour continuer, il faut corriger et affecter des types plus adaptés à chaque colonne, ce qui améliorera aussi les performances. Intuitivement, vous pourriez caster chaque colonne en FloatType() :
Mais ces appels répétitifs sont verbeux et propices aux erreurs. Écrivons une fonction plus propre pour tout faire :
La fonction suivante prend un DataFrame, des noms de colonnes et le nouveau type souhaité. Pour chaque colonne, elle caste vers le nouveau type puis renvoie le DataFrame :
# Import all from `sql.types`from pyspark.sql.types import*# Write a custom function to convert the data type of DataFrame columnsdefconvertColumn(df, names, newType):for name in names:
df = df.withColumn(name, df[name].cast(newType))return df
# Assign all column names to `columns`
columns =['households','housingMedianAge','latitude','longitude','medianHouseValue','medianIncome','population','totalBedRooms','totalRooms']# Conver the `df` columns to `FloatType()`
df = convertColumn(df, columns, FloatType())Propulsé par
Déjà plus lisible ! Vérifiez rapidement les types avec printSchema(), comme précédemment.
Maintenant, passons à l'exploration. L'accès colonne et les requêtes SQL sont deux atouts des DataFrames. Commençons petit : sélectionnez deux colonnes de df et affichez 10 lignes :
df.select('population','totalBedRooms').show(10)Propulsé par
Cette requête renvoie :
+----------+-------------+|population|totalBedRooms|+----------+-------------+|322.0|129.0||2401.0|1106.0||496.0|190.0||558.0|235.0||565.0|280.0||413.0|213.0||1094.0|489.0||1157.0|687.0||1206.0|665.0||1551.0|707.0|+----------+-------------+
only showing top 10 rowsPropulsé par
Vous pouvez complexifier, comme ci-dessous :
df.groupBy("housingMedianAge").count().sort("housingMedianAge",ascending=False).show()Propulsé par
Ce qui donne :
+----------------+-----+|housingMedianAge|count|+----------------+-----+|52.0|1273||51.0|48||50.0|136||49.0|134||48.0|177||47.0|198||46.0|245||45.0|294||44.0|356||43.0|353||42.0|368||41.0|296||40.0|304||39.0|369||38.0|394||37.0|537||36.0|862||35.0|824||34.0|689||33.0|615|+----------------+-----+
only showing top 20 rowsPropulsé par
Au-delà des requêtes, vous pouvez aussi décrire vos données et obtenir des statistiques résumées ; cela vous aidera pour la suite :
Observez les minimums et maximums des attributs numériques : plusieurs ont une large plage de valeurs. Vous devrez normaliser votre jeu de données.
Prétraitement des données
Avec ces premiers enseignements, vous pouvez prétraiter les données pour les injecter dans le modèle.
Pas de valeurs manquantes à gérer : les zéros ont été exclus du jeu de données.
Vous devrez probablement standardiser, car les écarts min/max sont importants.
Vous pouvez créer des attributs supplémentaires (chambres par pièce, pièces par ménage, etc.).
La variable cible a de grandes valeurs ; pour vous simplifier la vie, ajustez-les légèrement.
Prétraiter la variable cible
Commencez par medianHouseValue, votre variable dépendante. Pour faciliter le travail, exprimez les valeurs en unités de 100 000. Ainsi, 452600.000000 devient 4.526 :
# Import all from `sql.functions` from pyspark.sql.functions import*# Adjust the values of `medianHouseValue`
df = df.withColumn("medianHouseValue", col("medianHouseValue")/100000)# Show the first 2 lines of `df`
df.take(2)Propulsé par
Vous verrez clairement l'ajustement dans le résultat de take() :
Après l'ajustement de medianHouseValue, ajoutez les variables supplémentaires mentionnées :
Rooms per household : nombre de pièces par ménage pour chaque groupe ;
Population per household : nombre de personnes par ménage ; et
Bedrooms per room : proportion de chambres parmi les pièces.
Avec les DataFrames, utilisez select() pour sélectionner totalRooms, households et population. Indiquez que vous opérez sur des colonnes via col() afin d'effectuer des opérations élémentaires (division) sur ces variables :
# Import all from `sql.functions` if you haven't yetfrom pyspark.sql.functions import*# Divide `totalRooms` by `households`
roomsPerHousehold = df.select(col("totalRooms")/col("households"))# Divide `population` by `households`
populationPerHousehold = df.select(col("population")/col("households"))# Divide `totalBedRooms` by `totalRooms`
bedroomsPerRoom = df.select(col("totalBedRooms")/col("totalRooms"))# Add the new columns to `df`
df = df.withColumn("roomsPerHousehold", col("totalRooms")/col("households")) \
.withColumn("populationPerHousehold", col("population")/col("households")) \
.withColumn("bedroomsPerRoom", col("totalBedRooms")/col("totalRooms"))# Inspect the result
df.first()Propulsé par
Pour la première ligne, on obtient environ 6,98 pièces par ménage, 2,5 personnes par ménage et 0,14 chambre par pièce :
Ensuite — en anticipant un problème potentiel lors de la standardisation — réorganisez les colonnes : comme vous ne voulez pas standardiser la cible, isolez-la.
Ici, utilisez select() en passant l'ordre des colonnes souhaité. Placez medianHouseValue en premier pour éviter de l'affecter.
Remarque : c'est aussi le moment d'écarter des variables non utiles (« longitude », « latitude », « housingMedianAge », « totalRooms »).
# Re-order and select columns
df = df.select("medianHouseValue","totalBedRooms","population","households","medianIncome","roomsPerHousehold","populationPerHousehold","bedroomsPerRoom")Propulsé par
Standardisation
Une fois les colonnes réordonnées, vous pouvez normaliser. Mais avant, séparez les variables explicatives de la cible : en pratique, isolez la première colonne du reste.
Ici, vous utiliserez map() sur le RDD sous-jacent. Vous verrez aussi DenseVector(), un vecteur local basé sur un tableau de doubles, pour stocker des tableaux de valeurs en PySpark.
Ensuite, recréez un DataFrame à partir de input_data et renommez les colonnes en "label" et "features" :
# Import `DenseVector`from pyspark.ml.linalg import DenseVector
# Define the `input_data`
input_data = df.rdd.map(lambda x:(x[0], DenseVector(x[1:])))# Replace `df` with the new DataFrame
df = spark.createDataFrame(input_data,["label","features"])Propulsé par
Passez enfin à l'échelle. Utilisez Spark ML : la bibliothèque facilite et déploie le ML à grande échelle, avec algorithmes et pipelines. Ici, un pipeline serait superflu, mais consultez cette page si besoin.
La colonne d'entrée est features et la colonne de sortie dans scaled_df sera "features_scaled" :
# Import `StandardScaler` from pyspark.ml.feature import StandardScaler
# Initialize the `standardScaler`
standardScaler = StandardScaler(inputCol="features", outputCol="features_scaled")# Fit the DataFrame to the scaler
scaler = standardScaler.fit(df)# Transform the data in `df` with the scaler
scaled_df = scaler.transform(df)# Inspect the result
scaled_df.take(2)Propulsé par
Regardez le DataFrame obtenu : une troisième colonne features_scaled a été ajoutée, à comparer à features :
[Row(label=4.526, features=DenseVector([129.0,322.0,126.0,8.3252,6.9841,2.5556,0.1466]), features_scaled=DenseVector([0.3062,0.2843,0.3296,4.3821,2.8228,0.2461,2.5264])), Row(label=3.585, features=DenseVector([1106.0,2401.0,1138.0,8.3014,6.2381,2.1098,0.1558]), features_scaled=DenseVector([2.6255,2.1202,2.9765,4.3696,2.5213,0.2031,2.6851]))]Propulsé par
Remarque : ces lignes sont très proches de ce que vous feriez avec Scikit-Learn.
Construire un modèle de machine learning avec Spark ML
Le prétraitement terminé, construisons le modèle de régression linéaire ! Comme toujours, commencez par scinder les données en apprentissage et test via randomSplit() :
# Split the data into train and test sets
train_data, test_data = scaled_df.randomSplit([.8,.2],seed=1234)Propulsé par
Vous fournissez une liste avec les proportions d'apprentissage et de test, et une graine pour la reproductibilité. Pour aller plus loin, consultez le Python Machine Learning Tutorial de DataCamp.
Puis, créez le modèle !
Remarque : l'argument elasticNetParam correspond à α et le regParam (paramètre de régularisation) à λ. Plus d'infos ici.
# Import `LinearRegression`from pyspark.ml.regression import LinearRegression
# Initialize `lr`
lr = LinearRegression(labelCol="label", maxIter=10, regParam=0.3, elasticNetParam=0.8)# Fit the data to the model
linearModel = lr.fit(train_data)Propulsé par
Avec le modèle en place, générez des prédictions sur les données de test : utilisez transform() pour prédire les labels de test_data. Puis, utilisez des opérations RDD pour extraire prédictions et labels réels, et regroupez-les dans une liste predictionAndLabel.
Enfin, inspectez les valeurs prédites et réelles via l'indexation [] :
# Generate predictions
predicted = linearModel.transform(test_data)# Extract the predictions and the "known" correct labels
predictions = predicted.select("prediction").rdd.map(lambda x: x[0])
labels = predicted.select("label").rdd.map(lambda x: x[0])# Zip `predictions` and `labels` into a list
predictionAndLabel = predictions.zip(labels).collect()# Print out first 5 instances of `predictionAndLabel`
predictionAndLabel[:5]Propulsé par
Vous verrez les valeurs réelles et prédites (dans cet ordre) :
[(1.4491508524918457,0.14999),(1.5705029404692372,0.14999),(2.148727956912464,0.14999),(1.5831547768979277,0.344),(1.5182107797955968,0.398)]Propulsé par
Évaluer le modèle
Regarder des prédictions, c'est bien ; analyser des métriques, c'est mieux. Commencez par afficher les coefficients et l'ordonnée à l'origine du modèle :
# Coefficients for the model
linearModel.coefficients
# Intercept for the model
linearModel.interceptPropulsé par
Ce qui renvoie :
# The coefficients[0.0,0.0,0.0,0.276239709215,0.0,0.0,0.0]# The intercept0.990399577462Propulsé par
Ensuite, utilisez l'attribut summary pour récupérer le rootMeanSquaredError et le r2 :
# Get the RMSE
linearModel.summary.rootMeanSquaredError
# Get the R2
linearModel.summary.r2Propulsé par
Le RMSE mesure l'erreur entre une valeur prédite et une valeur observée. Plus il est faible, plus la prédiction est proche de l'observation.
Le R² (coefficient de détermination) indique la proximité des données à la droite de régression. Il varie entre 0 et 1 : 0 % signifie que le modèle n'explique aucune variabilité de la réponse autour de sa moyenne, 100 % l'inverse. En général, plus le R² est élevé, meilleur est l'ajustement.
Vous obtiendrez :
# RMSE0.8692118678997669# R20.4240895287218379Propulsé par
Votre modèle peut encore être amélioré ! Pour continuer, testez d'autres paramètres, sélections de variables, etc. Mais le tutoriel s'arrête ici pour l'instant !
Initiez-vous aux structures de données de Python : apprenez-en plus sur les types de données et les structures de données primitives et non primitives, telles que les chaînes de caractères, les listes, les piles, etc.
Découvrez le match-case de Python : un guide sur sa syntaxe, ses applications en data science, ML, et une analyse comparative avec le switch-case traditionnel.
Apache Spark et Python pour le big data et le machine learning
Apache Spark est reconnu comme un moteur généraliste, rapide et simple d'utilisation pour le traitement des big data, avec des modules intégrés pour le streaming, SQL, le machine learning (ML) et le traitement de graphes. Cette technologie est très recherchée par les data engineers, mais les data scientists y trouvent également un intérêt pour la phase d'exploration (EDA), l'extraction de variables et, bien sûr, le ML.
Dans ce tutoriel, vous utiliserez Spark avec Python via PySpark, l'API Python de Spark qui expose le modèle de programmation Spark à Python. Concrètement, vous vous concentrerez sur :
Si vous préférez utiliser Spark avec R, consultez le cours gratuit de DataCamp Introduction to Spark in R with sparklyr ou téléchargez l'aide-mémoire PySpark SQL.
Installer Apache Spark
L'installation de Spark et sa mise en route peuvent représenter un défi. Dans cette section, vous suivrez des étapes pour l'installer sur votre ordinateur.
Commencez par vérifier les prérequis. Spark est écrit en Scala et s'exécute sur la machine virtuelle Java (JVM). Vérifiez donc si un Java Development Kit (JDK) est installé. Le JDK fournit une ou plusieurs implémentations de la JVM. Idéalement, choisissez la version la plus récente ; au moment de la rédaction, il s'agissait du JDK8.
Ensuite, vous pouvez télécharger Spark !
Télécharger
pysparkavecpipVous pouvez télécharger et installer PySpark avec
pip. C'est très simple, comme pour n'importe quel autre package. Exécutez la commande habituelle et laissez l'outil faire le travail :Sinon, rendez-vous sur la page de téléchargement de Spark. Conservez les options par défaut pour les trois premières étapes et vous obtiendrez un lien téléchargeable à l'étape 4. Cliquez sur ce lien pour télécharger. Pour ce tutoriel, téléchargez Spark 2.2.0 et le package « Pre-built for Apache Hadoop 2.7 and later ».
Remarque : le téléchargement peut prendre du temps !
Télécharger Spark avec Homebrew
Vous pouvez également installer Spark avec Homebrew, un gestionnaire de paquets libre et open source. C'est particulièrement pratique sous macOS.
Exécutez simplement les commandes suivantes pour chercher Spark, obtenir des informations, puis l'installer sur votre ordinateur :
Télécharger et configurer Spark
Décompressez ensuite l'archive apparue dans votre dossier
Downloads. Cela peut se faire automatiquement en double-cliquant surspark-2.2.0-bin-hadoop2.7.tgzou via le Terminal avec la commande :Puis, déplacez le dossier extrait vers
/usr/local/sparkavec :Remarque : si vous obtenez une erreur de permission lors du déplacement, préfixez la commande par
sudo. La ligne devient alors$ sudo mv spark-2.1.0-bin-hadoop2.7 /usr/local/spark. Il vous sera demandé votre mot de passe, généralement celui que vous utilisez pour ouvrir votre session.Vous êtes prêt : ouvrez le fichier README dans
/usr/local/spark. Pour cela, exécutezVous arrivez ainsi dans le bon répertoire. Vous pouvez ensuite lister son contenu et lire le README inclus.
Utilisez d'abord
$ lspour afficher les fichiers et dossiers du dossierspark. Vous verrez un fichier README.md. Ouvrez-le avec l'une des commandes suivantes :Astuce : utilisez la touche Tab pour l'autocomplétion des noms de fichiers ; cela vous fera gagner du temps.
Ce README fournit des informations générales sur Spark, la documentation en ligne, la construction de Spark, les shells interactifs Scala et Python, des exemples, etc.
La partie la plus utile ici peut être la section sur la construction de Spark, mais elle ne concerne vraiment que les versions non précompilées. Pour ce tutoriel, vous avez téléchargé une version précompilée. Appuyez sur CTRL + X pour quitter le README et revenir au dossier spark.
Si vous avez choisi une version non construite, exécutez la commande indiquée dans le README. Au moment de la rédaction, c'était :
Notez que cette commande peut être longue.
Bases de PySpark : RDD
Maintenant que Spark et PySpark sont installés, commençons par explorer le shell interactif et ancrer quelques notions de base indispensables. Pour la suite du tutoriel, vous utiliserez PySpark dans un notebook Jupyter.
Applications Spark vs shell Spark
Le shell interactif est un environnement de type REPL (Read-Eval-Print-Loop) : tout ce que vous tapez est lu, exécuté puis affiché pour que vous puissiez enchaîner vos analyses. Cela rappelle IPython, le shell Python interactif que vous utilisez peut-être avec Jupyter. Pour en savoir plus, lisez l'article de DataCamp IPython or Jupyter.
Vous pouvez utiliser ce shell, disponible pour Python et Scala, pour tout travail interactif.
Outre le shell, vous pouvez écrire et déployer des applications Spark. Contrairement à une application, le SparkSession est déjà créé dans le shell pour vous faire gagner du temps.
Vous vous demandez peut-être : qu'est-ce que SparkSession ?
C'est le point d'entrée principal de Spark : il représente la connexion à un cluster Spark et permet de créer des RDD et de diffuser des variables sur ce cluster. Avec Spark, tout commence et finit avec ce SparkSession. Remarque : avant Spark 2.0.0, les objets de connexion principaux étaient SparkContext, SqlContext et HiveContext.
Vous en verrez plus par la suite. Concentrez-vous pour l'instant sur le shell.
Le shell Spark pour Python
Depuis le dossier
sparksitué en/usr/local/spark, lancez :Du texte s'affiche, puis « Spark » apparaît, comme ceci :
Une fois à cet écran, vous pouvez commencer à expérimenter dans le shell interactif !
Astuce : si vous préférez IPython au shell Spark, définissez la variable d'environnement suivante :
Créer des RDD
Commençons simplement en créant un RDD, brique de base de Spark. Un RDD représente des données, mais ce n'est ni un objet unique, ni une collection d'enregistrements, ni un data set classique. Il est conçu pour des données réparties sur plusieurs machines : un RDD peut être fragmenté sur des milliers de JVM, Spark partitionnant automatiquement les données pour paralléliser le traitement. Vous pouvez bien sûr ajuster le parallélisme. Un RDD est donc une collection de partitions.
Vous pouvez créer un RDD simple avec la fonction
parallelize()en lui passant des données (un itérable comme une liste, ou une collection) :Remarque : l'objet SparkSession expose l'objet SparkContext via
spark.sparkContext. Pour des raisons de compatibilité ascendante, vous pouvez encore utilisersc, par exemplerdd1 = sc.parallelize(['a',7),('a',2),('b',2)]).Opérations sur les RDD
Maintenant que vos RDD sont créés, vous pouvez opérer en parallèle sur les données de
rdd1etrdd2. Il existe deux types d'opérations : transformations et actions.Pour saisir intuitivement la différence, retenez que des transformations courantes sont
map(),filter(),flatMap(),sample(),randomSplit(),coalesce()etrepartition()tandis que des actions fréquentes sontreduce(),collect(),first(),take(),count(),saveAsHadoopFile().Les transformations sont paresseuses : elles créent un ou plusieurs nouveaux RDD. Les actions produisent une valeur non-RDD : résultat, nombre, fichier, …
Par exemple, agrégez tous les éléments de
rdd1avec une lambda simple et renvoyez le résultat au driver :Exécuter cette ligne renvoie :
('a', 7, 'a', 2, 'b', 2). Autre exemple de transformation :flatMapValues()sur un RDD de paires clé-valeur commerdd2. Ici, vous appliquezflatMapà chaque valeur sans modifier les clés (lambda ci-dessous) puis vous effectuez une action en collectant le résultat aveccollect().Les données
Après ces bases dans le shell interactif, passons à des données réelles. Pour ce tutoriel, vous utiliserez le jeu de données California Housing. Il s'agit certes de « petites » données ; utiliser Spark ici est sans doute excessif : le but est pédagogique, pour illustrer l'usage de PySpark pour construire un modèle de machine learning.
Charger et explorer vos données
Même si vous connaissez un peu votre jeu de données, prenez le temps de l'explorer plus en profondeur. Avant cela, configurez votre Jupyter Notebook avec Spark et amorcez la définition du SparkContext.
PySpark dans Jupyter Notebook
Pour cette partie, vous n'utiliserez pas le shell interactif, mais construirez votre propre application dans un Jupyter Notebook. Tout est déjà installé, vous n'avez presque rien à faire pour utiliser PySpark dans Jupyter.
Lancez l'application comme d'habitude avec
$ jupyter notebook. Créez un nouveau notebook, importez la bibliothèquefindsparket utilisez la fonctioninit(). Vous fournirez ici le chemin/usr/local/sparkàinit()car vous savez que Spark est installé à cet emplacement.Astuce : si vous ne connaissez pas votre chemin Spark, utilisez
findspark.find()pour le détecter automatiquement.Pour d'autres façons d'utiliser Spark dans Jupyter, consultez notre Apache Spark in Python : Beginner’s Guide.
Tout est en place : créons votre premier programme Spark !
Créer votre premier programme Spark
Commencez par importer
SparkSessiondepuispyspark.sqlet initialisez-le. Rappel : dans le shell interactif, il est créé automatiquement ! Ici, vous ferez un peu plus vous-même :Importez le module
SparkSessionpuis construisez-le avecbuilder(). Définissez l'URL du master, le nom de l'application, quelques paramètres comme la mémoire exécuteur, puisgetOrCreate()pour récupérer ou créer la session.Remarque : si vous obtenez une erreur de type FileNotFoundError du style : « No such file or directory: '/User/YourName/Downloads/spark-2.1.0-bin-hadoop2.7/./bin/spark-submit' », vous devez (ré)définir votre PATH Spark. Allez au dossier personnel avec
$ cdpuis éditez.bash_profileavec$ nano .bash_profile.Ajoutez par exemple en bas du fichier :
Quittez avec CTRL + X puis validez l'enregistrement des modifications avec Y. N'oubliez pas d'appliquer les changements avec
source .bash_profile.Astuce : vous pouvez définir d'autres variables d'environnement si besoin. Voici quelques exemples :
Remarque : vous avez initialisé un SparkSession par défaut. Dans la plupart des cas, vous souhaiterez le configurer davantage, surtout avec de gros volumes de données. Pour en savoir plus, consultez cette page.
Charger vos données
Ce tutoriel utilise le jeu de données California Housing, issu d'un article de 1997 intitulé Sparse Spatial Autoregressions par R. Kelley Pace et Ronald Barry, publié dans Statistics and Probability Letters. Les auteurs l'ont construit à partir du recensement californien de 1990.
Chaque ligne correspond à un groupe d'îlots de recensement, plus petite unité géographique pour laquelle le U.S. Census Bureau publie des échantillons (en général de 600 à 3 000 personnes). Dans cet échantillon, un groupe compte en moyenne 1425,5 individus sur une zone géographique compacte. Vous trouverez ces informations sur cette page ou dans l'article mentionné ici.
Ces données spatiales contiennent 20 640 observations de prix de l'immobilier avec 9 variables économiques :
Vous apprendrez également que tous les groupes avec des zéros sur les variables indépendantes et dépendantes ont été exclus.
Median house value est la variable cible de votre modèle de ML.
Téléchargez les données ici. Recherchez le dossier houses.zip, téléchargez et décompressez-le pour accéder aux fichiers.
Ensuite, utilisez
textFile()pour lire les données depuis le dossier téléchargé vers des RDD. Cette méthode prend l'URI du fichier (ici un chemin local) et lit une collection de lignes. Par commodité, lisez aussi le fichier .domain contenant l'en-tête, pour vérifier l'ordre des variables.Exploration des données
Vous avez déjà glané beaucoup d'informations via la page du jeu de données, mais rien ne remplace une inspection directe avec Spark en Python.
Important : l'exécution de Spark est paresseuse : rien n'a encore été exécuté. Les variables
rddetheaderne sont que des représentations. Vous devez « pousser » Spark à travailler. Utilisezcollect()pour regarderheader:collect()ramène l'intégralité du RDD sur une seule machine. Vous verrez :Astuce : attention avec
collect()! Cela peut saturer la mémoire du driver. Préféreztake()pour n'afficher que quelques éléments. En général, limitez votre jeu de résultats autant que possible, comme en SQL.Vous confirmez l'ordre des variables vu précédemment et que toutes les colonnes sont continues. Faisons travailler Spark pour vérifier cela sur les données California Housing.
Appelez
take()sur votre RDD :Cette commande prend les 2 premiers éléments du RDD. Le résultat est logique : en lisant avec
textFile(), les lignes sont lues telles quelles. Les entrées sont séparées par des virgules et les lignes aussi :Il faut corriger cela. Pas besoin de découper chaque entrée tout de suite, mais les lignes doivent devenir des éléments distincts. Utilisez
map()avec une lambda qui scinde la ligne sur la virgule, puis vérifiez avectake():Rappel : les lambdas sont des fonctions anonymes créées à l'exécution.
Vous obtenez :
Vous pouvez aussi utiliser :
Si vous avez l'habitude de Pandas ou des data frames en R, vous vous attendiez peut-être à un en-tête ; il n'y en a pas. Pour vous faciliter la vie, passez des RDD aux DataFrames, préférables quand ils sont disponibles, notamment en Python où ils sont plus performants.
Mais quelle est la différence ?
Utilisez des RDD pour des transformations/actions bas niveau sur des données non structurées, sans schéma ni accès par nom de colonne, et quand vous n'avez pas besoin des gains de performance des DataFrames. Privilégiez les RDD si vous manipulez les données avec des constructions fonctionnelles plutôt que des expressions spécifiques au domaine.
En résumé, passons aux DataFrames pour utiliser des expressions de haut niveau, effectuer des requêtes SQL et profiter de l'accès colonne par colonne.
Allons-y.
Première étape : créer un SchemaRDD (RDD de Row) avec un schéma. Comme pour un DataFrame, vous voulez des lignes et des colonnes, avec des types.
Utilisez encore
map()et une lambda pour associer chaque entrée à un champ nommé d'une Row. Par exemple, dans la première ligne :La lambda construit une Row dans un SchemaRDD en nommant l'élément d'indice 0 « longitude », etc.
Avec ce SchemaRDD, convertissez facilement le RDD en DataFrame via
toDF().Avec le DataFrame
df, inspectez-le avecfirst(),take(), mais aussihead()etshow():Vous voyez immédiatement une différence nette avec le RDD :
Astuce : utilisez
df.columnspour obtenir la liste des colonnes.Les colonnes semblent correctes, mais qu'en est-il des types ? Spark tente d'inférer un schéma à la lecture, mais y est-il arrivé ? Utilisez
df.dtypesoudf.printSchema()pour le savoir.Comme la première ligne n'est pas exécutée, vous obtenez :
Toutes les colonnes sont encore en chaîne de caractères … Décevant !
Pour continuer, il faut corriger et affecter des types plus adaptés à chaque colonne, ce qui améliorera aussi les performances. Intuitivement, vous pourriez caster chaque colonne en
FloatType():Mais ces appels répétitifs sont verbeux et propices aux erreurs. Écrivons une fonction plus propre pour tout faire :
La fonction suivante prend un DataFrame, des noms de colonnes et le nouveau type souhaité. Pour chaque colonne, elle caste vers le nouveau type puis renvoie le DataFrame :
Déjà plus lisible ! Vérifiez rapidement les types avec
printSchema(), comme précédemment.Maintenant, passons à l'exploration. L'accès colonne et les requêtes SQL sont deux atouts des DataFrames. Commençons petit : sélectionnez deux colonnes de
dfet affichez 10 lignes :Cette requête renvoie :
Vous pouvez complexifier, comme ci-dessous :
Ce qui donne :
Au-delà des requêtes, vous pouvez aussi décrire vos données et obtenir des statistiques résumées ; cela vous aidera pour la suite :
Observez les minimums et maximums des attributs numériques : plusieurs ont une large plage de valeurs. Vous devrez normaliser votre jeu de données.
Prétraitement des données
Avec ces premiers enseignements, vous pouvez prétraiter les données pour les injecter dans le modèle.
Prétraiter la variable cible
Commencez par
medianHouseValue, votre variable dépendante. Pour faciliter le travail, exprimez les valeurs en unités de 100 000. Ainsi,452600.000000devient4.526:Vous verrez clairement l'ajustement dans le résultat de
take():Feature engineering
Après l'ajustement de
medianHouseValue, ajoutez les variables supplémentaires mentionnées :Avec les DataFrames, utilisez
select()pour sélectionnertotalRooms,householdsetpopulation. Indiquez que vous opérez sur des colonnes viacol()afin d'effectuer des opérations élémentaires (division) sur ces variables :Pour la première ligne, on obtient environ 6,98 pièces par ménage, 2,5 personnes par ménage et 0,14 chambre par pièce :
Ensuite — en anticipant un problème potentiel lors de la standardisation — réorganisez les colonnes : comme vous ne voulez pas standardiser la cible, isolez-la.
Ici, utilisez
select()en passant l'ordre des colonnes souhaité. PlacezmedianHouseValueen premier pour éviter de l'affecter.Remarque : c'est aussi le moment d'écarter des variables non utiles (« longitude », « latitude », « housingMedianAge », « totalRooms »).
Standardisation
Une fois les colonnes réordonnées, vous pouvez normaliser. Mais avant, séparez les variables explicatives de la cible : en pratique, isolez la première colonne du reste.
Ici, vous utiliserez
map()sur le RDD sous-jacent. Vous verrez aussiDenseVector(), un vecteur local basé sur un tableau de doubles, pour stocker des tableaux de valeurs en PySpark.Ensuite, recréez un DataFrame à partir de
input_dataet renommez les colonnes en"label"et"features":Passez enfin à l'échelle. Utilisez Spark ML : la bibliothèque facilite et déploie le ML à grande échelle, avec algorithmes et pipelines. Ici, un pipeline serait superflu, mais consultez cette page si besoin.
La colonne d'entrée est
featureset la colonne de sortie dansscaled_dfsera"features_scaled":Regardez le DataFrame obtenu : une troisième colonne
features_scaleda été ajoutée, à comparer àfeatures:Remarque : ces lignes sont très proches de ce que vous feriez avec Scikit-Learn.
Construire un modèle de machine learning avec Spark ML
Le prétraitement terminé, construisons le modèle de régression linéaire ! Comme toujours, commencez par scinder les données en apprentissage et test via
randomSplit():Vous fournissez une liste avec les proportions d'apprentissage et de test, et une graine pour la reproductibilité. Pour aller plus loin, consultez le Python Machine Learning Tutorial de DataCamp.
Puis, créez le modèle !
Remarque : l'argument
elasticNetParamcorrespond à α et leregParam(paramètre de régularisation) à λ. Plus d'infos ici.Avec le modèle en place, générez des prédictions sur les données de test : utilisez
transform()pour prédire les labels detest_data. Puis, utilisez des opérations RDD pour extraire prédictions et labels réels, et regroupez-les dans une listepredictionAndLabel.Enfin, inspectez les valeurs prédites et réelles via l'indexation
[]:Vous verrez les valeurs réelles et prédites (dans cet ordre) :
Évaluer le modèle
Regarder des prédictions, c'est bien ; analyser des métriques, c'est mieux. Commencez par afficher les coefficients et l'ordonnée à l'origine du modèle :
Ce qui renvoie :
Ensuite, utilisez l'attribut
summarypour récupérer lerootMeanSquaredErroret ler2:Le RMSE mesure l'erreur entre une valeur prédite et une valeur observée. Plus il est faible, plus la prédiction est proche de l'observation.
Le R² (coefficient de détermination) indique la proximité des données à la droite de régression. Il varie entre 0 et 1 : 0 % signifie que le modèle n'explique aucune variabilité de la réponse autour de sa moyenne, 100 % l'inverse. En général, plus le R² est élevé, meilleur est l'ajustement.
Vous obtiendrez :
Votre modèle peut encore être amélioré ! Pour continuer, testez d'autres paramètres, sélections de variables, etc. Mais le tutoriel s'arrête ici pour l'instant !
Avant de partir…
Avant de quitter, arrêtez le SparkSession avec :
Aller plus loin avec les big data
Félicitations ! Vous avez terminé ce tutoriel et appris à construire une régression linéaire avec Spark ML.
Pour approfondir PySpark, suivez le cours Introduction to PySpark de DataCamp et découvrez Apache Spark Tutorial : ML with PySpark.