• [技术博客] K-Means算法


    遇到的问题

    在对微软(OCR)(api)进行测试的过程中,我发现有时候它并不能分析出一个表格的形态,也就是说不知道每个文本对应在表格中的第几行第几列。但是它可以较为准确的给出这些文本的坐标。

    不过问题是,位于同一行的文本,他们的纵坐标不是完全相同的,但是在一个大致的范围内。位于同一列的坐标也是有这样的问题。比如一个两行三列的表格,得到的(6)个纵坐标可能是这样的:([32, 32, 37, 68, 70, 72])。那么怎么才能较为准确的分辨出哪些纵坐标其实是属于同一行的呢?

    开始想过排序后看相邻差的变化量,但是这个值很难确定,大了会把两行分到一行,小了会把一行划成两行,在咨询了学长以后,学长推荐了我(K-Means)算法,于是对这个算法进行了学习。

    K-Means

    (K-Means)算法是聚类算法中的最常用的一种,算法最大的特点是简单,好理解,运算速度快,但是只能应用于连续型的数据,并且一定要在聚类前需要手工指定要分成几类。

    ​聚类算法完全是靠算法自己来判断各条数据之间的相似性,相似的就放在一起。非常适合于解决我遇到的问题。

    算法流程

    • 随机选取(k)个中心点
    • 遍历所有数据,将每个数据划分到最近的中心点中
    • 计算每个聚类的平均值,并作为新的中心点
    • 重复(2sim 3)步,直到这(k)个中心点不再变化,或迭代次数足够多

    如下图所示:

    img

    ((a))为初始的数据集,图((b))中随机选取了(2)个中心点,一个标红一个标蓝。接下来把离红点近的绿点标红,把离蓝点近的绿点标蓝。计算红点的质心和蓝点的质心作为新的中心点并不断迭代,最终得到图((f))

    应用K-Means解决问题

    ​可以看出,横、纵坐标互相独立,于是把他们分开处理。相当于在一维的直线上做聚类。

    ​以下以对纵坐标处理为例进行说明。

    K值的确定

    最开始时打算使用行数作为(K)值,但是经过实测,这样的聚类效果并不好,而且中心点难以选取。经过思考,决定把纵坐标排序以后相邻的作差。最终分为两类,一类差值表示他们是同一行之内的坐标差值,设他们的最大值为(eps),另一类的差值表示它们是不同行之间的坐标差值。最终认为两个坐标的差值(leq eps)则表示它们属于同一行。

    于是(k)值被设定为2。这样在只有一行或者一列的话会容易出问题,不过这种表格比较少见而且没有太大意义,所以暂不考虑(没想到咋处理)。

    初始质心的选择

    随机的话我担心有时候可能效果不好,于是固定为最小的差和最大的差。实测效果还行。

    距离计算

    ​因为是在一维的直线上,所以采取差的绝对值即可。

    代码实现

    def distEclud(a, b):
        return abs(a - b)
    
    def K_Means(arr):
        K = 2
        p = []
        arr.sort()
        N = len(arr) - 1
        for i in range(0, N):
            p.append(arr[i + 1] - arr[i])
        p.sort()
        cluster = [p[0], p[N - 1]]
        pos = [1 for i in range(0, N)]
        for t in range(1, 101):
            for i in range(0, N):
                pos[i] = 0
                for j in range(1, K):
                    if distEclud(cluster[pos[i]], p[i]) > distEclud(cluster[j], p[i]):
                        pos[i] = j
            num = [0 for i in range(0, K)]
            cluster = [0 for i in range(0, K)]
            for i in range(0, N):
                num[pos[i]] += 1
                cluster[pos[i]] += p[i]
            for i in range(0, K):
                cluster[i] /= num[i]
        eps = 0
        for i in range(N):
            if pos[i] == pos[0]:
                eps = max(eps, p[i])
        cnt = 0
        dic = {}
        lastVal = -2147483647
        for i in range(N + 1):
            if arr[i] - lastVal > eps:
                cnt = cnt + 1
            dic[arr[i]] = cnt
            lastVal = arr[i]
        dic["size"] = cnt
        return dic
    
  • 相关阅读:
    使用shutdown命令实现局域网内远程关机、重启整蛊他人
    在foxmail和outlook中设置QQ邮箱、gmail邮箱、新浪邮箱、微软邮箱、网易邮箱等的方法
    万能驱动助理篡改主页为2345的解决办法
    巧用UserAgent来解决浏览器的各种问题
    各大浏览器保存密码的文件
    使用代理软件之后其他软件不能联网的解决方法
    windows xp/7/8/8.1/10安全模式详解和系统修复讲解
    VirtualBox更改默认路径
    Virtualbox中不能为虚拟机打开一个新任务的原因及解决方法
    xampp打开显示缺少运行库的解决方法
  • 原文地址:https://www.cnblogs.com/MountVoom/p/12953749.html
Copyright © 2020-2023  润新知