如何使用keras与tensorflow概率分布相加的_weight()变量?

如何使用keras与tensorflow概率分布相加的_weight()变量?,tensorflow,keras,tensorflow2.0,Tensorflow,Keras,Tensorflow2.0,我正在创建一个新的keras层,它接受一个输入数据向量,并由两个标量(均值和标准偏差)参数化。我将输入数据建模为正态分布,并通过梯度下降估计其均值和方差。但是,当我初始化tfp.Normal(mu,sigma)时,其中mu和sigma来自add_weights(),build(),梯度不会通过mu和sigma传播 tensorflow概率文档说明,您可以传入分布参数的训练变量,并对其进行反向支持。我如何让它在keras内部工作 下面是一个简单的工作示例 import tensorflow as

我正在创建一个新的keras层,它接受一个输入数据向量,并由两个标量(均值和标准偏差)参数化。我将输入数据建模为正态分布,并通过梯度下降估计其均值和方差。但是,当我初始化tfp.Normal(mu,sigma)时,其中mu和sigma来自add_weights(),build(),梯度不会通过mu和sigma传播

tensorflow概率文档说明,您可以传入分布参数的训练变量,并对其进行反向支持。我如何让它在keras内部工作

下面是一个简单的工作示例

import tensorflow as tf
import tensorflow_probability as tfp
import numpy as np
tfk = tf.keras
tfkl = tf.keras.layers
tfd = tfp.distributions
tfpl = tfp.layers
EPS = 1e-5

batch_size = 4
N = 100
x = np.random.randn(batch_size, N)

class NormalLikelihood(tf.keras.layers.Layer):
    def __init__(self):
        super(NormalLikelihood, self).__init__()

    def build(self, input_shape):
        self.mu = self.add_weight("mean", shape=[1], initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1), dtype=tf.float32)
        self.sigma = self.add_weight("std", shape=[1], initializer=tf.keras.initializers.RandomUniform(minval=EPS, maxval=5.0, seed=None), constraint=tf.keras.constraints.non_neg(), dtype=tf.float32)
        self.distribution = tfp.distributions.Normal(self.mu[0], self.sigma[0])

    def call(self, input):
        r = self.distribution.prob(input)
        r = tf.clip_by_value(r, 1e-3, 1-1e-3)
        return r

input_layer = tf.keras.layers.Input(shape=(100,))
r = NormalLikelihood()(input_layer)
r = -tf.reduce_sum(tf.math.log(r))
model = tf.keras.models.Model(input_layer, r)
model.add_loss(r)
model.compile(optimizer='rmsprop', loss=None)
model.fit(x, y=None)
此代码导致内置。ValueError:没有为任何变量提供梯度:[“正常可能性/平均值:0”,“正常可能性/标准值:0'],这是不期望的。期望的行为是['normal_likelization/mean:0'、'normal_likelization/std:0']为其提供梯度


请参阅google colab中的代码:

tfp.distributions.Normal(self.mu[0],self.sigma[0])
更改为
tfp.distributions.Normal(self.mu,self.sigma)

之所以这样做,是因为在
.fit()
keras方法的框架下,梯度计算正在寻找可训练的变量。当你索引到模型的权重中时,你正在对破坏链式规则连接性的常数取梯度

示例

将numpy导入为np
导入tensorflow作为tf
导入tensorflow_概率作为tfp
EPS=1e-5
类NormalLikelihoodYours(tf.keras.layers.Layer):
定义初始化(自):
超级(正常可能性你的,自我)。\uuuu init\uuuu()
def构建(自我,输入_形状):
self.mu=self.add\u重量(
“平均值”,形状=[1],
初始值设定项=tf.keras.initializers.normal(
平均值=0.0,标准差=1),数据类型=tf.32)
self.sigma=self.add\u权重(
“标准”,形状=[1],
初始值设定项=tf.keras.initializers.random(
最小值=每股收益,最大值=5.0,种子值=无),
constraint=tf.keras.constraints.non_neg(),
dtype=tf.float32)
self.distribution=tfp.distributions.Normal(self.mu[0],self.sigma[0])
def呼叫(自我,输入):
r=自分布概率(输入)
r=tf。按值(r,1e-3,1-1e-3)剪裁
返回r
类NormalLikelihoodMine(tf.keras.layers.Layer):
定义初始化(自):
super(NormalLikelihoodMine,self)。\uuu init
def构建(自我,输入_形状):
self.mu=self.add\u重量(
“平均值”,形状=[1],
初始值设定项=tf.keras.initializers.normal(
平均值=0.0,标准差=1),数据类型=tf.32)
self.sigma=self.add\u权重(
“标准”,形状=[1],
初始值设定项=tf.keras.initializers.random(
最小值=每股收益,最大值=5.0,种子值=无),
constraint=tf.keras.constraints.non_neg(),
dtype=tf.float32)
self.distribution=tfp.distributions.Normal(self.mu,self.sigma)
def呼叫(自我,输入):
r=自分布概率(输入)
r=tf。按值(r,1e-3,1-1e-3)剪裁
返回r
#损失函数
def计算损失(后勤):
return-tf.math.reduce_sum(tf.math.log(logits))
#模型输入
input_layer=tf.keras.layers.input(shape=(100,))
x_in=tf.random.normal([4100])
#你的模型
your\u输出=NormalLikelihoodYours()(输入层)
您的模型=tf.keras.models.model(输入层,您的输出层)\
#我的模型
my_输出=NormalLikelihoodMine()(输入层)
my_model=tf.keras.models.model(输入层,my_输出层)
#您的没有渐变,因为网络权重不是渐变的
#包括在损失计算的任何地方。当你索引它们的时候
#在“[0]”中,它们不再是网络中的可训练变量,
#只是常数。
使用tf.GradientTape()作为磁带:
y_hat=您的_模型(x_英寸)
损失=计算损失(y)
打印(磁带.梯度(损耗,你的模型.可训练的变量))
#[没有,没有]
#我的模型有梯度,因为“损失”和
#“可训练的_变量”是连接的
使用tf.GradientTape()作为磁带:
y_hat=我的模型(x_英寸)
损失=计算损失(y)
打印(磁带.梯度(损耗,我的模型.可训练的变量))
# [,
#  ]

请查看如何创建一个工作实例。@gobrewers14添加了一个最小工作示例。请向上投票,这样这个问题就可以得到牵引力。改变
tfp.分布。正态(self.mu[0],self.sigma[0])
tfp.分布。正态(self.mu,self.sigma)
@gobrewers14我看到了这个效果。但是,为什么我的代码不起作用?我看不出有什么区别。具体来说,我正在训练一个更复杂的混合高斯模型。如果我的混合物中有5个高斯分布,我想有5个正态分布,每个正态分布有一个不同的μ和σ,这是可以训练的。那么你是说索引不能传递梯度?那么索引切片呢?我发现很难相信在tensorflow中没有一种干净的方法来使用索引和保留梯度。比较
my_model.weights[0][0]
my_model.weights[0]
之间的差异。前者是一个含有常数的
tf.张量;后者是网络图中的一个
tf.Variable
,具有特定名称空间
'normal\u likelion\u mine\u 3/mean:0'
。我看到了您的代码,但这对我来说没有意义。我有其他代码,它使用索引切片,梯度传播良好。我不明白为什么单元素的情况是特殊的。语法self.distribution=tfp.distributions.Normal(self.mu[0:1],self.sigma[0:1])有效吗?我已经为简单的高斯混合更新了colab。什么是让它工作的好方法?我发布的代码给了我同样的错误,没有梯度。