Run one training step of a tiny noise-prediction diffusion model and return the loss, gradient norm and updated weights.
xtL=αˉtx0+1−αˉtϵ,ϵ^=SiLU([xt, e(t)]W1+b1)W2+b2=mean((ϵ^−ϵ)2),θ←θ−ηmin(1,∥g∥+10−6c)gEach sample uses αˉt = alpha_bars at its own timestep, x0 is x0 and ϵ is noise. The embedding e(t)=[cos(tf),sin(tf)] has width embed_dim, where half = embed_dim // 2 and the vector f holds fi=exp(−ln(10000)i/half) for i=0,…,half−1. One zero is appended when embed_dim is odd, and the bracket concatenates along the feature axis.
The weights W1, b1, W2 and b2 are the entries W1, b1, W2 and b2 of params, and SiLU(z)=z⋅sigmoid(z). The loss L averages over all B times D entries. The vector g collects the gradients of L for all four tensors, ∥g∥ is their global L2 norm, η is learning_rate, c is max_grad_norm, and each weight tensor θ is updated with its own part of g.
Return a dict with "loss" (L before the update) and "grad_norm" (∥g∥ before clipping) as Python floats, and "params", a dict of new tensors with the same keys, shapes and dtype.
Input: x0 = [[1.0,-1.0],[0.5,0.5]], timesteps = [0,3], noise = [[0.2,-0.4],[1.0,0.0]], alpha_bars = [0.9,0.7,0.4,0.1], params = {"W1":[[0.5,-0.3,0.2],[0.1,0.4,-0.2],[0.3,0.0,0.1],[-0.2,0.2,0.5]],"b1":[0.0,0.1,-0.1],"W2":[[0.4,-0.1],[0.2,0.3],[-0.5,0.2]],"b2":[0.0,0.05]}, embed_dim = 2, learning_rate = 0.1, max_grad_norm = 100.0
Output: {"loss":0.283354,"grad_norm":0.750735,"params":{"W1":[[0.51813,-0.296348,0.179547],[0.096968,0.401509,-0.195672],[0.292565,-0.004809,0.106971],[-0.198283,0.20057,0.498159]],"b1":[0.016776,0.10323,-0.118989],"W2":[[0.410936,-0.109202],[0.194805,0.304335],[-0.496157,0.195084]],"b2":[0.057866,0.029459]}}
Explanation: The two samples are noised at timesteps 0 and 3, the MLP predicts their noise with a loss of about 0.283354, and the gradient norm 0.750735 is below max_grad_norm, so every weight moves by exactly 0.1 times its gradient.
Input: x0 = [[2.0,0.0],[-1.0,1.5],[0.0,-2.0]], timesteps = [1,2,4], noise = [[1.0,1.0],[-0.5,0.3],[0.8,-1.2]], alpha_bars = [0.95,0.8,0.6,0.35,0.15], params = {"W1":[[0.6,-0.4],[0.2,0.5],[0.3,-0.1],[-0.2,0.4],[0.1,0.1],[0.5,-0.3]],"b1":[0.1,-0.1],"W2":[[0.7,-0.2],[0.3,0.6]],"b2":[0.0,0.0]}, embed_dim = 4, learning_rate = 0.5, max_grad_norm = 0.1
Output: {"loss":0.784072,"grad_norm":1.007376,"params":{"W1":[[0.598729,-0.380109],[0.186212,0.490759],[0.295572,-0.093402],[-0.199701,0.395091],[0.091689,0.096041],[0.500144,-0.300154]],"b1":[0.100302,-0.104911],"W2":[[0.703093,-0.166994],[0.279822,0.59106]],"b2":[0.005674,-0.00356]}}
Input: x0 = [[0.3,-0.7]], timesteps = [0], noise = [[-1.0,0.5]], alpha_bars = [0.5,0.2], params = {"W1":[[0.2,0.1],[-0.3,0.4],[0.5,0.0],[0.1,-0.2],[0.7,0.7]],"b1":[0.0,0.0],"W2":[[0.3,-0.3],[0.1,0.2]],"b2":[0.1,-0.1]}, embed_dim = 3, learning_rate = 0.05, max_grad_norm = 1.0
Output: {"loss":0.93028,"grad_norm":1.537563,"params":{"W1":[[0.206443,0.099852],[-0.298159,0.399958],[0.486983,0.0003],[0.1,-0.2],[0.7,0.7]],"b1":[-0.013017,0.0003],"W2":[[0.289671,-0.29393],[0.101921,0.198871]],"b2":[0.061758,-0.077527]}}
Cloning each weight with detach().clone().requires_grad_(True) lets autograd compute gradients without touching the inputs.
The global norm is the square root of the sum of the squared entries of every gradient tensor together.
Apply the update inside torch.no_grad() so the new weights are plain tensors.
torch.nn.utils.clip_grad_norm_ uses this same global-norm formula and returns the norm before clipping.
Accepts: array
Accepts: array
Accepts: array
Accepts: array
Accepts: object
Accepts: integer
Accepts: number
Accepts: number
Run one training step of a tiny noise-prediction diffusion model and return the loss, gradient norm and updated weights.
xtL=αˉtx0+1−αˉtϵ,ϵ^=SiLU([xt, e(t)]W1+b1)W2+b2=mean((ϵ^−ϵ)2),θ←θ−ηmin(1,∥g∥+10−6c)gEach sample uses αˉt = alpha_bars at its own timestep, x0 is x0 and ϵ is noise. The embedding e(t)=[cos(tf),sin(tf)] has width embed_dim, where half = embed_dim // 2 and the vector f holds fi=exp(−ln(10000)i/half) for i=0,…,half−1. One zero is appended when embed_dim is odd, and the bracket concatenates along the feature axis.
The weights W1, b1, W2 and b2 are the entries W1, b1, W2 and b2 of params, and SiLU(z)=z⋅sigmoid(z). The loss L averages over all B times D entries. The vector g collects the gradients of L for all four tensors, ∥g∥ is their global L2 norm, η is learning_rate, c is max_grad_norm, and each weight tensor θ is updated with its own part of g.
Return a dict with "loss" (L before the update) and "grad_norm" (∥g∥ before clipping) as Python floats, and "params", a dict of new tensors with the same keys, shapes and dtype.
Input: x0 = [[1.0,-1.0],[0.5,0.5]], timesteps = [0,3], noise = [[0.2,-0.4],[1.0,0.0]], alpha_bars = [0.9,0.7,0.4,0.1], params = {"W1":[[0.5,-0.3,0.2],[0.1,0.4,-0.2],[0.3,0.0,0.1],[-0.2,0.2,0.5]],"b1":[0.0,0.1,-0.1],"W2":[[0.4,-0.1],[0.2,0.3],[-0.5,0.2]],"b2":[0.0,0.05]}, embed_dim = 2, learning_rate = 0.1, max_grad_norm = 100.0
Output: {"loss":0.283354,"grad_norm":0.750735,"params":{"W1":[[0.51813,-0.296348,0.179547],[0.096968,0.401509,-0.195672],[0.292565,-0.004809,0.106971],[-0.198283,0.20057,0.498159]],"b1":[0.016776,0.10323,-0.118989],"W2":[[0.410936,-0.109202],[0.194805,0.304335],[-0.496157,0.195084]],"b2":[0.057866,0.029459]}}
Explanation: The two samples are noised at timesteps 0 and 3, the MLP predicts their noise with a loss of about 0.283354, and the gradient norm 0.750735 is below max_grad_norm, so every weight moves by exactly 0.1 times its gradient.
Input: x0 = [[2.0,0.0],[-1.0,1.5],[0.0,-2.0]], timesteps = [1,2,4], noise = [[1.0,1.0],[-0.5,0.3],[0.8,-1.2]], alpha_bars = [0.95,0.8,0.6,0.35,0.15], params = {"W1":[[0.6,-0.4],[0.2,0.5],[0.3,-0.1],[-0.2,0.4],[0.1,0.1],[0.5,-0.3]],"b1":[0.1,-0.1],"W2":[[0.7,-0.2],[0.3,0.6]],"b2":[0.0,0.0]}, embed_dim = 4, learning_rate = 0.5, max_grad_norm = 0.1
Output: {"loss":0.784072,"grad_norm":1.007376,"params":{"W1":[[0.598729,-0.380109],[0.186212,0.490759],[0.295572,-0.093402],[-0.199701,0.395091],[0.091689,0.096041],[0.500144,-0.300154]],"b1":[0.100302,-0.104911],"W2":[[0.703093,-0.166994],[0.279822,0.59106]],"b2":[0.005674,-0.00356]}}
Input: x0 = [[0.3,-0.7]], timesteps = [0], noise = [[-1.0,0.5]], alpha_bars = [0.5,0.2], params = {"W1":[[0.2,0.1],[-0.3,0.4],[0.5,0.0],[0.1,-0.2],[0.7,0.7]],"b1":[0.0,0.0],"W2":[[0.3,-0.3],[0.1,0.2]],"b2":[0.1,-0.1]}, embed_dim = 3, learning_rate = 0.05, max_grad_norm = 1.0
Output: {"loss":0.93028,"grad_norm":1.537563,"params":{"W1":[[0.206443,0.099852],[-0.298159,0.399958],[0.486983,0.0003],[0.1,-0.2],[0.7,0.7]],"b1":[-0.013017,0.0003],"W2":[[0.289671,-0.29393],[0.101921,0.198871]],"b2":[0.061758,-0.077527]}}
Cloning each weight with detach().clone().requires_grad_(True) lets autograd compute gradients without touching the inputs.
The global norm is the square root of the sum of the squared entries of every gradient tensor together.
Apply the update inside torch.no_grad() so the new weights are plain tensors.
torch.nn.utils.clip_grad_norm_ uses this same global-norm formula and returns the norm before clipping.
Accepts: array
Accepts: array
Accepts: array
Accepts: array
Accepts: object
Accepts: integer
Accepts: number
Accepts: number