xbe.at

Data visualisation

Visual Guide to Active Learning in Machine Learning

10 min read, 16 sections

Contents
  1. Introduction to Active Learning in Machine Learning
  2. Source Code for Introduction to Active Learning in Machine Learning
  3. The Active Learning Process
  4. Source Code for The Active Learning Process
  5. Uncertainty Sampling
  6. Source Code for Uncertainty Sampling
  7. Query-by-Committee
  8. Source Code for Query-by-Committee
  9. Real-Life Example: Text Classification
  10. Source Code for Text Classification Example
  11. Real-Life Example: Image Classification
  12. Source Code for Image Classification Example
  13. Challenges and Considerations in Active Learning
  14. Source Code for Challenges and Considerations in Active Learning
  15. Conclusion and Future Directions
  16. Additional Resources

Introduction to Active Learning in Machine Learning

Active learning is a machine learning approach that interactively queries a user or other information source to label new data points. This method is particularly useful when dealing with unlabeled datasets, as it allows for efficient and targeted labeling of the most informative examples. Active learning aims to achieve high accuracy with a minimal amount of labeled training data, making it a cost-effective solution for many real-world applications.

Source Code for Introduction to Active Learning in Machine Learning

import random

class ActiveLearner:
    def __init__(self, unlabeled_data):
        self.labeled_data = []
        self.unlabeled_data = unlabeled_data
        self.model = None

    def initialize_seed(self, num_samples=10):
        # Randomly select initial samples for labeling
        seed_samples = random.sample(self.unlabeled_data, num_samples)
        for sample in seed_samples:
            label = self.query_oracle(sample)
            self.labeled_data.append((sample, label))
            self.unlabeled_data.remove(sample)

    def query_oracle(self, sample):
        # Simulating human labeling
        return input(f"Please label this sample ({sample}): ")

    def train_model(self):
        # Placeholder for model training
        print("Training model with", len(self.labeled_data), "labeled samples")
        self.model = "Trained Model"

    def predict(self, sample):
        # Placeholder for model prediction
        return random.random()  # Returns a confidence score between 0 and 1

    def select_samples(self, num_samples=5):
        # Select samples with lowest confidence for labeling
        predictions = [(sample, self.predict(sample)) for sample in self.unlabeled_data]
        sorted_predictions = sorted(predictions, key=lambda x: x[1])
        return [sample for sample, _ in sorted_predictions[:num_samples]]

    def active_learning_loop(self, iterations=5):
        for _ in range(iterations):
            self.train_model()
            samples_to_label = self.select_samples()
            for sample in samples_to_label:
                label = self.query_oracle(sample)
                self.labeled_data.append((sample, label))
                self.unlabeled_data.remove(sample)

# Example usage
unlabeled_data = [f"Sample_{i}" for i in range(100)]
learner = ActiveLearner(unlabeled_data)
learner.initialize_seed()
learner.active_learning_loop()

The Active Learning Process

The active learning process involves several key steps:

  1. Initial seed labeling: A small subset of the unlabeled data is manually labeled to create an initial training set.
  2. Model training: A machine learning model is trained on the currently labeled data.
  3. Prediction and confidence estimation: The model generates predictions and confidence scores for the remaining unlabeled data.
  4. Sample selection: Samples with the lowest confidence scores are selected for labeling.
  5. Oracle querying: The selected samples are presented to a human expert (oracle) for labeling.
  6. Iteration: Steps 2-5 are repeated until a satisfactory model performance is achieved or a labeling budget is exhausted.

This iterative process allows the model to focus on the most informative examples, improving its performance efficiently.

Source Code for The Active Learning Process

import random

def simulate_oracle(sample):
    # Simulates a human expert labeling a sample
    return random.choice(["positive", "negative"])

def train_model(labeled_data):
    # Placeholder for model training
    print(f"Training model with {len(labeled_data)} samples")
    return "Trained Model"

def predict_with_confidence(model, sample):
    # Placeholder for model prediction with confidence
    return random.random()

def active_learning_process(unlabeled_data, initial_seed_size=10, iterations=5, samples_per_iteration=5):
    labeled_data = []
    
    # Step 1: Initial seed labeling
    for _ in range(initial_seed_size):
        sample = unlabeled_data.pop()
        label = simulate_oracle(sample)
        labeled_data.append((sample, label))
    
    for iteration in range(iterations):
        # Step 2: Model training
        model = train_model(labeled_data)
        
        # Step 3: Prediction and confidence estimation
        confidences = [(sample, predict_with_confidence(model, sample)) for sample in unlabeled_data]
        
        # Step 4: Sample selection
        selected_samples = sorted(confidences, key=lambda x: x[1])[:samples_per_iteration]
        
        # Step 5: Oracle querying
        for sample, _ in selected_samples:
            label = simulate_oracle(sample)
            labeled_data.append((sample, label))
            unlabeled_data.remove(sample)
        
        print(f"Iteration {iteration + 1}: Labeled {len(labeled_data)} samples")
    
    return labeled_data, train_model(labeled_data)

# Example usage
unlabeled_data = [f"Sample_{i}" for i in range(1000)]
final_labeled_data, final_model = active_learning_process(unlabeled_data)
print(f"Final labeled data size: {len(final_labeled_data)}")

Uncertainty Sampling

Uncertainty sampling is a common strategy in active learning for selecting the most informative samples. This method chooses instances about which the current model is least certain. For classification tasks, uncertainty can be measured using metrics such as entropy or the difference between the top two class probabilities. By focusing on uncertain samples, the model can quickly improve its decision boundaries and overall performance.

Source Code for Uncertainty Sampling

import math
import random

def entropy(probabilities):
    return -sum(p * math.log2(p) for p in probabilities if p > 0)

def margin_sampling(probabilities):
    sorted_probs = sorted(probabilities, reverse=True)
    return sorted_probs[0] - sorted_probs[1]

class UncertaintySampler:
    def __init__(self, strategy='entropy'):
        self.strategy = strategy
    
    def get_uncertainty(self, probabilities):
        if self.strategy == 'entropy':
            return entropy(probabilities)
        elif self.strategy == 'margin':
            return -margin_sampling(probabilities)  # Negative so that higher values indicate more uncertainty
        else:
            raise ValueError("Unknown uncertainty strategy")

    def select_samples(self, unlabeled_data, model, k):
        uncertainties = []
        for sample in unlabeled_data:
            probabilities = model.predict_proba(sample)  # Assume model has this method
            uncertainty = self.get_uncertainty(probabilities)
            uncertainties.append((sample, uncertainty))
        
        sorted_samples = sorted(uncertainties, key=lambda x: x[1], reverse=True)
        return [sample for sample, _ in sorted_samples[:k]]

# Example usage
def mock_model_predict_proba(sample):
    # Mock function to simulate model predictions
    return [random.random() for _ in range(3)]

class MockModel:
    def predict_proba(self, sample):
        probs = mock_model_predict_proba(sample)
        return [p / sum(probs) for p in probs]  # Normalize to ensure sum is 1

unlabeled_data = [f"Sample_{i}" for i in range(100)]
model = MockModel()
sampler = UncertaintySampler(strategy='entropy')
selected_samples = sampler.select_samples(unlabeled_data, model, k=5)

print("Selected samples:", selected_samples)

Query-by-Committee

Query-by-Committee (QBC) is an active learning approach that uses multiple models (a committee) to select the most informative samples. Each committee member votes on the classification of unlabeled instances, and the samples with the highest disagreement among committee members are selected for labeling. This method helps to explore different regions of the hypothesis space and can lead to more robust models.

Source Code for Query-by-Committee

import random
from collections import Counter

class CommitteeMember:
    def __init__(self, name):
        self.name = name
    
    def predict(self, sample):
        # Simulate prediction (in reality, this would be a trained model)
        return random.choice(['A', 'B', 'C'])

class QueryByCommittee:
    def __init__(self, committee_members):
        self.committee = committee_members
    
    def vote(self, sample):
        return [member.predict(sample) for member in self.committee]
    
    def disagreement(self, votes):
        vote_counts = Counter(votes)
        most_common = vote_counts.most_common(1)[0][1]
        return 1 - (most_common / len(votes))
    
    def select_samples(self, unlabeled_data, k):
        sample_disagreements = []
        for sample in unlabeled_data:
            votes = self.vote(sample)
            disagreement = self.disagreement(votes)
            sample_disagreements.append((sample, disagreement))
        
        sorted_samples = sorted(sample_disagreements, key=lambda x: x[1], reverse=True)
        return [sample for sample, _ in sorted_samples[:k]]

# Example usage
committee_members = [CommitteeMember(f"Model_{i}") for i in range(5)]
qbc = QueryByCommittee(committee_members)

unlabeled_data = [f"Sample_{i}" for i in range(100)]
selected_samples = qbc.select_samples(unlabeled_data, k=5)

print("Selected samples:", selected_samples)

Real-Life Example: Text Classification

Active learning is particularly useful in text classification tasks, such as sentiment analysis or spam detection. In these scenarios, labeling large amounts of text data can be time-consuming and expensive. By using active learning, we can focus on labeling the most informative examples, allowing us to build effective classifiers with minimal human effort.

Source Code for Text Classification Example

import random
from collections import Counter

class TextClassifier:
    def __init__(self):
        self.word_counts = Counter()
        self.class_counts = Counter()
    
    def train(self, labeled_data):
        for text, label in labeled_data:
            words = text.lower().split()
            self.word_counts.update(words)
            self.class_counts[label] += 1
    
    def predict_proba(self, text):
        words = text.lower().split()
        class_scores = {c: 1 for c in self.class_counts.keys()}
        
        for word in words:
            for class_ in class_scores:
                if word in self.word_counts:
                    class_scores[class_] *= (self.word_counts[word] + 1) / (self.class_counts[class_] + len(self.word_counts))
        
        total = sum(class_scores.values())
        return {c: s/total for c, s in class_scores.items()}

def entropy(probabilities):
    return -sum(p * math.log2(p) for p in probabilities.values() if p > 0)

class ActiveTextLearner:
    def __init__(self, unlabeled_data):
        self.labeled_data = []
        self.unlabeled_data = unlabeled_data
        self.model = TextClassifier()
    
    def query_oracle(self, text):
        # Simulating human labeling
        return input(f"Please label this text (positive/negative): {text}\n")
    
    def select_sample(self):
        max_entropy = -1
        selected_sample = None
        
        for sample in self.unlabeled_data:
            probs = self.model.predict_proba(sample)
            sample_entropy = entropy(probs)
            if sample_entropy > max_entropy:
                max_entropy = sample_entropy
                selected_sample = sample
        
        return selected_sample
    
    def active_learning_loop(self, iterations=5):
        for _ in range(iterations):
            sample = self.select_sample()
            label = self.query_oracle(sample)
            self.labeled_data.append((sample, label))
            self.unlabeled_data.remove(sample)
            self.model.train(self.labeled_data)
            print(f"Iteration complete. Labeled data size: {len(self.labeled_data)}")

# Example usage
unlabeled_data = [
    "This product is amazing!",
    "I hate this service.",
    "The quality is okay.",
    "Worst experience ever.",
    "Highly recommended!",
    "Not sure how I feel about this.",
    "Could be better.",
    "Absolutely love it!",
    "Never buying this again.",
    "Mixed feelings about the results."
]

learner = ActiveTextLearner(unlabeled_data)
learner.active_learning_loop()

Real-Life Example: Image Classification

Active learning is also valuable in image classification tasks, especially when dealing with large datasets or specialized domains where expert annotation is required. For instance, in medical imaging, active learning can help prioritize the most challenging or ambiguous cases for expert review, improving the efficiency of the labeling process and the overall model performance.

Source Code for Image Classification Example

import random
from collections import Counter

class MockImageClassifier:
    def __init__(self):
        self.classes = ['dog', 'cat', 'bird']
    
    def predict_proba(self, image):
        # Simulate prediction probabilities
        probs = [random.random() for _ in self.classes]
        total = sum(probs)
        return {c: p/total for c, p in zip(self.classes, probs)}

def entropy(probabilities):
    return -sum(p * math.log2(p) for p in probabilities.values() if p > 0)

class ActiveImageLearner:
    def __init__(self, unlabeled_images):
        self.labeled_images = []
        self.unlabeled_images = unlabeled_images
        self.model = MockImageClassifier()
    
    def query_oracle(self, image):
        # Simulating expert labeling
        return input(f"Please label this image (dog/cat/bird): {image}\n")
    
    def select_sample(self):
        max_entropy = -1
        selected_sample = None
        
        for image in self.unlabeled_images:
            probs = self.model.predict_proba(image)
            image_entropy = entropy(probs)
            if image_entropy > max_entropy:
                max_entropy = image_entropy
                selected_sample = image
        
        return selected_sample
    
    def active_learning_loop(self, iterations=5):
        for i in range(iterations):
            sample = self.select_sample()
            label = self.query_oracle(sample)
            self.labeled_images.append((sample, label))
            self.unlabeled_images.remove(sample)
            print(f"Iteration {i+1} complete. Labeled images: {len(self.labeled_images)}")

# Example usage
unlabeled_images = [f"image_{i}.jpg" for i in range(100)]
learner = ActiveImageLearner(unlabeled_images)
learner.active_learning_loop()

Challenges and Considerations in Active Learning

While active learning offers many benefits, it also comes with challenges:

  1. Cold start problem: Initially, the model may not have enough information to make informed decisions about which samples to query.
  2. Sampling bias: The active learning process may introduce bias by focusing on certain types of examples.
  3. Batch mode vs. stream-based learning: Deciding whether to label samples in batches or one at a time can affect efficiency and model performance.
  4. Stopping criteria: Determining when to stop the active learning process is crucial for balancing performance and labeling effort.
  5. Human factors: The quality and consistency of human annotations can impact the effectiveness of active learning.

Addressing these challenges is essential for successful implementation of active learning in real-world scenarios.

Source Code for Challenges and Considerations in Active Learning

import random
from collections import Counter

class ActiveLearner:
    def __init__(self, unlabeled_data):
        self.labeled_data = []
        self.unlabeled_data = unlabeled_data
        self.model = None
        self.iteration = 0
        self.performance_history = []

    def cold_start(self, n_samples=10):
        # Addressing cold start problem
        initial_samples = random.sample(self.unlabeled_data, n_samples)
        for sample in initial_samples:
            label = self.query_oracle(sample)
            self.labeled_data.append((sample, label))
            self.unlabeled_data.remove(sample)

    def query_oracle(self, sample):
        # Simulating human labeling with potential inconsistencies
        return random.choice(['A', 'B', 'C'])

    def train_model(self):
        # Placeholder for model training
        self.model = "Trained Model"

    def evaluate_model(self):
        # Placeholder for model evaluation
        return random.random()

    def select_samples(self, batch_size=5):
        # Batch mode sample selection
        return random.sample(self.unlabeled_data, min(batch_size, len(self.unlabeled_data)))

    def check_stopping_criteria(self):
        # Example stopping criteria
        if self.iteration > 20 or len(self.labeled_data) > 100:
            return True
        if len(self.performance_history) > 5:
            if max(self.performance_history[-5:]) - min(self.performance_history[-5:]) < 0.01:
                return True
        return False

    def active_learning_loop(self):
        self.cold_start()
        while not self.check_stopping_criteria():
            self.train_model()
            performance = self.evaluate_model()
            self.performance_history.append(performance)
            
            samples_to_label = self.select_samples()
            for sample in samples_to_label:
                label = self.query_oracle(sample)
                self.labeled_data.append((sample, label))
                self.unlabeled_data.remove(sample)
            
            self.iteration += 1
            print(f"Iteration {self.iteration}: Performance = {performance:.4f}")

# Example usage
unlabeled_data = [f"Sample_{i}" for i in range(1000)]
learner = ActiveLearner(unlabeled_data)
learner.active_learning_loop()

Conclusion and Future Directions

Active learning has proven to be a powerful technique for efficiently building machine learning models with limited labeled data. As the field continues to evolve, several exciting directions are emerging:

  1. Integration with transfer learning and few-shot learning techniques
  2. Exploration of more sophisticated query strategies
  3. Development of active learning methods for complex tasks like object detection and segmentation
  4. Incorporation of human-in-the-loop feedback beyond simple labeling
  5. Application of active learning in reinforcement learning scenarios

These advancements promise to further enhance the efficiency and effectiveness of machine learning model development across various domains.

Additional Resources

For those interested in diving deeper into active learning, here are some valuable resources:

  1. Settles, B. (2009). Active Learning Literature Survey. Computer Sciences Technical Report 1648, University of Wisconsin–Madison. ArXiv: https://arxiv.org/abs/0912.0745
  2. Ren, P., et al. (2021). A Survey of Deep Active Learning. ArXiv: https://arxiv.org/abs/2009.00236
  3. Gal, Y., Islam, R., & Ghahramani, Z. (2017). Deep Bayesian Active Learning with Image Data. ArXiv: https://arxiv.org/abs/1703.02910
  4. Sener, O., & Savarese, S. (2018). Active Learning for Convolutional Neural Networks: A Core-Set Approach. ArXiv: https://arxiv.org/abs/1708.00489

These papers provide a comprehensive overview of active learning techniques, theoretical foundations, and recent advancements in the field.

More in Data visualisation

  1. Visualizing Borel Sets in Python7 min

    Borel sets are fundamental in measure theory and probability. They form a σ-algebra generated by open sets in a topological space. Let's explore their...

  2. Visual Demonstration of DBSCAN Clustering6 min

    DBSCAN (Density-Based Spatial Clustering of Applications with Noise) is a powerful clustering algorithm that groups data points based on density. Unlike...

  3. Visualizing Classifier Performance with ROC Plots11 min

    ROC (Receiver Operating Characteristic) plots are powerful tools for evaluating binary classification models. They visualize the trade-off between true...

  4. Understanding Skewness in Data Visualization11 min

    Skewness measures the asymmetry of a probability distribution, indicating whether data leans left or right. In statistical analysis, understanding skewness...

  5. Visualizing Complex Analysis with Python6 min

    Complex analysis is a branch of mathematics that studies functions of complex numbers. It has wide-ranging applications in physics, engineering, and other...

  6. Time Series Visualization in Python From Raw Data to Insights8 min

    Time series visualization is a powerful tool for understanding and analyzing data that changes over time. It allows us to identify patterns, trends, and...