pytorch多分类的语义分割

基于 segmentation_models.pytorch 实现的多分类的语义分割。

github中的示例没有包含多分类的内容,网上资料比较少,就动手调了一下。

segmentation_models.pytorch\examples\cars segmentation (camvid).ipynb 里写了怎么做单分类的调用,用来检测Camvid数据集中的Car类型。

实现多分类目标,要做的就是训练、测试时支持多分类。主要是把原来用到二维矩阵(图像)的地方,改为三维矩阵。

笔者只有一个GPU,只实践了单GPU的场景。

 

代码结构很清晰:数据加载/增强/训练/验证/测试

具体代码如下:

import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'

import numpy as np
import cv2
import matplotlib.pyplot as plt
DATA_DIR = r'D:\cv_ai\2d\segmentation\segmentation_models.pytorch\examples\data\CamVid'

# load repo with data if it is not exists
if not os.path.exists(DATA_DIR):
    print('Loading data...')
    os.system('git clone https://github.com/alexgkendall/SegNet-Tutorial ./data')
    print('Done!')

x_train_dir = os.path.join(DATA_DIR, 'train')
y_train_dir = os.path.join(DATA_DIR, 'trainannot')

x_valid_dir = os.path.join(DATA_DIR, 'val')
y_valid_dir = os.path.join(DATA_DIR, 'valannot')

x_test_dir = os.path.join(DATA_DIR, 'test')
y_test_dir = os.path.join(DATA_DIR, 'testannot')

# helper function for data visualization
def visualize(**images):
    """PLot images in one row."""
    n = len(images)
    plt.figure(figsize=(16, 5))
    for i, (name, image) in enumerate(images.items()):
        plt.subplot(1, n, i + 1)
        plt.xticks([])
        plt.yticks([])
        plt.title(' '.join(name.split('_')).title())
        plt.imshow(image)
    plt.show()

from torch.utils.data import DataLoader
from torch.utils.data import Dataset as BaseDataset

class Dataset(BaseDataset):
    """CamVid Dataset. Read images, apply augmentation and preprocessing transformations.
    
    Args:
        images_dir (str): path to images folder
        masks_dir (str): path to segmentation masks folder
        class_values (list): values of classes to extract from segmentation mask
        augmentation (albumentations.Compose): data transfromation pipeline 
            (e.g. flip, scale, etc.)
        preprocessing (albumentations.Compose): data preprocessing 
            (e.g. noralization, shape manipulation, etc.)
    
    """
    
    CLASSES = ['sky', 'building', 'pole', 'road', 'pavement', 
               'tree', 'signsymbol', 'fence', 'car', 
               'pedestrian', 'bicyclist', 'unlabelled']
    
    def __init__(
            self, 
            images_dir, 
            masks_dir, 
            classes=None, 
            augmentation=None, 
            preprocessing=None,
    ):
        self.ids = os.listdir(images_dir)
        self.images_fps = [os.path.join(images_dir, image_id) for image_id in self.ids]
        self.masks_fps = [os.path.join(masks_dir, image_id) for image_id in self.ids]
        
        # convert str names to class values on masks
        self.class_values = [self.CLASSES.index(cls.lower()) for cls in classes]
        
        self.augmentation = augmentation
        self.preprocessing = preprocessing
    
    def __getitem__(self, i):
        
        # read data
        image = cv2.imread(self.images_fps[i])
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mask = cv2.imread(self.masks_fps[i], 0)
        
        # extract certain classes from mask (e.g. cars)
        masks = [(mask == v) for v in self.class_values]
        mask = np.stack(masks, axis=-1).astype('float')
        
        # apply augmentations
        if self.augmentation:
            sample = self.augmentation(image=image, mask=mask)
            image, mask = sample['image'], sample['mask']
        
        # apply preprocessing
        if self.preprocessing:
            sample = self.preprocessing(image=image, mask=mask)
            image, mask = sample['image'], sample['mask']
            
        return image, mask
        
    def __len__(self):
        return len(self.ids)



# Lets look at data we have
# 这里选择定义了两种类型:车和行人
dataset = Dataset(x_train_dir, y_train_dir, classes=['car','pedestrian'])

image, mask = dataset[4] # get some sample
visualize(
    image=image, 
    cars_mask=mask[:,:,0].squeeze(),
    ped_mask=mask[:,:,1].squeeze(),
)


# 数据增广
import albumentations as albu
def get_training_augmentation():
    train_transform = [

        albu.HorizontalFlip(p=0.5),

        albu.ShiftScaleRotate(scale_limit=0.5, rotate_limit=0, shift_limit=0.1, p=1, border_mode=0),

        albu.PadIfNeeded(min_height=320, min_width=320, always_apply=True, border_mode=0),
        albu.RandomCrop(height=320, width=320, always_apply=True),

        albu.IAAAdditiveGaussianNoise(p=0.2),
        albu.IAAPerspective(p=0.5),

        albu.OneOf(
            [
                albu.CLAHE(p=1),
                albu.RandomBrightness(p=1),
                albu.RandomGamma(p=1),
            ],
            p=0.9,
        ),

        albu.OneOf(
            [
                albu.IAASharpen(p=1),
                albu.Blur(blur_limit=3, p=1),
                albu.MotionBlur(blur_limit=3, p=1),
            ],
            p=0.9,
        ),

        albu.OneOf(
            [
                albu.RandomContrast(p=1),
                albu.HueSaturationValue(p=1),
            ],
            p=0.9,
        ),
    ]
    return albu.Compose(train_transform)


def get_validation_augmentation():
    """Add paddings to make image shape divisible by 32"""
    test_transform = [
        albu.PadIfNeeded(384, 480)
    ]
    return albu.Compose(test_transform)


def to_tensor(x, **kwargs):
    return x.transpose(2, 0, 1).astype('float32')


def get_preprocessing(preprocessing_fn):
    """Construct preprocessing transform
    
    Args:
        preprocessing_fn (callbale): data normalization function 
            (can be specific for each pretrained neural network)
    Return:
        transform: albumentations.Compose
    
    """
    
    _transform = [
        albu.Lambda(image=preprocessing_fn),
        albu.Lambda(image=to_tensor, mask=to_tensor),
    ]
    return albu.Compose(_transform)

    #### Visualize resulted augmented images and masks

augmented_dataset = Dataset(
    x_train_dir, 
    y_train_dir, 
    augmentation=get_training_augmentation(), 
    classes=['car', 'pedestrian'],
)

# same image with different random transforms
for i in range(1):
    image, mask = augmented_dataset[1]
    visualize(image=image, 
    cars_mask=mask[:,:,0].squeeze(),
    ped_mask=mask[:,:,1].squeeze(),)

import torch
import numpy as np
import segmentation_models_pytorch as smp

ENCODER = 'se_resnext50_32x4d'
ENCODER_WEIGHTS = 'imagenet'
DEVICE = 'cuda'

CLASSES = ['car', 'pedestrian']
ACTIVATION = 'sigmoid'

# create segmentation model with pretrained encoder
model = smp.Unet(
    encoder_name=ENCODER, 
    encoder_weights=ENCODER_WEIGHTS, 
    classes=len(CLASSES), 
    activation=ACTIVATION,
)

preprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)

train_dataset = Dataset(
    x_train_dir, 
    y_train_dir, 
    augmentation=get_training_augmentation(), 
    preprocessing=get_preprocessing(preprocessing_fn),
    classes=CLASSES,
)

valid_dataset = Dataset(
    x_valid_dir, 
    y_valid_dir, 
    augmentation=get_validation_augmentation(), 
    preprocessing=get_preprocessing(preprocessing_fn),
    classes=CLASSES,
)

train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True, num_workers=0)
valid_loader = DataLoader(valid_dataset, batch_size=1, shuffle=False, num_workers=0)

# Dice/F1 score - https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient
# IoU/Jaccard score - https://en.wikipedia.org/wiki/Jaccard_index

loss = smp.utils.losses.BCEDiceLoss(eps=1.)
metrics = [
    smp.utils.metrics.IoUMetric(eps=1.),
    smp.utils.metrics.FscoreMetric(eps=1.),
]

optimizer = torch.optim.Adam([
    {'params': model.decoder.parameters(), 'lr': 1e-4}, 
    
    # decrease lr for encoder in order not to permute 
    # pre-trained weights with large gradients on training start
    {'params': model.encoder.parameters(), 'lr': 1e-6},  
])

# create epoch runners 
# it is a simple loop of iterating over dataloader`s samples
train_epoch = smp.utils.train.TrainEpoch(
    model, 
    loss=loss, 
    metrics=metrics, 
    optimizer=optimizer,
    device=DEVICE,
    verbose=True,
)

valid_epoch = smp.utils.train.ValidEpoch(
    model, 
    loss=loss, 
    metrics=metrics, 
    device=DEVICE,
    verbose=True,
)

# train model for 40 epochs

Train = False
TrainIters = 500 # 40
if Train == True:
    max_score = 0

    for i in range(0, TrainIters):
        
        print('\nEpoch: {}'.format(i))
        train_logs = train_epoch.run(train_loader)
        valid_logs = valid_epoch.run(valid_loader)
        
        # do something (save model, change lr, etc.)
        if max_score < valid_logs['iou']:
            max_score = valid_logs['iou']
            torch.save(model, './best_model_Iter' + str(TrainIters) + '_.pth')
            print('Model saved!')
            
        if i == 25:
            optimizer.param_groups[0]['lr'] = 1e-5
            print('Decrease decoder learning rate to 1e-5!')

# load best saved checkpoint
best_model = torch.load(r'D:\cv_ai\2d\segmentation\segmentation_models.pytorch\best_model_Iter500_.pth')

# 如果模型中有dropout或者batchnorm的话,一定要先将模型设置为eval模式,再保存,否则在用libtorch调用后会出现随机干扰;
best_model.eval()

# 生成一个样本供网络前向传播 forward()
example = torch.rand(1, 3, 384, 480)
# # 使用 torch.jit.trace 生成 torch.jit.ScriptModule 来跟踪
traced_script_module = torch.jit.trace(best_model, example.cuda())
traced_script_module.save("best_model.pt")

# create test dataset
test_dataset = Dataset(
    x_test_dir, 
    y_test_dir, 
    augmentation=get_validation_augmentation(), 
    preprocessing=get_preprocessing(preprocessing_fn),
    classes=CLASSES,
)

test_dataloader = DataLoader(test_dataset)

# evaluate model on test set
test_epoch = smp.utils.train.ValidEpoch(
    model=best_model,
    loss=loss,
    metrics=metrics,
    device=DEVICE,
)

logs = test_epoch.run(test_dataloader)

# test dataset without transformations for image visualization
test_dataset_vis = Dataset(
    x_test_dir, y_test_dir, 
    classes=CLASSES,
)

for i in range(50):
    n = np.random.choice(len(test_dataset))
    
    image_vis = test_dataset_vis[n][0].astype('uint8')
    image, gt_mask = test_dataset[n]
    
    gt_mask_car = gt_mask[0,:,:].squeeze()
    gt_mask_ped = gt_mask[1,:,:].squeeze()
    
    x_tensor = torch.from_numpy(image).to(DEVICE).unsqueeze(0)
    pr_mask = best_model.predict(x_tensor)
    pr_mask_car = pr_mask.squeeze()[0,:,:].cpu().numpy().round()
    pr_mask_ped = pr_mask.squeeze()[1,:,:].cpu().numpy().round()
        
    visualize(
        image=image_vis, 
        ground_truth_mask_car=gt_mask_car, 
        ground_truth_mask_ped=gt_mask_ped,
        predicted_mask_car=pr_mask_car,
        predicted_mask_ped=pr_mask_ped,
    )

camvid数据集下载:https://pan.baidu.com/s/15L0QAE6RMaCA4ZkZD8dbDg   提取码  57ge

验证用的编码器se_resnext50_32x4d下载:https://pan.baidu.com/s/1hu3dW7z0ENLC0P1tY1UYkA  提取码  7489

结果示例:

  • 12
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
### 回答1: PyTorch是一种广泛应用于深度学习的Python库,可以用于许多领域的人工智能应用,包括遥感图像处理。遥感语义分割是指对遥感图像进行像素级别的分类,以识别不同的物体和地物类型。 使用PyTorch进行遥感语义分割需要进行以下步骤: 第一,准备数据集。数据集应包含遥感图像以及每个像素的标签。在语义分割中,标签应用于指定每个像素所表示的地物或物体类别。 第二,构建模型。可以使用PyTorch的高级API,如torch.nn来构建自己的深度学习模型。这些模型通常基于卷积神经网络(CNN),使用卷积、池化以及其他操作来提取图像中的特征并进行分类。 第三,模型训练。在模型训练之前,需要将数据集拆分为训练、验证以及测试子集。在训练期间,使用优化器和损失函数来更新模型权重,并通过验证数据来调整超参数,如学习率和训练批次大小。 第四,模型测试。用测试数据集对模型进行评估,并计算出预测结果的精度和损失值以及其他性能指标。 整个遥感语义分割的流程涉及到数据预处理、模型训练、预测等多个步骤,需要在进行任务之前对任务需求和数据进行深入的了解和研究。其中,PyTorch的优点是具有快速迭代开发和丰富的API支持,同时也可以很好的与其他深度学习框架配合运用。 ### 回答2: PyTorch是一种用于机器学习和深度学习的Python库,它可以协助我们在计算中构建灵活的神经网络。在遥感数据分析中,语义分割是一种用于将像素点分类为不同的对象或场景的技术。它可以将遥感影像转化为具有语义信息的结果,方便进一步的分析及应用。 PyTorch在遥感语义分割中可以发挥巨大作用,它可以以端到端的方式处理大规模的遥感数据。PyTorch中存在许多现成的模型,如U-Net、FCN等,通过简单的调用,我们可以轻松地搭建和训练自己的遥感语义分割模型。在训练过程中,PyTorch可以实现数据增强、模型优化等功能,提高模型的准确率和泛化能力。 此外,PyTorch支持GPU加速,可以大幅提升训练速度和效率,避免了数据规模较大时的运算困难。同时,PyTorch也易于使用和维护,其代码可读性和灵活性是其他深度学习库无法比拟的。 总而言之,PyTorch可以为我们在遥感语义分割中提供强大的工具和支持,使我们能够更轻松地构建、训练和优化自己的模型,快速地获得准确的遥感数据分析结果。 ### 回答3: PyTorch深度学习框架之一,它在遥感语义分割领域中得到了广泛应用。遥感图像通常包含大量的地物信息,而遥感语义分割旨在将遥感图像中的每个像素分类为不同的地物类别,例如水、道路、建筑等。这对于资源管理、城市规划和灾害响应等领域非常重要。 使用PyTorch进行遥感语义分割的过程大致如下:首先,收集并准备遥感图像数据集,并对其进行预处理和标注。然后,将数据集加载到PyTorch中,并使用卷积神经网络(CNN)进行训练。在训练过程中,通过迭代反向传播并更新权重参数,来最小化损失函数。最后,使用训练好的模型对新的遥感图像进行分割。 PyTorch提供了很多有用的工具和库,例如TorchVision和TorchIO,可以帮助我们更轻松地进行数据处理和建立网络模型。此外,PyTorch还可以将模型部署到各种硬件上,例如GPU、CPU和移动设备,以提高性能和效率。 总之,使用PyTorch进行遥感语义分割是一项挑战性十足的任务,但是它可以提供准确的地物分类和高质量的地图制作,为我们的城市规划和资源管理工作提供有力支持。
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值