Klasifikasi Halaman SEO berbasis Machine Learning

Program ini dikembangkan atau dibuat berdasarkan hasil analisis terhadap beberapa paper penelitian terkait yang relevan. Implementasi program bertujuan untuk uji coba eksperimental, pembelajaran dan penelitian, sehingga hasil yang ada pada sistem masih bersifat eksperiment semata.

Penggunaan program, metode, maupun hasil eksperimen perlu disesuaikan dengan konteks penelitian serta mempertimbangkan keterbatasan data, metode, dan lingkungan pengujian yang digunakan. Alur proses

  1. Load data mentah (numerik + teks SEO On-Page)
  2. Pra-pemrosesan data (cleaning, teks preprocessing)
  3. Word Embedding dengan FastText untuk representasi vektor teks
  4. Perhitungan Cosine Similarity antar komponen (URL, Title, Meta, H1) untuk fitur X22–X27
  5. Split data latih/uji + penanganan imbalance (SMOTE) + scaling
  6. Seleksi fitur: Mutual Information (MI) dan Random Forest Feature Importance (RFFI)
  7. Pemodelan: Random Forest (RF) dan Weighted Voting Ensemble (WVE) = RF + XGBoost
  8. Evaluasi: Accuracy, Precision, Recall, ROC AUC
  9. Tabel perbandingan akhir + visualisasi

Catatan penting: Dataset masih bersifat sintesis (dummy) namun strukturnya dibuat semirip mungkin dengan beberapa paper yang relevan, supaya seluruh proses atau pipeline tetap bisa dijalankan end-to-end sebagai contoh/tes.

Instalasi & Import Library

1. scikit-learn : adalah library pada python yang digunakan untuk membangun dan melatih model machin learning. Beberapa fitur yang ada pada library ini mencakup

Calssification : Randon Forest, SVM, Logistic Regression, KNN

Regression : Linier regression, Randon Forest Regressor, Gradient Boosting

Clustering : K-Means

Prepocessing : One-Hot Encoding

Feature Selection

Model Selection : train-test-split, cross-validation, GridSearchCV, RandomizedSearchCV

Evaluation : Accuracy, Precision, Recall, F1-Score, MAE, MSE, dan lainnya

Fungsi scikit-learn pada sistem ini untuk menggunakan beberapa fitur seperti Random Forest, Mutual Information, metrik evaluasi, scaling, split data

2. xgboost adalah algoritam ensemble dan decision tree yang menggunakan teknik gradient boosting untuk membangun sebuah model prediksi secara bertahap. Extreme Gradient Boosting (komponen WVE) atau Weighted Voting Ensemble mampu menghasilkan prediksi yang dikombinasikan denga model machine learning lain misal Random Forest + XGBoost +Gradient Boosting

3. imbalanced-learn adalah library yang berfungsi menangani permasalahan ketidakseimbangan dataset. Sistem ini menggunakan SMOTE (Synthetic Minority Over-sampling Technique) yang menghasilkan sampel sintesis yang bersifat minoritas

4. FastText Word Embedding adalah proses yang berguna untuk mengubah suatu kata atau teks pada dataset menjadi data berbentuk vektor numerik. FastText Word Embedding menggunakan library gensim.

5. Tokenisasi teks adalah sebuah proses untuk memecah sebuah teks menjadi unit-unit kecil. Misal dari url/kalimat menjadi kata. Sedangkan Stopwords adalah kata kata umum yang dianggap kurang memberikan informasi mendalam dalam analisis teks. Proses stopwords dan tokenisasi pada sistem menggunakan library NLTK (Natural Language Toolkit) untuk melakukan preprocessing teks, menghapus stopwords sebelum teks dikonversi ke bentuk vektor.

6. Numpy adalah libarary yang berfungsi memudahkan operasi perhitungan tipe data numerik (penjumlahan, perkalian dan lainnya)

7. pandas adalah library yang berfungsi untuk mengubah dimensi data, membuat tabel, membaca data dan memproses data frame

8. re (regular Expression) adalah library untuk melakukan teks processing seperti menghapus tanda baca, angka atau karakter tertentu

9. seaborn adalah library yang digunakan untuk melakukan visualisasi data

10. matplotlib.pyplot adalah library untuk membuat grafik atau chart

11. Randon State digunakan untuk mengontrol proses randomisasi agar eksperimen machine learning dapat menghasilkan nilai konsisten ketika dijalankan

# Install library yang belum tersedia default di Colab
!pip install -q xgboost imbalanced-learn gensim nltk

import numpy as np
import pandas as pd
import re
import warnings
warnings.filterwarnings("ignore")

# Scikit-learn
from sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import mutual_info_classif
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (accuracy_score, precision_score, recall_score,
                              roc_auc_score, confusion_matrix, classification_report)
from sklearn.metrics.pairwise import cosine_similarity

# Imbalanced data
from imblearn.over_sampling import SMOTE

# Gradient boosting
from xgboost import XGBClassifier

# NLP
import nltk
nltk.download('stopwords', quiet=True)
nltk.download('punkt', quiet=True)
nltk.download('punkt_tab', quiet=True)
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize

# Word embedding
from gensim.models import FastText

# Visualisasi
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")

RANDOM_STATE = 42
np.random.seed(RANDOM_STATE)

print("Semua library berhasil di-import.")

Load Data

Struktur kolom pada data terdiri dari 21 fitur numerik utama (X1-X21), fitur similaritas (X2-X27) yang merupakan hasil perhitungan sendiri, dan Target (Y) keputusan apakah suatu halaman perlu di optimasi SEO nya atau tidak.

1. df = pd.read_csv berfungsi untuk membaca dataset yang sudah disediakan dalam format CSV.

2. df.head () berfungsi untuk menampilkan 5 data teratas

3. df.shape () berfungsi untuk melihat jumlah baris dan jumlah kolom dataset.

# ============================================================
# OPSI A — Upload CSV kamu sendiri (disarankan)
# ============================================================
from google.colab import files
import io

UPLOAD_YOUR_OWN_DATA = True  # ganti jadi True kalau mau upload CSV sendiri

if UPLOAD_YOUR_OWN_DATA:
    uploaded = files.upload()
    filename = list(uploaded.keys())[0]
    df = pd.read_csv(io.BytesIO(uploaded[filename]))
    print(f"Data '{filename}' berhasil dimuat: {df.shape[0]} baris, {df.shape[1]} kolom")
else:
    print("UPLOAD_YOUR_OWN_DATA = False -> akan membuat data sintetis pada sel berikutnya.")
# ============================================================
# OPSI B — Generate data SINTETIS (dipakai kalau tidak upload data sendiri)
# Struktur & rentang nilai meniru Tabel 2 & 3 pada paper.
# GANTI blok ini dengan data asli kamu untuk hasil yang bermakna.
# ============================================================
def generate_synthetic_seo_data(n=1500, random_state=RANDOM_STATE):
    rng = np.random.default_rng(random_state)

    contoh_judul = ["cara optimasi seo untuk pemula", "tips meningkatkan traffic website",
                     "panduan lengkap digital marketing", "strategi konten yang efektif",
                     "belajar machine learning dari nol", "review produk terbaik tahun ini"]
    contoh_meta = ["pelajari cara terbaik untuk meningkatkan performa halaman anda di mesin pencari",
                   "panduan praktis dan mudah dipahami untuk pemula maupun profesional",
                   "temukan strategi terbukti untuk hasil yang maksimal dan berkelanjutan"]
    contoh_h1 = ["cara optimasi seo", "tips traffic website", "panduan digital marketing",
                 "strategi konten efektif", "belajar machine learning", "review produk terbaik"]

    data = {
        "url_text": [rng.choice(contoh_judul).replace(" ", "-") for _ in range(n)],
        "title_text": [rng.choice(contoh_judul) for _ in range(n)],
        "meta_text": [rng.choice(contoh_meta) for _ in range(n)],
        "h1_text": [rng.choice(contoh_h1) for _ in range(n)],
        "X1": rng.integers(20, 130, n),                      # Panjang Judul
        "X2": rng.integers(300, 1200, n),                    # Lebar Pixel Judul
        "X3": rng.integers(60, 220, n),                      # Panjang Meta Description
        "X4": rng.integers(600, 1400, n),                    # Lebar Pixel Meta Description
        "X5": rng.integers(10, 90, n),                       # Panjang H1
        "X6": rng.integers(150, 300, n),                     # Ukuran (KB)
        "X7": rng.uniform(30, 45, n).round(1),                # Total Transfer (KB)
        "X8": rng.integers(800, 3500, n),                    # Jumlah Kata
        "X9": rng.integers(150, 450, n),                     # Jumlah Kalimat
        "X10": rng.uniform(5, 8, n).round(2),                 # Rata-rata kata/kalimat
        "X11": rng.uniform(9, 16, n).round(2),                # Flesch Reading Ease
        "X12": rng.choice(["Very Easy", "Easy", "Fairly Easy", "Normal",
                            "Fairly Hard", "Hard", "Very Hard"], n),  # Readability
        "X13": rng.uniform(6.5, 10, n).round(2),              # Rasio Teks
        "X14": rng.integers(1, 5, n),                        # Kedalaman Crawling
        "X15": rng.integers(20, 60, n),                      # Inlinks
        "X16": rng.integers(100, 160, n),                    # Outlinks
        "X17": rng.uniform(0.1, 0.3, n).round(2),             # Waktu Respon
        "X18": rng.uniform(10, 35, n).round(2),               # Kesulitan Kata Kunci
        "X19": rng.uniform(50, 800, n).round(1),              # Volume Pencarian
        "X20": rng.integers(0, 5000, n),                     # Traffic
        "X21": rng.uniform(30, 60, n).round(2),               # Perubahan Peringkat
    }
    df = pd.DataFrame(data)

    # Target dibuat berkorelasi lemah dengan beberapa fitur, supaya model punya sinyal untuk dipelajari
    score = (
        0.35 * (df["X20"] < 500).astype(int) +
        0.30 * (df["X21"] < 45).astype(int) +
        0.20 * (df["X18"] > 25).astype(int) +
        0.15 * rng.random(n)
    )
    df["Y"] = (score > np.quantile(score, 0.65)).astype(int)
    return df

df = generate_synthetic_seo_data()
print(f"Data sintetis dibuat: {df.shape[0]} baris, {df.shape[1]} kolom")
df.head()







Pra-Pemrosesan Data

 Tahapan pra-pemrosesan mencakup data teks & numerik:

  1. Pembersihan data : menhapus duplikat, tangani missing value
  2. Penghapusan tanda baca
  3. Penghapusan stopwords
  4. Penghapusan nilai numerik dalam teks
  5. Tokenisasi
  6. Lemmatization/stemming (di sini pakai stemming ringan)
  7. Encoding data kategorikal (kolom X12 Readability dilakukan one-hot encoding)

1. Lemmatization atau stemming adalah teknik normalisasi data dalam preprocessing teks/NLP untuk mengubah kata ke bentuk dasar (misal running ke run)

2. One-hot Encoding adalah proses mengubah bentuk data dari kategorik menjadi numerik. Mengapa perlu diubah? karena banyak algoritma machine learning atau statistika yang bekerja pada tipe data numerik contohnya regresi linier, k-NN, SVM dan lainnya. Biasanya one -hot encoding ini menggunakan kode “from sklearn.preprocessing import OneHotEncoder”

3. df = df.drop_duplicates() berfungsi untuk menghilangkan data duplikat

4. df = df.drop() berfungsi untuk menghilangkan fitru yang tidak digunakan dalam dataset

# ---- 2.0 Pembersihan data dasar ----
print("Jumlah duplikat sebelum dibersihkan:", df.duplicated().sum())
df = df.drop_duplicates().reset_index(drop=True)

print("Jumlah missing value per kolom:")
print(df.isna().sum()[df.isna().sum() > 0])

# Isi missing value numerik dengan rata-rata (sesuai paper: "penanganan missing value menggunakan rata-rata")
num_cols_for_na = df.select_dtypes(include=[np.number]).columns
df[num_cols_for_na] = df[num_cols_for_na].fillna(df[num_cols_for_na].mean())

# Isi missing value teks/kategorikal dengan modus
cat_cols_for_na = df.select_dtypes(include=["object"]).columns
for c in cat_cols_for_na:
    df[c] = df[c].fillna(df[c].mode()[0])

print(f"Data setelah cleaning: {df.shape[0]} baris")
# ---- 2.1 Preprocessing teks (untuk kolom url_text, title_text, meta_text, h1_text) ----
stop_words_id_en = set(stopwords.words('english'))  # ganti/gabung dengan stopwords Indonesia jika perlu
# Untuk teks Bahasa Indonesia, disarankan pakai library Sastrawi:
#   !pip install Sastrawi
#   from Sastrawi.Stemmer.StemmerFactory import StemmerFactory
#   from Sastrawi.StopWordRemover.StopWordRemoverFactory import StopWordRemoverFactory

def clean_text(text):
    text = str(text).lower()                          # lowercase
    text = re.sub(r'[^a-zA-Z\s-]', ' ', text)          # hapus tanda baca & angka, sisakan huruf dan '-'
    text = re.sub(r'-', ' ', text)                     # ubah '-' (dari URL) jadi spasi
    tokens = text.split()                              # tokenisasi sederhana
    tokens = [t for t in tokens if t not in stop_words_id_en and len(t) > 1]  # hapus stopwords
    return tokens

text_columns = ["url_text", "title_text", "meta_text", "h1_text"]
tokenized = {}
for col in text_columns:
    tokenized[col] = df[col].apply(clean_text)
    df[col + "_clean"] = tokenized[col].apply(lambda x: " ".join(x))

df[[c + "_clean" for c in text_columns]].head()
# ---- 2.2 Encoding fitur kategorikal (X12 - Readability) ----
# One-hot encoding: menghasilkan kolom seperti Readability_Fairly Hard, Readability_Very Hard, dst
# (persis seperti yang muncul di Tabel 4 & 7 pada paper)
df_readability_dummies = pd.get_dummies(df["X12"], prefix="Readability")
df = pd.concat([df.drop(columns=["X12"]), df_readability_dummies], axis=1)

print("Kolom hasil one-hot encoding Readability:")
print(list(df_readability_dummies.columns))

Representasi Teks (FastText) & Perhitungan Similaritas

Melatih FastText. Similaritas antar komponen dihitung dengan Cosine Similarity. Bagian ini menghasilkan X22-X27

  • X22 Similaritas URL–Title
  • X23 Similaritas URL–Meta
  • X24 Similaritas URL–H1
  • X25 Similaritas Title–Meta
  • X26 Similaritas Title–H1
  • X27 Similaritas Meta–H1

1. df.describe() digunakan untuk

  • mengetahui jumlah data pada setiap variabel
  • melihat rata-rata dan median
  • mengetahui rentang nilai (min–max)
  • melihat seberapa besar variasi data melalui std
  • mendeteksi kemungkinan outlier secara awal
# ---- 3.1 Latih FastText pada korpus gabungan ----
corpus = []
for col in text_columns:
    corpus.extend(tokenized[col].tolist())
corpus = [c for c in corpus if len(c) > 0]

fasttext_model = FastText(
    sentences=corpus,
    vector_size=100,   # dimensi vektor kata
    window=5,
    min_count=1,
    sg=1,              # skip-gram (mendekati arsitektur asli FastText)
    epochs=20,
    seed=RANDOM_STATE
)
print("FastText selesai dilatih. Ukuran vocabulary:", len(fasttext_model.wv))
# ---- 3.2 Ubah setiap dokumen (baris) jadi 1 vektor (rata-rata vektor kata) ----
def document_vector(tokens, model):
    vectors = [model.wv[t] for t in tokens if t in model.wv] if len(tokens) > 0 else []
    if len(vectors) == 0:
        return np.zeros(model.vector_size)
    return np.mean(vectors, axis=0)

doc_vectors = {}
for col in text_columns:
    doc_vectors[col] = np.vstack([document_vector(toks, fasttext_model) for toks in tokenized[col]])

print("Bentuk matriks vektor per kolom teks:")
for col in text_columns:
    print(f"  {col}: {doc_vectors[col].shape}")
# ---- 3.3 Hitung Cosine Similarity antar pasangan komponen ----
def pairwise_cosine(vec_a, vec_b):
    # cosine_similarity mengembalikan matriks n x n; kita hanya perlu diagonalnya
    # (similaritas baris ke-i dengan baris ke-i pasangannya)
    num = np.sum(vec_a * vec_b, axis=1)
    denom = (np.linalg.norm(vec_a, axis=1) * np.linalg.norm(vec_b, axis=1))
    denom[denom == 0] = 1e-10  # hindari pembagian dengan nol
    return num / denom

df["X22"] = pairwise_cosine(doc_vectors["url_text"],   doc_vectors["title_text"])  # URL_Title
df["X23"] = pairwise_cosine(doc_vectors["url_text"],   doc_vectors["meta_text"])   # URL_Meta
df["X24"] = pairwise_cosine(doc_vectors["url_text"],   doc_vectors["h1_text"])     # URL_H1
df["X25"] = pairwise_cosine(doc_vectors["title_text"], doc_vectors["meta_text"])   # Title_Meta
df["X26"] = pairwise_cosine(doc_vectors["title_text"], doc_vectors["h1_text"])     # Title_H1
df["X27"] = pairwise_cosine(doc_vectors["meta_text"],  doc_vectors["h1_text"])     # Meta_H1

df[["X22", "X23", "X24", "X25", "X26", "X27"]].describe()

Menyusun Dataset Final (Fitur X + Target Y)

Menyiapkan matriks X dan Y (target status SEO)

# Buang kolom teks mentah & teks hasil cleaning (sudah direpresentasikan lewat X22-X27)
drop_cols = text_columns + [c + "_clean" for c in text_columns]
df_final = df.drop(columns=drop_cols)

y = df_final["Y"]
X = df_final.drop(columns=["Y"])

print(f"Jumlah fitur akhir: {X.shape[1]}")
print(f"Distribusi kelas target (Y):")
print(y.value_counts(normalize=True).rename("proporsi"))

X.head()

Split Data, Penanganan Imbalance (SMOTE), dan Scaling

  • Split: 80% data latih, 20% data uji
  • SMOTE: diterapkan hanya pada data latih, sesuai praktik yang benar, untuk mengatasi ketidakseimbangan kelas “Optimasi” vs “Tidak Optimasi”
  • Scaling: StandardScaler fit pada data latih, lalu diterapkan ke data latih & uji

1. from sklearn.preprocessing import StandardScaler digunakan sebagai teknik normalisasi data atau melakukan standardisasi

contoh

Backlinks Traffic
100 5000
200 7000
300 9000

Rentang Backlinks dan Traffic berbeda jauh. Setelah StandardScaler, keduanya berada pada skala yang lebih sebanding. Kenapa perlu? Standardisasi penting untuk algoritma yang sensitif terhadap skala fitur, misalnya:

  • PCA
  • Logistic Regression
  • SVM
  • KNN
  • Neural Network
  • K-Means

2. Kalau SMOTE dilakukan sebelum train-test split, data sintetis yang dibuat bisa memiliki hubungan dengan data yang akhirnya masuk ke test set. Model mendapatkan informasi dari distribusi data test secara tidak langsung saat proses training ini disebut data leakage. Performa model pada test set bisa terlihat lebih tinggi atau lebih baik daripada kondisi sebenarnya, karena test set tidak lagi benar-benar independen.

SMOTE hanya diterapkan pada data latih agar data sintetis tidak memengaruhi data pengujian, sehingga test set tetap merepresentasikan data yang benar-benar belum pernah dilihat model dan evaluasi tidak mengalami data leakage.

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.20, stratify=y, random_state=RANDOM_STATE
)

print(f"Data latih : {X_train.shape[0]} baris")
print(f"Data uji   : {X_test.shape[0]} baris")

# SMOTE hanya di data latih
smote = SMOTE(random_state=RANDOM_STATE)
X_train_res, y_train_res = smote.fit_resample(X_train, y_train)
print(f"Data latih setelah SMOTE: {X_train_res.shape[0]} baris")
print(y_train_res.value_counts())

# Scaling
scaler = StandardScaler()
X_train_scaled = pd.DataFrame(scaler.fit_transform(X_train_res), columns=X.columns)
X_test_scaled  = pd.DataFrame(scaler.transform(X_test),         columns=X.columns)

Seleksi Fitur

Mutual Information (MI) mengukur seberapa besar informasi yang dibagikan antara sebuah fitur dan target 

# ---- 6.1 Mutual Information ----
mi_scores = mutual_info_classif(X_train_scaled, y_train_res, random_state=RANDOM_STATE)
mi_ranking = pd.Series(mi_scores, index=X.columns).sort_values(ascending=False)

print("20 Fitur Terpenting berdasarkan Mutual Information:")
print(mi_ranking.head(20))

plt.figure(figsize=(8, 6))
mi_ranking.head(20).sort_values().plot(kind='barh', color='steelblue')
plt.title("20 Fitur Terpenting — Mutual Information")
plt.xlabel("Skor MI")
plt.tight_layout()
plt.show()

Random Forest & Weighted Voting Ensemble (WVE)

Random Forest (dengan GridSearchCV). Hyperparameter yang di-tuning n_estimators, max_depth, min_samples_split, min_samples_leaf.

Weighted Voting Ensemble (RF + XGBoost), bobot tiap model dihitung proporsional terhadap akurasinya:

def train_random_forest(X_tr, y_tr):
    """Melatih Random Forest dengan GridSearchCV, mengoptimalkan ROC AUC."""
    param_grid = {
        'n_estimators': [100, 200, 300],
        'max_depth': [None, 10, 20],
        'min_samples_split': [2, 5, 10],
        'min_samples_leaf': [1, 2, 4],
    }
    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=RANDOM_STATE)
    grid = GridSearchCV(
        RandomForestClassifier(random_state=RANDOM_STATE),
        param_grid=param_grid,
        scoring='roc_auc',
        cv=cv,
        n_jobs=-1
    )
    grid.fit(X_tr, y_tr)
    return grid.best_estimator_


def train_xgboost(X_tr, y_tr):
    """Melatih XGBoost dengan GridSearchCV ringan."""
    param_grid = {
        'n_estimators': [100, 200],
        'max_depth': [3, 5, 7],
        'learning_rate': [0.05, 0.1],
    }
    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=RANDOM_STATE)
    grid = GridSearchCV(
        XGBClassifier(random_state=RANDOM_STATE, eval_metric='logloss'),
        param_grid=param_grid,
        scoring='roc_auc',
        cv=cv,
        n_jobs=-1
    )
    grid.fit(X_tr, y_tr)
    return grid.best_estimator_


def weighted_voting_ensemble_predict(rf_model, xgb_model, X_tr, y_tr, X_eval):
    """
    Menghitung bobot tiap model berdasarkan akurasi di data latih (Persamaan 4),
    lalu mengembalikan probabilitas soft-voting tertimbang untuk X_eval.
    """
    acc_rf  = accuracy_score(y_tr, rf_model.predict(X_tr))
    acc_xgb = accuracy_score(y_tr, xgb_model.predict(X_tr))
    total = acc_rf + acc_xgb
    w_rf, w_xgb = acc_rf / total, acc_xgb / total

    proba_rf  = rf_model.predict_proba(X_eval)[:, 1]
    proba_xgb = xgb_model.predict_proba(X_eval)[:, 1]
    proba_final = (w_rf * proba_rf) + (w_xgb * proba_xgb)

    return proba_final, (w_rf, w_xgb)


def evaluate_model(y_true, y_pred, y_proba):
    return {
        "Accuracy": accuracy_score(y_true, y_pred),
        "Precision": precision_score(y_true, y_pred, zero_division=0),
        "Recall": recall_score(y_true, y_pred),
        "ROC_AUC": roc_auc_score(y_true, y_proba),
    }

print("Fungsi pemodelan siap digunakan.")

Menjalankan Seluruh Skenario

Uji coba

  • Seleksi fitur: Tanpa seleksi (Semua fitur), Mutual Information, RFFI
  • Jumlah fitur: Semua, 20, 14, 10 (khusus MI & RFFI)
  • Model: Random Forest, Weighted Voting Ensemble (RF + XGBoost)
results = []

def run_scenario(method_name, k, feature_list):
    cols = feature_list if k == "Full" else feature_list[:k]
    X_tr = X_train_scaled[cols]
    X_te = X_test_scaled[cols]

    # ---- Random Forest ----
    rf_model = train_random_forest(X_tr, y_train_res)
    rf_pred  = rf_model.predict(X_te)
    rf_proba = rf_model.predict_proba(X_te)[:, 1]
    rf_metrics = evaluate_model(y_test, rf_pred, rf_proba)
    rf_metrics.update({"Metode_Seleksi": method_name, "Jumlah_Fitur": k, "Model": "Random Forest"})
    results.append(rf_metrics)

    # ---- Weighted Voting Ensemble (RF + XGBoost) ----
    xgb_model = train_xgboost(X_tr, y_train_res)
    wve_proba, weights = weighted_voting_ensemble_predict(rf_model, xgb_model, X_tr, y_train_res, X_te)
    wve_pred = (wve_proba >= 0.5).astype(int)
    wve_metrics = evaluate_model(y_test, wve_pred, wve_proba)
    wve_metrics.update({"Metode_Seleksi": method_name, "Jumlah_Fitur": k, "Model": "WVE",
                         "Bobot_RF": round(weights[0], 3), "Bobot_XGB": round(weights[1], 3)})
    results.append(wve_metrics)

    print(f"[{method_name} | {k} fitur] selesai -> RF ROC AUC: {rf_metrics['ROC_AUC']:.4f} | "
          f"WVE ROC AUC: {wve_metrics['ROC_AUC']:.4f}")

# ---- Skenario 1: Tanpa seleksi fitur (semua fitur) ----
run_scenario("Tanpa Seleksi (Base Model)", "Full", list(X.columns))

# ---- Skenario 2: Mutual Information (20, 14, 10 fitur) ----
mi_features = mi_ranking.index.tolist()
for k in [20, 14, 10]:
    run_scenario("Mutual Information", k, mi_features)

# ---- Skenario 3: Random Forest Feature Importance (20, 14, 10 fitur) ----
rffi_features = rffi_ranking.index.tolist()
for k in [20, 14, 10]:
    run_scenario("RFFI", k, rffi_features)

print("\nSemua skenario selesai dijalankan.")

#BelajarsambilNulis

Leave a Reply

Your email address will not be published. Required fields are marked *