Bagaimana cara mengatur bobot kelas untuk kelas yang tidak seimbang di Keras?


130

Saya tahu bahwa ada kemungkinan Keras dengan class_weightskamus parameter yang pas, tapi saya tidak bisa menemukan contoh. Akankah seseorang dengan baik hati menyediakannya?

Ngomong-ngomong, dalam hal ini praksis yang tepat hanya untuk membebani kelas minoritas secara proporsional dengan perwakilannya yang kurang?


Apakah ada metode baru yang diperbarui menggunakan Keras? mengapa kamus terdiri dari tiga kelas dan untuk kelas: 0: 1.0 1: 50.0 2: 2.0 ???? tidak seharusnya: 2: 1.0 juga?
Chuck

Jawaban:


112

Jika Anda berbicara tentang kasus biasa, di mana jaringan Anda hanya menghasilkan satu output, maka asumsi Anda sudah benar. Untuk memaksa algoritme Anda memperlakukan setiap instance dari kelas 1 sebagai 50 instance dari kelas 0, Anda harus:

  1. Tetapkan kamus dengan label Anda dan bobotnya yang terkait

    class_weight = {0: 1.,
                    1: 50.,
                    2: 2.}
  2. Beri makan kamus sebagai parameter:

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

EDIT: "memperlakukan setiap instance dari kelas 1 sebagai 50 instance dari kelas 0 " berarti bahwa dalam fungsi kerugian Anda, Anda memberikan nilai yang lebih tinggi untuk instance ini. Oleh karena itu, kerugian menjadi rata-rata tertimbang, di mana berat masing-masing sampel ditentukan oleh class_weight dan kelas yang sesuai.

Dari Keras docs: class_weight : Indeks kelas pemetaan kamus opsional (integer) ke nilai weight (float), digunakan untuk memberi bobot pada fungsi kerugian (hanya selama pelatihan).


1
Lihat juga di github.com/fchollet/keras/issues/3653 jika Anda bekerja dengan data 3D.
herve

Bagi saya itu memberikan kesalahan karena tidak memiliki atribut bentuk.
Flávio Filho

Saya percaya Keras bisa mengubah cara kerjanya, ini untuk versi Agustus 2016. Saya akan memverifikasi untuk Anda dalam seminggu
layser

4
@layser Apakah ini hanya berfungsi untuk kerugian 'category_crossentropy'? Bagaimana Anda memberikan class_weight ke keras untuk kehilangan 'sigmoid' dan 'binary_crossentropy'?
Naman

1
@layser Bisakah Anda menjelaskan `untuk memperlakukan setiap instance dari kelas 1 sebagai 50 instance dari kelas 0`? Apakah itu dalam set latihan, baris yang sesuai dengan kelas 1 diduplikasi 50 kali untuk membuatnya seimbang atau proses lain mengikuti?
Divyanshu Shekhar

122

Anda cukup menerapkan class_weightdari sklearn:

  1. Mari kita impor modulnya terlebih dahulu

    from sklearn.utils import class_weight
  2. Untuk menghitung bobot kelas, lakukan hal berikut

    class_weights = class_weight.compute_class_weight('balanced',
                                                     np.unique(y_train),
                                                     y_train)
  3. Ketiga dan terakhir menambahkannya ke model pas

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

Perhatian : Saya mengedit posting ini dan mengubah nama variabel dari class_weight menjadi class_weight s agar tidak menimpa modul yang diimpor. Sesuaikan sesuai saat menyalin kode dari komentar.


21
Bagi saya, class_weight.compute_class_weight menghasilkan array, saya perlu mengubahnya menjadi dict agar dapat bekerja dengan Keras. Lebih khusus lagi, setelah langkah 2, gunakanclass_weight_dict = dict(enumerate(class_weight))
C.Lee

5
Ini tidak berhasil untuk saya. Untuk masalah tiga kelas dalam keras y_trainadalah (300096, 3)array numpy. Jadi class_weight=barisnya memberi saya TypeError: tipe yang tidak dapat ditembus: 'numpy.ndarray'
Lembik

3
@ Lembik Saya punya masalah yang sama, di mana setiap baris y adalah vektor kode kelas yang di-hot-encoded. Aku tetap dengan mengubah representasi satu-panas ke int seperti ini: y_ints = [y.argmax() for y in y_train].
tkocmathla

3
Bagaimana jika saya melakukan pelabelan multi-kelas sehingga vektor y_true saya memiliki beberapa 1 di dalamnya: [1 0 0 0 1 0 0] misalnya, di mana beberapa x memiliki label 0 dan 4. Bahkan, total # dari masing-masing dari saya label tidak seimbang. Bagaimana saya menggunakan bobot kelas dengan itu?
Aalok

22

Saya menggunakan aturan semacam ini untuk 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.logmenghaluskan beban untuk kelas yang sangat tidak seimbang! Ini mengembalikan:

{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
Mengapa menggunakan log alih-alih hanya membagi jumlah sampel untuk suatu kelas dengan jumlah total sampel? Saya berasumsi ada sesuatu yang saya tidak mengerti masuk ke param class_weight pada model.fit_generator (...)
startoftext

@startoftext Begitulah cara saya melakukannya, tapi saya pikir Anda sudah terbalik. Saya menggunakan n_total_samples / n_class_samplesuntuk setiap kelas.
colllin

2
Dalam contoh Anda kelas 0 (memiliki 2813 contoh) dan kelas 6 (memiliki 7914 contoh) memiliki bobot tepat 1,0. Mengapa demikian? Kelas 6 beberapa kali lebih besar! Anda ingin kelas 0 ditingkatkan dan kelas 6 diturunkan untuk membawa mereka ke level yang sama.
Vladislavs Dovgalecs

9

CATATAN: Lihat komentar, jawaban ini sudah usang.

Untuk menimbang semua kelas secara merata, Anda sekarang dapat mengatur class_weight menjadi "otomatis" seperti:

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

1
Saya tidak dapat menemukan referensi class_weight='auto'dalam dokumentasi Keras maupun dalam kode sumber. Bisakah Anda tunjukkan di mana Anda menemukan ini?
Fábio Perez

2
Jawaban ini mungkin salah. Lihat masalah ini: github.com/fchollet/keras/issues/5116
Fábio Perez

Aneh. Saya menggunakan class_balanced = 'auto' pada saat saya memposting komentar, tetapi saya tidak dapat menemukan referensi untuk itu sekarang. Mungkin itu telah berubah karena Keras telah berkembang pesat.
David Groppe

Seperti yang ditentukan dalam masalah Keras yang ditunjukkan di atas , Anda dapat meneruskan string acak apa pun class_weightdan tidak akan berpengaruh apa pun. Karena itu jawaban ini tidak benar.
ncasas

3

class_weight baik-baik saja tetapi seperti yang dikatakan @Aokok ini tidak akan berhasil jika Anda adalah kelas multilabel enkode satu-panas. Dalam hal ini, gunakan sample_weight :

sample_weight: array opsional dengan panjang yang sama dengan x, berisi bobot untuk diterapkan pada kehilangan model untuk setiap sampel. Dalam hal data temporal, Anda bisa melewatkan array 2D dengan bentuk (sampel, sequence_length), untuk menerapkan bobot yang berbeda untuk setiap catatan waktu setiap sampel. Dalam hal ini Anda harus memastikan untuk menentukan sample_weight_mode = "temporal" dalam kompilasi ().

sample_weights digunakan untuk memberikan bobot untuk setiap sampel pelatihan . Itu berarti bahwa Anda harus lulus array 1D dengan jumlah elemen yang sama dengan sampel pelatihan Anda (menunjukkan bobot untuk masing-masing sampel).

class_weights digunakan untuk memberikan bobot atau bias untuk setiap kelas output . Ini berarti Anda harus memberikan bobot untuk setiap kelas yang Anda coba klasifikasikan.

sample_weight harus diberi array numpy, karena bentuknya akan dievaluasi.

Lihat juga jawaban ini: https://stackoverflow.com/questions/48315094/using-sample-weight-in-keras-for-afterence-labelling


2

Menambahkan ke solusi di https://github.com/keras-team/keras/issues/2115 . Jika Anda membutuhkan lebih dari pembobotan kelas di mana Anda ingin biaya yang berbeda untuk positif palsu dan negatif palsu. Dengan versi keras baru sekarang Anda hanya dapat mengesampingkan fungsi kerugian masing-masing seperti yang diberikan di bawah ini. Perhatikan bahwa itu weightsadalah matriks persegi.

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

Saya menemukan contoh pengkodean bobot kelas dalam fungsi kerugian menggunakan dataset minist. Lihat tautan di sini: 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()}

Ini bekerja dengan generator atau standar. Kelas terbesar Anda akan memiliki bobot 1 sedangkan yang lain akan memiliki nilai lebih besar dari 1 relatif terhadap kelas terbesar.

bobot kelas menerima input jenis kamus

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.