PyTorch微调终极指南1:预训练模型调整

如今,在训练深度学习模型时,通过根据自己的数据微调预训练模型来进行迁移学习(transfer learning)已成为首选方法。 通过微调这些模型,我们可以利用他们的专业知识并使它们适应我们的特定任务,从而节省宝贵的时间和计算资源。

本系列分为四篇文章,侧重于微调模型的不同方面,本文为第一篇,我们将深入研究定义预训练模型并配置它以适合你的目标任务。

定义模型包括一系列重要决策,包括选择适当的架构、定制模型头、配置损失函数和学习率、设置所需的浮点精度以及确定冻结或微调哪些层等等 更多的。 在本文中,我们将详细探讨每个方面,提供有价值的见解,帮助你有效定义和微调模型。

在线工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器

1、加载预训练模型

在加载预训练模型之前,清楚地了解你的具体问题并相应地选择合适的架构至关重要。 虽然这项任务似乎具有挑战性,但重要的是不要随机选择模型架构。 考虑你的业务需求并选择符合这些需求的合适架构。

DensNet 架构示例

例如,如果要对分类进行微调,并且优先考虑低延迟,那么像 MobileNet 这样的架构将是一个不错的选择。 通过做出明智的架构决策,你可以优化微调实验以获得更好的结果。

请注意,你可以从多个来源加载预训练模型以进行微调。 在本文中,我指的是 timm(Pytorch 图像模型)和 Torchvision 模型

以下是从 torchvision 加载预训练的 resnet50 模型的示例:

# From torchvision.models
from torchvision import models
model = models.resnet50(pretrained=False)

或者从 timm(Pytorch 图像模型)加载预训练模型:

import timm# from timm
pretrained_model_name = "resnet50"
model = timm.create_model(pretrained_model_name, pretrained=False)

需要注意的是,无论预训练模型的来源如何,所需的关键修改是调整模型的全连接 FC 层(或者可以是线性/分类器/头部)。 此外,对于你的目标任务,可以合并额外的线性层。 我们将在下一节中进一步探讨这一点。

2、修改模型头

修改模型的头部对于使其与你的特定目标任务保持一致至关重要。 预训练模型在大型数据集(如用于图像分类的 ImageNet)上进行训练,或在文本数据(如 BooksCorpus 和 Wikipedia)上进行训练以进行文本生成。 通过修改模型的头部,预训练的模型可以适应新任务并利用其学到的有价值的特征,从而增强其在新任务中的性能。

适用于目标任务的预训练模型的视觉描述,具有头部修改之外的附加层

例如,可以修改 RestNet head 进行分类任务:

import torch.nn as nn
import timmnum_classes = 4 # Replace num_classes with the number of classes in your data# Load pre-trained model from timm
model = timm.create_model('resnet50', pretrained=True)# Modify the model head for fine-tuning
num_features = model.fc.in_features
model.fc = nn.Linear(num_features, num_classes)

或者,在修改 RestNet 头以进行分类任务的同时,添加额外的线性层以增强模型的预测能力(ps - 这只是一个说明性示例):

import torch.nn as nn
import timmnum_classes = 4 # Replace num_classes with the number of classes in your data# Load pre-trained model from timm
model = timm.create_model('resnet50', pretrained=True)# Modify the model head for fine-tuning
num_features = model.fc.in_features# Additional linear layer and dropout layer
model.fc = nn.Sequential(nn.Linear(num_features, 256),  # Additional linear layer with 256 output featuresnn.ReLU(inplace=True),         # Activation function (you can choose other activation functions too)nn.Dropout(0.5),               # Dropout layer with 50% probabilitynn.Linear(256, num_classes)    # Final prediction fc layer
)

或者修改 RestNet 头来执行回归任务:

model = timm.create_model('resnet50', pretrained=True)# Modify the model head for regression
num_features = model.fc.in_features
model.fc = nn.Linear(num_features, 1) # Regression task has a single output

需要注意的一件事是,模型并不总是具有我们修改输出特征(例如 num_classes)的 FC(全连接)层。 模型的架构可能有所不同,我们需要修改的层的名称和位置也可能有所不同。

在许多预训练模型中,尤其是在卷积神经网络 (CNN) 架构中,模型末尾通常有一个线性层或 FC 层来执行最终分类。 然而,这不是严格的规则,并且某些模型可能具有不同的结构。

要识别需要修改的层,可以执行以下操作:

import torch
import timm# Load pre-trained model from timm
model = timm.create_model('resnet50', pretrained=True)
print(model)

打印的模型架构显示了需要修改的特定最后一层的标识

通过打印模型,可以看到其架构并确定要修改的适当层。 寻找用作最终分类层的线性或 FC 层,并将其替换为与类数量或任务要求相匹配的新层。

3、设置优化器、学习率、权重衰减和动量

在微调中,学习率、损失函数和优化器是相互关联的组件,它们共同影响模型适应新任务的能力,同时利用从预训练中获得的知识。 精心选择的学习率确保模型以合理的速度有效收敛,精心选择的损失函数使训练过程中的损失最小化与目标任务保持一致,适当的优化器有效地优化模型的参数。

微调需要对这些组件进行仔细的实验和迭代调整,以达到适当的平衡并在微调模型中达到所需的性能水平。

3.1 优化器

要更详细地了解如何选择合适的优化器进行微调,我建议参考这篇博客。

优化器根据反向传播期间计算的梯度确定用于更新模型参数的算法。 不同的优化器,例如 SGD、Adam 或 RMSprop,具有不同的参数更新规则和收敛特性。 优化器的选择可以显着影响模型训练和微调模型的最终性能。 选择最合适的优化器需要考虑任务的性质、数据集的大小和可用的计算资源等因素。

3.2 学习率、动量和权重衰减

在定义优化器时,我们还必须设置学习率(LR),它是一个超参数,用于确定优化过程中每次迭代的步长。 它控制模型参数在反向传播过程中根据计算出的梯度进行更新的程度。 选择合适的学习率至关重要,因为将其设置得太高可能会导致优化过程振荡或发散或超出最佳解决方案,而将其设置太低可能会导致收敛缓慢或陷入局部极小值。

除了学习率之外,定义优化器时还需要考虑其他重要的超参数,例如权重衰减和动量(特定于 SGD)。 让我们快速浏览一下这两个超参数:

  • 权重衰减,也称为 L2 正则化,是一种用于防止过度拟合并鼓励模型学习更简单、更通用的表示的技术。
  • 动量用于随机梯度下降 (SGD),以加速收敛并逃离局部最小值。
import torch.optim as optim# Define your optimizer with weight decay
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=0.001)# Define your optimizer with weight decay
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=0.0001)

3.3 选择损失函数

要探索并选择适合你的目标任务的损失函数,我建议参考有关损失函数的 PyTorch 官方文档。

损失函数衡量模型的预测输出与实际正确答案之间的差异或差距。 它为我们提供了一种了解模型在任务上执行情况的方法。 在微调预训练模型时,选择适合我们正在处理的特定任务的损失函数非常重要。 例如,对于分类任务,常用交叉熵损失,而均方误差更适合回归问题。 选择正确的损失函数可确保模型在训练期间专注于优化所需目标。

import torch.nn as nn# Define the loss function - For classification problem
loss_function = nn.CrossEntropyLoss()# Define the loss function - For regression problem
loss_function = nn.MSELoss()  # Mean Squared Error loss

另请注意,关于损失函数的选择和处理,可以应用一些额外的考虑因素和技术。 其中一些例子是:

  • 自定义损失函数 - 你可能需要修改或自定义损失函数以满足特定要求。 一个例子是对重要的单个类别的错误分类进行 10 倍的惩罚。 下面是一个示例代码,演示了自定义损失的实现,让你了解如何完成它:
import torch
import torch.nn.functional as Fclass CustomLoss(torch.nn.Module):def __init__(self, class_weights):super(CustomLoss, self).__init__()self.class_weights = class_weightsdef forward(self, inputs, targets):ce_loss = F.cross_entropy(inputs, targets, reduction='none')weights = torch.ones_like(targets).float()for class_idx, weight in enumerate(self.class_weights):weights[targets == class_idx] = weightweighted_loss = ce_loss * weightsreturn torch.mean(weighted_loss)# Assuming you have a model and training data
model = YourModel()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# Assuming 5 classes so class_weights = [1.0, 1.0, 1.0, 1.0, 10.0]
criterion = CustomLoss(class_weights=[1.0, 1.0, 1.0, 1.0, 10.0])  # Inside the training loop
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
  • 基于指标的损失 - 在某些情况下,模型的性能可能会根据损失本身以外的指标进行评估。 在这种情况下,你可以设计或调整损失函数以直接优化这些指标。
  • 正则化 - 在微调过程中可以将正则化方法(例如 L1 或 L2 正则化)纳入损失函数中,以防止过度拟合并提高模型泛化能力。 正则化项可以帮助控制模型的复杂性,并降低过度强调数据中特定模式或特征的风险。 L2 正则化可以通过在优化器中设置weight_decay 值来应用,而L1 正则化需要稍微不同的方法。

L2正则化实现:

# Define a loss function
criterion = nn.CrossEntropyLoss()# L2 regularization 
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0.01)

L1正则化实现:

# Define a loss function
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)# Inside the training loop
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, targets)# L1 regularization
regularization_loss = 0.0
for param in model.parameters():regularization_loss += torch.norm(param, 1)
loss += 0.01 * regularization_loss#Adjust regularization strength as needed

4、冻结全部或部分网络

当我们指的是冻结时,这意味着在微调过程中固定特定层或整个网络的权重。 网络冻结允许我们保留预训练模型捕获的知识,同时仅更新某些层以适应目标任务。 因此,这是非常重要的,如果你正在微调预训练模型,则不应忽视这一点。

在微调之前决定是否应该冻结预训练模型的所有层(完整网络)或部分层,这一切都归结为你的特定目标任务。

例如,如果预训练模型已经在与目标任务类似的大规模数据集上进行了训练,那么冻结整个网络可以帮助保留学习到的表示,防止它们被覆盖。 在这种情况下,仅修改模型的头部并从头开始训练。

另一方面,当预训练模型的较低层捕获可能与新任务相关的一般特征时,仅冻结网络的一部分可能会很有用。 通过冻结这些较低层,我们可以利用预训练模型的知识,同时更新较高层以专门研究特定于任务的功能。 这种方法在目标数据集很小或与训练预训练模型的数据集显着不同的情况下特别有用。

要在 PyTorch 中实现冻结,你可以访问模型中的各个层或模块,并将其 require_grad 属性设置为 False。 这可以防止在向后传递过程中计算梯度和更新权重。

下面是一个示例代码,演示了冻结整个网络的实现:

# Freeze all the layers of the pre-trained model
for param in model.parameters():param.requires_grad = False# Modify the model's head for a new task
num_classes = 10
model.fc = nn.Linear(model.fc.in_features, num_classes)

仅冻结网络中的卷积层:

# Freeze only the convolutional layers of the pre-trained model
for param in model.parameters():if isinstance(param, nn.Conv2d):param.requires_grad = False# Modify the model's head for a new task
num_classes = 10
model.fc = nn.Linear(model.fc.in_features, num_classes)

仅冻结网络中的特定层:

# Freeze specific layers (e.g.,the first two convolutional layers) of the pre-trained model
for name, param in model.named_parameters():if 'conv1' in name or 'layer1' in name:param.requires_grad = False# Modify the model's head for a new task
num_classes = 10
model.fc = nn.Linear(model.fc.in_features, num_classes)

需要注意的是,冻结图层应该经过深思熟虑,考虑到任务和数据集的具体要求。 这是利用预先训练的知识和让模型有效适应新任务之间的微妙平衡。

5、定义模型浮点精度

快速总结定义模型浮点精度是指深度学习模型计算时用来表示数值的数据类型。 在 PyTroch 中,32 位(float32 或 FP32)和 16 位(float16 或 FP16 或半精度)是两种常用的浮点精度。

  • float32 — 这种精度提供了宽动态范围和高数值精度,允许精确计算,但会消耗更多内存。 FP32 使用 32 位来表示数字。
  • float16 — 这种较低的精度可以减少模型的内存占用和计算要求,从而潜在提高效率和速度。 然而,它可能会导致数值精度损失,并可能影响模型的准确性或收敛性。 FP16 使用 16 位来表示一个数字。
  • FP16 和 FP32 被称为单精度,两者都有自己的优点和缺点,正如我们在上面的指针中看到的那样。 为了利用两者的优势,我们在训练管道中结合了 FP16 和 FP32 浮点精度的混合精度。 混合精度可提高计算效率、减少内存占用、加速训练并增加模型容量。

对于混合精度训练更详细的理解,我建议参考这篇文章。

以下是如何使用自动混合精度 (AMP) 库在 PyTorch 训练管道中实现混合精度训练的示例:

import torch
from torch import nn, optim
from torch.cuda.amp import autocast, GradScaler# Define your model and optimizer
model = YourModel()
optimizer = optim.Adam(model.parameters(), lr=0.001)# Define loss function
criterion = nn.CrossEntropyLoss()# Define scaler for automatic scaling of gradients
scaler = GradScaler()# Define your training loop
for epoch in range(num_epochs):for batch_idx, (data, targets) in enumerate(train_loader):data, targets = data.to(device), targets.to(device)# Zero the gradientsoptimizer.zero_grad()# Enable autocasting for mixed precisionwith autocast():# Forward passoutputs = model(data)loss = criterion(outputs, targets)# Perform backward pass and gradient scalingscaler.scale(loss).backward()# Update model parametersscaler.step(optimizer)scaler.update()# Print training progressif batch_idx % log_interval == 0:print(f"Epoch {epoch+1}/{num_epochs} | Batch {batch_idx}/{len(train_loader)} | Loss: {loss.item():.4f}")

在上面的示例中, GradScaler() 对象用于执行梯度缩放。 以下是所使用方法的细分:

  • scaler.scale(loss):此方法通过缩放器确定的适当因子来缩放损失值。 它返回将用于反向传播的缩放损失。
  • scaler.step(optimizer):此方法使用反向传播过程中计算的梯度来更新优化器的参数。 它像往常一样执行优化器步骤,但考虑了缩放器执行的梯度缩放。
  • scaler.update():该方法调整缩放器在下一次迭代中使用的缩放因子。 它通过根据梯度大小动态调整比例来帮助防止下溢或溢出问题。 它在 scaler.step(optimizer)之后调用。

使用 GradScaler() 和相关方法的目的是减轻使用较低精度 (FP16) 计算时可能出现的潜在数值不稳定问题。 通过适当缩放损失和梯度,缩放器可确保优化器的更新保持在稳定范围内。

使用 PyTorch 的 AMP 库实现混合精度训练可以有效利用 FP16 计算,从而提高训练速度并减少内存使用量,同时保持使用 FP32 进行准确权重更新所需的精度。

虽然混合精确训练可以带来多种好处,但在某些情况下它可能不适合或可能损害训练过程。

使用混合精确训练的潜在危害包括:

  • 由于 FP16 而导致数值精度损失,这种精度损失可能会导致模型精度降低,特别是在需要高精度的任务中。 当涉及数值精度关键任务时,混合精度可能不适合。
  • 由于训练期间的数值不稳定,下溢和溢出的脆弱性增加,这可能会影响模型的收敛和性能。
  • 由于混合精度而增加了复杂性,这需要额外的考虑,例如管理精度转换、缩放梯度以及处理与精度不匹配相关的可能问题。
  • 如果你的模型遇到严重的梯度爆炸或消失问题,在混合精度训练中切换到较低精度计算(FP16)可能会使这些问题恶化。 在这种情况下,在考虑混合精度训练之前解决潜在的不稳定问题至关重要。

6、训练模型和验证模式

微调模型时,加载预训练模型后默认处于训练模式。 但是,我们可以在推理或验证期间将模型切换到验证模式。 这些模式变化会相应地改变模型的行为。

6.1 训练模式

当模型处于训练模式时,它会启用训练过程中所需的特定操作,例如计算梯度、更新参数以及应用 dropout 等正则化技术。 在此模式下,模型的行为就像正在训练数据集上进行训练一样,并准备好从数据中学习。

model.train() # sets model in training mode

6.2 验证模式

当模型处于评估模式时,它会禁用某些仅在训练期间所需的操作,例如计算梯度、dropout 和更新参数。 当你想要评估模型在未见过的数据上的性能时,通常在验证或测试期间使用此模式。

model.val() # sets model in validation mode

在微调过程中将模型设置为正确的模式非常重要,因为将模型设置为正确的模式可确保每个阶段(训练或评估)的一致行为和正确操作。 这会带来准确的结果、高效的资源利用,并防止过度拟合或标准化不一致等问题。

7、单 GPU 和多 GPU

GPU 对于深度学习和微调任务至关重要,因为它们擅长执行高度并行计算,从而显着加快训练过程。 如果可以使用多个 GPU,可以利用它们的集体力量来进一步加速训练。

以下是如何利用多个 GPU(如果有可用)的示例:

# Define your model
model = MyModel()
model = model.to(device)  # Move the model to the desired device (CPU or GPU)# Check if multiple GPUs are available
if torch.cuda.device_count() > 1:print("Using", torch.cuda.device_count(), "GPUs for training.")model = nn.DataParallel(model)  # Wrap the model with DataParallel# Define your loss function and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

此代码片段首先使用 torch.cuda.device_count() 检查多个 GPU 是否可用。 如果有多个 GPU,则模型将使用 nn.DataParallel 进行包装,从而允许其利用所有可用的 GPU 进行训练。 每个 GPU 同时处理一部分数据,从而缩短训练时间。 如果仅存在一个 GPU,则代码将在该 GPU 上运行,而不使用 nn.DataParallel

8、结束语

在 PyTorch 微调终极指南的第一部分中,我们探索了微调预训练模型以适应我们的特定任务所涉及的基本步骤。 通过利用迁移学习,我们可以节省大量时间和计算资源,同时取得令人印象深刻的结果。 在本文中,我们学习了如何加载预训练模型,调整其头部架构以匹配目标任务,以及自定义学习率、优化器和权重衰减等超参数以优化微调过程。 此外,我们还研究了选择适当的损失函数以及冻结网络特定部分以进行更受控的微调的好处。

此外,我们还讨论了定义模型浮点精度的重要性以及微调期间在训练和验证模式之间切换的重要性。 此外,我们还探讨了如何充分利用单 GPU 和多 GPU 设置来加速训练并提高性能。

在本系列的第 2 部分中,我们将更深入地研究先进技术,以提高微调模型的准确性和泛化能力。 我们将探索数据增强、学习率计划、梯度裁剪和集成等方法,以进一步提高模型在多样化和具有挑战性的数据集上的性能。


原文链接:PyTorch微调终极指南(1) - BimAnt

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

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

相关文章

sqlserver 删除master数据库特定前缀开头的所有表的sql语句

sqlserver数据库删除指定数据库特定前缀开头的所有表的sql语句sqlserver删除数据库指定字符开头的所有表的sql语句 USE master;DECLARE TableName NVARCHAR(128); DECLARE SQL NVARCHAR(MAX);DECLARE TableCursor CURSOR FOR SELECT name FROM sys.tables WHERE name LIKE Whi…

【miniQMT实盘量化4】获取实时行情数据

前言 上篇,我们介绍了如何获取历史数据,有了历史数据,我们可以进行分析和回测。但,下一步,我们更需要的是实时数据,只有能有效的监控实时行情数据,才能让我们变成市场上的“千里眼,…

go defer用法_类似与python_java_finially

defer 执行 时间 defer 一般 定义在 函数 开头, 但是 他会 最后 被执行 A defer statement defers the execution of a function until the surrounding function returns. 如果说 为什么 不在 末尾 定义 defer 呢, 因为 当 错误 发生时, 程序 执行 不到 末尾 就会 崩溃. d…

从0开始学习JavaScript--深入探究JavaScript类型化数组

JavaScript类型化数组是一种特殊的数组类型,引入了对二进制数据的更底层的操作。这种数组提供了对内存中的二进制数据直接进行读写的能力,为处理图形、音频、视频等大规模数据提供了高效的手段。本文将深入探讨JavaScript类型化数组的基本概念、常见类型…

场景交互与场景漫游-对象选取(8-2)

对象选取示例的代码如程序清单8-11所示: /******************************************* 对象选取示例 *************************************/ // 对象选取事件处理器 class PickHandler :public osgGA::GUIEventHandler { public:PickHandler() :_mx(0.0f), _my…

48. 旋转图像

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&…

用平板当电脑副屏(spacedesk)双端分享

文章目录 1.准备工作2.操作流程1. 打开spacedesk点击2. 勾选USB Cable Android3. 用数据线连接移动端和pc端,选择仅充电4. 打开安装好的spacedesk 记得在win系统中设置扩展显示器: 1.准备工作 下载软件spacedesk Driver Console pc端: 移动…

macos苹果电脑清理软件有哪些?cleanmymac和腾讯柠檬哪个好

MacOS是一款优秀的操作系统,但是随着使用时间的增加,它也会产生一些不必要的垃圾文件,占用磁盘空间和内存资源,影响系统的性能和稳定性。为了保持MacOS的清洁和高效,我们需要使用一些专业的清理软件来定期扫描和清除这…

Pandas数据集的合并与连接merge()方法_Python数据分析与可视化

数据集的合并与连接 merge()解析merge()的主要参数 merge()解析 merge()可根据一个或者多个键将不同的DataFrame连接在一起,类似于SQL数据库中的合并操作。 数据连接的类型 一对一的连接: df1 pd.DataFrame({employee: [Bob, Jake, Lisa, Sue], grou…

华为OD机试 - 机器人搬砖(Java JS Python C)

目录 题目描述 输入描述 输出描述 用例 题目解析 JavaScript算法源码 Java算法源码

【Linux】:体系结构与进程概念

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关Linux体系结构和进程的知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入…

腾讯云轻量数据库1核1G性能测评、租用费用和详细介绍

腾讯云轻量数据库服务采用腾讯云自研的新一代云原生数据库 TDSQL-C,融合了传统数据库、云计算与新硬件技术的优势,100%兼容 MySQL,实现超百万级 QPS 的高吞吐,128TB 海量分布式智能存储,保障数据安全可靠。腾讯云百科t…

机器人制作开源方案 | 智能照科植物花架

作者:付菲菲、于海鑫、王子敏单位:黑河学院指导老师:索向峰、李岩 1. 概述 1.1设计背景​ 随着时代的发展,城市化脚步加快、城市人口密度越来越大、城市生活节奏快压力大作息难成规律。城市建筑建筑面积迅速增加、而绿…

力扣第841题 钥匙和房间 C++ DFS BFS 附Java代码

题目 841. 钥匙和房间 中等 相关标签 深度优先搜索 广度优先搜索 图 有 n 个房间,房间按从 0 到 n - 1 编号。最初,除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而,你不能在没有获得钥匙的时候进入锁住的房间…

【MISRA C 2012】Rule 4.2 不应该使用三连符

1. 规则1.1 原文1.2 分类 2. 关键描述3. 代码实例 1. 规则 1.1 原文 Rule 4.2 Trigraphs should not be used Category Advisory Analysis Decidable, Single Translation Unit Applies to C90, C99 1.2 分类 规则4.2:不应该使用三连符 Advisory建议类规范。 2…

Leetcode—5.最长回文子串【中等】

2023每日刷题(三十五) Leetcode—5.最长回文子串 中心扩展法算法思想 可以使用一种叫作“中心扩展法”的算法。由回文的性质可以知道,回文一定有一个中心点,从中心点向左和向右所形成的字符序列是一样的,并且如果字符…

Rust宏详解之类函数宏

文章目录 过程宏类函数宏调用与测试 Rust基础教程:初步⚙所有权⚙结构体和枚举类⚙函数进阶⚙泛型和特征⚙并发和线程通信⚙cargo包管理 Rust进阶教程:用宏实现参数可变的函数 过程宏 在Rust中,macro_rules!是最常用的宏,也叫声…

11.1 文件拷贝移动与删除

在编程中,针对磁盘与目录的操作也是非常重要的,本章将重点介绍如何实现针对文件目录与磁盘的操作方法,其中包括了删除文件,文件拷贝,文件读写,目录遍历输出,遍历磁盘容量信息,磁盘格…

Vue移动 HTML 元素到指定位置 teleport 标签

teleport 标签&#xff1a;用于将组件中的 HTML 元素移动到任意的位置。 使用 teleport 标签移动 HTML 元素&#xff1a; <!-- 将 teleport 中的内容移动到 body 标签中 --> <teleport to"body"><div><h3>我是第三层组件的标题</h3>…

如何使用http来获取thingsbord中的设备数据

背景 有个读者问我,他想做tb的二次开发,想要通过一个接口来查询设备的遥测数据。 于是我给他写了这篇文章。 具体实现 由于他使用的是cloud版本,于是我使用cloud来做演示 文档的接口 https://thingsboard.cloud/swagger-ui/#/telemetry-controller/getTimeseriesUsing…