python聚类分析代码,python实现聚类实例讲解
K-means算法是最经典的基于划分的聚类方法,是十大经典数据挖掘算法之一。以下文章主要介绍Python自定义索引聚类的相关信息,通过示例代码详细介绍。有需要的朋友可以参考一下。
00-1010前言和KMeans比较Yolo检测盒聚类摘要
目录
最近研究Yolov2的论文时,发现作者在先验盒聚类中使用的指标是IOU,而不是欧氏距离。找了很多资料,基本确定Python没有自定义索引聚类的功能,所以打算自己做一个。
设训练集的形状为[n _样本,n _特征]。基本思想是:
聚类中心初始化:第一个聚类中心样本的特征均值,shape=[n _ feature,];从第二个聚类中心开始,用距离函数(自定义)计算每个样本到最近中心点的距离,归一化值作为选择下一个聚类中心的概率。3354次迭代,直到选择了足够多的聚类中心。聚类中心调整:多轮训练,每轮以样本到最近中心点的距离之和为损失,梯度下降的Adam优化器逼近最优解,当损失浮动值小于阈值的次数达到一定值时停止训练,因为设计之初就打算使用自定义距离函数。作者天赋不高,最后决定利用PyTorch自动推导的天然优势。
首先给出了欧氏距离的计算函数。
def Eu_dist(数据,中心):
以欧氏距离为聚类标准的距离计算函数
[n _样本,n _特征]形式的Data:张量
形状像[n_cluster,n_feature]的Center:张量“”
data=data . unsqueze(1)
center=center . unsqueze(0)
dist=((数据中心)** 2)。sum(dim=2)
返回区
然后就是集群的代码:用的时候只注意__init__,fit和classify函数。
进口火炬
将numpy作为np导入
将matplotlib.pyplot作为plt导入
亚当=torch.optim.Adam
def get_progress(当前,目标,bar_len=30):
当前:当前已完成的任务数
target:任务总数
Bar_len:进度栏长度
Return:进度条字符串
断言当前=目标
百分比=四舍五入(当前/目标* 100,1)
单位=100/bar_len
solid=int(百分比/单位)
空心=条形透镜-实心
返回 *实心 *空心f { current }/{ target }({ percent } %)
类别集群:
群集
N_cluster:集群中心编号
Dist_fun:距离计算函数
kwargs:
[n_sample,n_feather]形式的Data:张量
Center:张量形状像[n_cluster,n_feature]
Return:形状类似[n_sample,n_cluster]的张量
Init:初始聚类中心
最大迭代次数
Lr:中心点坐标学习率
Stop_thresh:停止训练的损失浮动阈值
集群中心3360集群中心
Labels_:聚类结果
def __init__(self,n_cluster,dist_fun,init=None,max_iter=300,lr=0.08,stop_thresh=1e-4):
自我。_ n _群集=n _群集
自我。_dist_fu
n = dist_fun
self._max_iter = max_iter
self._lr = lr
self._stop_thresh = stop_thresh
# 初始化参数
self.cluster_centers_ = None if init is None else torch.FloatTensor(init)
self.labels_ = None
self._bar_len = 20
def fit(self, data):
""" data: 形如 [n_sample, n_feature] 的 tensor
return: loss浮动日志"""
if self.cluster_centers_ is None:
self._init_cluster(data, self._max_iter // 5)
log = self._train(data, self._max_iter, self._lr)
# 开始若干轮次的训练,得到loss浮动日志
return log
def classify(self, data, show=False):
""" data: 形如 [n_sample, n_feature] 的 tensor
show: 绘制分类结果
return: 分类标签"""
dist = self._dist_fun(data, self.cluster_centers_)
self.labels_ = dist.argmin(axis=1)
# 将标签加载到实例属性
if show:
for idx in range(self._n_cluster):
container = data[self.labels_ == idx]
plt.scatter(container[:, 0], container[:, 1], alpha=0.7)
plt.scatter(self.cluster_centers_[:, 0], self.cluster_centers_[:, 1], c="gold", marker="p", s=50)
plt.show()
return self.labels_
def _init_cluster(self, data, epochs):
self.cluster_centers_ = data.mean(dim=0).reshape(1, -1)
for idx in range(1, self._n_cluster):
dist = np.array(self._dist_fun(data, self.cluster_centers_).min(dim=1)[0])
new_cluster = data[np.random.choice(range(data.shape[0]), p=dist / dist.sum())].reshape(1, -1)
# 取新的中心点
self.cluster_centers_ = torch.cat([self.cluster_centers_, new_cluster], dim=0)
progress = get_progress(idx, self._n_cluster, bar_len=self._n_cluster if self._n_cluster <= self._bar_len else self._bar_len)
print(f"\rCluster Init: {progress}", end="")
self._train(data, epochs, self._lr * 2.5, init=True)
# 初始化簇中心时使用较大的lr
def _train(self, data, epochs, lr, init=False):
center = self.cluster_centers_.cuda()
center.requires_grad = True
data = data.cuda()
optimizer = Adam([center], lr=lr)
# 将中心数据加载到 GPU 上
init_patience = int(epochs ** 0.5)
patience = init_patience
update_log = []
min_loss = np.inf
for epoch in range(epochs):
# 对样本分类并更新中心点
sample_dist = self._dist_fun(data, center).min(dim=1)
self.labels_ = sample_dist[1]
loss = sum([sample_dist[0][self.labels_ == idx].mean() for idx in range(len(center))])
# loss 函数: 所有样本到中心点的最小距离和 - 中心点间的最小间隔
loss.backward()
optimizer.step()
optimizer.zero_grad()
# 反向传播梯度更新中心点
loss = loss.item()
progress = min_loss - loss
update_log.append(progress)
if progress > 0:
self.cluster_centers_ = center.cpu().detach()
min_loss = loss
# 脱离计算图后记录中心点
if progress < self._stop_thresh:
patience -= 1
# 耐心值减少
if patience < 0:
break
# 耐心值归零时退出
else:
patience = init_patience
# 恢复耐心值
progress = get_progress(init_patience - patience, init_patience, bar_len=self._bar_len)
if not init:
print(f"\rCluster: {progress}\titer: {epoch + 1}", end="")
if not init:
print("")
return torch.FloatTensor(update_log)
与KMeans++比较
KMeans++ 是以欧式距离为聚类准则的经典聚类算法。在 iris 数据集上,KMeans++ 远远快于我的聚类器。但在我反复对比测试的几轮里,我的聚类器精度也是不差的 —— 可以看到下图里的聚类结果完全一致
[5.0060, 3.4280, 1.4620, 0.2460],
[6.8500, 3.0737, 5.7421, 2.0711]]
[5.0063, 3.4284, 1.4617, 0.2463],
[6.8500, 3.0741, 5.7420, 2.0714]]
虽然速度方面与老牌算法对比的确不行,但是我的这个聚类器最大的亮点还是自定义距离函数
Yolo 检测框聚类
本来想用 Yolov4 检测框聚类引入的CIoU 做聚类,但是没法解决梯度弥散的问题,所以退其次用了 DIoU
def DIoU_dist(boxes, anchor):""" 以 DIoU 为聚类准则的距离计算函数
boxes: 形如 [n_sample, 2] 的 tensor
anchor: 形如 [n_cluster, 2] 的 tensor"""
n_sample = boxes.shape[0]
n_cluster = anchor.shape[0]
dist = Eu_dist(boxes, anchor)
# 计算欧式距离
union_inter = torch.prod(boxes, dim=1).reshape(-1, 1) + torch.prod(anchor, dim=1).reshape(1, -1)
boxes = boxes.unsqueeze(1).repeat(1, n_cluster, 1)
anchor = anchor.unsqueeze(0).repeat(n_sample, 1, 1)
compare = torch.stack([boxes, anchor], dim=2)
# 组合检测框与 anchor 的信息
diag = torch.sum(compare.max(dim=2)[0] ** 2, dim=2)
dist /= diag
# 计算外接矩形的对角线长度
inter = torch.prod(compare.min(dim=2)[0], dim=2)
iou = inter / (union_inter - inter)
# 计算 IoU
dist += 1 - iou
return dist
我提取了DroneVehicle 数据集的 650156 个预测框的尺寸做聚类,在这个过程中发现因为小尺寸的预测框过多,导致聚类中心聚集在原点附近。所以对 loss 函数做了改进:先分类,再计算每个分类下的最大距离之和
横轴表示检测框的宽度,纵轴表示检测框的高度,其数值都是相对于原图尺寸的比例。若原图尺寸为 608 * 608,则得到的 9 个先验框为:
总结
到此这篇关于Python自定义指标聚类的文章就介绍到这了,更多相关Python自定义指标聚类内容请搜索盛行IT软件开发工作室以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT软件开发工作室!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。