aitech-eks-pub/cw/02a_tfidf_tasks.ipynb
2021-03-09 13:46:07 +01:00

23 KiB

Zajęcia 2

Na tych zajęciach za aktywnośc można otrzymać po 5 punktów za wartościową wypowiedź. Maksymalnie jedna osoba może zdobyć na tych ćwiczeniach do 15 punktów.

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?

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']

PYTANIA

jak będzie słowo "jak" w reprezentacji wektorowej TF?

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
def word_to_index(word):
    vec = np.zeros(len(vocabulary))
    if word in vocabulary:
        idx = vocabulary.index(word)
        vec[idx] = 1
    else:
        vec[-1] = 1
    return vec
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
def tf(document):
    document_vector = None
    for word in document:
        if document_vector is None:
            document_vector = word_to_index(word)
        else:
            document_vector += word_to_index(word)
    return document_vector
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

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):
    numerator = np.sum(query * document)
    denominator = np.sqrt(np.sum(query*query)) * np.sqrt(np.sum(document*document)) 
    return numerator / denominator
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
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 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ę
query = 'i chomika'
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.24999999999999994
'Ola lubi zwierzęta oraz ma kota a także chomika!'
0.2357022603955158
'I Jan jeździ na rowerze.'
0.31622776601683794
'2 wojna światowa była wielkim konfliktem zbrojnym'
0.0
'Tomek lubi psy, ma psa  i jeździ na motorze i rowerze.'
0.39223227027636803

ZADANIE 4 NAPISAĆ IDF w celu zmiany wag z TF na TF- IDF

Proszę użyć wersję 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