Spaces:
Sleeping
Sleeping
Copy deployment files to gradio branch
Browse files- LICENSE +0 -21
- README.md +0 -17
- deployment/app.py β app.py +0 -0
- environment.yml +0 -25
- deployment/model.py β model.py +0 -0
- models/vit_exported.pkl +0 -3
- deployment/requirements.txt β requirements.txt +0 -0
- tests/__init__.py +0 -0
- tests/test_validation_transforms.py +0 -109
- training/birds/config.py +0 -6
- training/birds/train.py +0 -81
- training/birds/utils/kaggle.py +0 -57
- training/notebooks/lab.ipynb +0 -0
- training/notebooks/transforms-lab.ipynb +0 -0
- deployment/transforms.py β transforms.py +0 -0
- models/vit_saved.pth β vit_saved.pth +0 -0
- deployment/vocab.py β vocab.py +0 -0
LICENSE
DELETED
@@ -1,21 +0,0 @@
|
|
1 |
-
MIT License
|
2 |
-
|
3 |
-
Copyright (c) 2023 Javi Carnero
|
4 |
-
|
5 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
-
of this software and associated documentation files (the "Software"), to deal
|
7 |
-
in the Software without restriction, including without limitation the rights
|
8 |
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
-
copies of the Software, and to permit persons to whom the Software is
|
10 |
-
furnished to do so, subject to the following conditions:
|
11 |
-
|
12 |
-
The above copyright notice and this permission notice shall be included in all
|
13 |
-
copies or substantial portions of the Software.
|
14 |
-
|
15 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
-
SOFTWARE.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
# birds-classification
|
2 |
-
|
3 |
-
Train model for birds classification and gradio app
|
4 |
-
|
5 |
-
Training is done using fastai, deployment mimics its transforms to publish a gradio app that has no fastai dependencies.
|
6 |
-
|
7 |
-
## Train
|
8 |
-
|
9 |
-
```bash
|
10 |
-
conda env create -f environment.yml
|
11 |
-
```
|
12 |
-
|
13 |
-
```bash
|
14 |
-
conda activate fastai
|
15 |
-
cd training
|
16 |
-
python -m birds.train
|
17 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deployment/app.py β app.py
RENAMED
File without changes
|
environment.yml
DELETED
@@ -1,25 +0,0 @@
|
|
1 |
-
name: fastai
|
2 |
-
channels:
|
3 |
-
- pytorch
|
4 |
-
- nvidia
|
5 |
-
- conda-forge
|
6 |
-
- fastchan
|
7 |
-
- defaults
|
8 |
-
dependencies:
|
9 |
-
- python=3.10
|
10 |
-
- pip
|
11 |
-
- cudatoolkit=11.7
|
12 |
-
- pytorch=1.13.1
|
13 |
-
- torchvision=0.14.1
|
14 |
-
- pytorch-cuda=11.7
|
15 |
-
- fastcore=1.5.28
|
16 |
-
- fastai=2.7.11
|
17 |
-
- pip:
|
18 |
-
- ipykernel
|
19 |
-
- ipywidgets
|
20 |
-
- gradio==3.20.1
|
21 |
-
- timm==0.6.12
|
22 |
-
- kaggle==1.5.12
|
23 |
-
- flake8
|
24 |
-
- black
|
25 |
-
- pytest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deployment/model.py β model.py
RENAMED
File without changes
|
models/vit_exported.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:c96e179026917191d7a7e8b62c64d1a212ee77779965523a09a4b26fd6ccb934
|
3 |
-
size 23232463
|
|
|
|
|
|
|
|
deployment/requirements.txt β requirements.txt
RENAMED
File without changes
|
tests/__init__.py
DELETED
File without changes
|
tests/test_validation_transforms.py
DELETED
@@ -1,109 +0,0 @@
|
|
1 |
-
import pytest
|
2 |
-
|
3 |
-
from pathlib import Path
|
4 |
-
from typing import List
|
5 |
-
import numpy as np
|
6 |
-
from PIL import Image
|
7 |
-
import torch
|
8 |
-
import torchvision.transforms as tvtfms
|
9 |
-
from fastai.vision.data import PILImage
|
10 |
-
import fastai.vision.augment as fastai_aug
|
11 |
-
|
12 |
-
from deployment.transforms import resized_crop_pad, gpu_crop
|
13 |
-
|
14 |
-
DATA_PATH = "data/200-bird-species-with-11788-images"
|
15 |
-
|
16 |
-
|
17 |
-
def get_birds_images(path: Path) -> List[str]:
|
18 |
-
with open(path / "images.txt", "r") as file:
|
19 |
-
lines = [
|
20 |
-
path.resolve() / "images" / line.strip().split()[1]
|
21 |
-
for line in file.readlines()
|
22 |
-
]
|
23 |
-
return lines
|
24 |
-
|
25 |
-
|
26 |
-
class TestTransforms:
|
27 |
-
im_idx = 510
|
28 |
-
|
29 |
-
@pytest.fixture
|
30 |
-
def img_paths(self) -> List[str]:
|
31 |
-
path = Path(DATA_PATH) / "CUB_200_2011"
|
32 |
-
return get_birds_images(path.resolve())
|
33 |
-
|
34 |
-
@pytest.fixture
|
35 |
-
def im_fastai(self, img_paths: List[str]) -> PILImage:
|
36 |
-
fname = img_paths[self.im_idx]
|
37 |
-
return PILImage.create(fname)
|
38 |
-
|
39 |
-
@pytest.fixture
|
40 |
-
def im_pil(self, img_paths: List[str]) -> Image:
|
41 |
-
fname = img_paths[self.im_idx]
|
42 |
-
return Image.open(fname)
|
43 |
-
|
44 |
-
def testImageFastaiEqualsPillow(self, im_fastai: PILImage, im_pil: Image):
|
45 |
-
assert (np.array(im_fastai) == np.array(im_pil)).all()
|
46 |
-
|
47 |
-
# RandomResizedCrop is not exactly equal to CropPad in validation
|
48 |
-
# # def testRandomResizedCropEqualsCropPad(self, im_fastai: PILImage):
|
49 |
-
# # crop_fastai = fastai_aug.CropPad((460, 460))
|
50 |
-
# # crop_rrc = fastai_aug.RandomResizedCrop((460, 460))
|
51 |
-
|
52 |
-
# # cropped_rrc = crop_rrc(im_fastai, split_idx=1)
|
53 |
-
# # cropped_fastai = crop_fastai(im_fastai, split_idx=1)
|
54 |
-
|
55 |
-
# # assert (np.array(cropped_rrc) == np.array(cropped_fastai)).all()
|
56 |
-
|
57 |
-
def testRandomResizedCropEqualsCustomResizedCropPad(
|
58 |
-
self, im_fastai: PILImage, im_pil: Image
|
59 |
-
):
|
60 |
-
crop_rrc = fastai_aug.RandomResizedCrop((460, 460))
|
61 |
-
|
62 |
-
assert (
|
63 |
-
np.array(crop_rrc(im_fastai, split_idx=1))
|
64 |
-
== np.array(resized_crop_pad(im_pil, (460, 460)))
|
65 |
-
).all()
|
66 |
-
|
67 |
-
def testFlipEqualsCustomGPUCrop(self, im_fastai: PILImage, im_pil: Image):
|
68 |
-
# apply flip augmentation on validation
|
69 |
-
tt_fastai = fastai_aug.ToTensor()
|
70 |
-
i2f_fastai = fastai_aug.IntToFloatTensor()
|
71 |
-
flip = fastai_aug.Flip(size=(224, 224))
|
72 |
-
result_im_fastai = flip(
|
73 |
-
i2f_fastai(tt_fastai(im_fastai).unsqueeze(0)), split_idx=1
|
74 |
-
)
|
75 |
-
|
76 |
-
# apply custom gpu crop
|
77 |
-
tt_torch = tvtfms.ToTensor()
|
78 |
-
result_im_tv = gpu_crop(tt_torch(im_pil).unsqueeze(0), size=(224, 224))
|
79 |
-
|
80 |
-
assert torch.allclose(result_im_fastai, result_im_tv)
|
81 |
-
assert (result_im_fastai == result_im_tv).all()
|
82 |
-
|
83 |
-
def testFastaiTransformsEqualsCustom(self, im_fastai: PILImage, im_pil: Image):
|
84 |
-
# fastai transforms
|
85 |
-
crop_rrc = fastai_aug.RandomResizedCrop((460, 460))
|
86 |
-
tt_fastai = fastai_aug.ToTensor()
|
87 |
-
i2f_fastai = fastai_aug.IntToFloatTensor()
|
88 |
-
flip = fastai_aug.Flip(size=(224, 224))
|
89 |
-
brightness = fastai_aug.Brightness()
|
90 |
-
norm_fastai = fastai_aug.Normalize.from_stats(
|
91 |
-
*fastai_aug.imagenet_stats, cuda=False
|
92 |
-
)
|
93 |
-
|
94 |
-
# custom transforms
|
95 |
-
tt_torch = tvtfms.ToTensor()
|
96 |
-
norm_torch = tvtfms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
|
97 |
-
|
98 |
-
# apply all fastai augmentations on validation (transformations)
|
99 |
-
batch_im_fastai = tt_fastai(crop_rrc(im_fastai, split_idx=1)).unsqueeze(0)
|
100 |
-
result_im_fastai = norm_fastai(
|
101 |
-
brightness(flip(i2f_fastai(batch_im_fastai), split_idx=1), split_idx=1)
|
102 |
-
)
|
103 |
-
|
104 |
-
# apply all custom transformations
|
105 |
-
batch_im_tv = tt_torch(resized_crop_pad(im_pil, (460, 460))).unsqueeze(0)
|
106 |
-
result_im_tv = norm_torch(gpu_crop(batch_im_tv, size=(224, 224)))
|
107 |
-
|
108 |
-
assert torch.allclose(result_im_fastai, result_im_tv)
|
109 |
-
assert (result_im_fastai == result_im_tv).all()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
training/birds/config.py
DELETED
@@ -1,6 +0,0 @@
|
|
1 |
-
DATA_STORAGE_PATH = "../data"
|
2 |
-
MODELS_STORAGE_PATH = "../models"
|
3 |
-
DATASET = "200-bird-species-with-11788-images"
|
4 |
-
OWNER = "veeralakrishna"
|
5 |
-
|
6 |
-
DATA_PATH = DATA_STORAGE_PATH + "/" + DATASET
|
|
|
|
|
|
|
|
|
|
|
|
|
|
training/birds/train.py
DELETED
@@ -1,81 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
from pathlib import Path
|
3 |
-
|
4 |
-
from fastai.vision.data import (
|
5 |
-
IndexSplitter,
|
6 |
-
DataBlock,
|
7 |
-
ImageBlock,
|
8 |
-
CategoryBlock,
|
9 |
-
RegexLabeller,
|
10 |
-
)
|
11 |
-
from fastai.vision.augment import (
|
12 |
-
RandomResizedCrop,
|
13 |
-
aug_transforms,
|
14 |
-
Normalize,
|
15 |
-
imagenet_stats,
|
16 |
-
)
|
17 |
-
|
18 |
-
from fastai.callback import schedule # noqa: F401
|
19 |
-
from fastai.vision.learner import vision_learner, accuracy
|
20 |
-
|
21 |
-
from birds import config
|
22 |
-
from birds.utils.kaggle import download_dataset
|
23 |
-
|
24 |
-
|
25 |
-
def get_birds_images(path):
|
26 |
-
with open(path / "images.txt", "r") as file:
|
27 |
-
lines = [
|
28 |
-
path.resolve() / "images" / line.strip().split()[1]
|
29 |
-
for line in file.readlines()
|
30 |
-
]
|
31 |
-
return lines
|
32 |
-
|
33 |
-
|
34 |
-
def BirdsSplitter(path):
|
35 |
-
with open(path / "train_test_split.txt", "r") as file:
|
36 |
-
valid_idx = [
|
37 |
-
int(line.strip().split()[0]) - 1
|
38 |
-
for line in file.readlines()
|
39 |
-
if line.strip().split()[1] == "1"
|
40 |
-
]
|
41 |
-
return IndexSplitter(valid_idx)
|
42 |
-
|
43 |
-
|
44 |
-
if __name__ == "__main__":
|
45 |
-
bs = 64
|
46 |
-
|
47 |
-
if download_dataset(config.OWNER, config.DATASET, config.DATA_PATH):
|
48 |
-
import tarfile
|
49 |
-
|
50 |
-
with tarfile.open(Path(config.DATA_PATH) / "CUB_200_2011.tgz", "r:gz") as tar:
|
51 |
-
tar.extractall(path=config.DATA_PATH)
|
52 |
-
|
53 |
-
os.remove(Path(config.DATA_PATH) / "CUB_200_2011.tgz")
|
54 |
-
os.remove(Path(config.DATA_PATH) / "segmentations.tgz")
|
55 |
-
|
56 |
-
path = Path(config.DATA_PATH) / "CUB_200_2011"
|
57 |
-
|
58 |
-
item_tfms = RandomResizedCrop(460, min_scale=0.75, ratio=(1.0, 1.0))
|
59 |
-
batch_tfms = [
|
60 |
-
*aug_transforms(size=224, max_warp=0),
|
61 |
-
Normalize.from_stats(*imagenet_stats),
|
62 |
-
]
|
63 |
-
|
64 |
-
birds = DataBlock(
|
65 |
-
blocks=(ImageBlock, CategoryBlock),
|
66 |
-
get_items=get_birds_images,
|
67 |
-
splitter=BirdsSplitter(path),
|
68 |
-
get_y=RegexLabeller(pat=r"/([^/]+)_\d+_\d+\.jpg"),
|
69 |
-
item_tfms=item_tfms,
|
70 |
-
batch_tfms=batch_tfms,
|
71 |
-
)
|
72 |
-
|
73 |
-
dls = birds.dataloaders(path)
|
74 |
-
|
75 |
-
learner = vision_learner(dls, "vit_tiny_patch16_224", metrics=[accuracy])
|
76 |
-
|
77 |
-
learner.fine_tune(7, base_lr=0.001, freeze_epochs=12)
|
78 |
-
|
79 |
-
learner.export(Path(config.MODELS_STORAGE_PATH).resolve() / "vit_exported.pkl")
|
80 |
-
learner.model_dir = Path(config.MODELS_STORAGE_PATH).resolve()
|
81 |
-
learner.save("vit_saved", with_opt=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
training/birds/utils/kaggle.py
DELETED
@@ -1,57 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
from pathlib import Path
|
3 |
-
from zipfile import ZipFile
|
4 |
-
from kaggle import api
|
5 |
-
|
6 |
-
|
7 |
-
def running_on_kaggle() -> bool:
|
8 |
-
"""
|
9 |
-
Checks if script is running on kaggle
|
10 |
-
:return: true if the script is running on kaggle, false otherwise
|
11 |
-
"""
|
12 |
-
if os.environ.get("KAGGLE_KERNEL_RUN_TYPE", ""):
|
13 |
-
return True
|
14 |
-
else:
|
15 |
-
return False
|
16 |
-
|
17 |
-
|
18 |
-
def download_competition_data(competition: str, input_path: str | Path) -> None:
|
19 |
-
"""
|
20 |
-
Downloads data from kaggle competition only if input folder is empty
|
21 |
-
:param comptetition: string with the competition name id of kaggle
|
22 |
-
:param input_path: path of the input folder
|
23 |
-
"""
|
24 |
-
data_path = Path(input_path)
|
25 |
-
if not data_path.exists():
|
26 |
-
data_path.mkdir(parents=True)
|
27 |
-
if not any(data_path.iterdir()):
|
28 |
-
api.competition_download_cli(competition, path=data_path)
|
29 |
-
with ZipFile(data_path / (competition + ".zip"), "r") as zipObj:
|
30 |
-
# Extract all the contents of zip file in current directory
|
31 |
-
zipObj.extractall(path=data_path)
|
32 |
-
os.remove(data_path / (competition + ".zip"))
|
33 |
-
|
34 |
-
print(os.listdir(data_path))
|
35 |
-
|
36 |
-
|
37 |
-
def download_dataset(owner: str, dataset: str, input_path: str | Path) -> bool:
|
38 |
-
"""
|
39 |
-
Downloads data from kaggle competition only if input folder is empty
|
40 |
-
:param comptetition: string with the competition name id of kaggle
|
41 |
-
:param input_path: path of the input folder
|
42 |
-
"""
|
43 |
-
downloaded = False
|
44 |
-
data_path = Path(input_path)
|
45 |
-
if not data_path.exists():
|
46 |
-
data_path.mkdir(parents=True)
|
47 |
-
if not any(data_path.iterdir()):
|
48 |
-
downloaded = True
|
49 |
-
api.dataset_download_files(f"{owner}/{dataset}", path=data_path)
|
50 |
-
with ZipFile(data_path / (dataset + ".zip"), "r") as zipObj:
|
51 |
-
# Extract all the contents of zip file in current directory
|
52 |
-
zipObj.extractall(path=data_path)
|
53 |
-
os.remove(data_path / (dataset + ".zip"))
|
54 |
-
|
55 |
-
print(os.listdir(data_path))
|
56 |
-
|
57 |
-
return downloaded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
training/notebooks/lab.ipynb
DELETED
The diff for this file is too large to render.
See raw diff
|
|
training/notebooks/transforms-lab.ipynb
DELETED
The diff for this file is too large to render.
See raw diff
|
|
deployment/transforms.py β transforms.py
RENAMED
File without changes
|
models/vit_saved.pth β vit_saved.pth
RENAMED
File without changes
|
deployment/vocab.py β vocab.py
RENAMED
File without changes
|