Training
Training fits the model to your data, learning the distribution of normal rows. Nolabel_column is needed — anomaly detection is fully unsupervised. No validation metrics are computed for anomaly models.
import os, time, requests
api_key = os.getenv("WOODWIDE_API_KEY")
base_url = "https://api.woodwide.ai"
headers = {"Authorization": f"Bearer {api_key}"}
# Upload data
with open("transactions.csv", "rb") as f:
resp = requests.post(
f"{base_url}/datasets",
headers=headers,
files={"file": ("transactions.csv", f, "text/csv")},
data={"dataset_name": "transactions"},
)
dataset_id = resp.json()["dataset"]["id"]
# Train an anomaly detection model
resp = requests.post(
f"{base_url}/models/train",
headers=headers,
json={
"model_name": "fraud_detector",
"model_type": "anomaly",
"dataset_id": dataset_id,
},
)
model_id = resp.json()["model"]["id"]
# Wait for training
while True:
model = requests.get(
f"{base_url}/models/{model_id}", headers=headers
).json()
if model["status"] == "ready":
break
time.sleep(5)
import time
from pathlib import Path
from woodwide import WoodWide
client = WoodWide()
dataset = client.datasets.create(
file=Path("transactions.csv"),
dataset_name="transactions",
override=True,
)
model = client.models.train(
model_type="anomaly",
dataset_id=dataset.id,
)
model_id = model.id
while True:
model = client.models.retrieve(model_id)
if model.status in {"ready", "failed"}:
break
time.sleep(5)
if model.status == "failed":
raise RuntimeError("Training failed")
const fs = require("fs");
const FormData = require("form-data");
const apiKey = process.env.WOODWIDE_API_KEY;
const baseUrl = "https://api.woodwide.ai";
const headers = { Authorization: `Bearer ${apiKey}` };
// Upload data
const uploadForm = new FormData();
uploadForm.append("file", fs.createReadStream("transactions.csv"), "transactions.csv");
uploadForm.append("dataset_name", "transactions");
const uploadResp = await fetch(`${baseUrl}/datasets`, {
method: "POST",
headers: { ...headers, ...uploadForm.getHeaders() },
body: uploadForm,
});
const { dataset: { id: datasetId } } = await uploadResp.json();
// Train an anomaly detection model
const trainResp = await fetch(`${baseUrl}/models/train`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
model_name: "fraud_detector",
model_type: "anomaly",
dataset_id: datasetId,
}),
});
const { model: { id: modelId } } = await trainResp.json();
// Wait for training
while (true) {
const modelResp = await fetch(`${baseUrl}/models/${modelId}`, { headers });
const model = await modelResp.json();
if (model.status === "ready") break;
await new Promise((r) => setTimeout(r, 5000));
}
export WOODWIDE_API_KEY="your_api_key_here"
export BASE_URL="https://api.woodwide.ai"
# Upload data
DATASET_ID=$(curl -s -X POST "$BASE_URL/datasets" \
-H "Authorization: Bearer $WOODWIDE_API_KEY" \
-F "file=@transactions.csv" \
-F "dataset_name=transactions" | jq -r '.dataset.id')
# Train an anomaly detection model
MODEL_ID=$(curl -s -X POST "$BASE_URL/models/train" \
-H "Authorization: Bearer $WOODWIDE_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model_name\": \"fraud_detector\",
\"model_type\": \"anomaly\",
\"dataset_id\": \"$DATASET_ID\"
}" | jq -r '.model.id')
# Wait for training
while true; do
STATUS=$(curl -s "$BASE_URL/models/$MODEL_ID" \
-H "Authorization: Bearer $WOODWIDE_API_KEY" | jq -r '.status')
if [ "$STATUS" = "ready" ]; then break; fi
if [ "$STATUS" = "failed" ]; then echo "Training failed."; exit 1; fi
sleep 5
done
Inference
Run inference on data you want to scan for anomalies. This can be the training data itself (to find outliers within it) or new data (to detect rows that deviate from the training distribution). The output format depends on theanomaly_format parameter:
| Value | Description |
|---|---|
ids_only (default) | Returns a compact list of row indices flagged as anomalous. |
per_row | Returns a row for every input instance with an anomaly flag and score. |
Per-row anomaly output (
anomaly_format=per_row) is available via the HTTP API only.# Detect anomalies -- compact format (default)
with open("transactions.csv", "rb") as f:
resp = requests.post(
f"{base_url}/models/{model_id}/infer",
headers=headers,
files={"file": ("transactions.csv", f, "text/csv")},
data={"output_type": "json", "anomaly_format": "ids_only"},
)
results = resp.json()["data"]
print(results) # {"anomalous_ids": [3, 17, 42]}
# Uses client and model_id from the training example above.
from pathlib import Path
result = client.models.infer(
model_id,
file=Path("transactions.csv"),
anomaly_format="ids_only",
output_type="json",
)
print(result["data"]["anomalous_ids"])
// Detect anomalies -- compact format (default)
const form = new FormData();
form.append("file", fs.createReadStream("transactions.csv"), "transactions.csv");
form.append("output_type", "json");
form.append("anomaly_format", "ids_only");
const resp = await fetch(`${baseUrl}/models/${modelId}/infer`, {
method: "POST",
headers: { ...headers, ...form.getHeaders() },
body: form,
});
const { data: results } = await resp.json();
console.log(results); // { anomalous_ids: [3, 17, 42] }
curl -s -X POST "$BASE_URL/models/$MODEL_ID/infer" \
-H "Authorization: Bearer $WOODWIDE_API_KEY" \
-F "file=@transactions.csv" \
-F "output_type=json" \
-F "anomaly_format=ids_only" | jq '.data'
# Response data: { "anomalous_ids": [3, 17, 42] }
# Detailed per-row output
with open("transactions.csv", "rb") as f:
resp = requests.post(
f"{base_url}/models/{model_id}/infer",
headers=headers,
files={"file": ("transactions.csv", f, "text/csv")},
data={"output_type": "json", "anomaly_format": "per_row"},
)
results = resp.json()["data"]
print(results)
// Detailed per-row output
const form = new FormData();
form.append("file", fs.createReadStream("transactions.csv"), "transactions.csv");
form.append("output_type", "json");
form.append("anomaly_format", "per_row");
const resp = await fetch(`${baseUrl}/models/${modelId}/infer`, {
method: "POST",
headers: { ...headers, ...form.getHeaders() },
body: form,
});
const { data: results } = await resp.json();
console.log(results);
curl -s -X POST "$BASE_URL/models/$MODEL_ID/infer" \
-H "Authorization: Bearer $WOODWIDE_API_KEY" \
-F "file=@transactions.csv" \
-F "output_type=json" \
-F "anomaly_format=per_row" | jq '.data'