diff --git a/supervisely_integration/train/aug_templates/hard.json b/supervisely_integration/train/aug_templates/hard.json deleted file mode 100644 index 354ac4c8..00000000 --- a/supervisely_integration/train/aug_templates/hard.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "pipeline": [ - { - "category": "color", - "name": "MultiplyAndAddToBrightness", - "params": { - "mul": [ - 0.5, - 1.5 - ], - "add": [ - -80, - 80 - ], - "to_colorspace": "YCrCb", - "from_colorspace": "RGB", - "random_order": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.color.MultiplyAndAddToBrightness(mul=(0.5, 1.5), add=(-80, 80), to_colorspace='YCrCb', from_colorspace='RGB', random_order=True))" - }, - { - "category": "contrast", - "name": "GammaContrast", - "params": { - "gamma": [ - 0.5, - 2.0 - ], - "per_channel": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.contrast.GammaContrast(gamma=(0.5, 2.0), per_channel=True))" - }, - { - "category": "geometric", - "name": "Affine", - "params": { - "scale": [ - 0.6, - 1.6 - ], - "translate_percent": { - "x": [-0.3, 0.3], - "y": [-0.3, 0.3] - }, - "rotate": [ - -90, - 90 - ], - "shear": { - "x": [-45, 45], - "y": [-45, 45] - }, - "order": 1, - "cval": 0, - "mode": "constant", - "fit_output": false - }, - "sometimes": 0.7, - "python": "iaa.Sometimes(0.7, iaa.geometric.Affine(scale=(0.6, 1.6), translate_percent={'x': (-0.3, 0.3), 'y': (-0.3, 0.3)}, rotate=(-90, 90), shear={'x': (-45, 45), 'y': (-45, 45)}, order=1, cval=0, mode='constant', fit_output=False))" - }, - { - "category": "size", - "name": "CropAndPad", - "params": { - "percent": [ - -0.3, - 0.3 - ], - "pad_mode": "constant", - "pad_cval": 0, - "keep_size": false, - "sample_independently": true - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.size.CropAndPad(percent=(-0.3, 0.3), pad_mode='constant', pad_cval=0, keep_size=False, sample_independently=True))" - }, - { - "category": "flip", - "name": "Fliplr", - "params": { - "p": 1.0 - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.flip.Fliplr(p=1.0))" - }, - { - "category": "flip", - "name": "Flipud", - "params": { - "p": 1.0 - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.flip.Flipud(p=1.0))" - } - ], - "random_order": false -} \ No newline at end of file diff --git a/supervisely_integration/train/aug_templates/hard_corrupt.json b/supervisely_integration/train/aug_templates/hard_corrupt.json deleted file mode 100644 index cb81b0a8..00000000 --- a/supervisely_integration/train/aug_templates/hard_corrupt.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "pipeline": [ - { - "category": "color", - "name": "MultiplyAndAddToBrightness", - "params": { - "mul": [ - 0.5, - 1.5 - ], - "add": [ - -80, - 80 - ], - "to_colorspace": "YCrCb", - "from_colorspace": "RGB", - "random_order": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.color.MultiplyAndAddToBrightness(mul=(0.5, 1.5), add=(-80, 80), to_colorspace='YCrCb', from_colorspace='RGB', random_order=True))" - }, - { - "category": "contrast", - "name": "GammaContrast", - "params": { - "gamma": [ - 0.5, - 2.0 - ], - "per_channel": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.contrast.GammaContrast(gamma=(0.5, 2.0), per_channel=True))" - }, - { - "category": "geometric", - "name": "Affine", - "params": { - "scale": [ - 0.6, - 1.6 - ], - "translate_percent": { - "x": [-0.3, 0.3], - "y": [-0.3, 0.3] - }, - "rotate": [ - -90, - 90 - ], - "shear": { - "x": [-45, 45], - "y": [-45, 45] - }, - "order": 1, - "cval": 0, - "mode": "constant", - "fit_output": false - }, - "sometimes": 0.7, - "python": "iaa.Sometimes(0.7, iaa.geometric.Affine(scale=(0.6, 1.6), translate_percent={'x': (-0.3, 0.3), 'y': (-0.3, 0.3)}, rotate=(-90, 90), shear={'x': (-45, 45), 'y': (-45, 45)}, order=1, cval=0, mode='constant', fit_output=False))" - }, - { - "category": "size", - "name": "CropAndPad", - "params": { - "percent": [ - -0.3, - 0.3 - ], - "pad_mode": "constant", - "pad_cval": 0, - "keep_size": false, - "sample_independently": true - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.size.CropAndPad(percent=(-0.3, 0.3), pad_mode='constant', pad_cval=0, keep_size=False, sample_independently=True))" - }, - { - "category": "flip", - "name": "Fliplr", - "params": { - "p": 1.0 - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.flip.Fliplr(p=1.0))" - }, - { - "category": "flip", - "name": "Flipud", - "params": { - "p": 1.0 - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.flip.Flipud(p=1.0))" - }, - { - "category": "blur", - "name": "GaussianBlur", - "params": { - "sigma": [ - 0, - 3 - ] - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.blur.GaussianBlur(sigma=(0, 3)))" - }, - { - "category": "arithmetic", - "name": "JpegCompression", - "params": { - "compression": [ - 5, - 20 - ] - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.arithmetic.JpegCompression(compression=(5, 20)))" - }, - { - "category": "arithmetic", - "name": "AdditiveGaussianNoise", - "params": { - "loc": 0, - "scale": [ - 1, - 15 - ], - "per_channel": false - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.arithmetic.AdditiveGaussianNoise(loc=0, scale=(1, 15), per_channel=False))" - }, - { - "category": "arithmetic", - "name": "SaltAndPepper", - "params": { - "p": [ - 0, - 0.4 - ], - "per_channel": false - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.arithmetic.SaltAndPepper(p=(0, 0.4), per_channel=False))" - } - ], - "random_order": false -} \ No newline at end of file diff --git a/supervisely_integration/train/aug_templates/light.json b/supervisely_integration/train/aug_templates/light.json deleted file mode 100644 index 4aa7edc9..00000000 --- a/supervisely_integration/train/aug_templates/light.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "pipeline": [ - { - "category": "color", - "name": "MultiplyAndAddToBrightness", - "params": { - "mul": [ - 0.8, - 1.2 - ], - "add": [ - -30, - 30 - ], - "to_colorspace": "YCrCb", - "from_colorspace": "RGB", - "random_order": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.color.MultiplyAndAddToBrightness(mul=(0.8, 1.2), add=(-30, 30), to_colorspace='YCrCb', from_colorspace='RGB', random_order=True))" - }, - { - "category": "contrast", - "name": "GammaContrast", - "params": { - "gamma": [ - 0.8, - 1.2 - ], - "per_channel": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.contrast.GammaContrast(gamma=(0.8, 1.2), per_channel=True))" - }, - { - "category": "geometric", - "name": "Affine", - "params": { - "scale": [ - 0.8, - 1.2 - ], - "translate_percent": { - "x": [-0.1, 0.1], - "y": [-0.1, 0.1] - }, - "rotate": [ - -30, - 30 - ], - "shear": { - "x": [-15, 15], - "y": [-15, 15] - }, - "order": 1, - "cval": 0, - "mode": "constant", - "fit_output": false - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.geometric.Affine(scale=(0.8, 1.2), translate_percent={'x': (-0.1, 0.1), 'y': (-0.1, 0.1)}, rotate=(-30, 30), shear={'x': (-15, 15), 'y': (-15, 15)}, order=1, cval=0, mode='constant', fit_output=False))" - }, - { - "category": "size", - "name": "CropAndPad", - "params": { - "percent": [ - -0.1, - 0.1 - ], - "pad_mode": "constant", - "pad_cval": 0, - "keep_size": false, - "sample_independently": true - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.size.CropAndPad(percent=(-0.1, 0.1), pad_mode='constant', pad_cval=0, keep_size=False, sample_independently=True))" - } - ], - "random_order": false -} \ No newline at end of file diff --git a/supervisely_integration/train/aug_templates/light_corrupt.json b/supervisely_integration/train/aug_templates/light_corrupt.json deleted file mode 100644 index 72cb37a3..00000000 --- a/supervisely_integration/train/aug_templates/light_corrupt.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "pipeline": [ - { - "category": "color", - "name": "MultiplyAndAddToBrightness", - "params": { - "mul": [ - 0.8, - 1.2 - ], - "add": [ - -30, - 30 - ], - "to_colorspace": "YCrCb", - "from_colorspace": "RGB", - "random_order": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.color.MultiplyAndAddToBrightness(mul=(0.8, 1.2), add=(-30, 30), to_colorspace='YCrCb', from_colorspace='RGB', random_order=True))" - }, - { - "category": "contrast", - "name": "GammaContrast", - "params": { - "gamma": [ - 0.8, - 1.2 - ], - "per_channel": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.contrast.GammaContrast(gamma=(0.8, 1.2), per_channel=True))" - }, - { - "category": "geometric", - "name": "Affine", - "params": { - "scale": [ - 0.8, - 1.2 - ], - "translate_percent": { - "x": [-0.1, 0.1], - "y": [-0.1, 0.1] - }, - "rotate": [ - -30, - 30 - ], - "shear": { - "x": [-15, 15], - "y": [-15, 15] - }, - "order": 1, - "cval": 0, - "mode": "constant", - "fit_output": false - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.geometric.Affine(scale=(0.8, 1.2), translate_percent={'x': (-0.1, 0.1), 'y': (-0.1, 0.1)}, rotate=(-30, 30), shear={'x': (-15, 15), 'y': (-15, 15)}, order=1, cval=0, mode='constant', fit_output=False))" - }, - { - "category": "size", - "name": "CropAndPad", - "params": { - "percent": [ - -0.1, - 0.1 - ], - "pad_mode": "constant", - "pad_cval": 0, - "keep_size": false, - "sample_independently": true - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.size.CropAndPad(percent=(-0.1, 0.1), pad_mode='constant', pad_cval=0, keep_size=False, sample_independently=True))" - }, - { - "category": "blur", - "name": "GaussianBlur", - "params": { - "sigma": [ - 0, - 3 - ] - }, - "sometimes": 0.2, - "python": "iaa.Sometimes(0.2, iaa.blur.GaussianBlur(sigma=(0, 3)))" - }, - { - "category": "arithmetic", - "name": "JpegCompression", - "params": { - "compression": [ - 0, - 10 - ] - }, - "sometimes": 0.2, - "python": "iaa.Sometimes(0.2, iaa.arithmetic.JpegCompression(compression=(0, 10)))" - }, - { - "category": "arithmetic", - "name": "AdditiveGaussianNoise", - "params": { - "loc": 0, - "scale": [ - 1, - 5 - ], - "per_channel": false - }, - "sometimes": 0.2, - "python": "iaa.Sometimes(0.2, iaa.arithmetic.AdditiveGaussianNoise(loc=0, scale=(1, 5), per_channel=False))" - }, - { - "category": "arithmetic", - "name": "SaltAndPepper", - "params": { - "p": [ - 0, - 0.2 - ], - "per_channel": false - }, - "sometimes": 0.2, - "python": "iaa.Sometimes(0.2, iaa.arithmetic.SaltAndPepper(p=(0, 0.2), per_channel=False))" - } - ], - "random_order": false -} \ No newline at end of file diff --git a/supervisely_integration/train/aug_templates/medium.json b/supervisely_integration/train/aug_templates/medium.json deleted file mode 100644 index 1ae46bcb..00000000 --- a/supervisely_integration/train/aug_templates/medium.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "pipeline": [ - { - "category": "color", - "name": "MultiplyAndAddToBrightness", - "params": { - "mul": [ - 0.7, - 1.3 - ], - "add": [ - -50, - 50 - ], - "to_colorspace": "YCrCb", - "from_colorspace": "RGB", - "random_order": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.color.MultiplyAndAddToBrightness(mul=(0.7, 1.3), add=(-50, 50), to_colorspace='YCrCb', from_colorspace='RGB', random_order=True))" - }, - { - "category": "contrast", - "name": "GammaContrast", - "params": { - "gamma": [ - 0.7, - 1.5 - ], - "per_channel": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.contrast.GammaContrast(gamma=(0.7, 1.5), per_channel=True))" - }, - { - "category": "geometric", - "name": "Affine", - "params": { - "scale": [ - 0.7, - 1.4 - ], - "translate_percent": { - "x": [-0.2, 0.2], - "y": [-0.2, 0.2] - }, - "rotate": [ - -60, - 60 - ], - "shear": { - "x": [-30, 30], - "y": [-30, 30] - }, - "order": 1, - "cval": 0, - "mode": "constant", - "fit_output": false - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.geometric.Affine(scale=(0.7, 1.4), translate_percent={'x': (-0.2, 0.2), 'y': (-0.2, 0.2)}, rotate=(-60, 60), shear={'x': (-30, 30), 'y': (-30, 30)}, order=1, cval=0, mode='constant', fit_output=False))" - }, - { - "category": "size", - "name": "CropAndPad", - "params": { - "percent": [ - -0.2, - 0.2 - ], - "pad_mode": "constant", - "pad_cval": 0, - "keep_size": false, - "sample_independently": true - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.size.CropAndPad(percent=(-0.2, 0.2), pad_mode='constant', pad_cval=0, keep_size=False, sample_independently=True))" - }, - { - "category": "flip", - "name": "Fliplr", - "params": { - "p": 1.0 - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.flip.Fliplr(p=1.0))" - } - ], - "random_order": false -} \ No newline at end of file diff --git a/supervisely_integration/train/aug_templates/medium_corrupt.json b/supervisely_integration/train/aug_templates/medium_corrupt.json deleted file mode 100644 index 23d66a1a..00000000 --- a/supervisely_integration/train/aug_templates/medium_corrupt.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "pipeline": [ - { - "category": "color", - "name": "MultiplyAndAddToBrightness", - "params": { - "mul": [ - 0.7, - 1.3 - ], - "add": [ - -50, - 50 - ], - "to_colorspace": "YCrCb", - "from_colorspace": "RGB", - "random_order": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.color.MultiplyAndAddToBrightness(mul=(0.7, 1.3), add=(-50, 50), to_colorspace='YCrCb', from_colorspace='RGB', random_order=True))" - }, - { - "category": "contrast", - "name": "GammaContrast", - "params": { - "gamma": [ - 0.7, - 1.5 - ], - "per_channel": true - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.contrast.GammaContrast(gamma=(0.7, 1.5), per_channel=True))" - }, - { - "category": "geometric", - "name": "Affine", - "params": { - "scale": [ - 0.7, - 1.4 - ], - "translate_percent": { - "x": [-0.2, 0.2], - "y": [-0.2, 0.2] - }, - "rotate": [ - -60, - 60 - ], - "shear": { - "x": [-30, 30], - "y": [-30, 30] - }, - "order": 1, - "cval": 0, - "mode": "constant", - "fit_output": false - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.geometric.Affine(scale=(0.7, 1.4), translate_percent={'x': (-0.2, 0.2), 'y': (-0.2, 0.2)}, rotate=(-60, 60), shear={'x': (-30, 30), 'y': (-30, 30)}, order=1, cval=0, mode='constant', fit_output=False))" - }, - { - "category": "size", - "name": "CropAndPad", - "params": { - "percent": [ - -0.2, - 0.2 - ], - "pad_mode": "constant", - "pad_cval": 0, - "keep_size": false, - "sample_independently": true - }, - "sometimes": 0.4, - "python": "iaa.Sometimes(0.4, iaa.size.CropAndPad(percent=(-0.2, 0.2), pad_mode='constant', pad_cval=0, keep_size=False, sample_independently=True))" - }, - { - "category": "flip", - "name": "Fliplr", - "params": { - "p": 1.0 - }, - "sometimes": 0.5, - "python": "iaa.Sometimes(0.5, iaa.flip.Fliplr(p=1.0))" - }, - { - "category": "blur", - "name": "GaussianBlur", - "params": { - "sigma": [ - 0, - 3 - ] - }, - "sometimes": 0.3, - "python": "iaa.Sometimes(0.3, iaa.blur.GaussianBlur(sigma=(0, 3)))" - }, - { - "category": "arithmetic", - "name": "JpegCompression", - "params": { - "compression": [ - 0, - 15 - ] - }, - "sometimes": 0.3, - "python": "iaa.Sometimes(0.3, iaa.arithmetic.JpegCompression(compression=(0, 15)))" - }, - { - "category": "arithmetic", - "name": "AdditiveGaussianNoise", - "params": { - "loc": 0, - "scale": [ - 1, - 20 - ], - "per_channel": false - }, - "sometimes": 0.3, - "python": "iaa.Sometimes(0.3, iaa.arithmetic.AdditiveGaussianNoise(loc=0, scale=(1, 20), per_channel=False))" - }, - { - "category": "arithmetic", - "name": "SaltAndPepper", - "params": { - "p": [ - 0, - 0.1 - ], - "per_channel": false - }, - "sometimes": 0.3, - "python": "iaa.Sometimes(0.3, iaa.arithmetic.SaltAndPepper(p=(0, 0.1), per_channel=False))" - } - ], - "random_order": false -} \ No newline at end of file diff --git a/supervisely_integration/train/visualize_scheduler.py b/supervisely_integration/train/scripts/visualize_scheduler.py similarity index 100% rename from supervisely_integration/train/visualize_scheduler.py rename to supervisely_integration/train/scripts/visualize_scheduler.py diff --git a/supervisely_integration/train/ui/augmentations.py b/supervisely_integration/train/ui/augmentations.py deleted file mode 100644 index 6a356354..00000000 --- a/supervisely_integration/train/ui/augmentations.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -from pathlib import Path - -from supervisely.app.widgets import AugmentationsWithTabs, Button, Card, Container, Switch - -import supervisely_integration.train.globals as g -import supervisely_integration.train.ui.parameters as parameters -import supervisely_integration.train.ui.utils as utils - - -def name_from_path(aug_path): - name = os.path.basename(aug_path).split(".json")[0].capitalize() - name = " + ".join(name.split("_")) - return name - - -template_dir = "supervisely_integration/train/aug_templates" -template_paths = list(map(str, Path(template_dir).glob("*.json"))) -template_paths = sorted(template_paths, key=lambda x: x.replace(".", "_"))[::-1] - -templates = [{"label": name_from_path(path), "value": path} for path in template_paths] - - -switcher = Switch(True) -augments = AugmentationsWithTabs(g, task_type="detection", templates=templates) - -select_augs_button = Button("Select") -container = Container([switcher, augments, select_augs_button]) - -card = Card( - title="Training augmentations", - description="Choose one of the prepared templates or provide custom pipeline", - content=container, - lock_message="Select splits to unlock", -) -card.lock("Confirm splits.") - - -def reset_widgets(): - if switcher.is_switched(): - augments.show() - else: - augments.hide() - - -def get_selected_aug(): - # path to aug pipline (.json file) - if switcher.is_switched(): - return augments._current_augs._template_path - else: - return None - - -@switcher.value_changed -def on_switch(is_switched: bool): - reset_widgets() - - -reset_widgets() - - -@select_augs_button.click -def splits_selected(): - if select_augs_button.text == "Select": - # widgets to disable - utils.disable_enable([switcher, augments, container, card], True) - - utils.update_custom_button_params(select_augs_button, utils.reselect_params) - g.update_step(step=6) - - # unlock - parameters.card.unlock() - else: - # lock - parameters.card.lock() - utils.update_custom_button_params(select_augs_button, utils.select_params) - - # widgets to enable - utils.disable_enable([switcher, augments, container, card], False) - g.update_step(back=True) diff --git a/supervisely_integration/train/ui/classes.py b/supervisely_integration/train/ui/classes.py deleted file mode 100644 index 8feb750f..00000000 --- a/supervisely_integration/train/ui/classes.py +++ /dev/null @@ -1,71 +0,0 @@ -from typing import Optional - -import supervisely as sly -from supervisely.app.widgets import Button, Card, ClassesTable, Container, Text - -import supervisely_integration.train.globals as g -import supervisely_integration.train.ui.augmentations as augmentations -import supervisely_integration.train.ui.parameters as parameters -import supervisely_integration.train.ui.splits as splits -import supervisely_integration.train.ui.utils as utils - -empty_notification = Text("Please, select at least one class.", status="warning") -train_classes_selector = ClassesTable(project_id=g.PROJECT_ID) -train_classes_selector.hide() - -select_classes_button = Button("Select") - -card = Card( - title="Training classes", - description="Select classes to train the model on", - content=Container([train_classes_selector, select_classes_button]), - lock_message="Select model to unlock", -) -card.lock() - - -def fill_classes_selector(clear: Optional[bool] = False): - if not clear: - train_classes_selector.set_project_meta(g.project_meta) - train_classes_selector.select_all() - train_classes_selector.show() - else: - train_classes_selector.set_project_meta(sly.ProjectMeta()) - train_classes_selector.hide() - - -@select_classes_button.click -def classes_selected(): - if select_classes_button.text == "Select": - # widgets to disable - utils.disable_enable([empty_notification, train_classes_selector, card], True) - - selected_classes = train_classes_selector.get_selected_classes() - if not selected_classes: - return - g.selected_classes = selected_classes - sly.logger.info(f"Selected classes: {selected_classes}") - - utils.update_custom_button_params(select_classes_button, utils.reselect_params) - g.update_step() - - # unlock - splits.card.unlock() - else: - g.selected_classes = None - - # lock - splits.card.lock() - utils.update_custom_button_params(splits.select_splits_button, utils.select_params) - - augmentations.card.lock() - utils.update_custom_button_params(augmentations.select_augs_button, utils.select_params) - - parameters.card.lock() - # utils.update_custom_button_params(parameters.run_training, utils.select_params) - - utils.update_custom_button_params(select_classes_button, utils.select_params) - - # widgets to enable - utils.disable_enable([empty_notification, train_classes_selector, card], False) - g.update_step(back=True) diff --git a/supervisely_integration/train/ui/globalsb.py b/supervisely_integration/train/ui/globalsb.py deleted file mode 100644 index a01b6083..00000000 --- a/supervisely_integration/train/ui/globalsb.py +++ /dev/null @@ -1,126 +0,0 @@ -import os -import sys -from typing import Optional - -from dotenv import load_dotenv - -import supervisely as sly -from rtdetr_pytorch.model_list import _models -from supervisely.nn.artifacts.rtdetr import RTDETR - -if sly.is_development: - load_dotenv("local.env") - load_dotenv(os.path.expanduser("~/supervisely.env")) - -# region constants -cwd = os.getcwd() -sly.logger.debug(f"Current working directory: {cwd}") -rtdetr_pytorch_path = os.path.join(cwd, "rtdetr_pytorch") -sys.path.insert(0, rtdetr_pytorch_path) -sly.logger.debug("Added rtdetr_pytorch to the system path") -CONFIG_PATHS_DIR = os.path.join(rtdetr_pytorch_path, "configs", "rtdetr") -default_config_path = os.path.join(CONFIG_PATHS_DIR, "placeholder.yml") -CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) -sly.logger.debug(f"Current directory: {CURRENT_DIR}") -TEMP_DIR = os.path.join(CURRENT_DIR, "temp") -DOWNLOAD_DIR = os.path.join(TEMP_DIR, "download") -CONVERTED_DIR = os.path.join(TEMP_DIR, "converted") -sly.fs.mkdir(DOWNLOAD_DIR, remove_content_if_exists=True) -sly.fs.mkdir(CONVERTED_DIR, remove_content_if_exists=True) -sly.logger.debug(f"Download dir: {DOWNLOAD_DIR}, converted dir: {CONVERTED_DIR}") -OUTPUT_DIR = os.path.join(TEMP_DIR, "output") -sly.fs.mkdir(OUTPUT_DIR, remove_content_if_exists=True) -sly.logger.debug(f"Output dir: {OUTPUT_DIR}") -AUG_TEMPLATES_DIR = os.path.join(CURRENT_DIR, "aug_templates") - -data_dir = os.path.join(CURRENT_DIR, "data") -sly.fs.mkdir(data_dir, remove_content_if_exists=True) - -MODEL_MODES = ["Pretrained models", "Custom weights"] -TABLE_COLUMNS = [ - "Name", - "Dataset", - "AP_Val", - "Params(M)", - "FRPS(T4)", -] -PRETRAINED_MODELS = [ - [value for key, value in model_info.items() if key != "meta"] for model_info in _models -] -OPTIMIZERS = ["Adam", "AdamW", "SGD"] -SCHEDULERS = [ - "Without scheduler", - "CosineAnnealingLR", - "LinearLR", - "MultiStepLR", - "OneCycleLR", -] - -# endregion - -# region envvars -TASK_ID = sly.env.task_id() -TEAM_ID = sly.env.team_id() -WORKSPACE_ID = sly.env.workspace_id() -PROJECT_ID = sly.env.project_id() - -USE_CACHE = True -STOP_TRAINING = False - -rtdetr_artifacts = RTDETR(TEAM_ID) - -# endregion -api = sly.Api.from_env() -augs = [] -# region state -team = api.team.get_info_by_id(TEAM_ID) -stepper = None -project_info = None -project_meta = None -project_dir = None -project = None -converted_project = None -train_dataset_path = None -val_dataset_path = None -custom_config_path = None -train_mode = None -selected_classes = None -splits = None -widgets = None -# endregion - -model_mode = None -best_checkpoint_path = None -latest_checkpoint_name = "last.pth" -latest_checkpoint_path = None - -app = None - - -def update_step(back: Optional[bool] = False, step: Optional[int] = None) -> None: - if step is None: - current_step = stepper.get_active_step() - sly.logger.debug(f"Current step: {current_step}") - step = current_step - 1 if back else current_step + 1 - sly.logger.debug(f"Next step: {step}") - stepper.set_active_step(step) - - -def read_augs(): - aug_files = sly.fs.list_files( - AUG_TEMPLATES_DIR, valid_extensions=[".json"], ignore_valid_extensions_case=True - ) - sly.logger.debug(f"Found {len(aug_files)} augmentation templates") - - for aug_path in aug_files: - aug_name = sly.utils.camel_to_snake(sly.fs.get_file_name(aug_path)) - template = { - "label": aug_name, - "value": aug_path, - } - augs.append(template) - - sly.logger.debug(f"Prepared {len(augs)} augmentation templates") - - -read_augs() diff --git a/supervisely_integration/train/ui/input.py b/supervisely_integration/train/ui/input.py deleted file mode 100644 index f3e15216..00000000 --- a/supervisely_integration/train/ui/input.py +++ /dev/null @@ -1,24 +0,0 @@ -import supervisely as sly -from supervisely.app.widgets import Card, Checkbox, Container, ProjectThumbnail, Text -from supervisely.project.download import is_cached - -import supervisely_integration.train.globals as g - -g.project_info = g.api.project.get_info_by_id(g.PROJECT_ID) -g.project_meta = sly.ProjectMeta.from_json(g.api.project.get_meta(g.PROJECT_ID)) -sly.logger.info("Project meta saved into globals.") -sly.logger.info(f"Selected project: {g.project_info.name} with ID: {g.project_info.id}") -project_thumbnail = ProjectThumbnail(g.project_info) - -if is_cached(g.PROJECT_ID): - _text = "Use cached data stored on the agent to optimize project download" -else: - _text = "Cache data on the agent to optimize project download for future trainings" -use_cache_text = Text(_text) -use_cache_checkbox = Checkbox(use_cache_text, checked=g.USE_CACHE) - -card = Card( - title="Selected project", - description="The project that will be used for training.", - content=Container(widgets=[project_thumbnail, use_cache_checkbox]), -) diff --git a/supervisely_integration/train/ui/mainbp.py b/supervisely_integration/train/ui/mainbp.py deleted file mode 100644 index ec9c80b0..00000000 --- a/supervisely_integration/train/ui/mainbp.py +++ /dev/null @@ -1,28 +0,0 @@ -import supervisely as sly -from supervisely.app.widgets import Stepper - -import supervisely_integration.train.globals as g -import supervisely_integration.train.ui.augmentations as augmentations -import supervisely_integration.train.ui.classes as classes -import supervisely_integration.train.ui.input as input -import supervisely_integration.train.ui.model as model -import supervisely_integration.train.ui.output as output -import supervisely_integration.train.ui.parameters as parameters -import supervisely_integration.train.ui.splits as splits - -g.stepper = Stepper( - widgets=[ - input.card, - model.card, - classes.card, - splits.card, - augmentations.card, - parameters.card, - output.card, - ], - active_step=2, - widget_id="main_stepper", -) - -app = sly.Application(layout=g.stepper) -g.app = app diff --git a/supervisely_integration/train/ui/model.py b/supervisely_integration/train/ui/model.py deleted file mode 100644 index de3fbc86..00000000 --- a/supervisely_integration/train/ui/model.py +++ /dev/null @@ -1,123 +0,0 @@ -from collections import namedtuple -from typing import Dict, List, Optional - -import supervisely as sly -from supervisely.app.widgets import ( - Button, - Card, - Checkbox, - Container, - Field, - RadioTable, - RadioTabs, - TeamFilesSelector, -) - -import supervisely_integration.train.globals as g -import supervisely_integration.train.ui.augmentations as augmentations -import supervisely_integration.train.ui.classes as classes -import supervisely_integration.train.ui.parameters as parameters -import supervisely_integration.train.ui.splits as splits -import supervisely_integration.train.ui.utils as utils - -TrainMode = namedtuple("TrainMode", ["pretrained", "custom", "finetune"]) - - -select_custom_weights = TeamFilesSelector(g.TEAM_ID, selection_file_type="file") -pretrained_models_table = RadioTable(columns=g.TABLE_COLUMNS, rows=g.PRETRAINED_MODELS) - -finetune_checkbox = Checkbox("Fine-tune", True) -finetune_field = Field( - finetune_checkbox, - title="Enable fine-tuning", - description="Fine-tuning allows you to continue training a model from a checkpoint. If not selected, model will be trained from scratch and will be configured as in selected checkpoint.", -) - -select_model_button = Button("Select") - -model_mode = RadioTabs( - g.MODEL_MODES.copy(), - contents=[ - Container([pretrained_models_table, finetune_field]), - select_custom_weights, - ], -) - -card = Card( - title="Select a model", - description="Select a model to train", - content=Container([model_mode, select_model_button]), - lock_message="Click on the Change model button to select another model", -) - - -@select_model_button.click -def model_selected(): - if select_model_button.text == "Select": - # widgets to disable - utils.disable_enable( - [ - select_custom_weights, - pretrained_models_table, - finetune_checkbox, - finetune_field, - model_mode, - card, - ], - True, - ) - - mode = model_mode.get_active_tab() - g.model_mode = mode - if mode == g.MODEL_MODES[0]: - pretrained: List[str] = pretrained_models_table.get_selected_row() - custom = None - sly.logger.debug(f"Selected mode: {mode}, selected pretrained model: {pretrained}") - else: - pretrained = None - custom: List[str] = select_custom_weights.get_selected_paths() - # TODO: Add single-item mode to the widget and remove indexing - custom = custom[0] if custom else None - sly.logger.debug(f"Selected mode: {mode}, path to custom weights: {custom}") - finetune = finetune_checkbox.is_checked() - g.train_mode = TrainMode(pretrained, custom, finetune) - classes.fill_classes_selector() - - utils.update_custom_button_params(select_model_button, utils.reselect_params) - g.update_step() - - # unlock - classes.card.unlock() - else: - g.train_mode = None - g.model_mode = None - classes.fill_classes_selector(clear=True) - - # lock - classes.card.lock() - utils.update_custom_button_params(classes.select_classes_button, utils.select_params) - - splits.card.lock() - utils.update_custom_button_params(splits.select_splits_button, utils.select_params) - - augmentations.card.lock() - utils.update_custom_button_params(augmentations.select_augs_button, utils.select_params) - - parameters.card.lock() - # utils.update_custom_button_params(parameters.run_training, utils.select_params) - - utils.update_custom_button_params(select_model_button, utils.select_params) - - # widgets to enable - utils.disable_enable( - [ - select_custom_weights, - pretrained_models_table, - finetune_checkbox, - finetune_field, - model_mode, - card, - ], - False, - ) - g.update_step(back=True) diff --git a/supervisely_integration/train/ui/model_benchmark.py b/supervisely_integration/train/ui/model_benchmark.py deleted file mode 100644 index e3d348bd..00000000 --- a/supervisely_integration/train/ui/model_benchmark.py +++ /dev/null @@ -1,210 +0,0 @@ -import os -from typing import Dict, List - -import torch - -import supervisely as sly -import supervisely_integration.train.globals as g -from supervisely import DatasetInfo, ProjectInfo -from supervisely.app.widgets import ( - Field, - Progress, - ReportThumbnail, - SlyTqdm, - TrainValSplits, -) -from supervisely.io.fs import get_file_name, get_file_name_with_ext -from supervisely.nn.benchmark import ObjectDetectionBenchmark -from supervisely.nn.inference import SessionJSON -from supervisely_integration.train.serve import RTDETRModelMB - - -def get_eval_results_dir_name(api: sly.Api, task_id: int, project_info: ProjectInfo) -> str: - task_info = api.task.get_info_by_id(task_id) - task_dir = f"{task_id}_{task_info['meta']['app']['name']}" - eval_res_dir = f"/model-benchmark/evaluation/{project_info.id}_{project_info.name}/{task_dir}/" - eval_res_dir = api.storage.get_free_dir_name(sly.env.team_id(), eval_res_dir) - return eval_res_dir - - -def run_model_benchmark( - api: sly.Api, - root_source_path: str, - local_artifacts_dir: str, - remote_weights_dir: str, - remote_config_path: str, - project_info: ProjectInfo, - dataset_infos: List[DatasetInfo], - ds_name_to_id: Dict[str, int], - train_val_split: TrainValSplits, - train_set: list, - val_set: list, - selected_classes: List[str], - use_speedtest: bool, - model_benchmark_report: ReportThumbnail, - creating_report_f: Field, - model_benchmark_pbar: SlyTqdm, - model_benchmark_pbar_secondary: Progress, -) -> bool: - model_benchmark_done = False - try: - best_filename = get_file_name_with_ext(g.best_checkpoint_path) - checkpoint_path = os.path.join(remote_weights_dir, best_filename) - - sly.logger.info(f"Creating the report for the best model: {best_filename!r}") - creating_report_f.show() - model_benchmark_pbar.show() - model_benchmark_pbar(message="Starting Model Benchmark evaluation...", total=1) - - repo_root_path = os.path.dirname(os.path.dirname(root_source_path)) - - # 0. Serve trained model - m = RTDETRModelMB( - model_dir=local_artifacts_dir + "/weights", - use_gui=False, - custom_inference_settings=os.path.join( - repo_root_path, "supervisely_integration", "serve", "inference_settings.yaml" - ), - ) - device = "cuda" if torch.cuda.is_available() else "cpu" - sly.logger.info(f"Using device: {device}") - - deploy_params = dict( - device=device, - runtime=sly.nn.inference.RuntimeType.PYTORCH, - model_source="Custom models", - task_type="object detection", - checkpoint_name=best_filename, - checkpoint_url=checkpoint_path, - config_url=remote_config_path, - ) - m._load_model(deploy_params) - m.serve() - # m.model.overrides["verbose"] = False - session = SessionJSON(api, session_url="http://localhost:8000") - sly.fs.remove_dir(g.data_dir + "/benchmark") - - # 1. Init benchmark (todo: auto-detect task type) - benchmark_dataset_ids = None - benchmark_images_ids = None - train_dataset_ids = None - train_images_ids = None - - split_method = train_val_split._content.get_active_tab() - - if split_method == "Based on datasets": - if hasattr(train_val_split._val_ds_select, "get_selected_ids"): - benchmark_dataset_ids = train_val_split._val_ds_select.get_selected_ids() - train_dataset_ids = train_val_split._train_ds_select.get_selected_ids() - else: - benchmark_dataset_ids = [ - ds_name_to_id[d] for d in train_val_split._val_ds_select.get_value() - ] - train_dataset_ids = [ - ds_name_to_id[d] for d in train_val_split._train_ds_select.get_value() - ] - else: - - def get_image_infos_by_split(split: list): - ds_infos_dict = {ds_info.name: ds_info for ds_info in dataset_infos} - image_names_per_dataset = {} - for item in split: - image_names_per_dataset.setdefault(item.dataset_name, []).append(item.name) - image_infos = [] - for ( - dataset_name, - image_names, - ) in image_names_per_dataset.items(): - if "/" in dataset_name: - dataset_name = dataset_name.split("/")[-1] - ds_info = ds_infos_dict[dataset_name] - image_infos.extend( - api.image.get_list( - ds_info.id, - filters=[ - { - "field": "name", - "operator": "in", - "value": image_names, - } - ], - ) - ) - return image_infos - - val_image_infos = get_image_infos_by_split(val_set) - train_image_infos = get_image_infos_by_split(train_set) - benchmark_images_ids = [img_info.id for img_info in val_image_infos] - train_images_ids = [img_info.id for img_info in train_image_infos] - - bm = ObjectDetectionBenchmark( - api, - project_info.id, - output_dir=g.data_dir + "/benchmark", - gt_dataset_ids=benchmark_dataset_ids, - gt_images_ids=benchmark_images_ids, - progress=model_benchmark_pbar, - progress_secondary=model_benchmark_pbar_secondary, - classes_whitelist=selected_classes, - ) - - train_info = { - "app_session_id": g.TASK_ID, - "train_dataset_ids": train_dataset_ids, - "train_images_ids": train_images_ids, - "images_count": len(train_set), - } - bm.train_info = train_info - - # 2. Run inference - bm.run_inference(session) - - # 3. Pull results from the server - gt_project_path, dt_project_path = bm.download_projects(save_images=False) - - # 4. Evaluate - bm._evaluate(gt_project_path, dt_project_path) - - # 5. Upload evaluation results - eval_res_dir = get_eval_results_dir_name(api, g.TASK_ID, project_info) - bm.upload_eval_results(eval_res_dir + "/evaluation/") - - # 6. Speed test - if use_speedtest: - bm.run_speedtest(session, project_info.id) - model_benchmark_pbar_secondary.hide() - bm.upload_speedtest_results(eval_res_dir + "/speedtest/") - - # 7. Prepare visualizations, report and upload - bm.visualize() - remote_dir = bm.upload_visualizations(eval_res_dir + "/visualizations/") - report = bm.upload_report_link(remote_dir) - - # 8. UI updates - benchmark_report_template = api.file.get_info_by_path( - sly.env.team_id(), remote_dir + "template.vue" - ) - model_benchmark_done = True - creating_report_f.hide() - model_benchmark_report.set(benchmark_report_template) - model_benchmark_report.show() - model_benchmark_pbar.hide() - sly.logger.info( - f"Predictions project name: {bm.dt_project_info.name}. Workspace_id: {bm.dt_project_info.workspace_id}" - ) - sly.logger.info( - f"Differences project name: {bm.diff_project_info.name}. Workspace_id: {bm.diff_project_info.workspace_id}" - ) - except Exception as e: - sly.logger.error(f"Model benchmark failed. {repr(e)}", exc_info=True) - creating_report_f.hide() - model_benchmark_pbar.hide() - model_benchmark_pbar_secondary.hide() - try: - if bm.dt_project_info: - api.project.remove(bm.dt_project_info.id) - if bm.diff_project_info: - api.project.remove(bm.diff_project_info.id) - except Exception as e2: - pass - return model_benchmark_done diff --git a/supervisely_integration/train/ui/output.py b/supervisely_integration/train/ui/output.py deleted file mode 100644 index 2a2e7a34..00000000 --- a/supervisely_integration/train/ui/output.py +++ /dev/null @@ -1,457 +0,0 @@ -import os -import shutil -from datetime import datetime -from typing import Any, Dict, List - -import numpy as np -import yaml -from pycocotools.coco import COCO - -import rtdetr_pytorch.train as train_cli -import supervisely as sly -import supervisely_integration.train.globals as g -import supervisely_integration.train.ui.classes as classes_ui -import supervisely_integration.train.ui.input as input_ui -import supervisely_integration.train.ui.parameters as parameters_ui -import supervisely_integration.train.ui.splits as splits_ui -import supervisely_integration.train.workflow as w -from supervisely.app.widgets import ( - Button, - Card, - Container, - DoneLabel, - Empty, - Field, - FolderThumbnail, - LineChart, - Progress, - ReportThumbnail, - SlyTqdm, -) -from supervisely.io.fs import get_file_name, get_file_name_with_ext -from supervisely_integration.train.ui.model_benchmark import run_model_benchmark -from supervisely_integration.train.ui.project_cached import download_project - -# TODO: Fix import, now it's causing error -# from rtdetr_pytorch.src.misc.sly_logger import Logs - -start_train_btn = Button("Train") -stop_train_btn = Button("Stop", button_type="danger") -stop_train_btn.hide() -stop_train_btn.disable() - -btn_container = Container( - [start_train_btn, stop_train_btn, Empty()], - "horizontal", - overflow="wrap", - fractions=[1, 1, 10], - gap=1, -) - -loss = LineChart("Loss", series=[{"name": "Loss", "data": []}]) -learning_rate = LineChart( - "Learning Rate", - series=[ - {"name": "lr0", "data": []}, - {"name": "lr1", "data": []}, - {"name": "lr2", "data": []}, - {"name": "lr3", "data": []}, - ], -) -cuda_memory = LineChart("CUDA Memory", series=[{"name": "Memory", "data": []}]) -validation_metrics = LineChart( - "Validation Metrics", - series=[ - {"name": "AP@IoU=0.50:0.95|maxDets=100", "data": []}, - {"name": "AP@IoU=0.50|maxDets=100", "data": []}, - {"name": "AP@IoU=0.75|maxDets=100", "data": []}, - {"name": "AR@IoU=0.50:0.95|maxDets=1", "data": []}, - {"name": "AR@IoU=0.50:0.95|maxDets=10", "data": []}, - {"name": "AR@IoU=0.50:0.95|maxDets=100", "data": []}, - ], -) - - -# charts_grid = Grid([loss, learning_rate, cuda_memory, validation_metrics], columns=2, gap=5) - -charts_grid = Container( - [ - Container([loss, learning_rate], direction="horizontal", widgets_style="display: null"), - Container( - [cuda_memory, validation_metrics], direction="horizontal", widgets_style="display: null" - ), - ] -) - -charts_grid_f = Field(charts_grid, "Training and validation metrics") -charts_grid_f.hide() - -progress_bar_download_project = Progress() -progress_bar_prepare_project = Progress() -progress_bar_download_model = Progress() -progress_bar_epochs = Progress(hide_on_finish=False) -progress_bar_iters = Progress(hide_on_finish=False) -progress_bar_upload_artifacts = Progress() - -# Model benchmark -model_benchmark_pbar = SlyTqdm() -model_benchmark_pbar_secondary = Progress(hide_on_finish=False) -creating_report_f = Field(Empty(), "", "Creating report on model...") -creating_report_f.hide() -model_benchmark_report = ReportThumbnail() -model_benchmark_report.hide() - -output_folder = FolderThumbnail() -output_folder.hide() - -success_msg = DoneLabel("Training completed. Training artifacts were uploaded to Team Files.") -success_msg.hide() - -card = Card( - title="Training Progress", - description="Task progress, detailed logs, metrics charts, and other visualizations", - content=Container( - [ - success_msg, - output_folder, - model_benchmark_report, - creating_report_f, - progress_bar_download_project, - progress_bar_prepare_project, - progress_bar_download_model, - progress_bar_epochs, - progress_bar_iters, - progress_bar_upload_artifacts, - model_benchmark_pbar, - model_benchmark_pbar_secondary, - btn_container, - charts_grid_f, - ] - ), - lock_message="Select parameterts to unlock", -) -card.lock() - - -def iter_callback(logs): - iter_idx = logs.iter_idx - loss.add_to_series("Loss", (iter_idx, logs.loss)) - add_lrs(iter_idx, logs.lrs) - cuda_memory.add_to_series("Memory", (iter_idx, logs.cuda_memory)) - - -def eval_callback(logs): - add_metrics(logs.epoch, logs.evaluation_metrics) - - -def add_lrs(iter_idx: int, lrs: Dict[str, float]): - for series_name, lr in lrs.items(): - learning_rate.add_to_series(series_name, (iter_idx, lr)) - - -def add_metrics(epoch: int, metrics: Dict[str, float]): - for series_name, metric in metrics.items(): - if series_name.startswith("per_class"): - continue - validation_metrics.add_to_series(series_name, (epoch, metric)) - - -train_cli.setup_callbacks(iter_callback=iter_callback, eval_callback=eval_callback) - - -@start_train_btn.click -def run_training(): - project_dir = os.path.join(g.data_dir, "sly_project") - g.project_dir = project_dir - # iter_progress = Progress("Iterations", hide_on_finish=False) - - g.USE_CACHE = input_ui.use_cache_checkbox.is_checked() - download_project( - api=g.api, - project_id=g.PROJECT_ID, - project_dir=project_dir, - use_cache=g.USE_CACHE, - progress=progress_bar_download_project, - ) - g.project = sly.read_project(project_dir) - # prepare split files - try: - splits_ui.dump_train_val_splits(project_dir) - except Exception: - if not g.USE_CACHE: - raise - sly.logger.warn( - "Failed to dump train/val splits. Trying to re-download project.", exc_info=True - ) - download_project( - api=g.api, - project_id=g.PROJECT_ID, - project_dir=project_dir, - use_cache=False, - progress=progress_bar_download_project, - ) - splits_ui.dump_train_val_splits(project_dir) - g.project = sly.read_project(project_dir) - - # add prepare model progress - with progress_bar_prepare_project( - message="Preparing project...", total=1 - ) as prepare_project_pbar: - g.splits = splits_ui.trainval_splits.get_splits() - sly.logger.debug("Read splits from the widget...") - create_trainval() - custom_config = parameters_ui.read_parameters(len(g.splits[0])) - prepare_config(custom_config) - prepare_project_pbar.update(1) - - stop_train_btn.enable() - charts_grid_f.show() - - cfg = train() - save_config(cfg) - - progress_bar_epochs.hide() - progress_bar_iters.hide() - - # Upload artifacts - remote_artifacts_dir, file_info = upload_model(cfg.output_dir) - - # Model Benchmark - dataset_infos = g.api.dataset.get_list(g.PROJECT_ID) - ds_name_to_id = {ds.name: ds.id for ds in dataset_infos} - selected_classes = classes_ui.train_classes_selector.get_selected_classes() - train_set, val_set = g.splits - - # paths - remote_weights_path = g.rtdetr_artifacts.get_weights_path(remote_artifacts_dir) - remote_config_path = g.rtdetr_artifacts.get_config_path(remote_artifacts_dir) - local_artifacts_dir = os.path.join(cfg.output_dir, "upload") - local_checkpoints_dir = os.path.join(local_artifacts_dir, "weights") - - model_benchmark_done = False - if parameters_ui.run_model_benchmark_checkbox.is_checked(): - model_benchmark_done = run_model_benchmark( - api=g.api, - root_source_path=g.CURRENT_DIR, - local_artifacts_dir=local_artifacts_dir, - remote_weights_dir=remote_weights_path, - remote_config_path=remote_config_path, - project_info=g.project_info, - dataset_infos=dataset_infos, - ds_name_to_id=ds_name_to_id, - train_val_split=splits_ui.trainval_splits, - train_set=train_set, - val_set=val_set, - selected_classes=selected_classes, - use_speedtest=parameters_ui.run_speedtest_checkbox.is_checked(), - model_benchmark_report=model_benchmark_report, - creating_report_f=creating_report_f, - model_benchmark_pbar=model_benchmark_pbar, - model_benchmark_pbar_secondary=model_benchmark_pbar_secondary, - ) - - if not model_benchmark_done: - benchmark_report_template = None - w.workflow_output( - g.api, - "RT-DETR", # get_file_name(g.latest_checkpoint_path), - remote_artifacts_dir, - get_file_name(g.best_checkpoint_path), - benchmark_report_template, - ) - - # hide buttons - start_train_btn.hide() - stop_train_btn.hide() - - # add file tb - output_folder.set(file_info) - # add success text - success_msg.show() - output_folder.show() - start_train_btn.disable() - stop_train_btn.disable() - - # stop app - - g.app.stop() - - -@stop_train_btn.click -def stop_training(): - # TODO: Implement the stop process - g.STOP_TRAINING = True - stop_train_btn.disable() - - -def train(): - file_info = None - if g.model_mode == g.MODEL_MODES[0]: - model = g.train_mode.pretrained[0] - finetune = g.train_mode.finetune - else: - model = g.train_mode.custom - file_info = g.api.file.get_info_by_path(g.TEAM_ID, model) - finetune = True - - # ---------------------------------- Init And Set Workflow Input --------------------------------- # - w.workflow_input(g.api, g.project_info, file_info) - # ----------------------------------------------- - ---------------------------------------------- # - - cfg = train_cli.train( - model, - finetune, - g.custom_config_path, - progress_bar_download_model, - progress_bar_epochs, - progress_bar_iters, - stop_train_btn, - charts_grid_f, - ) - return cfg - - -def upload_model(output_dir): - remote_artifacts_dir = f"/RT-DETR/{g.project_info.name}/{g.TASK_ID}" - remote_weights_path = g.rtdetr_artifacts.get_weights_path(remote_artifacts_dir) - remote_config_path = g.rtdetr_artifacts.get_config_path(remote_artifacts_dir) - local_artifacts_dir = os.path.join(output_dir, "upload") - local_checkpoints_dir = os.path.join(local_artifacts_dir, "weights") - sly.fs.mkdir(local_artifacts_dir) - sly.fs.mkdir(local_checkpoints_dir) - sly.logger.info(f"Local artifacts dir: {local_artifacts_dir}") - - # Move last checkpoint to checkpoints folder - shutil.move(g.latest_checkpoint_path, f"{local_checkpoints_dir}/{g.latest_checkpoint_name}") - - # Move best checkpoint to checkpoints folder - best_checkpoint_file_name = get_file_name(g.best_checkpoint_path) - shutil.move(g.best_checkpoint_path, f"{local_checkpoints_dir}/{best_checkpoint_file_name}.pth") - - # Move log and config files to artifacts folder - shutil.move(f"{output_dir}/log.txt", f"{local_artifacts_dir}/log.txt") - shutil.move(f"{output_dir}/config.yml", f"{local_artifacts_dir}/config.yml") - - # Save link to app ui - app_url = f"/apps/sessions/{g.TASK_ID}" - app_link_path = os.path.join(local_artifacts_dir, "open_app.lnk") - with open(app_link_path, "w") as text_file: - print(app_url, file=text_file) - - # Upload artifacts - local_files = sly.fs.list_files_recursively(local_artifacts_dir) - total_size = sum([sly.fs.get_file_size(file_path) for file_path in local_files]) - with progress_bar_upload_artifacts( - message="Uploading train artifacts to Team Files...", - total=total_size, - unit="bytes", - unit_scale=True, - ) as artifacts_pbar: - out_path = g.api.file.upload_directory( - sly.env.team_id(), - local_artifacts_dir, - remote_artifacts_dir, - progress_size_cb=artifacts_pbar, - ) - - # Upload train metadata - g.rtdetr_artifacts.generate_metadata( - app_name=g.rtdetr_artifacts.app_name, - task_id=g.TASK_ID, - artifacts_folder=remote_artifacts_dir, - weights_folder=remote_weights_path, - weights_ext=g.rtdetr_artifacts.weights_ext, - project_name=g.project_info.name, - task_type="object detection", - config_path=remote_config_path, - ) - - file_info = g.api.file.get_info_by_path( - g.TEAM_ID, os.path.join(remote_artifacts_dir, "open_app.lnk") - ) - sly.logger.info("Training artifacts uploaded successfully") - sly.output.set_directory(remote_artifacts_dir) - return out_path, file_info - - -def create_trainval(): - # g.splits = splits.trainval_splits.get_splits() - train_items, val_items = g.splits - sly.logger.debug(f"Creating trainval datasets from splits: {g.splits}...") - train_items: List[sly.project.project.ItemInfo] - val_items: List[sly.project.project.ItemInfo] - - converted_project_dir = os.path.join(g.CONVERTED_DIR, g.project_info.name) - sly.logger.debug(f"Converted project will be saved to {converted_project_dir}.") - sly.fs.mkdir(converted_project_dir) - train_dataset_path = os.path.join(converted_project_dir, "train") - val_dataset_path = os.path.join(converted_project_dir, "val") - sly.logger.debug( - f"Train dataset path: {train_dataset_path}, val dataset path: {val_dataset_path}." - ) - - g.train_dataset_path = train_dataset_path - g.val_dataset_path = val_dataset_path - - project_meta_path = os.path.join(converted_project_dir, "meta.json") - sly.json.dump_json_file(g.project.meta.to_json(), project_meta_path) - - for items, dataset_path in zip( - [train_items, val_items], [train_dataset_path, val_dataset_path] - ): - prepare_dataset(dataset_path, items) - - g.converted_project = sly.Project(converted_project_dir, sly.OpenMode.READ) - sly.logger.info(f"Project created in {converted_project_dir}") - - for dataset_fs in g.converted_project.datasets: - dataset_fs: sly.Dataset - selected_classes = g.selected_classes - - coco_anno = get_coco_annotations(dataset_fs, g.converted_project.meta, selected_classes) - coco_anno_path = os.path.join(dataset_fs.directory, "coco_anno.json") - sly.json.dump_json_file(coco_anno, coco_anno_path) - - sly.logger.info("COCO annotations created") - - -def prepare_dataset(dataset_path: str, items: List[sly.project.project.ItemInfo]): - sly.logger.debug(f"Preparing dataset in {dataset_path}...") - img_dir = os.path.join(dataset_path, "img") - ann_dir = os.path.join(dataset_path, "ann") - sly.fs.mkdir(img_dir) - sly.fs.mkdir(ann_dir) - for item in items: - src_img_path = os.path.join(g.project_dir, fix_widget_path(item.img_path)) - src_ann_path = os.path.join(g.project_dir, fix_widget_path(item.ann_path)) - dst_img_path = os.path.join(img_dir, item.name) - dst_ann_path = os.path.join(ann_dir, f"{item.name}.json") - sly.fs.copy_file(src_img_path, dst_img_path) - sly.fs.copy_file(src_ann_path, dst_ann_path) - - sly.logger.info(f"Dataset prepared in {dataset_path}") - - -def fix_widget_path(bugged_path: str) -> str: - """Fixes the broken ItemInfo paths from TrainValSplits widget. - Removes the first two folders from the path. - - Bugged path: app_data/1IkWRgJG62f1ZuZ/ds0/ann/pexels_2329440.jpeg.json - Corrected path: ds0/ann/pexels_2329440.jpeg.json - - :param bugged_path: Path to fix - :type bugged_path: str - :return: Fixed path - :rtype: str - """ - path = bugged_path.split("/") - - if sly.is_development(): - updated_path = path[8:] - else: - updated_path = path[7:] - correct_path = "/".join(updated_path) - return correct_path - - -# parameters handlers diff --git a/supervisely_integration/train/ui/parameters.py b/supervisely_integration/train/ui/parameters.py deleted file mode 100644 index 6ee04e9d..00000000 --- a/supervisely_integration/train/ui/parameters.py +++ /dev/null @@ -1,650 +0,0 @@ -import numpy as np -import yaml - -import supervisely as sly -import supervisely_integration.train.globals as g -import supervisely_integration.train.ui.output as output_ui -import supervisely_integration.train.ui.schedulers as schedulers -import supervisely_integration.train.ui.utils as utils_ui -import supervisely_integration.train.utils as utils -from supervisely.app.widgets import ( - BindedInputNumber, - Button, - Card, - Checkbox, - Container, - Editor, - Empty, - Field, - Input, - InputNumber, - LineChart, - Select, - Switch, - Tabs, - Text, -) - -# region advanced widgets -advanced_mode_checkbox = Checkbox("Advanced mode") -advanced_mode_field = Field( - advanced_mode_checkbox, - title="Advanced mode", - description="Enable advanced mode to specify custom training parameters manually.", -) -with open(g.default_config_path, "r") as f: - default_config = f.read() - sly.logger.debug(f"Loaded default config from {g.default_config_path}") -advanced_mode_editor = Editor(default_config, language_mode="yaml", height_lines=100) -advanced_mode_editor.hide() -# endregion - -# region general widgets -number_of_epochs_input = InputNumber(value=20, min=1) -number_of_epochs_field = Field( - number_of_epochs_input, - title="Number of epochs", - description="The number of epochs to train the model for", -) -input_size_input = BindedInputNumber(640, 640) -input_size_field = Field( - input_size_input, - title="Input size", - description="Images will be resized to this size.", -) - -train_batch_size_input = InputNumber(value=4, min=1) -train_batch_size_field = Field( - train_batch_size_input, - title="Train batch size", - description="The number of images in a batch during training", -) - -val_batch_size_input = InputNumber(value=8, min=1) -val_batch_size_field = Field( - val_batch_size_input, - title="Validation batch size", - description="The number of images in a batch during validation", -) - -validation_interval_input = InputNumber(value=1, min=1) -validation_interval_field = Field( - validation_interval_input, - title="Validation interval", - description="The number of epochs between each validation run", -) - -checkpoints_interval_input = InputNumber(value=1, min=1) -checkpoints_interval_field = Field( - checkpoints_interval_input, - title="Checkpoint interval", - description="The number of epochs between each checkpoint save", -) - -general_tab = Container( - [ - number_of_epochs_field, - input_size_field, - train_batch_size_field, - val_batch_size_field, - validation_interval_field, - checkpoints_interval_field, - ] -) -# endregion - -# region optimizer widgets -optimizer_select = Select([Select.Item(opt) for opt in g.OPTIMIZERS]) -optimizer_field = Field( - optimizer_select, - title="Select optimizer", - description="Choose the optimizer to use for training", -) -learning_rate_input = InputNumber(value=0.0002, step=0.00005) -learning_rate_field = Field( - learning_rate_input, - title="Learning rate", - description="The learning rate to use for the optimizer", -) -wight_decay_input = InputNumber(value=0.0001, step=0.00001) -wight_decay_field = Field( - wight_decay_input, - title="Weight decay", - description="The amount of L2 regularization to apply to the weights", -) -momentum_input = InputNumber(value=0.9, step=0.1) -momentum_field = Field( - momentum_input, - title="Momentum", - description="The amount of momentum to apply to the weights", -) -momentum_field.hide() -beta1_input = InputNumber(value=0.9, step=0.1) -beta1_field = Field( - beta1_input, - title="Beta 1", - description="The exponential decay rate for the first moment estimates", -) -beta2_input = InputNumber(value=0.999, step=0.001) -beta2_field = Field( - beta2_input, - title="Beta 2", - description="The exponential decay rate for the second moment estimates", -) - -clip_gradient_norm_checkbox = Checkbox("Clip gradient norm") -clip_gradient_norm_input = InputNumber(value=0.1, step=0.01) -clip_gradient_norm_field = Field( - Container([clip_gradient_norm_checkbox, clip_gradient_norm_input]), - title="Clip gradient norm", - description="Select the highest gradient norm to clip the gradients", -) - - -optimization_tab = Container( - [ - optimizer_field, - learning_rate_field, - wight_decay_field, - momentum_field, - beta1_field, - beta2_field, - clip_gradient_norm_field, - ] -) - -# endregion - -# region scheduler widgets -scheduler_preview_chart = LineChart( - "Scheduler preview", stroke_curve="straight", height=400, decimalsInFloat=6, markers_size=0 -) -scheduler_preview_chart.hide() -scheduler_preview_btn = Button("Preview", button_size="small") -scheduler_clear_btn = Button("Clear", button_size="small", plain=True) -scheduler_preview_info = Text("", status="info") - - -select_scheduler_items = [Select.Item(val, label) for val, label in schedulers.schedulers] -select_scheduler = Select(items=select_scheduler_items) -select_scheduler_field = Field( - select_scheduler, - title="Select scheduler", -) - -enable_warmup_input = Switch(True) -enable_warmup_field = Field(enable_warmup_input, "Enable warmup") - -warmup = utils_ui.OrderedWidgetWrapper("warmup") -warmup_iterations_input = InputNumber(25, 1, step=1) -warmup_iterations_field = Field( - warmup_iterations_input, "Warmup iterations", "The number of iterations that warmup lasts" -) -warmup.add_input("warmup_iters", warmup_iterations_input, warmup_iterations_field) - -warmup_ratio = InputNumber(0.001, step=0.0001) -warmup_ratio_field = Field( - warmup_ratio, - "Warmup ratio", - "LR used at the beginning of warmup equals to warmup_ratio * initial_lr", -) -warmup.add_input("warmup_ratio", warmup_ratio, warmup_ratio_field) - -learning_rate_scheduler_tab = Container( - [ - select_scheduler_field, - Container( - [ - schedulers.multi_steps_scheduler.create_container(hide=True), - schedulers.cosineannealing_scheduler.create_container(hide=True), - schedulers.linear_scheduler.create_container(hide=True), - schedulers.onecycle_scheduler.create_container(hide=True), - ], - gap=0, - ), - enable_warmup_field, - warmup.create_container(), - scheduler_preview_chart, - Container( - [scheduler_preview_btn, scheduler_clear_btn, Empty()], - "horizontal", - 0, - fractions=[1, 1, 10], - ), - ], -) - -# endregion - -select_params_button = Button("Select") - -parameters_tabs = Tabs( - ["General", "Optimizer (Advanced)", "Learning rate scheduler (Advanced)"], - contents=[ - general_tab, - # checkpoints_tab, - optimization_tab, - learning_rate_scheduler_tab, - ], -) - -# Model Benchmark -run_model_benchmark_checkbox = Checkbox(content="Run Model Benchmark evaluation", checked=True) -run_speedtest_checkbox = Checkbox(content="Run speed test", checked=True) - -model_benchmark_f = Field( - Container( - widgets=[ - run_model_benchmark_checkbox, - run_speedtest_checkbox, - ] - ), - title="Model Evaluation Benchmark", - description=f"Generate evalutaion dashboard with visualizations and detailed analysis of the model performance after training. The best checkpoint will be used for evaluation. You can also run speed test to evaluate model inference speed.", -) -docs_link = 'documentation' -model_benchmark_learn_more = Text( - f"Learn more about Model Benchmark in the {docs_link}.", status="info" -) - - -content = Container( - [ - advanced_mode_field, - advanced_mode_editor, - parameters_tabs, - model_benchmark_f, - model_benchmark_learn_more, - select_params_button, - ], -) - -card = Card( - title="Training hyperparameters", - description="Specify training hyperparameters using one of the methods.", - content=content, - lock_message="Select augmentations to unlock", -) -card.lock() - - -@run_model_benchmark_checkbox.value_changed -def change_model_benchmark(value): - if value: - run_speedtest_checkbox.show() - else: - run_speedtest_checkbox.hide() - - -@advanced_mode_checkbox.value_changed -def advanced_mode_changed(is_checked: bool): - if is_checked: - advanced_mode_editor.show() - parameters_tabs.hide() - else: - advanced_mode_editor.hide() - parameters_tabs.show() - - -@optimizer_select.value_changed -def optimizer_changed(optimizer: str): - if optimizer == "Adam": - beta1_field.show() - beta2_field.show() - momentum_field.hide() - elif optimizer == "AdamW": - beta1_field.hide() - beta2_field.hide() - momentum_field.hide() - elif optimizer == "SGD": - beta1_field.hide() - beta2_field.hide() - momentum_field.show() - - -@enable_warmup_input.value_changed -def warmup_changed(is_switched: bool): - if is_switched: - warmup_iterations_field.show() - warmup_iterations_input.show() - warmup_ratio_field.show() - warmup_ratio.show() - else: - warmup_iterations_field.hide() - warmup_iterations_input.hide() - warmup_ratio_field.hide() - warmup_ratio.hide() - - -@scheduler_preview_btn.click -def on_preview_scheduler(): - import torch - import visualize_scheduler - from torch.optim import SGD - - from rtdetr_pytorch.utils import name2cls - - total_epochs = number_of_epochs_input.get_value() - batch_size = train_batch_size_input.get_value() - start_lr = learning_rate_input.get_value() - total_images = g.project_info.items_count - - from supervisely.app.widgets import TrainValSplits - from supervisely_integration.train.ui.splits import trainval_splits - - split_widget: TrainValSplits = trainval_splits - # split_widget.get_splits() - self = split_widget - split_method = self._content.get_active_tab() - if split_method == "Random": - splits_counts = self._random_splits_table.get_splits_counts() - train_count = splits_counts["train"] - val_count = splits_counts["val"] - val_part = val_count / (val_count + train_count) - n_images = total_images - val_count = round(val_part * n_images) - train_count = n_images - val_count - elif split_method == "Based on item tags": - # can't predict - train_count = total_images - elif split_method == "Based on datasets": - train_ds_ids = self._train_ds_select.get_selected_ids() - val_ds_ids = self._val_ds_select.get_selected_ids() - ds_infos = self._api.dataset.get_list(self._project_id) - train_ds_names, val_ds_names = [], [] - train_count, val_count = 0, 0 - for ds_info in ds_infos: - if ds_info.id in train_ds_ids: - train_ds_names.append(ds_info.name) - train_count += ds_info.items_count - if ds_info.id in val_ds_ids: - val_ds_names.append(ds_info.name) - val_count += ds_info.items_count - - dataloader_len = train_count // batch_size - - def instantiate(d, **cls_kwargs): - d = d.copy() - cls = name2cls(d.pop("type")) - return cls(**{**d, **cls_kwargs}) - - custom_config = read_parameters(train_count) - lr_scheduler = custom_config.get("lr_scheduler") - dummy_optim = SGD([torch.nn.Parameter(torch.tensor([5.0]))], start_lr) - if lr_scheduler is not None: - lr_scheduler = instantiate(lr_scheduler, optimizer=dummy_optim) - - use_lr_warmup = enable_warmup_input.is_switched() - if use_lr_warmup: - lr_warmup = custom_config.get("lr_warmup") - if lr_warmup is not None: - lr_warmup = instantiate(lr_warmup, optimizer=dummy_optim) - else: - lr_warmup = None - - x, lrs = visualize_scheduler.test_schedulers( - lr_scheduler, lr_warmup, dummy_optim, dataloader_len, total_epochs - ) - - scheduler_name = select_scheduler.get_value() - if scheduler_name == "empty": - scheduler_name = "Without scheduler" - - name = f"{scheduler_name} warmup" if use_lr_warmup else scheduler_name - scheduler_preview_chart.add_series(f"{name}", x, lrs) - scheduler_preview_chart.show() - scheduler_preview_info.set( - f"Estimated train images count = {train_count}. Actual curve can be different.", - status="info", - ) - - -@scheduler_clear_btn.click -def on_clear_preview(): - scheduler_preview_chart.set_series([]) - - -@select_scheduler.value_changed -def update_scheduler(new_value): - for scheduler in schedulers.schedulers_params.keys(): - if new_value == scheduler: - schedulers.schedulers_params[scheduler].show() - else: - schedulers.schedulers_params[scheduler].hide() - - -@select_params_button.click -def select_params(): - if select_params_button.text == "Select": - # widgets to disable - utils_ui.disable_enable( - [ - advanced_mode_checkbox, - advanced_mode_editor, - number_of_epochs_input, - input_size_input, - train_batch_size_input, - val_batch_size_input, - validation_interval_input, - checkpoints_interval_input, - optimizer_select, - learning_rate_input, - wight_decay_input, - momentum_input, - beta1_input, - beta2_input, - clip_gradient_norm_checkbox, - clip_gradient_norm_input, - warmup_iterations_input, - scheduler_preview_chart, - scheduler_preview_btn, - scheduler_clear_btn, - select_scheduler, - enable_warmup_input, - warmup, - warmup_iterations_input, - warmup_ratio, - parameters_tabs, - run_model_benchmark_checkbox, - run_speedtest_checkbox, - ], - True, - ) - - utils_ui.update_custom_button_params(select_params_button, utils_ui.reselect_params) - g.update_step() - - # unlock - output_ui.card.unlock() - else: - # lock - output_ui.card.lock() - utils_ui.update_custom_button_params(select_params_button, utils_ui.select_params) - - # widgets to enable - utils_ui.disable_enable( - [ - advanced_mode_checkbox, - advanced_mode_editor, - number_of_epochs_input, - input_size_input, - train_batch_size_input, - val_batch_size_input, - validation_interval_input, - checkpoints_interval_input, - optimizer_select, - learning_rate_input, - wight_decay_input, - momentum_input, - beta1_input, - beta2_input, - clip_gradient_norm_checkbox, - clip_gradient_norm_input, - warmup_iterations_input, - scheduler_preview_chart, - scheduler_preview_btn, - scheduler_clear_btn, - select_scheduler, - enable_warmup_input, - warmup, - warmup_iterations_input, - warmup_ratio, - parameters_tabs, - run_model_benchmark_checkbox, - run_speedtest_checkbox, - ], - False, - ) - g.update_step(back=True) - - -def read_parameters(train_items_cnt: int): - sly.logger.debug("Reading training parameters...") - if advanced_mode_checkbox.is_checked(): - sly.logger.info("Advanced mode enabled, using custom config from the editor.") - custom_config = advanced_mode_editor.get_value() - else: - sly.logger.info("Advanced mode disabled, reading parameters from the widgets.") - with open(g.default_config_path, "r") as f: - custom_config = f.read() - custom_config = yaml.safe_load(custom_config) - - clip_max_norm = ( - clip_gradient_norm_input.get_value() if clip_gradient_norm_checkbox.is_checked() else -1 - ) - general_params = { - "epoches": number_of_epochs_input.value, - "val_step": validation_interval_input.value, - "checkpoint_step": checkpoints_interval_input.value, - "clip_max_norm": clip_max_norm, - } - - total_steps = general_params["epoches"] * np.ceil( - train_items_cnt / train_batch_size_input.value - ) - - optimizer_params = read_optimizer_parameters() - scheduler_params, scheduler_cls_params = read_scheduler_parameters(total_steps) - - sly.logger.debug(f"General parameters: {general_params}") - sly.logger.debug(f"Optimizer parameters: {optimizer_params}") - sly.logger.debug(f"Scheduler parameters: {scheduler_cls_params}") - - custom_config.update(general_params) - custom_config["optimizer"]["type"] = optimizer_params["optimizer"] - custom_config["optimizer"]["lr"] = optimizer_params["learning_rate"] - custom_config["optimizer"]["weight_decay"] = optimizer_params["weight_decay"] - if optimizer_params.get("momentum"): - custom_config["optimizer"]["momentum"] = optimizer_params["momentum"] - else: - custom_config["optimizer"]["betas"] = [ - optimizer_params["beta1"], - optimizer_params["beta2"], - ] - - # Set input_size - w, h = input_size_input.get_value() - for op in custom_config["train_dataloader"]["dataset"]["transforms"]["ops"]: - if op["type"] == "Resize": - op["size"] = [w, h] - for op in custom_config["val_dataloader"]["dataset"]["transforms"]["ops"]: - if op["type"] == "Resize": - op["size"] = [w, h] - if "HybridEncoder" in custom_config: - custom_config["HybridEncoder"]["eval_spatial_size"] = [w, h] - else: - custom_config["HybridEncoder"] = {"eval_spatial_size": [w, h]} - if "RTDETRTransformer" in custom_config: - custom_config["RTDETRTransformer"]["eval_spatial_size"] = [w, h] - else: - custom_config["RTDETRTransformer"] = {"eval_spatial_size": [w, h]} - - custom_config["train_dataloader"]["batch_size"] = train_batch_size_input.value - custom_config["val_dataloader"]["batch_size"] = val_batch_size_input.value - custom_config["train_dataloader"]["num_workers"] = utils.get_num_workers( - train_batch_size_input.value - ) - custom_config["val_dataloader"]["num_workers"] = utils.get_num_workers( - val_batch_size_input.value - ) - - # LR scheduler - if scheduler_params["type"] == "Without scheduler": - custom_config["lr_scheduler"] = None - else: - custom_config["lr_scheduler"] = scheduler_cls_params - - if scheduler_params["enable_warmup"]: - custom_config["lr_warmup"] = { - "type": "LinearLR", - "total_iters": scheduler_params["warmup_iterations"], - "start_factor": 0.001, - "end_factor": 1.0, - } - - return custom_config - - -def read_optimizer_parameters(): - optimizer = optimizer_select.get_value() - - parameters = { - "optimizer": optimizer, - "learning_rate": learning_rate_input.get_value(), - "weight_decay": wight_decay_input.get_value(), - "clip_gradient_norm": clip_gradient_norm_checkbox.is_checked(), - "clip_gradient_norm_value": clip_gradient_norm_input.get_value(), - } - - if optimizer in ["Adam", "AdamW"]: - parameters.update( - { - "beta1": beta1_input.get_value(), - "beta2": beta2_input.get_value(), - } - ) - elif optimizer == "SGD": - parameters.update({"momentum": momentum_input.get_value()}) - - return parameters - - -def read_scheduler_parameters(total_steps: int): - scheduler = select_scheduler.get_value() - if scheduler == "empty": - scheduler = "Without scheduler" - - parameters = { - "type": scheduler, - "enable_warmup": enable_warmup_input.is_switched(), - "warmup_iterations": warmup_iterations_input.get_value(), - } - - if scheduler == "Without scheduler": - return parameters, {} - - scheduler_cls_params = { - "type": scheduler, - } - - scheduler_widgets = schedulers.schedulers_params[scheduler]._widgets - if scheduler_widgets is not None: - for key, widget in scheduler_widgets.items(): - if isinstance(widget, (InputNumber, Input, Select)): - scheduler_cls_params[key] = widget.get_value() - elif isinstance(widget, Checkbox): - scheduler_cls_params[key] = widget.is_checked() - elif isinstance(widget, Switch): - if not key == "by_epoch": - scheduler_cls_params[key] = widget.is_switched() - - if scheduler_cls_params["type"] == "OneCycleLR": - scheduler_cls_params["total_steps"] = int(total_steps) - elif scheduler_cls_params["type"] == "LinearLR": - scheduler_cls_params["total_iters"] = int(total_steps) - elif scheduler_cls_params["type"] == "MultiStepLR": - scheduler_cls_params["milestones"] = [ - int(step.strip()) for step in scheduler_cls_params["milestones"].split(",") - ] - - return parameters, scheduler_cls_params diff --git a/supervisely_integration/train/ui/project_cached.py b/supervisely_integration/train/ui/project_cached.py deleted file mode 100644 index 89144c47..00000000 --- a/supervisely_integration/train/ui/project_cached.py +++ /dev/null @@ -1,93 +0,0 @@ -import os - -import supervisely as sly -from supervisely.app.widgets import Progress -from supervisely.project.download import ( - copy_from_cache, - download_to_cache, - get_cache_size, - is_cached, -) - - -def _no_cache_download( - api: sly.Api, project_info: sly.ProjectInfo, project_dir: str, progress: Progress -): - total = project_info.items_count - with progress(message="Downloading input data...", total=total) as pbar: - sly.download( - api=api, - project_id=project_info.id, - dest_dir=project_dir, - dataset_ids=None, - log_progress=True, - progress_cb=pbar.update, - ) - - -def download_project( - api: sly.Api, - project_id: int, - project_dir: str, - use_cache: bool, - progress: Progress, -): - if os.path.exists(project_dir): - sly.fs.clean_dir(project_dir) - project_info = api.project.get_info_by_id(project_id) - if not use_cache: - _no_cache_download(api, project_info, project_dir, progress) - return - try: - # get datasets to download and cached - dataset_infos = api.dataset.get_list(project_id) - to_download = [info for info in dataset_infos if not is_cached(project_info.id, info.name)] - cached = [info for info in dataset_infos if is_cached(project_info.id, info.name)] - if len(cached) == 0: - log_msg = "No cached datasets found" - else: - log_msg = "Using cached datasets: " + ", ".join( - f"{ds_info.name} ({ds_info.id})" for ds_info in cached - ) - sly.logger.info(log_msg) - if len(to_download) == 0: - log_msg = "All datasets are cached. No datasets to download" - else: - log_msg = "Downloading datasets: " + ", ".join( - f"{ds_info.name} ({ds_info.id})" for ds_info in to_download - ) - sly.logger.info(log_msg) - # get images count - total = sum([ds_info.images_count for ds_info in dataset_infos]) - # download - with progress(message="Downloading input data...", total=total) as pbar: - download_to_cache( - api=api, - project_id=project_info.id, - dataset_infos=dataset_infos, - log_progress=True, - progress_cb=pbar.update, - ) - # copy datasets from cache - total = sum([get_cache_size(project_info.id, ds.name) for ds in dataset_infos]) - with progress( - message="Retrieving data from cache...", - total=total, - unit="B", - unit_scale=True, - unit_divisor=1024, - ) as pbar: - dataset_names = [ds_info.name for ds_info in dataset_infos] - copy_from_cache( - project_id=project_info.id, - dest_dir=project_dir, - dataset_names=dataset_names, - progress_cb=pbar.update, - ) - except Exception: - sly.logger.warning( - f"Failed to retreive project from cache. Downloading it...", exc_info=True - ) - if os.path.exists(project_dir): - sly.fs.clean_dir(project_dir) - _no_cache_download(api, project_info, project_dir, progress) diff --git a/supervisely_integration/train/ui/schedulers.py b/supervisely_integration/train/ui/schedulers.py deleted file mode 100644 index c2c23e3c..00000000 --- a/supervisely_integration/train/ui/schedulers.py +++ /dev/null @@ -1,219 +0,0 @@ -from typing import List - -from supervisely.app.widgets import ( - Container, - Empty, - Field, - Input, - InputNumber, - SelectString, - Switch, -) -from supervisely_integration.train.ui.utils import ( - OrderedWidgetWrapper, - create_linked_getter, - get_switch_value, - set_switch_value, -) - -schedulers = [("empty", "Without scheduler")] - - -def get_multisteps(input_w: Input) -> List[int]: - steps: str = input_w.get_value() - return [int(st.strip()) for st in steps.split(",")] - - -def set_multisteps(input_w: Input, value: List[int]): - input_w.set_value(",".join(value)) - - -multi_steps_scheduler = OrderedWidgetWrapper("MultiStepLR") - -multi_steps_input = Input("16,22") -multi_steps_field = Field( - multi_steps_input, - "LR sheduler steps", - "Many int step values splitted by comma", -) -multi_steps_scheduler.add_input( - "milestones", - multi_steps_input, - wraped_widget=multi_steps_field, - custom_value_getter=get_multisteps, - custom_value_setter=set_multisteps, -) - -multi_steps_gamma_input = InputNumber(0.1, 0, step=1e-5, size="small") -multi_steps_gamma_field = Field(multi_steps_gamma_input, "Gamma") -multi_steps_scheduler.add_input("gamma", multi_steps_gamma_input, multi_steps_gamma_field) -schedulers.append((repr(multi_steps_scheduler), "Multistep LR")) - -# CosineAnnealingLR -cosineannealing_scheduler = OrderedWidgetWrapper("CosineAnnealingLR") - -cosineannealing_tmax_input = InputNumber(1, 1, step=1, size="small") -cosineannealing_tmax_field = Field( - cosineannealing_tmax_input, - "T max", - "Maximum number of iterations", -) -cosineannealing_scheduler.add_input( - "T_max", - cosineannealing_tmax_input, - cosineannealing_tmax_field, -) - -etamin_switch_input = Switch(True) -etamin_input = InputNumber(0, 0, step=1e-6, size="small") -etamin_field = Field( - Container([etamin_switch_input, etamin_input]), - "Min LR", - "Minimum learning rate", -) - -etamin_ratio_input = InputNumber(0, 0, step=1e-6, size="small") -etamin_ratio_input.disable() -etamin_ratio_field = Field( - etamin_ratio_input, - "Min LR Ratio", - "The ratio of the minimum parameter value to the base parameter value", -) - -cosineannealing_scheduler.add_input( - "eta_min", - etamin_input, - etamin_field, - custom_value_getter=create_linked_getter( - etamin_input, - etamin_ratio_input, - etamin_switch_input, - True, - ), -) - -schedulers.append((repr(cosineannealing_scheduler), "Cosine Annealing LR")) - - -# LinearLR -linear_scheduler = OrderedWidgetWrapper("LinearLR") - -linear_start_factor_input = InputNumber(0.333, 1e-4, step=1e-4) -linear_start_factor_field = Field( - linear_start_factor_input, - "Start factor", - description=( - "The number we multiply learning rate in the " - "first epoch. The multiplication factor changes towards end factor " - "in the following epochs" - ), -) -linear_scheduler.add_input("start_factor", linear_start_factor_input, linear_start_factor_field) - - -linear_end_factor_input = InputNumber(1.0, 1e-4, step=1e-4) -linear_end_factor_field = Field( - linear_end_factor_input, - "End factor", - description=("The number we multiply learning rate at the end " "of linear changing process"), -) -linear_scheduler.add_input("end_factor", linear_end_factor_input, linear_end_factor_field) -schedulers.append((repr(linear_scheduler), "Linear LR")) - -# OneCycleLR -onecycle_scheduler = OrderedWidgetWrapper("OneCycleLR") - -# TODO: теоретически этот параметр может быть списком. Надо ли? -onecycle_eta_input = InputNumber(1, 0, step=1e-6, size="small") -onecycle_eta_field = Field( - onecycle_eta_input, "Max LR", "Upper parameter value boundaries in the cycle" -) -onecycle_scheduler.add_input( - "max_lr", - onecycle_eta_input, - onecycle_eta_field, -) - -onecycle_total_steps_input = InputNumber(100, 1, step=1, size="small") -onecycle_total_steps_field = Field( - onecycle_total_steps_input, "Total steps", "The total number of steps in the cycle" -) -onecycle_scheduler.add_input( - "total_steps", - onecycle_total_steps_input, - onecycle_total_steps_field, -) - -onecycle_pct_start_input = InputNumber(0.3, 0, step=0.001) -onecycle_pct_start_field = Field( - onecycle_pct_start_input, - "Start percentage", - "The percentage of the cycle (in number of steps) spent increasing the learning rate.", -) -onecycle_scheduler.add_input( - "pct_start", - onecycle_pct_start_input, - onecycle_pct_start_field, -) - -onecycle_anneal_strategy_input = SelectString(["cos", "linear"]) -onecycle_anneal_strategy_field = Field(onecycle_anneal_strategy_input, "Anneal strategy") -onecycle_scheduler.add_input( - "anneal_strategy", - onecycle_anneal_strategy_input, - onecycle_anneal_strategy_field, - custom_value_getter=lambda w: w.get_value(), - custom_value_setter=lambda w, v: w.set_value(v), -) - -onecycle_div_factor_input = InputNumber(25, 1e-4, step=1e-3) -onecycle_div_factor_field = Field( - onecycle_div_factor_input, - "Div factor", - "Determines the initial learning rate via initial_param = max_lr/div_factor", -) -onecycle_scheduler.add_input( - "div_factor", - onecycle_div_factor_input, - onecycle_div_factor_field, -) - -onecycle_findiv_factor_input = InputNumber(10000, 1e-6, step=1) -onecycle_findiv_factor_field = Field( - onecycle_findiv_factor_input, - "Final div factor", - "Determines the minimum learning rate via min_lr = initial_param/final_div_factor", -) -onecycle_scheduler.add_input( - "final_div_factor", - onecycle_findiv_factor_input, - onecycle_findiv_factor_field, -) - - -onecycle_three_phase_input = Switch(True) -onecycle_three_phase_field = Field( - onecycle_three_phase_input, - "Use three phase", - ( - "If turned on, use a third phase of the schedule to " - "annihilate the learning rate according to `final_div_factor` " - "instead of modifying the second phase" - ), -) -onecycle_scheduler.add_input( - "three_phase", - onecycle_three_phase_input, - onecycle_three_phase_field, - get_switch_value, - set_switch_value, -) -schedulers.append((repr(onecycle_scheduler), "OneCycleLR")) - -schedulers_params = { - "Without scheduler": Empty(), - repr(multi_steps_scheduler): multi_steps_scheduler, - repr(cosineannealing_scheduler): cosineannealing_scheduler, - repr(linear_scheduler): linear_scheduler, - repr(onecycle_scheduler): onecycle_scheduler, -} diff --git a/supervisely_integration/train/ui/splits.py b/supervisely_integration/train/ui/splits.py deleted file mode 100644 index ce43336f..00000000 --- a/supervisely_integration/train/ui/splits.py +++ /dev/null @@ -1,56 +0,0 @@ -import supervisely as sly -from supervisely.app.widgets import Button, Card, Container, TrainValSplits - -import supervisely_integration.train.globals as g -import supervisely_integration.train.ui.augmentations as augmentations -import supervisely_integration.train.ui.parameters as parameters -import supervisely_integration.train.ui.utils as utils - -trainval_splits = TrainValSplits(g.PROJECT_ID) -select_splits_button = Button("Select") - -card = Card( - title="Train / Validation splits", - description="Select splits for training and validation", - content=Container([trainval_splits, select_splits_button]), - lock_message="Select classes to unlock", -) -card.lock() - - -@select_splits_button.click -def splits_selected(): - if select_splits_button.text == "Select": - # widgets to disable - utils.disable_enable([trainval_splits, card], True) - - # g.splits = trainval_splits.get_splits() - - utils.update_custom_button_params(select_splits_button, utils.reselect_params) - g.update_step() - - # unlock - augmentations.card.unlock() - else: - # lock - augmentations.card.lock() - utils.update_custom_button_params(augmentations.select_augs_button, utils.select_params) - - parameters.card.lock() - # utils.update_custom_button_params(parameters.run_training, utils.select_params) - - utils.update_custom_button_params(select_splits_button, utils.select_params) - - # widgets to enable - utils.disable_enable([trainval_splits, card], False) - g.update_step(back=True) - - -def dump_train_val_splits(project_dir): - # splits._project_id = None - trainval_splits._project_fs = sly.Project(project_dir, sly.OpenMode.READ) - g.splits = trainval_splits.get_splits() - train_split, val_split = g.splits - app_dir = g.data_dir # @TODO: change to sly.app.get_synced_data_dir()? - sly.json.dump_json_file(train_split, f"{app_dir}/train_split.json") - sly.json.dump_json_file(val_split, f"{app_dir}/val_split.json") diff --git a/supervisely_integration/train/ui/utils.py b/supervisely_integration/train/ui/utils.py deleted file mode 100644 index eefa11f1..00000000 --- a/supervisely_integration/train/ui/utils.py +++ /dev/null @@ -1,205 +0,0 @@ -import os -from collections import OrderedDict -from typing import Any, Callable, Dict, Iterable, List, Optional - -import yaml -from pycocotools.coco import COCO - -import supervisely as sly -from supervisely.app import DataJson -from supervisely.app.widgets import ( - Button, - Card, - Container, - InputNumber, - Stepper, - Switch, - Widget, -) -from supervisely.io.fs import get_file_name_with_ext - - -def read_parameters(train_items_cnt: int): - sly.logger.debug("Reading training parameters...") - if advanced_mode_checkbox.is_checked(): - sly.logger.info("Advanced mode enabled, using custom config from the editor.") - custom_config = advanced_mode_editor.get_value() - else: - sly.logger.info("Advanced mode disabled, reading parameters from the widgets.") - with open(g.default_config_path, "r") as f: - custom_config = f.read() - custom_config = yaml.safe_load(custom_config) - - clip_max_norm = ( - clip_gradient_norm_input.get_value() if clip_gradient_norm_checkbox.is_checked() else -1 - ) - general_params = { - "epoches": number_of_epochs_input.value, - "val_step": validation_interval_input.value, - "checkpoint_step": checkpoints_interval_input.value, - "clip_max_norm": clip_max_norm, - } - - total_steps = general_params["epoches"] * np.ceil( - train_items_cnt / train_batch_size_input.value - ) - - optimizer_params = read_optimizer_parameters() - scheduler_params, scheduler_cls_params = read_scheduler_parameters(total_steps) - - sly.logger.debug(f"General parameters: {general_params}") - sly.logger.debug(f"Optimizer parameters: {optimizer_params}") - sly.logger.debug(f"Scheduler parameters: {scheduler_cls_params}") - - custom_config.update(general_params) - custom_config["optimizer"]["type"] = optimizer_params["optimizer"] - custom_config["optimizer"]["lr"] = optimizer_params["learning_rate"] - custom_config["optimizer"]["weight_decay"] = optimizer_params["weight_decay"] - if optimizer_params.get("momentum"): - custom_config["optimizer"]["momentum"] = optimizer_params["momentum"] - else: - custom_config["optimizer"]["betas"] = [ - optimizer_params["beta1"], - optimizer_params["beta2"], - ] - - # Set input_size - w, h = input_size_input.get_value() - for op in custom_config["train_dataloader"]["dataset"]["transforms"]["ops"]: - if op["type"] == "Resize": - op["size"] = [w, h] - for op in custom_config["val_dataloader"]["dataset"]["transforms"]["ops"]: - if op["type"] == "Resize": - op["size"] = [w, h] - if "HybridEncoder" in custom_config: - custom_config["HybridEncoder"]["eval_spatial_size"] = [w, h] - else: - custom_config["HybridEncoder"] = {"eval_spatial_size": [w, h]} - if "RTDETRTransformer" in custom_config: - custom_config["RTDETRTransformer"]["eval_spatial_size"] = [w, h] - else: - custom_config["RTDETRTransformer"] = {"eval_spatial_size": [w, h]} - - custom_config["train_dataloader"]["batch_size"] = train_batch_size_input.value - custom_config["val_dataloader"]["batch_size"] = val_batch_size_input.value - custom_config["train_dataloader"]["num_workers"] = utils.get_num_workers( - train_batch_size_input.value - ) - custom_config["val_dataloader"]["num_workers"] = utils.get_num_workers( - val_batch_size_input.value - ) - - # LR scheduler - if scheduler_params["type"] == "Without scheduler": - custom_config["lr_scheduler"] = None - else: - custom_config["lr_scheduler"] = scheduler_cls_params - - if scheduler_params["enable_warmup"]: - custom_config["lr_warmup"] = { - "type": "LinearLR", - "total_iters": scheduler_params["warmup_iterations"], - "start_factor": 0.001, - "end_factor": 1.0, - } - - return custom_config - - -def prepare_config(custom_config: Dict[str, Any]): - if g.model_mode == g.MODEL_MODES[0]: - model_name = g.train_mode.pretrained[0] - arch = model_name.split("_coco")[0] - config_name = f"{arch}_6x_coco" - sly.logger.info(f"Model name: {model_name}, arch: {arch}, config_name: {config_name}") - else: - model_name = get_file_name_with_ext(g.train_mode.custom) - config_name = "custom" - sly.logger.info(f"Model name: {model_name}, config_name: {config_name}") - - if g.model_mode == g.MODEL_MODES[0]: - custom_config["__include__"] = [f"{config_name}.yml"] - else: - custom_config["__include__"] = [ - "../dataset/coco_detection.yml", - "../runtime.yml", - "./include/dataloader.yml", - "./include/optimizer.yml", - "./include/rtdetr_r50vd.yml", - ] - custom_config["remap_mscoco_category"] = False - custom_config["num_classes"] = len(g.selected_classes) - custom_config["train_dataloader"]["dataset"]["img_folder"] = f"{g.train_dataset_path}/img" - custom_config["train_dataloader"]["dataset"][ - "ann_file" - ] = f"{g.train_dataset_path}/coco_anno.json" - custom_config["val_dataloader"]["dataset"]["img_folder"] = f"{g.val_dataset_path}/img" - custom_config["val_dataloader"]["dataset"]["ann_file"] = f"{g.val_dataset_path}/coco_anno.json" - selected_classes = g.selected_classes - custom_config["sly_metadata"] = { - "classes": selected_classes, - "project_id": g.PROJECT_ID, - "project_name": g.project_info.name, - "model": model_name, - } - - g.custom_config_path = os.path.join(g.CONFIG_PATHS_DIR, "custom.yml") - with open(g.custom_config_path, "w") as f: - yaml.dump(custom_config, f) - - -def save_config(cfg): - if "__include__" in cfg.yaml_cfg: - cfg.yaml_cfg.pop("__include__") - - output_path = os.path.join(cfg.output_dir, "config.yml") - - with open(output_path, "w") as f: - yaml.dump(cfg.yaml_cfg, f) - - -def get_coco_annotations(dataset: sly.Dataset, meta: sly.ProjectMeta, selected_classes: List[str]): - coco_anno = {"images": [], "categories": [], "annotations": []} - cat2id = {name: i for i, name in enumerate(selected_classes)} - img_id = 1 - ann_id = 1 - for name in dataset.get_items_names(): - ann = dataset.get_ann(name, meta) - img_dict = { - "id": img_id, - "height": ann.img_size[0], - "width": ann.img_size[1], - "file_name": name, - } - coco_anno["images"].append(img_dict) - - for label in ann.labels: - if isinstance(label.geometry, (sly.Bitmap, sly.Polygon)): - rect = label.geometry.to_bbox() - elif isinstance(label.geometry, sly.Rectangle): - rect = label.geometry - else: - continue - class_name = label.obj_class.name - if class_name not in selected_classes: - continue - x, y, x2, y2 = rect.left, rect.top, rect.right, rect.bottom - ann_dict = { - "id": ann_id, - "image_id": img_id, - "category_id": cat2id[class_name], - "bbox": [x, y, x2 - x, y2 - y], - "area": (x2 - x) * (y2 - y), - "iscrowd": 0, - } - coco_anno["annotations"].append(ann_dict) - ann_id += 1 - - img_id += 1 - - coco_anno["categories"] = [{"id": i, "name": name} for name, i in cat2id.items()] - # Test: - coco_api = COCO() - coco_api.dataset = coco_anno - coco_api.createIndex() - return coco_anno