From acc70bbdc08053a068ce2f840f645e6e34b90d5a Mon Sep 17 00:00:00 2001 From: ydcjeff <32727188+ydcjeff@users.noreply.github.com> Date: Sat, 15 May 2021 01:09:13 +0630 Subject: [PATCH 1/8] feat(template): complete cifar10 classification --- .gitignore | 2 + scripts/check_copies.py | 21 ++++ src/components/CodeBlock.vue | 2 + src/components/PaneRight.vue | 4 +- src/components/TabHandlers.vue | 5 + src/metadata/metadata.json | 16 ++- src/templates/template-common/README.md | 67 +++++++++++++ src/templates/template-common/utils.py | 97 +++++++++---------- .../template-vision-classification/README.md | 41 +++++++- .../config.yaml | 49 ++++++++++ .../template-vision-classification/main.py | 42 +++++++- .../trainers.py | 10 +- .../template-vision-classification/utils.py | 97 +++++++++---------- src/templates/templates.json | 2 +- 14 files changed, 341 insertions(+), 114 deletions(-) create mode 100644 src/templates/template-common/README.md create mode 100644 src/templates/template-vision-classification/config.yaml diff --git a/.gitignore b/.gitignore index 878bfbea..92f644eb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ dist-ssr *.local __pycache__ *.log +.vscode +*.tar.gz diff --git a/scripts/check_copies.py b/scripts/check_copies.py index 503a3a0f..61190894 100644 --- a/scripts/check_copies.py +++ b/scripts/check_copies.py @@ -21,5 +21,26 @@ def check_utils(): print(red, "Unmatched", file, reset) +def check_readme(): + red = "\033[31m" + green = "\033[32m" + reset = "\033[0m" + + with open("./src/templates/template-common/README.md", "r") as f: + common_utils = f.read() + + path = Path("./src/templates/") + + for file in path.rglob("**/README.md"): + utils = file.read_text("utf-8") + if utils.find(common_utils) > -1: + print(green, "Matched", file, reset) + else: + print(red, "Unmatched", file, reset) + + if __name__ == "__main__": check_utils() + print() + check_readme() + print() diff --git a/src/components/CodeBlock.vue b/src/components/CodeBlock.vue index a34fc105..3ca71dcf 100644 --- a/src/components/CodeBlock.vue +++ b/src/components/CodeBlock.vue @@ -18,6 +18,7 @@ diff --git a/src/metadata/metadata.json b/src/metadata/metadata.json index cb9f7b9f..289c8607 100644 --- a/src/metadata/metadata.json +++ b/src/metadata/metadata.json @@ -1,4 +1,16 @@ { + "templates_config_lib": { + "argparse": { + "name": "argparse", + "type": "radio", + "description": "Use Argparse/JSON configurations" + }, + "hydra": { + "name": "hydra", + "type": "radio", + "description": "Use Hydra/YAML configurations" + } + }, "training": { "deterministic": { "name": "deterministic", diff --git a/src/store.js b/src/store.js index de03a2c9..180eba1f 100644 --- a/src/store.js +++ b/src/store.js @@ -33,7 +33,8 @@ export const __DEV_CONFIG_FILE__ = '__DEV_CONFIG__.json' export const store = reactive({ code: {}, config: { - template: '' + template: '', + config_lib: '' } }) @@ -50,15 +51,25 @@ export function saveConfig(key, value) { } // render the code if there are fetched files for current selected template -export async function genCode() { +export function genCode() { const currentFiles = files[store.config.template] if (currentFiles && Object.keys(currentFiles).length) { for (const file in currentFiles) { + if (store.config.config_lib === 'hydra' && file === 'config.json') { + delete store.code[file] + continue + } else if ( + store.config.config_lib === 'argparse' && + (file === 'config.yaml' || file === 'config.yml') + ) { + delete store.code[file] + continue + } store.code[file] = ejs.render(currentFiles[file], store.config) } - } - if (isDev) { - store.code[__DEV_CONFIG_FILE__] = JSON.stringify(store.config, null, 2) + if (isDev) { + store.code[__DEV_CONFIG_FILE__] = JSON.stringify(store.config, null, 2) + } } } diff --git a/src/templates/template-common/utils.py b/src/templates/template-common/utils.py index 7f7841d2..f355f538 100644 --- a/src/templates/template-common/utils.py +++ b/src/templates/template-common/utils.py @@ -1,5 +1,4 @@ import logging -from argparse import ArgumentParser from datetime import datetime from logging import Logger from pathlib import Path @@ -18,14 +17,6 @@ from ignite.utils import setup_logger -def get_default_parser(config): - parser = ArgumentParser(add_help=False) - for key, value in config.items(): - parser.add_argument(f"--{key}", default=value) - - return parser - - def log_metrics(engine: Engine, tag: str) -> None: """Log `engine.state.metrics` with given `engine` and `tag`. @@ -97,7 +88,7 @@ def setup_output_dir(config: Any, rank: int): path.mkdir(parents=True, exist_ok=True) config.output_dir = path.as_posix() - return Path(idist.broadcast(config.output_dir, src=0)) + return idist.broadcast(config.output_dir, src=0) def setup_logging(config: Any) -> Logger: @@ -138,7 +129,9 @@ def setup_handlers( #::: if (it.save_training || it.save_evaluation) { :::# # checkpointing - saver = DiskSaver(config.output_dir / "checkpoints", require_empty=False) + saver = DiskSaver( + Path(config.output_dir) / "checkpoints", require_empty=False + ) #::: if (it.save_training) { :::# ckpt_handler_train = Checkpoint( to_save_train, @@ -249,3 +242,20 @@ def setup_exp_logging(config, trainer, optimizers, evaluators): #::: } :::# + +#::: if (it.config_lib === 'argparse') { :::# +def get_default_parser(): + import json + from argparse import ArgumentParser + + with open("config.json", "r") as f: + config = json.load(f) + + parser = ArgumentParser(add_help=False) + for key, value in config.items(): + parser.add_argument(f"--{key}", default=value) + + return parser + + +#::: } :::# diff --git a/src/templates/template-vision-classification/main.py b/src/templates/template-vision-classification/main.py index 87c64e0d..a0098200 100644 --- a/src/templates/template-vision-classification/main.py +++ b/src/templates/template-vision-classification/main.py @@ -2,13 +2,16 @@ from pprint import pformat from typing import Any +#::: if (it.config_lib) { :::# +import hydra + +#::: } :::# import ignite.distributed as idist from data import setup_data from ignite.engine import Events from ignite.metrics import Accuracy, Loss from ignite.utils import manual_seed from model import Net -from omegaconf import OmegaConf from torch import nn, optim from torch.utils.data.distributed import DistributedSampler from trainers import setup_evaluator, setup_trainer @@ -51,7 +54,6 @@ def run(local_rank: int, config: Any): # print training configurations logger = setup_logging(config) logger.info("Configuration: %s", pformat(vars(config))) - OmegaConf.save(config, config.output_dir / "dumped-config.yaml") trainer.logger = evaluator.logger = logger # set epoch for distributed sampler @@ -129,11 +131,34 @@ def _(): # main +#::: if (it.config_lib === 'hydra') { :::# +@hydra.main(config_name="config") +def main(config): + #::: } :::# + #::: if (it.dist === 'spawn') { :::# + #::: if (it.nproc_per_node && it.nnodes && it.master_addr && it.master_port) { :::# + kwargs = { + "nproc_per_node": config.nproc_per_node, + "nnodes": config.nnodes, + "master_addr": config.master_addr, + "master_port": config.master_port, + } + #::: } else if (it.nproc_per_node) { :::# + kwargs = {"nproc_per_node": config.nproc_per_node} + #::: } :::# + with idist.Parallel(config.backend, **kwargs) as p: + p.run(run, config=config) + #::: } else { :::# + with idist.Parallel(config.backend) as p: + p.run(run, config=config) + #::: } :::# + + +#::: if (it.config_lib === 'argparse') { :::# def main(): - config = OmegaConf.load("./config.yaml") - parser = ArgumentParser(parents=[get_default_parser(config)]) + #::: } :::# + parser = ArgumentParser(parents=[get_default_parser()]) config = parser.parse_args() - #::: if (it.dist === 'spawn') { :::# #::: if (it.nproc_per_node && it.nnodes && it.master_addr && it.master_port) { :::# kwargs = { diff --git a/src/templates/template-vision-classification/requirements.txt b/src/templates/template-vision-classification/requirements.txt index 1d41793c..70984cdb 100644 --- a/src/templates/template-vision-classification/requirements.txt +++ b/src/templates/template-vision-classification/requirements.txt @@ -1,3 +1,7 @@ torch>=1.8.0 +torchvision>=0.9.0 pytorch-ignite>=0.4.4 -omegaconf + +#::: if (it.config_lib === 'hydra') { :::# +hydra-core>=1.0.0 +#::: } :::# diff --git a/src/templates/template-vision-classification/utils.py b/src/templates/template-vision-classification/utils.py index 7f7841d2..f355f538 100644 --- a/src/templates/template-vision-classification/utils.py +++ b/src/templates/template-vision-classification/utils.py @@ -1,5 +1,4 @@ import logging -from argparse import ArgumentParser from datetime import datetime from logging import Logger from pathlib import Path @@ -18,14 +17,6 @@ from ignite.utils import setup_logger -def get_default_parser(config): - parser = ArgumentParser(add_help=False) - for key, value in config.items(): - parser.add_argument(f"--{key}", default=value) - - return parser - - def log_metrics(engine: Engine, tag: str) -> None: """Log `engine.state.metrics` with given `engine` and `tag`. @@ -97,7 +88,7 @@ def setup_output_dir(config: Any, rank: int): path.mkdir(parents=True, exist_ok=True) config.output_dir = path.as_posix() - return Path(idist.broadcast(config.output_dir, src=0)) + return idist.broadcast(config.output_dir, src=0) def setup_logging(config: Any) -> Logger: @@ -138,7 +129,9 @@ def setup_handlers( #::: if (it.save_training || it.save_evaluation) { :::# # checkpointing - saver = DiskSaver(config.output_dir / "checkpoints", require_empty=False) + saver = DiskSaver( + Path(config.output_dir) / "checkpoints", require_empty=False + ) #::: if (it.save_training) { :::# ckpt_handler_train = Checkpoint( to_save_train, @@ -249,3 +242,20 @@ def setup_exp_logging(config, trainer, optimizers, evaluators): #::: } :::# + +#::: if (it.config_lib === 'argparse') { :::# +def get_default_parser(): + import json + from argparse import ArgumentParser + + with open("config.json", "r") as f: + config = json.load(f) + + parser = ArgumentParser(add_help=False) + for key, value in config.items(): + parser.add_argument(f"--{key}", default=value) + + return parser + + +#::: } :::# diff --git a/src/templates/templates.json b/src/templates/templates.json index 582f71cd..1eabf0e6 100644 --- a/src/templates/templates.json +++ b/src/templates/templates.json @@ -1,6 +1,7 @@ { "template-vision-classification": { "README.md": "README.md", + "config.json": "config.json", "config.yaml": "config.yaml", "data.py": "data.py", "main.py": "main.py", From b55ed3a17f0d868e2767f64dc44458f48f022a4e Mon Sep 17 00:00:00 2001 From: ydcjeff <32727188+ydcjeff@users.noreply.github.com> Date: Sat, 15 May 2021 23:22:32 +0630 Subject: [PATCH 5/8] fix: merge config in json file --- src/components/NavBar.vue | 8 +++++++- src/store.js | 8 +++++--- src/templates/template-vision-classification/config.json | 2 -- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index 0d70df3e..dcec6687 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -101,9 +101,15 @@ export default { const downloadProject = () => { const zip = new JSZip() if (store.code && Object.keys(store.code).length) { - if (!store.config.output_dir) { + if (!store.config.config_lib) { + msg.showMsg = true + msg.content = `Configuration Library Type is required. Please choose in Templates tab.` + } else if (!store.config.output_dir) { msg.showMsg = true msg.content = `Output directory is required. Please input in Loggers tab.` + } else if (!store.config.log_every_iters) { + msg.showMsg = true + msg.content = `Logging interval is required. Please input in Loggers tab.` } else { for (const filename in store.code) { zip.file(filename, store.code[filename]) diff --git a/src/store.js b/src/store.js index da4be935..e647985c 100644 --- a/src/store.js +++ b/src/store.js @@ -62,17 +62,19 @@ export function genCode() { const currentFiles = files[store.config.template] if (currentFiles && Object.keys(currentFiles).length) { for (const file in currentFiles) { + store.code[file] = ejs.render(currentFiles[file], store.config) + if (file === 'config.json') { + const json = JSON.parse(currentFiles[file]) + store.code[file] = JSON.stringify({ ...json, ...store.config }, null, 2) + } if (store.config.config_lib === 'hydra' && file === 'config.json') { delete store.code[file] - continue } else if ( store.config.config_lib === 'argparse' && (file === 'config.yaml' || file === 'config.yml') ) { delete store.code[file] - continue } - store.code[file] = ejs.render(currentFiles[file], store.config) } if (isDev) { store.code[__DEV_CONFIG_FILE__] = JSON.stringify(store.config, null, 2) diff --git a/src/templates/template-vision-classification/config.json b/src/templates/template-vision-classification/config.json index 640a5789..8604c9f8 100644 --- a/src/templates/template-vision-classification/config.json +++ b/src/templates/template-vision-classification/config.json @@ -8,9 +8,7 @@ "max_epochs": 2, "train_epoch_length": 4, "eval_epoch_length": 4, - "log_every_iters": 2, "lr": 1e-3, - "resume_from": null, "use_amp": false, "verbose": false } From 51053e77973e453eb11a768d3b7a636690069220 Mon Sep 17 00:00:00 2001 From: ydcjeff <32727188+ydcjeff@users.noreply.github.com> Date: Sun, 16 May 2021 23:29:25 +0630 Subject: [PATCH 6/8] fix!: keep yaml/hydra combo, add logger pkg in requirements.txt remove json/argparse combo since json.load will result an error for plain `int` and argparse will fail to call string "int" for given inputs. --- src/components/NavBar.vue | 5 +- src/components/TabTemplates.vue | 12 +--- src/metadata/metadata.json | 16 +---- src/store.js | 19 ++---- src/templates/template-common/utils.py | 23 ++----- .../config.json | 14 ----- .../template-vision-classification/data.py | 13 +++- .../template-vision-classification/main.py | 60 ++++++++----------- .../requirements.txt | 11 +++- .../template-vision-classification/utils.py | 23 ++----- src/templates/templates.json | 1 - 11 files changed, 64 insertions(+), 133 deletions(-) delete mode 100644 src/templates/template-vision-classification/config.json diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index 72be58c2..282cd3d0 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -102,10 +102,7 @@ export default { const zip = new JSZip() if (store.code && Object.keys(store.code).length) { msg.color = '#ff0000' - if (!store.config.config_lib) { - msg.showMsg = true - msg.content = `Configuration Library Type is required. Please choose in Templates tab.` - } else if (!store.config.output_dir) { + if (!store.config.output_dir) { msg.showMsg = true msg.content = `Output directory is required. Please input in Loggers tab.` } else if (!store.config.log_every_iters) { diff --git a/src/components/TabTemplates.vue b/src/components/TabTemplates.vue index d0383e3f..c6f298f8 100644 --- a/src/components/TabTemplates.vue +++ b/src/components/TabTemplates.vue @@ -10,32 +10,26 @@ @change.prevent="downloadTemplates" /> -

Choose Configuration Library and File

-