伊江痕 发表于 2024-2-29 21:59:14

机器学习中的KMeans算法实现_AutoLISP版本

本帖最后由 伊江痕 于 2024-3-4 10:22 编辑

算法流程如下:

输入:
    X: 数据集
    K: 簇的数量
    max_iterations: 最大迭代次数
    tolerance: 簇中心变化的容忍度

过程:
1. 初始化:
    从X中随机选择K个数据点作为初始簇中心centroids
2. 迭代直到达到最大迭代次数或簇中心变化小于tolerance:
    a. 对于X中的每个点x_i,找到距离最近的簇中心c_j,并将x_i分配给相应的簇S_j
    b. 对于每个簇S_j,更新簇中心c_j为S_j中所有点的平均值
    c. 计算簇中心的变化量,如果所有簇中心的变化量都小于tolerance,则结束迭代

输出:
    簇分配结果
    簇中心


代码如下:
;在CAD中随机绘制一些半径相同的圆,这些圆最好能肉眼可见的一簇一簇的(效果会好点),当然这些簇之间也不用完全的分离开,可以相互有些交错的那种(真实的数据集肯定不是那么的泾渭分明的),然后运行本代码,框选所有圆即可。
;这里的k代表你想把这些圆分成多少簇。
(defun kmeans(k / max_i)
(setq x (ssget))
;最大迭代次数
(setq max_i 20)
(setq lx (sslength x))
;centroids用来放属于不同簇的圆
(setq centroids nil)
;centroids_core_p用来放簇中心的点坐标
(setq centroids_core_p nil)
;从X中随机选择K个数据点作为初始簇中心centroids
(repeat k
    (setq ent (ssname x (randrange 0 (- lx 1))))
    (setq cen (list ent))
    (setq centroids (append centroids (list cen)))
    (setq ent_p (cdr (assoc 10 (entget ent))))
    (setq centroids_core_p (append centroids_core_p (list ent_p)))
    )
(setq ii 0)
(repeat max_i
    (princ (strcat "第" (itoa ii) "次迭代"))
    (setq i 0)
    (repeat lx
      ;获取选择集中的一个圆圈
      (setq ci (ssname x i))
      (setq cip (cdr (assoc 10 (entget ci))))
      ;分别和centroids_core_p的圆心计算距离,看哪个距离最小就属于哪个簇
      (setq min_dist nil)
      (setq min_index 0)
      (setq j 0)
      (repeat k
      (setq centroidp (nth j centroids_core_p))
      (setq dist (distance cip centroidp))
      (if (/= dist 0)
          (if (or (< dist min_dist) (= min_dist nil))
            (progn
            (setq min_dist dist)
            (setq min_index j)
            )

            )
         
          )
      (setq j (+ j 1))
      )
      ;更新centroids
      (setq centroids
             (subst
               (append (nth min_index centroids) (list ci))
               (nth min_index centroids)
               centroids
               )
             )
      ;更新centroids_core_p
      (setq centroids_min_index (nth min_index centroids))
      (setq center_min_index
             (mapcar
               '(lambda (c)(cdr (assoc 10 (entget c))))
               centroids_min_index
               )
             )
      (setq x_lst (mapcar '(lambda (l)(car l)) center_min_index))
      (setq x_total (apply '+ x_lst))
      (setq x_average (/ x_total (length x_lst)))
      (setq y_lst (mapcar '(lambda (l)(cadr l)) center_min_index))
      (setq y_total (apply '+ y_lst))
      (setq y_average (/ y_total (length y_lst)))
      (setq z_lst (mapcar '(lambda (l)(caddr l)) center_min_index))
      (setq z_total (apply '+ z_lst))
      (setq z_average (/ z_total (length z_lst)))
      (setq newp (list x_average y_average z_average))
      (setq centroids_core_p
             (subst
               newp
               (nth min_index centroids_core_p)
               centroids_core_p
               )
             )

      (setq i (+ i 1))
      )
    (setq ii (+ ii 1))
    )
;给不同簇中的圆上不同色
(setq index 0)
(repeat k
    (setq centroids_index (nth index centroids))
    (setq l (length centroids_index))
    (setq i 0)
    (repeat l
      (setq e (nth i centroids_index))
      (setq e_data (entget e))
      (if (= (assoc 62 e_data) nil)
      (setq e_data (cons (cons 62 (+ index 1)) e_data))
      (setq e_data
             (subst
               (cons 62 (+ index 1))
               (assoc 62 e_data)
               e_data
               )
             )
      )
      
      (entmod e_data)
      (setq i (+ i 1))
      )
    (setq index (+ index 1))
    )

)
(defun rand ( / a c m )
    (setq m   4294967296.0
          a   1664525.0
          c   1013904223.0
          $xn (rem (+ c (* a (cond ($xn) ((getvar 'date))))) m)
    )
    (/ $xn m)
)
(defun randrange ( a b )
    (+ (min a b) (fix (* (rand) (1+ (abs (- a b))))))
)

枫叶棋语 发表于 2024-2-29 22:03:07

伊神牛鼻plus

超超超人1670 发表于 2024-2-29 22:07:45

膜拜一下

ssyfeng 发表于 2024-3-1 10:03:06

我K=4,max_i=50,(kmeans 4),好像结果有些问题,是否迭代次数不够多的原因,结果如下:


伊江痕 发表于 2024-3-1 10:26:12

ssyfeng 发表于 2024-3-1 10:03
我K=4,max_i=50,(kmeans 4),好像结果有些问题,是否迭代次数不够多的原因,结果如下:

出现这种情况很正常,大多都是迭代次数不够。你也可以把簇之间的距离弄得靠拢一点,重合一点,增加点难度。

伊江痕 发表于 2024-3-1 10:27:24

ssyfeng 发表于 2024-3-1 10:03
我K=4,max_i=50,(kmeans 4),好像结果有些问题,是否迭代次数不够多的原因,结果如下:

还有可能是,初始随机选取簇中心的时候,两个簇中心选到一块去了,就会这样。

ssyfeng 发表于 2024-3-1 10:34:07

伊江痕 发表于 2024-3-1 10:27
还有可能是,初始随机选取簇中心的时候,两个簇中心选到一块去了,就会这样。

应该还有优化空间

伊江痕 发表于 2024-3-1 10:47:38

ssyfeng 发表于 2024-3-1 10:34
应该还有优化空间

是的,这只是按照基础的算法流程撸下来的;P

你有种再说一遍 发表于 2024-3-1 18:45:07

写个c#的看看

伊江痕 发表于 2024-3-4 10:20:09

你有种再说一遍 发表于 2024-3-1 18:45
写个c#的看看

我想看你写:$:$:$
页: [1]
查看完整版本: 机器学习中的KMeans算法实现_AutoLISP版本