Bag-of-words (BoW) – technika uproszczonej reprezentacji tekstu. Polega na przekształeceniu sekwencji segmentów do policzonego zbioru segmentów. Kolejność segmentów nie ma znaczenia. Głównym zastosowaniem jest odwzorowanie podobieństwa znaczeniowego/miar podobieństwa dokumentów.¶
In [1]:
from nltk.tokenize import WordPunctTokenizer
tokenizer = WordPunctTokenizer()
In [2]:
texts = [
"Agnieszka ma nowy samochód",
"Agnieszka z Warszawy ma piękny samochód",
"Arek ma nowy rower",
"Samochód Agnieszki jest nowy"
]
Podział tekstu na tokeny
In [3]:
docs_orth = [tokenizer.tokenize(text) for text in texts]
docs_orth
Out[3]:
[['Agnieszka', 'ma', 'nowy', 'samochód'], ['Agnieszka', 'z', 'Warszawy', 'ma', 'piękny', 'samochód'], ['Arek', 'ma', 'nowy', 'rower'], ['Samochód', 'Agnieszki', 'jest', 'nowy']]
Przekształcenie listy do słownika wraz z liczbą wystąpień poszczególnych elementów
In [4]:
from nltk.probability import FreqDist
freq_orth = [FreqDist(doc) for doc in docs_orth]
freq_orth
Out[4]:
[FreqDist({'Agnieszka': 1, 'ma': 1, 'nowy': 1, 'samochód': 1}), FreqDist({'Agnieszka': 1, 'z': 1, 'Warszawy': 1, 'ma': 1, 'piękny': 1, 'samochód': 1}), FreqDist({'Arek': 1, 'ma': 1, 'nowy': 1, 'rower': 1}), FreqDist({'Samochód': 1, 'Agnieszki': 1, 'jest': 1, 'nowy': 1})]
Miara podobieństwa zbiorów w oparciu o indeks Jaccarda.¶
- przecięcie zbiorów = zbiór lematów występujących w obu zbiorach jednoczesnie
- suma zbiorów = zbiów wszystkich lematów występujących w obu zbiorach
- J(A,B) = |przecięcie zbiorów| / |suma zbiorów|
In [5]:
# funkcja przyjmuje 2 argumenty - reprezentacje dwóch dokumentów
def bow_similarity(bow1: FreqDist, bow2: FreqDist) -> float:
# suma zbiorów
bow_all = bow1 + bow2
# przecięcie zbiorów. Iteracja po elementach sumy zbiorów i sprawdzanie czy występuje w każdym z dokumentów
bow_common = [word for word in bow_all.keys() if word in bow1 and word in bow2]
# jeżeli liczność sumy zbiorów jest zerowa to zwracamy zero
# jeżeli liczność sumy zbiorów nie jest zerowa to postępujemy zgodnie ze wzorem Jaccarda: J(A,B)
return 0 if len(bow_all) == 0 else len(bow_common) / len(bow_all)
In [6]:
print(texts[0], "\n")
for n in range(1, 4):
sim = bow_similarity(freq_orth[0], freq_orth[n])
print(f"{sim:<6.4} {texts[n]}")
Agnieszka ma nowy samochód 0.4286 Agnieszka z Warszawy ma piękny samochód 0.3333 Arek ma nowy rower 0.1429 Samochód Agnieszki jest nowy
W powyższym przykładzie prównujemy miarę podobieństwa pomiędzy zdaniem „Agnieszka ma nowy samochód”, a pozostałymi. Jak widzimy zdanie „Arek ma nowy rower” otrzymało lepszy ranking niż „Samochód Agnieszki jest nowy”. Dzieje się tak dlatego, ponieważ indeks Jaccarda nie wskazuje, które zdania są bliższe pod względem semantycznym, ale które współdzielą więcej form tekstowych.