Kerasで不​​均衡なクラスのクラスの重みを設定する方法は?


130

class_weights適合時のパラメータディクショナリでKerasに可能性があることは知っていますが、例は見つかりませんでした。誰かがそれを提供してくれますか?

ところで、この場合、適切な実践は単純に少数派クラスをその過少表現に比例して重み付けすることですか?


Kerasを使用した新しい更新されたメソッドはありますか?辞書が3つのクラスで構成され、クラスが0:1.0 1:50.0 2:2.0になっているのはなぜですか???? するべきではない:2:1.0ですか?
チャック

回答:


112

ネットワークが1つの出力のみを生成する通常のケースについて話している場合、仮定は正しいです。アルゴリズムがクラス1のすべてのインスタンスをクラス0の 50インスタンスとして処理するように強制するには、以下を行う必要があります。

  1. ラベルとそれに関連する重みで辞書を定義する

    class_weight = {0: 1.,
                    1: 50.,
                    2: 2.}
  2. 辞書をパラメーターとしてフィードします。

    model.fit(X_train, Y_train, nb_epoch=5, batch_size=32, class_weight=class_weight)

編集:「クラス1のすべてのインスタンスをクラス0の 50インスタンスとして扱う」とは、損失関数でこれらのインスタンスにより高い値を割り当てることを意味します。したがって、損失は加重平均になります。各サンプルの重みは、class_weightとその対応するクラスによって指定されます。

Keras docsから:class_weight:クラスインデックス(整数)を重み(浮動)値にマッピングするオプションの辞書。損失関数の重み付けに使用します(トレーニング中のみ)。


1
3Dデータを使用している場合は、github.com / fchollet / keras / issues / 3653ご覧ください。
-Herve

私にとっては、エラーを示していますdicにはshape属性がありません。
フラビオフィリョ

私はこれは私が一週間であなたのために確認します8月2016のバージョン用で、Kerasはこれが機能する方法を変更することができ信じている
layser

4
@layserこれは「category_crossentropy」損失に対してのみ機能しますか?「シグモイド」と「binary_crossentropy」の損失について、どのようにclass_weightをkerasに与えますか?
ナマン

1
@layser「クラス1のすべてのインスタンスをクラス0の50インスタンスとして扱う」ことを説明できますか?トレーニングセットでは、クラス1に対応する行が50回複製されてバランスが取れているのですか、それとも他のプロセスが続くのですか?
ディビアンシュシェカル

122

単にclass_weightfromを実装できますsklearn

  1. 最初にモジュールをインポートしましょう

    from sklearn.utils import class_weight
  2. クラスの重みを計算するには、次を実行します

    class_weights = class_weight.compute_class_weight('balanced',
                                                     np.unique(y_train),
                                                     y_train)
  3. 第三に、最後にモデルのフィッティングに追加します

    model.fit(X_train, y_train, class_weight=class_weights)

注意:私はこの記事を編集したから変数名を変更しclass_weightclass_weight インポートされたモジュールを上書きしないようにするために。コメントからコードをコピーするときは、それに応じて調整してください。


21
私にとってclass_weight.compute_class_weight は、配列を生成します。Kerasで作業するには、配列を辞書に変更する必要があります。より具体的には、ステップ2の後、次を使用しますclass_weight_dict = dict(enumerate(class_weight))
-C.Lee

5
これは私にはうまくいきません。ケラスの3つのクラスの問題y_trainは、(300096, 3)numpy配列です。したがって、class_weight=行は私にTypeErrorを与えます:ハッシュできない型: 'numpy.ndarray'
Lembik

3
@Lembik同様の問題がありました。yの各行は、クラスインデックスのワンホットエンコードされたベクトルです。ワンホット表現を次のようにintに変換することで修正しましたy_ints = [y.argmax() for y in y_train]
tkocmathla

3
y_trueベクトルに複数の1が含まれるようにマルチクラスラベル付けを行う場合:[1 0 0 0 1 0 0]、たとえば、xにラベル0と4が含まれる場合でも、それぞれの合計#ラベルのバランスが取れていません。それでクラスの重みをどのように使用しますか?
アーロック

22

私はこの種のルールをclass_weight以下に使用します:

import numpy as np
import math

# labels_dict : {ind_label: count_label}
# mu : parameter to tune 

def create_class_weight(labels_dict,mu=0.15):
    total = np.sum(labels_dict.values())
    keys = labels_dict.keys()
    class_weight = dict()

    for key in keys:
        score = math.log(mu*total/float(labels_dict[key]))
        class_weight[key] = score if score > 1.0 else 1.0

    return class_weight

# random labels_dict
labels_dict = {0: 2813, 1: 78, 2: 2814, 3: 78, 4: 7914, 5: 248, 6: 7914, 7: 248}

create_class_weight(labels_dict)

math.log非常に不均衡なクラスの重みを滑らかにします!これは返します:

{0: 1.0,
 1: 3.749820767859636,
 2: 1.0,
 3: 3.749820767859636,
 4: 1.0,
 5: 2.5931008483842453,
 6: 1.0,
 7: 2.5931008483842453}

3
クラスのサンプル数をサンプルの総数で割るのではなく、ログを使用するのはなぜですか?model.fit_generator(...)のparam class_weightに理解できないことがあると思います
-startoftext

@startoftextそれは私がそれをやった方法ですが、私はあなたがそれを逆にしたと思います。n_total_samples / n_class_samples各クラスに使用しました。
コリン

2
この例では、クラス0(2813の例あり)およびクラス6(7914の例あり)の重みは正確に1.0です。何故ですか?クラス6は数倍大きいです!クラス0をアップスケールし、クラス6をダウンスケールして同じレベルにする必要があります。
ヴラディスラフ・ドヴガレス

9

注:コメントを参照してください。この答えは時代遅れです。

すべてのクラスを均等に重み付けするには、次のようにclass_weightを「auto」に設定するだけです。

model.fit(X_train, Y_train, nb_epoch=5, batch_size=32, class_weight = 'auto')

1
class_weight='auto'Kerasのドキュメントにもソースコードにも参照が見つかりませんでした。これを見つけた場所を教えていただけますか?
ファビオペレス

2
この答えはおそらく間違っています。:この問題をチェックgithub.com/fchollet/keras/issues/5116
ファビオ・ペレス

奇数。コメントを投稿した時点でclass_balanced = 'auto'を使用していましたが、現在、その参照を見つけることができません。おそらく、Kerasが急速に進化しているので、変更されています。
デビッドグロッペ

上記Kerasの問題で指定されているように、任意のランダムな文字列を渡すことができclass_weight、効果はありません。したがって、この答えは正しくありません。
ncasas

3

class_weightは問題ありませんが、@ Aalokが述べたように、ワンホットエンコードマルチラベルクラスの場合、これは機能しません。この場合、sample_weightを使用します

sample_weight:各サンプルのモデルの損失に適用する重みを含む、xと同じ長さのオプションの配列。時間データの場合、形状(サンプル、sequence_length)を含む2D配列を渡して、すべてのサンプルのすべてのタイムステップに異なる重みを適用できます。この場合、compile()でsample_weight_mode = "temporal"を指定する必要があります。

sample_weights、各トレーニングサンプルの重み提供するために使用されます。つまり、トレーニングサンプルと同じ要素数の1D配列を渡す必要があります(これらの各サンプルの重みを示します)。

class_weights、各出力クラスの重みまたはバイアスを提供するために使用されます。つまり、分類しようとしている各クラスに重みを渡す必要があります。

sample_weightには、その形状が評価されるため、numpy配列を指定する必要があります。

この回答も参照してください:https : //stackoverflow.com/questions/48315094/using-sample-weight-in-keras-for-sequence-labelling


2

https://github.com/keras-team/keras/issues/2115でソリューションに追加します。偽陽性と偽陰性に対して異なるコストが必要なクラスの重み付け以上が必要な場合。新しいkerasバージョンでは、以下のようにそれぞれの損失関数をオーバーライドすることができます。weightsは正方行列であることに注意してください。

from tensorflow.python import keras
from itertools import product
import numpy as np
from tensorflow.python.keras.utils import losses_utils

class WeightedCategoricalCrossentropy(keras.losses.CategoricalCrossentropy):

    def __init__(
        self,
        weights,
        from_logits=False,
        label_smoothing=0,
        reduction=losses_utils.ReductionV2.SUM_OVER_BATCH_SIZE,
        name='categorical_crossentropy',
    ):
        super().__init__(
            from_logits, label_smoothing, reduction, name=f"weighted_{name}"
        )
        self.weights = weights

    def call(self, y_true, y_pred):
        weights = self.weights
        nb_cl = len(weights)
        final_mask = keras.backend.zeros_like(y_pred[:, 0])
        y_pred_max = keras.backend.max(y_pred, axis=1)
        y_pred_max = keras.backend.reshape(
            y_pred_max, (keras.backend.shape(y_pred)[0], 1))
        y_pred_max_mat = keras.backend.cast(
            keras.backend.equal(y_pred, y_pred_max), keras.backend.floatx())
        for c_p, c_t in product(range(nb_cl), range(nb_cl)):
            final_mask += (
                weights[c_t, c_p] * y_pred_max_mat[:, c_p] * y_true[:, c_t])
        return super().call(y_true, y_pred) * final_mask

0

ミニストデータセットを使用して損失関数でクラスの重みをコーディングする次の例を見つけました。こちらのリンクをご覧くださいhttps : //github.com/keras-team/keras/issues/2115

def w_categorical_crossentropy(y_true, y_pred, weights):
    nb_cl = len(weights)
    final_mask = K.zeros_like(y_pred[:, 0])
    y_pred_max = K.max(y_pred, axis=1)
    y_pred_max = K.reshape(y_pred_max, (K.shape(y_pred)[0], 1))
    y_pred_max_mat = K.equal(y_pred, y_pred_max)
    for c_p, c_t in product(range(nb_cl), range(nb_cl)):
        final_mask += (weights[c_t, c_p] * y_pred_max_mat[:, c_p] * y_true[:, c_t])
    return K.categorical_crossentropy(y_pred, y_true) * final_mask

0
from collections import Counter
itemCt = Counter(trainGen.classes)
maxCt = float(max(itemCt.values()))
cw = {clsID : maxCt/numImg for clsID, numImg in itemCt.items()}

これは、ジェネレーターまたは標準で動作します。最大クラスの重みは1で、他のクラスの最大値は1を超えています。

クラスの重みは辞書タイプの入力を受け入れます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.