You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

训练时反归一化数据:TensorFlow/Keras CNN损失值还原求助

How to Get Original-Scale Loss When Training a CNN with Normalized Data in TensorFlow/Keras

Hey there! Let's work through this problem together. First off, I notice you're using the same MinMaxScaler for both your input X and output Y—that's a small but important mistake! Input and output data almost always have different value ranges and distributions, so you should use separate scalers for each. Let's fix that first, then dive into getting your original-scale loss.


Step 1: Fix Your Data Normalization

First, let's split the scalers for inputs and outputs to avoid issues later:

from sklearn.preprocessing import MinMaxScaler

# Create separate scalers for input and output
scaler_X = MinMaxScaler()
scaler_Y = MinMaxScaler()

# Normalize input and output data
X_scaled = scaler_X.fit_transform(X)
Y_scaled = scaler_Y.fit_transform(Y)

# Keep a copy of your original Y data (unscaled) for later loss calculations
Y_original = Y.copy()

Step 2: Three Ways to Get Original-Scale Loss

Now, let's cover three practical methods to compute loss on the original data scale, depending on your needs.

Method 1: Use a Callback to Calculate Original Loss After Each Epoch

This is great if you still want to train on normalized data (which helps model stability) but want to track the original-scale loss during training. We'll create a custom callback that runs after each epoch to predict, inverse-transform, and compute the original loss:

import tensorflow as tf
from tensorflow.keras.callbacks import Callback

class OriginalScaleLossCallback(Callback):
    def __init__(self, val_X_scaled, val_Y_original, scaler_Y):
        super().__init__()
        self.val_X = val_X_scaled  # Normalized validation input
        self.val_Y_true = val_Y_original  # Unscaled validation labels
        self.scaler = scaler_Y

    def on_epoch_end(self, epoch, logs=None):
        # Predict normalized outputs from the model
        val_Y_pred_scaled = self.model.predict(self.val_X, verbose=0)
        # Inverse-transform predictions back to original scale
        val_Y_pred_original = self.scaler.inverse_transform(val_Y_pred_scaled)
        # Calculate original-scale MSE (or your preferred loss)
        original_mse = tf.keras.losses.MeanSquaredError()(self.val_Y_true, val_Y_pred_original).numpy()
        # Add the original loss to logs so it shows up in training output
        logs['original_mse'] = original_mse
        print(f"\nEpoch {epoch+1} - Original-Scale MSE: {original_mse:.4f}")

# Initialize the callback (make sure you have validation data ready)
original_loss_callback = OriginalScaleLossCallback(
    val_X_scaled=X_val_scaled,  # Your normalized validation X
    val_Y_original=Y_val_original,  # Your unscaled validation Y
    scaler_Y=scaler_Y
)

# Train with the callback
model.fit(
    X_scaled, Y_scaled,
    epochs=50,
    validation_data=(X_val_scaled, Y_val_scaled),
    callbacks=[original_loss_callback]
)

Method 2: Custom Loss Function That Inverse-Transforms Internally

If you want the model to optimize directly for the original-scale loss, you can create a custom loss function that converts the normalized predictions and labels back to the original scale before calculating loss.

Note: To make this compatible with model saving/loading, we'll extract the scaler's parameters (instead of passing the scaler object directly, which can't be serialized):

# Extract scaler parameters for output data
y_min = tf.constant(scaler_Y.data_min_, dtype=tf.float32)
y_scale = tf.constant(scaler_Y.scale_, dtype=tf.float32)

def original_scale_mse(y_true_scaled, y_pred_scaled):
    # Manually inverse-transform: original_value = scaled_value * scale + min
    y_true_original = y_true_scaled * y_scale + y_min
    y_pred_original = y_pred_scaled * y_scale + y_min
    # Calculate MSE on original scale
    return tf.keras.losses.MSE(y_true_original, y_pred_original)

# Compile your model with this custom loss
model.compile(optimizer='adam', loss=original_scale_mse)

# Train as usual (still using normalized X and Y)
model.fit(X_scaled, Y_scaled, epochs=50, validation_data=(X_val_scaled, Y_val_scaled))

Method 3: Add an Inverse-Transform Layer to Your Model

If you want the model to directly output predictions in the original scale, you can add a Lambda layer to the end of your model that handles the inverse transformation. Then you can train using the original (unscaled) labels directly:

# Build your base CNN model (outputs normalized values)
base_cnn = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=X_scaled.shape[1:]),
    tf.keras.layers.MaxPooling2D((2,2)),
    # Add your other CNN/Dense layers here...
    tf.keras.layers.Dense(3)  # Outputs 3 normalized values
])

# Create the inverse-transform layer
def inverse_transform(y_scaled):
    return y_scaled * y_scale + y_min

# Attach the inverse-transform layer to the base model
original_output = tf.keras.layers.Lambda(inverse_transform)(base_cnn.output)
full_model = tf.keras.Model(inputs=base_cnn.input, outputs=original_output)

# Compile with standard loss (since we're using original labels now)
full_model.compile(optimizer='adam', loss='mse')

# Train using normalized X and original (unscaled) Y
full_model.fit(X_scaled, Y_original, epochs=50, validation_data=(X_val_scaled, Y_val_original))

Key Notes

  • Always use separate scalers for inputs and outputs—sharing them will lead to incorrect inverse transformations and messed-up loss calculations.
  • If you plan to save your model, avoid using loss functions that depend on external scaler objects (use Method 2's parameter-based approach or Method 3 instead).

内容的提问来源于stack exchange,提问作者Murilo Souza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:43:54