yolozyk commited on
Commit
de2377a
·
1 Parent(s): 98fa575

update sample tools

Browse files
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
+