第二章 聚类分析基础与无监督学习概念

说实话,很多人一上来就急着敲命令跑聚类,结果出来一堆看不懂的图。为啥?因为没搞明白聚类到底在做什么。我个人习惯是先理清概念再动手,这样哪怕报错也知道从哪查。这一章我们就聊聊无监督学习里的聚类,以及它和Visium空间转录组数据怎么搭上关系。

2.1 无监督学习:没有标签的探索

监督学习就像老师带着做题——给你输入和正确答案,你学会后去预测新题。无监督学习则像是给你一堆散落的照片,没人告诉你谁是谁,只能自己根据相似度分类。聚类就是其中最典型的代表。

我在做单细胞项目时遇到过这种情况:手动标记了上千个细胞类型,结果聚类出来的群和生物学注释对不上。后来才意识到,聚类本身不关心“正确分类”,它只关心数据内在的分布结构。你想想看,这对Visium数据尤为重要——空间位置和基因表达混合在一起,没有先验的“区域标签”。

特性监督学习无监督学习
是否需要标签是否
目标预测/分类发现结构/分组
典型算法随机森林、SVM、神经网络K-means、DBSCAN、层次聚类
Visium中的应用(较少,除非有病理标注)Spot聚类、空间域识别
无监督学习不是“胡乱分”,而是让数据自己说话。Visium数据的聚类,本质上是在找“哪些spot的基因表达模式相似,且空间上靠近”。

2.2 常用聚类算法:选哪个最顺手?

我们课程里会用到几种算法,我先简单介绍,后续实战再细说参数调优。

K-means

最直观的一种。你告诉它要分几类(K值),它随机挑K个中心,然后迭代优化。速度快,适合大规模数据。但问题也很明显:必须事先指定K,而且对球形分布的数据效果好。Visium的spot通常呈连续过渡,K-means容易把边界硬切。

我建议第一次跑Visium聚类时先用K-means快速摸个底,看看大概能分出几个区域,然后再换其他方法精调。曾经我图省事直接上了层次聚类,等了几小时还没跑完……

DBSCAN

基于密度的算法。它不需要你告诉它分几类,只需要设置半径和最少点数。它能自动识别异常点(噪声),对非球形分布适应好。Visium数据中,不同组织区域密度可能差异大——比如肿瘤区spots密集,间质区稀疏——DBSCAN在这种场景下表现不错。

层次聚类

可以理解为建立一棵“聚类树”,你从树的不同高度切开得到不同粒度的分组。优点是结果可以可视化(树状图),而且不需要预设K值。缺点:O(n²)复杂度,spot数较多时(比如一万个以上)内存会炸。我个人只在探索性分析时用,比如抽几百个代表性spot。

2.3 距离度量:聚类的“相似”怎么算?

所有聚类算法都依赖“距离”或“相似度”。你用欧氏距离还是余弦相似度,结果可能完全不同。

  • 欧氏距离:最常用,但受量纲影响大。Visium的基因表达矩阵通常先标准化再算。
  • 余弦相似度:只看方向,不考虑绝对大小,适合稀疏的高维表达数据。
  • 曼哈顿距离:对异常值更鲁棒,偶尔用在空间坐标距离上。

举个例子,计算两个spot的基因表达余弦相似度:

import numpy as np

spot_a = np.array([0.1, 0.8, 0.3, 0.0])
spot_b = np.array([0.2, 0.7, 0.4, 0.1])

cos_sim = np.dot(spot_a, spot_b) / (np.linalg.norm(spot_a) * np.linalg.norm(spot_b))
print(f"余弦相似度: {cos_sim:.3f}")
注意:用欧氏距离时务必先对表达矩阵做标准化(比如z-score),不然高表达基因会主导距离计算。曾经我在项目中没标准化就直接聚类,结果全按线粒体基因分了群,完全没生物学意义。

2.4 怎么知道聚类结果好不好?

无监督学习因为没有真值,评估聚类质量只能靠内部指标。常用的有几个:

指标原理推荐值我的使用习惯
轮廓系数衡量类内紧凑 vs 类间分离0.25以上首选,直观
Davies-Bouldin指数类间距离/类内距离的均值越低越好配合轮廓系数一起看
Calinski-Harabasz指数类间方差/类内方差的比值越高越好计算快,但偏好在球形簇

不过话说回来,内部指标只能作为参考。真正的“好”还要看生物学解释——比如聚类出来的区域是否对应已知的组织结构(皮质层、肿瘤核心等)。我通常先用轮廓系数粗筛几个候选K值,再结合空间分布可视化做最终判断。

2.5 Visium聚类复现的整体流程

下面这张SVG图把从原始数据到聚类结果的关键步骤串起来了。注意看,我们后续Docker部署就是要把这一套流程容器化。

Visium聚类分析流程 后续章节将逐一讲解每个步骤在Docker中的部署细节 读取Visium数据 h5 + image + json 预处理与标准化 QC + LogNormalize 降维(PCA) 前30个主成分 聚类算法 K-means / DBSCAN 聚类评估 轮廓系数 + 可视化 空间映射与标注

你看,整个流程并不复杂。但难点在于环境一致性——不同的库版本、不同的CUDA版本,稍有不慎结果就不可复现。这就是为什么我们要用Docker把所有依赖打包,跑出来的聚类结果换个机器也能一模一样。

我个人习惯在进入Docker之前,先在本地用conda搭建一个最简环境跑通流程,确认代码逻辑无误。然后再写Dockerfile固化环境。这样可以少踩很多“镜像构建好了但代码bug没发现”的坑。