inisiasi project

This commit is contained in:
E41222120_syamaidzar adani syah 2026-07-28 08:42:36 +07:00
commit 07282b085c
56 changed files with 43762 additions and 0 deletions

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# Isi virtual environment (ratusan MB, JANGAN diupload).
# Folder virtualEnvironment/ sendiri TIDAK diabaikan, karena kode
# skripsi (app.py, dataset/, models/, dll) tersimpan di dalamnya.
virtualEnvironment/Lib/
virtualEnvironment/Scripts/
virtualEnvironment/Include/
virtualEnvironment/share/
virtualEnvironment/etc/
virtualEnvironment/pyvenv.cfg
venv/
.venv/
env/
# Python cache
__pycache__/
*.py[cod]
*.egg-info/
# Jupyter
.ipynb_checkpoints/
# Secrets / konfigurasi lokal
.env
.streamlit/secrets.toml
# Data privat (tidak diupload)
data yang aku simpan/
# File catatan lokal
catatan perbaikan kata.txt
logika.py
# OS
Thumbs.db
desktop.ini
.DS_Store

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.11.9

9
requirements.txt Normal file
View File

@ -0,0 +1,9 @@
streamlit==1.54.0
pandas==2.3.0
numpy==2.2.0
scikit-learn==1.8.0
joblib==1.5.3
Sastrawi==1.0.1
matplotlib==3.10.8
seaborn==0.13.2
plotly==6.5.2

1
runtime.txt Normal file
View File

@ -0,0 +1 @@
python-3.11.9

23
virtualEnvironment/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Virtual environment packages (folder besar, tidak perlu)
Lib/
Scripts/
Include/
pyvenv.cfg
# JupyterLab config dan share (tidak perlu)
etc/
share/
# Python cache
__pycache__/
*.pyc
*.pyo
# Sistem
.env
.DS_Store
Thumbs.db
# CATATAN: dataset/, images/, output/, *.ipynb, dan *_copy.py
# sengaja TIDAK diabaikan -- semuanya bagian dari bukti proses skripsi
# dan ikut diupload sebagai arsip.

View File

@ -0,0 +1,128 @@
# 4 mencari nilai K
import joblib
import pandas as pd
import numpy as np
import math
import matplotlib.pyplot as plt
import os
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score, GridSearchCV
print("🔬 [EKSPERIMEN] Membandingkan 3 Metode Mencari Nilai K Terbaik...")
# --- PENYESUAIAN FOLDER ---
FOLDER_OUTPUT = 'virtualEnvironment/output'
FOLDER_IMAGES = 'virtualEnvironment/images'
# 0. CEK KEAMANAN
file_xtrain = f'{FOLDER_OUTPUT}/X_train.pkl'
if not os.path.exists(file_xtrain):
raise FileNotFoundError(f"❌ File '{file_xtrain}' tidak ditemukan! Pastikan Tahap 1 sudah dijalankan.")
# Pastikan folder images ada di dalam virtualEnvironment
os.makedirs(FOLDER_IMAGES, exist_ok=True)
# 1. LOAD DATA
X_train = joblib.load(file_xtrain)
y_train = joblib.load(f'{FOLDER_OUTPUT}/y_train.pkl')
jumlah_data = X_train.shape[0]
print(f" - Jumlah Data Latih: {jumlah_data} baris")
print("-" * 50)
results = [] # Untuk menyimpan hasil perbandingan
# =========================================================
# METODE 1: AKAR KUADRAT (Square Root Rule)
# Rumus: K = Akar(Total Data)
# =========================================================
print("1⃣ Menguji Metode Akar Kuadrat...")
k_sqrt = int(math.sqrt(jumlah_data))
# Aturan: K harus ganjil biar gak seri (draw)
if k_sqrt % 2 == 0:
k_sqrt += 1
# Uji Akurasinya (Pakai n_jobs=-1 biar ngebut)
knn_sq = KNeighborsClassifier(n_neighbors=k_sqrt, metric='cosine')
scores_sq = cross_val_score(knn_sq, X_train, y_train, cv=5, scoring='accuracy', n_jobs=-1)
acc_sq = scores_sq.mean() * 100
print(f" -> Hasil: K={k_sqrt}, Akurasi={acc_sq:.2f}%")
results.append({'Metode': 'Akar Kuadrat', 'K': k_sqrt, 'Akurasi': acc_sq})
# =========================================================
# METODE 2: ELBOW METHOD (Metode Siku)
# Coba manual dari 1 sampai 40, lalu cari error terkecil
# =========================================================
print("\n2⃣ Menguji Metode Elbow (Looping 1-40)...")
print(" (Tunggu sebentar, sedang menghitung manual...)")
error_rates = []
acc_rates = []
k_range = range(1, 41, 2) # Coba angka ganjil: 1, 3, 5, ... 39
best_k_elbow = 0
best_acc_elbow = 0
for k in k_range:
knn = KNeighborsClassifier(n_neighbors=k, metric='cosine')
# Pakai n_jobs=-1 di sini juga biar loopingnya gak kelamaan
scores = cross_val_score(knn, X_train, y_train, cv=5, scoring='accuracy', n_jobs=-1)
acc = scores.mean()
# Simpan data buat grafik
acc_rates.append(acc)
error_rates.append(1 - acc) # Error = 100% - Akurasi
# Cek apakah ini rekor terbaik?
if acc > best_acc_elbow:
best_acc_elbow = acc
best_k_elbow = k
print(f" -> Hasil Terbaik di Range Ini: K={best_k_elbow}, Akurasi={best_acc_elbow*100:.2f}%")
results.append({'Metode': 'Elbow (Manual)', 'K': best_k_elbow, 'Akurasi': best_acc_elbow*100})
# Bikin Grafik Elbow
plt.figure(figsize=(10, 6))
plt.plot(k_range, error_rates, color='red', linestyle='dashed', marker='o',
markerfacecolor='blue', markersize=8)
plt.title('Grafik Elbow (Mencari Error Terkecil)', fontsize=14, fontweight='bold', pad=15)
plt.xlabel('Nilai K', fontsize=12, fontweight='bold')
plt.ylabel('Tingkat Error', fontsize=12, fontweight='bold')
plt.grid(True, linestyle='--', alpha=0.6)
# Simpan ke folder images
nama_gambar_elbow = f'{FOLDER_IMAGES}/grafik_elbow_knn.png'
plt.savefig(nama_gambar_elbow, dpi=300, bbox_inches='tight')
print(f" 🖼️ Grafik Elbow tersimpan: {nama_gambar_elbow}")
plt.close() # Tutup grafik biar memori lega
# =========================================================
# METODE 3: GRID SEARCH CV (Validasi Silang)
# Ini metode paling 'Sultan' dan Valid
# =========================================================
print("\n3⃣ Menguji Metode Grid Search CV (Otomatis)...")
param_grid = {'n_neighbors': [3, 5, 7, 9, 11, 15, 19, 21, 25, 29]}
grid = GridSearchCV(KNeighborsClassifier(metric='cosine'), param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid.fit(X_train, y_train)
k_grid = grid.best_params_['n_neighbors']
acc_grid = grid.best_score_ * 100
print(f" -> Hasil: K={k_grid}, Akurasi={acc_grid:.2f}%")
results.append({'Metode': 'Grid Search CV', 'K': k_grid, 'Akurasi': acc_grid})
# =========================================================
# KESIMPULAN AKHIR
# =========================================================
print("\n" + "="*50)
print("🏆 TABEL PERBANDINGAN METODE PENENTUAN K")
print("="*50)
df_res = pd.DataFrame(results)
print(df_res.to_string(index=False))
print("-" * 50)
# Cari pemenang
best_method = df_res.loc[df_res['Akurasi'].idxmax()]
print(f"✅ REKOMENDASI: Gunakan K = {best_method['K']}")
print(f" (Berdasarkan metode {best_method['Metode']} dengan akurasi tertinggi)")

1207
virtualEnvironment/app.py Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
# 3.2 Menampilkan Distribusi Data
# Menampilkan perbandingan jumlah data dan tampilan grafik perbandingan data
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import os
# Pastikan folder images ada untuk menyimpan hasil
os.makedirs('images', exist_ok=True)
# ==========================================
# 1. LOAD DATA
# ==========================================
nama_file = r'd:\project skripsi machine learning intoleransi\virtualEnvironment\dataset\dataYangDiPakai\data_label_revisi_goblog.csv'
print(f"📂 Membaca file: {nama_file}...")
df = pd.read_csv(nama_file, sep=';')
# ==========================================
# 2. HITUNG JUMLAH LABEL
# ==========================================
# Kita pastikan kolom 'label' ada
if 'label' not in df.columns:
raise ValueError("❌ Kolom 'label' tidak ditemukan di dataset! Coba cek separatornya (sep=';' atau sep=',')")
jumlah_label = df['label'].value_counts().sort_index()
print("\n📊 STATISTIK JUMLAH DATA:")
print("-" * 30)
label_names = {0: "Netral (0)", 1: "Kritik (1)", 2: "Hujatan (2)"}
for lbl, count in jumlah_label.items():
print(f" {label_names.get(lbl, lbl)}: {count} data")
print("-" * 30)
print(f" TOTAL: {len(df)} data")
# ==========================================
# 3. BUAT GRAFIK (VISUALISASI)
# ==========================================
plt.figure(figsize=(8, 6)) # Ukuran gambar (Lebar, Tinggi)
# Bikin Bar Chart warna-warni (tambah hue=... agar tidak muncul warning di versi Seaborn terbaru)
ax = sns.barplot(x=jumlah_label.index, y=jumlah_label.values, hue=jumlah_label.index, palette='viridis', legend=False)
# Hiasan Grafik
plt.title('Perbandingan Jumlah Data per Label', fontsize=16, fontweight='bold', pad=15)
plt.xlabel('Kategori Label', fontsize=12, fontweight='bold')
plt.ylabel('Jumlah Data', fontsize=12, fontweight='bold')
# Pastikan urutan label sesuai dengan 0, 1, 2
urutan_label = sorted(jumlah_label.index.tolist())
plt.xticks(ticks=range(len(urutan_label)), labels=['0\n(Netral)', '1\n(Kritik)', '2\n(Hujatan)'])
plt.grid(axis='y', linestyle='--', alpha=0.5)
# Tampilkan Angka di Atas Batang
for p in ax.patches:
ax.annotate(f'{int(p.get_height())}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center',
xytext=(0, 10),
textcoords='offset points',
fontsize=14, fontweight='bold', color='black')
# Simpan Gambar di dalam folder 'images'
nama_gambar = 'virtualEnvironment/images/grafik_distribusi_data.png'
plt.savefig(nama_gambar, dpi=300, bbox_inches='tight')
print(f"\n🖼️ Grafik berhasil disimpan: {nama_gambar}")
# Tampilkan gambar di layar
plt.show()

View File

@ -0,0 +1,46 @@
# Distribusi prediksi SVM pada keseluruhan dataset
import joblib
import pandas as pd
import numpy as np
FOLDER_OUTPUT = 'virtualEnvironment/output'
FOLDER_MODELS = 'virtualEnvironment/models'
# Load model dan vectorizer
svm_model = joblib.load(f'{FOLDER_MODELS}/model_svm.pkl')
vectorizer = joblib.load(f'{FOLDER_MODELS}/vectorizer_tfidf.pkl')
# Load dataset asli
FILE_DATA = r'D:\project skripsi machine learning intoleransi\virtualEnvironment\dataset\dataYangDiPakai\data_label_3_kategori_v2 - Copy.csv'
df = pd.read_csv(FILE_DATA, sep=';')
df = df.dropna(subset=['clean_text', 'label']).reset_index(drop=True)
print(f"Total tweet di dataset: {len(df)}")
# Transform pakai vectorizer yang sama
X_all = vectorizer.transform(df['clean_text'].astype(str))
# Prediksi semua tweet
y_pred_all = svm_model.predict(X_all)
# Hitung distribusi prediksi
label_map = {0: 'Netral', 1: 'Rasional Negatif', 2: 'Cacimaki/Intoleransi'}
distribusi_prediksi = pd.Series(y_pred_all).value_counts().sort_index()
print("\n📊 DISTRIBUSI PREDIKSI SVM PADA KESELURUHAN DATA:")
for label, jumlah in distribusi_prediksi.items():
persen = jumlah / len(y_pred_all) * 100
print(f" - {label_map[label]}: {jumlah} tweet ({persen:.2f}%)")
# Bandingkan dengan distribusi label asli
print("\n📊 DISTRIBUSI LABEL ASLI:")
distribusi_asli = df['label'].astype(int).value_counts().sort_index()
for label, jumlah in distribusi_asli.items():
persen = jumlah / len(df) * 100
print(f" - {label_map[label]}: {jumlah} tweet ({persen:.2f}%)")
# Simpan dataframe lengkap dengan prediksi (untuk eksplorasi lanjut)
df['prediksi_svm'] = [label_map[x] for x in y_pred_all]
df['label_asli_nama'] = [label_map[x] for x in df['label'].astype(int)]
df.to_csv(f'{FOLDER_OUTPUT}/dataset_dengan_prediksi.csv', index=False, sep=';')
print(f"\n💾 Dataset dengan prediksi tersimpan: {FOLDER_OUTPUT}/dataset_dengan_prediksi.csv")

View File

@ -0,0 +1,82 @@
# 3 evaluasi hasil model
import joblib
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import os
print("📊 [TAHAP 3] Evaluasi Hasil Model...")
# --- PENYESUAIAN FOLDER ---
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
FOLDER_OUTPUT = os.path.join(BASE_DIR, 'output')
FOLDER_MODELS = os.path.join(BASE_DIR, 'models')
FOLDER_IMAGES = os.path.join(BASE_DIR, 'images')
# 0. CEK KEAMANAN
file_xtest = f'{FOLDER_OUTPUT}/X_test.pkl'
file_svm = f'{FOLDER_MODELS}/model_svm.pkl'
if not os.path.exists(file_xtest):
raise FileNotFoundError(f"❌ File '{file_xtest}' tidak ditemukan! Pastikan Tahap 1 sudah dijalankan.")
if not os.path.exists(file_svm):
raise FileNotFoundError(f"❌ File '{file_svm}' tidak ditemukan! Pastikan Tahap 2 sudah selesai 100%.")
# Bikin folder images otomatis di dalam virtualEnvironment
os.makedirs(FOLDER_IMAGES, exist_ok=True)
# 1. Ambil Data Uji
print(" - Memuat data uji...")
X_test = joblib.load(file_xtest)
y_test = joblib.load(f'{FOLDER_OUTPUT}/y_test.pkl')
# 2. Daftar Model
print(" - Memuat model-model AI...")
daftar_model = {
"SVM": joblib.load(file_svm),
"KNN": joblib.load(f'{FOLDER_MODELS}/model_knn.pkl'),
"Ensemble": joblib.load(f'{FOLDER_MODELS}/model_ensemble.pkl')
}
# 3. Loop Evaluasi
for nama, model in daftar_model.items():
print(f"\n==========================================")
print(f"--- Evaluasi Model: {nama} ---")
print(f"==========================================")
# Lakukan Prediksi
y_pred = model.predict(X_test)
# Hitung Akurasi
acc = accuracy_score(y_test, y_pred)
print(f"🎯 Akurasi {nama}: {acc*100:.2f}%\n")
# Laporan Lengkap (Precision, Recall, F1-Score)
print("📋 Laporan Klasifikasi:")
print(classification_report(y_test, y_pred))
# Bikin Grafik Confusion Matrix
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(7, 5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
annot_kws={"size": 14}) # Angka di dalam kotak diperbesar
# Hiasan Grafik (Lebih rapi untuk masuk ke buku Skripsi)
plt.title(f'Confusion Matrix - {nama}\n(Akurasi: {acc*100:.2f}%)', fontsize=14, fontweight='bold', pad=15)
plt.xlabel('Prediksi Mesin', fontsize=12, fontweight='bold')
plt.ylabel('Label Asli', fontsize=12, fontweight='bold')
# Tambahan Keterangan Sumbu X dan Y biar dosen mudah baca
plt.xticks(ticks=[0.5, 1.5, 2.5], labels=['0 (Netral)', '1 (rasional negatif)', '2 (cacimaki/intoleransi)'])
plt.yticks(ticks=[0.5, 1.5, 2.5], labels=['0 (Netral)', '1 (rasional negatif)', '2 (cacimaki/intoleransi)'], rotation=0)
# Simpan Gambar dengan resolusi tinggi (dpi=300)
nama_file = f'{FOLDER_IMAGES}/cm_{nama}.png'
plt.savefig(nama_file, bbox_inches='tight', dpi=300)
print(f"🖼️ Grafik Confusion Matrix tersimpan: {nama_file}")
# Tutup plot supaya tidak numpuk di memori
plt.close()
print("\n🎉 SEMUA TAHAPAN SELESAI!")
print(f"Cek folder '{FOLDER_IMAGES}' untuk melihat gambar Confusion Matrix-nya ya.")

View File

@ -0,0 +1,210 @@
# 3 evaluasi hasil model
import joblib
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import os
print("📊 [TAHAP 3] Evaluasi Hasil Model...")
# --- PENYESUAIAN FOLDER ---
FOLDER_OUTPUT = 'virtualEnvironment/output'
FOLDER_MODELS = 'virtualEnvironment/models'
FOLDER_IMAGES = 'virtualEnvironment/images'
# 0. CEK KEAMANAN
file_xtest = f'{FOLDER_OUTPUT}/X_test.pkl'
file_svm = f'{FOLDER_MODELS}/model_svm.pkl'
if not os.path.exists(file_xtest):
raise FileNotFoundError(f"❌ File '{file_xtest}' tidak ditemukan! Pastikan Tahap 1 sudah dijalankan.")
if not os.path.exists(file_svm):
raise FileNotFoundError(f"❌ File '{file_svm}' tidak ditemukan! Pastikan Tahap 2 sudah selesai 100%.")
# Bikin folder images otomatis di dalam virtualEnvironment
os.makedirs(FOLDER_IMAGES, exist_ok=True)
# 1. Ambil Data Uji
print(" - Memuat data uji...")
X_test = joblib.load(file_xtest)
y_test = joblib.load(f'{FOLDER_OUTPUT}/y_test.pkl')
# 2. Daftar Model
print(" - Memuat model-model AI...")
daftar_model = {
"SVM": joblib.load(file_svm),
"KNN": joblib.load(f'{FOLDER_MODELS}/model_knn.pkl'),
"Ensemble": joblib.load(f'{FOLDER_MODELS}/model_ensemble.pkl')
}
# 3. Loop Evaluasi
for nama, model in daftar_model.items():
print(f"\n==========================================")
print(f"--- Evaluasi Model: {nama} ---")
print(f"==========================================")
# Lakukan Prediksi
y_pred = model.predict(X_test)
# Hitung Akurasi
acc = accuracy_score(y_test, y_pred)
print(f"🎯 Akurasi {nama}: {acc*100:.2f}%\n")
# Laporan Lengkap (Precision, Recall, F1-Score)
print("📋 Laporan Klasifikasi:")
print(classification_report(y_test, y_pred))
# Bikin Grafik Confusion Matrix
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(7, 5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
annot_kws={"size": 14}) # Angka di dalam kotak diperbesar
# Hiasan Grafik (Lebih rapi untuk masuk ke buku Skripsi)
plt.title(f'Confusion Matrix - {nama}\n(Akurasi: {acc*100:.2f}%)', fontsize=14, fontweight='bold', pad=15)
plt.xlabel('Prediksi Mesin', fontsize=12, fontweight='bold')
plt.ylabel('Label Asli', fontsize=12, fontweight='bold')
# Tambahan Keterangan Sumbu X dan Y biar dosen mudah baca
plt.xticks(ticks=[0.5, 1.5, 2.5], labels=['0 (Netral)', '1 (rasional negatif)', '2 (cacimaki/intoleransi)'])
plt.yticks(ticks=[0.5, 1.5, 2.5], labels=['0 (Netral)', '1 (rasional negatif)', '2 (cacimaki/intoleransi)'], rotation=0)
# Simpan Gambar dengan resolusi tinggi (dpi=300)
nama_file = f'{FOLDER_IMAGES}/cm_{nama}.png'
plt.savefig(nama_file, bbox_inches='tight', dpi=300)
print(f"🖼️ Grafik Confusion Matrix tersimpan: {nama_file}")
# Tutup plot supaya tidak numpuk di memori
plt.close()
print("\n🎉 SEMUA TAHAPAN SELESAI!")
print(f"Cek folder '{FOLDER_IMAGES}' untuk melihat gambar Confusion Matrix-nya ya.")
# 3 evaluasi hasil model
import joblib
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import os
print("📊 [TAHAP 3] Evaluasi Hasil Model...")
# --- PENYESUAIAN FOLDER ---
FOLDER_OUTPUT = 'virtualEnvironment/output'
FOLDER_MODELS = 'virtualEnvironment/models'
FOLDER_IMAGES = 'virtualEnvironment/images'
# 0. CEK KEAMANAN
file_xtest = f'{FOLDER_OUTPUT}/X_test.pkl'
file_svm = f'{FOLDER_MODELS}/model_svm.pkl'
if not os.path.exists(file_xtest):
raise FileNotFoundError(f"❌ File '{file_xtest}' tidak ditemukan! Pastikan Tahap 1 sudah dijalankan.")
if not os.path.exists(file_svm):
raise FileNotFoundError(f"❌ File '{file_svm}' tidak ditemukan! Pastikan Tahap 2 sudah selesai 100%.")
# Bikin folder images otomatis di dalam virtualEnvironment
os.makedirs(FOLDER_IMAGES, exist_ok=True)
# 1. Ambil Data Uji
print(" - Memuat data uji...")
X_test = joblib.load(file_xtest)
y_test = joblib.load(f'{FOLDER_OUTPUT}/y_test.pkl')
# 2. Daftar Model
print(" - Memuat model-model AI...")
daftar_model = {
"SVM": joblib.load(file_svm),
"KNN": joblib.load(f'{FOLDER_MODELS}/model_knn.pkl'),
"Ensemble": joblib.load(f'{FOLDER_MODELS}/model_ensemble.pkl')
}
# 3. Loop Evaluasi
for nama, model in daftar_model.items():
print(f"\n==========================================")
print(f"--- Evaluasi Model: {nama} ---")
print(f"==========================================")
# Lakukan Prediksi
y_pred = model.predict(X_test)
# Hitung Akurasi
acc = accuracy_score(y_test, y_pred)
print(f"🎯 Akurasi {nama}: {acc*100:.2f}%\n")
# Laporan Lengkap (Precision, Recall, F1-Score)
print("📋 Laporan Klasifikasi:")
print(classification_report(y_test, y_pred))
# Bikin Grafik Confusion Matrix
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(7, 5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
annot_kws={"size": 14}) # Angka di dalam kotak diperbesar
# Hiasan Grafik (Lebih rapi untuk masuk ke buku Skripsi)
plt.title(f'Confusion Matrix - {nama}\n(Akurasi: {acc*100:.2f}%)', fontsize=14, fontweight='bold', pad=15)
plt.xlabel('Prediksi Mesin', fontsize=12, fontweight='bold')
plt.ylabel('Label Asli', fontsize=12, fontweight='bold')
# Tambahan Keterangan Sumbu X dan Y biar dosen mudah baca
plt.xticks(ticks=[0.5, 1.5, 2.5], labels=['0 (Netral)', '1 (rasional negatif)', '2 (cacimaki/intoleransi)'])
plt.yticks(ticks=[0.5, 1.5, 2.5], labels=['0 (Netral)', '1 (rasional negatif)', '2 (cacimaki/intoleransi)'], rotation=0)
# Simpan Gambar dengan resolusi tinggi (dpi=300)
nama_file = f'{FOLDER_IMAGES}/cm_{nama}.png'
plt.savefig(nama_file, bbox_inches='tight', dpi=300)
print(f"🖼️ Grafik Confusion Matrix tersimpan: {nama_file}")
# Tutup plot supaya tidak numpuk di memori
plt.close()
print("\n🎉 SEMUA TAHAPAN SELESAI!")
print(f"Cek folder '{FOLDER_IMAGES}' untuk melihat gambar Confusion Matrix-nya ya.")
# ============================================
# TAMBAHAN: Ekstraksi Pola Kesalahan SVM
# ============================================
print("\n" + "="*60)
print("📋 EKSTRAKSI POLA KESALAHAN KLASIFIKASI (SVM)")
print("="*60)
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
# Load ulang data asli untuk dapat teks tweet
FILE_DATA = r'D:\project skripsi machine learning intoleransi\virtualEnvironment\dataset\dataYangDiPakai\data_label_3_kategori_v2 - Copy.csv'
df_asli = pd.read_csv(FILE_DATA, sep=';')
df_asli = df_asli.dropna(subset=['clean_text', 'label']).reset_index(drop=True)
# Replikasi split yang sama dengan Tahap 1 (random_state=42)
indeks_semua = np.arange(len(df_asli))
_, indeks_test = train_test_split(indeks_semua, test_size=0.2, random_state=42)
# Prediksi pakai SVM
y_pred_svm = daftar_model['SVM'].predict(X_test)
y_test_arr = np.array(y_test)
# Filter yang salah
salah_mask = y_pred_svm != y_test_arr
indeks_salah = indeks_test[salah_mask]
# Bikin dataframe kesalahan
label_map = {0: 'Netral', 1: 'Rasional Negatif', 2: 'Cacimaki/Intoleransi'}
df_salah = df_asli.loc[indeks_salah].copy()
df_salah['label_asli'] = [label_map[x] for x in y_test_arr[salah_mask]]
df_salah['label_prediksi'] = [label_map[x] for x in y_pred_svm[salah_mask]]
df_salah['pola_kesalahan'] = df_salah['label_asli'] + '' + df_salah['label_prediksi']
# Simpan ke CSV
nama_file_salah = f'{FOLDER_OUTPUT}/kesalahan_svm.csv'
df_salah[['clean_text', 'label_asli', 'label_prediksi', 'pola_kesalahan']].to_csv(
nama_file_salah, index=False, sep=';'
)
# Statistik pola kesalahan
print(f"\n📊 Total kesalahan SVM: {len(df_salah)} dari {len(y_test_arr)} data uji")
print(f"\n📊 Distribusi Pola Kesalahan:")
print(df_salah['pola_kesalahan'].value_counts().to_string())
print(f"\n💾 Detail tersimpan di: {nama_file_salah}")

View File

@ -0,0 +1,81 @@
# 4 Ekstraksi Feature Importance dari SVM Linear
import joblib
import numpy as np
import pandas as pd
import os
print("🔍 [TAHAP 4] Ekstraksi Kata Penanda per Kelas (SVM Linear)...")
FOLDER_OUTPUT = 'virtualEnvironment/output'
FOLDER_MODELS = 'virtualEnvironment/models'
# Load model SVM dan vectorizer TF-IDF
svm_model = joblib.load(f'{FOLDER_MODELS}/model_svm.pkl')
vectorizer = joblib.load(f'{FOLDER_MODELS}/vectorizer_tfidf.pkl')
# Cek apakah kernel linear (kalau bukan linear, coef_ tidak tersedia)
if svm_model.kernel != 'linear':
raise ValueError(f"❌ Kernel SVM bukan linear (saat ini: {svm_model.kernel}). Feature importance hanya bisa diekstrak dari kernel linear.")
# Ambil daftar kata dari vectorizer
daftar_kata = np.array(vectorizer.get_feature_names_out())
# Ambil koefisien SVM
# Untuk multiclass (3 kelas), shape coef_ = (n_classes, n_features) untuk linear SVC dengan ovr
# Tapi sklearn pakai ovo (one-vs-one) jadi shape = (n_classes*(n_classes-1)/2, n_features)
# Untuk 3 kelas: shape = (3, n_features) → kombinasi 0v1, 0v2, 1v2
koef = svm_model.coef_.toarray() if hasattr(svm_model.coef_, 'toarray') else svm_model.coef_
print(f" - Shape koefisien: {koef.shape}")
print(f" - Jumlah fitur (kata): {len(daftar_kata)}")
# Untuk OVO multiclass dengan 3 kelas:
# Baris 0: kelas 0 vs kelas 1 (positif → kelas 1, negatif → kelas 0)
# Baris 1: kelas 0 vs kelas 2 (positif → kelas 2, negatif → kelas 0)
# Baris 2: kelas 1 vs kelas 2 (positif → kelas 2, negatif → kelas 1)
label_map = {0: 'Netral', 1: 'Rasional_Negatif', 2: 'Cacimaki_Intoleransi'}
# Hitung "skor kelas" dengan menjumlahkan koefisien dari pasangan yang relevan
# Skor kelas 0 = -koef[0] (lawan kelas 1) + -koef[1] (lawan kelas 2)
# Skor kelas 1 = +koef[0] (vs kelas 0) + -koef[2] (vs kelas 2)
# Skor kelas 2 = +koef[1] (vs kelas 0) + +koef[2] (vs kelas 1)
skor_per_kelas = {
0: -koef[0] - koef[1], # Netral
1: koef[0] - koef[2], # Rasional Negatif
2: koef[1] + koef[2], # Cacimaki/Intoleransi
}
TOP_N = 20
print("\n" + "="*60)
print(f"📌 TOP {TOP_N} KATA PENANDA PER KELAS")
print("="*60)
hasil_semua = []
for kelas, skor in skor_per_kelas.items():
nama_kelas = label_map[kelas]
# Sort: ambil indeks dengan skor tertinggi
top_idx = np.argsort(skor)[::-1][:TOP_N]
top_kata = daftar_kata[top_idx]
top_skor = skor[top_idx]
print(f"\n🔹 Kelas {kelas} - {nama_kelas}:")
for i, (kata, s) in enumerate(zip(top_kata, top_skor), 1):
print(f" {i:2d}. {kata:<25s} (skor: {s:.4f})")
hasil_semua.append({
'kelas': nama_kelas,
'rank': i,
'kata': kata,
'skor': s
})
# Simpan ke CSV
df_hasil = pd.DataFrame(hasil_semua)
nama_file = f'{FOLDER_OUTPUT}/feature_importance_svm.csv'
df_hasil.to_csv(nama_file, index=False, sep=';')
print(f"\n💾 Hasil tersimpan di: {nama_file}")
print("\n✅ SELESAI!")

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
kelas;rank;kata;skor
Netral;1;orang;3.747452530756425
Netral;2;anjir;3.2343104518912824
Netral;3;bakar;3.1979186820620673
Netral;4;sakit;2.952096243485207
Netral;5;jangan;2.855109685945343
Netral;6;baik;2.829229032424351
Netral;7;kaca;2.689608636542816
Netral;8;jawa;2.611014652608258
Netral;9;bodoh;2.5463763166610147
Netral;10;resah;2.535202036506705
Netral;11;budaya;2.533168835681284
Netral;12;rusak;2.524773314904466
Netral;13;kuping;2.467380439698882
Netral;14;memang;2.40569011238711
Netral;15;gila;2.287101458397813
Netral;16;makin;2.2351236014648648
Netral;17;rumah;2.0675240398559565
Netral;18;mati;1.9614625703458581
Netral;19;iring;1.9434527406585436
Netral;20;sdm;1.9398206502085944
Rasional_Negatif;1;horeg;3.5827241407812505
Rasional_Negatif;2;sound;3.3063727752488212
Rasional_Negatif;3;coba;2.027316402092368
Rasional_Negatif;4;nyata;1.8024385073551026
Rasional_Negatif;5;nya;1.765984046478093
Rasional_Negatif;6;nih;1.721179686045779
Rasional_Negatif;7;denger;1.5717649385047197
Rasional_Negatif;8;kirim;1.4241361502495373
Rasional_Negatif;9;jakarta;1.423823250655758
Rasional_Negatif;10;neng;1.4011636312752702
Rasional_Negatif;11;cinta;1.3527802502323012
Rasional_Negatif;12;mania;1.3439064360333743
Rasional_Negatif;13;kalah;1.3067750068257977
Rasional_Negatif;14;biar;1.3050998989281029
Rasional_Negatif;15;pakai;1.266067394726762
Rasional_Negatif;16;sdm;1.2519822759925099
Rasional_Negatif;17;bang;1.2359473489570614
Rasional_Negatif;18;surabaya;1.2306564757306657
Rasional_Negatif;19;dengerin;1.2267600074948484
Rasional_Negatif;20;wkwk;1.2262976802685361
Cacimaki_Intoleransi;1;pecah;2.4369828103330717
Cacimaki_Intoleransi;2;berisik;2.20490755690924
Cacimaki_Intoleransi;3;lebih;1.9364312134556836
Cacimaki_Intoleransi;4;prabowo;1.9188562488668541
Cacimaki_Intoleransi;5;takut;1.9061480771684323
Cacimaki_Intoleransi;6;karnaval;1.8035365544769433
Cacimaki_Intoleransi;7;malah;1.7446854042306823
Cacimaki_Intoleransi;8;kayak;1.7075003033299314
Cacimaki_Intoleransi;9;kak;1.6849583237136558
Cacimaki_Intoleransi;10;soundnya;1.6803348309563613
Cacimaki_Intoleransi;11;tempat;1.677682762738017
Cacimaki_Intoleransi;12;sih;1.6528835143922378
Cacimaki_Intoleransi;13;resah;1.6044096678382982
Cacimaki_Intoleransi;14;mana;1.5996271476268982
Cacimaki_Intoleransi;15;soal;1.5423938651658127
Cacimaki_Intoleransi;16;eh;1.526995915647059
Cacimaki_Intoleransi;17;larang;1.4805137441039407
Cacimaki_Intoleransi;18;speaker;1.4553045647876774
Cacimaki_Intoleransi;19;serius;1.4493363051547334
Cacimaki_Intoleransi;20;ganti;1.4213752846691405
1 kelas rank kata skor
2 Netral 1 orang 3.747452530756425
3 Netral 2 anjir 3.2343104518912824
4 Netral 3 bakar 3.1979186820620673
5 Netral 4 sakit 2.952096243485207
6 Netral 5 jangan 2.855109685945343
7 Netral 6 baik 2.829229032424351
8 Netral 7 kaca 2.689608636542816
9 Netral 8 jawa 2.611014652608258
10 Netral 9 bodoh 2.5463763166610147
11 Netral 10 resah 2.535202036506705
12 Netral 11 budaya 2.533168835681284
13 Netral 12 rusak 2.524773314904466
14 Netral 13 kuping 2.467380439698882
15 Netral 14 memang 2.40569011238711
16 Netral 15 gila 2.287101458397813
17 Netral 16 makin 2.2351236014648648
18 Netral 17 rumah 2.0675240398559565
19 Netral 18 mati 1.9614625703458581
20 Netral 19 iring 1.9434527406585436
21 Netral 20 sdm 1.9398206502085944
22 Rasional_Negatif 1 horeg 3.5827241407812505
23 Rasional_Negatif 2 sound 3.3063727752488212
24 Rasional_Negatif 3 coba 2.027316402092368
25 Rasional_Negatif 4 nyata 1.8024385073551026
26 Rasional_Negatif 5 nya 1.765984046478093
27 Rasional_Negatif 6 nih 1.721179686045779
28 Rasional_Negatif 7 denger 1.5717649385047197
29 Rasional_Negatif 8 kirim 1.4241361502495373
30 Rasional_Negatif 9 jakarta 1.423823250655758
31 Rasional_Negatif 10 neng 1.4011636312752702
32 Rasional_Negatif 11 cinta 1.3527802502323012
33 Rasional_Negatif 12 mania 1.3439064360333743
34 Rasional_Negatif 13 kalah 1.3067750068257977
35 Rasional_Negatif 14 biar 1.3050998989281029
36 Rasional_Negatif 15 pakai 1.266067394726762
37 Rasional_Negatif 16 sdm 1.2519822759925099
38 Rasional_Negatif 17 bang 1.2359473489570614
39 Rasional_Negatif 18 surabaya 1.2306564757306657
40 Rasional_Negatif 19 dengerin 1.2267600074948484
41 Rasional_Negatif 20 wkwk 1.2262976802685361
42 Cacimaki_Intoleransi 1 pecah 2.4369828103330717
43 Cacimaki_Intoleransi 2 berisik 2.20490755690924
44 Cacimaki_Intoleransi 3 lebih 1.9364312134556836
45 Cacimaki_Intoleransi 4 prabowo 1.9188562488668541
46 Cacimaki_Intoleransi 5 takut 1.9061480771684323
47 Cacimaki_Intoleransi 6 karnaval 1.8035365544769433
48 Cacimaki_Intoleransi 7 malah 1.7446854042306823
49 Cacimaki_Intoleransi 8 kayak 1.7075003033299314
50 Cacimaki_Intoleransi 9 kak 1.6849583237136558
51 Cacimaki_Intoleransi 10 soundnya 1.6803348309563613
52 Cacimaki_Intoleransi 11 tempat 1.677682762738017
53 Cacimaki_Intoleransi 12 sih 1.6528835143922378
54 Cacimaki_Intoleransi 13 resah 1.6044096678382982
55 Cacimaki_Intoleransi 14 mana 1.5996271476268982
56 Cacimaki_Intoleransi 15 soal 1.5423938651658127
57 Cacimaki_Intoleransi 16 eh 1.526995915647059
58 Cacimaki_Intoleransi 17 larang 1.4805137441039407
59 Cacimaki_Intoleransi 18 speaker 1.4553045647876774
60 Cacimaki_Intoleransi 19 serius 1.4493363051547334
61 Cacimaki_Intoleransi 20 ganti 1.4213752846691405

View File

@ -0,0 +1,91 @@
clean_text;label_asli;label_prediksi;pola_kesalahan
rapat kan mikir lantik kan hibur sound horeg datengin langsung jawa timur;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
jir sound horeg bain cok memang tidak waras ni negara mana laku nyetel musik norak berandal;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
tidak bisa tidur gara2 sound horeg pawai hari santri lgian hari santri malah nyetel dj an si anjing;Rasional Negatif;Netral;Rasional Negatif → Netral
tlong setel kenceng pk sound horeg depan istana negara;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
kira orang gelut bidang sound horeg tidak punya kuping indra dengar tp kok mas punya kata tanya fungsi cuma pajang deh kata tanya;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
kalau bener sound horeg malu;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
berani mati kata seru lah orang mau demo besar2an gagal lantik presiden wapres pilih tidak jadi 1001 alas takut sama sound horeg kata tanya;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
mending bunuh sekarang gede jadi jawir suka sound horeg ijazah palsu ngaku lulus ugm jadi presiden amit amit jabang bayi setan;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
tidak sound horeg jam 6 pagi;Netral;Rasional Negatif;Netral → Rasional Negatif
bahkan d youtube banyak pro dgn sound horeg indonesia beneran krisis kualitas sdm rusak malah puji puji;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
community sih community ganggu orang also stasiun mrt bukan open and public spaces it is enclosed i guess banyak denger sound horeg will ruin the remaining braincells punya;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
moga sound horeg segera adaptasi efek keren;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
otak kebanyakan denger sound horeg makanya gapunya pikir;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
punya sound horeg;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
kalau semua usaha siap siap sound horeg masuk wilayah kalian langsung tuntut maksud;Rasional Negatif;Netral;Rasional Negatif → Netral
segala sound horeg nya lagi tai buat kuping pengang langsung bete mau lanjut lari anjing;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
capek anjir cuma nonton sound horeg jalan parkir venue baris tinggal 8 truk pp hampir 5k langkah temenin kakak;Rasional Negatif;Netral;Rasional Negatif → Netral
norak bener moga2 cpt laknat tuhan masang sound horeg;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
asa eropa endak masalah sound horeg orang antem trotoar rebut jalan guru dipenjarain anak2 tawur jalan ormas arogan;Netral;Rasional Negatif;Netral → Rasional Negatif
sorry tidak layan logical fallacy desibel sound horeg orang latih poundfit jelas jauh beda;Rasional Negatif;Netral;Rasional Negatif → Netral
anjing tempelin telinga mentri2 nya si gemoy njr sound horeg;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
guys kalau daerah belum infeksi sound horeg segera usir masuk daerah serius tidak obat;Rasional Negatif;Netral;Rasional Negatif → Netral
wahhh aku kalau suara berisik dikit deh ngamuk ngamuk kalau tetangga sound horeg begini kek deh kujeglekin tu listrik tetangga;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
kok rusuh banget yo dancok kata tanya sumpah aku tidak paham esensi ne sound horeg;Rasional Negatif;Netral;Rasional Negatif → Netral
tidak apa apa kan udah tuli kena sound horeg makanya yandaktawukogtanyasayaaaaaa kata tanya;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
wkwk buzzer sound horeg kh stress;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
punya alam buruk dengung sound horeg kata tanya sesat pikir puja sound horeg bela hobi egois ganggu anti kritik kata seru;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
kualitas sdm rendah suka ganggu orang tidak punya common sense kira semua orang suka kali kata tanya hidihhh tidak suka sama selera kamu jelek udah selera jelek ganggu orang moga sound horeg jijik segera musnah sama orang2nya aamiin;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
wah model baru sound horeg mode menyalaaaaa;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
jawa timur semenjak pimpin khofifah banyakin aji rakyat tidak urus prguruan silat jawa timur rusuh dijepang 0 reaksi jalan ancur 0 reaksi trend sound horeg bodoh rugi masyarakat 0 reaksi jawa timur makin mundur makin ancur sdmnya;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
kamu bela wasit sayang kamu ntr timnas kamu bakal sambut hangat supporter timnas pokok hotel tempat timnas bahrain nginap jam 7 9 serang pakai sound horeg jam 9 1 pesta kembang api depan hotel jam 1 5 balap liar depan hotel;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
kocak banget anjir;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
maap allah kali musibah tp aku bilang alhamdulillah akhir sound horeg bising gagal alam;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
heran perintah sus jawa timur tidak punya kuat larang giat sound horeg tak manfaat sama sekali pawai sound horeg sangat ganggu jawa timur langsung 24jam bahkan lebih libat anak anak pemuda manula;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
nambahin moga dapet tetangga berisik motong keramik sound jj horeg salip emak emak sein kanan kiri jempol kepentok ujung meja;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
sungguh informasi bagong sering suka dekat dekat sound horeg anak anak efek apa anak anak kata tanya;Rasional Negatif;Netral;Rasional Negatif → Netral
jngankan sound horeg by kt udah bakar bakar ban ngelemparin botol beling tengah jalan megang banner 3x1 dipilok merah oren;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
kalau tidak sound horeg karnaval nya tidak jalan kata tanya;Rasional Negatif;Netral;Rasional Negatif → Netral
tantrumnya pasang sound horeg;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
larang sound horeg lebih banyak rugi jangan warga selesai cara nya ayo buat negeri damai aman sejahtera sampeyan punya jiwa korsa laksana punya nyali;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
berapa decibel suara sound horeg kata tanya kompas kata bs 135 decibel bs buat tuli gambar bagi masyrakat coba bawa jakarta jadi tuli kata tanya;Rasional Negatif;Netral;Rasional Negatif → Netral
fck kata seru vibe malang selatan boyong dukuh atas 5 tronton soundsystem jejer gempa lokal dancok kali sepakat sama sound horeg nottinghill carnival dgn arif lokal;Rasional Negatif;Netral;Rasional Negatif → Netral
jauh ibukota takut ketemu sound horeg acara jelek;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
mati kalau makan kopek gemar sound horeg;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
toa ukur segitu sound horeg sujud semua;Rasional Negatif;Netral;Rasional Negatif → Netral
kocak banget prindapan land udah banyak maen sound horeg parahh;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
kan aku bilang apa sound horeg support ma renta makanya makin jamur;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
saran anak anak kek kumpulin terus suruh diri depan sound horeg lama minggu;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
nampolin naro sound horeg depan gedung retweet;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
pihak perintah tolong cari solusi sound horeg;Rasional Negatif;Netral;Rasional Negatif → Netral
sangat pas sekali bulan puasa banyak jual kembang api sound horeg bangunin sahur;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
alhamdulillah please damkar kalau panggil buat alas kalau nanganin sound horeg begini;Netral;Rasional Negatif;Netral → Rasional Negatif
kirim rudal pak kim sound horeg;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
apa bom sound horeg kata tanya;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
gedang kontaminasi sound horeg tibae;Rasional Negatif;Netral;Rasional Negatif → Netral
dok review pilih sound horeg jadi budaya daerah kab malang tidak tahu gimana tiba bupati sanusi tetap jedag jedug salah satu budaya daerah;Netral;Rasional Negatif;Netral → Rasional Negatif
plsss bjirrr sp muterin sound horeg blkng kantor;Rasional Negatif;Netral;Rasional Negatif → Netral
semalem gara gara liatin interview sound horeg bawa mimpi cok rem banget sumpah please horeg ulah ka pabuaran sieun amuk massa;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
cari temen baru nder tidak pacar kalau sepi mah join sound horeg rame;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
gara2 sound horeg kata tanya;Rasional Negatif;Netral;Rasional Negatif → Netral
gedeg kalau acara sound horeg;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
sini tidak sound horeg knalpot brong2;Netral;Rasional Negatif;Netral → Rasional Negatif
mas nya jelasin enteng banget gimana orang nyewa warga sendiri milik sound mah dapet duid pasar bukti tinggal nglayanin apa minta sih kalau memang warga tolak tidak lanjut dong fenomena horeg;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
apa faktor pengaruh popularitas sound horeg kalang generasi muda kata tanya;Rasional Negatif;Netral;Rasional Negatif → Netral
ngilangi sound horeg sis;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
gimana kalau suka sound horeg barisin satu satu depan soundnya setel paling kenceng biar gendang telinga pecah sekali makin sawer tidak;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
serius nanya selenggara nikmat sound horeg punya otak tidak si kata tanya;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
beranta politik amerika tp rakyat dapet festival indonesia udah negara beranta rakyat cuma dapet sound horeg;Rasional Negatif;Netral;Rasional Negatif → Netral
guedeg aku mbek sound horeg gerebek kuping seng duwe ide gawe hibur siapa seh;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
suara adzan suruh dikecilin sound horeg dibesarin sakit jiwa;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
capekk banget bziirr kekk gaa liat konsep lagunyaa hahhhh kata tanya kata seru jgn datengin sound horeg tp puter lagu aespa;Rasional Negatif;Netral;Rasional Negatif → Netral
tidak jadi tiktok referensi hidup tidak candu narkoboy tidak candu judi tidak gabung komunitas sound horeg suatu menang kata seru;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
turun jawa mikir sama tempat bapak tidak sound horeg sih;Netral;Rasional Negatif;Netral → Rasional Negatif
gaco tamsis kek pakai sound horeg biar tambah brisik;Rasional Negatif;Netral;Rasional Negatif → Netral
saiki urip ning deso tidak sahdu banyak sound horeg sampah masyarat wujud guru silat;Rasional Negatif;Netral;Rasional Negatif → Netral
bagaimana kalau senjata rahasia macam sound horeg kirim pusat hamas gaza iran dekat rumah khamaini biar sana getar bahana dibunyiin dangdut koplo plus plus;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
penasaran kalau kaca 2 gedung tinggi pecah segitiga emas jakarta masuk instansi perintah pegawai minggu liburin dong love sound horeg;Cacimaki/Intoleransi;Rasional Negatif;Cacimaki/Intoleransi → Rasional Negatif
anjirrr wtf kayak mimpi buruk kalau deket sound horeg;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
udah mah sound horeg tambah suara 4 manusia talenta;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
hancurin gmn kata tanya kesel liat laku supporter sound horeg;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
pernah komen ig lamtur kaya begini malah serang sama puja sound horeg dikatain kamu miskin kalau kamu tau harga sound nya berapa bla bla bla kata teh tidak peduli njir mau mahal apa harga mental habit kalian tetep miskin horeg;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
sound horeg gpp asal lagu;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
guys kayak fakta baru ungkap sedang jadi debat sekitar pulau jawa tembok lin runtuh akibat sound horeg tidak lewat;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
maju jangan korupsi berantas sound horeg simbol bodoh;Rasional Negatif;Cacimaki/Intoleransi;Rasional Negatif → Cacimaki/Intoleransi
konsep outdoor mirip sound horeg jawa timur selatan;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
gendang telinga rusak dengar kurang blm asa efek2 keq begini untung sound horeg;Netral;Rasional Negatif;Netral → Rasional Negatif
sound horeg versi lite udah nyampe jakarta astaga kenceng bener kedengeran gym padahal jarak lumayan gymnya lantai 2;Rasional Negatif;Netral;Rasional Negatif → Netral
sound horeg bukti gagal perintah beri ruang publik hibur buka implikasi rendah empati banyak masyarakat kata tanya nyata dua;Rasional Negatif;Netral;Rasional Negatif → Netral
infoin cara nyambungin bluetooth sound horeg plisss mau tin playlistku;Netral;Cacimaki/Intoleransi;Netral → Cacimaki/Intoleransi
mau ngatain lah bang sound horeg kan hina;Cacimaki/Intoleransi;Netral;Cacimaki/Intoleransi → Netral
1 clean_text label_asli label_prediksi pola_kesalahan
2 rapat kan mikir lantik kan hibur sound horeg datengin langsung jawa timur Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
3 jir sound horeg bain cok memang tidak waras ni negara mana laku nyetel musik norak berandal Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
4 tidak bisa tidur gara2 sound horeg pawai hari santri lgian hari santri malah nyetel dj an si anjing Rasional Negatif Netral Rasional Negatif → Netral
5 tlong setel kenceng pk sound horeg depan istana negara Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
6 kira orang gelut bidang sound horeg tidak punya kuping indra dengar tp kok mas punya kata tanya fungsi cuma pajang deh kata tanya Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
7 kalau bener sound horeg malu Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
8 berani mati kata seru lah orang mau demo besar2an gagal lantik presiden wapres pilih tidak jadi 1001 alas takut sama sound horeg kata tanya Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
9 mending bunuh sekarang gede jadi jawir suka sound horeg ijazah palsu ngaku lulus ugm jadi presiden amit amit jabang bayi setan Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
10 tidak sound horeg jam 6 pagi Netral Rasional Negatif Netral → Rasional Negatif
11 bahkan d youtube banyak pro dgn sound horeg indonesia beneran krisis kualitas sdm rusak malah puji puji Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
12 community sih community ganggu orang also stasiun mrt bukan open and public spaces it is enclosed i guess banyak denger sound horeg will ruin the remaining braincells punya Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
13 moga sound horeg segera adaptasi efek keren Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
14 otak kebanyakan denger sound horeg makanya gapunya pikir Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
15 punya sound horeg Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
16 kalau semua usaha siap siap sound horeg masuk wilayah kalian langsung tuntut maksud Rasional Negatif Netral Rasional Negatif → Netral
17 segala sound horeg nya lagi tai buat kuping pengang langsung bete mau lanjut lari anjing Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
18 capek anjir cuma nonton sound horeg jalan parkir venue baris tinggal 8 truk pp hampir 5k langkah temenin kakak Rasional Negatif Netral Rasional Negatif → Netral
19 norak bener moga2 cpt laknat tuhan masang sound horeg Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
20 asa eropa endak masalah sound horeg orang antem trotoar rebut jalan guru dipenjarain anak2 tawur jalan ormas arogan Netral Rasional Negatif Netral → Rasional Negatif
21 sorry tidak layan logical fallacy desibel sound horeg orang latih poundfit jelas jauh beda Rasional Negatif Netral Rasional Negatif → Netral
22 anjing tempelin telinga mentri2 nya si gemoy njr sound horeg Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
23 guys kalau daerah belum infeksi sound horeg segera usir masuk daerah serius tidak obat Rasional Negatif Netral Rasional Negatif → Netral
24 wahhh aku kalau suara berisik dikit deh ngamuk ngamuk kalau tetangga sound horeg begini kek deh kujeglekin tu listrik tetangga Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
25 kok rusuh banget yo dancok kata tanya sumpah aku tidak paham esensi ne sound horeg Rasional Negatif Netral Rasional Negatif → Netral
26 tidak apa apa kan udah tuli kena sound horeg makanya yandaktawukogtanyasayaaaaaa kata tanya Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
27 wkwk buzzer sound horeg kh stress Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
28 punya alam buruk dengung sound horeg kata tanya sesat pikir puja sound horeg bela hobi egois ganggu anti kritik kata seru Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
29 kualitas sdm rendah suka ganggu orang tidak punya common sense kira semua orang suka kali kata tanya hidihhh tidak suka sama selera kamu jelek udah selera jelek ganggu orang moga sound horeg jijik segera musnah sama orang2nya aamiin Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
30 wah model baru sound horeg mode menyalaaaaa Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
31 jawa timur semenjak pimpin khofifah banyakin aji rakyat tidak urus prguruan silat jawa timur rusuh dijepang 0 reaksi jalan ancur 0 reaksi trend sound horeg bodoh rugi masyarakat 0 reaksi jawa timur makin mundur makin ancur sdmnya Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
32 kamu bela wasit sayang kamu ntr timnas kamu bakal sambut hangat supporter timnas pokok hotel tempat timnas bahrain nginap jam 7 9 serang pakai sound horeg jam 9 1 pesta kembang api depan hotel jam 1 5 balap liar depan hotel Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
33 kocak banget anjir Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
34 maap allah kali musibah tp aku bilang alhamdulillah akhir sound horeg bising gagal alam Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
35 heran perintah sus jawa timur tidak punya kuat larang giat sound horeg tak manfaat sama sekali pawai sound horeg sangat ganggu jawa timur langsung 24jam bahkan lebih libat anak anak pemuda manula Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
36 nambahin moga dapet tetangga berisik motong keramik sound jj horeg salip emak emak sein kanan kiri jempol kepentok ujung meja Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
37 sungguh informasi bagong sering suka dekat dekat sound horeg anak anak efek apa anak anak kata tanya Rasional Negatif Netral Rasional Negatif → Netral
38 jngankan sound horeg by kt udah bakar bakar ban ngelemparin botol beling tengah jalan megang banner 3x1 dipilok merah oren Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
39 kalau tidak sound horeg karnaval nya tidak jalan kata tanya Rasional Negatif Netral Rasional Negatif → Netral
40 tantrumnya pasang sound horeg Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
41 larang sound horeg lebih banyak rugi jangan warga selesai cara nya ayo buat negeri damai aman sejahtera sampeyan punya jiwa korsa laksana punya nyali Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
42 berapa decibel suara sound horeg kata tanya kompas kata bs 135 decibel bs buat tuli gambar bagi masyrakat coba bawa jakarta jadi tuli kata tanya Rasional Negatif Netral Rasional Negatif → Netral
43 fck kata seru vibe malang selatan boyong dukuh atas 5 tronton soundsystem jejer gempa lokal dancok kali sepakat sama sound horeg nottinghill carnival dgn arif lokal Rasional Negatif Netral Rasional Negatif → Netral
44 jauh ibukota takut ketemu sound horeg acara jelek Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
45 mati kalau makan kopek gemar sound horeg Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
46 toa ukur segitu sound horeg sujud semua Rasional Negatif Netral Rasional Negatif → Netral
47 kocak banget prindapan land udah banyak maen sound horeg parahh Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
48 kan aku bilang apa sound horeg support ma renta makanya makin jamur Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
49 saran anak anak kek kumpulin terus suruh diri depan sound horeg lama minggu Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
50 nampolin naro sound horeg depan gedung retweet Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
51 pihak perintah tolong cari solusi sound horeg Rasional Negatif Netral Rasional Negatif → Netral
52 sangat pas sekali bulan puasa banyak jual kembang api sound horeg bangunin sahur Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
53 alhamdulillah please damkar kalau panggil buat alas kalau nanganin sound horeg begini Netral Rasional Negatif Netral → Rasional Negatif
54 kirim rudal pak kim sound horeg Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
55 apa bom sound horeg kata tanya Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
56 gedang kontaminasi sound horeg tibae Rasional Negatif Netral Rasional Negatif → Netral
57 dok review pilih sound horeg jadi budaya daerah kab malang tidak tahu gimana tiba bupati sanusi tetap jedag jedug salah satu budaya daerah Netral Rasional Negatif Netral → Rasional Negatif
58 plsss bjirrr sp muterin sound horeg blkng kantor Rasional Negatif Netral Rasional Negatif → Netral
59 semalem gara gara liatin interview sound horeg bawa mimpi cok rem banget sumpah please horeg ulah ka pabuaran sieun amuk massa Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
60 cari temen baru nder tidak pacar kalau sepi mah join sound horeg rame Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
61 gara2 sound horeg kata tanya Rasional Negatif Netral Rasional Negatif → Netral
62 gedeg kalau acara sound horeg Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
63 sini tidak sound horeg knalpot brong2 Netral Rasional Negatif Netral → Rasional Negatif
64 mas nya jelasin enteng banget gimana orang nyewa warga sendiri milik sound mah dapet duid pasar bukti tinggal nglayanin apa minta sih kalau memang warga tolak tidak lanjut dong fenomena horeg Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
65 apa faktor pengaruh popularitas sound horeg kalang generasi muda kata tanya Rasional Negatif Netral Rasional Negatif → Netral
66 ngilangi sound horeg sis Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
67 gimana kalau suka sound horeg barisin satu satu depan soundnya setel paling kenceng biar gendang telinga pecah sekali makin sawer tidak Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
68 serius nanya selenggara nikmat sound horeg punya otak tidak si kata tanya Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
69 beranta politik amerika tp rakyat dapet festival indonesia udah negara beranta rakyat cuma dapet sound horeg Rasional Negatif Netral Rasional Negatif → Netral
70 guedeg aku mbek sound horeg gerebek kuping seng duwe ide gawe hibur siapa seh Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
71 suara adzan suruh dikecilin sound horeg dibesarin sakit jiwa Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
72 capekk banget bziirr kekk gaa liat konsep lagunyaa hahhhh kata tanya kata seru jgn datengin sound horeg tp puter lagu aespa Rasional Negatif Netral Rasional Negatif → Netral
73 tidak jadi tiktok referensi hidup tidak candu narkoboy tidak candu judi tidak gabung komunitas sound horeg suatu menang kata seru Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
74 turun jawa mikir sama tempat bapak tidak sound horeg sih Netral Rasional Negatif Netral → Rasional Negatif
75 gaco tamsis kek pakai sound horeg biar tambah brisik Rasional Negatif Netral Rasional Negatif → Netral
76 saiki urip ning deso tidak sahdu banyak sound horeg sampah masyarat wujud guru silat Rasional Negatif Netral Rasional Negatif → Netral
77 bagaimana kalau senjata rahasia macam sound horeg kirim pusat hamas gaza iran dekat rumah khamaini biar sana getar bahana dibunyiin dangdut koplo plus plus Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
78 penasaran kalau kaca 2 gedung tinggi pecah segitiga emas jakarta masuk instansi perintah pegawai minggu liburin dong love sound horeg Cacimaki/Intoleransi Rasional Negatif Cacimaki/Intoleransi → Rasional Negatif
79 anjirrr wtf kayak mimpi buruk kalau deket sound horeg Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
80 udah mah sound horeg tambah suara 4 manusia talenta Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
81 hancurin gmn kata tanya kesel liat laku supporter sound horeg Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral
82 pernah komen ig lamtur kaya begini malah serang sama puja sound horeg dikatain kamu miskin kalau kamu tau harga sound nya berapa bla bla bla kata teh tidak peduli njir mau mahal apa harga mental habit kalian tetep miskin horeg Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
83 sound horeg gpp asal lagu Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
84 guys kayak fakta baru ungkap sedang jadi debat sekitar pulau jawa tembok lin runtuh akibat sound horeg tidak lewat Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
85 maju jangan korupsi berantas sound horeg simbol bodoh Rasional Negatif Cacimaki/Intoleransi Rasional Negatif → Cacimaki/Intoleransi
86 konsep outdoor mirip sound horeg jawa timur selatan Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
87 gendang telinga rusak dengar kurang blm asa efek2 keq begini untung sound horeg Netral Rasional Negatif Netral → Rasional Negatif
88 sound horeg versi lite udah nyampe jakarta astaga kenceng bener kedengeran gym padahal jarak lumayan gymnya lantai 2 Rasional Negatif Netral Rasional Negatif → Netral
89 sound horeg bukti gagal perintah beri ruang publik hibur buka implikasi rendah empati banyak masyarakat kata tanya nyata dua Rasional Negatif Netral Rasional Negatif → Netral
90 infoin cara nyambungin bluetooth sound horeg plisss mau tin playlistku Netral Cacimaki/Intoleransi Netral → Cacimaki/Intoleransi
91 mau ngatain lah bang sound horeg kan hina Cacimaki/Intoleransi Netral Cacimaki/Intoleransi → Netral

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,87 @@
# 1 extraction features TF-IDF (VERSI RANDOM SPLIT - TANPA STRATIFIED)
import pandas as pd
import joblib
import os
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
print("🔄 [TAHAP 1] Memulai Preprocessing & TF-IDF...")
# --- CONFIG ---
# Gunakan Absolute Path dengan 'r' di depan agar aman di Windows
FILE_DATA = r'D:\project skripsi machine learning intoleransi\virtualEnvironment\dataset\dataYangDiPakai\data_label_3_kategori_v2 - Copy.csv'
# Tentukan folder tujuan penyimpanan
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
FOLDER_OUTPUT = os.path.join(BASE_DIR, 'output')
FOLDER_MODELS = os.path.join(BASE_DIR, 'models')
# 0. CEK KEAMANAN FILE SEBELUM JALAN
if not os.path.exists(FILE_DATA):
raise FileNotFoundError(f"❌ File tidak ditemukan di jalur:\n{FILE_DATA}\nCoba pastikan nama file dan foldernya sudah persis sama!")
# Bikin folder otomatis (pakai slash '/' biar aman di semua sistem)
os.makedirs(FOLDER_OUTPUT, exist_ok=True)
os.makedirs(FOLDER_MODELS, exist_ok=True)
# 1. LOAD DATA
print(" - Membaca dataset...")
df = pd.read_csv(FILE_DATA, sep=';')
# Cek keamanan kolom (Biar tidak error kalau nama kolom salah)
if 'clean_text' not in df.columns or 'label' not in df.columns:
print(f"Daftar kolom yang ada di filemu: {df.columns.tolist()}")
raise KeyError("❌ Kolom 'clean_text' atau 'label' tidak ada! Coba cek tulisan di atas, pastikan namanya cocok.")
# Bersihkan data kosong
df = df.dropna(subset=['clean_text', 'label'])
print(f" - Total data bersih yang siap diproses: {len(df)} baris")
# 2. TF-IDF (Ubah Huruf jadi Angka)
print(" - Melakukan ekstraksi fitur TF-IDF...")
vectorizer = TfidfVectorizer(max_features=5000)
X = vectorizer.fit_transform(df['clean_text'].astype(str))
y = df['label'].astype(int) # Pastikan label berupa angka (0, 1, 2)
# 3. SIMPAN KAMUS TF-IDF
print(f" - Menyimpan kamus TF-IDF ke '{FOLDER_MODELS}'...")
joblib.dump(vectorizer, f'{FOLDER_MODELS}/vectorizer_tfidf.pkl')
# 4. SPLIT DATA dengan RANDOM SPLIT (80% Latih, 20% Uji)
print(" - Memecah data dengan Random Split (80% Data Latih, 20% Data Uji)...")
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 4.1 TAMPILKAN HASIL DISTRIBUSI LABEL (Untuk Perbandingan dengan Stratified)
print("\n" + "="*55)
print("📊 HASIL RANDOM SPLIT (TANPA STRATIFIED)")
print("="*55)
print(f"\n📈 Total Data Awal : {len(y)} baris")
print(f"📈 Total Data Latih : {len(y_train)} baris ({len(y_train)/len(y)*100:.1f}%)")
print(f"📈 Total Data Uji : {len(y_test)} baris ({len(y_test)/len(y)*100:.1f}%)")
print(f"\n📊 Distribusi Label Dataset Awal:")
for label, jumlah in y.value_counts().sort_index().items():
persen = jumlah / len(y) * 100
print(f" - Label {label}: {jumlah} data ({persen:.2f}%)")
print(f"\n📊 Distribusi Label Data Latih (Random Split):")
for label, jumlah in y_train.value_counts().sort_index().items():
persen = jumlah / len(y_train) * 100
print(f" - Label {label}: {jumlah} data ({persen:.2f}%)")
print(f"\n📊 Distribusi Label Data Uji (Random Split):")
for label, jumlah in y_test.value_counts().sort_index().items():
persen = jumlah / len(y_test) * 100
print(f" - Label {label}: {jumlah} data ({persen:.2f}%)")
print("="*55 + "\n")
# 5. SIMPAN DATA MATANG
print(f" - Menyimpan data matang ke folder '{FOLDER_OUTPUT}'...")
joblib.dump(X_train, f'{FOLDER_OUTPUT}/X_train.pkl')
joblib.dump(X_test, f'{FOLDER_OUTPUT}/X_test.pkl')
joblib.dump(y_train, f'{FOLDER_OUTPUT}/y_train.pkl')
joblib.dump(y_test, f'{FOLDER_OUTPUT}/y_test.pkl')
print("✅ SELESAI TAHAP 1. Data sudah siap!")
print("👉 Silakan lanjut jalankan file '2_training.py' di terminal")

View File

@ -0,0 +1,92 @@
# 1 extraction features TF-IDF (VERSI MODIFIKASI - STRATIFIED + BIGRAM)
import pandas as pd
import joblib
import os
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
print("🔄 [TAHAP 1] Memulai Preprocessing & TF-IDF...")
# --- CONFIG ---
FILE_DATA = r'D:\project skripsi machine learning intoleransi\virtualEnvironment\dataset\dataYangDiPakai\data_label_3_kategori_v2 - Copy.csv'
FOLDER_OUTPUT = 'virtualEnvironment/output'
FOLDER_MODELS = 'virtualEnvironment/models'
# 0. CEK KEAMANAN FILE
if not os.path.exists(FILE_DATA):
raise FileNotFoundError(f"❌ File tidak ditemukan di jalur:\n{FILE_DATA}")
os.makedirs(FOLDER_OUTPUT, exist_ok=True)
os.makedirs(FOLDER_MODELS, exist_ok=True)
# 1. LOAD DATA
print(" - Membaca dataset...")
df = pd.read_csv(FILE_DATA, sep=';')
if 'clean_text' not in df.columns or 'label' not in df.columns:
print(f"Daftar kolom: {df.columns.tolist()}")
raise KeyError("❌ Kolom 'clean_text' atau 'label' tidak ada!")
df = df.dropna(subset=['clean_text', 'label'])
print(f" - Total data bersih: {len(df)} baris")
# 2. TF-IDF (MODIFIKASI: Tambah bigram, min_df, max_df)
print(" - Melakukan ekstraksi fitur TF-IDF (Unigram + Bigram)...")
vectorizer = TfidfVectorizer(
max_features=10000, # naik dari 5000
ngram_range=(1, 2), # tambah bigram
min_df=2, # buang kata yang muncul cuma 1x
max_df=0.95 # buang kata yang muncul di >95% dokumen
)
X = vectorizer.fit_transform(df['clean_text'].astype(str))
y = df['label'].astype(int)
print(f" - Jumlah fitur TF-IDF aktual: {X.shape[1]}")
# 3. SIMPAN VECTORIZER
print(f" - Menyimpan vectorizer ke '{FOLDER_MODELS}'...")
joblib.dump(vectorizer, f'{FOLDER_MODELS}/vectorizer_tfidf.pkl')
# 4. SPLIT DATA dengan STRATIFIED (80% Latih, 20% Uji)
print(" - Memecah data dengan Stratified Split (menjaga distribusi label)...")
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y # ← PERUBAHAN UTAMA: stratify
)
# 4.1 TAMPILKAN HASIL DISTRIBUSI LABEL
print("\n" + "="*55)
print("📊 HASIL STRATIFIED SPLIT")
print("="*55)
print(f"\n📈 Total Data Awal : {len(y)} baris")
print(f"📈 Total Data Latih : {len(y_train)} baris ({len(y_train)/len(y)*100:.1f}%)")
print(f"📈 Total Data Uji : {len(y_test)} baris ({len(y_test)/len(y)*100:.1f}%)")
print(f"\n📊 Distribusi Label Dataset Awal:")
for label, jumlah in y.value_counts().sort_index().items():
persen = jumlah / len(y) * 100
print(f" - Label {label}: {jumlah} data ({persen:.2f}%)")
print(f"\n📊 Distribusi Label Data Latih:")
for label, jumlah in y_train.value_counts().sort_index().items():
persen = jumlah / len(y_train) * 100
print(f" - Label {label}: {jumlah} data ({persen:.2f}%)")
print(f"\n📊 Distribusi Label Data Uji:")
for label, jumlah in y_test.value_counts().sort_index().items():
persen = jumlah / len(y_test) * 100
print(f" - Label {label}: {jumlah} data ({persen:.2f}%)")
print("="*55 + "\n")
# 5. SIMPAN DATA MATANG
print(f" - Menyimpan data matang ke '{FOLDER_OUTPUT}'...")
joblib.dump(X_train, f'{FOLDER_OUTPUT}/X_train.pkl')
joblib.dump(X_test, f'{FOLDER_OUTPUT}/X_test.pkl')
joblib.dump(y_train, f'{FOLDER_OUTPUT}/y_train.pkl')
joblib.dump(y_test, f'{FOLDER_OUTPUT}/y_test.pkl')
print("✅ SELESAI TAHAP 1. Lanjut jalankan 'training2.py'")

View File

@ -0,0 +1,458 @@
{
"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
}

View File

@ -0,0 +1,6 @@
streamlit>=1.35.0
pandas>=2.0.0
numpy>=1.26.0
joblib==1.5.3
plotly>=5.18.0
scikit-learn==1.8.0

View File

@ -0,0 +1,371 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "adf2c795",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import re\n",
"import string"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2dbcc87",
"metadata": {},
"outputs": [],
"source": [
"data = pd.read_csv('dataset\\dataYangDiPakai\\data_sound_horeg_total_mei.csv')\n",
"data.head(20)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8e082cd4",
"metadata": {},
"outputs": [],
"source": [
"\n",
"# 1. Pastikan 'favorite_count' TIDAK ada di daftar kolom yang dibuang\n",
"kolomDibuang = [\n",
" 'conversation_id_str', 'id_str', 'image_url', 'in_reply_to_screen_name',\n",
" 'lang', 'location', 'quote_count', 'reply_count', 'retweet_count',\n",
" 'tweet_url', 'user_id_str', 'username'\n",
"]\n",
"\n",
"# 2. Hapus kolom sampah\n",
"dataPembersihan = data.drop(columns=kolomDibuang)\n",
"\n",
"# 3. Hapus baris kosong agar tidak error\n",
"dataPembersihan = dataPembersihan.dropna(subset=['full_text'])\n",
"\n",
"# 4. Fungsi Pembersihan (Tetap sama)\n",
"def cleaningKhusus(text):\n",
" text = re.sub(r'http\\S+|www\\S+|https\\S+', '', text, flags=re.MULTILINE)\n",
" text = re.sub(r'\\@\\w+|\\#','', text)\n",
" text = text.translate(str.maketrans('', '', string.punctuation))\n",
" text = re.sub(r'\\d+', '', text)\n",
" text = text.strip()\n",
" return text\n",
"\n",
"def cleaningUmum(text):\n",
" text = text.lower()\n",
" text = re.sub(r'\\s+', ' ', text)\n",
" text = text.encode('ascii', 'ignore').decode('ascii')\n",
" text = ' '.join([word for word in text.split() if len(word) > 1])\n",
" return text\n",
"\n",
"# 5. BERSIHKAN TEKS dan langsung simpan kembali ke kolom 'full_text'\n",
"# Ini akan menimpa teks asli dengan teks yang sudah bersih\n",
"dataPembersihan['full_text'] = dataPembersihan['full_text'].apply(cleaningKhusus).apply(cleaningUmum)\n",
"\n",
"# 6. Pilih hanya 3 kolom yang Anda minta\n",
"df_hasil_akhir = dataPembersihan[['created_at', 'favorite_count', 'full_text']]\n",
"\n",
"# 7. Simpan ke CSV\n",
"df_hasil_akhir.to_csv('dataset\\dataYangDiPakai\\data_sound_horeg_total_mei_dibersihkan.csv', index=False)\n",
"\n",
"# Tampilkan hasil\n",
"print(\"Berhasil! Kolom sekarang hanya: created_at, favorite_count, dan full_text (sudah bersih).\")\n",
"print(df_hasil_akhir.head(20))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ee52f2ae",
"metadata": {},
"outputs": [],
"source": [
"pd.set_option('display.max_colwidth', None)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "414378d3",
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_csv('dataset\\dataYangDiPakai\\hasil_preprocessing_intoleransi.csv')\n",
"df.head(20)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "69e6b2a9",
"metadata": {},
"outputs": [],
"source": [
"#membersihkan data\n",
"import pandas as pd\n",
"import re\n",
"from Sastrawi.StopWordRemover.StopWordRemoverFactory import StopWordRemoverFactory, StopWordRemover, ArrayDictionary\n",
"from Sastrawi.Stemmer.StemmerFactory import StemmerFactory\n",
"\n",
"# --- PENGATURAN TAMPILAN ---\n",
"pd.set_option('display.max_colwidth', None)\n",
"\n",
"# 1. LOAD DATA\n",
"# Path file sesuai dengan yang kamu berikan\n",
"filename = '/content/drive/MyDrive/proyek skiprsi sound horeg/hasil_data_bersih_lengkap2.csv'\n",
"\n",
"try:\n",
" df = pd.read_csv(filename)\n",
" print(f\"Data berhasil dimuat: {len(df)} baris\")\n",
"except FileNotFoundError:\n",
" print(\"Error: File tidak ditemukan. Pastikan path di Google Drive sudah benar.\")\n",
" # Dummy data untuk antisipasi error jika dijalankan orang lain\n",
" df = pd.DataFrame({'full_text': ['sound horeg jancok gak enak', 'asu tenan', 'saya tidak setuju']})\n",
"\n",
"# Hapus Duplikat\n",
"df = df.drop_duplicates(subset=['full_text'])\n",
"\n",
"# 2. DEFINISI KAMUS (Kamus Alay + Istilah Intoleransi + Bahasa Jawa)\n",
"kamus_alay = {\n",
" # --- A. KATA GANTI & SINGKATAN UMUM ---\n",
" 'org': 'orang', 'yg': 'yang', 'jg': 'juga', 'ga': 'tidak', 'udh': 'sudah',\n",
" 'jatim': 'jawa timur', 'bgt': 'banget', 'wong': 'orang', 'tak': 'tidak',\n",
" 'utk': 'untuk', 'trs': 'terus', 'gak': 'tidak', 'tu': 'itu', 'gimana': 'bagaimana',\n",
" 'sampe': 'sampai', 'ampe': 'sampai', 'jd': 'jadi', 'gw': 'aku', 'tau': 'tahu',\n",
" 'gara': 'karena', 'trus': 'terus', 'sm': 'sama', 'pake': 'pakai', 'klo': 'kalau',\n",
" 'gue': 'aku', 'tp': 'tapi', 'dr': 'dari', 'jgn': 'jangan', 'fasum': 'fasilitas umum',\n",
" 'gini': 'ini', 'ama': 'sama', 'knp': 'kenapa', 'cm': 'cuma', 'udah': 'sudah',\n",
" 'gada': 'tidak ada', 'gmn': 'bagaimana', 'emg': 'memang', 'krn': 'karena',\n",
" 'sdh': 'sudah', 'aja': 'saja', 'dlm': 'dalam', 'blm': 'belum', 'dgn': 'dengan',\n",
" 'scr': 'secara', 'adlh': 'adalah', 'tdk': 'tidak', 'skrg': 'sekarang',\n",
" 'bkn': 'bukan', 'sbg': 'sebagai', 'kalo': 'kalau', 'buanter': 'kencang',\n",
"\n",
" # --- B. ISTILAH KONFLIK & INTOLERANSI ---\n",
" 'brisik': 'berisik', 'bising': 'berisik', 'budeg': 'tuli', 'brebeken': 'berisik',\n",
" 'pekok': 'bodoh', 'goblok': 'bodoh', 'tolol': 'bodoh', 'edan': 'gila',\n",
" 'gendeng': 'gila', 'stress': 'gila', 'rusuh': 'rusak', 'ancur': 'hancur',\n",
" 'bakar': 'bakar', 'matek': 'mati', 'modar': 'mati', 'sampah': 'buruk',\n",
" 'sdm': 'sumber daya manusia', 'rendah': 'buruk', 'norak': 'kampungan',\n",
" 'ganggu': 'mengganggu', 'keganggu': 'terganggu',\n",
"\n",
" # --- C. BAHASA JAWA TIMURAN & KATA KASAR (UPDATED) ---\n",
" 'nek': 'kalau', 'iso': 'bisa', 'ra': 'tidak', 'ora': 'tidak', 'ae': 'saja',\n",
" 'wae': 'saja', 'akeh': 'banyak', 'seng': 'yang', 'sing': 'yang', 'wes': 'sudah',\n",
" 'wis': 'sudah', 'urung': 'belum', 'durung': 'belum', 'lapo': 'kenapa',\n",
" 'opo': 'apa', 'iki': 'ini', 'kuwi': 'itu', 'kae': 'itu', 'elek': 'jelek',\n",
" 'apik': 'bagus', 'onok': 'ada', 'karo': 'sama',\n",
"\n",
" # PERBAIKAN DI SINI:\n",
" 'cok': 'jancok', # Singkatan disamakan ke jancok\n",
" 'dancok': 'jancok', # Varian disamakan ke jancok\n",
" # 'jancok' KITA BIARKAN (TIDAK ADA DI KAMUS) AGAR TIDAK BERUBAH\n",
"\n",
" 'asu': 'anjing', # Hewan tetap diterjemahkan\n",
"\n",
" # --- D. PERBAIKAN TYPO ---\n",
" 'gabisa': 'tidak bisa', 'gaenak': 'tidak enak', 'soundhoreg': 'sound horeg',\n",
" 'soundsystem': 'sound system', 'gasemua': 'tidak semua',\n",
"\n",
" # --- E. PENGHAPUSAN (Kata tanpa makna) ---\n",
" 'wkwkwkw': '', 'wkwkw': '', 'wkwkwkwk': '', 'wkwk': '', 'sih': '', 'nya': ''\n",
"}\n",
"\n",
"# 3. PERSIAPAN SASTRAWI (MODIFIKASI KHUSUS INTOLERANSI)\n",
"factory_stop = StopWordRemoverFactory()\n",
"stopwords_list = factory_stop.get_stop_words()\n",
"\n",
"# WHITELIST: Kata yang HARAM dihapus\n",
"whitelist = [\n",
" 'tidak', 'enggak', 'bukan', 'jangan', 'tapi',\n",
" 'masalah', 'kurang', 'belum', 'tak', 'tanpa',\n",
" 'soal', 'sebab', 'karena', 'akibat', 'padahal'\n",
"]\n",
"\n",
"# Hapus whitelist dari daftar stopword bawaan\n",
"for word in whitelist:\n",
" if word in stopwords_list:\n",
" stopwords_list.remove(word)\n",
"\n",
"# Tambahkan stopword sampah\n",
"stopwords_list.extend(['min', 'kak', 'gan', 'sis', 'guys', 'halo', 'hai'])\n",
"\n",
"dictionary = ArrayDictionary(stopwords_list)\n",
"stopword_remover = StopWordRemover(dictionary)\n",
"\n",
"factory_stem = StemmerFactory()\n",
"stemmer = factory_stem.create_stemmer()\n",
"\n",
"# 4. FUNGSI PEMBERSIH UTAMA\n",
"def clean_text_complete(text):\n",
" if not isinstance(text, str):\n",
" return \"\"\n",
"\n",
" # A. Case Folding\n",
" text = text.lower()\n",
"\n",
" # B. Ganti simbol dengan spasi\n",
" text = re.sub(r'[^a-zA-Z0-9]', ' ', text)\n",
"\n",
" # C. Normalisasi Kata\n",
" words = text.split()\n",
" normalized_words = []\n",
" for w in words:\n",
" if w in kamus_alay:\n",
" # Jika ada di kamus, ganti. Jika replacement '', kata dihapus.\n",
" if kamus_alay[w] != '':\n",
" normalized_words.append(kamus_alay[w])\n",
" else:\n",
" # Jika tidak ada di kamus (misal: 'jancok'), biarkan apa adanya\n",
" normalized_words.append(w)\n",
"\n",
" text = ' '.join(normalized_words)\n",
"\n",
" # D. Stopword Removal\n",
" text = stopword_remover.remove(text)\n",
"\n",
" # E. Stemming\n",
" text = stemmer.stem(text)\n",
"\n",
" # F. Rapikan Spasi\n",
" text = re.sub(r'\\s+', ' ', text).strip()\n",
"\n",
" return text\n",
"\n",
"# 5. EKSEKUSI\n",
"print(\"Sedang memproses teks... (Mohon tunggu)\")\n",
"df['full_text_clean'] = df['full_text'].apply(clean_text_complete)\n",
"\n",
"# 6. SIMPAN HASIL\n",
"columns_to_save = ['created_at', 'favorite_count', 'full_text_clean']\n",
"valid_columns = [col for col in columns_to_save if col in df.columns]\n",
"df_final = df[valid_columns]\n",
"\n",
"# Rename kolom hasil bersih menjadi 'full_text' agar siap dipakai\n",
"df_final = df_final.rename(columns={'full_text_clean': 'full_text'})\n",
"\n",
"# Hapus baris kosong\n",
"df_final = df_final[df_final['full_text'].str.strip() != '']\n",
"\n",
"# Simpan\n",
"output_file = 'hasil_preprocessing_intoleransi_final.csv'\n",
"df_final.to_csv(output_file, index=False)\n",
"\n",
"print(\"\\n--- SELESAI! ---\")\n",
"print(f\"File siap disimpan sebagai: {output_file}\")\n",
"print(\"\\nContoh Hasil (5 baris pertama):\")\n",
"print(df_final.head())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3d1636b6",
"metadata": {},
"outputs": [],
"source": [
"#labeling data ke 3 kategori\n",
"\n",
"import pandas as pd\n",
"# 1. MOUNT DRIVE\n",
"# 2. LOAD DATA BERSIH\n",
"# Pastikan path file benar\n",
"input_filename = '/content/drive/MyDrive/proyek skiprsi sound horeg/hasil_preprocessing_intoleransi_final.csv'\n",
"df = pd.read_csv(input_filename)\n",
"\n",
"# 3. DEFINISI KATA KUNCI (3 KATEGORI)\n",
"\n",
"# KATA KUNCI RASIONAL (Label 1)\n",
"# Fokus: Dampak fisik, gangguan situasi, penolakan logis\n",
"keywords_rasional = [\n",
" 'ganggu', 'bising', 'berisik', 'brisik', 'polusi', 'tuli', 'budeg',\n",
" 'pecah', 'getar', 'runtuh', 'rusak', 'macet', 'blokir', 'tutup jalan',\n",
" 'sakit', 'pusing', 'jantung', 'bayi', 'orang tua', 'anak', 'nangis',\n",
" 'tidak setuju', 'tidak suka', 'tolak', 'keberatan', 'komplain',\n",
" 'aturan', 'izin', 'waktu', 'jam', 'solusi', 'saran', 'uang', 'biaya',\n",
" 'sebab', 'karena', 'gara', 'akibat', 'dampak', 'bikin', 'buat',\n",
" 'tidur', 'istirahat', 'belajar', 'ibadah', 'sholat', 'ngaji'\n",
"]\n",
"\n",
"# KATA KUNCI CACI MAKI (Label 2)\n",
"# Fokus: Hinaan personal, hewan, kotoran, ancaman kosong\n",
"keywords_cacian = [\n",
" 'jancok', 'cok', 'dancok', 'asu', 'anjing', 'bangsat', 'bajingan',\n",
" 'goblok', 'tolol', 'pekok', 'bodoh', 'bego', 'idiot', 'setan', 'iblis',\n",
" 'sakit jiwa', 'gila', 'edan', 'gendeng', 'sdm rendah', 'kampungan',\n",
" 'bakar', 'musnah', 'usir', 'mati', 'modar', 'sampah', 'norak',\n",
" 'jelek', 'buruk'\n",
"]\n",
"\n",
"# 4. FUNGSI LABELING 3 KATEGORI\n",
"def auto_label_3_class(text):\n",
" if not isinstance(text, str):\n",
" return 0 # Default Netral\n",
"\n",
" text_check = f\" {text} \"\n",
"\n",
" # PRIORITAS 1: Apakah ini Kritik Rasional? (Label 1)\n",
" # Aturan: Walaupun kasar, kalau ada poin rasional, masuk sini.\n",
" for word in keywords_rasional:\n",
" if f\" {word} \" in text_check:\n",
" return 1\n",
"\n",
" # PRIORITAS 2: Apakah ini Murni Caci Maki? (Label 2)\n",
" # Aturan: Kasar tapi tidak ada alasan jelas.\n",
" for word in keywords_cacian:\n",
" if f\" {word} \" in text_check:\n",
" return 2\n",
"\n",
" # PRIORITAS 3: Sisanya adalah Netral/Info (Label 0)\n",
" return 0\n",
"\n",
"# 5. EKSEKUSI\n",
"print(\"Sedang melabeli data menjadi 3 Kategori...\")\n",
"print(\"0: Netral | 1: Kritik Rasional | 2: Caci Maki\")\n",
"df['label'] = df['full_text'].apply(auto_label_3_class)\n",
"\n",
"# 6. CEK HASIL SEBARAN\n",
"counts = df['label'].value_counts().sort_index()\n",
"print(\"\\n--- Statistik Label Sementara ---\")\n",
"print(f\"Label 0 (Netral/Info) : {counts.get(0, 0)} data\")\n",
"print(f\"Label 1 (Kritik Rasional) : {counts.get(1, 0)} data\")\n",
"print(f\"Label 2 (Caci Maki Murni) : {counts.get(2, 0)} data\")\n",
"\n",
"# 7. SIMPAN KE FILE BARU\n",
"output_file = '/content/drive/MyDrive/proyek skiprsi sound horeg/data_label_3_kategori.csv'\n",
"df.to_csv(output_file, index=False)\n",
"print(f\"\\n[SUKSES] File siap diverifikasi manual: {output_file}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dd1ab0e0",
"metadata": {},
"outputs": [],
"source": []
}
],
"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
}

View File

@ -0,0 +1,124 @@
# 2 pelatihan model SVM, KNN dan ensemble
import joblib
import os
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.model_selection import GridSearchCV, cross_val_score
print("🏋️ [TAHAP 2] Training: DATA ASLI (Mode TURBO Aktif 🚀)...")
# --- PENYESUAIAN FOLDER ---
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
FOLDER_OUTPUT = os.path.join(BASE_DIR, 'output')
FOLDER_MODELS = os.path.join(BASE_DIR, 'models')
# 0. CEK KEAMANAN: Pastikan folder dan file Tahap 1 sudah ada
file_xtrain = f'{FOLDER_OUTPUT}/X_train.pkl'
if not os.path.exists(file_xtrain):
raise FileNotFoundError(f"❌ File '{file_xtrain}' tidak ditemukan! Pastikan kamu sudah menjalankan '1_preprocessing.py' terlebih dahulu.")
# Jaga-jaga kalau folder models belum ada, otomatis dibikin
os.makedirs(FOLDER_MODELS, exist_ok=True)
# 1. AMBIL DATA DARI TAHAP 1
print(" - Memuat data latih...")
X_train = joblib.load(f'{FOLDER_OUTPUT}/X_train.pkl')
y_train = joblib.load(f'{FOLDER_OUTPUT}/y_train.pkl')
print(f" - Jumlah Data Latih Asli: {len(y_train)} baris")
print(f" - Komposisi Label: {y_train.value_counts().to_dict()}")
# ---------------------------------------------------------
# 2. LATIH SVM (MODEL UTAMA)
# ---------------------------------------------------------
print("\n🚀 Melatih SVM (Mencari Settingan Terbaik)...")
print(" (Menggunakan seluruh inti CPU laptop...)")
param_svm = {
'C': [0.01, 0.1, 1, 10, 100],
'kernel': ['linear'],
}
svm_grid = GridSearchCV(SVC(probability=True, random_state=42), param_svm, cv=5, scoring='f1_macro', verbose=1, n_jobs=-1)
svm_grid.fit(X_train, y_train)
best_svm = svm_grid.best_estimator_
joblib.dump(best_svm, f'{FOLDER_MODELS}/model_svm.pkl')
# ⭐ TAMBAHAN: Print akurasi validasi & parameter terbaik SVM
svm_acc_val = svm_grid.best_score_ * 100
print(f" ✅ SVM Selesai (Akurasi Validasi: {svm_acc_val:.2f}%)")
print(f" 📌 Parameter Terbaik SVM: {svm_grid.best_params_}")
# ---------------------------------------------------------
# 3. LATIH KNN (METRIC COSINE)
# ---------------------------------------------------------
print("\n🚀 Melatih KNN (Wajib Cosine)...")
param_knn = {
'n_neighbors': [3, 5, 7, 9, 11, 13, 15],
'metric': ['cosine'],
'weights': ['uniform', 'distance']
}
knn_grid = GridSearchCV(KNeighborsClassifier(algorithm='brute'), param_knn, cv=5, scoring='f1_macro', verbose=1, n_jobs=-1)
knn_grid.fit(X_train, y_train)
best_knn = knn_grid.best_estimator_
joblib.dump(best_knn, f'{FOLDER_MODELS}/model_knn.pkl')
# ⭐ TAMBAHAN: Print akurasi validasi & parameter terbaik KNN
knn_acc_val = knn_grid.best_score_ * 100
print(f" ✅ KNN Selesai (Best K: {knn_grid.best_params_['n_neighbors']})")
print(f" 📌 Parameter Terbaik KNN: {knn_grid.best_params_}")
print(f" 📌 Akurasi Validasi KNN: {knn_acc_val:.2f}%")
# ---------------------------------------------------------
# 4. LATIH ENSEMBLE (SVM + KNN)
# ---------------------------------------------------------
print("\n🚀 Melatih ENSEMBLE (Voting SVM + KNN)...")
# Gabungkan dua model terbaik
ensemble_model = VotingClassifier(
estimators=[
('svm', best_svm),
('knn', best_knn)
],
voting='soft',
weights=[2, 1] # SVM kita beri bobot suara lebih tinggi
)
ensemble_model.fit(X_train, y_train)
joblib.dump(ensemble_model, f'{FOLDER_MODELS}/model_ensemble.pkl')
print(" ✅ Ensemble Selesai.")
# ⭐ TAMBAHAN: Hitung akurasi validasi Ensemble pakai cross_val_score
print(" 📌 Menghitung Akurasi Validasi Ensemble (5-fold CV)...")
print(" (Sabar ya, ini agak lama karena ensemble = SVM + KNN x 5 fold)")
ensemble_scores = cross_val_score(ensemble_model, X_train, y_train, cv=5, scoring='f1_macro', n_jobs=-1)
ensemble_acc_val = ensemble_scores.mean() * 100
print(f" 📌 Akurasi Validasi Ensemble: {ensemble_acc_val:.2f}%")
# ---------------------------------------------------------
# 5. RINGKASAN AKHIR
# ---------------------------------------------------------
print("\n" + "="*60)
print("📊 RINGKASAN HASIL TRAINING")
print("="*60)
print(f"\n🔹 SVM")
print(f" Parameter : {svm_grid.best_params_}")
print(f" Akurasi Validasi : {svm_acc_val:.2f}%")
print(f"\n🔹 KNN")
print(f" Parameter : {knn_grid.best_params_}")
print(f" Akurasi Validasi : {knn_acc_val:.2f}%")
print(f"\n🔹 Ensemble (SVM + KNN)")
print(f" Voting : soft, weights=[2, 1]")
print(f" Akurasi Validasi : {ensemble_acc_val:.2f}%")
print("\n" + "="*60)
print("🎉 TRAINING DATA MURNI SELESAI!")
print("👉 Silakan jalankan '3_evaluasi.py' untuk melihat hasil akhirnya.")
print("="*60)

View File

@ -0,0 +1,140 @@
# 2 pelatihan model SVM, KNN dan ensemble
import joblib
import pandas as pd
import os
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.model_selection import GridSearchCV, cross_val_score
print("🏋️ [TAHAP 2] Training: DATA ASLI (Mode TURBO Aktif 🚀)...")
# --- PENYESUAIAN FOLDER ---
FOLDER_OUTPUT = 'virtualEnvironment/output'
FOLDER_MODELS = 'virtualEnvironment/models'
# 0. CEK KEAMANAN: Pastikan folder dan file Tahap 1 sudah ada
file_xtrain = f'{FOLDER_OUTPUT}/X_train.pkl'
if not os.path.exists(file_xtrain):
raise FileNotFoundError(f"❌ File '{file_xtrain}' tidak ditemukan! Pastikan kamu sudah menjalankan '1_preprocessing.py' terlebih dahulu.")
# Jaga-jaga kalau folder models belum ada, otomatis dibikin
os.makedirs(FOLDER_MODELS, exist_ok=True)
# 1. AMBIL DATA DARI TAHAP 1
print(" - Memuat data latih...")
X_train = joblib.load(f'{FOLDER_OUTPUT}/X_train.pkl')
y_train = joblib.load(f'{FOLDER_OUTPUT}/y_train.pkl')
print(f" - Jumlah Data Latih Asli: {len(y_train)} baris")
print(f" - Komposisi Label: {y_train.value_counts().to_dict()}")
# ---------------------------------------------------------
# 2. LATIH SVM (MODEL UTAMA)
# ---------------------------------------------------------
print("\n🚀 Melatih SVM (Mencari Settingan Terbaik)...")
print(" (Menggunakan seluruh inti CPU laptop...)")
param_svm = {
'C': [0.01, 0.1, 1, 10, 100],
'kernel': ['linear'],
'gamma': ['scale']
}
svm_grid = GridSearchCV(
SVC(probability=True, random_state=42),
param_svm,
cv=5,
scoring='f1_macro',
verbose=1,
n_jobs=-1
)
svm_grid.fit(X_train, y_train)
best_svm = svm_grid.best_estimator_
joblib.dump(best_svm, f'{FOLDER_MODELS}/model_svm.pkl')
# ⭐ TAMBAHAN: Print akurasi validasi & parameter terbaik SVM
svm_acc_val = svm_grid.best_score_ * 100
print(f" ✅ SVM Selesai (F1-Macro Validasi: {svm_acc_val:.2f}%)")
print(f" 📌 Parameter Terbaik SVM: {svm_grid.best_params_}")
# ---------------------------------------------------------
# 3. LATIH KNN (METRIC COSINE)
# ---------------------------------------------------------
print("\n🚀 Melatih KNN (Wajib Cosine)...")
param_knn = {
'n_neighbors': [3, 5, 7, 9, 11, 15, 21, 25],
'metric': ['cosine'],
'weights': ['uniform', 'distance']
}
knn_grid = GridSearchCV(
KNeighborsClassifier(),
param_knn,
cv=5,
scoring='f1_macro',
verbose=1,
n_jobs=-1
)
knn_grid.fit(X_train, y_train)
best_knn = knn_grid.best_estimator_
joblib.dump(best_knn, f'{FOLDER_MODELS}/model_knn.pkl')
# ⭐ TAMBAHAN: Print akurasi validasi & parameter terbaik KNN
knn_acc_val = knn_grid.best_score_ * 100
print(f" ✅ KNN Selesai (Best K: {knn_grid.best_params_['n_neighbors']})")
print(f" 📌 Parameter Terbaik KNN: {knn_grid.best_params_}")
print(f" 📌 F1-Macro Validasi KNN: {knn_acc_val:.2f}%")
# ---------------------------------------------------------
# 4. LATIH ENSEMBLE (SVM + KNN)
# ---------------------------------------------------------
print("\n🚀 Melatih ENSEMBLE (Voting SVM + KNN)...")
# Gabungkan dua model terbaik
ensemble_model = VotingClassifier(
estimators=[
('svm', best_svm),
('knn', best_knn)
],
voting='soft',
weights=[2, 1] # SVM kita beri bobot suara lebih tinggi
)
ensemble_model.fit(X_train, y_train)
joblib.dump(ensemble_model, f'{FOLDER_MODELS}/model_ensemble.pkl')
print(" ✅ Ensemble Selesai.")
# ⭐ TAMBAHAN: Hitung akurasi validasi Ensemble pakai cross_val_score
print(" 📌 Menghitung Akurasi Validasi Ensemble (3-fold CV)...")
print(" (Sabar ya, ini agak lama karena ensemble = SVM + KNN x 3 fold)")
ensemble_scores = cross_val_score(ensemble_model, X_train, y_train, cv=3, n_jobs=-1)
ensemble_acc_val = ensemble_scores.mean() * 100
print(f" 📌 Akurasi Validasi Ensemble: {ensemble_acc_val:.2f}%")
# ---------------------------------------------------------
# 5. RINGKASAN AKHIR
# ---------------------------------------------------------
print("\n" + "="*60)
print("📊 RINGKASAN HASIL TRAINING")
print("="*60)
print(f"\n🔹 SVM")
print(f" Parameter : {svm_grid.best_params_}")
print(f" Akurasi Validasi : {svm_acc_val:.2f}%")
print(f"\n🔹 KNN")
print(f" Parameter : {knn_grid.best_params_}")
print(f" Akurasi Validasi : {knn_acc_val:.2f}%")
print(f"\n🔹 Ensemble (SVM + KNN)")
print(f" Voting : soft, weights=[2, 1]")
print(f" Akurasi Validasi : {ensemble_acc_val:.2f}%")
print("\n" + "="*60)
print("🎉 TRAINING DATA MURNI SELESAI!")
print("👉 Silakan jalankan '3_evaluasi.py' untuk melihat hasil akhirnya.")
print("="*60)