def _forward(self, arguments, outputs, device=None, outputs_to_retain=None): ''' Computes the values of speficied variables in ``outputs``, using values provided in ``arguments`` that correspond to each input `Variable` of the function whose ``is_input`` is `True`. This function calls :func:`forward`, which is to be implemented by the user. Args: arguments (tuple): Value objects of the Function's input outputs (iterable): outputs to fetch values for. device (:class:`~cntk.device.DeviceDescriptor`, default `None`): the device descriptor that contains the type and id of the device on which the computation is. If `None`, the default device is used. Returns: A BackPropState instance, which is used by :func:`backward`. ''' arguments = tuple(value_to_seq(v) for v in arguments) map_if_possible(outputs) map_if_possible(outputs_to_retain) args = arguments if len(arguments) > 1 else arguments[0] if len(outputs) <= 1: state, result = self.forward(args, device, outputs_to_retain) for k in outputs: outputs[k] = result else: state = self.forward(args, outputs, device, outputs_to_retain) if not isinstance(state, cntk_py.BackPropState): state = cntk_py.UserBackPropState(self, device, state) for k, v in outputs.items(): if v is None: raise ValueError('not all outputs have been provided') # FIXME: seq_starts outputs[k] = sanitize_batch(k, v, None, device) return state, outputs
def _backward(self, state, root_gradients, variables): ''' Backpropagates supplied ``root_gradients`` for one or more of the output variables of the Function, to calculate gradients with respect to ``variables``. Formally, multiplies the values of ``root_gradients`` by the Jacobian of the Function and returns the subset of the output that corresponds to ``variables``. This function calls :func:`backward`, which is to be implemented by the user. Example: TBD Args: state (BackPropState): state obtained from a previous call to the func:`cntk.ops.Function.forward` method on this Function for the computation that this gradient backpropagation corresponds to. root_gradients (dict): the gradients that will be backpropagated variables (set): a list of input variables with respect to which the gradients have to be computed. Returns: dict: mapping of ``variables`` to NumPy arrays ''' for v in root_gradients: root_gradients[v] = value_to_seq(root_gradients[v]) map_if_possible(variables) if len(variables) > 1: self.backward(cntk_py.UserBackPropState.data(state), root_gradients, variables) else: for rg in root_gradients.values(): break result = self.backward(cntk_py.UserBackPropState.data(state), rg) for k in variables: variables[k] = result for k, v in variables.items(): if v is None: raise ValueError( 'gradients were not provided for all variables') variables[k] = sanitize_batch(k, v, None, state.device())
def backward(self, state, root_gradients, variables): ''' Backpropagates supplied ``root_gradients`` for one or more of the output variables of the Function, to calculate gradients with respect to ``variables``. Formally, multiplies the values of ``root_gradients`` by the Jacobian of the Function and returns the subset of the output that corresponds to ``variables``. Example: >>> # compute the value and the derivative of the sigmoid at 0 >>> v = C.input_variable(shape=(1,), needs_gradient=True) >>> f = C.sigmoid(v) >>> df, fv = f.forward({v:[[0]]}, [f.output], set([f.output])) >>> value = list(fv.values())[0] >>> grad = f.backward(df, {f.output: np.ones_like(value)}, set([v])) >>> value array([[[ 0.5]]], dtype=float32) >>> list(grad.values())[0] array([[[ 0.25]]], dtype=float32) Args: state (BackPropState): state obtained from a previous call to the func:`cntk.ops.Function.forward` method on this Function for the computation that this gradient backpropagation corresponds to. root_gradients (dict): the gradients that will be backpropagated variables (set): a list of input variables with respect to which the gradients have to be computed. Returns: dict: mapping of ``variables`` to NumPy arrays ''' device = state.device() root_gradients = sanitize_var_map(self.outputs, root_gradients, None, device) var_gradients = dict((var, None) for var in variables) self._backward(state, root_gradients, var_gradients) for var, value in var_gradients.items(): var_gradients[var] = value_to_seq(value) return var_gradients
def backward(self, state, root_gradients, variables): """ Backpropagates supplied ``root_gradients`` for one or more of the output variables of the Function, to calculate gradients with respect to ``variables``. Formally, multiplies the values of ``root_gradients`` by the Jacobian of the Function and returns the subset of the output that corresponds to ``variables``. Example: >>> # compute the value and the derivative of the sigmoid at 0 >>> v = C.input_variable(shape=(1,), needs_gradient=True) >>> f = C.sigmoid(v) >>> df, fv = f.forward({v:[[0]]}, [f.output], set([f.output])) >>> value = list(fv.values())[0] >>> grad = f.backward(df, {f.output: np.ones_like(value)}, set([v])) >>> value array([[[ 0.5]]], dtype=float32) >>> list(grad.values())[0] array([[[ 0.25]]], dtype=float32) Args: state (BackPropState): state obtained from a previous call to the func:`cntk.ops.Function.forward` method on this Function for the computation that this gradient backpropagation corresponds to. root_gradients (dict): the gradients that will be backpropagated variables (set): a list of input variables with respect to which the gradients have to be computed. Returns: dict: mapping of ``variables`` to NumPy arrays """ device = state.device() root_gradients = sanitize_var_map(self.outputs, root_gradients, None, device) var_gradients = dict((var, None) for var in variables) self._backward(state, root_gradients, var_gradients) for var, value in var_gradients.items(): var_gradients[var] = value_to_seq(value) return var_gradients
def forward(self, arguments, outputs, keep_for_backward=None, device=None): ''' Computes the values of speficied variables in ``outputs``, using values provided in ``arguments`` that correspond to each input `Variable` of the function whose ``is_input`` is `True`. Example: >>> v = C.input_variable(shape=(3,)) >>> f = C.reciprocal(v) >>> _, fv = f.forward({v:[[1, 2, 4]]}, [f.output]) >>> list(fv.values())[0] array([[[ 1. , 0.5 , 0.25]]], dtype=float32) Args: arguments: maps variables to their input data. The interpretation depends on the input type: * dict: keys are input variable or names, and values are the input data. To specify a minibatch, provide a list of arrays. The shape of each array must be compatible with the shape of the dictionary key.If the array denotes a sequence then the elements of the sequence are grouped along axis 0. * any other type: if node has an unique input, arguments is mapped to this input. For nodes with more than one input, only dict is allowed. In both cases, every every sample in the data will be interpreted as a new sequence. Sequences can be marked as continuations of the same sequence in the previous minibatch (that is the sequence in the same slot). There are two possibilities for this: * specifying arguments as a `tuple` where the first element is used as arguments and the second one will be used as a list of bools, denoting whether a sequence is a new one (`True`) or a continuation of the sequence in the same slot of the previous minibatch (`False`). This will be applied to all batches. * specifying arguments as a dictionary of variables to tuples where the first element is used as arguments and the second one will be used as a list of bools, denoting whether a sequence is a new one (`True`) or a continuation of the sequence in the same slot of the previous minibatch (`False`). This will be applied to all batches. Data should be either NumPy arrays or a :class:`~cntk.io.MinibatchData` instance. outputs (iterable): outputs to fetch values for. keep_for_backward (set, default `None`): the subset of the Function's output variables for which gradients shall be calculated in a subsequent backward call. If `None`, the returned state will be `None` and a subsequent call to :func:`backward` will not be possible. device (:class:`~cntk.device.DeviceDescriptor`, default `None`): the device descriptor that contains the type and id of the device on which the computation is. If `None`, the default device is used. Returns: A tuple (BackpropState, map of outputs to NumPy arrays). The BackpropState is a handle taken by :func:`backward`. ''' if device is None: device = DeviceDescriptor.use_default_device() in_var_map = sanitize_var_map(self.arguments, arguments, None, device) output_map = {v: None for v in outputs} keep_for_backward = set(keep_for_backward or {}) state = super(Function, self)._forward(in_var_map, output_map, device, keep_for_backward) for k in output_map: output_map[k] = value_to_seq(output_map[k]) return state, output_map
def forward(self, arguments, outputs, keep_for_backward=None, device=None): """ Computes the values of speficied variables in ``outputs``, using values provided in ``arguments`` that correspond to each input `Variable` of the function whose ``is_input`` is `True`. Example: >>> v = C.input_variable(shape=(3,)) >>> f = C.reciprocal(v) >>> _, fv = f.forward({v:[[1, 2, 4]]}, [f.output]) >>> list(fv.values())[0] array([[[ 1. , 0.5 , 0.25]]], dtype=float32) Args: arguments: maps variables to their input data. The interpretation depends on the input type: * dict: keys are input variable or names, and values are the input data. To specify a minibatch, provide a list of arrays. The shape of each array must be compatible with the shape of the dictionary key.If the array denotes a sequence then the elements of the sequence are grouped along axis 0. * any other type: if node has an unique input, arguments is mapped to this input. For nodes with more than one input, only dict is allowed. In both cases, every every sample in the data will be interpreted as a new sequence. Sequences can be marked as continuations of the same sequence in the previous minibatch (that is the sequence in the same slot). There are two possibilities for this: * specifying arguments as a `tuple` where the first element is used as arguments and the second one will be used as a list of bools, denoting whether a sequence is a new one (`True`) or a continuation of the sequence in the same slot of the previous minibatch (`False`). This will be applied to all batches. * specifying arguments as a dictionary of variables to tuples where the first element is used as arguments and the second one will be used as a list of bools, denoting whether a sequence is a new one (`True`) or a continuation of the sequence in the same slot of the previous minibatch (`False`). This will be applied to all batches. Data should be either NumPy arrays or a :class:`~cntk.io.MinibatchData` instance. outputs (iterable): outputs to fetch values for. keep_for_backward (set, default `None`): the subset of the Function's output variables for which gradients shall be calculated in a subsequent backward call. If `None`, the returned state will be `None` and a subsequent call to :func:`backward` will not be possible. device (:class:`~cntk.device.DeviceDescriptor`, default `None`): the device descriptor that contains the type and id of the device on which the computation is. If `None`, the default device is used. Returns: A tuple (BackpropState, map of outputs to NumPy arrays). The BackpropState is a handle taken by :func:`backward`. """ if device is None: device = DeviceDescriptor.use_default_device() in_var_map = sanitize_var_map(self.arguments, arguments, None, device) output_map = {v: None for v in outputs} keep_for_backward = set(keep_for_backward or {}) state = super(Function, self)._forward(in_var_map, output_map, device, keep_for_backward) for k in output_map: output_map[k] = value_to_seq(output_map[k]) return state, output_map