当前位置: 首页 > news >正文

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

目录

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

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

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

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


数据集:refuge视盘数据集

训练轮次:50

评价指标:dice coefficient、mean IOU

Architecture
dice coefficientmean IOU
unet0.98961.1
se-unet010.98963.3
se-unet020.98860.5
se-unet030.98867.3
se-unet040.98967.2

(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/rnn.ReLU(),nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> cnn.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操作:将得到的权重乘以原来的特征图xreturn 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_channelssuper(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, 步长为2nn.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_channelsself.num_classes = num_classesself.bilinear = bilinearself.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,采用转置矩阵上采样为 1factor = 2 if bilinear else 1# 最后一个下采样,如果是双线插值则输出通道为512,否则为1024self.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}

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

 模型改动代码

from typing import Dict
import torch
import torch.nn as nn
import torch.nn.functional as F# se-unet02
'''-------------一、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/rnn.ReLU(),nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> cnn.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操作:将得到的权重乘以原来的特征图xreturn 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_channelssuper(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, 步长为2nn.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_channelsself.num_classes = num_classesself.bilinear = bilinearself.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,采用转置矩阵上采样为 1factor = 2 if bilinear else 1# 最后一个下采样,如果是双线插值则输出通道为512,否则为1024self.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)# 正向传播过程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)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/rnn.ReLU(),nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> cnn.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操作:将得到的权重乘以原来的特征图xreturn 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_channelssuper(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, 步长为2nn.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_channelsself.num_classes = num_classesself.bilinear = bilinearself.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,采用转置矩阵上采样为 1factor = 2 if bilinear else 1# 最后一个下采样,如果是双线插值则输出通道为512,否则为1024self.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)# 正向传播过程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)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/rnn.ReLU(),nn.Linear(inchannel // ratio, inchannel, bias=False),  # 从 c/r -> cnn.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操作:将得到的权重乘以原来的特征图xreturn 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_channelssuper(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, 步长为2nn.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_channelsself.num_classes = num_classesself.bilinear = bilinearself.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,采用转置矩阵上采样为 1factor = 2 if bilinear else 1# 最后一个下采样,如果是双线插值则输出通道为512,否则为1024self.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.SE4 = SE_Block(base_c)# 正向传播过程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)x = self.SE4(x)# 4. 最后1*1输出卷积logits = self.out_conv(x)return {"out": logits}

相关文章:

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

目录 (1)se-unet01(在卷积后,下采样前,添加SE模块) (2)se-unet02(在卷积后,上采样前,添加SE模块) (3)se-un…...

JVM垃圾回收篇之相关概念和算法

垃圾回收相关概念 什么是垃圾 垃圾就是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收掉的垃圾,如果不及时进行清理,越积越多就会导致内存溢出. 为什么需要GC 不进行回收,早晚会导致内存溢出,Java自动管理垃圾回收,不需要开发人员手动干预,这就有可能导致开…...

(学习日记)2023.04.27

写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈。 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录&a…...

亚马逊CPC广告每日该怎么调整?

01 CPC广告需要每日调整吗? 其实,亚马逊广告是不建议每天都做过多调整的。 为什么呢?调整太频繁了,看不到每天调整的结果是不是? 什么时候需要调整呢? 就是广告指标,比如说曝光、点击、转化率情…...

ffmpeg下载及ffmpy3安装使用

ffmpeg下载及ffmpy3安装使用 1.下载ffmpeg 进入网址:https://www.gyan.dev/ffmpeg/builds/ 在release builds中下载ffmpeg-release-full.7z 下载好后解压到自己想存放的目录,例如:D:\Tool\ffmpeg-6.0-full_build 2.配置环境变量 右键此电…...

设计模式之~原型模式

定义:用原型实例指导创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。 优点: 一般在初始化的信息不发生变化的情况下,克隆是最…...

多传感器融合SLAM --- 8.LIO-SAM基础知识解读

目录 1 惯性测量单元简介及预积分 1.1 IMU 器件介绍及选型建议 1.2 IMU状态传递方程...

多模态大模型时代下的文档图像智能分析与处理

多模态大模型时代下的文档图像智能分析与处理 0. 前言1. 人工智能发展历程1.1 传统机器学习1.2 深度学习1.3 多模态大模型时代 2. CCIG 文档图像智能分析与处理论坛2.1 文档图像智能分析与处理的重要性和挑战2.2 文档图像智能分析与处理高峰论坛2.3 走进合合信息 3. 文档图像智…...

SAP-MM-内向外向交货单

1、内向&外向交货单概念 外向交货(outbound delivery)是用在客户与企业之间的交货单,而内向交货(inbound delivery)则是用在供应商与企业之间的交货单;换言之,外向交货多用于SD 模块&#…...

Mysql - date、datetime、timestamp 的区别

date、datetime 的区别 顾名思义,date 日期,datetime 日期时间,所以 date 是 datetime 的日期部分MySQL 以 格式检索和显示 datetime 值 YYYY-MM-DD hh:mm:ss datetime 支持的日期时间范围 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 d…...

离散数学_十章-图 ( 4 ):图的表示和图的同构

📷10.4 图的表示和图的同构 1. 图的表示1.1 邻接表1.1.1 简单图的邻接表1.1.2 有向图的邻接表 1.2 邻接矩阵❗在邻接表和邻接矩阵之间取舍1.3 关联矩阵 2. 图同构3. ⚡判断两个简单图是否同构 图的表示方式有很多种,选择最方便的表示有助于对图的处理~ …...

MySQL锁的分类

MySQL锁的分类 全局锁 表级锁 ● 表锁 ● 元数据锁,Meta Data Lock,MDL锁 ● 意向锁 ● AUTO_INC 锁 行级锁(Innodb引擎牛比的地方) ● record lock,记录锁,也就是仅仅把一条记录给锁上了 ● gap lock,间隙锁&#xff…...

程序员如何给变量起名字

程序员如何给变量起名字 在编写代码时,为变量命名是非常重要的。良好的命名习惯可以提高代码的可读性和可维护性,使得其他开发者能够更容易地理解你的代码。在这篇文章中,我们将讨论程序员如何为变量选择合适的名称。 规范 首先&#xff0…...

隔板法(求解的组数)

文章目录 隔板法(求解的组数)隔板法扩展 例题 隔板法(求解的组数) 文章首发于我的个人博客:欢迎大佬们来逛逛 隔板法 隔板法能够解决的问题: 求线性不定方程的解的组数求相同元素分组的方案数 给我们 …...

智能文档处理黑科技,拥抱更高效的数字世界

目录 0 写在前面1 为何要关注智慧文档?2 图像弯曲矫正3 手写板反光擦除4 版面元素检测5 文档篡改检测总结 0 写在前面 近期,中国图象图形学学会文档图像分析与识别专业委员会与上海合合信息科技有限公司联合打造了《文档图像智能分析与处理》高峰论坛。…...

vue ts写法

Vue.js 和 TypeScript 结合使用可以让你的项目更加健壮和易于维护。在 Vue 3 中,你可以使用 Vue.js 的 Composition API 和 TypeScript 一起使用。以下是一个简单的 Vue.js 和 TypeScript 结合使用的例子: 首先,确保你已经安装了 Vue.js 和 T…...

Unity中的PostProcessBuild:深入解析与实用案例

Unity中的PostProcessBuild:深入解析与实用案例 在Unity游戏开发中,我们经常需要在构建完成后对生成的应用程序进行一些额外的处理。这时,我们可以使用Unity提供的PostProcessBuild功能。本文将详细介绍Unity中的PostProcessBuild方法&#…...

SimpleCG绘图函数(4)--绘制圆

在前一篇教程我们利用绘制矩形功能绘制了一个城市,接下来我们讲解另外一个同样重要且基础的图形----圆形。并一起看看该图形能绘制哪些应用呢。 绘制圆形相关函数如下: //圆心坐标(nXCenter,nYCenter),半径为nRatio//绘无填充制圆 void circle( int nXCenter, int …...

打包和优化

私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版,配图更多,CSDN博文图片需要手动上传,因此文章配图较少,看不懂的可以去菜鸡博客参考一下配图! 系列文章目录 前端系列文章——传送门 后端系列文章——传送…...

linuxOPS基础_Linux文件管理

Linux下文件命名规则 可以使用哪些字符&#xff1f; 理论上除了字符“/”之外&#xff0c;所有的字符都可以使用&#xff0c;但是要注意&#xff0c;在目录名或文件名中&#xff0c;不建议使用某些特殊字符&#xff0c;例如&#xff0c; <、>、&#xff1f;、* 等&…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...

DiscuzX3.5发帖json api

参考文章&#xff1a;PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下&#xff0c;适配我自己的需求 有一个站点存在多个采集站&#xff0c;我想通过主站拿标题&#xff0c;采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...

WEB3全栈开发——面试专业技能点P4数据库

一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库&#xff0c;基于 mysql 库改进而来&#xff0c;具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点&#xff1a; 支持 Promise / async-await&#xf…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...