Bilgisayarlı Görme için Veri Arttırma: Teknikler ve En İyi Uygulamalar
Bilgisayarla görmede en yaygın sorunlardan biri aşırı uyumdur: model, ezbere öğrenir genelleme yapmak yerine eğitim seti En etkili çözüm veri artırma: yapay olarak artırmak için eğitim sırasında görüntülere rastgele dönüşümler uygulama Verilerin çeşitliliğini ve modelin görevle ilgisi olmayan dönüşümlere karşı değişmezliğini öğretir.
İyi tasarlanmış bir büyütme stratejisi, veri kümesini iki katına çıkarmak kadar iyi olabilir. Bir strateji yanlış performansı düşürebilir. Bu yazıda temel teknikleri göreceğiz. Albümasyonlar (en güçlü kütüphane) e torchvision.transforms, MixUp ve CutMix gibi gelişmiş teknikler ve her alan için doğru büyütmenin nasıl seçileceği. Ayrıca, her birinin gerçek etkisini ölçerek özel işlem hatlarının nasıl uygulanacağını da ele alacağız. minimum hesaplama yüküyle dönüşüm ve üretime dağıtma.
Ne Öğreneceksiniz
- veri artırma neden işe yarıyor: değişmezlik ilkesi
- Albümasyonlar vs torchvision: ne zaman kullanılmalı
- Geometrik teknikler: çevirme, döndürme, kırpma, perspektif dönüşümü
- Fotometrik teknikler: parlaklık, kontrast, renk değişimi, CLAHE
- Gelişmiş teknikler: MixUp, CutMix, Mosaic, GridDistortion
- AutoAugment ve RandAugment: Otomatik politika araması
- Tespit ve segmentasyon için büyütme (koordinatlar ve maskelerle)
- Etki alanına özgü büyütme: tıbbi, endüstriyel, uydu
- Ablasyon çalışmasıyla büyütmenin etkinliği nasıl ölçülür?
- Hızlı eğitim ve minimum masraf için optimize edilmiş işlem hattı
1. Veri Artırma neden işe yarıyor?
Veri artırma temel bir prensibe dayanmaktadır: uyguladığımız dönüşümler görüntünün anlamsal anlamını değiştirmemelidirler (modelin doğru çıktısı), ancak modelin sadece desenleri saklamaması için pikselleri değiştirmeleri gerekiyor yüzeysel.
Öğrenme teorisi açısından bakıldığında, veri artırma bir öğrenme biçimidir. örtülü düzenleme: dönüşümlerin alanını saygıyla genişletir modelin değişmez olmasını istiyoruz. Yatay taklalarla antrenman yaparsak, model, bir kedinin sol tarafının ve sağ tarafının etkilemediğini öğrenir sınıflandırma. Parlaklıktaki değişikliklerle antrenman yaparsak model şunu öğrenir: aydınlatma koşullarını göz ardı edin.
# Esempio: classificazione gatti/cani
# Trasformazioni CORRETTE (preservano la semantica):
# - Flip orizzontale: un gatto capovolto orizzontalmente è ancora un gatto ✓
# - Variazione luminosita: un gatto in penombra è ancora un gatto ✓
# - Crop random: un dettaglio del gatto è ancora riconoscibile come gatto ✓
# - Rotazione lieve: un gatto ruotato di 15 gradi è ancora un gatto ✓
# Trasformazioni PERICOLOSE (potrebbero cambiare la semantica):
# - Flip VERTICALE per traffico stradale: "stop" capovolto perde significato ✗
# - Rotazione >45 gradi per testo/numeri: "6" ruotato diventa "9" ✗
# - Scala estrema: un oggetto crop al 5% potrebbe perdere contesto ✗
# - Color jitter estremo su diagnostica medica: il colore è semanticamente rilevante ✗
# Regola d'oro:
# "Un'augmentation è valida se un umano, vedendo l'immagine aumentata,
# darebbe ancora la stessa label"
# Impatto pratico su benchmark (stesso modello ResNet-18, stessi iperparametri):
# CIFAR-10 senza augmentation: ~84.3% accuracy
# + Flip + Crop: ~91.8% accuracy (+7.5%)
# + Color Jitter: ~93.2% accuracy (+1.4%)
# + Cutout/CoarseDropout: ~94.1% accuracy (+0.9%)
# + MixUp (alpha=0.2): ~95.3% accuracy (+1.2%)
# + CutMix (alpha=1.0): ~95.8% accuracy (+0.5%)
# + AutoAugment (CIFAR-10 policy): ~97.1% accuracy (+1.3%)
# TrivialAugment + MixUp: ~97.4% accuracy (miglior combinazione)
# Nota: ogni +% è applicato sul modello SENZA aggiungere dati reali.
# Data augmentation = dataset virtualmente infinito da dati finiti.
1.1 Öğrenilecek Değişmezlik Türleri
Bilgisayarla görme uygulamalarının tümü aynı değişmezlikleri gerektirmez. önce Büyütme hattını oluştururken kendimize şu soruyu soralım: istediğimiz şeyden modeli sağlam mı?
Etki Alanı İçin Değişmezlik-Dönüşüm Haritası
| İhtisas | Yararlı değişmezlikler | TEHLİKELİ değişmezlikler |
|---|---|---|
| Doğal fotoğraflar | H Çevirme, kırpma, parlaklık, renk | Çevirme V, 90 derece dönüş |
| Metin/OCR | Parlaklık, hafif gürültü | Döndürme, çevirme, bozulma |
| Trafik/Sinyaller | Parlaklık, bulanıklık, kırpma | Çevirme V, 90 derece dönüş |
| Röntgen (göğüs) | Flip H, hafif döndürme, kontrast | Flip V, renk kaydırma, güçlü döndürme |
| Histoloji | Yatay/Düz çevirme, 90 döndürme, hafif renk kayması | Güçlü elastik dönüş, aşırı ölçek |
| Endüstriyel muayene | 360° dönüş, parlaklık, bulanıklık, gürültü | Çok aşırı ölçekler (ayrıntı kusurlarını kaybeder) |
| Uydu | 90/180 dönüş, Y/V çevirme | Güçlü renk değişimi (spektral bant) |
2. Albümler: Referans Kitaplığı
Albümasyonlar için en güçlü ve esnek veri artırma kütüphanesidir. bilgisayar görüşü. torchvision.transforms'un aksine, yerel olarak şunları destekler:
- Görüntüler + segmentasyon maskeleri (senkronize geometrik dönüşümler)
- Resimler + sınırlayıcı kutular (koordinatlar otomatik olarak güncellenir)
- Görseller + önemli noktalar (anahtar noktalar tutarlı tutuldu)
- OpenCV ile optimize edilmiş işlem hatları (PIL'den %15-40 daha hızlı)
- Kullanıma hazır 70'ten fazla dönüşüm mevcut
Boru hattı mimarisi şekillendirilebilir: her dönüşümün bir olasılığı vardır
p uygulanacak dönüşümler ve A.OneOf izin ver
bir dizi alternatif dönüşümden örnek almak için. Bu bir alan yaratır
yalnızca birkaç satır kodla katlanarak büyük artış.
import albumentations as A
from albumentations.pytorch import ToTensorV2
import numpy as np
import cv2
# ---- Pipeline standard per classificazione ----
def get_classification_transforms(img_size: int = 224, is_train: bool = True):
if is_train:
return A.Compose([
# --- Geometriche ---
A.RandomResizedCrop(img_size, img_size, scale=(0.7, 1.0),
ratio=(0.75, 1.33), p=1.0),
A.HorizontalFlip(p=0.5),
A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.15,
rotate_limit=15, border_mode=cv2.BORDER_REFLECT, p=0.7),
A.OneOf([
A.Perspective(scale=(0.05, 0.1)),
A.GridDistortion(num_steps=5, distort_limit=0.3),
A.ElasticTransform(alpha=1, sigma=50, alpha_affine=50)
], p=0.3),
# --- Fotometriche ---
A.RandomBrightnessContrast(brightness_limit=0.3, contrast_limit=0.3, p=0.7),
A.HueSaturationValue(hue_shift_limit=20, sat_shift_limit=30,
val_shift_limit=20, p=0.5),
A.OneOf([
A.GaussNoise(var_limit=(10, 50)),
A.GaussianBlur(blur_limit=(3, 7)),
A.MotionBlur(blur_limit=7),
A.MedianBlur(blur_limit=5)
], p=0.4),
A.ImageCompression(quality_lower=70, quality_upper=100, p=0.2),
# --- Dropout e occlusione ---
A.CoarseDropout(max_holes=8, max_height=32, max_width=32,
min_holes=1, p=0.3), # simile a Cutout
A.RandomGridShuffle(grid=(3, 3), p=0.1),
# --- Normalizzazione ImageNet ---
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
else:
# Validation: solo operazioni deterministiche
return A.Compose([
A.Resize(int(img_size * 1.14), int(img_size * 1.14)),
A.CenterCrop(img_size, img_size),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
# ---- Pipeline per object detection (aggiorna bounding boxes!) ----
def get_detection_transforms(img_size: int = 640, is_train: bool = True):
if is_train:
return A.Compose([
A.RandomResizedCrop(img_size, img_size, scale=(0.5, 1.0)),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.7),
A.HueSaturationValue(p=0.5),
A.OneOf([
A.GaussNoise(),
A.GaussianBlur(blur_limit=3)
], p=0.3),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
],
# CRITICO: specifica formato bbox per aggiornamento automatico
bbox_params=A.BboxParams(
format='yolo', # o 'pascal_voc', 'coco', 'albumentations'
label_fields=['class_labels'],
min_visibility=0.3, # rimuovi bbox se visibilità < 30%
min_area=100 # rimuovi bbox se area < 100 pixel
))
else:
return A.Compose([
A.Resize(img_size, img_size),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
],
bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels']))
# ---- Pipeline per segmentazione (aggiorna maschere!) ----
def get_segmentation_transforms(img_size: int = 512, is_train: bool = True):
if is_train:
return A.Compose([
A.RandomResizedCrop(img_size, img_size, scale=(0.7, 1.0)),
A.HorizontalFlip(p=0.5),
A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1,
rotate_limit=10, p=0.5),
A.ElasticTransform(alpha=1, sigma=50, alpha_affine=50, p=0.3),
A.RandomBrightnessContrast(p=0.5),
A.GaussNoise(var_limit=(10, 30), p=0.3),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
# NOTA: la maschera viene passata come argomento 'mask' - aggiornata automaticamente!
else:
return A.Compose([
A.Resize(img_size, img_size),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
# ---- Utilizzo con detection ----
transform = get_detection_transforms(is_train=True)
image = cv2.imread('image.jpg')
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
bboxes = [(0.5, 0.5, 0.3, 0.4)] # formato YOLO [x_c, y_c, w, h]
labels = [0]
result = transform(image=image_rgb, bboxes=bboxes, class_labels=labels)
transformed_image = result['image'] # tensor [3, H, W]
transformed_boxes = result['bboxes'] # bbox aggiornate automaticamente!
print(f"Bbox originale: {bboxes} -> Bbox trasformata: {transformed_boxes}")
2.1 Albümasyonlar vs torchvision.transforms: Hangisi Ne Zaman Kullanılmalı
Albümentasyonlar ve torchvision.transforms Karşılaştırması
| karakteristik | Albümasyonlar | torchvision.transforms |
|---|---|---|
| bbox/maske desteği | Yerel ve otomatik | Yalnızca görseller (medya yok) |
| Dönüşüm sayısı | 70+ dönüşüm | ~30 dönüşüm |
| Hız | Çok hızlı (OpenCV arka ucu) | Daha yavaş (arka uç GSYH) |
| PyTorch entegrasyonu | ToTensorV2 gerekli | Yerli |
| OtomatikAugment/RandAugment | Özel uygulama | Torchvision'da yerel 0,12+ |
| Önerilen kullanım | Tespit, segmentasyon, özel işlem hatları | Basit sınıflandırma, Otomatik Büyütme |
3. İleri Büyütme Teknikleri
3.1 MixUp: Görüntüler Arasında Enterpolasyon
Karıştır (Zhang ve diğerleri, 2018) iki görüntüyü ve ilgili etiketlerini karıştırır. Beta dağılımından örneklenen bir lambda katsayısı. Modeli davranmaya zorla sınıflar arasında doğrusal olarak bulunur ve tahminlere olan güveni önemli ölçüde azaltır, Kalibrasyonun ve sağlamlığın iyileştirilmesi. Kayıp ortalama olarak hesaplanmalıdır iki ayrı CrossEntropyLoss'un ağırlıklandırılması.
import torch
import numpy as np
def mixup_batch(images: torch.Tensor, labels: torch.Tensor,
alpha: float = 0.2) -> tuple:
"""
MixUp: interpola linearmente due immagini e le loro label.
Output: immagine mista, label miste (soft labels).
lambda ~ Beta(alpha, alpha)
image_mixed = lambda * image_a + (1 - lambda) * image_b
label_mixed = lambda * label_a + (1 - lambda) * label_b
Nota: con alpha=0.2, lambda e tipicamente vicino a 0 o 1 (quasi pura),
con alpha=1.0 (uniform Beta), le immagini sono equamente miscelate.
"""
batch_size = images.size(0)
lam = np.random.beta(alpha, alpha)
# Indici random per la seconda immagine nel batch
perm = torch.randperm(batch_size)
mixed_images = lam * images + (1 - lam) * images[perm]
labels_a = labels
labels_b = labels[perm]
# Per calcolo loss: loss = lam * CE(pred, a) + (1-lam) * CE(pred, b)
return mixed_images, labels_a, labels_b, lam
def cutmix_batch(images: torch.Tensor, labels: torch.Tensor,
alpha: float = 1.0) -> tuple:
"""
CutMix: sostituisce una regione rettangolare di un'immagine con quella di un'altra.
Più efficace di MixUp per task di detection (preserva regioni intatte).
lambda ~ Beta(alpha, alpha) # determina la proporzione dell'area tagliata
"""
batch_size, C, H, W = images.size()
lam = np.random.beta(alpha, alpha)
perm = torch.randperm(batch_size)
# Calcola dimensioni del box da tagliare
cut_ratio = np.sqrt(1 - lam)
cut_w = int(W * cut_ratio)
cut_h = int(H * cut_ratio)
# Centro del box random
cx = np.random.randint(W)
cy = np.random.randint(H)
# Coordinate box (clipped ai bordi)
x1 = np.clip(cx - cut_w // 2, 0, W)
x2 = np.clip(cx + cut_w // 2, 0, W)
y1 = np.clip(cy - cut_h // 2, 0, H)
y2 = np.clip(cy + cut_h // 2, 0, H)
# Applica CutMix (immutabile: crea una copia)
mixed_images = images.clone()
mixed_images[:, :, y1:y2, x1:x2] = images[perm, :, y1:y2, x1:x2]
# Ricalcola lambda effettivo basato sull'area reale del box
lam = 1 - (x2 - x1) * (y2 - y1) / (W * H)
return mixed_images, labels, labels[perm], lam
def mosaic_augmentation(images: list, labels: list) -> tuple:
"""
Mosaic augmentation (introdotto in YOLOv5):
Combina 4 immagini in un mosaico 2x2 con crop random centrale.
Particolarmente efficace per detection su oggetti piccoli:
ogni immagine nel mosaico e al 25% della dimensione originale,
simulando oggetti a distanza maggiore.
"""
assert len(images) == 4, "Mosaic richiede esattamente 4 immagini"
_, H, W = images[0].shape
mosaic = torch.zeros(3, H * 2, W * 2)
# Posiziona le 4 immagini nel mosaico
mosaic[:, 0:H, 0:W] = images[0] # top-left
mosaic[:, 0:H, W:2*W] = images[1] # top-right
mosaic[:, H:2*H, 0:W] = images[2] # bottom-left
mosaic[:, H:2*H, W:2*W] = images[3] # bottom-right
# Crop centrale random (simula diverse prospettive)
crop_y = np.random.randint(H // 2, H)
crop_x = np.random.randint(W // 2, W)
mosaic_cropped = mosaic[:, crop_y-H//2:crop_y+H//2,
crop_x-W//2:crop_x+W//2]
# In pratica i bbox vanno aggiornati di conseguenza (offset per posizione)
combined_labels = []
for i, lbl in enumerate(labels):
combined_labels.extend(lbl)
return mosaic_cropped, combined_labels
# ---- Training loop con MixUp/CutMix ----
def train_with_advanced_augmentation(
model, train_loader, optimizer, criterion, device,
mixup_alpha: float = 0.2, cutmix_alpha: float = 1.0,
mixup_prob: float = 0.5, cutmix_prob: float = 0.5
) -> float:
"""
Training step che applica MixUp o CutMix con probabilità data.
La scelta e esclusiva: se entrambe superano threshold, si usa la prima.
"""
model.train()
total_loss = 0.0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
r = np.random.rand()
if r < mixup_prob:
mixed_images, labels_a, labels_b, lam = mixup_batch(images, labels, mixup_alpha)
outputs = model(mixed_images)
loss = lam * criterion(outputs, labels_a) + (1 - lam) * criterion(outputs, labels_b)
elif r < mixup_prob + cutmix_prob:
mixed_images, labels_a, labels_b, lam = cutmix_batch(images, labels, cutmix_alpha)
outputs = model(mixed_images)
loss = lam * criterion(outputs, labels_a) + (1 - lam) * criterion(outputs, labels_b)
else:
outputs = model(images)
loss = criterion(outputs, labels)
optimizer.zero_grad(set_to_none=True)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
total_loss += loss.item()
return total_loss / len(train_loader)
3.2 OtomatikAugment, RandAugment ve TrivialAugment
Otomatik Büyütme (Çubuk ve diğerleri, 2019) arama yapmak için pekiştirmeli öğrenmeyi kullanır bir veri kümesindeki en uygun artırma politikasını otomatik olarak belirler. Sorun: pahalı araştırma (CIFAR-10'da 5000 GPU saati). RandArttırma basitleştirin: N işlemi uygulayın Ayarlanacak yalnızca 2 hiperparametrenin olduğu, tekdüze M büyüklüğüne sahip sabit bir listeden rastgele seçilir. Önemsiz Arttırma daha da ileri gidiyor: Rastgele büyüklükte 1 rastgele işlem, genellikle daha karmaşık yöntemlerin ötesine geçer.
from torchvision import transforms
MEAN = [0.485, 0.456, 0.406]
STD = [0.229, 0.224, 0.225]
# ---- AutoAugment (policy appresa su ImageNet) ----
train_auto = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.AutoAugment(
policy=transforms.AutoAugmentPolicy.IMAGENET, # o CIFAR10, SVHN
interpolation=transforms.InterpolationMode.BILINEAR
),
transforms.ToTensor(),
transforms.Normalize(mean=MEAN, std=STD)
])
# ---- RandAugment (N=2, M=9 sono i valori ottimali tipici) ----
train_rand = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.RandAugment(num_ops=2, magnitude=9),
transforms.ToTensor(),
transforms.Normalize(mean=MEAN, std=STD)
])
# ---- TrivialAugment: ancora più semplice, spesso meglio ----
# Seleziona 1 operazione casuale con magnitude casuale uniforme
train_trivial = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.TrivialAugmentWide(), # PyTorch 1.13+
transforms.ToTensor(),
transforms.Normalize(mean=MEAN, std=STD),
transforms.RandomErasing(p=0.1) # Aggiunge cutout
])
# ---- Augmentation Mix Strategy (raccomandato) ----
# TrivialAugment + MixUp/CutMix = la combinazione con il miglior rapporto
# semplicità/performance su quasi tutti i task di classificazione
# Non usare AutoAugment su dataset diversi da quello per cui e stata appresa
# (la policy di ImageNet non e ottimale per CIFAR-10 o per dataset medici)
3.3 Artırma Test Süresi (TTA)
Il Test Süresini Artırma (TTA) dönüşümleri çıkarım anında da uygulayın, artırılmış görüntü üzerinde birden fazla tahmin gerçekleştirmek ve sonuçları toplamak. Çıkarım süresinin N katı maliyetle, yeniden eğitim gerektirmeden sağlamlığı artırır.
import torch
import torch.nn.functional as F
import torchvision.transforms.functional as TF
@torch.no_grad()
def tta_predict(model: torch.nn.Module,
image: torch.Tensor, # [1, C, H, W]
n_augmentations: int = 5) -> torch.Tensor:
"""
Test-Time Augmentation: mediatura di predizioni su immagini aumentate.
Trucchi pratici:
- TTA di flip orizzontale e crop centrale sono le più affidabili
- Evitare TTA con rotazioni forti (possono degradare le performance)
- n=5 e un buon compromesso velocità/performance
"""
device = image.device
model.eval()
predictions = []
# 1. Immagine originale
pred = F.softmax(model(image), dim=1)
predictions.append(pred)
# 2. Flip orizzontale
flipped = TF.hflip(image)
pred = F.softmax(model(flipped), dim=1)
predictions.append(pred)
# 3. Flip verticale (solo se semanticamente valido)
# vflipped = TF.vflip(image)
# predictions.append(F.softmax(model(vflipped), dim=1))
# 4-N. Crop random dall'immagine scalata al 90%
_, C, H, W = image.shape
scale = 0.9
for _ in range(n_augmentations - 2):
# Scala lievemente
new_h, new_w = int(H * scale), int(W * scale)
resized = TF.resize(image, (new_h, new_w))
# Crop casuale alla dimensione originale
top = torch.randint(0, H - new_h + 1, (1,)).item()
left = torch.randint(0, W - new_w + 1, (1,)).item()
cropped = TF.crop(resized, top, left, new_h, new_w)
cropped = TF.resize(cropped, (H, W)) # rimetti a dimensione originale
pred = F.softmax(model(cropped), dim=1)
predictions.append(pred)
# Media delle probabilità (ensemble di N predizioni)
mean_pred = torch.stack(predictions).mean(dim=0)
return mean_pred.argmax(dim=1) # classe predetta
4. Alana Özel Veri Arttırma
4.1 Doktor: Klinik Anlambilimin Korunması
Tıbbi görüntüler oldukça muhafazakar büyütme stratejileri gerektirir. Kanallar renk klinik bilgi taşır (örn. histolojide H&E boyama), yönelim anatomik ve alakalı (göğüs röntgeninin dikey hareketi anatomik olarak geçersizdir) ve gürültü özellikleri tarayıcının tipine bağlıdır. Her zaman bir uzmana danışın İşlem hattını tanımlamadan önce etki alanı.
import albumentations as A
from albumentations.pytorch import ToTensorV2
def get_medical_transforms(img_size: int = 512, modality: str = 'xray'):
"""
Pipeline augmentation per immagini mediche.
Diversa per modalità: radiografia, ecografia, risonanza, istologia.
"""
common = [
A.Resize(img_size, img_size),
]
if modality == 'xray':
augmentations = [
# Flip solo orizzontale (anatomicamente valido per torace)
A.HorizontalFlip(p=0.5),
# Rotazione lieve (paziente non sempre perfettamente allineato)
A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.05,
rotate_limit=10, p=0.5),
# CLAHE migliora contrasto su radiografie
A.CLAHE(clip_limit=2.0, tile_grid_size=(8, 8), p=0.5),
# Rumore realistico del sensore
A.GaussNoise(var_limit=(5, 25), p=0.4),
# Variazione contrasto lieve
A.RandomGamma(gamma_limit=(80, 120), p=0.5),
# NON usare: flip verticale, color jitter, hue shift
]
elif modality == 'histology':
augmentations = [
A.HorizontalFlip(p=0.5),
A.VerticalFlip(p=0.5), # OK per istologia (no orientamento fisso)
A.RandomRotate90(p=0.5),
# Variazione colore importante per istologia (diversi laboratori/staining)
A.HueSaturationValue(hue_shift_limit=10, sat_shift_limit=20,
val_shift_limit=20, p=0.7),
A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.7),
A.ElasticTransform(alpha=120, sigma=120 * 0.05,
alpha_affine=120 * 0.03, p=0.3),
]
elif modality == 'mri':
augmentations = [
A.HorizontalFlip(p=0.5),
A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1,
rotate_limit=15, p=0.5),
# MRI: variazioni di intensità tra scanner diversi
A.RandomBrightnessContrast(brightness_limit=0.3, contrast_limit=0.3, p=0.7),
# Simula artifact di movimento MRI
A.GaussianBlur(blur_limit=3, p=0.3),
# Elastic deformation anatomica realistica
A.ElasticTransform(alpha=1, sigma=50, alpha_affine=50, p=0.2),
]
else:
augmentations = []
norm = [
A.Normalize(mean=[0.5], std=[0.5]), # Grayscale normalization
# Per immagini RGB: A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
]
return A.Compose(common + augmentations + norm + [ToTensorV2()])
4.2 Endüstriyel: Edinim Değişikliklerine Karşı Dayanıklılık
import albumentations as A
from albumentations.pytorch import ToTensorV2
def get_industrial_transforms(img_size: int = 256, is_train: bool = True):
"""
Pipeline per ispezione visiva industriale.
Obiettivo: robustezza a variazioni di illuminazione, rotazione parziale,
rumore del sensore e piccole deformazioni meccaniche.
"""
if is_train:
return A.Compose([
A.RandomResizedCrop(img_size, img_size, scale=(0.8, 1.0)),
# Rotazione: prodotti su nastro trasportatore hanno orientamenti variabili
A.RandomRotate90(p=0.5),
A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.1,
rotate_limit=30, border_mode=0, p=0.7),
# Illuminazione: variazioni da illuminazione industriale (LED, fluorescente)
A.OneOf([
A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0.4),
A.CLAHE(clip_limit=4.0),
A.RandomGamma(gamma_limit=(70, 130))
], p=0.8),
# Sensore: rumore e blur da sistemi di acquisizione industriali
A.OneOf([
A.GaussNoise(var_limit=(10, 60)),
A.ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5)),
A.MultiplicativeNoise(multiplier=[0.9, 1.1]),
], p=0.4),
A.OneOf([
A.GaussianBlur(blur_limit=(3, 5)),
A.Defocus(radius=(1, 3)),
A.MotionBlur(blur_limit=5)
], p=0.3),
# Artefatti da riflessione/ombra
A.RandomShadow(shadow_roi=(0, 0, 1, 1), p=0.2),
A.Downscale(scale_min=0.7, scale_max=0.9, p=0.2), # simula bassa risoluzione
# CoarseDropout simula occlusione parziale del pezzo
A.CoarseDropout(max_holes=3, max_height=32, max_width=32, p=0.2),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
else:
return A.Compose([
A.Resize(img_size, img_size),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
4.3 Uydu ve Uzaktan Algılama
Uydu görüntülerinin benzersiz özellikleri vardır: dönme açısından değişmez yönelim (hiçbir sabit "yukarı" ve "aşağı"), çoklu spektral bantlar (RGB'ye ek olarak NIR, SWIR) ve çözünürlük farklı sensörler arasında çok değişken boşluk.
import albumentations as A
import numpy as np
def get_satellite_transforms(img_size: int = 256, n_bands: int = 4,
is_train: bool = True):
"""
Pipeline per immagini satellitari multi-banda.
n_bands: numero di bande spettrali (es. 3=RGB, 4=RGBI con NIR, 13=Sentinel-2)
"""
if is_train:
return A.Compose([
A.RandomCrop(img_size, img_size, p=1.0),
# Rotazione: immagini satellitari non hanno orientamento fisso
A.D4(p=1.0), # tutte le 8 simmetrie del quadrato (rotazioni 0/90/180/270 + flip)
# Shift/scale lieve per variazione di zoom/scala
A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.15,
rotate_limit=45, border_mode=4, p=0.5),
# Variazioni radiometriche tra diverse scene e stagioni
A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.6),
A.RandomGamma(gamma_limit=(80, 120), p=0.5),
# Rumore da sensore satellitare
A.GaussNoise(var_limit=(5, 30), p=0.3),
# Blur da risoluzione atmosferica
A.GaussianBlur(blur_limit=(3, 5), p=0.2),
# CloudDropout: simula copertura nuvolosa parziale
# (custom transform: riempie regioni con valori "cloud")
A.CoarseDropout(
max_holes=5, max_height=64, max_width=64,
fill_value=255, # bianco = nuvola
p=0.2
),
# Normalizzazione per multibanda (media/std per banda)
# Usa valori specifici del sensore, qui esempio per Sentinel-2 4 bande
A.Normalize(
mean=[0.485, 0.456, 0.406, 0.4], # 4 bande: R, G, B, NIR
std=[0.229, 0.224, 0.225, 0.22]
),
])
else:
return A.Compose([
A.CenterCrop(img_size, img_size),
A.Normalize(
mean=[0.485, 0.456, 0.406, 0.4],
std=[0.229, 0.224, 0.225, 0.22]
),
])
5. Ablasyon Çalışması: Büyütme Etkinliği Nasıl Ölçülür?
İşlem hattınızı gelişmiş dönüşümlerle karmaşıklaştırmadan önce sistematik olarak ölçüm yapın her birinin etkisi. Büyütme üzerine bir ablasyon çalışması: aynı mimari, aynı zamanlayıcı, aynı eğitim hiperparametreleri - yalnızca artırma değişiklikleri.
import torch
import albumentations as A
from albumentations.pytorch import ToTensorV2
from torchvision import datasets
import pandas as pd
def run_augmentation_ablation(
model_class,
dataset_path: str,
augmentation_configs: dict, # nome -> A.Compose
n_epochs: int = 30,
device: str = 'cuda'
) -> pd.DataFrame:
"""
Esegue ablation study sistematico su diverse configurazioni di augmentation.
Confronta tutte le configurazioni in condizioni identiche.
augmentation_configs = {
'baseline': A.Compose([A.Resize(224,224), A.Normalize(...), ToTensorV2()]),
'flip+crop': A.Compose([A.RandomResizedCrop(224,224), A.HorizontalFlip(0.5), ...]),
'flip+crop+col': A.Compose([..., A.ColorJitter(0.3,0.3,0.3), ...]),
'full_pipeline': get_classification_transforms(is_train=True),
}
"""
results = []
for config_name, transform in augmentation_configs.items():
print(f"\n=== Ablation: {config_name} ===")
# Dataset con questa specifica augmentation
train_dataset = datasets.ImageFolder(
f"{dataset_path}/train",
transform=lambda img: transform(image=np.array(img))['image']
)
val_dataset = datasets.ImageFolder(f"{dataset_path}/val",
transform=get_val_transform(224))
train_loader = torch.utils.data.DataLoader(
train_dataset, batch_size=64, shuffle=True,
num_workers=4, pin_memory=True
)
val_loader = torch.utils.data.DataLoader(
val_dataset, batch_size=128, shuffle=False, num_workers=4
)
# Modello fresco per ogni configurazione (stessi pesi iniziali!)
torch.manual_seed(42)
model = model_class(num_classes=len(train_dataset.classes)).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.01)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=n_epochs)
criterion = torch.nn.CrossEntropyLoss()
best_val_acc = 0.0
for epoch in range(n_epochs):
# Training
model.train()
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
loss = criterion(model(images), labels)
optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
scheduler.step()
# Validation
model.eval()
correct = total = 0
with torch.no_grad():
for images, labels in val_loader:
images, labels = images.to(device), labels.to(device)
preds = model(images).argmax(1)
correct += preds.eq(labels).sum().item()
total += labels.size(0)
val_acc = 100.0 * correct / total
best_val_acc = max(best_val_acc, val_acc)
results.append({
'config': config_name,
'best_val_acc': round(best_val_acc, 2)
})
print(f"Best val accuracy: {best_val_acc:.2f}%")
df = pd.DataFrame(results).sort_values('best_val_acc', ascending=False)
print("\n=== Risultati Ablation Study ===")
print(df.to_string(index=False))
return df
Veri Arttırmanın CIFAR-10 (ResNet-18) Üzerindeki Etkisi
| Büyütme yapılandırması | Değer Doğruluğu | Delta |
|---|---|---|
| Arttırma yok | %84,3 | - |
| Çevir + Rastgele Kırp | %91,8 | +%7,5 |
| + Renk Değişimi | %93,2 | +%1,4 |
| + Kesme/Kaba Bırakma | %94,1 | +%0,9 |
| + Karıştır (alfa=0,2) | %95,3 | +%1,2 |
| + CutMix (alfa=1.0) | %95,8 | +%0,5 |
| Otomatik Büyütme (CIFAR-10 politikası) | %97,1 | +%1,3 |
| Önemsiz Arttırma + Karıştırma | %97,4 | +%0,3 |
6. Yaygın Hatalar ve En İyi Uygulamalar
Büyütmede Yaygın Hatalar
- Doğrulama setindeki büyütme: Doğrulama veya test setinde ASLA rastgele büyütme uygulamayın. Yalnızca deterministik işlemleri kullanın (yeniden boyutlandırma, normalleştirme). Val seti gerçek performansı ölçmek için kullanılır.
- Alanı yoksay: Gri tonlamalı görüntüler için Renk Değişimi'ni kullanmayın. Metin görselleri için dikey çevirmeyi kullanmayın. Ufuk içeren doğal sahneler için 90 derece döndürmeyi kullanmayın.
- Çok fazla büyütme: 20 dönüşüme sahip bir işlem hattının, 5 iyi seçilmiş dönüşüme sahip bir işlem hattından mutlaka daha iyi olması gerekmez. Büyütme aşırı uyumu gerçektir: Model, artırılmış görüntülerin gerçek olanlardan farklı olduğunu öğrenebilir.
- Senkronizasyonu unutun: Tespit ve segmentasyon için geometrik dönüşümlerin görüntüye ve açıklamalara aynı şekilde uygulanması ZORUNLUDUR. Albümantasyonlar bunu otomatik olarak yapar; torchvision.transforms bunu yapmaz.
- CutMix/MixUp'ı doğru kayıplar olmadan uygulayın: Yumuşak etiketlerle (sınıf karışımı) standart CrossEntropyLoss, ağırlıklı ortalama olarak hesaplanmalıdır. Kaybı hesaplamak için etiketlerde argmax kullanmayın.
- Görsel olarak doğrulamayın: Antrenmandan önce 20-30 artırılmış görsel görüntüleyin. İnsan gözüne bile "tuhaf" görünüyorlarsa muhtemelen çok saldırgandırlar.
- Artırmada parti boyutunun göz ardı edilmesi: MixUp/CutMix, parti büyüklüğü >= 2 gerektirir. Parti büyüklüğü = 1 olduğunda bu yöntemler anlamsızdır.
Optimum Artırma Ardışık Düzenleri için En İyi Uygulamalar
- Basit başlayın, yavaş yavaş karmaşıklığı artırın: Flip + RandomCrop ile başlayın. Renk Değişimi ekleyin. Daha sonra MixUp'ı seçin. Ablasyon çalışmasıyla her adımda etkiyi ölçün.
- Uygun num_workers kullanın: Arttırma CPU çalışanlarında meydana gelir. num_workers=4 ve pin_memory=True ile ön işleme, karmaşık işlem hatlarında bile hiçbir zaman darboğaz oluşturmaz.
- Optimize etmeden önce profil: Gerçek veri yükleyici süresini ölçmek için torch.profiler veya time.perf_counter'ı kullanın. Eğitim adımının %10'unun altındaysa büyütme darboğaz değildir.
- Büyük veri kümeleri için kalıcı genişletmeler: Çok büyük veri kümeleri (100.000'den fazla görüntü) için, artırılmış sürümleri çevrimdışı olarak önceden oluşturun ve bunları diske kaydedin. Bu, gerçek zamanlı bilgi işlemi azaltır ancak depolamayı artırır.
- Müfredat genişletme: Eğitim sırasında güçlendirmenin büyüklüğünü kademeli olarak artırın. Hafif artışlarla başlar ve ilerleyen dönemlerde daha agresif hale gelir.
Sonuçlar
Veri artırma, modelleri geliştirmek için en güçlü ve düşük maliyetli araçlardan biridir bilgisayarlı görme. Doğru tekniklerle doğrulukta artışlar elde etmek mümkündür Tek bir ek veri bile toplamadan %5-15. Bu yazıda şunları gördük:
- Temel prensip: geçerli büyütme = anlamsal koruma ve görevle değişmeyen dönüşüm
- Senkronize bbox/mask ile algılama ve segmentasyon için yerel desteğe sahip bir referans kitaplığı olarak albümler
- MixUp, CutMix ve Mosaic: örnekler arasında enterpolasyon yoluyla %2-5 doğruluk artışı sağlayan gelişmiş teknikler
- AutoAugment, RandAugment ve TrivialAugment: en uygun politikayı otomatik olarak arama
- Test Süresini Artırma: Yeniden eğitim gerektirmeden çıkarımı iyileştirme
- Tıbbi, endüstriyel ve uydu için alana özel güçlendirme
- Her dönüşümün gerçek etkisini ölçmek için bir ablasyon çalışmasının nasıl yapılandırılacağı







