在R中编写自己的kmeans算法

我正在尝试在R中编写我的第一个kmeans算法。我在这个领域是新手,所以请不要因为我没有看到明显的问题而批评我。

目前,算法接受两个向量xy,计算每个数据点到聚类中心的距离,并将距离其中心最近的聚类分配给该数据点。当分配没有变化,因此聚类中心也没有变化时,算法就会停止。

# 样本数据    set.seed(100)xval <- rnorm(12, mean = rep(1:3, each = 4), sd = 0.2)yval <- rnorm(12, mean = rep(c(1,2,1), each = 4), sd = 0.2)# Kmeans函数kclus <- function(x, y, nclus) {    # 以随机聚类中心开始    xcen <- runif(n = nclus, min = min(x), max = max(x))       ycen <- runif(n = nclus, min = min(y), max = max(y))    # 数据点和聚类分配在“data”中    # 聚类坐标在“clus”中    data <- data.frame(xval = x, yval = y, clus = NA)    clus <- data.frame(name = 1:nclus, xcen = xcen, ycen = ycen)    finish <- FALSE    while(finish == FALSE) {        # 为每个数据点分配距离最近的聚类        for(i in 1:length(x)) {            dist <- sqrt((x[i]-clus$xcen)^2 + (y[i]-clus$ycen)^2)            data$clus[i] <- which.min(dist)        }        xcen_old <- clus$xcen        ycen_old <- clus$ycen        # 计算新的聚类中心        for(i in 1:nclus) {            clus[i,2] <- mean(subset(data$xval, data$clus == i))            clus[i,3] <- mean(subset(data$yval, data$clus == i))        }        # 如果聚类坐标没有变化,则停止循环        if(identical(xcen_old, clus$xcen) & identical(ycen_old, clus$ycen)) finish <- TRUE    }    data}# 对样本数据应用kmeans函数cluster <- kclus(xval, yval, 4)# 绘制结果ggplot(cluster, aes(xval, yval, color = as.factor(clus))) + geom_point()

到目前为止,这运行得相当好。但是我不知道如何强制算法使用特定的聚类数量。我的kclus()函数中已经实现了nclus参数,但我不知道如何利用它。

对于给定的样本数据,算法只给了我三个聚类。我想强制它返回四个聚类。

这里有人能给我一些建议吗?

非常感谢,Marcus


回答:

你实现的算法并不总是给你三个聚类,可能是因为你没有运行足够多次。这里是对你的代码的轻微修改,我们将能够看到聚类输出的数量取决于聚类中心的初始化(这些中心是随机选择的,可以通过random.seed来控制):

# 样本数据    set.seed(100)xval <- rnorm(12, mean = rep(1:3, each = 4), sd = 0.2)yval <- rnorm(12, mean = rep(c(1,2,1), each = 4), sd = 0.2)# 带有random.seed初始化的Kmeans函数kclus <- function(x, y, nclus, random.seed=123) {  set.seed(random.seed)  # 以随机聚类中心开始  xcen <- runif(n = nclus, min = min(x), max = max(x))     ycen <- runif(n = nclus, min = min(y), max = max(y))  # 数据点和聚类分配在“data”中  # 聚类坐标在“clus”中  data <- data.frame(xval = x, yval = y, clus = NA)  clus <- data.frame(name = 1:nclus, xcen = xcen, ycen = ycen)  finish <- FALSE  while(finish == FALSE) {    # 为每个数据点分配距离最近的聚类    for(i in 1:length(x)) {      dist <- sqrt((x[i]-clus$xcen)^2 + (y[i]-clus$ycen)^2)      data$clus[i] <- which.min(dist)    }    xcen_old <- clus$xcen    ycen_old <- clus$ycen    # 计算新的聚类中心    for(i in 1:nclus) {      clus[i,2] <- mean(subset(data$xval, data$clus == i))      clus[i,3] <- mean(subset(data$yval, data$clus == i))    }    # 如果聚类坐标没有变化,则停止循环    if(identical(xcen_old, clus$xcen) & identical(ycen_old, clus$ycen)) finish <- TRUE  }  data}# 使用默认的随机种子123,你应该能够重现结果# 如你所见,在这种情况下,没有数据点被分配到第四个聚类cluster <- kclus(xval, yval, 4)cluster.centers <- aggregate(.~clus, cluster, mean)ggplot(cluster, aes(xval, yval, color = as.factor(clus))) +   geom_point(size=5) +   geom_point(data=cluster.centers, aes(xval, yval, col=as.factor(clus)), pch=8, size=5)

enter image description here

# 使用不同的随机种子12运行# 如你所见,在这种情况下,算法输出4个聚类,其中第二个聚类只有一个数据点被分配    cluster <- kclus(xval, yval, 4, 12)    cluster.centers <- aggregate(.~clus, cluster, mean)    ggplot(cluster, aes(xval, yval, color = as.factor(clus))) +       geom_point(size=5) +       geom_point(data=cluster.centers, aes(xval, yval, col=as.factor(clus)), pch=8, size=5)

enter image description here

# 使用不同的随机种子12345运行# 如你所见,在这种情况下,算法输出2个聚类,所有数据点都被分配到第一个和第二个聚类    cluster <- kclus(xval, yval, 4, 12345)    cluster.centers <- aggregate(.~clus, cluster, mean)    ggplot(cluster, aes(xval, yval, color = as.factor(clus))) +       geom_point(size=5) +       geom_point(data=cluster.centers, aes(xval, yval, col=as.factor(clus)), pch=8, size=5)

enter image description here

从上面的例子可以看出,聚类是否在收敛时没有分配点取决于初始中心位置和数据分布。一般来说,如果kmeans结束时有一个聚类中心为空,这意味着如果你试图强制将一个点分配到空聚类中,可能会导致聚类质量变差,这是你不希望看到的。

你可以尝试以下几种方法:

  1. 首先,你可以多次运行你的算法,每次使用不同的随机初始化中心,然后选择聚类质量最高的结果(通过SSE等衡量)。
  2. 第二,你可以尝试使用Kmeans++进行更智能的初始化。
  3. 一个不太好的选择是修改你的算法,确保在重新分配聚类时保证每个聚类(k=4)至少有一个点被分配(如果没有,则不重新分配)。
  4. 最后,你可以尝试其他算法,例如层次聚类,它通过树状图为你提供更多的灵活性,让你可以选择想要的聚类数量。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注