[JAVA] [Apprentissage automatique avec Apache Spark] Associez l'importance (Feature Importance) d'une variable de modèle d'arbre au nom de la variable (nom de la variable explicative)

Aperçu

environnement

Modèles pouvant utiliser la méthode ** featureImportances **

** L'importance des fonctionnalités ** peut être calculée par ** Apache Spark ** à l'aide du modèle d'apprentissage basé sur l'arborescence suivant.

GBTRegressionModel < / td> Modèle de régression d'arbre avec amplification de gradient
GBTClassificationModel < / td> Modèle de classification d'arbre de renforcement de gradient
RandomForestRegressionModel < / td> Modèle de régression de forêt aléatoire
RandomForestClassificationModel < / td> Modèle de classification aléatoire des forêts
DecisionTreeRegressionModel < / td> Modèle de régression de l'arbre de décision
DecisionTreeClassificationModel < / td> Détermination du modèle de classification des arbres

Quelle est l'importance d'une variable (** Feature Importance **)?

La partie pratique de l'algorithme du système d'arborescence est qu'il peut être [calculé](https: :) quelle variable était importante dans la construction du modèle à la suite de la formation comme "importance de la variable (** Feature Importance **)". //web.stanford.edu/~hastie/ElemStatLearn/printings/ESLII_print12.pdf) (P.593).

Cependant, comme les forêts aléatoires et les arbres de renforcement de gradient sont des méthodes qui combinent plusieurs modèles appelés ** apprentissage d'ensemble , l'importance des variables ( Feature Importance **) est similaire au coefficient de régression. Il est à noter qu'il ne s'agit pas d'un index pour rechercher une interprétation directe.

Méthode pour obtenir l'importance d'une variable (** Feature Importance **)

Le modèle d'entraînement obtenu en entraînant ** RandomForest ** dans ** spark.ml **, par exemple, la classe ** RandomForestRegressionModel ** a la méthode ** RandomForestRegressionModel # featureImportances ** lors de l'utilisation de Random Forest comme algorithme. y a-t-il.

En appelant cette méthode, l'importance de la variable (** Feature Importance **) peut être calculée.

À propos, la valeur de retour de la méthode ** RandomForestRegressionModel # featureImportances ** est de type ** SparseVector **, et puisque le vecteur ne contient que la composante numérique indiquant l'importance, la valeur numérique et quelle variable sont spécifiquement. Il est difficile de comprendre s'il est pris en charge. Il est difficile de dire si c'est là.

Par conséquent, le but de cet article est d'associer les ** Importances des fonctionnalités ** aux noms de variables.

Préparez les variables à passer aux arguments en 3 étapes à associer.

On suppose que l'objet modèle appelé randomForestRegressionModel a été acquis en partant du principe que l'apprentissage est terminé.

** ÉTAPE1 ** Tout d'abord, obtenez ** Importances des fonctionnalités ** comme suit.

Vector importances=randomForestRegressionModel.featureImportances();

** ÉTAPE2 ** Ensuite, récupérez le nom de la variable utilisé comme quantité de caractéristiques. (Comme il s'agit d'un nom de variable que vous avez initialement décidé, vous pouvez le coder en dur)

String featuresCol = this.randomForestRegressionModel.getFeaturesCol()

** STEP3 ** Enfin, récupérez le schéma

StructType schema = predictions.schema();

Passez les trois que vous venez de préparer à la méthode suivante. Cette méthode est utilisée pour la liaison (plus le tri).

List<Importance> zipImportances(Vector featureImportances, String featuresCol, StructType schema) {

    final int indexOfFeaturesCol = (Integer) schema.getFieldIndex(featuresCol).get();

    final StructField featuresField = schema.fields()[indexOfFeaturesCol];

    final Metadata metadata = featuresField
            .metadata();

    final Metadata featuresFieldAttrs = metadata
            .getMetadata("ml_attr")
            .getMetadata("attrs");

    final Map<Integer, String> idNameMap = new HashMap<>();

    final String[] fieldKeys = { "nominal", "numeric", "binary" };

    final Collector<Metadata, ?, HashMap<Integer, String>> metaDataMapperFunc = Collectors
            .toMap(
                    metaData -> (int) metaData.getLong("idx"), // key of map
                    metaData -> metaData.getString("name"), // value of map
                    (oldVal, newVal) -> newVal,
                    HashMap::new);

    for (String fieldKey : fieldKeys) {
        if (featuresFieldAttrs.contains(fieldKey)) {
            idNameMap.putAll(Arrays
                    .stream(featuresFieldAttrs.getMetadataArray(fieldKey))
                    .collect(metaDataMapperFunc));
        }
    }

    final double[] importanceScores = featureImportances.toArray();

    final List<Importance> rawImportanceList = IntStream
            .range(0, importanceScores.length)
            .mapToObj(idx -> new Importance(idx, idNameMap.get(idx), importanceScores[idx], 0))
            .collect(Collectors.toList());

    final List<Importance> descSortedImportanceList = rawImportanceList
            .stream()
            .sorted(Comparator.comparingDouble((Importance ifeature) -> ifeature.score).reversed())
            .collect(Collectors.toList());

    for (int i = 0; i < descSortedImportanceList.size(); i++) {
        descSortedImportanceList.get(i).rank = i;
    }

    final List<Importance> finalImportanceList;

    switch (this.sort) {
    case ASCENDING:
        final List<Importance> ascSortedImportantFeatureList = descSortedImportanceList
                .stream()
                .sorted(Comparator.comparingDouble((Importance ifeature) -> ifeature.score))
                .collect(Collectors.toList());

        finalImportanceList = ascSortedImportantFeatureList;
        break;
    case DESCENDING:
        finalImportanceList = descSortedImportanceList;
        break;
    case UNSORTED:
    default:
        finalImportanceList = rawImportanceList;
        break;
    }
    return finalImportanceList;

}

La clé de cette méthode

La clé de cette méthode est la suivante.

final StructField featuresField = schema.fields()[indexOfFeaturesCol];

final Metadata metadata = featuresField
        .metadata();

final Metadata featuresFieldAttrs = metadata
        .getMetadata("ml_attr")
        .getMetadata("attrs");

** schema ** stocke la structure de l'ensemble de données, mais il y a une variable en tant que quantité de caractéristiques à apprendre, et les métadonnées (informations de conception) de celui-ci sont extraites et la position (index) des données est extraite. Et obtenez les informations d'association de la variable correspondante.

Métadonnées stockées dans le schéma

Ce qui arrive aux métadonnées réelles peut être extrait au format JSON suivant. (Étant donné que le format change en fonction du transformateur pris en sandwich dans le traitement du pipeline, voici un exemple)

Exemple de MetaData(Exemple de prédiction du prix des accessoires de bijoux)



{
   "ml_attr":{
      "attrs":{
         "numeric":[
            {
               "idx":0,
               "name":"weight"
            }
         ],
         "nominal":[
            {
               "vals":[
                  "platine",
                  "or",
                  "argent",
                  "Dia"
               ],
               "idx":1,
               "name":"materialIndex"
            },
            {
               "vals":[
                  "broche",
                  "Collier",
                  "Des boucles d'oreilles",
                  "bague",
                  "bracelet"
               ],
               "idx":2,
               "name":"shapeIndex"
            },
            {
               "vals":[
                  "Marque nationale célèbre",
                  "Marque super célèbre à l'étranger",
                  "Sans marque",
                  "Marques d'outre-mer célèbres"
               ],
               "idx":3,
               "name":"brandIndex"
            },
            {
               "vals":[
                  "Grand magasin",
                  "Magasin géré directement",
                  "Boutique pas chère"
               ],
               "idx":4,
               "name":"shopIndex"
            }
         ]
      },
      "num_attrs":5
   }
}

Comme vous pouvez le voir dans ces données, vous pouvez obtenir la paire de ** idx ** et ** name **, donc si vous la mappez avec ** Vector ** que vous avez obtenu avec la méthode ** FeatureImportances , ce sera comme suit. Grâce à un certain apprentissage, il est possible de connaître l'importance du nom de la variable ( nom ) ( score **).

Classement de l'importance des variables explicatives


FeatureInfo [rank=0, score=0.3015643580333446, name=weight]
FeatureInfo [rank=1, score=0.2707593044437997, name=materialIndex]
FeatureInfo [rank=2, score=0.20696065038166056, name=brandIndex]
FeatureInfo [rank=3, score=0.11316392134864546, name=shapeIndex]
FeatureInfo [rank=4, score=0.10755176579254973, name=shopIndex]

Résumé

Code source complet

Le code source est ci-dessous https://github.com/riversun/spark-ml-feature-importance-helper

Maven Également disponible dans le référentiel Maven

<dependency>
	<groupId>org.riversun</groupId>
	<artifactId>spark-ml-feature-importance-helper</artifactId>
	<version>1.0.0</version>
</dependency>

Recommended Posts

[Apprentissage automatique avec Apache Spark] Associez l'importance (Feature Importance) d'une variable de modèle d'arbre au nom de la variable (nom de la variable explicative)
Introduction à l'apprentissage automatique avec Spark "Price Estimate" # 2 Prétraitement des données (gestion des variables de catégorie)
[Apprentissage automatique avec Apache Spark] Vecteur fragmenté (vecteur fragmenté) et vecteur dense (vecteur dense)
Trouvez le nombre de jours dans un mois avec Kotlin