---
config:
theme: 'dark'
---
flowchart LR
subgraph Experiment Pipeline
direction LR
DL[DataLoader]
RPreprc[Raw_Preprocessing]
Prdgm[Paradigm]
EPreprc[Epoch_Preprocessing]
Spl[Split]
Aug[Augmentation]
Train[Model Training]
Eval[Evaluation]
Viz[Visualization]
Sv[Save]
end
subgraph Validators
VALID[Validation]
DLV([DataLoaderConfig])
other([...])
Epoch([EpochConfig])
end
DLV --o ExConf
Epoch --o ExConf
YAML[.yaml] --> VALID[Validation]
VALID --> ExConf[ExperimentConfig]
ExConf --> DL
DL --> RPreprc
RPreprc --> Prdgm
Prdgm --> EPreprc
EPreprc --> Spl
Spl --> Aug
Aug --> Train
Train --> Sv
Prdgm --> Eval
Eval --> Viz
.yaml- struktura konfiguračních souborů (zpracovány knihovnouhydra)Validate- validace konfiguračních souborů (využitíPydantic)DataLoader- načtení datasetůRaw_Preprocessing- počáteční zpracování datasetů, vyčištění od šumu apod.Paradigm- využití moabb paradigm (epochování, základní filtrace, apod.)Epoch_Preprocessing- volitelný preprocessing na již epochovaných datechSplit- rozdělení na trénovací, testovací dataAugmentation- augmentace, generování nových datasetů pomocí transformacíModel Training- trénování modeluSave- uložení natrénovaných datEvaluation- detekce fíčur v datechVisualization- vizualizace výsledků (gui, grafy)
Konfigurační soubory se nacházejí ve složce config/ a jsou uloženy v hierarchické struktuře .yaml souborů. Kořenový
konfigurační soubor config.yaml obsahuje reference na jednotlivé konfigurační soubory kroků pipeline.
Při spuštění nahradí hydra tyto reference obsahem .yaml souborů (klíč = složka, hodnota = soubor). Např. pro
raw_preprocessing: testing uloží do klíče preprocessing obsah souboru raw_preprocessing/testing.yaml.
V budoucnu bude pro každý krok existovat více různých implementací, jejichž konfigurace půjdou tímto způsobem lehce
nahrazovat. Zároveň je potřeba, aby každý konfigurační soubor obsahoval jeho název, protože nahrazením hodnoty mne
obsahem souboru ztratíme informaci o zvolení právě této implementace. Aktuálně je tato hodnota v všech souborech uložena
pod klíčem backend.
Díky použití knihovny hydra se celá konfigurace při každém běhu automaticky uloží do složky outputs/ spolu s logy z
stdout.
Validace konfigurace bude provedena pomocí knihovny pydantic. Ta umožňuje vytvořit "otisk" struktury .yaml
konfigurace pomocí tříd v pythonu a automaticky umí zvalidovat:
- existenci parametrů
- datové typy
- strukturu configu
- hodnotové rozsahy
- existence vstupních souborů (
FilePath)
Pro každou část konfiguračního souboru se tedy vytvoří třída se stejnou strukturou atributů + omezeními které je potřeba zvalidovat.
Konfiguračnímu souboru raw_preprocessing/testing.yaml:
backend: testing
high_pass_filter:
l_freq: 1.0
notch_filter:
freqs: [ 50 ]
annotate_break:
min_break_duration: 2.0Odpovídá třída RawPreprocessingConfig:
class RawPreprocessingConfig(AStageConfig):
backend: Literal["testing"]
high_pass_filter: HighPassFilterConfig
notch_filter: NotchFilterConfig
annotate_break: AnnotateBreakConfigNa základě atributu backend dokáže pydantic automaticky detekovat která z implementací je aktuálně v konfiguraci, a
zvaliduje jí podle správné třídy. V kořenové konfiguraci ExperimentConfig jsou nadefinované atributy pro každý krok
pipeline s výčtem možností implementací.
Např. u kroku augmentace jsou přípustné 2 implementace - basic a none. Implementace je vybrána na základě klíče
backend:
augmentation: Union[AugmentationConfigBasic, AugmentationConfigNone] = Field(discriminator="backend")Po vytvoření celé struktury konfigurace je potřeba pouze zvalidot kořenovou konfiguraci ExperimentConfig a všechny
její části se "rekurzivně" zvalidují. Tento krok zajišťuje třída ExperimentConfigValidator, která navíc při chybě
validace poskytuje list ValidationMessage, které popisují chybné/chybějící části konfigurace.
Kromě atributů shodných se vstupními .yaml konfiguracemi obsahují třídy s konfigurací i atribut _target_class. Ten
obsahuje cestu k třídě, kterou konfiguruje jako řetězec (k zamezení cyclic dependencies). Po zvalidování konfigurace z
ní jde jednoduše vytvořit instance třídy _target_class pomocí metody AStageConfig.get_instance() a není potřeba
žádné další složité logiky (přiřazování správné třídy k danému configu).
Jak už bylo zmíněno, každý krok pipeline může mít více implementací (pro různé knihovny různé implementace). Druh
implementace/knihovny bude vybrán v config.yaml. Jednotlivé implementace (src/impl/*) implementují rozhraní daného
kroku (src/types/interfaces), díky čemuž se dají jednoduše nahrazovat.
Jednoduchý příklad implementace DataLoaderu - načítání vstupních dat:
class DummyLoader(IDataLoader):
def run(self, input_dto: DatasetConfig, run_ctx: RunContext) -> StepResult[RawDataDTO]:
# ... načtení souborů ...
return StepResult(foo, bar, [])Pro přenos dat z výstupu jednoho kroku na vstup následujícího kroku jsou využívány DTO - data transfer objects (viz
src/types/dto). Každý krok má tedy nadefinovanou strukturu vstupních dat (DTO), kterou přebírá při spuštění.
Celou orchestraci zajišťuje třída ExperimentPipeline, která postupně spouští kroky pipeline, vytváří DTO a předává
je následujícím krokům.
V metodě run() přebírá již výše zmíněný ExperimentConfig a instance všech kroků pipeline. Dále zajišťuje i
správné spouštění kroků na základě módu, ve kterém je pipeline spuštěna (training, experiment).