很抱歉标题太模糊,不能有太多字符。
简要说明:
我正在为一个图像分析程序实现一个自动编码器CNN架构,该程序需要定制丢失功能,而这些功能在keras后端或tensorflow中的任何地方都不存在。这没什么大不了的;我喜欢数值计算,并很乐意提高我的OOP技能。
为了让我的程序顺利运行,我需要我的loss函数是可调用的对象(基本上是keras实现其loss对象的方式,因此我基于keras loss源代码镜像了我的loss类)。到目前为止,我已经学到了很多。我有一个类ReconstructionLoss(LossWrapper):
,它是LossWrapper(Loss):
的一个子类,它是父Loss(object):
的一个子类。具体来说,重建损失的实例需要一个张量“DecodeOut”kwarg,但其他不同类型的损失不需要这个kwarg张量。它们都需要y_true并对参数进行编码。
问题:
ReconstructionLoss实例应该能够采用**kwargs(始终为“DecodeOut”=某个张量)。但不知何故,kwarg被传递到父类Loss()
中的__call__()
签名,导致TypeError: __call__() got an unexpected keyword argument 'DecodeOut'
。我认为当Loss类中的__call__(self, arg1, arg2):
主体中使用losswrapercall()
方法时,就会发生这种情况。如果我继续谈论它,它会变得更加复杂(哈哈),所以我会让你自己看看代码,看看其余的细节。我确信我犯了一个非常微妙的新手错误,因为我已经有一分钟没有做OOP了。为任何奇怪的凹痕道歉;复制并粘贴到此处的代码块中是一件麻烦事。(我在这里也只展示了相关模块):
from __future__ import absolute_import, division, print_function, unicode_literals
import sys,os
from pathlib import Path
from matplotlib import pyplot, cm
import tensorflow as tf
import tensorflow_io as tfio
import keras
from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras import Model
from tensorflow.python.framework import tensor_util
from tensorflow.python.keras import backend as KB
from tensorflow.python.keras.utils import tf_utils
from tensorflow.python.ops.losses import util as tf_losses_util
from tensorflow.python.keras.utils import losses_utils
class Loss(object):
def __init__(self):
print('Loss Object Instantiated')
#HERE IS WHERE I GET THE ERROR once compilation gets here.
#kwarg 'DecodeOut' is being passed as a parameter
# in __call__(self, y_true, EncodeOut, sample_weight = None).
#It should only be called in the *return value* of the call() method
# defined in LossWrapper class.
def __call__(self, y_true, EncodeOut, sample_weight = None):#boom, error
graph_ctx = tf_utils.graph_context_for_symbolic_tensors(
y_true,EncodeOut, sample_weight)
with KB.name_scope(self.__class__.__name__), graph_ctx:
losses = self.call(y_true,EncodeOut)
return losses_utils.compute_weighted_loss(losses, sample_weight)
def call(self, y_true, EncodeOut):
"""Invokes the `Loss` instance.
Args:
y_true: Ground truth values, with the same shape as 'y_pred'.
y_pred: The predicted values.
"""
NotImplementedError('Must be implemented in subclasses.')
class LossWrapper(Loss):
def __init__(self, func, **kwargs):
super(LossWrapper, self).__init__()
self.func = func
self.func_kwargs = kwargs
#See below, def call(self, y_true, EncodeOut), RETURNS
#reconstruction_loss(y_true,EncodeOut, **self.func_kwargs),
#but the kwarg = 'DecodeOut' is getting passed in the __call()__ signature when I implement:
# recon_loss_obj = ReconstructionLoss()
# reconLoss = recon_loss_obj (trueLabels, someTensor, DecodeOut = someOtherTensor)
def call(self, y_true, EncodeOut):
return self.func(y_true,EncodeOut, **self.func_kwargs)
class ReconstructionLoss(LossWrapper):
def __init__(self, DecodeOut = [0]):
super(ReconstructionLoss, self).__init__(
reconstruction_loss, #this is the value of 'func' in all instances,
#function defined at below
DecodeOut = DecodeOut)
self.DecodeOut = DecodeOut
def reconstruction_loss( y_true, residual, DecodeOut = [0]):
print(DecodeOut.shape)
K = tf.size(residual[0,:,:,:]).numpy()
L1_norm_batches = tf.norm(residual-DecodeOut, ord = 1, axis = [-3,-2])
reconstruction_loss = np.sum(L1_norm_batches.numpy())/K
return reconstruction_loss
实施示例:
loss_object = ReconstructionLoss()
a=loss_object(y_true_train, conv11, DecodeOut = select)
Loss Object Instantiated
Traceback (most recent call last):
File "C:\Python File\Tamper.py", line 794, in <module>
a=loss_object(y_true_train, conv11, DecodeOut = select)
TypeError: __call__() got an unexpected keyword argument 'DecodeOut'
我无法理解为什么“DecodeOut”首先要传递给__call__()
签名,而它应该只传递给self.func(y_true, EncodeOut, **kwarg)
(在这种情况下,每当ReconstructionLoss()
对象被实例化时self.func = reconstruction_loss
)
我知道这可能是一个非常明显的错误,这是很多信息,但我正试图尽可能详尽。如果你想知道我为什么采用这种方法,那是因为我有几个其他损失对象(例如重建损失与激活损失)我需要能够调用,我这样做是为了学习。此外,我感到困惑,因为这个实现与Keras Loss源代码直接类似……就我而言,它们采用完全相同的方法。 如果您愿意,请查看他们的代码,特别是Loss、LossFunctionWrapper,然后是BinaryCrossEntropy类和函数BinaryCrossEntropy:
https://keras-gym.readthedocs.io/en/stable/_modules/tensorflow/python/keras/losses.html
我很确定我遗漏了一些我不知道的开发人员所做的微妙的事情,或者我对继承发生的事情有严重的误解。 在调用super()之前,我已经尝试过定义所有的self属性,但没有成功…甚至不确定这是否合适。 对于那些想知道为什么要这样设计的人来说,下面是一个keras代码的示例片段,其中包含Loss、Wrapper、LossTypeClass、Loss类型的函数和kwargs:keras代码有点拥挤,因为它们还有几个可选参数,如reduce、name等
class Loss(object):
def __init__(self, reduction=losses_utils.ReductionV2.AUTO,name=None):
losses_utils.ReductionV2.validate(reduction)
self.reduction = reduction
self.name = name
def __call__(self, y_true, y_pred, sample_weight=None):
# If we are wrapping a lambda function strip '<>' from the name as it is not
# accepted in scope name.
scope_name = 'lambda' if self.name == '<lambda>' else self.name
graph_ctx = tf_utils.graph_context_for_symbolic_tensors(
y_true, y_pred, sample_weight)
with K.name_scope(scope_name or self.__class__.__name__), graph_ctx:
losses = self.call(y_true, y_pred)
return losses_utils.compute_weighted_loss(
losses, sample_weight, reduction=self._get_reduction())
class LossFunctionWrapper(Loss):
def __init__(self,
fn,
reduction=losses_utils.ReductionV2.AUTO,
name=None,
**kwargs):
super(LossFunctionWrapper, self).__init__(reduction=reduction, name=name)
self.fn = fn
self._fn_kwargs = kwargs
def call(self, y_true, y_pred):
if tensor_util.is_tensor(y_pred) and tensor_util.is_tensor(y_true):
y_pred, y_true = tf_losses_util.squeeze_or_expand_dimensions(
y_pred, y_true)
return self.fn(y_true, y_pred, **self._fn_kwargs)
class BinaryCrossentropy(LossFunctionWrapper):
def __init__(self,
from_logits=False,# a kwarg not passed in __call__() of Loss(), passed when you call the instantiation of BinaryCrossentropy().
label_smoothing=0,# same
reduction=losses_utils.ReductionV2.AUTO,
name='binary_crossentropy'):
super(BinaryCrossentropy, self).__init__(
binary_crossentropy,
name=name,
reduction=reduction,
from_logits=from_logits,
label_smoothing=label_smoothing)
self.from_logits = from_logits
def binary_crossentropy(y_true, y_pred, from_logits=False, label_smoothing=0): # pylint: disable=missing-docstring
y_pred = ops.convert_to_tensor(y_pred)
y_true = math_ops.cast(y_true, y_pred.dtype)
label_smoothing = ops.convert_to_tensor(label_smoothing,dtype=K.floatx())
def _smooth_labels():
return y_true * (1.0 - label_smoothing) + 0.5 * label_smoothing
y_true = smart_cond.smart_cond(label_smoothing,
_smooth_labels, lambda: y_true)
return K.mean(
K.binary_crossentropy(y_true, y_pred, from_logits=from_logits), axis=-1)
~z~谢谢
代码没有什么问题,对OOP的理解很慢,代码实现不正确
kwargs
应该在实例化对象时传递,而不是在尝试调用实例时传递而不是:
相关问题 更多 >
编程相关推荐