机器学习算法中真正的王者!

174 阅读7分钟

聚类分析已经成为数据分析、机器学习和数据科学中最重要的方法之一。

聚类的总体思路是将一组具有各种特征的对象划分为若干组,称为聚类。

现在用于聚类的算法有很多,但我给大家介绍的是最强大也是最简单的一种算法,名为K-means聚类。

本文,将从如下几个方面进行讲解:

  • 数据集
  • 算法介绍
  • 使用方法
  • 聚类验证:轮廓分析

下面,就开始介绍本文的详细内容吧!

数据集

本文将在San Francisco Open Data提供的数据集上应用应用并验证聚类算法的全部功能,这是旧金山国际机场航空公司的月度统计数据。

在我们的工作中,我们将使用如下Python工具包:

  • matplotlib(seaborn)
  • pandas
  • scikit-learn
  • numpy

下面,就开始详细介绍吧!首先,就像每一个数据科学任务一样,我们必须熟悉我们正在处理的数据。

df = pd.read_csv("Datasets/Air_Traffic_Passenger_Statistics.csv")
df.head()

img

如上图所示,在我们的数据集中有多个列,但对于聚类分析,我们将使用运营航空公司,地理区域,乘客数量和每个航空公司持有的航班。

所以,首先,为了确定潜在的异常值并对我们的数据有一些了解,让我们使用Python数据可视化库Seaborn绘制一些图。

img

从航空公司的频率图中,我们可以看到,有一些样本的航班量比其他航空公司大。这些是美联航和联合航空在2013年7月1日前的数据。

所以,这些航空公司是我们潜在的异常值。

为什么我们要试图寻找和消除异常值的原因将在后面解释。

我们来看看持有航班的国家/地区的频率图。

plt.figure(figsize = (15,15))
sns.countplot(x = "GEO Region", palette = "Set3",data = df)
plt.xticks(rotation = 90)
plt.ylabel("Number of fights held")
plt.show()

img

正如我们所看到的,美国是国家中的排名第一的,这是因为我们的异常航空公司(联合航空公司和联合航空-2013年7月1日之前)是美国航空公司。我们显然可以看到,“国家”或“航空公司”可以按其持有的航班数量分成几个群组。

算法介绍

K-means聚类是当今最流行的聚类算法之一。它是由Hugo Steinhaus于1950年代提出的。

该算法的主要思想是将n维空间中的一组点X划分为质心C,从而使目标函数(这些点和相应质心的MSE)最小。

img

让我们看看算法具体的实现步骤:

首先,质心被随机初始化。第二步是重复两个步骤,直到收敛为止(质心与上一次迭代相同):

  • 为X中的每个点找到最接近的质心并将此点添加到群组中
  • 计算每个聚类的新质心,取每个维度的平均值

因此,由于此算法需要处理距离,因此对异常值非常敏感。

因此,在进行聚类分析之前,我们必须识别离群值并将其从数据集中删除。为了更准确地找到离群值,我们将构建散点图。

airline_count = df["Operating Airline"].value_counts()
airline_count.sort_index(inplace=True)
passenger_count = df.groupby("Operating Airline").sum()["Passenger Count"]
passenger_count.sort_index(inplace=True)
from sklearn.preprocessing import scale
x = airline_count.values
y = passenger_count.values
plt.figure(figsize = (10,10))
plt.scatter(x, y)
plt.xlabel("Flights held")
plt.ylabel("Passengers")
for i, txt in enumerate(airline_count.index.values):
    a = plt.gca()
    plt.annotate(txt, (x[i], y[i]))
plt.show()

img

我们可以看到,大多数航空公司被分组在图表的左下方,其中一些在它们的上方,还有我们的两个离群值联合航空公司和联合航空公司。因此,让我们抛弃它们。

df_1 = airline_count + passenger_count
df_1.sort_values(ascending = False, inplace = True)
outliers = df_1.head(2).index.values
airline_count = airline_count.drop(outliers)
airline_count.sort_index(inplace=True)
passenger_count = passenger_count.drop(outliers)
passenger_count.sort_index(inplace = True)
x = airline_count.values
y = passenger_count.values

我们已经准备好数据集进行聚类。但是,问题是如何确定要使用的“最佳”群组数量,下面将介绍一下。

使用方法

正如前面讨论过的,我们要做的就是尝试最小化目标函数。

很明显,我们拥有的质心越多,目标函数的值就越小。

此外,当所有点本身都是质心时,目标函数将变为0 —全局最小值。但是,这样并不满足我们的需求。

让我们根据簇数绘制目标函数值的关系图:

img

我们的任务是找到簇的数量,之后目标函数的下降速度不会如此之快或与人的手臂类似:如果我们将图想象为人的手臂,那么最优的簇数量将成为肘部。

在最佳情况下,聚类的最佳数量为6。

因此,现在我们可以对数据集进行聚类分析了。

kmeans = KMeans(n_clusters=6)
kmeans.fit(X)
y_kmeans = kmeans.predict(X)
plt.figure(figsize = (15,15))
plt.xlabel("Flights held")
plt.ylabel("Passengers")
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=300, cmap='Set1')
for i, txt in enumerate(airline_count.index.values):
    plt.annotate(txt, (X[i,0], X[i,1]), size = 7)
plt.show()

img

现在,我们已经完成了数据的聚类,并基于结果做出了一些假设和预测。

我们可以看到,美国航空公司和SkyWest航空公司拥有许多航班并运送许多人,因此我们可以推断出,这两家航空公司的飞机容量更大,或者人们使用这些航空公司的人数比其他航空公司更多。

聚类分析使你可以获得有关数据的许多见解。现在,对于我们来说,验证聚类的结果非常重要(以查看分区的好坏)。

效果验证

为了理解此类验证的主要特征,我们必须了解什么是轮廓系数。

img

对于数据集中的每个点,我们都可以使用上面的公式来计算轮廓系数,但是关于a(i)和b(i)是什么还不清楚。因此,让我们了解a(i)参数的含义。

img

对于每个点,我们可以计算a(i)--当前点i与i的群集相邻点之间的平均距离, 即类内距离,用j表示(i或j应该对应于同一群集)。

现在,让我们了解b(i)参数的含义。

img

对于每个点,我们可以计算b(i)--当前点i和与i不属于同一类的点j之间的距离的最小值,即类间距离

使用上述参数,我们可以计算数据集中每个点的轮廓系数。轮廓分数取值范围为[-1, 1]。

绘制轮廓图是很有用的,能够让我们弄清楚它是什么样的。

在轮廓图中,我们为分组成簇并在其中排序的每个点绘制轮廓系数。

查看该图,我们可以判断出分类的好坏:如果所有“条”的宽度相同,并且群组中的每个点的轮廓系数都大于全局平均值,那么这种分类可以称为好,否则聚类效果不是特别完美,但是也可以接受。

现在,让我们为聚类绘制轮廓图(代码基于scikit-learn文档中有关“轮廓分析”的示例)。

from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.cm as cm
n_clusters = 6
plt.figure(figsize = (10,10))
plt.gca().set_xlim([-0.1,1])
plt.gca().set_ylim([0, len(X) + (n_clusters + 1) * 10])
clusterer = KMeans(n_clusters=n_clusters, random_state=10)
labels = clusterer.fit_predict(X)
print("The average silhouette_score is :{}".format(silhouette_score(X, labels)))
sample_silhouette_values = silhouette_samples(X, labels)
y_lower = 10
for i in range(n_clusters):
    ith_cluster_silhouette_values = \
    sample_silhouette_values[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)
    plt.gca().fill_betweenx(np.arange(y_lower, y_upper),
                          0, ith_cluster_silhouette_values,
                          facecolor=color, edgecolor=color, alpha=0.7)
    plt.gca().text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
    y_lower = y_upper + 10
plt.gca().axvline(x=silhouette_score(X, labels), color="red", linestyle="--", label = "Avg silhouette score")
plt.title("The silhouette plot")
plt.xlabel("The silhouette coefficient values")
plt.ylabel("Cluster label")
plt.legend()
plt.show()

img

如我们所见,聚类的大小不一样,并且并非所有聚类都由轮廓系数大于全局平均值的点组成,但查看我们的数据,结合前面散点图来看,虽然达不到完美的程度,但是整体聚类效果良好。


干货推荐

为了方便大家,我花费了半个月的时间把这几年来收集的各种技术干货整理到一起,其中内容包括但不限于Python、机器学习、深度学习、计算机视觉、推荐系统、Linux、工程化、Java,内容多达5T+,我把各个资源下载链接整理到一个文档内,目录如下:

v2-b6cf2d07be31820426981c656677c6d2_b.jpg

所有干货送给大家,希望能够点赞支持一下! https://http://pan.baidu.com/s/1eks7CUyjbWQ3A7O9cmYljA (提取码:0000)