ar27111994 commited on
Commit
ef4dce5
·
verified ·
1 Parent(s): 6e353fb

Upload lewm_model.py

Browse files
Files changed (1) hide show
  1. lewm_model.py +211 -0
lewm_model.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+
5
+ class SIGReg(nn.Module):
6
+ """Sketched Isotropic Gaussian Regularizer (SIGReg)
7
+ Encourages latent embeddings to match an isotropic Gaussian N(0, I).
8
+ Uses random 1D projections + Epps-Pulley test statistic.
9
+ Input shape: (N, D) or (T, B, D) — any shape where last dim is D.
10
+ """
11
+ def __init__(self, knots=17, num_proj=1024, t_min=0.2, t_max=4.0):
12
+ super().__init__()
13
+ self.num_proj = num_proj
14
+ t = torch.linspace(t_min, t_max, knots, dtype=torch.float32)
15
+ dt = (t_max - t_min) / (knots - 1)
16
+ weights = torch.full((knots,), 2 * dt, dtype=torch.float32)
17
+ weights[[0, -1]] = dt
18
+ window = torch.exp(-t.square() / 2.0)
19
+ self.register_buffer("t", t)
20
+ self.register_buffer("phi", window)
21
+ self.register_buffer("weights", weights * window)
22
+
23
+ def forward(self, proj):
24
+ # proj: (*, D) where D is embedding dim
25
+ D = proj.size(-1)
26
+ A = torch.randn(D, self.num_proj, device=proj.device, dtype=proj.dtype)
27
+ A = A.div_(A.norm(p=2, dim=0, keepdim=True))
28
+ x = proj @ A # (*, num_proj)
29
+ x_t = x.unsqueeze(-1) * self.t # (*, num_proj, knots)
30
+ cos_mean = x_t.cos().mean(dim=-3) # (num_proj, knots)
31
+ sin_mean = x_t.sin().mean(dim=-3) # (num_proj, knots)
32
+ err = (cos_mean - self.phi).square() + sin_mean.square()
33
+ statistic = (err @ self.weights) * proj.size(-2)
34
+ return statistic.mean()
35
+
36
+ class AdaLNBlock(nn.Module):
37
+ """Transformer block with Adaptive Layer Normalization (AdaLN-zero) conditioning."""
38
+ def __init__(self, dim, heads=16, mlp_ratio=4.0, dropout=0.1):
39
+ super().__init__()
40
+ self.norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
41
+ self.norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
42
+ self.attn = nn.MultiheadAttention(dim, heads, dropout=dropout, batch_first=True)
43
+ mlp_dim = int(dim * mlp_ratio)
44
+ self.mlp = nn.Sequential(
45
+ nn.Linear(dim, mlp_dim),
46
+ nn.GELU(),
47
+ nn.Dropout(dropout),
48
+ nn.Linear(mlp_dim, dim),
49
+ nn.Dropout(dropout),
50
+ )
51
+ self.adaLN_modulation = nn.Sequential(
52
+ nn.SiLU(),
53
+ nn.Linear(dim, 6 * dim, bias=True)
54
+ )
55
+ nn.init.constant_(self.adaLN_modulation[-1].weight, 0.0)
56
+ nn.init.constant_(self.adaLN_modulation[-1].bias, 0.0)
57
+
58
+ def modulate(self, x, shift, scale):
59
+ return x * (1 + scale) + shift
60
+
61
+ def forward(self, x, c):
62
+ if c.size(1) != x.size(1):
63
+ pad_len = x.size(1) - c.size(1)
64
+ c = F.pad(c, (0, 0, 0, pad_len), mode='constant', value=0)
65
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
66
+ self.adaLN_modulation(c).chunk(6, dim=-1)
67
+ )
68
+ normed = self.modulate(self.norm1(x), shift_msa, scale_msa)
69
+ attn_out, _ = self.attn(normed, normed, normed, need_weights=False)
70
+ x = x + gate_msa * attn_out
71
+ normed = self.modulate(self.norm2(x), shift_mlp, scale_mlp)
72
+ x = x + gate_mlp * self.mlp(normed)
73
+ return x
74
+
75
+ class Predictor(nn.Module):
76
+ """Causal Transformer predictor with AdaLN action conditioning."""
77
+ def __init__(self, dim, depth=6, heads=16, mlp_ratio=4.0, dropout=0.1, max_len=512):
78
+ super().__init__()
79
+ self.pos_emb = nn.Parameter(torch.randn(1, max_len, dim) * 0.02)
80
+ self.blocks = nn.ModuleList([
81
+ AdaLNBlock(dim, heads, mlp_ratio, dropout) for _ in range(depth)
82
+ ])
83
+ self.norm = nn.LayerNorm(dim, eps=1e-6)
84
+
85
+ def forward(self, x, act_emb):
86
+ B, T, D = x.shape
87
+ x = x + self.pos_emb[:, :T, :]
88
+ for block in self.blocks:
89
+ x = block(x, act_emb)
90
+ x = self.norm(x)
91
+ return x
92
+
93
+ class ActionEncoder(nn.Module):
94
+ """Encode continuous actions into embedding space."""
95
+ def __init__(self, action_dim, out_dim, hidden_dim=256):
96
+ super().__init__()
97
+ self.net = nn.Sequential(
98
+ nn.Linear(action_dim, hidden_dim),
99
+ nn.ReLU(),
100
+ nn.Linear(hidden_dim, hidden_dim),
101
+ nn.ReLU(),
102
+ nn.Linear(hidden_dim, out_dim),
103
+ )
104
+
105
+ def forward(self, a):
106
+ return self.net(a)
107
+
108
+ class LeWorldModel(nn.Module):
109
+ """
110
+ LeWorldModel (LeWM): Stable end-to-end JEPA world model from pixels.
111
+ Encoder: ViT-Tiny + BatchNorm projector.
112
+ Predictor: Causal Transformer with AdaLN-zero action conditioning.
113
+ """
114
+ def __init__(
115
+ self,
116
+ img_size=224,
117
+ patch_size=14,
118
+ in_chans=3,
119
+ embed_dim=192,
120
+ encoder_depth=12,
121
+ encoder_heads=3,
122
+ predictor_depth=6,
123
+ predictor_heads=16,
124
+ action_dim=2,
125
+ latent_dim=192,
126
+ dropout=0.1,
127
+ max_len=512,
128
+ ):
129
+ super().__init__()
130
+ self.patch_embed = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
131
+ num_patches = (img_size // patch_size) ** 2
132
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
133
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim))
134
+ self.pos_drop = nn.Dropout(p=dropout)
135
+ encoder_layer = nn.TransformerEncoderLayer(
136
+ d_model=embed_dim,
137
+ nhead=encoder_heads,
138
+ dim_feedforward=int(embed_dim * 4),
139
+ dropout=dropout,
140
+ activation='gelu',
141
+ batch_first=True,
142
+ norm_first=True,
143
+ )
144
+ self.encoder_transformer = nn.TransformerEncoder(encoder_layer, num_layers=encoder_depth)
145
+ self.encoder_norm = nn.LayerNorm(embed_dim, eps=1e-6)
146
+
147
+ self.projector = nn.Sequential(
148
+ nn.Linear(embed_dim, latent_dim),
149
+ nn.BatchNorm1d(latent_dim),
150
+ )
151
+
152
+ self.action_encoder = ActionEncoder(action_dim, latent_dim)
153
+
154
+ self.predictor = Predictor(
155
+ dim=latent_dim,
156
+ depth=predictor_depth,
157
+ heads=predictor_heads,
158
+ dropout=dropout,
159
+ max_len=max_len,
160
+ )
161
+
162
+ self.pred_projector = nn.Sequential(
163
+ nn.Linear(latent_dim, latent_dim),
164
+ nn.BatchNorm1d(latent_dim),
165
+ )
166
+
167
+ self._init_weights()
168
+
169
+ def _init_weights(self):
170
+ nn.init.normal_(self.cls_token, std=0.02)
171
+ nn.init.normal_(self.pos_embed, std=0.02)
172
+
173
+ def encode(self, obs):
174
+ if obs.dim() == 5:
175
+ B, T, C, H, W = obs.shape
176
+ obs = obs.reshape(B * T, C, H, W)
177
+ reshape_back = True
178
+ else:
179
+ B = obs.size(0)
180
+ reshape_back = False
181
+
182
+ x = self.patch_embed(obs)
183
+ x = x.flatten(2).transpose(1, 2)
184
+ cls_tokens = self.cls_token.expand(x.size(0), -1, -1)
185
+ x = torch.cat([cls_tokens, x], dim=1)
186
+ x = x + self.pos_embed
187
+ x = self.pos_drop(x)
188
+ x = self.encoder_transformer(x)
189
+ x = self.encoder_norm(x)
190
+ cls = x[:, 0]
191
+ z = self.projector(cls)
192
+
193
+ if reshape_back:
194
+ z = z.reshape(B, T, -1)
195
+ return z
196
+
197
+ def predict(self, emb, actions):
198
+ B, T, D = emb.shape
199
+ act_emb = self.action_encoder(actions)
200
+ pad = torch.zeros(B, 1, D, device=act_emb.device, dtype=act_emb.dtype)
201
+ act_emb = torch.cat([act_emb, pad], dim=1)
202
+ preds = self.predictor(emb, act_emb)
203
+ preds = preds.reshape(B * T, D)
204
+ preds = self.pred_projector(preds)
205
+ preds = preds.reshape(B, T, D)
206
+ return preds
207
+
208
+ def forward(self, obs, actions):
209
+ emb = self.encode(obs)
210
+ pred_emb = self.predict(emb, actions)
211
+ return emb, pred_emb