Phase 05: NLP: Foundations to Advanced

Thùng từ, TF-IDF và biểu diễn văn bản

TF-IDF vẫn đánh bại các nhiệm vụ được xác định rõ ràng vào năm 2026.

Type: Build

Languages: Python

Prerequisites: Phase 5 · 01 (Text Processing), Phase 2 · 02 (Linear Regression from Scratch)

Time: ~75 minutes

Vấn đề

Mô hình cần số, anh có dây.

Mỗi đường ống NLP phải trả lời cùng một câu hỏi. Làm thế nào để biến một dòng token dài biến thành một vector kích thước cố định mà một trình phân loại có thể tiêu thụ. Câu trả lời đầu tiên mà trường đáp lại là câu trả lời ngu ngốc nhất.

Dòng vector đó đã mang lại nhiều NLP sản xuất hơn bất kỳ mô hình nhúng nào. Trình lọc spam, phân loại chủ đề, phát hiện bất thường nhật ký, xếp hạng tìm kiếm (trước BM25), làn sóng đầu tiên của phân tích cảm xúc, thập kỷ đầu tiên của các tiêu chuẩn NLP học thuật. 2026 các học viên vẫn tiếp cận nó trước tiên trong các nhiệm vụ phân loại hẹp. Nó nhanh chóng, có thể giải thích và thường không thể phân biệt với mô hình tích hợp các tham số 400M trong các nhiệm vụ mà sự hiện diện của từ ngữ là điều quan trọng.

Bài học này xây dựng túi từ, sau đó là TF-IDF, từ đầu. sau đó cho thấy scikit-learn làm điều tương tự trong ba dòng. sau đó đặt tên chế độ thất bại khiến bạn tìm kiếm các bản nhúng.

Khái niệm

Bag of Words (BoW)cho mỗi tài liệu, đếm bao nhiêu lần mỗi từ vựng xuất hiện. chiều dài vector là kích thước từ vựng. vị trí ilà số từ i- Tôi không biết.

TF-IDFMột từ xuất hiện trong mọi tài liệu là không thông tin, vì vậy hãy giảm nó. Một từ hiếm trên toàn bộ bộ các tập hợp nhưng thường xuyên trong một tài liệu là tín hiệu, vì vậy hãy tăng nó.

TF-IDF(w, d) = TF(w, d) * IDF(w)
             = count(w in d) / |d| * log(N / df(w))

Ở đâu TFlà tần số thuật ngữ trong tài liệu, dflà tần số tài liệu (nhiều tài liệu bao nhiêu chứa từ),Nlà tổng số tài liệu.loggiữ trọng lượng giới hạn cho các từ ở khắp mọi nơi.

Cấu trúc: cả hai tạo ra các vector hiếm có với trục giải thích. Bạn có thể xem trọng lượng của một phân loại được đào tạo và đọc những từ đẩy một tài liệu hướng tới mỗi lớp. Bạn không thể làm điều này với một nhúng BERT 768 chiều.

Hãy xây dựng nó

Bước 1: xây dựng từ vựng

pythondef build_vocab(docs):
    vocab = {}
    for doc in docs:
        for token in doc:
            if token not in vocab:
                vocab[token] = len(vocab)
    return vocab

Nhập: danh sách các tài liệu được token hóa (bất kỳ token level nào sẽ làm được; code/main.pytrong bài học này sử dụng một biến thể chữ nhỏ đơn giản).{word: index}Thiết lập lệnh nhập ổn định nghĩa là chữ index 0 là từ đầu tiên được nhìn thấy trong tài liệu đầu tiên.

Bước 2: túi từ

pythondef bag_of_words(docs, vocab):
    matrix = [[0] * len(vocab) for _ in docs]
    for i, doc in enumerate(docs):
        for token in doc:
            if token in vocab:
                matrix[i][vocab[token]] += 1
    return matrix
python>>> docs = [["cat", "sat", "on", "mat"], ["cat", "cat", "ran"]]
>>> vocab = build_vocab(docs)
>>> bag_of_words(docs, vocab)
[[1, 1, 1, 1, 0], [2, 0, 0, 0, 1]]

Các hàng là tài liệu, cột là chỉ số từ vựng.[i][j]là "tôi bao nhiêu lần từ jxuất hiện trong tài liệu i. " Doc 1 đã có cat2 lần vì nó đã làm.rankhông lần vì nó không làm.

Bước 3: Tần suất thuật ngữ và tần suất tài liệu

pythonimport math


def term_frequency(doc_bow, doc_length):
    return [c / doc_length if doc_length else 0 for c in doc_bow]


def document_frequency(bow_matrix):
    df = [0] * len(bow_matrix[0])
    for row in bow_matrix:
        for j, count in enumerate(row):
            if count > 0:
                df[j] += 1
    return df


def inverse_document_frequency(df, n_docs):
    return [math.log((n_docs + 1) / (d + 1)) + 1 for d in df]

Hai thủ thuật làm trơn đáng để đặt tên.(n+1)/(d+1)tránh log(x/0)- Đường sau+1đảm bảo một từ trong mỗi tài liệu vẫn có IDF 1 (không phải 0), phù hợp với mặc định của scikit-learn.log(N/df)Cả hai đều hoạt động, phiên bản trơn hơn là thân thiện hơn.

Bước 4: TF-IDF

pythondef tfidf(bow_matrix):
    n_docs = len(bow_matrix)
    df = document_frequency(bow_matrix)
    idf = inverse_document_frequency(df, n_docs)
    out = []
    for row in bow_matrix:
        length = sum(row)
        tf = term_frequency(row, length)
        out.append([tf_j * idf_j for tf_j, idf_j in zip(tf, idf)])
    return out
python>>> docs = [
...     ["the", "cat", "sat"],
...     ["the", "dog", "sat"],
...     ["the", "cat", "ran"],
... ]
>>> vocab = build_vocab(docs)
>>> bow = bag_of_words(docs, vocab)
>>> tfidf(bow)

Ba tài liệu, năm từ ngữ (the- cat- sat- dog- ran ).thexuất hiện trong cả ba, vì vậy IDF của nó là thấp. dogxuất hiện trong một, do đó IDF của nó là cao. Các vector là hiếm (những mục nhập lớn nhất là nhỏ) và các từ phân biệt đối xử pop.

Bước 5: L2- bình thường hóa hàng

pythondef l2_normalize(matrix):
    out = []
    for row in matrix:
        norm = math.sqrt(sum(x * x for x in row))
        out.append([x / norm if norm else 0 for x in row])
    return out

Nếu không có bình thường hóa, một tài liệu dài hơn sẽ có một vector lớn hơn và thống trị điểm tương đồng. bình thường hóa L2 đặt mọi tài liệu trên siêu cầu đơn vị. Sự tương đồng cosine giữa các hàng bây giờ chỉ là một sản phẩm điểm.

Sử dụng nó

Scikit-learn sẽ đưa ra phiên bản sản xuất.

pythonfrom sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer

docs = ["the cat sat on the mat", "the dog sat on the mat", "the cat ran"]

bow_vectorizer = CountVectorizer()
bow = bow_vectorizer.fit_transform(docs)
print(bow_vectorizer.get_feature_names_out())
print(bow.toarray())

tfidf_vectorizer = TfidfVectorizer()
tfidf = tfidf_vectorizer.fit_transform(docs)
print(tfidf.toarray().round(3))

CountVectorizerlàm tokenization, từ vựng, và BoW trong một cuộc gọi. TfidfVectorizerThêm trọng lượng IDF và bình thường hóa L2. Cả hai đều trả lại các matrix mỏng. Đối với 100k tài liệu, phiên bản dày đặc không phù hợp với bộ nhớ; giữ mỏng cho đến khi phân loại yêu cầu dày đặc.

Những nút mà thay đổi mọi thứ:

ArgEffect
ngram_range=(1, 2)Include bigrams. Usually boosts classification.
min_df=2Drop words in fewer than 2 docs. Trims vocabulary on noisy data.
max_df=0.95Drop words in more than 95% of docs. Approximates stopword removal without a hardcoded list.
stop_words="english"scikit-learn's builtin stopword list. Task-dependent — sentiment analysis should not drop negations.
sublinear_tf=TrueUse 1 + log(tf) instead of raw tf. Helps when a term repeats many times in one doc.

Khi TF-IDF vẫn thắng (từ 2026)

  • Phát hiện spam, đánh dấu chủ đề, đánh dấu bất thường nhật ký.
  • Các chế độ dữ liệu thấp (chỉ trăm ví dụ được dán nhãn). TF-IDF cộng với sự lùi hậu cần không có chi phí trước khi đào tạo.
  • Bất cứ nơi nào thời gian trễ quan trọng. TF-IDF cộng với mô hình tuyến tính trả lời trong microsecond. Nhập một tài liệu thông qua một biến thể mất 10-100ms.
  • Hệ thống phải giải thích dự đoán của họ kiểm tra các hệ số phân loại từ tích cực cao là lý do.

Khi TF-IDF thất bại

Sự thất bại về mù ngữ học. Hãy xem xét hai tài liệu này:

  • "Trong phim không tốt cả".
  • "Tác phẩm phim rất tuyệt vời".

Một là một đánh giá tiêu cực, một là tích cực, sự chồng chéo giữa TF và IDF của họ chính xác{the, movie, was}Một người phân loại từ phải ghi nhớ từ đónotgần goodNó có thể học được điều này với đủ dữ liệu, nhưng không bao giờ như một mô hình hiểu tổng hợp.

Sự thất bại khác: từ ngoài từ vựng khi suy luận. Một mô hình BoW được đào tạo trên đánh giá IMDb không biết phải làm gì với Zoomer-approvedNếu token đó không xuất hiện trong đào tạo. Subword Embeddings (đọc 04) xử lý điều này. TF-IDF không thể.

Hybrid: Thiết bị nhúng trọng TF-IDF

Các mặc định thực tế năm 2026 cho phân loại dữ liệu trung bình: sử dụng trọng lượng TF-IDF như sự chú ý hơn so với việc nhúng từ.

pythondef tfidf_weighted_embedding(doc, tfidf_scores, embedding_table, dim):
    vec = [0.0] * dim
    total_weight = 0.0
    for token in doc:
        if token not in embedding_table or token not in tfidf_scores:
            continue
        weight = tfidf_scores[token]
        emb = embedding_table[token]
        for i in range(dim):
            vec[i] += weight * emb[i]
        total_weight += weight
    if total_weight == 0:
        return vec
    return [v / total_weight for v in vec]

Bạn có được khả năng ngữ nghĩa từ nhúng, và nhấn mạnh từ hiếm từ từ TF-IDF. Classifier đào tạo trên vector tập hợp. Điều này vượt qua một mình cho tình cảm, chủ đề và định mệnh phân loại dưới khoảng 50k ví dụ được dán nhãn.

Chuyển nó

Cứ như outputs/prompt-vectorization-picker.md- Có thể là:

markdown---
name: vectorization-picker
description: Given a text-classification task, recommend BoW, TF-IDF, embeddings, or a hybrid.
phase: 5
lesson: 02
---

You recommend a text-vectorization strategy. Given a task description, output:

1. Representation (BoW, TF-IDF, transformer embeddings, or a hybrid). Explain why in one sentence.
2. Specific vectorizer configuration. Name the library. Quote the arguments (`ngram_range`, `min_df`, `max_df`, `sublinear_tf`, `stop_words`).
3. One failure mode to test before shipping.

Refuse to recommend embeddings when the user has under 500 labeled examples unless they show evidence of semantic failure in a TF-IDF baseline. Refuse to remove stopwords for sentiment analysis (negations carry signal). Flag class imbalance as needing more than a vectorizer change.

Example input: "Classifying 30k customer support tickets into 12 categories. Most tickets are 2-3 sentences. English only. Need explainability for audit logs."

Example output:

- Representation: TF-IDF. 30k examples is not small; explainability requirement rules out dense embeddings.
- Config: `TfidfVectorizer(ngram_range=(1, 2), min_df=3, max_df=0.95, sublinear_tf=True, stop_words=None)`. Keep stopwords because category keywords sometimes are stopwords ("not working" vs "working").
- Failure to test: verify `min_df=3` does not drop rare category keywords. Run `get_feature_names_out` filtered by class and eyeball.

Các bài tập

  1. Easy.Thực hiệncosine_similarity(doc_vec_a, doc_vec_b)trên các output TF-IDF chuẩn hóa L2. Kiểm tra rằng các tài liệu giống hệt có điểm số 1.0 và các tài liệu từ vựng không liên kết có điểm số 0.0.
  2. Medium.Thêm n-gramhỗ trợbag_of_words- Parameterntạo ra số lượng hơn n- Thử thử.n=2["the", "cat", "sat"]tạo ra số lượng lớn cho ["the cat", "cat sat"]- Tôi không biết.
  3. Hard.Xây dựng bộ lắp ráp hợp chất TF-IDF cân nặng trên bằng cách sử dụng các vector GloVe 100d (tải xuống một lần, cache). So sánh độ chính xác phân loại với TF-IDF đơn giản và các bản lắp ráp trung bình đơn giản trên bộ dữ liệu 20 Newsgroups.

Các điều khoản chính

TermWhat people sayWhat it actually means
BoWWord frequency vectorCounts of vocabulary words in one document. Throws away order.
TFTerm frequencyCount of a word in a document, optionally normalized by document length.
DFDocument frequencyCount of documents containing the word at least once.
IDFInverse document frequencylog(N / df) smoothed. Downweights words that appear everywhere.
Sparse vectorMostly zerosVocabulary is typically 10k-100k words; most are absent from any given document.
Cosine similarityVector angleDot product of L2-normalized vectors. 1 is identical, 0 is orthogonal.

Đọc thêm

This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.

Browse the complete course catalog or open this lesson on GitHub.