Warning
This page is located in archive. Go to the latest version of this course pages.

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

courses:b4b33rph:cviceni:program_po_tydnech:tyden_09 [2018/12/04 16:25]
courses:b4b33rph:cviceni:program_po_tydnech:tyden_09 [2018/12/04 16:25] (current)
Line 1: Line 1:
 +====== Spam II ======
 +
 +  * Dotazy a odpovědi
 +  * Testík
 +  * Diskuse DÚ: confusion matrix, funkce kvality
 +  * Interaktivní programování:​ ''​BinaryConfusionMatrix''​ metodou TDD
 +  * Diskuse: jak dokončit krok 3 spam filtru? Co se bude odevzdávat?​
 +  * Hádanka
 +  * Diskuse DÚ: jednoduché filtry
 +===== Dotazy a odpovědi =====
 +
 +===== Programovací testík =====
 +Zadání na interních stránkách.
 +
 +
 +===== Diskuse =====
 +  * Jak hodnotit kvalitu filtru? Co jsou to TP, FP, TN, FN? 
 +
 +> {{page>​..:​..:​internal:​cviceni:​spam:​krok3#​matice_zamen&​editbtn&​noheader}}
 +
 +  * Definice funkce kvality
 +
 +> {{page>​..:​..:​internal:​cviceni:​spam:​krok3#​funkce_kvality&​editbtn&​noheader}}
 +
 +
 +===== TDD: BinaryConfusionMatrix =====
 +Příprava:
 +  * Projděte si specifikace třídy ''​BinaryConfusionMatrix''​.
 +  * Jaké testy (scénáře použití) dokážete pro danou třídu vymyslet?  
 +
 +> {{page>​..:​..:​internal:​cviceni:​spam:​krok3#​info&​editbtn&​noheader}}
 +
 +
 +==== Krok 0 ====
 +> {{page>​..:​..:​internal:​cviceni:​spam:​krok3#​teachers_0&​editbtn&​noheader}}
 +
 +++++ 0. Vytvoření kostry |
 +TDD nás nabádá, abychom vždy psali testy před produkčním kódem. Budeme používat modul ''​unittest''​ a testy pro třídu ''​BinaryConfusionMatrix''​ (dále jen BCF) budeme psát jako metody třídy ''​BinaryConfusionMatrixTest''​. Založme tedy soubor ''​test_confmat.py''​ a vložme do něj následující kostru:
 +
 +<code python>
 +import unittest
 +
 +class BinaryConfusionMatrixTest(unittest.TestCase):​
 +    pass
 +    ​
 +if __name__=='​__main__':​
 +    unittest.main()
 +</​code>​
 +
 +Poslední část (''​if <​nowiki>​__name__</​nowiki>​...''​) je nutná tehdy, když chcete testy spouštět v shellu nebo v debuggeru. Pokud využijete nástroj Testing z PyCharm, tato část nutná není.
 +
 +Spusťte test. Měli byste vidět, že proběhlo 0 testů.
 +
 +Doplňme teď do testu import třídy BCF z modulu ''​confmat'',​ kterou chceme testovat:
 +
 +<code python>
 +from confmat import BinaryConfusionMatrix
 +</​code>​
 +
 +Když modul s testem spustíme (viz předchozí cvičení), dostaneme ''​ImportError''​. Jak by ne, modul ''​confmat''​ neexistuje. Založme tedy soubor ''​confmat.py''​. ​
 +Po opětovném spuštění modulu s testem opět dostaneme ''​ImportError'',​ protože se snažíme importovat třídu BCM, která v modulu zatím neexistuje. Založme tedy v modulu ''​confmat''​ prázdnou třídu ''​BinaryConfusionMatrix''​. ​
 +
 +<code python>
 +class BinaryConfusionMatrix:​
 +    pass
 +</​code>​
 +
 +Test nyní nekončí chybou, bezva, máme připravenou kostru pro naše testy. V modulu ''​test_confmat''​ ovšem zatím žádné skutečné testy nemáme. Zkusme je tedy postupně vytvořit.
 +++++
 +
 +==== Krok 1 ====
 +++++ 1. Po vytvoření třídy jsou čítače vynulovány |
 +Doplňte do testové třídy následující test. Aby třída byla obecná, musíme jí při vytváření předat kódy, jimiž budeme označovat spam a korektní emaily. Následně se pokusíme získat matici ve formě slovníku a tento slovník porovnáme s očekávanými hodnotami, které by měly být hned po vytvoření objektu samozřejmě nulové.
 +<code python> ​       ​
 +    def test_countersAreZero_afterCreation(self):​
 +        # Prepare fixture
 +        cm = BinaryConfusionMatrix(pos_tag=SPAM_TAG,​ neg_tag=HAM_TAG)
 +        # Exercise the SUT
 +        cmdict = cm.as_dict()
 +        # Assert
 +        self.assertDictEqual(cmdict,​ {'​tp':​ 0, '​tn':​ 0, '​fp':​ 0, '​fn':​ 0})
 +</​code>​
 +
 +Spusťte test. Test skončí s chybou ''​NameError'',​ protože nemáme definovány konstanty ''​SPAM_TAG''​ a ''​HAM_TAG''​. Jelikož by tyto konstanty měly mít platnost co nejmenší, aby neovlivňovaly žádné další testy, definujme tyto konstanty přímo v modulu ''​test_confmat''​. Jejich hodnoty si pro účely testování můžete zvolit prakticky libovolně.
 +
 +<code python>
 +SPAM_TAG = '​SPAM'​
 +HAM_TAG = '​OK'​
 +</​code>​
 +
 +Spusťte test. Dostanete výjimku ''​TypeError'',​ protože třída BCM nemá definovanou metodu ''<​nowiki>​__init__</​nowiki>''​ se dvěma argumenty. Doplňme ji tedy (do třídy BCM). Zapamatujme si kódy pro spam a ham a inicializujme čítače TP, TN, FP, FN.
 +
 +<code python>
 +    def __init__(self,​ pos_tag, neg_tag):
 +        self.tp = 0
 +        self.tn = 0
 +        self.fp = 0
 +        self.fn = 0
 +        self.pos_tag = pos_tag
 +        self.neg_tag = neg_tag
 +</​code>​
 +
 +Spusťte test. Třída teď jde instanciovat. Dostáváme ale ''​AttributeError'',​ protože třída BCM nemá metodu ''​as_dict()''​. Doplňme ji do třídy BCM: 
 +<code python>
 +    def as_dict(self):​
 +        """​Return the conf. mat. as a dictionary."""​
 +        return {'​tp':​ self.tp, '​tn':​ self.tn, '​fp':​ self.fp, '​fn':​ self.fn}
 +</​code>​
 +
 +Spusťte test. Měl by proběhnout bez chyby. V čem je hodnota tohoto testu? Zaručí nám, že pokud se v budoucnu budeme '​hrabat'​ v metodě ''<​nowiki>​__init__</​nowiki>​()''​ nebo ''​as_dict()''​ a zaneseme tam nechtěně nějakou chybu, pak ji test s velkou pravděpodobností odhalí.
 +++++
 +
 +
 +
 +==== Krok 2 ====
 +> {{page>​..:​..:​internal:​cviceni:​spam:​krok3#​teachers_2&​editbtn&​noheader}}
 +
 +++++ 2. Metoda update() správně upraví čítač TP |
 +Vytvořme test:
 +<code python>
 +    def test_updatesTPcorrectly(self):​
 +        # Prepare fixture
 +        cm = BinaryConfusionMatrix(pos_tag=SPAM_TAG,​ neg_tag=HAM_TAG)
 +        # Exercise the SUT
 +        cm.update(SPAM_TAG,​ SPAM_TAG)
 +        # Assert
 +        self.assertDictEqual(cm.as_dict(), ​
 +                             ​{'​tp':​ 1, '​tn':​ 0, '​fp':​ 0, '​fn':​ 0})
 +</​code>​
 +
 +Spusťte test. Dostáváme ''​AttributeError'',​ protože BCM nemá metodu ''​update()''​. Vytvořme v BCM prázdnou metodu ''​update()''​ se dvěma parametry ''​truth''​ a ''​prediction''​. ​
 +Spusťte test. Metoda nyní lze zavolat, ale nefunguje tak, jak by měla (''​AssertionError''​). Ve výsledku testu i vidíme, kde se vrácený slovník liší od očekávaného. Doplňme tedy její tělo: očekáváme,​ že metoda zvýší čítač TP. Vyplňme tedy kód metody:
 +<code python>
 +    def update(self,​ truth, prediction):​
 +        """​Compare the truth with the prediction and increment the related counter."""​
 +        self.tp += 1
 +</​code>​
 +Spusťte test. Měl by projít bez chyby.
 +++++
 +
 +==== Krok 3 ====
 +
 +++++ 3. Ruční refactoring opakujícího se kódu |
 +Prohlédněte si vaše 2 testovací metody. Nemají něco společného?​
 +Mají, zdá se, že ve všech testech budeme potřebovat čerstvou instanci BCM. Extrahujme tedy její vytvoření do metody ''​setUp()''​. Abychom vytvořenou instanci BCM mohli používat i v jiných metodách, musíme ji přiřadit do členské proměnné testové třídy. Z testových metod pak odstraníme vytváření BCM a budeme rovnou používat tu instanci v členské proměnné (použití ''​self''​). Kód testové třídy by po změnách měl vypadat takto: ​         ​
 +<code python>
 +class BinaryConfusionMatrixTest(unittest.TestCase):​
 +    ​
 +    def setUp(self):​
 +        # Prepare fixture
 +        self.cm = BinaryConfusionMatrix(pos_tag=SPAM_TAG,​ neg_tag=HAM_TAG)
 +        ​
 +    def test_countersAreZero_afterCreation(self):​
 +        # Exercise the SUT
 +        cmdict = self.cm.as_dict()
 +        # Assert
 +        self.assertDictEqual(cmdict,​ {'​tp':​ 0, '​tn':​ 0, '​fp':​ 0, '​fn':​ 0})
 +        ​
 +    def test_updatesTPcorrectly(self):​
 +        # Exercise the SUT
 +        self.cm.update(SPAM_TAG,​ SPAM_TAG)
 +        # Assert
 +        self.assertDictEqual(self.cm.as_dict(), ​
 +                             ​{'​tp':​ 1, '​tn':​ 0, '​fp':​ 0, '​fn':​ 0})
 +</​code>​
 +Spusťte testy. Měly by proběhnout v pořádku. Ale možná jste při opravách někde něco přehlédli,​ testy by vám pak na onom místě měly selhat.
 +++++
 +
 +
 +==== Krok 4 ====
 +++++ 4. Metoda update() správně upraví čítač TN |
 +Vytvořme test:
 +<code python>
 +    def test_updatesTNcorrectly(self):​
 +        # Exercise the SUT
 +        self.cm.update(HAM_TAG,​ HAM_TAG)
 +        # Assert
 +        self.assertDictEqual(self.cm.as_dict(), ​
 +                             ​{'​tp':​ 0, '​tn':​ 1, '​fp':​ 0, '​fn':​ 0})
 +</​code>​
 +
 +Spusťte test. Metoda selhává (''​AssertionError''​),​ protože místo čítače TN inkrementovala čítač TP. Upravme tedy její tělo co nejjednodušším způsobem tak, aby procházel test:
 +<code python>
 +    def update(self,​ truth, prediction):​
 +        """​Compare the truth with the prediction and increment the related counter."""​
 +        if prediction == self.pos_tag:​
 +            self.tp += 1
 +        elif prediction == self.neg_tag:​
 +            self.tn += 1
 +</​code>​
 +Spusťte test. Měl by projít bez chyby.
 +++++
 +
 +==== Krok 5 ====
 +++++ 5. Metoda update() správně upraví čítač FP |
 +Vytvořme test:
 +<code python>
 +    def test_updatesFPcorrectly(self):​
 +        # Exercise the SUT
 +        self.cm.update(HAM_TAG,​ SPAM_TAG)
 +        # Assert
 +        self.assertDictEqual(self.cm.as_dict(), ​
 +                             ​{'​tp':​ 0, '​tn':​ 0, '​fp':​ 1, '​fn':​ 0})
 +</​code>​
 +
 +Spusťte test. Metoda selhává (''​AssertionError''​). Upravme tedy její tělo co nejjednodušším způsobem tak, aby procházel test:
 +<code python>
 +    def update(self,​ truth, prediction):​
 +        """​Compare the truth with the prediction and increment the related counter."""​
 +        if prediction == self.pos_tag:​
 +            if truth == prediction:
 +                self.tp += 1
 +            else:
 +                self.fp += 1
 +        elif prediction == self.neg_tag:​
 +            self.tn += 1
 +</​code>​
 +Spusťte test. Měl by projít bez chyby.
 +++++
 +
 +==== Krok 6 ====
 +++++ 6. Metoda update() správně upraví čítače FN |
 +Vytvořme test:
 +<code python>
 +    def test_updatesFNcorrectly(self):​
 +        # Exercise the SUT
 +        self.cm.update(SPAM_TAG,​ HAM_TAG)
 +        # Assert
 +        self.assertDictEqual(self.cm.as_dict(), ​
 +                             ​{'​tp':​ 0, '​tn':​ 0, '​fp':​ 0, '​fn':​ 1})
 +</​code>​
 +
 +Spusťte test. Metoda selhává (''​AssertionError''​). Upravme tedy její tělo co nejjednodušším způsobem tak, aby procházel test:
 +<code python>
 +    def update(self,​ truth, prediction):​
 +        """​Compare the truth with the prediction and increment the related counter."""​
 +        if prediction == self.pos_tag:​
 +            if truth == prediction:
 +                self.tp += 1
 +            else:
 +                self.fp += 1
 +        elif prediction == self.neg_tag:​
 +            if truth == prediction:
 +                self.tn += 1
 +            else:
 +                self.fn += 1
 +</​code>​
 +Spusťte test. Měl by projít bez chyby.
 +++++
 +
 +
 +==== Krok 7 ====
 +> {{page>​..:​..:​internal:​cviceni:​spam:​krok3#​teachers_7&​editbtn&​noheader}}
 +
 +++++ 7. Co má metoda update dělat při chybném vstupu? |
 +Metoda ''​update(truth,​ prediction)''​ přebírá 2 argumenty, jejichž hodnoty by vždy měly být rovny pozitivnímu štítku nebo negativnímu štítku, které jsme zadali při vytváření instannce BCM. Co má metoda dělat, pokud některý z těchto argumentů tuto podmínku nesplňuje?
 +
 +Metoda by v takovém případě nejspíš měla vyhodit výjimku ''​ValueError''​. Jak se dá toto otestovat?
 +
 +Pokud v tuto chvíli nevíte nic o výjimkách,​ zkuste si o nich [[http://​openbookproject.net/​thinkcs/​python/​english3e/​exceptions.html|něco přečíst]],​ diskutujte se cvičícím,​ ptejte se na fóru. Nevadí, pokud tomuto tématu nerozumíte,​ ale bude dobře, když o něm budete alespoň vědět. ​
 +
 +<code python>
 +    def test_update_raisesValueError_forWrongTruthValue(self):​
 +        # Assert and exercise the SUT
 +        with self.assertRaises(ValueError):​
 +            self.cm.update('​a bad value',​ SPAM_TAG)
 +        ​
 +    def test_update_raisesValueError_forWrongPredictionValue(self):​
 +        # Assert and exercise the SUT
 +        with self.assertRaises(ValueError):​
 +            self.cm.update(SPAM_TAG,​ 'a bad value'​)
 +</​code>​
 +
 +Spusťte test. Měl by selhat. Upravme metodu ''​update()''​ třeba takto:
 +
 +<code python>
 +    def update(self,​ truth, prediction):​
 +        """​Compare the truth with the prediction and increment the related counter."""​
 +        if truth not in (self.pos_tag,​ self.neg_tag):​
 +            raise ValueError('​The "​truth"​ parameter can be either %s or %s.' \
 +                             % (self.pos_tag,​ self.neg_tag))
 +        if prediction not in (self.pos_tag,​ self.neg_tag):​
 +            raise ValueError('​The "​prediction"​ parameter can be either %s or %s.' \
 +                             % (self.pos_tag,​ self.neg_tag))
 +        if prediction == self.pos_tag:​
 +</​code>​
 +
 +Spusťte test. Měl by proběhnout bez chyb.
 +
 +Hmm, ale opakuje se tam kód na kontrolu hodnot argumentů. Proveďte refactoring.
 +++++
 +
 +==== Krok 8 ====
 +> {{page>​..:​..:​internal:​cviceni:​spam:​krok3#​teachers_8&​editbtn&​noheader}}
 +
 +++++ 8. Refactoring s pomocí IDE |
 +Metoda ''​update()''​ teď má na začátku dva téměř stejné bloky kódu: jeden kontroluje hodnotu parametru ''​truth'',​ druhý hodnotu parametru ''​prediction''​. Udělejme pro kontrolu hodnoty zvláštní metodu, kterou jen pak 2x zavoláme.
 +
 +Označte následující blok kódu:
 +<code python>
 +        if truth not in (self.pos_tag,​ self.neg_tag):​
 +            raise ValueError('​The "​truth"​ parameter can be either %s or %s.' \
 +                             % (self.pos_tag,​ self.neg_tag))
 +</​code>​
 +
 +Z menu **Refactor** nebo z nabídky **Refactor** v kontextovém menu vyberte položku **Extract** a následně **Method**. V dialogu **Extract method** zvolte jako jméno metody třeba ''​check_value_of''​ a klikněte na tlačítko **OK**.
 +
 +Ve vašem kódu by měly nastat 2 změny:
 +  * Automaticky byla vytvořena metoda ''​check_value_of()''​ s následujícím tělem:<​code python> ​   def check_value_of(self,​ truth):
 +        if truth not in (self.pos_tag,​ self.neg_tag):​
 +            raise ValueError('​The "​truth"​ parameter can be either %s or %s.' \
 +                             % (self.pos_tag,​ self.neg_tag))</​code>​
 +  * Na místě, kde se tento kód vykonával, je teď jen volání nové metody.
 +
 +Spusťte testy, abyste si ověřili, že IDE nezměnilo funkčnost kódu. Nyní bychom ale měli trochu upravit tělo nové metody a také jejím voláním nahradit druhý opakující se blok kódu. To už musíme udělat ručně. Upravme tedy ''​check_value_of()''​ třeba takto:
 +<code python>
 +    def check_value_of(self,​ value):
 +        """​Raise ValueError if var does not contain either positive or negative tag."""​
 +        if value not in (self.pos_tag,​ self.neg_tag):​
 +            raise ValueError('​The arguments may be either %s or %s.' \
 +                             % (self.pos_tag,​ self.neg_tag))
 +</​code>​
 +
 +A začátek metody ''​update()''​ upravte takto:
 +<code python>
 +    def update(self,​ truth, prediction):​
 +        """​Compare the truth with the prediction and increment the related counter."""​
 +        self.check_value_of(truth)
 +        self.check_value_of(prediction)
 +        if prediction == self.pos_tag:​
 +            ...
 +</​code> ​     ​
 +Spusťte testy pro kontrolu, zda jsme do kódu nezanesli nějakou chybu.
 +++++
 +
 +
 +==== Krok 9 ====
 +++++ 9. Metoda compute_from_dicts() správně updatuje čítače |
 +Vytvořme test: 
 +<code python>
 +    def test_computeFromDicts_allCasesOnce(self):​
 +        # Prepare fixture
 +        truth = {1: SPAM_TAG,
 +                 2: SPAM_TAG,
 +                 3: HAM_TAG,
 +                 4: HAM_TAG}
 +        prediction = {1: SPAM_TAG,
 +                      2: HAM_TAG,
 +                      3: SPAM_TAG,
 +                      4: HAM_TAG}
 +        # Excercise the SUT
 +        self.cm.compute_from_dicts(truth,​ prediction)
 +        # Assert
 +        self.assertDictEqual(self.cm.as_dict(), ​
 +                             ​{'​tp':​ 1, '​tn':​ 1, '​fp':​ 1, '​fn':​ 1})
 +</​code>​
 +
 +Spusťme test. Selhává (''​AttributeError''​),​ protože třída BCM nemá metodu ''​compute_from_dicts()''​. Vytvořme ji.
 +
 +<code python>
 +    def compute_from_dicts(self,​ truth_dict, pred_dict):
 +        """​Update the matrix using the corresponding values from both dictionaries.
 +        It is assumed that both dicts have the same keys.
 +        """​
 +        for key in truth_dict:
 +            self.update(truth_dict[key],​ pred_dict[key])
 +</​code>​
 +
 +Spusťe test. Měl by proběhnout v pořádku.
 +++++
 +
 +
 +==== Krok 10 ====
 +++++ 10. Diskuse: další testy? |
 +  * Jak bychom dál měli otestovat metodu compute_from_dicts()?​
 +    * Co když slovníky nebudou mít shodné klíče? V naší aplikaci by se to stát nemělo, bude-li dobře napsaná. Ale co když uděláme někde chybu a tato situace nastane?
 +    * Měli bychom to v tichosti přejít a prostě napočítat matici záměn jen z hodnot se stejnými klíči? Nebo vyhodit výjimku?
 +++++  ​
 +
 +
 +
 +
 +===== Hádanka =====
 +> {{page>​courses:​b4b33rph:​internal:​puzzles#​cviceni_9}}
 +
 +===== Programovací trik =====
 +++++ Volání funkce s argumenty připravenými v seznamu nebo slovníku |
 +Jak volat metodu či funkci, pro jejíž argumenty máme hodnoty připravené v seznamu nebo slovníku? ​
 +
 +Mějme následující funkci:
 +<code python>
 +def print_args(a,​ b):
 +    print(a, b)
 +</​code>​
 +
 +Předpokládejme,​ že bychom pro tuto funkci měli hodnoty parametrů ''​a''​ a ''​b''​ připraveny v poli nebo slovníku:
 +
 +<code python>
 +>>>​ arg_list = ['​Seznam',​ '​hodnot'​]
 +>>>​ arg_dict = {'​a':​ '​Slovnik',​ '​b':​ '​hodnot'​}
 +</​code>​
 +
 +Kdybychom chtěli funkci spustit s těmito hodnotami, museli bychom seznam i slovník ručně rozebrat na prvky a ty pak předat funkci, např. takto:
 +
 +<code python>
 +>>>​ print_args(arg_list[0],​ arg_list[1])
 +Seznam hodnot
 +>>>​ print_args(arg_dict['​a'​],​ arg_dict['​b'​])
 +Slovnik hodnot
 +</​code>​
 +
 +Takový způsob volání funkce je ale nepohodlný,​ obzvlášť tehdy, bude-li mít seznam či slovník mnohem více prvků. Python proto nabízí **trik**:
 +
 +<code python>
 +>>>​ print_args(*arg_list)
 +Seznam hodnot
 +>>>​ print_args(**arg_dict)
 +Slovnik hodnot
 +</​code>​
 +
 +Tento trik můžete s výhodou použít, až budete hodnoty prvků matice záměn získané metodou ''​as_dict()''​ přeávat do funkce ''​quality_score()''​.
 +
 +++++
 +
 +===== Diskuse: jednoduché filtry =====
 +Pokud jste tak ještě neučinili, přečtěte si specifikace [[..:​spam:​krok4|kroku 4]] úlohy Spam filtr:
 +  * Jaké metody filtr musí mít?
 +  * Musejí být implementovány všechny?
 +  * Jak tedy bude implementace jednoduchých filtrů přibližně vypadat?
 +
 +
 +===== Kontrola DÚ z minulého cvičení =====
 +  * Zkontrolovat,​ zda funkce ''​read_classification_from_file()''​ a třída ''​Corpus''​ procházejí unit testem.
 +  * Pokud ne, probrat výstup z unit testu a nasměrovat na správnou cestu!
 +
 +===== Programovací úloha =====
 +  * Dokončete [[..:​spam:​krok3|krok 3]] úlohy spam filtr, tedy funkci pro výpočet kvality filtru. Od neděle za týden budete modul ''​quality''​ společně s dalšími potřebnými moduly odevzdávat do upload systému!
 +  * Můžete začít pracovat na [[..:​spam:​krok4|kroku 4]] úlohy spam filtr, tj. implementujte 3 jednoduché filtry.
 +
 +
 +====== Domácí úkol ======
 +
 +===== Programování =====
 +  * Vyzkoušejte si/​proveďte/​dokončete programování třídy BinaryConfusionMatrix podle výše uvedeného návodu. Snažte se přitom pochopit nejen co děláte, ale i proč to děláte.
 +  * Pokud jste nestihli na cvičení, dokončete [[..:​spam:​krok3|krok 3]] úlohy spam filtr, tedy funkci pro výpočet kvality filtru. Tuto funkci budete za týden odevzdávat do upload systému a bude se hodnotit!!!
 +  * Dokončete [[..:​spam:​krok4|krok 4]] úlohy spam filtr, implementujte 3 jednoduché filtry:
 +    * Zkuste je aplikovat na nějaký korpus.
 +    * Použijte funkci z kroku 3 na odhad kvality filtru.
 +    * Jaké hodnoty kvality jste dostali pro naše 3 jednoduché filtry???
 +
 +
 +===== Příprava =====
 +Přečtete si něco o dědičnosti:​
 +> {{section>​..:​spam:​krok4#​priprava:​dedicnost&​noheader&​editbtn}}
 +
 +Příprava na krok 5 úlohy spam filtr:
 +> {{section>​..:​spam:​krok5#​priprava&​noheader&​editbtn}}
 +
  
courses/b4b33rph/cviceni/program_po_tydnech/tyden_09.txt · Last modified: 2018/12/04 16:25 by xposik