File size: 1,853 Bytes
67fffca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import json
from pathlib import Path

import torch
from safetensors.torch import load_file
from torchvision import transforms
from torchvision.models import ResNet18_Weights, resnet18


MEAN = [0.485, 0.456, 0.406]
STD = [0.229, 0.224, 0.225]


def build_model(num_classes: int, pretrained: bool = False):
    model = resnet18(weights=ResNet18_Weights.DEFAULT if pretrained else None)
    model.fc = torch.nn.Linear(model.fc.in_features, num_classes)
    return model


def preprocessing(config: dict, training: bool = False):
    size = config["image_size"]
    operations = (
        [transforms.RandomResizedCrop(size, scale=(.8, 1.0)), transforms.RandomHorizontalFlip()]
        if training else
        [transforms.Resize(round(size * 256 / 224)), transforms.CenterCrop(size)]
    )
    return transforms.Compose(operations + [
        transforms.ToTensor(),
        transforms.Normalize(config["mean"], config["std"]),
    ])


def load_classifier(directory: str | Path):
    directory = Path(directory)
    config = json.loads((directory / "config.json").read_text())
    model = build_model(len(config["labels"]))
    model.load_state_dict(load_file(str(directory / "model.safetensors")))
    model.eval()
    return model, config


@torch.inference_mode()
def classify(model, config: dict, image, top_k: int):
    if image is None:
        raise ValueError("Choose an image")
    if not 1 <= top_k <= len(config["labels"]):
        raise ValueError("Invalid top_k")
    tensor = preprocessing(config)(image.convert("RGB")).unsqueeze(0)
    tensor = tensor.to(next(model.parameters()).device)
    probabilities = model(tensor).softmax(dim=1)[0]
    scores, indices = probabilities.topk(top_k)
    return [
        {"label": config["labels"][index], "score": float(score)}
        for score, index in zip(scores.tolist(), indices.tolist())
    ]