聚类算法 K-Means和DBSCAN学习小结

文章目录

  • 引言
    • 无监督学习
  • k-means
    • 特点
    • 要点归纳
    • 代码
      • 导库
      • 导入一张图片
      • 构建算法
  • DBSCAN算法
    • dbScan中的一些概念
    • 流程
    • 参数的选择
      • 半径
      • 密度阈值
    • 特点
      • 优势
      • 劣势
  • 代码

引言

k-means聚类算法(k-means clustering algorithm)是一种迭代求解的聚类分析算法。也是一种无监督学习算法。
DBSCAN是一种著名的密度聚类算法,它使用一组关于“邻域”的参数来描述样本分布的紧密程度。

无监督学习

缺乏足够的先验知识,因此难以人工标注类别或进行人工类别标注的成本太高。根据类别未知(没有被标记结果)的训练样本解决模式识别中的各种问题,称之为无监督学习。

k-means

  • 随机生成k个初始点(质心),作为聚类的中心
  • 计算每个样本点和初始点的距离(一般使用欧式距离公式或曼哈顿距离),把样本归类为最近的那个初始点。这样就形成了K个聚集类,每一类结果被称为“簇”。
  • 计算每一簇的均值点,以它为新的质心,重新做一次距离计算。
  • 重复几次,直到达到指定的迭代次数或最小调整幅度阈值,则停止。

聚类算法 K-Means和DBSCAN学习小结_第1张图片

特点

kmeans:简单,快速,适合常规数据集。但是缺点也很明显:K值难确定、复杂度与样本呈线性关系、很难发现任意形状的簇。一些奇奇怪怪的样本,就很难分类好。
聚类算法 K-Means和DBSCAN学习小结_第2张图片

要点归纳

一个很简单的算法,和KNN有点像,主要就是注意下面几点。

  • K值的选择
  • 距离的度量
  • 质心的更新

代码

使用k-means压缩图片的灰度值

导库

import numpy as np
from sklearn.cluster import KMeans
#skimage就是Scikit-Image,搜索Scikit-Image可以安装
#pip install Scikit-Image
from skimage import io

导入一张图片

先准备一张图片,最好是小点的,不然代码要跑半天·····


# 使用Scikit-Image处理图片的读取和输出
img = io.imread('ttt.jpg')
# io.imshow(img)
# io.show()

# 我这张图片是宽高500*300像素,因此行列分别应该是300和500
rows = img.shape[0]
cols = img.shape[1]

构建算法

# 变化图像的形状,分为RGB三个通道
img = img.reshape(img.shape[0] * img.shape[1], 3)
# 因为图片色值范围是0——255,聚类选择创建一半的簇来压缩图片,使用KMeans指定簇就行,还有其他参数在这里不太需要。
k_means = KMeans(n_clusters=128)
k_means.fit(img)

# 簇的数量
clusters = np.asarray(k_means.cluster_centers_, dtype=np.uint8)
# 这里面是所有的样本点,因为每个像素点有RGB三个通道,所以应该是500*300*3=450000个值
labels = np.asarray(k_means.labels_, dtype=np.uint8)
# 将压缩后的图像形状变化回500*300
labels = labels.reshape(rows, cols)
print("__________________")
print(labels)
print("__________________")
print(labels.shape)

#灰度聚类压缩后的图片,会保存在根目录下
io.imsave('lll.jpg', labels)

输出结果:
__________________
[[ 40  40  90 ...  64  64  64]
 [ 40  40  40 ...  64  64  64]
 [ 40  40  40 ...  64  64  64]
 ...
 [ 73  73  73 ... 119  52  52]
 [  7   7   7 ... 119  52  52]
 [ 84  84   7 ...  11  52  52]]
__________________
(300, 500)

DBSCAN算法

dbScan中的一些概念

在描述具体的算法之前,我们首先定义几个相关的概念:

  • 邻域:对于任意样本i和给定距离e,样本i的e邻域是指所有与样本i距离不大于e的样本集合;
  • 核心对象:若样本i的e邻域中至少包含MinPts个样本,则i是一个核心对象;
  • 密度直达:若样本j在样本i的e邻域中,且i是核心对象,则称样本j由样本i密度直达;
  • 密度可达:对于样本i和样本j,如果存在样本序列p1,p2,…,pn,其中p1=i,pn=j,并且pm由pm-1密度直达,则称样本i与样本j密度可达;
  • 密度相连:对于样本i和样本j,若存在样本k使得i与j均由k密度可达,则称i与j密度相连。
  • 边界点:属于某一个类的非核心点,不能发展下线了。
  • 噪声点:不属于任何一个类簇的点,从任何一个核心点出发都是密度不。
  • 根据以上的概念,DBSCAN 将簇定义为:由密度可达关系导出的最大的密度相连样本集合。

流程

  1. 标记所有样本点为未分类
  2. 随机选择一个点为核心对象,找到所有密度可达的样本点
  3. 找到边界点后在找其他的核心对象,重复步骤2,但是不标记已分类的样本点
  4. 从更新后的核心对象集合重复执行2-3步直到核心对象都被遍历或移除。

参数的选择

参数一般都是经验之谈,没有什么规定说一定要这样用,总之就是根据实际情况吧。。

半径

可以根据K距离来设定:找突变点K距离:给定数据集P={p(i); i=0,1,…n},计算点P(i)到集合D的子集S中所有点 之间的距离,距离按照从小到大的顺序排序,d(k)就被称为k-距离。

密度阈值

一般取小一些。

特点

优势

  • 不需要指定簇个数
  • 可以发现任意形状的簇
  • 擅长找到离群点(检测任务)
  • 两个参数就够了

劣势

  • 高维数据有些困难(可以做降维)
  • 参数难以选择(参数对结果的影响非常大)
  • Sklearn中效率很慢(数据削减策略)

代码

import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

# 读取数据
beer = pd.read_csv('data.txt', sep=" ")

# 读取啤酒的四个属性 组成新的DataFrame
X = beer[["calories", "sodium", "alcohol", "cost"]]
# 训练kmeans模型
k_means = KMeans(n_clusters=3).fit(X)

# 将模型分类结果存到clusters列中 并排序
beer['clusters'] = k_means.labels_
beer.sort_values('clusters')

# from pandas.plotting import scatter_matrix

# 质心
cluster_centers = k_means.cluster_centers_
mean = beer.groupby('clusters').mean()
centers = mean.reset_index()
print(cluster_centers)
print("————————————")

print(centers)

# 画图
plt.rcParams['font.size'] = 14
colors = np.array(['red', 'green', 'blue', 'yellow'])

# calories和alcohol两个属性的聚类结果
# plt.scatter(beer["calories"], beer["alcohol"], c=colors[beer["clusters"]])
# plt.scatter(centers.calories, centers.alcohol, linewidths=3, marker='+', s=300, c='black')

# plt.xlabel("Calories")
# plt.ylabel("Alcohol")

# 两两对比不同属性间的聚类结果(可以做降维,但是在这里就练习一下,两两对比)
# matrix = scatter_matrix(beer[["calories", "sodium", "alcohol", "cost"]], s=100, alpha=1, c=colors[beer["clusters"]],
#                         figsize=(10, 10))
# plt.suptitle("this is title")
plt.show()

# 归一化处理
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_Scaled = scaler.fit_transform(X)
# print(X_Scaled)

# 归一化后再来看一下聚类结果
k_means = KMeans(n_clusters=3).fit(X_Scaled)
beer['scaled_clusters'] = k_means.labels_
beer.sort_values('scaled_clusters')
print("_____归一化处理后的聚类结果_____")
print(beer)

# 轮廓系数评估一下做归一化后是否更好
from sklearn import metrics

# 轮廓系数接近1 则说明聚类越合理,-1则应该分到其他类,为0说明在两个簇的边界
score_scaled = metrics.silhouette_score(X, beer.scaled_clusters)
score = metrics.silhouette_score(X, beer.clusters)
print("做了归一化后轮廓系数为:", score_scaled)
print("没做归一化轮廓系数为:", score)

# 在这里显然不做归一化反而更好
# 接下来确定K(几簇)
scores = []
for k in range(2, 20):
    k_means = KMeans(n_clusters=k).fit(X)
    score = metrics.silhouette_score(X, k_means.labels_)
    scores.append(score)

plt.plot(list(range(2, 20)), scores)
plt.suptitle("不同簇的轮廓系数")
plt.xlabel("K")
plt.ylabel("轮廓系数")
plt.show()
print(scores)

# DBSCAN代码实现 其他的没什么区别,只是建模调用的东西不一样
from sklearn.cluster import DBSCAN

db = DBSCAN(eps=10, min_samples=2).fit(X)

你可能感兴趣的:(A)