有序数据的 k-均值聚类
使用映射来揭示数据中类似数字的行为
图片由来自 Pixabay 的 Reimund Bertrams 拍摄
K——意思是聚类。这是无监督学习的圣杯。老实说。我明白为什么…当然,决定你应该计算的集群的数量*有一点艺术形式,但是总的来说,坐下来让算法做它的事情是不可思议的。尽管如此,有一个非常重要的警告: k-means 聚类只对数值数据有效 … 对吗?!*
嗯…也许吧
一般来说,试图扩大 k-means 到分类应用是不稳定的。k-means 聚类最重要的部分是寻找它们之间具有最小距离的点。我们如何定义分类变量之间的距离?苹果离橘子有多远?那些更接近蓝莓还是西瓜?在无序的上下文中,问这些问题是没有意义的。
但是如果我的分类数据是有序的呢?
哇,我很高兴你问了。正如你敏锐地指出的,在某些情况下,似乎应该有一种方法来回答这些问题。
最近,我在看一些来自 KFF 的数据,这些数据是关于美国为应对新冠肺炎病毒而实施的全州范围的社会距离指令。经过一番争论,下面是最初几行的样子:
(注:如果想看我扯皮分析这个数据集的全过程,可以在 Github 上查看我的 R 代码。)
例如,考虑留在家中订单属性。此属性描述了一个州是否有留在家中的命令,以及该命令会影响哪些人群。显然,没有秩序不同于全州秩序,全州秩序不同于仅适用于高风险人群的秩序。这里是重要的部分:一个全州范围的命令似乎“更接近”一个只影响高风险人群的命令,而不是根本没有命令。
似乎我们应该能够回答 k-means 聚类所需的问题。我们数据的自然排序有助于进一步分析。当然,它实际上不是数字数据,但它以一种大致数字的方式表现,那么是什么阻碍了我们将它视为数字数据呢?用 Hannah Montana 无处不在的话说,生活是由你创造的,所以让我们对有序数据执行 k-means 聚类成为可能!(没有?那不是她的原话吗?开枪吧。)
要点如下:为了使 k-means 在有序数据集上成为可能,我们将定义一个从有序数据到数值的映射。本质上,我们为每个因素级别分配权重,并使用这些权重来执行我们的分析。
(提醒:如果你的分类数据没有自然排序,这是一个坏主意。我们基本上是在创造数量级和数量级的概念,并把它们强加给我们的数据集。如果这对您的数据没有意义,您可能会对 k-modes 集群感兴趣。)
对于我的数据,我有 3 个想要转换的属性。为了确保属性在聚类过程中同等重要,我确保每个属性的可能权重范围从 0 到 10。0 和 10 的精确选择并不像确保所有属性具有相似数量级的想法那么重要。在定义映射时,这可能是需要记住的最重要的事情。
为所有属性创建相似量级的标度是转换 k-means 分析的有序数据时要考虑的最重要的方面。
最后,我将我的映射定义如下:
将有序社会距离变量映射为数值
一旦定义了映射,我就对现在的数字变量进行了完整的 k-means 聚类分析。下面是我转换后的数据的一瞥:
转换后的序数数据,以及由 k-means 识别的聚类
这看起来效果很好:我的聚类均值彼此截然不同,三个变量的每个组合的散点图恰当地说明了聚类之间的界限。(查看 Github 上的代码了解全部细节。)说了这么多,做了这么多,我最终得到了以下的集群标识。
通过有序数据的 k-均值聚类确定的全州社会距离任务的分类
这是一种艺术形式,不是科学。
定义从序号到数值的映射是一个非常主观的过程。这在很大程度上取决于数据的细节,我认为除了关于如何选择映射的一般建议之外,不可能给出任何其他的建议。
在某些数据集中,有序数据的简单排序可能是最合适的。在其他情况下,比如我们的案例研究,可能需要一个更灵活的尺度。这种方法没有放之四海而皆准的方法,但我仍然相信它有潜力产生非常有价值的见解。
K-Means 聚类——一个将它们全部分组的规则
本文将尝试:
- 用图形解释 K-Means 聚类的基础,这是一种无监督的机器学习算法
- 讨论寻找 K 的最佳值和团簇质心位置的方法
你应该已经知道的:
基础数学和欧几里德几何
在机器学习中,经常遇到的问题之一是将相似的数据分组在一起。你知道人们的收入水平,现在你想把收入水平相似的人分组在一起。你想知道谁是低收入人群,谁是高收入或非常高收入人群,你认为这有助于设计一个完美的营销策略。您有客户的购物数据,现在您想要将具有相似购物偏好的客户分组在一起,或者您是生物专业的学生,想要根据您手头的细胞数据了解哪些细胞具有相似的属性。
所有上述问题都属于称为聚类的无监督机器学习方法的范畴。虽然有许多聚类算法,但当谈到最简单的一个,奖项将授予 K-Means 聚类。为了理解该算法,让我们假设我们有以下数据:
我们有十个数据点,给出了它们的 x 和 y 坐标。这里我们有表示为 x 和 y 的变量,但在实际情况中,它们可以是不同的,比如在市场细分情况下的月收入或日支出。让我们从两个集群开始,首先将上述数据可视化,以便对其有所了解:
上图显示了我们手头的数据(图 1)和我们可以从中得出的两个可能的聚类(图 2)。请注意,在任何数据中查找集群数量(本例中为 2)的决定完全是任意的&基于随机猜测。稍后,您将发现这种随机猜测最初是如何引导我们找到数据中可能的最佳聚类数的。现在的问题是如何计算和分析找出这两个集群。
方法背后的逻辑
- 首先,假设数据附近的任意两个随机点&将它们视为两个集群的中心(质心)
- 找出每个数据点到两个中心的距离
- 将每个数据点分配到离其最近的质心,从而形成两个聚类
- 计算形成的两个簇的中心,并在那里移动质心。
- 转到步骤 1,重复该过程,直到形成的簇没有变化。
让我们将上述逻辑应用于给定的数据。我们知道在笛卡尔坐标系中,两点(x1,y1)和(x2,y2)之间的距离由下式给出:
使用上面的公式,我们可以计算每个点与假设中心之间的距离。让我们假设我们的质心在 C1 = (4,1)和 C2 = (6,1),从图形上来说,如下图所示:
如果我们计算每个点到两个质心的距离,结果如下表所示:
基于上述距离值,每个点将被分配到其距离最小或最近的质心,例如考虑第一个数据点,其距离 C1 为 3.6,距离 C2 为 5.4。由于它更靠近 C1,它将被分配到这个特定的质心。对每个点做同样的操作,分配如下所示:
从上表可以看出,每个点都根据其与假定质心的距离被分配给一个质心。分配后,通过计算分配给每个簇的点的中心来计算质心的新位置,如表中所示,计算每个坐标的平均距离。因此,质心的新位置将是 C1 = (2.6,3.8)和 C2 = (7.6,3.4),如下图所示:
上图显示了单个计算循环是如何使两个质心更接近数据的。如果您运行另一个计算循环,您可以看到质心不再移动,并且没有数据点将其质心从 C1 改变到 C2,反之亦然。这是计算循环或递归停止的地方。
将数据分配给质心以使其成为聚类的上述过程被称为 K 均值聚类,并且 K 的值是所形成的聚类的数量,例如在当前情况下 K 的值将是 2。
最后两个集群如下所示:
如何决定 K 的值?
我们从 K=2 的随机坐标开始,也就是说,我们给质心的数量和位置都分配了随机值。虽然我们最终找到了计算质心最终位置的方法,但仍然存在的问题是以最佳方式拟合给定数据的 K 的值是多少。我们也试图找到这个问题的答案,但首先让我们了解什么是成本函数。
成本函数
它是一个函数,给出了相对于每一个 K 值的模型缺陷的度量。理想情况下,聚类中的每个点应该尽可能靠近其质心,或者特定聚类中的每个点与其质心之间的距离应该为零。我们将计算这个距离&,将距离的平方和视为缺陷的成本或度量。我们将对每个集群重复同样的操作&将找到使该成本最小化的 K 的值。
参考表-1 和表-2,我们将每个点分类到一个簇中,并将这些点与它们各自质心的距离总结如下:
每个距离的平方相加,两个聚类的总和为 15.72 + 10.76 = 26.84。这个数字是我们设 K = 2 时模型的成本。同样,可以针对不同的 K 值计算成本,当成本值相对于 K 值绘制时,我们将得到如下所示的图表:
根据上图, K 的最佳值将是显示最大偏差(用红色标记)或成本曲线形成弯头的值。虽然较高的 K 值进一步降低了成本,但会导致过拟合。这种找出最佳值 K 的方法称为肘法。
到目前为止,我们所做的任何事情的 Python 代码如下所示:
这都是关于 K-均值聚类&如何找出 K 的最佳值和质心的位置。希望你喜欢。请发表您的评论
延伸阅读:
- 主成分分析
- DBSCAN
谢谢,
过得愉快😊
如果你对这篇文章有任何疑问,你可以通过 LinkedIn 联系我
原载于 2020 年 4 月 10 日 https://wildregressor.com。
使用 Spotify 歌曲特征的 K-means 聚类
使用高维数据聚类自动创建播放列表
spotify API 允许我们创建一个简单的服务器端应用程序,从 spotify 应用程序中访问用户相关数据。它还可以让你获得应用程序上没有的信息,如艺术家受欢迎程度、歌曲指标、专辑封面图片等。它允许你创建、删除和修改用户帐户中的现有播放列表。
这个项目的目标是使用聚类算法将一个大的播放列表分解成更小的播放列表。为此,使用了歌曲度量标准,如“可跳性”、“效价”、“速度”、“活跃度”、“语速”。
在这篇文章中,你会发现:
- 使用 Spotify API 收集数据
- 寻找理想的集群数量
- 肘方法和轮廓分析
- 特征选择
- 聚类算法
- 播放列表创建和性能分析
如果您还不是会员,请在此获得您的中级会员资格!
数据收集
import spotipy
from spotipy.oauth2 import SpotifyOAuth
from spotipy.oauth2 import SpotifyClientCredentials
连接 spotify API 非常简单,内容也有很好的文档记录。这个链接将为您提供连接所需的所有信息,而这个将为您提供 python 示例代码。
前往https://developer.spotify.com/dashboard/,点击创建客户端 ID 或创建 App ,获取您的“客户端 ID ”和“客户端密码”。之后,重定向 URI 必须被更改到你在 Spotify 应用程序的设置中决定的任何页面。
scope = "user-library-read playlist-modify-private"
OAuth = SpotifyOAuth(
scope=scope,
redirect_uri='http://localhost:8888/callback',
client_id = 'client_id',
client_secret = 'client_secret',
username= 'username')
变量“sp”将作为 spotify 数据的访问键,并允许我们使用所有功能来获取和发布 Spotify 的请求。
sp = spotipy.Spotify(auth_manager=OAuth)
保存的曲目数据收集
下一步是从我的“ 喜欢的歌曲 ”播放列表中收集数据。
API 一次只能提取 50 首歌曲,因此我们运行一个 while 循环,以 50 首为一组遍历整个播放列表。用来访问我个人喜欢的歌曲的函数是current _ user _ saved _ tracks()。
offset = 0songs = []
items = []
ids = []
while True:
content = sp.current_user_saved_tracks(limit=**50,** offset=offset)
songs += content['items']
if content['next'] is not None:
offset += **100** else:
break
下面的步骤是使用 audio_features() 函数提取每首歌曲的音频特征:
for i in songs:
ids.append(i['track']['id'])while index < len(ids):
audio_features += sp.audio_features(ids[index:index + 50])
index += 50features_list = []
for features in audio_features:
features_list.append([features['energy']**,** features['liveness']**,** features['tempo']**,**features['speechiness']**,
...** features['mode']**,** features['type']**,** features['uri']])
df = pd.DataFrame(features_list**,** columns=['energy'**,** 'liveness'**,** 'tempo'**,** 'speechiness'**,** ...'mode'**,** 'type'**,**'uri'])
结果是一个 pandas 数据帧,它收集了行上的歌曲和列上它们各自的特征。每首歌曲都由它唯一的 uri 来标识。
数据框的右侧
数据框的左侧
只是为了好玩,我决定查看播放列表中所有歌曲的特征值分布情况:
歌曲特征(标准化后):
我很快得出结论,这些指标之间没有很强的相关性。歌曲并没有根据它们的类型遵循固定的模式。当尝试基于所有 12 个特征执行聚类时,结果并不理想…为此,我决定使用较少数量的指标,并测试所有可能产生最佳聚类的特征组合。
不同指标之间没有真正的相关性…
寻找理想的集群数量
在对所有 12 个特征尝试聚类算法之前,我决定精选几个特征进行聚类,希望它能产生更好的聚类质量。
第一步是归一化所有的值,使它们位于[0;1].
from sklearn import preprocessingx = df.values
min_max_scaler = preprocessing.MinMaxScaler()
x_scaled = min_max_scaler.fit_transform(x)
df = pd.DataFrame(x_scaled)
接下来的步骤是基于一些特征的选择找到一个聚类质量度量。我做的第一件事是创建一个包含所有歌曲指标的列表,然后使用 permutations() 函数创建一组当时使用三个歌曲特征的所有可能组合。
columns = ["energy"**,** "speechiness"**,** "acousticness"**,** "instrumentalness"**,** "loudness"**,**"tempo"**,**"danceability"**,**'valence' **,** "liveness", "time_signature", "key"]
perm = permutations(columns**, 3**)
output = set(map(lambda x: tuple(sorted(x))**,**perm))
对于每个排列,我使用肘方法计算了一个聚类分数。
肘法 是一种启发式方法,用于确定数据集中的聚类数。 方法 包括绘制所解释的变异作为聚类数的函数,并拾取曲线的 【肘形】 作为聚类数来使用。
x 轴为肘值,y 轴为分数。每条线对应不同的排列。
对于每个排列,我提取了一个肘分数和一个肘值。分数告诉我该排列的聚类有多好,肘值告诉我该排列的理想聚类数。
model = KMeans(random_state=**0**)
visualizer = KElbowVisualizer(model**,** k=(**2,12**)**,** metric='silhouette'**,** timings=False)
visualizer.fit(x_scaled)
score = visualizer.elbow_score_
value = visualizer.elbow_value_
结果是一个熊猫数据帧的特征排列和他们相应的理想数量的集群和质量评分这些集群。
一旦计算出每个排列的分数,我决定选择大于 0.4 的排列,并将它们保存在一个单独的数据框中。
if score>**0.4**:
idx = df.columns
mylist = idx.tolist()
dict = {
"features": mylist**,** "score": score**,** "elbow": value
}
df2 = df2.append(dict**,** ignore_index=True)
集群和播放列表创建
有许多排列产生了很好的结果,得分甚至超过 0.5。
排列**[‘工具性’,‘语音性’,‘化合价’]**建议创建 4 个集群,得分为 0.504。这是我在创建播放列表时选择的一个。
我缩放了一个只包含这三个指标的数据帧,然后使用 KMeans() 函数执行了聚类。
from sklearn.cluster import KMeanskmeans = KMeans(init="k-means++"**,** n_clusters=**4,** random_state=**15,** max_iter = **500**).fit(x_scaled)
df1['kmeans'] = kmeans.labels_
df1.columns = ['energy'**,** 'instrumentalness'**,** 'loudness'**,**'kmeans' ]
结果是数据帧包含每行一首歌曲,标签[0,1,2,3]对应于特定歌曲被分配到的每个簇。特征分布清楚地表明,能量和响度在所有集群中分布不同。
3D 散点图给出了不同的视角,显示了较高的乐器度值被分类到相同的群中,而能量和响度区分了其余三个群。
聚类的三维散点图
非常感谢你的阅读!
更多类似的文章,请点击在 Medium 上找到我!
如果您有任何关于如何改进的问题、建议或想法,请在下面留下评论或通过 LinkedIn 这里取得联系。
Github 知识库
所有的代码都可以在 Github 这里找到。排列脚本可以在 analysis_v2.py 中找到,聚类可以在 feature_analysis.py 中找到。
k 表示使用 Python 进行集群
在本文中,我们将了解 K 均值聚类的基础知识,并使用著名的机器学习库 Scikit-learn 在 Python 中实现它
尼克尼斯在 Unsplash 上的照片
什么是 K 均值聚类?
K 均值聚类是一种无监督机器学习算法。它接收混合数据并且基于数据中的模式将数据分成小的组/簇。
K 均值算法的目标
奥德里布 曾经说过:
K-means 的目标很简单:将相似的数据点组合在一起,发现潜在的模式。为了实现这个目标,K-means 在数据集中寻找固定数量的聚类( k )。
K 均值聚类的工作原理
为了解释 K 均值算法的工作原理,让我们假设我们有一些使用散点图绘制出来的数据。
数据绘制在散点图上,照片由作者提供
现在在稍微分析数据之后,我们可以看到我们的数据可以分成两个独立的组/簇。现在, K 表示将要做的是,它还会将数据划分为两个簇**,并为每个簇标记一些边界**。因此,每当一些新的数据被馈送到模型时,它将检查这个数据点落入了什么边界**,并在最后告诉我们集群的名称或**编号。****
k 均值聚类算法讲解
首先我们会选择簇数(k) (本例中 k=2 )。这意味着现在我们将随机假设 2 个点,它们将作为我们的簇质心。(簇形心是簇的中心点)。
K 均值聚类有两个主要步骤:
- 聚类分配步骤:在该步骤中,靠近质心的数据点将分别落在那些质心聚类中。
- 移动质心步骤:在这一步中,我们将计算一个聚类中所有数据点的平均值,并将该聚类的质心移动到该平均值位置。
一旦后的条件之一为真:,我们将重复上述两步****
- 我们的质心停止改变位置。
- 达到最大迭代次数。
我们的数据现在被安排到簇中。
选择 K 的值
为了选择最适合我们数据的簇数,我们可以使用著名的肘法。这种肘方法背后的基本思想是,它用变化的集群数量(k) 来绘制成本(误差)的各种值。随着组数(k) 增加,每个组的数据点数 减少。因此平均失真减少。小于一个簇中的个数的数据点,这些数据点离它们的质心越近**。所以 k 处畸变下降最大的值称为**拐点。****
选择合适的 k 值,作者照片
有时我们图的斜率是相当平滑所以很难选择 k 的值,因为没有明确的拐点。在这种情况下,我们利用我们的行业经验和连续实验来确定 k 的值。
没有清晰的肘点,作者照片
用 Python 实现
现在我们已经很好地理解了 K 意味着聚类算法是如何工作的。所以现在我们将在一个数据集上实现 K Means 以获得关于它的更清晰的直觉**。为此,我们将使用 Python 的著名的机器学习库、 Scikit-learn。**
什么是 Scikit-learn?
Scikit-learn (又名 sklearn )是一个针对 Python 的机器学习库。它包括各种分类、回归、和聚类算法以及支持向量机(SVM) 、随机森林、梯度提升、、 k -means 和 DBSCAN ,并且被设计为与像 NumPy 、这样的 Python 库一起工作
k 表示使用 Scikit-learn 进行聚类
K 表示聚类是一种非常直接且易于使用的算法。特别是在这个 Scikit learn 库的帮助下,它的实现和使用变得相当容易。现在,我们开始使用 Sklearn 。
在 Python 中导入重要库
import seaborn as sns
import matplotlib.pyplot as plt
创建人工数据
from sklearn.datasets import make_blobs
data = make_blobs(n_samples=200, n_features=2,centers=4, cluster_std=1.8,random_state=101)
可视化我们的数据
plt.scatter(data[0][:,0],data[0][:,1],c=data[1],cmap='rainbow')
可视化我们的数据,作者照片
创建集群
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=4)
kmeans.fit(data[0])
现在,在从sklearn.cluster
导入了KMeans
之后,我们创建了一个KMeans
类的对象kmeans
。在这里,你可能会发现奇怪的一点是我们指定了n_clusters=4
。这是因为在创建数据时,我们指定了centers=4
,所以我们知道该数据应该有 4 个簇。所以我们手动指定了它。但是如果我们不知道这些中心,那么我们将不得不使用肘方法来确定正确的星团数量。
好了,现在继续向前,我们的代码的这kmeans.fit(data[0])
段分析数据,使集群、甚至将每个集群的质心匹配到它们的适当位置。
现在为了检查我们的质心的位置,我们可以使用下面的代码。
print(kmeans.cluster_centers_)
它会打印出一个 (4,2) 数组,分别显示每个簇的质心的位置。
每个聚类的质心值,照片由作者提供
应用 K 后对比原始数据集 VS 表示
f, (ax1, ax2) = plt.subplots(1, 2, sharey=True,figsize=(10,6))
ax1.set_title('K Means')
ax1.scatter(data[0][:,0],data[0][:,1],c=kmeans.labels_,cmap='rainbow')
ax2.set_title("Original")
ax2.scatter(data[0][:,0],data[0][:,1])
对比 原始数据集 VS 应用 K 表示 ,作者照片
恭喜恭喜!我们已经在我们的数据集上成功实现了 K 均值聚类。
学习成果
到目前为止,我们已经了解了什么是 K 均值聚类算法,它的工作,以及如何选择 K 的值。另外,我们已经使用 Python 的著名的机器学习库,即 Scikit-learn,在数据集上实现了 K Means 。
k 表示用 python 代码解释的集群
一种解决聚类问题的简化无监督学习算法
k 均值聚类是机器学习中的另一种简化算法。它被归类为无监督学习,因为这里我们还不知道结果(不知道将形成哪个聚类)。该算法用于数据的矢量量化,取自信号处理方法。这里数据被分成几组,每组中的数据点都有相似的特征。这些聚类通过计算数据点之间的距离来决定。这个距离是众多无人认领的数据点之间关系的度量。
k 表示不应与 KNN 算法 混淆,因为二者使用相同的距离测量技术。这两种流行的机器学习算法有一个基本的区别。K means 处理数据,并将其分成不同的聚类/组,而 KNN 处理新的数据点,并通过计算最近邻方法将它们放入组中。数据点将移动到具有最大数量邻居的簇。
随机点数据集
K 表示聚类算法步骤
- 在数据中选择随机数量的质心。即 k=3。
- 在 2D 画布上选择与质心数量相同的随机点。
- 计算每个数据点到质心的距离。
- 将数据点分配到距质心距离最小的聚类中。
- 重新计算新的质心。
- 重新计算每个数据点到新质心的距离。
- 重复从第 3 点开始的步骤,直到没有数据点改变其聚类。
k 表示将数据分成不同的簇,簇的数量等于 k 的值,即如果 k=3,则数据将被分成 3 个簇。k 的每个值都是质心,数据点将围绕质心聚集。
取自 rosalind.info 的欧氏距离图像
距离计算可以通过四种方法中的任何一种来完成,即欧几里得、曼哈顿、相关和艾森。这里,我们使用欧几里德方法进行距离测量,即两点(x1,y1)和(x2,y2)之间的距离为
以下是在机器学习中使用 k 均值聚类算法的一些优点和缺点
优点:
- 一种相对简单的算法
- 灵活,能够很好地处理大量数据
- 保证了收敛
缺点:
- 我们必须手动定义质心的数量
- 无法避免离群值
- 取决于所选质心的初始值
现在,我们将尝试用 python 语言创建一个算法。在这里,我们将调用一些基本的和重要的库来工作。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
sklearn 是机器学习中最重要的包之一,它提供了最大数量的函数和算法。要使用 k 意味着聚类我们需要从 sklearn 包中调用它。
为了获得一个样本数据集,我们可以使用 numpy 生成一个随机序列
x1=10*np.random.rand(100,2)
通过上面的代码行,我们得到了一个包含 100 个点的随机代码,它们组成了一个形状为(100,2)的数组,我们可以使用这个命令来检查它
x1.shape
现在,我们将通过处理所有数据来训练我们的算法。这里,簇的数量将是 3。这个数字是我们任意给定的。我们可以选择任何数字来定义集群的数量
kmean=KMeans(n_clusters=3)
kmean.fit(x1)
使用下面的命令,我们可以看到我们的三个中心
kmean.cluster_centers_
要检查创建的标签,我们可以使用下面的命令。它给出了为我们的数据创建的标签
kmean.labels_
通过 k 均值聚类输出 3 个聚类
我们可以看到,由于手动选择质心的数量,我们的集群没有很大的分离。一个集群有一组相似的信息,我们的目标是使集群尽可能的独特。它有助于从给定的数据集中提取更多的信息。因此,我们可以绘制一条肘形曲线,它可以清楚地描述质心数量和信息增益之间的平衡。
wcss = []
for i in range(1,20):
kmeans = KMeans(n_clusters=i,init=’k-means++’,max_iter=300,n_init=10,random_state=0)
kmeans.fit(x1)
wcss.append(kmeans.inertia_)
print(“Cluster”, i, “Inertia”, kmeans.inertia_)
plt.plot(range(1,20),wcss)
plt.title(‘The Elbow Curve’)
plt.xlabel(‘Number of clusters’)
plt.ylabel(‘WCSS’) ##WCSS stands for total within-cluster sum of square
plt.show()
你可以看到有 K-means++作为方法比传统的 K-means。前一种方法克服了人工选择时容易出现的质心选择错误的缺点。有时选择的质心离点太远,以至于在它们的簇中没有任何数据点。
输出图可以帮助我们确定为更好的聚类选择的质心的数量。
确定质心实际数量的曲线
该曲线清楚地表明,如果我们选择质心的数量为 7、8、9 或 10,那么我们有更好的机会进行精细聚类。数据非常分散,我们甚至可以选择 14 作为质心的数量。这条曲线有助于在计算开销和从数据集获取知识之间做出决定。现在,让我们选择质心为 10,这样我们有 10 个独立的集群。
10 个由 K 均值聚类创建的聚类
结论:
我们已经成功地从随机数据集中创建了三个集群。所有数据分类以不同的颜色显示。我们可以使用相同的代码对其他数据进行聚类,甚至可以改变算法中聚类的数量。然后通过使用肘方法,我们预测更多的质心可以改善聚类。因此,在选择更多的聚类之后,我们得到了具有改进的信息增益的更好的聚类。
K-means、DBSCAN、GMM、凝聚聚类——掌握细分问题中的流行模型
为无监督聚类实现 python 中最广泛使用的模型的一站式商店
梅尔·普尔在 Unsplash 上的照片
在当今时代,大量客户/产品的粒度数据的可用性以及高效处理数 Pb 数据的技术能力正在快速增长。正因为如此,现在有可能提出非常有战略意义和有意义的集群来进行有效的定位。并且识别目标分段需要稳健的分段练习。在这篇博客中,我们将讨论最流行的无监督聚类算法,以及如何用 python 实现它们。
在这篇博客中,我们将使用一家为孕妇提供服装的在线商店的点击流数据。它包括产品类别、照片在网页上的位置、IP 地址的来源国以及产品的美元价格等变量。它有 2008 年 4 月到 2008 年 8 月的数据。
第一步是为分割准备数据。我建议您在继续下一步之前,查看下面的文章,以深入了解为分段准备数据的不同步骤:
One Hot 编码、标准化、PCA:python 中分割的数据准备
选择最佳聚类数是处理分割问题时需要注意的另一个关键概念。如果您阅读下面的文章,将有助于您理解选择集群的流行指标的完整列表:
我们将在本博客中讨论 4 类模型:
- k 均值
- 凝聚聚类
- 基于密度的空间聚类
- 高斯混合模型(GMM)
K-表示
K 均值算法是一个迭代过程,有三个关键阶段:
- 选择初始聚类质心
该算法从选取初始的 k 个聚类中心开始,这些聚类中心被称为质心。确定最佳聚类数(即 k)以及正确选择初始聚类对于模型的性能极其重要。聚类的数量应该总是取决于数据集的性质,而初始聚类的不良选择会导致局部收敛的问题。幸运的是,我们对这两者都有解决方案。
有关选择最佳集群数量的更多详细信息,请参考这篇详细的博客。对于初始聚类的选择,我们可以运行具有各种初始化的模型的多次迭代来挑选最稳定的一个,或者使用具有以下步骤的“k-means++”算法:
- 从数据集中随机选择第一个质心
- 计算数据集中所有点与所选质心的距离
- 选择一个点作为新的质心,该点具有与该距离成比例的最大概率
- 重复步骤 2 和 3,直到 k 个质心被采样
该算法将质心初始化为彼此远离,从而产生比随机初始化更稳定的结果。
2.集群分配
K-means 然后根据点和所有质心之间的欧几里德距离将数据点分配给最近的聚类质心。
3。移动质心
该模型最后计算聚类中所有点的平均值,并将质心移动到该平均位置。
重复步骤 2 和 3,直到聚类中没有变化或者可能满足一些其他停止条件(例如最大迭代次数)。
为了在 python 中实现模型,我们需要首先指定集群的数量。我们使用了肘方法、间隙统计、轮廓评分、Calinski Harabasz 评分和 Davies Bouldin 评分。对于这些方法中的每一种,最佳聚类数如下:
- 肘法:8
- 差距统计:29
- 剪影评分:4
- 卡林斯基·哈拉巴斯得分:2
- 戴维斯·波尔丁得分:4
如上所述,5 种方法中有 2 种建议我们应该使用 4 个集群。如果每个模型建议不同数量的聚类,我们可以取平均值或中值。找到最佳 k 数的代码可以在这里找到,关于每种方法的更多细节可以在博客中找到。
一旦我们有了最佳数量的聚类,我们就可以拟合模型,并使用 Silhouette 评分、Calinski Harabasz 评分和 Davies Bouldin 评分来获得模型的性能。
# K meansfrom sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.metrics import calinski_harabasz_score
from sklearn.metrics import davies_bouldin_score# Fit K-Means
kmeans_1 = KMeans(n_clusters=4,random_state= 10)# Use fit_predict to cluster the dataset
predictions = kmeans_1.fit_predict(cluster_df)# Calculate cluster validation metricsscore_kemans_s = silhouette_score(cluster_df, kmeans_1.labels_, metric='euclidean')score_kemans_c = calinski_harabasz_score(cluster_df, kmeans_1.labels_)score_kemans_d = davies_bouldin_score(cluster_df, predictions)print('Silhouette Score: %.4f' % score_kemans_s)
print('Calinski Harabasz Score: %.4f' % score_kemans_c)
print('Davies Bouldin Score: %.4f' % score_kemans_d)
图 1:K-Means 的聚类验证指标(作者图片)
我们还可以使用簇间距离图来检查簇的相对大小和分布。
# Inter cluster distance map
from yellowbrick.cluster import InterclusterDistance# Instantiate the clustering model and visualizervisualizer = InterclusterDistance(kmeans_1)visualizer.fit(cluster_df) # Fit the data to the visualizer
visualizer.show() # Finalize and render the figure
图 2:聚类间距离图:K 均值(图片由作者提供)
如上图所示,两个集群相对于其他集群来说相当大,并且它们之间似乎有适当的间隔。然而,如果两个聚类在 2D 空间重叠,这并不意味着它们在原始特征空间重叠。关于这款车型的更多细节可以在这里找到。最后,K-means 的其他变体,如小批量 K-Means,K-Medoids 将在单独的博客中讨论。
凝聚聚类
凝聚聚类是一类通用的聚类算法,它通过连续合并数据点来构建嵌套聚类。这种聚类层次结构可以表示为一个树形图,称为树状图。树的顶部是包含所有数据点的单个聚类,而底部包含各个点。以连续方式链接数据点有多种选择:
- 单连锁:它最小化成对聚类的最近观测值之间的距离
- **完全或最大关联:**试图最小化成对聚类的观测值之间的最大距离
- **平均关联:**最小化所有聚类对之间的平均距离
- Ward: 类似于 k-means,因为它最小化了所有聚类内的平方差之和,但采用了分层方法。我们将在练习中使用该选项。
通过基于聚类验证指标(Silhouette 评分、Calinski Harabasz 评分和 Davies Bouldin 评分)检查哪种连锁方法表现最佳,可以挑选出理想的选项。与 K-means 相似,我们必须指定该模型中的聚类数,树状图可以帮助我们做到这一点。
# Dendrogram for Hierarchical Clustering
import scipy.cluster.hierarchy as shc
from matplotlib import pyplot
pyplot.figure(figsize=(10, 7))
pyplot.title("Dendrograms")
dend = shc.dendrogram(shc.linkage(cluster_df, method='ward'))
图 3:树状图(图片由作者提供)
从图 3 中,我们可以看到我们可以选择 4 或 8 个集群。我们还使用肘方法、剪影得分和 Calinski Harabasz 得分来寻找最佳聚类数,并得到以下结果:
- 肘法:10
- 戴维斯·波尔丁得分:8
- 剪影评分:3
- 卡林斯基·哈拉巴斯得分:2
我们将按照戴维斯·波尔丁评分和树状图的建议进行 8 分。如果指标给出了不同的集群数量,我们可以继续使用树状图建议的数量(因为它是基于这个特定的模型),或者取所有指标的平均值/中值。寻找最佳集群数量的代码可以在这里找到,关于每种方法的更多细节可以在这个博客中找到。
与 K-means 类似,我们可以用最佳的集群数量和链接类型来拟合模型,并使用 K-means 中使用的三个指标来测试其性能。
# Agglomerative clustering
from numpy import unique
from numpy import where
from sklearn.cluster import AgglomerativeClustering
from matplotlib import pyplot# define the model
model = AgglomerativeClustering(n_clusters=4)
# fit model and predict clusters
yhat = model.fit(cluster_df)
yhat_2 = model.fit_predict(cluster_df)
# retrieve unique clusters
clusters = unique(yhat)# Calculate cluster validation metricsscore_AGclustering_s = silhouette_score(cluster_df, yhat.labels_, metric='euclidean')score_AGclustering_c = calinski_harabasz_score(cluster_df, yhat.labels_)score_AGclustering_d = davies_bouldin_score(cluster_df, yhat_2)print('Silhouette Score: %.4f' % score_AGclustering_s)
print('Calinski Harabasz Score: %.4f' % score_AGclustering_c)
print('Davies Bouldin Score: %.4f' % score_AGclustering_d)
图 4:聚类验证指标:聚集聚类(作者图片)
比较图 1 和图 4,我们可以看到,基于所有的聚类验证指标,K-means 优于凝聚聚类。
基于密度的空间聚类
DBSCAN 将紧密聚集在一起的点组合在一起,同时将其他点标记为孤立在低密度区域中的异常值。定义“密度”需要模型中的两个关键参数:形成密集区域所需的最小点数min_samples
和定义邻域的距离eps
。更高的min_samples
或更低的eps
需要更大的密度来形成集群。
基于这些参数,DBSCAN 从任意点 x 开始,根据eps
识别 x 邻域内的点,并将 x 分类为以下类型之一:
- 核心点:如果邻域内的点数至少等于
min_samples
参数,则称之为核心点,并在 x 周围形成一个聚类。 - 边界点:如果 x 是具有不同核心点的聚类的一部分,但其邻域内的点数小于
min_samples
参数,则 x 被视为边界点。直观上,这些点位于星团的边缘。
3.异常值或噪声:如果 x 不是核心点,且与任何核心样本的距离至少等于或大于eps
,则认为是异常值或噪声。
为了调整模型的参数,我们首先通过找到一个点的相邻点之间的距离并绘制最小距离来确定最佳的eps
值。这给了我们找到数据点密度的肘形曲线,并且可以在拐点找到最佳的eps
值。我们使用NearestNeighbours
函数来获得最小距离,使用KneeLocator
函数来识别拐点。
# parameter tuning for eps
from sklearn.neighbors import NearestNeighbors
nearest_neighbors = NearestNeighbors(n_neighbors=11)
neighbors = nearest_neighbors.fit(cluster_df)
distances, indices = neighbors.kneighbors(cluster_df)
distances = np.sort(distances[:,10], axis=0)from kneed import KneeLocator
i = np.arange(len(distances))
knee = KneeLocator(i, distances, S=1, curve='convex', direction='increasing', interp_method='polynomial')
fig = plt.figure(figsize=(5, 5))
knee.plot_knee()
plt.xlabel("Points")
plt.ylabel("Distance")print(distances[knee.knee])
图 5:每股收益的最佳值(图片由作者提供)
如上所示,eps
的最佳值是 1.9335816413107338。我们将该值用于参数向前发展,并尝试根据剪影得分、Calinski Harabasz 得分和 Davies Bouldin 得分找到min_samples
参数的最佳值。对于这些方法中的每一种,最佳聚类数如下:
- 剪影得分:18
- 卡林斯基·哈拉巴斯得分:29
- 戴维斯·波尔丁得分:2
找到最佳数量min_samples
的代码可以在这里找到,关于每种方法的更多细节可以在这个博客中找到。我们继续采用建议的中间值,即侧影得分为 18。如果我们没有时间对这些指标进行网格搜索,一个快速的经验法则是将min_samples
参数设置为特性数量的两倍。
# dbscan clustering
from numpy import unique
from numpy import where
from sklearn.cluster import DBSCAN
from matplotlib import pyplot
# define dataset
# define the model
model = DBSCAN(eps=1.9335816413107338, min_samples= 18)# rule of thumb for min_samples: 2*len(cluster_df.columns)# fit model and predict clusters
yhat = model.fit_predict(cluster_df)
# retrieve unique clusters
clusters = unique(yhat)# Calculate cluster validation metricsscore_dbsacn_s = silhouette_score(cluster_df, yhat, metric='euclidean')score_dbsacn_c = calinski_harabasz_score(cluster_df, yhat)score_dbsacn_d = davies_bouldin_score(cluster_df, yhat)print('Silhouette Score: %.4f' % score_dbsacn_s)
print('Calinski Harabasz Score: %.4f' % score_dbsacn_c)
print('Davies Bouldin Score: %.4f' % score_dbsacn_d)
图 6:集群验证指标:DBSCAN(作者图片)
比较图 1 和图 6,我们可以看到 DBSCAN 在轮廓得分上比 K-means 表现得更好。该模型在论文中描述为:
在另一篇博客中,我们将讨论更高级版本的 DBSCAN,称为基于层次密度的空间聚类(HDBSCAN)。
高斯混合模型(GMM)
高斯混合模型是一种基于距离的概率模型,它假设所有数据点都是由具有未知参数的多元高斯分布的线性组合生成的。像 K-均值一样,它考虑了潜在高斯分布的中心,但与 K-均值不同,它还考虑了分布的协方差结构。该算法实现了期望最大化(EM)算法,以迭代方式查找使模型质量度量(称为对数似然)最大化的分布参数。该模型中执行的关键步骤是:
- 初始化 k 个高斯分布
- 计算每个点与每个分布关联的概率
- 根据与分布相关的每个点的概率重新计算分布参数
- 重复该过程,直到对数似然最大化
在 GMM,有 4 种计算协方差的方法:
- **完整:**每个分布都有自己的一般协方差矩阵
- **并列:**所有分布共享一般协方差矩阵
- Diag: 每个分布都有自己的对角协方差矩阵
- **球形:**每个分布都有自己的单方差
除了选择协方差类型,我们还需要选择模型中的最佳聚类数。我们使用 BIC 评分、剪影评分、Calinski Harabasz 评分和 Davies Bouldin 评分来使用网格搜索选择两个参数。对于这些方法中的每一种,最佳聚类数如下:
- BIC 分数:协方差-“满”和聚类数- 26
- 轮廓得分:协方差-“并列”和聚类数- 2
- Calinski Harabasz 得分:协方差-“球形”和聚类数- 4
- 戴维斯·波尔丁得分:协方差-“满”和聚类数- 8
寻找最佳参数值的代码可以在这里找到,关于每种方法的更多细节可以在这个博客中找到。我们选择协方差为“完全”,基于 BIC 评分的聚类数为 26,因为它是基于这个特定的模型。如果我们有来自多个指标的相似配置,我们可以取所有指标的平均值/中值/众数。我们现在可以拟合模型并检查模型性能。
# gaussian mixture clustering
from numpy import unique
from numpy import where
from sklearn.mixture import GaussianMixture
from matplotlib import pyplot
# define the model
model = GaussianMixture(n_components= 26,covariance_type= "full", random_state = 10)
# fit the model
model.fit(cluster_df)
# assign a cluster to each example
yhat = model.predict(cluster_df)
# retrieve unique clusters
clusters = unique(yhat)# Calculate cluster validation scorescore_dbsacn_s = silhouette_score(cluster_df, yhat, metric='euclidean')score_dbsacn_c = calinski_harabasz_score(cluster_df, yhat)score_dbsacn_d = davies_bouldin_score(cluster_df, yhat)print('Silhouette Score: %.4f' % score_dbsacn_s)
print('Calinski Harabasz Score: %.4f' % score_dbsacn_c)
print('Davies Bouldin Score: %.4f' % score_dbsacn_d)
图 7:集群验证指标:GMM(图片由作者提供)
比较图 1 和图 7,我们可以看到 K-means 在所有集群验证指标上都优于 GMM。在另一篇博客中,我们将讨论 GMM 的更高级版本,称为变分贝叶斯高斯混合。
结论
这篇博客的目的是帮助读者理解 4 种流行的聚类模型是如何工作的,以及它们在 python 中的具体实现。如下所示,每种模式都有其优缺点:
图 8:聚类算法的利与弊(图片由作者提供)
最后,重要的是要理解这些模型只是一种手段,用于找到合理的、易于理解的、可以有效定位的客户/产品细分市场。因此,在大多数实际情况下,我们将最终尝试多种模型,并从每次迭代中创建客户/产品档案,直到我们找到最具商业意义的细分市场。因此,分段既是一门艺术也是一门科学。
你对这个博客有什么问题或建议吗?请随时留言。
感谢您的阅读!
如果你和我一样,对人工智能、数据科学或经济学充满热情,请随时添加/关注我的 LinkedIn 、 Github 和 Medium 。
参考
- Ester,M .,Kriegel,H . P .,Sander,J .,,Xu .一种基于密度的算法,用于在带有噪声的大型空间数据库中发现聚类。美国:不扩散条约,1996 年。网络。
- 多变量观察的分类和分析的一些方法。第五届伯克利数理统计和概率研讨会会议录,第 1 卷:统计,281–297,加州大学出版社,伯克利,加利福尼亚州,1967 年。https://projecteuclid.org/euclid.bsmsp/1200512992
- sci kit-learn:Python 中的机器学习,Pedregosa 等人,JMLR 12,第 2825–2830 页,2011 年。
k-意味着用 NumPy 从头开始
使用这种快速简单的聚类算法回归基础
图片来自 unsplash
K-means 是最简单的聚类算法。它很容易理解和实现,当试图理解无监督学习的世界时,这是一个很好的起点。
无监督学习指的是机器学习的整个子领域,其中数据没有标签。我们不是训练一个模型来预测标签,而是希望揭示数据中的某种潜在结构,否则这些结构可能不明显。
它是如何工作的?
K-means 从假设数据可以分成 K 个不同的簇开始。每个聚类的特征在于与该聚类相关的点的平均值(因此得名… K-means)。
识别 K 个不同装置位置的程序如下:
- 随机分配数据中的每个点到一个簇中
- 计算分配给特定聚类的每个点的平均值
- 对于每个点,根据最接近该点的平均值更新分配的平均值。
- 重复步骤 2 和 3,直到平均值收敛到恒定值。
来实施吧!
为了便于跟踪不同的点以及它们之间的关联,让我们构建一个小类(注意,同样的事情可以通过命名元组或字典轻松实现)。
import numpy as npK = 3class point():
def __init__(self, data):
self.data = data
self.k = np.random.randint(0,K)
def __repr__(self):
return str({"data":self.data, "k":self.k})
也不需要__repr__
函数,但它有助于查看引擎内部发生了什么。
现在我们需要一些模拟数据来玩:
N = 200
data1 = np.random.randn(N//3,2) + np.array([5,6])
data2 = np.random.randn(N//3,2) + np.array([-5,-6])
data3 = np.random.randn(N//3,2) + np.array([-10,3])
data = np.concatenate((data1, data2, data3))
绘制如下图:
用 3 个集群模拟数据
第一步:随机分配
当我们为每个数据点实例化类时,我们的类通过随机选择一个指定的平均值来处理这个问题。
points = [point(d) for d in data]
第二步:计算平均值
为了完成这一步,我们需要构建两个函数。一个创建分配给每个分类的点的列表,另一个计算每个分类的平均值。我们可以使用如下的collections.defaultdict
实现第一个:
from collections inport defaultdictdef make_k_mapping(points):
point_dict = defaultdict(list)
for p in points:
point_dict[p.k] = point_dict[p.k] + [p.data]
return point_dict
然后是第二个功能:
def calc_k_means(point_dict):
means = [np.mean(point_dict[k],axis=0) for k in range(K)]
return means
步骤 3:更新点群分配
现在我们需要计算距离,并根据最接近的聚类平均值更新关联的聚类。
def update_k(points,means):
for p in points:
dists = [np.linalg.norm(means[k]-p.data) for k in range(K)]
p.k = np.argmin(dists)
训练循环
现在,我们只需要将这些函数组合在一个循环中,为我们的新聚类算法创建一个训练函数。
def fit(points, epochs=10):
for e in range(epochs):
point_dict = make_k_mapping(points)
means = calc_k_means(point_dict)
update_k(points, means)
return means, pointsnew_means, new_points = fit(points)
如果我们把新的方法和原始点一起画出来,我们会得到这样的结果:
聚类和原始数据
参数调谐
对于你们当中目光敏锐的人来说,你们会意识到我们从一开始就选择了正确的集群数量。如果我们选择 K 大于或小于 3,我们会有一个较差的数据拟合。
K = 2
K = 4
这就引出了一个问题,我们如何更好地量化模型的拟合度?一个自然的选择是计算一个点与其聚类平均值的平均距离。考虑到点被分配给具有欧几里德距离的簇,使用这个作为性能的度量看起来是一个合理的选择。
def evaluate(points):
point_dict = make_k_mapping(points)
means = calc_k_means(point_dict)
dists = [np.linalg.norm(means[p.k]-p.data) for p in points]
return np.mean(dists)
作为 K 的函数的平均距离
表面上看起来是可行的,但仍然有一个问题,K = 4 的平均距离小于 K = 3。仔细想想,这很有意义,每个数据点都有一个聚类,平均距离为 0。然而,这显然是一个过度拟合数据的模型。
对此的一个解决方案被称为 Akaike 信息标准(AIC)。该度量以模型参数的数量来惩罚模型的可能性。
对于 K-means,AIC 是点与其分配的均值之间的平方距离之和,由训练参数的数量决定。这来自于假设每个聚类是具有单位协方差的高斯分布。K 均值模型的训练参数的数量是K * d
,其中d
是维度的数量。
K 均值的 AIC
这个图有一个明确的最小值 3,这正是我们想要的!
我们学到了什么?
所以在这里,我们已经了解了 K-means 如何工作,如何用 NumPy 构建模型,以及如何训练它。我们还研究了一些评估模型性能的潜在方法,以及如何使用模型参数的数量来惩罚评估指标。
K-means 是一种轻量级但功能强大的算法,可用于解决许多不同的聚类问题。现在您知道它是如何工作的,以及如何自己构建它了!
营销分析中的 K-Means:聚类 210 个美国 DMA
从头到尾的 K-Means 聚类分析
佩德罗·拉斯特拉在 Unsplash 上的照片
当我可以将机器学习技术应用于日常营销问题时,这让我非常兴奋。给你一些背景,我在一家大型广告公司工作,我的一些客户是美国的顶级广告商,他们的产品和服务覆盖整个国家,他们有很多营销预算要花(每年大约 9 位数!).所以我们的工作就是帮助他们聪明高效地花钱!
挑战之一是为所有当地市场制定战略,即 DMA(DMA代表“指定市场区域”,也可能被称为媒体市场)。美国有 210 个 DMA,你当然可以按地区划分,或者简单地按西部、东部和中部划分。然而,考虑到大区域内的人口统计和经济差异,我们可能希望使用一些更数据驱动的方式来查看所有这些 DMA。因此,今天我将使用聚类来获得几个市场聚类,每个聚类中具有最大的相似性。
在深入细节之前,我想给你看一下我在探索机器学习时最喜欢的图表。
聚类属于无监督学习的分支,该分支在没有预先存在标签的数据集中寻找以前未检测到的模式。聚类背后的主要思想是,您希望以一种方式将对象分组到相似的类中,即同一组的成员之间的相似性高,而不同组的相似性低。
最流行和最广泛使用的算法是 **K-Means 聚类算法。**k-means 聚类算法是一种迭代算法,在未标记的数据集中达到预定数量的聚类,基本工作方式如下:
- 选择𝑘初始种子
- 将每个观察值分配给具有最近平均值(最小平方欧几里得距离)的聚类
- 为分配给每个聚类的观测值重新计算聚类质心
- 继续迭代,直到质心没有变化
k 表示聚类示例(k = 7)
对于 k-means 聚类算法来说,两个假设非常重要:
- 要计算“聚类中心”,需要计算属于该聚类的所有点的(算术)平均值。在每次新的迭代开始时,重新计算每个聚类中心
- 在重新计算聚类中心之后,如果给定点现在比其当前聚类的中心更靠近不同的聚类中心,则该点被重新分配给最近的中心
我将用一个营销支出数据集来演示它在 Python 中是如何工作的,这个数据集是我从一家研究公司 Kantar 收集的,它是过去两年中排名前 30 位的科技公司的数据。
market.head(5)
数据字典
- 市场:指定市场区域(DMA)是指人们可以接收相同电视和广播电台节目的区域,也可能包括其他类型的媒体,包括报纸和互联网内容
- 媒体:10 种常见媒体类型,包括电视、广播、户外、数字等。
- 观众人口:12 岁以上的电视/广播观众人口
- 公司/品牌:在该市场做广告的公司/品牌数量
- IsTop31:电信行业对市场重要性的主观分类。0 表示不是前 31 名市场,1 表示前 31 名市场
- Spend_K:以千美元计的媒体支出(000)
预处理分类值
#Change MARKETS to categorical
market['DMA_Code']=market['DMA_Code'].astype('category')#One Hot Encoding for Media and Industryfrom sklearn.preprocessing import LabelBinarizerdef labelcoder(data, column):
onehot = data.copy()
lb=LabelBinarizer()
lb_results = lb.fit_transform(onehot[column])
lb_results_df = pd.DataFrame(lb_results, columns=lb.classes_)
return lb_results_dfmedia_features = labelcoder(market, 'MEDIA')
industry_features = labelcoder(market, 'INDUSTRY')
division_features = labelcoder(market, 'Division_Name')#Put them all together
market_df = pd.concat([market['DMA_Code'],scaled_features, media_features, industry_features, division_features], axis=1)
标准化数字特征
num_features = market[['Audience Population','BRAND','Company','IsTop31','Spend_K']]from sklearn.preprocessing import MinMaxScaler
#Scale the numerical features
scaler = MinMaxScaler()
scaled = scaler.fit_transform(num_features)
scaled_features = pd.DataFrame(scaled, columns = num_features.columns)
设置 K-Means
用肘法求最优 K
这一步非常重要,因为我们不仅想要任意数量的聚类,还想要找到尽可能多地分隔数据点而不引入整体复杂性的 K 的数量。常用的度量是惯性——样本到其最近聚类中心的平方距离之和
import matplotlib.pyplot as plt
from sklearn.cluster import KMeanssum_of_squared_distances = []K = range(1,20)
for k in K:
km = KMeans(n_clusters=k)
km = km.fit(market_df)
sum_of_squared_distances.append(km.inertia_) plt.plot(K, sum_of_squared_distances, 'bx-')
plt.xlabel('k')
plt.ylabel('Sum of Squared Distances to the Nearest Cluster')
plt.title('Elbow Method For Optimal k')
plt.show()
如上图所示,收益递减达到 k = 3。在 k =3 之后,度量以慢得多的速度变得更好。
#Fit the model with k = 3
k_means = KMeans(n_clusters=3, random_state=0).fit(market_df)
cluster_preds = k_means.predict(market_df)
k_means.get_params
使用 Calinski Harabasz 评分(方差比)评估聚类适合度
from sklearn.metrics.cluster import calinski_harabasz_score calinski_harabasz_score(market_df, cluster_preds)39451.77110587817
解释
理解 k-means 聚类结果不像解释监督学习结果那样容易,因为我们本质上是在寻找数据的“底层”模式,并且经常需要领域知识。
但是,我们可以查看描述性统计数据来了解这些分类。
n_clusters = 3
k_means_cluster_stats = pd.DataFrame()for i in range(n_clusters):
k_means_cluster_stats['Cluster ' + str(i+1)] = market_df[k_means.labels_ == i].mean(axis=0)
k_means_cluster_stats = k_means_cluster_stats.T
我们可以看出,集群 2 拥有最大的平均受众群体和最高的平均支出水平,这可能表明这一组市场是排名前 30 位的科技公司的主要关注点或领域。
- 集群 0 和集群 2 混合了不同的部门,而集群 1 只有西部市场
- 集群 0 的本地支出中值最高,数字支出的百分比也很高
- 第 1 组主要是户外活动,数字消费相对较低
- 群组 2 的广告电视支出最高
感谢您的关注,希望这对您的集群项目有所帮助!
完整的代码可以在我的 github 这里找到。
K-Means 与 DBSCAN 聚类—适用于初学者
图片来源——un splash
聚类——一种无监督的机器学习技术
聚类是将未标记的数据点以这样的方式分组:
同一组内的数据点彼此相似,
不同组内的数据点彼此不相似。
目标是创建具有高的类内相似性和低的类间相似性的类。
k 均值聚类
K-Means 聚类是最常用的无监督机器学习聚类技术之一。这是一种基于质心的聚类技术,需要您决定聚类(质心)的数量,并随机放置聚类质心来开始聚类过程。目标是重复地将 N 个观察值分成 K 个簇,直到不能形成更多的组。
K 均值的优势
- 易于理解和实施。
- 能很好的处理大型数据集。
K 均值的缺点
- 对选择的簇/质心的数量敏感。即使使用了像 Elbow 方法这样的技术,有时也很难生成好的集群。
- 不适用于异常值。质心可能会被离群值拖住,导致聚类偏斜。
- 在高维空间中变得困难,因为点之间的距离增加,欧几里德距离发散(收敛到恒定值)。
- 随着维数的增加而变慢。
k-均值算法
1.决定集群的数量。这个数叫做 K,聚类数等于质心数。基于 K 的值,生成 K 个随机质心的坐标。
2.对于每个点,计算该点和每个质心之间的欧几里德距离。
3.将该点指定给其最近的质心。分配给同一个质心的点形成一个簇。
4.一旦聚类形成,通过取聚类平均值来计算每个聚类的新质心。聚类平均值是属于该聚类的所有点的 x 和 y 坐标的平均值。
5.重复步骤 2、3 和 4,直到质心不能再移动。换句话说,重复这些步骤直到收敛。
使用 Scikit Learn 的虹膜数据集的 k 均值
import pandas as pd
from sklearn import metrics
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt# reading the classic iris dataset into a df
iris_df = pd.read_csv(“iris_dataset.csv”)# Setting the independent features (input)
X = iris_df.drop(“species”, axis=1).values# Creating the KMeans object and fitting it to the Iris data
iris_kmeans = KMeans(n_clusters=3)iris_kmeans.fit(X)# Predicting the cluster labels
labels = iris_kmeans.predict(X)
print(labels)# Finding the final centroids
centroids = iris_kmeans.cluster_centers_# Evaluating the quality of clusters
s = metrics.silhouette_score(X, labels, metric=’euclidean’)
print(f”Silhouette Coefficient for the Iris Dataset Clusters: {s:.2f}”)# plotting the clusters using sepal_length and sepal_width
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap=”rainbow”)
plt.show()
输出
标签值代表聚类数。
[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 0 0 0 0 2 0 0 0 0
0 0 2 2 0 0 0 0 2 0 2 0 2 0 0 2 2 0 0 0 0 0 2 0 0 0 0 2 0 0 0 2 0 0 0 2 0
0 2]Silhouette Coefficient for the Iris Dataset Clusters: 0.55
三个 K 均值聚类
寻找 K-Means 的最佳聚类数(肘方法)
使用 K-Means 形成的聚类的质量很大程度上取决于 K 的选定值。K 的错误选择会导致较差的聚类。那么如何挑选 K 呢?我们来看看常用的手法叫做“肘法”。目标是选择形成弯头的 K 点。
步骤:
1.对于不同的 K 值,执行以下步骤:
2.对于每个聚类,计算每个点到其质心的距离的平方和。
3.将每个聚类的距离平方和相加,得到 k 值的距离平方和。
4.不断将每个 K 的距离平方和添加到列表中。
5.绘制距离平方和(使用上一步创建的列表)及其 K 值。
6.选择发生急剧变化的 K(看起来像曲线的弯头)。
# Arbitrarily selecting a range of values for K
K = range(1,10)sum_of_squared_distances = []# Using Scikit Learn’s KMeans Algorithm to find sum of squared distancesfor k in K:
model = KMeans(n_clusters=k).fit(X)
sum_of_squared_distances.append(model.inertia_)plt.plot(K, sum_of_squared_distances, “bx-”)
plt.xlabel(“K values”)
plt.ylabel(“Sum of Squared Distances”)
plt.title(“Elbow Method”)
plt.show()
查看该图,我们可以看到 K=3 处的拐点,因此这是该数据集的最佳聚类数。
有时,我们可能会以多个值显示一个肘形结束。在这种情况下,为了找到最佳 K,可以使用像轮廓系数这样的评估度量。应该选择将返回轮廓系数的最高正值的 K。
基于密度的含噪声应用空间聚类(DBSCAN)
DBSCAN 是一种基于密度的聚类算法,它形成数据点密集区域的聚类,忽略低密度区域(将其视为噪声)。
维基百科图片
DBSCAN 的优势
- 适用于噪声数据集。
- 可以轻松识别异常值。
- 聚类可以采取任何不规则的形状,不像 K-Means 中的聚类或多或少是球形的。
DBSCAN 的缺点
- 不适用于稀疏数据集或密度变化的数据集。
- 对 eps 和 minPts 参数敏感。
- 对于多处理器系统不可分区。
DBSCAN 参数
DBSCAN 使用以下两个用户定义的参数进行聚类:
ε(EPS):定义为被认为是相邻点(属于同一个聚类)的两点之间的最大距离。
最小点(min_samples 或 minPts) :这定义了一个给定点需要被视为核心数据点的相邻点的最小数量。这包括点本身。例如,如果最小点数设置为 4,则给定点需要有 3 个或更多的相邻数据点才能被视为核心数据点。
如果最小数量的点满足ε距离要求,则它们被视为一个聚类。
DBS can 中的重要术语
**Core Point:** A data point is considered to be a core point if it has minimum number of neighboring data points (min_samples) at an epsilon distance from it. This minimum number of data points includes the original data point.**Border Point:** A data point that has less than minimum number of data points needed but has at least one core point in the neighborhood.**Noise:** A data point that is not a core point or a border point is considered noise or an outlier.
DBSCAN 算法
1 。决定 eps 和 minPts 的值。
2 。对于每个点:
- 计算它与所有其他点的距离。如果距离小于或等于 eps,则将该点标记为 x 的邻居。
- 如果该点的相邻点计数大于或等于 minPts,则将其标记为核心点或已访问点。
3 。对于每个核心点,如果它还没有被分配给一个集群,则创建一个新的集群。递归地找到它的所有邻近点,并把它们分配到与核心点相同的簇中。
4 。继续这些步骤,直到覆盖所有未访问的点。
使用 Scikit Learn 对虹膜数据集进行 DBSCAN 聚类
import pandas as pd
from sklearn import metrics
from sklearn.cluster import DBSCAN
import matplotlib.pyplot as plt# reading the classic iris dataset into a df
iris_df = pd.read_csv(“iris_dataset.csv”)X = iris_df.drop(“species”, axis=1).valuesiris_dbscan = DBSCAN(eps=0.5, min_samples=5)
iris_dbscan.fit(X)labels = iris_dbscan.labels_# label=-1 means the point is an outlier. Rest of the values represent the label/cluster number starting from 0
print(labels)# Creating a numpy array with all values set to false by default
core_samples_mask = np.zeros_like(labels, dtype=bool)# Setting core and border points (all points that are not -1) to True
core_samples_mask[iris_dbscan.core_sample_indices_] = True# Finding the number of clusters in labels (ignoring noise if present)
n_clusters_ = len(set(labels)) — (1 if -1 in labels else 0)
n_noise_ = list(labels).count(-1)# Printing the number of clusters and number of noise points (outliers)
print(“Estimated number of clusters: %d” % n_clusters_)
print(“Estimated number of noise points: %d” % n_noise_)# Evaluating the quality of clusters
s = metrics.silhouette_score(X, iris_dbscan.labels_)
print(f”Silhouette Coefficient for the Iris Dataset Clusters: {s:.2f}”)
输出
Label = -1 表示它是一个噪声点(异常值)。
Label = 0 或以上,表示集群编号。
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 -1 1 1 -1 1 1 1 1 1 1 1 -1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -1 1 1 1 1 1 -1 1 1
1 1 -1 1 1 1 1 1 1 -1 -1 1 -1 -1 1 1 1 1 1 1 1 -1 -1 1
1 1 -1 1 1 1 1 1 1 1 1 -1 1 1 -1 -1 1 1 1 1 1 1 1 1
1 1 1 1 1 1]Estimated number of clusters: 2Estimated number of noise points: 17Silhouette Coefficient for the Iris Dataset Clusters: 0.49
绘制聚类图
unique_labels = set(labels)colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))for k, col in zip(unique_labels, colors):
if k == -1:
# Black used for noise
col = “k”
class_member_mask = (labels == k)
xy = X[class_member_mask & core_samples_mask]
plt.plot(xy[:, 0], xy[:, 1], “o”, markerfacecolor=col,
markeredgecolor=”k”, markersize=10) xy = X[class_member_mask & ~core_samples_mask]
plt.plot(xy[:, 0], xy[:, 1], “o”, markerfacecolor=col,
markeredgecolor=”k”, markersize=5)plt.title(“Estimated number of clusters: %d” % n_clusters_)
plt.show()
黑色代表异常值,彩色点代表两个 DBSCAN 集群
寻找每股收益的最佳值(拐点法)
DBSCAN 聚类算法对我们选择的 eps 值很敏感。那么如何才能知道我们选择了最优的 eps 值呢?这里有一个常用的手法叫做“膝法”。目标是找出每个点到其 K 个最近邻点的平均距离,并选择曲率最大或发生急剧变化的距离。K 的值被设置为等于 minPoints。
以下是一个使用 Scikit Learn 的 NearestNeighbors 模块显示最佳 eps 值选择的示例。
from sklearn.neighbors import NearestNeighbors
nearest_neighbors = NearestNeighbors(n_neighbors=5)nearest_neighbors.fit(X)
distances, indices = nearest_neighbors.kneighbors(X)
distances = np.sort(distances, axis=0)[:, 1]
print(distances)plt.plot(distances)
plt.show()
输出
[0\. 0\. 0\. 0\. 0\. 0.1
0.1 0.1 0.1 0.1 0.1 0.1
0.1 0.14142136 0.14142136 0.14142136 0.14142136 0.14142136
0.14142136 0.14142136 0.14142136 0.14142136 0.14142136 0.14142136
0.14142136 0.14142136 0.14142136 0.14142136 0.14142136 0.14142136
0.14142136 0.14142136 0.14142136 0.14142136 0.14142136 0.14142136
0.14142136 0.14142136 0.14142136 0.14142136 0.14142136 0.14142136
0.14142136 0.14142136 0.14142136 0.14142136 0.14142136 0.14142136
0.17320508 0.17320508 0.17320508 0.17320508 0.17320508 0.17320508
0.17320508 0.2 0.2 0.2 0.2 0.2
0.2 0.2 0.2 0.2 0.2 0.2
0.2236068 0.2236068 0.2236068 0.2236068 0.2236068 0.2236068
0.2236068 0.2236068 0.2236068 0.2236068 0.24494897 0.24494897
0.24494897 0.24494897 0.24494897 0.24494897 0.24494897 0.24494897
0.24494897 0.24494897 0.24494897 0.24494897 0.26457513 0.26457513
0.26457513 0.26457513 0.26457513 0.26457513 0.26457513 0.26457513
0.26457513 0.26457513 0.26457513 0.26457513 0.28284271 0.28284271
0.28284271 0.28284271 0.3 0.3 0.3 0.3
0.3 0.3 0.3 0.31622777 0.31622777 0.31622777
0.33166248 0.33166248 0.33166248 0.33166248 0.33166248 0.34641016
0.34641016 0.34641016 0.34641016 0.34641016 0.34641016 0.34641016
0.36055513 0.36055513 0.36055513 0.36055513 0.37416574 0.38729833
0.38729833 0.38729833 0.41231056 0.41231056 0.41231056 0.41231056
0.42426407 0.42426407 0.43588989 0.45825757 0.48989795 0.48989795
0.53851648 0.53851648 0.55677644 0.6244998 0.63245553 0.73484692]
最佳值应该是我们看到最大曲率的值,在这种情况下,该值似乎接近 0.5。
有时,我们可能会以多个值显示急剧变化而告终。在这种情况下,为了找到最佳 K,可以使用像轮廓系数这样的评估度量。应该选择将返回轮廓系数的最高正值的 K。
何时使用这两种聚类技术中的哪一种,取决于问题。尽管 K-Means 是最流行的聚类技术,但也有使用 DBSCAN 得到更好的聚类的用例。
k 表示没有库的集群
Billy Huynh 在 Unsplash 上拍摄的照片
—使用 Python
Kmeans 是一种广泛使用的聚类工具,用于分析和分类数据。然而,我怀疑,人们往往并不完全了解幕后发生了什么。如果你理解最终产品传达了什么,这不一定是一件坏事,但是通过从头开始构建算法来了解发生了什么肯定会导致对其背后的推理的更深入的理解。
我 想先强调一下互联网是程序员和工程师的绝佳去处。答案和资源随处可得,只需谷歌搜索即可。假装这一切都是我自己想出来的是愚蠢的。我很乐意承认,有时需要通读其他人在算法方面的工作,才能理解如何更好地接近它。代码的美妙之处在于它可以用许多不同的方式编写,每种方式强调的质量都略有不同。在你的学习中利用这一点。
既然我已经谈到了那一点,让我们开始吧!
K 表示聚类最简单的形式是一种算法,它在数据簇中找到密切的关系,并将它们分组以便于分类。
你在这里看到的是一种算法,根据特定的质量将不同的数据点分类成组或段…接近(或接近)一个中心点。
最常见的是 Scikit-Learn 的 KMeans 算法,看起来像这样:
from sklearn.cluster import KMeanskm = KMeans(
*n_clusters*=3, *init*='random',
*n_init*=10, *max_iter*=300,
*random_state*=42
)y_km = km.fit_predict(X)
您可能不太理解这些部分,但它的方法相当简单。它主要做的是,它说我们需要 3 个集群,从 10 次迭代开始(或运行,每次细化集群和位置),3 个中心点的初始化是随机的,最大迭代次数是 300,随机状态只是指每次运行它,它都是相同的。然后我们运行预测。更多信息可在此处阅读关于可使用的不同参数。
那么,我们如何从头开始创建这些代码呢……尤其是在我们不确定发生了什么的情况下?我们来想办法吧!
第一步是思考并描述正在发生的事情。首先,这篇文章很好地描述了每一步。总之,我们在散点图上绘制出 k 数量的点(也称为质心),通常是随机的,并找到最接近这些点的数据。然后,我们不断地重新计算从数据到质心的平均距离和质心位置,直到每个 k 质心周围都有清晰的数据组。
我失去你了吗?希望不会。让我们浏览一下每个流程,看看发生了什么:
- 第一步是我们需要决定要将数据分成多少个簇。这有一个方法,但是为了简单起见,我们说我们将使用 3 个集群,或者, k = 3。代码看起来像这样:
k = 3
clusters = {}for i in range(k):
clusters[i] = []
你在上面看到的只是创建了 3 个空的集群。看起来是这样的…
{0: [], 1: [], 2: []}
很简单,对吧?
然后,我们以类似的方式设置质心,但这次我们使用我们正在使用的数据。在我的例子中,我使用的是波士顿住房数据集。 *X,在本例中,*是我从数据集中选择的两个数据点的数组。
for i in range(k):
centroids[i] = X[i]
接下来,我们需要找到每个数据点到质心的距离。这个概念很简单,但是下一个块一开始看起来有点混乱。我建议搜索和阅读这篇文章的不同部分,以便更好地了解正在发生的事情。例如,如果你在谷歌上搜索“np.linalg.norm ”,你会发现 这个页面 描述了它是什么以及它的作用。
for data in X:
euc_dist = []
for j in range(k):
euc_dist.append(np.linalg.norm(data - centroids[j]))
clusters[euc_dist.index(min(euc_dist))].append(data)
初始化质心和聚类后,我们要重新计算这两个值!为什么?因为它们在某种程度上是随机初始化的,所以我们需要慢慢地将它们移向数据自然分段的最理想方式(如果有的话,,但那是另一个讨论)。
我写了两个函数来实现这个功能。让我们来看看:
def recalculate_clusters(*X*, *centroids*, *k*):
""" Recalculates the clusters """
# Initiate empty clusters
clusters = {}
# Set the range for value of k (number of centroids)
for i in range(k):
clusters[i] = []
for data in X:
euc_dist = []
for j in range(k):
euc_dist.append(np.linalg.norm(data - centroids[j]))
# Append the cluster of data to the dictionary
clusters[euc_dist.index(min(euc_dist))].append(data)
return clustersdef recalculate_centroids(*centroids*, *clusters*, *k*):
""" Recalculates the centroid position based on the plot """
for i in range(k):
centroids[i] = np.average(clusters[i], *axis*=0)
return centroids
我希望您能在这两个函数中识别出一些相同的代码。请密切注意不同的零件和部件。学习的最好方法之一是剖析内在发生的事情。再一次,我要求你用谷歌搜索这段代码的个别部分。这就像把收音机拆开,然后再组装起来。把那个内在的工程师拿出来!
从那里,我们将把一个绘图函数放在一起,绘制每个集群,并为其分配不同的颜色。这就像将数据输入分拣机,分拣机根据数据的去向对不同的数据进行颜色编码。出来的东西看起来像这样:
我们可以看到数据被清晰地分割成不同的部分,尽管它们分布得不是很好。这是因为这仅仅是数据的第一次迭代!我还应该提到,这种形状并不完全适合于聚类,这本身就是一个关于算法的优点和缺点的教训。
那么,当我们希望在集群之间有更好的分布时,我们该怎么做呢?…重新计算,重新计算,重新计算!在这种情况下,如果我们运行它,比如说,10 次,它将如下所示:
现在有一个稍微好一点的分配,不是吗?我们看到的是数据中 3 种不同的非监督分类。算法告诉我们,这三种颜色可能意味着数据中至少值得研究的东西。请注意,这并不意味着它实际上很重要。这就是数据的有趣之处。计算机努力增强我们在数据中寻找关系的能力,但最终还是要由我们来决定这些关系(如果有的话)意味着什么。你的数据科学工作可能会持续一段时间。唷!
更有趣的旁注之一…当决定运行多少次迭代时(或者换句话说,你想要重新计算多少次),你可以把通常所说的“肘图”放在一起,看看迭代真正开始失去区分能力的地方。我的看起来像这样:
您可以看到,大约 3 到 4 次重复后,它开始失去每次迭代在调整这些簇和质心时产生的动量。这是一个很好的检查有多少计算你真的想运行。毕竟这对于一个公司来说,时间就是金钱,资源一般都是有限的!当然,这是非常低级的东西,所以没什么大不了的,但它总是值得你和/或团队就项目和分析进行一次对话。
要查看整个(进行中)笔记本,请导航到我的 GitHub 库,这里是T5!你将会看到我如何选择数据集以及最初探索数据集的一些细节。
我希望你喜欢 K 均值聚类的概述!请注意,我对数据科学的世界还相当陌生,所以我绝对愿意接受本文中详述的观点和方法的修正。毕竟,我觉得学习是持续的,我一点也不介意提高我解释和利用这些方法的能力。我们都从错误中学习!如果您觉得有任何错误或需要澄清的地方,请联系我们。谢谢大家!
k-最近邻
邻居很重要!!
照片由 Pexels 的 Engin Akyurt 拍摄
介绍
KNN 或 k-最近邻是一种监督学习算法。它可以应用于回归和分类问题的解决。KNN 是一种基于其最近样本来识别样本空间中任何特定点的类别或类标签的技术。KNN 中的字母 k 表示我们将在邻域中考虑多少样本,以预测我们在样本空间中关注点的类别标签。
KNN 也被称为基于实例的学习算法**。在基于实例的学习中,当我们获得训练样本时,我们不会处理它们并学习模型,而是存储训练样本,当我们需要对实例进行分类时,我们会对测试样本进行训练和类标签关联。所以基于实例的学习算法也被称为懒惰算法。**
分类中的 KNN
作者图片
我们被给予不同的样本点(x₁,y₁),(x₂,y₂),(x₃,y₃),……,(xₙ,yₙ).所有这些点都被归入类标签 1 或类标签 2。我们引入一个点(xₜ,yₜ)and 想预测它的类标签。因此,我们使用 KNN 方法,并遵循以下步骤。
第一步:选择 k 的值
我们可以决定 k 的值,它将决定我们需要考虑的预测测试 sample(xₜ,yₜ).值的最近样本的总数在我们的例子中,我们考虑 k=5。
第二步:计算距离
使用欧几里得距离公式确定 5 个最接近的样品 w.r.t 测试样品。对于任意两个给定点,欧几里德距离使用以下公式计算
作者图片
步骤 3:根据类别对最近的点进行分类
从图中可以观察到,在 5 个最近的点中,它们中的 3 个属于类别 label-2,它们中的两个属于类别 label-1。因此,根据大多数情况,可以得出结论,点(xₜ,yₜ)属于类 label-2。
回归中的 KNN
作者图片
在 KNN 回归的情况下,我们将使用与分类几乎相同的方法。只有第 3 步有所不同,在这一步中,我们不会为测试样本取多数类别标签。相反,我们将取所有类标签的平均值,并将该值设置为测试样本的类标签。例如,在上图中,我们考虑 k=5 来确定测试样本的类别标签。通过使用距离公式,我们识别 5 个最近的训练样本点,并识别它们的类别标签。因为这是一个回归问题,所以所有 5 个训练样本的类别标签是相似的。因此,取所有测试样本的类别标签的平均值,就得到测试样本的类别标签。
属性和权重
我们看到每个样本点都有自己的 x 和 y 坐标。但是除了坐标之外,还有代表样本空间中的点的特征或属性。这些属性也非常有助于将点分成各种类别标签。因此,在寻找最近的 k 点时,也必须从所有这些属性计算欧几里德距离。
作者图片
现在我们知道我们考虑回归 KNN 问题的类别标签的平均值。当我们考虑 k 的大值时,那么在分类过程中有必要取类别标签的平均值,而不是多数考虑。以下是一些原因:
- 属性中的噪声——由于噪声的存在,距离测试样本最近的样本点可能无法捕捉到测试样本的所有特征,而稍远的点可能能够捕捉到。
- 类别标签中的噪声——由于类别标签中存在噪声,测试样本的错误分类几率很高
- 部分重叠的类别标签-类别标签的重叠导致算法无法分配样本测试点的正确类别标签。
你有没有想过为什么我们不考虑属性的权重?如果认为所有属性的权重相等,则以下关于样本空间的假设将成立:
- 所有属性都具有相同的比例。拥有相同的尺度意味着所有的属性都使用相同的单位来测量,也就是说,用厘米和英尺来测量学生的身高是不相关的。
- 所有属性的范围必须相同。例如,所有属性的值必须从 0 到 100 变化,而不是从 0 到 1000。
限制
不平衡
作者图片
考虑样本空间中的一种情况,其中大约有 1000 个点被分类为 A 类或 b 类。假设 1000 个点中有 800 个点属于 A 类,这表明数据集高度不平衡。这会影响新测试样本的分类吗?是的,肯定的!!考虑我们想要在样本空间中找到点 X 的类标签。如果我们认为 k 值非常大,比如大约 150,那么不平衡的数据集将迫使点 X 落入 A 类,这可能导致错误分类。
异常值
作者图片
考虑上面的图像具有属于类别 1 的异常值。假设我们想要预测 1 类标记异常值和 2 类标记训练点之间的点。测试点可能属于类别 2 标签,但由于类别 1 异常值的存在,这些点可能会被错误地分类到类别 1 中。
因此,KNN 的局限性在于,由于不平衡或异常数据集的存在,它可能会对点进行错误分类。
如何选择 K 的最佳值?
k 的最佳值是提供最小误差和最大精度的值。我们集中于为 k 取一组随机的值,并且在训练和测试模型时验证我们获得最小错误率的 k 值。
编码和学习
我们现在来看看 Kaggle 著名的 Pima Indian 糖尿病数据集任务,以便更好地理解 KNN 分类。这里,在给定的数据集中,我们有各种独立的特征,例如葡萄糖、血压、皮肤厚度、胰岛素、身体质量指数等,这些特征决定了该人是否患有糖尿病的结果。
作者图片
因此,最初我们做一些探索性的分析,以发现独立特性之间的相互依赖性和相关性。
作者图片
拆分测试和训练数据
调用 KNN 分类器对 k=1 的数据进行训练和测试
使用混淆矩阵和分类报告检查准确性
作者图片
基于从 1 到 50 的 k 值对数据进行训练和测试。
检查并绘制从 1 到 50 的所有 k 值的误差率。
作者图片
k=13 时误差最小。训练和测试数据,同时检查准确性
作者图片
感谢您阅读文章!!
k 近邻(KNN)算法
妮娜·斯特雷尔在 Unsplash 拍摄的照片
使用简单的东西在泰坦尼克号比赛中获得高分
数据科学很热门,似乎每个人都在从事某种涉及最新艺术(SOTA)算法的项目。当然有充分的理由,因为在许多情况下,我们可以使用数据来给出非常合理的预测,几乎在任何领域。虽然最近有很多人关注 SOTA 算法,但更简单的方法有时会被遗忘。
想入门 Python?从这里开始!
最近,我玩了一个 k 近邻(KNN)算法,我惊讶于它的强大。这项技术本身也用于许多其他领域。例如,我用它来识别我在博士期间的一个研究项目的高速记录的连续帧中的相同粒子。粒子的坐标是已知的,我们在下一帧中寻找该位置周围最近的粒子。当然,当有多个粒子非常接近时,你就有麻烦了。为此,您可以利用来自多个帧的高阶信息,如速度或加速度向量。对于机器学习中的 KNN,我们通常没有时态数据,因此,我们只使用它的一阶,这是最简单的形式。
当我们想用 KNN 对新数据进行分类,即进行预测时,我们使用已知数据(或标记数据)作为一种查找表。我们选择与我们想要预测的新数据相似的数据,并从该选择中选择最突出的类。所以我们将一个未知的例子和一个已知的数据集进行比较。没有训练,没有层次,没有重量。唯一的参数是 k ,它指定了预测类时要考虑的邻居数量。例如,为了对一种水果进行分类,我们可以从数据集中选择五个最相似的例子。现在我们说,那五个被选择的例子中最突出的类可能也是我们想要预测的类。如果我们发现了三个苹果和两个梨,我们也会预测一个苹果。
现在我们来看另一个问题:我们如何从特征列表中选择最相似的例子。当我们有一个单一的特征时,例如高度,这将是非常容易的。我们简单地计算差异并选择最接近的 k 个匹配。但是当我们也有重量和宽度的时候该怎么办呢?我们必须量化每个特性的差异,并将结果聚合成一个值。幸运的是,有很多方法可以做到这一点。其中最常见的是欧几里德距离,可以看作是两点之间最短的直线。
欧几里德距离:毕达哥拉斯引入的魔法!(我自己的图解技巧)
欧几里德距离适用于许多特征(或维度),但是有一个缺点适用于一般的 KNN。特征必须是数字,为了计算距离,数字必须代表意义。高度特征具有与数字相关的含义。数字越大,物体越大。数字越小,物体越小。这使得距离,即两个高度之间的差异有意义,因为它是高度的差异。现在我们以颜色为特征。颜色不是数字(至少我们使用名称,而不是波长),因此,我们需要将其转换为分类单位来创建数字。现在,绿色的值用 0 表示,红色用 1 表示,以此类推。虽然这些数字表示对象附带的颜色,但值本身没有实际意义。如果我们有一个颜色值为 100 的对象和另一个颜色值为 50 的对象,除了颜色不同之外,这些数字没有任何意义。因此,这些数字之间的差异(或距离)对 KNN 来说是没有意义和没有用的。
虽然真正的分类变量是不可用的,但并不是所有东西都丢失了。例如,我们可以使用诸如 is_green 的二进制指示器来指示一个对象是否是绿色的。尽管如此,如果我确信这些特性会增加预测,我还是会使用它们。
创建算法
我们将创建一个 KNN 算法,并将其用于著名的泰坦尼克号数据集,预测谁在这场悲惨的灾难中幸存下来。因此,所有函数都将与该数据集有某种链接。要创建 KNN 预测算法,我们必须执行以下步骤:
1.计算未知点和已知数据集之间的距离。
2。从该数据集中选择 k 个最近邻。
3。做一个预测
简单的 GIF 展示了 KNN 是如何工作的
如上图所示,我们只需应用毕达哥拉斯来计算欧几里得距离:
接下来我们需要做一个循环来计算和比较数据集与我们想要预测的点的所有距离:
现在我们有了一个函数,它给出了 k 个最近邻的列表。最后一步是从列表中选择最突出的类,并将其用作预测。这很容易通过取平均值和四舍五入来解决。请随意检查这是不是真的。
准备数据集
在测试算法之前,我们需要准备数据。这是读入数据并以巧妙的方式填充缺失的值。我们不会使用登船和客舱功能,所以我们不必担心这些。有一个票价缺失,我们将使用该 Pclass 的中间值来填充。有相当多的年龄数据丢失。我们将使用标题对每个缺失的年龄做一个很好的猜测。
接下来,我们创建几个附加特征(特征)。我最近在 Kaggle 上看到的一个是家庭生存。它假设家庭成员互相帮助,如果你的家人幸存下来,你也很可能幸存下来。这个想法来自于徐顺江/血浓于水我认为这是相当惊人的。此外,我们将增加家庭规模,将性别更改为二元指标,缩放所有变量并将其拆分回训练/测试。
做预测
算法准备好了,数据准备好了,我们准备做一些预测!
这需要很长时间,因为我们的算法相当慢。如果都是正确的,你应该得到一个 83.5% 的准确度。我们唯一可以调整的参数是要考虑的邻居数量。我玩了一下,10 似乎给出了最好的结果。现在让我们制作一个 Kaggle 提交数据集!
把这个提交给 Kaggle 会得到 81.3% 的分数。略低于训练集,但仍然相当高。我们只用一个简单的 KNN 算法就做到了。
一些最后的想法
我们创造的算法强大而有效。唯一的缺点是我们的实现非常慢。对于每个向量,我们需要计算距离并与完整的数据集进行比较。Sci-kit learn 提供了更智能的实现,使用基于树的方法来最小化所需的计算量,并使其速度大大加快。
这里我们使用 KNN 作为分类器,但是,它的工作方式也非常类似于回归。对于回归,结果是连续的,因此,例如,在进行预测时对最近邻进行平均是有意义的。
我希望你和我一样开心。如有任何问题,欢迎通过 LinkedIn 联系我。
所有代码都可以在我的 Github 和 Kaggle 上获得。
我将非常感谢对我的 Kaggle 笔记本的任何支持。
k-最近邻
关于 KNN 你需要知道的。
罗伯特·卡茨基在 Unsplash 上的照片
“一个人因他交的朋友而出名。”
我必须说这是一个完美的开场白,用来展示 K 个最近的邻居。是的,这就是 KNN 背后的简单概念。它只是根据一个数据点最近的几个邻居对其进行分类。有多少邻居?这是我们的决定。
看起来你已经知道了很多关于这个简单模型的知识。让我们深入了解一下。
在继续之前,知道 KNN 可用于分类和回归问题是很重要的。我们将首先理解它是如何解决分类问题的,从而使可视化回归变得更容易。
KNN 分类器
我们将要使用的数据是 乳腺癌威斯康星(诊断)数据集 。有 30 个属性对应于为考虑中的细胞核计算的实值特征。该数据中共有 569 个样本,其中 357 个被归类为‘良性’(无害),其余 212 个被归类为*‘恶性’*(有害)。
诊断栏分别包含恶性和良性癌症的“M”或“B”值。为了更好的分析,我将这些值分别改为 1 和 0。
此外,为了这篇文章,我将只使用数据中的两个属性→“平均半径”和“平均纹理”。这将有助于我们理解 KNN 划定的决策界限。下面是最终数据的样子(洗牌后):
让我们给 KNN 编码:
# Defining X and y
X = data.drop('diagnosis',axis=1)
y = data.diagnosis# Splitting data into train and test
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.25,random_state=42)# Importing and fitting KNN classifier for k=3
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train,y_train)# Predicting results using Test data set
pred = knn.predict(X_test)
from sklearn.metrics import accuracy_score
accuracy_score(pred,y_test)
上面的代码应该会给你一个稍微不同的输出。
0.8601398601398601
刚刚发生了什么?当我们在训练数据上训练 KNN 时,它对每个数据样本采取以下步骤:
- 借助欧几里得方法计算数据样本和其他样本之间的距离。
- 按升序排列这些距离值。
- 从排序的距离中选择前 K 个值。
- 根据上述 K 值中最常见的类别将类别分配给样本。
让我们想象一下 KNN 是如何在训练数据集上画出一个决策边界的,以及这个边界是如何被用来对测试数据集进行分类的。
K=3 时的 KNN 分类。图片由 Sangeet Aggarwal
训练准确率为 93%,测试准确率为 86%,我们的模型在这里可能显示出过度拟合。为什么这样
当 K 值或邻域数太低时,模型仅选取最接近数据样本的值,从而形成如上所示的非常复杂的决策边界。这种模型无法在测试数据集上很好地概括,从而显示出较差的结果。
这个问题可以通过调整参数 n_neighbors 的值来解决。随着我们增加邻居的数量,模型开始很好地泛化,但是增加太多的值会再次降低性能。
因此,找到 K 的最佳值很重要,这样模型就能够在测试数据集上很好地分类。让我们观察训练,并随着邻居数量的增加测试精确度。
在 k=11 时观察到最佳结果
上面的结果可以通过下面的图得到最好的可视化。
图片由 Sangeet Aggarwal
该图显示了测试精度在某一点上的总体上升趋势,之后精度再次开始下降。这是最近邻的最佳数量,在本例中为 11,测试准确率为 90%。
让我们再次画出 k=11 的决策边界,看看它是什么样子。
K=11 时的 KNN 分类。图片由 Sangeet Aggarwal 提供
我们通过微调邻居的数量来改善结果。此外,KNN 的决策边界现在平滑得多,能够很好地概括测试数据。
现在让我们来理解 KNN 是如何用于回归的。
KNN 回归量
KNN 分类器返回最近 K 个邻居的模式,而 KNN 回归器返回最近 K 个邻居的平均值。
我们将用广告数据来理解 KNN 的回归。下面是电视预算和销量的前几排。
# Defining X and Y
X_ad = ad.TV.values.reshape(-1,1)
y_ad = ad.sales# Splitting data into train and test
train_x, test_x, train_y, test_y = train_test_split(X_ad, y_ad, test_size=0.25, random_state=42)# Running KNN for various values of n_neighbors and storing results
knn_r_acc = []for i in range(1,17,1):
knn = KNeighborsRegressor(n_neighbors=i)
knn.fit(train_x,train_y) test_score = knn.score(test_x,test_y)
train_score = knn.score(train_x,train_y) knn_r_acc.append((i, test_score ,train_score))df = pd.DataFrame(knn_r_acc, columns=['K','Test Score','Train Score'])
print(df)
上面的代码将针对不同的 K 值(从 1 到 16)运行 KNN,并将训练和测试分数存储在一个数据帧中。让我们看看当我们增加 n_neighbors(或 K) 的值时,这些分数是如何变化的。
K=4 时的最佳结果
在 K=1 时,KNN 倾向于紧密跟随训练数据,因此显示出高的训练分数。但相比较而言,测试分数相当低,从而说明过度拟合。
让我们想象一下 KNN 如何绘制不同 k 值的回归路径
**左:用 KNN 回归器训练数据集右:**用相同的 KNN 回归器测试数据集。图片由 Sangeet Aggarwal 提供
随着 K 的增加,KNN 将数据拟合为更平滑的曲线。这是因为 K 值越高,考虑的数据就越多,从而降低了模型的整体复杂性和灵活性。
正如我们之前看到的,增加 K 值会将分数提高到一定程度,之后分数会再次下降。这一点可以通过下面的情节更好的理解。
图片由 Sangeet Aggarwal 提供
正如我们在该图中看到的,该模型在 K=4 时产生最佳结果。我用 R 来评估这个模型,这是我们能得到的最好的结果。这是因为我们的数据集太小而且太分散。
关于 KNN,需要了解的其他几点很重要:
- KNN 分类器没有任何专门的训练阶段,因为它使用所有的训练样本进行分类,并简单地将结果存储在内存中。
- KNN 是一种非参数算法,因为它没有对训练数据做任何假设。这对于具有非线性数据的问题非常有用。
- 如果数据非常大,KNN 在时间和存储方面的计算都很昂贵,因为 KNN 必须存储训练数据才能工作。其他监督学习模型一般不会出现这种情况。
- KNN 对数据的规模非常敏感,因为它依赖于计算距离。对于比例较高的要素,计算出的距离可能会很大,结果可能会很差。因此,建议在运行 KNN 之前缩放数据。
这个帖子到此为止。我希望你在学习 KNN 的时候过得愉快。欲知详情,敬请关注。
举例说明 Python 中的 k-最近邻算法
通过代码示例介绍 K-最近邻
图片由李可莹通过 Unsplash 提供
k-最近邻(KNN)
KNN 是一种受监督的机器学习算法,可用于解决分类和回归问题。KNN 的原理是一个数据点的值或类别由该值周围的数据点决定。
要理解 KNN 分类算法,最好通过例子来说明。本教程将演示如何在 Python 中使用 KNN 来解决自己的分类问题。与这个例子相对应的 Jupyter 笔记本可以在这里找到,如果你想跟随的话。
预测算法计算从未知点 x 到数据中所有点的距离。然后,数据中的点按离 x 的距离递增进行排序。通过从“K”个最近点预测多数标注来进行预测。
选择一个 K 会影响一个新点将被分配到什么类。
在下面的示例中,选择 K 值 2 会将未知点(黑色圆圈)分配给类别 2。但是,如果 K 值为 7,未知点将被分配到类别 1。
作者个人制作,斯蒂芬·福特汉姆
创建假数据集
首先,我们导入我们需要的库,然后使用 sklearn 的 makeblobs 函数创建一个假数据集。我们可以传入样本数量、数据集中的特征、数据将落入多少个中心或类中,以及最终这些聚类的标准偏差。为了这个 Jupyter 笔记本的多次运行之间的一致性,我将整数 101 赋给了 random_state 参数。
注意,首先,我们将有一个大的集群标准偏差。这将在分类中引入方差,我们可以通过专门选择最佳 K 值来对其进行改进。这可以通过肘法来实现。
sklearn 的 makeblobs 函数返回一个 2 元素元组。我们可以使用 pd 创建我们特征的数据框架。DataFrame,并传入对应于特征数据的第一个元组的索引。数据元组的第二个元素对应于特征的标签。
我们现在可以通过从 Sklearn.preprocessing 导入 MinMaxScaler 来缩放数据。与其他机器学习算法不同,在执行我们的训练测试分割之前,我们在上拟合和转换所有训练数据。
预测算法和优化
为了在代码中实现预测,我们首先从 sklearn.neighbors 导入 KNeighborsClassifier,然后实例化 KNeighborsClassifier 的一个实例,将参数 1 传递给 n_neighbors,并将其赋给变量 knn。传递给 n_neighbors 的值表示 K 值。
然后,在进行预测之前,我们通过调用 KNeighborsClassifier 对象上的 predict 方法来拟合定型数据。
现在我们可以使用分类报告和混淆矩阵来评估预测的准确性。
这些指标表明准确性已经非常好了。这可能是因为我们用 makeblobs 制作了数据集,并特别要求了 2 个中心。然而,我们确实故意为集群标准偏差设置了一个大值,以引入方差。这导致了我们数据集中 4 个点的错误分类。
提高数据准确性
我们可以尝试通过修改邻居的数量来提高结果的准确性。这可以通过肘法来实现。
我们首先遍历 40 个邻居值,用这个数量的邻居实例化一个 KNeighborsClassifier 对象。然后,我们可以将训练数据拟合到此 KNN 模型,获得预测,并追加预测 pred_i 和正确值 y_test 之间的平均值。
如果 pred_i 和 y_test 在数组中不匹配,则返回值为 1 的真值。这个数字越高,分类就越不准确。
错误率的较低值将对应于性能更好的模型。
这些结果可以用 x 轴上的 I 值范围对 y 轴上的误差率来绘制。
现在我们可以选择返回的 K 的最低值,最低的错误率。在这里,我们可以选择 5。
现在,我们可以再次使用混淆矩阵和分类报告重新运行准确性评估,以查看我们是否能够更准确地对 4 个未对齐点进行分类。我们有所改进,从 4 个错误分类点减少到 2 个。
针对新数据点的培训
我们现在可以使用原始数据创建一个数据点。首先,我们创建两个数据帧;一个包含要素,一个包含标注,将它们连接成单个数据帧,并选择第一行作为预测标注的数据点。我们必须记住缩放数据点,因为模型是根据缩放后的数据训练的。
预测表明,数据点 1 将给出标签 0,这与原始数据集点相匹配,通过调用 df.head(1)进行验证。
摘要
KNN 很简单,训练也很简单,而且很容易添加更多的数据。此外,参数很少。在本教程中,我只在 KNeighborsClassifier 类的调用中包含了 K 参数(n_neighbors)。
然而,KNNs 也有缺点,包括高预测成本,这对于大型数据集来说更糟。KNN 对异常值也很敏感,因为异常值对最近的点有影响。此外,它们不适用于高维数据集,分类特征也不太适用。随着数据越来越多,KNN 算法变得越来越慢,因为模型需要存储所有这些数据点,以便确定它们之间的距离。
这个例子是人为设计的,主要是为了理解如何用 Python 编写 KNN。我特意使用 makeblobs 制作了数据集,以说明这个函数作为练习 KNNs 的工具是多么有用。如果你想得到我所有的 Jupyter 笔记本,可以在这里找到。
尝试将聚类标准差更改为更高的值,然后尝试使用错误率最低的 K 值来优化 KNN 分类。
此外,KNNs 可以与一起使用来对多个类进行分类。通过增加传递给中心的值,可以在 makeblobs 函数调用中进一步改变这一点。这些参数可以很容易地调整,将有助于理解 KNNs,所以我们可以有效地将它们纳入我们的分析。
快乐分类。
k 手动最近邻:
打开“黑匣子”并理解其中的算法
数据科学家有时谈论数据科学的“黑盒”方法;也就是说,当你理解了不同机器学习算法的用例,以及如何在不理解算法在表面下如何工作的情况下插入数据。但是算法仅仅是——它们是“算法”——一组用于解决特定类型问题的指令。
在这篇博客文章中,我将向您展示如何手工构建一个简单的机器学习算法。我们将要构建的模型是 knarestneighborsclassifier(KNN classifier)。
什么是 KNNClassifier,它是如何工作的?
KNNClassifier 是我用过的第一个机器学习算法。它很简单,但是非常强大。该算法被认为是“懒惰”的,因为它本身并不学习;该算法只是记忆数据,而不是在训练过程中学习数据中的模式,并根据这些模式对新数据进行预测。然后,该模型将数据与其自身相关联,并找到每个数据点之间的“欧几里德距离”(本质上是直线距离)。当添加新数据时,算法会根据我们指定的“最近邻”数据点的数量来预测该数据的目标。
举个简单的例子,假设一个数据集有两三个特征。将这些数据映射到 2D 或 3D 图形上,并找到每个点之间的距离非常简单——只要沿着轴,在图形上找到适当的点时添加数据点。然后,当我们向模型中输入新数据时,该算法会找到与新数据最近的 k 个数据点,并预测该目标是邻居中最常出现的目标。
如果这仍然令人困惑,不要害怕!一旦我们将它应用到真实的数据集,它将变得有意义。
泰坦尼克号数据集
如果您想了解这个示例,只需在这里 下载数据 ,并将 csv 文件添加到与 Python 笔记本相同的文件夹中。(参见源代码 此处 )
由于 KNNClassifier 是一个分类模型,我们需要一个数据集来预测一些离散的目标(与回归模型相反,回归模型预测连续统上的值)。在这个练习中,我们将使用可能是学习分类模型最流行的数据集 Titanic 数据集。我们试图预测的是,根据我们对一个人的一些特征的了解,这个人是否能在泰坦尼克号沉没后幸存下来。
下面是我们需要的导入和加载数据的代码:
如果我们接着运行df.head()
,我们可以观察前 5 行,并看到我们正在处理的特性:
如果您不熟悉数据集,请花点时间尝试并预测哪些特征与存活率最相关。
预处理和列车测试分割
survived
列——最左边的列——是我们试图预测的(也称为 y 变量或“目标”)。其余的特征是我们用来预测目标的。我们现在需要做一些预处理工作,以最好地建立我们的模型来预测。由于这篇文章的重点是机器学习算法,我们将尽可能简单地进行预处理。
我们将首先删除“姓名”和“费用”列,因为“姓名”并不能决定某人是否幸存,而“费用”与“等级”密切相关。然后我们将使用pd.get_dummies()
将“性别”列一次性编码成两列,男性和女性。代码如下所示:
现在,我们的数据已经处理完毕,我们可以执行训练测试分割。这是监督机器学习模型的标准——其思想是,我们在训练数据上训练算法,并通过在我们的测试数据上评估它来测试模型的性能。幸运的是,scikit-learn 库有一个内置模块可以轻松地执行分割:
带 Scikit 的 KNN classifier-Learn
首先让我们看看如何使用 scikit-learn 来编码这个问题。由于所有的预处理和训练-测试-拆分都已完成,因此根据数据训练模型并做出预测只需三行代码。我们将首先制作一个模型对象,然后使用.fit
方法根据数据训练模型,使用.predict
方法根据模型从训练数据中学到的知识对测试集中的每个值进行预测:
输出显示了测试数据中表示的每个人的生存状态预测。“1”表示模型预测此人幸存,“0”表示模型预测此人未能幸存:
手动 KNN 算法
既然我们已经使用 sklearn 库对测试集进行了预测,并且对幕后发生的事情有了基本的了解,那么让我们构建自己的 KNNClassifier 自制版本。我们将使用面向对象的编程来构建模型。(如果您对类和 OOP 不熟悉,请务必关注我的一系列博客文章,在这些文章中,我们将学习 OOP 并使用类来构建一个基于文本的冒险游戏!)
我们首先需要的是辅助函数。这些函数存在于类的外部,我们在类内部调用它们来执行一些操作。下面是一个自制的平方根函数和欧几里德距离公式:
如上所述,欧几里德距离本质上是两条数据之间的直线。该函数只是简单地将两行之间的每一列的值之间的距离相加,然后求出该和的平方根。
接下来我们将编写 KNNHomebrew 函数。该类中的方法模拟了 scikit-learn knnclassifier 模型完成的操作。我们有一个model_fit()
方法,一个model_predict
方法,它对一个新的数据点进行预测,还有一个model_predict_all
方法,它对一组新的数据进行预测。文本注释中解释了每行代码的具体功能。
现在是关键时刻了!让我们看看是否可以像调用 scikit-learn 模型一样调用我们的模型:
输出应该如下所示:
如果你继续下去,你会注意到 hombrew 模型比 scikit-learn 模型慢得多——可怜可怜我吧,我只是个初级开发人员。我可能会写一篇后续的博客文章,在那里我优化了算法,并使时间低于 1 秒,但现在,让我们看看我们的模型与 scikit-learn 模型的性能相比如何。
比较型号
现在是时候对比一下车型的性能了。首先,我们需要找到基线。在运行任何机器学习模型之前,这是最佳实践。对于分类问题,问题是,如果我们猜测所有新数据的主要类别,我们猜对的频率是多少?
这是一个非常简单的计算,我们需要做的就是将训练集中多数类的出现次数除以数据集中的观察总数。多数类为 0 或“未存活”。假设我们的训练数据是一个真正的随机样本,在我们进行任何机器学习或预测分析之前,我们应该用任何新数据来预测这个类。由于我们的准确率是 61.24%,我们可以假设这个天真的预测有 61.24%是正确的。
现在让我们看看我们的模型和 scikit-learn 模型是如何比较的。下面是查找 Scikit-Learn 模型和我们的 KNNHomebrew 模型的准确性的代码:
- Scikit-learn:
model.score(X_test, y_test)
- KNNHomebrew:
knnHB.model_accuracy(X_test, y_test)
结果是:
- 科学知识-学习:75.84%
- KNNHomebrew: 72.47%
因此,在其当前的形式下,似乎 scikit-learn 模型将比我们的 KNNHomebrew 模型多正确预测新数据的生存大约 3.5%…失败:(
开玩笑!这个练习的目的不是要打败 Python 中最流行的机器学习库之一,而是要了解其背后的东西。
敬请关注。
揭示机器学习算法的所谓“黑盒”中的魔力是有趣且极具教育意义的。我计划在其他预测模型上发表更多这样的博文,所以请保持警惕!
k-最近邻分类从零开始
没有第三方库的 R 中的分步示例
这篇文章旨在探索一步一步的方法来创建一个K-最近邻算法而不需要任何第三方库的帮助。在实践中,这种算法应该足够有用,每当我们已经进行了分类(在这种情况下,颜色)时,我们就可以对我们的数据进行分类,这将作为查找邻居的起点。
在这篇文章中,我们将使用一个特定的数据集,它可以在这里下载。它包含 539 个二维数据点,每个数据点都有特定的颜色分类。我们的目标是将他们分成两组(训练和测试),并根据我们的算法建议尝试猜测我们的测试样本颜色。
训练和测试样本生成
我们将创建两个不同的样品组:
- **训练集:**这将包含我们 75%的工作数据,随机选择。这个集合将用于生成我们的模型。
- **测试集:**我们剩余的 25%工作数据将用于测试我们模型的样本外准确性。一旦我们做出了 25%的预测,我们将通过比较预测值和实际值来检查“正确分类的百分比”。
*# Load Data*
library(readr)
RGB <- as.data.frame(read_csv("RGB.csv"))
RGB$x <- **as.numeric**(RGB$x)
RGB$y <- **as.numeric**(RGB$y)
print("Working data ready")*# Training Dataset*
smp_siz = **floor**(0.75*nrow(RGB))
train_ind = sample(**seq_len**(nrow(RGB)),size = smp_siz)
train =RGB[train_ind,]*# Testting Dataset*
test=RGB[-train_ind,]
OriginalTest <- test
paste("Training and test sets done")
培训用数据
我们可以看到,我们的训练数据根据颜色分为 3 类。
*# We plot test colored datapoints*
library(ggplot2)
colsdot <- **c**("Blue" = "blue", "Red" = "darkred", "Green" = "darkgreen")
ggplot() +
geom_tile(data=train,mapping=aes(x, y), alpha=0) +
*##Ad tiles according to probabilities*
*##add points*
geom_point(data=train,mapping=aes(x,y, colour=Class),size=3 ) +
scale_color_manual(values=colsdot) +
*#add the labels to the plots*
xlab('X') + ylab('Y') + ggtitle('Train Data')+
*#remove grey border from the tile*
scale_x_continuous(expand=**c**(0,.05))+scale_y_continuous(expand=**c**(0,.05))
训练数据:我们可以观察 3 个类别(蓝色、绿色和红色)
测试数据
即使我们知道测试数据的原始颜色分类,我们也将尝试创建一个模型,该模型可以仅根据有根据的猜测来猜测它的颜色。为此,我们将删除它们的原始颜色,保存它们只是为了测试的目的;一旦我们的模型做出预测,我们将能够通过比较原始预测和我们的预测来计算我们的模型精度。
*# We plot test colored datapoints*
colsdot <- **c**("Blue" = "blue", "Red" = "darkred", "Green" = "darkgreen")
ggplot() +
geom_tile(data=test,mapping=aes(x, y), alpha=0) +
*##Ad tiles according to probabilities*
*##add points*
geom_point(data=test,mapping=aes(x,y),size=3 ) +
scale_color_manual(values=colsdot) +
*#add the labels to the plots*
xlab('X') + ylab('Y') + ggtitle('Test Data')+
*#remove grey border from the tile*
scale_x_continuous(expand=**c**(0,.05))+scale_y_continuous(expand=**c**(0,.05))
测试数据:我们删除并故意忘记了它的分类颜色,以创建一个能够猜测它们的模型。
k-最近邻算法
下面是实现该算法的分步示例。我们想要实现的是,对于上面每个选定的灰点(我们的测试值),据称我们不知道它们的实际颜色,从我们的训练值中找到最近的邻居或最近的彩色数据点,并分配与此相同的颜色。
具体来说,我们需要:
- **归一化数据:**即使在这种情况下不需要,因为所有值都是相同的标度(0 到 1 之间的小数),建议归一化,以便有一个“标准距离度量”。
- **定义我们如何度量距离:**我们可以将这个二维数据集中两点之间的距离定义为它们之间的欧氏距离。我们将计算 L1(绝对差之和)和 L2(平方差之和)距离,尽管最终结果将使用 L2 计算,因为它比 L1 更不宽容。
- **计算距离:**我们需要计算每个测试数据点和训练数据集中每个值之间的距离。标准化在这里是至关重要的,因为在身体结构的情况下,体重(1 公斤)和身高(1 米)的距离是不可比的。我们可以预见到公斤比米的偏差更大,导致总距离不正确。
- **排序距离:**一旦我们计算出每个测试点和训练点之间的距离,我们就需要按降序对它们进行排序。
- **选择前 K 个最近的邻居:**我们将选择前 K 个最近的训练数据点,以检查它们属于哪个类别(颜色),以便将该类别分配给我们的测试点。由于我们可能使用多个邻居,我们可能会有多个类别,在这种情况下,我们应该计算一个概率。
*# We define a function for prediction*
KnnL2Prediction <- function(x,y,K) {
*# Train data*
Train <- train
*# This matrix will contain all X,Y values that we want test.*
Test <- data.frame(X=x,Y=y)
*# Data normalization*
Test$X <- (Test$X - **min**(Train$x))/(**min**(Train$x) - **max**(Train$x))
Test$Y <- (Test$Y - **min**(Train$y))/(**min**(Train$y) - **max**(Train$y))
Train$x <- (Train$x - **min**(Train$x))/(**min**(Train$x) - **max**(Train$x))
Train$y <- (Train$y - **min**(Train$y))/(**min**(Train$y) - **max**(Train$y)) *# We will calculate L1 and L2 distances between Test and Train values.*
VarNum <- ncol(Train)-1
L1 <- 0
L2 <- 0
for (i in 1:VarNum) {
L1 <- L1 + (Train[,i] - Test[,i])
L2 <- L2 + (Train[,i] - Test[,i])^2
}
*# We will use L2 Distance*
L2 <- **sqrt**(L2)
*# We add labels to distances and sort*
Result <- data.frame(Label=Train$Class,L1=L1,L2=L2)
*# We sort data based on score*
ResultL1 <-Result[order(Result$L1),]
ResultL2 <-Result[order(Result$L2),]
*# Return Table of Possible classifications*
a <- prop.table(table(head(ResultL2$Label,K)))
b <- as.data.frame(a)
**return**(**as.character**(b$Var1[b$Freq == **max**(b$Freq)]))
}
使用交叉验证找到正确的 K 参数
为此,我们将使用一种叫做“交叉验证”的方法。这意味着,我们将在训练数据本身中进行预测,并针对数据的许多不同折叠或排列,对许多不同的 K 值进行迭代。一旦我们完成,我们将平均我们的结果,并为我们的“K-最近邻”算法获得最佳 K。
*# We will use 5 folds*
FoldSize = **floor**(0.2*nrow(train)) *# Fold1*
piece1 = sample(**seq_len**(nrow(train)),size = FoldSize )
Fold1 = train[piece1,]
rest = train[-piece1,] *# Fold2*
piece2 = sample(**seq_len**(nrow(rest)),size = FoldSize)
Fold2 = rest[piece2,]
rest = rest[-piece2,] *# Fold3*
piece3 = sample(**seq_len**(nrow(rest)),size = FoldSize)
Fold3 = rest[piece3,]
rest = rest[-piece3,] *# Fold4*
piece4 = sample(**seq_len**(nrow(rest)),size = FoldSize)
Fold4 = rest[piece4,]
rest = rest[-piece4,] *# Fold5*
Fold5 <- rest*# We make folds*
Split1_Test <- rbind(Fold1,Fold2,Fold3,Fold4)
Split1_Train <- Fold5Split2_Test <- rbind(Fold1,Fold2,Fold3,Fold5)
Split2_Train <- Fold4Split3_Test <- rbind(Fold1,Fold2,Fold4,Fold5)
Split3_Train <- Fold3Split4_Test <- rbind(Fold1,Fold3,Fold4,Fold5)
Split4_Train <- Fold2Split5_Test <- rbind(Fold2,Fold3,Fold4,Fold5)
Split5_Train <- Fold1*# We select best K*
OptimumK <- data.frame(K=**NA**,Accuracy=**NA**,Fold=**NA**)
results <- trainfor (i in 1:5) {
if(i == 1) {
train <- Split1_Train
test <- Split1_Test
} else if(i == 2) {
train <- Split2_Train
test <- Split2_Test
} else if(i == 3) {
train <- Split3_Train
test <- Split3_Test
} else if(i == 4) {
train <- Split4_Train
test <- Split4_Test
} else if(i == 5) {
train <- Split5_Train
test <- Split5_Test
}
for(j in 1:20) {
results$Prediction <- mapply(KnnL2Prediction, results$x, results$y,j)
*# We calculate accuracy*
results$Match <- ifelse(results$Class == results$Prediction, 1, 0)
Accuracy <- **round**(**sum**(results$Match)/nrow(results),4)
OptimumK <- rbind(OptimumK,data.frame(K=j,Accuracy=Accuracy,Fold=paste("Fold",i)))
}
}OptimumK <- OptimumK [-1,]
MeanK <- aggregate(Accuracy ~ K, OptimumK, mean)
ggplot() +
geom_point(data=OptimumK,mapping=aes(K,Accuracy, colour=Fold),size=3 ) +
geom_line(aes(K, Accuracy, colour="Moving Average"), linetype="twodash", MeanK) +
scale_x_continuous(breaks=seq(1, **max**(OptimumK$K), 1))
20 个不同 K 值的 5 倍
如上图所示,我们可以观察到,对于所有褶皱,我们算法的预测精度在 88%-95%的范围内,并且从 K=3 开始下降。我们可以在 K=1 (3 也是一个很好的选择)上观察到最高的一致精度结果。
基于前 1 个最近邻进行预测。
模型精度
*# Predictions over our Test sample*
test <- OriginalTest
K <- 1
test$Prediction <- mapply(KnnL2Prediction, test$x, test$y,K)
head(test,10)*# We calculate accuracy*
test$Match <- ifelse(test$Class == test$Prediction, 1, 0)
Accuracy <- **round**(**sum**(test$Match)/nrow(test),4)
print(paste("Accuracy of ",Accuracy*100,"%",sep=""))
使用 K=1 的前 10 个预测
从上面的结果可以看出,我们有望在 93%的时间里“猜出正确的类别或颜色”。
原始颜色
下面我们可以观察测试样本的原始颜色或类别。
ggplot() +
geom_tile(data=test,mapping=aes(x, y), alpha=0) +
geom_point(data=test,mapping=aes(x,y,colour=Class),size=3 ) +
scale_color_manual(values=colsdot) +
xlab('X') + ylab('Y') + ggtitle('Test Data')+
scale_x_continuous(expand=**c**(0,.05))+scale_y_continuous(expand=**c**(0,.05))
这是我们测试样品的原始颜色/等级
预测颜色
使用我们的算法,我们为最初无色的样本数据集获得以下颜色。
ggplot() +
geom_tile(data=test,mapping=aes(x, y), alpha=0) +
geom_point(data=test,mapping=aes(x,y,colour=Prediction),size=3 ) +
scale_color_manual(values=colsdot) +
xlab('X') + ylab('Y') + ggtitle('Test Data')+
scale_x_continuous(expand=**c**(0,.05))+scale_y_continuous(expand=**c**(0,.05))
在红圈里,我们标出了不同或不正确的分类。
从上面的图中可以看出,尽管我们的算法对大多数数据点进行了正确的分类,但其中一些数据点还是失败了(用红色标记)。
决策限制
最后,我们可以可视化我们在原始测试数据集上的“决策限制”。这为我们的模型如何对数据进行分类以及其分类空间的限制提供了一个极好的可视化近似值。
简而言之,我们将在原始数据集的范围内模拟 160,000 个数据点(400x400 矩阵),当稍后绘制时,将使用颜色填充大部分空白空间。这将帮助我们详细地表达我们的模型将如何在其学习的颜色类别中分类这个 2D 空间。我们生成的点数越多,我们的“分辨率”就越好,就像电视上的像素一样。
*# We calculate background colors*
x_coord = seq(**min**(train[,1]) - 0.02,**max**(train[,1]) + 0.02,length.out = 40)
y_coord = seq(**min**(train[,2]) - 0.02,**max**(train[,2]) + 0.02, length.out = 40)
coord = expand.grid(x = x_coord, y = y_coord)
coord[['prob']] = mapply(KnnL2Prediction, coord$x, coord$y,K)*# We calculate predictions and plot decition area*
colsdot <- **c**("Blue" = "blue", "Red" = "darkred", "Green" = "darkgreen")
colsfill <- **c**("Blue" = "#aaaaff", "Red" = "#ffaaaa", "Green" = "#aaffaa")
ggplot() +
geom_tile(data=coord,mapping=aes(x, y, fill=prob), alpha=0.8) +
geom_point(data=test,mapping=aes(x,y, colour=Class),size=3 ) +
scale_color_manual(values=colsdot) +
scale_fill_manual(values=colsfill) +
xlab('X') + ylab('Y') + ggtitle('Decision Limits')+
scale_x_continuous(expand=**c**(0,0))+scale_y_continuous(expand=**c**(0,0))
如上所述,彩色区域代表我们的算法将定义为“彩色数据点”的区域。显而易见,为什么它未能对其中一些进行正确分类。
最后的想法
k-最近邻是一种简单的算法,似乎可以提供很好的结果。尽管在这里我们可以用肉眼对物品进行分类,但是这个模型也适用于我们不能仅仅用肉眼观察的高维情况。为了实现这一点,我们需要一个具有现有分类的训练数据集,我们稍后将使用它来对周围的数据进行分类,这意味着它是一个由 T2 监督的机器学习算法。
遗憾的是,这种方法在一些情况下存在困难,例如在存在无法用简单的直线距离表示的复杂模式的情况下,比如在放射状或嵌套式集群的情况下。它还存在性能问题,因为对于新数据点的每个分类,我们需要将其与训练数据集中的每个点进行比较,这是资源和时间密集型的,因为它需要复制和迭代整个集合。
用 NumPy 从零开始进行 k-最近邻分类
妮娜·斯特雷尔在 Unsplash 拍摄的照片
W 欢迎来到另一个用 NumPy 从零开始实现机器学习算法的帖子。在这篇文章中,我将实现 K 近邻(KNN),这是一种机器学习算法,可用于分类和回归的目的。它属于监督学习算法的范畴,为看不见的观察预测目标值。换句话说,它对带标签的数据集进行操作,并预测测试数据的类(分类)或数值(回归)。
正如我在其他帖子中提到的,我不会使用已经实现的 k-nearest neighborhood 算法,我将只使用 NumPy 实现它,并将使用其他库进行数据可视化,并创建和使用数据集。
考虑到有很多有用的资源可以解释 KNN 的基本原理,我想马上进入编码部分。我们继续吧!
我们从导入将在整个实现中使用的库和函数开始:
- numpy :显然,它将用于多维数组的数值计算,因为我们大量处理高维向量。
- make _ class ification:我们将使用它来创建我们自己的分类数据集。我们可以决定需要多少个类和特征,是否需要对样本进行聚类,等等。
在 KNN 算法中,我们需要一个函数来计算训练数据点和我们想要分类的数据之间的距离。这里,我选择了欧几里德距离,因为它是机器学习应用中广泛使用的距离。可以尝试使用其他距离度量,如曼哈顿距离、切比雪夫距离等。
当我们考虑 KNN 的实际情况时,我们会尝试找到邻居,即最接近我们想要分类的数据的数据点。数据点是否接近是由我们上面实现的欧几里德距离函数决定的。这里,数据点之间距离的实际值并不重要,相反,我们感兴趣的是这些距离的顺序。
在训练过程之前,我们挑选一个数字作为我们的超参数(k ),并挑选 k 个与我们想要在训练期间分类的数据点最近的邻居。因此,例如,当我们说 5 个最近的邻居时,我们指的是前 5 个最近的数据点。
在上面的 kneighbors 函数中,我们找到了测试数据集中每个点(我们要分类的数据点)与数据集其余部分(即训练数据)之间的距离。我们将这些距离存储在 point_dist 中,其中每一行对应于一个测试数据点和所有训练数据之间的距离列表。因此,我们检查每一行,对其进行计数,然后根据距离进行排序。我们枚举每一行的原因是因为我们不想丢失用来计算距离的训练数据点的索引,因为我们稍后将引用它们。
因此, sorted_neigh 保存了我们的测试数据点的前 k 个最近邻,并且根据它们的欧几里德距离对它们进行排序。然后,我们从 sorted_neigh 中提取索引和距离值,并返回它们。
在找到 k-最近邻后,我们尝试预测我们的测试数据点所属的类。这里,我们有 k 个邻居,每个邻居都有一票来决定类别标签。然而,投票机制可以根据所选择的标准而变化。这里,在上面的预测函数中,如果权重被选择为统一,这意味着每个邻居在决定类标签时具有相等的投票(权重),而与它们的距离无关。
假设我们的测试数据点有 5 个最近邻,其中 3 个属于 A 类,2 个属于 b 类。我们不考虑邻居的距离,并得出测试数据点属于 A 类的结论,因为大多数邻居都属于 A 类。但是,如果选择权重作为距离,那么这意味着邻居的距离确实很重要。因此,最接近测试数据点的邻居具有与其距离的倒数成比例的最大权重(投票)。因此,关于前述示例,如果属于类别 A 的那 2 个点比其他 3 个点更接近测试数据点,则仅这一事实就可以在决定数据点的类别标签中起很大作用。
在预测功能中,如果权重是均匀的,那么预测数据点的标签是相当容易的。首先,我们获得邻居的索引,然后使用这些索引从训练数据集中获得它们对应的类别标签。 neighbors 中的每一行对应于每个测试数据点所具有的一组邻居。然后,我们使用 nump y 的 bincount 函数找到类标签的出现,并获得对应于预测的类标签的最大出现的索引。
如果我们选择权重作为距离,事情会变得有点混乱。在这种情况下,我们找到相邻距离的平均倒数,并计算每个测试数据点的分类概率。
我实现了 score 函数,作为一个在分类问题中广泛使用的非常简单的准确性度量。我们只是返回正确分类标签的百分比。这很简单!
现在,是时候创建数据集了,我们将在其上测试我们的 knn 算法。我们利用 sklearn.dataset 的 make_classification 函数来填充数据集。之后,我们通过减去平均值然后除以标准偏差来标准化每个数据点。
在创建数据集之后,我们为我们的训练阶段实现随机分割,从而获得我们的训练和测试集。
In[104]: kneighbors(X_test)Out[104]: array([[ 85, 405, 370, 63, 694],
[345, 64, 189, 136, 32],
[554, 216, 690, 672, 51],
...,
[560, 323, 99, 418, 295],
[214, 180, 534, 190, 133],
[ 20, 129, 564, 103, 679]])
是时候测试我们的 knn 实现代码了。我们得到了测试数据集的 k 个最近邻。请注意,每一行都与测试集中的每个数据点相关,并且每一行中的元素都对应于测试数据点的邻居的索引。
In[105]: predict(X_test)Out[105]: array([0, 1, 2, 1, 0, 2, 1, 1, 0, 0, 2, 1, 1, 0, 0, 1, 0, 0, 0, 1, 2, 2, ..., 2, 1, 0, 2, 1, 2, 1, 0, 0, 1, 1, 0, 0, 2, 0, 2, 1, 2, 0, 1, 2, 0])
如预期的那样,预测函数输出测试数据的预测类标签。
In [106]: score(X_test, y_test)Out[106]: 0.99
看起来我们的实现做得非常好,给出了很高的准确度分数。
K-最近邻的类实现
在实现了所有必要的函数之后,创建 knn 的类实现就相当容易了。这里唯一新增加的函数是我们类的 init 函数。
将我们的实现与 Sklearn 的 KNeighborsClassifier 进行比较
Out[110]:
Our Implementation Sklearn's Implementation Accuracy 0.955556 0.955556
原来我们自己实现的精度和 sklearn 的实现看起来是一样的。这是好消息,对吗?我们在实现方面做得相当不错。
你也可以查看我的 GitHub 简介来沿着 jupyter 笔记本阅读代码或者简单地使用代码来实现。
将来,我一定会带来更多的实现。
编码快乐!