科研工具合集
t-SNE是一种用于探索高维数据结构的非线性降维技术。它特别适用于高维数据的可视化,因为它能够在低维空间中保留原始高维数据的局部结构。由于这个特性,t-SNE在机器学习和数据分析领域越来越受到重视。
1算法解读:
t-SNE的核心思想是在高维空间中为数据点之间定义一种概率分布,表示点与点之间的相似性,然后在低维空间中创建一个相似的概率分布。通过最小化这两个分布之间的差异(使用KL散度),算法将高维数据映射到低维空间,以便我们可以可视化。
2步骤和细节:
Step1.计算高维空间中的相似度
我们使用高斯分布(正态分布)来计算点之间的相似性。高斯分布是一种常见的概率分布,其形状呈钟型,由均值和方差(标准差的平方)决定。高斯分布有一个很好的性质:它的形状由均值(中心点)和方差(分布的宽度)决定。当我们围绕一个数据点x画一个高斯分布时,这个分布会给予附近的点较高的概率值,而离得远的点则会有较低的概率值。这与我们直觉上对“相似性”的理解相一致:靠近的点更相似,远离的点不相似。
对于每个数据点xi,我们计算所有其他点xj与其的条件概率pj∣i。这个概率反映了点xj是点xi的近邻的可能性。计算公式为:
pj∣i=∑k=iexp(−∥xi−xk∥2/2σi2)exp(−∥xi−xj∥2/2σi2)
这里,分子部分计算了xi和xj之间的欧氏距离的平方(即−∥xi−xj∥2),然后通过高斯分布转换成概率。分母部分是一个归一化因子,确保所有pj∣i的和为1。
σi是高斯分布的方差,决定了近邻的范围。不同的点可能有不同的密度,因此σi对于每个点xi可能是不同的,需要通过一种叫做“困惑度”的量来确定。
最后,为了得到一个对称的相似度矩阵,我们取pj∣i和pi∣j的平均值得到pij:
pij=2npj∣i+pi∣j
这样,我们就得到了一个对称的相似度矩阵,其中的每个元素pij都反映了数据点xi和xj在高维空间中的相似性。通过这一步,我们成功地量化了高维空间中数据点之间的相似性,为后续的低维空间嵌入奠定了基础。
Step2.初始化低维空间的点
这一步可以是随机初始化,只需保证初始化点的数量和原数据相同,维度更低即可。
Step3.计算低维空间的点的相似度
在t-SNE算法中,高维空间的相似度是通过高斯(正态)分布计算的,而低维空间的相似度是通过t分布(具体来说是自由度为1的t分布,也叫做柯西分布)计算的。这种设计的目的是为了解决“拥挤问题”。
当我们将高维空间中的数据点降维到低维空间时,数据点之间的距离会发生变化。特别是在低维空间中,点与点之间可用的空间更少,容易出现拥挤的情况。如果直接使用高斯分布来计算低维空间的相似度,那么低维空间中远离的点之间的相似度可能会被过高地估计,导致降维结果的可视化效果不佳。
t分布(自由度为1)有一个重要的特性:它的尾部比高斯分布更“厚”(heavy-tailed)。这意味着,在低维空间中,即使两个点距离较远,它们之间的相似度(通过t分布计算)也不会迅速减小到0。这有助于缓解拥挤问题,因为低维空间中远离的点之间的相似度会被较低地估计。
在低维空间中,我们计算点yi和yj之间的相似度qij如下:
qij=∑k=l(1+∥yk−yl∥2)−1(1+∥yi−yj∥2)−1
这个公式来源于自由度为1的t分布。分子部分计算了yi和yj之间的欧氏距离的平方,并转换成了概率。分母部分是一个归一化因子,确保所有的qij之和为1。通过这种方式,我们得到了低维空间中点之间的相似度矩阵{qij}。接下来,t-SNE算法会试图使高维空间的相似度矩阵{pij}和低维空间的相似度矩阵{qij}尽可能地一致,从而得到合适的低维空间表示。
Step4.优化低维空间的点的位置
通过最小化Kullback-Leibler散度(KL散度)来优化低维空间中的点的位置。KL散度用于衡量高维空间和低维空间中的相似度分布之间的差异。
C=i=j∑pijlogqijpij
使用梯度下降方法来最小化KL散度,更新低维空间中的点的位置。
δyiδC=4j∑(pij−qij)(yi−yj)(1+∥yi−yj∥2)−1
在梯度下降的计算中,输入是低维空间中每个点的坐标{yj}。这些坐标是我们要优化的参数。输出是低维空间中点与点之间的相似度{qij}。这些相似度是由当前的低维坐标{yj}计算出来的。标签是高维空间中点与点之间的相似度{pij}。这些相似度是已知的,因为它们是由原始高维数据计算得出的。我们的目标是通过调整低维空间中的点的坐标{yj}(即输入),使得由这些坐标计算出的相似度{qij}(即输出)尽可能接近已知的高维空间的相似度{pij}(即标签)。
为了实现这个目标,我们计算损失函数(即KL散度)相对于每个低维坐标的梯度,并使用这个梯度来更新低维坐标。这个过程会重复进行,直到达到预定的迭代次数,或者低维坐标的变化小于某个阈值。
3. 代码实现
import numpy as np
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
# 假设 features 是你的特征向量,形状为 (256, 256, 64)
features = np.random.rand(256, 256, 64)
# 假设 mask 是你的掩码数组,形状为 (256, 256),由0和1组成
mask = np.random.randint(0, 2, size=(256, 256))
# 重塑特征向量为 (65536, 64)
reshaped_features = features.reshape(-1, 64) # 65536 = 256 * 256
# 将掩码展平为1D数组
flattened_mask = mask.flatten()
# 对特征向量应用 t-SNE
tsne = TSNE(n_components=2, random_state=42)
features_2d = tsne.fit_transform(reshaped_features)
# 根据掩码的值来选择颜色
colors = np.where(flattened_mask == 1, 'red', 'blue')
# 可视化点云,不同的掩码值对应不同的颜色
plt.figure(figsize=(10, 10))
plt.scatter(features_2d[:, 0], features_2d[:, 1], c=colors, s=1)
plt.title('t-SNE Visualization of Features with Mask Colors')
plt.show()2. JS散度(Jensen-Shannon divergence)
1) KL散度(Kullback-Leibler divergence)定义
- 两个概率分布(probability distribution)间差异的非对称性度量;
- 参与计算的一个概率分布为真实分布,另一个为理论(拟合)分布,相对熵表示使用理论分布拟合真实分布时产生的信息损耗。
- KL散度(Kullback-Leibler divergence),也称为相对熵(relative entropy),是用来衡量两个概率分布之间差异的一种指标。在机器学习中,KL散度常常用于度量两个概率分布之间的相似度或差异性。
KL[P(X)∣∣Q(X)]=x∈X∑[P(x)logQ(x)P(x)]=Ex∼P(x)[logQ(x)P(x)]
在这种情况下,KL散度衡量了教师模型的输出分布q和学生模型的输出分布p之间的差异。通过最小化KL散度损失,学生模型被鼓励从教师模型中学习,并产生相似的输出分布。
此外,KL散度还经常用于变分自编码器(VAEs)中。VAEs是一种生成模型,它们学习数据的低维表示,可以用于生成新样本。在VAEs中,KL散度被用来鼓励学习到的潜在变量遵循先验分布,例如标准正态分布。这有助于正则化模型并防止过拟合。
聚类:KL散度可以用于聚类,以度量两个聚类之间的差异。在这种情况下,KL散度可以用于评估聚类质量,并指导聚类算法的优化过程。
在上面的概率拟合应用场景下, KL[P∣∣Q] 也被称为前向KL散度(forward Kullback-Leibler Divergence),将 KL[Q∣∣P] 称为反向KL散度(reverse Kullback-Leibler Divergence)。
这里需要注意的是,只有在概率拟合的应用场景下(也就是确定了真实分布和拟合分布两个角色之后),前向KL散度 KL[P∣∣Q]和反向KL散度 KL[Q∣∣P] 的定义才是有意义的,否则二者只是相同公式改变正负号、并交换P和 Q 符号表示之后的平凡结果。
具体情况
- 标准正态分布KL散度计算: N(μ,σ2) 与 N(0,1)
正态分布 N(μ,σ2) 的概率密度函数为:
p(x)=2πσ21e−(x−μ)2/2σ2
标准正态分布 N(0,1)的概率密度函数为:
q(x)=2π1e−2x2
KL散度计算:
KL(N(μ,σ2)∥N(0,1))=∑p(x)logq(x)p(x)=∫2πσ21e−(x−μ)2/2σ2(log2π1e−x2/22πσ21e−(x−μ)2/2σ2)dx=∫2πσ21e−(x−μ)2/2σ2log{σ21exp{21[x2−(x−μ)2/σ2]}}dx=21∫2πσ21e−(x−μ)2/2σ2[−logσ2+x2−(x−μ)2/σ2]dx=21∫p(x)[−logσ2+x2−(x−μ)2/σ2]dx
整个结果分为三项积分,第一项实际上就是−logσ2 乘以概率密度的积分(也就是 1),所以结果是 −logσ2 ;第二项实际是正态分布的二阶矩,熟悉正态分布的朋友应该都清楚正态分布的二阶矩为 μ2+σ2 ;而根据定义,第三项实际上就是“-方差除以方差=-1”。所以总结果就是:
KL(N(μ,σ2)∥N(0,1))=21(−logσ2+μ2+σ2−1)
- 正态分布KL散度计算: N(μ1,σ12) 与 N(μ2,σ22)
KL散度计算:
KL(N(μ1,σ12)∥N(μ2,σ22))=∑p(x)logq(x)p(x)=∫2πσ121e−(x−μ1)2/2σ12(log2πσ221e−(x−μ2)2/2σ222πσ121e−(x−μ1)2/2σ12)dx=∫2πσ121e−(x−μ1)2/2σ12log{σ12σ22exp{21[σ22(x−μ2)2−σ12(x−μ1)2]}}dx=21∫2πσ121e−(x−μ1)2/2σ12[logσ22−logσ12+σ22(x−μ2)2−σ12(x−μ1)2]dx=21∫p(x)[logσ22−logσ12+σ22(x−μ2)2−σ12(x−μ1)2]dx
整个结果分为四项积分,第一项实际上就是 logσ22乘以概率密度的积分(也就是 1),所以结果是logσ22;第二项实际上就是 −logσ12 乘以概率密度的积分(也就是 1),所以结果是 −logσ12 ;第三项实际是异正态分布的二阶矩,熟悉正态分布的朋友应该都清楚异正态分布的二阶矩为 $ \frac{\sigma_{1}{2}+(\mu_{1}-\mu_{2}){2}}{\sigma_{2}^{2}} $ ;而根据定义,第四项实际上就是“-方差除以方差=-1”。所以总结果就是:
KL(N(μ1,σ12)∥N(μ2,σ22))=21(logσ22−logσ12+σ22σ12+(μ1−μ2)2−1)
2) 两类KL散度
考虑到需要用人工设计的近似分布 Qθ(X) 来拟合真实分布 P(X) ,这里下标 θ 强调 Q 是一个受到参数 θ 控制的分布。
例如: Q 是正态分布N(μ,σ2), P 是正态分布 N(μ0,σ02) ,现在希望用 Q 来拟合 P ,其中Q 的均值和方差 {μ,σ2} 就是拟合过程中可以调整的参数θ 。于是基于前向KL和反向KL代价的分布拟合问题分别转化为以下两个优化问题:
- 命题1. 极小化前向KL:argminθKL(P∣∣Qθ) 等价于对参数 θ 的极大似然估计。
- 命题2. 极小化反向KL: argminθKL(Qθ∣∣P) 相当于在要求 Qθ 在拟合 P 的同时尽可能保持单一模态。
首先,证明命题一,过程如下:
argminθKL(P∣∣Q)=argminθ(EX∼P[−logQθ(X)])+H(P(X))=argminθEX∼P[−logQθ(X)]=argmaxθEX∼P[logQθ(X)]≈argmaxθEX∼Pdata[logQθ(X)]
其中 H(P(X))=−∑x[P(x)logP(x)],代表信息熵(Entropy)。上述推导的最终结果正好就是极大似然代价的定义式。
推导过程分析:上面的推导过程中,第2行到第3行利用了 H(P(X)) 是与优化自变量 θ 无关的,故删除该项不会改变最优化问题的解,因此可以直接省略。第3行到第4行则是通过来将求最小值问题转化为求最大值问题消去负号。第4行到第5行利用了机器学习训练中一般假设特征在样本集上的分布可以被近似看作真实分布,即: Pdata(X)≈P(X) 。
综上命题1成立。
其次,证明命题2,推导如下:
argminθKL(Q∣∣P)=argminθ(EX∼Qθ[−logP(X)]+H(Qθ(X)))
观察上面的等式右侧 argminθ 中的两项:
EX∼Qθ[−logP(X)]+H(Qθ(X))
要想令上面两项之和最小,就意味着要找到参数θ 的一个合适的取值,使得上面两项中的每一项 EX∼Qθ[−logP(X)]) 和 H(Qθ(X) 都尽可能小。根据熵的性质可知,当 Qθ(X) 越接近于均匀分布,第二项 H(Qθ(X)的值越大,反之当 Qθ(X) 越去向于单一模态分布(可以通俗理解为单峰分布) H(Qθ(X)的值越小。因此反向KL散度相当于在要求 Qθ(X) 在拟合 P 的同时尽可能保持单一模态。
3) JS散度(Jensen-Shannon divergence)
JS 散度(Jensen-Shannon Divergence,缩写 JSD)是基于 KL 散度(相对熵)的一种统计学度量,能够衡量两个概率分布之间的差异程度。
设概率空间上有两个概率分布 P 和 Q,M=21(P+Q),为P 和 Q的平均,则,P 和 Q 的JS散度定义为
JSD(P∣∣Q)=21DKL(P∣∣M)+21DKL(Q∣∣M),
其中,DKL 表示KL散度。
KL散度的缺点:不是距离、不对称。因此引进JS散度的概念,其取值是0到1之间。由定义可以看出,JS散度是对称的,可以用于衡量两种不同分布之间的差异。JS散度用于生成对抗网络的数学推导上。
3. kmeans++
K-means++算法是对传统的K-means聚类算法的一种改进,它解决了K-means算法的一个主要缺点——对初始聚类中心选择的敏感性。K-means++通过一种更合理的方式来选择初始聚类中心,使得算法更有可能找到全局最优解,而不是陷入局部最优。
K-means++算法流程:
随机选择第一个聚类中心:从数据集中随机选择一个样本点作为第一个聚类中心 K-means++算法c1。
计算距离并选择剩余的聚类中心:
- 对于数据集中的每个点x2,计算它到已选聚类中心的最短距离D(xi)。
- 选择下一个聚类中心cj的概率与 D(xi)2 成正比。这意味着那些距离现有聚类中心较远的点有更大的机会被选为新的聚类中心。
重复上述过程:
- 直到选择了所有k。
公式:
在K-means++中,对于每一个未被选作聚类中心的点x,计算其到最近的聚类中心的距离D(x),定义为:
D(x)=j∈[1,k]min∣∣x−cj∣∣
这里∣∣⋅∣∣表示向量的范数,通常是欧几里得距离:
∣∣x−cj∣∣=d=1∑D(xd−cjd)2
其中 xd和cjd分别是点 x 和聚类中心cj在第d维的坐标值,D是数据的维度。
概率选择新聚类中心:
选择下一个聚类中心的概率与距离平方成正比:
P(cj=x)∝D(x)2P=∑x∈XD(x)2D(x)2
其中D(x)2代表单个点到x其最近的已选质心(centroid)的距离的平方。这里的D(x)是点x到最近的聚类中心的距离。∑x∈XD(x)2是指对数据集中X的所有点x的距离平方D(x)2。
这意味着,如果一个点x到最近的聚类中心的距离较大,那么它被选为下一个聚类中心的概率也较高。
通过这种方式,K-means++算法倾向于在数据集的不同区域选择聚类中心,从而避免了K-means算法可能产生的偏斜聚类中心的问题,提高了算法的稳定性和聚类质量。
4. RPN
区域建议网络(RPN,Region Proposal Network)是Faster-RCNN网络用于提取预选框。
在 Faster R-CNN 中,区域候选网络 (RPN) 每一步的特征大小会随着网络的不同层逐渐减小,下面用具体的特征大小显示 RPN 的各个步骤。假设输入图像大小为 H×W×3),以常见的 VGG16 作为 Backbone 为例:
1. 输入图像 (Image)
- 大小: H×W×3
- H和 W是输入图像的高度和宽度,3 表示 RGB 通道。
2. 特征提取网络 (Backbone, e.g., VGG16)
- VGG16 的卷积层会将输入图像下采样 16 倍 (stride=16),输出的特征图大小为:
- 大小: 16H×16W×512
- 输出的特征图具有 512 个通道。
3. 滑动窗口生成 Anchors
- RPN 在特征图的每个像素上生成多个锚框 (anchors),典型的 anchor 尺寸有 3 种尺度和 3 种长宽比,所以每个像素点会生成 9 个 anchor。
- 大小: $ \frac{H}{16} \times \frac{W}{16} \times 9$
- 9 是每个像素位置生成的 anchor 个数。
4. RPN 分类 (前景/背景)
- RPN 对每个 anchor 进行二分类(目标/非目标),输出一个分类分数(2 个通道)。
- 大小: 16H×16W×18
- 18 是 9个 anchor 的前景和背景类别概率。
5. 边界框回归 (修正 Anchor 坐标)
- RPN 还会预测每个 anchor 的修正参数,输出的是 4 个参数 (中心点坐标和宽高)。
- 大小: 16H×16W×36
- 36 是 9 个 anchor 每个 anchor 对应的 4 个回归参数 (9×4=36)。
6. 候选区域 (RoIs)
- 根据 RPN 分类的输出,对正样本 anchor 进行边界框回归后得到候选区域 (Region of Interest, RoIs)。
- 候选区域数目通常为预定数目(如 2000 个),在 Non-Maximum Suppression (NMS) 之后保留最佳的 RoIs。
- 大小: 约 2000×4
- 每个候选区域用 4 个值表示(x, y, w, h))。
7. 非极大值抑制 (NMS)
- 对生成的候选区域进行 NMS,去除冗余的候选框,只保留高置信度的框。非极大值抑制 (NMS, Non-Maximum Suppression) 是在区域候选网络(RPN)以及物体检测任务中常用的一种后处理算法,目的是去除冗余的候选框,只保留置信度较高且位置相对不重叠的框。以下是 NMS 的主要步骤及其原理:
NMS 处理步骤:
输入候选框 (RoIs):
- 经过 RPN 或目标检测器输出的一系列候选框 (bounding boxes),每个框都有一个置信度分数(表示是否包含物体的概率)。
按置信度排序:
- 将所有候选框按其置信度分数从高到低排序,优先处理高置信度的候选框。
选择最高置信度的框:
- 选择置信度最高的框作为基准框,将其加入最终保留的框列表。
计算重叠区域(IoU):
- 对其他候选框,计算它们与当前基准框的交并比 (IoU, Intersection over Union),即两个框的重叠区域与总区域的比值。IoU 的公式为:
IoU=并集区域交集区域 - 如果 IoU 大于一个预定的阈值(例如 0.7),则认为两个框重叠过多,视为冗余框。
- 对其他候选框,计算它们与当前基准框的交并比 (IoU, Intersection over Union),即两个框的重叠区域与总区域的比值。IoU 的公式为:
删除重叠过多的框:
- 删除与基准框 IoU 大于阈值的框,因为这些框基本上在表示同一个物体。
重复步骤:
- 从剩下的框中选择置信度次高的框作为新的基准框,并重复上述步骤,直到所有的候选框都处理完毕。
输出结果:
- 最后保留下来的框是通过 NMS 过滤掉重叠候选框后的结果。它们代表了高置信度且位置不重叠的候选区域。
关键点:
- IoU 阈值:决定了候选框之间的重叠程度。如果 IoU 大于这个阈值,候选框就会被认为过于相似,冗余框会被删除。
- 置信度分数:用来排序候选框,确保优先保留最有可能包含物体的框。
- NMS 的输出:是去除了重叠候选框的最终区域,通常用于进一步的分类和边界框精修 (refinement)。
图示化流程
[输入候选区域 RoIs]
↓
[按置信度排序]
↓
[选择置信度最高的框]
↓
[计算 IoU]
↓
[删除重叠大的候选框]
↓
[重复以上步骤直到处理完所有框]
↓
[输出最终候选框]NMS 是 RPN 中的重要步骤之一,保证了从大量冗余候选区域中提取高质量的、没有过多重叠的区域用于后续物体检测和分类。
8. 输出候选区域 (用于后续 R-CNN)
- 大小: 约 N×4
- N是通过 NMS 保留下来的 RoI 数量(例如 300 个),4 是每个候选框的坐标。
流程图带上特征大小:
[输入图像 HxWx3]
↓
[特征提取网络 VGG16]
↓
[特征图 H/16 x W/16 x 512]
↓
[生成 Anchor H/16 x W/16 x 9]
↓
[RPN 分类 H/16 x W/16 x 18]
↓
[边界框回归 H/16 x W/16 x 36]
↓
[候选区域 (RoIs) 2000x4]
↓
[非极大值抑制 (NMS)]
↓
[输出 RoIs Nx4]这个流程描述了 RPN 的特征大小变化,Anchors、分类和回归分别对应不同大小的特征。
9. RPN 网络代码示例
import torch
import torch.nn as nn
import torch.nn.functional as F
class RPN(nn.Module):
def __init__(self, in_channels, num_anchors):
super(RPN, self).__init__()
# 3x3 卷积层用于提取特征
self.conv = nn.Conv2d(in_channels, 512, kernel_size=3, padding=1)
# 分类分支 (前景/背景)
self.cls_score = nn.Conv2d(512, num_anchors * 2, kernel_size=1)
# 回归分支 (边界框回归)
self.bbox_pred = nn.Conv2d(512, num_anchors * 4, kernel_size=1)
def forward(self, x):
# 特征提取
x = F.relu(self.conv(x))
# 分类分支输出
cls_score = self.cls_score(x)
# 边界框回归分支输出
bbox_pred = self.bbox_pred(x)
return cls_score, bbox_pred
# 测试示例
if __name__ == "__main__":
# 假设输入特征图的通道数为 256,锚点数为 9
rpn = RPN(in_channels=256, num_anchors=9)
# 模拟输入特征图的大小 (batch_size, channels, height, width)
input_tensor = torch.randn(1, 256, 64, 64)
cls_score, bbox_pred = rpn(input_tensor)
print("Classification score shape:", cls_score.shape)
print("Bounding box prediction shape:", bbox_pred.shape)