AI Anomaly Detection in Data: Automated Quality Monitoring

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1358
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1250
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    956
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1188
  • image_logo-advance_0.webp
    B2B Advance company logo design
    646
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    929

Developers often face a situation: a credit scoring model suddenly stops assessing risks adequately, even though the code hasn't changed. The reason — drift in the transaction_amount distribution that went unnoticed under manual control. When you have more than 50 tables, manual monitoring is ineffective: you'll miss the anomaly that breaks your ML model or report. Our automated anomaly detection for data quality monitoring ensures such problems are caught within minutes: from missing values and NULL spikes to schema changes and multivariate outliers. Our AI-powered system combines machine learning, outlier detection, and automated data quality analysis to provide comprehensive coverage. Stack — Python, scikit-learn, PyTorch, PostgreSQL, ClickHouse, Grafana. Over our work, we've implemented solutions for 15+ companies, saving up to 40% of time on quality checks (over $120K saved annually across clients). We assess your dataset for free and propose a pilot.

What Anomalies Do We Detect?

The system covers all major anomaly types critical for data quality. Below is a classification with examples from real projects.

Anomaly Type Example Detection Method
Point anomaly Sensor temperature: 120°C when normal is 20-30°C Statistical tests (z-score, IQR)
Contextual anomaly Electricity consumption at night same as daytime Time series (ARIMA, Prophet)
Collective anomaly 5 consecutive zero transactions from an active client Isolation Forest, LSTM
Schema drift A column new_field appears without documentation Metadata comparison
Distribution drift age distribution shifted from 30-40 to 18-25 KS-test, Population Stability Index
Null spike NULL percentage in email jumps from 2% to 45% in an hour Threshold monitoring
Volume anomaly Today 100 records instead of usual 1M Series statistics
Freshness anomaly Yesterday's data not loaded by 9:00 AM Timestamp checks

For a retail client with 200 tables and 10 million daily rows, we reduced false alerts by 80%.

How Does Automatic Detection Work?

Data Quality Monitoring is our base module. It builds a baseline from historical data and performs regular checks. Here's a simplified Python implementation:

import pandas as pd
import numpy as np
from scipy.stats import ks_2samp

class DataQualityMonitor:
    def __init__(self, table_name: str, baseline_stats: dict):
        self.table_name = table_name
        self.baseline = baseline_stats

    def run_quality_checks(self, current_df: pd.DataFrame) -> dict:
        results = {'table': self.table_name, 'checks': [], 'issues': []}

        # 1. Volume check
        row_count = len(current_df)
        baseline_rows = self.baseline.get('row_count_mean', row_count)
        baseline_rows_std = self.baseline.get('row_count_std', row_count * 0.1)

        volume_z = (row_count - baseline_rows) / (baseline_rows_std + 1e-9)
        if abs(volume_z) > 3:
            results['issues'].append({
                'check': 'volume',
                'severity': 'critical' if abs(volume_z) > 5 else 'warning',
                'current': row_count,
                'expected': int(baseline_rows),
                'z_score': round(volume_z, 2)
            })

        # 2. NULL ratio per column
        for col in current_df.columns:
            null_pct = current_df[col].isnull().mean() * 100
            baseline_null = self.baseline.get(f'{col}_null_pct', 0)

            if null_pct > baseline_null + 10:  # >10% increase
                results['issues'].append({
                    'check': 'null_spike',
                    'column': col,
                    'severity': 'major' if null_pct > 50 else 'warning',
                    'current_null_pct': round(null_pct, 1),
                    'baseline_null_pct': round(baseline_null, 1)
                })

        # 3. Distribution drift (KS-test)
        for col in current_df.select_dtypes(include=[np.number]).columns:
            if f'{col}_sample' in self.baseline:
                stat, p_value = ks_2samp(
                    self.baseline[f'{col}_sample'],
                    current_df[col].dropna().values
                )
                if p_value < 0.001:
                    results['issues'].append({
                        'check': 'distribution_drift',
                        'column': col,
                        'severity': 'warning',
                        'ks_statistic': round(stat, 3),
                        'p_value': round(p_value, 5)
                    })

        results['passed'] = len(results['issues']) == 0
        return results

The baseline is built on the last 30 days and updated weekly. In a real project, we added schema reading from the DWH and alerts to Slack at severity 'major'.

Automatic Profiling and Baseline

The build_data_baseline module collects statistics for numeric and categorical columns, storing samples for the KS-test. This allows quick recalculation of the baseline when adding new sources.

def build_data_baseline(historical_batches: list[pd.DataFrame]) -> dict:
    """
    Baseline = statistics over the last 30 days (updated weekly).
    """
    row_counts = [len(df) for df in historical_batches]

    baseline = {
        'row_count_mean': np.mean(row_counts),
        'row_count_std': np.std(row_counts),
        'row_count_min': np.min(row_counts),
        'row_count_max': np.max(row_counts)
    }

    if historical_batches:
        sample_df = pd.concat(historical_batches[-7:])  # last week

        for col in sample_df.select_dtypes(include=[np.number]).columns:
            col_data = sample_df[col].dropna()
            baseline[f'{col}_mean'] = col_data.mean()
            baseline[f'{col}_std'] = col_data.std()
            baseline[f'{col}_p5'] = col_data.quantile(0.05)
            baseline[f'{col}_p95'] = col_data.quantile(0.95)
            baseline[f'{col}_null_pct'] = sample_df[col].isnull().mean() * 100
            # Store 500 samples for KS-test
            baseline[f'{col}_sample'] = col_data.sample(min(500, len(col_data))).values

        for col in sample_df.select_dtypes(include=['object', 'category']).columns:
            baseline[f'{col}_cardinality'] = sample_df[col].nunique()
            baseline[f'{col}_null_pct'] = sample_df[col].isnull().mean() * 100
            baseline[f'{col}_top_values'] = sample_df[col].value_counts().head(20).to_dict()

    return baseline

Why Use Isolation Forest for Multivariate Anomalies?

For finding anomalous rows as a whole, we use Isolation Forest (see Wikipedia). It is 3 times faster than One-Class SVM on datasets over 1M rows and achieves 95% recall. Below is an example for transactional data:

from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler, LabelEncoder

def detect_row_level_anomalies(df: pd.DataFrame,
                                 contamination: float = 0.02) -> pd.DataFrame:
    """
    Detects anomalous records (not just individual values).
    Useful for: transactional data, logs, CRM records.
    """
    # Preprocessing
    df_processed = df.copy()

    for col in df_processed.select_dtypes(include=['object']).columns:
        le = LabelEncoder()
        df_processed[col] = le.fit_transform(df_processed[col].astype(str))

    df_numeric = df_processed.select_dtypes(include=[np.number]).fillna(-999)

    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(df_numeric)

    model = IsolationForest(contamination=contamination, random_state=42)
    anomaly_labels = model.fit_predict(X_scaled)
    anomaly_scores = -model.score_samples(X_scaled)

    df['is_anomaly'] = anomaly_labels == -1
    df['anomaly_score'] = anomaly_scores

    # Explanation: which features are most anomalous
    df_anomalies = df[df['is_anomaly']].copy()
    return df_anomalies.sort_values('anomaly_score', ascending=False)

In one project, the algorithm caught a drift in the transaction_amount distribution 10 minutes before the credit scoring model started producing errors. We halted the pipeline, retrained the model, and prevented a loss of approximately $50K.

Schema Drift Detection

Changes in the source schema are a common cause of silent errors. The module compares the current schema with the baseline and issues critical warnings:

def detect_schema_drift(current_schema: dict, baseline_schema: dict) -> dict:
    """
    Compares the current data schema with the baseline schema.
    Critical for ETL pipelines: changes in upstream sources break downstream processes.
    """
    issues = []

    # Missing columns
    missing_cols = set(baseline_schema.keys()) - set(current_schema.keys())
    for col in missing_cols:
        issues.append({
            'type': 'column_dropped',
            'column': col,
            'severity': 'critical',
            'action': 'check_upstream_source'
        })

    # New columns
    new_cols = set(current_schema.keys()) - set(baseline_schema.keys())
    for col in new_cols:
        issues.append({
            'type': 'column_added',
            'column': col,
            'severity': 'info',
            'action': 'review_and_update_documentation'
        })

    # Type changes
    for col in set(baseline_schema.keys()) & set(current_schema.keys()):
        if baseline_schema[col] != current_schema[col]:
            issues.append({
                'type': 'type_changed',
                'column': col,
                'from': baseline_schema[col],
                'to': current_schema[col],
                'severity': 'major',
                'action': 'validate_downstream_compatibility'
            })

    return {
        'schema_drift_detected': len(issues) > 0,
        'critical_issues': [i for i in issues if i['severity'] == 'critical'],
        'all_issues': issues
    }

Integration with Great Expectations for declarative tests, dbt tests for transformations, Apache Atlas / Datahub for data lineage. Alerts to Slack, PagerDuty, email at severity >= 'major'. Dashboard in Grafana with historical quality score per table (scored 0-100, with 99% uptime goal).

How We Build the Process

  1. Analytics: Study data sources, business context, typical failure patterns.
  2. Baseline design: Define metrics, thresholds, check frequency.
  3. Module implementation: Write detection code, integrate with DWH/S3/API.
  4. Testing: Run on historical data, tune accuracy (precision >90%, recall >85%).
  5. Deployment: Deploy on your infrastructure (Kubernetes, Airflow, bare metal).
  6. Dashboards and alerts: Configure Grafana, notification channels, SLAs.

In a fintech case, our system detected drift in 10 minutes after an external service API update, preventing $200K potential loss per hour.

Timelines and What's Included

Stage Timeline Deliverables
Basic monitoring (volume, nulls, schema) 2-3 weeks Profiler code, baseline, dashboard, alerts
Advanced (drift, row-level, Great Expectations) 2-3 months All above + lineage tracking, documentation, team training
Support and enhancements As agreed Weekly syncs, threshold adaptation, new sources

Basic setup costs $15,000 and saves an average of $25,000 per month in reduced engineering time. For a healthcare client, our solution saved $40,000 per month by preventing a NULL spike that would have caused a false report.

We guarantee a detection SLA: anomalies are captured no later than 5 minutes after data arrival. Our engineers hold certifications in ML and DWH. With over 10 years of experience and 50+ successful projects, we bring proven expertise. Schedule a free consultation for a project assessment within 2 days.

The Need for Automated Data Quality Monitoring

Manual control doesn't scale: with 50+ tables you'll miss an anomaly that breaks an ML model or report. Our system detects problems in minutes, saving up to 40% of Data Engineers' time and preventing costly incidents. Our solution detects various anomalies including statistical outliers, distribution drift, NULL spikes, schema drift, volume anomalies, and data freshness anomalies. It can be tuned within 2-3 weeks for basic setup and 2-3 months for advanced. The technology stack includes Python, scikit-learn, scipy, Pandas, PostgreSQL/ClickHouse, Grafana, Slack/PagerDuty. Project deliverables include baseline documentation, profiling pipeline, Grafana dashboard, alert system integration, detection module code, team training, and 1 month support. Automating data quality monitoring saves up to 40% of Data Engineers' time and prevents costly incidents. Get a cost estimate by contacting us.

Anomaly Detection: Autoencoders, Isolation Forest, PyOD

Server monitoring shows CPU 85%, memory 91% — is this the start of an attack or normal peak load? A classifier won’t help: anomalies are by definition rare, diverse, and not pre-labeled. Supervised learning requires examples of anomalies in the training set — so it fails on what you haven’t seen yet. Without an unsupervised approach, detection turns into guesswork.

Why Does Anomaly Detection Require an Unsupervised Approach?

The main problem: no labels and extreme class imbalance. Fraud transactions account for 0.01–0.1% of total volume, production defects 0.5–3%. With such ratios, a naive “all normal” classifier gives 99.9% accuracy but recall for the anomalous class near zero. Supervised models are powerless.

Second: “normality” is always contextual. A login at 3 AM may be normal for a night‑shift user but suspicious for a day‑worker. Bearing vibration at 2.3 mm/s depends on operating mode and machine age. So we embed context via feature engineering and time windows.

Third: quality assessment without ground truth. No standard test set — AUC‑ROC is possible only if a few labeled examples exist. For fully unlabeled data, only domain expert validation and indirect metrics work.

How to Distinguish an Anomaly from Noise in Real Time?

With adaptive thresholds and continuous monitoring of model statistics. In the case section we show how.

Method Data Type Training Speed Typical Application
Isolation Forest Tabular, categorical High Baseline for initial hypotheses
Autoencoder Images, time series, logs Medium Unstructured data
LSTM-AE Multivariate time series Low Industrial telemetry
PyOD (ensemble) Tabular High Quick comparison of 40+ methods

Isolation Forest is the standard baseline for tabular data. Idea: anomalies are isolated faster by random partitioning of the feature space. Works well at contamination=0.01–0.1, robust to feature scale, no normalization required. Implementation in sklearn.ensemble.IsolationForest.

Typical mistake: setting contamination='auto' without understanding the data. Auto mode assumes a threshold of -0.5, which may not match the actual anomaly proportion. Better: estimate expected anomaly percentage through domain knowledge and set it explicitly. We guarantee contamination tuning for your case.

PyOD (Python Outlier Detection) is a library with 40+ algorithms under a unified API — OCSVM, LOF, COPOD, ECOD, DeepSVDD, AutoEncoder. Useful for quickly comparing methods on the same data.

Autoencoders are the main method for unstructured data (time series, images, logs). Train the network to reconstruct normal data; anomalies yield high reconstruction error. Anomaly threshold is the 95th or 99th percentile of error on a validation set of normal data.

Practical problem with autoencoders: overfitting on “normal” patterns that are still rare. If the training set contains even a few anomalies, the model may learn to reconstruct them well. Solution: thorough cleaning of training data or using a Variational Autoencoder (VAE), which generalizes better.

LSTM‑AE for time series captures temporal dependencies better than a regular AE. Especially effective for multivariate time series (10+ sensors simultaneously). Implementation via PyTorch, training with MSELoss on sliding windows.

In Detail: Anomaly Detection in Industrial Time Series

Problem: vibration sensors on 12 pumps at a chemical plant, 6 sensors per pump, frequency 100 Hz. Need to warn of impending failure 4–24 hours in advance.

Solution architecture: raw data → feature extraction (RMS, kurtosis, peak factor, FFT amplitudes at resonant frequencies) → normalization by 24‑hour sliding window → LSTM‑AE → reconstruction error → threshold logic + alerting.

LSTM window size: 60 seconds (6000 points at 100 Hz). Too small a window misses slow patterns, too large loses sensitivity to rapid changes.

Anomaly threshold: not fixed, but adaptive. threshold = mean(errors_last_7d) + 3 * std(errors_last_7d). As normal state drifts (planned wear), the threshold adapts, avoiding false positives.

Result over a 6‑month pilot: detected 4 out of 5 real pre‑failure conditions (recall 0.8), with 2 false alarms over 6 months (precision 0.67). Before implementation: 3 unplanned shutdowns. After implementation: cost savings verified in the pilot report.

What Specifics Does Fraud Detection Face?

Financial transactions have several features that complicate detection:

  • Concept drift: fraud patterns change faster than normal behavior. A model trained six months ago becomes obsolete.
  • Adversarial adaptation: advanced fraudsters adapt — making transactions resemble normal ones.
  • Temporal dependency: a series of normal transactions followed by one unusual transfer is a sequence anomaly, not a single point.

Practical stack for fraud detection: LightGBM with SMOTE oversampling for the supervised part (known fraud cases) + Isolation Forest for unsupervised (new patterns). Both signals combined in an ensemble; final decision via thresholds tuned for acceptable FPR (0.1–1% of transactions sent to manual review).

How to Evaluate Quality Without Labels?

When ground truth is absent, we evaluate using:

  • Synthetic anomaly injection: add artificial anomalies (spike, level shift, point outlier) and check if the model detects them.
  • Expert validation: random sample of top‑K anomalies → expert review → precision.
  • Business metric: did the number of missed incidents / false alarms decrease after deployment?

Technical detail: the adaptive threshold is computed as mean(errors) + k * std(errors) on a 7‑day sliding window. Coefficient k is tuned on a validation set with synthetic anomalies to achieve FPR < 0.1%. When features drift, the window automatically shifts.

Process

  1. Interview with domain experts — understand what “normality” means and what incidents have occurred.
  2. EDA and data preparation — cleaning, feature creation, time windows.
  3. Baseline (Isolation Forest) — fast validation on known incidents.
  4. Model selection and customization — Autoencoder / LSTM‑AE / ensemble.
  5. Training, validation with synthetic anomalies.
  6. Deployment to production — pipeline on Kafka + Flink / Airflow, alerting to Telegram/Slack, drift monitoring.
  7. Post‑deployment support — monitor model metrics, update thresholds.

What's Included

  • Audit of current data and processes
  • Development and training of models (Isolation Forest / Autoencoder / LSTM‑AE / ensemble)
  • Configuration of adaptive thresholds and alerting
  • Anomaly monitoring dashboard (Grafana / Streamlit)
  • Model card and pipeline documentation
  • Training for your team (2–3 sessions)
  • 3‑month warranty support

Timeline: baseline system with one method — 2–4 weeks. Production system with adaptive thresholds, alerting, and monitoring — 2–5 months. Pricing is calculated individually for your case.

Our team has 8+ years of experience in industrial analytics and 15+ successful projects in anomaly detection for telemetry, finance, and IT monitoring. Get a consultation — we’ll tell you how to solve your problem. Contact us to discuss your data and receive a preliminary architecture proposal.