机器学习中的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))))))
)
伊神牛鼻plus 膜拜一下 我K=4,max_i=50,(kmeans 4),好像结果有些问题,是否迭代次数不够多的原因,结果如下:
ssyfeng 发表于 2024-3-1 10:03
我K=4,max_i=50,(kmeans 4),好像结果有些问题,是否迭代次数不够多的原因,结果如下:
出现这种情况很正常,大多都是迭代次数不够。你也可以把簇之间的距离弄得靠拢一点,重合一点,增加点难度。 ssyfeng 发表于 2024-3-1 10:03
我K=4,max_i=50,(kmeans 4),好像结果有些问题,是否迭代次数不够多的原因,结果如下:
还有可能是,初始随机选取簇中心的时候,两个簇中心选到一块去了,就会这样。 伊江痕 发表于 2024-3-1 10:27
还有可能是,初始随机选取簇中心的时候,两个簇中心选到一块去了,就会这样。
应该还有优化空间 ssyfeng 发表于 2024-3-1 10:34
应该还有优化空间
是的,这只是按照基础的算法流程撸下来的;P 写个c#的看看 你有种再说一遍 发表于 2024-3-1 18:45
写个c#的看看
我想看你写:$:$:$
页:
[1]