You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

BroadcastShape.py 4.7 kB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from __future__ import absolute_import
  2. import numpy as np
  3. from .Node import Op
  4. from .ReduceSum import reduce_sum_op
  5. from ..gpu_links import broadcast_shape_simple
  6. from .. import ndarray
  7. class BroadcastShapeOp(Op):
  8. def __init__(self, node_A, shape, add_axes=(), ctx=None):
  9. super().__init__(BroadcastShapeOp, [node_A], ctx)
  10. self.target_shape = shape
  11. self.add_axes = add_axes
  12. def compute(self, input_vals, output_val, stream_handle=None):
  13. assert self.target_shape is not None and self.add_axes is not None
  14. if self.on_cpu:
  15. input_shape = list(input_vals[0].shape)
  16. for i in range(len(input_shape)):
  17. if self.add_axes and i in self.add_axes:
  18. input_shape[i] = 1
  19. output_val[:] = np.broadcast_to(
  20. input_vals[0].asnumpy().reshape(input_shape), self.target_shape)
  21. else:
  22. if self.inplace:
  23. input_vals[0].broadcast_to(
  24. self.target_shape, output_val, self.add_axes)
  25. else:
  26. # broadcast_shape(input_vals[0], output_val, self.add_axes, stream_handle)
  27. broadcast_shape_simple(
  28. input_vals[0], output_val, self.out_strides, self.in_dims, stream_handle)
  29. def gradient(self, output_grad):
  30. self.grad_node = reduce_sum_op(
  31. output_grad, None, None, ctx=self.raw_ctx)
  32. return [self.grad_node]
  33. def infer_shape(self, input_shapes):
  34. assert self.target_shape is not None and self.add_axes is not None
  35. assert len(input_shapes) == 1
  36. input_shape = list(input_shapes[0])
  37. output_shape = list(self.target_shape)
  38. output_ndim = len(output_shape)
  39. assert len(input_shape) <= output_ndim
  40. diff = output_ndim - len(input_shape)
  41. if self.add_axes:
  42. assert diff == len(self.add_axes) or input_shape == [1]
  43. assert all([axis < output_ndim for axis in self.add_axes])
  44. in_ind = 0
  45. for i in range(output_ndim):
  46. if i not in self.add_axes:
  47. assert input_shape[in_ind] == output_shape[i]
  48. in_ind += 1
  49. if hasattr(self, 'grad_node'):
  50. self.grad_node.axes = tuple(self.add_axes)
  51. self.grad_node.axes.keepdims = [False] * len(self.add_axes)
  52. else:
  53. axes = list(range(diff))
  54. keepdims = [False] * diff
  55. input_shape = [1] * diff + input_shape
  56. for i in range(output_ndim):
  57. if output_shape[i] == -1:
  58. output_shape[i] = input_shape[i]
  59. assert output_shape[i] > 0 and isinstance(output_shape[i], int)
  60. assert input_shape[i] == 1 or input_shape[i] == output_shape[i]
  61. if i >= diff and input_shape[i] == 1 and output_shape[i] > 1:
  62. axes.append(i)
  63. keepdims.append(True)
  64. if hasattr(self, 'grad_node'):
  65. self.grad_node.axes = axes
  66. self.grad_node.keepdims = keepdims
  67. # here we save the output strides and input dimensions for GPU computation
  68. if self.on_gpu and not self.inplace:
  69. input_shape = list(input_shapes[0])
  70. out_strides = [0 for _ in range(output_ndim)]
  71. temp_size = 1
  72. for i in range(output_ndim - 1, -1, -1):
  73. out_strides[i] = temp_size
  74. temp_size *= output_shape[i]
  75. if self.add_axes:
  76. in_dims = [0 for _ in range(output_ndim)]
  77. for i in range(diff):
  78. in_dims[self.add_axes[i]] = 1
  79. temp_ind = 0
  80. for dim in input_shape:
  81. while in_dims[temp_ind] == 1:
  82. temp_ind += 1
  83. in_dims[temp_ind] = dim
  84. else:
  85. in_dims = [1 for _ in range(diff)] + input_shape
  86. self.out_strides = ndarray.array(
  87. out_strides, self.ctx, data_type=np.int32)
  88. self.in_dims = ndarray.array(in_dims, self.ctx, data_type=np.int32)
  89. return tuple(output_shape)
  90. def backward_hook(self, config):
  91. self.inplace = config.enable_lazy and self not in config.eval_node_list
  92. def broadcast_shape_op(node_A, shape, add_axes=(), ctx=None):
  93. """Creates a node that represents np.broadcast_to(node_A, shape).
  94. Parameters:
  95. ----
  96. node_a : Node
  97. The Node to be broadcast.
  98. shape : tuple
  99. Target shape.
  100. Returns:
  101. ----
  102. A new Node instance created by Op.
  103. """
  104. return BroadcastShapeOp(node_A, shape, add_axes=add_axes, ctx=ctx)