latest (dev)
Copy
Latest development documentation · Updated 2026-10-08
Source code for tensorplay.distributed.tensor.parallel.loss
"""Distributed cross-entropy execution for class-sharded logits."""
from __future__ import annotations
import contextlib
from typing import Any, cast
import tensorplay as tp
from tensorplay.primitives.common import (
ELEMENTWISE_TYPE_PROMOTION_KIND,
elementwise_dtypes,
)
from tensorplay.autograd.function import Function
from ... import _functional_collectives as funcol
from .._api import DTensor
from .._dtensor_spec import TensorMeta
from .._ops._embedding_ops import _MaskPartial
from .._ops._math_ops import Reduction, _skip_dim, replicate_reduction_dims
from .._ops.utils import normalize_dim
from ..placement_types import Partial, Placement, Replicate, Shard
__all__ = ["loss_parallel"]
def _mesh_ndim(mesh: Any) -> int:
value = getattr(mesh, "ndim")
return int(value() if callable(value) else value)
def _reduction_name(value: Any) -> str:
if isinstance(value, Reduction):
name = value.value
elif isinstance(value, str):
name = value
elif type(value) is int:
name = {0: "none", 1: "mean", 2: "sum"}.get(value, "")
else:
name = ""
if name not in {"none", "mean", "sum"}:
raise ValueError(f"unsupported loss reduction: {value!r}")
return name
def _find_all_reduce_mesh_dim(
placements: tuple[Placement, ...], dim: int
) -> int:
shard_mesh_dims = [
index
for index, placement in enumerate(placements)
if isinstance(placement, Shard) and placement.dim == dim
]
if len(shard_mesh_dims) != 1:
raise ValueError(
"loss_parallel requires exactly one mesh dimension to shard "
f"tensor dimension {dim}; got {placements}"
)
mesh_dim = shard_mesh_dims[0]
for index, placement in enumerate(placements):
if index != mesh_dim and not isinstance(placement, (Shard, Replicate)):
raise ValueError(
"loss_parallel accepts only Shard or Replicate on non-class "
f"mesh dimensions; got {placement} at {index}"
)
return mesh_dim
def _cast_to_dtensor(
tensor: Any, placements: tuple[Placement, ...], mesh: Any
) -> DTensor:
if isinstance(tensor, DTensor):
if tensor.placements == placements:
return tensor
raise RuntimeError(
f"expected placements {placements}, got {tensor.placements}"
)
if isinstance(tensor, tp.Tensor):
if any(isinstance(placement, Shard) for placement in placements):
raise ValueError(
"a plain tensor cannot represent a sharded loss operand; "
f"use a distributed tensor for placements {placements}"
)
return DTensor.from_local(
tensor, device_mesh=mesh, placements=placements, run_check=False
)
raise TypeError(f"unsupported loss operand type: {type(tensor)!r}")
def _propagate_tensor_meta(
operation: Any, args: tuple[Any, ...], kwargs: dict[str, Any]
) -> TensorMeta | None:
try:
op_info = DTensor._op_dispatcher.unwrap_to_op_info(
operation, args, kwargs
)
schema = op_info.schema
if schema is None:
return None
tensor_meta = (
DTensor._op_dispatcher.sharding_propagator._propagate_tensor_meta(
schema
)
)
except (AttributeError, TypeError, ValueError, RuntimeError):
return None
if isinstance(tensor_meta, TensorMeta):
return tensor_meta
if isinstance(tensor_meta, (tuple, list)) and tensor_meta:
first = tensor_meta[0]
if isinstance(first, TensorMeta):
return first
return None
def _log_softmax(
value: Any,
dim: int,
half_to_float: bool,
requested_dtype: Any,
mesh: Any,
mesh_dim: int,
) -> Any:
computation_dtype, result_dtype = elementwise_dtypes(
value,
type_promotion_kind=ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT,
)
if requested_dtype is not None:
result_dtype = requested_dtype
value = value.to(dtype=requested_dtype)
value = value.to(dtype=computation_dtype).contiguous()
if value.numel() == 0:
shifted = value
else:
value_max = tp.amax(value, dim, keepdim=True)
value_max = funcol.all_reduce(
value_max, reduce_op="max", group=(mesh, mesh_dim)
)
value_max = funcol.wait_tensor(value_max)
shifted = value - value_max
sum_exp = tp.sum(tp.exp(shifted), dim, keepdim=True)
sum_exp = funcol.all_reduce(
sum_exp, reduce_op="sum", group=(mesh, mesh_dim)
)
sum_exp = funcol.wait_tensor(sum_exp)
result = shifted - tp.log(sum_exp)
if not half_to_float:
result = result.to(dtype=result_dtype)
return result
def _log_softmax_handler(
operation: Any, args: tuple[Any, ...], kwargs: dict[str, Any]
) -> DTensor:
value = cast(DTensor, args[0])
dim = normalize_dim(cast(int, args[1]), value.ndim)
option = args[2] if len(args) > 2 else tp.undefined
if isinstance(option, bool):
half_to_float = option
requested_dtype = None
else:
half_to_float = False
requested_dtype = None if option in (None, tp.undefined) else option
spec = DTensor._op_dispatcher._spec_from_dtensor(value)
mesh_dim = _find_all_reduce_mesh_dim(spec.placements, dim)
result = _log_softmax(
value.to_local(),
dim,
half_to_float,
requested_dtype,
spec.mesh,
mesh_dim,
)
output_meta = _propagate_tensor_meta(operation, args, kwargs)
if output_meta is None:
output_meta = TensorMeta(value.shape, value.stride(), result.dtype)
else:
output_meta = TensorMeta(
output_meta.shape, output_meta.stride, result.dtype
)
return DTensor(
result,
spec.mesh,
spec.placements,
shape=output_meta.shape,
stride=output_meta.stride,
)
def _log_softmax_backward_handler(
operation: Any, args: tuple[Any, ...], kwargs: dict[str, Any]
) -> DTensor:
del operation, kwargs
grad_output = cast(DTensor, args[0])
input_dtype = args[3]
return grad_output.to(input_dtype)
def _nll_loss_forward(
value: Any,
target: Any,
weight: Any,
local_weight: Any,
reduction: str,
ignore_index: int,
input_shape: tuple[int, ...],
channel_dim: int,
mesh: Any,
mesh_dim: int,
) -> tuple[Any, Any]:
ndim = int(value.dim())
def weight_view(current: Any) -> Any:
if ndim > 1:
shape = [1] * ndim
shape[channel_dim] = current.shape[0]
return current.view(shape)
return current
if weight is not None:
if local_weight is None:
raise AssertionError("local class weights are required")
value = value * weight_view(local_weight)
safe_target = tp.where(target != ignore_index, target, 0)
safe_target_with_dim = safe_target.unsqueeze(channel_dim)
partial = _MaskPartial(
offset_shape=input_shape,
offset_dim=channel_dim,
)
partitioned_target = partial._partition_value(
safe_target_with_dim, mesh, mesh_dim
)
selected = tp.gather(value, channel_dim, partitioned_target)
reduced = partial._reduce_value(selected, mesh, mesh_dim)
reduced = funcol.wait_tensor(reduced)
result = -reduced.squeeze(channel_dim)
result = tp.where(target != ignore_index, result, 0)
if reduction == "none" and ndim > 1:
return result, value.new_full((), 0.0)
if weight is not None:
full_weight = weight_view(weight)
shape = list(value.shape)
shape[channel_dim] = -1
expanded_weight = full_weight.expand(shape)
weight_sum = tp.gather(
expanded_weight, channel_dim, safe_target_with_dim
).squeeze(channel_dim)
weight_sum = tp.where(target != ignore_index, weight_sum, 0)
total_weight = weight_sum.sum()
else:
total_weight = (target != ignore_index).sum().to(
dtype=value.dtype, device=value.device
)
if reduction == "sum":
result = result.sum()
elif reduction == "mean":
result = result.sum() / total_weight
return result, total_weight
def _output_meta(
meta: TensorMeta | None,
result: Any,
target: DTensor,
reduction: str,
input_ndim: int,
) -> TensorMeta:
if meta is not None:
return TensorMeta(meta.shape, meta.stride, result.dtype)
if reduction == "none" and input_ndim > 1:
return TensorMeta(target.shape, target.stride(), result.dtype)
return TensorMeta((), (), result.dtype)
def _nll_loss_forward_handler(
operation: Any, args: tuple[Any, ...], kwargs: dict[str, Any]
) -> tuple[DTensor, Any]:
value = cast(DTensor, args[0])
target_value = args[1]
weight_value = args[2]
reduction = _reduction_name(args[3])
ignore_index = int(args[4])
channel_dim = 1 if value.ndim >= 2 else 0
spec = DTensor._op_dispatcher._spec_from_dtensor(value)
mesh_dim = _find_all_reduce_mesh_dim(spec.placements, channel_dim)
target_placements = _skip_dim(
replicate_reduction_dims(spec.placements, [channel_dim]), channel_dim
)
all_replicate = (Replicate(),) * _mesh_ndim(spec.mesh)
target = _cast_to_dtensor(target_value, target_placements, spec.mesh)
weight = (
None
if weight_value is None
else _cast_to_dtensor(weight_value, all_replicate, spec.mesh)
)
local_weight = None
if weight is not None:
sharded_weight_placements = tuple(
Shard(0) if index == mesh_dim else Replicate()
for index in range(_mesh_ndim(spec.mesh))
)
local_weight = weight.redistribute(
spec.mesh, sharded_weight_placements
).to_local()
if local_weight.shape[0] != value.to_local().shape[channel_dim]:
raise AssertionError("class weight shape does not match logits")
if reduction == "none":
output_placements = target_placements
else:
if reduction == "mean" and _mesh_ndim(spec.mesh) > 1:
raise NotImplementedError(
"mean loss reduction requires a one-dimensional mesh"
)
output_placements = tuple(
Replicate()
if index == mesh_dim
else Partial()
if isinstance(placement, Shard)
else placement
for index, placement in enumerate(spec.placements)
)
meta_args = list(args)
meta_args[1] = target
meta_args[2] = weight
output_meta = _propagate_tensor_meta(operation, tuple(meta_args), kwargs)
result, total_weight = _NLLLossFunction.apply(
value.to_local(),
target.to_local(),
None
if weight is None
else weight.to_local().detach(),
None if local_weight is None else local_weight.detach(),
reduction,
ignore_index,
channel_dim,
_NLLLossFunction._MeshRef(spec.mesh, tuple(value.shape)),
mesh_dim,
)
output_meta = _output_meta(
output_meta, result, target, reduction, value.ndim
)
return (
DTensor(
result,
spec.mesh,
output_placements,
shape=output_meta.shape,
stride=output_meta.stride,
),
total_weight,
)
def _nll_loss_and_log_softmax_backward(
grad_output: Any,
value: Any,
target: Any,
weight: Any,
reduction: str,
ignore_index: int,
total_weight: Any,
input_shape: tuple[int, ...],
channel_dim: int,
mesh: Any,
mesh_dim: int,
fuse_log_softmax: bool = True,
) -> Any:
channel_dim = 0 if value.dim() < 2 else 1
if reduction == "mean":
grad_output = grad_output / total_weight
target = target.unsqueeze(channel_dim)
safe_target = tp.where(target != ignore_index, target, 0)
grad_input = tp.zeros_like(value)
partial = _MaskPartial(
offset_shape=input_shape,
offset_dim=channel_dim,
)
flat_target = safe_target.squeeze(channel_dim).flatten()
masked_target = partial._partition_value(flat_target, mesh, mesh_dim)
if partial.mask_buffer.data is None:
raise AssertionError("loss target mask was not materialized")
grad_update = partial.mask_buffer.data.to(grad_input.dtype) - 1.0
indices = tp.arange(
masked_target.shape[0], device=masked_target.device
)
if value.dim() == 1:
grad_input[masked_target] = grad_update
elif value.dim() == 2:
grad_input[indices, masked_target] = grad_update
else:
transposed = grad_input.transpose(channel_dim, -1)
intermediate_shape = transposed.shape
flattened = transposed.reshape(-1, value.shape[channel_dim])
flattened[indices, masked_target] = grad_update
grad_input = flattened.view(intermediate_shape).transpose(
channel_dim, -1
)
if grad_input.dim() > grad_output.dim() > 0:
grad_output = grad_output.unsqueeze(channel_dim)
if weight is not None:
shape = [1] * value.dim()
shape[channel_dim] = weight.shape[0]
weight = weight.reshape(shape)
expanded_shape = list(value.shape)
expanded_shape[channel_dim] = -1
expanded_weight = weight.expand(expanded_shape)
target_weight = tp.gather(
expanded_weight, channel_dim, target
)
grad_output = grad_output * target_weight
grad_output = tp.where(target != ignore_index, grad_output, 0)
if fuse_log_softmax:
return (grad_input + tp.exp(value)) * grad_output
return grad_input * grad_output
class _NLLLossFunction(Function):
class _MeshRef:
__slots__ = ("mesh", "input_shape")
def __init__(self, mesh: Any, input_shape: tuple[int, ...]) -> None:
self.mesh = mesh
self.input_shape = input_shape
@staticmethod
def forward(
context: Any,
value: Any,
target: Any,
weight: Any,
local_weight: Any,
reduction: str,
ignore_index: int,
channel_dim: int,
mesh_ref: Any,
mesh_dim: int,
) -> tuple[Any, Any]:
context.reduction = reduction
context.ignore_index = ignore_index
context.input_shape = mesh_ref.input_shape
context.channel_dim = channel_dim
context.mesh = mesh_ref.mesh
context.mesh_dim = mesh_dim
context.save_for_backward(value, target, weight, local_weight)
result, total_weight = _nll_loss_forward(
value,
target,
weight,
local_weight,
reduction,
ignore_index,
context.input_shape,
channel_dim,
context.mesh,
mesh_dim,
)
context.total_weight = total_weight
return result, total_weight
@staticmethod
def backward(
context: Any, grad_output: Any, grad_total_weight: Any
) -> tuple[Any, ...]:
del grad_total_weight
value, target, weight, _local_weight = context.saved_tensors
grad_value = _nll_loss_and_log_softmax_backward(
grad_output,
value,
target,
weight,
context.reduction,
context.ignore_index,
context.total_weight,
context.input_shape,
context.channel_dim,
context.mesh,
context.mesh_dim,
fuse_log_softmax=False,
)
return (
grad_value,
None,
None,
None,
None,
None,
None,
None,
None,
)
def _nll_loss_backward_handler(
operation: Any, args: tuple[Any, ...], kwargs: dict[str, Any]
) -> DTensor:
grad_output = cast(DTensor, args[0])
value = cast(DTensor, args[1])
target_value = args[2]
weight_value = args[3]
reduction = _reduction_name(args[4])
ignore_index = int(args[5])
total_weight_value = args[6]
channel_dim = 1 if value.ndim >= 2 else 0
spec = DTensor._op_dispatcher._spec_from_dtensor(value)
mesh_dim = _find_all_reduce_mesh_dim(spec.placements, channel_dim)
target_placements = _skip_dim(
replicate_reduction_dims(spec.placements, [channel_dim]), channel_dim
)
all_replicate = (Replicate(),) * _mesh_ndim(spec.mesh)
target = _cast_to_dtensor(target_value, target_placements, spec.mesh)
weight = (
None
if weight_value is None
else _cast_to_dtensor(weight_value, all_replicate, spec.mesh)
)
if reduction == "none":
grad_output = grad_output.redistribute(
spec.mesh, target_placements
)
meta_args = list(args)
meta_args[0] = grad_output
meta_args[2] = target
meta_args[3] = weight
meta_args[6] = _cast_to_dtensor(
total_weight_value, all_replicate, spec.mesh
)
output_meta = _propagate_tensor_meta(operation, tuple(meta_args), kwargs)
total_weight = (
total_weight_value.to_local()
if isinstance(total_weight_value, DTensor)
else total_weight_value
)
result = _nll_loss_and_log_softmax_backward(
grad_output.to_local(),
value.to_local(),
target.to_local(),
None if weight is None else weight.to_local(),
reduction,
ignore_index,
total_weight,
tuple(value.shape),
channel_dim,
spec.mesh,
mesh_dim,
)
output_meta = _output_meta(
output_meta, result, target, "sum", value.ndim
)
return DTensor(
result,
spec.mesh,
spec.placements,
shape=output_meta.shape,
stride=output_meta.stride,
)
_CUSTOM_LOSS_HANDLERS = {
"log_softmax": _log_softmax_handler,
"_log_softmax": _log_softmax_handler,
"_log_softmax_backward_data": _log_softmax_backward_handler,
"nll_loss": _nll_loss_forward_handler,
"nll_loss_forward": _nll_loss_forward_handler,
"nll_loss2d": _nll_loss_forward_handler,
"nll_loss2d_forward": _nll_loss_forward_handler,
"nll_loss_backward": _nll_loss_backward_handler,
"nll_loss2d_backward": _nll_loss_backward_handler,
}
def _enable_custom_loss_ops() -> None:
DTensor._op_dispatcher._custom_op_handlers.update(_CUSTOM_LOSS_HANDLERS)
def _disable_custom_loss_ops() -> None:
for operation in _CUSTOM_LOSS_HANDLERS:
DTensor._op_dispatcher._custom_op_handlers.pop(operation, None)
[docs]
@contextlib.contextmanager
def loss_parallel():
_enable_custom_loss_ops()
try:
yield
finally:
_disable_custom_loss_ops()Help improve this page
Found an error, an unclear step, or a missing example?
Was this page helpful?

