|
| 1 | +import math |
| 2 | + |
| 3 | +import torch |
| 4 | +import torch.nn as nn |
| 5 | +import torch.nn.functional as F |
| 6 | + |
| 7 | +from diffsynth_engine.distributed.parallel_state import ( |
| 8 | + get_tensor_model_parallel_rank, |
| 9 | + get_tensor_model_parallel_world_size, |
| 10 | + get_tp_group, |
| 11 | + is_tp_group_initialized, |
| 12 | +) |
| 13 | + |
| 14 | + |
| 15 | +def get_tp_size() -> int: |
| 16 | + return get_tensor_model_parallel_world_size() if is_tp_group_initialized() else 1 |
| 17 | + |
| 18 | + |
| 19 | +def get_tp_rank() -> int: |
| 20 | + return get_tensor_model_parallel_rank() if is_tp_group_initialized() else 0 |
| 21 | + |
| 22 | + |
| 23 | +@torch.compiler.disable |
| 24 | +def tp_all_reduce(output: torch.Tensor) -> torch.Tensor: |
| 25 | + return get_tp_group().all_reduce(output) |
| 26 | + |
| 27 | + |
| 28 | +@torch.compiler.disable |
| 29 | +def tp_all_gather(output: torch.Tensor, dim: int) -> torch.Tensor: |
| 30 | + return get_tp_group().all_gather(output, dim=dim) |
| 31 | + |
| 32 | + |
| 33 | +class ColumnParallelLinear(nn.Module): |
| 34 | + def __init__( |
| 35 | + self, |
| 36 | + in_features: int, |
| 37 | + out_features: int, |
| 38 | + bias: bool = True, |
| 39 | + gather_output: bool = False, |
| 40 | + dtype: torch.dtype | None = None, |
| 41 | + device: torch.device | str | None = None, |
| 42 | + ): |
| 43 | + super().__init__() |
| 44 | + tp_size = get_tp_size() |
| 45 | + if out_features % tp_size != 0: |
| 46 | + raise ValueError( |
| 47 | + f"ColumnParallelLinear: out_features ({out_features}) must be divisible by tp_size ({tp_size})" |
| 48 | + ) |
| 49 | + |
| 50 | + self.in_features = in_features |
| 51 | + self.out_features = out_features |
| 52 | + self.gather_output = gather_output |
| 53 | + self.out_features_per_partition = out_features // tp_size |
| 54 | + self.tp_size = tp_size |
| 55 | + self.tp_rank = get_tp_rank() |
| 56 | + |
| 57 | + factory_kwargs = {"dtype": dtype, "device": device} |
| 58 | + self.weight = nn.Parameter(torch.empty(self.out_features_per_partition, in_features, **factory_kwargs)) |
| 59 | + if bias: |
| 60 | + self.bias = nn.Parameter(torch.empty(self.out_features_per_partition, **factory_kwargs)) |
| 61 | + else: |
| 62 | + self.register_parameter("bias", None) |
| 63 | + self.reset_parameters() |
| 64 | + |
| 65 | + def reset_parameters(self) -> None: |
| 66 | + nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5)) |
| 67 | + if self.bias is not None: |
| 68 | + bound = 1 / math.sqrt(self.in_features) if self.in_features > 0 else 0 |
| 69 | + nn.init.uniform_(self.bias, -bound, bound) |
| 70 | + |
| 71 | + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: |
| 72 | + output = F.linear(hidden_states, self.weight, self.bias) |
| 73 | + if self.gather_output and self.tp_size > 1: |
| 74 | + output = tp_all_gather(output, dim=-1) |
| 75 | + return output |
| 76 | + |
| 77 | + def extra_repr(self) -> str: |
| 78 | + return ( |
| 79 | + f"in_features={self.in_features}, out_features={self.out_features}, " |
| 80 | + f"out_per_partition={self.out_features_per_partition}, " |
| 81 | + f"bias={self.bias is not None}, gather_output={self.gather_output}" |
| 82 | + ) |
| 83 | + |
| 84 | + |
| 85 | +class RowParallelLinear(nn.Module): |
| 86 | + def __init__( |
| 87 | + self, |
| 88 | + in_features: int, |
| 89 | + out_features: int, |
| 90 | + bias: bool = True, |
| 91 | + input_is_parallel: bool = True, |
| 92 | + dtype: torch.dtype | None = None, |
| 93 | + device: torch.device | str | None = None, |
| 94 | + ): |
| 95 | + super().__init__() |
| 96 | + tp_size = get_tp_size() |
| 97 | + if in_features % tp_size != 0: |
| 98 | + raise ValueError(f"RowParallelLinear: in_features ({in_features}) must be divisible by tp_size ({tp_size})") |
| 99 | + |
| 100 | + self.in_features = in_features |
| 101 | + self.out_features = out_features |
| 102 | + self.input_is_parallel = input_is_parallel |
| 103 | + self.in_features_per_partition = in_features // tp_size |
| 104 | + self.tp_size = tp_size |
| 105 | + self.tp_rank = get_tp_rank() |
| 106 | + |
| 107 | + factory_kwargs = {"dtype": dtype, "device": device} |
| 108 | + self.weight = nn.Parameter(torch.empty(out_features, self.in_features_per_partition, **factory_kwargs)) |
| 109 | + if bias: |
| 110 | + self.bias = nn.Parameter(torch.empty(out_features, **factory_kwargs)) |
| 111 | + else: |
| 112 | + self.register_parameter("bias", None) |
| 113 | + self.reset_parameters() |
| 114 | + |
| 115 | + def reset_parameters(self) -> None: |
| 116 | + bound = 1 / math.sqrt(self.in_features) if self.in_features > 0 else 0 |
| 117 | + nn.init.uniform_(self.weight, -bound, bound) |
| 118 | + if self.bias is not None: |
| 119 | + nn.init.uniform_(self.bias, -bound, bound) |
| 120 | + |
| 121 | + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: |
| 122 | + if self.tp_size == 1: |
| 123 | + return F.linear(hidden_states, self.weight, self.bias) |
| 124 | + |
| 125 | + if not self.input_is_parallel: |
| 126 | + hidden_states = hidden_states.chunk(self.tp_size, dim=-1)[self.tp_rank].contiguous() |
| 127 | + |
| 128 | + output = F.linear(hidden_states, self.weight, None) |
| 129 | + output = tp_all_reduce(output) |
| 130 | + if self.bias is not None: |
| 131 | + output = output + self.bias |
| 132 | + return output |
| 133 | + |
| 134 | + def extra_repr(self) -> str: |
| 135 | + return ( |
| 136 | + f"in_features={self.in_features}, out_features={self.out_features}, " |
| 137 | + f"in_per_partition={self.in_features_per_partition}, " |
| 138 | + f"bias={self.bias is not None}, input_is_parallel={self.input_is_parallel}" |
| 139 | + ) |
0 commit comments