Skip to content

Commit 5ef1bd1

Browse files
Keep synthesized layer_norm gamma/beta at normalized_shape
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 c89f6a4 commit 5ef1bd1

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
@@ -1956,11 +1956,11 @@ def replace_layer_norm(
19561956
bias = None if node.args[3] is None else _get_operand(values_map, node, 3)
19571957
eps = node.args[4]
19581958

1959-
numel = int(np.prod(normalized_shape))
1959+
# Identity gamma/beta keep normalized_shape to match the declared `axes`.
19601960
if weight is None:
1961-
weight = coreai.constant([1.0] * numel, dtype=np.float32)
1961+
weight = coreai.constant(np.ones(normalized_shape, dtype=np.float32))
19621962
if bias is None:
1963-
bias = coreai.constant([0.0] * numel, dtype=np.float32)
1963+
bias = coreai.constant(np.zeros(normalized_shape, dtype=np.float32))
19641964

19651965
input_rank = x.type.rank
19661966
input_ele_type = x.type.element_type
@@ -2008,11 +2008,7 @@ def layer_norm(input: Value, gamma: Value, beta: Value) -> Value:
20082008
weight = coreai.cast(gamma, input_ele_type)
20092009
bias = coreai.cast(beta, input_ele_type)
20102010

2011-
# Reshape for multi-dim normalized_shape (e.g. [32] → [4, 8]).
2012-
if len(normalized_shape) > 1:
2013-
weight = coreai.reshape(weight, list(normalized_shape))
2014-
bias = coreai.reshape(bias, list(normalized_shape))
2015-
2011+
# gamma/beta always arrive shaped like normalized_shape.
20162012
# Broadcast gamma/beta to match the norm output shape.
20172013
norm_shape = coreai.get_shape(norm)
20182014
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
@@ -6015,6 +6015,39 @@ def forward(self, x: Tensor) -> Tensor:
60156015
""",
60166016
)
60176017

6018+
def test_multi_dim_normalized_shape_without_affine(self) -> None:
6019+
"""Synthesized gamma/beta keep normalized_shape, matching `axes`."""
6020+
6021+
class LayerNormModel(nn.Module):
6022+
def __init__(self):
6023+
super().__init__()
6024+
self.ln = nn.LayerNorm((4, 8), elementwise_affine=False)
6025+
6026+
def forward(self, x: Tensor) -> Tensor:
6027+
return self.ln(x)
6028+
6029+
ir = get_ir(LayerNormModel().eval(), x=torch.rand(2, 4, 8))
6030+
filecheck_pattern(
6031+
ir,
6032+
check_file="""
6033+
// CHECK-LABEL: module {
6034+
// 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"} {
6035+
// CHECK-NOT: coreai.reshape
6036+
// CHECK: %[[NORM:.*]] = coreai.decomposable.broadcasting_mul %{{.*}}, %{{.*}} : (tensor<2x4x8xf32>, tensor<2x1x1xf32>) -> tensor<2x4x8xf32>
6037+
// CHECK-NEXT: %[[SCALED:.*]] = coreai.decomposable.broadcasting_mul %[[NORM]], %[[GAMMA]] : (tensor<2x4x8xf32>, tensor<4x8xf32>) -> tensor<2x4x8xf32>
6038+
// CHECK-NEXT: %[[SHIFTED:.*]] = coreai.decomposable.broadcasting_add %[[SCALED]], %[[BETA]] : (tensor<2x4x8xf32>, tensor<4x8xf32>) -> tensor<2x4x8xf32>
6039+
// CHECK-NEXT: coreai.output %[[SHIFTED]] : tensor<2x4x8xf32>
6040+
// CHECK-NEXT: }
6041+
// CHECK-NEXT: coreai.graph @main(%[[X:.*]]: tensor<2x4x8xf32> {coreai.name = "x"}) -> (tensor<2x4x8xf32> {coreai.name = "{{.*}}"}) attributes {__coreai_pure__} {
6042+
// CHECK-NEXT: %[[ONE:.*]] = coreai.constant dense<1.000000e+00> : tensor<4x8xf32>
6043+
// CHECK-NEXT: %[[ZERO:.*]] = coreai.constant dense<0.000000e+00> : tensor<4x8xf32>
6044+
// CHECK-NEXT: %[[R:.*]] = coreai.invoke @layer_norm_{{.*}}(%[[X]], %[[ONE]], %[[ZERO]]) : (tensor<2x4x8xf32>, tensor<4x8xf32>, tensor<4x8xf32>) -> tensor<2x4x8xf32>
6045+
// CHECK-NEXT: coreai.output %[[R]] : tensor<2x4x8xf32>
6046+
// CHECK-NEXT: }
6047+
// CHECK-NEXT: }
6048+
""",
6049+
)
6050+
60186051

60196052
class TestNeScalarIR:
60206053
def test_static(self) -> None:

0 commit comments

Comments
 (0)