keras 自定义函数不会 eval/compile/fit

keras custom function won't eval/compile/fit

我正在尝试使用以下(自定义)损失函数来训练 keras 神经网络:

y_pred 和 y_true 是长度为 40 的数组。 假设 y_true 在任何地方都是 0,除了第 j 个分量,它等于 1, 分别为 y_true 和 y_pred 写 y 和 z。然后:

空白"> {i<40}(|i-j|+1)\cdot(y_i-z_i)^2" title="boostSquare(y,z)=\sum_{i<40}(|i-j|+1)\cdot(y_i-z_i)^2" />

这是我打算使用的代码:

import keras.backend as K
def boost_square(y_true, y_pred):
    w = K.constant(np.array([[np.abs(i - j) + 1 for i in range(40)] for j in 
                            range(40)]), dtype=np.float64)
    return K.sum(K.transpose(w * y_true) * K.square(y_true - y_pred))

运行 这有效并按预期打印 2.25 :

y_true = np.array([int(i == 2) for i in range(40)])
y_pred = np.array([0.5 * int(i < 2) for i in range(40)])
print(K.eval(boost_square(y_true, y_pred)

然而,编译失败并显示以下错误消息:

from keras.layers import Input, Dense
input_layer = Input(shape=(40,), name='input_layer')
output_layer = Dense(units=40, name='output_layer')(input_layer)
model = Model([input_layer], [output_layer])
model.compile(optimizer='adam', loss=boost_square, 
              metrics=['accuracy'])

TypeError: Input 'y' of 'Mul' Op has type float32 that does not match type float64 of argument 'x'.

因为我很固执,所以我也尝试了这个,但没有解决任何问题并且可能会影响性能:

def boost_square_bis(y_true, y_pred):
    z_true = K.cast(y_true, np.float64)
    z_pred = K.cast(y_pred, np.float64)
    w = K.constant(np.array([[np.abs(i - j) + 1 for i in range(40)] for j in 
                   range(40)]), dtype=np.float64)
    boost = K.transpose(w * z_true)
    boost = K.cast(boost, dtype=np.float64)
    square = K.square(z_true - z_pred)
    square = K.cast(square, np.float64)
    ret = K.sum(boost * square)
    return K.cast(ret, dtype=np.float64)

我错过了什么?这个错误从何而来?

解决方案 1

感谢 AnnaKrogager:w 的 dtype 与模型不兼容。这 模型在定义时编译:

def boost_square(y_true, y_pred):
    w = K.constant(np.array([[np.abs(i - j) + 1 for i in range(40)] for j in 
                            range(40)]), dtype=np.float64)
    return K.sum(K.transpose(w * y_true) * K.square(y_true - y_pred))

迭代 1

现在,模型编译但不适合,我收到此错误消息(128 是 batch_size):

ValueError: Dimensions must be equal, but are 40 and 128 for 'mul_2' (op: 'Mul') with input shapes: [40,40], [128,40].

我的自定义损失函数确实在第一个轴上表现得很奇怪, 此代码将引发完全相同的错误:

fake_input = np.random.rand(128,40)
fake_output = np.random.rand(128,40)
print(K.eval(boost_square(fake_intput,fake_output)))

迭代 2

正如 AnnaKrogager 指出的那样,使用正确的 np.dot 比 * 后跟换位(与批处理轴混淆)更一致。所以我想出了 boost_square 的这个新定义:

def boost_square(y_true, y_pred):
    w = K.constant(np.array([[np.abs(i - j) + 1 for i in range(40)] for j in 
                              range(40)]), dtype=np.float32)
    return K.sum(K.dot(w, y_true) * K.square(y_true - y_pred))

但是当我尝试拟合模型时,这会触发以下操作:

AttributeError: 'numpy.ndarray' object has no attribute 'get_shape'

因此,我尝试了

def boost_square(y_true, y_pred):
    w = K.constant(np.array([[np.abs(i - j) + 1 for i in range(40)] for j in 
                             range(40)]), dtype=np.float32)
    return K.sum(K.dot(K.dot(w, y_true), K.square(y_true - y_pred)))

并收到一条全新的错误消息 \o/ :

Matrix size-incompatible: In[0]: [40,40], In[1]: [32,40]

最终解决方案

感谢 AnnaKrogager

成分

  1. 使用适当的矩阵乘积 K.dot 比 * .
  2. 虽然 w 本来是要应用于 y_true,但不要使用 K.dot(w,y_true) 因为 它弄乱了批处理轴。 Ratter,使用 K.dot(y_true,w) 并转置以具有匹配的形状。
  3. 如果你想用 np.arrays 测试损失函数,比如 y_true 和 y_pred,确保你将它们重铸为 K.constant。

这是代码

def boost_square(y_true, y_pred):
    w = K.constant(np.array([[np.abs(i - j) + 1 for i in range(40)] for j in
                range(40)]), dtype=np.float32)
    return K.sum(K.dot(K.dot(y_true, w), K.transpose(K.square(y_true - 
                                                              y_pred))))

测试:

y_true = K.constant(np.array([[int(i == 2) for i in range(40)]], 
                             dtype=np.float32))
y_pred = K.constant(np.array([[0.5 * int(i < 2) for i in range(40)]], 
                    dtype=np.float32))
print(K.eval(boost_square(y_true,y_pred)))
>>2.25

问题是您的模型输出 float32 而损失函数中的常量 w 是类型 float64。您可以通过简单地更改 w:

的数据类型来解决此问题
def boost_square(y_true, y_pred):
    w = K.constant(np.array([[np.abs(i - j) + 1 for i in range(40)] for j in 
                            range(40)]), dtype=np.float32)
    return K.sum(K.transpose(w * y_true) * K.square(y_pred))

第二个问题的答案:如果你在 Keras 中乘以张量,这意味着张量在元素方面得到相乘,因此它们必须具有相同的形状。你想要的是矩阵乘积,所以你应该使用 K.dot(y, w) 而不是 w * y.