k-means算法C语言实现
K-Means算法实现
K-Means算法实现一. K-Means算法简介k-means algorithm算法是一个聚类算法,把n的对象根据他们的属性分为k个分割,k < n。
它与处理混合正态分布的最大期望算法很相似,因为他们都试图找到数据中自然聚类的中心。
它假设对象属性来自于空间向量,并且目标是使各个群组内部的均方误差总和最小。
k-means 算法的工作过程说明如下:首先从n个数据对象任意选择 k 个对象作为初始聚类中心;而对于所剩下其它对象,则根据它们与这些聚类中心的相似度(距离),分别将它们分配给与其最相似的(聚类中心所代表的)聚类;然后再计算每个所获新聚类的聚类中心(该聚类中所有对象的均值);不断重复这一过程直到标准测度函数开始收敛为止。
一般都采用均方差作为标准测度函数. k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。
具体如下:输入:k, data[n];(1)选择k个初始中心点,例如c[0]=data[0],…c[k-1]=data[k-1];(2)对于data[0]….data[n], 分别与c[0]…c[n-1]比较,假定与c[i]差值最少,就标记为i;(3)对于所有标记为i点,重新计算c[i]={ 所有标记为i的data[j]之和}/标记为i的个数;(4)重复(2)(3),直到所有c[i]值的变化小于给定阈值。
二. K-Means算法的实现(C++环境下实现)#include <iostream>#include <fstream>#include <cmath>#include <cstdlib>#include <ctime>using namespace std;// 数据对象,size为维度struct Vector{double* coords; // 所有维度的数值int size;Vector() : coords(0), size(0) {}Vector(int d) { create(d); }// 创建维度为d的数据,并将各维度初始化为0void create(int d){ size = d;coords = new double[size];for (int i=0; i<size; i++)coords[i] = 0.0; }// 复制一个数据void copy(const Vector& other){ if (size == 0) // 如果原来没有数据,创建之create(other.size);for (int i=0; i<size; i++)coords[i] = other.coords[i]; }// 将另一个数据的各个维度加在自身的维度上void add(const Vector& other){ for (int i=0; i<size; i++)coords[i] += other.coords[i]; }// 释放数值的空间~Vector(){if(coords)delete[] coords;size = 0; }};// 聚类结构struct Cluster{Vector center; // 中心/引力数据对象int* member; // 该聚类中各个数据的索引int memberNum; // 数据的数量};// KMeans算法类class KMeans{private:int num; // 输入数据的数量int dimen; // 数据的维数int clusterNum; // 数据的聚类数Vector* observations; // 所有数据存放在这个数组中Cluster* clusters; // 聚类数组int passNum; // 迭代的趟数public:// 初始化参数和动态分配内存KMeans(int n, int d, int k, Vector* ob): num(n), dimen(d), clusterNum(k), observations(ob), clusters(new Cluster[k]){for (int k=0; k<clusterNum; k++)clusters[k].member = new int[n]; }// 释放内存~KMeans(){for (int k=0; k<clusterNum; k++)delete [] clusters[k].member;delete [] clusters; }void initClusters(){ // 由于初始数据中心是任意的,// 所以直接把前个数据作为NumClusters个聚类的数据中心for (int i=0; i<clusterNum; i++){ clusters[i].member[0] = i; // 记录这个数据的索引到第i个聚类中clusters[i].center.copy(observations[i]); // 把这个数据作为数据中心 } }void run(){ bool converged = false; // 是否收敛passNum = 0;while (!converged && passNum < 999)// 如果没有收敛,则再次迭代// 正常情况下总是会收敛,passNum < 999是防万一{distribute(); // 将数据分配到聚中心最近的聚类converged = recalculateCenters(); // 计算新的聚类中心,如果计算结果和上次相同,认为已经收敛passNum++; } }void distribute(){// 将上次的记录的该聚类中的数据数量清0,重新开始分配数据 for(int k=0; k<clusterNum; k++)getCluster(k).memberNum = 0;// 找出每个数据的最近聚类数据中心,并将该数据分配到该聚类for(int i=0; i<num; i++){Cluster& cluster = getCluster(closestCluster(i)); // 找出最接近的其中心的聚类int memID = cluster.memberNum; // memberNum是当前记录的数据数量,也是新加入数据在member数组中的位置cluster.member[memID] = i; // 将数据索引加入Member数组 cluster.memberNum++; // 聚类中的数据数量加1 } }int closestCluster(int id) { int clusterID = 0;// 暂时假定索引为id的数据最接近第一个聚类double minDist = eucNorm(id, 0); // 计算到第一个聚类中心的误差(本程序中用距离的平方和作为误差)// 计算其它聚类中心到数据的误差,找出其中最小的一个for (int k=1; k<clusterNum; k++){ double d = eucNorm(id, k);if(d < minDist) // 如果小于前最小值,将改值作为当前最小值{minDist = d;clusterID = k;}}return clusterID; }// 索引为id的数据到第k个聚类中心的误差(距离的平方) double eucNorm(int id, int k){Vector& observ = observations[id];Vector& center = clusters[k].center;double sumOfSquare = 0;// 将每个维度的差的平方相加,得到距离的平方for (int d=0; d<dimen; d++){ double dist = observ.coords[d] - center.coords[d]; // 在一个维度上中心到数据的距离 sumOfSquare += dist*dist; }return sumOfSquare; }// 重新计算聚类中心bool recalculateCenters(){ bool converged = true;for (int k=0; k<clusterNum; k++){ Cluster& cluster = getCluster(k);Vector average(dimen); // 初始的数据平均值// 统计这个聚类中数据的总和(因为在构造函数中会将各维数值清0,所以可以直接加)for (int m=0; m<cluster.memberNum; m++)average.add(observations[cluster.member[m]]);// 计算各个维度的评价值for(int d=0; d<dimen; d++){ average.coords[d] /= cluster.memberNum;if(average.coords[d]!=cluster.center.coords[d]) // 如果和原来的聚类中心不同// 表示没有收敛{ converged = false;cluster.center.coords[d] = average.coords[d]; // 用这次的平均值作为新的聚类中心 } } }return converged; }// 获得第id个聚类Cluster& getCluster(int id){ return clusters[id]; }};// 打印一个数据void printVector(ostream& output, const Vector& v){ for (int i=0; i<v.size; i++){ if(i != 0)output << ",";output << v.coords[i]; }}void partitionObservations(istream& input){// 从input输入中获取数据int n, dimen, k;// 文本文件中头三个数据分别是数据数量(n)、数据维度(dimen)和聚类数量(k)input >> n >> dimen >> k;// 创建存储数据的数值Vector* obs = new Vector[n];// 将数据读入数组for (int i=0; i<n; i++){ obs[i].create(dimen); // 创建数据// 依次读入各个维度的数值for (int d=0; d<dimen; d++){input >> obs[i].coords[d]; } }// 建立KMeans算法类实例KMeans kmeans(n, dimen, k, obs);kmeans.initClusters(); // 初始化kmeans.run(); // 执行算法// 输出聚类数据,如果希望输出到文件中,// 将后面的output的定义改为下面的形式即可// ofstream output("result.txt");ostream& output = cout;for (int c=0; c<k; c++){Cluster& cluster = kmeans.getCluster(c);output << "---- 第" << (c + 1) << "个聚类 ----\n"; // 显示第c个聚类output << "聚类中心:";printVector(output, cluster.center);for (int m=0; m<cluster.memberNum; m++){int id = cluster.member[m];printVector(output, obs[id]);output << "\n";}output << endl; }delete[] obs;}int main(){const char* fileName = "observations.txt";ifstream obIn(fileName);if (obIn.is_open())partitionObservations(obIn);elsecout << "open " << fileName << " is fail!" << endl;return 0;}。
K-means算法总结
将如下三维空间的点进行 k-means 分类: [input.txt] 1.0 , 5.7 , 2.8
4.5 , 5.2 , -0.3 -0.9 , 8.1 , 1.4 0.5 , 6.6 , 2.3 3.5 , 4.7 , 0.2 4.7 , 5.9 , -1 5.1 , 8.2 , 0.9 2.1 , 7.4 , 3.0 0.6 , 6.5 , 3.8 在三维空间的图及 k-means 分类的结果
K-means 算法总结及 c++编程实现
/* *@author:郑海波 zhb931706659@ */nuptboyzhb/ *参考:斯坦福大学 machine learning */
算法描述:
k-means 算法是无监督学习算法,它是将给定的训练数据集合{x1, x2, x3,..., xn} 进
{
it->print(); } //计算误差函数
double J=0; for(it = InputVector.begin(); it != InputVector.end(); ++it)
{
J+=it->distanceOf(U[it->cluster]); } cout<<"误差函数 J(c,u)="<<J<<endl;
minDist=dist[j]; it->cluster=j; } } }
//第二步 更新重心 U
double sum[K][dim],num[K]; memset(&sum,0,sizeof(double)*K*dim);
memset(&num,0,sizeof(double)*K);
opencv kmeans c代码
opencv kmeans c代码在OpenCV库中,k-means算法的C语言实现是非常简单的。
下面是一个使用OpenCV进行k-means聚类的基本示例:c#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>int main(int argc, char** argv){// 加载数据cv::Mat data = cv::imread("your_data.png", CV_LOAD_IMAGE_GRAYSCALE);if (data.empty()) {std::cout << "Could not open or find the image" << std::endl;return -1;}// 转化为浮点数矩阵cv::Mat data_fl = data.clone();cv::Mat labels;int clusterCount = 3; // 设置期望的簇的数量cv::TermCriteria criteria(cv::TermCriteria::EPS+cv::TermCriteria::COUNT, 10, 1.0);cv::Mat centers;// 进行k-means聚类cv::kmeans(data_fl, clusterCount, labels, criteria, 3, cv::KMEANS_PP_CENTERS, centers);// 绘制结果cv::Mat show_img = data.clone();int index = 0;for (int i = 0; i < data.rows; i++) {for (int j = 0; j < data.cols; j++) {show_img.at<uchar>(i,j) = centers.at<float>(labels.at<int>(i,j), 0); }}cv::imshow("k-means Clustering", show_img);cv::waitKey(0);return 0;}请注意,这段代码中的"your_data.png" 应被替换为您自己的图像路径。
K-Means聚类算法C++实现
// K-Means算法实现// K-means 均值算法本代码使用二维(坐标)聚类(用结构体实现)// k-means算法简介:// 算法接受输入量k ;然后将n个数据对象划分为k个聚类以便使得所获得的聚类满足:// 同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。
// 聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。
// 湖南科技大学2010级计算机学院计一班1005010124 上传#include<iostream>#include<math.h>#include<stdlib.h>#include<stdio.h>using namespace std;void InitData(); //数据初始化void CenterCpy(); //保存久质心void InitCenter(); //初始化质心void CpyCenter();void UpdateCluster(); //更新每一类的数据void Random(int n, int k, struct Coord *index); //随即取值int GetPoint(double x, double y, struct Coord *Center); //找出每个数据距离质点最近的质点。
void AddToCluster(int point, double x, double y); //将该数据加入该质点并且统计该质点包含的数据void UpdateCenter(); //更新质点中心int CmpCenter(struct Coord *OldCenter, struct Coord *Center); //比较旧质心和新质心是否相同void Print(); //输出int k; //聚类数int n; //数据数int *count; //统计每一类的数据个数struct Coord{double x;double y;};Coord *DataSet; //保存坐标点数据Coord *Center; //质心Coord *OldCenter; //保存旧质心Coord **Cluster; //保存每一类数据Coord *Index; //索引int main(){int flag = 1;InitData();InitCenter();while(flag){UpdateCluster();UpdateCenter();if( CmpCenter(OldCenter, Center)){flag = 0;}else{CenterCpy();}}Print();return 0;}void InitData(){double a, b;cout<<"您想要输入多少组数据:";cin>>n;cout<<"你想要分为多少个聚类:";cin>>k;if(k>n || k<0 || n<=0){cout<<"输入无效!!"<<endl;exit(1);}Index = new Coord [sizeof(struct Coord *) * k]; //索引点OldCenter = new Coord [sizeof(struct Coord *) * k]; //保存旧的中心Center = new Coord [sizeof(struct Coord *) * k]; //质心DataSet = new struct Coord [sizeof(struct Coord *) * n];//定义结构体数组保存数据count = new int [sizeof(int) * n]; //统计每一类的数据个数Cluster = new struct Coord * [sizeof(struct Coord *) * k];//定义二维结构体数组分类保存数据for(int i=0; i<k; i++) //将k类中每一类的集合进行初始化申请空间{Cluster[i] =new struct Coord [sizeof(struct Coord *)*n];}cout<<"请输入数据"<<endl;for(i=0; i<n; i++){cin>>a;DataSet[i].x = a;cin>>b;DataSet[i].y = b;}if(k == 0){cout<<"该类聚为:"<<endl;for(i=0; i<n; i++){cout<<"("<<DataSet[i].x<<" , "<<DataSet[i].y<<")"<<"\t";if((i+1)%4 == 0)cout<<endl;}cout<<endl;exit(0);}}void InitCenter(){Random(n, k, Index); //随即取k个起点中心for(int i=0; i<k; i++) //把所取点代表的数据赋值给质点值{Center[i].x = Index[i].x;Center[i].y = Index[i].y;}CenterCpy(); //把质点保存到旧质点以便能与产生的新支点作比较}void Random(int n, int k, struct Coord *index){int i=0, j=0;for(i=0; i<k; i++){int a = rand() % n;int b = rand() % n;for(j=0; j<i; j++){if(Index[j].x == a || Index[j].y == b)break;}if(j >= i && a > 0 && b>0){Index[i].x = a;Index[i].y = b;}else{i--;}}}void CenterCpy(){for(int i=0; i<k; i++){OldCenter[i].x = Center[i].x;OldCenter[i].y = Center[i].y;}}void UpdateCluster(){int i, point; //point返回一个距离中心最近的点for(i=0; i<k; i++) //初始化使每一类质点数为0{count[i] = 0;}for(i=0; i<n; i++){point = GetPoint(DataSet[i].x, DataSet[i].y, Center); //找出每个数据距离质点最近的质点。
K-MEANS(K均值聚类算法-C均值算法)
• 算法描述
1. 为中心向量c1, c2, …, ck初始化k个种子 2. 分组: 将样本分配给距离其最近的中心向量 由这些样本构造不相交( non-overlapping ) 的聚类 3. 确定中心: 用各个聚类的中心向量作为新的中心 4. 重复分组和确定中心的步骤,直至算法收敛
算法 k-means算法 输入:簇的数目k和包含n个对象的数据库。 输出:k个簇,使平方误差准则最小。 算法步骤: 1.为每个聚类确定一个初始聚类中心,这样就有K 个 初始聚类中心。 2.将样本集中的样本按照最小距离原则分配到最邻 近聚类 3.使用每个聚类中的样本均值作为新的聚类中心。 4.重复步骤2.3直到聚类中心不再变化。 5.结束,得到K个聚类
d M 1 , O3
d M 2 , O3
0 1.52 2 02
0 1.52 0 02
2.5
1.5
显然 d M 2 , O3 d M 1 , O3 ,故将O3分配给C 2
• 对于
O4
: d M 1 , O4
d xi , x j
x
d k 1
ik
x jk
2
• (2)选择评价聚类性能的准则函数 k-means聚类算法使用误差平方和准则函数来 评价聚类性能。给定数据集X,其中只包含描述属 性,不包含类别属性。假设X包含k个聚类子集 X1,X2,…XK;各个聚类子集中的样本数量分别为n1, n2,…,nk;各个聚类子集的均值代表点(也称聚类中 心)分别为m1,m2,…,mk。则误差平方和准则函数 公式为: 2
分割后的效果
注:最大迭代次数为20次,需运行多次才有可能得到较好的效果。
2016/12/9
C语言课程设计题目要求
综合题设计实现K均值聚类算法K均值(K-means)聚类算法是无监督聚类(聚类(clustering)是将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇(cluster)”)算法中的一种,也是最常用的聚类算法。
K表示类别数,Means表示均值。
K-means主要思想是在给定K值和若干样本(点)的情况下,把每个样本(点)分到离其最近的类簇中心点所代表的类簇中,所有点分配完毕之后,根据一个类簇内的所有点重新计算该类簇的中心点(取平均值),然后再迭代的进行分配点和更新类簇中心点的步骤,直至类簇中心点的变化很小,或者达到指定的迭代次数。
K-means算法流程如下:(a)随机选取K个初始cluster center(b)分别计算所有样本到这K个cluster center的距离(c)如果样本离cluster center Ci最近,那么这个样本属于Ci点簇;如果到多个cluster center 的距离相等,则可划分到任意簇中(d)按距离对所有样本分完簇之后,计算每个簇的均值(最简单的方法就是求样本每个维度的平均值),作为新的cluster center(e)重复(b)(c)(d)直到新的cluster center和上轮cluster center变化很小或者达到指定的迭代次数,算法结束具体要求:将数据集的数据,分别聚成2,3,4类,观察分析实验结果。
数据集如下所示。
-25 22.2 35.3431.2 -14.4 2332.02 -23 24.44-25.35 36.3 -33.34-20.2 27.333 -28.22-15.66 17.33 -23.3326.3 -31.34 16.3-22.544 16.2 -32.2212.2 -15.22 22.11-41.241 25.232 -35.338-22.22 45.22 23.55-34.22 50.14 30.9815.23 -30.11 20.987-32.5 15.3 -25.22-38.97 20.11 33.22。
K-Means算法(C#)
K-means 算法:step1 初始化K个质心step2 将所有的点分配给最近的质心step3 更新质心step4 若质心都没用变化,则停止,否则返回step2using System;using System.Collections.Generic;using System.Text;namespace BisectingKMeans{class KMeans{#region variable & parameterint[] _K_Centroid; //keep centroidint[] _New_K_Centroid; //save the new centroidpublic List<int>[] _PointListofCentroidI; //save the points belongs to CentroidI #endregion/// <summary>/// Creat inition K Centroid/// </summary>/// <param name="K">K value</param>/// <param name="FileNum">file number</param>private void Init_K_Centroid(int K, int FileNum){Random _random = new Random();_K_Centroid = new int[K];_New_K_Centroid = new int[K];_PointListofCentroidI = new List<int>[K];for (int i = 0; i < K; i++){_PointListofCentroidI[i] = new List<int>();}for (int i = 0; i < K; i++){int _index = _random.Next(FileNum);//save centroidint j = 0;for (j = 0; j < i; j++) // judge whether new centroid has been producted {if (_index == _K_Centroid[j]){break;}}if (j == i){_K_Centroid[i] = _index;_New_K_Centroid[i] = _index;}elsei--;}}/// <summary>/// Assigning Point to Closest Centroid/// </summary>/// <param name="Point">Point</param>/// <returns>the index of the Closest Centroid</returns>private int Assigning_Points_to_Closest_Centroid(int Point){double _minDistance = 0;int _centroidIndex = -1;for (int i = 0; i < _New_K_Centroid.Length; i++){double _distance = DistanceBetweenTwoPoints(Point, _New_K_Centroid[i]); if (i==0||_minDistance > _distance){_minDistance = _distance;_centroidIndex = i;}}return _centroidIndex;}/// <summary>/// Calculate the distance between two points/// </summary>/// <param name="Point1">the first point</param>/// <param name="Point2">the second point</param>/// <returns>distance</returns>private double DistanceBetweenTwoPoints(int Point1, int Point2){//replace with your codedouble _distance = 0;_distance = Math.Pow((data[Point1, 0] - data[Point2, 0]), 2) + Math.Pow((data[Point1, 1] - data[Point2, 1]), 2);return Math.Sqrt(_distance);}/// <summary>/// ReCompute the new Centroid/// </summary>/// <param name="OldCentroidIndex">Old Centroid Index</param>/// <returns> new Centroid</returns>private int ReCompute_the_Centroid(int OldCentroidIndex){int _NewCentroid = -1;double _SumDistance = 0;for (int i = 0; i < _PointListofCentroidI[OldCentroidIndex].Count; i++){double sum = 0;for (int j = 0; j < _PointListofCentroidI[OldCentroidIndex].Count; j++) {sum +=DistanceBetweenTwoPoints(_PointListofCentroidI[OldCentroidIndex][i],_PointListofCentroi dI[OldCentroidIndex][j]);}if (i == 0 || sum < _SumDistance){_SumDistance = sum;_NewCentroid= i;}}return _PointListofCentroidI[OldCentroidIndex][_NewCentroid];}/// <summary>/// Judge if the Centroids have been changed/// </summary>/// <returns>change return true,else return false</returns>private bool JudgeCentroidChange(){bool _YesChange = false;for (int i = 0; i < _K_Centroid.Length; i++) {if (_K_Centroid[i] != _New_K_Centroid[i]) {_YesChange = true;break;}}return _YesChange;}public void K_Means(int K, int FileNum){Init_K_Centroid(K, FileNum);int _centroidIndex = -1;int _newCentroid = -1;bool _stop = false;do{for (int i = 0; i < K; i++){_PointListofCentroidI[i].Clear();}for (int i = 0; i < FileNum; i++){_centroidIndex = Assigning_Points_to_Closest_Centroid(i); _PointListofCentroidI[_centroidIndex].Add(i);_newCentroid = ReCompute_the_Centroid(_centroidIndex);_New_K_Centroid[_centroidIndex] = _newCentroid;}_stop=JudgeCentroidChange();if (_stop){for (int i = 0; i < _K_Centroid.Length; i++){_K_Centroid[i] = _New_K_Centroid[i];}}} while (_stop);}}}//测试用例public int[,] data = new int[50, 2];public void ProductData(){Random _random=new Random();for (int i = 0; i < data.Length/2; i++) {data[i, 0] = _random.Next(100);data[i, 1] = _random.Next(100);}}////。
Kmeans聚类算法以及实现
K means聚类算法以及实现一、Kmeans算法k-means 算法接受参数 k ;然后将事先输入的n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小;聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”引力中心来进行计算的;K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一;K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类;通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果;假设要把样本集分为c个类别,算法描述如下:1适当选择c个类的初始中心;2在第k次迭代中,对任意一个样本,求其到c个中心的距离,将该样本归到距离最短的中心所在的类;3利用均值等方法更新该类的中心值;4对于所有的c个聚类中心,如果利用23的迭代法更新后,值保持不变,则迭代结束,否则继续迭代;该算法的最大优势在于简洁和快速;算法的关键在于初始中心的选择和距离公式二、算法流程首先从n个数据对象任意选择 k 个对象作为初始聚类中心;而对于所剩下其它对象,则根据它们与这些聚类中心的相似度距离,分别将它们分配给与其最相似的聚类中心所代表的聚类;然后再计算每个所获新聚类的聚类中心该聚类中所有对象的均值;不断重复这一过程直到标准测度函数开始收敛为止;一般都采用均方差作为标准测度函数. k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开;Kmeans算法实现的步骤具体描述为:1从疗个数据对象中任意选取k个对象作为初始的聚类中心;2分别计算每个对象到各个聚类中心的距离,把对象分配到距离最近的聚类中;3所有对象分配完成后,重新计算k个聚类的中心;4与前一次计算得到的k个聚类中心比较,如果聚类中心发生变化,转2,否则转5;5输出聚类结果;实现的流程框图为首先从n个数据对象中任意选择k个对象作为初始聚类中心;而对于所剩下的其它对象,则根据他们与这些聚类中心的相似度距离,分别将他们分配给与其最相似的聚类中心所代表的聚类;然后再计算每个所新聚类的聚类中心该聚类中所有对象的均值;不断重复这一过程直到标准测度函数开始收敛为止;一般都采用均方差作为标准测度函数,具体定义如下:其中E为数据库中所有对象的均方差之和;p为代表对象的空间中的一个点;m,为聚类G的均值p和m,均是多维的.上述公式所示聚类标准旨在使所获得的k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类间尽可能的分开;三、设计实现K-Means算法是聚类算法的一种,它通过计算样本数据点之间的逻辑距离来判断某个样本数据点属于哪一个簇,算法最终的目的是要把用于算法的样本数据点分配到K个簇中,使簇内的点有较大的相似度,而簇间的点有较小的相似度;K-Means中的K表示聚类中心的个数,在算法运行过程中,要反复扫描所有样本数据点,要计算每个非中心数据点与某个聚类中心点的距离,并将这个数据点归为与其距离最小的那个聚类中心对应的簇之中;每扫描一次就要重新计算每个聚类中心点的位置;当聚类中心点的位置变化在一定的阈值之内的时候停止处理,最后就可以得到K个簇,并且簇中每个样本数据点到本簇的中心的距离都小于到其它簇中心的距离;本程序使用C++实现算法本身,然后用C调用C++函数完成了数据的传送和接收算法的输出并可视化结果;并且数据是保存在Access数据库中的一个sample表格中然后通过字符串连接数据库读入数据也可以使用其他数据库,例如sqlserver,修改相应的字符串连接语句即可;下面主要介绍一个导出的函数DataMining_KMeans,这个函数要接收C传过来的原始数据、K值及其它参数,同时还要将处理的结果赋值给引用参数以便在C中可以接收到处理结果;DataMining_KMeans函数的原型如下:/Author:YinPSoftparam:raw:原始数据count:数据点个数K:聚类中心个数means:初始聚类中心minOffset:聚类中心的最小偏移量,用于控制聚类处理的精度;times:最大迭代次数c:每个聚类的数据点索引值sizes:每个聚类的容量finalMeans:最终的聚类中心位置/void DataMining_KMeans double data,int count,int K,int means,double minOffset,int times,int c,int sizes,double finalMeans;在这个原型声明中可以看到初始聚类中心点要从外部输入,从外部输入这种方式有更大的灵活性,当有特定的初始聚类中心生成策略的时候可能通过这个策略来生成中心点,而没有策略的时候也可以通过随机来生成;初始聚类中心的值可以很大程度地影响到整个算法的效率,适当的选择聚类中心点可以减少算法的迭代次数; 接着是初始化:for i=0;i<K;i++{vector<int>cluster;cluster;means+i;means+i.isMean=true;means+i.isVirtual=false;上面的代码有两个目的:一是初始化vector<int>数组用于存放K个簇的样本点索引;二是将外部传进来的K个聚类中心点添加到dots对象之中, dots对象是vector<Dot2D>类型的;在DataMining_KMeans最开始就要通过PreProcessor函数将外部传进来的数据点封装成vector<Dot2 D>的对象,在这里是dots;Dot2D的定义如下,isMean和isVirtual是数据点的两个标志,前者用于标识这个数据点是否是聚类中心,后者用于表示这个点是不是虚拟数据点,虚拟数据点在这里意为这个点的位置是通过计算得到,而不是原始数据中的数据点;实际上,在这个算法中,虚拟数据点都是在处理过程中得到的聚类中心点,因为每一轮计算完成后要重新计算聚类中心位置,而这个计算出来的位置很可能不同于原始数据中的任何一个点,所以要把这些点保存下来用于下一轮的计算:typedef struct Dot{double x;double y;bool isMean;bool isVirtual;}Dot2D,Ptr_Dot2D;接下来就是一个while循环,反复地扫描样本数据点并将其分配K个簇中;在这个while循环中包括两大部分,首先就是计算并比较数据点与聚类中心的距离并进行分配;其次就是重新计算聚类中心;代码如下:for i=0; i < count; i++{if i.isMean && i.isVirtual{dist = INFINITE_DISTANCE;for j = 0; j < K; j++{term = Distancedotsi, dotsmeanIndexj;if term < dist{dist = term;ush_backi;}}对所有数据点的扫描计算的前提是这些数据点不是聚类中心并且也不是虚拟数据点;然后将中间距离变量设置一个初值,接下来的for循环就要计算当前这个数据点与每个聚类中心的距离,如果当前点到第K个聚类中心的距离是最小的,那么就把它的索引值存放到clusters的第K个vector<int>对象中;当所有的样本数据点都被分配到K个簇中以后就要重新计算中心点的位置,代码如下:for i = 0; i < K; i++{maxminvalues = FindMaxMinValuesdots, i;= 0 + 2 / ;= 1 + 3 / ;= true;= true;term = DistancenewMean, dotsmeanIndexi;flag = term > minOffset true : flag;i.isMean = false;newMean;finalMeans+i = ;finalMeans+i+K = ;i = - 1;}上面的代码用于计算新的聚类中心点的位置,并覆盖之前的聚类中心位置;在这个算法中通过计算簇所占有的矩形区域的中心点来作为新的聚类中心点的位置;在这个for循环中还有一件事情就是要计算新的聚类中心点与前一轮的聚类中心点的距离或者称为聚类中心点的位移,在函数原型中我们设置了这个位移的最小值,当K个位移都小于这个值的时候就要结束算法处理;另外每次进入while循环的时候要清空clusters对象,否则clusters中会有多余的数据,并且会随原始数据量而膨胀;以上就是K-Means算法实现C++部分的主要代码,也是这个演示程序的核心部分;除了算法实现部分之外就是算法结果的展示了;另一个项目用于向算法传送原始数据以及接收算法的输出并可视化结果;在项目中要调用C++函数,方法是通过的P/Invoke调用,其代码如下:///<summary>/// K-Means聚类分析///</summary>///<param name="rawdata">KMeans原始数据</param>///<param name="rows">数据点个数</param>///<param name="K">聚类个数</param>///<param name="means">初始聚类中心</param>///<param name="minOffset">聚类中心的最小位移量</param>///<param name="times">最大迭代次数,-1为不控制</param>///<param name="result">聚类划分结果</param>>///<param name="finalMeans">返回的最终聚类中心</param>///<param name="size">表示第一个参数的长度</param>///<param name="finalMeans_size">表示第九个参数的长度</param> DllImport, EntryPoint = "DataMining_KMeans",CharSet =public static extern void DataMining_KMeans MarshalAs, SizeParamIndex = 9double rawdata,int rows,int K,MarshalAs, SizeParamIndex = 2int means,double minOffset,int times,MarshalAs, SizeParamIndex = 1int result,MarshalAs, SizeParamIndex = 2int sizes,MarshalAs, SizeParamIndex = 10double finalMeans,int size,int finalMeans_size;实验最终效果图如下:K=5K=4。
机器学习--K均值聚类算法原理、方法及代码实现
机器学习--K均值聚类算法原理、⽅法及代码实现⼀、K-means算法原理 k-means算法是⼀种简单的迭代型聚类算法,采⽤距离作为相似性指标,从⽽发现给定数据集中的K个类,且每个类的中⼼是根据类中所有值的均值得到,每个类⽤聚类中⼼来描述。
对于给定的⼀个包含n个d维数据点的数据集X以及要分得的类别K,选取欧式距离作为相似度指标,聚类⽬标是使得各类的聚类平⽅和最⼩,即最⼩化: 结合最⼩⼆乘法和拉格朗⽇原理,聚类中⼼为对应类别中各数据点的平均值,同时为了使得算法收敛,在迭代过程中,应使最终的聚类中⼼尽可能的不变。
⼆、算法实现⼀般流程 K-means是⼀个反复迭代的过程,算法分为四个步骤: 1)选取数据空间中的K个对象作为初始中⼼,每个对象代表⼀个聚类中⼼; 2)对于样本中的数据对象,根据它们与这些聚类中⼼的欧⽒距离,按距离最近的准则将它们分到距离它们最近的聚类中⼼(最相似)所对应的类; 3)更新聚类中⼼:将每个类别中所有对象所对应的均值作为该类别的聚类中⼼,计算⽬标函数的值; 4)判断聚类中⼼和⽬标函数的值是否发⽣改变,若不变,则输出结果,若改变,则返回2)。
三、算法应⽤实例--鸢尾花分类问题 1.Iris数据集 Iris也称鸢尾花卉数据集,是⼀类多重变量分析的数据集。
数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性。
可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪⼀类。
代码实现:import mathfrom collections import defaultdictimport numpy as npdataname = "data.txt"def loadIRISdata(filename):data = []with open(filename, mode="r", encoding="utf-8") as rf:for line in rf:if line == '\n':continuedata.append(list(map(float, line.split(""))))return datadef generateCenters(data):'''求解初始聚类中⼼'''centers = []'''已知维度为4''''''分三类,取第0,50,100的三个向量作为分界'''centers.append(data[0])centers.append(data[50])centers.append(data[100])return centersdef distance(a ,b):'''欧式距离'''sum = 0for i in range(4):sq = (a[i]-b[i])*(a[i]-b[i])sum += sqreturn math.sqrt(sum)def point_avg(points):'''对维度求平均值'''new_center = []for i in range(4):sum = 0for p in points:sum += p[i]new_center.append(float("%.8f" % (sum/float(len(points)))))return new_centerdef updataCenters(data, assigments):new_means = defaultdict(list)centers = []for assigment, point in zip(assigments, data):new_means[assigment].append(point)'''将同⼀类的数据进⾏整合'''for i in range(3):points = new_means[i]centers.append(point_avg(points))return centersdef assignment(data, centers):assignments = []'''对应位置显⽰对应类群'''for point in data:'''遍历所有数据'''shortest = float('inf')shortestindex = 0for i in range(3):'''遍历三个中⼼向量,与哪个类中⼼欧⽒距离最短就将其归为哪类''' value = distance(point, centers[i])if value < shortest:shortest = valueshortestindex = iassignments.append(shortestindex)return assignmentsdef kmeans(data):k_data = generateCenters(data)assigments = assignment(data, k_data)old_assigments = Nonewhile assigments != old_assigments:new_centers = updataCenters(data, assigments)old_assigments = assigmentsassigments = assignment(data, new_centers)result = list(zip(assigments, data))return resultdef acc(result):sum = 0all = 0for i in range(50):if result[i][0] == 0:sum += 1all += 1for i in range(50):if result[i+50][0] == 1:sum += 1all += 1for i in range(50):if result[i+100][0] == 2:sum += 1all += 1print('sum:', sum, 'all:', all)return sum, allif__name__ == "__main__":data = loadIRISdata(dataname)result = kmeans(data)for i in range(3):tag = 0print('\n')print("第%d类数据有:" % (i+1))for tuple in range(len(result)):if(result[tuple][0] == i):print(tuple, end='')tag += 1if tag > 20 :print('\n')tag = 0#print(result)print('\n')sum, all = acc(result)print('c-means准确度为:%2f%%' % ((sum/all)*100))运⾏结果:。
K-Means原理及代码实现
K-Means 原理及代码实现对于有监督学习,我们知道其训练数据形式为T =(x (1),y (1)),(x (2),y (2)),⋯,(x (n ),y (n )),其中,x 表⽰样本实例,y 表⽰样本所属类别。
⽽对于⽆监督学习,训练数据不提供对应的类别,训练数据形式为T =(x (1)),(x (2)),⋯,(x (n ))。
这⾥,对⽆监督的聚类算法K-Means 进⾏总结。
1 K-Means 原理K-Means 作为聚类算法是如何完成聚类的呢?正如其算法名称,K 表⽰簇的个数,Means 表⽰均值。
(注: 在聚类中,把数据所在的集合称为簇)。
K-Means 算法的基本思想是将训练数据集按照各个样本到聚类中⼼的距离分配到距离较近的K 个簇中,然后计算每个簇中样本的平均位置,将聚类中⼼移动到该位置。
根据上⾯K-Means 算法的基本思想,K-Means 算法可以总结成两步:- 簇分配:样本分配到距离较近的簇- 移动聚类中⼼:将聚类中⼼移动到簇中样本平均值的位置假设有K 个簇(C 1,C 2,⋯,C k ),样本距离簇类中⼼的距离的表达式为:k∑i =1∑x ϵC i ‖其中C_{i}是第1到K 个最接近样本x 的聚类中⼼,\mu _{i}是该簇C_{i}中所有样本点的均值组成的向量。
\mu _{i}的表达式为:\mu _{i}=\frac{1}{|C_{i}|}\sum_{x\epsilon C_{i}}x举例,如何计算\mu _{2}的聚类中⼼?假设被分配到了聚类中⼼2的4个样本有:x^{(1)},x^{(5)},x^{(6)},x^{(10)},也就是C_{(1)}=2,C_{(5)}=2,C_{(6)}=2,C_{(10)}=2,\mu _{2}=\frac{1}{4}\left [ x^{(1)}+x^{(5)}+x^{(6)}+x^{(10)} \right ]\epsilon \mathbb{R}^{n}n 表⽰样本的特征个数。
KMeans实现
end
KMeans实现
for k = 1:K mu(k) := 根据得到的c, 计算出新的中心(平均值, 得到的centroids不一定要在样本中)
end }
损失函数
KMeans中的损失函数一般用于基于elbow判断K的取值
Processing math:, . . . , c(m), µ1, µ2, . . . , µk) = m∑mi= 1 | | x(i) − µc( i ) | | 2
选择K
方法1: 根据常识判断 方法2: 将K从1遍历到一个值, 如果得到CostFunction与K的图是elbow形状的, 则拐点为我们期望的值
请求出错错误代码400请尝试刷新页面重试
KMeans实现
符号
K: 聚类的个数 x(i): 第i个样本 µ1, µ2, . . . µK: K个中心节点 c(i): 第i个样本对应的是哪个聚类, c(i)的值在1-K m: 样本的数量 n: 特征的数量
实现
随机初始化中心centroids 在指定的迭代个数内 centroids = init_centroids Repeat within maxIter {
C#下实现的基础K-MEANS多维聚类
C#下实现的基础K-MEANS多维聚类资源下载#本⽂PDF版下载#本⽂代码下载前⾔最近由于上C # 课的时候,⽼师提到了-我们的课程成绩由⼏个部分组成.分别是「最终作品展⽰」「⼩组合作聊天记录评分」「组内成员匿名互评」「报告书评分」这四项综合评价.⽼师希望我能够通过这四个项⽬对所有同学进⾏聚类,然后根据离每簇的中⼼距离来评价最终的分数.由于我没有接触过这⽅⾯的算法,所以就选了实现较为⽅便并且直观的聚类⽅法K-MEANS.所以下⽂中就会对我这次学习到的⼀些⼼得进⾏分享.由于是C # 课程,所以本次的算法将以C# 为例⼦进⾏介绍.聚类&K-Means聚类百科上对于聚类的解释是这样的:将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类.由聚类所⽣成的簇是⼀组数据对象的集合,这些对象与同⼀个簇中的对象彼此相似,与其他簇中的对象相异.简单的来说聚类就是将相似的归在⼀起产⽣⼀个簇,使不相似的分在不同的簇⾥.聚类和分类的区别分类:从特定的数据中根据⼈为打上的数据表浅进⾏数据挖掘,做出判断.是将类别已知.并且样本数据已经做了标记的数据进⾏归类.是⼀个有监督的过程.聚类:⽬的是分类数据,但是在分类结束前我们都不知道数据是怎么分类的,有什么特点,只是根据算法,⾃动的将相似性的分类在了⼀起.数据本⾝没有标记.本⾝也没有类别.通过聚类的算法将相似性⾼的数据通过算法打上的标签归类在⼀起.是⼀种⽆监督的过程.K-Means算法K-Means算法是基于距离(我在这次中使⽤了欧⼏⾥德距离)的聚类算法 , 采⽤距离或者特征向量作为相似程度的考量,数据之间的距离/向量余弦越接近, 其相似度就越⼤.在K-Means聚类算法中-簇是由距离较为相近的数据对象构成的,故⽤K-Means算法的⽬的是想要得到数据对象相对紧凑且独⽴的不同簇.「评分系统」和K-Means的结合这次⽼师让我做的就是希望我能通过给出的「最终作品展⽰」「⼩组合作聊天记录评分」「组内成员匿名互评」「报告书评分」这四个维度的数据对所有同学的成绩使⽤K-Means聚成四个类,分别对应90-100/80-90/70-80/60-70/不及格这五个等第.分出登第后,五个类的中⼼分别是95/85/75/65/55,根据离数据中⼼的欧⼏⾥得距离按⽐例来决定最终的成绩.这就是⽼师让我实现的功能.K-Means的⾮适⽤性K-Means算法适应于连续形式的数据,由于数据的分布不同,有时候就⽆法分得准确的类,⽐如下⾯的两种形式1.⾮标准正态分布⾮标准正态分布K-Means⽤于表⽰聚类趋势只是说在数据符合正态分布或偏态不太严重时才是合理的.如果偏态严重,或者有异常的极⼤极⼩值,对均数影响很⼤,这时K-MEANS不能代表聚类的趋势.故在实际的样本中,我们作出的假设可能会并不适⽤,这个时候⽤基础的K-Means算法就不是特别的恰当了.这个时候我们就应该⽤到⼀些改进的算法,⽐如 Kernel K-means和Spectral Clustering.这两种算法会在后⾯的⽂章中进⾏介绍.2.⾮均匀分布⾮均匀分布由于分布的不均匀,疏密程度就会对中⼼的选取造成影响.就会偏向左侧较密的地⽅.如下所⽰:K-Means的优点和缺点优点:1. 算法简单并且效率很⾼,迭代次数.2. K-Means聚类算法的时间复杂度是O(nkt) {n数据量/k簇的个数/t算法迭代次数}故时间复杂度近似为线性.3. ⾃⾝具有优化迭代的特点,通过不断的计算中⼼来修正聚类结果.4. 对于数据有着很好的伸缩性.缺点:1. 需要指定K的值我们在每次聚类之前需要指定数据会被分成⼏类即要有⼏个中⼼点.2. 对于离群值较为敏感由于在每次聚类之后都要重新⽣成簇的中⼼.⽽中⼼是根据所有簇种数据对象的”质⼼”来的,所以当数据集不⼤的时候,这些离群的特殊点对中⼼就会产⽣很⼤的影响.这可以通过对数据集进⾏预处理,筛出离群的点.离群的点可以全部归类为第(k+1)类,因为它们本⾝有着⾮常独特的性质,是我们分析时候可以研究的对象.3. 初始点的选取对结果会产⽣影响初始的点我们⼀般通过随机选定,但是随机选出的点可能点和点之间并不合理,会造成最终聚类的结果会不相同.4. 最终的聚类结果是⼀个球形的簇由于使⽤了欧⼏⾥得距离,最终⼀定是以簇中⼼为球⼼(圆⼼)的⼀个球形(圆形)的簇.5. 维度对聚类结果的贡献⽆法得到⽆法确定哪⼀个维度对聚类结果的产⽣的影响⽐较⼤.K-Means的⼀些细节欧⼏⾥德距离欧⼏⾥德距离简称欧式距离,是我们平时经常⽤到的⼀个距离度量公式,具体就是基础求距离公式,并且拓展⾄了n维空间.我们以三维空间举⼀个例⼦,如下图所⽰.A,B点根据欧⼏⾥得距离公式算出来的就是中间灰⾊线上⽅的距离公式欧⼏⾥德公式“质⼼”公式计算“质⼼”的选取是因为每⼀次聚类后都需要通过簇中的数据对象⽣成新的中⼼,⽽⽣成新中⼼的⽅式就是产⽣所有数据对象的”质⼼”.公式也可以拓展⾄n维,这⾥我们以3维的空间为例⼦A/B/C三个点的”质⼼”就是图中的灰点,坐标为((X1+X2+X3)/3),(Y1+Y2+Y3)/3,(Z1+Z2+Z3)/3)拓展到n维就是((X1+…+Xn)/n,…,((Z1+…Zn)/n)).K-Means算法实现步骤这⾥我们介绍的是K-Means算法的本⾝步骤,没有加上任何的优化⽅式,总共分为 5 步,分别如下:1. ⾸先,就像上⽂中说的⼀样.K-Means算法必须提供k即簇的树⽬.使我们能够通过聚类算法得到k个分组2. 从我们要进⾏聚类的数据集中随机选择k个点作为每个簇的初始中⼼3. 通过⼀定的计算⽅法(欧⼏⾥德距离)来计算每个数据对象距离每⼀个簇中⼼的距离.并且得到距离最近的簇的中⼼点,那么这个数据对象就被归类到了这个簇中.4. 当所有的点都被聚类后,对于每⼀个簇算出新的中⼼.(通过算法找到”质⼼”).5. 迭代3,4两个步骤,直到4步骤选出来的新中⼼和原来的中⼼⼩于某个我们默认的阈值(⼏乎中⼼不再发⽣变化),算法停⽌.我们已经通过聚类算法得到了我们最终的结果.接下来我们通过举例⼦的⽅法来更好的理解⼀下K-Means算法,我们随机⽣成了七个点,它们的位置即坐标如下图所⽰K-Means算法1很明显,我们如果有监督的将他们分类,那么结果⼀定是如下图所⽰的情况K-Means算法2那么我们按照K-Means算法的步骤,看⼀下聚类的过程是怎么样的.1.⾸先我们先随机选择两个点作为簇的中⼼,就选A1/B3这两个点K-Means算法32.计算么个数据对象距离各个簇中⼼的距离对于A2 - 距A1的距离就是((1-2)^2 + (1-2)^2)^(1/2) = √2/距A2的距离就是((1-6)^2+(1-3)^2)^(1/2) = √29 由于√29>√2,所以A2被归类为以A1为中⼼的簇,同理,其他的点经过归类后的结果如下图所⽰:K-Means算法43.重新计算每个簇的中⼼我们现在得到了两个簇,现在我们把每个簇⾥的数据对象的中⼼求出来,根据质⼼的求法,我们可以知道红⾊A组的新中⼼是((2+1+3)/3,(2+1+1)/3) = (2,4/3),棕⾊B组的新中⼼是((5+6+6+7)/4,(4+4+3+2)/4) = (6,3)这个时候我们发现B的中⼼就是B3这个点,所以对于B组⽽⾔不需要再次进⾏聚类了.对于A组我们可以发现,A1,A2,A3距离新的中⼼(2,4/3)相等,故再进⾏计算新中⼼的话中⼼也不会发⽣改变,所以聚类停⽌.4.最终的聚类结果就是A1/A2/A3⼀组.B1/B2/B3/B4⼀组.K-Means在C#下算法的实现(在这⾥我展⽰的是没有优化过的K-Means实现)1.⾸先是初始化中⼼.根据我们设定的簇的数量,我们可以随机从所有的数据对象中选择k个点作为初始的中⼼点.这⾥只需要使⽤Random类的Next⽅法即可,所以此步骤不进⾏介绍.2.聚类步骤的实现代码特点解释:1. 我的程序是先将数据从Excel的Xls⽂件中导出到了C#控件中的ListView⾥,这个控件的名字是:XlsDataSh,在程序的代码⾥可以看到这个控件的名字.2. 由于我们的Xls⽂件中并不是只有这四个项的数据,其中的其他⼏项是另外的⼀些信息,有着编号/姓名/学号姓息分别占了ListView的0/1/2这三列,⽽3-6这四个列分别是对应四个维度的.这就是为什么循环中经常会出现类似for (int k = 3; k < 7; k++)的原意.我们先来看⼀下聚类这个⽅法中的运⾏步骤,如下所⽰:聚类步骤流程图解释:1. ArrayList中存储的事每⼀个簇所拥有的数据的编号.所以当我们每⼀次重新计算出了簇的中⼼之后,会对数据所在的簇进⾏重新聚类,故⾸先我们要把ArrayList中所存储的数据进⾏清除.2. 判断簇为0是⼀个重要的步骤.这也是我“偷懒”了之后⽤的⽅法,由于我们的初始中⼼是随意选择的,所以很有可能选择的数据中⼼再⼀次聚类之后没有分得任何的数据点,这时候这个类就是⽆效的.我们应该对它进⾏处理,⽽我⽤的⽅法就是偷懒的让它再次随机选定中⼼,直到每个类都⼀定能分到其他的数据点.3. ClassNum - 设定的簇的数⽬.即k4. RowCount - 数据对象的个数.即n下⾯就是聚类⽅法中的代码:private void Cluster(){int tmpclass = 0;double tmpClusDis = 0, tmpClusMinDis = 0;//清除每⼀个簇⾥原来的项for (int i = 0; i < ClassNum; i++){ClusterAssem[i].Clear();}//进⾏欧⼏⾥德距离计算并聚类for (int i = 0; i < RowCount - 1; i++){tmpClusMinDis = vurMax;for (int j = 0; j < ClassNum; j++){tmpClusDis = 0;//这⾥是取出每个维度的数据进⾏加和for (int k = 3; k < 7; k++){tmpClusDis += Math.Pow((System.Convert.ToDouble(XlsDataSh.Items[i].SubItems[k].Text) - CenterArrayParams[j, k - 3]), 2);}if (tmpClusDis < tmpClusMinDis){tmpclass = j;tmpClusMinDis = tmpClusDis;}}ClusterAssem[tmpclass].Add(i);}//重新初始化if (ClusterAssem[0].Count == 0 || ClusterAssem[1].Count == 0 || ClusterAssem[2].Count == 0 || ClusterAssem[3].Count == 0 || ClusterAssem[4].Count == 0) {InitCenter();//重新初始化中⼼的⽅法.Cluster();}}3.重新⽣成每个簇的中⼼解释:ClusterAssem - 存储每⼀个簇中包含的数据对象ArrayListRenewCenterArrayParams - 存储每个簇中⼼的各个维度的ArrayListprivate void RenewCenter() {double tmpSameDis = 0;for (int i = 0; i < ClassNum; i++) {for (int k = 3; k < 7; k++){tmpSameDis = 0;//遍历每个簇的点,求出中⼼点foreach (object n in ClusterAssem[i]) {tmpSameDis += System.Convert.ToDouble(XlsDataSh.Items[System.Convert.ToInt16(n)].SubItems[k].Text);}RenewCenterArrayParams[i, k - 3] = (tmpSameDis * 1.0 / (ClusterAssem[i].Count+1));}}}4.计算结束的标记结束标志的意图就是当中⼼不再发⽣变化或⼩于⼀个阈值的时候就停⽌算法的进⾏了.解释:1. BalEndFlag:⽤于存储前⼀次的中⼼加和⽤于和本次进⾏⽐对private Boolean CalEndFlag() {double tmpDifferDis = 0, tmpSameDis = 0;for (int i = 0; i < ClassNum; i++) {tmpSameDis = 0;for (int j = 0; j < 4; j++) {tmpSameDis += Math.Pow((RenewCenterArrayParams[i, j] - CenterArrayParams[i, j]),2);}tmpDifferDis += Math.Pow(tmpSameDis,1.0/2);}//判断中⼼点和前⼀次的偏移if ((BalEndFlag - tmpDifferDis) <= EndFlag) return false;else {//算法没有结束,所以将新的中⼼赋给⽤于计算的ArrayListfor (int i = 0; i < ClassNum; i++) {for (int j = 0; j < 4; j++) {CenterArrayParams[i, j] = RenewCenterArrayParams[i, j];}}BalEndFlag = tmpDifferDis;tmpDifferDis = 0;return true;}}结果展⽰下⾯就是我做的基于K—Means的⾃动聚类评分系统的界⾯.⾸先我们先看看有监督下的聚类是否符合预期,如下图所⽰:结果展⽰1我们通过上图的红⾊框中可以看出这个聚类的结果和我们的预期是⼗分的符合的.下⾯就是我通过随机的⽣成⽅法⽣成的数据,来观察K-Means算法在⽆监督下的运⾏结果.如下图所⽰:结果展⽰2以上就是本次探究的最终成果展⽰.K—Means的收敛性K-Means算法⼀定是收敛的,否则就会陷⼊⼀直寻找新的”质⼼”⽽没有最终的结果了.具体的证明步骤较为复杂.通过参考⼀些⽂章(会在下⽂中参考中标出)涉及到了E-M算法.将⼀个聚类问题转化为⼀个最⼤似然估计问题可证明K-Means是E-M算法的⼀个特例.在E-M算法下,求得的参数⼀定是收敛的.在K-Means算法中⽬标是使损失函数最⼩,所以在E-step时,找到⼀个最逼近⽬标的函数,在M-step时,固定这个函数(数据对象的分配),更新均值(簇的中⼼),所以最后⼀定会收敛了.基础K-Means算法的优化由于K-Means是⾮常成熟的算法,所以有很多先辈们改良了这个算法,在这⾥仅仅只是介绍在各个⽅⾯有哪些能够参考改进的⽅法.* 通过上⽂,我们可以知道.K-Means算法容易受到离群值的⼲扰.所以我们可以通过预处理⽐如「LOF算法」来先检测出离群点.单独处理这部分点. * K-Means的k值在没有指定的时候是很难选择的,这个时候,可以通过「k-Means算法的k值⾃适应优化⽅法」* 聚类的中⼼的选择.由于本⽂是随机选择初始的k个点,但是这是不合理的,应该是选择数据中距离尽可能远的K个点,这⾥也有⼀个算法「Canopy 算法」* 现在有许多改进了的K-Means的算法,⽐较出名的有「K-means++」「ISODATA」「Kernel K-means」等,在后⾯的⽂章中我会进⾏分享.参考。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
void RunKMeans(); // Overall control K-means process
void ShowClusters(); // Show results on screen
void SaveClusters(char *fname); // Save results to file
} /* endfor */
} /* endfor */
printf("Input patterns:\n");
for (i=0; i<NumPatterns; i++) {
printf("Pattern[%d]=(%2.3f,%2.3f)\n",i,Pattern[i][0],Pattern[i][1]);
printf("\n");
}
void System::RunKMeans(){
int converged;
int pass;
pass=1;
converged=FALSE;
while (converged==FALSE) {
printf("PASS=%d\n",pass++);
fscanf(InFilePtr, "%d", &SizeVector); // Read dimension of vector
fscanf(InFilePtr, "%d", &NumClusters); // Read # of clusters for K-Means
for (i=0; i<NumPatterns; i++) { // For each vector
//**************************************************************************
void System::InitClusters(){
int i,j;
printf("Initial cluster centers:\n");
char znum[40];
char *pnum;
pnum=&znum[0];
strcpy(zout,"d=sqrt(");
printf("The distance from pattern %d to cluster %d is calculated as:\n",c,p);
dist=0;
#define MAXPATTERN 20
#define MAXCLUSTER 10
char *f2a(double x, int width)
{ //transform double data into string
char cbuf[255];
int NumMembers;
};
struct aVector {//
double Center[MAXVECTDIM];
int Size;
};
class System {
private:
double Pattern[MAXPATTERN][MAXVECTDIM+1];
double dist,x; // between pattern vector, p, and cluster
int i; // center, c.
char zout[128];
} else {
if (d==0) {
strcat(cbuf,".");
strcat(cbuf,cp);
}
else {
k=-d;
strcat(cbuf,".");
for (i=0; i<SizeVector ;i++){
x=(Cluster[c].Center[i]-Pattern[p][i])*(Cluster[c].Center[i]-Pattern[p][i]);
strcat(zout,f2a(x,4));
if (i==0)
strcat(zout,"+");
FILE *InFilePtr;
int i,j;
double x;
if((InFilePtr = fopen(fname, "r")) == NULL)
return FAILURE;
fscanf(InFilePtr, "%d", &NumPatterns); // Read # of patterns
} /* endfor */
for (i=0; i<NumClusters; i++) {
printf("ClusterCenter[%d]=(%f,%f)\n",i,Cluster[i].Center[0],Cluster[i].Center[1]);
} /* endfor */
#define SUCCESS 1
#define FAILURE 0
#define TRUE 1
#define FALSE 0
#define MAXVECTDIM 20
char *cp;
int i,k;
int d,s;
cp=fcvt(x,width,&d,&s); //把一个浮点数转换为字符串
if (s) {
strcpy(cbuf,"-");
}
else {
strcpy(cbuf," ");
public:
system();
int LoadPatterns(char *fname); // Get pattern data to be clustered
void InitClusters(); // Step 1 of K-means algorithm
int NumClusters; // Number of clusters
void DistributeSamples(); // Step 3 of K-means algorithm
int CalcNewClustCenters();// Step 4 of K-means algorithm
} /* endif */
if (d>0) {
for (i=0; i<d; i++) {
cbuf[i+1]=cp[i];
} /* endfor */
cbuf[d+1]=0;
cp+=d;
strcat(cbuf,".");
strcat(cbuf,cp);
cp=&cbuf[0];
return cp;
}
// ***** Defined structures & classes *****
struct aCluster {// cluster structures
double Center[MAXVECTDIM];
int Member[MAXPATTERN];
printf("ClusterCenter[%d]=(%f,%f)\n",i,Cluster[i].Center[0],Cluster[i].Center[1]);
} /* endfor */
printf("\n");
}
int System::LoadPatterns(char *fname){
void ShowCenters();
};
void System::ShowCenters(){
int i,j;
printf("Cluster centers:\n");
for (i=0; i<NumClusters; i++) {
Cluster[i].Member[0]=i;
DistributeSamples();
converged=CalcNewClustCenters();
ShowCenters/
}
double System::EucNorm(int p, int c){ // Calc Euclidean norm of vector difference
aCluster Cluster[MAXCLUSTER];
int NumPatterns; // Number of patterns
int SizeVector; // Number of dimensions in vector
for (i=0; i<k; i++) {
strcat(cbuf,"0");
} /* endfor */
strcat(cbuf,cp);
} /* endif */
} /* endif */
for (j=0; j<SizeVector; j++) { // create a pattern
fscanf(InFilePtr,"%lg",&x); // consisting of all elements