Handboek · hoofdstuk 42Classificatie en clustering
Labels voorspellen, groepen ontdekken en de kosten van verkeerde besluiten meten
Een droge plant kan dezelfde warme bladeren hebben als een zieke plant. Je leert sensorkenmerken naar een label omzetten en ongeëtiketteerde patronen groeperen. De centrale vraag blijft welk bewijs een voorspelling werkelijk levert en welke fout het station kan dragen.

- Je kan logistische regressie afleiden uit sigmoid en kruis-entropie.
- Je kan KNN en een beslisboom uitvoeren en hun grenzen vergelijken.
- Je kan k-means met echte iteraties bouwen en interpreteren.
- Je kan confusiematrix, precisie, recall en actiedrempel berekenen.
- Je kan klasse-onbalans en plantgroepen eerlijk evalueren.
Eerst het labelcontract
Een label ‘ziek’ vraagt een definitie: welke ziekte, volgens welke referentie, op welk moment? Een stresslabel uit bladtemperatuur is geen betrouwbare ziektediagnose zonder aanvullend bewijs. Droogte, hitte, licht en infectie kunnen overlappende patronen veroorzaken. De voorbeelden hier gebruiken synthetische labels om de algoritmen te leren.
Bewaar plant-ID, batch, meetplaats, tijd UTC+1, meetkwaliteit en labelherkomst. Meerdere beelden van dezelfde plant mogen niet over training en test worden verdeeld als je prestaties op nieuwe planten wilt kennen. Een referentiemeting wordt onafhankelijk van de modelsuggestie vastgesteld; anders bevestigt de annotator onbedoeld het model.
| vraag | vastleggen |
|---|---|
| positief | expliciet gedefinieerd probleem |
| referentie | controle door deskundige of passende proef |
| horizon | nu of toekomstige aantasting |
| actie | inspectievoorstel, geen directe diagnose |
Logistische regressie voorspelt een labelkans
Neem een score en . De log-odds is . Een lineaire score krijgt zo een waarde tussen nul en één. De grens p=0,5 is z=0: een rechte lijn bij twee features. De naam regressie verwijst naar het passen van parameters; het doel is hier classificatie.
Minimaliseer de gemiddelde binaire kruis-entropie. Met wordt de afgeleide naar z precies p−y. De gradiënt naar de gewichten is dus het gemiddelde van , plus een gekozen regularisatiestraf. Dit is een concrete verbinding met gradiëntafdaling uit e02.
Een kans en één trainingsstap
- label y=1
- leersnelheid η=0,1; geen regularisatie
- p en nieuwe parameters
- 1z=−1+0,8×1+(−0,6)×(−1)=0,4; p=0,598688.
- 2Afgeleide p−y=−0,401312. Gradiënten voor b,w1,w2: −0,401312; −0,401312; +0,401312.
- 3Aftrekken van η maal gradiënt geeft bnieuw=−0,959869; wnieuw=(0,840131;−0,640131). De positieve waarneming verhoogt zo haar score.
K-dichtstbijzijnde buren gebruikt lokale voorbeelden
KNN bewaart de trainingswaarnemingen. Bereken de afstand tot een nieuwe plant, kies k buren en stem op hun labels. Met k=1 is de grens grillig; een grotere k verlaagt gevoeligheid voor lokale ruis maar kan kleine probleemgroepen overschreeuwen. Kies k op validatie.
Afstand vraagt betekenisvolle schaal. Een vochtpercentage met bereik 0–100 kan een temperatuurverschil van enkele kelvin domineren. Standaardiseer op training of kies een fysisch onderbouwde afstand. KNN extrapoleert niet betrouwbaar ver buiten bekende voorbeelden. Ontbrekende features mogen niet als nul worden toegevoegd. Bewaar ook de burenafstand als aanwijzing voor invoer buiten het domein.
Buren werkelijk tellen
- features al gelijk geschaald
- k=3
- buren en label
- 1Kwadratische afstanden: A=1, B=1, C=2, D=9.
- 2De drie buren zijn A,B,C; twee van drie zijn ziek. De meerderheidsvoorspelling is ziek; de stemmenfractie is 2/3.
- 3Die fractie is geen automatisch gekalibreerde kans. A en B zijn even ver; bij een grens op die afstand is een vaste tie-regel nodig.
Een beslisboom verdeelt met expliciete regels
Een boom kiest splitsingen zoals ΔT>1,5 K en daarna vocht<45%. Een blad bevat een klasse of trainingsfractie. De Gini-onzuiverheid voor een binaire fractie p is : nul bij een zuiver blad, 0,5 bij een helft-helftverdeling.
Een split wordt beoordeeld met de naar omvang gewogen onzuiverheid van de kinderen. Herhaalde greedy splits vinden een bruikbare lokale keuze, geen gegarandeerd globaal beste boom. Een diepe boom kan iedere trainingsplant memoriseren. Beperk diepte en minimumbladomvang en toets die instellingen onafhankelijk. De regels zijn leesbaar, maar worden pas oorzakelijk als het onderzoeksontwerp dat draagt.
| instelling | gevolg |
|---|---|
| grotere diepte | meer lokale details |
| groter minimumblad | minder kleine uitzonderingsregels |
| Gini-criterium | vermindert gemengde labels |
| validatie | kiest bruikbare complexiteit |
Een splitsing narekenen
- binaire labels
- Gini voor en na splitsing
- 1Ouder: 2×0,4×0,6=0,48.
- 2Links: 2×0,8×0,2=0,32; rechts 0. Gewogen resultaat=(5/10)×0,32=0,16.
- 3Afname is 0,48−0,16=0,32. Dat is een verbetering op training; de vijf rechterplanten bewijzen nog geen perfecte regel buiten deze steekproef.
Drie classifiers op hetzelfde plantcontract
De code traint logistieke regressie met gradiëntafdaling, KNN en een echte recursieve beslisboom in NumPy. Stratificatie bewaart de synthetische 20% positieve prevalentie. Eén onafhankelijke waarneming per plant vermijdt herhaalde-plantenlekken. De validatieset kiest alleen de logistieke actiedrempel bij kosten vijf voor missen en één voor vals alarm; de eindtest blijft apart.
import numpy as nprng=np.random.default_rng(42)# Eén onafhankelijke modelwaarneming per plant; geen echte ziektegegevens.y=np.r_[np.zeros(360,dtype=int),np.ones(90,dtype=int)]deltaT=rng.normal(0.0+2.0*y,1.3,len(y))vocht=np.clip(rng.normal(55-12*y,10,len(y)),5,95)X=np.column_stack((deltaT,vocht))delen=[[],[],[]]for c in (0,1): ids=rng.permutation(np.flatnonzero(y==c)) for j,a in enumerate(np.split(ids,[int(.6*len(ids)),int(.8*len(ids))])): delen[j].extend(a)tr,va,te=[np.array(a) for a in delen]gem=X[tr].mean(axis=0); sd=X[tr].std(axis=0)Z=np.column_stack((np.ones(len(X)),(X-gem)/sd))def sigmoid(a): return 1/(1+np.exp(-np.clip(a,-40,40)))b=np.zeros(3)for stap in range(3000): p=sigmoid(Z[tr]@b) grad=Z[tr].T@(p-y[tr])/len(tr)+.01*np.r_[0,b[1:]] b-=.1*gradp=sigmoid(Z@b)rooster=np.arange(.05,.96,.05)def kosten(ids,q,drempel): pred=q>=drempel return int(5*np.sum((y[ids]==1)&~pred)+np.sum((y[ids]==0)&pred))drempel=min(rooster,key=lambda t:kosten(va,p[va],t))def maat(waar,pred): tn=np.sum((waar==0)&(pred==0)); fp=np.sum((waar==0)&(pred==1)) fn=np.sum((waar==1)&(pred==0)); tp=np.sum((waar==1)&(pred==1)) return [int(tn),int(fp),int(fn),int(tp)], tp/max(tp+fp,1),tp/max(tp+fn,1)print("drempel validatie",round(drempel,2),"kosten",kosten(va,p[va],drempel))for naam,q in (("logistiek0.5",p[te]>=.5),("logistiekgekozen",p[te]>=drempel)): cm,pr,re=maat(y[te],q) print(naam,"TN FP FN TP",cm,"precisie",round(pr,3),"recall",round(re,3))# KNN: afstanden in de met training gestandaardiseerde ruimte.afstand=((Z[te,None,1:]-Z[None,tr,1:])**2).sum(axis=2)ids=np.argsort(afstand,axis=1)[:,:7]knn=(y[tr][ids].mean(axis=1)>=.5)print("KNN7",maat(y[te],knn))# Kleine echte beslisboom: gewogen Gini, diepte3, minimaal12 per blad.def gini(a): q=a.mean(); return 2*q*(1-q)def boom(ids,diepte=0): blad=float(y[ids].mean()) if diepte==3 or len(ids)<24 or blad in (0.,1.): return blad beste=(len(ids)*gini(y[ids]),None,None,None,None) for j in range(2): v=np.unique(X[ids,j]); grenzen=(v[:-1]+v[1:])/2 for t in grenzen: links=ids[X[ids,j]<=t]; rechts=ids[X[ids,j]>t] if min(len(links),len(rechts))<12: continue verlies=len(links)*gini(y[links])+len(rechts)*gini(y[rechts]) if verlies<beste[0]: beste=(verlies,j,t,links,rechts) if beste[1] is None: return blad _,j,t,l,r=beste return (j,float(t),boom(l,diepte+1),boom(r,diepte+1))def boomkans(knoop,x): if isinstance(knoop,float): return knoop j,t,l,r=knoop; return boomkans(l if x[j]<=t else r,x)tree=boom(tr)pt=np.array([boomkans(tree,x) for x in X[te]])print("boom3",maat(y[te],pt>=.5))print("boomwortel feature/grens",tree[:2])print("meerderheidsbasislijn accuracy",round(np.mean(y[te]==0),3))assert len(set(tr)&set(te))==0drempel validatie 0.25 kosten 21 logistiek0.5 TN FP FN TP [68, 4, 9, 9] precisie 0.692 recall 0.5 logistiekgekozen TN FP FN TP [58, 14, 5, 13] precisie 0.481 recall 0.722 KNN7 ([64, 8, 8, 10], np.float64(0.5555555555555556), np.float64(0.5555555555555556)) boom3 ([66, 6, 9, 9], np.float64(0.6), np.float64(0.5)) boomwortel feature/grens (0, 1.4779002283226077) meerderheidsbasislijn accuracy 0.8
De werkelijke testuitvoer beoordelen
- 90 testplanten: 18 positief, 72 negatief
- gekozen matrix TN=58, FP=14, FN=5, TP=13
- foutkosten en inzetbesluit
- 1De gekozen drempel geeft precisie 13/27=48,15% en recall 13/18=72,22%. Foutkosten=5×5+14=39.
- 2Bij drempel 0,5 waren FN=9 en FP=4: kosten=49. Altijd gezond kost 5×18=90. De gekozen actie helpt in dit voorbeeld, terwijl accuracy 71/90=78,89% lager is dan de 80%-meerderheidsbasislijn.
- 3Een voorstel voor 90% recall wordt nog niet gehaald. Met slechts 18 positieve testplanten is de onzekerheid bovendien aanzienlijk. Gebruik de lesuitkomst niet als bewezen kasdiagnose.
Confusiematrix legt de foutsoorten bloot
Kies ‘ziek’ als positieve klasse. TP is een juist ziektevoorstel, FP een onterechte inspectie, FN een gemiste zieke plant en TN een correct gezond voorstel. Zet vooraf vast of rijen de waarheid of de voorspelling voorstellen. De code print TN,FP,FN,TP expliciet om verwisseling te voorkomen.
Precisie=TP/(TP+FP) beantwoordt hoeveel meldingen terecht zijn. Recall=TP/(TP+FN) vertelt hoeveel zieken gevonden zijn. Accuracy=(TP+TN)/N kan goed lijken wanneer ziekte zeldzaam is. Geef aantallen naast percentages: recall 100% op één zieke plant is weinig bewijs. Bij geen positieve voorspellingen is precisie niet informatief; documenteer hoe je die lege noemer rapporteert.
| werkelijkheid / voorspelling | gezond | ziek |
|---|---|---|
| gezond | TN | FP |
| ziek | FN | TP |
Een goede accuracy kan veel missen
- positief is ziek
- matrix, accuracy, precisie, recall en F1
- 1TP=6, FP=2, FN=4, TN=88.
- 2Accuracy=94/100=94%; precisie=6/8=75%; recall=6/10=60%.
- 3F1=2TP/(2TP+FP+FN)=12/18=66,67%. Een altijd-gezondbasislijn heeft al 90% accuracy, maar recall nul.
Drempels volgen foutkosten en kalibratie
Een lagere drempel meldt meer planten: doorgaans stijgt recall en daalt precisie. Kies de drempel op validatie vanuit inspectiecapaciteit en foutkosten. Als p een goed gekalibreerde ziekte-kans is, kost inspecteren en missen . Inspecteren wint als . Bij kosten 1 en 5 is dat 1/6.
Deze afleiding veronderstelt die kosten en kalibratie. De code kiest empirisch een grens uit een rooster, zodat dezelfde vereenvoudigingen niet als gemeten klinische of biologische waarheid worden gepresenteerd. Controleer op test de gekozen actie, scorekalibratie en prestaties per batch of omgeving. Een modelscore is een voorstel aan de inspecteur.
Onevenwichtige data veranderen wat je ziet
Bij 1% positieve planten kan een model dat nooit ziek zegt 99% accuracy bereiken. Rapporteer daarom prevalentie, recall, precisie, foutaantallen en een precision-recallcurve over drempels. Vergelijk prestaties op dezelfde beoogde populatie; precisie verandert wanneer ziekte zeldzamer wordt.
Klassegewichten en oversampling kunnen training helpen, maar veranderen de verliesfunctie of trainingsverdeling. Een gewogen logistieke score is niet vanzelf de oorspronkelijke populatiekans. Corrigeer of kalibreer op representatieve onafhankelijke data. Resampling gebeurt uitsluitend binnen training. Houd alle herhalingen van een plant en synthetische afgeleiden in dezelfde fold. Maak het testdeel representatief voor de echte inzet.
| ingreep | controle |
|---|---|
| klassegewichten | kosten en scorekalibratie |
| oversampling | geen kopieën in test |
| stratificatie | voldoende positieve voorbeelden |
| groep-split | nieuwe planten echt nieuw |
Een gewogen score is een andere kans
- populatiekans p
- ideaal passende gewogen score q=9p/(9p+1−p)
- de oorspronkelijke kans bij q=0,75
- 1Herschikken van q=9p/(1+8p) geeft p=q/(9−8q).
- 2Bij q=0,75 is p=0,75/(9−6)=0,25. Een gewogen score van 75% kan dus overeenkomen met 25% onder het oorspronkelijke populatiemodel.
- 3Deze rekenrelatie veronderstelt een ideaal passend model en precies die gewichten. Controleer werkelijke scorekalibratie op representatieve onafhankelijke data.
Clustering vraagt geen ziektelabels
K-means zoekt k centra en minimaliseert de som van kwadratische afstanden: . Wissel twee stappen af: wijs ieder punt toe aan het dichtste centrum; vervang ieder centrum door het gemiddelde van zijn toegewezen punten. Het gemiddelde minimaliseert de kwadratensom bij vaste toewijzing.
Elke gewone stap verhoogt J niet, maar de oplossing kan een lokaal minimum zijn. Begin meerdere keren en behoud de laagste J. Lege clusters vragen een expliciete herstartregel. Schaal kenmerken voordat je afstand vergelijkt. Groepen met verschillende dichtheid, langgerekte vorm of uitschieters passen niet altijd bij deze geometrie.
K-means met meerdere starts
De ongeëtiketteerde dataset bevat 210 geschaalde modelwaarnemingen. Acht starts beperken toevallige slechte initialisatie. De code toont centra, omvang, iteraties en J voor verschillende k. Er wordt geen ziektelabel gebruikt. Een lagere J bij meer centra is op zichzelf geen bewijs voor het juiste aantal biologische groepen.
import numpy as nprng=np.random.default_rng(420)# Ongeëtiketteerde, gestandaardiseerde modelsamples.X=np.vstack([rng.normal(c,.45,(70,2)) for c in [(-1,-1),(1,0),(0,1.5)]])def kmeans(X,k,seed): r=np.random.default_rng(seed) c=X[r.choice(len(X),k,replace=False)].copy() for it in range(100): lab=((X[:,None,:]-c[None,:,:])**2).sum(axis=2).argmin(axis=1) nieuw=np.array([X[lab==j].mean(axis=0) if np.any(lab==j) else X[r.integers(len(X))] for j in range(k)]) if np.max(np.abs(nieuw-c))<1e-8: c=nieuw; break c=nieuw lab=((X[:,None,:]-c[None,:,:])**2).sum(axis=2).argmin(axis=1) sse=np.sum((X-c[lab])**2) return sse,c,lab,it+1sse,c,lab,it=min([kmeans(X,3,s) for s in range(8)],key=lambda v:v[0])print("k3 SSE",round(sse,3),"iteraties",it)print("clusteromvang",np.bincount(lab).tolist())print("centra",np.round(c,3))print("SSE per k",[(k,round(min(kmeans(X,k,s)[0] for s in range(8)),2)) for k in (1,2,3,4)])k3 SSE 75.519 iteraties 6 clusteromvang [69, 70, 71] centra [[ 1.037 0.022] [-0.998 -0.974] [ 0.046 1.53 ]] SSE per k [(1, np.float64(443.84)), (2, np.float64(189.5)), (3, np.float64(75.52)), (4, np.float64(65.22))]
Een cluster krijgt betekenis door een nieuwe vraag
Een operator kan clusters gebruiken om representatieve planten voor inspectie te selecteren. Vergelijk per cluster licht, batch, cultivar en referentiediagnose. Een cluster kan een hoek van de kas of sensormontage herkennen in plaats van ziekte. Veranderde clusteromvang kan daarom een meetprobleem of veranderde omgeving zijn.
De primaire algoritmedocumentatie staat bij logistische regressie, beslisbomen en k-means van scikit-learn, geraadpleegd 30-09-2026. De lescode gebruikt NumPy zodat de update en splitsing zichtbaar blijven. Clusters worden pas als probleemgroepen benoemd na externe beoordeling.
De evaluatie op één blad
| symbool | betekenis | eenheid |
|---|---|---|
| juist positieve voorspellingen | — | |
| vals positief | — | |
| vals negatief | — |
| methode | vraagt |
|---|---|
| logistiek | lineaire score, goede features en kalibratie |
| KNN | schaal en representatieve buren |
| boom | complexiteitsgrens en onafhankelijke test |
| k-means | k, afstand en externe interpretatie |
Van plantdata naar begrensde inspectie
Een classifier brengt referentielabels, statistiek en beheer samen. Een cluster helpt zoeken. Het station bewaart waarom een plant is voorgesteld en wie het voorstel bevestigde.
Onzekerheid in sensorkenmerken.
Eerlijke splits en preprocessing.
Meer flexibele beslisgrenzen.
Bevoegdheid en bias.
Een toetsbaar inspectiecontract.
Habitat start met een inspectievoorstel. Datasetlabels, batch-split, drempel en foutkosten worden vastgelegd. De getoonde vocht- en bladkenmerken zijn lesvoorbeelden en voegen geen nieuwe sensor aan de vaste stationsconfiguratie toe.
In het kort
- Labels vragen een onafhankelijke referentie en een duidelijk tijdstip.
- Logistiek, KNN en bomen leren verschillende beslisgrenzen.
- Clustering vindt overeenkomst zonder automatisch een diagnose te geven.
- Precisie en recall beoordelen verschillende foutkosten.
- Onbalans, kalibratie en groepslekken horen bij de evaluatie.
Wat je nu kent
- classificatie
- Een waarneming aan een vooraf gedefinieerd label toewijzen.
- clustering
- Groepen zoeken op overeenkomst zonder de doelgroepen als labels aan te leveren.
- precisie
- Aandeel werkelijk positieve gevallen onder positieve voorspellingen.
- recall
- Aandeel gevonden positieve gevallen onder alle werkelijk positieve gevallen.
- actiedrempel
- Scoregrens waarboven een vooraf afgesproken handeling volgt.