【第二十周】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 可以分为三部分:
第一部分是主干特征提取部分,遵循经典的卷积网络架构,是卷积和最大池化的堆叠,利用主干特征提取部分我们可以获得五个初步有效特征层。
第二部分是加强特征提取部分,对主干特征提取部分得到的五个初步有效特征层进行逐步的上采样和拼接融合,得到与第一个初步有效特征层有相同通道数的特征层。
第三部分是预测部分,对第二部分得到的特征层进行卷积操作,对每一个像素点分类,得到图像分割结果图。
下面对具体的卷积层和上采样层进行说明:

  1. 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倍(通道数加倍以弥补下采样带来的损失)。
  2. copy and crop:对主干特征提取部分得到的前四个初步有效特征层进行裁剪,以便与上采样得来的特征层进行拼接。
  3. 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,池化前后的通道数不变,宽高减半。
  4. up-conv 2 × 2 2\times 2 2×2:上采样,可用转置卷积或双线性插值等,转置卷积前后宽高加倍,通道数减半。
  5. 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 F

class 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 激活函数

    @staticmethod
    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,
                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 ITC=OT
在这里插入图片描述
转置卷积就是按照此思想还原出输出的形状,注意,转置卷积不是卷积的逆运算,而只是形状上的相反关系:
O T ∗ C T = I T O^T*C^T=I^T OTCT=IT
在这里插入图片描述
转置卷积的操作:

  1. 在输入特征图元素间填充s-1行、列0(其中s表示转置卷积的步距)
  2. 在输入特征图四周填充k-p-1行、列0(其中k表示转置卷积的kernel_size大小,p为转置卷积的padding,注意这里的padding和卷积操作中有些不同)
  3. 将卷积核参数上下、左右翻转
  4. 做正常卷积运算(填充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 nn


def 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

根据输出可知,官方的转置卷积函数与通过以上的步骤实现的转置卷积的结果一致。

创新性与不足

创新性:

  1. 编码器-解码器结构:编码器通过卷积和池化操作逐步提取特征,降低分辨率。解码器通过上采样和卷积操作逐步恢复分辨率。这种结构能够有效地捕捉图像的全局信息和局部细节。
  2. 跳跃连接:在编码器和解码器之间引入了跳跃连接,将低层特征图与高层特征图拼接。保留了低层特征的细节信息,有助于精确分割目标边界。
  3. 数据增强策略:训练过程中使用了弹性形变等数据增强技术。提高了模型在少量标注数据上的泛化能力。

不足:

  1. 对小目标的检测能力有限:尽管 U-Net 通过跳跃连接保留了低层特征,但其低层特征的语义信息不足,在处理极小目标时仍可能丢失细节信息。
  2. 对遮挡目标的处理能力有限: 此模型难以区分重叠目标的边界。
  3. 长距离依赖性不足:UNet捕捉全局上下文的能力有限,虽然跳连接保留了部分空间信息,但在处理更大范围内的上下文关系时,传统卷积操作仍然难以有效捕捉远距离的依赖性。

总结

U-NetU-Net 采用对称的编码器-解码器设计,编码器通过卷积和池化操作提取特征,解码器通过上采样和卷积操作恢复分辨率。这种架构允许U-Net进行端到端训练,并有效地从有限的数据集中学习。U-Net 最初用于医学图像分割(如细胞分割、肿瘤检测),但其高效的架构和强大的性能使其迅速扩展到其他领域,包括:卫星图像分析、工业检测、自然图像处理等。尽管存在一些局限性,但通过不断的改进和优化,U-Net 及其变体在图像分割任务中仍然具有广泛的应用前景。

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

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

相关文章

GD32F303 GCC 环境搭建

一、引言 在嵌入式开发领域&#xff0c;GD32F303 微控制器以其出色的性能和丰富的功能被广泛应用。为了充分发挥其潜力&#xff0c;搭建一个高效的开发环境并深入理解项目构建过程至关重要。本文将详细介绍如何基于 GCC 工具链搭建 GD32F303 的开发环境&#xff0c;重点聚焦于…

【语言处理和机器学习】概述篇(基础小白入门篇)

前言 自学笔记&#xff0c;分享给语言学/语言教育学方向的&#xff0c;但对语言数据处理感兴趣但是尚未入门&#xff0c;却需要在论文中用到的小伙伴&#xff0c;欢迎大佬们补充或绕道。ps&#xff1a;本文不涉及公式讲解&#xff08;文科生小白友好体质&#xff09;&#xff…

智能系统的感知和决策

智能系统在感知和决策过程中具备的关键能力表现在智能感知/自主判定上&#xff0c;下面可以从感知的本质、自主判断的含义及其在智能系统中的作用进行深入分析。 1、智能感知&#xff1a;信息获取与理解 智能感知是指智能系统通过传感器或其他数据采集手段获取环境中的信息&…

AD7606, 逐次逼近型ADC以及一次被GPT坑了的过程.

首先, 我的项目中, 已有的一个ADC芯片, 8通道, 并行, Analog家的ad7606, 在采集高速的正弦信号的时候, 我发现采集到的值怎么都不太对. 但是宏观来看, 并没有太大问题, 首先我怀疑的是量程问题, 接入一个5伏直流, 得到的读数确实是接近16bit的正半量程的读数, 32xxx. 接着我用信…

16.5万煤气柜柜位计故障分析

一、事故经过&#xff1a; 2015年8月1&#xff14;日20点45分&#xff0c;16.5万立煤气柜柜顶油封溢流口有大量油液溢出&#xff0c;此时雷达柜位计在计算机上示值为63.79米&#xff0c;由于接近傍晚天色较暗&#xff0c;岗位操作员并未及时发现这一异常状况。22点45分左右&…

成就与远见:2024年技术与思维的升华

个人主页&#xff1a;chian-ocean 前言: 2025年1月17日&#xff0c;2024年博客之星年度评选——创作影响力评审的入围名单公布。我很荣幸能够跻身Top 300&#xff0c;虽然与顶尖博主仍有一定差距&#xff0c;但这也为我提供了更加明确的发展方向与指引。展望崭新的2025年&…

Android 天气APP(二十七)增加地图天气的逐小时天气、太阳和月亮数据

imageId R.mipmap.icon_213d; break; case “300”: imageId R.mipmap.icon_300d; break; case “301”: imageId R.mipmap.icon_301d; break; case “302”: imageId R.mipmap.icon_302d; break; case “303”: imageId R.mipmap.icon_303d; break; case “…

vue2使用flv.js在浏览器打开flv格式视频

组件地址&#xff1a;GitHub - bilibili/flv.js: HTML5 FLV Player flv.js 仅支持 H.264 和 AAC/MP3 编码的 FLV 文件。如果视频文件使用了其他编码格式就打不开。 flv.vue <template><div><el-dialog :visible.sync"innerVisibleFlv" :close-on-pre…

Restormer模型代码解析

上一篇我们对Restormer的论文进行了解析。这篇对Restormer的代码进行解析。 论文地址&#xff1a;Restormer: Efficient Transformer for High-Resolution Image Restoration。代码地址&#xff1a;Restormer 以Deraining项目中的test.py文件为切入点&#xff0c;来分析其mod…

微信小程序怎么制作自己的小程序?手把手带你入门(适合新手小白观看)

对于初学者来说&#xff0c;制作一款微信小程序总感觉高大上&#xff0c;又害怕学不会。不过&#xff0c;今天我就用最简单、最有耐心的方式&#xff0c;一步一步给大家讲清楚!让你知道微信小程序的制作&#xff0c;居然可以这么轻松(希望你别吓跑啊!)。文中还加了实战经验&…

MPLS VPN 部署与应用

一.简介 MPLS&#xff0c;称之为多协议标签交换&#xff0c;在九十年代中期被提出来&#xff0c;用于解决传统IP报文依赖查表转发而产生的瓶颈&#xff0c;现多用于VPN技术&#xff0c;MPLS报头封装在数据链路层之上&#xff0c;网络层之下。本文为结合了华为技术和新华三技术…

定时器setTimeout和setInterval

setTimeOut()异步 setInterval()异步

PCL 部分点云视点问题【2025最新版】

目录 一、问题概述二、解决方案1、软件实现2、代码实现三、调整之后博客长期更新,本文最近更新时间为:2025年1月18日。 一、问题概述 针对CloudCompare软件处理过的pcd格式点云,在使用PCL进行特征点提取、配准等实验中最终显示结果出现点云位置偏差较大的问题,本博客给出解…

SpringCloud+Vue+Python人工智能(fastAPI,机器学习,深度学习)前后端架构各功能实现思路——主目录(持续更新)

随着公司业务的增加&#xff0c;公司需要一个javapython人工智能相互配合架构&#xff0c;正常网站业务用java来做&#xff0c;而ai&#xff0c;例如电价预测等回归任务&#xff0c;以及大模型预测全网负荷&#xff0c;新能源出力等任务&#xff0c;使用python通过fastapi暴露接…

Python数据可视化(够用版):懂基础 + 专业的图表抛给Tableau等专业绘图工具

我先说说文章标题中的“够用版”啥意思&#xff0c;为什么这么写。 按照我个人观点&#xff0c;在使用Python进行数据分析时&#xff0c;我们有时候肯定要结合到图表去进行分析&#xff0c;去直观展现数据的规律和特定&#xff0c;那么我们肯定要做一些简单的可视化&#xff0…

[微服务]注册中心优化

环境隔离 企业实际开发中&#xff0c;往往会搭建多个运行环境&#xff0c;例如&#xff1a; 开发环境测试环境预发布环境生产环境 这些不同环境之间的服务和数据之间需要隔离。 还有的企业中&#xff0c;会开发多个项目&#xff0c;共享nacos集群。此时&#xff0c;这些项目…

【Python使用】嘿马python高级进阶全体系教程第11篇:静态Web服务器-面向对象开发,1. 以面向对象的方式开发静态W

本教程的知识点为&#xff1a;操作系统 1. 常见的操作系统 4. 小结 ls命令选项 2. 小结 mkdir和rm命令选项 1. mkdir命令选项 压缩和解压缩命令 1. 压缩格式的介绍 2. tar命令及选项的使用 3. zip和unzip命令及选项的使用 4. 小结 编辑器 vim 1. vim 的介绍 2. vim 的工作模式 …

无限续杯Cursor方案

解决方案一&#xff1a; 每个账号可以删除三次&#xff0c;如图点击Delete Account&#xff0c;删除账户并重新登录即可获得免费试用。 解决方案二 1.首先判断电脑系统类型&#xff1a; Windows 系统 打开 设置&#xff08;Win I&#xff09;。进入 系统 > 关于。在 …

AI编程工具使用技巧:在Visual Studio Code中高效利用阿里云通义灵码

AI编程工具使用技巧&#xff1a;在Visual Studio Code中高效利用阿里云通义灵码 前言一、通义灵码介绍1.1 通义灵码简介1.2 主要功能1.3 版本选择1.4 支持环境 二、Visual Studio Code介绍1.1 VS Code简介1.2 主要特点 三、安装VsCode3.1下载VsCode3.2.安装VsCode3.3 打开VsCod…

2024国游销量前20游戏分析:某开放世界武侠(排名11)

1、销量约20万套&#xff0c;销售额1400万人民币。 与一代的发售间隔为三年。 虽然对于网游大厂来说这个数字不够看&#xff0c;但对一个小团队来说足够维持了&#xff0c;三年的运营成本不是小数目。 2、开发商属于国内最早做3DMMO的厂商之一&#xff0c;创始人曾在国外大学…