วิธีการตั้งค่าน้ำหนักของคลาสสำหรับคลาสที่ไม่สมดุลใน Keras


129

ฉันรู้ว่ามีความเป็นไปได้ใน Keras ด้วยclass_weightsพจนานุกรมพารามิเตอร์ที่เหมาะสม แต่ฉันไม่พบตัวอย่างใด ๆ ใครบางคนจะใจดีที่จะให้อย่างใดอย่างหนึ่ง?

โดยวิธีการในกรณีนี้แพรคซิที่เหมาะสมเป็นเพียงการเพิ่มน้ำหนักให้กับชนกลุ่มน้อยตามสัดส่วนการแสดง?


มีวิธีการอัพเดตใหม่โดยใช้ Keras หรือไม่? เหตุใดพจนานุกรมจึงประกอบด้วยสามคลาสและสำหรับคลาส: 0: 1.0 1: 50.0 2: 2.0 ???? ไม่ควร: 2: 1.0 เช่นกัน?
Chuck

คำตอบ:


112

หากคุณกำลังพูดถึงกรณีปกติที่เครือข่ายของคุณผลิตออกเดียวเท่านั้นการสันนิษฐานของคุณถูกต้อง ในการบังคับอัลกอริทึมของคุณให้ปฏิบัติต่อทุกอินสแตนซ์ของคลาส 1เป็น 50 อินสแตนซ์ของคลาส 0คุณต้อง:

  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เป็น 50 อินสแตนซ์ของคลาส 0 " หมายความว่าในฟังก์ชันการสูญเสียของคุณคุณกำหนดค่าที่สูงขึ้นให้กับอินสแตนซ์เหล่านี้ ดังนั้นการสูญเสียจะกลายเป็นค่าเฉลี่ยถ่วงน้ำหนักโดยน้ำหนักของตัวอย่างแต่ละตัวอย่างถูกระบุโดยclass_weightและคลาสที่เกี่ยวข้อง

จาก Keras docs: class_weight : ดัชนีคลาสการแม็พทางเลือก (จำนวนเต็ม) เป็นค่าน้ำหนัก (ลอย) ที่ใช้สำหรับถ่วงน้ำหนักฟังก์ชั่นการสูญเสีย (ระหว่างการฝึกอบรมเท่านั้น)


1
และดูgithub.com/fchollet/keras/issues/3653 ด้วยหากคุณทำงานกับข้อมูล 3D
Herve

สำหรับฉันมันให้ข้อผิดพลาด dic ไม่มีคุณลักษณะรูปร่าง
Flávio Filho

ฉันเชื่อว่า Keras อาจเปลี่ยนวิธีการทำงานนี้เป็นรุ่นสิงหาคม 2559 ฉันจะตรวจสอบให้คุณในสัปดาห์
layser

4
@layser ใช้งานได้กับการสูญเสีย 'category_crossentropy' เท่านั้นหรือไม่ คุณให้ class_weight แก่ keras อย่างไรสำหรับการสูญเสีย 'sigmoid' และ 'binary_crossentropy'
Naman

1
@ layser คุณสามารถอธิบาย `เพื่อปฏิบัติต่อทุกอินสแตนซ์ของคลาส 1 เป็น 50 อินสแตนซ์ของคลาส 0 'ได้หรือไม่ ในชุดการฝึกอบรมแถวที่สอดคล้องกับคลาส 1 จะทำซ้ำ 50 ครั้งเพื่อให้มีความสมดุลหรือกระบวนการอื่นตามมาหรือไม่?
Divyanshu Shekhar

122

คุณสามารถใช้class_weightจาก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_weightเป็นclass_weight sเพื่อไม่ให้เขียนทับโมดูลที่นำเข้า ปรับตามเมื่อคัดลอกรหัสจากความคิดเห็น


21
สำหรับฉันclass_weight.compute_class_weight สร้างอาร์เรย์ฉันต้องเปลี่ยนเป็น dict เพื่อทำงานกับ Keras โดยเฉพาะอย่างยิ่งหลังจากขั้นตอนที่ 2 ให้ใช้class_weight_dict = dict(enumerate(class_weight))
C.Lee

5
มันไม่ได้ผลสำหรับฉัน สำหรับปัญหาคลาสสามใน keras y_trainคือ(300096, 3)อาร์เรย์ numpy ดังนั้นclass_weight=บรรทัดให้ฉัน TypeError: unhashable ประเภท: 'numpy.ndarray'
Lembik

3
@ Lembik ฉันมีปัญหาที่คล้ายกันโดยที่แต่ละแถวของ y เป็นเวกเตอร์ที่เข้ารหัสร้อนแรงหนึ่งรายการของดัชนีชั้นเรียน ฉันคงได้โดยการแปลงเป็นตัวแทนหนึ่งร้อนเพื่อ int y_ints = [y.argmax() for y in y_train]เช่นนี้:
tkocmathla

3
จะเกิดอะไรขึ้นถ้าฉันกำลังทำป้ายกำกับมัลติคลาสเพื่อให้เวกเตอร์ y_true ของฉันมีหลาย 1s ในนั้น: [1 0 0 0 1 0 0] ตัวอย่างเช่นที่ x บางคนมีป้ายกำกับ 0 และ 4 แม้ตอนนี้จำนวน # ทั้งหมดของฉัน ฉลากไม่สมดุล ฉันจะใช้ตุ้มน้ำหนักสำหรับชั้นเรียนอย่างไร
Aalok

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
เหตุใดจึงต้องใช้บันทึกแทนที่จะแยกจำนวนตัวอย่างสำหรับคลาสด้วยจำนวนตัวอย่างทั้งหมด ฉันคิดว่ามีบางสิ่งที่ฉันไม่เข้าใจจะเข้าสู่ param class_weight ใน model.fit_generator (... )
startoftext

@startoftext นั่นเป็นวิธีที่ฉันทำ แต่ฉันคิดว่าคุณมี inverted ฉันใช้n_total_samples / n_class_samplesสำหรับแต่ละชั้น
colllin

2
ในตัวอย่างคลาส 0 ของคุณ (มีตัวอย่าง 2813 ตัวอย่าง) และคลาส 6 (มีตัวอย่าง 7914 ตัวอย่าง) มีน้ำหนัก 1.0 เท่า ทำไมถึงเป็นอย่างนั้น? ชั้น 6 ใหญ่ขึ้นสองสามเท่า! คุณต้องการให้มีการลดขนาดของคลาส 0 และลดระดับของคลาส 6 เพื่อให้อยู่ในระดับเดียวกัน
Vladislavs Dovgalecs

9

หมายเหตุ: ดูความคิดเห็นคำตอบนี้ล้าสมัย

หากต้องการให้น้ำหนักทุกคลาสเท่ากันคุณสามารถตั้ง class_weight เป็น "auto" ได้ดังนี้:

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

1
ฉันไม่พบการอ้างอิงclass_weight='auto'ในเอกสารประกอบของKeras หรือในซอร์สโค้ด คุณช่วยแสดงให้เราเห็นว่าคุณพบสิ่งนี้ได้จากที่ใด
Fábio Perez

2
คำตอบนี้อาจผิด ตรวจสอบปัญหานี้: github.com/fchollet/keras/issues/5116
Fábio Perez

แปลก ฉันใช้ class_ Balance = 'auto' ในขณะที่โพสต์ความคิดเห็น แต่ฉันไม่สามารถหาข้อมูลอ้างอิงได้ในขณะนี้ บางทีมันอาจจะเปลี่ยนไปเนื่องจาก Keras พัฒนาขึ้นอย่างรวดเร็ว
David Groppe

ตามที่ระบุในปัญหา Keras ที่ระบุไว้ข้างต้นคุณสามารถส่งสตริงแบบสุ่มใด ๆ ได้class_weightและจะไม่มีผลกระทบใด ๆ คำตอบนี้ไม่ถูกต้อง
ncasas

3

class_weight นั้นใช้ได้ แต่ @Aalok บอกว่ามันจะไม่ทำงานถ้าคุณเป็นคลาสที่มีการเข้ารหัสแบบหลายชั้น ในกรณีนี้ใช้sample_weight :

sample_weight: อาร์เรย์ที่เป็นตัวเลือกที่มีความยาวเท่ากับ x ซึ่งมีน้ำหนักที่จะนำไปใช้กับการสูญเสียของแบบจำลองสำหรับแต่ละตัวอย่าง ในกรณีของข้อมูลชั่วคราวคุณสามารถส่งอาร์เรย์ 2 มิติด้วยรูปร่าง (ตัวอย่าง, ลำดับความยาว) เพื่อนำน้ำหนักที่แตกต่างกันไปใช้กับการตั้งเวลาของทุกตัวอย่างทุกครั้ง ในกรณีนี้คุณควรตรวจสอบให้แน่ใจว่าได้ระบุ 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

ฉันพบตัวอย่างต่อไปนี้ของการเขียนน้ำหนักของคลาสในฟังก์ชันการสูญเสียโดยใช้ชุดข้อมูล minist ดูลิงก์ที่นี่: 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.