You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Tensorflow Probability模型预测不稳定问题及优化咨询

控制TensorFlow Probability概率模型的预测稳定性与损失优化问题

问题背景

我正在用TensorFlow Probability构建概率模型,模型输出是概率结果(因此误差导数不能趋近于零,否则会退化为确定性模型)。目前遇到两个核心困扰:

  1. 预测不稳定:损失函数的导数存在明显波动区间(类似凸优化场景中导数在1.2到0.2之间波动),导致相同超参数下每次训练的预测结果差异极大——有时拟合效果极佳(真实值为红色,蓝色线为预测值±2标准差),有时拟合效果很差,甚至出现镜像结果,这严重不符合业务对稳定预测输出的需求。
  2. 损失无法进一步降低:即便尝试降低学习率、增加训练轮数,损失仍停留在1.42左右,无法继续优化。

初始模型代码

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import tensorflow_probability as tfp
np.random.seed(42)
dataframe = pd.read_csv('Apple_Data_300.csv').ix[0:800,:]
dataframe.head()
plt.plot(range(0,dataframe.shape[0]),dataframe.iloc[:,1])
x1=np.array(dataframe.iloc[:,1]+np.random.randn(dataframe.shape[0])).astype(np.float32).reshape(-1,1)
y=np.array(dataframe.iloc[:,1]).T.astype(np.float32).reshape(-1,1)
tfd = tfp.distributions
model = tf.keras.Sequential([
 tf.keras.layers.Dense(1,kernel_initializer='glorot_uniform'),
 tfp.layers.DistributionLambda(lambda t: tfd.Normal(loc=t, scale=1)),
 tfp.layers.DistributionLambda(lambda t: tfd.Normal(loc=t, scale=1)),
 tfp.layers.DistributionLambda(lambda t: tfd.Normal(loc=t, scale=1))
])
negloglik = lambda x, rv_x: -rv_x.log_prob(x)
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.0001), loss=negloglik)
model.fit(x1,y, epochs=500, verbose=True)
yhat = model(x1)
mean = yhat.mean()
init = tf.global_variables_initializer()
with tf.Session() as sess:
 sess.run(init)
 mm = sess.run(mean)
 mean = yhat.mean()
 stddev = yhat.stddev()
 mean_plus_2_std = sess.run(mean - 2. * stddev)
 mean_minus_2_std = sess.run(mean + 2. * stddev)
plt.figure(figsize=(8,6))
plt.plot(y,color='red',linewidth=1)
#plt.plot(mm)
plt.plot(mean_minus_2_std,color='blue',linewidth=1)
plt.plot(mean_plus_2_std,color='blue',linewidth=1)

训练损失(最后三轮)

Epoch 498/500 801/801 [==============================] - 0s 32us/sample - loss: 2.4169
Epoch 499/500 801/801 [==============================] - 0s 30us/sample - loss: 2.4078
Epoch 500/500 801/801 [==============================] - 0s 31us/sample - loss: 2.3944

修改后的可用代码

init = tf.global_variables_initializer()
with tf.Session() as sess:
 model = tf.keras.Sequential([
 tf.keras.layers.Dense(1,kernel_initializer='glorot_uniform'),
 tfp.layers.DistributionLambda(lambda t: tfd.Normal(loc=t, scale=1))
 ])
 negloglik = lambda x, rv_x: -rv_x.log_prob(x)
 model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.0001), loss=negloglik)
 model.fit(x1,y, epochs=500, verbose=True, batch_size=16)
 yhat = model(x1)
 mean = yhat.mean()
 sess.run(init)
 mm = sess.run(mean)
 mean = yhat.mean()
 stddev = yhat.stddev()
 mean_plus_2_std = sess.run(mean - 3. * stddev)
 mean_minus_2_std = sess.run(mean + 3. * stddev)

核心问题分析与解决方案

一、预测不稳定的根源与解决方法

预测不稳定主要来自冗余模型结构训练随机性失控两个方面:

  1. 移除冗余的DistributionLambda层
    你初始模型连续堆叠3个固定scale=1的Normal分布层,这完全是多余的——多次分布变换只会引入计算噪声,放大初始权重的随机性,让梯度传播变得混乱。修改后保留1层是正确方向,但还可以优化:不要固定scale为1,让模型自适应学习不确定性,比如:

    tfp.layers.DistributionLambda(
        lambda t: tfd.Normal(
            loc=t, 
            scale=tf.math.softplus(tfp.layers.VariableLayer(initial_value=1.0)) + 1e-6
        )
    )
    

    用softplus保证scale为正,加1e-6避免数值下溢。

  2. 彻底控制训练随机性

    • 你只设置了np.random.seed(42),但TensorFlow和TFP也需要单独固定种子:
      tf.random.set_seed(42)
      tfp.random.set_seed(42)
      
    • 时序数据不要打乱batch:设置fit(shuffle=False),避免每次训练的batch输入顺序不同;如果用batch训练,固定采样索引进一步消除随机性。
    • 不要在Session内重复定义模型:修改后的代码在Session里定义模型,每次运行都会重新初始化权重,应该把模型定义放在Session外,确保每次训练用相同的初始权重。

二、损失无法降低的关键要点

损失卡在1.42左右,说明模型收敛到了局部最优,大概率是模型容量不足训练策略不合理导致的:

  1. 提升模型容量
    仅用1个Dense(1)线性层,完全无法捕捉股价这类时序数据的非线性趋势。建议增加隐藏层,同时让模型学习均值和不确定性:

    model = tf.keras.Sequential([
        tf.keras.layers.Dense(32, activation='relu', kernel_initializer='glorot_uniform'),
        tf.keras.layers.Dense(16, activation='relu'),
        # 输出2个值:一个对应均值,一个对应对数方差(转成scale)
        tf.keras.layers.Dense(2),
        tfp.layers.DistributionLambda(
            lambda t: tfd.Normal(
                loc=t[..., :1],
                scale=tf.math.softplus(t[..., 1:]) + 1e-6
            )
        )
    ])
    
  2. 优化训练策略

    • 调整学习率:lr=0.0001太小,导致模型收敛慢、容易卡在局部最优。可以用学习率调度器,比如初始0.001,随训练轮数指数衰减:
      lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
          initial_learning_rate=0.001,
          decay_steps=100,
          decay_rate=0.9
      )
      model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=lr_schedule), loss=negloglik)
      
    • 理解损失函数:固定scale=1时,负对数似然近似于MSE加常数;让模型学习scale后,损失会更合理地权衡拟合误差和不确定性,避免陷入无效的局部最优。
  3. 修正数据预处理逻辑
    你用当前步的带噪声数据预测当前步真实值,这种输入设计让模型难以学习有效模式。对于时序数据,应该用历史窗口输入(比如前5个时间步的数据预测下一个时间步),同时对数据做标准化(比如StandardScaler),让输入均值为0、方差为1,帮助优化器更快收敛。

三、额外的稳定性提升技巧

  • 用更稳定的初始化器:比如tf.keras.initializers.HeNormal(),适配ReLU激活函数的特性,减少初始权重的随机性影响。
  • 加入正则化:添加Dropout层或L2正则化(kernel_regularizer=tf.keras.regularizers.L2(0.01)),防止过拟合同时提升训练稳定性。
  • 多轮训练取平均:如果业务允许,进行5-10次训练,对每次的预测均值和标准差取平均,抵消单次训练的随机性波动。

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

火山引擎 最新活动