ResNet / train_food.py
JangTaeng's picture
Upload 8 files
b1fe7e3 verified
Raw
History Blame Contribute Delete
7.25 kB
"""Food-101 ๋ฐ์ดํ„ฐ์…‹์œผ๋กœ MyResNet์„ ํ•™์Šตํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ.
torchvision์˜ ImageNet pretrained ResNet-18 ๊ฐ€์ค‘์น˜๋ฅผ ๊ฐ€์ ธ์™€์„œ
MyResNet์— ๋กœ๋“œํ•œ ํ›„ Food-101์— fine-tuningํ•ฉ๋‹ˆ๋‹ค.
์‚ฌ์šฉ๋ฒ•:
python train_food.py
์š”๊ตฌ์‚ฌํ•ญ:
pip install torch torchvision transformers datasets accelerate
ํ•™์Šต ์‹œ๊ฐ„ (GPU 1์žฅ ๊ธฐ์ค€):
- Food-101 full (101์ข…): ์•ฝ 2~4์‹œ๊ฐ„ (10 epoch)
- ๋น ๋ฅธ ํ…Œ์ŠคํŠธ: epochs=3์œผ๋กœ ์ค„์ด๋ฉด 1์‹œ๊ฐ„ ๋‚ด
"""
import numpy as np
import torch
import torch.nn as nn
import torchvision.models as tv_models
from datasets import load_dataset
from torchvision.transforms import (
Compose,
Normalize,
RandomCrop,
RandomHorizontalFlip,
Resize,
ToTensor,
)
from transformers import DefaultDataCollator, Trainer, TrainingArguments
from configuration_myresnet import MyResNetConfig
from modeling_myresnet import MyResNetForImageClassification
# ============================================================
# 1) ๋ฐ์ดํ„ฐ์…‹ ๋กœ๋”ฉ
# ============================================================
print("Food-101 ๋ฐ์ดํ„ฐ์…‹ ๋กœ๋”ฉ ์ค‘...")
dataset = load_dataset("food101")
# Food-101์˜ 101๊ฐœ ํด๋ž˜์Šค๋ช… ๊ฐ€์ ธ์˜ค๊ธฐ
food_classes = dataset["train"].features["label"].names
NUM_CLASSES = len(food_classes)
print(f"์ด ํด๋ž˜์Šค ์ˆ˜: {NUM_CLASSES}")
print(f"ํ•™์Šต ์ด๋ฏธ์ง€: {len(dataset['train']):,}์žฅ")
print(f"๊ฒ€์ฆ ์ด๋ฏธ์ง€: {len(dataset['validation']):,}์žฅ")
# ============================================================
# 2) ๋ชจ๋ธ ์ค€๋น„ - ResNet-18 + Pretrained ๊ฐ€์ค‘์น˜ ๋กœ๋“œ
# ============================================================
config = MyResNetConfig(
num_channels=3,
num_labels=NUM_CLASSES,
block_type="basic",
layers=[2, 2, 2, 2], # ResNet-18
hidden_sizes=[64, 128, 256, 512],
image_size=224,
id2label={i: name for i, name in enumerate(food_classes)},
label2id={name: i for i, name in enumerate(food_classes)},
)
model = MyResNetForImageClassification(config)
def load_pretrained_resnet18(model):
"""torchvision์˜ ImageNet pretrained ResNet-18 ๊ฐ€์ค‘์น˜๋ฅผ MyResNet์— ๋ณต์‚ฌ.
๋‘ ๋ชจ๋ธ์˜ ๋ ˆ์ด์–ด ์ด๋ฆ„์ด ๋‹ค๋ฅด๋ฏ€๋กœ ๋งคํ•‘ํ•ด์ค๋‹ˆ๋‹ค.
๋งˆ์ง€๋ง‰ FC ๋ ˆ์ด์–ด(classifier)๋Š” ํด๋ž˜์Šค ์ˆ˜๊ฐ€ ๋‹ค๋ฅด๋ฏ€๋กœ ์Šคํ‚ต.
"""
print("ImageNet pretrained ResNet-18 ๊ฐ€์ค‘์น˜ ๋กœ๋”ฉ...")
tv_resnet = tv_models.resnet18(weights=tv_models.ResNet18_Weights.IMAGENET1K_V1)
tv_state = tv_resnet.state_dict()
# torchvision -> MyResNet ๋ ˆ์ด์–ด ์ด๋ฆ„ ๋งคํ•‘
# tv_resnet: conv1, bn1, layer1~4, fc
# MyResNet: stem.0 (conv1), stem.1 (bn1), stage1~4, classifier
mapping = {
"conv1.": "stem.0.",
"bn1.": "stem.1.",
"layer1.": "stage1.",
"layer2.": "stage2.",
"layer3.": "stage3.",
"layer4.": "stage4.",
}
# downsample์€ shortcut์œผ๋กœ ๋งคํ•‘ (๊ฐ stage์˜ ์ฒซ ๋ธ”๋ก์—๋งŒ ์žˆ์Œ)
new_state = {}
for k, v in tv_state.items():
if k.startswith("fc."):
continue # FC ๋ ˆ์ด์–ด๋Š” ์Šคํ‚ต (ํด๋ž˜์Šค ์ˆ˜๊ฐ€ ๋‹ค๋ฆ„)
new_k = k
for old, new in mapping.items():
if new_k.startswith(old):
new_k = new_k.replace(old, new, 1)
break
# layer1.0.downsample.0 -> stage1.0.shortcut.0
new_k = new_k.replace(".downsample.", ".shortcut.")
new_state[new_k] = v
# strict=False๋กœ ๋ถ€๋ถ„ ๋กœ๋“œ (classifier๋Š” ํ•™์Šต ํ•„์š”)
missing, unexpected = model.load_state_dict(new_state, strict=False)
print(f"๋กœ๋“œ ์„ฑ๊ณต. ๋ˆ„๋ฝ๋œ ํ‚ค {len(missing)}๊ฐœ (classifier ๋“ฑ), "
f"์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํ‚ค {len(unexpected)}๊ฐœ")
return model
model = load_pretrained_resnet18(model)
print(f"๋ชจ๋ธ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆ˜: {sum(p.numel() for p in model.parameters()):,}")
# ============================================================
# 3) ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ
# ============================================================
IMAGENET_MEAN = [0.485, 0.456, 0.406]
IMAGENET_STD = [0.229, 0.224, 0.225]
train_transform = Compose([
Resize(256),
RandomCrop(224),
RandomHorizontalFlip(),
ToTensor(),
Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
eval_transform = Compose([
Resize((224, 224)),
ToTensor(),
Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
def preprocess_train(batch):
batch["pixel_values"] = [
train_transform(img.convert("RGB")) for img in batch["image"]
]
batch["labels"] = batch["label"]
return batch
def preprocess_eval(batch):
batch["pixel_values"] = [
eval_transform(img.convert("RGB")) for img in batch["image"]
]
batch["labels"] = batch["label"]
return batch
train_ds = dataset["train"].with_transform(preprocess_train)
eval_ds = dataset["validation"].with_transform(preprocess_eval)
# ============================================================
# 4) ํ‰๊ฐ€ ์ง€ํ‘œ (top-1 + top-5 accuracy)
# ============================================================
def compute_metrics(eval_pred):
logits, labels = eval_pred
# Top-1 accuracy
top1_preds = np.argmax(logits, axis=-1)
top1_acc = (top1_preds == labels).mean()
# Top-5 accuracy
top5_preds = np.argsort(-logits, axis=-1)[:, :5]
top5_correct = np.any(top5_preds == labels[:, None], axis=-1)
top5_acc = top5_correct.mean()
return {
"accuracy": float(top1_acc),
"top5_accuracy": float(top5_acc),
}
# ============================================================
# 5) Trainer ์„ค์ •
# ============================================================
training_args = TrainingArguments(
output_dir="./my-resnet18-food101",
num_train_epochs=10, # 10 epoch ๊ถŒ์žฅ (pretrained์ด๋ฏ€๋กœ ์ ๊ฒŒ)
per_device_train_batch_size=64,
per_device_eval_batch_size=64,
learning_rate=1e-3, # pretrained์ด๋ฏ€๋กœ ๋” ์ž‘๊ฒŒ ์‹œ์ž‘
weight_decay=1e-4,
lr_scheduler_type="cosine",
warmup_ratio=0.05,
eval_strategy="epoch",
save_strategy="epoch",
save_total_limit=2,
logging_steps=50,
load_best_model_at_end=True,
metric_for_best_model="accuracy",
greater_is_better=True,
fp16=torch.cuda.is_available(),
remove_unused_columns=False,
dataloader_num_workers=4,
report_to="none",
push_to_hub=False, # True๋กœ ๋ฐ”๊พธ๋ฉด ์ž๋™ ์—…๋กœ๋“œ
# hub_model_id="your-username/my-resnet18-food101",
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_ds,
eval_dataset=eval_ds,
data_collator=DefaultDataCollator(),
compute_metrics=compute_metrics,
)
# ============================================================
# 6) ํ•™์Šต ์‹คํ–‰
# ============================================================
if __name__ == "__main__":
print("\n=== Food-101 ํ•™์Šต ์‹œ์ž‘ ===")
trainer.train()
metrics = trainer.evaluate()
print(f"\n์ตœ์ข… Top-1 ์ •ํ™•๋„: {metrics['eval_accuracy']:.4f}")
print(f"์ตœ์ข… Top-5 ์ •ํ™•๋„: {metrics['eval_top5_accuracy']:.4f}")
trainer.save_model("./my-resnet18-food101")
print("๋ชจ๋ธ ์ €์žฅ ์™„๋ฃŒ: ./my-resnet18-food101")
# ํ—ˆ๋ธŒ ์—…๋กœ๋“œ:
# trainer.push_to_hub()