مقالات

شناخت تصویر با Coco CNNS: راهنمای مرحله -by -by -step


# coco_multilabel_train.py
import os
import numpy as np
from PIL import Image
from tqdm import tqdm

import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
import torchvision
from torchvision import transforms, models

from pycocotools.coco import COCO
from sklearn.metrics import average_precision_score

# -----------------------
# Config (======edit these======)
COCO_ROOT = "/path/to/coco/val2017"  # path to images dir (val2017)
ANN_FILE  = "/path/to/coco/annotations/instances_val2017.json"
BATCH_SIZE = 16
NUM_EPOCHS = 5
LR = 1e-4
MAX_SAMPLES = 2000   # set smaller for quick tests; or None to use all
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# -----------------------

class CocoMultiLabelDataset(Dataset):
    """Builds a multi-label dataset from COCO 'instances' annotations."""
    def __init__(self, root, ann_file, transforms=None, max_samples=None):
        self.root = root
        self.coco = COCO(ann_file)
        self.img_ids = self.coco.getImgIds()
        if max_samples:
            self.img_ids = self.img_ids[:max_samples]
        # get list of category ids and names in a deterministic order
        self.cat_ids = self.coco.getCatIds()
        self.cat_id_to_idx = {cid: i for i, cid in enumerate(self.cat_ids)}
        self.idx_to_cat = [self.coco.loadCats([cid])[0]['name'] for cid in self.cat_ids]
        self.num_classes = len(self.cat_ids)
        self.transforms = transforms

    def __len__(self):
        return len(self.img_ids)

    def __getitem__(self, idx):
        img_id = self.img_ids[idx]
        img_info = self.coco.loadImgs([img_id])[0]
        img_path = os.path.join(self.root, img_info['file_name'])
        image = Image.open(img_path).convert("RGB")
        ann_ids = self.coco.getAnnIds(imgIds=[img_id], iscrowd=None)
        anns = self.coco.loadAnns(ann_ids)

        label = np.zeros(self.num_classes, dtype=np.float32)
        for ann in anns:
            cid = ann['category_id']
            if cid in self.cat_id_to_idx:
                label[self.cat_id_to_idx[cid]] = 1.0

        if self.transforms:
            image = self.transforms(image)

        return image, torch.from_numpy(label)

def get_transforms(train=True):
    if train:
        return transforms.Compose([
            transforms.Resize(256),
            transforms.RandomResizedCrop(224),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225])
        ])
    else:
        return transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225])
        ])

def build_model(num_classes):
    model = models.resnet50(pretrained=True)
    in_features = model.fc.in_features
    model.fc = nn.Linear(in_features, num_classes)
    return model

def train_one_epoch(model, loader, optimizer, criterion, device):
    model.train()
    running_loss = 0.0
    for imgs, targets in tqdm(loader, desc="train"):
        imgs = imgs.to(device)
        targets = targets.to(device)
        outputs = model(imgs)
        loss = criterion(outputs, targets)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        running_loss += loss.item() * imgs.size(0)
    return running_loss / len(loader.dataset)

def evaluate(model, loader, device):
    model.eval()
    all_targets = []
    all_scores = []
    with torch.no_grad():
        for imgs, targets in tqdm(loader, desc="eval"):
            imgs = imgs.to(device)
            outputs = model(imgs)
            scores = torch.sigmoid(outputs).cpu().numpy()
            all_scores.append(scores)
            all_targets.append(targets.numpy())
    y_true = np.vstack(all_targets)
    y_scores = np.vstack(all_scores)
    aps = []
    for i in range(y_true.shape[1]):
        if np.sum(y_true[:, i]) == 0:
            aps.append(np.nan)  # no positives for this class in val set
        else:
            aps.append(average_precision_score(y_true[:, i], y_scores[:, i]))
    mean_ap = np.nanmean([a for a in aps if not np.isnan(a)])
    return mean_ap, aps

def predict_image(model, img_path, transform, idx_to_cat, device, topk=5):
    model.eval()
    img = Image.open(img_path).convert("RGB")
    x = transform(img).unsqueeze(0).to(device)
    with torch.no_grad():
        out = model(x)
        probs = torch.sigmoid(out).cpu().numpy()[0]
    topk_idx = probs.argsort()[-topk:][::-1]
    return [(idx_to_cat[i], float(probs[i])) for i in topk_idx]

def main():
    # builds datasets
    train_t = get_transforms(train=False)   # using val2017; no augmentation for simplicity
    val_t = get_transforms(train=False)
    dataset = CocoMultiLabelDataset(COCO_ROOT, ANN_FILE, transforms=train_t, max_samples=MAX_SAMPLES)
    # Split: simple 80/20 split for demo
    n = len(dataset)
    split = int(0.8 * n)
    train_subset, val_subset = torch.utils.data.random_split(dataset, [split, n - split])
    train_loader = DataLoader(train_subset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2)
    val_loader = DataLoader(val_subset, batch_size=BATCH_SIZE, shuffle=False, num_workers=2)

    model = build_model(dataset.num_classes).to(DEVICE)
    criterion = nn.BCEWithLogitsLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=LR)

    for epoch in range(1, NUM_EPOCHS + 1):
        loss = train_one_epoch(model, train_loader, optimizer, criterion, DEVICE)
        mean_ap, aps = evaluate(model, val_loader, DEVICE)
        print(f"Epoch {epoch}: train_loss={loss:.4f}, val_mAP={mean_ap:.4f}")

    # example inference
    sample_img = os.path.join(COCO_ROOT, dataset.coco.loadImgs([dataset.img_ids[0]])[0]['file_name'])
    preds = predict_image(model, sample_img, val_t, dataset.idx_to_cat, DEVICE, topk=5)
    print("Top predictions for sample image:", preds)

if __name__ == "__main__":
    main()


Source link

نوشته های مشابه

دیدگاهتان را بنویسید

همچنین ببینید
بستن
دکمه بازگشت به بالا