Warning
This page is located in archive.

Spam filter - krok 6: Vlastní filtr

Implementujte vlastní, co nejlepší filtr.

Testy ke kroku 6:

Příprava

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.

Programovací tipy

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).

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í:

  • str.lower() a str.upper(), pokud např. nebudete chtít rozlišovat mezi 'RESULT', 'Result' a 'result',
  • str.lstrip(), str.rstrip(), str.strip(), pokud budete chtít z nějakého řetězce třeba odstranit úvodní, koncové, nebo obojí mezery,
  • str.startswith(), str.endswith(), pokud budete chtít zjistit, zda nějaký řetězec začíná či končí jiným řetězcem,
  • short_str in long_str, pokud budete chtít zjistit, zda se jeden řetězec vyskytuje ve druhém,
  • str.find(), pokud budete chtít zjistit, kde přesně se jeden řetězec vyskytuje ve druhém,
  • str.replace(), budete-li chtít např. nějaké podřetězce ignorovat a nahradit je např. mezerami nebo jiným řetězcem,
  • str.translate(), budete-li chtít hromadně zaměnit jednotlivé znaky za jiné, a konečně
  • str.split(), budete-li chtít dlouhý řetězec rozsekat na pole kratších řetězců podle specifikovaného znaku nebo podle bílých znaků.

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í:

  • counter['neexistující klíč'] vrací 0, nikoli KeyError. (Nemusíme tedy používat metodu get().)

Oproti obyčejnému slovníku má také pár užitečných metod navíc:

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))
vypíší
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:

counter2 = Counter(counter.most_common(5))
sorted(counter2)
Výsledek:
[('aenean', 3), ('at', 2), ('leo', 4), ('nisl', 3), ('pretium', 3)]
Je to seznam dvojic (token, četnost tokenu) seřazený abecedně podle tokenu.

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)
Dostaneme kýžený výsledek:
[('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:

Vlastní filtr

Úkol:

  • Vytvořte vlastní funkční spam filtr.

K čemu nám to bude:

  • Budete jej odevzdávat a budete za něj hodnoceni.

Specifikace

Inspirujte se u jednoduchých filtrů z kroku 4. Vnitřní mechanismy vašeho spam filtru jsou zcela na vás.

Abychom mohli váš filtr úspěšně volat v testovacím skriptu, musí splňovat tyto specifikace.

courses/a4b99rph/cviceni/spam/krok6.txt · Last modified: 2013/10/04 13:02 (external edit)