如何在Keras中实现单样本加权的自定义二元交叉熵损失函数
I get it, you want to apply different weights to each sample (or each class within a sample) when calculating binary crossentropy loss. Let's break down how to implement this properly, depending on your specific needs.
Case 1: Per-Class Weights (Static)
If you have fixed weights for each class (like your example: <0.81,0.9,1.0> for three classes), you can hardcode these weights into your custom loss function using Keras backend operations:
import numpy as np from tensorflow.keras import backend as K def weighted_binary_crossentropy(y_true, y_pred): # Define your per-class weights here class_weights = K.constant([0.81, 0.9, 1.0], dtype=K.floatx()) # Calculate standard binary crossentropy per element bce = K.binary_crossentropy(y_true, y_pred) # Multiply each element's loss by its corresponding class weight weighted_bce = bce * class_weights # Return the mean of the weighted loss (use K.sum if you want total loss instead) return K.mean(weighted_bce)
Case 2: Per-Sample Weights (Dynamic)
If you want each sample in the batch to have its own weight (and want to compute these weights dynamically within the loss function, like using your base_factor), here's how to complete your partial code:
def my_binary_crossentropy(y_true, y_pred): base_factor = 0.9 # Get the batch size at runtime (handles dynamic batch sizes, which K.int_shape can't) batch_size = K.shape(y_true)[0] # Generate weights for each sample (example: base_factor raised to the sample index) sample_indices = K.arange(0, batch_size, dtype=K.floatx()) sample_weights = K.pow(base_factor, sample_indices) # Reshape weights to (batch_size, 1) so they broadcast across all classes in the sample sample_weights = K.reshape(sample_weights, (batch_size, 1)) # Calculate binary crossentropy per element bce = K.binary_crossentropy(y_true, y_pred) # Multiply each element's loss by its sample's weight weighted_bce = bce * sample_weights # Return the mean of the weighted loss return K.mean(weighted_bce)
Case 3: Passing External Weights (Flexible)
If your weights are dynamic and depend on external data (not computed inside the loss function), use a closure to create a reusable loss function that accepts weights as a parameter:
def create_weighted_bce(weights): # Convert weights to a Keras constant to integrate with the graph weights_tensor = K.constant(weights, dtype=K.floatx()) def weighted_bce(y_true, y_pred): bce = K.binary_crossentropy(y_true, y_pred) weighted_bce = bce * weights_tensor return K.mean(weighted_bce) return weighted_bce # Usage example: my_weights = [0.81, 0.9, 1.0] model.compile(loss=create_weighted_bce(my_weights), optimizer='adam')
Key Tips
- Always use Keras backend functions (
K.pow,K.arange, etc.) instead of NumPy operations—this ensures the computation is part of the TensorFlow graph and can be differentiated during training. - Match the shape of your weights to the binary crossentropy output: for per-sample weights, reshape to
(batch_size, 1)to broadcast across all classes in each sample. - If you just need simple per-sample weights during training, Keras has a built-in
sample_weightparameter inmodel.fit()that you can use without writing a custom loss. But for dynamic weight calculation or per-class weights, the custom loss approach is better.
内容的提问来源于stack exchange,提问作者KM.




