Spaces:
Runtime error
Runtime error
remove random state, matrix labels on specific rows
Browse files- models/X_train_sample.npy +2 -2
- models/scaler.pkl +1 -1
- models/skin_cancer_model.pkl +2 -2
- src/data.py +3 -4
- src/explainability.py +16 -4
- src/model.py +2 -2
- src/plots.py +14 -5
- train_main.py +1 -1
models/X_train_sample.npy
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6c297b12b2ad73e2c32805b123ab456d0c57135c33469314cbb570588a653f1f
|
| 3 |
+
size 12393352
|
models/scaler.pkl
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 1487
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c7c5bd15219def3155b39a51b6627cf54d82b87acc63de2be03e7e3073793435
|
| 3 |
size 1487
|
models/skin_cancer_model.pkl
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e815f34d3e4a2538499199c8eb4529d3b8719263b736727ea1c992941c287dc8
|
| 3 |
+
size 135524049
|
src/data.py
CHANGED
|
@@ -25,8 +25,7 @@ def balance_dataset(df):
|
|
| 25 |
df_resampled = resample(
|
| 26 |
class_subset,
|
| 27 |
replace=True,
|
| 28 |
-
n_samples=max_count
|
| 29 |
-
random_state=42
|
| 30 |
)
|
| 31 |
balanced_dfs.append(df_resampled)
|
| 32 |
|
|
@@ -34,7 +33,7 @@ def balance_dataset(df):
|
|
| 34 |
df_balanced = pd.concat(balanced_dfs)
|
| 35 |
|
| 36 |
# Shuffle the dataset so classes aren't grouped together
|
| 37 |
-
df_balanced = df_balanced.sample(frac=1
|
| 38 |
|
| 39 |
print(f"Original size: {len(df)} -> Balanced size: {len(df_balanced)}")
|
| 40 |
return df_balanced
|
|
@@ -62,7 +61,7 @@ def load_metadata(limit=None, balance=True):
|
|
| 62 |
if limit:
|
| 63 |
print(f"Subsampling to {limit}...")
|
| 64 |
actual_limit = min(limit, len(df))
|
| 65 |
-
df, _ = train_test_split(df, train_size=actual_limit, stratify=df['label_idx']
|
| 66 |
|
| 67 |
# Apply balancing (Upsampling)
|
| 68 |
# if balance:
|
|
|
|
| 25 |
df_resampled = resample(
|
| 26 |
class_subset,
|
| 27 |
replace=True,
|
| 28 |
+
n_samples=max_count
|
|
|
|
| 29 |
)
|
| 30 |
balanced_dfs.append(df_resampled)
|
| 31 |
|
|
|
|
| 33 |
df_balanced = pd.concat(balanced_dfs)
|
| 34 |
|
| 35 |
# Shuffle the dataset so classes aren't grouped together
|
| 36 |
+
df_balanced = df_balanced.sample(frac=1).reset_index(drop=True)
|
| 37 |
|
| 38 |
print(f"Original size: {len(df)} -> Balanced size: {len(df_balanced)}")
|
| 39 |
return df_balanced
|
|
|
|
| 61 |
if limit:
|
| 62 |
print(f"Subsampling to {limit}...")
|
| 63 |
actual_limit = min(limit, len(df))
|
| 64 |
+
df, _ = train_test_split(df, train_size=actual_limit, stratify=df['label_idx'])
|
| 65 |
|
| 66 |
# Apply balancing (Upsampling)
|
| 67 |
# if balance:
|
src/explainability.py
CHANGED
|
@@ -67,10 +67,22 @@ def generate_lime_explanations(model, X_train, X_test, y_test, feature_names, cl
|
|
| 67 |
)
|
| 68 |
|
| 69 |
# 4. Save Plot
|
| 70 |
-
# We
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
plt.tight_layout()
|
| 75 |
|
| 76 |
save_path = os.path.join(output_dir, f'{model_name}_inst_{i}_lime.png')
|
|
|
|
| 67 |
)
|
| 68 |
|
| 69 |
# 4. Save Plot
|
| 70 |
+
# FIX: We must explicitly tell pyplot which label to plot.
|
| 71 |
+
# exp.local_exp keys are the class indices that were explained.
|
| 72 |
+
# Since we used top_labels=1, there is only one key.
|
| 73 |
+
available_labels = list(exp.local_exp.keys())
|
| 74 |
+
if not available_labels:
|
| 75 |
+
continue
|
| 76 |
+
|
| 77 |
+
explained_label_idx = available_labels[0]
|
| 78 |
+
|
| 79 |
+
# Get class name for title
|
| 80 |
+
pred_label_name = class_names[explained_label_idx]
|
| 81 |
+
true_label_name = class_names[y_test[i]]
|
| 82 |
+
|
| 83 |
+
# Pass the specific label we computed to avoid KeyError
|
| 84 |
+
fig = exp.as_pyplot_figure(label=explained_label_idx)
|
| 85 |
+
plt.title(f"LIME ({model_name}): Test Inst {i}\nTrue: {true_label_name} | Pred: {pred_label_name}")
|
| 86 |
plt.tight_layout()
|
| 87 |
|
| 88 |
save_path = os.path.join(output_dir, f'{model_name}_inst_{i}_lime.png')
|
src/model.py
CHANGED
|
@@ -18,8 +18,8 @@ def train_and_evaluate_split(X_train, y_train, X_test, y_test, classes):
|
|
| 18 |
|
| 19 |
# 1. Define Models
|
| 20 |
techniques = {
|
| 21 |
-
"RF": RandomForestClassifier(n_estimators=100, class_weight='balanced'
|
| 22 |
-
"SVM": SVC(probability=True, class_weight='balanced'
|
| 23 |
}
|
| 24 |
|
| 25 |
best_score = 0
|
|
|
|
| 18 |
|
| 19 |
# 1. Define Models
|
| 20 |
techniques = {
|
| 21 |
+
"RF": RandomForestClassifier(n_estimators=100, class_weight='balanced'),
|
| 22 |
+
"SVM": SVC(probability=True, class_weight='balanced')
|
| 23 |
}
|
| 24 |
|
| 25 |
best_score = 0
|
src/plots.py
CHANGED
|
@@ -10,23 +10,31 @@ from . import config
|
|
| 10 |
def plot_confusion_matrix(y_true, y_pred, classes, model_name):
|
| 11 |
"""Generates and saves both Raw and Normalized confusion matrix heatmaps."""
|
| 12 |
|
| 13 |
-
# 1.
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
# Plot Raw Counts
|
| 17 |
plt.figure(figsize=(10, 8))
|
| 18 |
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
|
| 19 |
-
xticklabels=
|
| 20 |
plt.title(f"{model_name} Confusion Matrix (Counts)")
|
| 21 |
plt.ylabel('True Label')
|
| 22 |
plt.xlabel('Predicted Label')
|
|
|
|
| 23 |
plt.tight_layout()
|
| 24 |
|
| 25 |
filename = f"{model_name.lower()}_confusion_matrix.png"
|
| 26 |
plt.savefig(os.path.join(config.MODEL_DIR, filename))
|
| 27 |
plt.close()
|
| 28 |
|
| 29 |
-
#
|
| 30 |
# Divide each row element by the sum of that row (True Label count)
|
| 31 |
cm_norm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
|
| 32 |
# Replace NaN with 0 (safe guard for empty classes)
|
|
@@ -36,10 +44,11 @@ def plot_confusion_matrix(y_true, y_pred, classes, model_name):
|
|
| 36 |
plt.figure(figsize=(10, 8))
|
| 37 |
# Use fmt='.2f' to show 2 decimal places (e.g., 0.95)
|
| 38 |
sns.heatmap(cm_norm, annot=True, fmt='.2f', cmap='Greens',
|
| 39 |
-
xticklabels=
|
| 40 |
plt.title(f"{model_name} Confusion Matrix (Normalized)")
|
| 41 |
plt.ylabel('True Label')
|
| 42 |
plt.xlabel('Predicted Label')
|
|
|
|
| 43 |
plt.tight_layout()
|
| 44 |
|
| 45 |
filename_norm = f"{model_name.lower()}_confusion_matrix_normalized.png"
|
|
|
|
| 10 |
def plot_confusion_matrix(y_true, y_pred, classes, model_name):
|
| 11 |
"""Generates and saves both Raw and Normalized confusion matrix heatmaps."""
|
| 12 |
|
| 13 |
+
# 1. Determine Alphabetical Order
|
| 14 |
+
# y_true/y_pred are indices (0, 1, 2...) mapping to the original 'classes' list.
|
| 15 |
+
# We want to display them in alphabetical order of the class names.
|
| 16 |
+
sorted_indices = np.argsort(classes)
|
| 17 |
+
sorted_classes = np.array(classes)[sorted_indices]
|
| 18 |
+
|
| 19 |
+
# 2. Calculate Raw Matrix
|
| 20 |
+
# passing 'labels=sorted_indices' forces the matrix rows/cols to follow the alphabetical order
|
| 21 |
+
cm = confusion_matrix(y_true, y_pred, labels=sorted_indices)
|
| 22 |
|
| 23 |
# Plot Raw Counts
|
| 24 |
plt.figure(figsize=(10, 8))
|
| 25 |
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
|
| 26 |
+
xticklabels=sorted_classes, yticklabels=sorted_classes)
|
| 27 |
plt.title(f"{model_name} Confusion Matrix (Counts)")
|
| 28 |
plt.ylabel('True Label')
|
| 29 |
plt.xlabel('Predicted Label')
|
| 30 |
+
plt.xticks(rotation=45)
|
| 31 |
plt.tight_layout()
|
| 32 |
|
| 33 |
filename = f"{model_name.lower()}_confusion_matrix.png"
|
| 34 |
plt.savefig(os.path.join(config.MODEL_DIR, filename))
|
| 35 |
plt.close()
|
| 36 |
|
| 37 |
+
# 3. Calculate Normalized Matrix
|
| 38 |
# Divide each row element by the sum of that row (True Label count)
|
| 39 |
cm_norm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
|
| 40 |
# Replace NaN with 0 (safe guard for empty classes)
|
|
|
|
| 44 |
plt.figure(figsize=(10, 8))
|
| 45 |
# Use fmt='.2f' to show 2 decimal places (e.g., 0.95)
|
| 46 |
sns.heatmap(cm_norm, annot=True, fmt='.2f', cmap='Greens',
|
| 47 |
+
xticklabels=sorted_classes, yticklabels=sorted_classes)
|
| 48 |
plt.title(f"{model_name} Confusion Matrix (Normalized)")
|
| 49 |
plt.ylabel('True Label')
|
| 50 |
plt.xlabel('Predicted Label')
|
| 51 |
+
plt.xticks(rotation=45)
|
| 52 |
plt.tight_layout()
|
| 53 |
|
| 54 |
filename_norm = f"{model_name.lower()}_confusion_matrix_normalized.png"
|
train_main.py
CHANGED
|
@@ -68,7 +68,7 @@ def main():
|
|
| 68 |
|
| 69 |
# Split DataFrame FIRST to avoid data leakage
|
| 70 |
df_train, df_test = train_test_split(
|
| 71 |
-
df, test_size=0.
|
| 72 |
)
|
| 73 |
|
| 74 |
print(f"Training Samples (Files): {len(df_train)}")
|
|
|
|
| 68 |
|
| 69 |
# Split DataFrame FIRST to avoid data leakage
|
| 70 |
df_train, df_test = train_test_split(
|
| 71 |
+
df, test_size=0.1, stratify=df['label_idx']
|
| 72 |
)
|
| 73 |
|
| 74 |
print(f"Training Samples (Files): {len(df_train)}")
|