CoolFace
Apppublic

chendl/compositional_test

sourceHugging Faceupdated 3y agoView on Hugging Face
1likes
run_semantic_segmentation_no_trainer.py692 linesDownload Raw Back to semantic-segmentation
1# coding=utf-82# Copyright 2022 The HuggingFace Inc. team. All rights reserved.3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8#     http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.15""" Finetuning any ๐Ÿค— Transformers model supported by AutoModelForSemanticSegmentation for semantic segmentation."""16 17import argparse18import json19import math20import os21import random22from pathlib import Path23 24import datasets25import evaluate26import numpy as np27import torch28from accelerate import Accelerator29from accelerate.logging import get_logger30from accelerate.utils import set_seed31from datasets import load_dataset32from huggingface_hub import Repository, create_repo, hf_hub_download33from PIL import Image34from torch.utils.data import DataLoader35from torchvision import transforms36from torchvision.transforms import functional37from tqdm.auto import tqdm38 39import transformers40from transformers import (41    AutoConfig,42    AutoImageProcessor,43    AutoModelForSemanticSegmentation,44    SchedulerType,45    default_data_collator,46    get_scheduler,47)48from transformers.utils import check_min_version, get_full_repo_name, send_example_telemetry49from transformers.utils.versions import require_version50 51 52# Will error if the minimal version of Transformers is not installed. Remove at your own risks.53check_min_version("4.28.0")54 55logger = get_logger(__name__)56 57require_version("datasets>=2.0.0", "To fix: pip install -r examples/pytorch/semantic-segmentation/requirements.txt")58 59 60def pad_if_smaller(img, size, fill=0):61    min_size = min(img.size)62    if min_size < size:63        original_width, original_height = img.size64        pad_height = size - original_height if original_height < size else 065        pad_width = size - original_width if original_width < size else 066        img = functional.pad(img, (0, 0, pad_width, pad_height), fill=fill)67    return img68 69 70class Compose:71    def __init__(self, transforms):72        self.transforms = transforms73 74    def __call__(self, image, target):75        for t in self.transforms:76            image, target = t(image, target)77        return image, target78 79 80class Identity:81    def __init__(self):82        pass83 84    def __call__(self, image, target):85        return image, target86 87 88class Resize:89    def __init__(self, size):90        self.size = size91 92    def __call__(self, image, target):93        image = functional.resize(image, self.size)94        target = functional.resize(target, self.size, interpolation=transforms.InterpolationMode.NEAREST)95        return image, target96 97 98class RandomResize:99    def __init__(self, min_size, max_size=None):100        self.min_size = min_size101        if max_size is None:102            max_size = min_size103        self.max_size = max_size104 105    def __call__(self, image, target):106        size = random.randint(self.min_size, self.max_size)107        image = functional.resize(image, size)108        target = functional.resize(target, size, interpolation=transforms.InterpolationMode.NEAREST)109        return image, target110 111 112class RandomCrop:113    def __init__(self, size):114        self.size = size115 116    def __call__(self, image, target):117        image = pad_if_smaller(image, self.size)118        target = pad_if_smaller(target, self.size, fill=255)119        crop_params = transforms.RandomCrop.get_params(image, (self.size, self.size))120        image = functional.crop(image, *crop_params)121        target = functional.crop(target, *crop_params)122        return image, target123 124 125class RandomHorizontalFlip:126    def __init__(self, flip_prob):127        self.flip_prob = flip_prob128 129    def __call__(self, image, target):130        if random.random() < self.flip_prob:131            image = functional.hflip(image)132            target = functional.hflip(target)133        return image, target134 135 136class PILToTensor:137    def __call__(self, image, target):138        image = functional.pil_to_tensor(image)139        target = torch.as_tensor(np.array(target), dtype=torch.int64)140        return image, target141 142 143class ConvertImageDtype:144    def __init__(self, dtype):145        self.dtype = dtype146 147    def __call__(self, image, target):148        image = functional.convert_image_dtype(image, self.dtype)149        return image, target150 151 152class Normalize:153    def __init__(self, mean, std):154        self.mean = mean155        self.std = std156 157    def __call__(self, image, target):158        image = functional.normalize(image, mean=self.mean, std=self.std)159        return image, target160 161 162class ReduceLabels:163    def __call__(self, image, target):164        if not isinstance(target, np.ndarray):165            target = np.array(target).astype(np.uint8)166        # avoid using underflow conversion167        target[target == 0] = 255168        target = target - 1169        target[target == 254] = 255170 171        target = Image.fromarray(target)172        return image, target173 174 175def parse_args():176    parser = argparse.ArgumentParser(description="Finetune a transformers model on a text classification task")177    parser.add_argument(178        "--model_name_or_path",179        type=str,180        help="Path to a pretrained model or model identifier from huggingface.co/models.",181        default="nvidia/mit-b0",182    )183    parser.add_argument(184        "--dataset_name",185        type=str,186        help="Name of the dataset on the hub.",187        default="segments/sidewalk-semantic",188    )189    parser.add_argument(190        "--reduce_labels",191        action="store_true",192        help="Whether or not to reduce all labels by 1 and replace background by 255.",193    )194    parser.add_argument(195        "--train_val_split",196        type=float,197        default=0.15,198        help="Fraction of the dataset to be used for validation.",199    )200    parser.add_argument(201        "--cache_dir",202        type=str,203        help="Path to a folder in which the model and dataset will be cached.",204    )205    parser.add_argument(206        "--use_auth_token",207        action="store_true",208        help="Whether to use an authentication token to access the model repository.",209    )210    parser.add_argument(211        "--per_device_train_batch_size",212        type=int,213        default=8,214        help="Batch size (per device) for the training dataloader.",215    )216    parser.add_argument(217        "--per_device_eval_batch_size",218        type=int,219        default=8,220        help="Batch size (per device) for the evaluation dataloader.",221    )222    parser.add_argument(223        "--learning_rate",224        type=float,225        default=5e-5,226        help="Initial learning rate (after the potential warmup period) to use.",227    )228    parser.add_argument(229        "--adam_beta1",230        type=float,231        default=0.9,232        help="Beta1 for AdamW optimizer",233    )234    parser.add_argument(235        "--adam_beta2",236        type=float,237        default=0.999,238        help="Beta2 for AdamW optimizer",239    )240    parser.add_argument(241        "--adam_epsilon",242        type=float,243        default=1e-8,244        help="Epsilon for AdamW optimizer",245    )246    parser.add_argument("--num_train_epochs", type=int, default=3, help="Total number of training epochs to perform.")247    parser.add_argument(248        "--max_train_steps",249        type=int,250        default=None,251        help="Total number of training steps to perform. If provided, overrides num_train_epochs.",252    )253    parser.add_argument(254        "--gradient_accumulation_steps",255        type=int,256        default=1,257        help="Number of updates steps to accumulate before performing a backward/update pass.",258    )259    parser.add_argument(260        "--lr_scheduler_type",261        type=SchedulerType,262        default="polynomial",263        help="The scheduler type to use.",264        choices=["linear", "cosine", "cosine_with_restarts", "polynomial", "constant", "constant_with_warmup"],265    )266    parser.add_argument(267        "--num_warmup_steps", type=int, default=0, help="Number of steps for the warmup in the lr scheduler."268    )269    parser.add_argument("--output_dir", type=str, default=None, help="Where to store the final model.")270    parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.")271    parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.")272    parser.add_argument(273        "--hub_model_id", type=str, help="The name of the repository to keep in sync with the local `output_dir`."274    )275    parser.add_argument("--hub_token", type=str, help="The token to use to push to the Model Hub.")276    parser.add_argument(277        "--checkpointing_steps",278        type=str,279        default=None,280        help="Whether the various states should be saved at the end of every n steps, or 'epoch' for each epoch.",281    )282    parser.add_argument(283        "--resume_from_checkpoint",284        type=str,285        default=None,286        help="If the training should continue from a checkpoint folder.",287    )288    parser.add_argument(289        "--with_tracking",290        required=False,291        action="store_true",292        help="Whether to enable experiment trackers for logging.",293    )294    parser.add_argument(295        "--report_to",296        type=str,297        default="all",298        help=(299            'The integration to report the results and logs to. Supported platforms are `"tensorboard"`,'300            ' `"wandb"`, `"comet_ml"` and `"clearml"`. Use `"all"` (default) to report to all integrations.'301            "Only applicable when `--with_tracking` is passed."302        ),303    )304    args = parser.parse_args()305 306    # Sanity checks307    if args.push_to_hub or args.with_tracking:308        if args.output_dir is None:309            raise ValueError(310                "Need an `output_dir` to create a repo when `--push_to_hub` or `with_tracking` is specified."311            )312 313    if args.output_dir is not None:314        os.makedirs(args.output_dir, exist_ok=True)315 316    return args317 318 319def main():320    args = parse_args()321 322    # Sending telemetry. Tracking the example usage helps us better allocate resources to maintain them. The323    # information sent is the one passed as arguments along with your Python/PyTorch versions.324    send_example_telemetry("run_semantic_segmentation_no_trainer", args)325 326    # Initialize the accelerator. We will let the accelerator handle device placement for us in this example.327    # If we're using tracking, we also need to initialize it here and it will by default pick up all supported trackers328    # in the environment329    accelerator_log_kwargs = {}330 331    if args.with_tracking:332        accelerator_log_kwargs["log_with"] = args.report_to333        accelerator_log_kwargs["logging_dir"] = args.output_dir334 335    accelerator = Accelerator(gradient_accumulation_steps=args.gradient_accumulation_steps, **accelerator_log_kwargs)336 337    logger.info(accelerator.state, main_process_only=False)338    if accelerator.is_local_main_process:339        datasets.utils.logging.set_verbosity_warning()340        transformers.utils.logging.set_verbosity_info()341    else:342        datasets.utils.logging.set_verbosity_error()343        transformers.utils.logging.set_verbosity_error()344 345    # If passed along, set the training seed now.346    # We set device_specific to True as we want different data augmentation per device.347    if args.seed is not None:348        set_seed(args.seed, device_specific=True)349 350    # Handle the repository creation351    if accelerator.is_main_process:352        if args.push_to_hub:353            if args.hub_model_id is None:354                repo_name = get_full_repo_name(Path(args.output_dir).name, token=args.hub_token)355            else:356                repo_name = args.hub_model_id357            create_repo(repo_name, exist_ok=True, token=args.hub_token)358            repo = Repository(args.output_dir, clone_from=repo_name, token=args.hub_token)359 360            with open(os.path.join(args.output_dir, ".gitignore"), "w+") as gitignore:361                if "step_*" not in gitignore:362                    gitignore.write("step_*\n")363                if "epoch_*" not in gitignore:364                    gitignore.write("epoch_*\n")365        elif args.output_dir is not None:366            os.makedirs(args.output_dir, exist_ok=True)367    accelerator.wait_for_everyone()368 369    # Load dataset370    # In distributed training, the load_dataset function guarantees that only one local process can concurrently371    # download the dataset.372    # TODO support datasets from local folders373    dataset = load_dataset(args.dataset_name, cache_dir=args.cache_dir)374 375    # Rename column names to standardized names (only "image" and "label" need to be present)376    if "pixel_values" in dataset["train"].column_names:377        dataset = dataset.rename_columns({"pixel_values": "image"})378    if "annotation" in dataset["train"].column_names:379        dataset = dataset.rename_columns({"annotation": "label"})380 381    # If we don't have a validation split, split off a percentage of train as validation.382    args.train_val_split = None if "validation" in dataset.keys() else args.train_val_split383    if isinstance(args.train_val_split, float) and args.train_val_split > 0.0:384        split = dataset["train"].train_test_split(args.train_val_split)385        dataset["train"] = split["train"]386        dataset["validation"] = split["test"]387 388    # Prepare label mappings.389    # We'll include these in the model's config to get human readable labels in the Inference API.390    if args.dataset_name == "scene_parse_150":391        repo_id = "huggingface/label-files"392        filename = "ade20k-id2label.json"393    else:394        repo_id = args.dataset_name395        filename = "id2label.json"396    id2label = json.load(open(hf_hub_download(repo_id, filename, repo_type="dataset"), "r"))397    id2label = {int(k): v for k, v in id2label.items()}398    label2id = {v: k for k, v in id2label.items()}399 400    # Load pretrained model and image processor401    config = AutoConfig.from_pretrained(args.model_name_or_path, id2label=id2label, label2id=label2id)402    image_processor = AutoImageProcessor.from_pretrained(args.model_name_or_path)403    model = AutoModelForSemanticSegmentation.from_pretrained(args.model_name_or_path, config=config)404 405    # Preprocessing the datasets406    # Define torchvision transforms to be applied to each image + target.407    # Not that straightforward in torchvision: https://github.com/pytorch/vision/issues/9408    # Currently based on official torchvision references: https://github.com/pytorch/vision/blob/main/references/segmentation/transforms.py409    if "shortest_edge" in image_processor.size:410        # We instead set the target size as (shortest_edge, shortest_edge) to here to ensure all images are batchable.411        size = (image_processor.size["shortest_edge"], image_processor.size["shortest_edge"])412    else:413        size = (image_processor.size["height"], image_processor.size["width"])414    train_transforms = Compose(415        [416            ReduceLabels() if args.reduce_labels else Identity(),417            RandomCrop(size=size),418            RandomHorizontalFlip(flip_prob=0.5),419            PILToTensor(),420            ConvertImageDtype(torch.float),421            Normalize(mean=image_processor.image_mean, std=image_processor.image_std),422        ]423    )424    # Define torchvision transform to be applied to each image.425    # jitter = ColorJitter(brightness=0.25, contrast=0.25, saturation=0.25, hue=0.1)426    val_transforms = Compose(427        [428            ReduceLabels() if args.reduce_labels else Identity(),429            Resize(size=size),430            PILToTensor(),431            ConvertImageDtype(torch.float),432            Normalize(mean=image_processor.image_mean, std=image_processor.image_std),433        ]434    )435 436    def preprocess_train(example_batch):437        pixel_values = []438        labels = []439        for image, target in zip(example_batch["image"], example_batch["label"]):440            image, target = train_transforms(image.convert("RGB"), target)441            pixel_values.append(image)442            labels.append(target)443 444        encoding = {}445        encoding["pixel_values"] = torch.stack(pixel_values)446        encoding["labels"] = torch.stack(labels)447 448        return encoding449 450    def preprocess_val(example_batch):451        pixel_values = []452        labels = []453        for image, target in zip(example_batch["image"], example_batch["label"]):454            image, target = val_transforms(image.convert("RGB"), target)455            pixel_values.append(image)456            labels.append(target)457 458        encoding = {}459        encoding["pixel_values"] = torch.stack(pixel_values)460        encoding["labels"] = torch.stack(labels)461 462        return encoding463 464    with accelerator.main_process_first():465        train_dataset = dataset["train"].with_transform(preprocess_train)466        eval_dataset = dataset["validation"].with_transform(preprocess_val)467 468    train_dataloader = DataLoader(469        train_dataset, shuffle=True, collate_fn=default_data_collator, batch_size=args.per_device_train_batch_size470    )471    eval_dataloader = DataLoader(472        eval_dataset, collate_fn=default_data_collator, batch_size=args.per_device_eval_batch_size473    )474 475    # Optimizer476    optimizer = torch.optim.AdamW(477        list(model.parameters()),478        lr=args.learning_rate,479        betas=[args.adam_beta1, args.adam_beta2],480        eps=args.adam_epsilon,481    )482 483    # Figure out how many steps we should save the Accelerator states484    checkpointing_steps = args.checkpointing_steps485    if checkpointing_steps is not None and checkpointing_steps.isdigit():486        checkpointing_steps = int(checkpointing_steps)487 488    # Scheduler and math around the number of training steps.489    overrode_max_train_steps = False490    num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)491    if args.max_train_steps is None:492        args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch493        overrode_max_train_steps = True494 495    lr_scheduler = get_scheduler(496        name=args.lr_scheduler_type,497        optimizer=optimizer,498        num_warmup_steps=args.num_warmup_steps * args.gradient_accumulation_steps,499        num_training_steps=args.max_train_steps * args.gradient_accumulation_steps,500    )501 502    # Prepare everything with our `accelerator`.503    model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(504        model, optimizer, train_dataloader, eval_dataloader, lr_scheduler505    )506 507    # We need to recalculate our total training steps as the size of the training dataloader may have changed.508    num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)509    if overrode_max_train_steps:510        args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch511    # Afterwards we recalculate our number of training epochs512    args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)513 514    # Instantiate metric515    metric = evaluate.load("mean_iou")516 517    # We need to initialize the trackers we use, and also store our configuration.518    # The trackers initializes automatically on the main process.519    if args.with_tracking:520        experiment_config = vars(args)521        # TensorBoard cannot log Enums, need the raw value522        experiment_config["lr_scheduler_type"] = experiment_config["lr_scheduler_type"].value523        accelerator.init_trackers("semantic_segmentation_no_trainer", experiment_config)524 525    # Train!526    total_batch_size = args.per_device_train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps527 528    logger.info("***** Running training *****")529    logger.info(f"  Num examples = {len(train_dataset)}")530    logger.info(f"  Num Epochs = {args.num_train_epochs}")531    logger.info(f"  Instantaneous batch size per device = {args.per_device_train_batch_size}")532    logger.info(f"  Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}")533    logger.info(f"  Gradient Accumulation steps = {args.gradient_accumulation_steps}")534    logger.info(f"  Total optimization steps = {args.max_train_steps}")535    # Only show the progress bar once on each machine.536    progress_bar = tqdm(range(args.max_train_steps), disable=not accelerator.is_local_main_process)537    completed_steps = 0538    starting_epoch = 0539 540    # Potentially load in the weights and states from a previous save541    if args.resume_from_checkpoint:542        if args.resume_from_checkpoint is not None or args.resume_from_checkpoint != "":543            accelerator.print(f"Resumed from checkpoint: {args.resume_from_checkpoint}")544            accelerator.load_state(args.resume_from_checkpoint)545            path = os.path.basename(args.resume_from_checkpoint)546        else:547            # Get the most recent checkpoint548            dirs = [f.name for f in os.scandir(os.getcwd()) if f.is_dir()]549            dirs.sort(key=os.path.getctime)550            path = dirs[-1]  # Sorts folders by date modified, most recent checkpoint is the last551        # Extract `epoch_{i}` or `step_{i}`552        training_difference = os.path.splitext(path)[0]553 554        if "epoch" in training_difference:555            starting_epoch = int(training_difference.replace("epoch_", "")) + 1556            resume_step = None557        else:558            resume_step = int(training_difference.replace("step_", ""))559            starting_epoch = resume_step // len(train_dataloader)560            resume_step -= starting_epoch * len(train_dataloader)561 562    for epoch in range(starting_epoch, args.num_train_epochs):563        if args.with_tracking:564            total_loss = 0565        model.train()566        for step, batch in enumerate(train_dataloader):567            # We need to skip steps until we reach the resumed step568            if args.resume_from_checkpoint and epoch == starting_epoch:569                if resume_step is not None and step < resume_step:570                    completed_steps += 1571                    continue572 573            with accelerator.accumulate(model):574                outputs = model(**batch)575                loss = outputs.loss576                # We keep track of the loss at each epoch577                if args.with_tracking:578                    total_loss += loss.detach().float()579                accelerator.backward(loss)580                optimizer.step()581                lr_scheduler.step()582                optimizer.zero_grad()583 584            # Checks if the accelerator has performed an optimization step behind the scenes585            if accelerator.sync_gradients:586                progress_bar.update(1)587                completed_steps += 1588 589            if isinstance(checkpointing_steps, int):590                if completed_steps % checkpointing_steps == 0:591                    output_dir = f"step_{completed_steps }"592                    if args.output_dir is not None:593                        output_dir = os.path.join(args.output_dir, output_dir)594                    accelerator.save_state(output_dir)595 596                    if args.push_to_hub and epoch < args.num_train_epochs - 1:597                        accelerator.wait_for_everyone()598                        unwrapped_model = accelerator.unwrap_model(model)599                        unwrapped_model.save_pretrained(600                            args.output_dir,601                            is_main_process=accelerator.is_main_process,602                            save_function=accelerator.save,603                        )604                        if accelerator.is_main_process:605                            image_processor.save_pretrained(args.output_dir)606                            repo.push_to_hub(607                                commit_message=f"Training in progress {completed_steps} steps",608                                blocking=False,609                                auto_lfs_prune=True,610                            )611 612            if completed_steps >= args.max_train_steps:613                break614 615        logger.info("***** Running evaluation *****")616        model.eval()617        for step, batch in enumerate(tqdm(eval_dataloader, disable=not accelerator.is_local_main_process)):618            with torch.no_grad():619                outputs = model(**batch)620 621            upsampled_logits = torch.nn.functional.interpolate(622                outputs.logits, size=batch["labels"].shape[-2:], mode="bilinear", align_corners=False623            )624            predictions = upsampled_logits.argmax(dim=1)625 626            predictions, references = accelerator.gather_for_metrics((predictions, batch["labels"]))627 628            metric.add_batch(629                predictions=predictions,630                references=references,631            )632 633        eval_metrics = metric.compute(634            num_labels=len(id2label),635            ignore_index=255,636            reduce_labels=False,  # we've already reduced the labels before637        )638        logger.info(f"epoch {epoch}: {eval_metrics}")639 640        if args.with_tracking:641            accelerator.log(642                {643                    "mean_iou": eval_metrics["mean_iou"],644                    "mean_accuracy": eval_metrics["mean_accuracy"],645                    "overall_accuracy": eval_metrics["overall_accuracy"],646                    "train_loss": total_loss.item() / len(train_dataloader),647                    "epoch": epoch,648                    "step": completed_steps,649                },650                step=completed_steps,651            )652 653        if args.push_to_hub and epoch < args.num_train_epochs - 1:654            accelerator.wait_for_everyone()655            unwrapped_model = accelerator.unwrap_model(model)656            unwrapped_model.save_pretrained(657                args.output_dir, is_main_process=accelerator.is_main_process, save_function=accelerator.save658            )659            if accelerator.is_main_process:660                image_processor.save_pretrained(args.output_dir)661                repo.push_to_hub(662                    commit_message=f"Training in progress epoch {epoch}", blocking=False, auto_lfs_prune=True663                )664 665        if args.checkpointing_steps == "epoch":666            output_dir = f"epoch_{epoch}"667            if args.output_dir is not None:668                output_dir = os.path.join(args.output_dir, output_dir)669            accelerator.save_state(output_dir)670 671    if args.with_tracking:672        accelerator.end_training()673 674    if args.output_dir is not None:675        accelerator.wait_for_everyone()676        unwrapped_model = accelerator.unwrap_model(model)677        unwrapped_model.save_pretrained(678            args.output_dir, is_main_process=accelerator.is_main_process, save_function=accelerator.save679        )680        if accelerator.is_main_process:681            image_processor.save_pretrained(args.output_dir)682            if args.push_to_hub:683                repo.push_to_hub(commit_message="End of training", auto_lfs_prune=True)684 685            all_results = {f"eval_{k}": v for k, v in eval_metrics.items()}686            with open(os.path.join(args.output_dir, "all_results.json"), "w") as f:687                json.dump(all_results, f)688 689 690if __name__ == "__main__":691    main()692