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。尝试 运行 通过您的函数进行批处理,看看它是如何工作的。我发现另一件对处理形状问题非常有帮助的事情是使用 ipdbipython 中的嵌入模式。您可以设置断点并进入有问题的行并观察和修复。祝你好运!

解决方案(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) 

我认为问题出在批处理轴上。为了做得更好,我需要了解领域,但我还有一些学习要做:)