Datadrift: verschuivingen in featureverdelingen door de tijd
Cursisten simuleren drift door een invoerfeature geleidelijk te verschuiven, berekenen de Population Stability Index (PSI) en KL-divergentie en stellen waarschuwingen op basis van drempelwaarden in.
Datadrift: verschuivingen in featureverdelingen door de tijd is een gratis Machine Learning Academy-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Machine Learning Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Machine Learning Academy bevat in totaal 4 lessen.
Wat is gegevensverschuiving?
Gegevensverschuiving (ook covariate shift genoemd) treedt op wanneer de statistische verdeling van invoerkenmerken na implementatie verandert ten opzichte van de verdeling die tijdens het trainen is waargenomen. Een fraudedetectiemodel dat is getraind op transactiepatronen uit 2022 kan in 2024 te maken krijgen met heel andere transactiebedragen en handelaarcategorieën. De aangeleerde beslissingsgrenzen van het model komen dan niet meer overeen met de nieuwe gegevensverdeling, waardoor de prestaties ongemerkt afnemen. Dat wordt pas zichtbaar door monitoring.
Verschuiving simuleren: geleidelijke verschuiving van kenmerken
Om verschuiving te bestuderen, kunnen we die simuleren door het gemiddelde van een kenmerk geleidelijk te laten verschuiven. In productie kan dit staan voor seizoensgebonden veranderingen in gebruikersgedrag, economische verschuivingen die de koopkracht beïnvloeden of veranderende fraudepatronen. Door de verdeling van het kenmerk voor elke week te plotten, zie je wanneer de verschuiving statistisch significant wordt en een melding voor opnieuw trainen moet activeren.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
np.random.seed(42)
# Training distribution: income ~ Normal(50000, 10000)
train_income = np.random.normal(50000, 10000, 5000)
# Production weeks 1-12: mean gradually shifts from 50k to 65k
prod_weeks = []
for week in range(1, 13):
shifted_mean = 50000 + week * 1250 # +1250 per week
week_data = np.random.normal(shifted_mean, 10000, 500)
prod_weeks.append({'week': week, 'income': week_data})
print('Training mean:', train_income.mean().round(0))
for pw in [prod_weeks[0], prod_weeks[5], prod_weeks[-1]]:
print(f'Week {pw["week"]} mean: {pw["income"].mean().round(0)}')Population Stability Index (PSI)
De Population Stability Index (PSI) is de meestgebruikte metriek voor het detecteren van verschuivingen in kenmerken in de financiële sector. De index vergelijkt twee verdelingen door ze in intervallen op te delen en het verschil in de verhoudingen per interval te meten. Een PSI lager dan 0,1 betekent dat er geen significante verschuiving is; 0,1–0,2 wijst op een gematigde verschuiving die onderzoek vereist; boven 0,2 is sprake van een grote verschuiving waarvoor direct opnieuw trainen nodig is.
import numpy as np
def compute_psi(reference, current, buckets=10):
breakpoints = np.percentile(reference, np.linspace(0, 100, buckets + 1))
breakpoints[0], breakpoints[-1] = -np.inf, np.inf
ref_pcts = np.histogram(reference, bins=breakpoints)[0] / len(reference)
cur_pcts = np.histogram(current, bins=breakpoints)[0] / len(current)
# Avoid division by zero
ref_pcts = np.where(ref_pcts == 0, 1e-6, ref_pcts)
cur_pcts = np.where(cur_pcts == 0, 1e-6, cur_pcts)
psi = np.sum((cur_pcts - ref_pcts) * np.log(cur_pcts / ref_pcts))
return round(psi, 4)
train_income = np.random.normal(50000, 10000, 5000)
week6_income = np.random.normal(57500, 10000, 500)
week12_income = np.random.normal(65000, 10000, 500)
print('PSI week 6:', compute_psi(train_income, week6_income))
print('PSI week 12:', compute_psi(train_income, week12_income))KL-divergentie voor het meten van verschuiving
KL-divergentie (Kullback-Leibler-divergentie) meet hoeveel de ene kansverdeling verschilt van een referentieverdeling. De waarde is altijd niet-negatief en is alleen nul wanneer de verdelingen identiek zijn. In tegenstelling tot PSI is KL-divergentie asymmetrisch: D(P||Q) ≠ D(Q||P). Bereken voor detectie van verschuiving de KL-divergentie tussen histogrammen van de trainingsgegevens en histogrammen van de productiegegevens voor elk kenmerk, en geef een waarschuwing wanneer de waarde een gekalibreerde drempel overschrijdt.
import numpy as np
from scipy.stats import entropy
from scipy.special import kl_div
def compute_kl_divergence(reference, current, buckets=20):
bins = np.linspace(
min(reference.min(), current.min()),
max(reference.max(), current.max()),
buckets + 1
)
ref_hist, _ = np.histogram(reference, bins=bins, density=True)
cur_hist, _ = np.histogram(current, bins=bins, density=True)
# Add small epsilon to avoid log(0)
ref_hist = ref_hist + 1e-10
cur_hist = cur_hist + 1e-10
kl = entropy(cur_hist, ref_hist) # KL(current || reference)
return round(kl, 4)
train = np.random.normal(0, 1, 5000)
current = np.random.normal(0.5, 1.2, 1000)
print('KL divergence:', compute_kl_divergence(train, current))Statistische toetsen: Kolmogorov-Smirnov-toets
De Kolmogorov-Smirnov-toets (KS-toets) is een niet-parametrische statistische toets die het maximale verschil tussen twee cumulatieve verdelingsfuncties meet. Gebruik scipy.stats.ks_2samp om referentiesteekproeven en productiesteekproeven te vergelijken. De toets retourneert een statistiek (groter = meer verschillend) en een p-waarde (kleiner = statistisch significanter). Een p-waarde lager dan 0,05 wijst op een statistisch significant verschil tussen de verdelingen.
import numpy as np
from scipy.stats import ks_2samp
np.random.seed(42)
train = np.random.normal(50000, 10000, 5000)
week_results = []
for week in range(1, 13):
prod_sample = np.random.normal(50000 + week * 1250, 10000, 500)
stat, p_value = ks_2samp(train, prod_sample)
week_results.append((week, round(stat, 4), round(p_value, 4)))
alert = 'DRIFT ALERT' if p_value < 0.05 else 'OK'
print(f'Week {week:2d}: KS={stat:.4f} p={p_value:.4f} {alert}')Meerdere kenmerken monitoren
Productiedatasets bevatten veel kenmerken en in elk daarvan kan drift optreden. Houd alle invoerkenmerken in de gaten door voor elke kolom in elke monitoringperiode driftscores te berekenen. Gebruik een heatmap om PSI- of KS-statistiekwaarden voor de kenmerken in de loop van de tijd zichtbaar te maken. Kenmerken met voortdurend hoge driftscores zijn de belangrijkste verdachten wanneer de modelprestaties verslechteren en moeten als eerste worden onderzocht tijdens de analyse van de hoofdoorzaak.
import numpy as np
import pandas as pd
from scipy.stats import ks_2samp
# Simulate reference and production with drift in some features
np.random.seed(42)
n_features = 5
reference = pd.DataFrame(
np.random.normal(0, 1, (5000, n_features)),
columns=[f'feature_{i}' for i in range(n_features)]
)
# Introduce drift in features 1 and 3
production = pd.DataFrame(
np.random.normal([0, 1.5, 0, 2.0, 0], 1, (1000, n_features)),
columns=reference.columns
)
drift_report = {}
for col in reference.columns:
stat, p_val = ks_2samp(reference[col], production[col])
drift_report[col] = {'ks_stat': round(stat, 4), 'p_value': round(p_val, 4),
'drift': p_val < 0.05}
for feature, result in drift_report.items():
status = '*** DRIFT ***' if result['drift'] else 'stable'
print(f'{feature}: KS={result["ks_stat"]} {status}')Waarschuwingsdrempels instellen
Waarschuwingsdrempels moeten worden gekalibreerd op historische gegevens en niet willekeurig worden gekozen. Een veelgebruikte aanpak is om de driftmaat te berekenen tussen uw trainingsset en meerdere afzonderlijk gehouden validatiesplitsingen, zodat u een basisverdeling van scores zonder drift kunt vaststellen. Stel de waarschuwingsdrempel in op het 99e percentiel van deze basisverdeling — elke productiescore boven dit niveau komt statistisch gezien waarschijnlijk niet uit dezelfde verdeling voort, wat op echte drift wijst.
import numpy as np
from scipy.stats import ks_2samp
train = np.random.normal(0, 1, 5000)
# Calibrate: compute KS statistic between train and 100 random validation splits
calibration_scores = []
for _ in range(100):
val_sample = np.random.normal(0, 1, 500) # same distribution
stat, _ = ks_2samp(train, val_sample)
calibration_scores.append(stat)
threshold_99 = np.percentile(calibration_scores, 99)
print(f'No-drift KS scores -- mean: {np.mean(calibration_scores):.4f}')
print(f'99th percentile threshold: {threshold_99:.4f}')
# Any production score above this triggers an alert
prod_sample_drifted = np.random.normal(0.5, 1, 500) # shifted
stat, _ = ks_2samp(train, prod_sample_drifted)
print(f'Production KS: {stat:.4f} -- ALERT: {stat > threshold_99}')Drift in categorische kenmerken
Bij categorische kenmerken betekent drift dat de frequentieverdeling van categorieën verandert. Een veld voor een land kan bijvoorbeeld verschuiven van 80% USA in de training naar 60% USA in productie wanneer het product wereldwijd wordt uitgebreid. Gebruik chi-kwadraattoetsen of Jensen-Shannon-divergentie om categorische verdelingen te vergelijken. Nieuwe categorieën die in productie verschijnen maar niet in de training voorkwamen, vormen een speciaal geval — ze veroorzaken KeyError in labelcoders die niet zijn gefit met handle_unknown='ignore'.
import numpy as np
from scipy.stats import chi2_contingency
# Training distribution
train_countries = np.random.choice(
['US', 'GB', 'DE', 'FR'], p=[0.7, 0.15, 0.1, 0.05], size=5000
)
# Production: more EU traffic
prod_countries = np.random.choice(
['US', 'GB', 'DE', 'FR'], p=[0.5, 0.2, 0.2, 0.1], size=1000
)
categories = ['US', 'GB', 'DE', 'FR']
train_counts = [np.sum(train_countries == c) for c in categories]
prod_counts = [np.sum(prod_countries == c) for c in categories]
contingency = np.array([train_counts, prod_counts])
chi2, p_value, dof, _ = chi2_contingency(contingency)
print(f'Chi-squared: {chi2:.2f}, p-value: {p_value:.4f}')
print('Categorical drift detected:', p_value < 0.05)Driftmetingen in de loop van de tijd loggen
Driftmonitoring is alleen nuttig als je metingen in de loop van de tijd consistent logt en trends zichtbaar maakt. Sla wekelijkse driftscores op in een tijdreeksdatabase (InfluxDB, PostgreSQL of zelfs een CSV-bestand) en zet ze uit met een voortschrijdend venster. Een plotselinge piek in de driftscore wijst de exacte week aan waarin een gegevenspijplijn veranderde of een externe gebeurtenis het gebruikersgedrag beïnvloedde. Zo kan het team sneller de hoofdoorzaak analyseren.
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
records = []
base_date = datetime(2024, 1, 1)
for week in range(12):
prod_sample = np.random.normal(week * 0.1, 1, 500) # gradual drift
from scipy.stats import ks_2samp
train_sample = np.random.normal(0, 1, 5000)
stat, p = ks_2samp(train_sample, prod_sample)
records.append({
'date': base_date + timedelta(weeks=week),
'ks_stat': round(stat, 4),
'p_value': round(p, 4),
'alert': p < 0.05
})
df = pd.DataFrame(records)
print(df.to_string(index=False))
# In production: df.to_sql('drift_log', engine, if_exists='append')Wat te doen wanneer drift wordt gedetecteerd
Wanneer drift wordt gedetecteerd, zijn er afhankelijk van de ernst verschillende reactiestrategieën. Lichte drift: verhoog de monitoringsfrequentie en onderzoek de oorzaak. Matige drift: start het opnieuw trainen met het meest recente gegevensvenster. Ernstige drift: ga na of de oude feature-engineering nog geldig is voor de nieuwe verdeling; mogelijk moeten de kenmerken opnieuw worden ontworpen. Documenteer de driftgebeurtenis altijd, evenals de bedrijfscontext (bijvoorbeeld een marketingcampagne of een platformwijziging) die de drift heeft veroorzaakt.
def respond_to_drift(psi_score, ks_p_value):
if psi_score < 0.1 and ks_p_value > 0.05:
return 'No action needed. All features stable.'
elif psi_score < 0.2 and ks_p_value > 0.01:
return ('Moderate drift detected. '
'Increase monitoring to daily. '
'Schedule retraining for next cycle.')
else:
return ('SEVERE DRIFT. '
'Trigger emergency retraining now. '
'Consider feature engineering review. '
'Alert data engineering team.')
# Example
print(respond_to_drift(0.05, 0.3)) # stable
print(respond_to_drift(0.15, 0.04)) # moderate
print(respond_to_drift(0.35, 0.001)) # severeDrift van kenmerken in de loop van de tijd zichtbaar maken
Door drift als tijdreeks zichtbaar te maken, krijgen belanghebbenden inzicht in de ernst en timing. Zet de PSI- of KS-statistiek voor elk gemonitord kenmerk wekelijks uit met een voortschrijdend venster. Wanneer een kenmerklijn de waarschuwingsdrempel overschrijdt, voeg je aan de grafiek de datum en de aard van de bedrijfsgebeurtenis toe die de verschuiving heeft veroorzaakt (bijvoorbeeld een marketingcampagne, een prijswijziging of een seizoenspatroon). Zo worden ruwe statistieken omgezet in bruikbare bedrijfsinformatie.
import matplotlib.pyplot as plt
import numpy as np
weeks = list(range(1, 13))
psi_feature_A = [0.01, 0.02, 0.03, 0.05, 0.07, 0.11, 0.18, 0.22, 0.25, 0.24, 0.23, 0.26]
psi_feature_B = [0.01, 0.01, 0.02, 0.01, 0.02, 0.03, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08]
plt.figure(figsize=(10, 4))
plt.plot(weeks, psi_feature_A, marker='o', label='amount_usd (drifting)')
plt.plot(weeks, psi_feature_B, marker='s', label='merchant_category (stable)')
plt.axhline(0.1, color='orange', linestyle='--', label='Mild drift threshold')
plt.axhline(0.25, color='red', linestyle='--', label='Severe drift threshold')
plt.annotate('Pricing change', xy=(6, 0.11), xytext=(6, 0.16), arrowprops=dict(arrowstyle='->'))
plt.xlabel('Week')
plt.ylabel('PSI')
plt.legend()
plt.title('PSI Trend — Feature-Level Drift Monitoring')
plt.tight_layout()
plt.savefig('psi_trend.png', dpi=150)Snelle controle
Toets uw begrip van de concepten rond machinaal leren met Python uit deze les.
Samenvatting van de les
In deze les heb je geleerd dat datadrift een verschuiving is in de verdelingen van invoerkenmerken tussen het moment van trainen en het moment van gebruik in productie, waardoor modellen ongemerkt slechter kunnen presteren, dat PSI- en KS-toetsen kwantitatieve driftscores opleveren met waarschuwingsdrempels die in de sector gebruikelijk zijn (PSI > 0,2 = aanzienlijke drift) en dat driftmonitoring alle invoerkenmerken moet omvatten en scores in de loop van de tijd moet loggen om trendanalyse en het vaststellen van de hoofdoorzaak mogelijk te maken. Hierna maken we onderscheid tussen conceptdrift — wanneer de relatie tussen de invoer en het doel-label zelf verandert — een subtieler en gevaarlijker verschijnsel.
Leer Python met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 30
- Lessen
- 120
Veelgestelde vragen
Is de les “Datadrift: verschuivingen in featureverdelingen door de tijd” gratis?
Ja — de volledige tekst van “Datadrift: verschuivingen in featureverdelingen door de tijd” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Machine Learning Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Machine Learning Academy bevat in totaal 4 lessen.
Wat leer ik in “Datadrift: verschuivingen in featureverdelingen door de tijd”?
Cursisten simuleren drift door een invoerfeature geleidelijk te verschuiven, berekenen de Population Stability Index (PSI) en KL-divergentie en stellen waarschuwingen op basis van drempelwaarden in. Je oefent met Machine Learning Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met Machine Learning Academy te beginnen?
Ervaring vooraf is niet nodig. Machine Learning Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.
Hoe lang duurt de les “Datadrift: verschuivingen in featureverdelingen door de tijd”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over Machine Learning Academy?
Ja. Elke les over Machine Learning Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Datadrift: verschuivingen in featureverdelingen door de tijd
- Conceptdrift: wanneer de relatie tussen X en Y verandert
- Voorspellingsverdelingen en betrouwbaarheidsscores monitoren
- Een driftwaarschuwingspipeline bouwen met Evidently AI