{"id":17221418,"url":"https://github.com/bloodaxe/pytorch-toolbelt","last_synced_at":"2025-04-29T14:40:06.618Z","repository":{"id":34365649,"uuid":"175851515","full_name":"BloodAxe/pytorch-toolbelt","owner":"BloodAxe","description":"PyTorch extensions for fast R\u0026D prototyping and Kaggle farming","archived":false,"fork":false,"pushed_at":"2025-03-01T18:12:48.000Z","size":3220,"stargazers_count":1535,"open_issues_count":5,"forks_count":123,"subscribers_count":27,"default_branch":"develop","last_synced_at":"2025-04-10T00:07:22.881Z","etag":null,"topics":["augmentation","deep-learning","focal-loss","image-classification","image-processing","image-segmentation","jaccard-loss","kaggle","machine-learning","object-detection","pipeline","python","pytorch","segmentation","test-time-augmentation","tta"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BloodAxe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-15T16:02:49.000Z","updated_at":"2025-04-08T08:21:41.000Z","dependencies_parsed_at":"2023-02-14T07:01:08.873Z","dependency_job_id":"eda315fe-9024-4900-9c42-56a27710305b","html_url":"https://github.com/BloodAxe/pytorch-toolbelt","commit_stats":{"total_commits":1068,"total_committers":7,"mean_commits":"152.57142857142858","dds":"0.0074906367041198685","last_synced_commit":"245e7bb7ee4423b80951e1302d1e95c25048a75a"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BloodAxe%2Fpytorch-toolbelt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BloodAxe%2Fpytorch-toolbelt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BloodAxe%2Fpytorch-toolbelt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BloodAxe%2Fpytorch-toolbelt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BloodAxe","download_url":"https://codeload.github.com/BloodAxe/pytorch-toolbelt/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251520564,"owners_count":21602533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["augmentation","deep-learning","focal-loss","image-classification","image-processing","image-segmentation","jaccard-loss","kaggle","machine-learning","object-detection","pipeline","python","pytorch","segmentation","test-time-augmentation","tta"],"created_at":"2024-10-15T03:55:03.666Z","updated_at":"2025-04-29T14:40:06.596Z","avatar_url":"https://github.com/BloodAxe.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Important Update\n\n![ukraine-flag](docs/480px-Flag_of_Ukraine.jpg)\n\nOn February 24th, 2022, Russia declared war and invaded peaceful Ukraine. \nAfter the annexation of Crimea and the occupation of the Donbas region, Putin's regime decided to destroy Ukrainian nationality.\nUkrainians show fierce resistance and demonstrate to the entire world what it's like to fight for the nation's independence.\n\nUkraine's government launched a website to help russian mothers, wives \u0026 sisters find their beloved ones killed or captured in Ukraine - https://200rf.com \u0026 https://t.me/rf200_now (Telegram channel).\nOur goal is to inform those still in Russia \u0026 Belarus, so they refuse to assault Ukraine. \n\nHelp us get maximum exposure to what is happening in Ukraine, violence, and inhuman acts of terror that the \"Russian World\" has brought to Ukraine. \nThis is a comprehensive Wiki on how you can help end this war: https://how-to-help-ukraine-now.super.site/ \n\nOfficial channels\n* [Official account of the Parliament of Ukraine](https://t.me/verkhovnaradaofukraine)\n* [Ministry of Defence](https://www.facebook.com/MinistryofDefence.UA)\n* [Office of the president](https://www.facebook.com/president.gov.ua)\n* [Cabinet of Ministers of Ukraine](https://www.facebook.com/KabminUA)\n* [Center of strategic communications](https://www.facebook.com/StratcomCentreUA)\n* [Minister of Foreign Affairs of Ukraine](https://twitter.com/DmytroKuleba)\n\nGlory to Ukraine!\n\n\n# Pytorch-toolbelt\n\nA `pytorch-toolbelt` is a Python library with a set of bells and whistles for PyTorch for fast R\u0026D prototyping and Kaggle farming:\n\n## What's inside\n\n* Easy model building using flexible encoder-decoder architecture.\n* Modules: CoordConv, SCSE, Hypercolumn, Depthwise separable convolution and more.\n* GPU-friendly test-time augmentation TTA for segmentation and classification\n* GPU-friendly inference on huge (5000x5000) images\n* Every-day common routines (fix/restore random seed, filesystem utils, metrics)\n* Losses: BinaryFocalLoss, Focal, ReducedFocal, Lovasz, Jaccard and Dice losses, Wing Loss and more.\n* Extras for [Catalyst](https://github.com/catalyst-team/catalyst) library (Visualization of batch predictions, additional metrics) \n\nShowcase: [Catalyst, Albumentations, Pytorch Toolbelt example: Semantic Segmentation @ CamVid](https://colab.research.google.com/drive/1OUPJYU7TzH5Vz1si6FBkooackuIlzaGr#scrollTo=GUWuiO5K3aUm)\n\n# Why\n\nHonest answer is \"I needed a convenient way to re-use code for my Kaggle career\". \nDuring 2018 I achieved a [Kaggle Master](https://www.kaggle.com/bloodaxe) badge and this been a long path. \nVery often I found myself re-using most of the old pipelines over and over again. \nAt some point it crystallized into this repository. \n\nThis lib is not meant to replace catalyst / ignite / fast.ai high-level frameworks. Instead it's designed to complement them.\n\n# Installation\n\n`pip install pytorch_toolbelt`\n\n# How do I ... \n\n## Model creation\n\n### Create Encoder-Decoder U-Net model\n\nBelow a code snippet that creates vanilla U-Net model for binary segmentation. \nBy design, both encoder and decoder produces a list of tensors, from fine (high-resolution, indexed `0`) to coarse (low-resolution) feature maps. \nAccess to all intermediate feature maps is beneficial if you want to apply deep supervision losses on them or encoder-decoder of object detection task, \nwhere access to intermediate feature maps is necessary.\n \n```python\nfrom torch import nn\nfrom pytorch_toolbelt.modules import encoders as E\nfrom pytorch_toolbelt.modules import decoders as D\n\nclass UNet(nn.Module):\n    def __init__(self, input_channels, num_classes):\n        super().__init__()\n        self.encoder = E.UnetEncoder(in_channels=input_channels, out_channels=32, growth_factor=2)\n        self.decoder = D.UNetDecoder(self.encoder.channels, decoder_features=32)\n        self.logits = nn.Conv2d(self.decoder.channels[0], num_classes, kernel_size=1)\n\n    def forward(self, x):\n        x = self.encoder(x)\n        x = self.decoder(x)\n        return self.logits(x[0])\n```\n\n### Create Encoder-Decoder FPN model with pretrained encoder\n\nSimilarly to previous example, you can change decoder to FPN with contatenation. \n\n ```python\nfrom torch import nn\nfrom pytorch_toolbelt.modules import encoders as E\nfrom pytorch_toolbelt.modules import decoders as D\n\nclass SEResNeXt50FPN(nn.Module):\n    def __init__(self, num_classes, fpn_channels):\n        super().__init__()\n        self.encoder = E.SEResNeXt50Encoder()\n        self.decoder = D.FPNCatDecoder(self.encoder.channels, fpn_channels)\n        self.logits = nn.Conv2d(self.decoder.channels[0], num_classes, kernel_size=1)\n\n    def forward(self, x):\n        x = self.encoder(x)\n        x = self.decoder(x)\n        return self.logits(x[0])\n```\n\n### Change number of input channels for the Encoder\n\nAll encoders from `pytorch_toolbelt` supports changing number of input channels. Simply call `encoder.change_input_channels(num_channels)` and first convolution layer will be changed.\nWhenever possible, existing weights of convolutional layer will be re-used (in case new number of channels is greater than default, new weight tensor will be padded with randomly-initialized weigths).\nClass method returns `self`, so this call can be chained.\n\n\n```python\nfrom pytorch_toolbelt.modules import encoders as E\n\nencoder = E.SEResnet101Encoder()\nencoder = encoder.change_input_channels(6)\n```\n\n\n## Misc\n\n\n## Count number of parameters in encoder/decoder and other modules\n\nWhen designing a model and optimizing number of features in neural network, I found it's quite useful to print number of parameters in high-level blocks (like `encoder` and `decoder`).\nHere is how to do it with `pytorch_toolbelt`:\n\n\n```python\nfrom torch import nn\nfrom pytorch_toolbelt.modules import encoders as E\nfrom pytorch_toolbelt.modules import decoders as D\nfrom pytorch_toolbelt.utils import count_parameters\n\nclass SEResNeXt50FPN(nn.Module):\n    def __init__(self, num_classes, fpn_channels):\n        super().__init__()\n        self.encoder = E.SEResNeXt50Encoder()\n        self.decoder = D.FPNCatDecoder(self.encoder.channels, fpn_channels)\n        self.logits = nn.Conv2d(self.decoder.channels[0], num_classes, kernel_size=1)\n\n    def forward(self, x):\n        x = self.encoder(x)\n        x = self.decoder(x)\n        return self.logits(x[0])\n\nnet = SEResNeXt50FPN(1, 128)\nprint(count_parameters(net))\n# Prints {'total': 34232561, 'trainable': 34232561, 'encoder': 25510896, 'decoder': 8721536, 'logits': 129}\n\n```\n\n### Compose multiple losses\n\nThere are multiple ways to combine multiple losses, and high-level DL frameworks like Catalyst offers way more flexible way to achieve this, but here's 100%-pure PyTorch implementation of mine:\n\n```python\nfrom pytorch_toolbelt import losses as L\n\n# Creates a loss function that is a weighted sum of focal loss \n# and lovasz loss with weigths 1.0 and 0.5 accordingly.\nloss = L.JointLoss(L.FocalLoss(), L.LovaszLoss(), 1.0, 0.5)\n```\n\n\n## TTA / Inferencing\n\n### Apply Test-time augmentation (TTA) for the model\n\nTest-time augmetnation (TTA) can be used in both training and testing phases. \n\n```python\nfrom pytorch_toolbelt.inference import tta\n\nmodel = UNet()\n\n# Truly functional TTA for image classification using horizontal flips:\nlogits = tta.fliplr_image2label(model, input)\n\n# Truly functional TTA for image segmentation using D4 augmentation:\nlogits = tta.d4_image2mask(model, input)\n\n```\n\n### Inference on huge images:\n\nQuite often, there is a need to perform image segmentation for enormously big image (5000px and more). There are a few problems with such a big pixel arrays:\n 1. There are size limitations on maximum size of CUDA tensors (Concrete numbers depends on driver and GPU version)\n 2. Heavy CNNs architectures may eat up all available GPU memory with ease when inferencing relatively small 1024x1024 images, leaving no room to bigger image resolution.\n  \nOne of the solutions is to slice input image into tiles (optionally overlapping) and feed each through model and concatenate the results back. \nIn this way you can guarantee upper limit of GPU ram usage, while keeping ability to process arbitrary-sized images on GPU.\n  \n\n```python\nimport numpy as np\nfrom torch.utils.data import DataLoader\nimport cv2\n\nfrom pytorch_toolbelt.inference.tiles import ImageSlicer, CudaTileMerger\nfrom pytorch_toolbelt.utils.torch_utils import tensor_from_rgb_image, to_numpy\n\n\nimage = cv2.imread('really_huge_image.jpg')\nmodel = get_model(...)\n\n# Cut large image into overlapping tiles\ntiler = ImageSlicer(image.shape, tile_size=(512, 512), tile_step=(256, 256))\n\n# HCW -\u003e CHW. Optionally, do normalization here\ntiles = [tensor_from_rgb_image(tile) for tile in tiler.split(image)]\n\n# Allocate a CUDA buffer for holding entire mask\nmerger = CudaTileMerger(tiler.target_shape, 1, tiler.weight)\n\n# Run predictions for tiles and accumulate them\nfor tiles_batch, coords_batch in DataLoader(list(zip(tiles, tiler.crops)), batch_size=8, pin_memory=True):\n    tiles_batch = tiles_batch.float().cuda()\n    pred_batch = model(tiles_batch)\n\n    merger.integrate_batch(pred_batch, coords_batch)\n\n# Normalize accumulated mask and convert back to numpy\nmerged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype(np.uint8)\nmerged_mask = tiler.crop_to_orignal_size(merged_mask)\n```\n\n## Advanced examples\n\n1. [Inria Sattelite Segmentation](https://github.com/BloodAxe/Catalyst-Inria-Segmentation-Example)\n1. [CamVid Semantic Segmentation](https://github.com/BloodAxe/Catalyst-CamVid-Segmentation-Example)\n\n\n## Citation\n\n```\n@misc{Khvedchenya_Eugene_2019_PyTorch_Toolbelt,\n  author = {Khvedchenya, Eugene},\n  title = {PyTorch Toolbelt},\n  year = {2019},\n  publisher = {GitHub},\n  journal = {GitHub repository},\n  howpublished = {\\url{https://github.com/BloodAxe/pytorch-toolbelt}},\n  commit = {cc5e9973cdb0dcbf1c6b6e1401bf44b9c69e13f3}\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbloodaxe%2Fpytorch-toolbelt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbloodaxe%2Fpytorch-toolbelt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbloodaxe%2Fpytorch-toolbelt/lists"}