Instructions to use Cccccz/HY with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use Cccccz/HY with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("Cccccz/HY", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
Download trainer/distributed/device_communicators/base_device_communicator.py from Cccccz/HY: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/Cccccz/HY/resolve/main/trainer/distributed/device_communicators/base_device_communicator.py
- Command line
-
hf download hf://Cccccz/HY/trainer/distributed/device_communicators/base_device_communicator.py
-
curl -L -o base_device_communicator.py https://huggingface.co/Cccccz/HY/resolve/main/trainer/distributed/device_communicators/base_device_communicator.py
11.9 kB
| # SPDX-License-Identifier: Apache-2.0 | |
| # Adapted from https://github.com/vllm-project/vllm/blob/v0.7.3/vllm/distributed/device_communicators/base_device_communicator.py | |
| from typing import Any | |
| import torch | |
| import torch.distributed as dist | |
| from torch import Tensor | |
| from torch.distributed import ProcessGroup, ReduceOp | |
| class DistributedAutograd: | |
| """Collection of autograd functions for distributed operations. | |
| This class provides custom autograd functions for distributed operations like all_reduce, | |
| all_gather, and all_to_all. Each operation is implemented as a static inner class with | |
| proper forward and backward implementations. | |
| """ | |
| class AllReduce(torch.autograd.Function): | |
| """Differentiable all_reduce operation. | |
| The gradient of all_reduce is another all_reduce operation since the operation | |
| combines values from all ranks equally. | |
| """ | |
| def forward(ctx: Any, | |
| group: ProcessGroup, | |
| input_: Tensor, | |
| op: dist.ReduceOp | None = None) -> Tensor: | |
| ctx.group = group | |
| ctx.op = op | |
| output = input_.clone() | |
| dist.all_reduce(output, group=group, op=op) | |
| return output | |
| def backward(ctx: Any, | |
| grad_output: Tensor) -> tuple[None, Tensor, None]: | |
| grad_output = grad_output.clone() | |
| dist.all_reduce(grad_output, group=ctx.group, op=ctx.op) | |
| return None, grad_output, None | |
| class AllGather(torch.autograd.Function): | |
| """Differentiable all_gather operation. | |
| The operation gathers tensors from all ranks and concatenates them along a specified dimension. | |
| The backward pass uses reduce_scatter to efficiently distribute gradients back to source ranks. | |
| """ | |
| def forward(ctx: Any, group: ProcessGroup, input_: Tensor, | |
| world_size: int, dim: int) -> Tensor: | |
| ctx.group = group | |
| ctx.world_size = world_size | |
| ctx.dim = dim | |
| ctx.input_shape = input_.shape | |
| input_size = input_.size() | |
| output_size = (input_size[0] * world_size, ) + input_size[1:] | |
| output_tensor = torch.empty(output_size, | |
| dtype=input_.dtype, | |
| device=input_.device) | |
| dist.all_gather_into_tensor(output_tensor, input_, group=group) | |
| output_tensor = output_tensor.reshape((world_size, ) + input_size) | |
| output_tensor = output_tensor.movedim(0, dim) | |
| output_tensor = output_tensor.reshape(input_size[:dim] + | |
| (world_size * | |
| input_size[dim], ) + | |
| input_size[dim + 1:]) | |
| return output_tensor | |
| def backward(ctx: Any, | |
| grad_output: Tensor) -> tuple[None, Tensor, None, None]: | |
| # Split the gradient tensor along the gathered dimension | |
| dim_size = grad_output.size(ctx.dim) // ctx.world_size | |
| grad_chunks = grad_output.reshape(grad_output.shape[:ctx.dim] + | |
| (ctx.world_size, dim_size) + | |
| grad_output.shape[ctx.dim + 1:]) | |
| grad_chunks = grad_chunks.movedim(ctx.dim, 0) | |
| # Each rank only needs its corresponding gradient | |
| grad_input = torch.empty(ctx.input_shape, | |
| dtype=grad_output.dtype, | |
| device=grad_output.device) | |
| dist.reduce_scatter_tensor(grad_input, | |
| grad_chunks.contiguous(), | |
| group=ctx.group) | |
| return None, grad_input, None, None | |
| class AllToAll4D(torch.autograd.Function): | |
| """Differentiable all_to_all operation specialized for 4D tensors. | |
| This operation is particularly useful for attention operations where we need to | |
| redistribute data across ranks for efficient parallel processing. | |
| The operation supports two modes: | |
| 1. scatter_dim=2, gather_dim=1: Used for redistributing attention heads | |
| 2. scatter_dim=1, gather_dim=2: Used for redistributing sequence dimensions | |
| """ | |
| def forward(ctx: Any, group: ProcessGroup, input_: Tensor, | |
| world_size: int, scatter_dim: int, | |
| gather_dim: int) -> Tensor: | |
| ctx.group = group | |
| ctx.world_size = world_size | |
| ctx.scatter_dim = scatter_dim | |
| ctx.gather_dim = gather_dim | |
| if world_size == 1: | |
| return input_ | |
| assert input_.dim( | |
| ) == 4, f"input must be 4D tensor, got {input_.dim()} and shape {input_.shape}" | |
| if scatter_dim == 2 and gather_dim == 1: | |
| bs, shard_seqlen, hn, hd = input_.shape | |
| seqlen = shard_seqlen * world_size | |
| shard_hn = hn // world_size | |
| input_ = input_.transpose( | |
| 0, 2).contiguous() # hn, shard_seqlen, bs, hd | |
| output = torch.empty_like(input_) | |
| dist.all_to_all_single(output, input_, | |
| group=group) # hn, shard_seqlen, bs, hd | |
| output = torch.cat(output.split(shard_hn), | |
| dim=1) # sharded hn, seqlen, bs, hd | |
| output = output.transpose( | |
| 0, 2).contiguous() # bs, seqlen, sharded_hn, hd | |
| return output | |
| elif scatter_dim == 1 and gather_dim == 2: | |
| bs, seqlen, shard_hn, hd = input_.shape | |
| hn = shard_hn * world_size | |
| shard_seqlen = seqlen // world_size | |
| input_ = input_.transpose( | |
| 0, 2).contiguous() # shard_hn, seqlen, bs, hd | |
| input_ = input_.reshape(shard_hn, world_size, shard_seqlen, bs, | |
| hd).transpose(0, 1).reshape( | |
| shard_hn * world_size, shard_seqlen, | |
| bs, hd).contiguous() | |
| output = torch.empty_like(input_) | |
| dist.all_to_all_single(output, input_, group=group) | |
| output = output.transpose( | |
| 0, 2).contiguous() # bs, seqlen, sharded_hn, hd | |
| return output | |
| else: | |
| raise RuntimeError( | |
| f"Invalid scatter_dim={scatter_dim}, gather_dim={gather_dim}. " | |
| f"Only (scatter_dim=2, gather_dim=1) and (scatter_dim=1, gather_dim=2) are supported." | |
| ) | |
| def backward( | |
| ctx: Any, | |
| grad_output: Tensor) -> tuple[None, Tensor, None, None, None]: | |
| if ctx.world_size == 1: | |
| return None, grad_output, None, None, None | |
| # For backward pass, we swap scatter_dim and gather_dim | |
| output = DistributedAutograd.AllToAll4D.apply( | |
| ctx.group, grad_output, ctx.world_size, ctx.gather_dim, | |
| ctx.scatter_dim) | |
| return None, output, None, None, None | |
| class DeviceCommunicatorBase: | |
| """ | |
| Base class for device-specific communicator with autograd support. | |
| It can use the `cpu_group` to initialize the communicator. | |
| If the device has PyTorch integration (PyTorch can recognize its | |
| communication backend), the `device_group` will also be given. | |
| """ | |
| def __init__(self, | |
| cpu_group: ProcessGroup, | |
| device: torch.device | None = None, | |
| device_group: ProcessGroup | None = None, | |
| unique_name: str = ""): | |
| self.device = device or torch.device("cpu") | |
| self.cpu_group = cpu_group | |
| self.device_group = device_group | |
| self.unique_name = unique_name | |
| self.rank = dist.get_rank(cpu_group) | |
| self.world_size = dist.get_world_size(cpu_group) | |
| self.ranks = dist.get_process_group_ranks(cpu_group) | |
| self.global_rank = dist.get_rank() | |
| self.global_world_size = dist.get_world_size() | |
| self.rank_in_group = dist.get_group_rank(self.cpu_group, | |
| self.global_rank) | |
| def all_reduce(self, | |
| input_: torch.Tensor, | |
| op: dist.ReduceOp | None = ReduceOp.SUM) -> torch.Tensor: | |
| """Performs an all_reduce operation with gradient support.""" | |
| return DistributedAutograd.AllReduce.apply(self.device_group, input_, | |
| op) | |
| def all_gather(self, input_: torch.Tensor, dim: int = -1) -> torch.Tensor: | |
| """Performs an all_gather operation with gradient support.""" | |
| if dim < 0: | |
| dim += input_.dim() | |
| return DistributedAutograd.AllGather.apply(self.device_group, input_, | |
| self.world_size, dim) | |
| def all_to_all_4D(self, | |
| input_: torch.Tensor, | |
| scatter_dim: int = 2, | |
| gather_dim: int = 1) -> torch.Tensor: | |
| """Performs a 4D all-to-all operation with gradient support.""" | |
| return DistributedAutograd.AllToAll4D.apply(self.device_group, input_, | |
| self.world_size, | |
| scatter_dim, gather_dim) | |
| def gather(self, | |
| input_: torch.Tensor, | |
| dst: int = 0, | |
| dim: int = -1) -> torch.Tensor | None: | |
| """ | |
| NOTE: We assume that the input tensor is on the same device across | |
| all the ranks. | |
| NOTE: `dst` is the local rank of the destination rank. | |
| """ | |
| world_size = self.world_size | |
| assert -input_.dim() <= dim < input_.dim(), ( | |
| f"Invalid dim ({dim}) for input tensor with shape {input_.size()}") | |
| if dim < 0: | |
| # Convert negative dim to positive. | |
| dim += input_.dim() | |
| # Allocate output tensor. | |
| if self.rank_in_group == dst: | |
| gather_list = [torch.empty_like(input_) for _ in range(world_size)] | |
| else: | |
| gather_list = None | |
| # Gather. | |
| torch.distributed.gather(input_, | |
| gather_list, | |
| dst=self.ranks[dst], | |
| group=self.device_group) | |
| if self.rank_in_group == dst: | |
| output_tensor = torch.cat(gather_list, dim=dim) | |
| else: | |
| output_tensor = None | |
| return output_tensor | |
| def send(self, tensor: torch.Tensor, dst: int | None = None) -> None: | |
| """Sends a tensor to the destination rank in a non-blocking way""" | |
| """NOTE: `dst` is the local rank of the destination rank.""" | |
| if dst is None: | |
| dst = (self.rank_in_group + 1) % self.world_size | |
| torch.distributed.send(tensor, self.ranks[dst], self.device_group) | |
| def recv(self, | |
| size: torch.Size, | |
| dtype: torch.dtype, | |
| src: int | None = None) -> torch.Tensor: | |
| """Receives a tensor from the source rank.""" | |
| """NOTE: `src` is the local rank of the source rank.""" | |
| if src is None: | |
| src = (self.rank_in_group - 1) % self.world_size | |
| tensor = torch.empty(size, dtype=dtype, device=self.device) | |
| torch.distributed.recv(tensor, self.ranks[src], self.device_group) | |
| return tensor | |
| def destroy(self) -> None: | |
| pass | |