K-Means聚类算法原理及实现

由于个人理解有限,难免有错误之处,欢迎指正。

k-means 聚类算法原理:

  1、从包含多个数据点的数据集 D 中随机取 k 个点,作为 k 个簇的各自的中心。

  2、分别计算剩下的点到 k 个簇中心的相异度,将这些元素分别划归到相异度最低的簇。两个点之间的相异度大小采用欧氏距离公式衡量,对于两个点 T0(x1,y2)和 T1(x2,y2),T0 和 T1 之间的欧氏距离为:


欧氏距离越小,说明相异度越小

  3、根据聚类结果,重新计算 k 个簇各自的中心,计算方法是取簇中所有点各自维度的算术平均数。

  4、将 D 中全部点按照新的中心重新聚类。

  5、重复第 4 步,直到聚类结果不再变化。

  6、将结果输出。

  举例说明, 假设包含 9 个点数据 D 如下(见 simple_k-means.txt), 从 D 中随机取 k 个元素,作为 k 个簇的各自的中心, 假设选 k=2, 即将如下的 9 个点聚类成两个类(cluster)


  1.假设选 C0(1 1)和 C1(2 1)前两个点作为两个类的簇心。
  2. 分别计算剩下的点到 k 个簇中心的相异度,将这些元素分别划归到相异度最低的簇。结果为:
K-Means聚类算法原理及实现_第1张图片
K-Means聚类算法原理及实现_第2张图片
  3.根据 2 的聚类结果,重新计算 k 个簇各自的中心,计算方法是取簇中所有元素各自维度的算术平均数。
C0 新的簇心为: 1.0,1.5
C1 新的簇心为: 5.857142857142857, 5.714285714285714
  4.将 D 中全部元素按照新的中心重新聚类。
K-Means聚类算法原理及实现_第3张图片
  5.重复第 4 步,直到聚类结果不再变化。当每个簇心点前后移动的距离小于某个阈值t的时候,就认为聚类已经结束了,不需要再迭代,这里的值选t=0.001距离计算采用欧氏距离。
C0 的簇心为: 1.6666666666666667, 1.75
C1 的簇心为: 7.971428571428572, 7.942857142857143
K-Means聚类算法原理及实现_第4张图片
K-Means聚类算法原理及实现_第5张图片
C0 的簇心为: 1.777777777777778, 1.7916666666666667
C1 的簇心为: 8.394285714285715, 8.388571428571428
K-Means聚类算法原理及实现_第6张图片
C0 的簇心为: 1.7962962962962965, 1.7986111111111114
C1 的簇心为: 8.478857142857143, 8.477714285714285
K-Means聚类算法原理及实现_第7张图片
C0 的簇心为: 1.799382716049383, 1.7997685185185184
C1 的簇心为: 8.495771428571429, 8.495542857142857
K-Means聚类算法原理及实现_第8张图片
C0 的簇心为: 1.7998971193415638, 1.7999614197530864
C1 的簇心为: 8.499154285714287, 8.499108571428572

#include 
#include 
#include 
#include 
#include 

using namespace std;

class Cluster//聚类,每个聚类都包含两个属性,一个是簇心的属性(维数),另一个是距离本簇心最近的样本点
{
public:
    vector  centroid;//存放簇心的属性(维数)
    vector  samples;//存放属于相同簇心样本的下标
};

double CalculateDistance(vector a, vector b)//计算两个向量之间的距离
{
    int len1 = a.size();
    int len2 = b.size();
    if(len1 != len2)
        cerr<<"Dimensions of two vectors must be same!!\n";
    double temp = 0;
    for(int i = 0; i  < len1; ++i)
        temp += pow(a[i]-b[i], 2);
    return sqrt(temp);
}

//max_iteration表示最大的迭代次数,min_move_distance
vector KMeans(vector >data_set, int k, int max_iteration, double threshold)
{
    int row_number = data_set.size();//数据的个数
    int col_number = data_set[0].size();//每个向量(属性)的维数

    //初始随机选取k个质心
    vector cluster(k);//存放k个簇心。vector v(n,i)形式,v包含n 个值为 i 的元素
    srand((int)time(0));
    for(int i = 0; i < k; ++i)
    {
        int c = rand()%row_number;
        cluster[i].centroid = data_set[c];//把第c个作为簇心,并把它相应的属性赋值给centroid
    }

    //iteration
    int iter = 0;
    while(iter < max_iteration)
    {
        iter++;
        for(int i = 0; i < k; ++i)
            cluster[i].samples.clear();
        //找出每个样本点所属的质心
        for(int i = 0; i < row_number; ++i)
        {
            double min_distance = INT_MAX;
            int index = 0;
            //计算离样本点i最近的质心
            for(int j = 0; j < k; ++j)
            {
                double temp_distance = CalculateDistance(data_set[i], cluster[j].centroid);
                if(min_distance > temp_distance)
                {
                    min_distance = temp_distance;
                    index = j;
                }
            }
            cluster[index].samples.push_back(i);//把第i个样本点放入,距离其最近的质心的samples
        }

        double max_move_distance = INT_MIN;
        //更新簇心
        for(int i = 0; i < k; ++i)
        {
            vector temp_value(col_number, 0.0);
            for(int num = 0; num < cluster[i].samples.size(); ++num)//计算每个样本的属性之和
            {
                int temp_same = cluster[i].samples[num];
                for(int j = 0; j < col_number; ++j)
                    temp_value[j] += data_set[temp_same][j];
            }
            vector temp_centroid = cluster[i].centroid;
            for(int j = 0; j < col_number; ++j)
                cluster[i].centroid[j] = temp_value[j]/cluster[i].samples.size();
            //计算从上一个簇心移动到当前新的簇心的距离
            double temp_distance = CalculateDistance(temp_centroid, cluster[i].centroid);
            if(max_move_distance < temp_distance)
                max_move_distance = temp_distance;
        }
        if(max_move_distance < threshold)
            break;
    }
    return cluster;
}

int main()
{
    int threshold = 0.001;//当从上一个簇心移动到当前粗心的距离几乎不变时,可以结束。这里用threshold作为阈值
    vector  >data_set(9, vector(2, 0.0));
    int point_number;
    cin>>point_number;
    for(int i = 0; i < point_number; ++i)
    {
        for(int j = 0; j < 2; ++j)
            cin>>data_set[i][j];
    }

    int col = data_set[0].size();
    vector cluster_res = KMeans(data_set, 2, 200, threshold);
    for(int i = 0; i < cluster_res.size(); ++i)
    {
        cout<<"Cluster "<
K-Means聚类算法原理及实现_第9张图片

你可能感兴趣的:(Machine,Learning)