python机器学习 | 聚类算法之K-Means算法介绍及实现

本篇博客具体学习参考:
K-means聚类算法原理及python实现

1 K-Means算法介绍

1.1 聚类算法介绍

  • 对于"监督学习"(supervised learning),其训练样本是带有标记信息的,并且监督学习的目的是:对带有标记的数据集进行模型学习,从而便于对新的样本进行分类。
  • 而在“无监督学习”(unsupervised learning)中,训练样本的标记信息是未知的,目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律,为进一步的数据分析提供基础。对于无监督学习,应用最广的便是"聚类"(clustering)。
  • 聚类算法试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇”(cluster),通过这样的划分,每个簇可能对应于一些潜在的概念或类别。
  • 简单来说:聚类算法的样本集都是没有标签的,那我们就需要根据样本的特征,给样本数据集进行聚类。并且无标签的算法也叫做无监督学习。

1.2 K-means算法思想和流程介绍

kmeans算法,又称为k均值算法。K-means算法中的k表示的是聚类为k个簇,means代表取每一个聚类中数据值的均值作为该簇的中心,或者称为质心,即用每一个的类的质心对该簇进行描述。

  • 即,K-Means算法接受参数K;然后将样本数据集划分为K个聚类。获得的聚类需要满足:同一个聚类中的样本数据集相似度较高;而不同聚类中的样本数据集相似度较小。

  • 算法思想为:以空间中K个点为中心进行聚类(即先从样本集中随机选取 k个样本作为簇中心),对最靠近他们的对象归类(所有样本与这 k个“簇中心”的距离,对于每一个样本,将其划分到与其距离最近的“簇中心”所在的簇中)。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。

  • 算法流程:

  1. .先从没有标签的元素集合A中随机取K个元素,作为K个子集各自的质心。
  2. 分别计算剩下的元素到K个子集质心的距离,根据距离将元素分别划分到最近的子集。
  3. 根据聚类结果,重新计算质心(计算方法为子集中所有元素各个维度的算术平均数)
  4. 将集合A中全部元素按照新的质心然后再重新聚类。
  5. 重复第4步,直到聚类结果不再发生变化。

1.3 K-means算法的代码实现

1.3.1 简单例子

先简单举个例子,来串一下流程,再封装函数
例子:我们现在我们有A、B、C、D四个数据。其中,每个数据都有两个特征。现在我们需要给它们进行聚类。
在这里插入图片描述
聚类实现代码为:
导入库+读取数据

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.read_excel("test.xlsx",index_col=0)

在这里插入图片描述

  1. .先从没有标签的元素集合A中随机取K个元素,作为K个子集各自的质心。
# 初始化质心
c1 = (1,1)
c2 = (2,1)
  1. 分别计算剩下的元素到K个子集质心的距离,根据距离将元素分别划分到最近的子集。
# 每个点分别到c1与c2的距离  (广播机制)
# 点到点的距离公式为:np.sqrt((f1-x1)**2+(f2-x2)**2)
df["c1_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c1)**2,axis=1))
df["c2_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c2)**2,axis=1))
df
"""
需要比较 c1_distance 与 c2_distance 大小
- c1_dis小:1 否则赋值为0
- c2_dis小:1 否则赋值为0
"""
def test(s):
    if s[0]<s[1]:
        return 1
    else:
        return 0
    
def test2(s):
    if s[0]>s[1]:
        return 1
    else:
        return 0

df["group_c1"] = df.loc[:,"c1_distance":"c2_distance"].apply(test,axis=1)
df["group_c2"] = df.loc[:,"c1_distance":"c2_distance"].apply(test2,axis=1)
df

在这里插入图片描述
3. 根据聚类结果,重新计算质心(计算方法为子集中所有元素各个维度的算术平均数)
4. 将集合A中全部元素按照新的质心然后再重新聚类。

"""
此时并不平衡,需要去调整质心。
- c1-->1
- c2-->3
"""
c2 = df[df.group_c2==1].iloc[:,[0,1]].mean(axis=0).values  #根据聚类结果,重新计算质心(计算方法为子集中所有元素各个维度的算术平均数)
c2
# 每个点分别到c1与c2的距离
# 点到点的距离公式为:np.sqrt((f1-x1)**2+(f2-x2)**2)
df["c1_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c1)**2,axis=1))
df["c2_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c2)**2,axis=1))
df

def test(s):
    if s[0]<s[1]:
        return 1
    else:
        return 0
    
def test2(s):
    if s[0]>s[1]:
        return 1
    else:
        return 0

df["group_c1"] = df.loc[:,"c1_distance":"c2_distance"].apply(test,axis=1)
df["group_c2"] = df.loc[:,"c1_distance":"c2_distance"].apply(test2,axis=1)
df
plt.scatter(df.feature1.values,df.feature2.values,marker="*",s=200)
plt.scatter(c1[0],c1[1],color="r")
plt.scatter(c2[0],c2[1],color="r")
plt.show()

在这里插入图片描述
5. 重复第4步,直到聚类结果不再发生变化。

# 两个点的质心都需要调整
c1 = df[df.group_c1==1].iloc[:,[0,1]].mean(axis=0).values
c2 = df[df.group_c2==1].iloc[:,[0,1]].mean(axis=0).values

# 每个点分别到c1与c2的距离
# 点到点的距离公式为:np.sqrt((f1-x1)**2+(f2-x2)**2)
df["c1_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c1)**2,axis=1))
df["c2_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c2)**2,axis=1))
df

def test(s):
    if s[0]<s[1]:
        return 1
    else:
        return 0
    
def test2(s):
    if s[0]>s[1]:
        return 1
    else:
        return 0

df["group_c1"] = df.loc[:,"c1_distance":"c2_distance"].apply(test,axis=1)
df["group_c2"] = df.loc[:,"c1_distance":"c2_distance"].apply(test2,axis=1)
df
# 每个点分别到c1与c2的距离
# 点到点的距离公式为:np.sqrt((f1-x1)**2+(f2-x2)**2)
df["c1_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c1)**2,axis=1))
df["c2_distance"] = np.sqrt(np.sum((df.iloc[:,[0,1]].values-c2)**2,axis=1))
df

def test(s):
    if s[0]<s[1]:
        return 1
    else:
        return 0
    
def test2(s):
    if s[0]>s[1]:
        return 1
    else:
        return 0

df["group_c1"] = df.loc[:,"c1_distance":"c2_distance"].apply(test,axis=1)
df["group_c2"] = df.loc[:,"c1_distance":"c2_distance"].apply(test2,axis=1)

在这里插入图片描述

plt.scatter(df.feature1.values,df.feature2.values,marker="*",s=200)
plt.scatter(c1[0],c1[1],color="r")
plt.scatter(c2[0],c2[1],color="r")
plt.show()  # 质心分布合适

在这里插入图片描述
质心不再发生改变,并且质心与点的距离,和质心都不再改变,聚类完成。

1.3.2 python代码实现

import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
" 计算质心到点的距离"
def calcDis(dataSet, centroids, k):
    clalist=[]
    for data in dataSet:
        diff = np.tile(data, (k, 1)) - centroids  #相减   (np.tile(a,(2,1))就是把a先沿x轴复制1倍,即没有复制,仍然是 [0,1,2]。 再把结果沿y方向复制2倍得到array([[0,1,2],[0,1,2]]))
        squaredDiff = diff ** 2     #平方
        squaredDist = np.sum(squaredDiff, axis=1)   #和  (axis=1表示行)
        distance = squaredDist ** 0.5  #开根号
        clalist.append(distance) 
    clalist = np.array(clalist)  #返回一个每个点到质点的距离len(dateSet)*k的数组

    return clalist
""" 计算质心,求出样本属于哪一个簇 """
def classify(dataSet, centroids, k):
    # 计算样本到质心的距离
    clalist = calcDis(dataSet, centroids, k)
    # 分组并计算新的质心
    minDistIndices = np.argmin(clalist, axis=1)    #axis=1 表示求出每行的最小值的下标   
    newCentroids = pd.DataFrame(dataSet).groupby(minDistIndices).mean() #DataFramte(dataSet)对DataSet分组,groupby(min)按照min进行统计分类,mean()对分类结果求均值
    newCentroids = newCentroids.values
    # 计算变化量
    changed = newCentroids - centroids
 
    return changed, newCentroids
""" 点的簇分布 --> 展示出来"""
def showCluster(cluster): 
    
    """
    绘制样本点
    """
    # 用不同颜色形状来表示各个类别
    mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']  
    
    for i,points in enumerate(cluster):   # 取出簇
        # 因为同一个簇的需要绘制同样的颜色,所以需要得到同一个簇的索引
        markIndex = int(i)  
        for point in points :    # 取出属于簇中的每个样本点
            plt.plot(point[0],point[1],mark[markIndex])
    
    """
    绘制质心点
    """
    # 用不同颜色形状来表示各个类别
    mark = ['*r', '*b', '*g', '*k', '^b', '+b', 'sb', 'db', '<b', 'pb'] 
    
    # 绘制质心点
    for j in range(len(centroids)):
        plt.plot(centroids[j][0],centroids[j][1],mark[j], markersize = 20)
        plt.show
"""封装并 使用k-means分类"""
def kmeans(dataSet, k):
    # 随机取质心
    centroids = random.sample(dataSet, k)
    
    # 更新质心 直到变化量全为0
    changed, newCentroids = classify(dataSet, centroids, k)
    while np.any(changed != 0):
        changed, newCentroids = classify(dataSet, newCentroids, k)
 
    centroids = sorted(newCentroids.tolist())   #tolist()将矩阵转换成列表 sorted()排序
 
    # 根据质心计算每个集群
    cluster = []
    clalist = calcDis(dataSet, centroids, k) #调用欧拉距离
    minDistIndices = np.argmin(clalist, axis=1)

    for i in range(k):
        cluster.append([])
    for i, j in enumerate(minDistIndices):   #enumerate()可同时遍历索引和遍历元素 i对应索引,j对应元素(0,...,k-1),而元素对应的刚好是k
        cluster[j].append(dataSet[i])
    
    # 展示点的簇分布
    showCluster(cluster)
    
    # 计算平均损失,判断不同k的分类效果
    minDistance = np.min(clalist, axis=1)    # axis=1 表示求出每行的最小值 
    loss = np.mean(minDistance)   # # 计算距离变化值(损失)  样本跟它所属簇的误差求和/样本个数-->平均损失
    
    return centroids, cluster,loss
"""创建数据集 """
def createDataSet():
    data = pd.read_csv("data.csv").values
    plt.scatter(data[:,0],data[:,1])
    plt.show()
    data = np.array(data)
    data = data.tolist()
    return data

数据集分布如下:
在这里插入图片描述

if __name__=='__main__': 
    dataset = createDataSet()
    
    # 存储每个k值下的损失
    loss_list = []
    
    for k in range(2,10):  # k的范围在2,9
        centroids, cluster,loss = kmeans(dataset, k)
#         print('质心为:%s' % centroids)
#         print('集群为:%s' % cluster)
        loss_list.append(loss)
    

    # 观察k值与损失的关系
    plt.figure(figsize=(8,6))
    plt.plot(range(2,9),loss_list)  # 绘制不同k下的效果
    plt.xlabel('k')
    plt.ylabel("loss")
    plt.show()

k为9的分类情况
在这里插入图片描述
在这里插入图片描述

1.4 K-means评价

优点:容易实现
缺点:1)对K个初始质心的选择比较敏感,容易陷入局部最小值;2)K值是用户指定的,而用户很难去选择一个合适的完美的K值。又因为没有标签,很难进行评估;3)对于一些复杂的数据分布就无法进行正确的聚类,如下图。
在这里插入图片描述

1.5 K-means优化

解决对初始质心敏感问题:使用多次的随机初始化,计算每一次建模得到的代价函数的值,选取代价函数最小结果作为聚类结果。

解决k值选择困难问题——肘部法则:肘部法则实际上就是来观察代价函数与k之间的关系。找出拐点位置(肘部位置)的点也就是理想的K值取值。

如上例:
在这里插入图片描述

2 K-means的API

2.1 API介绍

from sklearn.cluster import KMeans

KMeans(
    n_clusters=8,
    *,
    init='k-means++',
    n_init=10,
    max_iter=300,
    tol=0.0001,
    precompute_distances='deprecated',
    verbose=0,
    random_state=None,
    copy_x=True,
    n_jobs='deprecated',
    algorithm='auto',
)
  • n_clusters: 即 K 值,一般需要多试一些 K 值来保证更好的聚类效果。你可以随机设置一些 K 值,然后选择聚类效果最好的作为最终的 K 值;
  • max_iter: 最大迭代次数,如果聚类很难收敛的话,设置最大迭代次数可以让我们及时得到反馈结果,否则程序运行时间会非常长;
  • n_init:初始化中心点的运算次数,默认是 10。程序是否能快速收敛和中心点的选择关系非常大,所以在中心点选择上多花一些时间,来争取整体时间上的快速收敛还是非常值得的。由于每一次中心点都是随机生成的,这样得到的结果就有好有坏,非常不确定,所以要运行 n_init 次, 取其中最好的作为初始的中心点。如果 K 值比较大的时候,你可以适当增大 n_init 这个值;
  • init: 即初始值选择的方式,默认是采用优化过的 k-means++ 方式,你也可以自己指定中心点,或者采用 random 完全随机的方式。自己设置中心点一般是对于个性化的数据进行设置,很少采用。random 的方式则是完全随机的方式,一般推荐采用优化过的 k-means++ 方式;
  • algorithm:k-means 的实现算法,有“auto” “full”“elkan”三种。一般来说建议直接用默认的"auto"。简单说下这三个取值的区别,如果你选择"full"采用的是传统的 K-Means 算法,“auto”会根据数据的特点自动选择是选择“full”还是“elkan”。我们一般选择默认的取值,即“auto” 。

2.2 API应用

2.2.1 国家的足球运动等级聚类

读取以下数据,根据2019年国际排名、2018世界杯、2015亚洲杯获奖情况,对国家足球进行聚类。
在这里插入图片描述

import numpy as np
import pandas as pd
"""
得出信息
- 20行样本数据
- 3个特征
- 有无缺失值:无
"""
df = pd.read_csv("athlates.csv",encoding="gbk")
df.info()
df.head()
# 获取三列特征值
train_x = df[["2019年国际排名","2018世界杯","2015亚洲杯"]]
df_02 = pd.DataFrame(train_x)

导入模块

from sklearn.cluster import KMeans
from sklearn import preprocessing
# 建立模型  设置为3个簇
k_model = KMeans(n_clusters=3)
k_model
# 数据归一化
min_max_scaler = preprocessing.MinMaxScaler()
train_x = min_max_scaler.fit_transform(train_x)
# 训练模型
k_model.fit(train_x)
predict_y = k_model.predict(train_x)
predict_y
df["聚类"] = predict_y
df

在这里插入图片描述

2.2.2 菊花图像的颜色分割

对如下图片进行颜色分割
在这里插入图片描述

from matplotlib.image import imread
from matplotlib import pyplot as plt
from sklearn.cluster import KMeans
image = imread("flowers.png")

# 三维:(行,列,颜色RGB)
image.shape     # (533, 800, 3)
# 转为像素点 --> 也就是 行*列 个像素点
# 重塑形状 指定为-1 也就是 行*列*颜色/颜色
X = image.reshape(-1,3)

# 像素点的个数,颜色
X.shape   # (426400, 3)
kmeans = KMeans(n_clusters=2).fit(X)
# 获取最终每个簇的质心位置
kmeans.cluster_centers_
# 以质心来表示同一个簇的值
print(kmeans.labels_)
print(kmeans.cluster_centers_)
# 将labels_传到cluster_centers_取到对应簇的值
seg_img = kmeans.cluster_centers_[kmeans.labels_]  # 像素点对应的簇的值 
print(seg_img.shape)  # (426400, 3)
seg_img
# 重塑图片形状  将像素点重置为图片形状
seg_img = seg_img.reshape(533,800,3)
seg_img
plt.figure(figsize=(14,8))
plt.imshow(seg_img)

在这里插入图片描述

2.2.3微信界面的颜色分割

对如下图片进行颜色分割
在这里插入图片描述

f = open("./weixin.jpg",'rb')

"""
获取每个像素的颜色特征,存储二维数组
- 获取图片每个像素点
    - 获取图片大小(行,列)
    - 嵌套循环来获取每个像素点
- 获取每个像素点的颜色特征
- 存储为二维数组
"""
import PIL.Image as image

data = []

img = image.open(f)
width,height = img.size

# 获取每一个像素点
for x in range(width):
    for y in range(height):
        # 获取每个像素点的RGB颜色特征
        c1,c2,c3 = img.getpixel((x,y))
        data.append([c1,c2,c3])
        
f.close()

data
"""
数据的归一化
"""
from sklearn.preprocessing import MinMaxScaler

# 构建归一化模型
min_max = MinMaxScaler()
# 训练
data = min_max.fit_transform(data)
data
"""
聚类
"""
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=2) # 2类
kmeans
kmeans.fit(data)
label = kmeans.predict(data)
label
"""
聚类的标识就为0或者1
除此之外,得到的聚类的结果是一维的向量
需要将它转化为图像尺寸的矩阵
"""
label = label.reshape([width,height])
label
"""
想要将聚类后分割的结果呈现出来,我们可以构建新的图片对象
"""
# 构建新的对象
pic_mark = image.new("L",(width,height))
pic_mark
"""
怎么将聚类好的颜色呈现上去
所以,我们需要将0,1转为颜色特征(灰度值)0-255

实现:
- 取出每个点,添加颜色值
    - 标签类别:1-->设置灰度值为255
    - 标签类别:0-->设置灰度值为127
"""
for x in range(width):
    for y in range(height):
        # 每个聚类的标签:为0,1
        # print(label[x,y])
        # print(int(256/(label[x,y]+1))-1)
        
        # 给每个像素点设置颜色值
        if label[x,y] == 1:
            pic_mark.putpixel((x,y),255)
        else:
            pic_mark.putpixel((x,y),127)
        
# 保存新的图片
pic_mark.save("wechat_mark.jpg","JPEG")

在这里插入图片描述

### 回答1: K-Means 聚类算法是一种常用的无监督学习算法,它可以将数据集划分为 K 个不同的类别,其中 K 是预先设定的。在 K-Means 算法中,我们需要指定 K 值和距离计算方法,然后通过迭代的方式不断调整聚类中心,直到达到某个停止准则为止。 下面我们以鸢尾花数据集为例,来实现 K-Means 聚类算法。 首先,我们需要导入数据集并进行预处理。这里我们使用 sklearn 中的 load_iris 函数来加载数据集,并使用 MinMaxScaler 对数据进行归一化处理: ``` python from sklearn.datasets import load_iris from sklearn.preprocessing import MinMaxScaler # 加载数据集 iris = load_iris() X = iris.data # 数据归一化 scaler = MinMaxScaler() X = scaler.fit_transform(X) ``` 接下来,我们需要实现 K-Means 算法。这里我们使用 scikit-learn 中的 KMeans 类来实现: ``` python from sklearn.cluster import KMeans # 设置 K 值 k = 3 # 初始化 KMeans 模型 kmeans = KMeans(n_clusters=k) # 训练模型并预测结果 y_pred = kmeans.fit_predict(X) ``` 最后,我们可以使用 Matplotlib 来可视化聚类结果: ``` python import matplotlib.pyplot as plt # 绘制聚类结果 plt.scatter(X[:, 0], X[:, 1], c=y_pred) plt.title("K-Means Clustering") plt.show() ``` 运行以上代码,即可得到鸢尾花数据的聚类结果。 ### 回答2: K-Means聚类算法是一种常用的无监督学习方法,能够对数据进行聚类。在K-Means算法中,通过计算数据点与聚类中心的距离,将数据点归类到距离最近的聚类中心,从而实现数据的聚类。 鸢尾花数据是机器学习中常用的数据集之一,包含了150个样本,每个样本有4个特征,分别是花萼长度、花萼宽度、花瓣长度和花瓣宽度。这些样本被分为三个类别,分别是山鸢尾、变色鸢尾和维吉尼亚鸢尾。 使用K-Means聚类算法对鸢尾花数据进行聚类的过程如下: 1. 随机选择K个初始聚类中心。K代表要将数据聚成的类别数,这里我们选择K=3,即将鸢尾花数据聚成3个类别。 2. 对每个数据点,计算其与各个聚类中心的距离,并将其归类到距离最近的聚类中心。 3. 更新每个聚类中心的位置,将其移动到所归类数据点的平均位置。 4. 重复步骤2和3,直到聚类中心不再发生变化或达到预定的迭代次数。 通过上述步骤,可以将鸢尾花数据聚类成3个类别。每个类别中的数据点具有相似的特征,并且与其他类别中的数据点的特征有较大的区别。 K-Means聚类算法的优点是简单易实现,计算效率高。然而,这种算法对初始聚类中心的选择较为敏感,可能会收敛到局部最优解。因此,在应用K-Means算法时,需要进行多次实验,以避免得到不理想的聚类结果。同时,K-Means算法对于离群点比较敏感,离群点可能会影响聚类结果的准确性。 ### 回答3: K-Means 聚类算法是一种常用的无监督学习算法,主要用于将数据集中的样本划分成不同的簇。下面以实现鸢尾花数据的聚类为例进行解释。 首先,我们需要加载鸢尾花数据集,该数据集包含了150个样本,每个样本有4个特征,分别是花萼长度、花萼宽度、花瓣长度和花瓣宽度。我们将这些样本表示为一个150x4的矩阵。 然后,我们需要确定簇的数量 k,即要将数据集划分成几个簇。在这里,我们可以根据经验或者领域知识来选择一个合适的值。 接下来,我们需要初始化 k 个簇的中心点。可以随机从数据集中选取 k 个样本作为初始的簇中心点。 然后,对于每个样本,我们计算其与各个簇中心点的距离,并将其分配给距离最近的簇中心点所在的簇。 接着,我们更新每个簇的中心点,即将每个簇中的样本的特征均值作为新的簇中心点。 最后,我们重复执行以上两个步骤,直到簇中心点不再发生变化,或者到达预定的迭代次数。 完成聚类后,我们可以根据簇的中心点和每个样本所属的簇来进行结果的分析和可视化。例如,可以绘制不同簇中心点的特征值分布图,以及将样本点按簇的标签进行颜色分类的散点图等。 K-Means 聚类算法能够有效地将数据集划分为不同的簇,实现了对样本的聚类。在鸢尾花数据集这个例子中,我们可以根据花萼和花瓣的特征值将鸢尾花分为不同的类别,从而更好地了解这些花的分类情况。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值