Spaces:
Runtime error
Runtime error
update sample tools
Browse files- src/sample_utils/__init__.py +3 -0
- src/sample_utils/beta_sampler.py +36 -0
- src/sample_utils/lib/ckpt/fm_20251117_172225/checkpoints/best.pt +3 -0
- src/sample_utils/lib/ckpt/fm_20251117_172225/config.json +26 -0
- src/sample_utils/lib/ckpt/fm_20251117_172225/logs/training.log +170 -0
- src/sample_utils/lib/data_stats/pose_stats.pt +3 -0
- src/sample_utils/lib/data_stats/pp_pose_stats.pt +3 -0
- src/sample_utils/lib/data_stats/t_pose_stats.pt +3 -0
- src/sample_utils/lib/model/ddpm.py +194 -0
- src/sample_utils/lib/model/flow_matching.py +248 -0
- src/sample_utils/lib/model/unet1d.py +259 -0
- src/sample_utils/pose_sampler.py +138 -0
- src/sample_utils/trans_sampler.py +135 -0
src/sample_utils/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .pose_sampler import PoseSampler
|
| 2 |
+
from .beta_sampler import sample_beta
|
| 3 |
+
from .trans_sampler import sample_transl4pp, sample_transl4t, sample_transl4m
|
src/sample_utils/beta_sampler.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
|
| 4 |
+
def sample_beta(batch_size=1, sampling_method='normal', range_limit=3.0, device='cpu'):
|
| 5 |
+
"""
|
| 6 |
+
对SMPL模型的beta参数进行采样,返回1x10的PyTorch张量。
|
| 7 |
+
|
| 8 |
+
参数:
|
| 9 |
+
batch_size (int): 采样样本数量,默认为1。
|
| 10 |
+
sampling_method (str): 采样方法,'uniform'(均匀采样)或'normal'(正态分布采样)。默认为'normal'。
|
| 11 |
+
range_limit (float): beta参数的范围限制,默认为3.0(即[-3, 3])。
|
| 12 |
+
device (torch.device): 计算设备 (e.g., 'cuda' or 'cpu')
|
| 13 |
+
|
| 14 |
+
返回:
|
| 15 |
+
torch.Tensor: 形状为(batch_size, 10)的beta参数张量。
|
| 16 |
+
|
| 17 |
+
异常:
|
| 18 |
+
ValueError: 如果sampling_method不是'uniform'或'normal'。
|
| 19 |
+
"""
|
| 20 |
+
beta_dim = 10 # SMPL beta参数维度
|
| 21 |
+
|
| 22 |
+
if sampling_method == 'uniform':
|
| 23 |
+
# 均匀分布采样
|
| 24 |
+
beta = np.random.uniform(low=-range_limit, high=range_limit, size=(batch_size, beta_dim))
|
| 25 |
+
elif sampling_method == 'normal':
|
| 26 |
+
# 正态分布采样
|
| 27 |
+
beta = np.random.normal(loc=0, scale=2, size=(batch_size, beta_dim))
|
| 28 |
+
beta = np.clip(beta, -range_limit, range_limit) # 限制在[-range_limit, range_limit]
|
| 29 |
+
else:
|
| 30 |
+
raise ValueError("sampling_method must be 'uniform' or 'normal'")
|
| 31 |
+
|
| 32 |
+
# 转换为PyTorch张量
|
| 33 |
+
beta_tensor = torch.tensor(beta, dtype=torch.float32).to(device)
|
| 34 |
+
|
| 35 |
+
return beta_tensor
|
| 36 |
+
|
src/sample_utils/lib/ckpt/fm_20251117_172225/checkpoints/best.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4bafdbcfb572a80b3cae8d2df794a9dbb5b5cd4bf2f671eefd1c0549dcdd8ead
|
| 3 |
+
size 59298341
|
src/sample_utils/lib/ckpt/fm_20251117_172225/config.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_type": "flow_matching",
|
| 3 |
+
"device": "cuda:0",
|
| 4 |
+
"epochs": 50,
|
| 5 |
+
"batch_size": 64,
|
| 6 |
+
"learning_rate": 0.0001,
|
| 7 |
+
"weight_decay": 0.0,
|
| 8 |
+
"grad_clip": 1.0,
|
| 9 |
+
"num_workers": 0,
|
| 10 |
+
"timesteps": 1000,
|
| 11 |
+
"flow_sigma": 0.01,
|
| 12 |
+
"base_channels": 128,
|
| 13 |
+
"channel_multipliers": [
|
| 14 |
+
1,
|
| 15 |
+
2,
|
| 16 |
+
4
|
| 17 |
+
],
|
| 18 |
+
"time_emb_dim": 128,
|
| 19 |
+
"mid_structure": "attention",
|
| 20 |
+
"mid_num_heads": 4,
|
| 21 |
+
"output_dir": "outputs",
|
| 22 |
+
"exp_name": "",
|
| 23 |
+
"log_interval": 100,
|
| 24 |
+
"save_interval": 999,
|
| 25 |
+
"resume": ""
|
| 26 |
+
}
|
src/sample_utils/lib/ckpt/fm_20251117_172225/logs/training.log
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[2025-11-17 17:22:25] [INFO] Using device: cuda:0
|
| 2 |
+
[2025-11-17 17:22:25] [INFO] Experiment: 20251117_172225
|
| 3 |
+
[2025-11-17 17:22:25] [INFO] Output directory: outputs/20251117_172225
|
| 4 |
+
[2025-11-17 17:22:25] [INFO] Configuration saved to: outputs/20251117_172225/config.json
|
| 5 |
+
[2025-11-17 17:22:25] [INFO] Loading datasets...
|
| 6 |
+
[2025-11-17 17:23:12] [INFO] Loaded train data: 184000 samples
|
| 7 |
+
[2025-11-17 17:23:12] [INFO] Initializing FLOW_MATCHING model...
|
| 8 |
+
[2025-11-17 17:23:12] [INFO] UNet mid structure: attention
|
| 9 |
+
[2025-11-17 17:23:12] [INFO] Attention heads: 4
|
| 10 |
+
[2025-11-17 17:23:12] [INFO] FlowMatching initialized with sigma=0.01
|
| 11 |
+
[2025-11-17 17:23:12] [INFO] Total parameters: 4,931,329
|
| 12 |
+
[2025-11-17 17:23:12] [INFO] Trainable parameters: 4,931,329
|
| 13 |
+
[2025-11-17 17:23:12] [INFO] Hyperparameters:
|
| 14 |
+
[2025-11-17 17:23:12] [INFO] model_type: flow_matching
|
| 15 |
+
[2025-11-17 17:23:12] [INFO] device: cuda:0
|
| 16 |
+
[2025-11-17 17:23:12] [INFO] epochs: 50
|
| 17 |
+
[2025-11-17 17:23:12] [INFO] batch_size: 64
|
| 18 |
+
[2025-11-17 17:23:12] [INFO] learning_rate: 0.0001
|
| 19 |
+
[2025-11-17 17:23:12] [INFO] weight_decay: 0.0
|
| 20 |
+
[2025-11-17 17:23:12] [INFO] grad_clip: 1.0
|
| 21 |
+
[2025-11-17 17:23:12] [INFO] num_workers: 0
|
| 22 |
+
[2025-11-17 17:23:12] [INFO] timesteps: 1000
|
| 23 |
+
[2025-11-17 17:23:12] [INFO] flow_sigma: 0.01
|
| 24 |
+
[2025-11-17 17:23:12] [INFO] base_channels: 128
|
| 25 |
+
[2025-11-17 17:23:12] [INFO] channel_multipliers: [1, 2, 4]
|
| 26 |
+
[2025-11-17 17:23:12] [INFO] time_emb_dim: 128
|
| 27 |
+
[2025-11-17 17:23:12] [INFO] mid_structure: attention
|
| 28 |
+
[2025-11-17 17:23:12] [INFO] mid_num_heads: 4
|
| 29 |
+
[2025-11-17 17:23:12] [INFO] output_dir: outputs
|
| 30 |
+
[2025-11-17 17:23:12] [INFO] exp_name:
|
| 31 |
+
[2025-11-17 17:23:12] [INFO] log_interval: 100
|
| 32 |
+
[2025-11-17 17:23:12] [INFO] save_interval: 999
|
| 33 |
+
[2025-11-17 17:23:12] [INFO] resume:
|
| 34 |
+
[2025-11-17 17:23:12] [INFO] Starting training...
|
| 35 |
+
[2025-11-17 17:29:55] [INFO] Epoch [1/50] Average Loss: 1.047125
|
| 36 |
+
[2025-11-17 17:29:55] [INFO] New best model! Loss: 1.047125
|
| 37 |
+
[2025-11-17 17:29:56] [INFO] Checkpoint saved at epoch 1
|
| 38 |
+
[2025-11-17 17:36:55] [INFO] Epoch [2/50] Average Loss: 0.947381
|
| 39 |
+
[2025-11-17 17:36:55] [INFO] New best model! Loss: 0.947381
|
| 40 |
+
[2025-11-17 17:37:10] [INFO] Checkpoint saved at epoch 2
|
| 41 |
+
[2025-11-17 17:44:06] [INFO] Epoch [3/50] Average Loss: 0.922550
|
| 42 |
+
[2025-11-17 17:44:06] [INFO] New best model! Loss: 0.922550
|
| 43 |
+
[2025-11-17 17:44:11] [INFO] Checkpoint saved at epoch 3
|
| 44 |
+
[2025-11-17 17:50:46] [INFO] Epoch [4/50] Average Loss: 0.909522
|
| 45 |
+
[2025-11-17 17:50:46] [INFO] New best model! Loss: 0.909522
|
| 46 |
+
[2025-11-17 17:50:47] [INFO] Checkpoint saved at epoch 4
|
| 47 |
+
[2025-11-17 17:57:23] [INFO] Epoch [5/50] Average Loss: 0.899388
|
| 48 |
+
[2025-11-17 17:57:23] [INFO] New best model! Loss: 0.899388
|
| 49 |
+
[2025-11-17 17:57:24] [INFO] Checkpoint saved at epoch 5
|
| 50 |
+
[2025-11-17 18:04:04] [INFO] Epoch [6/50] Average Loss: 0.893692
|
| 51 |
+
[2025-11-17 18:04:04] [INFO] New best model! Loss: 0.893692
|
| 52 |
+
[2025-11-17 18:04:05] [INFO] Checkpoint saved at epoch 6
|
| 53 |
+
[2025-11-17 18:10:46] [INFO] Epoch [7/50] Average Loss: 0.886531
|
| 54 |
+
[2025-11-17 18:10:46] [INFO] New best model! Loss: 0.886531
|
| 55 |
+
[2025-11-17 18:10:47] [INFO] Checkpoint saved at epoch 7
|
| 56 |
+
[2025-11-17 18:17:26] [INFO] Epoch [8/50] Average Loss: 0.881362
|
| 57 |
+
[2025-11-17 18:17:26] [INFO] New best model! Loss: 0.881362
|
| 58 |
+
[2025-11-17 18:17:27] [INFO] Checkpoint saved at epoch 8
|
| 59 |
+
[2025-11-17 18:24:08] [INFO] Epoch [9/50] Average Loss: 0.878617
|
| 60 |
+
[2025-11-17 18:24:08] [INFO] New best model! Loss: 0.878617
|
| 61 |
+
[2025-11-17 18:24:09] [INFO] Checkpoint saved at epoch 9
|
| 62 |
+
[2025-11-17 18:30:53] [INFO] Epoch [10/50] Average Loss: 0.874885
|
| 63 |
+
[2025-11-17 18:30:53] [INFO] New best model! Loss: 0.874885
|
| 64 |
+
[2025-11-17 18:30:53] [INFO] Checkpoint saved at epoch 10
|
| 65 |
+
[2025-11-17 18:37:29] [INFO] Epoch [11/50] Average Loss: 0.870838
|
| 66 |
+
[2025-11-17 18:37:29] [INFO] New best model! Loss: 0.870838
|
| 67 |
+
[2025-11-17 18:37:30] [INFO] Checkpoint saved at epoch 11
|
| 68 |
+
[2025-11-17 18:44:02] [INFO] Epoch [12/50] Average Loss: 0.868847
|
| 69 |
+
[2025-11-17 18:44:02] [INFO] New best model! Loss: 0.868847
|
| 70 |
+
[2025-11-17 18:44:03] [INFO] Checkpoint saved at epoch 12
|
| 71 |
+
[2025-11-17 18:50:45] [INFO] Epoch [13/50] Average Loss: 0.867175
|
| 72 |
+
[2025-11-17 18:50:45] [INFO] New best model! Loss: 0.867175
|
| 73 |
+
[2025-11-17 18:50:46] [INFO] Checkpoint saved at epoch 13
|
| 74 |
+
[2025-11-17 18:57:22] [INFO] Epoch [14/50] Average Loss: 0.864302
|
| 75 |
+
[2025-11-17 18:57:22] [INFO] New best model! Loss: 0.864302
|
| 76 |
+
[2025-11-17 18:57:23] [INFO] Checkpoint saved at epoch 14
|
| 77 |
+
[2025-11-17 19:04:01] [INFO] Epoch [15/50] Average Loss: 0.862865
|
| 78 |
+
[2025-11-17 19:04:01] [INFO] New best model! Loss: 0.862865
|
| 79 |
+
[2025-11-17 19:04:01] [INFO] Checkpoint saved at epoch 15
|
| 80 |
+
[2025-11-17 19:10:41] [INFO] Epoch [16/50] Average Loss: 0.859902
|
| 81 |
+
[2025-11-17 19:10:41] [INFO] New best model! Loss: 0.859902
|
| 82 |
+
[2025-11-17 19:10:42] [INFO] Checkpoint saved at epoch 16
|
| 83 |
+
[2025-11-17 19:17:17] [INFO] Epoch [17/50] Average Loss: 0.857788
|
| 84 |
+
[2025-11-17 19:17:17] [INFO] New best model! Loss: 0.857788
|
| 85 |
+
[2025-11-17 19:17:18] [INFO] Checkpoint saved at epoch 17
|
| 86 |
+
[2025-11-17 19:24:00] [INFO] Epoch [18/50] Average Loss: 0.855244
|
| 87 |
+
[2025-11-17 19:24:00] [INFO] New best model! Loss: 0.855244
|
| 88 |
+
[2025-11-17 19:24:00] [INFO] Checkpoint saved at epoch 18
|
| 89 |
+
[2025-11-17 19:30:42] [INFO] Epoch [19/50] Average Loss: 0.854479
|
| 90 |
+
[2025-11-17 19:30:42] [INFO] New best model! Loss: 0.854479
|
| 91 |
+
[2025-11-17 19:30:43] [INFO] Checkpoint saved at epoch 19
|
| 92 |
+
[2025-11-17 19:37:24] [INFO] Epoch [20/50] Average Loss: 0.853335
|
| 93 |
+
[2025-11-17 19:37:24] [INFO] New best model! Loss: 0.853335
|
| 94 |
+
[2025-11-17 19:37:25] [INFO] Checkpoint saved at epoch 20
|
| 95 |
+
[2025-11-17 19:44:00] [INFO] Epoch [21/50] Average Loss: 0.851748
|
| 96 |
+
[2025-11-17 19:44:00] [INFO] New best model! Loss: 0.851748
|
| 97 |
+
[2025-11-17 19:44:01] [INFO] Checkpoint saved at epoch 21
|
| 98 |
+
[2025-11-17 19:50:42] [INFO] Epoch [22/50] Average Loss: 0.849092
|
| 99 |
+
[2025-11-17 19:50:42] [INFO] New best model! Loss: 0.849092
|
| 100 |
+
[2025-11-17 19:50:42] [INFO] Checkpoint saved at epoch 22
|
| 101 |
+
[2025-11-17 19:57:25] [INFO] Epoch [23/50] Average Loss: 0.847517
|
| 102 |
+
[2025-11-17 19:57:25] [INFO] New best model! Loss: 0.847517
|
| 103 |
+
[2025-11-17 19:57:25] [INFO] Checkpoint saved at epoch 23
|
| 104 |
+
[2025-11-17 20:04:06] [INFO] Epoch [24/50] Average Loss: 0.845896
|
| 105 |
+
[2025-11-17 20:04:06] [INFO] New best model! Loss: 0.845896
|
| 106 |
+
[2025-11-17 20:04:07] [INFO] Checkpoint saved at epoch 24
|
| 107 |
+
[2025-11-17 20:10:44] [INFO] Epoch [25/50] Average Loss: 0.846312
|
| 108 |
+
[2025-11-17 20:17:26] [INFO] Epoch [26/50] Average Loss: 0.844638
|
| 109 |
+
[2025-11-17 20:17:26] [INFO] New best model! Loss: 0.844638
|
| 110 |
+
[2025-11-17 20:17:27] [INFO] Checkpoint saved at epoch 26
|
| 111 |
+
[2025-11-17 20:24:07] [INFO] Epoch [27/50] Average Loss: 0.842196
|
| 112 |
+
[2025-11-17 20:24:07] [INFO] New best model! Loss: 0.842196
|
| 113 |
+
[2025-11-17 20:24:08] [INFO] Checkpoint saved at epoch 27
|
| 114 |
+
[2025-11-17 20:30:50] [INFO] Epoch [28/50] Average Loss: 0.840379
|
| 115 |
+
[2025-11-17 20:30:50] [INFO] New best model! Loss: 0.840379
|
| 116 |
+
[2025-11-17 20:30:51] [INFO] Checkpoint saved at epoch 28
|
| 117 |
+
[2025-11-17 20:37:33] [INFO] Epoch [29/50] Average Loss: 0.840037
|
| 118 |
+
[2025-11-17 20:37:33] [INFO] New best model! Loss: 0.840037
|
| 119 |
+
[2025-11-17 20:37:34] [INFO] Checkpoint saved at epoch 29
|
| 120 |
+
[2025-11-17 20:44:17] [INFO] Epoch [30/50] Average Loss: 0.839178
|
| 121 |
+
[2025-11-17 20:44:17] [INFO] New best model! Loss: 0.839178
|
| 122 |
+
[2025-11-17 20:44:18] [INFO] Checkpoint saved at epoch 30
|
| 123 |
+
[2025-11-17 20:50:56] [INFO] Epoch [31/50] Average Loss: 0.835445
|
| 124 |
+
[2025-11-17 20:50:56] [INFO] New best model! Loss: 0.835445
|
| 125 |
+
[2025-11-17 20:50:57] [INFO] Checkpoint saved at epoch 31
|
| 126 |
+
[2025-11-17 20:57:37] [INFO] Epoch [32/50] Average Loss: 0.837469
|
| 127 |
+
[2025-11-17 21:04:20] [INFO] Epoch [33/50] Average Loss: 0.835059
|
| 128 |
+
[2025-11-17 21:04:20] [INFO] New best model! Loss: 0.835059
|
| 129 |
+
[2025-11-17 21:04:21] [INFO] Checkpoint saved at epoch 33
|
| 130 |
+
[2025-11-17 21:11:02] [INFO] Epoch [34/50] Average Loss: 0.835552
|
| 131 |
+
[2025-11-17 21:17:46] [INFO] Epoch [35/50] Average Loss: 0.832502
|
| 132 |
+
[2025-11-17 21:17:46] [INFO] New best model! Loss: 0.832502
|
| 133 |
+
[2025-11-17 21:17:47] [INFO] Checkpoint saved at epoch 35
|
| 134 |
+
[2025-11-17 21:24:42] [INFO] Epoch [36/50] Average Loss: 0.833365
|
| 135 |
+
[2025-11-17 21:31:20] [INFO] Epoch [37/50] Average Loss: 0.831459
|
| 136 |
+
[2025-11-17 21:31:20] [INFO] New best model! Loss: 0.831459
|
| 137 |
+
[2025-11-17 21:31:21] [INFO] Checkpoint saved at epoch 37
|
| 138 |
+
[2025-11-17 21:37:55] [INFO] Epoch [38/50] Average Loss: 0.829138
|
| 139 |
+
[2025-11-17 21:37:55] [INFO] New best model! Loss: 0.829138
|
| 140 |
+
[2025-11-17 21:37:56] [INFO] Checkpoint saved at epoch 38
|
| 141 |
+
[2025-11-17 21:44:30] [INFO] Epoch [39/50] Average Loss: 0.829815
|
| 142 |
+
[2025-11-17 21:51:10] [INFO] Epoch [40/50] Average Loss: 0.829309
|
| 143 |
+
[2025-11-17 21:57:46] [INFO] Epoch [41/50] Average Loss: 0.828454
|
| 144 |
+
[2025-11-17 21:57:46] [INFO] New best model! Loss: 0.828454
|
| 145 |
+
[2025-11-17 21:57:47] [INFO] Checkpoint saved at epoch 41
|
| 146 |
+
[2025-11-17 22:04:26] [INFO] Epoch [42/50] Average Loss: 0.828211
|
| 147 |
+
[2025-11-17 22:04:26] [INFO] New best model! Loss: 0.828211
|
| 148 |
+
[2025-11-17 22:04:27] [INFO] Checkpoint saved at epoch 42
|
| 149 |
+
[2025-11-17 22:11:06] [INFO] Epoch [43/50] Average Loss: 0.828369
|
| 150 |
+
[2025-11-17 22:17:38] [INFO] Epoch [44/50] Average Loss: 0.827580
|
| 151 |
+
[2025-11-17 22:17:38] [INFO] New best model! Loss: 0.827580
|
| 152 |
+
[2025-11-17 22:18:00] [INFO] Checkpoint saved at epoch 44
|
| 153 |
+
[2025-11-17 22:24:56] [INFO] Epoch [45/50] Average Loss: 0.826730
|
| 154 |
+
[2025-11-17 22:24:56] [INFO] New best model! Loss: 0.826730
|
| 155 |
+
[2025-11-17 22:24:57] [INFO] Checkpoint saved at epoch 45
|
| 156 |
+
[2025-11-17 22:32:10] [INFO] Epoch [46/50] Average Loss: 0.826514
|
| 157 |
+
[2025-11-17 22:32:10] [INFO] New best model! Loss: 0.826514
|
| 158 |
+
[2025-11-17 22:32:10] [INFO] Checkpoint saved at epoch 46
|
| 159 |
+
[2025-11-17 22:39:14] [INFO] Epoch [47/50] Average Loss: 0.826701
|
| 160 |
+
[2025-11-17 22:45:49] [INFO] Epoch [48/50] Average Loss: 0.825706
|
| 161 |
+
[2025-11-17 22:45:49] [INFO] New best model! Loss: 0.825706
|
| 162 |
+
[2025-11-17 22:45:50] [INFO] Checkpoint saved at epoch 48
|
| 163 |
+
[2025-11-17 22:52:25] [INFO] Epoch [49/50] Average Loss: 0.824755
|
| 164 |
+
[2025-11-17 22:52:25] [INFO] New best model! Loss: 0.824755
|
| 165 |
+
[2025-11-17 22:52:26] [INFO] Checkpoint saved at epoch 49
|
| 166 |
+
[2025-11-17 22:59:00] [INFO] Epoch [50/50] Average Loss: 0.827608
|
| 167 |
+
[2025-11-17 22:59:01] [INFO] Checkpoint saved at epoch 50
|
| 168 |
+
[2025-11-17 22:59:01] [INFO] Training completed!
|
| 169 |
+
[2025-11-17 22:59:01] [INFO] Best loss: 0.824755
|
| 170 |
+
[2025-11-17 22:59:01] [INFO] Checkpoints saved in: outputs/20251117_172225/checkpoints
|
src/sample_utils/lib/data_stats/pose_stats.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:99993ef6d0e6359054c8dd3866f9a63951b85d4e6c5b5999a98f11fe01246bf5
|
| 3 |
+
size 1962
|
src/sample_utils/lib/data_stats/pp_pose_stats.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:62c10d637ce1543d895dbc6ccee898b1f44e9a0e0672f26432f857266d853875
|
| 3 |
+
size 1980
|
src/sample_utils/lib/data_stats/t_pose_stats.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8b5264858353aadcebf6547da84dcb3a3c046682ee1036fe047a78f983449fb1
|
| 3 |
+
size 1974
|
src/sample_utils/lib/model/ddpm.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# motion_generation/lib/model/ddpm.py
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
from lib.model.unet1d import UNet1D
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class DDPM(nn.Module):
|
| 9 |
+
def __init__(self, model: UNet1D, beta_schedule='linear', timesteps=1000):
|
| 10 |
+
super().__init__()
|
| 11 |
+
self.model = model # 噪声预测器 (UNet1D)
|
| 12 |
+
self.timesteps = timesteps
|
| 13 |
+
|
| 14 |
+
# ----------------------------------------------------
|
| 15 |
+
# 1. 定义 Beta 调度和相关参数
|
| 16 |
+
# ----------------------------------------------------
|
| 17 |
+
|
| 18 |
+
# 1.1. 计算 Betas (噪声水平)
|
| 19 |
+
if beta_schedule == 'linear':
|
| 20 |
+
betas = self._linear_beta_schedule(timesteps)
|
| 21 |
+
else:
|
| 22 |
+
raise NotImplementedError(f"不支持的 beta_schedule: {beta_schedule}")
|
| 23 |
+
|
| 24 |
+
# 将所有参数注册为 buffer (不会被训练,但会保存到 state_dict)
|
| 25 |
+
self.register_buffer('betas', betas)
|
| 26 |
+
|
| 27 |
+
# 1.2. 计算 Alpha 相关的参数
|
| 28 |
+
alphas = 1.0 - betas
|
| 29 |
+
alphas_cumprod = torch.cumprod(alphas, dim=0) # 累积乘积: \bar{\alpha}_t
|
| 30 |
+
alphas_cumprod_prev = torch.cat([torch.tensor([1.0]), alphas_cumprod[:-1]])
|
| 31 |
+
|
| 32 |
+
# 方便计算的根号形式
|
| 33 |
+
sqrt_alphas_cumprod = torch.sqrt(alphas_cumprod)
|
| 34 |
+
sqrt_one_minus_alphas_cumprod = torch.sqrt(1.0 - alphas_cumprod)
|
| 35 |
+
sqrt_recip_alphas = torch.sqrt(1.0 / alphas) # 1/sqrt(alpha_t)
|
| 36 |
+
|
| 37 |
+
# 后验均值计算所需的参数 (反向过程)
|
| 38 |
+
posterior_variance = betas * (1.0 - alphas_cumprod_prev) / (1.0 - alphas_cumprod)
|
| 39 |
+
|
| 40 |
+
# 注册 buffers
|
| 41 |
+
self.register_buffer('alphas', alphas)
|
| 42 |
+
self.register_buffer('alphas_cumprod', alphas_cumprod)
|
| 43 |
+
self.register_buffer('sqrt_alphas_cumprod', sqrt_alphas_cumprod)
|
| 44 |
+
self.register_buffer('sqrt_one_minus_alphas_cumprod', sqrt_one_minus_alphas_cumprod)
|
| 45 |
+
self.register_buffer('sqrt_recip_alphas', sqrt_recip_alphas)
|
| 46 |
+
self.register_buffer('posterior_variance', posterior_variance)
|
| 47 |
+
|
| 48 |
+
# ----------------------------------------------------
|
| 49 |
+
|
| 50 |
+
def _linear_beta_schedule(self, timesteps, start=0.0001, end=0.02):
|
| 51 |
+
"""线性 Beta 调度"""
|
| 52 |
+
return torch.linspace(start, end, timesteps)
|
| 53 |
+
|
| 54 |
+
def get_index_from_t(self, variances, t, x_shape):
|
| 55 |
+
"""从 (T,) 维度的张量中,根据时间步 t (B,) 提取对应的方差/系数,并重塑至 (B, 1, 1...)"""
|
| 56 |
+
B = t.shape[0]
|
| 57 |
+
out = variances.gather(-1, t) # 从 variances 中提取 t 对应的值
|
| 58 |
+
|
| 59 |
+
# 重塑维度以匹配 x 的形状 (B, C) -> (B, C)
|
| 60 |
+
# 对于我们的姿态数据 (B, 72), C=72,所以只需要重塑到 (B, 1)
|
| 61 |
+
return out.reshape(B, *([1] * (len(x_shape) - 1)))
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# ----------------------------------------------------
|
| 65 |
+
# 2. 前向扩散 (加噪)
|
| 66 |
+
# ----------------------------------------------------
|
| 67 |
+
def forward_diffusion(self, x_start, t, noise=None):
|
| 68 |
+
"""
|
| 69 |
+
前向过程:计算 x_t = sqrt(alpha_bar_t) * x_0 + sqrt(1 - alpha_bar_t) * noise
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
x_start: 初始数据 x_0 (B, 72)
|
| 73 |
+
t: 时间步 (B,)
|
| 74 |
+
noise: 用于加噪的噪声,如果为 None 则随机生成
|
| 75 |
+
|
| 76 |
+
Returns:
|
| 77 |
+
x_t: t时刻加噪后的数据 (B, 72)
|
| 78 |
+
noise: 实际使用的噪声 (B, 72)
|
| 79 |
+
"""
|
| 80 |
+
if noise is None:
|
| 81 |
+
noise = torch.randn_like(x_start)
|
| 82 |
+
|
| 83 |
+
# 提取 sqrt(alpha_bar_t) 和 sqrt(1 - alpha_bar_t)
|
| 84 |
+
sqrt_alpha_bar_t = self.get_index_from_t(self.sqrt_alphas_cumprod, t, x_start.shape)
|
| 85 |
+
sqrt_one_minus_alpha_bar_t = self.get_index_from_t(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape)
|
| 86 |
+
|
| 87 |
+
# 公式实现
|
| 88 |
+
x_t = sqrt_alpha_bar_t * x_start + sqrt_one_minus_alpha_bar_t * noise
|
| 89 |
+
return x_t, noise
|
| 90 |
+
|
| 91 |
+
# ----------------------------------------------------
|
| 92 |
+
# 3. 损失计算 (训练过程)
|
| 93 |
+
# ----------------------------------------------------
|
| 94 |
+
def forward(self, x_start):
|
| 95 |
+
"""
|
| 96 |
+
训练时的前向传播,用于计算损失。
|
| 97 |
+
|
| 98 |
+
Args:
|
| 99 |
+
x_start: 批量原始数据 (归一化后的姿态) (B, 72)
|
| 100 |
+
|
| 101 |
+
Returns:
|
| 102 |
+
loss: 均方误差 (MSE) 损失
|
| 103 |
+
"""
|
| 104 |
+
B = x_start.shape[0]
|
| 105 |
+
|
| 106 |
+
# 1. 随机采样时间步 t
|
| 107 |
+
t = torch.randint(0, self.timesteps, (B,), device=x_start.device).long()
|
| 108 |
+
|
| 109 |
+
# 2. 随机生成噪声
|
| 110 |
+
noise = torch.randn_like(x_start)
|
| 111 |
+
|
| 112 |
+
# 3. 计算 x_t 和 t 时刻的真实噪声
|
| 113 |
+
x_t, _ = self.forward_diffusion(x_start, t, noise)
|
| 114 |
+
|
| 115 |
+
# 4. 噪声预测网络预测噪声
|
| 116 |
+
predicted_noise = self.model(x_t, t)
|
| 117 |
+
|
| 118 |
+
# 5. 计算损失:预测噪声和真实噪声的均方误差 (MSE)
|
| 119 |
+
loss = nn.functional.mse_loss(predicted_noise, noise)
|
| 120 |
+
|
| 121 |
+
return loss
|
| 122 |
+
|
| 123 |
+
# ----------------------------------------------------
|
| 124 |
+
# 4. 采样 (生成过程)
|
| 125 |
+
# ----------------------------------------------------
|
| 126 |
+
@torch.no_grad()
|
| 127 |
+
def sample(self, sample_shape, device, return_intermediates=False, save_interval=None):
|
| 128 |
+
"""
|
| 129 |
+
逆向过程:从纯噪声开始,迭代去噪 T 步,生成新的姿态数据。
|
| 130 |
+
|
| 131 |
+
Args:
|
| 132 |
+
sample_shape: 要生成的样本形状 (B, 72)
|
| 133 |
+
device: 运行设备
|
| 134 |
+
return_intermediates: 是否返回中间状态
|
| 135 |
+
save_interval: 保存中间状态的间隔(timesteps),如果为None则不保存
|
| 136 |
+
|
| 137 |
+
Returns:
|
| 138 |
+
如果 return_intermediates=False:
|
| 139 |
+
x_0: 最终生成的归一化姿态 (B, 72)
|
| 140 |
+
如果 return_intermediates=True:
|
| 141 |
+
(x_0, intermediates): 最终姿态和中间状态列表
|
| 142 |
+
intermediates: List[(timestep, x_t)] 包含时间步和对应的状态
|
| 143 |
+
"""
|
| 144 |
+
# 从纯噪声开始 x_T ~ N(0, I)
|
| 145 |
+
x = torch.randn(sample_shape, device=device)
|
| 146 |
+
|
| 147 |
+
# 存储中间状态
|
| 148 |
+
intermediates = []
|
| 149 |
+
if return_intermediates:
|
| 150 |
+
# 保存初始噪声状态
|
| 151 |
+
intermediates.append((self.timesteps, x.clone().cpu()))
|
| 152 |
+
|
| 153 |
+
# 从 T-1 步迭代到 0 步
|
| 154 |
+
for t in reversed(range(0, self.timesteps)):
|
| 155 |
+
t_tensor = torch.full((sample_shape[0],), t, device=device, dtype=torch.long)
|
| 156 |
+
|
| 157 |
+
# 1. 预测噪声
|
| 158 |
+
predicted_noise = self.model(x, t_tensor)
|
| 159 |
+
|
| 160 |
+
# 2. 提取当前时刻的系数
|
| 161 |
+
beta_t = self.get_index_from_t(self.betas, t_tensor, x.shape)
|
| 162 |
+
sqrt_one_minus_alpha_bar_t = self.get_index_from_t(self.sqrt_one_minus_alphas_cumprod, t_tensor, x.shape)
|
| 163 |
+
sqrt_recip_alpha_t = self.get_index_from_t(self.sqrt_recip_alphas, t_tensor, x.shape)
|
| 164 |
+
|
| 165 |
+
# 3. 计算均值 (mu_t-1)
|
| 166 |
+
# DDPM公式: μ_t = (1/sqrt(alpha_t)) * (x_t - (beta_t/sqrt(1-alpha_bar_t)) * predicted_noise)
|
| 167 |
+
mean = sqrt_recip_alpha_t * (x - beta_t * predicted_noise / sqrt_one_minus_alpha_bar_t)
|
| 168 |
+
|
| 169 |
+
# 4. 添加噪声项
|
| 170 |
+
if t > 0:
|
| 171 |
+
variance = self.get_index_from_t(self.posterior_variance, t_tensor, x.shape)
|
| 172 |
+
noise = torch.randn_like(x)
|
| 173 |
+
# x_{t-1} = \mu_{t-1} + \sigma_{t-1} * z
|
| 174 |
+
x = mean + torch.sqrt(variance) * noise
|
| 175 |
+
else:
|
| 176 |
+
x = mean # t=0 时不加噪声,直接取均值作为最终输出
|
| 177 |
+
|
| 178 |
+
# 保存中间状态
|
| 179 |
+
if return_intermediates:
|
| 180 |
+
if save_interval is None:
|
| 181 |
+
# 保存所有状态
|
| 182 |
+
intermediates.append((t, x.clone().cpu()))
|
| 183 |
+
elif t % save_interval == 0 or t == 0:
|
| 184 |
+
# 按间隔保存
|
| 185 |
+
intermediates.append((t, x.clone().cpu()))
|
| 186 |
+
|
| 187 |
+
# 将输出限制在 [-1, 1] 附近(可选,取决于您的归一化范围)
|
| 188 |
+
# x = x.clamp(-1., 1.)
|
| 189 |
+
|
| 190 |
+
if return_intermediates:
|
| 191 |
+
return x, intermediates
|
| 192 |
+
return x
|
| 193 |
+
|
| 194 |
+
|
src/sample_utils/lib/model/flow_matching.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Flow Matching implementation for continuous normalizing flows.
|
| 3 |
+
|
| 4 |
+
Flow matching learns a velocity field v(x, t) that transports samples from a noise
|
| 5 |
+
distribution to the data distribution via ordinary differential equations (ODEs).
|
| 6 |
+
|
| 7 |
+
Reference: "Flow Matching for Generative Modeling" (Lipman et al., 2023)
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
from tqdm import tqdm
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class FlowMatching(nn.Module):
|
| 16 |
+
"""
|
| 17 |
+
Flow Matching model that learns to transport noise to data via velocity fields.
|
| 18 |
+
|
| 19 |
+
Args:
|
| 20 |
+
model: Neural network that predicts velocity field v(x, t)
|
| 21 |
+
Should take (x, t) and return predicted velocity of same shape as x
|
| 22 |
+
sigma: Standard deviation for conditional flow matching (default: 0.0)
|
| 23 |
+
When sigma > 0, uses conditional flow matching with Gaussian paths
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
def __init__(self, model, sigma=0.0):
|
| 27 |
+
super().__init__()
|
| 28 |
+
self.model = model
|
| 29 |
+
self.sigma = sigma
|
| 30 |
+
|
| 31 |
+
def forward(self, x_0, return_loss=True):
|
| 32 |
+
"""
|
| 33 |
+
Compute flow matching loss for a batch of data.
|
| 34 |
+
|
| 35 |
+
Forward process:
|
| 36 |
+
- Sample t uniformly from [0, 1]
|
| 37 |
+
- Sample noise x_1 ~ N(0, I)
|
| 38 |
+
- Interpolate: x_t = t * x_0 + (1 - t) * x_1
|
| 39 |
+
- True velocity: v_t = x_0 - x_1
|
| 40 |
+
- Loss: MSE(predicted_velocity, true_velocity)
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
x_0: Clean data samples (B, D)
|
| 44 |
+
return_loss: If True, return scalar loss. If False, return per-sample losses
|
| 45 |
+
|
| 46 |
+
Returns:
|
| 47 |
+
loss: Scalar loss if return_loss=True, else (B,) tensor of per-sample losses
|
| 48 |
+
"""
|
| 49 |
+
batch_size = x_0.shape[0]
|
| 50 |
+
device = x_0.device
|
| 51 |
+
|
| 52 |
+
# Sample random timesteps uniformly from [0, 1]
|
| 53 |
+
t = torch.rand(batch_size, device=device)
|
| 54 |
+
|
| 55 |
+
# Sample noise from standard normal
|
| 56 |
+
x_1 = torch.randn_like(x_0)
|
| 57 |
+
|
| 58 |
+
# Interpolate between noise and data
|
| 59 |
+
# x_t = t * x_0 + (1 - t) * x_1
|
| 60 |
+
t_expanded = t.view(batch_size, *([1] * (x_0.ndim - 1))) # (B, 1, 1, ...)
|
| 61 |
+
x_t = t_expanded * x_0 + (1 - t_expanded) * x_1
|
| 62 |
+
|
| 63 |
+
# True velocity field: dx_t/dt = x_0 - x_1
|
| 64 |
+
# This is the derivative of the linear interpolation
|
| 65 |
+
true_velocity = x_0 - x_1
|
| 66 |
+
|
| 67 |
+
# Add Gaussian noise for conditional flow matching (if sigma > 0)
|
| 68 |
+
if self.sigma > 0:
|
| 69 |
+
x_t = x_t + self.sigma * torch.randn_like(x_t)
|
| 70 |
+
|
| 71 |
+
# Predict velocity using the model
|
| 72 |
+
predicted_velocity = self.model(x_t, t)
|
| 73 |
+
|
| 74 |
+
# Compute MSE loss
|
| 75 |
+
loss = (predicted_velocity - true_velocity) ** 2
|
| 76 |
+
|
| 77 |
+
if return_loss:
|
| 78 |
+
return loss.mean()
|
| 79 |
+
else:
|
| 80 |
+
# Return per-sample loss (averaged over dimensions)
|
| 81 |
+
return loss.view(batch_size, -1).mean(dim=1)
|
| 82 |
+
|
| 83 |
+
@torch.no_grad()
|
| 84 |
+
def sample(self,
|
| 85 |
+
sample_shape,
|
| 86 |
+
device='cuda',
|
| 87 |
+
num_steps=100,
|
| 88 |
+
method='euler',
|
| 89 |
+
return_intermediates=False,
|
| 90 |
+
save_interval=10,
|
| 91 |
+
verbose=True):
|
| 92 |
+
"""
|
| 93 |
+
Generate samples by solving the ODE: dx/dt = v(x, t) from t=0 to t=1.
|
| 94 |
+
|
| 95 |
+
Args:
|
| 96 |
+
sample_shape: Shape of samples to generate (B, D)
|
| 97 |
+
device: Device to generate samples on
|
| 98 |
+
num_steps: Number of integration steps (default: 100)
|
| 99 |
+
method: ODE solver method ('euler', 'midpoint', 'rk4')
|
| 100 |
+
return_intermediates: If True, return intermediate states
|
| 101 |
+
save_interval: Save intermediate states every N steps (if return_intermediates=True)
|
| 102 |
+
verbose: Show progress bar
|
| 103 |
+
|
| 104 |
+
Returns:
|
| 105 |
+
samples: Generated samples (B, D)
|
| 106 |
+
intermediates: List of (t, x_t) tuples (if return_intermediates=True)
|
| 107 |
+
"""
|
| 108 |
+
# Start from noise at t=0
|
| 109 |
+
x = torch.randn(sample_shape, device=device)
|
| 110 |
+
|
| 111 |
+
# Time steps from 0 to 1
|
| 112 |
+
timesteps = torch.linspace(0, 1, num_steps + 1, device=device)
|
| 113 |
+
dt = 1.0 / num_steps
|
| 114 |
+
|
| 115 |
+
intermediates = []
|
| 116 |
+
if return_intermediates:
|
| 117 |
+
intermediates.append((0.0, x.cpu().clone()))
|
| 118 |
+
|
| 119 |
+
# Progress bar
|
| 120 |
+
iterator = tqdm(range(num_steps), desc='Sampling') if verbose else range(num_steps)
|
| 121 |
+
|
| 122 |
+
for i in iterator:
|
| 123 |
+
t = timesteps[i]
|
| 124 |
+
|
| 125 |
+
# Create batch of timesteps
|
| 126 |
+
t_batch = torch.full((sample_shape[0],), t, device=device)
|
| 127 |
+
|
| 128 |
+
if method == 'euler':
|
| 129 |
+
# Euler method: x_{t+dt} = x_t + dt * v(x_t, t)
|
| 130 |
+
v = self.model(x, t_batch)
|
| 131 |
+
x = x + dt * v
|
| 132 |
+
|
| 133 |
+
elif method == 'midpoint':
|
| 134 |
+
# Midpoint method (RK2)
|
| 135 |
+
# k1 = v(x_t, t)
|
| 136 |
+
# k2 = v(x_t + 0.5*dt*k1, t + 0.5*dt)
|
| 137 |
+
# x_{t+dt} = x_t + dt * k2
|
| 138 |
+
k1 = self.model(x, t_batch)
|
| 139 |
+
t_mid = t + 0.5 * dt
|
| 140 |
+
t_mid_batch = torch.full((sample_shape[0],), t_mid, device=device)
|
| 141 |
+
k2 = self.model(x + 0.5 * dt * k1, t_mid_batch)
|
| 142 |
+
x = x + dt * k2
|
| 143 |
+
|
| 144 |
+
elif method == 'rk4':
|
| 145 |
+
# Classic RK4 method
|
| 146 |
+
t_half = t + 0.5 * dt
|
| 147 |
+
t_next = t + dt
|
| 148 |
+
|
| 149 |
+
t_batch_half = torch.full((sample_shape[0],), t_half, device=device)
|
| 150 |
+
t_batch_next = torch.full((sample_shape[0],), t_next, device=device)
|
| 151 |
+
|
| 152 |
+
k1 = self.model(x, t_batch)
|
| 153 |
+
k2 = self.model(x + 0.5 * dt * k1, t_batch_half)
|
| 154 |
+
k3 = self.model(x + 0.5 * dt * k2, t_batch_half)
|
| 155 |
+
k4 = self.model(x + dt * k3, t_batch_next)
|
| 156 |
+
|
| 157 |
+
x = x + (dt / 6.0) * (k1 + 2*k2 + 2*k3 + k4)
|
| 158 |
+
|
| 159 |
+
else:
|
| 160 |
+
raise ValueError(f"Unknown method: {method}. Choose from 'euler', 'midpoint', 'rk4'")
|
| 161 |
+
|
| 162 |
+
# Save intermediate states
|
| 163 |
+
if return_intermediates and (i + 1) % save_interval == 0:
|
| 164 |
+
intermediates.append((timesteps[i + 1].item(), x.cpu().clone()))
|
| 165 |
+
|
| 166 |
+
# Final state at t=1
|
| 167 |
+
if return_intermediates:
|
| 168 |
+
if len(intermediates) == 0 or intermediates[-1][0] != 1.0:
|
| 169 |
+
intermediates.append((1.0, x.cpu().clone()))
|
| 170 |
+
return x, intermediates
|
| 171 |
+
|
| 172 |
+
return x
|
| 173 |
+
|
| 174 |
+
@torch.no_grad()
|
| 175 |
+
def sample_ode(self,
|
| 176 |
+
sample_shape,
|
| 177 |
+
device='cuda',
|
| 178 |
+
rtol=1e-5,
|
| 179 |
+
atol=1e-5,
|
| 180 |
+
method='dopri5',
|
| 181 |
+
return_intermediates=False,
|
| 182 |
+
verbose=True):
|
| 183 |
+
"""
|
| 184 |
+
Generate samples using adaptive ODE solvers from torchdiffeq.
|
| 185 |
+
|
| 186 |
+
This method uses scipy-style adaptive solvers that automatically
|
| 187 |
+
adjust step sizes for accuracy. Requires: pip install torchdiffeq
|
| 188 |
+
|
| 189 |
+
Args:
|
| 190 |
+
sample_shape: Shape of samples to generate (B, D)
|
| 191 |
+
device: Device to generate samples on
|
| 192 |
+
rtol: Relative tolerance for ODE solver
|
| 193 |
+
atol: Absolute tolerance for ODE solver
|
| 194 |
+
method: Solver method ('dopri5', 'dopri8', 'adams', 'rk4', etc.)
|
| 195 |
+
return_intermediates: If True, return trajectory
|
| 196 |
+
verbose: Print status messages
|
| 197 |
+
|
| 198 |
+
Returns:
|
| 199 |
+
samples: Generated samples (B, D)
|
| 200 |
+
trajectory: Full trajectory if return_intermediates=True
|
| 201 |
+
"""
|
| 202 |
+
try:
|
| 203 |
+
from torchdiffeq import odeint
|
| 204 |
+
except ImportError:
|
| 205 |
+
raise ImportError(
|
| 206 |
+
"torchdiffeq is required for adaptive ODE solvers. "
|
| 207 |
+
"Install with: pip install torchdiffeq"
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
if verbose:
|
| 211 |
+
print(f"Sampling with adaptive ODE solver: {method}")
|
| 212 |
+
|
| 213 |
+
# Start from noise at t=0
|
| 214 |
+
x_0 = torch.randn(sample_shape, device=device)
|
| 215 |
+
|
| 216 |
+
# Define velocity field function
|
| 217 |
+
def velocity_fn(t, x):
|
| 218 |
+
# t is a scalar tensor, need to broadcast to batch
|
| 219 |
+
t_batch = torch.full((sample_shape[0],), t.item(), device=device)
|
| 220 |
+
return self.model(x, t_batch)
|
| 221 |
+
|
| 222 |
+
# Integration time points
|
| 223 |
+
if return_intermediates:
|
| 224 |
+
# Return full trajectory with 100 points
|
| 225 |
+
t_span = torch.linspace(0, 1, 100, device=device)
|
| 226 |
+
else:
|
| 227 |
+
# Just start and end points
|
| 228 |
+
t_span = torch.tensor([0.0, 1.0], device=device)
|
| 229 |
+
|
| 230 |
+
# Solve ODE
|
| 231 |
+
trajectory = odeint(
|
| 232 |
+
velocity_fn,
|
| 233 |
+
x_0,
|
| 234 |
+
t_span,
|
| 235 |
+
rtol=rtol,
|
| 236 |
+
atol=atol,
|
| 237 |
+
method=method
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
# Extract final state
|
| 241 |
+
x_final = trajectory[-1]
|
| 242 |
+
|
| 243 |
+
if return_intermediates:
|
| 244 |
+
# Convert trajectory to list of (t, x) tuples
|
| 245 |
+
intermediates = [(t.item(), x.cpu()) for t, x in zip(t_span, trajectory)]
|
| 246 |
+
return x_final, intermediates
|
| 247 |
+
|
| 248 |
+
return x_final
|
src/sample_utils/lib/model/unet1d.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# motion_generation/lib/model/unet1d.py
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import math
|
| 6 |
+
|
| 7 |
+
class PositionalEncoding(nn.Module):
|
| 8 |
+
"""
|
| 9 |
+
用于编码时间步 t 的标准 Transformer 位置编码
|
| 10 |
+
"""
|
| 11 |
+
def __init__(self, dim):
|
| 12 |
+
super().__init__()
|
| 13 |
+
self.dim = dim
|
| 14 |
+
self.register_buffer('inv_freq', 1. / (10000 ** (torch.arange(0, dim, 2).float() / dim)))
|
| 15 |
+
|
| 16 |
+
def forward(self, x):
|
| 17 |
+
# x 形状: (B,) 时间步索引
|
| 18 |
+
sinusoid_inp = torch.einsum('i, j -> i j', x.float(), self.inv_freq)
|
| 19 |
+
emb = torch.cat((sinusoid_inp.sin(), sinusoid_inp.cos()), dim=-1)
|
| 20 |
+
return emb
|
| 21 |
+
|
| 22 |
+
class ResBlock1D(nn.Module):
|
| 23 |
+
"""
|
| 24 |
+
一维残差块,包含 time embedding 的融合
|
| 25 |
+
"""
|
| 26 |
+
def __init__(self, in_channels, out_channels, time_dim, kernel_size=3):
|
| 27 |
+
super().__init__()
|
| 28 |
+
padding = kernel_size // 2
|
| 29 |
+
|
| 30 |
+
self.conv1 = nn.Conv1d(in_channels, out_channels, kernel_size, padding=padding)
|
| 31 |
+
self.bn1 = nn.BatchNorm1d(out_channels)
|
| 32 |
+
self.act1 = nn.GELU()
|
| 33 |
+
|
| 34 |
+
self.conv2 = nn.Conv1d(out_channels, out_channels, kernel_size, padding=padding)
|
| 35 |
+
self.bn2 = nn.BatchNorm1d(out_channels)
|
| 36 |
+
self.act2 = nn.GELU()
|
| 37 |
+
|
| 38 |
+
# 时间步嵌入层
|
| 39 |
+
self.time_proj = nn.Linear(time_dim, out_channels)
|
| 40 |
+
|
| 41 |
+
# 确保输入/输出通道匹配
|
| 42 |
+
self.residual_conv = nn.Conv1d(in_channels, out_channels, 1) if in_channels != out_channels else nn.Identity()
|
| 43 |
+
|
| 44 |
+
def forward(self, x, time_emb):
|
| 45 |
+
# x: (B, C_in, L) L=72, C_in=通道数
|
| 46 |
+
|
| 47 |
+
h = self.conv1(x)
|
| 48 |
+
h = self.bn1(h)
|
| 49 |
+
h = self.act1(h)
|
| 50 |
+
|
| 51 |
+
# 融合时间嵌入:沿特征维度广播并相加
|
| 52 |
+
time_emb_proj = self.time_proj(time_emb).unsqueeze(-1) # (B, C_out) -> (B, C_out, 1)
|
| 53 |
+
h = h + time_emb_proj
|
| 54 |
+
|
| 55 |
+
h = self.conv2(h)
|
| 56 |
+
h = self.bn2(h)
|
| 57 |
+
h = self.act2(h)
|
| 58 |
+
|
| 59 |
+
return h + self.residual_conv(x)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class SelfAttention1D(nn.Module):
|
| 63 |
+
"""
|
| 64 |
+
一维自注意力模块,包含 time embedding 的融合
|
| 65 |
+
"""
|
| 66 |
+
def __init__(self, channels, time_dim, num_heads=4):
|
| 67 |
+
super().__init__()
|
| 68 |
+
self.channels = channels
|
| 69 |
+
self.num_heads = num_heads
|
| 70 |
+
assert channels % num_heads == 0, "channels must be divisible by num_heads"
|
| 71 |
+
|
| 72 |
+
# Group normalization for better stability
|
| 73 |
+
self.norm = nn.GroupNorm(num_groups=8, num_channels=channels)
|
| 74 |
+
|
| 75 |
+
# Multi-head attention components
|
| 76 |
+
self.qkv = nn.Conv1d(channels, channels * 3, 1)
|
| 77 |
+
self.proj = nn.Conv1d(channels, channels, 1)
|
| 78 |
+
|
| 79 |
+
# Time embedding projection
|
| 80 |
+
self.time_proj = nn.Linear(time_dim, channels)
|
| 81 |
+
|
| 82 |
+
def forward(self, x, time_emb):
|
| 83 |
+
# x: (B, C, L)
|
| 84 |
+
B, C, L = x.shape
|
| 85 |
+
|
| 86 |
+
# Normalize input
|
| 87 |
+
h = self.norm(x)
|
| 88 |
+
|
| 89 |
+
# Add time embedding
|
| 90 |
+
time_emb_proj = self.time_proj(time_emb).unsqueeze(-1) # (B, C, 1)
|
| 91 |
+
h = h + time_emb_proj
|
| 92 |
+
|
| 93 |
+
# Compute Q, K, V
|
| 94 |
+
qkv = self.qkv(h) # (B, 3*C, L)
|
| 95 |
+
qkv = qkv.reshape(B, 3, self.num_heads, C // self.num_heads, L)
|
| 96 |
+
qkv = qkv.permute(1, 0, 2, 4, 3) # (3, B, num_heads, L, head_dim)
|
| 97 |
+
q, k, v = qkv[0], qkv[1], qkv[2]
|
| 98 |
+
|
| 99 |
+
# Attention
|
| 100 |
+
scale = (C // self.num_heads) ** -0.5
|
| 101 |
+
attn = torch.matmul(q, k.transpose(-2, -1)) * scale # (B, num_heads, L, L)
|
| 102 |
+
attn = torch.softmax(attn, dim=-1)
|
| 103 |
+
|
| 104 |
+
# Apply attention to values
|
| 105 |
+
out = torch.matmul(attn, v) # (B, num_heads, L, head_dim)
|
| 106 |
+
out = out.permute(0, 1, 3, 2).reshape(B, C, L) # (B, C, L)
|
| 107 |
+
|
| 108 |
+
# Project and add residual
|
| 109 |
+
out = self.proj(out)
|
| 110 |
+
return out + x
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
class UNet1D(nn.Module):
|
| 114 |
+
def __init__(self, pose_dim=72, base_channels=128, channel_multipliers=[1, 2, 4],
|
| 115 |
+
time_emb_dim=256, mid_structure='conv', mid_num_heads=4):
|
| 116 |
+
"""
|
| 117 |
+
UNet1D model for 1D sequence processing
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
pose_dim: Dimension of input pose (default: 72)
|
| 121 |
+
base_channels: Base number of channels (default: 128)
|
| 122 |
+
channel_multipliers: Channel multipliers for each level (default: [1, 2, 4])
|
| 123 |
+
time_emb_dim: Time embedding dimension (default: 256)
|
| 124 |
+
mid_structure: Structure for middle layer, either 'conv' or 'attention' (default: 'conv')
|
| 125 |
+
mid_num_heads: Number of attention heads for mid layer when using attention (default: 4)
|
| 126 |
+
"""
|
| 127 |
+
super().__init__()
|
| 128 |
+
|
| 129 |
+
self.mid_structure = mid_structure
|
| 130 |
+
assert mid_structure in ['conv', 'attention'], "mid_structure must be 'conv' or 'attention'"
|
| 131 |
+
|
| 132 |
+
# 1. Time Embedding
|
| 133 |
+
self.time_mlp = nn.Sequential(
|
| 134 |
+
PositionalEncoding(base_channels),
|
| 135 |
+
nn.Linear(base_channels, time_emb_dim),
|
| 136 |
+
nn.GELU(),
|
| 137 |
+
nn.Linear(time_emb_dim, time_emb_dim)
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
# 调整输入通道:姿态向量 (B, 72) -> (B, 1, 72)
|
| 141 |
+
# 我们将特征维度 (72) 视为长度 L,将 1 视为通道 C
|
| 142 |
+
in_channels = 1
|
| 143 |
+
|
| 144 |
+
# 2. 编码器 (Downsampling)
|
| 145 |
+
channels = [in_channels] + [base_channels * m for m in channel_multipliers]
|
| 146 |
+
self.downs = nn.ModuleList()
|
| 147 |
+
|
| 148 |
+
for i in range(len(channel_multipliers)):
|
| 149 |
+
in_c = channels[i]
|
| 150 |
+
out_c = channels[i+1]
|
| 151 |
+
|
| 152 |
+
self.downs.append(nn.ModuleList([
|
| 153 |
+
ResBlock1D(in_c if i == 0 else in_c, out_c, time_emb_dim), # 输入是 1, L 或 C_in, L
|
| 154 |
+
nn.MaxPool1d(2) if i < len(channel_multipliers) - 1 else nn.Identity()
|
| 155 |
+
]))
|
| 156 |
+
|
| 157 |
+
# 3. 中间层 - 根据 mid_structure 选择使用卷积或自注意力
|
| 158 |
+
mid_c = channels[-1]
|
| 159 |
+
if mid_structure == 'conv':
|
| 160 |
+
self.mid = ResBlock1D(mid_c, mid_c, time_emb_dim)
|
| 161 |
+
elif mid_structure == 'attention':
|
| 162 |
+
# 确保通道数能被注意力头数整除
|
| 163 |
+
assert mid_c % mid_num_heads == 0, f"mid_c ({mid_c}) must be divisible by mid_num_heads ({mid_num_heads})"
|
| 164 |
+
self.mid = SelfAttention1D(mid_c, time_emb_dim, num_heads=mid_num_heads)
|
| 165 |
+
|
| 166 |
+
# 4. 解码器 (Upsampling)
|
| 167 |
+
self.ups = nn.ModuleList()
|
| 168 |
+
reversed_channels = list(reversed(channels))
|
| 169 |
+
|
| 170 |
+
for i in range(len(channel_multipliers)):
|
| 171 |
+
in_c = reversed_channels[i] # 来自上一层的通道数
|
| 172 |
+
# 最后一层输出 base_channels,而不是 in_channels (1)
|
| 173 |
+
out_c = reversed_channels[i+1] if i < len(channel_multipliers) - 1 else base_channels
|
| 174 |
+
skip_c = in_c # 跳跃连接通道数(来自对应编码器层)
|
| 175 |
+
|
| 176 |
+
self.ups.append(nn.ModuleList([
|
| 177 |
+
# ResBlock 接收拼接后的通道: in_c(来自上层) + skip_c(来自编码器)
|
| 178 |
+
# 输出为 out_c 通道
|
| 179 |
+
ResBlock1D(in_c + skip_c, out_c, time_emb_dim),
|
| 180 |
+
# 上采样到下一层的空间尺寸
|
| 181 |
+
nn.ConvTranspose1d(out_c, out_c, kernel_size=2, stride=2) if i < len(channel_multipliers) - 1 else nn.Identity(),
|
| 182 |
+
]))
|
| 183 |
+
|
| 184 |
+
# 5. 输出层 (回到 1 个通道)
|
| 185 |
+
self.out_conv = nn.Conv1d(base_channels, in_channels, kernel_size=1)
|
| 186 |
+
|
| 187 |
+
def forward(self, x, t):
|
| 188 |
+
# import pdb; pdb.set_trace()
|
| 189 |
+
# x: (B, 72) 归一化姿态,t: (B,) 时间步索引
|
| 190 |
+
x = x.unsqueeze(1) # (B, 1, 72)
|
| 191 |
+
|
| 192 |
+
# 1. Time Embedding
|
| 193 |
+
time_emb = self.time_mlp(t)
|
| 194 |
+
|
| 195 |
+
# 2. 编码器
|
| 196 |
+
skips = []
|
| 197 |
+
for resblock, downsample in self.downs:
|
| 198 |
+
x = resblock(x, time_emb)
|
| 199 |
+
skips.append(x)
|
| 200 |
+
x = downsample(x)
|
| 201 |
+
|
| 202 |
+
# 3. 中间层
|
| 203 |
+
x = self.mid(x, time_emb)
|
| 204 |
+
|
| 205 |
+
# 4. 解码器
|
| 206 |
+
for i, (resblock, upsample) in enumerate(self.ups):
|
| 207 |
+
skip = skips.pop()
|
| 208 |
+
|
| 209 |
+
# 跳跃连接
|
| 210 |
+
# 检查维度是否匹配,如果 MaxPool 导致了奇数/偶数长度不匹配,需要裁剪
|
| 211 |
+
if x.shape[-1] != skip.shape[-1]:
|
| 212 |
+
x = nn.functional.pad(x, (0, skip.shape[-1] - x.shape[-1]))
|
| 213 |
+
|
| 214 |
+
# 拼接跳跃连接
|
| 215 |
+
x = torch.cat((x, skip), dim=1) # 沿通道维度拼接
|
| 216 |
+
|
| 217 |
+
# 处理拼接后的特征
|
| 218 |
+
x = resblock(x, time_emb)
|
| 219 |
+
|
| 220 |
+
# 上采样到下一层的空间尺寸(最后一层不上采样)
|
| 221 |
+
if i < len(self.ups) - 1:
|
| 222 |
+
x = upsample(x) # (B, C, L) -> (B, C, L*2)
|
| 223 |
+
|
| 224 |
+
# 5. 输出层
|
| 225 |
+
x = self.out_conv(x) # (B, 1, 72)
|
| 226 |
+
|
| 227 |
+
return x.squeeze(1) # (B, 72)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
if __name__ == "__main__":
|
| 231 |
+
# 测试 UNet1D 模型
|
| 232 |
+
print("Testing UNet1D with conv mid structure...")
|
| 233 |
+
model_conv = UNet1D(mid_structure='conv')
|
| 234 |
+
|
| 235 |
+
x = torch.randn(4, 72) # 模拟输入 (B, 72)
|
| 236 |
+
t = torch.randint(0, 1000, (4,)) # 模拟时间步索引 (B,)
|
| 237 |
+
|
| 238 |
+
output_conv = model_conv(x, t)
|
| 239 |
+
print(f"Input shape: {x.shape}")
|
| 240 |
+
print(f"Output shape (conv): {output_conv.shape}")
|
| 241 |
+
print(f"Expected output shape: (4, 72)")
|
| 242 |
+
print(f"Test passed (conv): {output_conv.shape == torch.Size([4, 72])}")
|
| 243 |
+
|
| 244 |
+
print("\nTesting UNet1D with attention mid structure...")
|
| 245 |
+
model_attn = UNet1D(mid_structure='attention', base_channels=128, mid_num_heads=8)
|
| 246 |
+
|
| 247 |
+
output_attn = model_attn(x, t)
|
| 248 |
+
print(f"Output shape (attention): {output_attn.shape}")
|
| 249 |
+
print(f"Test passed (attention): {output_attn.shape == torch.Size([4, 72])}")
|
| 250 |
+
|
| 251 |
+
# 统计参数量
|
| 252 |
+
conv_params = sum(p.numel() for p in model_conv.parameters())
|
| 253 |
+
attn_params = sum(p.numel() for p in model_attn.parameters())
|
| 254 |
+
print(f"\nParameters (conv): {conv_params:,}")
|
| 255 |
+
print(f"Parameters (attention): {attn_params:,}")
|
| 256 |
+
print(f"Difference: {abs(attn_params - conv_params):,}")
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
|
src/sample_utils/pose_sampler.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import os
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import numpy as np
|
| 5 |
+
import json
|
| 6 |
+
import sys
|
| 7 |
+
|
| 8 |
+
# PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
| 9 |
+
# sys.path.insert(0, str(PROJECT_ROOT))
|
| 10 |
+
|
| 11 |
+
from .lib.model.unet1d import UNet1D
|
| 12 |
+
from .lib.model.flow_matching import FlowMatching
|
| 13 |
+
|
| 14 |
+
class PoseSampler:
|
| 15 |
+
def __init__(self,
|
| 16 |
+
device='cpu',
|
| 17 |
+
dataset='pp'):
|
| 18 |
+
"""
|
| 19 |
+
初始化 PoseSampler 类。
|
| 20 |
+
在此处加载配置、统计量和模型,确保这些重型操作只执行一次。
|
| 21 |
+
"""
|
| 22 |
+
self.device = torch.device(device)
|
| 23 |
+
self.checkpoint_path = "sample_utils/lib/ckpt/fm_20251117_172225/checkpoints/best.pt"
|
| 24 |
+
self.dataset = dataset
|
| 25 |
+
self.stats_dir = "sample_utils/lib/data_stats"
|
| 26 |
+
|
| 27 |
+
# 1. 自动检测 run_dir 并加载配置
|
| 28 |
+
self.run_dir = self._detect_run_dir(self.checkpoint_path)
|
| 29 |
+
self.config = self._load_config(self.run_dir)
|
| 30 |
+
|
| 31 |
+
# 2. 加载数据统计量 (Mean/Std)
|
| 32 |
+
self.pose_mean, self.pose_std = self._load_pose_stats()
|
| 33 |
+
|
| 34 |
+
# 3. 加载模型
|
| 35 |
+
self.model = self._load_model()
|
| 36 |
+
print(f"Pose Sampler loaded successfully from {self.checkpoint_path}")
|
| 37 |
+
|
| 38 |
+
def _detect_run_dir(self, checkpoint_path):
|
| 39 |
+
"""内部辅助方法:从 checkpoint 路径推断 run 目录"""
|
| 40 |
+
abs_path = os.path.abspath(checkpoint_path)
|
| 41 |
+
if 'checkpoints' in abs_path:
|
| 42 |
+
checkpoint_dir = os.path.dirname(abs_path)
|
| 43 |
+
if os.path.basename(checkpoint_dir) == 'checkpoints':
|
| 44 |
+
return os.path.dirname(checkpoint_dir)
|
| 45 |
+
return os.path.dirname(abs_path)
|
| 46 |
+
|
| 47 |
+
def _load_config(self, run_dir):
|
| 48 |
+
"""内部辅助方法:加载 config.json"""
|
| 49 |
+
config_path = os.path.join(run_dir, 'config.json')
|
| 50 |
+
if not os.path.exists(config_path):
|
| 51 |
+
raise FileNotFoundError(f"Config file not found at {config_path}")
|
| 52 |
+
|
| 53 |
+
with open(config_path, 'r') as f:
|
| 54 |
+
config = json.load(f)
|
| 55 |
+
return config
|
| 56 |
+
|
| 57 |
+
def _load_pose_stats(self):
|
| 58 |
+
"""内部辅助方法:加载 Pose 统计量"""
|
| 59 |
+
if self.dataset == "pp":
|
| 60 |
+
filename = "pose_stats.pt"
|
| 61 |
+
else:
|
| 62 |
+
filename = "t_pose_stats.pt"
|
| 63 |
+
|
| 64 |
+
file_path = os.path.join(self.stats_dir, filename)
|
| 65 |
+
if not os.path.exists(file_path):
|
| 66 |
+
raise FileNotFoundError(f"Stats file not found at {file_path}")
|
| 67 |
+
|
| 68 |
+
stats = torch.load(file_path, map_location=self.device)
|
| 69 |
+
return stats['mean'], stats['std']
|
| 70 |
+
|
| 71 |
+
def _load_model(self):
|
| 72 |
+
"""内部辅助方法:初始化并加载模型权重"""
|
| 73 |
+
unet = UNet1D(
|
| 74 |
+
pose_dim=72,
|
| 75 |
+
base_channels=self.config['base_channels'],
|
| 76 |
+
channel_multipliers=self.config['channel_multipliers'],
|
| 77 |
+
time_emb_dim=self.config['time_emb_dim'],
|
| 78 |
+
mid_structure=self.config['mid_structure'],
|
| 79 |
+
mid_num_heads=self.config['mid_num_heads']
|
| 80 |
+
).to(self.device)
|
| 81 |
+
|
| 82 |
+
model = FlowMatching(
|
| 83 |
+
model=unet,
|
| 84 |
+
sigma=0.0
|
| 85 |
+
).to(self.device)
|
| 86 |
+
|
| 87 |
+
checkpoint = torch.load(self.checkpoint_path, map_location=self.device)
|
| 88 |
+
|
| 89 |
+
if 'model_state_dict' in checkpoint:
|
| 90 |
+
model.load_state_dict(checkpoint['model_state_dict'])
|
| 91 |
+
else:
|
| 92 |
+
model.load_state_dict(checkpoint)
|
| 93 |
+
|
| 94 |
+
model.eval()
|
| 95 |
+
return model
|
| 96 |
+
|
| 97 |
+
def _unnormalize_pose(self, pose_norm):
|
| 98 |
+
"""内部辅助方法:反归一化"""
|
| 99 |
+
return pose_norm * self.pose_std + self.pose_mean
|
| 100 |
+
|
| 101 |
+
@torch.no_grad()
|
| 102 |
+
def sample(self, batch_size=1, num_steps=100, method="euler", verbose=False):
|
| 103 |
+
"""
|
| 104 |
+
采样方法。
|
| 105 |
+
每次调用只需执行推理,无需重新加载模型。
|
| 106 |
+
"""
|
| 107 |
+
samples_norm = self.model.sample(
|
| 108 |
+
sample_shape=(batch_size, 72),
|
| 109 |
+
device=self.device,
|
| 110 |
+
num_steps=num_steps,
|
| 111 |
+
method=method,
|
| 112 |
+
verbose=verbose
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
samples_raw = self._unnormalize_pose(samples_norm)
|
| 116 |
+
return samples_raw
|
| 117 |
+
|
| 118 |
+
# 使用示例
|
| 119 |
+
if __name__ == '__main__':
|
| 120 |
+
# 1. 实例化 Sampler (只加载一次模型,耗时操作在这里)
|
| 121 |
+
print("Initializing sampler...")
|
| 122 |
+
sampler = PoseSampler(
|
| 123 |
+
device='cpu',
|
| 124 |
+
dataset='pp'
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
# 2. 多次采样 (非常快)
|
| 128 |
+
print("Sampling batch 1...")
|
| 129 |
+
pose_batch_1 = sampler.sample(batch_size=1)
|
| 130 |
+
|
| 131 |
+
print("Sampling batch 2 (with different batch size)...")
|
| 132 |
+
pose_batch_2 = sampler.sample(batch_size=4) # 甚至可以改变 batch size
|
| 133 |
+
|
| 134 |
+
print(f"Batch 1 shape: {pose_batch_1.shape}")
|
| 135 |
+
print(f"Batch 2 shape: {pose_batch_2.shape}")
|
| 136 |
+
|
| 137 |
+
import pdb; pdb.set_trace()
|
| 138 |
+
|
src/sample_utils/trans_sampler.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import numpy as np
|
| 3 |
+
from scipy.stats import truncnorm
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def sample_transl4pp(batch_size, device):
|
| 7 |
+
"""
|
| 8 |
+
为 SMPL 模型生成全局平移参数 (transl),与数据集分布相似。
|
| 9 |
+
|
| 10 |
+
参数:
|
| 11 |
+
batch_size (int): 采样数量
|
| 12 |
+
device (torch.device): 计算设备 (e.g., 'cuda' or 'cpu')
|
| 13 |
+
|
| 14 |
+
返回:
|
| 15 |
+
transl (torch.Tensor): 形状为 (batch_size, 3) 的全局平移参数 [X, Y, Z]
|
| 16 |
+
"""
|
| 17 |
+
# X 坐标:均匀分布在 [0.45, 0.85]
|
| 18 |
+
x_min, x_max = 0.45, 0.85
|
| 19 |
+
x = torch.rand(batch_size, 1) * (x_max - x_min) + x_min # 均匀分布采样
|
| 20 |
+
|
| 21 |
+
# Y 坐标:均匀分布在 [1.05, 1.45]
|
| 22 |
+
y_min, y_max = 1.05, 1.45
|
| 23 |
+
y = torch.rand(batch_size, 1) * (y_max - y_min) + y_min # 均匀分布采样
|
| 24 |
+
|
| 25 |
+
# 卧姿对应
|
| 26 |
+
# Z 坐标:截断正态分布,均值 0.08,标准差 0.03,范围 [-0.02, 0.24]
|
| 27 |
+
z_mean, z_std = 0.08, 0.03
|
| 28 |
+
z_min, z_max = -0.02, 0.24
|
| 29 |
+
# 计算截断正态分布的标准化边界
|
| 30 |
+
a, b = (z_min - z_mean) / z_std, (z_max - z_mean) / z_std
|
| 31 |
+
# 使用 scipy 的 truncnorm 生成截断正态分布采样
|
| 32 |
+
z = truncnorm.rvs(a, b, loc=z_mean, scale=z_std, size=(batch_size, 1))
|
| 33 |
+
z = torch.tensor(z, dtype=torch.float32)
|
| 34 |
+
|
| 35 |
+
# # 站姿适应
|
| 36 |
+
# # Z 坐标:均匀分布在 [0.75, 0.85]
|
| 37 |
+
# z_min, z_max = 0.75, 0.85
|
| 38 |
+
# z = torch.rand(batch_size, 1) * (z_max - z_min) + z_min # 均匀分布采样
|
| 39 |
+
|
| 40 |
+
# 组合 X, Y, Z
|
| 41 |
+
transl = torch.cat([x, y, z], dim=1).to(device)
|
| 42 |
+
return transl
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def sample_transl4m(batch_size, device):
|
| 46 |
+
"""
|
| 47 |
+
为 SMPL 模型生成全局平移参数 (transl),与第二个数据集分布相似。
|
| 48 |
+
|
| 49 |
+
参数:
|
| 50 |
+
batch_size (int): 采样数量
|
| 51 |
+
device (torch.device): 计算设备 (e.g., 'cuda' or 'cpu')
|
| 52 |
+
|
| 53 |
+
返回:
|
| 54 |
+
transl (torch.Tensor): 形状为 (batch_size, 3) 的全局平移参数 [X, Y, Z]
|
| 55 |
+
"""
|
| 56 |
+
# X 坐标:截断正态分布,均值 0.030337209,标准差 0.059348222,范围 [-0.22423534, 0.3106258]
|
| 57 |
+
x_mean, x_std = 0.030337209, 0.059348222
|
| 58 |
+
# x_min, x_max = -0.22423534, 0.3106258
|
| 59 |
+
x_min, x_max = -0.05, 0.1
|
| 60 |
+
# 计算截断正态分布的标准化边界
|
| 61 |
+
a_x, b_x = (x_min - x_mean) / x_std, (x_max - x_mean) / x_std
|
| 62 |
+
# 使用 scipy 的 truncnorm 生成截断正态分布采样
|
| 63 |
+
x = truncnorm.rvs(a_x, b_x, loc=x_mean, scale=x_std, size=(batch_size, 1))
|
| 64 |
+
x = torch.tensor(x, dtype=torch.float32)
|
| 65 |
+
|
| 66 |
+
# Y 坐标:截断正态分布,均值 0.5841795,标准差 0.2390917,范围 [-0.09659827, 1.2293766]
|
| 67 |
+
y_mean, y_std = 0.5841795, 0.2390917
|
| 68 |
+
# y_min, y_max = -0.09659827, 1.2293766
|
| 69 |
+
y_min, y_max = 0.0, 1.2
|
| 70 |
+
# 计算截断正态分布的标准化边界
|
| 71 |
+
a_y, b_y = (y_min - y_mean) / y_std, (y_max - y_mean) / y_std
|
| 72 |
+
# 使用 scipy 的 truncnorm 生成截断正态分布采样
|
| 73 |
+
y = truncnorm.rvs(a_y, b_y, loc=y_mean, scale=y_std, size=(batch_size, 1))
|
| 74 |
+
y = torch.tensor(y, dtype=torch.float32)
|
| 75 |
+
|
| 76 |
+
# # Z 坐标:均匀分布在 [0.75, 0.85]
|
| 77 |
+
# z_min, z_max = 0.75, 0.85
|
| 78 |
+
# z = torch.rand(batch_size, 1) * (z_max - z_min) + z_min # 均匀分布采样
|
| 79 |
+
|
| 80 |
+
# z全零
|
| 81 |
+
z = torch.zeros((batch_size, 1))
|
| 82 |
+
|
| 83 |
+
# 组合 X, Y, Z
|
| 84 |
+
transl = torch.cat([x, y, z], dim=1).to(device)
|
| 85 |
+
return transl
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def sample_transl4t(batch_size, device):
|
| 89 |
+
"""
|
| 90 |
+
为 SMPL 模型生成全局平移参数 (transl),与第三个数据集分布相似。
|
| 91 |
+
|
| 92 |
+
参数:
|
| 93 |
+
batch_size (int): 采样数量
|
| 94 |
+
device (torch.device): 计算设备 (e.g., 'cuda' or 'cpu')
|
| 95 |
+
|
| 96 |
+
返回:
|
| 97 |
+
transl (torch.Tensor): 形状为 (batch_size, 3) 的全局平移参数 [X, Y, Z]
|
| 98 |
+
"""
|
| 99 |
+
# X 坐标:截断正态分布,均值 0.35497144,标准差 0.08321648,范围 [0.10660601, 0.72766024]
|
| 100 |
+
x_mean, x_std = 0.35497144, 0.08321648
|
| 101 |
+
# x_min, x_max = 0.10660601, 0.72766024
|
| 102 |
+
x_min, x_max = 0.15, 0.55
|
| 103 |
+
# 计算截断正态分布的标准化边界
|
| 104 |
+
a_x, b_x = (x_min - x_mean) / x_std, (x_max - x_mean) / x_std
|
| 105 |
+
# 使用 scipy 的 truncnorm 生成截断正态分布采样
|
| 106 |
+
x = truncnorm.rvs(a_x, b_x, loc=x_mean, scale=x_std, size=(batch_size, 1))
|
| 107 |
+
x = torch.tensor(x, dtype=torch.float32)
|
| 108 |
+
|
| 109 |
+
# Y 坐标:截断正态分布,均值 0.943629,标准差 0.0685662,范围 [0.7616181, 1.4328215]
|
| 110 |
+
y_mean, y_std = 0.943629, 0.0685662
|
| 111 |
+
# y_min, y_max = 0.7616181, 1.4328215
|
| 112 |
+
y_min, y_max = 0.8, 1.1
|
| 113 |
+
# 计算截断正态分布的标准化边界
|
| 114 |
+
a_y, b_y = (y_min - y_mean) / y_std, (y_max - y_mean) / y_std
|
| 115 |
+
# 使用 scipy 的 truncnorm 生成截断正态分布采样
|
| 116 |
+
y = truncnorm.rvs(a_y, b_y, loc=y_mean, scale=y_std, size=(batch_size, 1))
|
| 117 |
+
y = torch.tensor(y, dtype=torch.float32)
|
| 118 |
+
|
| 119 |
+
# Z 坐标:截断正态分布,均值 -0.15257776,标准差 0.055761524,范围 [-0.44515115, 0.021567477]
|
| 120 |
+
z_mean, z_std = -0.15257776, 0.055761524
|
| 121 |
+
# z_min, z_max = -0.44515115, 0.021567477
|
| 122 |
+
z_min, z_max = -0.18, -0.1
|
| 123 |
+
# 计算截断���态分布的标准化边界
|
| 124 |
+
a_z, b_z = (z_min - z_mean) / z_std, (z_max - z_mean) / z_std
|
| 125 |
+
# 使用 scipy 的 truncnorm 生成截断正态分布采样
|
| 126 |
+
z = truncnorm.rvs(a_z, b_z, loc=z_mean, scale=z_std, size=(batch_size, 1))
|
| 127 |
+
z = torch.tensor(z, dtype=torch.float32)
|
| 128 |
+
|
| 129 |
+
# 组合 X, Y, Z
|
| 130 |
+
transl = torch.cat([x, y, z], dim=1).to(device)
|
| 131 |
+
return transl
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
|