๐Ÿ”ฎ Sesi 09 dari 16 โ€” QA Cerdas Dimulai!

Prediksi Defect dengan Machine Learning

Membangun model yang mampu memprediksi modul mana yang paling berisiko memiliki bug โ€” sebelum pengujian dilakukan. Fokuskan energi QA di tempat yang paling dibutuhkan.

๐Ÿ“Š NASA MDP & PROMISE Dataset
๐ŸŒฒ Random Forest & XGBoost
โš–๏ธ SMOTE Imbalanced Handling
โฑ๏ธ 3 ร— 50 menit
๐ŸŽฏ Bagian 1 โ€” Mengapa Prediksi Defect Penting?

Triage Sebelum Pengujian Dimulai

๐Ÿ’ก ANALOGI โ€” Dokter Spesialis vs Dokter Umum

Tanpa prediksi defect: Tim QA menguji semua modul dengan intensitas yang sama โ€” seperti dokter umum yang memeriksa setiap pasien dari ujung kepala hingga ujung kaki tanpa mempertimbangkan risiko.

Dengan prediksi defect: Model ML mengidentifikasi modul mana yang paling "berisiko" berdasarkan karakteristik kodenya. Tim QA bisa berlaku seperti dokter spesialis โ€” memberikan perhatian ekstra pada modul berisiko tinggi, dan pengujian ringan pada modul stabil.

Hasilnya: Dengan waktu dan sumber daya yang sama, tim QA menemukan jauh lebih banyak bug yang signifikan.

๐Ÿ“ˆ Dampak Nyata Prediksi Defect

2ร—
lebih banyak bug ditemukan per jam testing dengan model prediksi (Google Engineering study)
20%
modul kode biasanya mengandung 80% dari semua bug โ€” Prinsip Pareto berlaku di sini
60%
penghematan waktu testing pada proyek yang menggunakan risk-based prioritization dengan ML
๐Ÿ“ฆ Bagian 2 โ€” Dataset untuk Software Defect Prediction

Belajar dari Data Historis NASA & Open Source

๐Ÿ’ก ANALOGI โ€” Data Satelit NASA

NASA mengumpulkan data dari proyek-proyek software luar angkasa mereka selama puluhan tahun โ€” setiap modul kode dicatat metriknya (kompleksitas, ukuran, riwayat perubahan) beserta apakah akhirnya ditemukan bug atau tidak. Data ini menjadi "dataset emas" untuk melatih model prediksi defect. Seperti belajar dari rekaman penerbangan pesawat untuk memprediksi mana yang akan bermasalah.

๐Ÿš€
NASA MDP
NASA Metrics Data Program
Dataset klasik dari proyek software NASA. Berisi metrik kode (Halstead, McCabe) per modul + label buggy/clean. 13 dataset berbeda.
NASA
๐Ÿ›๏ธ
PROMISE Repository
promisedata.org
Repositori riset SE โ€” berisi puluhan dataset defect prediction dari berbagai proyek open source. Standar benchmark penelitian.
Public
๐Ÿ›
Bugzilla Datasets
Mozilla, Eclipse, etc.
Bug report historis dari proyek besar open source. Digunakan untuk NLP-based prediction dan duplicate detection.
Public
๐Ÿ™
GitHub Metrics
GHTorrent, GitHub API
Data dari ribuan repository GitHub: commit frequency, contributor count, issue/PR history โ€” fitur proses yang sangat prediktif.
Public
๐Ÿ”ง Bagian 3 โ€” Feature Engineering untuk Prediksi Defect

Mengubah Kode Menjadi Angka yang Bisa Dipelajari ML

๐Ÿ’ก ANALOGI โ€” Kartu Rapor Modul Kode

Seperti kartu rapor siswa yang mencatat nilai matematika, bahasa, olahraga, kehadiran, dll โ€” model ML membutuhkan "kartu rapor" numerik untuk setiap modul kode. Feature engineering adalah proses membuat kartu rapor ini dari kode sumber. Semakin relevan fiturnya, semakin akurat prediksi "siswa mana yang mungkin tidak lulus ujian (punya bug)".

KategoriFiturCara EkstraksiRelevansi
Metrik Kode Statis
Static
Cyclomatic Complexity (CC), Lines of Code (LOC), Number of Methods, Depth of Inheritance Tools: pylint, radon (Python), SonarQube, Understand CC tinggi โ†’ banyak cabang โ†’ lebih susah ditest โ†’ lebih rawan bug
Metrik Halstead
Static
Program Vocabulary, Volume, Difficulty, Effort Dihitung dari jumlah operator/operand unik dalam kode Halstead Difficulty tinggi โ†’ kode sulit dipahami โ†’ lebih rawan kesalahan
Metrik Proses
Process
Number of commits, Number of authors, Age of code, Lines changed per commit Git history: git log, git blame, git shortstat Banyak author + frequent change โ†’ higher defect density
Metrik Riwayat
History
Bug count sebelumnya, Reopened bug count, Fix time average Jira/GitHub Issues API, bug tracker export "Bug masa lalu adalah prediktor terbaik bug masa depan"
Object-Oriented
Static
CK Metrics: WMC, DIT, NOC, CBO, RFC, LCOM Tools: ckjm (Java), pymetrics (Python) CBO (Coupling) tinggi โ†’ banyak dependensi โ†’ bug di satu class merembet

Aturan emas feature engineering: "Process metrics" (riwayat commit, jumlah author) biasanya lebih prediktif daripada "code metrics" semata. Kombinasi keduanya memberikan hasil terbaik.

๐Ÿค– Bagian 4 โ€” Model ML untuk Klasifikasi Defect

Pilih Model yang Tepat untuk Tugas yang Tepat

๐Ÿ“ˆ
Logistic Regression
Baseline sederhana. Memprediksi probabilitas bug (0โ€“1). Mudah diinterpretasikan.
โœ“ Interpretable, cepat, baseline yang baik
โœ— Kurang akurat untuk data non-linear
๐ŸŒฟ
Decision Tree
Aturan "jika CC > 10 DAN author > 5 โ†’ buggy". Sangat explainable.
โœ“ Visualisasi mudah, no preprocessing
โœ— Overfitting pada data kecil
๐ŸŒฒ
Random Forest
Ensemble dari 100+ decision tree. Lebih robust, fitur importance otomatis. Pilihan default terbaik.
โœ“ Akurasi tinggi, feature importance
โœ— Lebih lambat, kurang interpretable
โšก
XGBoost
Gradient boosting terbaik saat ini. State-of-the-art di kompetisi defect prediction.
โœ“ Akurasi tertinggi, handles imbalance
โœ— Hyperparameter banyak, butuh tuning
๐Ÿ“Š
Naive Bayes
Probabilistik, sangat cepat. Cocok untuk teks (bug report classification).
โœ“ Sangat cepat, baikuntuk NLP
โœ— Asumsi independensi fitur
๐Ÿ“Š Bagian 5 โ€” Evaluasi Model: Lebih dari Sekadar Akurasi

Mengapa Accuracy Saja Tidak Cukup?

๐Ÿ’ก ANALOGI โ€” Test COVID yang "Akurat 99%"

Bayangkan test COVID dengan akurasi 99% โ€” kedengarannya hebat! Tapi jika prevalensi COVID di populasi hanya 1%, model yang selalu bilang "negatif" sudah mencapai akurasi 99%. Model tersebut TIDAK berguna sama sekali. Untuk prediksi defect, yang penting bukan akurasi global, tapi seberapa baik model menemukan modul yang benar-benar buggy (recall/sensitivity tinggi pada kelas defective).

Confusion Matrix โ€” Model Prediksi Defect
Pred: CLEANPred: BUGGY
Actual: CLEAN
142
True Negative (TN)
18
False Positive (FP)
Over-testing
Actual: BUGGY
8
False Negative (FN)
โ— Berbahaya!
32
True Positive (TP)
Bug terdeteksi โœ“
87%
Accuracy
(TP+TN)/Total. Menyesatkan jika data imbalanced!
80%
Recall
TP/(TP+FN). % bug yang berhasil ditemukan. PRIORITAS UTAMA!
64%
Precision
TP/(TP+FP). % prediksi buggy yang benar-benar buggy.
71%
F1-Score
Harmonic mean Precision & Recall. Balance keduanya.

Untuk prediksi defect: Recall (sensitivity) adalah metrik terpenting. False Negative (bug yang tidak terdeteksi dan lolos ke production) jauh lebih mahal dari False Positive (modul clean yang dicurigai buggy).

โš–๏ธ Bagian 6 โ€” Tantangan Data Imbalanced & SMOTE

Kebanyakan Modul Tidak Buggy โ€” Itu Masalah!

๐Ÿ’ก ANALOGI โ€” Belajar dari Kelas yang Tidak Seimbang

Bayangkan mengajarkan model untuk mengenali huruf "Z" dari dataset 10.000 huruf, tapi "Z" hanya muncul 200 kali (2%). Model cenderung "malas" โ€” lebih mudah bilang bukan Z untuk semua huruf dan mencapai akurasi 98%. Ini analog dengan dataset defect: biasanya hanya 15โ€“30% modul yang buggy. Model naif akan prediksi "clean" semua dan tetap akurat tinggi.

โš–๏ธ Solusi Imbalanced Data: SMOTE

SMOTE (Synthetic Minority Over-sampling Technique) menciptakan sampel sintetis baru dari kelas minoritas (buggy) dengan cara interpolasi antara sampel yang ada โ€” bukan sekadar menduplikasi, tapi membuat variasi baru yang "realistis".

SEBELUM SMOTE
700
Clean (87.5%)
100
Buggy (12.5%)
Ratio 7:1 โ€” imbalanced!
โ†’
SMOTE
SETELAH SMOTE
700
Clean (50%)
700*
Buggy (50%)
Balanced 1:1 โœ“

*600 sampel sintetis dibuat SMOTE dari 100 sampel asli buggy.

Penting: SMOTE hanya diterapkan pada data training, bukan data test! Data test harus tetap merepresentasikan distribusi data nyata.

๐Ÿ”„ Bagian 7 โ€” Pipeline Lengkap: Dari Dataset ke Prediksi

Alur Kerja Software Defect Prediction (SDP)

1

Kumpulkan & Bersihkan Data

Ambil dataset (NASA MDP / PROMISE / historis sendiri). Tangani missing values, hapus duplikat, normalisasi format. Verifikasi label akurasi.

2

Exploratory Data Analysis (EDA)

Distribusi kelas, korelasi fitur, outlier detection. Pahami karakteristik dataset sebelum modelling. Plot pairplot, heatmap korelasi.

3

Feature Selection & Engineering

Hapus fitur dengan korelasi sangat tinggi (multicollinearity). Buat fitur baru jika relevan (rasio, interaksi). Normalisasi jika diperlukan.

4

Train-Test Split + SMOTE

Split 80% train / 20% test dengan stratified sampling. Terapkan SMOTE HANYA pada data training. Test set tetap asli.

5

Train Model & Hyperparameter Tuning

Latih multiple model (Logistic Regression, Random Forest, XGBoost). Gunakan GridSearchCV atau RandomizedSearchCV untuk tuning. Cross-validation 5-fold.

6

Evaluasi & Interpretasi

Confusion matrix, Precision, Recall, F1, AUC-ROC. Feature importance dari Random Forest/XGBoost. SHAP values untuk explainability.

7

Deployment ke Workflow QA

Integrasikan ke CI/CD pipeline. Saat ada code push, model otomatis menilai risiko setiap modul yang berubah. Hasilkan laporan risiko untuk QA team.

๐Ÿ Bagian 8 โ€” Praktik Python: Pipeline Prediksi Defect Lengkap

Implementasi Nyata dengan scikit-learn & XGBoost

Software Defect Prediction โ€” Pipeline Lengkap dengan NASA KC1 Dataset Python ยท scikit-learn ยท imbalanced-learn ยท XGBoost
# ================================================================
# S11409 - Sesi 9: Software Defect Prediction (SDP) Pipeline
# Dataset: NASA KC1 (simulasi dengan fitur realistis)
# Dosen: Riadi Marta Dinata, S.Ti., M.Kom. | ISTN Jakarta
# ================================================================
# pip install scikit-learn imbalanced-learn xgboost pandas numpy
#             matplotlib seaborn shap

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings; warnings.filterwarnings('ignore')

from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (classification_report, confusion_matrix,
                               roc_auc_score, RocCurveDisplay)
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline
import xgboost as xgb

# โ”€โ”€ STEP 1: BUAT DATASET SIMULASI NASA KC1-STYLE โ”€โ”€
np.random.seed(42)
n_samples = 1000

# Fitur kode (terinspirasi NASA KC1)
df = pd.DataFrame({
    # Halstead Metrics
    'halstead_volume':   np.random.lognormal(5, 1.5, n_samples),
    'halstead_effort':   np.random.lognormal(8, 2.0, n_samples),
    'halstead_difficulty': np.random.lognormal(2, 1.0, n_samples),

    # McCabe / Cyclomatic Complexity
    'cyclomatic_complexity': np.random.lognormal(1.5, 0.8, n_samples).astype(int) + 1,
    'essential_complexity': np.random.lognormal(1.2, 0.7, n_samples).astype(int) + 1,

    # Size Metrics
    'loc':               np.random.lognormal(4, 1.2, n_samples).astype(int),
    'num_operands':      np.random.lognormal(4, 1.0, n_samples).astype(int),
    'num_operators':     np.random.lognormal(3.5, 1.0, n_samples).astype(int),

    # Process Metrics
    'num_commits':       np.random.poisson(8, n_samples),
    'num_authors':       np.random.randint(1, 8, n_samples),
    'code_age_months':   np.random.randint(1, 36, n_samples),
    'prev_bugs':         np.random.poisson(1.5, n_samples),
})

# Label: buggy (1) jika kombinasi fitur berisiko tinggi
# (simulasi hubungan yang realistis)
risk_score = (
    0.3 * (df['cyclomatic_complexity'] > 10).astype(float) +
    0.25 * (df['num_authors'] > 4).astype(float) +
    0.2 * (df['prev_bugs'] > 0).astype(float) +
    0.15 * (df['loc'] > 200).astype(float) +
    0.1 * (df['num_commits'] > 12).astype(float) +
    np.random.normal(0, 0.15, n_samples)   # noise
)
df['buggy'] = (risk_score > 0.5).astype(int)

X = df.drop('buggy', axis=1)
y = df['buggy']

print("="*60)
print("    SOFTWARE DEFECT PREDICTION PIPELINE")
print("    NASA KC1 Style Dataset | S11409 ISTN")
print("="*60)
print(f"\nDataset: {len(df)} modul kode")
print(f"Clean  : {(y==0).sum()} ({(y==0).mean():.1%})")
print(f"Buggy  : {(y==1).sum()} ({(y==1).mean():.1%})")
print(f"Fitur  : {X.shape[1]} fitur")

# โ”€โ”€ STEP 2: SPLIT DATA โ”€โ”€
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.20, random_state=42, stratify=y)

print(f"\nTraining: {len(X_train)} | Test: {len(X_test)}")

# โ”€โ”€ STEP 3: DEFINISI MODELS โ”€โ”€
models = {
    'Logistic Regression': ImbPipeline([
        ('smote', SMOTE(random_state=42)),
        ('scaler', StandardScaler()),
        ('clf', LogisticRegression(max_iter=1000, random_state=42))
    ]),
    'Random Forest': ImbPipeline([
        ('smote', SMOTE(random_state=42)),
        ('clf', RandomForestClassifier(
            n_estimators=200, max_depth=10,
            class_weight='balanced', random_state=42))
    ]),
    'XGBoost': ImbPipeline([
        ('smote', SMOTE(random_state=42)),
        ('clf', xgb.XGBClassifier(
            n_estimators=200, learning_rate=0.05, max_depth=5,
            scale_pos_weight=3, use_label_encoder=False,
            eval_metric='logloss', random_state=42))
    ]),
}

# โ”€โ”€ STEP 4: TRAIN & EVALUATE โ”€โ”€
results = {}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

print("\n{'Model':<22} {'AUC-ROC':>10} {'F1':>8} {'Recall':>8} {'Precision':>12}")
print("-"*65)

for name, pipeline in models.items():
    pipeline.fit(X_train, y_train)
    y_pred = pipeline.predict(X_test)
    y_prob = pipeline.predict_proba(X_test)[:, 1]

    from sklearn.metrics import f1_score, recall_score, precision_score
    auc  = roc_auc_score(y_test, y_prob)
    f1   = f1_score(y_test, y_pred)
    rec  = recall_score(y_test, y_pred)
    prec = precision_score(y_test, y_pred)

    results[name] = {'auc': auc, 'f1': f1, 'recall': rec,
                     'precision': prec, 'model': pipeline, 'y_pred': y_pred}
    print(f"{name:<22} {auc:>10.3f} {f1:>8.3f} {rec:>8.3f} {prec:>12.3f}")

# โ”€โ”€ STEP 5: FEATURE IMPORTANCE (Random Forest) โ”€โ”€
rf_model = results['Random Forest']['model'].named_steps['clf']
feat_imp = pd.Series(rf_model.feature_importances_, index=X.columns)
feat_imp = feat_imp.sort_values(ascending=False).head(8)

print("\n--- Top 8 Feature Importance (Random Forest) ---")
for feat, imp in feat_imp.items():
    bar = 'โ–ˆ' * int(imp * 100)
    print(f"  {feat:<25} {bar} {imp:.3f}")

# โ”€โ”€ STEP 6: VISUALISASI โ”€โ”€
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
fig.patch.set_facecolor('#0A0608')

# Plot 1: Confusion Matrix terbaik (XGBoost)
ax1 = axes[0]
ax1.set_facecolor('#160C0E')
cm = confusion_matrix(y_test, results['XGBoost']['y_pred'])
sns.heatmap(cm, annot=True, fmt='d', ax=ax1,
            cmap='RdYlGn', linewidths=0.5,
            xticklabels=['Clean','Buggy'], yticklabels=['Clean','Buggy'])
ax1.set_title('Confusion Matrix โ€” XGBoost', color='white', pad=10)
ax1.tick_params(colors='#9B7A80')

# Plot 2: Feature Importance
ax2 = axes[1]
ax2.set_facecolor('#160C0E')
feat_imp.plot(kind='barh', ax=ax2,
              color=['#C8102E','#E8374F','#F97316','#FCD34D',
                     '#22C55E','#38BDF8','#A78BFA','#9B7A80'])
ax2.set_title('Feature Importance (Random Forest)', color='white', pad=10)
ax2.tick_params(colors='#9B7A80')
ax2.invert_yaxis()

# Plot 3: ROC Curve perbandingan
ax3 = axes[2]
ax3.set_facecolor('#160C0E')
colors_roc = {'Logistic Regression': '#38BDF8',
               'Random Forest': '#F97316', 'XGBoost': '#C8102E'}
for name, res in results.items():
    y_prob = res['model'].predict_proba(X_test)[:, 1]
    RocCurveDisplay.from_predictions(
        y_test, y_prob, name=f"{name} (AUC={res['auc']:.3f})",
        ax=ax3, color=colors_roc[name])
ax3.plot([0,1],[0,1], '--', color='#4B5563', label='Random')
ax3.set_title('ROC Curve โ€” Model Comparison', color='white', pad=10)
ax3.tick_params(colors='#9B7A80')
ax3.legend(fontsize=9)
ax3.grid(alpha=0.15)

plt.tight_layout()
plt.savefig('sdp_results.png', dpi=150, facecolor='#0A0608', bbox_inches='tight')
print("\nโœ… Hasil tersimpan: sdp_results.png")

# โ”€โ”€ STEP 7: PREDIKSI MODUL BARU โ”€โ”€
print("\n--- Prediksi Risiko Modul Kode Baru ---")
modul_baru = pd.DataFrame([
    {'halstead_volume':1200, 'halstead_effort':50000,  'halstead_difficulty':25,
     'cyclomatic_complexity':18, 'essential_complexity':12,
     'loc':320, 'num_operands':180, 'num_operators':120,
     'num_commits':15, 'num_authors':6, 'code_age_months':18, 'prev_bugs':3},
    {'halstead_volume':80,  'halstead_effort':1200,   'halstead_difficulty':3,
     'cyclomatic_complexity':3,  'essential_complexity':2,
     'loc':35,  'num_operands':20,  'num_operators':15,
     'num_commits':3,  'num_authors':1, 'code_age_months':2,  'prev_bugs':0},
])
modul_names = ["PaymentProcessor.py", "StringUtils.py"]
best_model = results['XGBoost']['model']
proba_baru = best_model.predict_proba(modul_baru)[:, 1]

for nama, prob in zip(modul_names, proba_baru):
    risk = "๐Ÿ”ด HIGH RISK" if prob > 0.6 else ("๐ŸŸก MEDIUM" if prob > 0.35 else "๐ŸŸข LOW RISK")
    print(f"  {nama:<30} Risk: {prob:.1%}  {risk}")
โœ๏ธ Latihan Mandiri
๐Ÿ“ KUIS SESI 9

Uji Pemahaman Anda

Soal 1 โ€” Feature Engineering

Anda memiliki akses ke repository Git proyek SIAKAD ISTN. Sebutkan dan jelaskan minimal 6 fitur yang bisa Anda ekstrak dari Git history (bukan dari kode sumber) untuk prediksi defect. Mengapa fitur-fitur ini relevan?

Soal 2 โ€” Evaluasi Model

Model A: Accuracy 92%, Recall 45%. Model B: Accuracy 78%, Recall 85%. Untuk digunakan dalam prediksi defect production system e-banking, mana yang lebih baik? Jelaskan dengan analogi biaya False Negative vs False Positive.

Soal 3 โ€” SMOTE

Dataset Anda memiliki 900 modul clean dan 100 modul buggy. (a) Apa risiko melatih model tanpa penanganan imbalanced? (b) Jelaskan cara kerja SMOTE dan berapa sampel sintetis yang dibuat untuk mencapai rasio 1:1. (c) Mengapa SMOTE tidak boleh diterapkan pada data test?

Soal 4 โ€” Interpretasi Hasil

Feature importance model Anda menunjukkan: prev_bugs (0.31), cyclomatic_complexity (0.22), num_authors (0.18), loc (0.12). Apa 3 rekomendasi konkret yang Anda berikan kepada tim developer berdasarkan data ini?

Soal 5 โ€” Python Challenge

Modifikasi kode di atas untuk: (a) Tambahkan Decision Tree ke daftar model yang dibandingkan, (b) Implementasikan threshold tuning โ€” ubah threshold dari default 0.5 menjadi 0.35 untuk meningkatkan recall, (c) Hitung berapa penghematan (dalam jam testing) jika model memprioritaskan 30% modul tertinggi risikonya vs menguji semua modul secara merata.

๐Ÿ’ก Rangkuman Kunci Sesi 9

Prediksi defect bukan memprediksi dengan sempurna โ€” tapi memprioritaskan modul berisiko tinggi agar QA effort lebih efisien

Process metrics (git history, num authors) seringkali lebih prediktif dari code metrics murni โ€” kombinasi keduanya terbaik

Recall adalah raja untuk defect prediction โ€” False Negative (bug lolos) jauh lebih mahal dari False Positive

SMOTE mengatasi imbalanced data dengan membuat sampel sintetis kelas minoritas โ€” hanya di training set!

XGBoost umumnya terbaik secara akurasi; Random Forest lebih mudah diinterpretasi lewat feature importance

AUC-ROC > 0.8 adalah target yang baik; di bawah 0.7 perlu perbaikan feature engineering atau lebih banyak data