如何为keras写一个自定义的加权平均f1损失函数?

t3psigkw  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(98)

我正在尝试在keras中进行多类分类。到目前为止,我使用categorical_crossentropy作为损失函数。但是由于所需的度量是weighted-f1,我不确定categorical_crossentropy是否是最佳的损失选择。我试图使用sklearn.metrics.f1_score在keras中实现加权f1分数,但由于Tensor和标量之间的转换问题,我遇到了错误。
大概是这样的:

def f1_loss(y_true, y_pred):
   return 1 - f1_score(np.argmax(y_true, axis=1), np.argmax(y_pred, axis=1), average='weighted')

字符串
其次是

model.compile(loss=f1_loss, optimizer=opt)


如何用keras写这个损失函数?

编辑:
y_true和y_pred的形状是**(n_samples,n_classes)在我的例子中是(n_samples,4)**
y_truey_pred都是Tensor,所以sklearn的f1_score不能直接作用于它们。我需要一个函数来计算Tensor上的加权f1。

xesrikrc

xesrikrc1#

变量是自我解释的:

def f1_weighted(true, pred): #shapes (batch, 4)

    #for metrics include these two lines, for loss, don't include them
    #these are meant to round 'pred' to exactly zeros and ones
    #predLabels = K.argmax(pred, axis=-1)
    #pred = K.one_hot(predLabels, 4) 

    ground_positives = K.sum(true, axis=0) + K.epsilon()       # = TP + FN
    pred_positives = K.sum(pred, axis=0) + K.epsilon()         # = TP + FP
    true_positives = K.sum(true * pred, axis=0) + K.epsilon()  # = TP
        #all with shape (4,)
    
    precision = true_positives / pred_positives 
    recall = true_positives / ground_positives
        #both = 1 if ground_positives == 0 or pred_positives == 0
        #shape (4,)

    f1 = 2 * (precision * recall) / (precision + recall + K.epsilon())
        #still with shape (4,)

    weighted_f1 = f1 * ground_positives / K.sum(ground_positives) 
    weighted_f1 = K.sum(weighted_f1)

    
    return 1 - weighted_f1 #for metrics, return only 'weighted_f1'

字符串

重要提示:

这种损失将分批工作(如任何Keras损失)。
因此,如果您使用小批量,则每个批次之间的结果将不稳定,并且您可能会得到一个坏结果。使用大批量,足以包括所有类别的大量样本。
由于这种损失会导致批量大小崩溃,因此您将无法使用某些依赖于批量大小的Keras功能,例如样本权重。

2guxujil

2guxujil2#

我创建了自定义weigted_f1函数,并在model.compile()过程中将其传递到“metrics =”中。
请参见函数和模型.compile()

def f1_weighted(true, pred): #shapes (batch, 4)

    #for metrics include these two lines, for loss, don't include them
    #these are meant to round 'pred' to exactly zeros and ones
    #predLabels = K.argmax(pred, axis=-1)
    #pred = K.one_hot(predLabels, 4) 

    ground_positives = K.sum(true, axis=0) + K.epsilon()       # = TP + FN
    pred_positives = K.sum(pred, axis=0) + K.epsilon()         # = TP + FP
    true_positives = K.sum(true * pred, axis=0) + K.epsilon()  # = TP
        #all with shape (4,)
    
    precision = true_positives / pred_positives 
    recall = true_positives / ground_positives
        #both = 1 if ground_positives == 0 or pred_positives == 0
        #shape (4,)

    f1 = 2 * (precision * recall) / (precision + recall + K.epsilon())
        #still with shape (4,)

    weighted_f1 = f1 * ground_positives / (K.sum(ground_positives) + K.epsilon())
    weighted_f1 = K.sum(weighted_f1)

    return weighted_f1 #for metrics, return only 'weighted_f1'

字符串
model.compile如下:

vocab_size = len(tokenizer.word_index) + 1

# build model
model = Sequential()
model.add(Embedding(input_dim=vocab_size, # Size of the vocabulary
                    output_dim=50, # Length of the vector for each word
                    input_length = MAX_SEQUENCE_LENGTH)) # Maximum length of a sequence
model.add(SpatialDropout1D(0.5))
model.add(LSTM(50, dropout=0.5, recurrent_dropout=0.5))
model.add(Dense(25,input_shape=y_train.shape, activation='softmax'))
model.add(Dropout(0.2))
model.add(Dense(20, activation='softmax'))
model.add(Dropout(0.2))
model.add(Dense(num_top_labels,activation='softmax'))
#model.add(Dense(1))
model.compile(loss = 'categorical_crossentropy', 
              optimizer=Adam(learning_rate=0.001), 
              metrics=['f1_weighted'])

print(model.summary())


在此之后,当我运行model.fit时,我得到ValueError:未知度量函数:'f1_weighted'

history = model.fit(X_train, y_train, 
                    class_weight=class_weights, # fix class imbalance
                    epochs = 5, 
                    batch_size=batch_size, 
                    #validation_split=0.2,
                    validation_data=(X_test, y_test), 
                    callbacks=[
                      EarlyStopping(monitor = "val_loss", patience = 10, restore_best_weights = True),
                       ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=2, mode='min') 
                    ]
                   )


请帮助我调试此错误。基本上,我想在每个时期之后绘制/跟踪训练和验证f1分数

相关问题