1.算法介绍
对于"监督学习"(supervised learning),其训练样本是带有标记信息的,并且监督学习的目的是:对带有标记的数据集进行模型学习,从而便于对新的样本进行分类。而在“无监督学习”(unsupervised learning)中,训练样本的标记信息是未知的,目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律,为进一步的数据分析提供基础。对于无监督学习,应用最广的便是"聚类"(clustering)。聚类算法"试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇”(cluster),通过这样的划分,每个簇可能对应于一些潜在的概念或类别。
kmeans算法又名k均值算法,K-means算法中的k表示的是聚类为k个簇,means代表取每一个聚类中数据值的均值作为该簇的中心,或者称为质心,即用每一个的类的质心对该簇进行描述。其算法思想大致为:先从样本集中随机选取 k个样本作为簇中心,并计算所有样本与这 k个“簇中心”的距离,对于每一个样本,将其划分到与其距离最近的“簇中心”所在的簇中,对于新的簇计算各个簇的新的“簇中心”。根据以上描述,我们大致可以猜测到实现kmeans算法的主要四点:簇个数k 的选择、各个样本点到“簇中心”的距离、根据新划分的簇,更新“簇中心”、重复上述2、3过程,直至"簇中心"没有移动。
2.运行环境
Pycharm
3.运行步骤
![](https://i-blog.csdnimg.cn/blog_migrate/c08623715f6f96b23b6f26c15f53bdf6.png)
4.运行结果
初始点 0 : [56.30228444197009, 92.03970140883656] 2
初始点 1 : [94.37542404009427, 43.714034475096156] 3
初始点 2 : [100.14581516307273, 41.18565216612042] 3
DB= 1.2699906930079754
第 0 簇,标签最多的类为 2 占比 100.0 %
第 1 簇,标签最多的类为 1 占比 97.0873786407767 %
第 2 簇,标签最多的类为 3 占比 100.0 %
![](https://i-blog.csdnimg.cn/blog_migrate/7fdc16cf339de1de3ab7db28b3a67fd0.png)
5.运行代码
import math # 数学
import random # 随机
import numpy as np
import matplotlib.pyplot as plt
# 计算两个样本距离
def dist(xy, a, b):
d_l = 0 # 储存当前距离值
for i in range(len(xy) - 1): # 对每个属性
d_l += (xy[i][a] - xy[i][b]) ** 2 # 计算平方和
d_l = d_l ** 0.5 # 开根号
return d_l
# 计算avg(C)
def avg(xy, Ci):
sum = 0
for i in Ci:
for j in Ci:
sum += dist(xy, i, j)
return sum * 2 / (len(Ci) * (len(Ci) - 1))
# 计算DB指数
def DB(xy, C, u):
sum = 0
for i in range(len(C)): # 每一簇
max_C = -float('inf')
for j in range(len(C)):
if i == j:
continue
dcen = 0
for k in range(len(xy) - 1): # 对每个属性
dcen += (u[i][k] - u[j][k]) ** 2 # 计算平方和
dcen = dcen ** 0.5 # 开根号
max_ = (avg(xy, C[i]) + avg(xy, C[j])) / dcen
if max_ > max_C:
max_C = max_
sum += max_C
return sum / len(C)
# k均值聚类算法,分k簇。
def jvlei_k(xy, k):
n = len(xy[0]) # 储存数据数。
u = [] # 储存簇的均值向量
C = [] # 储存簇划分
# 挑选k个不重复的样本。
i = 0
j = 0
u = random.sample(range(n), k) # (如果数据随机,这一步其实可以直接选前k个)
for i in range(k): # 对每个初始点。
l = u[i] # 暂存
u[i] = []
print('初始点', i, end='\t: ')
for j in range(len(xy) - 1): # 对每个属性
u[i].append(xy[j][l])
print(u[i], xy[j + 1][l])
while 1:
C = [] # 储存簇划分
for i in range(k): # k 簇
C.append([]) # 初始化C。
for i in range(n): # 所有的数据
d = float('inf') # 初始无穷大
l = 0 # 属于哪个簇
for j in range(k): # 看和每个簇的距离,找最小。
d_ = 0 # 储存当前距离值
for ii in range(len(xy) - 1): # 对每个属性
d_ += (xy[ii][i] - u[j][ii]) ** 2 # 计算平方和
d_ = d_ ** 0.5 # 开根号
if d_ < d: # 距离更近
d = d_ # 最近值更改
l = j # 属于第j簇
C[l].append(i) # 并入第l簇
u_ = [] # 新的均值向量
for i in range(k): # 重新计算每个簇的均值
u_.append([0] * (len(xy) - 1))
for j in C[i]: # 对簇中每个样本
for ii in range(len(xy) - 1): # 对每个属性
u_[i][ii] += xy[ii][j] # 求和
for ii in range(len(xy) - 1): # 对每个属性
u_[i][ii] /= len(C[i]) # 均值
l = 0 # 标记
for i in range(k): # 每个均值向量
for j in range(len(u[0])): # 每个属性
l += (u[i][j] - u_[i][j]) ** 2 # 求误差
if l < 0.01:
print('DB=', DB(xy, C, u))
return C
else:
u = u_[:]
# 簇分类和标签对比,返回【最多标签,最多标签所占比例】
def panduan(yi, Ci):
biaoqian = {} # 标签,出现次数
for i in Ci:
if yi[i] in biaoqian: # 存在
biaoqian[yi[i]] += 1 # 加一
else:
biaoqian[yi[i]] = 1 # 创建
max_ = -1 # 标签
max_t = -1 # 标签数
for i in biaoqian: # 所有标签
if biaoqian[i] > max_t: # 找最多的
max_ = i
max_t = biaoqian[i]
return [max_, (max_t * 100 / len(Ci))]
# 生成数据集=============================================
x = [] # 数据集x属性
y = [] # 数据集y属性
x.extend(np.random.normal(loc=040.0, scale=10, size=100)) # 向x中放100个均值为40,标准差为10,正态分布的随机数
x.extend(np.random.normal(loc=060.0, scale=10, size=200)) # 向x中放200个均值为60,标准差为10,正态分布的随机数
x.extend(np.random.normal(loc=100.0, scale=10, size=300)) # 向x中放300个均值为100,标准差为10,正态分布的随机数
y.extend(np.random.normal(loc=050.0, scale=10, size=100)) # 向y中放100个均值为50,标准差为10,正态分布的随机数
y.extend(np.random.normal(loc=100.0, scale=10, size=200)) # 向y中放200个均值为100,标准差为10,正态分布的随机数
y.extend(np.random.normal(loc=040.0, scale=10, size=300)) # 向y中放300个均值为40,标准差为10,正态分布的随机数
c = [1] * 100 # c标签第一类为 1 x均值:40 y均值:50
c.extend([2] * 200) # # c标签第二类为 2 x均值:60 y均值:100
c.extend([3] * 300) # # c标签第三类为 3 x均值:100 y均值:40
# 生成训练集与测试集=======================================
lt = list(range(600)) # 得到一个顺序序列
random.shuffle(lt) # 打乱序列
x1 = [[], [], []] # 初始化x1
for i in lt: # 做训练集
x1[0].append(x[i]) # 加上数据集x属性
x1[1].append(y[i]) # 加上数据集y属性
x1[2].append(c[i]) # 加上数据集c标签
# 生成簇划分 ==========================================
k = 3 # 分成三簇
C = jvlei_k(x1, k)
# 计算正确率===========================================
for i in range(k):
c = panduan(x1[2], C[i])
print('第', i, '簇,标签最多的类为', c[0], '占比', c[1], '%')
# 绘图 ===============================================
plt.figure(1) # 第一张画布
for i in range(len(x1[2])): # 对数据集‘上色’
if x1[2][i] == 1:
x1[2][i] = 'r' # 数据集 1 红色
else:
if x1[2][i] == 2:
x1[2][i] = 'y' # 数据集 2 黄色
else:
x1[2][i] = 'b' # 数据集 3 蓝色
plt.scatter(x1[0], x1[1], c=x1[2], alpha=0.5) # 绘点数据集
plt.xlabel('x') # x轴标签
plt.ylabel('y') # y轴标签
plt.title('Initial') # 标题
# 簇划分:==========================================
plt.figure(2) # 第二张画布
for i in range(k): # 对数据集‘上色’
for j in C[i]:
if i == 1:
x1[2][j] = 'r' # 簇 1 红色
else:
if i == 2:
x1[2][j] = 'y' # 簇 2 黄色
else:
x1[2][j] = 'b' # 簇 3 蓝色
plt.scatter(x1[0], x1[1], c=x1[2], alpha=0.5) # 绘点数据集
plt.xlabel('x') # x轴标签
plt.ylabel('y') # y轴标签
plt.title('End') # 标题
plt.show() # 显示