ML Atlas

02 · Dane · 4 min czytania · Interaktywne · aktualizacja

Czym jest TF-IDF i jak zamienić tekst na liczby dla modelu?

W skrócie

TF-IDF zamienia tekst na wektor wag słów: słowo jest ważne, gdy często pada w dokumencie, a rzadko w całym zbiorze. Prosty, mocny start dla klasyfikacji.

Co to jest

Worek słów (ang. bag of words) to reprezentacja tekstu jako wektora zliczeń: każda pozycja odpowiada jednemu słowu ze słownika, a wartość mówi, ile razy słowo wystąpiło w dokumencie. Kolejność słów znika — zostaje tylko ich skład. TF-IDF (term frequency – inverse document frequency) to ważenie tych zliczeń: słowo dostaje dużą wagę, gdy często występuje w danym dokumencie (TF), ale rzadko w całym zbiorze dokumentów (IDF).

Pomysł odwrotnej częstości dokumentowej pochodzi od Karen Spärck Jones (1972); TF-IDF stał się standardem wyszukiwania informacji, a w uczeniu maszynowym to wciąż mocny punkt odniesienia dla klasyfikacji tekstów, wykrywania spamu czy grupowania dokumentów.

Intuicja: słowo „na” pojawia się prawie wszędzie, więc nic nie mówi o temacie tekstu. Słowo „kardiomiopatia” pojawia się rzadko — jeśli jest w dokumencie, prawie na pewno mówi, o czym on jest.

Mechanizm — dlaczego tak działa

Model potrzebuje wektorów o stałej długości, a teksty mają różną długość. Worek słów rozwiązuje to, budując słownik wszystkich słów w zbiorze treningowym; każdy dokument staje się wektorem o długości słownika, w większości wypełnionym zerami (macierz rzadka).

Same zliczenia mają wadę: dominują w nich najczęstsze słowa — spójniki, przyimki, zaimki — które występują we wszystkich dokumentach. Częstości słów w języku są skrajnie nierówne (prawo Zipfa), więc kilka słów funkcyjnych zagłusza resztę. IDF to naprawia: idf(t) = log(N / df(t)), gdzie N to liczba dokumentów, a df(t) — liczba dokumentów zawierających słowo t. Słowo obecne wszędzie dostaje wagę bliską zera, słowo rzadkie — dużą. Waga końcowa to tf · idf, a wektor dokumentu normalizuje się do długości 1, by długie teksty nie miały przewagi.

Podobieństwo dokumentów mierzy się wtedy cosinusem kąta między wektorami. Dwa teksty są bliskie, gdy dzielą słowa, i to głównie słowa rzadkie, charakterystyczne.

Ograniczenia: TF-IDF nie zna znaczenia ani kolejności. „Pies ugryzł człowieka” i „człowiek ugryzł psa” mają podobne wektory, a „samochód” i „auto” są dla niego zupełnie różnymi słowami. Częściowo pomagają n-gramy (pary i trójki słów), a w polszczyźnie — lematyzacja, bo „kot”, „kota” i „kotem” to inne tokeny. Zanurzenia słów i modele językowe rozwiązują te problemy, ale TF-IDF z regresją logistyczną bywa zaskakująco trudny do pobicia na małych zbiorach i jest w pełni przejrzysty: wagi da się przeczytać.

Na przykładzie

Trzy krótkie dokumenty: „kot śpi na macie”, „pies śpi na trawie”, „kot goni mysz na trawie”. Słownik ma 8 słów. W domyślnej wersji scikit-learn idf(t) = ln((1 + N) / (1 + df(t))) + 1, więc słowo „na”, obecne we wszystkich trzech dokumentach, dostaje idf = 1,0, słowa obecne w dwóch („kot”, „śpi”, „trawie”) — 1,288, a słowa z jednego dokumentu („macie”, „pies”, „goni”, „mysz”) — 1,693. Po normalizacji w pierwszym dokumencie „macie” waży 0,632, „kot” i „śpi” po 0,48, a „na” tylko 0,373.

Podobieństwo cosinusowe dokumentów 1 i 2 na surowych zliczeniach wynosi 0,5 — połowę tej zgodności daje wspólne „na”. Po ważeniu TF-IDF spada do 0,37: wspólne słowo funkcyjne liczy się mniej, a różniące się słowa treściowe bardziej. W prawdziwym korpusie, gdzie słów funkcyjnych są dziesiątki, efekt jest znacznie silniejszy.

Ta ilustracja działa w przeglądarce z włączonym JavaScriptem: zabawkowy RAG: dla 5 pytań wyszukiwanie po słowach (TF-IDF lub BM25) umieszcza fragment z odpowiedzią w top 5 w 4 przypadkach, a pytanie sformułowane synonimami nie znajduje go wcale (wynik 0).

W praktyce

  • TfidfVectorizer w scikit-learn łączy tokenizację, zliczanie i ważenie; CountVectorizer daje surowe zliczenia.
  • Ważne parametry: ngram_range=(1, 2), min_df (usuwa słowa zbyt rzadkie), max_df (zbyt częste), sublinear_tf=True (log z TF), stop_words.
  • Wektoryzator dopasowuj tylko na danych treningowych — IDF liczony na teście to wyciek.
  • Dobre pary modeli: LogisticRegression, LinearSVC, MultinomialNB; dobrze radzą sobie z rzadkimi, wysokowymiarowymi wektorami.
  • Dla polskiego rozważ lematyzację lub n-gramy znakowe (analyzer='char_wb'), bo fleksja rozbija słowa na wiele tokenów.
  • Typowy błąd: zamiana macierzy rzadkiej na gęstą (.toarray()) przy dużym słowniku — zabraknie pamięci.

Najczęstsze pytania

Czy TF-IDF jest jeszcze potrzebny w erze modeli językowych?
Tak, jako szybki i interpretowalny punkt odniesienia, w wyszukiwaniu słów kluczowych (np. BM25, jego następca) oraz tam, gdzie danych jest mało, a obliczenia mają być tanie. Często służy też jako jedna z metod w wyszukiwaniu hybrydowym.
Dlaczego logarytm w IDF?
Bez logarytmu słowo występujące w jednym dokumencie na milion dostałoby wagę milion razy większą niż słowo obecne wszędzie, co zdominowałoby wszystko. Logarytm spłaszcza tę skalę, zachowując kolejność.
Czym różni się worek słów od zanurzeń słów?
W worku słów każde słowo to osobny wymiar i wszystkie słowa są od siebie jednakowo odległe. Zanurzenia przypisują słowom gęste wektory, w których słowa o podobnym znaczeniu leżą blisko siebie.

Źródła

  • Spärck Jones K. (1972). „A statistical interpretation of term specificity and its application in retrieval”. Journal of Documentation, 28(1), 11–21.
  • Salton G., Buckley C. (1988). „Term-weighting approaches in automatic text retrieval”. Information Processing & Management, 24(5), 513–523.
  • Manning C. D., Raghavan P., Schütze H. „Introduction to Information Retrieval”. Cambridge University Press, 2008, rozdz. 6.
  • Jurafsky D., Martin J. H. „Speech and Language Processing”, 3rd ed. (wersja robocza), rozdz. „Vector Semantics and Embeddings”.
  • Dokumentacja scikit-learn: Text feature extraction, https://scikit-learn.org/stable/modules/feature_extraction.html#text-feature-extraction

Zobacz też