Faza 02 · lecția 17
Gestionarea datelor dezechilibrate
Scopul lecției: Tip: Construire Limbaj: Python Cerințe preliminare: Faza 2, lecțiile 01–09 — în special metricile de evaluare Durată: ~90 de minute
Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.
Cuprinsul lecției
- Obiective de învățare
- Problema
- Conceptul
- De ce eșuează acuratețea
- Metrici mai bune
- Pipeline-ul pentru date dezechilibrate
- SMOTE: tehnica supraeșantionării sintetice a clasei minoritare
- Compararea strategiilor de eșantionare
- Ponderile claselor
- Reglarea pragului
- Învățarea sensibilă la costuri
- Diagrama decizională
- Construiți
- Pasul 1: generați un set de date dezechilibrat
- Pasul 2: SMOTE de la zero
- Pasul 3: supraeșantionare și subeșantionare aleatoare
- Pasul 4: regresie logistică cu ponderi de clasă
- Pasul 5: reglarea pragului
- Pasul 6: funcțiile de evaluare
- Pasul 7: comparați toate abordările
- Folosiți
- Livrați
- Exerciții
- Termeni-cheie
- Lecturi suplimentare
Când 99% dintre date sunt „normale”, acuratețea este o minciună.
Tip: Construire Limbaj: Python Cerințe preliminare: Faza 2, lecțiile 01–09 — în special metricile de evaluare Durată: ~90 de minute
Obiective de învățare
- Să implementați SMOTE de la zero și să explicați cum diferă supraeșantionarea sintetică de duplicarea aleatoare.
- Să evaluați clasificatoarele pe date dezechilibrate folosind F1, AUPRC și coeficientul de corelație Matthews în locul acurateții.
- Să comparați ponderarea claselor, reglarea pragului și strategiile de reeșantionare și să selectați abordarea potrivită pentru un anumit raport de dezechilibru.
- Să construiți și să evaluați fluxuri pentru date dezechilibrate care folosesc SMOTE, ponderi ale claselor și reglarea pragului, separat sau în combinații validate.
Problema
Construiți un model de detectare a fraudelor. Obține o acuratețe de 99,9%. Sărbătoriți. Apoi vă dați seama că prezice „nu este fraudă” pentru fiecare tranzacție.
Aceasta nu este o eroare. Este lucrul rațional de făcut când numai 0,1% dintre tranzacții sunt frauduloase. Modelul învață că prezicerea constantă a clasei majoritare minimizează eroarea totală. Este corect din punct de vedere tehnic și complet inutil.
Acest lucru se întâmplă oriunde clasificarea reală contează. Diagnosticarea bolilor: rată pozitivă de 1%. Intruziuni în rețea: 0,01% atacuri. Defecte de fabricație: 0,5% produse defecte. Filtrarea spamului: 20% spam. Predicția abandonului: 5% clienți care abandonează. Cu cât clasa minoritară are consecințe mai importante, cu atât tinde să fie mai rară.
Acuratețea eșuează deoarece tratează toate predicțiile corecte în mod egal. Etichetarea corectă a unei tranzacții legitime și detectarea corectă a fraudei valorează fiecare câte un punct de acuratețe. Însă detectarea fraudei este întregul motiv pentru care există modelul. Avem nevoie de metrici, tehnici și strategii de antrenare care să oblige modelul să acorde atenție clasei rare, dar importante.
Notă tehnică a traducerii: Acuratețea nu este intrinsec falsă; este insuficientă atunci când costurile erorilor și prevalența claselor fac ca rezultatul agregat să ascundă performanța clasei relevante. Nici predicția constantă a clasei majoritare nu este inevitabil „rațională” pentru orice algoritm: depinde de obiectiv, ponderi, regularizare și pragul de decizie.
Conceptul
De ce eșuează acuratețea
Luați un set cu 1.000 de eșantioane: 990 negative și 10 pozitive. Un model care prezice întotdeauna clasa negativă produce:
| Pozitiv prezis | Negativ prezis | |
|---|---|---|
| Pozitiv în realitate | 0 (TP) | 10 (FN) |
| Negativ în realitate | 0 (FP) | 990 (TN) |
Acuratețe = (0 + 990) / 1.000 = 99,0%
Modelul nu detectează nicio fraudă. Nicio boală. Niciun defect. Dar acuratețea spune 99%. Din acest motiv, acuratețea este periculoasă în problemele dezechilibrate.
Metrici mai bune
Precizie = TP / (TP + FP). Dintre toate cazurile marcate drept pozitive, câte sunt într-adevăr pozitive? O precizie mare înseamnă puține alarme false.
Recall = TP / (TP + FN). Dintre toate cazurile pozitive în realitate, câte am detectat? Un recall mare înseamnă puține cazuri pozitive ratate.
Scor F1 = 2 * precizie * recall / (precizie + recall). Media armonică. Penalizează dezechilibrul extrem dintre precizie și recall mai mult decât ar face-o media aritmetică.
Scor F-beta = (1 + beta^2) * precizie * recall / (beta^2 * precizie + recall). Când beta > 1, recall-ul contează mai mult. Când beta < 1, precizia contează mai mult. F2 este folosit frecvent în detectarea fraudelor — ratarea fraudei este mai gravă decât o alarmă falsă.
AUPRC — aria de sub curba precizie–recall. Seamănă cu AUC-ROC, dar este mai informativă pentru date dezechilibrate. Un clasificator aleator are AUPRC egală cu rata clasei pozitive, nu cu 0,5 ca ROC. Astfel, îmbunătățirile sunt mai ușor de observat.
Coeficientul de corelație Matthews = (TP * TN - FP * FN) / sqrt((TP+FP)(TP+FN)(TN+FP)(TN+FN)). Variază de la -1 la +1. Oferă un scor mare numai când modelul se descurcă bine pe ambele clase. Rămâne echilibrat chiar și când clasele au dimensiuni foarte diferite.
Pentru modelul care „prezice întotdeauna negativ” de mai sus: precizie = 0/0 — nedefinită, adesea stabilită la 0 —, recall = 0/10 = 0, F1 = 0 și MCC = 0. Aceste metrici identifică în mod corect modelul drept inutil.
Notă tehnică a traducerii: „AUPRC” poate desemna integrarea trapezoidală a curbei sau, în unele contexte, average precision; valorile nu sunt întotdeauna identice. Nivelul de referință bazat pe prevalență este o proprietate a clasificatorului aleator în așteptare, iar alegerea metricii trebuie să urmeze costurile și utilizarea concretă, nu numai dezechilibrul.
Pipeline-ul pentru date dezechilibrate
Notă tehnică a traducerii: Pragurile 80/20, 95/5 și 99/1 sunt euristici, nu reguli statistice. În pipeline-ul afișat, SMOTE reechilibrează mai întâi
y, iarclass_weight="balanced"este calculat ulterior deLogisticRegressionpe etichetele transmise lafit; pentru clase perfect echilibrate, ponderile devin egale, deci nu există automat o compensare dublă. Totuși, alte strategii de reeșantionare sau ponderi particularizate pot schimba costurile efective și trebuie validate pe date cu distribuția reală.
SMOTE: tehnica supraeșantionării sintetice a clasei minoritare
Supraeșantionarea aleatoare duplică eșantioanele minoritare existente. Funcționează, dar riscă supraînvățarea deoarece modelul vede în mod repetat puncte identice.
SMOTE creează eșantioane minoritare sintetice noi, plauzibile, dar care nu sunt copii. Algoritmul:
- Pentru fiecare eșantion minoritar
x, găsiți cei mai apropiațikvecini dintre celelalte eșantioane minoritare. - Alegeți aleatoriu un vecin.
- Creați un eșantion nou pe segmentul dintre
xși vecinul respectiv.
Formula: new_sample = x + random(0, 1) * (neighbor - x)
Aceasta interpolează între puncte minoritare reale și creează eșantioane în aceeași regiune a spațiului caracteristicilor fără a copia pur și simplu datele existente.
Compararea strategiilor de eșantionare
Supraeșantionare aleatoare: duplică eșantioanele minoritare până la egalarea numărului celor majoritare.
- Avantaje: simplă, fără pierdere de informații.
- Dezavantaje: duplicatele exacte provoacă supraînvățare și măresc timpul de antrenare.
Subeșantionare aleatoare: elimină eșantioane majoritare până la egalarea numărului celor minoritare.
- Avantaje: antrenare rapidă, simplă.
- Dezavantaje: aruncă date majoritare potențial utile și produce o varianță mai mare.
SMOTE: creează eșantioane minoritare sintetice prin interpolare.
- Avantaje: generează puncte de date noi și reduce supraînvățarea față de supraeșantionarea aleatoare.
- Dezavantaje: poate crea eșantioane zgomotoase lângă frontiera de decizie și nu ține cont de distribuția clasei majoritare.
| Strategie | Date modificate | Risc | Când să o folosiți |
|---|---|---|---|
| Supraeșantionare | Clasa minoritară este duplicată | Supraînvățare | Seturi mici, dezechilibru moderat |
| Subeșantionare | Clasa majoritară este redusă | Pierdere de informații | Seturi mari, când doriți antrenare rapidă |
| SMOTE | Sunt adăugate date minoritare sintetice | Zgomot la frontieră | Dezechilibru moderat și suficiente exemple minoritare pentru k-NN |
Notă tehnică a traducerii: SMOTE nu reduce universal supraînvățarea și poate mări suprapunerea dintre clase. Distanțele trebuie să fie semnificative, caracteristicile numerice trebuie preprocesate în interiorul pliului de antrenare, iar datele mixte numeric–categoriale cer o variantă precum
SMOTENC. Reeșantionarea trebuie aplicată numai datelor de antrenare, nu înaintea împărțirii sau pe setul de testare.
Ponderile claselor
În loc să schimbați datele, schimbați modul în care modelul tratează erorile. Atribuiți o pondere mai mare clasificării greșite a clasei minoritare.
Pentru o problemă binară cu 950 de eșantioane negative și 50 pozitive:
- Ponderea clasei negative = n_samples / (2 * n_negative) = 1.000 / (2 * 950) = 0,526.
- Ponderea clasei pozitive = n_samples / (2 * n_positive) = 1.000 / (2 * 50) = 10,0.
Clasa pozitivă primește o pondere de 19 ori mai mare. Clasificarea greșită a unui eșantion pozitiv costă cât clasificarea greșită a 19 eșantioane negative. Modelul este obligat să acorde atenție clasei minoritare.
În regresia logistică, aceasta modifică funcția de pierdere:
weighted_loss = -sum(w_i * [y_i * log(p_i) + (1-y_i) * log(1-p_i)])
unde w_i depinde de clasa eșantionului i.
Ponderile claselor sunt echivalente matematic, în așteptare, cu supraeșantionarea, dar fără a crea puncte de date noi. De aceea sunt mai rapide și evită riscul de supraînvățare produs de eșantioanele duplicate.
Notă tehnică a traducerii: Echivalența este limitată la anumite obiective de risc empiric și scheme de ponderare. Regularizarea, normalizarea pierderii, antrenarea mini-batch, optimizarea și algoritmul concret pot face ponderarea diferită de duplicarea observațiilor; nici viteza superioară nu este garantată în orice implementare.
Reglarea pragului
Majoritatea clasificatoarelor emit o probabilitate. Pragul implicit este 0,5: dacă P(pozitiv) >= 0,5, preziceți clasa pozitivă. Însă 0,5 este arbitrar. Când clasele sunt dezechilibrate, pragul optim este de obicei mult mai mic.
Procesul:
- Antrenați un model.
- Obțineți probabilitățile prezise pe setul de validare.
- Parcurgeți praguri de la 0,0 la 1,0.
- Calculați F1 sau metrica aleasă la fiecare prag.
- Alegeți pragul care maximizează metrica.
Un model ar putea emite P(fraudă) = 0,15 pentru o tranzacție frauduloasă. La pragul 0,5, aceasta este clasificată drept nefrauduloasă. La pragul 0,10, este detectată corect. Calibrarea probabilității contează mai puțin decât ordonarea — cât timp fraudele primesc probabilități mai mari decât tranzacțiile legitime, există un prag care le separă.
Notă tehnică a traducerii: Pragul 0,5 are o interpretare decizională pentru probabilități calibrate, clase și costuri simetrice; pragul optim nu este întotdeauna mai mic. Calibrarea rămâne importantă pentru costuri așteptate, comunicarea riscului și stabilitatea pragului. Pragul trebuie reglat exclusiv pe date de validare sau prin validare încrucișată, nu pe setul final de testare.
Învățarea sensibilă la costuri
Aceasta generalizează ponderile claselor. În loc de costuri uniforme, atribuiți costuri specifice clasificărilor greșite:
| Pozitiv prezis | Negativ prezis | |
|---|---|---|
| Pozitiv în realitate | 0 — corect | C_FN = 100 |
| Negativ în realitate | C_FP = 1 | 0 — corect |
Ratarea unei tranzacții frauduloase — FN — costă de 100 de ori mai mult decât o alarmă falsă — FP. Modelul optimizează costul total, nu numărul total de erori.
Aceasta este abordarea cea mai riguroasă când puteți estima costurile din lumea reală. Un diagnostic de cancer ratat are un cost foarte diferit de o alarmă falsă care duce la o biopsie suplimentară. Exprimarea explicită a acestor costuri impune compromisurile potrivite.
Diagrama decizională
Notă tehnică a traducerii: Raporturile și pragurile de 1.000/10.000 de eșantioane din diagramă sunt euristici didactice fără valabilitate universală. Selecția trebuie făcută prin validare stratificată, metrici și costuri adecvate, păstrând distribuția naturală în setul de testare.
class-imbalance
Construiți
Pasul 1: generați un set de date dezechilibrat
import numpy as np
def make_imbalanced_data(n_majority=950, n_minority=50, seed=42):
rng = np.random.RandomState(seed)
X_maj = rng.randn(n_majority, 2) * 1.0 + np.array([0.0, 0.0])
X_min = rng.randn(n_minority, 2) * 0.8 + np.array([2.5, 2.5])
X = np.vstack([X_maj, X_min])
y = np.concatenate([np.zeros(n_majority), np.ones(n_minority)])
shuffle_idx = rng.permutation(len(y))
return X[shuffle_idx], y[shuffle_idx]
Pasul 2: SMOTE de la zero
def euclidean_distance(a, b):
return np.sqrt(np.sum((a - b) ** 2))
def find_k_neighbors(X, idx, k):
distances = []
for i in range(len(X)):
if i == idx:
continue
d = euclidean_distance(X[idx], X[i])
distances.append((i, d))
distances.sort(key=lambda x: x[1])
return [d[0] for d in distances[:k]]
def smote(X_minority, k=5, n_synthetic=100, seed=42):
rng = np.random.RandomState(seed)
n_samples = len(X_minority)
k = min(k, n_samples - 1)
synthetic = []
for _ in range(n_synthetic):
idx = rng.randint(0, n_samples)
neighbors = find_k_neighbors(X_minority, idx, k)
neighbor_idx = neighbors[rng.randint(0, len(neighbors))]
t = rng.random()
new_point = X_minority[idx] + t * (X_minority[neighbor_idx] - X_minority[idx])
synthetic.append(new_point)
return np.array(synthetic)
Notă tehnică a traducerii: Implementarea cere cel puțin două eșantioane minoritare și
k >= 1. Cu zero ori un singur eșantion, lista vecinilor este vidă și apelul aleatoriu eșuează; codul de producție trebuie să valideze explicit dimensiunile șin_synthetic.
Pasul 3: supraeșantionare și subeșantionare aleatoare
def random_oversample(X, y, seed=42):
rng = np.random.RandomState(seed)
classes, counts = np.unique(y, return_counts=True)
max_count = counts.max()
X_resampled = list(X)
y_resampled = list(y)
for cls, count in zip(classes, counts):
if count < max_count:
cls_indices = np.where(y == cls)[0]
n_needed = max_count - count
chosen = rng.choice(cls_indices, size=n_needed, replace=True)
X_resampled.extend(X[chosen])
y_resampled.extend(y[chosen])
X_out = np.array(X_resampled)
y_out = np.array(y_resampled)
shuffle = rng.permutation(len(y_out))
return X_out[shuffle], y_out[shuffle]
def random_undersample(X, y, seed=42):
rng = np.random.RandomState(seed)
classes, counts = np.unique(y, return_counts=True)
min_count = counts.min()
X_resampled = []
y_resampled = []
for cls in classes:
cls_indices = np.where(y == cls)[0]
chosen = rng.choice(cls_indices, size=min_count, replace=False)
X_resampled.extend(X[chosen])
y_resampled.extend(y[chosen])
X_out = np.array(X_resampled)
y_out = np.array(y_resampled)
shuffle = rng.permutation(len(y_out))
return X_out[shuffle], y_out[shuffle]
Pasul 4: regresie logistică cu ponderi de clasă
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
def logistic_regression_weighted(X, y, weights, lr=0.01, epochs=200):
n_samples, n_features = X.shape
w = np.zeros(n_features)
b = 0.0
for _ in range(epochs):
z = X @ w + b
pred = sigmoid(z)
error = pred - y
weighted_error = error * weights
gradient_w = (X.T @ weighted_error) / n_samples
gradient_b = np.mean(weighted_error)
w -= lr * gradient_w
b -= lr * gradient_b
return w, b
def compute_class_weights(y):
classes, counts = np.unique(y, return_counts=True)
n_samples = len(y)
n_classes = len(classes)
weight_map = {}
for cls, count in zip(classes, counts):
weight_map[cls] = n_samples / (n_classes * count)
return np.array([weight_map[yi] for yi in y])
Pasul 5: reglarea pragului
def find_optimal_threshold(y_true, y_probs, metric="f1"):
best_threshold = 0.5
best_score = -1.0
for threshold in np.arange(0.05, 0.96, 0.01):
y_pred = (y_probs >= threshold).astype(int)
tp = np.sum((y_pred == 1) & (y_true == 1))
fp = np.sum((y_pred == 1) & (y_true == 0))
fn = np.sum((y_pred == 0) & (y_true == 1))
if metric == "f1":
precision = tp / (tp + fp) if (tp + fp) > 0 else 0.0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0.0
score = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0
elif metric == "recall":
score = tp / (tp + fn) if (tp + fn) > 0 else 0.0
elif metric == "precision":
score = tp / (tp + fp) if (tp + fp) > 0 else 0.0
if score > best_score:
best_score = score
best_threshold = threshold
return best_threshold, best_score
Pasul 6: funcțiile de evaluare
def confusion_matrix_values(y_true, y_pred):
tp = np.sum((y_pred == 1) & (y_true == 1))
tn = np.sum((y_pred == 0) & (y_true == 0))
fp = np.sum((y_pred == 1) & (y_true == 0))
fn = np.sum((y_pred == 0) & (y_true == 1))
return tp, tn, fp, fn
def compute_metrics(y_true, y_pred):
tp, tn, fp, fn = confusion_matrix_values(y_true, y_pred)
accuracy = (tp + tn) / (tp + tn + fp + fn)
precision = tp / (tp + fp) if (tp + fp) > 0 else 0.0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0.0
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0
denom = np.sqrt(float((tp + fp) * (tp + fn) * (tn + fp) * (tn + fn)))
mcc = (tp * tn - fp * fn) / denom if denom > 0 else 0.0
return {
"accuracy": accuracy,
"precision": precision,
"recall": recall,
"f1": f1,
"mcc": mcc,
}
Pasul 7: comparați toate abordările
X, y = make_imbalanced_data(950, 50, seed=42)
split = int(0.8 * len(y))
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]
# Baseline: no treatment
w_base, b_base = logistic_regression_weighted(
X_train, y_train, np.ones(len(y_train)), lr=0.1, epochs=300
)
probs_base = sigmoid(X_test @ w_base + b_base)
preds_base = (probs_base >= 0.5).astype(int)
# Oversampled
X_over, y_over = random_oversample(X_train, y_train)
w_over, b_over = logistic_regression_weighted(
X_over, y_over, np.ones(len(y_over)), lr=0.1, epochs=300
)
preds_over = (sigmoid(X_test @ w_over + b_over) >= 0.5).astype(int)
# SMOTE
minority_mask = y_train == 1
X_minority = X_train[minority_mask]
synthetic = smote(X_minority, k=5, n_synthetic=len(y_train) - 2 * int(minority_mask.sum()))
X_smote = np.vstack([X_train, synthetic])
y_smote = np.concatenate([y_train, np.ones(len(synthetic))])
w_sm, b_sm = logistic_regression_weighted(
X_smote, y_smote, np.ones(len(y_smote)), lr=0.1, epochs=300
)
preds_smote = (sigmoid(X_test @ w_sm + b_sm) >= 0.5).astype(int)
# Class weights
sample_weights = compute_class_weights(y_train)
w_cw, b_cw = logistic_regression_weighted(
X_train, y_train, sample_weights, lr=0.1, epochs=300
)
probs_cw = sigmoid(X_test @ w_cw + b_cw)
preds_cw = (probs_cw >= 0.5).astype(int)
# Threshold tuning (tune on held-out validation set, not test set)
probs_val = sigmoid(X_val @ w_cw + b_cw)
best_thresh, best_f1 = find_optimal_threshold(y_val, probs_val, metric="f1")
preds_thresh = (probs_cw >= best_thresh).astype(int)
Fișierul de cod rulează toate acestea într-un singur script și afișează rezultatele.
Notă tehnică a traducerii: Fragmentul de mai sus nu rulează independent deoarece
X_valșiy_valnu sunt definite. Trebuie creat un set de validare separat din datele de antrenare, înaintea reglării pragului; setul de testare trebuie păstrat exclusiv pentru evaluarea finală. În fișierul complet, pragul este ajustat pe probabilitățile modeluluiw_val, dar apoi aplicat probabilităților altei versiuni,w_cw; folosiți pentru reglare și predicție aceeași versiune a modelului, deoarece scările probabilităților nu sunt garantat identice.
Folosiți
Cu scikit-learn și imbalanced-learn, aceste tehnici se exprimă într-o singură linie:
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, f1_score
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
from imblearn.pipeline import Pipeline
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y)
model_weighted = LogisticRegression(class_weight="balanced")
model_weighted.fit(X_train, y_train)
print(classification_report(y_test, model_weighted.predict(X_test)))
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)
model_smote = LogisticRegression()
model_smote.fit(X_resampled, y_resampled)
print(classification_report(y_test, model_smote.predict(X_test)))
pipeline = Pipeline([
("smote", SMOTE()),
("model", LogisticRegression(class_weight="balanced")),
])
pipeline.fit(X_train, y_train)
print(classification_report(y_test, pipeline.predict(X_test)))
Implementările de la zero arată exact ce face fiecare tehnică. SMOTE este pur și simplu interpolare k-NN pe clasa minoritară. Ponderile claselor multiplică pierderea. Reglarea pragului este o buclă peste valori de separare. Nu există magie.
Notă tehnică a traducerii: În validarea încrucișată, folosiți
imblearn.pipeline.Pipelineastfel încât SMOTE să fie ajustat numai în interiorul fiecărui pliu de antrenare. Fragmentul nu fixeazărandom_statepentru împărțire și pentru SMOTE din pipeline, deci rezultatele pot varia. În combinația afișată,class_weight="balanced"este calculat după reeșantionare și nu aplică automat o a doua ponderare a frecvențelor; combinația nu trebuie totuși presupusă superioară fără comparație controlată.
Livrați
Această lecție produce:
outputs/skill-imbalanced-data.md— o listă decizională pentru gestionarea problemelor de clasificare dezechilibrată.
Exerciții
-
Borderline-SMOTE: modificați implementarea SMOTE astfel încât să genereze eșantioane sintetice numai pentru punctele minoritare aflate aproape de frontiera de decizie — cele ai căror
kvecini cei mai apropiați includ eșantioane din clasa majoritară. Comparați rezultatele cu SMOTE standard pe un set în care clasele se suprapun. -
Optimizarea matricei de costuri: implementați învățarea sensibilă la costuri în care matricea costurilor este un parametru. Creați o funcție care primește o matrice de costuri și întoarce predicțiile optime ce minimizează costul așteptat. Testați rapoarte diferite — 1:10, 1:100 și 1:1000 — și reprezentați cum se schimbă compromisul precizie–recall.
-
Calibrarea pragului: implementați scalarea Platt — ajustați o regresie logistică pe ieșirile brute ale modelului pentru a produce probabilități calibrate. Comparați curba precizie–recall înainte și după calibrare. Arătați că aceasta nu schimbă ordonarea — AUC rămâne aceeași —, dar face probabilitățile mai semnificative.
-
Ansamblu cu bagging echilibrat: antrenați mai multe modele, fiecare pe un eșantion bootstrap echilibrat — toate exemplele minoritare plus un subset aleatoriu din clasa majoritară. Faceți media predicțiilor. Comparați abordarea cu un singur model antrenat cu SMOTE. Măsurați atât performanța, cât și varianța între rulări.
-
Experiment privind raportul de dezechilibru: luați un set echilibrat și creșteți treptat dezechilibrul — 50/50, 70/30, 90/10, 95/5 și 99/1. Pentru fiecare raport, antrenați cu și fără SMOTE. Reprezentați F1 în funcție de raport pentru ambele abordări. De la ce raport începe SMOTE să producă o diferență semnificativă?
Notă tehnică a traducerii: Calibrarea trebuie ajustată pe date separate sau prin validare încrucișată. O transformare sigmoidă strict monotonă păstrează ordonarea și ROC-AUC; alte calibrări, precum cea izotonică, pot introduce egalități și pot modifica metricile de rang.
Termeni-cheie
| Termen | Ce spun oamenii | Ce înseamnă de fapt |
|---|---|---|
| Dezechilibrul claselor | „O clasă are mult mai multe eșantioane” | Distribuția claselor din set este puternic asimetrică, ceea ce face modelele să favorizeze clasa majoritară |
| SMOTE | „Supraeșantionare sintetică” | Creează eșantioane minoritare noi prin interpolare între eșantioane minoritare existente și cei mai apropiați k vecini minoritari ai lor |
| Ponderi de clasă | „Erorile pe clase rare devin mai scumpe” | Multiplicarea funcției de pierdere cu ponderi specifice claselor, astfel încât modelul să penalizeze mai sever clasificarea greșită a clasei minoritare |
| Reglarea pragului | „Deplasarea frontierei de decizie” | Schimbarea valorii-limită a probabilității de la 0,5 la o valoare care optimizează metrica dorită |
| Compromis precizie–recall | „Nu le puteți avea pe amândouă” | Coborârea pragului detectează mai multe pozitive — recall mai mare —, dar semnalează și mai multe alarme false — precizie mai mică — și invers |
| AUPRC | „Aria de sub curba PR” | Rezumă curba precizie–recall într-un singur număr; este mai informativă decât AUC-ROC când clasele sunt puternic dezechilibrate |
| Coeficientul de corelație Matthews | „Metrica echilibrată” | O corelație între etichetele prezise și cele reale, care produce un scor mare numai când modelul se descurcă bine pe ambele clase |
| Învățare sensibilă la costuri | „Greșelile diferite au costuri diferite” | Încorporarea costurilor reale ale clasificării greșite în obiectivul de antrenare, astfel încât modelul să optimizeze costul total, nu numărul erorilor |
| Supraeșantionare aleatoare | „Duplicați clasa minoritară” | Repetarea eșantioanelor clasei minoritare pentru echilibrarea numărului de exemple; este simplă, dar riscă supraînvățarea punctelor duplicate |
Lecturi suplimentare
- „SMOTE: Synthetic Minority Over-sampling Technique”, Chawla și colaboratorii (2002) — lucrarea SMOTE originală, încă una dintre cele mai citate despre învățarea din date dezechilibrate.
- „Learning from Imbalanced Data”, He și Garcia (2009) — sinteză amplă despre eșantionare, metode sensibile la costuri și abordări algoritmice.
- Documentația imbalanced-learn — bibliotecă Python cu variante SMOTE, strategii de subeșantionare și integrare în pipeline-uri.
- „The Precision-Recall Plot Is More Informative than the ROC Plot”, Saito și Rehmsmeier (2015) — când și de ce sunt preferabile curbele PR celor ROC în probleme dezechilibrate.
Sursă: Originalul în limba engleză
Navigare: ← Lecția 02.16 — Detectarea anomaliilor · Faza 2 — Bazele învățării automate · Catalog complet · în continuare: 02.18 — Selecția caracteristicilor