P2SAMAPA commited on
Commit
b718359
Β·
verified Β·
1 Parent(s): 7508065

[auto] Sync code from GitHub

Browse files
Files changed (1) hide show
  1. model_c.py +49 -58
model_c.py CHANGED
@@ -1,6 +1,4 @@
1
  # model_c.py β€” Option C: Wavelet-Parallel-Dual-Stream-CNN-LSTM
2
- # Two streams: ETF wavelet features + Macro features β†’ merged classification
3
-
4
  import os
5
  import numpy as np
6
  import tensorflow as tf
@@ -12,11 +10,9 @@ MODEL_NAME = "model_c"
12
  N_CLASSES = len(config.ETFS)
13
 
14
 
15
- def build_model(lookback: int, n_etf_features: int,
16
- n_macro_features: int) -> keras.Model:
17
-
18
- # ── ETF stream ────────────────────────────────────────────────────────────
19
- etf_inp = keras.Input(shape=(lookback, n_etf_features), name="etf_input")
20
  e = layers.Conv1D(64, 3, padding="causal", activation="relu")(etf_inp)
21
  e = layers.BatchNormalization()(e)
22
  e = layers.Conv1D(32, 3, padding="causal", activation="relu")(e)
@@ -26,8 +22,8 @@ def build_model(lookback: int, n_etf_features: int,
26
  e = layers.Dropout(0.2)(e)
27
  e = layers.LSTM(32)(e)
28
 
29
- # ── Macro stream ─────────────────────────────────────────────────────────
30
- mac_inp = keras.Input(shape=(lookback, n_macro_features), name="macro_input")
31
  m = layers.Conv1D(32, 3, padding="causal", activation="relu")(mac_inp)
32
  m = layers.BatchNormalization()(m)
33
  m = layers.Dropout(0.2)(m)
@@ -35,35 +31,32 @@ def build_model(lookback: int, n_etf_features: int,
35
  m = layers.Dropout(0.2)(m)
36
  m = layers.LSTM(16)(m)
37
 
38
- # ── Fusion ───────────────────────────────────────────────────────────────
39
- fused = layers.Concatenate()([e, m])
40
- x = layers.Dense(64, activation="relu")(fused)
41
- x = layers.Dropout(0.3)(x)
42
- x = layers.Dense(32, activation="relu")(x)
43
- out = layers.Dense(N_CLASSES, activation="softmax", name="output")(x)
44
 
45
  model = keras.Model(inputs=[etf_inp, mac_inp], outputs=out, name=MODEL_NAME)
46
  model.compile(
47
- optimizer = keras.optimizers.Adam(learning_rate=3e-4),
48
- loss = "sparse_categorical_crossentropy",
49
- metrics = ["accuracy"],
50
  )
51
  return model
52
 
53
 
54
  def get_callbacks(lookback: int) -> list:
55
- ckpt_path = os.path.join(config.MODELS_DIR, MODEL_NAME,
56
- f"lb{lookback}", "best.keras")
57
- os.makedirs(os.path.dirname(ckpt_path), exist_ok=True)
58
  return [
59
- keras.callbacks.EarlyStopping(
60
- monitor="val_accuracy", patience=config.PATIENCE,
61
- restore_best_weights=True, mode="max"),
62
- keras.callbacks.ModelCheckpoint(
63
- ckpt_path, monitor="val_accuracy",
64
- save_best_only=True, mode="max"),
65
- keras.callbacks.ReduceLROnPlateau(
66
- monitor="val_loss", factor=0.5, patience=5, min_lr=1e-6),
67
  ]
68
 
69
 
@@ -71,7 +64,6 @@ def save_model(model, lookback):
71
  path = os.path.join(config.MODELS_DIR, MODEL_NAME, f"lb{lookback}", "final.keras")
72
  os.makedirs(os.path.dirname(path), exist_ok=True)
73
  model.save(path)
74
- print(f" [{MODEL_NAME}] Saved β†’ {path}")
75
 
76
 
77
  def load_model(lookback):
@@ -80,42 +72,41 @@ def load_model(lookback):
80
 
81
 
82
  def train(prep: dict, epochs: int = config.MAX_EPOCHS):
83
- lookback = prep["lookback"]
84
- n_etf = prep["n_etf_features"]
85
- n_macro = prep["n_features"] - n_etf
86
- # Convert targets: handle both 1D class labels and 2D raw returns
87
- _y_tr_raw = prep["y_tr"]
88
- _y_va_raw = prep["y_va"]
89
- if _y_tr_raw.ndim == 2 and _y_tr_raw.shape[1] > 1:
90
- print(f" WARNING: y shape {_y_tr_raw.shape} β€” converting via argmax")
91
- y_tr = _y_tr_raw.argmax(axis=1).astype(np.int32)
92
- y_va = _y_va_raw.argmax(axis=1).astype(np.int32)
93
  else:
94
- y_tr = _y_tr_raw.flatten().astype(np.int32)
95
- y_va = _y_va_raw.flatten().astype(np.int32)
96
 
97
- print(f"\n[{MODEL_NAME}] lookback={lookback} "
98
- f"etf_feats={n_etf} macro_feats={n_macro}")
99
- print(f" Class dist (train): {dict(zip(*np.unique(y_tr, return_counts=True)))}")
100
 
101
  from sklearn.utils.class_weight import compute_class_weight
102
  cw = compute_class_weight("balanced", classes=np.arange(N_CLASSES), y=y_tr)
103
- class_weights = {i: w for i, w in enumerate(cw)}
104
 
105
- model = build_model(lookback, n_etf, max(n_macro, 1))
106
- X_tr_etf = prep["X_tr"][:, :, :n_etf]
107
- X_tr_mac = prep["X_tr"][:, :, n_etf:]
108
- X_va_etf = prep["X_va"][:, :, :n_etf]
109
- X_va_mac = prep["X_va"][:, :, n_etf:]
110
 
111
  history = model.fit(
112
- [X_tr_etf, X_tr_mac], y_tr,
113
- validation_data = ([X_va_etf, X_va_mac], y_va),
114
- epochs = epochs,
115
- batch_size = config.BATCH_SIZE,
116
- callbacks = get_callbacks(lookback),
117
- class_weight = class_weights,
118
- verbose = 1,
119
  )
120
  save_model(model, lookback)
121
  return model, history
 
1
  # model_c.py β€” Option C: Wavelet-Parallel-Dual-Stream-CNN-LSTM
 
 
2
  import os
3
  import numpy as np
4
  import tensorflow as tf
 
10
  N_CLASSES = len(config.ETFS)
11
 
12
 
13
+ def build_model(lookback: int, n_etf: int, n_macro: int) -> keras.Model:
14
+ # ETF stream
15
+ etf_inp = keras.Input(shape=(lookback, n_etf), name="etf_input")
 
 
16
  e = layers.Conv1D(64, 3, padding="causal", activation="relu")(etf_inp)
17
  e = layers.BatchNormalization()(e)
18
  e = layers.Conv1D(32, 3, padding="causal", activation="relu")(e)
 
22
  e = layers.Dropout(0.2)(e)
23
  e = layers.LSTM(32)(e)
24
 
25
+ # Macro stream
26
+ mac_inp = keras.Input(shape=(lookback, n_macro), name="macro_input")
27
  m = layers.Conv1D(32, 3, padding="causal", activation="relu")(mac_inp)
28
  m = layers.BatchNormalization()(m)
29
  m = layers.Dropout(0.2)(m)
 
31
  m = layers.Dropout(0.2)(m)
32
  m = layers.LSTM(16)(m)
33
 
34
+ # Fusion
35
+ x = layers.Concatenate()([e, m])
36
+ x = layers.Dense(64, activation="relu")(x)
37
+ x = layers.Dropout(0.3)(x)
38
+ x = layers.Dense(32, activation="relu")(x)
39
+ out = layers.Dense(N_CLASSES, activation="softmax")(x)
40
 
41
  model = keras.Model(inputs=[etf_inp, mac_inp], outputs=out, name=MODEL_NAME)
42
  model.compile(
43
+ optimizer=keras.optimizers.Adam(3e-4),
44
+ loss="sparse_categorical_crossentropy",
45
+ metrics=["accuracy"],
46
  )
47
  return model
48
 
49
 
50
  def get_callbacks(lookback: int) -> list:
51
+ ckpt = os.path.join(config.MODELS_DIR, MODEL_NAME, f"lb{lookback}", "best.keras")
52
+ os.makedirs(os.path.dirname(ckpt), exist_ok=True)
 
53
  return [
54
+ keras.callbacks.EarlyStopping(monitor="val_accuracy", patience=config.PATIENCE,
55
+ restore_best_weights=True, mode="max"),
56
+ keras.callbacks.ModelCheckpoint(ckpt, monitor="val_accuracy",
57
+ save_best_only=True, mode="max"),
58
+ keras.callbacks.ReduceLROnPlateau(monitor="val_loss", factor=0.5,
59
+ patience=5, min_lr=1e-6),
 
 
60
  ]
61
 
62
 
 
64
  path = os.path.join(config.MODELS_DIR, MODEL_NAME, f"lb{lookback}", "final.keras")
65
  os.makedirs(os.path.dirname(path), exist_ok=True)
66
  model.save(path)
 
67
 
68
 
69
  def load_model(lookback):
 
72
 
73
 
74
  def train(prep: dict, epochs: int = config.MAX_EPOCHS):
75
+ lookback = prep["lookback"]
76
+ n_etf = prep["n_etf_features"]
77
+ n_macro = max(prep["n_features"] - n_etf, 1)
78
+
79
+ _ytr = prep["y_tr"]
80
+ _yva = prep["y_va"]
81
+ if _ytr.ndim == 2 and _ytr.shape[1] > 1:
82
+ print(f" WARNING: y shape {_ytr.shape} β€” converting via argmax")
83
+ y_tr = _ytr.argmax(axis=1).astype(np.int32)
84
+ y_va = _yva.argmax(axis=1).astype(np.int32)
85
  else:
86
+ y_tr = _ytr.flatten().astype(np.int32)
87
+ y_va = _yva.flatten().astype(np.int32)
88
 
89
+ print(f"\n[{MODEL_NAME}] lookback={lookback} etf={n_etf} macro={n_macro} classes={N_CLASSES}")
90
+ print(f" Class dist: {dict(zip(*np.unique(y_tr, return_counts=True)))}")
 
91
 
92
  from sklearn.utils.class_weight import compute_class_weight
93
  cw = compute_class_weight("balanced", classes=np.arange(N_CLASSES), y=y_tr)
94
+ class_weights = {i: float(w) for i, w in enumerate(cw)}
95
 
96
+ model = build_model(lookback, n_etf, n_macro)
97
+ X_tr_e = prep["X_tr"][:, :, :n_etf]
98
+ X_tr_m = prep["X_tr"][:, :, n_etf:]
99
+ X_va_e = prep["X_va"][:, :, :n_etf]
100
+ X_va_m = prep["X_va"][:, :, n_etf:]
101
 
102
  history = model.fit(
103
+ [X_tr_e, X_tr_m], y_tr,
104
+ validation_data=([X_va_e, X_va_m], y_va),
105
+ epochs=epochs,
106
+ batch_size=config.BATCH_SIZE,
107
+ callbacks=get_callbacks(lookback),
108
+ class_weight=class_weights,
109
+ verbose=1,
110
  )
111
  save_model(model, lookback)
112
  return model, history