class Tensor:
def __init__(self, value, requires_grad=False, local_gradients=None):
self.value = value
def __add__(self, other):
print("adding")
other = other if isinstance(other, Tensor) else Tensor(other)
value = self.value + other.value
return Tensor(value)
def __radd__(self, other):
print("radding")
other = other if isinstance(other, Tensor) else Tensor(other)
value = self.value + other.value
return Tensor(value)
@staticmethod
def ones(shape):
return Tensor(np.ones(shape))
a = np.random.randn(4, 3, 2, 1)
b = Tensor.ones((3, 2, 1))
b + a
a + b
我们得到以下结论
adding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
radding
我们看到__add__它被执行了一次。正如我想要的那样。但是__radd__它被执行了 24 次(数组中有 24 个元素),所以它似乎是__radd__逐个元素地执行的。这不是我们所期望的。有什么方法可以纠正这种行为吗?
要点是,只有在方法y未定义时才会调用方法
__radd__y 。它显然是以这样一种方式定义的:它只是尝试将其张量的每个元素添加到您的对象中,为此使用张量的每个单独元素来调用它。object2__add__object1Numpyobject1object2__radd__object1他们用英文
SO写道,如果您继承np.ndarray并覆盖该方法,就可以克服这个问题__add__,但对我来说,这个方案并没有立即起作用,我需要弄清楚。诀窍是只有当该方法抛出异常
__radd__时才会被调用。但是,例如,看看您的实现:您是否抛出异常?不,您的加法方法正在尝试将右侧参数转换为所需的类型。因此,在张量位于左侧的表达式中,右侧操作数的radd方法将永远不会被调用。__add__NotImplemented__add__ndarray在同一件事的情况下。该方法ndarray.__add__尝试通过调用从正确的操作数构造一个数组np.asarray()。该函数要么构造对象ndarray,要么返回原样的对象。对于张量,会出现第二种情况 - 它按原样返回。该函数ndarray.__add__发现右侧操作数不是数组,并执行数组标量加法,即将数组的每个元素与张量相加。由于,
float没有加法运算Tensor。Tensor.__radd__下面是一个稍加修改的加法实现,显示操作数的类型:
我们得到:
事实证明,
numpy它将你的张量解释为标量,并执行添加标量的操作,即逐元素加法。恕我直言,你对此无能为力。因此,如果要对数组进行向量运算,则需要先放置张量。
我根本不会重载该运算符
+,但这样做Tensor.add_array(a)是为了不意外地混淆操作数的顺序,并且在几个小时内都不会捕获错误。