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

缩减CSV样本量后Val_loss出现NaN,求适配原示例的调整方案

解决Keras时间序列天气预报示例val_loss为NaN的问题

针对你把10分钟间隔数据改成小时级后出现的val_loss=NaN、提示“val_loss did not improve from inf”的问题,核心问题大概率出在归一化逻辑不匹配、样本生成错误或数据异常上,以下是具体的修复方案:

1. 重新适配归一化流程,避免数据泄露

原代码的归一化统计量(均值、标准差)是基于10分钟间隔的全量数据计算的,改成小时级后必须重新计算,还要排除未使用的3列:

# 假设你的数据框是df,新增的3列是col_a、col_b、col_c
# 只保留原示例用到的特征列,排除新增列
used_features = [col for col in df.columns if col not in ['col_a', 'col_b', 'col_c']]
df_used = df[used_features]

# 按8:2分割训练/验证集,用训练集单独计算归一化统计量(关键!防止数据泄露)
train_split = int(0.8 * len(df_used))
train_data = df_used.iloc[:train_split]
mean = train_data.mean(axis=0)
std = train_data.std(axis=0)

# 对全量数据做归一化
df_normalized = (df_used - mean) / std

重点:绝对不能用全量数据计算均值/std,否则验证集的信息会提前泄露给模型,同时新增列哪怕不用也要排除,避免它们的异常值干扰归一化结果。

2. 修复时间序列样本生成逻辑

原step=6对应10分钟间隔下的1小时步长,改成小时级后step=1是对的,但要调整窗口长度(look_back):
原示例的look_back可能是24*6=144(对应24小时的10分钟数据),现在要改成24(对应24小时的小时级数据),否则窗口过长会导致样本无效:

import numpy as np

def create_dataset(X, y, look_back=24, step=1):
    Xs, ys = [], []
    # 从look_back索引开始遍历,避免越界
    for i in range(look_back, len(X), step):
        Xs.append(X.iloc[i-look_back:i].values)
        ys.append(y.iloc[i].values)
    return np.array(Xs), np.array(ys)

# 假设目标变量是'temp'(原示例的预报目标)
y = df_normalized['temp']
X = df_normalized.drop('temp', axis=1)

# 生成训练/验证样本
X_train, y_train = create_dataset(X.iloc[:train_split], y.iloc[:train_split], look_back=24, step=1)
X_val, y_val = create_dataset(X.iloc[train_split:], y.iloc[train_split:], look_back=24, step=1)

生成后先验证样本数:print(len(X_train), len(X_val)),确保验证集样本数不为0,且没有空样本。

3. 清理数据中的异常值和缺失值

筛选整点数据后可能存在缺失段,或者新增列带进来的异常值影响了主特征:

# 检查主特征列的缺失值
print(df_used.isna().sum())
# 优先用前后值填充缺失值,避免删除过多样本
df_used = df_used.fillna(method='ffill').fillna(method='bfill')

# 过滤极端值(Z-score超过3的视为异常)
from scipy import stats
z_scores = stats.zscore(df_used)
abs_z_scores = np.abs(z_scores)
filtered_entries = (abs_z_scores < 3).all(axis=1)
df_used = df_used[filtered_entries]

如果某小时数据完全缺失,填充后再生成样本,避免模型读取空数据导致损失计算出现NaN。

4. 调整模型训练参数,避免梯度爆炸

如果前面的步骤都做完还是有问题,可能是原学习率太高导致梯度爆炸,尝试以下调整:

  • 降低学习率,改用更稳定的损失函数:
from tensorflow.keras.optimizers import Adam

model.compile(optimizer=Adam(learning_rate=0.0001), loss='mae')

MAE损失比MSE对极端值更宽容,先用来验证模型是否能正常收敛,收敛后再换回MSE。

  • 添加梯度裁剪回调,防止梯度溢出:
from tensorflow.keras.callbacks import Callback

class GradientClipping(Callback):
    def on_train_batch_end(self, batch, logs=None):
        for layer in self.model.layers:
            weights = layer.get_weights()
            # 把权重裁剪到[-1,1]区间,避免梯度爆炸
            weights = [np.clip(w, -1.0, 1.0) for w in weights]
            layer.set_weights(weights)

# 训练时加入回调
model.fit(X_train, y_train, validation_data=(X_val, y_val), 
          epochs=50, batch_size=16, callbacks=[GradientClipping()])

验证步骤

  1. 检查归一化后的数据:print(df_normalized.describe()),确保均值接近0,标准差接近1,没有inf或NaN。
  2. 查看生成的样本:print(X_train[0], y_train[0]),确认是有效的时间序列片段,没有空值。
  3. 先跑1个epoch的小批量训练(batch_size=8),观察训练loss是否正常,再逐步增大epochs。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:42:23