Traitement des ensembles de données déséquilibrés dans Spark MLlib

je travaille sur un problème de classification binaire particulier avec un ensemble de données fortement déséquilibré, et je me demandais si quelqu'un avait essayé de mettre en œuvre des techniques spécifiques pour traiter les ensembles de données déséquilibrées (comme SMOTE) dans les problèmes de classification en utilisant le MLlib de Spark.

J'utilise L'implémentation de la forêt au hasard de MLLib et j'ai déjà essayé l'approche la plus simple de sous-échantillonner au hasard la classe plus grande mais ça n'a pas marché aussi bien que moi devrait.

je vous saurais gré de me faire part de vos commentaires sur votre expérience de questions semblables.

Merci,

19
demandé sur zero323 2015-10-27 19:04:16

4 réponses

A partir de ce moment précis, la pondération de classe pour le Forêt Aléatoire l'algorithme est encore en développement (voir ici)

Mais Si vous êtes prêt à essayer d'autres classificateurs - cette fonctionnalité a déjà été ajouté à Régression Logistique.

prenons le cas où nous avons 80% de positifs (label == 1) dans l'ensemble de données, donc, théoriquement, nous voulons "le sous-échantillon" le positif de la classe. La fonction d'objectif de perte logistique doit traiter la classe négative (étiquette == 0) avec un poids plus élevé.

voici un exemple dans Scala de générer ce poids, nous ajoutons une nouvelle colonne au datagramme pour chaque enregistrement dans l'ensemble de données:

def balanceDataset(dataset: DataFrame): DataFrame = {

    // Re-balancing (weighting) of records to be used in the logistic loss objective function
    val numNegatives = dataset.filter(dataset("label") === 0).count
    val datasetSize = dataset.count
    val balancingRatio = (datasetSize - numNegatives).toDouble / datasetSize

    val calculateWeights = udf { d: Double =>
      if (d == 0.0) {
        1 * balancingRatio
      }
      else {
        (1 * (1.0 - balancingRatio))
      }
    }

    val weightedDataset = dataset.withColumn("classWeightCol", calculateWeights(dataset("label")))
    weightedDataset
  }

ensuite, nous créons un classeur comme suit:

new LogisticRegression().setWeightCol("classWeightCol").setLabelCol("label").setFeaturesCol("features")

Pour plus de détails, voir ici: https://issues.apache.org/jira/browse/SPARK-9610

- Pouvoir Prédictif

une autre question vous devriez vérifier - si vos caractéristiques ont un "pouvoir prédictif" pour l'étiquette que vous essayez de prévoir. Dans un cas où, après un sous-échantillonnage, vous avez encore une faible précision, peut-être que cela n'a rien à voir avec le fait que votre ensemble de données est déséquilibré par nature.


je voudrais faire un analyse exploratoire des données - Si le classificateur ne fait pas mieux qu'un choix aléatoire, il y a un risque qu'il n'y a tout simplement aucun lien entre les caractéristiques et la classe.

  • Exécuter analyse de corrélation pour chaque fonction avec l'étiquette.
  • Génération de classe spécifique histogrammes pour les fonctions (c'est à dire de tracé des histogrammes des données pour chaque classe, pour une fonction sur le même axe) peut également être un bon moyen de montrer si un fonction discrimine bien entre les deux classe.

Survitting - une erreur faible sur votre ensemble d'entraînement et une erreur élevée sur votre ensemble d'essai pourrait être une indication que vous survit en utilisant un ensemble de fonctionnalités trop flexible.


biais variance-vérifiez si votre Classificateur souffre d'un biais élevé ou d'un problème de grande variance.

  • erreur de formation vs. erreur de validation-graphique l'erreur de validation et l'erreur de jeu de formation, en fonction des exemples de formation (faire l'apprentissage incrémental)
    • Si les lignes ne semblent converger vers la même valeur et sont proches à la fin, alors votre classificateur a des biais importants. Dans ce cas, ajouter plus de données n'aidera pas. Changez le classificateur pour un qui a une plus grande variance, ou tout simplement baisser le paramètre de régularisation de votre courant.
    • en revanche, Si les lignes sont très éloignés, et vous avez un faible formation d'erreur mais élevé d'erreur de validation, puis votre classificateur est trop élevé écart. Dans ce cas, obtenir plus de données est très susceptible d'aider. Si après avoir obtenu plus de données la variance sera encore trop élevée, vous pouvez augmenter le paramètre de régularisation.
33
répondu Serendipity 2017-01-10 22:11:26

j'ai utilisé la solution par @Serendipity, mais nous pouvons optimiser la fonction balanceDataset pour éviter d'utiliser un udf. J'ai également ajouté la possibilité de changer l'étiquette de la colonne utilisée. C'est la version de la fonction que j'ai terminé avec:

def balanceDataset(dataset: DataFrame, label: String = "label"): DataFrame = {
  // Re-balancing (weighting) of records to be used in the logistic loss objective function
  val (datasetSize, positives) = dataset.select(count("*"), sum(dataset(label))).as[(Long, Double)].collect.head
  val balancingRatio = positives / datasetSize

  val weightedDataset = {
    dataset.withColumn("classWeightCol", when(dataset(label) === 0.0, balancingRatio).otherwise(1.0 - balancingRatio))
  }
  weightedDataset
}

nous créons le classificateur comme il a déclaré wtih:

new LogisticRegression().setWeightCol("classWeightCol").setLabelCol("label").setFeaturesCol("features")
1
répondu kanielc 2017-04-07 13:02:18

@dbakr avez-vous obtenu une réponse pour votre prédiction biaisée sur votre ensemble de données déséquilibré ?

bien que je ne sois pas sûr que ce soit votre plan original, notez que si vous sous-échantillonnez d'abord la classe majoritaire de votre ensemble de données par un ratio r, alors, afin d'obtenir des prédictions non pondérées pour la régression logistique de Spark, vous pouvez soit: - utilisez la prédiction brute fournie par le transform() fonction et ajuster l'intercept avec log(r) - ou vous pouvez former votre régression avec des poids en utilisant .setWeightCol("classWeightCol") (voir l'article cité ici pour trouver la valeur qui doit être définie dans le poids).

0
répondu PSAfrance 2017-08-24 09:14:48

nous étions également aux prises avec le problème de la résolution d'un ensemble de données déséquilibré et nous voulions utiliser le SMOTE pour le suréchantillonnage des minorités. L'implémentation de Python avait des limites avec l'exécution d'un seul noeud et voulait quelque chose qui fonctionnerait dans un environnement distribué. Nous avons développé notre algorithme en utilisant à la fois LSH (Locality Sensitivity Hashing ) et l'avons récemment amélioré avec des arbres métriques. Le travail a été publié dans ACM icihttps://dl.acm.org/citation.cfm?id=3170535) nous apporterons le travail de milieu ouvert tôt pour apporter d'autres améliorations. Travail en équipe (Avnish Rastogi, Zamir Siddiqui et Nitin Narang)

-1
répondu Nitin 2018-04-09 14:25:30