1. 先容

你需要的最周全的K-means聚类指南_质心_年夜众 AI快讯

每当我在网站上碰着推举引擎时,我都迫不及待地将其分解并理解它的底层是如何事情的。
我想这是成为数据科学家的最主要的品质之一!

这些系统真正令我着迷的是我们如何将类似的物品,产品和用户组合在一起。
这种分组或分段适用于各个行业。
这便是聚类观点在数据科学中如此主要的缘故原由。

聚类有助于我们以独特的办法理解我们的数据 - 通过将事物分组。

在本文中,我们将全面先容K-means聚类及其扩展。
我们将研究聚类,它为什么主要,它的运用,然后深入研究K-means聚类(包括如何在真实数据集上用Python实现它)。

2. 什么是聚类?

让我们用一个大略的例子来办理问题。
银行希望向其客户***优惠。
目前,他们查看每个客户的详细信息,并根据这些信息,决定该当向哪个客户供应哪个优惠。

现在,该银行可能拥有数百万客户。
分别查看每个客户的详细信息然后做出决定是否故意义?当然没有!
这是一个手动过程,须要花费大量韶光。
那么银行可以做些什么呢?一种选择是将其客户划分为不同的组。
例如,银行可以根据客户的收入对其进行分组:

银行现在可以制订三种不同的策略或优惠,每组一个。
在这里,他们不必为个人客户创建不同的策略,而只需制订3种策略。
这将减少韶光和人力。
我上面显示的组称为簇(clusers),创建这些组的过程称为聚类(clustering)。
在形式上,我们可以说:

聚类是基于数据中的模式将全体数据划分为组(也称为簇)的过程。

你能猜出聚类是哪种类型的学习问题吗?这是一个有监督还是无监督的学习问题吗?

考虑一下,并利用我们刚才看到的例子。
是的,聚类是一种无监督的学习问题!

2.1. 聚类为什么是一个无监督学习问题?

假设你正在开展一个须要预测市场发卖的项目:

或者,你的任务是预测是否批准贷款的项目:

在上面提到的两个例子中,我们有一个想要预测的固定的目标。
在发卖预测问题中,我们必须根据outlet_size,outlet_location_type等预测Item_Outlet_Sales,并且在贷款审批问题中,我们必须根据Gender,Married,ApplicantIncome等预测Loan_Status。

因此,当我们根据一组给定的预测因子变量或自变量去预测一个目标变量时,这些问题被称为监督学习问题。

现在,可能存在没有预测目标变量的情形。

没有任何固定目标变量的这些问题被称为无监督学习问题。
在这些问题中,我们只有自变量而没有目标/因变量。

在聚类中,我们没有预测目标变量。
我们查看数据然后考试测验进行类似的不雅观察并形身分歧的簇。
因此,这是一个无监督的学习问题。

我们现在知道什么是簇和聚类的观点。
接下来,让我们看一下在形成簇时我们必须考虑的这些簇的属性。

2.2. 簇的属性

还是以上面提到银行业务为例子,为大略起见,我们假设银行只想利用客户的收入和债务来进行细分。
他们网络了客户数据并利用散点图对其进行可视化:

X轴代表客户的收入,y轴代表债务的数额。
在这里,我们可以清楚地看到这些客户可以分为4个不同的组,如下所示:

这便是聚类若何从数据创建组(簇)。
银行可以进一步利用这些组制订策略并为其客户供应折扣。
那么让我们来看看这些组的属性。

属性1

组中的所有数据点该当彼此相似。
让我用上面的例子来解释它:

如果特定组中的客户彼此不相似,那么他们的哀求可能会有所不同,对吧?如果银行给他们相同的报价,他们可能不喜好它,对银行可能会比较失落望,因此效果并不理想。

同一组中拥有类似的数据点有助于银行利用有针对性的营销。
你可以考虑日常生活中的类似示例,并思考聚类将如何(或已经)影响业务计策。

属性2

来自不同组的数据点应尽可能不同。
如果你节制了上述属性,这将是直不雅观故意义的。
让我们再次利用相同的示例来理解此属性:

你认为上面哪种是更好的簇结果?如果你看一下案例一:

赤色和蓝色簇中的客户彼此非常相似。
赤色簇中的前四个点与蓝色簇中的前两个客户具有相似的属性,他们有高收入和高债务。
在这里,我们对它们进行了不同的聚类。
然而,如果你看看案例二:

赤色簇与蓝色簇中的客户完备不同。
赤色簇中的所有客户都有高收入和高负债,蓝色簇中的客户收入高,债务价。
显然,在这种情形下,我们有更好的客户群。

因此,来自不同簇的数据点应尽可能彼此不同,以形成更故意义的簇。

到目前为止,我们已经理解了聚类是什么以及聚类的不同属性。
但为什么我们须要聚类?让我们不才一节中清楚解答这个疑问,看一下簇的一些运用。

2.3. 聚类在真实天下中的运用

聚类是业界广泛利用的技能。
它实际上被用于各个领域,从银行到推举引擎,文档聚类到图像分割。

a. 客户细分

我们之前先容了这一点,聚类最常见的运用之一是客户细分。
但它不仅限于银行业务,该计策涉及各种功能,包括电信,电子商务,体育,广告,发卖等。

b. 文档聚类

这是聚类的另一种常见运用。
假设你有多个文档,并且须要将类似的文档集中在一起。
聚类可帮助我们对这些文档进行分组,使类似文档位于同一簇中。

c. 图像分割

我们还可以利用聚类来实行图像分割。
在这里,我们考试测验将图像中的相似像素聚拢在一起。
我们可以运用聚类来创建在同一组中具有相似像素的各个簇。

你可以参考这篇文章[1],理解我们如何利用聚类进行图像分割任务。

d. 推举引擎

聚类也可用于推举引擎。
假设你想向朋友推举歌曲。
你可以查看该人喜好的歌曲,然后利用聚类查找类似的歌曲,末了推举最相似的歌曲。

还有更多的运用,我相信你已经想过了。
你可以不才面的评论部分分享这些运用。
接下来,让我们看看我们如何评估我们聚类出来的簇。

2.4. 理解聚类的不同评估度量标准

聚类的紧张目的不仅仅是创建簇,而是创建好的和故意义的簇。
我们不才面的例子中看到了这一点:

在这里,我们只利用了两个特色,因此我们很随意马虎可视化并决定哪些簇更好。

不幸的是,这不是真实天下场景的事情办法,由于我们将拥有大量的特色。
让我们再次看一下客户细分示例,我们将拥有客户收入,职业,性别,年事等功能。
将所有这些功能可视化并决定更好,更故意义的簇对我们来说是不可能的。

这是我们可以利用评估指标的地方。
让我们谈论个中的一些,并理解我们如何利用它们来评估簇的质量。

a. Inertia

回忆一下上面先容的簇的第一个属性。
这便是Inertia评估。
Inertia实际上打算簇内所有点到该簇的质心的间隔的总和。

我们为所有簇打算这个总和,终极Inertia值是所有这些间隔的总和。
簇内的这个间隔称为簇内间隔(intracluster distance.)。
因此,Inertia为我们供应了簇内间隔的总和:

现在,你认为一个好的簇的Inertia值该当是什么?小的Inertia好还是大的Inertia好?我们希望同一簇中的点彼此相似,对吧?因此,它们之间的间隔应尽可能小。

记住这一点,我们可以说Inertia越小,我们的聚类越好。

b. Dunn Index

我们现在知道Inertia试图最小化簇内间隔。
它正试图划分更紧凑的簇。

让我这样说吧 - 如果一个簇的质心与该簇中的点之间的间隔很小,则意味着这些点彼此更靠近。
因此,Inertia可确保知足簇的第一个属性。
但它并不关心第二个属性,不同的簇应尽可能彼此不同。

这便是Dunn Index可以用来评估的地方。

除了质心和点之间的间隔,Dunn Index还考虑了两个簇之间的间隔。
两个不同簇的质心之间的间隔称为簇间间隔(inter-cluster distance)。
让我们看看Dunn Index指数的公式:

Dunn Index是簇间间隔的最小值与簇内间隔的最大值之比。

我们希望最大化Dunn Index,Dunn Index的值越大,簇就越好。
让我们直不雅观理解下Dunn Index:

为了最大化Dunn Index的值,分子该当是最大的。
在这里,我们采取最小的簇间间隔。
因此,纵然是最近的簇之间的间隔也该当更大,这终极将确保簇彼此阔别。

此外,分母该当是最小的,以最大化Dunn Index。
在这里,我们采纳最大的簇内间隔。
同样,这里的直觉也是一样的。
簇质心和点之间的最大间隔总和该当最小,这终极将确保划分的簇紧凑。

3. K-means聚类先容

回忆一下簇的第一个属性,它表明簇中的点该当彼此相似。
因此,我们的目标是最小化簇内点之间的间隔。

有一种算法试图通过它们的质心最小化簇中点的间隔,那便是K-means聚类技能。

K-means是一种基于质心的算法,或基于间隔的算法,我们打算将点分配给一个簇的间隔。
在K-means中,每个聚类都与一个质心干系联。

K-means算法的紧张目的是最小化点与它们各自的簇质心之间的间隔之和。

现在让我们举个例子来理解K-means实际上是如何事情的:

我们有这8个点,我们想要运用K-means来为这些点划分簇。
下面将演示我们是若何做到的。

第一步:选择簇的数目k

K-means的第一步是选择簇的数目k。

第二步:从数据中选择k个随机点作为质心

接下来,我们为每个簇随机选择质心。
假设我们想要有2个簇,以是k在这里即是2。
然后我们随机选择质心:

这里,赤色和绿色圆圈代表这些簇的质心。

第三步:将所有点分配给到某个质心间隔最近的簇

一旦我们初始化了质心,我们就将每个点分配给到某个质心间隔最近的簇:

在这里,你可以看到更靠近红点的点被分配给赤色簇,而更靠近绿点的点被分配给绿色簇。

第四步:重新打算新形成的簇的质心

现在,一旦我们将所有点分配到任一簇中,下一步便是打算新形成的簇的质心:

在这里,赤色和绿色叉号是新的质心。

第五步:重复第三步和第四步

然后我们重复第三步和第四步:

打算质心并基于它们与质心的间隔将所有点分配给簇的步骤是单次迭代。
但我们什么时候该当停滞这个过程?它不能永久运行下去对吧?

停滞K-means聚类的标准

基本上有三种停滞标准可用于停滞K-means算法:

新形成的簇的质心不会改变数据点保留在同一个簇中达到最大迭代次数

如果新形成的簇的质心没有变革,我们就可以停滞算法。
纵然在多次迭代之后,所有簇都还是相同的质心,我们可以说该算法没有学习任何新模式,并且它是停滞演习的标志。

另一个明显的迹象表明,在多次迭代演习的之后,如果数据点仍旧都在同一簇中,我们该当停滞演习过程。

末了,如果达到最大迭代次数,我们可以停滞演习。
假设我们将迭代次数设置为100。
在停滞之前,该过程将重复100次迭代。

从零开始在Python中实现K-means聚类

是时候启动我们的 Jupyter notebooks(或者你利用的任何一款IDE)!

我们将在大型市场发卖数据集上进行处理,你可以在此处***[2]。

我建议你在此处[3]阅读有关数据集和问题陈述的更多信息。
这将帮助你可视化我们正在处理的事情(以及我们为什么这样做)。
这是任何数据科学项目中的两个非常主要的问题。

首先,导入所有必需的库:

import pandas as pdimport numpy as npimport random as rdimport matplotlib.pyplot as plt

现在,我们将读取CSV文件并查看数据的前五行:

data = pd.read_csv('clustering.csv')data.head()

3对付本文,我们将仅从数据中获取两个变量:“LoanAmount”和“ApplicantIncome”。
这样也可以轻松地将步骤可视化。
让我们选择这两个变量并可视化数据点:

X = data[[\公众LoanAmount\公众,\"大众ApplicantIncome\公众]]#可视化plt.scatter(X[\"大众ApplicantIncome\公众],X[\公众LoanAmount\公众],c='black')plt.xlabel('AnnualIncome')plt.ylabel('Loan Amount (In Thousands)')plt.show()

K-means的步骤1和2是关于选择簇数(k)和为每个簇选择随机质心。
我们将选择3个簇,然后从数据中随机选择样本作为质心:

#簇的个数K=3# 随机选择不雅观察值作为簇心Centroids = (X.sample(n=K))plt.scatter(X[\公众ApplicantIncome\公众],X[\公众LoanAmount\公众],c='black')plt.scatter(Centroids[\"大众ApplicantIncome\公众],Centroids[\公众LoanAmount\"大众],c='red')plt.xlabel('AnnualIncome')plt.ylabel('Loan Amount (In Thousands)')plt.show()

这里,红点代表每个簇的3个质心。
请把稳,我们是随机选择了这些点,因此每次运行此代码时,你可能会得到不同的质心。

接下来,我们将定义一些条件来实现K-means聚类算法。
我们先来看看代码:

# 第三步:将所有点分配给到某个质心间隔最近的簇# 第四步:重新打算新形成的簇的质心# 第五步:重复第三步和第四步diff = 1j=0while(diff!=0): XD=X i=1 for index1,row_c in Centroids.iterrows(): ED=[] for index2,row_d in XD.iterrows(): d1=(row_c[\"大众ApplicantIncome\"大众]-row_d[\"大众ApplicantIncome\公众])2 d2=(row_c[\公众LoanAmount\公众]-row_d[\"大众LoanAmount\"大众])2 d=np.sqrt(d1+d2) ED.append(d) X[i]=ED i=i+1 C=[] for index,row in X.iterrows(): min_dist=row[1] pos=1 for i in range(K): if row[i+1] < min_dist: min_dist = row[i+1] pos=i+1 C.append(pos) X[\"大众Cluster\"大众]=C Centroids_new = X.groupby([\"大众Cluster\"大众]).mean()[[\"大众LoanAmount\"大众,\"大众ApplicantIncome\"大众]] if j == 0: diff=1 j=j+1 else: diff = (Centroids_new['LoanAmount'] - Centroids['LoanAmount']).sum() + (Centroids_new['ApplicantIncome'] - Centroids['ApplicantIncome']).sum() print(diff.sum()) Centroids = X.groupby([\"大众Cluster\"大众]).mean()[[\"大众LoanAmount\"大众,\"大众ApplicantIncome\"大众]]

每次运行时,这些值可能会有所不同。
在这里,我们在两次迭代后质心没有变革时停滞演习。
我们最初将diff定义为1并且在while循环内部,我们将此diff打算为前一次迭代中的质心与当前迭代之间的差异。

当这个差异为0时,我们停滞演习。
现在让我们看看我们得到的簇:

color=['blue','green','cyan']for k in range(K): data=X[X[\"大众Cluster\公众]==k+1] plt.scatter(data[\"大众ApplicantIncome\公众],data[\公众LoanAmount\公众],c=color[k])plt.scatter(Centroids[\"大众ApplicantIncome\"大众],Centroids[\"大众LoanAmount\"大众],c='red')plt.xlabel('Income')plt.ylabel('Loan Amount (In Thousands)')plt.show()

在这里,我们可以清楚地看到三个簇。
红点代表每个簇的质心。
我希望你现在能清楚地理解K-means的事情事理。

但是,在某些情形下,此算法效果可能不是很好。
让我们来看看在利用K-means时你可能面临的一些寻衅。

4. K-means聚类算法面临的寻衅

利用K-means时我们面临的普遍寻衅之一是簇的大小不同。
假设我们有以下几点:

与中央簇比较,左侧和最右侧的簇的点的数量比较少。
现在,如果我们在这些点上运用K-means聚类,结果将是这样的:

K-means的另一个寻衅是当原始点的密度不同时。
比如下面这些原始点:

这里,赤色簇中的点比较疏松,而别的簇中的点紧密地堆积在一起。
现在,如果我们在这些点上运用K-means,我们将得到这样的簇:

我们可以看到紧凑的点已分配给同一个簇。
而实际疏松分布但在同一簇中的点却分配给不同的簇。
效果并不理想,我们能做些什么呢?

个中一个办理方案是利用更多的簇进行划分。
因此,在所有上述场景中,我们可以拥有更多的簇,而不是利用3个簇。
大概设置k = 10可能会产生更故意义的簇。

还记得我们如何在K-means聚类中随机初始化质心吗?嗯,这也可能有问题,由于我们每次都可能得到不同的簇。
因此,为理解决随机初始化的这个问题,有一种称为K-means ++的算法可用于为K-means选择初始值或初始簇质心。

5. K-means ++为K-means聚类选择初始簇质心

在某些情形下,如果簇的初始化不得当,K-means可能导致产生坏的簇。
这便是K-means ++产生浸染的地方。
它指定了在利用标准K-means聚类算法向前推进之前初始化簇质心的过程。

利用K-means ++算法,我们优化了随机选择簇质心的步骤。
在利用K-means ++初始化时,我们更有可能找到与最佳K-means办理方案竞争的办理方案。

利用K-means ++初始化质心的步骤是:

从我们想要聚类的数据点随机均匀地选择第一个簇。
这类似于我们在K-means中所做的,但不是随机挑选所有质心,而是在这里选择一个质心接下来,我们打算每个数据点到已经选择的簇的质心的间隔(D(x))然后,从数据点中选择新的簇质心,最大点到起其最近的质心的间隔的平方对应的点为新的簇的质心然后,我们重复步骤2和3,直到选择了k个簇

让我们举个例子来更清楚地理解这一点。
假设我们有以下几点,我们想在这里划分3个簇:

现在,第一步是随机选择一个数据点作为簇质心:

假设我们选择绿点作为初始质心。
现在,我们将利用此质心打算每个数据点与质心的间隔(D(x)):

下一个质心将是其平方间隔(D(x)2)离当前质心最远的那个点:

在这种情形下,红点将当选为下一个质心。
现在,为了选择末了一个质心,我们将打算所有点到其最近的质心的间隔,个中具有最大平方间隔的点作为下一个质心:

我们将选择末了一个质心为:

初始化质心后,我们可以连续利用K-means算法。
利用K-means ++初始化质心每每会改进聚类结果。
虽然相对付随机初始化而言打算本钱很高,但随后的K-means常日会更快地收敛。

我确信自本文开头以来你一贯在想一个问题 :我们该当划分多少个簇?实行K-means时最佳簇的个数该当是多少?

6. 如何在K-means聚类中选择精确的簇的个数?

每个人在利用K-means时最常见的疑问之一便是如何选择得当数量的簇。

那么,让我们看看一种技能,它将帮助我们为K-means算法选择精确的簇的个数。
我们来看看之前看到的客户细分示例。
总而言之,该银行希望根据其收入和债务金额对客户进行细分:

在这里,我们可以有两个簇,将客户分开,如下所示:

所有低收入的客户都在一个簇中,而高收入的客户则在第二个簇中。
我们还可以有4个簇:

在这里,一个簇可能代表低收入和低债务的客户,其他簇是客户具有高收入和高负债等等。
也可以有8个簇:

诚笃说,我们可以拥有任意数量的簇。
你能猜出可能的最大簇数是多少吗?我们想到将每个点分配给一个单独的簇。
因此,在这种情形下,簇的数量将即是点数或不雅观察数量。
以是,

最大可能的簇数将即是数据集中的不雅观察数。

但我们如何才能确定最佳簇数?我们可以做的一件事是绘制图形,个中x轴表示簇的数量,y轴将是评估度量。
我们暂时说是Inertia 。

你也可以选择任何其他评估指标,如Dunn Index:

接下来,我们将从一个小的簇值开始,比如说2利用2个簇演习模型,打算该模型的Inertia,末了将其绘制在上图中。
假设我们得到Inertia大约为1000:

现在,我们将增加簇的数量,再次演习模型,并绘制Inertia。
这是我们得到的图:

当我们将簇值从2变动为4时,Inertia急剧低落。
随着我们进一步增加簇的数量,Inertia的低落变得缓慢并终极变得稳定。

Inertia的减小幅度变为常数时对应的簇的个数可以选择作为我们数据的精确簇的个数。

在这里,我们可以选择6到10之间的任意数量的簇的个数。
我们可以有7个,8个乃至9个簇。
你还必须在确定簇的个数时查看打算本钱。
如果我们增加簇的数量,打算本钱也会增加。
因此,如果你没有高打算资源,我的建议是选择较少数量的簇。

现在让我们在Python中实现K-means聚类算法。
我们还将看到如何利用K-means ++来初始化质心,并且还将绘制曲线以确定我们的数据集的精确簇的个数。

7. 在Python中实现K-means聚类

我们将致力于批发客户细分问题。
你可以利用此链接[4]***数据集。
数据托管在UCI机器学习存储库中。

这个问题的目的是根据各种产品种别(如牛奶,杂货店,地区等)的年度支出对批发经销商的客户进行细分。
让我们开始动手吧!

我们将首先导入所需的库:

import pandas as pdimport numpy as npimport matplotlib.pyplot as plt%matplotlib inlinefrom sklearn.cluster import KMeans

接下来,让我们读取数据并查看前五行:

data=pd.read_csv(\"大众Wholesale customers data.csv\公众)data.head()

我们有客户对付不同产品的消费细节,如牛奶,杂货,冷冻,洗涤剂等。
现在,我们必须根据供应的细节细分客户。
在此之前,让我们提取一些与数据干系的统计数据:

data.describe()

在这里,我们看到数据的量纲存在很大差异。
像Channel和Region这样的变量量纲很小,而Fresh,Milk,Grocery等变量量纲较大。

由于K-means是基于间隔的算法,因此这种幅度差异可能会产生问题。
因此,让我们首先将所有变量置于同一量纲下:

# 标准化数据from sklearn.preprocessing import StandardScalerscaler = StandardScaler()data_scaled = scaler.fit_transform(data)# 统计标准化后的数据pd.DataFrame(data_scaled).describe()

现在看起来差不多了,接下来,让我们创建一个kmeans函数并将其拟合到数据上:

# 定义kmeans函数,并利用K-means++初始化kmeans = KMeans(n_clusters=2, init='K-means++')# 拟合标准化后的数据kmeans.fit(data_scaled)

我们初始化了两个簇并把稳到初始化在这里不是随机的。
我们已经利用了K-means ++初始化,它常日会产生更好的结果,正如我们在前一节中所谈论的那样。

让我们评估形成的簇的好坏程度。
为此,我们将打算簇的Inertia :

# 算法结束后的Inertia值kmeans.Inertia_Output: 2599.38555935614

我们的Inertia值靠近2600.现在,让我们看看我们如何通过在Python中绘制曲线来确定的最佳簇数。
我们将首先拟合多个K-means模型,我们将增加簇的数量。
我们存储每个模型的Inertia值,然后绘制它以显示结果:

# 拟合多个K-means模型并将各个模型的Inertia值存储到空的列表中SSE = []for cluster in range(1,20): kmeans = KMeans(n_jobs = -1, n_clusters = cluster, init='K-means++') kmeans.fit(data_scaled) SSE.append(kmeans.Inertia_)# 绘制图形frame = pd.DataFrame({'Cluster':range(1,20), 'SSE':SSE})plt.figure(figsize=(12,6))plt.plot(frame['Cluster'], frame['SSE'], er='o')plt.xlabel('Number of clusters')plt.ylabel('Inertia')

你能从这个图等分辨出最佳的簇的个数吗?不雅观察上面的曲线,我们可以选择5到8之间的任意值作为簇的个数。
让我们将聚类数设置为5并拟合模型:

kmeans = KMeans(n_jobs = -1, n_clusters = 5, init='K-means++')kmeans.fit(data_scaled)pred = kmeans.predict(data_scaled)

末了,让我们看一下上面形成的每个簇中的点的个数:

frame = pd.DataFrame(data_scaled)frame['cluster'] = predframe['cluster'].value_counts()

因此,有234个数据点属于簇4(索引3),然后是簇2(索引1)中的125个点,依此类推。
这便是我们如何在Python中实现K-means聚类。

8. 结语

在本文中,我们谈论了一种最着名的聚类算法--K-means。
我们从零开始逐步实现它。
我们研究了在利用K-means时可能碰着的寻衅,并理解了在初始化簇质心时K-means ++起到的浸染。

末了,我们实现了K-means并通过绘制和不雅观察曲线找到在K-means算法中的最佳簇数。

[1]: https://www.analyticsvidhya.com/blog/2019/04/introduction-image-segmentation-techniques-python/?utm_source=blog&utm_medium=comprehensive-guide-K-means-clustering

[2]: https://drive.google.com/file/d/1ZzEouo7lRJvajxK6jLM2K_p9xAwGw1tS/view?usp=sharing

[3]: https://datahack.analyticsvidhya.com/contest/practice-problem-big-mart-sales-iii/?utm_source=blog&utm_medium=comprehensive-guide-K-means-clustering

[4]: https://archive.ics.uci.edu/ml/machine-learning-databases/00292/Wholesale%20customers%20data.csv