Keras'ta dengesiz sınıflar için sınıf ağırlıkları nasıl belirlenir?


129

Keras'da class_weightsparametre sözlüğü ile uydurma ihtimalinin olduğunu biliyorum , ancak hiçbir örnek bulamadım. Biri bir tane sağlamak için çok nazik misiniz?

Bu arada, bu durumda uygun praksis, azınlık sınıfını düşük beyanına göre orantılı olarak arttırmak için mi?


Keras kullanarak yeni bir güncellenmiş yöntem var mı? neden sözlük üç sınıftan ve sınıf için oluşuyor: 0: 1.0 1: 50.0 2: 2.0 ???? yapmamalı: 2: 1.0?
Chuck

Yanıtlar:


112

Ağınızın yalnızca bir çıktı ürettiği normal durum hakkında konuşuyorsanız, varsayımınız doğrudur. Algoritmanınızı sınıf 1'in her örneğine 0 sınıfının 50 örneği olarak göstermeye zorlamak için yapmanız gereken:

  1. Etiketlerinizle ve bunlarla ilişkili ağırlıklarıyla bir sözlük tanımlayın.

    class_weight = {0: 1.,
                    1: 50.,
                    2: 2.}
    
  2. Sözlüğü parametre olarak besleyin:

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

EDIT: " sınıf 1'in her örneğine sınıf 0'ın 50 örneği olarak bak ", kayıp fonksiyonunuzda bu örneklere daha yüksek değer atayacağınız anlamına gelir. Dolayısıyla, kayıp, her bir numunenin ağırlığının class_weight ve karşılık gelen sınıf ile belirtildiği ağırlıklı bir ortalamaya dönüşür .

Keras docs'dan : class_weight : İsteğe bağlı sözlük eşleme sınıf indekslerini (tamsayılar) ağırlık (değişken) değerine eşleme, kayıp işlevini ağırlıklandırmak için kullanılır (sadece eğitim sırasında).


1
Ayrıca, 3B verileriyle çalışıyorsanız github.com/fchollet/keras/issues/3653 adresini ziyaret edin.
herve

Benim için bir hata veriyor, dic, şekil özelliğine sahip değil.
Flávio Filho

Bu bir hafta içinde sizin için doğrulayacaktır Ağustos 2016 sürümü için ise, Keras bu çalışma şeklini değiştirme olabilir inanıyoruz
layser

4
@layser Bu sadece 'category_crossentropy' kaybı için çalışıyor mu? Class_weight 'sigmoid' ve 'binary_crossentropy' kaybı için nasıl keras verirsiniz?
Naman

1
@layser `Sınıf 1'in her bir örneğini 0 sınıfının 50 örneği olarak ele almak için 'açıklayabilir misiniz? Eğitim setinde, sınıf 1'e karşılık gelen satırın dengelenmesi için 50 kez çoğaltılması mı yoksa diğer bazı işlemlerin yapılması mı?
Divyanshu Shekhar

122

Sadece uygulamak class_weightdan sklearn:

  1. Önce modülü içe aktaralım

    from sklearn.utils import class_weight
  2. Sınıf ağırlığını hesaplamak için aşağıdakileri yapın

    class_weights = class_weight.compute_class_weight('balanced',
                                                     np.unique(y_train),
                                                     y_train)
    
  3. Üçüncü ve son olarak model montajına ekleyin

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

Dikkat : Bu yazıyı düzenledim ve içe aktarılan modülün üzerine yazmamak için değişken adını class_weight'den class_weight s'ye değiştirdim . Yorumlardan kod kopyalarken uygun şekilde ayarlayın.


21
Benim için, class_weight.compute_class_weight bir dizi üretir, Keras ile çalışabilmek için onu bir dize değiştirmem gerekiyor. Daha spesifik olarak, 2. adımdan sonra, 4class_weight_dict = dict(enumerate(class_weight))
33'te

5
Bu benim için çalışmıyor. Keras y_traincinsinden üç sınıf bir problem için (300096, 3)numpy dizisidir. Bu yüzden class_weight=çizgi bana TypeError verir: unhashable type: 'numpy.ndarray'
Lembik

3
@ Lembik Benzer bir problemim vardı, burada her y satırı, sınıf indeksinin tek bir sıcak kodlanmış vektörüdür. Böyle bir int bir sıcak temsil dönüştürerek sabit: y_ints = [y.argmax() for y in y_train].
tkocmathla

3
Peki ya çok sınıflı etiketler yapıyorum ki y_true vektörlerimde birden fazla 1 var. Örneğin, [1 0 0 0 1 0 0], örneğin, bazı x'lerde 0 ve 4 etiketleri var. etiketler dengeli değil. Bununla beraber sınıf ağırlıklarını nasıl kullanırım?
Aalok

22

Bu tür kuralları kullanıyorum 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çok dengesiz sınıfların ağırlıklarını düzeltir! Bu döner:

{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
Bir sınıf için sadece örnek sayısını toplam örnek sayısına bölmek yerine neden log kullanılmalı? Anlamadığım bir şey olduğunu sanıyorum model.fit_generator (...)
startoftext

@startoftext Bu şekilde yaptım, ama tersine çevirdiğini düşünüyorum. n_total_samples / n_class_samplesHer ders için kullandım .
colllin

2
Örneğinizde sınıf 0 (2813 örnek var) ve sınıf 6 (7914 örnek var) tam olarak 1.0 ağırlığa sahip. Neden? Sınıf 6 birkaç kat daha büyük! Sınıf 0'ın yükseltilmesini ve sınıf 6'nın aynı seviyeye getirilmesi için küçültülmesini istersiniz.
Vladislavs Dovgalecs

9

NOT: Yorumlara bakın, bu cevap modası geçmiş.

Tüm sınıfları eşit olarak ağırlıklandırmak için, şimdi class_weight ayarını "auto" olarak ayarlayabilirsiniz:

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

1
class_weight='auto'Keras belgelerinde veya kaynak kodunda referans bulamadım . Bize bunu nerede bulduğunu gösterebilir misin?
Fábio Perez

2
Bu cevap muhtemelen yanlıştır. Bu sorunu kontrol edin: github.com/fchollet/keras/issues/5116
Fábio Perez

Garip. Yorum gönderdiğim sırada class_balanced = 'auto' kullanıyordum, ancak şimdi referans bulamıyorum. Belki de Keras hızla evrimleşmekte olduğu için değişmiştir.
David Groppe

Yukarıda belirtilen Keras sayısında belirtildiği gibi , rastgele herhangi bir dizgiyi geçebilirsiniz class_weightve bunun bir etkisi olmaz. Bu nedenle bu cevap doğru değil.
ncasas

3

class_weight gayet iyi ancak @Aalok'un dediği gibi, bir sıcak kodlama çok etiketli sınıflarsanız bu işe yaramaz. Bu durumda, sample_weight kullanın :

sample_weight: Her numune için modelin kaybına uygulanacak ağırlıkları içeren, x ile aynı uzunlukta isteğe bağlı dizi. Geçici veri söz konusu olduğunda, her bir numunenin her zaman adımına farklı bir ağırlık uygulamak için şekilli bir 2D diziyi (örnekler, sıra_ uzunluğu) geçirebilirsiniz. Bu durumda compile () 'deki sample_weight_mode = "temporal"' ı belirttiğinizden emin olmalısınız.

sample_weights , her eğitim numunesi için bir ağırlık sağlamak için kullanılır . Bu, antrenman numunelerinizle aynı sayıda elemana sahip bir 1D dizisini geçmeniz gerektiği anlamına gelir (bu numunelerin her birinin ağırlığını gösterir).

class_weights , her çıktı sınıfı için bir ağırlık veya önyargı sağlamak için kullanılır . Bu, sınıflandırmaya çalıştığınız her sınıf için bir ağırlık geçirmeniz gerektiği anlamına gelir.

sample_weight bir şekil dizisi verilmelidir, çünkü şekli değerlendirilecektir.

Ayrıca bu cevaba bakınız: https://stackoverflow.com/questions/48315094/using-sample-weight-in-keras-for-sequence-labelling


2

Çözüme https://github.com/keras-team/keras/issues/2115 adresinden ekleyin . Yanlış pozitifler ve yanlış negatifler için farklı maliyetler istediğiniz yerde sınıflandırmadan daha fazlasına ihtiyacınız varsa. Şimdi yeni keras versiyonuyla ilgili kayıp fonksiyonunu aşağıda verilen şekilde geçersiz kılabilirsiniz. Bunun weightsbir kare matris olduğunu unutmayın .

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

Bakanlık veri setini kullanarak kayıp fonksiyonunda sınıf ağırlıklarını kodlayan aşağıdaki örneği buldum. Buraya bakınız: 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()}

Bu bir jeneratör veya standart ile çalışır. En büyük sınıfınızın ağırlığı 1, diğerlerinin ise en büyük sınıfa göre 1'den büyük değerleri olacaktır.

sınıf ağırlıkları bir sözlük türü girişini kabul eder.

Licensed under cc by-sa 3.0 with attribution required.