xbe.at

Machine learning / Feature engineering and preprocessing

Data Augmentation Boosting Model Performance with Python

10 min read, 15 sections

Contents
  1. Introduction to Data Augmentation
  2. Image Augmentation Basics
  3. Text Augmentation Techniques
  4. Data Augmentation for Time Series
  5. Augmentation for Natural Language Processing
  6. Augmentation in Audio Processing
  7. Geometric Transformations for Image Augmentation
  8. Color Space Transformations
  9. Noise Injection
  10. Mixup Augmentation
  11. Real-life Example: Augmenting Medical Images
  12. Real-life Example: Augmenting Satellite Imagery
  13. Balancing Augmentation and Overfitting
  14. Evaluating the Impact of Data Augmentation
  15. Additional Resources

Introduction to Data Augmentation

Data augmentation is a powerful technique that enhances model performance by creating new training examples from existing data. It involves applying various transformations to the original dataset, effectively increasing its size and diversity without collecting additional data. This process helps models learn more robust features and generalize better to unseen examples.

import random

def simple_text_augmentation(text):
    # Simulate a simple text augmentation by randomly capitalizing words
    words = text.split()
    augmented_words = [word.upper() if random.random() > 0.5 else word for word in words]
    return ' '.join(augmented_words)

original_text = "The quick brown fox jumps over the lazy dog"
augmented_text = simple_text_augmentation(original_text)

print(f"Original: {original_text}")
print(f"Augmented: {augmented_text}")

Image Augmentation Basics

Image augmentation is one of the most common applications of data augmentation. It involves applying various transformations to images, such as rotation, flipping, scaling, and color adjustments. These transformations create new variations of the original images, helping the model learn invariance to these changes.

from PIL import Image, ImageEnhance

def augment_image(image_path):
    # Open the image
    img = Image.open(image_path)
    
    # Rotate the image
    rotated_img = img.rotate(45)
    
    # Flip the image horizontally
    flipped_img = img.transpose(Image.FLIP_LEFT_RIGHT)
    
    # Adjust brightness
    enhancer = ImageEnhance.Brightness(img)
    brightened_img = enhancer.enhance(1.5)
    
    return rotated_img, flipped_img, brightened_img

# Usage
original_img_path = "path/to/your/image.jpg"
rotated, flipped, brightened = augment_image(original_img_path)

# Save augmented images
rotated.save("rotated_image.jpg")
flipped.save("flipped_image.jpg")
brightened.save("brightened_image.jpg")

Text Augmentation Techniques

Text augmentation involves creating new text samples by applying various transformations to existing text data. Common techniques include synonyms replacement, back-translation, and random insertion or deletion of words. These methods help models become more robust to variations in language.

import random

def synonym_replacement(text, num_replacements=1):
    words = text.split()
    synonyms = {
        "quick": ["fast", "speedy", "swift"],
        "lazy": ["idle", "sluggish", "slothful"]
    }
    
    for _ in range(num_replacements):
        replaceable_words = [word for word in words if word in synonyms]
        if replaceable_words:
            word_to_replace = random.choice(replaceable_words)
            replacement = random.choice(synonyms[word_to_replace])
            words[words.index(word_to_replace)] = replacement
    
    return " ".join(words)

original_text = "The quick brown fox jumps over the lazy dog"
augmented_text = synonym_replacement(original_text, num_replacements=2)

print(f"Original: {original_text}")
print(f"Augmented: {augmented_text}")

Data Augmentation for Time Series

Time series data augmentation involves creating new sequences by applying transformations such as time warping, magnitude warping, or adding noise. These techniques help models learn invariance to time shifts and amplitude changes, improving their ability to generalize across different time series patterns.

import random
import math

def time_warp(sequence, sigma=0.2, knot=4):
    length = len(sequence)
    warp = [1.0]
    for i in range(knot):
        warp.append(random.gauss(0, sigma))
    warp.append(1.0)
    warp = sorted(warp)
    warped = []
    for i, p in enumerate(warp):
        start = math.floor(length * p)
        end = math.floor(length * warp[i + 1]) if i < knot else length
        warped.extend(sequence[start:end])
    return warped

# Example usage
original_sequence = [i for i in range(100)]
warped_sequence = time_warp(original_sequence)

print(f"Original length: {len(original_sequence)}")
print(f"Warped length: {len(warped_sequence)}")
print(f"First 10 original: {original_sequence[:10]}")
print(f"First 10 warped: {warped_sequence[:10]}")

Augmentation for Natural Language Processing

In Natural Language Processing (NLP), data augmentation techniques help create diverse text samples while preserving the original meaning. Methods like word substitution, sentence paraphrasing, and back-translation can significantly improve model performance on various NLP tasks.

import random

def word_dropout(text, dropout_rate=0.1):
    words = text.split()
    augmented_words = [word for word in words if random.random() > dropout_rate]
    return ' '.join(augmented_words)

def random_swap(text, n_swaps=1):
    words = text.split()
    for _ in range(n_swaps):
        if len(words) > 1:
            idx1, idx2 = random.sample(range(len(words)), 2)
            words[idx1], words[idx2] = words[idx2], words[idx1]
    return ' '.join(words)

original_text = "The quick brown fox jumps over the lazy dog"
dropped_text = word_dropout(original_text)
swapped_text = random_swap(original_text)

print(f"Original: {original_text}")
print(f"Word Dropout: {dropped_text}")
print(f"Random Swap: {swapped_text}")

Augmentation in Audio Processing

Audio data augmentation involves creating new audio samples by applying various transformations to existing recordings. Techniques such as time stretching, pitch shifting, and adding background noise can help models become more robust to variations in audio input.

import wave
import struct
import random

def add_noise(audio_path, output_path, noise_factor=0.005):
    with wave.open(audio_path, 'rb') as wf:
        params = wf.getparams()
        frames = wf.readframes(params.nframes)
    
    # Convert binary data to list of integers
    audio_data = list(struct.unpack(f"{params.nframes}h", frames))
    
    # Add random noise
    noisy_audio = [int(sample + random.uniform(-1, 1) * noise_factor * 32767) for sample in audio_data]
    
    # Clip values to prevent overflow
    noisy_audio = [max(min(sample, 32767), -32768) for sample in noisy_audio]
    
    # Convert back to binary data
    noisy_frames = struct.pack(f"{params.nframes}h", *noisy_audio)
    
    # Write the noisy audio to a new file
    with wave.open(output_path, 'wb') as wf:
        wf.setparams(params)
        wf.writeframes(noisy_frames)

# Usage
add_noise("input_audio.wav", "noisy_audio.wav")
print("Noisy audio created and saved as 'noisy_audio.wav'")

Geometric Transformations for Image Augmentation

Geometric transformations are essential in image augmentation. They include operations like rotation, scaling, shearing, and translation. These transformations help models learn spatial invariance, improving their ability to recognize objects regardless of their position or orientation in the image.

from PIL import Image

def geometric_augmentation(image_path, output_path):
    with Image.open(image_path) as img:
        # Rotate
        rotated = img.rotate(30)
        
        # Scale
        width, height = img.size
        scaled = img.resize((int(width * 1.2), int(height * 1.2)))
        
        # Shear
        sheared = img.transform(
            img.size,
            Image.AFFINE,
            (1, 0.2, 0, 0, 1, 0),
            Image.BICUBIC
        )
        
        # Translate
        translated = img.transform(
            img.size,
            Image.AFFINE,
            (1, 0, 50, 0, 1, 50),
            Image.BICUBIC
        )
        
        # Combine all transformations
        combined = Image.new('RGB', (width * 2, height * 2))
        combined.paste(rotated, (0, 0))
        combined.paste(scaled, (width, 0))
        combined.paste(sheared, (0, height))
        combined.paste(translated, (width, height))
        
        combined.save(output_path)

# Usage
geometric_augmentation("input_image.jpg", "augmented_image.jpg")
print("Augmented image created and saved as 'augmented_image.jpg'")

Color Space Transformations

Color space transformations are another important aspect of image augmentation. These include adjusting brightness, contrast, saturation, and hue. Such transformations help models become invariant to lighting conditions and color variations, leading to more robust performance across different environments.

from PIL import Image, ImageEnhance

def color_augmentation(image_path, output_path):
    with Image.open(image_path) as img:
        # Brightness adjustment
        brightness_enhancer = ImageEnhance.Brightness(img)
        brightened = brightness_enhancer.enhance(1.5)
        
        # Contrast adjustment
        contrast_enhancer = ImageEnhance.Contrast(img)
        contrasted = contrast_enhancer.enhance(1.2)
        
        # Color (saturation) adjustment
        color_enhancer = ImageEnhance.Color(img)
        saturated = color_enhancer.enhance(1.5)
        
        # Combine all transformations
        width, height = img.size
        combined = Image.new('RGB', (width * 2, height * 2))
        combined.paste(img, (0, 0))
        combined.paste(brightened, (width, 0))
        combined.paste(contrasted, (0, height))
        combined.paste(saturated, (width, height))
        
        combined.save(output_path)

# Usage
color_augmentation("input_image.jpg", "color_augmented_image.jpg")
print("Color augmented image created and saved as 'color_augmented_image.jpg'")

Noise Injection

Noise injection is a technique used to improve model robustness by adding random perturbations to the input data. This can be applied to various data types, including images, audio, and numerical data. By exposing the model to noisy inputs during training, it learns to focus on essential features and becomes more resilient to noise in real-world scenarios.

import numpy as np
from PIL import Image

def add_gaussian_noise(image_path, output_path, mean=0, std=25):
    with Image.open(image_path) as img:
        img_array = np.array(img)
        
        # Generate Gaussian noise
        noise = np.random.normal(mean, std, img_array.shape).astype(np.uint8)
        
        # Add noise to the image
        noisy_img_array = np.clip(img_array + noise, 0, 255).astype(np.uint8)
        
        # Create a new image from the noisy array
        noisy_img = Image.fromarray(noisy_img_array)
        noisy_img.save(output_path)

# Usage
add_gaussian_noise("input_image.jpg", "noisy_image.jpg")
print("Noisy image created and saved as 'noisy_image.jpg'")

Mixup Augmentation

Mixup is an advanced augmentation technique that creates new training examples by linearly interpolating between pairs of images and their labels. This method helps the model learn smoother decision boundaries and improves generalization, especially in classification tasks.

import numpy as np
from PIL import Image

def mixup_images(image1_path, image2_path, output_path, alpha=0.2):
    with Image.open(image1_path) as img1, Image.open(image2_path) as img2:
        # Ensure images are the same size
        img2 = img2.resize(img1.size)
        
        # Convert images to numpy arrays
        array1 = np.array(img1)
        array2 = np.array(img2)
        
        # Generate mixup weight
        lam = np.random.beta(alpha, alpha)
        
        # Perform mixup
        mixed_array = (lam * array1 + (1 - lam) * array2).astype(np.uint8)
        
        # Create a new image from the mixed array
        mixed_img = Image.fromarray(mixed_array)
        mixed_img.save(output_path)
        
        return lam

# Usage
lam = mixup_images("image1.jpg", "image2.jpg", "mixup_image.jpg")
print(f"Mixup image created with lambda {lam:.2f} and saved as 'mixup_image.jpg'")

Real-life Example: Augmenting Medical Images

In medical imaging, data augmentation is crucial due to limited datasets and the need for robust models. By applying various transformations to medical scans, we can create a more diverse training set, helping models generalize better across different patients and scanning conditions.

from PIL import Image, ImageEnhance, ImageOps

def augment_medical_image(image_path, output_prefix):
    with Image.open(image_path) as img:
        # Rotate
        rotated = img.rotate(10)
        rotated.save(f"{output_prefix}_rotated.png")
        
        # Adjust contrast
        contrast_enhancer = ImageEnhance.Contrast(img)
        contrasted = contrast_enhancer.enhance(1.2)
        contrasted.save(f"{output_prefix}_contrasted.png")
        
        # Flip horizontally
        flipped = ImageOps.mirror(img)
        flipped.save(f"{output_prefix}_flipped.png")
        
        # Crop
        width, height = img.size
        cropped = img.crop((width*0.1, height*0.1, width*0.9, height*0.9))
        cropped = cropped.resize((width, height))
        cropped.save(f"{output_prefix}_cropped.png")

# Usage
augment_medical_image("brain_scan.png", "augmented_scan")
print("Augmented medical images created with prefix 'augmented_scan'")

Real-life Example: Augmenting Satellite Imagery

Satellite imagery augmentation is essential for tasks like land use classification and object detection. By applying various transformations, we can simulate different viewing angles, atmospheric conditions, and seasonal changes, improving model performance across diverse geographical regions and time periods.

from PIL import Image, ImageEnhance, ImageOps

def augment_satellite_image(image_path, output_prefix):
    with Image.open(image_path) as img:
        # Rotate to simulate different viewing angles
        rotated = img.rotate(45)
        rotated.save(f"{output_prefix}_rotated.png")
        
        # Adjust brightness to simulate different lighting conditions
        brightness_enhancer = ImageEnhance.Brightness(img)
        brightened = brightness_enhancer.enhance(1.3)
        brightened.save(f"{output_prefix}_brightened.png")
        
        # Adjust color to simulate seasonal changes
        color_enhancer = ImageEnhance.Color(img)
        color_shifted = color_enhancer.enhance(0.8)
        color_shifted.save(f"{output_prefix}_color_shifted.png")
        
        # Flip to increase spatial diversity
        flipped = ImageOps.flip(img)
        flipped.save(f"{output_prefix}_flipped.png")

# Usage
augment_satellite_image("satellite_image.png", "augmented_satellite")
print("Augmented satellite images created with prefix 'augmented_satellite'")

Balancing Augmentation and Overfitting

While data augmentation is powerful, it's crucial to strike a balance to avoid overfitting or introducing unwanted biases. Excessive or inappropriate augmentation can lead to models learning unrealistic patterns. It's important to validate the augmentation techniques and their parameters on a separate validation set to ensure they genuinely improve model performance.

import random

def balanced_augmentation(data, augmentation_functions, max_augmentations=3):
    augmented_data = []
    for item in data:
        # Always include the original item
        augmented_data.append(item)
        
        # Randomly apply a subset of augmentation functions
        num_augmentations = random.randint(1, min(max_augmentations, len(augmentation_functions)))
        selected_augmentations = random.sample(augmentation_functions, num_augmentations)
        
        for aug_func in selected_augmentations:
            augmented_item = aug_func(item)
            augmented_data.append(augmented_item)
    
    return augmented_data

# Example usage (pseudo-code)
# augmentation_functions = [rotate, flip, adjust_brightness, add_noise]
# original_data = load_data()
# augmented_dataset = balanced_augmentation(original_data, augmentation_functions)
# train_model(augmented_dataset)

Evaluating the Impact of Data Augmentation

To ensure that data augmentation is beneficial, it's crucial to evaluate its impact on model performance. This involves comparing models trained with and without augmentation, as well as analyzing performance on both augmented and non-augmented test sets. Metrics such as accuracy, precision, recall, and F1-score can help quantify the improvements gained from augmentation.

def evaluate_augmentation_impact(original_data, augmented_data, model_class):
    # Split data into train and test sets
    train_original, test_original = split_data(original_data)
    train_augmented, test_augmented = split_data(augmented_data)
    
    # Train and evaluate model without augmentation
    model_original = model_class()
    model_original.train(train_original)
    score_original = model_original.evaluate(test_original)
    
    # Train and evaluate model with augmentation
    model_augmented = model_class()
    model_augmented.train(train_augmented)
    score_augmented = model_augmented.evaluate(test_augmented)
    
    # Compare performance
    improvement = score_augmented - score_original
    return improvement

# Example usage (pseudo-code)
# original_data = load_data()
# augmented_data = apply_augmentation(original_data)
# improvement = evaluate_augmentation_impact(original_data, augmented_data, MyModelClass)
# print(f"Performance improvement: {improvement}")

Additional Resources

For those interested in delving deeper into data augmentation techniques and their applications, here are some valuable resources:

  1. "A survey on Image Data Augmentation for Deep Learning" by C. Shorten and T. M. Khoshgoftaar (2019) ArXiv: https://arxiv.org/abs/1912.05230
  2. "Data Augmentation for Machine Learning" by Y. Wu et al. (2020) ArXiv: https://arxiv.org/abs/2006.06165
  3. "AutoAugment: Learning Augmentation Strategies from Data" by E. D. Cubuk et al. (2018) ArXiv: https://arxiv.org/abs/1805.09501

These papers provide comprehensive overviews of various data augmentation techniques, their theoretical foundations, and practical applications across different domains of machine learning.

More in Feature engineering and preprocessing

  1. Data Cleansing and Transformation for Machine Learning14 min

    Data preparation begins with loading and assessing the raw data to understand its structure, identify potential issues, and plan the necessary cleaning...

  2. 8 Advanced Feature Engineering for Machine Learning9 min

    Polynomial features capture nonlinear relationships in data. They create new features by combining existing ones, raising them to powers, or multiplying them...

  3. Data Preprocessing Techniques in Machine Learning9 min

    StandardScaler transforms features by removing the mean and scaling to unit variance, resulting in a distribution with a mean of 0 and standard deviation of...

  4. 5 Reasons for Feature Selection in Machine Learning14 min

    Feature selection significantly reduces computational complexity and training time by eliminating irrelevant or redundant features. This process becomes...

  5. Data Version Control for Machine Learning with Python7 min

    Data Version Control (DVC) is a powerful tool for managing and versioning datasets and machine learning models. It helps data scientists and ML engineers...

  6. Data Warehousing and Dimensional Modeling for Machine Learning Using Python5 min

    Introduction to Data Warehousing and Dimensional Modeling