python机器学习系列:K-均值算法(K-Means)的实现及应用

K-均值算法(K-Means)属于无监督学习、聚类算法。即将无标签的数据集进行分类,并且无训练过程(监督学习的数据集才存在训练一说)等,又可理解为自动分类器

本文对于原作者的代码进行了一点修改以符合当今情况。

K-均值算法(K-Means)的实现

这个算法不算是很难理解,实际上很容易理解,在此就不多废话了。

简单说说等下代码实现的思想原理以及相关的需要注意的一些东西。关于这个实现的过程中,会想从前一样,使用自制的数据集样本,在其中会选择两个作为最初的中心点(亦可通过洗牌后进行选择),然后将剩下的数据集与这两个中心点进行计算,通过得出的距离大小使得离得哪个中心点近就归属于那个中心点的分类处(非0即1的分类),接着会从这些已经各就各位的点中得出平均点。我们会设置一个最大迭代次数以及一个固定公差数(迭代次数在此不够严谨,仅仅起到学习认知的作用),关于这个公差数是起到一个监督的作用,若是在未得出平均点之前的数据点与中心点之间的公差数值大于固定公差数阈值,就说明优化失败了,之后这次的优化就忽略,直接进行下一次的优化过程(得出最佳平均点就是一次又一次优化的过程,最终目标就是得出最佳的平均点,最具代表性的点)。最终我们会通过数据可视化来进行图表结果的展示。

以下是实现代码。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import numpy as np
自制训练数据集
X = np.array([[1,2],
[1.5,1.8],
[5,8],
[8,8],
[1,0.6],
[9,11],
[1,3],
[8,9],
[0,3],
[5,4],
[6,4]])
class K_Means():
def __init__(self, k=2, tol=0.001, max_iter=300):
self.k = k
self.tol = tol
self.max_iter = max_iter #最大迭代次数
def fit(self, data):
self.centroids = {} #质点圆心
#取两个作为中心点
for i in range(self.k):
self.centroids[i] = data[i]
for i in range(self.max_iter):
self.classifications = {}
for i in range(self.k):
self.classifications[i] = []
for featureset in data:
#计算点与点之间的距离,分配数据集进入各自合适的阵营
distances = [np.linalg.norm(featureset-self.centroids[centroid]) for centroid in self.centroids]
classification = distances.index(min(distances)) #取最小的距离索引位置点
self.classifications[classification].append(featureset)
prev_centroids = dict(self.centroids) #保留现在的原数据,之后计算公差要用到
for classification in self.classifications:
#得出平均点
self.centroids[classification] = np.average(self.classifications[classification], axis=0)
optimized = True
for c in self.centroids:
original_centroid = prev_centroids[c]
current_centroid = self.centroids[c]
if np.sum((current_centroid - original_centroid)/original_centroid*100.0) > self.tol:
print(np.sum((current_centroid - original_centroid)/original_centroid*100))
optimized = False
if optimized:
break
def predict(self, data):
#代入数据集与圆心点进行距离计算,并且进行分类0/1
distances = [np.linalg.norm(data-self.centroids[centroid]) for centroid in self.centroids]
classification = distances.index(min(distances))
return classification

下面是训练过程以及图表展示相关的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import matplotlib.pyplot as plt
plt.style.use('ggplot')
colors = ['g', 'r', 'c', 'b', 'k']
clf = K_Means()
clf.fit(X)
# 可视化中心原点
for centroid in clf.centroids:
plt.scatter(clf.centroids[centroid][0], clf.centroids[centroid][1], marker='o', color='k', s=150, linewidths=5)
#可视化已分类好的各就各位的点
for classification in clf.classifications:
color = colors[classification]
for featrueset in clf.classifications[classification]:
plt.scatter(featrueset[0], featrueset[1], marker='x', color=color, s=150, linewidths=5)
plt.show()

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import matplotlib.pyplot as plt
import numpy as np
plt.style.use('ggplot')
# 自制训练数据集
X = np.array([[1,2],
[1.5,1.8],
[5,8],
[8,8],
[1,0.6],
[9,11],
[1,3],
[8,9],
[0,3],
[5,4],
[6,4]])
colors = ['g', 'r', 'c', 'b', 'k']
print(colors)
class K_Means():
def __init__(self, k=2, tol=0.001, max_iter=300):
self.k = k
self.tol = tol
self.max_iter = max_iter #最大迭代次数
def fit(self, data):
self.centroids = {} #质点圆心
#取两个作为中心点
for i in range(self.k):
self.centroids[i] = data[i]
for i in range(self.max_iter):
self.classifications = {}
for i in range(self.k):
self.classifications[i] = []
for featureset in data:
#计算点与点之间的距离,分配数据集进入各自合适的阵营
distances = [np.linalg.norm(featureset-self.centroids[centroid]) for centroid in self.centroids]
classification = distances.index(min(distances)) #取最小的距离索引位置点
self.classifications[classification].append(featureset)
prev_centroids = dict(self.centroids) #保留现在的原数据,之后计算公差要用到
for classification in self.classifications:
#得出平均点
self.centroids[classification] = np.average(self.classifications[classification], axis=0)
optimized = True
for c in self.centroids:
original_centroid = prev_centroids[c]
current_centroid = self.centroids[c]
if np.sum((current_centroid - original_centroid)/original_centroid*100.0) > self.tol:
print(np.sum((current_centroid - original_centroid)/original_centroid*100))
optimized = False
if optimized:
break
def predict(self, data):
#代入数据集与圆心点进行距离计算,并且进行分类0/1
distances = [np.linalg.norm(data-self.centroids[centroid]) for centroid in self.centroids]
classification = distances.index(min(distances))
return classification
clf = K_Means()
clf.fit(X)
# 可视化中心原点
for centroid in clf.centroids:
plt.scatter(clf.centroids[centroid][0], clf.centroids[centroid][1], marker='o', color='k', s=150, linewidths=5)
#可视化已分类好的各就各位的点
for classification in clf.classifications:
color = colors[classification]
for featrueset in clf.classifications[classification]:
plt.scatter(featrueset[0], featrueset[1], marker='x', color=color, s=150, linewidths=5)
plt.show()

铺助理解链接:

展示结果

这样在这里就完成了实现的过程。这个算法真的不怎么难,至少相较于上次的SVM来说。

项目应用

sklearn的K-Means模型训练

这次用到的数据是再熟悉不过的titanic数据集,就是预测生死的那个kaggle入门级比赛的那个,哈哈。

数据地址:https://pythonprogramming.net/static/downloads/machine-learning-data/titanic.xls

在利用这个数据时,先将数据集进行简单的数据预处理,以及将非数值数据进行简单的数值转换(即将非数值型数值转换为数值型数据),之后代入现成的sklearn对应的K-Means模型进行训练,最后得出预测准确性。

下面是代码实现部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
from sklearn import preprocessing
import pandas as pd
plt.style.use('ggplot')
df = pd.read_excel('titanic.xls')
#简单的数据预处理
df.drop(['body','name'], 1, inplace=True)
df.fillna(0, inplace=True)
#处理非数值数据
def handle_non_numerical_data(df):
columns = df.columns.values
for column in columns: #取出各个列名作为遍历的基调
text_digit_vals = {}
def convert_to_int(val):
return text_digit_vals[val]
if df[column].dtype != np.int64 and df[column].dtype != np.float64:
column_contents = df[column].values.tolist() #转换为列表类型以便下方处理
unique_elements = set(column_contents) #去掉重复值
x = 0
for unique in unique_elements:
if unique not in text_digit_vals:
text_digit_vals[unique] = x #即在此将非数值的数据改为了数值型数据集
x+=1
df[column] = list(map(convert_to_int, df[column])) #将列名替代掉上面的unique非数值列名
return df
df = handle_non_numerical_data(df)
df.drop(['sex','boat'], 1, inplace=True)
X = np.array(df.drop(['survived'], 1).astype(float))
X = preprocessing.scale(X) #进行缩放,标准化
y = np.array(df['survived'])
clf = KMeans(n_clusters=2) #得出0/1
clf.fit(X)
correct = 0
for i in range(len(X)):
predict_me = np.array(X[i].astype(float))
predict_me = predict_me.reshape(-1, len(predict_me)) #得出每排的数据集
prediction = clf.predict(predict_me)
if prediction[0] == y[i]:
correct += 1
print(correct/len(X))

铺助理解链接:

下面是得出的结果:

由于数据简单的预处理了一下,所以准确性在0.3~0.7之间。

sklearn的K-Means模型自制数据集训练

这是用自制的数据集进行的模型训练,最后将结果进行图表展示,可以更好的理解这个在sklearn模块中的现成K-Means模型的运作情况。

以下是相关的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
plt.style.use('ggplot')
X = np.array([[1,2],
[1.5,1.8],
[5,8],
[8,8],
[1,0.6],
[9,11]])
clf = KMeans(n_clusters=2) #聚点设置,必须小于数据集的长度
clf.fit(X)
centroids = clf.cluster_centers_ #聚类中心坐标
labels = clf.labels_ #标签
colors = ['g.','r.','c.','b.'] #点加颜色配合
for i in range(len(X)):
plt.plot(X[i][0], X[i][1], colors[labels[i]], markersize = 25)
plt.scatter(centroids[:,0], centroids[:,1], marker='x', s=150, linewidths=5)
plt.show()

结果图表展示:

还是不错的,有助于理解学习。

用手动实现的K-Means算法训练数据集

在此将要使用上面实现的K-Means算法来训练上面的titanic数据集,将会输出预测准确性。

下面是实现的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import matplotlib.pyplot as plt
import numpy as np
plt.style.use('ggplot')
class K_Means():
def __init__(self, k=2, tol=0.001, max_iter=300):
self.k = k
self.tol = tol
self.max_iter = max_iter #最大迭代次数
def fit(self, data):
self.centroids = {} #质点圆心
#取两个作为中心点
for i in range(self.k):
self.centroids[i] = data[i]
for i in range(self.max_iter):
self.classifications = {}
for i in range(self.k):
self.classifications[i] = []
for featureset in data:
#计算点与点之间的距离,分配数据集进入各自合适的阵营
distances = [np.linalg.norm(featureset-self.centroids[centroid]) for centroid in self.centroids]
classification = distances.index(min(distances)) #取最小的距离索引位置点
self.classifications[classification].append(featureset)
prev_centroids = dict(self.centroids) #保留现在的原数据,之后计算公差要用到
for classification in self.classifications:
#得出平均点
self.centroids[classification] = np.average(self.classifications[classification], axis=0)
optimized = True
for c in self.centroids:
original_centroid = prev_centroids[c]
current_centroid = self.centroids[c]
if np.sum((current_centroid - original_centroid)/original_centroid*100.0) > self.tol:
print(np.sum((current_centroid - original_centroid)/original_centroid*100))
optimized = False
if optimized:
break
def predict(self, data):
#代入数据集与圆心点进行距离计算,并且进行分类0/1
distances = [np.linalg.norm(data-self.centroids[centroid]) for centroid in self.centroids]
classification = distances.index(min(distances))
return classification
df = pd.read_excel('titanic.xls')
#简单的数据预处理
df.drop(['body','name'], 1, inplace=True)
df.fillna(0, inplace=True)
#处理非数值数据
def handle_non_numerical_data(df):
columns = df.columns.values
for column in columns: #取出各个列名作为遍历的基调
text_digit_vals = {}
def convert_to_int(val):
return text_digit_vals[val]
if df[column].dtype != np.int64 and df[column].dtype != np.float64:
column_contents = df[column].values.tolist() #转换为列表类型以便下方处理
unique_elements = set(column_contents) #去掉重复值
x = 0
for unique in unique_elements:
if unique not in text_digit_vals:
text_digit_vals[unique] = x #即在此将非数值的数据改为了数值型数据集
x+=1
df[column] = list(map(convert_to_int, df[column])) #将列名替代掉上面的unique非数值列名
return df
df = handle_non_numerical_data(df)
df.drop(['sex','boat'], 1, inplace=True)
X = np.array(df.drop(['survived'], 1).astype(float))
X = preprocessing.scale(X) #进行缩放,标准化
y = np.array(df['survived'])
clf = K_Means()
clf.fit(X)
correct = 0
for i in range(len(X)):
predic_me = np.array(X[i].astype(float))
predic_me = predic_me.reshape(-1, len(predic_me))
prediction = clf.predict(predic_me)
if prediction == y[i]:
correct += 1
print(correct/len(X))

只是上面代码的连接罢了,代码本身也不难理解。

下面是输出的预测准确性:

额…效果不咋地…

这样一来这篇文章可以接近尾声了…不懂的地方可以去对应的课程去看看,还有的是,多看书,利用好搜索引擎。

---------------本文终---------------

文章作者:刘俊

最后更新:2019年01月02日 - 14:01

许可协议: 转载请保留原文链接及作者。