Öğrenimi Aktarın: Önceden Eğitilmiş Modellerin Yeniden Kullanımı
Bir çocuğa köpek ırklarını tanımayı öğretmek zorunda olduğunuzu hayal edin. Eğer o çocuk zaten öğrenmişse şekilleri, renkleri, dokuları ve genel anatomik yapıları tanımak, görevin çok daha karmaşık hale gelmesine neden olur. basit. Sıfırdan başlamanıza gerek yok: yapabilirsiniz aktarma halihazırda edinilmiş olan bilgi yeni görev. Tam olarak bu Öğrenimi Aktar derin öğrenmede.
Serinin bu ikinci makalesinde Derin Öğrenme ile Bilgisayarlı Görme, keşfedeceğiz Transfer Öğrenimi hakkında derinlemesine bilgi: neden işe yarıyor, hangi stratejiler var, modelin nasıl seçileceği önceden eğitilmiş haklar ve PyTorch'ta tüm işlem hatlarının nasıl uygulanacağı. Endüstriyel bir örnek olay incelemesine bakacağız gerçeklik ve profesyonellerin her gün kullandığı ileri teknikler.
Seriye Genel Bakış
| # | Öğe | Odak |
|---|---|---|
| 1 | CNN: Evrişimsel Ağlar | Mimari, eğitim, dağıtım |
| 2 | Buradasınız - Transfer Öğrenimi ve İnce Ayar | Önceden eğitilmiş modeller, etki alanı uyarlaması |
| 3 | YOLO ile Nesne Algılama | Gerçek zamanlı nesne algılama |
| 4 | Anlamsal Segmentasyon | Piksel düzeyinde sınıflandırma |
| 5 | GAN ve Difüzyon ile Görüntü Üretimi | Sentetik görüntü oluşturma |
| 6 | Uç Dağıtımı ve Optimizasyonu | Gömülü cihazlardaki modeller |
Ne Öğreneceksiniz
- Transfer Öğrenme nedir ve neden işe yarar (CNN'lerdeki özellik hiyerarşileri)
- Ana stratejiler: özellik çıkarma, ince ayar, etki alanı uyarlaması
- Doğru önceden eğitilmiş model nasıl seçilir (ResNet, EfficientNet, ViT, ConvNeXt)
- PyTorch'ta tam uygulama: veri hazırlığından dağıtıma kadar
- Gelişmiş teknikler: ayırt edici öğrenme oranları, kademeli olarak çözülme, öğrenme hızının ısınması
- Transfer Öğrenimi için optimize edilmiş veri artırma
- Pratik vaka çalışması: ResNet-50 ile endüstriyel kusur sınıflandırması
- Nesne tespitine uygulanan Transfer Öğrenimi (Daha Hızlı R-CNN, YOLO)
- Yaygın hatalar ve bunlardan nasıl kaçınılacağı
1. Transfer Öğrenme Nedir?
Il Öğrenimi Aktar ve eğitilmiş bir modelin kullanıldığı bir makine öğrenme tekniği bir görevde (denilen kaynak görevi) farklı bir görev için başlangıç noktası olarak yeniden kullanılır (dedi hedef görevler). Milyonlarca görüntü üzerinden bir sinir ağını sıfırdan eğitmek yerine, önceden eğitilmiş bir modeli ele alalım (genellikle ImageNet'te, 1000 sınıfta 1,2 milyon görüntü) ve bunu kendi özel problemimize uyarlıyoruz.
1.1 İnsan Analojisi
Beynimiz sürekli olarak transfer öğrenme modunda çalışır. Yeni bir şey öğrenen bir cerrah Ameliyat tekniği anatomi, fizyoloji ve temel el becerilerini yeniden öğrenmek zorunda değildir. Bir müzisyen Caz'a geçiş yapan klasik, enstrümantal tekniği, nota okumasını ve teoriyi aktarıyor harmonik. Rust öğrenen bir Python programcısı programlama kavramlarını aktarıyor, hata ayıklıyor zihinsel ve algoritmik mantık. Bütün bu durumlarda ön bilgi hızlanmak yeni alanı muazzam bir şekilde öğreniyorum.
1.2 neden işe yarıyor: Özellik Hiyerarşisi
Transfer Öğrenmenin CNN'lerde çalışmasının temel nedeni, özellik hiyerarşisi öğrendim. Araştırmacılar CNN'lerin ImageNet'te eğitim alarak artan soyutlama seviyelerinde düzenlenen özellikleri öğrenirler:
CNN'lerde özellik hiyerarşisi
| Katmanlar | Özellik Öğrenildi | özgüllük | Aktarılabilirlik |
|---|---|---|---|
| Katmanlar 1-2 | Kenarlar, köşeler, renk geçişleri | Genel (görevden bağımsız) | Çok yüksek |
| Katmanlar 3-4 | Dokular, tekrarlanan desenler, geometrik motifler | Yarı jenerik | Yüksek |
| Katmanlar 5-6 | Nesnelerin parçaları (gözler, tekerlekler, pencereler) | Yarı spesifik | Ortalama |
| Katman 7+ | Eksiksiz nesneler, sahneler, kompozisyonlar | Göreve özel | Düşük |
İlk katmanlar özellikleri öğrenir evrensel: kenarlar, dokular ve degradeler herhangi bir görsel görev için kullanışlıdır. Ara katmanlar daha karmaşık fakat yine de desenleri yakalar oldukça genel. Yalnızca son birkaç katman orijinal göreve oldukça özeldir. Bu, ağın çoğunu güçlü bir ağ olarak yeniden kullanabileceğimiz anlamına gelir. özellik çıkarıcı ve sadece son kısımları görevimize uyarlayalım.
ImageNet Pre-trained CNN (es. ResNet-50):
Input Immagine
|
v
[Layer 1-2] ---> Bordi orizzontali, verticali, diagonali
| Gradienti di colore, blob
| UNIVERSALI: utili per QUALSIASI immagine
v
[Layer 3-4] ---> Texture (pelo, metallo, legno, tessuto)
| Pattern geometrici, griglie
| SEMI-GENERICHE: utili per molti domini
v
[Layer 5-6] ---> Parti di oggetti (occhi, ruote, ali)
| Composizioni locali
| SEMI-SPECIFICHE: dipendono dal dominio
v
[Layer 7+] ---> Classi intere (gatto, auto, uccello)
| Feature altamente specifiche per ImageNet
| TASK-SPECIFICHE: da sostituire/adattare
v
[Classifier] ---> 1000 classi ImageNet
SEMPRE da sostituire per il tuo task
Biçimsel Tanım
T_s görevlerine sahip bir kaynak etki alanı D_s ve T_t görevlerine sahip bir hedef etki alanı D_t verildiğinde, Transfer Öğrenme bilgiyi kullanarak hedef alanda öğrenme fonksiyonunu geliştirmeyi amaçlamaktadır D_s ve T_s'den çıkartılmıştır, burada D_s != D_t veya T_s != T_t. Temel olarak öğrenilen teta ağırlıkları kaynak görevden gelenler, hedef göreve ilişkin eğitim için başlatma (theta_0) olarak kullanılır, rastgele başlatma yerine.
2. Öğrenme Stratejilerini Aktarın
Transfer Öğrenmeyi uygulamanın tek bir yolu yoktur. Optimum strateji şunlara bağlıdır: Hedef veri kümesinin boyutu, kaynak veri kümesine ve kaynaklara benzerliği hesaplama kaynakları mevcuttur. Dört ana stratejiye bakalım.
2.1 Özellik Çıkarma (Omurgayı Dondur, Sınıflandırıcıyı Eğit)
En basit ve en doğrudan strateji: evet donuyor Önceden eğitilmiş ağın tamamını (dondurun) ve sabit özellik çıkarıcı olarak kullanılır. Eğitilen tek parça yeni bir parçadır sınıflandırıcı üste eklendi. Omurga ağırlıkları antrenman sırasında güncellenmez.
Ne zaman kullanılır: Küçük hedef veri kümesi (yüzlerce/birkaç bin görüntü) e kaynağa benzer alan adı (örneğin, model ImageNet'te önceden eğitildiğinde köpek ırklarının sınıflandırılması) birçok köpek resmi içeren).
Pre-trained ResNet-50 (ImageNet):
+---------------------------------------------------+
| [Conv layers] --> [Res blocks] --> [Global AvgPool] | CONGELATO (frozen)
| 25.5M parametri - NON aggiornati | - requires_grad = False
+---------------------------------------------------+
|
v
Feature vector (2048-dim)
|
v
+-------------------+
| [Linear 2048->N] | ADDESTRATO (trainable)
| N = tue classi | - requires_grad = True
+-------------------+
|
v
Output: N classi
Vantaggi:
+ Training velocissimo (pochi parametri da ottimizzare)
+ Non richiede GPU potente
+ Rischio minimo di overfitting
+ Bastano pochi dati
Svantaggi:
- Meno flessibile (le feature sono fisse)
- Performance limitata se il dominio e molto diverso
2.2 İnce Ayar (Bazı/Tüm Katmanların Çözülmesi)
İnce ayarda, ağı önceden eğitilmiş ağırlıklarla başlattıktan sonra, evet onlar erir katmanların bir kısmı veya tamamı ve ağın tamamını (veya bir kısmını) yeniden eğitmek çok düşük bir öğrenme oranına sahip. Önceden eğitilmiş katmanlar uyum sağlayacak şekilde biraz güncellendi Halihazırda edinilen bilgileri koruyarak yeni alana aktarın.
Ne zaman kullanılır: Orta-büyük hedef veri kümesi (binlerce-onbinlerce görüntü) ve/veya alan adı kaynaktan orta derecede farklı.
Strategia di Fine-Tuning Progressivo:
Fase 1 - Feature Extraction (5-10 epochs):
[Backbone CONGELATO] --> [Nuovo Classifier] ADDESTRATO (lr=1e-3)
Fase 2 - Partial Fine-Tuning (10-20 epochs):
[Layer 1-3 CONGELATI] --> [Layer 4 SCONGELATO lr=1e-5] --> [Classifier lr=1e-4]
Fase 3 - Full Fine-Tuning (opzionale, 5-10 epochs):
[TUTTI i layer SCONGELATI lr=1e-6] --> [Classifier lr=1e-5]
Learning Rates progressivi:
Layer iniziali: lr = 1e-6 (feature generiche, cambiare poco)
Layer intermedi: lr = 1e-5 (adattare gradualmente)
Layer finali: lr = 1e-4 (adattare al nuovo dominio)
Classifier: lr = 1e-3 (apprendere da zero)
2.3 Etki Alanı Uyarlaması
La Etki Alanı Uyarlaması ve Transfer Öğreniminin özel bir formu şu durumlarda kullanılır: kaynak etki alanı ve hedef etki alanı aynı sınıfları paylaşır ancak veri dağılımlarına sahiptir farklı. Örneğin profesyonel ürün fotoğrafları konusunda eğitim almış bir modelin çalışması gerekir fabrikada değişken ışıklandırmayla çekilen fotoğraflarda. Gibi teknikler LANET ETMEK (Etki Alanı-Rakip Sinir Ağı), ağı zorlayan bir etki alanı ayırıcısı ekler etki alanında değişmeyen özellikleri öğrenmek için.
2.4 Sıfır Atış ve Birkaç Atış Transferi
Gibi modellerin ortaya çıkmasıyla KLİP (Karşılaştırmalı Dil-Görüntü Ön Eğitimi), Görselleri kategorilere ayırmak mümkündür hiç görülmedi eğitim sırasında (sıfır atış) veya çok az örnekle (az atış). CLIP öğrenir ortak bir metin-görüntü gösterimi: "bir kusurun fotoğrafı" gibi bir metin istemi verildiğinde Kaynak" özelliği sayesinde model, herhangi bir özel eğitim gerektirmeden görüntüleri sınıflandırabilir.
Transfer Öğrenme Stratejilerinin Karşılaştırılması
| Strateji | Gerekli Veriler | Eğitim süresi | Performans | Aşırı uyum riski |
|---|---|---|---|---|
| Özellik Çıkarma | 100-1000 | dakika | İyi | Çok düşük |
| Kısmi İnce Ayar | 1000-10000 | Saat | Çok güzel | Bas |
| Tam İnce Ayar | 10000+ | Saat-Gün | Harika | Orta |
| Etki Alanı Uyarlaması | Değişken | Saat | İyi-Mükemmel | Orta |
| Sıfır Atış (CLIP) | 0 | Hiç kimse | Değişken | Hiç kimse |
3. Bilgisayarla Görme için Önceden Eğitilmiş Modeller
Önceden eğitilmiş modeli seçmek çok önemli bir karardır. Her mimarinin ödünleşimleri vardır doğruluk, çıkarım hızı, model boyutu ve bellek gereksinimleri arasında farklılık gösterir. İşte 2025-2026'da en çok kullanılan modellere genel bir bakış.
Karşılaştırma Tablosu Önceden Eğitimli Modeller
| Modeli | Parametreler | ImageNet İlk-1 | Tip | İdeal Kullanım |
|---|---|---|---|---|
| ResNet-50 | 25,6 milyon | %76,1 (v1) / %80,9 (v2) | CNN | Sağlam temel, kolay dağıtım |
| EfficientNet-B0 | 5,3 milyon | %77,1 | CNN | Mobil, uç, sınırlı kaynaklar |
| EfficientNet-B7 | 66M | %84,3 | CNN | Maksimum CNN doğruluğu |
| ViT-B/16 | 86 milyon | %77,9 (ImageNet-1k) | Transformatörler | Büyük veri kümeleri, büyük ölçekli ön eğitim |
| ConvNeXt-T | 28,6 milyon | %82,1 | Modern CNN | En iyi doğruluk/hız dengesi |
| ConvNeXt-B | 88,6 milyon | %83,8 | Modern CNN | CNN ile yüksek doğruluğa ihtiyaç duyduğunuzda |
| Swin-T | 28,3 milyon | %81,3 | Transformatörler | Tespit ve segmentasyon |
| KLİP ViT-B/32 | 151M (görüntüleme) | %63,2 (sıfır atış) | Çok modlu | Sıfır atış, görsel arama |
| DINOv2 ViT-S/14 | 22M | %81,1 (doğrusal prob) | Kendi kendini denetleyen | Genel özellikler, az sayıda etiketli veri |
3.1 ResNet-50: Beygir Gücü
ResNet-50 Transfer Öğrenmenin en popüler modeli olmayı sürdürüyor. basitlik, eğitimde istikrar ve geniş ekosistem desteği. Bağlantıları atla (tanıtıldı) önceki makalede) eğim sorunlarını ortadan kaldırmadan derin ağları eğitmenize olanak tanır. Ağırlıkların (IMAGENET1K_V2) Mixup, CutMix ve gibi modern tekniklerle eğitilmiş V2 versiyonu Rastgele Silme, etkileyici bir %80,9 ilk 1'e ulaşır.
3.2 EfficientNet: Bileşik Ölçeklenebilirlik
Aile EfficientNet bir yöntem kullanır bileşik ölçeklendirme ne ölçek Ağın derinliği, genişliği ve çözünürlüğü eşit şekilde sağlanır. EfficientNet-B0 aşağıdakiler için idealdir: Sınırlı kaynaklara (5,3 milyon parametre) sahip cihazlar, B7 ise en yüksek doğruluğu sunar (%84,3) çok daha büyük bir model (66M parametre) pahasına.
3.3 Görüş Transformatörü (ViT) ve Swin Transformatörü
I Görüş Transformatörleri Transformer mimarisini uygulayın (başlangıçta NLP için yaratılmış) bilgisayar görüşüne dönüştürün. Görüntü parçalara bölünmüştür (örneğin 16x16 piksel), her yama bir "belirteç" olarak ele alınır ve kişisel dikkatle işlenir. ViT excel büyük veri kümeleri (ImageNet-21k, JFT-300M) üzerinde önceden eğitildiğinde ancak daha az olabilir CNN'lere kıyasla küçük veri kümelerinde etkilidir. Swin Transformatörü tanıtıyor sürgülü pencerelere (kaydırılmış pencereler) dikkat edilerek daha verimli ve özellikle algılama ve segmentasyon gibi yoğun görevler için uygundur.
3.4 ConvNeXt: Modernize edilmiş CNN
Dönüşüm CNN'lerin modernleştirilmesi halinde Transformers'la rekabet edebileceğini kanıtlıyor aynı eğitim teknikleriyle (AdamW, Mixup, katman ölçeği, Stokastik Derinlik). ConvNeXt-T yalnızca 28,6 milyon parametreyle %82,1'e ulaşır ve doğruluk arasında mükemmel bir denge sunar, dağıtım hızı ve basitliği.
3.5 DINOv2: Kendi Kendine Denetimli Öğrenme
DINOv2 ve kendi kendini denetleyen öğrenmeyle eğitilmiş bir model (etiketsiz) büyük bir seçilmiş veri kümesinde (LVD-142M görüntüleri). Çıkarılan özellikler son derece geneldir ve aktarılabilir: üstüne eklenen basit bir doğrusal sınıflandırıcı, sonuçlara ulaşır denetlenen modellerin tam ince ayarıyla rekabet edebilir. Ve özellikle şu durumlarda faydalıdır: hedef alanda etiketlenmiş çok az veriniz var.
4. Transfer Öğrenme Ne Zaman Kullanılmalı: Karar Matrisi
Strateji seçimi iki temel faktöre bağlıdır: veri kümesinin boyutu hedef ve benzerlik Kaynak etki alanı ile hedef etki alanı arasında. Bu, dört karar çeyreği oluşturur.
SOMIGLIANZA CON SOURCE DOMAIN
Alta Bassa
+-------------------------+-------------------------+
| | |
Grande | QUADRANTE 1 | QUADRANTE 2 |
(10k+) | Fine-tuning completo | Fine-tuning con |
| - Unfreeze tutti i | cautela |
D | layer | - Unfreeze solo layer |
A | - Learning rate basso | finali |
T | - Alta performance | - Learning rate molto |
A | | basso per backbone |
S | Es: Razze di cani | - Augmentation forte |
E | (ImageNet contiene | |
T | già molti cani) | Es: Immagini medicali |
| | (molto diverse da |
S +-------------------------+ ImageNet) |
I | +-------------------------+
Z | QUADRANTE 3 | QUADRANTE 4 |
E | Feature Extraction | Opzioni limitate |
| - Freeze backbone | - Prova feature |
Piccolo | - Solo train | extraction |
(100-1k) | classifier | - Augmentation molto |
| - No overfitting | aggressiva |
| - Training veloce | - Considera raccolta |
| | più dati |
| Es: 200 foto di fiori | - DINOv2 / CLIP |
| (simili a ImageNet) | (self-supervised) |
+-------------------------+-------------------------+
Pratik Kural
2025-2026'da “Transfer Öğrenmeyi Kullanmalı mıyım?” sorusunun cevabı ve neredeyse her zaman si. Bir CNN'i sıfırdan eğitmek ve yalnızca çok özel durumlarda gerekçelendirilmesi: Devasa veri kümeleri (milyonlarca görüntü), doğal görüntülerden kökten farklı alan adı (örneğin spektrogramlar, radar sinyalleri) veya belirli mimari kısıtlamalar.
5. PyTorch ile Uygulama
Hadi uygulamaya geçelim. Transfer Learning'i PyTorch'ta adım adım uygulayacağız, önceden eğitilmiş bir modelin yüklenmesinden başlayarak tam eğitime kadar.
5.1 Önceden Eğitilmiş Bir Modelin Yüklenmesi
PyTorch, önceden eğitilmiş modellerin yüklenmesi için iki API sunar. Modern API (tanımlandı)
torchvision 0.13+) numaralandırmayı kullanır Weights detaylı bilgi sağlayan
gerekli ön işleme dönüşümleri de dahil olmak üzere ağırlıklar üzerinde.
import torch
import torch.nn as nn
from torchvision import models
from torchvision.models import ResNet50_Weights
# ---- API moderna (raccomandata): usa l'enum Weights ----
# Carica ResNet-50 con pesi IMAGENET1K_V2 (80.9% top-1)
weights = ResNet50_Weights.IMAGENET1K_V2
model = models.resnet50(weights=weights)
# I pesi includono le trasformazioni di preprocessing
preprocess = weights.transforms()
# Questo crea automaticamente: Resize(232) -> CenterCrop(224)
# -> ToTensor() -> Normalize(mean, std)
print(f"Modello caricato: {sum(p.numel() for p in model.parameters()):,} parametri")
print(f"Device: {next(model.parameters()).device}")
# ---- API legacy (deprecata ma ancora funzionante) ----
# model = models.resnet50(pretrained=True) # NON usare più
# ---- Ispeziona la struttura del modello ----
# ResNet-50 ha: conv1, bn1, relu, maxpool, layer1-4, avgpool, fc
print(model)
5.2 Özellik Çıkarma: Sınıflandırıcıyı Dondurun ve Değiştirin
ResNet-50'yi özellik çıkarıcı olarak kullanmak için şunları yapmamız gerekir: (1) tüm omurga parametrelerini dondurmak, (2) tamamen bağlı son katmanı, sınıf sayımıza uygun yeni bir katmanla değiştirin.
import torch
import torch.nn as nn
from torchvision import models
from torchvision.models import ResNet50_Weights
def create_feature_extractor(num_classes: int, device: str = 'cuda') -> nn.Module:
"""Crea un feature extractor basato su ResNet-50 pre-trained.
Args:
num_classes: Numero di classi per il task target
device: Device su cui caricare il modello
Returns:
Modello con backbone congelato e classificatore trainable
"""
# 1. Carica il modello pre-addestrato
weights = ResNet50_Weights.IMAGENET1K_V2
model = models.resnet50(weights=weights)
# 2. CONGELA tutti i parametri del backbone
for param in model.parameters():
param.requires_grad = False
# 3. Sostituisci il classificatore finale
# Il layer fc originale: Linear(2048 -> 1000)
num_features = model.fc.in_features # 2048
model.fc = nn.Sequential(
nn.Linear(num_features, 512),
nn.ReLU(inplace=True),
nn.Dropout(0.3),
nn.Linear(512, num_classes)
)
# I nuovi layer hanno requires_grad=True di default
# 4. Sposta sul device
model = model.to(device)
# Verifica parametri trainable vs congelati
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
total = sum(p.numel() for p in model.parameters())
frozen = total - trainable
print(f"Parametri totali: {total:,}")
print(f"Parametri congelati: {frozen:,} ({frozen/total*100:.1f}%)")
print(f"Parametri trainable: {trainable:,} ({trainable/total*100:.1f}%)")
return model
# Esempio: classificazione binaria (difettoso / non difettoso)
model = create_feature_extractor(num_classes=2)
# Output:
# Parametri totali: 24,607,170
# Parametri congelati: 23,508,032 (95.5%)
# Parametri trainable: 1,099,138 (4.5%)
5.3 Transfer Öğrenimi için Veri Arttırma
Veri artırma, özellikle küçük veri kümeleriyle Transfer Öğreniminde temeldir. Dönüşümler, önceden eğitilmiş modelin ön işlemiyle uyumlu olmalıdır: özellikle normalleştirme ImageNet ortalamasını ve standart sapmayı kullanmalıdır (ortalama=[0,485, 0,456, 0,406], std=[0,229, 0,224, 0,225]).
from torchvision import transforms
from torchvision.transforms import v2 # API v2 (PyTorch 2.0+)
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
# ---- Normalizzazione ImageNet (OBBLIGATORIA per modelli pre-trained) ----
IMAGENET_MEAN = [0.485, 0.456, 0.406]
IMAGENET_STD = [0.229, 0.224, 0.225]
# ---- Trasformazioni per il training (con augmentation) ----
train_transforms = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomVerticalFlip(p=0.2),
transforms.RandomRotation(degrees=15),
transforms.ColorJitter(
brightness=0.3,
contrast=0.3,
saturation=0.2,
hue=0.1
),
transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),
transforms.RandomGrayscale(p=0.05),
transforms.ToTensor(),
transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
transforms.RandomErasing(p=0.2, scale=(0.02, 0.15)),
])
# ---- Trasformazioni per validazione/test (SENZA augmentation) ----
val_transforms = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
# ---- Caricamento dataset con ImageFolder ----
# Struttura directory richiesta:
# data/
# train/
# classe_A/ (img1.jpg, img2.jpg, ...)
# classe_B/ (img1.jpg, img2.jpg, ...)
# val/
# classe_A/ (...)
# classe_B/ (...)
train_dataset = ImageFolder(root='data/train', transform=train_transforms)
val_dataset = ImageFolder(root='data/val', transform=val_transforms)
train_loader = DataLoader(
train_dataset,
batch_size=32,
shuffle=True,
num_workers=4,
pin_memory=True,
drop_last=True
)
val_loader = DataLoader(
val_dataset,
batch_size=64,
shuffle=False,
num_workers=4,
pin_memory=True
)
print(f"Classi: {train_dataset.classes}")
print(f"Training: {len(train_dataset)} immagini")
print(f"Validation: {len(val_dataset)} immagini")
RandAugment ve TrivialAugmentWide
Arttırma seçimini basitleştirmek için PyTorch otomatik politikalar sunar.
RandArttırma M yoğunluğuna sahip N rastgele dönüşüm uygulayın.
Önemsiz ArttırmaGeniş rastgele yoğunlukta tek bir dönüşüm uygular,
ve genellikle karmaşık stratejilerden daha etkilidir. Sadece manuel dönüşümleri değiştirin
tek satırla:
transforms.TrivialAugmentWide() veya
transforms.RandAugment(num_ops=2, magnitude=9).
5.4 Eğitim Döngüsünü Tamamlayın
Transfer Öğrenimi için eğitim döngüsü standart olana benzer, ancak bazı önlemler vardır: daha düşük öğrenme oranları, kademeli ısınma ve aşırı uyumun dikkatli bir şekilde izlenmesi.
import torch
import torch.nn as nn
from torch.optim import AdamW
from torch.optim.lr_scheduler import CosineAnnealingWarmRestarts
from typing import Tuple
import time
def train_one_epoch(
model: nn.Module,
train_loader,
criterion: nn.Module,
optimizer: torch.optim.Optimizer,
device: str,
epoch: int
) -> Tuple[float, float]:
"""Addestra il modello per un'epoca."""
model.train()
running_loss = 0.0
correct = 0
total = 0
for batch_idx, (images, labels) in enumerate(train_loader):
images = images.to(device, non_blocking=True)
labels = labels.to(device, non_blocking=True)
# Forward pass
outputs = model(images)
loss = criterion(outputs, labels)
# Backward pass
optimizer.zero_grad(set_to_none=True) # Più efficiente di zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
# Metriche
running_loss += loss.item() * images.size(0)
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
epoch_loss = running_loss / total
epoch_acc = 100.0 * correct / total
return epoch_loss, epoch_acc
@torch.no_grad()
def evaluate(
model: nn.Module,
val_loader,
criterion: nn.Module,
device: str
) -> Tuple[float, float]:
"""Valuta il modello sul validation set."""
model.eval()
running_loss = 0.0
correct = 0
total = 0
for images, labels in val_loader:
images = images.to(device, non_blocking=True)
labels = labels.to(device, non_blocking=True)
outputs = model(images)
loss = criterion(outputs, labels)
running_loss += loss.item() * images.size(0)
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
val_loss = running_loss / total
val_acc = 100.0 * correct / total
return val_loss, val_acc
def train_transfer_learning(
model: nn.Module,
train_loader,
val_loader,
num_epochs: int = 30,
lr: float = 1e-3,
device: str = 'cuda'
) -> dict:
"""Pipeline completa di training per Transfer Learning."""
model = model.to(device)
# Ottimizzatore: solo parametri trainable (non congelati)
trainable_params = [p for p in model.parameters() if p.requires_grad]
optimizer = AdamW(trainable_params, lr=lr, weight_decay=1e-4)
# Scheduler: cosine annealing con warm restarts
scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2)
# Loss function con label smoothing
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
# Tracking
best_val_acc = 0.0
history = {'train_loss': [], 'val_loss': [], 'train_acc': [], 'val_acc': []}
for epoch in range(num_epochs):
start_time = time.time()
# Training
train_loss, train_acc = train_one_epoch(
model, train_loader, criterion, optimizer, device, epoch
)
# Validation
val_loss, val_acc = evaluate(model, val_loader, criterion, device)
# Scheduler step
scheduler.step()
# Salvataggio metriche
history['train_loss'].append(train_loss)
history['val_loss'].append(val_loss)
history['train_acc'].append(train_acc)
history['val_acc'].append(val_acc)
elapsed = time.time() - start_time
print(
f"Epoch {epoch+1}/{num_epochs} "
f"[{elapsed:.1f}s] "
f"Train: {train_loss:.4f} ({train_acc:.1f}%) | "
f"Val: {val_loss:.4f} ({val_acc:.1f}%)"
)
# Salva il miglior modello
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save({
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'val_acc': val_acc,
}, 'best_model.pth')
print(f" --> Nuovo miglior modello salvato! Val Acc: {val_acc:.2f}%")
print(f"\nMiglior Val Accuracy: {best_val_acc:.2f}%")
return history
6. Gelişmiş İnce Ayar
Temel ince ayar (tüm katmanların tek bir öğrenme oranıyla çözülmesi) çoğu zaman işe yaramaz. ve en uygun strateji. Gelişmiş teknikler önemli performans elde etmenizi sağlar özellikle orta büyüklükteki veri kümeleriyle daha iyidir.
6.1 Ayırt Edici Öğrenme Oranları
Fikir basit ama güçlü: atama farklı öğrenme oranları katman gruplarına farklı. İlk katmanların (genel özellikleri öğrenmiş olan) güncellenmesi gerekiyor çok az, ara katmanlar biraz daha fazla ve öğrenme oranıyla son sınıflandırıcı daha uzun. Bu, son katmanları uyarlarken ilk katmanlardaki bilgiyi korur.
from torchvision import models
from torchvision.models import ResNet50_Weights
import torch.nn as nn
from torch.optim import AdamW
def create_finetuning_model(num_classes: int, device: str = 'cuda'):
"""Crea modello con gruppi di parametri per discriminative LR."""
weights = ResNet50_Weights.IMAGENET1K_V2
model = models.resnet50(weights=weights)
# Sostituisci il classificatore
num_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Linear(num_features, 512),
nn.ReLU(inplace=True),
nn.Dropout(0.3),
nn.Linear(512, num_classes)
)
model = model.to(device)
# Definisci gruppi di parametri con LR diversi
# Gruppo 1: Layer iniziali (conv1, bn1, layer1, layer2) - LR molto basso
# Gruppo 2: Layer intermedi (layer3) - LR medio
# Gruppo 3: Layer finali (layer4) - LR più alto
# Gruppo 4: Classificatore (fc) - LR massimo
base_lr = 1e-4
param_groups = [
{
'params': list(model.conv1.parameters()) +
list(model.bn1.parameters()) +
list(model.layer1.parameters()) +
list(model.layer2.parameters()),
'lr': base_lr * 0.01, # 1e-6
'name': 'early_layers'
},
{
'params': list(model.layer3.parameters()),
'lr': base_lr * 0.1, # 1e-5
'name': 'mid_layers'
},
{
'params': list(model.layer4.parameters()),
'lr': base_lr, # 1e-4
'name': 'late_layers'
},
{
'params': list(model.fc.parameters()),
'lr': base_lr * 10, # 1e-3
'name': 'classifier'
},
]
optimizer = AdamW(param_groups, weight_decay=1e-4)
# Verifica i learning rate
for group in optimizer.param_groups:
n_params = sum(p.numel() for p in group['params'])
print(f"{group.get('name', 'unknown'):<15} LR={group['lr']:.2e} "
f"Params={n_params:,}")
return model, optimizer
6.2 Kademeli Çözme
Tüm katmanları bir kerede çözmek yerine, kademeli buz çözme son katmanlardan ilkine, çağdan çağa doğru ilerler. Bu, sınıflandırıcıya zaman kazandırır. Yıkıcı güncellemelerden kaçınarak temel özellikleri değiştirmeden önce uyum sağlayın.
import torch.nn as nn
from torchvision import models
from torchvision.models import ResNet50_Weights
def gradual_unfreeze_training(
num_classes: int,
train_loader,
val_loader,
device: str = 'cuda'
):
"""Training con scongelamento graduale dei layer."""
# Carica modello e congela tutto
weights = ResNet50_Weights.IMAGENET1K_V2
model = models.resnet50(weights=weights)
# Congela tutti i layer
for param in model.parameters():
param.requires_grad = False
# Sostituisci il classificatore (trainable di default)
model.fc = nn.Sequential(
nn.Linear(model.fc.in_features, 512),
nn.ReLU(inplace=True),
nn.Dropout(0.3),
nn.Linear(512, num_classes)
)
model = model.to(device)
# Definisci le fasi di unfreezing
unfreeze_schedule = [
# (epoch_start, layers_to_unfreeze, learning_rate)
(0, [], 1e-3), # Solo classifier
(5, [model.layer4], 5e-4), # + layer4
(10, [model.layer3], 2e-4), # + layer3
(15, [model.layer2], 1e-4), # + layer2
(20, [model.layer1, model.conv1, model.bn1], 5e-5), # Tutto
]
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
current_phase = 0
for epoch in range(30):
# Controlla se dobbiamo scongeleare nuovi layer
if (current_phase < len(unfreeze_schedule) - 1 and
epoch >= unfreeze_schedule[current_phase + 1][0]):
current_phase += 1
phase = unfreeze_schedule[current_phase]
# Scongela i nuovi layer
for layer_group in phase[1]:
for param in layer_group.parameters():
param.requires_grad = True
# Ricrea optimizer con nuovo LR
trainable_params = [
p for p in model.parameters() if p.requires_grad
]
optimizer = torch.optim.AdamW(
trainable_params, lr=phase[2], weight_decay=1e-4
)
n_trainable = sum(
p.numel() for p in model.parameters() if p.requires_grad
)
print(f"\n=== Fase {current_phase}: Epoch {epoch} ===")
print(f"Parametri trainable: {n_trainable:,}")
print(f"Learning rate: {phase[2]:.2e}")
elif epoch == 0:
# Prima fase: solo classifier
optimizer = torch.optim.AdamW(
model.fc.parameters(), lr=1e-3, weight_decay=1e-4
)
# Training e validation standard
# (usa le funzioni train_one_epoch e evaluate definite prima)
train_loss, train_acc = train_one_epoch(
model, train_loader, criterion, optimizer, device, epoch
)
val_loss, val_acc = evaluate(
model, val_loader, criterion, device
)
print(
f"Epoch {epoch+1}/30 | "
f"Train: {train_loss:.4f} ({train_acc:.1f}%) | "
f"Val: {val_loss:.4f} ({val_acc:.1f}%)"
)
6.3 Öğrenme Hızı Isınması ve Kosinüs Tavlaması
Erken dönemlerde öğrenme oranının kademeli olarak ısınması, çok agresif güncellemeleri önler önceden eğitilmiş ağırlıkları yok edebilir. Isınmadan sonra küçük bir tavlama programı gittikçe hassaslaşan ince ayar için öğrenme oranını kademeli olarak azaltır.
import math
from torch.optim.lr_scheduler import LambdaLR
def get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps: int,
num_training_steps: int,
min_lr_ratio: float = 0.01
):
"""Cosine annealing con linear warmup.
Args:
optimizer: L'ottimizzatore PyTorch
num_warmup_steps: Numero di step di warmup
num_training_steps: Numero totale di step di training
min_lr_ratio: Rapporto LR minimo / LR massimo
Returns:
LambdaLR scheduler
"""
def lr_lambda(current_step: int) -> float:
# Fase di warmup: LR cresce linearmente da 0 a max
if current_step < num_warmup_steps:
return float(current_step) / float(max(1, num_warmup_steps))
# Fase di cosine annealing: LR decresce con coseno
progress = float(current_step - num_warmup_steps) / float(
max(1, num_training_steps - num_warmup_steps)
)
cosine_decay = 0.5 * (1.0 + math.cos(math.pi * progress))
return max(min_lr_ratio, cosine_decay)
return LambdaLR(optimizer, lr_lambda)
# Esempio di utilizzo
num_epochs = 30
steps_per_epoch = len(train_loader)
total_steps = num_epochs * steps_per_epoch
warmup_steps = 3 * steps_per_epoch # 3 epoche di warmup
scheduler = get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps=warmup_steps,
num_training_steps=total_steps
)
# Nel training loop, chiama scheduler.step() DOPO ogni batch
# for batch in train_loader:
# ...
# optimizer.step()
# scheduler.step() # Step per batch, non per epoca
Learning Rate durante il Training (warmup + cosine annealing):
LR
^
| ___
| / \
| / \
| / \
| / \
| / \___
| / \___
| / \___
|/ \___
+--+--------+--------+--------+--------+---> Epoch
0 warmup 5 10 20 30
Fase 1 (Epoch 0-3): Linear Warmup
LR cresce linearmente da ~0 a LR_max
Fase 2 (Epoch 3-30): Cosine Annealing
LR decresce seguendo una curva coseno fino a min_lr
6.4 Karıştırma ve CutMix Artırma
Karışıklıklar e Kesim Karışımı bunlar gelişmiş büyütme teknikleridir özellikle ince ayarda etkilidir. Mixup, doğrusal bir kombinasyon olarak yeni örnekler oluşturur iki resim ve bunların ilgili etiketleri. CutMix farklı görüntüler arasındaki dikdörtgenleri keser ve yapıştırır. Her ikisi de güçlü düzenleyiciler olarak çalışır ve genellemeyi geliştirir.
from torchvision.transforms import v2
# CutMix e MixUp applicati a livello di batch (non di singola immagine)
cutmix = v2.CutMix(num_classes=10)
mixup = v2.MixUp(num_classes=10, alpha=0.2)
# Combina entrambi con probabilità 50/50
cutmix_or_mixup = v2.RandomChoice([cutmix, mixup])
# Nel training loop:
# for images, labels in train_loader:
# images, labels = cutmix_or_mixup(images, labels)
# # Nota: labels ora sono soft (one-hot probabilistico)
# # La CrossEntropyLoss gestisce automaticamente soft labels
# outputs = model(images)
# loss = criterion(outputs, labels)
7. Transfer Öğrenimi için Gelişmiş Veri Arttırma
Veri artırma seçiminin performans üzerinde büyük etkisi vardır, özellikle küçük veri kümeleriyle. 2025-2026'daki en etkili seçenekleri görelim.
7.1 Albümlemeler: Profesyonel Düzeyde Arttırma
Albümasyonlar ve görüntü verilerinin arttırılması için özel bir kütüphane, OpenCV kullanımı nedeniyle torchvision.transforms'tan önemli ölçüde daha hızlı. Sunuyor özellikle uzmanlaşmış alanlar (medikal, endüstriyel, uydu) için faydalı dönüşümler.
import albumentations as A
from albumentations.pytorch import ToTensorV2
import cv2
import numpy as np
from torch.utils.data import Dataset
from PIL import Image
def get_albumentations_transforms(is_training: bool = True):
"""Pipeline di augmentation con Albumentations."""
if is_training:
return A.Compose([
A.RandomResizedCrop(height=224, width=224, scale=(0.7, 1.0)),
A.HorizontalFlip(p=0.5),
A.VerticalFlip(p=0.2),
# Augmentation geometriche
A.ShiftScaleRotate(
shift_limit=0.1,
scale_limit=0.15,
rotate_limit=15,
border_mode=cv2.BORDER_REFLECT_101,
p=0.5
),
# Augmentation di colore/illuminazione
A.OneOf([
A.RandomBrightnessContrast(
brightness_limit=0.3, contrast_limit=0.3, p=1.0
),
A.HueSaturationValue(
hue_shift_limit=10, sat_shift_limit=30,
val_shift_limit=20, p=1.0
),
A.CLAHE(clip_limit=4.0, p=1.0),
], p=0.7),
# Rumore e blur
A.OneOf([
A.GaussNoise(var_limit=(10, 50), p=1.0),
A.GaussianBlur(blur_limit=(3, 5), p=1.0),
A.MotionBlur(blur_limit=5, p=1.0),
], p=0.3),
# Dropout spaziale
A.CoarseDropout(
max_holes=8, max_height=16, max_width=16,
min_holes=1, min_height=8, min_width=8,
fill_value=0, p=0.3
),
# Normalizzazione ImageNet
A.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
),
ToTensorV2(),
])
else:
return A.Compose([
A.Resize(256, 256),
A.CenterCrop(224, 224),
A.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
),
ToTensorV2(),
])
class AlbumentationsDataset(Dataset):
"""Dataset wrapper che usa Albumentations per le trasformazioni."""
def __init__(self, image_paths: list, labels: list, transform=None):
self.image_paths = image_paths
self.labels = labels
self.transform = transform
def __len__(self) -> int:
return len(self.image_paths)
def __getitem__(self, idx: int):
# Leggi immagine con OpenCV (BGR -> RGB)
image = cv2.imread(self.image_paths[idx])
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
label = self.labels[idx]
if self.transform:
augmented = self.transform(image=image)
image = augmented['image']
return image, label
8. Vaka Çalışması: ResNet-50 ile Endüstriyel Kusurların Sınıflandırılması
Hepsini gerçek bir kullanım örneğiyle bir araya getirelim: endüstriyel kalite kontrol sistemi Elektronik bileşenlerdeki kusurları sınıflandıran. Veri kümesi yüksek çözünürlüklü görüntüler içeriyor Dört sınıfa sahip baskılı devre kartlarının (PCB'ler): OK (kusur yok), lehim köprüsü (kaynak köprüsü), eksik_bileşen (eksik bileşen) e çizik (çizik).
Örnek Olay Kurulumu
- Veri kümeleri: 4000 görüntü (sınıf başına 1000), 70/15/15'e bölünmüş
- Çözünürlük: 512x512 piksel, RGB
- Modeli: ImageNet üzerinde önceden eğitilmiş ResNet-50 (V2 ağırlıkları)
- Strateji: Özellik çıkarma -> Kademeli ince ayar
- Donanım: 8GB+ VRAM'li NVIDIA GPU
"""
Pipeline completa per classificazione difetti industriali.
Transfer Learning con ResNet-50 e gradual unfreezing.
"""
import torch
import torch.nn as nn
from torch.optim import AdamW
from torch.utils.data import DataLoader
from torchvision import models, transforms
from torchvision.models import ResNet50_Weights
from torchvision.datasets import ImageFolder
from sklearn.metrics import (
classification_report,
confusion_matrix,
f1_score
)
import numpy as np
from pathlib import Path
# ============================================================
# 1. CONFIGURAZIONE
# ============================================================
class Config:
"""Configurazione centralizzata del training."""
data_dir = Path('data/pcb_defects')
num_classes = 4
class_names = ['OK', 'missing_component', 'scratch', 'solder_bridge']
# Training
batch_size = 32
num_workers = 4
phase1_epochs = 10 # Feature extraction
phase2_epochs = 15 # Fine-tuning layer4
phase3_epochs = 10 # Full fine-tuning
phase1_lr = 1e-3
phase2_lr = 1e-4
phase3_lr = 1e-5
# Device
device = 'cuda' if torch.cuda.is_available() else 'cpu'
# ============================================================
# 2. PREPARAZIONE DATI
# ============================================================
IMAGENET_MEAN = [0.485, 0.456, 0.406]
IMAGENET_STD = [0.229, 0.224, 0.225]
train_transforms = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomVerticalFlip(p=0.5),
transforms.RandomRotation(degrees=20),
transforms.ColorJitter(brightness=0.3, contrast=0.3),
transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),
transforms.ToTensor(),
transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
transforms.RandomErasing(p=0.2),
])
val_transforms = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
cfg = Config()
train_dataset = ImageFolder(cfg.data_dir / 'train', transform=train_transforms)
val_dataset = ImageFolder(cfg.data_dir / 'val', transform=val_transforms)
test_dataset = ImageFolder(cfg.data_dir / 'test', transform=val_transforms)
train_loader = DataLoader(
train_dataset, batch_size=cfg.batch_size,
shuffle=True, num_workers=cfg.num_workers, pin_memory=True
)
val_loader = DataLoader(
val_dataset, batch_size=cfg.batch_size * 2,
shuffle=False, num_workers=cfg.num_workers, pin_memory=True
)
test_loader = DataLoader(
test_dataset, batch_size=cfg.batch_size * 2,
shuffle=False, num_workers=cfg.num_workers, pin_memory=True
)
# ============================================================
# 3. MODELLO
# ============================================================
def build_model(num_classes: int, device: str) -> nn.Module:
"""Crea il modello ResNet-50 per il Transfer Learning."""
weights = ResNet50_Weights.IMAGENET1K_V2
model = models.resnet50(weights=weights)
# Congela tutto
for param in model.parameters():
param.requires_grad = False
# Nuovo classificatore
model.fc = nn.Sequential(
nn.Linear(2048, 512),
nn.ReLU(inplace=True),
nn.Dropout(0.4),
nn.Linear(512, 128),
nn.ReLU(inplace=True),
nn.Dropout(0.2),
nn.Linear(128, num_classes)
)
return model.to(device)
# ============================================================
# 4. TRAINING MULTI-FASE
# ============================================================
def run_training():
"""Esegue il training multi-fase con gradual unfreezing."""
model = build_model(cfg.num_classes, cfg.device)
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
best_val_acc = 0.0
# ---- Fase 1: Feature Extraction ----
print("=" * 60)
print("FASE 1: Feature Extraction (solo classifier)")
print("=" * 60)
optimizer = AdamW(model.fc.parameters(), lr=cfg.phase1_lr, weight_decay=1e-4)
for epoch in range(cfg.phase1_epochs):
train_loss, train_acc = train_one_epoch(
model, train_loader, criterion, optimizer, cfg.device, epoch
)
val_loss, val_acc = evaluate(
model, val_loader, criterion, cfg.device
)
print(f" Epoch {epoch+1}/{cfg.phase1_epochs} | "
f"Train: {train_acc:.1f}% | Val: {val_acc:.1f}%")
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save(model.state_dict(), 'best_model.pth')
# ---- Fase 2: Fine-tuning layer4 ----
print("\n" + "=" * 60)
print("FASE 2: Fine-tuning layer4 + classifier")
print("=" * 60)
for param in model.layer4.parameters():
param.requires_grad = True
optimizer = AdamW([
{'params': model.layer4.parameters(), 'lr': cfg.phase2_lr * 0.1},
{'params': model.fc.parameters(), 'lr': cfg.phase2_lr},
], weight_decay=1e-4)
for epoch in range(cfg.phase2_epochs):
train_loss, train_acc = train_one_epoch(
model, train_loader, criterion, optimizer, cfg.device, epoch
)
val_loss, val_acc = evaluate(
model, val_loader, criterion, cfg.device
)
print(f" Epoch {epoch+1}/{cfg.phase2_epochs} | "
f"Train: {train_acc:.1f}% | Val: {val_acc:.1f}%")
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save(model.state_dict(), 'best_model.pth')
# ---- Fase 3: Full Fine-tuning ----
print("\n" + "=" * 60)
print("FASE 3: Full Fine-tuning (tutti i layer)")
print("=" * 60)
for param in model.parameters():
param.requires_grad = True
optimizer = AdamW([
{'params': model.conv1.parameters(), 'lr': cfg.phase3_lr * 0.01},
{'params': model.layer1.parameters(), 'lr': cfg.phase3_lr * 0.1},
{'params': model.layer2.parameters(), 'lr': cfg.phase3_lr * 0.1},
{'params': model.layer3.parameters(), 'lr': cfg.phase3_lr},
{'params': model.layer4.parameters(), 'lr': cfg.phase3_lr * 2},
{'params': model.fc.parameters(), 'lr': cfg.phase3_lr * 10},
], weight_decay=1e-4)
for epoch in range(cfg.phase3_epochs):
train_loss, train_acc = train_one_epoch(
model, train_loader, criterion, optimizer, cfg.device, epoch
)
val_loss, val_acc = evaluate(
model, val_loader, criterion, cfg.device
)
print(f" Epoch {epoch+1}/{cfg.phase3_epochs} | "
f"Train: {train_acc:.1f}% | Val: {val_acc:.1f}%")
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save(model.state_dict(), 'best_model.pth')
print(f"\nMiglior Val Accuracy: {best_val_acc:.2f}%")
return model
8.1 Değerlendirme ve Ölçümler
Bir kalite kontrol sistemi için küresel doğruluk yeterli değildir. Analiz etmeliyiz Her kusur sınıfı için kesinlik, geri çağırma ve F1 puanı. Yanlış negatif (tespit edilemeyen kusur) yanlış bir pozitiften (iyi parçanın atılmasından) çok daha pahalıya mal olur.
@torch.no_grad()
def full_evaluation(
model: nn.Module,
test_loader,
class_names: list,
device: str = 'cuda'
) -> dict:
"""Valutazione completa con metriche dettagliate."""
model.eval()
all_preds = []
all_labels = []
all_probs = []
for images, labels in test_loader:
images = images.to(device)
outputs = model(images)
probs = torch.softmax(outputs, dim=1)
_, predicted = outputs.max(1)
all_preds.extend(predicted.cpu().numpy())
all_labels.extend(labels.numpy())
all_probs.extend(probs.cpu().numpy())
all_preds = np.array(all_preds)
all_labels = np.array(all_labels)
# Classification Report
report = classification_report(
all_labels, all_preds,
target_names=class_names,
digits=4
)
print("Classification Report:")
print(report)
# Confusion Matrix
cm = confusion_matrix(all_labels, all_preds)
print("\nConfusion Matrix:")
print(f"{'':.<15} " + " ".join(f"{name:>12}" for name in class_names))
for i, row in enumerate(cm):
print(f"{class_names[i]:<15} " + " ".join(f"{val:>12d}" for val in row))
# F1-Score (macro e weighted)
f1_macro = f1_score(all_labels, all_preds, average='macro')
f1_weighted = f1_score(all_labels, all_preds, average='weighted')
print(f"\nF1-Score Macro: {f1_macro:.4f}")
print(f"F1-Score Weighted: {f1_weighted:.4f}")
return {
'report': report,
'confusion_matrix': cm,
'f1_macro': f1_macro,
'f1_weighted': f1_weighted,
'predictions': all_preds,
'labels': all_labels,
'probabilities': np.array(all_probs)
}
# Esegui la valutazione
model.load_state_dict(torch.load('best_model.pth', weights_only=True))
results = full_evaluation(model, test_loader, Config.class_names)
8.2 TorchScript ile Dağıtım
Modeli üretime geçirmek için şuna dönüştürüyoruz: TorchScript: bir biçim Python olmadan çalışabilecek şekilde optimize edilmiş, C++ sistemlerine entegrasyon için ideal, yüksek performanslı mobil uygulamalar veya çıkarım sunucuları.
import torch
def export_model(model: torch.nn.Module, save_dir: str = 'exported_models'):
"""Esporta il modello in formati ottimizzati per la produzione."""
from pathlib import Path
save_path = Path(save_dir)
save_path.mkdir(exist_ok=True)
model.eval()
model = model.cpu()
dummy_input = torch.randn(1, 3, 224, 224)
# ---- TorchScript (tracing) ----
scripted = torch.jit.trace(model, dummy_input)
scripted_path = save_path / 'model_scripted.pt'
scripted.save(str(scripted_path))
print(f"TorchScript salvato: {scripted_path} "
f"({scripted_path.stat().st_size / 1024 / 1024:.1f} MB)")
# ---- ONNX ----
onnx_path = save_path / 'model.onnx'
torch.onnx.export(
model,
dummy_input,
str(onnx_path),
input_names=['input'],
output_names=['output'],
dynamic_axes={
'input': {0: 'batch_size'},
'output': {0: 'batch_size'},
},
opset_version=17
)
print(f"ONNX salvato: {onnx_path} "
f"({onnx_path.stat().st_size / 1024 / 1024:.1f} MB)")
# ---- Verifica ----
loaded = torch.jit.load(str(scripted_path))
with torch.no_grad():
orig_out = model(dummy_input)
loaded_out = loaded(dummy_input)
diff = (orig_out - loaded_out).abs().max().item()
print(f"Differenza max originale vs TorchScript: {diff:.2e}")
return scripted_path, onnx_path
export_model(model)
9. Nesne Algılama için Öğrenmeyi Aktarın
Transfer Öğrenme sınıflandırmayla sınırlı değildir. Tüm önemli çerçeveler nesne algılama, özellik çıkarıcı olarak önceden eğitilmiş omurgaları kullanır. bakalım Daha Hızlı R-CNN ve YOLO'ya ince ayarın nasıl uygulanacağı.
9.1 Daha Hızlı R-CNN İnce Ayarı
Daha hızlı R-CNN ResNet-50-FPN omurgası ve referans dedektörü ile meşale görüşü. FPN (Özellik Piramidi Ağı), temel olan çok ölçekli özellikleri çıkarır. Farklı boyutlardaki nesneleri tespit edin. İnce ayar kafanın değiştirilmesini gerektirir kutu tahmincisi sınıflandırması.
import torchvision
from torchvision.models.detection import (
fasterrcnn_resnet50_fpn_v2,
FasterRCNN_ResNet50_FPN_V2_Weights
)
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
def get_detection_model(num_classes: int) -> torchvision.models.detection.FasterRCNN:
"""Crea un Faster R-CNN pre-trained per il fine-tuning.
Args:
num_classes: Numero di classi target + 1 (background)
Returns:
Modello Faster R-CNN pronto per il fine-tuning
"""
# Carica il modello pre-trained su COCO (91 classi)
weights = FasterRCNN_ResNet50_FPN_V2_Weights.DEFAULT
model = fasterrcnn_resnet50_fpn_v2(weights=weights)
# Sostituisci la testa del box predictor
in_features = model.roi_heads.box_predictor.cls_score.in_features
model.roi_heads.box_predictor = FastRCNNPredictor(
in_features, num_classes
)
return model
# Esempio: rilevamento difetti (3 tipi + background = 4 classi)
det_model = get_detection_model(num_classes=4)
# Training: Faster R-CNN usa una loss composita
# (classification_loss + box_regression_loss + RPN losses)
# Non serve definire la loss manualmente: model(images, targets)
# restituisce direttamente le losses in training mode
9.2 YOLO ince ayarı
YOLO için CLI sayesinde Transfer Öğrenimi daha da kolay Ultralitik. YOLO önceden eğitilmiş bir omurga kullanır (genellikle CSPDarknet veya varyantları) ve tek bir komut satırı veya birkaç satır kodla ince ayar yapmayı destekler.
from ultralytics import YOLO
# Carica modello pre-trained su COCO
model = YOLO('yolo11n.pt') # nano (più veloce) o yolo11s.pt, yolo11m.pt
# Fine-tuning sul tuo dataset
# Il dataset deve essere in formato YOLO (images/ + labels/ con .txt)
results = model.train(
data='pcb_defects.yaml', # Config del dataset
epochs=100,
imgsz=640,
batch=16,
lr0=0.001, # Learning rate iniziale
lrf=0.01, # Learning rate finale (ratio)
warmup_epochs=3,
freeze=10, # Congela i primi 10 layer del backbone
augment=True, # Augmentation automatica
patience=20, # Early stopping
device='0' # GPU 0
)
# Valutazione sul test set
metrics = model.val(data='pcb_defects.yaml', split='test')
print(f"mAP50: {metrics.box.map50:.4f}")
print(f"mAP50-95: {metrics.box.map:.4f}")
10. Yaygın Hatalar ve Bunlardan Nasıl Kaçınılacağı
Transfer Öğrenimi teoride basit görünüyor, ancak pratikte birçok tuzak var bu da performansları mahvedebilir. İşte en sık karşılaşılan hatalar.
Transfer Öğreniminde En Önemli 10 Hata
| # | Hata | Sonuçlar | Çözüm |
|---|---|---|---|
| 1 | Öğrenme oranı çok yüksek | Önceden eğitilmiş ağırlıkları yok eder | Sıfırdan eğitimden 10-100 kat daha düşük LR kullanın (1e-4 veya daha az) |
| 2 | Yanlış normalleştirme | Tamamen yanlış özellikler | ImageNet'te önceden eğitilmiş modeller için HER ZAMAN ImageNet'in ortalamasını/std'sini kullanın |
| 3 | Veri artırma yok | Küçük veri kümeleriyle aşırı uyum | Agresif büyütme: çevirme, döndürme, renk değişimi, CutMix |
| 4 | BatchNorm değerlendirme modunda değil | Küçük gruplarla istikrarsız istatistikler | dondurulmuş özellikler için model.eval() veya BN'leri açıkça dondurur |
| 5 | Giriş görüntüleri ölçeklendirilmemiş | Hatalar veya kötü performans | 224x224 (veya modelin beklediği boyut) olarak yeniden boyutlandırın |
| 6 | Isınmadan ince ayar | İlk güncellemeler çok agresif | 3-5 dönem boyunca doğrusal ısınma |
| 7 | Tüm katmanlar için aynı LR | İlk katmanlar aşırı düzenlendi | Ayırt edici öğrenme oranları |
| 8 | Veri kümesi düzenleme olmadan çok küçük | Şiddetli aşırı uyum | Bırakma, ağırlık azalması, etiket yumuşatma, erken durdurma |
| 9 | En iyi modeli kaydetme | En son Epoch modelini kullanın (fazla donatılmış) | Doğruluk/kayıp değerine dayalı model kontrol noktası |
| 10 | Sınıf dengesizliğini göz ardı edin | Model çoğunluk sınıfına karşı önyargılı | Ağırlıklı kayıp, aşırı örnekleme, odak kaybı |
def freeze_batchnorm(model: nn.Module):
"""Congela i layer BatchNorm anche quando il modello e in train mode.
Importante quando si fa fine-tuning con batch size piccoli:
le statistiche di batch sarebbero instabili e rovinerebbero
le running_mean e running_var accumulate durante il pre-training.
"""
for module in model.modules():
if isinstance(module, (nn.BatchNorm2d, nn.BatchNorm1d)):
module.eval() # Usa running stats, non batch stats
module.weight.requires_grad = False
module.bias.requires_grad = False
# Utilizzo nel training loop:
model.train()
freeze_batchnorm(model) # BN resta in eval mode
11. Karar Ağacı: Model ve Stratejiyi Seçin
Seçiminizi kolaylaştırmak için kullanım örneklerini kapsayan pratik bir karar ağacını burada bulabilirsiniz daha yaygın.
HAI UN DATASET ETICHETTATO?
/ \
SI NO
/ \
Quante immagini? Usa CLIP (zero-shot)
/ | \ oppure DINOv2 + kNN
<500 500-10k >10k
| | |
Feature Partial Full
Extraction Fine-tune Fine-tune
| | |
Modello? Modello? Modello?
| | |
v v v
Serve velocità? Serve massima accuracy?
/ \ / \
SI NO SI NO
| | | |
EfficientNet ConvNeXt-T ConvNeXt-B ResNet-50
-B0/B1 o ResNet-50 o ViT-L (baseline)
Serve multi-scala (detection/segmentation)?
--> Swin Transformer o backbone con FPN
Dominio molto diverso da ImageNet?
--> DINOv2 (self-supervised) come backbone
Budget computazionale limitato?
--> EfficientNet-B0 con feature extraction
Uygulama Önerisi 2025-2026
Çoğu bilgisayarlı görüntü projesi için şununla başlayın: ResNet-50 V2 temel olarak. Daha fazla doğruluğa ihtiyacınız varsa şuraya geçin: ConvNeXt-T (en iyi doğruluk/hız dengesi). Çok az veri içeren görevler için şunu kullanın: DINOv2 özellik çıkarıcı olarak. Edge/mobil dağıtım için şunu seçin: EfficientNet-B0 o MobileNet-V3.
12. Sonuçlar
Transfer Öğrenme, bilgisayar görüşünü demokratikleştirdi. Bir zamanlar gerekli olan milyonlarca görüntü, haftalar süren eğitim ve pahalı donanımla bugün başarılabilir birkaç yüz görüntü, tek bir GPU ve birkaç saatlik çalışmayla. Sezgiler Bu makalenin anahtarı:
- CNN özellikleri hiyerarşiktir ve aktarılabilir: ilk katman yakalama evrensel desenler (kenarlar, dokular), ikincisi göreve özgüdür. Bu, Transferi yapar Öğrenme mümkün ve etkilidir.
- Strateji verilere ve etki alanına bağlıdır: benzer alanda çok az veri var mı? Özellikler çıkarma. Farklı etki alanlarında çok fazla veri mi var? Dikkatli bir şekilde tam ince ayar yapın. 4 çeyreklik matris karara rehberlik eder.
- Ayrıntılar fark yaratır: ayırt edici öğrenme oranları, kademeli çözülme, ısınma, doğru normalleştirme ve uygun veri artırma performansı artırabilir saf ince ayar ile karşılaştırıldığında% 5-15 oranında.
- Ekosistem olgunlaşmıştır: PyTorch, torchvision ve Ultralytics basit API'ler sunar ve yüksek kaliteli, önceden eğitilmiş modeller. Sıfırdan antrenman yapmak için neredeyse hiçbir zaman bir neden yoktur.
Serinin bir sonraki makalesinde Transfer Öğrenmeyi uygulayacağız.Nesne Algılama YOLO'lu: sadece sınıflandırmak değil Ne bir resim var ama aynı zamanda Nerede gerçek zamanlı sınırlayıcı kutularla bulunur.
Sonraki Makale
Madde 3: YOLO ile Nesne Tespiti - Gerçek zamanlı nesne tespiti, YOLO mimarisi, çapasız algılama, özel veri kümeleri ve dağıtım konusunda eğitim gerçek zamanlı uygulamalar için.







