Adam Oudad

Adam Oudad

(Machine) Learning log.

5 minutes read

tqdm 1is a Python library for adding progress bar. It lets you configure and display a progress bar with metrics you want to track. Its ease of use and versatility makes it the perfect choice for tracking machine learning experiments.

I organize this tutorial in two parts. I will first introduce tqdm, then show an example for machine learning. For each code fragment in this article, we will import the sleep function from Python's time library as it will let us slow down the program to see the progress bar update.

from time import sleep

Tqdm

You can install tqdm with pip install tqdm. The library comes with various iterators each dedicated to a specific use that I am going to present.

tqdm is the default iterator. It takes an iterator object as argument and displays a progress bar as it iterates over it.

from tqdm import tqdm

lst = [1, 3, 5, 7, 11]

for element in tqdm(lst):
sleep(0.1)

Output is

100%|█████████████████████████████████| 5/5 [00:00<00:00,  9.90it/s]

You can see the nice output with 9.90it/s meaning an average speed of 9.90 iterations per second. "it" for iterations can be configured to something else and this is what we will see on the next example.

trange follows the same template as range in Python. For example, give to trange the number of iterations.

from tqdm import trange

iterations = 20
for i in trange(iterations, desc="Proving P=NP", unit="carrots"):
sleep(0.1)
Proving P=NP: 100%|████████████| 20/20 [00:02<00:00,  9.91carrots/s]

You can see in this example we added a (joke) description of what we are doing and the unit for each iteration.

Update the progress bar during execution

tqdm has two methods that can update what is displayed in the progress bar.

  • .set_description, is used to update the text prepended in front of the progress bar (the prefix).
  • .set_postfix is used to update the text appended after the progress bar (the postfix).

To use these methods, we need to assign the tqdm iterator instance to a variable. This can be done either with the = operator or the with keyword in Python.

We can for example update the postfix with the list of divisors of the number i. Let's use this function to get the list of divisors

def get_divisors(n):
 divisors = []
 for m in range(1, n):
     if n % m == 0:
         divisors.append(m)
 return divisors

And here is our code with progress bar.

iterations = 10
pbar = trange(iterations, desc="Proving P=NP", unit="carrots")
for i in pbar:
 sleep(0.5)
 if i % 2:
     pbar.set_description(f"Testing odd number {i}")
 else:
     pbar.set_description(f"Testing even number {i}")
 pbar.set_postfix(divisors=get_divisors(i))

If you feel like a distinguished Python programmer, you may use with keyword like so

iterations = 10
with trange(iterations,
         desc="Proving P=NP", unit="carrots") as pbar:
 for i in pbar:
     sleep(0.5)
     if i % 2:
         pbar.set_description(f"Testing odd number {i}")
     else:
         pbar.set_description(f"Testing even number {i}")
     pbar.set_postfix(divisors=get_divisors(i))

Here is the status displayed at i=6.

Testing even number 6:  70%|██████████████▋      | 7/10 [00:03<00:01,  1.76carrots/s, divisors=[1, 2, 3]]

Track loss and accuracy

In this section, we use a neural network wrote in PyTorch and train it using tqdm to display the loss and accuracy. Here is the model

import torch
from torch import nn
import torch.nn.functional as F

class Perceptron(nn.Module):
 def __init__(self):
     super().__init__()
     self.dropout2 = nn.Dropout(0.5)
     self.fc1 = nn.Linear(28 * 28, 128)  # 9216
     self.fc2 = nn.Linear(128, 10)

 def forward(self, x):
     x = torch.flatten(x, 1)
     x = self.fc1(x)
     x = F.relu(x)
     x = self.dropout2(x)
     x = self.fc2(x)
     output = F.log_softmax(x, dim=1)
     return output

This is a simple perceptron model that we can use for processing images of digits from MNIST dataset. The following code for loading MNIST dataset is inspired from a PyTorch example2.

from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torch.optim import Adam
from torch import nn

transform=transforms.Compose([
 transforms.ToTensor(),
 transforms.Normalize((0.1307,), (0.3081,))
])
dataset = datasets.MNIST('./mnist_data', train=True, download=True,
                      transform=transform)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

model = Perceptron().to(device)

optimizer = Adam(model.parameters())
criterion = nn.CrossEntropyLoss()
batch_size = 64

train_loader = DataLoader(dataset,
                       batch_size=batch_size,
                       num_workers=1,
                       pin_memory=True,
                       shuffle=True,
                       drop_last=True)

We just loaded our data, and defined our model and settings, we can now run a training experiment.

model.train()
for epoch in range(1, 5):
 with tqdm(train_loader, unit="batch") as tepoch:
     for data, target in tepoch:
         tepoch.set_description(f"Epoch {epoch}")

         data, target = data.to(device), target.to(device)
         optimizer.zero_grad()
         output = model(data)
         predictions = output.argmax(dim=1, keepdim=True).squeeze()
         loss = F.nll_loss(output, target)
         correct = (predictions == target).sum().item()
         accuracy = correct / batch_size

         loss.backward()
         optimizer.step()
         
         tepoch.set_postfix(loss=loss.item(), accuracy=100. * accuracy)
         sleep(0.1)

I am once again using sleep function to pause the program so that we can see the update of the progress bar. As you can see, we just applied what we learned previously here, in particular with tepoch.set_postfix and tepoch.set_description which let you update the information displayed by the progress bar. Here is a capture of the output while the program was running

Epoch 1:  15%|▉     | 142/937 [00:16<01:32,  8.56batch/s, accuracy=89.1, loss=0.341]

This gives us an idea of how tqdm can be used in practical.

Conclusion

You can achieve much more with tqdm, like adapting it to Jupyter notebooks, finely configuring the progress bar updates or nesting progress bars, so I recommend you to read the documentation for more: https://github.com/tqdm/tqdm

Footnotes


1

"tqdm" means "progress" in arabic or is short for "te quiero demasiado" in spanish. You can pronounce it tek-a-dem if you prefer.

comments powered by Disqus

Recent posts

See more

Categories

About

This website is a weblog were I write about computer science, machine learning, language learning.