如何将 Keras 模型拟合到 Gamma 分布?

How to fit a Keras model to a Gamma Distribution?

我正在尝试拟合输出变量始终为正的 keras 模型。我想使用伽玛分布来模拟这个问题。问题是loss总是输出NAN。

我构建了以下keras模型:

model_max = tf.keras.Sequential([
            tf.keras.layers.Dense(20,input_dim=10, activation="relu"),    
            tf.keras.layers.Dense(15,activation="relu"),
            tf.keras.layers.Dense(10,activation="relu"),
            tf.keras.layers.Dense(5,activation="relu"),
            tf.keras.layers.Dense(2),
            tfp.layers.DistributionLambda(lambda t:
            tfd.Gamma(concentration = tf.math.softplus(0.005*t[...,:1])+0.001,
             rate = tf.math.softplus(0.005*t[...,1:])+0.001)
            ),
])            

请注意,我使用了 softplus,因为分布的两个参数都必须为正。我还添加了 0.001 以确保参数始终大于零。

我的损失函数如下:

def gamma_loss(y_true, my_dist):

    dist_mean = my_dist.mean()
    dist_stddev = my_dist.stddev()
    alpha = (dist_mean / dist_stddev)**2
    beta = dist_mean / dist_stddev**2
    gamma_distr = tfd.Gamma(concentration=alpha, rate=beta)
    return -tf.reduce_mean(gamma_distr.log_prob(y_true))

这个功能似乎工作正常。例如,如果我 运行 下面的代码 运行 没问题:

import tensorflow as tf
import tensorflow_probability as tfp
tfd = tfp.distributions

def gamma_loss(y_true, my_dist):

    dist_mean = my_dist.mean()
    dist_stddev = my_dist.stddev()
    alpha = (dist_mean / dist_stddev)**2
    beta = dist_mean / dist_stddev**2
    #print(alpha)
    gamma_distr = tfd.Gamma(concentration=alpha, rate=beta)
    return -tf.reduce_mean(gamma_distr.log_prob(y_true)).numpy()

dist = tfd.Gamma(1,1)

gamma_loss(100, dist)

但是,如果我用下面的行编译它:

model_max.compile(optimizer=tf.optimizers.Adam(learning_rate = 0.001),loss=gamma_loss)

loss总是输出nan

我做错了什么?我尝试了不同的损失函数,但似乎没有任何效果。我认为它与 浓度 参数相关,因为我已经有了一个与正态分布类似的模型。在该模型中,我没有对均值 (loc) 使用 softplus,因为该分布接受任何正值或负值。我使用标准偏差的确切结构,因为它在正态分布中也必须是正数。它工作得很好。为什么它不适用于 Gamma 分布?

感谢您向任何可以帮助我理解我做错了什么的人提出建议。

绝对删除 gamma_loss 末尾的 .numpy(),因为这会破坏梯度反向传播。

您可能希望 gamma 参数的最小值稍微大一些,因为它们可以使分布非常尖锐。特别是低至 0.5 的浓度参数使分布极度集中在 0。(这就是维基百科 https://en.wikipedia.org/wiki/Gamma_distribution 上称为 'shape/alpha/k' 的那个)。

这很容易在某处导致 +/-inf,然后在其他地方产生 nan。

我想与大家分享我为使我的代码正常工作所做的一切:

1) 我确保每一层都有一个 kernel_initializer='random_uniform' 语句,并且, 2) 我把整个 gamma_loss 函数变成了:lambda y, p_y: -p_y.log_prob(y)v

我不确定 gamma_loss 是否是问题所在,但我发现有人在做我正在做的同样事情的例子,而且更简单的 lambda y, p_y: -p_y.log_prob(y) 函数工作正常,所以我同意了。我认为我的主要问题是权重没有被随机初始化。

此外,我想重复一下我在搜索答案时在网上找到的一些建议:尝试拟合一个示例并确保在使用真实训练数据之前效果很好。在我的例子中,我通过采用一个训练示例并将该行复制数千次(创建一个所有行都相等的数据集)然后仅使用它来训练我的模型来实现这一点。当我的模型无法适应时,更容易逐层分析每一层的结果。

Brian Patton 给出的答案非常有帮助,因为它确实为我指明了正确的方向,即尝试理解每一层输出的内容并用一个简单的例子测试你的假设。

为了将来参考,这是我的代码现在的样子:

model_max = tf.keras.Sequential([
            tf.keras.layers.Dense(20,input_dim=10, activation="relu", kernel_initializer='random_uniform' ),   
            tf.keras.layers.Dense(15,activation="relu",kernel_initializer='random_uniform' ),
            tf.keras.layers.Dense(10,activation="relu",kernel_initializer='random_uniform' ),
            tf.keras.layers.Dense(5,activation="relu",kernel_initializer='random_uniform' ),
            tf.keras.layers.Dense(2, kernel_initializer='random_uniform' ),
            tfp.layers.DistributionLambda(lambda t:
            tfd.Gamma(concentration = tf.math.softplus(t[:,0])+0.000000001,
            rate = tf.math.softplus(t[:,1])+0.000000001),
            ),
])     


negloglik = lambda y, p_y: -p_y.log_prob(y)

model_max.compile(optimizer=tf.optimizers.Adamax(learning_rate = 0.0001),loss=negloglik)