DDPM 核心代码解析(1)

所有代码 已上传至GitHub - duhanyue349/diffusion_model_learned_ddpm_main: 扩散模型基础框架源代码

目录结构如下

在train_cifar.py 中展示了扩散模型训练的所有代码

如果没有安装wandb  可以在create_argparser()设置 log_to_wandb=False

 一、加载模型参数 args

 这里用了一个create_argparser()函数创建命令行解析器

args = create_argparser().parse_args()
def create_argparser():device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")run_name = datetime.datetime.now().strftime("ddpm-%Y-%m-%d-%H-%M")defaults = dict(learning_rate=2e-4,batch_size=128,iterations=800000,log_to_wandb=True,log_rate=10,checkpoint_rate=10,log_dir="~/ddpm_logs",project_name='ddpm',run_name=run_name,model_checkpoint=None,optim_checkpoint=None,schedule_low=1e-4,schedule_high=0.02,device=device,)defaults.update(script_utils.diffusion_defaults())parser = argparse.ArgumentParser()script_utils.add_dict_to_argparser(parser, defaults)return parser

 

defaults是基础的一些参数,用defaults.update(script_utils.diffusion_defaults())可以将模型参数加载进来  使用了这个函数diffusion_defaults()其返回的是一个字典
def diffusion_defaults():defaults = dict(num_timesteps=1000,schedule="linear",loss_type="l2",use_labels=False,base_channels=128,channel_mults=(1, 2, 2, 2),num_res_blocks=2,time_emb_dim=128 * 4,norm="gn",dropout=0.1,activation="silu",attention_resolutions=(1,),schedule_low=1e-4,schedule_high=0.02,ema_decay=0.9999,ema_update_rate=1,)return defaults
随后  实例化命令行解析器
parser = argparse.ArgumentParser()
script_utils.add_dict_to_argparser(parser, defaults)为解析器添加参数#这个函数是运用了字典存储命令函参数的形式,通过命令行参数的键值对来获取参数
def add_dict_to_argparser(parser, default_dict):"""https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/script_util.py"""for k, v in default_dict.items():v_type = type(v)if v is None:v_type = strelif isinstance(v, bool):v_type = str2boolparser.add_argument(f"--{k}", default=v, type=v_type)
基础的创建解析器的步骤为
#parser = argparse.ArgumentParser() 创建命令行解析器
#parser.add_argument() 添加命令行参数
#args = parser.parse_args() 对命令行参数进行解析

二、获得 diffusion 模型架构

diffusion = script_utils.get_diffusion_from_args(args).to(device)

这里调用了 get_diffusion_from_args   函数 加载模型,输入时刚刚创建的参数解析器

def get_diffusion_from_args(args):activations = {"relu": F.relu,"mish": F.mish,"silu": F.silu,}model = UNet(img_channels=3,base_channels=args.base_channels,channel_mults=args.channel_mults,time_emb_dim=args.time_emb_dim,norm=args.norm,dropout=args.dropout,activation=activations[args.activation],attention_resolutions=args.attention_resolutions,num_classes=None if not args.use_labels else 10,initial_pad=0,)if args.schedule == "cosine":betas = generate_cosine_schedule(args.num_timesteps)else:betas = generate_linear_schedule(args.num_timesteps,args.schedule_low * 1000 / args.num_timesteps,args.schedule_high * 1000 / args.num_timesteps,)diffusion = GaussianDiffusion(model, (32, 32), 3, 10,betas,ema_decay=args.ema_decay,ema_update_rate=args.ema_update_rate,ema_start=2000,loss_type=args.loss_type,)return diffusion

返回的是一个  GaussianDiffusion类   把model(UNet)、betas、loss_type等传给了这个类

 这里beta 有两种定义方法  一个时cosine 一个是linear 

betas = generate_cosine_schedule(args.num_timesteps)
betas = generate_linear_schedule(args.num_timesteps,args.schedule_low * 1000 / args.num_timesteps,args.schedule_high * 1000 / args.num_timesteps,)
GaussianDiffusionz这个类在diffusion .py 文件中
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as Ffrom functools import partial
from copy import deepcopyfrom .ema import EMA
from .utils import extractclass GaussianDiffusion(nn.Module):__doc__ = r"""Gaussian Diffusion model. Forwarding through the module returns diffusion reversal scalar loss tensor.Input:x: tensor of shape (N, img_channels, *img_size)y: tensor of shape (N)Output:scalar loss tensorArgs:model (nn.Module): model which estimates diffusion noiseimg_size (tuple): image size tuple (H, W)img_channels (int): number of image channelsbetas (np.ndarray): numpy array of diffusion betasloss_type (string): loss type, "l1" or "l2"ema_decay (float): model weights exponential moving average decayema_start (int): number of steps before EMAema_update_rate (int): number of steps before each EMA update"""def __init__(self,model,img_size,img_channels,num_classes,betas,loss_type="l2",ema_decay=0.9999,ema_start=5000,ema_update_rate=1,):super().__init__()self.model = modelself.ema_model = deepcopy(model)self.ema = EMA(ema_decay)self.ema_decay = ema_decayself.ema_start = ema_startself.ema_update_rate = ema_update_rateself.step = 0self.img_size = img_sizeself.img_channels = img_channelsself.num_classes = num_classesif loss_type not in ["l1", "l2"]:raise ValueError("__init__() got unknown loss type")self.loss_type = loss_typeself.num_timesteps = len(betas)alphas = 1.0 - betasalphas_cumprod = np.cumprod(alphas)to_torch = partial(torch.tensor, dtype=torch.float32)self.register_buffer("betas", to_torch(betas))self.register_buffer("alphas", to_torch(alphas))self.register_buffer("alphas_cumprod", to_torch(alphas_cumprod))self.register_buffer("sqrt_alphas_cumprod", to_torch(np.sqrt(alphas_cumprod)))self.register_buffer("sqrt_one_minus_alphas_cumprod", to_torch(np.sqrt(1 - alphas_cumprod)))self.register_buffer("reciprocal_sqrt_alphas", to_torch(np.sqrt(1 / alphas)))self.register_buffer("remove_noise_coeff", to_torch(betas / np.sqrt(1 - alphas_cumprod)))self.register_buffer("sigma", to_torch(np.sqrt(betas)))def update_ema(self):self.step += 1if self.step % self.ema_update_rate == 0:if self.step < self.ema_start:self.ema_model.load_state_dict(self.model.state_dict())else:self.ema.update_model_average(self.ema_model, self.model)@torch.no_grad()def remove_noise(self, x, t, y, use_ema=True):if use_ema:return ((x - extract(self.remove_noise_coeff, t, x.shape) * self.ema_model(x, t, y)) *extract(self.reciprocal_sqrt_alphas, t, x.shape))else:return ((x - extract(self.remove_noise_coeff, t, x.shape) * self.model(x, t, y)) *extract(self.reciprocal_sqrt_alphas, t, x.shape))@torch.no_grad()def sample(self, batch_size, device, y=None, use_ema=True):if y is not None and batch_size != len(y):raise ValueError("sample batch size different from length of given y")x = torch.randn(batch_size, self.img_channels, *self.img_size, device=device)for t in range(self.num_timesteps - 1, -1, -1):t_batch = torch.tensor([t], device=device).repeat(batch_size)x = self.remove_noise(x, t_batch, y, use_ema)if t > 0:x += extract(self.sigma, t_batch, x.shape) * torch.randn_like(x)return x.cpu().detach()@torch.no_grad()def sample_diffusion_sequence(self, batch_size, device, y=None, use_ema=True):if y is not None and batch_size != len(y):raise ValueError("sample batch size different from length of given y")x = torch.randn(batch_size, self.img_channels, *self.img_size, device=device)diffusion_sequence = [x.cpu().detach()]for t in range(self.num_timesteps - 1, -1, -1):t_batch = torch.tensor([t], device=device).repeat(batch_size)x = self.remove_noise(x, t_batch, y, use_ema)if t > 0:x += extract(self.sigma, t_batch, x.shape) * torch.randn_like(x)diffusion_sequence.append(x.cpu().detach())return diffusion_sequencedef perturb_x(self, x, t, noise):return (extract(self.sqrt_alphas_cumprod, t, x.shape) * x +extract(self.sqrt_one_minus_alphas_cumprod, t, x.shape) * noise)   def get_losses(self, x, t, y):noise = torch.randn_like(x)perturbed_x = self.perturb_x(x, t, noise)estimated_noise = self.model(perturbed_x, t, y)if self.loss_type == "l1":loss = F.l1_loss(estimated_noise, noise)elif self.loss_type == "l2":loss = F.mse_loss(estimated_noise, noise)return lossdef forward(self, x, y=None):b, c, h, w = x.shapedevice = x.deviceif h != self.img_size[0]:raise ValueError("image height does not match diffusion parameters")if w != self.img_size[0]:raise ValueError("image width does not match diffusion parameters")t = torch.randint(0, self.num_timesteps, (b,), device=device)return self.get_losses(x, t, y)def generate_cosine_schedule(T, s=0.008):def f(t, T):return (np.cos((t / T + s) / (1 + s) * np.pi / 2)) ** 2alphas = []f0 = f(0, T)for t in range(T + 1):alphas.append(f(t, T) / f0)betas = []for t in range(1, T + 1):betas.append(min(1 - alphas[t] / alphas[t - 1], 0.999))return np.array(betas)def generate_linear_schedule(T, low, high):return np.linspace(low, high, T)

 三、获得优化器、数据集

optimizer = torch.optim.Adam(diffusion.parameters(), lr=args.learning_rate)
#省略了原代码中if args.model_checkpoint is not None:、if args.log_to_wandb:.....这些batch_size = args.batch_sizetrain_dataset = datasets.CIFAR10(root='./cifar_train',train=True,download=True,transform=script_utils.get_transform(),)test_dataset = datasets.CIFAR10(root='./cifar_test',train=False,download=True,transform=script_utils.get_transform(),)train_loader = script_utils.cycle(DataLoader(train_dataset,batch_size=batch_size,shuffle=True,drop_last=True,num_workers=2,))test_loader = DataLoader(test_dataset, batch_size=batch_size, drop_last=True, num_workers=2)

这里采用了一个cycle 的函数  循环加载数据  后面会和next 一起使用,x, y = next(train_loader)

from torch.utils.data import DataLoader
from torchvision import datasets
import torchvision.transforms as Tclass RescaleChannels:def __call__(self, sample):return 2 * sample - 1'''return 2 * sample - 1 - 这一行代码是对输入 sample 进行线性变换的公式。
它将 sample 的每个像素值乘以 2,然后减去 1。这样的变换通常用于将像素值从 [0, 1] 范围映射到 [-1, 1] 范围。
为什么要做这样的转换?在神经网络训练中,将数据归一化到特定的范围可以带来以下好处:
数值稳定性:某些激活函数(如 tanh)在输入接近 [-1, 1] 范围时性能更好。
加速收敛:归一化的数据可以减少梯度消失或爆炸的问题,从而加快模型的训练速度。
标准化:确保不同来源或不同尺度的数据在模型中具有相似的影响。
所以,当你有一个 sample,比如说一个图像,其像素值范围是 [0, 1],
通过 RescaleChannels 类的实例调用,它会将像素值转换到 [-1, 1] 范围,这在很多情况下对于模型训练是有利的'''
def get_transform():return T.Compose([T.ToTensor(),RescaleChannels(),])
train_dataset = datasets.CIFAR10(root='./cifar_train',train=True,download=True,transform=get_transform(),
)
def cycle(dl):"""https://github.com/lucidrains/denoising-diffusion-pytorch/"""while True:for data in dl:yield data#这个 cycle 函数是一个无限循环的生成器,它的作用是让数据加载器(dl)的数据可以被无限次地迭代。这种设计通常在深度学习中用于数据增强或者当训练数据集较小而希望增加训练轮次时使用。
#当这个函数被调用时,它会不断地从 dl 中取出数据,一旦 dl 的数据被完全遍历,它会重新开始遍历,从而形成一个无限循环的数据流。
#这种设计允许你在训练模型时,即使数据集很小,也可以像拥有无限数据一样进行训练。
train_loader = cycle(DataLoader(train_dataset,batch_size=batch_size,shuffle=True,drop_last=True,num_workers=2,))

 四、开始训练

acc_train_loss = 0for iteration in range(1, args.iterations + 1):diffusion.train()x, y = next(train_loader)x = x.to(device)y = y.to(device)if args.use_labels:loss = diffusion(x, y)else:loss = diffusion(x)acc_train_loss += loss.item()optimizer.zero_grad()loss.backward()optimizer.step()diffusion.update_ema()

 五、测试部分

            if iteration % args.log_rate == 0:test_loss = 0with torch.no_grad():diffusion.eval()for x, y in test_loader:x = x.to(device)y = y.to(device)if args.use_labels:loss = diffusion(x, y)else:loss = diffusion(x)test_loss += loss.item()if args.use_labels:samples = diffusion.sample(10, device, y=torch.arange(10, device=device))else:samples = diffusion.sample(10, device)#将张量(tensor)转换回可以显示或保存的图像格式samples = ((samples + 1) / 2).clip(0, 1).permute(0, 2, 3, 1).numpy()#将数据范围从[-1, 1](常见的归一化范围)转换到[0, 2] 将范围从[0, 2]缩放到[0, 1],这是常见的图像像素值范围。函数确保所有像素值都在0和1之间。这可以防止因为浮点数运算误差导致的像素值超出正常范围。,permute将其转换为(batch_size, height, width, channels),这通常是将数据从PyTorch的通道优先格式转换为更通用的格式,便于显示或保存图像。test_loss /= len(test_loader)acc_train_loss /= args.log_ratewandb.log({"test_loss": test_loss,"train_loss": acc_train_loss,"samples": [wandb.Image(sample) for sample in samples],})acc_train_loss = 0if iteration % args.checkpoint_rate == 0:model_filename = f"{args.log_dir}/{args.project_name}-{args.run_name}-iteration-{iteration}-model.pth"optim_filename = f"{args.log_dir}/{args.project_name}-{args.run_name}-iteration-{iteration}-optim.pth"# 获取目录路径log_dir1 = os.path.dirname(model_filename)log_dir2 = os.path.dirname(optim_filename)# 创建目录,如果它不存在os.makedirs(log_dir1, exist_ok=True)os.makedirs(log_dir2, exist_ok=True)# 使用完整的文件路径保存模型和优化器状态torch.save(diffusion.state_dict(), model_filename)torch.save(optimizer.state_dict(), optim_filename)

六、整个train_cifar 所有代码

 

import argparse
import datetime
import torch
import wandb
from torch.utils.data import DataLoader
from torchvision import datasets
from ddpm import script_utils
import osos.environ["WANDB_API_KEY"] = "b9171ddb0a1638d8cca0425e41c8a9d789281515"
os.environ["WANDB_MODE"] = "online"wandb.login(key="b9171ddb0a1638d8cca0425e41c8a9d789281515")
def main():args = create_argparser().parse_args()device = args.devicetry:diffusion = script_utils.get_diffusion_from_args(args).to(device)optimizer = torch.optim.Adam(diffusion.parameters(), lr=args.learning_rate)if args.model_checkpoint is not None:diffusion.load_state_dict(torch.load(args.model_checkpoint))if args.optim_checkpoint is not None:optimizer.load_state_dict(torch.load(args.optim_checkpoint))if args.log_to_wandb:if args.project_name is None:raise ValueError("args.log_to_wandb set to True but args.project_name is None")run = wandb.init(project=args.project_name,config=vars(args),name=args.run_name,)wandb.watch(diffusion)batch_size = args.batch_sizetrain_dataset = datasets.CIFAR10(root='./cifar_train',train=True,download=True,transform=script_utils.get_transform(),)test_dataset = datasets.CIFAR10(root='./cifar_test',train=False,download=True,transform=script_utils.get_transform(),)train_loader = script_utils.cycle(DataLoader(train_dataset,batch_size=batch_size,shuffle=True,drop_last=True,num_workers=2,))test_loader = DataLoader(test_dataset, batch_size=batch_size, drop_last=True, num_workers=2)acc_train_loss = 0for iteration in range(1, args.iterations + 1):diffusion.train()x, y = next(train_loader)x = x.to(device)y = y.to(device)if args.use_labels:loss = diffusion(x, y)else:loss = diffusion(x)acc_train_loss += loss.item()optimizer.zero_grad()loss.backward()optimizer.step()diffusion.update_ema()if iteration % args.log_rate == 0:test_loss = 0with torch.no_grad():diffusion.eval()for x, y in test_loader:x = x.to(device)y = y.to(device)if args.use_labels:loss = diffusion(x, y)else:loss = diffusion(x)test_loss += loss.item()if args.use_labels:samples = diffusion.sample(10, device, y=torch.arange(10, device=device))else:samples = diffusion.sample(10, device)#将张量(tensor)转换回可以显示或保存的图像格式samples = ((samples + 1) / 2).clip(0, 1).permute(0, 2, 3, 1).numpy()#将数据范围从[-1, 1](常见的归一化范围)转换到[0, 2] 将范围从[0, 2]缩放到[0, 1],这是常见的图像像素值范围。函数确保所有像素值都在0和1之间。这可以防止因为浮点数运算误差导致的像素值超出正常范围。,permute将其转换为(batch_size, height, width, channels),这通常是将数据从PyTorch的通道优先格式转换为更通用的格式,便于显示或保存图像。test_loss /= len(test_loader)acc_train_loss /= args.log_ratewandb.log({"test_loss": test_loss,"train_loss": acc_train_loss,"samples": [wandb.Image(sample) for sample in samples],})acc_train_loss = 0if iteration % args.checkpoint_rate == 0:model_filename = f"{args.log_dir}/{args.project_name}-{args.run_name}-iteration-{iteration}-model.pth"optim_filename = f"{args.log_dir}/{args.project_name}-{args.run_name}-iteration-{iteration}-optim.pth"# 获取目录路径log_dir1 = os.path.dirname(model_filename)log_dir2 = os.path.dirname(optim_filename)# 创建目录,如果它不存在os.makedirs(log_dir1, exist_ok=True)os.makedirs(log_dir2, exist_ok=True)# 使用完整的文件路径保存模型和优化器状态torch.save(diffusion.state_dict(), model_filename)torch.save(optimizer.state_dict(), optim_filename)if args.log_to_wandb:run.finish()except KeyboardInterrupt:if args.log_to_wandb:run.finish()print("Keyboard interrupt, run finished early")def create_argparser():device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")run_name = datetime.datetime.now().strftime("ddpm-%Y-%m-%d-%H-%M")defaults = dict(learning_rate=2e-4,batch_size=128,iterations=800000,log_to_wandb=True,log_rate=10,checkpoint_rate=10,log_dir="~/ddpm_logs",project_name='ddpm',run_name=run_name,model_checkpoint=None,optim_checkpoint=None,schedule_low=1e-4,schedule_high=0.02,device=device,)defaults.update(script_utils.diffusion_defaults())parser = argparse.ArgumentParser()script_utils.add_dict_to_argparser(parser, defaults)return parserif __name__ == "__main__":main()

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

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

相关文章

直播圈不再只讲技术和千川,管理成为新焦点

直播圈不讲技术&#xff0c;也不讲千川啦&#xff0c;开始讲管理啦&#xff0c;感叹直播带货发展真快&#xff0c;都说要傻瓜化&#xff0c;讲的东西一年能迭代 3 个版本&#xff0c;甚至带货线下课不讲半天团队管理你的课都显得low。关键没一个能告诉你管理是什么管什么&#…

【1】CPU飙升到200%以上问题汇总

原链接 【1】CPU飙升到200%以上问题汇总 CPU飙升到200%以上是生成中常见的问题 注意&#xff1a; 1. linux的cpu使用频率是根据cpu个数和核数决定的 2. top&#xff0c;然后你按一下键盘的1&#xff0c;这就是单个核心的负载&#xff0c;不然是所有核心的负载相加&#xff0c;…

弹幕背后:B站UP主创作服务解析

引言 在B站&#xff0c;每一条飘过的弹幕都是一个故事的碎片&#xff0c;它们汇聚成一幅幅生动的社交画卷。这里&#xff0c;不仅仅是一个视频分享平台&#xff0c;弹幕背后更是一个充满活力的创作者生态系统。B站以其独特的弹幕文化&#xff0c;为创作者和观众之间搭建起了一座…

RxJava基础使用

Rx思想 事件从起点流向终点。 过程中可以对事件进行拦截&#xff0c;拦截时可以对事件进行处理&#xff0c;处理后将处理后的事件继续流向终点。 终点接收上一次处理后的事件。 获取网络图片并显示 基础实现 使用Observable.just(path) 创建图片路径的Observable。 使用m…

Unity | Shader基础知识(第二十集:应用-简易流光、LOD)

目录 一、前言 二、LOD 1.什么是LOD 2.代码如何调节LOD 三、流光 1.资源准备 2.uv移动 3.获取图片中的uv 4.改变uv去取流光的颜色&#xff08;时间的应用&#xff09; 5.图片叠加 6.透明图片的叠加 四、纯净代码 五、作者的碎碎念 一、前言 有小伙伴问&#xf…

Studying-代码随想录训练营day45| 115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇

第45天&#xff0c;子序列part03&#xff0c;编辑距离&#x1f4aa;(ง •_•)ง&#xff0c;编程语言&#xff1a;C 目录 115.不同的子序列 583. 两个字符串的删除操作 72. 编辑距离 编辑距离总结篇 115.不同的子序列 文档讲解&#xff1a;代码随想录不同的子序列 视频讲…

分布式搜索引擎ES--Elasticsearch集群

1.Elasticsearch集群的概念 分片机制&#xff1a;每个索引都可以被分片 索引my_doc只有一个主分片&#xff1b;索引shop有三个主分片&#xff1b;索引shop2有5个主分片;(参考前面案例) 每个主分片都包含索引的数据&#xff0c;由于目前是单机&#xff0c;所以副分片是没有的&a…

安全开发第一篇

文章目录 参与RASP、IAST等安全防护工具的开发技术背景开发流程技术挑战工具和资源示例结论 以phpstudydvwa为列 实现 SQL 注入检测脚本 参与RASP、IAST等安全防护工具的开发 参与 RASP&#xff08;Runtime Application Self-Protection&#xff09;和 IAST&#xff08;Intera…

为开源奉献一份自己的力量:Cesium没有热力图组件,我们自己封装!

大家好&#xff0c;我是日拱一卒的攻城师不浪&#xff0c;专注可视化、数字孪生、前端提效、nodejs、AI学习、GIS等学习沉淀&#xff0c;这是2024年输出的第27/100篇文章。 交流合作&#xff1a;brown_7778 前言 热力图&#xff0c;在很多可视化场景开发中会被经常提到&#x…

electron 主进程和渲染进程

最近在整理electron 相关的项目问题&#xff0c;对自己来说也是温故知新&#xff0c;也希望能对小伙伴们有所帮助&#xff0c;大家共同努力共同进步。加油&#xff01;&#xff01;&#xff01;&#xff01; 虽然最近一年前端大环境不好&#xff0c;但是大家还是要加油鸭&#…

网安零基础入门神书,全面介绍Web渗透核心攻击与防御方式!

Web安全是指Web服务程序的漏洞&#xff0c;通常涵盖Web漏洞、操作系统洞、数据库漏洞、中间件漏洞等。 “渗透测试”作为主动防御的一种关键手段&#xff0c;对评估网络系统安全防护及措施至关重要&#xff0c;因为只有发现问题才能及时终止并预防潜在的安全风险。 根据网络安…

【Qt开发】No matching signal for on_toolButton_clicked() 解决方案

【Qt开发】No matching signal for on_toolButton_clicked() 解决方案 文章目录 No matching signal for xxx 解决方案附录&#xff1a;C语言到C的入门知识点&#xff08;主要适用于C语言精通到Qt的C开发入门&#xff09;C语言与C的不同C中写C语言代码C语言到C的知识点Qt开发中…

Linux安装青龙面板并将本地服务映射至公网实现远程访问

文章目录 前言一、前期准备本教程环境为&#xff1a;Centos7&#xff0c;可以跑Docker的系统都可以使用。本教程使用Docker部署青龙&#xff0c;如何安装Docker详见&#xff1a; 二、安装青龙面板三、映射本地部署的青龙面板至公网四、使用固定公网地址访问本地部署的青龙面板 …

全局异常 @ControllerAdvice 该怎么写

本文首发于稀土掘金&#xff1a;全局异常 ControllerAdvice 该怎么写&#xff0c;该账号即为本人账号&#xff0c;非搬运。 问题由来 很多小伙伴刚进公司做项目的时候&#xff0c;会看到项目里面有一个ControllerAdvice标记的类&#xff0c;整个类的编码结构大概是这样子&…

程序员极力推荐的几款插件

前言 h之前分享程序员喜欢的神器之后&#xff0c;还是相当多的伙伴喜欢&#xff0c;那一期的工具不仅是协助你工作还是工作外摸鱼划水&#xff0c;体验感是相当不错的。 这一期我还是打算把我非常喜欢用的几款插件分享给大家&#xff0c;这几款差劲不仅帮助我节省很多时间&am…

2024年软件系统与信息处理国际会议(ICSSIP 2024)即将召开!

2024年软件系统与信息处理国际会议&#xff08;ICSSIP 2024&#xff09;将于2024年10月25-27日在中国昆明举行。引领技术前沿&#xff0c;共谋创新未来。ICSSIP 2024将汇聚来自世界各地的专家学者&#xff0c;他们将在会上分享最新的研究成果、技术突破及实践经验。会议议题涵盖…

WAAP替代传统WAF已成趋势

数字化时代&#xff0c;Web应用和API已成为企业运营的核心。然而&#xff0c;随着网络攻击手段的不断进化&#xff0c;自动化攻击愈发频繁&#xff0c;传统的Web应用防火墙&#xff08;WAF&#xff09;已难以满足现代企业的安全需求。WAAP&#xff08;Web Application and API …

我出一道面试题,看看你能拿 3k 还是 30k!

大家好&#xff0c;我是程序员鱼皮。欢迎屏幕前的各位来到今天的模拟面试现场&#xff0c;接下来我会出一道经典的后端面试题&#xff0c;你只需要进行 4 个简单的选择&#xff0c;就能判断出来你的水平是新手&#xff08;3k&#xff09;、初级&#xff08;10k&#xff09;、中…

大镜山阿里巴巴国际站数据采集软件使用方法|阿里国际站商家信息采集软件使用方法|阿里国际站信息采集软件使用方法

大镜山阿里巴巴国际站数据采集软件一款采集阿里巴巴国际站alibaba.com商家数据的软件&#xff0c;采集的数据包括店铺名称、店铺年份、评分、邮件地址、手机号码、网址及社交连接等。 下载大镜山阿里巴巴国际站数据采集软件 大镜山阿里巴巴国际站数据采集软件下载地址 大镜山…

ubuntu20.04.6 安装Skywalking 10.0.1

1.前置准备 1.1. **jdk17&#xff08;Skywalking10 jdk22不兼容&#xff0c;用17版本即可&#xff09;**安装&#xff1a; https://blog.csdn.net/CsethCRM/article/details/140768670 1.2. elasticsearch安装&#xff1a; https://blog.csdn.net/CsethCRM/article/details…