<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Multi-Class Image Classification]]></title><description><![CDATA[Multi-Class Image Classification]]></description><link>https://multi-class-image-classification.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 01 Sep 2026 17:01:36 GMT</lastBuildDate><atom:link href="https://multi-class-image-classification.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Multi-Class Image Classification with FashionMNIST in PyTorch]]></title><description><![CDATA[Introduction
In this tutorial, we’ll walk through building a multi-class image classification model using PyTorch and the FashionMNIST dataset.

This task involves identifying 10 categories of clothing such as shirts, trousers, and sneakers. PyTorch ...]]></description><link>https://multi-class-image-classification.hashnode.dev/multi-class-image-classification-with-fashionmnist-in-pytorch</link><guid isPermaLink="true">https://multi-class-image-classification.hashnode.dev/multi-class-image-classification-with-fashionmnist-in-pytorch</guid><category><![CDATA[CNN]]></category><category><![CDATA[Multiclass classification]]></category><category><![CDATA[pytorch]]></category><dc:creator><![CDATA[Tanayendu Bari]]></dc:creator><pubDate>Wed, 25 Jun 2025 21:14:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/eBRTYyjwpRY/upload/59cec78d3b1e77adfd6462e4f290e00c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p>In this tutorial, we’ll walk through building a <strong>multi-class image classification model</strong> using <strong>PyTorch</strong> and the <strong>FashionMNIST</strong> dataset.</p>
<p><img src="https://imgs.search.brave.com/D1ZJO-xtKOLydwtkX9wQo76FnDxGH-rTl-vqlTxEYgE/rs:fit:860:0:0:0/g:ce/aHR0cHM6Ly9tYWNo/aW5lbGVhcm5pbmdt/YXN0ZXJ5LmNvbS93/cC1jb250ZW50L3Vw/bG9hZHMvMjAxOS8w/Mi9QbG90LW9mLWEt/U3Vic2V0LW9mLUlt/YWdlcy1mcm9tLXRo/ZS1GYXNoaW9uLU1O/SVNULURhdGFzZXQt/MTAyNHg3NjgucG5n" alt="Plot of a Subset of Images From the Fashion-MNIST Dataset" /></p>
<p>This task involves identifying 10 categories of clothing such as shirts, trousers, and sneakers. PyTorch makes it easy to build, train, and evaluate deep learning models with minimal boilerplate.</p>
<h3 id="heading-step-1-import-required-libraries">Step 1: Import Required Libraries</h3>
<p>Before we start building the model, we need to import the essential libraries from PyTorch and Torchvision:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> torch
<span class="hljs-keyword">import</span> torchvision
<span class="hljs-keyword">from</span> torch <span class="hljs-keyword">import</span> nn
<span class="hljs-keyword">from</span> torchvision <span class="hljs-keyword">import</span> datasets, transforms
<span class="hljs-keyword">from</span> torchvision.transforms <span class="hljs-keyword">import</span> ToTensor
<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt
</code></pre>
<h3 id="heading-explanation">Explanation:</h3>
<ul>
<li><p><code>torch</code>: The core PyTorch library for tensor operations and deep learning components.</p>
</li>
<li><p><code>torchvision</code>: A utility library for image datasets and transformations.</p>
</li>
<li><p><code>datasets</code>: Contains ready-to-use datasets like MNIST, CIFAR10, and FashionMNIST.</p>
</li>
<li><p><code>transforms</code>: Provides tools to preprocess and normalize images.</p>
</li>
<li><p><code>ToTensor</code>: Converts images from PIL format to PyTorch tensors.</p>
</li>
<li><p><code>matplotlib.pyplot</code>: A popular library for visualizing images and plots.</p>
</li>
</ul>
<p>This setup is essential for loading, transforming, and visualizing the FashionMNIST dataset, which we’ll use for multi-class classification.</p>
<h3 id="heading-step-2-set-the-device-cpu-or-gpu">Step 2: Set the Device (CPU or GPU)</h3>
<p>To make our model run efficiently, we should check if a <strong>GPU (CUDA)</strong> is available and use it. Otherwise, we'll fall back to the CPU.</p>
<pre><code class="lang-python">device = <span class="hljs-string">"cuda"</span> <span class="hljs-keyword">if</span> torch.cuda.is_available() <span class="hljs-keyword">else</span> <span class="hljs-string">"cpu"</span>
</code></pre>
<h3 id="heading-why-this-matters">Why This Matters:</h3>
<ul>
<li><p><strong>GPU acceleration</strong> significantly speeds up training for deep learning models.</p>
</li>
<li><p>If you’re working on Google Colab, Kaggle, or a local machine with a compatible GPU, PyTorch will automatically use it with this line.</p>
</li>
<li><p>This setup ensures that your code works both on <strong>CPU-only environments</strong> and on systems with <strong>CUDA-enabled GPUs</strong>.</p>
</li>
</ul>
<p>From this point onward, we’ll make sure to send all our data and models to this <code>device</code>.</p>
<h3 id="heading-step-3-load-the-fashionmnist-dataset">Step 3: Load the FashionMNIST Dataset</h3>
<p>We'll use the <a target="_blank" href="https://pytorch.org/vision/stable/datasets.html#fashionmnist"><strong>FashionMNIST</strong></a> dataset provided by <code>torchvision.datasets</code>. It contains <strong>28x28 grayscale images</strong> of clothing items, each labeled with one of 10 classes such as shirts, trousers, shoes, etc.</p>
<p>Here's how to download and load it into your project:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> torchvision <span class="hljs-keyword">import</span> datasets
<span class="hljs-keyword">from</span> torchvision.transforms <span class="hljs-keyword">import</span> ToTensor

<span class="hljs-comment"># Download and load the training dataset</span>
train_data = datasets.FashionMNIST(
    root=<span class="hljs-string">'data'</span>,            <span class="hljs-comment"># Directory to store the dataset</span>
    train=<span class="hljs-literal">True</span>,             <span class="hljs-comment"># Load the training set</span>
    download=<span class="hljs-literal">True</span>,          <span class="hljs-comment"># Download if it's not already available</span>
    transform=ToTensor(),   <span class="hljs-comment"># Convert PIL images to PyTorch tensors</span>
    target_transform=<span class="hljs-literal">None</span>   <span class="hljs-comment"># We’ll use raw integer labels (0–9)</span>
)

<span class="hljs-comment"># Download and load the test dataset</span>
test_data = datasets.FashionMNIST(
    root=<span class="hljs-string">'data'</span>,
    train=<span class="hljs-literal">False</span>,            <span class="hljs-comment"># Load the test set</span>
    transform=ToTensor(),
    download=<span class="hljs-literal">True</span>,
    target_transform=<span class="hljs-literal">None</span>
)
</code></pre>
<h3 id="heading-what-happens-here">What Happens Here:</h3>
<ul>
<li><p>PyTorch will <strong>automatically download</strong> the dataset if it’s not already present.</p>
</li>
<li><p>Each image is converted into a tensor using <code>ToTensor()</code>, scaling pixel values to the range [0, 1].</p>
</li>
<li><p>The data is stored in a folder called <code>data/</code>.</p>
</li>
</ul>
<h3 id="heading-step-4-visualize-a-sample-image">Step 4: Visualize a Sample Image</h3>
<p>Let’s take a quick look at one of the training images to better understand the dataset. We'll use <a target="_blank" href="https://matplotlib.org/">Matplotlib</a> for visualization.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt

<span class="hljs-comment"># Get the second image and its label from the training dataset</span>
image, label = train_data[<span class="hljs-number">1</span>]

<span class="hljs-comment"># Print the shape of the image tensor</span>
print(<span class="hljs-string">f"Image shape is: <span class="hljs-subst">{image.shape}</span>"</span>)

<span class="hljs-comment"># Plot the image</span>
plt.imshow(image.squeeze(), cmap=<span class="hljs-string">'gray'</span>)
plt.title(class_names[label])
plt.axis(<span class="hljs-literal">False</span>)
</code></pre>
<h3 id="heading-explanation-1">Explanation:</h3>
<ul>
<li><p><code>train_data[1]</code> gives us the second image and its label from the dataset.</p>
</li>
<li><p><code>image.shape</code> will be <code>[1, 28, 28]</code>, indicating a single-channel (grayscale) image of size 28x28.</p>
</li>
<li><p><code>squeeze()</code> removes the single-channel dimension so Matplotlib can display it properly.</p>
</li>
<li><p><code>cmap='gray'</code> renders the image in grayscale.</p>
</li>
<li><p><code>class_names[label]</code> maps the numeric label (e.g., <code>0</code>) to its actual class name (e.g., <code>'T-shirt/top'</code>).</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750884622838/d3ca046a-82bf-441c-bbb0-d65ad374238e.png" alt class="image--center mx-auto" /></p>
<p>To make this work, you should define the list of class names before this block:</p>
<pre><code class="lang-python">class_names = train_data.classes
</code></pre>
<h3 id="heading-5-prepare-the-data-loaders">5: Prepare the Data Loaders</h3>
<p>Now that we've loaded the dataset, we need to prepare it for training using PyTorch’s <code>DataLoade``r</code>. This allows us to load the data in <strong>m**</strong>ini-batches**, which improves training efficiency and supports GPU processing.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> torch.utils.data <span class="hljs-keyword">import</span> DataLoader

<span class="hljs-comment"># Create the DataLoader for training data</span>
train_dataloader = DataLoader(
    dataset=train_data,
    batch_size=<span class="hljs-number">32</span>,  <span class="hljs-comment"># Number of samples per batch</span>
    shuffle=<span class="hljs-literal">True</span>    <span class="hljs-comment"># Shuffle data at every epoch</span>
)

<span class="hljs-comment"># Create the DataLoader for test data</span>
test_dataloader = DataLoader(
    dataset=test_data,
    batch_size=<span class="hljs-number">32</span>,
    shuffle=<span class="hljs-literal">False</span>   <span class="hljs-comment"># No need to shuffle test data</span>
)
</code></pre>
<h3 id="heading-why-use-a-dataloader">Why Use a DataLoader?</h3>
<ul>
<li><p>Batching allows the model to <strong>process multiple images simultaneously</strong>, speeding up training.</p>
</li>
<li><p><code>shuffle=True</code> ensures the training data is randomized each epoch, helping the model generalize better.</p>
</li>
<li><p><code>batch_size=32</code> is a commonly used value that balances performance and memory usage.</p>
</li>
</ul>
<h3 id="heading-step-6-define-a-convolutional-neural-network-cnn">Step 6: Define a Convolutional Neural Network (CNN)</h3>
<p>For image classification tasks like FashionMNIST, <strong>Convolutional Neural Networks (CNNs)</strong> are highly effective because they can capture spatial hierarchies in images using convolutional layers.</p>
<p>Let’s define a small CNN architecture inspired by the <a target="_blank" href="https://arxiv.org/abs/1409.1556">VGGNet</a> family, called <code>TinyVGG</code>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TinyVGG</span>(<span class="hljs-params">nn.Module</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, input_shape: int, hidden_units: int, output_shape: int</span>):</span>
        super().__init__()

        <span class="hljs-comment"># Convolutional blocks</span>
        self.block_1 = nn.Sequential(
            nn.Conv2d(input_shape, hidden_units, kernel_size=<span class="hljs-number">3</span>, padding=<span class="hljs-number">1</span>),
            nn.ReLU(),
            nn.Conv2d(hidden_units, hidden_units, kernel_size=<span class="hljs-number">3</span>, padding=<span class="hljs-number">1</span>),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=<span class="hljs-number">2</span>)
        )

        self.block_2 = nn.Sequential(
            nn.Conv2d(hidden_units, hidden_units, kernel_size=<span class="hljs-number">3</span>, padding=<span class="hljs-number">1</span>),
            nn.ReLU(),
            nn.Conv2d(hidden_units, hidden_units, kernel_size=<span class="hljs-number">3</span>, padding=<span class="hljs-number">1</span>),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=<span class="hljs-number">2</span>)
        )

        <span class="hljs-comment"># Infer flattened size</span>
        <span class="hljs-keyword">with</span> torch.no_grad():
            sample_input = torch.randn(<span class="hljs-number">1</span>, input_shape, <span class="hljs-number">28</span>, <span class="hljs-number">28</span>)  <span class="hljs-comment"># assuming 28x28 image</span>
            sample_output = self.block_2(self.block_1(sample_input))
            self.flattened_size = sample_output.view(<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>).shape[<span class="hljs-number">1</span>]

        <span class="hljs-comment"># Classifier</span>
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(self.flattened_size, output_shape)
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">forward</span>(<span class="hljs-params">self, x</span>):</span>
        x = self.block_1(x)
        x = self.block_2(x)
        x = self.classifier(x)
        <span class="hljs-keyword">return</span> x
</code></pre>
<h3 id="heading-explanation-2">Explanation:</h3>
<ul>
<li><p>The model takes <strong>28x28 grayscale images</strong> (so <code>input_shape = 1</code>).</p>
</li>
<li><p>It uses two convolutional blocks:</p>
<ul>
<li>Each block has two convolutional layers followed by ReLU activation and a MaxPooling layer.</li>
</ul>
</li>
<li><p>After the convolutions, the image tensor size is reduced from <code>28x28</code> → <code>14x14</code> → <code>7x7</code>.</p>
</li>
<li><p>The final layer is a fully connected (linear) layer that maps the extracted features to <strong>10 output classes</strong>.</p>
</li>
</ul>
<h3 id="heading-step-7-instantiate-the-model-and-move-it-to-device">Step 7: Instantiate the Model and Move it to Device</h3>
<p>Now that we’ve defined the <code>TinyVGG</code> model architecture, let’s create an instance of the model and move it to the appropriate device (CPU or GPU) for training.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create an instance of the model</span>
model = TinyVGG(input_shape=<span class="hljs-number">1</span>, hidden_units=<span class="hljs-number">10</span>, output_shape=<span class="hljs-number">10</span>)

<span class="hljs-comment"># Move the model to the available device (CPU or GPU)</span>
model = model.to(device)
</code></pre>
<h3 id="heading-explanation-3">Explanation:</h3>
<ul>
<li><p><code>input_shape=1</code> because FashionMNIST images are grayscale (1 channel).</p>
</li>
<li><p><code>hidden_units=10</code> defines how many filters each convolutional layer will learn (you can increase this for a deeper model).</p>
</li>
<li><p><code>output_shape=10</code> corresponds to the 10 classes in the FashionMNIST dataset.</p>
</li>
<li><p><a target="_blank" href="http://model.to"><code>model.to</code></a><code>(device)</code> ensures the model runs on GPU if available, otherwise on CPU.</p>
</li>
</ul>
<h3 id="heading-step-8-define-loss-function-optimizer-and-accuracy-metric">Step 8: Define Loss Function, Optimizer, and Accuracy Metric</h3>
<p>To train a neural network, we need:</p>
<ul>
<li><p>A <strong>loss function</strong> to measure how wrong the model's predictions are.</p>
</li>
<li><p>An <strong>optimizer</strong> to adjust the model’s parameters to minimize that loss.</p>
</li>
<li><p>A <strong>metric</strong> like accuracy to evaluate how well the model is performing.</p>
</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> torch.nn <span class="hljs-keyword">as</span> nn
<span class="hljs-keyword">import</span> torch

<span class="hljs-comment"># Define the loss function</span>
loss_fn = nn.CrossEntropyLoss()

<span class="hljs-comment"># Define the optimizer</span>
optimizer = torch.optim.SGD(model.parameters(), lr=<span class="hljs-number">0.1</span>)
</code></pre>
<p>We’re using:</p>
<ul>
<li><p><strong>CrossEntropyLoss</strong> because it's the standard loss function for multi-class classification.</p>
</li>
<li><p><strong>SGD (Stochastic Gradient Descent)</strong> with a learning rate of <code>0.1</code> as the optimizer. You can later experiment with optimizers like <code>Adam</code> for faster convergence.</p>
</li>
</ul>
<h3 id="heading-accuracy-function">Accuracy Function</h3>
<p>We'll also define a function to calculate accuracy:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">accuracy_fn</span>(<span class="hljs-params">y_true, y_pred</span>):</span>
    <span class="hljs-string">"""
    Calculates accuracy between true labels and predicted labels.
    Args:
        y_true (torch.Tensor): Ground truth labels.
        y_pred (torch.Tensor): Predicted class labels.
    Returns:
        Float accuracy score (e.g., 78.5)
    """</span>
    correct = torch.eq(y_true, y_pred).sum().item()
    acc = (correct / len(y_pred)) * <span class="hljs-number">100</span>
    <span class="hljs-keyword">return</span> acc
</code></pre>
<p>Note:<br />Do <strong>not</strong> call <code>accuracy_fn = accuracy_fn(y_true, y_pred)</code> at this stage, because <code>y_true</code> and <code>y_pred</code> don’t exist yet. We'll use this function inside our training and evaluation loops after the model makes predictions.</p>
<h3 id="heading-step-9-define-the-training-loop">Step 9: Define the Training Loop</h3>
<p>Now that we have our model, loss function, optimizer, and accuracy metric ready, it’s time to build the <strong>training loop</strong>.</p>
<p>This loop goes through the dataset in batches, updates the model’s weights using backpropagation, and tracks performance.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> tqdm.auto <span class="hljs-keyword">import</span> tqdm
torch.manual_seed(<span class="hljs-number">41</span>)  <span class="hljs-comment"># Set a seed for reproducibility</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">train_step</span>(<span class="hljs-params">model: torch.nn.Module,
               data_loader: torch.utils.data.DataLoader,
               loss_fn: torch.nn.Module,
               optimizer: torch.optim.Optimizer,
               accuracy_fn,
               device: torch.device = device</span>):</span>

    train_loss, train_acc = <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
    model.to(device)

    <span class="hljs-keyword">for</span> batch, (X, y) <span class="hljs-keyword">in</span> enumerate(data_loader):
        <span class="hljs-comment"># Move data to the target device (CPU or GPU)</span>
        X, y = X.to(device), y.to(device)

        <span class="hljs-comment"># 1. Forward pass</span>
        y_pred = model(X)

        <span class="hljs-comment"># 2. Compute the loss</span>
        loss = loss_fn(y_pred, y)
        train_loss += loss

        <span class="hljs-comment"># 3. Calculate accuracy</span>
        train_acc += accuracy_fn(y_true=y, y_pred=y_pred.argmax(dim=<span class="hljs-number">1</span>))  <span class="hljs-comment"># Convert logits to predicted labels</span>

        <span class="hljs-comment"># 4. Backpropagation</span>
        optimizer.zero_grad()   <span class="hljs-comment"># Clear gradients</span>
        loss.backward()         <span class="hljs-comment"># Backpropagate</span>
        optimizer.step()        <span class="hljs-comment"># Update weights</span>

    <span class="hljs-comment"># Average loss and accuracy across batches</span>
    train_loss /= len(data_loader)
    train_acc /= len(data_loader)
    print(<span class="hljs-string">f"Train loss: <span class="hljs-subst">{train_loss:<span class="hljs-number">.5</span>f}</span> | Train accuracy: <span class="hljs-subst">{train_acc:<span class="hljs-number">.2</span>f}</span>%"</span>)
</code></pre>
<h3 id="heading-whats-happening-here">What’s Happening Here:</h3>
<ul>
<li><p><strong>DataLoader</strong> feeds the model mini-batches of images and labels.</p>
</li>
<li><p>The model makes predictions with a <strong>forward pass</strong>.</p>
</li>
<li><p>The <strong>loss</strong> is calculated and used to compute gradients via <code>loss.backward()</code>.</p>
</li>
<li><p>The <strong>optimizer</strong> updates the model weights using <code>optimizer.step()</code>.</p>
</li>
<li><p>Accuracy is tracked across the entire dataset for reporting.</p>
</li>
</ul>
<blockquote>
<p>Note: We call <code>.argmax(dim=1)</code> to convert raw model outputs (logits) into predicted class labels.</p>
</blockquote>
<p>This function is modular and reusable—you can call it for every training epoch!</p>
<h3 id="heading-step-10-define-the-evaluation-loop">Step 10: Define the Evaluation Loop</h3>
<p>After training your model, it's essential to evaluate its performance on unseen data. The <code>test_step</code> function helps you do that by calculating the <strong>test loss</strong> and <strong>test accuracy</strong> across the validation or test dataset.</p>
<p>Here’s the complete evaluation function:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_step</span>(<span class="hljs-params">data_loader: torch.utils.data.DataLoader,
              model: torch.nn.Module,
              loss_fn: torch.nn.Module,
              accuracy_fn,
              device: torch.device = device</span>):</span>

    test_loss, test_acc = <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
    model.to(device)
    model.eval()  <span class="hljs-comment"># Set the model to evaluation mode</span>

    <span class="hljs-comment"># Disable gradient tracking for faster inference</span>
    <span class="hljs-keyword">with</span> torch.inference_mode():
        <span class="hljs-keyword">for</span> X, y <span class="hljs-keyword">in</span> data_loader:
            <span class="hljs-comment"># Move data to the target device</span>
            X, y = X.to(device), y.to(device)

            <span class="hljs-comment"># 1. Forward pass</span>
            test_pred = model(X)

            <span class="hljs-comment"># 2. Calculate loss and accuracy</span>
            test_loss += loss_fn(test_pred, y)
            test_acc += accuracy_fn(
                y_true=y,
                y_pred=test_pred.argmax(dim=<span class="hljs-number">1</span>)  <span class="hljs-comment"># Convert logits to predicted class labels</span>
            )

    <span class="hljs-comment"># Average the results over all batches</span>
    test_loss /= len(data_loader)
    test_acc /= len(data_loader)
    print(<span class="hljs-string">f"Test loss: <span class="hljs-subst">{test_loss:<span class="hljs-number">.5</span>f}</span> | Test accuracy: <span class="hljs-subst">{test_acc:<span class="hljs-number">.2</span>f}</span>%\n"</span>)
</code></pre>
<h3 id="heading-key-highlights">Key Highlights:</h3>
<ul>
<li><p><code>model.eval()</code> tells PyTorch that the model is in <strong>inference mode</strong>, which disables certain layers like dropout and batch normalization.</p>
</li>
<li><p><code>with torch.inference_mode()</code> disables gradient calculations, saving memory and improving speed during testing.</p>
</li>
<li><p>We calculate loss and accuracy for each batch and then average them across the entire dataset.</p>
</li>
</ul>
<blockquote>
<p>This function is structured almost the same as the training loop—except we don’t backpropagate or update weights.</p>
</blockquote>
<h3 id="heading-step-11-create-a-general-model-evaluation-function">Step 11: Create a General Model Evaluation Function</h3>
<p>To make evaluation more modular and reusable, let’s define a utility function that returns the model's name, average loss, and accuracy in a structured format. This is helpful when comparing multiple models later on.</p>
<pre><code class="lang-python">torch.manual_seed(<span class="hljs-number">42</span>)  <span class="hljs-comment"># For reproducibility</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">eval_mode</span>(<span class="hljs-params">model: torch.nn.Module,
              data_loader: torch.utils.data.DataLoader,
              loss_fn: torch.nn.Module,
              accuracy_fn</span>):</span>

    loss, acc = <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
    model.eval()  <span class="hljs-comment"># Set the model to evaluation mode</span>

    <span class="hljs-keyword">with</span> torch.inference_mode():  <span class="hljs-comment"># Disable gradient tracking</span>
        <span class="hljs-keyword">for</span> X, y <span class="hljs-keyword">in</span> data_loader:
            X, y = X.to(device), y.to(device)
            y_pred = model(X)
            loss += loss_fn(y_pred, y)
            acc += accuracy_fn(y_true=y, y_pred=y_pred.argmax(dim=<span class="hljs-number">1</span>))

    loss /= len(data_loader)
    acc /= len(data_loader)

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"Model_name"</span>: model.__class__.__name__,
        <span class="hljs-string">"Model_loss"</span>: loss.item(),
        <span class="hljs-string">"Model_acc"</span>: acc
    }
</code></pre>
<h3 id="heading-what-this-does">What This Does:</h3>
<ul>
<li><p>This is a <strong>general-purpose evaluation function</strong>.</p>
</li>
<li><p>It calculates <strong>average loss and accuracy</strong> across a dataset.</p>
</li>
<li><p>It returns a dictionary with:</p>
<ul>
<li><p>The model’s class name</p>
</li>
<li><p>Final loss</p>
</li>
<li><p>Final accuracy</p>
</li>
</ul>
</li>
<li><p>Useful for <strong>benchmarking or reporting results</strong>.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you learned how to build a <strong>multi-class image classification model</strong> using <strong>PyTorch</strong> and the <strong>FashionMNIST</strong> dataset. We covered:</p>
<ul>
<li><p>Understanding multi-class classification</p>
</li>
<li><p>Loading and preprocessing image data</p>
</li>
<li><p>Building a custom CNN model (<code>TinyVGG</code>)</p>
</li>
<li><p>Writing modular training and evaluation loops</p>
</li>
<li><p>Calculating accuracy and visualizing predictions</p>
</li>
</ul>
<p>This end-to-end pipeline is a strong foundation for many real-world computer vision tasks.</p>
<h2 id="heading-references">References</h2>
<ol>
<li><p><a target="_blank" href="https://pytorch.org/docs/stable/index.html"><strong>PyTorch Documentation</strong></a></p>
</li>
<li><p><a target="_blank" href="https://pytorch.org/vision/stable/datasets.html"><strong>Torchvision Datasets and Transforms</strong></a></p>
</li>
<li><p><a target="_blank" href="https://github.com/zalandoresearch/fashion-mnist"><strong>FashionMNIST Dataset</strong></a></p>
</li>
<li><p><a target="_blank" href="https://pytorch.org/tutorials/beginner/introyt/trainingyt.html"><strong>PyTorch Training Loop Best Practices</strong></a></p>
</li>
<li><p><a target="_blank" href="https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html"><strong>Deep Learning with PyTorch: A 60 Minute Blitz</strong></a></p>
</li>
<li><p><a target="_blank" href="https://arxiv.org/abs/1409.1556"><strong>CNN Architectures – VGGNet</strong></a></p>
</li>
<li><p><a target="_blank" href="https://scikit-learn.org/stable/modules/model_evaluation.html"><strong>Scikit-learn Metrics for Classification</strong>  
 </a></p>
</li>
</ol>
<p>Also read about Binary Classification here <a target="_blank" href="https://binary-classification-using-pytorch.hashnode.dev/">https://binary-classification-using-pytorch.hashnode.dev/</a> and for CNNs <a target="_blank" href="https://convolutional-neural-networks-cnns.hashnode.dev/">https://convolutional-neural-networks-cnns.hashnode.dev/</a></p>
]]></content:encoded></item></channel></rss>