Machine Learning 📂 Unsupervised Learning · 1 of 3 264 min read

Unsupervised Learning

Discover how machines find hidden patterns in unlabelled data. This tutorial covers clustering, dimensionality reduction, and anomaly detection with real-world stories, SVG diagrams, and colour-coded Python examples.

🌐
SectionSection第 01 节

The Story That Explains Unsupervised Learningवह कहानी जो Unsupervised Learning समझाती है一个解释无监督学习的故事

The New Librarian and 10,000 Unlabelled Booksनया Librarian और 10,000 बिना label की किताबें新来的图书管理员和 10,000 本没有标签的书
Imagine you walk into a chaotic library. Ten thousand books are piled everywhere — no shelves, no categories, no catalogue system. Your job: organise them.

Nobody tells you how many categories to create. Nobody gives you a labelling guide. You just start reading the covers and noticing patterns. Books about war and politics drift together. Recipe books cluster near the kitchen corner. Science and maths titles move to the back wall. You invent the organisation system purely from the content itself.

That is exactly what unsupervised learning does. The algorithm gets raw data with no labels and must discover the hidden structure on its own — grouping it, compressing it, or flagging what does not belong.
कल्पना करें कि आप एक अस्त-व्यस्त library में जाते हैं। दस हज़ार किताबें हर जगह ढेर में पड़ी हैं — न shelves, न categories, न catalogue system. आपका काम: इन्हें व्यवस्थित करना।

कोई नहीं बताता कि कितनी categories बनानी हैं। कोई labelling guide नहीं देता। आप बस covers पढ़ना और patterns देखना शुरू करते हैं। युद्ध और राजनीति की किताबें एक साथ आ जाती हैं। खाना बनाने की किताबें रसोई वाले कोने के पास जमा होती हैं। Science और गणित की किताबें पीछे की दीवार की ओर चली जाती हैं। आप व्यवस्था का तरीका सिर्फ़ किताबों की सामग्री से बनाते हैं।

Unsupervised learning ठीक यही करता है। Algorithm को बिना labels का कच्चा data मिलता है और उसे छिपा ढाँचा खुद खोजना होता है — उसे groups में बाँटना, छोटा (compress) करना, या जो मेल नहीं खाता उसे पकड़ना।
想象你走进一座乱糟糟的图书馆。一万本书到处堆着 —— 没有书架、没有分类、没有目录系统。你的任务:把它们整理好。

没人告诉你该分几类,也没人给你分类指南。你只能开始看封面、发现规律。 关于战争和政治的书慢慢聚到一起;菜谱集中到厨房角落;科学和数学书搬到后墙。你完全根据书的内容本身发明出一套整理方法。

这正是无监督学习(unsupervised learning)所做的事。算法拿到没有标签的原始数据,必须自己发现其中隐藏的结构 —— 把数据分组、压缩,或者找出不属于这里的东西。
🖼️ SUPERVISED vs UNSUPERVISED — THE SAME DATA, WITH AND WITHOUT LABELSSUPERVISED vs UNSUPERVISED — वही DATA, LABELS के साथ और बिना有监督 vs 无监督 — 同样的数据,有标签与无标签
Supervised vs unsupervised SUPERVISED: data comes WITH labelsSUPERVISED: data के साथ labels हैं有监督:数据带有标签 catdogbird Learn to predict the labelLabel predict करना सीखें学习预测标签 UNSUPERVISED: NO labels at allUNSUPERVISED: कोई label नहीं无监督:完全没有标签 ? ? ? Find the groups yourselfGroups खुद खोजें自己找出分组

In supervised learning, you give the model thousands of emails already labelled Spam / Not Spam, and it learns to predict the label. In unsupervised learning, you give it the same emails with no labels at all and ask: "What natural groups exist here?"

Supervised learning में आप model को हज़ारों emails देते हैं जिन पर पहले से Spam / Not Spam का label है, और वह label predict करना सीखता है। Unsupervised learning में आप वही emails बिना किसी label के देते हैं और पूछते हैं: "यहाँ कौन से स्वाभाविक groups हैं?"

在有监督学习中,你给模型成千上万封已标好垃圾邮件 / 非垃圾邮件的邮件,它学习预测标签。 在无监督学习中,你给它同样的邮件,但完全没有标签,然后问:"这里存在哪些自然的分组?"

💡
The Core Ideaमुख्य विचार核心思想

Unsupervised learning finds hidden structure in unlabelled data. There is no teacher and no correct answer — just patterns, groups, compressions and anomalies discovered from the data itself. Because there is no answer key, checking the result is the hard part: you always need plots, statistics and domain knowledge to decide whether what the algorithm found is real and useful.

Unsupervised learning बिना label वाले data में छिपा ढाँचा खोजता है। कोई teacher नहीं, कोई सही जवाब नहीं — सिर्फ़ patterns, groups, compressions और anomalies जो data से ही मिलते हैं। क्योंकि कोई answer key नहीं है, इसलिए नतीजे को जाँचना सबसे मुश्किल हिस्सा है: यह तय करने के लिए कि algorithm ने जो पाया वह असली और काम का है, आपको हमेशा plots, statistics और domain की समझ चाहिए।

无监督学习在无标签数据中寻找隐藏的结构。没有老师,也没有标准答案 —— 只有从数据本身发现的规律、分组、压缩和异常。 正因为没有答案,检验结果才是最难的部分:你总是需要图表、统计和领域知识来判断算法找到的东西是否真实、是否有用。


SectionSection第 02 节

The Three Families of Unsupervised LearningUnsupervised Learning के तीन परिवार无监督学习的三大家族

Unsupervised methods fall into three broad families. Each one answers a different question about your data.

Unsupervised तरीके तीन बड़े परिवारों में आते हैं। हर एक आपके data के बारे में एक अलग सवाल का जवाब देता है।

无监督方法分为三大家族,每一类回答关于数据的不同问题。

🔵
ClusteringClustering聚类
Group similar pointsमिलते-जुलते points को group करें把相似的点分组
"Which things belong together?"

K-Means, DBSCAN and hierarchical clustering split your data into natural groups — customer segments, topic clusters, gene expression profiles.
"कौन सी चीज़ें साथ हैं?"

K-Means, DBSCAN और hierarchical clustering आपके data को स्वाभाविक groups में बाँटते हैं — customer segments, topic clusters, gene expression profiles.
"哪些东西属于一类?"

K-Means、DBSCAN 和层次聚类把数据分成自然的组 —— 客户细分、主题聚类、基因表达谱。
📐
Dimensionality ReductionDimensionality Reduction降维
Compress & visualiseCompress और visualise压缩与可视化
"What is the essence of this data?"

PCA, t-SNE and autoencoders turn hundreds of features into a few meaningful ones — for plots, denoising or feature engineering.
"इस data का सार क्या है?"

PCA, t-SNE और autoencoders सैकड़ों features को कुछ सार्थक features में बदलते हैं — plots, denoising या feature engineering के लिए।
"这些数据的本质是什么?"

PCA、t-SNE 和自编码器把成百上千个特征变成少数几个有意义的特征 —— 用于作图、去噪或特征工程。
🚨
Anomaly DetectionAnomaly Detection异常检测
Find the outliersOutliers खोजें找出离群点
"What does not belong here?"

Isolation Forest and One-Class SVM learn what "normal" data looks like and flag anything outside it — fraud, machine faults, network intrusions.
"यहाँ क्या मेल नहीं खाता?"

Isolation Forest और One-Class SVM सीखते हैं कि "सामान्य" data कैसा दिखता है और उससे बाहर की हर चीज़ पकड़ते हैं — fraud, machine faults, network intrusions.
"什么东西不属于这里?"

Isolation Forest 和 One-Class SVM 学习"正常"数据的样子,并标出任何不符合的东西 —— 欺诈、机器故障、网络入侵。

SectionSection第 03 节

Clustering — K-Means: The City PlannerClustering — K-Means: शहर का Planner聚类 — K-Means:城市规划师

Opening Five Fire Stations in a New Cityनए शहर में पाँच Fire Stations खोलना在新城市开设五个消防站
A city planner must open exactly 5 fire stations so every neighbourhood is reached quickly. Nobody has decided which neighbourhood belongs to which station. The planner's rule: each station serves the houses closest to it, and each station should sit at the centre of the houses it serves.

The planner starts by dropping 5 pins at random on the map. Every house joins its nearest pin. Then each pin moves to the average location of its houses. Houses re-join. Pins move again. After a few rounds the pins stop moving — they have found the natural centres. That repeated "assign → move → reassign" loop is K-Means.
एक city planner को ठीक 5 fire stations खोलने हैं ताकि हर मोहल्ले तक जल्दी पहुँचा जा सके। किसी ने तय नहीं किया कि कौन सा मोहल्ला किस station का है। Planner का नियम: हर station अपने सबसे पास के घरों की सेवा करे, और हर station अपने घरों के बीचों-बीच हो।

Planner नक्शे पर random जगह 5 pins लगाकर शुरू करता है। हर घर अपने सबसे पास वाले pin से जुड़ता है। फिर हर pin अपने घरों की औसत जगह पर खिसकता है। घर फिर से जुड़ते हैं। Pins फिर खिसकते हैं। कुछ rounds बाद pins रुक जाते हैं — उन्हें स्वाभाविक केंद्र मिल गए। यही बार-बार चलने वाला "assign → move → reassign" loop ही K-Means है।
一位城市规划师必须正好开设 5 个消防站,让每个街区都能被快速覆盖。没人规定哪个街区归哪个消防站。 规划师的规则:每个消防站服务离它最近的房子,并且每个消防站应位于它所服务房子的中心。

规划师先在地图上随机插 5 个图钉。每栋房子归到最近的图钉。然后每个图钉移到它所属房子的平均位置。房子重新归属,图钉再移动。 几轮之后图钉不再移动 —— 它们找到了自然的中心。这个反复的"分配 → 移动 → 再分配"循环就是 K-Means。
🔁 K-Means Algorithm — Step by StepK-Means Algorithm — Step by StepK-Means 算法 — 逐步讲解
Step 1
Choose K, the number of clusters. Place K centroids (scikit-learn uses the smarter k-means++ start by default). K चुनें, यानी clusters की संख्या। K centroids रखें (scikit-learn default में समझदार k-means++ शुरुआत इस्तेमाल करता है)। 选择簇的数量 K,放置 K 个质心(scikit-learn 默认使用更聪明的 k-means++ 初始化)。
Step 2
Assign: each point joins the nearest centroid (Euclidean distance). Assign: हर point सबसे पास वाले centroid से जुड़ता है (Euclidean distance)। 分配:每个点归入最近的质心(欧氏距离)。
Step 3
Update: each centroid moves to the mean of the points assigned to it. Update: हर centroid अपने points के mean पर चला जाता है। 更新:每个质心移到所属点的均值处。
Step 4
Repeat steps 2–3 until no point changes its cluster (convergence). Steps 2–3 तब तक दोहराएँ जब तक कोई point अपना cluster न बदले (convergence)। 重复第 2–3 步,直到没有点再改变所属簇(收敛)。
Resultनतीजा结果
Each point belongs to exactly one cluster. The centroid is the cluster's representative point. हर point ठीक एक cluster में है। Centroid उस cluster का प्रतिनिधि point है। 每个点恰好属于一个簇,质心是该簇的代表点。
LIVELIVE互动Watch K-Means RunK-Means को चलते देखें观看 K-Means 运行

Coloured regions show which centroid each area belongs to. Dashed trails show how the centroids move. Change K, or press "New random start" to see that a different start can end in a worse result.

रंगीन हिस्से दिखाते हैं कि हर इलाका किस centroid का है। Dashed निशान दिखाते हैं कि centroids कैसे चलते हैं। K बदलें, या "नई random शुरुआत" दबाएँ और देखें कि अलग शुरुआत ख़राब नतीजे पर भी ख़त्म हो सकती है।

彩色区域表示每块区域属于哪个质心,虚线轨迹显示质心如何移动。改变 K,或点击"重新随机初始化",看看不同的起点可能得到更差的结果。

The quantity K-Means minimises is called inertia: the sum of squared distances from each point to its centroid. Lower inertia means tighter clusters. Every assign step and every move step can only lower it, so K-Means always stops — but possibly at a local minimum, which is why it is run several times (n_init) from different starts.

K-Means जिस मात्रा को घटाता है उसे inertia कहते हैं: हर point से उसके centroid तक की दूरी² का जोड़। कम inertia मतलब कसे हुए clusters. हर assign step और हर move step इसे सिर्फ़ घटा सकता है, इसलिए K-Means हमेशा रुकता है — पर शायद किसी local minimum पर, इसीलिए इसे अलग-अलग शुरुआतों से कई बार चलाया जाता है (n_init)।

K-Means 最小化的量叫作惯性(inertia):每个点到其质心距离平方之和。惯性越低,簇越紧凑。每次分配和每次移动都只会让它下降,所以 K-Means 一定会停止 —— 但可能停在局部最小值,这就是为什么要从不同起点运行多次(n_init)。

Inertia (WCSS)Inertia (WCSS)惯性(WCSS)
Σ ||xᵢ − μₖ||²
Sum of squared distances from each point to its assigned centroid. K-Means minimises this.
हर point से उसके centroid तक की दूरी² का जोड़। K-Means इसे घटाता है।
每个点到其所属质心距离平方之和。K-Means 最小化它。
Centroid UpdateCentroid Update质心更新
μₖ = (1/|Cₖ|) Σ xᵢ
New centroid = mean of all points assigned to cluster k.
नया centroid = cluster k के सभी points का mean.
新质心 = 簇 k 中所有点的均值。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_blobs

# ── Generate synthetic customer data ────────────────────
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.8, random_state=42)

# ── Scale (K-Means IS distance-sensitive) ───────────────
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# ── Elbow method — find optimal K ───────────────────────
inertias = []
K_range = range(1, 11)
for k in K_range:
    km = KMeans(n_clusters=k, n_init=10, random_state=42)
    km.fit(X_scaled)
    inertias.append(km.inertia_)
print("Inertia for K=1..10:", [round(v, 1) for v in inertias])

# ── Fit final model ──────────────────────────────────────
kmeans = KMeans(n_clusters=4, n_init=20, random_state=42)
labels = kmeans.fit_predict(X_scaled)

print(f"Inertia:         {kmeans.inertia_:.2f}")
print(f"Cluster centres: {kmeans.cluster_centers_.shape}")
print(f"Cluster sizes:   {np.bincount(labels)}")
OUTPUT
Inertia for K=1..10: [600.0, 312.7, 66.7, 11.3, 10.1, 9.0, 7.9, 7.0, 6.4, 5.7] Inertia: 11.32 Cluster centres: (4, 2) Cluster sizes: [75 75 75 75]
⚠️
K-Means Weaknesses — Know These Before Using ItK-Means की कमज़ोरियाँ — इस्तेमाल से पहले जानेंK-Means 的弱点 — 使用前必须了解

K-Means needs K in advance, assumes clusters are roughly round and of similar size, is sensitive to outliers (one extreme point can pull a centroid far away), and always returns K clusters even if the data has no real groups. Scale your features first: K-Means uses distances, so a feature in large units would dominate.

K-Means को K पहले से चाहिए, यह मानता है कि clusters लगभग गोल और एक जैसे आकार के हैं, यह outliers से प्रभावित होता है (एक बहुत अलग point centroid को दूर खींच सकता है), और data में असली groups न हों तब भी हमेशा K clusters लौटाता है। पहले features scale करें: K-Means दूरियाँ इस्तेमाल करता है, इसलिए बड़ी units वाला feature हावी हो जाएगा।

K-Means 需要事先指定 K,假设簇大致是圆形且大小相近,对离群点敏感(一个极端点就能把质心拉得很远), 而且即使数据里没有真正的分组,它也总会返回 K 个簇。请先缩放特征:K-Means 依赖距离,单位大的特征会占主导。


SectionSection第 04 节

Choosing K — The Elbow Method and Silhouette ScoreK चुनना — Elbow Method और Silhouette Score如何选择 K — 肘部法与轮廓系数

Picking K is one of the hardest decisions in K-Means. Two tools help: the elbow in the inertia curve, and the silhouette score.

K चुनना K-Means का सबसे मुश्किल फ़ैसला है। दो औज़ार मदद करते हैं: inertia curve का elbow, और silhouette score.

选择 K 是 K-Means 中最难的决定之一。有两个工具可以帮忙:惯性曲线的肘部,以及轮廓系数(silhouette score)。

LIVELIVE互动Choosing K — Elbow and Silhouette, Computed LiveK चुनना — Elbow और Silhouette, live निकाले गए选择 K — 实时计算肘部与轮廓系数

Pick how many blobs the data really has. For every K from 1 to 8 the page runs K-Means (best of 4 starts) and computes inertia (blue line) and silhouette (green bars). Click a K to see that clustering.

चुनें कि data में असल में कितने blobs हैं। 1 से 8 तक हर K के लिए page K-Means चलाता है (4 शुरुआतों में सबसे अच्छा) और inertia (नीली line) व silhouette (हरी bars) निकालता है। किसी K पर click करके वह clustering देखें।

选择数据中真实的团数。页面对 K = 1 到 8 分别运行 K-Means(取 4 次初始化中最好的),计算惯性(蓝线)和轮廓系数(绿柱)。点击某个 K 查看对应的聚类结果。

Silhouette ScoreSilhouette Score轮廓系数
s = (b − a) / max(a, b)
a = mean distance to points in the same cluster. b = mean distance to the nearest other cluster. Range −1 to +1; higher is better.
a = अपने cluster के points से औसत दूरी। b = सबसे पास वाले दूसरे cluster से औसत दूरी। Range −1 से +1; ज़्यादा बेहतर।
a = 到同簇点的平均距离;b = 到最近的其他簇的平均距离。范围 −1 到 +1,越高越好。
Interpret ScoreScore पढ़ना如何解读
+1 → 0 → −1
+1 = well separated. 0 = on the border between clusters. Negative = the point is probably in the wrong cluster.
+1 = अच्छी तरह अलग। 0 = clusters की सीमा पर। Negative = point शायद ग़लत cluster में है।
+1 = 分得很开;0 = 位于簇的边界;负数 = 这个点可能分错了簇。
from sklearn.metrics import silhouette_score

# ── Test K = 2 to 10 using silhouette score ─────────────
sil_scores = []
for k in range(2, 11):
    km = KMeans(n_clusters=k, n_init=10, random_state=42)
    lbl = km.fit_predict(X_scaled)
    score = silhouette_score(X_scaled, lbl)
    sil_scores.append((k, score))
    print(f"K={k}  Silhouette={score:.4f}")

best_k = max(sil_scores, key=lambda x: x[1])
print(f"\nBest K: {best_k[0]}  Score: {best_k[1]:.4f}")
OUTPUT
K=2 Silhouette=0.5700 K=3 Silhouette=0.7642 K=4 Silhouette=0.8386 K=5 Silhouette=0.7082 K=6 Silhouette=0.5767 K=7 Silhouette=0.4564 K=8 Silhouette=0.3362 K=9 Silhouette=0.3509 K=10 Silhouette=0.3675 Best K: 4 Score: 0.8386
🎯
What the Real Numbers Sayअसली Numbers क्या कहते हैं真实数字告诉我们什么

The inertia list from the code above (K = 1…10: 600, 313, 67, 11.3, 10.1, 9.0 …) drops sharply until K = 4 and then barely moves — a clear elbow. The silhouette agrees: 0.84 at K = 4 is the highest. On real data the elbow is often blurry, so use both tools, and then ask whether the groups make sense.

ऊपर के code की inertia list (K = 1…10: 600, 313, 67, 11.3, 10.1, 9.0 …) K = 4 तक तेज़ी से गिरती है और फिर लगभग नहीं बदलती — साफ़ elbow. Silhouette भी यही कहता है: K = 4 पर 0.84 सबसे ज़्यादा है। असली data पर elbow अक्सर धुँधला होता है, इसलिए दोनों औज़ार इस्तेमाल करें, और फिर पूछें कि groups का कोई मतलब बनता है या नहीं।

上面代码中的惯性列表(K = 1…10:600、313、67、11.3、10.1、9.0 …)在 K = 4 之前急剧下降,之后几乎不变 —— 明显的肘部。 轮廓系数也一致:K = 4 时为 0.84,最高。真实数据中的肘部常常不明显,所以两个工具都要用,然后再问问这些分组是否有意义。


SectionSection第 05 节

Clustering — DBSCAN: The Astronomer's LensClustering — DBSCAN: खगोलशास्त्री का Lens聚类 — DBSCAN:天文学家的镜头

Mapping Star Clusters Without Knowing How Many There Areबिना जाने कि कितने हैं, तारों के Clusters का नक्शा बनाना在不知道数量的情况下绘制星团
An astronomer photographs thousands of stars. She wants to find natural star clusters, but she has no idea how many exist, and some parts of the sky have only a few scattered background stars (noise, not real clusters).

Her method: pick a star and count how many other stars lie within a fixed radius ε. If there are at least minPts, it is a core star that anchors a cluster. She grows the cluster outward from every core star, adding all reachable neighbours. Stars that no cluster reaches are labelled noise.

This is DBSCAN — Density-Based Spatial Clustering of Applications with Noise. It finds clusters of any shape and labels noise. You never specify K.
एक खगोलशास्त्री हज़ारों तारों की photo लेती है। वह स्वाभाविक तारों के clusters खोजना चाहती है, पर उसे नहीं पता कि कितने हैं, और आसमान के कुछ हिस्सों में सिर्फ़ कुछ बिखरे हुए तारे हैं (noise, असली clusters नहीं)।

उसका तरीका: एक तारा चुनें और गिनें कि एक तय radius ε के अंदर कितने दूसरे तारे हैं। अगर कम से कम minPts हों, तो वह एक core तारा है जो cluster को थामता है। वह हर core तारे से cluster को बाहर की ओर बढ़ाती है और पहुँच वाले सभी पड़ोसियों को जोड़ती है। जिन तारों तक कोई cluster नहीं पहुँचता, उन्हें noise कहा जाता है।

यही DBSCAN है — Density-Based Spatial Clustering of Applications with Noise. यह किसी भी आकार के clusters खोजता है और noise पहचानता है। आपको K कभी नहीं बताना पड़ता।
一位天文学家拍下了成千上万颗星星。她想找出自然形成的星团,但不知道有多少个,而且天空中有些区域只有零星的背景星(噪声,不是真正的星团)。

她的方法:选一颗星,数一数固定半径 ε 内有多少颗其他星。如果至少有 minPts 颗,它就是一颗核心星,可以撑起一个星团。 她从每颗核心星向外扩张,把所有能到达的邻居都加进同一个星团。没有被任何星团覆盖的星被标为噪声。

这就是 DBSCAN(基于密度的带噪声空间聚类)。它能找到任意形状的簇,并且标出噪声。你完全不需要指定 K。
LIVELIVE互动DBSCAN — Play with ε and minPtsDBSCAN — ε और minPts से खेलेंDBSCAN — 调节 ε 和 minPts

Filled dots = core points, hollow dots = border points, × = noise. "Animate the growth" shows clusters spreading from core to core. Then compare with K-Means.

भरे dots = core points, खोखले dots = border points, × = noise. "बढ़ना animate करें" दिखाता है कि clusters core से core तक कैसे फैलते हैं। फिर K-Means से तुलना करें।

实心点 = 核心点,空心点 = 边界点,× = 噪声。"动画演示扩张过程"展示簇如何从一个核心点扩散到另一个核心点。然后与 K-Means 比较。

Clusters foundमिले clusters找到的簇
—
Noise pointsNoise points噪声点
—

Propertyगुण属性K-MeansDBSCAN
Number of clustersClusters की संख्या簇的数量Must give KK देना ज़रूरी必须指定 KFound automaticallyअपने आप मिलती है自动发现
Cluster shapesCluster के आकार簇的形状Round blobsगोल ढेर圆形团Any shapeकोई भी आकार任意形状
Handles noise/outliersNoise/outliers संभालता है处理噪声/离群点No — every point is assignedनहीं — हर point किसी cluster में否 —— 每个点都被分配Yes — noise label −1हाँ — noise label −1是 —— 噪声标签为 −1
Clusters of different densityअलग density वाले clusters密度不同的簇OK if well separatedठीक, अगर अच्छी तरह अलग हों分得开就行Hard — one ε for all (try HDBSCAN)मुश्किल — सबके लिए एक ε (HDBSCAN आज़माएँ)困难 —— 只有一个 ε(可试 HDBSCAN)
Scales to large dataबड़े data पर大数据扩展性Very fastबहुत तेज़非常快Slower, and weak in many dimensionsधीमा, और ज़्यादा dimensions में कमज़ोर较慢,高维时效果差
ParametersParameters参数n_clusterseps, min_samples
from sklearn.cluster import DBSCAN, KMeans
from sklearn.datasets import make_circles
from sklearn.metrics import adjusted_rand_score

# ── Concentric rings — K-Means can't handle this ────────
X_rings, y_true = make_circles(n_samples=400, noise=0.05, factor=0.5, random_state=42)
X_rings = StandardScaler().fit_transform(X_rings)

# ── DBSCAN ───────────────────────────────────────────────
db = DBSCAN(eps=0.3, min_samples=8)
db_labels = db.fit_predict(X_rings)

n_clusters = len(set(db_labels)) - (1 if -1 in db_labels else 0)
n_noise    = list(db_labels).count(-1)

print(f"Clusters found:  {n_clusters}")
print(f"Noise points:    {n_noise}")
print(f"Unique labels:   {sorted(int(l) for l in set(db_labels))}")

# ── Compare with K-Means (K=2) using the true ring labels ──
km_labels = KMeans(n_clusters=2, n_init=10, random_state=42).fit_predict(X_rings)
print(f"\nAgreement with true rings (ARI, 1.0 = perfect):")
print(f"  DBSCAN : {adjusted_rand_score(y_true, db_labels):.3f}")
print(f"  K-Means: {adjusted_rand_score(y_true, km_labels):.3f}")
OUTPUT
Clusters found: 2 Noise points: 0 Unique labels: [0, 1] Agreement with true rings (ARI, 1.0 = perfect): DBSCAN : 1.000 K-Means: -0.002
🔑
Choosing eps and min_sampleseps और min_samples चुनना如何选择 eps 和 min_samples

A common start: min_samples ≈ 2 × number of features (at least 4). Then sort every point's distance to its min_samples-th neighbour and plot it (the "k-distance plot"); pick eps where the curve bends upward. On the rings above, DBSCAN matches the true rings perfectly (ARI = 1.0), while K-Means scores about 0 — no better than chance.

एक आम शुरुआत: min_samples ≈ 2 × features की संख्या (कम से कम 4)। फिर हर point की उसके min_samples-वें पड़ोसी तक की दूरी sort करके plot करें ("k-distance plot"); जहाँ curve ऊपर मुड़ता है वहाँ eps चुनें। ऊपर के rings पर DBSCAN असली rings से बिल्कुल मेल खाता है (ARI = 1.0), जबकि K-Means का score लगभग 0 है — अंदाज़े से बेहतर नहीं।

常见的起点:min_samples ≈ 2 × 特征数(至少 4)。然后把每个点到它第 min_samples 个邻居的距离排序并画出来("k 距离图"), 在曲线开始上翘的地方选 eps。在上面的圆环数据上,DBSCAN 与真实圆环完全吻合(ARI = 1.0),而 K-Means 的得分约为 0 —— 和随机猜差不多。


SectionSection第 06 节

Clustering — Hierarchical: The Family TreeClustering — Hierarchical: वंश-वृक्ष聚类 — 层次聚类:家谱树

Building a Species Family Tree from DNADNA से प्रजातियों का वंश-वृक्ष बनाना用 DNA 构建物种家谱
A biologist sequences the DNA of 200 species and wants to understand how they are related. She does not want a flat list of clusters — she wants a tree of life, showing that chimps and humans are closer to each other than either is to dolphins.

She starts with every species as its own group (200 clusters). Then she merges the two most similar groups. Then the next two most similar groups. Over and over, until everything is one big group. At any point she can cut the tree horizontally to get as many clusters as she wants — without running the algorithm again. The result is a dendrogram: a branching diagram of the whole merge history.
एक जीव-वैज्ञानिक 200 प्रजातियों का DNA पढ़ती है और समझना चाहती है कि वे आपस में कैसे जुड़ी हैं। उसे clusters की सपाट list नहीं चाहिए — उसे एक जीवन-वृक्ष चाहिए, जो दिखाए कि चिंपैंज़ी और इंसान एक-दूसरे के ज़्यादा पास हैं, डॉल्फ़िन से नहीं।

वह हर प्रजाति को उसका अपना group मानकर शुरू करती है (200 clusters)। फिर सबसे मिलते-जुलते दो groups को जोड़ती है। फिर अगले सबसे मिलते-जुलते दो को। बार-बार, जब तक सब एक बड़ा group न बन जाए। किसी भी समय वह tree को आड़ा काटकर जितने चाहे उतने clusters पा सकती है — algorithm दोबारा चलाए बिना। नतीजा एक dendrogram है: पूरे merge इतिहास का शाखाओं वाला चित्र।
一位生物学家测序了 200 个物种的 DNA,想了解它们之间的亲缘关系。她不要一个平铺的簇列表 —— 她想要一棵生命之树, 显示黑猩猩和人类彼此更近,而离海豚更远。

她一开始把每个物种都当作一个单独的组(200 个簇),然后合并最相似的两个组,再合并接下来最相似的两个……一直到所有物种合成一个大组。 任何时候她都可以把树横着切一刀,得到想要的簇数 —— 无需重新运行算法。结果就是一张树状图(dendrogram):整个合并历史的分支图。
LIVELIVE互动Build a Dendrogram, One Merge at a Timeएक-एक merge करके Dendrogram बनाएँ一步一步合并,构建树状图

Left: 12 points; the orange line is the newest merge. Right: the dendrogram growing. The height of each joint is the distance at which the two groups merged. Move the cut to choose the number of clusters, and try different linkages.

बाएँ: 12 points; नारंगी line सबसे नया merge है। दाएँ: बढ़ता हुआ dendrogram. हर जोड़ की ऊँचाई वह दूरी है जिस पर दो groups जुड़े। Clusters की संख्या चुनने के लिए cut खिसकाएँ, और अलग-अलग linkages आज़माएँ।

左:12 个点,橙色线是最新的一次合并。右:逐步生长的树状图,每个连接点的高度就是两组合并时的距离。移动切线来选择簇数,并试试不同的连接方式。

🔗
Single linkageSingle linkage单连接
closest pairसबसे पास का जोड़ा最近的一对点
Distance between groups = their two closest points. Follows long, thin shapes, but tends to "chain" everything into one big group.
Groups के बीच दूरी = उनके दो सबसे पास points. लंबे, पतले आकारों के साथ चलता है, पर सबको एक बड़े group में "chain" कर देता है।
组间距离 = 两组中最近两点的距离。能跟随细长形状,但容易把所有点"串"成一个大组。
📏
Complete / AverageComplete / Average全连接 / 平均连接
farthest pair / meanसबसे दूर का जोड़ा / औसत最远的一对 / 平均
Complete uses the farthest pair, average uses the mean of all pairs. Both give compact, balanced clusters.
Complete सबसे दूर का जोड़ा इस्तेमाल करता है, average सभी जोड़ों का औसत। दोनों सघन, संतुलित clusters देते हैं।
全连接用最远的一对点,平均连接用所有点对的平均距离。两者都会得到紧凑、均衡的簇。
⚖️
Ward linkageWard linkageWard 连接
smallest growth in variancevariance में सबसे कम बढ़ोतरी方差增加最小
Merges the two groups that increase the within-cluster variance the least. Behaves much like K-Means and is the scikit-learn default.
वे दो groups जोड़ता है जिनसे within-cluster variance सबसे कम बढ़े। काफ़ी हद तक K-Means जैसा व्यवहार, और scikit-learn का default.
合并使簇内方差增加最少的两组。表现很像 K-Means,也是 scikit-learn 的默认方式。
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage

# ── Fit hierarchical clustering ──────────────────────────
hier = AgglomerativeClustering(
    n_clusters=4,
    linkage='ward'      # minimises intra-cluster variance at each merge
)
hier_labels = hier.fit_predict(X_scaled)

print(f"Cluster sizes: {np.bincount(hier_labels)}")

# ── Full dendrogram (use linkage on a small sample) ──────
Z = linkage(X_scaled[:50], method='ward')
# Z is a (49 x 4) merge matrix — plot with scipy dendrogram()
print(f"Merge matrix shape: {Z.shape}")
print(f"Last merge distance: {Z[-1, 2]:.3f}")
OUTPUT
Cluster sizes: [75 75 75 75] Merge matrix shape: (49, 4) Last merge distance: 9.448

SectionSection第 07 节

Dimensionality Reduction — PCA: The Shadow on the WallDimensionality Reduction — PCA: दीवार पर परछाई降维 — PCA:墙上的影子

The Sculpture and Its Shadowमूर्ति और उसकी परछाई雕塑与它的影子
Imagine a complex 3-D sculpture — a twisted helix of metal. Shine a spotlight on it and look at the 2-D shadow on the wall. The shadow loses some depth, but from a good angle it still shows most of the shape.

Now turn the spotlight until the shadow is as large and spread out as possible. That is Principal Component Analysis (PCA). It finds the direction in which your data varies most (PC1), then the next one at 90° (PC2), and so on — so hundreds of features can be replaced by a handful of principal components that keep most of the variance.
एक जटिल 3-D मूर्ति की कल्पना करें — धातु का मुड़ा हुआ helix. उस पर spotlight डालें और दीवार पर 2-D परछाई देखें। परछाई कुछ गहराई खो देती है, पर अच्छे कोण से वह फिर भी ज़्यादातर आकार दिखाती है।

अब spotlight को तब तक घुमाएँ जब तक परछाई सबसे बड़ी और फैली हुई न हो जाए। यही Principal Component Analysis (PCA) है। यह वह दिशा खोजता है जिसमें आपका data सबसे ज़्यादा बदलता है (PC1), फिर 90° पर अगली (PC2), और इसी तरह आगे — ताकि सैकड़ों features की जगह कुछ principal components ले सकें जो ज़्यादातर variance बचाते हैं।
想象一座复杂的三维雕塑 —— 一条扭曲的金属螺旋。用聚光灯照它,看墙上的二维影子。影子会丢失一些深度信息,但只要角度合适,仍能显示出大部分形状。

现在转动聚光灯,直到影子尽可能大、尽可能舒展。这就是主成分分析(PCA)。 它找到数据变化最大的方向(PC1),然后找与之成 90° 的次大方向(PC2),依此类推 —— 于是成百上千个特征可以被少数几个保留了大部分方差的主成分代替。
📐 PCA — THE AXES OF MAXIMUM VARIANCEPCA — सबसे ज़्यादा VARIANCE के AXESPCA — 方差最大的坐标轴
PCA axes PC1: most variancePC1: सबसे ज़्यादा variancePC1:方差最大 PC2: next most, at 90°PC2: अगला, 90° परPC2:次大,成 90°

PC1 runs along the longest side of the cloud. PC2 is at 90° and captures the next most variance.

PC1 बादल की सबसे लंबी तरफ़ चलता है। PC2 उससे 90° पर है और अगला सबसे ज़्यादा variance पकड़ता है।

PC1 沿着点云最长的方向,PC2 与它成 90°,捕获次多的方差。

from sklearn.decomposition import PCA
from sklearn.datasets import load_digits

# ── Digits dataset: 1797 samples × 64 pixel features ────
digits = load_digits()
X_dig  = digits.data          # shape (1797, 64)
X_dig  = StandardScaler().fit_transform(X_dig)

# ── How many components explain 95% variance? ────────────
pca_full = PCA(n_components=0.95)
X_pca    = pca_full.fit_transform(X_dig)

print(f"Original dims:   {X_dig.shape[1]}")
print(f"Reduced dims:    {X_pca.shape[1]}")
print(f"Variance retained: {sum(pca_full.explained_variance_ratio_):.4f}")

# ── 2D projection for visualisation ─────────────────────
pca_2d  = PCA(n_components=2)
X_2d    = pca_2d.fit_transform(X_dig)
print(f"2D variance:     {sum(pca_2d.explained_variance_ratio_):.4f}")
print(f"PC1 explains:    {pca_2d.explained_variance_ratio_[0]:.4f}")
print(f"PC2 explains:    {pca_2d.explained_variance_ratio_[1]:.4f}")
OUTPUT
Original dims: 64 Reduced dims: 40 Variance retained: 0.9508 2D variance: 0.2159 PC1 explains: 0.1203 PC2 explains: 0.0956
🔑
Scale Before PCA — UsuallyPCA से पहले Scale करें — आमतौर परPCA 之前先缩放 —— 通常如此

PCA follows variance, so a feature measured in big numbers (yearly salary) would dominate one in small numbers (age). When features use different units, apply StandardScaler first. When they share one unit — like the pixels here — scaling is optional: on raw pixels 29 components keep 95% of the variance, after scaling it takes 40, because scaling gives the almost-always-blank border pixels as much weight as the centre ones.

PCA variance के पीछे चलता है, इसलिए बड़े numbers वाला feature (सालाना salary) छोटे numbers वाले (उम्र) पर हावी हो जाएगा। जब features की units अलग हों, तो पहले StandardScaler लगाएँ। जब सबकी एक ही unit हो — जैसे यहाँ pixels — तो scaling वैकल्पिक है: raw pixels पर 29 components 95% variance रखते हैं, scaling के बाद 40 लगते हैं, क्योंकि scaling लगभग हमेशा ख़ाली रहने वाले किनारे के pixels को बीच वालों जितना weight दे देती है।

PCA 追随方差,所以数值大的特征(年薪)会压倒数值小的特征(年龄)。特征单位不同时,先用 StandardScaler。 当所有特征单位相同时(比如这里的像素),缩放是可选的:用原始像素时 29 个主成分就能保留 95% 的方差,缩放后却需要 40 个, 因为缩放让几乎总是空白的边缘像素与中心像素拥有同样的权重。


SectionSection第 08 节

Dimensionality Reduction — t-SNE: The Neighbourhood MapperDimensionality Reduction — t-SNE: पड़ोस का नक्शा降维 — t-SNE:邻里地图

Moving a City onto a 2-D Map While Keeping Neighbours Togetherशहर को 2-D नक्शे पर लाना, पड़ोसियों को साथ रखते हुए把城市搬到二维地图上,同时让邻居保持相邻
Imagine a city in 100-dimensional space (impossible to picture) that you must draw on a 2-D map so that neighbours stay neighbours. Two people on the same street must still be close on your map, even if the overall geography gets distorted.

t-SNE does exactly this. It turns high-dimensional distances into probabilities — "how likely is A to pick B as a neighbour?" — and then moves points around in 2-D until the same neighbour probabilities appear. Groups that are truly distinct become separate islands. It is excellent for looking at complex data, but not for compression or as input to other models.
100-dimensional space में एक शहर की कल्पना करें (जिसकी तस्वीर बनाना असंभव है) जिसे आपको 2-D नक्शे पर ऐसे बनाना है कि पड़ोसी पड़ोसी ही रहें। एक ही गली के दो लोग आपके नक्शे पर भी पास होने चाहिए, भले ही पूरा भूगोल थोड़ा बिगड़ जाए।

t-SNE ठीक यही करता है। यह high-dimensional दूरियों को probabilities में बदलता है — "A, B को पड़ोसी चुने इसकी कितनी संभावना है?" — और फिर 2-D में points को तब तक खिसकाता है जब तक वही पड़ोसी-probabilities न आ जाएँ। सच में अलग groups अलग द्वीप बन जाते हैं। यह जटिल data को देखने के लिए बेहतरीन है, पर compression या दूसरे models के input के लिए नहीं।
想象一座位于 100 维空间中的城市(根本无法想象),你要把它画到二维地图上,并且邻居仍然是邻居。 住在同一条街上的两个人在地图上也必须靠近,哪怕整体地理被扭曲。

t-SNE 做的正是这件事。它把高维距离转换成概率 ——"A 选 B 当邻居的可能性有多大?"—— 然后在二维中移动这些点,直到出现同样的邻居概率。真正不同的组会变成分开的"岛屿"。 它非常适合用来观察复杂数据,但不适合做压缩,也不适合作为其他模型的输入。
LIVELIVE互动Run t-SNE in Your Browserअपने Browser में t-SNE चलाएँ在浏览器中运行 t-SNE

300 handwritten digits (64 pixels each), 30 of each digit. The page computes real t-SNE (500 steps). The digit labels are used only to colour the points; t-SNE never sees them. Then compare with a PCA 2-D view.

300 हाथ से लिखे digits (हर एक 64 pixels), हर digit के 30. Page असली t-SNE (500 steps) निकालता है। Digit labels सिर्फ़ points को रंग देने के लिए हैं; t-SNE उन्हें कभी नहीं देखता। फिर PCA 2-D view से तुलना करें।

300 个手写数字(每个 64 像素),每种数字 30 个。页面实时计算真正的 t-SNE(500 步)。数字标签只用来给点上色,t-SNE 从未看到它们。然后与 PCA 二维视图比较。

Iteration —KL —

Try perplexity 5: t-SNE looks only at very close neighbours and breaks digits into many small pieces. Perplexity 30–50 is a common choice.

Perplexity 5 आज़माएँ: t-SNE सिर्फ़ बहुत पास के पड़ोसियों को देखता है और digits को कई छोटे टुकड़ों में तोड़ देता है। 30–50 आम पसंद है।

试试困惑度 5:t-SNE 只看非常近的邻居,会把数字拆成很多小块。常用值是 30–50。

📊 When to Use t-SNEt-SNE कब इस्तेमाल करें何时使用 t-SNE
✅ Visualising high-dimensional data in 2-D/3-DHigh-dimensional data को 2-D/3-D में देखना把高维数据可视化到二维/三维
✅ Checking cluster structure by eyeCluster ढाँचे को आँखों से जाँचना目测检查聚类结构
✅ Exploring a new datasetनए dataset को समझना探索新的数据集
✅ Spotting sub-groups inside groupsGroups के अंदर छोटे groups देखना发现组内的子组
🚫 When NOT to Use t-SNEt-SNE कब न इस्तेमाल करें何时不要用 t-SNE
❌ Features for a model (it cannot transform new data)Model के features के लिए (यह नया data transform नहीं कर सकता)作为模型的特征(它不能变换新数据)
❌ Reading distances or sizes between clustersClusters के बीच दूरी या आकार पढ़ना解读簇之间的距离或大小
❌ Very large data with exact t-SNE (use Barnes-Hut or UMAP)Exact t-SNE से बहुत बड़ा data (Barnes-Hut या UMAP)用精确 t-SNE 处理超大数据(请用 Barnes-Hut 或 UMAP)
❌ Results that must be identical every run (fix random_state)हर बार एक जैसे नतीजे चाहिए (random_state तय करें)要求每次结果完全一致(需固定 random_state)
from sklearn.manifold import TSNE

# ── t-SNE: always reduce with PCA first for speed ────────
pca_50  = PCA(n_components=50, random_state=42)
X_50    = pca_50.fit_transform(X_dig)        # PCA → 50 dims first

tsne    = TSNE(
    n_components=2,
    perplexity=40,          # typical range: 5–50
    max_iter=1000,           # called n_iter before scikit-learn 1.5
    random_state=42,
    learning_rate='auto'
)
X_tsne = tsne.fit_transform(X_50)

print(f"t-SNE output shape: {X_tsne.shape}")
print(f"KL divergence:      {tsne.kl_divergence_:.4f}")
# Lower KL = better embedding (closer to original distances)
OUTPUT
t-SNE output shape: (1797, 2) KL divergence: 0.7965
💡
About the CodeCode के बारे में关于这段代码

Recent scikit-learn versions renamed n_iter to max_iter; the old name now raises an error. Reducing to 50 PCA components first (as here) is a standard trick: it removes noise and makes t-SNE much faster. A lower KL divergence means the 2-D map matches the neighbour probabilities better, but only compare KL values for the same data and perplexity.

नए scikit-learn versions में n_iter का नाम बदलकर max_iter कर दिया गया है; पुराना नाम अब error देता है। पहले 50 PCA components तक घटाना (जैसे यहाँ) एक आम तरीका है: इससे noise हटता है और t-SNE बहुत तेज़ होता है। कम KL divergence का मतलब है कि 2-D नक्शा पड़ोसी-probabilities से बेहतर मेल खाता है, पर KL values की तुलना सिर्फ़ एक ही data और perplexity पर करें।

较新的 scikit-learn 把 n_iter 改名为 max_iter,旧名字现在会报错。先用 PCA 降到 50 维(如这里所示)是标准技巧: 它能去除噪声,并让 t-SNE 快很多。KL 散度越低,说明二维地图与邻居概率越吻合,但只能在相同数据和相同困惑度下比较 KL 值。


SectionSection第 09 节

Autoencoders — The Art of Compressing and RebuildingAutoencoders — Compress करने और दोबारा बनाने की कला自编码器 — 压缩与重建的艺术

The Whisper Game — Lossy but Structuredफुसफुसाहट का खेल — कुछ खोकर भी व्यवस्थित传话游戏 — 有损但有结构
Imagine a game where you must pass a 100-word story to a friend using only 10 words. Your friend then rebuilds the full story from those 10 words. You practise together — you learn to compress, they learn to rebuild. After many rounds, your 10-word summaries capture exactly the most essential information.

An autoencoder is a neural network that plays this game. The encoder squeezes the input into a small latent space; the decoder rebuilds the input from it. The network trains by making the rebuilt output as close as possible to the input — no labels needed.
एक खेल की कल्पना करें जिसमें आपको 100 शब्दों की कहानी सिर्फ़ 10 शब्दों में दोस्त तक पहुँचानी है। फिर आपका दोस्त उन 10 शब्दों से पूरी कहानी दोबारा बनाता है। आप साथ अभ्यास करते हैं — आप compress करना सीखते हैं, वे दोबारा बनाना। कई rounds के बाद आपके 10 शब्दों के सार ठीक सबसे ज़रूरी जानकारी पकड़ लेते हैं।

Autoencoder एक neural network है जो यही खेल खेलता है। Encoder input को एक छोटे latent space में दबाता है; decoder उससे input को दोबारा बनाता है। Network इस तरह train होता है कि दोबारा बना output, input के जितना हो सके उतना पास हो — कोई label नहीं चाहिए।
想象一个游戏:你只能用 10 个字把一个 100 字的故事传给朋友,朋友再根据这 10 个字还原整个故事。 你们一起练习 —— 你学会压缩,他学会还原。经过很多轮,你的 10 字摘要恰好抓住了最关键的信息。

自编码器(autoencoder)就是玩这个游戏的神经网络。编码器把输入压缩到一个很小的潜在空间(latent space), 解码器再从中重建输入。网络通过让重建结果尽可能接近输入来训练 —— 不需要任何标签。
🧠 AUTOENCODER ARCHITECTURE (AS IN THE CODE BELOW)AUTOENCODER की बनावट (नीचे के CODE जैसी)自编码器结构(与下方代码一致)
Autoencoder INPUTINPUT输入 64 dims64 dims64 维 ENCODERENCODER编码器 32 dims32 dims32 维 LATENTLATENT潜在层 8 dims8 dims8 维 DECODERDECODER解码器 32 dims32 dims32 维 OUTPUTOUTPUT输出 64 dims64 dims64 维 bottleneck: forces compressionbottleneck: compression के लिए मजबूर करता है瓶颈:迫使网络压缩信息 ← encoder← encoder← 编码器 decoder →decoder →解码器 →

64 pixels → 32 → 8 (bottleneck) → 32 → 64 pixels. Each circle stands for a group of neurons.

64 pixels → 32 → 8 (bottleneck) → 32 → 64 pixels. हर गोला neurons के एक group को दिखाता है।

64 像素 → 32 → 8(瓶颈)→ 32 → 64 像素。每个圆代表一组神经元。

import tensorflow as tf
from tensorflow import keras
import numpy as np
from sklearn.decomposition import PCA

tf.keras.utils.set_random_seed(42)   # reproducible weights

# ── Simple autoencoder for MNIST-like digits ─────────────
input_dim  = 64    # sklearn digits: 8x8 = 64 pixels
latent_dim = 8     # compress to 8 dimensions

# ── Encoder ───────────────────────────────────────────────
inputs  = keras.Input(shape=(input_dim,))
encoded = keras.layers.Dense(32, activation='relu')(inputs)
latent  = keras.layers.Dense(latent_dim, activation='relu')(encoded)

# ── Decoder ───────────────────────────────────────────────
decoded  = keras.layers.Dense(32, activation='relu')(latent)
outputs  = keras.layers.Dense(input_dim, activation='sigmoid')(decoded)

# ── Full autoencoder ──────────────────────────────────────
autoencoder = keras.Model(inputs, outputs)
autoencoder.compile(optimizer='adam', loss='mse')

# ── Normalise to [0,1] ────────────────────────────────────
X_norm = digits.data / 16.0

autoencoder.fit(X_norm, X_norm,   # target = input (reconstruction)
              epochs=50, batch_size=32, validation_split=0.1, verbose=0)

# ── Extract compressed representations ───────────────────
encoder_model = keras.Model(inputs, latent)
X_compressed  = encoder_model.predict(X_norm, verbose=0)
print(f"Compressed shape: {X_compressed.shape}")  # (1797, 8)

# ── How well does it rebuild the images? Compare with PCA (8 components) ──
X_rebuilt = autoencoder.predict(X_norm, verbose=0)
pca8 = PCA(n_components=8).fit(X_norm)
X_pca_rebuilt = pca8.inverse_transform(pca8.transform(X_norm))
print(f"Reconstruction MSE, autoencoder: {np.mean((X_norm - X_rebuilt) ** 2):.4f}")
print(f"Reconstruction MSE, PCA (8):     {np.mean((X_norm - X_pca_rebuilt) ** 2):.4f}")
OUTPUT
Compressed shape: (1797, 8) Reconstruction MSE, autoencoder: 0.0199 Reconstruction MSE, PCA (8): 0.0239
🔍
Autoencoder vs PCAAutoencoder vs PCA自编码器 vs PCA

With the same 8 numbers per image, this small autoencoder rebuilds the digits a little better than PCA (MSE 0.0199 vs 0.0239), because it can learn curved patterns. But it needs training, tuning and a deep-learning library. A linear autoencoder with no activations learns the same subspace as PCA, so always try PCA first as the baseline.

हर image के वही 8 numbers होने पर, यह छोटा autoencoder digits को PCA से थोड़ा बेहतर दोबारा बनाता है (MSE 0.0199 vs 0.0239), क्योंकि यह मुड़े हुए patterns सीख सकता है। पर इसे training, tuning और एक deep-learning library चाहिए। Activations के बिना linear autoencoder वही subspace सीखता है जो PCA, इसलिए baseline के रूप में हमेशा पहले PCA आज़माएँ।

在每张图同样只用 8 个数的情况下,这个小自编码器重建数字的效果比 PCA 略好(MSE 0.0199 对 0.0239),因为它能学习弯曲的模式。 但它需要训练、调参和深度学习库。没有激活函数的线性自编码器学到的子空间与 PCA 相同,所以一定要先用 PCA 做基线。


SectionSection第 10 节

Anomaly Detection — Isolation Forest: The Odd One OutAnomaly Detection — Isolation Forest: अलग वाला异常检测 — 孤立森林:找出"另类"

Finding the Counterfeit Coin in a Bagथैले में नकली सिक्का खोजना在一袋硬币中找出假币
You have a bag of 1,000 coins. One is counterfeit — a bit heavier and a bit wider. Keep splitting the coins at random cut-offs ("heavier than x?", "wider than y?"). The counterfeit ends up alone after very few cuts, because it is far from all the others. A genuine coin looks like hundreds of others, so it takes many cuts before it is alone.

Isolation Forest uses exactly this idea. It builds many random trees; points that get isolated with short paths (few splits) on average are flagged as anomalies. It never needs a description of "normal" — normal points are simply the ones that are hard to isolate.
आपके पास 1,000 सिक्कों का थैला है। एक नकली है — थोड़ा भारी और थोड़ा चौड़ा। सिक्कों को random सीमाओं पर बाँटते रहें ("x से भारी?", "y से चौड़ा?")। नकली सिक्का बहुत कम cuts में अकेला रह जाता है, क्योंकि वह बाकी सबसे दूर है। असली सिक्का सैकड़ों दूसरों जैसा दिखता है, इसलिए उसे अकेला होने में कई cuts लगते हैं।

Isolation Forest ठीक यही विचार इस्तेमाल करता है। यह बहुत सारे random trees बनाता है; जो points औसतन छोटे paths (कम splits) में अकेले हो जाते हैं, उन्हें anomaly माना जाता है। इसे "सामान्य" की परिभाषा कभी नहीं चाहिए — सामान्य points बस वे हैं जिन्हें अकेला करना मुश्किल है।
你有一袋 1,000 枚硬币,其中一枚是假币 —— 稍重一点、稍宽一点。不断用随机的界限去切分硬币("比 x 重吗?""比 y 宽吗?")。 假币离其他硬币都很远,所以只需很少几刀就会被单独分出来;真币和成百上千枚硬币长得一样,要切很多刀才能单独分出来。

孤立森林(Isolation Forest)正是利用这个思想。它构建很多棵随机树;平均只需很短路径(很少次切分)就被孤立的点会被标记为异常。 它从不需要描述什么是"正常"—— 正常点就是那些很难被孤立的点。
LIVELIVE互动Isolation Forest — Short Path = AnomalyIsolation Forest — छोटा Path = Anomaly孤立森林 — 路径短 = 异常

A real Isolation Forest (100 trees, 64 samples each) runs in the page. Red background = high anomaly score. Click a point to watch one random tree cut around it; click empty space to add a point.

Page में एक असली Isolation Forest (100 trees, हर एक में 64 samples) चलता है। लाल background = ऊँचा anomaly score. किसी point पर click करके देखें कि एक random tree उसके चारों ओर कैसे cut करता है; ख़ाली जगह पर click करके point जोड़ें।

页面中运行的是真正的孤立森林(100 棵树,每棵 64 个样本)。红色背景 = 异常分数高。点击一个点,观看一棵随机树如何围绕它切分;点击空白处可添加新点。

Flagged as anomaliesAnomaly माने गए被标记为异常
—
Avg path, selectedऔसत path, चुना गया平均路径(选中点)
—
Avg path, normal pointऔसत path, normal point平均路径(正常点)
—

from sklearn.ensemble import IsolationForest
import numpy as np

# ── Credit card fraud detection simulation ───────────────
np.random.seed(42)
X_normal = np.random.randn(500, 5)                     # 500 normal transactions
X_fraud  = np.random.randn(20, 5) * 4 + 6              # 20 anomalous transactions
X_all    = np.vstack([X_normal, X_fraud])

# ── Isolation Forest ─────────────────────────────────────
iso = IsolationForest(
    n_estimators=200,
    contamination=0.04,   # expected anomaly fraction (~4%)
    random_state=42
)
preds  = iso.fit_predict(X_all)        # +1 = normal, -1 = anomaly
scores = iso.score_samples(X_all)       # lower score = more anomalous

n_detected = (preds == -1).sum()
actual_frauds_found = sum((preds[500:]) == -1)

print(f"Total anomalies flagged: {n_detected}")
print(f"Real frauds detected:    {actual_frauds_found} / 20")
print(f"Lowest anomaly score:    {scores.min():.4f}")
OUTPUT
Total anomalies flagged: 21 Real frauds detected: 20 / 20 Lowest anomaly score: -0.7537
✅
Isolation Forest in ProductionProduction में Isolation Forest生产环境中的孤立森林

All 20 simulated frauds were caught, with just 1 false alarm among 500 normal transactions (21 flagged, because contamination=0.04 flags the top 4%). Isolation Forest is one of the most practical anomaly detectors: no labels, fast, and works on tabular data. contamination only sets the cut-off on the scores; the scores themselves (score_samples) are often more useful as a ranking.

सभी 20 नकली (fraud) transactions पकड़े गए, 500 सामान्य transactions में सिर्फ़ 1 ग़लत alarm के साथ (21 flagged, क्योंकि contamination=0.04 सबसे ऊपर के 4% को flag करता है)। Isolation Forest सबसे काम के anomaly detectors में से एक है: labels नहीं चाहिए, तेज़ है, और tabular data पर चलता है। contamination सिर्फ़ scores पर cut-off तय करता है; खुद scores (score_samples) अक्सर ranking के रूप में ज़्यादा काम के होते हैं।

20 笔模拟欺诈全部被抓出,500 笔正常交易中只有 1 次误报(共标记 21 笔,因为 contamination=0.04 会标记得分最高的 4%)。 孤立森林是最实用的异常检测器之一:不需要标签、速度快、适用于表格数据。contamination 只决定分数的切点; 分数本身(score_samples)作为排序往往更有用。


SectionSection第 11 节

End-to-End Pipeline — Customer SegmentationEnd-to-End Pipeline — Customer Segmentation端到端流程 — 客户细分

E-Commerce: Finding Natural Customer Segments from Purchase HistoryE-Commerce: ख़रीद के इतिहास से स्वाभाविक Customer Segments खोजना电商:从购买记录中找出自然的客户群
A retail company has thousands of customers. For each one it knows recency, frequency, total spend, average order value, categories browsed and returns. The marketing team wants personalised campaigns but has no predefined groups — they want the data to tell them who the natural segments are.

The pipeline: fix skewed features → scale → PCA (remove noise) → K-Means → profile each cluster → name and target each segment.
एक retail company के हज़ारों customers हैं। हर एक के लिए वह recency, frequency, कुल खर्च, औसत order value, देखी गई categories और returns जानती है। Marketing team personalised campaigns चाहती है पर उसके पास पहले से तय groups नहीं हैं — वह चाहती है कि data ख़ुद बताए कि स्वाभाविक segments कौन से हैं।

Pipeline: टेढ़े (skewed) features ठीक करें → scale → PCA (noise हटाएँ) → K-Means → हर cluster का profile → हर segment को नाम दें और target करें।
一家零售公司有数千名客户。对每位客户,它知道最近一次购买距今天数、购买频率、总消费、平均订单金额、浏览的品类数和退货次数。 市场团队想做个性化营销,但没有预先定义的分组 —— 他们希望让数据告诉他们天然的客户群是什么。

流程:处理偏斜特征 → 缩放 → PCA(去噪)→ K-Means → 给每个簇画像 → 为每个客户群命名并定向营销。
01
Load & inspectLoad और जाँच加载与检查
Load the customer table. Check missing values, outliers and feature types. Understand the business meaning of each column before touching the data.
Customer table load करें। Missing values, outliers और feature types जाँचें। Data छूने से पहले हर column का business मतलब समझें।
加载客户表,检查缺失值、离群点和特征类型。在动数据之前先理解每一列的业务含义。
02
Fix skew, then scaleSkew ठीक करें, फिर scale先处理偏斜,再缩放
Money columns are heavily skewed: a few customers spend 100× more. Take log1p of them first, then apply StandardScaler — recency (days) and spend (₹) are on completely different scales.
पैसे वाले columns बहुत टेढ़े होते हैं: कुछ customers 100× ज़्यादा खर्च करते हैं। पहले उनका log1p लें, फिर StandardScaler लगाएँ — recency (दिन) और spend (₹) बिल्कुल अलग scales पर हैं।
金额类列严重偏斜:少数客户的消费是别人的 100 倍。先对它们取 log1p,再用 StandardScaler —— 天数和金额(₹)的尺度完全不同。
03
PCA for denoisingDenoising के लिए PCAPCA 去噪
Keep the components that explain 90% of the variance. This removes noisy directions and makes clustering faster on wide tables.
वे components रखें जो 90% variance समझाते हैं। इससे noisy दिशाएँ हटती हैं और चौड़ी tables पर clustering तेज़ होती है।
保留能解释 90% 方差的主成分。这样能去掉噪声方向,并让宽表上的聚类更快。
04
Choose KK चुनें选择 K
Look at the silhouette and elbow for K = 2–8, but also at what the marketing team can act on. Statistics and business sense must agree.
K = 2–8 के लिए silhouette और elbow देखें, पर यह भी कि marketing team किस पर काम कर सकती है। Statistics और business समझ दोनों मेल खाने चाहिए।
查看 K = 2–8 的轮廓系数和肘部,同时考虑市场团队能执行什么。统计结果和业务判断必须一致。
05
Fit K-Means & profile segmentsK-Means fit करें और segments का profile बनाएँ拟合 K-Means 并给客户群画像
Fit K-Means, then group the ORIGINAL (unscaled) data by cluster and compute the means. Give each segment a human name the team can use.
K-Means fit करें, फिर ORIGINAL (बिना scale) data को cluster के हिसाब से group करें और means निकालें। हर segment को ऐसा नाम दें जो team इस्तेमाल कर सके।
拟合 K-Means,然后按簇对原始(未缩放)数据分组并计算均值。给每个客户群起一个团队能用的名字。
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

# ── Simulate customers from 4 hidden behaviour types ───────
rng = np.random.default_rng(42)
types = {            # recency, frequency, spend, order value, categories, returns
    'vip':        (10,  20, 2500, 120, 10, 2.0),
    'regular':    (30,  10,  600,  60,  6, 1.0),
    'occasional': (70,   4,  200,  45,  3, 0.5),
    'at_risk':    (160,  2,  120,  55,  2, 0.3),
}
sizes = [200, 700, 700, 400]
rows = []
for (name, m), n in zip(types.items(), sizes):
    rows.append(pd.DataFrame({
        'recency_days':       rng.gamma(4, m[0] / 4, n),
        'frequency':          rng.poisson(m[1], n),
        'total_spend':        rng.lognormal(np.log(m[2]), 0.35, n),
        'avg_order_value':    rng.lognormal(np.log(m[3]), 0.25, n),
        'categories_browsed': rng.poisson(m[4], n),
        'returns_count':      rng.poisson(m[5], n),
    }))
df = pd.concat(rows, ignore_index=True)

# ── Step 1: tame skew, then scale ──────────────────────────
X = df.copy()
X[['total_spend', 'avg_order_value']] = np.log1p(X[['total_spend', 'avg_order_value']])
X_scaled = StandardScaler().fit_transform(X)

# ── Step 2: PCA — 90% variance ────────────────────────────
pca   = PCA(n_components=0.90, random_state=42)
X_pca = pca.fit_transform(X_scaled)
print(f"Features after PCA: {X_pca.shape[1]} (from {X.shape[1]})")

# ── Step 3: Choose K with the silhouette score ─────────────
best_k, best_sil = 2, -1   # silhouette alone
for k in range(2, 9):
    lbl = KMeans(n_clusters=k, n_init=10, random_state=42).fit_predict(X_pca)
    sil = silhouette_score(X_pca, lbl)
    print(f"  K={k}  silhouette={sil:.3f}")
    if sil > best_sil:
        best_sil, best_k = sil, k
print(f"Best K by silhouette: {best_k}")

# ── Step 4: Fit K=4 (business choice) and profile in ORIGINAL units ──
K = 4
df['segment'] = KMeans(n_clusters=K, n_init=20, random_state=42).fit_predict(X_pca)
profile = df.groupby('segment').mean().round(1)
profile['size'] = df['segment'].value_counts().sort_index()
print("\nSegment profiles (means):")
print(profile.to_string())
OUTPUT
Features after PCA: 4 (from 6) K=2 silhouette=0.385 K=3 silhouette=0.361 K=4 silhouette=0.327 K=5 silhouette=0.320 K=6 silhouette=0.271 K=7 silhouette=0.262 K=8 silhouette=0.272 Best K by silhouette: 2 Segment profiles (means): recency_days frequency total_spend avg_order_value categories_browsed returns_count size segment 0 200.5 2.1 131.6 59.6 2.2 0.2 286 1 10.6 19.8 2588.9 124.6 10.2 2.0 200 2 31.8 9.7 655.2 62.6 6.1 1.0 696 3 69.9 3.7 209.6 47.9 2.9 0.4 818
⚠️
Two Honest Lessons from This Runइस Run से दो ईमानदार सबक这次运行带来的两个坦诚的教训

1. The silhouette is not the final judge. It prefers K = 2 (a simple "active vs inactive" split), but K = 4 recovers the real VIP group almost exactly (200 customers, recency 11 days, spend ≈ 2,600) plus regular, occasional and at-risk customers. Choose K with statistics and business sense.
2. K-Means always finds clusters — even in noise. If you simulate customers with independent random features (no real groups), K-Means still returns neat-looking "segments"; we tried it and the best silhouette was only 0.19. A low silhouette (below about 0.25) is a warning that the segments may not be real.

1. Silhouette आख़िरी जज नहीं है। यह K = 2 पसंद करता है (सरल "active vs inactive" बँटवारा), पर K = 4 असली VIP group को लगभग ठीक-ठीक पकड़ लेता है (200 customers, recency 11 दिन, खर्च ≈ 2,600), साथ में regular, occasional और at-risk customers भी। K को statistics और business समझ दोनों से चुनें।
2. K-Means हमेशा clusters खोज लेता है — noise में भी। अगर आप customers को स्वतंत्र random features के साथ simulate करें (कोई असली group नहीं), तो भी K-Means साफ़-सुथरे दिखने वाले "segments" लौटाता है; हमने आज़माया और सबसे अच्छा silhouette सिर्फ़ 0.19 था। कम silhouette (लगभग 0.25 से नीचे) चेतावनी है कि segments शायद असली नहीं हैं।

1. 轮廓系数不是最终裁判。它偏好 K = 2(简单的"活跃 vs 不活跃"划分),但 K = 4 几乎完美地还原了真实的 VIP 群体 (200 位客户,最近购买 11 天前,消费约 2,600),以及常规、偶尔和流失风险客户。选择 K 要同时依靠统计和业务判断。
2. K-Means 总能找到簇 —— 即使在噪声里。如果用相互独立的随机特征模拟客户(没有真实分组),K-Means 仍会返回看起来很整齐的"客户群"; 我们试过,最佳轮廓系数只有 0.19。轮廓系数低(大约低于 0.25)就是一个警告:这些分群可能并不真实。


SectionSection第 12 节

Algorithm Comparison — When to Use WhatAlgorithms की तुलना — कब क्या इस्तेमाल करें算法对比 — 何时用什么

AlgorithmAlgorithm算法Best forकिसके लिए सबसे अच्छा最适合Needs scaling?Scaling चाहिए?需要缩放?Main settingमुख्य setting主要参数Handles noise?Noise संभालता है?能处理噪声?
K-MeansLarge data, round clusters, fast baselineबड़ा data, गोल clusters, तेज़ baseline大数据、圆形簇、快速基线Yesहाँ是n_clustersNoनहीं否
DBSCANOdd shapes, unknown K, noise pointsअजीब आकार, अनजान K, noise points不规则形状、K 未知、有噪声点Yesहाँ是eps, min_samplesYes — label −1हाँ — label −1是 —— 标签 −1
HierarchicalSmall–medium data, need the full merge treeछोटा–मध्यम data, पूरा merge tree चाहिए中小数据、需要完整合并树Recommendedसलाह दी जाती है建议linkage + cutlinkage + cut连接方式 + 切分Partly (single linkage)कुछ हद तक (single linkage)部分(单连接)
PCALinear compression, preprocessing, denoisingLinear compression, preprocessing, denoising线性压缩、预处理、去噪Usually (not for same-unit data)आमतौर पर (एक unit वाले data पर नहीं)通常需要(同单位数据除外)n_componentsPartlyकुछ हद तक部分
t-SNE2-D/3-D visualisation onlyसिर्फ़ 2-D/3-D visualisation仅用于二维/三维可视化Usuallyआमतौर पर通常perplexityPartlyकुछ हद तक部分
AutoencoderNon-linear compression, denoising, anomaly scoresNon-linear compression, denoising, anomaly scores非线性压缩、去噪、异常评分Yes (scale to 0–1 or standardise)हाँ (0–1 या standardise)是(缩放到 0–1 或标准化)latent sizelatent size潜在维度Yes (denoising AE)हाँ (denoising AE)是(去噪自编码器)
Isolation ForestAnomaly / fraud detection on tabular dataTabular data पर anomaly / fraud detection表格数据上的异常 / 欺诈检测Noनहीं否contaminationYes — that is its jobहाँ — यही इसका काम है是 —— 这正是它的任务

SectionSection第 13 节

Golden Rules for Unsupervised LearningUnsupervised Learning के सुनहरे नियम无监督学习的黄金法则

🌿 Unsupervised Learning — Rules to RememberUnsupervised Learning — याद रखने के नियम无监督学习 — 必记规则
1
Scale before distance- and variance-based methods. K-Means, DBSCAN, hierarchical clustering, PCA and t-SNE all depend on feature size. Use StandardScaler by default (and log-transform heavily skewed money columns). Exceptions: features already in one unit (pixels), and tree-based methods like Isolation Forest.
Distance और variance वाले तरीकों से पहले scale करें। K-Means, DBSCAN, hierarchical clustering, PCA और t-SNE सभी features के आकार पर निर्भर हैं। Default में StandardScaler इस्तेमाल करें (और बहुत टेढ़े पैसे वाले columns का log लें)। अपवाद: पहले से एक unit वाले features (pixels), और Isolation Forest जैसे tree-based तरीके।
在基于距离和方差的方法之前先缩放。 K-Means、DBSCAN、层次聚类、PCA 和 t-SNE 都依赖特征的数值大小。默认使用 StandardScaler(并对严重偏斜的金额列取对数)。例外:本来就是同一单位的特征(像素),以及孤立森林这类基于树的方法。
2
Never trust cluster labels alone — always profile. A cluster number means nothing. Compute per-cluster statistics on the original (unscaled) features, and look at examples from each cluster, before giving it a name.
सिर्फ़ cluster labels पर भरोसा न करें — हमेशा profile बनाएँ। Cluster number का कोई मतलब नहीं। नाम देने से पहले original (बिना scale) features पर हर cluster के statistics निकालें, और हर cluster के उदाहरण देखें।
永远不要只看簇标签 —— 一定要画像。 簇编号本身毫无意义。在命名之前,先用原始(未缩放)特征计算每个簇的统计量,并看看每个簇的样例。
3
Reduce dimensions before clustering wide data. In 100+ dimensions distances become similar ("curse of dimensionality"). PCA (or another reduction) often makes K-Means faster and more stable. Check that it actually helps on your data.
चौड़े data को cluster करने से पहले dimensions घटाएँ। 100+ dimensions में दूरियाँ एक जैसी हो जाती हैं ("curse of dimensionality")। PCA (या कोई और reduction) अक्सर K-Means को तेज़ और ज़्यादा stable बनाता है। जाँचें कि आपके data पर इससे सच में फ़ायदा होता है।
对宽表聚类之前先降维。 在 100 维以上时,距离会变得差不多("维度灾难")。PCA(或其他降维方法)常常让 K-Means 更快、更稳定。要确认它在你的数据上确实有帮助。
4
t-SNE is for looking, not for features. Its distances are meaningful only locally, and it cannot transform new data. Do not feed t-SNE output into a model; use PCA, UMAP or an autoencoder for pipelines.
t-SNE देखने के लिए है, features के लिए नहीं। इसकी दूरियों का मतलब सिर्फ़ आसपास (locally) होता है, और यह नया data transform नहीं कर सकता। t-SNE output को model में न डालें; pipelines के लिए PCA, UMAP या autoencoder इस्तेमाल करें।
t-SNE 用来看,不用来做特征。 它的距离只在局部有意义,而且不能变换新数据。不要把 t-SNE 的输出喂给模型;流水线中请使用 PCA、UMAP 或自编码器。
5
Judge results with domain knowledge, not just metrics. Silhouette and inertia measure compactness, not business value. Four segments the team can act on beat eight segments with a slightly higher score. And a low silhouette warns that the clusters may not be real at all.
नतीजों को सिर्फ़ metrics से नहीं, domain की समझ से भी परखें। Silhouette और inertia सघनता मापते हैं, business की क़ीमत नहीं। चार segments जिन पर team काम कर सके, थोड़े ऊँचे score वाले आठ segments से बेहतर हैं। और कम silhouette चेतावनी है कि clusters शायद असली ही नहीं हैं।
用领域知识评判结果,而不只看指标。 轮廓系数和惯性衡量的是紧凑程度,而不是业务价值。团队能执行的 4 个客户群,胜过分数稍高的 8 个。而且轮廓系数低是在警告:这些簇可能根本不真实。
6
Set random_state on everything random. K-Means, t-SNE and Isolation Forest all start randomly. Without a fixed seed, results change every run and debugging becomes impossible. For K-Means, also keep n_init above 1.
हर random चीज़ पर random_state set करें। K-Means, t-SNE और Isolation Forest सभी random शुरुआत करते हैं। तय seed के बिना हर बार नतीजे बदलते हैं और debugging असंभव हो जाती है। K-Means के लिए n_init को भी 1 से ज़्यादा रखें।
对所有随机过程设置 random_state。 K-Means、t-SNE 和孤立森林都有随机初始化。不固定随机种子,每次结果都不同,调试将无从下手。对 K-Means,还要让 n_init 大于 1。
7
For anomaly detection, treat scores as a ranking. Set contamination from a realistic expected rate (fraud rate, fault rate) rather than guessing, or keep "auto" and choose your own threshold on score_samples based on how many alerts the team can review.
Anomaly detection में scores को ranking की तरह देखें। contamination को अंदाज़े से नहीं, असली अपेक्षित दर (fraud दर, fault दर) से set करें, या "auto" रखें और score_samples पर अपनी threshold चुनें — इस हिसाब से कि team कितने alerts देख सकती है।
做异常检测时,把分数当作排序。 根据真实的预期比例(欺诈率、故障率)设置 contamination,而不是瞎猜;或者保留 "auto",根据团队能处理多少警报,在 score_samples 上自己选阈值。