OzzyGT HF Staff commited on
Commit
338c3c5
·
verified ·
1 Parent(s): f05ddae

Upload 13 files

Browse files
LICENSE ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ NVIDIA Open Model Agreement
2
+
3
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
4
+
5
+ Release Date: April 2, 2026
6
+
7
+ NVIDIA Works released under this License are intended to be used permissively and enable the further development of AI technologies. Subject to the terms of this License, NVIDIA confirms that:
8
+
9
+ • Works are commercially usable.
10
+
11
+ • You are free to create and distribute Derivative Works.
12
+
13
+ • NVIDIA does not claim ownership to any outputs generated using the Works or Derivative Works.
14
+
15
+ By using, reproducing, modifying, distributing, performing or displaying any portion or element of the Works or Derivative Works, or otherwise accepting the terms of this License, you agree to be bound by this License.
16
+
17
+ 1. Definitions.
18
+
19
+ “License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 10 of this document.
20
+
21
+ “Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ “You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
24
+
25
+ “Work” shall mean the work of authorship, including machine learning model, software, checkpoints, learnt weights, algorithms, parameters, configuration files and documentation, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
26
+
27
+ “Derivative Works” shall mean any work, whether in source or object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
28
+
29
+ 2. Grant of License. Subject to the terms and conditions of this License, NVIDIA hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in source or object form.
30
+
31
+ If You institute patent or copyright litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or an output from the Work constitutes direct or contributory patent or copyright infringement, then any licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
32
+
33
+ 3. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in source or object form, provided that You meet the following conditions:
34
+
35
+ a. You must give any other recipients of the Work a copy of this License; and
36
+
37
+ b. You must retain, in the source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
38
+
39
+ c. If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the following attribution notice within a “Notice” text file with such copies and the following statement: “Licensed by NVIDIA Corporation under the NVIDIA Open Model Agreement.”
40
+
41
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
42
+
43
+ 4. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of NVIDIA, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
44
+
45
+ 5. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, NVIDIA provides the Work on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
46
+
47
+ 6. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall NVIDIA be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work, a Derivative Work or an output from the Work or Derivative Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if NVIDIA has been advised of the possibility of such damages.
48
+
49
+ 7. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of NVIDIA, and only if You agree to indemnify, defend, and hold NVIDIA harmless for any liability incurred by, or claims asserted against, NVIDIA by reason of your accepting any such warranty or additional liability. You will indemnify and hold harmless NVIDIA from and against any claim by any third party arising out of or related to your use or distribution of the Works, Derivative Works thereof, or output from the Works or Derivative Works.
50
+
51
+ 8. Feedback. NVIDIA appreciates your feedback, and You agree that NVIDIA may use it without restriction or compensation to You.
52
+
53
+ 9. Governing Law. This Agreement will be governed in all respects by the laws of the United States and the laws of the State of Delaware, without regard to conflict of laws principles or the United Nations Convention on Contracts for the International Sale of Goods. The state and federal courts residing in Santa Clara County, California will have exclusive jurisdiction over any dispute or claim arising out of or related to this Agreement, and the parties irrevocably consent to personal jurisdiction and venue in those courts; except that, either party may apply for injunctive remedies or an equivalent type of urgent legal relief in any jurisdiction.
54
+
55
+ 10. Trade and Compliance. You agree to comply with all applicable export, import, trade and economic sanctions laws and regulations, as amended, including without limitation U.S. Export Administration Regulations and Office of Foreign Assets Control regulations. These laws include restrictions on destinations, end-users and end-use.
56
+
57
+ (v. March 9, 2026)
58
+
59
+ END OF TERMS AND CONDITIONS
NOTICE ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Licensed by NVIDIA Corporation under the NVIDIA Open Model Agreement.
2
+ Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+
4
+ The agreement is included as LICENSE and published at
5
+ https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-agreement/
6
+
7
+ This repository repacks the SOMA checkpoints of nvidia/MotionBricks (soma/soma-v6-nova-4n8g-ea43dd8-fp32-v1) for
8
+ Diffusers. The float32 tensors of the root model, the pose model and the decoder of the pose VQ-VAE are unchanged,
9
+ renamed for the Diffusers model code next to them; the fused attention input projections were split into query, key
10
+ and value. The VQ-VAE encoder and the training-only codebook statistics were left out. The normalization statistics and
11
+ the neutral joint positions of the skeleton come from the same package and are stored as buffers, with the standard
12
+ deviations as sqrt(std^2 + 1e-5), as in NVIDIA's exported ONNX model.
13
+
14
+ The model code is new, Apache-2.0, and written by The HuggingFace Team.
README.md ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ license: other
5
+ license_name: nvidia-open-model-agreement
6
+ license_link: LICENSE
7
+ base_model:
8
+ - nvidia/MotionBricks
9
+ library_name: diffusers
10
+ tags:
11
+ - motionbricks
12
+ - modular-diffusers
13
+ - motion-generation
14
+ - motion-inbetweening
15
+ - soma
16
+ ---
17
+
18
+ # MotionBricks SOMA
19
+
20
+ <video controls loop muted playsinline src="https://huggingface.co/datasets/OzzyGT/diffusers-examples/resolve/main/motionbricks_soma_diffusers/motionbricks_soma_run_stop.mp4"></video>
21
+
22
+ *The start of a Kimodo run, then the 30 frames generated with the code below, then the standing end of another Kimodo take, placed 1.4 m ahead. The generated frames are drawn on SOMA77 with a relaxed finger pose. At normal playback speed; the cameras follow the root.*
23
+
24
+ This is the SOMA model of [nvidia/MotionBricks](https://huggingface.co/nvidia/MotionBricks), repacked into the Diffusers format in float32. It runs as a Modular Diffusers pipeline with the [MotionBricks SOMA custom blocks](https://huggingface.co/OzzyGT/motionbricks_soma_custom_blocks) and generates the transition from the last poses of one motion to target poses in one forward pass, choosing the length of the transition itself.
25
+
26
+ The three components work together:
27
+
28
+ - `root_model` (40M parameters) chooses the length and predicts the root trajectory.
29
+ - `pose_model` (137M parameters) predicts the pose codes of every token of four frames.
30
+ - `pose_decoder` (12M parameters) decodes the codes to frames.
31
+
32
+ Note: You need `diffusers>=0.41.0`.
33
+
34
+ ## Usage
35
+
36
+ The custom blocks load the model code shipped in this repository, hence `trust_remote_code` and `trust_components_code`.
37
+
38
+ **Note:** both flags run Python code downloaded from the Hub: `trust_remote_code=True` runs the code of the blocks repository, and `trust_components_code=True` runs the code shipped in the `components_repo`. Only use them with repositories you trust, and review their code before passing different ones.
39
+
40
+ ```python
41
+ import numpy as np
42
+ import torch
43
+ from diffusers import ModularPipelineBlocks
44
+ from huggingface_hub import hf_hub_download
45
+
46
+ blocks = ModularPipelineBlocks.from_pretrained(
47
+ "OzzyGT/motionbricks_soma_custom_blocks",
48
+ trust_remote_code=True,
49
+ components_repo="OzzyGT/motionbricks_soma_diffusers",
50
+ trust_components_code=True,
51
+ )
52
+ pipe = blocks.init_pipeline()
53
+ pipe.load_components(dtype=torch.float32)
54
+ pipe.to("cuda")
55
+
56
+ first = hf_hub_download("OzzyGT/diffusers-examples", "motionbricks_soma_diffusers/first.npz", repo_type="dataset")
57
+ second = hf_hub_download("OzzyGT/diffusers-examples", "motionbricks_soma_diffusers/second.npz", repo_type="dataset")
58
+ context = dict(np.load(first))
59
+ target = dict(np.load(second))
60
+
61
+ motion = pipe(context=context, target=target, target_offset=(1.4, 0.0), num_target_poses=4, output="motion")[0]
62
+ np.savez_compressed("transition.npz", **{key: value.cpu().numpy() for key, value in motion.items()})
63
+ ```
64
+
65
+ Both clips come from [Kimodo SOMA Modular](https://huggingface.co/OzzyGT/Kimodo_SOMA_Modular): the start of a run, and a standing pose that `target_offset` places 1.4 m ahead of it. The model accepts SOMA30 or SOMA77 poses at 30 FPS and returns a SOMA30 motion; here it brings the run to a stop in 30 frames. See the [custom blocks](https://huggingface.co/OzzyGT/motionbricks_soma_custom_blocks) for the options.
66
+
67
+ Float32 reproduces NVIDIA's ONNX graph with PyTorch's default settings; float16 halves the memory with small drift, while bfloat16 changes the motion noticeably.
68
+
69
+ ## Licenses
70
+
71
+ The model weights are licensed under the [NVIDIA Open Model Agreement](LICENSE), the same as the original. The model code in this repository is Apache-2.0. See [NOTICE](NOTICE) for what was changed.
modular_model_index.json ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "ModularPipeline",
3
+ "_blocks_class_name": "MotionBricksBlocks",
4
+ "root_model": [
5
+ "root_motionbricks",
6
+ "MotionBricksRootModel",
7
+ {
8
+ "pretrained_model_name_or_path": null,
9
+ "revision": null,
10
+ "subfolder": "root_model",
11
+ "type_hint": [
12
+ "root_motionbricks",
13
+ "MotionBricksRootModel"
14
+ ],
15
+ "variant": null
16
+ }
17
+ ],
18
+ "pose_model": [
19
+ "transformer_motionbricks",
20
+ "MotionBricksPoseTransformer",
21
+ {
22
+ "pretrained_model_name_or_path": null,
23
+ "revision": null,
24
+ "subfolder": "pose_model",
25
+ "type_hint": [
26
+ "transformer_motionbricks",
27
+ "MotionBricksPoseTransformer"
28
+ ],
29
+ "variant": null
30
+ }
31
+ ],
32
+ "pose_decoder": [
33
+ "decoder_motionbricks",
34
+ "MotionBricksPoseDecoder",
35
+ {
36
+ "pretrained_model_name_or_path": null,
37
+ "revision": null,
38
+ "subfolder": "pose_decoder",
39
+ "type_hint": [
40
+ "decoder_motionbricks",
41
+ "MotionBricksPoseDecoder"
42
+ ],
43
+ "variant": null
44
+ }
45
+ ]
46
+ }
pose_decoder/config.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "MotionBricksPoseDecoder",
3
+ "_diffusers_version": "0.41.0",
4
+ "auto_map": {
5
+ "AutoModel": "decoder_motionbricks.MotionBricksPoseDecoder"
6
+ },
7
+ "code_embed_dim": 32,
8
+ "codebook_size": 16,
9
+ "depth": 4,
10
+ "dilation_growth_rate": 3,
11
+ "feature_dim": 365,
12
+ "frames_per_token": 4,
13
+ "max_tokens": 16,
14
+ "num_heads_codebook": 5,
15
+ "num_joints": 30,
16
+ "pose_dim": 268,
17
+ "velocity_dim": 2,
18
+ "width": 512
19
+ }
pose_decoder/decoder_motionbricks.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+ import torch.nn.functional as F
20
+
21
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
22
+ from diffusers.models.modeling_utils import ModelMixin
23
+ from diffusers.utils import BaseOutput
24
+
25
+
26
+ class MotionBricksConv1d(nn.Conv1d):
27
+ """Stride-1 `nn.Conv1d` computed as one matrix product over the shifted inputs, faster than cuDNN at these
28
+ sizes."""
29
+
30
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
31
+ kernel, dilation, padding = self.kernel_size[0], self.dilation[0], self.padding[0]
32
+ length = hidden_states.shape[-1]
33
+ if kernel > 1:
34
+ padded = F.pad(hidden_states, (padding, padding))
35
+ shifted = [padded[..., i * dilation : i * dilation + length] for i in range(kernel)]
36
+ hidden_states = torch.stack(shifted, dim=2).flatten(1, 2)
37
+ weight = self.weight.reshape(self.out_channels, -1)
38
+ return torch.matmul(weight, hidden_states) + self.bias[:, None]
39
+
40
+
41
+ class MotionBricksResnetBlock(nn.Module):
42
+ def __init__(self, channels: int, dilation: int):
43
+ super().__init__()
44
+ self.conv1 = MotionBricksConv1d(channels, channels, 3, padding=dilation, dilation=dilation)
45
+ self.conv2 = MotionBricksConv1d(channels, channels, 1)
46
+
47
+ def forward(self, hidden_states: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
48
+ residual = hidden_states * mask
49
+ hidden_states = self.conv1(F.relu(residual) * mask)
50
+ hidden_states = self.conv2(F.relu(hidden_states) * mask)
51
+ return (hidden_states + residual) * mask
52
+
53
+
54
+ class MotionBricksUpBlock(nn.Module):
55
+ def __init__(self, channels: int, depth: int, dilation_growth_rate: int):
56
+ super().__init__()
57
+ dilations = [dilation_growth_rate**i for i in reversed(range(depth))]
58
+ self.resnets = nn.ModuleList([MotionBricksResnetBlock(channels, dilation) for dilation in dilations])
59
+ self.conv = MotionBricksConv1d(channels, channels, 3, padding=1)
60
+
61
+
62
+ class MotionBricksConditionedDecoder(nn.Module):
63
+ """Dilated 1D convolution decoder that upsamples tokens to frames, conditioned on per-frame targets and
64
+ external features."""
65
+
66
+ def __init__(
67
+ self,
68
+ in_channels: int,
69
+ out_channels: int,
70
+ target_channels: int,
71
+ external_channels: int,
72
+ width: int = 512,
73
+ depth: int = 4,
74
+ dilation_growth_rate: int = 3,
75
+ num_tokens: int = 16,
76
+ num_frames: int = 64,
77
+ ):
78
+ super().__init__()
79
+ self.width = width
80
+ self.num_frames = num_frames
81
+ lengths = [num_tokens, num_tokens * 2]
82
+ self.conv_in = MotionBricksConv1d(in_channels, width, 3, padding=1)
83
+ self.target_projections = nn.ModuleList(
84
+ [nn.Linear(target_channels, width * length // num_frames) for length in lengths]
85
+ )
86
+ self.external_projections = nn.ModuleList(
87
+ [nn.Linear(width + external_channels * num_frames // length, width) for length in lengths]
88
+ )
89
+ self.up_blocks = nn.ModuleList([MotionBricksUpBlock(width, depth, dilation_growth_rate) for _ in lengths])
90
+ self.conv_mid = MotionBricksConv1d(width, width, 3, padding=1)
91
+ self.conv_out = MotionBricksConv1d(width, out_channels, 3, padding=1)
92
+
93
+ def forward(
94
+ self,
95
+ hidden_states: torch.Tensor,
96
+ token_mask: torch.Tensor,
97
+ target: torch.Tensor,
98
+ target_mask: torch.Tensor,
99
+ external: torch.Tensor,
100
+ ) -> torch.Tensor:
101
+ """
102
+ Args:
103
+ hidden_states: `(batch_size, in_channels, num_tokens)` token features.
104
+ token_mask: `(batch_size, num_tokens)` float mask of the valid tokens.
105
+ target: `(batch_size, num_frames, target_channels)` per-frame targets.
106
+ target_mask: `(batch_size, num_frames)` boolean mask of the frames with a target.
107
+ external: `(batch_size, num_frames, external_channels)` per-frame conditioning.
108
+
109
+ Returns:
110
+ `(batch_size, out_channels, num_frames)` frame features.
111
+ """
112
+ batch_size = hidden_states.shape[0]
113
+ mask = token_mask[:, None]
114
+ hidden_states = F.relu(self.conv_in(hidden_states * mask))
115
+ for block, target_projection, external_projection in zip(
116
+ self.up_blocks, self.target_projections, self.external_projections
117
+ ):
118
+ length = hidden_states.shape[-1]
119
+ projected = F.relu(target_projection(target))
120
+ grouped = hidden_states.transpose(1, 2).reshape(batch_size, self.num_frames, -1)
121
+ grouped = torch.where(target_mask[..., None], projected, grouped)
122
+ grouped = grouped.reshape(batch_size, length, self.width)
123
+ conditioned = torch.cat([grouped, external.reshape(batch_size, length, -1)], dim=-1)
124
+ hidden_states = F.relu(external_projection(conditioned).transpose(1, 2))
125
+ for resnet in block.resnets:
126
+ hidden_states = resnet(hidden_states, mask)
127
+ hidden_states = F.interpolate(hidden_states, scale_factor=2, mode="nearest")
128
+ mask = mask.repeat_interleave(2, dim=-1)
129
+ hidden_states = block.conv(hidden_states * mask)
130
+ hidden_states = F.relu(self.conv_mid(hidden_states * mask)) * mask
131
+ return self.conv_out(hidden_states) * mask
132
+
133
+
134
+ @dataclass
135
+ class MotionBricksPoseDecoderOutput(BaseOutput):
136
+ """
137
+ Args:
138
+ features (`torch.Tensor` of shape `(batch_size, num_frames, feature_dim)`):
139
+ The normalized features of every frame.
140
+ """
141
+
142
+ features: torch.Tensor
143
+
144
+
145
+ class MotionBricksPoseDecoder(ModelMixin, ConfigMixin):
146
+ """Decoder of the pose VQ-VAE: looks up the codes of every token and decodes them to normalized frame features,
147
+ conditioned on the given poses and the root velocity.
148
+
149
+ The features of a frame are the local root (heading rate, ground velocity, height) followed by the body: joint
150
+ positions relative to the root, 6D global rotations, joint velocities and foot contacts. Their normalization
151
+ statistics and the neutral joint positions of the skeleton are stored as buffers.
152
+ """
153
+
154
+ _no_split_modules = ["MotionBricksConditionedDecoder"]
155
+ _repeated_blocks = ["MotionBricksResnetBlock"]
156
+
157
+ @register_to_config
158
+ def __init__(
159
+ self,
160
+ num_heads_codebook: int = 5,
161
+ codebook_size: int = 16,
162
+ code_embed_dim: int = 32,
163
+ pose_dim: int = 268,
164
+ velocity_dim: int = 2,
165
+ feature_dim: int = 365,
166
+ num_joints: int = 30,
167
+ width: int = 512,
168
+ depth: int = 4,
169
+ dilation_growth_rate: int = 3,
170
+ frames_per_token: int = 4,
171
+ max_tokens: int = 16,
172
+ ):
173
+ super().__init__()
174
+ self.codebook = nn.Parameter(torch.zeros(num_heads_codebook, codebook_size, code_embed_dim))
175
+ self.decoder = MotionBricksConditionedDecoder(
176
+ num_heads_codebook * code_embed_dim,
177
+ feature_dim,
178
+ target_channels=pose_dim,
179
+ external_channels=velocity_dim,
180
+ width=width,
181
+ depth=depth,
182
+ dilation_growth_rate=dilation_growth_rate,
183
+ num_tokens=max_tokens,
184
+ num_frames=max_tokens * frames_per_token,
185
+ )
186
+ self.register_buffer("feature_mean", torch.zeros(feature_dim))
187
+ self.register_buffer("feature_std", torch.ones(feature_dim))
188
+ self.register_buffer("neutral_joints", torch.zeros(num_joints, 3))
189
+
190
+ def denormalize(self, features: torch.Tensor) -> torch.Tensor:
191
+ # The buffers can sit on another device while the model is offloaded.
192
+ std = self.feature_std.to(features.device, features.dtype)
193
+ return features * std + self.feature_mean.to(features.device, features.dtype)
194
+
195
+ def forward(
196
+ self,
197
+ codes: torch.LongTensor,
198
+ num_tokens: torch.Tensor,
199
+ pose: torch.Tensor,
200
+ has_pose: torch.Tensor,
201
+ velocity: torch.Tensor,
202
+ return_dict: bool = True,
203
+ ) -> MotionBricksPoseDecoderOutput | tuple:
204
+ """
205
+ Args:
206
+ codes (`torch.LongTensor` of shape `(batch_size, max_tokens, num_heads_codebook)`):
207
+ The pose code of every token and head.
208
+ num_tokens (`torch.LongTensor` of shape `(batch_size,)`):
209
+ The motion length in tokens.
210
+ pose (`torch.Tensor` of shape `(batch_size, num_frames, pose_dim)`):
211
+ Normalized pose features on the frame timeline.
212
+ has_pose (`torch.BoolTensor` of shape `(batch_size, num_frames)`):
213
+ The frames whose pose is given.
214
+ velocity (`torch.Tensor` of shape `(batch_size, num_frames, velocity_dim)`):
215
+ Normalized ground velocity of the root.
216
+ return_dict (`bool`, defaults to `True`):
217
+ Whether to return a `MotionBricksPoseDecoderOutput` instead of a tuple.
218
+
219
+ Returns:
220
+ `MotionBricksPoseDecoderOutput` or `tuple`: The normalized frame features; a one-element tuple when
221
+ `return_dict` is `False`.
222
+ """
223
+ heads = torch.arange(self.config.num_heads_codebook, device=codes.device)
224
+ embeds = self.codebook[heads, codes].flatten(2)
225
+ token_mask = (torch.arange(codes.shape[1], device=codes.device)[None] < num_tokens[:, None]).to(embeds.dtype)
226
+ features = self.decoder(embeds.transpose(1, 2), token_mask, pose, has_pose, velocity).transpose(1, 2)
227
+
228
+ if not return_dict:
229
+ return (features,)
230
+ return MotionBricksPoseDecoderOutput(features=features)
pose_decoder/diffusion_pytorch_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d3ff3958e82b62e409d116276685cd523b7162793224e3af9aa4fbdf66ceebc4
3
+ size 48817860
pose_model/config.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "MotionBricksPoseTransformer",
3
+ "_diffusers_version": "0.41.0",
4
+ "auto_map": {
5
+ "AutoModel": "transformer_motionbricks.MotionBricksPoseTransformer"
6
+ },
7
+ "code_embed_dim": 32,
8
+ "codebook_size": 16,
9
+ "ff_dim": 2048,
10
+ "frames_per_token": 4,
11
+ "hidden_dim": 1024,
12
+ "length_embed_dim": 128,
13
+ "max_tokens": 16,
14
+ "min_tokens": 6,
15
+ "num_heads": 16,
16
+ "num_heads_codebook": 5,
17
+ "num_layers": 16,
18
+ "pose_dim": 268,
19
+ "root_dim": 4,
20
+ "root_embed_dim": 256,
21
+ "token_dim": 640
22
+ }
pose_model/diffusion_pytorch_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f7498f8f8ca90c098370c2e4c7c809cc590d83847ac8d41b2f76af278ba06aa2
3
+ size 546109888
pose_model/transformer_motionbricks.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+ import torch.nn.functional as F
20
+
21
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
22
+ from diffusers.models.attention import AttentionMixin, AttentionModuleMixin
23
+ from diffusers.models.attention_dispatch import dispatch_attention_fn
24
+ from diffusers.models.modeling_utils import ModelMixin
25
+ from diffusers.utils import BaseOutput
26
+
27
+
28
+ class MotionBricksMLP(nn.Module):
29
+ """Two linear layers, each followed by LeakyReLU(0.01), then a linear projection."""
30
+
31
+ def __init__(self, in_features: int, hidden_features: int, out_features: int):
32
+ super().__init__()
33
+ self.layers = nn.ModuleList(
34
+ [nn.Linear(in_features, hidden_features), nn.Linear(hidden_features, hidden_features)]
35
+ )
36
+ self.proj_out = nn.Linear(hidden_features, out_features)
37
+
38
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
39
+ for layer in self.layers:
40
+ hidden_states = F.leaky_relu(layer(hidden_states), 0.01)
41
+ return self.proj_out(hidden_states)
42
+
43
+
44
+ class MotionBricksAttnProcessor:
45
+ _attention_backend = None
46
+ _parallel_config = None
47
+
48
+ def __call__(
49
+ self,
50
+ attn: "MotionBricksAttention",
51
+ hidden_states: torch.Tensor,
52
+ attention_mask: torch.Tensor | None = None,
53
+ ) -> torch.Tensor:
54
+ batch_size, seq_len, _ = hidden_states.shape
55
+ shape = (batch_size, seq_len, attn.heads, -1)
56
+ query = attn.to_q(hidden_states).view(shape)
57
+ key = attn.to_k(hidden_states).view(shape)
58
+ value = attn.to_v(hidden_states).view(shape)
59
+ hidden_states = dispatch_attention_fn(
60
+ query,
61
+ key,
62
+ value,
63
+ attn_mask=attention_mask,
64
+ backend=self._attention_backend,
65
+ parallel_config=self._parallel_config,
66
+ )
67
+ hidden_states = hidden_states.flatten(2, 3).to(query.dtype)
68
+ return attn.to_out[0](hidden_states)
69
+
70
+
71
+ class MotionBricksAttention(nn.Module, AttentionModuleMixin):
72
+ """Multi-head self-attention. `attention_mask` is a boolean `(batch_size, 1, 1, seq_len)` mask of the keys to
73
+ attend to."""
74
+
75
+ _default_processor_cls = MotionBricksAttnProcessor
76
+ _available_processors = [MotionBricksAttnProcessor]
77
+
78
+ def __init__(self, dim: int, heads: int):
79
+ super().__init__()
80
+ self.heads = heads
81
+ self.to_q = nn.Linear(dim, dim)
82
+ self.to_k = nn.Linear(dim, dim)
83
+ self.to_v = nn.Linear(dim, dim)
84
+ self.to_out = nn.ModuleList([nn.Linear(dim, dim), nn.Dropout(0.0)])
85
+ self.set_processor(MotionBricksAttnProcessor())
86
+
87
+ def forward(self, hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None) -> torch.Tensor:
88
+ return self.processor(self, hidden_states, attention_mask)
89
+
90
+
91
+ class MotionBricksTransformerLayer(nn.Module):
92
+ """Post-norm transformer encoder layer with a ReLU feed-forward."""
93
+
94
+ def __init__(self, dim: int, heads: int, ff_dim: int):
95
+ super().__init__()
96
+ self.attn = MotionBricksAttention(dim, heads)
97
+ self.norm1 = nn.LayerNorm(dim)
98
+ self.ff_in = nn.Linear(dim, ff_dim)
99
+ self.ff_out = nn.Linear(ff_dim, dim)
100
+ self.norm2 = nn.LayerNorm(dim)
101
+
102
+ def forward(self, hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None) -> torch.Tensor:
103
+ hidden_states = self.norm1(hidden_states + self.attn(hidden_states, attention_mask))
104
+ return self.norm2(hidden_states + self.ff_out(F.relu(self.ff_in(hidden_states))))
105
+
106
+
107
+ def key_mask(num_tokens: torch.Tensor, max_tokens: int, prefix: int = 0) -> torch.Tensor:
108
+ """Boolean `(batch_size, 1, 1, prefix + max_tokens)` attention mask: the `prefix` positions, then the first
109
+ `num_tokens` of `max_tokens` token positions."""
110
+ positions = torch.arange(max_tokens, device=num_tokens.device)
111
+ valid = positions[None] < num_tokens[:, None]
112
+ valid = F.pad(valid, (prefix, 0), value=True)
113
+ return valid[:, None, None]
114
+
115
+
116
+ @dataclass
117
+ class MotionBricksPoseTransformerOutput(BaseOutput):
118
+ """
119
+ Args:
120
+ logits (`torch.Tensor` of shape `(batch_size, max_tokens, num_heads_codebook, codebook_size)`):
121
+ The code logits of every token and head.
122
+ """
123
+
124
+ logits: torch.Tensor
125
+
126
+
127
+ class MotionBricksPoseTransformer(ModelMixin, ConfigMixin, AttentionMixin):
128
+ """Masked token transformer that predicts the pose codes of every token in one pass, from the given poses, the
129
+ root of every frame and the length of the motion. Each token covers `frames_per_token` frames and has one code
130
+ per head."""
131
+
132
+ _no_split_modules = ["MotionBricksTransformerLayer"]
133
+ _repeated_blocks = ["MotionBricksTransformerLayer"]
134
+ _skip_layerwise_casting_patterns = ["norm", "embed"]
135
+
136
+ @register_to_config
137
+ def __init__(
138
+ self,
139
+ pose_dim: int = 268,
140
+ root_dim: int = 4,
141
+ num_heads_codebook: int = 5,
142
+ codebook_size: int = 16,
143
+ code_embed_dim: int = 32,
144
+ token_dim: int = 640,
145
+ root_embed_dim: int = 256,
146
+ length_embed_dim: int = 128,
147
+ hidden_dim: int = 1024,
148
+ num_heads: int = 16,
149
+ ff_dim: int = 2048,
150
+ num_layers: int = 16,
151
+ frames_per_token: int = 4,
152
+ min_tokens: int = 6,
153
+ max_tokens: int = 16,
154
+ ):
155
+ super().__init__()
156
+ code_dim = num_heads_codebook * code_embed_dim
157
+ # Each head has codebook_size codes and a mask code.
158
+ self.code_embed = nn.Embedding(num_heads_codebook * (codebook_size + 1), code_embed_dim)
159
+ self.code_proj = MotionBricksMLP(code_dim, token_dim, token_dim)
160
+ self.pose_proj = nn.Linear(pose_dim, token_dim // frames_per_token)
161
+ self.root_proj = nn.Linear(root_dim * frames_per_token, root_embed_dim)
162
+ self.length_embed = nn.Embedding(max_tokens - min_tokens + 1, length_embed_dim)
163
+ self.proj_in = nn.Linear(token_dim + root_embed_dim + length_embed_dim, hidden_dim)
164
+ self.position_embed = nn.Parameter(torch.zeros(max_tokens, hidden_dim))
165
+ self.layers = nn.ModuleList(
166
+ [MotionBricksTransformerLayer(hidden_dim, num_heads, ff_dim) for _ in range(num_layers)]
167
+ )
168
+ self.proj_out = nn.Linear(hidden_dim, num_heads_codebook * codebook_size)
169
+
170
+ def forward(
171
+ self,
172
+ pose: torch.Tensor,
173
+ has_pose: torch.Tensor,
174
+ root: torch.Tensor,
175
+ num_tokens: torch.Tensor,
176
+ return_dict: bool = True,
177
+ ) -> MotionBricksPoseTransformerOutput | tuple:
178
+ """
179
+ Args:
180
+ pose (`torch.Tensor` of shape `(batch_size, num_frames, pose_dim)`):
181
+ Normalized pose features on the frame timeline.
182
+ has_pose (`torch.BoolTensor` of shape `(batch_size, num_frames)`):
183
+ The frames whose pose is given.
184
+ root (`torch.Tensor` of shape `(batch_size, num_frames, root_dim)`):
185
+ Normalized root of every frame: ground position and the cosine and sine of the heading.
186
+ num_tokens (`torch.LongTensor` of shape `(batch_size,)`):
187
+ The motion length in tokens.
188
+ return_dict (`bool`, defaults to `True`):
189
+ Whether to return a `MotionBricksPoseTransformerOutput` instead of a tuple.
190
+
191
+ Returns:
192
+ `MotionBricksPoseTransformerOutput` or `tuple`: The code logits; a one-element tuple when `return_dict` is
193
+ `False`.
194
+ """
195
+ config = self.config
196
+ batch_size = pose.shape[0]
197
+ heads, size = config.num_heads_codebook, config.codebook_size
198
+ mask_codes = size + torch.arange(heads, device=pose.device) * (size + 1)
199
+ codes = self.code_embed(mask_codes).flatten()
200
+ codes = self.code_proj(codes).expand(batch_size, config.max_tokens, -1)
201
+ codes = codes.reshape(batch_size, -1, self.pose_proj.out_features)
202
+ has_pose = has_pose[..., None].to(pose.dtype)
203
+ frames = self.pose_proj(pose) * has_pose + codes * (1 - has_pose)
204
+ frames = frames.reshape(batch_size, config.max_tokens, -1)
205
+ root = self.root_proj(root.reshape(batch_size, config.max_tokens, -1))
206
+ length = self.length_embed(num_tokens - config.min_tokens)[:, None].expand(-1, config.max_tokens, -1)
207
+ hidden_states = F.relu(self.proj_in(torch.cat([frames, root, length], dim=-1))) + self.position_embed
208
+ mask = key_mask(num_tokens, config.max_tokens)
209
+ for layer in self.layers:
210
+ hidden_states = layer(hidden_states, mask)
211
+ logits = self.proj_out(hidden_states).unflatten(-1, (heads, size))
212
+
213
+ if not return_dict:
214
+ return (logits,)
215
+ return MotionBricksPoseTransformerOutput(logits=logits)
root_model/config.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "MotionBricksRootModel",
3
+ "_diffusers_version": "0.41.0",
4
+ "auto_map": {
5
+ "AutoModel": "root_motionbricks.MotionBricksRootModel"
6
+ },
7
+ "context_frames": 4,
8
+ "decoder_depth": 4,
9
+ "decoder_dilation_growth_rate": 3,
10
+ "ff_dim": 2048,
11
+ "frames_per_token": 4,
12
+ "global_root_dim": 5,
13
+ "hidden_dim": 512,
14
+ "local_root_dim": 4,
15
+ "max_tokens": 16,
16
+ "min_tokens": 6,
17
+ "num_heads": 16,
18
+ "num_shared_layers": 6,
19
+ "num_token_layers": 2,
20
+ "pose_dim": 268,
21
+ "pose_embed_dim": 256,
22
+ "root_embed_dim": 64,
23
+ "target_frames": 4
24
+ }
root_model/diffusion_pytorch_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:78f99fbeca2df6f73320b2aa81befda1986b571322ed98fb9b426a40b76586e3
3
+ size 161697028
root_model/root_motionbricks.py ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+ import torch.nn.functional as F
20
+
21
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
22
+ from diffusers.models.attention import AttentionMixin, AttentionModuleMixin
23
+ from diffusers.models.attention_dispatch import dispatch_attention_fn
24
+ from diffusers.models.modeling_utils import ModelMixin
25
+ from diffusers.utils import BaseOutput
26
+
27
+
28
+ class MotionBricksMLP(nn.Module):
29
+ """Two linear layers, each followed by LeakyReLU(0.01), then a linear projection."""
30
+
31
+ def __init__(self, in_features: int, hidden_features: int, out_features: int):
32
+ super().__init__()
33
+ self.layers = nn.ModuleList(
34
+ [nn.Linear(in_features, hidden_features), nn.Linear(hidden_features, hidden_features)]
35
+ )
36
+ self.proj_out = nn.Linear(hidden_features, out_features)
37
+
38
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
39
+ for layer in self.layers:
40
+ hidden_states = F.leaky_relu(layer(hidden_states), 0.01)
41
+ return self.proj_out(hidden_states)
42
+
43
+
44
+ class MotionBricksAttnProcessor:
45
+ _attention_backend = None
46
+ _parallel_config = None
47
+
48
+ def __call__(
49
+ self,
50
+ attn: "MotionBricksAttention",
51
+ hidden_states: torch.Tensor,
52
+ attention_mask: torch.Tensor | None = None,
53
+ ) -> torch.Tensor:
54
+ batch_size, seq_len, _ = hidden_states.shape
55
+ shape = (batch_size, seq_len, attn.heads, -1)
56
+ query = attn.to_q(hidden_states).view(shape)
57
+ key = attn.to_k(hidden_states).view(shape)
58
+ value = attn.to_v(hidden_states).view(shape)
59
+ hidden_states = dispatch_attention_fn(
60
+ query,
61
+ key,
62
+ value,
63
+ attn_mask=attention_mask,
64
+ backend=self._attention_backend,
65
+ parallel_config=self._parallel_config,
66
+ )
67
+ hidden_states = hidden_states.flatten(2, 3).to(query.dtype)
68
+ return attn.to_out[0](hidden_states)
69
+
70
+
71
+ class MotionBricksAttention(nn.Module, AttentionModuleMixin):
72
+ """Multi-head self-attention. `attention_mask` is a boolean `(batch_size, 1, 1, seq_len)` mask of the keys to
73
+ attend to."""
74
+
75
+ _default_processor_cls = MotionBricksAttnProcessor
76
+ _available_processors = [MotionBricksAttnProcessor]
77
+
78
+ def __init__(self, dim: int, heads: int):
79
+ super().__init__()
80
+ self.heads = heads
81
+ self.to_q = nn.Linear(dim, dim)
82
+ self.to_k = nn.Linear(dim, dim)
83
+ self.to_v = nn.Linear(dim, dim)
84
+ self.to_out = nn.ModuleList([nn.Linear(dim, dim), nn.Dropout(0.0)])
85
+ self.set_processor(MotionBricksAttnProcessor())
86
+
87
+ def forward(self, hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None) -> torch.Tensor:
88
+ return self.processor(self, hidden_states, attention_mask)
89
+
90
+
91
+ class MotionBricksTransformerLayer(nn.Module):
92
+ """Post-norm transformer encoder layer with a ReLU feed-forward."""
93
+
94
+ def __init__(self, dim: int, heads: int, ff_dim: int):
95
+ super().__init__()
96
+ self.attn = MotionBricksAttention(dim, heads)
97
+ self.norm1 = nn.LayerNorm(dim)
98
+ self.ff_in = nn.Linear(dim, ff_dim)
99
+ self.ff_out = nn.Linear(ff_dim, dim)
100
+ self.norm2 = nn.LayerNorm(dim)
101
+
102
+ def forward(self, hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None) -> torch.Tensor:
103
+ hidden_states = self.norm1(hidden_states + self.attn(hidden_states, attention_mask))
104
+ return self.norm2(hidden_states + self.ff_out(F.relu(self.ff_in(hidden_states))))
105
+
106
+
107
+ def key_mask(num_tokens: torch.Tensor, max_tokens: int, prefix: int = 0) -> torch.Tensor:
108
+ """Boolean `(batch_size, 1, 1, prefix + max_tokens)` attention mask: the `prefix` positions, then the first
109
+ `num_tokens` of `max_tokens` token positions."""
110
+ positions = torch.arange(max_tokens, device=num_tokens.device)
111
+ valid = positions[None] < num_tokens[:, None]
112
+ valid = F.pad(valid, (prefix, 0), value=True)
113
+ return valid[:, None, None]
114
+
115
+
116
+ class MotionBricksConv1d(nn.Conv1d):
117
+ """Stride-1 `nn.Conv1d` computed as one matrix product over the shifted inputs, faster than cuDNN at these
118
+ sizes."""
119
+
120
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
121
+ kernel, dilation, padding = self.kernel_size[0], self.dilation[0], self.padding[0]
122
+ length = hidden_states.shape[-1]
123
+ if kernel > 1:
124
+ padded = F.pad(hidden_states, (padding, padding))
125
+ shifted = [padded[..., i * dilation : i * dilation + length] for i in range(kernel)]
126
+ hidden_states = torch.stack(shifted, dim=2).flatten(1, 2)
127
+ weight = self.weight.reshape(self.out_channels, -1)
128
+ return torch.matmul(weight, hidden_states) + self.bias[:, None]
129
+
130
+
131
+ class MotionBricksResnetBlock(nn.Module):
132
+ def __init__(self, channels: int, dilation: int):
133
+ super().__init__()
134
+ self.conv1 = MotionBricksConv1d(channels, channels, 3, padding=dilation, dilation=dilation)
135
+ self.conv2 = MotionBricksConv1d(channels, channels, 1)
136
+
137
+ def forward(self, hidden_states: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
138
+ residual = hidden_states * mask
139
+ hidden_states = self.conv1(F.relu(residual) * mask)
140
+ hidden_states = self.conv2(F.relu(hidden_states) * mask)
141
+ return (hidden_states + residual) * mask
142
+
143
+
144
+ class MotionBricksUpBlock(nn.Module):
145
+ def __init__(self, channels: int, depth: int, dilation_growth_rate: int):
146
+ super().__init__()
147
+ dilations = [dilation_growth_rate**i for i in reversed(range(depth))]
148
+ self.resnets = nn.ModuleList([MotionBricksResnetBlock(channels, dilation) for dilation in dilations])
149
+ self.conv = MotionBricksConv1d(channels, channels, 3, padding=1)
150
+
151
+
152
+ class MotionBricksConditionedDecoder(nn.Module):
153
+ """Dilated 1D convolution decoder that upsamples tokens to frames, conditioned on per-frame targets and
154
+ external features."""
155
+
156
+ def __init__(
157
+ self,
158
+ in_channels: int,
159
+ out_channels: int,
160
+ target_channels: int,
161
+ external_channels: int,
162
+ width: int = 512,
163
+ depth: int = 4,
164
+ dilation_growth_rate: int = 3,
165
+ num_tokens: int = 16,
166
+ num_frames: int = 64,
167
+ ):
168
+ super().__init__()
169
+ self.width = width
170
+ self.num_frames = num_frames
171
+ lengths = [num_tokens, num_tokens * 2]
172
+ self.conv_in = MotionBricksConv1d(in_channels, width, 3, padding=1)
173
+ self.target_projections = nn.ModuleList(
174
+ [nn.Linear(target_channels, width * length // num_frames) for length in lengths]
175
+ )
176
+ self.external_projections = nn.ModuleList(
177
+ [nn.Linear(width + external_channels * num_frames // length, width) for length in lengths]
178
+ )
179
+ self.up_blocks = nn.ModuleList([MotionBricksUpBlock(width, depth, dilation_growth_rate) for _ in lengths])
180
+ self.conv_mid = MotionBricksConv1d(width, width, 3, padding=1)
181
+ self.conv_out = MotionBricksConv1d(width, out_channels, 3, padding=1)
182
+
183
+ def forward(
184
+ self,
185
+ hidden_states: torch.Tensor,
186
+ token_mask: torch.Tensor,
187
+ target: torch.Tensor,
188
+ target_mask: torch.Tensor,
189
+ external: torch.Tensor,
190
+ ) -> torch.Tensor:
191
+ """
192
+ Args:
193
+ hidden_states: `(batch_size, in_channels, num_tokens)` token features.
194
+ token_mask: `(batch_size, num_tokens)` float mask of the valid tokens.
195
+ target: `(batch_size, num_frames, target_channels)` per-frame targets.
196
+ target_mask: `(batch_size, num_frames)` boolean mask of the frames with a target.
197
+ external: `(batch_size, num_frames, external_channels)` per-frame conditioning.
198
+
199
+ Returns:
200
+ `(batch_size, out_channels, num_frames)` frame features.
201
+ """
202
+ batch_size = hidden_states.shape[0]
203
+ mask = token_mask[:, None]
204
+ hidden_states = F.relu(self.conv_in(hidden_states * mask))
205
+ for block, target_projection, external_projection in zip(
206
+ self.up_blocks, self.target_projections, self.external_projections
207
+ ):
208
+ length = hidden_states.shape[-1]
209
+ projected = F.relu(target_projection(target))
210
+ grouped = hidden_states.transpose(1, 2).reshape(batch_size, self.num_frames, -1)
211
+ grouped = torch.where(target_mask[..., None], projected, grouped)
212
+ grouped = grouped.reshape(batch_size, length, self.width)
213
+ conditioned = torch.cat([grouped, external.reshape(batch_size, length, -1)], dim=-1)
214
+ hidden_states = F.relu(external_projection(conditioned).transpose(1, 2))
215
+ for resnet in block.resnets:
216
+ hidden_states = resnet(hidden_states, mask)
217
+ hidden_states = F.interpolate(hidden_states, scale_factor=2, mode="nearest")
218
+ mask = mask.repeat_interleave(2, dim=-1)
219
+ hidden_states = block.conv(hidden_states * mask)
220
+ hidden_states = F.relu(self.conv_mid(hidden_states * mask)) * mask
221
+ return self.conv_out(hidden_states) * mask
222
+
223
+
224
+ @dataclass
225
+ class MotionBricksRootOutput(BaseOutput):
226
+ """
227
+ Args:
228
+ num_tokens (`torch.LongTensor` of shape `(batch_size,)`):
229
+ The chosen motion length in tokens; the motion spans `num_tokens * frames_per_token` frames.
230
+ root (`torch.Tensor` of shape `(batch_size, num_frames, 5)`):
231
+ The normalized global root of every frame: position, then cosine and sine of the heading.
232
+ """
233
+
234
+ num_tokens: torch.LongTensor
235
+ root: torch.Tensor
236
+
237
+
238
+ class MotionBricksRootModel(ModelMixin, ConfigMixin, AttentionMixin):
239
+ """Chooses the length of a transition and predicts its root trajectory from `context_frames` context poses and
240
+ `target_frames` target poses. Stores the normalization statistics of the guide features as buffers."""
241
+
242
+ _no_split_modules = ["MotionBricksTransformerLayer", "MotionBricksConditionedDecoder"]
243
+ _repeated_blocks = ["MotionBricksTransformerLayer"]
244
+ _skip_layerwise_casting_patterns = ["norm", "embed"]
245
+
246
+ @register_to_config
247
+ def __init__(
248
+ self,
249
+ pose_dim: int = 268,
250
+ global_root_dim: int = 5,
251
+ local_root_dim: int = 4,
252
+ pose_embed_dim: int = 256,
253
+ root_embed_dim: int = 64,
254
+ hidden_dim: int = 512,
255
+ num_heads: int = 16,
256
+ ff_dim: int = 2048,
257
+ num_shared_layers: int = 6,
258
+ num_token_layers: int = 2,
259
+ context_frames: int = 4,
260
+ target_frames: int = 4,
261
+ frames_per_token: int = 4,
262
+ min_tokens: int = 6,
263
+ max_tokens: int = 16,
264
+ decoder_depth: int = 4,
265
+ decoder_dilation_growth_rate: int = 3,
266
+ ):
267
+ super().__init__()
268
+ guide_frames = context_frames + target_frames
269
+ num_lengths = max_tokens - min_tokens + 1
270
+ guide_dim = pose_embed_dim + 2 * root_embed_dim
271
+
272
+ self.pose_proj = nn.Linear(pose_dim, pose_embed_dim)
273
+ self.local_root_proj = nn.Linear(local_root_dim, root_embed_dim)
274
+ self.global_root_proj = nn.Linear(global_root_dim, root_embed_dim)
275
+ self.missing_pose_embed = nn.Parameter(torch.zeros(pose_embed_dim))
276
+ self.missing_local_root_embed = nn.Parameter(torch.zeros(root_embed_dim))
277
+ self.missing_global_root_embed = nn.Parameter(torch.zeros(root_embed_dim))
278
+ self.context_proj = MotionBricksMLP(guide_dim, hidden_dim, hidden_dim)
279
+ self.target_proj = MotionBricksMLP(guide_dim, hidden_dim, hidden_dim)
280
+ self.guide_position_embed = nn.Parameter(torch.zeros(guide_frames, hidden_dim))
281
+
282
+ # Index num_lengths + 1 asks for a prediction; the length classes are min_tokens..max_tokens and one unused.
283
+ self.length_query_embed = nn.Embedding(num_lengths + 2, hidden_dim)
284
+ self.shared_layers = nn.ModuleList(
285
+ [MotionBricksTransformerLayer(hidden_dim, num_heads, ff_dim) for _ in range(num_shared_layers)]
286
+ )
287
+ self.length_head = nn.Linear(hidden_dim, num_lengths + 1)
288
+
289
+ self.length_embed = nn.Embedding(num_lengths + 1, hidden_dim)
290
+ self.token_position_embed = nn.Parameter(torch.zeros(max_tokens, hidden_dim))
291
+ self.token_layers = nn.ModuleList(
292
+ [MotionBricksTransformerLayer(hidden_dim, num_heads, ff_dim) for _ in range(num_token_layers)]
293
+ )
294
+
295
+ self.missing_frame_embed = nn.Parameter(torch.zeros(hidden_dim))
296
+ self.decoder = MotionBricksConditionedDecoder(
297
+ hidden_dim,
298
+ global_root_dim,
299
+ target_channels=global_root_dim,
300
+ external_channels=hidden_dim,
301
+ width=hidden_dim,
302
+ depth=decoder_depth,
303
+ dilation_growth_rate=decoder_dilation_growth_rate,
304
+ num_tokens=max_tokens,
305
+ num_frames=max_tokens * frames_per_token,
306
+ )
307
+
308
+ self.register_buffer("global_root_mean", torch.zeros(global_root_dim))
309
+ self.register_buffer("global_root_std", torch.ones(global_root_dim))
310
+ self.register_buffer("local_root_mean", torch.zeros(local_root_dim))
311
+ self.register_buffer("local_root_std", torch.ones(local_root_dim))
312
+ self.register_buffer("pose_mean", torch.zeros(pose_dim))
313
+ self.register_buffer("pose_std", torch.ones(pose_dim))
314
+
315
+ def _statistics(self, value: torch.Tensor, name: str) -> tuple[torch.Tensor, torch.Tensor]:
316
+ # The buffers can sit on another device while the model is offloaded.
317
+ mean, std = getattr(self, f"{name}_mean"), getattr(self, f"{name}_std")
318
+ return mean.to(value.device, value.dtype), std.to(value.device, value.dtype)
319
+
320
+ def normalize(self, value: torch.Tensor, name: str) -> torch.Tensor:
321
+ """Normalize `global_root`, `local_root` or `pose` features with this model's statistics."""
322
+ mean, std = self._statistics(value, name)
323
+ return (value - mean) / std
324
+
325
+ def denormalize(self, value: torch.Tensor, name: str) -> torch.Tensor:
326
+ mean, std = self._statistics(value, name)
327
+ return value * std + mean
328
+
329
+ def place_guides(self, guides: torch.Tensor, num_tokens: torch.Tensor, fill) -> torch.Tensor:
330
+ """Place `(batch_size, guide_frames, ...)` guide values on the frame timeline: the context poses at the start
331
+ and the target poses at the last frames of the chosen length. Other frames hold `fill`."""
332
+ config = self.config
333
+ shape = (guides.shape[0], config.max_tokens * config.frames_per_token, *guides.shape[2:])
334
+ if isinstance(fill, torch.Tensor):
335
+ timeline = fill.to(guides.dtype).expand(shape).clone()
336
+ else:
337
+ timeline = torch.full(shape, fill, dtype=guides.dtype, device=guides.device)
338
+ timeline[:, : config.context_frames] = guides[:, : config.context_frames]
339
+ end = num_tokens * config.frames_per_token
340
+ frames = end[:, None] - config.target_frames + torch.arange(config.target_frames, device=end.device)
341
+ batch = torch.arange(guides.shape[0], device=guides.device)[:, None]
342
+ timeline[batch, frames] = guides[:, config.context_frames :]
343
+ return timeline
344
+
345
+ def forward(
346
+ self,
347
+ global_root: torch.Tensor,
348
+ local_root: torch.Tensor,
349
+ pose: torch.Tensor,
350
+ has_global_root: torch.Tensor,
351
+ has_local_root: torch.Tensor,
352
+ has_pose: torch.Tensor,
353
+ allowed_num_tokens: torch.Tensor,
354
+ return_dict: bool = True,
355
+ ) -> MotionBricksRootOutput | tuple:
356
+ """
357
+ Args:
358
+ global_root (`torch.Tensor` of shape `(batch_size, guide_frames, global_root_dim)`):
359
+ Normalized global root of the guide poses.
360
+ local_root (`torch.Tensor` of shape `(batch_size, guide_frames, local_root_dim)`):
361
+ Normalized local root of the guide poses.
362
+ pose (`torch.Tensor` of shape `(batch_size, guide_frames, pose_dim)`):
363
+ Normalized pose features of the guide poses.
364
+ has_global_root (`torch.BoolTensor` of shape `(batch_size, guide_frames)`):
365
+ Which guide poses have a global root.
366
+ has_local_root (`torch.BoolTensor` of shape `(batch_size, guide_frames)`):
367
+ Which guide poses have a local root.
368
+ has_pose (`torch.BoolTensor` of shape `(batch_size, guide_frames)`):
369
+ Which guide poses have pose features.
370
+ allowed_num_tokens (`torch.BoolTensor` of shape `(batch_size, max_tokens - min_tokens + 1)`):
371
+ The lengths the model may choose, from `min_tokens`.
372
+ return_dict (`bool`, defaults to `True`):
373
+ Whether to return a `MotionBricksRootOutput` instead of a tuple.
374
+
375
+ Returns:
376
+ `MotionBricksRootOutput` or `tuple`: The chosen length and the normalized root; a tuple of the two when
377
+ `return_dict` is `False`.
378
+ """
379
+ config = self.config
380
+ has_pose_f = has_pose[..., None].to(pose.dtype)
381
+ has_local_f = has_local_root[..., None].to(pose.dtype)
382
+ has_global_f = has_global_root[..., None].to(pose.dtype)
383
+ pose_embeds = self.pose_proj(pose) * has_pose_f + self.missing_pose_embed * (1 - has_pose_f)
384
+ local_embeds = self.local_root_proj(local_root) * has_local_f + self.missing_local_root_embed * (
385
+ 1 - has_local_f
386
+ )
387
+ global_embeds = self.global_root_proj(global_root) * has_global_f + self.missing_global_root_embed * (
388
+ 1 - has_global_f
389
+ )
390
+ guides = torch.cat([pose_embeds, local_embeds, global_embeds], dim=-1)
391
+ guides = torch.cat(
392
+ [
393
+ self.context_proj(guides[:, : config.context_frames]),
394
+ self.target_proj(guides[:, config.context_frames :]),
395
+ ],
396
+ dim=1,
397
+ )
398
+ positioned = guides + self.guide_position_embed
399
+
400
+ batch_size = guides.shape[0]
401
+ num_lengths = config.max_tokens - config.min_tokens + 1
402
+ query = self.length_query_embed(torch.full((batch_size, 1), num_lengths + 1, device=guides.device))
403
+ hidden_states = torch.cat([query, positioned], dim=1)
404
+ for layer in self.shared_layers:
405
+ hidden_states = layer(hidden_states)
406
+ logits = self.length_head(hidden_states[:, 0])
407
+ allowed = F.pad(allowed_num_tokens, (0, 1), value=False)
408
+ length = logits.masked_fill(~allowed, float("-inf")).argmax(dim=-1)
409
+ num_tokens = length + config.min_tokens
410
+
411
+ tokens = self.token_position_embed.expand(batch_size, -1, -1)
412
+ hidden_states = torch.cat([self.length_embed(length)[:, None], positioned, tokens], dim=1)
413
+ mask = key_mask(num_tokens, config.max_tokens, prefix=1 + guides.shape[1])
414
+ for layer in self.token_layers:
415
+ hidden_states = layer(hidden_states, mask)
416
+ tokens = hidden_states[:, -config.max_tokens :]
417
+
418
+ has_guide = has_pose | has_local_root | has_global_root
419
+ frame_guides = torch.where(has_guide[..., None], guides, self.missing_frame_embed)
420
+ external = self.place_guides(frame_guides, num_tokens, fill=self.missing_frame_embed)
421
+ target = self.place_guides(global_root, num_tokens, fill=0)
422
+ target_mask = self.place_guides(has_global_root, num_tokens, fill=False)
423
+ token_mask = (torch.arange(config.max_tokens, device=num_tokens.device)[None] < num_tokens[:, None]).to(
424
+ tokens.dtype
425
+ )
426
+ root = self.decoder(tokens.transpose(1, 2), token_mask, target, target_mask, external).transpose(1, 2)
427
+
428
+ if not return_dict:
429
+ return (num_tokens, root)
430
+ return MotionBricksRootOutput(num_tokens=num_tokens, root=root)