Spaces:
Build error
Build error
Upload 7 files
Browse files- Dockerfile +0 -0
- README +15 -0
- Script.py +35 -0
- app.py +29 -9
- bimboca.csv +51 -0
- requirements.txt +7 -0
- train.py +51 -0
Dockerfile
ADDED
|
File without changes
|
README
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 API de Detecção de Anomalias - JMeter
|
| 2 |
+
|
| 3 |
+
Este Space implementa uma **API FastAPI** para detectar anomalias em testes de carga.
|
| 4 |
+
|
| 5 |
+
## ▶ Funcionalidades
|
| 6 |
+
|
| 7 |
+
- Treina baseline com `resultados_carga_normal.csv`
|
| 8 |
+
- Valida com `resultados_carga_anomala.csv`
|
| 9 |
+
- API `/upload` para classificar novos CSVs
|
| 10 |
+
- Retorna:
|
| 11 |
+
- % de anomalias
|
| 12 |
+
- se disparou alerta (>5%)
|
| 13 |
+
|
| 14 |
+
## ▶ Como rodar treinamento no Space
|
| 15 |
+
|
Script.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from huggingface_hub import HfApi, create_repo, upload_file
|
| 2 |
+
|
| 3 |
+
# -------------------------------------------
|
| 4 |
+
# CONFIGURAÇÕES
|
| 5 |
+
# -------------------------------------------
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
REPO_NAME = "ChristianSardo/bimboca-dataset" # Ex: christian-sardo/bimboca-dataset
|
| 9 |
+
LOCAL_FILE_PATH = r"C:\Users\christian_sardo\Documents\ra\bimboca.csv"
|
| 10 |
+
REMOTE_PATH = "bimboca.csv" # Nome do arquivo no hub
|
| 11 |
+
|
| 12 |
+
# -------------------------------------------
|
| 13 |
+
# EXECUÇÃO
|
| 14 |
+
# -------------------------------------------
|
| 15 |
+
|
| 16 |
+
api = HfApi()
|
| 17 |
+
|
| 18 |
+
# Criar repo se não existir
|
| 19 |
+
try:
|
| 20 |
+
create_repo(repo_id=REPO_NAME, token=HF_TOKEN, repo_type="dataset")
|
| 21 |
+
print(f"Repositório criado: https://huggingface.co/datasets/{REPO_NAME}")
|
| 22 |
+
except Exception as e:
|
| 23 |
+
print(f"Repo já existe ou outro aviso: {e}")
|
| 24 |
+
|
| 25 |
+
# Upload do arquivo
|
| 26 |
+
upload_file(
|
| 27 |
+
token=HF_TOKEN,
|
| 28 |
+
path_or_fileobj=LOCAL_FILE_PATH,
|
| 29 |
+
repo_id=REPO_NAME,
|
| 30 |
+
repo_type="dataset",
|
| 31 |
+
path_in_repo=REMOTE_PATH
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
print("Upload completo!")
|
| 35 |
+
print(f"Dataset disponível em: https://huggingface.co/datasets/{REPO_NAME}")
|
app.py
CHANGED
|
@@ -3,27 +3,47 @@ import pandas as pd
|
|
| 3 |
import joblib
|
| 4 |
from io import BytesIO
|
| 5 |
|
| 6 |
-
app = FastAPI()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
model = joblib.load("modelo_anomalia.pkl")
|
| 9 |
|
| 10 |
@app.get("/")
|
| 11 |
def home():
|
| 12 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
|
|
|
| 16 |
content = await file.read()
|
| 17 |
df = pd.read_csv(BytesIO(content))
|
| 18 |
|
| 19 |
-
X = df
|
| 20 |
preds = model.predict(X)
|
| 21 |
|
|
|
|
|
|
|
| 22 |
percent_anom = (preds == -1).mean() * 100
|
| 23 |
|
|
|
|
|
|
|
| 24 |
return {
|
| 25 |
-
"anomalias": int((preds == -1).sum()),
|
| 26 |
"total": len(df),
|
| 27 |
-
"
|
| 28 |
-
"
|
|
|
|
|
|
|
| 29 |
}
|
|
|
|
| 3 |
import joblib
|
| 4 |
from io import BytesIO
|
| 5 |
|
| 6 |
+
app = FastAPI(title="Detecção de Anomalias JMeter")
|
| 7 |
+
|
| 8 |
+
MODEL_PATH = "modelo_anomalia.pkl"
|
| 9 |
+
|
| 10 |
+
# Carrega o modelo na inicialização
|
| 11 |
+
model = joblib.load(MODEL_PATH)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def preprocess(df):
|
| 15 |
+
if df["success"].dtype == object:
|
| 16 |
+
df["success"] = df["success"].map(lambda x: 1 if str(x).lower() == "true" else 0)
|
| 17 |
+
return df[["elapsed", "success"]]
|
| 18 |
|
|
|
|
| 19 |
|
| 20 |
@app.get("/")
|
| 21 |
def home():
|
| 22 |
+
return {
|
| 23 |
+
"status": "online",
|
| 24 |
+
"msg": "API de detecção de anomalias está funcionando!",
|
| 25 |
+
"rota_upload": "/upload"
|
| 26 |
+
}
|
| 27 |
|
| 28 |
+
|
| 29 |
+
@app.post("/upload")
|
| 30 |
+
async def classify(file: UploadFile = File(...)):
|
| 31 |
content = await file.read()
|
| 32 |
df = pd.read_csv(BytesIO(content))
|
| 33 |
|
| 34 |
+
X = preprocess(df)
|
| 35 |
preds = model.predict(X)
|
| 36 |
|
| 37 |
+
df["anomalia"] = preds
|
| 38 |
+
|
| 39 |
percent_anom = (preds == -1).mean() * 100
|
| 40 |
|
| 41 |
+
alerta = percent_anom > 5
|
| 42 |
+
|
| 43 |
return {
|
|
|
|
| 44 |
"total": len(df),
|
| 45 |
+
"anomalias": int((preds == -1).sum()),
|
| 46 |
+
"percentual_anomalias": percent_anom,
|
| 47 |
+
"alerta": alerta,
|
| 48 |
+
"limite": "Ultrapassado (>5%)" if alerta else "Normal",
|
| 49 |
}
|
bimboca.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect
|
| 2 |
+
1764196174810,1401,transacao,200,OK,Thread Group 1-1,text,true,,3612,0,3,3,https://dev-finance.netlify.app/#,0,0,0
|
| 3 |
+
1764196174810,1443,transacao,200,OK,Thread Group 1-2,text,true,,3612,0,4,4,https://dev-finance.netlify.app/#,0,0,0
|
| 4 |
+
1764196175715,855,transacao,200,OK,Thread Group 1-3,text,true,,3612,0,4,4,https://dev-finance.netlify.app/#,0,0,0
|
| 5 |
+
1764196177358,879,transacao,200,OK,Thread Group 1-4,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 6 |
+
1764196178046,696,transacao,200,OK,Thread Group 1-5,text,true,,3612,0,3,3,https://dev-finance.netlify.app/#,0,0,0
|
| 7 |
+
1764196179305,679,transacao,200,OK,Thread Group 1-6,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 8 |
+
1764196180452,673,transacao,200,OK,Thread Group 1-7,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 9 |
+
1764196181669,723,transacao,200,OK,Thread Group 1-8,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 10 |
+
1764196182884,651,transacao,200,OK,Thread Group 1-9,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 11 |
+
1764196184104,641,transacao,200,OK,Thread Group 1-10,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 12 |
+
1764196185225,659,transacao,200,OK,Thread Group 1-11,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 13 |
+
1764196186443,1161,transacao,200,OK,Thread Group 1-12,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 14 |
+
1764196187686,1295,transacao,200,OK,Thread Group 1-13,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 15 |
+
1764196188907,674,transacao,200,OK,Thread Group 1-14,text,true,,3612,0,3,3,https://dev-finance.netlify.app/#,0,0,0
|
| 16 |
+
1764196190084,699,transacao,200,OK,Thread Group 1-15,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 17 |
+
1764196191306,826,transacao,200,OK,Thread Group 1-16,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 18 |
+
1764196192438,645,transacao,200,OK,Thread Group 1-17,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 19 |
+
1764196193641,656,transacao,200,OK,Thread Group 1-18,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 20 |
+
1764196194834,629,transacao,200,OK,Thread Group 1-19,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 21 |
+
1764196196019,675,transacao,200,OK,Thread Group 1-20,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 22 |
+
1764196197254,723,transacao,200,OK,Thread Group 1-21,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 23 |
+
1764196198436,681,transacao,200,OK,Thread Group 1-22,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 24 |
+
1764196199617,645,transacao,200,OK,Thread Group 1-23,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 25 |
+
1764196200852,674,transacao,200,OK,Thread Group 1-24,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 26 |
+
1764196202089,1465,transacao,200,OK,Thread Group 1-25,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 27 |
+
1764196203203,707,transacao,200,OK,Thread Group 1-26,text,true,,3612,0,3,3,https://dev-finance.netlify.app/#,0,0,0
|
| 28 |
+
1764196204435,671,transacao,200,OK,Thread Group 1-27,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 29 |
+
1764196205768,727,transacao,200,OK,Thread Group 1-28,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 30 |
+
1764196206851,752,transacao,200,OK,Thread Group 1-29,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 31 |
+
1764196208034,654,transacao,200,OK,Thread Group 1-30,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 32 |
+
1764196209262,660,transacao,200,OK,Thread Group 1-31,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 33 |
+
1764196210451,668,transacao,200,OK,Thread Group 1-32,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 34 |
+
1764196211703,668,transacao,200,OK,Thread Group 1-33,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 35 |
+
1764196212885,733,transacao,200,OK,Thread Group 1-34,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 36 |
+
1764196214036,676,transacao,200,OK,Thread Group 1-35,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 37 |
+
1764196215251,634,transacao,200,OK,Thread Group 1-36,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 38 |
+
1764196216437,666,transacao,200,OK,Thread Group 1-37,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 39 |
+
1764196217656,655,transacao,200,OK,Thread Group 1-38,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 40 |
+
1764196218821,662,transacao,200,OK,Thread Group 1-39,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 41 |
+
1764196220034,672,transacao,200,OK,Thread Group 1-40,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 42 |
+
1764196221288,684,transacao,200,OK,Thread Group 1-41,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 43 |
+
1764196222450,697,transacao,200,OK,Thread Group 1-42,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 44 |
+
1764196223620,780,transacao,200,OK,Thread Group 1-43,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 45 |
+
1764196224886,628,transacao,200,OK,Thread Group 1-44,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 46 |
+
1764196226102,690,transacao,200,OK,Thread Group 1-45,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 47 |
+
1764196227301,685,transacao,200,OK,Thread Group 1-46,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 48 |
+
1764196228484,822,transacao,200,OK,Thread Group 1-47,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 49 |
+
1764196229637,708,transacao,200,OK,Thread Group 1-48,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 50 |
+
1764196230817,645,transacao,200,OK,Thread Group 1-49,text,true,,3612,0,2,2,https://dev-finance.netlify.app/#,0,0,0
|
| 51 |
+
1764196232037,625,transacao,200,OK,Thread Group 1-50,text,true,,3612,0,1,1,https://dev-finance.netlify.app/#,0,0,0
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
pandas
|
| 4 |
+
scikit-learn
|
| 5 |
+
joblib
|
| 6 |
+
python-multipart
|
| 7 |
+
matplotlib
|
train.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
from sklearn.ensemble import IsolationForest
|
| 3 |
+
from sklearn.preprocessing import LabelEncoder
|
| 4 |
+
import joblib
|
| 5 |
+
|
| 6 |
+
# ARQUIVOS
|
| 7 |
+
CSV_NORMAL = "resultados_carga_normal.csv"
|
| 8 |
+
CSV_ANORMAL = "resultados_carga_anomala.csv"
|
| 9 |
+
OUTPUT_MODEL = "modelo_anomalia.pkl"
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def preprocess(df):
|
| 13 |
+
# Converte success TRUE/FALSE para 1/0 se necessário
|
| 14 |
+
if df["success"].dtype == object:
|
| 15 |
+
df["success"] = df["success"].map(lambda x: 1 if str(x).lower() == "true" else 0)
|
| 16 |
+
|
| 17 |
+
# Apenas colunas importantes
|
| 18 |
+
return df[["elapsed", "success"]]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def train_baseline():
|
| 22 |
+
print("🔵 Treinando baseline com resultados normais...")
|
| 23 |
+
|
| 24 |
+
df = pd.read_csv(CSV_NORMAL)
|
| 25 |
+
X = preprocess(df)
|
| 26 |
+
|
| 27 |
+
model = IsolationForest(contamination=0.05, random_state=42)
|
| 28 |
+
model.fit(X)
|
| 29 |
+
|
| 30 |
+
joblib.dump(model, OUTPUT_MODEL)
|
| 31 |
+
print("✅ Modelo salvo como", OUTPUT_MODEL)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def validate_anomalies():
|
| 35 |
+
print("🔴 Validando com dados anômalos...")
|
| 36 |
+
|
| 37 |
+
df = pd.read_csv(CSV_ANORMAL)
|
| 38 |
+
X = preprocess(df)
|
| 39 |
+
|
| 40 |
+
model = joblib.load(OUTPUT_MODEL)
|
| 41 |
+
|
| 42 |
+
preds = model.predict(X)
|
| 43 |
+
|
| 44 |
+
percent_anom = (preds == -1).mean() * 100
|
| 45 |
+
|
| 46 |
+
print(f"🚨 Percentual de anomalias detectadas: {percent_anom:.2f}%")
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
if __name__ == "__main__":
|
| 50 |
+
train_baseline()
|
| 51 |
+
validate_anomalies()
|