Я обучаю модель классификации изображений (EfficientNet-B3 на изображениях кандзи в оттенках серого) с помощью PyTorch с:
- AMP (torch.amp.autocast + GradScaler)
- сбалансированный по классам разделенный набор данных из HDF5
- Вход: (1, 128, 128) изображений в оттенках серого (~ 620 000 изображений в 3036 классах)
- Потери: CrossEntropyLoss (сглаживание метки = 0,1)
- Оптимизатор: AdamW (снижение веса = 1e-4)
Моя настройка (упрощенная):
model:
num_classes: 3036
pretrained: True
training:
batch_size: 256
learning_rate: 0.001
epochs: 40
num_workers: 6
shuffle: True
pin_memory: True
persistent_workers: True
prefetch_factor: 4
unfreeze_epoch: 3
optimizer:
type: adamW
weight_decay: 0.0001
data:
...
normalize_mean: [0.5]
normalize_std: [0.5]
val_split: 0.2
device: cuda
...
...
model = EfficientNetB3Kanji(num_classes=3036, pretrained=True)
for param in model.model.features.parameters():
param.requires_grad = False
optimizer = torch.optim.AdamW(
model.parameters(),
lr=config["training"]["learning_rate"],
weight_decay=config["optimizer"]["weight_decay"]
)
epochs = config["training"]["epochs"]
unfreeze_epoch = config["training"]["unfreeze_epoch"]
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer,
T_max=epochs * len(train_loader),
eta_min=1e-6
)
...
...
for epoch in range(epochs):
if epoch == unfreeze_epoch:
print(f"\nEpoch {epoch + 1}: Unfreezing backbone")
for param in model.model.features.parameters():
param.requires_grad = True
model.train()
...
for step, (images, labels) in loop:
images = images.to(device, non_blocking=True)
labels = labels.to(device, non_blocking=True)
with autocast(device_type="cuda"):
outputs = model(images)
loss = criterion(outputs, labels)
scaler.scale(loss).backward()
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
scale_before = scaler.get_scale()
scaler.step(optimizer)
scaler.update()
scale_after = scaler.get_scale()
if scale_after >= scale_before:
scheduler.step()
optimizer.zero_grad(set_to_none=True)
...
Что касается моих дополнений, я делаю:
def get_train_transforms(mean, std):
return v2.Compose([
v2.RandomAffine(
degrees=12,
translate=(0.05, 0.05),
scale=(0.95, 1.05),
fill=0
),
v2.Normalize(mean=mean, std=std)
])
def get_val_transforms(mean, std):
return v2.Compose([
v2.Normalize(mean=mean, std=std)
])