Trusting AI/ML: The Automated Testing Behind the Tech?
People often think automated testing is the Swiss Army knife for AI models handling everything with ease. But in reality, it’s more like bringing a butter knife to a sword fight. There’s no one tool that can cut through every challenge. — Breaking The Swiss Army Knife Myth ?

The world around us is changing at an incredible pace, and at the center of this transformation are AI and ML. Every day, these technologies quietly shape the way we live — from the apps that guide our daily routines to the systems that make decisions about our health, finances, and even the relationships we build. As I watch this shift unfold, I can’t help but ask myself: Are we putting too much trust in these AI/ML models? We rely on them so heavily, but are they really tested to ensure they’re doing what we expect? And if they are tested, what exactly goes into that process? What kinds of checks and safeguards are in place to make sure these models are accurate, fair, and secure? These questions linger in my mind as we navigate this ever-changing world, reminding me that while technology advances, the need for trust and reliability remains constant.
How to ensure trust in AI ?
Imagine you’re working on an AI model that’s going to help doctors diagnose diseases more accurately. It’s an exciting project with the potential to save lives, but there’s one thing that keeps you up at night: How can you be sure that this model will actually work as intended? After all, it’s not just about getting the code right — it’s about making sure the model can handle real-world data, make fair decisions, and remain reliable over time. This is where automated testing comes into play ?
So, how do we go about this automated testing I don't know but I have my thoughts ? Let’s dive into the process.
It all starts with data validation testing. Data is the lifeblood of any AI model, and if your data is flawed, your model will be too. Imagine trying to build a house on a shaky foundation — it just won’t hold up. Automated data validation checks the data for missing values, outliers, or inconsistencies that could throw off your model. For example, if you’re working with patient records, you need to make sure there are no duplicate entries or impossible values, like a negative age. By catching these issues early, you’re setting your model up for success.
Next comes model performance testing. This is where you find out if your model is actually good at what it’s supposed to do. Automated tests measure the model’s accuracy, precision, recall, and other metrics that tell you whether it’s hitting the mark. Think of it like a dress rehearsal before the big show — you want to make sure everything is perfect before you go live. For instance, if you’ve built a sentiment analysis model, you’ll want to test it with a variety of text inputs to see if it can correctly identify sarcasm or ambiguity.
But what about fairness? In a world where AI models are making decisions that impact real lives, ensuring that these decisions are fair is critical. That’s where bias and fairness testing comes in. Automated tests help you detect if your model is unfairly favoring one group over another. Imagine a hiring algorithm that consistently favors candidates from one demographic — it’s not just unfair, it’s potentially illegal. By running these tests, you can identify and correct biases before they cause harm.
Then there’s the question of robustness. How well can your model handle the unexpected? Robustness testing, also known as adversarial testing, throws tricky, unexpected inputs at your model to see how it reacts. It’s like stress-testing a bridge by driving heavy trucks over it — you want to be sure it won’t collapse under pressure. For example, in a facial recognition system, you might introduce small changes to an image to see if the model still recognizes the person. If your model fails these tests, it’s back to the drawing board.
But even if your model passes all these tests, there’s one more piece of the puzzle: model explainability. It’s not enough for a model to make the right decision; we also need to understand why it made that decision. Imagine a doctor relying on an AI model to diagnose a patient — wouldn’t you want to know why the model suggests one treatment over another? Automated explainability testing uses tools like SHAP or LIME to break down the model’s decisions into understandable terms, ensuring that the people using the model can trust its outputs.
Finally, we need to consider how the model performs in the real world. This is where scalability and performance testing come into play. These automated tests simulate real-world conditions to see how the model handles large amounts of data or simultaneous requests. It’s like testing a car on the highway — you want to know it can handle high speeds and heavy traffic without breaking down.
Automated testing is more than just a technical process; it’s about ensuring that the AI/ML models we build are reliable, fair, and safe to use in the real world. It’s the assurance that when a model makes a decision — whether it’s diagnosing a disease, approving a loan, or recommending a product — it’s doing so accurately, fairly, and with the best possible outcome in mind. And in a world where AI is becoming increasingly integrated into our lives, that assurance is more important than ever.
Meet the Tools Behind the Scenes
Now that we’ve explored the importance of automated testing in ensuring the reliability and safety of AI/ML models, it’s time to meet the tools that make this process possible. Just like a skilled carpenter relies on a set of trusted tools to craft a sturdy piece of furniture, data scientists and engineers use specialized tools to test and refine their AI models. These tools help us validate data, assess model performance, check for bias, and more — all crucial steps in building AI systems we can trust.
- SHAP (SHapley Additive exPlanations): When it comes to understanding why an AI model makes certain decisions, SHAP is one of the most powerful tools at our disposal. It helps break down the output of complex models into understandable components, showing us how each feature contributes to a particular prediction. For example, in our hypothetical disease diagnosis model, SHAP can tell us which symptoms or patient characteristics led the model to predict a certain diagnosis. This transparency is essential, especially in fields like healthcare, where every decision matters. Read more at https://shap.readthedocs.io/en/latest/index.html
- LIME (Local Interpretable Model-agnostic Explanations): Similar to SHAP, LIME helps explain model predictions, but with a slightly different approach. LIME creates simple, interpretable models around each prediction to approximate the more complex model’s behavior. It’s like zooming in on a small part of a map to understand the details of a specific area. LIME is particularly useful when you need quick, localized explanations for individual predictions. Read more at https://github.com/marcotcr/lime
- DeepXplore: DeepXplore is a pioneering tool in the field of deep learning testing. It’s the first white-box testing framework for neural networks, designed to automatically find defects and vulnerabilities in AI models by generating inputs that trigger inconsistencies between multiple models. Imagine you’re testing a model that drives autonomous cars — you would want to ensure that small changes in the environment, like lighting conditions or road markings, don’t cause the car to make dangerous decisions. DeepXplore helps identify these critical issues before they become real-world problems. “Implementing DeepXplore can be challenging due to its complexity and resource demands, making it more suitable for advanced users and critical applications where rigorous testing is essential.” Read more at https://github.com/peikexin9/deepxplore
- CleverHans: Named after a famous horse that was believed to perform arithmetic tasks but was actually responding to subtle cues from its trainer, CleverHans is all about exposing the vulnerabilities in AI models. This tool specializes in generating adversarial examples — inputs that are subtly altered to confuse the model and cause it to make mistakes. By using CleverHans, we can test the robustness of our AI models, ensuring they don’t get easily tricked by unexpected inputs. Read more at https://github.com/cleverhans-lab/cleverhans
- FOOLBOX: FOOLBOX is another tool focused on adversarial testing, but it offers more flexibility and customization. It’s a comprehensive framework that allows us to craft and test various types of adversarial attacks against our models. Whether you’re working with deep learning models in TensorFlow, PyTorch, or any other framework, FOOLBOX provides the tools to ensure your model can withstand attacks and remain reliable under pressure. Read more at https://github.com/bethgelab/foolbox
- Fairness Indicators: Fairness in AI is non-negotiable, and tools like Fairness Indicators help us keep our models in check. This tool provides metrics and visualizations that reveal how different demographic groups are affected by a model’s predictions. For instance, in our hiring algorithm example, Fairness Indicators would show whether the model is treating all candidates equally, regardless of their gender, ethnicity, or age. Read more at https://www.tensorflow.org/responsible_ai/fairness_indicators/tutorials/Fairness_Indicators_Example_Colab
- TensorFlow Model Analysis (TFMA): When you need to analyze the performance of your model across different slices of data, TensorFlow Model Analysis (TFMA) is the go-to tool. TFMA allows us to evaluate and visualize model performance on different subsets of data, such as age groups, income levels, or geographic regions. This detailed analysis helps ensure that the model is not only accurate overall but also performs consistently well across all segments of the population. Read more at https://www.tensorflow.org/tfx/tutorials/model_analysis/tfma_basic
- MLflow: Managing the lifecycle of machine learning models can be a daunting task, but MLflow makes it easier. It helps track experiments, package code into reproducible runs, and manage and deploy models. With MLflow, we can ensure that every version of our AI model is tested thoroughly, and we can trace back any issues to their source quickly. Read more at https://mlflow.org/docs/latest/index.html
Why These Tools Matter
These tools aren’t just helpful — they’re essential for building AI models that are trustworthy, reliable, and fair. They allow us to test our models from every angle, ensuring that they perform well in real-world scenarios, treat everyone fairly, and make decisions that can be understood and trusted by the people who rely on them. In the ever-evolving world of AI, these tools give us the confidence to innovate while keeping safety and ethics at the forefront.
But even with all these powerful tools at our disposal, there’s no straightforward, one-size-fits-all approach to testing AI/ML models. Each model is unique, with its own complexities and potential pitfalls, which means testing must be tailored to fit the specific context and requirements. This often involves a combination of tools and techniques, along with a deep understanding of both the technology and the domain in which it operates. In short, while these tools are invaluable, they are part of a broader, more nuanced strategy to ensure AI models are not only functional but truly reliable and safe.
From Theory to Practice — Roll Up Sleeves for Implementation
When it comes to implementing automated testing for AI/ML models, there’s no one-size-fits-all approach. Each model, depending on its complexity and the domain it operates in, may require a unique combination of tools and techniques. The process often begins with setting up automated data validation to ensure that the data feeding into the model is clean and consistent. From there, you might implement model performance testing to evaluate how well the model meets key metrics like accuracy and precision. Bias and fairness testing requires thoughtful integration of tools that can detect and correct for potential inequities in the model’s decision-making process. Meanwhile, robustness testing and adversarial testing add layers of protection by ensuring the model can handle unexpected or malicious inputs. Finally, model explainability and scalability tests ensure that the model not only performs well but also does so in a way that’s transparent, fair, and scalable to real-world scenarios. While the implementation process can be complex, involving various tools like SHAP, LIME, CleverHans, and TensorFlow Model Analysis, the result is a comprehensive testing framework that helps build reliable, trustworthy AI/ML systems.
Building and Testing a Robust CNN with Adversarial Inputs
1. Importing Required Libraries
torch
: Core PyTorch library for tensor operations.torch.nn
: Defines modules for building neural networks.torch.optim
: Provides optimization algorithms like Adam.torch.nn.functional
: Includes functions like ReLU, cross-entropy loss, etc.torchvision.datasets
: Provides access to datasets like MNIST.torchvision.transforms
: Allows for preprocessing and transforming datasets.torch.utils.data.DataLoader
: Facilitates batch loading and shuffling of datasets.numpy
: Used for numerical operations.
2. Defining the SimpleCNN Model
A simple Convolutional Neural Network (CNN) is defined with:
- 2 Convolutional Layers: Extract features from input images.
- 2 Fully Connected Layers: Perform the final classification.
The forward method defines how data passes through the network.
3. Loading and Normalizing the MNIST Dataset
- Transformations: Images are converted to tensors and normalized with a mean and standard deviation suited for MNIST.
- Dataset and DataLoader:
train_loader
andtest_loader
are created for batching the training and testing datasets, respectively.
4. Initializing the Model, Loss Function, and Optimizer
- Model Initialization: An instance of the
SimpleCNN
class is created. - Optimizer: Adam optimizer is used to update the model parameters during training.
5. Training the Model
- train Function: Runs through one epoch, processing batches of data, calculating loss, backpropagating errors, and updating model weights.
Logs the loss every 100 batches for monitoring.
6. Generating Adversarial Inputs
- generate_adversarial_input Function: Creates adversarial examples by slightly perturbing the input images in the direction of the gradient of the loss with respect to the input.
- Perturbation: A small value
epsilon
is added to the input to create the adversarial example.
The perturbed image is clamped to ensure pixel values stay within valid ranges.
7. Maximizing Neuron Coverage
- maximize_neuron_coverage Function: Iteratively perturbs the input image over multiple iterations to activate different neurons in the model.
- This simulates the DeepXplore approach of maximizing coverage across the model.
Logs each iteration for debugging and insight into the process.
8. Testing the Model
- test_models Function: Tests the model’s robustness by comparing its performance on original and perturbed data.
- Correct Predictions: Counts the number of correct classifications on original and perturbed data.
- Discrepancies: Counts the cases where the model’s prediction changes due to the perturbation.
Logs detailed batch-level information, including the number of discrepancies found.
9. Running the Test
- The
test_models
function is run to evaluate the model’s robustness against adversarial attacks. - After all batches are processed, a summary is printed showing:
- Total number of samples tested.
- Accuracy on original vs. perturbed data.
- Total discrepancies found.
10. Final Output
- The final output helps you understand how resilient the model is to adversarial attacks by providing detailed insights into where the model’s predictions fail when the inputs are slightly modified.
!pip install torch torchvision numpy
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.fc1 = nn.Linear(12*12*64, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
return x
# Load and normalize MNIST dataset
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST('../data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('../data', train=False, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
# Initialize model, loss function, and optimizer
model = SimpleCNN()
optimizer = optim.Adam(model.parameters())
# Train the model (for demonstration purposes)
def train(model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.cross_entropy(output, target)
loss.backward()
optimizer.step()
if batch_idx % 100 == 0:
print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}] Loss: {loss.item():.6f}')
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# Train for 1 epoch for demonstration
train(model, device, train_loader, optimizer, 1)
def generate_adversarial_input(model, input_image, target_label, epsilon=0.1):
input_image.requires_grad = True
output = model(input_image)
loss = nn.CrossEntropyLoss()(output, target_label)
model.zero_grad()
loss.backward()
# Perturb the input image
perturbed_image = input_image + epsilon * input_image.grad.sign()
perturbed_image = torch.clamp(perturbed_image, 0, 1) # Ensure the image remains in valid range
return perturbed_image
def maximize_neuron_coverage(model, input_image, iterations=10, epsilon=0.1):
for i in range(iterations):
print(f"Iteration {i+1}/{iterations}")
output = model(input_image)
perturbed_image = generate_adversarial_input(model, input_image, torch.argmax(output, dim=1))
input_image = perturbed_image.detach() # Detach to avoid accumulating gradients
return input_image
def test_models(model, test_loader):
discrepancies = 0
total_samples = 0
total_correct_original = 0
total_correct_perturbed = 0
for batch_idx, (data, target) in enumerate(test_loader):
data, target = data.to(device), target.to(device)
perturbed_data = maximize_neuron_coverage(model, data.clone())
# Forward pass on original and perturbed data
output_original = model(data)
output_perturbed = model(perturbed_data)
# Predictions
pred_original = torch.argmax(output_original, dim=1)
pred_perturbed = torch.argmax(output_perturbed, dim=1)
# Count correct predictions
correct_original = pred_original.eq(target).sum().item()
correct_perturbed = pred_perturbed.eq(target).sum().item()
# Discrepancies between original and perturbed predictions
discrepancies_batch = pred_original.ne(pred_perturbed).sum().item()
discrepancies += discrepancies_batch
# Update totals
total_samples += data.size(0)
total_correct_original += correct_original
total_correct_perturbed += correct_perturbed
# Detailed logging
print(f'Batch {batch_idx + 1}/{len(test_loader)}:')
print(f' Original Correct Predictions: {correct_original}/{data.size(0)}')
print(f' Perturbed Correct Predictions: {correct_perturbed}/{data.size(0)}')
print(f' Discrepancies in this batch: {discrepancies_batch}/{data.size(0)}\n')
# Final summary
accuracy_original = total_correct_original / total_samples * 100
accuracy_perturbed = total_correct_perturbed / total_samples * 100
print(f'Total samples tested: {total_samples}')
print(f'Total correct predictions (original): {total_correct_original} ({accuracy_original:.2f}%)')
print(f'Total correct predictions (perturbed): {total_correct_perturbed} ({accuracy_perturbed:.2f}%)')
print(f'Total discrepancies found: {discrepancies} ({discrepancies / total_samples * 100:.2f}%)')
# Run the test
test_models(model, test_loader)

You will find this code at below url. We will learn more about it in our next blog….. Stay tuned
About Me🚀 Hello! I’m Toni Ramchandani 👋. I’m deeply passionate about all things technology! My journey is about exploring the vast and dynamic world of tech, from cutting-edge innovations to practical business solutions. I believe in the power of technology to transform our lives and work. 🌐
Let’s connect at https://www.linkedin.com/in/toni-ramchandani/ and exchange ideas about the latest tech trends and advancements! 🌟
Engage & Stay Connected 📢 If you find value in my posts, please Clapp 👏 | Like 👍 and share 📤 them. Your support inspires me to continue sharing insights and knowledge. Follow me for more updates and let’s explore the fascinating world of technology together! 🛰️

This story is published on Generative AI. Connect with us on LinkedIn and follow Zeniteq to stay in the loop with the latest AI stories.
Subscribe to our newsletter and YouTube channel to stay updated with the latest news and updates on generative AI. Let’s shape the future of AI together!
