ML Lab Manual
ML Lab Manual
LAB MANUAL
AIM: Implement Linear Regression to predict California housing prices and evaluate model
performance.
OBJECTIVE: Understand supervised regression, apply feature scaling, compute MSE/RMSE/R², and
visualize predictions.
SOFTWARE REQUIRED
• Python 3.10+
• NumPy
• Pandas
• Scikit-learn
• Matplotlib
THEORY
Linear Regression models a continuous output as a weighted linear combination of input features: y
= β₀ + β₁x₁ + ... + βₙxₙ + ε. Coefficients are optimised by minimising the Mean Squared Error (MSE)
using Ordinary Least Squares (OLS). Key metrics are RMSE (lower is better) and R² (1 = perfect fit).
StandardScaler normalises features so no single variable dominates gradient descent.
The California Housing dataset contains 20,640 census block records with 8 socio-economic features.
Target: median house value in units of $100,000. Industry domain: Real Estate / Urban Planning.
DATASET
California Housing (sklearn) – 20,640 rows × 8 features: MedInc, HouseAge, AveRooms, AveBedrms,
Population, AveOccup, Latitude, Longitude. Target: MedHouseVal ($100k).
ALGORITHM / PROCEDURE
4. StandardScaler on X
5. Fit LinearRegression
6. Predict on test set
PROGRAM / CODE
# 1. Load
data = fetch_california_housing(as_frame=True)
X, y = [Link], [Link]
print(f"Shape: {[Link]} | Target range: {[Link]():.2f}–{[Link]():.2f}")
# 3. Train
model = LinearRegression()
[Link](X_tr, y_tr)
y_pred = [Link](X_te)
# 4. Metrics
rmse = [Link](mean_squared_error(y_te, y_pred))
r2 = r2_score(y_te, y_pred)
print(f"RMSE: {rmse:.4f} | R2: {r2:.4f}")
print("Coefficients:")
print([Link](model.coef_, index=data.feature_names).sort_values().round(4))
# 5. Plot
fig, ax = [Link](1,2,figsize=(12,5))
ax[0].scatter(y_te, y_pred, alpha=0.35, c='steelblue')
ax[0].plot([y_te.min(),y_te.max()],[y_te.min(),y_te.max()],'r--')
ax[0].set(xlabel='Actual',ylabel='Predicted',title=f'Actual vs Predicted R2={r2:.3f}')
ax[1].hist(y_te-y_pred, bins=50, color='steelblue', ec='white')
ax[1].set(xlabel='Residuals', title='Residual Distribution')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 1 complete.")
EXPECTED OUTPUT
Shape: (20640, 8) | RMSE: ~0.745 | R2: ~0.576
MedInc has the highest positive coefficient (~0.83); Latitude shows strong negative effect.
Scatter plot shows moderate positive correlation; residuals approximately normal around zero.
RESULT
VIVA QUESTIONS
• Q1. What is the difference between simple and multiple linear regression?
OBJECTIVE: Understand binary classification, sigmoid function, and evaluate using accuracy, AUC-
ROC, precision-recall.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• Pandas
• Seaborn
• Matplotlib
THEORY
Logistic Regression models the probability P(y=1|x) = σ(wᵀx+b) where σ is the sigmoid function. The
model is trained by minimising binary cross-entropy loss. Unlike linear regression, the output is
bounded [0,1] and a threshold (default 0.5) converts probability to class label.
DATASET
Breast Cancer Wisconsin (sklearn) – 569 samples, 30 numeric features (radius, texture, perimeter,
area, smoothness, etc.). Binary target: Malignant (0) or Benign (1).
ALGORITHM / PROCEDURE
4. StandardScaler
5. Fit LogisticRegression
6. Predict and compute probability scores
PROGRAM / CODE
# 1. Load
data = load_breast_cancer(as_frame=True)
X, y = [Link], [Link]
print(f"Shape: {[Link]} | Classes: {list(data.target_names)}")
print(f"Class counts: {dict(zip(*[Link](y, return_counts=True)))}")
# 3. Train
model = LogisticRegression(max_iter=1000, C=1.0)
[Link](X_tr, y_tr)
y_pred = [Link](X_te)
y_proba = model.predict_proba(X_te)[:,1]
# 4. Metrics
acc = accuracy_score(y_te, y_pred)
auc = roc_auc_score(y_te, y_proba)
print(f"Accuracy: {acc*100:.2f}% | AUC: {auc:.4f}")
print(classification_report(y_te, y_pred, target_names=data.target_names))
# 5. Plots
fig, ax = [Link](1,2,figsize=(12,5))
cm = confusion_matrix(y_te, y_pred)
[Link](cm, annot=True, fmt='d', cmap='Blues',
xticklabels=data.target_names, yticklabels=data.target_names, ax=ax[0])
ax[0].set(xlabel='Predicted', ylabel='Actual', title='Confusion Matrix')
fpr,tpr,_ = roc_curve(y_te, y_proba)
ax[1].plot(fpr,tpr,'b-',label=f'AUC={auc:.3f}'); ax[1].plot([0,1],[0,1],'r--')
ax[1].set(xlabel='FPR',ylabel='TPR',title='ROC Curve'); ax[1].legend()
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 2 complete.")
EXPECTED OUTPUT
RESULT
Logistic Regression achieved 95.6% accuracy and AUC=0.996 on breast cancer classification,
demonstrating excellent separability between malignant and benign tumours.
VIVA QUESTIONS
OBJECTIVE: Understand Gini impurity / information gain, visualise tree structure, analyse feature
importance.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• Pandas
• NumPy
• Seaborn
• Matplotlib
THEORY
A Decision Tree recursively partitions the feature space by selecting splits that maximise information
gain (or minimise Gini impurity). Gini(t) = 1 − Σpᵢ². At each node the algorithm selects the feature and
threshold that produce the purest child nodes. max_depth and min_samples_split control overfitting.
Feature importance = normalised sum of weighted impurity reductions across all splits on a feature.
Decision trees are interpretable ("white-box") and handle both categorical and numeric features.
Domain: Telecommunications / CRM Analytics.
DATASET
Telecom Churn dataset (simulated IBM Telco style) – 1,000 records, features: tenure,
monthly_charges, total_charges, contract type, internet service, tech support. Target: Churn (0/1).
ALGORITHM / PROCEDURE
3. Split 80/20
4. Fit DecisionTreeClassifier(max_depth=5)
5. Evaluate accuracy and report
9. Record observations
PROGRAM / CODE
# 3. Metrics
print(f"Accuracy: {accuracy_score(y_te,y_pred)*100:.2f}%")
print(classification_report(y_te, y_pred, target_names=['No Churn','Churn']))
# 4. Feature Importance + CM
fig, ax = [Link](1, 2, figsize=(13,5))
fi = [Link](dt.feature_importances_, index=[Link]).sort_values()
[Link](ax=ax[0], color='steelblue')
ax[0].set(title='Feature Importances')
cm = confusion_matrix(y_te, y_pred)
[Link](cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['No Churn','Churn'], yticklabels=['No Churn','Churn'], ax=ax[1])
ax[1].set(title='Confusion Matrix')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 3 complete.")
EXPECTED OUTPUT
RESULT
Decision Tree (max_depth=5) achieved ~82% accuracy on telecom churn. Feature importance
analysis shows monthly_charges as the most discriminative predictor.
VIVA QUESTIONS
• Q3. Why are decision trees prone to overfitting, and how do you prevent it?
• Q5. How does a Random Forest improve upon a single Decision Tree?
EXPERIMENT 4: Random Forest – Credit Card Fraud Detection
AIM: Use Random Forest with SMOTE to detect fraudulent credit card transactions from an
imbalanced dataset.
OBJECTIVE: Understand ensemble bagging, handle class imbalance with SMOTE, optimise for recall
and precision-recall AUC.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• imbalanced-learn
• Pandas
• Matplotlib
• Seaborn
THEORY
Random Forest builds an ensemble of decision trees, each trained on a bootstrap sample with
random feature subsets. Final prediction uses majority voting (classification). This reduces variance
without increasing bias — overcoming the overfitting tendency of single trees.
Credit card fraud datasets are highly imbalanced (<1% fraud). SMOTE (Synthetic Minority
Oversampling Technique) generates synthetic fraud samples in feature space to balance classes.
Evaluation focuses on Recall (catching all fraud), Precision (avoiding false alarms), and PR-AUC
rather than accuracy. Domain: FinTech / Banking.
DATASET
Simulated credit card transactions – 10,000 samples, 97% legitimate / 3% fraud, 10 numerical
transaction features. Industry proxy for the Kaggle Credit Card Fraud dataset.
ALGORITHM / PROCEDURE
5. Train RandomForestClassifier(n_estimators=200)
PROGRAM / CODE
# 2. Split
X_tr,X_te,y_tr,y_te = train_test_split(X,y,test_size=0.2,random_state=42,stratify=y)
# 3. SMOTE
sm = SMOTE(random_state=42)
X_tr_b, y_tr_b = sm.fit_resample(X_tr, y_tr)
print(f"After SMOTE – train samples: {X_tr_b.shape[0]}")
# 4. Train
rf = RandomForestClassifier(n_estimators=200, max_depth=10,
class_weight='balanced', n_jobs=-1, random_state=42)
[Link](X_tr_b, y_tr_b)
y_pred = [Link](X_te)
y_proba = rf.predict_proba(X_te)[:,1]
# 5. Metrics
auc = roc_auc_score(y_te, y_proba)
apr = average_precision_score(y_te, y_proba)
print(f"ROC-AUC: {auc:.4f} | PR-AUC: {apr:.4f}")
print(classification_report(y_te, y_pred, target_names=['Legit','Fraud']))
# 6. Plots
fig, ax = [Link](1,2,figsize=(12,5))
fpr,tpr,_ = roc_curve(y_te, y_proba)
ax[0].plot(fpr,tpr,'b-',label=f'AUC={auc:.3f}'); ax[0].plot([0,1],[0,1],'r--')
ax[0].set(title='ROC Curve',xlabel='FPR',ylabel='TPR'); ax[0].legend()
cm = confusion_matrix(y_te, y_pred)
[Link](cm, annot=True, fmt='d', cmap='Reds',
xticklabels=['Legit','Fraud'], yticklabels=['Legit','Fraud'], ax=ax[1])
ax[1].set(title='Confusion Matrix')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 4 complete.")
EXPECTED OUTPUT
RESULT
Random Forest with SMOTE achieved ROC-AUC=0.98 on fraud detection. SMOTE effectively
addressed class imbalance. The model is suitable for real-time fraud screening.
VIVA QUESTIONS
• Q3. What is SMOTE and how does it differ from random oversampling?
• Q4. How do you select the optimal decision threshold for fraud detection?
• Q5. What are the limitations of Random Forest on very large datasets?
EXPERIMENT 5: Support Vector Machine – Digit Recognition
AIM: Train an SVM with RBF kernel to recognise handwritten digits using PCA-reduced features.
OBJECTIVE: Understand the kernel trick, margin maximisation, PCA for speed-up, and multi-class
SVM.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• NumPy
• Matplotlib
THEORY
SVM finds the hyperplane that maximises the margin between classes. For non-linearly separable
data, the kernel trick implicitly maps inputs to a higher-dimensional space: K(xᵢ,xⱼ) = φ(xᵢ)·φ(xⱼ). The
RBF kernel K(xᵢ,xⱼ)=exp(−γ||xᵢ−xⱼ||²) is effective for image data. Hyperparameters C (regularisation)
and γ (kernel width) are tuned via cross-validation.
PCA reduces the 64-dimensional pixel space to 40 principal components (retaining >96% variance),
dramatically reducing SVM training time. Multi-class SVM uses One-vs-One voting. Domain:
Computer Vision / Security / OCR.
DATASET
Digits dataset (sklearn) – 1,797 samples of 8×8 pixel handwritten digits (0–9), 64 features. Proxy for
MNIST in a resource-efficient setting.
ALGORITHM / PROCEDURE
PROGRAM / CODE
import numpy as np
import matplotlib; [Link]('Agg')
import [Link] as plt, seaborn as sns
from [Link] import load_digits
from sklearn.model_selection import train_test_split
from [Link] import StandardScaler
from [Link] import PCA
from [Link] import SVC
from [Link] import accuracy_score, classification_report, confusion_matrix
# 1. Load
data = load_digits()
X, y = [Link], [Link]
print(f"Samples: {[Link][0]} Features: {[Link][1]} Classes: {len([Link](y))}")
# 2. PCA
pca = PCA(n_components=40, whiten=True)
X_pca = pca.fit_transform(X)
print(f"Variance retained: {pca.explained_variance_ratio_.sum()*100:.2f}%")
# 4. Train SVM
svm = SVC(kernel='rbf', C=10, gamma=0.01, decision_function_shape='ovo')
[Link](X_tr, y_tr)
y_pred = [Link](X_te)
# 5. Metrics
acc = accuracy_score(y_te, y_pred)
print(f"Accuracy: {acc*100:.2f}%")
print(classification_report(y_te, y_pred))
# 6. Sample Predictions
fig, axes = [Link](2, 5, figsize=(12,5))
for i, ax in enumerate([Link]):
[Link]([Link][i], cmap='gray_r')
pred = [Link]([Link]([Link](X[i].reshape(1,-1))))[0]
c = 'green' if pred==y[i] else 'red'
ax.set_title(f'T:{y[i]} P:{pred}', color=c, fontsize=9); [Link]('off')
[Link]('SVM Digit Predictions', fontsize=12)
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 5 complete.")
EXPECTED OUTPUT
Variance retained by PCA(40): ~96.5% | SVM Accuracy: ~98.3%
PCA speeds training ~10×. RBF kernel correctly classifies nearly all digits. Confusion heatmap shows
rare confusion between 8/3 and 4/9.
RESULT
SVM with RBF kernel achieved 98.3% accuracy on digit recognition. PCA pre-processing reduced
training time significantly while preserving discriminative information.
VIVA QUESTIONS
• Q3. What is the difference between hard and soft margin SVM?
• Q5. How does One-vs-One differ from One-vs-Rest for multi-class SVM?
EXPERIMENT 6: K-Nearest Neighbours – Wine Quality Classification
AIM: Classify wine quality using KNN and analyse the effect of k on model accuracy.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• Pandas
• Matplotlib
• Seaborn
THEORY
KNN classifies a sample by majority vote among its k nearest neighbours in feature space, using
distance metrics such as Euclidean d(x,y)=√Σ(xᵢ−yᵢ)² or Manhattan d=Σ|xᵢ−yᵢ|. KNN is a lazy learner
(no explicit training phase) — the entire dataset is the model. Choosing k involves a bias-variance
trade-off: small k → high variance, large k → high bias.
Feature scaling is critical for KNN since unscaled features with larger ranges dominate distance
computations. Dimensionality is also a concern (curse of dimensionality). Domain: Food Science /
Quality Control / Beverage Industry.
DATASET
Wine Quality (UCI via sklearn load_wine) – 178 samples, 13 chemical features (alcohol, malic acid,
ash, etc.), 3 cultivar classes. Binary version used: quality ≥ 2 → "Good".
ALGORITHM / PROCEDURE
3. Train-test split
4. StandardScaler
PROGRAM / CODE
# 1. Load
data = load_wine(as_frame=True)
X, y = [Link], [Link]
print(f"Samples: {[Link][0]} | Classes: {list(data.target_names)}")
# 3. Find optimal k
cv_scores = []
k_range = range(1, 21)
for k in k_range:
knn = KNeighborsClassifier(n_neighbors=k, metric='euclidean')
scores = cross_val_score(knn, X_tr, y_tr, cv=5)
cv_scores.append([Link]())
best_k = k_range[[Link](cv_scores)]
print(f"Best k: {best_k} CV Accuracy: {max(cv_scores)*100:.2f}%")
# 5. Plots
fig, ax = [Link](1,2,figsize=(12,5))
ax[0].plot(k_range, cv_scores, 'bo-'); ax[0].axvline(best_k, color='r', ls='--')
ax[0].set(xlabel='k', ylabel='CV Accuracy', title='k vs Accuracy')
cm = confusion_matrix(y_te, y_pred)
[Link](cm, annot=True, fmt='d', cmap='Greens',
xticklabels=data.target_names, yticklabels=data.target_names, ax=ax[1])
ax[1].set(title='Confusion Matrix')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 6 complete.")
EXPECTED OUTPUT
RESULT
KNN achieved 97.2% accuracy with optimal k=7 on wine quality classification. Feature scaling was
essential — unscaled KNN accuracy dropped to ~74%.
VIVA QUESTIONS
AIM: Build a Multinomial Naïve Bayes spam classifier on SMS messages using TF-IDF features.
OBJECTIVE: Understand probabilistic classification, Bayes theorem, text vectorisation, and NLP
preprocessing.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• NLTK
• Pandas
• Matplotlib
• WordCloud
THEORY
Naïve Bayes applies Bayes theorem with the "naïve" conditional independence assumption:
P(y|x₁,...,xₙ) ∝ P(y)·Πᵢ P(xᵢ|y). For text classification, MultinomialNB uses word frequencies as
features. TF-IDF (Term Frequency – Inverse Document Frequency) weighting reduces the influence
of common words and amplifies discriminative terms.
DATASET
SMS Spam Collection (UCI) simulated – 5,574 messages: 86.6% ham, 13.4% spam. Labels: "ham" /
"spam". Features: raw message text, transformed to TF-IDF vectors.
ALGORITHM / PROCEDURE
PROGRAM / CODE
def clean(text):
text = [Link]()
text = [Link](r'[^a-z\s]', '', text)
return text
df['clean'] = df['message'].apply(clean)
X, y = df['clean'], (df['label']=='spam').astype(int)
print(f"Spam: {[Link]()} Ham: {(y==0).sum()}")
# 2. TF-IDF + Split
tfidf = TfidfVectorizer(max_features=5000, ngram_range=(1,2))
X_tfidf = tfidf.fit_transform(X)
X_tr,X_te,y_tr,y_te = train_test_split(X_tfidf, y, test_size=0.2, random_state=42)
# 3. Train
nb = MultinomialNB(alpha=0.1)
[Link](X_tr, y_tr)
y_pred = [Link](X_te)
# 4. Metrics
acc = accuracy_score(y_te, y_pred)
print(f"Accuracy: {acc*100:.2f}%")
print(classification_report(y_te, y_pred, target_names=['Ham','Spam']))
# 5. Confusion Matrix
fig, ax = [Link](figsize=(6,5))
cm = confusion_matrix(y_te, y_pred)
[Link](cm, annot=True, fmt='d', cmap='Oranges',
xticklabels=['Ham','Spam'], yticklabels=['Ham','Spam'], ax=ax)
[Link](xlabel='Predicted', ylabel='Actual', title='Spam Confusion Matrix')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 7 complete.")
EXPECTED OUTPUT
RESULT
Multinomial Naïve Bayes with TF-IDF features achieved ~98.5% accuracy on SMS spam detection.
The probabilistic model trained in milliseconds and scales to millions of messages.
VIVA QUESTIONS
• Q1. State and explain Bayes theorem in the context of spam filtering.
AIM: Segment retail customers into behavioural groups using K-Means clustering and the Elbow
method.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• Pandas
• Matplotlib
• Seaborn
THEORY
The Elbow method plots WCSS vs K and selects the K at the inflection point. The Silhouette score s(i)
= (b−a)/max(a,b) ∈ [−1,1] measures cohesion vs separation. RFM (Recency, Frequency, Monetary)
features are standard for retail segmentation. Domain: Retail / E-commerce / Marketing Analytics.
DATASET
Mall Customers dataset (simulated) – 200 customers with Annual Income ($k) and Spending Score
(1–100). Industry standard clustering benchmark dataset.
ALGORITHM / PROCEDURE
2. Standardise features
5. Fit KMeans(optimal K)
6. Assign cluster labels
PROGRAM / CODE
# 2. Scale
sc = StandardScaler()
X = sc.fit_transform(df)
# 3. Elbow + Silhouette
wcss, sil = [], []
K_range = range(2, 11)
for k in K_range:
km = KMeans(n_clusters=k, init='k-means++', random_state=42, n_init=10)
[Link](X)
[Link](km.inertia_)
[Link](silhouette_score(X, km.labels_))
best_k = K_range[[Link](sil)]
print(f"Best K (silhouette): {best_k} Score: {max(sil):.4f}")
# 4. Final model
km = KMeans(n_clusters=best_k, init='k-means++', random_state=42, n_init=10)
df['Cluster'] = km.fit_predict(X)
print([Link]('Cluster').mean().round(2))
# 5. Plots
fig, ax = [Link](1,2,figsize=(13,5))
ax[0].plot(list(K_range), wcss, 'bo-')
ax[0].set(xlabel='K', ylabel='WCSS', title='Elbow Method')
colours = [Link].Set1([Link](0,1,best_k))
for c in range(best_k):
sub = df[df['Cluster']==c]
ax[1].scatter(sub['Income'], sub['Spending'], label=f'Seg {c}', alpha=0.7)
cx = sc.inverse_transform(km.cluster_centers_)
ax[1].scatter(cx[:,0], cx[:,1], c='black', s=120, marker='X')
ax[1].set(xlabel='Annual Income ($k)', ylabel='Spending Score', title='Customer Segments')
ax[1].legend()
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 8 complete.")
EXPECTED OUTPUT
RESULT
K-Means segmented 200 customers into 5 actionable groups. The Elbow and Silhouette methods
both suggested K=5. Cluster profiles reveal distinct buying behaviours useful for targeted marketing.
VIVA QUESTIONS
• Q1. What is the Elbow method and why does WCSS decrease monotonically with K?
AIM: Apply DBSCAN to detect anomalous patterns in IoT sensor time-series data.
OBJECTIVE: Understand density-based clustering, epsilon and min_samples parameters, and noise
point identification.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• NumPy
• Pandas
• Matplotlib
THEORY
DBSCAN (Density-Based Spatial Clustering of Applications with Noise) defines clusters as dense
regions separated by sparser areas. A point is a core point if it has ≥ min_samples neighbours within
radius ε. Points reachable from a core point are assigned to its cluster; unreachable points are
labelled −1 (noise/anomaly).
Unlike K-Means, DBSCAN (1) does not require specifying K, (2) detects arbitrary shapes, and (3)
naturally identifies outliers. Parameter selection: ε via k-distance graph (elbow at sorted distances),
min_samples ≥ 2×dimensions. Domain: IoT / Industrial / Predictive Maintenance.
DATASET
Synthetic IoT sensor readings – 1,000 timesteps of temperature and vibration signals. ~5%
anomalous spikes injected (sudden surges or drops).
ALGORITHM / PROCEDURE
2. Standardise features
PROGRAM / CODE
# 2. Scale
sc = StandardScaler(); X_sc = sc.fit_transform(X)
# 4. DBSCAN
db = DBSCAN(eps=0.5, min_samples=5)
labels = db.fit_predict(X_sc)
detected = (labels == -1).sum()
print(f"DBSCAN detected anomalies: {detected}")
hit_rate = [Link]((labels==-1)[anomaly_idx]) * 100
print(f"Detection rate on injected anomalies: {hit_rate:.1f}%")
# 5. Plots
fig, ax = [Link](1,2,figsize=(13,5))
ax[0].plot(dist_sorted[:200], color='steelblue')
ax[0].axhline(0.5, color='red', ls='--', label='ε=0.5')
ax[0].set(title='k-Distance Graph (k=5)', xlabel='Points', ylabel='Distance')
ax[0].legend()
normal_mask = labels != -1
ax[1].scatter(X[normal_mask,0], X[normal_mask,1], c='steelblue', alpha=0.4, s=10,
label='Normal')
ax[1].scatter(X[~normal_mask,0], X[~normal_mask,1], c='red', s=30, marker='x',
label='Anomaly')
ax[1].set(xlabel='Temperature (°C)', ylabel='Vibration', title='DBSCAN Anomaly Detection')
ax[1].legend()
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 9 complete.")
EXPECTED OUTPUT
RESULT
DBSCAN detected ~90% of injected IoT anomalies without needing K specified. The density-based
approach successfully isolated rare sensor fault patterns.
VIVA QUESTIONS
AIM: Compare PCA (unsupervised) and LDA (supervised) for dimensionality reduction on the
Olivetti Faces dataset.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• NumPy
• Matplotlib
THEORY
PCA finds orthogonal components that maximise explained variance. Covariance matrix C=XᵀX/n is
decomposed: eigenvectors form the principal directions. Top K eigenvectors (eigenfaces) capture
most information. LDA (Linear Discriminant Analysis) finds projections maximising between-class
scatter while minimising within-class scatter, making it supervised and directly optimised for
classification.
In face recognition, PCA reduces 4096-dim face images to ~50 eigenface components. LDA further
separates identities. Reconstruction MSE quantifies information loss. Domain: Biometrics / Security /
Healthcare Imaging.
DATASET
Olivetti Faces (sklearn) – 400 grayscale 64×64 face images of 40 subjects (10 images each), 4096
features per image.
ALGORITHM / PROCEDURE
8. Evaluate accuracy
PROGRAM / CODE
import numpy as np
import matplotlib; [Link]('Agg')
import [Link] as plt
from [Link] import fetch_olivetti_faces
from sklearn.model_selection import train_test_split
from [Link] import PCA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from [Link] import KNeighborsClassifier
from [Link] import accuracy_score
# 1. Load
data = fetch_olivetti_faces(shuffle=True, random_state=42)
X, y = [Link], [Link]
print(f"Faces: {[Link]} | Subjects: {[Link](y).size}")
# 2. PCA
n_comp = 50
pca = PCA(n_components=n_comp, whiten=True)
X_tr_pca = pca.fit_transform(X_tr)
X_te_pca = [Link](X_te)
print(f"PCA variance retained ({n_comp} comps):
{pca.explained_variance_ratio_.sum()*100:.1f}%")
# 3. PCA + KNN
knn_pca = KNeighborsClassifier(n_neighbors=1)
knn_pca.fit(X_tr_pca, y_tr)
acc_pca = accuracy_score(y_te, knn_pca.predict(X_te_pca))
print(f"PCA + KNN accuracy: {acc_pca*100:.2f}%")
# 4. PCA + LDA
lda = LDA()
X_tr_lda = lda.fit_transform(X_tr_pca, y_tr)
X_te_lda = [Link](X_te_pca)
knn_lda = KNeighborsClassifier(n_neighbors=1)
knn_lda.fit(X_tr_lda, y_tr)
acc_lda = accuracy_score(y_te, knn_lda.predict(X_te_lda))
print(f"PCA + LDA + KNN accuracy: {acc_lda*100:.2f}%")
# 5. Visualise eigenfaces
fig, axes = [Link](2,5,figsize=(12,5))
for i, ax in enumerate([Link]):
[Link](pca.components_[i].reshape(64,64), cmap='bone')
ax.set_title(f'Eigenface {i+1}', fontsize=8); [Link]('off')
[Link]('Top 10 Eigenfaces (PCA Components)')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 10 complete.")
EXPECTED OUTPUT
PCA (50 comps) retains ~90% variance | PCA+KNN: ~91% | PCA+LDA+KNN: ~96%
Eigenfaces capture lighting/shape patterns. LDA improves identity separation over pure PCA.
RESULT
VIVA QUESTIONS
AIM: Predict employee attrition using XGBoost and interpret predictions with SHAP values.
OBJECTIVE: Implement gradient boosting, understand learning rate and tree depth, use SHAP for
explainability (XAI).
SOFTWARE REQUIRED
• Python 3.10+
• XGBoost
• SHAP
• Scikit-learn
• Pandas
• Matplotlib
THEORY
SHAP (SHapley Additive exPlanations) assigns each feature a contribution to a specific prediction
based on cooperative game theory. SHAP values enable auditable, fair AI — critical for HR decisions.
Domain: HR Analytics / People Analytics.
DATASET
ALGORITHM / PROCEDURE
4. Train-test split
5. Fit XGBClassifier
PROGRAM / CODE
enc = OrdinalEncoder()
df['OverTime'] = enc.fit_transform(df[['OverTime']])
X = [Link]('Attrition', axis=1); y = df['Attrition']
X_tr,X_te,y_tr,y_te = train_test_split(X,y,test_size=0.2,random_state=42,stratify=y)
# 2. Train XGBoost
ratio = (y_tr==0).sum()/(y_tr==1).sum()
model = [Link](n_estimators=200, max_depth=4, learning_rate=0.05,
scale_pos_weight=ratio, use_label_encoder=False, eval_metric='logloss',
random_state=42)
[Link](X_tr, y_tr, eval_set=[(X_te,y_te)], verbose=False)
y_pred = [Link](X_te)
y_proba = model.predict_proba(X_te)[:,1]
# 3. Metrics
print(f"Accuracy: {accuracy_score(y_te,y_pred)*100:.2f}%")
print(f"AUC: {roc_auc_score(y_te,y_proba):.4f}")
print(classification_report(y_te, y_pred, target_names=['Stay','Leave']))
# 4. Feature Importance
xgb.plot_importance(model, max_num_features=10, importance_type='gain',
title='XGBoost Feature Importance (Gain)')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 11 complete.")
EXPECTED OUTPUT
RESULT
XGBoost achieved AUC=0.95 on employee attrition prediction. Feature importance shows job
satisfaction and overtime as dominant factors. XGBoost's built-in regularisation prevents overfitting.
VIVA QUESTIONS
• Q3. Explain SHAP values and why they are preferred over feature importance.
AIM: Apply L1 (Lasso) and L2 (Ridge) regularisation to predict diabetes progression and perform
feature selection.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• Pandas
• Matplotlib
• NumPy
THEORY
Ridge Regression adds L2 penalty: J=MSE+λΣβᵢ². It shrinks coefficients towards zero but rarely
makes them exactly zero. Lasso adds L1 penalty: J=MSE+λΣ|βᵢ|, which can zero out coefficients
completely, performing automatic feature selection. ElasticNet combines both penalties.
λ (alpha) controls regularisation strength: large λ → more shrinkage → higher bias, lower variance.
Optimal α is found via cross-validation (RidgeCV/LassoCV). Domain: Healthcare / Epidemiology /
Pharmaceutical Research.
DATASET
Diabetes dataset (sklearn) – 442 patients, 10 physiological features (age, sex, BMI, blood pressure, 6
serum measurements). Target: disease progression score one year later.
ALGORITHM / PROCEDURE
2. Standardise features
PROGRAM / CODE
# 1. Load
data = load_diabetes(as_frame=True)
X, y = [Link], [Link]
print(f"Shape: {[Link]} | Target range: {[Link]():.1f}–{[Link]():.1f}")
# 2. Models
alphas = [Link](-3, 2, 100)
models = {
'Linear': LinearRegression(),
'Ridge': RidgeCV(alphas=alphas, cv=5),
'Lasso': LassoCV(alphas=alphas, cv=5, max_iter=5000),
}
results = {}
for name, m in [Link]():
[Link](X_tr, y_tr)
yp = [Link](X_te)
rmse = [Link](mean_squared_error(y_te, yp))
r2 = r2_score(y_te, yp)
results[name] = {'RMSE': rmse, 'R2': r2}
print(f"{name:8s} RMSE={rmse:.3f} R2={r2:.4f}")
# 4. Plot coefficients
fig, ax = [Link](1,2,figsize=(13,5))
coef.sort_values().[Link](ax=ax[0], color=['red' if v<0 else 'steelblue' for v in
coef.sort_values()])
ax[0].set(title='Lasso Coefficients', xlabel='Value')
ax[0].axvline(0, color='black', lw=0.8)
ridge = models['Ridge']
y_pred_ridge = [Link](X_te)
ax[1].scatter(y_te, y_pred_ridge, alpha=0.5, c='steelblue')
ax[1].plot([y_te.min(),y_te.max()],[y_te.min(),y_te.max()],'r--')
ax[1].set(xlabel='Actual', ylabel='Predicted', title=f'Ridge
R2={results["Ridge"]["R2"]:.3f}')
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 12 complete.")
EXPECTED OUTPUT
RESULT
Lasso regularisation identified 7 key predictors of diabetes progression, zeroing out 3 noise features.
Ridge provided marginal improvement in RMSE. Both prevent overfitting better than unregularised
regression.
VIVA QUESTIONS
AIM: Systematically optimise hyperparameters of a Random Forest classifier using Grid and
Randomised search.
OBJECTIVE: Understand cross-validation, search strategies, learning curves, and avoid data leakage
during tuning.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• Pandas
• Matplotlib
• NumPy
THEORY
Learning curves plot train/validation score vs training size — revealing underfitting (both scores
low) or overfitting (large gap). Validation curves plot score vs a single hyperparameter. Domain:
General ML best practices; critical for production model deployment.
DATASET
Iris dataset (sklearn) – 150 samples, 4 features, 3 flower species. Used to demonstrate tuning on a
classification task.
ALGORITHM / PROCEDURE
1. Load dataset
PROGRAM / CODE
# 1. Data
data = load_iris(as_frame=True)
X, y = [Link], [Link]
X_tr,X_te,y_tr,y_te = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 2. GridSearchCV
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 3, 5, 10],
'min_samples_split':[2, 5, 10],
}
t0 = [Link]()
gs = GridSearchCV(RandomForestClassifier(random_state=42), param_grid, cv=5,
scoring='accuracy', n_jobs=-1)
[Link](X_tr, y_tr)
t_gs = [Link]()-t0
print(f"GridSearch – Best: {gs.best_params_} CV={gs.best_score_*100:.2f}%
Time={t_gs:.1f}s")
# 3. RandomizedSearchCV
from [Link] import randint
param_dist = {'n_estimators': randint(50,300), 'max_depth': [None,3,5,10,15],
'min_samples_split': randint(2,15)}
t0 = [Link]()
rs = RandomizedSearchCV(RandomForestClassifier(random_state=42), param_dist,
n_iter=20, cv=5, scoring='accuracy', n_jobs=-1, random_state=42)
[Link](X_tr, y_tr)
t_rs = [Link]()-t0
print(f"RandomSearch – Best: {rs.best_params_} CV={rs.best_score_*100:.2f}%
Time={t_rs:.1f}s")
# 4. Test accuracy
best_model = gs.best_estimator_
print(f"Test Accuracy: {accuracy_score(y_te, best_model.predict(X_te))*100:.2f}%")
# 5. Learning Curves
train_sizes, tr_scores, val_scores = learning_curve(
best_model, X, y, cv=5, train_sizes=[Link](0.1,1.0,8), scoring='accuracy', n_jobs=-1)
fig, ax = [Link](figsize=(8,5))
[Link](train_sizes, tr_scores.mean(1), 'b-o', label='Train')
ax.fill_between(train_sizes, tr_scores.mean(1)-tr_scores.std(1),
tr_scores.mean(1)+tr_scores.std(1), alpha=0.1, color='blue')
[Link](train_sizes, val_scores.mean(1), 'r-o', label='Validation')
ax.fill_between(train_sizes, val_scores.mean(1)-val_scores.std(1),
val_scores.mean(1)+val_scores.std(1), alpha=0.1, color='red')
[Link](xlabel='Training Samples', ylabel='Accuracy', title='Learning Curves')
[Link](); plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 13 complete.")
EXPECTED OUTPUT
GridSearch: Best params found in ~2s | CV=~97.5% | RandomSearch: Similar result in ~0.4s
Learning curves show model converges after ~100 samples; train/val gap narrows — no overfitting.
RESULT
VIVA QUESTIONS
• Q2. Why should hyperparameter tuning use cross-validation and not the test set?
• Q3. What does a learning curve with a large train-validation gap indicate?
• Q5. What is Bayesian hyperparameter optimisation and how does it differ from random
search?
EXPERIMENT 14: Time Series Forecasting – Stock Price Prediction
(ARIMA)
AIM: Forecast stock closing prices using ARIMA and compare with a simple LSTM baseline.
SOFTWARE REQUIRED
• Python 3.10+
• statsmodels
• Pandas
• NumPy
• Matplotlib
• Scikit-learn
THEORY
ARIMA (AutoRegressive Integrated Moving Average) models time series as: φ(B)(1−B)ᵈXₜ = θ(B)εₜ
where p=AR order, d=differencing for stationarity, q=MA order. Stationarity (constant
mean/variance) is tested with the Augmented Dickey-Fuller (ADF) test. ACF plots identify q (MA lag),
PACF plots identify p (AR lag). AIC/BIC guide model order selection.
Evaluation uses MAE, RMSE, and MAPE (Mean Absolute Percentage Error). For financial forecasting,
directional accuracy (whether predicted direction matches actual) is also important. Domain:
Finance / FinTech / Algorithmic Trading.
DATASET
Synthetic stock price series – 500 trading days with trend, seasonality, and noise. Mimics OHLCV data
from equity markets.
ALGORITHM / PROCEDURE
5. Fit ARIMA(p,d,q)
PROGRAM / CODE
# 2. ADF test
adf_result = adfuller(ts)
print(f"ADF Statistic: {adf_result[0]:.4f} p-value: {adf_result[1]:.4f}")
print("Stationary" if adf_result[1]<0.05 else "Non-stationary — differencing required")
# 3. Train-test split
train, test = ts[:-30], ts[-30:]
# 4. Fit ARIMA
model = ARIMA(train, order=(2,1,2))
fitted = [Link]()
print([Link]())
# 5. Forecast
fc = [Link](steps=30)
mae = mean_absolute_error(test, fc)
rmse = [Link](mean_squared_error(test, fc))
mape = [Link]([Link](([Link] - [Link])/[Link]))*100
print(f"MAE={mae:.3f} RMSE={rmse:.3f} MAPE={mape:.2f}%")
# 6. Plot
fig, ax = [Link](figsize=(13,5))
[Link](train[-60:], label='Train (last 60 days)', color='steelblue')
[Link](test, label='Actual', color='green')
[Link]([Link], fc, label='ARIMA Forecast', color='red', ls='--')
[Link](title=f'ARIMA(2,1,2) Stock Forecast | MAPE={mape:.2f}%',
xlabel='Date', ylabel='Price ($)')
[Link](); plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 14 complete.")
EXPECTED OUTPUT
RESULT
ARIMA(2,1,2) achieved MAPE=1.5% on 30-day stock price forecasting. First differencing successfully
removed non-stationarity. The model is suitable for short-horizon forecasting.
VIVA QUESTIONS
• Q2. How do ACF and PACF plots guide ARIMA order selection?
• Q4. What are the limitations of ARIMA for stock price forecasting?
AIM: Detect network intrusion attacks using Isolation Forest and One-Class SVM on simulated
network traffic.
SOFTWARE REQUIRED
• Python 3.10+
• Scikit-learn
• Pandas
• NumPy
• Matplotlib
• Seaborn
THEORY
Isolation Forest isolates anomalies by randomly partitioning data; anomalies require fewer splits to
isolate, yielding shorter path lengths. Anomaly score = 2^(-avg_path/c(n)) where c(n) is the average
path for a random binary tree. Contamination=0.05 sets the threshold at the 5th percentile of scores.
One-Class SVM learns a decision boundary enclosing normal data in a high-dimensional kernel space;
points outside are anomalies. Isolation Forest scales better to large datasets (O(n log n)) while One-
Class SVM is more effective with lower-dimensional data. Domain: Cybersecurity / Network Security
/ SOC Operations.
DATASET
Simulated NSL-KDD-style network traffic – 5,000 connections: 95% normal, 5% attack (DoS, Probe,
R2L types). 15 numeric traffic features.
ALGORITHM / PROCEDURE
2. Standardise features
3. Fit IsolationForest(contamination=0.05)
PROGRAM / CODE
# 2. Isolation Forest
ifor = IsolationForest(n_estimators=100, contamination=0.05, random_state=42, n_jobs=-1)
y_if = ifor.fit_predict(X_sc)
print("Isolation Forest:")
print(classification_report(y_true, y_if, target_names=['Attack','Normal']))
# 3. One-Class SVM
ocsvm = OneClassSVM(kernel='rbf', gamma='scale', nu=0.05)
[Link](X_sc[y_true==1])
y_oc = [Link](X_sc)
print("One-Class SVM:")
print(classification_report(y_true, y_oc, target_names=['Attack','Normal']))
# 4. PCA 2D visualisation
pca2 = PCA(n_components=2)
X_2d = pca2.fit_transform(X_sc)
fig, ax = [Link](1,2,figsize=(13,5))
for label, title, y_pred in [(y_if,'Isolation Forest'),(y_oc,'One-Class SVM')]:
pass
for i,(y_pred,ttl) in enumerate([(y_if,'Isolation Forest'),(y_oc,'One-Class SVM')]):
ax[i].scatter(X_2d[y_pred==1,0], X_2d[y_pred==1,1], c='steelblue', alpha=0.3, s=8,
label='Normal')
ax[i].scatter(X_2d[y_pred==-1,0], X_2d[y_pred==-1,1], c='red', s=20, marker='x',
label='Anomaly')
ax[i].set(title=ttl, xlabel='PC1', ylabel='PC2'); ax[i].legend()
plt.tight_layout(); [Link]('[Link]', dpi=120)
print("Experiment 15 complete.")
EXPECTED OUTPUT
Isolation Forest: Attack Recall ~88%, Precision ~74% | One-Class SVM: Attack Recall ~85%,
Precision ~79%
Both algorithms successfully detect most intrusions. False positive rate ~5% aligns with
contamination parameter.
RESULT
Isolation Forest detected 88% of network attacks with no labelled attack training data. One-Class
SVM achieved slightly higher precision. Both are viable for real-time network intrusion detection.
VIVA QUESTIONS
• Q2. What is the contamination parameter and how should you set it?
• Q4. What is the difference between supervised and unsupervised anomaly detection?