前言
最近老师布置的作业中含有Borderline-SMOTE算法,而k-means算法是其基础,我需要将论文中的模型改造为自己的模型,从论文中的伪代码写出自己的框架,下面是自己动手热身实现的第一版k-means算法,很简单,如有错误请多多指教。
k-means算法
该算法很简单,开始时随机选取k个核心,利用欧式距离不断将数据集的样本进行分类,直至核心不在变化或者收敛为止。本篇博客重点在于算法实现,如果不明白算法实现可以实际计算一遍,就会恍然大悟,没有比这更简单的算法了。
函数运行流程
文本的第一行分别为数据的行数和列数,从第二行起为数据集。
根据面向对象的编程方式,将k-means函数的过程拆分为5个函数,方便调用。
#include
#include
#include
#include
#include
#define ROW 10000
#define COLUMN 10
#define MAX 10
double data[ROW][COLUMN]; //支持的最大数据量
double core[MAX][COLUMN]; //最大支持K 10
double old_core[MAX][COLUMN]; //用于判断程序结束
double threshold = 0.0001 ; // 阈值
int _row = 0; //记录真实数据的行数
int _column = 0 ; //记录真实数据的列数
int k = 0 ;
using namespace std;
void readFile(string ); //进行读取数据
void k_means(); //算法函数
void initCore(); //初始化k个核心
double getDistance(int,int); //计算样本与核心之间的距离
void classify(); //对数据集进行归类
void adjustCore(); //调整核心
bool isOver(); //判断核心是否收敛
void saveFile(); //保存数据
readFile函数,代码中的_row和_column变量在读取文件时进行赋值,这样做是为了编程方便。
void readFile(string path)
{
ifstream input ;
input.open(path,iostream::in);
if(!input.is_open())
{
cout << "文件读取失败" << endl;
exit(-1);
}
input >> _row ;
input >> _column ;
for(int i=0;i < _row ;i++)
{
for(int j=0; j < _column ; j++)
{
input >> data[i][j];
}
}
input.close();
}
void saveFile(string path)
{
ofstream output;
output.open(path);
if(!output.is_open())
{
cout << "文件保存失败" << endl;
exit(-1);
}
for(int i = 0 ; i < _row ; i ++)
{
for(int j = 0 ; j <= _column ; j++)
{
output << data[i][j] ;
output << " ";
}
output << endl;
}
output.close();
}
该函数主要是编写逻辑,然后在具体实现逻辑函数。
void k_means()
{
initCore(); //初始化核心
classify(); //对样本集进行分类
adjustCore(); //调整核心
while(!isOver()) //判断核心是否收敛
{
classify(); //重新进行分类
adjustCore(); //调整核心
}
}
该函数使用rand()生成随机数随机选择k个核心,支持多维数据。
void initCore()
{
//随机选取k个中心
for(int i = 0 ; i < k ; i++)
{
int temp = rand() % _row ;
for(int j = 0 ; j < _column ; j++)
{
core[i][j] = data[temp][j] ;
}
}
}
该函数将每一行数据的最后一位后的一位作为改行所属类别,当然也可以用其他形势的编写方法。
void classify()
{
for(int i = 0 ; i < _row ; i++)
{
double minDistance = INT_MAX;
for(int j = 0 ; j < k ; j++)
{
double Distance = getDistance(i,j);
if(Distance < minDistance)
{
minDistance = Distance;
data[i][_column] = j; //标记改行所属类别
}
}
}
}
该函数将每个类别的所有样本相加求平均值来作为调整后的核心。
void adjustCore()
{
double temp[MAX][MAX]={0};
int count[MAX]={0};
for(int i = 0 ; i < _row; i++)
{
int index = data[i][_column];
for(int j = 0 ;j < _column ; j++)
{
temp[index][j] += data[i][j];
}
count[index]++;
}
for(int i=0 ; i< k ; i++)
{
for(int j = 0 ; j < _column; j++)
{
old_core[i][j] = core[i][j] ; //保存旧核
core[i][j] = temp[i][j] / count[i]; //生成新核
}
}
}
该函数通过比较新旧核心的距离是否小于阈值来判断是否收敛。
bool isOver()
{
for(int i=0; i < k ;i ++)
{
double sum = 0.0 ;
for(int j = 0 ; j < _column ; j++)
{
sum += pow(core[i][j] - old_core[i][j],2);
}
if(sum > threshold) //有大于阈值数据进行下一轮
{
return false;
}
}
return true;
}
该函数使用欧式距离来进行技术。
double getDistance(int i,int j)
{
double sum = 0.0 ;
for(k=0; k < _column ; k++)
sum += pow(data[i][k]-core[j][k],2);
return sum ;
}
Iris数据集:http://archive.ics.uci.edu/ml/machine-learning-databases/iris/
测试结果
真实值 分类值
1 0 0
2 0 0
3 0 0
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 0
12 0 0
13 0 0
14 0 0
15 0 0
16 0 0
17 0 0
18 0 0
19 0 0
20 0 0
21 0 0
22 0 0
23 0 0
24 0 0
25 0 0
26 0 0
27 0 0
28 0 0
29 0 0
30 0 0
31 0 0
32 0 0
33 0 0
34 0 0
35 0 0
36 0 0
37 0 0
38 0 0
39 0 0
40 0 0
41 0 0
42 0 0
43 0 0
44 0 0
45 0 0
46 0 0
47 0 0
48 0 0
49 0 0
50 0 0
51 1 2
52 2 2
53 1 2
54 2 2
55 2 2
56 2 2
57 2 2
58 2 2
59 2 2
60 2 2
61 2 2
62 2 2
63 2 2
64 2 2
65 2 2
66 2 2
67 2 2
68 2 2
69 2 2
70 2 2
71 2 2
72 2 2
73 2 2
74 2 2
75 2 2
76 2 2
77 2 2
78 1 2
79 2 2
80 2 2
81 2 2
82 2 2
83 2 2
84 2 2
85 2 2
86 2 2
87 2 2
88 2 2
89 2 2
90 2 2
91 2 2
92 2 2
93 2 2
94 2 2
95 2 2
96 2 2
97 2 2
98 2 2
99 2 2
100 2 2
101 1 1
102 2 1
103 1 1
104 1 1
105 1 1
106 1 1
107 2 1
108 1 1
109 1 1
110 1 1
111 1 1
112 1 1
113 1 1
114 2 1
115 2 1
116 1 1
117 1 1
118 1 1
119 1 1
120 2 1
121 1 1
122 2 1
123 1 1
124 2 1
125 1 1
126 1 1
127 2 1
128 2 1
129 1 1
130 1 1
131 1 1
132 1 1
133 1 1
134 2 1
135 1 1
136 1 1
137 1 1
138 1 1
139 2 1
140 1 1
141 1 1
142 1 1
143 2 1
144 1 1
145 1 1
146 1 1
147 2 1
148 1 1
149 1 1
150 2 1
#include
#include
#include
#include
#include
#define ROW 10000
#define COLUMN 10
#define MAX 10
double data[ROW][COLUMN]; //支持的最大数据量
double core[MAX][COLUMN]; //最大支持K 10
double old_core[MAX][COLUMN]; //用于判断程序结束
double threshold = 0.0001 ; // 阈值
int _row = 0; //记录真实数据的行数
int _column = 0 ; //记录真实数据的列数
int k = 0 ;
using namespace std;
void readFile(string ); //进行读取数据
void k_means(); //算法函数
void initCore(); //初始化k个核心
double getDistance(int,int); //计算样本与核心之间的距离
void classify(); //对数据集进行归类
void adjustCore(); //调整核心
bool isOver(); //判断核心是否收敛
void saveFile(); //保存数据
void saveFile(string path)
{
ofstream output;
output.open(path);
if(!output.is_open())
{
cout << "文件保存失败" << endl;
exit(-1);
}
for(int i = 0 ; i < _row ; i ++)
{
for(int j = 0 ; j <= _column ; j++)
{
output << data[i][j] ;
output << " ";
}
output << endl;
}
output.close();
}
void readFile(string path)
{
ifstream input ;
input.open(path,iostream::in);
if(!input.is_open())
{
cout << "文件读取失败" << endl;
exit(-1);
}
input >> _row ;
input >> _column ;
for(int i=0;i < _row ;i++)
{
for(int j=0; j < _column ; j++)
{
input >> data[i][j];
}
}
input.close();
}
//初始化核
void initCore()
{
//随机选取k个中心
for(int i = 0 ; i < k ; i++)
{
int temp = rand() % _row ;
for(int j = 0 ; j < _column ; j++)
{
core[i][j] = data[temp][j] ;
}
}
}
//生成新核
void adjustCore()
{
double temp[MAX][MAX]={0};
int count[MAX]={0};
for(int i = 0 ; i < _row; i++)
{
int index = data[i][_column];
for(int j = 0 ;j < _column ; j++)
{
temp[index][j] += data[i][j];
}
count[index]++;
}
for(int i=0 ; i< k ; i++)
{
for(int j = 0 ; j < _column; j++)
{
old_core[i][j] = core[i][j] ; //保存旧核
core[i][j] = temp[i][j] / count[i]; //生成新核
}
}
}
//获取各点到核的距离
double getDistance(int i,int j)
{
double sum = 0.0 ;
for(k=0; k < _column ; k++)
sum += pow(data[i][k]-core[j][k],2);
return sum ;
}
//进行聚类
void classify()
{
for(int i = 0 ; i < _row ; i++)
{
double minDistance = INT_MAX;
for(int j = 0 ; j < k ; j++)
{
double Distance = getDistance(i,j);
if(Distance < minDistance)
{
minDistance = Distance;
data[i][_column] = j;
}
}
}
}
bool isOver()
{
for(int i=0; i < k ;i ++)
{
double sum = 0.0 ;
for(int j = 0 ; j < _column ; j++)
{
sum += pow(core[i][j] - old_core[i][j],2);
}
if(sum > threshold) //有大于阈值数据进行下一轮
{
return false;
}
}
return true;
}
//采用欧式距离
void k_means()
{
initCore();
classify();
adjustCore();
while(!isOver())
{
classify();
adjustCore();
}
}
int main()
{
cin >> k;
readFile("C:\\Users\\Administrator\\Desktop\\1.txt");
k_means();
saveFile("C:\\Users\\Administrator\\Desktop\\2.txt");
getchar();
getchar();
return 0;
}
文件:链接: https://pan.baidu.com/s/1zpolypOUXSERBSuG98oHzw 提取码: 9v2s