聚类
最近我在学习用yolov5来实现行人的检测,为了更好的检测效果我决定对样本标注框进行聚类,首先将手动标注生成的txt文件导入,初始化x和y用来存放标注框的宽和高,标注生成的txt文件在labels_work文件下
import numpy as np
from matplotlib import pyplot
import os
files = os.listdir('labels_work')
x = np.zeros(247)
y = np.zeros(247)
ii = 0
用python打开每一个txt并读取内容,内容中最重要的是标注框的宽和高,这也是我想提取的内容,我习惯处理矩阵,所以在读取后我将数据全部转化为矩阵的形式,去掉每行头尾空白并按换行符分割数据。txt中的数据最终保存为n行五列的矩阵,其中第四列为标注框的宽,第五列为标注框的高,将宽保存到x中,将高保存到y中
for f in files:
txt = open('labels_work/' + f)
data = txt.readlines()
row = len(data)
x_matrix = np.zeros((row, 5)) # 将数据存储为矩阵形式
index = 0
for line in data:
line = line.strip() # 去掉每行头尾空白
line = line.split(" ") # 按换行符分割数据
x_matrix[index, :] = line[0:5]
index += 1
txt.close()
for i in range(row):
x[ii] = x_matrix[i, 3]
y[ii] = x_matrix[i, 4]
ii = ii + 1
提取到想要的数据后可以先画个散点图看看效果:
pyplot.xlim(xmax=1, xmin=0)
pyplot.ylim(ymax=1, ymin=0)
pyplot.scatter(x, y) # 画散点图
pyplot.show()
接下来可以对这些点进行聚类,我决定聚为4类,先随机初始化四个聚类中心,每个点都会对应一个标签,根据标签可以知道这个点距离哪个聚类中心最近。初始化误差与迭代次数。
k = 4 # 聚成4类
initialization_x = np.random.random(k) # 初始化中心坐标
initialization_y = np.random.random(k) # 初始化中心坐标
m, = x.shape
labels = np.zeros(m) # 初始化标签
error = 100
time = 1
接下来开始聚类,直到误差小于某个值就结束迭代,计算每个点到聚类中心的距离。这样每个点就对应有4个距离,这个点会分配到距离最小的聚类中心,将此聚类中心保存到此点对应的标签中,所有的点分配完后进行误差的计算。例如在第一类中,将所有分配到此类中的点的x坐标值相加再取平均,
y坐标也相加取平均,这样就会产生一个新的坐标点,将此坐标点与第一类的聚类中心作比较,计算两者的距离作为误差,第二类第三类与第四类都是这样计算误差,最后根据这些误差判断是否继续进行迭代,如果继续进行迭代,那么更新聚类中心,将产生的四个新坐标作为下一次迭代的聚类中心。
while error > 0.1: # 定义精度
for i in range(m):
distance = np.zeros(k)
for j in range(k):
distance[j] = (x[i] - initialization_x[j]) ** 2 + (y[i] - initialization_y[j]) ** 2
distance[j] = distance[j] ** 0.5
index = 0
min_dis = distance[0]
for j in range(k):
if distance[j] < min_dis:
index = j
labels[i] = index # 分类
class_label = np.zeros(k)
sum_x = np.zeros(k)
sum_y = np.zeros(k)
ave_x = np.zeros(k)
ave_y = np.zeros(k)
for i in range(m):
z = labels[i]
z = int(z)
sum_x[z] = sum_x[z] + x[i]
sum_y[z] = sum_y[z] + y[i]
class_label[z] = class_label[z] + 1
for i in range(k):
if class_label[i] != 0:
ave_x[i] = sum_x[i] / class_label[i]
ave_y[i] = sum_y[i] / class_label[i]
error_x = initialization_x - ave_x
error_y = initialization_y - ave_y
error = sum(abs(error_y)) + sum(abs(error_x))
if error > 0.1:
initialization_x = ave_x
initialization_y = ave_y
print("此次迭代的误差为:", error)
time += 1
if time > 100:
break
在运行时发现了一个问题,就是不断的迭代中误差已收敛但无法达到循环结束的条件就是误差不小于0.1,这样我用if来强制结束循环,使迭代次数不能超过一百次。迭代结束后输出一下迭代次数与聚类中心
print("共迭代次数为", time-1)
print("聚类中心的x坐标")
print(initialization_x)
print("聚类中心的y坐标")
print(initialization_y)
将聚类结果可视化:
colors = ['r', 'b', 'y', 'c']
for i in range(m):
z = labels[i]
z = int(z)
pyplot.scatter(x[i], y[i], c=colors[z])
pyplot.show()
缺点:结果不稳定,每次运行时的聚类结果会有差异,需要进行改进。