The500Feed.Live

Everything going on in AI - updated daily from 500+ sources

← Back to The 500 Feed
Score: 10🌐 NewsAugust 22, 2026

The Anomaly Detector That Learns by Counting

10,000 Bayesian models, no training loop: how conjugate priors caught red-team activity in a billion-event authentication log Bayesian statistics gives you a principled way to combine prior knowledge with observed data. The catch is computational: updating beliefs usually means numerical integration, MCMC sampling, or an optimization loop you have to babysit. There is a family of cases where none of that is necessary. Choose the prior from the right mathematical family — a conjugate prior — and the belief update collapses to arithmetic. No gradients, no convergence checks, no retraining. This article works through the theory and then puts it to a real test: anomaly detection on the Los Alamos National Laboratory (LANL) cybersecurity dataset, which contains over one billion authentication events (1.6 billion events across all its sources). We train on a 29.4-million-event subset and build 10,413 independent per-computer models — each one updated by incrementing two integers. The Foundation: Bayesian Inference Every Bayesian analysis has three components. The prior P(θ) captures what you believe about the parameters before seeing data. The likelihood P(data | θ) says how probable the observed data is under given parameter values. The posterior P(θ | data) is your updated belief after observing the data. Bayes’ theorem connects them: P(θ | data) = P(data | θ) · P(θ) / P(data) The denominator is where the trouble starts. The marginal likelihood P(data) = ∫ P(data | θ′) · P(θ′) dθ′ rarely has a closed-form solution, which is why practitioners reach for MCMC sampling, variational approximation, or numerical integration. All three work, and all three bring approximation error, convergence monitoring, and computational overhead. What Conjugacy Buys You A prior P(θ) is conjugate to a likelihood P(data | θ) if the posterior P(θ | data) belongs to the same distributional family as the prior. When that holds, the intractable integral never has to be computed — Bayes’ theorem reduces to a parameter update you can write on one line. The Dirichlet–Categorical Conjugate Pair For categorical data — authentication types, user identities, event classes — the natural conjugate pair is Dirichlet–Categorical. Likelihood (Categorical): P(xᵢ = k | θ) = θₖ, where θ = (θ₁, …, θ_K) and θ₁ + … + θ_K = 1. Prior (Dirichlet): up to a normalizing constant, Dir(α) ∝ θ₁^(α₁−1) · θ₂^(α₂−1) · … · θ_K^(α_K−1) Posterior: also Dirichlet. If the data contains n₁ observations of category 1, n₂ of category 2, and so on: θ | data ~ Dir(α₁ + n₁, α₂ + n₂, …, α_K + n_K) Add the observed counts to the prior parameters. That is the entire update. It translates directly into code — two increments per event: def update(self, observations): for obs in observations: self.counts[obs] += 1 # n_k += 1 for the observed category self.total += 1 # N += 1 -- total event count No matrix operations, no learning rate, no batch size. Each authentication event increments two integers. Posterior predictive. For a new observation, the probability of category k is P(next = k | data) = (α + nₖ) / (K·α + N) where K is the number of categories, N is the total number of observations, and α is the symmetric prior pseudo-count used throughout this article. Understanding the Prior Parameter α The parameter α controls how strongly you believe categories are equally likely before seeing any data. With α = 1 (a uniform prior), every category starts with one pseudo-observation and no category is favored. A larger α, say 10, takes more data to pull beliefs away from uniform — useful when you expect balance. An α below 1 encodes sparsity: most categories should be rare. The choice matters most for categories the model has never seen. For a computer with N = 1,000 events across 4 auth types, the probability assigned to an unseen type is P(unseen) = α / ((K+1)·α + N): alpha P(unseen) Score Effect 0.1 9.99 x 10-5 9.21 Very sensitive to novelty 1.0 9.95 x 10-4 6.91 Balanced 10.0 9.52 x 10-3 4.65 Conservative, harder to flag With large training data (N = 29.4M at the global level), different α values produce nearly identical scores — the prior washes out. At the per-computer level (N = 100–10,000 events), α meaningfully controls sensitivity. That is why α = 1 is a sensible default: it regularizes the small per-computer models without distorting the global picture. Case Study: Enterprise Authentication Anomaly Detection The Dataset The LANL Comprehensive Multi-Source Cyber-Security Events dataset [6] records 58 days of activity from Los Alamos National Laboratory’s internal network: 1.6 billion events in total across its sources, of which the authentication log (auth.txt) contains just over one billion events covering 12,425 users and 17,684 computers. A red team exercise ran during the collection window, and its 749 attack events are documented in a separate ground-truth file. All user and computer identifiers are anonymized by the lab. The dataset is released by LANL for public use under a CC0 license (approved for public release, LA-UR-15–23810), which permits commercial use. Each authentication event contains: timestamp, source_user, dest_user, source_computer, dest_computer, auth_type, logon_type, auth_orientation, success_status The Modeling Approach We model two categorical distributions per computer, each with its own Dirichlet–Categorical model. Model 1 covers the authentication type : which protocols (Kerberos, NTLM, …) does this machine normally see? Model 2 covers the source user : which users normally authenticate to this machine? These two features carry complementary signals. Machines specialize: domain controllers speak almost pure Kerberos, legacy servers lean on NTLM, workstations show mixed local-system authentications. And machines have social circles: a personal workstation is dominated by its owner, a server by its administrator group. An attacker moving laterally tends to violate both patterns at once — an unusual protocol from an unusual user. We use α = 1 for all models: no domain bias, and no zero probabilities for categories a computer has never seen. Implementation and Evaluation Strategy Temporal split. The model trains only on data before the first red team attack and is evaluated during and after the attack period. Never training on future data is what makes the evaluation resemble real deployment. Labels. Exact timestamp matching located only 3 of the 749 red team events in the authentication log — most attacks touched machines outside it. We therefore label any access to a compromised computer during the attack window as suspicious, which yields 1,247 suspicious-window events, enough signal for a reliable evaluation. Scoring. Each test event receives auth_score = −log P(auth_type | computer history) user_score = −log P(source_user | computer history) combined_score = (auth_score + user_score) / 2 Higher score means more surprising, means more anomalous. The scoring function maps directly to code; note how the K → K+1 adjustment for unseen categories (explained in the worked example below) appears as a single conditional: def anomaly_score(self, category): n_k = self.counts.get(category, 0) # 0 if never seen K = len(self.counts) if category not in self.counts: K += 1 # unseen: K -> K+1 alpha_0 = K * self.alpha_prior + self.total # denominator prob = (self.alpha_prior + n_k) / alpha_0 return -np.log(prob) The full implementation, including per-computer models and a global fallback, is in the companion notebook on GitHub. A Worked Example: Scoring One Authentication Event Before looking at results at scale, let’s trace exactly what the algorithm computes for a single event. The setting is real: destination computer C457 and source computer C663 appear in authentication records discussed by Heard and Rubin-Delanchy [5], whose study of this same network identified C17693 as one of four confirmed red-team source machines (ranked 5th most anomalous out of 16,230). The training counts below are illustrative — round numbers chosen so the arithmetic is easy to follow — but the record structure, the machines, and the scoring formula are exactly those used in the full experiment. Notation for this section: α is the symmetric prior pseudo-count (= 1 throughout), nₖ is the training count of category k, K is the number of distinct categories seen in training, N is the total training count, and the posterior predictive probability of category k is [4]: P(k | data) = (α + nₖ) / (K·α + N) What the model knows about C457 (illustrative: 5,000 training events, K = 3 auth types, K = 3 users, so the denominator is 3×1 + 5,000 = 5,003): Auth Type n_k P(k|C457) Score = -log P Kerberos 4,100 0.820 0.20 ? (Unknown) 750 0.150 1.90 NTLM 150 0.030 3.50 Source User n_k P(u|C457) Score = -log P U31@DOM1 3,000 0.600 0.51 U45@DOM1 1,250 0.250 1.39 U58@DOM1 750 0.150 1.90 Scenario 1 — Normal Event This record structure appears in the LANL authentication log [5]: timestamp=3, source_user=U31@DOM1, source_computer=C663, dest_computer=C457, auth_type=Kerberos U31@DOM1 is C457’s dominant user; Kerberos is its dominant protocol. The figure traces each value from the training table (left) into its slot in the formula (right), with colors matched in the legend. Annotated walkthrough showing how the normal event’s training counts flow into the posterior predictive formula, producing a combined score of 0.35. Source: Image by the author. Combined score 0.35 — well within normal range. Scenario 2 — Suspicious Event (Confirmed Red-Team Machine C17693) source_user=U842@DOM1 (never seen on C457), source_computer=C17693, dest_computer=C457, auth_type=NTLM NTLM is known but rare on C457 (nₖ = 150). The user is the interesting part: U842@DOM1 never appeared on C457 during training, which triggers the K → K+1 rule. The denominator grows by one α unit to give the new category its share of prior mass, so the probability is small but never zero. Annotated walkthrough of the suspicious event showing the unseen-user adjustment and a combined score of 6.01 Source: Image by the author. Combined score 6.01–17× higher than the normal event. The same arithmetic runs across all 10,413 computer models simultaneously. Results What the Algorithm Learned The global authentication distribution over 29.4M training events (6 normalized auth types): Bar chart of the global authentication type distribution across 29.4 million training events . Source: Image by the author. Auth Type Events % Total Score (global) ? (Unknown system auth) 17,004,222 57.8% 0.55 Kerberos 10,367,997 35.2% 1.04 NTLM 1,431,374 4.9% 3.02 Negotiate 604,153 2.1% 3.89 MSAUTHPKG ~16,239 0.1% 7.82 Wave 6 0.0% 15.25 The “?” category represents local system authentications where the protocol type was not logged — a common artifact in enterprise Windows environments. The model learns this is the norm and scores it accordingly. Each machine also develops its own authentication fingerprint: Chart showing per-computer authentication type profiles for five representative machines . Source: Image by the author. C586 (3.6M events): 49.9% Unknown system auth -> high-traffic domain resource C625 (1.97M events): 56.2% Unknown system auth -> active infrastructure node C988 (269K events): 48.0% Unknown system auth -> mid-tier server C1020 (156 events): 74.4% Unknown system auth -> isolated/edge system C1069 (149 events): 74.5% Unknown system auth -> isolated/edge system Deviations from these per-computer norms are what drive anomaly scores up. Performance ROC curve for the Dirichlet-Categorical anomaly detector showing AUC of 0.826 . Source: Image by the author The detector reached an AUC-ROC of 0.826 on the temporal test split, training on 29.4 million events and building 10,413 computer models in a single pass, with 1,247 suspicious-window events identified for evaluation. Overlapping histograms of anomaly scores for attack versus normal events, showing clear separation with means of 4.24 and 2.12 . Source: Image by the author. The score separation is clear: attack events average 4.24 versus 2.12 for normal events. The Effect of α, Confirmed on Real Data Two-panel chart showing anomaly scores converging across alpha values as observations accumulate, and unseen-category scores as training data grows . Source: Image by the author The left panel shows anomaly scores falling as a category is observed more often — the model learning the normal pattern. All α values converge as observations accumulate. The right panel tracks the score of a completely unseen auth type as training data grows: at LANL scale (N = 29.4M) every α choice produces nearly identical results. The prior only matters when data is sparse — which is exactly when you need it, in the small per-computer models for edge systems. When to Use This Approach Reach for Dirichlet–Categorical conjugate priors when your problem has categorical inputs (authentication types, user identities, protocol classes), streaming updates with no retraining budget, many per-entity models to maintain at once, sparse data with unseen categories, and a hard interpretability requirement — every score here has a direct meaning, such as “this event type appeared 3 times in 5,000 observations.” Look elsewhere when you have high-dimensional continuous features (Gaussian processes, kernel methods, neural networks), complex non-linear temporal dependencies (LSTMs, Transformers), or abundant labelled data where supervised models can learn richer representations. Implementation Details The companion notebook contains the full DirichletCategorical class, the EnterpriseAuthDetector orchestrator, data loading, and every plot shown here. It runs on standard Colab hardware in about 15 minutes. You can find the code in the GitHub repository and the dataset on the LANL cybersecurity data page . Conclusion Conjugate priors turn a hard computational problem into bookkeeping. Because the Dirichlet posterior has the same form as the prior, 29.4 million training events reduce to counting, 10,413 per-computer behavioral models come essentially for free, and every anomaly score can be traced by hand — as the C457 walkthrough showed. The resulting detector reached an AUC-ROC of 0.826 on highly imbalanced data with a single hyperparameter left at its default. This is not a replacement for gradient-based models, neural networks, or ensembles — those remain the right tools for many problems. The point is narrower and, I think, more useful: when the problem structure matches the model assumptions — categorical data, streaming updates, a need for interpretability — the conjugate prior approach is analytically exact, transparent, and fast enough to be boring. Seeing why each quantity in the formula is what it is, on a concrete operational problem, is the kind of understanding that transfers well beyond this particular use case. References [1] A. Gelman, J. Carlin, H. Stern, D. Dunson, A. Vehtari and D. Rubin, Bayesian Data Analysis, 3rd Edition (2013), Chapman & Hall/CRC [2] K. Murphy, Machine Learning: A Probabilistic Perspective (2012), MIT Press [3] C. Bishop, Pattern Recognition and Machine Learning (2006), Springer [4] S. Tu, The Dirichlet-Multinomial and Dirichlet-Categorical Models for Bayesian Inference (2019), technical writeup [5] N. Heard and P. Rubin-Delanchy, Network-wide anomaly detection via the Dirichlet process (2016), IEEE Conference on Intelligence and Security Informatics (ISI) [6] A. D. Kent, Comprehensive, Multi-Source Cyber-Security Events (2015), Los Alamos National Laboratory — released for public use under a CC0 license (LA-UR-15–23810) All results produced on the unmodified LANL dataset; illustrative values in the worked example are labeled as such. All images by the author. The Anomaly Detector That Learns by Counting was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.

Read Original Article →

Source

https://pub.towardsai.net/the-anomaly-detector-that-learns-by-counting-ad4c88703528?source=rss----98111c9905da---4