目录
[一、k-means 算法](#一、k-means 算法)
一、k-means 算法
k-means 是一种无监督聚类算法,常用的聚类算法还有 DBSCAN。k-means 由于其原理简单,可解释强,实现方便,收敛速度快,在数据挖掘、数据分析、异常检测、模式识别、金融风控、数据科学、智能营销和数据运营等领域有着广泛的应用。具体实现步骤为:
- 设定 K 个类别的中心的初值;
- 计算每个样本到 K个中心的距离,按最近距离进行分类;
- 以每个类别中样本的均值,更新该类别的中心;
- 重复迭代以上步骤,直到达到终止条件(迭代次数、最小平方误差、簇中心点变化率)。
k-means 算法可以通过纯原生 python 语法实现,也可以通过 numpy 科学数据包实现。
纯原生 python 语法实现:
python
# 定义 List 求距离的函数
def getDistance(point1, point2, dim):
sum = 0
for i in range(dim):
sum += (point1[i]-point2[i])**2
return pow(sum, 0.5)
# 定义 List 求和的函数,引用调用
def getSum(point1, point2, dim):
for i in range(dim):
point1[i] += point2[i]
# 定义 List 求除法的函数,引用调用
def getDiv(point1, v, dim):
for i in range(dim):
point1[i] /= v
# 定义 List 深拷贝的函数
def deepCopy(det1, det2):
m, n = len(det1), len(det1[0])
for i in range(m):
for j in range(n):
det1[i][j] = det2[i][j]
# 定义主函数
def kmeans(dets, k, n):
nums, dim = len(dets), len(dets[0])
# 初始化旧的聚类中心,默认前k
center_old = []
for i in range(k):
center_old.append(dets[i][:]) # [:]是浅拷贝 == copy()
# 初始化新的聚类中心
center_new = []
for i in range(k):
center_new.append([0]*dim)
# 初始化一个记录的 List
center_num = [0]*dim
# 迭代 n 次
for _ in range(n):
for i in range(nums):
min_dis = 1e10
min_idx = 0
for j in range(k): # 基于最新距离找到第 i 数据的新的类别归属
dis = getDistance(dets[i], center_old[j], dim)
if min_dis > dis:
min_dis = dis
min_idx = j
getSum(center_new[min_idx], dets[i], dim) # 在新的类别归属上累计求和
center_num[min_idx] += 1 # 记录数量,以求均值
for i in range(k): # 求取均值,获得新的聚类中心
getDiv(center_new[i], center_num[i], dim)
deepCopy(center_old, center_new) # 将新的聚类中心赋值给旧的聚类中心
for i in range(k): # 清空新的聚类中心
for j in range(dim):
center_new[i][j] = 0
center_num[i] = 0
return center_old
if __name__ == "__main__":
x = [[1, 2], [1.5, 1.8], [5, 8], [8, 8], [1, 0.6], [9, 11]]
y = kmeans(x, 2, 50)
print(y) # 结果为:[[1.1667, 1.4666], [7.3333, 9.0]]
numpy 科学数据包实现:参考
python
class Kmeans:
def __init__(self, k=2, tolerance=0.01, max_iter=300):
self.k = k
self.tol = tolerance
self.max_iter = max_iter
self.features_count = -1
self.classifications = None
self.centroids = None
def fit(self, data):
"""
:param data: numpy数组,约定shape为:(数据数量,数据维度)
:type data: numpy.ndarray
"""
self.features_count = data.shape[1]
# 初始化聚类中心(维度:k个 * features种数)
self.centroids = np.zeros([self.k, data.shape[1]])
for i in range(self.k):
self.centroids[i] = data[i]
for i in range(self.max_iter):
# 清空聚类列表
self.classifications = [[] for i in range(self.k)]
# 对每个点与聚类中心进行距离计算
for feature_set in data:
# 预测分类
classification = self.predict(feature_set)
# 加入类别
self.classifications[classification].append(feature_set)
# 记录前一次的结果
prev_centroids = np.ndarray.copy(self.centroids)
# 更新中心
for classification in range(self.k):
self.centroids[classification] = np.average(self.classifications[classification], axis=0)
# 检测相邻两次中心的变化情况
for c in range(self.k):
if np.linalg.norm(prev_centroids[c] - self.centroids[c]) > self.tol:
break
# 如果都满足条件(上面循环没break),则返回
else:
return
def predict(self, data):
# 距离
distances = np.linalg.norm(data - self.centroids, axis=1)
# 最小距离索引
return distances.argmin()
二、NMS
非极大值抑制 (Non-maximum supression) 简称 NMS,其作用是去除冗余的检测框,去冗余手段是剔除与极大值重叠较多的检测框结果 。 通常我们所说的 NMS 指的是标准 NMS ,非标准的 NMS 还有 soft NMS 和 softer NMS 参考 参考。那么为什么一定要去冗余呢?因为图像中的目标是多种多样的形状、大小和长宽比,目标检测算法中为了更好的保障目标的召回率,通常会使用 SelectiveSearch、RPN (例如:Faster-RCNN)、Anchor (例如:YOLO) 等方式生成长宽不同、数量较多的候选边界框 (BBOX)。因此在算法预测生成这些边界框后,紧接着需要跟着一个 NMS 后处理算法,进行去冗余操作,为每一个目标输出相对最佳的边界框,依次作为该目标最终检测结果。默认的两个框相似度的评判工具是 IOU ,其它常用的还有 GIOU、DIOU、CIOU、EIOU 参考参考。
一般NMS后处理算法需要经历以下步骤(不含背景类,背景类无需NMS):
step1:先将所有的边界框按照类别进行区分;
step2:把每个类别中的边界框,按照置信度从高到低进行降序排列;
step3:选择某类别所有边界框中置信度最高的边界框bbox1,然后从该类别的所有边界框列表中将该置信度最高的边界框bbox1移除并同时添加到输出列表中;
step4:依次计算该bbox1和该类别边界框列表中剩余的bbox计算IOU;
step5:将IOU与NMS预设阈值Thre进行比较,若某bbox与bbox1的IOU大于Thre,即视为bbox1的"邻域",则在该类别边界框列表中移除该bbox,即去除冗余边界框;
step6:重复step3~step5,直至该类别的所有边界框列表为空,此时即为完成了一个物体类别的遍历;
step7:重复step2~step6,依次完成所有物体类别的NMS后处理过程;
step8:输出列表即为想要输出的检测框,NMS流程结束。
python
import numpy as np
def IOU(point, points):
# 计算交集面积
lu = np.maximum(point[0:2], points[:, 0:2])
rd = np.minimum(point[2:4], points[:, 2:4])
intersection = np.maximum(0, rd-lu)
inter_area = intersection[:,0]*intersection[:,1]
# 计算每个框的单独面积
area1 = (point[2]-point[0])*(point[3]-point[1])
area2 = (points[:,2]-points[:,0])*(points[:,3]-points[:,1])
union_area = np.maximum(area1+area2-inter_area, 1e-10)
# 计算交并比
return inter_area/union_area
def nms(dets, thresh):
points = dets[:,:4]
score = dets[:,4]
order = score.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
ious = IOU(points[i,:], points[order[1:]]) # 当order.size==1时,order[1]会报错,但order[1:]不会报错
inds = np.where(ious<=thresh)[0] # 对于IOU函数,order[1:]不会报错,但np.array([])则会报错
order = order[inds+1] # 定义函数时,不需要特殊处理,numpy会自动帮忙处理这种情况
return keep
if __name__ == "__main__":
dets = np.array([
[30, 10, 200, 200, 0.95],
[25, 15, 180, 220, 0.98],
[35, 40, 190, 170, 0.96],
[60, 60, 90, 90, 0.3],
[20, 30, 40, 50, 0.1],
])
print(nms(dets, 0.5))