Publié par
Il y a 2 années · 23 minutes · Data

From scikit-learn to Spark ML

spark-logo.pngscikit-learn

Dans un récent billet de blog de Databricks et Olivier Girardot, From Pandas to Apache Spark’s DataFrame, les auteurs nous montraient comment transposer le traitement et l’analyse de données faites avec la librairie Python pandas en DataFrames PySpark. L’article prouvait que, bien que quelques différences techniques existent dues au fait que les objets traités ne sont pas les mêmes, le passage à l’échelle en Spark DataFrame est relativement simple et intuitif pour les utilisateurs de pandas.

Nous allons ici proposer une démarche similaire afin de montrer comment passer de la librairie scikit-learn de Python à Spark ML pour la réalisation de pipelines complexes de Machine Learning. L’objectif étant de présenter au lecteur les principaux concepts introduits par Spark ML et de montrer les différences et similitudes avec scikit-learn, nous allons supposer que les notions de DataFrame (pandas et Spark) sont déjà connues, sans toutefois qu’il y ait besoin d’en connaître les moindres détails. Le cas échéant, nous vous invitons à lire l’article de blog mentionné plus haut, ainsi que de parcourir rapidement la documentation de pandas et de Spark DataFrame. Notre précédent article de blog sur Python Data Tools peut aussi être une bonne base de travail.

Un peu d’histoire

Créée en 2007, scikit-learn est l’une des librairies open-source les plus populaires pour le Machine Learning en Python, et est en particulier très prisée par les Data Scientists. Bénéficiant d’une communauté extrêmement active, elle regroupe tous les principaux algorithmes de Machine Learning (classification, clustering, régression) ainsi que de nombreux modules pour la réduction de dimension, le Feature Engineering et l’évaluation de modèles.

Dévoilée à partir de la version 1.2 de Spark, Spark ML est l’une des deux librairies de Machine Learning sous Spark. Cette librairie a été créée en parallèle à MLlib, la librairie historique de Spark pour le Machine Learning, dans le but de contrer certains des problèmes de cette dernière, notamment son manque occasionnel d’homogénéité dans la manière d’entraîner et de chainer les algorithmes, et l’obligation de fournir en entrée des RDD avec des types bien particuliers.

Spark ML a été conçue dans le but de standardiser les APIs de Machine Learning, afin entre autres de faciliter la combinaison de plusieurs algorithmes au sein d’une même Pipeline. Dans sa conception et sa logique, Spark ML se rapproche de manière très flagrante de scikit-learn, librairie ayant maintenant fait ses preuves dans le domaine. scikit-learn et Spark ML étant relativement proches dans leur fonctionnement global, le passage de l’une à l’autre pour des analyses à grande échelle n’en est que simplifié.

NB: Lors de l’écriture de cet article, Spark ML est un composant en version beta inclus dans Spark, donc toujours soumis à des développements intensifs. Toutes les fonctionnalités de MLlib, et de manière plus importante de scikit-learn, ne sont pas encore présentes, ce qui n’enlève rien à sa puissance et son potentiel.

scikit-learn & Spark ML: Des APIs aux structures similaires

Comme expliqué dans le paragraphe précédent, Spark ML a été conçu en gardant en mémoire la structure des API de Machine Learning les plus utilisées, dont scikit-learn fait partie. La structure des deux APIs est donc très similaire, rassemblant les différents algorithmes en packages par grandes fonctionnalités. Cependant, les packages de scikit-learn sont plus nombreux car correspondent à un maillage plus fin des algorithmes.

tableau from SL to Spark ML

Comme on peut le constater, les modules de scikit-learn sont très nombreux et permettent de répondre à tous types de problématiques, des plus simples aux plus complexes. Spark ML a une structure générale moins dense, mais certainement plus simple à appréhender car regroupant les algorithmes en grandes catégories d’application. Si on prends l’exemple des algorithmes de classification, on peut voir que scikit-learn dispose de nombreux packages selon le type d’algorithme, alors que Spark ML propose un même et unique package classification regroupant tous les algorithmes dédiés à cette tâche.

Tous ces packages permettent de gérer toutes les étapes d’une pipeline complexe de Machine Learning: Feature Engineering (passage de données brutes à des features exploitables par des algorithmes de Machine Learning), Apprentissage / Modelling et Prédiction. Ces différentes étapes sont rappelées dans le schéma ci-dessous, inspiré de la documentation de scikit-learn.

Les principales abstractions

Données d’entrée

La plupart des algorithmes de scikit-learn utilisent des jeux de données sous forme de tableaux (ou de matrices) à deux dimensions, qui sont du type numpy.ndarray ou scipy.sparse. En pratique, il est aussi possible d’utiliser directement une DataFrame pandas, en fournissant à l’algorithme son attribut values pour fournir les données à l’algorithme sous forme d’un numpy.ndarray, en faisant bien attention que les colonnes sélectionnées du DataFrame soient bien numériques.

De la même manière que l’on utilise souvent des DataFrames pandas comme entrée des algorithmes de scikit-learn (avec l’attribut values), ceux de Spark ML utilisent des DataFrames Spark comme données d’entrée. Les DataFrames on pour avantage le fait que les colonnes d’une même DataFrame peuvent être de types différents, correspondant plus à la réalité des données brutes collectées.

Estimators et Transformers

Toutes les opérations sur les données au sein d’une Pipeline de Machine Learning peuvent se résumer en deux catégories:

  • Opérations lignes par lignes: Transformations

  • Opérations nécessitant au préalable un passage sur toutes les lignes du dataset pour fournir un modèle permettant de faire ensuite des opérations ligne par ligne: Estimations + Transformations

Le point clé se trouve donc dans le fait de savoir si il est nécessaire ou non de passer au préalable sur toutes les lignes du dataset afin de transformer les données. Cette distinction est présente en Spark ML sous les notions primordiales de Transformer et d’Estimator.

Un Transformer est un algorithme transformant un DataFrame en un autre, typiquement contenant une ou plusieurs colonnes supplémentaires. La transformation se fait à l’aide de la méthode transform(), et se fait donc ligne par ligne.

Un Estimator est un algorithme utilisant des DataFrames en entrée pour produire un Model, qui est un Transformer. Tout algorithme d’apprentissage est implémenté en utilisant cette abstraction. La phase d’apprentissage se lance à l’aide de la méthode fit(), et va donc parcourir toutes les données pour créer le Model (nécessitant donc des échanges de données dans le cas de données sur un cluster), qui sera ensuite utilisé en tant que Transformer pour rajouter une ou plusieurs colonnes, ligne par ligne.

En scikit-learn, la notion de Transformer n’est pas aussi explicite. Dans la plupart des cas, qu’il y ait besoin de passer sur toutes les données au préalable ou non, on retrouve des classes Estimator, qui possèdent une méthode fit(). Selon le cas d’usage de la classe, on retrouvera de plus une méthode predict() pour prédire de nouveaux labels une fois le modèle appris, ou transform() pour transformer le jeu de données. C’est la même instance qui est utilisée pour la phase d’apprentissage (fit) et de prédiction (predict / transform). Pour les opérations ne nécessitant pas de passer au préalable sur toutes les lignes du dataset, on retrouve en général tout de même une méthode fit, mais qui ne modifie rien. Cette méthode est présente afin de garder la consistance de l’API pour produire des pipelines.

Prenons un exemple pour illustrer ces propos. On a à notre disposition un jeu de données (Iris dans cet exemple), et on cherche à binariser l’une des variables selon un certain seuil. Ceci est faisable grâce à la classe Binarizer, présent dans scikit-learn et Spark ML.

scikit-learn

import pandas as pd
from sklearn.datasets import load_iris

# Load data in pandas DataFrame
data = pd.DataFrame(data=load_iris().data, columns=['sepal_length', 'sepal_width', 'petal_length', 'petal_width'])

# Binarizer
from sklearn.preprocessing import Binarizer
binarizer = Binarizer(threshold=5)
binarizer.fit_transform(data.sepal_length)

Spark ML

# Create DataFrame
df = sqlContext.createDataFrame(data)

# Binarizer
from pyspark.ml.feature import Binarizer
binarizer = Binarizer(threshold=5.0, inputCol='sepal_length', outputCol='sepal_length_bin')
binarizer.transform(df).show(5)

Comme on peut le constater, la démarche globale est exactement la même pour les deux librairies: Instancier la classe Binarizer avec le seuil souhaité, et utiliser la méthode transform pour faire la transformation.

Quelques différences importantes subsistent tout de même:

  • En scikit-learn, transform renvoie uniquement la ou les colonnes transformées, alors que Spark ML renvoie un DataFrame contenant toutes les colonnes initiales auxquelles sont rajoutées la ou les colonnes transformées. Cela est dû au fait que les DataFrames, comme tout RDD, est immuable. Il est impossible de transformer directement un DataFrame, il faut alors en créer un nouveau. Cela permet cependant de garder au sein de la même structure les données initiales et les données transformées, ce qui est utile si les données initiales doivent être réutilisées pour d’autres transformations.
  • Lors de l’instanciation de Binarizer, Spark ML oblige à stipuler à minima le nom de la colonne à transformer. Ce n’est pas le cas en scikit-learn, pour lequel le choix de la ou des colonnes se fait au moment du transform.

Ces différences se retrouveront de manière générale à chaque comparaison entre scikit-learn et Spark ML.

Nous proposons maintenant de parcourir les similitudes et différences entre scikit-learn et Spark ML à travers un exemple complet comprenant toutes les étapes d’une chaîne de traitement de Machine Learning. Nous verrons que, même si certaines abstractions et utilisations diffèrent, le fonctionnement global des deux librairies est, de manière très flagrante, similaire.

Préparation des données

Nous allons utiliser un dataset nommé 20 NewsGroup, contenant des commentaires d’articles de journaux pour de nombreuses catégories, allant de l’automobile à la politique, en passant par la science et le hardware. L’objectif est alors de construire un modèle permettant de prédire, à partir de son contenu, à quelle catégorie l’article appartient.

NB: Cet exemple est largement inspiré d’un tutoriel de scikit-learn, ce qui permet d’avoir une base d’exemple pour présenter les concepts de Spark ML.

scikit-learn possède une méthode permettant d’accéder directement à ces données. Nous allons l’utiliser pour créer notre table, ainsi que notre DataFrame Spark.

scikit-learn

# Import data
from sklearn.datasets import fetch_20newsgroups
categories = ['rec.autos', 'rec.sport.baseball', 'comp.graphics', 'comp.sys.mac.hardware',
              'sci.space', 'sci.crypt', 'talk.politics.guns', 'talk.religion.misc']
newsgroup = fetch_20newsgroups(subset='train', categories=categories, shuffle=True, random_state=42)
print newsgroup.data[0]
# Create pandas DataFrames for values and targets
import pandas as pd
pdf_newsgroup = pd.DataFrame(data=newsgroup.data, columns=['news']) # Texts
pdf_newsgroup_target = pd.DataFrame(data=newsgroup.target, columns=['target']) # Targets

Spark ML

from pyspark.sql import SQLContext
sqlContext = SQLContext(sc)

# Create DataFrame
df_newsgroup = sqlContext.createDataFrame(pd.concat([pdf_newsgroup, pdf_newsgroup_target], axis=1))
df_newsgroup.printSchema()
df_newsgroup.show(3)

Une différence notable est qu’en scikit-learn, les caractéristiques sont généralement séparées de leurs targets (les classes à prédire dans notre cas), alors qu’en Spark ML, la colonne de target doit se trouver dans le même DataFrame que le reste des données.

Pour pouvoir tester les performances de nos classifications, il est important de séparer au préalable notre dataset en deux: un training set qui servira de base d’entraînement des algorithmes, et un test set qui correspondra à nos “nouvelles données” pour lesquelles on fera une prédiction et on pourra les comparer avec les catégories réellement observées. On utilisera la fonction train_test_split pour scikit-learn, et randomSplit pour Spark ML.

scikit-learn

from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(newsgroup.data, newsgroup.target, train_size=0.8, random_state=42)

Spark ML

(df_train, df_test) = df_newsgroup.randomSplit([0.8, 0.2])
Passons maintenant aux différentes étapes essentielles de constitution d’une Pipeline complète de Machine Learning, afin d’observer la facilité de passage de scikit-learn à Spark ML.

Feature Engineering

Le Feature Engineering permet de transformer, extraire et sélectionner des caractéristiques (features) afin de tirer le maximum d’information des données pour optimiser les performances des algorithmes de Machine Learning. En particulier, la plupart des algorithmes de Machine Learning prennent en entrée des données numériques.

Dans notre exemple, nos données d’entrée sont textuelles, il faut donc les transformer pour créer des features numériques qui les caractérisent au mieux. Nous allons ici effectuer les transformations suivantes:

  • Tokenizing: Transformation d’un texte en une liste de mots
  • Term Frequency: Plus un terme est fréquent dans un même document, plus il a de chances d’être révélateur de l’information contenue dans celui-ci (sauf si c’est un stop-word)
  • Inverse Document Frequency: Si un terme apparaît dans la plupart des documents du corpus, il y a peu de chance qu’il soit utile pour les distinguer et les classer

Comme nous allons pouvoir le constater, les outils employés par scikit-learn et Spark ML pour réaliser ces transformations sont extrêmement ressemblant, et s’utilisent de la même manière.

Pour scikit-learn, nous allons utiliser les estimators CountVectorizer (qui joue le rôle de Tokenizer, avec d’autres fonctionnalités) et TfIdfTransformer. Ces deux éléments possèdent une méthode fit() et transform(), ainsi qu’une méthode les rassemblant: fit_transform().

Pour Spark ML, nous allons utiliser les Transformers Tokenizer et HashingTF, et l’Estimator IDF. L’Estimator nécessite une étape d’apprentissage sur toutes les données (fit) pour construire un modèle qui sera ensuite appliqué ligne par ligne (transform). Comme son nom l’indique, HashingTF incorpore une notion de « hashing », qui est utilisé pour passer d’une chaine de caractères à une valeur numérique qui sera utilisée comme indice pour la construction de la matrice d’appartenance des termes à un document pour gagner en espace mémoire. L’utilisation de fonction de hash entraîne une probabilité de collision de hash entre deux termes (même valeur de hash pour termes différents), qui est de plus accentuée par le fait que l’on puisse se restreindre à un certain nombre de features. Cependant, ces collisions ont un impact extrêmement restreint sur les performances globales du modèle.

NB: Les classes utilisés pour les deux librairies pour cet exemple n’étant pas exactement les mêmes, les résultats fournis peuvent donc être différents. C’est la démarche d’utilisation de Spark ML que nous cherchons à présenter, et sa similarité d’utilisation avec scikit-learn.

scikit-learn

# Tokenizing and Occurrence Counts
from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(X_train)

# TF-IDF
from sklearn.feature_extraction.text import TfidfTransformer
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)

Spark ML

# Tokenizing
from pyspark.ml.feature import Tokenizer
tokenizer = Tokenizer(inputCol='news', outputCol='news_words')
df_train_words = tokenizer.transform(df_train)

# Hashing Term-Frequency
from pyspark.ml.feature import HashingTF
hashing_tf = HashingTF(inputCol=tokenizer.getOutputCol(), outputCol='news_tf', numFeatures=10000)
df_train_tf = hashing_tf.transform(df_train_words)

# Inverse Document Frequency
from pyspark.ml.feature import IDF
idf = IDF(inputCol=hashing_tf.getOutputCol(), outputCol="news_tfidf")
idf_model = idf.fit(df_train_tf) # fit to build the model on all the data, and then apply it line by line
df_train_tfidf = idf_model.transform(df_train_tf)

df_train_tfidf.show(5)
Comme on le voit, HashingTF et IDF renvoient un DataFrame avec des colonnes supplémentaires de type SparseVector: On spécifie sa taille (ici 10000), la liste des positions pour lesquelles la valeur du vecteur est non nulle, puis la liste des valeurs non nulles. Cela permet de ne pas encoder complètement le vecteur lorsqu’il contient énormément de 0.

Création d’un Modèle de Machine Learning

Les données étant maintenant prêtes à être exploitées par un algorithme de Machine Learning, nous pouvons passer à la phase de modelling. Nous allons utiliser un modèle simple dans notre exemple: un Arbre de Décision. Ils sont utilisables grâce aux classes DecisionTreeClassifier de scikit-learn et Spark ML.

Les paramètres de ce classifier sont les mêmes pour les deux librairies, mais avec des noms légèrement différents. De même, leur utilisation est exactement la même quelle que soit la librairie utilisée.

A noter cependant que, bien que scikit-learn comprenne que le label fourni correspond à des classes (puisque l’on utilise un Classifier), ce n’est pas le cas de Spark ML. Il faut donc lui spécifier que les valeurs de la colonne de label sont des classes et non des entiers. Il faut aussi que ces valeurs soient de la forme 0, 1, 2, …, num_labels-1. Cette transformation est faite à l’aide de StringIndexer. C’est un estimator, car il faut d’abord passer par toutes les données pour savoir combien de classes il y a dans le dataset, et ensuite indexer les valeurs. Pour le reste, le fonctionnement est le même.

Pour rappel, la phase d’apprentissage se fait sur le training set, alors que la phase de prédiction se fait sur le test set. Il faut de plus bien faire attention à appliquer toutes les transformations faites précédemment sur le test set, pour pouvoir effectuer les prédictions.

scikit-learn

# Training a Decision Tree on training set
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(max_depth=10).fit(X_train_tfidf, y_train)

# Transform test set
X_test_counts = count_vect.transform(X_test)
X_test_tfidf = tfidf_transformer.transform(X_test_counts)

# Predictions on the test set
y_test_pred = clf.predict(X_test_tfidf)

Spark ML

# Indexing the target
from pyspark.ml.feature import StringIndexer
string_indexer = StringIndexer(inputCol='target', outputCol='target_indexed')
string_indexer_model = string_indexer.fit(df_train_tfidf)
df_train_final = string_indexer_model.transform(df_train_tfidf)

# Training a Decision Tree on training set
from pyspark.ml.classification import DecisionTreeClassifier
dt = DecisionTreeClassifier(featuresCol=idf.getOutputCol(), labelCol=string_indexer.getOutputCol())
dt_model = dt.fit(df_train_final)

# Transform the test set
df_test_words = tokenizer.transform(df_test)
df_test_tf = hashing_tf.transform(df_test_words)
df_test_tfidf = idf_model.transform(df_test_tf)
df_test_final = string_indexer_model.transform(df_test_tfidf)

# Preditions on the test set
df_test_pred = dt_model.transform(df_test_final)

df_test_pred.select('news', 'target', 'prediction', 'probability').show(5)

Construction de Pipelines

Si on résume les actions faites jusque là, nous avons:

  • Transformation des textes en listes de mots (CountVectorizer / Tokenizer)
  • Transformation Term Frequency – Inverse Document Frequency (TfidfTransfofmer / HashingTF + IDF)
  • Apprentissage d’un Arbre de Décision (DecisionTreeClassifier)

Le nombre d’étapes peut être relativement important en fonction de l’application. Enchaîner toutes les étapes sur le training set pour entraîner un modèle, puis les reprendre toutes sur le test set pour faire les prédictions peut être long et fastidieux. C’est pourquoi la notion de Pipeline est présente pour alléger l’écriture. Une Pipeline est une classe dans laquelle on va regrouper toutes les étapes de transformation de la donnée, pour ne créer qu’un seul et unique estimateur, qui sera ensuite utilisé sur les données brutes des training et test sets.

La démarche est alors la suivante:

  1. Créer une instance de chaque Transformer / Estimator à utiliser
  2. Les regrouper dans une instance de Pipeline
  3. Appeler la méthode fit() de Pipeline pour lancer la transformation et l’apprentissage sur le training set
  4. Appeler la méthode transform() pour réaliser les prédictions sur le test set

Lors de l’appel à fit(), l’instance de Pipeline va appeler, tour à tour pour chaque étape, la méthode fit() de l’estimator si s’en est un, puis sa méthode transform().

scikit-learn

from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline

# Instanciate a Pipeline
text_clf = Pipeline([('vect', CountVectorizer()),
                     ('tfidf', TfidfTransformer()),
                     ('clf', DecisionTreeClassifier(max_depth=10)),
                    ])

# Transform the data and train the classifier on the training set
text_clf = text_clf.fit(X_train, y_train)

# Transform the data and perform predictions on the test set
y_test_pred = text_clf.predict(X_test)

Spark ML

from pyspark.ml.feature import Tokenizer, HashingTF, IDF, StringIndexer
from pyspark.ml.classification import DecisionTreeClassifier
from pyspark.ml import Pipeline

# Instanciate all the Estimators and Transformers necessary
tokenizer = Tokenizer(inputCol='news', outputCol='news_words')
hashing_tf = HashingTF(inputCol=tokenizer.getOutputCol(), outputCol='news_tf', numFeatures=10000)
idf = IDF(inputCol=hashing_tf.getOutputCol(), outputCol="news_tfidf")
string_indexer = StringIndexer(inputCol='target', outputCol='target_indexed')
dt = DecisionTreeClassifier(featuresCol=idf.getOutputCol(), labelCol=string_indexer.getOutputCol(), maxDepth=10)

# Instanciate a Pipeline
pipeline = Pipeline(stages=[tokenizer,
                            hashing_tf,
                            idf,
                            string_indexer,
                            dt])

# Transform the data and train the classifier on the training set
pipeline_model = pipeline.fit(df_train)

# Transform the data and perform predictions on the test set
df_test_pred = pipeline_model.transform(df_test)

df_test_pred.show(5)

Evaluation de modèle

Une fois notre Pipeline construite, il est temps d’évaluer les résultats obtenus et de mesurer les performances de notre modèle prédictif. C’est à ce moment que le test set est crucial. Les prédictions sont faites sur ce dernier, en faisant comme s’il s’agissait de nouvelles données dont on ne connaît pas la classe, pour ensuite comparer les prédictions avec les classes réelles. Si nous faisions cela sur le training set, les résultats seraient alors biaisés car l’évaluation serait faite sur des données qui ont servi à l’apprentissage. Le fait de garder un test set pour lequel les données n’ont pas servi à l’apprentissage permet de mesurer la capacité de généralisation du modèle créé.

Pour mesurer les performances dans notre cas, nous allons utiliser la précision comme métrique, c’est à dire le pourcentage global de bonnes prédictions. Cette métrique est présente dans scikit-learn avec la méthode precision_score, et dans Spark ML avec la classe MulticlassClassificationEvaluator.

scikit-learn

from sklearn.metrics import precision_score

# Evaluate the predictions done on the test set
precision_score(y_test_pred, y_test, average='micro')

Spark ML

 from pyspark.ml.evaluation import MulticlassClassificationEvaluator

# Instanciate a MulticlassClassificationEvaluator with precision metric
evaluator = MulticlassClassificationEvaluator(predictionCol='prediction', labelCol='target_indexed', metricName='precision')

# Evaluate the predictions done on the test set
evaluator.evaluate(df_test_pred)

Tuning de paramètres

Nous voudrions maintenant chercher à améliorer le score de notre modèle. Une manière de faire cela est de tuner les paramètres, afin de trouver la combinaison donnant les meilleures capacités de généralisation. Le tuning est généralement fait en utilisant les outils suivants:

  • Grid Search: Spécifier toutes les valeurs de chaque paramètre que l’on souhaite tester
  • Cross Validation: Test à plusieurs reprises de toutes les combinaisons de paramètres, sur différentes séparations du training set

En scikit-learn, on peut utiliser pour cela la classe GridSearchCV. En Spark ML, c’est une classe CrossValidator. Dans chaque cas, trois informations sont à fournir:

  • La grille de paramètres (on utilise pour cela un ParamGridBuilder en Spark ML)
  • L’estimator (ou la pipeline)
  • La fonction de scoring pour décider quelle combinaison donne le meilleur score

scikit-learn

from sklearn.grid_search import GridSearchCV

# Create the parameters grid
parameters = {'tfidf__use_idf': (True, False),
              'clf__max_depth': (10, 20)
             }

# Instanciate a GridSearchCV object with the pipeline, the parameters grid and the scoring function
gs_clf = GridSearchCV(text_clf, parameters, score_func=precision_score, n_jobs=-1)

# Transform the data and train the classifier on the training set
gs_clf = gs_clf.fit(X_train, y_train)

# Transform the data and perform predictions on the test set
y_test_pred = gs_clf.predict(X_test)

# Evaluate the predictions done on the test set
precision_score(X_test_pred, y_test, average='micro')

Spark ML

from pyspark.ml.tuning import ParamGridBuilder
from pyspark.ml.tuning import CrossValidator

# Instanciation of a ParamGridBuilder
grid = (ParamGridBuilder()
        .baseOn([evaluator.metricName, 'precision'])
        .addGrid(dt.maxDepth, [10, 20])
        .build())

# Instanciation of a CrossValidator
cv = CrossValidator(estimator=pipeline, estimatorParamMaps=grid, evaluator=evaluator)

# Transform the data and train the classifier on the training set
cv_model = cv.fit(df_train)

# Transform the data and perform predictions on the test set
df_test_pred = cv_model.transform(df_test)

# Evaluate the predictions done on the test set
evaluator.evaluate(df_test_pred)

Conclusion

Cet article avait pour but d’introduire les principales notions utilisées en Spark ML, et de montrer à quel point le passage de scikit-learn à cette librairie de Machine Learning distribué est simplifié. Il y a quelques différences notables entre les deux librairies, en termes d’implémentation ainsi que dans la manière de traiter la donnée, mais elles sont minimales. Spark ML a été construit de manière à avoir une utilisation proche de scikit-learn, et cela aide énormément lorsque l’on passe à l’échelle avec Spark pour constuire des pipelines complexes de Machine Learning.

Spark ML est toujours en développement actif, et a encore pour le moment un nombre limité d’algorithmes implémentés en comparaison avec scikit-learn. La liste va s’étendre de plus en plus à chaque nouvelle version de Spark, et il sera alors encore plus simple de passer de scikit-learn à Spark ML pour tous types d’applications.

L’intégralité du code de cet article est disponible dans un notebook sur Github. Pour aller plus loin avec Spark ML, les DataFrames et la data science en général sur Spark nous vous recommandons la formation Analyse de données et Machine Learning avec Spark, donnée par nos Data Scientists.

Yoann Benoit
Yoann est Data Scientist chez Xebia. Il est également formateur au sein de Xebia Training .

Une réflexion au sujet de « From scikit-learn to Spark ML »

  1. Publié par Hieu Nguyen Canh, Il y a 1 année

    merci beaucoup !!! c’est très intéressant.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *