Tensorflow聚集和无信息权重更新(Tensorflow Python)



我正在TensorFlow(2.1版(中构建一个简单的模型,并且在tf.gather中遇到了一些奇怪的行为——可能我不明白它的作用。

我正在考虑一个可能有多个拦截的模型(即y = a[i] + X@b(。我定义了一个新的层,如下所示,

class GroupedInterceptLinearCoeffs_gather(tf.keras.layers.Layer):
"""
"""
def __init__(self, ngroup=1, **kwargs):
super(GroupedInterceptLinearCoeffs_gather, self).__init__()
self.ngroup = ngroup
def build(self, input_shape):
self.a = self.add_weight(
shape=(self.ngroup,), dtype="float32",
initializer="random_normal", trainable=True
)
self.b = self.add_weight(
shape=(input_shape[1][-1],), dtype="float32",
initializer="random_normal", trainable=True
)
@tf.function()
def call(self, inputs):
out = tf.gather(self.a, inputs[0], axis=0, batch_dims=0) + tf.linalg.matvec(inputs[1], self.b)
return out

然后检查它是否符合我对的期望

import numpy as np
import tensorflow as tf
nobs = 100
alpha = 1.0  # To keep things simple, we'll only have one intercept here
beta = np.array([0.0, 0.5, 0.25])
L = np.array([[1.0, 0.0, 0.0], [0.25, 1.1, 0.0], [0.2, 0.2, 1.25]])
X = np.random.randn(nobs, 3) @ L
y = alpha + X@beta

检查模型是否可以重现我的数据表明(有效(为0错误

gilc_g = GroupedInterceptLinearCoeffs_gather(ngroup=1)
gilc_g([np.zeros((X.shape[0],), dtype=np.int32), X.astype(np.float32)])
gilc_g.set_weights([np.array([alpha], dtype=np.float32), beta.astype(np.float32)])
np.max(
np.abs(
gilc_g(
[np.zeros((X.shape[0],), dtype=np.int32), X.astype(np.float32)]
).numpy() - (alpha + X@beta)
)
)

但当我尝试将一个模型与之匹配时,它很快就停止了进展。

class OLS_gather(tf.keras.Model):
"""
"""
def __init__(self, ngroups=1, name="ols", **kwargs):
super(OLS_gather, self).__init__(name=name, **kwargs)
self.lm = GroupedInterceptLinearCoeffs_gather(ngroups)
def call(self, inputs):
print(inputs[0].shape)
print(inputs[1].shape)
out = self.lm(inputs)
return out

olsmodel_g = OLS_gather(ngroups=1)
olsmodel_g.compile(optimizer, loss=tf.keras.losses.MeanSquaredError())
olsmodel_g.fit([np.zeros((X.shape[0],), dtype=np.int32), X.astype(np.float32)], y.astype(np.float32), epochs=50)

检查b权重表明,它并没有朝着正确的方向移动权重,但类似的模型(没有聚集(会迅速收敛(请参阅所有代码的要点(。我是否错误地使用了tf.gather?如果是这样的话,有没有其他方法可以"重新索引"这样的数组,以按特定顺序生成重复项?

(此外,我知道我不一定需要构建自己的层/模型,但我的实际示例有点复杂,我需要自定义损失函数等。(

我能够重现您描述的行为,我的猜测是使用tf.gather动态使用权重会以某种方式干扰模型学习。

您可以避免完全使用tf.gather,而是使用带有tf.one_hot的单热编码和简单的乘法来选择要使用的截距。

下面的代码对于一个有两个截取的模型是成功的,其中输入数据总是选择第一个:

import numpy as np
import tensorflow as tf
nobs = 100
alpha = 1.0
beta = np.array([0.0, 0.5, 0.25])
L = np.array([[1.0, 0.0, 0.0], [0.25, 1.1, 0.0], [0.2, 0.2, 1.25]])
X = np.random.randn(nobs, 3) @ L
y = alpha + X@beta
class GroupedInterceptLinearCoeffs_gather(tf.keras.layers.Layer):
""""""
def __init__(self, ngroup=2, **kwargs):
super(GroupedInterceptLinearCoeffs_gather, self).__init__()
self.ngroup = ngroup
def build(self, input_shape):
self.a = self.add_weight(
shape=(self.ngroup,), dtype="float32",
initializer="random_normal", trainable=True
)
self.b = self.add_weight(
shape=(input_shape[1][-1],), dtype="float32",
initializer="random_normal", trainable=True
)
@tf.function()
def call(self, inputs):
out = tf.linalg.matvec(inputs[0], self.a) 
+ tf.linalg.matvec(inputs[1], self.b)
return out
class OLS_gather(tf.keras.Model):
""""""
def __init__(self, ngroups=1, name="ols", **kwargs):
super(OLS_gather, self).__init__(name=name, **kwargs)
self.lm = GroupedInterceptLinearCoeffs_gather(ngroups)
def call(self, inputs):
out = self.lm(inputs)
return out
olsmodel_g = OLS_gather(ngroups=2)
optimizer_g = tf.keras.optimizers.Adam(learning_rate=1e-2)
olsmodel_g.compile(optimizer_g, loss=tf.keras.losses.MeanSquaredError())
x = [tf.cast(tf.one_hot(np.zeros((X.shape[0],), dtype=np.int32), depth=2), tf.float32),
tf.constant(X)]
y = tf.constant(y)
olsmodel_g.fit(x=x, y=y,
epochs=25, batch_size=25, verbose=False
)
print(olsmodel_g.trainable_weights)
olsmodel_g.fit(x=x, y=y,
epochs=1, batch_size=25
)

在@spencerlyon2的帮助下,我认为我们对正在发生的事情有了更好的了解。

问题似乎是,当将一维数组作为输入传递给tensorflow时,他们会在标准化输入时自动将其转换为二维数组(请参阅,这些代码行(。这会导致形状不一致,而且在拟合时似乎会无声地失败,并且没有正确更新参数,而不是抛出错误(?(。

解决这一问题的一种强力方法是简单地挤压GroupedInterceptLinearCoeffs_gathercall方法中的第一个输入。

相关内容

最新更新