pytorch-toolbelt

BloodAxeMIT长期未更新

一套 PyTorch 扩展工具集,提供数据增强、损失函数、训练流水线等常用组件,帮你在做图像分割、检测和分类的原型研发与 Kaggle 比赛时少写重复代码。

PyTorch extensions for fast R&D prototyping and Kaggle farming

0.8.0发布于 2024-11-21 20:06 UTC1,575star126fork同步于 2026-11-07 07:33 UTC

怎么上手

由 README 提炼,执行前请确认
pip install pytorch_toolbelt
硬件要求按 README 说明

README 未说明具体显存要求;大图推理示例使用 CUDA 张量缓冲,需要 NVIDIA GPU 和 CUDA 环境。

需要 CUDA
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

PyTorch 扩展工具库,帮你快速搭模型、跑 TTA 和大图推理,省去重复造轮子

一套 PyTorch 扩展工具集,提供数据增强、损失函数、训练流水线等常用组件,帮你在做图像分割、检测和分类的原型研发与 Kaggle 比赛时少写重复代码。

核心能力

  • 提供编码器-解码器架构,可自由组合 U-Net、FPN 等模型
  • 内置 CoordConv、SCSE、Hypercolumn、深度可分离卷积等模块
  • 支持 GPU 上的 TTA,含分类水平翻转和分割 D4 增强
  • 用切片加 CUDA 缓冲合并,在 GPU 上推理 5000x5000 大图
  • 提供 Focal、Lovasz、Dice、Jaccard、Wing 等多种损失函数

容易踩的坑

大图推理依赖 CUDA 缓冲,无 GPU 时该流程无法直接运行;该库定位为补充而非替代 Catalyst、Ignite、fast.ai 等高层框架;README 未说明依赖版本约束,建议自行锁定 torch 版本。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本0.8.0
  1. 0.8.0Pytorch Toolbelt 0.8.0最新正式版2024-11-21 · 2024-11-21 20:06 UTC

    What's Changed

    New Contributors

    Full Changelog: https://github.com/BloodAxe/pytorch-toolbelt/compare/0.7.0...0.8.0

  2. 0.7.0Pytorch Toolbelt 0.7.02023-08-19 · 2023-08-19 14:26 UTC
    查看更新说明

    New stuff

    • All encoders & decoders & heads are now inherit from HasOutputFeaturesSpecification interface to query number of output channels and strides this module outputs.

    • New loss class QualityFocalLoss from https://arxiv.org/abs/2006.04388

    • New function pad_tensor_to_size -A generic padding function for N-dimensional tensors [B,C, ...] shape.

    • Added DropPath layer (aka DropConnect)

    • Pretrained weights for SegFormer backbones

    • first_class_background_init for initializing last output convolution/linear block to have zeros in weights and bias layer set to [logit(bg_prob), logit(1-bg_prob), ...]

    • New function instantiate_normalization_block to create normalization layer by name. This is used in some decoder layers / heads.

    • Improvements

    • Improve numeric accuracy of focal_loss_with_logits function by explicitly disabling AMP autocast for this function and casting preds & targets to float32.

    • MultiscaleTTA now allows setting interpolation mode and align_corners for resizing input and predictions.

    • BinaryFocalLoss now has __repr__

    • name_for_stride now accepts stride argument to be None. In this case the function is noop and returns input argument name.

    • RandomSubsetDataset now takes optional weights argument to select samples with given probability.

    Bugfixes

    • Implementation of get_collate_fn for RandomSubsetDataset is not correct and returns collate function instead of calling it

    Breaking Changes

    • Signature of decoders changes to require first argument input_spec to be of type FeatureMapsSpecification.

    • Rewritten BiFPN decoder to support arbitrary number of input feature maps, user-defined normalization & activation & BiFPN block.

    • Rewritten UNetDecoder to allow setting upsample block as string type

    • WeightedLoss and JointLoss classes has been removed. If your code was using these classes here they are - copy paste them to your project and live happily, but I strongly suggest to use modern deep learning frameworks that support defining losses from configuration files.

    class WeightedLoss(_Loss):
        """Wrapper class around loss function that applies weighted with fixed factor.
        This class helps to balance multiple losses if they have different scales
        """
    
        def __init__(self, loss, weight=1.0):
            super().__init__()
            self.loss = loss
            self.weight = weight
    
        def forward(self, *input):
            return self.loss(*input) * self.weight
    
    
    class JointLoss(_Loss):
        """
        Wrap two loss functions into one. This class computes a weighted sum of two losses.
        """
    
        def __init__(self, first: nn.Module, second: nn.Module, first_weight=1.0, second_weight=1.0):
            super().__init__()
            self.first = WeightedLoss(first, first_weight)
            self.second = WeightedLoss(second, second_weight)
    
        def forward(self, *input):
            return self.first(*input) + self.second(*input)
    
  3. 0.6.2Pytorch Toolbelt 0.6.22022-12-25 · 2022-12-25 21:39 UTC

    这个版本没有写更新说明。

  4. 0.6.1Pytorch Toolbelt 0.6.12022-10-25 · 2022-10-25 15:59 UTC
    查看更新说明

    Pytorch Toolbelt 0.6.1

    • Fixes to CI actions
    • Adding support for python 3.10
    • Bugfix in DatasetMeanStdCalculator when mask argument was used
  5. 0.6.0PyTorch Toolbelt 0.6.02022-10-20 · 2022-10-20 21:13 UTC
    查看更新说明

    Breaking Changes

    All catalyst-related callbacks are moved to fork of the Catalyst library

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Important Update

    ukraine-flag

    On February 24th, 2022, Russia declared war and invaded peaceful Ukraine. After the annexation of Crimea and the occupation of the Donbas region, Putin's regime decided to destroy Ukrainian nationality. Ukrainians show fierce resistance and demonstrate to the entire world what it's like to fight for the nation's independence.

    Ukraine's government launched a website to help russian mothers, wives & sisters find their beloved ones killed or captured in Ukraine - https://200rf.com & https://t.me/rf200_now (Telegram channel). Our goal is to inform those still in Russia & Belarus, so they refuse to assault Ukraine.

    Help us get maximum exposure to what is happening in Ukraine, violence, and inhuman acts of terror that the "Russian World" has brought to Ukraine. This is a comprehensive Wiki on how you can help end this war: https://how-to-help-ukraine-now.super.site/

    Official channels

    Glory to Ukraine!

    Pytorch-toolbelt

    A pytorch-toolbelt is a Python library with a set of bells and whistles for PyTorch for fast R&D prototyping and Kaggle farming:

    What's inside

    • Easy model building using flexible encoder-decoder architecture.
    • Modules: CoordConv, SCSE, Hypercolumn, Depthwise separable convolution and more.
    • GPU-friendly test-time augmentation TTA for segmentation and classification
    • GPU-friendly inference on huge (5000x5000) images
    • Every-day common routines (fix/restore random seed, filesystem utils, metrics)
    • Losses: BinaryFocalLoss, Focal, ReducedFocal, Lovasz, Jaccard and Dice losses, Wing Loss and more.
    • Extras for Catalyst library (Visualization of batch predictions, additional metrics)

    Showcase: Catalyst, Albumentations, Pytorch Toolbelt example: Semantic Segmentation @ CamVid

    Why

    Honest answer is "I needed a convenient way to re-use code for my Kaggle career". During 2018 I achieved a Kaggle Master badge and this been a long path. Very often I found myself re-using most of the old pipelines over and over again. At some point it crystallized into this repository.

    This lib is not meant to replace catalyst / ignite / fast.ai high-level frameworks. Instead it's designed to complement them.

    Installation

    pip install pytorch_toolbelt

    How do I ...

    Model creation

    Create Encoder-Decoder U-Net model

    Below a code snippet that creates vanilla U-Net model for binary segmentation. By design, both encoder and decoder produces a list of tensors, from fine (high-resolution, indexed 0) to coarse (low-resolution) feature maps. Access to all intermediate feature maps is beneficial if you want to apply deep supervision losses on them or encoder-decoder of object detection task, where access to intermediate feature maps is necessary.

    from torch import nn
    from pytorch_toolbelt.modules import encoders as E
    from pytorch_toolbelt.modules import decoders as D
    
    class UNet(nn.Module):
        def __init__(self, input_channels, num_classes):
            super().__init__()
            self.encoder = E.UnetEncoder(in_channels=input_channels, out_channels=32, growth_factor=2)
            self.decoder = D.UNetDecoder(self.encoder.channels, decoder_features=32)
            self.logits = nn.Conv2d(self.decoder.channels[0], num_classes, kernel_size=1)
    
        def forward(self, x):
            x = self.encoder(x)
            x = self.decoder(x)
            return self.logits(x[0])
    

    Create Encoder-Decoder FPN model with pretrained encoder

    Similarly to previous example, you can change decoder to FPN with contatenation.

    from torch import nn
    from pytorch_toolbelt.modules import encoders as E
    from pytorch_toolbelt.modules import decoders as D
    
    class SEResNeXt50FPN(nn.Module):
       def __init__(self, num_classes, fpn_channels):
           super().__init__()
           self.encoder = E.SEResNeXt50Encoder()
           self.decoder = D.FPNCatDecoder(self.encoder.channels, fpn_channels)
           self.logits = nn.Conv2d(self.decoder.channels[0], num_classes, kernel_size=1)
    
       def forward(self, x):
           x = self.encoder(x)
           x = self.decoder(x)
           return self.logits(x[0])
    

    Change number of input channels for the Encoder

    All 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. Whenever 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). Class method returns self, so this call can be chained.

    from pytorch_toolbelt.modules import encoders as E
    
    encoder = E.SEResnet101Encoder()
    encoder = encoder.change_input_channels(6)
    

    Misc

    Count number of parameters in encoder/decoder and other modules

    When 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). Here is how to do it with pytorch_toolbelt:

    from torch import nn
    from pytorch_toolbelt.modules import encoders as E
    from pytorch_toolbelt.modules import decoders as D
    from pytorch_toolbelt.utils import count_parameters
    
    class SEResNeXt50FPN(nn.Module):
        def __init__(self, num_classes, fpn_channels):
            super().__init__()
            self.encoder = E.SEResNeXt50Encoder()
            self.decoder = D.FPNCatDecoder(self.encoder.channels, fpn_channels)
            self.logits = nn.Conv2d(self.decoder.channels[0], num_classes, kernel_size=1)
    
        def forward(self, x):
            x = self.encoder(x)
            x = self.decoder(x)
            return self.logits(x[0])
    
    net = SEResNeXt50FPN(1, 128)
    print(count_parameters(net))
    # Prints {'total': 34232561, 'trainable': 34232561, 'encoder': 25510896, 'decoder': 8721536, 'logits': 129}
    
    

    Compose multiple losses

    There 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:

    from pytorch_toolbelt import losses as L
    
    # Creates a loss function that is a weighted sum of focal loss 
    # and lovasz loss with weigths 1.0 and 0.5 accordingly.
    loss = L.JointLoss(L.FocalLoss(), L.LovaszLoss(), 1.0, 0.5)
    

    TTA / Inferencing

    Apply Test-time augmentation (TTA) for the model

    Test-time augmetnation (TTA) can be used in both training and testing phases.

    from pytorch_toolbelt.inference import tta
    
    model = UNet()
    
    # Truly functional TTA for image classification using horizontal flips:
    logits = tta.fliplr_image2label(model, input)
    
    # Truly functional TTA for image segmentation using D4 augmentation:
    logits = tta.d4_image2mask(model, input)
    
    

    Inference on huge images:

    Quite 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:

    1. There are size limitations on maximum size of CUDA tensors (Concrete numbers depends on driver and GPU version)
    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.

    One of the solutions is to slice input image into tiles (optionally overlapping) and feed each through model and concatenate the results back. In this way you can guarantee upper limit of GPU ram usage, while keeping ability to process arbitrary-sized images on GPU.

    import numpy as np
    from torch.utils.data import DataLoader
    import cv2
    
    from pytorch_toolbelt.inference.tiles import ImageSlicer, CudaTileMerger
    from pytorch_toolbelt.utils.torch_utils import tensor_from_rgb_image, to_numpy
    
    
    image = cv2.imread('really_huge_image.jpg')
    model = get_model(...)
    
    # Cut large image into overlapping tiles
    tiler = ImageSlicer(image.shape, tile_size=(512, 512), tile_step=(256, 256))
    
    # HCW -> CHW. Optionally, do normalization here
    tiles = [tensor_from_rgb_image(tile) for tile in tiler.split(image)]
    
    # Allocate a CUDA buffer for holding entire mask
    merger = CudaTileMerger(tiler.target_shape, 1, tiler.weight)
    
    # Run predictions for tiles and accumulate them
    for tiles_batch, coords_batch in DataLoader(list(zip(tiles, tiler.crops)), batch_size=8, pin_memory=True):
        tiles_batch = tiles_batch.float().cuda()
        pred_batch = model(tiles_batch)
    
        merger.integrate_batch(pred_batch, coords_batch)
    
    # Normalize accumulated mask and convert back to numpy
    merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype(np.uint8)
    merged_mask = tiler.crop_to_orignal_size(merged_mask)
    

    Advanced examples

    1. Inria Sattelite Segmentation
    2. CamVid Semantic Segmentation

    Citation

    @misc{Khvedchenya_Eugene_2019_PyTorch_Toolbelt,
      author = {Khvedchenya, Eugene},
      title = {PyTorch Toolbelt},
      year = {2019},
      publisher = {GitHub},
      journal = {GitHub repository},
      howpublished = {\url{https://github.com/BloodAxe/pytorch-toolbelt}},
      commit = {cc5e9973cdb0dcbf1c6b6e1401bf44b9c69e13f3}
    }