Skip to content

Commit 5fbd5bd

Browse files
Keep synthesized layer_norm gamma/beta at normalized_shape (#55)
When elementwise_affine=False, replace_layer_norm built the identity gamma/beta as a flat numel-element vector, so a layer_norm over normalized_shape (4, 8) produced a composite taking tensor<32xf32> gamma/beta while its declaration said axes = [1, 2]. The body reshaped them back before broadcasting, so numerics were correct, but the composite boundary was inconsistent with the declared axes and differed from the elementwise_affine=True case, where ATen's real params arrive already shaped (4, 8). A consumer that implements the composite from its declaration instead of inlining the body sees a rank-1 gamma against two normalized axes. Build the constants with np.ones/np.zeros at normalized_shape so both paths present the same interface, and drop the now-dead reshape.
1 parent 46d15b7 commit 5fbd5bd

2 files changed

Lines changed: 37 additions & 8 deletions

File tree

coreai_torch/_aten_to_core.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,11 +1970,11 @@ def replace_layer_norm(
19701970
bias = None if node.args[3] is None else _get_operand(values_map, node, 3)
19711971
eps = node.args[4]
19721972

1973-
numel = int(np.prod(normalized_shape))
1973+
# Identity gamma/beta keep normalized_shape to match the declared `axes`.
19741974
if weight is None:
1975-
weight = coreai.constant([1.0] * numel, dtype=np.float32)
1975+
weight = coreai.constant(np.ones(normalized_shape, dtype=np.float32))
19761976
if bias is None:
1977-
bias = coreai.constant([0.0] * numel, dtype=np.float32)
1977+
bias = coreai.constant(np.zeros(normalized_shape, dtype=np.float32))
19781978

19791979
input_rank = x.type.rank
19801980
input_ele_type = x.type.element_type
@@ -2022,11 +2022,7 @@ def layer_norm(input: Value, gamma: Value, beta: Value) -> Value:
20222022
weight = coreai.cast(gamma, input_ele_type)
20232023
bias = coreai.cast(beta, input_ele_type)
20242024

2025-
# Reshape for multi-dim normalized_shape (e.g. [32] → [4, 8]).
2026-
if len(normalized_shape) > 1:
2027-
weight = coreai.reshape(weight, list(normalized_shape))
2028-
bias = coreai.reshape(bias, list(normalized_shape))
2029-
2025+
# gamma/beta always arrive shaped like normalized_shape.
20302026
# Broadcast gamma/beta to match the norm output shape.
20312027
norm_shape = coreai.get_shape(norm)
20322028
w_shape = coreai.constant(list(normalized_shape), dtype=np.uint32)

tests/ops/test_ops_ir.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6052,6 +6052,39 @@ def forward(self, x: Tensor) -> Tensor:
60526052
""",
60536053
)
60546054

6055+
def test_multi_dim_normalized_shape_without_affine(self) -> None:
6056+
"""Synthesized gamma/beta keep normalized_shape, matching `axes`."""
6057+
6058+
class LayerNormModel(nn.Module):
6059+
def __init__(self):
6060+
super().__init__()
6061+
self.ln = nn.LayerNorm((4, 8), elementwise_affine=False)
6062+
6063+
def forward(self, x: Tensor) -> Tensor:
6064+
return self.ln(x)
6065+
6066+
ir = get_ir(LayerNormModel().eval(), x=torch.rand(2, 4, 8))
6067+
filecheck_pattern(
6068+
ir,
6069+
check_file="""
6070+
// CHECK-LABEL: module {
6071+
// CHECK-NEXT: coreai.graph private noinline @layer_norm_{{.*}}(%[[INPUT:.*]]: tensor<2x4x8xf32> {coreai.name = "input"}, %[[GAMMA:.*]]: tensor<4x8xf32> {coreai.name = "gamma"}, %[[BETA:.*]]: tensor<4x8xf32> {coreai.name = "beta"}) -> tensor<2x4x8xf32> attributes {__coreai_pure__, composite_decl = #coreai.composite_declaration<"layer_norm" = {input_names = ["input", "gamma", "beta"], op_attrs = {axes = [1 : si64, 2 : si64], eps = 9.99999974E-6 : f32, version = 1 : si64}, output_names = ["output"]}>, template_op = "layer_norm"} {
6072+
// CHECK-NOT: coreai.reshape
6073+
// CHECK: %[[NORM:.*]] = coreai.decomposable.broadcasting_mul %{{.*}}, %{{.*}} : (tensor<2x4x8xf32>, tensor<2x1x1xf32>) -> tensor<2x4x8xf32>
6074+
// CHECK-NEXT: %[[SCALED:.*]] = coreai.decomposable.broadcasting_mul %[[NORM]], %[[GAMMA]] : (tensor<2x4x8xf32>, tensor<4x8xf32>) -> tensor<2x4x8xf32>
6075+
// CHECK-NEXT: %[[SHIFTED:.*]] = coreai.decomposable.broadcasting_add %[[SCALED]], %[[BETA]] : (tensor<2x4x8xf32>, tensor<4x8xf32>) -> tensor<2x4x8xf32>
6076+
// CHECK-NEXT: coreai.output %[[SHIFTED]] : tensor<2x4x8xf32>
6077+
// CHECK-NEXT: }
6078+
// CHECK-NEXT: coreai.graph @main(%[[X:.*]]: tensor<2x4x8xf32> {coreai.name = "x"}) -> (tensor<2x4x8xf32> {coreai.name = "{{.*}}"}) attributes {__coreai_pure__} {
6079+
// CHECK-NEXT: %[[ONE:.*]] = coreai.constant dense<1.000000e+00> : tensor<4x8xf32>
6080+
// CHECK-NEXT: %[[ZERO:.*]] = coreai.constant dense<0.000000e+00> : tensor<4x8xf32>
6081+
// CHECK-NEXT: %[[R:.*]] = coreai.invoke @layer_norm_{{.*}}(%[[X]], %[[ONE]], %[[ZERO]]) : (tensor<2x4x8xf32>, tensor<4x8xf32>, tensor<4x8xf32>) -> tensor<2x4x8xf32>
6082+
// CHECK-NEXT: coreai.output %[[R]] : tensor<2x4x8xf32>
6083+
// CHECK-NEXT: }
6084+
// CHECK-NEXT: }
6085+
""",
6086+
)
6087+
60556088

60566089
class TestNeScalarIR:
60576090
def test_static(self) -> None:

0 commit comments

Comments
 (0)