【第二十周】U-Net:用于生物图像分割的卷积神经网络
文章目录
- 摘要
- Abstract
- 文章信息
- 研究动机
- U-Net网络结构
- U-Net网络搭建
- 数据增强
- 损失函数
- 转置卷积
- 创新性与不足
- 创新性:
- 不足:
- 总结
摘要
U-Net(Convolutional Networks for Biomedical Image Segmentation)是一种用于图像分割的深度学习网络,最初设计用于医学图像分割任务。其核心结构由对称的编码器-解码器组成:编码器通过卷积和池化操作逐步提取图像的抽象特征并降低分辨率,从而捕捉目标的全局语义信息;解码器通过上采样和卷积操作逐步恢复分辨率,并结合编码器提供的低层特征图(通过跳跃连接)重建目标的细节信息,从而实现精确的分割。为了解决深层网络中的细节丢失问题,U-Net 引入了跳跃连接,将编码器的低层特征图与解码器的高层特征图拼接,从而保留细节信息并提升分割精度。针对医学图像标注数据有限的问题,U-Net 采用了弹性形变的数据增强技术,增强了模型在少量数据上的泛化能力。然而,U-Net 也存在一些局限性:如对小目标的检测能力有限,对多尺度目标的适应性不足,以及对数据增强的依赖较强。为了解决这些问题,许多方法被提出,例如引入注意力机制(如 CBAM、SE Block)增强特征表达能力,使用多尺度特征融合(如空洞卷积、金字塔池化)提升对多尺度目标的适应性,以及通过自监督学习减少对标注数据的依赖。这些改不仅提升了U-Net的性能,也进一步扩大了其应用范围。
Abstract
U-Net (Convolutional Networks for Biomedical Image Segmentation) is a deep learning network designed for image segmentation, initially developed for medical image segmentation tasks. Its core structure consists of a symmetric encoder-decoder architecture: the encoder gradually extracts abstract features from the image and reduces resolution through convolutional and pooling operations, capturing the global semantic information of the target; the decoder gradually restores resolution through upsampling and convolutional operations, combining low-level feature maps from the encoder (via skip connections) to reconstruct detailed information of the target, thereby achieving precise segmentation. To address the issue of detail loss in deep networks, U-Net introduces skip connections, which concatenate low-level feature maps from the encoder with high-level feature maps from the decoder, preserving detail information and improving segmentation accuracy. To tackle the limited availability of annotated medical image data, U-Net employs data augmentation techniques such as elastic deformation, enhancing the model’s generalization ability with small datasets. However, U-Net also has some limitations: for example, its ability to detect small targets is limited, its adaptability to multi-scale targets is insufficient, and it heavily relies on data augmentation. To address these issues, many methods have been proposed, such as introducing attention mechanisms (e.g., CBAM, SE Block) to enhance feature representation, using multi-scale feature fusion (e.g., dilated convolution, pyramid pooling) to improve adaptability to multi-scale targets, and leveraging self-supervised learning to reduce dependence on annotated data. These improvements not only enhance the performance of U-Net but also further expand its application scope.
文章信息
Title:U-Net: Convolutional Networks for Biomedical Image Segmentation
Author:Olaf Ronneberger, Philipp Fischer, and Thomas Brox
Source:https://arxiv.org/abs/1505.04597
研究动机
从2012年Alexnet的提出以来,卷积神经网络已经广泛运用于计算机视觉任务。卷积网络的典型用途是分类任务,其中图像的输出是单个类别标签。然而,在许多视觉任务中,尤其是在生物医学图像处理中,期望的输出应当包括定位,即,假设将类标签分配给每个像素。此外,在生物医学任务中,用于训练的数据很少。所以,本文构建了一种全卷积网络用来分割图像,这种网络需要的训练图像很少,却能产生精确的分割结果。
U-Net网络结构
U-Net网络是一个全卷积网络,是一个编码-解码的结构,有基本的对称性。
U-Net的网络架构如上图所示,U-Net 可以分为三部分:
第一部分是主干特征提取部分,遵循经典的卷积网络架构,是卷积和最大池化的堆叠,利用主干特征提取部分我们可以获得五个初步有效特征层。
第二部分是加强特征提取部分,对主干特征提取部分得到的五个初步有效特征层进行逐步的上采样和拼接融合,得到与第一个初步有效特征层有相同通道数的特征层。
第三部分是预测部分,对第二部分得到的特征层进行卷积操作,对每一个像素点分类,得到图像分割结果图。
下面对具体的卷积层和上采样层进行说明:
- conv 3 × 3 3\times 3 3×3,ReLU:此结构中所有的卷积都是 s t r i d e = 1 stride = 1 stride=1, p a d d i n g = 0 padding = 0 padding=0,此结构在主干特征提取部分使用,除连接输入的卷积层用的第一个卷积核通道是64外,其他卷积层的通道数都是输入通道数的2倍(通道数加倍以弥补下采样带来的损失)。
- copy and crop:对主干特征提取部分得到的前四个初步有效特征层进行裁剪,以便与上采样得来的特征层进行拼接。
- max pool 2 × 2 2\times 2 2×2:做 2 × 2 2\times 2 2×2的最大池化操作(下采样), s t r i d e = 2 stride = 2 stride=2, p a d d i n g = 0 padding = 0 padding=0,池化前后的通道数不变,宽高减半。
- up-conv 2 × 2 2\times 2 2×2:上采样,可用转置卷积或双线性插值等,转置卷积前后宽高加倍,通道数减半。
- conv 1 × 1 1\times 1 1×1:卷积核为 1 × 1 1\times 1 1×1的卷积操作, s t r i d e = 1 stride = 1 stride=1, p a d d i n g = 0 padding = 0 padding=0,用在预测部分,卷积后,通道数变为类别数(包括背景类别)。
U-Net 还是一个编码-解码的结构,编码器就是下采样路径,通过卷积和池化操作提取特征。解码器就是上采样路径,通过上采样和跳跃连接恢复分辨率。
U-Net网络搭建
首先,为了方便搭建网络,可以定义一个标准的卷积块。另外吗,本网络不是严格按中文中的网络设置进行搭建的。在实现中,实际上可以在下采样的卷积操作时设置 padding = 1 ,以保持卷积前后的宽高不变,这样在与上采样的结果进行拼接时就不需要裁剪(文中的上采样后宽高加倍),最终得到的分割结果也是和输入图像的尺寸一样,而不是输入图像中间的一部分。
def _block(in_channels, features, name):"""定义一个标准的卷积块。参数:in_channels (int): 输入通道数。features (int): 输出通道数。name (str): 块的名称(用于调试)。返回:nn.Sequential: 包含两个卷积层、两个批归一化层和两个 ReLU 激活函数的序列模块。"""return nn.Sequential(nn.Conv2d(in_channels=in_channels,out_channels=features,kernel_size=3,padding=1, #不同于原文,设置 padding=1 使卷积前后的宽高保持不变bias=False,),nn.BatchNorm2d(num_features=features), # 批归一化层nn.ReLU(inplace=True), # ReLU 激活函数nn.Conv2d(in_channels=features,out_channels=features,kernel_size=3,padding=1,bias=False,),nn.BatchNorm2d(num_features=features), # 批归一化层nn.ReLU(inplace=True), # ReLU 激活函数)
标准卷积块的参数信息:
in_channels:是卷积块的输入通道数,int型;
features:是卷积块的输出通道数,int型;
name:是块的名称,方便调试,string型。
本函数返回的是经过两组卷积、Batchnormal、ReLu操作后的结果。
接下来搭建编码器部分:
先说明网络中的参数:
in_channels : 输入图像的通道数(例如,灰度图为 1,RGB 图为 3),int型。
out_channels : 输出图像的通道数(例如,二分类任务为 1,多分类任务为类别数),int型。
init_features : 初始特征通道数(决定网络的宽度),int型。
features = init_features #定义编码器的通道数# 编码器部分(下采样路径)# 第一个编码器块self.encoder1 = UNet._block(in_channels, features, name="enc1") # 最大池化层self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2) # 第二个编码器块self.encoder2 = UNet._block(features, features * 2, name="enc2") # 最大池化层self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2) # 第三个编码器块self.encoder3 = UNet._block(features * 2, features * 4, name="enc3") # 最大池化层self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2) # 第四个编码器块self.encoder4 = UNet._block(features * 4, features * 8, name="enc4") # 最大池化层self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)
编码器和解码器之间还有一个连接的部分:
# 底部(最深层)self.bottleneck = UNet._block(features * 8, features * 16, name="bottleneck")
下面搭建解码器部分:
# 上采样层self.upconv4 = nn.ConvTranspose2d(features * 16, features * 8, kernel_size=2, stride=2) # 第四个解码器块self.decoder4 = UNet._block(features * 16, features * 8, name="dec4")# 上采样层 self.upconv3 = nn.ConvTranspose2d(features * 8, features * 4, kernel_size=2, stride=2) # 第三个解码器块self.decoder3 = UNet._block(features * 8, features * 4, name="dec3") # 上采样层self.upconv2 = nn.ConvTranspose2d(features * 4, features * 2, kernel_size=2, stride=2) # 第二个解码器块self.decoder2 = UNet._block(features * 4, features * 2, name="dec2") # 上采样层self.upconv1 = nn.ConvTranspose2d(features * 2, features, kernel_size=2, stride=2) # 第一个解码器块self.decoder1 = UNet._block(features * 2, features, name="dec1")
下面是输出层(预测层):
# 输出层self.conv = nn.Conv2d(in_channels=features, out_channels=out_channels, kernel_size=1) # 1x1 卷积层
整体网络搭建:
import torch
import torch.nn as nn
import torch.nn.functional as Fclass UNet(nn.Module):def __init__(self, in_channels=1, out_channels=1, init_features=32):"""U-Net 初始化函数。参数:in_channels (int): 输入图像的通道数(例如,灰度图为 1,RGB 图为 3)。out_channels (int): 输出图像的通道数(例如,二分类任务为 1,多分类任务为类别数)。init_features (int): 初始特征通道数(决定网络的宽度)。"""super(UNet, self).__init__()# 定义编码器的特征通道数features = init_features# 编码器部分(下采样路径)self.encoder1 = UNet._block(in_channels, features, name="enc1") # 第一个编码器块self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2) # 最大池化层self.encoder2 = UNet._block(features, features * 2, name="enc2") # 第二个编码器块self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2) # 最大池化层self.encoder3 = UNet._block(features * 2, features * 4, name="enc3") # 第三个编码器块self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2) # 最大池化层self.encoder4 = UNet._block(features * 4, features * 8, name="enc4") # 第四个编码器块self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2) # 最大池化层# 底部(最深层)self.bottleneck = UNet._block(features * 8, features * 16, name="bottleneck") # 瓶颈层# 解码器部分(上采样路径)self.upconv4 = nn.ConvTranspose2d(features * 16, features * 8, kernel_size=2, stride=2) # 上采样层self.decoder4 = UNet._block(features * 16, features * 8, name="dec4") # 第四个解码器块self.upconv3 = nn.ConvTranspose2d(features * 8, features * 4, kernel_size=2, stride=2) # 上采样层self.decoder3 = UNet._block(features * 8, features * 4, name="dec3") # 第三个解码器块self.upconv2 = nn.ConvTranspose2d(features * 4, features * 2, kernel_size=2, stride=2) # 上采样层self.decoder2 = UNet._block(features * 4, features * 2, name="dec2") # 第二个解码器块self.upconv1 = nn.ConvTranspose2d(features * 2, features, kernel_size=2, stride=2) # 上采样层self.decoder1 = UNet._block(features * 2, features, name="dec1") # 第一个解码器块# 输出层self.conv = nn.Conv2d(in_channels=features, out_channels=out_channels, kernel_size=1) # 1x1 卷积层def forward(self, x):"""前向传播函数。参数:x (torch.Tensor): 输入图像张量,形状为 (batch_size, in_channels, height, width)。返回:torch.Tensor: 输出分割结果,形状为 (batch_size, out_channels, height, width)。"""# 编码器部分enc1 = self.encoder1(x) # 第一个编码器块enc2 = self.encoder2(self.pool1(enc1)) # 第二个编码器块enc3 = self.encoder3(self.pool2(enc2)) # 第三个编码器块enc4 = self.encoder4(self.pool3(enc3)) # 第四个编码器块# 底部(最深层)bottleneck = self.bottleneck(self.pool4(enc4)) # 瓶颈层# 解码器部分dec4 = self.upconv4(bottleneck) # 上采样dec4 = torch.cat((dec4, enc4), dim=1) # 跳跃连接(拼接编码器的特征图)dec4 = self.decoder4(dec4) # 第四个解码器块dec3 = self.upconv3(dec4) # 上采样dec3 = torch.cat((dec3, enc3), dim=1) # 跳跃连接dec3 = self.decoder3(dec3) # 第三个解码器块dec2 = self.upconv2(dec3) # 上采样dec2 = torch.cat((dec2, enc2), dim=1) # 跳跃连接dec2 = self.decoder2(dec2) # 第二个解码器块dec1 = self.upconv1(dec2) # 上采样dec1 = torch.cat((dec1, enc1), dim=1) # 跳跃连接dec1 = self.decoder1(dec1) # 第一个解码器块# 输出层return torch.sigmoid(self.conv(dec1)) # 1x1 卷积 + Sigmoid 激活函数@staticmethoddef _block(in_channels, features, name):"""定义一个标准的卷积块。参数:in_channels (int): 输入通道数。features (int): 输出通道数。name (str): 块的名称(用于调试)。返回:nn.Sequential: 包含两个卷积层、两个批归一化层和两个 ReLU 激活函数的序列模块。"""return nn.Sequential(nn.Conv2d(in_channels=in_channels,out_channels=features,kernel_size=3,padding=1,bias=False,),nn.BatchNorm2d(num_features=features), # 批归一化层nn.ReLU(inplace=True), # ReLU 激活函数nn.Conv2d(in_channels=features,out_channels=features,kernel_size=3,padding=1,bias=False,),nn.BatchNorm2d(num_features=features), # 批归一化层nn.ReLU(inplace=True), # ReLU 激活函数)
下面对模型进行实例化,测试是否正常:
model = UNet(in_channels=1, out_channels=1, init_features=32)
print(model)
运行的部分结果如下:
可见输出与输入的宽高尺寸一致,各部分也都正常。
数据增强
作者使用弹性形变来进行数据增强,由于数据集的不足,并且数据集是细胞组织的图像,细胞组织的边界每时每刻都会发生不规则的畸变,所以这种弹性形变非常有必要。弹性形变可以让网络学习更稳定的图像特征。
使用随机位移矢量在粗糙的3*3网格上(random displacement vectors on a coarse 3 by 3 grid)产生平滑形变(smooth deformations)。 位移是从10像素标准偏差的高斯分布中采样的。然后使用双三次插值计算每个像素的位移。在contracting path的末尾采用drop-out 层更进一步增加数据。
损失函数
文中使用的损失函数为:pixel-wise softmax + cross_entropy
softmax函数为:
其中 K K K 代表类别数量, x x x 代表像素位置, a k ( x ) a_k(x) ak(x) 表示像素 x x x 预测为 K K K 的概率。
交叉熵损失函数:
ι:Ω→(1,2,3…K)代表true label, w 是权重
对于细胞的分割,细胞间的间隙需要设置较大的损失权重,而大片的背景区域需要设置相对较小的损失权重,下图是文中的权重热力图。
文中对损失权重的定义为:
其中, W c ( x ) W_c(x) Wc(x)是用于平衡类别频率的权重函数, d 1 d_1 d1是与最近细胞的距离, d 2 d_2 d2是与第二近的细胞的距离。
转置卷积
转置卷积(Transposed Convolution)在语义分割和对抗神经网络(GAN)中较为常见,其主要作用就是上采样。
转置卷积也是一种卷积操作,但不是卷积的逆运算。
卷积操作直观上理解就是卷积核在输入上进行滑动卷积
等效矩阵:每次一个卷积核在一个位置上的卷积操作可以等效为矩阵的乘法:
输入转换成一个向量,每一个等效矩阵转化为一个列向量,然后拼接在一起形成矩阵。
通过输入向量和卷积核矩阵的相乘获得输出向量。输出的向量经过整形便可得到我们的二维输出特征。
将输入记为 I I I,向量化的卷积向量记为 C C C,输出向量记为 O O O,则有: I T ∗ C = O T I^T*C=O^T IT∗C=OT
转置卷积就是按照此思想还原出输出的形状,注意,转置卷积不是卷积的逆运算,而只是形状上的相反关系:
O T ∗ C T = I T O^T*C^T=I^T OT∗CT=IT
转置卷积的操作:
- 在输入特征图元素间填充s-1行、列0(其中s表示转置卷积的步距)
- 在输入特征图四周填充k-p-1行、列0(其中k表示转置卷积的kernel_size大小,p为转置卷积的padding,注意这里的padding和卷积操作中有些不同)
- 将卷积核参数上下、左右翻转
- 做正常卷积运算(填充0,步距1)
下面是 s = 2 , p = 1 , k = 3 s=2,p=1,k=3 s=2,p=1,k=3的计算例子:
转置卷积后特征图的大小计算如下:
其中stride[0]表示高度方向的stride,padding[0]表示高度方向的padding,kernel_size[0]表示高度方向的kernel_size,索引[1]都表示宽度方向上的。通过上面公式可看出padding越大,输出的特征矩阵高、宽越小,你可以理解为正向卷积过程中进行了padding然后得到了特征图,现在使用转置卷积还原到原来高、宽后要把之前的padding减掉。
pytorch中给出的转置卷积函数信息如下:
输出特征图宽高计算如下:
下面对转置卷积进行实验:
import torch
import torch.nn as nndef transposed_conv_official():feature_map = torch.as_tensor([[1, 0],[2, 1]], dtype=torch.float32).reshape([1, 1, 2, 2])print(feature_map)trans_conv = nn.ConvTranspose2d(in_channels=1, out_channels=1,kernel_size=3, stride=1, bias=False)trans_conv.load_state_dict({"weight": torch.as_tensor([[1, 0, 1],[0, 1, 1],[1, 0, 0]], dtype=torch.float32).reshape([1, 1, 3, 3])})print(trans_conv.weight)output = trans_conv(feature_map)print(output)def transposed_conv_self():feature_map = torch.as_tensor([[0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0],[0, 0, 1, 0, 0, 0],[0, 0, 2, 1, 0, 0],[0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0]], dtype=torch.float32).reshape([1, 1, 6, 6])print(feature_map)conv = nn.Conv2d(in_channels=1, out_channels=1,kernel_size=3, stride=1, bias=False)conv.load_state_dict({"weight": torch.as_tensor([[0, 0, 1],[1, 1, 0],[1, 0, 1]], dtype=torch.float32).reshape([1, 1, 3, 3])})print(conv.weight)output = conv(feature_map)print(output)def main():transposed_conv_official()print("---------------")transposed_conv_self()if __name__ == '__main__':main()
transposed_conv_official函数是使用官方的转置卷积进行计算,transposed_conv_self函数是按照上面讲的步骤自己对输入特征图进行填充并通过卷积得到的结果。
终端输出为:
tensor([[[[1., 0.],[2., 1.]]]])
Parameter containing:
tensor([[[[1., 0., 1.],[0., 1., 1.],[1., 0., 0.]]]], requires_grad=True)
tensor([[[[1., 0., 1., 0.],[2., 2., 3., 1.],[1., 2., 3., 1.],[2., 1., 0., 0.]]]], grad_fn=<SlowConvTranspose2DBackward>)
---------------
tensor([[[[0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 0., 0.],[0., 0., 1., 0., 0., 0.],[0., 0., 2., 1., 0., 0.],[0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 0., 0.]]]])
Parameter containing:
tensor([[[[0., 0., 1.],[1., 1., 0.],[1., 0., 1.]]]], requires_grad=True)
tensor([[[[1., 0., 1., 0.],[2., 2., 3., 1.],[1., 2., 3., 1.],[2., 1., 0., 0.]]]], grad_fn=<ThnnConv2DBackward>)Process finished with exit code 0
根据输出可知,官方的转置卷积函数与通过以上的步骤实现的转置卷积的结果一致。
创新性与不足
创新性:
- 编码器-解码器结构:编码器通过卷积和池化操作逐步提取特征,降低分辨率。解码器通过上采样和卷积操作逐步恢复分辨率。这种结构能够有效地捕捉图像的全局信息和局部细节。
- 跳跃连接:在编码器和解码器之间引入了跳跃连接,将低层特征图与高层特征图拼接。保留了低层特征的细节信息,有助于精确分割目标边界。
- 数据增强策略:训练过程中使用了弹性形变等数据增强技术。提高了模型在少量标注数据上的泛化能力。
不足:
- 对小目标的检测能力有限:尽管 U-Net 通过跳跃连接保留了低层特征,但其低层特征的语义信息不足,在处理极小目标时仍可能丢失细节信息。
- 对遮挡目标的处理能力有限: 此模型难以区分重叠目标的边界。
- 长距离依赖性不足:UNet捕捉全局上下文的能力有限,虽然跳连接保留了部分空间信息,但在处理更大范围内的上下文关系时,传统卷积操作仍然难以有效捕捉远距离的依赖性。
总结
U-NetU-Net 采用对称的编码器-解码器设计,编码器通过卷积和池化操作提取特征,解码器通过上采样和卷积操作恢复分辨率。这种架构允许U-Net进行端到端训练,并有效地从有限的数据集中学习。U-Net 最初用于医学图像分割(如细胞分割、肿瘤检测),但其高效的架构和强大的性能使其迅速扩展到其他领域,包括:卫星图像分析、工业检测、自然图像处理等。尽管存在一些局限性,但通过不断的改进和优化,U-Net 及其变体在图像分割任务中仍然具有广泛的应用前景。
相关文章:

【第二十周】U-Net:用于生物图像分割的卷积神经网络
文章目录 摘要Abstract文章信息研究动机U-Net网络结构U-Net网络搭建数据增强损失函数转置卷积创新性与不足创新性:不足: 总结 摘要 U-Net(Convolutional Networks for Biomedical Image Segmentation)是一种用于图像分割的深度学…...

部署Metricbeat监测ES
官方参考文档 安装Metricbeat curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.17.27-linux-x86_64.tar.gztar xzvf metricbeat-7.17.27-linux-x86_64.tar.gz设置 Metricbeat连接到 Elasticsearch 进入metricbeat目录配置metricbeat.yml …...

Pytorch|YOLO
🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊 一、 前期准备 1. 设置GPU 如果设备上支持GPU就使用GPU,否则使用CPU import torch import torch.nn as nn import torchvision.transforms as transforms im…...
云计算与物联网技术的融合应用(在工业、农业、家居、医疗、环境、城市等整理较全)
摘要 为生产领域带来更加全面和深入的变革。通过云计算平台对物联网数据进行处理和分析,企业可以实现对生产过程的更加精细化的管理和控制。 1. 智能生产调度 通过云计算和物联网技术的融合应用,企业可以实现对生产线上各个环节的实时监控和数据分析。…...

基于python+Django+mysql鲜花水果销售商城网站系统设计与实现
博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育、辅导。 所有项目都配有从入门到精通的基础知识视频课程ÿ…...
Golang:报错no required module provides package github.com/xx的解决方法
报错 问题重现可能的原因及解决方法1. 未初始化 Go 模块解决方法: 2. 没有添加依赖解决方法: 3. 网络问题解决方法: 4. 依赖版本问题解决方法: 5. 包未发布或路径拼写错误解决方法: 6. go mod tidy 未运行解决方法&…...

数据结构与算法(2):顺序表与链表
1.前言 哈喽大家好喔,今天博主继续进行数据结构的分享与学习,今天的主要内容是顺序表与链表,是最简单但又相当重要的数据结构,为以后的学习有重要的铺垫,希望大家一起交流学习,互相进步,让我们…...
华为OD机试E卷 --过滤组合字符串--24年OD统一考试(Java JS Python C C++)
文章目录 题目描述输入描述输出描述用例题目解析JS算法源码Java算法源码python算法源码c算法源码c++算法源码题目描述 数字 0、1、2、3、4、5、6、7、8、9 分别关联 a~z 26 个英文字母。 0 关联“a”"b”"c1 关联“d”"e”"f2 关联“g"“h”“i”3 关…...

QT跨平台应用程序开发框架(3)—— 信号和槽
目录 一,基本概念 二,connect函数使用 2.1 connect 2.2 Qt内置信号和槽 2.3 一些细节 三,自定义信号和槽 3.1 自定义槽函数 3.2 自定义信号 3.3 带参数的信号槽 四,信号和槽的意义 五,信号和槽断开连接 六&…...

从 0 开始实现一个 SpringBoot + Vue 项目
从 0 开始实现一个 SpringBoot Vue 项目 从 0 开始实现一个 SpringBoot Vue 项目 软件和工具创建 SpringBoot 后端项目创建 MySQL 数据库配置文件实现增删改查接口 Model 层mapper 层service 层controller 层测试 实现项目功能接口 代码测试 创建 Vue 前端 安装 Node.js配置…...

【无标题】微调是迁移学习吗?
是的,微调(Fine-Tuning)可以被视为一种迁移学习(Transfer Learning)的形式。迁移学习是一种机器学习方法,其核心思想是利用在一个任务上学到的知识来改进另一个相关任务的性能。微调正是通过在预训练模型的…...

虚幻基础1:hello world
能帮到你的话,就给个赞吧 😘 文章目录 hello world创建项目创建关卡创建蓝图将蓝图插入关卡中运行 hello world 本文引擎为5.5.1 创建项目 如图 创建后如图。 创建关卡 如图 创建蓝图 如图 选择actor 双击进入蓝图节点 选择事件图表 创…...
C链表的一些基础知识
一、链表的基本概念 链表是一种常见的线性数据结构,它由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针(单链表情况)。通过指针将各个节点连接起来,与数组不同,链表在内存中的存储不是连续的…...

JDK长期支持版本(LTS)
https://blogs.oracle.com/java/post/the-arrival-of-java-23 jdk长期支持版本(LTS):JDK 8、11、17、21:...
【超详细】Python datetime(当前日期、时间戳转换、前一天日期等)【附:时区原理详解】
文章目录 相关文献当前时间前一天日期、后一天日期东八区(北京)时间时间戳转换datetime -> strstr -> datetimedatetime -> timestamp(时间戳)timestamp -> datetime 获取日期中的年、季度、月、周、日、小时、分、秒等时区原理时区问题复杂…...

【Excel】【VBA】双列排序:坐标从Y从大到小排列之后相同Y坐标的行再对X从小到大排列
Excel VBA 双列排序 功能概述 这段VBA代码实现了Excel中的双列排序功能,具体是: 跳过前3行表头先按C列数据从大到小排序在C列值相同的情况下,按B列从大到小排序排序时保持整行数据的完整性 流程图 #mermaid-svg-XJERemQluZlM4K8l {font-fa…...

为什么相关性不是因果关系?人工智能中的因果推理探秘
目录 一、背景 (一)聚焦当下人工智能 (二)基于关联框架的人工智能 (三)基于因果框架的人工智能 二、因果推理的基本理论 (一)因果推理基本范式:因果模型࿰…...
Nginx调优
Nginx 是一个高性能的反向代理服务器和负载均衡器,在处理大量并发请求时表现出色。但是,随着系统负载的增加,Nginx 的性能可能受到多方面的影响,因此进行适当的调优至关重要。以下是 Nginx 调优的几个方向和关键点: 1…...
联德胜w801开发板(四)实现腾讯云mqtt的订阅和发布
一、开发准备 在设备开发这里我们就能看到物模型的topic,跟之前用stm32esp8266一样 附上之前的链接: STM32ESP8266连接腾讯IOT上传数据(四)_stm32通过esp8266上传数据到云平台-CSDN博客https://blog.csdn.net/Try1harder/article/details/134914027?…...

LLM框架对比选择:MaxKB、Dify、FastGPT、RagFlow【RAG+AI工作流+Agent]
1.MaxKB MaxKB Max Knowledge Base,是一款基于 LLM 大语言模型的开源知识库问答系统,旨在成为企业的最强大脑。它能够帮助企业高效地管理知识,并提供智能问答功能。想象一下,你有一个虚拟助手,可以回答各种关于公司内…...
安全访问家中 Linux 服务器的远程方案 —— 专为单用户场景设计
在现代远程办公与频繁差旅的背景下,许多人需要从外地访问家中的 Linux 文件服务器,以获取重要文件。在涉及敏感数据(如客户资料、财务信息)时,数据的安全性成为首要考虑因素。以下内容将聚焦于如何在仅有一台笔记本电脑…...
OpenCV 滑动条调整图像对比度和亮度
一、知识点 1、int createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange 0, void * userdata 0); (1)、创建一个滑动条并将其附在指定窗口上。 (2)、参数说明: trackbarname: 创建的…...
rk3588 区分两个相同的usb相机
有时候会插入两个一模一样的usb相机,担心每次启动他们所对应的设备节点 /dev/video* 会变化,所以需要绑定usb口,区分两个相机。把两个相机都插入后,查看usb信息 rootrk3588:/# udevadm info --attribute-walk --name/dev/video0U…...

统信桌面专业版如何使用python开发平台jupyter
哈喽呀,小伙伴们 最近有学员想了解在统信UOS桌面专业版系统上开发python程序,Anaconda作为python开发平台,anaconda提供图形开发平台,提供大量的开发插件和管理各种插件的平台,但是存在版权问题,有没有其他工具可以替代Anaconda呢…...
Vue Fragment vs React Fragment
文章目录 前言🧩 一、概念对比:Vue Fragment vs React Fragment📦 二、使用示例对比✅ Vue 3 中使用 Fragment✅ React 中使用 Fragment 🔍 三、差异解析1. **使用方式**2. **传递属性(如 key)**3. **插槽系…...

Vue3学习(4)- computed的使用
1. 简述与使用 作用:computed 用于基于响应式数据派生出新值,其值会自动缓存并在依赖变化时更新。 缓存机制:依赖未变化时直接返回缓存值,避免重复计算(通过 _dirty 标志位实现)。响应式更新&…...
数据类型 -- 字符
在C中,字符型(char)用于存储单个字符,如字母、数字、符号等。字符型是最基本的数据类型之一,常用于处理文本、字符数组(字符串)等场景。 1. 基本类型 • char:标准字符类型&#x…...

【Java后端基础 005】ThreadLocal-线程数据共享和安全
📚博客主页:代码探秘者 ✨专栏:文章正在持续更新ing… ✅C语言/C:C(详细版) 数据结构) 十大排序算法 ✅Java基础:JavaSE基础 面向对象大合集 JavaSE进阶 Java版数据结构JDK新特性…...

图片压缩工具 | 图片生成PDF文档
OPEN-IMAGE-TINY,一个基于 Electron VUE3 的图片压缩工具,项目开源地址:https://github.com/0604hx/open-image-tiny ℹ️ 需求描述 上一版本发布后,有用户提出想要将图片转换(或者说生成更为贴切)PDF文档…...
PHP:Web 开发的强大基石与未来展望
在当今数字化时代,Web 开发技术日新月异,各种编程语言和框架层出不穷。然而,PHP 作为一种历史悠久且广泛应用的服务器端脚本语言,依然在 Web 开发领域占据着重要地位。 PHP 的历史与现状 PHP(Hypertext Preprocessor…...