stisiTT's picture
Add files using upload-large-folder tool
9aa90e0 verified
Raw History Blame Contribute Delete
9.48 kB
# SPDX-FileCopyrightText: © 2025 Tenstorrent USA, Inc.
# SPDX-License-Identifier: Apache-2.0
import math
from typing import Optional
import torch
import ttnn
class PaddingConfig:
"""
Configuration for model padding to enable tensor parallelism.
This class handles the calculation and validation of padding requirements
for attention heads and hidden dimensions to make them divisible by the
tensor parallel factor.
"""
def __init__(
self, original_heads: int, target_heads: int, head_dim: int, tensor_parallel_factor: Optional[int] = None
):
"""
Initialize padding configuration.
Args:
original_heads: Original number of attention heads
target_heads: Target number of heads (must be >= original_heads)
head_dim: Dimension per attention head (remains constant)
tensor_parallel_factor: TP factor for validation (optional)
"""
self.original_heads = original_heads
self.target_heads = target_heads
self.head_dim = head_dim
# Calculate derived dimensions
self.original_dim = original_heads * head_dim
self.target_dim = target_heads * head_dim
# Padding amounts
self.head_padding = target_heads - original_heads
self.dim_padding = self.target_dim - self.original_dim
# Validation
self._validate(tensor_parallel_factor)
def _validate(self, tensor_parallel_factor: Optional[int]):
"""Validate padding configuration."""
if self.target_heads < self.original_heads:
raise ValueError(f"target_heads ({self.target_heads}) must be >= original_heads ({self.original_heads})")
if self.head_dim <= 0:
raise ValueError(f"head_dim must be positive, got {self.head_dim}")
if tensor_parallel_factor is not None:
if self.target_heads % tensor_parallel_factor != 0:
raise ValueError(
f"target_heads ({self.target_heads}) must be divisible by "
f"tensor_parallel_factor ({tensor_parallel_factor})"
)
@classmethod
def from_tensor_parallel_factor(
cls, original_heads: int, head_dim: int, tensor_parallel_factor: int
) -> "PaddingConfig":
"""
Create padding config automatically based on tensor parallel factor.
Args:
original_heads: Original number of attention heads
head_dim: Dimension per attention head
tensor_parallel_factor: Desired TP factor
Returns:
PaddingConfig with target_heads rounded up to be divisible by TP factor
"""
target_heads = math.ceil(original_heads / tensor_parallel_factor) * tensor_parallel_factor
return cls(original_heads, target_heads, head_dim, tensor_parallel_factor)
def is_padding_needed(self) -> bool:
"""Return True if any padding is needed."""
return self.head_padding > 0
def __repr__(self) -> str:
return (
f"PaddingConfig(original_heads={self.original_heads}, "
f"target_heads={self.target_heads}, head_dim={self.head_dim}, "
f"dim_padding={self.dim_padding})"
)
def pad_weight_tensor(
weight: torch.Tensor, padding_config: PaddingConfig, pad_input_dim: bool = False, pad_output_dim: bool = False
) -> torch.Tensor:
"""
Pad a weight tensor according to padding configuration.
Args:
weight: Weight tensor to pad (typically 2D: [input_dim, output_dim])
padding_config: Padding configuration
pad_input_dim: Whether to pad the input dimension
pad_output_dim: Whether to pad the output dimension
Returns:
Padded weight tensor
"""
if not padding_config.is_padding_needed():
return weight
padded_weight = weight.clone()
# Pad input dimension (dimension 0 for transposed weights)
if pad_input_dim and padding_config.dim_padding > 0:
input_padding = torch.zeros(
padding_config.dim_padding, weight.shape[1], dtype=weight.dtype, device=weight.device
)
padded_weight = torch.cat([padded_weight, input_padding], dim=0)
# Pad output dimension (dimension 1 for transposed weights)
if pad_output_dim and padding_config.dim_padding > 0:
output_padding = torch.zeros(
padded_weight.shape[0], padding_config.dim_padding, dtype=weight.dtype, device=weight.device
)
padded_weight = torch.cat([padded_weight, output_padding], dim=1)
return padded_weight
def pad_bias_tensor(bias: torch.Tensor, padding_config: PaddingConfig) -> torch.Tensor:
"""
Pad a bias tensor according to padding configuration.
Args:
bias: Bias tensor to pad
padding_config: Padding configuration
Returns:
Padded bias tensor
"""
if not padding_config.is_padding_needed():
return bias
if padding_config.dim_padding > 0:
bias_padding = torch.zeros(padding_config.dim_padding, dtype=bias.dtype, device=bias.device)
return torch.cat([bias, bias_padding], dim=-1)
return bias
def pad_qkv_weights(
q_weight: torch.Tensor, k_weight: torch.Tensor, v_weight: torch.Tensor, padding_config: PaddingConfig
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
"""
Pad QKV weight tensors for attention layers using structured padding.
Args:
q_weight: Query projection weight (in_dim, out_dim)
k_weight: Key projection weight (in_dim, out_dim)
v_weight: Value projection weight (in_dim, out_dim)
padding_config: Padding configuration
Returns:
Tuple of padded (q_weight, k_weight, v_weight)
"""
if not padding_config.is_padding_needed():
return q_weight, k_weight, v_weight
original_dim = padding_config.original_dim
target_dim = padding_config.target_dim
def pad_qkv_weight(weight):
in_dim, out_dim = weight.shape
mult = out_dim // original_dim
assert mult == 3, f"Only 3-way fused QKV weight matrices are supported, given weight shape {weight.shape}"
# Reshape: (in_dim, mult_factor * original_dim) -> (in_dim, mult_factor, original_dim)
weight = weight.reshape(weight.shape[0], mult_factor, original_dim)
# Pad output dimension: (in_dim, mult_factor, original_dim) -> (in_dim, mult_factor, target_dim)
output_padding = torch.zeros(
weight.shape[0], mult_factor, target_dim - original_dim, dtype=weight.dtype, device=weight.device
)
weight = torch.cat([weight, output_padding], dim=2)
# Reshape back: (in_dim, mult_factor, target_dim) -> (in_dim, mult_factor * target_dim)
weight = weight.reshape(weight.shape[0], -1)
return weight
padded_q = pad_qkv_weight(q_weight)
padded_k = pad_qkv_weight(k_weight)
padded_v = pad_qkv_weight(v_weight)
return padded_q, padded_k, padded_v
def pad_qkv_biases(
q_bias: Optional[torch.Tensor],
k_bias: Optional[torch.Tensor],
v_bias: Optional[torch.Tensor],
padding_config: PaddingConfig,
) -> tuple[Optional[torch.Tensor], Optional[torch.Tensor], Optional[torch.Tensor]]:
"""
Pad QKV bias tensors for attention layers using structured padding.
Args:
q_bias: Query projection bias (can be None)
k_bias: Key projection bias (can be None)
v_bias: Value projection bias (can be None)
padding_config: Padding configuration
Returns:
Tuple of padded (q_bias, k_bias, v_bias)
"""
if not padding_config.is_padding_needed():
return q_bias, k_bias, v_bias
original_dim = padding_config.original_dim
target_dim = padding_config.target_dim
def pad_qkv_bias(bias):
if bias is None:
return None
orig_shape = bias.shape
mult_factor = orig_shape[0] // original_dim
assert mult_factor == 3, "Only 3-way fused QKV bias matrices are supported"
bias = bias.reshape(mult_factor, original_dim)
# Pad: (mult_factor, original_dim) -> (mult_factor, target_dim)
bias_padding = torch.zeros(mult_factor, target_dim - original_dim, dtype=bias.dtype, device=bias.device)
bias = torch.cat([bias, bias_padding], dim=1)
# Reshape back: (mult_factor, target_dim) -> (mult_factor * target_dim,)
bias = bias.reshape(orig_shape)
return bias
padded_q_bias = pad_qkv_bias(q_bias)
padded_k_bias = pad_qkv_bias(k_bias)
padded_v_bias = pad_qkv_bias(v_bias)
return padded_q_bias, padded_k_bias, padded_v_bias
def get_padded_vision_seq_len(N, num_devices):
divisor = ttnn.TILE_SIZE * num_devices
# Calculate padding needed to make seq_len divisible by both tile size and num_devices
padded_seq_len = math.ceil(N / divisor) * divisor
padding = padded_seq_len - N
shard_size = padded_seq_len // num_devices
return padded_seq_len
def pad_vision_seq_parallel(tensor, num_devices):
"""
Sequence parallelism shards the vision tensor in dim2.
dim2 must be divisible by tile size and num_devices.
"""
seq_len = tensor.shape[2]
padded_seq_len = get_padded_vision_seq_len(seq_len, num_devices)
pad_len = padded_seq_len - seq_len
# Pad the sequence length dimension (dim2) on the right
if pad_len > 0:
tensor = torch.nn.functional.pad(tensor, (0, 0, 0, pad_len))
return tensor