--- id: text-processing-classic title: Classic Text Processing and TF-IDF code: CT subtitle: Tokenize, stem, lemmatize, count, weight with TF-IDF, classify sentiment and find topics with LDA or BERTopic. hue: 30 sources: phases/05-nlp-foundations-to-advanced/01-text-processing, phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf, phases/05-nlp-foundations-to-advanced/05-sentiment-analysis, phases/05-nlp-foundations-to-advanced/15-topic-modeling source_hash: 8993666a55a0debd --- ## Classic text pipeline type: flow from: phases/05-nlp-foundations-to-advanced/01-text-processing/docs/en.md, phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf/docs/en.md, phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md 1. **Tokenize**: split the string into word tokens. Keep punctuation as its own tokens. 2. **Stem or lemmatize**: stem for speed. Lemmatize with a POS tag when meaning matters. 3. **Negation scope**: prefix tokens after a negation word with `NOT_` up to punctuation. 4. **Count**: bag of words. One row per document, one column per vocabulary word. 5. **Weight**: TF-IDF scales down common words. L2 rows make cosine a dot product. 6. **Classify**: Naive Bayes for a baseline, logistic regression for production. 7. **Evaluate**: per-class precision and recall, macro-F1, confusion matrix. ## Tasks in code type: task-table span: 2 from: phases/05-nlp-foundations-to-advanced/01-text-processing/code/main.py, phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf/code/main.py, phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md, phases/05-nlp-foundations-to-advanced/15-topic-modeling/code/main.py | Task | Code | |---|---| | Tokenize, stem and lemmatize | `preprocess(text, pos_tagger=demo_pos_tagger)` | | Porter step 1a | `stem_step_1a(word)` | | Table lemmatizer with fallback | `lemmatize(word, pos)` | | Count matrix | `bag_of_words(docs, build_vocab(docs))` | | TF-IDF rows on the unit sphere | `l2_normalize(tfidf(bow))` | | Scope negation | `apply_negation(tokens)` | | Naive Bayes baseline | `train_nb(docs_by_class, vocab, alpha=1.0)` | | LDA by collapsed Gibbs sampling | `collapsed_gibbs_lda(docs, n_topics=3, n_iters=300, seed=42)` | ## NLTK: tokens, stems, lemmas type: annotated-code lang: python from: phases/05-nlp-foundations-to-advanced/01-text-processing/docs/en.md ```python nltk.download("punkt_tab") nltk.download("wordnet") nltk.download("averaged_perceptron_tagger_eng") tokens = word_tokenize(text) # contractions, Unicode stems = [PorterStemmer().stem(t) for t in tokens] # all five phases tagged = pos_tag(tokens) # Penn Treebank tags lemmatizer = WordNetLemmatizer() lemmas = [lemmatizer.lemmatize(t, nltk_pos_to_wordnet(tag)) for t, tag in tagged] # V>v J>a R>r else n ``` ## Sentiment baseline in scikit-learn type: annotated-code lang: python from: phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md ```python pipe = Pipeline([ ("tfidf", TfidfVectorizer( ngram_range=(1, 2), # bigrams, so not_good is a feature min_df=2, sublinear_tf=True, # dampens repeated words stop_words=None)), # keeps negations ("clf", LogisticRegression(C=1.0, max_iter=1000)), ]) pipe.fit(X_train, y_train) print(pipe.score(X_test, y_test)) # 75% to 85% on SST-2 with the 3 flags ``` ## Vectorizer knobs type: command-table from: phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf/docs/en.md | Command | What it does | |---|---| | `CountVectorizer()` | Tokenization, vocabulary and BoW in one call | | `TfidfVectorizer()` | Adds IDF weighting and L2 normalization | | `ngram_range=(1, 2)` | Includes bigrams. Usually boosts classification | | `min_df=2` | Drops words in fewer than 2 docs | | `max_df=0.95` | Drops words in more than 95% of docs | | `stop_words="english"` | Builtin stopword list. Not for sentiment | | `sublinear_tf=True` | Uses `1 + log(tf)` instead of raw `tf` | | `get_feature_names_out()` | Lists the vocabulary in column order | ## Topic models type: annotated-code lang: python from: phases/05-nlp-foundations-to-advanced/15-topic-modeling/docs/en.md ```python cv = CountVectorizer(max_features=max_features, stop_words="english", min_df=2, max_df=0.9) # LDA needs raw counts X = cv.fit_transform(documents) lda = LatentDirichletAllocation(n_components=n_topics, random_state=42, max_iter=50, learning_method="online") doc_topic = lda.fit_transform(X) # (n_docs, n_topics), rows sum to 1 topic_model = BERTopic( embedding_model="sentence-transformers/all-MiniLM-L6-v2", min_topic_size=15) # library default is 10 topics, probs = topic_model.fit_transform(documents) info = topic_model.get_topic_info() # Topic -1 is the outlier bucket ``` ## Formulas type: concepts from: phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf/docs/en.md, phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md, phases/05-nlp-foundations-to-advanced/15-topic-modeling/docs/en.md - **TF-IDF**: $\frac{\mathrm{count}(w \text{ in } d)}{|d|} \cdot \log\frac{N}{\mathrm{df}(w)}$ - **Smoothed IDF**: $\log\frac{n + 1}{d + 1} + 1$. A word in every doc keeps IDF 1. - **L2 row**: divide each row by its norm. Long docs stop dominating. - **Naive Bayes**: log prior plus the sum of log $P(\text{word} \mid \text{class})$. Take the max. - **Laplace**: $(\mathrm{count} + \alpha) / (\mathrm{total} + \alpha |V|)$. No zero probabilities. - **Logistic regression**: sigmoid of $Xw + b$. Predict 1 at 0.5 or more. - **Macro-F1**: mean of per-class F1, equally weighted. - **c_v coherence**: NPMI of top-word pairs, compared by cosine. Higher is better. ## Defaults type: defaults from: phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md, phases/05-nlp-foundations-to-advanced/15-topic-modeling/docs/en.md, phases/05-nlp-foundations-to-advanced/15-topic-modeling/code/main.py | Setting | Default | Note | |---|---|---| | NB `alpha` | 1.0 | Teaching default. 0.01 is common in practice | | LR `l2` | 0.01 | Start here and tune. Sparse features overfit | | LR `epochs`, `lr` | 500 | Learning rate 0.05 | | LR `C` | 1.0 | With `max_iter=1000` in scikit-learn | | BERTopic `min_topic_size` | 10 | Use 50 or 100 over 10,000 docs | | Gibbs `alpha`, `beta` | 0.1 | Beta is 0.01. Both must be positive | | Gibbs `n_iters` | 200 | The demo runs 300 | | Topic count | `max(5, round(sqrt(n_docs)))` | Clamp to 200 under 40,000 docs | ## Troubleshooting type: troubleshooting from: phases/05-nlp-foundations-to-advanced/01-text-processing/docs/en.md, phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf/docs/en.md, phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md, phases/05-nlp-foundations-to-advanced/15-topic-modeling/docs/en.md | Symptom | Cause | Fix | |---|---|---| | Accuracy drops after deploy | Training and inference preprocess differ | Ship one preprocess function in the model | | Tokens change after an upgrade | Library version drift | Pin versions. Freeze 20 sample sentences | | `ponies` stems to `poni` | Step 1a `ies` rule only | Use full Porter with step 1b | | `watched` stays `watched` | Lemma table has no `ed` rule | Use WordNet or spaCy | | "not good" reads positive | Stopword list drops negations | `stop_words=None` plus bigrams | | High score, bad minority class | Micro-F1 or accuracy on imbalance | Report macro-F1, AUPRC over 95-5 | | LDA junk topics | Stopwords and frequent terms | Set `min_df` and `max_df` | | Many docs in topic -1 | HDBSCAN outlier bucket | Check `min_topic_size`, read the -1 docs | ## Use X when type: qa from: phases/05-nlp-foundations-to-advanced/01-text-processing/docs/en.md, phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf/docs/en.md, phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md, phases/05-nlp-foundations-to-advanced/15-topic-modeling/docs/en.md - **Stem or lemmatize?** Stem for search indexing. Lemmatize for text the user reads. - **NLTK or spaCy?** NLTK to teach and swap parts. spaCy for production speed. - **TF-IDF or embeddings?** TF-IDF for spam, logs, low data and explainable models. - **When a transformer?** Sarcasm, aspects, sentiment shifts and low-resource languages. - **LDA or BERTopic?** LDA for long docs with topic mixtures. BERTopic for short text. - **No GPU?** Use LDA or NMF. ## Tips type: tips from: phases/05-nlp-foundations-to-advanced/02-bag-of-words-tfidf/docs/en.md, phases/05-nlp-foundations-to-advanced/05-sentiment-analysis/docs/en.md, phases/05-nlp-foundations-to-advanced/15-topic-modeling/docs/en.md - Stay sparse. Dense TF-IDF for 100k documents does not fit in memory. - Read 5 wrong predictions per class before you trust a metric. - Regenerate the baseline on your own pipeline, not the paper's number.