[ Skip to the content ]

Institute of Formal and Applied Linguistics Wiki


[ Back to the navigation ]

Table of Contents

Řízení pokusů pomocí Makefilů

Danova úvodní poznámka: V poslední době se mi několikrát stalo, že jsem potřeboval aplikovat stejnou řadu skriptů za několik různých datových souborů, případně stejný pokus zopakovat po delší době, takže bylo pracné si vzpomenout, jak přesně jsem co volal. Konkrétně šlo o vícejazyčné soutěže ve zpracování přirozených jazyků (CoNLL parsing, Morpho Challenge), a pak také o vyhodnocování úspěšnosti zápočtových úkolů. Nabízelo se řešení pomocí make a Makefilů, kde lze posloupnost zpracování dat jednotlivými skripty dobře popsat. Současně jsem ale vždy znova zápasil s věcmi, které se mi v Makefilech řešily těžko. Tenhle dokument je tedy pokusem shrnout problémy a poznamenat si řešení, vlastně jakýsi makefile k Makefilům.

Výchozí předpoklady

Odněkud jsme získali výchozí data. Ta leží v jedné nebo několika složkách, kde je chceme nechat beze změny, nepoškodit je chybným spuštěním skriptu ani do těchto složek přidávat soubory, které nesouvisí se zdrojovými daty, ale spíše s jejich konkrétním zpracováním v rámci nějakého pokusu.

Data tedy chceme nejdříve zkopírovat do pracovní složky. Typicky máme obdobná data pro několik různých jazyků. Je možné, že původní data (resp. jejich vydavatel) používají pojmenovávací konvenci, která nám nevyhovuje, takže je během kopírování chceme také přejmenovat. Je také možné, že na začátku potřebujeme provést nějaké operace, které nejsou pro všechny jazyky stejné, např. některé jazyky mohou používat nějaké exotické kódování a my chceme všechno sjednotit na UTF-8, nebo třeba chceme vytáhnout nějaké informace z morfologických značek, které mají v každém jazyce jinou strukturu.

V rámci jednoho jazyka můžeme mít několik zdrojových souborů. Například trénovací data, vývojová data, závěrečná vyhodnocovací data atd. Je možné, že chceme nachystat paralelně dvě řady pokusů, kde v první řadě rozsekneme (svým vlastním algoritmem) oficiální trénovací data na trénovací a pokusná testovací, ve druhé pak použijeme celá oficiální trénovací data a otestujeme metodu na oficiálních testovacích. Kromě použitých vstupních dat bude zpracování v obou případech stejné.

Rozdělení na zdrojovou a pracovní složku

Je na uvážení, které kroky chceme dělat přímo ve zdrojové složce s daty a které až po zkopírování do pracovní složky. Sjednocení kódování na UTF-8 nebo převod dat do jiného formátu může být užitečné i pro úplně jiné pokusy, proto ho možná chceme provést ve zdrojové složce.

Změna pojmenovávací konvence nebo vlastní rozdělení trénovacích dat na vývojová trénovací a vývojová testovací jsou příklady úprav, které leží na hranici. Můžeme je považovat za dostatečně obecně užitečné, abychom je chtěli dělat už ve zdrojové složce, nebo je můžeme odkázat až do pracovní složky.

Trénování modelu, ukládání zpracovaných dat a hlášení vyhodnocovacího programu naopak typicky patří do pracovní složky.

Standardní řetězec operací nad daty se nejlépe popisuje pomocí šablonových pravidel (např. jak vznikne soubor %.csts ze souboru %.conll). Bohužel proměnná část v takovém pravidle může být jen jedna. Může obsahovat i cestu, ale to se dá těžko použít pro zpracování spojené s kopírováním dat z jedné složky do jiné.

Zdá se tedy, že je ideální rozdělit zpracování dat na dvě části (a zřejmě dva různě koncipované Makefily). V té první se provedou jazykově závislé operace (pro každý jazyk jiné explicitní pravidlo) a data se zkopírují ze zdrojové složky do pracovní. Druhou část tvoří již uniformní posloupnost operací s daty popsaná šablonovými pravidly.

Pomocné soubory a úklid

Většinu mezisouborů bychom měli současně uvést mezi cíli, aby nám je make nesmazal jako přechodné. Šetříme tím čas pro případ, že bude nutné zopakovat zpracování jen části souborů, a také činíme závislosti mezi soubory více explicitní (jinak se může stát, že změníme závislost přechodného souboru, který je smazán, a pro make to nebude dostatečný důvod, aby přepracoval cílový soubor).

Je také dobré si nagenerovat cíle pro úklid zejména prázdných souborů, které vzniknou, když nějaký cíl volá příkaz, který píše na standardní výstup, standardní výstup je přesměrován do souboru a zpracování příkazu skončí nějakou chybou. Dá se také nastavit v Makefilu, aby dílčí cíle po chybě mazal. Jinak tam bude soubor, který nemá správný obsah, ale bude mít dost čerstvou časovou nálepku, takže ho make po odstranění chyby nebude chtít přepracovat. Pak je těžké v tom udržet pořádek.

Pokud Makefile obsahuje zvláštní cíl .SECONDARY bez prerekvizit, znamená to, že make nemaže soubory, které považuje za přechodné.

Pokud Makefile obsahuje zvláštní cíl .DELETE_ON_ERROR bez prerekvizit, make smaže cíle, které se změnily, ale některý příkaz v jejich pravidle skončil chybou. Je velmi vhodné tuto “volbu” zapnout. Většinu nástrojů volám tak, že píšou na STDOUT, který mám přesměrovaný do souboru. Pokud nástroj skončí chybou, je většinou cílový soubor už vytvořen, byť má nulovou velikost. Má ale čerstvý údaj o čase vzniku, takže až chybu opravím a pustím make znova, make si bude myslet, že už tento soubor nemá předělávat.

Výpočty na clusteru

Některé kroky bývají výpočetně náročné a hodilo by se je dělat na clusteru. Zvláště pokud zpracováváme stejným způsobem třeba 10 jazyků, hodilo by se zpracovávat je všechny paralelně. Paralelní práce s makem je problém, protože je potřeba propojit hlídání závislostí s distribucí úloh: qsub se vrátí dříve než je cíl připraven, takže nemůžeme hned přejít k tvorbě cílů, které na něm závisí. GNU make umí paralelizovat zpracování v rámci několika procesorů téhož stroje (při volání můžeme říct, na kolik procesů se smí zpracování rozštěpit), ale podpora pro spolupráci s clusterem mu chybí. Existují nějaké paralelní maky, ale myslím, že nejsou standardně nainstalované všude (pravděpodobně ani u nás) a řešení na nich postavené by nebylo snadno přenositelné.

Absolutní a relativní cesty

Make neprovádí pwd, takže neví, že $(MOJESLOZKA)/soubor.txt a soubor.txt je případně tentýž soubor (cíl). Má-li být součástí zpracování kopírování souborů z jedné složky do jiné, pak je nejbezpečnější uvádět všechny soubory absolutní cestou. V tom případě je ovšem potřeba počítat s tím, že make bude s dlouhou cestou k souboru pracovat po celou dobu včetně všech šablonových pravidel, což může případně komplikovat návrh šablon v těchto pravidlech.

Nevýhodou je také délka. Kopie volání, které make vypíše do terminálu, budou méně přehledné. Pokud navíc pracujeme s velkým množstvím souborů, snadněji se nám stane, že někde překročíme nejvyšší povolenou délku příkazového řádku.

Pokud oddělíme kopírování dat ze vzdálených složek do samostatného Makefilu a pokud v rámci jednoho Makefilu omezíme případné přesuny mezi složkami na takové, které se dají vyjádřit relativní cestou (tj. typicky podstrom složky, ve které leží Makefile), pak si vystačíme s relativními cestami. Poznámka: I kopírování ze vzdálených složek lze pak vyřešit tím, že si na vzdálené složky vyrobíme ze složky s Makefilem symbolický odkaz.

Rozměry

Soubory, které při zpracování vznikají, lze rozdělit podle následujících kritérií. Kritéria je vhodné zohlednit ve jménech souborů nebo složek, aby bylo možné zpracovávat skupinu souborů se stejným kritériem pomocí jednoho pravidla. Bohužel je často obtížné navrhnout optimální rozmístění kritérií v cestě k souboru, protože make umí v šablonových pravidlech pracovat pouze s jedním souvislým proměnným úsekem.

mdmake

Tohle jsou Danovy poznámky k budoucímu nástroji, který by měl řadu problémů odstranit tím, že z šablony makefile.mdm vygeneruje Makefile pro normální gnu make. MD-make znamená „multidimenzionální make“.

.MDIMS: LANGUAGES/ DE TRAINTEST -PREPROCESSINGS .STATES
.MDRULE
.md.rul mst.conll < blind.conll mst
.md.dep $(TOOLDIR)/runmst.pl
.md.for: LANGUAGES DE PREPROCESSINGS
.md.fix: test
        @echo Running MST for language $(*LANGUAGES):
        $(TOOLDIR)/runmst.pl -m $(*2) < $< > $@
.MDALL: d hi conll

se přepíše jako

.PHONY: all_d_hi_conll
all_d_hi_conll: <seznam všech souborů obsahujících hodnoty "d", "hi" a "conll">

Pozor! Podporu pro odesílání cílů na cluster, plánovanou níže, psát nemusím! Existuje totiž qmake, který si poradí s normálním makefilem pro GNU make a sám rozesílá úlohy na cluster. Jediný podstatný rozdíl, na který je třeba dát pozor, je, že pravidlo nesmí obsahovat několik příkazů na samostatných řádcích. Pokud má obsahovat více než jeden příkaz, musejí být všechny na jednom řádku oddělené středníky a před případnými zalomeními řádku musí být backslash.


[ Back to the navigation ] [ Back to the content ]