Kmeans算法
Kmeans是简单的聚类分析算法。其常用在数据分析与人工智能中。
简单说,Kmeans算法就是把一个集合中的东西分为若干子集,这几个子集内的元素具有空间相近或者特点相近。
做法:
1. 随机选取K各中心点,生成对应的k个簇。
2. 遍历所有的数据点,依据“距离’”将每一个数据点划分到最近的中心点所在的簇。
3. 计算每个簇所有的数据点的平均值,并作为该簇新的中心。
4. 重复2-3步,直到这k个簇的中心点不再变化,或者达到我们规定的迭代次数。
注意:
1. kmean算法思想本身比较简单,但是设置k数量与初始中心点却比较难以确定。
2. 需要判断处理空聚类以防某个初始中心点设置不合理。
3. 这里的“距离”指欧氏距离或者是余弦相似度,也可以指某种特征,不要被“距离”二字局限
4. 每个数据点是几维的,那么数据中心也应该是几维的,这里kmeans可以想象成将高维空间中的若干个数据点分为若干团。(其实也有特殊情况)
Kmeans++算法
Kmeans++算法,主要可以解决初始中心的选择问题,不可解决k的个数。
Kmeans++主要思想是选择的初始聚类中心要尽量的远。
做法:
1. 在输入的数据点中随机选一个作为第一个聚类中心。
2. 对于所有数据点,计算它与最近的聚类中心的距离D(x)
3. 重新选择一个数据点作为聚类中心,选择原则:D(x)较大的点被选为聚类中心的概率较大。
4. 重复2-3直到选出k个聚类中心。
5. 运行Kmeans算法。
kmeans示例
PS:其初始中心的设置我在这直接取了数据点中最大与最小点的等分值,数据预处理部分代码去掉。
题目是对一个具有四波段的海洋遥感图做聚类分析。
将整幅图划分为若干块,然后对每个块取其波段均值,然后做聚类分析。
#include <iostream>
#include <cmath>
#include <list>
#include <fstream>
#include <cstdlib>
//kmeans算法
//数据预处理代码已去除
using namespace std;
class Map{
public:
int row,col;
int valid; //有效值
float value[4];
int which_center = -1;
public:
Map() {
row = 0;
col = 0;
valid = 0;
value[0] = value[1] =value[2] =value[3]= 0;
}
void calc_row_column(int x,int y) {
row = x*300;
col = y*300;
}
bool is_valid() {
return (value[0]!=0||value[1]!=0||value[2]!=0||value[3]!=0);
}
};
class vector4 {
public:
float val[4];
float& operator[](int n) {
return val[n];
}
vector4() {
val[0] = val[1] = val[2] = val[3] =0;
}
};
template<typename T_>
class KCenter {
public:
int index; //编号
vector4 center; //中心
int num; //成员数
void set_kcenter(int index,vector4 center){
this->index = index;
this->center = center;
}
typename list<T_>::iterator is_member(T_ map) {
for(typename list<T_>::iterator itr = member.begin(); itr!=member.end() ; itr++) {
if(*itr == map) {
return itr;
}
}
return member.end();
}
void push(T_ map) {
member.push_back(map);
map->which_center = index;
num ++;
}
void pop(T_ map) {
typename list<T_>::iterator pos = is_member(map);
if(pos != member.end()) {
(*pos)->which_center = -1;
member.erase(pos);
num--;
}
}
float distance_from_map(T_ samp) {
//计算samp距离KCenter的距离
float dist = 0;
dist += pow( (center[0]- (samp->value)[0]), 2);
dist += pow( (center[1]- (samp->value)[1]), 2);
dist += pow( (center[2]- (samp->value)[2]), 2);
dist += pow( (center[3]- (samp->value)[3]), 2);
return sqrt(dist);
}
void adjust_center() {
//依据成员重新调整自身中心
center[0] = center[1] = center[2] = center[3] = 0;
for(typename list<T_>::iterator itr = member.begin(); itr!=member.end() ; itr++) {
for(int j=0; j<4; j++)
center[j] += ((*itr)->value)[j];
}
for(int j=0; j<4; j++)
center[j] /= num;
}
float all_distance() {
float dist = 0;
for(typename list<T_>::iterator itr = member.begin(); itr!=member.end() ; itr++) {
dist += distance_from_map(*itr);
}
return dist;
}
private:
list<T_> member;
};
const int N_K = 4; //K中心个数
KCenter<Map*> kcenters[N_K];
//寻找初始簇的质心
void find_centers(vector4 centers[],int n,Map maps[],int n_maps) {
//平均取样值作为簇中心 (预先计算了数据中最大147最小值0)
int span = 147;
for(int i=0;i<n;i++) {
for(int j=0;j<4;j++)
centers[i][j] = i*span/(N_K-1);
}
for(int i=0;i<N_K;i++)
cout<<"cu "<<centers[i][0]<<' '<<centers[i][1]<<' '<<centers[i][2]<<' '<<centers[i][3]<<endl;
}
void init_centers(vector4 centers[],int n) {
//设置初始簇的质心
for(int i=0; i<n; i++) {
kcenters[i].set_kcenter(i,centers[i]);
}
}
bool divide(Map maps[],int n) {
//为每个样本分类
//并返回这次是否重新调整过的标志
float dist = 0;
int num_select = 0;
bool is_adjust = false;
for(int i=0; i<n; i++) {
//寻找最近簇
dist = kcenters[0].distance_from_map(maps+i);
num_select = 0;
for(int j=1; j<N_K; j++) {
if( dist > kcenters[j].distance_from_map(maps+i) ) {
dist = kcenters[j].distance_from_map(maps+i);
num_select = j;
}
}
if(maps[i].which_center == -1) {
//从未分类过
kcenters[num_select].push(maps+i);
is_adjust = true;
} else if (maps[i].which_center != num_select){
//被分到其他簇
//从之前簇取出
kcenters[maps[i].which_center].pop(maps+i);
//压入新簇
kcenters[num_select].push(maps+i);
is_adjust = true;
}
}
for(int i=0; i<N_K; i++)
kcenters[i].adjust_center();
return is_adjust;
}
/ 以下为获得有效数据 //
int n_maps = -1; //有效矩阵数
Map *maps;
int main() {
//get_data(maps,&n_maps);//读入原始数据 maps数组保存,n_maps表示元素个数
vector4 centers[5];
find_centers(centers,N_K,maps,n_maps);
init_centers(centers,N_K);
//各簇内数据不再变动作为迭代判断
while(divide(maps,n_maps));
for(int i=0;i<N_K;i++) {
cout<<kcenters[i].num<<' ';
for(int j=0;j<4;j++) cout<<kcenters[i].center[j]<<' ';
cout<<endl;
}
delete [] maps;
return 0;
}
分析结果为每种聚类赋一种颜色。