570 lines
22 KiB
Python
570 lines
22 KiB
Python
# Copyright (c) Microsoft Corporation.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# DeepSpeed Team
|
|
"""
|
|
Configurable AutoTP API
|
|
|
|
This module provides a unified specification for tensor parallel layer partitioning.
|
|
The design is inspired by Universal Checkpointing's SubparamShape and provides
|
|
a single, well-defined format that users can easily understand, customize, and extend.
|
|
"""
|
|
|
|
import re
|
|
from dataclasses import dataclass, field
|
|
from typing import List, Tuple, Union, Optional
|
|
from enum import Enum
|
|
from deepspeed.utils.logging import warning_once
|
|
|
|
|
|
class PartitionType(Enum):
|
|
"""How the layer should be partitioned for tensor parallelism."""
|
|
COLUMN = "column" # Partition output dim, AllReduce in backward
|
|
ROW = "row" # Partition input dim, AllReduce in forward
|
|
SKIP = "skip" # Do not partition this layer
|
|
|
|
|
|
@dataclass
|
|
class TPLayerSpec:
|
|
"""
|
|
Unified specification for tensor parallel layer partitioning.
|
|
|
|
This is inspired by Universal Checkpointing's SubparamShape but extended
|
|
for AutoTP's needs (forward/backward communication patterns).
|
|
|
|
The `shape` parameter supports at most 1-level nesting at the partition dimension:
|
|
- (3, -1) -> 3 equal-size sub-params
|
|
- ((q, k, v), -1) -> 3 unequal-size sub-params (1-level nesting)
|
|
|
|
Examples:
|
|
# Simple row-parallel layer (e.g., o_proj, down_proj)
|
|
TPLayerSpec(
|
|
patterns=[".*\\.o_proj$", ".*\\.down_proj$"],
|
|
partition_type=PartitionType.ROW,
|
|
)
|
|
|
|
# Simple column-parallel layer (e.g., q_proj, k_proj, v_proj)
|
|
TPLayerSpec(
|
|
patterns=[".*\\.[qkv]_proj$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
)
|
|
|
|
# Fused QKV - GLM style [Q, K, V] concatenated on dim 0
|
|
TPLayerSpec(
|
|
patterns=[".*\\.query_key_value\\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=(3, -1), # 3 equal sub-params, -1 = infer
|
|
partition_dim=0,
|
|
)
|
|
|
|
# Fused QKV - Bloom style [q1,k1,v1,q2,k2,v2,...]
|
|
TPLayerSpec(
|
|
patterns=[".*\\.query_key_value\\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
# No reshape needed, just split along dim 0
|
|
)
|
|
|
|
# GQA with different Q/K/V sizes (1-level nesting)
|
|
TPLayerSpec(
|
|
patterns=[".*\\.qkv_proj\\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=((q_size, k_size, v_size), -1), # Unequal sub-params
|
|
partition_dim=0,
|
|
)
|
|
|
|
# Chunked MLP (gate_up_proj)
|
|
TPLayerSpec(
|
|
patterns=[".*\\.gate_up_proj\\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=(2, -1), # [gate, up] packed
|
|
partition_dim=0,
|
|
)
|
|
|
|
# MoE FFN with expert dimension
|
|
TPLayerSpec(
|
|
patterns=[".*\\.experts\\..*\\.w1\\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=(num_experts, -1, hidden_in), # View as 3D
|
|
partition_dim=1, # Partition the hidden_out dimension
|
|
)
|
|
|
|
# Skip layer (e.g., MoE gate)
|
|
TPLayerSpec(
|
|
patterns=[".*\\.gate$", ".*\\.router$"],
|
|
partition_type=PartitionType.SKIP,
|
|
)
|
|
"""
|
|
|
|
# Layer identification - regex patterns to match parameter names
|
|
patterns: List[str]
|
|
|
|
# Partition type determines communication pattern
|
|
partition_type: PartitionType = PartitionType.COLUMN
|
|
|
|
# Optional: logical shape for partitioning
|
|
# - Use -1 for dimensions that should be inferred
|
|
# - Use tuple of ints at partition_dim for unequal sub-params (1-level nesting only)
|
|
# Examples:
|
|
# (3, -1) -> 3 equal sub-params
|
|
# ((4096, 1024, 1024), -1) -> 3 unequal sub-params (GQA)
|
|
# (n_experts, -1, hidden) -> MoE reshape
|
|
shape: Optional[Tuple[Union[int, Tuple[int, ...]], ...]] = None
|
|
|
|
# Which dimension to partition (after optional reshape)
|
|
# Default: 0 for COLUMN, 1 for ROW (standard 2D weight matrix)
|
|
partition_dim: Optional[int] = None
|
|
|
|
# Optional: model type constraint (only apply for specific models)
|
|
model_types: Optional[List[str]] = None
|
|
|
|
def __post_init__(self):
|
|
if isinstance(self.partition_type, str):
|
|
self.partition_type = PartitionType(self.partition_type.lower())
|
|
if self.shape is not None:
|
|
self.shape = self._normalize_shape(self.shape)
|
|
self._validate_shape_format()
|
|
|
|
@staticmethod
|
|
def _normalize_shape(shape):
|
|
if isinstance(shape, list):
|
|
return tuple(TPLayerSpec._normalize_shape(item) for item in shape)
|
|
if isinstance(shape, tuple):
|
|
return tuple(TPLayerSpec._normalize_shape(item) if isinstance(item, list) else item for item in shape)
|
|
return shape
|
|
|
|
def _validate_shape_format(self):
|
|
if not isinstance(self.shape, tuple):
|
|
raise ValueError("AutoTP shape must be a tuple of ints or a tuple at partition_dim.")
|
|
partition_dim = self.get_partition_dim()
|
|
if partition_dim < 0 or partition_dim >= len(self.shape):
|
|
raise ValueError(
|
|
f"AutoTP partition_dim {partition_dim} is out of range for shape length {len(self.shape)}.")
|
|
nested_tuple_seen = False
|
|
for idx, dim in enumerate(self.shape):
|
|
if isinstance(dim, tuple):
|
|
if idx != partition_dim:
|
|
raise ValueError(
|
|
f"AutoTP shape nested tuple only allowed at partition_dim={partition_dim}, got at {idx}.")
|
|
if nested_tuple_seen:
|
|
raise ValueError("AutoTP shape supports only 1-level nesting at partition_dim.")
|
|
nested_tuple_seen = True
|
|
if len(dim) == 0:
|
|
raise ValueError("AutoTP shape nested tuple cannot be empty.")
|
|
for val in dim:
|
|
if isinstance(val, tuple):
|
|
raise ValueError("AutoTP shape supports only 1-level nesting at partition_dim.")
|
|
if not isinstance(val, int) or val <= 0:
|
|
raise ValueError("AutoTP nested sub-parameter sizes must be positive integers.")
|
|
elif isinstance(dim, int):
|
|
if dim == 0 or dim < -1:
|
|
raise ValueError("AutoTP shape dimensions must be positive integers or -1.")
|
|
else:
|
|
raise ValueError("AutoTP shape must contain only integers or a tuple at partition_dim.")
|
|
|
|
def get_partition_dim(self) -> int:
|
|
"""Get effective partition dimension."""
|
|
if self.partition_dim is not None:
|
|
return self.partition_dim
|
|
# Default based on partition type for 2D weight matrices
|
|
return 0 if self.partition_type == PartitionType.COLUMN else 1
|
|
|
|
def has_unequal_sub_params(self) -> bool:
|
|
"""Check if this spec has unequal sub-parameters (nested tuple at partition_dim)."""
|
|
if self.shape is None:
|
|
return False
|
|
dim = self.get_partition_dim()
|
|
if dim >= len(self.shape):
|
|
return False
|
|
return isinstance(self.shape[dim], tuple)
|
|
|
|
def get_sub_param_sizes(self) -> Optional[Tuple[int, ...]]:
|
|
"""Get sub-parameter sizes if using unequal sub-params."""
|
|
if not self.has_unequal_sub_params():
|
|
return None
|
|
return self.shape[self.get_partition_dim()]
|
|
|
|
def get_num_sub_params(self) -> Optional[int]:
|
|
"""Get the number of sub-parameters."""
|
|
if self.shape is None:
|
|
return None
|
|
dim = self.get_partition_dim()
|
|
if dim >= len(self.shape):
|
|
return None
|
|
if isinstance(self.shape[dim], tuple):
|
|
return len(self.shape[dim])
|
|
elif isinstance(self.shape[dim], int) and self.shape[dim] > 0:
|
|
return self.shape[dim]
|
|
return None
|
|
|
|
def matches(self, param_name: str, model_type: Optional[str] = None) -> bool:
|
|
"""Check if this spec matches the given parameter."""
|
|
# Check model type constraint
|
|
if self.model_types:
|
|
if model_type is None:
|
|
return False
|
|
model_type_norm = str(model_type).lower()
|
|
model_types_norm = [str(mt).lower() for mt in self.model_types]
|
|
if model_type_norm not in model_types_norm:
|
|
return False
|
|
# Check pattern match
|
|
return any(re.match(pattern, param_name) for pattern in self.patterns)
|
|
|
|
|
|
@dataclass
|
|
class AutoTPConfig:
|
|
"""
|
|
Configuration for Automatic Tensor Parallelism.
|
|
|
|
Example usage:
|
|
config = AutoTPConfig(
|
|
tp_size=4,
|
|
layer_specs=[
|
|
# Row-parallel layers (AllReduce after forward)
|
|
TPLayerSpec(
|
|
patterns=[".*\\.o_proj", ".*\\.down_proj"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
# Column-parallel layers
|
|
TPLayerSpec(
|
|
patterns=[".*\\.[qkv]_proj", ".*\\.up_proj", ".*\\.gate_proj"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
# Skip MoE gates
|
|
TPLayerSpec(
|
|
patterns=[".*\\.gate$"],
|
|
partition_type=PartitionType.SKIP,
|
|
),
|
|
],
|
|
)
|
|
"""
|
|
|
|
tp_size: int = 1
|
|
|
|
# Unified layer specifications
|
|
layer_specs: List[TPLayerSpec] = field(default_factory=list)
|
|
|
|
# Embedding configuration
|
|
embedding_partition_dim: int = 1 # Usually partition vocab dim
|
|
|
|
# LM head configuration
|
|
lm_head_patterns: List[str] = field(default_factory=lambda: ["lm_head", "embed_out"])
|
|
|
|
# Behavior flags
|
|
use_default_specs: bool = True # Merge with built-in specs
|
|
strict_mode: bool = False # Fail if unmatched Linear layers found
|
|
|
|
def find_matching_spec(self, param_name: str, model_type: Optional[str] = None) -> Optional[TPLayerSpec]:
|
|
"""Find the first matching spec for a parameter."""
|
|
matches = [spec for spec in self.layer_specs if spec.matches(param_name, model_type)]
|
|
if not matches:
|
|
return None
|
|
if len(matches) > 1:
|
|
matched_patterns = [spec.patterns for spec in matches]
|
|
warning_once(f"AutoTPConfig: parameter {param_name} matched multiple layer_specs {matched_patterns}; "
|
|
"using the first match.")
|
|
return matches[0]
|
|
|
|
@classmethod
|
|
def from_dict(cls, config_dict: dict) -> "AutoTPConfig":
|
|
"""Create config from dictionary (JSON config)."""
|
|
layer_specs = []
|
|
for spec_dict in config_dict.get("layer_specs", []):
|
|
# Convert partition_type string to enum
|
|
partition_type_str = spec_dict.get("partition_type", "column")
|
|
if isinstance(partition_type_str, str):
|
|
partition_type = PartitionType(partition_type_str.lower())
|
|
else:
|
|
partition_type = partition_type_str
|
|
|
|
# Convert shape from list to tuple if necessary
|
|
shape = spec_dict.get("shape")
|
|
if shape is not None:
|
|
shape = cls._convert_shape(shape)
|
|
|
|
layer_specs.append(
|
|
TPLayerSpec(
|
|
patterns=spec_dict.get("patterns", []),
|
|
partition_type=partition_type,
|
|
shape=shape,
|
|
partition_dim=spec_dict.get("partition_dim"),
|
|
model_types=spec_dict.get("model_types"),
|
|
))
|
|
|
|
return cls(
|
|
tp_size=config_dict.get("tp_size", 1),
|
|
layer_specs=layer_specs,
|
|
embedding_partition_dim=config_dict.get("embedding_partition_dim", 1),
|
|
lm_head_patterns=config_dict.get("lm_head_patterns", ["lm_head", "embed_out"]),
|
|
use_default_specs=config_dict.get("use_default_specs", True),
|
|
strict_mode=config_dict.get("strict_mode", False),
|
|
)
|
|
|
|
@staticmethod
|
|
def _convert_shape(shape):
|
|
"""Convert shape from list to tuple, handling nested structures."""
|
|
if isinstance(shape, list):
|
|
return tuple(AutoTPConfig._convert_shape(item) if isinstance(item, list) else item for item in shape)
|
|
return shape
|
|
|
|
|
|
class AutoTPPresets:
|
|
"""Built-in presets for common model architectures."""
|
|
|
|
@staticmethod
|
|
def llama() -> AutoTPConfig:
|
|
"""LLaMA-style models (separate Q, K, V projections)."""
|
|
return AutoTPConfig(layer_specs=[
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.o_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.[qkv]_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.down_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.(up|gate)_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def llama_gqa(num_heads: int, num_kv_heads: int, head_dim: int) -> AutoTPConfig:
|
|
"""LLaMA with Grouped Query Attention (fused QKV variant)."""
|
|
q_size = num_heads * head_dim
|
|
kv_size = num_kv_heads * head_dim
|
|
return AutoTPConfig(
|
|
layer_specs=[
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.o_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
# Fused QKV with unequal sizes (GQA)
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.qkv_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=((q_size, kv_size, kv_size), -1), # 1-level nesting
|
|
partition_dim=0,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.down_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.(up|gate)_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def bloom() -> AutoTPConfig:
|
|
"""BLOOM-style models (fused QKV with interleaved heads)."""
|
|
return AutoTPConfig(
|
|
layer_specs=[
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attention\.dense\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attention\.query_key_value\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
# Bloom style: [q1,k1,v1,q2,k2,v2,...] - no reshape needed
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.dense_4h_to_h\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.dense_h_to_4h\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def chatglm() -> AutoTPConfig:
|
|
"""ChatGLM-style models (GLM-style fused QKV)."""
|
|
return AutoTPConfig(
|
|
layer_specs=[
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attention\.dense\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attention\.query_key_value\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=(3, -1), # [Q, K, V] concatenated
|
|
partition_dim=0,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.dense_4h_to_h\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.dense_h_to_4h\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=(2, -1), # [gate, up] packed
|
|
partition_dim=0,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def mixtral() -> AutoTPConfig:
|
|
"""Mixtral MoE model."""
|
|
return AutoTPConfig(
|
|
layer_specs=[
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.o_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.[qkv]_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
# MoE experts
|
|
TPLayerSpec(
|
|
patterns=[r".*\.block_sparse_moe\.experts\.\d+\.w2\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.block_sparse_moe\.experts\.\d+\.w[13]\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
# Skip MoE gate
|
|
TPLayerSpec(
|
|
patterns=[r".*\.block_sparse_moe\.gate\.weight$"],
|
|
partition_type=PartitionType.SKIP,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def deepseek_v2() -> AutoTPConfig:
|
|
"""DeepSeek-V2 with MLA (Multi-head Latent Attention)."""
|
|
return AutoTPConfig(
|
|
layer_specs=[
|
|
# Standard attention output
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.o_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
# MLA uses compressed KV, skip low-rank projections
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.(q_a_proj|kv_a_proj_with_mqa)\.weight$"],
|
|
partition_type=PartitionType.SKIP,
|
|
),
|
|
# Q/K/V projections from latent
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.(q_b_proj|kv_b_proj)\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
# MoE experts
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.experts\.\d+\.down_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.experts\.\d+\.(up|gate)_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
# Skip MoE gate
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.gate\.weight$"],
|
|
partition_type=PartitionType.SKIP,
|
|
),
|
|
# Shared expert
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.shared_experts\.down_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.shared_experts\.(up|gate)_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def qwen2() -> AutoTPConfig:
|
|
"""Qwen2 model."""
|
|
return AutoTPConfig(layer_specs=[
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.o_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.[qkv]_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.down_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.(up|gate)_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def phi3() -> AutoTPConfig:
|
|
"""Phi3 model with fused QKV and chunked MLP."""
|
|
return AutoTPConfig(
|
|
layer_specs=[
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.o_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
# Phi3 has fused qkv_proj
|
|
TPLayerSpec(
|
|
patterns=[r".*\.self_attn\.qkv_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=(3, -1), # [Q, K, V] concatenated
|
|
partition_dim=0,
|
|
),
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.down_proj\.weight$"],
|
|
partition_type=PartitionType.ROW,
|
|
),
|
|
# Phi3 has gate_up_proj fused
|
|
TPLayerSpec(
|
|
patterns=[r".*\.mlp\.gate_up_proj\.weight$"],
|
|
partition_type=PartitionType.COLUMN,
|
|
shape=(2, -1), # [gate, up] packed
|
|
partition_dim=0,
|
|
),
|
|
], )
|
|
|
|
@staticmethod
|
|
def get_preset(model_type: str) -> Optional[AutoTPConfig]:
|
|
"""Get a preset configuration by model type name."""
|
|
presets = {
|
|
"llama": AutoTPPresets.llama,
|
|
"bloom": AutoTPPresets.bloom,
|
|
"chatglm": AutoTPPresets.chatglm,
|
|
"mixtral": AutoTPPresets.mixtral,
|
|
"deepseek_v2": AutoTPPresets.deepseek_v2,
|
|
"qwen2": AutoTPPresets.qwen2,
|
|
"phi3": AutoTPPresets.phi3,
|
|
}
|
|
preset_fn = presets.get(model_type.lower())
|
|
if preset_fn:
|
|
return preset_fn()
|
|
return None
|
|
|
|
|
|
def merge_autotp_configs(base: AutoTPConfig, override: AutoTPConfig) -> AutoTPConfig:
|
|
"""Merge two AutoTP configs, with override taking precedence."""
|
|
# Combine layer specs - override specs come first (higher priority)
|
|
merged_specs = list(override.layer_specs) + list(base.layer_specs)
|
|
|
|
return AutoTPConfig(
|
|
tp_size=override.tp_size if override.tp_size > 1 else base.tp_size,
|
|
layer_specs=merged_specs,
|
|
embedding_partition_dim=override.embedding_partition_dim,
|
|
lm_head_patterns=override.lm_head_patterns or base.lm_head_patterns,
|
|
use_default_specs=override.use_default_specs,
|
|
strict_mode=override.strict_mode,
|
|
)
|