昇思25天学习打卡营第19天 | ResNet50迁移学习再续

训练模型部分代码解析

构建Resnet50网络

两行初始化代码

weight_init = Normal(mean=0, sigma=0.02)

这行代码定义了一个初始化器weight_init,它将使用均值为0,标准差为0.02的正态分布来初始化网络中的权重。这种初始化策略有助于在网络的初始阶段避免梯度消失或爆炸的问题,因为权重被初始化为接近于0但又不至于太小的值。

gamma_init = Normal(mean=1, sigma=0.02)

这行代码定义了一个初始化器gamma_init,通常用于初始化批量归一化(Batch Normalization)层中的尺度参数(gamma)。批量归一化是一种常用的技术,用于加速神经网络的训练过程,并通过提高稳定性来改善性能。在这种情况下,gamma参数的初始化通常设置为接近1的值,因为它们在批量归一化层中起到缩放的作用,初始化为1意味着在初始阶段不会改变激活的尺度。

class ResidualBlockBase(nn.Cell):expansion: int = 1  # 最后一个卷积核数量与第一个卷积核数量相等def __init__(self, in_channel: int, out_channel: int,stride: int = 1, norm: Optional[nn.Cell] = None,down_sample: Optional[nn.Cell] = None) -> None:super(ResidualBlockBase, self).__init__()if not norm:self.norm = nn.BatchNorm2d(out_channel)else:self.norm = normself.conv1 = nn.Conv2d(in_channel, out_channel,kernel_size=3, stride=stride,weight_init=weight_init)self.conv2 = nn.Conv2d(in_channel, out_channel,kernel_size=3, weight_init=weight_init)self.relu = nn.ReLU()self.down_sample = down_sampledef construct(self, x):"""ResidualBlockBase construct."""identity = x  # shortcuts分支out = self.conv1(x)  # 主分支第一层:3*3卷积层out = self.norm(out)out = self.relu(out)out = self.conv2(out)  # 主分支第二层:3*3卷积层out = self.norm(out)if self.down_sample is not None:identity = self.down_sample(x)out += identity  # 输出为主分支与shortcuts之和out = self.relu(out)return out

这段代码定义了一个名为ResidualBlockBase的类,它是一个残差块(Residual Block)的基础实现。残差块是残差网络的基本构建块,它允许网络在增加层数的同时,仍然能够通过反向传播有效地训练。

函数解析:

  1. __init__:
    • 作用:这是一个构造函数,用于初始化残差块。它定义了残差块的基本结构和操作,包括卷积层、批量归一化层和ReLU激活函数。
    • 参数:
      • in_channel:输入通道数。
      • out_channel:输出通道数。
      • stride:卷积层的步幅。
      • norm:可选的规范化层,默认为None,此时会使用批量归一化。
      • down_sample:可选的下采样层,用于匹配输入和输出的维度,默认为None
    • 代码逻辑:
      • 调用父类的构造函数:super(ResidualBlockBase, self).__init__()
      • 判断是否提供了自定义的规范化层,如果没有,则使用批量归一化:nn.BatchNorm2d(out_channel)
      • 定义两个卷积层conv1conv2,其中conv1可能包含步幅stride用于降采样。
      • 定义ReLU激活函数relu
      • 保存可选的下采样层down_sample
  2. construct:
    • 作用:这是执行前向传播的函数,用于定义输入数据通过残差块的流程。
    • 参数:
      • x:输入数据,通常是特征图。
    • 代码逻辑:
      • 保存输入数据x作为身份映射(shortcuts分支)。
      • 通过第一个卷积层conv1处理输入数据,然后进行规范化、ReLU激活。
      • 通过第二个卷积层conv2处理激活后的数据,然后进行规范化。
      • 判断是否存在下采样层down_sample,如果存在,则对身份映射进行下采样以匹配输出的维度。
      • 将主分支的输出out与身份映射identity相加,实现跳跃连接(skip connection)。
      • 对相加的结果应用ReLU激活函数。
      • 返回最终的输出out
        残差块通过跳跃连接允许梯度直接流过身份映射,这有助于解决深度网络中的梯度消失问题,并允许网络学习更复杂的函数。
class ResidualBlockBase(nn.Cell):expansion: int = 1  # 最后一个卷积核数量与第一个卷积核数量相等def __init__(self, in_channel: int, out_channel: int,stride: int = 1, norm: Optional[nn.Cell] = None,down_sample: Optional[nn.Cell] = None) -> None:super(ResidualBlockBase, self).__init__()if not norm:self.norm = nn.BatchNorm2d(out_channel)else:self.norm = normself.conv1 = nn.Conv2d(in_channel, out_channel,kernel_size=3, stride=stride,weight_init=weight_init)self.conv2 = nn.Conv2d(in_channel, out_channel,kernel_size=3, weight_init=weight_init)self.relu = nn.ReLU()self.down_sample = down_sampledef construct(self, x):"""ResidualBlockBase construct."""identity = x  # shortcuts分支out = self.conv1(x)  # 主分支第一层:3*3卷积层out = self.norm(out)out = self.relu(out)out = self.conv2(out)  # 主分支第二层:3*3卷积层out = self.norm(out)if self.down_sample is not None:identity = self.down_sample(x)out += identity  # 输出为主分支与shortcuts之和out = self.relu(out)return out
---------------------------------------------------------------------------NameError                                 Traceback (most recent call last)Cell In[3], line 1
----> 1 class ResidualBlockBase(nn.Cell):2     expansion: int = 1  # 最后一个卷积核数量与第一个卷积核数量相等4     def __init__(self, in_channel: int, out_channel: int,5                  stride: int = 1, norm: Optional[nn.Cell] = None,6                  down_sample: Optional[nn.Cell] = None) -> None:NameError: name 'nn' is not defined

这段代码定义了一个名为ResidualBlock的类,它也是残差网络(ResNet)中的一个残差块,但是与之前的基础版残差块相比,这个类实现了更复杂的残差学习结构,这个残差块包含了三个卷积层,其中第一个和第三个卷积层是1x1的,用于降维和升维,第二个卷积层是3x3的,用于提取特征。

函数解析

  1. __init__:
    • 作用:这是一个构造函数,用于初始化残差块。它定义了残差块的基本结构和操作,包括三个卷积层、三个批量归一化层和ReLU激活函数。
    • 参数:
      • in_channel:输入通道数。
      • out_channel:输出通道数,这里的输出通道数是指第一个卷积层之后的通道数,最终的输出通道数将是这个数的4倍,由expansion变量控制。
      • stride:第二个卷积层的步幅,用于降采样。
      • down_sample:可选的下采样层,用于匹配输入和输出的维度,默认为None
    • 代码逻辑:
      • 调用父类的构造函数:super(ResidualBlock, self).__init__()
      • 定义三个卷积层conv1conv2conv3,其中conv1conv3是1x1卷积,conv2是3x3卷积,conv2可能包含步幅stride用于降采样。
      • 定义三个对应的批量归一化层norm1norm2norm3
      • 定义ReLU激活函数relu
      • 保存可选的下采样层down_sample
  2. construct:
    • 作用:这是执行前向传播的函数,用于定义输入数据通过残差块的流程。
    • 参数:
      • x:输入数据,通常是特征图。
    • 代码逻辑:
      • 保存输入数据x作为身份映射(shortcuts分支)。
      • 通过第一个卷积层conv1处理输入数据,然后进行规范化、ReLU激活。
      • 通过第二个卷积层conv2处理激活后的数据,然后进行规范化、ReLU激活。
      • 通过第三个卷积层conv3处理激活后的数据,然后进行规范化。
      • 判断是否存在下采样层down_sample,如果存在,则对身份映射进行下采样以匹配输出的维度。
      • 将主分支的输出out与身份映射identity相加,实现跳跃连接(skip connection)。
      • 对相加的结果应用ReLU激活函数。
      • 返回最终的输出out
        这个残差块通过跳跃连接允许梯度直接流过身份映射,这有助于解决深度网络中的梯度消失问题,并允许网络学习更复杂的函数。这种结构在许多图像识别任务中表现出色,并且是现代深度学习架构的重要组成部分。
def make_layer(last_out_channel, block: Type[Union[ResidualBlockBase, ResidualBlock]],channel: int, block_nums: int, stride: int = 1):down_sample = None  # shortcuts分支if stride != 1 or last_out_channel != channel * block.expansion:down_sample = nn.SequentialCell([nn.Conv2d(last_out_channel, channel * block.expansion,kernel_size=1, stride=stride, weight_init=weight_init),nn.BatchNorm2d(channel * block.expansion, gamma_init=gamma_init)])layers = []layers.append(block(last_out_channel, channel, stride=stride, down_sample=down_sample))in_channel = channel * block.expansion# 堆叠残差网络for _ in range(1, block_nums):layers.append(block(in_channel, channel))return nn.SequentialCell(layers)

这段代码定义了一个名为make_layer的函数,它的作用是创建并堆叠多个残差块(Residual Blocks),形成残差网络(ResNet)中的一个层。这个函数可以根据给定的参数生成不同数量的残差块,并且可以处理不同层的输入和输出通道数不匹配的情况。

函数解析

  1. 函数参数:
    • last_out_channel:前一个层的输出通道数。
    • block:残差块类型,可以是ResidualBlockBaseResidualBlock
    • channel:当前层的输入通道数(对于第一个残差块)。
    • block_nums:要堆叠的残差块数量。
    • stride:第一个残差块的步幅,用于降采样。
  2. 函数逻辑:
    • 首先定义一个down_sample变量,用于可能需要的下采样操作。
    • 检查是否需要下采样,即如果stride不等于1或者last_out_channel不等于channel * block.expansion,则创建一个下采样层,包括一个1x1的卷积层和一个批量归一化层。
    • 初始化一个layers列表,并将第一个残差块添加到列表中,这个残差块使用down_sample作为其下采样层。
    • 然后使用一个循环,将剩余的残差块添加到layers列表中,每个残差块的输入通道数是前一个残差块的输出通道数。
    • 最后,使用nn.SequentialCell(layers)将堆叠的残差块封装为一个顺序层(Sequential Layer),并返回这个层。
      这个函数是构建残差网络的关键部分,因为它允许开发者通过简单地指定残差块的数量和配置来创建复杂的网络结构。通过这种方式,可以很容易地增加网络的深度,而不会遇到梯度消失问题,因为残差块的结构允许梯度直接流过身份映射。
from mindspore import load_checkpoint, load_param_into_netclass ResNet(nn.Cell):def __init__(self, block: Type[Union[ResidualBlockBase, ResidualBlock]],layer_nums: List[int], num_classes: int, input_channel: int) -> None:super(ResNet, self).__init__()self.relu = nn.ReLU()# 第一个卷积层,输入channel为3(彩色图像),输出channel为64self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, weight_init=weight_init)self.norm = nn.BatchNorm2d(64)# 最大池化层,缩小图片的尺寸self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same')# 各个残差网络结构块定义,self.layer1 = make_layer(64, block, 64, layer_nums[0])self.layer2 = make_layer(64 * block.expansion, block, 128, layer_nums[1], stride=2)self.layer3 = make_layer(128 * block.expansion, block, 256, layer_nums[2], stride=2)self.layer4 = make_layer(256 * block.expansion, block, 512, layer_nums[3], stride=2)# 平均池化层self.avg_pool = nn.AvgPool2d()# flattern层self.flatten = nn.Flatten()# 全连接层self.fc = nn.Dense(in_channels=input_channel, out_channels=num_classes)def construct(self, x):x = self.conv1(x)x = self.norm(x)x = self.relu(x)x = self.max_pool(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.avg_pool(x)x = self.flatten(x)x = self.fc(x)return xdef _resnet(model_url: str, block: Type[Union[ResidualBlockBase, ResidualBlock]],layers: List[int], num_classes: int, pretrained: bool, pretrianed_ckpt: str,input_channel: int):model = ResNet(block, layers, num_classes, input_channel)if pretrained:# 加载预训练模型download(url=model_url, path=pretrianed_ckpt, replace=True)param_dict = load_checkpoint(pretrianed_ckpt)load_param_into_net(model, param_dict)return modeldef resnet50(num_classes: int = 1000, pretrained: bool = False):"ResNet50模型"resnet50_url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/resnet50_224_new.ckpt"resnet50_ckpt = "./LoadPretrainedModel/resnet50_224_new.ckpt"return _resnet(resnet50_url, ResidualBlock, [3, 4, 6, 3], num_classes,pretrained, resnet50_ckpt, 2048)

函数部分解析:

  1. __init__:
    • 作用:这是一个构造函数,用于初始化ResNet模型。它定义了模型的所有层和操作。
    • 参数:
      • block:残差块类型,可以是ResidualBlockBaseResidualBlock
      • layer_nums:一个列表,指定每个残差网络结构块中残差块的数量。
      • num_classes:输出类别数,用于全连接层的输出尺寸。
      • input_channel:全连接层的输入通道数。
    • 代码逻辑:
      • 调用父类的构造函数:super(ResNet, self).__init__()
      • 定义ReLU激活函数relu
      • 定义第一个卷积层conv1,用于处理输入图像。
      • 定义批量归一化层norm
      • 定义最大池化层max_pool,用于缩小图像尺寸。
      • 定义四个残差网络结构块layer1layer4,每个块由make_layer函数创建。
      • 定义平均池化层avg_pool
      • 定义Flatten层flatten,用于将特征图展平为一维向量。
      • 定义全连接层fc,用于最终分类。
  2. construct:
    • 作用:这是执行前向传播的函数,用于定义输入数据通过整个ResNet模型的流程。
    • 参数:
      • x:输入数据,通常是特征图。
    • 代码逻辑:
      • 通过第一个卷积层conv1处理输入数据,然后进行批量归一化和ReLU激活。
      • 通过最大池化层max_pool处理激活后的数据。
      • 通过四个残差网络结构块layer1layer4处理数据。
      • 通过平均池化层avg_pool处理数据。
      • 通过Flatten层flatten将特征图展平为一维向量。
      • 通过全连接层fc进行最终的分类。
      • 返回最终的输出。
  3. _resnet:
    • 作用:这是一个辅助函数,用于创建并初始化ResNet模型,可以选择是否加载预训练的权重。
    • 参数:
      • model_url:预训练模型的URL。
      • block:残差块类型。
      • layers:残差块的数量列表。
      • num_classes:输出类别数。
      • pretrained:一个布尔值,指示是否加载预训练模型。
      • pretrianed_ckpt:预训练模型的本地路径。
      • input_channel:全连接层的输入通道数。
    • 代码逻辑:
      • 创建ResNet模型model
      • 如果pretrained为True,则从model_url下载预训练模型并将其加载到model中。
  4. resnet50:
    • 作用:这是一个工厂函数,用于创建ResNet50模型。
    • 参数:
      • num_classes:输出类别数,默认为1000。
      • pretrained:一个布尔值,指示是否加载预训练模型,默认为False。
    • 代码逻辑:
      • 定义ResNet50模型的URL和本地检查点路径。
      • 调用_resnet函数创建并返回ResNet50模型,可以选择是否加载预训练的权重。

固定特征进行训练

这部分代码附有注释,看代码即可

import mindspore as ms
import matplotlib.pyplot as plt
import os
import timenet_work = resnet50(pretrained=True)# 全连接层输入层的大小
in_channels = net_work.fc.in_channels
# 输出通道数大小为狼狗分类数2
head = nn.Dense(in_channels, 2)
# 重置全连接层
net_work.fc = head# 平均池化层kernel size为7
avg_pool = nn.AvgPool2d(kernel_size=7)
# 重置平均池化层
net_work.avg_pool = avg_pool# 冻结除最后一层外的所有参数
for param in net_work.get_parameters():if param.name not in ["fc.weight", "fc.bias"]:param.requires_grad = False# 定义优化器和损失函数
opt = nn.Momentum(params=net_work.trainable_params(), learning_rate=lr, momentum=0.5)
loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')def forward_fn(inputs, targets):logits = net_work(inputs)loss = loss_fn(logits, targets)return lossgrad_fn = ms.value_and_grad(forward_fn, None, opt.parameters)def train_step(inputs, targets):loss, grads = grad_fn(inputs, targets)opt(grads)return loss# 实例化模型
model1 = train.Model(net_work, loss_fn, opt, metrics={"Accuracy": train.Accuracy()})

训练和评估

训练这块之前有做过类似操作,不过多赘述

import mindspore as ms
import matplotlib.pyplot as plt
import os
import time
dataset_train = create_dataset_canidae(data_path_train, "train")
step_size_train = dataset_train.get_dataset_size()dataset_val = create_dataset_canidae(data_path_val, "val")
step_size_val = dataset_val.get_dataset_size()num_epochs = 5# 创建迭代器
data_loader_train = dataset_train.create_tuple_iterator(num_epochs=num_epochs)
data_loader_val = dataset_val.create_tuple_iterator(num_epochs=num_epochs)
best_ckpt_dir = "./BestCheckpoint"
best_ckpt_path = "./BestCheckpoint/resnet50-best-freezing-param.ckpt"
import mindspore as ms
import matplotlib.pyplot as plt
import os
import time
# 开始循环训练
print("Start Training Loop ...")best_acc = 0for epoch in range(num_epochs):losses = []net_work.set_train()epoch_start = time.time()# 为每轮训练读入数据for i, (images, labels) in enumerate(data_loader_train):labels = labels.astype(ms.int32)loss = train_step(images, labels)losses.append(loss)# 每个epoch结束后,验证准确率acc = model1.eval(dataset_val)['Accuracy']epoch_end = time.time()epoch_seconds = (epoch_end - epoch_start) * 1000step_seconds = epoch_seconds/step_size_trainprint("-" * 20)print("Epoch: [%3d/%3d], Average Train Loss: [%5.3f], Accuracy: [%5.3f]" % (epoch+1, num_epochs, sum(losses)/len(losses), acc))print("epoch time: %5.3f ms, per step time: %5.3f ms" % (epoch_seconds, step_seconds))if acc > best_acc:best_acc = accif not os.path.exists(best_ckpt_dir):os.mkdir(best_ckpt_dir)ms.save_checkpoint(net_work, best_ckpt_path)print("=" * 80)
print(f"End of validation the best Accuracy is: {best_acc: 5.3f}, "f"save the best ckpt file in {best_ckpt_path}", flush=True)

可视化模型预测

看代码注释即可

import matplotlib.pyplot as plt
import mindspore as msdef visualize_model(best_ckpt_path, val_ds):net = resnet50()# 全连接层输入层的大小in_channels = net.fc.in_channels# 输出通道数大小为狼狗分类数2head = nn.Dense(in_channels, 2)# 重置全连接层net.fc = head# 平均池化层kernel size为7avg_pool = nn.AvgPool2d(kernel_size=7)# 重置平均池化层net.avg_pool = avg_pool# 加载模型参数param_dict = ms.load_checkpoint(best_ckpt_path)ms.load_param_into_net(net, param_dict)model = train.Model(net)# 加载验证集的数据进行验证data = next(val_ds.create_dict_iterator())images = data["image"].asnumpy()labels = data["label"].asnumpy()class_name = {0: "dogs", 1: "wolves"}# 预测图像类别output = model.predict(ms.Tensor(data['image']))pred = np.argmax(output.asnumpy(), axis=1)# 显示图像及图像的预测值plt.figure(figsize=(5, 5))for i in range(4):plt.subplot(2, 2, i + 1)# 若预测正确,显示为蓝色;若预测错误,显示为红色color = 'blue' if pred[i] == labels[i] else 'red'plt.title('predict:{}'.format(class_name[pred[i]]), color=color)picture_show = np.transpose(images[i], (1, 2, 0))mean = np.array([0.485, 0.456, 0.406])std = np.array([0.229, 0.224, 0.225])picture_show = std * picture_show + meanpicture_show = np.clip(picture_show, 0, 1)plt.imshow(picture_show)plt.axis('off')plt.show()

2024-07-07 23:39:40 Mindstorm

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java基础之集合

集合和数组的类比 数组: 长度固定可以存基本数据类型和引用数据类型 集合: 长度可变只能存引用数据类型存储基本数据类型要把他转化为对应的包装类 ArrayList集合 ArrayList成员方法 添加元素 删除元素 索引删除 查询 遍历数组

day30【LeetCode力扣】18.四数之和

day30【LeetCode力扣】18.四数之和 1.题目描述 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个…

Linux: Mysql环境安装

Mysql环境安装(Centos) 前言一、卸载多余环境1.1 卸载mariadb1.2 查看并卸载系统mysql和mariadb安装包 二、换取mysql官方yum源三、安装并启动mysql服务3.1 yum源加载3.2 安装yum源3.3 安装mysql服务3.3.1 安装指令3.3.2 GPG密钥问题解决方法3.3.3 查看是…

循环结构(一)——for语句【互三互三】

文章目录 🍁 引言 🍁 一、语句格式 🍁 二、语句执行过程 🍁 三、语句格式举例 🍁四、例题 👉【例1】 🚀示例代码: 👉【例2】 【方法1】 🚀示例代码: 【方法2】…

【C++ 编程】引用 - 给变量起别名、浅复制

基本语法:数据类型 &别名 原名int a 10; int &b a;引用必须初始化 (❌ int &b;),初始化后不可改变 (int c 5; b c:b 没有变成c的别名,而是 a、b 对应的值变更为了 c 的值)本质是指针常量, 浅复制 【黑马程序员匠…

Cartographer重入门到精通(二):运行作者demo及自己的数据集

在demo数据包上运行cartographer 现在Cartographer和Cartographer的Ros包已经都安装好了,你可以下载官方的数据集到指定的目录(比如在Deutsches Museum用背包采集的2D和3D 数据),然后使用roslauch来启动demo。 注:la…

IO半虚拟化-Virtio学习笔记

参考:《深入浅出DPDK》及大佬们的各种博客 Virtio简介&运行环境 Virtio 是一种用于虚拟化环境中的半虚拟化 I/O 框架,目的是在虚拟机和主机之间提供一种高效的 I/O 机制。关于什么是半虚拟化和全虚拟化:见SR-IOV学习笔记。 YES&#xf…

PDMS二次开发(二十二)——关于1.0.3.1版本升级内容的说明

目录 1.更新内容介绍2.效果演示3.关于重构自动添加焊口功能的说明3.1错误示例 3.问题交流1.创建焊口提示失败2.程序崩溃 1.更新内容介绍 在添加焊口之前先清除当前branch已有焊口;显示清除焊口的个数和添加焊口的个数;重构了自动添加焊口功能&#xff0…

值得关注的数据资产入表

不错的讲解视频,来自:第122期-杜海博士-《数据资源入表及数据资产化》-大数据百家讲坛-厦门大学数据库实验室主办第122期-杜海博士-《数据资源入表及数据资产化》-大数据百家讲坛-厦门大学数据库实验室主办-20240708_哔哩哔哩_bilibili

《A++ 敏捷开发》- 10 二八原则

团队成员协作,利用项目数据,分析根本原因,制定纠正措施,并立马尝试,判断是否有效,是改善的“基本功”。10-12章会探索里面的注意事项,13章会看两家公司的实施情况和常见问题。 如果已经获得高层…

Linq的常用方法

LINQ(Language Integrated Query)是.NET Framework中用于数据查询的组件,它将查询功能集成到C#等.NET语言中。LINQ提供了丰富的查询操作符,这些操作符可以应用于各种数据源,如内存中的集合、数据库、XML等。以下是一些…

java中的String 以及其方法(超详细!!!)

文章目录 一、String类型是什么String不可变的原因(经典面试题)String不可变的好处 二、String的常用构造形式1.使用常量串构造2.使用newString对象构造3.字符串数组构造 三、常用方法1. length() 获取字符串的长度2. charAt() 获取字符串中指定字符的值 (代码单元)3. codePoin…

水的几个科学问题及引发的思考

水的几个科学问题及引发的思考 两个相同的容器A和B,分别装有同质量的水,然后,在A容器中加入水,在B容器中加入冰,如果加入水和冰的质量相同。问,容器B的水位将与容器A的水位相同吗(假设冰未融化时…

Log4j的原理及应用详解(二)

本系列文章简介: 在软件开发的广阔领域中,日志记录是一项至关重要的活动。它不仅帮助开发者追踪程序的执行流程,还在问题排查、性能监控以及用户行为分析等方面发挥着不可替代的作用。随着软件系统的日益复杂,对日志管理的需求也日…

MySQL和SQlServer的区别

MySQL和SQlServer的区别 说明:在一些常用的SQL语句中,MySQL和SQLServer存在有一些区别,后续我也会将我遇到的不同点持续更新在这篇博客中。 1. 获取当前时间 SQLServer: -- SQLServer -- 1.获取当前时间 SELECT GETDATE(); --…

Vue2切换图片小案例

代码中 v-show "index>0",是表示下标只有大于零时上一页按钮才会显示v-show "index<list.length-1",是表示下标只有小于list数组的最大值才会显示&#xff0c;反之隐藏。click "index--"和click "index",是点击按钮后加减数…

【ZooKeeper学习笔记】

1. ZooKeeper基本概念 Zookeeper官网&#xff1a;https://zookeeper.apache.org/index.html Zookeeper是Apache Hadoop项目中的一个子项目&#xff0c;是一个树形目录服务Zookeeper翻译过来就是动物园管理员&#xff0c;用来管理Hadoop&#xff08;大象&#xff09;、Hive&…

AR0132AT 1/3 英寸 CMOS 数字图像传感器可提供百万像素 HDR 图像处理(器件编号包含:AR0132AT6R、AR0132AT6C)

AR0132AT 1/3 英寸 CMOS 数字图像传感器&#xff0c;带 1280H x 960V 有效像素阵列。它能在线性或高动态模式下捕捉图像&#xff0c;且带有卷帘快门读取。它包含了多种复杂的摄像功能&#xff0c;如自动曝光控制、开窗&#xff0c;以及视频和单帧模式。它适用于低光度和高动态范…

QML界面控件加载与显示顺序

一、QML界面控件加载顺序 QML在界面加载时的顺序和我们认知的有很大的不同&#xff0c;有时候会对我们获取参数以及界面实现造成很大的困扰 1、加载顺序 import QtQuick 2.12 import QtQml 2.12 import QtQuick.Window 2.12 import QtQuick.VirtualKeyboard 2.4Window {id: …

Open3D点云算法与点云深度学习案例汇总(长期更新)

目录 引言 Open3D算法汇总 Open3D快速安装 测试点云资料 一、点云的读写与显示 二、KD tree和八叉树的应用 三、点云特征提取 四、点云滤波算法 五、点云配准算法 六、点云分割算法&#xff08;待更新&#xff09; 七、常用操作 八、数据转换 九、常用小工具 三维…