Kmeans是最流行的,以及最简单的用于挖掘数据潜在结构的机器学习算法之一。Kmeans的目标很简单:根据数据的均值,将数据划分为若干个簇。假定每一个簇的均值能够很好地表明簇内的每个观察值。算法
假定咱们想把数据划分为k个簇,那么咱们就须要找出这k个簇的k个中心。该如何定义,以及寻找这些中心呢?app
咱们只须要求解方程:$min\sum_i^{N}\sum_j^KO_i^j||x_i-u_j||^2$,其中当观察点i为簇j的中心时$O_i^j=1$,不然为0.机器学习
咱们正在寻找k个中心,使得各个簇内的点到簇中心的距离最小。这是一个最优化问题,可是上面的目标函数是非凸的,并且有一些变量是二元的,没法用传统的梯度降低法求解。函数
解决这个问题的方法以下:学习
既然,咱们已经有了伪代码算法,接下来咱们能够用R语言实现Kmeans算法。首先,咱们建立5类数据,它们都服从2维高斯分布。优化
require(MASS) require(ggplot2) set.seed(1234) set1 <- mvrnorm(n = 300, mu = c(-4, 10), Sigma = matrix(c(1.5, 1, 1, 1.5), 2)) set2 <- mvrnorm(n = 300, mu = c(5, 7), Sigma = matrix(c(1, 2, 2, 6), 2)) set3 <- mvrnorm(n = 300, mu = c(-1, 1), Sigma = matrix(c(4, 0, 0, 4), 2)) set4 <- mvrnorm(n = 300, mu = c(10, -10), Sigma = matrix(c(4, 0, 0, 4), 2)) set5 <- mvrnorm(n = 300, mu = c(3, -3), Sigma = matrix(c(4, 0, 0, 4), 2)) DF <- data.frame(rbind(set1, set2, set3, set4, set5), cluster=as.factor(rep(1:5, each = 300))) ggplot(DF, aes(x=X1, y=X2, color=cluster)) + geom_point()
在这个数据集中,Kmeans算法将会获得一个很好的结果,由于每一个分布都呈现圆形,如上图所示。ui
簇中心的初始化很是重要,它可以影响算法。所以,咱们从数据集中随机选取K的点。spa
# 中心初始化 centroids <- data[sample.int(nrow(data), K), ] # 中止准则初始化 current_stop_crit <- 10e10 # 初始化每一个点的指定中心 cluster <- rep(0, nrow(data)) # 算法是否收敛 converged <- FALSE iter <- 1
在每一次迭代中,每一个点将会归为离它最近的簇。咱们可使用欧几里得距离来计算每一个点到每一个中心的距离,并保存各个点到各个中心的最近距离以及相应的簇中心。code
# 在观察值上进行迭代 for (i in 1:nrow(data)){ # 设置最小距离 min_dist <- 10e10 # 在中心上进行迭代 for (centroid in 1:nrow(centroids)){ # 计算欧式距离 distance_to_centroid <- sum((centroids[centroid, ] - data[i, ]) ^ 2) # 距离点最近的中心 if (distance_to_centroid <= min_dist){ # 这个点将会被归为这个簇 cluster[i] <- centroid min_dist <- distance_to_centroid } } }
一旦每一个观察值都被归为距离最近的簇,每一个簇的中心坐标就会被更新。簇中心的新坐标为相应簇中全部点的平均值。收敛准则为:当各个簇的中心中止变更时,算法就应该终止。orm
while (current_stop_crit >= stop_crit & converged == FALSE){ iter <- iter + 1 if (current_stop_crit <= stop_crit){ converged <- TRUE } old_centroids <- centroids # 更新中止准则 current_stop_crit <- mean((old_centroids - centroids) ^ 2)) }
kmeans <- function(data, K=4, stop_crit=10e-5){ # 初始化簇中心 centroids <- data[sample.int(nrow(data), K), ] current_stop_crit <- 1000 cluster <- rep(0, nrow(data)) converged <- FALSE iter <- 1 while (current_stop_crit >= stop_crit & converged == FALSE){ iter <- iter + 1 if (current_stop_crit <= stop_crit){ converged <- TRUE } old_centroids <- centroids # 把每一个点纳入相应的簇 for (i in 1:nrow(data)){ min_dist <- 10e10 for (centroid in 1:nrow(centroids)){ distance_to_centroid <- sum((centroids[centroid, ] - data[i, ]) ^ 2) if (distance_to_centroid <= min_dist){ cluster[i] <- centroid min_dist <- distance_to_centroid } } } # 更新簇中心 for (i in 1:nrow(centroids)){ centroids[i, ] <- apply(data[cluster == i, ], 2, mean) } current_stop_crit <- mean((old_centroids - centroids) ^ 2) } return(list(data=data.frame(data, cluster), centroids=centroids)) }
咱们可使用上述代码来观察咱们的数据:
res <- kmeans(DF[1:2], K=5) res$centroids$cluster <- 1:5 res$data$isCentroid <- FALSE res$centroids$isCentroid <- TRUE data_plot <- rbind(res$centroids, res$data) ggplot(data_plot,aes(x=X1,y=X2,color=as.factor(cluster),size=isCentroid,alpha=isCentroid)) + geom_point()