深度学习常用代码总结(k-means, NMS)

目录

一、k-means 算法

二、NMS


一、k-means 算法

k-means 是一种无监督聚类算法,常用的聚类算法还有 DBSCAN。k-means 由于其原理简单,可解释强,实现方便,收敛速度快,在数据挖掘、数据分析、异常检测、模式识别、金融风控、数据科学、智能营销和数据运营等领域有着广泛的应用。具体实现步骤为:

  • 设定 K 个类别的中心的初值;
  • 计算每个样本到 K个中心的距离,按最近距离进行分类;
  • 以每个类别中样本的均值,更新该类别的中心;
  • 重复迭代以上步骤,直到达到终止条件(迭代次数、最小平方误差、簇中心点变化率)。

k-means 算法可以通过纯原生 python 语法实现,也可以通过 numpy 科学数据包实现。

纯原生 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 科学数据包实现:参考

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流程结束。

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))

你可能感兴趣的:(深度学习--基本工具,深度学习,kmeans,人工智能,python)