神经网络(四):UNet图像分割网络
文章目录
- 一、简介
- 二、网络结构
- 2.1编码器部分
- 2.2解码器部分
- 2.3完整代码
- 三、实战案例
论文链接:点击跳转
一、简介
UNet网络是一种用于图像分割的卷积神经网络,其特点是采用了U型网络结构,因此称为UNet。该网络具有编码器和解码器结构,两种结构的功能如下:
- 编码器:逐步提取输入图像的特征并降低空间分辨率。
- 解码器:通过上采样操作将特征图恢复到原始输入图像的尺寸,并逐步生成分割结果。
【CNN角度的编码器、解码器】以卷积神经网络为例,输入为一个猫,进行特征提取后输出图片类别。
- 编码器:完成对输入图片中猫的特征提取。
- 解码器:将特征提取的结果解码为分类结果。

【RNN角度的编码器、解码器】以循环神经网络LSTM为例,输入为一个文本,进行特征提取再输出
- 编码器:将文本表示为向量并实现特征提取。
- 解码器:将向量转化为输出。

UNet算法的关键创新是在解码器中引入了跳跃连接(Skip Connections),即将编码器中的特征图与解码器中对应的特征图进行连接。这种跳跃连接可以帮助解码器更好地利用不同层次的特征信息,从而提高图像分割的准确性和细节保留能力。
二、网络结构
UNet的设计思想是通过编码器逐渐提取丰富的低级特征和高级特征,然后通过解码器逐渐恢复分辨率,并将低级特征和高级特征进行融合,以便获取准确且具有上下文信息的分割结果。这种U字形结构使得UNet能够同时利用全局(高分辨率时的特征图)和局部信息(低分辨率时的特征图),适用于图像分割任务。执行过程可粗略描述为:
输入层 -> 编码器(下采样模块 + 编码器模块) -> 解码器(上采样模块 + 解码器模块)-> 输出层。
即:
- 编码器(Encoder)部分:
- 输入层:接受输入图像作为模型的输入。
- 下采样模块(Downsampling Block):由一系列卷积层(通常是卷积、批归一化和激活函数的组合)和池化层组成,用于逐渐减小特征图的尺寸和通道数。这样可以逐渐提取出更高级别的特征信息。
- 编码器模块(Encoder Block):重复使用多个下采样模块,以便逐渐减小特征图的尺寸和通道数。每个编码器模块通常包含一个下采样模块和一个跳跃连接(Skip Connection),将上一级的特征图连接到下一级,以便在解码器中进行特征融合。
- 解码器(Decoder)部分:
- 上采样模块(Upsampling Block):由一系列上采样操作(如反卷积或转置卷积)和卷积操作组成,用于逐渐增加特征图的尺寸和通道数。这样可以逐渐恢复分辨率并且保留更多的细节信息。
- 解码器模块(Decoder Block):重复使用多个上采样模块,以便逐渐增加特征图的尺寸和通道数。每个解码器模块通常包含一个上采样模块、一个跳跃连接和一个融合操作(如拼接或加权求和),用于将来自编码器的特征图与当前解码器的特征图进行融合。
- 输出层:最后一层是一个卷积层,用于生成最终的分割结果。通常,输出层的通道数等于任务中的类别数,并应用适当的激活函数(如sigmoid或softmax),以产生每个像素点属于各个类别的概率分布。
跳跃连接(skip connection):输入数据直接添加到网络某一层输出之上。这种设计使得信息可以更自由地流动,并且保留了原始输入数据中的细节和语义信息。 使信息更容易传播到后面的层次,避免了信息丢失。跳跃连接通常会通过求和操作或拼接操作来实现。
以图像分类任务为例,假设我们使用卷积神经网络进行特征提取,在每个卷积层后面都加入一个池化层来减小特征图尺寸。然而,池化操作可能导致信息损失。通过添加一个跳跃连接,将原始输入直接与最后一个池化层输出相加或拼接起来,可以保留原始图像中更多的细节和语义信息。
2.1编码器部分

编码器部分由多个下采样模块(down sampling step)组成,每个下采样模块都由两个卷积层(卷积核大小为3x3,且与ReLU函数配合使用。由于图像尺寸变小,可见并未填充)和一个最大池化层(池化核大小2x2,步幅为2,将图像尺寸收缩一半)组成,并且每一次下采样操作后特征图的通道数均增加一倍。
事实上,随着不断执行下采样模块(也成为收缩路径),特征图通道数随着卷积操作也不断增加,从而获取了图像的更多特征。并且在进入下一下采样模块前,进行 2x2 最大池化以获得最大像素值,虽然丢失一些特征,但保留最大像素值。通过这种方式,可将图像中目标的像素按类别进行分割。每一下采样模块的实现代码如下:
【第一个下采样模块】
卷积操作:
self.conv1_1 = nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=1, padding=0)#(572,572,1)->((572-3+1),(572-3+1),64)->(570,570,64)self.relu1_1 = nn.ReLU(inplace=True)self.conv1_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=0) # (570,570,64)->((570-3+1),(570-3+1),64)->(568,568,64)self.relu1_2 = nn.ReLU(inplace=True)
池化操作
#采用最大池化进行下采样,图片大小减半,通道数不变,由(568,568,64)->(284,284,64)
self.maxpool_1 = nn.MaxPool2d(kernel_size=2, stride=2)
【第二个下采样模块】
卷积操作:
self.conv2_1 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=0) #(284,284,64)->(282,282,128)self.relu2_1 = nn.ReLU(inplace=True)self.conv2_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=0) #(282,282,128)->(280,280,128)self.relu2_2 = nn.ReLU(inplace=True)
池化操作:
# 采用最大池化进行下采样(280,280,128)->(140,140,128)
self.maxpool_2 = nn.MaxPool2d(kernel_size=2, stride=2)
编码器部分总代码:
class Unet(nn.Module):def __init__(self):super(Unet, self).__init__()#第一个下采样模块self.conv1_1 = nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=1, padding=0)self.relu1_1 = nn.ReLU(inplace=True)self.conv1_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=0) self.relu1_2 = nn.ReLU(inplace=True)self.maxpool_1 = nn.MaxPool2d(kernel_size=2, stride=2) #第二个下采样模块self.conv2_1 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=0) #(284,284,64)->(282,282,128)self.relu2_1 = nn.ReLU(inplace=True)self.conv2_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=0) #(282,282,128)->(280,280,128)self.relu2_2 = nn.ReLU(inplace=True)self.maxpool_2 = nn.MaxPool2d(kernel_size=2, stride=2) #第三个下采样模块self.conv3_1 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=0)self.relu3_1 = nn.ReLU(inplace=True)self.conv3_2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=0)self.relu3_2 = nn.ReLU(inplace=True)self.maxpool_3 = nn.MaxPool2d(kernel_size=2, stride=2)#第四个下采样模块self.conv4_1 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=0)self.relu4_1 = nn.ReLU(inplace=True)self.conv4_2 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=0)self.relu4_2 = nn.ReLU(inplace=True)self.maxpool_4 = nn.MaxPool2d(kernel_size=2, stride=2) #第五个下采样模块self.conv5_1 = nn.Conv2d(in_channels=512, out_channels=1024, kernel_size=3, stride=1, padding=0) # 32*32*512->30*30*1024self.relu5_1 = nn.ReLU(inplace=True)self.conv5_2 = nn.Conv2d(1024, 1024, kernel_size=3, stride=1, padding=0)self.relu5_2 = nn.ReLU(inplace=True)
在五个下采样操作后,特征图大小变为 ( 28 , 28 , 1024 ) (28,28,1024) (28,28,1024)。
2.2解码器部分

- up-conv 2x2:上采样操作,通过反卷积操作实现。
- copy and crop:复制和裁剪,将下采样模块输出的特征图进行复制和裁剪,方便和上采样生成的特征图进行拼接。
下采样操作后,虽然使用最大池化运算丢失了部分细节信息,但模型已经得到了所有类的像素特征值。而在上采样中,模型将特征图通过具有相同级别的上采样过滤器来将特征图恢复到原始的分辨率大小,并通过跳跃连接的方式将下采样模块输出的每个特征图都添加到上采样模块的相应特征层中,从而保留特征。最后,通过完整的特征图即可定位每个类在图像中存在的位置,从而实现类别的定位。
第一个上采样模块细节如下:

最下面的下采样模块输出特征图大小为 ( 28 , 28 , 1024 ) (28,28,1024) (28,28,1024),经过反卷积操作(up-conv 2x2)得到大小为 ( 56 , 56 , 512 ) (56,56,512) (56,56,512)的特征图,即尺寸扩大一倍,通道数减半。之后,将左侧下采样模块输出的 ( 64 , 64 , 512 ) (64,64,512) (64,64,512)图像进行复制并中心裁剪(copy and crop)同样转化为 ( 56 , 56 , 512 ) (56,56,512) (56,56,512)大小,并与之拼接得到 ( 56 , 56 , 1024 ) (56,56,1024) (56,56,1024)大小的特征图(可见,此拼接仅是通道方向的拼接)。代码实现:
# 上采样中反卷积操作的实现
self.up_conv_1 = nn.ConvTranspose2d(in_channels=1024, out_channels=512, kernel_size=2, stride=2, padding=0) # 28*28*1024->56*56*512
同理也可得到其他反卷积操作的实现:
self.up_conv_2 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=2, stride=2, padding=0) # 52*52*512->104*104*256
self.up_conv_3 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=2, stride=2, padding=0) # 100*100*256->200*200*128
self.up_conv_4 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=2, stride=2, padding=0) # 196*196*128->392*392*64
右半部分卷积操作的代码实现:
【第一次卷积】
self.conv6_1 = nn.Conv2d(in_channels=1024, out_channels=512, kernel_size=3, stride=1, padding=0) # 56*56*1024->54*54*512self.relu6_1 = nn.ReLU(inplace=True)self.conv6_2 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=0) # 54*54*512->52*52*512self.relu6_2 = nn.ReLU(inplace=True)
【第二次卷积】
self.conv7_1 = nn.Conv2d(in_channels=512, out_channels=256, kernel_size=3, stride=1, padding=0) # 104*104*512->102*102*256self.relu7_1 = nn.ReLU(inplace=True)self.conv7_2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=0) # 102*102*256->100*100*256self.relu7_2 = nn.ReLU(inplace=True)
【第三次卷积】
self.conv8_1 = nn.Conv2d(in_channels=256, out_channels=128, kernel_size=3, stride=1, padding=0) # 200*200*256->198*198*128self.relu8_1 = nn.ReLU(inplace=True)self.conv8_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=0) # 198*198*128->196*196*128self.relu8_2 = nn.ReLU(inplace=True)
【第四次卷积】
self.conv9_1 = nn.Conv2d(in_channels=128, out_channels=64, kernel_size=3, stride=1, padding=0) # 392*392*128->390*390*64self.relu9_1 = nn.ReLU(inplace=True)self.conv9_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=0) # 390*390*64->388*388*64self.relu9_2 = nn.ReLU(inplace=True)
【第五次卷积】
# 最后的conv1*1self.conv_10 = nn.Conv2d(in_channels=64, out_channels=2, kernel_size=1, stride=1, padding=0) #64x388x388->2x388x388
中心裁剪操作的实现:
# 中心裁剪,def crop_tensor(self, tensor, target_tensor):target_size = target_tensor.size()[2]tensor_size = tensor.size()[2]delta = tensor_size - target_sizedelta = delta // 2# 如果原始张量的尺寸为10,而delta为2,那么"delta:tensor_size - delta"将截取从索引2到索引8的部分,长度为6,以使得截取后的张量尺寸变为6。return tensor[:, :, delta:tensor_size - delta, delta:tensor_size - delta]
【第一次上采样+拼接】
# 第一次上采样,需要"Copy and crop"(复制并裁剪)up1 = self.up_conv_1(x10) # 得到56*56*512# 需要对x8进行裁剪,从中心往外裁剪crop1 = self.crop_tensor(x8, up1)# 拼接操作up_1 = torch.cat([crop1, up1], dim=1)
【第二次上采样+拼接】
# 第二次上采样,需要"Copy and crop"(复制并裁剪)up2 = self.up_conv_2(y2)# 需要对x6进行裁剪,从中心往外裁剪crop2 = self.crop_tensor(x6, up2)# 拼接up_2 = torch.cat([crop2, up2], dim=1)
【第三次上采样+拼接】
# 第三次上采样,需要"Copy and crop"(复制并裁剪)up3 = self.up_conv_3(y4)# 需要对x4进行裁剪,从中心往外裁剪crop3 = self.crop_tensor(x4, up3)up_3 = torch.cat([crop3, up3], dim=1)
【第四次上采样+拼接】
# 第四次上采样,需要"Copy and crop"(复制并裁剪)up4 = self.up_conv_4(y6)# 需要对x2进行裁剪,从中心往外裁剪crop4 = self.crop_tensor(x2, up4)up_4 = torch.cat([crop4, up4], dim=1)
2.3完整代码

import torch
import torch.nn as nnclass Unet(nn.Module):def __init__(self):super(Unet, self).__init__()self.conv1_1 = nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=1, padding=0) # 由572*572*1变成了570*570*64self.relu1_1 = nn.ReLU(inplace=True)self.conv1_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=0) # 由570*570*64变成了568*568*64self.relu1_2 = nn.ReLU(inplace=True)self.maxpool_1 = nn.MaxPool2d(kernel_size=2, stride=2) # 采用最大池化进行下采样,图片大小减半,通道数不变,由568*568*64变成284*284*64self.conv2_1 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=0) # 284*284*64->282*282*128self.relu2_1 = nn.ReLU(inplace=True)self.conv2_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=0) # 282*282*128->280*280*128self.relu2_2 = nn.ReLU(inplace=True)self.maxpool_2 = nn.MaxPool2d(kernel_size=2, stride=2) # 采用最大池化进行下采样 280*280*128->140*140*128self.conv3_1 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=0) # 140*140*128->138*138*256self.relu3_1 = nn.ReLU(inplace=True)self.conv3_2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=0) # 138*138*256->136*136*256self.relu3_2 = nn.ReLU(inplace=True)self.maxpool_3 = nn.MaxPool2d(kernel_size=2, stride=2) # 采用最大池化进行下采样 136*136*256->68*68*256self.conv4_1 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=0) # 68*68*256->66*66*512self.relu4_1 = nn.ReLU(inplace=True)self.conv4_2 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=0) # 66*66*512->64*64*512self.relu4_2 = nn.ReLU(inplace=True)self.maxpool_4 = nn.MaxPool2d(kernel_size=2, stride=2) # 采用最大池化进行下采样 64*64*512->32*32*512self.conv5_1 = nn.Conv2d(in_channels=512, out_channels=1024, kernel_size=3, stride=1, padding=0) # 32*32*512->30*30*1024self.relu5_1 = nn.ReLU(inplace=True)self.conv5_2 = nn.Conv2d(1024, 1024, kernel_size=3, stride=1, padding=0) # 30*30*1024->28*28*1024self.relu5_2 = nn.ReLU(inplace=True)# 接下来实现上采样中的up-conv2*2self.up_conv_1 = nn.ConvTranspose2d(in_channels=1024, out_channels=512, kernel_size=2, stride=2, padding=0) # 28*28*1024->56*56*512self.conv6_1 = nn.Conv2d(in_channels=1024, out_channels=512, kernel_size=3, stride=1, padding=0) # 56*56*1024->54*54*512self.relu6_1 = nn.ReLU(inplace=True)self.conv6_2 = nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=0) # 54*54*512->52*52*512self.relu6_2 = nn.ReLU(inplace=True)self.up_conv_2 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=2, stride=2, padding=0) # 52*52*512->104*104*256self.conv7_1 = nn.Conv2d(in_channels=512, out_channels=256, kernel_size=3, stride=1, padding=0) # 104*104*512->102*102*256self.relu7_1 = nn.ReLU(inplace=True)self.conv7_2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=0) # 102*102*256->100*100*256self.relu7_2 = nn.ReLU(inplace=True)self.up_conv_3 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=2, stride=2, padding=0) # 100*100*256->200*200*128self.conv8_1 = nn.Conv2d(in_channels=256, out_channels=128, kernel_size=3, stride=1, padding=0) # 200*200*256->198*198*128self.relu8_1 = nn.ReLU(inplace=True)self.conv8_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=0) # 198*198*128->196*196*128self.relu8_2 = nn.ReLU(inplace=True)self.up_conv_4 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=2, stride=2, padding=0) # 196*196*128->392*392*64self.conv9_1 = nn.Conv2d(in_channels=128, out_channels=64, kernel_size=3, stride=1, padding=0) # 392*392*128->390*390*64self.relu9_1 = nn.ReLU(inplace=True)self.conv9_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=0) # 390*390*64->388*388*64self.relu9_2 = nn.ReLU(inplace=True)# 最后的conv1*1self.conv_10 = nn.Conv2d(in_channels=64, out_channels=2, kernel_size=1, stride=1, padding=0)# 中心裁剪,def crop_tensor(self, tensor, target_tensor):target_size = target_tensor.size()[2]tensor_size = tensor.size()[2]delta = tensor_size - target_sizedelta = delta // 2# 如果原始张量的尺寸为10,而delta为2,那么"delta:tensor_size - delta"将截取从索引2到索引8的部分,长度为6,以使得截取后的张量尺寸变为6。return tensor[:, :, delta:tensor_size - delta, delta:tensor_size - delta]def forward(self, x):x1 = self.conv1_1(x)x1 = self.relu1_1(x1)x2 = self.conv1_2(x1)x2 = self.relu1_2(x2) # 这个后续需要使用down1 = self.maxpool_1(x2)x3 = self.conv2_1(down1)x3 = self.relu2_1(x3)x4 = self.conv2_2(x3)x4 = self.relu2_2(x4) # 这个后续需要使用down2 = self.maxpool_2(x4)x5 = self.conv3_1(down2)x5 = self.relu3_1(x5)x6 = self.conv3_2(x5)x6 = self.relu3_2(x6) # 这个后续需要使用down3 = self.maxpool_3(x6)x7 = self.conv4_1(down3)x7 = self.relu4_1(x7)x8 = self.conv4_2(x7)x8 = self.relu4_2(x8) # 这个后续需要使用down4 = self.maxpool_4(x8)x9 = self.conv5_1(down4)x9 = self.relu5_1(x9)x10 = self.conv5_2(x9)x10 = self.relu5_2(x10)# 第一次上采样,需要"Copy and crop"(复制并裁剪)up1 = self.up_conv_1(x10) # 得到56*56*512# 需要对x8进行裁剪,从中心往外裁剪crop1 = self.crop_tensor(x8, up1)up_1 = torch.cat([crop1, up1], dim=1)y1 = self.conv6_1(up_1)y1 = self.relu6_1(y1)y2 = self.conv6_2(y1)y2 = self.relu6_2(y2)# 第二次上采样,需要"Copy and crop"(复制并裁剪)up2 = self.up_conv_2(y2)# 需要对x6进行裁剪,从中心往外裁剪crop2 = self.crop_tensor(x6, up2)up_2 = torch.cat([crop2, up2], dim=1)y3 = self.conv7_1(up_2)y3 = self.relu7_1(y3)y4 = self.conv7_2(y3)y4 = self.relu7_2(y4)# 第三次上采样,需要"Copy and crop"(复制并裁剪)up3 = self.up_conv_3(y4)# 需要对x4进行裁剪,从中心往外裁剪crop3 = self.crop_tensor(x4, up3)up_3 = torch.cat([crop3, up3], dim=1)y5 = self.conv8_1(up_3)y5 = self.relu8_1(y5)y6 = self.conv8_2(y5)y6 = self.relu8_2(y6)# 第四次上采样,需要"Copy and crop"(复制并裁剪)up4 = self.up_conv_4(y6)# 需要对x2进行裁剪,从中心往外裁剪crop4 = self.crop_tensor(x2, up4)up_4 = torch.cat([crop4, up4], dim=1)y7 = self.conv9_1(up_4)y7 = self.relu9_1(y7)y8 = self.conv9_2(y7)y8 = self.relu9_2(y8)# 最后的conv1*1out = self.conv_10(y8)return out
if __name__ == '__main__':input_data = torch.randn([1, 1, 572, 572])unet = Unet()output = unet(input_data)print(output.shape)# torch.Size([1, 2, 388, 388])
三、实战案例
准备复现论文:点击跳转、点击跳转
准备复现项目:点击跳转
相关文章:
神经网络(四):UNet图像分割网络
文章目录 一、简介二、网络结构2.1编码器部分2.2解码器部分2.3完整代码 三、实战案例 论文链接:点击跳转 一、简介 UNet网络是一种用于图像分割的卷积神经网络,其特点是采用了U型网络结构,因此称为UNet。该网络具有编码器和解码器结构&#…...
Java 编码系列:注解处理器详解与面试题解析
引言 在上一篇文章中,我们详细探讨了 Java 注解的基本概念、自定义注解、元注解等技术。本文将继续深入探讨 Java 注解处理器(Annotation Processor),介绍如何编写注解处理器,并结合大厂的最佳实践和面试题详细解析其…...
C语言 | Leetcode C语言题解之第441题排列硬币
题目: 题解: class Solution { public:int arrangeCoins(int n) {return (int) ((sqrt((long long) 8 * n 1) - 1) / 2);} };...
Linux noVNC远程桌面(xfce)部署
一、安装 VNC 服务器和桌面环境 Notebook实验 常用vnc服务 VNC (Virtual Network Computing) 是一种远程桌面协议,可以让你通过网络访问服务器的图形界面。 TurboVNC:专为图形密集型应用设计,尤其适合 3D 可视化和高分辨率图像的远程传输…...
【网络安全】身份认证
1. 身份认证 1.1 定义 身份认证(Authentication)是确认用户身份的过程,确保只有授权的用户才能访问系统或资源。它通常涉及验证用户提供的凭证,如密码、生物特征或其他识别标志。 1.2 重要性 身份认证是信息安全的第一道防线&…...
LeetCode - #124 二叉树中的最大路径和(Top 100)
文章目录 前言1. 描述2. 示例3. 答案关于我们前言 本题为 LeetCode 前 100 高频题 我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新到 123 期…...
Java:插入排序
目录 排序的概念 插入排序 直接插入排序 哈希排序 排序的概念 排序:所谓的排序,就是使一串记录,按照某个或某些关键字的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在多个…...
How FAR ARE WE FROM AGI?(ICLR AGI Workshop 2024)概览
关注B站可以观看更多实战教学视频:hallo128的个人空间 How FAR ARE WE FROM AGI?官网 How FAR ARE WE FROM AGI?(ICLR AGI Workshop 2024) 该研讨会将于2024年5月11日在奥地利维也纳以混合模式举行,作为 ICLR 2024年会议的一部…...
leetcode刷题day33|动态规划Part02(62.不同路径、63. 不同路径 II、 343.整数拆分、96.不同的二叉搜索树)
62.不同路径 机器人从(0 , 0) 位置出发,到(m - 1, n - 1)终点。 动规五部曲 1、确定dp数组(dp table)以及下标的含义 dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路…...
基于Python大数据的B站热门视频的数据分析及可视化系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…...
matlab-批处理图像质量变化并形成折线图 (PSNR)
%修改路径就能用,图片分辨率要一致 %clc;clear all;close all;tic;%清理内存 file_pathE:\test\resources\image\;% 批量图像所在的文件夹下 file_save_pathE:\test\resources\SaveImage\;% 要存储的地址 img_path_listdir(strcat(file_path,*.jpg));% 获取批量bm…...
[Doc][Ros2]ros2中Qos(Quality of Service,服务质量)介绍
在 ROS 2 中,QoS(Quality of Service,服务质量)是用于控制节点之间消息传递的可靠性、历史存储和数据持久性等方面的机制。通过 QoS 设置,用户可以更细粒度地控制消息传递的行为,确保在不同网络环境或应用场景中满足特定的通信需求。 几个常用的包: QoSProfile: 含义…...
SpringBoot日志集成-LogBack
Log4J:最早的Java日志框架之一,由Apache基金会发起,提供灵活而强大的日志记录机制JDK自带的日志框架:java.util.logging.Logg,是JDK1.4之后提供的日志API,已淘汰logback: logback一个开源的日志…...
Google BigTable架构详解
文章目录 什么是BigTable?架构图一、整体架构二、数据存储与索引存储模型 三、数据拆分与存储四、元数据管理五、读写流程 其他内容概览负载平衡其他存储和数据库选项 什么是BigTable? Bigtable是Google开发的一个高性能、可扩展的分布式存储系统,用于管理大规模…...
【python】如何切换ipynb的kernel至指定conda环境
需求介绍 打开(若无新建环境) 环境 conda env list conda activate cvml conda install ipykernel python -m ipykernel install --name cvml 以上完成后,打开jupyter 创建一个python文件 在kernel——>change kernel——>python[conda env:cvml] 参考资料…...
Linux【基础指令汇总】
目录 Linux命令的特点 1、文件管理 ls命令 cp命令 mkdir命令 mv命令 pwd命令 2、文档编辑 cat命令 echo命令 rm命令 tail命令 rmdir命令 3、系统管理 rpm命令 find命令 startx命令 uname命令 vmstat命令 4、磁盘管理 df命令 fdisk命令 lsblk命令 hdpar…...
SpringCloud-EurekaClient
创建Module pom.xml <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency> spring:application:name: provider # 应用程序的名称,…...
配置Scrapy项目
配置Scrapy项目是一个涉及多个步骤的过程,在上一篇博客中已经写了安装Scrapy、创建Scrapy项目的步骤。 接下来应该定义Item类、编写爬虫程序以及配置settings.py文件等。以下是一个详细的配置Scrapy项目的步骤: 一、定义Item类 在项目目录下…...
航顺芯片HK32MCU受邀出席汽车芯片国产化与技术创新闭门研讨会
[中国,北京,2024年9月21日]近日,深圳市航顺芯片技术研发有限公司(以下简称“航顺芯片”)产品总监郑增忠受邀出席由中国设备管理协会新能源汽车产业发展促进中心主办的“汽车芯片国产化与技术创新闭门研讨会”。 会上航…...
【深度学习】(6)--图像数据增强
文章目录 图像数据增强一、作用二、增强方法三、代码体现四、增强体现 总结 图像数据增强 数据增强(Data Augmentation),也称为数据增广,是一种在机器学习和深度学习中常用的技术,它通过对现有数据进行各种变换和处理…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...

