web-dev-qa-db-ja.com

Spark MLlibで不均衡なデータセットを扱う

私は非常に不均衡なデータセットで特定のバイナリ分類問題に取り組んでおり、不均衡なデータセットを処理するための特定の技術を実装しようとする人がいるかどうか疑問に思っていました( [〜#〜] smote [〜#〜 ] )SparkのMLlibを使用した分類問題。

私はMLLibのランダムフォレスト実装を使用しており、より大きなクラスをランダムにアンダーサンプリングする最も単純なアプローチを既に試しましたが、期待どおりに機能しませんでした。

同様の問題についてのあなたの経験に関するフィードバックをお願いします。

おかげで、

28
dbakr

Spark MLを使用したクラスの重み

この時点で、Random Forestアルゴリズムのクラスの重み付けはまだ開発中です( here を参照)

しかし、他の分類子を試してみたい場合は、この機能 Logistic Regression に既に追加されています。

データセットに80%の陽性(ラベル== 1)がある場合を考えてみましょう。したがって、理論的には陽性クラスを「アンダーサンプリング」したいと思います。ロジスティック損失目的関数は、負のクラス(ラベル== 0)をより高い重みで処理する必要があります。

Scalaこのウェイトを生成する例です。データセットの各レコードのデータフレームに新しい列を追加します。

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
  }

次に、次のように分類子を作成します。

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

詳細については、こちらをご覧ください: https://issues.Apache.org/jira/browse/SPARK-961

-予測力

確認する必要がある別の問題-予測しようとしているラベルに対して、フィーチャに「予測力」があるかどうか。アンダーサンプリング後も精度が低い場合、データセットが本質的に不均衡であるという事実とはおそらく関係ありません。


私は探索的データ分析を行います-分類器がランダムな選択よりもうまくいかない場合、機能とクラスの間に単に関係がないというリスクがあります。

  • ラベルを持つすべてのフィーチャに対して相関分析を実行します。
  • クラス固有の生成histogramsフィーチャ(つまり、各軸の特定のフィーチャの各クラスのデータのヒストグラムをプロットする)は、フィーチャが2つのクラス間で適切に区別されるかどうかを示す良い方法です。 。

オーバーフィッティング-トレーニングセットでの低いエラーとテストセットでの高いエラーは、過度に柔軟な機能セットを使用してオーバーフィットしていることを示している可能性があります。


バイアス分散-分類器が高バイアスまたは高分散の問題を抱えているかどうかを確認します。

  • トレーニングエラーと検証エラー-トレーニング例の関数として検証エラーとトレーニングセットエラーをグラフ化します(インクリメンタル学習を行う)
    • 線が同じ値に収束し、最後に近いように見える場合、分類器には高いバイアスがあります。そのような場合、データを追加しても効果はありません。分散が大きい分類器の分類器を変更するか、現在の分類器の正規化パラメーターを単純に低くします。
    • 一方、ラインが非常に離れており、トレーニングセットエラーが低く、検証エラーが高い場合、分類器の分散が高すぎます。この場合、より多くのデータを取得することが非常に役立ちます。より多くのデータを取得した後でも分散が大きすぎる場合は、正則化パラメーターを増やすことができます。
49
Serendipity

@Serendipityによるソリューションを使用しましたが、balanceDataset関数を最適化して、udfの使用を回避できます。また、使用されているラベル列を変更する機能も追加しました。これは私が終わった関数のバージョンです:

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
}

彼が述べたように分類器を作成します。

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

@dbakr不均衡なデータセットの偏った予測に対する答えは得られましたか?

元の計画かどうかはわかりませんが、最初にデータセットの多数決クラスを比率rでサブサンプリングした場合、 Sparkのロジスティック回帰の根拠のない予測を取得するには、次のいずれかを実行できます。-transform()関数で提供されるrawPredictionを使用し、log(r)でインターセプトを調整するか、.setWeightCol("classWeightCol")を使用して重みで回帰をトレーニングできます(引用記事を参照重みに設定する必要がある値を計算します)。

0
PSAfrance