Skip to content
Open
85 changes: 38 additions & 47 deletions src/paddlefleet/transformer/transformer_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from __future__ import annotations

import functools
import logging
import math
from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal
Expand All @@ -29,11 +30,14 @@
get_magic_init_method,
init_method_normal,
scaled_init_method_normal,
truncated_init_method_normal,
)

if TYPE_CHECKING:
from collections.abc import Callable

logger = logging.getLogger(__name__)


@dataclass
class TransformerConfig(ModelParallelConfig):
Expand Down Expand Up @@ -619,38 +623,27 @@ class TransformerConfig(ModelParallelConfig):
####################
init_method: callable = None
"""Method to initialize weights. Note that bias is always set to zero. Should be a function that
takes a single Tensor and initializes it. If None, will be set to
paddlefleet.utils.init_method_normal(init_method_std) which is paddle nn init normal with
mean=0.0 and std=init_method_std."""

embedding_init_method: Callable | None = None
"""
Method to initialize weights of the embedding layer. If None, will be set as described
in init_method above.
"""

embedding_init_method_std: float | None = None
"""
Standard deviation of the zero mean normal for the default initialization method for the
embedding layer. If None, will be set to init_method_std.
"""
takes a single Tensor and initializes it. If None, weights are initialized with a truncated
normal distribution N(0, sigma^2) clipped to
[-3*sigma, 3*sigma] with sigma=0.5/sqrt(hidden_size)."""

output_layer_init_method: callable = None
"""Method to initialize weights of the output layer of both attention and MLP blocks. If None,
will be set to paddlefleet.utils.scaled_init_method_normal(init_method_std) which is paddle nn
init normal with mean=0.0 and std=init_method_std / math.sqrt(2.0 * num_hidden_layers)."""

init_method_std: float = 0.02
init_method_std: float | None = None

This comment was marked as outdated.

"""Standard deviation of the zero mean normal for the default initialization method, not used if
init_method and output_layer_init_method are provided."""
init_method and output_layer_init_method are provided. If None, will be set to
0.5/sqrt(hidden_size) for truncated normal init or 0.02 when hidden_size is 0."""

embedding_init_method: callable = None
"""
Method to initialize weights of the embedding layer. If None, will be set as described
in init_method above.
"""

embedding_init_method_std: float = None
embedding_init_method_std: float | None = None
"""
Standard deviation of the zero mean normal for the default initialization method for the
embedding layer. If None, will be set to init_method_std.
Expand Down Expand Up @@ -1062,27 +1055,8 @@ def __post_init__(self):
if self.apply_query_key_layer_scaling:
self.attention_softmax_in_fp32 = True

# Set the embedding init method
if self.embedding_init_method_std is None:
# By default, use the same init std as you use for every other non-output layer.
self.embedding_init_method_std = self.init_method_std

if self.embedding_init_method is None:
if self.init_method is None or (
self.embedding_init_method_std != self.init_method_std
):
# In this case, we set both the init method and the embedding init method to
# whatever std value requested (or defaulted) for the embedding_init_layer
self.embedding_init_method = init_method_normal(
self.embedding_init_method_std
)
else:
# Replicate the current behavior where if you are not changing the std of the
# embedding init differently and the init method is set, we fallback to the
# init method for this layer. Since we are here after an OR we know that
# init_method is not None
self.embedding_init_method = self.init_method

# Force truncated normal initialization: magic_init is intentionally disabled.
self.magic_init = False

This comment was marked as outdated.

if self.magic_init:
if self.hidden_size == 0:
raise ValueError(
Expand All @@ -1091,8 +1065,30 @@ def __post_init__(self):
sigma = math.sqrt(0.3333 / self.hidden_size)
self.init_method = get_magic_init_method(sigma)
self.init_method_std = sigma
self.output_layer_init_method = self.init_method
self.embedding_init_method = self.init_method
self.embedding_init_method_std = self.init_method_std
elif self.init_method is None:
self.init_method = init_method_normal(self.init_method_std)
sigma = self.init_method_std
if sigma is None:
sigma = (
0.02
if self.hidden_size == 0
else 0.5 / math.sqrt(self.hidden_size)
)
self.init_method = truncated_init_method_normal(sigma)
self.init_method_std = sigma
if self.output_layer_init_method is None:
self.output_layer_init_method = self.init_method
if self.embedding_init_method is None:
self.embedding_init_method = self.init_method

This comment was marked as outdated.

logger.info(
f"[init] default truncated normal init: TruncNormal(0, sigma^2) clipped to "
f"[-3*sigma, 3*sigma], sigma={sigma}"
)

if self.init_method_std is None:
self.init_method_std = 0.02

if (
self.first_k_dense_replace
Expand Down Expand Up @@ -1148,9 +1144,7 @@ def __post_init__(self):
"recompute_granularity must be one of full and selective"
)

if self.magic_init:
self.output_layer_init_method = self.init_method
elif self.output_layer_init_method is None:
if self.output_layer_init_method is None:
self.output_layer_init_method = scaled_init_method_normal(
self.init_method_std,
self.num_hidden_layers,
Expand All @@ -1162,10 +1156,7 @@ def __post_init__(self):
# By default, use the same init std as you use for every other non-output layer.
self.embedding_init_method_std = self.init_method_std

if self.magic_init:
self.embedding_init_method = self.init_method
self.embedding_init_method_std = self.init_method_std
elif self.embedding_init_method is None:
if self.embedding_init_method is None:
if self.init_method is None or (
self.embedding_init_method_std != self.init_method_std
):
Expand Down
23 changes: 23 additions & 0 deletions src/paddlefleet/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,29 @@ def init_method(weight):
return init_method


def truncated_init_method_normal(sigma, truncate_factor=3.0):
"""Init method based on truncated normal N(0, sigma^2) clipped to
[-truncate_factor*sigma, truncate_factor*sigma].

Initialized under an fp32 default dtype guard to avoid numerical issues
in low-precision (e.g. bf16) default dtype. Independent from the other
init methods so existing behavior is unaffected.
"""

def init_method(weight):
dtype = paddle.get_default_dtype()
try:
paddle.set_default_dtype("float32")
bound = truncate_factor * sigma
paddle.nn.init.trunc_normal_(
weight, mean=0.0, std=sigma, a=-bound, b=bound
)
finally:
paddle.set_default_dtype(dtype)

return init_method


def get_pg_size(group=None):
"""Get world size for a distributed group.

Expand Down
17 changes: 15 additions & 2 deletions tests/multi_card_tests/pipeline_parallel/test_gpt_pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
PP_DEGREE = 4
REPO_FLAG = os.getenv("repo_flag")
SKIP_TESTS = REPO_FLAG != "paddlefleet"
CHECK_DETERMINISTIC_BASELINE = os.getenv("check_deterministic_baseline") == "1"


def get_gpu_models_via_nvidia_smi():
Expand Down Expand Up @@ -227,12 +228,24 @@ def test_pp(self):
pp = pprint.PrettyPrinter(depth=None, width=200, compact=False)
pp.pprint(rst)

if judge_machine_type() == "H":
assert paddle.isfinite(overlap_loss).item(), (
f"Loss is not finite: {overlap_loss.item()}"
)
assert overlap_loss.item() > 0, (
f"Loss should be positive: {overlap_loss.item()}"
)
for name, param in overlap_gpt_model.named_parameters():
if param.grad is not None:
assert paddle.all(paddle.isfinite(param.grad)).item(), (
f"{name}'s grad is not finite"
)

if CHECK_DETERMINISTIC_BASELINE and judge_machine_type() == "H":
assert overlap_loss._md5sum() == "bce3fed95247f1b7a165e32b33d6fca7"
if paddle.distributed.get_rank() == 0:
for name, p in overlap_gpt_model.named_parameters():
print(f" {name}: {p.grad._md5sum()}")
elif judge_machine_type() == "B":
elif CHECK_DETERMINISTIC_BASELINE and judge_machine_type() == "B":
print(f"[SKIP] loss MD5: {overlap_loss._md5sum()}")
if paddle.distributed.get_rank() == 0:
for name, p in overlap_gpt_model.named_parameters():
Expand Down
17 changes: 15 additions & 2 deletions tests/multi_card_tests/pipeline_parallel/test_gpt_pp_with_moe.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
# skip test for paddle pr 79368 merge
REPO_FLAG = os.getenv("repo_flag")
SKIP_TESTS = REPO_FLAG != "paddlefleet"
CHECK_DETERMINISTIC_BASELINE = os.getenv("check_deterministic_baseline") == "1"


def get_gpu_models_via_nvidia_smi():
Expand Down Expand Up @@ -247,7 +248,19 @@ def test_pp(self):
pp = pprint.PrettyPrinter(depth=None, width=200, compact=False)
pp.pprint(rst)

if judge_machine_type() == "H":
assert paddle.isfinite(overlap_loss).item(), (
f"Loss is not finite: {overlap_loss.item()}"
)
assert overlap_loss.item() > 0, (
f"Loss should be positive: {overlap_loss.item()}"
)
for name, param in overlap_gpt_model.named_parameters():
if param.grad is not None:
assert paddle.all(paddle.isfinite(param.grad)).item(), (
f"{name}'s grad is not finite"
)

if CHECK_DETERMINISTIC_BASELINE and judge_machine_type() == "H":
actual_md5 = overlap_loss._md5sum()
expected_md5 = "ba38c67745e4702582cf8b0004198aea"
print(
Expand Down Expand Up @@ -298,7 +311,7 @@ def test_pp(self):
assert param.grad._md5sum() == baseline[name], (
f"{name}'s grad has diff"
)
elif judge_machine_type() == "B":
elif CHECK_DETERMINISTIC_BASELINE and judge_machine_type() == "B":
assert overlap_loss._md5sum() == "cc2a9b0deaf25a56cc571465947c756a"
if paddle.distributed.get_rank() == 0:
baseline = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
# skip test for paddle pr 79368 merge
REPO_FLAG = os.getenv("repo_flag")
SKIP_TESTS = REPO_FLAG != "paddlefleet"
CHECK_DETERMINISTIC_BASELINE = os.getenv("check_deterministic_baseline") == "1"


def get_gpu_models_via_nvidia_smi():
Expand Down Expand Up @@ -244,7 +245,19 @@ def test_pp(self):
pp = pprint.PrettyPrinter(depth=None, width=200, compact=False)
pp.pprint(rst)

if judge_machine_type() == "H":
assert paddle.isfinite(overlap_loss).item(), (
f"Loss is not finite: {overlap_loss.item()}"
)
assert overlap_loss.item() > 0, (
f"Loss should be positive: {overlap_loss.item()}"
)
for name, param in overlap_gpt_model.named_parameters():
if param.grad is not None:
assert paddle.all(paddle.isfinite(param.grad)).item(), (
f"{name}'s grad is not finite"
)

if CHECK_DETERMINISTIC_BASELINE and judge_machine_type() == "H":
actual_md5 = overlap_loss._md5sum()
expected_md5 = "e5fdb6c3bc189ea3e4f2235f0e73353d"
print(
Expand Down Expand Up @@ -295,7 +308,7 @@ def test_pp(self):
assert param.grad._md5sum() == baseline[name], (
f"{name}'s grad has diff"
)
elif judge_machine_type() == "B":
elif CHECK_DETERMINISTIC_BASELINE and judge_machine_type() == "B":
assert overlap_loss._md5sum() == "a7d554835b295e80ec1211e740cfa188"
if paddle.distributed.get_rank() == 0:
baseline = {
Expand Down
11 changes: 7 additions & 4 deletions tests/multi_card_tests/test_gpt_model_dense_cp.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,14 @@ def run_cp(seed, batch_size, seq_len, vocab_size, config):
loss = gpt_pipe_model.forward_backward_pipeline(inputs)
loss.backward()

assert paddle.isfinite(loss).item(), f"Loss is not finite: {loss.item()}"
assert loss.item() > 0, f"Loss should be positive: {loss.item()}"
for name, param in gpt_model.named_parameters():
if param.grad is not None:
assert paddle.all(paddle.isfinite(param.grad)).item(), (
f"{name}'s grad is not finite"
)
print(f"actual loss: {loss.item()}")
loss_baseline = 7.212946
np.testing.assert_allclose(
np.array(loss), np.array(loss_baseline), rtol=1e-6, atol=1e-8
)


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,9 @@ def run_experimental_dataflow_cp_e2e():
if not paddle.all(paddle.isfinite(param.grad)).item():
nan_grad_count += 1

assert grad_count > 0, "No gradients were produced."
assert nan_grad_count == 0, f"Found {nan_grad_count} non-finite gradients."
print(f"actual loss: {loss.item()}")
loss_baseline = 8.547827
np.testing.assert_allclose(
np.array(loss), np.array(loss_baseline), rtol=1e-6, atol=1e-8
)


if __name__ == "__main__":
Expand Down
4 changes: 0 additions & 4 deletions tests/multi_card_tests/test_gpt_model_vha_cp.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,6 @@ def run_vha_cp_e2e():
assert loss.item() > 0, f"Loss should be positive: {loss.item()}"

print(f"actual loss: {loss.item()}")
loss_baseline = 8.556518
np.testing.assert_allclose(
np.array(loss), np.array(loss_baseline), rtol=1e-6, atol=1e-8
)

# Verify VHA parameter gradients exist and are finite
vha_param_names = ["premix_weight", "postmix_U", "postmix_V"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ def init_gate_weight(tensor):
moe_intermediate_size=8,
moe_shared_expert_gate=True,
init_method=init_method,
output_layer_init_method=init_method,
embedding_init_method=init_method,
)

def fake_mlp_init(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,11 @@ class TestTransformerConfigEdgeCases(unittest.TestCase):
"""Test edge cases and boundary conditions."""

def test_zero_hidden_layers(self):
# num_hidden_layers=0 causes ZeroDivisionError in output_layer_init_method
with self.assertRaises(ZeroDivisionError):
_make_config(num_hidden_layers=0)
cfg = _make_config(num_hidden_layers=0)
self.assertEqual(cfg.num_hidden_layers, 0)
self.assertIsNotNone(cfg.init_method)
self.assertIsNotNone(cfg.output_layer_init_method)
self.assertIsNotNone(cfg.embedding_init_method)

def test_large_hidden_size(self):
cfg = _make_config(hidden_size=4096, num_attention_heads=32)
Expand Down
Loading
Loading