[Demo Gradio] Add to Huggingface Space
Browse files- AUTHORS.rst +13 -0
- README.md +2 -11
- app.py +9 -0
- components/callbacks.py +4 -0
- components/data_module.py +81 -0
- config.py +3 -0
- config/config.yaml +0 -0
- data/.gitkeep +0 -0
- docker-compose.yml +23 -0
- models/__init__.py +0 -0
- models/base_model/classification.py +63 -0
- models/base_model/gan.py +0 -0
- models/base_model/regression.py +55 -0
- models/metrics/classification.py +44 -0
- models/metrics/regression.py +28 -0
- models/model_lit.py +50 -0
- models/modules/sample_torch_module.py +12 -0
- requirements.txt +2 -0
- tests/test_resource.py +4 -0
- utils/.gitkeep +0 -0
AUTHORS.rst
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=======
|
2 |
+
Credits
|
3 |
+
=======
|
4 |
+
|
5 |
+
Development Lead
|
6 |
+
----------------
|
7 |
+
|
8 |
+
* Full name of the author <Email of the author>
|
9 |
+
|
10 |
+
Contributors
|
11 |
+
------------
|
12 |
+
|
13 |
+
None yet. Why not be the first?
|
README.md
CHANGED
@@ -1,12 +1,3 @@
|
|
1 |
-
|
2 |
-
title: Table Extraction
|
3 |
-
emoji: 🔥
|
4 |
-
colorFrom: purple
|
5 |
-
colorTo: purple
|
6 |
-
sdk: gradio
|
7 |
-
sdk_version: 3.28.1
|
8 |
-
app_file: app.py
|
9 |
-
pinned: false
|
10 |
-
---
|
11 |
|
12 |
-
|
|
|
1 |
+
# huggingface-space
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
+
Base structure for Deep learning project
|
app.py
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
|
4 |
+
def greet(name):
|
5 |
+
return "Hello " + name + "!!"
|
6 |
+
|
7 |
+
|
8 |
+
iface = gr.Interface(fn=greet, inputs="text", outputs="text")
|
9 |
+
iface.launch()
|
components/callbacks.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Define callbacks here
|
2 |
+
from pytorch_lightning.callbacks import EarlyStopping
|
3 |
+
|
4 |
+
early_stopping = EarlyStopping(monitor="loss", min_delta=0, patience=3)
|
components/data_module.py
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Callable, List, Optional, Union
|
2 |
+
|
3 |
+
import torch
|
4 |
+
from pytorch_lightning import LightningDataModule
|
5 |
+
from torch.utils.data import DataLoader, Dataset
|
6 |
+
|
7 |
+
|
8 |
+
class SampleDataset(Dataset):
|
9 |
+
|
10 |
+
def __init__(self,
|
11 |
+
x: Union[List, torch.Tensor],
|
12 |
+
y: Union[List, torch.Tensor],
|
13 |
+
transforms: Optional[Callable] = None) -> None:
|
14 |
+
super(SampleDataset, self).__init__()
|
15 |
+
self.x = x
|
16 |
+
self.y = y
|
17 |
+
|
18 |
+
if transforms is None:
|
19 |
+
# Replace None with some default transforms
|
20 |
+
# If image, could be an Resize and ToTensor
|
21 |
+
self.transforms = lambda x: x
|
22 |
+
else:
|
23 |
+
self.transforms = transforms
|
24 |
+
|
25 |
+
def __len__(self):
|
26 |
+
return len(self.x)
|
27 |
+
|
28 |
+
def __getitem__(self, index: int):
|
29 |
+
x = self.x[index]
|
30 |
+
y = self.y[index]
|
31 |
+
|
32 |
+
x = self.transforms(x)
|
33 |
+
return x, y
|
34 |
+
|
35 |
+
|
36 |
+
class SampleDataModule(LightningDataModule):
|
37 |
+
|
38 |
+
def __init__(self,
|
39 |
+
x: Union[List, torch.Tensor],
|
40 |
+
y: Union[List, torch.Tensor],
|
41 |
+
transforms: Optional[Callable] = None,
|
42 |
+
val_ratio: float = 0,
|
43 |
+
batch_size: int = 32) -> None:
|
44 |
+
super(SampleDataModule, self).__init__()
|
45 |
+
assert 0 <= val_ratio < 1
|
46 |
+
assert isinstance(batch_size, int)
|
47 |
+
self.x = x
|
48 |
+
self.y = y
|
49 |
+
|
50 |
+
self.transforms = transforms
|
51 |
+
self.val_ratio = val_ratio
|
52 |
+
self.batch_size = batch_size
|
53 |
+
|
54 |
+
self.setup()
|
55 |
+
self.prepare_data()
|
56 |
+
|
57 |
+
def setup(self, stage: Optional[str] = None) -> None:
|
58 |
+
pass
|
59 |
+
|
60 |
+
def prepare_data(self) -> None:
|
61 |
+
n_samples: int = len(self.x)
|
62 |
+
train_size: int = n_samples - int(n_samples * self.val_ratio)
|
63 |
+
|
64 |
+
self.train_dataset = SampleDataset(x=self.x[:train_size],
|
65 |
+
y=self.y[:train_size],
|
66 |
+
transforms=self.transforms)
|
67 |
+
if train_size < n_samples:
|
68 |
+
self.val_dataset = SampleDataset(x=self.x[train_size:],
|
69 |
+
y=self.y[train_size:],
|
70 |
+
transforms=self.transforms)
|
71 |
+
else:
|
72 |
+
self.val_dataset = SampleDataset(x=self.x[-self.batch_size:],
|
73 |
+
y=self.y[-self.batch_size:],
|
74 |
+
transforms=self.transforms)
|
75 |
+
|
76 |
+
def train_dataloader(self) -> DataLoader:
|
77 |
+
return DataLoader(dataset=self.train_dataset,
|
78 |
+
batch_size=self.batch_size)
|
79 |
+
|
80 |
+
def val_dataloader(self) -> DataLoader:
|
81 |
+
return DataLoader(dataset=self.val_dataset, batch_size=self.batch_size)
|
config.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
from dynaconf import Dynaconf
|
2 |
+
|
3 |
+
CFG = Dynaconf(envvar_prefix="DYNACONF", settings_files=["config/config.yaml"])
|
config/config.yaml
ADDED
File without changes
|
data/.gitkeep
ADDED
File without changes
|
docker-compose.yml
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: "3.7"
|
2 |
+
|
3 |
+
services:
|
4 |
+
model_name:
|
5 |
+
build:
|
6 |
+
context: .
|
7 |
+
dockerfile: .docker/Dockerfile
|
8 |
+
container_name: model_name
|
9 |
+
ports:
|
10 |
+
- "8996:8996"
|
11 |
+
env_file:
|
12 |
+
- ./.env
|
13 |
+
volumes:
|
14 |
+
- ./data:/home/working/data:ro
|
15 |
+
|
16 |
+
# This part is used to enable GPU support
|
17 |
+
deploy:
|
18 |
+
resources:
|
19 |
+
reservations:
|
20 |
+
devices:
|
21 |
+
- driver: nvidia
|
22 |
+
count: 1
|
23 |
+
capabilities: [ gpu ]
|
models/__init__.py
ADDED
File without changes
|
models/base_model/classification.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from abc import abstractmethod
|
2 |
+
from typing import Any, Dict, List
|
3 |
+
|
4 |
+
import torch
|
5 |
+
from pytorch_lightning import LightningModule
|
6 |
+
from torch import Tensor
|
7 |
+
|
8 |
+
|
9 |
+
class LightningClassification(LightningModule):
|
10 |
+
|
11 |
+
@abstractmethod
|
12 |
+
def __init__(self, *args, **kwargs) -> None:
|
13 |
+
super(LightningClassification, self).__init__(*args, **kwargs)
|
14 |
+
self.train_batch_output: List[Dict] = []
|
15 |
+
self.validation_batch_output: List[Dict] = []
|
16 |
+
self.log_value_list: List[str] = ['loss', 'f1', 'precision', 'recall']
|
17 |
+
|
18 |
+
@abstractmethod
|
19 |
+
def forward(self, *args, **kwargs) -> Any:
|
20 |
+
pass
|
21 |
+
|
22 |
+
@abstractmethod
|
23 |
+
def configure_optimizers(self):
|
24 |
+
pass
|
25 |
+
|
26 |
+
@abstractmethod
|
27 |
+
def loss(self, input: Tensor, target: Tensor, **kwargs) -> Tensor:
|
28 |
+
pass
|
29 |
+
|
30 |
+
@abstractmethod
|
31 |
+
def training_step(self, batch, batch_idx):
|
32 |
+
pass
|
33 |
+
|
34 |
+
def __average(self, key: str, outputs: List[Dict]) -> Tensor:
|
35 |
+
target_arr = torch.Tensor([val[key] for val in outputs]).float()
|
36 |
+
return target_arr.mean()
|
37 |
+
|
38 |
+
@torch.no_grad()
|
39 |
+
def on_train_epoch_start(self) -> None:
|
40 |
+
self.train_batch_output = []
|
41 |
+
|
42 |
+
@torch.no_grad()
|
43 |
+
def on_train_epoch_end(self) -> None:
|
44 |
+
for key in self.log_value_list:
|
45 |
+
val = self.__average(key=key, outputs=self.train_batch_output)
|
46 |
+
log_name = f"training/{key}"
|
47 |
+
self.log(name=log_name, value=val)
|
48 |
+
|
49 |
+
@abstractmethod
|
50 |
+
@torch.no_grad()
|
51 |
+
def validation_step(self, batch, batch_idx):
|
52 |
+
pass
|
53 |
+
|
54 |
+
@torch.no_grad()
|
55 |
+
def on_validation_epoch_start(self) -> None:
|
56 |
+
self.validation_batch_output = []
|
57 |
+
|
58 |
+
@torch.no_grad()
|
59 |
+
def on_validation_epoch_end(self) -> None:
|
60 |
+
for key in self.log_value_list:
|
61 |
+
val = self.__average(key=key, outputs=self.validation_batch_output)
|
62 |
+
log_name = f"val/{key}"
|
63 |
+
self.log(name=log_name, value=val)
|
models/base_model/gan.py
ADDED
File without changes
|
models/base_model/regression.py
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from abc import abstractmethod
|
2 |
+
from typing import Any, Dict, List
|
3 |
+
|
4 |
+
import torch
|
5 |
+
from pytorch_lightning import LightningModule
|
6 |
+
from torch import Tensor
|
7 |
+
|
8 |
+
|
9 |
+
class LightningRegression(LightningModule):
|
10 |
+
|
11 |
+
@abstractmethod
|
12 |
+
def __init__(self, *args, **kwargs) -> None:
|
13 |
+
super(LightningRegression, self).__init__(*args, **kwargs)
|
14 |
+
self.train_step_output: List[Dict] = []
|
15 |
+
self.validation_step_output: List[Dict] = []
|
16 |
+
self.log_value_list: List[str] = ['loss', 'mse', 'mape']
|
17 |
+
|
18 |
+
@abstractmethod
|
19 |
+
def forward(self, *args, **kwargs) -> Any:
|
20 |
+
pass
|
21 |
+
|
22 |
+
@abstractmethod
|
23 |
+
def configure_optimizers(self):
|
24 |
+
pass
|
25 |
+
|
26 |
+
@abstractmethod
|
27 |
+
def loss(self, input: Tensor, output: Tensor, **kwargs):
|
28 |
+
return 0
|
29 |
+
|
30 |
+
@abstractmethod
|
31 |
+
def training_step(self, batch, batch_idx):
|
32 |
+
pass
|
33 |
+
|
34 |
+
def __average(self, key: str, outputs: List[Dict]) -> Tensor:
|
35 |
+
target_arr = torch.Tensor([val[key] for val in outputs]).float()
|
36 |
+
return target_arr.mean()
|
37 |
+
|
38 |
+
@torch.no_grad()
|
39 |
+
def on_train_epoch_end(self) -> None:
|
40 |
+
for key in self.log_value_list:
|
41 |
+
val = self.__average(key=key, outputs=self.train_step_output)
|
42 |
+
log_name = f"training/{key}"
|
43 |
+
self.log(name=log_name, value=val)
|
44 |
+
|
45 |
+
@torch.no_grad()
|
46 |
+
@abstractmethod
|
47 |
+
def validation_step(self, batch, batch_idx):
|
48 |
+
pass
|
49 |
+
|
50 |
+
@torch.no_grad()
|
51 |
+
def validation_epoch_end(self, outputs):
|
52 |
+
for key in self.log_value_list:
|
53 |
+
val = self.__average(key=key, outputs=self.validation_step_output)
|
54 |
+
log_name = f"training/{key}"
|
55 |
+
self.log(name=log_name, value=val)
|
models/metrics/classification.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict
|
2 |
+
|
3 |
+
import torch
|
4 |
+
from torchmetrics import functional as FM
|
5 |
+
|
6 |
+
|
7 |
+
def classification_metrics(
|
8 |
+
preds: torch.Tensor,
|
9 |
+
target: torch.Tensor,
|
10 |
+
num_classes: int,
|
11 |
+
average: str = 'macro',
|
12 |
+
task: str = 'multiclass') -> Dict[str, torch.Tensor]:
|
13 |
+
"""
|
14 |
+
get_classification_metrics
|
15 |
+
Return some metrics evaluation the classification task
|
16 |
+
|
17 |
+
Parameters
|
18 |
+
----------
|
19 |
+
preds : torch.Tensor
|
20 |
+
logits, probs
|
21 |
+
target : torch.Tensor
|
22 |
+
targets label
|
23 |
+
|
24 |
+
Returns
|
25 |
+
-------
|
26 |
+
Dict[str, torch.Tensor]
|
27 |
+
_description_
|
28 |
+
"""
|
29 |
+
f1 = FM.f1_score(preds=preds,
|
30 |
+
target=target,
|
31 |
+
num_classes=num_classes,
|
32 |
+
task=task,
|
33 |
+
average=average)
|
34 |
+
recall = FM.recall(preds=preds,
|
35 |
+
target=target,
|
36 |
+
num_classes=num_classes,
|
37 |
+
task=task,
|
38 |
+
average=average)
|
39 |
+
precision = FM.precision(preds=preds,
|
40 |
+
target=target,
|
41 |
+
num_classes=num_classes,
|
42 |
+
task=task,
|
43 |
+
average=average)
|
44 |
+
return dict(f1=f1, precision=precision, recall=recall)
|
models/metrics/regression.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict
|
2 |
+
|
3 |
+
import torch
|
4 |
+
from torchmetrics import functional as FM
|
5 |
+
|
6 |
+
|
7 |
+
def regression_metrics(preds: torch.Tensor,
|
8 |
+
target: torch.Tensor) -> Dict[str, torch.Tensor]:
|
9 |
+
"""
|
10 |
+
get_classification_metrics
|
11 |
+
Return some metrics evaluation the classification task
|
12 |
+
|
13 |
+
Parameters
|
14 |
+
----------
|
15 |
+
preds : torch.Tensor
|
16 |
+
logits, probs
|
17 |
+
target : torch.Tensor
|
18 |
+
targets label
|
19 |
+
|
20 |
+
Returns
|
21 |
+
-------
|
22 |
+
Dict[str, torch.Tensor]
|
23 |
+
_description_
|
24 |
+
"""
|
25 |
+
mse: torch.Tensor = FM.mean_squared_error(preds=preds, target=target)
|
26 |
+
mape: torch.Tensor = FM.mean_absolute_percentage_error(preds=preds,
|
27 |
+
target=target)
|
28 |
+
return dict(mse=mse, mape=mape)
|
models/model_lit.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from torch import Tensor, nn, optim
|
2 |
+
from torch.nn import functional as F
|
3 |
+
|
4 |
+
from .base_model.classification import LightningClassification
|
5 |
+
from .metrics.classification import classification_metrics
|
6 |
+
from .modules.sample_torch_module import UselessLayer
|
7 |
+
|
8 |
+
|
9 |
+
class UselessClassification(LightningClassification):
|
10 |
+
|
11 |
+
def __init__(self, n_classes: int, lr: float, **kwargs) -> None:
|
12 |
+
super(UselessClassification).__init__()
|
13 |
+
self.save_hyperparameters()
|
14 |
+
self.n_classes = n_classes
|
15 |
+
self.lr = lr
|
16 |
+
self.main = nn.Sequential(UselessLayer(), nn.GELU())
|
17 |
+
|
18 |
+
def forward(self, x: Tensor) -> Tensor:
|
19 |
+
return self.main(x)
|
20 |
+
|
21 |
+
def loss(self, input: Tensor, target: Tensor) -> Tensor:
|
22 |
+
return F.mse_loss(input=input, target=target)
|
23 |
+
|
24 |
+
def configure_optimizers(self):
|
25 |
+
optimizer = optim.Adam(params=self.parameters(), lr=self.lr)
|
26 |
+
return optimizer
|
27 |
+
|
28 |
+
def training_step(self, batch, batch_idx):
|
29 |
+
x, y = batch
|
30 |
+
|
31 |
+
logits = self.forward(x)
|
32 |
+
loss = self.loss(input=x, target=y)
|
33 |
+
metrics = classification_metrics(preds=logits,
|
34 |
+
target=y,
|
35 |
+
num_classes=self.n_classes)
|
36 |
+
|
37 |
+
self.train_batch_output.append({'loss': loss, **metrics})
|
38 |
+
return loss
|
39 |
+
|
40 |
+
def validation_step(self, batch, batch_idx):
|
41 |
+
x, y = batch
|
42 |
+
|
43 |
+
logits = self.forward(x)
|
44 |
+
loss = self.loss(input=x, target=y)
|
45 |
+
metrics = classification_metrics(preds=logits,
|
46 |
+
target=y,
|
47 |
+
num_classes=self.n_classes)
|
48 |
+
|
49 |
+
self.validation_batch_output.append({'loss': loss, **metrics})
|
50 |
+
return loss
|
models/modules/sample_torch_module.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from torch import Tensor, nn
|
2 |
+
|
3 |
+
|
4 |
+
class UselessLayer(nn.Module):
|
5 |
+
|
6 |
+
def __init__(self) -> None:
|
7 |
+
super(UselessLayer, self).__init__()
|
8 |
+
self.seq = nn.Identity()
|
9 |
+
|
10 |
+
def forward(self, x: Tensor) -> Tensor:
|
11 |
+
x = self.seq(x)
|
12 |
+
return x
|
requirements.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
dynaconf==3.1.12
|
2 |
+
pytorch-lightning==2.0.0
|
tests/test_resource.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def test_cuda():
|
2 |
+
from torch.cuda import is_available
|
3 |
+
assert is_available()
|
4 |
+
|
utils/.gitkeep
ADDED
File without changes
|