fela-pdm / space /app.py
itstheraj's picture
initial commit
031d0d7
Raw
History Blame Contribute Delete
4.06 kB
import os
import sys
import gradio as gr
import numpy as np
import torch
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from modeling import load_model, validate_window
WEIGHTS = os.environ.get("FELA_PDM_WEIGHTS", ".")
MODELS = {}
def _try_load(variant):
try:
return load_model(WEIGHTS, variant=variant)
except Exception:
return None
for v in ("cmapss_FD001", "cwru"):
MODELS[v] = _try_load(v)
EXAMPLE_RUL = np.tile(np.linspace(0.3, 0.7, 30)[:, None], (1, 14))
def score_rul(text):
m = MODELS.get("cmapss_FD001")
if m is None:
return "RUL weights not found. Set FELA_PDM_WEIGHTS to a dir with cmapss_FD001.safetensors + config.json."
try:
rows = [r for r in text.strip().splitlines() if r.strip()]
arr = np.array(
[[float(x) for x in r.replace(",", " ").split()] for r in rows],
dtype=np.float32,
)
except Exception:
return "Could not parse. Provide 30 rows of 14 numbers (whitespace or comma separated)."
if arr.shape != (30, 14):
return f"Expected a (30, 14) window, got {arr.shape}."
x = torch.from_numpy(arr).reshape(1, 30, 14)
validate_window(x, m.cfg)
rul = m.predict(x, task="rul")
return f"estimated remaining useful life: {rul:.1f} cycles (capped at 125)"
def load_rul_example():
return "\n".join((" ".join((f"{v:.3f}" for v in row)) for row in EXAMPLE_RUL))
def score_cwru(text, use_synth):
m = MODELS.get("cwru")
if m is None:
return "Bearing weights not found. Set FELA_PDM_WEIGHTS to a dir with cwru.safetensors + config.json."
if use_synth or not text.strip():
t = np.linspace(0, 1, 2048)
sig = np.sin(2 * np.pi * 120 * t) + 0.2 * np.random.randn(2048)
else:
try:
sig = np.array(
[float(x) for x in text.replace(",", " ").split()], dtype=np.float32
)
except Exception:
return (
"Could not parse. Provide 2048 whitespace- or comma-separated samples."
)
if sig.size != 2048:
return f"Expected 2048 samples, got {sig.size}."
sig = (sig - sig.mean()) / (sig.std() + 1e-06)
x = torch.from_numpy(sig.astype(np.float32)).reshape(1, 2048, 1)
validate_window(x, m.cfg)
idx, prob = m.predict(x, task="cls")
return f"predicted fault class index: {idx} (probability {prob:.4f})"
with gr.Blocks(title="FELA-PdM playground") as demo:
gr.Markdown(
"# FELA-PdM playground\nOn-device predictive maintenance. Feed a sensor window and see the model's call. For research and illustration only, not a safety-critical controller; do not act on the remaining-useful-life number without independent validation."
)
with gr.Tab("Remaining useful life (C-MAPSS)"):
gr.Markdown(
"Paste 30 cycles of 14 normalized sensor values (30 rows, 14 numbers each). The FD001 head was not trained on FD002/FD003/FD004, so a window from those public NASA subsets is a real out-of-distribution test. The example below is synthetic and illustrative."
)
rul_in = gr.Textbox(label="sensor window (30 x 14)", lines=8)
rul_out = gr.Textbox(label="result")
with gr.Row():
gr.Button("Load synthetic example").click(load_rul_example, outputs=rul_in)
gr.Button("Score").click(score_rul, inputs=rul_in, outputs=rul_out)
with gr.Tab("Bearing fault (CWRU)"):
gr.Markdown(
"Paste 2048 raw vibration samples (12 kHz), or tick the box to score a synthetic illustrative window. The output is a fault-class index (10 classes)."
)
cwru_in = gr.Textbox(label="vibration window (2048 samples)", lines=4)
cwru_synth = gr.Checkbox(
label="use a synthetic illustrative window", value=True
)
cwru_out = gr.Textbox(label="result")
gr.Button("Score").click(
score_cwru, inputs=[cwru_in, cwru_synth], outputs=cwru_out
)
if __name__ == "__main__":
demo.launch()