当前位置: 首页 > news >正文

YOLOv8 基于MGD的知识蒸馏

YOLOv8 基于MGD的知识蒸馏

接着上一篇我们介绍了YOLOv8的剪枝方案和代码,本篇文章将剪枝后的模型作为学生模型,剪枝前的模型作为教师模型对剪枝模型进行蒸馏,从而进一步提到轻量模型的性能。

Channel-wise Distillation (CWD)

在这里插入图片描述

问题和方法

在计算机视觉任务中,图像分类只需要预测整张图像的类别,而密集预测需要对每个像素或对象进行预测,输出更丰富的结果,如语义分割、目标检测等。直接应用分类任务中的知识蒸馏方法于密集预测任务效果不佳。已有的方法通过建模空间位置之间(指的是图像中的像素位置)的关系来传递结构化知识。

论文提出了一种通道级的知识蒸馏方法。主要分为两个步骤:

  • 对特征图的每个通道进行softmax标准化,得到一个概率分布(表示了该通道中每个位置的相对重要性或响应强度)。
  • 计算教师网络和学生网络相应通道概率分布之间的asymmetric KL散度作为损失,使学生网络在前景显著区域模仿教师网络。

具体实现

对特征图或logits的每个通道,对H×W个位置的激活值进行softmax计算,得到概率分布表示每个位置的相对重要性。
然后计算这个分布与教师网络中相应通道分布的asymmetric KL距离,重点对齐前景显著区域。

代码如下:

class CWDLoss(nn.Module):"""PyTorch version of `Channel-wise Distillation for Semantic Segmentation.<https://arxiv.org/abs/2011.13256>`_."""def __init__(self, channels_s, channels_t, tau=1.0):super(CWDLoss, self).__init__()self.tau = taudef forward(self, y_s, y_t):"""Forward computation.Args:y_s (list): The student model prediction withshape (N, C, H, W) in list.y_t (list): The teacher model prediction withshape (N, C, H, W) in list.Return:torch.Tensor: The calculated loss value of all stages."""assert len(y_s) == len(y_t)losses = []for idx, (s, t) in enumerate(zip(y_s, y_t)):assert s.shape == t.shapeN, C, H, W = s.shape# normalize in channel diemensionimport torch.nn.functional as Fsoftmax_pred_T = F.softmax(t.view(-1, W * H) / self.tau, dim=1)  # [N*C, H*W]logsoftmax = torch.nn.LogSoftmax(dim=1)cost = torch.sum(softmax_pred_T * logsoftmax(t.view(-1, W * H) / self.tau) -softmax_pred_T * logsoftmax(s.view(-1, W * H) / self.tau)) * (self.tau ** 2)losses.append(cost / (C * N))loss = sum(losses)return loss

Masked Generative Distillation (MGD)

图片

问题和方法

知识蒸馏主要可以分为logit蒸馏和feature蒸馏。其中feature蒸馏具有更好的拓展性,已经在很多视觉任务中得到了应用。但由于不同任务的模型结构差异,许多feature蒸馏方法是针对某个特定任务设计的。

之前的知识蒸馏方法着力于使学生去模仿更强的教师的特征,以使学生特征具有更强的表征能力。我们认为提升学生的表征能力并不一定需要通过直接模仿教师实现。从这点出发,我们把模仿任务修改成了生成任务:让学生凭借自己较弱的特征去生成教师较强的特征。在蒸馏过程中,我们对学生特征进行了随机mask,强制学生仅用自己的部分特征去生成教师的所有特征,以提升学生的表征能力。

具体实现

对特征图或logits生成1×H×W的随机mask,广播到所有通道然后对特征图所有通道进行掩码操作,基于masked特征图输入生成网络,输出特征与教师特征图计算mse损失进行回归训练。

代码如下:

class MGDLoss(nn.Module):def __init__(self, channels_s, channels_t, alpha_mgd=0.00002, lambda_mgd=0.65):super(MGDLoss, self).__init__()device = 'cuda' if torch.cuda.is_available() else 'cpu'self.alpha_mgd = alpha_mgdself.lambda_mgd = lambda_mgdself.generation = [nn.Sequential(nn.Conv2d(channel_s, channel, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(channel, channel, kernel_size=3, padding=1)).to(device) for channel_s, channel inzip(channels_s, channels_t)]def forward(self, y_s, y_t, layer=None):"""Forward computation.Args:y_s (list): The student model prediction withshape (N, C, H, W) in list.y_t (list): The teacher model prediction withshape (N, C, H, W) in list.Return:torch.Tensor: The calculated loss value of all stages."""assert len(y_s) == len(y_t)losses = []for idx, (s, t) in enumerate(zip(y_s, y_t)):# print(s.shape)# print(t.shape)# assert s.shape == t.shapeif layer == "outlayer":idx = -1losses.append(self.get_dis_loss(s, t, idx) * self.alpha_mgd)loss = sum(losses)return lossdef get_dis_loss(self, preds_S, preds_T, idx):loss_mse = nn.MSELoss(reduction='sum')N, C, H, W = preds_T.shapedevice = preds_S.devicemat = torch.rand((N, 1, H, W)).to(device)mat = torch.where(mat > 1 - self.lambda_mgd, 0, 1).to(device)masked_fea = torch.mul(preds_S, mat)new_fea = self.generation[idx](masked_fea)dis_loss = loss_mse(new_fea, preds_T) / Nreturn dis_loss

YOLOv8蒸馏

基于前一章所述的剪枝模型作为学生模型,剪枝前的模型作为教师模型

model_s = YOLO(weights="weights/prune.pt")
model_t = YOLO(weights="weights/last.pt")

为了在训练过程中使用教师模型指导学生模型训练,我们首先修改接口,在train函数中传入教师模型和蒸馏损失类型。

self.yolo.train(data="diagram.yaml", Distillation=model_t.model, loss_type=loss_type, amp=False, imgsz=640,epochs=100, batch=20, device=0, workers=4, lr0=0.001)

同时修改ultralytics/engine/trainer.py-333行,读取Distillation参数和loss_type参数。

Args:cfg (str, optional): Path to a configuration file. Defaults to DEFAULT_CFG.overrides (dict, optional): Configuration overrides. Defaults to None.
# 新增=======================================
if overrides and "Distillation" in overrides:self.Distillation = overrides["Distillation"]overrides.pop("Distillation")
else:self.Distillation = None
if overrides and "loss_type" in overrides:self.loss_type = overrides['loss_type']overrides.pop("loss_type")
else:self.loss_type = 'None'
# 新增=======================================
self.args = get_cfg(cfg, overrides)

修改了接口处之后,在加载当前学生模型的时候,同时对教师模型进行处理。trainer.py修改481行

def _setup_train(self, world_size):"""Builds dataloaders and optimizer on correct rank process."""# Modelself.run_callbacks("on_pretrain_routine_start")ckpt = self.setup_model()self.model = self.model.to(self.device)# 新增=======================================if self.Distillation is not None:#    for k, v in self.Distillation.model.named_parameters():#        v.requires_grad = Trueself.Distillation = self.Distillation.to(self.device)# 新增=======================================self.set_model_attributes()......

这里新增的注释部分是打开教师模型的梯度计算,但是一般我们不需要,然后将教师模型也移动到device上。

    self.amp = bool(self.amp)  # as booleanself.scaler = torch.cuda.amp.GradScaler(enabled=self.amp)if world_size > 1:self.model = nn.parallel.DistributedDataParallel(self.model, device_ids=[RANK], find_unused_parameters=True)# 新增=======================================if self.Distillation is not None:self.Distillation = nn.parallel.DistributedDataParallel(self.Distillation, device_ids=[RANK])self.Distillation.eval()# 新增=======================================# Check imgsz

然后在_setup_train函数的521行进行分布式训练模型处理的时候,将教师模型做同样的处理。

然后是增加蒸馏损失,这一块我们可以添加到_do_train函数中。

if self.args.close_mosaic:base_idx = (self.epochs - self.args.close_mosaic) * nbself.plot_idx.extend([base_idx, base_idx + 1, base_idx + 2])
# 新增=======================================
if self.Distillation is not None:distillation_loss = Distillation_loss(self.model, self.Distillation, distiller=self.loss_type)
epoch = self.start_epoch
self.optimizer.zero_grad()  # zero any resumed gradients to ensure stability on train start
while True:self.epoch = epochself.run_callbacks("on_train_epoch_start")

这里Distillation_loss传入学生模型和教师模型,以及蒸馏损失的类型,该类实现如下:

class Distillation_loss:def __init__(self, modeln, modelL, distiller="CWDLoss"):  # model must be de-paralleledself.distiller = distiller# layers = ["2","4","6","8","12","15","18","21"]layers = ["6", "8", "12", "15", "18", "21"]# layers = ["15","18","21"]# get channels_s, channels_t from modelL and modelnchannels_s = []channels_t = []for name, ml in modelL.named_modules():if name is not None:name = name.split(".")if name[0] == "module":name.pop(0)if len(name) == 3:if name[1] in layers:if "cv2" in name[2]:channels_t.append(ml.conv.out_channels)for name, ml in modeln.named_modules():if name is not None:name = name.split(".")if name[0] == "module":name.pop(0)if len(name) == 3:if name[1] in layers:if "cv2" in name[2]:channels_s.append(ml.conv.out_channels)nl = len(layers)channels_s = channels_s[-nl:]channels_t = channels_t[-nl:]self.D_loss_fn = FeatureLoss(channels_s=channels_s, channels_t=channels_t, distiller=distiller[:3])self.teacher_module_pairs = []self.student_module_pairs = []self.remove_handle = []for mname, ml in modelL.named_modules():if mname is not None:name = mname.split(".")if name[0] == "module":name.pop(0)if len(name) == 3:if name[1] in layers:if "cv2" in mname:self.teacher_module_pairs.append(ml)for mname, ml in modeln.named_modules():if mname is not None:name = mname.split(".")if name[0] == "module":name.pop(0)if len(name) == 3:# print(mname)if name[1] in layers:if "cv2" in mname:self.student_module_pairs.append(ml)def register_hook(self):self.teacher_outputs = []self.origin_outputs = []def make_layer_forward_hook(l):def forward_hook(m, input, output):l.append(output)return forward_hookfor ml, ori in zip(self.teacher_module_pairs, self.student_module_pairs):# 为每层加入钩子,在进行Forward的时候会自动将每层的特征传送给model_outputs和origin_outputsself.remove_handle.append(ml.register_forward_hook(make_layer_forward_hook(self.teacher_outputs)))self.remove_handle.append(ori.register_forward_hook(make_layer_forward_hook(self.origin_outputs)))def get_loss(self):quant_loss = 0# for index, (mo, fo) in enumerate(zip(self.teacher_outputs, self.origin_outputs)):#     print(mo.shape,fo.shape)# quant_loss += self.D_loss_fn(mo, fo)quant_loss += self.D_loss_fn(y_t=self.teacher_outputs, y_s=self.origin_outputs)if self.distiller != 'cwd':quant_loss *= 0.3self.teacher_outputs.clear()self.origin_outputs.clear()return quant_lossdef remove_handle_(self):for rm in self.remove_handle:rm.remove()

这个类里面指定了一些要进行蒸馏的层,然后定义了一个注册每一层的钩子的函数,这样每一层前向传播完会得到所有层的特征,这些特征传入FeatureLoss类,进行特征损失计算。FeatureLoss类如下:

class FeatureLoss(nn.Module):def __init__(self, channels_s, channels_t, distiller='mgd', loss_weight=1.0):super(FeatureLoss, self).__init__()self.loss_weight = loss_weightself.distiller = distillerdevice = 'cuda' if torch.cuda.is_available() else 'cpu'self.align_module = nn.ModuleList([nn.Conv2d(channel, tea_channel, kernel_size=1, stride=1, padding=0).to(device)for channel, tea_channel in zip(channels_s, channels_t)])self.norm = [nn.BatchNorm2d(tea_channel, affine=False).to(device)for tea_channel in channels_t]self.norm1 = [nn.BatchNorm2d(set_channel, affine=False).to(device)for set_channel in channels_s]if distiller == 'mgd':self.feature_loss = MGDLoss(channels_s, channels_t)elif distiller == 'cwd':self.feature_loss = CWDLoss(channels_s, channels_t)else:raise NotImplementedErrordef forward(self, y_s, y_t):assert len(y_s) == len(y_t)tea_feats = []stu_feats = []for idx, (s, t) in enumerate(zip(y_s, y_t)):if self.distiller == 'cwd':s = self.align_module[idx](s)s = self.norm[idx](s)else:s = self.norm1[idx](s)t = self.norm[idx](t)tea_feats.append(t)stu_feats.append(s)loss = self.feature_loss(stu_feats, tea_feats)return self.loss_weight * loss

上面DistillationLoss和FeatureLoss两个类呢我们单独放到trainer.py文件开头。

回到_do_train函数,在前面声明了distillation_loss实例之后,首先我们为教师模型和学生模型注册钩子函数,这个必须在模型调用之前,因此放在了for循环训练之前。

self.tloss = None
# 新增=======================================
if self.Distillation is not None:distillation_loss.register_hook()
# 新增=======================================
for i, batch in pbar:self.run_callbacks("on_train_batch_start")# Warmup

然后就是模型计算损失的部分,如下:

self.tloss = ((self.tloss * i + self.loss_items) / (i + 1) if self.tloss is not None else self.loss_items
)
# 新增=======================================
if self.Distillation is not None:distill_weight = ((1 - math.cos(i * math.pi / len(self.train_loader))) / 2) * (0.1 - 1) + 1with torch.no_grad():pred = self.Distillation(batch['img'])self.d_loss = distillation_loss.get_loss()self.d_loss *= distill_weightif i == 0:print(self.d_loss, '-----------------')print(self.loss, '-----------------')self.loss += self.d_loss
# 新增=======================================

这里呢,设置了蒸馏损失的权重,大致是下面的曲线。然后把蒸馏损失加到原损失上即可。注意,在教师模型推理的时候,用了with torch.no_grad()包装,因为不需要训练教师模型,也就不计算梯度,这样做可以减少显存消耗。

image-20241005105504596

最后,模型train完一轮,需要把钩子函数给去掉,如下:

        if self.args.plots and ni in self.plot_idx:self.plot_training_samples(batch, ni)self.run_callbacks("on_train_batch_end")
# 新增=======================================
if self.Distillation is not None:distillation_loss.remove_handle_()
self.lr = {f"lr/pg{ir}": x["lr"] for ir, x in enumerate(self.optimizer.param_groups)}  # for loggers
self.run_callbacks("on_train_epoch_end")

至此,所有要修改的地方都改完了。此时,使用如下语句训练即可

self.yolo.train(data="diagram.yaml", Distillation=model_t.model, loss_type=loss_type, amp=False, imgsz=640,epochs=100, batch=20, device=0, workers=4, lr0=0.001)

为了代码简洁方便,对稀疏训练、剪枝和蒸馏做了封装,形成如下类:

import os
from tqdm import tqdm
from prune import prune_model
from relation import find_parent_nodes, visualize_nodes, metric
from ultralytics import YOLOclass PruneModel:def __init__(self, weights="weights/last.pt"):# Load a modelself.yolo = YOLO(weights)def prune(self, factor=0.7, save_dir="weights/prune.pt"):prune_model(self.yolo, save_dir, factor)def train(self, save_dir="weights/retrain.pt"):self.yolo.train(data='diagram.yaml', Distillation=None, loss_type='None', amp=False, imgsz=640,epochs=50, batch=20, device=1, workers=4, name="default")self.yolo.save(save_dir)def sparse_train(self, save_dir='weight/sparse.pt'):self.yolo.train(data='diagram.yaml', Distillation=None, loss_type='sparse', amp=False, imgsz=640,epochs=50, batch=20, device=0, workers=4, name="sparse")self.yolo.save(save_dir)def distill(self, t_weight, loss_type='mgd', save_dir="weights/distill.pt"):model_t = YOLO(t_weight)self.yolo.train(data="diagram.yaml", Distillation=model_t.model, loss_type=loss_type, amp=False, imgsz=640,epochs=100, batch=20, device=0, workers=4, lr0=0.001)self.yolo.save(save_dir)def export(self, **kwargs):self.yolo.export(**kwargs)@staticmethoddef compare(weights=None):# 统计压缩前后的参数量,精度,计算量if weights is None:weights = []results = []for weight in weights:yolo = YOLO(weight)metric = yolo.val(data='diagram.yaml', imgsz=640)n_l, n_p, n_g, flops = yolo.info()acc = metric.box.mapresults.append((weight, n_l, n_p, n_g, flops, acc))for weight, layer, n_p, n_g, flops, acc in results:print(f"Weight: {weight}, Acc: {acc}, Params: {n_p}, FLOPs: {flops}")def predict(self, source):results = self.yolo.predict(source)[0]nodes = results.boxes.xyxynodes = nodes.tolist()ori_img = results.orig_imgparent_nodes = find_parent_nodes(nodes)visualize_nodes(ori_img, nodes, parent_nodes)def evaluate(self, data_path):bboxes_list = []pred_bboxes_list = []parent_ids_list = []pred_parent_ids_list = []imgs_path = os.path.join(data_path, "images/val")labels_path = os.path.join(data_path, "plabels/val")# 读取标注文件for img in tqdm(os.listdir(imgs_path)):img_path = os.path.join(imgs_path, img)# 检查文件后缀并构建相应的标注文件路径if img.endswith(".png"):label_path = os.path.join(labels_path, img.replace(".png", ".txt"))elif img.endswith(".webp"):label_path = os.path.join(labels_path, img.replace(".webp", ".txt"))else:continuewith open(label_path, "r") as f:lines = f.readlines()results = self.yolo.predict(img_path)[0]pred_bboxes = results.boxes.xyxypred_bboxes = pred_bboxes.tolist()pred_bboxes_list.append(pred_bboxes)pred_parent_ids = find_parent_nodes(pred_bboxes)pred_parent_ids_list.append(pred_parent_ids)ih, iw = results.orig_img.shape[:2]bboxes = []parent_ids = []for line in lines:line = line.strip().split()x, y, w, h, px, py, pw, ph, p = map(float, line[1:])x1, y1, x2, y2 = int((x - w / 2) * iw), int((y - h / 2) * ih), int((x + w / 2) * iw), int((y + h / 2) * ih)bboxes.append((x1, y1, x2, y2))parent_ids.append(int(p))bboxes_list.append(bboxes)parent_ids_list.append(parent_ids)precision, recall, f1_score = metric(bboxes_list, pred_bboxes_list, parent_ids_list, pred_parent_ids_list)print(f"Precision: {precision}")print(f"Recall: {recall}")print(f"F1 Score: {f1_score}")if __name__ == '__main__':model = PruneModel("weights/yolov8n.pt")model.sparse_train("weights/sparse.pt")model.prune(factor=0.2, save_dir="weights/prune.pt")model.train()model.distill("weights/sparse.pt", loss_type="mgd")model.evaluate("datasets/diagram")model.predict("datasets/diagram/images/val/0593.png")

相关文章:

YOLOv8 基于MGD的知识蒸馏

YOLOv8 基于MGD的知识蒸馏 接着上一篇我们介绍了YOLOv8的剪枝方案和代码&#xff0c;本篇文章将剪枝后的模型作为学生模型&#xff0c;剪枝前的模型作为教师模型对剪枝模型进行蒸馏&#xff0c;从而进一步提到轻量模型的性能。 Channel-wise Distillation (CWD) 问题和方法 …...

全国消防知识竞赛活动方案哪家强

关键词&#xff1a;消防安全、预防火灾、消防意识、消防员、防火安全 适合行业&#xff1a;所有行业 推荐功能&#xff1a;答题、投票、H5 宣传角度 1.从日常生活场景出发&#xff0c;指导大家如何检查家庭中的火灾隐患。例如检查电线是否老化、插座是否过载、是否在楼梯间…...

JavaEE学习一条龙服务————概述

鉴于之前的笔记较乱&#xff0c;没有逻辑关系&#xff0c;&#xff0c;博主决定从JacaEE整个学习的阶段出发&#xff0c;整理一系列博客&#xff0c;供大家学习交流&#xff0c;提升自己。 此文章已绑定一篇我为大家梳理的JavaEE一条龙学习知识点的文档&#xff0c;大家可下载…...

分支预测器BPU

分支预测器BPU 0 Intro0.1 CPU执行过程0.2 分支预测0.2.1 TAGE预测器0.2.2 跳转地址 分支预测器BPU是深入研究一个高性能处理器的一个很好的开始项目&#xff1b; 0 Intro 条件分支是指后续具有两路可执行的分支。可以分为跳转分支(taken branch)和不跳转分支(not-taken branc…...

Go 系列教程 —— 数组和切片

数组 数组是同一类型元素的集合。例如&#xff0c;整数集合 5,8,9,79,76 形成一个数组。Go 语言中不允许混合不同类型的元素&#xff0c;例如包含字符串和整数的数组。&#xff08;译者注&#xff1a;当然&#xff0c;如果是 interface{} 类型数组&#xff0c;可以包含任意类型…...

适配器模式【对象适配器模式和类适配器模式,以及具体使用场景】

2.1-适配器模式 ​ 类的适配器模式是把适配者类的API转换成为目标类的API&#xff0c;适配器模式使得原来由于接口不兼容而不能一起工作的那些类可以一起工作&#xff0c;其实在具体的开发中&#xff0c;对于自己系统一开始的设计不会优先考虑适配器模式&#xff0c;通常会将接…...

【EXCEL数据处理】保姆级教程 000016案例 EXCEL的vlookup函数。

【EXCEL数据处理】000016案例 vlookup函数。 前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】保姆级教…...

【软件推荐】通过Rufus制作信创操作系统U盘安装盘 _ 统信 _ 麒麟 _ 方德

原文链接&#xff1a;【软件推荐】通过Rufus制作信创操作系统U盘安装盘 | 统信 | 麒麟 | 方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于如何使用Rufus制作信创操作系统&#xff08;如统信UOS、麒麟KOS、中科方德等&#xff09;的U盘启动安装盘的文章。Ruf…...

MySql 多表设计

项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xff0c;基本分为&#xff1a;一对多&#xff0c;多对多&a…...

wpf实现新用户页面引导

第一步 第二部 部分代码: private void show(int xh, FrameworkElement fe, string con, Visibility vis Visibility.Visible) {Point point fe.TransformToAncestor(Window.GetWindow(fe)).Transform(new Point(0, 0));//获取控件坐标点RectangleGeometry rg new Rectangl…...

【小白向】机器人入门之ROS系统的学习(Ubuntu24.04+ROS2)

目录 一.复杂的机器人系统 二.ROS机器人系统 1.简介 1.节点 2.话题 2.安装 3.测试 4.可视化 RQT&#xff1a; RVIZ&#xff1a; 显示属性&#xff1a; 显示状态&#xff1a; 一.复杂的机器人系统 依照我们现在的技术来看&#xff0c;机器人系统仍是极其复杂的&#xff0c;往…...

SNAP-MS策略:可溶性水凝胶微珠,高效表征蛋白质复合物

大家好&#xff01;今天来了解一种高效的蛋白质复合物纯化和表征策略的文章——《Biofunctionalized dissolvable hydrogel microbeads enable efficient characterization of native protein complexes》发表于《Nature Communications》。蛋白质复合物在生命过程中起着关键作…...

java对象序列化Serializable的应用场景

目录 Java对象序列化的应用场景 网络通信&#xff1a; 对象持久化&#xff1a; 分布式计算&#xff1a; 缓存存储&#xff1a; 远程方法调用&#xff08;RMI&#xff09;&#xff1a; 基于JMS的消息传递&#xff1a; Java集合类中的对象需要被存储&#xff1a; 对象深…...

springboot-网站开发-linux服务器部署jar格式图片存档路径问题

springboot-网站开发-linux服务器部署jar格式图片存档路径问题&#xff01;近期在部署自己的网站源码&#xff0c;使用的是jar格式的编码格式。发布到远程服务器后&#xff0c;发现客户捐款的证书图片存在异常。 经过排查代码&#xff0c;找到了原因。下面分享给大家。 1&…...

面试--java基础

Java基础 Java 中的几种基本数据类型了解么&#xff1f;基本类型和包装类型的区别&#xff1f;包装类型的缓存机制了解么&#xff1f;成员变量与局部变量的区别&#xff1f;静态变量有什么作用&#xff1f;静态方法为什么不能调用非静态成员?重载和重写有什么区别&#xff1f…...

NLP自然语言处理

计算机视觉和图像处理 Tensorflow入门深度神经网络图像分类目标检测图像分割OpenCVPytorchNLP自然语言处理 NLP自然语言处理 一、NLP简介二、文本预处理2.1 文本预处理简介2.2 文本处理的基本方法2.3 文本张量表示方法2.3.1 onehot编码2.3.2 word2vec编码 2.4 文本数据分析2.5…...

web自动化测试基础(从配置环境到自动化实现登录测试用例的执行,vscode如何导入自己的python包)

接下来的一段时间里我会和大家分享自动化测试相关的一些知识希望大家可以多多支持&#xff0c;一起进步。 一、环境的配置 前提安装好了python解释器并配好了环境&#xff0c;并安装好了VScode 下载的浏览器和浏览器驱动需要一样的版本号(只看大版本)。 1、安装浏览器 Chro…...

鸿蒙 Next 实战: 电子木鱼

前言 正所谓&#xff1a;Hello Word 是程序员学任何一门语言的第一个程序实践。这其实也是一个不错的正反馈&#xff0c;那如何让学习鸿蒙 Next 更有成就感呢&#xff1f;下面就演示一下从零开发一个鸿蒙 Next 版的电子木鱼&#xff0c;主打就是一个抽象&#xff01; 实现要点…...

SQLite SQL调优指南及高级SQL技巧

记忆已更新 以下是《SQLite SQL调优指南及高级SQL技巧》文章的完整输出&#xff0c;字数目标为30000字&#xff0c;详细介绍并结合2024年最新技术趋势和优化策略。代码部分不计入字数统计。 SQLite SQL调优指南及高级SQL技巧 SQLite 是广泛使用的嵌入式数据库&#xff0c;因其…...

WordPress 6.7即将发布的新功能(和截图)

我们一直在密切关注 WordPress 6.7 的开发并测试该版本的测试版&#xff0c;它将带来一些令人兴奋的更新和几个新功能。 例如&#xff0c;我们很高兴地发现即将发布的版本将附带全新的默认主题&#xff0c;并对块编辑器和站点编辑体验进行大规模改进。 在本文中&#xff0c;我…...

SpringBoot整合QQ邮箱

SpringBoot可以通过导入依赖的方式集成多种技术&#xff0c;这当然少不了我们常用的邮箱&#xff0c;现在本章演示SpringBoot整合QQ邮箱发送邮件.... 下面按步骤进行&#xff1a; 1.获取QQ邮箱授权码 1.1 登录QQ邮箱 1.2 开启SMTP服务 找到下图中的SMTP服务区域&#xff0c;…...

低质量数据的多模态融合方法

目录 多模态融合 低质量多模态融合的核心挑战 噪声多模态数据学习 缺失模态插补 平衡多模态融合 动态多模态融合 启发式动态融合 基于注意力的动态融合 不确定性感知动态融合 论文 多模态融合 多模态融合侧重于整合多种模态的信息,以实现更准确的预测,在自动驾驶、…...

计算机毕业设计 基于Django的在线考试系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…...

Shell脚本linux登录自动检查

.bashrc 用于设置用户的 Bash shell 环境&#xff0c;在每次打开一个新的终端窗口或启动一个新的 Bash 会话时被执行 代码 login_check.sh #!/bin/bash clear LogFileNamepolling.$(date %F-%T) EchoFormat$(for (( i0; i<30; i )); do echo -n ""; done)# 显示…...

Golang | Leetcode Golang题解之第450题删除二叉搜索树的节点

题目&#xff1a; 题解&#xff1a; func deleteNode(root *TreeNode, key int) *TreeNode {var cur, curParent *TreeNode root, nilfor cur ! nil && cur.Val ! key {curParent curif cur.Val > key {cur cur.Left} else {cur cur.Right}}if cur nil {retur…...

Linux 之 Linux应用编程概念、文件IO、标准IO

Linux应用编程概念、文件IO、标准IO 学习任务&#xff1a; 1、 学习Linux 应用开发概念&#xff0c;什么是系统调用&#xff0c;什么是库函数 2、 学习文件IO&#xff1a;包括 read、write、open、close、lseek 3、 深入文件IO&#xff1a;错误处理、exit 等 4、 学习标准IO&a…...

PDF处理技巧:Windows电脑如何选择合适的 PDF 编辑器

您可以阅读本文以了解用于在 PC 上编辑 PDF 的顶级免费软件&#xff0c;而无需花费任何费用即可轻松进行快速编辑、拆分、合并、注释、转换和共享您的 PDF。 PDF 或可移植文档文件是由 Adobe 创建的一种多功能文件格式。它可以帮助您轻松可靠地交换文档&#xff0c;无论相关方…...

【c++】初步了解类和对象2

1、类的作用域 类定义了一个新的作用域&#xff0c;类的所有成员都在类的作用域中。在类体外定义成员时&#xff0c;需要使用 :: 作用域操作符指明成员属于哪个类域。 如图&#xff0c;此时在类内声明了函数firstUniqChar()&#xff0c;在类外进行了函数体的具体定义。 但是却…...

Python库pandas之四

Python库pandas之四 输入/输出read_json函数应用实列 输入/输出 read_json 函数 词法&#xff1a;pandas.read_json(path_or_buf, *, orientNone, typ‘frame’, dtypeNone, convert_axesNone, convert_datesTrue, keep_default_datesTrue, precise_floatFalse, date_unitNo…...

网络攻防技术--第三次作业

文章目录 第三次作业一、通过搜索引擎搜索自己在因特网上的足迹&#xff0c;并确认是否存在隐私和敏感信息泄露问题。如果有信息泄露&#xff0c;提出解决方法。二、结合实例总结web搜索和挖掘的方法。三、网络扫描有哪几种类型&#xff1f;分别有什么作用&#xff1f;利用一种…...