Concepts / Topic Modeling

Topic Modeling

Topic modeling is the task of assigning each document to one or several topics, usually without supervision.

  • Programming

An Archive Nobody Has Labeled

Imagine a newsroom archive with tens of thousands of articles and no one has ever tagged them. A reader who wants every article about the economy, or every article about a local election, has no easy way to find them. Text data like this is everywhere: customer reviews, support tickets, research abstracts, and social media posts. Most of it arrives as raw sentences with no category attached.

Topic modeling is a technique often applied to text for exactly this situation. Instead of asking a person to read everything and label it, the technique tries to discover what the documents are about. This article starts with a short pipeline that runs end to end, then explains each step and shows where the results can go wrong.

Documents, Topics, and Supervision

Definition

Topic modeling is the task of assigning each document to one or several topics, usually without supervision.

The phrase one or several topics is the key to the definition. The task does not force every document into a single bin. A document may touch more than one subject, and the method's job is to say which subjects are present. The phrase usually without supervision signals that the topics are not handed to the method in advance. They have to be found from the documents themselves.

In the real world

News data might be grouped into topics such as politics, sports, and finance. A newspaper editor would recognize these as the sections of the paper. Topic modeling aims to produce this kind of grouping from the text of the articles, rather than from a list of section names someone wrote down beforehand.

The method used most often for this task is Latent Dirichlet Allocation, usually abbreviated LDA. LDA discovers hidden topics in a body of text. The word hidden matters: nobody labels the topics in advance. The model proposes them from patterns of word use across documents, and the person running it has to read the output and decide what each topic means.

Running the Pipeline First

The lesson begins with running code rather than definitions. The pipeline takes a collection of text documents and returns two outputs: a per-document summary of which topics each document leans toward, and a per-topic summary of which words define each topic. Read the code once now, then follow each stage in the sections that come after it.

python
Notes on the code
  • CountVectorizer turns raw text into the numeric document-word table X
  • fit_transform fits the LDA model to X and returns the document-level output in one step
  • components_ holds the topic-word importance table produced by the fit
  • argsort and the reversed column slice put the most important word positions first in each row
Raw documentsplain sentencesCountVectorizerbuilds the vocabularyDocument-word table Xrows are documents, columnsare wordsFitted LDA modellearns the topicsdocument_topicsper-document topic summaryRanked word listsreadable words per topiccomponents_topic-word importance tablefit_transformproducesfit_transformreturnsstoresargsort and sort
What happens to raw text at each step, and what does each intermediate object look like before the model is fitted?

Follow the diagram from left to right. Each stage depends on the one before it. Documents become a count table, the count table is fitted to produce a document-level output and a topic-word table, and the topic-word table is sorted into readable lists. A problem at any early stage shows up later as a strange topic, so the chain is worth keeping in mind whenever results look wrong.

Turning Text into a Count Table

LDA does not read raw sentences. It needs a numeric table in which each row is a document and each column is a vocabulary word, and each cell holds how often that word appears in that document. scikit-learn's CountVectorizer builds this table. In the pipeline above, the vectorizer is created with two settings, and the call to fit_transform learns the vocabulary from the training texts and converts them into the table stored in X.

python
Notes on the code
  • max_features caps the vocabulary size so the table stays manageable
  • max_df drops words that occur in a large share of documents
  • fit_transform learns the vocabulary and builds X in one call

The two settings shape what the model is allowed to see. A cap on vocabulary size keeps the table manageable. Dropping words that appear in a large share of documents removes terms that show up almost everywhere and therefore say little about any particular subject. The values 1000 and 0.95 used here are illustrative choices, not fixed rules. X is the only thing LDA receives, so any choice made at this stage carries straight through to the topics.

Hyperparameters Chosen Before Fitting

Each keyword argument in the LDA call is a hyperparameter, meaning a setting chosen by the person before fitting rather than something the model learns from the data. The table below summarizes the settings used in the lesson pipeline and what each one changes.

SettingValue in the pipelineWhat it controls
n_components10How many topics the model produces; every topic changes when this changes
learning_methodbatchThe fitting method; the source notes batch is slower but usually gives better results than the faster online default
max_iter25How many passes the model makes over the data
random_statea fixed valueThe random starting point, so rerunning the code reproduces the same result

Hyperparameters in the lesson's LDA call

The call to fit_transform does two jobs at once. It fits the model to X and returns the document-level output stored in document_topics. Doing both in one step avoids the second pass that would be needed if you fitted first and transformed afterward.

How the Model Stores Its Topics

Before you look at topic words, it helps to know how the fitted model stores its topics. The model keeps a table called components_, which records how important each word is for each topic. Each cell answers a single question: how important is this word to this topic? A large value means the word carries a lot of weight in that topic, and a small value means it hardly matters.

What do you think happens?

Before reading further, predict the shape of components_ when the model has 10 topics and a vocabulary of many words.

Reveal answer
Answer:

(n_topics, n_words), so for 10 topics it has 10 rows, one per topic, and one column per vocabulary word.

Each row is a topic and each column is a word in the vocabulary built by CountVectorizer. The cell at a given row and column is the importance of that word to that topic.

The components_ table has the same role as the components in the matrix decomposition methods covered earlier in the course, so if you remember those, you already know the layout. The next diagram shows how a row of this table maps to a topic and how its values feed the ranked word list.

components_shape (n_topics, n_words)Row 0Topic 0 word weightsSorted positionsfrom argsort and reversedcolumnsRanked wordsmapped through featurenamesRow 1Topic 1 word weightsRow 2Topic 2 word weightsRow 9Topic 9 word weightsrowrowrowrowargsortfeature_names
How does each row of components_ map to a topic, and how do its numeric values map to a ranked list of words?

The rows are the topics. The columns are the words. Reading a topic means reading one row, and reading a word's role across topics means reading one column.

Ranking the Words in Each Topic

A row of numbers is hard to read, so the next step converts each row into a list of its strongest words. The approach is to sort each topic's word weights and then map the sorted positions back to vocabulary names. The critical detail is that np.argsort returns positions, not values. For a row, it answers the question: which word positions, in order from smallest weight to largest, make up this row?

Code walkthrough
sorting = np.argsort(lda.components_, axis=1)[:, ::-1]
for topic_idx, row in enumerate(sorting):
    top_words = [feature_names[i] for i in row[:10]]
    print(topic_idx, top_words)
  1. 1
    np.argsort with axis=1 sorts each row of components_ and returns the word positions in ascending order of weight
  2. 2
    [:, ::-1] reverses the columns, so the first entry of each row points to the most important word
  3. 3
    The loop goes through each topic row and maps its first ten positions to readable words through feature_names
  4. 4
    The topic index is printed only as a label; topic numbers carry no ranking
Worked example

Ranking one topic row by hand

A toy model has a vocabulary of four words in this order: goal, vote, budget, team. One topic's row of components_ holds the weights 0.1, 2.5, 0.4, 1.7. Produce the ranked word list for that topic.

  1. 1
    Pair each weight with its position

    Position 0 is goal with weight 0.1, position 1 is vote with 2.5, position 2 is budget with 0.4, and position 3 is team with 1.7.

  2. 2
    Sort the positions by ascending weight

    Ordering from smallest to largest weight gives the positions 0, 2, 3, 1.

  3. 3
    Reverse the order

    Applying the reversed column slice gives 1, 3, 2, 0, so the first entry points at the largest weight.

  4. 4
    Map positions to words

    Positions 1, 3, 2, 0 map to vote, team, budget, goal through the feature names.

Result

The ranked list for this topic is vote, team, budget, goal. These numbers and words are illustrative and generated for teaching, not taken from a real fitted model.

Choosing the Number of Topics

The number of topics is the most consequential hyperparameter in this pipeline. Ten topics are few enough that a reader can inspect every one of them. A hundred topics give a more fine-grained picture, but reading all 100 would be overwhelming, so the source inspects a hand-picked subset of them by index.

python
Notes on the code
  • Only n_components changes relative to the 10-topic model
  • The selected indices are a judgment call made for illustration
  • Each selected row is ranked with the same argsort idea used earlier

Notice that the selected topic numbers are not a sequence and carry no inherent meaning. They were picked because they look interesting and representative. A reader who picks differently will see a different slice of the model.

One Topic or Several per Document

The source describes two cases that both fall under topic modeling. In the first, each document is assigned a single topic. The source calls this the task of clustering the documents, which connects to the clustering ideas covered earlier in the course. In the second, each document can have more than one topic, and the source connects this case to the decomposition methods covered earlier.

One clusterexactly one topic perdocumentCluster labela single binSeveral topicsa document can touch manytopicsCoefficients pertopiceach topic gets its owndegree of relatednessassigned todescribed by
How does a document's assignment differ when it belongs to exactly one cluster versus a mix of several topics?

When a document is allowed several topics, it helps to picture the document as a bundle of topics rather than a single label. The source explains that the coefficients of the components in a document's representation indicate how strongly the document relates to each topic. A stronger coefficient means a stronger connection.

Notice what this does not claim. It does not say the article belongs to exactly one bin. Each topic receives its own degree of relatedness. This is the practical meaning of a document having multiple topics, and it is what separates this case from clustering.

Topics as Components, Documents as Coefficients

The multi-topic case is linked to decomposition methods. In a decomposition method, the data is expressed in terms of a set of components. In topic modeling, each component the method learns corresponds to one topic. The representation of any single document then records how much of each component it contains, and those amounts are its coefficients.

Document rowone row of thedocument-word tableTopic coefficientsone value per topicTopic 0 componenta row of components_Topic 1 componenta row of components_Topic 9 componenta row of components_represented byweightsweightsweights
How do the document-topic coefficients and topic-word components combine to approximate the document-word matrix?

Latent Dirichlet Allocation is the decomposition method people most often mean when they talk about topic modeling. In these terms, LDA is one specific way of learning the components, and each component is read as a topic. The source does not give the internal mathematics here, so the important takeaway is the shape of the result: topics as components, and documents as coefficients over those components.

Where Topic Results Diverge

When the topic words look wrong, the fault usually sits at one identifiable stage. Each mistake below names the stage where the divergence starts, which is the question a debugger needs answered first.

  • Common mistake

    Treating topic numbers as a ranking

    What it looks like

    Assuming Topic 0 matters more than Topic 9 because it comes first.

    Why it fails

    A topic has no built-in rank, and the numbering is arbitrary.

    Do this instead

    Rank the words within each topic, and compare topics by reading their words rather than by index.

  • Common mistake

    Reading components_ values as a sorted list

    What it looks like

    Taking the first column of a row and assuming it holds the strongest word.

    Why it fails

    np.argsort returns positions, not values, and the raw row is in vocabulary order.

    Do this instead

    Apply argsort, reverse the columns with [:, ::-1], and then map the positions through the feature names.

  • Common mistake

    Expecting one topic to stay the same when the count changes

    What it looks like

    Comparing Topic 3 from a 10-topic fit with Topic 3 from a 100-topic fit as if they describe the same subject.

    Why it fails

    Changing the number of topics changes all of them.

    Do this instead

    Treat each fit as its own model and read its topics fresh.

  • Common mistake

    Forgetting that X sets the ceiling on what the model can see

    Why it fails

    LDA only receives X, so vocabulary cuts and frequency cuts made when building it shape every topic.

    Do this instead

    Check the vectorizer settings and the shape of X before blaming the model.

  • Common mistake

    Assuming the model names its own topics

    Why it fails

    The topics are proposed from patterns of word use, and a person has to read the output and decide what each topic means.

    Do this instead

    Read the top words of each topic and write down a label yourself.

Best practice

When a topic looks strange, check the stages in the same order the data flows. Begin with the vocabulary and the shape of X, confirm the shape of components_ against the number of topics you set, then check the sorting and the feature names. Most divergences are found at the earliest stage that disagrees with your expectation.

Debugging a Strange Topic

MEDIUM

A colleague fits LDA with 10 topics and reports that Topic 0 looks like a mix of unrelated words. Using the chain of stages, list in order the checks you would run to find where the divergence begins, and say what each check would reveal.

Hints
  • Start with the vocabulary and the shape of X
  • Next confirm that components_ has one row per topic you set
  • Then check the argsort and reversed-column step, followed by the feature names

Putting the Pipeline Together

  1. 1
    Topic modeling assigns each document to one or several topics, usually without supervision, so the topics must be found from the documents themselves.
  2. 2
    LDA needs a document-word table X from CountVectorizer; its components_ table holds one row per topic and one column per word, and argsort with a reversed slice turns each row into a ranked word list.
  3. 3
    The number of topics and the learning method are hyperparameters; changing the number of topics changes every topic, and topics have no built-in rank.
  4. 4
    Document clustering assigns one topic per document, while decomposition-based topic modeling gives each document coefficients over several topic components.
  5. 5
    When results look wrong, check the stages in data-flow order, starting with X, then components_, then the sorting and feature names.

Key Takeaways

  • Topic modeling assigns each document to one or several topics, usually without supervision, so the topics are discovered from the text rather than supplied in advance.
  • LDA reads a numeric document-word table built by CountVectorizer and stores its topics in components_, which has shape (n_topics, n_words).
  • Ranking a topic means applying argsort, reversing the columns, and mapping the positions through the feature names; topics have no built-in order.
  • Clustering assigns each document to one topic, while decomposition-based topic modeling represents each document as coefficients over several topic components.
  • Hyperparameters such as the number of topics and the learning method change the topics, so diagnose problems by checking the earliest stage that disagrees with expectation.

Related concepts

More concepts starting with T