Scikit-Learn Tutorial: Build Your First ML Model in 30 Minutes
โก Quick Answer
Scikit-learn tutorial for beginners โ build your first machine learning model in 30 minutes with the complete workflow: data loading, preprocessing, training, evaluation, and tuning.
Get more content like this on Telegram!
Daily AI tips, notes & resources โ free
Advertisement
Scikit-Learn Tutorial: Build Your First ML Model in 30 Minutes
Scikit-learn is Python's standard library for traditional machine learning โ classification, regression, clustering, and preprocessing, all behind one consistent API. The first time you build a model that works on real data, the surprise isn't the result; it's how little code it took.
Training a Random Forest classifier, evaluating it with proper cross-validation, tuning its hyperparameters, and generating a full evaluation report runs about 40 lines. The hard part was never the code โ it's understanding what each step does and making good calls about data preparation.
This tutorial walks the complete scikit-learn workflow, from loading data to a tuned, deployable model, on a real classification dataset. By the end, you'll understand every line you write, not just be able to copy it.
Setup
pip install scikit-learn pandas numpy matplotlib seabornRequired versions for this tutorial:
import sklearn
print(sklearn.__version__) # Should be 1.3+ for all examples to workDataset: Breast Cancer Classification
The breast cancer dataset is scikit-learn's built-in medical dataset โ 30 features computed from breast-mass cell nuclei, with the target being malignant or benign. It's small enough to run in seconds and realistic enough that the lessons transfer directly to production tabular data.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_breast_cancer
# Load dataset
data = load_breast_cancer()
# Convert to DataFrame for easier exploration
df = pd.DataFrame(data.data, columns=data.feature_names)
df['target'] = data.target # 0 = malignant, 1 = benign
print(f"Dataset shape: {df.shape}")
print(f"\nTarget distribution:")
print(df['target'].value_counts())
print(f"\nMalignant: {(df['target'] == 0).sum()} ({(df['target'] == 0).mean()*100:.1f}%)")
print(f"Benign: {(df['target'] == 1).sum()} ({(df['target'] == 1).mean()*100:.1f}%)")Output:
Dataset shape: (569, 31)
Target distribution:
1 357
0 212
Malignant: 212 (37.3%)
Benign: 357 (62.7%)Step 1: Exploratory Data Analysis
Exploratory data analysis (EDA) means checking your data's shape, missing values, and feature scales before you train anything. Skip it, and you'll waste a training run debugging a problem you could have spotted in one .describe() call.
# Basic statistics
print(df.describe())
# Check for missing values
print("\nMissing values:")
print(df.isnull().sum().sum()) # Should be 0 for this dataset
# Feature correlations with target
correlation_with_target = df.corr()['target'].sort_values()
print("\nTop 5 features most correlated with malignancy (negative = malignant):")
print(correlation_with_target.head(5))Key findings from exploration:
- No missing values in this dataset
- Features like
worst radius,worst perimeter, andmean concave pointscorrelate most strongly with malignancy - Features vary widely in scale (radius ~ 10-28, area ~ 143-2501) โ standardization will be important
# Visualize feature distributions by class
fig, axes = plt.subplots(2, 3, figsize=(15, 8))
top_features = ['mean radius', 'mean texture', 'mean perimeter',
'mean area', 'mean smoothness', 'mean compactness']
for i, feature in enumerate(top_features):
ax = axes[i//3, i%3]
df[df['target'] == 0][feature].hist(alpha=0.7, label='Malignant', ax=ax, bins=20)
df[df['target'] == 1][feature].hist(alpha=0.7, label='Benign', ax=ax, bins=20)
ax.set_title(feature)
ax.legend()
plt.tight_layout()
plt.show()Step 2: Data Preparation
Data preparation splits the dataset into train and test sets, then scales features โ and the order matters. Fit the scaler on training data only; fitting it on test data leaks test-set statistics into your evaluation and inflates your score.
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Separate features and target
X = df.drop('target', axis=1)
y = df['target']
# Split into train and test sets
# random_state ensures reproducibility
# stratify=y ensures same class distribution in train and test
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2, # 20% held out for final evaluation
random_state=42,
stratify=y # Maintain class balance
)
print(f"Training set size: {X_train.shape[0]}")
print(f"Test set size: {X_test.shape[0]}")
print(f"\nTrain class distribution: {y_train.value_counts(normalize=True).round(3).to_dict()}")
print(f"Test class distribution: {y_test.value_counts(normalize=True).round(3).to_dict()}")
# Scale features
# IMPORTANT: Fit ONLY on training data
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # Fit and transform training data
X_test_scaled = scaler.transform(X_test) # Only transform test data (using train statistics)Step 3: Training Your First Model
Logistic Regression is the right first model for almost any classification task โ it's fast, interpretable, and tells you how hard the problem actually is before you reach for anything heavier.
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# Train the model
lr_model = LogisticRegression(random_state=42, max_iter=1000)
lr_model.fit(X_train_scaled, y_train)
# Make predictions
y_pred_lr = lr_model.predict(X_test_scaled)
# Evaluate
print("=== Logistic Regression Results ===")
print(f"Accuracy: {accuracy_score(y_test, y_pred_lr):.4f}")
print("\nClassification Report:")
print(classification_report(y_test, y_pred_lr,
target_names=['Malignant', 'Benign']))
# Confusion Matrix
cm = confusion_matrix(y_test, y_pred_lr)
sns.heatmap(cm, annot=True, fmt='d',
xticklabels=['Malignant', 'Benign'],
yticklabels=['Malignant', 'Benign'])
plt.title('Confusion Matrix โ Logistic Regression')
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.show()Output:
=== Logistic Regression Results ===
Accuracy: 0.9561
Classification Report:
precision recall f1-score support
Malignant 0.93 0.95 0.94 42
Benign 0.97 0.96 0.96 72
accuracy 0.96 114
macro avg 0.95 0.95 0.95 114
weighted avg 0.96 0.96 0.96 114Understanding the Metrics
- Precision: Of all cases predicted as malignant, how many were actually malignant? (0.93 = 93%)
- Recall: Of all actual malignant cases, how many did we catch? (0.95 = 95%)
- F1-score: Harmonic mean of precision and recall โ useful when both matter
- In medical contexts: Recall for malignant is critical โ missing a cancer (false negative) is worse than a false alarm
Step 4: Trying Multiple Algorithms
No single algorithm wins every dataset, so compare several with cross-validation before committing to one. Five-fold cross-validation below trains and scores each model five times on different splits, giving a much more stable estimate than one train/test check.
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score
# Define models to compare
models = {
'Logistic Regression': LogisticRegression(random_state=42, max_iter=1000),
'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42),
'Gradient Boosting': GradientBoostingClassifier(n_estimators=100, random_state=42),
'SVM': SVC(random_state=42),
'K-Nearest Neighbors': KNeighborsClassifier(n_neighbors=5),
}
# Evaluate each model with 5-fold cross-validation
results = {}
for name, model in models.items():
cv_scores = cross_val_score(model, X_train_scaled, y_train, cv=5, scoring='accuracy')
results[name] = {
'mean': cv_scores.mean(),
'std': cv_scores.std(),
'scores': cv_scores
}
print(f"{name}: {cv_scores.mean():.4f} (+/- {cv_scores.std()*2:.4f})")Output:
Logistic Regression: 0.9736 (+/- 0.0244)
Random Forest: 0.9604 (+/- 0.0228)
Gradient Boosting: 0.9670 (+/- 0.0196)
SVM: 0.9779 (+/- 0.0173)
K-Nearest Neighbors: 0.9604 (+/- 0.0357)Note: Cross-validation scores on training data differ from the test set evaluation โ this is expected and normal.
Step 5: Hyperparameter Tuning
Hyperparameter tuning searches combinations of settings โ tree depth, split thresholds, estimator count โ to find the configuration that scores best on cross-validation. GridSearchCV below tries every combination in the grid; for larger grids, swap in RandomizedSearchCV to sample instead of exhaust.
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
# Define parameter grid
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 10, 20, 30],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
}
# Grid Search (exhaustive โ tries all combinations)
# Note: Can be slow for large grids. Use RandomizedSearchCV for large grids.
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(
rf, param_grid,
cv=5,
scoring='recall', # Optimize for recall of malignant class
n_jobs=-1, # Use all CPU cores
verbose=1
)
grid_search.fit(X_train_scaled, y_train)
print(f"\nBest parameters: {grid_search.best_params_}")
print(f"Best CV recall: {grid_search.best_score_:.4f}")
# Evaluate best model on test set
best_rf = grid_search.best_estimator_
y_pred_rf = best_rf.predict(X_test_scaled)
print("\n=== Tuned Random Forest Results ===")
print(classification_report(y_test, y_pred_rf, target_names=['Malignant', 'Benign']))Step 6: Understanding Feature Importance
Feature importance ranks which inputs the model relies on most โ a built-in advantage of tree-based models like Random Forest over black-box alternatives. It's the difference between a model that says "benign" and one that can also tell you why.
# Feature importance from Random Forest
feature_importance = pd.Series(
best_rf.feature_importances_,
index=data.feature_names
).sort_values(ascending=False)
# Plot top 15 features
plt.figure(figsize=(10, 6))
feature_importance.head(15).plot(kind='barh')
plt.title('Top 15 Most Important Features')
plt.xlabel('Feature Importance')
plt.tight_layout()
plt.show()
print("\nTop 5 most important features:")
print(feature_importance.head(5))Step 7: Building a Pipeline
A Pipeline bundles preprocessing and the model into one object, so scaling and prediction always happen in the same order โ in training, in testing, and later in production.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
# Create a Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('classifier', RandomForestClassifier(n_estimators=100, random_state=42))
])
# The pipeline applies transformations and trains together
pipeline.fit(X_train, y_train) # Note: using raw X_train, not X_train_scaled
# Evaluate
y_pred_pipeline = pipeline.predict(X_test) # Raw X_test โ scaling happens inside pipeline
print("Pipeline accuracy:", accuracy_score(y_test, y_pred_pipeline))
# Save the entire pipeline (preprocessing + model)
import joblib
joblib.dump(pipeline, 'breast_cancer_classifier.pkl')
# Load and use later
loaded_pipeline = joblib.load('breast_cancer_classifier.pkl')
new_prediction = loaded_pipeline.predict(X_test[:5]) # Predicts on raw featuresPipelines matter for production ML because they guarantee:
- Identical preprocessing โ test data goes through the exact same transformation as training data, every time.
- One artifact to deploy โ preprocessing and model ship together, so there's nothing to keep in sync manually.
- No forgotten scaling step โ predictions can't accidentally run on unscaled input.
Complete Workflow Summary
# Complete end-to-end workflow
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
import joblib
# 1. Load data
data = load_breast_cancer()
X, y = data.data, data.target
# 2. Split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 3. Build pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('model', RandomForestClassifier(n_estimators=100, random_state=42))
])
# 4. Evaluate with cross-validation
cv_scores = cross_val_score(pipeline, X_train, y_train, cv=5)
print(f"CV Accuracy: {cv_scores.mean():.4f} +/- {cv_scores.std():.4f}")
# 5. Train on full training set
pipeline.fit(X_train, y_train)
# 6. Final evaluation on test set
y_pred = pipeline.predict(X_test)
print(classification_report(y_test, y_pred, target_names=data.target_names))
# 7. Save
joblib.dump(pipeline, 'model.pkl')Next Steps
This same seven-step workflow applies to any tabular dataset, not just breast cancer classification. Swap in your own data and the code barely changes:
- Load and explore โ check shape, missing values, and target distribution first.
- Clean the data โ handle missing values and encode categorical columns.
- Split with stratification โ keep class balance consistent across train and test.
- Compare algorithms with cross-validation โ never trust a single train/test split.
- Tune the best performer โ grid or randomized search on its key hyperparameters.
- Wrap it in a Pipeline โ then evaluate once on the held-out test set, and only once.
For feature engineering and imbalanced-data techniques beyond this tutorial, see the scikit-learn official documentation. For the theory behind these algorithms, see our neural networks explained guide.
Further Reading
- Kaggle Competition Guide: How to Rank in the Top 10% Every Time
- Math for Machine Learning: What You Actually Need (and What You Don't)
- Machine Learning for Beginners: A Honest Guide to Getting Started
- Best Machine Learning Courses in 2025: Ranked After Taking Them All
- Neural Networks Explained: From Perceptron to Deep Learning
- Python File Handling Guide: Read, Write, Process Any File Type
- Fine-Tuning LLMs: When to Do It and How to Do It Right
- Ollama Tutorial: Run LLMs Locally on Your Computer (Complete Setup Guide)
Advertisement
๐ฌ DiscussionPowered by GitHub Discussions
Frequently Asked Questions

Software Testing Expert & Prompt Engineering
Ensures every release is bug-free through rigorous testing, and crafts high-precision prompts that power our AI-driven workflows. Abdullah Al Arman Emon leads QA and prompt engineering across AiTechWorlds.
Not sure yet? Ask AI about this article
Get an instant, unbiased AI summary of โScikit-Learn Tutorial: Build Your First ML Model in 30 Minutesโ.
Advertisement
Related Articles
Best Machine Learning Courses in 2026: Ranked After Taking Them All
The best machine learning courses in 2026 โ ranked by a practitioner who completed them. Honest assessments of Coursera, Fast.ai, Kaggle, and 7 others with cost and time required.
Computer Vision Tutorial: Build an Image Classifier from Scratch
Computer vision tutorial for beginners โ build a real image classifier using CNNs and PyTorch, understand how computers see images, and learn transfer learning for production results.
Feature Engineering Guide: Turn Raw Data into Powerful ML Inputs
Feature engineering guide for machine learning โ practical techniques to create, transform, and select features that improve model accuracy, with Python code examples for every method.
Kaggle Competition Guide: How to Rank in the Top 10% Every Time
Kaggle competition guide โ the systematic approach to finishing in the top 10%, from EDA and baseline models to ensembling and post-competition learning, used by Kaggle Masters.