fa
Feedback
Data eXplore : Data Science, ML, Big Data, LLMs and AI Security

Data eXplore : Data Science, ML, Big Data, LLMs and AI Security

رفتن به کانال در Telegram

Exploring Data Science, Big Data Analytics & Visualization, ML/DL, Neural Networks, LLMs with GitHub, Kaggle, HuggingFace and some white papers by big institutions. Not just data, but science behind data Paid project? premodi@zohomail.in@DataML

نمایش بیشتر
کشور مشخص نشده استفناوری و برنامه‌ها45 658
581
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+37 روز
+1930 روز

در حال بارگیری داده...

جذب مشترکین
اوت '26
اوت '26
+23
در 0 کانال‌ها
ژوئیه '26
+23
در 0 کانال‌ها
Get PRO
ژوئن '26
+26
در 1 کانال‌ها
Get PRO
مه '26
+54
در 1 کانال‌ها
Get PRO
آوریل '26
+14
در 2 کانال‌ها
Get PRO
مارس '26
+53
در 10 کانال‌ها
Get PRO
فوریه '26
+22
در 2 کانال‌ها
Get PRO
ژانویه '26
+28
در 2 کانال‌ها
Get PRO
دسامبر '25
+46
در 4 کانال‌ها
Get PRO
نوامبر '25
+128
در 4 کانال‌ها
Get PRO
اکتبر '25
+149
در 3 کانال‌ها
Get PRO
سپتامبر '250
در 2 کانال‌ها
Get PRO
اوت '25
+99
در 0 کانال‌ها
Get PRO
ژوئیه '250
در 0 کانال‌ها
Get PRO
ژوئن '250
در 0 کانال‌ها
Get PRO
مه '250
در 0 کانال‌ها
Get PRO
آوریل '250
در 1 کانال‌ها
Get PRO
مارس '250
در 0 کانال‌ها
Get PRO
فوریه '250
در 0 کانال‌ها
Get PRO
ژانویه '25
+14
در 2 کانال‌ها
Get PRO
دسامبر '24
+11
در 0 کانال‌ها
تاریخ
رشد مشترکین
اشارات
کانال‌ها
27 اوت+1
26 اوت0
25 اوت+1
24 اوت+1
23 اوت0
22 اوت+1
21 اوت+1
20 اوت+1
19 اوت0
18 اوت+1
17 اوت0
16 اوت0
15 اوت+1
14 اوت0
13 اوت+1
12 اوت0
11 اوت+2
10 اوت0
09 اوت+3
08 اوت+2
07 اوت+1
06 اوت+2
05 اوت0
04 اوت+1
03 اوت+1
02 اوت+1
01 اوت+1
پست‌های کانال
2
Deep systemic analysis of AI constraints from context to internal weight editing. 📂 PDF #AIRedTeaming #AISafety #LLMs #AIJai+1
Deep systemic analysis of AI constraints from context to internal weight editing. 📂 PDF #AIRedTeaming #AISafety #LLMs #AIJailbreak #Pliny
89
3
Adaptive Gradient Thresholding Why Fixed Gradient Clipping Kills Deep RecSys When Feedback Drifts? When the distribution of u
Adaptive Gradient Thresholding Why Fixed Gradient Clipping Kills Deep RecSys When Feedback Drifts? When the distribution of user feedback changes drastically: a viral post, a failure in the logging pipeline, or a seasonal spike. deep recommendation models experience anomalous gradients. Standard gradient clipping with a threshold of 1.0 either truncates all gradients, slowing down convergence, or allows outliers to pass through, causing the loss to skyrocket. The problem is that the threshold is fixed for all parameters and doesn't adapt to the current statistics. ➡️ How Adaptive Gradient Thresholding Works? The idea is to maintain a running average and standard deviation of the gradient norm for each parameter (or layer). The clipping threshold is calculated as the mean plus k times the standard deviation. If the gradient norm exceeds the threshold, it is clipped to that threshold. This prevents normal gradients from being truncated, while isolating anomalies. Example in PyTorch: class AdaptiveGradientClipping: def __init__(self, model, k=4.0, alpha=0.99): self.k = k self.alpha = alpha self.running_mean = {} self.running_std = {} def step(self): for name, param in model.named_parameters(): if param.grad is None: continue g_norm = param.grad.norm().item() if name not in self.running_mean: self.running_mean[name] = g_norm self.running_std[name] = g_norm continue self.running_mean[name] = self.alpha self.running_mean[name] + (1 - self.alpha) g_norm self.running_std[name] = self.alpha self.running_std[name] + (1 - self.alpha) abs(g_norm - self.running_mean[name]) threshold = self.running_mean[name] + self.k * self.running_std[name] if g_norm > threshold: param.grad.mul_(threshold / (g_norm + 1e-8)) ➡️ Why this is Crucial for RecSys? Sudden changes in feedback: a viral post, for example can cause abnormally large gradients for features related to that event. Adaptive trimming isolates these spikes without slowing down training on the rest of the data. In practice, this reduces the variance of the loss by 30-50% during sharp CTR spikes compared to fixed clipping. Convergence is accelerated by 1.2-1.5 times. ➡️ Engineering Trade-offs & a Common Mistake The hyperparameter k represents a balance. A small value (k=2) can truncate important gradients that might carry signals about rare but significant events. A large value (k=6+) can allow outliers to pass through. I recommend starting with k=4 and monitoring the quantiles of the gradient norm in the logs. Alpha represents the adaptation speed. If the data changes rapidly (e.g., hourly cycles), set it to 0.9. If the data is stable (e.g., daily training), set it to 0.999. Don't tune this globally on the validation set; instead, check it on reproducible subsets with drift. A common mistake is to apply a single threshold for the embedding layer and the MLP. The gradient norms in the embedding layers are typically an order of magnitude higher due to sparse features. It's better to calculate statistics separately for each layer or parameter. ➡️ So what's the Conclusion? Adaptive gradient thresholding is a simple engineering technique that stabilizes training when feedback drifts by using an adaptive threshold, reducing loss variance and accelerating convergence without expensive retraining. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
145
4
Attribution Maps for Real-time Boosting How to Avoid Latency? Interpretability in production is great, but it becomes problem
Attribution Maps for Real-time Boosting How to Avoid Latency? Interpretability in production is great, but it becomes problematic when you calculate SHAP values for a batch of thousands of objects and experience latency of tens of milliseconds. With 10,000+ requests per second (RPS) and a time budget of less than 10 milliseconds, batch SHAP or LIME simply won't work. The main mistake is trying to calculate full attributions for every request without considering the trade-offs between accuracy and speed. ➡️ Three Approaches to Online Attribution The first approach is TreeSHAP. It's the most accurate, but has a complexity of O(T*D*L). For CatBoost with 500 trees and a depth of 8, this already results in 100-200 microseconds per object. You can cache path-dependent gradients, but it's still computationally expensive. The second approach is Fast SHAP approximation using expected gradients or Gradient SHAP. It works in O(T*D) – an order of magnitude faster. You lose some accuracy, but for most production tasks, the difference is not significant. The third approach is surrogate LIME. You build a linear model on the fly around the request, using a sample of 100-200 objects. The time complexity is O(k*T*D), and it can be parallelized across rows. ➡️ How to Control Latency? The most reliable method is adaptive timeout: class OnlineAttributor: def __init__(self, model, latency_budget_ms=5): self.model = model self.budget = latency_budget_ms / 1000 async def get_attribution(self, features): shap_values = await asyncio.to_thread( self._tree_shap, features, timeout=self.budget ) if shap_values is None: shap_values = await asyncio.to_thread( self._global_importance, features ) return shap_values It's also helpful to: * Batching: Group requests into batches of 10-50 and calculate SHAP values vectorially. The latency per item decreases significantly. * Precomputed SHAP for streaming requests: Perform offline attribution every 10 minutes and cache the results. If the features don't drift drastically, this is often sufficient. ➡️ Typical Mistakes & Trade-offs Linear models like LIME don't work well with the non-linearities of boosting. For CatBoost or LightGBM, it's better to use the built-in TreeSHAP through predict with pred_contrib=True – it's cheaper and more accurate. Another common mistake is not considering the distribution of latency. With high throughput, the average latency might be 1 millisecond, but the 95th percentile could be 50 milliseconds due to complex objects. You need to set a timeout for the 99th percentile and fall back to global importance if the timeout is exceeded. In my experience, a combination of Fast TreeSHAP with pruning, adaptive timeout, and fallback to global importance provides a typical time of less than 2 milliseconds per object with 100 trees. Without this, interpretation in production becomes a bottleneck. ➡️ What is the Conclusion? For online attribution in gradient boosting with high-throughput inference, use Fast TreeSHAP with adaptive timeout and caching, rather than full SHAP for every request. This provides a balance between accuracy and latency. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
130
5
Feature Aliasing in Real-time Pipelines How to Identify Feature Synonyms Using Graphs When You Don't Have Time for Batch Join
Feature Aliasing in Real-time Pipelines How to Identify Feature Synonyms Using Graphs When You Don't Have Time for Batch Joins? When the same feature arrives under a dozen different names in a real-time stream, the model either sees sparse features or learns noise. In production, this manifests when scaling, where rule-based mapping using if-else statements turns into a support nightmare, and the synonym dictionary grows faster than the infrastructure. PROBLEM: Synonyms Proliferate, Latency is Unforgiving user_id, userId, user.id, in a batch process, these would be merged with a single join. In a real-time pipeline with millions of events per second, such luxury is unavailable. A rule-based approach with configurations works until the first expansion, and then each new feature name requires code modifications and redeployment. The most common mistake is trying to maintain the synonym dictionary manually or using regular expressions, which breaks down at the first non-standard pattern. SOLUTION: Graph-based Deduplication with Union-Find The approach that works in real-world production: a synonym graph is built, where nodes are feature names or their values, and edges represent semantic or statistical relationships. Then, using Union-Find (or Connected Components), canonical groups are identified. For a prototype, NetworkX is suitable, but for production, incremental updates are necessary. ➡️ Example code for core logic: class UnionFind: def init(self): self.parent = {} def find(self, x): if self.parent.setdefault(x, x) != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): px, py = self.find(x), self.find(y) self.parent[px] = py This code is the foundation. In a real-time pipeline, the graph must be dynamically updated: new aliases are identified using HLL sketches or LSH, and the graph itself resides in Redis or RocksDB. If the latency is less than 10ms, pre-compute and load it into memory at the start of the stream. ➡️ PRODUCTION SCENARIO: Kafka Streams and Graph Reconstruction In practice, this looks like this: at the Kafka Streams stage, each new feature is passed through a lookup table of relationships, where Union-Find returns the canonical name. The graph is rebuilt every hour based on fresh logs, which allows for the consideration of new synonyms without stopping the stream. A typical gain is a 30-50% reduction in feature cardinality, which directly reduces the model's dimensionality and inference latency. ➡️ Trade-offs & Caveats This approach is particularly effective in multi-tenant systems where datasets from different teams have different naming conventions, in A/B tests where columns are renamed on the fly, or when feature engineering is done manually without CI/CD. However, there is a key trade-off: speed versus accuracy. If you try to identify every synonym, the graph grows, and the latency increases. If you do it less frequently, some aliases remain, and the model sees noise again. The mistake is trying to find all synonyms at once. It's better to start with Union-Find, then perform incremental clustering, gradually expanding to HLL sketches for rare patterns. ➡️ Conclusion: Graph-based deduplication with Union-Find and incremental updates in Redis is an engineering balance between flexibility and latency that solves the feature aliasing problem without tons of messy code in real-time pipelines. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
106
6
Feature-wise Gradient Noise Injection vs. Concept Drift Concept drift occurs when a model suddenly loses accuracy because the
Feature-wise Gradient Noise Injection vs. Concept Drift Concept drift occurs when a model suddenly loses accuracy because the data has changed. In online pipelines, this is a major problem. The model simply overfits to the current distribution, and when there's a shift, the metrics plummet. Retraining? It's too slow. Drift detection? It's not immediate, and relabeling is also required. There's a more elegant solution: Feature-wise Gradient Noise Injection. ➡️ Essence of the Method In short: we add noise to the gradients, but not randomly, but for each feature separately, taking into account its variance in the batch. This prevents the model from learning fragile patterns that are typical of drift. Features with high variance, those that drift most often and receive more noise, reducing their influence on weight updates. The model learns to generalize, rather than memorize random correlations. ➡️ Why It Works? The noise adapts to the current distribution, if the variance changes during drift, the noise automatically adjusts. And no separate detector is needed; the regularization is built directly into the training process. In a mini-batch, we calculate the variance of each feature σ²_j. Then, we add noise N(0, λ·σ²_j) to the gradient for that feature. λ is a hyperparameter. ➡️ Example in PyTorch import torch def add_feature_wise_noise(grad, features, lambda_noise=0.01): var = features.var(dim=0, unbiased=True) noise = torch.randn_like(grad) (lambda_noise var.sqrt()) return grad + noise for x_batch, y_batch in dataloader: pred = model(x_batch) loss = criterion(pred, y_batch) loss.backward() for param in model.parameters(): if param.grad is not None: param.grad = add_feature_wise_noise(param.grad, x_batch) optimizer.step() optimizer.zero_grad() ➡️ Practical Tips and Warnings - λ is a key parameter. Too small: no effect. Too large: the model will stop converging. I usually start with 10⁻³ and tune it based on validation on historical drifts. A common mistake is not tuning λ for the specific data. - FGNI does not eliminate drift monitoring, but it noticeably increases robustness in the intervals between detections. It does not replace metric tracking, but complements it, providing an additional layer of reliability. - The method works best on tabular data and MLPs. For RNNs or transformers, you'll need to modify it, for example, adding noise to the hidden state. Directly applying it to the gradients of the parameters in these architectures can be unstable. ➡️ What is the conclusion? Feature-wise gradient noise injection is a simple and computationally inexpensive way to make online pipelines more resilient to drift by using an adaptive regularizer directly in gradient descent. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
109
7
Batch-level caching for embeddings How to avoid recalculating the same things in real-time? In production recommendation syst
Batch-level caching for embeddings How to avoid recalculating the same things in real-time? In production recommendation systems and NLP services, generating embeddings is often a bottleneck. When a single user request requires inferring embeddings for dozens of candidates, each request requires a vector representation of each object. Latency increases, and specialists often overlook a simple optimization trick: caching within a batch, rather than globally. ➡️ Why batch-level caching? Requests to the inference service arrive in batches. If you calculate the embeddings for each candidate from scratch, the number of calls to the model increases linearly with the number of requests and objects in each batch. However, many objects are repeated between requests: top-recommended products, popular texts, frequent entities. Batch-level caching solves this by deduplicating IDs within a single batch, minimizing redundant calculations. ➡️ How it works? You collect all object IDs from all requests in the batch, deduplicate them, calculate embeddings only for the unique IDs, and then distribute the results through a mapping. Here's an example in Python: def process_batch(batch_requests): all_unique_ids = {} for req in batch_requests: for item_id in req['item_ids']: all_unique_ids[item_id] = True unique_ids_list = list(all_unique_ids.keys()) embeddings_map = compute_embeddings(unique_ids_list) results = [] for req in batch_requests: req_embeddings = [embeddings_map[i] for i in req['item_ids']] results.append(compute_scores(req['user_vec'], req_embeddings)) return results ➡️ Production example: Suppose you have a service for scoring ads in real-time: 100 requests in a batch, each with 50 items, but only 200 unique items. Without caching, there are 5000 calls to the embedding model; with batch-level caching, there are 200. That's a 25-fold reduction. For a latency-critical pipeline, this is the difference between an SLA violation and stable operation. Practical advice and trade-offs Add a second-level LRU cache for hot items with a TTL of 1 minute. Batch-level caching is the first filter, eliminating duplicates within the batch, while a global cache catches reuse between batches. But don't forget: synchronization is required within the pipeline, you need to collect all IDs before calculations. This can become a bottleneck for batch sizes greater than 1000 or with asynchronous processing. A common mistake is to confuse batch-level caching with a global TTL cache and miss duplicates within a single batch. Conclusion: Batch-level caching is the simplest way to reduce duplicate load on embedding generation in real-time, reducing latency by orders of magnitude without significant overhead in terms of memory or infrastructure. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
117
8
Feature-wise Quantization-Aware Training for Production Inference How to Preserve Metrics When Deploying 4-bit Models on GPUs
Feature-wise Quantization-Aware Training for Production Inference How to Preserve Metrics When Deploying 4-bit Models on GPUs with Limited Memory? Quantizing to INT4 in production reduces memory usage, but often negatively impacts metrics on sensitive features. Standard QAT averages the scaling factor across the entire tensor, which can obscure rare but critical features. Feature-wise QAT addresses this by applying quantization at the level of individual features. ➡️ Why Standard QAT Can Hurt Metrics? Standard quantization trains a single scaling factor for the entire tensor. In production models, especially in recommendation systems or NLP, some features have high variance or are unevenly distributed. Examples include embeddings of rare entities or time series with outliers. A single scaling factor averages out these outliers, and the model loses important nuances, resulting in a 2-5% decrease in performance on classification and regression tasks. ➡️ How Feature-wise QAT Works? Instead of a single scaling factor, you learn separate parameters for each feature: a scaling factor and a zero-point. During fine-tuning, the model adjusts each channel to compensate for the distortions introduced by the 4-bit representation. Here's pseudocode for a custom layer: class QuantLayer(nn.Module): def __init__(self, num_features, bits=4): super().__init__() self.scales = nn.Parameter(torch.ones(num_features)) self.zero_points = nn.Parameter(torch.zeros(num_features)) self.max_val = 2**(bits-1) - 1 def forward(self, x): x_scaled = x / self.scales x_quant = torch.clamp(torch.round(x_scaled), -self.max_val, self.max_val) return x_quant * self.scales ⁠☞ Practical tip: Use learnable parameters initialized from a pre-calibration step on a representative dataset. This speeds up convergence and reduces the risk of overfitting. ➡️ Production Metrics and Trade-offs: For BERT-like models, FWQAT provides a 2-3% increase in F1 score compared to standard QAT. ResNet-50 loses only 0.5% compared to FP32, while standard QAT results in a 2-3% decrease. A common mistake is to perform fine-tuning without a representative sample. For stability, you need at least 1% of the training data, preserving the original distribution. On older GPUs without hardware INT4 support (e.g., P40), emulation is expensive, but a hybrid Int8+FP16 approach using custom operations can improve quantization performance. ➡️ Engineering Considerations for Deployment: In production, FWQAT is easily integrated: custom operations for TensorRT or direct access to the learnable parameters from the runtime. Warning: After fine-tuning, it's essential to recalibrate the scaling factor and zero-point on a validation dataset; otherwise, data drift will negatively impact metrics. Latency increases by 5-10% due to per-feature operations, but this is offset by the reduced memory requirements, especially when the batch size is less than 4 GB. What is the Conclusion? Feature-wise QAT preserves metrics in production models with INT4 by quantizing each feature separately, but it requires representative fine-tuning and mandatory recalibration in the production pipeline. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
114
9
Positional attention decay in transformer models: How information is lost from the middle of the context and what to do about
Positional attention decay in transformer models: How information is lost from the middle of the context and what to do about it in production? You give the model a long document and it confidently answers questions about the beginning and end, but fails when the answer is buried in the middle. This is not a bug in the implementation, it's a fundamental limitation of self-attention that breaks production RAG systems, agents and pipelines for analyzing long documents. A common mistake is to assume that the model will evenly distribute attention, and to not consider positional decay when designing prompts and architecture. ➡️ Why This Happens? Self-attention itself is invariant to position without positional encodings, it doesn't see distance. Absolute encodings (Sinusoidal, Learnable) quickly decay in practice, while relative encodings (RoPE, ALiBi) add bias: the further a token is from the current one, the less its contribution to the attention score. In deep layers, middle tokens receive fewer gradients: information from the center of the context is replaced by noise from edge tokens. In production, with lengths of 8k-16k tokens, this leads to a 20-40% drop in recall for facts located between 30% and 70% of the sequence. Production case: losing a fact in the middle of the context Example: you pass a prompt with 10k tokens of context containing 5 facts about a customer, and ask it to answer fact #3, which is hidden in the middle. I ran an A/B test on GPT-4 and Llama-3 70B with synthetic data: accuracy on middle queries was 62% compared to 94% on edge queries. In a RAG pipeline, this means that the retriever can find the block, but the model simply ignores it and you get an answer based on noise, not on data. ➡️ Practical techniques for production 1️⃣ Multi-turn summarization with chunking: you split the context into blocks of 2-4k tokens, summarize each block with a separate call to the model, and pass the compressed summary + the last chunk. Trade-off: latency increases by 2-4x, but we reduced the error rate by 35% in production. 2️⃣ Sparse attention with a sliding window: use architectures like Mistral, LongLoRA, or LongRoPE. Global tokens (the first 512) hold the beginning and end, while the local window (4096) holds the middle. If you're taking a model into production, look at YaRN or NTK-aware scaling, they redistribute RoPE frequencies for even coverage. 3️⃣ Context augmentation through re-ranking: in RAG, duplicate critical facts at the beginning and end of the prompt. Or add a weighted positional bias: inject a position_id modification into the embeddings. Warning: don't do this on the entire pipeline: it can break attention for short queries (test on real data). 4️⃣ Fine-tune with samples from the center: add examples to the training where the answer is located between 30% and 70% of the length. But there's a nuance: bias towards the center worsens recall at the edges; adjust the ratio to no more than 1:5 (center : edges) and validate on both ends. What's the Conclusion? Positional decay is not a bug, but a property of attention design, so in production, either compress the context through summarization, or structure it by duplicating key facts at the edges, or change the architecture to RWKV or Mamba, where positional encoding does not create this effect. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
132
10
Label Leakage: When Validation Lies, and Metrics are an Illusion Are you happy with a ROC-AUC of 0.99 on a complex pipeline,
Label Leakage: When Validation Lies, and Metrics are an Illusion Are you happy with a ROC-AUC of 0.99 on a complex pipeline, but the model performs at only 0.6 in production? Sounds familiar. This is label leakage: the leakage of the target variable. But often, the problem isn't a simple mistake like scaler.fit(X_train, y_train). The hidden causes are more subtle, and they often catch out mid-level and senior engineers. 1️⃣ Aggregates with a Focus on the Future A classic in feature engineering. For example, you calculate the average target value by category: df['avg_target_by_city'] = df.groupby('city')['target'].transform('mean') If you do this on the entire dataset before splitting into training and validation sets, the model will see the average calculated including future target values during validation. Metrics will skyrocket, but performance in production will be disastrous. Solution: Calculate aggregates strictly within the training fold, using target encoding in a Pipeline or GroupKFold. And avoid using transform before splitting the data. 2️⃣ Time-Aware Validation: The Illusion of Order Time series data without strict time-based splitting leads to leakage due to shuffling. The model "peeks" at data from the future during validation. A simple rule: avoid using train_test_split with random_state. Use TimeSeriesSplit or PurgedGroupTimeSeriesSplit instead. And check your lag features, they often look ahead. A common mistake: adding rolling aggregates to the entire dataset, rather than within a specific time window. 3️⃣ Feature Strings That Know the Answer Sometimes, a field like user_flag only appears after an event (the target). Or transaction_id correlates with the target: new transactions have a higher risk of default. Remove ID fields, and check their correlation with the target. A value greater than 0.95 is a clear sign of leakage. Another production example: in an NLP pipeline, when a token from a document is used as a feature, but it's assigned after the target has been labeled. This breaks validation in LabelPropagation when using streaming data. How to Detect Hidden Leakage? ☞ Lasso Regression: If the model keeps 1-2 features with extremely high weights, that's a red flag. ☞ Permutation Importance: An abnormally large drop in the metric when permuting a single feature. ☞ Lookahead Bias Audit: Make sure features are calculated at time t-1, not t. Use reverse engineering on a time-delayed sample. So the conclusion is Label leakage kills ML products. It's better to spend an hour auditing your pipeline with time-aware validation and permutation tests than two months trying to repair your reputation. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
134
11
Gradient Stability in Long Sequences How Spherical Gradient Protects Against Explosions and Vanishing Gradients in Online Time Series Learning? In online time series learning, gradients either explode or vanish when dealing with long sequences. LSTMs and GRUs handle this instability poorly: with streaming data, the model fails to adapt to new patterns due to the exponential growth or collapse of the gradient. The Problem with Standard Gradient Clipping Classic gradient clipping with a fixed threshold often fails in online mode. With short sequences, it aggressively truncates, losing information about rare events. And with long sequences, it doesn't protect against vanishing gradients because it only works with the upper bound of the norm. Spherical Gradient: Principle and Implementation This approach normalizes the gradient at each step, fixing its length while preserving its direction. This is L2 normalization, which solves both problems: - The gradient doesn't explode because the norm is limited (e.g., 1.0). - The gradient doesn't vanish because even when the norm is close to zero, it's restored to a fixed value. Here's an example in PyTorch for a production scenario: def spherical_gradient_clip(grad, max_norm=1.0, eps=1e-8): norm = grad.norm() if norm > max_norm: return grad * (max_norm / norm) elif norm < eps: return torch.randn_like(grad) * eps return grad Engineering Trade-offs in Production ML Combine Spherical Gradient with layer normalization and gradient checkpointing when the sequence length is greater than 500 steps (finance, IoT, logistics). Note: Gradient normalization increases latency by about 5-10%, but the stability of convergence pays off with real data. A common mistake is to apply Spherical Gradient to a Transformer without weight normalization, which breaks attention scores with high dimensionality. Practical Advice for Validation For online learning with streaming data, compare the variance of the gradients before and after applying Spherical Gradient on synthetic data with a length of 500. In production, for time series Transformers, Spherical Gradient shows a reduction in variance of 40-60% and accelerates loss convergence by 1.5 times compared to gradient clipping. Conclusion: Normalize the gradient in spherical space, rather than simply truncating it and this is the only way to maintain the stability of online learning on long sequences without losing sensitivity to rare events. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
276
12
Delayed Feedback In CVR Models How to avoid breaking conversion learning and evaluation due to delayed labels? In CVR, a user
Delayed Feedback In CVR Models How to avoid breaking conversion learning and evaluation due to delayed labels? In CVR, a user may convert minutes, days, or weeks after clicking, so labeling "as is" often turns future positive conversions into false negatives. This is critical for advertising, recommendations, marketplace funnels, and A/B tests, where the model is trained on incomplete logs. Problem: label may not yet be available. For a click at time click_time = t, we want to estimate: P(conversion | click, x) But at the time the dataset is collected (T), we only know one of two things: ☞ Conversion has already occurred before T. ☞ Conversion is not yet visible. The second case does not mean converted = 0. It's a censored observation: the user was not observed for long enough. A typical mistake: converted = 1, if conversion_time - click_time <= 7d converted = 0, else Fresh clicks have artificially low CVRs, the model learns from false negatives, offline metrics depend on the "maturity" of the data, and production calibration drifts: the model predicts the full-window CVR, while monitoring sees the partial-window CVR. Baseline: Train only on mature data. If the target horizon is a 7-day conversion, and data is available up to 2025-01-31, then for training, use clicks no later than 2025-01-24. ➜ Pros: ☞ Honest labels ☞ Simple validation ☞ Easy to debug the pipeline and identify data leakage ➜ Cons: ☞ Loss of fresh data ☞ Poorer adaptation to seasonality and traffic changes ☞ With a long conversion lag, the training data becomes significantly outdated. Practical advice: Explicitly store the event_time, label_observed_until, horizon, and label_age fields in the feature store or training dataset. Without them, it's impossible to reproduce the labeling and understand why the CVR changed after retraining. A more robust approach: Model the delay. The problem can be broken down into the probability of conversion and the distribution of the delay: P(y = 1, delay <= H | x) For example: ☞ CVR model estimates the probability of conversion itself. ☞ delay model estimates P(delay <= age | y=1, x) This approach is closer to survival analysis: there's an event, the time until the event, and censored observations. This approach is particularly useful if the delay depends on the product category, channel, geography, price, device, or retargeting strategy. An alternative is a discrete hazard function: P(conversion at day k | no conversion before day k, x) In this case, a click observed only 2 days ago is still useful for training the first two steps, rather than being discarded entirely. The trade-off is that the model and inference become more complex, but there's less data loss and a more accurate handling of the long tail of conversions. Evaluation: The test set should also be mature. If the horizon = 7d, the holdout set should only contain objects for which at least 7 days have passed since the click. Otherwise, you're measuring the immaturity of the labels, not the quality of the model. A good approach: train: clicks [D0, D1] validation: clicks [D2, D3] label cutoff: >= D3 + horizon In the production environment, also consider metrics related to delay buckets: * 0-1 hour * 1-24 hours * 1-3 days * 3-7 days * 7 days+ This helps identify where the system is failing: whether it's in fast conversions, the long tail, data freshness, attribution, or due to censored labels. This is especially important for A/B tests: an early readout can overestimate the effect of a model that performs well with fast conversions but underperforms over the full time window. Conclusion: Delayed feedback in CVR (Conversion Rate) is not just a matter of labeling; it's an engineering limitation of the ML system. Without mature labels, a clear label cutoff, and proper validation, the model optimizes for logging artifacts instead of actual conversions. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
210
13
Delayed feedback in CVR models: How to avoid breaking conversion learning and evaluation due to delayed labels? In CVR, a user may convert minutes, days, or weeks after clicking, so labeling "as is" often turns future positive conversions into false negatives. This is critical for advertising, recommendations, marketplace funnels, and A/B tests, where the model is trained on incomplete logs. The problem: The label may not yet be available. For a click at time click_time = t, we want to estimate: P(conversion | click, x) But at the time the dataset is collected (T), we only know one of two things: * The conversion has already occurred before T. * The conversion is not yet visible. The second case does not mean converted = 0. It's a censored observation: the user was not observed for long enough. A typical mistake: converted = 1, if conversion_time - click_time <= 7d converted = 0, else Fresh clicks have artificially low CVRs, the model learns from false negatives, offline metrics depend on the "maturity" of the data, and production calibration drifts: the model predicts the full-window CVR, while monitoring sees the partial-window CVR. Baseline: Train only on mature data. If the target horizon is a 7-day conversion, and data is available up to 2025-01-31, then for training, use clicks no later than 2025-01-24. Pros: * Honest labels * Simple validation * Easy to debug the pipeline and identify data leakage Cons: * Loss of fresh data * Poorer adaptation to seasonality and traffic changes * With a long conversion lag, the training data becomes significantly outdated. Practical advice: Explicitly store the event_time, label_observed_until, horizon, and label_age fields in the feature store or training dataset. Without them, it's impossible to reproduce the labeling and understand why the CVR changed after retraining. A more robust approach: Model the delay. The problem can be broken down into the probability of conversion and the distribution of the delay: P(y = 1, delay <= H | x) For example: * The CVR model estimates the probability of conversion itself. * The delay model estimates P(delay <= age | y=1, x) This approach is closer to survival analysis: there's an event, the time until the event, and censored observations. This approach is particularly useful if the delay depends on the product category, channel, geography, price, device, or retargeting strategy. An alternative is a discrete hazard function: P(conversion at day k | no conversion before day k, x) In this case, a click observed only 2 days ago is still useful for training the first two steps, rather than being discarded entirely. The trade-off is that the model and inference become more complex, but there's less data loss and a more accurate handling of the long tail of conversions. Evaluation: The test set should also be mature. If the horizon = 7d, the holdout set should only contain objects for which at least 7 days have passed since the click. Otherwise, you're measuring the immaturity of the labels, not the quality of the model. A good approach: train: clicks [D0, D1] validation: clicks [D2, D3] label cutoff: >= D3 + horizon In the production environment, also consider metrics related to delay buckets: * 0-1 hour * 1-24 hours * 1-3 days * 3-7 days * 7 days+ This helps identify where the system is failing: whether it's in fast conversions, the long tail, data freshness, attribution, or due to censored labels. This is especially important for A/B tests: an early readout can overestimate the effect of a model that performs well with fast conversions but underperforms over the full time window. Conclusion: Delayed feedback in CVR (Conversion Rate) is not just a matter of labeling; it's an engineering limitation of the ML system. Without mature labels, a clear label cutoff, and proper validation, the model optimizes for logging artifacts instead of actual conversions. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
2
14
Adding reactions to posts is disabled from now on. We are also reviewing the save and forward options.
235
15
Updated the encoder - broke the ANN? How to migrate embeddings without pain In embedding-based systems, the encoder is part o
Updated the encoder - broke the ANN? How to migrate embeddings without pain In embedding-based systems, the encoder is part of the data contract. It cannot be updated like a regular ML model: the ANN index already contains vectors from the old space, and a common mistake is to assume compatibility due to the same dimension and metric. Why compatibility breaks? Even if the dimension is the same and the cosine is the same, and the offline benchmark is better, the new encoder does not have to be compatible with the old index. After the update, the following change: - geometry of the space; - distribution of norms; - local neighborhoods; - ranking of nearest neighbors; - calibration of scores; - behavior of the ANN structure: HNSW/IVF/PQ were built for the old distribution. The main anti-pattern: writing new documents with the new encoder into the old index with old embeddings. Such an index becomes mixed: some vectors live in one space, others in another. The ANN works formally, but the nearest neighbors no longer have correct semantics. Versioning the embedding space as a production contract You need to version not just the model_name, but the full contract: embedding_version = encoder + tokenizer + pooling + normalization + dim + metric If any of these has changed, it's a new version of the space. Practical advice: keep the embedding_version next to the document, query, index, and retrieval logs. Otherwise, if recall or CTR degrades, you won't understand which encoder was actually involved in the delivery. Raising a new index and enabling dual-write The old path: docs_v1 -> embeddings_v1 -> ann_index_v1 The new path: docs_v2 -> embeddings_v2 -> ann_index_v2 Even if the documents are the same, the embeddings must be recalculated with the new encoder. For ANN, this is a new corpus. Importantly: the index parameters should also be tuned. For example, for HNSW, the old M, efConstruction, efSearch may not be optimal for the new distribution. During the migration, write new and updated documents to both versions: on_document_upsert(doc): emb_v1 = encoder_v1(doc) emb_v2 = encoder_v2(doc) index_v1.upsert(doc.id, emb_v1) index_v2.upsert(doc.id, emb_v2) This is more expensive in terms of compute and ingestion latency, but the old retrieval continues to work and the new index catches up with the current state. If v1 is soon shut down, dual-write can be kept only until the cutover plus a short rollback window. Backfill, shadow-read, and readiness criteria For v2, we need to recalculate the embeddings of the entire corpus and upload them to the new index. Here, it's not about notebook metrics, but about engineering reliability: - idempotency of tasks; - control of lag; - deduplication of upserts; - checkpoints; - separate limits on encoder and ANN ingestion; - document count comparison between indexes; - percentage of documents without v2 embeddings. The migration is not ready until the new index covers the production corpus with an acceptable lag. Before switching, enable shadow-read: query -> encoder_v1 -> index_v1 -> results_v1 -> encoder_v2 -> index_v2 -> results_v2 Show only v1 to the user, but compare: - recall@k on labeled data; - overlap@k between v1 and v2; - NDCG/MRR if there are clicks or raters; - p95/p99 latency; - tail failures; - score distribution; - downstream metrics in ranking, recommendations, or RAG. Warning: high overlap@k does not guarantee product improvement. The new retrieval may change diversity, freshness, coverage, and load on the next ranker. It's better to do the cutover via a feature flag, with monitoring of quality, latency, error rate, and a quick rollback to ann_index_v1. Conclusion: Updating the encoder is a migration of the embedding contract and ANN infrastructure, not a simple model replacement in the inference path. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
290
16
How to Find Harmful Training Examples Before Fine-Tuning: Influence Functions, TracIn, and Data Pruning in Production ML In p
How to Find Harmful Training Examples Before Fine-Tuning: Influence Functions, TracIn, and Data Pruning in Production ML In production ML, "bad" training examples can be costly: a cluster of mislabeled, outdated, or anomalous objects can consistently degrade fine-tuning on a fresh data set. A common mistake is to clean the dataset only based on heuristics and not checking which samples actually increase the loss on the production-like validation set. 1️⃣ Influence Functions Idea: estimate how the loss on the validation set z_val would change if we slightly increase the weight of the training example z_train. I(z_train, z_val) ≈ - ∇L_val^T H^-1 ∇L_train where H is the Hessian with respect to the model parameters. If the influence is large and positive, the training example is likely harming the validation loss and quality. Pros: - rigorous theoretical formulation; - can associate specific training examples with specific model errors. Cons: - expensive H^-1; - poorly scalable to large neural networks; - sensitive to non-convexity, batchnorm/dropout, checkpoints, and Hessian approximation. In production, we typically use approximations: LiSSA, conjugate gradients, low-rank approximation, or calculate the influence only for the last layer/head of the model. 2️⃣ TracIn A more engineering-oriented approach: a training example is useful for the validation set if their gradients evolve similarly during training. It's harmful if they evolve in the opposite direction. TracIn(z_train, z_val) = Σ_c η_c · ∇L_train(θ_c) · ∇L_val(θ_c) where θ_c are checkpoints and η_c is the learning rate. A strongly negative score means: the training example is pulling the model in the opposite direction of what's useful for validation. A mini sketch for the last layer: for ckpt in checkpoints: model.load_state_dict(load(ckpt)) g_val = mean_grad(model.head, val_loader) for i, batch in enumerate(train_subset): g_train = grad(model.head, batch) scores[i] += lr[ckpt] * dot(g_train, g_val) harmful = argsort(scores)[:K] Practical advice: calculate the score not on the entire validation set, but on important production slices: new users, rare classes, problematic regions, fresh drift, segments with high business value or SLA. 3️⃣ Data pruning before fine-tuning Workflow: 1. Freeze a production-like validation set without leakage. 2. Train a baseline / fine-tune and save several checkpoints. 3. Calculate the influence or TracIn for train→val. 4. Check the top harmful samples: - label noise; - outdated distribution; - conflicting duplicates; - corrupted inputs; - incorrect task/schema version. 5. Remove, downweight, or relabel them. 6. Repeat fine-tuning and check not only the overall metric but also the regression by segment. Production example: before retraining a recommendation model on fresh logs, you might find old interactions with a changed product taxonomy, conflicting labels after a schema migration, or bot traffic that degrades the ranking loss on a fresh holdout. 4️⃣ Caution Don't blindly remove all "harmful" examples. Sometimes they degrade the current validation, but are needed for long-tail robustness, fairness, or resilience to rare scenarios. It's safer to start with top-K, do a human-in-the-loop audit, compare remove / downweight / relabel options, and look at the trade-off between quality, latency of recalculation, cost of labeling, reproducibility, and monitoring reliability. Conclusion: Influence Functions and TracIn are useful not as a magic data cleaning tool, but as an engineering approach to make fine-tuning less toxic to noise, outdated data, and conflicting labeling. •••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
295
17
Temporal leakage in the feature store: How point-in-time joins, backfills, and feature causality checks save a model from bea
Temporal leakage in the feature store: How point-in-time joins, backfills, and feature causality checks save a model from beautiful offline metrics and failure in production? Temporal leakage in the feature store is one of the most expensive ways to get great offline metrics and a useless model in production. The problem isn't that the feature is bad, but that on train it knows more than the model would have known at the moment of decision-making. We predict churn on date t, but in the features we use transactions_last_30d, calculated after a backfill from a table where transactions arrived with a delay or were recalculated with future fixes. Offline is all beautiful. Online - a slump. 1️⃣ Point-in-time join - basic protection For each training row, there is prediction_time. The features should be in the state they were in at that moment. It's important to distinguish: - event_time - when the event actually happened; - ingestion_time / created_at - when it entered the system; - available_at - when the feature became available to the model; - prediction_time - the moment of prediction. The correct join should take into account not only event_time <= prediction_time, but also available_at <= prediction_time: WITH ranked_features AS ( SELECT l.entity_id, l.prediction_time, f.feature_value, ROW_NUMBER() OVER ( PARTITION BY l.entity_id, l.prediction_time ORDER BY f.event_time DESC ) AS rn FROM labels l JOIN features f ON f.entity_id = l.entity_id AND f.event_time <= l.prediction_time AND f.available_at <= l.prediction_time ) SELECT * FROM ranked_features WHERE rn = 1; If there is no available_at, you often can't prove that there is no leakage. 2️⃣ Backfills - a hidden source of leakage Backfills are dangerous because they create the illusion of historical completeness. For example, today you recalculated a feature for the past year: - corrected old events; - added data from a new source; - changed the business logic; - caught up with late-arriving events; - used a reference that wasn't available at the time. As a result, train gets a history that didn't actually exist at the moment of prediction. A correct backfill should answer the question: What feature would the model have seen then if the pipeline had worked with the same delays, sources, and availability rules? If the answer is unknown, it's not historical truth, but reconstructed truth. For model training, these are different things. 3️⃣ Checking the causality of features Before training, every feature should be run through a causality review. ➡️ Minimum checklist: 1. Is the feature available before prediction_time? It's not that the event happened, but that the value of the feature was available. 2. Is there a label proxy in the feature? For example, days_since_last_payment_failed for a default task might be almost a direct consequence of a future target. 3. Is the aggregation window strictly in the past? last_7d should mean [t-7d, t), not a calendar week that includes the future relative to t. 4. Are there future-aware reference tables? Segments, statuses, limits, antifraud flags, and CRM attributes are often backfilled. 5. Is the source latency taken into account? If the data arrives in 6 hours, you can't use an event at 09:55 for a prediction at 10:00. In production ML, a feature is considered valid not when it's historically correct, but when it's demonstrably available to the model at the moment of decision-making. •••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
210
18
Conformal intervals in production ML with covariate shift: How to maintain coverage without unnecessarily wide predictions? S
Conformal intervals in production ML with covariate shift: How to maintain coverage without unnecessarily wide predictions? Split conformal works well with exchangeability: train, calibration, and test come from same distribution. In production, this often breaks down due to geo, devices, channels, seasonality or a change in acquisition mix and a common mistake is to simply expand intervals "with a margin". What exactly breaks down? With covariate shift, we have: p_prod(x) != p_cal(x) but we assume that p(y|x) approximately holds. If we calculate usual conformal quantile on old calibration set, coverage on current traffic might drop. Naive solution is to globally increase correction. Coverage will partially recover, but price prediction interval, ETA interval or forecast band will become so wide that downstream system will no longer trust them. Basic production recipe 1️⃣ Train a quantile model: q_low(x), q_high(x) 2️⃣ Calculate nonconformity scores on calibration set: s_i = max(q_low(x_i)-y_i, y_i-q_high(x_i), 0) 3️⃣ Estimate importance weights: w_i ~= p_prod(x_i) / p_cal(x_i) 4️⃣ Use weighted quantile scores instead of usual ones. 5️⃣ For a new object, construct: C(x) = [q_low(x)-tau, q_high(x)+tau] Minimal skeleton: import numpy as np def weighted_quantile(values, weights, q): order = np.argsort(values) v = np.asarray(values)[order] w = np.asarray(weights)[order] cw = np.cumsum(w) return v[np.searchsorted(cw, q * cw[-1])] alpha = 0.1 scores = np.maximum(q_low_cal - y_cal, y_cal - q_high_cal, 0) weights = ratio_model.predict_weight(X_cal) tau = weighted_quantile(scores, weights, 1 - alpha) low = q_low_prod - tau high = q_high_prod + tau This way, calibration distribution becomes closer to production distribution without unnecessarily widening all intervals. How not to get too wide intervals? One global tau often overestimates uncertainty if model error strongly depends on x. Practically helps: - CQR instead of point prediction: Conformalized Quantile Regression already models heteroscedastic uncertainty, so conformal correction is usually smaller. - Normalized score: for example s_i = |y_i - y_hat_i| / sigma_hat(x_i), and the interval is constructed as y_hat(x) +- tau * sigma_hat(x). - Local calibration: a separate tau per geo, device, channel, price bucket, or risk bucket. This is close to Mondrian conformal, but requires a sufficient number of calibration examples in each segment. - Rolling calibration buffer: for recommendations, scoring and forecasting, old calibration set quickly stops describing current traffic mix. Main risk - bad weights Density ratio model can be noisy. A few objects with huge weights effectively "replace" entire calibration set. Control: ESS = (sum w)^2 / sum(w^2) If ESS is low, the weighted quantile is unstable and intervals start jumping from release to release. Practical measures: - clip weights and monitor proportion of clipped weights; - smooth the density ratio; - merge rare segments; - not calibrate a segment where there are few fresh labels; - run recalibration when ESS drops or distribution drifts on X. Production checklist - a separate calibration set, not mixed with training; - drift detection on feature distribution; - density ratio model between prod traffic and calibration traffic; - weighted conformal calibration; - monitor coverage, average width, coverage by slices, ESS, and latency; - alerts on increasing interval width without increasing error; - A/B validation if intervals affect routing, fallback or human review. It's important not to confuse marginal and conditional coverage. Conformal can maintain 90% coverage on stream on average, but fail in individual microsegments. This needs to be explicitly checked in production. With covariate shift, goal is not to blindly widen the intervals, but to calibrate them to current mix of production objects and monitor the reliability of this calibration. ••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
198
19
⁣Corpus drift in RAG systems: How to notice the degradation of retrieval without labels, annotations, and obvious errors? In
⁣Corpus drift in RAG systems: How to notice the degradation of retrieval without labels, annotations, and obvious errors? In RAG retrieval, things often break silently: same model, same embedding model, same prompt, normal latency, but the answers have gotten worse. A typical mistake is to immediately tweak the prompt or blame the LLM, even though the problem lies deeper: the corpus has changed. 1️⃣ Monitor the corpus drift itself We don't directly measure quality, but we look at how the space in which the retriever operates has changed: - distribution of embedding chunks; - average chunk length, overlap, number of chunks per document; - proportion of new, deleted, and modified chunks; - duplicates and near-duplicates; - distribution of domains, document types, languages, dates; - density of the embedding space: have many chunks "clumped" together. If the corpus has noticeably shifted, old retrieval thresholds and expectations of top-k might become garbage. Especially if the confidence logic is tied to score or the gap between top-1 and top-2. 2️⃣ Anchor queries instead of labels In production, there are almost never labels like "these chunks are relevant for this query". But we can take a stable set of production queries: for example, 500-5,000 frequent or business-critical queries. This isn't annotation. We don't know the correct chunk. But we know that the retrieval behavior shouldn't change chaotically after each corpus update. For each anchor query, save the baseline: - top-k doc/chunk ids; - retrieval scores; - rank positions; - gap between top-1 and top-2; - diversity of top-k; - source distribution. After the corpus update, compare the new retrieval with the baseline. Useful proxy metrics: - Jaccard@k between the old and new top-k; - p95_top1_score_drop; - score_wasserstein between the baseline and current scores. 3️⃣ How to interpret the signals - mean_jaccard@10 has dropped sharply: the retriever has started returning different context; - the top-1 score systematically drops: the queries are matching the corpus less well; - the score distribution has shifted significantly: old thresholds and confidence logic might have broken. Practical advice: don't just look globally, but also by segments - sources, languages, document types, product domains. A global average easily hides degradation in a critical segment. 4️⃣ Retrieval confidence without ground truth Even without annotations, you can look at the "confidence" of the retriever: - high top-1 score; - large gap between top-1 and top-2; - consistency of dense retrieval and BM25; - stability of top-k when query rewriting; - low proportion of duplicates in top-k; - coverage of needed sources. If dense and lexical retrieval suddenly start diverging, don't just chalk it up to noise. Often, this means that the corpus or queries have changed in a way that one of the strategies no longer works as before. Production minimum for RAG: - store a snapshot of retrieval results for anchor queries; - calculate overlap, score drift, and rank churn after each corpus update; - monitor duplicates, new chunks, and source distributions separately; - set alerts not on a single query, but on aggregates by segments. Corpus drift is annoying because it doesn't look like a crash. The system responds, there are no errors, and the latency is normal. It's just that the context has become slightly less relevant. Then a little more. And the RAG quality slowly declines. The conclusion is WITHOUT LABELS, you CAN'T honestly measure relevance, but you can monitor the stability of retrieval behavior, the retriever's confidence, and corpus changes to catch degradation before users do. ••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
202
20
⁣Corpus drift in RAG systems In RAG retrieval, things often break silently: same model, same embedding model, same prompt, no
⁣Corpus drift in RAG systems In RAG retrieval, things often break silently: same model, same embedding model, same prompt, normal latency, but the answers have gotten worse. A typical mistake is to immediately tweak the prompt or blame the LLM, even though the problem lies deeper: the corpus has changed. ➡️ How to notice degradation of retrieval without labels, annotations and obvious errors? 1️⃣ Monitor the corpus drift itself We don't directly measure quality, but we look at how the space in which the retriever operates has changed: - distribution of embedding chunks; - average chunk length, overlap, number of chunks per document; - proportion of new, deleted, and modified chunks; - duplicates and near-duplicates; - distribution of domains, document types, languages, dates; - density of the embedding space: have many chunks "clumped" together. If the corpus has noticeably shifted, old retrieval thresholds and expectations of top-k might become garbage. Especially if the confidence logic is tied to score or the gap between top-1 and top-2. 2️⃣ Anchor queries instead of labels In production, there are almost never labels like "these chunks are relevant for this query". But we can take a stable set of production queries: for example, 500-5,000 frequent or business-critical queries. This isn't annotation. We don't know the correct chunk. But we know that the retrieval behavior shouldn't change chaotically after each corpus update. For each anchor query, save the baseline: - top-k doc/chunk ids; - retrieval scores; - rank positions; - gap between top-1 and top-2; - diversity of top-k; - source distribution. After the corpus update, compare the new retrieval with the baseline. Useful proxy metrics: - Jaccard@k between the old and new top-k; - p95_top1_score_drop; - score_wasserstein between the baseline and current scores. 3️⃣ How to interpret the signals - mean_jaccard@10 has dropped sharply: the retriever has started returning different context; - the top-1 score systematically drops: the queries are matching the corpus less well; - the score distribution has shifted significantly: old thresholds and confidence logic might have broken. Practical advice: don't just look globally, but also by segments - sources, languages, document types, product domains. A global average easily hides degradation in a critical segment. 4️⃣ Retrieval confidence without ground truth Even without annotations, you can look at the "confidence" of the retriever: - high top-1 score; - large gap between top-1 and top-2; - consistency of dense retrieval and BM25; - stability of top-k when query rewriting; - low proportion of duplicates in top-k; - coverage of needed sources. If dense and lexical retrieval suddenly start diverging, don't just chalk it up to noise. Often, this means that the corpus or queries have changed in a way that one of the strategies no longer works as before. Production minimum for RAG: - store a snapshot of retrieval results for anchor queries; - calculate overlap, score drift, and rank churn after each corpus update; - monitor duplicates, new chunks, and source distributions separately; - set alerts not on a single query, but on aggregates by segments. Corpus drift is annoying because it doesn't look like a crash. The system responds, there are no errors, and the latency is normal. It's just that the context has become slightly less relevant. Then a little more. And the RAG quality slowly declines. Conclusion is WITHOUT LABELS, you CAN'T honestly measure relevance, but you can monitor the stability of retrieval behavior, the retriever's confidence, and corpus changes to catch degradation before users do. ••••••••••••••••••••••••••••••••••••• 🤖 Data & ML | @DataXplore
2