Skip to content

efficientnet

birdnet_stm32.models.efficientnet

N6-B0: an EfficientNet-B0 backbone for INT8 on the STM32N6 (2.2).

EfficientNet-B0's inverted-residual MBConv blocks (Tan & Le 2019) behind the project's audio frontends, with the three changes that make B0 survive INT8 on the Neural-ART NPU: ReLU6 instead of SiLU, no squeeze-and-excite, and a bounded residual stream (ReLU6 after each projection and residual add, so the stream stays in [0, 6] as in the DS-CNN blocks). B0 as published lost 14% of its WABAD AP to post-training quantization; N6-B0 trained to 2.2's float WABAD-61 AUPRC 0.465 (2.1 Raw: 0.374), INT8 0.441.

The block table and stochastic depth follow B0 at width and depth 1.0. The top 1x1 conv emits embeddings_size channels (1280), pooled by the same gap layer as the DS-CNN so the embedding loss and the head fine-tune find their layers.

mbconv_block(x, out_ch, expand, kernel, stride, drop_rate, name)

MBConv without squeeze-and-excite: 1x1 expand, k x k depthwise, 1x1 project, bounded residual.

Parameters:

Name Type Description Default
x Tensor

Input tensor [B, F, T, C].

required
out_ch int

Output channels of the projection.

required
expand int

Expansion ratio of the hidden width (1 skips the expand conv).

required
kernel int

Square depthwise kernel size.

required
stride int

Depthwise stride on both axes.

required
drop_rate float

Stochastic-depth rate on the residual branch.

required
name str

Base name for layers.

required

Returns:

Type Description
Tensor

Output tensor [B, F', T', out_ch].

Source code in birdnet_stm32/models/efficientnet.py
def mbconv_block(
    x: tf.Tensor,
    out_ch: int,
    expand: int,
    kernel: int,
    stride: int,
    drop_rate: float,
    name: str,
) -> tf.Tensor:
    """MBConv without squeeze-and-excite: 1x1 expand, k x k depthwise, 1x1 project, bounded residual.

    Args:
        x: Input tensor [B, F, T, C].
        out_ch: Output channels of the projection.
        expand: Expansion ratio of the hidden width (1 skips the expand conv).
        kernel: Square depthwise kernel size.
        stride: Depthwise stride on both axes.
        drop_rate: Stochastic-depth rate on the residual branch.
        name: Base name for layers.

    Returns:
        Output tensor [B, F', T', out_ch].
    """
    in_ch = int(x.shape[-1])
    y = x
    if expand != 1:
        y = layers.Conv2D(in_ch * expand, 1, padding="same", use_bias=False, name=f"{name}_expand_conv")(y)
        y = layers.BatchNormalization(name=f"{name}_expand_bn")(y)
        y = _relu6(f"{name}_expand_act")(y)
    y = layers.DepthwiseConv2D(kernel, strides=stride, padding="same", use_bias=False, name=f"{name}_dw")(y)
    y = layers.BatchNormalization(name=f"{name}_dw_bn")(y)
    y = _relu6(f"{name}_dw_act")(y)
    y = layers.Conv2D(out_ch, 1, padding="same", use_bias=False, name=f"{name}_project_conv")(y)
    y = layers.BatchNormalization(name=f"{name}_project_bn")(y)
    # B0's linear bottleneck is unbounded, and its outliers set coarse per-tensor INT8 grids.
    y = _relu6(f"{name}_project_relu")(y)
    if stride == 1 and in_ch == out_ch:
        if drop_rate > 0:
            y = layers.Dropout(drop_rate, noise_shape=(None, 1, 1, 1), name=f"{name}_drop")(y)
        y = layers.Add(name=f"{name}_add")([x, y])
        y = _relu6(f"{name}_add_relu")(y)
    return y

efficientnet_b0_backbone(x, embeddings_size, blocks=B0_BLOCKS)

N6-B0 stem, seven MBConv stages and the top conv on a frontend's [B, F, T, C] output.

Parameters:

Name Type Description Default
x Tensor

Frontend output.

required
embeddings_size int

Channels of the top 1x1 conv (the pooled embedding).

required
blocks tuple

The stage table, (expand, kernel, repeats, output channels, first stride) per stage; B0's by default.

B0_BLOCKS

Returns:

Type Description
Tensor

Feature map [B, F/32, T/32, embeddings_size].

Source code in birdnet_stm32/models/efficientnet.py
def efficientnet_b0_backbone(x: tf.Tensor, embeddings_size: int, blocks: tuple = B0_BLOCKS) -> tf.Tensor:
    """N6-B0 stem, seven MBConv stages and the top conv on a frontend's [B, F, T, C] output.

    Args:
        x: Frontend output.
        embeddings_size: Channels of the top 1x1 conv (the pooled embedding).
        blocks: The stage table, (expand, kernel, repeats, output channels, first stride) per stage; B0's by default.

    Returns:
        Feature map [B, F/32, T/32, embeddings_size].
    """
    x = layers.Conv2D(32, 3, strides=2, padding="same", use_bias=False, name="stem_conv")(x)
    x = layers.BatchNormalization(name="stem_bn")(x)
    x = _relu6("stem_act")(x)

    total = sum(reps for _, _, reps, _, _ in blocks)
    done = 0
    for si, (expand, kernel, reps, out_ch, stride) in enumerate(blocks, start=1):
        for bi in range(reps):
            x = mbconv_block(
                x,
                out_ch,
                expand=expand,
                kernel=kernel,
                stride=stride if bi == 0 else 1,
                drop_rate=DROP_CONNECT_RATE * done / total,
                name=f"block{si}{chr(ord('a') + bi)}",
            )
            done += 1

    x = layers.Conv2D(int(embeddings_size), 1, padding="same", use_bias=False, name="top_conv")(x)
    x = layers.BatchNormalization(name="top_bn")(x)
    return _relu6("top_act")(x)