web-dev-qa-db-ja.com

Tensorフローのクラス不均衡バイナリ分類器の損失関数

ターゲットクラス(500k、31K)間のクラスの不均衡が大きいバイナリ分類問題にディープラーニングを適用しようとしています。私は次のようなカスタム損失関数を書きたいと思います:maximize(100-((predicted_smallerclass)/(total_smallerclass))* 100)

このロジックをどのように構築できるかについてのポインタを評価してください。

50

ロジットを乗算することにより、損失関数にクラスの重みを追加できます。通常のクロスエントロピー損失は次のとおりです。

loss(x, class) = -log(exp(x[class]) / (\sum_j exp(x[j])))
               = -x[class] + log(\sum_j exp(x[j]))

加重の場合:

loss(x, class) = weights[class] * -x[class] + log(\sum_j exp(weights[class] * x[j]))

したがって、ロジットを乗算することにより、各クラスの予測をそのクラスの重みで再スケーリングします。

例えば:

ratio = 31.0 / (500.0 + 31.0)
class_weight = tf.constant([ratio, 1.0 - ratio])
logits = ... # shape [batch_size, 2]
weighted_logits = tf.mul(logits, class_weight) # shape [batch_size, 2]
xent = tf.nn.softmax_cross_entropy_with_logits(
  weighted_logits, labels, name="xent_raw")

バッチごとの重量をサポートする標準の損失関数があります:

tf.losses.sparse_softmax_cross_entropy(labels=label, logits=logits, weights=weights)

重みをクラスの重みから例ごとの重みに変換する必要がある場合(形状[batch_size]を使用)。 ドキュメントはこちら を参照してください。

45
ilblackdragon

あなたが提案したコードは私には間違っているようです。損失に重さを掛けるべきだ、と私は同意します。

しかし、ロジットにクラスの重みを掛けると、次のようになります:

weights[class] * -x[class] + log( \sum_j exp(x[j] * weights[class]) )

第2項は次と等しくありません:

weights[class] * log(\sum_j exp(x[j]))

これを示すために、後者を次のように書き換えることができます。

log( (\sum_j exp(x[j]) ^ weights[class] )

だからここに私が提案しているコードがあります:

ratio = 31.0 / (500.0 + 31.0)
class_weight = tf.constant([[ratio, 1.0 - ratio]])
logits = ... # shape [batch_size, 2]

weight_per_label = tf.transpose( tf.matmul(labels
                           , tf.transpose(class_weight)) ) #shape [1, batch_size]
# this is the weight for each datapoint, depending on its label

xent = tf.mul(weight_per_label
         , tf.nn.softmax_cross_entropy_with_logits(logits, labels, name="xent_raw") #shape [1, batch_size]
loss = tf.reduce_mean(xent) #shape 1
40
JL Meunier

tf.nn.weighted_cross_entropy_with_logits() を使用し、pos_weightを1 /(正の期待される比率)に設定します。

11
Malay Haldar

Tensorflowでガイドを確認できます https://www.tensorflow.org/api_guides/python/contrib.losses

...

スカラー損失を指定すると、バッチ全体で損失が再スケーリングされますが、バッチサンプルごとの損失を再スケーリングすることが必要な場合があります。たとえば、正確に取得するためにより重要な特定の例がある場合、他のサンプルのミスの重要性が低いほど損失を大きくしたい場合があります。この場合、バッチ内の各サンプルの損失が対応する重み要素によってスケーリングされる結果になる、batch_sizeの長さの重みベクトルを提供できます。たとえば、精度を最大限に高めたいが、特定のクラスの高精度を取得することに特に関心がある分類問題の場合を考えてみましょう。

inputs, labels = LoadData(batch_size=3)
logits = MyModelPredictions(inputs)

# Ensures that the loss for examples whose ground truth class is `3` is 5x
# higher than the loss for all other examples.
weight = tf.multiply(4, tf.cast(tf.equal(labels, 3), tf.float32)) + 1

onehot_labels = tf.one_hot(labels, num_classes=5)
tf.contrib.losses.softmax_cross_entropy(logits, onehot_labels, weight=weight)

私は複数のクラスの同様の不均衡なデータセットで作業しなければなりませんでしたが、これは私がそれを介して働いた方法です、それが同様の解決策を探している誰かに役立つことを願っています:

これは、トレーニングモジュールの内部に入ります。

from sklearn.utils.class_weight import compute_sample_weight
#use class weights for handling unbalanced dataset
if mode == 'INFER' #test/dev mode, not weighing loss in test mode
   sample_weights = np.ones(labels.shape)
else:
   sample_weights = compute_sample_weight(class_weight='balanced', y=labels)

これは、モデルクラス定義の内部に入ります。

#an extra placeholder for sample weights
#assuming you already have batch_size tensor
self.sample_weight = tf.placeholder(dtype=tf.float32, shape=[None],
                       name='sample_weights')
cross_entropy_loss = tf.nn.sparse_softmax_cross_entropy_with_logits(
                       labels=self.label, logits=logits, 
                       name='cross_entropy_loss')
cross_entropy_loss = tf.reduce_sum(cross_entropy_loss*self.sample_weight) / batch_size
3
bitspersecond

Ops tf.nn.weighted_cross_entropy_with_logits() は2つのクラスに対して:

classes_weights = tf.constant([0.1, 1.0])
cross_entropy = tf.nn.weighted_cross_entropy_with_logits(logits=logits, targets=labels, pos_weight=classes_weights)
2
Denis Shcheglov