inisiasi project
This commit is contained in:
commit
07282b085c
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
3.11.9
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
python-3.11.9
|
||||
|
|
@ -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.
|
||||
|
|
@ -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)")
|
||||
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
|
|
@ -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()
|
||||
|
|
@ -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")
|
||||
|
|
@ -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.")
|
||||
|
|
@ -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}")
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
Binary file not shown.
Binary file not shown.
|
|
@ -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")
|
||||
|
|
@ -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'")
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
Loading…
Reference in New Issue