AlgoX commited on
Commit
fca5f5f
·
1 Parent(s): f69e29e

feat : mamba training file

Browse files
Files changed (1) hide show
  1. train/mamba_train.py +618 -0
train/mamba_train.py ADDED
@@ -0,0 +1,618 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from torch.utils.data import Dataset, DataLoader
5
+ import numpy as np
6
+ import pandas as pd
7
+ import matplotlib.pyplot as plt
8
+ from sklearn.model_selection import train_test_split
9
+ from sklearn.preprocessing import StandardScaler
10
+ import os
11
+ from datetime import datetime
12
+ import json
13
+
14
+
15
+
16
+
17
+ def get_model_device(model):
18
+ return next(iter(model.parameters())).device
19
+
20
+
21
+ class CausalConv1d(nn.Module):
22
+
23
+
24
+ def __init__(self, hidden_size, kernel_size):
25
+ super().__init__()
26
+ self.hidden_size = hidden_size
27
+ self.kernel_size = kernel_size
28
+ self.conv = nn.Conv1d(
29
+ hidden_size, hidden_size, kernel_size, groups=hidden_size, bias=True
30
+ )
31
+
32
+ def init_state(self, batch_size: int, device: torch.device | None = None):
33
+ if device is None:
34
+ device = get_model_device(self)
35
+ return torch.zeros(
36
+ batch_size, self.hidden_size, self.kernel_size - 1, device=device
37
+ )
38
+
39
+ def forward(self, x: torch.Tensor, state: torch.Tensor):
40
+ x_with_state = torch.concat([state, x[:, :, None]], dim=-1)
41
+ out = self.conv(x_with_state)
42
+ new_state = x_with_state[:, :, 1:]
43
+ return out.squeeze(-1), new_state
44
+
45
+
46
+ class Mamba2(nn.Module):
47
+ def __init__(
48
+ self,
49
+ hidden_size: int,
50
+ inner_size: int | None = None,
51
+ head_size: int = 64,
52
+ bc_head_size: int = 128,
53
+ conv_kernel_size: int = 4,
54
+ ):
55
+ super().__init__()
56
+
57
+ self.head_size = head_size
58
+ self.bc_head_size = bc_head_size
59
+ if inner_size is None:
60
+ inner_size = 2 * hidden_size
61
+ assert inner_size % head_size == 0
62
+ self.inner_size = inner_size
63
+ self.num_heads = inner_size // head_size
64
+
65
+ # Projections
66
+ self.input_proj = nn.Linear(hidden_size, inner_size, bias=False)
67
+ self.z_proj = nn.Linear(hidden_size, inner_size, bias=False)
68
+ self.b_proj = nn.Linear(hidden_size, bc_head_size, bias=False)
69
+ self.c_proj = nn.Linear(hidden_size, bc_head_size, bias=False)
70
+ self.dt_proj = nn.Linear(hidden_size, self.num_heads, bias=True)
71
+
72
+ # Convs
73
+ self.input_conv = CausalConv1d(inner_size, conv_kernel_size)
74
+ self.b_conv = CausalConv1d(bc_head_size, conv_kernel_size)
75
+ self.c_conv = CausalConv1d(bc_head_size, conv_kernel_size)
76
+
77
+ # Other parameters
78
+ self.a = nn.Parameter(-torch.empty(self.num_heads).uniform_(1, 16))
79
+ self.d = nn.Parameter(torch.ones(self.num_heads))
80
+
81
+ # Output
82
+ self.norm = nn.RMSNorm(inner_size, eps=1e-5)
83
+ self.out_proj = nn.Linear(inner_size, hidden_size, bias=False)
84
+
85
+ def init_state(self, batch_size: int, device: torch.device | None = None):
86
+ if device is None:
87
+ device = get_model_device(self)
88
+ conv_states = [
89
+ conv.init_state(batch_size, device)
90
+ for conv in [self.input_conv, self.b_conv, self.c_conv]
91
+ ]
92
+ ssm_state = torch.zeros(
93
+ batch_size, self.num_heads, self.head_size, self.bc_head_size, device=device
94
+ )
95
+ return conv_states + [ssm_state]
96
+
97
+ def forward(self, t, state):
98
+ batch_size = t.shape[0]
99
+
100
+ x = self.input_proj(t)
101
+ z = self.z_proj(t)
102
+ b = self.b_proj(t)
103
+ c = self.c_proj(t)
104
+ dt = self.dt_proj(t)
105
+
106
+ x_conv_state, b_conv_state, c_conv_state, ssm_state = state
107
+ x, x_conv_state = self.input_conv(x, x_conv_state)
108
+ b, b_conv_state = self.b_conv(b, b_conv_state)
109
+ c, c_conv_state = self.c_conv(c, c_conv_state)
110
+ x = F.silu(x)
111
+ b = F.silu(b)
112
+ c = F.silu(c)
113
+
114
+ x = x.view(batch_size, self.num_heads, self.head_size)
115
+ dt = F.softplus(dt)
116
+
117
+ # SSM computation, this implements the discretized state space model.
118
+ # new_state computation: h[t] = exp(A*dt) * h[t-1] + dt * B * x[t]
119
+ # [batch_size, num_heads]
120
+ decay = torch.exp(self.a[None] * dt)
121
+ # Broadcasting everything to the right shapes:
122
+ # dt is [batch_size, num_heads]
123
+ # b is [batch_size, bc_head_size]
124
+ # x is [batch_size, head_size]
125
+ # The new contribution (and ssm_state) is [batch_size, num_heads, head_size, bc_head_size]
126
+ new_state_contrib = dt[:, :, None, None] * b[:, None, None] * x[:, :, :, None]
127
+ ssm_state = decay[:, :, None, None] * ssm_state + new_state_contrib
128
+
129
+ # output computation: y[t] = C @ h[t] + D * x[t]
130
+ # The accumulation in the product of C and h[t] is on the bc_head_size dimension
131
+ state_contrib = torch.einsum("bc,bnhc->bnh", c, ssm_state)
132
+ # d has shape [num_heads], broadcasting it to the shape of x.
133
+ y = state_contrib + self.d[None, :, None] * x
134
+
135
+ # Combine heads
136
+ y = y.view(batch_size, self.inner_size)
137
+ # Gate, normalization and out
138
+ y = y * F.silu(z)
139
+ y = self.norm(y)
140
+ output = self.out_proj(y)
141
+
142
+ new_state = [x_conv_state, b_conv_state, c_conv_state, ssm_state]
143
+ return output, new_state
144
+
145
+
146
+ class Mamba2Predictor(nn.Module):
147
+ """Full model with input projection and output head"""
148
+
149
+ def __init__(
150
+ self,
151
+ input_size: int,
152
+ hidden_size: int,
153
+ num_layers: int = 2,
154
+ inner_size: int | None = None,
155
+ head_size: int = 64,
156
+ bc_head_size: int = 128,
157
+ conv_kernel_size: int = 4,
158
+ dropout: float = 0.1,
159
+ ):
160
+ super().__init__()
161
+ self.input_size = input_size
162
+ self.hidden_size = hidden_size
163
+ self.num_layers = num_layers
164
+
165
+ # Input projection
166
+ self.input_proj = nn.Linear(input_size, hidden_size)
167
+ self.input_norm = nn.LayerNorm(hidden_size)
168
+
169
+ # Mamba2 layers
170
+ self.mamba_layers = nn.ModuleList(
171
+ [
172
+ Mamba2(
173
+ hidden_size,
174
+ inner_size=inner_size,
175
+ head_size=head_size,
176
+ bc_head_size=bc_head_size,
177
+ conv_kernel_size=conv_kernel_size,
178
+ )
179
+ for _ in range(num_layers)
180
+ ]
181
+ )
182
+
183
+ # Layer norms
184
+ self.layer_norms = nn.ModuleList(
185
+ [nn.LayerNorm(hidden_size) for _ in range(num_layers)]
186
+ )
187
+
188
+ # Dropout
189
+ self.dropout = nn.Dropout(dropout)
190
+
191
+ # Output head
192
+ self.output_head = nn.Sequential(
193
+ nn.Linear(hidden_size, hidden_size // 2),
194
+ nn.GELU(),
195
+ nn.Dropout(dropout),
196
+ nn.Linear(hidden_size // 2, 1),
197
+ )
198
+
199
+ def forward(self, x: torch.Tensor, states=None):
200
+
201
+ batch_size, seq_len, _ = x.shape
202
+ device = x.device
203
+
204
+ # Initialize states if needed
205
+ if states is None:
206
+ states = [
207
+ layer.init_state(batch_size, device) for layer in self.mamba_layers
208
+ ]
209
+
210
+ # Input projection
211
+ x = self.input_proj(x) # (batch, seq, hidden)
212
+ x = self.input_norm(x)
213
+
214
+ outputs = []
215
+ final_states = []
216
+
217
+ # Process sequence
218
+ for t in range(seq_len):
219
+ x_t = x[:, t, :] # (batch, hidden)
220
+
221
+ # Pass through Mamba2 layers
222
+ new_states = []
223
+ for i, (mamba_layer, layer_norm) in enumerate(
224
+ zip(self.mamba_layers, self.layer_norms)
225
+ ):
226
+ residual = x_t
227
+ x_t, state = mamba_layer(x_t, states[i])
228
+ x_t = layer_norm(x_t + residual)
229
+ x_t = self.dropout(x_t)
230
+ new_states.append(state)
231
+
232
+ states = new_states
233
+ outputs.append(x_t)
234
+
235
+ # Stack outputs
236
+ outputs = torch.stack(outputs, dim=1) # (batch, seq, hidden)
237
+
238
+ # Generate predictions
239
+ predictions = self.output_head(outputs) # (batch, seq, 1)
240
+
241
+ return predictions, states
242
+
243
+
244
+
245
+
246
+ class TimeSeriesDataset(Dataset):
247
+ def __init__(self, features, targets, seq_length=20):
248
+ self.features = features
249
+ self.targets = targets
250
+ self.seq_length = seq_length
251
+
252
+ def __len__(self):
253
+ return len(self.features) - self.seq_length
254
+
255
+ def __getitem__(self, idx):
256
+ x = self.features[idx : idx + self.seq_length]
257
+ y = self.targets[idx : idx + self.seq_length]
258
+ return torch.FloatTensor(x), torch.FloatTensor(y).squeeze(-1)
259
+
260
+
261
+
262
+
263
+ class MetricsLogger:
264
+ def __init__(self, save_dir):
265
+ self.save_dir = save_dir
266
+ self.metrics = {
267
+ "train_loss": [],
268
+ "val_loss": [],
269
+ "train_mse": [],
270
+ "val_mse": [],
271
+ "train_mae": [],
272
+ "val_mae": [],
273
+ "learning_rates": [],
274
+ }
275
+
276
+ def update(self, epoch_metrics):
277
+ for key, value in epoch_metrics.items():
278
+ if key in self.metrics:
279
+ self.metrics[key].append(value)
280
+
281
+ def save(self):
282
+ with open(os.path.join(self.save_dir, "metrics.json"), "w") as f:
283
+ json.dump(self.metrics, f, indent=4)
284
+
285
+ def plot_metrics(self):
286
+ fig, axes = plt.subplots(2, 2, figsize=(15, 10))
287
+ fig.suptitle("Training Metrics", fontsize=16)
288
+
289
+ # Loss
290
+ ax = axes[0, 0]
291
+ ax.plot(self.metrics["train_loss"], label="Train Loss", marker="o")
292
+ ax.plot(self.metrics["val_loss"], label="Val Loss", marker="s")
293
+ ax.set_xlabel("Epoch")
294
+ ax.set_ylabel("Loss")
295
+ ax.set_title("Training and Validation Loss")
296
+ ax.legend()
297
+ ax.grid(True)
298
+
299
+ # MSE
300
+ ax = axes[0, 1]
301
+ ax.plot(self.metrics["train_mse"], label="Train MSE", marker="o")
302
+ ax.plot(self.metrics["val_mse"], label="Val MSE", marker="s")
303
+ ax.set_xlabel("Epoch")
304
+ ax.set_ylabel("MSE")
305
+ ax.set_title("Mean Squared Error")
306
+ ax.legend()
307
+ ax.grid(True)
308
+
309
+ # MAE
310
+ ax = axes[1, 0]
311
+ ax.plot(self.metrics["train_mae"], label="Train MAE", marker="o")
312
+ ax.plot(self.metrics["val_mae"], label="Val MAE", marker="s")
313
+ ax.set_xlabel("Epoch")
314
+ ax.set_ylabel("MAE")
315
+ ax.set_title("Mean Absolute Error")
316
+ ax.legend()
317
+ ax.grid(True)
318
+
319
+ # Learning Rate
320
+ ax = axes[1, 1]
321
+ ax.plot(self.metrics["learning_rates"], marker="o", color="purple")
322
+ ax.set_xlabel("Epoch")
323
+ ax.set_ylabel("Learning Rate")
324
+ ax.set_title("Learning Rate Schedule")
325
+ ax.grid(True)
326
+ ax.set_yscale("log")
327
+
328
+ plt.tight_layout()
329
+ plt.savefig(os.path.join(self.save_dir, "training_metrics.png"), dpi=300)
330
+ plt.close()
331
+
332
+
333
+ def calculate_metrics(predictions, targets):
334
+ """Calculate MSE and MAE"""
335
+ mse = F.mse_loss(predictions, targets).item()
336
+ mae = F.l1_loss(predictions, targets).item()
337
+ return mse, mae
338
+
339
+
340
+ def save_checkpoint(
341
+ model, optimizer, scheduler, epoch, metrics, save_dir, is_best=False
342
+ ):
343
+ checkpoint = {
344
+ "epoch": epoch,
345
+ "model_state_dict": model.state_dict(),
346
+ "optimizer_state_dict": optimizer.state_dict(),
347
+ "scheduler_state_dict": scheduler.state_dict() if scheduler else None,
348
+ "metrics": metrics,
349
+ }
350
+
351
+ # Save regular checkpoint
352
+ checkpoint_path = os.path.join(save_dir, f"checkpoint_epoch_{epoch}.pt")
353
+ torch.save(checkpoint, checkpoint_path)
354
+
355
+ # Save best model
356
+ if is_best:
357
+ best_path = os.path.join(save_dir, "best_model.pt")
358
+ torch.save(checkpoint, best_path)
359
+ print(f"✓ Saved best model at epoch {epoch}")
360
+
361
+ # Keep only last 5 checkpoints
362
+ checkpoints = sorted(
363
+ [f for f in os.listdir(save_dir) if f.startswith("checkpoint_epoch_")]
364
+ )
365
+ if len(checkpoints) > 5:
366
+ for old_ckpt in checkpoints[:-5]:
367
+ os.remove(os.path.join(save_dir, old_ckpt))
368
+
369
+
370
+
371
+
372
+ def train_epoch(model, train_loader, optimizer, criterion, device):
373
+ model.train()
374
+ total_loss = 0
375
+ all_predictions = []
376
+ all_targets = []
377
+
378
+ for batch_idx, (x, y) in enumerate(train_loader):
379
+ x, y = x.to(device), y.to(device)
380
+
381
+ optimizer.zero_grad()
382
+
383
+ # Forward pass
384
+ predictions, _ = model(x)
385
+ predictions = predictions.squeeze(-1)
386
+
387
+ # Calculate loss
388
+ loss = criterion(predictions, y)
389
+
390
+ # Backward pass
391
+ loss.backward()
392
+ torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
393
+ optimizer.step()
394
+
395
+ total_loss += loss.item()
396
+ all_predictions.append(predictions.detach())
397
+ all_targets.append(y.detach())
398
+
399
+ avg_loss = total_loss / len(train_loader)
400
+ all_predictions = torch.cat(all_predictions, dim=0)
401
+ all_targets = torch.cat(all_targets, dim=0)
402
+ mse, mae = calculate_metrics(all_predictions, all_targets)
403
+
404
+ return avg_loss, mse, mae
405
+
406
+
407
+ def validate(model, val_loader, criterion, device):
408
+ model.eval()
409
+ total_loss = 0
410
+ all_predictions = []
411
+ all_targets = []
412
+
413
+ with torch.no_grad():
414
+ for x, y in val_loader:
415
+ x, y = x.to(device), y.to(device)
416
+
417
+ predictions, _ = model(x)
418
+ predictions = predictions.squeeze(-1)
419
+
420
+ loss = criterion(predictions, y)
421
+
422
+ total_loss += loss.item()
423
+ all_predictions.append(predictions)
424
+ all_targets.append(y)
425
+
426
+ avg_loss = total_loss / len(val_loader)
427
+ all_predictions = torch.cat(all_predictions, dim=0)
428
+ all_targets = torch.cat(all_targets, dim=0)
429
+ mse, mae = calculate_metrics(all_predictions, all_targets)
430
+
431
+ return avg_loss, mse, mae
432
+
433
+
434
+ def train_model(model, train_loader, val_loader, config):
435
+ """Main training loop"""
436
+ device = config["device"]
437
+ model = model.to(device)
438
+
439
+ # Setup
440
+ criterion = nn.MSELoss()
441
+ optimizer = torch.optim.AdamW(
442
+ model.parameters(),
443
+ lr=config["learning_rate"],
444
+ weight_decay=config["weight_decay"],
445
+ )
446
+ scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
447
+ optimizer, mode="min", factor=0.5, patience=5, verbose=True
448
+ )
449
+
450
+ # Create save directory
451
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
452
+ save_dir = os.path.join(config["save_dir"], f"run_{timestamp}")
453
+ os.makedirs(save_dir, exist_ok=True)
454
+
455
+ # Save config
456
+ with open(os.path.join(save_dir, "config.json"), "w") as f:
457
+ json.dump(config, f, indent=4)
458
+
459
+ # Initialize logger
460
+ logger = MetricsLogger(save_dir)
461
+ best_val_loss = float("inf")
462
+
463
+ print(f"{'='*60}")
464
+ print(f"Training started at {timestamp}")
465
+ print(f"Model: {config['model_name']}")
466
+ print(f"Device: {device}")
467
+ print(f"Save directory: {save_dir}")
468
+ print(f"{'='*60}\n")
469
+
470
+ # Training loop
471
+ for epoch in range(1, config["num_epochs"] + 1):
472
+ # Train
473
+ train_loss, train_mse, train_mae = train_epoch(
474
+ model, train_loader, optimizer, criterion, device
475
+ )
476
+
477
+ # Validate
478
+ val_loss, val_mse, val_mae = validate(model, val_loader, criterion, device)
479
+
480
+ # Update scheduler
481
+ scheduler.step(val_loss)
482
+ current_lr = optimizer.param_groups[0]["lr"]
483
+
484
+ # Log metrics
485
+ epoch_metrics = {
486
+ "train_loss": train_loss,
487
+ "val_loss": val_loss,
488
+ "train_mse": train_mse,
489
+ "val_mse": val_mse,
490
+ "train_mae": train_mae,
491
+ "val_mae": val_mae,
492
+ "learning_rates": current_lr,
493
+ }
494
+ logger.update(epoch_metrics)
495
+
496
+ # Print progress
497
+ print(f"Epoch {epoch}/{config['num_epochs']}")
498
+ print(
499
+ f" Train - Loss: {train_loss:.6f}, MSE: {train_mse:.6f}, MAE: {train_mae:.6f}"
500
+ )
501
+ print(f" Val - Loss: {val_loss:.6f}, MSE: {val_mse:.6f}, MAE: {val_mae:.6f}")
502
+ print(f" LR: {current_lr:.2e}")
503
+
504
+ # Save checkpoint
505
+ is_best = val_loss < best_val_loss
506
+ if is_best:
507
+ best_val_loss = val_loss
508
+
509
+ if epoch % config["save_every"] == 0 or is_best:
510
+ save_checkpoint(
511
+ model, optimizer, scheduler, epoch, epoch_metrics, save_dir, is_best
512
+ )
513
+
514
+ # Plot metrics every 10 epochs
515
+ if epoch % 10 == 0:
516
+ logger.plot_metrics()
517
+
518
+ print()
519
+
520
+ # Final save
521
+ logger.save()
522
+ logger.plot_metrics()
523
+
524
+ print(f"{'='*60}")
525
+ print(f"Training completed!")
526
+ print(f"Best validation loss: {best_val_loss:.6f}")
527
+ print(f"Results saved to: {save_dir}")
528
+ print(f"{'='*60}")
529
+
530
+ return model, logger
531
+
532
+
533
+
534
+ if __name__ == "__main__":
535
+ from data_prep.data_clean import clean_indicator
536
+ from data_prep.data_load import prepare_data
537
+
538
+ torch.autograd.set_detect_anomaly(True)
539
+
540
+ # Configuration
541
+ config = {
542
+ "model_name": "Mamba2Predictor",
543
+ "seq_length": 20,
544
+ "hidden_size": 128,
545
+ "num_layers": 3,
546
+ "inner_size": None, # Will be 2 * hidden_size by default
547
+ "head_size": 64,
548
+ "bc_head_size": 128,
549
+ "conv_kernel_size": 4,
550
+ "dropout": 0.2,
551
+ "batch_size": 64,
552
+ "num_epochs": 100,
553
+ "learning_rate": 0.001,
554
+ "weight_decay": 1e-5,
555
+ "train_split": 0.8,
556
+ "save_every": 5,
557
+ "save_dir": "./checkpoints_mamba2",
558
+ "device": "cuda" if torch.cuda.is_available() else "cpu",
559
+ }
560
+
561
+ print("Loading data...")
562
+ test_dir = "/home/aman/code/ml_fr/ml_stocks/data/NIFTY_5_years.csv"
563
+
564
+ load_df = prepare_data(test_dir)
565
+ df = clean_indicator(load_df)
566
+
567
+ # Prepare features and target
568
+ target_col = "Low"
569
+ feature_cols = [col for col in df.columns if col != target_col]
570
+
571
+ train_size = int(len(df) * config["train_split"])
572
+ train_df = df[:train_size]
573
+ val_df = df[train_size:]
574
+
575
+ scaler = StandardScaler()
576
+ train_features = scaler.fit_transform(train_df[feature_cols].values)
577
+ val_features = scaler.transform(val_df[feature_cols].values)
578
+
579
+ train_targets = train_df[target_col].values.reshape(-1, 1)
580
+ val_targets = val_df[target_col].values.reshape(-1, 1)
581
+
582
+ # Create datasets
583
+ train_dataset = TimeSeriesDataset(
584
+ train_features, train_targets, config["seq_length"]
585
+ )
586
+ val_dataset = TimeSeriesDataset(val_features, val_targets, config["seq_length"])
587
+
588
+ train_loader = DataLoader(
589
+ train_dataset, batch_size=config["batch_size"], shuffle=True, num_workers=0
590
+ )
591
+ val_loader = DataLoader(
592
+ val_dataset, batch_size=config["batch_size"], shuffle=False, num_workers=0
593
+ )
594
+
595
+ print(f"Training samples: {len(train_dataset)}")
596
+ print(f"Validation samples: {len(val_dataset)}")
597
+ print(f"Input features: {len(feature_cols)}")
598
+
599
+ # Initialize model
600
+ model = Mamba2Predictor(
601
+ input_size=len(feature_cols),
602
+ hidden_size=config["hidden_size"],
603
+ num_layers=config["num_layers"],
604
+ inner_size=config["inner_size"],
605
+ head_size=config["head_size"],
606
+ bc_head_size=config["bc_head_size"],
607
+ conv_kernel_size=config["conv_kernel_size"],
608
+ dropout=config["dropout"],
609
+ )
610
+
611
+ print(f"\nModel parameters: {sum(p.numel() for p in model.parameters()):,}")
612
+
613
+ # Train model
614
+ trained_model, metrics_logger = train_model(model, train_loader, val_loader, config)
615
+
616
+ print(
617
+ "\nTraining complete! Check the checkpoints_mamba2 directory for saved models and metrics."
618
+ )