Skip to content

profiler

birdnet_stm32.models.profiler

Model profiling: per-layer MACs, parameters, and activation memory.

Provides a simple profiling utility that inspects a Keras model and reports per-layer statistics useful for estimating NPU/MCU cost.

LayerProfile dataclass

Per-layer profiling result.

Attributes:

Name Type Description
name str

Layer name.

layer_type str

Keras layer class name.

output_shape str

Output shape as string.

params int

Number of trainable + non-trainable parameters.

macs int

Estimated multiply-accumulate operations.

activation_bytes int

Estimated activation memory in bytes (float32).

n6_supported bool

Whether this layer type is known to be N6 NPU-compatible.

Source code in birdnet_stm32/models/profiler.py
@dataclass
class LayerProfile:
    """Per-layer profiling result.

    Attributes:
        name: Layer name.
        layer_type: Keras layer class name.
        output_shape: Output shape as string.
        params: Number of trainable + non-trainable parameters.
        macs: Estimated multiply-accumulate operations.
        activation_bytes: Estimated activation memory in bytes (float32).
        n6_supported: Whether this layer type is known to be N6 NPU-compatible.
    """

    name: str
    layer_type: str
    output_shape: str
    params: int
    macs: int
    activation_bytes: int
    n6_supported: bool

profile_model(model)

Profile a Keras model and return per-layer statistics.

Parameters:

Name Type Description Default
model Model

Compiled or uncompiled Keras model.

required

Returns:

Type Description
list[LayerProfile]

List of LayerProfile for each layer.

Source code in birdnet_stm32/models/profiler.py
def profile_model(model: tf.keras.Model) -> list[LayerProfile]:
    """Profile a Keras model and return per-layer statistics.

    Args:
        model: Compiled or uncompiled Keras model.

    Returns:
        List of LayerProfile for each layer.
    """
    profiles = []
    for layer in model.layers:
        ltype = type(layer).__name__
        n6_ok = ltype in N6_SUPPORTED_OPS
        if ltype in N6_WARN_OPS:
            n6_ok = False

        out_shape = str(layer.output_shape) if hasattr(layer, "output_shape") else "?"
        try:
            params = layer.count_params()
        except ValueError:
            params = 0
        macs = _estimate_macs(layer)
        act_bytes = _activation_bytes(layer)

        profiles.append(
            LayerProfile(
                name=layer.name,
                layer_type=ltype,
                output_shape=out_shape,
                params=params,
                macs=macs,
                activation_bytes=act_bytes,
                n6_supported=n6_ok,
            )
        )
    return profiles

print_profile(model, warn_unsupported=True)

Print a formatted profiling table for a Keras model.

Parameters:

Name Type Description Default
model Model

Keras model to profile.

required
warn_unsupported bool

Print warnings for layers not known to be N6-compatible.

True
Source code in birdnet_stm32/models/profiler.py
def print_profile(model: tf.keras.Model, warn_unsupported: bool = True) -> None:
    """Print a formatted profiling table for a Keras model.

    Args:
        model: Keras model to profile.
        warn_unsupported: Print warnings for layers not known to be N6-compatible.
    """
    profiles = profile_model(model)
    total_params = sum(p.params for p in profiles)
    total_macs = sum(p.macs for p in profiles)
    total_act = sum(p.activation_bytes for p in profiles)

    print(f"\n{'Layer':<35} {'Type':<25} {'Output Shape':<25} {'Params':>10} {'MACs':>12} {'N6':>4}")
    print("-" * 115)
    warnings = []
    for p in profiles:
        n6_str = "OK" if p.n6_supported else "?"
        if not p.n6_supported and p.layer_type not in ("InputLayer",):
            warnings.append(p)
        print(f"{p.name:<35} {p.layer_type:<25} {p.output_shape:<25} {p.params:>10,} {p.macs:>12,} {n6_str:>4}")

    print("-" * 115)
    print(f"{'Total':<35} {'':<25} {'':<25} {total_params:>10,} {total_macs:>12,}")
    print(f"Activation memory: {total_act / 1024:.1f} KB (float32)")
    print(f"Model size: ~{total_params * 4 / 1024:.1f} KB (float32), ~{total_params / 1024:.1f} KB (INT8)")

    if warn_unsupported and warnings:
        print(f"\nWARNING: {len(warnings)} layer(s) have unknown N6 NPU compatibility:")
        for p in warnings:
            print(f"  - {p.name} ({p.layer_type})")

check_n6_compatibility(model)

Check model for layers with unknown N6 NPU compatibility.

Parameters:

Name Type Description Default
model Model

Keras model to check.

required

Returns:

Type Description
list[LayerProfile]

List of LayerProfile for layers not known to be N6-compatible

list[LayerProfile]

(excludes InputLayer).

Source code in birdnet_stm32/models/profiler.py
def check_n6_compatibility(model: tf.keras.Model) -> list[LayerProfile]:
    """Check model for layers with unknown N6 NPU compatibility.

    Args:
        model: Keras model to check.

    Returns:
        List of LayerProfile for layers not known to be N6-compatible
        (excludes InputLayer).
    """
    profiles = profile_model(model)
    return [p for p in profiles if not p.n6_supported and p.layer_type != "InputLayer"]