DDPM 核心代码解析(1)

news2025/1/13 10:20:38

所有代码 已上传至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 = str
        elif isinstance(v, bool):
            v_type = str2bool
        parser.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 F

from functools import partial
from copy import deepcopy

from .ema import EMA
from .utils import extract

class 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 tensor
    Args:
        model (nn.Module): model which estimates diffusion noise
        img_size (tuple): image size tuple (H, W)
        img_channels (int): number of image channels
        betas (np.ndarray): numpy array of diffusion betas
        loss_type (string): loss type, "l1" or "l2"
        ema_decay (float): model weights exponential moving average decay
        ema_start (int): number of steps before EMA
        ema_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 = model
        self.ema_model = deepcopy(model)

        self.ema = EMA(ema_decay)
        self.ema_decay = ema_decay
        self.ema_start = ema_start
        self.ema_update_rate = ema_update_rate
        self.step = 0

        self.img_size = img_size
        self.img_channels = img_channels
        self.num_classes = num_classes

        if loss_type not in ["l1", "l2"]:
            raise ValueError("__init__() got unknown loss type")

        self.loss_type = loss_type
        self.num_timesteps = len(betas)

        alphas = 1.0 - betas
        alphas_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 += 1
        if 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_sequence

    def 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 loss

    def forward(self, x, y=None):
        b, c, h, w = x.shape
        device = x.device

        if 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)) ** 2
    
    alphas = []
    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_size

        train_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 T

class 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 = 0

        for 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 = 0
                with 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_rate

                wandb.log({
                    "test_loss": test_loss,
                    "train_loss": acc_train_loss,
                    "samples": [wandb.Image(sample) for sample in samples],
                })

                acc_train_loss = 0

            if 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 os

os.environ["WANDB_API_KEY"] = "b9171ddb0a1638d8cca0425e41c8a9d789281515"
os.environ["WANDB_MODE"] = "online"

wandb.login(key="b9171ddb0a1638d8cca0425e41c8a9d789281515")
def main():
    args = create_argparser().parse_args()
    device = args.device

    try:
        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_size

        train_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 = 0

        for 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 = 0
                with 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_rate

                wandb.log({
                    "test_loss": test_loss,
                    "train_loss": acc_train_loss,
                    "samples": [wandb.Image(sample) for sample in samples],
                })

                acc_train_loss = 0

            if 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 parser


if __name__ == "__main__":
    main()

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

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

相关文章

语音转文字在线免费有什么工具?这4款工具让记录更高效

在当今职场、学术界和内容创作领域&#xff0c;人们越来越需要一种高效的方式来整理会议记录、讲座内容或采访对话。 幸运的是&#xff0c;除了传统的手动记笔记方式&#xff0c;我们还可以通过录音转文字软件来实现这一目标。这些软件能够直接将音频资料转写为文本&#xff0…

4nm点状激光模组的应用让未来科技走向潮流

在科技发展时代&#xff0c;激光技术以其高精度、高效率的特性&#xff0c;正逐步成为众多行业不可或缺的核心技术之一。其中&#xff0c;4nm点状激光模组作为激光技术领域的佼佼者&#xff0c;凭借其卓越的性能和广泛的应用前景&#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…

Apollo:源码分析之cyber/mainboard启动入口介绍-my write, test ok

软件结构图 cyber入口 cyber的入口在"cyber/mainboard"目录中: ├── mainboard.cc // 主函数 ├── module_argument.cc // 模块输入参数 ├── module_argument.h ├── module_controller.cc // 模块加载,卸载 └── module_controller.…

Feature Corrective Transfer Learning (2024CVPR)

Feature Corrective Transfer learning Framework &#xff08;特征矫正迁移学习框架&#xff09; 旨在引导非理想图像上的模型训练与理想图像上训练的模型的特中层更紧密地对齐 Model Selection and Training on Ideal Images 首先在理想图像上训练&#xff0c;得到理想参数…

NV170D语音芯片:为洗地扫地一体机带来新体验!

随着物联网、人工智能技术的飞速发展&#xff0c;家用电器的智能化转型已成为不可逆转的趋势。在这一背景下&#xff0c;洗地扫地一体机&#xff0c;作为家务自动化的先锋&#xff0c;融合了高效清洁与便捷操作的双重优势&#xff0c;而语音芯片的应用&#xff0c;更是为其增添…

使用 nvm在linux上安装多个版本的node

使用 nvm&#xff08;Node Version Manager&#xff09;: nvm 是一个流行的 Node.js 版本管理工具&#xff0c;允许你安装和使用多个版本的 Node.js。 1、安装nvm wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash source ~/.nvm/nvm.sh2、…

javaEE(3)

目录 一. 前端浏览器保存用户信息 二. 前端路由导航守卫 三. 路由嵌套 四. web会话跟踪 1. web会话跟踪原理 2. JWT 2.1 传统的session认证 2.2 基于token的鉴权机制 2.3 jwt的构成 2.4 jwt搭建 五. 前端发送请求携带token 5.1 请求拦截器 六. 后端过滤器验证toke…

springboot合肥师范学院实习实训管理系统-计算机毕业设计源码31290

摘要 随着社会对高校毕业生的职业素养和实践能力要求不断提高&#xff0c;高校实习实训教育愈发受到重视。信息化技术的快速发展也为高校教学管理带来了新的机遇。合肥师范学院实习实训管理系统的研究就是源自当前高等教育对学生实习实训管理的需求。 实习实训管理系统充分利用…

手机录屏直播,教你3个方法,秒变录屏高手

在移动互联网飞速发展的今天&#xff0c;手机录屏直播已成为越来越多用户分享内容、交流心得的重要方式。无论是游戏高手展示高超技艺&#xff0c;还是教育从业者进行远程授课&#xff0c;手机录屏直播都能提供极大的便利。 在手机录屏的世界里&#xff0c;安卓手机和苹果手机…

深入分析 Android ContentProvider (九)

文章目录 深入分析 Android ContentProvider (九)ContentProvider 的高级使用及最佳实践&#xff08;续&#xff09;1. 复杂查询与联合查询复杂查询示例 2. 数据同步与一致性示例&#xff1a;使用事务确保数据一致性 3. 数据分页加载示例&#xff1a;分页加载数据 4. 内容提供者…

jmeter录制

1、添加代理服务器 添加方法&#xff1a;“测试计划”右键 -> 添加 -> 非测试元件 -> HTTP代理服务器 2、添加线程组 添加方法&#xff1a;“测试计划”右键->添加->线程&#xff08;用户&#xff09;->线程组 3、配置http代理服务器 &#xff08;1&a…

电脑录屏软件带声音,3款软件推荐,一键录制

在今天&#xff0c;电脑录屏软件带声音的功能已经悄然改变了我们的学习、工作和娱乐方式。录屏软件&#xff0c;这个看似简单的工具&#xff0c;实则蕴藏着无穷的魅力。它不仅能够捕捉屏幕上的每一个细节&#xff0c;还能将声音完美地融入其中。无论是游戏中的背景音乐、会议中…

谷粒商城实战笔记-77-商品服务-API-平台属性-规格参数列表

文章目录 一&#xff0c;新增product/attr/base/list接口二&#xff0c;踩坑记录1. 使用 Lazy 注解2. 使用 PostConstruct 注解代码分析解决方案分析 这一节的主要内容是完成规格参数的列表查询功能。 一&#xff0c;新增product/attr/base/list接口 这个接口用来查询规格参数…

电力巡检红外热成像夜视手持终端有多强?

电力巡检红外热成像夜视手持终端在电力巡检中展现出强大的功能和应用价值。这些手持终端结合了红外热成像技术和夜视功能&#xff0c;能够在夜间或光线不足的环境下对电力设备进行精确的温度测量和状态监测。以下是对其强大之处的详细分析&#xff1a; 1. 精准的红外热成像能力…

【机器学习】正规方程的简单介绍以及如何使用Scikit-Learn实现基于正规方程的闭式解线性回归

引言 Scikit-learn 是一个开源的机器学习库&#xff0c;它支持 Python 编程语言。它提供了多种机器学习算法的实现&#xff0c;并用于数据挖掘和数据分析 文章目录 引言一、正规方程的定义二、正规方程的原理三、使用 Scikit-Learn 实现基于正规方程的闭式解线性回归3.1 工具3.…

爬虫“拥抱大模型”,有没有搞头?

验证码坐标识别 数据采集过程中&#xff0c;可能会碰到各种风控策略。其中&#xff0c;验证码人机验证是较为常见的&#xff0c;点选类验证码需要识别出相应的坐标&#xff0c;碰到这种情况&#xff0c;一般要么自己训练模型&#xff0c;要么对接打码平台。现在也可以将识别工…

RocketMQ事务消息机制原理

RocketMQ工作流程 在RocketMQ当中&#xff0c;当消息的生产者将消息生产完成之后&#xff0c;并不会直接将生产好的消息直接投递给消费者&#xff0c;而是先将消息投递个中间的服务&#xff0c;通过这个服务来协调RocketMQ中生产者与消费者之间的消费速度。 那么生产者是如何…

领夹麦哪个牌子音质好?直播采访十大公认音质好的麦克风!

在追求内容品质的今天&#xff0c;音频质量成为了衡量作品成功与否的关键指标之一。对于频繁出镜的互联网从业者、短视频创作者及直播达人而言&#xff0c;一款性能卓越的无线领夹麦克风无疑是提升专业形象的得力助手。它不仅轻便易携&#xff0c;更能在复杂环境中捕捉纯净、清…

[QT开发_音乐播放器项目笔记01]

目录 一&#xff1a;常用类 26 QByteArray 是 Qt 框架中的一个类&#xff0c;用于处理字节数组。它提供了动态大小的字节数组&#xff0c;可以用于存储和操作二进制数据&#xff0c;比如文件内容、网络数据等。 QT项目记录&#xff1a; 一&#xff1a;常用类 26 QByteArray…