mohsinramzan commited on
Commit
46cb848
Β·
verified Β·
1 Parent(s): 3c47152

Fixing a lethal bug

Browse files
Files changed (1) hide show
  1. app.py +55 -35
app.py CHANGED
@@ -9,7 +9,7 @@ from fastapi.middleware.cors import CORSMiddleware
9
  from tensorflow.keras import layers
10
  import keras
11
 
12
- # ── Custom SpecAugment layer (must match your training definition exactly) ─────
13
  @keras.saving.register_keras_serializable()
14
  class SpecAugment(layers.Layer):
15
  def __init__(self, time_mask=20, freq_mask=15, **kwargs):
@@ -46,17 +46,17 @@ class SpecAugment(layers.Layer):
46
  cfg = super().get_config()
47
  cfg.update({"time_mask": self.time_mask, "freq_mask": self.freq_mask})
48
  return cfg
49
-
50
- # ── Constants (from model_config.json) ────────────────────────────────────────
51
  SR = 22050
52
  DURATION = 3
53
  HOP_LENGTH = 512
54
  TIME_FRAMES = 130
55
  N_BINS = 84
56
- N_MFCC = 120
57
  CLASSES = ["Muwa", "east", "loyal", "poems", "rai"]
58
 
59
- # ── App ───────────────────────────────────────────────────────────────────────
60
  app = FastAPI(title="Audio Classifier API")
61
 
62
  app.add_middleware(
@@ -71,8 +71,12 @@ app.add_middleware(
71
  async def load_artifacts():
72
  global model, scaler_cqt, scaler_mfcc, label_encoder
73
 
74
- model = tf.keras.models.load_model("model_artifacts/best_cross_attn.keras", custom_objects={"SpecAugment": SpecAugment}, compile=False)
75
- print(" Model loaded")
 
 
 
 
76
 
77
  with open("model_artifacts/scaler_cqt.pkl", "rb") as f:
78
  scaler_cqt = pickle.load(f)
@@ -85,33 +89,42 @@ async def load_artifacts():
85
 
86
  print(" Scalers and label encoder loaded")
87
 
88
- # ── Feature extraction ─────────────────────────────────────────────────────────
89
- def extract_features(audio_path: str):
90
- y, sr = librosa.load(audio_path, sr=SR, duration=DURATION)
91
-
92
- # Pad if audio is shorter than expected
93
- expected_samples = SR * DURATION
94
- if len(y) < expected_samples:
95
- y = np.pad(y, (0, expected_samples - len(y)))
96
-
97
- # CQT β†’ (TIME_FRAMES, N_BINS)
98
- cqt = np.abs(librosa.cqt(y, sr=sr, hop_length=HOP_LENGTH, n_bins=N_BINS))
99
- cqt = librosa.util.fix_length(cqt, size=TIME_FRAMES, axis=1).T # (130, 84)
100
-
101
- # Apply scaler (fit was on 2D, so flatten β†’ scale β†’ reshape)
102
- cqt_shape = cqt.shape
103
- cqt = scaler_cqt.transform(cqt.reshape(-1, cqt_shape[-1])).reshape(cqt_shape)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- # MFCC β†’ (TIME_FRAMES, N_MFCC)
106
- mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=N_MFCC, hop_length=HOP_LENGTH)
107
- mfcc = librosa.util.fix_length(mfcc, size=TIME_FRAMES, axis=1).T # (130, 40)
108
 
109
- # Apply scaler
110
- mfcc_shape = mfcc.shape
111
- mfcc = scaler_mfcc.transform(mfcc.reshape(-1, mfcc_shape[-1])).reshape(mfcc_shape)
112
-
113
- # Add batch dimension β†’ (1, 130, 84) and (1, 130, 40)
114
- return cqt[np.newaxis], mfcc[np.newaxis]
115
 
116
  # ── Predict endpoint ───────────────────────────────────────────────────────────
117
  @app.post("/predict")
@@ -125,10 +138,17 @@ async def predict(file: UploadFile = File(...)):
125
 
126
  try:
127
  cqt, mfcc = extract_features(tmp_path)
128
- preds = model.predict([cqt, mfcc])[0] # (5,)
129
- top_idx = int(np.argmax(preds))
130
 
131
- # Use label_encoder if sklearn LabelEncoder, else fall back to CLASSES list
 
 
 
 
 
 
 
 
 
132
  try:
133
  label = label_encoder.inverse_transform([top_idx])[0]
134
  except Exception:
 
9
  from tensorflow.keras import layers
10
  import keras
11
 
12
+ # ── Custom SpecAugment layer ───────────────────────────────────────────────────
13
  @keras.saving.register_keras_serializable()
14
  class SpecAugment(layers.Layer):
15
  def __init__(self, time_mask=20, freq_mask=15, **kwargs):
 
46
  cfg = super().get_config()
47
  cfg.update({"time_mask": self.time_mask, "freq_mask": self.freq_mask})
48
  return cfg
49
+
50
+ # ── Constants ──────────────────────────────────────────────────────────────────
51
  SR = 22050
52
  DURATION = 3
53
  HOP_LENGTH = 512
54
  TIME_FRAMES = 130
55
  N_BINS = 84
56
+ N_MFCC = 40 # 40 Γ— 3 (+ delta + delta2) = 120 features
57
  CLASSES = ["Muwa", "east", "loyal", "poems", "rai"]
58
 
59
+ # ── App ────────────────────────────────────────────────────────────────────────
60
  app = FastAPI(title="Audio Classifier API")
61
 
62
  app.add_middleware(
 
71
  async def load_artifacts():
72
  global model, scaler_cqt, scaler_mfcc, label_encoder
73
 
74
+ model = tf.keras.models.load_model(
75
+ "model_artifacts/best_cross_attn.keras",
76
+ custom_objects={"SpecAugment": SpecAugment},
77
+ compile=False
78
+ )
79
+ print("Model loaded")
80
 
81
  with open("model_artifacts/scaler_cqt.pkl", "rb") as f:
82
  scaler_cqt = pickle.load(f)
 
89
 
90
  print(" Scalers and label encoder loaded")
91
 
92
+ # ── Feature extraction (mirrors training pipeline exactly) ─────────────────────
93
+ def extract_features_from_array(y):
94
+ # Pad or trim to fixed duration
95
+ expected = SR * DURATION
96
+ if len(y) < expected:
97
+ y = np.pad(y, (0, expected - len(y)))
98
+ else:
99
+ y = y[:expected]
100
+
101
+ # CQT β†’ amplitude_to_db β†’ (T, 84)
102
+ cqt = librosa.cqt(y=y, sr=SR, hop_length=HOP_LENGTH,
103
+ n_bins=N_BINS, bins_per_octave=12)
104
+ cqt = librosa.amplitude_to_db(np.abs(cqt)).T # (T, 84)
105
+
106
+ # MFCC + delta + delta2 β†’ (T, 120)
107
+ mfcc = librosa.feature.mfcc(y=y, sr=SR, n_mfcc=N_MFCC, hop_length=HOP_LENGTH)
108
+ delta = librosa.feature.delta(mfcc)
109
+ delta2 = librosa.feature.delta(mfcc, order=2)
110
+ mfcc = np.vstack([mfcc, delta, delta2]).T # (T, 120)
111
+
112
+ # Align & pad/trim to TIME_FRAMES
113
+ min_t = min(cqt.shape[0], mfcc.shape[0])
114
+ cqt, mfcc = cqt[:min_t], mfcc[:min_t]
115
+ if min_t < TIME_FRAMES:
116
+ cqt = np.pad(cqt, ((0, TIME_FRAMES - min_t), (0, 0)))
117
+ mfcc = np.pad(mfcc, ((0, TIME_FRAMES - min_t), (0, 0)))
118
+ else:
119
+ cqt = cqt[:TIME_FRAMES]
120
+ mfcc = mfcc[:TIME_FRAMES]
121
+
122
+ return cqt, mfcc # (130, 84), (130, 120)
123
 
 
 
 
124
 
125
+ def extract_features(audio_path: str):
126
+ y, _ = librosa.load(audio_path, sr=SR, duration=DURATION)
127
+ return extract_features_from_array(y)
 
 
 
128
 
129
  # ── Predict endpoint ───────────────────────────────────────────────────────────
130
  @app.post("/predict")
 
138
 
139
  try:
140
  cqt, mfcc = extract_features(tmp_path)
 
 
141
 
142
+ # Apply scalers exactly as in training
143
+ cqt_shape = cqt.shape
144
+ mfcc_shape = mfcc.shape
145
+ cqt = scaler_cqt.transform(cqt.reshape(-1, 84)).reshape(cqt_shape)
146
+ mfcc = scaler_mfcc.transform(mfcc.reshape(-1, 120)).reshape(mfcc_shape)
147
+
148
+ # Add batch dim
149
+ preds = model.predict([cqt[np.newaxis], mfcc[np.newaxis]])[0]
150
+ top_idx = int(np.argmax(preds))
151
+
152
  try:
153
  label = label_encoder.inverse_transform([top_idx])[0]
154
  except Exception: