Mục lục
- Mục tiêu bài học
- Transfer Learning là gì
- Vì sao Transfer Learning hiệu quả
- Strategy 1 — Feature Extraction
- Strategy 2 — Fine-tuning
- Quy tắc chọn strategy theo data + domain
- Load pretrained ResNet trong PyTorch
- Freeze backbone, replace head
- Inspect requires_grad
- Pretrained weights và cache
- Common pretrained backbone
- Pre-processing theo pretrained
- Differential learning rate
- Layer-wise LR decay và discriminative LR
- timm — vision model collection
- HuggingFace Transformers cho NLP
- Use case TL phổ biến
- Limitations và domain shift
- Save và share model
- Code Python — fine-tune ResNet-18 trên CIFAR-10
- Bài tập
Mục tiêu bài học
Sau bài học, bạn sẽ:
- Giải thích được Transfer Learning là gì và vì sao backbone pretrained giúp ít data vẫn cho accuracy cao.
- Phân biệt rõ hai strategy: feature extraction (freeze) và fine-tuning (tune nhẹ backbone).
- Chọn được strategy phù hợp dựa trên data size và độ tương đồng giữa task mới với ImageNet.
- Load và fine-tune ResNet pretrained bằng
torchvision.models. - Cấu hình differential learning rate cho backbone và head.
- So sánh kết quả TL với train from scratch trên CIFAR-10.
Bài này nối Bài 31 — Xây dựng CNN CIFAR-10 (train from scratch) với Bài 33 — Các kiến trúc CNN nổi bật.
Transfer Learning là gì
Transfer Learning là kỹ thuật chuyển kiến thức từ một model đã train trên dataset nguồn (source) sang task đích (target) với data ít hơn. Trong computer vision, source thường là ImageNet (1.28 triệu ảnh, 1000 class) — Russakovsky et al., 2015.
Quy trình điển hình:
- Lấy backbone đã pretrain trên ImageNet (ResNet, EfficientNet, ViT...).
- Bỏ classifier head cũ (1000 class).
- Gắn head mới phù hợp số class của task đích.
- Train trên data đích — có thể freeze hoặc tune backbone.
Yosinski et al. (2014) — "How transferable are features in deep neural networks?" (arXiv:1411.1792) — đã chứng minh feature ở layer thấp (gần input) transferable giữa các task, trong khi layer cao phụ thuộc vào task gốc.
Vì sao Transfer Learning hiệu quả
Có ba lý do cụ thể:
- Backbone đã học general features: layer đầu phát hiện edge / blob / color gradient, layer giữa học texture và part, layer cuối học object. Edge với texture là tín hiệu chung cho phần lớn ảnh tự nhiên — không phải học lại từ đầu.
- Ít data đủ dùng: với 100–1000 ảnh / class, train from scratch thường overfit nặng. Backbone pretrained đóng vai trò "regularizer mạnh" qua khởi tạo có cấu trúc.
- Hội tụ nhanh và accuracy cao hơn: model bắt đầu ở vùng tham số tốt, gradient nhỏ, vài epoch là đạt accuracy mà train from scratch cần hàng chục epoch.
Ví dụ trong Kornblith, Shlens & Le (2019) — "Do Better ImageNet Models Transfer Better?" (arXiv:1805.08974): trên 12 dataset downstream, model có accuracy ImageNet cao hơn thường cho accuracy transfer cao hơn (correlation rõ rệt nhưng không tuyệt đối).
Strategy 1 — Feature Extraction
Feature Extraction coi backbone pretrained như một bộ trích xuất feature cố định:
- Freeze backbone: đặt
requires_grad = Falsecho mọi tham số ngoài head. Optimizer sẽ không cập nhật chúng. - Replace classifier head: lớp FC cuối (1000 chiều cho ImageNet) thay bằng FC mới ứng với số class của task đích.
- Train chỉ head trên data mới. Backward vẫn chạy qua backbone để tính gradient cho head, nhưng weight backbone giữ nguyên.
Đặc điểm:
- Train rất nhanh — chỉ vài chục nghìn param của head được update.
- VRAM thấp — không cần lưu gradient cho backbone (PyTorch tự bỏ qua khi
requires_grad=False). - Ít risk overfit khi data nhỏ.
Use case: data rất ít (vài trăm ảnh), task tương tự ImageNet (vd phân loại loài chó, loài hoa, đồ vật thông thường).
Strategy 2 — Fine-tuning
Fine-tuning cho phép backbone được cập nhật nhẹ trên data mới:
- Replace classifier head (giống Strategy 1).
- Không freeze backbone, hoặc chỉ freeze vài layer đầu.
- Train cả backbone với LR thấp (vd
1e-5) + head với LR cao hơn (vd1e-3). - Backbone được "tune nhẹ" để thích nghi với domain mới mà không phá hỏng feature đã học.
Tại sao LR thấp cho backbone? Vì gradient lớn có thể làm backbone "quên" general features (catastrophic forgetting). LR thấp giúp chuyển dịch chậm trong không gian tham số.
Use case: data nhiều hơn (vài nghìn ảnh trở lên), task khác xa ImageNet nhưng vẫn là ảnh tự nhiên (vd phân loại sản phẩm e-commerce, mèo theo giống đặc biệt).
Quy tắc chọn strategy theo data + domain
Hai trục cần xem: data size và khoảng cách domain với ImageNet.
| Data size | Domain so với ImageNet | Strategy |
|---|---|---|
| Rất ít (< 1k ảnh) | Tương tự | Feature Extraction — freeze backbone, train head |
| Vừa (1k – 10k) | Tương tự | Fine-tune vài layer cuối backbone + head |
| Nhiều (> 10k) | Tương tự | Fine-tune full backbone với LR thấp |
| Nhiều (> 10k) | Khác xa (medical, satellite) | Fine-tune full backbone, LR vừa; cân nhắc domain-specific pretrain |
| Rất nhiều (> 100k) | Khác hoàn toàn | Có thể train from scratch hoặc init từ pretrained tùy benchmark |
| Rất ít | Khác xa | Trường hợp khó — TL có thể không giúp; cần augment mạnh hoặc thu thêm data |
Quy tắc nhanh: data càng ít thì freeze càng nhiều; domain càng xa thì tune càng nhiều layer. Khi không chắc, làm baseline cả hai và so sánh trên validation set.
Load pretrained ResNet trong PyTorch
torchvision.models cung cấp hàng loạt model với pretrained weights:
import torch
import torch.nn as nn
from torchvision import models
# Load ResNet-18 với weight tốt nhất hiện có
model = models.resnet18(weights="DEFAULT")
# Hoặc chỉ định version cụ thể
model_v2 = models.resnet50(weights="IMAGENET1K_V2")
# Không load weight (train from scratch)
model_blank = models.resnet18(weights=None)
API weights= được giới thiệu từ torchvision v0.13 (2022), thay cho cú pháp cũ pretrained=True (đã deprecated). Mỗi enum weight đi kèm metadata (accuracy, transforms khuyến nghị).
Freeze backbone, replace head
Pattern chuẩn cho Feature Extraction:
import torch.nn as nn
from torchvision import models
model = models.resnet18(weights="DEFAULT")
# 1) Freeze toàn bộ backbone
for param in model.parameters():
param.requires_grad = False
# 2) Replace head — fc cuối của ResNet
num_classes = 10
model.fc = nn.Linear(model.fc.in_features, num_classes)
# Sau bước này, chỉ model.fc có requires_grad=True
Tại sao chỉ model.fc còn requires_grad=True? Vì khi gán nn.Linear mới, layer này có weight được khởi tạo lại với requires_grad=True mặc định, ghi đè lên layer cũ đã bị freeze.
Với architecture khác, tên head khác nhau. Vài ví dụ:
- ResNet, ResNeXt:
model.fc - VGG, AlexNet:
model.classifier[-1] - EfficientNet:
model.classifier[1] - ViT:
model.heads.head
Luôn print(model) để xem cấu trúc trước khi thay head.
Inspect requires_grad
Cách kiểm tra layer nào đang train, layer nào freeze:
for name, param in model.named_parameters():
print(f"{name:40s} requires_grad={param.requires_grad}")
# Đếm số param sẽ được train
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
total = sum(p.numel() for p in model.parameters())
print(f"Trainable: {trainable:,} / Total: {total:,}")
Với ResNet-18 freeze backbone + head 10 class: trainable khoảng 5,130 / total khoảng 11,181,642. Tỷ lệ train chưa đến 0.05%.
Tip: khi truyền vào optimizer, lọc luôn param đã freeze để tránh warning:
trainable_params = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.AdamW(trainable_params, lr=1e-3)
Pretrained weights và cache
weights="DEFAULT": weight tốt nhất hiện có. Khuyến nghị cho phần lớn use case.weights="IMAGENET1K_V1": phiên bản đầu tiên (training recipe gốc).weights="IMAGENET1K_V2": training recipe cải tiến (TrivialAugment, longer training, label smoothing). Thường accuracy cao hơn V1 vài %.weights=None: không load — train from scratch.
Lần đầu gọi, PyTorch auto download và cache trong ~/.cache/torch/hub/checkpoints/ (Linux/macOS) hoặc %USERPROFILE%/.cache/torch/hub/checkpoints/ (Windows). Lần sau load từ cache.
Xem accuracy ImageNet của từng weight:
from torchvision.models import ResNet50_Weights
print(ResNet50_Weights.IMAGENET1K_V2.meta["_metrics"])
# {'ImageNet-1K': {'acc@1': 80.858, 'acc@5': 95.434}}
Common pretrained backbone
| Họ model | Variant phổ biến | Ghi chú |
|---|---|---|
| ResNet | 18 / 50 / 101 / 152 | Baseline cổ điển; 18 nhẹ, 50 cân bằng, 152 deep |
| EfficientNet | B0 → B7 | Compound scaling, B0 nhẹ nhất, B7 mạnh nhưng nặng |
| MobileNet | V2 / V3-small / V3-large | Cho edge / mobile, depthwise separable conv |
| ViT | vit_b_16, vit_l_16 | Vision Transformer, cần data lớn hoặc pretrained tốt |
| ConvNeXt | tiny / small / base / large | Modern CNN, accuracy ngang ViT |
| RegNet | regnet_y_8gf, ... | Họ model do Facebook AI thiết kế |
Quy tắc thực dụng: bắt đầu bằng ResNet-50 (cân bằng compute / accuracy). Nếu cần nhẹ cho mobile, dùng MobileNet hoặc EfficientNet-B0. Nếu có nhiều GPU và muốn accuracy cao, thử ConvNeXt hoặc ViT.
Pre-processing theo pretrained
Mỗi pretrained model được train với một pipeline pre-processing cụ thể (resize, normalize). Phải áp dụng pipeline tương ứng khi inference / fine-tune; nếu sai, accuracy giảm rõ rệt.
Stats normalize chuẩn ImageNet:
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
torchvision cung cấp helper weights.transforms() trả về đúng pipeline:
from torchvision.models import ResNet18_Weights
weights = ResNet18_Weights.DEFAULT
preprocess = weights.transforms()
# preprocess là một callable: img -> tensor đã resize + normalize
print(preprocess)
# ImageClassification(
# crop_size=[224]
# resize_size=[256]
# mean=[0.485, 0.456, 0.406]
# std=[0.229, 0.224, 0.225]
# interpolation=InterpolationMode.BILINEAR
# )
Khi fine-tune với input nhỏ (vd CIFAR 32x32), thường resize lên 224 hoặc giảm input size phù hợp (vd 96, 128) tùy compute. Resize quá thô có thể làm mất chi tiết — luôn so sánh accuracy theo nhiều input size.
Differential learning rate
Khi fine-tune cả backbone, ta thường chia thành 2 nhóm param với LR khác nhau:
import torch
# Head: train mới hoàn toàn -> LR cao
# Backbone: tune nhẹ -> LR thấp
optimizer = torch.optim.AdamW([
{"params": model.fc.parameters(), "lr": 1e-3},
{"params": [p for n, p in model.named_parameters() if "fc" not in n],
"lr": 1e-5},
], weight_decay=1e-4)
Lý do: head có weight ngẫu nhiên — cần gradient lớn để học từ đầu; backbone đã ở vùng tham số tốt — chỉ cần dịch chuyển nhẹ. Tỷ lệ phổ biến: lr_head / lr_backbone = 10x đến 100x.
Trong PyTorch, mỗi dict trong params là một "param group" độc lập, có thể đặt cả lr, weight_decay, momentum riêng:
optimizer.param_groups[0]["lr"] # 1e-3
optimizer.param_groups[1]["lr"] # 1e-5
Layer-wise LR decay và discriminative LR
Layer-wise LR decay (LLRD): LR giảm dần từ head xuống layer đầu — layer càng sâu (gần input) càng giữ general features, không nên tune mạnh. Công thức điển hình:
\[ \text{lr}_l = \text{lr}_\text{head} \cdot \alpha^{(L - l)} \]
Với \( L \) là số block, \( l \) là index từ đầu, \( \alpha \approx 0.65 \div 0.9 \). Phổ biến khi fine-tune ViT, BERT — He et al. (2022), Howard & Ruder (2018) ULMFiT (arXiv:1801.06146).
Discriminative LR (fast.ai): chia model thành 3 nhóm — early / middle / late layer — mỗi nhóm một LR riêng. Đơn giản hơn LLRD, hiệu quả gần tương đương trong nhiều benchmark.
Trong thực tế, với CNN nhỏ (ResNet-18, ResNet-50) và dataset cỡ trung, differential LR 2 nhóm (head vs backbone) thường đủ. LLRD chỉ thực sự cần với model rất sâu (ViT-Large, ConvNeXt-Base).
timm — vision model collection
timm (PyTorch Image Models, Wightman) là thư viện tổng hợp hàng ngàn vision model với pretrained weight, API thống nhất.
pip install timm
import timm
# Liệt kê model có pretrained
print(len(timm.list_models(pretrained=True))) # > 1000
# Tạo model với head tùy chỉnh
model = timm.create_model(
"resnet50",
pretrained=True,
num_classes=10, # tự thay head
)
# Hoặc với EfficientNet-B0
model = timm.create_model("efficientnet_b0", pretrained=True, num_classes=10)
Lợi ích so với torchvision.models:
- Nhiều model hơn (ConvNeXt-V2, MaxViT, EVA, DINOv2 backbone...).
- Tham số
num_classestự động thay head. - Hỗ trợ
features_only=Trueđể lấy multi-scale feature cho detection / segmentation. - Helper
timm.data.resolve_data_configtự tìm pre-processing chuẩn.
Khi chọn model ngoài ResNet/VGG, timm là default tốt hơn torchvision.
HuggingFace Transformers cho NLP
Transfer Learning cũng là pattern chuẩn cho NLP. HuggingFace Transformers cung cấp API tương tự cho text:
from transformers import AutoModelForSequenceClassification, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModelForSequenceClassification.from_pretrained(
"bert-base-uncased",
num_labels=3, # task có 3 class
)
BERT pretrained trên masked language modeling — học general language representation, sau đó fine-tune cho classification, NER, QA. Cùng triết lý với fine-tune ResNet pretrained.
Series 4 (ai-llm-genai) sẽ đi sâu vào TL cho NLP, prompt engineering, LoRA, RLHF.
Use case TL phổ biến
- Medical imaging: X-ray, MRI, CT — phân loại bệnh, segment tổn thương. Data label thường ít (cần bác sĩ), TL từ ImageNet hoặc từ pretrained medical (RadImageNet) giúp đáng kể.
- Wildlife classification: nhận diện loài chim, thú từ camera trap.
- Quality control manufacturing: phát hiện lỗi sản phẩm (vết nứt, vết bẩn) trên dây chuyền — dữ liệu công ty ít, TL là chuẩn.
- Custom object detection: dùng backbone pretrained làm feature extractor cho Faster R-CNN, YOLO, DETR.
- Satellite imagery: phân loại đất sử dụng, phát hiện công trình.
- OCR và document understanding: backbone CNN/ViT pretrained kết hợp với head riêng cho từng task.
Limitations và domain shift
Pretrained không phải lúc nào tốt:
- Pretrained dataset bias: ImageNet thiên về vật thể trong ảnh "Western, web-curated". Object hiếm trong dataset gốc → feature backbone yếu cho object đó.
- Domain shift quá lớn: medical X-ray, satellite, audio spectrogram, công nghiệp — phân bố ảnh khác xa ImageNet. TL có thể chỉ giúp một phần nhỏ; đôi khi train from scratch với data đủ lớn cho kết quả tương đương.
- Compute vẫn cần: dù ít hơn train from scratch, fine-tune ResNet-50 trên dataset vài chục nghìn ảnh vẫn cần GPU.
- Catastrophic forgetting: nếu LR quá cao và data nhỏ, backbone có thể bị overwrite, mất general feature đã học.
Một nghiên cứu đáng đọc: Raghu et al. (2019) — "Transfusion: Understanding Transfer Learning for Medical Imaging" (arXiv:1902.07208). Kết quả: với một số medical task, model nhỏ train from scratch ngang ngửa ResNet-50 pretrained — pretrained chủ yếu giúp ở scaling / weight statistics chứ không phải reuse high-level feature.
Khi vào domain đặc biệt, cân nhắc:
- Pretrain trên dataset cùng domain (RadImageNet cho medical, BigEarthNet cho satellite).
- Self-supervised pretrain trên data unlabel của chính domain (SimCLR, DINOv2, MAE).
- Nếu data đủ lớn (> 100k), thử cả TL và from scratch để so sánh.
Save và share model
Sau khi fine-tune, lưu weight bằng state_dict:
torch.save(model.state_dict(), "resnet18_cifar10.pth")
# Load lại
model = models.resnet18(weights=None)
model.fc = nn.Linear(model.fc.in_features, 10)
model.load_state_dict(torch.load("resnet18_cifar10.pth", map_location="cpu"))
model.eval()
Để share công khai, dùng HuggingFace Hub:
from huggingface_hub import PyTorchModelHubMixin
# Thêm mixin vào custom class
class MyResNet(nn.Module, PyTorchModelHubMixin):
...
model = MyResNet(...)
model.push_to_hub("user/resnet18-cifar10")
Lúc inference, bất cứ ai cũng có thể: MyResNet.from_pretrained("user/resnet18-cifar10").
Code Python — fine-tune ResNet-18 trên CIFAR-10
Bài này so sánh trực tiếp với SimpleCNN ở Bài 31. Cùng dataset, cùng số epoch.
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms, models
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 1) Dataset + transform — resize lên 224 cho ResNet pretrained
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
train_tf = transforms.Compose([
transforms.Resize(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean, std),
])
test_tf = transforms.Compose([
transforms.Resize(224),
transforms.ToTensor(),
transforms.Normalize(mean, std),
])
train_set = datasets.CIFAR10("./data", train=True, download=True, transform=train_tf)
test_set = datasets.CIFAR10("./data", train=False, download=True, transform=test_tf)
train_loader = DataLoader(train_set, batch_size=64, shuffle=True, num_workers=2)
test_loader = DataLoader(test_set, batch_size=128, shuffle=False, num_workers=2)
# 2) Model: ResNet-18 pretrained, freeze backbone, head 10 class
model = models.resnet18(weights="DEFAULT")
for p in model.parameters():
p.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, 10)
model = model.to(device)
# 3) Loss + Optimizer (chỉ train head)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(
[p for p in model.parameters() if p.requires_grad],
lr=1e-3,
)
# 4) Train loop
def train_one_epoch():
model.train()
for x, y in train_loader:
x, y = x.to(device), y.to(device)
logits = model(x)
loss = criterion(logits, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
@torch.no_grad()
def evaluate():
model.eval()
correct = total = 0
for x, y in test_loader:
x, y = x.to(device), y.to(device)
pred = model(x).argmax(dim=1)
correct += (pred == y).sum().item()
total += y.size(0)
return correct / total
for epoch in range(5):
train_one_epoch()
acc = evaluate()
print(f"Epoch {epoch + 1}: test acc = {acc:.4f}")
Kết quả tham khảo (5 epoch trên GPU):
- SimpleCNN Bài 31 (train from scratch): ~70–75% test accuracy.
- ResNet-18 frozen + new head: ~80–85%.
- ResNet-18 fine-tune toàn bộ + differential LR: ~92–94%.
Phiên bản fine-tune toàn bộ với differential LR:
model = models.resnet18(weights="DEFAULT")
model.fc = nn.Linear(model.fc.in_features, 10) # head mới, backbone vẫn requires_grad=True
model = model.to(device)
optimizer = torch.optim.AdamW([
{"params": model.fc.parameters(), "lr": 1e-3},
{"params": [p for n, p in model.named_parameters() if "fc" not in n], "lr": 1e-5},
], weight_decay=1e-4)
Phần train loop / evaluate giữ nguyên. Khác biệt chỉ ở chỗ backbone được tune nhẹ.
Bài tập
- Fine-tune ResNet-18 pretrained trên CIFAR-10 (5 epoch, frozen backbone). So sánh test accuracy với SimpleCNN Bài 31 cùng số epoch.
- Lặp lại bài 1 nhưng không freeze backbone (fine-tune toàn bộ với differential LR
1e-3/1e-5). So sánh accuracy và training time. - Đếm số tham số trainable trong 3 trường hợp: SimpleCNN, ResNet-18 frozen + new head, ResNet-18 fine-tune toàn bộ. Comment về tỷ lệ.
- Dùng
weights.transforms()củaResNet18_Weights.DEFAULTthay cho pipeline tự viết, train lại. Verify accuracy giữ nguyên hoặc tốt hơn. - Thử
timm.create_model("efficientnet_b0", pretrained=True, num_classes=10)trên CIFAR-10 với cùng pipeline. So sánh accuracy với ResNet-18. - Thử freeze chỉ layer1 và layer2 của ResNet-18, fine-tune layer3, layer4, và fc. Quan sát accuracy so với hai cách extreme (freeze tất cả / tune tất cả).
- Lưu model fine-tune bằng
torch.save, load lại trên process mới, verify inference cho ra output giống hệt. - Đọc paper Raghu et al. (2019) Transfusion. Tóm tắt 3 kết luận chính về TL trong medical imaging.
Đáp án ngắn
- Frozen backbone thường vượt SimpleCNN 5–10% accuracy với cùng số epoch, dù ít trainable param hơn.
- Fine-tune toàn bộ đạt cao nhất (~92–94%), nhưng training time gấp 2–3x do gradient lan tỏa toàn bộ backbone.
- SimpleCNN ~100k–500k param trainable; ResNet-18 frozen ~5k (chỉ head); ResNet-18 full tune ~11.2M. Accuracy không tỷ lệ thuận với số trainable param — feature pretrained quan trọng hơn.
- Accuracy tương đương;
weights.transforms()đảm bảo dùng đúng resize / normalize gốc. - EfficientNet-B0 thường ngang ResNet-18 trên CIFAR-10, ít param hơn (~5M vs 11M). Trade-off: B0 chậm hơn một chút do depthwise conv.
- Freeze layer1+layer2 + tune layer3+layer4+fc thường cho accuracy giữa hai extreme, training time ngắn hơn full tune.
- Cần đặt
model.eval()trước inference để tắt BatchNorm running stats update.torch.allclosegiữa 2 lần forward trên cùng input. - Tóm tắt Raghu et al.: (1) trên medical task, model nhỏ train from scratch có thể ngang ResNet-50 pretrained; (2) feature reuse từ ImageNet hạn chế cho medical; (3) lợi ích pretrained chủ yếu từ weight statistics, không phải high-level feature.
Bài kế tiếp: Bài 33 — Các kiến trúc CNN nổi bật — đi qua LeNet, AlexNet, VGG, GoogLeNet, ResNet, DenseNet, EfficientNet, ConvNeXt và quy luật thiết kế chung.
- Yosinski et al. (2014) - How transferable are features in deep neural networks?
- Kornblith, Shlens & Le (2019) - Do Better ImageNet Models Transfer Better?
- Howard & Ruder (2018) - Universal Language Model Fine-tuning for Text Classification (ULMFiT)
- Raghu et al. (2019) - Transfusion: Understanding Transfer Learning for Medical Imaging
- He et al. (2016) - Deep Residual Learning for Image Recognition (ResNet)
- Russakovsky et al. (2015) - ImageNet Large Scale Visual Recognition Challenge
- torchvision.models documentation
- torchvision.transforms documentation
- timm - PyTorch Image Models (Ross Wightman)
- HuggingFace Transformers - Fine-tuning a pretrained model
- CS231n - Transfer Learning
