| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| """Utils for training models in low precision.""" |
|
|
| import functools |
| from typing import Callable, Tuple, Union |
|
|
| import jax |
| import jax.numpy as jnp |
|
|
|
|
| |
| @functools.partial(jax.custom_vjp, nondiff_argnums=(1, 2)) |
| def reduce_precision(x, exponent_bits, mantissa_bits): |
| return jax.tree_util.tree_map( |
| lambda y: jax.lax.reduce_precision(y, exponent_bits, mantissa_bits), x) |
|
|
|
|
| def reduce_precision_fwd(x, exponent_bits, mantissa_bits): |
| return reduce_precision(x, exponent_bits, mantissa_bits), None |
|
|
|
|
| def reduce_precision_bwd(exponent_bits, mantissa_bits, res, dout): |
| del res |
| return reduce_precision(dout, exponent_bits, mantissa_bits), |
|
|
|
|
| reduce_precision.defvjp(reduce_precision_fwd, reduce_precision_bwd) |
|
|
|
|
| def wrap_fn_for_upcast_downcast(inputs: Union[jnp.ndarray, |
| Tuple[jnp.ndarray, ...]], |
| fn: Callable[[Union[jnp.ndarray, |
| Tuple[jnp.ndarray, ...]]], |
| Union[jnp.ndarray, |
| Tuple[jnp.ndarray, ...]]], |
| f32_upcast: bool = True, |
| guard_against_excess_precision: bool = True |
| ) -> Union[jnp.ndarray, |
| Tuple[jnp.ndarray, ...]]: |
| """Wraps `fn` to upcast to float32 and then downcast, for use with BF16.""" |
| |
| |
| |
| if isinstance(inputs, Tuple): |
| f32_upcast = f32_upcast and inputs[0].dtype != jnp.float32 |
| orig_dtype = inputs[0].dtype |
| else: |
| f32_upcast = f32_upcast and inputs.dtype != jnp.float32 |
| orig_dtype = inputs.dtype |
|
|
| if f32_upcast: |
| inputs = jax.tree_util.tree_map(lambda x: x.astype(jnp.float32), inputs) |
|
|
| if guard_against_excess_precision: |
| |
| |
| |
| finfo = jnp.finfo(orig_dtype) |
| inputs = reduce_precision(inputs, finfo.nexp, finfo.nmant) |
|
|
| output = fn(inputs) |
| if f32_upcast: |
| output = jax.tree_util.tree_map(lambda x: x.astype(orig_dtype), output) |
| return output |
|
|