【第二十周】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 大语言模型的开源知识库问答系统,旨在成为企业的最强大脑。它能够帮助企业高效地管理知识,并提供智能问答功能。想象一下,你有一个虚拟助手,可以回答各种关于公司内…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
AI语音助手的Python实现
引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
