Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...



target_dtype should decide which lists need to be overridden.
For example, in the future bfloat16 support may be added in which case these lists for operators running in bfloat16 will also be added to AMP.
In this case, target_dtype will allow users to choose the right dtype for the mixed precision model.


Code Block
languagepy
def convert_hybrid_block(block, target_dtype="float16", target_dtype_ops=None,
                         fp32_ops=None, widest_dtype_ops=None, conditional_fp32_ops=None,
                         excluded_sym_names=None, input_names=['data']):
    """Given a hybrid block/symbol block representing a neural network of data type FP32 and target_dtype,
    return a block which will add mixed precision support for the block

    Parameters
    ----------
    block : HybridBlock or SymbolBlock object
        FP32 HybridBlock or SymbolBlock object
    target_dtype : str or numpy
        currently only supports float16. The target dtype indicates to add cast layers
        when possible so that lower precision computation can be leveraged.
    target_precision_ops : list of strs
        Override the list of operator names casted to target_dtype.
        If None, uses the framework's default list to be casted to target dtype.
    fp32_ops : list of strs
        Override the lists of operator names casted to FP32.
        If None, uses the framework's default list to be casted to FP32.
    widest_precision_ops : list of strs
        Override the list of operator names which should run in widest precision among its
        input arguments.
        If None, uses the framework's default list of widest_precision_ops.
    conditional_fp32_ops : list of (string, string, list of string)
        Override the list of functions casted to FP32.
        The format of the list is
        (name of the function, name of the parameter,
         list of values of the parameter that make the operator to be casted to
        fp32)
    excluded_sym_names : list of strs
        A list of strings that represent the names of symbols that users want to exclude
        from being quantized.
    input_names : list of strs
        A list of strings representing the names of input variables

User experience will be similar to the export API experience today. Users will have to call hybridize followed by one forward pass before calling convert_model.

Backend Changes

NNVM Pass

Add a NNVM pass for the backend. This would use the amp lists based on the original_dtype and target_dtype.
This pass will perform graph traversal and add amp_cast and amp_multicast layers for FP16 and FP32 ops based on the op whitelists and excluded_sym_names. Some of the ideas have been borrowed from quantization pass added as part of quantization support [2].

...

  1. map from a node to a copy node in the casted graph (mirror_map)
  2. map from an input entry to the corresponding fp32 casted entry (mirror_entry_fp32_map)
  3. map from an input entry to the target dtype (e.g. fp16) casted entry (mirror_entry_target_map) (please see below fig for why b 2 and c 3 are needed)

Consider the below script:

...

Without the mirror_entry_target_map there would have been 3 cast nodes instead of 2: 1 for amp_cast float16 and two others going as amp_casted fp32 input to exp0 and sqrt0. Thus, the two additional data structures help optimize and reuse such commonly used cast operatorsshare common cast node inputs among different nodes.


2. Visit nodes of the graph in a topologically sorted order and when each node is visited do the following:

  1. Create a copy node
  2. Clear inputs of the copy node
  3.  
    1. If node is a variable:
      1. Add mapping of the node to the copy node in mirror_map.
    2. Else if node is not in any whitelist:
      1. Find all inputs of original node and find corresponding mirror entry and add as inputs to the copy node.
    3. Else if node's op_name is in the fp32 whitelist:
      1. Iterate through inputs: If input is already in mirror_entry_fp32_map, add it the mapped node to the copy node inputs.                                                                          If If not, insert a cast node between copy_node and mirror node of previous node.                                                                  Add Add mapping from input node to fp32 cast node in mirror_entry_fp32_map.
    4. Else if node's op_name is in the target dtype whitelist:
      1. Iterate through inputs:
        1. If input is already in mirror_entry_target_map, add it to the copy node inputs.
        2. If not, insert a cast node between copy_node and mirror_node of previous node. Add mapping from input node to target dtype cast node in mirror_entry_target_map.
      Else if node's op_name is in the target dtype whitelist:
      1. Iterate through inputs:
        1. If input is already in mirror_entry_target_map, add it the mapped node to the copy node inputs.
        2. If not, insert a cast node between copy_node and mirror_node of previous node. Add mapping from input node to target dtype cast node in mirror_entry_target_map.
    5. Else if node's op_name is in the widest_precision_ops whitelist:
      1. Add amp_multicast between mirror of node's inputs and copy of current node.
  4. Add mapping from node to copy node in mirror_map.

3. Create a new graph using the copy nodes with the node to copy node mapping.obtained from mirror_map. 

4. Return the newly created graph. 

...

As you can see above the converted graph has amp_cast, amp_multicast nodes which allow for appropriate casting of inputs.


Symbol Changes

After mixed precision pass is done and amp_cast and amp_multicast layers are added, the symbolic representation needs to be modified to store the right dtype attrs for some of its inputs. This will require running InferType after the NNVM pass and then using the obtained information to set the data types of inputs.

This will ensure that the dtype corresponding to each param or aux input will have the right dtype, by casting the arg_params and aux_params accordingly inside convert_model.


Gluon Changes

For Gluon code, we need to add an internal API to retrieve sym, arg_params and aux_params from a hybrid_block. Following this, convert_model can be used to convert a symbol json, model params and auxiliary params. After conversion model (json, arg_params, aux_params).


Frontend Bindings

Need to add amp convert_model API support for different bindings like C++, Scala etc. 

FAQ

Will the arg_params and aux_params be casted to fp16 ?

Depends on the whitelists provided. The default whitelists have been selected in a way to avoid casting of the params, for commonly used convnet networks. If the whitelist is such that the type inference decides that certain param needs to be float16 then it will be casted.

Gluon User Experience Improvement

How is this different from casting inputs to FP16 and casting params to FP16 in Gluon ?

Casting inputs to FP16 and params to FP16 for gluon ensures that you are able to execute the model in FP16 precision. Generally, there may be some ops which may need to run in FP16 while other in FP32 for accuracy and performance considerations. This is where the AMP APIs will be useful.

Will the dtype attribute in the serialized model change after convert_model is called ?

Yes dtype attribute in the serialized model can change after convert_model is called. This depends on how the whitelist affects the model in question and if the type inference decides that certain params needs to be in flaot16.

Is there a need for hybridizing and running a forward pass for the AMP converted gluon model ?

No there is no need to hybridize since it will return SymbolBlocks which are already hybridized.TO BE ADDED

References

  1. https://github.com/apache/incubator-mxnet/pull/14173
  2. https://github.com/apache/incubator-mxnet/pull/9552