mirror of
https://github.com/macaodha/batdetect2.git
synced 2025-06-29 22:51:58 +02:00
Add target index documentation
This commit is contained in:
parent
eda5f91c86
commit
22036743d1
@ -1,19 +1,53 @@
|
||||
"""Module that helps define what the training targets are.
|
||||
"""Main entry point for the BatDetect2 Target Definition subsystem.
|
||||
|
||||
The goal of this module is to configure how raw sound event annotations (tags)
|
||||
are processed to determine which events are relevant for training and what
|
||||
specific class label each relevant event should receive. Also, define how
|
||||
predicted class labels map back to the original tag system for interpretation.
|
||||
This package (`batdetect2.targets`) provides the tools and configurations
|
||||
necessary to define precisely what the BatDetect2 model should learn to detect
|
||||
and classify from audio data. It involves several conceptual steps, managed
|
||||
through configuration files and culminating in executable functions:
|
||||
|
||||
1. **Terms (`.terms`)**: Defining a controlled vocabulary for annotation tags.
|
||||
2. **Filtering (`.filtering`)**: Selecting relevant sound event annotations.
|
||||
3. **Transformation (`.transform`)**: Modifying tags (e.g., standardization,
|
||||
derivation).
|
||||
4. **Class Definition (`.classes`)**: Mapping tags to specific target class
|
||||
names (encoding) and defining how predicted class names map back to tags
|
||||
(decoding).
|
||||
|
||||
This module exposes the key components for users to configure and utilize this
|
||||
target definition pipeline, primarily through the `TargetConfig` data structure
|
||||
and the `Targets` class, which encapsulates the configured processing steps.
|
||||
"""
|
||||
|
||||
from batdetect2.targets.labels import (
|
||||
LabelConfig,
|
||||
generate_heatmaps,
|
||||
load_label_config,
|
||||
from typing import List, Optional
|
||||
|
||||
from soundevent import data
|
||||
|
||||
from batdetect2.configs import BaseConfig, load_config
|
||||
from batdetect2.targets.classes import (
|
||||
ClassesConfig,
|
||||
SoundEventDecoder,
|
||||
SoundEventEncoder,
|
||||
TargetClass,
|
||||
build_decoder_from_config,
|
||||
build_encoder_from_config,
|
||||
build_generic_class_tags_from_config,
|
||||
get_class_names_from_config,
|
||||
load_classes_config,
|
||||
load_decoder_from_config,
|
||||
load_encoder_from_config,
|
||||
)
|
||||
from batdetect2.targets.filtering import (
|
||||
FilterConfig,
|
||||
FilterRule,
|
||||
SoundEventFilter,
|
||||
build_filter_from_config,
|
||||
load_filter_config,
|
||||
load_filter_from_config,
|
||||
)
|
||||
from batdetect2.targets.terms import (
|
||||
TagInfo,
|
||||
TermInfo,
|
||||
TermRegistry,
|
||||
call_type,
|
||||
get_tag_from_info,
|
||||
get_term_from_key,
|
||||
@ -28,36 +62,387 @@ from batdetect2.targets.transform import (
|
||||
ReplaceRule,
|
||||
SoundEventTransformation,
|
||||
TransformConfig,
|
||||
build_transform_from_rule,
|
||||
build_transformation_from_config,
|
||||
derivation_registry,
|
||||
get_derivation,
|
||||
load_transformation_config,
|
||||
load_transformation_from_config,
|
||||
register_derivation,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"DerivationRegistry",
|
||||
"ClassesConfig",
|
||||
"DeriveTagRule",
|
||||
"LabelConfig",
|
||||
"FilterConfig",
|
||||
"FilterRule",
|
||||
"MapValueRule",
|
||||
"ReplaceRule",
|
||||
"SoundEventDecoder",
|
||||
"SoundEventEncoder",
|
||||
"SoundEventFilter",
|
||||
"SoundEventTransformation",
|
||||
"TagInfo",
|
||||
"TargetClass",
|
||||
"TargetConfig",
|
||||
"Targets",
|
||||
"TermInfo",
|
||||
"TransformConfig",
|
||||
"build_transform_from_rule",
|
||||
"build_decoder_from_config",
|
||||
"build_encoder_from_config",
|
||||
"build_filter_from_config",
|
||||
"build_generic_class_tags_from_config",
|
||||
"build_transformation_from_config",
|
||||
"call_type",
|
||||
"derivation_registry",
|
||||
"generate_heatmaps",
|
||||
"get_class_names_from_config",
|
||||
"get_derivation",
|
||||
"get_tag_from_info",
|
||||
"get_term_from_key",
|
||||
"individual",
|
||||
"load_label_config",
|
||||
"load_classes_config",
|
||||
"load_decoder_from_config",
|
||||
"load_encoder_from_config",
|
||||
"load_filter_config",
|
||||
"load_filter_from_config",
|
||||
"load_target_config",
|
||||
"load_transformation_config",
|
||||
"load_transformation_from_config",
|
||||
"register_derivation",
|
||||
"register_term",
|
||||
"term_registry",
|
||||
]
|
||||
|
||||
|
||||
class TargetConfig(BaseConfig):
|
||||
"""Unified configuration for the entire target definition pipeline.
|
||||
|
||||
This model aggregates the configurations for the optional filtering and
|
||||
transformation steps, and the mandatory class definition step. It serves as
|
||||
the primary input for building a complete `Targets` processing object.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
filtering : FilterConfig, optional
|
||||
Configuration for filtering sound event annotations. If None or
|
||||
omitted, no filtering is applied.
|
||||
transforms : TransformConfig, optional
|
||||
Configuration for transforming annotation tags. If None or omitted, no
|
||||
transformations are applied.
|
||||
classes : ClassesConfig
|
||||
Configuration defining the specific target classes, their matching
|
||||
rules, decoding rules (`output_tags`), and the generic class
|
||||
definition. This section is mandatory.
|
||||
"""
|
||||
|
||||
filtering: Optional[FilterConfig] = None
|
||||
|
||||
transforms: Optional[TransformConfig] = None
|
||||
|
||||
classes: ClassesConfig
|
||||
|
||||
|
||||
def load_target_config(
|
||||
path: data.PathLike,
|
||||
field: Optional[str] = None,
|
||||
) -> TargetConfig:
|
||||
"""Load the unified target configuration from a file.
|
||||
|
||||
Reads a configuration file (typically YAML) and validates it against the
|
||||
`TargetConfig` schema, potentially extracting data from a nested field.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : data.PathLike
|
||||
Path to the configuration file.
|
||||
field : str, optional
|
||||
Dot-separated path to a nested section within the file containing the
|
||||
target configuration. If None, the entire file content is used.
|
||||
|
||||
Returns
|
||||
-------
|
||||
TargetConfig
|
||||
The loaded and validated unified target configuration object.
|
||||
|
||||
Raises
|
||||
------
|
||||
FileNotFoundError
|
||||
If the config file path does not exist.
|
||||
yaml.YAMLError
|
||||
If the file content is not valid YAML.
|
||||
pydantic.ValidationError
|
||||
If the loaded configuration data does not conform to the
|
||||
`TargetConfig` schema (including validation within nested configs
|
||||
like `ClassesConfig`).
|
||||
KeyError, TypeError
|
||||
If `field` specifies an invalid path within the loaded data.
|
||||
"""
|
||||
return load_config(path=path, schema=TargetConfig, field=field)
|
||||
|
||||
|
||||
class Targets:
|
||||
"""Encapsulates the complete configured target definition pipeline.
|
||||
|
||||
This class holds the functions for filtering, transforming, encoding, and
|
||||
decoding annotations based on a loaded `TargetConfig`. It provides a
|
||||
high-level interface to apply these steps and access relevant metadata
|
||||
like class names and generic class tags.
|
||||
|
||||
Instances are typically created using the `Targets.from_config` or
|
||||
`Targets.from_file` classmethods.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
class_names : list[str]
|
||||
An ordered list of the unique names of the specific target classes
|
||||
defined in the configuration.
|
||||
generic_class_tags : List[data.Tag]
|
||||
A list of `soundevent.data.Tag` objects representing the configured
|
||||
generic class (e.g., the default 'Bat' class).
|
||||
"""
|
||||
|
||||
class_names: list[str]
|
||||
generic_class_tags: List[data.Tag]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
encode_fn: SoundEventEncoder,
|
||||
decode_fn: SoundEventDecoder,
|
||||
class_names: list[str],
|
||||
generic_class_tags: List[data.Tag],
|
||||
filter_fn: Optional[SoundEventFilter] = None,
|
||||
transform_fn: Optional[SoundEventTransformation] = None,
|
||||
):
|
||||
"""Initialize the Targets object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
encode_fn : SoundEventEncoder
|
||||
The configured function to encode annotations to class names.
|
||||
decode_fn : SoundEventDecoder
|
||||
The configured function to decode class names to tags.
|
||||
class_names : list[str]
|
||||
The ordered list of specific target class names.
|
||||
generic_class_tags : List[data.Tag]
|
||||
The list of tags representing the generic class.
|
||||
filter_fn : SoundEventFilter, optional
|
||||
The configured function to filter annotations. Defaults to None (no
|
||||
filtering).
|
||||
transform_fn : SoundEventTransformation, optional
|
||||
The configured function to transform annotation tags. Defaults to
|
||||
None (no transformation).
|
||||
"""
|
||||
self.class_names = class_names
|
||||
self.generic_class_tags = generic_class_tags
|
||||
|
||||
self._filter_fn = filter_fn
|
||||
self._encode_fn = encode_fn
|
||||
self._decode_fn = decode_fn
|
||||
self._transform_fn = transform_fn
|
||||
|
||||
def filter(self, sound_event: data.SoundEventAnnotation) -> bool:
|
||||
"""Apply the configured filter to a sound event annotation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sound_event : data.SoundEventAnnotation
|
||||
The annotation to filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if the annotation should be kept (passes the filter),
|
||||
False otherwise. If no filter was configured, always returns True.
|
||||
"""
|
||||
if not self._filter_fn:
|
||||
return True
|
||||
return self._filter_fn(sound_event)
|
||||
|
||||
def encode(self, sound_event: data.SoundEventAnnotation) -> Optional[str]:
|
||||
"""Encode a sound event annotation to its target class name.
|
||||
|
||||
Applies the configured class definition rules (including priority)
|
||||
to determine the specific class name for the annotation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sound_event : data.SoundEventAnnotation
|
||||
The annotation to encode. Note: This should typically be called
|
||||
*after* applying any transformations via the `transform` method.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str or None
|
||||
The name of the matched target class, or None if the annotation
|
||||
does not match any specific class rule (i.e., it belongs to the
|
||||
generic category).
|
||||
"""
|
||||
return self._encode_fn(sound_event)
|
||||
|
||||
def decode(self, class_label: str) -> List[data.Tag]:
|
||||
"""Decode a predicted class name back into representative tags.
|
||||
|
||||
Uses the configured mapping (based on `TargetClass.output_tags` or
|
||||
`TargetClass.tags`) to convert a class name string into a list of
|
||||
`soundevent.data.Tag` objects.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
class_label : str
|
||||
The class name to decode.
|
||||
|
||||
Returns
|
||||
-------
|
||||
List[data.Tag]
|
||||
The list of tags corresponding to the input class name.
|
||||
"""
|
||||
return self._decode_fn(class_label)
|
||||
|
||||
def transform(
|
||||
self, sound_event: data.SoundEventAnnotation
|
||||
) -> data.SoundEventAnnotation:
|
||||
"""Apply the configured tag transformations to an annotation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sound_event : data.SoundEventAnnotation
|
||||
The annotation whose tags should be transformed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data.SoundEventAnnotation
|
||||
A new annotation object with the transformed tags. If no
|
||||
transformations were configured, the original annotation object is
|
||||
returned.
|
||||
"""
|
||||
if self._transform_fn:
|
||||
return self._transform_fn(sound_event)
|
||||
return sound_event
|
||||
|
||||
@classmethod
|
||||
def from_config(
|
||||
cls,
|
||||
config: TargetConfig,
|
||||
term_registry: TermRegistry = term_registry,
|
||||
derivation_registry: DerivationRegistry = derivation_registry,
|
||||
) -> "Targets":
|
||||
"""Build a Targets object from a loaded TargetConfig.
|
||||
|
||||
This factory method takes the unified configuration object and
|
||||
constructs all the necessary functional components (filter, transform,
|
||||
encoder, decoder) and extracts metadata (class names, generic tags) to
|
||||
create a fully configured `Targets` instance.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
config : TargetConfig
|
||||
The loaded and validated unified target configuration object.
|
||||
term_registry : TermRegistry, optional
|
||||
The TermRegistry instance to use for resolving term keys. Defaults
|
||||
to the global `batdetect2.targets.terms.term_registry`.
|
||||
derivation_registry : DerivationRegistry, optional
|
||||
The DerivationRegistry instance to use for resolving derivation
|
||||
function names. Defaults to the global
|
||||
`batdetect2.targets.transform.derivation_registry`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Targets
|
||||
An initialized `Targets` object ready for use.
|
||||
|
||||
Raises
|
||||
------
|
||||
KeyError
|
||||
If term keys or derivation function keys specified in the `config`
|
||||
are not found in their respective registries.
|
||||
ImportError, AttributeError, TypeError
|
||||
If dynamic import of a derivation function fails (when configured).
|
||||
"""
|
||||
filter_fn = (
|
||||
build_filter_from_config(
|
||||
config.filtering,
|
||||
term_registry=term_registry,
|
||||
)
|
||||
if config.filtering
|
||||
else None
|
||||
)
|
||||
encode_fn = build_encoder_from_config(
|
||||
config.classes,
|
||||
term_registry=term_registry,
|
||||
)
|
||||
decode_fn = build_decoder_from_config(
|
||||
config.classes,
|
||||
term_registry=term_registry,
|
||||
)
|
||||
transform_fn = (
|
||||
build_transformation_from_config(
|
||||
config.transforms,
|
||||
term_registry=term_registry,
|
||||
derivation_registry=derivation_registry,
|
||||
)
|
||||
if config.transforms
|
||||
else None
|
||||
)
|
||||
class_names = get_class_names_from_config(config.classes)
|
||||
generic_class_tags = build_generic_class_tags_from_config(
|
||||
config.classes,
|
||||
term_registry=term_registry,
|
||||
)
|
||||
|
||||
return cls(
|
||||
filter_fn=filter_fn,
|
||||
encode_fn=encode_fn,
|
||||
decode_fn=decode_fn,
|
||||
class_names=class_names,
|
||||
generic_class_tags=generic_class_tags,
|
||||
transform_fn=transform_fn,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_file(
|
||||
cls,
|
||||
config_path: data.PathLike,
|
||||
field: Optional[str] = None,
|
||||
term_registry: TermRegistry = term_registry,
|
||||
derivation_registry: DerivationRegistry = derivation_registry,
|
||||
) -> "Targets":
|
||||
"""Load a Targets object directly from a configuration file.
|
||||
|
||||
This convenience factory method loads the `TargetConfig` from the
|
||||
specified file path and then calls `Targets.from_config` to build
|
||||
the fully initialized `Targets` object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
config_path : data.PathLike
|
||||
Path to the configuration file (e.g., YAML).
|
||||
field : str, optional
|
||||
Dot-separated path to a nested section within the file containing
|
||||
the target configuration. If None, the entire file content is used.
|
||||
term_registry : TermRegistry, optional
|
||||
The TermRegistry instance to use. Defaults to the global default.
|
||||
derivation_registry : DerivationRegistry, optional
|
||||
The DerivationRegistry instance to use. Defaults to the global
|
||||
default.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Targets
|
||||
An initialized `Targets` object ready for use.
|
||||
|
||||
Raises
|
||||
------
|
||||
FileNotFoundError, yaml.YAMLError, pydantic.ValidationError, KeyError,
|
||||
TypeError
|
||||
Errors raised during file loading, validation, or extraction via
|
||||
`load_target_config`.
|
||||
KeyError, ImportError, AttributeError, TypeError
|
||||
Errors raised during the build process by `Targets.from_config`
|
||||
(e.g., missing keys in registries, failed imports).
|
||||
"""
|
||||
config = load_target_config(
|
||||
config_path,
|
||||
field=field,
|
||||
)
|
||||
return cls.from_config(
|
||||
config,
|
||||
term_registry=term_registry,
|
||||
derivation_registry=derivation_registry,
|
||||
)
|
||||
|
@ -6,7 +6,12 @@ from pydantic import Field
|
||||
from soundevent import data
|
||||
|
||||
from batdetect2.configs import BaseConfig, load_config
|
||||
from batdetect2.targets.terms import TagInfo, get_tag_from_info
|
||||
from batdetect2.targets.terms import (
|
||||
TagInfo,
|
||||
TermRegistry,
|
||||
get_tag_from_info,
|
||||
term_registry,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"FilterConfig",
|
||||
@ -149,7 +154,10 @@ def equal_tags(
|
||||
return tags == sound_event_tags
|
||||
|
||||
|
||||
def build_filter_from_rule(rule: FilterRule) -> SoundEventFilter:
|
||||
def build_filter_from_rule(
|
||||
rule: FilterRule,
|
||||
term_registry: TermRegistry = term_registry,
|
||||
) -> SoundEventFilter:
|
||||
"""Creates a callable filter function from a single FilterRule.
|
||||
|
||||
Parameters
|
||||
@ -168,7 +176,10 @@ def build_filter_from_rule(rule: FilterRule) -> SoundEventFilter:
|
||||
ValueError
|
||||
If the rule contains an invalid `match_type`.
|
||||
"""
|
||||
tag_set = {get_tag_from_info(tag_info) for tag_info in rule.tags}
|
||||
tag_set = {
|
||||
get_tag_from_info(tag_info, term_registry=term_registry)
|
||||
for tag_info in rule.tags
|
||||
}
|
||||
|
||||
if rule.match_type == "any":
|
||||
return partial(has_any_tag, tags=tag_set)
|
||||
@ -188,39 +199,33 @@ def build_filter_from_rule(rule: FilterRule) -> SoundEventFilter:
|
||||
)
|
||||
|
||||
|
||||
def merge_filters(*filters: SoundEventFilter) -> SoundEventFilter:
|
||||
"""Combines multiple filter functions into a single filter function.
|
||||
|
||||
The resulting filter function applies AND logic: an annotation must pass
|
||||
*all* the input filters to pass the merged filter.
|
||||
def _passes_all_filters(
|
||||
sound_event_annotation: data.SoundEventAnnotation,
|
||||
filters: List[SoundEventFilter],
|
||||
) -> bool:
|
||||
"""Check if the annotation passes all provided filters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
*filters_with_rules : Tuple[FilterRule, SoundEventFilter]
|
||||
Variable number of tuples, each containing the original FilterRule
|
||||
and its corresponding filter function (SoundEventFilter).
|
||||
sound_event_annotation : data.SoundEventAnnotation
|
||||
The annotation to check.
|
||||
filters : List[SoundEventFilter]
|
||||
A list of filter functions to apply.
|
||||
|
||||
Returns
|
||||
-------
|
||||
SoundEventFilter
|
||||
A single function that returns True only if the annotation passes
|
||||
all the input filters.
|
||||
bool
|
||||
True if the annotation passes all filters, False otherwise.
|
||||
"""
|
||||
for filter_fn in filters:
|
||||
if not filter_fn(sound_event_annotation):
|
||||
logging.debug(
|
||||
f"Sound event annotation {sound_event_annotation.uuid} "
|
||||
f"excluded due to rule {filter_fn}",
|
||||
)
|
||||
return False
|
||||
|
||||
def merged_filter(
|
||||
sound_event_annotation: data.SoundEventAnnotation,
|
||||
) -> bool:
|
||||
for filter_fn in filters:
|
||||
if not filter_fn(sound_event_annotation):
|
||||
logging.debug(
|
||||
f"Sound event annotation {sound_event_annotation.uuid} "
|
||||
f"excluded due to rule {filter_fn}",
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
return merged_filter
|
||||
return True
|
||||
|
||||
|
||||
class FilterConfig(BaseConfig):
|
||||
@ -236,7 +241,10 @@ class FilterConfig(BaseConfig):
|
||||
rules: List[FilterRule] = Field(default_factory=list)
|
||||
|
||||
|
||||
def build_filter_from_config(config: FilterConfig) -> SoundEventFilter:
|
||||
def build_filter_from_config(
|
||||
config: FilterConfig,
|
||||
term_registry: TermRegistry = term_registry,
|
||||
) -> SoundEventFilter:
|
||||
"""Builds a merged filter function from a FilterConfig object.
|
||||
|
||||
Creates individual filter functions for each rule in the configuration
|
||||
@ -252,8 +260,11 @@ def build_filter_from_config(config: FilterConfig) -> SoundEventFilter:
|
||||
SoundEventFilter
|
||||
A single callable filter function that applies all defined rules.
|
||||
"""
|
||||
filters = [build_filter_from_rule(rule) for rule in config.rules]
|
||||
return merge_filters(*filters)
|
||||
filters = [
|
||||
build_filter_from_rule(rule, term_registry=term_registry)
|
||||
for rule in config.rules
|
||||
]
|
||||
return partial(_passes_all_filters, filters=filters)
|
||||
|
||||
|
||||
def load_filter_config(
|
||||
@ -278,7 +289,9 @@ def load_filter_config(
|
||||
|
||||
|
||||
def load_filter_from_config(
|
||||
path: data.PathLike, field: Optional[str] = None
|
||||
path: data.PathLike,
|
||||
field: Optional[str] = None,
|
||||
term_registry: TermRegistry = term_registry,
|
||||
) -> SoundEventFilter:
|
||||
"""Loads filter configuration from a file and builds the filter function.
|
||||
|
||||
@ -299,4 +312,4 @@ def load_filter_from_config(
|
||||
The final merged filter function ready to be used.
|
||||
"""
|
||||
config = load_filter_config(path=path, field=field)
|
||||
return build_filter_from_config(config)
|
||||
return build_filter_from_config(config, term_registry=term_registry)
|
||||
|
@ -26,18 +26,19 @@ from batdetect2.targets.terms import (
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"SoundEventTransformation",
|
||||
"MapValueRule",
|
||||
"DeriveTagRule",
|
||||
"ReplaceRule",
|
||||
"TransformConfig",
|
||||
"DerivationRegistry",
|
||||
"derivation_registry",
|
||||
"get_derivation",
|
||||
"DeriveTagRule",
|
||||
"MapValueRule",
|
||||
"ReplaceRule",
|
||||
"SoundEventTransformation",
|
||||
"TransformConfig",
|
||||
"build_transform_from_rule",
|
||||
"build_transformation_from_config",
|
||||
"derivation_registry",
|
||||
"get_derivation",
|
||||
"load_transformation_config",
|
||||
"load_transformation_from_config",
|
||||
"register_derivation",
|
||||
]
|
||||
|
||||
SoundEventTransformation = Callable[
|
||||
@ -671,3 +672,28 @@ def load_transformation_from_config(
|
||||
derivation_registry=derivation_registry,
|
||||
term_registry=term_registry,
|
||||
)
|
||||
|
||||
|
||||
def register_derivation(
|
||||
key: str,
|
||||
derivation: Derivation,
|
||||
derivation_registry: DerivationRegistry = derivation_registry,
|
||||
) -> None:
|
||||
"""Register a new derivation function in the global registry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
key : str
|
||||
The unique key to associate with the derivation function.
|
||||
derivation : Derivation
|
||||
The callable derivation function (takes str, returns str).
|
||||
derivation_registry : DerivationRegistry, optional
|
||||
The registry instance to register the derivation function with.
|
||||
Defaults to the global `derivation_registry`.
|
||||
|
||||
Raises
|
||||
------
|
||||
KeyError
|
||||
If a derivation function with the same key is already registered.
|
||||
"""
|
||||
derivation_registry.register(key, derivation)
|
||||
|
@ -1,6 +1,65 @@
|
||||
# Selecting Targets
|
||||
# Defining Training Targets
|
||||
|
||||
Hi!
|
||||
A crucial aspect of training any supervised machine learning model, including BatDetect2, is clearly defining the **training targets**.
|
||||
This process determines precisely what the model should learn to detect and recognize from the input data (in this case, spectrograms).
|
||||
The choices made here directly influence the model's focus, its performance, and how its predictions should be interpreted.
|
||||
|
||||
For BatDetect2, defining targets involves specifying:
|
||||
|
||||
- Which sounds in your annotated dataset are relevant for training.
|
||||
- How these sounds should be categorized into distinct **classes** (e.g., different species, types of calls, noise categories).
|
||||
- How the model's output (predicted class names) should be translated back into meaningful tags.
|
||||
|
||||
## Sound Event Annotations: The Starting Point
|
||||
|
||||
BatDetect2 assumes your training data consists of audio recordings where relevant sound events have been **annotated**.
|
||||
A typical annotation for a single sound event provides two key pieces of information:
|
||||
|
||||
1. **Location & Extent:** Information defining _where_ the sound occurs in time and frequency, usually represented as a **bounding box** drawn on a spectrogram.
|
||||
2. **Description (Tags):** Information _about_ the sound event, provided as a set of descriptive **tags**.
|
||||
|
||||
Tags are fundamental to how BatDetect2 understands annotations.
|
||||
Each tag is a **key-value pair**, much like labels used in many data systems.
|
||||
For example, a single echolocation pulse annotation might have tags like:
|
||||
|
||||
- `species: Myotis daubentonii`
|
||||
- `quality: Good`
|
||||
- `call_type: Echolocation`
|
||||
- `verified_by: ExpertA`
|
||||
|
||||
A key aspect is that a single sound event can (and often does) have **multiple tags** associated with it, allowing for rich, detailed descriptions capturing various facets of information (taxonomy, signal quality, functional type, verification status, etc.).
|
||||
|
||||
While this detailed, multi-tag approach is powerful for data representation, standard classification models typically require a single target class label for each training example.
|
||||
Therefore, the core task of the **target definition process** described in the following sections is to provide BatDetect2 with clear rules to:
|
||||
|
||||
- Interpret the meaning of different tag keys (**Terms**).
|
||||
- Select only the relevant annotations (**Filtering**).
|
||||
- Potentially standardize or modify the tags (**Transforming**).
|
||||
- Ultimately map the rich set of tags on each selected annotation to a single, definitive **target class** label for training (**Classes**).
|
||||
|
||||
## Configuration-Driven Workflow
|
||||
|
||||
BatDetect2 is designed so that researchers can configure this entire target definition process primarily through **configuration files** (typically written in YAML format), minimizing the need for direct programming for standard workflows.
|
||||
These settings are usually grouped under a main `targets:` key within your overall training configuration file.
|
||||
|
||||
## The Target Definition Steps
|
||||
|
||||
Defining the targets involves several sequential steps, each configurable and building upon the previous one:
|
||||
|
||||
1. **Defining Vocabulary:** Understand how annotations use tags (key-value pairs like `species: Myotis daubentonii`).
|
||||
This first step involves defining the meaning (**Terms**) behind the keys used in your tags (e.g., establishing what `species` or `call_type` represents using standard or custom definitions).
|
||||
Often, the default vocabulary provided by BatDetect2 is sufficient, so you may not need to configure this step explicitly.
|
||||
However, reading through this section is encouraged to understand how tags are formally defined via keys, which is essential for using them effectively in subsequent steps like filtering and class definition.
|
||||
2. **Filtering Sound Events:** Select only the sound event annotations that are relevant for your specific training goal, based on their tags (e.g., keeping only high-quality echolocation calls).
|
||||
3. **Transforming Tags (Optional):** Modify the tags on the selected annotations.
|
||||
This is useful for standardizing inconsistent labels, correcting errors, grouping related concepts (like mapping species to genus), or deriving new information.
|
||||
4. **Defining Classes & Decoding Rules:** Map the final set of tags on each annotation to a specific target **class name** (like `pippip` or `myodau`) that the model will learn.
|
||||
This step also includes setting priorities for overlapping definitions and specifying how predicted class names should be translated back into tags (decoding).
|
||||
5. **The `Targets` Object:** Understand the outcome of this configuration process – a functional object used internally by BatDetect2 that encapsulates all your defined rules for filtering, transforming, encoding, and decoding.
|
||||
|
||||
The result of this entire configuration process is a clear set of instructions that BatDetect2 uses during training data preparation to determine the correct "answer" (the ground truth label or target) for each relevant sound event.
|
||||
|
||||
Explore the detailed steps using the links below:
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
@ -11,4 +70,5 @@ filtering
|
||||
transform
|
||||
classes
|
||||
labels
|
||||
use
|
||||
```
|
||||
|
76
docs/source/targets/use.md
Normal file
76
docs/source/targets/use.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Bringing It All Together: The `Targets` Object
|
||||
|
||||
## Recap: Defining Your Target Strategy
|
||||
|
||||
Previously, we covered the steps to precisely define what your BatDetect2 model should learn:
|
||||
|
||||
1. **Terms:** Establishing the vocabulary for annotation tags.
|
||||
2. **Filtering:** Selecting relevant sound event annotations.
|
||||
3. **Transforming:** Optionally modifying tags.
|
||||
4. **Classes:** Defining target categories, setting priorities, and specifying decoding rules.
|
||||
|
||||
You define these aspects within a configuration file (e.g., YAML), which holds the complete specification for your target definition strategy.
|
||||
|
||||
## What is the `Targets` Object?
|
||||
|
||||
While the configuration file specifies _what_ you want to happen, BatDetect2 needs a component to actually _perform_ these steps.
|
||||
This is the role of the `Targets` object.
|
||||
|
||||
Think of the `Targets` object as an organized container that holds all the specific functions and settings derived from your configuration file (specifically, your `TargetConfig` section).
|
||||
It's created directly from your configuration and provides methods to apply the filtering, transformation, encoding, and decoding steps you defined.
|
||||
It effectively bundles together all the logic determined by your settings into a single, usable object.
|
||||
|
||||
## How is it Created and Used?
|
||||
|
||||
For most standard training workflows, you typically won't need to create or interact with the `Targets` object directly in Python code.
|
||||
BatDetect2 usually handles its creation automatically when you provide your main configuration file during training setup.
|
||||
|
||||
Conceptually, here's what happens behind the scenes:
|
||||
|
||||
1. You provide the path to your configuration file (e.g., `my_training_config.yaml`).
|
||||
2. BatDetect2 reads this file and finds your `targets` configuration section.
|
||||
3. It uses this configuration to build an instance of the `Targets` object, loading it with the appropriate functions for filtering, transforming, encoding, and decoding based on your settings.
|
||||
|
||||
```python
|
||||
# Conceptual Example: How BatDetect2 might use your configuration
|
||||
from batdetect2.targets import Targets # The class we are discussing
|
||||
|
||||
# You provide this path, usually as part of the main training setup
|
||||
target_config_file = "path/to/your/target_config.yaml"
|
||||
|
||||
# --- BatDetect2 Internally Does Something Like This: ---
|
||||
# Loads your config and builds the Targets object using a factory method
|
||||
targets_processor = Targets.from_file(target_config_file)
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# Now, 'targets_processor' holds all your configured logic and is ready
|
||||
# to be used internally by the training pipeline.
|
||||
```
|
||||
|
||||
## What Does the `Targets` Object Do?
|
||||
|
||||
Once created, the `targets_processor` object plays two vital roles within the BatDetect2 system:
|
||||
|
||||
1. **Preparing Training Data:** During the data loading phase of training, BatDetect2 uses this object to process each annotation from your dataset _before_ the final heatmap targets (Step 5) are generated.
|
||||
For each annotation, it will internally apply the logic defined in your configuration using methods like `targets_processor.filter(...)`, `targets_processor.transform(...)`, and `targets_processor.encode(...)`.
|
||||
2. **Interpreting Model Predictions:** When you use a trained model, this object (or the configuration used to create it) is needed to translate the model's raw output (predicted class names) back into the meaningful annotation tags you defined using the decoding rules (`targets_processor.decode(...)` and accessing `targets_processor.generic_class_tags`).
|
||||
3. **Providing Metadata:** It conveniently holds useful information derived from your configuration, such as the final list of specific class names (`targets_processor.class_names`) and the tags representing the generic class (`targets_processor.generic_class_tags`).
|
||||
|
||||
## Why is Understanding This Important?
|
||||
|
||||
As a researcher using BatDetect2, your primary interaction is typically through the **configuration file**.
|
||||
The `Targets` object is the component that brings that configuration to life.
|
||||
|
||||
Understanding its existence and role is key:
|
||||
|
||||
- It helps connect the settings in your configuration file to the actual behavior observed during training or when interpreting model outputs.
|
||||
If the results aren't as expected, reviewing the relevant sections of your `TargetConfig` is the first step in debugging.
|
||||
- Furthermore, understanding this structure is beneficial if you plan to create custom Python scripts.
|
||||
While standard training runs handle this object internally, the underlying functions for filtering, transforming, encoding, and decoding are accessible or can be built individually.
|
||||
This modular design provides the **flexibility to use or customize specific parts of the target definition workflow programmatically** for advanced analyses, integration tasks, or specialized data processing pipelines, should you need to go beyond the standard configuration-driven approach.
|
||||
|
||||
## Summary
|
||||
|
||||
The `Targets` object encapsulates the entire configured target definition logic specified in your configuration file.
|
||||
It acts as the central hub within BatDetect2 for applying filtering, tag transformation, class encoding (for training preparation), and class decoding (for interpreting predictions).
|
||||
It bridges the gap between your declarative configuration and the functional steps needed for training and using BatDetect2 models effectively, while also offering components for more advanced, scripted workflows.
|
@ -114,22 +114,6 @@ def test_build_filter_from_rule():
|
||||
)
|
||||
|
||||
|
||||
def test_merge_filters(create_annotation):
|
||||
def filter1(annotation):
|
||||
return "tag1" in [tag.value for tag in annotation.tags]
|
||||
|
||||
def filter2(annotation):
|
||||
return "tag2" in [tag.value for tag in annotation.tags]
|
||||
|
||||
merged_filter = merge_filters(filter1, filter2)
|
||||
|
||||
annotation_pass = create_annotation(["tag1", "tag2"])
|
||||
assert merged_filter(annotation_pass) is True
|
||||
|
||||
annotation_fail = create_annotation(["tag1"])
|
||||
assert merged_filter(annotation_fail) is False
|
||||
|
||||
|
||||
def test_build_filter_from_config(create_annotation):
|
||||
config = FilterConfig(
|
||||
rules=[
|
||||
|
Loading…
Reference in New Issue
Block a user