|
import gradio as gr |
|
import numpy as np |
|
import pandas as pd |
|
from momentfm import MOMENTPipeline |
|
import matplotlib.pyplot as plt |
|
from io import StringIO |
|
|
|
|
|
model = MOMENTPipeline.from_pretrained( |
|
"AutonLab/MOMENT-1-large", |
|
model_kwargs={"task_name": "reconstruction"}, |
|
) |
|
model.init() |
|
|
|
def detect_anomalies(data_input, threshold=0.05): |
|
""" |
|
Process time-series data and detect anomalies using MOMENT model |
|
""" |
|
try: |
|
|
|
if isinstance(data_input, str): |
|
|
|
try: |
|
df = pd.read_csv(StringIO(data_input)) |
|
except: |
|
|
|
try: |
|
df = pd.read_json(StringIO(data_input)) |
|
except: |
|
return "Error: Could not parse input data. Please provide valid CSV or JSON." |
|
elif isinstance(data_input, dict): |
|
df = pd.DataFrame(data_input) |
|
else: |
|
return "Error: Unsupported input format" |
|
|
|
|
|
if 'timestamp' not in df.columns or 'value' not in df.columns: |
|
return "Error: Data must contain 'timestamp' and 'value' columns" |
|
|
|
|
|
df['timestamp'] = pd.to_datetime(df['timestamp']) |
|
df = df.sort_values('timestamp') |
|
|
|
|
|
time_series = df['value'].values.astype(float) |
|
|
|
|
|
reconstruction = model.reconstruct(time_series) |
|
|
|
|
|
error = np.abs(time_series - reconstruction) |
|
|
|
|
|
df['anomaly_score'] = error |
|
df['is_anomaly'] = error > threshold * np.max(error) |
|
|
|
|
|
fig, ax = plt.subplots(figsize=(12, 6)) |
|
ax.plot(df['timestamp'], df['value'], label='Original', color='blue') |
|
ax.scatter( |
|
df[df['is_anomaly']]['timestamp'], |
|
df[df['is_anomaly']]['value'], |
|
color='red', |
|
label='Anomaly' |
|
) |
|
ax.set_title('Time Series with Anomalies Detected') |
|
ax.set_xlabel('Timestamp') |
|
ax.set_ylabel('Value') |
|
ax.legend() |
|
ax.grid(True) |
|
|
|
|
|
anomalies = df[df['is_anomaly']] |
|
stats = { |
|
"total_points": len(df), |
|
"anomalies_detected": len(anomalies), |
|
"anomaly_percentage": f"{100 * len(anomalies)/len(df):.2f}%", |
|
"max_anomaly_score": np.max(error), |
|
"threshold_used": threshold |
|
} |
|
|
|
return fig, stats, df.to_dict(orient='records') |
|
|
|
except Exception as e: |
|
return f"Error processing data: {str(e)}" |
|
|
|
|
|
with gr.Blocks(title="Equipment Anomaly Detection") as demo: |
|
gr.Markdown("# ๐ ๏ธ Equipment Sensor Anomaly Detection") |
|
gr.Markdown(""" |
|
**Detect anomalies in equipment sensor data using the MOMENT-1-large model** |
|
- Upload CSV/JSON data with 'timestamp' and 'value' columns |
|
- Adjust the sensitivity threshold as needed |
|
- Get visual and statistical results |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
input_data = gr.Textbox( |
|
label="Paste your time-series data (CSV/JSON)", |
|
placeholder="timestamp,value\n2023-01-01,1.2\n2023-01-02,1.5...", |
|
lines=5 |
|
) |
|
file_upload = gr.File(label="Or upload a file") |
|
threshold = gr.Slider( |
|
minimum=0.01, |
|
maximum=0.2, |
|
value=0.05, |
|
step=0.01, |
|
label="Anomaly Detection Sensitivity (lower = more sensitive)" |
|
) |
|
submit_btn = gr.Button("Detect Anomalies", variant="primary") |
|
|
|
with gr.Column(): |
|
plot_output = gr.Plot(label="Anomaly Detection Results") |
|
stats_output = gr.JSON(label="Detection Statistics") |
|
data_output = gr.JSON(label="Processed Data with Anomaly Scores") |
|
|
|
|
|
def process_file(file): |
|
if file: |
|
with open(file.name, 'r') as f: |
|
return f.read() |
|
return "" |
|
|
|
file_upload.change(process_file, inputs=file_upload, outputs=input_data) |
|
|
|
submit_btn.click( |
|
detect_anomalies, |
|
inputs=[input_data, threshold], |
|
outputs=[plot_output, stats_output, data_output] |
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch(server_name="0.0.0.0", server_port=7860) |
|
|