Machine Learning Academy · Les

Bag of Words: CountVectorizer en TfidfVectorizer

U tokeniseert tekst, bouwt een vocabulaire, zet documenten om in countvectoren en past TF-IDF-weging toe om veelvoorkomende woorden minder zwaar te laten meetellen.

Les 2 van 413 stappen

Bag of Words: CountVectorizer en TfidfVectorizer is een gratis Machine Learning Academy-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Machine Learning Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Machine Learning Academy bevat in totaal 4 lessen.

Het probleem van tekst naar getallen

Modellen voor machinaal leren hebben numerieke invoer nodig, maar tekst is van nature ongestructureerd. Om tekstdocumenten om te zetten naar een indeling die een model kan begrijpen, is een systematische aanpak nodig. Het Bag-of-Words-model (BoW) is de eenvoudigste en meest gebruikte methode: het behandelt een document als een ongeordende verzameling woorden, waarbij grammatica en woordvolgorde worden genegeerd, en telt hoe vaak elk woord voorkomt. Het resultaat is een numerieke vector: één getal per woord in de woordenschat. Ondanks zijn eenvoud is BoW verrassend effectief voor tekstclassificatie, spamfiltering en sentimentanalyse.

# Bag of Words: ignore order, just count words
doc1 = 'the cat sat on the mat'
doc2 = 'the cat ate the rat'

# Vocabulary: all unique words across documents
vocab = sorted(set(doc1.split() + doc2.split()))
print('Vocabulary:', vocab)

# Count vectors
vec1 = [doc1.split().count(w) for w in vocab]
vec2 = [doc2.split().count(w) for w in vocab]
print('doc1 vector:', vec1)
print('doc2 vector:', vec2)

CountVectorizer: de woordenschat opbouwen

CountVectorizer van Scikit-learn automatiseert het Bag-of-Words-proces. Het: (1) tokeniseert elk document door te splitsen op witruimte en leestekens, (2) bouwt een woordenschat op uit alle unieke tokens die tijdens fit() worden aangetroffen, en (3) zet elk document om in een ijle vector met woordfrequenties. Het resultaat is een document-termmatrix waarin de rijen documenten zijn en de kolommen woorden uit de woordenschat. Er wordt een ijle indeling gebruikt omdat de meeste woorden slechts in een klein deel van de documenten voorkomen: de meeste waarden zijn nul.

from sklearn.feature_extraction.text import CountVectorizer

corpus = [
    'I love Python and machine learning',
    'Machine learning is awesome',
    'Python is great for data science',
    'I hate bugs in Python code'
]

vec = CountVectorizer()
X = vec.fit_transform(corpus)  # Returns sparse matrix

print('Vocabulary size:', len(vec.vocabulary_))
print('Matrix shape:', X.shape)  # (4 docs, N vocabulary words)
print('Vocabulary:', sorted(vec.vocabulary_.keys()))

IJle matrices en geheugenefficiëntie

CountVectorizer retourneert een ijle matrix, omdat de meeste woordfrequenties nul zijn: een document over Python vermeldt niet 'olifant' of 'melkwegstelsel'. Alle nullen opslaan zou enorm veel geheugen verspillen. Een ijle matrix slaat alleen de waarden die niet nul zijn en hun posities op. Voor een woordenschat van 100.000 woorden en 10.000 documenten zou de volledige matrix 8 GB groot zijn; de ijle versie misschien slechts 50 MB. Houd matrices met tekstkenmerken altijd in een ijle indeling totdat u zeker weet dat uw verdere bewerkingen dit ondersteunen. Een omzetting naar een dichte matrix kan bij grote tekstverzamelingen al het geheugen verbruiken.

from sklearn.feature_extraction.text import CountVectorizer
from scipy.sparse import issparse
import numpy as np

corpus = ['Python is great', 'Machine learning rocks', 'Data science rules']
vec = CountVectorizer()
X_sparse = vec.fit_transform(corpus)

print('Is sparse:', issparse(X_sparse))        # True
print('Shape:', X_sparse.shape)
print('Non-zero entries:', X_sparse.nnz)       # Only non-zero values stored
print('Density:', X_sparse.nnz / np.prod(X_sparse.shape))  # Very low

# Dense (RAM-expensive for large corpora)
X_dense = X_sparse.toarray()
print('Dense shape:', X_dense.shape)

Voorbewerking: stopwoorden, kleine letters en n-grammen

CountVectorizer biedt ingebouwde opties voor tekstvoorbewerking. lowercase=True (standaard) zorgt ervoor dat 'Python' en 'python' hetzelfde token zijn. stop_words='english' verwijdert veelvoorkomende woorden zoals 'the', 'is' en 'a' die weinig betekenis bevatten. ngram_range=(1,2) neemt zowel afzonderlijke woorden (unigrammen) als opeenvolgende woordparen (bigrammen) op, waardoor uitdrukkingen zoals 'not good' worden vastgelegd. Een model met alleen unigrammen zou die afzonderlijk als 'not' en 'good' verkeerd kunnen interpreteren. Bigrammen verbeteren de classificatiekwaliteit aanzienlijk bij sentimentanalyse, waarbij ontkenning belangrijk is.

from sklearn.feature_extraction.text import CountVectorizer

corpus = ['not good at all', 'very good movie', 'bad experience']

# Unigrams only (default)
uni_vec = CountVectorizer(stop_words='english')
print('Unigram features:', uni_vec.fit(corpus).get_feature_names_out())

# Bigrams too
bigram_vec = CountVectorizer(ngram_range=(1,2), stop_words='english')
bigram_features = bigram_vec.fit(corpus).get_feature_names_out()
print('Unigram+Bigram features:', bigram_features)
# 'not good' appears as a bigram -- captures negation

Het probleem met onbewerkte frequenties: veelvoorkomende woorden

Onbewerkte woordfrequenties hebben een fundamenteel probleem: zeer veelvoorkomende woorden domineren de vector, ook al bevatten ze weinig onderscheidende informatie. Het woord 'the' kan 50 keer in een nieuwsartikel voorkomen, maar vertelt u niets over het onderwerp van het artikel. Een zeldzame technische term zoals 'eigenvalue' die slechts twee keer voorkomt, wijst daarentegen sterk op een document over wiskunde. TF-IDF (Term Frequency-Inverse Document Frequency) lost dit op door woorden die in veel documenten voorkomen minder zwaar te wegen en woorden die in weinig documenten maar vaak in het huidige document voorkomen zwaarder te wegen.

from sklearn.feature_extraction.text import CountVectorizer
import numpy as np

corpus = [
    'the cat sat on the mat the cat is fat',
    'the eigenvalue decomposition is powerful math'
]

vec = CountVectorizer()
X = vec.fit_transform(corpus).toarray()
features = vec.get_feature_names_out()

for i, doc in enumerate(corpus[:1]):
    counts = sorted(zip(features, X[i]), key=lambda x: -x[1])
    print('Top words by count in doc 1:')
    for word, count in counts[:5]:
        print(f'  {word}: {count}')
# 'the' dominates even though it is meaningless

TF-IDF: inverse documentfrequentie van termfrequentie

TF-IDF past het belang van woorden aan de hand van twee factoren aan: Term Frequency (TF) — hoe vaak het woord in dit document voorkomt (lokaal belangrijk), en Inverse Document Frequency (IDF) — de logaritme van de verhouding tussen het totale aantal documenten en het aantal documenten dat het woord bevat (globaal zeldzaam = onderscheidender). TF-IDF(w, d) = TF(w,d) * IDF(w), waarbij IDF(w) = log((N+1)/(df+1)) + 1 (scikit-learn gebruikt een afgevlakte IDF). Een woord dat in slechts 1 van 1000 documenten voorkomt, heeft een zeer hoge IDF; een woord dat in elk document voorkomt, heeft een IDF dicht bij 0.

import numpy as np

# Manual TF-IDF
documents = [
    'cat sat mat cat',   # 'cat' appears twice here
    'dog ran park',
    'cat dog park'
]

N = len(documents)  # 3 documents

# IDF for 'cat': appears in documents 0 and 2 (df=2)
df_cat = 2
idf_cat = np.log((N + 1) / (df_cat + 1)) + 1

# TF for 'cat' in doc0: 2 out of 4 words
tf_cat_doc0 = 2 / 4

tfidf_cat_doc0 = tf_cat_doc0 * idf_cat
print(f'TF-IDF(cat, doc0) = {tfidf_cat_doc0:.4f}')

TfidfVectorizer: tekst in één stap transformeren

TfidfVectorizer combineert tokenisatie, vectorisatie van frequenties en TF-IDF-weging in één transformer. Standaard past het L2-normalisatie toe, zodat elke documentvector eenheidslengte heeft. Daardoor kunnen documenten met verschillende lengtes met elkaar worden vergeleken. Het resultaat is nog steeds een ijle matrix. TfidfVectorizer presteert bij tekstclassificatietaken bijna altijd beter dan CountVectorizer, vooral wanneer de tekstverzameling documenten van verschillende lengtes bevat of wanneer veelvoorkomende stopwoorden niet expliciet zijn verwijderd.

from sklearn.feature_extraction.text import TfidfVectorizer

corpus = [
    'I love Python and machine learning',
    'Machine learning is awesome',
    'Python is great for data science',
    'I hate bugs in Python code'
]

tfidf = TfidfVectorizer(stop_words='english', max_features=20)
X = tfidf.fit_transform(corpus)

print('Shape:', X.shape)
print('Features:', tfidf.get_feature_names_out())

# Inspect TF-IDF weights for document 0
import numpy as np
weights = zip(tfidf.get_feature_names_out(), X.toarray()[0])
for word, weight in sorted(weights, key=lambda x: -x[1])[:5]:
    print(f'  {word}: {weight:.4f}')

Parameters max_features en vocabulary

Bij grote tekstverzamelingen met miljoenen documenten kan de woordenschat groeien tot honderdduizenden termen. max_features=N behoudt alleen de N meest frequente woorden, waardoor het geheugengebruik afneemt en de training sneller verloopt. min_df negeert woorden die in minder documenten voorkomen dan deze waarde (en verwijdert zeer zeldzame termen die typefouten of unieke identificatiegegevens kunnen zijn). max_df negeert woorden die in meer dan dit deel van de documenten voorkomen (en verwijdert feitelijke stopwoorden die overal voorkomen). Gebruikelijke instellingen voor productie: max_features=50,000, min_df=5, max_df=0.95.

from sklearn.feature_extraction.text import TfidfVectorizer

corpus = ['...']  # assume large corpus

tfidf = TfidfVectorizer(
    max_features=50000,   # Top 50k words by frequency
    min_df=5,            # Ignore words in fewer than 5 documents
    max_df=0.95,         # Ignore words in more than 95% of docs
    ngram_range=(1, 2),  # Include bigrams
    stop_words='english',
    sublinear_tf=True    # Replace TF with 1+log(TF) to dampen outliers
)

print('CountVectorizer vs TfidfVectorizer settings configured')
print('sublinear_tf=True: dampens high-frequency words further')

CountVectorizer versus TfidfVectorizer: wanneer gebruikt u welke

Gebruik CountVectorizer wanneer: (1) uw model de weging van frequenties al intern afhandelt (Multinomial Naive Bayes verwacht bijvoorbeeld onbewerkte frequenties), of (2) de documentlengte uniform is en verschillen in frequentie betekenisvol zijn. Gebruik TfidfVectorizer wanneer: (1) documenten verschillende lengtes hebben, (2) veelvoorkomende woorden niet met stopwoorden worden gefilterd, of (3) u algoritmen gebruikt die uitgaan van genormaliseerde vectoren met kenmerken (SVM, logistische regressie). Specifiek voor Naive Bayes werkt CountVectorizer met MultinomialNB vaak het best. Voor SVM of logistische regressie is TfidfVectorizer de standaardkeuze.

from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score

corpus = ['spam message buy now', 'hello friend how are you',
          'click here to buy', 'good morning everyone']
labels = [1, 0, 1, 0]  # 1=spam, 0=ham

# Naive Bayes: works well with CountVectorizer
nb_pipe = Pipeline([('vec', CountVectorizer()), ('clf', MultinomialNB())])

# Logistic regression: works better with TF-IDF
lr_pipe = Pipeline([('vec', TfidfVectorizer()), ('clf', LogisticRegression())])

print('CountVectorizer + MultinomialNB: standard for text Naive Bayes')
print('TfidfVectorizer + LogisticRegression: standard for text linear models')

De document-termmatrix bekijken

Na de vectorisatie kunt u de document-termmatrix bekijken om te begrijpen wat er is geleerd. Door deze om te zetten naar een Pandas DataFrame met namen van kenmerken als kolomkoppen, ziet u eenvoudig hoe elk woord wordt weergegeven. Deze foutopsporingsstap is essentieel: u ziet of de tokenisatie correct werkte, of stopwoorden zijn verwijderd, of de woordenschat de verwachte termen bevat en of de TF-IDF-gewichten logisch lijken. Bekijk altijd een voorbeeld van de matrix voordat u uw model traint, zodat u fouten in de voorbewerking vroeg ontdekt.

from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

corpus = [
    'Python machine learning tutorial',
    'Deep learning neural networks',
    'Python data analysis pandas numpy'
]

tfidf = TfidfVectorizer()
X = tfidf.fit_transform(corpus)

# Convert to DataFrame for inspection
df = pd.DataFrame(
    X.toarray(),
    columns=tfidf.get_feature_names_out()
)

print('Document-Term Matrix (TF-IDF weights):')
print(df.to_string())

N-grammen op tekenniveau voor robuuste tokenisatie

Tokenisatie op woordniveau werkt niet goed wanneer tekst typefouten, afkortingen of morfologische varianten bevat. N-grammen op tekenniveau behandelen overlappende reeksen tekens als kenmerken in plaats van volledige woorden. Als u analyzer='char_wb' instelt in TfidfVectorizer met ngram_range=(3,5), worden substrings van 3 tot 5 tekens met woordgrenzen gegenereerd. Hierdoor is het model beter bestand tegen spelfouten en meertalige tekst. Dit is vooral nuttig voor taaldetectie, auteurschapsanalyse en tekst van sociale media, waarin de spelling niet consistent is. N-grammen op tekenniveau vormen ook de basis voor subwoordtokenisatie die wordt gebruikt in transformermodellen zoals BERT.

from sklearn.feature_extraction.text import TfidfVectorizer

corpus = [
    'python programming language',
    'pythn programmng (typo)',  # Misspellings
    'java programming'
]

# Word-level (default): typos create unseen tokens
word_vec = TfidfVectorizer(analyzer='word')
word_features = word_vec.fit(corpus).get_feature_names_out()
print('Word features:', word_features)

# Character n-gram: handles typos gracefully
char_vec = TfidfVectorizer(analyzer='char_wb', ngram_range=(3, 4))
X_char = char_vec.fit_transform(corpus)
print('Char n-gram features:', len(char_vec.get_feature_names_out()), 'total')
print('Typos share substrings with correct words -> similar vectors')

Korte controle

Test je begrip van de concepten voor Machine Learning met Python uit deze les.

Samenvatting van de les

In deze les heb je geleerd: Bag of Words zet tekst om in vectoren met woordfrequenties met CountVectorizer, TF-IDF verlaagt het gewicht van veelvoorkomende woorden en verhoogt het gewicht van onderscheidende zeldzame woorden met TfidfVectorizer, en wat de belangrijkste parameters voor voorbewerking zijn, waaronder stop_words, ngram_range, max_features en min_df. Hierna trainen we een Multinomial Naive Bayes-classificeerder op tekstgegevens.

Gratis beginnen

Leer Python met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
30
Lessen
120

Veelgestelde vragen

Is de les “Bag of Words: CountVectorizer en TfidfVectorizer” gratis?

Ja — de volledige tekst van “Bag of Words: CountVectorizer en TfidfVectorizer” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Machine Learning Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Machine Learning Academy bevat in totaal 4 lessen.

Wat leer ik in “Bag of Words: CountVectorizer en TfidfVectorizer”?

U tokeniseert tekst, bouwt een vocabulaire, zet documenten om in countvectoren en past TF-IDF-weging toe om veelvoorkomende woorden minder zwaar te laten meetellen. Je oefent met Machine Learning Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Machine Learning Academy te beginnen?

Ervaring vooraf is niet nodig. Machine Learning Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Bag of Words: CountVectorizer en TfidfVectorizer”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Machine Learning Academy?

Ja. Elke les over Machine Learning Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. De stelling van Bayes in begrijpelijke taal
  2. Bag of Words: CountVectorizer en TfidfVectorizer
  3. Een Multinomial Naive Bayes-classifier trainen
  4. Laplace-smoothing en het probleem van nulwaarschijnlijkheden
← Terug naar Machine Learning Academy