CKD Diagnosis: ML Classifiers + GA
Train three ML classifiers and build a genetic algorithm for CKD feature selection.
Introduction
30 Second Summary
When a doctor orders a panel of 24 blood tests, not all of them carry equal weight for diagnosing a specific condition. Some tests are redundant, others barely move the needle. The challenge is figuring out which handful of results actually matter.
In this project, you will train three machine learning classifiers to diagnose Chronic Kidney Disease, then build a Genetic Algorithm from scratch that evolves toward the smallest set of blood tests needed for an accurate diagnosis.
What You'll Build
You will run your GA and watch it evolve across 50 generations, then compare a bar chart showing that 5-8 features match or beat the accuracy of all 24, with every classifier producing probability scores ready for Part 3's fusion engine.
By the end of this project, you'll have:
- Three trained classifiers (Decision Tree, SVM, and MLP) that diagnose CKD and reveal which algorithm handles the data best.
- A complete Genetic Algorithm built from pure Python and NumPy that prints fitness improvements generation-by-generation as it discovers the optimal feature subset.
- A side-by-side accuracy comparison proving that 5-8 blood tests match the diagnostic power of all 24, with probability-capable models exported and ready for Part 3's neuro-symbolic fusion engine.
- Secret Mission: Enhance your GA with elitism and visualize convergence curves comparing basic GA versus elite GA performance.
Are there any prerequisites?
This project is Part 2 of the CKD Diagnosis series. You need the preprocessed CSV file (400 patients, 24 encoded features) produced in Part 1, saved in your Google Drive.
You'll work in Google Colab, which comes with scikit-learn, NumPy, pandas, matplotlib, and seaborn pre-installed. All you need is a Google account.
Before We Start...
Before we dive in, let's lock in what you're about to build and why it matters. You're picking up where Part 1 left off, turning a cleaned dataset into a diagnostic system that proves most blood tests are unnecessary.
Set Up Your Environment
To train classifiers and run evolutionary search, you need a compute environment with all the scientific Python libraries pre-installed. You also need the preprocessed CSV from Part 1 accessible in your workspace.
In this step, you'll open a fresh Google Colab notebook and mount your Google Drive to load the cleaned CKD dataset.
In this step, get ready to:
- Create a new Google Colab notebook for Part 2.
- Mount Google Drive and load the preprocessed CKD CSV.
- Verify the dataset shape (400 patients, 24 features).
Create a new Colab notebook
- Open Google Colab by navigating to colab.research.google.com in your browser.
- Click New notebook in the bottom-left corner of the dialog.
- Rename the notebook to CKD_Part2_Classifiers_GA.ipynb by clicking the title at the top-left.
Mount Google Drive and load the dataset
- Mount your Google Drive by running this code in the first cell:
# Mount Google Drive to access Part 1's preprocessed CSV
from google.colab import drive
drive.mount('/content/drive')
What does this code do?
This mounts your entire Google Drive as a folder inside Colab. You'll see a permission prompt asking you to allow access. Click Allow to proceed.
- Load the preprocessed dataset from Part 1 by running this in a new cell:
import pandas as pd
import numpy as np
# Load the cleaned CKD dataset from Part 1
df = pd.read_csv('/content/drive/MyDrive/CKD_Project/ckd_preprocessed.csv')
# Verify the dataset shape
print(f"Dataset shape: {df.shape}")
print(f"Columns: {list(df.columns)}")
print(f"\nTarget distribution:")
print(df['class'].value_counts())
What should I see?
You should see Dataset shape: (400, 25) — 400 patients, 24 features plus the target column class. The target distribution shows roughly 250 CKD cases and 150 non-CKD cases.
🙋♀️ Seeing a FileNotFoundError?
The file path depends on where you saved Part 1's output. Open your Google Drive in a browser, navigate to the CSV, right-click it, and select Copy path. Replace the path in the code with your actual path.
Help me find my Part 1 CSV file in Google Drive.
Your environment is ready and the data is loaded. Next up, you'll train three classifiers and discover which one handles the CKD dataset best.
Train Three CKD Classifiers
You have 400 patients described by 24 blood test results. The question is: which mathematical model can learn to separate CKD from non-CKD patients most reliably?
In this step, you'll train three fundamentally different classifiers on the same data. One of them will reveal a critical problem that motivates everything you build in the next step.
In this step, get ready to:
- Split and scale the dataset for fair model comparison.
- Train a Decision Tree, SVM, and MLP classifier.
- Diagnose a critical overfitting problem that motivates feature selection.
Split and scale the data
- Separate features from the target and create train/test splits by running this in a new cell:
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Separate features (X) from target (y)
X = df.drop('class', axis=1)
y = df['class']
# 80/20 train-test split with fixed seed for reproducibility
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Scale features for SVM and MLP (they need normalized inputs)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f"Training set: {X_train.shape[0]} samples")
print(f"Test set: {X_test.shape[0]} samples")
What does this code do?
The stratify=y parameter ensures both sets have the same proportion of CKD and non-CKD patients. Without it, random chance could put most CKD patients in one set.
StandardScaler normalizes each feature to mean=0, std=1. The Decision Tree doesn't need this (it splits on thresholds), but SVM and MLP perform poorly on unscaled data.
Train the Decision Tree (and spot the problem)
The Decision Tree is your baseline. It builds a branching flowchart that clinicians can audit. But before you run this, predict: will the training accuracy and test accuracy be similar, or very different?
- Train a Decision Tree and compare train vs test accuracy by running:
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
# Train Decision Tree (no scaling needed)
dt_clf = DecisionTreeClassifier(random_state=42)
dt_clf.fit(X_train, y_train)
# Compare train vs test accuracy
train_acc = accuracy_score(y_train, dt_clf.predict(X_train))
test_acc = accuracy_score(y_test, dt_clf.predict(X_test))
print(f"Decision Tree - Train accuracy: {train_acc:.4f}")
print(f"Decision Tree - Test accuracy: {test_acc:.4f}")
print(f"Gap: {train_acc - test_acc:.4f}")
You'll see 100% training accuracy but lower test accuracy. That gap is overfitting — the tree memorized the training data instead of learning generalizable patterns.
- Inspect which features the tree actually relies on by running:
import matplotlib.pyplot as plt
# Show which features the Decision Tree uses
importances = dt_clf.feature_importances_
feature_names = X.columns
# Sort by importance
sorted_idx = np.argsort(importances)[::-1]
plt.figure(figsize=(12, 5))
plt.bar(range(len(importances)), importances[sorted_idx])
plt.xticks(range(len(importances)), feature_names[sorted_idx], rotation=45, ha='right')
plt.title('Decision Tree Feature Importances')
plt.ylabel('Importance')
plt.tight_layout()
plt.show()
# Count zero-importance features
zero_features = sum(importances == 0)
print(f"\nFeatures with ZERO importance: {zero_features} out of {len(importances)}")
What does this reveal?
Many features have zero importance — the tree never uses them for any split. The tree overfits by memorizing patterns in noise from all 24 features. If you could identify and remove the useless features, you'd get a model that generalizes better.
This is exactly the problem the Genetic Algorithm solves in the next step. But first, let's train the other two classifiers for comparison.
Train the SVM and MLP classifiers
- Train an SVM (Support Vector Machine) on the scaled data by running:
from sklearn.svm import SVC
from sklearn.metrics import classification_report
# Train SVM on scaled features
svm_clf = SVC(random_state=42)
svm_clf.fit(X_train_scaled, y_train)
# Evaluate on test set
svm_pred = svm_clf.predict(X_test_scaled)
svm_acc = accuracy_score(y_test, svm_pred)
print(f"SVM Test Accuracy: {svm_acc:.4f}")
print(f"\nClassification Report:")
print(classification_report(y_test, svm_pred, target_names=['Not CKD', 'CKD']))
What is an SVM?
An SVM projects your data into a high-dimensional space and finds the plane that separates CKD from non-CKD with the widest possible margin. It works well on this dataset because the classes are reasonably separable after scaling.
- Train an MLP (Multi-Layer Perceptron neural network) by running:
from sklearn.neural_network import MLPClassifier
# Train MLP neural network on scaled features
mlp_clf = MLPClassifier(
hidden_layer_sizes=(100,),
max_iter=500,
random_state=42
)
mlp_clf.fit(X_train_scaled, y_train)
# Evaluate on test set
mlp_pred = mlp_clf.predict(X_test_scaled)
mlp_acc = accuracy_score(y_test, mlp_pred)
print(f"MLP Test Accuracy: {mlp_acc:.4f}")
print(f"\nClassification Report:")
print(classification_report(y_test, mlp_pred, target_names=['Not CKD', 'CKD']))
What is an MLP?
A Multi-Layer Perceptron is a feedforward neural network. It captures complex, non-linear correlations between biological markers through hidden layers of mathematical nodes. The hidden_layer_sizes=(100,) means one hidden layer with 100 neurons.
- Compare all three classifiers side by side by running:
# Side-by-side comparison of all three classifiers
print("="*50)
print("CLASSIFIER COMPARISON (All 24 Features)")
print("="*50)
print(f"Decision Tree Test Accuracy: {test_acc:.4f}")
print(f"SVM Test Accuracy: {svm_acc:.4f}")
print(f"MLP Test Accuracy: {mlp_acc:.4f}")
print("="*50)
print(f"\nAll three classifiers achieve high accuracy on all 24 features.")
print(f"But the Decision Tree's overfitting tells us: not all features help.")
print(f"Can we achieve the same accuracy with fewer blood tests?")
All three classifiers score high, but the Decision Tree's overfitting and zero-importance features prove a critical point: most of those 24 blood tests are noise. Next, you'll build a Genetic Algorithm to discover which handful actually matters.
Build a Genetic Algorithm from Scratch
You proved that most features are dead weight. Now you need an optimizer that can search through all possible combinations of 24 features to find the smallest subset that still diagnoses CKD accurately.
A brute-force search would evaluate 2²⁴ (over 16 million) combinations. Instead, you'll build a Genetic Algorithm from scratch that evolves toward the answer in just 50 generations, using only pure Python and NumPy.
In this step, get ready to:
- Define the chromosome encoding and fitness function.
- Implement selection, crossover, and mutation operators.
- Assemble the full evolutionary loop that prints fitness improvements generation-by-generation.
Define the fitness function
Each candidate solution is a binary chromosome: a string of 24 bits where 1 means "include this blood test" and 0 means "exclude it." The fitness function scores each chromosome by training a classifier on only the selected features.
- Define the fitness function that evaluates a chromosome by running:
from sklearn.model_selection import cross_val_score
def fitness_function(chromosome, X_train, y_train):
"""Evaluate a chromosome's fitness using cross-validated accuracy
with a penalty for using too many features."""
# Select only the features where chromosome bit = 1
selected = np.where(chromosome == 1)[0]
# If no features selected, return worst fitness
if len(selected) == 0:
return 0.0
# Train a Decision Tree on selected features only
X_subset = X_train[:, selected]
dt = DecisionTreeClassifier(random_state=42)
# 5-fold cross-validation for robust accuracy estimate
scores = cross_val_score(dt, X_subset, y_train, cv=5, scoring='accuracy')
accuracy = scores.mean()
# Penalty: prefer fewer features (0.01 per feature ratio)
feature_ratio = len(selected) / len(chromosome)
fitness = accuracy - 0.01 * feature_ratio
return fitness
Why a Decision Tree inside the fitness function?
The GA evaluates 30 chromosomes × 50 generations × 5 CV folds = 7,500 model trainings. A Decision Tree trains in milliseconds on 320 samples. Using SVM or MLP here would make the search painfully slow.
The penalty term 0.01 * feature_ratio nudges evolution toward smaller subsets. When two chromosomes have similar accuracy, the one using fewer blood tests wins.
Implement the genetic operators
Evolution needs three operators: selection picks the parents, crossover combines their genes, and mutation introduces random variation to escape local optima.
- Implement tournament selection, single-point crossover, and bit-flip mutation by running:
def tournament_selection(population, fitness_scores, tournament_size=3, rng=None):
"""Select a parent using tournament selection.
Pick tournament_size random individuals, return the fittest."""
indices = rng.choice(len(population), size=tournament_size, replace=False)
best_idx = indices[np.argmax(fitness_scores[indices])]
return population[best_idx].copy()
def single_point_crossover(parent1, parent2, crossover_rate=0.8, rng=None):
"""Combine two parents at a random cut point.
With probability crossover_rate, swap tails."""
if rng.random() < crossover_rate:
# Pick a random cut point (not at the edges)
point = rng.integers(1, len(parent1))
child1 = np.concatenate([parent1[:point], parent2[point:]])
child2 = np.concatenate([parent2[:point], parent1[point:]])
return child1, child2
return parent1.copy(), parent2.copy()
def bit_flip_mutation(chromosome, mutation_rate=0.05, rng=None):
"""Flip each bit with a small probability.
This introduces random variation to explore new feature combos."""
for i in range(len(chromosome)):
if rng.random() < mutation_rate:
chromosome[i] = 1 - chromosome[i] # Flip 0->1 or 1->0
return chromosome
What do these operators do?
- Tournament selection: picks 3 random chromosomes and returns the fittest. This gives good chromosomes a higher chance of reproducing without completely ignoring weaker ones.
- Single-point crossover: cuts two parents at a random position and swaps their tails. This combines feature subsets from two good solutions.
- Bit-flip mutation: randomly flips individual bits with 5% probability. This prevents the population from converging too early on a suboptimal solution.
Assemble the evolutionary loop
- Define the main GA function that initializes a population and evolves it by running:
def run_genetic_algorithm(X_train, y_train, n_features=24, pop_size=30,
n_generations=50, mutation_rate=0.05,
crossover_rate=0.8, tournament_size=3, seed=42):
"""Run the complete Genetic Algorithm for feature selection."""
rng = np.random.default_rng(seed)
# Initialize random population of binary chromosomes
population = rng.integers(0, 2, size=(pop_size, n_features))
# Track the best solution across all generations
best_chromosome = None
best_fitness = -1
fitness_history = []
print("Generation | Best Fitness | Features Selected")
print("-" * 50)
for gen in range(n_generations):
# Evaluate fitness for every chromosome
fitness_scores = np.array([
fitness_function(chrom, X_train, y_train)
for chrom in population
])
# Track the best of this generation
gen_best_idx = np.argmax(fitness_scores)
gen_best_fitness = fitness_scores[gen_best_idx]
gen_best_features = int(population[gen_best_idx].sum())
- Add the generation update logic that creates the next population by running in the same cell:
# Update global best if this generation improved
if gen_best_fitness > best_fitness:
best_fitness = gen_best_fitness
best_chromosome = population[gen_best_idx].copy()
fitness_history.append(gen_best_fitness)
# Print progress every 10 generations
if gen % 10 == 0 or gen == n_generations - 1:
print(f" {gen:3d} | {gen_best_fitness:.4f} | {gen_best_features}")
# Create next generation through selection, crossover, mutation
new_population = []
for _ in range(pop_size // 2):
parent1 = tournament_selection(population, fitness_scores,
tournament_size, rng)
parent2 = tournament_selection(population, fitness_scores,
tournament_size, rng)
child1, child2 = single_point_crossover(parent1, parent2,
crossover_rate, rng)
child1 = bit_flip_mutation(child1, mutation_rate, rng)
child2 = bit_flip_mutation(child2, mutation_rate, rng)
new_population.extend([child1, child2])
population = np.array(new_population[:pop_size])
return best_chromosome, best_fitness, fitness_history
What does this code do?
Each generation: evaluate all chromosomes, track the best, then build the next population by selecting parents, crossing them over, and mutating the children. After 50 generations, the best chromosome found across all generations is returned.
- Run the GA and watch it evolve by executing in a new cell:
# Run the Genetic Algorithm
print("Starting Genetic Algorithm...\n")
best_chromosome, best_fitness, fitness_history = run_genetic_algorithm(
X_train.values, y_train.values
)
# Show the result
selected_features = X.columns[best_chromosome == 1]
print(f"\nGA Complete!")
print(f"Best fitness: {best_fitness:.4f}")
print(f"Features selected: {len(selected_features)} of 24")
print(f"Selected: {list(selected_features)}")
What should I see?
You'll see fitness improving across generations, converging toward a solution that uses 5-8 features. The GA discovers which handful of blood tests carry the diagnostic signal — the rest are noise.
Your GA just proved that most blood tests are redundant for CKD diagnosis. Next, you'll validate this claim by retraining all three classifiers on only the GA-selected features and exporting them for Part 3.
Select Features and Export Models
The GA claims 5-8 blood tests are enough. That's a bold claim when patient lives are at stake. In this step, you'll put it to the test by retraining all three classifiers on only the GA-selected features and comparing accuracy head-to-head.
You'll also wrap each classifier with probability calibration so they output confidence scores, then export everything for Part 3's neuro-symbolic fusion engine.
In this step, get ready to:
- Retrain all classifiers on GA-selected features and compare accuracy.
- Wrap models with CalibratedClassifierCV for probability output.
- Export the complete pipeline for Part 3.
Retrain classifiers on GA-selected features
Before you run this, predict: will the classifiers trained on 5-8 features match the accuracy of classifiers trained on all 24?
- Retrain all three classifiers using only the GA-selected features by running:
# Extract only the GA-selected features
selected_mask = best_chromosome == 1
X_train_ga = X_train.values[:, selected_mask]
X_test_ga = X_test.values[:, selected_mask]
# Scale the reduced feature set for SVM and MLP
scaler_ga = StandardScaler()
X_train_ga_scaled = scaler_ga.fit_transform(X_train_ga)
X_test_ga_scaled = scaler_ga.transform(X_test_ga)
print(f"Using {selected_mask.sum()} features: {list(selected_features)}")
- Train and evaluate each classifier on the reduced feature set by running:
# Retrain Decision Tree on GA-selected features
dt_ga = DecisionTreeClassifier(random_state=42)
dt_ga.fit(X_train_ga, y_train)
dt_ga_acc = accuracy_score(y_test, dt_ga.predict(X_test_ga))
# Retrain SVM on GA-selected features (scaled)
svm_ga = SVC(random_state=42)
svm_ga.fit(X_train_ga_scaled, y_train)
svm_ga_acc = accuracy_score(y_test, svm_ga.predict(X_test_ga_scaled))
# Retrain MLP on GA-selected features (scaled)
mlp_ga = MLPClassifier(hidden_layer_sizes=(100,), max_iter=500, random_state=42)
mlp_ga.fit(X_train_ga_scaled, y_train)
mlp_ga_acc = accuracy_score(y_test, mlp_ga.predict(X_test_ga_scaled))
print(f"\nAccuracy Comparison: All 24 Features vs GA-Selected ({selected_mask.sum()})")
print("=" * 55)
print(f"Decision Tree: {test_acc:.4f} -> {dt_ga_acc:.4f}")
print(f"SVM: {svm_acc:.4f} -> {svm_ga_acc:.4f}")
print(f"MLP: {mlp_acc:.4f} -> {mlp_ga_acc:.4f}")
print("=" * 55)
What should I see?
The GA-selected features should match or come very close to full-feature accuracy (above 95%). You've just proven that a handful of blood tests carries the same diagnostic signal as the full panel of 24.
- Visualize the comparison as a bar chart by running:
# Side-by-side bar chart: all features vs GA-selected
labels = ['Decision Tree', 'SVM', 'MLP']
all_feat_acc = [test_acc, svm_acc, mlp_acc]
ga_feat_acc = [dt_ga_acc, svm_ga_acc, mlp_ga_acc]
x = np.arange(len(labels))
width = 0.35
fig, ax = plt.subplots(figsize=(8, 5))
bars1 = ax.bar(x - width/2, all_feat_acc, width, label='All 24 Features')
bars2 = ax.bar(x + width/2, ga_feat_acc, width, label=f'GA-Selected ({selected_mask.sum()} Features)')
ax.set_ylabel('Test Accuracy')
ax.set_title('Full Features vs GA-Selected Features')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()
ax.set_ylim(0.85, 1.02)
plt.tight_layout()
plt.show()
Wrap models with probability calibration and export
Part 3's fusion engine needs probability scores, not just class labels. You'll wrap SVM and MLP with CalibratedClassifierCV to enable predict_proba() output.
- Wrap the classifiers for probability output by running:
from sklearn.calibration import CalibratedClassifierCV
# Wrap SVM with calibration for probability output
# ensemble=False trains one model on all data, calibrates via CV
svm_calibrated = CalibratedClassifierCV(SVC(random_state=42), ensemble=False)
svm_calibrated.fit(X_train_ga_scaled, y_train)
# MLP already supports predict_proba natively
mlp_final = MLPClassifier(hidden_layer_sizes=(100,), max_iter=500, random_state=42)
mlp_final.fit(X_train_ga_scaled, y_train)
# Verify probability output works
print("SVM probability output (first 3 patients):")
print(svm_calibrated.predict_proba(X_test_ga_scaled[:3]))
print("\nMLP probability output (first 3 patients):")
print(mlp_final.predict_proba(X_test_ga_scaled[:3]))
Why CalibratedClassifierCV?
In scikit-learn 1.9, the probability parameter on SVC is deprecated. The modern replacement is wrapping with CalibratedClassifierCV(SVC(), ensemble=False). This trains one base SVC on all data and uses cross-validation only for calibration.
- Export the GA-selected features and retrained classifiers as individual files by running:
import joblib
# === Export the GA-selected feature names ===
# selected_features is the list of optimal feature name strings
joblib.dump(selected_features, 'ga_selected_features.pkl')
print("Successfully exported GA-selected feature names!")
# === Export the retrained, probability-wrapped classifiers ===
# These are the GA-trained versions (trained on X_train_ga)
joblib.dump(dt_ga, 'dt_classifier.pkl')
joblib.dump(svm_calibrated, 'svm_classifier.pkl')
joblib.dump(mlp_final, 'mlp_classifier.pkl')
print("Successfully exported all classifiers to .pkl!")
What does this export?
- ga_selected_features.pkl — the list of feature names the GA identified as optimal.
- dt_classifier.pkl — Decision Tree trained on GA-selected features (supports predict_proba() natively).
- svm_classifier.pkl — SVM wrapped with CalibratedClassifierCV so Part 3's fusion engine can call predict_proba().
- mlp_classifier.pkl — MLP retrained on GA-selected features (supports predict_proba() natively).
Part 3 loads each file individually with joblib.load(). Exporting as separate files keeps each model independently loadable and replaceable.
Secret mission
Add Elitism and Convergence Visualization
Your basic GA can accidentally destroy its best solution through crossover and mutation. What if the top individuals were guaranteed to survive into the next generation? Add elitism to your GA and visualize whether it converges faster than the basic approach.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs in Google Colab with files stored on Google Drive, so there are no ongoing costs.
Resources you used:
- Google Colab notebook (CKD Part 2 - Classifiers + GA).
- ckd_models_part2.pkl (pickled dictionary containing trained models, scaler, and GA results, saved in Google Drive).
- ckd_part2_results.png (accuracy comparison and feature selection visualization, saved in Google Drive).
- ga_convergence_comparison.png (Secret Mission convergence chart, saved in Google Drive).
Keep everything running
No action needed. Choose this if you are continuing directly to Part 3 of the CKD Diagnosis series.
- The ckd_models_part2.pkl file in your Google Drive is required for Part 3. Do not delete it.
- All files remain in your CKD_Project folder on Google Drive alongside your Part 1 output (ckd_preprocessed.csv).
- The Colab runtime will disconnect after idle time, but your Drive files persist. When you start Part 3, open a new Colab notebook and mount Drive again.
Pause - I'll come back to this later
Your files are stored on Google Drive, so they persist even after the Colab runtime disconnects. There is nothing to pause or shut down.
- Keep ckd_models_part2.pkl intact in your CKD_Project Drive folder. Part 3 loads this file directly.
- The Colab runtime disconnects automatically after idle time. Your notebook is auto-saved in Google Drive and you can reopen it anytime.
- When you return for Part 3, open a new Colab notebook, mount Drive, and you're ready to go.
Delete - I don't want to use this again
Remove all Part 2 output files from Google Drive. Only do this if you do not plan to continue to Part 3.
Deleting breaks Part 3
Part 3's fusion engine loads ckd_models_part2.pkl from your Google Drive to access your trained classifiers and GA-selected features. If you delete it, you will need to re-run all of Part 2 before starting Part 3.
- Open Google Drive in your browser, navigate to the CKD_Project folder, and delete ckd_models_part2.pkl, ckd_part2_results.png, and ga_convergence_comparison.png.
- If you also want to remove the Colab notebook, find it in your Google Drive (or under Colab Notebooks folder) and delete it.
- No packages to uninstall. Colab provides all libraries in its managed runtime.
Nice Work!
Nice Work!
You just built a complete machine learning classification pipeline with evolutionary optimization for CKD diagnosis. Three classifiers trained, a Genetic Algorithm coded from scratch, and a minimal feature subset discovered that maintains diagnostic accuracy above 95%.
You've learned how to:
- Train Decision Tree, SVM, and MLP classifiers on patient data, diagnose overfitting through feature importance analysis, and compare models side by side.
- Build a complete Genetic Algorithm from scratch using binary chromosome encoding, tournament selection, single-point crossover, and bit-flip mutation to evolve optimal feature subsets across 50 generations.
- Prove that GA-selected features (5-8 of 24) match full-feature accuracy while reducing complexity by 60-80%, and wrap all classifiers with CalibratedClassifierCV for probability output ready for Part 3's fusion engine.
- Secret Mission: Enhanced the GA with elitism and visualized convergence curves showing how preserving top solutions accelerates optimization.
Ready to quiz yourself?