update
This commit is contained in:
parent
2f52a2e70c
commit
3aa8b81285
Binary file not shown.
|
|
@ -0,0 +1,32 @@
|
|||
import joblib
|
||||
import numpy as np
|
||||
|
||||
# Load model
|
||||
model = joblib.load('ml_assets/ml_pipeline_internal.pkl')
|
||||
|
||||
# Ekstrak komponen
|
||||
vec = model.named_steps['tfidf']
|
||||
clf = model.named_steps['clf']
|
||||
|
||||
# Dapatkan vocabulary
|
||||
feature_names = vec.get_feature_names_out()
|
||||
|
||||
# Dapatkan bobot koefisien
|
||||
coefficients = clf.coef_
|
||||
intercepts = clf.intercept_
|
||||
|
||||
# Tampilkan bobot untuk kata-kata di korpus mini
|
||||
target_words = ['web', 'develop', 'analyst']
|
||||
|
||||
print("BOBOT KOEFISIEN RIIL:")
|
||||
print("=" * 60)
|
||||
for word in target_words:
|
||||
if word in feature_names:
|
||||
idx = list(feature_names).index(word)
|
||||
print(f"\nKata: '{word}'")
|
||||
for i, cls in enumerate(clf.classes_):
|
||||
print(f" Kelas {cls}: {coefficients[i][idx]:.4f}")
|
||||
|
||||
print("\nINTERSEP (Bias):")
|
||||
for i, cls in enumerate(clf.classes_):
|
||||
print(f" Kelas {cls}: {intercepts[i]:.4f}")
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 114 KiB |
|
|
@ -1 +0,0 @@
|
|||
{"status": "error", "message": "Processing failed: 'charmap' codec can't encode character '\\U0001f602' in position 885944: character maps to <undefined>", "processed_rows": 0, "total_rows": 0, "source_type": "unknown"}
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"status": "error", "message": "Processing failed: 'charmap' codec can't encode character '\\U0001f602' in position 885944: character maps to <undefined>", "processed_rows": 0, "total_rows": 0, "source_type": "unknown"}
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"status": "error", "message": "Processing failed: 'charmap' codec can't encode character '\\U0001f602' in position 885944: character maps to <undefined>", "processed_rows": 0, "total_rows": 0, "source_type": "unknown"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,29 +0,0 @@
|
|||
{"timestamp": "2026-06-05T19:08:42.267450", "level": "INFO", "logger": "retrain_worker", "message": "[init] Log file tersimpan di: D:\\msaif\\Project\\Laravel\\sista_mif_ta\\fastapi\\ml_assets\\logs\\retrain_log_20260605_190842.jsonl"}
|
||||
{"timestamp": "2026-06-05T19:08:42.267450", "level": "INFO", "logger": "retrain_worker", "message": "Retraining session dimulai. Log file: retrain_log_20260605_190842.jsonl", "stage": "started", "event_type": "session_start", "metadata": {"extra_csv_provided": true}}
|
||||
{"timestamp": "2026-06-05T19:08:42.268452", "level": "INFO", "logger": "retrain_worker", "message": "[started] Worker dimulai", "stage": "started"}
|
||||
{"timestamp": "2026-06-05T19:08:42.269454", "level": "INFO", "logger": "retrain_worker", "message": "[backup] Membuat backup model lama...", "stage": "backup"}
|
||||
{"timestamp": "2026-06-05T19:08:42.271473", "level": "INFO", "logger": "retrain_worker", "message": "Backup tersimpan: D:\\msaif\\Project\\Laravel\\sista_mif_ta\\fastapi\\ml_assets\\ml_pipeline_internal.pkl.bak"}
|
||||
{"timestamp": "2026-06-05T19:08:42.271473", "level": "INFO", "logger": "retrain_worker", "message": "[loading_data] Memuat dan menggabungkan data training...", "stage": "loading_data"}
|
||||
{"timestamp": "2026-06-05T19:08:42.275472", "level": "INFO", "logger": "retrain_worker", "message": "Corpus asli: 250 baris"}
|
||||
{"timestamp": "2026-06-05T19:08:42.445530", "level": "INFO", "logger": "retrain_worker", "message": "Data manual_override: 1 baris | weight=6.6648x (log-damped) | influence aktual=2.6% (target nominal 30%)"}
|
||||
{"timestamp": "2026-06-05T19:08:42.465047", "level": "INFO", "logger": "retrain_worker", "message": "Total setelah merge (tanpa deduplicate): 251 baris"}
|
||||
{"timestamp": "2026-06-05T19:08:42.465047", "level": "INFO", "logger": "retrain_worker", "message": "Data training siap: 251 baris", "stage": "loading_data", "event_type": "data_merged", "metadata": {"corpus_rows": 250, "override_rows": 1, "override_weight": 6.6648, "influence_pct": 2.6, "total_merged": 251}}
|
||||
{"timestamp": "2026-06-05T19:08:42.479066", "level": "INFO", "logger": "retrain_worker", "message": "Dynamic split: 175 train | 76 test"}
|
||||
{"timestamp": "2026-06-05T19:08:42.480074", "level": "INFO", "logger": "retrain_worker", "message": "[evaluating] Mengevaluasi model lama pada test set baru...", "stage": "evaluating"}
|
||||
{"timestamp": "2026-06-05T19:08:42.492120", "level": "INFO", "logger": "retrain_worker", "message": "Baseline F1 (old model, same test set): 0.9059"}
|
||||
{"timestamp": "2026-06-05T19:08:42.494119", "level": "INFO", "logger": "retrain_worker", "message": "[training] K-Fold validation & training... (175 sampel)", "stage": "training"}
|
||||
{"timestamp": "2026-06-05T19:08:42.534414", "level": "INFO", "logger": "retrain_worker", "message": "[training] K-Fold selesai: fold 1/5 | acc=0.5143", "stage": "training"}
|
||||
{"timestamp": "2026-06-05T19:08:42.542922", "level": "INFO", "logger": "retrain_worker", "message": "[training] K-Fold selesai: fold 2/5 | acc=0.6857", "stage": "training"}
|
||||
{"timestamp": "2026-06-05T19:08:42.552262", "level": "INFO", "logger": "retrain_worker", "message": "[training] K-Fold selesai: fold 3/5 | acc=0.5429", "stage": "training"}
|
||||
{"timestamp": "2026-06-05T19:08:42.561273", "level": "INFO", "logger": "retrain_worker", "message": "[training] K-Fold selesai: fold 4/5 | acc=0.5714", "stage": "training"}
|
||||
{"timestamp": "2026-06-05T19:08:42.569782", "level": "INFO", "logger": "retrain_worker", "message": "[training] K-Fold selesai: fold 5/5 | acc=0.7143", "stage": "training"}
|
||||
{"timestamp": "2026-06-05T19:08:42.569782", "level": "INFO", "logger": "retrain_worker", "message": "K-Fold 5 lipatan selesai. Akurasi per fold: [0.5143, 0.6857, 0.5429, 0.5714, 0.7143]", "stage": "training", "event_type": "kfold_completed", "metadata": {"fold_accuracies": [0.5143, 0.6857, 0.5429, 0.5714, 0.7143], "mean_accuracy": 0.6057, "std_accuracy": 0.0796}}
|
||||
{"timestamp": "2026-06-05T19:08:42.570788", "level": "INFO", "logger": "retrain_worker", "message": "[training] Training model final pada seluruh data training...", "stage": "training"}
|
||||
{"timestamp": "2026-06-05T19:08:42.579235", "level": "INFO", "logger": "retrain_worker", "message": "Candidate model tersimpan: D:\\msaif\\Project\\Laravel\\sista_mif_ta\\fastapi\\ml_assets\\ml_pipeline_candidate.pkl"}
|
||||
{"timestamp": "2026-06-05T19:08:42.589948", "level": "INFO", "logger": "retrain_worker", "message": "[evaluating] Mengevaluasi model baru vs model lama (test set sama)...", "stage": "evaluating"}
|
||||
{"timestamp": "2026-06-05T19:08:42.593983", "level": "INFO", "logger": "retrain_worker", "message": "Baseline F1 (old model, same test) : 0.9059"}
|
||||
{"timestamp": "2026-06-05T19:08:42.593983", "level": "INFO", "logger": "retrain_worker", "message": "Candidate F1 (new model, same test): 0.7011 (delta: -0.2048)"}
|
||||
{"timestamp": "2026-06-05T19:08:42.593983", "level": "INFO", "logger": "retrain_worker", "message": "Evaluasi selesai. Delta F1: -0.2048 (rollback)", "stage": "evaluating", "event_type": "evaluation_completed", "metadata": {"baseline_f1": 0.9059, "candidate_f1": 0.7011, "delta": -0.2048, "delta_pct": -20.48, "threshold_min": 0.01, "decision": "rollback", "reason": "Model baru lebih buruk secara signifikan (-20.48% weighted F1)", "new_accuracy": 0.6711, "per_class_f1": {"Programmer": 0.8, "Data Analyst": 0.19, "Wirausaha Informatika": 0.474, "Non-IT": 0.897}}}
|
||||
{"timestamp": "2026-06-05T19:08:42.593983", "level": "INFO", "logger": "retrain_worker", "message": "Rollback berhasil: pkl lama dipulihkan dari .bak"}
|
||||
{"timestamp": "2026-06-05T19:08:42.593983", "level": "INFO", "logger": "retrain_worker", "message": "Model lama dipertahankan. Model baru lebih buruk secara signifikan (-20.48% weighted F1)", "stage": "rolled_back", "event_type": "rollback", "metadata": {"reason": "Model baru lebih buruk secara signifikan (-20.48% weighted F1)", "old_f1": 0.9059, "new_f1": 0.7011}}
|
||||
{"timestamp": "2026-06-05T19:08:42.594976", "level": "INFO", "logger": "retrain_worker", "message": "[rolled_back] Model lama dipertahankan. Model baru lebih buruk secara signifikan (-20.48% weighted F1)", "stage": "rolled_back"}
|
||||
Binary file not shown.
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"stage": "rolled_back",
|
||||
"message": "Model lama dipertahankan. Model baru lebih buruk secara signifikan (-20.44% weighted F1)",
|
||||
"timestamp": "2026-06-08T11:35:03.809787",
|
||||
"result": "rolled_back",
|
||||
"reason": "Model baru lebih buruk secara signifikan (-20.44% weighted F1)",
|
||||
"old_f1": 0.9064,
|
||||
"new_f1": 0.702,
|
||||
"_reloaded": true
|
||||
}
|
||||
|
|
@ -0,0 +1,409 @@
|
|||
import pandas as pd
|
||||
import numpy as np
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
import math
|
||||
|
||||
# Buat workbook
|
||||
wb = Workbook()
|
||||
|
||||
# ============================================
|
||||
# SHEET 1: KORPUS & TOKENISASI
|
||||
# ============================================
|
||||
ws1 = wb.active
|
||||
ws1.title = "1. Korpus & Tokenisasi"
|
||||
|
||||
# Header
|
||||
ws1['A1'] = "DOKUMEN"
|
||||
ws1['B1'] = "TEKS ASLI"
|
||||
ws1['C1'] = "TEKS SETEMAH STEMMING (Sastrawi)"
|
||||
ws1['D1'] = "KETERANGAN"
|
||||
|
||||
# Styling header
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=11)
|
||||
for cell in ws1[1]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
# Data
|
||||
data_korpus = [
|
||||
["D1", "web developer", "web develop", "Stemming: developer → develop"],
|
||||
["D2", "web analyst", "web analyst", "Tidak berubah"],
|
||||
["D3", "web developer analyst", "web develop analyst", "Stemming: developer → develop"]
|
||||
]
|
||||
|
||||
for i, row in enumerate(data_korpus, start=2):
|
||||
for j, val in enumerate(row, start=1):
|
||||
ws1.cell(row=i, column=j, value=val)
|
||||
|
||||
# Catatan penting
|
||||
ws1['A6'] = "CATATAN PENTING:"
|
||||
ws1['A6'].font = Font(bold=True, color="FF0000")
|
||||
ws1['A7'] = "1. Korpus ini dirancang agar setiap kata muncul minimal 2x (kompatibel dengan min_df=2)"
|
||||
ws1['A8'] = "2. Stemming Sastrawi mengubah 'developer' menjadi 'develop'"
|
||||
ws1['A9'] = "3. Total dokumen N = 3"
|
||||
|
||||
# Set column width
|
||||
ws1.column_dimensions['A'].width = 12
|
||||
ws1.column_dimensions['B'].width = 25
|
||||
ws1.column_dimensions['C'].width = 30
|
||||
ws1.column_dimensions['D'].width = 40
|
||||
|
||||
# ============================================
|
||||
# SHEET 2: MATRIKS TF & SUBLINEAR TF
|
||||
# ============================================
|
||||
ws2 = wb.create_sheet("2. Matriks TF")
|
||||
|
||||
# Matriks TF Mentah
|
||||
ws2['A1'] = "MATRIKS TF MENTAH (Frekuensi Term)"
|
||||
ws2['A1'].font = Font(bold=True, size=12)
|
||||
|
||||
ws2['A2'] = "Dokumen"
|
||||
ws2['B2'] = "web"
|
||||
ws2['C2'] = "develop"
|
||||
ws2['D2'] = "analyst"
|
||||
|
||||
for cell in ws2[2]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Data TF
|
||||
tf_data = [
|
||||
["D1", 1, 1, 0],
|
||||
["D2", 1, 0, 1],
|
||||
["D3", 1, 1, 1]
|
||||
]
|
||||
|
||||
for i, row in enumerate(tf_data, start=3):
|
||||
for j, val in enumerate(row, start=1):
|
||||
ws2.cell(row=i, column=j, value=val)
|
||||
|
||||
# Sublinear TF
|
||||
ws2['A7'] = "SUBLINEAR TF (1 + ln(tf))"
|
||||
ws2['A7'].font = Font(bold=True, size=12)
|
||||
|
||||
ws2['A8'] = "Dokumen"
|
||||
ws2['B8'] = "web"
|
||||
ws2['C8'] = "develop"
|
||||
ws2['D8'] = "analyst"
|
||||
|
||||
for cell in ws2[8]:
|
||||
cell.fill = PatternFill(start_color="70AD47", end_color="70AD47", fill_type="solid")
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Hitung Sublinear TF
|
||||
sublinear_data = [
|
||||
["D1", 1 + math.log(1), 1 + math.log(1), 0],
|
||||
["D2", 1 + math.log(1), 0, 1 + math.log(1)],
|
||||
["D3", 1 + math.log(1), 1 + math.log(1), 1 + math.log(1)]
|
||||
]
|
||||
|
||||
for i, row in enumerate(sublinear_data, start=9):
|
||||
for j, val in enumerate(row, start=1):
|
||||
if j == 1:
|
||||
ws2.cell(row=i, column=j, value=val)
|
||||
else:
|
||||
ws2.cell(row=i, column=j, value=round(val, 4))
|
||||
|
||||
ws2['A13'] = "CATATAN:"
|
||||
ws2['A13'].font = Font(bold=True)
|
||||
ws2['A14'] = "Karena semua TF bernilai 0 atau 1, dan ln(1) = 0, maka Sublinear TF = TF mentah"
|
||||
|
||||
# Set column width
|
||||
for col in ['A', 'B', 'C', 'D']:
|
||||
ws2.column_dimensions[col].width = 15
|
||||
|
||||
# ============================================
|
||||
# SHEET 3: PERHITUNGAN DF & SMOOTH IDF
|
||||
# ============================================
|
||||
ws3 = wb.create_sheet("3. Smooth IDF")
|
||||
|
||||
ws3['A1'] = "PERHITUNGAN DOCUMENT FREQUENCY (DF) & SMOOTH IDF"
|
||||
ws3['A1'].font = Font(bold=True, size=12)
|
||||
|
||||
ws3['A3'] = "Kata (Vocabulary)"
|
||||
ws3['B3'] = "DF (Jumlah Dokumen)"
|
||||
ws3['C3'] = "Rumus Smooth IDF Scikit-Learn"
|
||||
ws3['D3'] = "Nilai IDF"
|
||||
|
||||
for cell in ws3[3]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Data DF dan IDF
|
||||
N = 3
|
||||
df_data = [
|
||||
["web", 3],
|
||||
["develop", 2],
|
||||
["analyst", 2]
|
||||
]
|
||||
|
||||
for i, (word, df) in enumerate(df_data, start=4):
|
||||
ws3.cell(row=i, column=1, value=word)
|
||||
ws3.cell(row=i, column=2, value=df)
|
||||
|
||||
# Rumus: ln((1+N)/(1+df)) + 1
|
||||
rumus = f"=LN((1+{N})/(1+{df}))+1"
|
||||
ws3.cell(row=i, column=3, value=rumus)
|
||||
|
||||
# Hitung nilai
|
||||
idf_value = math.log((1 + N) / (1 + df)) + 1
|
||||
ws3.cell(row=i, column=4, value=round(idf_value, 4))
|
||||
ws3.cell(row=i, column=4).fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
|
||||
|
||||
ws3['A8'] = "CATATAN:"
|
||||
ws3['A8'].font = Font(bold=True)
|
||||
ws3['A9'] = f"Total Dokumen N = {N}"
|
||||
ws3['A10'] = "Rumus Smooth IDF: ln((1+N)/(1+df)) + 1"
|
||||
ws3['A11'] = "Smooth IDF mencegah pembagian dengan nol dan menstabilkan bobot untuk term langka"
|
||||
|
||||
# Highlight contoh perhitungan
|
||||
ws3['A13'] = "CONTOH PERHITUNGAN MANUAL untuk kata 'web':"
|
||||
ws3['A13'].font = Font(bold=True, color="FF0000")
|
||||
ws3['A14'] = f"IDF = ln((1+3)/(1+3)) + 1 = ln(4/4) + 1 = ln(1) + 1 = 0 + 1 = 1.0000"
|
||||
ws3['A15'] = f"IDF = ln((1+3)/(1+2)) + 1 = ln(4/3) + 1 = 0.2877 + 1 = 1.2877"
|
||||
|
||||
for col in ['A', 'B', 'C', 'D']:
|
||||
ws3.column_dimensions[col].width = 30
|
||||
|
||||
# ============================================
|
||||
# SHEET 4: MATRIKS TF-IDF & NORMALISASI L2
|
||||
# ============================================
|
||||
ws4 = wb.create_sheet("4. TF-IDF & L2 Norm")
|
||||
|
||||
ws4['A1'] = "MATRIKS TF-IDF MENTAH (Sublinear TF × IDF)"
|
||||
ws4['A1'].font = Font(bold=True, size=12)
|
||||
|
||||
ws4['A3'] = "Dokumen"
|
||||
ws4['B3'] = "web"
|
||||
ws4['C3'] = "develop"
|
||||
ws4['D3'] = "analyst"
|
||||
|
||||
for cell in ws4[3]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Hitung TF-IDF mentah
|
||||
idf_values = {
|
||||
"web": 1.0000,
|
||||
"develop": 1.2877,
|
||||
"analyst": 1.2877
|
||||
}
|
||||
|
||||
tfidf_data = [
|
||||
["D1", 1 * idf_values["web"], 1 * idf_values["develop"], 0],
|
||||
["D2", 1 * idf_values["web"], 0, 1 * idf_values["analyst"]],
|
||||
["D3", 1 * idf_values["web"], 1 * idf_values["develop"], 1 * idf_values["analyst"]]
|
||||
]
|
||||
|
||||
for i, row in enumerate(tfidf_data, start=4):
|
||||
for j, val in enumerate(row, start=1):
|
||||
if j == 1:
|
||||
ws4.cell(row=i, column=j, value=val)
|
||||
else:
|
||||
ws4.cell(row=i, column=j, value=round(val, 4))
|
||||
|
||||
# L2 Norm
|
||||
ws4['A8'] = "L2 NORM (Panjang Vektor per Dokumen)"
|
||||
ws4['A8'].font = Font(bold=True, size=12)
|
||||
|
||||
ws4['A10'] = "Dokumen"
|
||||
ws4['B10'] = "L2 Norm = √(x₁² + x₂² + x₃²)"
|
||||
|
||||
for cell in ws4[10]:
|
||||
cell.fill = PatternFill(start_color="70AD47", end_color="70AD47", fill_type="solid")
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Hitung L2 Norm
|
||||
l2_norms = []
|
||||
for row in tfidf_data:
|
||||
values = [v for v in row[1:] if isinstance(v, (int, float))]
|
||||
l2 = math.sqrt(sum(v**2 for v in values))
|
||||
l2_norms.append(l2)
|
||||
ws4.cell(row=11 + tfidf_data.index(row), column=1, value=row[0])
|
||||
ws4.cell(row=11 + tfidf_data.index(row), column=2, value=round(l2, 4))
|
||||
ws4.cell(row=11 + tfidf_data.index(row), column=2).fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
|
||||
|
||||
# Vektor Fitur Akhir (Ternormalisasi L2)
|
||||
ws4['A15'] = "VEKTOR FITUR AKHIR (TF-IDF Ternormalisasi L2)"
|
||||
ws4['A15'].font = Font(bold=True, size=12, color="FF0000")
|
||||
|
||||
ws4['A17'] = "Dokumen"
|
||||
ws4['B17'] = "web"
|
||||
ws4['C17'] = "develop"
|
||||
ws4['D17'] = "analyst"
|
||||
|
||||
for cell in ws4[17]:
|
||||
cell.fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
|
||||
cell.font = Font(bold=True, color="FFFFFF")
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Hitung vektor ternormalisasi
|
||||
for i, (row, l2) in enumerate(zip(tfidf_data, l2_norms), start=18):
|
||||
ws4.cell(row=i, column=1, value=row[0])
|
||||
for j, val in enumerate(row[1:], start=2):
|
||||
if isinstance(val, (int, float)) and val > 0:
|
||||
normalized = val / l2
|
||||
ws4.cell(row=i, column=j, value=round(normalized, 4))
|
||||
else:
|
||||
ws4.cell(row=i, column=j, value=0)
|
||||
|
||||
ws4['A22'] = "CATATAN:"
|
||||
ws4['A22'].font = Font(bold=True)
|
||||
ws4['A23'] = "Normalisasi L2 memastikan panjang vektor = 1 (standar Scikit-Learn)"
|
||||
ws4['A24'] = "Vektor ini yang akan dikalikan dengan bobot koefisien model Logistic Regression"
|
||||
|
||||
for col in ['A', 'B', 'C', 'D']:
|
||||
ws4.column_dimensions[col].width = 20
|
||||
|
||||
# ============================================
|
||||
# SHEET 5: SIMULASI INFERENSI LOGISTIC REGRESSION
|
||||
# ============================================
|
||||
ws5 = wb.create_sheet("5. Inferensi Manual")
|
||||
|
||||
ws5['A1'] = "SIMULASI FORWARD PASS LOGISTIC REGRESSION"
|
||||
ws5['A1'].font = Font(bold=True, size=12)
|
||||
|
||||
ws5['A3'] = "Dokumen Uji: D1 ('web develop')"
|
||||
ws5['A3'].font = Font(bold=True, color="FF0000")
|
||||
|
||||
# Vektor fitur dari Sheet 4
|
||||
ws5['A5'] = "VEKTOR FITUR (x)"
|
||||
ws5['A5'].font = Font(bold=True)
|
||||
|
||||
ws5['A6'] = "Fitur"
|
||||
ws5['B6'] = "Nilai (x)"
|
||||
|
||||
for cell in ws5[6]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
|
||||
# Ambil vektor D1 yang sudah dinormalisasi
|
||||
vec_d1 = {
|
||||
"web": round(1.0000 / l2_norms[0], 4),
|
||||
"develop": round(1 * idf_values["develop"] / l2_norms[0], 4),
|
||||
"analyst": 0
|
||||
}
|
||||
|
||||
features = ["web", "develop", "analyst"]
|
||||
for i, feat in enumerate(features, start=7):
|
||||
ws5.cell(row=i, column=1, value=feat)
|
||||
ws5.cell(row=i, column=2, value=vec_d1[feat])
|
||||
|
||||
# Bobot koefisien (simulasi berdasarkan output model)
|
||||
ws5['A11'] = "BOBOT KOEFISIEN MODEL (w)"
|
||||
ws5['A11'].font = Font(bold=True)
|
||||
|
||||
ws5['A12'] = "Fitur"
|
||||
ws5['B12'] = "Kelas Programmer"
|
||||
ws5['C12'] = "Kelas Non-IT"
|
||||
ws5['D12'] = "Kelas Data Analyst"
|
||||
ws5['E12'] = "Kelas Wirausaha"
|
||||
|
||||
for cell in ws5[12]:
|
||||
cell.fill = PatternFill(start_color="70AD47", end_color="70AD47", fill_type="solid")
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Bobot koefisien (disesuaikan dengan output model)
|
||||
weights = {
|
||||
"web": [0.7727, -0.5, -0.3, 0.2],
|
||||
"develop": [1.2192, -0.4, -0.2464, 0.1],
|
||||
"analyst": [-1.0188, -0.3, 2.8400, -0.6561]
|
||||
}
|
||||
|
||||
for i, feat in enumerate(features, start=13):
|
||||
ws5.cell(row=i, column=1, value=feat)
|
||||
for j, w in enumerate(weights[feat], start=2):
|
||||
ws5.cell(row=i, column=j, value=w)
|
||||
|
||||
# Intersep (bias)
|
||||
ws5['A17'] = "Intersep (b)"
|
||||
ws5['B17'] = -0.5
|
||||
ws5['C17'] = -0.895
|
||||
ws5['D17'] = -1.2
|
||||
ws5['E17'] = -0.45
|
||||
|
||||
for cell in ws5[17]:
|
||||
cell.fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
|
||||
|
||||
# Hitung skor linear z = w^T * x + b
|
||||
ws5['A19'] = "SKOR LINEAR (z = w^T × x + b)"
|
||||
ws5['A19'].font = Font(bold=True, size=12)
|
||||
|
||||
ws5['A20'] = "Kelas"
|
||||
ws5['B20'] = "Skor (z)"
|
||||
|
||||
for cell in ws5[20]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
|
||||
classes = ["Programmer", "Non-IT", "Data Analyst", "Wirausaha"]
|
||||
z_scores = []
|
||||
|
||||
for i, cls in enumerate(classes, start=21):
|
||||
ws5.cell(row=i, column=1, value=cls)
|
||||
|
||||
# Hitung z = sum(w * x) + b
|
||||
z = 0
|
||||
for feat in features:
|
||||
z += weights[feat][i-21] * vec_d1[feat]
|
||||
z += [-0.5, -0.895, -1.2, -0.45][i-21]
|
||||
|
||||
z_scores.append(z)
|
||||
ws5.cell(row=i, column=2, value=round(z, 4))
|
||||
|
||||
# Hitung Softmax
|
||||
ws5['A26'] = "PERHITUNGAN SOFTMAX (Probabilitas Posterior)"
|
||||
ws5['A26'].font = Font(bold=True, size=12)
|
||||
|
||||
ws5['A27'] = "Kelas"
|
||||
ws5['B27'] = "z"
|
||||
ws5['C27'] = "e^z"
|
||||
ws5['D27'] = "Probabilitas"
|
||||
|
||||
for cell in ws5[27]:
|
||||
cell.fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
|
||||
cell.font = Font(bold=True, color="FFFFFF")
|
||||
cell.alignment = Alignment(horizontal="center")
|
||||
|
||||
# Hitung e^z
|
||||
exp_z = [math.exp(z) for z in z_scores]
|
||||
sum_exp_z = sum(exp_z)
|
||||
|
||||
for i, (cls, z, e_z) in enumerate(zip(classes, z_scores, exp_z), start=28):
|
||||
ws5.cell(row=i, column=1, value=cls)
|
||||
ws5.cell(row=i, column=2, value=round(z, 4))
|
||||
ws5.cell(row=i, column=3, value=round(e_z, 4))
|
||||
|
||||
prob = e_z / sum_exp_z
|
||||
ws5.cell(row=i, column=4, value=f"{prob:.4f} ({prob*100:.1f}%)")
|
||||
ws5.cell(row=i, column=4).fill = PatternFill(start_color="00FF00", end_color="00FF00", fill_type="solid")
|
||||
|
||||
ws5['A33'] = f"Total Penyebut (Σe^z): {sum_exp_z:.4f}"
|
||||
ws5['A33'].font = Font(bold=True)
|
||||
|
||||
ws5['A35'] = "KESIMPULAN:"
|
||||
ws5['A35'].font = Font(bold=True, color="FF0000", size=12)
|
||||
ws5['A36'] = f"Probabilitas tertinggi: {max(exp_z)/sum_exp_z*100:.1f}% untuk kelas {classes[exp_z.index(max(exp_z))]}"
|
||||
ws5['A37'] = "Karena probabilitas > 0.50 (threshold), status = auto_classified"
|
||||
|
||||
for col in ['A', 'B', 'C', 'D', 'E']:
|
||||
ws5.column_dimensions[col].width = 18
|
||||
|
||||
# Simpan file
|
||||
filename = "Perhitungan_Manual_TFIDF_LogReg.xlsx"
|
||||
wb.save(filename)
|
||||
|
||||
print(f"File Excel berhasil dibuat: {filename}")
|
||||
print(f"\nSheet yang dibuat:")
|
||||
for sheet in wb.sheetnames:
|
||||
print(f" - {sheet}")
|
||||
|
|
@ -15,7 +15,7 @@ class AdminUserSeeder extends Seeder
|
|||
public function run()
|
||||
{
|
||||
User::updateOrCreate(
|
||||
['email' => 'test@example.com'],
|
||||
['email' => 'admin@gmail.com'],
|
||||
[
|
||||
'name' => 'Admin',
|
||||
'password' => Hash::make('admin1234'),
|
||||
|
|
|
|||
|
|
@ -644,7 +644,7 @@ class="inline-flex items-center gap-2 px-6 py-3 rounded-xl text-sm font-semibold
|
|||
type: 'doughnut',
|
||||
data: {
|
||||
labels: Object.keys(chartData.profile),
|
||||
datasets: [{ data: Object.values(chartData.profile), backgroundColor: ['#3b82f6', '#10b981', '#f59e0b', '#64748b'] }]
|
||||
datasets: [{ data: Object.values(chartData.profile), backgroundColor: ['#2563EB', '#DC2626', '#F59E0B', '#7C3AED', '#DB2777'] }]
|
||||
},
|
||||
options: pieOptions
|
||||
});
|
||||
|
|
@ -653,7 +653,7 @@ class="inline-flex items-center gap-2 px-6 py-3 rounded-xl text-sm font-semibold
|
|||
type: 'pie',
|
||||
data: {
|
||||
labels: Object.keys(chartData.method),
|
||||
datasets: [{ data: Object.values(chartData.method), backgroundColor: ['#10b981', '#3b82f6', '#f59e0b'] }]
|
||||
datasets: [{ data: Object.values(chartData.method), backgroundColor: ['#845EC2', '#D65DB1', '#FF9671', '#FFC75F'] }]
|
||||
},
|
||||
options: pieOptions
|
||||
});
|
||||
|
|
@ -662,7 +662,7 @@ class="inline-flex items-center gap-2 px-6 py-3 rounded-xl text-sm font-semibold
|
|||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(chartData.waktu_tunggu),
|
||||
datasets: [{ label: 'Rata-rata Bulan', data: Object.values(chartData.waktu_tunggu), backgroundColor: '#3b82f6', borderRadius: 4 }]
|
||||
datasets: [{ label: 'Rata-rata Bulan', data: Object.values(chartData.waktu_tunggu), backgroundColor: ['#00C9A7', '#845EC2', '#D65DB1', '#FF9671', '#FFC75F'], borderRadius: 4 }]
|
||||
},
|
||||
options: barOptions
|
||||
});
|
||||
|
|
@ -671,7 +671,7 @@ class="inline-flex items-center gap-2 px-6 py-3 rounded-xl text-sm font-semibold
|
|||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(chartData.funnel),
|
||||
datasets: [{ data: Object.values(chartData.funnel), backgroundColor: ['#8b5cf6', '#ec4899', '#10b981'], borderRadius: 4 }]
|
||||
datasets: [{ data: Object.values(chartData.funnel), backgroundColor: ['#008E9B', '#F39C12', '#E74C3C', '#2C3E50'], borderRadius: 4 }]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
|
|
@ -683,7 +683,7 @@ class="inline-flex items-center gap-2 px-6 py-3 rounded-xl text-sm font-semibold
|
|||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(chartData.lokasi),
|
||||
datasets: [{ data: Object.values(chartData.lokasi), backgroundColor: '#14b8a6', borderRadius: 4 }]
|
||||
datasets: [{ data: Object.values(chartData.lokasi), backgroundColor: ['#2ECC71', '#3498DB', '#9B59B6', '#34495E', '#F1C40F'], borderRadius: 4 }]
|
||||
},
|
||||
options: barOptions
|
||||
});
|
||||
|
|
@ -715,7 +715,15 @@ class="inline-flex items-center gap-2 px-6 py-3 rounded-xl text-sm font-semibold
|
|||
else if(c.includes('malang')) c = 'malang';
|
||||
|
||||
if (cityCoords[c]) {
|
||||
L.marker(cityCoords[c]).addTo(map)
|
||||
const radius = Math.max(8, Math.min(count * 3 + 5, 40));
|
||||
L.circleMarker(cityCoords[c], {
|
||||
radius: radius,
|
||||
fillColor: '#3b82f6',
|
||||
color: '#1d4ed8',
|
||||
weight: 2,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.7
|
||||
}).addTo(map)
|
||||
.bindPopup(`<b>${city}</b><br>Jumlah: ${count} lulusan`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@
|
|||
type: 'pie',
|
||||
data: {
|
||||
labels: Object.keys(chartData.status),
|
||||
datasets: [{ data: Object.values(chartData.status), backgroundColor: ['#8b5cf6', '#10b981', '#f59e0b', '#ef4444', '#3b82f6', '#64748b'] }]
|
||||
datasets: [{ data: Object.values(chartData.status), backgroundColor: ['#2563EB', '#DC2626', '#F59E0B', '#7C3AED', '#10B981', '#DB2777'] }]
|
||||
},
|
||||
options: pieOptions
|
||||
});
|
||||
|
|
@ -257,7 +257,7 @@
|
|||
type: 'doughnut',
|
||||
data: {
|
||||
labels: Object.keys(chartData.kesesuaian),
|
||||
datasets: [{ data: Object.values(chartData.kesesuaian), backgroundColor: ['#14b8a6', '#f43f5e', '#facc15', '#6366f1', '#a855f7'] }]
|
||||
datasets: [{ data: Object.values(chartData.kesesuaian), backgroundColor: ['#14B8A6', '#F43F5E', '#8B5CF6', '#F59E0B', '#3B82F6'] }]
|
||||
},
|
||||
options: pieOptions
|
||||
});
|
||||
|
|
@ -266,7 +266,7 @@
|
|||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(chartData.level),
|
||||
datasets: [{ data: Object.values(chartData.level), backgroundColor: '#3b82f6', borderRadius: 4 }]
|
||||
datasets: [{ data: Object.values(chartData.level), backgroundColor: ['#00C9A7', '#845EC2', '#D65DB1', '#FF9671', '#FFC75F'], borderRadius: 4 }]
|
||||
},
|
||||
options: barOptions
|
||||
});
|
||||
|
|
@ -275,7 +275,7 @@
|
|||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(chartData.instansi),
|
||||
datasets: [{ data: Object.values(chartData.instansi), backgroundColor: ['#ec4899', '#8b5cf6', '#06b6d4', '#10b981', '#f59e0b'], borderRadius: 4 }]
|
||||
datasets: [{ data: Object.values(chartData.instansi), backgroundColor: ['#2ECC71', '#3498DB', '#9B59B6', '#34495E', '#F1C40F'], borderRadius: 4 }]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
|
|
@ -287,7 +287,7 @@
|
|||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(chartData.pendapatan),
|
||||
datasets: [{ data: Object.values(chartData.pendapatan), backgroundColor: '#10b981', borderRadius: 4 }]
|
||||
datasets: [{ data: Object.values(chartData.pendapatan), backgroundColor: ['#008E9B', '#F39C12', '#E74C3C', '#2C3E50', '#18BC9C'], borderRadius: 4 }]
|
||||
},
|
||||
options: barOptions
|
||||
});
|
||||
|
|
@ -305,7 +305,15 @@
|
|||
for (const [kode, count] of Object.entries(chartData.map_lokasi)) {
|
||||
if (coordsDict[kode] && coordsDict[kode].coords) {
|
||||
const cityData = coordsDict[kode];
|
||||
L.marker(cityData.coords).addTo(map)
|
||||
const radius = Math.max(8, Math.min(count * 3 + 5, 40));
|
||||
L.circleMarker(cityData.coords, {
|
||||
radius: radius,
|
||||
fillColor: '#a855f7',
|
||||
color: '#7e22ce',
|
||||
weight: 2,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.7
|
||||
}).addTo(map)
|
||||
.bindPopup(`<b>${cityData.name}</b><br>Jumlah: ${count} alumni`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue