České vysoké učení technické v Praze Fakulta elektrotechnická
Transkript
České vysoké učení technické v Praze Fakulta elektrotechnická
České vysoké učení technické v Praze Fakulta elektrotechnická Katedra počítačů Bakalářská práce Návrhové vzory v aplikaci FAKE GAME Jakub Weberschinke vedoucí práce: Ing. Pavel Kordík, Ph.D. Studijní program: Softwarové technologie a managment, strukturovaný, bakalářský Obor: Softwarové inženýrství 11.června 2009 Poděkování Rád bych poděkoval vedoucímu této práce Pavlu Kordíkovi za jeho skvělý osobní přístup. Dále pak svým rodičům a přítelkyni, bez jejichž podpory by tato práce nemohla vzniknout. Prohlášení Prohlašuji, že jsme práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu §60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon). V Praze dne 11. 6. 2009......................................................................................................................... Abstract This thesis contains introduction to design pattern issues with focus on GoF and GRASP patterns. The next part contains description of basic terms of data mining with characterization of Fake Game and Weka apllications. Describes the most used methods of evaluation of data mining models and compares the former applications in this respect. Introduces current problems of Fake Game API and discusses possible solutions. The last part is a description of an integration of Weka evaluation module into Fake Game. Abstrakt Obsahem práce je úvod do problematiky návrhových vzorů se zaměřením na GoF a GRASP vzory. Další částí je popis základních termínů oblasti data miningu s popisem programů Fake Game a Weka. Popisuje hlavní metody evaluace data miningových modelů a v tomto ohledu blíže srovnává obě dvě aplikace. Nastiňuje aktuální problémy programu Fake Game API a diskutuje možná řešení. Poslední část popisuje integraci evaluačního modulu z Weka do Fake Game. Obsah Úvod...................................................................................................................................................16 Popis problému, specifikace cíle........................................................................................................18 Popis problému..............................................................................................................................18 Specifikace cíle..............................................................................................................................18 Teorie..................................................................................................................................................20 Data mining....................................................................................................................................20 Fake Game................................................................................................................................21 Weka..........................................................................................................................................21 GRASP vzory.................................................................................................................................21 GoF vzory......................................................................................................................................23 Creational Patterns....................................................................................................................23 Structural Patterns.....................................................................................................................25 Behavioral patterns...................................................................................................................27 Analýza evaluačních metod a metrik.............................................................................................32 Hold-out....................................................................................................................................32 K-fold křížová validace.............................................................................................................33 Leave-one-out...........................................................................................................................33 Validace opakovaným vytvářením náhodných vzorků.............................................................33 Bootstrap...................................................................................................................................34 Metriky...........................................................................................................................................34 Chyba........................................................................................................................................34 Zaujetí.......................................................................................................................................34 Variance.....................................................................................................................................35 Matice záměn............................................................................................................................35 Evaluace klasifikace..................................................................................................................35 Matice cen.................................................................................................................................36 ROC křivka...............................................................................................................................36 AUC..........................................................................................................................................37 Ostatní metriky..........................................................................................................................37 Analýza...............................................................................................................................................38 Základní stavba Fake Game...........................................................................................................38 Problémy spojené se současnou verzí.......................................................................................38 Analýza evaluačních metod ve Fake Game...................................................................................39 Evaluace modelů přes metodu saveToFile().............................................................................39 Evaluace modelů pomocí balíčku preprocessing.statistic.........................................................41 Evaluace v programu Weka...........................................................................................................43 Návrh & Implementace......................................................................................................................45 Diskuse možných postupů.............................................................................................................45 Propojení Weka a Fake Game........................................................................................................45 Diskuse......................................................................................................................................45 Řešení........................................................................................................................................46 Úprava balíčku preprocessing.statistic..........................................................................................48 Diskuse......................................................................................................................................48 Řešení........................................................................................................................................48 Grafické rozhraní používající evaluaci z Weka.............................................................................50 Diskuse......................................................................................................................................50 Řešení........................................................................................................................................50 Testování.............................................................................................................................................52 Manuální testování.........................................................................................................................52 Unit testy s běžnými objekty.........................................................................................................52 Unit testy s mock objektem...........................................................................................................53 Závěr...................................................................................................................................................54 Literatura............................................................................................................................................56 Seznam použitých zkratek..................................................................................................................58 Uživatelská příručka...........................................................................................................................60 Obsah CD...........................................................................................................................................62 Seznam obrázků ROC křivka klasifikačního modelu....................................................................................................36 kvalita klasifikace vyjádřená metrikou AUC (křivky shora: skvělá, dobrá, špatná)..........................37 UML diagram základních modulů Fake Game..................................................................................38 vzorek kódu metody saveToFile() objektu TreeData.........................................................................39 UML diagram tříd, které využívají metodu saveToFile()...................................................................40 UML diagram tříd, které se v modulu statistics podílí na evaluaci....................................................42 modul statistického balíčku s maticí záměn a statistikami souvisejících metrik................................42 modul statistického balíčku zobrazující bias a varianci.....................................................................43 třídy spolupracující na evaluaci v programu Weka............................................................................44 UML diagram znázorňující balíček adaptérů.....................................................................................46 UML diagram výjimek a jejich původců............................................................................................47 UML diagram statistic.core................................................................................................................49 UML diagram vztahů GUI a datových tříd balíčku statistic ..............................................................49 grafické rozhraní modulu Weka Evaluation.......................................................................................50 vzorek kódu testů................................................................................................................................52 Seznam tabulek matice záměn pro tři třídy...................................................................................................................34 matice cen pro tři generické třídy.......................................................................................................35 Kapitola 1 Úvod V posledních letech si zejména velké společnosti, které disponují rozsáhlými databázemi s obrovským množstvím dat o klientech, produktech, obchodech, atp., začaly uvědomovat cenu informací v nich skrytých. A velice záhy také zjistily, že zdaleka nejcennější jsou ty informace, které nejsou na první pohled zřejmé. Začaly se hledat metody, jak co možná nejrychleji a nejefektivněji odhalit tyto skryté informace a tak z nich moci vytěžit co největší prospěch. A to je přesně ten důvod, proč oblast data miningu, v Čechách též známá jak vytěžování dat, zažívá v poslední dekádě takový rozmach.[14] Postupně se vytvářely nové a lepší postupy, jak odkrývat a popisovat tyto na první pohled nezřejmé vztahy, které byly pochopitelně čím dál tím víc složitější. A tak se začal řešit ještě jeden problém: vytvořit co možná nejjednodušší, ale zároveň výstižný systém, který by byl schopen porovnat úspěšnost více rozdílných data miningových technik mezi sebou a určit tu vhodnější pro daný problém. Odpovědí na tento problém se staly evaluační metody. Nejdříve se jednalo o statistické metody, které původně vznikly pro potřeby jiných odvětví, ale postupně se vytvářely i postupy přímo pro data mining jako takový. Není tedy divu, že aplikace, které se zabývají vytěžováním dat, se snaží v co největší míře implementovat tyto metody. Krom vyšší kvantity je samozřejmě cílem i vyšší kvalita, a to jak po stránce evaluačních metodik a metrik, tak po stránce jejich implementace. Trendem je směřování k vyšší přehlednosti, znovupoužitelnosti a rozšiřitelnosti. 15 16 Kapitola 2 Popis problému, specifikace cíle Popis problému Řešeným problémem této bakalářské práce je popsání aktuálního stavu programu Fake Game po jeho implementační stránce se zaměřením na evaluaci modelů vytvářeních právě v rámci této aplikace. Úkolem je rozšířit poskytované evaluační metody a spolu se stávajícími je sjednotit pod společné rozhraní. Pokud to bude možné, bude snaha o maximální použití již implementovaných a otestovaných modulů programu Weka. Návrh by měl respektovat zásady programování na základě GRASP a GoF vzorů tak, aby byl kód přehledný a efektivní. Program bude napsán v programovacím jazyce Java. Specifikace cíle Prvním krokem práce je blízké seznámení se s principy programování s použitím GoF a GRASP vzorů. Popíši jejich možnosti užití i případné úskalí, které může jejich aplikace přinést, stejně jako konkrétní výhody, které z jejich používání plynou. Tyto budou v následujících částech použity jako vzory pro úpravu kódu aplikace a navržení kódu nového. Dalším krokem bude popis dostupných metodik a metrik, které se váží k evaluaci modelů v data miningu. Bude potřeba zjistit, jaké metody jsou nejúčinější v jakém kontextu a za jakých podmínek naopak není jejich použití nejvhodnější. Důležitým aspektem je také posouzení srozumitelnosti, protože všechny výstupy těchto metod budou analyzovány přímo uživatelem aplikace. Následně je nutné analyzovat aktuální stav v aplikaci Fake Game. To bude zahrnovat nejprve pochopení těch procesů, kterými v rámci zpracování dat aplikace prochází. Následovat bude prozkoumání hlavních problémů, které má aplikace po implementační stránce, protože je velice pravděpodobné, že s těmito se bude nutné vypořádat i při návrhu konkrétního rozšíření. Poté bude žádoucí porovnat mezi sebou aplikace Fake Game a Weka, co se týče jejich řešení evaluačních problémů. Zde bude nutno zohlednit znalosti získané studiem návrhových vzorů, aby bylo nastínit i případná řešení nalezených nedostatků. V této části bude také určeno, jaké části se použijí. Následuje identifikace nedostatků částí, u kterých bylo rozhodnuto, že budou v systému ponechány a návrh jejich úpravy tak, aby splňovali zásady GRASP. Zároveň bude proveden návrh adaptérů tak, aby bylo možné používat funkcionalitu programu Weka v GAME. Nakonec bude provedena samotná implementace. Podle použitých částí bude vybrán nejvhodnější způsob testování a ten bude použit. 17 18 Kapitola 3 Teorie Data mining Pojem data mining, česky vytěžování dat, je „netriviální proces identifikace pravdivých, dosud neznámých, potenciálně využitelných a naprosto srozumitelných vzorů v datech“1. To znamená, že v datech hledá vztahy, které nejsou na první pohled patrné a jejichž znalost nám pomůže v dosažení určitého cíle. Jednotlivé vzorky dat se nazývají instance, jejich vlastnosti jsou pdotom vyjádřeny atributy, které zpravidla bývají vyjádřeny číselnou hodnotou. [9] Problémy, které data mining řeší, se rozdělují do několika skupin: 1. klasifikační problémy, kdy se snažíme jednotlivé instance dat podle hodnot jejich atributů přiřadit do jedné z předem určených tříd. Příkladem klasifikace může být určování zdravotního stavu pacienta podle jeho příznaků, kdy třídy jsou pak jednotlivé nemoci. 2. regrese, kde z hodnot atributů matematickými operacemi získáváme numerický výstup. Zde je jako příklad možno uvést zkoumání vlivů jako je cena výrobku, cena u konkurence a inflace na prodejnost produktu firmy. 3. predikce používá postupy regrese i klasifikace, zde jsou data chronologicky seřazena a z vývoje jejich hodnot v minulosti určujeme hodnoty budoucí. Příkladem může být předpověď počasí, kdy z vývoje naměřených meteorologických hodnot určujeme předpokládané teploty na další dny. 4. shlukování je pak další typ, který je podobný klasifikaci. V tomto případě ale nejsou předem určené skupiny, do kterých se data zařazují, ale cílem je odhalení skupin nebo shluků dat s podobnými atributy a interpretace těchto míst hustšího výskytu instancí. Problémy tohoto typu se velice často řeší v oblasti marketingu, například při určování skladby trhu, kdy se identifikují cílové skupiny zákazníků a vlastnosti, které danou skupinu charakterizují. 5. asociační pravidla se používají k odhalení vnitřních závislostí dat nebo podskupiny dat, tedy to, zda je hodnota atributu ovlivněna hodnotami jiných atributů v rámci jednotlivých instancí. Toho pak lze využít u jedné z nejznámější úlohy data miningu, kdy se v obsahu nákupních košíků zákazníků supermarketů hledají závislosti, na základě kterých se pak upravuje umístění jednotlivých druhů zboží, aby zákazník musel projít co největší část obchodu. 6. bagging & boosting jsou techniky, které se používají ke kombinování více modelů dohromady. Do vytěžování dat samozřejmě patří ještě další metody, ale výše uvedené patří mezi nejznámější. U většiny problémů si data mining vystačí s klasifikací, predikcí a regresí. [12] Data, která se v oblasti vytěžování používají, často nejsou v původním stavu vhodná k vytváření modelů. Aby bylo možné jejich matematické zpracování, musí většinou projít procesem zvaným preprocessing. Ten má za úkol nahrazení textových hodnot číselnými, označení chybějících hodnot, odstranění duplicit a podobně. Někdy může být jeho součástí i doplnění dalších instancí vygenerovaných na základě těch, které máme k dispozici. K získávání znalostí z dat se používají tzv. modely. Ty v případě klasifikačních metod odpovídají matematickým funkcím více proměnných, kde hodnoty atributů jedné instance jsou proměnné a výstup odpovídá funkční hodnotě. V ostatních případech se používají různé statistické modely, nástroje na určení podobnosti atributů na základě vzdálenosti (ať už eukleidovské manhattonské) atp. U klasifikačních metod používáme k vytváření modelů trénovací data, jejich správnost potom ověřujeme na testovacích datech. Obě tyto množiny dat musí mít stanovené správné výstupy, v trénování se tyto používají k hledání optimální přenosové funkce, při testování se pak zjišťuje odchylka očekávané hodnoty 1 Definice Fayyad et al. 19 podle modelu a její hodnoty skutečné. [9] Poznámka: V této práci často používám, zejména v kontextu data miningových softwarů, termín klasifikace nebo klasifikační problém. Vzhledem k tomu, že se často používá v širším kontextu, abych předešel nejasnostem, budu používat dva termíny s odlišným významem. Souslovím klasifikační metody budu označovat metody, které řeší problémy, kde na základě vstupů určujeme výstup, u kterého je možno objektivně určit jeho správnost. Tímto je tedy myšlena skupina, do které lze zařadit klasifikaci, regresi a predikci. V případě, že budu mít na mysli konkrétní metodu, budu používat mluvit pouze o klasifikaci. Fake Game Fake Game je data miningový software od skupiny Computational Intelligence Group na katedře počítačů. Jeho vývoj probíhá v programovacím jazyce Java SE , jeho grafické rozhraní je ovládáno přes grafické rozhraní, které pracuje nad komponenty Swing. Poslední vývoj směřuje také k používání Java anotací. Program se skládá ze dvou hlavních částí. Jádrem je rozhraní Game, které zajišťuje samotné vytváření modelů, případně ensemble modelů, nad daty. Program umožňuje jak tvorbu modelů jednoduchých, kdy vstup je na výstup překládán pouze jednou matematickou funkcí, tak i složitých modelů GAME, kdy dochází ke skládání více funkcí za pomocí neuronových sítí a genetických algoritmů. Takto generované modely pak na základě předloženého pole reálných čísel vrací jeden reálný výstup. Toto rozhraní umožňuje další práci s těmito modely, jako je vizualizace či základní evaluace. [5] Druhou částí je pak Fake, které má za úkol maximální automatizaci celého procesu, od předzpracování dat až k nastavení parametrů trénovacích algoritmů. Obě dvě tyto části mají společné grafické rozhraní, ve vývoji je také rozhraní příkazové řádky. Automatizaci, poskytovanou Fake, je tak možné použít jen některé operace v procesu a zbylé provádět ručně. Weka Nástroj Weka je data miningový software vyvíjený skupinou z univerzity Waikato na Novém Zélandu. Stejně jako předchozí program je programován v jazyce Java, grafické rozhraní je vytvořeno na základě frameworku Swing. Krom rozhraní grafického může být ovládán také přes rozhraní příkazové řádky. Program Weka disponuje třemi uživatelskými rozhraními. Základem je prostředí Explorer, které umožňuje základní operace s prostředím, které do značné míry odpovídají funkcím GAME. Jedná se o tvorbu modelů, jejich hodnocení a následné zobrazení výsledků. Také poskytuje možnost provedení další analýzy dat, jako je například shluková analýza, analýza atributů. [3] Další části, Experimenter a KnowledgeFlow, jsou pouze rozšířením předchozího a nabízejí větší volnost. Experimenter umožňuje práci s větším množstvím datových zdrojů najednou, zároveň dovoluje použití většího množství klasifikátorů – je ale velice výpočetně náročná a proto se nehodí k běžnému používání. KnowledgeFlow naopak znamená posun k většímu uživatelskému komfortu, protože nabízí plnou vizualizaci procesu zpracování. Všechny úkony jsou reprezentovány ikonami, jejichž spojováním se vytváří posloupnost spustitelných instrukcí, které jsou překládány na vnitřní volání metod programu. Rozhraní těchto částí programu jsou plně nezávislá a navzájem se nijak neovlivňují. Zcela odděleny jsou také jednotlivé funkční moduly, kam patří předzpracování dat, tvorba klasifikátorů, shlukování, tvorba asociačních pravidel, analýza atributů a vizualizace. Sdílejí jen rozhraní pro načítání a ukládání dat. Každou z těchto částí je pak možno používat nezávisle na zbytku systému.[4] GRASP vzory GRASP (Obecné vzory přidělování odpovědností v softwaru) se poprvé objevily v knize Applying UML and Patterns Craiga Larmana. Jedná se o nejobecnější z návrhových vzorů, neboť popisují metodiku přidělování odpovědností jednotlivým třídám, a jen v omezené míře říkají, jaké konkrétní třídy máme 20 používat. Z tohoto důvodu jsou vzory GRASP také někdy označovány jako zásady (principles) nebo procesní vzory (process patterns). Všechny uvedené vzory jsou obsahem knihy [1]. Mezi GRASP patří následující vzory: Information Expert Vzor určující, které třídě máme přiřadit zodpovědnost za úkol, který již máme určený. Říká, že odpovědnost by měl nést informační expert, to znamená třída, která má k provedení úkolu nejvíce informací. Creator Tento vzor řeší, jaká třída má mít za úkol vytváření instancí jiných tříd. Třída A je tedy tvůrcem třídy B, pokud platí, že: A agreguje B (tudíž B je součástí A), A komponuje B (B nemá jiné užití než ve vazbě s A), A zaznamenává instance B, A úzce používá objekty B, A má k dispozici inicializační data potřebná k tvorbě instance B. Controller Určuje, kdo bude zpracovávat událost v systému. Pokud se stane, že program musí reagovat na události, které mají původ mimo systém, neměl by je nutně zpracovávat objekt, který je jejich příjemcem. Vzor Controller nabízí dvě varianty určení třídy, která by se měla starat o zpracování: třída, která reprezentuje celý přijímající systém nebo třída, která představuje scénář případu užití v rámci kterého k události došlo. Protected variations Zabývá se problém přiřazování odpovědností tak, aby v případě změn v programu nedošlo k nežádoucím dopadům na ostatní součásti softwaru. Řešením je určení bodů možných změn a přiřazení odpovědností tak, aby bylo kolem tohoto bodu vytvořeno neměnné rozhraní. Tímto je míněn interface v širším slova smyslu – v důsledku se může jednat o rozsáhlý subsystém. Low Coupling Tento princip říká, že bychom se měli snažit maximálně snížit závislost mezi třídami, čímž dojde k omezení dopadu změn v rámci jedné třídy na třídy okolní a zvýšení znovupoužitelnosti. V jazyce Java se tohoto dosahuje předáváním interface místo konkrétních implementací. High Cohesion Coheze je v kontextu objektového programování míra toho, nakolik spolu souvisí úkoly, které má objekt vykonávat, a jak moc jsou specializované. Snahou vysoké koheze je zvýšení přehlednosti, pochopitelnosti a robustnosti programu. Příkladem nízké koheze je slučování dat a jejich grafické reprezentace do jedné třídy. Polymorphism Říká, že pokud se chování daného objektu liší, pokud se liší i typ objektu, pak je nutné toto řešit pomocí dědění od rodičovské třídy, kde konkrétní implementace chování konkrétního typu je definována v rámci potomků. V Javě se často problémy tohoto typu chybně řeší pomocí pomocných proměnných obsahujících typ a rozdělením různých funkcionalit v metodě pomocí switch. 21 Pure Fabrication Jako třída Pure Fabrication se označuje taková, která nemá svůj obraz v problémové doméně a která byla vytvořena, aby bylo vyhověno zásadám High Cohesion a Low Coupling. Návrhové vzory jako jsou GoF jsou tvořeny prakticky výhradně takovými třídami. Indirection Aby se snížila provázanost objektů, ve kterých může dojít ke změně, používají se třídy, které slouží jako prostředníci. Tyto třídy zpravidla umožňují jednomu objektu pomocí obecných metod volat? složitější metody jiného objektu, přičemž tyto metody jsou určeny parametry předávanými metodám prostředníka. GoF vzory GoF (Gang of Four) vzory jsou pojmenovány podle čtveřice autorů, kteří je poprvé definovali v knize Dessign Patterns: Elements of Reusable Object-Oriented Software. Jsou to Erich Gamma, Richard Helm, Ralph Johnson a John Vlissides. GoF vzory jsou rozděleny do tří kategorií podle toho, jakou skupinou problémů se zabývají. Všechny tyto vzory jsou uvedeny v knize [2]. Creational Patterns Jsou vzory, které se zabývají vytvářením nových objektů a jejich správou. Třídy, které jsou na základě těchto vzorů vytvořeny, plní dvě zapouzdřovací funkce: jednak skrytí toho, jaké konkrétní třídy software používá, ale také toho, jak jsou jednotlivé instance vytvářeny a sestavovány dohromady. Abstract Factory Poskytuje rozhraní pro vytváření skupin souvisejících nebo závislých objektů bez specifikace jejich konkrétních tříd. Tento vzor se používá, pokud platí, že: • systém by měl být nezávislý na tom, jak jsou jeho produkty vytvořeny, skládány a reprezentovány • systém by měl být tvořen vždy jednou z více skupin objektů • skupina souvisejících objektů je určena k používání jako celek a toto omezení musí být splněno za každých okolností • chceme poskytnout třídní knihovnu objektů, jejichž rozhraní má být odkryto na rozdíl od implementace Princip tohoto vzoru spočívá v tom, že vytvoříme abstraktní třídu či rozhraní, které definuje, jaké obecné typy souvisejících objektů chceme instanciovat. Tyto jsou definovány pomocí rozhraní. Potomci abstraktní továrny potom vytvářejí konkrétní objekty, které jsou definovány v třídách implementujících rozhraní. Aplikací tohoto vzoru dosáhneme izolace konkrétních tříd, zjednodušíme výměnu skupin objektů a zajistíme konzistenci používaných objektů, protože nehrozí, že by zároveň byly použity dva objekty z různých skupin. Přidávání nových typů objektů je ale relativně složité, neboť musí dojít ke změně rozhraní a tím pádem i změnám ve všech třídách, které ho implementují. Builder Snahou tohoto vzoru je oddělit tvorbu komplexního objektu od jeho reprezentace tak, aby samotný konstrukční proces mohl vytvářet různé reprezentace. Měl by se použít, pokud platí některý z těchto případů: • algoritmus pro vytváření komplexních objektů by měl být nezávislý na částech, ze kterých se objekt skládá, a na tom, jak jsou sestavovány • proces vytváření objektu musí umožnit rozdílné reprezentace objektu 22 Jádro tohoto vzoru tvoří tři skupiny objektů: ředitel, stavitel a produkt. Produkt je objekt, který chceme sestavovat; je zpravidla komplexní, skládá se z více částí. Funkcionalita poskytovaná stavitelem je určena abstraktní třídou, potomci této třídy překrývají její abstraktní metody. Stavitel poskytuje metody, které se použijí při sestavování objektu, ale sám je nepoužívá. Ředitel se stará o provedení dílčích kroků sestavování, k tomu používá rozhraní poskytované stavitelem. Použitím tohoto vzoru získáme možnost změny vnitřní reprezentace objektu, neboť ta je před uživatelem skryta, zároveň oddělíme kód tvorby objektu od jeho reprezentace, protože tvorbu má na starosti nezávislá třída. Také poskytuje jemnější kontrolu nad procesem konstrukce instancí, protože ty jsou vytvářeny krok po kroku. Factory Method Cílem je definovat rozhraní pro vytváření objektů, ale zároveň ponechat na objektech, které ho implementují, aby určily, jaké konkrétní třídy se mají instanciovat. Je vhodné aplikovat jej v těchto případech: • třída předem neví, jaký typ objektu má vytvářet • potřebujeme, aby potomci třídy specifikovali objekty, které třída vytváří • třídy delegují odpovědnost na jednoho z mnoha pomocných potomků a my chceme zjistit, který z potomků je momentálně delegován Tento vzor má několik implementačních variant, všechny však pracují s třídou produktu a tvůrce, od nichž se odvozují všechny ostatní. Produkt je abstraktní, jeho potomci jsou konkrétními objekty, které chceme vytvářet. Tvůrce pak definuje tovární metodu, která vrací produkt. Jednou variantou pak je definovat tovární metodu jako abstraktní a používat pouze potomků, druhou variantou je používání rodičovské tovární metody pro vytváření nějakého konkrétního produktu. Další z možných úprav tohoto vzoru je parametrizovaná tovární metoda, kde je typ vracených produktů určován parametrem metody. Důsledek tohoto vzoru je větší flexibilita při vytváření objektů a jejich snažší úprava a také větší přehlednost vztahů mezi jednotlivými typy objektů. Prototype Vytváří nové objekty pomocí prototypových instancí a jejich následného kopírování. Měl by být použit za předpokladu, že systém má být nezávislý na tom, jak se jeho produkty tvoří, sestavují a reprezentují a je také splněna alespoň jedna z těchto podmínek: • třídy, které se mají instanciovat, jsou určeny až za běhu aplikace • nechceme tvořit složitou hierarchii továren kopírující hierarchii produktů • instance třídy mohou nabývat jen omezených kombinací stavů a chceme ušetřit zdroje potřebné k opakovanému vytváření nových instancí s odpovídajícím stavem. Tento vzor pracuje s třídou produktu, který definuje klonovací metodu (v jazyce Java se jedná o metodu clone()). Konkrétní produkty jsou potomci této třídy, klient je klonuje pomocí rozhraní, které poskytl jejich rodič. Výhody prototypu se v mnohém shodují s výhodami abstraktní továrny a stavitele. Jeho další přednosti ale vyniknou spíše v jazycích, které neumožňují práci s třídami jako objekty. Objektový přístup ke třídám lze totiž použít k řešení těchto problémů. Singleton Zajišťuje, že třída bude mít vždy právě jednu instanci a poskytuje k ní jeden globální přístupový bod. Je to jeden z nejčastěji používaných vzorů. Používá se v případech, kdy potřebujeme: 23 • aby existovala právě jedna instance třídy a klienti k ní mohli přistupovat přes veřejně známý přístupový bod • aby tato instance byla rozšiřitelná potomky a klient mohl tuto rozšířenou instanci používat bez úpravy jejich kódu. Jedináček se realizuje tak, že jeho konstruktor skryjeme, čímž zajistíme, že jej klient nebude moci vytvořit. V jazyce Java toho dosáhneme označením konstruktoru jako private. Pokud chceme pracovat s více typy jedináčků a z nich vybírat až za běhu, jedna z možností je práce s registrem jedináčků, který je sám jedináčkem. Přístupů pro práci s jedináčky je více, ale tento je z nich nejflexibilnější a nejvíce „objektový“. Používání jedináčků přináší celou řadu výhod. Krom snadného zajištění vytvoření právě jedné instance je to zpřehlednění kódu snížením počtu globálních proměnných, které bychom museli jinak použít. V případě použití registru potomků je to navíc možnost zvolení konkrétního typu jedináčka za běhu. Oproti použití statických třídních operací získáváme větší přizpůsobivost například v tom, že můžeme pracovat i s jiným počtem instancí než jenom jednou, což může být v některých případech výhodné. Structural Patterns Zabývají se skládáním tříd a objektů do větších celků. V případě tříd je to používání dědičnosti za účelem vytvoření nových rozhraní nebo implementací. V případě objektů je to popis, jak složit více objektů k vytvoření nové funkcionality. Adapter Smyslem tohoto vzoru je přizpůsobit rozhraní třídy tak, aby bylo schopné fungovat s klientem, který očekává jiné rozhraní. Je dobré ho použít za těchto předpokladů: • chceme použít existující třídu, ale její rozhraní nevyhovuje našim potřebám • chceme vytvořit třídu, která bude znovupoužitelná i v případě práce s nyní neznámými nebo nedefinovanými třídami • chceme používat dědičnost, ale nechceme pro každý objekt zvlášť upravovat rozhraní. Ať už se volí jakákoli z implementačních variant tohoto vzoru, vždy je dobré mít na paměti, že bychom se vždy měli snažit pracovat s co možná nejužším rozhraním. Tedy takovým, které plní co nejmenší počet funkcí. Rozsáhlejší rozhraní bývají zpravidla nepřehledná, proto je lepší preferovat malá a specializovaná. Jeden adaptér je pak tvořen abstraktní třídou nebo rozhraním, které definuje abstraktní metody, které požaduje klient. Jeho potomci potom implementací těchto metod určují, jak se klientův požadavek bude překládat do rozhraní adaptované třídy. Krom možnosti úpravy rozhraní má tento vzor ještě dvě výhody: dokáže měnit rozhraní více tříd v případě, že používáme dědičnost (adaptér lze použít i na všechny potomky). Také ztěžuje obcházení funkcí třídy, na kterou je adaptér použit. Bridge Cílem tohoto vzoru je oddělit abstrakci od implementace tak, aby se tyto dvě mohly libovolně měnit nezávisle na sobě. Most bychom měli použít, pokud : • chceme zabránit provázanosti abstrakce a implementace. Často tomu tak je, pokud chceme implementace měnit za běhu programu • jak abstrakce, tak implementace má být rozšiřitelná děděním • změny v abstrakci by se neměly dotknout klientů (např. vynucením přepsání části kódu klienta). Implementace tohoto vzoru je velice jednoduchá. Místo jedné hierarchie tříd zavedeme hierarchie dvě. Ta, která představuje implementaci, se odkazuje na tu, která představuje abstrakci. 24 Kromě výše zmíněných dopadů přináší použití mostu několik dalších pozitiv. Mimo jiné to je lepší strukturování systému, protože tento vzor podporuje zavádění vrstev (implementace je součástí vyšší vrstvy než abstrakce) a odstínění klienta od vnitřní struktury kódu, který používá. Composite Skládá objekty do stromových struktur tak, aby reprezentovaly hierarchie se vztahy část-celek. To umožňuje přistupovat ke všem jednoduchým i složeným objektům uniformě. Po tomto vzoru bychom měli sáhnout, když: • chceme vytvořit hierarchii, která v sobě obsahuje vazbu typu část-celek • chceme, aby klienti nemuseli rozlišovat mezi objekty jednoduchými a objekty složenými Hlavním prvkem tohoto vzoru je komponenta, která je abstrakcí jak jednoduchého objektu, tak objektu složeného. Definuje standardní chování vlastní všem objektům hierarchie, včetně metod potřebných pro sestavování složených objektů. Tyto metody jsou v komponentě zpravidla prázdné a jsou potom u složených objektů přepsány tak, aby skutečně vykonávaly svou funkci (přidávaly objekty apod.). Kompozita určuje hierarchie, které obsahují jak objekty jednoduché, tak objekty složené z těchto jednoduchých objektů, ale i jiných objektů složených. Tím zjednodušují kód na straně klienta, který nerozlišuje, s jakým typem objektu pracuje; často to ani neví. Také umožňuje snadné přidávání nových tříd, protože máme jasně dáno, co musí nové typy objektů splňovat. Toto ale s sebou nese určitá rizika, neboť je obtížnější omezovat nové typy objektů (můžeme například vyžadovat, aby spolu dvě konkrétní třídy netvořily složený objekt).Proto musíme počítat s nutností provádět některé kontroly za běhu. Decorator Dynamicky přiřazuje objektům dodatečnou funkcionalitu a poskytuje flexibilní alternativu k dědičnosti. Používá se tam, kde: • chceme zajistit dynamické a transparentní přidělování odpovědností objektu, aniž bychom ovlivnili okolní objekty • je zapotřebí funkcionalita, kterou je možné odstranit za běhu • je použití dědičnosti nepraktické; zejména se jedná o případy, kde by přidáním nových funkcí neúměrně narostl počet tříd. Tento vzor pracuje s třídami komponent, to jest objektů, kterým chceme přidávat funkcionalitu. Ty dědí od rodičovské abstraktní komponenty, od které také dědí abstraktní dekorátor, jehož potomci potom představují právě přidávanou funkci nebo stav. Výhodou oproti statickému přístupu v podobě přidávání funkcí pomocí dědičnosti je to, že se jedná o výrazněji flexibilnější způsob. Dekorátory totiž umožňují své kombinování, takže jednomu objektu můžeme přidat více než jednu funkcionalitu. Navíc je možné používat je opakovaně, pokud chceme dosáhnout znásobení stejné funkcionality. Díky tomuto přístupu spotřebováváme systémové prostředky jenom v okamžiku, kdy je to nezbytně nutné. Stinnou stránkou tohoto vzoru ale je relativně vysoká komplexnost, která mimo jiné ztěžuje například debugování. Navíc je nutné dávat pozor na chyby, které mohou vzniknout při určování identičnosti a ekvivalence objektů, protože objekt s dekorátorem není shodný s objektem bez dekorátoru, ač mohou být jejich vnitřní stavy a proměnné stejné. Facade Poskytuje unifikované rozhraní skupině rozhraní nebo podsystému, které zjednodušuje jeho používání. Po tomto vzoru sáhneme, pokud nastane některá z těchto situací: • podsystém je složitý, ale my mu chceme přiřadit rozhraní, které bude snadno čitelné a neměnné • existuje příliš mnoho vazeb mezi klientem a třídami podsystému, které znamenají přílišné propojení 25 klienta se systémem a tím pádem špatnou přenositelnost • chceme používat vrstvy, které spolu budou komunikovat přes jasně dané rozhraní. Tento vzor je implementačně velice jednoduchý. Třída fasády implementuje rozhraní požadované klientem, požadavky obdržené voláním metod potom předává dál jednotlivým částem podsystému, které je dále zpracují. Obdržené výsledky zpracuje tak, aby byl výsledný výstup pro klienta čitelný. Výstupem je krom výše uvedených důsledků i snížení provázanosti v rámci systému, což kromě zvýšení přehlednosti znamená i to, že po případných úpravách uvnitř podsystému musí dojít pouze k úpravě kódu fasády, nikam jinam se změny nepropagují. Flyweight Používá sdílení k zajištění vysoké granularity a efektivity při používání většího množství objektů. Protože prospěšnost tohoto vzoru velmi závisí na kontextu, ve kterém je použit, je nutné používat tento vzor jen pokud jsou splněny následující podmínky: • aplikace používá velké množství objektů • ukládání objektů je spojeno s velkou režií, která je dána právě kvantitou objektů • většina stavů objektů jsou vnější, tj. lze je reprezentovat vně objektu • velké množství skupin objektů lze nahradit malým množstvím sdílených objektů v momentě, kdy odstraníme stavy reprezentovatelné mimo objekt • aplikace není závislá na identičnosti objektů. Jádro tohoto vzoru tvoří továrna, která má na starost vytváření a správné sdílení objektů „muší váhy“. Pokud přijde továrně žádost o objekt, nejdříve zkontroluje fond, kde jsou všechny doposud vytvořené sdílené objekty uloženy. V případě, že objekt nenalezne, vytvoří nový a uloží ho do fondu. Nakonec odkaz předá klientovi. Metody sdílených objektů jsou určeny abstraktním objektem „muší váhy“. Tento vzor s sebou přináší zvýšené nároky na výpočetní kapacitu pro přenos, vyhledávání nebo výpočet vnějších stavů, což je ovšem vyváženo snížením nároků na paměť. Míra snížení paměťových nároků je dána mnoha faktory jako počet objektů, množství možných stavů apod. Proxy Poskytuje zástupce pro další objekt a řídí přístup k němu. Používá se pokaždé, když potřebujeme chytřejší způsob odkazování než jen obyčejnou referenci nebo ukazatel. Zpravidla zastupuje objekty, které splňují některou z těchto vlastností: • objekt nacházející se v jiném adresním prostoru (vzdálená proxy) • vzdálený objekt, který není k dispozici na počítači, kde program běží (virtuální proxy) • velký objekt, který je náročný na vytvoření a udržování (virtuální proxy) • chráněný objekt, kterému potřebujeme různá přístupová práva pro různé klienty (ochranná proxy) • zamykatelný objekt, ke kterému smí najednou přistupovat pouze jeden klient (ochranná proxy). Objekt i zástupce dědí od abstraktní třídy, která určuje rozhraní zastupovaného objektu. Zástupný objekt zpracovává příkazy od klienta a pak je určí, zda je zastupovanému objektu předat nebo ne. Není možné volat přímo metody zastupovaného objektu. Vzdálená proxy umožňuje předávat požadavky na objekty z jiných adresních prostorů, aniž by o tom klient věděl. Virtuální proxy odkládá načtení velkého objektu do paměti, dokud není jeho instanciace nezbytně nutná. Ochranné proxy ověřují, zda má daný klient právo v danou chvíli nakládat s objektem. 26 Behavioral patterns Pracují s algoritmy a přiřazováním zodpovědností objektům. Popisují nejenom samotné třídy a objekty, ale také komunikaci mezi nimi, poskytují řešení správné kontroly toku. Pokud pracují pouze s třídami, používají dědičnosti, pokud s objekty, zpravidla přichází ke slovu kompozice. Chain of responsibility Zabraňuje příliš těsné vazbě mezi odesílatelem požadavku a jeho příjemcem tím, že dává možnost zpracování tohoto příkazu většímu množství objektů. Příjemci jsou zřetězeni a předávají si zprávu tak dlouho, dokud ji některý článek řetězu nezpracuje. Řetěz odpovědnosti je vhodné použít v momentě, kdy: • více než jen objekt může zpracovávat požadavek, ale předem není známo, který objekt to nakonec bude • chceme zasílat požadavky jednomu z několika objektů, ale bez konkrétní specifikace příjemce • množina objektů, která zpracovává požadavky, by měla být specifikována dynamicky. Skládá se z abstraktní třídy obsluhovače, která definuje rozhraní zpracovávání událostí. Dále ze samotných obsluhovačů, které definují konkrétní reakce na události a také podmínky, za kterých událost předají následujícímu prvku v řetězu. Existují dvě implementační varianty: v první je v každém obsluhovači odkaz na následující prvek v řetězu, druhá k určení následnosti používá externí hierarchii. Velkou výhodou tohoto vzoru je snížení provázanosti, protože odesílatel ani příjemce o sobě nemusí přímo vědět, stejně tak jako řetěz nemusí znát svoji strukturu. Ze samé podstaty vzoru také plyne větší flexibilita, protože jednotlivé příjemce je možno z řetězu odebírat nebo do něj naopak přidávat za běhu aplikace. Je ale důležité uvědomit si, že jelikož je příjemce žádosti nespecifikován, v rámci řetězu vůbec nemusí dojít k jejímu zpracování. Command Obaluje žádost do podoby objektu, čímž umožňuje zasílání různých příkazů pouze změnou parametru, žurnálování a používání front žádostí, stejně jako podporu stornovatelných operací. Použijeme ho, pokud chceme: • pracovat s voláními, které se provedou až po určité době • vytvářet, řadit do fronty a vykonávat požadavky v různých časech • používat funkci obnovení stavu před provedení volané metody undo/redo • pracovat s logováním operací •sestavit systém s vysoko-úrovňovými operacemi nad systémem s nízko-úrovňovými operacemi; takové systémy často používají transakce. Vzor příkaz se skládá z třídy příkazu, který definuje abstraktní metody vykonání operace. Konkrétní příkazy dědí od této třídy a překrýváním zmíněných metod dodávají konkrétní funkcionalitu. Příjemce vykonává operace určené příkazem, třída vyvolávače určuje, v jaký moment bude operace provedena. V případě, že požadujeme podporu undo/redo, je nutné ukládat kopie příkazů do seznamu a zajistit, aby si příkaz pamatoval, jak a za jakých okolností byl spuštěn (toto se zpravidla bude velmi lišit program od programu). Příkaz zavádí volnější vazbu mezi objekty vyvolávajícími a vykonávajícími operaci. Navíc, jelikož je příkaz plnohodnotný objekt, můžeme jej rozšiřovat děděním nebo jej skládat do složitějších příkazů či maker pomocí vzoru Composite. Proto je relativně snadné přidávat nové příkazy, protože není nutné upravovat kód příjemce příkazu. 27 Interpreter Při daném jazyku definuje reprezentaci jeho gramatiky současně s tlumočníkem, který tuto reprezentaci používá k překladu vět v tomto jazyce. Tento vzor se použije v případě, že se v programu vyskytuje jazyk, který je třeba interpretovat, a zároveň se výrazy v něm dají vyjádřit v podobě stromů abstraktní gramatiky. Tlumočník pak nejlépe pracuje za těchto podmínek: • gramatika je jednoduchá; v případě složitých gramatik příliš narůstá množství tříd v hierarchii reprezentující gramatiku a stává se těžko spravovatelnou • efektivita nemá nejvyšší prioritu; syntaktický strom zpravidla není nejefektivnějším přístupem, použitím stavových automatů se obyčejně dosahuje lepších výsledků. Tento vzor pracuje s třídou abstraktního výrazu, který definuje abstraktní metody společné všem uzlům abstraktního stromu. Od toho pak dědí terminální a neterminální výrazy, které určují konkrétní chování. Syntaktický strom je potom sestaven klientem na základě kontextu. Důsledkem tohoto vzoru je snadná změna či rozšiřování gramatiky, zároveň je celá gramatika relativně jednoduchá. Co se týče implementace, také je jednodušší přidat nový způsob interpretace gramatiky. Nevýhodou je bezpochyby to, že rozsáhlejší gramatiky je obtížné spravovat. Iterator Poskytuje nástroj sekvenčního přístupu k prvkům agregovaného objektu bez odkrytí jeho vnitřní reprezentace. Jeho použití je vhodné v těchto případech: • k zajištění přístupu k agregovanému objektu tak, aby jeho vnitřní reprezentace zůstala klientovi skryta • k podpoře vícenásobného průchodu agregovanými objekty • k poskytnutí uniformního rozhraní pro procházení různých agregovaných struktur (to znamená k podpoře polymorfní iterace). Jádro vzoru tvoří třídy agregátu a iterátoru, jejichž abstraktní metody určují rozhraní jejich potomků. Iterátory pak definují konkrétní metody pro procházení agregátu, agregát definuje metodu, která vytváří instanci iterátoru pro daný typ agregátu. Procházení může být kontrolováno buď iterátorem, nebo klientem, samotný iterační algoritmus může být obsažen stejně dobře v iterátoru jako v agregátu. Díky iterátoru můžeme volit různé způsoby průchodu komplexních agregovaných objektů, zároveň ale máme přehled o tom, jaká pravidla tento konkrétní průchod splňuje. Také není problém, aby s jedním agregátem pracovalo více iterátorů, které se navzájem nijak neovlivňují. V neposlední řadě dochází ke zjednodušení rozhraní agregovaného objektu. Mediator Definuje objekt, který obaluje skupinu objektů a interakce mezi nimi. Mediátor vynucuje snížení provázanosti odstraněním explicitních referencí mezi objekty této skupiny a umožňuje jejich interakce nezávisle měnit. Můžeme ho použít, pokud: • množina objektů spolu komunikuje jasně definovaným, ale přesto komplexním způsobem, a výsledné závislosti jsou nestrukturované a těžko pochopitelné • chceme zvýšit znovupoužitelnost objektů, které to nyní kvůli referencím na okolní objekty a komunikaci s nimi neumožňují • potřebujeme chování, které bude rozvržené mezi několik tříd a které bude snadno upravitelné bez rozsáhlejšího použití dědičnosti. Tento vzor pracuje s třídami mediátorů a kolegů, jejichž rozhraní opět určuje rodičovská třída s abstraktními metodami. Mediátor obsahuje reference na všechny kolegy, jejichž komunikaci řídí. Každý z kolegů obsahuje referenci pouze na mediátora a s ostatními kolegy komunikuje přes něj. 28 Použití tohoto vzoru s sebou přináší významné omezení počtu tříd, neboť nové chování vyžaduje často pouze nového potomka mediátora a ne změnu všech objektů ve skupině. Dále zmenšuje množství komunikačních kanálů z M:N na 1:N a přináší abstrakci komunikace objektů. Tím zvyšuje pochopitelnost a přináší centralizovanou kontrolu. Memento Bez narušení zapouzdření zjistí a uloží vnitřní stav objektu tak, aby bylo možné objekt navrátit do vnitřního stavu později. Používá se, pokud platí obě následující podmínky: • chceme ukládat vnitřní stav objektu • použití rozhraní, které by umožňovalo přímý přístup by narušilo zapouzdření a nežádoucím způsobem by odhalovalo vnitřní strukturu objektu. Skládá se ze správce, mementa a původce. Memento je objekt, který ukládá vnitřní stav původce, což je objekt, jehož stav chceme uložit pro pozdější použití. Tento má plný přístup k datům uloženým uvnitř mementa. Správce je objekt, který si vyžádá memento od původce, ale nikdy nepracuje nad jeho daty, nanejvýše smí memento předat jinému objektu. Memento zachovává princip zapouzdření a zjednodušuje původce, protože veškerá logika spojená se správou uložených stavů je ponechaná na klientovi a správci. Nicméně použití tohoto vzoru může být paměťově náročné, pokud se ukládá větší objem dat, navíc sám správce neví, jak velkou část objektu je v mementu uložena a tudíž je obtížně zjistitelné, jak velké paměťové nároky klade na paměť. Observer Definuje závislost 1:N takovou, že pokud se změní stav objektu, všem objektům na něm závisejícím je o tom předána informace. Lze ho aplikovat v libovolném z následujících situací: • abstrakce má více aspektů, na sobě navzájem závislých, které chceme zapouzdřit do samostatných objektů, abychom zvýšili znovupoužitelnost (příkladem mohou být dvě grafické reprezentace nad jedněmi daty) • změna jednoho objektu vyžaduje změnu ostatních, přičemž nevíme, kolik těchto objektů je • objekt by měl být schopen zpravit ostatní objekty bez znalosti toho, o jaké konkrétní objekty se jedná. Vzor se skládá z pozorovatele a subjektu, oba mají opět rozhraní určena rodičovskými třídami a jejich abstraktními metodami, které překrývají. Subjekt udržuje reference na všechny pozorovatele, poskytuje rozhraní na jejich přidávání a odstraňování, v případě změny vnitřního stavu umí všechny pozorovatele informovat. Pozorovatel zpracovává zprávy o změnách stavu, může se jednat o prosté vyvolání metody bez parametrů. V případě, že je pozorovatel zaregistrován u více subjektů, je vhodné parametricky určit objekt, ze kterého zpráva přišla. Pozorovatel přináší volnou vazbu mezi pozorovatelem a subjektem, podporu plošného posílání zpráv, protože jednou notifikací jsme schopni vyvolat reakci v neomezeném množství objektů. Je ovšem důležité uvědomit si, že při častých změnách subjektu a velkém počtu pozorovatelů může být použití tohoto vzoru výpočetně náročné. State Dovoluje objektu měnit chování při změně jeho interního stavu. Objekt budí dojem, že se změnila jeho třída. Použití tohoto vzoru se doporučuje, pokud platí následující: • chování objektu závisí na jeho vnitřním stavu, jeho chování se musí měnit za běhu v závislosti na tomto stavu • metody obsahují rozsáhlé systémy podmínek pracující se stavem objektu, které se v kódu často opakují; tento stav je většinou reprezentován jedním či více výčtovými typy. 29 Vzor pracuje s třídou kontextu, která reprezentuje objekt nabývající různé stavy, ty jsou potom definovány třídami konkrétních stavů, které dědí od svého abstraktního předka, který určuje jejich rozhraní. Každý tento konkrétní stav pak definuje chování, které danému stavu přísluší. Podmínky pro přechody mezi stavy mohou být definovány jak v kontextu, tak ve stavech samotných. Přínosem tohoto vzoru je jasné určení, jaké chování je ovlivněno změnou stavu a jak přesně se bude v daném stavu chovat. Zároveň je stav čitelnější než v případě použití vnitřní proměnné. Tyto stavy navíc mohou být bez problémů sdílené mezi objekty, čímž se zvyšuje znovupoužitelnost kódu. Strategy Definuje rodinu algoritmů, zapouzdřuje je a umožňuje jejich zaměnitelnost. Algoritmus se tedy pak liší od klienta ke klientovi. Použijeme ho v těchto případech: • pracujeme s množstvím tříd, které se liší pouze svým chováním • chceme pracovat s různými variantami algoritmu plnícího stejnou úlohu • algoritmus pracuje s daty, která nemají být přístupná klientovi • třída definuje mnoho způsobů chování, z nichž je vybíráno složitým systémem vnořených podmínek. Vzor se skládá z abstraktní třídy strategie, která definuje rozhraní pro všechny podporované algoritmy. Od té potom dědí konkrétní strategie, kde je implementován samotný algoritmus. Tyto objekty se potom použijí k nastavování chování kontextu. Ten může definovat rozhraní, které umožňuje strategiím přistupovat k jeho datům. Po použití tohoto vzoru získáme rodiny souvisejících algoritmů, což zvyšuje přehlednost kódu. Zároveň získáváme flexibilnější variantu k úpravě funkcionality děděním, zároveň nepoužívají složité větvení podmínek. Nevýhodou je navýšení počtu objektů a režie spojené s komunikací mezi objekty. Template Method Definuje kostru algoritmu v operaci, ale implementaci některých jejích kroků ponechává na svých potomcích. Ti tak mohou měnit měnit tyto kroky a tak i podstatu algoritmu bez změny jeho struktury. Jeho použití se doporučuje, když: • je výhodné definovat neměnné části algoritmu v rodičovské třídě a definici proměnných částí ponechat na potomcích • potomci některé rodičovské třídy vykazují podobné chování • vyžadujeme kontrolu nad potomky; použitím šablonové metody dáváme možnost změny chování potomků jen v přesně určených místech. Abstraktní třída obsahuje šablonovou metodu, která je stejná pro všechny potomky, a dílčí operace, které jsou v potomcích různě implementovány a způsobují tak rozdílné chování operace. Hlavní síla tohoto vzoru je v zajištění znovupoužitelnosti kódu, kterou s sebou přináší. Většinou se uplatňuje v knihovních metodách. Poskytuje také inverzi řízení, protože volání metod potomků určuje jejich rodičovská třída. Visitor Představuje operaci, která má být vykonána na částech datové struktury. Umožňuje definování nové operace bez úpravy tříd, se kterými pracuje. Jinými slovy se jedná o oddělení algoritmu od struktury, se kterou pracuje. Používá se, pokud: • datová struktura obsahuje třídy objektů s odlišnými rozhraními a na těchto objektech chceme provádět operace 30 • nad objekty ve struktuře objektu je nutné vykonávat množství rozdílných operací, které v průběhu času přibývají, toto ale nechceme řešit připisováním nových metod • struktura objektu se málo mění, ale potřeba nových operací nad nimi je celkem častá. Skládá se z návštěvníků, kteří implementují operace definované jejich rodičovským rozhraním. Zajišťují prostředí pro běh daného algoritmu a ukládají jeho stavy. Stejný vztah je u elementu, který definuje abstraktní metodu příjmu návštěvníka, a jeho potomci ji implementují. Každý element navíc poskytuje návštěvníkovi rozhraní, přes které může přistupovat k jeho datům. Poslední částí je datová struktura, která agreguje elementy, případně poskytuje rozhraní vyšší úrovně pro přístup návštěvníků ke všem jeho elementům. Díky tomuto vzoru je přidávání nových operací velice snadné. Navíc sdružuje související operace a odděluje ty nesouvisející, které jsou uzavřeny v třídách návštěvníků. Každý návštěvník může nést vnitřní stav, protože může navštívit jakýkoli element struktury. Nicméně je obtížné přidávat nové elementy, protože každý návštěvník musí mít oddělenou metodu pro každý element zvlášť a tudíž přidání vyústí v napsání více kódu. Také může porušit zapouzdření, protože aby mohl návštěvník s elementy správně fungovat, potřebuje dostatečně silné rozhraní. Analýza evaluačních metod a metrik V data miningu je evaluace velice důležitý pojem, bez kterého se prakticky nelze obejít. Ačkoli je používán ve více kontextech, většinou se slovem evaluace myslí hodnocení modelů. Většinou se provádí až na hotových modelech. Slouží k tomu, abychom byli vůbec schopni určit stupeň vhodnosti používání daného algoritmu. Používá se jednak k tomu, abychom měli představu, jak dobře bude model fungovat v budoucnosti, pak také jako nedílná součást tvorby modelů. Běžným postupem je vytvořit více modelů s odlišnými parametry a z nich vybrat ten nejvhodnější právě aplikací evaluačních metrik. Výstupem evaluace je vždy množina metrik, která popisuje soubor použitých algoritmů. Jelikož jsou ale tyto algoritmy často velice odlišné a mohou pracovat s rozdílnými typy dat, je těchto metod více a jen minimum z nich se dá použít univerzálně. Které metody to jsou bude diskutováno v popisech jednotlivých metod. Evaluace se samozřejmě nedělí jen podle metrik, které používáme. Další aspektem, který je mnohdy důležitější než výběr konkrétní metriky, je určení, oproti čemu bude účinnost klasifikačních metod hodnocena, jaká data budou určena jako trénovací a jaká data budou testovací. V případě, že bychom data trénovali a testovali na stejné množině, byly by výsledky vychýleny. V tento moment se projeví princip optimismu, kdy metoda ověřovaná na stejných datech, nad kterými byla postavena, má v reálu horší výkonost. Plyne to z toho, že metoda dosahující nejlepších výsledků při trénování musí nutně dosáhnout nejlepších i při testování a tudíž má test nulovou vypovídací hodnotu. Tak se snadno může stát, že jsme vybrali sice podle výsledků nejlepší, ale ve skutečnosti špatnou metodu, která je pouze extrémně přizpůsobena konkrétním datům. V takovém případě metodu nazýváme přeučenou, nebo také metodu s vysokým stupněm zaujetí. Přeučení v tomto případě znamená, že je metoda až příliš přizpůsobená konkrétním datům a je velká pravděpodobnost, že na jiných datech bude neefektivní. Proto se používají různé postupy, jak odhalit přeučené metody tím, že na trénování a testování se použijí jiná data. Tyto data spolu samozřejmě musí souviset a musí pocházet ze stejného zdroje. Data, která by se týkala stejné problematiky, ale byla by získávána různými způsoby, se mohou lišit ve významu atributů.[7] Níže uvedené systémy pracují především s metrikou chybovosti modelu. Hold-out Data, která máme k dispozici, si rozdělíme na dvě skupiny, z nichž jedna je použita pro testování a druhá pro trénování. Čím větší je množství dat v trénovací skupině, tím přesnější je potom klasifikační metoda, nicméně menší množství dat testovacích znamená, že hodnocení modelu je více náchylné k chybě a musíme uvažovat velkou toleranci získaných hodnot. Opačný stav vede k tomu, že hodnocení modelu má velkou přesnost, ale klasifikační model má horší pravděpodonost správného natrénování a jeho očekávaná chyba je 31 větší. Obvyklé je používat poměr 70:30, kdy 70% dat určíme jako trénovací a zbytek jako testovací. Výhodou tohoto systému je zejména jeho rychlost, stinnou stránkou je pak nebezpečí špatně zvolených testovacích dat. Pokud náhodou vybereme chybně a testovací data budou mít extrémní hodnoty, budou ve shluku či budou jinak nestandardní, je takto získané ohodnocení modelu prakticky nepoužitelné. Použití tohoto systému se příliš nevyplácí, je možné ho použít jako doplňkový, pokud se nám podaří dodatečně získat další data, která již nemohou být použita v procesu. Jinak je pro svojí nepřesnost a malou flexibilitu prakticky nevyužitelný. Tento systém se ale často používá v jednom konkrétním případě. Společnosti, které disponují bohatými datovými zdroji, si nechávají vyhotovit klasifikační problémy externě a chtějí si jejich správnost následně ověřit. Data jsou proto rozdělena do dvou skupin. Jedna skupina dat je poskytnuta vyhotoviteli, který na něm postaví své klasifikační metody. Po odevzdání výsledného modelu zadavateli pak tento provede ověření správnosti modelu na druhé skupině dat, kterou si ponechal.[7] K-fold křížová validace Systém křížové validace je asi nejčastěji používaný. Počítá s tím, že máme relativně velké množství dat. Fold je v tomto případě náhodně vybraná podmnožina dat, kdy všechny foldy jsou stejně velké. Jejich počet je pak určen číslem k, které zvolí uživatel. Po vytvoření foldů se pak postupuje v cyklech, kdy se jeden z foldů použije k testování a zbytek dat je použit k trénování modelu. Tento cyklus se opakuje k-krát. Získané metriky se potom zprůměrují a takto získáváme konečný výstup. Ten velice dobře odráží to, jak se daný model bude chovat v budoucnosti na doposud neznámých datech. Výhodou křížové validace je její flexibilita. Podle množství foldů, které si nastavíme, můžeme snadno ovlivňovat rychlost a přesnost. Čím více foldů, tím větší je přesnost, protože eliminujeme pravděpodobnost většího vlivu náhodných skupin nestandardních dat. Samozřejmě větší množství foldů znamená i delší výpočetní doba, protože tolikrát se musí procházet všechna data. V praxi je běžné používání deseti foldů, kdy je již dostatečně velká přesnost, zároveň se jedná o rozumně rychlou variantu. Nedoporučuje se používat hodnoty menší než 5, protože výsledky jsou příliš zkreslené. Křížová validace se ještě používá ve specifické variantě stratifikované křížové validace. V této metodě se zohledňuje to, že na modelu se projeví i rozložení výstupů napříč použitými daty. Toto je důležité zejména u klasifikace a regrese. Pokud by se totiž stalo, že některé foldy obsahují jen výstupy jednoho typu (u klasifikace), či hodnoty jen v jedné části intervalu výstupů (u regrese), zavádíme tak do modelu výchylku. Toto se odstraňuje stratifikací, kdy se v každém foldu snažíme dodržet stejné rozložení výstupů, jako je tomu u celé množiny dat. U klasifikace se snažíme zachovat stejný poměr zastoupení jednotlivých tříd, u regrese se pak řídíme střední hodnotou výstupů.[7] Leave-one-out Tento přístup je vlastně variantou křížové validace, kde počet foldů k je shodný s počtem instancí. Dochází tedy k tomu, že trénování modelu probíhá vždy na celé množině s vynecháním pouze jediné instance. Tato metoda se používá v případech, kdy máme k dispozici pouze omezené množství dat (obvykle se uvádí méně jak sto instancí). Výhodou je velká přesnost evaluace. Nevýhodou je výpočetní náročnost vzhledem k počtu cyklů, kterými se v rámci tohoto systému prochází a nízká flexibilita.[7] Validace opakovaným vytvářením náhodných vzorků Další blízký příbuzný křížové validace. Jedná se o systém, který pro testovací a trénovací data používá náhodně vybraných podmnožin dat, přičemž je pouze dodržována zásada, že množina trénovacích dat je vždy větší než množina dat testovacích. Výhoda oproti křížové validaci je v tom, že neexistuje vztah mezi velikostí podmnožin a počtem opakování. Tento systém se v oblasti data miningu příliš nepoužívá. Ač je velice flexibilní, jeho problémem je 32 neopakovatelnost, která plyne z náhodnosti vzorků. Také zapříčiňuje, že nemáme záruku, že se nám v různých cyklech nebudou opakovat stejná instance v testovacích. Tím se můžeme dostat do stejných problémů, jaké jsou u systému hold-out, pokud opakovaně do testovacích dat zařadíme instance s extrémními hodnotami. Tomu se sice můžeme vyhnout použitím stratifikace podobně jako u křížové validace, tím ale narážíme na problém. Protože v každém kroku cyklu se volí vzorek dat náhodně, není možné si předem rozdělit množinu dat tak, aby se zajistila ve všech krocích. Tím pádem by bylo třeba vybrané vzorky upravovat před každým trénováním tak, aby si stratifikačně odpovídali, což by výrazně zvedlo výpočetní náročnost.[15] Bootstrap Tento systém se někdy uvádí pod jménem Bootstrap 0.632. Používá se v situacích, kdy je k dispozici extrémně malé množství dat, obvykle se uvádí pod padesát instancí. Systém spočívá v tom, že vždy v jednom kroku cyklu rozdělíme data na dvě části, na trénování se použije ta o velikosti hruba dvou třetin. Oproti výše zmíneným metodám ale tentokrát ohodnotíme model jak na testovacích, tak na trénovacích datech, přičemž výsledná hodnota je dána rovnicí: e=0.632∗e train0.368∗e test Tento postup opakujeme tolikrát, kolik instancí máme. Výsledek je průměr hodnot. Bootstrap se používá prakticky výhradně v případech, kdy máme k dispozici malé množství dat, kdy má oproti ostatním výhodu vyšší přesnosti. Při větším počtu instancí nemá oproti ostatním přístupům žádné výhody – je výpočetně náročný, není flexibilní a neposkytuje výrazně větší přesnost.[7] Metriky V následujících odstavcích jsou posány metriky, které se používají k ohodnocování modelů v data miningu. Uvedl jsem i metriky, které sice neslouží přímo k hodnocení modelu, ale ostatní metriky se o jejich hodnoty mohou opírat. Chyba Chyba modelu je základní statistika, která se používá na určení správnosti modelu. Je to numerická hodnota, která určuje rozdíl mezi skutečnou hodnotou atributu, které klasifikujeme a jeho predikovanou hodnotou. Chyba modelu se uvádí vždy jako průměr chyb jednotlivých klasifikací. Jsou celkem 3 způsoby výpočtu chyb, z nichž je nejpoužívanější ta poslední: běžná, absolutní a kvadratická. m 1 Err = ∑ y i − y 'i m i=1 m Err ab = 1 ∑ ∣y − y '∣ m i=1 i i Err sq= 1 yi − y 'i 2 ∑ m i=1 m Chybu lze potom potom rozdělit podle toho, na jakých datech ji počítáme. Chyba trénovací je taková chyba, kterou model vykazuje na těch datech, na kterých byl vytvořen. Chyba testovací se zjišťuje na datech, u kterých známe hodnoty výstupů, ale které jsme nepoužili pro trénování. Chyba reálná je potom chyba, kterou bude model vykazovat na všech datech, které mu předložíme. Tuto chybu samozřejmě neznáme, ale snažíme se jí co nejlépe přiblížit. Zaujetí Zaujetí nebo anglicky bias je metrika, která nám říká, jak moc se očekávaná hodnota modelu (ta je rovna 33 průměru ze všech možných hodnot, kterých může nabývat vynásobených jejich váhou) liší od skutečné hodnoty parametru. Skutečná hodnota je v tomto kontextu uvažovaná ze všech možných hodnot, ne pouze z množin dat, kterou disponujeme. Bias=E − zde potom je očekávaná hodnota modelu a je pak skutečná hodnota. E Variance Variance je hodnota, která udává, jaká je míra rozptylu, průměr druhé mocniny možných hodnot modelu k očekávaným hodnotám. Var X =E [− E 2 ] Matice záměn predikovaná hodnota reálná hodnota třída A třída B třída C celkem třída A 3 5 8 16 třída B 7 4 6 17 3 13 4 13 20 34 27 třída C celkem Tabulka 1: matice záměn pro tři třídy Matice záměn je metrika, která se používá výhradně u klasifikace. Je to čtvercová matice, jejíž velikost je dána počtem tříd, do kterých klasifikujeme. Řádky představují třídy, které jsme klasifikovali pomocí našeho modelu, sloupce jsou potom reálné hodnoty. Hodnoty, které jsou na jednotlivých pozicích matice, potom představují, kolik příslušníků třídy sloupce bylo ve skutečnosti klasifikováno jako příslušníci třídy řádku. (tabulka č.1) Evaluace klasifikace Pod tímto pojmem jsou myšleny metriky, které se vztahují ke klasifikaci. Ta klasifikuje hodnoty do jednoho ze dvou možných stavů, které se označují jako pozitivní a negativní. Tyto metriky lze samozřejmě použít i v případě klasifikace do více tříd, nicméně se posuzuje každý model zvlášť, kdy pozitivní stav znamená příslušnost k dané třídě. Patří sem následující metriky: • TPr, true positive rate - určuje, kolik instancí do třídy patřící bylo správně klasifikovaných, také bývá označována jako citlivost nebo recall • TNr, true negative rate - určuje, kolik instancí do třídy nepatřící bylo správně klasifikovaných, bývá též označována jako specificita • FPr, false postive rate - určuje, kolik instancí do třídy nepatřící bylo špatně klasifikovaných jako patřící do třídy • FNr, false negative rate - určuje, kolik instancí do třídy patřící bylo špatně klasifikovaných jako nepatřící do třídy • Pre, precision - určuje, kolik instancí, klasifikovaných jako do třídy patřící do třídy patřilo i ve skutečnosti • F-1 measure – je kombinace výše uvedených metrik, která se používá k identifikaci takového nastavení, které je vhodným kompromisem mezi výše uvedenými metrikami 34 F1= 2∗Pre∗TPr PreTPr Z těchto hodnot lze potom dopočítávat další metriky. Například je vidět, že chybu modelu spočítáme tak, že sečteme falešně negativní a falešně pozitivní klasifikace a ty potom podělíme celkovým počtem tříd. [6] Matice cen Matice cen je metrika, která není výsledkem evaluace modelů. Často se s ní můžeme setkat, protože mnohé metriky ji mohou zohledňovat ve svých výpočtech za předpokladu, že je definovaná. Tato matice ovlivňuje i samotné trénování modelu. Říká, jakou třídu by měl model preferovat v případě, že se rozhoduje mezi více variantami. Tuto metriku nelze nijak vypočítat či odvodit přímo z dat, většinou ji vytvářejí doménoví experti, kteří znají přesný význam dat a jejich klasifikace. Tato matice vypadá na první pohled stejně, jako matice záměn, ale význam čísel se významně liší. Číslo na souřadnicích (A,B) nám totiž říká, jaká je cena klasifikace do třídy A v případě, že je instance ve skutečnosti příslušníkem třídy B. Platí zásada, že klasifikace do správné třídy má nulovou cenu, jednotlivé ceny pak bývá zvykem uvádět v celých číslech, které se zpravidla pohybují od jedné do deseti. predikovaná hodnota třída A třída B třída C celkem třída A 0 1 2 3 třída B 5 0 1 6 třída C celkem 10 15 5 6 0 3 15 Tabulka 2: matice cen pro tři generické třídy V tomto příkladě (tabulka č.2) je ilustrováno, jak může matice cen vypadat. Zde jasně vidíme, že je pro nás výhodnější chybovat tím, že nesprávně označíme instanci jako patřící do třídy A, než chybně prohlásit instanci za příslušníka třídy C. Konkrétně by se mohlo jednat o například o klasifikaci na základě lékařských dat, která má zjistit, zda je pacient nemocen. Třída A by pak znamenala pokročilé stádium, třída B prvotní stádium a třída C znamenala, že je pacient zdráv. V případě, že by byla klasifikována přítomnost nemoci v libovolném stádiu, pacient by byl poslán na další sérii testů, které doporučí další léčbu. Z matice pak lze odvodit, že náklady za další testování pacienta jsou výrazně menší než náklady spojené s přehlédnutím nemoci a následnou léčbou. ROC křivka ROC křivka, někdy také označována jako hraniční křivka. Je to metrika, která nám popisuje chování klasifikátoru tím, že zaznamenává změnu citlivosti v závislosti na změně míry falešně negativních výsledků. Na obrázku (obrázek č.1) můžeme vidět příklad ROC křivky. Rovná čára, která rozděluje graf na dvě poloviny, představuje klasifikaci náhodným výběrem (z toho plyne její poloha, neboť náhodná klasifikace má stejnou pravděpodobnost zařazení do třídy pozitivní i negativní). Hranatá křivka potom představuje zkoumaný klasifikátor. Čím větší je konvexnost této křivky, tím je daný model lepší. Naší snahou totiž je přiblížit modely co nejvíce souřadnici (0,1), která představuje bod optimálního klasifikátoru.[10] Průběh ROC křivky získáme tam, že měníme hranici, která představuje rozdíl mezi klasifikací do positive a negative, která je běžně nastavena na hodnotu 0,5. Jejím posunem oběma směry získáme nové klasifikační výstupy, jejichž poměr citlivosti a false positive rate nám dává body na ROC. 35 Obrázek 1: ROC křivka klasifikačního modelu AUC AUC nebo-li plocha pod křivkou je mírou určování kvality modelu. Získává se tak, že spočítáme velikost plochy pod křivkou ROC. Čím větší tato plocha je, tím více se blížíme bodu (0,1), který představuje ideální klasifikátor. V následujícím obrázku (obrátek č. 2) obrázku je přehled, jakou plochu by měl mít výborný, dobrý či špatný klasifikátor. Tato metrika bývá také označována jako diskriminace, tedy schopnost správně rozlišovat negativní a pozitivní instance.[11] Obrázek 2: kvalita klasifikace vyjádřená metrikou AUC (křivky shora: skvělá, dobrá, špatná) Ostatní metriky Evaluačních metrik je samozřejmě mnohem více, ale jejich použití není příliš časté, už jen proto, že pro běžného uživatele je jejich význam nejasný. Používání takových metrik v data miningových software se tak snadno stane kontraproduktivním, protože tyto metriky mohou být neznalou osobou špatně interpretovány. Příkladem dalších metrik může být Cohenova kappa statistika (míra shody modelu s reálnými hodnotami) nebo tzv. informační hodnota (angl. information score) dvojice Kononenko & Bratko (hodnotí klasifikátory s tím, že zohledňuje pravděpodobnostní rozložení jednotlivých tříd). 36 Kapitola 4 Analýza Základní stavba Fake Game Konfigurace Vizualizace Načítání dat Preprocessing GAME Generování reportů PMML Obrázek 3: UML diagram základních modulů Fake Game Program lze takto rozdělit podle posloupnosti funkcí, které vykonává. Načítáním dat pracuje s obyčejnými textovými soubory. Pokud v nich najde odpovídající hlavičku v podobě výčtu atributů, s výstupními atributy opatřenými vykřičníky, nahraje jejich obsah do vnitřních struktur programu. Preprocessing není nezbytnou součástí zpracování. Mezi jeho nejvýznamnější funkce patří standardizace hodnot do intervalu <0,1>, která významně zvyšuje přesnost následně generovaných modelů. Jádro Game vytváří modely na základě konfigurace, která je mu dodána (ať už z externích souborů nebo z grafického uživatelského rozhraní). Modely pak lze ukládat ve formě PMML souborů a stejně tak je možno je i nahrávat. Výstupem jsou reporty a vizualizace, které vycházejí jak z původních dat, tak z modelů, které byly vygenerovány. Problémy spojené se současnou verzí Většina problémů tohoto programu je spojená s tím, že byl od začátku vyvíjen systémem ad hoc. Neexistovala větší koncepce o budoucím obsahu, která by umožňovala návrh programu po větších částech, na programu navíc spolupracovalo větší množství lidí, přičemž každý měl o funkci programu svojí představu. Toto se velmi projevilo na zapouzdření, které v mnohých částech programu prakticky neexistuje. Příkladem může být původní nastavení viditelnosti proměnných v třídě TreeData, která reprezentuje množinu instancí, která vstupuje do programu, i s jejich atributy a výstupními daty. Ačkoli hodnoty některých proměnných jsou přímo závislé na jiných, a tudíž jejich hodnoty musí vždy reflektovat tyto jiné hodnoty, je přístup k nim nastaven na public. Konkrétně je to například celočíselná hodnota iNumber, která určuje, kolik vstupních atributů se v nahraných datech nachází a měla by vždy odpovídat velikosti vektoru iAttr, uživatel ji může měnit z libovolné části programu. Asi nejzákladnějším problémem je prakticky nulová dokumentace. V novějším úsecích kódu se již začíná objevovat, ale staré části, které tvoří základní metody, které musí stejně všechny nově napsané moduly používat, dokumentovány nejsou vůbec. 37 Dalším problémem je, především tedy ve starších částech kódu, oddělení grafické reprezentace a obsahu. V třídách, které operují s grafickým rozhraním, obsahují kód, ve kterém se provádí výpočty nesouvisející s GUI a měnící vnitřní stavy programu. Toto je nejmarkantnější na třídě GMDHtree, která měla původně na starost nejen vykreslování oken, ale také se starala o uchovávání informací o síti, která prováděla trénování modelů. Poslední vývoj sice vedl k odstranění nejhorší prohrešků, ale stále je co opravovat. Jedním z nejvýraznějších problémů, který se významně podílí na počtu chyb v nově vznikajících částech, je celková nejasnost kódu. Proměnné jsou často pojmenované zkratkami, ze kterých není patrné, k čemu by měly sloužit. V těchto případech navíc není používána velbloudí notace, takže pak se můžeme setkat například s tím, že metoda getiVal() ve skutečnosti znamená „get input value“ a getStiValue() „get standardized input value“. Toto je navíc spojeno s absencí dokumentace, takže pro pochopení funkcí metod je často nutné zdlouhavě procházet kód a přesnou činnost a případná omezení číst přímo něj. Se všemi výše zmíněnými pak souvisí poslední neduh programu, kterým je redundance. Jelikož je obsah funkcí špatně čitelný, mnohdy se stává, že je zvolen přístup menšího zla, kdy je raději podmínka ověřována dvakrát, než aby nebyla ověřena vůbec. Dalším projevem pak je zajišťování kontrolních funkcí mimo metody, se kterými souvisí, přestože tyto jsou v nich již obsaženy. Toto je typické zejména pro volání metod jiných balíčků, kde je autorem jiná skupina lidí a proto je zde nízká znalost kódu. Analýza evaluačních metod ve Fake Game Cílem tohoto rozboru bylo určit, jaké evaluační metody jsou v této chvíli v programu Fake Game k dispozici a jak jsou implementovány. Dále jsou metody analyzovány v kontextu správnosti kódu a použitých návrhových vzorů. Nakonec je určeno, zda je tato metoda vhodná a je možné ji použít při rozšiřování aplikace, nebo je vhodné ji nahradit jinou metodou. Také je diskutována možnost použití návrhových vzorů do budoucna. Ve Fake Game jsou modely uloženy v singleton objektu Models, data pak v singletonu TreeData. Obě dostupné evaluace vždy hodnotí pouze ty modely, které jsou v singletonu. Evaluace modelů přes metodu saveToFile() Program Fake Game dlouhou dobu nepoužíval většinu evaluačních metrik. Prováděla se pouze základní analýza chyb, která byla počítána v rámci objektu TreeData v metodě saveToFile(). Výsledky této metody byly potom rozděleny do dvou částí, které se dále zpracovávaly odlišně. První část výpočtů, která je rozsáhlejší z těchto dvou, je postupně ukládána do řetězce line, na konci výpočtu je pomocí Obrázek 4: vzorek kódu metody saveToFile() objektu TreeData 38 DataOutputStream zapsána do souboru. Druhá část je zpracovávána do řetězce ret a doplněna o značky, které umožňují následné parsování. Tento řetězec je potom vracen jako výstupní hodnota metody. Tento výstup není převážnou většinou metod programu dále zpracováván. Objekt GMHDtree, přes který je program Fake Game spouštěn a obsahuje velkou část grafického rozhraní, však výstupní řetězec ukládá. Třída ho ale ve výsledku nijak nevyužije, pouze dojde k jeho zobrazení do dialogového okna, kde je díky zalomení textu vidět jen malá část. Pravděpodobně jde o chybu. Jediné místo, kde ke zpracování informací skutečně dojde, jsou objekty TrainTestStrategy a CrossvalidationStrategy. Jak sám název napovídá, jedná se o objekty, které provádí analýzu modelů pomocí systémů Hold-Out a k-fold křížové validace, přičemž uvažují metriku chyby. Konkrétně jde o druhou odmocninu kvadratické chyby a klasifikační chybu. Ta je v tomto případě definována tak, že je rovna nule, pokud je instance klasifikována správně, nebo rovna jedné, pokud je instance klasifikována špatně. Z objektového hlediska je tento způsob evaluace modelů velmi nevhodný. Důvodů je hned několik. Jedna jejich část se týká správnosti implementace ve smyslu GRASP, další z nich přehlednosti kódu a část náročnosti kódu. V následujícím rozboru uvažujeme jenom ty problémy, které se týkají přímo metody související s evaluací, tedy metody saveToFile(). fakegame CrossvalidationStrategy game.data +runGAME() TreeData +saveToFile() TrainTestStrategy další zpracovávání výstupu metody v těchto třídách +runGAME() game.gui game.cli FAKEGAME +FAKEGAME() +generateGAME() Controls +repeatGenerating() +saveFile() GMDHtree +saveMenuActionPerformed() s výstupem manipuluje, ale výsledky se ztratí Obrázek 5: UML diagram tříd, které využívají metodu saveToFile() Prvním problémem je v třídě TreeData, kde dochází k míchání nesouvisejících oblastí kódu. Primární úkol této třídy je pracovat s množinou dat, kterou máme k dispozici, a provádět operace s ní související. V takovém případě ale není úplně správná přítomnost metody, která pracuje s ukládáním a nahráváním souborů, protože ta přímo nesouvisí se správou instancí. To je nicméně spíše kosmetická záležitost, která by pomohla zpřehlednění kódu. Důležitější otázkou spíše je, proč metoda, která má primárně na starost ukládání potomků třídy Model a pracuje prakticky výhradně s jejich daty, je v třídě, která pracuje s datasetem. Ještě horší je, že v přímo v metodě saveToFile() dochází k práci s grafickým rozhraním, konkrétně jde o vytváření swingových objektů. To znamená, že tato metoda může být smysluplně volána pouze z GUI. Její použití kdykoli mimo grafické rozhraní je nespolehlivé a vyžaduje „přiohýbání“ kódu, které přináší zvýšenou výpočetní náročnost a často do kódu zanáší těžko odhalitelné chyby. Zbytečnou režii navíc představuje i spojení výpočtu evaluace a ukládání souboru. Zejména v případě křížové validace je toto markantní, protože vytvářené soubory jsou neustále přepisovány, takže režie se zápisem do souboru přichází vniveč. Vzhledem k tomu, že křížová validace je používána velice často, je toto obzvláště nešťastné. Další chyba je v celkové nejasnosti. Konkrétně se jedná o to, že metoda je sama o sobě velmi dlouhá a její rozdělení na menší úseky by její jádro výrazně zpřehlednilo. Nicméně toto může být záležitost 39 programátorského vkusu a proto se nejedná o chybu jako takovou. Daleko větším problémem je to, že v rámci vkládání dat do řetězce dochází k tomu, že se nevkládají přímo hodnoty proměnných, ale dochází k jejich přepočítávání. Tak je velice obtížné dovtípit se, jaká je skutečná hodnota, která se posílá dál, a především jaký je její význam. Toto se týká jak ukládání do souboru skrze řetězec line, tak i výstupu funkce v řetězci ret. Posledním neduhem je pak fakt, že část chování této metody není určena pouze parametry metody, ale také proměnnými v rámci TreeData. Konkrétně se jedná o proměnnou usePMML, která určuje, zda se mají modely uložit i ve formátu PMML, a saveResponses, která znamená ukládání většího množství parametrů. Tyto proměnné se používají pouze v případě volání saveToFile(), žádný jiný účel nemají. Ač jsou nastaveny na přístup private, lze je neomezeně nastavovat příslušejícími settery, takže změna může být provedena odkudkoli. Takto volající bezdůvodně ztrácí kontrolu nad voláním funkce. Pokud se před voláním zapomene ujistit, že jsou tyto proměnné nastavené tak, jak potřebuje, může se mu chování této metody změnit, aniž by o tom věděl. Vzhledem k tomu, že TreeData jsou implementována jako singleton, je pravděpodobnost, že tento stav nastane, značná. Poslední drobnost je matoucí název tříd CrossvalidationStrategy a TrainTestStrategy. Nejedná se o aplikaci návrhového vzoru Strategy, jak by se z názvu šlo domnívat, ale o objekty typu Template Method. Co se týče možného použití dalších návrhových vzorů, tak by zde existovala možnost řešení problémů s nastavováním vnitřních proměnných tak, že pro ukládání rozšířených dat a PMML souborů by se použil vzor Dekorator, kdy rozšiřovanou funkčnost by představovalo právě ukládání. Dokud je ale ukládání zařazeno v TreeData, jsou tyto úvahy zbytečné, protože tento vzor by vyžadoval přesunutí ukládání do zvláštní třídy. Použití tohoto způsobu evaluace v dalších verzích programu Fake Game není kvůli počtu chyb v žádném případě žádoucí. První pokusy s úpravou této metody a jejím částečným oddělením do nové třídy Evaluation, která by sdružovala veškeré proměnné a metody spojené s evaluací, skončil nezdarem. Zvětšila se tak sice přehlednost v ohledu umístění proměnných a byly opraveny chyby s nastavením přístupů, nicméně se nepodařilo vynutit odpovídající rozhraní, přes které by bylo možné k datům přistupovat. Toto se stalo zejména z toho důvodu, že by toto znamenalo příliš velká změny v okolním kódu, které by byly příliš zdlouhavé. Případné chyby v převodu volání na nové rozhraní by pak mohly vést i ke skrytému omezení funkčnosti. Evaluace modelů pomocí balíčku preprocessing.statistic Tento modul byl do Fake Game přidán teprve nedávno. Odpovídá na narůstající potřebu použití sofistikovanějších evaluačních metod než těch, které poskytuje metoda saveToFile(). Staví na sjednoceném rozhraní, které je definováno v interface StatisticsWorker. To definuje jednoduché metody, které musí implementující objekty splňovat. Metoda run(TreeData treeData) vrací JPanel, který obsahuje grafické rozhraní jednotlivých statistických metod. Metoda getName() pak vrací jméno dané metody. Všechny tyto statistické metody jsou potom zobrazovány v rámci jednotného rozhraní, které je obsaženo v třídě StatisticsGUI. Jedná se o jednoduchý swingovský objekt dědící od JFrame, jehož jedinou funkcí je zobrazování objektů, které implementují již zmíněné rozhraní StatisticksWorker. Tyto objekty jsou načítány za pomoci třídy UnitLoader, která je vytváří na základě textového souboru, kde jsou zapsána jména tříd. Na UML třídním diagramu (obrázek č. 6) můžeme vidět, které třídy v rámci balíčku používají evaluační metody. Nicméně výše uvedené třídy nejsou jediné, které dědí od StatisticsWorker, ale ostatní nepracují s evaluací modelů. Třída FalsePositiveNegative zobrazuje matici záměn pro jednotlivé třídy a počítá pro ně přesnost a citlivost. Zároveň umožňuje zobrazení statistik FPr, FNr, TPr, TNr a přesností klasifikace do pozitivních a negativních. Velice zajímavý rys je slider, kterým lze měnit hraniční hodnotu oddělující klasifikaci do positive a negative. Se změnou této hraniční hodnoty se potom vytvoří nová statistika. Výpočty tohoto modulu probíhají v metodě compute(TreePath[] selectedNames). Ta jako vstup bere označenou cestu stromem, skrz který probíhá výběr (obrázek č.7). Na základě jména, které je v tomto stromě označeno, se pak provádí výpočty. Výsledky jsou potom v podobě textu zobrazeny přímo do GUI. Metoda sama o sobě nic nevrací. 40 StatisticsWorker porovnávání výstupů +run() +getName() StatisticalBlock StatisticsGUI zobrazení ROC křivky ReceiverOperatingStatistics FalsePositiveNegativeDistribution FalsePositiveNegative +display(TreeData) UnitLoader distribuce FP a FN zobrazení matice záměn +statName +newInstance() Obrázek 6: UML diagram tříd, které se v modulu statistics podílí na evaluaci Třída FalsePositiveNegativeDistribution má na starost vykreslování grafů rozložení míry FN a FP v závislosti na změně hraniční hodnoty. Používá velice podobné výpočty jako předchozí třída. Jádro jejích výpočtů opět probíhá v metodě compute(TreePath[] selectedNames) se stejným principem. Obrázek 7: modul statistického balíčku s maticí záměn a statistikami souvisejících metrik Třída ReceiverOperatorCharasteristics funguje opět na stejných principech jako předchozí, jen zobrazuje ROC křivku. Metoda compute(TreePath[] selectedNames) opět obsahuje výpočty. Poslední třída pracující s evaluací modelů je StatisticalDataProcessor (obrázek č. 8). Jeho obsahem je výpočet zaujetí a variance (zde označovaných jako systematic error a random error). Je možné porovnávat model vůči reálným hodnotám, ale také model vůči jinému modelu. V takovém případě samozřejmě musí platit, že oba modely musí pracovat nad stejnými daty. Další funkcí by mělo být vytváření reportů ve formátu pdf, možnost volby různých druhů testů atp. V době vytváření této práce nicméně tyto funkce ještě nebyly zprovozněny. Jádrem výpočtů je metoda compute(), doplňkové metody jsou precompute(), computeAutomatic() a computeEnsemble(). V těchto výpočtech jsou používány třídy balíčku preprocessing.statistic.statisicalblock. Tento statistický balíček je velkým posunem kupředu jak s ohledem na funkčnost, tak s ohledem na čistotu designovou a implementační. Už jenom nastavení rozhraní společného pro součásti, které patří do tohoto balíčku, zajistilo výrazně velkou přehlednost kódu. Zároveň jsou související data udržována v oddělených třídách a obsahují malé množství metod, takže je snadné se v nich zorientovat. 41 Obrázek 8: modul statistického balíčku zobrazující bias a varianci Nicméně i tak má tato část aplikace své stinné stránky. Opět je zde ten problém, že se dohromady míchá grafická reprezentace a data. To by byl opět problém kosmetického rázu, kdyby ovšem neexistovala mezi těmito dvěma částmi silná vazba. Jak je vidět na většině výše uvedených metod, vstupy výpočetních funkcí jsou objekty z frameworku Swing, čímž je zajištěno, že jakékoli volání těchto metod mimo GUI bude velice obtížné. Hlavní problém je ale v tom, že v metodách výpočtu jsou výstupy nedosažitelné, neboť se neukládají do žádných proměnných a pouze se tisknou do grafického rozhraní. To by ještě bylo pochopitelné u těch částí, kde dochází k vykreslování grafů. Provázanost grafiky a dat by tudíž byla přijatelná, ale u ostatních částí je to značný nedostatek. Výše uvedené problémy tak opět přináší nemožnost použití těchto metod v jiném kontextu, jako již v několikrát zmíněném CLI. Toto by se ale mělo změnit i z toho důvodu, že za aktuálního stavu není možné provádět testování bez pomoci speciálních software, které umí číst hodnoty z grafických výstupů. Přitom zrovna v Fake Game je celkem častým jevem to, že změny jádra mají tendenci ovlivňovat externí moduly. Důvod jejich nefunkčnosti je pak těžko odhalitelný, neboť se chyba zpravidla projeví až s odstupem. Potom je zapotřebí zpětně dohledávat, která konkrétní změna tuto chybu vyvolala. Tomu by se přitom snadno dalo zabránit začleněním automatických testů do build manageru Maven, který je v rámci projektu Fake Game používán. Z pohledu návrhových vzorů tento balíček nemá nedostatky, je relativně jednoduchý a nucené zavedení návrhových vzorů by nepřineslo žádná pozitiva, ale pouze zkomplikovalo kód. Samozřejmě by mohla vyvstát otázka, zda by například po oddělení GUI a výpočtových části nestálo za to změnit na návrhový vzor Strategy, protože aktuální použití funkce run() na první pohled vypadá obdobně. Zde si je důležité uvědomit, že tento vzor představuje pouze zapouzdření metody do objektu. Zde ovšem potřebujeme držet informace o komponentách a tak je tento vzor nepoužitelný. Evaluace v programu Weka Evaluace je v programu Weka velice propracovanou částí se kterou se setkáváme pokaždé, když vytváříme klasifikátory, jelikož je součástí standardního statistického výstupu. Základ tvoří třída Evaluation, ve které se udržují všechny důležité proměnné a která provádí i výpočty. Jen minimum kalkulací se provádí mimo vlastní třídu, jedinou výjimkou je v tomto případě výpočet ROC křivky, který probíhá za pomoci třídy 42 TresholdCurve. Při regresních problémech je malá část výpočtů zajišťována třídou KernelEstimator. Všechny hodnoty jsou uloženy v primitivních datových typech. Opět se ale najde výjimka v podobě matice cen uložené v objektu CostMatrix. Weka používá k vytvoření evaluace modelu dvě základní metody, které se liší pouze použitým systémem. První je evaluateModel(). Do té v parametrech vstupují (mimo jiné) instance třídy Classifier a Instances. Tato metoda provede ohodnocení modelu na jedné množině dat, což odpovídá systému Hold-Out. Druhá je pak crossValidateModel(). Ta funguje obdobně, jen je v ní další parametr - počet foldů. Evaluace pak probíhá křížovou validací. (obrázek č. 9) KernelEstimator +getProbability() Evaluation CostMatrix +toSummaryString() +evaluateModel() +crossValidateModel() pouze u regresních problémů Classifier TresholdCurve Instances Obrázek 9: třídy spolupracující na evaluaci v programu Weka Třída Evaluation poskytuje velice širokou nabídku metrik. Přitom využívá toho, že výpočty mnohých z nich jsou podobné, takže celkový počet výpočtů na jejich získání je snížen pomocí sdílení proměnných získaných v mezikrocích výpočtu. Nabízené metriky zahrnují matice záměn a metriky s ní související včetně F1 míry a mnoho druhů chyb, jsou ale nabízené i složitější, jako je kappa statistika nebo information score. Devizou této třídy je také to, že umožňuje jak ovládání přes GUI, tak přes CLI. Protože i v grafickém rozhraní je výstup čistě textový, není v příkazové řádce nijak omezena funkcionalita. Přístup přes CLI navíc předpokládá absolutní izolovanost, takže je možné spouštět evaluační metody bez toho, aby byl zbytek programu aktivní. Stačí jen do parametrů spuštění zadat soubory, ve kterých jsou uloženy modely a data. Po návrhové a implementační stránce není tomuto modulu co vytýkat. K metrikám je dokonce možno přistupovat přímo a ne jen přes výstupní řetězec, takže není problém vybrat jen ty metriky, které jsou pro nás relevantní. Krom toho je také možno snadno dílčí metriky testovat. Jediné, co by možná zlepšilo používání evaluace ve Wece, by byla zjednodušená verze Evaluation, která by obsahovala skutečně jen ty nejzákladnější metriky pro případ, kdy chceme provádět větší množství rychlých evaluací. 43 Kapitola 5 Návrh & Implementace Diskuse možných postupů Při návrhu nového evaluačního modulu máme na výběr celkem ze tří variant: První je varianta napsání vlastního kódu zcela odděleného od veškerých existujících modulů. Toto se nevyplatí zejména z toho důvodu, že by to vyžadovalo větší zásah do prostředí Fake Game a celá implementace by byla výrazně časově náročnější a s větším sklonem k chybám. Dalším nežádoucím aspektem je to, že by tím došlo k rozdrobení stejné funkcionality do více míst a tím pádem by aplikace nebyla tak uživatelsky přátelská. Druhou možností je zakomponovat nově vytvořený kód do Fake Game za použití grafického rozhraní již existujícího modulu z balíčku statistic. Tento postup má ovšem také značnou nevýhodu. Ta spočívá v tom, že metody používané k evaluaci jsou většinou velmi složité, co se týká jejich matematické stránky. Tím pádem by bylo velice obtížné napsat odpovídající testy tak, aby pokrývaly skutečně všechny alternativy chyb, které by mohly nastat. Poslední varianta je převzetí evaluačního modulu z programu Weka a zakomponování ho do balíčku statistics. Tato varianta je nejlepší, protože se při ní vyhneme problémům popsaným výše. Proto bude tato varianta zvolena. Propojení Weka a Fake Game Diskuse Důležitou otázkou je, jakým způsobem bude provedeno napojení evaluačního balíčku nástroje Weka na prostředí Fake Game. Samozřejmě musíme co nejvíce respektovat styl návrhu zvolený vývojáři Weky (obrázek č. 9). Opět existuje několik variant, které bych rád diskutoval. První varianta je, že použijeme existující třídu Evaluation, vytvoříme nového potomka GameEvaluation a všechny její metody přepíšeme tak, že budou moci přijímat objekty z prostředí Game TreeData a Model. V rámci každé metody bude speciální volání metody, která bude mít za úkol provedení konverze dat z formátu Fake Game do formátu Weka. Nad těmito daty se následně provedou výpočty. Tento postup není vhodný kvůli tomu, že převod mezi objekty používanými oběma programy není úplně triviální. Weka používá mnohem více příznaků, na základě kterých se určuje, které evaluační metody je možno použít na kterou část dat. To by mohlo vyústit v to, že po zavolání konkrétní metody se provede převod, zavolá se metoda mateřské třídy, ale dojde k neprovedení výpočtu. Tím pádem by došlo k zcela zbytečnému převodu. Zároveň by ale bylo velice obtížné stanovovat tyto příznaky apriori. Znamenalo by to ke každé metodě dopsat kontrolní kód, nikde by navíc nebyla záruka, že výpočetní náročnost tohoto kódu nebude srovnatelná s náročností plného převodu. Druhou variantou je provést částečnou konverzi objektů a ty pak používat. Realizace by byla taková, že bychom vytvořili potomky tříd Instances, Classifier a Evaluation a zablokovali část jejich funkcí, které nezbytně nepotřebujeme. To by se provedlo přepsáním metod rodičovských tříd novými, které by pouze vyhazovaly UnsupportedOperationException. Tak bychom získali pouze funkčnost, kterou potřebujeme pro evaluační balíček a zbytek bychom prostě nepoužívali. Tím bychom ušetřili čas na programování oproti variantě kompletního přepsání se stejným ziskem. Bohužel, tato idea není reálně proveditelná. Třída Evaluation totiž používá některé funkce klasifikátorů, které na první pohled s evaluací nesouvisí. Je to například opětovné trénování modelů v případě použití křížové validace. Z tohoto důvodu není možné určit jasnou hranici, která by oddělovala metody, které ještě jsou využity v evaluaci a které ne. Odlaďování takového kódu by navíc bylo velmi problematické právě z důvodu značného propojení evaluačních metod 44 s metodami klasifikátoru. Třetí a poslední je varianta plné konverze objektů mezi oběma programy. To s sebou přináší tu výhodu, že jednotlivé fáze konverze jsme schopni snadno odspodu (attribute → instance → instances) testovat a tím pádem bude jednoduché snáze identifikovat chyby v implementaci. Dalším znatelným plusem je i fakt, že díky plné konverzi budeme dále moci využívat i další balíčky programu Weka, které pracují s klasifikátory, a jejich výstupy přenášet do prostředí Game. Řešení Řešením, které se v tomto případě jeví jako nejlepší, je provést konverzi ve dvou krocích. V první části se provede transformace TreeData na Instances společně s objekty, které obě třídy používají. Tím získáme základ, oproti kterému budeme moci ověřovat účinnost modelů a provádět evaluaci. V druhé fázi pak vytvoříme klasifikátor prostředí Weka, který bude postaven na modelech prostředí Fake Game. Takto získané reprezentace dat a modelů potom předložíme třídě Evaluation ke zpracování. V případě, že se ale rozhodneme použít křížovou validaci, je nutné počítat s tím, že se musí provést také konverze zpět z Instances na TreeData, aby bylo možné provádět správné trénování v rámci jednotlivých foldů. Protože jsou výsledky evaluace vraceny v primitivních datových typech, případně v instancích třídy String, nebude nutné dál tento výstup upravovat. Tento výstup se pak zobrazí v grafickém rozhraní Fake Game. TreeData WekaToGAMETreeDataFactory GAMEToWekaInstancesFactory +createTreeData(Instances) Instances +createWekaInstances(TreeData) GAMEToWekaClassifier +numberOfInputs +modelsUsed +models GAMEToWekaAttributeFactory Attribute +buildClassifier(Instances) +classifyInstance(Instance) +createWekaInputAtt(TreeData) +createWekaRegressionAtt(TreeData) +createWekaClassAtt(TreeData) Model Instance GAMEToWekaClassifierFactory GAMEToWekaInstanceFactory +createWekaInstance(TreeData, int, Instances) +createGAMEToWekaClassifier(TreeData, Models, int[]) Obrázek 10: UML diagram znázorňující balíček adaptérů Na tomto obrázku (obrázek č. 10) vidíme, jak vypadá balíček, který zajišťuje výše rozebranou konverzi. Základem je třída GAMEToWekaClassifier, která dědí od třídy Classifier z programu Weka. Jedná se o aplikaci návrhového vzoru Adapter. Obsahuje odkazy na všechny modely Fake Game, které byly vytvořeny. Zde je důležité si uvědomit, že může být vytvořeno více modelů na jeden výstup, mezi kterými má být možnost přepínání. Od toho je tady proměnná modelsUsed, kde jsou v poli uložena čísla modelů. Pořadí v poli odpovídá pořadí výstupů. Proměnná numberOfInputs potom určuje, s kolika vstupy klasifikátor počítá. Je zde z toho důvodu, aby bylo možné hned zjistit, pokud by se uživatel pokusil klasifikovat data, která nejsou s klasifikátorem kompatibilní. Hlavní metody této třídy jsou následující: classifyInstance(Instance) a buildClassifier(Instances). První z nich provádí klasifikaci vložené instance do příslušné třídy, případně regresi. Výstupem je číslo třídy, která byla klasifikací vybrána jako správná. Konstanta CLASS_MISSING je vrácena, pokud se klaifikátor není schopen rozhodnout, kam danou instanci zařadit. Druhá metoda je volána kdykoli, kdy má proběhnout trénování modelu. V rámci jejího běhu se vložený objekt Instances zkonvertuje na objekt TreeData, přes který potom proběhne trénování nativními metodami Fake Game. Zde je nutné počítat s tím, že se musí 45 zasáhnout do instance objektu Models. Prostředí totiž neumožňuje provést přímo přetrénování vybraného modelu. Pokud se natrénuje nový model, je automaticky zařazen na konec vektoru v Models. Běžnou evaluaci systémem Hold-out toto nijak neovlivní, ale v případě křížové validace bychom se dostali do problémů. Proto je nutné zajistit, aby se tyto nové modely neukládaly, ale nahrazovaly své předchůdce. Třída GAMEToWekaClassifier je vytvářena pomocí továrny GAMEToWekaClassifierFactory. Ta byla vytvořena kvůli potřebě oddělit metody vykonávající operace nad samotnými a metody, které zajišťují vytváření objektu. Protože je vhodné používat ji ze statického kontextu, byla aplikována jako návrhový vzor Singleton. Jejím vstupem jsou TreeData, Models a pole int[]. První je používán čistě pro referenci, aby bylo jasné, nad jakými daty byly dané modely vytvořeny. Na základě tohoto vstupu se inicializuje proměnná numberOfInputs. Models obsahuje odkaz na všechny modely, které byly v prostředí Fake Game doposud vytvořené. Zde je důležité zajistit, aby byla metoda volána až v momentě, kdy nějaké metody existují, jinak dojde k vyhození výjimky. Toto ale lze podchytit na vyšší úrovni, proto se tímto nezabýváme. Poslední vstupem pole specifikující modely, které budou použity. V případě, že indexy neodpovídají žádným modelům, je vyhozena výjimka. Stejně tak je výjimka vyhozena v případě, že modely a data si neodpovídají ať už počtem vstupů nebo výstupů. Pokud jsou data prázdná, dojde také k vyvolání výjimky. Před vytvořením samotného klasifikátoru se nejdříve zavolá metoda továrny GAMEToWekaInstancesFactory, která se postará o vytvoření objektu Instances. Podobně dále deleguje jednotlivé úkoly na odpovídající továrny pro vytváření jednotlivých instancí a atributů. Atributy mají potom rozdílný postup vytváření v případě, že se jedná o vstupní, výstupní klasifikační nebo výstupní regresní atributy. Je to tak z toho důvodu, že každý z těchto zmíněných atributů má v prostředí Weka jiné příznaky, které ale nejsou ve Fake Game explicitně uvedeny, a tak musí dojít k jejich vytvoření na základě vlastností objektu TreeData. Protože se jedná o operace, které jdou snadno zaměnit a jejich záměna by byla obtížně odhalitelná, byla jejich tvorba oddělena do tří metod. GAMEToWekaClassifierFactory ClassifierCreationException GAMEToWekaClassifier ModelSpecificationException IncompatibilityException GAMEToWekaInstancesFactory IncompleteDataException Obrázek 11: UML diagram výjimek a jejich původců Aby byl zjednodušen proces ladění a testování programu, byla vytvořena sada výjimek (obrázek č. 11). Jejich jména odrážejí jejich funkci. ClassifierCreationException je vyhozena v případě, že dojde k chybě při vytváření při vytváření klasifikátoru, která ale nesouvisí s chybami dat. ModelSpecificationException je vyhozena v případě, že modely specifikované indexy v poli modelsUsed neodpovídají modelům v Models. IncompatibilityException je vyhozena, pokud při tvorbě klasifikátoru nebo při pokusu o klasifikaci dat dojde k nekompatibilitě v počtu výstupů a vybraných modelů. IncompleteDataException je pak vyhozena v případě, že data obsažená v TreeData jsou nekompletní. Za nekompletní data jsou považována taková, která neobsahují žádné instance či mají nulový počet vstupních nebo výstupních atributů. 46 Úprava balíčku preprocessing.statistic Diskuse Přestože je tento balíček navržen docela dobře, i tak má jisté neduhy. Ty byly popsány v části analytické. Otázkou je, zda je by bylo skutečným přínosem, zkoušet je nějak odstranit, nebo zda by se jednalo jen o zbytečný programátorský purismus. Je celkem zřejmé, že v tomto případě se úpravy vyplatí, protože sloučení výpočtů s grafickým rozhraním není do budoucna rozhodně dobrá volba. Jedná se o velice silnou vazbu, která by do budoucna komplikovala ladění, navíc se oddělením výrazně zvýší přehlednost. V následujících odstavcích budu uvažovat pouze změny, které nezahrnují modul pracující kolem třídy StatisticalBlock. To je z toho důvodu, že v době vytváření této práce nebyl ještě kompletní a případné změny rozhraní by se snadno mohli dostat do konfliktu s jeho novějšími verzemi. Pro potřeby této práce nám postačí, že sdílí společné grafické rozhraní. Navíc jeho návrh byl proveden kvalitně a v jeho dosavadní verzi je dobře oddělena část prezentační a výpočetní. První uvažovanou variantou bylo sloučení všech evaluačních výpočtů pod jedno rozhraní, přes které by se pak všechny metody shodně ovládaly. Tento postup ale není vhodný už z důvodů povahy těchto metod. Jejich vnitřní fungování, vstupní parametry a typy výstupů jsou natolik odlišné, že se použití rozhraní nejeví jako vhodné. Ani velice jednoduché rozhraní, které by definovalo metodu getResults() zde nelze použít. Část výstupů je sice snadno převeditelná do formy textu a tak by se zdánlivě nabízelo použít jako návratový typ objekt String, ale tato strategie by záhy narazila u metod, jejichž výstup je graf. Je sice pravda, že i ten by šel převést do číselných hodnot a následně do textu, ale takový výstup by měl pro uživatele prakticky nulovou informační hodnotu. Momentální stav, kdy je rozhraní použito na definování funkcí GUI, je tedy dostatečný. Druhou variantou tedy bylo použití návrhového vzoru Facade, který by sjednotil evaluační metody do jedné třídy. To by zvýšilo přehlednost, ale zároveň by si to nevynutilo standardizaci výstupů evaluačních funkcí. Zároveň implementace podle vzoru Singleton zajistí, že evaluační metody budou přístupné i mimo grafické rozhraní, ve kterém jsou nyní implementovány. Součástí této úpravy pochopitelně musí být izolování výpočtů, které jsou nyní prováděny, a jejich přesunutí do externí třídy. Tyto třídy by měly být sjednoceny do jednoho balíčku a jejich konstruktory skryty tak, aby k nim měl přístup pouze objekt fasády. Voláním jeho metod se pak budou tyto objekty vytvářet a zároveň bude docházet k provádění výpočtů. Výsledky těchto výpočtů budou uloženy v těchto objektech. Tyto metody potom budou vracet celé tyto objekty. Řešení Základním krokem bude vytvoření balíčku core v rámci balíčku statistics, kam se budou postupně přemísťovat evaluační metody. Každá třída v tomto balíčku bude obsahovat metody nutné k provedení výpočtů evaluace. Zároveň musí obsahovat proměnné, které budou obsahovat výsledky. Jak u evaluací textových, tak u evaluací grafických, by se mělo jednat o primitivní datové typy. Evaluace, kde lze výstup převést na text, budou pouze potomky třídy Object. Grafické evaluace, jejichž výstupem jsou křivky, které se potom zakreslí do grafů, budou dědit od třídy XYSeries, která pochází z otevřeného balíčku org.jfree.data.xy a která představuje právě jednu křivku v grafu. (obrázek č.12) Fasáda sdružuje metody, které vytvářejí objekty metrik a naplňují je daty na základě vstupů. Aby se zabránilo přímému volání konstruktorů objektů, jsou nastaveny na přístup protected. Každý z objektů byl vytvořen na základě výpočtů, které probíhaly v rámci tříd, které jsou reprezentací GUI. Původně pevně integrovaný kód byl přesunut. Nyní objekty GUI volají metody fasády, na základě těchto žádostí se vrací odpovídající objekt. V případě ConfusionMatrix, ConfusionMetrics a WekaEvaluationAdapter je výstup vracen v textové či číselné formě, kterou lze přímo vypisovat. Třídy MetricsGraphData a ROCGraphData obsahují pouze množinu kartézských souřadnic. Tyto třídy se vkládají do XYSeriesCollection. Jeden tento objekt umí udržet neomezené množství tříd. On sám je pak vložen do objektu JFreeChart, který již tvoří přímo graf, kde se každý objekt XYSeries zobrazí jako samostatný průběh funkce. Které třídy pracují s jakými objekty je vidět na obrázku. (obrázek č.13) 47 ConfusionMatrix WekaEvaluationAdapter ConfusionMetrics MetricsGraphData ROCGraphData XYSeries EvaluationFacade +confusionMatrix(TreeData) +confusionMetricSingleModel(TreeData, int) +confusionMetricSingleModel(TreeData, int, double) +confusionMetricsMultiModel(TreeData, int) +confusionMetricsMultiModel(TreeData, int, double) +falseNegativeGraphData(TreeData, int, String) +falsePositiveGraphData(TreeData, int, String) +ROCGraphData(TreeData, int, String) +wekaEvaluation(TreeData, Models, int[]) Obrázek 12: UML diagram statistic.core WekaEvaluation WekaEvaluationAdapter ConfusionMatrix FalsePositiveNegative ConfusionMetrics FalsePositiveNegativeDistribution MetricsGraphData ReceiverOperatorCharacteristics ROCGraphData Obrázek 13: UML diagram vztahů GUI a datových tříd balíčku statistic Objekt WekaEvalutionAdapter má funkci odlišnou od ostatních částí balíčku core. Zatímco v ostatních objektech přímo dochází k výpočtu, tento je aplikací návrhového vzoru Adapter a všechny volání přesměrovává dál na třídu Evaluation, která je součástí kódu programu Weka. Před voláním samotných metod v něm dochází k transformaci veškerých informací a objektů nutných k výpočtu do jejich ekvivalentů z Weka. K tomu jsou používány dříve zmíněné továrny a adaptéry. 48 Grafické rozhraní používající evaluaci z Weka Diskuse Grafické rozhraní by mělo umožňovat využití všech funkcí, které má třída Evaluation se zachováním co možná největší jednoduchosti. Protože většina procesů je již plně automatizována v rámci adaptérů, bude potřeba implementovat jen relativně omezené množství funkcí. Bude nutné, aby bylo možné zvolit ty modely, které mají být hodnoceny. Zároveň nesmí dojít k tomu, že by uživatel zvolil dva modely pro stejný výstup. Dále musí být umožněno přepínání mezi systémem hold-out, leave-one-out a křížovou validací. U té je navíc nutné, aby si uživatel mohl zvolit počet foldů, které budou použity. Zde musí proběhnout kontrola, aby bylo číslo větší než pět a zároveň menší nebo rovno třetině počtu instancí. To vychází z předpokladu, že vyšší číslo by byla spíše chyba na vstupu , protože pokud mu jde o větší přesnost, použil by systém leave-one-out. Systém bootstrap se v tomto případě vůbec neuvažuje, protože se hodí k hodnocení modelů, které jsou ze své podstaty nepřesné, takže evaluace by nebyla v přesnějších metrikách směrodatná. Řešení Obrázek 14: grafické rozhraní modulu Weka Evaluation Takto vypadá grafické rozhraní modulu Weka Evaluation. (obrázek č. 14) Zobrazení výstupních atributů a 49 jejich modelů je provedeno pomocí třídy JTree, jako výstupní oblast slouží JTextArea, k vybírání jednotlivých systémů JRadioButton, v případě křížové validace se počet foldů určuje pomocí JFormattedTextField. Pokaždé, když je ze stromu zvolen model, nahradí svého předchůdce od odpovídajícího atributu. Pak se provede evaluace. Stejně tak se evaluace provede, když dojde ke změně systému nebo počtu foldů v případě křížové validace. V případě, že je počet foldů změněn na pět a méně, je nastaven na 6 a křížová validace se provede s tímto počtem. Stejně tak pokud je nastavena na více jak třetinu počtu instancí, je snížena na toto číslo a provedena evaluace. Z tohoto důvodu je křížová validace zakázána pro množiny dat s počtem instancí menším než 20. 50 Kapitola 5 Testování Testování tohoto programu se dá rozdělit celkem do tří celků. První je manuální testování, které probíhalo zejména v průběhu vývoje a bylo doplňkem ladění. Zaměřovalo se zejména na testování GUI. Další dvě testování probíhaly pod frameworkem JUnit. První bylo testování adaptérů, kdy se pracovalo se speciálně vytvořenými datovými soubory, ale krom toho byly používány běžné objekty. Cílem bylo podchytit možné chyby v kódu, který konvertoval data z Fake Game do Weka. Druhé z nich bylo opět se speciálně vytvořenými soubory, ale byly použity mock objekty. Předmětem testování nebylo pouze ověření, že správné vstupy budou zpracovány dobře. Část testů se zaměřila na to, zda jsou výjimky vyhazovány, kdy chceme, a zda program zahazuje špatné vstupy od uživatele. Manuální testování První fáze se zaměřovala na odlaďování kódu v tom smyslu, že se ještě před napsáním odpovídajícího unit testu otestovala základní funkčnost pomocí triviálních vstupů. Pokud tato prošla, pak následovalo psaní unit testů. Toto jsem používal zejména z toho důvodu, že po odhalená chyba někdy vyžadovala větší zásah do kódu a proto by bylo nutné přepisovat i větší část testovacích metod. Testování GUI probíhalo velice jednoduše a omezovalo se na to, zda vyvolávané akce správně plní svoji funkci. Snahou bylo vytvářet co nejnestandardnější vstupy, aby byla ověřena stabilita systému. Unit testy s běžnými objekty Testování probíhalo pomocí frameworku JUnit, konkrétně jeho verze 4.6. Pro potřeby této práce byla Obrázek 15: vzorek kódu testů 51 předpokládána správná funkčnost těch částí programu, které nebyly přímo modifikovány a specifické testy pro ně nebyly psány. Ostatní testy na nich samozřejmě závisely, takže případná chyba uvnitř Fake Game by se projevila tím, že většina, ne-li všechny z nich, by neprošly. Ovšem vzhledem k tomu, že testy byly psány tak, aby odhalily i chyby, které by při správné funkci jádra nemohly nastat, bylo by celkem snadné vytipovat. Tyto testy se omezovaly na testování správného převodu dat z Fake Game do Weka a zpět. (obrázek č. 15) K testování je použit zvlášť vytvořený soubor, který obsahuje předem známá data, oproti kterým je pak testována správná funkce. Unit testy s mock objektem Poslední část testů, stejně jako předchozí, proběhla za pomoci frameworku JUnit. Aby bylo možno přesně otestovat správnou funkci evaluačního balíčku, vytvořil se mock objekt DummyModel, který jednoduše simuluje chování běžného modelu. V proměnné drží, na jaký vstup je přiřazen, a podle toho vrací výstup na předložené vstupní atributy. Zde byl nastaven tak, že reagoval pouze na vstupy se shodnými atributy. Konkrétně pole s hodnotami 0.1 zařadí do první třídy, hodnoty 0.2 do druhé třídy atd. Všechny ostatní vstupy označí jako nepříslušící nikam. Zároveň byl vytvořen soubor, u kterého je předem znám počet správně a špatně klasifikovaných. Testy se pak prováděly tak, že hodnota dané metriky byla vypočítána stranou a pak v testu porovnána s výsledkem vráceným třídou Evaluation. 52 Kapitola 6 Závěr Cílem této práce bylo blíže se seznámit s návrhovými vzory a evaluačními metodami a tyto znalosti potom aplikovat v softwaru Fake Game implementací nových funkcí a upravením již existujících tak, aby umožňovali snažší rozšiřitelnost a byly přehledné. Prvním krokem bylo seznámení se s problematikou návrhových vzorů GoF a GRASP a popis jejich uplatnění společně s jejich hlavními výhodami a případně nevýhodami. Dále pak proběhlo prozkoumání nejpoužívanějších evaluačních metod data miningu. Tyto znalosti mi následně umožnili lépe posoudit, které metriky a metody bude nutné bezpodmínečně zakomponovat do Fake Game a které jsou spíše doplňkového charakteru. Následovala základní analýza funkce programů Fake Game a Weka a jejich popis. Hlubší rozbor pak byl proveden u všech částí, které se zabývají evaluací. Zde jsem krom popisu uvedl základní nedostatky z pohledu objektově orientovaného programování. Zároveň jsem diskutoval možné úpravy, které by vedly k lepší přehlednosti a rozšiřitelnosti. Toto jsem provedl jak u programu Fake Game, tak u programu Weka. Z analýzy vyplynulo, že nejlepším krokem bude použití evaluačního modulu z programu Weka. Navrhl jsem rozhraní, které umožňuje plnou konverzi datasetu a modelů z Weka do Fake Game a zpět. Zároveň jsem provedl takové úpravy, které umožňují práci s více modely najednou a jejich rychlé zaměňování, které doposud program Weka neumožňoval. Upravený modul jsem se rozhodl přidat do statistického balíčku Fake Game, kde jsem zároveň provedl úpravu ostatních evaluačních tříd a sjednotil jejich funkce pod jednu fasádu. Nakonec jsem vytvořil jednoduché grafické rozhraní, které umožňuje plně využít možnosti všech původních evaluačních funkcí a mnou doplněné vlastnosti. Finální částí bylo provedení testů funkčnosti jak manuálně, tak skrze framework JUnit verze 4.6. Tyto testy probíhaly jak za použití reálných objektů aplikace, tak s pomoc mock objektů, jejichž použití umožňovalo ověření funkcionality metod, kde by v reálné aplikaci figuroval příliš velký podíl náhody. Tato práce je přínosem pro projekt Fake Game, který doposud postrádal širší spektrum evaluačních metrik. Tento nový modul navíc dává možnost snadného srovnání různých kombinací modelů. Návrh byl směřován tak, aby poskytoval prostor pro případné rozšíření, eventuálně použití v jiné části programu. Důležité je i to, že adaptéry pro konverzi mezi Fake Game a Weka jsou použitelné univerzálně, takže se v budoucnu mohou použít i v jiných částech software. 53 54 Literatura [1] C. Larman. Applying UML and Patterns - An Introduction to Object-Oriented Analysis and Design and Iterative Development. 2005 [2] E.Gamma, R. Helm, R. Johnson, J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. 1995 [3] O programu Weka http://www.cs.waikato.ac.nz/~ml/index.html , stav z 23.5.2009 [4] Úvod do API programu Weka. http://weka.wiki.sourceforge.net/Primer , stav z 23.5.2009 [5] Úvod do programu FAKE GAME http://neuron.felk.cvut.cz/game/project.html [6] T. Bruckhaus. Prediction and Recall Calculation http://www.kdnuggets.com/faq/precision-recall.html , stav z 21.5.2009 [7] R. Kahavi. A Study of Cross-Validation and Bootstrap for Accuracy Estimation and Model Selection. 1995 http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=46FDB9640A8834EABECD5235 8244A180?doi=10.1.1.48.529&rep=rep1&type=pdf , stav z 26.5.2009 [8] J. Souza, S. Marvin, N. Japkowicz. Evaluating Data Mining Models: A Pattern Language. 2002 http://hillside.net/plop/plop2002/final/PLoP2002_jtsouza0_1.pdf , stav z 24.5.2009 [9] P. Pošík. Data Mining, část I. Co to je data mining http://cyber.felk.cvut.cz/gerstner/teaching/zbd/DataMining1-hout.pdf , stav z 23.5.2009 [10] ROC Curve Analysis, Introduction http://www.medcalc.be/manual/roc.php , stav z 25.5.2009 [11] The Area Under the ROC Curve http://gim.unmc.edu/dxtests/roc3.htm , stav z 28.5.2009 [12] A. Berson, S. Smith, K. Thearling. An Overview of Data Mining Techniques http://www.thearling.com/text/dmtechniques/dmtechniques.htm , stav z 19.5.2009 [13] B. Paye. The Resurgence of Data Mining http://www.secondmoment.org/articles/re-datamining.php , stav z 23.5.2009 [14] A Brief History of Data Mining http://www.data-mining-software.com/data_mining_history.htm , stav z 1.6.2009 [15] R.Guiterrez. Validation http://research.cs.tamu.edu/prism/lectures/iss/iss_l13.pdf , stav z 2.6.2009 55 56 Příloha A Seznam použitých zkratek FAKE GAME Fully Automated Knowledge Extraction Group of Adaptive Models Evolution GRASP General Responsibility Assignment Software Patterns GoF Gang of Four GUI Graphical User Interface CLI Command-line Interface Weka Waikato Environment for Knowledge Analysis TPr True Positive Rate TNr True Negative Rate FPr False Positive Rate FNr False Negative Rate Pre Precision ROC Receiver Operating Characteristic / Relative Operating Characteristic AUC Area Under Curve API Application Programming Interface PMML Predictive Model Markup Language UML Unified Modeling Language PDF Portable Document Format 57 58 Příloha B Uživatelská příručka 1. Pro spuštění použijte zdrojové kódy ve formě projektu prostředí IntelliJ Idea, main metoda se nachází v třídě game.gui.GMHDtree 2. Nahrajte soubor iris.txt, který je součástí složky \data pomocí: File → Load 3. Vytvořte modely pomocí: Model → Create single model 4. Spusťte statistický modul: Model → Statistical evaluation of models 5. V horním roletovém menu zvolte: Weka Evaluation 6. Proběhne první evaluace modelu, automaticky jsou zvoleny první modely od každého atributu 7. Pro změnu modelů použijte strom vlevo; rozklikněte uzel atributu, jehož model chcete změnit, objeví se nabídka modelů, kliknutí provede náhradu modelu u daného atributu a provede se evaluace tohoto modelu v kombinaci se zbylými dvěma 8. Pro změnu systému evaluace klikněte na některé z radiových tlačítek v dolní části, zde můžete měnit i počet foldů v případě křížové validace 59 60 Příloha C Obsah CD 61
Podobné dokumenty
Singularita
Poznamenávám, že nenavrhuji, abychom ignorovali výzkum AI nebo na něj věnovali méně
prostředků. To, co se povede v AI bude asi použitelné i v IA a naopak. Navrhuji pouze
uvědomit si, že ve výzkumu ...
DATA MINING - MOŽNOSTI A ZPŮSOBY JEHO VYUŽITÍ
Odhalení závislostí a rozdílů
Data mining nenahrazuje, ale doplňuje dosud užívané postupy vyhodnocování hromadných dat.
Dolování dat je mnohem komplexnější proces, než "prosté" výše uvedené metod...