988 lines
39 KiB
Python
988 lines
39 KiB
Python
import torch
|
||||
|
|
import torch.nn as nn
|
|||
|
|
import torch.utils.checkpoint as cp
|
|||
|
|
|
|||
|
|
from torch.distributed.fsdp import MixedPrecision as MP
|
|||
|
|
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
|
|||
|
|
|
|||
|
|
from wall_x.fusions import ops
|
|||
|
|
|
|||
|
|
from peft import LoraConfig, get_peft_model
|
|||
|
|
from typing import Optional, Union, Dict
|
|||
|
|
from packaging import version
|
|||
|
|
|
|||
|
|
from transformers import GenerationMixin
|
|||
|
|
from transformers.activations import ACT2FN
|
|||
|
|
from transformers.modeling_utils import AttentionInterface
|
|||
|
|
|
|||
|
|
from transformers.utils import logging, is_torch_xla_available
|
|||
|
|
|
|||
|
|
from wall_x.model.action_head import ActionProcessor
|
|||
|
|
from wall_x.model.model_utils import find_first_last_ones
|
|||
|
|
|
|||
|
|
ALL_ATTENTION_FUNCTIONS: AttentionInterface = AttentionInterface()
|
|||
|
|
logger = logging.get_logger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
X2ROBOT_ATTENTION_FUNCTIONS = []
|
|||
|
|
ATTENTION_TYPES_WITH_2D_MASK = [
|
|||
|
|
"sdpa",
|
|||
|
|
]
|
|||
|
|
ATTENTION_TYPES_WITH_FLASH_MASK = []
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TokenTypeRouter(nn.Module):
|
|||
|
|
def __init__(self, num_experts: int):
|
|||
|
|
super().__init__()
|
|||
|
|
self.num_experts = num_experts
|
|||
|
|
|
|||
|
|
def forward(self, token_types: torch.Tensor) -> torch.Tensor:
|
|||
|
|
"""
|
|||
|
|
Assigns tokens to different experts based on `token_type`.
|
|||
|
|
Args:
|
|||
|
|
token_types (torch.Tensor): A tensor of shape (batch_size, seq_length) representing the type of each token.
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
experts_indices (torch.Tensor): A tensor of shape (batch_size, seq_length) representing the expert index assigned to each token.
|
|||
|
|
"""
|
|||
|
|
experts_indices = token_types % self.num_experts
|
|||
|
|
return experts_indices
|
|||
|
|
|
|||
|
|
|
|||
|
|
class BlockSparseMLP(nn.Module):
|
|||
|
|
def __init__(self, config, use_selective_recompute: bool = False):
|
|||
|
|
super().__init__()
|
|||
|
|
self.hidden_size = config["hidden_size"]
|
|||
|
|
self.intermediate_size = config["intermediate_size"]
|
|||
|
|
self.hidden_act = config["hidden_act"]
|
|||
|
|
|
|||
|
|
self.use_selective_recompute = use_selective_recompute
|
|||
|
|
|
|||
|
|
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
|||
|
|
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
|||
|
|
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
|
|||
|
|
|
|||
|
|
self.act_fn = ACT2FN[self.hidden_act]
|
|||
|
|
|
|||
|
|
def _full_mlp(self, hidden_state):
|
|||
|
|
gate_out = self.gate_proj(hidden_state)
|
|||
|
|
up_out = self.up_proj(hidden_state)
|
|||
|
|
act_out = self.act_fn(gate_out) * up_out
|
|||
|
|
return self.down_proj(act_out)
|
|||
|
|
|
|||
|
|
def forward(self, hidden_state):
|
|||
|
|
if self.use_selective_recompute:
|
|||
|
|
# Perform checkpoint recalculation for the entire expert MLP.
|
|||
|
|
return cp.checkpoint(
|
|||
|
|
self._full_mlp,
|
|||
|
|
hidden_state,
|
|||
|
|
use_reentrant=False,
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
return self._full_mlp(hidden_state)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SparseMoeBlock(nn.Module):
|
|||
|
|
def __init__(self, config, num_experts: int, use_selective_recompute: bool = False):
|
|||
|
|
super().__init__()
|
|||
|
|
self.num_experts = num_experts
|
|||
|
|
self.use_selective_recompute = use_selective_recompute
|
|||
|
|
|
|||
|
|
# Pass the `use_selective_recompute` parameter to each expert.
|
|||
|
|
self.experts = nn.ModuleList(
|
|||
|
|
[
|
|||
|
|
BlockSparseMLP(
|
|||
|
|
config.experts[i], use_selective_recompute=use_selective_recompute
|
|||
|
|
)
|
|||
|
|
for i in range(num_experts)
|
|||
|
|
]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if not hasattr(config, "dim_inputs") or not config.dim_inputs:
|
|||
|
|
raise ValueError("Configuration must contain a valid dim_inputs")
|
|||
|
|
|
|||
|
|
self.dim_inputs = config.dim_inputs
|
|||
|
|
self.permuted = config.mot_opt
|
|||
|
|
|
|||
|
|
def forward(
|
|||
|
|
self,
|
|||
|
|
hidden_states: torch.Tensor,
|
|||
|
|
experts_indices: torch.Tensor,
|
|||
|
|
start_indices: torch.Tensor,
|
|||
|
|
end_indices: torch.Tensor,
|
|||
|
|
) -> torch.Tensor:
|
|||
|
|
|
|||
|
|
if self.permuted:
|
|||
|
|
permuted_inputs = hidden_states
|
|||
|
|
else:
|
|||
|
|
batch_size, seq_length, hidden_dim = hidden_states.shape
|
|||
|
|
|
|||
|
|
flat_hidden = hidden_states.reshape(-1, hidden_dim)
|
|||
|
|
experts_indices = experts_indices.reshape(-1)
|
|||
|
|
probs = torch.ones_like(experts_indices, dtype=torch.float32).reshape(-1, 1)
|
|||
|
|
permuted_inputs, row_id_map = ops.permute(flat_hidden, experts_indices)
|
|||
|
|
|
|||
|
|
# buffer
|
|||
|
|
final_output = torch.zeros_like(permuted_inputs)
|
|||
|
|
|
|||
|
|
# Expert forward contain selective recompute
|
|||
|
|
for expert_idx, expert in enumerate(self.experts):
|
|||
|
|
start, end = start_indices[expert_idx], end_indices[expert_idx]
|
|||
|
|
if start == end:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
dim_input = self.dim_inputs[expert_idx]
|
|||
|
|
expert_input = permuted_inputs[start:end, :dim_input]
|
|||
|
|
|
|||
|
|
partial_output = expert(expert_input)
|
|||
|
|
final_output[start:end, :dim_input] = partial_output[:, :dim_input]
|
|||
|
|
|
|||
|
|
if self.permuted:
|
|||
|
|
return final_output
|
|||
|
|
else:
|
|||
|
|
final_output = ops.unpermute(final_output, row_id_map, probs)
|
|||
|
|
return final_output.reshape(batch_size, seq_length, hidden_dim)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ActionModelMixMin:
|
|||
|
|
# config: Qwen2_5_VLConfig
|
|||
|
|
action_preprocessor: ActionProcessor
|
|||
|
|
router: TokenTypeRouter
|
|||
|
|
moe: SparseMoeBlock
|
|||
|
|
|
|||
|
|
def __init__(self, config, action_preprocessor, router, moe):
|
|||
|
|
self.config = config
|
|||
|
|
self.action_preprocessor = action_preprocessor
|
|||
|
|
self.router = router
|
|||
|
|
self.moe = moe
|
|||
|
|
self._mot_opt_warned = False
|
|||
|
|
|
|||
|
|
def set_normalizer(self, normalizer_action, normalizer_propri):
|
|||
|
|
if hasattr(self, "action_preprocessor"):
|
|||
|
|
self.action_preprocessor.set_normalizer(
|
|||
|
|
normalizer_action, normalizer_propri
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
logger.warning(
|
|||
|
|
"ActionModelMixMin.set_normalizer is called but action_preprocessor is not set"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def _apply_mlp_moe(self, hidden_states, token_types, start_indices, end_indices):
|
|||
|
|
if self.config.mlp_moe:
|
|||
|
|
hidden_states = self.moe(
|
|||
|
|
hidden_states, token_types, start_indices, end_indices
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
hidden_states = self.mlp(hidden_states)
|
|||
|
|
return hidden_states
|
|||
|
|
|
|||
|
|
def _apply_norm_moe(
|
|||
|
|
self,
|
|||
|
|
hidden_states,
|
|||
|
|
token_types,
|
|||
|
|
adarms_conds,
|
|||
|
|
norms, # list of norm layers (expert-wise)
|
|||
|
|
norm, # shared norm if not norm_moe
|
|||
|
|
start_indices=None,
|
|||
|
|
end_indices=None,
|
|||
|
|
use_selective_recompute=False,
|
|||
|
|
):
|
|||
|
|
"""
|
|||
|
|
MoE-aware LayerNorm with optional selective activation recomputation.
|
|||
|
|
|
|||
|
|
Only activation math is recomputed. No GEMM is recomputed.
|
|||
|
|
Safe for FSDP (use_reentrant=False).
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
gate = None
|
|||
|
|
gate_mask = None
|
|||
|
|
|
|||
|
|
# -------------------------
|
|||
|
|
# Case 1: norm_moe=True (expert-wise norm)
|
|||
|
|
# -------------------------
|
|||
|
|
if self.config.norm_moe:
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------
|
|||
|
|
# Case 1A: mot_opt=True (segments assigned by start/end)
|
|||
|
|
# ---------------------------------------------------------
|
|||
|
|
if self.config.mot_opt:
|
|||
|
|
new_hidden_states = torch.zeros_like(hidden_states)
|
|||
|
|
|
|||
|
|
for expert_idx, expert_norm in enumerate(norms):
|
|||
|
|
start = start_indices[expert_idx]
|
|||
|
|
end = end_indices[expert_idx]
|
|||
|
|
if start == end:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
dim_input = self.config.dim_inputs[expert_idx]
|
|||
|
|
selected = hidden_states[start:end] # [K, D]
|
|||
|
|
|
|||
|
|
# ====== reshape if adarms on flow expert ======
|
|||
|
|
if self.config.use_adarms and expert_idx == 1:
|
|||
|
|
selected = selected.view(
|
|||
|
|
-1,
|
|||
|
|
self.config.action_horizon_flow,
|
|||
|
|
selected.shape[-1],
|
|||
|
|
)
|
|||
|
|
input_slice = selected[:, :, :dim_input]
|
|||
|
|
cond = adarms_conds[expert_idx]
|
|||
|
|
else:
|
|||
|
|
input_slice = selected[:, :dim_input]
|
|||
|
|
cond = adarms_conds[expert_idx]
|
|||
|
|
|
|||
|
|
if use_selective_recompute:
|
|||
|
|
|
|||
|
|
def norm_chunk(t_x, t_cond, expert_norm=expert_norm):
|
|||
|
|
if t_cond is None or (
|
|||
|
|
isinstance(t_cond, torch.Tensor) and t_cond.numel() == 0
|
|||
|
|
):
|
|||
|
|
out, _ = expert_norm(t_x)
|
|||
|
|
else:
|
|||
|
|
out, _ = expert_norm(t_x, t_cond)
|
|||
|
|
return out
|
|||
|
|
|
|||
|
|
cond_for_cp = (
|
|||
|
|
cond
|
|||
|
|
if cond is not None
|
|||
|
|
else torch.empty(0, device=input_slice.device)
|
|||
|
|
)
|
|||
|
|
processed = cp.checkpoint(
|
|||
|
|
norm_chunk,
|
|||
|
|
input_slice,
|
|||
|
|
cond_for_cp,
|
|||
|
|
use_reentrant=False,
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
processed, gate = expert_norm(input_slice, cond)
|
|||
|
|
|
|||
|
|
# reshape back if needed
|
|||
|
|
if self.config.use_adarms and expert_idx == 1:
|
|||
|
|
processed = processed.view(-1, dim_input)
|
|||
|
|
|
|||
|
|
new_hidden_states[start:end, :dim_input] = processed.to(
|
|||
|
|
hidden_states.dtype
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
hidden_states = new_hidden_states
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------
|
|||
|
|
# Case 1B: mot_opt=False (token-level mask)
|
|||
|
|
# ---------------------------------------------------------
|
|||
|
|
else:
|
|||
|
|
|
|||
|
|
new_hidden_states = torch.zeros_like(hidden_states)
|
|||
|
|
B, S, D = hidden_states.shape
|
|||
|
|
|
|||
|
|
for expert_idx, expert_norm in enumerate(norms):
|
|||
|
|
mask = token_types == expert_idx
|
|||
|
|
if mask.sum() == 0:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
dim_input = self.config.dim_inputs[expert_idx]
|
|||
|
|
selected = hidden_states[mask] # [K, D]
|
|||
|
|
|
|||
|
|
if self.config.use_adarms and expert_idx == 1:
|
|||
|
|
gate_mask = mask
|
|||
|
|
selected = selected.view(
|
|||
|
|
-1,
|
|||
|
|
self.config.action_horizon_flow,
|
|||
|
|
selected.shape[-1],
|
|||
|
|
)
|
|||
|
|
input_slice = selected[:, :, :dim_input]
|
|||
|
|
cond = adarms_conds[expert_idx]
|
|||
|
|
else:
|
|||
|
|
input_slice = selected[:, :dim_input]
|
|||
|
|
cond = adarms_conds[expert_idx]
|
|||
|
|
|
|||
|
|
if use_selective_recompute:
|
|||
|
|
|
|||
|
|
def norm_chunk(t_x, t_cond, expert_norm=expert_norm):
|
|||
|
|
if t_cond is None or (
|
|||
|
|
isinstance(t_cond, torch.Tensor) and t_cond.numel() == 0
|
|||
|
|
):
|
|||
|
|
out, _ = expert_norm(t_x)
|
|||
|
|
else:
|
|||
|
|
out, _ = expert_norm(t_x, t_cond)
|
|||
|
|
return out
|
|||
|
|
|
|||
|
|
cond_for_cp = (
|
|||
|
|
cond
|
|||
|
|
if cond is not None
|
|||
|
|
else torch.empty(0, device=input_slice.device)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
processed = cp.checkpoint(
|
|||
|
|
norm_chunk,
|
|||
|
|
input_slice,
|
|||
|
|
cond_for_cp,
|
|||
|
|
use_reentrant=False,
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
processed, gate = expert_norm(input_slice, cond)
|
|||
|
|
|
|||
|
|
if self.config.use_adarms and expert_idx == 1:
|
|||
|
|
processed = processed.view(-1, dim_input)
|
|||
|
|
|
|||
|
|
# scatter back
|
|||
|
|
b_id, s_id = torch.where(mask)
|
|||
|
|
new_hidden_states[b_id, s_id, :dim_input] = processed.to(
|
|||
|
|
hidden_states.dtype
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
hidden_states = new_hidden_states
|
|||
|
|
|
|||
|
|
# -------------------------
|
|||
|
|
# Case 2: norm_moe=False (single LN)
|
|||
|
|
# -------------------------
|
|||
|
|
else:
|
|||
|
|
|
|||
|
|
def norm_chunk_shared(t_x, dummy, norm_module=norm):
|
|||
|
|
out, _ = norm_module(t_x)
|
|||
|
|
return out
|
|||
|
|
|
|||
|
|
if use_selective_recompute:
|
|||
|
|
dummy = torch.empty(0, device=hidden_states.device)
|
|||
|
|
hidden_states = cp.checkpoint(
|
|||
|
|
norm_chunk_shared,
|
|||
|
|
hidden_states,
|
|||
|
|
dummy,
|
|||
|
|
use_reentrant=False,
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
hidden_states, gate = norm(hidden_states)
|
|||
|
|
|
|||
|
|
return hidden_states, gate, gate_mask
|
|||
|
|
|
|||
|
|
def _gated_residual(self, x, y, gate, start_indices=None, end_indices=None):
|
|||
|
|
"""
|
|||
|
|
Applies gated residual connection with optional gate parameter.
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
x: Input tensor (residual)
|
|||
|
|
y: Output tensor to be added
|
|||
|
|
gate: Optional gate tensor to modulate the addition
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
x + y if gate is None, otherwise x + y * gate
|
|||
|
|
"""
|
|||
|
|
if x is None and y is None:
|
|||
|
|
return None
|
|||
|
|
if x is None or y is None:
|
|||
|
|
return x if x is not None else y
|
|||
|
|
if gate is None:
|
|||
|
|
return x + y
|
|||
|
|
|
|||
|
|
new_y = y.clone()
|
|||
|
|
selected_y = y[start_indices[1] : end_indices[1]]
|
|||
|
|
selected_y = selected_y.view(
|
|||
|
|
-1, self.config.action_horizon_flow, selected_y.shape[-1]
|
|||
|
|
)[:, :, : self.config.dim_inputs[1]]
|
|||
|
|
selected_y = selected_y.to(torch.float32) * gate
|
|||
|
|
new_y[start_indices[1] : end_indices[1], : self.config.dim_inputs[1]] = (
|
|||
|
|
selected_y.view(-1, self.config.dim_inputs[1]).to(new_y.dtype)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return x + new_y
|
|||
|
|
|
|||
|
|
def scatter_proprioception_embeddings(
|
|||
|
|
self, input_ids, inputs_embeds, proprioception, dataset_names, agent_pos_mask
|
|||
|
|
):
|
|||
|
|
if (
|
|||
|
|
proprioception is not None
|
|||
|
|
and not self.config.use_state_string_representation
|
|||
|
|
):
|
|||
|
|
proprioception = proprioception.to(inputs_embeds.device).to(
|
|||
|
|
inputs_embeds.dtype
|
|||
|
|
)
|
|||
|
|
agent_pos_mask = agent_pos_mask.to(inputs_embeds.device).to(
|
|||
|
|
inputs_embeds.dtype
|
|||
|
|
)
|
|||
|
|
proprioception = self.action_preprocessor.proprioception_proj(
|
|||
|
|
proprioception,
|
|||
|
|
dataset_names,
|
|||
|
|
agent_pos_mask,
|
|||
|
|
use_history=proprioception.shape[1] > 1,
|
|||
|
|
)
|
|||
|
|
mask = input_ids == self.action_token_id_set["propri_token_id"]
|
|||
|
|
mask_unsqueezed = mask.unsqueeze(-1)
|
|||
|
|
mask_expanded = mask_unsqueezed.expand_as(inputs_embeds)
|
|||
|
|
proprioception_mask = mask_expanded.to(inputs_embeds.device)
|
|||
|
|
|
|||
|
|
proprioception = proprioception.to(
|
|||
|
|
inputs_embeds.device, inputs_embeds.dtype
|
|||
|
|
)
|
|||
|
|
inputs_embeds = inputs_embeds.masked_scatter(
|
|||
|
|
proprioception_mask, proprioception
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return inputs_embeds
|
|||
|
|
|
|||
|
|
def scatter_flow_action_embeddings(
|
|||
|
|
self, input_ids, inputs_embeds, action_chunk, dataset_names, dof_mask
|
|||
|
|
):
|
|||
|
|
if not self.config.use_flow_action_expert:
|
|||
|
|
return inputs_embeds, None, None
|
|||
|
|
adarms_cond, flow = None, None
|
|||
|
|
if action_chunk is not None:
|
|||
|
|
action_chunk = action_chunk.to(inputs_embeds.device)
|
|||
|
|
dof_mask = dof_mask.to(inputs_embeds.device)
|
|||
|
|
noisy_action_emb, flow, adarms_cond = self.action_preprocessor(
|
|||
|
|
action_chunk, dataset_names, dof_mask
|
|||
|
|
)
|
|||
|
|
mask = input_ids == self.action_token_id_set["action_token_id"]
|
|||
|
|
mask_unsqueezed = mask.unsqueeze(-1)
|
|||
|
|
mask_expanded = mask_unsqueezed.expand_as(inputs_embeds)
|
|||
|
|
action_mask = mask_expanded.to(inputs_embeds.device)
|
|||
|
|
|
|||
|
|
noisy_action_emb = noisy_action_emb.to(
|
|||
|
|
inputs_embeds.device, inputs_embeds.dtype
|
|||
|
|
)
|
|||
|
|
inputs_embeds = inputs_embeds.masked_scatter(action_mask, noisy_action_emb)
|
|||
|
|
|
|||
|
|
return inputs_embeds, flow, adarms_cond
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def _update_position_ids(
|
|||
|
|
position_ids,
|
|||
|
|
moe_token_types,
|
|||
|
|
positional_masks,
|
|||
|
|
):
|
|||
|
|
if (
|
|||
|
|
positional_masks is None
|
|||
|
|
or "ar_predict_token_positions" not in positional_masks
|
|||
|
|
):
|
|||
|
|
return position_ids
|
|||
|
|
|
|||
|
|
new_position_ids = position_ids.clone()
|
|||
|
|
ar_predict_token_positions = positional_masks["ar_predict_token_positions"]
|
|||
|
|
flow_mask = moe_token_types == 1
|
|||
|
|
|
|||
|
|
start_ar_pos, end_ar_pos = find_first_last_ones(ar_predict_token_positions)
|
|||
|
|
start_flow_pos, end_flow_pos = find_first_last_ones(flow_mask)
|
|||
|
|
|
|||
|
|
for bs_i in range(position_ids.shape[1]):
|
|||
|
|
if start_ar_pos[bs_i] != -1 and end_ar_pos[bs_i] != -1:
|
|||
|
|
start_ar_ids = new_position_ids[:, bs_i, start_ar_pos[bs_i]]
|
|||
|
|
start_flow_ids = new_position_ids[:, bs_i, start_flow_pos[bs_i]]
|
|||
|
|
diff = start_flow_ids - start_ar_ids
|
|||
|
|
new_position_ids[:, bs_i, start_flow_pos[bs_i] :] = position_ids[
|
|||
|
|
:, bs_i, start_flow_pos[bs_i] :
|
|||
|
|
] - diff.unsqueeze(-1)
|
|||
|
|
|
|||
|
|
return new_position_ids
|
|||
|
|
|
|||
|
|
def _update_joint_attention_mask_2d(
|
|||
|
|
self,
|
|||
|
|
attention_mask,
|
|||
|
|
moe_token_types,
|
|||
|
|
positional_masks,
|
|||
|
|
):
|
|||
|
|
if attention_mask.dim() == 3: # bs, seq_len, seq_len
|
|||
|
|
return attention_mask
|
|||
|
|
|
|||
|
|
bs, seq_len = moe_token_types.shape[0], moe_token_types.shape[1]
|
|||
|
|
# Create a lower triangular matrix as a causal mask.
|
|||
|
|
causal_mask = torch.tril(
|
|||
|
|
torch.ones(
|
|||
|
|
(seq_len, seq_len), dtype=torch.bfloat16, device=moe_token_types.device
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
# Extended to the batch dimension.
|
|||
|
|
attention_mask = causal_mask.unsqueeze(0).expand(bs, -1, -1)
|
|||
|
|
|
|||
|
|
if positional_masks is not None and "padding_positions" in positional_masks:
|
|||
|
|
padding_positions = positional_masks["padding_positions"]
|
|||
|
|
# The padding is set to zero.
|
|||
|
|
attention_mask = torch.where(
|
|||
|
|
padding_positions[:, None, :],
|
|||
|
|
torch.zeros_like(attention_mask),
|
|||
|
|
attention_mask,
|
|||
|
|
)
|
|||
|
|
# The padding is set to zero.
|
|||
|
|
attention_mask = torch.where(
|
|||
|
|
padding_positions[:, :, None],
|
|||
|
|
torch.zeros_like(attention_mask),
|
|||
|
|
attention_mask,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Set all values in the moe1 section to 1, and disable the fast section.
|
|||
|
|
moe1_mask = (moe_token_types[:, :, None]) & (moe_token_types[:, None, :])
|
|||
|
|
|
|||
|
|
if (
|
|||
|
|
not self.config.causal_action_attention_mask
|
|||
|
|
): # If a causal action attention mask is not used, then all elements in the moe1 section are set to 1.
|
|||
|
|
attention_mask = torch.where(
|
|||
|
|
moe1_mask, torch.ones_like(attention_mask), attention_mask
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if (
|
|||
|
|
positional_masks is not None
|
|||
|
|
and "ar_predict_token_positions" in positional_masks
|
|||
|
|
):
|
|||
|
|
ar_predict_token_positions = positional_masks["ar_predict_token_positions"]
|
|||
|
|
moe1_mask = (moe_token_types[:, :, None]) & (
|
|||
|
|
ar_predict_token_positions[:, None, :]
|
|||
|
|
)
|
|||
|
|
attention_mask = torch.where(
|
|||
|
|
moe1_mask, torch.zeros_like(attention_mask), attention_mask
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if (
|
|||
|
|
positional_masks is not None
|
|||
|
|
and "valid_flow_action_positions" in positional_masks
|
|||
|
|
):
|
|||
|
|
# true in moe_token_types but false in valid_flow_action_positions
|
|||
|
|
nonvalid_flow_action_positions = (
|
|||
|
|
moe_token_types & ~positional_masks["valid_flow_action_positions"]
|
|||
|
|
)
|
|||
|
|
attention_mask = torch.where(
|
|||
|
|
nonvalid_flow_action_positions[:, None, :],
|
|||
|
|
torch.zeros_like(attention_mask),
|
|||
|
|
attention_mask,
|
|||
|
|
)
|
|||
|
|
attention_mask = torch.where(
|
|||
|
|
nonvalid_flow_action_positions[:, :, None],
|
|||
|
|
torch.zeros_like(attention_mask),
|
|||
|
|
attention_mask,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return attention_mask
|
|||
|
|
|
|||
|
|
def _update_joint_attention_flash_mask(
|
|||
|
|
self,
|
|||
|
|
attention_mask,
|
|||
|
|
moe_token_types,
|
|||
|
|
positional_masks,
|
|||
|
|
debug=False,
|
|||
|
|
):
|
|||
|
|
device = moe_token_types.device
|
|||
|
|
B, S = moe_token_types.shape
|
|||
|
|
i32 = torch.int32
|
|||
|
|
|
|||
|
|
# ---- Return vector initialization ----
|
|||
|
|
LTS = torch.ones((B, S), device=device, dtype=i32) * S
|
|||
|
|
UTE = (
|
|||
|
|
torch.arange(S, device=device, dtype=i32).unsqueeze(0).expand(B, S).clone()
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Handling padding positions
|
|||
|
|
if positional_masks is not None and "padding_positions" in positional_masks:
|
|||
|
|
padding_positions = positional_masks["padding_positions"]
|
|||
|
|
LTS[padding_positions] = 0
|
|||
|
|
UTE[padding_positions] = S
|
|||
|
|
|
|||
|
|
# Handling ar predict tokens
|
|||
|
|
if (
|
|||
|
|
positional_masks is not None
|
|||
|
|
and "ar_predict_token_positions" in positional_masks
|
|||
|
|
):
|
|||
|
|
start_ar_pos, end_ar_pos = find_first_last_ones(
|
|||
|
|
positional_masks["ar_predict_token_positions"]
|
|||
|
|
)
|
|||
|
|
for bs_i in range(B):
|
|||
|
|
if end_ar_pos[bs_i] != -1:
|
|||
|
|
LTS[bs_i, positional_masks["ar_predict_token_positions"][bs_i]] = (
|
|||
|
|
end_ar_pos[bs_i].to(i32) + 1
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Handling flow action bidirectional mask
|
|||
|
|
flow_mask = moe_token_types == 1
|
|||
|
|
if not self.config.causal_action_attention_mask:
|
|||
|
|
start_flow_pos, end_flow_pos = find_first_last_ones(flow_mask)
|
|||
|
|
for bs_i in range(B):
|
|||
|
|
if start_flow_pos[bs_i] != -1:
|
|||
|
|
UTE[bs_i, flow_mask[bs_i]] = start_flow_pos[bs_i].to(i32)
|
|||
|
|
|
|||
|
|
# Handling validate flow
|
|||
|
|
if (
|
|||
|
|
positional_masks is not None
|
|||
|
|
and "valid_flow_action_positions" in positional_masks
|
|||
|
|
):
|
|||
|
|
flow_mask = moe_token_types == 1
|
|||
|
|
nonvalid_flow_action_positions = (
|
|||
|
|
flow_mask & ~positional_masks["valid_flow_action_positions"]
|
|||
|
|
)
|
|||
|
|
if nonvalid_flow_action_positions.any():
|
|||
|
|
LTS[nonvalid_flow_action_positions] = 0
|
|||
|
|
UTE[nonvalid_flow_action_positions] = S
|
|||
|
|
|
|||
|
|
LTS = LTS.unsqueeze(-1)
|
|||
|
|
UTE = UTE.unsqueeze(-1)
|
|||
|
|
|
|||
|
|
startend_row_indices = torch.cat([LTS, UTE], dim=-1)
|
|||
|
|
# startend_row_indices = LTS
|
|||
|
|
|
|||
|
|
# add num_heads dimension
|
|||
|
|
startend_row_indices = startend_row_indices.unsqueeze(1)
|
|||
|
|
|
|||
|
|
return startend_row_indices
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ActionGenerationMixin(GenerationMixin):
|
|||
|
|
action_preprocessor: ActionProcessor
|
|||
|
|
|
|||
|
|
def to_bfloat16_for_selected_params(self, fsdp_plugin=None, accelerator=None):
|
|||
|
|
"""
|
|||
|
|
Keep some model parameters as float32, and convert others to bfloat16.
|
|||
|
|
- If `fsdp_plugin` exists, use FSDP v1's `mixed_precision` wrapper.
|
|||
|
|
- Otherwise, directly modify the parameter dtype.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def _assign_child(root_module, dotted_name: str, new_child):
|
|||
|
|
parts = dotted_name.split(".")
|
|||
|
|
parent = root_module
|
|||
|
|
for p in parts[:-1]:
|
|||
|
|
parent = getattr(parent, p)
|
|||
|
|
setattr(parent, parts[-1], new_child)
|
|||
|
|
|
|||
|
|
if fsdp_plugin:
|
|||
|
|
fsdp_version = getattr(fsdp_plugin, "fsdp_version", None)
|
|||
|
|
if fsdp_version != 1:
|
|||
|
|
raise RuntimeError("Only FSDP v1 is supported (fsdp_version=1).")
|
|||
|
|
|
|||
|
|
device = getattr(
|
|||
|
|
accelerator, "device", torch.device("cuda", torch.cuda.current_device())
|
|||
|
|
)
|
|||
|
|
if isinstance(device, torch.device) and device.type == "cuda":
|
|||
|
|
if device.index is not None:
|
|||
|
|
torch.cuda.set_device(device.index)
|
|||
|
|
device_id = device.index
|
|||
|
|
|
|||
|
|
# move model to device
|
|||
|
|
self = self.to(device)
|
|||
|
|
|
|||
|
|
# Define the mixed-precision strategy.
|
|||
|
|
bf16_policy = MP(
|
|||
|
|
param_dtype=torch.bfloat16,
|
|||
|
|
reduce_dtype=torch.float32,
|
|||
|
|
buffer_dtype=torch.bfloat16,
|
|||
|
|
cast_forward_inputs=False,
|
|||
|
|
cast_root_forward_inputs=False,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
fp32_policy = MP(
|
|||
|
|
param_dtype=torch.float32,
|
|||
|
|
reduce_dtype=torch.float32,
|
|||
|
|
buffer_dtype=torch.float32,
|
|||
|
|
cast_forward_inputs=False,
|
|||
|
|
cast_root_forward_inputs=False,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Step 1️⃣: Identify the top-level ActionProcessor module and wrap it separately with FSDP (FP32).
|
|||
|
|
for name, module in list(self.named_modules()):
|
|||
|
|
if isinstance(module, nn.Module) and any(
|
|||
|
|
k in name.lower() for k in ["action_preprocessor"]
|
|||
|
|
):
|
|||
|
|
if any(True for _ in module.children()):
|
|||
|
|
continue
|
|||
|
|
if getattr(module, "_fsdp_wrapped", False):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
print(f"[FSDP v1] wrapping module in FP32: {name}")
|
|||
|
|
wrapped = FSDP(
|
|||
|
|
module,
|
|||
|
|
mixed_precision=fp32_policy,
|
|||
|
|
sharding_strategy=torch.distributed.fsdp.ShardingStrategy.SHARD_GRAD_OP,
|
|||
|
|
backward_prefetch="BACKWARD_PRE",
|
|||
|
|
device_id=device_id,
|
|||
|
|
use_orig_params=True,
|
|||
|
|
)
|
|||
|
|
_assign_child(self, name, wrapped)
|
|||
|
|
setattr(wrapped, "_fsdp_wrapped", True)
|
|||
|
|
|
|||
|
|
# Step 2️⃣: The outermost layer uses unified FSDP (BF16 strategy).
|
|||
|
|
print("[FSDP v1] wrapping root model with bf16 mixed precision...")
|
|||
|
|
self = FSDP(
|
|||
|
|
self,
|
|||
|
|
mixed_precision=bf16_policy,
|
|||
|
|
sharding_strategy=torch.distributed.fsdp.ShardingStrategy.SHARD_GRAD_OP,
|
|||
|
|
backward_prefetch="BACKWARD_PRE",
|
|||
|
|
device_id=device_id,
|
|||
|
|
use_orig_params=True,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return self
|
|||
|
|
|
|||
|
|
# ----------------- Non-FSDP scenarios -----------------
|
|||
|
|
else:
|
|||
|
|
print("[INFO] Running manual dtype conversion (no FSDP).")
|
|||
|
|
self.to(dtype=torch.float32)
|
|||
|
|
|
|||
|
|
params_to_keep_float32 = []
|
|||
|
|
for name, _ in self.named_parameters():
|
|||
|
|
if any(
|
|||
|
|
k in name
|
|||
|
|
for k in [
|
|||
|
|
"input_layernorm",
|
|||
|
|
"post_attention_layernorm",
|
|||
|
|
"model.norm",
|
|||
|
|
"action_preprocessor",
|
|||
|
|
]
|
|||
|
|
):
|
|||
|
|
params_to_keep_float32.append(name)
|
|||
|
|
|
|||
|
|
for name, param in self.named_parameters():
|
|||
|
|
if name not in params_to_keep_float32:
|
|||
|
|
param.data = param.data.to(torch.bfloat16)
|
|||
|
|
|
|||
|
|
return self
|
|||
|
|
|
|||
|
|
def define_action_token_id(self):
|
|||
|
|
action_token_list = []
|
|||
|
|
if self.action_tokenizer_type:
|
|||
|
|
for i in range(self.action_tokenizer.vocab_size):
|
|||
|
|
action_token_id = self.processor.tokenizer.convert_tokens_to_ids(
|
|||
|
|
f"<|action_token_{i}|>"
|
|||
|
|
)
|
|||
|
|
action_token_list.append(action_token_id)
|
|||
|
|
|
|||
|
|
action_token_id = self.processor.tokenizer.convert_tokens_to_ids("<|action|>")
|
|||
|
|
propri_token_id = self.processor.tokenizer.convert_tokens_to_ids("<|propri|>")
|
|||
|
|
self.action_token_id_set = {
|
|||
|
|
"action_token_list": action_token_list,
|
|||
|
|
"propri_token_id": propri_token_id,
|
|||
|
|
"action_token_id": action_token_id,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def add_lora(
|
|||
|
|
self, r=8, lora_alpha=32, target_modules=["q_proj", "v_proj"], lora_dropout=0.1
|
|||
|
|
):
|
|||
|
|
"""Add LoRA adapter"""
|
|||
|
|
config = LoraConfig(
|
|||
|
|
r=r,
|
|||
|
|
lora_alpha=lora_alpha,
|
|||
|
|
target_modules=target_modules,
|
|||
|
|
lora_dropout=lora_dropout,
|
|||
|
|
bias="none",
|
|||
|
|
task_type="CAUSAL_LM",
|
|||
|
|
)
|
|||
|
|
self.model = get_peft_model(self.model, config)
|
|||
|
|
# Print trainable parameter information.
|
|||
|
|
self.model.print_trainable_parameters()
|
|||
|
|
|
|||
|
|
def compute_loss(
|
|||
|
|
self,
|
|||
|
|
hidden_states,
|
|||
|
|
logits,
|
|||
|
|
input_ids=None,
|
|||
|
|
dataset_names=None,
|
|||
|
|
labels=None,
|
|||
|
|
action_chunk=None,
|
|||
|
|
dof_mask=None,
|
|||
|
|
flow=None,
|
|||
|
|
flow_loss_mask=None,
|
|||
|
|
**kwargs,
|
|||
|
|
):
|
|||
|
|
if input_ids is not None:
|
|||
|
|
batch_size, seq_length = input_ids.shape
|
|||
|
|
|
|||
|
|
loss = 0
|
|||
|
|
cross_entropy_loss, flow_loss = None, None
|
|||
|
|
|
|||
|
|
# if dataset_names is not None:
|
|||
|
|
# unique_datasets_name = list(set(dataset_names))
|
|||
|
|
# channel_loss_dict = {
|
|||
|
|
# dataset_name: torch.tensor(0.0, device=logits.device)
|
|||
|
|
# for dataset_name in _ACTION_DATASET_NAMES + _MULTIMODAL_DATASET_NAMES
|
|||
|
|
# }
|
|||
|
|
# channel_loss_count_dict = {
|
|||
|
|
# dataset_name: torch.tensor(0, device=logits.device)
|
|||
|
|
# for dataset_name in _ACTION_DATASET_NAMES + _MULTIMODAL_DATASET_NAMES
|
|||
|
|
# }
|
|||
|
|
# else:
|
|||
|
|
unique_datasets_name, channel_loss_dict, channel_loss_count_dict = (
|
|||
|
|
None,
|
|||
|
|
None,
|
|||
|
|
None,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if labels is not None:
|
|||
|
|
action_accuracy = 0
|
|||
|
|
|
|||
|
|
shift_logits = logits[..., :-1, :].contiguous()
|
|||
|
|
shift_labels = labels[..., 1:].contiguous()
|
|||
|
|
shift_logits = shift_logits.view(-1, self.config.vocab_size)
|
|||
|
|
shift_labels = shift_labels.view(-1)
|
|||
|
|
# Enable model parallelism
|
|||
|
|
shift_labels = shift_labels.to(shift_logits.device)
|
|||
|
|
non_ignored_mask = shift_labels != -100
|
|||
|
|
_cross_entropy_loss = self.loss_fct(shift_logits, shift_labels)
|
|||
|
|
cross_entropy_loss = (
|
|||
|
|
_cross_entropy_loss[non_ignored_mask].mean()
|
|||
|
|
if non_ignored_mask.any()
|
|||
|
|
else torch.tensor(0.0, device=shift_logits.device)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# compute channel loss
|
|||
|
|
_cross_entropy_loss = _cross_entropy_loss.view(batch_size, seq_length - 1)
|
|||
|
|
non_ignored_mask = non_ignored_mask.view(batch_size, seq_length - 1)
|
|||
|
|
for dataset_name_i in unique_datasets_name:
|
|||
|
|
dataset_mask = torch.tensor(
|
|||
|
|
[name == dataset_name_i for name in dataset_names],
|
|||
|
|
device=logits.device,
|
|||
|
|
)
|
|||
|
|
combined_mask = dataset_mask.unsqueeze(1) & non_ignored_mask
|
|||
|
|
channel_loss_dict[dataset_name_i] = (
|
|||
|
|
_cross_entropy_loss[combined_mask].sum()
|
|||
|
|
if combined_mask.any()
|
|||
|
|
else torch.tensor(0.0, device=shift_logits.device)
|
|||
|
|
)
|
|||
|
|
channel_loss_count_dict[dataset_name_i] += combined_mask.sum()
|
|||
|
|
|
|||
|
|
if not torch.isnan(cross_entropy_loss):
|
|||
|
|
loss += cross_entropy_loss
|
|||
|
|
else:
|
|||
|
|
with torch.no_grad():
|
|||
|
|
cross_entropy_loss.detach()
|
|||
|
|
|
|||
|
|
# compute action token accuracy
|
|||
|
|
if len(self.action_token_id_set["action_token_list"]) > 0:
|
|||
|
|
shift_logits = logits[..., :-1, :].contiguous()
|
|||
|
|
action_preds = shift_logits.argmax(dim=-1)
|
|||
|
|
shift_labels = labels[..., 1:].contiguous()
|
|||
|
|
action_mask = (
|
|||
|
|
shift_labels > self.action_token_id_set["action_token_list"][0]
|
|||
|
|
)
|
|||
|
|
correct_preds = (action_preds == shift_labels) & action_mask
|
|||
|
|
action_accuracy = (
|
|||
|
|
correct_preds.sum().float() / action_mask.sum().float()
|
|||
|
|
)
|
|||
|
|
channel_loss_dict["action_accuracy"] = action_accuracy
|
|||
|
|
|
|||
|
|
if action_chunk is not None:
|
|||
|
|
action_mask = input_ids == self.action_token_id_set["action_token_id"]
|
|||
|
|
if action_mask.any():
|
|||
|
|
action_hidden_states = hidden_states[action_mask].to(torch.float32)
|
|||
|
|
flow = flow.reshape(-1, flow.shape[-1])
|
|||
|
|
_flow_loss = self.action_preprocessor.flow_loss(
|
|||
|
|
action_hidden_states, flow, action_chunk, dof_mask, flow_loss_mask
|
|||
|
|
)
|
|||
|
|
if isinstance(_flow_loss, torch.Tensor):
|
|||
|
|
flow_loss = _flow_loss.mean()
|
|||
|
|
loss += flow_loss * self.config.flow_loss_weight
|
|||
|
|
_flow_loss = _flow_loss.view(
|
|||
|
|
dof_mask.shape[0], dof_mask.shape[1], dof_mask.shape[2]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
loss,
|
|||
|
|
cross_entropy_loss,
|
|||
|
|
flow_loss,
|
|||
|
|
channel_loss_dict,
|
|||
|
|
channel_loss_count_dict,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AttentionsSelectorMixin:
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def _autoset_attn_implementation(
|
|||
|
|
cls,
|
|||
|
|
config,
|
|||
|
|
use_flash_attention_2: bool = False,
|
|||
|
|
torch_dtype: Optional[torch.dtype] = None,
|
|||
|
|
device_map: Optional[Union[str, Dict[str, int]]] = None,
|
|||
|
|
check_device_map: bool = True,
|
|||
|
|
):
|
|||
|
|
"""
|
|||
|
|
Automatically checks and dispatches to a default attention implementation. In order of priority:
|
|||
|
|
1. An implementation specified in `config._attn_implementation` (due for example to the argument attn_implementation="sdpa" in from_pretrained).
|
|||
|
|
2. DEPRECATED: if use_flash_attention_2 is set to `True` and `flash_attn` is available, flash attention. (`LlamaFlashAttention` for example)
|
|||
|
|
3. SDPA implementation, if available and supported by the model type. (`LlamaSdpaAttention` for example)
|
|||
|
|
4. The default model's implementation otherwise (`LlamaAttention` for example) .
|
|||
|
|
"""
|
|||
|
|
# Here we use config._attn_implementation_internal to check whether the attention implementation was explicitly set by the user.
|
|||
|
|
# The property `PretrainedConfig._attn_implementation` is never `None`, for backward compatibility (always fall back on "eager").
|
|||
|
|
# The `hasattr` here is used as some Transformers tests for some reason do not call PretrainedConfig __init__ (e.g. test_no_super_init_config_and_model)
|
|||
|
|
requested_attn_implementation = None
|
|||
|
|
if (
|
|||
|
|
hasattr(config, "_attn_implementation_internal")
|
|||
|
|
and config._attn_implementation_internal is not None
|
|||
|
|
):
|
|||
|
|
if (
|
|||
|
|
config._attn_implementation != "flash_attention_2"
|
|||
|
|
and use_flash_attention_2
|
|||
|
|
):
|
|||
|
|
raise ValueError(
|
|||
|
|
f'Both attn_implementation="{config._attn_implementation}" and `use_flash_attention_2=True` were used when loading the model, which are not compatible.'
|
|||
|
|
' We recommend to just use `attn_implementation="flash_attention_2"` when loading the model.'
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if (
|
|||
|
|
not isinstance(config._attn_implementation, dict)
|
|||
|
|
and config._attn_implementation
|
|||
|
|
not in ["eager"]
|
|||
|
|
+ ALL_ATTENTION_FUNCTIONS.valid_keys()
|
|||
|
|
+ X2ROBOT_ATTENTION_FUNCTIONS
|
|||
|
|
):
|
|||
|
|
message = f'Specified `attn_implementation="{config._attn_implementation}"` is not supported. The only possible arguments are `attn_implementation="eager"` (manual attention implementation)'
|
|||
|
|
if cls._supports_flash_attn_2:
|
|||
|
|
message += ', `"attn_implementation=flash_attention_2"` (implementation using flash attention 2)'
|
|||
|
|
if cls._supports_sdpa:
|
|||
|
|
message += ', `"attn_implementation=sdpa"` (implementation using torch.nn.functional.scaled_dot_product_attention)'
|
|||
|
|
if cls._supports_flex_attn:
|
|||
|
|
message += ', `"attn_implementation=flex_attention"` (implementation using torch\'s flex_attention)'
|
|||
|
|
raise ValueError(message + ".")
|
|||
|
|
|
|||
|
|
# If a config is passed with a preset attn_implementation, we skip the automatic dispatch and use the user-provided config, with hard checks that the requested attention implementation is available.
|
|||
|
|
requested_attn_implementation = config._attn_implementation_internal
|
|||
|
|
|
|||
|
|
if use_flash_attention_2:
|
|||
|
|
logger.warning_once(
|
|||
|
|
'The model was loaded with use_flash_attention_2=True, which is deprecated and may be removed in a future release. Please use `attn_implementation="flash_attention_2"` instead.'
|
|||
|
|
)
|
|||
|
|
config._attn_implementation = "flash_attention_2"
|
|||
|
|
|
|||
|
|
if config._attn_implementation == "flash_attention_2":
|
|||
|
|
cls._check_and_enable_flash_attn_2(
|
|||
|
|
config,
|
|||
|
|
torch_dtype=torch_dtype,
|
|||
|
|
device_map=device_map,
|
|||
|
|
hard_check_only=False,
|
|||
|
|
check_device_map=check_device_map,
|
|||
|
|
)
|
|||
|
|
elif requested_attn_implementation == "flex_attention":
|
|||
|
|
config = cls._check_and_enable_flex_attn(config, hard_check_only=True)
|
|||
|
|
elif (
|
|||
|
|
requested_attn_implementation in [None, "sdpa"]
|
|||
|
|
and not is_torch_xla_available()
|
|||
|
|
):
|
|||
|
|
# use_flash_attention_2 takes priority over SDPA, hence SDPA treated in this elif.
|
|||
|
|
config = cls._check_and_enable_sdpa(
|
|||
|
|
config,
|
|||
|
|
hard_check_only=(
|
|||
|
|
False if requested_attn_implementation is None else True
|
|||
|
|
),
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if (
|
|||
|
|
torch.version.hip is not None
|
|||
|
|
and config._attn_implementation == "sdpa"
|
|||
|
|
and torch.cuda.device_count() > 1
|
|||
|
|
and version.parse(torch.__version__) < version.parse("2.4.1")
|
|||
|
|
):
|
|||
|
|
logger.warning_once(
|
|||
|
|
"Using the `SDPA` attention implementation on multi-gpu setup with ROCM may lead to performance issues due to the FA backend. Disabling it to use alternative backends."
|
|||
|
|
)
|
|||
|
|
torch.backends.cuda.enable_flash_sdp(False)
|
|||
|
|
elif requested_attn_implementation in ALL_ATTENTION_FUNCTIONS.valid_keys():
|
|||
|
|
config._attn_implementation = requested_attn_implementation
|
|||
|
|
elif isinstance(requested_attn_implementation, dict):
|
|||
|
|
config._attn_implementation = None
|
|||
|
|
elif config._attn_implementation in X2ROBOT_ATTENTION_FUNCTIONS:
|
|||
|
|
pass
|
|||
|
|
else:
|
|||
|
|
config._attn_implementation = "eager"
|
|||
|
|
|
|||
|
|
config._attn_implementation_autoset = True
|
|||
|
|
return config
|
|||
|
|
|
|||
|
|
def _check_and_adjust_attn_implementation(
|
|||
|
|
self, attn_implementation: Optional[str], is_init_check: bool = False
|
|||
|
|
) -> str:
|
|||
|
|
assert (
|
|||
|
|
attn_implementation
|
|||
|
|
in ["eager", "flash_attention_2", "sdpa"] + X2ROBOT_ATTENTION_FUNCTIONS
|
|||
|
|
)
|
|||
|
|
return attn_implementation
|