Keras / Tensorflow中基于切片的分配?

sh3*_*211 6 python numpy slice keras tensorflow

看我的Keras自定义损失函数:

def custom_loss(y_true, y_pred):
    sqerr = (y_true - y_pred)**2
    sqerr[:,4:-1:7] = sqerr[:,4:-1:7] * ((y_true[:,2:-1:7]-y_true[:,3:-1:7])/y_true[:,2:-1:7])**2
    return sqerr.mean()
Run Code Online (Sandbox Code Playgroud)

但是'sqerr'不是一个numpy数组,因此此代码会导致错误

TypeError:“张量”对象不支持项目分配

因此,我阅读了“如何在Tensorflow中进行切片分配”一文,其中包括jdehesa的答案以及此讨论中GitHub页面。所以这就是我现在所拥有的...

def custom_loss(y_true, y_pred):
    sqerr = K.square(y_true-y_pred)

    sqerr = tf.Variable(  sqerr , validate_shape=False )
    with tf.control_dependencies([sqerr[:,4:-1:7].assign( sqerr[:,4:-1:7] * ((y_true[:,2:-1:7]-y_true[:,3:-1:7])/y_true[:,2:-1:7])**2 )]):
        sqerr = tf.identity(sqerr)

    return K.mean(sqerr)
Run Code Online (Sandbox Code Playgroud)

...但是很明显我在实际使用它的时候就把它搞砸了:

追溯(最近一次通话):setup_model中的文件“ my_awesome_nn.py”,第119行

model.compile(loss=custom_loss, optimizer=opt)  
Run Code Online (Sandbox Code Playgroud)

在编译的文件“ /opt/anaconda/envs/py35/lib/python3.5/site-packages/keras/engine/training.py”中,行850

sample_weight, mask)   
Run Code Online (Sandbox Code Playgroud)

文件“ /opt/anaconda/envs/py35/lib/python3.5/site-packages/keras/engine/training.py”,行465,加权后

score_array = K.mean(score_array, axis=list(range(weight_ndim, ndim))) 
Run Code Online (Sandbox Code Playgroud)

TypeError:“ NoneType”对象无法解释为整数

发生的事情是,TF切片仅允许应用于变量,而不是普通张量,因此我将其转换为变量。但是,当我强制转换为Variable时,它想知道形状,但是此时形状是“动态定义的”(即第一个元素是“?”)。因此,设置validate_shape = False可以让我实际定义一个变量,但这会破坏Keras以后想要的尺寸信息。观察:

def custom_loss(y_true, y_pred):  
        sqerr = K.square(y_true-y_pred)
        print("K.ndim(sqerr) #1 = ",K.ndim(sqerr))
        sqerr = tf.Variable(  sqerr , validate_shape=False )
        print("K.ndim(sqerr) #2 = ",K.ndim(sqerr))
        with tf.control_dependencies([sqerr[:,4:-1:7].assign( sqerr[:,4:-1:7] * ((y_true[:,2:-1:7]-y_true[:,3:-1:7])/y_true[:,2:-1:7])**2 )]):
            sqerr = tf.identity(sqerr)

        return K.mean(sqerr)
Run Code Online (Sandbox Code Playgroud)

...结果输出

K.ndim(sqerr)#1 = 2

K.ndim(sqerr)#2 =无

因此,稍后,当Keras training.py代码说“ ndim = K.ndim(score_array)”时,它以None结束,从而出现NoneType错误。

谁能阐明我该怎么做? 似乎我不能在不转换为变量的情况下进行切片,无法为将保留动态形状的动态形状的张量定义变量。

(如果我只是省略上面的“中间三行”,并使我的自定义损失为常规的MSE,那么这是一个完全有效的代码)

Yu-*_*ang 3

我认为可以避免这种自定义损失的切片分配。如果要调整 的损失值sqerr[:, 4:-1:7],可以从总损失中减去原始值,然后将调整后的损失值加回。

def custom_loss_keras(y_true, y_pred):
    # first sum up the squared error column-wise
    sqerr = K.square(y_true - y_pred)
    loss = K.sum(sqerr, axis=-1)

    # subtract the loss for the sliced part
    loss -= K.sum(sqerr[:, 4:-1:7], axis=-1)

    # add back the adjusted loss for the sliced part
    denominator = K.maximum(y_true[:, 2:-1:7], K.epsilon())  # handle zero-division
    numerator = y_true[:, 2:-1:7] - y_true[:, 3:-1:7]
    loss += K.sum(sqerr[:, 4:-1:7] * K.square(numerator / denominator), axis=-1)

    # take average
    ncols = K.int_shape(y_pred)[-1]
    loss /= ncols
    return K.mean(loss)
Run Code Online (Sandbox Code Playgroud)

您可以通过将其与原始 numpy 版本进行比较来验证此函数:

def custom_loss_numpy(y_true, y_pred):
    sqerr = (y_true - y_pred)**2
    sqerr[:,4:-1:7] = sqerr[:,4:-1:7] * ((y_true[:,2:-1:7]-y_true[:,3:-1:7])/y_true[:,2:-1:7])**2
    return sqerr.mean()

y_true = np.random.rand(50, 1000)
y_pred = np.random.rand(50, 1000)

print(custom_loss_numpy(y_true, y_pred))
889.992075384

print(K.eval(custom_loss_keras(K.variable(y_true), K.variable(y_pred))))
889.992
Run Code Online (Sandbox Code Playgroud)