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行
Run Code Online (Sandbox Code Playgroud)model.compile(loss=custom_loss, optimizer=opt)在编译的文件“ /opt/anaconda/envs/py35/lib/python3.5/site-packages/keras/engine/training.py”中,行850
Run Code Online (Sandbox Code Playgroud)sample_weight, mask)文件“ /opt/anaconda/envs/py35/lib/python3.5/site-packages/keras/engine/training.py”,行465,加权后
Run Code Online (Sandbox Code Playgroud)score_array = K.mean(score_array, axis=list(range(weight_ndim, ndim)))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,那么这是一个完全有效的代码)
我认为可以避免这种自定义损失的切片分配。如果要调整 的损失值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)