{"id":97806,"date":"2026-07-04T16:17:11","date_gmt":"2026-07-04T10:47:11","guid":{"rendered":"https:\/\/staging.nanoschool.in\/wp\/study\/real-time-data-validation-techniques\/"},"modified":"2026-07-04T16:17:11","modified_gmt":"2026-07-04T10:47:11","slug":"real-time-data-validation-techniques","status":"publish","type":"tva_lesson","link":"https:\/\/staging.nanoschool.in\/wp\/study\/real-time-data-validation-techniques\/","title":{"rendered":"Real-time data validation techniques"},"content":{"rendered":"<h3 data-end=\"268\" data-section-id=\"12nqjtr\" data-start=\"212\" style=\"\">1) What \u201creal-time validation\u201d means in ML pipelines<\/h3>\n<p data-end=\"401\" data-start=\"270\" style=\"\"><strong data-end=\"299\" data-start=\"270\">Real-time data validation<\/strong> is the practice of checking incoming data <em data-end=\"357\" data-start=\"342\">as it arrives<\/em> (streaming or micro-batch) to ensure it is:<\/p>\n<ul data-end=\"605\" data-start=\"403\">\n<li data-end=\"437\" data-section-id=\"1kwomwe\" data-start=\"403\" style=\"\"><strong data-end=\"420\" data-start=\"405\">Well-formed<\/strong> (schema correct)<\/li>\n<li data-end=\"473\" data-section-id=\"1cmtqis\" data-start=\"438\" style=\"\"><strong data-end=\"453\" data-start=\"440\">Plausible<\/strong> (values make sense)<\/li>\n<li data-end=\"542\" data-section-id=\"mirpvv\" data-start=\"474\" style=\"\"><strong data-end=\"490\" data-start=\"476\">Consistent<\/strong> (matches expected distributions and business rules)<\/li>\n<li data-end=\"605\" data-section-id=\"19jfhlf\" data-start=\"543\" style=\"\"><strong data-end=\"560\" data-start=\"545\">Safe to use<\/strong> (not poisoned, not anomalous, not malicious)<\/li>\n<\/ul>\n<p data-end=\"710\" data-start=\"607\" style=\"\">Instead of discovering problems after training degrades, real-time validation acts as a <strong data-end=\"709\" data-start=\"695\">gatekeeper<\/strong>:<\/p>\n<p data-end=\"830\" data-start=\"712\" style=\"\"><strong data-end=\"830\" data-start=\"712\">Source \u2192 Ingestion \u2192 Validation Gate \u2192 (Accept | Quarantine | Reject) \u2192 Storage\/Feature Store \u2192 Training\/Inference<\/strong><\/p>\n<h3 data-end=\"888\" data-section-id=\"1djni09\" data-start=\"837\" style=\"\">2) What threats real-time validation helps stop<\/h3>\n<ul data-end=\"1229\" data-start=\"890\">\n<li data-end=\"957\" data-section-id=\"1g2yljw\" data-start=\"890\" style=\"\"><strong data-end=\"914\" data-start=\"892\">Poisoning attempts<\/strong> (outliers, backdoor patterns, label flips)<\/li>\n<li data-end=\"1032\" data-section-id=\"x6trr4\" data-start=\"958\" style=\"\"><strong data-end=\"978\" data-start=\"960\">Schema attacks<\/strong> (type changes, column swaps, missing critical fields)<\/li>\n<li data-end=\"1106\" data-section-id=\"nq6ox6\" data-start=\"1033\" style=\"\"><strong data-end=\"1059\" data-start=\"1035\">Silent upstream bugs<\/strong> (wrong units, wrong encoding, shifted meaning)<\/li>\n<li data-end=\"1174\" data-section-id=\"92zpxe\" data-start=\"1107\" style=\"\"><strong data-end=\"1123\" data-start=\"1109\">Data drift<\/strong> (distribution shifts that break model assumptions)<\/li>\n<li data-end=\"1229\" data-section-id=\"uz6bbc\" data-start=\"1175\" style=\"\"><strong data-end=\"1190\" data-start=\"1177\">API abuse<\/strong> (payload flooding, malformed requests)<\/li>\n<\/ul>\n<h2 data-end=\"1293\" data-section-id=\"1acmrwj\" data-start=\"1236\" style=\"\">3) The validation stack (recommended layered approach)<\/h2>\n<h3 data-end=\"1338\" data-section-id=\"1xf4cf7\" data-start=\"1295\" style=\"\">Layer A \u2014 Schema validation (hard gate)<\/h3>\n<p data-end=\"1390\" data-start=\"1339\" style=\"\">Validate structure <strong data-end=\"1368\" data-start=\"1358\">before<\/strong> any ML preprocessing.<\/p>\n<p data-end=\"1399\" data-start=\"1392\" style=\"\">Checks:<\/p>\n<ul data-end=\"1555\" data-start=\"1400\">\n<li data-end=\"1425\" data-section-id=\"m8vz6j\" data-start=\"1400\" style=\"\">required fields present<\/li>\n<li data-end=\"1465\" data-section-id=\"130k716\" data-start=\"1426\" style=\"\">correct data types (int\/float\/string)<\/li>\n<li data-end=\"1506\" data-section-id=\"1dweqvw\" data-start=\"1466\" style=\"\">allowed categorical values (allowlist)<\/li>\n<li data-end=\"1555\" data-section-id=\"1sqeduw\" data-start=\"1507\" style=\"\">array lengths, image sizes, text length limits<\/li>\n<\/ul>\n<p data-end=\"1564\" data-start=\"1557\" style=\"\">Action:<\/p>\n<ul data-end=\"1658\" data-start=\"1565\">\n<li data-end=\"1606\" data-section-id=\"yqkxx1\" data-start=\"1565\" style=\"\"><strong data-end=\"1577\" data-start=\"1567\">Reject<\/strong> if required schema is broken<\/li>\n<li data-end=\"1658\" data-section-id=\"ichx8v\" data-start=\"1607\" style=\"\"><strong data-end=\"1623\" data-start=\"1609\">Quarantine<\/strong> if partially usable but suspicious<\/li>\n<\/ul>\n<h3 data-end=\"1720\" data-section-id=\"14m98oy\" data-start=\"1665\" style=\"\">Layer B \u2014 Constraint validation (hard + soft gates)<\/h3>\n<p data-end=\"1755\" data-start=\"1721\" style=\"\">Validate \u201cphysics\/business logic\u201d:<\/p>\n<p data-end=\"1766\" data-start=\"1757\" style=\"\">Examples:<\/p>\n<ul data-end=\"1901\" data-start=\"1767\">\n<li data-end=\"1783\" data-section-id=\"a9lm16\" data-start=\"1767\" style=\"\">age \u2208 [0, 120]<\/li>\n<li data-end=\"1796\" data-section-id=\"mpuog9\" data-start=\"1784\" style=\"\">salary \u2265 0<\/li>\n<li data-end=\"1827\" data-section-id=\"1h4zy6z\" data-start=\"1797\" style=\"\">timestamps not in the future<\/li>\n<li data-end=\"1864\" data-section-id=\"wgykx5\" data-start=\"1828\" style=\"\">latitude\/longitude in valid ranges<\/li>\n<li data-end=\"1901\" data-section-id=\"1apr986\" data-start=\"1865\" style=\"\">IDs follow expected format (regex)<\/li>\n<\/ul>\n<p data-end=\"1910\" data-start=\"1903\" style=\"\">Action:<\/p>\n<ul data-end=\"2017\" data-start=\"1911\">\n<li data-end=\"1950\" data-section-id=\"tgt6pb\" data-start=\"1911\" style=\"\"><strong data-end=\"1926\" data-start=\"1913\">Hard fail<\/strong> for impossible values<\/li>\n<li data-end=\"2017\" data-section-id=\"1iixzx4\" data-start=\"1951\" style=\"\"><strong data-end=\"1966\" data-start=\"1953\">Soft warn<\/strong> for rare but possible values (route to quarantine)<\/li>\n<\/ul>\n<h3 data-end=\"2084\" data-section-id=\"qzugu0\" data-start=\"2024\" style=\"\">Layer C \u2014 Statistical validation (drift + anomaly gates)<\/h3>\n<p data-end=\"2128\" data-start=\"2085\" style=\"\">Validate distribution changes and outliers:<\/p>\n<p data-end=\"2148\" data-start=\"2130\" style=\"\">Streaming metrics:<\/p>\n<ul data-end=\"2356\" data-start=\"2149\">\n<li data-end=\"2177\" data-section-id=\"1903yao\" data-start=\"2149\" style=\"\">missingness rate per field<\/li>\n<li data-end=\"2219\" data-section-id=\"dyra6v\" data-start=\"2178\" style=\"\">mean\/std\/quantiles for numeric features<\/li>\n<li data-end=\"2272\" data-section-id=\"2uymt9\" data-start=\"2220\" style=\"\">category frequency shifts for categorical features<\/li>\n<li data-end=\"2311\" data-section-id=\"1hdoouq\" data-start=\"2273\" style=\"\">duplicate rate \/ near-duplicate rate<\/li>\n<li data-end=\"2356\" data-section-id=\"nxgzmg\" data-start=\"2312\" style=\"\">embedding drift for text\/images (optional)<\/li>\n<\/ul>\n<p data-end=\"2380\" data-start=\"2358\" style=\"\">Common drift measures:<\/p>\n<ul data-end=\"2524\" data-start=\"2381\">\n<li data-end=\"2442\" data-section-id=\"1skx5si\" data-start=\"2381\" style=\"\"><strong data-end=\"2419\" data-start=\"2383\">PSI (Population Stability Index)<\/strong> for distribution shift<\/li>\n<li data-end=\"2478\" data-section-id=\"fs6b95\" data-start=\"2443\" style=\"\"><strong data-end=\"2478\" data-start=\"2445\">KL divergence \/ JS divergence<\/strong><\/li>\n<li data-end=\"2524\" data-section-id=\"gf14o2\" data-start=\"2479\" style=\"\"><strong data-end=\"2505\" data-start=\"2481\">Wasserstein distance<\/strong> (numeric features)<\/li>\n<\/ul>\n<p data-end=\"2533\" data-start=\"2526\" style=\"\">Action:<\/p>\n<ul data-end=\"2637\" data-start=\"2534\">\n<li data-end=\"2585\" data-section-id=\"166fq2a\" data-start=\"2534\" style=\"\"><strong data-end=\"2558\" data-start=\"2536\">Alert + quarantine<\/strong> if shift exceeds threshold<\/li>\n<li data-end=\"2637\" data-section-id=\"gcxsg4\" data-start=\"2586\" style=\"\"><strong data-end=\"2612\" data-start=\"2588\">Route to shadow mode<\/strong> (do not train on it yet)<\/li>\n<\/ul>\n<h3 data-end=\"2701\" data-section-id=\"1k9t32y\" data-start=\"2644\" style=\"\">Layer D \u2014 Label integrity checks (when labels arrive)<\/h3>\n<p data-end=\"2798\" data-start=\"2702\" style=\"\">If your pipeline includes real-time labels (human feedback, weak supervision, delayed outcomes):<\/p>\n<p data-end=\"2807\" data-start=\"2800\" style=\"\">Checks:<\/p>\n<ul data-end=\"2977\" data-start=\"2808\">\n<li data-end=\"2840\" data-section-id=\"1u6tqdc\" data-start=\"2808\" style=\"\">sudden class proportion spikes<\/li>\n<li data-end=\"2894\" data-section-id=\"gwyxwv\" data-start=\"2841\" style=\"\">labeler-level anomalies (accuracy drop vs gold set)<\/li>\n<li data-end=\"2945\" data-section-id=\"229dpf\" data-start=\"2895\" style=\"\">disagreements\/low agreement if multiple labelers<\/li>\n<li data-end=\"2977\" data-section-id=\"d5ata7\" data-start=\"2946\" style=\"\">label changes\/edits frequency<\/li>\n<\/ul>\n<p data-end=\"2986\" data-start=\"2979\" style=\"\">Action:<\/p>\n<ul data-end=\"3056\" data-start=\"2987\">\n<li data-end=\"3013\" data-section-id=\"1a2rwfe\" data-start=\"2987\" style=\"\">quarantine label batches<\/li>\n<li data-end=\"3056\" data-section-id=\"1fnmhd8\" data-start=\"3014\" style=\"\">require review before training ingestion<\/li>\n<\/ul>\n<h2 data-end=\"3131\" data-section-id=\"1p3nkle\" data-start=\"3063\" style=\"\">4) Real-time validation patterns (how to implement operationally)<\/h2>\n<h3 data-end=\"3179\" data-section-id=\"1n4z8rv\" data-start=\"3133\" style=\"\">Pattern 1 \u2014 \u201cAccept \/ Quarantine \/ Reject\u201d<\/h3>\n<p data-end=\"3208\" data-start=\"3180\" style=\"\">Every event gets a decision:<\/p>\n<ul data-end=\"3429\" data-start=\"3210\">\n<li data-end=\"3271\" data-section-id=\"1nuxzvm\" data-start=\"3210\" style=\"\"><strong data-end=\"3223\" data-start=\"3212\">Accept:<\/strong> clean, passes checks \u2192 goes to production store<\/li>\n<li data-end=\"3359\" data-section-id=\"9x718j\" data-start=\"3272\" style=\"\"><strong data-end=\"3289\" data-start=\"3274\">Quarantine:<\/strong> suspicious \u2192 stored separately, excluded from training until reviewed<\/li>\n<li data-end=\"3429\" data-section-id=\"1u8vfwb\" data-start=\"3360\" style=\"\"><strong data-end=\"3373\" data-start=\"3362\">Reject:<\/strong> malformed\/unsafe \u2192 dropped + logged for security review<\/li>\n<\/ul>\n<p data-end=\"3493\" data-start=\"3431\" style=\"\">This prevents one bad stream from contaminating training data.<\/p>\n<h3 data-end=\"3546\" data-section-id=\"1o4re9y\" data-start=\"3500\" style=\"\">Pattern 2 \u2014 Validation as code (versioned)<\/h3>\n<p data-end=\"3578\" data-start=\"3547\" style=\"\">Treat validation rules as code:<\/p>\n<ul data-end=\"3679\" data-start=\"3579\">\n<li data-end=\"3608\" data-section-id=\"1vtq5wl\" data-start=\"3579\" style=\"\">version rules (v1.0, v1.1\u2026)<\/li>\n<li data-end=\"3632\" data-section-id=\"1y23syj\" data-start=\"3609\" style=\"\">review changes via PR<\/li>\n<li data-end=\"3679\" data-section-id=\"1ayg6cl\" data-start=\"3633\" style=\"\">log which rule version validated each record<\/li>\n<\/ul>\n<p data-end=\"3715\" data-start=\"3681\" style=\"\">This supports audits and rollback.<\/p>\n<h3 data-end=\"3757\" data-section-id=\"1dreg5h\" data-start=\"3722\" style=\"\">Pattern 3 \u2014 \u201cShadow validation\u201d<\/h3>\n<p data-end=\"3785\" data-start=\"3758\" style=\"\">For new sources or changes:<\/p>\n<ul data-end=\"3886\" data-start=\"3786\">\n<li data-end=\"3827\" data-section-id=\"1r3wfue\" data-start=\"3786\" style=\"\">allow traffic to flow to a shadow store<\/li>\n<li data-end=\"3886\" data-section-id=\"pa41wt\" data-start=\"3828\" style=\"\">validate and monitor before promoting to training source<\/li>\n<\/ul>\n<p data-end=\"3951\" data-start=\"3888\" style=\"\">This reduces false positives and breaks fewer production flows.<\/p>\n<h2 data-end=\"4002\" data-section-id=\"1p8s41n\" data-start=\"3958\" style=\"\">5) Concrete validation rules by data type<\/h2>\n<h3 data-end=\"4033\" data-section-id=\"12c0t9n\" data-start=\"4004\" style=\"\">A) Tabular \/ transactions<\/h3>\n<ul data-end=\"4227\" data-start=\"4034\">\n<li data-end=\"4056\" data-section-id=\"m1fg67\" data-start=\"4034\" style=\"\">schema + type checks<\/li>\n<li data-end=\"4071\" data-section-id=\"xyoeqq\" data-start=\"4057\" style=\"\">range checks<\/li>\n<li data-end=\"4179\" data-section-id=\"1clr9fu\" data-start=\"4072\" style=\"\">cross-field consistency:\n<ul data-end=\"4179\" data-start=\"4101\">\n<li data-end=\"4131\" data-section-id=\"namvs2\" data-start=\"4101\" style=\"\">currency matches country<\/li>\n<li data-end=\"4179\" data-section-id=\"13735k5\" data-start=\"4134\" style=\"\">transaction_time within acceptable window<\/li>\n<\/ul>\n<\/li>\n<li data-end=\"4227\" data-section-id=\"sgqscf\" data-start=\"4180\" style=\"\">duplicate detection (same ID + time + amount)<\/li>\n<\/ul>\n<h3 data-end=\"4246\" data-section-id=\"xnv8tf\" data-start=\"4229\" style=\"\">B) Text (NLP)<\/h3>\n<ul data-end=\"4507\" data-start=\"4247\">\n<li data-end=\"4279\" data-section-id=\"108okkt\" data-start=\"4247\" style=\"\">length bounds (min\/max tokens)<\/li>\n<li data-end=\"4312\" data-section-id=\"1r0evph\" data-start=\"4280\" style=\"\">encoding normalization (UTF-8)<\/li>\n<li data-end=\"4360\" data-section-id=\"rfb8gc\" data-start=\"4313\" style=\"\">profanity\/unsafe content checks (if relevant)<\/li>\n<li data-end=\"4507\" data-section-id=\"1fua2dv\" data-start=\"4361\" style=\"\">suspicious patterns:\n<ul data-end=\"4507\" data-start=\"4386\">\n<li data-end=\"4411\" data-section-id=\"e6gjuf\" data-start=\"4386\" style=\"\">homoglyph\/unicode abuse<\/li>\n<li data-end=\"4447\" data-section-id=\"18oq05\" data-start=\"4414\" style=\"\">repeated characters\/spam bursts<\/li>\n<li data-end=\"4507\" data-section-id=\"124pso8\" data-start=\"4450\" style=\"\">prompt-injection markers (if model is LLM-based system)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 data-end=\"4522\" data-section-id=\"1ybl720\" data-start=\"4509\" style=\"\">C) Images<\/h3>\n<ul data-end=\"4761\" data-start=\"4523\">\n<li data-end=\"4554\" data-section-id=\"8souvb\" data-start=\"4523\" style=\"\">file type allowlist (png\/jpg)<\/li>\n<li data-end=\"4589\" data-section-id=\"av5iia\" data-start=\"4555\" style=\"\">size bounds (min\/max resolution)<\/li>\n<li data-end=\"4617\" data-section-id=\"iklwcc\" data-start=\"4590\" style=\"\">corrupted image detection<\/li>\n<li data-end=\"4691\" data-section-id=\"197vl8j\" data-start=\"4618\" style=\"\">pixel stats checks (all-black\/all-white, extreme compression artifacts)<\/li>\n<li data-end=\"4761\" data-section-id=\"35qb4v\" data-start=\"4692\" style=\"\">perceptual hash for near-duplicates (useful against replay attacks)<\/li>\n<\/ul>\n<h2 data-end=\"4822\" data-section-id=\"tgbcjg\" data-start=\"4768\" style=\"\">6) Thresholding strategy (how to set limits safely)<\/h2>\n<p data-end=\"4844\" data-start=\"4824\" style=\"\">Use a 3-step method:<\/p>\n<ol data-end=\"5168\" data-start=\"4846\">\n<li data-end=\"4916\" data-section-id=\"14i0wiw\" data-start=\"4846\" style=\"\"><strong data-end=\"4861\" data-start=\"4849\">Baseline<\/strong>: compute stats over known-good data (last 7\u201330 days)<\/li>\n<li data-end=\"5087\" data-section-id=\"61dwo0\" data-start=\"4917\" style=\"\"><strong data-end=\"4934\" data-start=\"4920\">Thresholds<\/strong>: set alert thresholds by risk:\n<ul data-end=\"5087\" data-start=\"4969\">\n<li data-end=\"5016\" data-section-id=\"1mx2vj0\" data-start=\"4969\" style=\"\">hard constraints (impossible values) \u2192 reject<\/li>\n<li data-end=\"5055\" data-section-id=\"1rdkmrl\" data-start=\"5020\" style=\"\">medium drift \u2192 quarantine + alert<\/li>\n<li data-end=\"5087\" data-section-id=\"ln8qgo\" data-start=\"5059\" style=\"\">mild drift \u2192 log + monitor<\/li>\n<\/ul>\n<\/li>\n<li data-end=\"5168\" data-section-id=\"69wv6u\" data-start=\"5088\" style=\"\"><strong data-end=\"5106\" data-start=\"5091\">Review loop<\/strong>: periodically update baselines and thresholds (with approval)<\/li>\n<\/ol>\n<blockquote data-end=\"5269\" data-start=\"5170\" style=\"\">\n<p data-end=\"5269\" data-start=\"5172\" style=\"\">Tip: false positives are costly. Quarantine is usually better than reject for \u201cmaybe-bad\u201d events.<\/p>\n<\/blockquote>\n<h2 data-end=\"5321\" data-section-id=\"1or8bdx\" data-start=\"5276\" style=\"\">7) Implementation examples (code patterns)<\/h2>\n<h3 data-end=\"5397\" data-section-id=\"rzih6j\" data-start=\"5323\" style=\"\">7.1 Simple Python \u201cgate\u201d function (schema + constraints + drift hooks)<\/h3>\n<p>from dataclasses import dataclassfrom typing import Dict, Any, Tuple, Listimport mathimport re@dataclassclass ValidationResult:    decision: str                 # &#8220;ACCEPT&#8221; | &#8220;QUARANTINE&#8221; | &#8220;REJECT&#8221;    reasons: List[str]    rule_version: str = &#8220;v1.0&#8243;def validate_event(event: Dict[str, Any]) -&gt; ValidationResult:    reasons = []    # &#8212;- Schema checks (hard) &#8212;-    required = [&#8220;user_id&#8221;, &#8220;amount&#8221;, &#8220;currency&#8221;, &#8220;timestamp&#8221;]    for f in required:        if f not in event:            return ValidationResult(&#8220;REJECT&#8221;, [f&#8221;Missing required field: {f}&#8221;])    if not isinstance(event[&#8220;user_id&#8221;], str) or len(event[&#8220;user_id&#8221;]) &lt; 3:        return ValidationResult(&#8220;REJECT&#8221;, [&#8220;Invalid user_id type\/length&#8221;])    if not isinstance(event[&#8220;amount&#8221;], (int, float)):        return ValidationResult(&#8220;REJECT&#8221;, [&#8220;amount must be numeric&#8221;])    # &#8212;- Constraint checks (hard\/soft) &#8212;-    amt = float(event[&#8220;amount&#8221;])    if math.isnan(amt) or math.isinf(amt):        return ValidationResult(&#8220;REJECT&#8221;, [&#8220;amount is NaN\/Inf&#8221;])    if amt &lt; 0:        return ValidationResult(&#8220;REJECT&#8221;, [&#8220;amount &lt; 0 is invalid&#8221;])    if amt &gt; 1_000_000:        reasons.append(&#8220;amount unusually high (soft)&#8221;)    cur = event[&#8220;currency&#8221;]    if cur not in {&#8220;USD&#8221;, &#8220;EUR&#8221;, &#8220;INR&#8221;, &#8220;GBP&#8221;}:        return ValidationResult(&#8220;REJECT&#8221;, [f&#8221;Unexpected currency: {cur}&#8221;])    # Timestamp format check (example)    if not re.match(r&#8221;^d{4}-d{2}-d{2}T&#8221;, str(event[&#8220;timestamp&#8221;])):        reasons.append(&#8220;timestamp format unexpected (soft)&#8221;)    # &#8212;- Drift\/anomaly hooks (soft) &#8212;-    # Here you&#8217;d call monitoring functions (PSI, missingness spikes, etc.)    # If anomaly score &gt; threshold -&gt; quarantine.    if reasons:        return ValidationResult(&#8220;QUARANTINE&#8221;, reasons)    return ValidationResult(&#8220;ACCEPT&#8221;, [])<\/p>\n<h3 data-end=\"7273\" data-section-id=\"yalxvd\" data-start=\"7224\" style=\"\">7.2 Streaming drift check idea (PSI skeleton)<\/h3>\n<p data-end=\"7391\" data-start=\"7275\" style=\"\">PSI compares current distribution to baseline bins. In real-time you compute PSI per window (e.g., last 10k events).<\/p>\n<p>import numpy as npdef psi(expected_counts, actual_counts, eps=1e-6):    expected = np.array(expected_counts, dtype=float) + eps    actual = np.array(actual_counts, dtype=float) + eps    expected = expected \/ expected.sum()    actual = actual \/ actual.sum()    return float(np.sum((actual &#8211; expected) * np.log(actual \/ expected)))<\/p>\n<p data-end=\"7774\" data-start=\"7745\" style=\"\"><strong data-end=\"7774\" data-start=\"7745\">Operational rule example:<\/strong><\/p>\n<ul data-end=\"7882\" data-start=\"7775\">\n<li data-end=\"7797\" data-section-id=\"161lf0w\" data-start=\"7775\" style=\"\">PSI &lt; 0.1 \u2192 normal<\/li>\n<li data-end=\"7837\" data-section-id=\"1xdsota\" data-start=\"7798\" style=\"\">0.1\u20130.25 \u2192 moderate drift (monitor)<\/li>\n<li data-end=\"7882\" data-section-id=\"l3ob6c\" data-start=\"7838\" style=\"\">\n<blockquote data-end=\"7882\" data-start=\"7840\" style=\"\">\n<p data-end=\"7882\" data-start=\"7842\" style=\"\">0.25 \u2192 high drift (quarantine + alert)<\/p>\n<\/blockquote>\n<\/li>\n<\/ul>\n<p data-end=\"7943\" data-start=\"7884\" style=\"\">(Exact thresholds depend on domain; tune them empirically.)<\/p>\n<h2 data-end=\"7997\" data-section-id=\"mfpfek\" data-start=\"7950\" style=\"\">8) What to log for audits and investigations<\/h2>\n<p data-end=\"8037\" data-start=\"7999\" style=\"\">For each event (or batch window), log:<\/p>\n<p 8075\"=\"\" data-section-id=\"14fsa7s\" data-start=\"8038\" \"=\"\" style=\"\">\n<ul>\n<li data-end=\"8075\" data-start=\"8040\" style=\"\">decision (accept\/quarantine\/reject)<\/li>\n<li data-end=\"8123\" data-start=\"8078\" style=\"\">reason codes (structured, not free text only)<\/li>\n<li data-end=\"8143\" data-start=\"8126\" style=\"\">rule version used<\/li>\n<li data-end=\"8191\" data-start=\"8146\" style=\"\">source identity (service account, dataset ID)<\/li>\n<li data-end=\"8220\" data-start=\"8194\" style=\"\">time window + partition ID<\/li>\n<li data-end=\"8264\" data-start=\"8223\" style=\"\">summary stats (missingness, drift scores)<\/li>\n<\/ul>\n<p style=\"\">\n<p data-end=\"8279\" data-start=\"8266\" style=\"\">This enables:<\/p>\n<ul data-end=\"8363\" data-start=\"8280\">\n<li data-end=\"8301\" data-section-id=\"19p85r1\" data-start=\"8280\" style=\"\">root cause analysis<\/li>\n<li data-end=\"8312\" data-section-id=\"1hs1dy6\" data-start=\"8302\" style=\"\">rollback<\/li>\n<li data-end=\"8336\" data-section-id=\"jkgn0o\" data-start=\"8313\" style=\"\">targeted data cleanup<\/li>\n<li data-end=\"8363\" data-section-id=\"17hgb3f\" data-start=\"8337\" style=\"\">training dataset hygiene<\/li>\n<\/ul>\n","protected":false},"template":"","class_list":["post-97806","tva_lesson","type-tva_lesson","status-publish","hentry","tva_courses-adversarial-ml-security-threats","post-wrapper","thrv_wrapper"],"acf":[],"_links":{"self":[{"href":"https:\/\/staging.nanoschool.in\/wp\/wp-json\/wp\/v2\/tva_lesson\/97806","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/staging.nanoschool.in\/wp\/wp-json\/wp\/v2\/tva_lesson"}],"about":[{"href":"https:\/\/staging.nanoschool.in\/wp\/wp-json\/wp\/v2\/types\/tva_lesson"}],"wp:attachment":[{"href":"https:\/\/staging.nanoschool.in\/wp\/wp-json\/wp\/v2\/media?parent=97806"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}