Concepts / Text Representation with Bag-of-Words

Text Representation with Bag-of-Words

CountVectorizer from scikit-learn converts a collection of text documents into a matrix of token counts, which the source calls a bag-of-words representation.

  • Programming

Predicting What the Output Shows

Machine learning algorithms work with numbers, not raw sentences. A sentence such as a line from a poem cannot be fed directly into a learning algorithm, so something has to convert the text into numbers first. This article shows one practical way to do that using the CountVectorizer tool from the scikit-learn library. CountVectorizer converts a collection of text documents into a matrix of token counts, and the source calls that matrix a bag-of-words representation. Each document becomes a row of counts, so the text can be processed numerically.

What do you think happens?

When you inspect the bag-of-words result, does its printed repr line show you the individual words and their counts?

  • Yes, it lists every word and how many times it appears

  • No, it shows only the shape of the matrix and that it is sparse

Reveal answer
Answer:

No, it shows only the shape of the matrix and that it is sparse

The repr line reports a sparse matrix with shape 2 by 13. It does not show the words or the individual counts, so you must look at the matrix contents or the vocabulary to see them.

Why Text Must Become Numbers

A learning algorithm needs each input as a set of numbers in a consistent layout. Text does not come in that layout: two documents can be different lengths and use different words. A bag-of-words representation solves this by fixing a list of distinct words across the whole collection, then recording, for each document, how many times each word appears. Every document is then described by the same set of columns, which is what makes the rows comparable.

Key point

The result of CountVectorizer is a matrix of token counts. Each row is one document, and each column is one word from the collection. The word 'bag' in the name is a useful reminder: the representation keeps counts and throws away the arrangement of words inside each document.

Tracing the Four-Line Workflow

The source program works on a variable named bards_words, which holds the collection of text documents used throughout the source. The program has four lines, and together they are the whole workflow. Read the code first, then trace what each line is responsible for before you look at the final printed result.

python
Notes on the code
  • Line 1 imports CountVectorizer from the feature_extraction.text module of scikit-learn
  • Line 2 creates an instance named vect
  • Line 3 fits vect to the documents so it learns which words it will count
  • Line 4 transforms the same documents into the bag-of-words result, which is assigned to a variable
Code walkthrough
Trace the four lines in order. Each line hands its result to the next line, so the trace follows the data from the raw collection to the final matrix.
  1. 1
    Line 1 the import makes the CountVectorizer class available. Nothing is computed about the documents yet.
  2. 2
    Line 2 vect is a new vectorizer object. It has not seen any documents, so it does not yet know any words.
  3. 3
    Line 3 fit examines bards_words and records the distinct words it finds. The vectorizer now knows its vocabulary, but this call does not return the count matrix.
  4. 4
    Line 4 transform uses the vocabulary learned in line 3 to turn bards_words into the matrix of counts, and the result is stored in bag_of_words.
  5. 5
    Evaluating the name bag_of_words on its own, for example in an interactive session, displays the repr line that reports the shape of the result.

Fit Learns, Transform Counts

The workflow has two stages. The fit stage examines the documents so the vectorizer knows the words it will count. The transform stage then uses that knowledge to turn the documents into the matrix of counts. Keeping these stages separate in your head makes the program much easier to debug, because a problem usually belongs to one stage or the other.

Document collectionbards_wordsfitlearns the wordsLearned vocabulary13 distinct wordstransformcounts the wordsBag-of-words matrixshape 2 by 13examined byproducesused bysame documentsreturns
Which call learns the vocabulary from the corpus, and which call uses that learned vocabulary to produce the count matrix?

Reading the 2 by 13 Shape

Read the shape $2 \times 13$ as two rows and thirteen columns. The two rows correspond to the two documents in the collection. The thirteen columns correspond to the thirteen distinct words the vectorizer found across the corpus. Each cell in the matrix therefore belongs to exactly one document (its row) and exactly one vocabulary term (its column).

Document 1, term 1one countDocument 2, term 1one countDocument 1, term 2one countDocument 2, term 2one countDocument 1, term 13one countDocument 2, term 13one count
Which axis of the output matrix corresponds to documents, and which to vocabulary words? How does each cell map to one document and one term?

The repr line gives the shape, but not the contents. It also tells you the matrix is sparse, which means most cells are expected to be zero for typical text. Because of that, the object stores only its nonzero entries rather than writing out every zero. The printed line therefore confirms the dimensions of the result without showing you which words occurred in which document.

Word order is also absent from this representation. Each row records how often each word occurs, not where it sits in the sentence. Two documents that contain the same words in a different order would therefore produce identical count rows. This follows directly from what a bag of words is: a collection of counts with the arrangement discarded.

Finding Where a Result Goes Wrong

Suppose you run the same program and the printed line reports a different shape from the one you predicted. Do not guess at the cause. Start at the top of the program and, at each line, ask what that line was supposed to produce. Then compare that expectation with what the next line receives. The first line where the expectation and the reality disagree is the place to look.

bards_wordscollection of documentsCreate vectno vocabulary yetvect.fitvocabulary learnedvect.transformcounts producedRepr shapecompare with prediction
At which step does the output first diverge from what you expected, and what state exists just before that step?
Worked example

Locating the divergence in a three-document run

You expected a bag-of-words result with 3 rows, one per sentence you supplied, but the repr line reports 2 rows. Find the step where the result first goes wrong.

  1. 1
    Check the input

    Count the documents in the collection passed to fit and transform. If the collection holds only 2 sentences, the problem starts before the vectorizer runs.

  2. 2
    Check the fit line

    fit learns the vocabulary from the documents it receives. It does not set the number of rows, so a row mismatch is not caused by fit.

  3. 3
    Check the transform line

    transform should return one row for each document it is given. If the collection passed to transform is the same as the one passed to fit and it has 3 sentences, the row count should be 3.

  4. 4
    Locate the first divergence

    The first line whose output disagrees with expectation is where to look. Here, if the collection itself has 2 sentences, the divergence is at the input, not in the vectorizer calls.

Result

The divergence is found by checking the input before the vectorizer. Only after the input is confirmed should you examine fit and transform.

Mistakes When Reading Bag-of-Words Output

  • Common mistake

    Expecting fit to return the count matrix

    What it looks like

    Writing result = vect.fit(bards_words) and then expecting result to hold counts.

    Why it fails

    fit examines the documents so the vectorizer knows the words it will count. The counts come from the transform stage.

    Do this instead

    Assign the output of transform to the variable, as in bag_of_words = vect.transform(bards_words).

  • Common mistake

    Reading the repr line as a list of words

    What it looks like

    Looking at the repr output for the names of the words it contains.

    Why it fails

    The repr line reports the sparse matrix shape, here 2 by 13, and does not show the words or the counts.

    Do this instead

    Read the first number as the number of documents and the second as the number of distinct vocabulary words, then inspect the matrix contents or the learned vocabulary to see the terms.

  • Common mistake

    Assuming the column count is the number of words in each document

    What it looks like

    Thinking 13 columns means each document contains 13 words.

    Why it fails

    The columns are the distinct words found across the whole corpus, not the word count of any single document.

    Do this instead

    Treat 13 as the size of the shared vocabulary for the entire collection.

  • Common mistake

    Guessing at the cause of a wrong shape

    What it looks like

    Changing the code at random when the printed shape differs from the prediction.

    Why it fails

    Random changes can hide the real problem and make it harder to find.

    Do this instead

    Trace from the top of the program and find the first line where the expected and actual values disagree, then check the input passed to that line.

  • Common mistake

    Forgetting that the same documents are passed to fit and transform, so a mismatch between the two calls can change the result

Practice: Predict the Row Count

EASY

Build a short list of two or three sentences of your own. Pass it to the same four-line workflow, and before you run it, predict the number of rows. Then predict the number of columns by counting the distinct words across the sentences. Compare your predictions with the printed shape. If they disagree, go back to the divergence check and find the first line responsible.

Hints
  • The row count should match the number of sentences you supplied.
  • The column count reflects the number of distinct words across all the sentences, not the number of words in one sentence.
  • If the shape differs from your prediction, check the input passed to fit and transform before looking at the vectorizer itself.

Key Takeaways

  • CountVectorizer converts a collection of text documents into a matrix of token counts, which is the bag-of-words representation, with one row per document.
  • fit learns the vocabulary from the documents; transform uses that vocabulary to produce the count matrix that is assigned to a variable.
  • The repr line reports a sparse matrix with shape 2 by 13: two rows for documents and thirteen columns for distinct corpus words, but not the words or the counts.
  • When a result is unexpected, trace the program from the top and locate the first line where expectation and reality disagree, then check that line's input.

Related concepts

More concepts starting with T