尝试在UNet的不同位置添加SE模块

news2025/1/10 23:51:18

目录

(1)se-unet01(在卷积后,下采样前,添加SE模块)

(2)se-unet02(在卷积后,上采样前,添加SE模块)

(3)se-unet03(在每两个卷积之后,加上SE模块)

(4)se-unet04(只在最后的输出卷积后,添加SE模块)


数据集:refuge视盘数据集

训练轮次:200

评价指标:dice coefficient、mean IOU。

Architecture
dice coefficientmean IOU
unet0.79983.0
se-unet010.98963.3

(1)在卷积后,下采样前,添加SE模块

模型改动代码

from typing import Dict
import torch
import torch.nn as nn
import torch.nn.functional as F

'''-------------一、SE模块-----------------------------'''
#全局平均池化+1*1卷积核+ReLu+1*1卷积核+Sigmoid
class SE_Block(nn.Module):
    def __init__(self, inchannel, ratio=16):
        super(SE_Block, self).__init__()
        # 全局平均池化(Fsq操作)
        self.gap = nn.AdaptiveAvgPool2d((1, 1))
        # 两个全连接层(Fex操作)
        self.fc = nn.Sequential(
            nn.Linear(inchannel, inchannel // ratio, bias=False),  # 从 c -> c/r
            nn.ReLU(),
            nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> c
            nn.Sigmoid()
        )

    def forward(self, x):
            # 读取批数据图片数量及通道数
            b, c, h, w = x.size()
            # Fsq操作:经池化后输出b*c的矩阵
            y = self.gap(x).view(b, c)
            # Fex操作:经全连接层输出(b,c,1,1)矩阵
            y = self.fc(y).view(b, c, 1, 1)
            # Fscale操作:将得到的权重乘以原来的特征图x
            return x * y.expand_as(x)

# 卷积,在uent中卷积一般成对使用
class DoubleConv(nn.Sequential):
    # 输入通道数, 输出通道数, mid_channels为成对卷积中第一个卷积层的输出通道数
    def __init__(self, in_channels, out_channels, mid_channels=None):
        if mid_channels is None:
            mid_channels = out_channels
        super(DoubleConv, self).__init__(
            # 3*3卷积,填充为1,卷积之后输入输出的特征图大小一致
            nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

# 下采样
class Down(nn.Sequential):
    def __init__(self, in_channels, out_channels):
        super(Down, self).__init__(
            # 1.最大池化的窗口大小为2, 步长为2
            nn.MaxPool2d(2, stride=2),
            # 2.两个卷积
            DoubleConv(in_channels, out_channels)
        )

# 上采样
class Up(nn.Module):
    # bilinear是否采用双线性插值
    def __init__(self, in_channels, out_channels, bilinear=True):
        super(Up, self).__init__()
        if bilinear:
            # 使用双线性插值上采样
            # 上采样率为2,双线性插值模式
            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
        else:
            # 使用转置卷积上采样
            self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)
            self.conv = DoubleConv(in_channels, out_channels)

    def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:
        x1 = self.up(x1)
        # [N, C, H, W]
        # 上采样之后的特征图与要拼接的特征图,高度方向的差值
        diff_y = x2.size()[2] - x1.size()[2]
        # 上采样之后的特征图与要拼接的特征图,宽度方向的差值
        diff_x = x2.size()[3] - x1.size()[3]

        # padding_left, padding_right, padding_top, padding_bottom
        # 1.填充差值
        x1 = F.pad(x1, [diff_x // 2, diff_x - diff_x // 2,
                        diff_y // 2, diff_y - diff_y // 2])

        # 2.拼接
        x = torch.cat([x2, x1], dim=1)
        # 3.卷积,两次卷积
        x = self.conv(x)
        return x

# 最后的1*1输出卷积
class OutConv(nn.Sequential):
    def __init__(self, in_channels, num_classes):
        super(OutConv, self).__init__(
            nn.Conv2d(in_channels, num_classes, kernel_size=1)
        )


class UNet(nn.Module):
    # 参数: 输入通道数, 分割任务个数, 是否使用双线插值, 网络中第一个卷积通道个数
    def __init__(self,
                 in_channels: int = 1,
                 num_classes: int = 2,
                 bilinear: bool = True,
                 base_c: int = 64):
        super(UNet, self).__init__()
        self.in_channels = in_channels
        self.num_classes = num_classes
        self.bilinear = bilinear

        self.in_conv = DoubleConv(in_channels, base_c)
        # SE模块
        self.SE1 = SE_Block(base_c)
        self.SE2 = SE_Block(base_c * 2)
        self.SE3 = SE_Block(base_c * 4)
        self.SE4 = SE_Block(base_c * 8)

        # 下采样,参数:输入通道,输出通道
        self.down1 = Down(base_c, base_c * 2)
        self.down2 = Down(base_c * 2, base_c * 4)
        self.down3 = Down(base_c * 4, base_c * 8)


        # 如果采用双线插值上采样为 2,采用转置矩阵上采样为 1
        factor = 2 if bilinear else 1
        # 最后一个下采样,如果是双线插值则输出通道为512,否则为1024
        self.down4 = Down(base_c * 8, base_c * 16 // factor)

        self.SE5 = SE_Block(base_c * 16 // factor)
        # 上采样,参数:输入通道,输出通道
        self.up1 = Up(base_c * 16, base_c * 8 // factor, bilinear)
        self.up2 = Up(base_c * 8, base_c * 4 // factor, bilinear)
        self.up3 = Up(base_c * 4, base_c * 2 // factor, bilinear)
        self.up4 = Up(base_c * 2, base_c, bilinear)
        # 最后的1*1输出卷积
        self.out_conv = OutConv(base_c, num_classes)

    # 正向传播过程
    def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
        # 1. 定义最开始的两个卷积层
        x1 = self.in_conv(x)
        x1 = self.SE1(x1)
        # 2. contracting path(收缩路径)
        x2 = self.down1(x1)
        x2 = self.SE2(x2)
        x3 = self.down2(x2)
        x3 = self.SE3(x3)
        x4 = self.down3(x3)
        x4 = self.SE4(x4)
        x5 = self.down4(x4)
        x5 = self.SE5(x5)
        # 3. expanding path(扩展路径)
        x = self.up1(x5, x4)
        x = self.up2(x, x3)
        x = self.up3(x, x2)
        x = self.up4(x, x1)
        # 4. 最后1*1输出卷积
        logits = self.out_conv(x)

        return {"out": logits}

训练结果:

[epoch: 199]
train_loss: 0.0265
lr: 0.000000
dice coefficient: 0.989
global correct: 97.8
average row correct: ['36.4', '99.3']
IoU: ['28.8', '97.8']
mean IoU: 63.3

(2)在卷积后,上采样前,添加SE模块

 模型改动代码

from typing import Dict
import torch
import torch.nn as nn
import torch.nn.functional as F

'''-------------一、SE模块-----------------------------'''
#全局平均池化+1*1卷积核+ReLu+1*1卷积核+Sigmoid
class SE_Block(nn.Module):
    def __init__(self, inchannel, ratio=16):
        super(SE_Block, self).__init__()
        # 全局平均池化(Fsq操作)
        self.gap = nn.AdaptiveAvgPool2d((1, 1))
        # 两个全连接层(Fex操作)
        self.fc = nn.Sequential(
            nn.Linear(inchannel, inchannel // ratio, bias=False),  # 从 c -> c/r
            nn.ReLU(),
            nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> c
            nn.Sigmoid()
        )

    def forward(self, x):
            # 读取批数据图片数量及通道数
            b, c, h, w = x.size()
            # Fsq操作:经池化后输出b*c的矩阵
            y = self.gap(x).view(b, c)
            # Fex操作:经全连接层输出(b,c,1,1)矩阵
            y = self.fc(y).view(b, c, 1, 1)
            # Fscale操作:将得到的权重乘以原来的特征图x
            return x * y.expand_as(x)

# 卷积,在uent中卷积一般成对使用
class DoubleConv(nn.Sequential):
    # 输入通道数, 输出通道数, mid_channels为成对卷积中第一个卷积层的输出通道数
    def __init__(self, in_channels, out_channels, mid_channels=None):
        if mid_channels is None:
            mid_channels = out_channels
        super(DoubleConv, self).__init__(
            # 3*3卷积,填充为1,卷积之后输入输出的特征图大小一致
            nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

# 下采样
class Down(nn.Sequential):
    def __init__(self, in_channels, out_channels):
        super(Down, self).__init__(
            # 1.最大池化的窗口大小为2, 步长为2
            nn.MaxPool2d(2, stride=2),
            # 2.两个卷积
            DoubleConv(in_channels, out_channels)
        )

# 上采样
class Up(nn.Module):
    # bilinear是否采用双线性插值
    def __init__(self, in_channels, out_channels, bilinear=True):
        super(Up, self).__init__()
        if bilinear:
            # 使用双线性插值上采样
            # 上采样率为2,双线性插值模式
            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
        else:
            # 使用转置卷积上采样
            self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)
            self.conv = DoubleConv(in_channels, out_channels)

    def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:
        x1 = self.up(x1)
        # [N, C, H, W]
        # 上采样之后的特征图与要拼接的特征图,高度方向的差值
        diff_y = x2.size()[2] - x1.size()[2]
        # 上采样之后的特征图与要拼接的特征图,宽度方向的差值
        diff_x = x2.size()[3] - x1.size()[3]

        # padding_left, padding_right, padding_top, padding_bottom
        # 1.填充差值
        x1 = F.pad(x1, [diff_x // 2, diff_x - diff_x // 2,
                        diff_y // 2, diff_y - diff_y // 2])

        # 2.拼接
        x = torch.cat([x2, x1], dim=1)
        # 3.卷积,两次卷积
        x = self.conv(x)
        return x

# 最后的1*1输出卷积
class OutConv(nn.Sequential):
    def __init__(self, in_channels, num_classes):
        super(OutConv, self).__init__(
            nn.Conv2d(in_channels, num_classes, kernel_size=1)
        )


class UNet(nn.Module):
    # 参数: 输入通道数, 分割任务个数, 是否使用双线插值, 网络中第一个卷积通道个数
    def __init__(self,
                 in_channels: int = 1,
                 num_classes: int = 2,
                 bilinear: bool = True,
                 base_c: int = 64):
        super(UNet, self).__init__()
        self.in_channels = in_channels
        self.num_classes = num_classes
        self.bilinear = bilinear

        self.in_conv = DoubleConv(in_channels, base_c)


        # 下采样,参数:输入通道,输出通道
        self.down1 = Down(base_c, base_c * 2)
        self.down2 = Down(base_c * 2, base_c * 4)
        self.down3 = Down(base_c * 4, base_c * 8)


        # 如果采用双线插值上采样为 2,采用转置矩阵上采样为 1
        factor = 2 if bilinear else 1
        # 最后一个下采样,如果是双线插值则输出通道为512,否则为1024
        self.down4 = Down(base_c * 8, base_c * 16 // factor)


        # 上采样,参数:输入通道,输出通道
        self.up1 = Up(base_c * 16, base_c * 8 // factor, bilinear)
        self.up2 = Up(base_c * 8, base_c * 4 // factor, bilinear)
        self.up3 = Up(base_c * 4, base_c * 2 // factor, bilinear)
        self.up4 = Up(base_c * 2, base_c, bilinear)
        # 最后的1*1输出卷积
        self.out_conv = OutConv(base_c, num_classes)

        # SE模块
        self.SE1 = SE_Block(base_c * 16 // factor)
        self.SE2 = SE_Block(base_c * 8 // factor)
        self.SE3 = SE_Block(base_c * 4 // factor)
        self.SE4 = SE_Block(base_c * 2 // factor)
        self.SE5 = SE_Block(num_classes)

    # 正向传播过程
    def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
        # 1. 定义最开始的两个卷积层
        x1 = self.in_conv(x)

        # 2. contracting path(收缩路径)
        x2 = self.down1(x1)
        x3 = self.down2(x2)

        x4 = self.down3(x3)

        x5 = self.down4(x4)
        x5 = self.SE1(x5)
        # 3. expanding path(扩展路径)
        x = self.up1(x5, x4)
        x = self.SE2(x)
        x = self.up2(x, x3)
        x = self.SE3(x)
        x = self.up3(x, x2)
        x = self.SE4(x)
        x = self.up4(x, x1)
        # 4. 最后1*1输出卷积
        logits = self.out_conv(x)
        logits = self.SE5(logits)

        return {"out": logits}

(3)在每两个卷积之后,加上SE模块

模型改动代码

from typing import Dict
import torch
import torch.nn as nn
import torch.nn.functional as F

# se-unet03
'''-------------一、SE模块-----------------------------'''
#全局平均池化+1*1卷积核+ReLu+1*1卷积核+Sigmoid
class SE_Block(nn.Module):
    def __init__(self, inchannel, ratio=16):
        super(SE_Block, self).__init__()
        # 全局平均池化(Fsq操作)
        self.gap = nn.AdaptiveAvgPool2d((1, 1))
        # 两个全连接层(Fex操作)
        self.fc = nn.Sequential(
            nn.Linear(inchannel, inchannel // ratio, bias=False),  # 从 c -> c/r
            nn.ReLU(),
            nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> c
            nn.Sigmoid()
        )

    def forward(self, x):
            # 读取批数据图片数量及通道数
            b, c, h, w = x.size()
            # Fsq操作:经池化后输出b*c的矩阵
            y = self.gap(x).view(b, c)
            # Fex操作:经全连接层输出(b,c,1,1)矩阵
            y = self.fc(y).view(b, c, 1, 1)
            # Fscale操作:将得到的权重乘以原来的特征图x
            return x * y.expand_as(x)

# 卷积,在uent中卷积一般成对使用
class DoubleConv(nn.Sequential):
    # 输入通道数, 输出通道数, mid_channels为成对卷积中第一个卷积层的输出通道数
    def __init__(self, in_channels, out_channels, mid_channels=None):
        if mid_channels is None:
            mid_channels = out_channels
        super(DoubleConv, self).__init__(
            # 3*3卷积,填充为1,卷积之后输入输出的特征图大小一致
            nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

# 下采样
class Down(nn.Sequential):
    def __init__(self, in_channels, out_channels):
        super(Down, self).__init__(
            # 1.最大池化的窗口大小为2, 步长为2
            nn.MaxPool2d(2, stride=2),
            # 2.两个卷积
            DoubleConv(in_channels, out_channels)
        )

# 上采样
class Up(nn.Module):
    # bilinear是否采用双线性插值
    def __init__(self, in_channels, out_channels, bilinear=True):
        super(Up, self).__init__()
        if bilinear:
            # 使用双线性插值上采样
            # 上采样率为2,双线性插值模式
            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
        else:
            # 使用转置卷积上采样
            self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)
            self.conv = DoubleConv(in_channels, out_channels)

    def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:
        x1 = self.up(x1)
        # [N, C, H, W]
        # 上采样之后的特征图与要拼接的特征图,高度方向的差值
        diff_y = x2.size()[2] - x1.size()[2]
        # 上采样之后的特征图与要拼接的特征图,宽度方向的差值
        diff_x = x2.size()[3] - x1.size()[3]

        # padding_left, padding_right, padding_top, padding_bottom
        # 1.填充差值
        x1 = F.pad(x1, [diff_x // 2, diff_x - diff_x // 2,
                        diff_y // 2, diff_y - diff_y // 2])

        # 2.拼接
        x = torch.cat([x2, x1], dim=1)
        # 3.卷积,两次卷积
        x = self.conv(x)
        return x

# 最后的1*1输出卷积
class OutConv(nn.Sequential):
    def __init__(self, in_channels, num_classes):
        super(OutConv, self).__init__(
            nn.Conv2d(in_channels, num_classes, kernel_size=1)
        )


class UNet(nn.Module):
    # 参数: 输入通道数, 分割任务个数, 是否使用双线插值, 网络中第一个卷积通道个数
    def __init__(self,
                 in_channels: int = 1,
                 num_classes: int = 2,
                 bilinear: bool = True,
                 base_c: int = 64):
        super(UNet, self).__init__()
        self.in_channels = in_channels
        self.num_classes = num_classes
        self.bilinear = bilinear

        self.in_conv = DoubleConv(in_channels, base_c)


        # 下采样,参数:输入通道,输出通道
        self.down1 = Down(base_c, base_c * 2)
        self.down2 = Down(base_c * 2, base_c * 4)
        self.down3 = Down(base_c * 4, base_c * 8)


        # 如果采用双线插值上采样为 2,采用转置矩阵上采样为 1
        factor = 2 if bilinear else 1
        # 最后一个下采样,如果是双线插值则输出通道为512,否则为1024
        self.down4 = Down(base_c * 8, base_c * 16 // factor)


        # 上采样,参数:输入通道,输出通道
        self.up1 = Up(base_c * 16, base_c * 8 // factor, bilinear)
        self.up2 = Up(base_c * 8, base_c * 4 // factor, bilinear)
        self.up3 = Up(base_c * 4, base_c * 2 // factor, bilinear)
        self.up4 = Up(base_c * 2, base_c, bilinear)
        # 最后的1*1输出卷积
        self.out_conv = OutConv(base_c, num_classes)

        # SE模块
        self.SE1 = SE_Block(base_c)
        self.SE2 = SE_Block(base_c * 2)
        self.SE3 = SE_Block(base_c * 4)
        self.SE4 = SE_Block(base_c * 8)
        self.SE5 = SE_Block(base_c * 16 // factor)
        self.SE6 = SE_Block(base_c * 8 // factor)
        self.SE7 = SE_Block(base_c * 4 // factor)
        self.SE8 = SE_Block(base_c * 2 // factor)
        self.SE9 = SE_Block(num_classes)

    # 正向传播过程
    def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
        # 1. 定义最开始的两个卷积层
        x1 = self.in_conv(x)
        x1 = self.SE1(x1)
        # 2. contracting path(收缩路径)
        x2 = self.down1(x1)
        x2 = self.SE2(x2)
        x3 = self.down2(x2)
        x3 = self.SE3(x3)
        x4 = self.down3(x3)
        x4 = self.SE4(x4)
        x5 = self.down4(x4)
        x5 = self.SE5(x5)
        # 3. expanding path(扩展路径)
        x = self.up1(x5, x4)
        x = self.SE6(x)
        x = self.up2(x, x3)
        x = self.SE7(x)
        x = self.up3(x, x2)
        x = self.SE8(x)
        x = self.up4(x, x1)
        # 4. 最后1*1输出卷积
        logits = self.out_conv(x)
        logits = self.SE9(logits)

        return {"out": logits}

(4)只在最后的输出卷积后,添加SE模块

 模型修改代码

from typing import Dict
import torch
import torch.nn as nn
import torch.nn.functional as F

# se-unet04
'''-------------一、SE模块-----------------------------'''
#全局平均池化+1*1卷积核+ReLu+1*1卷积核+Sigmoid
class SE_Block(nn.Module):
    def __init__(self, inchannel, ratio=16):
        super(SE_Block, self).__init__()
        # 全局平均池化(Fsq操作)
        self.gap = nn.AdaptiveAvgPool2d((1, 1))
        # 两个全连接层(Fex操作)
        self.fc = nn.Sequential(
            nn.Linear(inchannel, inchannel // ratio, bias=False),  # 从 c -> c/r
            nn.ReLU(),
            nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> c
            nn.Sigmoid()
        )

    def forward(self, x):
            # 读取批数据图片数量及通道数
            b, c, h, w = x.size()
            # Fsq操作:经池化后输出b*c的矩阵
            y = self.gap(x).view(b, c)
            # Fex操作:经全连接层输出(b,c,1,1)矩阵
            y = self.fc(y).view(b, c, 1, 1)
            # Fscale操作:将得到的权重乘以原来的特征图x
            return x * y.expand_as(x)

# 卷积,在uent中卷积一般成对使用
class DoubleConv(nn.Sequential):
    # 输入通道数, 输出通道数, mid_channels为成对卷积中第一个卷积层的输出通道数
    def __init__(self, in_channels, out_channels, mid_channels=None):
        if mid_channels is None:
            mid_channels = out_channels
        super(DoubleConv, self).__init__(
            # 3*3卷积,填充为1,卷积之后输入输出的特征图大小一致
            nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

# 下采样
class Down(nn.Sequential):
    def __init__(self, in_channels, out_channels):
        super(Down, self).__init__(
            # 1.最大池化的窗口大小为2, 步长为2
            nn.MaxPool2d(2, stride=2),
            # 2.两个卷积
            DoubleConv(in_channels, out_channels)
        )

# 上采样
class Up(nn.Module):
    # bilinear是否采用双线性插值
    def __init__(self, in_channels, out_channels, bilinear=True):
        super(Up, self).__init__()
        if bilinear:
            # 使用双线性插值上采样
            # 上采样率为2,双线性插值模式
            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
        else:
            # 使用转置卷积上采样
            self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)
            self.conv = DoubleConv(in_channels, out_channels)

    def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:
        x1 = self.up(x1)
        # [N, C, H, W]
        # 上采样之后的特征图与要拼接的特征图,高度方向的差值
        diff_y = x2.size()[2] - x1.size()[2]
        # 上采样之后的特征图与要拼接的特征图,宽度方向的差值
        diff_x = x2.size()[3] - x1.size()[3]

        # padding_left, padding_right, padding_top, padding_bottom
        # 1.填充差值
        x1 = F.pad(x1, [diff_x // 2, diff_x - diff_x // 2,
                        diff_y // 2, diff_y - diff_y // 2])

        # 2.拼接
        x = torch.cat([x2, x1], dim=1)
        # 3.卷积,两次卷积
        x = self.conv(x)
        return x

# 最后的1*1输出卷积
class OutConv(nn.Sequential):
    def __init__(self, in_channels, num_classes):
        super(OutConv, self).__init__(
            nn.Conv2d(in_channels, num_classes, kernel_size=1)
        )


class UNet(nn.Module):
    # 参数: 输入通道数, 分割任务个数, 是否使用双线插值, 网络中第一个卷积通道个数
    def __init__(self,
                 in_channels: int = 1,
                 num_classes: int = 2,
                 bilinear: bool = True,
                 base_c: int = 64):
        super(UNet, self).__init__()
        self.in_channels = in_channels
        self.num_classes = num_classes
        self.bilinear = bilinear

        self.in_conv = DoubleConv(in_channels, base_c)


        # 下采样,参数:输入通道,输出通道
        self.down1 = Down(base_c, base_c * 2)
        self.down2 = Down(base_c * 2, base_c * 4)
        self.down3 = Down(base_c * 4, base_c * 8)


        # 如果采用双线插值上采样为 2,采用转置矩阵上采样为 1
        factor = 2 if bilinear else 1
        # 最后一个下采样,如果是双线插值则输出通道为512,否则为1024
        self.down4 = Down(base_c * 8, base_c * 16 // factor)


        # 上采样,参数:输入通道,输出通道
        self.up1 = Up(base_c * 16, base_c * 8 // factor, bilinear)
        self.up2 = Up(base_c * 8, base_c * 4 // factor, bilinear)
        self.up3 = Up(base_c * 4, base_c * 2 // factor, bilinear)
        self.up4 = Up(base_c * 2, base_c, bilinear)
        # 最后的1*1输出卷积
        self.out_conv = OutConv(base_c, num_classes)

        # SE模块
        self.SE5 = SE_Block(num_classes)

    # 正向传播过程
    def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
        # 1. 定义最开始的两个卷积层
        x1 = self.in_conv(x)

        # 2. contracting path(收缩路径)
        x2 = self.down1(x1)
        x3 = self.down2(x2)

        x4 = self.down3(x3)

        x5 = self.down4(x4)
        # 3. expanding path(扩展路径)
        x = self.up1(x5, x4)
        x = self.up2(x, x3)
        x = self.up3(x, x2)
        x = self.up4(x, x1)
        # 4. 最后1*1输出卷积
        logits = self.out_conv(x)
        logits = self.SE5(logits)

        return {"out": logits}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt基础之三十六:异常处理

本文将介绍如何在Qt中使用try...catch和调试dump文件来处理异常。 Qt版本5.12.6 一.使用try...catch 一段简单的捕获异常的代码,新建一个控制台工程,pro文件不用修改 #include <QCoreApplication> #include <QDebug>int main(int argc, char *argv[]) {QCoreA…

历届蓝桥杯青少年编程比赛 计算思维题真题解析【已更新5套 持续更新中】

一、计算思维组考试范围 计算思维组面向小学生&#xff08;7-12 岁&#xff0c;约 1-6 年级&#xff09;&#xff0c;通过设计多个角度的考核题目、层次科学的试卷组合、线上限时的考试形式&#xff0c;更加精确地考查学生的计算能力、反应能力、思维与分析能力&#xff0c;使…

注解实现自动装配

要使用注解须知&#xff1a; 1.导入约束 context约束 2.配置注解的支持 官方配置文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/…

Flume实践

1 NetCat方式 ]# ./bin/flume-ng agent --conf conf--conf-file ./conf/flume_netcat.conf --name a1 -Dflume.root.loggerINFO,console [rootmaster ~]# yum -y intalll telnet 发数据&#xff1a; ]# telnet master 44444 数据接收&#xff0c;是在终端上接收的&#xff0…

[创业之路-70] :聊天的最高境界因场景不同而不同

一、聊天的最高境界因场景不同而不同。 销售式聊天&#xff1a; 聊天的最高境界不是真相&#xff0c;而是拨动对方的心弦&#xff0c;让对方心理爽&#xff0c;让对方舒心。聊天的结果是你买单。 消费式聊天&#xff1a; 聊天的最高境界不是真相&#xff0c;而是让自己心理爽…

Java面试集锦

1. html与jsp区别&#xff1f; 答&#xff1a;HTML是文本标记语言&#xff0c;它是静态页面&#xff1b;JSP页面是有JSP容器执行该页面的Java代码部分然后实时生成动态页面&#xff0c;可动态更新页面上的内容。 在jsp中用<%%>就可以写Java代码了&#xff0c;而html没有…

数组的存储和压缩

数组 定义 一维数组是有限个相同类型的数据元素构成的序列&#xff0c;逻辑关系是相邻关系 推广&#xff1a;一个二维数组可以看作相同类型的一维数组的一维数组&#xff1b;n维数组可以看作以n-1维数组作为元素的线性表 性质 数组中的数据元素数目…

提前布局

深圳最近的天气是美&#xff0c;有风&#xff0c;天空可以看得见飘来飘去的透明的云&#xff0c;傍晚时候也可以看见城市被霞光笼罩下那种很安静要睡去的样子&#xff0c;随处可以见到的青草绿叶和大树&#xff0c;从公司楼下穿过的一个小桥洞前看得见的红绿路牌也安静和谐。 今…

将 Nacos 转变为 Windows 系统服务,实现开机自启

文章目录 前言下载 WinSW配置 WinSW安装和启动 Nacos 服务联系我 前言 本文将为您介绍如何使用 WinSW 工具将 Nacos 打包成 Windows 系统服务&#xff0c;并实现开机自启动的便利功能。通过将 Nacos 安装为系统服务&#xff0c;您将摆脱每次手动启动的麻烦&#xff0c;从而提高…

论文阅读_增强语言模型综述

论文信息 name_en: Augmented Language Models: a Survey name_ch: 增强语言模型综述 paper_addr: http://arxiv.org/abs/2302.07842 date_read: 2023-05-20 date_publish: 2023-02-15 tags: [‘深度学习’,‘自然语言处理’,‘大模型’] author: Grgoire Mialon&#xff0c;M…

jQuery样式操作和效果操作

1. css方法 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width,…

day8--链表倒数第k个结点

链表中倒数最后k个结点 双指针法 是定义两个指针p和q&#xff0c;先让p指向链表的头结点&#xff0c;然后让q指向第k个结点。 接着&#xff0c;同时移动p和q&#xff0c;直到q指向链表的尾结点。此时&#xff0c;p指向的结点就是倒数第k个结点 struct ListNode {int val;Li…

git commit规范

目录 一、代码提交风格&#xff1a; 二、代码提交验证: 一、代码提交风格&#xff1a; 通常我们的git commit会按照统一的风格来提交&#xff0c;这样可以快速定位每次提交的内容&#xff0c;方便之后对版本进行控制。 但是如果每次手动来编写这些是比较麻烦的事情&#xff0…

【Flutter】widgets (1) 组件概述 widget tree 常见的widgets

文章目录 一、前言二、如何理解 widgets三、widgets 和 Div 布局很像四、常见的组件五、总结一、前言 在 Flutter 中,所有的 UI 元素都被称为 widgets,包括整个应用程序本身。一个 Flutter 应用通常由多个小的 widgets 组合而成,这些 widgets 可以是文本,按钮,图片,甚至…

JetBrains的C和C++集成开发环境CLion 2023版本在Linux系统的下载与安装配置教程

目录 前言一、CLion安装二、使用配置总结 前言 CLion是一款为C和C语言开发人员设计的集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的功能和工具&#xff0c;可以帮助开发人员更高效地编写、调试和部署C和C应用程序。注&#xff1a;已在CentOS7.9和Ubuntu20.04安装测…

《数据库》期末考试复习手写笔记-第10章 数据库恢复技术(日志文件+检查点)【10分】

知识点:事务+日志文件+检查点 考题一:日志记录 考题二:数据库恢复 如果一个数据库恢复系统采用检查点机制,且其日志文件如表4所示

深入了解平均精度(mAP):通过精确率-召回率曲线评估目标检测性能

平均精度&#xff08;Average Precision&#xff0c;mAP&#xff09;是一种常用的用于评估目标检测模型性能的指标。在目标检测任务中&#xff0c;模型需要识别图像中的不同目标&#xff0c;并返回它们的边界框&#xff08;bounding box&#xff09;和类别。mAP用于综合考虑模型…

开源情报搜集系统的核心技术

随着科技快速发展&#xff0c;科研方向的开源情报搜集系统的应用越来越广泛。为了满足科研工作者的需求&#xff0c;开发人员大力研发了许多功能强大的科研开源情报系统。这些系统不仅可以帮助科研人员更加高效地获取、管理和利用科研信息资源&#xff0c;还能为他们提供全方位…

【Android工具】免费好用无广告安卓手机解压缩软件工具:ZArchiver

微信关注公众号 “DLGG创客DIY” 设为“星标”&#xff0c;重磅干货&#xff0c;第一时间送达。 前言 压缩工具在日常工作和生活中很常用&#xff0c;不光可以减小文件大小&#xff0c;还可以将多个文件进行打包&#xff0c;方便管理。 当然还有一些其他的特殊功能&#xff0c;…

奇舞周刊第493期:Hook 革命!浅谈 React 新 Hook 的未来与思想

关注前端生态发展&#xff0c;了解行业动向。 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ Hook 革命&#xff01;浅谈 React 新 Hook 的未来与思想 作者阳羡曾写文章对 React 新 Hook use 的设计理念和限制进行了深入分析&#xff0c;并提供了一个可能的实现来帮助读者…