相同点:
k-means和混合高斯都是聚类算法,他们都是原型聚类,即需要初始化参数,应用于无监督的样本,每次划分后,所有的训练集都会有一个类别(这个区别于密度聚类,密度聚类划分后,有的训练集不一定有类别,这个训练集可能是噪声点)。
不同点:
对于簇数k
k-means算法在初始化时需要初始化簇数,这在某些情况下是不好估计簇数的
混合高斯算法是在运算中自己得出一个一个的簇,不用事先指定
K均值算法的优缺点:
优点:
1.对于大数据集,K均值聚类算法相对是可伸缩和高效的,它的计算复杂度是O(N*K*t)接近于线性,其中N是数据对象的数目,K是聚类的簇数,t是迭代的轮数。
2.尽管算法经常以局部最优结束,但一般情况下达到的局部最优已经可以满足聚类的需求。(因为在选取初始均值向量时,是随机选的,可以多选几次,通过最小平方误差来决定那个最好)
缺点:
1.需要人工预先确定初始K值,且该值和真实的数据分布未必吻合。
2.K均值只能收敛到局部最优,效果受到初始值很大。
3.易受到噪点的影响。
4.无法很好解决数据簇分布类别差别巨大的情况
k-means算法:(训练集为Iris数据集)
流程:
1.首先初步确定样本有几类,即确定簇数k;
2.然后根据簇数k,随机从样本中选择出k个样本作为初始的簇中心;
3.遍历样本集,计算出每个样本和每个簇中心的欧氏距离,并将这个样本归类到k个欧式距离中最小的欧氏距离的那一簇;
4.根据每个簇中的样本,更新簇中心;
5.重复3-4步,直至簇中心不变为止。
评价标准:最小化平方误差,DB指数
- 优点:容易实现
- 缺点:可能收敛到局部最小值,在大规模数据上收敛较慢
import math
import matplotlib.pyplot as plt
import random
import numpy as np
def readFile(filename, len):
"""
读取iris.txt
iris.txt为150行,每行有5个数据,前4个数据为特征,第5个为所属的类
参数
len:文件的行数
filename:读取的文件的地址
返回值
filesave:保存的数据
"""
f = open(filename, 'r')
filesave = [[] for i in range(len)] # 初始化全集
for i in range(len):
s = f.readline()
s = s.strip('\n')
s = s.strip(' ')
x = s.split('\t')
for j in range(5):
filesave[i].append(eval(x[j]))
return filesave
def AriGen():
fig = plt.figure()
data = []
k1 = []
for i in range(100):
x = random.randint(0, 50)
y = random.randint(0, 50)
temp = [x, y, 1]
while temp in k1:
x = random.randint(0, 50)
y = random.randint(0, 50)
temp = [x, y, 1]
k1.append(temp)
x1 = [k1[i][0] for i in range(len(k1))]
y1 = [k1[i][1] for i in range(len(k1))]
plt.scatter(x1, y1, c='y', linewidths=1)
k2 = []
for i in range(200):
x = random.randint(60, 140)
y = random.randint(60, 140)
temp = [x, y, 2]
while temp in k1 or temp in k2:
x = random.randint(60, 140)
y = random.randint(60, 140)
temp = [x, y, 2]
k2.append(temp)
x2 = [k2[i][0] for i in range(len(k2))]
y2 = [k2[i][1] for i in range(len(k2))]
plt.scatter(x2, y2, c='g', linewidths=1)
k3 = []
for i in range(300):
x = random.randint(150, 250)
y = random.randint(150, 250)
temp = [x, y, 3]
while temp in k1 or temp in k2 or temp in k3:
x = random.randint(150, 250)
y = random.randint(150, 250)
temp = [x, y, 3]
k3.append(temp)
x3 = [k3[i][0] for i in range(len(k3))]
y3 = [k3[i][1] for i in range(len(k3))]
plt.scatter(x3, y3, c='r', linewidths=1)
plt.title("init")
plt.show()
data.extend(k1)
data.extend(k2)
data.extend(k3)
return data
def data3():
x = [] # 数据集x属性
y = [] # 数据集y属性
x.extend(np.random.normal(loc=40.0, scale=10, size=100)) # 向x中放100个均值为30,方差为10,正态分布的随机数
x.extend(np.random.normal(loc=80.0, scale=10, size=200)) # 向x中放100个均值为80,方差为10,正态分布的随机数
x.extend(np.random.normal(loc=120.0, scale=10, size=300))
y.extend(np.random.normal(loc=80.0, scale=10, size=100)) # 向y中放100个均值为80,方差为10,正态分布的随机数
y.extend(np.random.normal(loc=40.0, scale=10, size=200)) # 向y中放100个均值为30,方差为10,正态分布的随机数
y.extend(np.random.normal(loc=120.0, scale=10, size=300))
c = [1] * 100 # c标签第一类为 1
c.extend([2] * 200) # # c标签第二类为 2
c.extend([3] * 300) # # c标签第二类为 3
# 生成训练集与测试集=======================================
lt = list(range(600)) # 得到一个顺序序列
random.shuffle(lt) # 打乱序列
data3 = []
for i in lt[0:600]: # 截取部分做训练集
temp = []
temp.append(x[i]) # 加上数据集x属性
temp.append(y[i]) # 加上数据集y属性
temp.append(c[i]) # 加上数据集c标签
data3.append(temp)
print(data3)
x = [data3[i][0] for i in range(len(data3))]
y = [data3[i][1] for i in range(len(data3))]
clor=['r','g','b']
for i in range(len(x)):
plt.scatter(x[i], y[i], c=clor[data3[i][-1]-1], linewidths=1)
return data3
def showData(data):
fig = plt.figure()
m = plt.axes(projection='3d')
color = ['r', 'g', 'b']
ls = [[] for j in range(len(data[0]))]
cindex = 0
for i in range(len(data)):
for j in range(len(data[0])):
ls[j].append(data[i][j])
if i % 49 == 0 and i != 0:
m.scatter3D(ls[0], ls[1], ls[2], c=color[cindex], alpha=0.8) # alpha透明度
cindex += 1
ls = [[] for j in range(len(data[0]))]
plt.title("init")
# plt.show()
class k_means:
def __init__(self, data, k=3, len=150, num=4):
"""
:param k:有k个簇
:param len: 数据集的长度
:param num: 有num个属性
:param data: 原始数据集
"""
self.k = k
self.len = len
self.num = num
self.data = data#[[],[],[]]
self.ls = [] # 保存初始均值向量
label=1
for i in range(k):#对每个簇,找出一个初始均值向量
temp = random.randint(0, len - 1)#随机整数
while self.data[temp][-1]!=label:
temp = random.randint(0, len - 1) # 随机整数
self.ls.append(self.data[temp][:-1])
label+=1
def method(self):
print("初始簇中心:",self.ls)
lst = self.ls
while 1:
cu = [[] for i in range(self.k)] # 初始化空,保存
flag = 0
# 根据均值向量进行分类
for i in range(len(self.data)): # 遍历数据集,对所有数据进行分类
value = [] # 用于保存距离
for k in range(self.k): # 遍历所有簇,计算每个簇的距离,保存到value中
tp = 0
for j in range(self.num): # 遍历这个样本的每个属性
tp += (lst[k][j] - self.data[i][j]) * (lst[k][j] - self.data[i][j])
tp = math.sqrt(tp)
value.append(tp)
cu[value.index(min(value))].append(self.data[i])
# 计算新的均值向量
for i in range(self.k): # 遍历cu每一个簇
tl = [0 for i in range(self.num)] # 用于保存新的均值向量
for j in range(len(cu[i])): # 遍历这个簇的所有数据
for p in range(self.num): # 遍历每个数据的每个属性
tl[p] += cu[i][j][p]
for j in range(self.num):
if len(cu[i]) != 0:
tl[j] /= len(cu[i])
result = 0 # 用于计算新的均值向量与旧的差距
for j in range(self.num):
result += abs(lst[i][j] - tl[j])
if result == 0: # 如果一样,那么flag+=1
flag += 1
else:
lst[i] = tl
if flag == self.k:
return cu,lst
def evaluation(self):
cu,lst= self.method()
print("最终簇中心:",lst)
if self.num==2:
self.show2D(cu)
else:
self.show(cu)
for i in range(self.k):
ls = [0 for i in range(self.k + 1)]
for j in range(len(cu[i])):
if (cu[i][j][-1] == 1):
ls[1] += 1
elif cu[i][j][-1] == 2:
ls[2] += 1
elif cu[i][j][-1] == 3:
ls[3] += 1
print("第{}簇中最多的是{},占比{}%".format(i + 1, ls.index(max(ls)), max(ls) / len(cu[i]) * 100))
print("DB指数为:",self.DB(cu,lst))
print("平方误差为:",self.errorRate(cu,lst))
def show(self, cu):
fig = plt.figure()
m = plt.axes(projection='3d')
color = ['r', 'g', 'b']
for i in range(self.k):
ls = [[] for j in range(self.num)]
for j in range(len(cu[i])):
for k in range(self.num):
ls[k].append(cu[i][j][k])
m.scatter3D(ls[0], ls[1], ls[2], c=color[i], alpha=0.8) # alpha透明度
plt.title("k-means")
# plt.show()
def show2D(self,cu):
fig = plt.figure()
color = ['r', 'g', 'b']
for i in range(self.k):
ls = [[] for j in range(self.num)]
for j in range(len(cu[i])):
for k in range(self.num):
ls[k].append(cu[i][j][k])
plt.scatter(ls[0], ls[1], c=color[i], alpha=0.8) # alpha透明度
plt.title("k-means")
def avg(self,ls):
t=0
for i in range(len(ls)):
for j in range(i+1,len(ls)):
t+=self.dist(ls[i],ls[j])
t=t*2/(len(ls)*(len(ls)-1))
return t
def dist(self,lst1,lst2):
t=0
for i in range(len(lst1)):
t+=(lst1[i]-lst2[i])*(lst1[i]-lst2[i])
t=math.sqrt(t)
return t
def DB(self,cu,lst):
db=0
for i in range(len(cu)):
l=[]
for j in range(len(cu)):
if i !=j:
l.append((self.avg(cu[i])+self.avg(cu[j]))/self.dist(lst[i],lst[j]))
db += max(l)
db=db/self.k
return db
def errorRate(self,cu,lst):
error=0
for k in range(len(cu)):
for i in range(len(cu[k])):
for j in range(len(cu[k][i])-1):
error+=(cu[k][i][j]-lst[k][j])*(cu[k][i][j]-lst[k][j])
return error
if __name__ == "__main__":
1.
# data = readFile("D:\\PythonProject_Class\\test_Data\\iris.txt", 150)
# showData(data)
# km = k_means(data)
# km.evaluation()
# plt.show()
2.
# data = data3()
# print(data)
# km = k_means(data, len=600, num=2)
# km.evaluation()
# plt.show()
3.
data = AriGen()
print(data)
km = k_means(data, len=600, num=2)
km.evaluation()
plt.show()
六.小结
1. 聚类是一种无监督的学习方法。聚类区别于分类,即事先不知道要寻找的内容,没有预先设定好的目标变量。
2. 聚类将数据点归到多个簇中,其中相似的数据点归为同一簇,而不相似的点归为不同的簇。相似度的计算方法有很多,具体的应用选择合适的相似度计算方法
3. K-means聚类算法,是一种广泛使用的聚类算法,其中k是需要指定的参数,即需要创建的簇的数目,K-means算法中的k个簇的质心可以通过随机的方式获得,但是这些点需要位于数据范围内。在算法中,计算每个点到质心得距离,选择距离最小的质心对应的簇作为该数据点的划分,然后再基于该分配过程后更新簇的质心。重复上述过程,直至各个簇的质心不再变化为止。
4. K-means算法虽然有效,但是容易受到初始簇质心的情况而影响,有可能陷入局部最优解。为了解决这个问题,可以使用另外一种称为二分K-means的聚类算法。二分K-means算法首先将所有数据点分为一个簇;然后使用K-means(k=2)对其进行划分;下一次迭代时,选择使得SSE下降程度最大的簇进行划分;重复该过程,直至簇的个数达到指定的数目为止。实验表明,二分K-means算法的聚类效果要好于普通的K-means聚类算法。