Navigation

Introduction to AI

Machine Learning

Deep Learning

Generative AI

Tools & Frameworks

General

Data Augmentation in Deep Learning

Data augmentation artificially expands the training dataset by applying transformations. This improves generalization, acts as a regularizer, and is especially valuable when data is limited.

Image Augmentations

Basic Geometric Transformations

import torchvision.transforms as T

basic_augmentations = T.Compose([
    T.RandomHorizontalFlip(p=0.5),
    T.RandomRotation(degrees=15),
    T.RandomAffine(degrees=0, translate=(0.1, 0.1)),
    T.RandomResize((224, 224)),
])

# Random crop with resize
crop_transform = T.Compose([
    T.RandomResizedCrop(size=224, scale=(0.8, 1.0)),
    T.RandomHorizontalFlip(),
])

Color Jittering

color_jitter = T.Compose([
    T.RandomHorizontalFlip(),
    T.ColorJitter(
        brightness=0.2,
        contrast=0.2,
        saturation=0.2,
        hue=0.05
    ),
])

Cutout and Random Erasing

class RandomErasing:
    def __init__(self, p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3)):
        self.p = p
        self.scale = scale
        self.ratio = ratio
    
    def __call__(self, img):
        if random.random() > self.p:
            return img
        
        c, h, w = img.shape
        area = h * w
        
        for _ in range(10):
            erase_area = random.uniform(*self.scale) * area
            aspect = random.uniform(*self.ratio)
            
            eh = int(math.sqrt(erase_area * aspect))
            ew = int(math.sqrt(erase_area / aspect))
            
            if eh < h and ew < w:
                i = random.randint(0, h - eh)
                j = random.randint(0, w - ew)
                img[:, i:i+eh, j:j+ew] = 0
                return img
        
        return img


# PyTorch built-in
from torchvision.transforms import RandomErasing
erasing = RandomErasing(p=0.5)

Mixup

Mix two images and their labels:

$$x_{mixed} = \lambda x_i + (1-\lambda)x_j$$ $$y_{mixed} = \lambda y_i + (1-\lambda)y_j$$

def mixup_data(x, y, alpha=0.2):
    lam = np.random.beta(alpha, alpha)
    batch_size = x.size(0)
    index = torch.randperm(batch_size)
    
    mixed_x = lam * x + (1 - lam) * x[index]
    y_a, y_b = y, y[index]
    
    return mixed_x, y_a, y_b, lam


def mixup_criterion(criterion, pred, y_a, y_b, lam):
    return lam * criterion(pred, y_a) + (1 - lam) * criterion(pred, y_b)

CutMix

Cut a region from one image and paste it onto another:

def cutmix_data(x, y, beta=1.0):
    lam = np.random.beta(beta, beta)
    batch_size = x.size(0)
    index = torch.randperm(batch_size)
    
    # Generate bounding box
    bbx1, bby1, bbx2, bby2 = rand_bbox(x.size(), lam)
    
    # Mix images
    x[:, :, bbx1:bbx2, bby1:bby2] = x[index, :, bbx1:bbx2, bby1:bby2]
    
    # Adjust lambda for loss
    lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (x.size()[-1] * x.size()[-2]))
    
    return x, y, y[index], lam


def rand_bbox(size, lam):
    W = size[2]
    H = size[3]
    cut_rat = np.sqrt(1. - lam)
    cut_w = int(W * cut_rat)
    cut_h = int(H * cut_rat)
    
    cx = np.random.randint(W)
    cy = np.random.randint(H)
    
    bbx1 = np.clip(cx - cut_w // 2, 0, W)
    bby1 = np.clip(cy - cut_h // 2, 0, H)
    bbx2 = np.clip(cx + cut_w // 2, 0, W)
    bby2 = np.clip(cy + cut_h // 2, 0, H)
    
    return bbx1, bby1, bbx2, bby2

AutoAugment

Learn augmentation policies from data:

# AutoAugment policies are learned search spaces
# PyTorch provides RandAugment and TrivialAugment

from torchvision.transforms.autoaugment import AutoAugment, RandAugment

# AutoAugment (learned policy)
auto_augment = AutoAugment()

# RandAugment (simpler, faster)
rand_augment = RandAugment(num_ops=9, magnitude=9)

Text Augmentations

# Back-translation
def back_translate(text, src_model, tgt_model):
    # Translate to another language
    translated = translate(text, src_model, tgt_model)
    # Translate back
    back_translated = translate(translated, tgt_model, src_model)
    return back_translated

# Synonym replacement
def synonym_replacement(text, n=2):
    words = text.split()
    new_words = words.copy()
    for _ in range(n):
        word = random.choice(words)
        synonyms = get_synonyms(word)
        if synonyms:
            new_words = [random.choice(synonyms) if w == word else w 
                        for w in new_words]
    return ' '.join(new_words)

Audio Augmentations

import librosa

def augment_audio(waveform, sample_rate):
    # Time stretching
    rate = random.uniform(0.9, 1.1)
    waveform = librosa.effects.time_stretch(waveform, rate=rate)
    
    # Pitch shifting
    n_steps = random.uniform(-2, 2)
    waveform = librosa.effects.pitch_shift(waveform, sr=sample_rate, n_steps=n_steps)
    
    # Add noise
    noise = np.random.randn(len(waveform)) * 0.005
    waveform = waveform + noise
    
    return waveform

Practical Recommendations

TaskAugmentations
Image classificationFlip, crop, color jitter, cutout, mixup
Object detectionFlips, scale, aspect ratio changes
Semantic segmentationSame geometric transforms for image and mask
NLPBack-translation, synonym replacement
AudioTime stretch, pitch shift, noise injection

Start with basic augmentations, then add more sophisticated ones if underfitting.