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.

function.py 5.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # -*- coding: utf-8 -*-
  2. # MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  3. #
  4. # Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
  5. #
  6. # Unless required by applicable law or agreed to in writing,
  7. # software distributed under the License is distributed on an
  8. # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. from abc import ABCMeta, abstractmethod
  10. from typing import Iterable, Tuple, Union
  11. import megengine._internal as mgb
  12. from .tensor import Tensor
  13. class _OverrideGradientCraniotome(mgb.craniotome.CraniotomeBase):
  14. __nr_inputs__ = None
  15. __nr_outputs__ = None
  16. __expand_single_outputs__ = False
  17. __allow_duplicate__ = False
  18. grad_func = None
  19. def setup(self, nr_inputs, nr_outputs, grad_func):
  20. self.__nr_inputs__ = nr_inputs + nr_outputs
  21. self.__nr_outputs__ = nr_outputs
  22. self.grad_func = grad_func
  23. def infer_shape(self, inp_shapes):
  24. return inp_shapes[-self.__nr_outputs__ :]
  25. def init_output_dtype(self, input_dtypes):
  26. return input_dtypes[-self.__nr_outputs__ :]
  27. def execute(self, inputs, outputs):
  28. for ivar, ovar in zip(inputs[-self.__nr_outputs__ :], outputs):
  29. ovar.set_value(ivar)
  30. def grad(self, wrt_idx, inputs, outputs, out_grad):
  31. # TODO: Make sure grad_values really have values in eager mode.
  32. # Porting to the new imperative engine would solve this, but if it
  33. # don't happen, EagerEvalManager should be changed.
  34. grads = self.grad_func(
  35. *(Tensor(x) if x is not None else None for x in out_grad)
  36. )
  37. # pylint: disable=literal-comparison
  38. if isinstance(grads, Tensor) or grads is None or grads is 0:
  39. grads = (grads,)
  40. assert (
  41. len(grads) == self.__nr_inputs__ - self.__nr_outputs__
  42. ), "Function.backward should return a tuple with len = {}, got {}".format(
  43. self.__nr_inputs__ - self.__nr_outputs__, len(grads)
  44. )
  45. # pylint: disable=literal-comparison
  46. return (
  47. list(x._symvar if x is not None and x is not 0 else 0 for x in grads)
  48. + [0] * self.__nr_outputs__
  49. )
  50. def get_serialize_params(self):
  51. raise NotImplementedError("Serialization of Function is not implemented")
  52. class Function(metaclass=ABCMeta):
  53. """
  54. Defines a block of operations with customizable differentiation.
  55. The computation should be defined in ``forward`` method, with gradient
  56. computation defined in ``backward`` method.
  57. Each instance of ``Function`` should be used only once during forwardding.
  58. Examples:
  59. .. testcode::
  60. class Sigmoid(Function):
  61. def forward(self, x):
  62. y = 1 / (1 + F.exp(-x))
  63. self.save_for_backward(y)
  64. return y
  65. def backward(self. output_grads):
  66. (y, ) = self.saved_tensors
  67. return output_grads * y * (1-y)
  68. """
  69. _has_saved_state = False
  70. saved_tensors = None
  71. def __init__(self):
  72. self.saved_tensors = ()
  73. @abstractmethod
  74. def forward(self, *inputs: Iterable[Tensor]) -> Union[Tuple[Tensor], Tensor]:
  75. """
  76. Applies operations to ``inputs`` and returns results. It must be overriden by all subclasses.
  77. Users can call :meth:`~.function.Function.save_for_backward` in this method to save tensors.
  78. :param input: Input tensors.
  79. :return: A tuple of Tensor or a single Tensor.
  80. .. note::
  81. This method should return a tuple of Tensor or a single Tensor representing the output
  82. of the function.
  83. """
  84. raise NotImplementedError
  85. @abstractmethod
  86. def backward(
  87. self, *output_grads: Iterable[Union[Tensor, None]]
  88. ) -> Union[Tuple[Tensor], Tensor]:
  89. """
  90. Compute the gradient of the forward function. It must be overriden by all subclasses.
  91. :param output_grads: gradients of outputs that are returned by :meth:`~.function.Function.forward`
  92. .. note::
  93. In case when some tensors of outputs are not related to loss function, the corresponding
  94. values in ``output_grads`` would be ``None``.
  95. .. note::
  96. This method should return a tuple which containing the gradients of all inputs, in the same order
  97. as the ``inputs`` argument of :meth:`~.function.Function.forward` . A ``Tensor`` could be returned
  98. instead if there is only one input. If users want to stop the propagation of some gradients,
  99. the corresponding returned values should be set ``None`` .
  100. """
  101. raise NotImplementedError
  102. def save_for_backward(self, *tensors: Iterable[Tensor]):
  103. """
  104. Saves tensors needed for gradient computation. This method should be called only
  105. once in :meth:`~.function.Function.forward`, additional calls will replace values saved previously.
  106. The saved tensors can be accessed through the ``saved_tensors`` attribute.
  107. """
  108. self.saved_tensors = tensors
  109. def __call__(self, *inputs):
  110. assert (
  111. not self._has_saved_state
  112. ), "A Function instance should not be called multiple times"
  113. outputs = self.forward(*inputs)
  114. if isinstance(outputs, Tensor):
  115. outputs = (outputs,)
  116. self._has_saved_state = True
  117. sv = (x._symvar for x in inputs + outputs)
  118. outputs = _OverrideGradientCraniotome.make(
  119. *sv, nr_inputs=len(inputs), nr_outputs=len(outputs), grad_func=self.backward
  120. )
  121. outputs = tuple(map(Tensor, outputs))
  122. if len(outputs) == 1:
  123. outputs = outputs[0]
  124. return outputs

MegEngine 安装包中集成了使用 GPU 运行代码所需的 CUDA 环境,不用区分 CPU 和 GPU 版。 如果想要运行 GPU 程序,请确保机器本身配有 GPU 硬件设备并安装好驱动。 如果你想体验在云端 GPU 算力平台进行深度学习开发的感觉,欢迎访问 MegStudio 平台