COURSE ยท 10 LESSONS ยท 100% FREE
๐Ÿค–

AI/ML Masterclass

From theory to production โ€” 10 lessons covering algorithms, neural networks, transformers, and real-world deployment.

0Lessons
0Code Examples
SomeProgramming Experience
0%
You've completed 0 of 10 lessons
J/โ†“ Next
K/โ†‘ Prev
Esc Collapse
/ Search
Foundations

The AI Hierarchy

Artificial Intelligence is the broadest field โ€” any technique that enables machines to mimic human intelligence. Machine Learning is a subset of AI where systems learn from data without being explicitly programmed. Deep Learning is a subset of ML using neural networks with many layers.

Types of Machine Learning

  • Supervised Learning: Labeled data โ†’ learn mapping function f(X) โ†’ y. Examples: classification, regression.
  • Unsupervised Learning: No labels โ†’ find hidden patterns. Examples: clustering, dimensionality reduction.
  • Reinforcement Learning: Agent learns by interacting with environment, maximizing cumulative reward.
  • Self-Supervised Learning: Labels derived from the data itself (used in modern LLMs like GPT, BERT).

Real-World Applications

Python
applications = {
    "Classification": ["Spam detection", "Medical diagnosis", "Sentiment analysis"],
    "Regression":     ["House pricing", "Stock prediction", "Demand forecasting"],
    "Clustering":     ["Customer segmentation", "Anomaly detection", "Document grouping"],
    "Generation":     ["Image synthesis", "Text generation", "Music composition"],
    "RL":             ["Game AI", "Robotics", "Autonomous driving"]
}
for task, examples in applications.items():
    print(f"{task}: {', '.join(examples)}")
๐Ÿ’ก
Key Insight: Most real-world ML problems fall into supervised learning. Master that first, then explore unsupervised and reinforcement learning.

The ML Workflow

  • 1. Define the problem โ†’ classification? regression?
  • 2. Collect and clean data
  • 3. Feature engineering and selection
  • 4. Choose a model
  • 5. Train, validate, tune hyperparameters
  • 6. Evaluate on test set
  • 7. Deploy and monitor
๐ŸงชQuick Check
Which type of ML uses labeled data to learn a mapping from inputs to outputs?

NumPy โ€” Numerical Computing

Python
import numpy as np
a = np.array([1, 2, 3, 4, 5])
print(a.mean(), a.std(), a.max())
X = np.random.randn(100, 5)
y = X @ np.array([1, 2, 3, 4, 5]) + np.random.randn(100) * 0.1
X_centered = X - X.mean(axis=0)

Pandas โ€” Data Manipulation

Python
import pandas as pd
df = pd.read_csv("housing.csv")
print(df.head())
print(df.describe())
df.dropna(inplace=True)
df["price_per_sqft"] = df["price"] / df["sqft"]
df.groupby("neighborhood")["price"].agg(["mean", "median", "count"])

Matplotlib โ€” Visualization

Python
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].scatter(df["sqft"], df["price"], alpha=0.3, s=10)
axes[0].set_xlabel("Square Feet")
axes[0].set_ylabel("Price")
axes[1].hist(df["price"], bins=50, edgecolor="black")
plt.tight_layout()
plt.savefig("eda_plots.png", dpi=150)
plt.show()

scikit-learn โ€” Model Building

Python
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
X = np.random.randn(1000, 3)
y = X @ [1.5, -2.0, 3.0] + np.random.randn(1000) * 0.5
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}")
print(f"R2:   {r2_score(y_test, y_pred):.4f}")
โœ…
Pro Tip: Always split data BEFORE any preprocessing. Use Pipeline to chain transforms and models to prevent data leakage.
โ† PrevAI Fundamentals
Core Algorithms

The Linear Model

Prediction: ลท = wโ‚xโ‚ + wโ‚‚xโ‚‚ + ... + wโ‚™xโ‚™ + b = wยทX + b

Goal: find weights w and bias b that minimize the error between predictions and actual values.

Cost Function (Mean Squared Error)

Python
import numpy as np
def compute_cost(X, y, w, b):
    m = len(y)
    predictions = X @ w + b
    cost = (1 / (2 * m)) * np.sum((predictions - y) ** 2)
    return cost

Gradient Descent

Python
def gradient_descent(X, y, w, b, learning_rate, epochs):
    m = len(y)
    cost_history = []
    for epoch in range(epochs):
        predictions = X @ w + b
        dw = (1 / m) * (X.T @ (predictions - y))
        db = (1 / m) * np.sum(predictions - y)
        w -= learning_rate * dw
        b -= learning_rate * db
        cost = compute_cost(X, y, w, b)
        cost_history.append(cost)
        if epoch % 100 == 0:
            print(f"Epoch {epoch}: Cost = {cost:.6f}")
    return w, b, cost_history
np.random.seed(42)
X = 2 * np.random.randn(100, 1)
y = 4 + 3 * X.squeeze() + np.random.randn(100) * 0.5
w = np.zeros(X.shape[1])
b = 0
w, b, costs = gradient_descent(X, y, w, b, learning_rate=0.1, epochs=1000)
print(f"Learned: w = {w[0]:.4f}, b = {b:.4f}")

Learning Rate Selection

  • Too small: Slow convergence, may get stuck in local minima
  • Too large: Overshoots, may diverge (cost increases)
  • Good rule: Start with 0.01, try 0.001 to 0.3

Evaluation Metrics

Python
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
y_true = np.array([3, -0.5, 2, 7])
y_pred = np.array([2.5, 0.0, 2, 8])
mae  = mean_absolute_error(y_true, y_pred)
mse  = mean_squared_error(y_true, y_pred)
rmse = np.sqrt(mse)
r2   = r2_score(y_true, y_pred)
print(f"MAE:  {mae:.4f}")
print(f"MSE:  {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"R2:   {r2:.4f}")
๐Ÿ’ก
Math: Rยฒ = 1 - (SS_res / SS_tot), where SS_res = ฮฃ(y - ลท)ยฒ and SS_tot = ฮฃ(y - ศณ)ยฒ. An Rยฒ of 0.85 means the model explains 85% of the variance.
โ† PrevPython for ML

Logistic Regression

Despite the name, it's a classification algorithm. Uses sigmoid function to map predictions to probabilities:

Python
import numpy as np
def sigmoid(z):
    return 1 / (1 + np.exp(-np.clip(z, -500, 500)))
z = np.linspace(-10, 10, 100)
probs = sigmoid(z)
def predict(X, w, b, threshold=0.5):
    return (sigmoid(X @ w + b) >= threshold).astype(int)

Binary Classification with scikit-learn

Python
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, confusion_matrix
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.2, random_state=42)
pipe = Pipeline([("scaler", StandardScaler()), ("model", LogisticRegression(max_iter=10000))])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print(classification_report(y_test, y_pred))
print(confusion_matrix(y_test, y_pred))

Support Vector Machines (SVM)

Python
from sklearn.svm import SVC
svm_linear = SVC(kernel="linear", C=1.0)
svm_linear.fit(X_train, y_train)
print(f"Accuracy: {svm_linear.score(X_test, y_test):.4f}")
svm_rbf = SVC(kernel="rbf", C=1.0, gamma="scale")
svm_rbf.fit(X_train, y_train)
print(f"Accuracy: {svm_rbf.score(X_test, y_test):.4f}")

Decision Trees & Random Forests

Python
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
dt = DecisionTreeClassifier(max_depth=5, random_state=42)
dt.fit(X_train, y_train)
print(f"Tree Accuracy:    {dt.score(X_test, y_test):.4f}")
rf = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
print(f"Forest Accuracy:  {rf.score(X_test, y_test):.4f}")
importances = rf.feature_importances_
top_idx = np.argsort(importances)[::-1][:5]
for i in top_idx:
    print(f"  {data.feature_names[i]}: {importances[i]:.4f}")
โœ…
Algorithm Selection Guide: Start with Logistic Regression (fast, interpretable). Try Random Forest (robust, handles non-linearity). Use SVM for smaller datasets with clear margins.
๐ŸงชQuick Check
What is the main advantage of Random Forests over a single Decision Tree?
โ† PrevLinear Regression

K-Means Clustering

Python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=42)
kmeans = KMeans(n_clusters=4, init="k-means++", n_init=10, random_state=42)
kmeans.fit(X)
labels = kmeans.labels_
centroids = kmeans.cluster_centers_
print(f"Inertia (SSE): {kmeans.inertia_:.2f}")
inertias = []
K_range = range(1, 11)
for k in K_range:
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    km.fit(X)
    inertias.append(km.inertia_)
plt.plot(K_range, inertias, "bo-")
plt.xlabel("Number of Clusters (k)")
plt.ylabel("Inertia")
plt.title("Elbow Method")
plt.savefig("elbow.png")

DBSCAN โ€” Density-Based Clustering

Python
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
X_scaled = StandardScaler().fit_transform(X)
dbscan = DBSCAN(eps=0.3, min_samples=5)
dbscan.fit(X_scaled)
labels = dbscan.labels_
n_clusters = len(set(labels)) - (1 if -1 in labels else 0)
n_noise = list(labels).count(-1)
print(f"Clusters found: {n_clusters}")
print(f"Noise points:   {n_noise}")

Hierarchical Clustering

Python
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
agg = AgglomerativeClustering(n_clusters=4, linkage="ward")
labels = agg.fit_predict(X)
Z = linkage(X, method="ward")
plt.figure(figsize=(12, 5))
dendrogram(Z, truncate_mode="lastp", p=30)
plt.title("Hierarchical Clustering Dendrogram")
plt.savefig("dendrogram.png")

Evaluation Methods

Python
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
sil = silhouette_score(X, labels)
ch  = calinski_harabasz_score(X, labels)
db  = davies_bouldin_score(X, labels)
print(f"Silhouette:           {sil:.4f}")
print(f"Calinski-Harabasz:    {ch:.1f}")
print(f"Davies-Bouldin:       {db:.4f}")
โš ๏ธ
When to use which: K-Means for spherical clusters with known k. DBSCAN for arbitrary shapes and noisy data. Hierarchical for small datasets where you need a dendrogram. Always scale features before clustering.
โ† PrevClassification
Deep Learning

The Perceptron

A single neuron computes: z = wโ‚xโ‚ + wโ‚‚xโ‚‚ + ... + wโ‚™xโ‚™ + b, then applies an activation function: a = ฯƒ(z).

Activation Functions

Python
import numpy as np
import matplotlib.pyplot as plt
z = np.linspace(-5, 5, 200)
def sigmoid(z): return 1 / (1 + np.exp(-z))
def relu(z): return np.maximum(0, z)
def tanh(z): return np.tanh(z)
def leaky_relu(z, alpha=0.01): return np.where(z > 0, z, alpha * z)
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
axes[0,0].plot(z, sigmoid(z)); axes[0,0].set_title("Sigmoid")
axes[0,1].plot(z, relu(z));    axes[0,1].set_title("ReLU")
axes[1,0].plot(z, tanh(z));    axes[1,0].set_title("Tanh")
axes[1,1].plot(z, leaky_relu(z)); axes[1,1].set_title("Leaky ReLU")
plt.savefig("activations.png")
โ„น๏ธ
Rule of thumb: Use ReLU for hidden layers (fast, avoids vanishing gradient). Use Sigmoid for binary output. Use Softmax for multi-class output.

Forward Pass from Scratch

Python
import numpy as np
def forward_pass(X, weights, biases):
    z1 = X @ weights["W1"] + biases["b1"]
    a1 = np.maximum(0, z1)
    z2 = a1 @ weights["W2"] + biases["b2"]
    a2 = 1 / (1 + np.exp(-z2))
    return {"z1": z1, "a1": a1, "z2": z2, "a2": a2}
np.random.seed(42)
n_features, n_hidden, n_output = 10, 4, 1
weights = {"W1": np.random.randn(n_features, n_hidden) * np.sqrt(2/n_features), "W2": np.random.randn(n_hidden, n_output) * np.sqrt(2/n_hidden)}
biases = {"b1": np.zeros(n_hidden), "b2": np.zeros(n_output)}
X_dummy = np.random.randn(5, n_features)
output = forward_pass(X_dummy, weights, biases)
print(f"Predictions shape: {output['a2'].shape}")

PyTorch โ€” Building Neural Networks

Python
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
X, y = make_classification(n_samples=1000, n_features=20, n_informative=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
X_train_t = torch.FloatTensor(X_train)
y_train_t = torch.FloatTensor(y_train).unsqueeze(1)
X_test_t = torch.FloatTensor(X_test)
y_test_t = torch.FloatTensor(y_test).unsqueeze(1)
class NeuralNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(nn.Linear(20, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, 32), nn.ReLU(), nn.Dropout(0.2), nn.Linear(32, 1), nn.Sigmoid())
    def forward(self, x): return self.net(x)
model = NeuralNet()
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
for epoch in range(100):
    model.train()
    optimizer.zero_grad()
    output = model(X_train_t)
    loss = criterion(output, y_train_t)
    loss.backward()
    optimizer.step()
    if (epoch + 1) % 20 == 0:
        model.eval()
        with torch.no_grad():
            test_output = model(X_test_t)
            preds = (test_output > 0.5).float()
            acc = (preds == y_test_t).float().mean()
        print(f"Epoch {epoch+1}: Loss={loss:.4f}, Acc={acc:.4f}")
๐Ÿ’ก
Backpropagation: The chain rule applied layer by layer from output to input. โˆ‚L/โˆ‚w = โˆ‚L/โˆ‚a ยท โˆ‚a/โˆ‚z ยท โˆ‚z/โˆ‚w. PyTorch's loss.backward() computes all gradients automatically.
โ† PrevClustering

Convolution Operation

Python
import torch
import torch.nn as nn
conv = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
x = torch.randn(1, 3, 224, 224)
out = conv(x)
print(f"Input:  {x.shape}")
print(f"Output: {out.shape}")
pool = nn.MaxPool2d(kernel_size=2, stride=2)
out_pooled = pool(out)
print(f"Pooled: {out_pooled.shape}")

Full CNN Architecture

Python
class CNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(),
            nn.Conv2d(32, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(),
            nn.MaxPool2d(2), nn.Dropout2d(0.25),
            nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
            nn.Conv2d(64, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
            nn.MaxPool2d(2), nn.Dropout2d(0.25),
            nn.Conv2d(64, 128, 3, padding=1), nn.BatchNorm2d(128), nn.ReLU(),
            nn.MaxPool2d(2), nn.Dropout2d(0.25))
        self.classifier = nn.Sequential(nn.Flatten(), nn.Linear(128 * 28 * 28, 256), nn.ReLU(), nn.Dropout(0.5), nn.Linear(256, num_classes))
    def forward(self, x): return self.classifier(self.features(x))

Transfer Learning

Python
import torchvision.models as models
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
for param in model.parameters():
    param.requires_grad = False
num_classes = 5
model.fc = nn.Sequential(nn.Linear(model.fc.in_features, 256), nn.ReLU(), nn.Dropout(0.3), nn.Linear(256, num_classes))
optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.001)
๐Ÿ’ก
Key Architectures: LeNet โ†’ AlexNet โ†’ VGG โ†’ GoogLeNet โ†’ ResNet (skip connections) โ†’ EfficientNet โ†’ Vision Transformer. Always start with transfer learning unless you have millions of images.
โ† PrevNeural Networks
Production & Projects

Self-Attention Mechanism

Python
import torch, torch.nn as nn, math
class SelfAttention(nn.Module):
    def __init__(self, embed_dim, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads
        self.scale = math.sqrt(self.head_dim)
        self.qkv = nn.Linear(embed_dim, 3 * embed_dim)
        self.out_proj = nn.Linear(embed_dim, embed_dim)
    def forward(self, x):
        B, T, C = x.shape
        qkv = self.qkv(x).reshape(B, T, 3, self.num_heads, self.head_dim)
        q, k, v = qkv.permute(2, 0, 3, 1, 4).unbind(0)
        attn = (q @ k.transpose(-2, -1)) / self.scale
        attn = torch.softmax(attn, dim=-1)
        out = (attn @ v).transpose(1, 2).reshape(B, T, C)
        return self.out_proj(out)
attn = SelfAttention(512, 8)
x = torch.randn(2, 10, 512)
print(attn(x).shape)
โ„น๏ธ
Why Attention? RNNs process sequentially (slow, no parallelism). Attention looks at ALL positions simultaneously, learning which words relate to each other. This enables massive parallelization and longer context.

Positional Encoding

Python
def positional_encoding(seq_len, d_model):
    pe = torch.zeros(seq_len, d_model)
    position = torch.arange(0, seq_len).unsqueeze(1).float()
    div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
    pe[:, 0::2] = torch.sin(position * div_term)
    pe[:, 1::2] = torch.cos(position * div_term)
    return pe
pe = positional_encoding(100, 512)
print(pe.shape)

Fine-Tuning with Hugging Face

Python
from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer
from datasets import load_dataset
dataset = load_dataset("imdb")
model_name = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
def tokenize(batch):
    return tokenizer(batch["text"], padding=True, truncation=True, max_length=512)
dataset = dataset.map(tokenize, batched=True)
args = TrainingArguments(output_dir="./results", num_train_epochs=3, per_device_train_batch_size=16, per_device_eval_batch_size=64, eval_strategy="epoch", learning_rate=2e-5, weight_decay=0.01, fp16=True)
trainer = Trainer(model=model, args=args, train_dataset=dataset["train"].shuffle(seed=42).select(range(5000)), eval_dataset=dataset["test"].shuffle(seed=42).select(range(1000)))
trainer.train()
๐Ÿ’ก
Model Landscape: BERT (encoder-only, understanding), GPT (decoder-only, generation), T5 (encoder-decoder, both). For LLMs: Llama, Mistral, Phi are open-source. Use LoRA/QLoRA for efficient fine-tuning.
โ† PrevCNNs

Save & Load Models

Python
import joblib, torch
joblib.dump(model, "model.joblib")
loaded_model = joblib.load("model.joblib")
torch.save(model.state_dict(), "model.pt")
model = MyModel()
model.load_state_dict(torch.load("model.pt"))
model.eval()

FastAPI Model Server

Python
from fastapi import FastAPI
from pydantic import BaseModel
import joblib, numpy as np
app = FastAPI()
model = joblib.load("model.joblib")
class PredictionRequest(BaseModel):
    features: list[float]
class PredictionResponse(BaseModel):
    prediction: float
    confidence: float | None = None
@app.post("/predict", response_model=PredictionResponse)
def predict(req: PredictionRequest):
    X = np.array(req.features).reshape(1, -1)
    pred = model.predict(X)[0]
    proba = getattr(model, "predict_proba", lambda X: [[None]])(X)[0]
    return PredictionResponse(prediction=float(pred), confidence=float(max(proba)) if proba[0] is not None else None)
@app.get("/health")
def health():
    return {"status": "ok", "model": type(model).__name__}

Dockerfile

Docker
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY model.joblib .
COPY api.py .
EXPOSE 8000
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]

Docker Build & Run

Shell
docker build -t ml-api .
docker run -p 8000:8000 ml-api
curl -X POST http://localhost:8000/predict -H "Content-Type: application/json" -d '{"features": [5.1, 3.5, 1.4, 0.2]}'

Model Monitoring

Python
import logging
from datetime import datetime
logging.basicConfig(filename="model_monitor.log", level=logging.INFO)
def log_prediction(features, prediction, latency_ms):
    logging.info({"timestamp": datetime.utcnow().isoformat(), "features": features, "prediction": prediction, "latency_ms": latency_ms})
def detect_drift(reference_data, new_data, threshold=0.05):
    from scipy.stats import ks_2samp
    drift_detected = False
    for col in reference_data.columns:
        stat, p_value = ks_2samp(reference_data[col], new_data[col])
        if p_value < threshold:
            print(f"DRIFT: {col} (p={p_value:.4f})")
            drift_detected = True
    return drift_detected
โœ…
MLOps Stack: FastAPI (serving) โ†’ Docker (containerization) โ†’ GitHub Actions (CI/CD) โ†’ Prometheus + Grafana (monitoring) โ†’ MLflow (experiment tracking) โ†’ DVC (data versioning).
โ† PrevTransformers & LLMs

Project 1: Email Spam Detector

Python
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
df = pd.read_csv("spam.csv", encoding="latin-1")
df = df[["v1", "v2"]].rename(columns={"v1": "label", "v2": "text"})
df["label"] = df["label"].map({"ham": 0, "spam": 1})
pipeline = Pipeline([("tfidf", TfidfVectorizer(max_features=5000, ngram_range=(1, 2))), ("clf", MultinomialNB(alpha=0.1))])
scores = cross_val_score(pipeline, df["text"], df["label"], cv=5, scoring="f1")
print(f"F1: {scores.mean():.4f} +/- {scores.std():.4f}")
pipeline.fit(df["text"], df["label"])
tests = ["Congratulations! You won a free iPhone. Click here!", "Hey, are we still meeting for lunch tomorrow?", "URGENT: Your account has been compromised. Send password now."]
for text in tests:
    pred = pipeline.predict([text])[0]
    proba = pipeline.predict_proba([text])[0][1]
    print(f"{'SPAM' if pred else 'HAM '}: {proba:.2%} | {text[:50]}")

Project 2: House Price Predictor

Python
import pandas as pd, numpy as np
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, r2_score
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
df = pd.read_csv("housing.csv")
numeric = ["sqft", "bedrooms", "bathrooms", "age"]
categorical = ["neighborhood", "condition"]
X = df[numeric + categorical]
y = df["price"]
preprocessor = ColumnTransformer([("num", StandardScaler(), numeric), ("cat", OneHotEncoder(handle_unknown="ignore"), categorical)])
pipeline = Pipeline([("prep", preprocessor), ("model", GradientBoostingRegressor(n_estimators=200, max_depth=5, learning_rate=0.1, random_state=42))])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(f"MAE:  ${mean_absolute_error(y_test, y_pred):,.0f}")
print(f"R2:   {r2_score(y_test, y_pred):.4f}")

Project 3: Image Classifier (CIFAR-10)

Python
import torch, torch.nn as nn, torch.optim as optim, torchvision, torchvision.transforms as transforms
transform = transforms.Compose([transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding=4), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616))])
trainset = torchvision.datasets.CIFAR10(root="./data", train=True, download=True, transform=transform)
testset = torchvision.datasets.CIFAR10(root="./data", train=False, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2)
testloader = torch.utils.data.DataLoader(testset, batch_size=128)
model = torchvision.models.resnet18(weights=None)
model.conv1 = nn.Conv2d(3, 64, 3, 1, 1, bias=False)
model.maxpool = nn.Identity()
model.fc = nn.Linear(512, 10)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50)
for epoch in range(50):
    model.train()
    for images, labels in trainloader:
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
    scheduler.step()
    model.eval()
    correct, total = 0, 0
    with torch.no_grad():
        for images, labels in testloader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            _, predicted = outputs.max(1)
            total += labels.size(0)
            correct += predicted.eq(labels).sum().item()
    print(f"Epoch {epoch+1}: Accuracy = {100.*correct/total:.2f}%")

Project 4: Simple Chatbot with LangChain

Python
from langchain_community.llms import Ollama
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
llm = Ollama(model="llama3.2:1b")
template = """You are a helpful AI assistant. Answer clearly and concisely. If you don't know, say so. Context: {context} Question: {question} Answer:"""
prompt = ChatPromptTemplate.from_template(template)
chain = ({"context": RunnablePassthrough(), "question": RunnablePassthrough()} | prompt | llm | StrOutputParser())
print("Chatbot ready! Type 'quit' to exit.")
while True:
    question = input("\nYou: ")
    if question.lower() in ["quit", "exit", "q"]: break
    response = chain.invoke({"context": "", "question": question})
    print(f"Bot: {response}")
โœ…
Portfolio Tip: Deploy each project on GitHub with a README, live demo (Streamlit/Gradio), and clear documentation. Recruiters value working projects over certificates.
โ† PrevMLOps

๐Ÿ“š Resources & Further Learning

๐ŸŽ“
fast.ai Practical Deep Learning
Top-down, code-first approach to deep learning. Free course with practical projects.
course.fast.ai โ†’
๐Ÿ“บ
Andrew Ng โ€” Machine Learning
The foundational course on ML. Covers theory, math, and implementation.
coursera.org โ†’
๐Ÿ“„
Papers With Code
State-of-the-art papers with implementations. Track benchmarks and trends.
paperswithcode.com โ†’
๐Ÿ“–
Neural Networks: Zero to Hero (Andrej Karpathy)
Build neural networks from scratch. Backprop, GPT, tokenization, and more.
karpathy.ai โ†’
๐Ÿ”ง
scikit-learn Documentation
Comprehensive API docs with examples for every algorithm.
scikit-learn.org โ†’
๐Ÿง 
Hugging Face Course
Transformers, NLP, and modern deep learning with the HF ecosystem.
huggingface.co/learn โ†’
AI
ML Tutor
ZenMux ยท GLM 4.7 Flash
Ask me anything about AI/ML! I can help with neural networks, transformers, scikit-learn, PyTorch, or explain any concept from the lessons above.