Une introduction à Redis-ML (partie cinq)

Une introduction à Redis-ML (partie cinq)

Ce poste est la cinquième partie d’une série de postes examinant les caractéristiques de la Redis-ML module. Le premier article de la série se trouve ici. L’exemple de code inclus dans cet article nécessite plusieurs bibliothèques Python et une instance Redis avec le module Redis-ML chargé. Des instructions de configuration détaillées pour l’environnement d’exécution sont fournies dans les deux partie un et deuxième partie de la série.

Arbres de décision

Arbres de décision sont un modèle prédictif utilisé pour les problèmes de classification et de régression en apprentissage automatique. Les arbres de décision modélisent une séquence de règles sous la forme d’un arbre binaire. Les nœuds intérieurs de l’arbre représentent une division ou une règle et les feuilles représentent une classification ou une valeur.

Chaque règle de l’arborescence agit sur une seule caractéristique de l’ensemble de données. Si la condition de la règle est remplie, passez à l’enfant de gauche ; sinon, déplacez-vous vers la droite. Pour une caractéristique catégorielle (énumérations), le test utilisé par la règle est l’appartenance à une catégorie particulière.

Pour les caractéristiques avec des valeurs continues, le test est « inférieur à » ou « égal à ». Pour évaluer un point de données, commencez par la note racine et parcourez l’arbre en évaluant les règles du nœud intérieur, jusqu’à ce qu’un nœud feuille soit atteint. Le nœud feuille est étiqueté avec la décision de revenir. Un exemple d’arbre de décision est présenté ci-dessous :

CART tree titanic survivors
Arbre CART de l’article d’apprentissage de l’arbre de décision de Wikipedia

De nombreux algorithmes différents (partitionnement récursif, induction descendante, etc.) peuvent être utilisés pour construire un arbre de décision, mais la procédure d’évaluation est toujours la même. Pour améliorer la précision des arbres de décision, ils sont souvent agrégés dans des forêts aléatoires qui utilisent plusieurs arbres pour classer un point de données et prendre la décision majoritaire parmi les arbres comme classification finale.

Pour démontrer comment fonctionnent les arbres de décision et comment un arbre de décision peut être représenté dans Redis, nous allons construire un prédicteur de survie Titanic à l’aide du package Python scikit-learn et de Redis.

Ensemble de données Titanic

Le 15 avril 1912, le Titanic a coulé dans l’océan Atlantique Nord après être entré en collision avec un iceberg. Plus de 1500 passagers sont morts à la suite de la collision, ce qui en fait l’une des catastrophes maritimes commerciales les plus meurtrières de l’histoire moderne. Bien qu’il y ait eu un élément de chance dans la survie à la catastrophe, l’examen des données montre des biais qui ont rendu certains groupes de passagers plus susceptibles de survivre que d’autres.

Le Titanic Dataset, dont une copie est disponible ici, est un jeu de données classique utilisé en machine learning. La copie de l’ensemble de données, provenant des archives Vanderbilt, que nous avons utilisée pour ce post contient des enregistrements pour 1309 des passagers du Titanic. Les dossiers se composent de 14 champs différents : classe de passagers, survivants, nom, sexe, âge, nombre de frères et sœurs/conjoints, nombre de parents/enfants à bord, numéro de billet, tarif, cabine, port d’embarquement, canot de sauvetage, numéro de corps et destination .

Une analyse rapide de nos données dans Excel montre de nombreuses données manquantes dans notre ensemble de données. Les champs manquants auront un impact sur nos résultats, nous devons donc faire un peu de nettoyage sur nos données avant de construire notre arbre de décision. Nous utiliserons le pandas bibliothèque pour prétraiter nos données. Vous pouvez installer la bibliothèque pandas à l’aide de pip, le gestionnaire de packages Python :

pip install pandas 

ou votre gestionnaire de paquets préféré.

À l’aide de pandas, nous pouvons obtenir une ventilation rapide du nombre de valeurs pour chacune des classes d’enregistrement dans nos données :

pclass       1309
survived     1309
name         1309
sex          1309
age          1046
sibsp        1309
parch        1309
ticket       1309
fare         1308
cabin         295
embarked     1307
boat          486
body          121
home.dest     745

Étant donné que les enregistrements de cabine, de bateau, de corps et de home.dest ont un grand nombre d’enregistrements manquants, nous allons simplement les supprimer de notre jeu de données. Nous allons également supprimer le champ du ticket, car il a peu de valeur prédictive. Pour notre prédicteur, nous finissons par construire un ensemble de fonctionnalités avec la classe de passagers (pclass), le statut de survie (survived), le sexe, l’âge, le nombre de frères et sœurs/conjoints (sibsp), le nombre de parents/enfants à bord (parch), le tarif et registres du port d’embarquement (“embarqué”). Même après avoir supprimé les colonnes peu peuplées, il manque encore plusieurs lignes de données, donc pour plus de simplicité, nous supprimerons ces enregistrements de passagers de notre ensemble de données.

L’étape initiale de nettoyage des données se fait à l’aide du code suivant :

import pandas as pd

# load data from excel 
orig_df = pd.read_excel('titanic3.xls', 'titanic3', index_col=None)
# remove columns we aren't going to work with, drop rows with missing data
df = orig_df.drop([“name”, "ticket", "body", "cabin", "boat", "home.dest"], axis=1)
df = df.dropna()

Le prétraitement final que nous devons effectuer sur nos données consiste à coder les données catégorielles à l’aide de constantes entières. Les colonnes pclass et survived sont déjà encodées sous forme de constantes entières, mais la colonne sex enregistre les valeurs de chaîne male ou female et la colonne embarqué utilise des codes alphabétiques pour représenter chaque port. Le package scikit fournit des utilitaires dans le sous-package de prétraitement pour effectuer le codage des données.

La deuxième étape du nettoyage des données, la transformation des caractéristiques catégorielles codées non entières, est accomplie avec le code suivant :

from sklearn import preprocessing

# convert enumerated columns (sex,)
encoder = preprocessing.LabelEncoder()
df.sex = encoder.fit_transform(df.sex)
df.embarked = encoder.fit_transform(df.embarked)

Maintenant que nous avons nettoyé nos données, nous pouvons calculer la valeur moyenne de plusieurs de nos colonnes de caractéristiques regroupées par classe de passagers (pclass) et sexe.

               survived        age     sibsp     parch        fare
pclass sex                                                        
1      female  0.961832  36.839695  0.564885  0.511450  112.485402
       male    0.350993  41.029250  0.403974  0.331126   74.818213
2      female  0.893204  27.499191  0.514563  0.669903   23.267395
       male    0.145570  30.815401  0.354430  0.208861   20.934335
3      female  0.473684  22.185307  0.736842  0.796053   14.655758
       male    0.169540  25.863027  0.488506  0.287356   12.103374

Remarquez les différences significatives dans le taux de survie entre les hommes et les femmes en fonction de la classe de passagers. Notre algorithme de construction d’un arbre de décision découvrira ces différences statistiques et les utilisera pour choisir les caractéristiques sur lesquelles se diviser.

Construire un arbre de décision

Nous utiliserons scikit-learn pour créer un classificateur d’arbre de décision sur nos données. Nous commençons par diviser nos données nettoyées en un ensemble d’entraînement et un ensemble de test. À l’aide du code suivant, nous séparons la colonne d’étiquette de nos données (survie) de l’ensemble de fonctionnalités et réservons les 20 derniers enregistrements de nos données pour un ensemble de test.

X = df.drop(['survived'], axis=1).values
Y = df['survived'].values

X_train = X[:-20]
X_test  = X[-20:]
Y_train = Y[:-20]
Y_test  = Y[-20:] 

Une fois que nous avons nos ensembles d’entraînement et de test, nous pouvons créer un arbre de décision avec une profondeur maximale de 10.

# Create the real classifier depth=10
cl_tree = tree.DecisionTreeClassifier(max_depth=10, random_state=0)
cl_tree.fit(X_train, Y_train)

Notre arbre de décision de profondeur 10 est difficile à visualiser dans un article de blog, donc pour visualiser la structure de l’arbre de décision, nous avons créé un deuxième arbre et limité la profondeur de l’arbre à 3. L’image ci-dessous montre la structure de l’arbre de décision, appris par le classificateur :

sample tree
Arbre de décision Titanic appris par Scikit

Chargement du prédicteur Redis

Le module Redis-ML fournit deux commandes pour travailler avec des forêts aléatoires : ML.FOREST.ADD pour créer un arbre de décision dans le contexte d’une forêt et ML.FOREST.RUN pour évaluer un point de données à l’aide d’une forêt aléatoire. Les commandes ML.FOREST ont la syntaxe suivante :

ML.FOREST.ADD key tree path ((NUMERIC|CATEGORIC) attr val | LEAF val [STATS]) [...]
ML.FOREST.RUN key sample (CLASSIFICATION|REGRESSION)

Chaque arbre de décision dans Redis-ML doit être chargé à l’aide d’une seule commande ML.FOREST.ADD. La commande ML.FOREST.ADD se compose d’une clé Redis, suivie d’un identifiant d’arbre entier, suivi des spécifications du nœud. Les spécifications de nœud consistent en un chemin, une séquence de fichiers . (racine), l et r, représentant le chemin vers le nœud dans un arbre. Les nœuds intérieurs sont des nœuds de fractionnement ou de règle et utilisent le mot-clé NUMERIC ou CATEGORIC pour spécifier le type de règle, l’attribut à tester et la valeur du seuil à fractionner. Pour les nœuds NUMERIC, l’attribut est testé par rapport au seuil et s’il est inférieur ou égal à celui-ci, le chemin de gauche est emprunté ; sinon le bon chemin est pris. Pour les nœuds CATEGORIC, le test est l’égalité. Les valeurs égales prennent le chemin de gauche et les valeurs inégales prennent le chemin de droite.

L’algorithme d’arbre de décision dans scikit-learn traite les attributs catégoriques comme numériques, donc lorsque nous représentons l’arbre dans Redis, nous n’utiliserons que les types de nœuds NUMERIC. Pour charger l’arborescence scikit dans Redis, nous devrons implémenter une routine qui traverse l’arborescence. Le code suivant effectue une traversée pré-ordonnée de l’arbre de décision scikit pour générer une commande ML.FOREST.ADD (puisque nous n’avons qu’un seul arbre, nous générons une forêt simple avec un seul arbre).

# scikit represents decision trees using a set of arrays,
# create references to make the arrays easy to access

the_tree = cl_tree
t_nodes = the_tree.tree_.node_count
t_left = the_tree.tree_.children_left
t_right = the_tree.tree_.children_right
t_feature = the_tree.tree_.feature
t_threshold = the_tree.tree_.threshold
t_value = the_tree.tree_.value
feature_names = df.drop(['survived'], axis=1).columns.values

# create a buffer to build up our command
forrest_cmd = StringIO()
forrest_cmd.write("ML.FOREST.ADD titanic:tree 0 ")

# Traverse the tree starting with the root and a path of “.”
stack = [ (0, ".") ]

while len(stack) > 0:
    node_id, path = stack.pop()
   
    # splitter node -- must have 2 children (pre-order traversal)
    if (t_left[node_id] != t_right[node_id]):
        stack.append((t_right[node_id], path + "r")) 
        stack.append((t_left[node_id], path + "l"))
        cmd = "{} NUMERIC {} {} ".format(path, feature_names[t_feature[node_id]], t_threshold[node_id])
        forrest_cmd.write(cmd)
      
    else:
        cmd = "{} LEAF {} ".format(path, np.argmax(t_value[node_id]))
        forrest_cmd.write(cmd)

# execute command in Redis
r = redis.StrictRedis('localhost', 6379)
r.execute_command(forrest_cmd.getvalue())

Comparer les résultats

Avec l’arbre de décision chargé dans Redis, nous pouvons créer deux vecteurs pour comparer les prédictions de Redis avec les prédictions de scikit-learn :

# generate a vector of scikit-learn predictors 
s_pred = cl_tree.predict(X_test)

# generate a vector of Redis predictions
r_pred = np.full(len(X_test), -1, dtype=int)
for i, x in enumerate(X_test):
    cmd = "ML.FOREST.RUN titanic:tree "

    # iterate over each feature in the test record to build up the 
    # feature:value pairs
    for j, x_val in enumerate(x):
        cmd += "{}:{},".format(feature_names[j], x_val)  

    cmd = cmd[:-1]
    r_pred[i] = int(r.execute_command(cmd))

Pour utiliser la commande ML.FOREST.RUN, nous devons générer un vecteur de caractéristiques composé d’une liste de paires : séparées par des virgules. La partie du vecteur est un nom de fonction de chaîne qui doit correspondre aux noms de fonction utilisés dans la commande ML.FOREST.ADD.

Comparaison des valeurs de prédiction r_pred et s_pred avec les valeurs réelles des étiquettes :

Y_test: [0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0]
r_pred: [1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0]
s_pred: [1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0]

Les prédictions de Redis sont identiques à celles du package scikit-learn, y compris la mauvaise classification des éléments de test 0 et 14.

Les chances de survie d’un passager étaient fortement corrélées à la classe et au sexe, il existe donc plusieurs cas surprenants d’individus ayant une forte probabilité de survie qui ont réellement péri. Enquêter sur certaines de ces valeurs aberrantes mène à des histoires fascinantes de ce voyage fatidique. Il existe de nombreuses ressources en ligne qui racontent les histoires des passagers et de l’équipage du Titanic, nous montrant les personnes derrière les données. Je vous encourage à enquêter sur certaines des personnes mal classées et à apprendre leurs histoires.

Dans le prochain et dernier article, nous allons tout relier et conclure cette introduction à Redis-ML. En attendant, si vous avez des questions à propos de ce message ou des messages précédents, veuillez me contacter (@tague) sur Twitter.

Development Source

Related Posts

RLEC 4.2.1 apporte des contrôles granulaires à la haute disponibilité et aux performances

RLEC 4.2.1 apporte des contrôles granulaires à la haute disponibilité et aux performances

Comment HolidayMe utilise Redis Enterprise comme base de données principale

Comment HolidayMe utilise Redis Enterprise comme base de données principale

Annonce de RedisGears 1.0 : un moteur sans serveur pour Redis

Annonce de RedisGears 1.0 : un moteur sans serveur pour Redis

Clés Redis dans la RAM |  Redis

Clés Redis dans la RAM | Redis

No Comment

Laisser un commentaire

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