-
Notifications
You must be signed in to change notification settings - Fork 815
[JAX] Add sqrtsoftplus router score function #3448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,7 +60,7 @@ def _inject_router(request): | |
| jax.config.update("jax_use_shardy_partitioner", True) | ||
|
|
||
| from test_fused_router import ( | ||
| reference_topk_softmax_sigmoid, | ||
| reference_topk_with_score_function, | ||
| reference_compute_scores_for_aux_loss, | ||
| reference_aux_loss, | ||
| make_logits, | ||
|
|
@@ -133,7 +133,7 @@ def target_fwd(x): | |
|
|
||
| logits_shards = jnp.reshape(logits, (num_dp_devices, local_num_tokens, num_experts)) | ||
| ref_fwd_fn = jax.jit( | ||
| lambda x: reference_topk_softmax_sigmoid( | ||
| lambda x: reference_topk_with_score_function( | ||
| x, | ||
| topk=topk, | ||
| score_function=score_function, | ||
|
|
@@ -160,21 +160,23 @@ def target_fwd(x): | |
| ), "Routing map mismatch in distributed fused_topk" | ||
|
|
||
| # === Backward === | ||
| grad_weights = jnp.linspace(0.5, 1.5, num_experts, dtype=jnp.float32)[None, :] | ||
|
|
||
| def target_loss(x): | ||
| p, _ = fused_topk_with_score_function( | ||
| x, | ||
| topk=topk, | ||
| score_function=score_function, | ||
| ) | ||
| return jnp.sum(p) | ||
| return jnp.sum(p * grad_weights) | ||
|
|
||
| def ref_chunk_loss(x_chunk): | ||
| p, _ = reference_topk_softmax_sigmoid( | ||
| p, _ = reference_topk_with_score_function( | ||
| x_chunk, | ||
| topk=topk, | ||
| score_function=score_function, | ||
| ) | ||
| return jnp.sum(p) | ||
| return jnp.sum(p * grad_weights) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
|
|
||
| target_grad = jax.jit(jax.grad(target_loss))(logits_sharded) | ||
|
|
||
|
|
@@ -195,7 +197,7 @@ def ref_chunk_loss(x_chunk): | |
| "num_tokens,num_experts,topk", | ||
| TOPK_CASES, | ||
| ) | ||
| @pytest.mark.parametrize("score_function", ["softmax", "sigmoid"]) | ||
| @pytest.mark.parametrize("score_function", ["softmax", "sigmoid", "sqrtsoftplus"]) | ||
| def test_distributed_topk( | ||
| self, | ||
| device_count, | ||
|
|
@@ -293,22 +295,24 @@ def target_fwd(x): | |
| ), "Routing map mismatch in distributed score_for_aux_loss" | ||
|
|
||
| # === Backward === | ||
| grad_weights = jnp.linspace(0.5, 1.5, num_experts, dtype=jnp.float32)[None, :] | ||
|
|
||
| def target_loss(x): | ||
| s, _ = fused_topk_with_score_function( | ||
| x, | ||
| topk=topk, | ||
| score_function=score_function, | ||
| compute_aux_scores=True, | ||
| ) | ||
| return jnp.sum(s) | ||
| return jnp.sum(s * grad_weights) | ||
|
|
||
| def ref_chunk_loss(x_chunk): | ||
| _, s = reference_compute_scores_for_aux_loss( | ||
| x_chunk, | ||
| topk=topk, | ||
| score_function=score_function, | ||
| ) | ||
| return jnp.sum(s) | ||
| return jnp.sum(s * grad_weights) | ||
|
|
||
| target_grad = jax.jit(jax.grad(target_loss))(logits_sharded) | ||
|
|
||
|
|
@@ -329,7 +333,7 @@ def ref_chunk_loss(x_chunk): | |
| "num_tokens,num_experts,topk", | ||
| TOPK_CASES, | ||
| ) | ||
| @pytest.mark.parametrize("score_function", ["softmax", "sigmoid"]) | ||
| @pytest.mark.parametrize("score_function", ["softmax", "sigmoid", "sqrtsoftplus"]) | ||
| def test_distributed_score_for_aux_loss( | ||
| self, | ||
| device_count, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -90,9 +90,9 @@ def _inject_router(request): | |
| "L2": ALL_SCORE_AUX_LOSS_CASES, | ||
| } | ||
|
|
||
| ALL_SCORE_FUNCTIONS = ["softmax", "sigmoid"] | ||
| ALL_SCORE_FUNCTIONS = ["softmax", "sigmoid", "sqrtsoftplus"] | ||
| SCORE_FUNCTIONS = { | ||
| "L0": ["softmax"], | ||
| "L0": ["softmax", "sqrtsoftplus"], | ||
| "L2": ALL_SCORE_FUNCTIONS, | ||
| } | ||
|
|
||
|
|
@@ -166,7 +166,7 @@ def reference_group_limited_topk( | |
| return probs, top_indices | ||
|
|
||
|
|
||
| def reference_topk_softmax_sigmoid( | ||
| def reference_topk_with_score_function( | ||
| logits: jnp.ndarray, | ||
| topk: int, | ||
| use_pre_softmax: bool = False, | ||
|
|
@@ -176,7 +176,7 @@ def reference_topk_softmax_sigmoid( | |
| score_function: str = "softmax", | ||
| expert_bias: Optional[jnp.ndarray] = None, | ||
| ): | ||
| """Reference implementation for topk + softmax/sigmoid.""" | ||
| """Reference implementation for topk with a supported score function.""" | ||
| num_tokens, num_experts = logits.shape | ||
|
|
||
| def compute_topk(scores, topk, num_groups=None, group_topk=None): | ||
|
|
@@ -199,8 +199,11 @@ def compute_topk(scores, topk, num_groups=None, group_topk=None): | |
| else: | ||
| scores, top_indices = compute_topk(logits, topk, num_groups, group_topk) | ||
| probs = jax.nn.softmax(scores.astype(jnp.float32), axis=-1).astype(logits.dtype) | ||
| elif score_function == "sigmoid": | ||
| scores = jax.nn.sigmoid(logits.astype(jnp.float32)).astype(logits.dtype) | ||
| elif score_function in ("sigmoid", "sqrtsoftplus"): | ||
| if score_function == "sigmoid": | ||
| scores = jax.nn.sigmoid(logits.astype(jnp.float32)).astype(logits.dtype) | ||
| else: | ||
| scores = jnp.sqrt(jax.nn.softplus(logits.astype(jnp.float32))).astype(logits.dtype) | ||
| if expert_bias is not None: | ||
| scores_for_routing = scores + expert_bias | ||
| _, top_indices = compute_topk(scores_for_routing, topk, num_groups, group_topk) | ||
|
|
@@ -233,6 +236,9 @@ def reference_compute_scores_for_aux_loss(logits: jnp.ndarray, topk: int, score_ | |
| elif score_function == "sigmoid": | ||
| scores = jax.nn.sigmoid(logits.astype(jnp.float32)) | ||
| scores = scores / (scores.sum(axis=-1, keepdims=True) + 1e-20) if topk > 1 else scores | ||
| elif score_function == "sqrtsoftplus": | ||
| scores = jnp.sqrt(jax.nn.softplus(logits.astype(jnp.float32))) | ||
| scores = scores / (scores.sum(axis=-1, keepdims=True) + 1e-20) if topk > 1 else scores | ||
| else: | ||
| raise ValueError(f"Invalid score_function: {score_function}") | ||
|
|
||
|
|
@@ -269,7 +275,7 @@ def reference_aux_loss( | |
|
|
||
| def make_logits(num_tokens, num_experts, score_function, dtype=jnp.float32): | ||
| """Create deterministic logits for testing.""" | ||
| if score_function == "sigmoid": | ||
| if score_function in ("sigmoid", "sqrtsoftplus"): | ||
| offset = jnp.arange(-num_tokens // 2, num_tokens // 2, dtype=dtype) * 1e-4 | ||
| logits = jnp.arange(-num_experts // 2, num_experts // 2, dtype=dtype) * 1e-2 | ||
| logits = logits[None, :].repeat(num_tokens, axis=0) + offset[:, None] | ||
|
|
@@ -306,7 +312,7 @@ def run_topk_comparison( | |
| """Compare fused vs reference top-k implementation, both jitted.""" | ||
| logits = make_logits(num_tokens, num_experts, score_function, dtype) | ||
|
|
||
| if enable_bias and score_function == "sigmoid": | ||
| if enable_bias and score_function in ("sigmoid", "sqrtsoftplus"): | ||
| expert_bias = jnp.arange(num_experts, dtype=jnp.float32) * 0.1 | ||
| expert_bias = jnp.flip(expert_bias) | ||
| else: | ||
|
|
@@ -315,7 +321,7 @@ def run_topk_comparison( | |
| # Forward: reference (jitted) | ||
| ref_fwd_fn = jax.jit( | ||
| partial( | ||
| reference_topk_softmax_sigmoid, | ||
| reference_topk_with_score_function, | ||
| topk=topk, | ||
| use_pre_softmax=use_pre_softmax, | ||
| num_groups=num_groups, | ||
|
|
@@ -348,8 +354,10 @@ def run_topk_comparison( | |
| assert jnp.array_equal(routing_map_ref, routing_map_fused), "Routing map mismatch" | ||
|
|
||
| # Backward: reference (jitted) | ||
| grad_weights = jnp.linspace(0.5, 1.5, num_experts, dtype=jnp.float32)[None, :] | ||
|
|
||
| def loss_ref(logits_): | ||
| p, _ = reference_topk_softmax_sigmoid( | ||
| p, _ = reference_topk_with_score_function( | ||
| logits_, | ||
| topk, | ||
| use_pre_softmax, | ||
|
|
@@ -359,7 +367,7 @@ def loss_ref(logits_): | |
| score_function, | ||
| expert_bias, | ||
| ) | ||
| return p.sum() | ||
| return jnp.sum(p * grad_weights) | ||
|
|
||
| def loss_fused(logits_): | ||
| p, _ = fused_topk_with_score_function( | ||
|
|
@@ -372,7 +380,7 @@ def loss_fused(logits_): | |
| score_function, | ||
| expert_bias, | ||
| ) | ||
| return p.sum() | ||
| return jnp.sum(p * grad_weights) | ||
|
|
||
| grad_ref = jax.jit(jax.grad(loss_ref))(logits) | ||
| grad_fused = jax.jit(jax.grad(loss_fused))(logits) | ||
|
|
@@ -408,6 +416,55 @@ def test_topk_sigmoid( | |
| ) | ||
|
|
||
|
|
||
| @pytest_parametrize_wrapper("dtype", DTYPES) | ||
| @pytest_parametrize_wrapper( | ||
| "num_tokens,num_experts,topk", | ||
| TOPK_CASES, | ||
| ) | ||
| @pytest_parametrize_wrapper("group_topk", GROUP_TOPK_OPTIONS) | ||
| @pytest_parametrize_wrapper("scaling_factor", SCALING_FACTOR_OPTIONS) | ||
| @pytest_parametrize_wrapper("enable_bias", ENABLE_BIAS_OPTIONS) | ||
| @pytest.mark.triton | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not just in your PR but I saw a lot of mark.triton on this file and it actually does not use triton here (fused router is a CUDA kernel not triton kernel so I can make anoother PR to remove all the wrong marks) |
||
| def test_topk_sqrtsoftplus( | ||
| dtype, num_tokens, num_experts, topk, group_topk, scaling_factor, enable_bias | ||
| ): | ||
| num_groups = 8 if group_topk else None | ||
| run_topk_comparison( | ||
| dtype=dtype, | ||
| num_tokens=num_tokens, | ||
| num_experts=num_experts, | ||
| topk=topk, | ||
| use_pre_softmax=False, | ||
| num_groups=num_groups, | ||
| group_topk=group_topk, | ||
| scaling_factor=scaling_factor, | ||
| score_function="sqrtsoftplus", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can also just pytest parameterize the score_function, what do you think? |
||
| enable_bias=enable_bias, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.triton | ||
| def test_sqrtsoftplus_score_function_enum(): | ||
| from transformer_engine.jax.router import ScoreFunction | ||
|
|
||
| logits = make_logits(128, 32, "sqrtsoftplus") | ||
| string_fn = jax.jit( | ||
| partial(fused_topk_with_score_function, topk=4, score_function="sqrtsoftplus") | ||
| ) | ||
| enum_fn = jax.jit( | ||
| partial( | ||
| fused_topk_with_score_function, | ||
| topk=4, | ||
| score_function=ScoreFunction.SQRTSOFTPLUS, | ||
| ) | ||
| ) | ||
|
|
||
| string_probs, string_routing_map = string_fn(logits) | ||
| enum_probs, enum_routing_map = enum_fn(logits) | ||
| assert jnp.array_equal(string_probs, enum_probs) | ||
| assert jnp.array_equal(string_routing_map, enum_routing_map) | ||
|
Comment on lines
+447
to
+465
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not seem to be needed to test between enum and string to be passed into score_function |
||
|
|
||
|
|
||
| @pytest_parametrize_wrapper("dtype", DTYPES) | ||
| @pytest_parametrize_wrapper( | ||
| "num_tokens,num_experts,topk", | ||
|
|
@@ -477,9 +534,11 @@ def test_fused_scores_for_aux_loss(dtype, num_tokens, num_experts, topk, score_f | |
| assert jnp.array_equal(routing_map_ref, routing_map_fused), "Routing map mismatch" | ||
|
|
||
| # Backward (jitted) | ||
| grad_weights = jnp.linspace(0.5, 1.5, num_experts, dtype=jnp.float32)[None, :] | ||
|
|
||
| def loss_ref(logits_): | ||
| _, s = reference_compute_scores_for_aux_loss(logits_, topk, score_function) | ||
| return s.sum() | ||
| return jnp.sum(s * grad_weights) | ||
|
|
||
| def loss_fused(logits_): | ||
| s, _ = fused_topk_with_score_function( | ||
|
|
@@ -488,7 +547,7 @@ def loss_fused(logits_): | |
| score_function=score_function, | ||
| compute_aux_scores=True, | ||
| ) | ||
| return s.sum() | ||
| return jnp.sum(s * grad_weights) | ||
|
|
||
| grad_ref = jax.jit(jax.grad(loss_ref))(logits) | ||
| grad_fused = jax.jit(jax.grad(loss_fused))(logits) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure why we are adding this?