归一化目标值后神经网络始终输出相同预测值的问题排查
问题分析与解决方案
你的核心问题是目标变量归一化后模型输出固定值,主要由两个关键原因导致:数据泄露和目标分布极端不平衡,以下是具体分析和修复步骤:
1. 致命错误:归一化过程存在数据泄露
你当前的代码先对整个数据集的目标值做归一化,再拆分训练/测试集,这会导致测试集的归一化用到了训练集以外的信息(包括极端值),属于严重的数据泄露,直接破坏模型泛化能力。
修复代码:
把归一化步骤放到训练测试拆分之后,仅用训练集拟合scaler:
def create_and_train_model_opti2(dff, target_column): df = dff.copy() y = df[target_column].values.reshape(-1, 1) X = df.drop([target_column], axis=1) # 归一化特征 scaler_X = MinMaxScaler(feature_range=(0, 1)) X_scaled = scaler_X.fit_transform(X) # 先拆分训练测试集 X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42) # 仅用训练集的目标值拟合scaler_y scaler_y = MinMaxScaler(feature_range=(0, 1)) y_train_scaled = scaler_y.fit_transform(y_train) y_test_scaled = scaler_y.transform(y_test) # 模型构建不变 model = Sequential([ Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(0.001)), Dropout(0.2), Dense(128, activation='relu', kernel_regularizer=l2(0.001)), Dropout(0.2), Dense(1) ]) model.compile(optimizer=Adam(learning_rate=0.001), loss='mean_squared_error') # 用归一化后的训练目标训练 history = model.fit(X_train, y_train_scaled, epochs=60, batch_size=64, validation_split=0.2) return model, history, y_train_scaled, y_test_scaled, X_train, X_test, scaler_y
2. 目标分布极端不平衡
从你的预测输出看,目标值存在极大离群点(比如2579.44),同时大量样本是0,MinMaxScaler会把这个极端值映射到1,其余绝大多数样本映射到接近0的区间。模型为了最小化MSE,会倾向于输出所有样本的平均归一化值,反归一化后就变成固定的353.5。
解决方法:
- 改用鲁棒归一化器:用
RobustScaler替代MinMaxScaler,它基于中位数和四分位数,对离群点的鲁棒性更强:from sklearn.preprocessing import RobustScaler scaler_y = RobustScaler() - 处理极端值:统计目标列的分位数,截断或删除极端值(比如保留99分位数以内的数据):
q99 = df[target_column].quantile(0.99) df = df[df[target_column] <= q99] - 对数变换:对目标值做对数变换缩小极端值影响,再归一化:
y = np.log1p(df[target_column].values.reshape(-1, 1)) # log1p避免0值问题 - 加权损失函数:给少数大目标样本赋予更高权重,强迫模型关注这些样本:
# 计算权重:用目标值的相对大小作为权重 weights = y_train.flatten() / y_train.flatten().max() history = model.fit(X_train, y_train_scaled, sample_weight=weights, epochs=60, batch_size=64, validation_split=0.2)
3. 调整训练策略
- 增加训练轮数+早停:当前60轮可能不足以让模型学到有效特征,增加到100-200轮,同时加入早停防止过拟合:
from tensorflow.keras.callbacks import EarlyStopping early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) history = model.fit(X_train, y_train_scaled, epochs=200, batch_size=64, validation_split=0.2, callbacks=[early_stop]) - 调整学习率:归一化后目标值范围缩小,可尝试降低学习率到
0.0001,或用学习率调度器动态调整:from tensorflow.keras.callbacks import ReduceLROnPlateau lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5) history = model.fit(X_train, y_train_scaled, epochs=200, batch_size=64, validation_split=0.2, callbacks=[early_stop, lr_scheduler])
内容的提问来源于stack exchange,提问作者William Dekeyser
相关产品推荐
相关产品推荐

