基于DDPM的PyTorch简单实现
基于DDPM的PyTorch简单实现
文章目录
- 基于DDPM的PyTorch简单实现
- 摘要
- Abstract
- 一、DDPM实现
- 1. 获取数据集
- 2. DDPM类
- 3. 训练算法
- 4. 去噪神经网络
- 5. 实验结果与采样
- 总结
摘要
本周的学习与实践围绕扩散模型(Diffusion Model)的基础理论和实现展开,重点复现了基于DDPM(Denoising Diffusion Probabilistic Models)的图像生成过程。在实践中,通过PyTorch实现了一个基于U-Net的去噪网络,并在MNIST数据集上完成了训练与采样。实验过程中,构建了DDPM类以实现正向扩散和反向采样流程,同时设计了带时间戳编码的U-Net网络以提升去噪性能。最终生成的图像在视觉上与MNIST数据集的真实图像接近,验证了模型的有效性。通过实验,进一步加深了对扩散模型核心算法及其实现细节的理解。
Abstract
This week’s focus was on the theoretical foundations and practical implementation of diffusion models, particularly Denoising Diffusion Probabilistic Models (DDPM). A U-Net-based denoising network was implemented using PyTorch and trained on the MNIST dataset to generate images. The DDPM class was designed to handle both the forward diffusion and reverse sampling processes, while the U-Net model was equipped with positional encoding to incorporate temporal information for improved denoising performance. The generated images closely resembled real MNIST samples, demonstrating the effectiveness of the model. The experiment provided deeper insights into the core algorithms of diffusion models and their implementation details.
一、DDPM实现
本次准备利用PyTorch实现一个基于U-Net的DDPM,并在MNIST数据集(经典的手写数字数据集)上训练。
1. 获取数据集
PyTorch的torchvision提供了获取了MNIST的接口,我们只需要用下面的函数就可以生成MNIST的Dataset实例。参数中,root为数据集的下载路径,download为是否自动下载数据集。令download=True的话,第一次调用该函数时会自动下载数据集,而第二次之后就不用下载了,函数会读取存储在root里的数据。
mnist = torchvision.datasets.MNIST(root='data/mnist', download=True)
我们可以用下面的代码来下载MNIST并输出该数据集的一些信息:
import torchvision
from torchvision.transforms import ToTensor
def download_dataset():mnist = torchvision.datasets.MNIST(root='data/mnist', download=True)print('length of MNIST', len(mnist))id = 4img, label = mnist[id]print(img)print(label)# On computer with monitor# img.show()img.save('work_dirs/tmp.jpg')tensor = ToTensor()(img)print(tensor.shape)print(tensor.max())print(tensor.min())if __name__ == '__main__':download_dataset()
执行这段代码,输出大致为:
length of MNIST 60000
<PIL.Image.Image image mode=L size=28x28 at 0x7FB3F09CCE50>
9
torch.Size([1, 28, 28])
tensor(1.)
tensor(0.)
第一行输出表明,MNIST数据集里有60000张图片。而从第二行和第三行输出中,我们发现每一项数据由图片和标签组成,图片是大小为28x28
的PIL格式的图片,标签表明该图片是哪个数字。我们可以用torchvision
里的ToTensor()
把PIL图片转成PyTorch张量,进一步查看图片的信息。最后三行输出表明,每一张图片都是单通道图片(灰度图),颜色值的取值范围是0~1。
我们可以查看一下每张图片的样子。如果你是在用带显示器的电脑,可以去掉img.show
那一行的注释,直接查看图片;如果你是在用服务器,可以去img.save
的路径里查看图片。该图片的应该长这个样子:
我们可以用下面的代码预处理数据并创建DataLoader
。由于DDPM会把图像和正态分布关联起来,我们更希望图像颜色值的取值范围是[-1, 1]
。为此,我们可以对图像做一个线性变换,减0.5再乘2。
def get_dataloader(batch_size: int):transform = Compose([ToTensor(), Lambda(lambda x: (x - 0.5) * 2)])dataset = torchvision.datasets.MNIST(root='data/mnist',transform=transform)return DataLoader(dataset, batch_size=batch_size, shuffle=True)
2. DDPM类
在代码中,我们要实现一个DDPM
类。它维护了扩散过程中的一些常量(比如 α \alpha α),并且可以计算正向过程和反向过程的结果。
先来实现一下DDPM
类的初始化函数。一开始,我们遵从论文的配置,用torch.linspace(min_beta, max_beta, n_steps)
从min_beta
到max_beta
线性地生成n_steps
个时刻的 β \beta β。接着,我们根据公式 α t = 1 − β t {\alpha _t} = 1 - {\beta _t} αt=1−βt, α ˉ t = ∏ i = 1 t α i {{\bar \alpha }_t} = \prod _{i = 1}^t{\alpha _i} αˉt=∏i=1tαi,计算每个时刻的alpha
和alpha_bar
。注意,为了方便实现,我们让t
的取值从0开始,要比论文里的t少1。
import torchclass DDPM():# n_steps 就是论文里的 Tdef __init__(self,device,n_steps: int,min_beta: float = 0.0001,max_beta: float = 0.02):betas = torch.linspace(min_beta, max_beta, n_steps).to(device)alphas = 1 - betasalpha_bars = torch.empty_like(alphas)product = 1for i, alpha in enumerate(alphas):product *= alphaalpha_bars[i] = productself.betas = betasself.n_steps = n_stepsself.alphas = alphasself.alpha_bars = alpha_bars
部分实现会让 DDPM 继承
torch.nn.Module
,但我认为这样不好。DDPM本身不是一个神经网络,它只是描述了前向过程和后向过程的一些计算。只有涉及可学习参数的神经网络类才应该继承torch.nn.Module
。
准备好了变量后,我们可以来实现DDPM
类的其他方法。先实现正向过程方法,该方法会根据公式 x t = α ˉ t x 0 + 1 − α ˉ t ϵ t {x_t} = \sqrt {{{\bar \alpha }_t}} {x_0} + \sqrt {1 - {{\bar \alpha }_t}} {\epsilon _t} xt=αˉtx0+1−αˉtϵt计算正向过程中的 x t {x_t} xt。
def sample_forward(self, x, t, eps=None):alpha_bar = self.alpha_bars[t].reshape(-1, 1, 1, 1)if eps is None:eps = torch.randn_like(x)res = eps * torch.sqrt(1 - alpha_bar) + torch.sqrt(alpha_bar) * xreturn res
这里要解释一些PyTorch编程上的细节。这份代码中,self.alpha_bars
是一个一维Tensor
。而在并行训练中,我们一般会令t
为一个形状为(batch_size, )
的Tensor
。PyTorch允许我们直接用self.alpha_bars[t]
从self.alpha_bars
里取出batch_size
个数,就像用一个普通的整型索引来从数组中取出一个数一样。有些实现会用torch.gather
从self.alpha_bars
里取数,其作用是一样的。
我们可以随机从训练集取图片做测试,看看它们在前向过程中是怎么逐步变成噪声的。
接下来实现反向过程。在反向过程中,DDPM会用神经网络预测每一轮去噪的均值,把 x t {x_t} xt复原回 x 0 {x_0} x0,以完成图像生成。反向过程即对应论文中的采样算法。
其实现如下:
def sample_backward(self, img_shape, net, device, simple_var=True):x = torch.randn(img_shape).to(device)net = net.to(device)for t in range(self.n_steps - 1, -1, -1):x = self.sample_backward_step(x, t, net, simple_var)return xdef sample_backward_step(self, x_t, t, net, simple_var=True):n = x_t.shape[0]t_tensor = torch.tensor([t] * n,dtype=torch.long).to(x_t.device).unsqueeze(1)eps = net(x_t, t_tensor)if t == 0:noise = 0else:if simple_var:var = self.betas[t]else:var = (1 - self.alpha_bars[t - 1]) / (1 - self.alpha_bars[t]) * self.betas[t]noise = torch.randn_like(x_t)noise *= torch.sqrt(var)mean = (x_t -(1 - self.alphas[t]) / torch.sqrt(1 - self.alpha_bars[t]) *eps) / torch.sqrt(self.alphas[t])x_t = mean + noisereturn x_t
其中,sample_backward
是用来给外部调用的方法,而sample_backward_step
是执行一步反向过程的方法。
sample_backward
会随机生成纯噪声x
(对应 x t {x_t} xt),再令t
从n_steps - 1
到0
,调用sample_backward_step
。
def sample_backward(self, img_shape, net, device, simple_var=True):x = torch.randn(img_shape).to(device)net = net.to(device)for t in range(self.n_steps - 1, -1, -1):x = self.sample_backward_step(x, t, net, simple_var)return x
在sample_backward_step
中,我们先准备好这一步的神经网络输出eps
。为此,我们要把整型的t
转换成一个格式正确的Tensor
。考虑到输入里可能有多个batch,我们先获取batch size n
,再根据它来生成t_tensor
。
def sample_backward_step(self, x_t, t, net, simple_var=True):n = x_t.shape[0]t_tensor = torch.tensor([t] * n,dtype=torch.long).to(x_t.device).unsqueeze(1)eps = net(x_t, t_tensor)
之后,我们来处理反向过程公式中的方差项。根据伪代码,我们仅在t
非零的时候算方差项。方差项用到的方差有两种取值,效果差不多,我们用simple_var
来控制选哪种取值方式。获取方差后,我们再随机采样一个噪声,根据公式,得到方差项。
if t == 0:noise = 0
else:if simple_var:var = self.betas[t]else:var = (1 - self.alpha_bars[t - 1]) / (1 - self.alpha_bars[t]) * self.betas[t]noise = torch.randn_like(x_t)noise *= torch.sqrt(var)
最后,我们把eps
和方差项套入公式,得到这一步更新过后的图像x_t
。
mean = (x_t -(1 - self.alphas[t]) / torch.sqrt(1 - self.alpha_bars[t]) *eps) / torch.sqrt(self.alphas[t])
x_t = mean + noisereturn x_t
稍后完成了训练后,我们再来看反向过程的输出结果。
3. 训练算法
接下来,我们先跳过神经网络的实现,直接完成论文里的训练算法。
再回顾一遍伪代码。首先,我们要随机选取训练图片 x 0 {x_0} x0,随机生成当前要训练的时刻t,以及随机生成一个生成 x t {x_t} xt的高斯噪声。之后,我们把 x t {x_t} xt和 t t t输入进神经网络,尝试预测噪声。最后,我们以预测噪声和实际噪声的均方误差为损失函数做梯度下降。
为此,我们可以用下面的代码实现训练。
import torch
import torch.nn as nn
from dldemos.ddpm.dataset import get_dataloader, get_img_shape
from dldemos.ddpm.ddpm import DDPM
import cv2
import numpy as np
import einopsbatch_size = 512
n_epochs = 100def train(ddpm: DDPM, net, device, ckpt_path):# n_steps 就是公式里的 T# net 是某个继承自 torch.nn.Module 的神经网络n_steps = ddpm.n_stepsdataloader = get_dataloader(batch_size)net = net.to(device)loss_fn = nn.MSELoss()optimizer = torch.optim.Adam(net.parameters(), 1e-3)for e in range(n_epochs):for x, _ in dataloader:current_batch_size = x.shape[0]x = x.to(device)t = torch.randint(0, n_steps, (current_batch_size, )).to(device)eps = torch.randn_like(x).to(device)x_t = ddpm.sample_forward(x, t, eps)eps_theta = net(x_t, t.reshape(current_batch_size, 1))loss = loss_fn(eps_theta, eps)optimizer.zero_grad()loss.backward()optimizer.step()torch.save(net.state_dict(), ckpt_path)
代码的主要逻辑都在循环里。首先是完成训练数据 x 0 {x_0} x0、 t t t、噪声的采样。采样 x 0 {x_0} x0的工作可以交给PyTorch的DataLoader完成,每轮遍历得到的x
就是训练数据。t的采样可以用torch.randint
函数随机从[0, n_steps - 1]
取数。采样高斯噪声可以直接用torch.randn_like(x)
生成一个和训练图片x
形状一样的符合标准正态分布的图像。
for x, _ in dataloader:current_batch_size = x.shape[0]x = x.to(device)t = torch.randint(0, n_steps, (current_batch_size, )).to(device)eps = torch.randn_like(x).to(device)
之后计算 x t {x_t} xt并将其和t输入进神经网络net
。计算 x t {x_t} xt的任务会由DDPM
类的sample_forward
方法完成,我们在上文已经实现了它。
x_t = ddpm.sample_forward(x, t, eps)
eps_theta = net(x_t, t.reshape(current_batch_size, 1))
得到了预测的噪声eps_theta
,我们调用PyTorch的API,算均方误差并调用优化器即可。
loss_fn = nn.MSELoss()
optimizer = torch.optim.Adam(net.parameters(), 1e-3)...loss = loss_fn(eps_theta, eps)optimizer.zero_grad()loss.backward()optimizer.step()
4. 去噪神经网络
在DDPM中,理论上我们可以用任意一种神经网络架构。但由于DDPM任务十分接近图像去噪任务,而U-Net又是去噪任务中最常见的网络架构,因此绝大多数DDPM都会使用基于U-Net的神经网络。
有人经过多次实验,发现DDPM的神经网络很难训练。哪怕是对于比较简单的MNIST数据集,结构差一点的网络(比如纯ResNet)都不太行,只有带了残差块和时序编码的U-Net才能较好地完成去噪。注意力模块倒是可以不用加上。
由于神经网络结构并不是DDPM学习的重点,我这里就不对U-Net的写法做解说,而是直接贴上代码了。代码中大部分内容都和普通的U-Net无异。唯一要注意的地方就是时序编码。去噪网络的输入除了图像外,还有一个时间戳t
。我们要考虑怎么把t
的信息和输入图像信息融合起来。大部分人的做法是对t
进行Transformer中的位置编码,把该编码加到图像的每一处上。
import torch
import torch.nn as nn
import torch.nn.functional as F
from dldemos.ddpm.dataset import get_img_shapeclass PositionalEncoding(nn.Module):def __init__(self, max_seq_len: int, d_model: int):super().__init__()# Assume d_model is an even number for convenienceassert d_model % 2 == 0pe = torch.zeros(max_seq_len, d_model)i_seq = torch.linspace(0, max_seq_len - 1, max_seq_len)j_seq = torch.linspace(0, d_model - 2, d_model // 2)pos, two_i = torch.meshgrid(i_seq, j_seq)pe_2i = torch.sin(pos / 10000**(two_i / d_model))pe_2i_1 = torch.cos(pos / 10000**(two_i / d_model))pe = torch.stack((pe_2i, pe_2i_1), 2).reshape(max_seq_len, d_model)self.embedding = nn.Embedding(max_seq_len, d_model)self.embedding.weight.data = peself.embedding.requires_grad_(False)def forward(self, t):return self.embedding(t)class ResidualBlock(nn.Module):def __init__(self, in_c: int, out_c: int):super().__init__()self.conv1 = nn.Conv2d(in_c, out_c, 3, 1, 1)self.bn1 = nn.BatchNorm2d(out_c)self.actvation1 = nn.ReLU()self.conv2 = nn.Conv2d(out_c, out_c, 3, 1, 1)self.bn2 = nn.BatchNorm2d(out_c)self.actvation2 = nn.ReLU()if in_c != out_c:self.shortcut = nn.Sequential(nn.Conv2d(in_c, out_c, 1),nn.BatchNorm2d(out_c))else:self.shortcut = nn.Identity()def forward(self, input):x = self.conv1(input)x = self.bn1(x)x = self.actvation1(x)x = self.conv2(x)x = self.bn2(x)x += self.shortcut(input)x = self.actvation2(x)return xclass ConvNet(nn.Module):def __init__(self,n_steps,intermediate_channels=[10, 20, 40],pe_dim=10,insert_t_to_all_layers=False):super().__init__()C, H, W = get_img_shape() # 1, 28, 28self.pe = PositionalEncoding(n_steps, pe_dim)self.pe_linears = nn.ModuleList()self.all_t = insert_t_to_all_layersif not insert_t_to_all_layers:self.pe_linears.append(nn.Linear(pe_dim, C))self.residual_blocks = nn.ModuleList()prev_channel = Cfor channel in intermediate_channels:self.residual_blocks.append(ResidualBlock(prev_channel, channel))if insert_t_to_all_layers:self.pe_linears.append(nn.Linear(pe_dim, prev_channel))else:self.pe_linears.append(None)prev_channel = channelself.output_layer = nn.Conv2d(prev_channel, C, 3, 1, 1)def forward(self, x, t):n = t.shape[0]t = self.pe(t)for m_x, m_t in zip(self.residual_blocks, self.pe_linears):if m_t is not None:pe = m_t(t).reshape(n, -1, 1, 1)x = x + pex = m_x(x)x = self.output_layer(x)return xclass UnetBlock(nn.Module):def __init__(self, shape, in_c, out_c, residual=False):super().__init__()self.ln = nn.LayerNorm(shape)self.conv1 = nn.Conv2d(in_c, out_c, 3, 1, 1)self.conv2 = nn.Conv2d(out_c, out_c, 3, 1, 1)self.activation = nn.ReLU()self.residual = residualif residual:if in_c == out_c:self.residual_conv = nn.Identity()else:self.residual_conv = nn.Conv2d(in_c, out_c, 1)def forward(self, x):out = self.ln(x)out = self.conv1(out)out = self.activation(out)out = self.conv2(out)if self.residual:out += self.residual_conv(x)out = self.activation(out)return outclass UNet(nn.Module):def __init__(self,n_steps,channels=[10, 20, 40, 80],pe_dim=10,residual=False) -> None:super().__init__()C, H, W = get_img_shape()layers = len(channels)Hs = [H]Ws = [W]cH = HcW = Wfor _ in range(layers - 1):cH //= 2cW //= 2Hs.append(cH)Ws.append(cW)self.pe = PositionalEncoding(n_steps, pe_dim)self.encoders = nn.ModuleList()self.decoders = nn.ModuleList()self.pe_linears_en = nn.ModuleList()self.pe_linears_de = nn.ModuleList()self.downs = nn.ModuleList()self.ups = nn.ModuleList()prev_channel = Cfor channel, cH, cW in zip(channels[0:-1], Hs[0:-1], Ws[0:-1]):self.pe_linears_en.append(nn.Sequential(nn.Linear(pe_dim, prev_channel), nn.ReLU(),nn.Linear(prev_channel, prev_channel)))self.encoders.append(nn.Sequential(UnetBlock((prev_channel, cH, cW),prev_channel,channel,residual=residual),UnetBlock((channel, cH, cW),channel,channel,residual=residual)))self.downs.append(nn.Conv2d(channel, channel, 2, 2))prev_channel = channelself.pe_mid = nn.Linear(pe_dim, prev_channel)channel = channels[-1]self.mid = nn.Sequential(UnetBlock((prev_channel, Hs[-1], Ws[-1]),prev_channel,channel,residual=residual),UnetBlock((channel, Hs[-1], Ws[-1]),channel,channel,residual=residual),)prev_channel = channelfor channel, cH, cW in zip(channels[-2::-1], Hs[-2::-1], Ws[-2::-1]):self.pe_linears_de.append(nn.Linear(pe_dim, prev_channel))self.ups.append(nn.ConvTranspose2d(prev_channel, channel, 2, 2))self.decoders.append(nn.Sequential(UnetBlock((channel * 2, cH, cW),channel * 2,channel,residual=residual),UnetBlock((channel, cH, cW),channel,channel,residual=residual)))prev_channel = channelself.conv_out = nn.Conv2d(prev_channel, C, 3, 1, 1)def forward(self, x, t):n = t.shape[0]t = self.pe(t)encoder_outs = []for pe_linear, encoder, down in zip(self.pe_linears_en, self.encoders,self.downs):pe = pe_linear(t).reshape(n, -1, 1, 1)x = encoder(x + pe)encoder_outs.append(x)x = down(x)pe = self.pe_mid(t).reshape(n, -1, 1, 1)x = self.mid(x + pe)for pe_linear, decoder, up, encoder_out in zip(self.pe_linears_de,self.decoders, self.ups,encoder_outs[::-1]):pe = pe_linear(t).reshape(n, -1, 1, 1)x = up(x)pad_x = encoder_out.shape[2] - x.shape[2]pad_y = encoder_out.shape[3] - x.shape[3]x = F.pad(x, (pad_x // 2, pad_x - pad_x // 2, pad_y // 2,pad_y - pad_y // 2))x = torch.cat((encoder_out, x), dim=1)x = decoder(x + pe)x = self.conv_out(x)return xconvnet_small_cfg = {'type': 'ConvNet','intermediate_channels': [10, 20],'pe_dim': 128
}convnet_medium_cfg = {'type': 'ConvNet','intermediate_channels': [10, 10, 20, 20, 40, 40, 80, 80],'pe_dim': 256,'insert_t_to_all_layers': True
}
convnet_big_cfg = {'type': 'ConvNet','intermediate_channels': [20, 20, 40, 40, 80, 80, 160, 160],'pe_dim': 256,'insert_t_to_all_layers': True
}unet_1_cfg = {'type': 'UNet', 'channels': [10, 20, 40, 80], 'pe_dim': 128}
unet_res_cfg = {'type': 'UNet','channels': [10, 20, 40, 80],'pe_dim': 128,'residual': True
}def build_network(config: dict, n_steps):network_type = config.pop('type')if network_type == 'ConvNet':network_cls = ConvNetelif network_type == 'UNet':network_cls = UNetnetwork = network_cls(n_steps, **config)return network
5. 实验结果与采样
把之前的所有代码综合一下,我们以带残差块的U-Net为去噪网络,执行训练。
if __name__ == '__main__':n_steps = 1000config_id = 4device = 'cuda'model_path = 'dldemos/ddpm/model_unet_res.pth'config = unet_res_cfgnet = build_network(config, n_steps)ddpm = DDPM(device, n_steps)train(ddpm, net, device=device, ckpt_path=model_path)
按照默认训练配置,在3090上花5分钟不到,训练30~40个epoch即可让网络基本收敛。最终收敛时loss在0.023~0.024左右。
batch size: 512
epoch 0 loss: 0.23103461712201437 elapsed 7.01s
epoch 1 loss: 0.0627968365987142 elapsed 13.66s
epoch 2 loss: 0.04828845852613449 elapsed 20.25s
epoch 3 loss: 0.04148937337398529 elapsed 26.80s
epoch 4 loss: 0.03801360730528831 elapsed 33.37s
epoch 5 loss: 0.03604260584712028 elapsed 39.96s
epoch 6 loss: 0.03357676289876302 elapsed 46.57s
epoch 7 loss: 0.0335664684087038 elapsed 53.15s
...
epoch 30 loss: 0.026149748386939366 elapsed 204.64s
epoch 31 loss: 0.025854381563266117 elapsed 211.24s
epoch 32 loss: 0.02589433005253474 elapsed 217.84s
epoch 33 loss: 0.026276464049021404 elapsed 224.41s
...
epoch 96 loss: 0.023299352884292603 elapsed 640.25s
epoch 97 loss: 0.023460942271351815 elapsed 646.90s
epoch 98 loss: 0.023584651704629263 elapsed 653.54s
epoch 99 loss: 0.02364126600921154 elapsed 660.22s
训练这个网络时,并没有特别好的测试指标,我们只能通过观察采样图像来评价网络的表现。我们可以用下面的代码调用DDPM的反向传播方法,生成多幅图像并保存下来。
def sample_imgs(ddpm,net,output_path,n_sample=81,device='cuda',simple_var=True):net = net.to(device)net = net.eval()with torch.no_grad():shape = (n_sample, *get_img_shape()) # 1, 3, 28, 28imgs = ddpm.sample_backward(shape,net,device=device,simple_var=simple_var).detach().cpu()imgs = (imgs + 1) / 2 * 255imgs = imgs.clamp(0, 255)imgs = einops.rearrange(imgs,'(b1 b2) c h w -> (b1 h) (b2 w) c',b1=int(n_sample**0.5))imgs = imgs.numpy().astype(np.uint8)cv2.imwrite(output_path, imgs)
一切顺利的话,我们可以得到一些不错的生成结果。下图是我得到的一些生成图片:
大部分生成的图片都对应一个阿拉伯数字,它们和训练集MNIST里的图片非常接近。这算是一个不错的生成结果。
如果神经网络的拟合能力较弱,生成结果就会差很多。下图是我训练一个简单的ResNet后得到的采样结果:
可以看出,每幅图片都很乱,基本对应不上一个数字。这就是一个较差的训练结果。
如果网络再差一点,可能会生成纯黑或者纯白的图片。这是因为网络的预测结果不准,在反向过程中,图像的均值不断偏移,偏移到远大于1或者远小于-1的值了。
总结
在复现DDPM时,最主要是要学习DDPM论文的两个算法,即训练算法和采样算法。两个算法很简单,可以轻松地把它们翻译成代码。而为了成功完成复现,还需要花一点心思在编写U-Net上,尤其是注意处理时间戳的部分。
相关文章:
基于DDPM的PyTorch简单实现
基于DDPM的PyTorch简单实现 文章目录 基于DDPM的PyTorch简单实现摘要Abstract一、DDPM实现1. 获取数据集2. DDPM类3. 训练算法4. 去噪神经网络5. 实验结果与采样 总结 摘要 本周的学习与实践围绕扩散模型(Diffusion Model)的基础理论和实现展开…...
php laravel 学习管理系统(LMS)
Lernen LMS(学习管理系统)是一个综合性的在线教育平台,旨在为学生和导师提供灵活、高效、便捷的学习体验。该系统不仅帮助学生找到最适合自己的导师,还通过一系列强大的功能,提升了课程安排、学习管理和师生互动的效率…...
【JAVA】Java高级:数据库监控与调优:SQL调优与执行计划的分析
作为Java开发工程师,理解SQL调优和执行计划的分析是至关重要的。这不仅可以帮助我们提高数据库查询的效率,还能减少系统资源的消耗,提升整体应用的性能。 1. SQL调优的重要性 随着数据量的增加和用户请求的增多,数据库的性能问题…...
centos9升级OpenSSH
需求 Centos9系统升级OpenSSH和OpenSSL OpenSSH升级为openssh-9.8p1 OpenSSL默认为OpenSSL-3.2.2(根据需求进行升级) 将源码包编译为rpm包 查看OpenSSH和OpenSSL版本 ssh -V下载源码包并上传到服务器 openssh最新版本下载地址 wget https://cdn.openb…...
jeccg-boot修改密码
最近在使用jeccg-boot框架,遇到一个需要批量修改用户密码的问题 由于框架使用的是加密盐算法生成的密码 ,无法直接通过数据库修改密码 例如将password字段和salt值复制过去,密码是不对的 查看代码发现通过user.getUsername(), user.getPasswo…...
linux 生成 nginx 的https ssl 证书详解
证书生成 1. 生成证书 会提示输入密码,输入两次相同密码即可。 openssl genrsa -des3 -out server.key 20482. 去除密码校验 如果想去除此输密码的步骤,可以执行如下命令,根据使用需求选择。 openssl rsa -in server.key -out server.ke…...
详细介绍vue的递归组件(重要)
递归组件在 Vue 中是一个非常强大的概念,尤其在渲染层级结构(如树形结构、嵌套列表、评论系统等)时,能够极大地简化代码。 什么是递归组件? 递归组件就是一个组件在其模板中引用自身。这种做法通常用于渲染树形结构或…...
gitlab配置调试minio
官方文档 rails console 调试 查看配置Settings.uploads.object_store加载minio clientrequire fog/awsfog_connection Fog::Storage.new(provider: AWS,aws_access_key_id: 你的MINIO_ACCESS_KEY,aws_secret_access_key: 你的MINIO_SECRET_KEY,region: <S3 region>,e…...
Docker(Nginx) 部署 uniapp
目录 一、准备工作 1.Docker安装nginx 2.安装HBuild X工具 二、HBuild X打包项目 1.在HBuild X导入项目 2.配置manifest.json 3.打包 (1)点击发行 (2)填写信息,点击发行 三、nginx部署uniapp 1.生成文件上传…...
Camp4-L2:LMDeploy 量化部署进阶实践
书生浦语大模型实战营第四期:LMDeploy 量化部署进阶实践 教程链接:https://github.com/InternLM/Tutorial/tree/camp4/docs/L2/LMDeploy视频链接:https://www.bilibili.com/video/BV18aUHY3EEG/?vd_sourceb96c7e6e6d1a48e73edafa36a36f1697…...
第二十四周学习周报
目录 摘要Abstract1. 文献阅读1.1 RNN1.2 Deep Recurrent Neural Networks1.3 实验1.4 讨论 2. AI虚拟主播生成总结 摘要 本周的主要任务是阅读了一篇关于循环神经网络的论文,该论文旨在探索将RNN扩展到深度RNN的不同方法。论文通过对RNN结构的理解和分析ÿ…...
深入解析 Android PMS —— APK 安装与解析全流程
文章目录 前言1. PMS 的初始化1.1 SystemServer 启动 PMS1.2 PMS 的入口方法 main1.3 PMS 构造函数1.4 扫描 APK 文件1.5 权限初始化1.6 提供对外服务 2. APK 安装机制2.1. 安装请求的触发2.2 APK 文件解析与验证2.3 签名校验2.4 权限管理2.4.1 权限声明2.4.2 权限校验与分配 2…...
RL仿真库pybullet
1. 介绍 PyBullet是一个基于Bullet Physics引擎的物理仿真Python接口,主要用于机器人仿真模拟。 1.1 主要特点 提供大量预设的机器人模型,例如URDF(统一机器人描述格式)、SDF、MJCF 格式。适用于训练和评估强化学习算法,提供了大量的强化学…...
Vue3组件通信(父传子,子传父,跨组件通信)
本文主要是讲述Vue3在setup语法糖下的组件间通信 Vue组件通信是指在Vue.js中,不同组件之间进行信息交流和共享数据的过程。在前端开发中,组件通信是非常重要的一部分,因为在一个复杂的应用中,不同的组件通常需要相互协作ÿ…...
从失败中学习:如何将错误转化为学习机会
失败是人生的一部分,无论是在个人生活还是职业生涯中,我们都难免会遇到挫折和错误。然而,失败并不意味着终结,而是一个潜在的学习机会。通过正确的态度和方法,我们可以从失败中汲取经验,转化为成长的动力。…...
[0629].第29节:配置中心业务规则与动态刷新
我的后端学习大纲 SpringCloud学习大纲 1、编码实现3377服务: 1.1.建module: 1.2.改pom: 1.3.写YML: 1.Nacos同Consul一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正…...
JUC并发编程
进程:系统中正在运行的一个应用程序,程序一旦运行就是进程,是资源分配的最小单元。 线程:系统分配处理器时间资源的基本单元,进程之内独立执行的一个单元执行流,是程序执行的最小单位。 Lock 需要手动上…...
贪心算法解题方法介绍+实操案例——会场安排与月饼售卖问题解析
从贪心算法到实操案例——会场安排与月饼售卖问题解析 前言 贪心算法是一种通过选择局部最优解来尝试构建全局最优解的算法。它简单高效,适用于许多优化问题。本文将详细介绍贪心算法的一般解题步骤,并通过两个实例——月饼售卖问题和会场安排问题——…...
ASP.NET Core API 前后端分离跨域
环境准备 数据库: sqlserver 2022 后端: vs2022 ASP.NET Core API .net 8 前端: Hbuilderx bootstrap 5.3.0 jquery v3.7.1 bootstrap-table 1.23.5 完整项目代码下载地址 功能 实现 单张表 的 增 删 改 查 创建数据库和表 create data…...
用Python绘制医学热图
在医学研究和临床实践中,数据的可视化是不可或缺的一部分。通过直观的数据展示,医学专业人员可以更好地理解各种疾病的治愈率、治疗效果以及医院之间的差异。今天,我们将介绍一种强大的数据可视化工具——热图(Heatmap)…...
使用 Spring Boot 和 GraalVM 的原生镜像
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:历代文学,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计…...
Flutter解压文件并解析数据
Flutter解压文件并解析数据 前言 在 Flutter 开发中,我们经常需要处理文件的读取和解压。 这在处理应用数据更新、安装包、存档文件等场景中尤为常见。 本文将介绍如何在Flutter中使用archive插件来解压文件并解析数据。 准备 在开始之前,我们需要…...
深入理解 JavaScript 引擎与消息队列的底层原理
深入理解 JavaScript 引擎与消息队列的底层原理 JavaScript 是现代 Web 开发中最为重要的编程语言之一,它的运行和执行方式常常是开发者关注的重点。为了更好地理解 JavaScript 的执行过程,我们需要深入探索 JavaScript 引擎的工作原理,尤其…...
使用 ANSYS Forming 和 LS-DYNA 进行金属成形仿真简介
了解金属成型 金属成型是制造业中的关键过程,其中原材料通过变形转化为所需的形状。这可能包括冲压、弯曲和深拉等操作。这些工艺的质量和效率在很大程度上取决于对各种参数的精确控制,例如材料特性、工具几何形状和加工条件。为了优化这些参数并确保成功…...
001-mysql安装
[rootcentos701 ~]# hostname -I 10.0.0.200 172.17.0.1 [rootcentos701 ~]# hostname centos701 [rootcentos701 ~]# rpm -qa | grep mariadb [rootcentos701 ~]# rpm -e --nodeps mariadb-libs-5.5.65-1.el7.x86_64 [rootcentos701 ~]# useradd mysql -s /sbin/nologin #创建…...
以攻击者的视角进行软件安全防护
1. 前言 孙子曰:知彼知己者,百战不殆;不知彼而知己,一胜一负,不知彼,不知己,每战必殆。 摘自《 孙子兵法谋攻篇 》在2500 年前的那个波澜壮阔的春秋战国时代,孙子兵法的这段话&…...
Go 语言性能优化全解析
在当今的软件开发环境中,Go 语言(Golang)因其简洁的语法、高效的并发模型和快速的编译速度而备受青睐。然而,随着应用程序复杂性的增加,即使是在 Go 中也可能会遇到性能瓶颈。为了帮助开发者构建高性能的应用程序&…...
《智能体雏形开发(高阶实操)》二、智能体雏形开发
基于阿里云百炼平台开发智能体应用:生成日报与周报 在智能体开发中,生成结构化的日报与周报是一个典型的任务。本篇文章将基于阿里云百炼平台,结合 Python 开发环境,介绍如何开发一个从日志文件提取信息并生成摘要的智能体。我们将从需求分析、任务设计到核心功能实现逐步…...
【k8s】kubelet 和 API Server的关系
文章目录 概述1. # kubelet 和 API Server 之间的关系**1. 角色和功能****1.1 kubelet****1.2 API Server** **2. 交互关系****2.1 kubelet 从 API Server 获取指令****2.2 kubelet 向 API Server 上报状态****2.3 kubelet 与 API Server 的认证和授权** **3. 典型交互场景****…...
POSTGRESQL跟ORACLE语法区别和相同之处
跟ORACLE语法区别之处 1. Update和delete语法区别 Pg 和MySQL Update和delete的时候表名不能加别名 2. 插入数字类型不一样 ORACLE 对number类型的数据可以用’’ 字符串标记插入,但是PG不行,必须要进行正确的数据类型 3. SEQ使用不同 ORACEL的SEQ…...
Distance in Tree 树形dp练习(树中两点距离为k的数量板子)
Distance in Tree 题面翻译 题目大意 输入点数为 N N N一棵树 求树上长度恰好为 K K K的路径个数 输入格式 第一行两个数字 N , K N,K N,K,如题意 接下来的 N − 1 N-1 N−1行中,每行两个整数 u , v u,v u,v表示一条树边 ( u , v ) (u,v) (u,v) 输出格式 一个整数 a n…...
【MySQL】库的操作+表的操作
库的操作表的操作 1.库的操作 1.1创建数据库1.2删除数据库1.3查找数据库1.4修改数据库1.5数据库备份和恢复1.6查看连接情况 2.库的操作 2.1创建表2.2查看表结构2.3修改表2.4删除表 点赞???收藏???关注??? 你的支持是对我最大的鼓励,我们一起努力吧???…...
vue异步更新,$nextTick
如果将isShowEdit改为true,就会显示输入框和确认按钮、 如果isShowEdit为false的话就显示“大标题”和编辑 想要获取元素焦点,但是vue是异步更新,会出错显示this.$refs.inp是undefined,是因为input元素并没有更新完成,所以需要使用…...
【3D AIGC】Img-to-3D、Text-to-3D、稀疏重建(2024年文章汇总)
文章目录 1. Wonderworld:拓展图片边界,生成3D场景2. 3DTopia-XL:扩散模型辅助生成3. 3DGS-Enhancer: 通过视图一致2D Diffusion,提升无界3D Gaussian Splatting (NlPs2024 Spotlight)4. L3DG:Latent 3D Gaussian Diff…...
简单的springboot使用sse功能
什么是sse? 1、SSE 是Server-Sent Events(服务器发送事件) 2、SSE是一种允许服务器主动向客户端推送实时更新的技术。 3、它基于HTTP协议,并使用了其长连接特性,在客户端与服务器之间建立一条持久化的连接。 通过这条连接&am…...
Nginx 防止IP伪造,绕过IP限制
背景介绍 在使用Nginx时,需要将IP地址转发到后置应用中,往往需要增加配置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 在后端程序通过读取请求头里的X-Forwarded-For来获取用户客户端IP。 public String getRemortIP(HttpServle…...
原生js仿el-table动态表头
解决动态表头数据量过大导致页面卡顿的问题解决固定前几列导致表头设置宽度失效或者错位的问题功能: 固定前几列合并指定单元格 <div class"tableJoint2"><div><table id"tableData"></table></div><div>…...
【opencv入门教程】9.视频加载
文章选自: 一、VideoCapture类 用于从视频文件、图像序列或摄像头捕获视频的类。函数:CV_WRAP VideoCapture();brief 默认构造函数CV_WRAP explicit VideoCapture(const String& filename, int apiPreference CAP_ANY);brief 使用 API 首选项打开…...
数据结构 ——无头单链表
数据结构 ——无头单链表 一、无头单链表的定义与特性 1、单链表简介 单链表是一种常见的基础数据结构,它由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。无头单链表是单链表的一种变体,其特点是没有明确的头节点࿰…...
【UE5】制作插件 并调试【vs2022】
视频教程:好看视频-轻松有收获 https://www.youtube.com/watch?vIjpa9mI2b5I 原文:【UE】制作插件_ue插件-CSDN博客 C制作插件 1. 我们可以在C工程中创建更多类型的插件,这里我们选择“空白”作为模板来创建插件 点击“创建插件”按钮后…...
Prometheus 采集postgresql监控数据
postgres_exporter 前言 postgres_exporter 是一个用于监控 PostgreSQL 数据库的 Prometheus 导出器。它允许你收集有关 PostgreSQL 数据库性能和状态的指标,并将这些指标暴露给 Prometheus,从而可以在 Grafana 等可视化工具中进行展示和告警。 postgres_exporter download…...
网络分层模型( OSI、TCP/IP、五层协议)
1、网络分层模型 计算机网络是一个极其复杂的系统。想象一下最简单的情况:两台连接在网络上的计算机需要相互传输文件。不仅需要确保存在一条传输数据的通路,还需要完成以下几项工作: 发起通信的计算机必须激活数据通路,这包括发…...
POI遍历行所有单元格的两种方式,getPhysicalNumberOfCells方式有问题,勿用
今天看POI源码的时候,发现HSSFWorkbook类型的工作簿,行数据是用TreeMap<Integer, HSSFRow>存储的,列数据是用HSSFCell[]数组来存的;XSSFWorkbook类型的工作簿,行数据是用SortedMap<Integer, XSSFRow>存储的…...
Latex转word(docx)或者说PDF转word 一个相对靠谱的方式
0. 前言 投文章过程中总会有各种各样的要求,其中提供word格式的手稿往往是令我头疼的一件事。尤其在多公式的文章中,其中公式转换是一个头疼的地方,还有很多图表,格式等等,想想就让人头疼欲裂。实践中摸索出一条相对靠…...
敖汉宝塔油页岩露天矿山安全自动化监测
1. 项目简介 本次项目位于内蒙古自治区赤峰市敖汉旗宝国吐乡大青山村,地理位置好。主营许可经营项目:无一般经营项目:页岩油生产;页岩油、润滑油、建筑材料(不含油漆)销售等单位规模1-20人,单位…...
Android笔记【14】结合LaunchedEffect实现计时器功能。
一、问题 cy老师第五次作业 结合LaunchedEffect实现计时器功能。要求:动态计时,每秒修改时间,计时的时间格式为“00:00:00”(小时:分钟:秒)提交源代码的文本和运行截图…...
三维重建(单目、双目、多目、点云、SFM、SLAM)
1 相机几何与标定1.1 相机模型中的坐标系1.2 四种坐标系之间的转换1.3 相机内参1.4 相机标定 2 传统三维重建2.1 RGBD三维重建2.1.1 KinectFusion2.1.2 BundleFusion 2.1 MVS三维重建2.2.1 COLMAP2.2.2 OpenMVS 3 点云三维重建3.1 3D点云任务3.2 点云数据3.3 特征提取3.3.1 Poi…...
软体机器人动态手内笔旋转研究
人工智能咨询培训老师叶梓 转载标明出处 软体机器人因其在安全互动方面的优势而备受关注,但在高速动态任务中却面临挑战。最近,卡内基梅隆大学机器人研究所的研究团队提出了一种名为SWIFT的系统,旨在通过学习和试错来实现软体机器人手的动态…...
福昕PDF低代码平台
福昕PDF低代码平台简介 福昕PDF 低代码平台是一款创新的工具,旨在简化PDF处理和管理的流程。通过这个平台,用户可以通过简单的拖拽界面上的按钮,轻松完成对Cloud API的调用工作流,而无需编写复杂的代码。这使得即使没有编程经验的…...
【笔记】Linux中使用到的一些操作
1、查找指定文件并执行删除 find . -name "checkpoint_*_*.pth" -type f -exec rm -f {} \; 2、查看每个文件夹占用空间 du -h --max-depth1 3、移动文件 mv valid.zip ./xg mv 文件 目标位置 4、删除文件夹 rmdir folder rm -r folder # 递归删除文件夹下所有内容…...