Machine learning lets computers learn from data and make predictions without being explicitly programmed. This tutorial takes you from zero to building real ML models in Python, with no prior ML experience required.
What you'll learn
| Topic | What you'll be able to do |
|---|---|
| Core concepts | Understand what ML is and how it works |
| Types of ML | Choose the right algorithm for your problem |
| Python ML stack | Use NumPy, pandas, scikit-learn |
| Data preprocessing | Clean and prepare data for training |
| Supervised learning | Train regression and classification models |
| Unsupervised learning | Cluster data and reduce dimensions |
| Model evaluation | Measure and improve model performance |
| Real projects | Build and deploy working ML models |
What is machine learning?
Machine learning is a branch of AI where algorithms learn patterns from data to make predictions or decisions.
Traditional programming vs Machine learning:
Traditional: Rules + Data → Output
Machine Learning: Data + Output → Rules (model)
Classic example: Spam filter
- Traditional: if email contains "buy now" AND "click here" → spam
- ML: show 10,000 spam/not-spam emails → model learns the pattern itself
| Approach | You write | Computer learns |
|---|---|---|
| Traditional programming | Rules | — |
| Machine learning | — | Rules (the model) |
Types of machine learning
| Type | How it learns | Example |
|---|---|---|
| Supervised | Labeled examples (input + correct answer) | Email spam detection, house price prediction |
| Unsupervised | Unlabeled data, finds patterns | Customer segmentation, anomaly detection |
| Reinforcement | Trial and error with rewards | Game playing AI, robotics |
| Semi-supervised | Mix of labeled and unlabeled | Image labeling with few examples |
| Self-supervised | Creates labels from data itself | LLMs, word2vec |
90% of practical ML is supervised or unsupervised. Start there.
Supervised learning subtypes
| Subtype | Output | Example algorithms |
|---|---|---|
| Regression | Continuous number | Linear Regression, Random Forest Regressor |
| Classification | Category/label | Logistic Regression, Decision Tree, SVM, KNN |
Unsupervised learning subtypes
| Subtype | What it does | Example algorithms |
|---|---|---|
| Clustering | Groups similar data | K-Means, DBSCAN, Hierarchical |
| Dimensionality reduction | Compresses features | PCA, t-SNE, UMAP |
| Anomaly detection | Finds outliers | Isolation Forest, One-class SVM |
Setup: Python ML environment
You need Python 3.9+ and four libraries.
Install
pip install numpy pandas scikit-learn matplotlib seaborn
Or with conda:
conda install numpy pandas scikit-learn matplotlib seaborn
Verify
import numpy as np
import pandas as pd
import sklearn
import matplotlib.pyplot as plt
print(f"numpy: {np.__version__}")
print(f"pandas: {pd.__version__}")
print(f"scikit-learn: {sklearn.__version__}")
Core libraries
| Library | What it does | Main use |
|---|---|---|
| NumPy | Fast numerical arrays | Math, matrix operations |
| pandas | Data tables (DataFrames) | Loading, cleaning, exploring data |
| scikit-learn | ML algorithms | Training, evaluating models |
| matplotlib | Plotting | Visualizing data and results |
| seaborn | Statistical plots | Correlation heatmaps, distributions |
The ML workflow
Every ML project follows this pipeline:
1. Define problem → 2. Collect data → 3. Explore data (EDA)
→ 4. Preprocess → 5. Choose algorithm → 6. Train model
→ 7. Evaluate → 8. Improve → 9. Deploy
Let's walk through each step with real code.
Step 1: Load and explore data
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Load a dataset
df = pd.read_csv("housing.csv")
# First look
print(df.shape) # rows, columns
print(df.head()) # first 5 rows
print(df.dtypes) # data types
print(df.describe()) # statistics (mean, std, min, max)
print(df.isnull().sum()) # missing values per column
Key EDA questions:
- How many rows and columns?
- Any missing values?
- What are the data types?
- What does the target variable look like?
- Are features correlated?
# Correlation heatmap
plt.figure(figsize=(10, 8))
sns.heatmap(df.corr(), annot=True, fmt=".2f", cmap="coolwarm")
plt.title("Feature Correlations")
plt.show()
# Distribution of target variable
df["price"].hist(bins=50)
plt.xlabel("Price")
plt.ylabel("Count")
plt.show()
Step 2: Preprocess data
Raw data is rarely ready for training. You need to:
Handle missing values
# Check missing
print(df.isnull().sum())
# Strategy 1: Drop rows with missing values
df_clean = df.dropna()
# Strategy 2: Fill with mean/median/mode
df["age"].fillna(df["age"].median(), inplace=True)
df["category"].fillna(df["category"].mode()[0], inplace=True)
# Strategy 3: scikit-learn imputer (better for pipelines)
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
df[["age", "income"]] = imputer.fit_transform(df[["age", "income"]])
| Strategy | Use when |
|---|---|
| Drop rows | Few missing values (<5%), data is plentiful |
| Fill mean | Numerical features, no heavy outliers |
| Fill median | Numerical features, outliers present |
| Fill mode | Categorical features |
| Model-based imputation | Many missing, important feature |
Encode categorical features
ML algorithms work with numbers, not text.
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
import pandas as pd
# Label encoding (ordinal categories: small < medium < large)
le = LabelEncoder()
df["size_encoded"] = le.fit_transform(df["size"])
# One-hot encoding (nominal categories: red, blue, green)
df_encoded = pd.get_dummies(df, columns=["color"], drop_first=True)
# Or with scikit-learn
from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder(sparse_output=False, drop="first")
color_encoded = ohe.fit_transform(df[["color"]])
| Encoding | Use when | Example |
|---|---|---|
| Label encoding | Ordinal (order matters) | Small=0, Medium=1, Large=2 |
| One-hot encoding | Nominal (no order) | Red=[1,0,0], Blue=[0,1,0] |
| Target encoding | High-cardinality cats | City → avg price in that city |
Scale features
Many algorithms are sensitive to feature scale (KNN, SVM, neural nets).
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# StandardScaler: mean=0, std=1 (best for most algorithms)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # use fit from train only!
# MinMaxScaler: range [0, 1] (good for neural nets)
mm_scaler = MinMaxScaler()
X_scaled = mm_scaler.fit_transform(X_train)
Critical: Fit the scaler on training data only. Apply to test data. Never fit on test data — it leaks information.
Split into train/test sets
from sklearn.model_selection import train_test_split
X = df.drop("price", axis=1) # features
y = df["price"] # target
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2, # 20% for testing
random_state=42, # reproducibility
stratify=y # for classification: keeps class ratios
)
print(f"Train: {X_train.shape}, Test: {X_test.shape}")
Step 3: Supervised learning — Regression
Goal: Predict a continuous number.
Linear Regression
The simplest model. Fits a line (or hyperplane) through data.
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
# Train
model = LinearRegression()
model.fit(X_train, y_train)
# Predict
y_pred = model.predict(X_test)
# Evaluate
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.2f}")
print(f"R² score: {r2:.4f}") # 1.0 = perfect, 0.0 = baseline
# Coefficients
for name, coef in zip(X.columns, model.coef_):
print(f" {name}: {coef:.4f}")
When to use Linear Regression:
- Target is continuous (price, temperature, salary)
- Relationship between features and target is approximately linear
- You need interpretability (which features matter most)
Random Forest Regressor
Ensemble of decision trees. More powerful than linear regression.
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor(
n_estimators=100, # number of trees
max_depth=None, # trees grow until pure
random_state=42
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.2f}, R²: {r2:.4f}")
# Feature importance
importances = pd.Series(
model.feature_importances_,
index=X.columns
).sort_values(ascending=False)
print(importances)
Step 4: Supervised learning — Classification
Goal: Predict a category/label.
Logistic Regression
Despite the name, it's for classification. Outputs probabilities.
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report
model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1] # probability of positive class
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred))
Decision Tree
Splits data by rules. Easy to interpret.
from sklearn.tree import DecisionTreeClassifier, plot_tree
model = DecisionTreeClassifier(
max_depth=5, # prevent overfitting
min_samples_split=10,
random_state=42
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
# Visualize the tree
plt.figure(figsize=(20, 10))
plot_tree(model, feature_names=X.columns, class_names=["No", "Yes"],
filled=True, max_depth=3)
plt.show()
Random Forest Classifier
Often the best out-of-the-box classifier.
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(
n_estimators=100,
random_state=42
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred))
Support Vector Machine (SVM)
Good for small-to-medium datasets, especially with many features.
from sklearn.svm import SVC
model = SVC(
kernel="rbf", # "linear", "poly", "rbf", "sigmoid"
C=1.0, # regularization (higher = less regularization)
probability=True # needed for predict_proba
)
model.fit(X_train_scaled, y_train) # requires scaled features!
y_pred = model.predict(X_test_scaled)
K-Nearest Neighbors (KNN)
Classifies based on the K closest training examples.
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier(n_neighbors=5)
model.fit(X_train_scaled, y_train) # requires scaled features!
y_pred = model.predict(X_test_scaled)
Algorithm comparison
| Algorithm | Speed | Interpretability | Requires scaling | Best for |
|---|---|---|---|---|
| Logistic Regression | Fast | High | Yes | Baseline, linear problems |
| Decision Tree | Fast | Very high | No | Interpretable rules |
| Random Forest | Medium | Medium | No | General purpose, best default |
| SVM | Slow (large data) | Low | Yes | Small/medium, high-dim |
| KNN | Slow (prediction) | Medium | Yes | Small datasets |
| Gradient Boosting | Medium | Low | No | Competitions, best accuracy |
Step 5: Model evaluation
Regression metrics
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import numpy as np
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"MSE: {mse:.2f}")
print(f"RMSE: {rmse:.2f}") # same units as target
print(f"MAE: {mae:.2f}") # robust to outliers
print(f"R²: {r2:.4f}") # 1.0 = perfect
| Metric | Formula | Good when |
|---|---|---|
| MSE | mean((y - ŷ)²) | Penalizes large errors heavily |
| RMSE | √MSE | Same units as target, interpretable |
| MAE | mean( | y - ŷ |
| R² | 1 - SS_res/SS_tot | Percentage of variance explained |
Classification metrics
from sklearn.metrics import (
accuracy_score, precision_score, recall_score,
f1_score, confusion_matrix, roc_auc_score
)
acc = accuracy_score(y_test, y_pred)
prec = precision_score(y_test, y_pred)
rec = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_prob)
print(f"Accuracy: {acc:.4f}")
print(f"Precision: {prec:.4f}")
print(f"Recall: {rec:.4f}")
print(f"F1 Score: {f1:.4f}")
print(f"ROC-AUC: {auc:.4f}")
# Confusion matrix
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues",
xticklabels=["No", "Yes"],
yticklabels=["No", "Yes"])
plt.ylabel("Actual")
plt.xlabel("Predicted")
plt.show()
| Metric | Formula | Use when |
|---|---|---|
| Accuracy | (TP+TN) / total | Balanced classes |
| Precision | TP / (TP+FP) | False positives are costly (spam filter) |
| Recall | TP / (TP+FN) | False negatives are costly (cancer detection) |
| F1 Score | 2×(P×R)/(P+R) | Imbalanced classes, balance P and R |
| ROC-AUC | Area under ROC curve | Ranking quality, imbalanced classes |
The confusion matrix:
Predicted No Predicted Yes
Actual No TN (correct) FP (false alarm)
Actual Yes FN (missed) TP (correct)
Cross-validation
More reliable than a single train/test split.
from sklearn.model_selection import cross_val_score, StratifiedKFold
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(
model, X, y,
cv=cv,
scoring="f1" # or "accuracy", "roc_auc", "neg_mean_squared_error"
)
print(f"CV F1: {scores.mean():.4f} ± {scores.std():.4f}")
Step 6: Improve your model
Hyperparameter tuning
Find the best settings for your algorithm.
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
# Grid search: try all combinations
param_grid = {
"n_estimators": [100, 200, 300],
"max_depth": [None, 5, 10, 20],
"min_samples_split": [2, 5, 10]
}
grid_search = GridSearchCV(
RandomForestClassifier(random_state=42),
param_grid,
cv=5,
scoring="f1",
n_jobs=-1, # use all CPU cores
verbose=1
)
grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best F1: {grid_search.best_score_:.4f}")
best_model = grid_search.best_estimator_
# Random search: faster for large spaces
from scipy.stats import randint
param_dist = {
"n_estimators": randint(50, 500),
"max_depth": [None, 5, 10, 15, 20, 30],
"min_samples_split": randint(2, 20)
}
random_search = RandomizedSearchCV(
RandomForestClassifier(random_state=42),
param_dist,
n_iter=50, # try 50 random combinations
cv=5,
scoring="f1",
random_state=42,
n_jobs=-1
)
random_search.fit(X_train, y_train)
Overfitting vs underfitting
| Problem | Symptoms | Fix |
|---|---|---|
| Underfitting | Low training AND test score | More features, complex model, less regularization |
| Overfitting | High training, low test score | More data, regularization, simpler model, cross-validation |
| Good fit | High training AND test score | You're done |
# Diagnose with learning curves
from sklearn.model_selection import learning_curve
train_sizes, train_scores, val_scores = learning_curve(
model, X, y, cv=5, scoring="f1",
train_sizes=np.linspace(0.1, 1.0, 10)
)
plt.plot(train_sizes, train_scores.mean(axis=1), label="Train")
plt.plot(train_sizes, val_scores.mean(axis=1), label="Validation")
plt.xlabel("Training set size")
plt.ylabel("F1 Score")
plt.legend()
plt.show()
Feature engineering
Often more impactful than algorithm choice.
# Polynomial features
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X_train)
# Log transform (for skewed distributions)
df["log_price"] = np.log1p(df["price"])
# Interaction features
df["rooms_per_person"] = df["rooms"] / df["household_size"]
df["price_per_sqft"] = df["price"] / df["sqft"]
# Binning continuous features
df["age_group"] = pd.cut(df["age"], bins=[0, 18, 35, 55, 100],
labels=["Youth", "Young Adult", "Middle", "Senior"])
Step 7: Unsupervised learning
K-Means Clustering
Groups data into K clusters.
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
# Always scale before clustering
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Find optimal K with elbow method
inertias = []
for k in range(1, 11):
km = KMeans(n_clusters=k, random_state=42, n_init=10)
km.fit(X_scaled)
inertias.append(km.inertia_)
plt.plot(range(1, 11), inertias, marker="o")
plt.xlabel("Number of clusters (K)")
plt.ylabel("Inertia (within-cluster sum of squares)")
plt.title("Elbow Method")
plt.show()
# Train with chosen K
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
df["cluster"] = kmeans.fit_predict(X_scaled)
# Analyze clusters
print(df.groupby("cluster").mean())
Principal Component Analysis (PCA)
Reduces dimensions while preserving variance.
from sklearn.decomposition import PCA
# Reduce to 2 components for visualization
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
print(f"Explained variance: {pca.explained_variance_ratio_}")
print(f"Total: {pca.explained_variance_ratio_.sum():.2%}")
# Plot in 2D
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap="viridis", alpha=0.6)
plt.xlabel("First Principal Component")
plt.ylabel("Second Principal Component")
plt.colorbar(label="Target")
plt.show()
# Choose n_components for target variance
pca_full = PCA()
pca_full.fit(X_scaled)
cumvar = pca_full.explained_variance_ratio_.cumsum()
n_components_95 = (cumvar < 0.95).sum() + 1
print(f"Components for 95% variance: {n_components_95}")
Step 8: Build ML pipelines
Pipelines combine preprocessing and modeling into one object. This prevents data leakage and makes deployment easier.
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier
# Define column types
numeric_features = ["age", "income", "credit_score"]
categorical_features = ["occupation", "region"]
# Preprocessing for numeric columns
numeric_transformer = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
])
# Preprocessing for categorical columns
categorical_transformer = Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore", drop="first"))
])
# Combine preprocessors
preprocessor = ColumnTransformer([
("num", numeric_transformer, numeric_features),
("cat", categorical_transformer, categorical_features)
])
# Full pipeline with model
pipeline = Pipeline([
("preprocessor", preprocessor),
("classifier", RandomForestClassifier(n_estimators=100, random_state=42))
])
# Train, evaluate — all in one
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
# Save the pipeline (model + preprocessor together)
import joblib
joblib.dump(pipeline, "model_pipeline.pkl")
# Load and predict
loaded_pipeline = joblib.load("model_pipeline.pkl")
predictions = loaded_pipeline.predict(new_data)
Project 1: Predict house prices (regression)
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.pipeline import Pipeline
# Load dataset (real California housing data)
data = fetch_california_housing(as_frame=True)
df = data.frame
X = df.drop("MedHouseVal", axis=1)
y = df["MedHouseVal"]
print(f"Dataset: {X.shape[0]} homes, {X.shape[1]} features")
print(df.describe())
# Split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Pipeline with Gradient Boosting
pipeline = Pipeline([
("scaler", StandardScaler()),
("model", GradientBoostingRegressor(
n_estimators=200,
learning_rate=0.1,
max_depth=5,
random_state=42
))
])
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"\nResults:")
print(f" RMSE: ${rmse * 100_000:.0f}")
print(f" R²: {r2:.4f} ({r2*100:.1f}% variance explained)")
# Cross-validation
cv_scores = cross_val_score(pipeline, X, y, cv=5, scoring="r2")
print(f" CV R²: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}")
# Feature importance
model = pipeline.named_steps["model"]
importances = pd.Series(
model.feature_importances_,
index=X.columns
).sort_values(ascending=False)
print(f"\nTop features:\n{importances.head()}")
Project 2: Classify iris flowers (classification)
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
# Load iris dataset
data = load_iris(as_frame=True)
X = data.data
y = data.target
target_names = data.target_names
print(f"Classes: {target_names}")
print(f"Shape: {X.shape}")
# Split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Train
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Evaluate
y_pred = model.predict(X_test)
print(f"\nAccuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred, target_names=target_names))
# Confusion matrix
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt="d",
xticklabels=target_names,
yticklabels=target_names,
cmap="Blues")
plt.title("Confusion Matrix")
plt.ylabel("Actual")
plt.xlabel("Predicted")
plt.show()
# Cross-validation
cv_acc = cross_val_score(model, X, y, cv=5, scoring="accuracy")
print(f"\nCV Accuracy: {cv_acc.mean():.4f} ± {cv_acc.std():.4f}")
# Feature importance
importances = pd.Series(
model.feature_importances_,
index=X.columns
).sort_values(ascending=False)
print(f"\nFeature importance:\n{importances}")
Project 3: Customer segmentation (clustering)
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt
import seaborn as sns
# Generate customer data (or load yours)
np.random.seed(42)
n_customers = 500
df = pd.DataFrame({
"annual_spend": np.random.exponential(5000, n_customers),
"purchase_freq": np.random.randint(1, 52, n_customers),
"avg_order_value": np.random.exponential(150, n_customers),
"days_since_last": np.random.randint(1, 365, n_customers),
"returns_rate": np.random.beta(1, 9, n_customers)
})
features = df.columns.tolist()
X = df[features].values
# Scale
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Find optimal K
silhouette_scores = []
inertias = []
K_range = range(2, 9)
for k in K_range:
km = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = km.fit_predict(X_scaled)
inertias.append(km.inertia_)
silhouette_scores.append(silhouette_score(X_scaled, labels))
# Plot elbow + silhouette
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.plot(K_range, inertias, marker="o")
ax1.set_title("Elbow Method")
ax1.set_xlabel("K")
ax1.set_ylabel("Inertia")
ax2.plot(K_range, silhouette_scores, marker="o")
ax2.set_title("Silhouette Score")
ax2.set_xlabel("K")
ax2.set_ylabel("Score")
plt.show()
best_k = K_range[np.argmax(silhouette_scores)]
print(f"Best K: {best_k}")
# Final clustering
kmeans = KMeans(n_clusters=best_k, random_state=42, n_init=10)
df["segment"] = kmeans.fit_predict(X_scaled)
# Analyze segments
segment_profiles = df.groupby("segment").mean().round(1)
print("\nSegment profiles:")
print(segment_profiles)
# Visualize with PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
plt.figure(figsize=(8, 6))
for seg in range(best_k):
mask = df["segment"] == seg
plt.scatter(X_pca[mask, 0], X_pca[mask, 1], label=f"Segment {seg}", alpha=0.6)
plt.xlabel(f"PC1 ({pca.explained_variance_ratio_[0]:.1%})")
plt.ylabel(f"PC2 ({pca.explained_variance_ratio_[1]:.1%})")
plt.legend()
plt.title("Customer Segments")
plt.show()
Choosing the right algorithm
Is your data labeled?
├── YES → Supervised learning
│ └── What is the output?
│ ├── Number → Regression
│ │ ├── Linear relationship? → Linear Regression
│ │ ├── Need interpretability? → Decision Tree
│ │ └── Best accuracy? → Gradient Boosting
│ └── Category → Classification
│ ├── Need interpretability? → Logistic Regression / Decision Tree
│ ├── Best default? → Random Forest
│ └── Small/medium dataset? → SVM
└── NO → Unsupervised learning
└── What is the goal?
├── Find groups → Clustering (K-Means, DBSCAN)
├── Reduce dimensions → PCA, t-SNE, UMAP
└── Find outliers → Isolation Forest, LOF
| Algorithm | Interpretable | Handles missing | Needs scaling | Training speed | Prediction speed |
|---|---|---|---|---|---|
| Linear/Logistic Regression | ✓ | ✗ | ✓ | Very fast | Very fast |
| Decision Tree | ✓ | ✗ | ✗ | Fast | Fast |
| Random Forest | Partial | ✗ | ✗ | Medium | Medium |
| Gradient Boosting | ✗ | ✗ | ✗ | Slow | Fast |
| SVM | ✗ | ✗ | ✓ | Slow | Fast |
| KNN | ✓ | ✗ | ✓ | None (lazy) | Slow |
| K-Means | ✗ | ✗ | ✓ | Fast | Fast |
ML learning path
| Stage | What to learn | Time | Resources |
|---|---|---|---|
| 1. Python basics | Python, NumPy, pandas | 2-4 weeks | Official docs, Kaggle learn |
| 2. ML fundamentals | Supervised/unsupervised, scikit-learn | 4-6 weeks | Hands-on ML book |
| 3. Practice on datasets | Kaggle competitions, UCI datasets | Ongoing | Kaggle.com |
| 4. Statistical foundations | Statistics, linear algebra, calculus | Parallel | StatQuest YouTube |
| 5. Deep learning | Neural networks, PyTorch/TensorFlow | 4-8 weeks | fast.ai, PyTorch tutorials |
| 6. Specialization | NLP, CV, time series, tabular | 4-12 weeks | Papers, domain-specific courses |
| 7. MLOps | Deployment, monitoring, pipelines | 4-8 weeks | MLflow, Airflow, Feast |
Free resources
| Resource | Best for |
|---|---|
| Kaggle Learn (free) | Practical ML fast |
| fast.ai (free) | Deep learning, top-down approach |
| StatQuest (YouTube) | Statistics and ML concepts |
| scikit-learn docs | Algorithm reference, examples |
| Hands-on ML with scikit-learn (book) | Comprehensive ML foundation |
| Google ML Crash Course (free) | Google's ML introduction |
| Papers With Code | State-of-the-art research |
Common mistakes
| Mistake | Problem | Fix |
|---|---|---|
| Data leakage | Test data influences training, inflated metrics | Fit preprocessors on train only, use Pipelines |
| Not splitting data | Can't measure generalization | Always train/test split, use cross-validation |
| Ignoring class imbalance | Model predicts majority class only | Use stratify, class_weight="balanced", SMOTE |
| Scaling test data with test stats | Data leakage | fit on train, transform only on test |
| Optimizing for wrong metric | High accuracy on imbalanced data means nothing | Use F1/AUC for imbalanced, RMSE for regression |
| Not checking data quality | Garbage in, garbage out | EDA first: missing values, outliers, distributions |
| Jumping to complex models | Hard to debug, no baseline | Start with Linear/Logistic Regression, improve iteratively |
| No cross-validation | Lucky/unlucky split gives false confidence | Use k-fold CV for robust estimates |
ML vs related terms
| Term | What it is | Relation to ML |
|---|---|---|
| AI | Broad field of machines acting intelligently | ML is a subset of AI |
| Deep Learning | ML using neural networks with many layers | Subset of ML |
| Neural Network | Algorithm inspired by the brain | A deep learning model |
| Data Science | Extracting insights from data (stats + ML + viz) | Uses ML as one tool |
| Statistics | Mathematical data analysis | Foundation of ML |
| MLOps | ML operations: deploy, monitor, maintain models | Productionizing ML |
| Feature Engineering | Creating/transforming input variables | Critical ML step |
| scikit-learn | Python ML library | Main tool for classical ML |
| PyTorch/TensorFlow | Deep learning frameworks | For neural networks |
| Kaggle | ML competition platform | Practice, datasets |
FAQ
Do I need math for machine learning?
For practical ML with scikit-learn: basic understanding of statistics helps (mean, variance, distributions) but isn't required to start. You can learn the math as you go. For deep learning research: linear algebra, calculus, and probability are essential.
Python vs R for machine learning?
Python. R is excellent for statistics and academia, but Python dominates industry ML. scikit-learn, PyTorch, TensorFlow, and most production tools are Python-first.
How much data do I need?
General rules: Linear regression ~50-100 rows per feature. Decision trees ~1,000 rows. Random forests ~5,000-10,000 rows. Deep learning: 10,000-1,000,000+. But these vary wildly by problem complexity.
What's the difference between AI, ML, and deep learning?
AI is the broad goal (machines that act intelligently). ML is one approach (learning from data). Deep learning is a subset of ML using neural networks with many layers. All deep learning is ML, all ML is AI.
When should I use deep learning vs classical ML?
Classical ML (scikit-learn): tabular/structured data, small-medium datasets (<100k rows), interpretability needed, limited compute. Deep learning: images, text, audio, video; large datasets; maximum accuracy more important than interpretability.
How do I get ML projects for my portfolio?
Kaggle competitions (start with Titanic, House Prices). UCI Machine Learning Repository. Open government datasets. Find a problem you care about and collect data yourself. Recreate a published ML paper.
Quick reference
# Full supervised learning workflow
import pandas as pd
import numpy as np
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
# 1. Load data
df = pd.read_csv("data.csv")
# 2. Explore
print(df.head(), df.describe(), df.isnull().sum())
# 3. Prepare features/target
X = df.drop("target", axis=1)
y = df["target"]
# 4. Split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 5. Build pipeline
pipeline = Pipeline([
("scaler", StandardScaler()),
("model", RandomForestClassifier(n_estimators=100, random_state=42))
])
# 6. Train and evaluate
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(classification_report(y_test, y_pred))
# 7. Cross-validate
scores = cross_val_score(pipeline, X, y, cv=5, scoring="f1_weighted")
print(f"CV F1: {scores.mean():.4f} ± {scores.std():.4f}")
# 8. Save
import joblib
joblib.dump(pipeline, "model.pkl")