基于Unet++在kaggle—2018dsb数据集上实现图像分割

目录

  • 1. 作者介绍
  • 2. 理论知识介绍
    • 2.1 Unet++模型介绍
  • 3. 实验过程
    • 3.1 数据集介绍
    • 3.2 代码实现
    • 3.3 结果
  • 4. 参考链接

1. 作者介绍

郭冠群,男,西安工程大学电子信息学院,2023级研究生
研究方向:机器视觉与人工智能
电子邮件:1347418097@qq.com

路治东,男,西安工程大学电子信息学院,2022级研究生,张宏伟人工智能课题组
研究方向:机器视觉与人工智能
电子邮件:2063079527@qq.com

2. 理论知识介绍

2.1 Unet++模型介绍

  • Unet
    语义分割是将图像划分为有意义的区域,并标注每个区域所属的类别。语义分割网络是实现这一任务的工具,其中Unet模型通过跨阶段融合不同尺寸的特征图来实现这一目标。
    在这里插入图片描述
  • 特征图融合
    特征图融合的目的是结合浅层和深层特征,提升分割效果。浅层特征能提取图像的简单特征如边界和颜色,而深层特征提取图像的深层次语义信息。多个特征图的融合能够弥补单一特征层次信息的不足。
  • Unet++
    Unet++通过嵌套的密集跳过路径连接编码器和解码器子网络,减少了特征映射之间的语义差距,从而提高了分割效果。在测试阶段,由于输入图像只进行前向传播,被剪掉的部分对前面输出没有影响,而在训练阶段,这些部分会帮助其他部分进行权重更新。
    在这里插入图片描述

3. 实验过程

3.1 数据集介绍

  • 数据集来源
    Kaggle—2018dsb数据集来自于2018年数据科学碗,其任务是从显微镜图像中分割细胞核。这对于推动医学发现具有重要意义,特别是在病理学、癌症研究和其他生命科学领域。
    在这里插入图片描述
  • 下载途径

百度网盘 链接:https://pan.baidu.com/s/1GXtZ0clE12oZKooF61siKQ
提取码:tsh7

  • 数据集内容
    数据集包含显微镜下细胞图像及其对应的分割掩码。训练集用于训练模型,测试集用于评估模型性能。
    在这里插入图片描述

3.2 代码实现

  1. train.py
import os
import argparse
from glob import glob
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from tqdm import tqdm
import albumentations as albu
from albumentations.core.composition import Compose, OneOf
from sklearn.model_selection import train_test_split
import archs
import losses
from dataset import CustomDataset
from metrics import iou_score
from utils import AverageMeter, str2bool

class Config:
    @staticmethod
    def from_cmdline():
        parser = argparse.ArgumentParser(description='Training configuration')
        parser.add_argument('--name', default=None, help='Model name: (default: arch+timestamp)')
        parser.add_argument('--epochs', type=int, default=100, help='Number of total epochs to run')
        parser.add_argument('-b', '--batch_size', type=int, default=8, help='Mini-batch size (default: 16)')
        parser.add_argument('--arch', default='NestedUNet', choices=archs.__all__, help='Model architecture')
        parser.add_argument('--deep_supervision', type=str2bool, default=False, help='Use deep supervision if True')
        parser.add_argument('--input_channels', type=int, default=3, help='Number of input channels')
        parser.add_argument('--num_classes', type=int, default=1, help='Number of classes')
        parser.add_argument('--input_w', type=int, default=96, help='Input image width')
        parser.add_argument('--input_h', type=int, default=96, help='Input image height')
        parser.add_argument('--loss', default='BCEDiceLoss', choices=losses.__all__, help='Loss function')
        parser.add_argument('--dataset', default='dsb2018_96', help='Dataset name')
        parser.add_argument('--img_ext', default='.png', help='Image file extension')
        parser.add_argument('--mask_ext', default='.png', help='Mask file extension')
        parser.add_argument('--optimizer', default='SGD', choices=['Adam', 'SGD'], help='Optimizer type')
        parser.add_argument('--lr', '--learning_rate', type=float, default=1e-3, help='Initial learning rate')
        parser.add_argument('--momentum', type=float, default=0.9, help='Optimizer momentum')
        parser.add_argument('--weight_decay', type=float, default=1e-4, help='Weight decay rate')
        parser.add_argument('--nesterov', type=str2bool, default=False, help='Nesterov momentum')
        parser.add_argument('--scheduler', default='CosineAnnealingLR',
                            choices=['CosineAnnealingLR', 'ReduceLROnPlateau', 'MultiStepLR', 'ConstantLR'],
                            help='Learning rate scheduler')
        parser.add_argument('--min_lr', type=float, default=1e-5, help='Minimum learning rate')
        parser.add_argument('--factor', type=float, default=0.1, help='Factor for ReduceLROnPlateau')
        parser.add_argument('--patience', type=int, default=2, help='Patience for ReduceLROnPlateau')
        parser.add_argument('--milestones', type=str, default='1,2', help='Milestones for MultiStepLR')
        parser.add_argument('--gamma', type=float, default=2 / 3, help='Gamma for MultiStepLR')
        parser.add_argument('--early_stopping', type=int, default=-1, help='Early stopping threshold')
        parser.add_argument('--num_workers', type=int, default=0, help='Number of data loading workers')

        args = parser.parse_args()
        return vars(args)


class ModelManager:
    def __init__(self, config):
        self.config = config
        self.model = self.create_model().cuda()
        self.criterion = self.create_criterion().cuda()
        self.optimizer = self.create_optimizer()
        self.scheduler = self.create_scheduler()

    def create_model(self):
        return archs.__dict__[self.config['arch']](
            self.config['num_classes'],
            self.config['input_channels'],
            self.config['deep_supervision']
        )

    def create_criterion(self):
        if self.config['loss'] == 'BCEWithLogitsLoss':
            return nn.BCEWithLogitsLoss()
        else:
            return losses.__dict__[self.config['loss']]()

    def create_optimizer(self):
        params = filter(lambda p: p.requires_grad, self.model.parameters())
        if self.config['optimizer'] == 'Adam':
            return optim.Adam(params, lr=self.config['lr'], weight_decay=self.config['weight_decay'])
        elif self.config['optimizer'] == 'SGD':
            return optim.SGD(params, lr=self.config['lr'], momentum=self.config['momentum'],
                             nesterov=self.config['nesterov'], weight_decay=self.config['weight_decay'])

    def create_scheduler(self):
        if self.config['scheduler'] == 'CosineAnnealingLR':
            return lr_scheduler.CosineAnnealingLR(
                self.optimizer, T_max=self.config['epochs'], eta_min=self.config['min_lr'])
        elif self.config['scheduler'] == 'ReduceLROnPlateau':
            return lr_scheduler.ReduceLROnPlateau(
                self.optimizer, factor=self.config['factor'], patience=self.config['patience'],
                min_lr=self.config['min_lr'])
        elif self.config['scheduler'] == 'MultiStepLR':
            milestones = list(map(int, self.config['milestones'].split(',')))
            return lr_scheduler.MultiStepLR(self.optimizer, milestones=milestones, gamma=self.config['gamma'])


class DataManager:
    def __init__(self, config):
        self.config = config
        self.train_loader, self.val_loader = self.setup_loaders()

    def setup_loaders(self):
        img_ids = glob(os.path.join('inputs', self.config['dataset'], 'images', '*' + self.config['img_ext']))
        img_ids = [os.path.splitext(os.path.basename(p))[0] for p in img_ids]
        train_img_ids, val_img_ids = train_test_split(img_ids, test_size=0.2, random_state=41)

        train_transform = Compose([
            albu.RandomRotate90(), albu.Flip(),
            OneOf([albu.HueSaturationValue(), albu.RandomBrightnessContrast()], p=1),
            albu.Resize(self.config['input_h'], self.config['input_w']), albu.Normalize()])

        val_transform = Compose([
            albu.Resize(self.config['input_h'], self.config['input_w']), albu.Normalize()])

        train_dataset = CustomDataset(img_ids=train_img_ids,
                                      img_dir=os.path.join('inputs', self.config['dataset'], 'images'),
                                      mask_dir=os.path.join('inputs', self.config['dataset'], 'masks'),
                                      img_ext=self.config['img_ext'],
                                      mask_ext=self.config['mask_ext'],
                                      num_classes=self.config['num_classes'],
                                      transform=train_transform)
        val_dataset = CustomDataset(img_ids=val_img_ids,
                                    img_dir=os.path.join('inputs', self.config['dataset'], 'images'),
                                    mask_dir=os.path.join('inputs', self.config['dataset'], 'masks'),
                                    img_ext=self.config['img_ext'],
                                    mask_ext=self.config['mask_ext'],
                                    num_classes=self.config['num_classes'],
                                    transform=val_transform)

        train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=self.config['batch_size'],
                                                   shuffle=True, num_workers=self.config['num_workers'], drop_last=True)
        val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=self.config['batch_size'],
                                                 shuffle=False, num_workers=self.config['num_workers'], drop_last=False)
        return train_loader, val_loader


def main():
    config = Config.from_cmdline()
    manager = ModelManager(config)
    data_manager = DataManager(config)

    for epoch in range(config['epochs']):
        train_loss, train_iou = train_epoch(data_manager.train_loader, manager.model, manager.criterion,
                                            manager.optimizer, config)
        val_loss, val_iou = validate_epoch(data_manager.val_loader, manager.model, manager.criterion, config)
        print(f'Epoch: {epoch}, Train Loss: {train_loss}, Train IOU: {train_iou}, Val Loss: {val_loss}, Val IOU: {val_iou}')
        # Update scheduler, save models, etc.
        if config['scheduler'] == 'ReduceLROnPlateau':
            manager.scheduler.step(val_loss)
        else:
            manager.scheduler.step()


def train_epoch(train_loader, model, criterion, optimizer, config):
        model.train()
        avg_meters = {'loss': AverageMeter(), 'iou': AverageMeter()}
        pbar = tqdm(total=len(train_loader), desc='Train')

        for data in train_loader:
            if len(data) == 2:
                input, target = data
            elif len(data) > 2:
                input, target, _ = data  # 根据实际返回的数据格式解包
            input = input.cuda()
            target = target.cuda()

            # Compute output
            if config['deep_supervision']:
                outputs = model(input)
                loss = 0
                for output in outputs:
                    loss += criterion(output, target)
                loss /= len(outputs)
                iou = iou_score(outputs[-1], target)
            else:
                output = model(input)
                loss = criterion(output, target)
                iou = iou_score(output, target)

            # Compute gradient and do optimizer step
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            avg_meters['loss'].update(loss.item(), input.size(0))
            avg_meters['iou'].update(iou, input.size(0))
            pbar.update(1)
            pbar.set_postfix({'Loss': avg_meters['loss'].avg, 'IoU': avg_meters['iou'].avg})

        pbar.close()
        return avg_meters['loss'].avg, avg_meters['iou'].avg

def validate_epoch(val_loader, model, criterion, config):
        model.eval()
        avg_meters = {'loss': AverageMeter(), 'iou': AverageMeter()}
        pbar = tqdm(total=len(val_loader), desc='Validate')

        with torch.no_grad():
            for data in val_loader:
                if len(data) == 2:
                    input, target = data
                elif len(data) > 2:
                    input, target, _ = data  # 根据实际返回的数据格式解包
                input = input.cuda()
                target = target.cuda()

                # Compute output
                if config['deep_supervision']:
                    outputs = model(input)
                    loss = 0
                    for output in outputs:
                        loss += criterion(output, target)
                    loss /= len(outputs)
                    iou = iou_score(outputs[-1], target)
                else:
                    output = model(input)
                    loss = criterion(output, target)
                    iou = iou_score(output, target)

                avg_meters['loss'].update(loss.item(), input.size(0))
                avg_meters['iou'].update(iou, input.size(0))
                pbar.update(1)
                pbar.set_postfix({'Loss': avg_meters['loss'].avg, 'IoU': avg_meters['iou'].avg})

        pbar.close()
        return avg_meters['loss'].avg, avg_meters['iou'].avg

    # Training and validation logic can go here using manager and data_manager


if __name__ == '__main__':
    main()
  1. val.py
import argparse
import os
from glob import glob
import matplotlib.pyplot as plt
import numpy as np
import cv2
import torch
import torch.backends.cudnn as cudnn
import yaml
import albumentations as albu
from albumentations.core.composition import Compose
from sklearn.model_selection import train_test_split
from tqdm import tqdm

import archs
from dataset import CustomDataset
from metrics import iou_score
from utils import AverageMeter

"""
需要指定参数:--name dsb2018_96_NestedUNet_woDS
"""

def parse_args():
    parser = argparse.ArgumentParser()

    parser.add_argument('--name', default=None,
                        help='model name')

    args = parser.parse_args()

    return args


def main():
    args = parse_args()

    if args.name is None:
        print("Error: You must specify the model name using the --name argument.")
        return

    with open(f'models/{args.name}/config.yml', 'r') as f:
        config = yaml.load(f, Loader=yaml.FullLoader)

    print('-' * 20)
    for key in config.keys():
        print('%s: %s' % (key, str(config[key])))
    print('-' * 20)

    cudnn.benchmark = True

    # create model
    print("=> creating model %s" % config['arch'])
    model = archs.__dict__[config['arch']](config['num_classes'],
                                           config['input_channels'],
                                           config['deep_supervision'])

    model = model.cuda()

    # Data loading code
    img_ids = glob(os.path.join('inputs', config['dataset'], 'images', '*' + config['img_ext']))
    img_ids = [os.path.splitext(os.path.basename(p))[0] for p in img_ids]

    _, val_img_ids = train_test_split(img_ids, test_size=0.2, random_state=41)

    model.load_state_dict(torch.load(f'models/{config["name"]}/model.pth'))
    model.eval()

    val_transform = Compose([
        albu.Resize(config['input_h'], config['input_w']),
        albu.Normalize(),
    ])

    val_dataset = CustomDataset(
        img_ids=val_img_ids,
        img_dir=os.path.join('inputs', config['dataset'], 'images'),
        mask_dir=os.path.join('inputs', config['dataset'], 'masks'),
        img_ext=config['img_ext'],
        mask_ext=config['mask_ext'],
        num_classes=config['num_classes'],
        transform=val_transform
    )
    val_loader = torch.utils.data.DataLoader(
        val_dataset,
        batch_size=config['batch_size'],
        shuffle=False,
        num_workers=config['num_workers'],
        drop_last=False
    )

    avg_meter = AverageMeter()

    for c in range(config['num_classes']):
        os.makedirs(os.path.join('outputs', config['name'], str(c)), exist_ok=True)

    with torch.no_grad():
        for input, target, meta in tqdm(val_loader, total=len(val_loader)):
            input = input.cuda()
            target = target.cuda()

            # 将元组转换为字典
            meta_dict = {'img_id': meta}
            print(f"meta_dict: {meta_dict}")

            # compute output
            if config['deep_supervision']:
                output = model(input)[-1]
            else:
                output = model(input)

            iou = iou_score(output, target)
            avg_meter.update(iou, input.size(0))

            output = torch.sigmoid(output).cpu().numpy()

            for i in range(len(output)):
                for c in range(config['num_classes']):
                    cv2.imwrite(os.path.join('outputs', config['name'], str(c), str(meta_dict['img_id'][i]) + '.png'),
                                (output[i, c] * 255).astype('uint8'))

    print('IoU: %.4f' % avg_meter.avg)

    plot_examples(input, target, model, num_examples=3)

    torch.cuda.empty_cache()


def plot_examples(datax, datay, model, num_examples=6):
    fig, ax = plt.subplots(nrows=num_examples, ncols=3, figsize=(18, 4 * num_examples))
    m = datax.shape[0]
    for row_num in range(num_examples):
        image_indx = np.random.randint(m)
        image_arr = model(datax[image_indx:image_indx + 1]).squeeze(0).detach().cpu().numpy()
        ax[row_num][0].imshow(np.transpose(datax[image_indx].cpu().numpy(), (1, 2, 0))[:, :, 0])
        ax[row_num][0].set_title("Orignal Image")
        ax[row_num][1].imshow(np.squeeze((image_arr > 0.40)[0, :, :].astype(int)))
        ax[row_num][1].set_title("Segmented Image localization")
        ax[row_num][2].imshow(np.transpose(datay[image_indx].cpu().numpy(), (1, 2, 0))[:, :, 0])
        ax[row_num][2].set_title("Target image")
    plt.show()


if __name__ == '__main__':
    main()
  1. archs.py
import torch
from torch import nn

__all__ = ['UNet', 'NestedUNet']

# 基本计算单元
class VGGBlock(nn.Module):
    def __init__(self, in_channels, middle_channels, out_channels):
        super().__init__()
        self.relu = nn.ReLU(inplace=True)
        self.conv1 = nn.Conv2d(in_channels, middle_channels, 3, padding=1)
        self.bn1 = nn.BatchNorm2d(middle_channels)
        self.conv2 = nn.Conv2d(middle_channels, out_channels, 3, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        # VGGBlock实际上就是相当于做了两次卷积
        out = self.conv1(x)
        out = self.bn1(out)     # 归一化
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        return out


class UNet(nn.Module):
    def __init__(self, num_classes, input_channels=3, **kwargs):
        super().__init__()

        nb_filter = [32, 64, 128, 256, 512]

        self.pool = nn.MaxPool2d(2, 2)
        self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)#scale_factor:放大的倍数  插值

        self.conv0_0 = VGGBlock(input_channels, nb_filter[0], nb_filter[0])
        self.conv1_0 = VGGBlock(nb_filter[0], nb_filter[1], nb_filter[1])
        self.conv2_0 = VGGBlock(nb_filter[1], nb_filter[2], nb_filter[2])
        self.conv3_0 = VGGBlock(nb_filter[2], nb_filter[3], nb_filter[3])
        self.conv4_0 = VGGBlock(nb_filter[3], nb_filter[4], nb_filter[4])

        self.conv3_1 = VGGBlock(nb_filter[3]+nb_filter[4], nb_filter[3], nb_filter[3])
        self.conv2_2 = VGGBlock(nb_filter[2]+nb_filter[3], nb_filter[2], nb_filter[2])
        self.conv1_3 = VGGBlock(nb_filter[1]+nb_filter[2], nb_filter[1], nb_filter[1])
        self.conv0_4 = VGGBlock(nb_filter[0]+nb_filter[1], nb_filter[0], nb_filter[0])

        self.final = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)


    def forward(self, input):
        x0_0 = self.conv0_0(input)
        x1_0 = self.conv1_0(self.pool(x0_0))
        x2_0 = self.conv2_0(self.pool(x1_0))
        x3_0 = self.conv3_0(self.pool(x2_0))
        x4_0 = self.conv4_0(self.pool(x3_0))

        x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], 1))
        x2_2 = self.conv2_2(torch.cat([x2_0, self.up(x3_1)], 1))
        x1_3 = self.conv1_3(torch.cat([x1_0, self.up(x2_2)], 1))
        x0_4 = self.conv0_4(torch.cat([x0_0, self.up(x1_3)], 1))

        output = self.final(x0_4)
        return output


class NestedUNet(nn.Module):
    def __init__(self, num_classes, input_channels=3, deep_supervision=False, **kwargs):
        super().__init__()
        # 定义了一个列表,包含NestedUNet中不同层的通道数
        nb_filter = [32, 64, 128, 256, 512]
        # 深度监督:是否需要都计算损失函数
        self.deep_supervision = deep_supervision

        self.pool = nn.MaxPool2d(2, 2)  # 最大池化,池化核大小为2x2,步幅为2
        # 创建一个上采样层实例,尺度因子为2,采用双线性插值的方式进行上采样,边缘对齐方式为True
        self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)

        self.conv0_0 = VGGBlock(input_channels, nb_filter[0], nb_filter[0])
        self.conv1_0 = VGGBlock(nb_filter[0], nb_filter[1], nb_filter[1])
        self.conv2_0 = VGGBlock(nb_filter[1], nb_filter[2], nb_filter[2])
        self.conv3_0 = VGGBlock(nb_filter[2], nb_filter[3], nb_filter[3])
        self.conv4_0 = VGGBlock(nb_filter[3], nb_filter[4], nb_filter[4])

        self.conv0_1 = VGGBlock(nb_filter[0]+nb_filter[1], nb_filter[0], nb_filter[0])
        self.conv1_1 = VGGBlock(nb_filter[1]+nb_filter[2], nb_filter[1], nb_filter[1])
        self.conv2_1 = VGGBlock(nb_filter[2]+nb_filter[3], nb_filter[2], nb_filter[2])
        self.conv3_1 = VGGBlock(nb_filter[3]+nb_filter[4], nb_filter[3], nb_filter[3])

        self.conv0_2 = VGGBlock(nb_filter[0]*2+nb_filter[1], nb_filter[0], nb_filter[0])
        self.conv1_2 = VGGBlock(nb_filter[1]*2+nb_filter[2], nb_filter[1], nb_filter[1])
        self.conv2_2 = VGGBlock(nb_filter[2]*2+nb_filter[3], nb_filter[2], nb_filter[2])

        self.conv0_3 = VGGBlock(nb_filter[0]*3+nb_filter[1], nb_filter[0], nb_filter[0])
        self.conv1_3 = VGGBlock(nb_filter[1]*3+nb_filter[2], nb_filter[1], nb_filter[1])

        self.conv0_4 = VGGBlock(nb_filter[0]*4+nb_filter[1], nb_filter[0], nb_filter[0])

        if self.deep_supervision:
            self.final1 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)
            self.final2 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)
            self.final3 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)
            self.final4 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)
        else:
            self.final = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)


    def forward(self, input):
        # 入口函数打个断点,看数据的维度很重要
        print('input:', input.shape)
        x0_0 = self.conv0_0(input)  # 第一次卷积
        print('x0_0:',x0_0.shape)   # 升维 input: torch.Size([8, 32, 96, 96])
        x1_0 = self.conv1_0(self.pool(x0_0))
        print('x1_0:', x1_0.shape)  # 升维,降数据量,x1_0: torch.Size([8, 32, 96, 96])
        x0_1 = self.conv0_1(torch.cat([x0_0, self.up(x1_0)], 1))
        # cat 拼接,再经历一次卷积,input是96=32+64,output=32
        print('x0_1:', x0_1.shape)   # x0_1: torch.Size([8, 32, 96, 96])
        # 梳理清楚一个关键点即可,后面依次类推,可以打印结果自己手动推一下
        x2_0 = self.conv2_0(self.pool(x1_0))
        print('x2_0:', x2_0.shape)
        x1_1 = self.conv1_1(torch.cat([x1_0, self.up(x2_0)], 1))
        print('x1_1:',x1_1.shape)
        x0_2 = self.conv0_2(torch.cat([x0_0, x0_1, self.up(x1_1)], 1))
        print('x0_2:',x0_2.shape)

        x3_0 = self.conv3_0(self.pool(x2_0))
        print('x3_0:',x3_0.shape)
        x2_1 = self.conv2_1(torch.cat([x2_0, self.up(x3_0)], 1))
        print('x2_1:',x2_1.shape)
        x1_2 = self.conv1_2(torch.cat([x1_0, x1_1, self.up(x2_1)], 1))
        print('x1_2:',x1_2.shape)
        x0_3 = self.conv0_3(torch.cat([x0_0, x0_1, x0_2, self.up(x1_2)], 1))
        print('x0_3:',x0_3.shape)
        x4_0 = self.conv4_0(self.pool(x3_0))
        print('x4_0:',x4_0.shape)
        x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], 1))
        print('x3_1:',x3_1.shape)
        x2_2 = self.conv2_2(torch.cat([x2_0, x2_1, self.up(x3_1)], 1))
        print('x2_2:',x2_2.shape)
        x1_3 = self.conv1_3(torch.cat([x1_0, x1_1, x1_2, self.up(x2_2)], 1))
        print('x1_3:',x1_3.shape)
        x0_4 = self.conv0_4(torch.cat([x0_0, x0_1, x0_2, x0_3, self.up(x1_3)], 1))
        print('x0_4:',x0_4.shape)

        if self.deep_supervision:
            output1 = self.final1(x0_1)
            output2 = self.final2(x0_2)
            output3 = self.final3(x0_3)
            output4 = self.final4(x0_4)
            return [output1, output2, output3, output4]

        else:
            # 输出一个结果,结果是0~1之间
            output = self.final(x0_4)
            return output

archs解读
在这里插入图片描述
在这里插入图片描述
conv00代表着图中的X00,conv20代表图中的X20,以此类推。
每一个vggblock
在这里插入图片描述

3.3 结果

在这里插入图片描述

4. 参考链接

深度学习分割任务——Unet++分割网络代码详细解读

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/717773.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代发考生战报:HCIP H12-725安全变题了

代发考生战报:HCIP H12-725安全变题了,幸好当天找客服办理的包过服务,听同考场的考生说,考试全是新题,只有1-2个是题库上的题,自己考的都考挂了,帮我答题的老师很厉害,很赞&#xff…

CesiumJS【Basic】- #006 浏览器控制台查看位置角度

文章目录 浏览器控制台查看位置角度1 目标 浏览器控制台查看位置角度 1 目标 浏览器控制台查看位置角度

探索国内首家文生软件平台:码上飞CodeFlying

前言: AIGC (AI Generated Content) 作为人工智能领域最火热的分支之一,以ChatGPT等大模型为代表,迅速掀起了全球热潮。 国内的大厂如阿里、字节跳动、百度、腾讯等也纷纷推出了自己的大模型产品,涵盖了文生文、文生图、文生视频…

计算机网络(6) UDP协议

一.UDP数据报格式 UDP(User Datagram Protocol,用户数据报协议)是一种简单的传输层协议,与TCP(Transmission Control Protocol,传输控制协议)相比,UDP提供一种无连接、不可靠的数据传…

Python云实例初始化和配置的工具库之cloud-init使用详解

概要 在云计算环境中,自动化配置和管理实例是非常重要的任务。cloud-init 是一个用于云实例初始化和配置的工具,广泛应用于各种云服务提供商(如 AWS、Azure、GCP 等)的实例启动过程。通过 cloud-init,用户可以在实例启动时自动执行脚本、安装软件包、配置网络等。本文将详…

Python学习打卡:day08

day8 笔记来源于:黑马程序员python教程,8天python从入门到精通,学python看这套就够了 目录 day858、数据容器(序列)的切片序列的常用操作——切片 59、序列的切片课后练习60、集合的定义和操作集合的定义集合的操作添加新元素移除元素从集合…

⭐ ▶《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch3 贝尔曼最优公式 【压缩映射定理】

PPT 截取必要信息。 课程网站做习题。总体 MOOC 过一遍 1、视频 学堂在线 习题 2、过 电子书,补充 【下载:本章 PDF 电子书 GitHub 界面链接】 [又看了一遍视频] 3、总体 MOOC 过一遍 习题 学堂在线 课程页面链接 中国大学MOOC 课程页面链接 B 站 视频链…

软考系统规划与管理师伴读脑图第9章

周末发系统规划与管理师的试听视频,占用了发送次数,所以上周的脑图推迟了今天发出。 不知不觉已经发到了第9章,感叹这就是坚持积累下来的力量,其实考试也是一样的道理。

Nginx 高级应用

目录 一.使用alias实现虚拟目录 二. 通过stub_status模块监控nginx的工作状态 三. 使用limit_rate限制客户端传输数据的速度 四. nginx虚拟主机配置 1.基于端口的虚拟主机 2. 基于IP的虚拟主机 3. 基于域名的虚拟主机 nginx配置文件: /…

中电金信:银行业数据中心何去何从

20多年前,计算机走进国内大众视野,计算机行业迎来在国内的高速发展时代。银行业是最早使用计算机的行业之一,也是计算机技术应用最广泛、最深入的行业之一。近年来,随着银行竞争加剧,科技如何引领业务、金融科技如何发…

面向对象和面向过程

Python完全采用了面向对象的思想,是真正面向对象的编程语言,完全支持面向对象的基本功能,例如:继承、多态、封装等。 Python支持面向过程、面向对象、函数式编程等多种编程方式。而Java编程语言支持面向对象的编程方式&#xff0…

元数据:数据的罗塞塔石碑

在大数据时代,我们每天都在生成和处理海量数据。但数据本身,如果没有适当的上下文和描述,就像是一堆没有翻译的古老文字。这就是元数据发挥作用的地方——它是大数据世界的罗塞塔石碑,为我们提供了理解和利用数据的关键。 文章目录…

从中概回购潮,看互联网的未来

王兴的饭否语录里有这样一句话:“对未来越有信心,对现在越有耐心。” 而如今的美团,已经不再掩饰对未来的坚定信心。6月11日,美团在港交所公告,计划回购不超过20亿美元的B类普通股股份。 而自从港股一季度财报季结束…

GStreamer——教程——基础教程3:Dynamic pipelines

基础教程3:Dynamic pipelines( 动态管道 ) 目标 本教程显示了使用GStreamer所需的其他基本概念,它允许“动态”构建pipeline(管道),信息变得可用,而不是在应用程序开始时定义一条单一的管道。 完成本教程后,您将具备…

Pyshark——安装、解析pcap文件

1、简介 PyShark是一个用于网络数据包捕获和分析的Python库,基于著名的网络协议分析工具Wireshark和其背后的libpcap/tshark库。它提供了一种便捷的方式来处理网络流量,适用于需要进行网络监控、调试和研究的场景。以下是PyShark的一些关键特性和使用方…

26 岁的“天才少年”,带队面壁打通高效大模型之路

每一轮技术浪潮出现时,冲在最前面的都是朝气蓬勃的年轻人。 当大模型代表的人工智能浪潮席卷全球,作为移动互联网“原住民”的年轻开发者,可以说是最活跃的群体。他们的脸庞还有些稚嫩,但在技术和方向上有着自己的想法&#xff0…

志愿服务管理系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,基础数据管理,广场论坛管理,志愿活动管理,活动报名管理 前台账户功能包括:系统首页,个人中心,志愿活动&a…

dp练习2

如何分析这个题目呢&#xff0c;要想着当前的最优解只和前面的最优解有关 class Solution { public:int numSquares(int n) {vector<int> f(n 1);for (int i 1; i < n; i) {int minn INT_MAX;for (int j 1; j * j < i; j) {minn min(minn, f[i - j * j]);}f[…

【Linux】进程_7

文章目录 五、进程8. 进程地址空间9. 进程终止10. 进程等待 未完待续 五、进程 8. 进程地址空间 我们上节知道了进程地址空间是根据页表来使虚拟地址转换成内存中的物理地址&#xff0c;那这种 地址空间 页表 的机制有什么好处呢&#xff1f;①这种机制可以将物理内存从无序…

探索 Perplexity:产品经理的新式 AI 工具

这是一篇国外博客的翻译文章&#xff0c;文中重点介绍了产品经理如何使用 AI 工具 Perplexity 来解决日常工作中的实际问题。通过深入调查和数百次电话访谈&#xff0c;收集了产品经理使用Perplexity 的具体方法&#xff0c;并列举了一些非常实用的例子。 这些方法包括理解和制…