Element-wise max of each of the input tensors (with Numpy-style broadcasting support).
| 3397 | |
| 3398 | # optimize max to support multi inputs |
| 3399 | class Max(Operator): |
| 3400 | """ |
| 3401 | Element-wise max of each of the input tensors (with Numpy-style |
| 3402 | broadcasting support). |
| 3403 | """ |
| 3404 | |
| 3405 | def __init__(self): |
| 3406 | super(Max, self).__init__() |
| 3407 | self.masks = [] |
| 3408 | |
| 3409 | def _max(self, a, b): |
| 3410 | """ |
| 3411 | Args: |
| 3412 | a (CTensor): First operand |
| 3413 | b (CTensor): Second operand |
| 3414 | Returns: |
| 3415 | CTensor, the output |
| 3416 | tuple of CTensor, mask tensor |
| 3417 | """ |
| 3418 | m = singa.__sub__(a, b) |
| 3419 | mask0 = singa.GEFloat(m, 0) |
| 3420 | mask1 = singa.LTFloat(m, 0) |
| 3421 | res = singa.__add__(singa.__mul__(mask0, a), singa.__mul__(mask1, b)) |
| 3422 | return res, (mask0, mask1) |
| 3423 | |
| 3424 | def forward(self, *x): |
| 3425 | """ |
| 3426 | Args: |
| 3427 | *x (a list of CTensor): List of tensors for max. |
| 3428 | Returns: |
| 3429 | CTensor, the output |
| 3430 | """ |
| 3431 | assert (len(x) > 0) |
| 3432 | self.l = len(x) |
| 3433 | if len(x) == 1: |
| 3434 | res, masks = self._max(x[0], x[0]) |
| 3435 | self.masks.append(masks) |
| 3436 | return x[0] |
| 3437 | res, masks = self._max(x[0], x[1]) |
| 3438 | self.masks.append(masks) |
| 3439 | for i in range(2, len(x)): |
| 3440 | res, masks = self._max(res, x[i]) |
| 3441 | self.masks.append(masks) |
| 3442 | return res |
| 3443 | |
| 3444 | def backward(self, dy): |
| 3445 | """ |
| 3446 | Args: |
| 3447 | dy (CTensor): the gradient tensor from upper operations |
| 3448 | Returns: |
| 3449 | a tuple for (*dx), dx is data for dL / dx. |
| 3450 | """ |
| 3451 | if self.l == 1: |
| 3452 | return self.masks[0][0] |
| 3453 | else: |
| 3454 | ret = [] |
| 3455 | cumulation = None |
| 3456 | for mask0, mask1 in self.masks[::-1]: |