无监督学习
有监督学习
聚类算法(无监督分类)
分类算法
聚类算法在sklearn中有两种表现形式:
这篇blog中用的聚类算法就是:(中括号中的参数就是可填可不填)
输入数据应注意:
找出k个质心,将离这些质心最近的数据分到这些之心所代表的簇中。
样本到其所在簇的质心的距离
注:余弦距离通常用来衡量文本和文本之间的差异。
簇内平方和(又叫"inertia"):
整体平方和(又叫"total inertia"):
total inertia越小,簇内样本越相似,聚类效果越好。
因此,kmeans希望找到让total inertia最小的质心 一 最优化问题。
注:kmeans中的total inertia类似于别的方法中的损失函数,但是要注意的是kmeans并没有损失函数这个概念。
距离 ~ 质心 ~ inertia:
欧几里得距离~ 均值 ~最小化每个样本点到质心的欧式距离之和;
曼哈顿距离 ~ 中位数 ~最小化每个样本点到质心曼哈顿距离之和;
余弦距离~ 均值 ~最小化每个样本点到质心的余弦距离之和
注:实验证明,使用不同的距离,就应该选用不同的质心和不同的inertia搭配,只要选对,就可以达到不错的聚类效果。
~
class sklearn.cluster.KMeans (n_clusters=8, init=’k-means++’, n_init=10, max_iter=300, tol=0.0001,
precompute_distances=’auto’, verbose=0, random_state=None, copy_x=True, n_jobs=None, algorithm=’auto’)
下面的参数的使用在例代码中都能看到(除了选看的那一部分参数)
plus = KMeans(n_clusters = 10).fit(X)
plus.n_iter_ #明显看出,k-means++迭代的次数小
#12
random = KMeans(n_clusters = 10,init="random",random_state=420).fit(X)
random.n_iter_
#19
random = KMeans(n_clusters = 10,init="random",max_iter=10,random_state=420).fit(X)
y_pred_max10 = random.labels_
silhouette_score(X,y_pred_max10)
#0.3952586444034157
random.n_iter_
#9
random = KMeans(n_clusters = 10,init="random",max_iter=20,random_state=420).fit(X)
y_pred_max20 = random.labels_
silhouette_score(X,y_pred_max20)
#0.3401504537571701
random.n_iter_
#19
随机初始化质心,聚类结果易受初始化质心的影响;(eg:选取了一个质心,但是其距离其他的质心都很远,是个孤立点)
思想:逐个选取k kk个簇中心,且离其它簇中心越远的样本点越有可能被选为下一个簇中心。
具体实现步骤:
1.从数据集X中随机(均匀分布)选取一个样本点作为第一个初始聚类中心ci;
2.接着计算每个样本与当前已有聚类中心之间的最短距离,用D ( x )表示;然后计算每个样本点被选为下一个聚类中心的概率P ( x ),最后选择最大概率值所对应的样本点作为下一个簇中心;
3.重复步骤2,直至选出k个质心;
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
#自己创建数据集,创建好的数据集是已经被分好标签的
X, y = make_blobs(n_samples=500,n_features=2,centers=4,random_state=1)
fig, ax1 = plt.subplots(1)#fig是画布,ax1是对象,对象在画布中,图画在对象上展现在画布中
ax1.scatter(X[:, 0], X[:, 1]
,marker='o'#点的形状
,s=8 #点的大小
)
plt.show()
#展示这些点的分布
color = ["red","pink","orange","gray"]
fig, ax1 = plt.subplots(1)
for i in range(4):
ax1.scatter(X[y==i, 0], X[y==i, 1]
,marker='o' #点的形状
,s=8 #点的大小
,c=color[i]
)
plt.show()
#对这些数据使用聚类算法,看他的聚类效果怎么样
from sklearn.cluster import KMeans
n_clusters = 3
cluster = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
y_pred = cluster.labels_
y_pred
#结果太长,这里就不写了,是一个一维数组,里面只有0,1,2
pre = cluster.fit_predict(X)
pre == y_pred
#本来就是利用X来聚类的,聚类好后又作用于X肯定结果都是True
当样本量太大或特征数量太多时,计算起来很麻烦,为了简化计算,我们可以只用样本的一部分进行聚类,将聚类结果再作用于整个数据集,但是这样的聚类效果不确定好还是坏。
cluster_smallsub = KMeans(n_clusters=n_clusters, random_state=0).fit(X[:200])
y_pred_ = cluster_smallsub.predict(X)
y_pred == y_pred_
#因为用少量样本聚出来的类的效果不一定特别好,所以结果会出现很多False
centroid = cluster.cluster_centers_
centroid
#array([[-7.09306648, -8.10994454],
# [-1.54234022, 4.43517599],
# [-8.0862351 , -3.5179868 ]])
sklearn.cluster.k_means (X, n_clusters, sample_weight=None, init=’k-means++’, precompute_distances=’auto’,
n_init=10, max_iter=300, verbose=False, tol=0.0001, random_state=None, copy_x=True, n_jobs=None,
algorithm=’auto’, return_n_iter=False)
eg.
#函数k均值聚类
from sklearn.cluster import k_means
k_means(X,4,return_n_iter=True)#依次返回质心坐标,每个样本对应的簇标签,inertia及最佳迭代次数(结果太长了,这里就不写了)
inertia并不是越小越好,inertia不适合作为指标的原因有以下几点:
k对inertia的影响:
n_clusters = 4
cluster_ = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
inertia_ = cluster_.inertia_
inertia_
#908.3855684760603
n_clusters = 5
cluster_ = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
inertia_ = cluster_.inertia_
inertia_
#811.0841324482416
n_clusters = 6
cluster_ = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
inertia_ = cluster_.inertia_
inertia_
#733.1538350083074
#通过上面的例子可以发现,n_clusters越大,inertia_越小,但是我们并不能仅靠越来越多的聚类数来实现好的聚类效果,类数越少聚类效果越好才是我们应该追求的目标。
#同时也说明了inertia_并不是一个好的模型聚类效果评价指标
k均值聚类算法在一些细长簇,环形簇,或者不规则形状的流形上的表现:
这种情况很少出现,如果出现了一般都用分类方法,但是也有需要用聚类算法的;
from sklearn.metrics import silhouette_score
from sklearn.metrics import silhouette_samples
silhouette_score(X,y_pred)#在上面的例子中,y_pred是聚成3类得出的标签
#0.5882004012129721
silhouette_score(X,cluster_.labels_)#在上面的例子中,cluster_.labels_是聚成6类得出的标签
#0.5150064498560357
silhouette_samples(X,y_pred)
#每个样本的轮廓系数都返回了,内容太长,这里不写了
轮廓系数的优点:
轮廓系数的缺点:在凸性类上表现出"虚高"(即返回的分数比真实轮廓系数高),例如,基于密度进行的聚类,通过DBSCAN获得的聚类结果等;
基于轮廓系数选择合适的n_clusters:
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
for n_clusters in [2,3,4,5,6,7]:
n_clusters = n_clusters
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.set_size_inches(18, 7)
ax1.set_xlim([-0.1, 1])
ax1.set_ylim([0, X.shape[0] + (n_clusters + 1) * 10])
clusterer = KMeans(n_clusters=n_clusters, random_state=10).fit(X)
cluster_labels = clusterer.labels_
silhouette_avg = silhouette_score(X, cluster_labels)
print("For n_clusters =", n_clusters,
"The average silhouette_score is :", silhouette_avg)
sample_silhouette_values = silhouette_samples(X, cluster_labels)
y_lower = 10
for i in range(n_clusters):
ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
ith_cluster_silhouette_values.sort()
size_cluster_i = ith_cluster_silhouette_values.shape[0]
y_upper = y_lower + size_cluster_i
color = cm.nipy_spectral(float(i)/n_clusters)
ax1.fill_betweenx(np.arange(y_lower, y_upper)
,ith_cluster_silhouette_values
,facecolor=color
,alpha=0.7
)
ax1.text(-0.05
, y_lower + 0.5 * size_cluster_i
, str(i))
y_lower = y_upper + 10
ax1.set_title("The silhouette plot for the various clusters.")
ax1.set_xlabel("The silhouette coefficient values")
ax1.set_ylabel("Cluster label")
ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
ax1.set_yticks([])
ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
ax2.scatter(X[:, 0], X[:, 1]
,marker='o'
,s=8
,c=colors
)
centers = clusterer.cluster_centers_
# Draw white circles at cluster centers
ax2.scatter(centers[:, 0], centers[:, 1], marker='x',
c="red", alpha=1, s=200)
ax2.set_title("The visualization of the clustered data.")
ax2.set_xlabel("Feature space for the 1st feature")
ax2.set_ylabel("Feature space for the 2nd feature")
plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
"with n_clusters = %d" % n_clusters),
fontsize=14, fontweight='bold')
plt.show()
For n_clusters = 2 The average silhouette_score is : 0.7049787496083262
For n_clusters = 3 The average silhouette_score is : 0.5882004012129721
For n_clusters = 4 The average silhouette_score is : 0.6505186632729437
For n_clusters = 5 The average silhouette_score is : 0.56376469026194
For n_clusters = 6 The average silhouette_score is : 0.4504666294372765
For n_clusters = 7 The average silhouette_score is : 0.39092211029930857
并且:
CHI的优点:计算快;
#使用时间戳计算运行时间来显式表示calinski_harabasz_score的计算比轮廓系数的计算快得多
from time import time
t0 = time()
calinski_harabasz_score(X, y_pred)
time() - t0
#0.000997304916381836
t0 = time()
silhouette_score(X,y_pred)
time() - t0
#0.005983591079711914
#将时间戳转化为易懂的形式
import datetime
datetime.datetime.fromtimestamp(t0).strftime("%Y-%m-%d %H:%M:%S")
#'2021-05-29 15:23:25'
CHI的缺点:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise_distances_argmin #对两个序列的点进行距离匹配的函数
from sklearn.datasets import load_sample_image #导入图片的一个数据集
from sklearn.utils import shuffle #将一个序列:字典,列表,图片等打乱重排的函数
china = load_sample_image("china.jpg")
china
china.dtype #一般图片的数据类型都是'uint8'
#dtype('uint8')
china.shape #三维:长度*宽度*像素,像素是三个数,由三个数决定一个颜色
#(427, 640, 3)
china[0][0]
#array([174, 201, 231], dtype=uint8)
newimage = china.reshape((427 * 640,3)) #将所有数据拉成一行,方便下面进行颜色个数的判断
import pandas as pd
pd.DataFrame(newimage).drop_duplicates().shape #去除重复值来查看有多少种颜色,发现有9万多种
plt.figure(figsize=(15,15))
plt.imshow(china) #imshow显示三维数组形成的图片(图1)
flower = load_sample_image("flower.jpg")
plt.figure(figsize=(15,15))
plt.imshow(flower) #(图2)
处理完后发现有9w多种颜色,我们想把它压缩至64种颜色,可用聚类算法;
n_clusters = 64
china = np.array(china, dtype=np.float64) / china.max()
w, h, d = original_shape = tuple(china.shape)
assert d == 3,"一个格子中的特征数目不等于3种" #如果不等于3就报错,所报的错误就是后面的字符串的内容
image_array = np.reshape(china, (w * h, d)) #将所有数据拉成一行
image_array
'''
array([[0.68235294, 0.78823529, 0.90588235],
[0.68235294, 0.78823529, 0.90588235],
[0.68235294, 0.78823529, 0.90588235],
...,
[0.16862745, 0.19215686, 0.15294118],
[0.05098039, 0.08235294, 0.02352941],
[0.05882353, 0.09411765, 0.02745098]])
'''
image_array.shape
#(273280, 3)
注:reshape的函数解释
a = np.random.random((2,4))
a
'''array([[0.43516089, 0.8142793 , 0.08701465, 0.14891459],
[0.6404939 , 0.8470681 , 0.89773921, 0.78202351]])'''
a.reshape((4,2))
'''array([[0.43516089, 0.8142793 ],
[0.08701465, 0.14891459],
[0.6404939 , 0.8470681 ],
[0.89773921, 0.78202351]])'''
np.reshape(a,(4,2))
#答案与上一个一样
np.reshape(a,(2,2,2))
'''array([[[0.43516089, 0.8142793 ],
[0.08701465, 0.14891459]],
[[0.6404939 , 0.8470681 ],
[0.89773921, 0.78202351]]])'''
np.reshape(a,(3,2)) #报错,无论有几维,只要维度之积不变,就可以reshape
image_array_sample = shuffle(image_array, random_state=0)[:1000]
kmeans = KMeans(n_clusters=n_clusters, random_state=0).fit(image_array_sample)
kmeans.cluster_centers_
#返回64个质心坐标
labels = kmeans.predict(image_array)
labels.shape
#(273280,)
image_kmeans = image_array.copy()
for i in range(w*h):
image_kmeans[i] = kmeans.cluster_centers_[labels[i]]
image_kmeans
'''array([[0.73524384, 0.82021116, 0.91925591],
[0.73524384, 0.82021116, 0.91925591],
[0.73524384, 0.82021116, 0.91925591],
...,
[0.15546218, 0.1557423 , 0.12829132],
[0.07058824, 0.0754637 , 0.0508744 ],
[0.07058824, 0.0754637 , 0.0508744 ]])'''
pd.DataFrame(image_kmeans).drop_duplicates().shape
#(64, 3)
image_kmeans = image_kmeans.reshape(w,h,d) #从二维变回三维
image_kmeans.shape
#(427, 640, 3)
centroid_random = shuffle(image_array, random_state=0)[:n_clusters]
labels_random = pairwise_distances_argmin(centroid_random,image_array,axis=0)
labels_random.shape
#(273280,)
centroid_random
#返回64个质心的坐标
len(set(labels_random)) #集合之后,重复数据被除,结果只有64个数据
#64
image_random = image_array.copy()
for i in range(w*h):
image_random[i] = centroid_random[labels_random[i]]
image_random = image_random.reshape(w,h,d)
image_random.shape
#(427, 640, 3)
plt.figure(figsize=(10,10))
plt.axis('off')
plt.title('Original image (96,615 colors)')
plt.imshow(china)
plt.figure(figsize=(10,10))
plt.axis('off')
plt.title('Quantized image (64 colors, K-Means)')
plt.imshow(image_kmeans)
plt.figure(figsize=(10,10))
plt.axis('off')
plt.title('Quantized image (64 colors, Random)')
plt.imshow(image_random)
plt.show()