Tensorflow:在模型拟合期间确定自定义损失函数中批量大小的问题(批量大小 "None")
Tensorflow: Issues with determining batch size in custom loss function during model fitting (batch size of "None")
我正在尝试创建一个自定义损失函数,我必须在其中对张量进行多次切片。下面列出了一个示例:
# Since different nodes need different activations, I decided to just do it like this
def activations(y_true, y_pred):
n = y_true.shape[1]
means = tf.slice(y_pred, begin=[0,0], size=[y_pred.shape[0], n])
stdevs = tf.slice(y_pred, begin=[0,n], size=[y_pred.shape[0], n])
corrs = tf.slice(y_pred, begin=[0,2*n], size=[y_pred.shape[0], y_pred.shape[1]-2*n])
stdevs = keras.activations.softplus(stdevs)
corrs = keras.activations.tanh(corrs)
这个(和整个损失函数)在自制张量 y_true 和 y_pred 上手动测试时工作正常,但在损失函数中使用它时会在模型拟合时出错(编译顺利)。
File <filename>, line 105, in activations *
means = tf.slice(y_pred, begin=[0,0], size=[y_true.shape[0], n])
TypeError: Expected int32 passed to parameter 'size' of op 'Slice', got [None, 3] of type 'list' instead. Error: Expected int32, but got None of type 'NoneType'.
显然,在损失层内执行时无法确定批量大小。
我该如何解决这个问题?
(注意:我不只是在寻找针对此特定代码的解决方案,因为我经常对张量进行切片。我正在寻找切片的通用解决方案)。
我试图查看 and and I read through this post。编写自定义生成器来使批量大小保持静态真的是唯一的方法吗?
提前致谢?
编辑:
这是触发错误的代码的(极大)简化版本。
import numpy as np
import numpy.random as npr
import keras
from keras import layers
import tensorflow as tf
# Since different nodes need different activations, I decided to just do it like this
def dummy_loss_func(y_true, y_pred):
n = y_true.shape[1]
means = tf.slice(y_pred, begin=[0,0], size=[y_pred.shape[0], n])
stdevs = tf.slice(y_pred, begin=[0,n], size=[y_pred.shape[0], n]) #I'm assuming these are all (0, infty)
corrs = tf.slice(y_pred, begin=[0,2*n], size=[y_pred.shape[0], y_pred.shape[1]-2*n])
stdevs = keras.activations.softplus(stdevs)
corrs = keras.activations.tanh(corrs)
relErrors = tf.math.square(means - y_true)/stdevs
return tf.reduce_mean(tf.math.square(relErrors))
def dummy_model(dim):
model = keras.Sequential(
[
keras.Input(shape=(1)),
layers.Dense(2*dim + int(round(dim * (dim-1)/2)), kernel_initializer = tf.keras.initializers.GlorotUniform()),
]
)
model.summary()
model.compile(loss=dummy_loss_func, optimizer="adam")
return model
#Generating some fake data
n = 5000
dim = 3
pts = npr.uniform(size=[n, 2*dim + int(round(dim * (dim-1)/2))])
dummy_in = np.zeros(n)
print(dummy_in.size)
print(pts.size)
#Comping the model goes fine
model = dummy_model(dim)
# Model exucution will go fine
print(model.predict([0]))
# Just calling the loss function also works
print(dummy_loss_func(tf.constant([[3., 2., 1.],[1., 2., 3.]]), tf.constant([[2., 1., 1., 5., 3., 2., 3., 2., 1.], [2., 5., 1., 1., 3., 6., 3., 4., 1.]])))
# The error only comes here
model.fit(dummy_in, pts, verbose=1)
让我们一起解决这个问题。可能我们俩都需要来回编辑。
我将解决您问题的切片部分,因为根据信息,这是最容易处理的部分。
让我们实例化一个形状为 [3, 3, 3] 的张量:
y = tf.constant([ [[1, 2, 3] , [4, 5, 6 ], [7, 8, 9 ]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
[[19, 20, 21], [22, 23, 24], [25, 26, 27]] ])
请注意,这是 1
张量,形状为 [3, 3, 3]。让我们想象一下:
[ins] In [50]: y[0]
Out[50]:
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=int32)>
[ins] In [51]: y[1]
Out[51]:
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
array([[10, 11, 12],
[13, 14, 15],
[16, 17, 18]], dtype=int32)>
[ins] In [52]: y[2]
Out[52]:
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
array([[19, 20, 21],
[22, 23, 24],
[25, 26, 27]], dtype=int32)>
在轴方面,我们可以想象 left-most 轴包含 3 个 3x3 矩阵,我们在上面使用 y[0]
、y[1]
和 y[2]
引用了它们。现在让我们雕刻这个数字立方体。
[nav] In [53]: tf.slice(y, begin=[0, 0, 0], size=[2, 2, 2])
Out[53]:
<tf.Tensor: shape=(2, 2, 2), dtype=int32, numpy=
array([[[ 1, 2],
[ 4, 5]],
[[10, 11],
[13, 14]]], dtype=int32)>
这里发生的事情是我们要从更大的立方体中得到一个更小的立方体,特别是形状 [2, 2, 2]
,我们希望它从点 [0, 0, 0]
开始。所以我们要对那个更大的立方体进行三次切割:首先我们要进入“计算机”轴两步,所以最深层的任何东西都不应该出现(数字 [19, 20, 21],[22, 23, 24],[25, 26, 27]
形状 [3, 3]
).然后我们要进行水平切割,这意味着 [7, 8, 9],[16, 17, 18]
中的 none 会出现, [25, 26, 27]
已经在最后一次切割中被切掉了。最后,我们从原点开始垂直切割 2 步,确保 [3, 6],[12,15]
不会出现。所以我们在第一次砍掉了 9 个数字,在第二次砍掉了 9 个,但是三个与第一次砍掉了重叠,所以我们只丢了 6 个。第三次砍,我们会输掉九个,但我们在第一次砍中损失了三个,在第二次砍中损失了两个(本来是三个,但一个与第一次重叠),最后一次砍中损失了四个. 27 - (9 + 6 + 4) = 8
这就是我们得到的。
其中一项关键工作是提出问题:我这里有一批,还是我正在处理的批次中有一个观察结果。你怎么知道? left-most 轴是批次轴,一般表示为 None
,这意味着批次数是可变的。让我们制作一批我们拥有的张量,您可以按如下方式使用上述张量:
[ins] In [57]: tf.reshape(y, shape=(-1, 3, 3, 3))
Out[57]:
<tf.Tensor: shape=(1, 3, 3, 3), dtype=int32, numpy=
array([[[[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9]],
[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]],
[[19, 20, 21],
[22, 23, 24],
[25, 26, 27]]]], dtype=int32)>
[ins] In [58]: tf.reshape(y, shape=(-1, 3, 3, 3)).shape
Out[58]: TensorShape([1, 3, 3, 3])
上面说的是重塑我的数据,以便我有一个 3x3x3 的立方体,但我还想要 left-most 中的一些东西,也就是批处理、轴。由于有 27 个数字,它只是“加深”了维度。这可以通过在上面的输出中添加另一对 [ ]
来看出。它毕竟无法为我们制造数字,因为这些是我们的观察结果。您也可以使用 tf.expand_dims
,但我发现 tf.reshape
更直观。
现在我们有一个大小为 1 的批次,其中每个观察值都是一个形状为 [3, 3, 3]
的立方体,如果您愿意,可以将其分配给 y_pred
。尝试 运行 通过您的函数进行批处理,看看它是如何工作的。我发现另一件对处理形状问题非常有帮助的事情是使用 ipdb
和 ipython
中的嵌入模式。您可以设置断点并进入有问题的行并观察和修复。祝你好运!
解决方案(w/o 任何基础领域知识。显然张量与领域无关:))
pts_tensor = tf.constant(pts)
dummy_in_tensor = tf.constant(tf.reshape(dummy_in, (-1,1)))
my_ds = tf.data.Dataset.from_tensor_slices((dummy_in_tensor, pts_tensor))
model.fit(my_ds, verbose=1)
我认为问题出在批处理轴上。为了做得更好,我需要了解领域,但我还有一些学习要做:)
我正在尝试创建一个自定义损失函数,我必须在其中对张量进行多次切片。下面列出了一个示例:
# Since different nodes need different activations, I decided to just do it like this
def activations(y_true, y_pred):
n = y_true.shape[1]
means = tf.slice(y_pred, begin=[0,0], size=[y_pred.shape[0], n])
stdevs = tf.slice(y_pred, begin=[0,n], size=[y_pred.shape[0], n])
corrs = tf.slice(y_pred, begin=[0,2*n], size=[y_pred.shape[0], y_pred.shape[1]-2*n])
stdevs = keras.activations.softplus(stdevs)
corrs = keras.activations.tanh(corrs)
这个(和整个损失函数)在自制张量 y_true 和 y_pred 上手动测试时工作正常,但在损失函数中使用它时会在模型拟合时出错(编译顺利)。
File <filename>, line 105, in activations *
means = tf.slice(y_pred, begin=[0,0], size=[y_true.shape[0], n])
TypeError: Expected int32 passed to parameter 'size' of op 'Slice', got [None, 3] of type 'list' instead. Error: Expected int32, but got None of type 'NoneType'.
显然,在损失层内执行时无法确定批量大小。
我该如何解决这个问题?
(注意:我不只是在寻找针对此特定代码的解决方案,因为我经常对张量进行切片。我正在寻找切片的通用解决方案)。
我试图查看
提前致谢?
编辑: 这是触发错误的代码的(极大)简化版本。
import numpy as np
import numpy.random as npr
import keras
from keras import layers
import tensorflow as tf
# Since different nodes need different activations, I decided to just do it like this
def dummy_loss_func(y_true, y_pred):
n = y_true.shape[1]
means = tf.slice(y_pred, begin=[0,0], size=[y_pred.shape[0], n])
stdevs = tf.slice(y_pred, begin=[0,n], size=[y_pred.shape[0], n]) #I'm assuming these are all (0, infty)
corrs = tf.slice(y_pred, begin=[0,2*n], size=[y_pred.shape[0], y_pred.shape[1]-2*n])
stdevs = keras.activations.softplus(stdevs)
corrs = keras.activations.tanh(corrs)
relErrors = tf.math.square(means - y_true)/stdevs
return tf.reduce_mean(tf.math.square(relErrors))
def dummy_model(dim):
model = keras.Sequential(
[
keras.Input(shape=(1)),
layers.Dense(2*dim + int(round(dim * (dim-1)/2)), kernel_initializer = tf.keras.initializers.GlorotUniform()),
]
)
model.summary()
model.compile(loss=dummy_loss_func, optimizer="adam")
return model
#Generating some fake data
n = 5000
dim = 3
pts = npr.uniform(size=[n, 2*dim + int(round(dim * (dim-1)/2))])
dummy_in = np.zeros(n)
print(dummy_in.size)
print(pts.size)
#Comping the model goes fine
model = dummy_model(dim)
# Model exucution will go fine
print(model.predict([0]))
# Just calling the loss function also works
print(dummy_loss_func(tf.constant([[3., 2., 1.],[1., 2., 3.]]), tf.constant([[2., 1., 1., 5., 3., 2., 3., 2., 1.], [2., 5., 1., 1., 3., 6., 3., 4., 1.]])))
# The error only comes here
model.fit(dummy_in, pts, verbose=1)
让我们一起解决这个问题。可能我们俩都需要来回编辑。
我将解决您问题的切片部分,因为根据信息,这是最容易处理的部分。
让我们实例化一个形状为 [3, 3, 3] 的张量:
y = tf.constant([ [[1, 2, 3] , [4, 5, 6 ], [7, 8, 9 ]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
[[19, 20, 21], [22, 23, 24], [25, 26, 27]] ])
请注意,这是 1
张量,形状为 [3, 3, 3]。让我们想象一下:
[ins] In [50]: y[0]
Out[50]:
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=int32)>
[ins] In [51]: y[1]
Out[51]:
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
array([[10, 11, 12],
[13, 14, 15],
[16, 17, 18]], dtype=int32)>
[ins] In [52]: y[2]
Out[52]:
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
array([[19, 20, 21],
[22, 23, 24],
[25, 26, 27]], dtype=int32)>
在轴方面,我们可以想象 left-most 轴包含 3 个 3x3 矩阵,我们在上面使用 y[0]
、y[1]
和 y[2]
引用了它们。现在让我们雕刻这个数字立方体。
[nav] In [53]: tf.slice(y, begin=[0, 0, 0], size=[2, 2, 2])
Out[53]:
<tf.Tensor: shape=(2, 2, 2), dtype=int32, numpy=
array([[[ 1, 2],
[ 4, 5]],
[[10, 11],
[13, 14]]], dtype=int32)>
这里发生的事情是我们要从更大的立方体中得到一个更小的立方体,特别是形状 [2, 2, 2]
,我们希望它从点 [0, 0, 0]
开始。所以我们要对那个更大的立方体进行三次切割:首先我们要进入“计算机”轴两步,所以最深层的任何东西都不应该出现(数字 [19, 20, 21],[22, 23, 24],[25, 26, 27]
形状 [3, 3]
).然后我们要进行水平切割,这意味着 [7, 8, 9],[16, 17, 18]
中的 none 会出现, [25, 26, 27]
已经在最后一次切割中被切掉了。最后,我们从原点开始垂直切割 2 步,确保 [3, 6],[12,15]
不会出现。所以我们在第一次砍掉了 9 个数字,在第二次砍掉了 9 个,但是三个与第一次砍掉了重叠,所以我们只丢了 6 个。第三次砍,我们会输掉九个,但我们在第一次砍中损失了三个,在第二次砍中损失了两个(本来是三个,但一个与第一次重叠),最后一次砍中损失了四个. 27 - (9 + 6 + 4) = 8
这就是我们得到的。
其中一项关键工作是提出问题:我这里有一批,还是我正在处理的批次中有一个观察结果。你怎么知道? left-most 轴是批次轴,一般表示为 None
,这意味着批次数是可变的。让我们制作一批我们拥有的张量,您可以按如下方式使用上述张量:
[ins] In [57]: tf.reshape(y, shape=(-1, 3, 3, 3))
Out[57]:
<tf.Tensor: shape=(1, 3, 3, 3), dtype=int32, numpy=
array([[[[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9]],
[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]],
[[19, 20, 21],
[22, 23, 24],
[25, 26, 27]]]], dtype=int32)>
[ins] In [58]: tf.reshape(y, shape=(-1, 3, 3, 3)).shape
Out[58]: TensorShape([1, 3, 3, 3])
上面说的是重塑我的数据,以便我有一个 3x3x3 的立方体,但我还想要 left-most 中的一些东西,也就是批处理、轴。由于有 27 个数字,它只是“加深”了维度。这可以通过在上面的输出中添加另一对 [ ]
来看出。它毕竟无法为我们制造数字,因为这些是我们的观察结果。您也可以使用 tf.expand_dims
,但我发现 tf.reshape
更直观。
现在我们有一个大小为 1 的批次,其中每个观察值都是一个形状为 [3, 3, 3]
的立方体,如果您愿意,可以将其分配给 y_pred
。尝试 运行 通过您的函数进行批处理,看看它是如何工作的。我发现另一件对处理形状问题非常有帮助的事情是使用 ipdb
和 ipython
中的嵌入模式。您可以设置断点并进入有问题的行并观察和修复。祝你好运!
解决方案(w/o 任何基础领域知识。显然张量与领域无关:))
pts_tensor = tf.constant(pts)
dummy_in_tensor = tf.constant(tf.reshape(dummy_in, (-1,1)))
my_ds = tf.data.Dataset.from_tensor_slices((dummy_in_tensor, pts_tensor))
model.fit(my_ds, verbose=1)
我认为问题出在批处理轴上。为了做得更好,我需要了解领域,但我还有一些学习要做:)