aitech-eks-pub-22/cw/03a_tfidf.ipynb
Jakub Pokrywka f4e46c4588 add 03
2022-03-22 21:56:40 +01:00

24 KiB
Raw Blame History

Logo 1

Ekstrakcja informacji

3. tfidf (1) [ćwiczenia]

Jakub Pokrywka (2021)

Logo 2

Zajęcia 3

import numpy as np
import re

zbiór dokumentów

documents = ['Ala lubi zwierzęta i ma kota oraz psa!',
             'Ola lubi zwierzęta oraz ma kota a także chomika!',
             'I Jan jeździ na rowerze.',
             '2 wojna światowa była wielkim konfliktem zbrojnym',
             'Tomek lubi psy, ma psa  i jeździ na motorze i rowerze.',
            ]

CZEGO CHCEMY?

  • chcemy zamienić teksty na zbiór słów

PYTANIE

  • czy możemy ztokenizować tekst np. documents.split(' ') jakie wystąpią wtedy problemy?

ODPOWIEDŹ

  • lepiej użyć preprocessingu i dopiero później tokenizacji

preprocessing

def get_str_cleaned(str_dirty):
    punctuation = '!"#$%&\'()*+,-./:;<=>?@[\\\\]^_`{|}~'
    new_str = str_dirty.lower()
    new_str = re.sub(' +', ' ', new_str)
    for char in punctuation:
        new_str = new_str.replace(char,'')
    return new_str
sample_document = get_str_cleaned(documents[0])
sample_document
'ala lubi zwierzęta i ma kota oraz psa'

tokenizacja

def tokenize_str(document):
    return document.split(' ')
tokenize_str(sample_document)
['ala', 'lubi', 'zwierzęta', 'i', 'ma', 'kota', 'oraz', 'psa']
documents_cleaned = [get_str_cleaned(d) for d in documents]
documents_cleaned
['ala lubi zwierzęta i ma kota oraz psa',
 'ola lubi zwierzęta oraz ma kota a także chomika',
 'i jan jeździ na rowerze',
 '2 wojna światowa była wielkim konfliktem zbrojnym',
 'tomek lubi psy ma psa i jeździ na motorze i rowerze']
documents_tokenized = [tokenize_str(d) for d in documents_cleaned]
documents_tokenized
[['ala', 'lubi', 'zwierzęta', 'i', 'ma', 'kota', 'oraz', 'psa'],
 ['ola', 'lubi', 'zwierzęta', 'oraz', 'ma', 'kota', 'a', 'także', 'chomika'],
 ['i', 'jan', 'jeździ', 'na', 'rowerze'],
 ['2', 'wojna', 'światowa', 'była', 'wielkim', 'konfliktem', 'zbrojnym'],
 ['tomek',
  'lubi',
  'psy',
  'ma',
  'psa',
  'i',
  'jeździ',
  'na',
  'motorze',
  'i',
  'rowerze']]

PYTANIA

  • jaki jest następny krok w celu stworzenia wektórów TF lub TF-IDF
  • jakie wielkości będzie wektor TF lub TF-IDF?
vocabulary = []
for document in documents_tokenized:
    for word in document:
        vocabulary.append(word)
vocabulary = sorted(set(vocabulary))
vocabulary
['2',
 'a',
 'ala',
 'była',
 'chomika',
 'i',
 'jan',
 'jeździ',
 'konfliktem',
 'kota',
 'lubi',
 'ma',
 'motorze',
 'na',
 'ola',
 'oraz',
 'psa',
 'psy',
 'rowerze',
 'także',
 'tomek',
 'wielkim',
 'wojna',
 'zbrojnym',
 'zwierzęta',
 'światowa']

ZADANIE 1 stworzyć funkcję word_to_index(word:str), funkcja ma zwarać one-hot vector w postaciu numpy array

def word_to_index(word):
    pass
word_to_index('psa')
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.,
       0., 0., 0., 0., 0., 0., 0., 0., 0.])

ZADANIE 2 NAPISAC FUNKCJĘ, która bierze listę słów i zamienia na wetktor TF

def tf(document):
    pass
tf(documents_tokenized[0])
array([0., 0., 1., 0., 0., 1., 0., 0., 0., 1., 1., 1., 0., 0., 0., 1., 1.,
       0., 0., 0., 0., 0., 0., 0., 1., 0.])
documents_vectorized = list()
for document in documents_tokenized:
    document_vector = tf(document)
    documents_vectorized.append(document_vector)
documents_vectorized
[array([0., 0., 1., 0., 0., 1., 0., 0., 0., 1., 1., 1., 0., 0., 0., 1., 1.,
        0., 0., 0., 0., 0., 0., 0., 1., 0.]),
 array([0., 1., 0., 0., 1., 0., 0., 0., 0., 1., 1., 1., 0., 0., 1., 1., 0.,
        0., 0., 1., 0., 0., 0., 0., 1., 0.]),
 array([0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0.,
        0., 1., 0., 0., 0., 0., 0., 0., 0.]),
 array([1., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 1., 1., 1., 0., 1.]),
 array([0., 0., 0., 0., 0., 2., 0., 1., 0., 0., 1., 1., 1., 1., 0., 0., 1.,
        1., 1., 0., 1., 0., 0., 0., 0., 0.])]

IDF

Wersja bez żadnej normalizacji

$idf_i = \Large\frac{|D|}{|\{d : t_i \in d \}|}$

$|D|$ - ilość dokumentów w korpusie $|\{d : t_i \in d \}|$ - ilość dokumentów w korpusie, gdzie dany term występuje chociaż jeden raz

idf = np.zeros(len(vocabulary))
idf = len(documents_vectorized) / np.sum(np.array(documents_vectorized) != 0,axis=0)
display(idf)
array([5.        , 5.        , 5.        , 5.        , 5.        ,
       1.66666667, 5.        , 2.5       , 5.        , 2.5       ,
       1.66666667, 1.66666667, 5.        , 2.5       , 5.        ,
       2.5       , 2.5       , 5.        , 2.5       , 5.        ,
       5.        , 5.        , 5.        , 5.        , 2.5       ,
       5.        ])
for i in range(len(documents_vectorized)):
    documents_vectorized[i] = documents_vectorized[i] * idf

ZADANIE 3 Napisać funkcję similarity, która zwraca podobieństwo kosinusowe między dwoma dokumentami w postaci zwektoryzowanej

def similarity(query, document):
    pass
documents[0]
'Ala lubi zwierzęta i ma kota oraz psa!'
documents_vectorized[0]
array([0., 0., 1., 0., 0., 1., 0., 0., 0., 1., 1., 1., 0., 0., 0., 1., 1.,
       0., 0., 0., 0., 0., 0., 0., 1., 0.])
documents[1]
'Ola lubi zwierzęta oraz ma kota a także chomika!'
documents_vectorized[1]
array([0., 1., 0., 0., 1., 0., 0., 0., 0., 1., 1., 1., 0., 0., 1., 1., 0.,
       0., 0., 1., 0., 0., 0., 0., 1., 0.])
similarity(documents_vectorized[0],documents_vectorized[1])
0.5892556509887895
def transform_query(query):
    query_vector = tf(tokenize_str(get_str_cleaned(query)))
    return query_vector
transform_query('psa')
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.,
       0., 0., 0., 0., 0., 0., 0., 0., 0.])
similarity(transform_query('psa kota'), documents_vectorized[0])
0.4999999999999999
# tak są obsługiwane 2 słowa
query = 'psa kota'
for i in range(len(documents)):
    display(documents[i])
    display(similarity(transform_query(query), documents_vectorized[i]))
'Ala lubi zwierzęta i ma kota oraz psa!'
0.4999999999999999
'Ola lubi zwierzęta oraz ma kota a także chomika!'
0.2357022603955158
'I Jan jeździ na rowerze.'
0.0
'2 wojna światowa była wielkim konfliktem zbrojnym'
0.0
'Tomek lubi psy, ma psa  i jeździ na motorze i rowerze.'
0.19611613513818402
# dlatego potrzebujemy mianownik w cosine similarity
# dłuższe dokumenty, w który raz wystąpie słowo rower są gorzej punktowane od
# krótszych. Jeżeli słowo rower wystąpiło w bardzo krótki dokumencie, to znaczy
# że jest większe prawdopodobieństwo że dokument jest o rowerze
query = 'rowerze'
for i in range(len(documents)):
    display(documents[i])
    display(similarity(transform_query(query), documents_vectorized[i]))
'Ala lubi zwierzęta i ma kota oraz psa!'
0.0
'Ola lubi zwierzęta oraz ma kota a także chomika!'
0.0
'I Jan jeździ na rowerze.'
0.4472135954999579
'2 wojna światowa była wielkim konfliktem zbrojnym'
0.0
'Tomek lubi psy, ma psa  i jeździ na motorze i rowerze.'
0.2773500981126146
# dlatego potrzebujemy term frequency → wiecej wystąpień słowa w dokumencie
# znaczy bardziej dopasowany dokument
query = 'i'
for i in range(len(documents)):
    display(documents[i])
    display(similarity(transform_query(query), documents_vectorized[i]))
'Ala lubi zwierzęta i ma kota oraz psa!'
0.35355339059327373
'Ola lubi zwierzęta oraz ma kota a także chomika!'
0.0
'I Jan jeździ na rowerze.'
0.4472135954999579
'2 wojna światowa była wielkim konfliktem zbrojnym'
0.0
'Tomek lubi psy, ma psa  i jeździ na motorze i rowerze.'
0.5547001962252291
# dlatego IDF - żeby ważniejsze słowa miał większą wagę
# słowo chomik ma większą wagę od i, ponieważ występuje w mniejszej ilości dokumentów
query = 'i chomika'
for i in range(len(documents)):
    display(documents[i])
    display(similarity(transform_query(query), documents_vectorized[i]))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-ca637083c8f1> in <module>
      2 # słowo chomik ma większą wagę od i, ponieważ występuje w mniejszej ilości dokumentów
      3 query = 'i chomika'
----> 4 for i in range(len(documents)):
      5     display(documents[i])
      6     display(similarity(transform_query(query), documents_vectorized[i]))

NameError: name 'documents' is not defined

Uwaga

Powyższe przykłady pokazują score dokuemntu. Aby zrobić wyszukiwarkę, powinniśmy posortować te dokumenty po score (od największego) i zaprezentwoać w tej kolejności.