Search
Implementujte vlastní, co nejlepší filtr.
Testy ke kroku 6:
Rozmyslete si, jaké vnitřní mechanismy byste chtěli ve svém filtru použít, podle čeho by se váš filtr měl rozhodovat mezi korektním emailem a spamem a jak by se dal takový filtr učit z dat.
Projděte si také sekci s několika tipy, které by se vám při konstrukci filtru mohly hodit.
Na tomto místě bychom vám rádi poskytli pár užitečných tipů ke konstrukci vlastních filtrů. Zdaleka ne vše, co je v této sekci uvedeno, budete nutně potřebovat. Např. nepředpokládáme, že budete využívat modul email nebo regulární výrazy (i když samozřejmě můžete).
email
Tokenizace
Rozhodnete-li se vytvořit filtr, který potřebuje v textu emailu identifikovat jednotlivá slova či jiné důležité fráze, budete nejspíš provádět tzv. tokenizaci textu (Tokenization). Prostudujte si operace s řetězci, které lze dělat. Zvlášť bychom vás chtěli upozornit na následující:
short_str in long_str
Uvedený výčet berte pouze jako upozornění na metody, které by vám mohly být užitečné. Jednoduchý tokenizer může vypadat např. takto:
shortphrase = """ Lorem ipsum dolor sit amet consectetuer Aliquam orci wisi pretium Praesent. Lorem pellentesque ac ipsum Aenean Sed pretium Pellentesque adipiscing enim at. Nisl tempus tempor interdum dictumst wisi consequat id at eu platea. Volutpat adipiscing Curabitur pede Aenean interdum Nulla congue nibh pharetra Maecenas. Convallis aliquam leo leo nisl mi cursus nisl enim Nam leo. Duis nunc nulla leo augue nunc pretium sit Aenean Nam. """ # Replace dots with spaces shortphrase = shortphrase.translate(str.maketrans('.',' ')) # Transform the letters to lowercase and cut the string into tokens. tokens = shortphrase.lower().split() print(tokens)
['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetuer', 'aliquam', 'orci', 'wisi', 'pretium', 'praesent', 'lorem', 'pellentesque', 'ac', 'ipsum', 'aenean', 'sed', 'pretium', 'pellentesque', 'adipiscing', 'enim', 'at', 'nisl', 'tempus', 'tempor', 'interdum', 'dictumst', 'wisi', 'consequat', 'id', 'at', 'eu', 'platea', 'volutpat', 'adipiscing', 'curabitur', 'pede', 'aenean', 'interdum', 'nulla', 'congue', 'nibh', 'pharetra', 'maecenas', 'convallis', 'aliquam', 'leo', 'leo', 'nisl', 'mi', 'cursus', 'nisl', 'enim', 'nam', 'leo', 'duis', 'nunc', 'nulla', 'leo', 'augue', 'nunc', 'pretium', 'sit', 'aenean', 'nam'] >>>
Čítače
V minulých cvičeních jsme si ukazovali, jak udělat čítač výskytů písmen nebo slov z obyčejného slovníku. Protože se toto využití slovníku vyskytuje poměrně často, obsahuje Python třídu collections.Counter, která je odvozena od slovníku (má tedy všechny jeho metody), ale zároveň má upravené chování:
collections.Counter
counter['neexistující klíč']
KeyError
get()
Oproti obyčejnému slovníku má také pár užitečných metod navíc:
Counter
Budeme-li pokračovat v předchozím příkladu, následující řádky
counter = Counter(tokens) print(counter) print() print(counter.most_common(10))
Counter({'leo': 4, 'pretium': 3, 'aenean': 3, 'nisl': 3, 'at': 2, 'enim': 2, 'nunc': 2, 'interdum': 2, 'nam': 2, 'pellentesque': 2, 'lorem': 2, 'aliquam': 2, 'ipsum': 2, 'wisi': 2, 'sit': 2, 'adipiscing': 2, 'nulla': 2, 'pharetra': 1, 'ac': 1, 'pede': 1, 'duis': 1, 'sed': 1, 'eu': 1, 'id': 1, 'praesent': 1, 'volutpat': 1, 'platea': 1, 'convallis': 1, 'congue': 1, 'dolor': 1, 'tempus': 1, 'nibh': 1, 'consequat': 1, 'cursus': 1, 'curabitur': 1, 'maecenas': 1, 'amet': 1, 'dictumst': 1, 'mi': 1, 'augue': 1, 'tempor': 1, 'orci': 1, 'consectetuer': 1}) [('leo', 4), ('pretium', 3), ('aenean', 3), ('nisl', 3), ('at', 2), ('enim', 2), ('nunc', 2), ('interdum', 2), ('nam', 2), ('pellentesque', 2)]
Procházení uspořádaných prvků slovníku (čítače)
Často chceme procházet prvky slovníku (čítače) uspořádané podle hodnoty četnosti. Python používá k řazení funkci sorted(). Zkusme, co udělají následující příkazy:
sorted()
counter2 = Counter(counter.most_common(5)) sorted(counter2)
[('aenean', 3), ('at', 2), ('leo', 4), ('nisl', 3), ('pretium', 3)]
My ale chceme seznam seřadit podle četnosti tokenu. Musíme funkci sorted() dát najevo, aby používala druhý prvek z dvojice jako primární klíč k řazení. To lze udělat např. pomocí tzv. lambda funkce. (V tuto chvíli to můžete prostě brát jako recept, kterak slovník řadit nikoli podle klíče, ale podle hodnoty). Navíc bychom jí měli sdělit, že chceme, aby řadila sestupně:
sorted(counter2, key=lambda pair: (pair[1],pair[0]), reverse=True)
[('leo', 4), ('pretium', 3), ('nisl', 3), ('aenean', 3), ('at', 2)]
Tokenizace pro pokročilé aneb regulární výrazy
Pro pokročilejší nebo zvídavější studenty uveďme, že Python umožňuje pracovat s regulárními výrazy. Ty umožňují velmi flexibilně popsat vzor v řetězci a následně najít všechny části řetězce, které danému vzoru odpovídají, příp. řetězec podle nich rozdělit, atd. Použití takového regulárního výrazu k jednoduché tokenizaci může vypadat např. takto:
import re pattern = r"""(?im)[a-zA-Z\$]+[\w\d\$\-']*""" compiledre = re.compile(pattern) tokens = compiledre.findall(message) counter = Counter(tokens)
Struktura mailu
Nechcete-li s emaily pracovat jen jako s nestrukturovaným řetězcem a raději byste jej nějakým způsobem rozebrali na části a ty pak zpracovávali zvlášť, odkazujeme vás na modul ''email'' a jeho funkce:
email.message_from_string()
email.message_from_file()
Message.walk()
Úkol:
K čemu nám to bude:
Následující specifikace pro třídu MyFilter jsou povinné a musíte jim vyhovět!
MyFilter
V modulu filter.py vytvořte třídu MyFilter, která bude schopna
filter.py
train(train_corpus_dir)
test(test_corpus_dir)
!prediction.txt
train()
Typické použití třídy MyFilter bude následující:
from filter import MyFilter filter = MyFilter() filter.train('/path/to/training/corpus') # Tento adresář bude obsahovat soubor !truth.txt filter.test('/path/to/testing/corpus') # V tomto adresáři metoda vytvoří soubor !prediction.txt
Protože by ale metoda test() měla být schopná pracovat i bez předchozího volání metody train(), je přípustné i toto použití (a bude také testováno):
test()
from filter import MyFilter filter = MyFilter() filter.test('/path/to/testing/corpus') # V tomto adresáři metoda vytvoří soubor !prediction.txt
Při hodnocení kvality predikcí filtru samozřejmě budeme vždy volat metodu train() před metodou test().
!truth.txt
nazevsouboru OK
nazevsouboru SPAM