The Story That Explains Unsupervised Learningवह कहानी जो Unsupervised Learning समझाती है一个解释无监督学习的故事
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.
कोई नहीं बताता कि कितनी categories बनानी हैं। कोई labelling guide नहीं देता। आप बस covers पढ़ना और patterns देखना शुरू करते हैं। युद्ध और राजनीति की किताबें एक साथ आ जाती हैं। खाना बनाने की किताबें रसोई वाले कोने के पास जमा होती हैं। Science और गणित की किताबें पीछे की दीवार की ओर चली जाती हैं। आप व्यवस्था का तरीका सिर्फ़ किताबों की सामग्री से बनाते हैं।
Unsupervised learning ठीक यही करता है। Algorithm को बिना labels का कच्चा data मिलता है और उसे छिपा ढाँचा खुद खोजना होता है — उसे groups में बाँटना, छोटा (compress) करना, या जो मेल नहीं खाता उसे पकड़ना।
没人告诉你该分几类,也没人给你分类指南。你只能开始看封面、发现规律。 关于战争和政治的书慢慢聚到一起;菜谱集中到厨房角落;科学和数学书搬到后墙。你完全根据书的内容本身发明出一套整理方法。
这正是无监督学习(unsupervised learning)所做的事。算法拿到没有标签的原始数据,必须自己发现其中隐藏的结构 —— 把数据分组、压缩,或者找出不属于这里的东西。
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 हैं?"
在有监督学习中,你给模型成千上万封已标好垃圾邮件 / 非垃圾邮件的邮件,它学习预测标签。 在无监督学习中,你给它同样的邮件,但完全没有标签,然后问:"这里存在哪些自然的分组?"
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 की समझ चाहिए।
无监督学习在无标签数据中寻找隐藏的结构。没有老师,也没有标准答案 —— 只有从数据本身发现的规律、分组、压缩和异常。 正因为没有答案,检验结果才是最难的部分:你总是需要图表、统计和领域知识来判断算法找到的东西是否真实、是否有用。
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 के बारे में एक अलग सवाल का जवाब देता है।
无监督方法分为三大家族,每一类回答关于数据的不同问题。
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 和层次聚类把数据分成自然的组 —— 客户细分、主题聚类、基因表达谱。
PCA, t-SNE and autoencoders turn hundreds of features into a few meaningful ones — for plots, denoising or feature engineering.
PCA, t-SNE और autoencoders सैकड़ों features को कुछ सार्थक features में बदलते हैं — plots, denoising या feature engineering के लिए।
PCA、t-SNE 和自编码器把成百上千个特征变成少数几个有意义的特征 —— 用于作图、去噪或特征工程。
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 学习"正常"数据的样子,并标出任何不符合的东西 —— 欺诈、机器故障、网络入侵。
Clustering — K-Means: The City PlannerClustering — K-Means: शहर का Planner聚类 — K-Means:城市规划师
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.
Planner नक्शे पर random जगह 5 pins लगाकर शुरू करता है। हर घर अपने सबसे पास वाले pin से जुड़ता है। फिर हर pin अपने घरों की औसत जगह पर खिसकता है। घर फिर से जुड़ते हैं। Pins फिर खिसकते हैं। कुछ rounds बाद pins रुक जाते हैं — उन्हें स्वाभाविक केंद्र मिल गए। यही बार-बार चलने वाला "assign → move → reassign" loop ही K-Means है।
规划师先在地图上随机插 5 个图钉。每栋房子归到最近的图钉。然后每个图钉移到它所属房子的平均位置。房子重新归属,图钉再移动。 几轮之后图钉不再移动 —— 它们找到了自然的中心。这个反复的"分配 → 移动 → 再分配"循环就是 K-Means。
k-means++ start by default).
K चुनें, यानी clusters की संख्या। K centroids रखें (scikit-learn default में समझदार k-means++ शुरुआत इस्तेमाल करता है)।
选择簇的数量 K,放置 K 个质心(scikit-learn 默认使用更聪明的 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)。
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)}")
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 依赖距离,单位大的特征会占主导。
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)。
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 查看对应的聚类结果。
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}")
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,最高。真实数据中的肘部常常不明显,所以两个工具都要用,然后再问问这些分组是否有意义。
Clustering — DBSCAN: The Astronomer's LensClustering — DBSCAN: खगोलशास्त्री का Lens聚类 — DBSCAN:天文学家的镜头
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.
उसका तरीका: एक तारा चुनें और गिनें कि एक तय radius ε के अंदर कितने दूसरे तारे हैं। अगर कम से कम minPts हों, तो वह एक core तारा है जो cluster को थामता है। वह हर core तारे से cluster को बाहर की ओर बढ़ाती है और पहुँच वाले सभी पड़ोसियों को जोड़ती है। जिन तारों तक कोई cluster नहीं पहुँचता, उन्हें noise कहा जाता है।
यही DBSCAN है — Density-Based Spatial Clustering of Applications with Noise. यह किसी भी आकार के clusters खोजता है और noise पहचानता है। आपको K कभी नहीं बताना पड़ता।
她的方法:选一颗星,数一数固定半径 ε 内有多少颗其他星。如果至少有 minPts 颗,它就是一颗核心星,可以撑起一个星团。 她从每颗核心星向外扩张,把所有能到达的邻居都加进同一个星团。没有被任何星团覆盖的星被标为噪声。
这就是 DBSCAN(基于密度的带噪声空间聚类)。它能找到任意形状的簇,并且标出噪声。你完全不需要指定 K。
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 比较。
| Propertyगुण属性 | K-Means | DBSCAN |
|---|---|---|
| Number of clustersClusters की संख्या簇的数量 | Must give KK देना ज़रूरी必须指定 K | Found 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_clusters | eps, 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}")
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 —— 和随机猜差不多。
Clustering — Hierarchical: The Family TreeClustering — Hierarchical: वंश-वृक्ष聚类 — 层次聚类:家谱树
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.
वह हर प्रजाति को उसका अपना group मानकर शुरू करती है (200 clusters)। फिर सबसे मिलते-जुलते दो groups को जोड़ती है। फिर अगले सबसे मिलते-जुलते दो को। बार-बार, जब तक सब एक बड़ा group न बन जाए। किसी भी समय वह tree को आड़ा काटकर जितने चाहे उतने clusters पा सकती है — algorithm दोबारा चलाए बिना। नतीजा एक dendrogram है: पूरे merge इतिहास का शाखाओं वाला चित्र।
她一开始把每个物种都当作一个单独的组(200 个簇),然后合并最相似的两个组,再合并接下来最相似的两个……一直到所有物种合成一个大组。 任何时候她都可以把树横着切一刀,得到想要的簇数 —— 无需重新运行算法。结果就是一张树状图(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 个点,橙色线是最新的一次合并。右:逐步生长的树状图,每个连接点的高度就是两组合并时的距离。移动切线来选择簇数,并试试不同的连接方式。
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}")
Dimensionality Reduction — PCA: The Shadow on the WallDimensionality Reduction — PCA: दीवार पर परछाई降维 — PCA:墙上的影子
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.
अब spotlight को तब तक घुमाएँ जब तक परछाई सबसे बड़ी और फैली हुई न हो जाए। यही Principal Component Analysis (PCA) है। यह वह दिशा खोजता है जिसमें आपका data सबसे ज़्यादा बदलता है (PC1), फिर 90° पर अगली (PC2), और इसी तरह आगे — ताकि सैकड़ों features की जगह कुछ principal components ले सकें जो ज़्यादातर variance बचाते हैं।
现在转动聚光灯,直到影子尽可能大、尽可能舒展。这就是主成分分析(PCA)。 它找到数据变化最大的方向(PC1),然后找与之成 90° 的次大方向(PC2),依此类推 —— 于是成百上千个特征可以被少数几个保留了大部分方差的主成分代替。
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}")
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 个,
因为缩放让几乎总是空白的边缘像素与中心像素拥有同样的权重。
Dimensionality Reduction — t-SNE: The Neighbourhood MapperDimensionality Reduction — t-SNE: पड़ोस का नक्शा降维 — t-SNE:邻里地图
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.
t-SNE ठीक यही करता है। यह high-dimensional दूरियों को probabilities में बदलता है — "A, B को पड़ोसी चुने इसकी कितनी संभावना है?" — और फिर 2-D में points को तब तक खिसकाता है जब तक वही पड़ोसी-probabilities न आ जाएँ। सच में अलग groups अलग द्वीप बन जाते हैं। यह जटिल data को देखने के लिए बेहतरीन है, पर compression या दूसरे models के input के लिए नहीं।
t-SNE 做的正是这件事。它把高维距离转换成概率 ——"A 选 B 当邻居的可能性有多大?"—— 然后在二维中移动这些点,直到出现同样的邻居概率。真正不同的组会变成分开的"岛屿"。 它非常适合用来观察复杂数据,但不适合做压缩,也不适合作为其他模型的输入。
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 二维视图比较。
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。
| ✅ 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 देखना发现组内的子组 |
| ❌ 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)
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 值。
Autoencoders — The Art of Compressing and RebuildingAutoencoders — Compress करने और दोबारा बनाने की कला自编码器 — 压缩与重建的艺术
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.
Autoencoder एक neural network है जो यही खेल खेलता है। Encoder input को एक छोटे latent space में दबाता है; decoder उससे input को दोबारा बनाता है। Network इस तरह train होता है कि दोबारा बना output, input के जितना हो सके उतना पास हो — कोई label नहीं चाहिए।
自编码器(autoencoder)就是玩这个游戏的神经网络。编码器把输入压缩到一个很小的潜在空间(latent space), 解码器再从中重建输入。网络通过让重建结果尽可能接近输入来训练 —— 不需要任何标签。
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}")
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 做基线。
Anomaly Detection — Isolation Forest: The Odd One OutAnomaly Detection — Isolation Forest: अलग वाला异常检测 — 孤立森林:找出"另类"
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.
Isolation Forest ठीक यही विचार इस्तेमाल करता है। यह बहुत सारे random trees बनाता है; जो points औसतन छोटे paths (कम splits) में अकेले हो जाते हैं, उन्हें anomaly माना जाता है। इसे "सामान्य" की परिभाषा कभी नहीं चाहिए — सामान्य points बस वे हैं जिन्हें अकेला करना मुश्किल है।
孤立森林(Isolation Forest)正是利用这个思想。它构建很多棵随机树;平均只需很短路径(很少次切分)就被孤立的点会被标记为异常。 它从不需要描述什么是"正常"—— 正常点就是那些很难被孤立的点。
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 个样本)。红色背景 = 异常分数高。点击一个点,观看一棵随机树如何围绕它切分;点击空白处可添加新点。
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}")
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)作为排序往往更有用。
End-to-End Pipeline — Customer SegmentationEnd-to-End Pipeline — Customer Segmentation端到端流程 — 客户细分
The pipeline: fix skewed features → scale → PCA (remove noise) → K-Means → profile each cluster → name and target each segment.
Pipeline: टेढ़े (skewed) features ठीक करें → scale → PCA (noise हटाएँ) → K-Means → हर cluster का profile → हर segment को नाम दें और target करें।
流程:处理偏斜特征 → 缩放 → PCA(去噪)→ K-Means → 给每个簇画像 → 为每个客户群命名并定向营销。
log1p of them first, then apply StandardScaler — recency (days) and spend (₹) are on completely different scales.log1p लें, फिर StandardScaler लगाएँ — recency (दिन) और spend (₹) बिल्कुल अलग scales पर हैं।log1p,再用 StandardScaler —— 天数和金额(₹)的尺度完全不同。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())
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)就是一个警告:这些分群可能并不真实。
Algorithm Comparison — When to Use WhatAlgorithms की तुलना — कब क्या इस्तेमाल करें算法对比 — 何时用什么
| AlgorithmAlgorithm算法 | Best forकिसके लिए सबसे अच्छा最适合 | Needs scaling?Scaling चाहिए?需要缩放? | Main settingमुख्य setting主要参数 | Handles noise?Noise संभालता है?能处理噪声? |
|---|---|---|---|---|
| K-Means | Large data, round clusters, fast baselineबड़ा data, गोल clusters, तेज़ baseline大数据、圆形簇、快速基线 | Yesहाँ是 | n_clusters | Noनहीं否 |
| DBSCAN | Odd shapes, unknown K, noise pointsअजीब आकार, अनजान K, noise points不规则形状、K 未知、有噪声点 | Yesहाँ是 | eps, min_samples | Yes — label −1हाँ — label −1是 —— 标签 −1 |
| Hierarchical | Small–medium data, need the full merge treeछोटा–मध्यम data, पूरा merge tree चाहिए中小数据、需要完整合并树 | Recommendedसलाह दी जाती है建议 | linkage + cutlinkage + cut连接方式 + 切分 | Partly (single linkage)कुछ हद तक (single linkage)部分(单连接) |
| PCA | Linear compression, preprocessing, denoisingLinear compression, preprocessing, denoising线性压缩、预处理、去噪 | Usually (not for same-unit data)आमतौर पर (एक unit वाले data पर नहीं)通常需要(同单位数据除外) | n_components | Partlyकुछ हद तक部分 |
| t-SNE | 2-D/3-D visualisation onlyसिर्फ़ 2-D/3-D visualisation仅用于二维/三维可视化 | Usuallyआमतौर पर通常 | perplexity | Partlyकुछ हद तक部分 |
| Autoencoder | Non-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 Forest | Anomaly / fraud detection on tabular dataTabular data पर anomaly / fraud detection表格数据上的异常 / 欺诈检测 | Noनहीं否 | contamination | Yes — that is its jobहाँ — यही इसका काम है是 —— 这正是它的任务 |
Golden Rules for Unsupervised LearningUnsupervised Learning के सुनहरे नियम无监督学习的黄金法则
StandardScaler by default (and log-transform heavily skewed money columns). Exceptions: features already in one unit (pixels), and tree-based methods like Isolation Forest.StandardScaler इस्तेमाल करें (और बहुत टेढ़े पैसे वाले columns का log लें)। अपवाद: पहले से एक unit वाले features (pixels), और Isolation Forest जैसे tree-based तरीके।StandardScaler(并对严重偏斜的金额列取对数)。例外:本来就是同一单位的特征(像素),以及孤立森林这类基于树的方法。n_init above 1.n_init को भी 1 से ज़्यादा रखें।n_init 大于 1。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.contamination को अंदाज़े से नहीं, असली अपेक्षित दर (fraud दर, fault दर) से set करें, या "auto" रखें और score_samples पर अपनी threshold चुनें — इस हिसाब से कि team कितने alerts देख सकती है।contamination,而不是瞎猜;或者保留 "auto",根据团队能处理多少警报,在 score_samples 上自己选阈值。