{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a954725d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1 extraction features TF-IDF\n",
    "import pandas as pd\n",
    "import joblib\n",
    "import os\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "print(\"🔄 [TAHAP 1] Memulai Preprocessing & TF-IDF...\")\n",
    "\n",
    "# --- CONFIG ---\n",
    "# Gunakan Absolute Path dengan 'r' di depan agar aman di Windows\n",
    "FILE_DATA = r'd:\\project skripsi machine learning intoleransi\\virtualEnvironment\\dataset\\dataYangDiPakai\\data_label_revisi_goblog.csv' \n",
    "\n",
    "# 0. CEK KEAMANAN FILE SEBELUM JALAN\n",
    "if not os.path.exists(FILE_DATA):\n",
    "    raise FileNotFoundError(f\"❌ File tidak ditemukan di jalur:\\n{FILE_DATA}\\nCoba pastikan nama file dan foldernya sudah persis sama!\")\n",
    "\n",
    "# Bikin folder otomatis\n",
    "os.makedirs('output', exist_ok=True) \n",
    "os.makedirs('models', exist_ok=True) \n",
    "\n",
    "# 1. LOAD DATA\n",
    "print(\"   - Membaca dataset...\")\n",
    "df = pd.read_csv(FILE_DATA, sep=';') \n",
    "\n",
    "# Cek keamanan kolom (Biar tidak error kalau nama kolom salah)\n",
    "if 'terjemahan_indo' not in df.columns or 'label' not in df.columns:\n",
    "    print(f\"Daftar kolom yang ada di filemu: {df.columns.tolist()}\")\n",
    "    raise KeyError(\"❌ Kolom 'terjemahan_indo' atau 'label' tidak ada! Coba cek tulisan di atas, pastikan namanya cocok.\")\n",
    "\n",
    "# Bersihkan data kosong\n",
    "df = df.dropna(subset=['terjemahan_indo', 'label']) \n",
    "print(f\"   - Total data bersih yang siap diproses: {len(df)} baris\")\n",
    "\n",
    "# 2. TF-IDF (Ubah Huruf jadi Angka)\n",
    "print(\"   - Melakukan ekstraksi fitur TF-IDF...\")\n",
    "vectorizer = TfidfVectorizer(max_features=5000)\n",
    "X = vectorizer.fit_transform(df['terjemahan_indo'].astype(str))\n",
    "y = df['label'].astype(int) # Pastikan label berupa angka (0, 1, 2)\n",
    "\n",
    "# 3. SIMPAN KAMUS TF-IDF\n",
    "joblib.dump(vectorizer, 'models/vectorizer_tfidf.pkl')\n",
    "\n",
    "# 4. SPLIT DATA (80% Latih, 20% Uji)\n",
    "print(\"   - Memecah data (80% Data Latih, 20% Data Uji)...\")\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 5. SIMPAN DATA MATANG\n",
    "print(\"   - Menyimpan data matang ke folder 'output'...\")\n",
    "joblib.dump(X_train, 'output/X_train.pkl')\n",
    "joblib.dump(X_test,  'output/X_test.pkl')\n",
    "joblib.dump(y_train, 'output/y_train.pkl')\n",
    "joblib.dump(y_test,  'output/y_test.pkl')\n",
    "\n",
    "print(\"✅ SELESAI TAHAP 1. Data sudah siap!\")\n",
    "print(\"👉 Silakan lanjut jalankan '2_training.py' atau '2_training.ipynb'\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15e21017",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2 pelatihan model SVM, KNN dan ensemble\n",
    "import joblib\n",
    "import pandas as pd\n",
    "import os\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.ensemble import VotingClassifier\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "\n",
    "print(\"🏋️ [TAHAP 2] Training: DATA ASLI (Tanpa Penyeimbang Apapun)...\")\n",
    "\n",
    "# 0. CEK KEAMANAN: Pastikan folder dan file Tahap 1 sudah ada\n",
    "if not os.path.exists('output/X_train.pkl'):\n",
    "    raise FileNotFoundError(\"❌ File 'output/X_train.pkl' tidak ditemukan! Pastikan kamu sudah menjalankan '1_preprocessing.py' terlebih dahulu.\")\n",
    "os.makedirs('models', exist_ok=True) # Jaga-jaga kalau folder models terhapus\n",
    "\n",
    "# 1. AMBIL DATA DARI TAHAP 1\n",
    "print(\"   - Memuat data latih...\")\n",
    "X_train = joblib.load('output/X_train.pkl')\n",
    "y_train = joblib.load('output/y_train.pkl')\n",
    "\n",
    "print(f\"   - Jumlah Data Latih Asli: {len(y_train)} baris\")\n",
    "print(f\"   - Komposisi Label: {y_train.value_counts().to_dict()}\")\n",
    "\n",
    "# ---------------------------------------------------------\n",
    "# 2. LATIH SVM (MODEL UTAMA)\n",
    "# ---------------------------------------------------------\n",
    "print(\"\\n🚀 Melatih SVM (Mencari Settingan Terbaik)...\")\n",
    "print(\"   (Mohon tunggu, ini akan memakan waktu beberapa menit ☕)\")\n",
    "\n",
    "param_svm = {\n",
    "    'C': [0.1, 1, 10],\n",
    "    'kernel': ['linear', 'rbf'],\n",
    "    'gamma': ['scale', 'auto']\n",
    "}\n",
    "\n",
    "# n_jobs=-1 artinya kita memakai seluruh \"otak\" CPU laptop agar cepat selesai\n",
    "svm_grid = GridSearchCV(SVC(probability=True, random_state=42), param_svm, cv=3, verbose=1, n_jobs=-1)\n",
    "svm_grid.fit(X_train, y_train) \n",
    "\n",
    "best_svm = svm_grid.best_estimator_\n",
    "joblib.dump(best_svm, 'models/model_svm.pkl')\n",
    "print(f\"   ✅ SVM Selesai (Akurasi Validasi: {svm_grid.best_score_*100:.2f}%)\")\n",
    "\n",
    "# ---------------------------------------------------------\n",
    "# 3. LATIH KNN (METRIC COSINE)\n",
    "# ---------------------------------------------------------\n",
    "print(\"\\n🚀 Melatih KNN (Wajib Cosine)...\")\n",
    "\n",
    "param_knn = {\n",
    "    'n_neighbors': [3, 5, 7, 9, 11, 15], \n",
    "    'metric': ['cosine'] \n",
    "}\n",
    "\n",
    "knn_grid = GridSearchCV(KNeighborsClassifier(), param_knn, cv=3, verbose=1, n_jobs=-1)\n",
    "knn_grid.fit(X_train, y_train)\n",
    "\n",
    "best_knn = knn_grid.best_estimator_\n",
    "joblib.dump(best_knn, 'models/model_knn.pkl')\n",
    "print(f\"   ✅ KNN Selesai (Best K: {knn_grid.best_params_['n_neighbors']})\")\n",
    "\n",
    "# ---------------------------------------------------------\n",
    "# 4. LATIH ENSEMBLE (SVM + KNN)\n",
    "# ---------------------------------------------------------\n",
    "print(\"\\n🚀 Melatih ENSEMBLE (Voting SVM + KNN)...\")\n",
    "\n",
    "# Gabungkan dua model terbaik\n",
    "ensemble_model = VotingClassifier(\n",
    "    estimators=[\n",
    "        ('svm', best_svm), \n",
    "        ('knn', best_knn)\n",
    "    ],\n",
    "    voting='soft',\n",
    "    weights=[2, 1] # SVM kita beri bobot suara lebih tinggi\n",
    ")\n",
    "\n",
    "ensemble_model.fit(X_train, y_train)\n",
    "joblib.dump(ensemble_model, 'models/model_ensemble.pkl')\n",
    "print(\"   ✅ Ensemble Selesai.\")\n",
    "\n",
    "print(\"\\n==================================================\")\n",
    "print(\"🎉 TRAINING DATA MURNI SELESAI!\")\n",
    "print(\"👉 Silakan jalankan '3_evaluasi.py' untuk melihat hasil akhirnya.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "38511828",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3 evaluasi hasil model\n",
    "import joblib\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix\n",
    "import os\n",
    "\n",
    "print(\"📊 [TAHAP 3] Evaluasi Hasil Model...\")\n",
    "\n",
    "# 0. CEK KEAMANAN\n",
    "if not os.path.exists('output/X_test.pkl'):\n",
    "    raise FileNotFoundError(\"❌ File 'output/X_test.pkl' tidak ditemukan! Pastikan Tahap 1 sudah dijalankan.\")\n",
    "if not os.path.exists('models/model_svm.pkl'):\n",
    "    raise FileNotFoundError(\"❌ File 'models/model_svm.pkl' tidak ditemukan! Pastikan Tahap 2 sudah selesai 100%.\")\n",
    "\n",
    "# Bikin folder images otomatis\n",
    "os.makedirs('images', exist_ok=True)\n",
    "\n",
    "# 1. Ambil Data Uji\n",
    "print(\"   - Memuat data uji...\")\n",
    "X_test = joblib.load('output/X_test.pkl')\n",
    "y_test = joblib.load('output/y_test.pkl')\n",
    "\n",
    "# 2. Daftar Model (CUMA 3 SEKARANG)\n",
    "print(\"   - Memuat model-model AI...\")\n",
    "daftar_model = {\n",
    "    \"SVM\": joblib.load('models/model_svm.pkl'),\n",
    "    \"KNN\": joblib.load('models/model_knn.pkl'),\n",
    "    \"Ensemble\": joblib.load('models/model_ensemble.pkl')\n",
    "}\n",
    "\n",
    "# 3. Loop Evaluasi\n",
    "for nama, model in daftar_model.items():\n",
    "    print(f\"\\n==========================================\")\n",
    "    print(f\"--- Evaluasi Model: {nama} ---\")\n",
    "    print(f\"==========================================\")\n",
    "\n",
    "    # Lakukan Prediksi\n",
    "    y_pred = model.predict(X_test)\n",
    "    \n",
    "    # Hitung Akurasi\n",
    "    acc = accuracy_score(y_test, y_pred)\n",
    "    print(f\"🎯 Akurasi {nama}: {acc*100:.2f}%\\n\")\n",
    "    \n",
    "    # Laporan Lengkap (Precision, Recall, F1-Score)\n",
    "    print(\"📋 Laporan Klasifikasi:\")\n",
    "    print(classification_report(y_test, y_pred))\n",
    "\n",
    "    # Bikin Grafik Confusion Matrix\n",
    "    cm = confusion_matrix(y_test, y_pred)\n",
    "    plt.figure(figsize=(7, 5))\n",
    "    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n",
    "                annot_kws={\"size\": 14}) # Angka di dalam kotak diperbesar\n",
    "    \n",
    "    # Hiasan Grafik (Lebih rapi untuk masuk ke buku Skripsi)\n",
    "    plt.title(f'Confusion Matrix - {nama}\\n(Akurasi: {acc*100:.2f}%)', fontsize=14, fontweight='bold', pad=15)\n",
    "    plt.xlabel('Prediksi Mesin', fontsize=12, fontweight='bold')\n",
    "    plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff1f299c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3.2 Menampilkan Distribusi Data\n",
    "# Menampilkan perbandingan jumlah data dan tampilan grafik perbandingan data\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import os\n",
    "\n",
    "# Pastikan folder images ada untuk menyimpan hasil\n",
    "os.makedirs('images', exist_ok=True)\n",
    "\n",
    "# ==========================================\n",
    "# 1. LOAD DATA\n",
    "# ==========================================\n",
    "nama_file = r'd:\\project skripsi machine learning intoleransi\\virtualEnvironment\\dataset\\dataYangDiPakai\\data_label_revisi_goblog.csv'\n",
    "\n",
    "print(f\"📂 Membaca file: {nama_file}...\")\n",
    "df = pd.read_csv(nama_file, sep=';') \n",
    "\n",
    "# ==========================================\n",
    "# 2. HITUNG JUMLAH LABEL\n",
    "# ==========================================\n",
    "# Kita pastikan kolom 'label' ada\n",
    "if 'label' not in df.columns:\n",
    "    raise ValueError(\"❌ Kolom 'label' tidak ditemukan di dataset! Coba cek separatornya (sep=';' atau sep=',')\")\n",
    "\n",
    "jumlah_label = df['label'].value_counts().sort_index()\n",
    "\n",
    "print(\"\\n📊 STATISTIK JUMLAH DATA:\")\n",
    "print(\"-\" * 30)\n",
    "label_names = {0: \"Netral (0)\", 1: \"Kritik (1)\", 2: \"Hujatan (2)\"}\n",
    "for lbl, count in jumlah_label.items():\n",
    "    print(f\"   {label_names.get(lbl, lbl)}: {count} data\")\n",
    "print(\"-\" * 30)\n",
    "print(f\"   TOTAL: {len(df)} data\")\n",
    "\n",
    "# ==========================================\n",
    "# 3. BUAT GRAFIK (VISUALISASI)\n",
    "# ==========================================\n",
    "plt.figure(figsize=(8, 6)) # Ukuran gambar (Lebar, Tinggi)\n",
    "\n",
    "# Bikin Bar Chart warna-warni (tambah hue=... agar tidak muncul warning di versi Seaborn terbaru)\n",
    "ax = sns.barplot(x=jumlah_label.index, y=jumlah_label.values, hue=jumlah_label.index, palette='viridis', legend=False)\n",
    "\n",
    "# Hiasan Grafik\n",
    "plt.title('Perbandingan Jumlah Data per Label', fontsize=16, fontweight='bold', pad=15)\n",
    "plt.xlabel('Kategori Label', fontsize=12, fontweight='bold')\n",
    "plt.ylabel('Jumlah Data', fontsize=12, fontweight='bold')\n",
    "\n",
    "# Pastikan urutan label sesuai dengan 0, 1, 2\n",
    "urutan_label = sorted(jumlah_label.index.tolist())\n",
    "plt.xticks(ticks=range(len(urutan_label)), labels=['0\\n(Netral)', '1\\n(Kritik)', '2\\n(Hujatan)'])\n",
    "plt.grid(axis='y', linestyle='--', alpha=0.5)\n",
    "\n",
    "# Tampilkan Angka di Atas Batang\n",
    "for p in ax.patches:\n",
    "    ax.annotate(f'{int(p.get_height())}', \n",
    "                (p.get_x() + p.get_width() / 2., p.get_height()), \n",
    "                ha='center', va='center', \n",
    "                xytext=(0, 10), \n",
    "                textcoords='offset points',\n",
    "                fontsize=14, fontweight='bold', color='black')\n",
    "\n",
    "# Simpan Gambar di dalam folder 'images'\n",
    "nama_gambar = 'images/grafik_distribusi_data.png'\n",
    "plt.savefig(nama_gambar, dpi=300, bbox_inches='tight')\n",
    "print(f\"\\n🖼️ Grafik berhasil disimpan: {nama_gambar}\")\n",
    "\n",
    "# Tampilkan gambar di layar\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c2325607",
   "metadata": {},
   "outputs": [],
   "source": [
    "import joblib\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import math\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.model_selection import cross_val_score, GridSearchCV\n",
    "\n",
    "print(\"🔬 [EKSPERIMEN] Membandingkan 3 Metode Mencari Nilai K Terbaik...\")\n",
    "\n",
    "# 0. CEK KEAMANAN\n",
    "if not os.path.exists('output/X_train.pkl'):\n",
    "    raise FileNotFoundError(\"❌ File 'output/X_train.pkl' tidak ditemukan! Pastikan Tahap 1 sudah dijalankan.\")\n",
    "\n",
    "# Pastikan folder images ada\n",
    "os.makedirs('images', exist_ok=True)\n",
    "\n",
    "# 1. LOAD DATA\n",
    "X_train = joblib.load('output/X_train.pkl')\n",
    "y_train = joblib.load('output/y_train.pkl')\n",
    "jumlah_data = X_train.shape[0]\n",
    "\n",
    "print(f\"   - Jumlah Data Latih: {jumlah_data} baris\")\n",
    "print(\"-\" * 50)\n",
    "\n",
    "results = [] # Untuk menyimpan hasil perbandingan\n",
    "\n",
    "# =========================================================\n",
    "# METODE 1: AKAR KUADRAT (Square Root Rule)\n",
    "# Rumus: K = Akar(Total Data)\n",
    "# =========================================================\n",
    "print(\"1️⃣ Menguji Metode Akar Kuadrat...\")\n",
    "k_sqrt = int(math.sqrt(jumlah_data))\n",
    "\n",
    "# Aturan: K harus ganjil biar gak seri (draw)\n",
    "if k_sqrt % 2 == 0:\n",
    "    k_sqrt += 1\n",
    "\n",
    "# Uji Akurasinya (Pakai n_jobs=-1 biar ngebut)\n",
    "knn_sq = KNeighborsClassifier(n_neighbors=k_sqrt, metric='cosine')\n",
    "scores_sq = cross_val_score(knn_sq, X_train, y_train, cv=5, scoring='accuracy', n_jobs=-1)\n",
    "acc_sq = scores_sq.mean() * 100\n",
    "\n",
    "print(f\"   -> Hasil: K={k_sqrt}, Akurasi={acc_sq:.2f}%\")\n",
    "results.append({'Metode': 'Akar Kuadrat', 'K': k_sqrt, 'Akurasi': acc_sq})\n",
    "\n",
    "# =========================================================\n",
    "# METODE 2: ELBOW METHOD (Metode Siku)\n",
    "# Coba manual dari 1 sampai 40, lalu cari error terkecil\n",
    "# =========================================================\n",
    "print(\"\\n2️⃣ Menguji Metode Elbow (Looping 1-40)...\")\n",
    "print(\"   (Tunggu sebentar, sedang menghitung manual...)\")\n",
    "error_rates = []\n",
    "acc_rates = []\n",
    "k_range = range(1, 41, 2) # Coba angka ganjil: 1, 3, 5, ... 39\n",
    "\n",
    "best_k_elbow = 0\n",
    "best_acc_elbow = 0\n",
    "\n",
    "for k in k_range:\n",
    "    knn = KNeighborsClassifier(n_neighbors=k, metric='cosine')\n",
    "    # Pakai n_jobs=-1 di sini juga biar loopingnya gak kelamaan\n",
    "    scores = cross_val_score(knn, X_train, y_train, cv=5, scoring='accuracy', n_jobs=-1)\n",
    "    acc = scores.mean()\n",
    "\n",
    "    # Simpan data buat grafik\n",
    "    acc_rates.append(acc)\n",
    "    error_rates.append(1 - acc) # Error = 100% - Akurasi\n",
    "\n",
    "    # Cek apakah ini rekor terbaik?\n",
    "    if acc > best_acc_elbow:\n",
    "        best_acc_elbow = acc\n",
    "        best_k_elbow = k\n",
    "\n",
    "print(f\"   -> Hasil Terbaik di Range Ini: K={best_k_elbow}, Akurasi={best_acc_elbow*100:.2f}%\")\n",
    "results.append({'Metode': 'Elbow (Manual)', 'K': best_k_elbow, 'Akurasi': best_acc_elbow*100})\n",
    "\n",
    "# Bikin Grafik Elbow\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.plot(k_range, error_rates, color='red', linestyle='dashed', marker='o',\n",
    "         markerfacecolor='blue', markersize=8)\n",
    "plt.title('Grafik Elbow (Mencari Error Terkecil)', fontsize=14, fontweight='bold', pad=15)\n",
    "plt.xlabel('Nilai K', fontsize=12, fontweight='bold')\n",
    "plt.ylabel('Tingkat Error', fontsize=12, fontweight='bold')\n",
    "plt.grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "# Simpan ke folder images\n",
    "nama_gambar_elbow = 'images/grafik_elbow_knn.png'\n",
    "plt.savefig(nama_gambar_elbow, dpi=300, bbox_inches='tight')\n",
    "print(f\"   🖼️ Grafik Elbow tersimpan: {nama_gambar_elbow}\")\n",
    "plt.close() # Tutup grafik biar memori lega\n",
    "\n",
    "# =========================================================\n",
    "# METODE 3: GRID SEARCH CV (Validasi Silang)\n",
    "# Ini metode paling 'Sultan' dan Valid\n",
    "# =========================================================\n",
    "print(\"\\n3️⃣ Menguji Metode Grid Search CV (Otomatis)...\")\n",
    "param_grid = {'n_neighbors': [3, 5, 7, 9, 11, 15, 19, 21, 25, 29]}\n",
    "grid = GridSearchCV(KNeighborsClassifier(metric='cosine'), param_grid, cv=5, scoring='accuracy', n_jobs=-1)\n",
    "grid.fit(X_train, y_train)\n",
    "\n",
    "k_grid = grid.best_params_['n_neighbors']\n",
    "acc_grid = grid.best_score_ * 100\n",
    "\n",
    "print(f\"   -> Hasil: K={k_grid}, Akurasi={acc_grid:.2f}%\")\n",
    "results.append({'Metode': 'Grid Search CV', 'K': k_grid, 'Akurasi': acc_grid})\n",
    "\n",
    "# =========================================================\n",
    "# KESIMPULAN AKHIR\n",
    "# =========================================================\n",
    "print(\"\\n\" + \"=\"*50)\n",
    "print(\"🏆 TABEL PERBANDINGAN METODE PENENTUAN K\")\n",
    "print(\"=\"*50)\n",
    "df_res = pd.DataFrame(results)\n",
    "print(df_res.to_string(index=False))\n",
    "print(\"-\" * 50)\n",
    "\n",
    "# Cari pemenang\n",
    "best_method = df_res.loc[df_res['Akurasi'].idxmax()]\n",
    "print(f\"✅ REKOMENDASI: Gunakan K = {best_method['K']}\")\n",
    "print(f\"   (Berdasarkan metode {best_method['Metode']} dengan akurasi tertinggi)\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "virtualEnvironment",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
