Spaces:
Runtime error
Runtime error
File size: 7,987 Bytes
de2377a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | # motion_generation/lib/model/ddpm.py
import torch
import torch.nn as nn
from lib.model.unet1d import UNet1D
class DDPM(nn.Module):
def __init__(self, model: UNet1D, beta_schedule='linear', timesteps=1000):
super().__init__()
self.model = model # 噪声预测器 (UNet1D)
self.timesteps = timesteps
# ----------------------------------------------------
# 1. 定义 Beta 调度和相关参数
# ----------------------------------------------------
# 1.1. 计算 Betas (噪声水平)
if beta_schedule == 'linear':
betas = self._linear_beta_schedule(timesteps)
else:
raise NotImplementedError(f"不支持的 beta_schedule: {beta_schedule}")
# 将所有参数注册为 buffer (不会被训练,但会保存到 state_dict)
self.register_buffer('betas', betas)
# 1.2. 计算 Alpha 相关的参数
alphas = 1.0 - betas
alphas_cumprod = torch.cumprod(alphas, dim=0) # 累积乘积: \bar{\alpha}_t
alphas_cumprod_prev = torch.cat([torch.tensor([1.0]), alphas_cumprod[:-1]])
# 方便计算的根号形式
sqrt_alphas_cumprod = torch.sqrt(alphas_cumprod)
sqrt_one_minus_alphas_cumprod = torch.sqrt(1.0 - alphas_cumprod)
sqrt_recip_alphas = torch.sqrt(1.0 / alphas) # 1/sqrt(alpha_t)
# 后验均值计算所需的参数 (反向过程)
posterior_variance = betas * (1.0 - alphas_cumprod_prev) / (1.0 - alphas_cumprod)
# 注册 buffers
self.register_buffer('alphas', alphas)
self.register_buffer('alphas_cumprod', alphas_cumprod)
self.register_buffer('sqrt_alphas_cumprod', sqrt_alphas_cumprod)
self.register_buffer('sqrt_one_minus_alphas_cumprod', sqrt_one_minus_alphas_cumprod)
self.register_buffer('sqrt_recip_alphas', sqrt_recip_alphas)
self.register_buffer('posterior_variance', posterior_variance)
# ----------------------------------------------------
def _linear_beta_schedule(self, timesteps, start=0.0001, end=0.02):
"""线性 Beta 调度"""
return torch.linspace(start, end, timesteps)
def get_index_from_t(self, variances, t, x_shape):
"""从 (T,) 维度的张量中,根据时间步 t (B,) 提取对应的方差/系数,并重塑至 (B, 1, 1...)"""
B = t.shape[0]
out = variances.gather(-1, t) # 从 variances 中提取 t 对应的值
# 重塑维度以匹配 x 的形状 (B, C) -> (B, C)
# 对于我们的姿态数据 (B, 72), C=72,所以只需要重塑到 (B, 1)
return out.reshape(B, *([1] * (len(x_shape) - 1)))
# ----------------------------------------------------
# 2. 前向扩散 (加噪)
# ----------------------------------------------------
def forward_diffusion(self, x_start, t, noise=None):
"""
前向过程:计算 x_t = sqrt(alpha_bar_t) * x_0 + sqrt(1 - alpha_bar_t) * noise
Args:
x_start: 初始数据 x_0 (B, 72)
t: 时间步 (B,)
noise: 用于加噪的噪声,如果为 None 则随机生成
Returns:
x_t: t时刻加噪后的数据 (B, 72)
noise: 实际使用的噪声 (B, 72)
"""
if noise is None:
noise = torch.randn_like(x_start)
# 提取 sqrt(alpha_bar_t) 和 sqrt(1 - alpha_bar_t)
sqrt_alpha_bar_t = self.get_index_from_t(self.sqrt_alphas_cumprod, t, x_start.shape)
sqrt_one_minus_alpha_bar_t = self.get_index_from_t(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape)
# 公式实现
x_t = sqrt_alpha_bar_t * x_start + sqrt_one_minus_alpha_bar_t * noise
return x_t, noise
# ----------------------------------------------------
# 3. 损失计算 (训练过程)
# ----------------------------------------------------
def forward(self, x_start):
"""
训练时的前向传播,用于计算损失。
Args:
x_start: 批量原始数据 (归一化后的姿态) (B, 72)
Returns:
loss: 均方误差 (MSE) 损失
"""
B = x_start.shape[0]
# 1. 随机采样时间步 t
t = torch.randint(0, self.timesteps, (B,), device=x_start.device).long()
# 2. 随机生成噪声
noise = torch.randn_like(x_start)
# 3. 计算 x_t 和 t 时刻的真实噪声
x_t, _ = self.forward_diffusion(x_start, t, noise)
# 4. 噪声预测网络预测噪声
predicted_noise = self.model(x_t, t)
# 5. 计算损失:预测噪声和真实噪声的均方误差 (MSE)
loss = nn.functional.mse_loss(predicted_noise, noise)
return loss
# ----------------------------------------------------
# 4. 采样 (生成过程)
# ----------------------------------------------------
@torch.no_grad()
def sample(self, sample_shape, device, return_intermediates=False, save_interval=None):
"""
逆向过程:从纯噪声开始,迭代去噪 T 步,生成新的姿态数据。
Args:
sample_shape: 要生成的样本形状 (B, 72)
device: 运行设备
return_intermediates: 是否返回中间状态
save_interval: 保存中间状态的间隔(timesteps),如果为None则不保存
Returns:
如果 return_intermediates=False:
x_0: 最终生成的归一化姿态 (B, 72)
如果 return_intermediates=True:
(x_0, intermediates): 最终姿态和中间状态列表
intermediates: List[(timestep, x_t)] 包含时间步和对应的状态
"""
# 从纯噪声开始 x_T ~ N(0, I)
x = torch.randn(sample_shape, device=device)
# 存储中间状态
intermediates = []
if return_intermediates:
# 保存初始噪声状态
intermediates.append((self.timesteps, x.clone().cpu()))
# 从 T-1 步迭代到 0 步
for t in reversed(range(0, self.timesteps)):
t_tensor = torch.full((sample_shape[0],), t, device=device, dtype=torch.long)
# 1. 预测噪声
predicted_noise = self.model(x, t_tensor)
# 2. 提取当前时刻的系数
beta_t = self.get_index_from_t(self.betas, t_tensor, x.shape)
sqrt_one_minus_alpha_bar_t = self.get_index_from_t(self.sqrt_one_minus_alphas_cumprod, t_tensor, x.shape)
sqrt_recip_alpha_t = self.get_index_from_t(self.sqrt_recip_alphas, t_tensor, x.shape)
# 3. 计算均值 (mu_t-1)
# DDPM公式: μ_t = (1/sqrt(alpha_t)) * (x_t - (beta_t/sqrt(1-alpha_bar_t)) * predicted_noise)
mean = sqrt_recip_alpha_t * (x - beta_t * predicted_noise / sqrt_one_minus_alpha_bar_t)
# 4. 添加噪声项
if t > 0:
variance = self.get_index_from_t(self.posterior_variance, t_tensor, x.shape)
noise = torch.randn_like(x)
# x_{t-1} = \mu_{t-1} + \sigma_{t-1} * z
x = mean + torch.sqrt(variance) * noise
else:
x = mean # t=0 时不加噪声,直接取均值作为最终输出
# 保存中间状态
if return_intermediates:
if save_interval is None:
# 保存所有状态
intermediates.append((t, x.clone().cpu()))
elif t % save_interval == 0 or t == 0:
# 按间隔保存
intermediates.append((t, x.clone().cpu()))
# 将输出限制在 [-1, 1] 附近(可选,取决于您的归一化范围)
# x = x.clamp(-1., 1.)
if return_intermediates:
return x, intermediates
return x
|