Term frequency × inverse document frequency: by hand, in scikit-learn, in a search engine, and where it breaks
A stop list fixes the first problem, for one language and only for the words someone thought of listing.
Spärck Jones (1972): a term that occurs in many documents is a poor way to tell documents apart, so it should count less.
Salton & Buckley (1988), comparing many weighting schemes, made tf × idf with length normalization the standard of information retrieval.
\(n\) documents, \(\text{df}(t)\) of them contain \(t\). Then every row is divided by its length (norm='l2').
smooth_idf=True: as if one extra document contained every word once. No division by zero.
a word in every document keeps weight 1, not 0: discounted, not deleted.
"filioque", in 2 posts: idf 9.2
"the", in nearly all: idf 1.18
| cachorro | comeu | do | dorme | gato | peixe | ração | sonha | |
|---|---|---|---|---|---|---|---|---|
| d1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
| d2 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 |
| d3 | 0 | 0 | 0 | 1 | 2 | 0 | 0 | 1 |
the counts of the bag-of-words page: "O gato comeu o peixe.", "O cachorro comeu a ração do gato.", "Gato dorme, gato sonha."
d1 = "gato comeu peixe", each word once.
| gato | comeu | peixe | |
|---|---|---|---|
| 1. tf × idf | 1 | 1.2877 | 1.6931 |
| 2. length | \(\sqrt{1^2 + 1.2877^2 + 1.6931^2} = \sqrt{5.5249} = 2.3505\) | ||
| 3. divide by it | 0.4254 | 0.5478 | 0.7203 |
tfidf = TfidfVectorizer() # defaults: smooth_idf=True, norm='l2', sublinear_tf=False
T = tfidf.fit_transform(docs)
cachorro comeu do dorme gato peixe ração sonha
d1 0.0000 0.5478 0.0000 0.0000 0.4254 0.7203 0.0000 0.0000
d2 0.5046 0.3838 0.5046 0.0000 0.2980 0.0000 0.5046 0.0000
d3 0.0000 0.0000 0.0000 0.5427 0.6411 0.0000 0.0000 0.5427
same idf as by hand: True
Row d1 matches the hand computation to the fourth decimal. TfidfVectorizer = CountVectorizer + exactly this weighting.
| option | dorme | gato | sonha |
|---|---|---|---|
| default | 0.5427 | 0.6411 | 0.5427 |
smooth_idf=False | 0.5864 | 0.5588 | 0.5864 |
sublinear_tf=True | 0.5774 | 0.5774 | 0.5774 |
norm=None | 1.6931 | 2 | 1.6931 |
smooth_idf=False: \(\ln(n/\text{df}) + 1\); the rare words get \(\ln 3 + 1 = 2.0986\) and overtake "gato".sublinear_tf=True: count → \(1 + \ln(\text{tf})\); the two "gato" count \(1.6931\), equal to the rare words' idf by coincidence.norm=None: raw products; longer documents get larger vectors (matters for Euclidean distance, not for the cosine).TF-IDF rows have length 1, so the cosine is just their dot product. d1 and d2 share comeu and gato:
\[ \cos(d_1, d_2) = 0.5478 \times 0.3838 + 0.4254 \times 0.2980 = 0.2102 + 0.1268 = 0.337 \]d1 and d3 share only gato: \(0.4254 \times 0.6411 = 0.273\).
d1 d2 d3
d1 1.000 0.516 0.471
d2 0.516 1.000 0.365
d3 0.471 0.365 1.000
d1 d2 d3
d1 1.000 0.337 0.273
d2 0.337 1.000 0.191
d3 0.273 0.191 1.000
A help-desk FAQ of ten questions. Turn the query into a vector with the same fitted vectorizer, rank the questions by cosine.
| query | score | best match | words used |
|---|---|---|---|
| esqueci minha senha | 0.35 | FAQ 0: trocar a senha… | senha |
| erro 0x80004005 | 0.53 | FAQ 9: o erro 0x80004005… | erro, 0x80004005 |
| quero contestar a correção do exame | 0.51 | FAQ 0: trocar a senha… | do |
| onde almoçar no sábado | 0.37 | FAQ 2: segunda via do boleto… | onde |
| perdi a fatura | 0.00 | FAQ 0 (first by default) | — |
quero contestar a correção do exame best score 0.00
onde almoçar no sábado best score 0.00
2,097 posts from four newsgroups, reduced with a truncated SVD (singular value decomposition) and drawn with t-SNE (the color is the newsgroup). Nearest 5 posts from the same newsgroup: counts 46%, TF-IDF 81% (random: about 25%).
Each word is its own axis, orthogonal to every other word.
| pair | cosine | shared words |
|---|---|---|
| synonyms: o carro é rápido / o automóvel é veloz | 0.00 | none |
| word order: o cachorro mordeu o homem / o homem mordeu o cachorro | 1.00 | cachorro, homem, mordeu |
| two meanings: a manga da camisa rasgou / comi uma manga madura | 0.14 | manga |
| same meaning: a manga da camisa rasgou / o punho da blusa descosturou | 0.14 | da |
'o cachorro latiu' non-zeros: 1 {'cachorro': 1.0}
'um felino adormeceu' non-zeros: 0 {}
"a feline fell asleep": a sentence about a cat sleeping, next to documents about cats sleeping, with similarity 0 to all of them.
Predict. In the lesson preset, untick smooth_idf: will the idf of the rare words go up or down? Then try the synonyms preset.