Tensorflow概率-Bijector训练

时间:2020-08-06 15:12:12

标签: python tensorflow tensorflow-probability

我一直在尝试遵循tutorial中的示例,但是在训练任何变量时遇到了麻烦。

我写了一个小例子,但我也无法使它工作:

# Train a shift bijector
shift = tf.Variable(initial_value=tf.convert_to_tensor([1.0], dtype=tf.float32), trainable=True, name='shift_var')
bijector = tfp.bijectors.Shift(shift=shift)

# Input
x = tf.convert_to_tensor(np.array([0]), dtype=tf.float32)
target = tf.convert_to_tensor(np.array([2]), dtype=tf.float32)

optimizer = tf.optimizers.Adam(learning_rate=0.5)
nsteps = 1

print(bijector(x).numpy(), bijector.shift)
for _ in range(nsteps):

    with tf.GradientTape() as tape:
        out = bijector(x)
        loss = tf.math.square(tf.math.abs(out - target))
        #print(out, loss)
    
        gradients = tape.gradient(loss, bijector.trainable_variables)
    
    optimizer.apply_gradients(zip(gradients, bijector.trainable_variables))
    
print(bijector(x).numpy(), bijector.shift)

对于nsteps = 1,两个打印语句将产生以下输出:

[1.] <tf.Variable 'shift_var:0' shape=(1,) dtype=float32, numpy=array([1.], dtype=float32)>
[1.] <tf.Variable 'shift_var:0' shape=(1,) dtype=float32, numpy=array([1.4999993], dtype=float32)>

即使bijector的打印值已更新,看来shift仍在使用原始的bijector.shift

在第一次迭代后,由于梯度为nsteps,我无法增加None,但出现了这个错误:

ValueError: No gradients provided for any variable: ['shift_var:0'].

我正在使用

tensorflow version 2.3.0
tensorflow-probability version 0.11.0

我也在colab笔记本上尝试过,所以我怀疑这是版本问题。

2 个答案:

答案 0 :(得分:1)

您发现了一个错误。 Bijector正向函数弱地缓存result-> input映射,以使下游逆和对数行列式快速。但是,这也以某种方式干扰了梯度。一种解决方法是添加一个del out,如https://colab.research.google.com/gist/brianwa84/04249c2e9eb089c2d748d05ee2c32762/bijector-cache-bug.ipynb

答案 1 :(得分:0)

仍然不确定我是否完全了解这里发生的事情,但至少我现在可以使我的示例开始工作。

由于某种原因,如果我将其包装在从tf.keras.Model继承的类中,则行为会有所不同:

public partial class LOAListBox : UserControl
{

    public static readonly DependencyProperty DataSource = DependencyProperty.Register(nameof(LOA_List), typeof(List<LOA>), typeof(LOAListBox), new PropertyMetadata());
    public List<LOA> LOA_List
    {
        get => (List<LOA>)GetValue(DataSource);
        set => SetValue(DataSource, value);

    }

}

我为训练迭代制作了一个函数,尽管这似乎不是必需的:

class BijectorModel(tf.keras.Model):

    def __init__(self):
        super().__init__()

        self.shift = tf.Variable(initial_value=tf.convert_to_tensor([1.5], dtype=tf.float32), trainable=True, name='shift_var')
        self.bijector = tfp.bijectors.Shift(shift=self.shift)

    def call(self, input):
        return self.bijector(input)

像这样执行

def training_iteration(model, input, target):

    optimizer = tf.optimizers.SGD(learning_rate=0.1)

    with tf.GradientTape() as tape:

        loss = tf.math.square(tf.math.abs(model(input) - target))

        gradients = tape.gradient(loss, model.trainable_variables)

    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

产生期望/期望的输出:

x = tf.convert_to_tensor(np.array([0]), dtype=tf.float32)
target = tf.convert_to_tensor(np.array([2]), dtype=tf.float32)
model = BijectorModel()

nsteps = 10
for _ in range(nsteps):
    training_iteration(model, x, target)
    print('Iteration {}: Output {}'.format(_, model(x)))

我的结论是,与通过bijector对象进行访问相比,可训练变量在模型的一部分时的处理方式有所不同。