modelmark 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. modelmark-0.1.0/LICENSE +21 -0
  2. modelmark-0.1.0/PKG-INFO +109 -0
  3. modelmark-0.1.0/README.md +91 -0
  4. modelmark-0.1.0/pyproject.toml +31 -0
  5. modelmark-0.1.0/setup.cfg +4 -0
  6. modelmark-0.1.0/src/modelmark/__init__.py +3 -0
  7. modelmark-0.1.0/src/modelmark/__main__.py +5 -0
  8. modelmark-0.1.0/src/modelmark/common/dataset.py +116 -0
  9. modelmark-0.1.0/src/modelmark/common/downloader.py +88 -0
  10. modelmark-0.1.0/src/modelmark/common/loader.py +63 -0
  11. modelmark-0.1.0/src/modelmark/common/logger.py +34 -0
  12. modelmark-0.1.0/src/modelmark/common/parser.py +134 -0
  13. modelmark-0.1.0/src/modelmark/common/report.py +734 -0
  14. modelmark-0.1.0/src/modelmark/common/tester.py +162 -0
  15. modelmark-0.1.0/src/modelmark/common/utils.py +105 -0
  16. modelmark-0.1.0/src/modelmark/config.py +97 -0
  17. modelmark-0.1.0/src/modelmark/constants.py +34 -0
  18. modelmark-0.1.0/src/modelmark/modelmark.py +206 -0
  19. modelmark-0.1.0/src/modelmark/models/conv.py +75 -0
  20. modelmark-0.1.0/src/modelmark/models/gru.py +45 -0
  21. modelmark-0.1.0/src/modelmark/models/linear.py +65 -0
  22. modelmark-0.1.0/src/modelmark/models/lstm.py +43 -0
  23. modelmark-0.1.0/src/modelmark/models/transformer.py +86 -0
  24. modelmark-0.1.0/src/modelmark.egg-info/PKG-INFO +109 -0
  25. modelmark-0.1.0/src/modelmark.egg-info/SOURCES.txt +27 -0
  26. modelmark-0.1.0/src/modelmark.egg-info/dependency_links.txt +1 -0
  27. modelmark-0.1.0/src/modelmark.egg-info/entry_points.txt +2 -0
  28. modelmark-0.1.0/src/modelmark.egg-info/requires.txt +7 -0
  29. modelmark-0.1.0/src/modelmark.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andrew Larin
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.
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: modelmark
3
+ Version: 0.1.0
4
+ Summary: Benchmark framework for neural network models
5
+ Author: Andrew Larin
6
+ License: MIT
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy>=2.4
11
+ Requires-Dist: pandas>=3.0
12
+ Requires-Dist: torch>=2.12
13
+ Requires-Dist: tqdm>=4.67
14
+ Requires-Dist: playwright>=1.62
15
+ Requires-Dist: rich>=15.0
16
+ Requires-Dist: thop>=0.1
17
+ Dynamic: license-file
18
+
19
+ # ModelMark
20
+ This tool will help you to test NN models against each other, and form a detailed report that is easy to embed to a website.
21
+
22
+ **Model evaluation report example:**
23
+
24
+ <img src="result.png" alt="Evaluation result" width="600">
25
+
26
+ ## About:
27
+
28
+ At each testing run iteration, modelmark:
29
+
30
+ 1) Selects next dataset, context, model and seed
31
+ 2) Seeds the generators for reproducibility
32
+ 3) Creates the loader, model and tester objects
33
+ 4) Trains the model for E epochs, restores the state with the least validation loss
34
+ 5) Tracks the GFLOPs, Memory, Time - all AVG over whole test
35
+ 5) Evaluates the model on dataset with metrics from configuration file
36
+ 6) Stores the mean result over S runs
37
+
38
+ That way, the more seeds you run, the more "fair" the results are.
39
+ Finally, modelmark will form the report with all the testing results, training stats and your machine metadata.
40
+
41
+ More on training stats:
42
+
43
+ Time - average time per epoch
44
+ Params - total number of model params
45
+ GFLOPs - average per batch
46
+ Peak Memory - max per training iteration
47
+
48
+ Test consists of F * C * M * S runs, where:
49
+
50
+ F - number of dataset files in the config (e.g. ["ETTh1" : ..., "Weather" : ...] - means F = 2)
51
+ C - number of context sizes (e.g. [32, 64, 128] - means C = 3)
52
+ M - number of models (e.g. ["Linear" : ..., "LSTM" : ...] - means M = 2)
53
+ S - number of seeds (e.g. [42, 43, 44] - means S = 2)
54
+
55
+ # Requirements:
56
+
57
+ OS: Windows or Linux
58
+ Python: 3.12+
59
+
60
+ # Usage:
61
+
62
+ 1) Install the package
63
+
64
+ pip install "modelmark @ git+https://github.com/gloptim77/ModelMark.git"
65
+
66
+ 2) Run the initialization in an empty folder
67
+
68
+ modelmark -t init
69
+
70
+ 3.1) It will create two folders "modelmark_files" and "models"
71
+
72
+ In modelmark_files/config.py there are 3 main configs:
73
+
74
+ model_config = {...} - Models hyperparameters (number of layers, hidden dim, kernel size, etc.)
75
+ data_config = {...} - Dataset parameters (path to file, input/output features, train/val ratios, etc.)
76
+ test_config = {...} - Testing options (optimizer, loss criterion, metrics, learning rate, etc.)
77
+
78
+ 3.2) (Optional) download the ETT dataset files
79
+
80
+ modelmark -t load
81
+
82
+ 4) When your config, model and data are ready, you can run the testing
83
+
84
+ modelmark -t run
85
+
86
+ 5) When the test is over, report results will be in files "result.html" and "result.png"
87
+
88
+ Example of the model: "src/modelmark/models/linear.py"
89
+ Example of the config: "src/modelmark/config.py"
90
+
91
+ You will find the detailed config example with description at [src/modelmark/config.py](src/modelmark/config.py).
92
+
93
+ Example Linear model with detailed description at [src/modelmark/models/linear.py](src/modelmark/models/linear.py).
94
+
95
+ You can change the config and add your model files to suit your test requirements.
96
+
97
+ (But make sure that config and models are compatible)
98
+
99
+ ## Troubleshooting
100
+
101
+ - **If something doesn't work**
102
+
103
+ - Please open an issue and attach your application logs (found at "modelmark_files/modelmark.log") so I can help you troubleshoot.
104
+ - Try to restart
105
+ ```python
106
+ modelmark -t restart
107
+ ```
108
+
109
+ - Try to manually delete "modelmark_files" and return to Usage->2)
@@ -0,0 +1,91 @@
1
+ # ModelMark
2
+ This tool will help you to test NN models against each other, and form a detailed report that is easy to embed to a website.
3
+
4
+ **Model evaluation report example:**
5
+
6
+ <img src="result.png" alt="Evaluation result" width="600">
7
+
8
+ ## About:
9
+
10
+ At each testing run iteration, modelmark:
11
+
12
+ 1) Selects next dataset, context, model and seed
13
+ 2) Seeds the generators for reproducibility
14
+ 3) Creates the loader, model and tester objects
15
+ 4) Trains the model for E epochs, restores the state with the least validation loss
16
+ 5) Tracks the GFLOPs, Memory, Time - all AVG over whole test
17
+ 5) Evaluates the model on dataset with metrics from configuration file
18
+ 6) Stores the mean result over S runs
19
+
20
+ That way, the more seeds you run, the more "fair" the results are.
21
+ Finally, modelmark will form the report with all the testing results, training stats and your machine metadata.
22
+
23
+ More on training stats:
24
+
25
+ Time - average time per epoch
26
+ Params - total number of model params
27
+ GFLOPs - average per batch
28
+ Peak Memory - max per training iteration
29
+
30
+ Test consists of F * C * M * S runs, where:
31
+
32
+ F - number of dataset files in the config (e.g. ["ETTh1" : ..., "Weather" : ...] - means F = 2)
33
+ C - number of context sizes (e.g. [32, 64, 128] - means C = 3)
34
+ M - number of models (e.g. ["Linear" : ..., "LSTM" : ...] - means M = 2)
35
+ S - number of seeds (e.g. [42, 43, 44] - means S = 2)
36
+
37
+ # Requirements:
38
+
39
+ OS: Windows or Linux
40
+ Python: 3.12+
41
+
42
+ # Usage:
43
+
44
+ 1) Install the package
45
+
46
+ pip install "modelmark @ git+https://github.com/gloptim77/ModelMark.git"
47
+
48
+ 2) Run the initialization in an empty folder
49
+
50
+ modelmark -t init
51
+
52
+ 3.1) It will create two folders "modelmark_files" and "models"
53
+
54
+ In modelmark_files/config.py there are 3 main configs:
55
+
56
+ model_config = {...} - Models hyperparameters (number of layers, hidden dim, kernel size, etc.)
57
+ data_config = {...} - Dataset parameters (path to file, input/output features, train/val ratios, etc.)
58
+ test_config = {...} - Testing options (optimizer, loss criterion, metrics, learning rate, etc.)
59
+
60
+ 3.2) (Optional) download the ETT dataset files
61
+
62
+ modelmark -t load
63
+
64
+ 4) When your config, model and data are ready, you can run the testing
65
+
66
+ modelmark -t run
67
+
68
+ 5) When the test is over, report results will be in files "result.html" and "result.png"
69
+
70
+ Example of the model: "src/modelmark/models/linear.py"
71
+ Example of the config: "src/modelmark/config.py"
72
+
73
+ You will find the detailed config example with description at [src/modelmark/config.py](src/modelmark/config.py).
74
+
75
+ Example Linear model with detailed description at [src/modelmark/models/linear.py](src/modelmark/models/linear.py).
76
+
77
+ You can change the config and add your model files to suit your test requirements.
78
+
79
+ (But make sure that config and models are compatible)
80
+
81
+ ## Troubleshooting
82
+
83
+ - **If something doesn't work**
84
+
85
+ - Please open an issue and attach your application logs (found at "modelmark_files/modelmark.log") so I can help you troubleshoot.
86
+ - Try to restart
87
+ ```python
88
+ modelmark -t restart
89
+ ```
90
+
91
+ - Try to manually delete "modelmark_files" and return to Usage->2)
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project.scripts]
6
+ modelmark = "modelmark.modelmark:run"
7
+
8
+ [project]
9
+ name = "modelmark"
10
+ version = "0.1.0"
11
+ description = "Benchmark framework for neural network models"
12
+ readme = "README.md"
13
+ requires-python = ">=3.12"
14
+ license = { text = "MIT" }
15
+
16
+ authors = [
17
+ { name = "Andrew Larin" }
18
+ ]
19
+
20
+ dependencies = [
21
+ "numpy>=2.4",
22
+ "pandas>=3.0",
23
+ "torch>=2.12",
24
+ "tqdm>=4.67",
25
+ "playwright>=1.62",
26
+ "rich>=15.0",
27
+ "thop>=0.1"
28
+ ]
29
+
30
+ [tool.setuptools.packages.find]
31
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Reusable testing framework."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ import sys
2
+ from modelmark import run
3
+
4
+ if __name__ == "__main__":
5
+ sys.exit(run())
@@ -0,0 +1,116 @@
1
+ from __future__ import annotations
2
+
3
+ import torch
4
+ from torch.utils.data import Dataset
5
+
6
+ import copy
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+ from modelmark.common.utils import load_config
11
+ config = load_config()
12
+
13
+ class CausalDataset(Dataset):
14
+ """
15
+ Sliding-window dataset for ETT.
16
+
17
+ Returns:
18
+ x: [seq_len, num_features]
19
+ y: [pred_len, 1]
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ data_config: dict,
25
+ seq_len: int,
26
+ pred_len: int,
27
+ split: str = "train",
28
+ mean: np.ndarray | None = None,
29
+ std: np.ndarray | None = None,
30
+ ):
31
+ super().__init__()
32
+
33
+ self.seq_len = seq_len
34
+ self.pred_len = pred_len
35
+
36
+ # Extract config
37
+ data_path = config.data_path + data_config["path"]
38
+ features = copy.deepcopy(data_config["input_features"])
39
+ targets = data_config["output_features"]
40
+ features.extend([item for item in targets if item not in features])
41
+ train_ratio = data_config["train_ratio"]
42
+ val_ratio = data_config["val_ratio"]
43
+
44
+ # Load, extract, convert
45
+ df = pd.read_csv(data_path)
46
+
47
+ df = df[features]
48
+ data = df.values.astype(np.float32)
49
+ self.input_indices = torch.tensor(df.columns.get_indexer(data_config["input_features"]), dtype=torch.int)
50
+ self.output_indices = torch.tensor(df.columns.get_indexer(data_config["output_features"]), dtype=torch.int)
51
+
52
+ n = len(data)
53
+
54
+ train_end = int(n * train_ratio)
55
+ val_end = int(n * (train_ratio + val_ratio))
56
+
57
+ if split == "train":
58
+ start = 0
59
+ end = train_end
60
+ elif split == "val":
61
+ start = train_end
62
+ end = val_end
63
+ elif split == "test":
64
+ start = val_end
65
+ end = n
66
+ else:
67
+ raise ValueError(f"Unknown split: {split}")
68
+
69
+ # Save scaling information
70
+ if split == "train":
71
+ self.mean = data[start:train_end].mean(axis=0)
72
+ self.std = data[start:train_end].std(axis=0)
73
+
74
+ # Prevent division by zero
75
+ self.std[self.std < 1e-8] = 1.0
76
+ else:
77
+ if mean is None or std is None:
78
+ raise ValueError(
79
+ "Validation/test datasets need training mean and std."
80
+ )
81
+
82
+ self.mean = mean
83
+ self.std = std
84
+
85
+ # Scale using TRAIN statistics
86
+ data = (data - self.mean) / self.std
87
+
88
+ # Keep only the split region
89
+ self.data = data[start:end]
90
+
91
+ # Number of valid windows
92
+ self.length = len(self.data) - seq_len - pred_len + 1
93
+
94
+ if self.length <= 0:
95
+ raise ValueError(
96
+ f"Split '{split}' is too short for "
97
+ f"seq_len={seq_len}, pred_len={pred_len}"
98
+ )
99
+
100
+ def __len__(self):
101
+ return self.length
102
+
103
+ def __getitem__(self, idx):
104
+ x_start = idx
105
+ x_end = x_start + self.seq_len
106
+
107
+ y_start = x_end
108
+ y_end = y_start + self.pred_len
109
+
110
+ x = self.data[x_start:x_end, self.input_indices]
111
+ y = self.data[y_start:y_end, self.output_indices]
112
+
113
+ return (
114
+ torch.from_numpy(x),
115
+ torch.from_numpy(y),
116
+ )
@@ -0,0 +1,88 @@
1
+ """Download time-series datasets to a local directory.
2
+
3
+ Supports:
4
+ - ETT (ETTh1, ETTh2, ETTm1, ETTm2) — from the ETDataset GitHub repo
5
+ - Air Quality (UCI Repository)
6
+ - Weather (Max Planck Institute / Jena climate dataset)
7
+ """
8
+
9
+ import argparse
10
+ import shutil
11
+ import urllib.request
12
+ import zipfile
13
+ from pathlib import Path
14
+
15
+ import logging
16
+ logger = logging.getLogger(__name__)
17
+ from rich.console import Console
18
+ console = Console()
19
+
20
+ DATASETS = {
21
+ "ett": {
22
+ "dir": "data/ett",
23
+ "kind": "multi_csv",
24
+ "base_url": "https://raw.githubusercontent.com/zhouhaoyi/ETDataset/main/ETT-small",
25
+ "files": ["ETTh1", "ETTh2", "ETTm1", "ETTm2"],
26
+ },
27
+ "air_quality": {
28
+ "dir": "data/air_quality",
29
+ "kind": "zip",
30
+ "url": "https://archive.ics.uci.edu/static/public/360/air+quality.zip",
31
+ "zip_name": "AirQualityUCI.zip",
32
+ },
33
+ "weather": {
34
+ "dir": "data/weather",
35
+ "kind": "zip",
36
+ "url": "https://storage.googleapis.com/tensorflow/tf-keras-datasets/jena_climate_2009_2016.csv.zip",
37
+ "zip_name": "jena_climate_2009_2016.csv.zip",
38
+ },
39
+ }
40
+
41
+
42
+ def download_ett(cfg: dict, out_dir: Path) -> None:
43
+ out_dir.mkdir(parents=True, exist_ok=True)
44
+ for name in cfg["files"]:
45
+ url = f"{cfg['base_url']}/{name}.csv"
46
+ path = out_dir / f"{name}.csv"
47
+ if path.exists():
48
+ logger.info(f"{name}.csv already exists, skipping.")
49
+ continue
50
+ logger.info(f"Downloading {name}...")
51
+ console.print(f"Downloading {name}...")
52
+ urllib.request.urlretrieve(url, path)
53
+ logger.info(f"Saved to {path}")
54
+ console.print(f"Saved to {path}", style="green")
55
+
56
+
57
+ def download_zip_dataset(cfg: dict, out_dir: Path) -> None:
58
+ out_dir.mkdir(parents=True, exist_ok=True)
59
+ zip_path = out_dir / cfg["zip_name"]
60
+
61
+ if not zip_path.exists():
62
+ logger.info(f"Downloading {cfg['zip_name']}...")
63
+ console.print(f"Downloading {cfg['zip_name']}...")
64
+ urllib.request.urlretrieve(cfg["url"], zip_path)
65
+ logger.info(f"Saved to {zip_path}")
66
+ console.print(f"Saved to {zip_path}", style="green")
67
+ else:
68
+ logger.info(f"{cfg['zip_name']} already exists, skipping download.")
69
+ console.print(f"{cfg['zip_name']} already exists, skipping download.", style="yellow")
70
+
71
+ logger.info(f"Extracting {zip_path.name}...")
72
+ console.print(f"Extracting {zip_path.name}...")
73
+ with zipfile.ZipFile(zip_path) as zf:
74
+ zf.extractall(out_dir)
75
+ console.print(f"Extracted to {out_dir}", color="green")
76
+
77
+ def download_dataset(name: str) -> None:
78
+ if name not in DATASETS:
79
+ valid = ", ".join(DATASETS)
80
+ raise ValueError(f"Unknown dataset '{name}'. Choose from: {valid}")
81
+
82
+ cfg = DATASETS[name]
83
+ out_dir = Path(cfg["dir"])
84
+
85
+ if cfg["kind"] == "multi_csv":
86
+ download_ett(cfg, out_dir)
87
+ elif cfg["kind"] == "zip":
88
+ download_zip_dataset(cfg, out_dir)
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ from modelmark.common.dataset import CausalDataset
4
+ from torch.utils.data import DataLoader
5
+
6
+ import logging
7
+ logger = logging.getLogger(__name__)
8
+ from modelmark.common.utils import load_config
9
+ config = load_config()
10
+
11
+ class Loader:
12
+
13
+ def __init__(self, file_config : dict, context_size : int):
14
+
15
+ self.get_dataloaders(file_config, context_size)
16
+ logger.debug(f"Dataset len stats: train={len(self.train_loader)} val={len(self.val_loader)} test={len(self.test_loader)}")
17
+
18
+ def get_dataloaders(self, file_config : dict, context_size : int) -> tuple[DataLoader, DataLoader, DataLoader]:
19
+ """Load the data, pack to datasets, create the loaders and return them"""
20
+
21
+ train_ds = CausalDataset(data_config = file_config,
22
+ seq_len = context_size,
23
+ pred_len = context_size,
24
+ split = "train")
25
+
26
+ val_ds = CausalDataset(data_config = file_config,
27
+ seq_len = context_size,
28
+ pred_len = context_size,
29
+ split = "val",
30
+ mean = train_ds.mean,
31
+ std = train_ds.std)
32
+
33
+ test_ds = CausalDataset(data_config = file_config,
34
+ seq_len = context_size,
35
+ pred_len = context_size,
36
+ split = "test",
37
+ mean = train_ds.mean,
38
+ std = train_ds.std)
39
+
40
+ self.train_loader = DataLoader(
41
+ train_ds,
42
+ batch_size = config.test_config["optim"]["batch_size"],
43
+ shuffle = True,
44
+ num_workers = 0,
45
+ pin_memory = True,
46
+ )
47
+
48
+ self.val_loader = DataLoader(
49
+ val_ds,
50
+ batch_size = config.test_config["optim"]["batch_size"],
51
+ shuffle = False,
52
+ num_workers = 0,
53
+ pin_memory = True,
54
+ )
55
+
56
+ self.test_loader = DataLoader(
57
+ test_ds,
58
+ batch_size = config.test_config["optim"]["batch_size"],
59
+ shuffle = False,
60
+ num_workers = 0,
61
+ pin_memory = True,
62
+ )
63
+
@@ -0,0 +1,34 @@
1
+ import logging
2
+ import sys
3
+ from typing import Optional
4
+
5
+ def setup_logging(log_format: Optional[str] = None) -> None:
6
+ """
7
+ Configure logging for the entire application.
8
+
9
+ Args:
10
+ log_format: Optional custom format string. If None, a default format is used.
11
+ """
12
+
13
+ if log_format is None:
14
+ log_format = "%(asctime)s [%(levelname)s] [%(name)s] - %(message)s"
15
+
16
+ logging.basicConfig(
17
+ level = logging.DEBUG,
18
+ filemode = 'w',
19
+ filename='modelmark_files/modelmark.log',
20
+ format = log_format,
21
+ datefmt = '%H:%M:%S',
22
+ )
23
+
24
+ def get_logger(name: str) -> logging.Logger:
25
+ """
26
+ Convenience function to get a logger for a module.
27
+
28
+ Args:
29
+ name: Usually __name__.
30
+
31
+ Returns:
32
+ A configured logger instance.
33
+ """
34
+ return logging.getLogger(name)