TowardsDataScience-博客中文翻译-2020-二十八-

龙哥盟 / 2024-10-14 / 原文

TowardsDataScience 博客中文翻译 2020(二十八)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

基于 KNN 和 SMOTE 的酒店取消分类

原文:https://towardsdatascience.com/classification-of-hotel-cancellations-using-knn-and-smote-3290cc87e74d?source=collection_archive---------40-----------------------

KNN(K-最*邻)是一种用于分类目的的常用方法。

在此特定示例中,KNN 算法用于根据取消风险对酒店预订进行分类(1 =模型预测客户将取消预订,0 =预测客户不会取消预订)。

假设该数据集是不*衡的,即 0(未消除)比 1(消除)多,则使用合成少数过采样技术(SMOTE)来*衡类,以便应用 KNN 算法。

SMOTE 过采样技术

鉴于数据集的不均匀性,有必要对次要类进行过采样(1 =取消),以确保 KNN 结果不会偏向主要类。

这可以通过 SMOTE 过采样技术来实现。

使用 MinMaxScaler 导入和缩放数据后,可以从 imblearn 库中导入 SMOTE。计数器是为了汇总类分布而导入的。

import imblearn
print(imblearn.__version__)
from imblearn.over_sampling import SMOTE
from collections import Counter

首先,调用训练-测试分割将数据分成训练和验证数据。

x1_train, x1_val, y1_train, y1_val = train_test_split(x_scaled, y1, random_state=0)

原班级分布由 0 : 21672, 1 : 8373 组成。

>>> counter = Counter(y_train)
>>> print(counter)Counter({0: 21672, 1: 8373})

然而,在应用 SMOTE 过采样技术之后,我们现在看到每一类中的观察值的数量是相等的。

>>> oversample = SMOTE()
>>> x_train, y_train = oversample.fit_resample(x_train, y_train)
>>> counter = Counter(y_train)
>>> print(counter)Counter({1: 21672, 0: 21672})

模型性能

该模型配置如下:

# KNN
knn = KNeighborsClassifier(n_neighbors=10)
model=knn.fit(x1_train, y1_train)
pred = model.predict(x1_val)
pred
print("Training set score: {:.2f}".format(knn.score(x1_train, y1_train)))
print("Validation set score: {:.2f}".format(knn.score(x1_val, y1_val)))# KNN Plot
mglearn.plots.plot_knn_classification(n_neighbors=10)
plt.show()

生成训练和测试集分数:

Training set score: 0.88
Validation set score: 0.46

以下是 KNN 模型所示的训练课程与测试预测的对比图:

来源:Jupyter 笔记本

以下是根据混淆矩阵对模型性能进行的细分:

[[2286 4980]
 [ 440 2309]]
              precision    recall  f1-score   support 0       0.84      0.31      0.46      7266
           1       0.32      0.84      0.46      2749 accuracy                           0.46     10015
   macro avg       0.58      0.58      0.46     10015
weighted avg       0.70      0.46      0.46     10015

虽然总体准确率很低,只有 46%,但根据 f1 评分,召回率相当不错,为 84%。

精确度与召回率

当处理分类数据时,还必须注意精度与召回读数,而不是简单的整体精度。

- Precision = ((True Positive)/(True Positive + False Positive))- Recall = ((True Positive)/(True Positive + False Negative))

这两个读数经常相互矛盾,也就是说,通常不可能在不降低召回率的情况下提高精确度,反之亦然。

对理想指标的评估很大程度上取决于所分析的具体数据。例如,癌症检测筛查出现假阴性(即表明患者没有患癌症,而事实上他们患有癌症)是一大禁忌。在这种情况下,召回是理想的衡量标准。

然而,对于电子邮件,人们可能更喜欢避免误报,例如,将一封重要的电子邮件发送到垃圾邮件文件夹,而实际上它是合法的。

f1 分数在设计一个更通用的分数时考虑了精确度和召回率。

哪个因素对预测酒店取消更重要?

从酒店的角度来看,他们可能希望更准确地识别出最终会取消预订的客户,这使得酒店能够更好地分配房间和资源。确定不打算取消预订的客户不一定会增加酒店分析的价值,因为酒店知道,无论如何,很大一部分客户最终都会坚持预订。

测试数据

让我们看看当模型对 H2(测试集)进行预测时,结果是什么样的。

在这里,我们可以看到 f1 得分的准确性略微提高到了 52%。

[[12569 33659]
 [ 4591 28511]]
              precision    recall  f1-score   support 0       0.73      0.27      0.40     46228
           1       0.46      0.86      0.60     33102 accuracy                           0.52     79330
   macro avg       0.60      0.57      0.50     79330
weighted avg       0.62      0.52      0.48     79330

但是,取消类(1)的召回率为 86%。如前所述,精确度和召回率经常相互矛盾,仅仅是因为假阳性倾向于增加召回率,而假阴性倾向于增加精确度。

假设酒店希望最大化召回率(即容忍一定数量的误报,同时识别所有将取消预订的客户),那么该模型符合该标准。

在所有取消预订的客户中,该模型正确识别了 86%的客户。

模型评估

虽然较高的召回率被认为是判断该模型的较好方式,但这不一定以较低的准确性为代价。

如果召回是惩罚假阴性,那么它也有利于假阳性——有太多的假阳性会破坏模型的目的——因为这本质上是假设所有客户都会取消,而事实并非如此。

在这方面,准确性和召回率将理想地最大化。例如,XGBoost 模型展示了 94%的召回率和 55%的 f1 分数准确度——两者都比这个例子中的略高。

结论

在本例中,我们研究了如何使用 KNN 作为分类算法,以及在判断模型性能时精确度和召回率的重要性。

非常感谢您的参与,这个例子的相关 GitHub 库可以在这里找到。

免责声明:本文是在“原样”的基础上编写的,没有担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。

参考

  • 安东尼奥、阿尔梅迪亚和努内斯(2019)。酒店预订需求数据集
  • GitHub 仓库(Msanjayds):交叉验证计算
  • 机器学习掌握:用 Python 对不*衡分类进行 SMOTE 过采样

使用 Foursquare 数据对加德满都和 Lalitpur 病房进行分类

原文:https://towardsdatascience.com/classification-of-kathmandu-and-lalitpur-wards-using-foursquare-data-60e5672e461e?source=collection_archive---------29-----------------------

介绍

加德满都大都市和拉利特普尔大都市是尼泊尔最大的两个城市。虽然是两个不同的城市,但对于这两个城市的居民来说,它们就像一个联合的大城市。每天都有成千上万的人往返于这两个城市之间,这是他们日常生活的一部分。

加德满都市区和拉利特普尔市区分别有 32 个市级选区和 29 个市级选区。对于这个项目,我们想根据病房中的场地对这两个城市的不同病房进行分类。我们的目标是将这两个城市作为一个单一的组合城市来研究,并找出这两个城市的相互反映或作为一个单一单位的选区。

由于人口密集的地区通常更加昂贵和嘈杂,我们也在研究每个病房的人口密度数据。这项研究对于那些想搬到或不得不搬到加德满都或拉利特普尔市的人来说,将是非常有见地的。这种分析将有助于他们根据对场地和人口密度的偏好缩小病房的范围。

数据收集

我们需要收集关于加德满都和拉利特普尔市的病房位置、人口密度和面积的数据。

由于我们正在收集两个城市的数据,我们将分别收集每个城市的数据,然后将它们合并为一个数据集。单个城市的数据被废弃,并从其各自的大都市网站提供的数据中收集。几乎所有关于选区、人口和位置的数据都是用尼泊尔语(梵文)提供的,所以我们必须使用谷歌翻译将数据翻译成英语,然后废弃这些数据。

两个城市的官方网站都提供了每个选区总部的位置。我们将使用地理编码器库使用这些位置数据来定位病房的纬度和经度。

使用纬度、经度数据和 four square API 可以获得每个区的场馆细节。

数据来源是:

  1. 加德满都病房及其总部的详细信息列表从[1]中删除,各个病房的人口数据从[2]中收集。病房面积数据从本文中导入[3]。
  2. 类似地,从[4]中删除了 Lalitpur 病房及其总部位置的列表,并从[5]中收集了各个病房的人口和面积数据。
  3. 使用地理编码库获得基于其总部位置的病房的经度和纬度,并使用谷歌地图[6]手动放置缺失值。
  4. 从 four square API [7]中收集关于场馆列表、类别、细节和每个病房的数量的数据。

方法学

数据准备

我们只选择了面积大于 0.5 *方米的病房。合并面积小于 0.5 *方公里的行政区。*方公里及其邻*的选区。这减少了病房的数量,而且如果病房很小的话,很多地点重叠的可能性很高,这可能会影响我们以后的研究。关于加德满都和拉利特普尔的病房位置、面积和人口密度的数据被合并,以获得最终的合并数据集,您可以在这个 github 链接上访问该数据集。

下面的地图是使用加德满都和拉利特普尔的病房的位置数据创建的。

加德满都和拉利特普尔的地图及其选区办公室位置

可以使用 four square API 收集为每个病房列出的场所,并且选择半径作为从各个病房的区域获得的半径。为了更好地进行分析和数据研究,我们只筛选出那些列出的场馆数量至少为五个的病房。

代表加德满都和拉利特普尔区场馆数量的条形图

大约有 94 个独特的场馆类别。因此,我们决定使用 four square API 提供的 10 个主要分类场馆,而不是子分类场馆。这 10 个分类场地及其各自的 id 是从下面列出的四个广场 API 中获得的。

主场馆类别及其 ID

从每个区的四个广场 API 中获得这十个类别的每个分类场所的数量。下面的方框图描述了这些类别在拉利特普尔和加德满都的分布情况。

代表加德满都和拉利特普尔病房分类场所的箱线图

我们可以观察到,食品在该类别中占主导地位,而商店和服务、户外活动和创作则排在食品之后。由于事件地点没有重要数据,我们删除了事件类别。

我们使用最小-最大比例(1:最高值,0:最低值)对数据进行标准化。这使数据正常化,并使我们更容易解释不同的场馆。缩放后的数据如下图所示。

使加德满都和拉利特普尔区的场馆类别数据正常化

使聚集

我们使用 k-means 聚类对加德满都和拉利特普尔的病房进行聚类,以每个病房的分级类别数据为基础。

不同组数(k)的初步结果可以解释如下:

1.k=2:场馆数量多和数量少的集群

2.k=3:学院数量多、场馆数量少的集群,第三个集群难以解释

3.k=4:具有高数量的学院、高数量的户外和娱乐场所的集群,其余两个被分类为高和低数量的场所

4.k=5:难以解释集群。

我们选择第四组的数字进行病房分析,因为它提供了比其他组更详细的见解。

聚类及其分类场所的盒图

我们可以从箱线图中观察到,当将数据分为四个簇时,我们得到以下簇。

  1. 集群 0:多样但低的场馆集群
  2. 集群 1:户外、创意、艺术和娱乐集群
  3. 集群 2:学院、大学、商店和服务集群
  4. 集群 3:多样化和高场馆集群

五大场馆

我们还对每个区的前五个子场馆类别进行了排名,列出了每个区最受欢迎的场馆。

每个选区的前五个场馆

标注人口密度

加德满都和拉利特普尔的人口密度分布

该直方图显示了加德满都和拉利特普尔选区的人口密度分布。

基于这个直方图,我们可以根据人口密度给每个病房贴上以下标签。

  1. 20,000 =低水*人口密度

  2. 20000–40000 =较低的中层 1 人口密度
  3. 40,000–60,000=更高的中层 2 人口密度
  4. 60,000–80,000 =高水*人口密度
  5. 80,000 =非常高的人口密度

将我们获得的每个区的所有细节与人口密度标签和聚类标签合并,我们得到以下最终数据集。

加德满都和拉利特普尔的病房及其集群标签、人口密度标签和前五名场馆

最后,我们实现了以下加德满都和拉利特普尔城市的地图。这张地图是用两个城市的不同区标绘的,区标有各自的集群、前三个热门地点和人口密度的信息。

代表加德满都和拉利特普尔区集群、场馆和人口密度的地图

结果

根据现有场馆对加德满都和拉利特普尔的不同行政区进行聚类,并在地图上进行绘制,这向我们展示了大多数多样但低矮的场馆区域位于拉利特普尔和加德满都联合城市的郊区,而联合城市的中心以及两个城市的中心由高大的场馆和特定的场馆区域组成。我们可以观察到两个城市在许多镜像地理位置上相互镜像。人口密度让我们进一步了解所谓的住宅和市场情况。

讨论

four square 数据确实信息丰富,但并不全面,尤其是对于像尼泊尔这样数字*台使用和可访问性刚刚开始繁荣的国家。我们可以观察到,许多病房在四方 API 中列出的场馆数量很少,这肯定与这些病房中的实际场馆数量不一致。此外,四方场馆数据侧重于类别,不包括场馆的规模。与餐馆相比,公园无疑会吸引更多的人。)

结论

我们可以在更大的范围内部署这样的工具,提供更多的功能,如住房率、空气质量率,以更详细地研究我们的城市和其他地区。

不仅对于寻找他们最喜欢的社区的人来说,这种分析也可以极大地有益于城市管理和商业团体等团体。

参考

[1] 加德满都病房办公室位置

[2] 加德满都病房简介

[3] Chhetri,Sachin & Kayastha,P… (2015 年)。尼泊尔加德满都大都市火灾潜在区划绘图的层次分析法(AHP)模型的表现。国际地理信息杂志。4.400–417.10.3390/ijgi4010400。

[4] 拉利特普尔病房办公室

[5] 拉利普尔病房详情

[6] 谷歌地图

[7] 四个正方形

我想提一下 Stanislav Rogozhin 对莫斯科地铁站所做的类似分析,它给了我很大的帮助,让我清楚地知道如何处理和实施这种分析。你可以在下面的链接中找到关于他的分析的研究文章。

https://towards data science . com/class ification-of-Moscow-metro-stations-using-four square-data-FB 8 aad 3 E0 e 4

基于机器学习的天空物体分类

原文:https://towardsdatascience.com/classification-of-sky-objects-with-machine-learning-be4b05816690?source=collection_archive---------49-----------------------

图片鸣谢&版权: 伊万·佩德雷蒂

第一步:问题和背景

分类是你在数据科学领域可能面临的主要项目之一。因此,我们的导师要求我们为第三个个人项目执行一个分类项目。

自从我开始从事数据科学工作以来,我一直想从事天文数据方面的工作。因此,在做了一些研究后,我决定处理 SDSS 的数据,其中包括关于天空物体及其各种特征的信息。

通过这个项目,我的目标是通过光谱和光度特征对天空物体如恒星、星系和类星体进行分类。SDSS 将这些特性保存在两个不同的表中,所以我不得不借助于一个 SQL 命令来合并它们。合并之后,我将这个表添加到我的项目文件夹中。好了,我得到了我的数据,所以是时候研究这个项目的方法了。

第二步:数据知识

我之前说过,我的数据集是由两个表组成的;一个是光谱特征,一个是光度特征。

  • 光谱数据:从物体光谱测得的各种量。例如它的光谱类型或红移。
  • 测光数据:从物体图像测量的各种量。例如它的亮度、mag、通量或大小。

这个数据集由 22 列和 500000 行组成,没有缺失值。我在目标列中有三个独特的值:星系,恒星和类星体。

我的目标变量的三个等级之间的*衡是 54%星系,35.4%恒星,和 10.6%类星体。因此,我的数据集没有不*衡,在对数据进行一些基本的数据清理和可视化之后,我可以跳到特征工程部分。

目标值的红移情况-真表示红移,假表示蓝移

步骤 3:特征工程

我标记了 11 个对构建一个良好的分类模型非常重要的特性。因此,我专注于这些专栏来做特性工程。以下是我为本节选择的功能:

  1. 电磁波谱:天体辐射的光的强度。观测波段范围:紫外线(u)、绿色(g)、红色(r)、红外线(I)和 z 波段。
  2. 通量密度:从天体表面流出的能量比率。
  3. 红移

我从异常值开始。在 I 波段和 z 波段有异常值,所以我用 pandas clip方法处理它们。其次,由于我的通量特征范围太广,我也做了一些单位转换。当我收集数据时,通量特征采用纳马吉单位,因此我将它们转换为扬斯基单位(光谱通量密度单位),1 纳马吉= 3.631 * 10^-6 扬斯基。此外,我做了一个对数转换,以减少红移特性的离群值的影响。

在这些操作之后,我检查了我的特征来进行提取。在这种情况下,我受益于频谱特性来创建新的特性。嗯,有一个特征叫做颜色指数,它定义了天体的颜色。将这个特性添加到我的数据集中太简单了,因为它是由两个光谱范围之间的差异定义的。

# Colour Indexes-difference between the two spectrum range
df1['color_u_g'] = df1['u'] - df1['g']
df1['color_g_r'] = df1['g'] - df1['r']
df1['color_r_i'] = df1['r'] - df1['i']
df1['color_i_z'] = df1['i'] - df1['z']

因此,在简单的四行代码之后,我设法向我的数据集添加了四个新特性。最后一步,我为将要创建的分类模型编码了目标变量。

步骤 4:分类模型

我从探索基本模型开始这一步:KNN、逻辑回归、朴素贝叶斯、线性 SVC 和决策树。在这些模型初始化之后,我开始定义集合模型:

  • Bagging 算法:随机森林、额外树和 Bagging 分类器
  • 增强算法:AdaBoost 和 GradientBoosting
  • 投票分类器:软和硬

我的方法是,在运行完本节中的所有模型后,选择最佳模型进行优化。因此,我用默认参数运行了所有的模型,以查看结果。

这是我的分类模型的结果。我还在表中添加了测试准确度分数,以确保我的模型中没有过度拟合。可以看出,我定义了所有众所周知的算法来为这个项目的问题选择最合适的模型。

我必须明确指出,现实世界中没有最好的模型。正如乔治 E.P 博克斯曾经说过的:“所有的模型都是错的,但有些是有用的。”你只需要找到对你的问题最有用的模型&数据。

随机森林-特征重要性表:前七个特征

嗯,Bagging 分类器模型是我选择用于进一步工作的模型,因为测试准确度分数。有了 99.38%的准确率,它似乎是我的项目的最佳模型。

第五步:改进模型

我跳到了模型评估之后的优化部分。为此,我决定使用GridSearch

#specify the parameters to be tested
n_est = [80, 100, 120, 140]
max_features_opt = [0.93, 0.95, 0.97]param_grid = dict(n_estimators = n_est, max_features=max_features_opt)

我们可以在确定将由GridSearchCV测试的参数后拟合模型。请注意,GridSearchCV本身包含了交叉验证,所以我也必须指定KFold的值。当拟合过程完成时,我可以检查最佳估计值。

kf=KFold(n_splits=10, shuffle=True, random_state=76)bag_grid = GridSearchCV(bag, param_grid, cv = kf, 	scoring='accuracy', n_jobs=-1)
bag_grid.fit(X_train, y_train)

并且,根据我从GridSearch得到的结果,我重新定义了 Bagging 分类器模型。

bag_opt = BaggingClassifier(base_estimator=dtree, n_estimators=120,  max_features=0.93, bootstrap=True, random_state=seed, n_jobs = -1)bag_opt_acc = round(cross_val_score(bag_opt, X_train, y_train, 																																																 cv=kf).mean())bag_opt_std = round(cross_val_score(bag_opt, X_train, y_train, cv=kf).std())

第六步:结果

在我的最后一步,我进行了保持测试,以查看优化的 Bagging 模型的准确性。我从优化后的模型中得到的结果是 99.44%,因此我实现了大约 0.06%的提高。这不是很多,但我的模型有一个很好的准确性评分。

混淆矩阵

最终结论:

  • 从特征重要性表中可以看出,红移和颜色索引是我的分类问题中最有价值的特征。
  • 我得到的最好的结果来自集合模型。在这个项目中,朴素贝叶斯和 KNN 模型的精度值相对较小。

好了,暂时就这些了。下一篇文章再见!

另外,如果你想了解更多,也可以看看我的 Github 简介!

多重测量分类——用更多证据建立信心

原文:https://towardsdatascience.com/classification-with-multiple-measurements-building-confidence-with-more-evidence-779599f25ada?source=collection_archive---------34-----------------------

基于朴素贝叶斯假设的贝叶斯方法

如果您熟悉分类问题,那么您可能对训练模型的整个过程并不陌生:分割数据、训练模型、使用 ROC 分析调整模型,最后,将模型发布到现实世界中为您做出预测..

如果有超过 1 个数据点来自同一个对象,而你的分类器给你的结果是混合的,该怎么办?或者,即使你的分类器给你一致的结果,你应该对结果有多少信心,因为你的分类器不是 100%准确的(它从来不是,也永远不会是)?

例如,如果您正在构建一个准确率为 85%的 cat 分类器应用程序。一个用户刚刚上传了同一只猫的 5 张照片。

或者你正在为一个时间序列建立一个信号检测分类器。一些异常事件可能偶尔出现,如果不是连续出现的话。

当我们手中有越来越多的数据时,我们如何利用这些测量数据并建立信心

在此之前,我们需要先回顾一些基础知识

概率链规则

让我们从两个变量 A 和 b 开始。贝叶斯定理指出

它可以简单地用文氏图表示出来。

然后我们可以再添加一个事件,事件 C:

有了这个模式,我们可以将公式推广到 n 个事件:

朴素贝叶斯

假设我们要构建一个分类器

我们可以用链式法则展开右边的分子

朴素贝叶斯假设条件独立性

有了朴素贝叶斯假设,我们就有了继续前进所需的东西。

利用多个证据做出综合决策

假设我们有 n 个测量值,分类器的输出标记为

我们试图回答的是给定 n 测量值,真实类的概率等于 1。即

假设在 n 个测量值中,我们的分类器预测其中 n0 个是 0,n1 个是 1。这给了我们最后一个表达式:

注意,这里出现的所有术语都可以从 ROC 分析中检索到,因此我们可以将这个公式集成到我们现有的监督学习过程中。

例子

让我们在公式中加入一些数字,看看它在实际中是如何工作的。以下是数字:

并且聚集预测相对于测量数量的曲线如下所示:

真类 y=0

真类 y=1

您可以看到,聚合预测对测量结果建立了信心,并最终收敛到正确的预测。

另一个观察结果是,只有一个度量时的预测不如 ROC 度量。这是因为我们考虑了 P(y=0)/P(y=1)的比值。这取决于你的应用来证明这个定量的估计是否准确。然而,当测量的数量增加时,这个比率的影响变小,因为其余的项呈指数增长。

PyCaret 分类:一个更好的机器学习库

原文:https://towardsdatascience.com/classification-with-pycaret-a-better-machine-learning-library-cff07a10a28c?source=collection_archive---------16-----------------------

用 PyCaret 解决分类任务——比你想象的要简单。

几天前,我已经介绍了 PyCaret 库的基础知识,以及如何使用它来处理回归任务。如果你是新来的, PyCaret 是一个低代码的机器学习库,可以为你做一切事情——从型号选择到部署。阅读前两篇文章不是先决条件,但是如果您以前没有使用过这个库,可以随意浏览它们。

奥斯曼·拉纳在 Unsplash 上拍摄的照片

分类问题是最常见的机器学习问题类型-信息是否是垃圾邮件,客户是否会离开,测试结果是肯定的还是否定的-仅举几个例子。出于这个原因,我们需要知道如何处理分类任务,以及如何轻松地这样做。

如果你更喜欢视频,或者只是想巩固你的知识,请随意观看我们关于这个主题的视频。源代码包括:

对于开发者来说, PyCaret 被认为比 Scikit-Learn 更好更友好。两者都很棒,不要误会我的意思,但是 PyCaret 将为您节省大量时间,否则这些时间将花费在型号选择和微调上。这是这份工作最无趣的部分。

本文假设您熟悉机器学习中的分类概念。您不必是专家,但前提是您知道如何使模型适合其他库中的数据。

这篇文章的结构如下:

  1. 数据集概述和清理
  2. 模型选择和培训
  3. 模型可视化和解释
  4. 预测和保存模型
  5. 结论

事不宜迟,我们开始吧!

数据集概述和清理

在我的大多数基于分类的文章中,我喜欢使用巨大的数据集。原因有很多,最明显的一个原因是它相当简单,但是对于数据清理和准备来说并不简单。

我们可以直接从 GitHub 加载到熊猫中:

data = pd.read_csv('https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv') data.head()

现在,我们可以继续进行数据清理了。我想做的是:

  • 删除无关栏目(车票乘客号
  • 性别列重新映射为 0 和 1
  • 检查一个乘客是否有一个独特的头衔(如医生)或更普通的头衔(如先生、小姐。)—可以从名称列中提取
  • 检查舱室信息是否已知——如果舱室栏的值不是 NaN
  • 开始的列创建虚拟变量— 3 个选项
  • 用简单*均值填充年龄

这是代码:

data.drop(['Ticket', 'PassengerId'], axis=1, inplace=True) gender_mapper = {'male': 0, 'female': 1} 
data['Sex'].replace(gender_mapper, inplace=True) data['Title'] = data['Name'].apply(lambda x: x.split(',')[1].strip().split(' ')[0]) 
data['Title'] = [0 if x in ['Mr.', 'Miss.', 'Mrs.'] else 1 for x in data['Title']] 
data = data.rename(columns={'Title': 'Title_Unusual'}) 
data.drop('Name', axis=1, inplace=True) data['Cabin_Known'] = [0 if str(x) == 'nan' else 1 for x in data['Cabin']] 
data.drop('Cabin', axis=1, inplace=True) emb_dummies = pd.get_dummies(data['Embarked'], drop_first=True, prefix='Embarked') 
data = pd.concat([data, emb_dummies], axis=1) 
data.drop('Embarked', axis=1, inplace=True) data['Age'] = data['Age'].fillna(int(data['Age'].mean()))

我鼓励你只是复制代码,因为很容易出现拼写错误,而且你不是来练习数据准备的。数据集现在看起来是这样的:

这个看起来好多了。我们还可以做一些事情,但是今天就到此为止,继续建模吧。

模型选择和培训

首先,让我们从 PyCaret 库中导入分类模块,并执行一个基本设置:

from pycaret.classification import * clf = setup(data, target='Survived', session_id=42)

我已经将随机种子设置为 42,因此您可以重现结果。

几秒钟后,您会在屏幕上看到一条成功消息,旁边有一个表格,显示您的数据信息。如果你愿意,可以通读一遍。接下来,我们将比较各种机器学习模型的性能,看看哪种模型总体上做得最好:

compare_models()

是的,就像函数调用一样简单。执行时间从几秒钟到一分钟不等,因为几个算法都经过交叉验证训练。完成后,您应该会看到下表:

看起来光梯度增强方法总体上做得最好,所以我们可以用它来创建我们的模型:

model = create_model('lightgbm')

您可以通过 tune_model() 函数自由地执行额外的超参数调优,但是它并没有提高我的性能。

我们将在下一节进行一些可视化和解释。

模型可视化和解释

首先,让我们看看 plot_model() 函数提供了什么:

plot_model(model)

ROC(受试者操作特征)曲线下的面积告诉我们该模型在区分类别方面有多好——预测存活为存活,预测死亡为死亡。

如果这仍然不能像你希望的那样很好地解释,下面是绘制混淆矩阵的方法:

plot_model(model, 'confusion_matrix')

如果我们考虑到建造它的容易程度,这个模型实际上是相当不错的。接下来,让我们用 SHAP 值来解释我们的模型。

SHAP,或沙普利附加解释,是一种解释机器学习模型输出的方式。通过绘制每个样本的每个特征的 SHAP 值,我们可以使用它来查看哪些特征最重要。

太好了!现在让我们对测试集进行最终评估,并将模型保存到一个文件中。

预测和保存模型

一旦我们对模型的表现感到满意,我们就可以在测试集(以前看不到的数据)上对它进行评估:

predictions = predict_model(model)

当我们调用本文开头的 setup() 函数时, PyCaret 以 70:30 的比例执行了训练/测试拆分。当然,这个比例是可以改变的,但是我对默认值很满意。

测试集上的结果有点糟糕,但这是意料之中的。在将模型保存到文件之前,我们需要完成它:

finalize_model(model)

现在可以保存模型了:

save_model(model, 'titanic_lgbm')

模型现在保存在笔记本所在的文件夹中。要在以后加载它,您可以使用 load_model() 函数,并将文件路径作为参数。

结论

我希望您已经跟上了,在数据准备上花了更多的时间,并最终得到了一个更好的模型。PyCaret 确实为我们做了很多事情,所以我们可以把时间花在更重要的事情上。

还有更多的东西需要探索,而且很快就会到来。感谢阅读。

加入我的私人邮件列表,获取更多有用的见解。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

[## 通过我的推荐链接加入 Medium-Dario rade ci

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@radecicdario/membership)

原载于 2020 年 7 月 18 日 https://betterdatascience.com**T21

Python 中的随机森林分类

原文:https://towardsdatascience.com/classification-with-random-forests-in-python-29b8381680ed?source=collection_archive---------24-----------------------

随机森林分类模型

来源

随机森林算法是一种机器学习方法,可用于监督学习任务,如分类和回归。该算法通过构建一组在随机特征子集上训练的决策树来工作。在分类的情况下,随机森林模型的输出是决策树中预测类的模式。在本帖中,我们将讨论如何在 python 中为分类任务构建随机森林模型。

我们开始吧!

随机森林分类

对于我们的分类任务,我们将使用蘑菇分类数据集,可以在这里找到。我们将对一个二元目标进行预测,该目标指定蘑菇是有毒的还是可食用的。

首先,让我们导入 pandas 库并将我们的数据读入数据框:

import pandas as pd 
df = pd.read_csv("mushrooms.csv")

让我们打印数据框的形状:

print("Shape: ", df.shape)

接下来,让我们打印数据框中的列:

print(df.columns)

现在让我们也来看看前五行的数据。head()'方法:

print(df.head())

属性信息如下

来源

我们将预测蘑菇的类别,其中可能的类别值为“e”表示可食用,“p”表示有毒。接下来我们将把每一列转换成机器可读的分类变量:

df_cat = pd.DataFrame()
for i in list(df.columns):
    df_cat['{}_cat'.format(i)] = df[i].astype('category').copy()
    df_cat['{}_cat'.format(i)] = df_cat['{}_cat'.format(i)].cat.codes

让我们打印结果数据框的前五行:

print(df_cat.head())

接下来,让我们定义我们的功能和目标:

X = df_cat.drop('class_cat', axis = 1)
y = df_cat['class_cat']

现在让我们从“sklearn”导入随机森林分类器:

from sklearn.ensemble import RandomForestClassifier

接下来,我们从‘sk learn’中的型号选择模块导入‘KFold’。我们将“折叠”以验证我们的模型。此外,我们将使用 f1 分数作为我们的准确度指标,它是准确度和召回率的调和*均值。让我们也用两次分割初始化“KFold”对象。最后,我们将初始化一个列表,我们将使用它来附加我们的 f1 分数:

from sklearn.model_selection import KFold
kf = KFold(n_splits=2, random_state = 42)
results = []

接下来,让我们迭代数据中的索引,并拆分数据用于训练和测试:

for train_index, test_index in kf.split(X):
     X_train, X_test = X[train_index], X[test_index]
     y_train, y_test = y[train_index], y[test_index]

在 for-loop 中,我们将定义随机森林模型对象,适合不同的训练数据折叠,预测相应的测试数据折叠,评估每次测试运行的 f1 分数,并将 f1 分数附加到我们的“结果”列表中。我们的模型将使用 100 个估计器,对应于 100 个决策树:

for train_index, test_index in kf.split(X):
     ...
     model = RandomForestClassifier(n_estimators = 100, random_state = 24)
     model.fit(X_train, y_train)
     y_pred = model.predict(X_test)
     results.append(f1_score(y_test, y_pred))

最后,让我们打印模型的*均性能:

print("Accuracy: ", np.mean(results))

如果我们将拆分数量增加到 5,则我们有:

kf = KFold(n_splits=3)
...
print("Accuracy: ", np.mean(results))

我就讲到这里,但是我鼓励你自己动手处理数据和编写代码。

结论

总之,在这篇文章中,我们讨论了如何用 python 训练一个随机森林分类模型。我们展示了如何将分类特征值转换成机器可读的分类值。此外,我们还展示了如何分割用于训练和测试的数据,初始化我们的随机森林模型对象,使其适合我们的训练数据,以及测量我们的模型的性能。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!

分类器校准

原文:https://towardsdatascience.com/classifier-calibration-7d0be1e05452?source=collection_archive---------2-----------------------

分类任务模型校准的原因、时间和方式。

Christophe Hautier 在 Unsplash 上拍摄的照片

当处理分类问题时,仅收集测试集上的预测是远远不够的;我们通常会带着某种程度的自信来赞美他们。为此,我们利用关联概率,即由分类器计算的可能性,它指定了每个样本的类别。但这总是反映现实吗?如果不是,我们怎么知道?

学习率是我每周给那些对 AI 和 MLOps 世界好奇的人发的简讯。你会在每周五收到我关于最新人工智能新闻、研究、回购和书籍的更新和想法。在这里订阅!

介绍

假设我们有两个二元分类器;型号A和型号B。模型A的每一次预测都有 85%的准确率和 0.86 的置信度。另一方面,模型B也有 85%的准确性,但对其每个预测的置信度为 0.99。你觉得哪个型号比较好?

在这个故事中,我会试着让你相信A模式更好。模型A认为自己在 86%的时间里都是精确的,事实上,差不多就是这样。相反,model B对自己的预测过于自信。这个玩具示例展示了概率和模型校准背后的直觉。

模型校准指的是这样一个过程,其中我们采用一个已经训练好的模型,并应用一个后处理操作,这改进了它的概率估计。因此,如果我们以 0.85 的概率检查估计为阳性的样本,我们预计其中 85%实际上是阳性的。

从形式上来说,如果对于任何概率值p,一个类的预测有信心p在 90%的时间里是正确的100*p,那么这个模型就是完美校准的。

现在,因为如果你试图将一幅图像可视化并绘制出p的每一个值,那么在从01的时间间隔内,一幅图像就抵得上千言万语,所以我们期望在计算出的概率和阳性分数之间得到一个完美的线性关系。

校准完美的分类器

在接下来的章节中,我们将了解校准分类器的原因、时间和方法。

为什么模型校准很重要?

只有当您关心模型计算的概率时,模型校准才是重要的。例如,假设您正在构建一个推荐引擎,它根据用户偏好对产品进行排序。如果您的模型估计用户u将以 0.9 的概率购买产品a,以 0.7 的概率购买商品b,那么您可以先提供产品a。不需要校准那个模型。

然而,如果您正在构建一个计算一个人生病的概率的关键任务应用程序,那么实际的概率值是非常重要的。例如,如果你的模型对一个特定的病人不那么有信心,人类医生当然应该知道这一点并采取相应的行动。

模型校准还有其他有用的情况:

  • 调试:我们想知道什么时候我们的模型是高置信度错误的,或者把低概率分配给正确的类
  • 集合:如果我们想要组合许多概率模型,拥有准确的预测是很重要的

如何检查你的模型

到目前为止,我们已经了解了什么是模型校准,以及为什么它在某些情况下很重要。但是我们如何检查我们的分类器是否被校准了呢?

最好的方法是用我们自己的眼睛去看。在这个实验中,我们使用 scikit-learn 提供的“make_classification”辅助方法创建了一个用于分类的随机数据集。

接下来,我们实例化两个分类器进行比较;一个简单的逻辑回归模型和一个由 scikit-learn 提供的支持向量机实现。

最后,我们将分类器拟合到我们的训练数据,并计算我们对测试数据集的预测。特别是对于 SVM,要获得正类的概率,我们需要知道决策函数如何分离测试样本,并将结果归一化为介于“0”和“1”之间。

现在让我们为两个分类器绘制Kernel Density Estimation

逻辑回归 Vs SVM KDE 图

正如我们所料,我们从逻辑回归得到的结果是从0扩散到1,而 SVM 预测正好是01。接下来,让我们检查两个二元分类器的 AUC-ROC 曲线,但是这次使用我们为 SVM 计算的概率。

逻辑回归与 SVM 曲线

我们可以看到,SVM 在这个数据集上几乎是完美的。但是精度和校准完全是两码事。我们可以有一个完全精确的模型,根本没有校准,另一方面,一个不比随机模型好的模型,尽管如此,也是完全校准的。那么,我们如何检查呢?

第一步是获取所有预测并将它们分组到箱中。我们将根据模型做出的概率估计对它们进行分组。接下来,我们计算每个箱的阳性分数,最后计算每个箱的*均置信度,这就是属于该箱的样本的概率估计的*均值。如果我们将*均值与每个箱中阳性部分相对照,我们就得到可靠性图。我们希望该图类似于我们在开始时看到的线性图。

为了绘制每个分类器的校准曲线,我们定义了如下所示的效用函数。

我们现在准备绘制每个模型的校准曲线。让我们从逻辑回归开始。

逻辑回归的校准图

看起来相当不错。我们为 SVM 做同样的事情。

SVM 校准图

很明显,SVM 模型远未校准。当它预测样本不属于正类时,我们可以说它是信心不足,否则就是过于自信。那么,我们该如何解决这个问题呢?

校准模型

校准机器学习模型的两种最流行的方法是isotonicPlatt's方法。

Scikit-learn通过CalibratedClassifierCV类提供校准模型的基本估计器。对于这个例子,我们将使用Platt's方法,这相当于将类的构造函数中的method参数设置为sigmoid。如果你想使用isotonic方法,你可以传递它。

校准的 SVM 分类器

SVM 分类器的结果令人印象深刻地不同。现在我们有一个校准的 SVM 分类器。请注意,如果您在 SVM 分类器上调用predict_proba方法,结果已经通过Platt's方法进行了校准(参见此处的)。你可以自己试试。

此外,请记住,校准后模型的准确性可能会降低。例如,AUC-ROC 曲线现在是 0.963。因此,我们可以看到,在某些情况下,我们可能需要在精度和校准之间进行权衡。您也可以随时检查其他指标(如精确度、召回率、F1 分数等。).

校准后的 AUC-ROC 曲线

结论

在这个故事中,我们研究了什么是模型校准,为什么以及何时使用它,如何检查您的分类器是否校准,以及如果没有校准,如何潜在地修复它。

最后,我想给出一个经验法则,关于是否使用普氏标度等张回归。等张回归不做 sigmoid 假设,因此,它可能更适合大量情况。另一方面,它更容易过度拟合。因此,如果我们有一个小数据集, Platt 的缩放可能会更好。

我叫 Dimitris Poulopoulos,是希腊比雷埃夫斯大学BigDataStack的机器学习研究员和博士(c)。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲中央银行、经合组织和宜家等主要客户设计和实施人工智能和软件解决方案。如果你有兴趣看更多关于机器学习、深度学习、数据科学的帖子,在 twitter 上关注我* LinkedIn@ james2pl**。***

使用预先训练的 CNN 模型对任何对象进行分类

原文:https://towardsdatascience.com/classify-any-object-using-pre-trained-cnn-model-77437d61e05f?source=collection_archive---------27-----------------------

现实世界中的 DS

使用预训练的 Inception v3 卷积神经网络模型的大规模图像分类

列宁·艾斯特拉达在 Unsplash 上的照片

今天,我们有了超级有效的技术转移学习,我们可以使用由谷歌人工智能预先训练的模型来对计算机视觉世界中任何已分类视觉对象的图像进行分类。

迁移学习是一种利用预训练神经网络的机器学习方法。这里,名为 Inception-v3 的图像识别模型由两部分组成:

  • 特征提取部分采用卷积神经网络。
  • 分类具有全连接和 softmax 层的零件。

Inception-v3 是一个预训练的卷积神经网络模型,深度为 48 层。

这是一个已经在来自 ImageNet 数据库的 100 多万张图像上训练过的网络版本。这是谷歌的第三版 Inception CNN 模型,最初是在 ImageNet 识别挑战期间发起的。

这个预先训练好的网络可以将图像分为 1000 个对象类别,例如键盘、鼠标、铅笔和许多动物。因此,该网络已经学习了各种图像的丰富特征表示。网络的图像输入大小为 299 x 299。该模型在第一部分从输入图像中提取一般特征,并在第二部分基于这些特征对它们进行分类。

《盗梦空间 3 版》示意图——谷歌人工智能

Inception v3 是一个广泛使用的图像识别模型,已被证明在 ImageNet 数据集上达到超过 78.1%的准确率,在前 5 名结果中达到大约 93.9%的准确率。该模型是过去几年多名研究人员提出的许多想法的结晶。它基于 Szegedy 等人的原始论文:“重新思考计算机视觉的初始架构”。艾尔。

更多关于初始架构的信息可以在这里找到。

在迁移学习中,当您构建一个新模型来对原始数据集进行分类时,您可以重复使用特征提取部分,并使用数据集重新训练分类部分。由于不需要训练特征提取部分(这是模型中最复杂的部分),所以可以用更少的计算资源和训练时间来训练模型。

在本文中,我们将仅仅使用 Inception v3 模型来预测一些图像,并获取相同图像的前 5 个预测类。我们开始吧。

我们用的是 Tensorflow v2.x

输入数据

import os
import numpy as np
from PIL import Image
from imageio import imread
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import tf_slim as slim
from tf_slim.nets import inception
import tf_slim as slim
import cv2
import matplotlib.pyplot as plt

数据加载

用默认文件位置和各自的值设置所有初始变量。

ckpt_path = "/kaggle/input/inception_v3.ckpt"
images_path = "/kaggle/input/animals/*"
img_width = 299
img_height = 299
batch_size = 16
batch_shape = [batch_size, img_height, img_width, 3]
num_classes = 1001
predict_output = []
class_names_path = "/kaggle/input/imagenet_class_names.txt"
with open(class_names_path) as f:
    class_names = f.readlines()

创建 Inception v3 模型

X = tf.placeholder(tf.float32, shape=batch_shape)

with slim.arg_scope(inception.inception_v3_arg_scope()):
    logits, end_points = inception.inception_v3(
        X, num_classes=num_classes, is_training=False
    )

predictions = end_points["Predictions"]
saver = tf.train.Saver(slim.get_model_variables())

定义用于加载图像和调整大小的函数,以便以 RGB 模式发送到模型进行评估。

def load_images(input_dir):
    global batch_shape
    images = np.zeros(batch_shape)
    filenames = []
    idx = 0
    batch_size = batch_shape[0]
    files = tf.gfile.Glob(input_dir)[:20]
    files.sort()
    for filepath **in** files:
        with tf.gfile.Open(filepath, "rb") as f:
            imgRaw = np.array(Image.fromarray(imread(f, as_gray=False, pilmode="RGB")).resize((299, 299))).astype(np.float) / 255.0
        images[idx, :, :, :] = imgRaw * 2.0 - 1.0
        filenames.append(os.path.basename(filepath))
        idx += 1
        if idx == batch_size:
            yield filenames, images
            filenames = []
            images = np.zeros(batch_shape)
            idx = 0
    if idx > 0:
        yield filenames, images

加载预训练模型

session_creator = tf.train.ChiefSessionCreator(
        scaffold=tf.train.Scaffold(saver=saver),
        checkpoint_filename_with_path=ckpt_path,
        master='')

使用模型对图像进行分类

with tf.train.MonitoredSession(session_creator=session_creator) as sess:
    for filenames, images **in** load_images(images_path):
        labels = sess.run(predictions, feed_dict={X: images})
        for filename, label, image **in** zip(filenames, labels, images):
            predict_output.append([filename, label, image])

预言

我们将使用来自 kaggle 的动物-10 数据集的一些图像来声明模型预测。

for x **in** predict_output:
    out_list = list(x[1])
    topPredict = sorted(range(len(out_list)), key=lambda i: out_list[i], reverse=True)[:5]
    plt.imshow((((x[2]+1)/2)*255).astype(int))
    plt.show()
    print("Filename:",x[0])
    print("Displaying the top 5 Predictions for above image:")
    for p **in** topPredict:
        print(class_names[p-1].strip())

最后,所有的类都被准确地分类了,我们还可以看到模型预测的前 5 个相似的类都非常好和精确。

[## 贷款信用风险模型中的贷款违约分析

探索和分析贷款违约背后的驱动因素,即,使贷款审批流程风险…

towardsdatascience.com](/insightful-loan-default-analysis-in-lending-credit-risk-model-b16bbfc94a2f)

用谷歌自动分类狗和猫

原文:https://towardsdatascience.com/classify-dogs-and-cats-with-google-automl-2ae6eac64117?source=collection_archive---------31-----------------------

让我们使用狗与猫的 Kaggle 数据集来测试 GCP 汽车

https://MC . ai/build-a-machine-learning-model-on-cloud-using-Google-automl/

这是我与谷歌 Auto ML 的第一个项目,我非常好奇想尝试一下,因为我在 Medium 上看到了很多关于它的有趣帖子。例如,我建议你阅读这篇来自 Sriram Gopal 的非常有趣的帖子,他解释了使用 Google AutoML 进行类似项目的所有步骤

我决定使用来自 Kaggle 的 狗对猫 数据集。目标是分类图像是否包含狗或猫

让我们从使用 CRISP-DM 流程(数据挖掘的跨行业流程)开始:

  1. 业务理解
  2. 数据理解
  3. 准备数据
  4. 数据建模
  5. 评估结果
  6. 展开

业务理解

图像分类是当今非常常见的任务,它包括将图像和一些类别作为输入,并输出输入图像属于一个或多个给定类别的概率

一部分来自我想尝试的项目的主要目标 Google Auto ML

数据理解

Kaggle 提供的数据:

  • 测试:包含 12500 张猫狗图片的文件夹,用作测试数据集
  • train :包含 25000 张猫狗图片的文件夹,图片名称中带有标签,即 dog.0.jpg cat.0.jpg
  • sample submission . CSV:CSV 文件,参赛时作为提交参考

这些图像是 RGB 的,并且具有不同的尺寸

Kaggle 的数据集结构与 Google AutoML 的数据集结构非常不同,Google AutoML 的数据集结构是每个标签一个文件夹,里面有图像

准备数据和数据建模

如前所述,Kaggle 已经完成了图像的标记,模型的选择和调整将由 Google AutoML Vision 完成

要使用谷歌自动视觉:

  1. 创建一个谷歌云*台账户
  2. 创建新项目
  3. 启用 AutoML API
  4. 创建服务帐户
  5. 创建一个桶
  6. 创建. csv 文件来映射数据集的图像
  7. 将图像上传到桶中
  8. 创建数据集并上传图像
  9. 训练模型
  10. 部署模型

因此,我们只需以预期的格式向 Google AutoML Vision 提供数据。要让 Google AutoML Vision 使用数据:

  1. 将 Kaggle train 文件夹中的图像分离到文件夹中
  2. 文件夹上传到谷歌云存储的桶中
  3. 创建一个 csv 文件来映射桶中数据集的图像

数据集已经在训练、验证和测试数据集中被自动拆分。也可以在映射 csv 文件中为每个图像指定训练、验证、测试或未分配,以更改用于训练、验证和测试的图像数量之间的比率

标签统计

模型的训练可能需要几个小时(您可以选择使用多少个节点小时),当它完成时,google 将发送一封电子邮件

评估结果

评估页面中,我们可以看到模型性能

GCP 评估模型

可以使用滑块改变置信度阈值

我们也可以检查一下混淆矩阵(顺便说一句,我建议你读一读这篇来自blog.exsilio.com的帖子

https://blog . ex silio . com/all/accuracy-Precision-recall-f1-score-interpretation-of-performance-measures/#:~:text = Precision %20% 2D % 20 Precision % 20 is % 20 比率% 20 总计% 20 预测% 20 正面% 20 观察值。&text = F1 %20 score % 20% 2D % 20 F1 % 20 score % 20 is,以及% 20false % 20negatives %进入% 20 帐户。

真阳性(TP) :这些是正确预测的阳性
真阴性(TN) :这些是正确预测的阴性
假阳性和假阴性,这些值出现在你的实际类与预测类相矛盾的时候。
【FP】:实际类为负,预测类为正
假阴性(FN) :实际类为正,预测类为负

精度 = (TP + TN) / (TP + FP + FN + TN)

精度 = TP / (TP + FP)

回忆 = TP / (TP + FN)

F1 得分 = 2(召回率精确度)/(召回率+精确度)

在我们的案例中:

TP = 1240
TN = 1249
FP = 1
FN = 10
精度=(1240+1249)/(1240+1+10+1249)= 0.995
精度= 1240 / (1240 + 1) = 0.999
召回= 1240 / (1240 + 10) = 0.992
F1

展开

培训结束后,我们可以在 GCP 部署并使用该模型

一个 dash 应用程序已经被开发为用户界面:可以上传要分类的图像。当没有上传图像时,应用程序显示训练数据集的概览

Dash web 应用程序主页

当提供图像并按下分类按钮时,应用程序显示分类

图像分类

在所示的例子中,我在谷歌图片上搜索了一只狗,并用它作为输入来检查我的分类器的输出

有趣的事实

我试图对我的个人资料图片进行分类,显然我是一只得分为 0.89 的狗

我的个人资料图片分类

注意:代码可以在这个 github 仓库中找到

新冠肺炎增长数据的模式分类

原文:https://towardsdatascience.com/classify-growth-patterns-for-covid-19-data-41af4c7adc55?source=collection_archive---------10-----------------------

不同国家/地区每日确诊病例的模式

全球冠状病毒病例数据的探索性数据分析

2 月 2 日,我启动了一个网络仪表板,用于跟踪最*冠状病毒(新冠肺炎)爆发的传播,它提供了全球确诊、康复和死亡病例的实时视图。

[## 冠状病毒新冠肺炎爆发全球病例监测

冠状病毒新冠肺炎监测提供冠状病毒全球传播的最新数据。

dash-coronavirus-2020.herokuapp.com](https://dash-coronavirus-2020.herokuapp.com/)

到目前为止,它已经吸引了超过 17,000 名活跃用户,并在社交媒体上被分享了* 3,500 次。我真的很高兴我能贡献出我的技能来帮助处于全球危机中的人们,特别要感谢那些支持这个网络仪表板运行的人。

回到仪表板本身,很明显,它上面显示的信息是为了有效和容易理解而设计的。它们总是及时更新,并且只提供有限数量的历史数据(即区域范围的历史数据)。这使得用户能够获得他们所关心区域的最新状态。

然而,除了显示在仪表板上的实时数据之外,还可以从这些数据中提取更多信息。今天,我将与你们分享从迄今收集的所有数据中提炼出的一些见解。这可能有助于提高公众意识,进一步促进预先准备和减缓传播。

像往常一样,如果你想练习代码,可以从我的 Github 访问笔记本。

我们预计每日确诊病例的增量模式是什么?

在我们讨论个别模式之前,最好用一幅最*流行的漫画(图 1)来解释我们期望实现什么样的模式来遏制病毒并减缓其传播。

图 1 |三相和*曲线止价差改编自https://twitter.com/SiouxsieW

流行病事件发展过程中的病例数可以用流行病曲线来说明(图 1,左图),它包括三个阶段。目前,大多数国家正处于第一阶段,每天都有越来越多的病例得到确认。

我们希望看到的理想情况是放慢第一阶段的速度,使曲线变*,以便我们的卫生系统有足够的时间做好准备,最重要的是,有足够的资源应对每一个脆弱的病例(图 1,中图)。为了达到这种情况,需要采取严格的措施,包括旅行限制、自我隔离、社会距离和个人卫生。

对于那些处于疫情最早期阶段的国家,还有另一个积极的选择,即通过迅速和积极主动地采取全面措施来阻止疫情蔓延(图 1,右图)。

不同国家/地区每日确诊病例的增量模式是什么?

考虑到这些,让我们看看不同国家/地区的个别疫情曲线。此处使用的数据是从 1 月 21 日至 3 月 14 日收集的。为了能够看出模式,只有那些确诊病例超过 50 例的国家/地区包括在本分析中。

如图 2 所示,截至 3 月 14 日,共有 49 个国家/地区出现 50 例以上确诊病例。当绘制每日增量病例数与日期的关系图时(图 2),我们可以清楚地看到不同的模式。例如,根据流行曲线的定义,mainland China(图 2,右上角)已经处于第三阶段,而大多数其他地区仍处于第一或第二阶段。

图 2 |每日增量病例数与日期的对比

我们可以对增量模式进行分类吗?

然而,这些曲线的视觉区分是相当困难的。最好将这些曲线分类成簇,其中相似的曲线归为同一组。在这里,我将 P 原则 C o 坐标 A 分析( PCoA )和 k 均值聚类结合起来,在二维空间上绘制国家/地区增量模式的相似性,并对它们进行分组。简而言之,在二维排序中,相邻的圆圈(即国家/地区)应该具有相似的生长模式,彼此远离的圆圈几乎没有共同的模式。

注意!由于我们只对模式感兴趣,数据大小的影响应最小化,例如,mainland China 的每日增量数字比许多其他国家/地区高几个数量级。我们不希望这些差异包含在我们的分析中。这里的解决方案是首先将数据转换为*方根,然后将它们归一化为 0 到 1 之间的数字。这样做,我们只在计算不同国家/地区之间的相似性时赶上模式的变化。

图 3 |不同国家/地区增长模式的排序

正如我们从排序中看到的(图 3),有 5 组用不同的颜色表示(圆圈大小表示确诊病例数)。Mainland China 形成了自己的集群(深绿色)。阿联酋、泰国、台湾、韩国、香港、日本和新加坡形成了另一个集群(浅绿色)。伊朗与伊拉克、马来西亚、巴林、印度、越南和科威特(蓝色)同组。大多数欧洲国家连同美国、加拿大和澳大利亚形成了一个大的集群(红色)。最后,芬兰、菲律宾、印度尼西亚、爱沙尼亚和卡塔尔被归为同一组。

通过这些分类,我们现在可以从曲线矩阵(图 4)中获得更多信息,例如哪些国家/地区具有相似的增长模式,以及它们处于哪个阶段。

根据流行病曲线的定义,Mainland China 现在已经处于第三阶段。这是一个令人鼓舞的迹象。然而,我们应该知道,这些都是建立在人们巨大牺牲的基础上的,通过在今年的节日期间(即中国新年)暂停整个国家,呆在家里,尽量减少户外活动,避免大型集会,如果外出戴口罩。此外,他们在几周内建立了两所新医院,并向武汉派遣了大约 43000 名医务人员。

图 4 |每日增量病例数与分类日期的对比

留给我们考虑的问题是,当疫情在许多其他国家/地区爆发时,我们是否有如此规模的资源来做中国所做的事情,或者我们是否应该迅速和积极主动地采取行动来减缓甚至阻止疫情的到来。

这些把我们带到第二个集群(浅绿色)。这些地区都在很早的时候记录了第一批确诊病例,然而,保持了相对*坦的曲线。这可能归功于他们及时部署了旨在遏制冠状病毒传播的深思熟虑的措施。这再次突出了迅速和主动采取行动的必要性。此外,这也意味着,如果我们坚持遵守所有措施,情况应该会有所改善。

你可能会注意到,韩国也属于这一类。这主要是因为曲线后来出现的高峰是由所谓的患者 31 触发的,他是一个超级传播者,并将它传递给了成千上万的其他人。幸运的是,韩国几乎已经度过了高峰期,正在回到一个合理的水*。

蓝色、黄色和红色组中的国家都处于第一阶段,但曲线风格略有不同。具体来说,蓝色组的特点是早期*坦曲线和晚期爬坡曲线的混合(图 5)。

图 5 |马来西亚和伊朗的累积病例

然而,黄组国家大多拥有确诊病例数的突然增加(图 6),这可能表明控制失败。

图 6 |卡塔尔和芬兰的累积案例

红色组都是典型的指数增长曲线,是最不想要的情况。这些曲线可能表明在事情变得更糟之前,系统还没有准备好。

图 7 |西班牙和意大利的累积病例

向红色集团内的国家发出警报

如开始所述,分类只检查了生长模式,没有考虑规模。现在,让我们检查红组国家的案例数。

显然,并不是红色组中的每个国家都像意大利、西班牙或德国那样有高确诊病例数(图 8)。话虽如此,如果不采取认真的措施,这些数字肯定会成倍攀升,这进一步意味着本可预防的生命损失将是不可预防的。对这些国家的警告是现在就采取行动,否则就太晚了。

图 8 |截至 AEDT 时间 3 月 18 日的总病例数

“生活是我们所有选择的总和。”

阿尔伯特·加缪

数据源来自:

  • 丁香园
  • 腾讯新闻
  • 澳洲政府卫生署
  • 世界计量表
  • 中华人民共和国国家卫生委员会
  • JCU-CSSE

这是所有的乡亲。和往常一样,我欢迎反馈、建设性的批评以及听到关于您的数据科学项目的信息。可以通过 Linkedin 和我的网站找到我。

使用卷积神经网络对图像进行分类

原文:https://towardsdatascience.com/classify-your-images-using-convolutional-neural-network-4b54989d93dd?source=collection_archive---------28-----------------------

从头开始建立一个 CNN 来分类图像

简介

你听说过计算机识别图像吗?嗯,这并不神秘,按照这篇代码文章来构建您自己的图像分类器。我们将从 CIFAR 数据集获取图像。然后将它发送到我们自己的神经网络中,在那里计算机识别各种事物,如颜色、大小、形状等。并根据我们提供的样本数据学习识别图像。然后我们将使用学习过的网络来预测新的输入图像是什么。

让我们开始吧!

什么是 CIFAR-10 数据集?

CIFAR-10 数据集由 10 类 60000 幅 32x32 彩色图像组成,每类 6000 幅图像。有 50000 个训练图像和 10000 个测试图像。

数据集分为五个训练批次和一个测试批次,每个批次有 10000 幅图像。测试批次包含从每个类别中随机选择的 1000 个图像。训练批次以随机顺序包含剩余的图像,但是一些训练批次可能包含来自一个类别的比来自另一个类别的更多的图像。在它们之间,训练批次正好包含来自每个类的 5000 个图像。

来源:https://www.cs.toronto.edu/~kriz/cifar.html

这些类是完全互斥的。汽车和卡车之间没有重叠。“汽车”包括轿车、越野车等诸如此类的东西。“卡车”仅包括大卡车。两者都不包括皮卡车。

火炬视觉数据集

在本文中,我们将使用 PyTorch 中的数据集。这里还有很多其他很棒的数据集。https://pytorch.org/docs/stable/torchvision/datasets.html

检查 GPU 计算能力

GPU 的并行计算能力将显著提高你的训练速度。请注意,CUDA 是由 NVIDIA 开发的,我们将在下面的代码中使用。

导入数据

在下面的代码中,我们从 torchvision 获取数据集。我已经指定了 20%的训练数据用于验证,以及 30 张图像的批量。我强烈建议您使用验证数据集,我们将在下面看到它如何帮助选择正确的网络配置。

在将图像传递给神经网络之前,我对图像进行了一些变换,比如水*翻转和 20°的随机旋转。对图像应用变换是非常有用的,因为这可以帮助你的网络学习和预测图像,即使它处于不同的方向、颜色、大小等。点击查看 torchvision 提供的各种变换。我们稍后将图像张量作为输入传递给我们的神经网络。

classes 数组包含数据集中的 10 个类。

查看训练数据的图像🔎

拉伸图像

从上面来自训练数据集的样本图像中,我们可以看到我们应用了 RandomRotation(20)的变换。

神经网络架构

我们的模型需要将 RGB 光谱(深度= 3)中的 32×32 像素图像作为输入,并将输出作为 10 个类别(飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、卡车)之一返回

我已经创建了如下所示的架构,第一个卷积层接受 32x32x3 图像张量的输入。第一层的输出是 32x32x16 张量,每一层之后我们可以看到深度增加。在某些网络架构中,您可能会在每个卷积层之间看到一个池层,以减小规模。尽管池层有助于减小大小,但它也会丢失一些数据。由于我们的输入图像很小,我在每 2 个卷积层后使用了池层。2x2 合并图层后,我们可以看到张量大小从 32x32 减小到 16x16。卷积层 3 和 4 的输入张量分别是 16×16×32 和 16×16×64。

我使用了 25%的压差。通过在训练过程中忽略神经元,Dropout 有助于防止过度拟合。我还创建了一个名为 weights_init_normal 的函数,用正态分布的权重初始化模型。您不需要这样做,但是在较大的模型中,这可以帮助训练和验证损失快速收敛。

作者图片

卷积层充当特征提取器。更多的卷积层有助于识别每幅图像中的细微事物。完全连接的层和输出层充当分类器。

定义损失函数和优化器

我选择了 Adam 优化器,点击这里查看其他优化器,包括 SGD。学习率设定为 0.001。

培训和验证

我们调用带有各种参数的训练方法,如段数、模型、加载器等。我们将来自训练数据集(loaders['train'])的图像(数据)传递给模型。然后计算损失并进行反向传播,在此期间,我们根据损失更新权重。model.eval()通知所有层我们处于 eval 模式,因此批 norm 和 dropout 层将在 eval 模式下工作。

训练和验证损失计算为每个时期的*均值,然后打印出来,如下所示。如果在一个时期内验证损失小于以前,我们将模型保存到 model.pt 文件。

训练完成后,从 model.pt 文件中加载模型,该文件将具有所有历元中最小的验证损失。

从上面的输出中,我们可以看到模型在纪元 54 之后被保存。它的验证损失为 0.54

虽然我已经运行了当前模型 60 个历元,但在更早的时候,使用一组不同的超参数,我能够在 15 个历元内获得类似的性能。

策划培训和验证损失

绘制训练和验证损失图,我们可以看到它们在一个点上汇聚,然后开始远离。它显示了我们的模型在最后是如何过度拟合的,这被描述为训练损失的持续减少和验证损失的增加。好在我们创建了验证集来检查这个条件,并且在模型过度拟合之前保存了它;)

测试模型

这个函数类似于训练,但是这里我们通过测试数据集并计算损失。

从上面的输出中,我们可以看到模型在一些类中表现得更好,而在另一些类中表现得不那么好。

可视化少量测试结果

当前的输出显示了所有 20 张正确分类的图片,测试一下你的图片,找出一些错误分类的图片。

关于改进模型的想法

我们看到这个模型在一些课上表现不错,而在另一些课上表现不太好。花了大量的纪元来训练。在训练过程中,我们可以传递更多不太精确的类的图像,以便模型更好地学习。增加卷积层的数量以识别更多的特征,同时优化 dropout 和 epoch 以*衡过度拟合。

尝试调整以下内容,亲自看看性能有何不同,

  • 学习率
  • 拒绝传统社会的人
  • 批量
  • 添加/移除转换
  • 添加/删除卷积层
  • 更改优化器

我喜欢听你的建议/反馈!

将 400 万个 Reddit 帖子分类到 4k 个子编辑中:一个端到端的机器学习管道

原文:https://towardsdatascience.com/classifying-4m-reddit-posts-in-4k-subreddits-an-end-to-end-machine-learning-pipeline-ee511d26b362?source=collection_archive---------20-----------------------

应用多标签文本分类,用 fastText 和 FastAPI 构建一个数据产品。

具有数据工件、执行和 API 端点的端到端 ML 管道。

找到合适的 subreddit 来提交你的帖子可能会很棘手,尤其是对 reddit 的新手来说。有数以千计的具有重叠内容的活动子编辑。如果对人类来说这不是一件容易的事,我没想到对机器来说会更容易。目前,Reddit 用户可以在一个特殊的子编辑中请求合适的子编辑: r/findareddit 。

一位 Reddit 用户请求 subreddit 建议。

在这篇文章中,我分享了如何建立一个端到端的机器学习管道和一个为帖子建议子编辑的实际数据产品。你可以访问数据、代码、API 端点和用户界面,用你的 Reddit 帖子进行尝试。

从 4k 个子编辑中收集 400 万条 Reddit 帖子

我使用 Python Reddit API 包装器( PRAW )请求 5k 个最受欢迎的子编辑,但只得到 4k 个。对于每个帖子,我收集了截至 3 月 17 日的最新 1k 帖子的标题和内容。我把数据导出到一个 CSV 文件中,可以在 S3 下载。

来自 400 万条 Reddit 帖子的数据框架样本。

就数据质量而言,这个数据集远非完美。有些子帖子太笼统,有些帖子彼此非常相似,有些帖子在文本中没有足够的信息来知道它们应该贴在哪里。之前的方法选择了 1k 个子主题的子集,这些子主题在主题方面更加一致。虽然这种数据选择确实会使模型在测试分割中获得更高的分数,但它不一定对真实数据更有用,因为它会错过许多子数据。

众所周知,数据清理会对模型的性能产生很大的影响,数据科学家会花费高达 80%的时间来清理数据。作为一名数据科学家,我经常高估数据转换的影响。为了避免在早期的数据转换上花费太多时间,我更喜欢尽可能快地直接构建一个端到端的简单基线。一旦我有了第一个结果,我就可以运行版本控制的实验来观察每个转换的影响。端到端解决方案还允许您尽早获得最终用户的反馈。

使用 fastText 进行多标签文本分类

在上一篇文章中,我使用 fastText 为文本分类构建了一个通用的 ML 管道。我重用这些代码,在 Reddit 数据集上训练它,使用标题自我文本作为特征,使用子编辑名称作为标签。在训练数据集中,每个帖子被分配到一个标签,但很自然地会将问题视为多标签问题。在多标签文本分类中,通过概率将每个帖子分配给每个子编辑。

由脸书发布的 fastText 是一个具有两层的神经网络。第一层训练单词向量,第二层训练分类器。正如原文中提到的,fastText 适用于大量标签。收集的数据集包含 3 亿个单词,足以用 Reddit 数据从头开始训练单词向量。最重要的是,fastText 为由两个参数控制的子词创建向量, minnmaxn,设置最小和最大字符跨度以将一个词拆分成子词。在 Reddit 上,打字错误是常见的,如果不使用子词,特定术语可能会超出词汇表。

机器学习流水线架构

机器学习管道由 5 个通过 Valohai 管道交换数据的执行组成。每个执行都是一个 Python CLI,你可以在 Github 上找到每个执行的代码,在上一篇文章中可以找到关于如何创建一个在云上运行的管道的更多细节。

具有数据工件、执行和 API 端点的端到端 ML 管道。

文本特征被连接,转换为小写,标点符号被删除。然后将数据分为训练(80%)、验证(10%)和测试(10%)。我将自动调优命令设置为在具有 16 个内核的云机器上运行 24 小时,以在验证数据集上找到最佳参数。最后,在所有数据上重新训练模型,并在测试数据集上报告最终指标。对于每次执行, Valohai 负责启动和停止具有适当环境、代码、数据和参数的云机器。

探索预测和指标

自动调优执行聪明地尝试了 9 个不同的参数集来决定一个最终的模型,该模型被训练了 100 个时期,具有 92 维的词向量、多达 3 个词的 n 元语法和 2 到 5 个字符的子词。这导致了 300 万单词的词汇量(包括子单词)和一个训练 7 小时、重 2 GB 的模型。

分类任务可以用经典的指标来评估,如精度、召回率和 f1 值。自动调优执行记录最佳参数,并在验证数据集中报告得分为 0.41 的 f1。

自动调优执行的日志。

在极端多标签文本分类任务中,通常还会报告度量 P@k(进行前 k 个预测时的精度)和 R@k(进行前 k 个预测时的召回)。下面,我们可以看到不同 k 值的测试数据集的精度和召回率。R@k 从进行一次预测(k=1)时的 0.4 变为进行二十次预测(k=20)时的 0.71。

比较不同 k 值的测试执行的度量

标签分类报告

自然,不同的子条目之间的度量也不同。通过子编辑来探究 f1 分数直方图以及预测概率(p@1)和 f1 分数之间的关系是很有趣的。我创建了一个 Google Colab 笔记本来制作基于 test_predictions.csv 的图表。

自然,f1 分数和 P@1(模型给出的第一次预测的概率)之间存在正相关。尽管如此,在测试数据集上,p@1 仍落后于 f1 得分。比如模型说第一个建议有 0.8 的概率,就要取为 0.5。该模型可能需要针对更多的时期进行训练,以更好地调整概率。

指标很重要,但它们不应该阻止你查看数据。相反,度量标准会告诉你应该往哪里看。例如,如果我们考虑 f1 值接* 1 的子网格,我们会发现一个很好的特性泄漏的例子。

在子编辑 r/wood_irl 和 r/anime_irl 的情况下,大多数帖子的标题是子编辑的名称,没有文本和图像。在 r/holdmyfries 中,大多数帖子以 HMF 开头,这一特征泄漏可能会阻止模型从其他文本特征中学习。看看表现最差的子编辑,我发现几个流行的子编辑不一定有主题连贯性,比如 r/AskReddit。主题方面最连贯的子主题具有*均 f1 分数,例如 r/MachineLearning 的 f1 分数为 0.4。

子编辑混淆矩阵

该模型不仅与用户分配的子编辑相矛盾,而且与标签空间相矛盾。如果你看看子编辑之间的混淆矩阵,你会发现子编辑有相似的主题。我上传了长格式的混淆矩阵到这个谷歌电子表格,所以你可以用它来找到类似的子编辑。例如,这里有 10 对经常被模型混淆的子网格。label 列指的是人的选择,label@1 列指的是第一个模型选择。

看看类似于 r/MachineLearning 的子编辑,它给出了一些关于为什么模型不总是能够预测人类选择的见解。通常有多种可能的选择。希望该模型仍然可以在数据中存在一些噪声的情况下进行学习,甚至在它以高概率预测不同的子网格时纠正人类的选择。

用 FastAPI 创建预测 API 端点

即使考虑到所有这些限制,R@5 为 0.6 的训练模型也是有用的。这意味着有三分之二的时间,前五个预测包括人类在测试数据集上选择的子集合。备选预测可以帮助用户发现新的子主题,并自己决定是否在那里发布。

为了用最新的帖子提交来测试模型,我使用 FastAPI 创建了一个 API 端点。FastAPI 是优步和微软的机器学习团队使用的 Python web 框架,因为它具有紧凑的代码、使用 Pydantic 的数据验证、自动文档编制以及 Starlette 带来的高性能。

api.py 中的代码足以声明特征和预测模型,将训练好的模型加载到内存中并创建预测端点。

在 Github 上完成 api.py。

将预测 API 端点部署到 Valohai

使用 Valohai 的部署特性,我在 valohai.yaml 配置文件中用 Docker 映像和服务器命令声明了一个端点。

在 Github 上完成 valohai.yaml。

通过 Valohai UI,我可以将端点链接到由之前的执行生成的模型。这都是受版本控制的,因此您可以 A/B 测试不同的版本,进行 canary 发布,并在出现问题时进行回滚。

您可以使用 curl 调用 API 端点,并在请求体上使用 text 特性发出 POST 请求。

curl -X POST "https://valohai.cloud/arimbr/reddit-fasttext/findareddit/production/predict?k=5" -d “{\”text\”:\”I’m wanting to learn the basics of growing food at home. All indoors as I don’t have a suitable yard. So things that can grow in a pot.\”}”

您应该得到这样的 JSON 响应。

{“predictions”:[{“label”:”IndoorGarden”,”probability”:0.33},{“label”:”gardening”,”probability”:0.06},{“label”:”plantclinic”,”probability”:0.04},{“label”:”IWantToLearn”,”probability”:0.03},{“label”:”homestead”,”probability”:0.03}]}

懒得开终端?我们掩护你。Valohai 团队精心制作了一个 UI 来与模型 进行交互。

试试子编辑查找器

下一步是什么?

我已经看到了几个方面的改进,使模型更加有用。例如,从用户可能不知道的不太流行的子编辑中收集数据。对于 Reddit 用例,报告的 R@5 的 0.6 分已经帮助用户发现新的子编辑。如果我们从测试数据集中删除包含图像的帖子,分数可能会高得多。在不同的业务案例中,该算法可用于以高 p@1 概率自动分类文档,并支持业务用户手动分类较难的案例。

在我看来,下一步将是监测模型的性能如何随着时间的推移而演变,因为用户在 Reddit 上发布的内容存在自然的概念漂移,并解决这一问题。在下面的文章中,我将收集最新的数据,比较模型度量,并构建一个 CI/CD 机器学习系统,以经常重新训练和发布新模型。

如果你有一个带有文本和标签的数据集,你可以按照这个 教程****对你的数据重新训练管道。您可以使用它来构建生产基准,以对市场中的产品进行分类,支持知识库中的票据或法律文档。

有用的链接

  • S3: Reddit 发布数据集
  • Github: ML 管道代码
  • 谷歌电子表格:指标
  • Google Colab: 探索
  • 子编辑查找器 UI

最初发表于 https://www.valohai.com。

如何用逻辑回归模型对猫图片进行分类

原文:https://towardsdatascience.com/classifying-cat-pics-with-a-logistic-regression-model-e35dfb9159bb?source=collection_archive---------9-----------------------

为二元分类建立两层神经网络

1.介绍

当我全神贯注于学期考试或家务杂事时,我会发现自己总是被猫视频所吸引。更重要的是,猫用可爱的爪子做傻事所带来的快乐超过了拖延的罪恶感。我心想,为什么不把我最大的两个兴趣——猫和机器学习——结合起来,以更好地促进技术进步。因此,用 2 层神经网络(本质上是逻辑回归模型)对猫图像进行即兴分类。

本教程的重点是设计一个简单的逻辑回归算法,用神经网络的思维方式来区分猫图像和非猫图像。

这篇文章仅仅关注教程的抽象概念;要深入了解代码,请参考 Github 资源库这里的。该库还具有 L 层深度神经网络,其性能优于 2 层模型。

2.数据

2.1 数据汇总

给定包含以下内容的数据集:

  • 标记为猫(y=1)或非猫(y=0)的 m_train 图像的训练集。
  • 标记为 cat (y=1)或非 cat (y=0)的 m_test 图像的测试集。
  • 每个图像的形状为(num_px,num_px,3),其中 3 表示 RGB 分量的 3 个通道。因此,每个图像都是(高度=数量 px)和(宽度=数量 px)的*方。

2.2 数据重塑

为了方便起见,我们应该将图像的形状(num_px,num_px,3)整形为一个 numpy 数组的形状(num _ px∫num _ px∫3,1)。在这之后,我们的训练和测试数据集将被转换成一个 numpy 数组,其中每一列代表一个展*的图像。应该分别有 m_train 和 m_test 列。

图像到矢量转换

2.2 数据预处理

为了表示彩色图像,必须为每个像素指定红色、绿色和蓝色通道(RGB );因此,每个像素值实际上是范围从 0 到 255 的三个数字的向量。

机器学习中一个常见的预处理步骤是对数据集进行居中和标准化,这意味着我们从每个示例中减去整个 numpy 数组的*均值,然后用整个 numpy 数组的标准偏差除以每个示例。但是,对于图片数据集,将数据集的每一行除以 255(像素通道的最大值)更简单、更方便。

3.学习算法的一般架构

学习算法体系结构

展*的输入矩阵将通过加权矩阵的参数 W[i]和偏置向量 b[i]传递。然后通过将参数传递通过 sigmoid 激活函数来预测 Yhat 的值。从参数的值和它们对应的 Yhat我们可以通过计算成本函数来最小化成本;然后学习建模的参数。

算法的数学表达

4.构建算法

构建神经网络的主要步骤是:

  1. 定义模型结构(如输入要素的数量)
  2. 初始化模型的参数
  3. 循环:
  • 计算电流损耗(正向传播)
  • 计算电流梯度(反向传播)
  • 使用梯度下降更新参数

我们通常分别实现功能/步骤 1-3,然后将它们集成到一个我们称之为model()的功能中。

4.1 助手功能

我们将首先使用数学表达式实现 sigmoid 函数:

sigmoid 函数

4.2 初始化参数

我们必须将 W 和 b 初始化为零的向量。

注:常规上,在更深层次的神经网络(层数和神经元更多)中,我们通常随机初始化向量 W。这是因为第一个隐藏层中的每个神经元将执行相同的计算。因此,即使在梯度下降的多次迭代之后,该层中的每个神经元都将与其他神经元一样计算相同的东西。更多信息请参考 Github 库。

4.3 向前和向后传播

既然我们的参数已经初始化,我们可以开始学习参数的“向前”和“向后”传播步骤。

4.4 优化

既然我们已经初始化了参数并计算了成本函数及其梯度,我们将继续使用梯度下降来更新参数。

目标是通过最小化代价函数j来学习参数 wb 对于一个参数θ,更新规则为**θ = θ − αdθ**,其中α为学习率。

4.5 预测Yhat

前一个函数将输出学习到的 wb;因此,w e 能够使用 wb 通过实现predict()函数来预测数据集 X 的标签。计算预测有两个步骤:

  1. 计算ŷ= a =σ(w . t * x+b)
  2. a 的条目转换为 0(如果激活< = 0.5)或 1(如果激活>0.5);将预测存储在矢量Y_prediction中。

4.6 关键要点

上面实现的功能:

  • 初始化( wb )。
  • 迭代优化损失学习参数( wb )。
  • 计算成本及其梯度。
  • 使用梯度下降更新参数。
  • 使用所学的( wb )来预测一组给定示例的标签。

5.建模

现在,我们将按照正确的顺序把所有的构建模块(在前面的部分中实现的功能)放在一起,看看整个模型是如何构建的。

培训和测试准确性

训练准确率接* 100%。这是一个很好的健全性检查:我们的模型是有效的,并且有足够高的容量来适应训练数据。另一方面,测试准确率为 70%。考虑到我们使用的小数据集以及逻辑回归是一个线性分类器,对于这个简单的模型来说,这实际上并不坏。

成本函数和梯度

解读:可以看到成本在下降。这表明正在学习参数。但是,您会发现您可以在训练集上对模型进行更多的训练。尝试增加上面单元格中的迭代次数,然后重新运行这些单元格。您可能会看到,训练集的准确性提高了,但测试集的准确性却降低了。这叫做过度拟合。

6.进一步分析

6.1 学习率的选择

为了让梯度下降起作用,你必须明智地选择学习率。学习率 α 决定了我们更新参数的速度。如果学习率过大,我们可能会“超调”最优值。类似地,如果它太小,我们将需要大量的迭代来收敛到最佳值。这就是为什么使用一个合理的学习率是至关重要的。

让我们比较一下我们模型的学习曲线和几种学习率的选择。也可以尝试不同于我们已经初始化的三个变量的值,看看会发生什么。

解读:

  • 不同的学习率给出不同的成本,从而得到不同的预测结果。
  • 如果学习率过大(0.01),成本可能会上下振荡。它甚至可能会发散(尽管在这个例子中,使用 0.01 最终仍然是物有所值的)。
  • 更低的成本并不意味着更好的模式。你必须检查是否有可能过度拟合;当训练精度大大高于测试精度时,就会发生这种情况。

在深度学习中,通常建议您选择最能最小化成本函数的学习速率。如果您的模型过度拟合,使用其他技术来减少过度拟合,例如超参数调整。

6.2 结果分析

让我们来看一些模型标注错误的图片。这将显示一些标签错误的图像。

标签错误的图像

模型表现不佳的几种图像类型包括:

  • 猫的身体在一个不寻常的位置
  • 猫出现在相似颜色的背景下
  • 不寻常的猫的颜色和种类
  • 照像镜头视角
  • 图片的亮度
  • 比例变化(猫在图像中很大或很小)

7.使用 Github 存储库中您自己的图像进行测试!

这是我在 Unsplash 上找到的一个样本小猫图片,我决定在模型上测试它:

来自 Unsplash

猜猜模型输出是什么:

输出

万岁!虽然这是从一个简单的逻辑回归模型中推导出来的,但它仍然是值得的——猫总是赢家。前往 Github 知识库,深入了解如何使用两层神经网络和一层深度神经网络对猫图像进行分类。

参考

特别感谢 deeplearning.ai

[1]用于 2 层和 L 层深度神经网络分类的 Github 知识库:https://github.com/TheClub4/Cat_Classification

[2]对于自动重新加载外部模块:http://stack overflow . com/questions/1907993/auto reload-of-modules-in-ipython

用机器学习对《龙与地下城》中的角色分类

原文:https://towardsdatascience.com/classifying-character-classes-in-dungeons-dragons-with-machine-learning-86751240594d?source=collection_archive---------17-----------------------

Clint Bustrillos 在 Unsplash 上的照片

几个月前,一个朋友邀请我加入他的在线龙与地下城活动。尽管我有着令人尊敬的书呆子名声,但我从未真正玩过 DnD。我并不是反对它,事实上,它听起来很有趣,我喜欢它鼓励合作和机智的创造力。也许这可以避免我在新冠肺炎的单独禁闭中写论文所带来的单调生活。

我胡乱拼凑了一个我认为会很有趣的角色——,一个变异的阿西玛六刃术士。我并不热衷于阅读大量记录游戏元素的神圣书籍来优化我的术士,但我的新 DnD 同胞轻轻地把我推向了正确的方向,给魅力分配了最大的属性(唉,我有一只出汗的袜子的实际魅力来公正地进行角色扮演。)

在这样做的过程中,我开始思考:实际游戏的*衡性如何?是不是所有的优化都会导致相同的基本角色构建?或者我可以根据一个有限的角色信息子集推断出一个角色的类别吗?

每个分类器都需要数据

为了建立一个分类器并回答这个问题,我需要一个 DnD 字符的数据集。我在他的 Github上找到了一个由 B. Ogan Mancarci 编译的。它包含了超过 7900 个由用户通过他创建的网络应用表格提交的字符条目。他列出了一些关于数据集的潜在警告,这些警告似乎都不会妨碍我正在尝试做的简单研究。我将使用 python 和 pandas 和 sklearn 库作为标准工具集。如果您有兴趣跟随,可以在这个 git 资源库中找到包含该分析的(凌乱的)Jupyter 笔记本。

数据包含许多不同的特征,包括提交条目的用户元数据,以及感兴趣的实际字符数据。这些信息中的大部分是不必要的,因为我们主要感兴趣的是角色的统计数据和生命体征,即:

  1. 能力分数或属性,即 Cha(risma)、Con(constitution)、Dex(terity)、Int(elligence)、Str(ength)和 Wis(dom)
  2. 生命值(HP)
  3. 装甲等级(AC)
  4. 铸造状态(分类特征,{Cha,Con,Dex,Int,Str,Wis}之一)

提供了关于角色可用的法术和技能的附加信息,但由于以下几个原因,这些信息大多会被忽略:a)许多可能是特定职业所独有的,因此使分类变得琐碎,b)在完全扩展的数据中有数百个法术和技能,这显著增加了适度规模的数据集的维度。这很容易导致后期的过度拟合问题,除非它们能够以不同的方式进行提炼或浓缩。现在,我们也将忽略这些功能和设备。

数据探索

首先,我们将探索数据的一些主要特征,看看是否有任何需要解决的意外情况,或者是否有任何可识别的数据模式可以利用。从一个简单的问题开始:每个类中有多少在完整的数据集中被表示?检查不同类别是否*衡很重要。

与其他十几个类相比,数据集中 Artificer 样本的稀缺性是显而易见的,也是令人担忧的潜在原因。事实证明,工匠职业是 DnD 5e 佳能最*增加的,因此,还没有足够长的时间供玩家大量选择。这种不到整个数据集百分之一的不*衡可能会在以后产生问题。为了简单起见,我们将在本研究的其余部分忽略这个设计者——至少在有更多的设计者数据可用之前。其余的,虽然没有完全*衡,但仍在合理范围内,无需进一步调整。

DnD 为角色提供了追求多个职业的能力,只要满足特定的条件。对于这个练习来说,这增加了一层额外的复杂性,角色的职业没有很好的定义,玩家在潜在的多个不同方向发展他们的角色时会引入模糊性。为了提高我们的敏感度,我们将只考虑只有一个职业的角色构建,剩下的不到 7,000 个角色。现在让我们探索一下前面提到的不同角色属性的分布,看看是否存在可以区分职业的相关性。

能力分数、生命值和护甲等级的分布

不同属性和类别之间的关联热图

从热图中可以看出,某些属性与某些类别的关联可能比其他类别更大。另一方面,一些属性,比如说,战斗/战斗/敏捷施法属性,看起来在所有职业中几乎是一致的,这一点值得注意。

需要警惕的一点是,游戏中的角色进展允许玩家随着游戏的推进增加这些不同的属性。这可能是潜在的混淆,因为一个角色被扮演的时间长度在样本中不是恒定的。下面显示了不同属性与角色等级的关系。

我们看到角色的 HP 与角色的等级相关,Pearson 分数为 0.93,其他属性的趋势要小得多。为了说明这一点,我们将使用角色的 HP 除以角色的等级,而不仅仅是 HP。

在对这些分布的研究中,有一些异常值,与大部分人口相比,这些异常值似乎具有不自然的大值。尽管有许多处理异常值的选项,我们将对 HP/level 特性应用熟悉的 1.5*IQR 规则,删除 79 个观察值。

到目前为止,在这个初步的探索性分析之后,我们已经以增强我们的建模的统计能力的方式清理和准备了数据集,并且我们还基于类和属性之间存在的不同相关性确定了字符属性似乎是类之间可行的鉴别器。我们现在可以动手做一些初步的建模了!

二元建模

虽然最终目标是预测 12 个类别中的一个,但我们可以从一个较低复杂度的分类模型开始,以了解我们可以从数据中预期的性能。为此,我们将手动识别每个类是否是一个施法类,并构建一些基本的二元分类器。

我们应该如何决定我们是否成功开发了一个预测模型?我们需要一个成功指标来评估我们的结果。总的来说,这使我们能够比较一个模型与另一个模型的性能,并表明对模型的改进是否使预测朝着正确的方向发展。一种方法可以是准确性度量。我们的数据并没有严重失衡,所以这可能就足够了,但我们还可以做得更好,通过优化 F1 的分数,努力在精确度和召回率方面做得更好。这通常是特定于问题的,您应该决定哪个更重要,是最小化预测负面错误识别还是正面错误识别案例的频率。在我们的情况下,无论我们以何种方式识别错误,都不一定是危险的,但我们可以尝试惩罚假阳性,并尝试实现至少 80%的真阳性率(TPR),同时只允许 5%的假阳性率(FPR)。

我们将首先尝试 75%-25%训练/测试数据集分割的逻辑回归分类器,并使用 10 重交叉验证方法。我们可以评估测试集的性能,评估我们的成功指标,并检查混淆矩阵。

这挺好的!我们成功实现了 87%的 TPR 和大约 10%的 FPR。我们可以用 ROC 曲线来更深入地研究这个问题,看看如果稍微调整一下决策边界,我们是否能达到 80/5 的比率。

逻辑回归 ROC 曲线

事实证明,通过对概率阈值的特殊解释,我们确实可以在 TPR 为 80.4%的情况下实现精确的 5% FPR。我们丢失了一些真实的标识,因为这个阈值在分配肯定的标识符以抑制 FPR 时有点保守。我们可以尝试不同的机器学习模型,决策树(DT),看看它是否允许我们改进我们的建模,并提供更好的 TPR/FPR 比。使用网格搜索交叉验证方法调整 DT 的超参数以改进建模

马上,我们有 92.6%/6.3%的 TPR/FPR,这已经显示出潜在的改善!通过执行相同的练习和检查 ROC 曲线,我们确实找到了一个阈值,在这个阈值内我们可以获得 5%的 FPR 和高达 90%的 TPR!

决策树 ROC 曲线

多类建模

这很好,但是现在我们应该停止训练,解决将 12 个不同的类分类的原始问题。我们可以从同样的逻辑回归和 DT 技术开始。有各种方法来计算一个多类分类的总数值分数,但总的来说,DT 似乎比逻辑回归和另一个模型,支持向量机(SVM)更能成功地对大多数单个类进行分类。以下是(有点复杂的)混淆矩阵,以及多类 DT 的几个成功指标。

这显示了希望,然而,每个职业的建模能力有很大的差异,从成功的(牧师)到相当糟糕的(术士)。

集成学习

也许我们可以尝试使用集成方法来扩展 DT 建模,例如 XGBoost 和随机森林(RF ),从而增加一点复杂性。就其本身而言,XGBoost 并没有明显优于 DT。另一方面,使用 RF,我们似乎仍然能够挤出多一点的辨别能力来改善分类。

在这一点上,就我们能够从我们选择使用的有限数据中提取多少信息而言,我们可能正在接*极限。对于一些职业,即吟游诗人、巫师和术士,由于相似的优化建造,模型似乎很难区分这三个施法者。我们最初忽略了法术的使用,因为它们有效地识别了各自的职业,但也许我们可以注入间接的法术信息,而不用求助于法术本身。具体来说,我们可以将某个特定角色已经学会的个法术作为一个特征。然而,就像 HP 一样,一个角色所知道的法术数量是依赖于等级的,随着等级的提高,新的法术也会被获得。因此,我们将添加一个每级法术的特性到我们的数据集中。

我们可以使用这个新添加的列重新运行我们的 RF 模型。

通过为每个职业注入最少的法术信息,我们确实能够在这些职业之间提供更多一点的分离。它仍然不完美,但我们已经达到了一个可接受的阶段,我们可以回过头来检查使用 RF 模型进行分类的重要特征。

最终随机森林模型的特征重要性

这似乎是明智的,回想一下来自相关矩阵的 Con/Dex/Str casting stat 显示了跨类的一致性。这反映在这个图中,当涉及到分类时,重要性相应地降低。

从这里开始,我们可以继续改进模型,改进现有的特性,删除不重要的特性,或者在模型中加入额外的特性。也许某些种族有利于某些职业的优势,也许某个职业更有利于具有某种道德一致性的角色扮演,更不用说我们从未接触过的武器或技能信息,但可以以类似于法术的方式进行改编。最终,我们应该根据模型在哪里挣扎来决定,并进入需要更多信息来解决的具有最高相似性级别的类。

然而,这篇文章已经写了足够长的时间了,我相信我们已经设法解决了我们试图回答的原始问题。这些职业以独特的方式发展,鼓励不同的角色,让玩家扮演不同的角色。值得一提的是,我们可以总结测试的每个不同多类分类器的不同成功度量分数:

哦,当我们总结事情的时候,也许是为了记录,即使它看起来仍然和我的角色的职业有最大的冲突,我们可以通过我们的小 RF 模型让他看看会发生什么。🤞🏼

🤗

非常感谢 Lance McDiffett,他在一开始就把我拉到了 DnD,并以他作为数据科学家的经验为我的分析提供了宝贵的帮助。

将气候变化推文分类

原文:https://towardsdatascience.com/classifying-climate-change-tweets-8245450a5e96?source=collection_archive---------25-----------------------

利用 NLP 和分类技术将推文分类为气候变化信徒或否认者推文

来源:slidescarnival.com

背景

你好!在本文中,我将通过一个分类示例来识别 2017 年至 2019 年的气候变化推文,无论是气候变化“相信者”还是“否认者”。从哈佛数据世界下载了 2000 万条气候变化推文 ID,并进行“水合”(即填充了包含实际推文、推文详情、转发详情、用户信息等的 JSON 文件。)使用[twarc](https://github.com/DocNow/twarc)。然后,推文文件被保存在 mongoDB 数据库中,所有的转发都被过滤掉,剩下的推文在标签和文本预处理的帮助下被分类为“相信者”或“否认者”。这一分类过程涵盖了该项目的第一部分(点击此处查看 github repo),而下一篇文章涵盖了第二部分(点击此处查看 github repo),并涉及主题建模和推文聚类,以根据地理位置确定情绪趋势。

如何将这些推文归类为“信徒”或“否认者”?我们不能简单地用情绪分析来区分这两者,因为相信者和否定者都会对气候变化产生积极和消极的情绪。因此,分类器模型是在包含明确的“相信者”或“否定者”标签的推特子集上训练的。标签列表是从 tweet 数据库中手动搜索和评估的;请参见下面的表 1,了解使用的内容。应该指出的是,否认者的推文比相信者的推文少得多,两者的比例分别为 6%和 94%。为了进行度量评估,相信者的推文代表负面情况,否认者的推文代表正面情况。

表 1:“权威”信徒和否认者标签

在执行上述分类流程时,将涵盖以下主题:

  1. 文本预处理
  2. 基线模型管道
  3. 绩效指标评估
  4. 网格搜索
  5. 阈值选择
  6. 整个数据集的分类

文本预处理

一旦 tweets 的子集从全集中提取出来,并进一步分成训练集和测试集,CountVectorizerTfidfVectorizer被设置成将每个集转换成将用于建模的文档术语矩阵。可以通过 [CountVectorizer](/hacking-scikit-learns-vectorizers-9ef26a7170af)[TfidfVectorizer](/hacking-scikit-learns-vectorizers-9ef26a7170af)构建和实现自定义预处理和标记化功能。首先,预处理函数见下文;所有推文都被清除了以下特征:

  1. 换行
  2. URL 的
  3. 数字
  4. 大写字母
  5. 标点
def tweet_preprocessor(tweet): tweet = tweet.replace('\n', ' ') # remove line breaks
    tweet = re.sub(r"\bhttps://t.co/\w+", '', tweet) # remove URL's
    tweet = re.sub('\w*\d\w*', ' ', tweet) # remove numbers
    tweet = re.sub('[%s]' % re.escape(string.punctuation), ' ',   
            tweet.lower()) # remove capital letters and punctuation

    return tweet

接下来,见下面的记号化函数;t okenization 将每条推文分割成一个单词集合(即令牌)。词汇化旨在将每个单词缩减到其基本形式。例如:大笑,大笑,大笑,大笑都会变成大笑。这通过减少唯一单词的数量来降低分析的复杂性。这两种技术都内置在 spaCy 包中,该包在令牌化函数中使用:

# create a spaCy tokenizer
spacy.load('en')
lemmatizer = spacy.lang.en.English()def tweet_tokenizer(doc):

    tokens = lemmatizer(doc)
    return [token.lemma_ for token in tokens]

除了预处理和标记化,还有许多其他的矢量器超参数需要微调。例如:

  1. 最大词频:设置一个词可以出现的最大 tweets 数(例如,如果一个词出现在超过 80%的 tweets 中,那么它可能不是 tweets 之间差异的良好指标)。
  2. 最小词频:设置一个单词必须出现的最少推文数(例如,如果一个单词出现在少于 1%的推文中,那么它可能是一个不相关的术语,甚至可能是一个错别字)。
  3. ngram range :每条推文中的词应该如何分组?我们只对单词感兴趣吗?成对分组的单词?三胞胎?双人和三人组值得一看,因为他们可以帮助捕捉常见的短语(如“气候变化”)。
  4. 停用词:除了从推文中删除常用停用词(如“the”、“is”、“are”等。),所有确定的标签(上面列出的)都作为停用词包含在内。标签呈现了潜在的数据泄漏,这可能导致我们的分类器过度拟合,无法推广到其他推文。

矢量化被集成到分类管道中,这可以在下一节的代码中看到。

基线模型管道

最初管道的目标是挑选出一些潜在的基线模型用于进一步探索。为管道创建了矢量字典和模型字典;矢量器字典包含许多不同的CountVectorizerTfidfVectorizer变体,例如具有不同的 ngram 范围、max_df 和 min_df,模型字典包含分类器列表。见下文:

矢量词典

vectorizer_dict = {'CV_1': CountVectorizer(ngram_range=(1,3),
                           max_df = 0.8, min_df = 3, 
                           preprocessor=tweet_preprocessor, 
                           tokenizer=tweet_tokenizer, 
                           stop_words=new_stop_words),
                   ...
                   'TF_1': TfidfVectorizer(ngram_range=(1,3),
                           max_df = 0.8, min_df = 3, 
                           preprocessor=tweet_preprocessor, 
                           tokenizer=tweet_tokenizer, 
                           stop_words=new_stop_words),
                   ...}

模型词典

model_dict = {'Logistic Regression': LogisticRegression(),
              'Naive Bayes': MultinomialNB(),
              'LinearSVM': SGDClassifier(random_state=42),
              'Decision Tree': DecisionTreeClassifier(max_depth=6),    
              'XGBoost': XGBClassifier(max_depth=6)}

由于数据集不*衡,使用SMOTEADASYNRandomOverSampler来评估过采样的效果。没有一个选项执行得很好,所以它们从模型管道中被省略了。基准模型都用精度、召回和 F1 分数性能指标进行了评估。以下是每一个的快速定义:

  1. 精度:正确的阳性病例预测的百分比
  2. 回忆:正确预测的阳性病例的百分比
  3. F1 得分:精确度和召回率的调和*均值。

为了以高效的方式编译结果,在每次管道迭代后,这些指标都存储在一个字典中,同时还有相应的矢量器和模型名称。请参见下面的管道:

class_results_dict = defaultdict(list)for vec_name, vectorizer in vectorizer_dict.items():

    X_train_cv = vectorizer.fit_transform(X_train)
    X_test_cv  = vectorizer.transform(X_test) for mod_name, model in model_dict.items():
        model.fit(X_train_cv, y_train);
        y_pred_cv = model.predict(X_test_cv) precision_cv = precision_score(y_test, y_pred_cv)
        recall_cv = recall_score(y_test, y_pred_cv)
        f1_cv = f1_score(y_test, y_pred_cv)

        class_results_dict['Vectorizer Type'].append(vec_name)
        class_results_dict['Model Name'].append(mod_name)
        class_results_dict['Precision'].append(precision_cv)
        class_results_dict['Recall'].append(recall_cv)
        class_results_dict['F1-score'].append(f1_cv)class_results_df = pd.DataFrame(class_results_dict)

绩效指标评估

在这种情况下,F1 分数是评估每个模型的最佳指标。精确度和召回率都很重要,所以不需要权衡孰轻孰重。换句话说,第一类(假阳性)和第二类(假阴性)错误是相等的。看看class_results_dfCountVectorizer中的 F1 分数,在所有矢量器中,ngram 范围为(1,3)(即 CV_2)的表现最好,最有效的模型是逻辑回归和线性 SVM(SGD 分类器的默认)。请参见下面的表 2 中的class_results_df片段:

表 2:类 _ 结果 _df

Logistic 回归和 ngram 范围为(1,3)的 SGD 分类器通过网格搜索进一步优化超参数。

网格搜索

对于逻辑回归和 SGD 分类器,在进行单个网格搜索时,需要考虑多个超参数。对正则化的类型和强度进行评估,同时对逻辑回归评估不同类型的优化算法,并对 SGD 分类器评估不同类型的损失函数。代码见下文;利用GridSearchCV,其中选择 cv=5 折叠进行交叉验证。

from sklearn.model_selection import GridSearchCV# Logistic Regression
logit_params = [{'penalty': ['none', 'l1', 'l2']}, 
                {'solver': ['liblinear', 'lbfgs']},
                {'C':np.logspace(-5,3,num=9,base=10,dtype='float')}]gs_logit = GridSearchCV(LogisticRegression(), logit_params, cv=5,   
                        scoring='f1', error_score=0.0)
gs_logit.fit(X_train_cv, y_train)
y_true, y_pred = y_test, gs_logit.predict(X_test_cv)
f1_score = f1_score(y_true, y_pred)#SGD Classifier
sgd_params = [{'loss':
              ['hinge','log','modified_huber','squared_hinge']}, 
              {'penalty': ['none', 'l1', 'l2']}, 
              {'alpha': 
                np.logspace(-4,3,num=8,base=10,dtype='float')}]gs_sgd = GridSearchCV(SGDClassifier(), sgd_params, 
                      cv=5, scoring='f1')
gs_sgd.fit(X_train_ros, y_train_ros)
y_true, y_pred = y_test, gs_sgd.predict(X_test_cv2)
f1_score = f1_score(y_true, y_pred)

两个模型的 F1 分数都通过一个 l2 惩罚进行了优化。逻辑回归使用 lbfgs 解算器表现最佳,SGD 分类器使用 modified_huber 成本函数表现最佳(二次*滑 SVM 模型,其中 gamma = 2 )。

阈值选择

优化每个模型的 F1 分数的下一步是找到最佳概率阈值。分类器的默认阈值设置为 50%,即大于 50%的概率表示拒绝的推文,而小于 50%的概率表示相信的推文。逻辑回归混淆矩阵的视频见下图 1;阈值小部件显示 F1 分数达到最大值,阈值设置为 0.32。

比较两个模型的 F1 得分,逻辑回归得分最高,F1 得分为 0.803 。为了直观地确认 F1 分数,有必要查看精确度-召回曲线(ROC 曲线对于不*衡的数据集不太可靠,因此在这种情况下没有使用 ROC 曲线)。参见下面的图 2:

图 2:逻辑回归精度-召回曲线

可以看出,当精度等于 0.870 且召回等于 0.746 时,曲线的最佳点出现;这些值计算出 F1 分数为 0.803。

为了提高这个分数,测试了不同的“确定的”标签组合(添加某些标签,删除某些标签,等等)。),但是事实证明,使用表 1 中使用的标签列表时,模型的性能最好。

整个数据集的分类

确定逻辑回归模型和阈值后,对整个数据集进行最终分类。为每条推文生成预测概率,并应用 0.32 的阈值。参见下面的代码:

believer_denier_preds = []
y_final_predict = logit.predict_proba(X_final_test)for i in y_final_predict:
    if i[1] < 0.32:
        believer_denier_preds.append(0)
    else:
        believer_denier_preds.append(1)

因为推文没有预先确定的“正确”标签,所以需要人工评估来决定模型是否是有效的分类器。一个很好的初始检查是计算拒绝者推文与相信者推文的比率,并查看它是否与推文训练集中的比率相似(94%相信者对 6%拒绝者)。这个比例是 90%的相信者对 10%的否认者,这与训练集的比例大致相当。更深入的人工检查证实了分类器是有效的。

请随意查看 github repo,了解本文中详细描述的分类过程,位于此处。当在 tweet 数据集上执行主题建模和聚类时,最终会使用这种分类。这个过程的 github repo 可以在这里找到。

感谢阅读!

基于量子增强迁移学习的文档分类

原文:https://towardsdatascience.com/classifying-documents-with-quantum-enhanced-transfer-learning-8ee6d04f3ccd?source=collection_archive---------21-----------------------

用变化的量子电路来补充预先训练的模型,将我们带到了量子增强的自然语言处理时代

最*,一个趋势非常明显:需要大型的预训练模型来实现计算机视觉和语言理解的最先进的性能。特别是,随着基于 Transformer 架构的模型的出现,自然语言处理(NLP)领域的能力和网络规模都在呈指数级增长(例如伯特、 GPT-2 )。在本文中,我将通过使用通用语句编码器(USE)嵌入和一种称为变分量子电路的量子机器学习操作符,重点关注短文档(科学论文的预印本)的分类,粗略地说,这相当于经典机器学习领域中的全连接(密集)层。关于单词和句子嵌入如何用于分类的介绍,参见我之前的文章用通用的句子嵌入对科学论文进行分类。如果你不熟悉量子计算的概念,我希望你会喜欢阅读我的文章CERN 的幽灵计算机。

量子机器学习

在我们深入研究实际代码(你可以在https://github.com/rdisipio/qnlp上找到)之前,需要几句话来解释什么是量子机器学习,以及它如何对这种应用有用。

首先,在量子计算中,人们处理称为量子位的等效位,由于量子系统的一种称为叠加的属性(有时据说它们可以同时代表 0 和 1),量子位众所周知可以处理非二进制状态的信息。量子电路是应用于量子位的一系列操作,通过改变它们的相对相位来改变它们的状态,例如。量子位可以用所谓的布洛赫球进行几何表示,因此对量子位的操作对应于这个虚拟空间中量子状态向量的旋转。

布洛赫球的图形表示。对量子位的量子操作对应于这个虚拟空间中量子状态向量的旋转。[鸣谢:维基百科]

根据计算的目的,有许多可能的量子电路。一般来说,量子电路实现一个量子算法。关于什么样的计算是可能的,请参考微软的斯蒂芬·乔丹维护的量子算法动物园。最著名的大概就是彼得·肖尔的因式分解算法。量子算法的一个关键概念是,由于固有的自然量子位,某些计算可以以比经典等价计算更小的复杂性来执行(这种特性被称为超多项式加速)。完全公*地说,也有很多正在进行的研究试图“去量子化”这种算法,并使经典版本和量子版本一样快——这是因为量子比特的初始状态必须在运行实际算法之前准备好,这降低了整体加速。这一方向的开创性论文是 Ewin Tang 的“量子启发的经典主成分分析和监督聚类算法”。

事实上,实际应用更可能是经典和量子运算的混合。尤其是在机器学习领域,人们可能会想,这两个领域是否可以结合在一起,联手合作,好消息是,事实上他们可以,他们也确实这样做了。加拿大初创公司 Xanadu 开发的一个名为 PennyLane 的软件库提供了常见 ML 框架如 PyTorch 和 TensorFlow 与量子计算生态系统之间的接口。主要的想法很简单,但非常强大:人们可以用量子对应物代替神经网络的一部分(比如说致密层),就是这样!你创造了一个混合经典量子神经网络。事实上,由于经典机器学习并不仅限于神经网络,量子机器学习也在各种方法中找到了它的优势,如量子支持向量机。为了全面了解什么是可能的,我建议关注 edX 上的在线课程,该课程由多伦多大学已故的彼得·魏特克教授。

变分电路(也称为参数化量子电路)是一系列混合量子经典算法,以类似于经典致密层的方式执行操作。该算法的核心是依赖于一组参数𝜃的量子电路 𝑈,以及定义待优化标量的目标函数。电路可以被认为是一个黑盒,优化通常是通过迭代方案实现的,例如通过基于梯度的优化。

变分量子电路示意图。[鸣谢:Xanadu.ai]

文件分类

正如我在用通用句子嵌入分类科学论文中解释的,谷歌在tensorflohub上发布了一个预训练版本的通用句子编码器(使用)模型。要使用它:

import tensorflow_hub as hub
embed = hub.load("https://tfhub.dev/google/universal-sentence-encoder/4")
embeddings = embed([
    "The quick brown fox jumps over the lazy dog.",
    "I am a sentence for which I would like to get its embedding"])

输出由一个 512 维的向量列表组成,每个向量对应于输入中的一个文本字符串(在上面的例子中例如“快速棕色狐狸…”)。按照迁移学习范式,这个模型可以作为一个更大的网络的一部分。人们可以直接使用它,其参数是固定的,或者通过允许优化者修改它们来微调它。反过来,使用模型的输出被传递到其他层,以便为给定的下游任务训练网络的其余部分。有几个来自官方文档的例子,但是文档分类可能是最直观的。在这个例子中,我们将把属于两个非常不同的类别(即astro-phcs.AI)的预印本的摘要转换成句子嵌入,并附加一个具有两个输出的密集层。每个输出节点对应一个类别,因此在将输出与实际标签进行比较之前,应用一个 softmax 激活功能。这是一个简单模型的样子:

有趣的是,人们可以在几秒钟内只用 1000 个例子训练这样的模型,但却获得 99.6%的准确率。

为了“量子化”我们的模型,我们必须用一个变分的量子电路来代替位于嵌入和输出之间的层。典型地,经典密集层具有 N 个输入和 M 个输出,因此在内部,它对应于矩阵乘法,随后是偏置的相加,以及激活函数的应用,,例如:

def call(self, inputs): x = tf.matmul(inputs, self.W1) + self.b1
   x = tf.math.tanh(x) return x

不幸的是,量子层不能明确地做到这一点,而是需要修饰。这意味着量子变分层实际上由三个操作组成:

  • 一个经典的密集层,将维度从 N_inputs 转换为 N _ qubits,并将输入缩放π/2(因此它可以表示围绕 Bloch 球的旋转)
  • 改变量子位状态的量子层
  • 一个经典的稠密层,将维度从 N 个量子位转换成 N 个输出

为了用 PennyLane 库做到这一点,必须定义一个执行量子操作的设备(例如模拟器default.qubit,或者一个真实的设备,比如由 IBM Q 或者 Rigetti 提供的设备)。然后,实际电路被编码到 python 函数中,例如:

def _circuit(inputs, parameters): qml.templates.embeddings.AngleEmbedding(inputs, wires=list(range(self.n_qubits))) qml.templates.layers.StronglyEntanglingLayers(parameters, wires=list(range(self.n_qubits))) return [qml.expval(qml.PauliZ(i)) for i in range(self.n_qubits)]

我们可以将许多致密层堆叠在一起,以增加网络的深度,量子变分层也可以做到这一点。在这个例子中,我们将保持简单,用四个量子位和两层快速运行。

最后,有一个包装类QNode负责将命令发送到后端设备,并得到结果。最值得注意的是,这个类通过简单地指定一个参数,例如,为 TensorFlow 和 PyTorch 提供了接口:

self.layer = qml.QNode(_circuit, self.dev, interface="tf")

整个量子模型看起来像这样:

结果和讨论

使用模拟器训练分类器的量子版本需要长得多的时间(每个时期大约一分钟,批量大小为 32)。这并不奇怪:如果我们有有效的量子过程模拟,我们首先就不需要量子硬件了!事实是,这种东西在原则上是不存在的,因为量子物理具有像纠缠这样的性质,而这在经典领域是没有对应的。因此,要传达的信息是,量子过程的模拟需要很长时间,并且随着量子比特数量的增加,变得越来越难以管理。另一方面,现实生活中的量子硬件是嘈杂的,因此约翰·普雷斯基尔定义我们目前生活在嘈杂的中等规模量子(NISQ)技术时代。事实上,热噪声限制了在不引入误差的情况下进行长时间计算的能力,并且可能是今天实现创建大规模、可靠的量子硬件的目标的主要障碍。

也就是说,与纯经典模型相比,混合网络能够以 98.8%的准确度执行分类。这也不应该太令人惊讶,因为繁重的工作实际上是由使用嵌入层完成的。然而,这实际上是一个了不起的结果:它意味着我们可以部署迁移学习,这样我们就不必为了同样的目的训练一个纯粹的量子网络,而只是将其用于下游的任务。在 PennyLane 网站上有另一个关于图像分类的迁移学习的例子,我鼓励大家去看看。

量子增强的自然语言处理时代即将到来!

时尚服装分类-计算机视觉入门

原文:https://towardsdatascience.com/classifying-fashion-apparel-getting-started-with-computer-vision-271aaf1baf0?source=collection_archive---------59-----------------------

通过创建一个对时尚服装图像进行分类的模型,开始学习计算机视觉。

在本指南中,您将训练一个神经网络模型来对衬衫、外套、运动鞋等服装图像进行分类。

咻!这对于初学者教程来说听起来太多了,我的意思是我们才刚刚开始,对吗?

不要担心!不要不知所措,不了解全部细节也没关系。相信我,随着你对这篇文章的深入研究,你会了解到所有的细节。

如果你对机器学习完全陌生,我建议你看看我的初学者教程。

这里是完成的 Colab 笔记本和 GitHub 回购。

说完了,我们开始吧!

数据

我们将使用时尚-MNIST 数据集。它是由 10 种服装的 60,000 个正方形(28x28 像素)灰度图像组成的数据集。

每件衣服都有一个特定的标签:

0- T-shirt/top1- Trouser2- Pullover3- Dress4- Coat5- Sandal6- Shirt7- Sneaker8- Bag9- Ankle boot

时尚 MNIST 数据集|来源:ZALANDO RESEARCH

让我们来看看代码

我们将使用 TensorFlow 和 TensorFlow Keras 来构建我们的模型。

TensorFlow 是一个机器学习的端到端开源*台。它有一个全面、灵活的工具、库和社区资源的生态系统,让研究人员推动 ML 的最新发展,开发人员轻松构建和部署 ML 驱动的应用程序。

Keras 是 TensorFlow 用于构建和训练深度学习模型的高级 API。

你可以了解更多。现在对这些工具有一个基本的了解就足够了,因为随着时间的推移,你会学到更多关于 TensorFlow 和 Keras 的知识。

少说多码!!!

导入库

我们将使用 numpy 和 matplotlib 作为辅助库。

我们从导入所有必需的库开始

导入数据

时尚-MNIST 数据在 Keras 数据集中很容易获得。

我们从 Keras 数据集导入时尚 MNIST 数据

这将把fashion_mnist数据加载到 4 个 NumPy 数组中:

  • train_imagestrain_labels数组是训练集——模型用来学习的数据。
  • 根据测试集、test_imagestest_labels阵列对模型进行测试。

探索数据

下面的代码显示有 6000 个训练图像和 1000 个 28x28 像素的测试图像。我们将在训练图像上训练模型,并通过在测试图像上执行预测来测试模型的性能。图像在train_labelstest_labels中有相应的标记。

我们发现我们加载的数据

Train Images Shape: (60000, 28, 28)
Train Labels Shape: (60000,)
Test Images Shape: (10000, 28, 28)
Test Labels Shape: (10000,)

现在让我们来看看刚刚加载的数据。

让我们来看看加载的图片!

从时尚 MNIST 数据集中加载的图像

由于像素值介于 0–255 之间,我们将其转换为介于 0 和 1 之间的值。即我们只是将像素值除以 255.0。

使图像正常化

创建模型

构建神经网络需要配置模型的层,然后编译模型。

层是神经网络的基本构建模块。他们从输入的数据中提取特征或表示。经过训练后,这些功能将帮助我们解决手头的问题——对时尚服装进行分类。

在这里,我们将链接一些简单的层来创建我们的模型。

我们通过链接层来构建我们的模型

网络的第一层,TF . keras . layers . flatten,将 2D 阵列(28×28 像素)的图像转换为 1D 阵列(大小为 28*28 = 784)。它基本上接受输入图像,将每行像素背靠背排列起来。该层仅用于转换数据。

一旦输入图像被展*层转换,网络就有两个 tf.keras.layers.Dense 层。

这些是井,密集连接或完全连接的层。

紧密相连的层

第一密集层有 128 个神经元,第二密集层是我们网络的最后一层,有 10 个神经元。网络的最后一层是输出层,它将提供模型的输出。10 个节点中的每一个都将包含指示当前图像属于 10 个类别之一的概率分数。(请记住,我们的数据中有 10 种服装分类)

编译模型

我们几乎准备好训练我们的模型了!在此之前,我们必须再配置一些设置。

损失函数:测量模型在训练过程中的精确度。您希望最小化该函数,以便将模型“导向”正确的方向。即,该模型试图通过训练的每一步来最小化损失函数,以改进该模型。

优化器:优化器更新权重参数,使损失函数最小化。

指标:指标是一个用来判断你的模型性能的函数。度量函数类似于损失函数,只是在训练模型时不使用评估度量的结果。以下模型使用准确度,即图像被正确分类的比例。

我们编译这个模型

你现在不需要知道损失函数sparse _ categorial _ cross entropy或 adam 优化器的所有细节。如果你需要了解更多,你可以查看文档。现在,掌握什么是损失函数和优化器就足够了。

训练模型

为了训练我们的模型,我们简单地将我们的训练数据和标签分别包含在train_imagestrain_labels中。

我们称之为model.fit方法来“拟合”训练数据的模型。

最后我们训练我们的模型

Epoch 1/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3768 - accuracy: 0.8636
Epoch 2/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3394 - accuracy: 0.8762
Epoch 3/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3145 - accuracy: 0.8851
Epoch 4/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2965 - accuracy: 0.8902
Epoch 5/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2818 - accuracy: 0.8957
Epoch 6/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2698 - accuracy: 0.9002
Epoch 7/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2582 - accuracy: 0.9043
Epoch 8/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2495 - accuracy: 0.9074
Epoch 9/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2409 - accuracy: 0.9095
Epoch 10/10
1875/1875 [==============================] - 4s 2ms/step - loss: 0.2324 - accuracy: 0.9137

我们可以看到模型训练时显示的损失和准确性指标。随着模型的训练,损耗降低,精度提高。太棒了。你的模特在学习!

评估模型

该模型对训练数据的准确率约为 90% (0.90)。您可能有大约 90%的值(不要担心它是否略有不同,因为它可能有一些随机性)

但这还不够!我们还没有测试这个模型。我们现在将在我们的测试数据上测试我们的模型,这是模型从未见过的!让我们看看它的表现如何。

让我们看看我们的模型在测试数据上表现如何!

313/313 - 1s - loss: 0.3366 - accuracy: 0.8838

Test accuracy: 0.8838000297546387

事实证明,测试数据集上的准确性比训练数据集稍低。这可能意味着我们的模型过度拟合了我们的训练数据。我们现在不用担心这个。在以后的文章中,我们将讨论是什么导致了过度拟合,以及我们如何防止它。

做预测

终于!我们现在可以使用我们的模型对图像进行预测。这里我们有一个函数来绘制 100 个随机测试图像和它们的预测标签。如果预测结果与test_labels数据集中提供的标签不同,我们将用红色突出显示。

我们构建一个函数来查看模型的预测

我们有一些错误,但对于这样一个简单的模型,它做得相当好!

哇!你做到了!您已经成功地创建了一个模型,它可以查看时尚服装的图像,并以良好的确定性对它们进行分类!仔细想想,只需要几行代码。

我们看到了一些错误,但是对于我们的第一个模型,事情看起来相当不错!

完整的 Colab 笔记本可以在这里获得,代码也可以在 GitHub 获得。

有了 TensorFlow、Keras 和一般机器学习的新知识,您将能够为各种数据集创建自己的模型。此外,您在这里学到的工具和技术是实践中使用的复杂模型的基础。

在接下来的教程中,我们将看看卷积神经网络——一种广泛用于计算机视觉应用的神经网络。我们将看到,我们可以使用 CNN 进一步提高我们的模型的准确性。

快乐编码!

时尚服装分类-卷积神经网络入门

原文:https://towardsdatascience.com/classifying-fashion-apparel-getting-started-with-convolutional-neural-networks-3ae4fc5d9f76?source=collection_archive---------52-----------------------

通过创建卷积神经网络(CNN)对时尚服装图像进行分类,开始学习计算机视觉。

来源: Lum3n 通过像素

在本指南中,我们将改进我们在上一节中训练的服装图像分类模型。

如果你是机器学习或使用 TensorFlow / Keras 的新手,我会建议你看看第一部分,在那里我们以一种更容易和初学者友好的方式解决了同样的问题。

那好吧。让我简单介绍一下我们将要做的事情:

我们将关注一种新的深度学习神经网络,称为卷积神经网络。它们最常用于计算机视觉应用,非常适合手头的任务。

简而言之,CNN 使网络能够将输入图像视为边缘、角落和颜色的组合,本质上捕捉了图像的内容。

要从更技术性的角度来看 CNN,我建议你浏览一下这篇文章。

在本指南中,我们不会过多地研究 CNN 是如何工作的,而是将重点放在如何使用 TensorFlow 和 Keras 在代码中实现它。

这里是完成的 Colab 笔记本和 GitHub 回购。

数据

像之前的指南一样,我们将使用时尚-MNIST 数据集。它是由 10 种服装的 60,000 个正方形(28x28 像素)灰度图像组成的数据集。

每件衣服都有一个特定的标签:

0- T-shirt/top1- Trouser2- Pullover3- Dress4- Coat5- Sandal6- Shirt7- Sneaker8- Bag9- Ankle boot

时尚 MNIST 数据集|来源: ZALANDO RESEARCH

代码

我们将使用 TensorFlow 和 TensorFlow Keras 来构建我们的模型。

我们将通过使用 CNN 作为我们的神经网络来扩展我们的知识。

我们将像在之前的教程中一样预处理图像。如果您不确定,请查看此链接。

完整的代码可以在 Colab 笔记本和 GitHub repo 中找到。

在这一部分中,我们将直接开始创建模型。本质上,我们正在将我们新的和改进的 CNN 模型插入到我们以前的代码中。

创建模型

我们的模型将一个 28px x 28px 的灰度图像作为输入,输出一个长度为 10 的浮点数组,表示图像是服装的概率。(数据集中有 10 种不同的服装,标记为 0-9)

让我们看看我们的模型,然后我们将通过每一层。

首先,我们添加一个与我们的输入形状匹配的输入层。

然后我们改变输入的形状,增加一个维度来表示我们图像的颜色通道。因为我们示例中的图像是灰度图像,所以颜色通道的数量是 1。

然后,我们有我们的第一个卷积 Conv2D 层。我们有 32 个内核大小为 3×3 的过滤器。

下面是一张 gif 图,展示了卷积是如何工作的。你可以查看这篇文章了解更多细节。( 嘘……如果你还没有 ,我建议你去看看)

卷积在行动!|来源: vdumoulin

接下来,我们有另一个 Conv2D 层,有 64 个滤镜和 3x3 内核。你可能已经从代码中猜到了。

接下来我们有新的东西,一个池层。

你问什么是池层?为什么说我什么时候可以给你看?我认为这张 gif 是不言自明的。

MaxPooling Layer |来源:谷歌

如果这还不能说明问题,是的,你猜对了,看看我经常提到的那篇文章吧!

接下来是辍学层。它所做的是在训练过程中随机忽略或“丢弃”网络中的一些层。这可以防止网络中的单元过度适应训练数据。

然后,我们展*前一层的输出,并将其传递给一个密集连接的网络,这将是我们的输出层。

最终层中的 10 个节点中的每一个都将包含指示当前图像属于 10 个类别之一的概率分数。(请记住,我们的数据中有 10 种服装分类)

然后,我们像在之前的指南中一样编译和训练模型。

完整的代码可以在 Colab 笔记本和 GitHub repo 中找到。

评估模型

我们的模型对测试数据有大约 92%的准确率。这比我们以前的型号大约增加了 5%。

313/313 [==============================] — 1s 2ms/step — loss: 0.3239 — accuracy: 0.9203 Test accuracy: 0.9203000068664551

模型似乎仍然过度拟合训练数据。但是我们会把它留到下一次。我们现在不想让事情变得太复杂。

但是有了这个指南,我希望你能够得到在 TensorFlow 和 Keras 中使用 CNN 的 101 指南。在接下来的指南中,我们将看看如何通过使用一种叫做迁移学习的技术来进一步改进我们的模型。

我们还将学习图像增强技术,以便我们不会用训练数据过度拟合我们的模型。在那之前,

快乐编码!

分类同质数据,你需要知道的!

原文:https://towardsdatascience.com/classifying-homogeneous-data-what-you-need-to-know-7b3a86e5f855?source=collection_archive---------16-----------------------

过去一些研究人员做的很粗略,你应该做得更好!

我在北岭加州州立大学和刘莉教授一起做人机交互(HCI)的硕士论文。在此期间,我发现了一些我们作为数据科学家可以改进的地方。我没有统计学学位,也没有在网上找到以前的相关工作,但我在大量相关论文中看到了这个问题,并决定与大家分享我的发现。我认为我们需要重新考虑处理同质数据的方式。

图片来自 Pixabay

什么是同质数据?

如果一个数据集是由彼此相似的事物组成的,那么它就是同构的。在本文的范围内,它意味着来自完全相同来源的数据。在监督学习的典型场景中,这将导致数据集在整个集合中具有完全相同的标签。

人们是如何处理同质数据的?

费边论文中的非加权概率和

在大多数过去的研究中,比如法比安关于击键动力学的论文,研究人员会根据所有计算出的概率的总和来评分。

人工神经网络的 soft-max 层本质上做同样的事情。它向网络的下一层中的节点输出加权和。

这背后的直觉很简单:

直觉:如果更多的推理倾向于一个结果,那么这个结果最有可能是正确的。

但远非完美…

我们在处理同质数据时遇到了什么问题?

概率总和背后的直觉是强大的,但远非完美。考虑这样一种情况,我们有两类数据,分别标记为 A 和 B。数据的分布如下图所示,其中 B 的分布区域由 A 包围。现在,如果我们发现样本来自区域 B,则样本可能具有标签 A 或 B。

典型数据分布图

当两个标签的样本数量相似时,我们可以获取多个样本,然后判断它们属于 B 类。这是因为 B 区域较小,因此 B 类的密度较高。因此,在 B 区域中,B 类比 a 类更有可能被找到。概率总和仍然有效。然而,如果标签为 B 的样本少得多,或者如果区域大小相似且重叠而不是封闭,我们可能会从 A 类获得比 B 类更多的样本,并且无法正确地对其来源进行分类。

调整与偏差成比例的概率和的权重肯定会有助于这些情况,但这种*似是粗略的…

我们应该如何处理同质数据?

在我们开始之前,让我们做 3 个假设:

  1. 多个获得的样本来自相同的来源。
  2. 抽样过程在概率上是独立的。
  3. 每个等级在他们的区域内都有一个概率分布。

现在我们可以考虑下面展示的两种直觉。为简单起见,让我们假设在每个区域内均匀分布。

典型数据分布图

直觉 1:如果样本来自 A 区但在 B 区之外,那么来源就是 A 类。

直觉 2:如果样本总是在区域 B 内,那么源极有可能是 B 类。

据此,我们可以推导出以下等式:

具有特定标注的采样要素的概率

现在让我们假设两个圆内均匀分布的点。假设红色圆圈 A 覆盖的面积是蓝色圆圈 B 的两倍,并且每个标签的样本数量相等。然后,如果我们随机选择一个位于蓝色圆圈内的点,因为红色点的密度是一半,所以选择的点实际上有 2 / 3 的机会属于蓝色区域。

如果我们从蓝色圆圈内进行 10 次观察,总几率不会改变。但是如果我们应用我们的新技术,我们会得到:

当我们已经看到 10 个样本都来自蓝色圆圈内时,这是一个更有可能的估计。

变异 k-NN 的性能

为了测试这一点,我创建了自己版本的 k-NN。它是原始 k-NN 的变异版本。这里是如何在 Node.js 中基于 k-d 树构建自己的 k-NN 的教程。输入数据来自我最*的一个实验,正如你所看到的,这些点彼此重叠。

重叠数据

性能如下表所示,其中 n 是选择 u 样本的次数。由于变异的 k-NN 的性能由于数据量小而不稳定,所以用随机混洗的数据进行了多次实验。

变异 k-NN 的性能

正如我们所看到的,变异后的 k-NN 总体上确实有超越性的表现。只有当数据分布在如上所述的封闭或严重重叠的情况下,性能改进才会更好。这将为我们处理这些类型的数据提供一些见解。

注释在最后…

我希望这篇文章对你有所帮助。我是一名计算机专业的学生,对很多事情都感兴趣。写作也是我的爱好,因为我喜欢把事情弄清楚并与他人分享,所以如果你在 Medium 上跟随我,你会看到更多这样的事情。

如果你感兴趣的话,我还有一篇关于我在数据科学领域的发现的非常老的文章:

苹果的失败如何给我们数据科学家上了一课

利用 fast.ai v2 对酒精饮料图像进行分类

原文:https://towardsdatascience.com/classifying-images-of-alcoholic-beverages-with-fast-ai-34c4560b5543?source=collection_archive---------16-----------------------

实践教程

U sing fast.ai v2 和 Google Colab 提供令人陶醉的数据和 dram 混合服务

照片由 Q.U.I 在 Unsplash 上拍摄

动机

我已经开始沉浸在更新的 fast.ai v2 深度学习课程中,我觉得应用和记录我迄今为止所学的东西将是理想的。在这篇文章中,我将分享我如何训练深度学习(CNN)分类器来区分不同类型的流行酒精饮料。

本演练的一个重要亮点是,它详细介绍了有关如何利用更新的 Microsoft Azure Bing Search V7 API 的最新说明,因为关键更改已于 2020 年 10 月 30 日实现。

链接

  • GitHub 回购
  • Jupyter 笔记本(完整版)
  • Jupyter 笔记本(干净版)

内容

第 1 节—设置

第 2 部分—下载图像数据

第 3 节—准备图像数据集

第 4 部分——训练模型

第 5 节—清理数据

第 6 节—使用图像分类器进行推理

第 7 部分—部署为 Web 应用

第 1 部分—设置

首先,我强烈推荐在 Google Colab 上运行这款笔记本。要了解更多关于 Google Colab 设置的信息,请访问此链接。

一旦这台笔记本在 Google Colab 上打开,打开笔记本的 GPU 加速器,方法是前往顶部菜单,单击**Runtime**>**Change runtime type**>,选择 **GPU** 作为硬件加速器。

接下来,在 Google Colab 运行时上安装 Google Drive。将出现一个链接,供您点击并检索您的授权码。授予 Google Drive 文件流的权限后,复制提供的代码,将其粘贴到“输入您的授权码”下的字段中,然后按 Enter 键。

from google.colab import drive
drive.mount('/content/drive/')

下一步是安装 fast.ai 依赖项。我发现这种依赖版本的组合在 Google Colab 上工作得很顺利。

!pip install fastai==2.0.19
!pip install fastai2==0.0.30
!pip install fastcore==1.3.1
!pip install -Uqq fastbookimport fastbook
from fastbook import *
fastbook.setup_book()
from fastai.vision.widgets import *

import warnings
warnings.filterwarnings("ignore")

import requests
import matplotlib.pyplot as plt
import PIL.Image
from io import BytesIO
import os

from IPython.display import Image
from IPython.core.display import HTML

然后,我们创建一个路径来存储将要下载的图像。注意,结果目录路径将是/content/images

try:
  os.mkdir('images')
except:
  pass

之后,我们需要检索 Azure Bing Search V7 的 API 密钥,因为我们将使用它从 Bing 中提取图像数据集。要了解更多关于在微软 Azure 门户中设置 Bing 搜索 API 密钥的信息,请查看我的 GitHub repo 中的自述文件,以及微软 Azure Bing 搜索 API 快速入门指南。

subscription_key = "XXX" #Replace XXX with your own API key
search_url = "https://api.bing.microsoft.com/v7.0/images/search"
headers = {"Ocp-Apim-Subscription-Key" : subscription_key}

一旦关键字被输入到subscription_key变量中,我们就可以检索一组与您选择的关键字相关的图像 URL。例如,为了找到whisky的一组图像,我们运行以下代码:

search_term = "whisky"#Add in count parameter so that max number of images (150) is  #retrieved upon each API call. Otherwise, default is 35.params  = {"q": search_term, "license": "public", "imageType": "photo", "count":"150"}response = requests.get(search_url, headers=headers, params=params)
response.raise_for_status()# Return json file
search_results = response.json()# Create a set of thumbnails for visualization
thumbnail_urls = [img["thumbnailUrl"] for img in search_results["value"][:16]]

我们可以创建一个 4 x 4 的缩略图网格图来可视化检索到的图像,允许我们验证这些图像确实代表了我们的关键字whisky。从展示的内容来看,我觉得这的确很像上好的老威士忌。

f, axes = plt.subplots(4, 4)
for i in range(4):
    for j in range(4):
        image_data = requests.get(thumbnail_urls[i+4*j])
        image_data.raise_for_status()
        image = Image.open(BytesIO(image_data.content))        
        axes[i][j].imshow(image)
        axes[i][j].axis("off")
plt.show()

下一步是整理来自搜索结果 JSON 文件的图像 URL 列表。与图像 URL 相关联的关键字是contentUrl

img_urls = [img['contentUrl'] for img in search_results["value"]]
len(img_urls)

len函数应该会返回一个与关键字whisky相关的 150 个图片 URL 的列表。然后,我们从 URL 下载并显示一张图片到我们的images文件夹中名为whisky_test.jpgdestining 文件中。

dest = 'images/whisky_test.jpg'
download_url(img_urls[1], dest)img = Image.open(dest)
img.to_thumb(224,224)

我们得到了上面的威士忌图片,显示上面的代码块完美地工作。我们现在准备好了主要的行动。

第 2 节—下载图像数据

更新:由于图片下载方式是动态的,请参考 fast.ai 图片部分 了解图片下载的最新方式

区分常见酒类的图像会很有趣,即威士忌、葡萄酒和啤酒。为此,我们首先在一个列表中定义三种酒精类型,并创建一个Path来存储我们稍后将要下载的图像。

alcohol_types = ['whisky','wine','beer']
path = Path('alcohol')

对于三种酒精类型中的每一种,我们都创建了一个子路径来存储图像,然后从整理好的图像 URL 中下载图像。

if not path.exists():
    path.mkdir()
    for alc_type in alcohol_types:
        dest = (path/alc_type)
        dest.mkdir(exist_ok=True)

        search_term = alc_type
        params  = {"q":search_term, "license":"public",   
                   "imageType":"photo", "count":"150"}
        response = requests.get(search_url, headers=headers, 
                   params=params)
        response.raise_for_status()
        search_results = response.json()
        img_urls = [img['contentUrl'] for img in   
                    search_results["value"]]

        # Downloading images from the list of image URLs
        download_images(dest, urls=img_urls)

现在,我们应该将图像下载到带有酒精类型标签的相应文件夹中。为了证实这一点,我们可以利用get_image_files函数。

img_files = get_image_files(path)
img_files(#445) [Path('alcohol/beer/00000069.jpeg'),Path('alcohol/beer/00000007.jpg'),Path('alcohol/beer/00000092.jpg'),Path('alcohol/beer/00000054.jpg'),Path('alcohol/beer/00000082.jpg'),Path('alcohol/beer/00000071.jpg'),Path('alcohol/beer/00000045.jpg'),Path('alcohol/beer/00000134.jpg'),Path('alcohol/beer/00000061.jpg'),Path('alcohol/beer/00000138.jpg')...]

另一种验证下载的方法是点击 Google Colab 屏幕左侧导航栏上的文件图标,并导航至content > alcohol以查看各自的文件夹。

之后,我们需要检查我们下载的文件是否损坏。幸运的是,fastai 通过verify_images函数提供了一种方便的方法来实现这一点。

failed = verify_images(img_files)
failed

然后我们可以使用mapunlink方法从数据集中移除这些损坏的文件(如果有的话)。

failed.map(Path.unlink)

第 3 节-准备影像数据集

fastai 有一个叫做数据块 API 的灵活系统。有了这个 API,我们可以完全定制DataLoaders对象的创建。DataLoaders可以存储我们放置在其中的任何DataLoader对象,并用于随后生成训练集和验证集。

在我的理解中,DataLoaders本质上是一个对象,它存储了关于我们将要运行模型的数据的信息。DataBlock基本上是一个创建DataLoaders的模板函数。

alcohols = DataBlock(
    blocks=(ImageBlock, CategoryBlock), 
    get_items=get_image_files, 
    splitter=RandomSplitter(valid_pct=0.2, seed=1),
    get_y=parent_label,
    item_tfms=Resize(128))

让我们一部分一部分地看一下上面的代码:

*blocks=(ImageBlock, CategoryBlock)*

这个blocks元组分别表示我们的独立相关变量的数据类型。因为我们的目标是对图像进行分类,我们的自变量是图像(ImageBlock),而因变量是类别/标签(CategoryBlock)

*get_items=get_image_files*

get_items参数指定要使用的函数,以便提取数据集中图像的文件路径。回想一下,我们之前使用 fastai 内置的get_image_files将文件路径放入变量img_files。这个get_image_files函数获取一个路径(我们将在后面指定),并返回该路径中所有图像的列表。

*splitter=RandomSplitter(valid_pct=0.2, seed=1)*

splitter方法将把数据集分成训练集和验证集。我们指定RandomSplitter来确保它被随机分割,其中的valid_pct参数用于指示数据集的多大比例将被分配为验证集。也可以在RandomSplitter中设置随机种子,以实现结果的再现性。

*get_y=parent_label*

parent_label是 fastai 提供的一个函数,用来获取图像文件所在文件夹的名称。由于我们已经将图像放在具有各自酒类名称的文件夹中,这个parent_label将返回文件夹名称威士忌、葡萄酒啤酒

*item_tfms=Resize(128)*

我们通常使用将图像调整为正方形,因为这样做更容易,因为原始图像可以有不同的高度/宽度和纵横比。为此,我们对每个项目(item_tfms)执行转换,将它调整为 128x128 像素的正方形。

既然已经提供了所有的细节和参数,我们就可以用下面的单行代码启动数据加载器了。请注意,dataloaders的参数是存储图像的path,即alcohol文件夹路径。

dls = alcohols.dataloaders(path)

让我们通过展示我们的验证集中的 10 个图像的子集来简要地看一下这些图像

注:以下显示的图像缩略图是验证数据下载是否正确的好方法。以前当我运行这段代码时,我意识到我没有相应地更新搜索词,导致所有图像都是威士忌。如果操作正确,接下来的一批应该会显示来自不同类别的大量图像。

dls.valid.show_batch(max_n=12, nrows=2)

演示数据增强的工作原理

在继续下一步之前,有必要讨论一下数据扩充的概念。为了丰富我们的训练数据集,我们可以创建输入数据的随机变化,使它们看起来不同,但实际上不会改变数据的原始含义和表示。

一种常见的方法包括RandomResizedCrop,它抓取原始图像的随机子集。我们使用unique=True用不同版本的RandomResizedCrop变换来重复相同的图像。

发生的情况是,在每个时期(这是一个完整的通过我们在数据集中的所有图像),我们随机选择每个图像的不同部分。这意味着我们的模型可以学习识别我们图像中的不同特征。它还反映了图像在现实世界中的工作方式,因为同一件物品的不同照片可能会以略微不同的方式取景。使用项目转换的好处是,它反过来有助于防止过度拟合。

指定的min_scale参数决定了每次最少选择多少图像。有了以上所有的决定,我们可以使用.new方法创建一个新的DataBlock对象,并运行它来给我们一个名为dls的新的DataLoaders对象。

alcohols = alcohols.new(item_tfms=RandomResizedCrop(128, min_scale=0.3))
dls = alcohols.dataloaders(path)
dls.train.show_batch(max_n=8, nrows=2, unique=True)

在项目转换(item_tfms)之后,我们运行批量转换(batch_tfms),在批量单个项目上应用一组标准的数据扩充转换(aug_transforms())。

item_tfmsbatch_tfms的区别在于item_tfms对每个单独的项目(如图像)进行转换,而batch_tfms对整批项目进行转换。

以下代码说明了增强变换aug_transforms对单个输入图像的影响。你将能够观察到某种形式的旋转、视角扭曲和对比度变化。

alcohols = alcohols.new(item_tfms=Resize(128), batch_tfms=aug_transforms())
dls = alcohols.dataloaders(path)
dls.train.show_batch(max_n=8, nrows=2, unique=True)

第 4 部分—训练模型

现在,图像已经准备好了(尽管还没有清理),我们可以开始训练,立即构建一个简单的深度学习模型。首先,我们用下面的代码准备好DataLoaders对象。在这个迭代中,我们以 224x224 像素的尺寸调整和裁剪我们的图像,min_scale为 0.5。

alcohols = alcohols.new(
    item_tfms=RandomResizedCrop(224, min_scale=0.5),
    batch_tfms=aug_transforms())
dls = alcohols.dataloaders(path)

卷积神经网络(CNN)是用于图像分类的事实上的神经网络类型,这就是我们将要使用的。架构方面,我对cnn_learner fast.ai 函数任意选择了 resnet34(即 34 层深)。resnet 的细节可以在这里找到。

我们使用.fine_tune方法而不是.fit方法,因为我们利用预训练的 resnet 模型来执行迁移学习。我们指定历元的数量为 4(即.fine_tune中的参数)。

learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(4)

从上面我们可以看到,在训练我们的 CNN 学习者几分钟后,我们获得了 0.229 的error_rate(即 77.1%的准确率)。考虑到我们还没有清理数据集,这是一个不错的开始。

使用混淆矩阵可以更好地可视化结果。

interpretation = ClassificationInterpretation.from_learner(learn)
interpretation.plot_confusion_matrix()

fast.ai 还提供了一种简单的方法,让我们可以找出哪些图像的丢失率最高。如果模型是不正确的(特别是如果它也对其不正确的答案有信心),或者如果它是正确的,但对其正确的答案没有信心,则损失是较高的数字。这些帮助我们识别模型有问题的图像。

interpretation.plot_top_losses(5, nrows=1)

从上面来看,似乎有些问题源于几个实际标签贴错了,而不是预测错了。

例如,行中间的图像很明显是一品脱啤酒的图像(这正是模型所预测的)。然而,分配给它的实际标签是威士忌,这是不正确的。这凸显了在训练任何类型的机器学习模型之前,拥有正确标记的数据(尽可能多)的重要性。

第 5 节—清理数据

请注意,我们在清理数据之前运行了模型。事实上,这样做使得数据清理更加容易。如上图所示,plot_top_losses已经可以指出哪些图像是模型最难处理的。

通过内置的 fast.ai ImageClassifierCleaner图形用户界面(GUI)小部件,数据清理过程变得很容易。

cleaner = ImageClassifierCleaner(learn)
cleaner

显示的图像行是损失最大的图像,这个 GUI 为您提供了查看和修改它们的机会。

清理是通过为上面的每个图像选择一个下拉选项,然后反复运行“清理”代码来完成的。由于网上的解释不清楚,我花了一些时间来弄清楚这个问题,所以这里有一些进一步的细节:

  • 步骤 1 :在cleaner输出单元中加载一行图像(如葡萄酒类别的训练集)后,根据您自己的判断,为您希望编辑的图像选择一个下拉选项。选项包括从数据集中删除图像,或将图像移动到新的类别中。如果不需要更改,则不需要选择任何选项,因为默认选项是<保持>。
  • 步骤 2 :一旦你完成了显示的图像行的选项更新,运行下面的“清理”代码来执行更改。
# Delete images marked as delete
for idx in cleaner.delete(): cleaner.fns[idx].unlink()

# Update category of image to the newly specified category by moving # it into the appropriate folder
for idx,cat in cleaner.change(): shutil.copyfile(cleaner.fns[idx], path/cat)

cleaner.delete删除您标记为<删除>的图像,同时cleaner.change将图像转移到带有更新标签的文件夹中。

  • 步骤 3: 再次返回到带有该行图像的cleaner单元格,并通过下拉菜单切换到一组新的图像,例如啤酒类别的验证组或威士忌类别的训练组
  • 步骤 4: 加载新一行图像后,选择每个图像的相关下拉选项,然后重新运行“清理”代码
  • 第 5 步:对每个数据集重复第 3 步和第 4 步,直到所有数据集至少被检查一次。

数据清理后重新训练模型

dls = alcohols.dataloaders(path)
learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(4)

通过一点点数据清理(主要是通过用.unlink方法移除不相关的图像),我们看到了error_rate的巨大改进(从 0.229 降低到 0.096)。这意味着准确率从更早的 77.1%(数据清理前)提高到了现在的 90.4%。

interpretation = ClassificationInterpretation.from_learner(learn)
interpretation.plot_confusion_matrix()

从上面的混淆矩阵中,我们可以清楚地看到,该模型在区分三种类型的酒精饮料方面变得更好。

训练完成后,我们希望导出模型,以便保存架构、训练参数和DataLoaders设置。这些都要存到泡菜里(。pkl)文件。

learn.export()path = Path()
path.ls(file_exts='.pkl')(#1) [Path('export.pkl')]

第 6 节—使用图像分类器进行推理

在加载包含我们深度学习图像分类模型信息的 pickle 文件后,我们可以用它来推断(或预测)新图像的标签。模型现在被加载到学习变量learn_inf中。

learn_inf = load_learner(path/'export.pkl')

我们使用一个样本图像来测试我们的模型

# Sample image
ims = ['https://alcohaul.sg/products/i/400/5f7edfe79ae56e6d7b8b49cf_0.jpg']
dest = 'images/test_whisky.jpg'
download_url(ims[0], dest)im = Image.open(dest)
im.to_thumb(224,224)

我们的样本图像是日比喜和谐威士忌的图像。让我们看看我们的模型是否能够识别它的类别。

learn_inf.predict('images/test_whisky.jpg')('whisky', tensor(1), tensor([4.1783e-04, 9.9951e-01, 7.0310e-05]))learn_inf.dls.vocab['beer', 'whisky', 'wine']

看起来一切都很好。该模型能够以高置信度确定测试图像代表威士忌的图像(概率为 99.95%)。

第 7 部分—部署为 Web 应用程序

让我们简单探讨一下模型的部署。我们首先为用户创建一个按钮,上传他们希望分类的新图像。

然后,我们利用PIL.Image.create方法检索上传的图像,并将其存储在img变量中

btn_upload = widgets.FileUpload()
btn_upload

# Retrieving the uploaded image
img = PIL.Image.create(btn_upload.data[-1])

然后我们设置一个Output小部件来显示上传的图像。

out_pl = widgets.Output()
out_pl.clear_output()
with out_pl: display(img.to_thumb(224,224))
out_pl

用户上传了一张 Talisker 18 威士忌图片。现在是时候再次测试我们建立的模型的分类能力了。

pred,pred_idx,probs = learn_inf.predict(img)lbl_pred = widgets.Label()
lbl_pred.value = f'Prediction: {pred}; Probability: {probs[pred_idx]:.04f}'
lbl_pred

从上面可以看出,我们的模型确实预测图像是威士忌的图像(概率为 99.65%)。我们现在可以继续构建我们的 web 应用程序,包括一个运行按钮,供用户单击并启动分类过程。

btn_run = widgets.Button(description='Classify Image')
btn_runButton(description='Classify Image', style=ButtonStyle())

然后我们设置一个回调,这样上面的按钮可以在点击时执行特定的功能。我们想要的是,每当用户为他/她上传的图像单击“分类图像”时,分类模型就会运行,然后生成分类预测。

def on_click_classify(change):
    img = PIL.Image.create(btn_upload.data[-1])
    out_pl.clear_output()
    with out_pl: display(img.to_thumb(128,128))
    pred,pred_idx,probs = learn_inf.predict(img)
    lbl_pred.value = f'Prediction: {pred}; Probability: {probs[pred_idx]:.04f}'

btn_run.on_click(on_click_classify)

现在,将所有这些放在一个 VBox 中,这样小部件就可以很好地排列在我们笔记本中的垂直 web 应用程序模板中。

VBox([widgets.Label('Select your alcohol!'), 
      btn_upload, btn_run, out_pl, lbl_pred]

进一步的步骤

为了在笔记本之外部署这个应用程序,我们可以利用Voila来创建一个真正独立的应用程序(基于 Jupyter 笔记本)。

由于这方面超出了本笔记本的范围,请随意在这里探索 Voila 的细节。

结论

至此,本教程到此结束。我们讨论了端到端的建模体验,从 fast.ai 和 Google Colab 设置,到数据摄取,一直到为我们构建的模型设置一个简单的 web 应用程序。

随着我继续 fast.ai 学习之旅,我将继续发布进一步的演练。同时,喝一杯的时间到了。干杯!

在你走之前

欢迎您加入我的数据科学学习之旅!点击此媒体页面,查看我的 GitHub ,了解更多令人兴奋的数据科学内容。

利用深度迁移学习对大众射手宣言进行分类

原文:https://towardsdatascience.com/classifying-mass-shooter-manifestos-with-deep-transfer-learning-f80576aabdf7?source=collection_archive---------49-----------------------

使用 TextCNN 和 ManifestoCorpus 的深度迁移学习教程

蒂姆·马德在 Unsplash 上的照片

简介

在美国,大规模枪击事件以惊人的速度发生。尽管存在与大规模枪击事件和公众反应间接相关的分类任务,但没有太多关注大规模枪击事件宣言本身。Mass shooter manifestos 的规模天生有限,这就是使用 TextCNN 等深度学习模型的迁移学习如何帮助正确分类小型数据集,如 ManifestoCorpus,这是一种为此目的制作的新数据集。Keras 发布了一个开发者指南,用于迁移学习和微调,供那些希望进一步了解最新实现的人使用。我在乌普萨拉大学的顶点项目的所有代码都可以在我的 GitHub 上找到,包括其他分类任务和模型。

迁移学习

NLP 有不同类型的迁移学习,可以根据源域和目标域是否完成相同的任务、源域和目标域的确切性质以及学习任务的精确顺序来粗略定义。任何迁移学习背后的直觉是,我们正在利用来自基础模型的知识来从受约束的数据集中获得洞察力。

出于 NLP 的目的,两个不同的领域朝着一个共同的任务努力,在源领域包含唯一的标记数据的情况下,领域适应是最常见的迁移学习类型。出于这里的目的,ManifestoCorpus 是由源数据集设置的标准手动标记的。在迁移学习中,通常还会改变基本模型,如层和参数,以适应再训练期间的特定目标数据集。这是通过在预训练期间冻结基本模型(即特征提取器)层和在再训练期间解冻微调模型来实现的。

数据

源数据 使用 Kaggle 提供的sensition 140 数据集作为源数据集。最初的数据集包括 160 万条推文,但我在训练中只包括了 16 万条推文。我将正面、负面和中性推文的标签二值化为非负面和负面的标签。
目标数据 ManifestoCorpus 包含 425 段大规模射手宣言和 425 段普通宣言。对于目标数据集,为每个分类任务手动标记文本,以最好地反映其源数据集的注释,交叉引用相关作品的人类注释者作为标记的动机。有二进制、多类和多标记数据集,但这里我们只看否定和非否定段落的二进制分类。

大众射手常用词

实现

首先,必须为基础模型准备好源数据集。我们在这里采取了一些额外的步骤来移除来自 Twitter 数据的杂波文本。

之后,我们为微调后的模型准备目标数据集。不像手动管理的数据那样需要清理。

我们将希望建立将在 TextCNN 的嵌入层中使用的单词嵌入。这里我们用手套。

现在,我们可以开始为一个为情感分析而设计的 CNN 构建基础和微调模型。实现遵循 Géron 的优秀动手机器学习中的迁移学习教程,使用 Scikit-Learn、Keras 和 TensorFlow。

重要的变化是我们选择在模型之间解冻哪些层。我们通过将感兴趣的层的可训练权重参数从假变为真来冻结和解冻层。我们解冻前四层,并在全连接层之前向微调模型添加几个卷积层。

最终评估是通过 Scikit-Learn 中的精度、召回率和 F1 的分类报告进行的。

结果

在我的发现中,阴性标签产生了 73%的准确率、66%的召回率和 69%的 F1,而非阴性标签产生了 71%的准确率、78%的召回率和 75%的 F1。非负面标签更容易分类和概括。这并不奇怪,因为非否定性文本的宽度比否定性文本宽。宣言和推文的领域可能太不一样了,也不适合更强的表现。ManifestoCorpus 也有其固有的偏见和局限性,但据我所知,它是唯一一个将 NLP 应用于大量射手文本的数据集。我们可以预期,伯特或 UMLFit⁴等最先进的语言模型将通过简单的软件包在迁移学习中取得更有前途的结果。自从这个顶点项目完成以来,NLP 的迁移学习在很短的时间内取得了很大的进展。请随意使用 ManifestoCorpus!

[1]:杰森·布朗利。(2017 年 12 月 20 日)。深度学习的迁移学习的温柔介绍https://machinelingmastery . com/Transfer-Learning-for-Deep-Learning/

[2]:奥雷连·盖伦。(2019 年 9 月)。使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习,第二版

[3]:威廉·法尔孔。(2019 年 11 月 25 日)。 Lit BERT: NLP 迁移学习 3 步走。https://towards data science . com/lit-Bert-NLP-transfer-learning-in-3-steps-272 a 866570 db

[4]: Akhilesh Ravi。(2019 年 11 月 23 日)。了解 ul mfit—NLP 中向迁移学习的转变https://towards data science . com/understanding-ul mfit-and-elmo-the-shift-forward-transfer-learning-in-NLP-b 5d 8 e 2e 3 f 664

评估机器学习模型的性能

原文:https://towardsdatascience.com/classifying-model-outcomes-true-false-positives-negatives-177c1e702810?source=collection_archive---------10-----------------------

评估模型的最常见和最快速的方法之一

凯文·Ku 在 Unsplash 上的照片

有多种方法可以评估机器学习模型的表现。然而,本文将介绍一种最常见、最快速的方法来评估模型的性能,这种方法被称为 Precision 和 Recall。

分类模型结果

在我们进入精确和回忆之前,我们必须首先理解真正的阳性、真正的阴性、假阳性和假阴性结果分类的含义。

为了更好地理解这些结果分类,假设我们正在建立一个分类模型来确定患者是否患有癌症。在这种情况下,因为我们的模型的目的是试图预测患者是否患有癌症,所以如果患者患有癌症,则阳性结果将是,并且可以理解的是,如果患者没有患有癌症,则阴性结果将是。

真阳性

真阳性(TP)是模型正确预测阳性结果

在我们的例子中,这意味着被模型预测为患有癌症的患者确实患有癌症。

真正的否定

真阴性(TN)是模型正确预测阴性结果 。**

在我们的例子中,这意味着被预测为健康的患者确实没有患癌症。

假阳性

假阳性(FP)是模型错误预测阳性结果 。这也称为 I 型误差。

在我们的例子中,这意味着被预测患有癌症的患者实际上是健康的。

假阴性

假阴性(FN)是模型错误预测阴性结果。这也被称为第二类错误。**

在我们的例子中,这意味着被预测为健康的患者实际上患有癌症。

精确度与召回率

尽管 TP、TN、FP 和 FN 值可以为数据科学家提供模型性能的一些指示,但很难在不参考其他值的情况下单独对每个值进行数值评估。这就是为什么数据科学家经常使用精度召回来评估模型。**

精确

精确度是积极的(或相关的)结果的百分比。计算如下:

在我们的例子中,精确度是实际患有癌症并被检测出患有癌症的患者人数与预测患有癌症的患者人数的比例。

回忆

召回率是被正确识别的实际阳性的百分比。计算如下:

在我们的例子中,回忆将是实际患有癌症和被预测患有癌症的患者数量占实际患有癌症的患者总数的比例。

精确度和召回率

理想情况下,一个性能良好的模型应该产生高精度值和高召回值。

拥有高精度低召回意味着虽然模型擅长预测正面类,但它只检测到正面结果总数的一小部分。在我们的例子中,这将意味着被预测患有癌症的患者最有可能患有癌症,但是在所有患有癌症的患者中,该模型仅预测其中一小部分患有癌症。因此,该模型低估了**

具有低精度但是高召回意味着尽管模型正确地预测了大多数肯定的情况,但是它也预测了许多否定也是肯定的。在我们的例子中,这意味着在所有实际患有癌症的患者中,大多数被正确地预测为患有癌症,也有许多没有患癌症的患者被模型预测为患有癌症。所以模型是过预测**

结论

这是一个评估模型的简单方法的快速介绍。这些值收集和计算起来真的很快,并且可以给出一个模型表现如何的估计。

对网上商店顾客进行分类

原文:https://towardsdatascience.com/classifying-online-shop-customers-65438e0cc58b?source=collection_archive---------40-----------------------

不同机器学习分类器的可视化方法

作者图片

在这篇文章中,我想展示如何使用不同的机器学习方法,利用在线商店的跟踪数据将客户分为购买和不购买。利用从原始数据中汇总的特征,例如访问次数和页面浏览次数,可以训练和可视化预测模型。

特别注意借助于二维图和决策边界的着色的预测模型的视觉呈现。不同方法的特点以及模型调整不足和过度的情况变得很明显。为了使可视化成为可能,我考虑只有两个特征的非常简化的模型。

数据导入

让我们从 CSV 文件加载原始数据。这些来自 Kaggle 数据集https://www . ka ggle . com/mkechinov/ecommerce-behavior-data-from-multi-category-store。这与我在关于分段和可视化的文章中使用的数据源相同:

https://towards data science . com/segmentation-of-online-shop-customers-8c 304 a2 d 84 b 4

为了演示和分析,我只使用了一部分数据。

跟踪网上商店的数据

在线商店的产品页面上有 1,000,000 条数据记录,具有产品类别、品牌、价格、用户会话 id 等特征。

为了进一步处理数据,我们形成了一个数据框架,该数据框架聚集了各个匿名访问者的数据,并计算了在线商店的页面浏览量和访问量。此外,为每个访问者计算购买物品的数量,从而创建一个标签,无论该顾客是否是购买者。

为了简化下面的演示,从分析中删除了一些访问者的数据。

网上商店用户的汇总数据

这为我们提供了来自 121 个网站访问者的数据。

数据的图形表示

在我们开始分析之前,让我们来看一个访问者的二维图,它使用访问次数和页面浏览量作为坐标轴。是否是买家由颜色代码表示。图书馆“Plotly”(【https://plotly.com/】)是用来搞阴谋的。

网店用户的特征空间/作者提供的图片

红点和绿点大多集中在这个“特征空间”的某些区域。在下文中,我们尝试创建分类模型,根据访问者在特征空间中的位置,正确地将他们识别为购买者。

为了能够可视化模型和它们的预测,我们需要一个辅助函数,它也可以在二维图中绘制决策边界。训练完模型后,该函数对一个狭窄的点网格执行分类预测,然后使用此信息根据预测对区域进行着色。

按会话和页面浏览量对访问者进行分类

对于分类模型的实现,我们使用库“Scikit Learn”(https://scikit-learn.org/)。我们考虑不同的方法,包括我们的可视化功能的参数。

朴素贝叶斯分类

贝叶斯分类器是从贝叶斯定理导出的分类器。它将每个对象分配给它最可能所属的类。形式上,它是一个数学函数,将一个类分配给特征空间中的每个点。

朴素贝叶斯分类器因其快速的可计算性而非常流行。天真的基本假设是属性是独立的。在我们的例子中,这并没有实现,页面浏览的数量取决于访问的数量。

如下图所示,结果的质量也一般。分类器只是粗略地划分了红点和绿点的区域。很多点都在错误的区域。

作者使用朴素贝叶斯/图像进行分类

关于朴素贝叶斯的更多信息:

[## 第 1 章:监督学习和朴素贝叶斯分类—第 1 部分(理论)

欢迎来到监督学习的踏脚石。我们首先讨论一个小场景,它将构成…

medium.com](https://medium.com/machine-learning-101/chapter-1-supervised-learning-and-naive-bayes-classification-part-1-theory-8b9e361897d5)

支持向量机分类

我们从线性支持向量分类开始。对于线性内核,最佳可能分离由直线实现。在图中,您可以看到直线是如何放置的,以保持到分类边界附*的点的距离尽可能大。这种线性模型定义复杂区域的可能性自然是有限的。

作者用 SVM /图像分类

关于支持向量机的更多信息:

[## 支持向量机的友好介绍(SVM)

机器学习被认为是人工智能的一个分支,它与人工智能的发展密切相关

towardsdatascience.com](/a-friendly-introduction-to-support-vector-machines-svm-925b68c5a079)

在具有多项式核的变体中,多项式被使用到指定的次数作为分离。该图显示了高达 4 次多项式的使用,这使得该方法有更多的可能性来适应数据。

作者用多项式 SVM /图像分类

如果程度设置得高,则可以在特征空间中定义更复杂的区域。具有混合点的区域仍然没有被更好地解决。然而,随着多项式次数的增加,有过度拟合的危险。

作者用多项式 SVM-10 次/图像进行分类

神经元网络

有了神经网络,可以定义任何复杂的功能和区域。这是由层数和神经元数定义的。在该图中,使用了具有 3 层,每层 25 个神经元的神经网络。

作者用神经网络/图像分类

关于神经网络分类的更多信息,请参见:

[## 基于神经网络的虹膜数据分类

Iris flowers 数据集是分类文献中最著名的数据集之一。目标是…

medium.com](https://medium.com/gadictos/iris-data-classification-using-neural-net-335d3303abd8)

决策树

根据允许的最大深度,决策树可以形成或多或少复杂的分类区域。然而,总是仅在轴方向上具有决策限制。这导致区域呈阶梯状。

作者用决策树/图片分类

在深度很深的地方,决策树很快就会过度适应。这可以在图中通过窄条纹清楚地看到,每个窄条纹仅分类一个数据点。

有关决策树的更多信息,请访问:

[## 机器学习和数据科学决策树指南

决策树是一类非常强大的机器学习模型,可以在许多任务中实现高精度,同时…

towardsdatascience.com](/a-guide-to-decision-trees-for-machine-learning-and-data-science-fe2607241956)

随机森林——决策树的集合

如果不使用单个决策树,而是使用多个决策树,然后让大多数单个分类器来决定整体分类,我们就称之为随机森林。这些具有决策树的一些优点,例如简单性和良好的可解释性,但是它们可以更灵活地划分分类区域,并且不会很快过度适应。

作者用随机森林/图像分类

该图显示了非常灵活的决策限制,但也显示了过度适应的方法。

有关随机森林的更多信息,请访问:

[## 随机森林的不合理有效性

机器学习从业者有喜欢的算法是很常见的。这有点不合理,因为没有…

medium.com](https://medium.com/rants-on-machine-learning/the-unreasonable-effectiveness-of-random-forests-f33c3ce28883)

使用这个只有两个特征的买家分类的例子,我们直观地展示了一些非常普遍的分类方法的典型属性。这有助于发展对方法的直觉,并更好地评估风险,如过度适应。

通过图像分析对帕金森病进行分类:第一部分

原文:https://towardsdatascience.com/classifying-parkinsons-disease-through-image-analysis-2e7a152fafc9?source=collection_archive---------39-----------------------

应用计算机视觉

预处理和探索性数据分析

在 Unsplash 上由halance拍摄的照片

我简介

阿金森氏病通常伴有震颤和僵硬等运动障碍症状。这些会对患有早期疾病的人的书写和素描产生明显的影响[1]。缩微图是一个人的笔迹中异常小的波动,然而,由于一个人发展的笔迹、语言、熟练程度和教育等的可变性,已经声称难以解释[1]。因此,2017 年进行的一项研究旨在通过使用螺旋和波浪的标准化分析来改善诊断。在这一系列的帖子中,我们将分析该研究中收集的原始图像,看看我们是否可以为一名帕金森患者创建一个分类器,并在此过程中得出一些结论。我们将使用的数据托管在 Kaggle [2]上,特别感谢 Kevin Mader 分享数据集上传。

图片来自作者。我们将使用的数据的样本图像。

在第 1 部分中,我们将进行一些探索性的数据分析,并对图像进行预处理,以创建一些有望有助于分类的特征。我选择不使用卷积神经网络(CNN)来简单地对图像进行分类,因为这将是一个黑盒——没有任何衡量曲线/草图之间潜在差异的指标。相反,我们不是简单地执行分类任务,而是试图使用图像处理来理解和量化差异。在随后的帖子中,我将与 CNN 进行比较。

来自吉菲

在我们开始之前,声明这并不意味着任何形式的医学研究或测试。请参考原始论文了解实际实验的细节,我并没有参与其中。
Zham P,Kumar DK,Dabnichki P,Poosapadi Arjunan S,Raghav S. 利用画螺旋的速度和笔压综合指数区分帕金森病的不同阶段。前神经元。2017;8:435.发布于 2017 年 9 月 6 日。doi:10.3389/fneur.2017.00435

探索性数据分析

首先,让我们看一下图像,执行一些基本的分割,并开始探索一些感兴趣的潜在特征。我们将全程使用熊猫来存储图像和信息。对于那些质疑你是否会阅读这一部分的人,这里是我们将要进入的内容:
-阈值处理和清理
-通过最*邻的厚度量化
-骨架化
-交叉点和边缘点

阈值处理和清洗

我们使用一个修改的读取和阈值函数,主要是从 Kevin Mader 在 Kaggle 上的原始笔记本中获得的[2]。在这里,当我们想要查看蒙太奇风格的图像时,可以选择调整大小,如上图和下图所示。我们首先读入并反转图像,使图形在黑色背景上是白色的,如果需要还可以调整大小。我们还应用了一个小的中值滤波器。

这个项目有相当多的代码,我不会把它们都放在这里,所以如果你想查看笔记本或 python 脚本以了解更多细节,请查看 github 链接。

from skimage.io import imread
from skimage.util import montage as montage2d
from skimage.filters import threshold_yen as thresh_func
from skimage.filters import median
from skimage.morphology import disk
import numpy as npdef process_imread(in_path, resize=True):
    """read images, invert and scale them"""
    c_img = 1.0-imread(in_path, as_gray=True)
    max_dim = np.max(c_img.shape)
    if not resize:
        return c_img
    if c_img.shape==(256, 256):
        return c_img
    if max_dim>256:
        big_dim = 512
    else:
        big_dim = 256
    """ pad with zeros and center image, sizing to either 256 or 512"""   
    out_img = np.zeros((big_dim, big_dim), dtype='float32')
    c_offset = (big_dim-c_img.shape[0])//2
    d_offset = c_img.shape[0]+c_offset

    e_offset = (big_dim-c_img.shape[1])//2
    f_offset = c_img.shape[1]+e_offset
    out_img[c_offset:d_offset, e_offset:f_offset] = c_img[:(d_offset-c_offset), :(f_offset-e_offset)]
    return out_imgdef read_and_thresh(in_path, resize=True):
    c_img = process_imread(in_path, resize=resize)
    c_img = (255*c_img).clip(0, 255).astype('uint8')
    c_img = median(c_img, disk(1))
    c_thresh = thresh_func(c_img)
    return c_img>c_thresh

最后,对于读入的内容,我们还通过移除任何与主草图无关的小对象来清理图像。

from skimage.morphology import label as sk_labeldef label_sort(in_img, cutoff=0.01):
    total_cnt = np.sum(in_img>0)
    lab_img = sk_label(in_img)
    new_image = np.zeros_like(lab_img)
    remap_index = []
    for k in np.unique(lab_img[lab_img>0]):
        cnt = np.sum(lab_img==k) # get area of labelled object
        if cnt>total_cnt*cutoff:
            remap_index+=[(k, cnt)]
    sorted_index = sorted(remap_index, key=lambda x: -x[1]) # reverse sort - largest is first
    for new_idx, (old_idx, idx_count) in enumerate(sorted_index, 1): #enumerate starting at id 1
        new_image[lab_img==old_idx] = new_idx
    return new_image

这是通过仅保留大于 1%的激活像素的足够大的分量来实现的;由截止值定义。首先标记图像中的每个单独的对象,并将每个标记的面积相加(不是 0)。如果计数超过总数的 1%,则保留索引。执行负排序,使最大的对象具有标签 1。用新的订购 id 替换旧的标签号。

图片来自作者。阈值处理和清理后的数据样本图像。

作为绘图差异的初始视图,我们可以创建一个骨架图像并形成一个新的数据框,其中每一行都是每个图像中非零像素的单个像素坐标。然后,我们可以将这些曲线中的每一条绘制在一个图表上——在归一化位置之后。我们不会使用这种格式的数据框,这只是为了可视化。

图片来自作者。一个地块上的所有图纸。

正如我们所看到的,健康的草图之间有很强的一致性。考虑到帕金森症状可能导致的随机运动,这是有意义的。

厚度量化

N ext,我们将尝试量化厚度。为此,我们将使用距离图来给出图纸宽度的*似值。中轴也返回一个距离图,但是骨骼化更干净,因为它做了一些修剪。

from skimage.morphology import medial_axis
from skimage.morphology import skeletonizedef stroke_thickness_img(in_img):
    skel, distance = medial_axis(in_img, return_distance=True)
    skeleton = skeletonize(in_img)
    # Distance to the background for pixels of the skeleton
    return distance * skeleton

图片来自作者。线条粗细计算。

通过绘制*均值和标准偏差,我们看到了这些图之间的一些相关性。主要是在标准差中,考虑到随机影响,这也是有意义的,事实上它是巨大的,并不小于健康也是有意义的。

图片来自作者。图纸的厚度。

交叉点和边缘点

D 由于图像骨架化的工作方式,根据线条的“*滑度”,更多起伏的曲线会有更多的端点。因此,与*滑线相比,这些是随机运动的一些度量。另外,我们可以计算交点的数量;完美的曲线没有交点,只有两个端点。这些在其他图像处理应用中也是有用的,例如道路绘图。

我不会在这里过多地讨论代码,因为需要进行相当多的清理,但是,我会尝试使用一些图像来解释我所做的事情。首先,我们计算曲线骨架的最*邻图像。除了在边缘点的值为 1 和在交叉点的值为 3 之外,这给了我们在任何地方的值为 2;这是在使用连通性 2(8 个最*的邻居)时。这是放大后的结果图像。

图片来自作者。交点和边缘点。

正如你所看到的,这和预期的一样,除了我们遇到了一些问题,如果我们想得到一个像素交叉点来正确量化交叉点的数量。右边的图像有三个值为 3 的像素,尽管这只是一个交集。当这些交点的总和大于“正确的”交点时,我们可以通过隔离这些区域,用下面的伪算法来清理这个问题。我们可以将最*邻(NN)图像和阈值相加来隔离它们。连通性 1 有直接邻居,连通 2 包括对角线。

  • 从原始神经网络,分别使用连接 1 和 2 求和。
  • 隔离连通性 2 中值≥ 8 的交叉点。
  • 标记连接到交叉点像素的每条边。
  • 对于连通性 1 图像,隔离总和在 3 和 5 之间的交叉点像素。这些是我们不想要的。
  • 覆盖不正确的交叉点像素。

结果如下:

图片来自作者。交叉点和边缘点,已更正。

如你所见,我们现在在交叉点位置有一个值为 3 的像素。我们现在可以简单地将这些位置相加,以量化交点的数量。如果我们把这些画在一条曲线上,我们可以看到结果,黄色下面是交叉点,绿色是边缘点。

图片来自作者。绘制了交点和边缘点的骨架曲线。

如果我们画出每种绘画类型的数量,我们可以看到相当强的相关性:

图片来自作者。*均边缘点数。

图片来自作者。*均相交数。

我们还可以看到,我们对具有大约 2 个边缘点的健康曲线的初始估计也是正确的。帕金森波图有非常多的边缘点,因为这些通常非常“尖锐”而不是*滑地弯曲,这在这些波的尖端产生了大量的边缘点。

最后,我们还可以检查骨架图像中像素总数的相关性。这与图纸的“间接”性质有关。这种量化非常简单,我们只需对骨架图像中大于零的像素求和。

图片来自作者。*均像素数。

没有一段感情那么牢固,但还是有意义的。

摘要

因此到目前为止,我们已经读入、清理并获得了一些潜在有用的指标,这些指标不仅有助于我们了解缩微程度,还可以用作分类器(如逻辑回归或随机森林)的输入。在本系列的第 2 部分中,我们将使用这些度量标准进行分类,并与一个更强大的黑盒神经网络进行比较。使用随机森林的优势在于,我们还可以看到哪些功能对模型的影响最大。根据以上观察,你是否已经有了直觉,知道哪些特性可能是最重要的?

如果你觉得这篇文章的任何部分提供了一些有用的信息或一点灵感,请关注我。

你可以在我的 github 上找到源代码。该项目目前仍在建设中。

链接到我的其他帖子:

  • 计算机视觉和终极 Pong AI——使用 Python 和 OpenCV 在线玩 Pong
  • 《我的世界》测绘仪 —计算机视觉和光学字符识别从截图和绘图中抓取位置

奖金

这里不使用,但为了好玩,我们也可以创建一个图形来表示每个图像,因为我们有节点和边,其中节点是交叉点或边点,边是连接这些点的绘图部分。

图片来自作者。网络图的绘制。

我们为此使用了 Networkx 库,其中每个数字要么是一个节点,要么是交叉点,其颜色对应于连接节点的绘图部分的长度。

参考

[1] Zham P,Kumar DK,Dabnichki P,Poosapadi Arjunan S,Raghav S. 利用画螺旋的速度和笔压综合指数区分帕金森病的不同阶段。前神经元。2017;8:435.发布于 2017 年 9 月 6 日。doi:10.3389/fneur . 2017.00435
【2】Mader,K. 帕金森素描概述。https://www.kaggle.com/kmader/parkinsons-drawings。获取日期:2020 年 7 月 9 日

通过图像分析对帕金森病进行分类:第二部分

原文:https://towardsdatascience.com/classifying-parkinsons-disease-through-image-analysis-part-2-ddbbf05aac21?source=collection_archive---------41-----------------------

应用计算机视觉

比较随机森林、ResNet50 特征提取和定制卷积神经网络

在我之前的帖子中,我概述了一些手动获取的特征,如交叉点和端点的数量、线条粗细、标准偏差等。在这篇文章中,我们将应用这些特征,尝试将两组图画分为健康型和帕金森型。此外,我们还将使用更高级的机器学习方法来研究其他两种分类方法:即结合逻辑回归的特征提取;以及定制的、从零开始训练的卷积模型。

在我们开始之前,声明这并不意味着任何形式的医学研究或测试。关于实际实验的细节请参考的原始论文,我并没有参与其中。
Zham P,Kumar DK,Dabnichki P,Poosapadi Arjunan S,Raghav S. 利用画螺旋的速度和笔压的综合指数区分帕金森病的不同阶段。前神经元。2017;8:435.发布于 2017 年 9 月 6 日。doi:10.3389/fneur.2017.00435。

在做这样一个项目时,重要的是要决定目标是什么?这里有两个:
1。了解波形和螺旋图像的草图类型(健康或帕金森)之间的可观察差异。
2。创建一个高精度的分类器来确定患者是否可能患有帕金森氏症。
正如我在以前的帖子中提到的,直接进入神经网络模型的一个限制是,它在很大程度上是一个黑盒分类器。我们失去了对帕金森氏症引起的绘画中的根本差异的理解。这就是我们的第一个分类器被使用的地方,它实际上同时针对这两个目标。我们将从那里开始。

来自吉菲

来自手工构建特征的随机森林分类器

这一部分的标题说明了一切。我在上一篇文章中对获得的特征使用了随机森林分类器。显而易见,我们将使用的特征是:
-【*均厚度】;标准厚度';number _ pixels ';数字 _ 边缘点';“数量 _ 交叉点”。
我发现的一个技巧是通过创建交互特征来合并一些非线性,其中每个特征与每个其他特征相乘。例如:均值 _ 厚度*数量 _ 交点。这将特征的数量增加到 15 个。
我们还必须记住标准化每个特性。这使得*均值为 0。本质上,我们减去*均值,然后除以每个特征列的标准偏差。
最后,我们必须为我们的目标类创建一个一次性编码。这只是一个二元分类,所以这就足够了。这将把我们的“健康”或“帕金森”标签转换为 0 或 1。我们还确保将培训数据与测试数据分开。
就是这样!我们可以将我们的特征列传递给一个随机的森林分类器。这里还需要注意的是,我们需要波浪和螺旋数据集的独立模型。一个明显但重要的注意事项。以下是 wave 的结果:

测试的波形准确度:80%

+--------------+-----------+----------+-------------+---------+
|     Wave     | precision | recall   | f1-score    | support |
+--------------+-----------+----------+-------------+---------+
| healthy      |      0.74 |     0.93 |        0.82 |      15 |
| parkinson    |      0.91 |     0.67 |        0.77 |      15 |
|              |           |          |             |         |
| accuracy     |           |          |        0.80 |      30 |
| macro avg    |      0.82 |     0.80 |        0.80 |      30 |
| weighted avg |      0.82 |     0.80 |        0.80 |      30 |
+--------------+-----------+----------+-------------+---------+

https://ozh.github.io/ascii-tables/

随机森林分类器提供了许多很好的优势,我在这里使用的主要优势是能够给出最有影响力的特征列表。以下是它们出现的顺序:

Feature impact (in order of importance): 
['std_thickness_num_ep' 'std_thickness' 'num_ep_num_inters'
 'num_pixels_num_ep' 'num_pixels_num_inters' 'mean_thickness_num_inters'
 'mean_thickness_num_ep' 'std_thickness_num_inters'
 'std_thickness_num_pixels' 'mean_thickness' 'num_ep' 'num_inters'
 'num_pixels' 'mean_thickness_std_thickness' 'mean_thickness_num_pixels']

对于螺旋:

测试的螺旋精度:67%

+--------------+-----------+----------+-------------+---------+
|    Spiral    | precision | recall   | f1-score    | support |
+--------------+-----------+----------+-------------+---------+
| healthy      |      0.63 |     0.80 |        0.71 |      15 |
| parkinson    |      0.73 |     0.53 |        0.62 |      15 |
|              |           |          |             |         |
| accuracy     |           |          |        0.67 |      30 |
| macro avg    |      0.68 |     0.67 |        0.66 |      30 |
| weighted avg |      0.68 |     0.67 |        0.66 |      30 |
+--------------+-----------+----------+-------------+---------+Feature impact (in order of importance): ['num_pixels_num_ep' 'num_pixels' 'std_thickness_num_ep'
 'num_pixels_num_inters' 'std_thickness_num_pixels'
 'mean_thickness_num_ep' 'mean_thickness' 'std_thickness_num_inters'
 'std_thickness' 'mean_thickness_std_thickness'
 'mean_thickness_num_pixels' 'mean_thickness_num_inters'
 'num_ep_num_inters' 'num_ep' 'num_inters']

在我的上一篇文章中,我问,“根据我们看到的结果,我们认为哪个是最重要的?”。机器学习的一个优势是,我们可以分配这些非线性的相互作用,这可能是我们难以预测的。此外,我们可以看到每种类型的绘画的影响是不同的。在波动集上,准确率还不错,为 80%,但在螺旋集上,准确率很低,为 67%——仍然比机会集(50%)好。显然,可以对特征进行一些微调,从而使螺旋集达到更好的精度。然而,使用随机森林,我们现在可以观察到绘图的某些部分有多重要。当然,通过端点和像素数识别的波纹在波浪和螺旋模型中都很突出。

用 ResNet50 进行特征提取,用逻辑回归进行分类

在下一节中,我们将首先通过在 ImageNet 上训练的 ResNet50 模型传递我们的图像,但保留图层的顶部。我们可以使用 tf.keras 轻松做到这一点:

model = ResNet50(weights="imagenet", include_top = False)

当一幅图像通过这个时,它将产生 2048 * 7 * 7 的“特征”——对图像进行分类的数字。这是 ResNet50 最后一层的数字。然后,我们可以通过逻辑回归来传递这个大向量,以获得分类——这是一种非常快速有效的方法,可以获得相当不错的准确性,而无需进行任何真正的训练。一个重要的注意事项是,您必须首先通过一些预处理来将图像转换成合适的格式。这个型号需要的尺寸是 224 x 224。张量形状应该是(N,224,224,3),其中 N 是样本数。有一个预定义的前置函数,用 imagenet _ utils . preprocess _ input 减去均值。值得一提的是,我从 Adrian Rosebrock 的《计算机视觉深度学习实践者》一书中学到了这项技术。

bs = 16
# loop in batches
for i in np.arange(0, len(imagePaths), bs):
    batchPaths = imagePaths[i:i + bs]
    batchLabels = labels[i:i + bs]
    batchImages = []
    # preprocess each image
    for j, imagePath in enumerate(batchPaths):
        image = load_img(imagePath, target_size=(224, 224), interpolation='bilinear')
        image = img_to_array(image)

        # expand dims and subtract mean RGB
        image = np.expand_dims(image, axis=0)
        image = imagenet_utils.preprocess_input(image)
        if j==0 and i==0:
            fig, axs = plt.subplots()
            plt.imshow(image.reshape((224,224,3)).astype(np.uint8),clim=(0,255), interpolation=None)
            plt.show()
        batchImages.append(image)

    batchImages = np.vstack(batchImages)
    # extract features
    features = model.predict(batchImages, batch_size=bs)
    features = features.reshape((features.shape[0], 100352))

一旦我们将我们的特征和相关标签存储在数据帧中,我们就可以将它们传递给逻辑回归。本质上,这里的想法是,在图像的这个非常高的维度表示中,应该有一个清晰的划分。我认为这是正确的,因为它很精确:

测试的波形准确度:87%

+--------------+-----------+----------+-------------+---------+
|     Wave     | precision | recall   | f1-score    | support |
+--------------+-----------+----------+-------------+---------+
| healthy      |      0.92 |     0.80 |        0.86 |      15 |
| parkinson    |      0.82 |     0.93 |        0.87 |      15 |
|              |           |          |             |         |
| accuracy     |           |          |        0.87 |      30 |
| macro avg    |      0.87 |     0.87 |        0.87 |      30 |
| weighted avg |      0.87 |     0.87 |        0.87 |      30 |
+--------------+-----------+----------+-------------+---------+

测试的螺旋精度:83%

+--------------+-----------+----------+-------------+---------+
|     Wave     | precision | recall   | f1-score    | support |
+--------------+-----------+----------+-------------+---------+
| healthy      |      0.81 |     0.87 |        0.84 |      15 |
| parkinson    |      0.86 |     0.80 |        0.83 |      15 |
|              |           |          |             |         |
| accuracy     |           |          |        0.83 |      30 |
| macro avg    |      0.83 |     0.83 |        0.83 |      30 |
| weighted avg |      0.83 |     0.83 |        0.83 |      30 |
+--------------+-----------+----------+-------------+---------+

与我们手动创建的只有 15 个特征的模型相比,这是一个明显的改进,也是需要最少努力的一个——但是除了分类之外,我们也没有获得太多信息。

定制卷积神经网络

在我们的最后一种方法中,我们从头开始构建一个定制模型并进行训练。我们只有很少的训练样本——只有 72 个,所以我们使用增强。在训练期间,我创建了一个占训练集 20%的验证集,并对迄今为止我一直在比较的 30 幅未接触过的图像进行了预测。
下面是螺旋模型的总结。我将图像缩减为原来的 2 倍,并转换成灰度。波浪模型完全相同,但图像大小不同。我还为对比度拉伸和标准化应用了一个预处理函数。有关增强、回调等的详细信息。请参考我的代码,因为这超出了本文的范围。优化是使用 Adam 完成的。

Model: "SpiralNet"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 128, 128, 32)      320       
_________________________________________________________________
activation (Activation)      (None, 128, 128, 32)      0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 128, 128, 32)      9248      
_________________________________________________________________
activation_1 (Activation)    (None, 128, 128, 32)      0         
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 64, 64, 32)        0         
_________________________________________________________________
dropout (Dropout)            (None, 64, 64, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 64, 64, 64)        18496     
_________________________________________________________________
activation_2 (Activation)    (None, 64, 64, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 64, 64, 64)        36928     
_________________________________________________________________
activation_3 (Activation)    (None, 64, 64, 64)        0         
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 32, 32, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 32, 32, 64)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 32, 32, 128)       73856     
_________________________________________________________________
activation_4 (Activation)    (None, 32, 32, 128)       0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 32, 32, 128)       147584    
_________________________________________________________________
activation_5 (Activation)    (None, 32, 32, 128)       0         
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 16, 16, 128)       0         
_________________________________________________________________
dropout_2 (Dropout)          (None, 16, 16, 128)       0         
_________________________________________________________________
flatten (Flatten)            (None, 32768)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               4194432   
_________________________________________________________________
activation_6 (Activation)    (None, 128)               0         
_________________________________________________________________
dropout_3 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 129       
_________________________________________________________________
activation_7 (Activation)    (None, 1)                 0         
=================================================================
Total params: 4,480,993
Trainable params: 4,480,993
Non-trainable params: 0
_________________________________________________________________

这是训练板:

来自作者。每个模型的训练和验证曲线。

最后,指标:

测试的波形准确度:87%

+--------------+-----------+----------+-------------+---------+
|   WaveNet    | precision | recall   | f1-score    | support |
+--------------+-----------+----------+-------------+---------+
| healthy      |      0.92 |     0.80 |        0.86 |      15 |
| parkinson    |      0.82 |     0.93 |        0.87 |      15 |
|              |           |          |             |         |
| accuracy     |           |          |        0.87 |      30 |
| macro avg    |      0.87 |     0.87 |        0.87 |      30 |
| weighted avg |      0.87 |     0.87 |        0.87 |      30 |
+--------------+-----------+----------+-------------+---------+

测试中的螺旋精度:90%

+--------------+-----------+----------+-------------+---------+
|  SpiralNet   | precision | recall   | f1-score    | support |
+--------------+-----------+----------+-------------+---------+
| healthy      |      0.93 |     0.87 |        0.90 |      15 |
| parkinson    |      0.88 |     0.93 |        0.90 |      15 |
|              |           |          |             |         |
| accuracy     |           |          |        0.90 |      30 |
| macro avg    |      0.90 |     0.90 |        0.90 |      30 |
| weighted avg |      0.90 |     0.90 |        0.90 |      30 |
+--------------+-----------+----------+-------------+---------+

虽然我们在波集上实现了与 ResNet50 特征提取相同的精度,但是我们在螺旋集上获得了更好的改进。我们的训练集中也只有 30 幅图像,非常小。随着数字的增加,我们可以看到两者的验证精度都更接*,最佳模型的验证精度都在 95%左右。

摘要

下面是对不同方法的相同测试集的结果的最终总结:

+------------------------+------+--------+
|    Test Accuracy %     | Wave | Spiral |
+------------------------+------+--------+
| Random Forest Manual   |   80 |     67 |
| ResNet50 feat. Log Reg |   87 |     83 |
| Custom Nets            |   87 |     90 |
+------------------------+------+--------+

看着这些图片,我对它们的表现很满意,有些图片我看不出有什么不同…一些更多的功能工程可能会影响随机森林手动模型,但我会把这个交给你。

来自吉菲

如果你觉得这篇文章的任何部分提供了一些有用的信息或一点灵感,请关注我。

你可以在我的 Github 上找到源代码。

链接到我的另一篇文章:

  • 通过图像分析对帕金森病进行分类:第一部分 —预处理和探索性数据分析

基于通用句子嵌入的科技论文分类

原文:https://towardsdatascience.com/classifying-scientific-papers-with-universal-sentence-embeddings-4e0695b70c44?source=collection_archive---------29-----------------------

如何教计算机区分天体物理学和计算机科学?

几年来,由于计算机视觉的惊人进步,计算机已经获得了几乎像人类一样的识别建筑物、动物和其他物体的能力。使这成为可能的突破是理论直觉(上世纪 80 年代卷积神经网络的引入)和硬件(得益于 GPU 卡的广泛和经济高效的可用性)的结合,这使得在合理的时间内执行复杂的算法成为可能。这一类别的缩影可能是 VGG 类的模型,即公开可用的部署,因为大多数预先训练的模型。像它的大多数兄弟一样, VGG -19 需要训练一个强大的计算机集群,但是一旦初始阶段完成,它可以部署在不太强大的机器上,在许多情况下甚至是移动设备上(例如参见 MobileNet )。然而,虽然计算机视觉领域在相当长的一段时间内一直处于黄金时代,但对于另一项任务:书面文本的解释来说,情况并非如此。

现在,计算机非常善于识别常见的物体。但是他们足够好去阅读一篇科学论文吗,更不用说去理解它的意思了?

可以说,如果人类不能在为时已晚之前识别出狮子、鬣狗和其他食肉动物的威胁,他们就不会在生物进化的早期阶段幸存下来。另一方面,人类学家认为,随着我们语言能力的提高,人类社会变得越来越复杂。虽然语言最初是如何成为一种东西的还没有被完全理解,但是很明显,交流基本信息并不需要一种具有语法的完全成熟的语言。这个概念由诺姆·乔姆斯基在他的语法等级理论中进行了著名的阐述。因此,期望机器能够理解人类语言,或者至少通过口头交流的方式与我们互动,这并不太遥远,尽管在开始时是有限的。长话短说,我们正在努力,但人类语言的复杂性是一个非常难啃的骨头。最著名的问题可能是共指消解、寻找文本中引用同一实体的所有表达式的任务。拿下面这句话来说:

"我让一个铁球滚下斜坡,当它碰到一扇玻璃窗时,就碎了。"

任何人都可以看出,第一个代词 it 指的是,而第二个代词指的是玻璃,反之亦然。从语法上来说,这两种选择都是完全正确的,但是物理世界的知识和一些直觉引导我们做出正确的匹配。这对机器来说绝非小事。当前的范例需要在大量文本(称为语料库)上训练大型神经网络模型,从这些文本中估计单词之间关联的概率( n -grams)。一旦模型被训练,它随后被微调到某个任务,例如 问答,语言生成,命名实体识别。在下文中,我们将关注一个相对简单的任务:文档分类。我们将训练一台机器根据摘要来识别给定科学论文的类别。

文本嵌入

虽然用计算机可以处理的数字对图像进行编码非常简单(通过将其像素表示为矩阵),但对文本来说却不是这样。其实不同的可能性都是存在的,最好的大多取决于任务本身。最基本的方法包括创建一个字典,,即语料库中出现的所有单词的索引集合。然后,给定的文本由一个向量表示,其中每个元素都是相应单词的索引。

*dictionary = ["apple", "banana", "zucchini"]"banana banana apple" -> [1, 1, 0]
"zucchini apple apple" -> [2, 1, 1]*

这种方法的问题是向量的长度是可变的(事实上相当于文本中的字数),这对于数字处理来说不是一个理想的特性。另一种常见的选择是将文本表示为矩阵,其中每行代表一个单词。反过来,每个单词由一个固定长度的二进制向量表示,其中除了唯一指向字典中相应单词的一个元素之外,所有元素都为零,例如:

*[1,0,0] -> "apple"
[0,1,0] -> "banana"
[0,0,1] -> "zucchini"*

这种方法叫做单热向量,也不是特别适合,因为它需要大量内存,这基本上是浪费了,因为大多数元素都是零。这个问题的常见解决方案是使用稀疏矩阵来代替。

*年来,人们已经意识到,更好的解决方案是将每个单词表示为固定长度的密集向量,例如:

*[0.1, -0.1] -> "apple"
[0.3, 0.1] -> "banana"
[-0.2, 0.1] -> "zucchini"*

最明显的优势是编码每个单词需要更少的维度。字典基本上变成了单词向量的查找表。然而,真正的交易是将语义意义附加到嵌入空间的每个维度的能力。最辉煌的例子如下:给定四个代表国王王后男性女性的向量,人们发现:**

*king - male + female = queen*

或者类似地:

*france - paris + rome = italy*

这是如何实现的超出了本文的范围,但足以说明的是,一个神经网络( word2vec 或 skip-gram 模型)是在一个语料库上训练的,以预测最可能的上下文,给定单词之后的单词,或者两个单词更有可能在给定单词之前和之后找到。

在最*,这种和其他类似的技巧已经被用来通过爬行数百万网页(包括维基百科的完整转储)来训练巨大的网络,如伯特和 GPT-2 。在这一点上,语言处理网络正在做着与计算机视觉网络相同的事情:如果不能访问庞大的样本数据库,它们的训练就不可能进行,而这反过来只有在互联网出现后才成为可能。

最后,对我们的目标来说最重要的是,一个类似的网络已经通过不仅考虑单个单词,而且考虑句子和短文段而得到训练。我们将部署由一个名为通用句子编码器 (USE)的预训练网络创建的嵌入向量,用数字表示文档,并应用一些简单的分类技术。

文件分类:微调神经网络

有了句子嵌入,我们现在可以把注意力转向实际的分类任务。对于这个例子,我们将通过下载 arXiv 服务器上出现的预印本摘要来创建一个用于训练/测试的小型数据库。关于如何下载这些文件的很好的介绍可以在这里找到。

将摘要保存在数据帧中后,我们可以稍后加载该文件,并使用带标签的信息来训练一个简单的网络。

请注意代码的以下部分:

  • 输入由作为文本存储的摘要序列组成。然后,输入被传递到第一层(UniversalEmbedding ),该层本质上是预训练的深度网络。其输出被传递到一个密集层,激活 softmax 来预测类别
  • 为了支持多个类别,输出是一个向量,其中每个元素对应一个给定的类。激活 softmax 使输出标准化,以便可以根据输入属于给定类别的概率进行解释。为了一致性,损失函数是分类交叉熵
*There are 2 known categories: ['cs.AI', 'astro-ph']
Training set has 14550 samples
Testing set has 4850 samplesModel: "AbstractClassifier"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
text_in (InputLayer)         [(None, 1)]               0
_________________________________________________________________
lambda (Lambda)              (None, 512)               0
_________________________________________________________________
dense (Dense)                (None, 16)                8208
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 34
=================================================================Total params: 8,242
Trainable params: 8,242
Non-trainable params: 0
_________________________________________________________________Training...
Train on 11640 samples, validate on 2910 samples
.
.
.
Epoch 19/20
11640/11640 [==============================] - 11s 952us/sample - loss: 0.0143 - acc: 0.9962 - val_loss: 0.0255 - val_acc: 0.9897
Epoch 20/20
11640/11640 [==============================] - 11s 978us/sample - loss: 0.0139 - acc: 0.9964 - val_loss: 0.0254 - val_acc: 0.9911Done training
Testing...
4850/1 - 4s - loss: 0.0097 - acc: 0.9938*

尽管相对简单,该代码能够区分 astro-ph 和 cs 之间的论文。具有 99%准确度和非常少的过度训练的 AI 类别(验证损失稳定在比训练损失 0.0139 更高的值 0.0254,训练损失 0.0139 持续下降更长时间)。事实上,这一壮举是可能的,由于使用嵌入做了繁重的工作。你刚刚发现了迁移学习的乐趣:一旦一个模型已经在一个大型数据库上预先训练好,你所要做的就是根据你的目的对进行微调!

信息太多!

这些单词或句子嵌入的一个方面是嵌入空间的绝对大小,这里是 512 维,这对于实际应用来说是相当麻烦的。最有可能出现的问题是缺乏大型数据库,甚至无法执行微调步骤—将嵌入层连接到具有 64 个输出节点的密集层将需要拟合 51264+64 = 32,832 个参数。根据经验,应该有至少 10 倍以上的训练示例,,即大约 300k,这对于许多应用来说可能很大。*

人们很容易理解,并不是所有的维度都携带有用的信息来完成手头的任务。一种在保留信息的同时减小嵌入空间大小的简单而有效的方法是应用主成分分析 (PCA)。想法是查看所有嵌入向量的变化,并丢弃存在大变化的维度(代表噪声),同时保留具有更强关联的维度(主分量)。

由于实际上不可能将 512 维中发生的事情可视化,一个好的方法是计算这些向量之间以及不同类别之间的余弦相似性(归一化点积)。这种操作背后的思想是,属于相似类别的向量应该彼此靠*,因此具有小角度,而属于不同类别的向量应该远离,因此具有大角度。余弦运算将角度压缩到 0 到 1 之间,使比较更容易。如果 PCA 正在做它的工作,人们应该期望在由于 PCA 的维数减少之后不同类别之间的分离有所改善,,即通过丢弃噪声应该出现“信号”。

这正是下面的图所显示的。蓝色直方图显示了使用完整的 512 嵌入空间的余弦相似性的分布,而红色直方图表示 PCA 后的相同分布。在对角线上,属于同一类的文档应该有一个非常接* 1 的余弦,但似乎不是这样(它们大多分布在 0.5 左右)。此外,通过查看不同类别之间的乘积(非对角线直方图),可以看到很少的分离,一些直方图峰值低于 0.5,但效果并不显著。如果我们使用完整的嵌入空间,对文档进行分类的希望很小!

PCA 前后文档嵌入的余弦相似性。

然而,在 PCA(红色直方图)之后,可以看到一些非常有趣的特征。首先,对角线上的直方图被推向 1,这是一个好现象:同一类别中的论文由彼此接*的向量表示!此外,现在查看非对角线直方图,很明显直方图被推向 0,,即,类别之间的相似性相当低。最后,有趣的是,即使在 PCA 之后,两个类别仍然难以区分:hep-ex(实验高能物理)和 astro-ph(天体物理)。这令人惊讶吗?数据基本上是在告诉我们,这两个科学分支比人工智能(cs)等其他分支有更强的相关性。AI)和数论(数学。NT)?

总而言之,正如许多人在温哥华 NeurIPS2019 大会上所争论的,自然语言处理(NLP)领域正在进入一个黄金时代。NLP 基本上正在经历与 2010 年计算机视觉相同的革命。迁移学习和预训练模型基本上为任何人提供了一个机会,为特定任务(如文档分类)微调简单模型。

保持(微调)调谐!

用🕸网络对莎士比亚进行分类

原文:https://towardsdatascience.com/classifying-shakespeare-with-networks-2a6c1c44e17f?source=collection_archive---------57-----------------------

没有 NLP 是什么造就了喜剧或悲剧(+代码!)

所有莎士比亚的戏剧,以网络形式

威廉·莎士比亚是我们最著名和最受称赞的剧作家之一。他的写作生涯跨越了 36 部戏剧(很容易归因),他作品的一个有趣的方面是这些戏剧可以被分成明确的类别:喜剧、悲剧和历史。喜剧有快乐的结局,令人振奋,其中主要人物克服了一些障碍。悲剧通常以有缺陷的主角的死亡而告终。然而,我们怎样才能正式区分喜剧和悲剧呢?这就是我们将在本文中解决的问题,但我们不会以您可能期望的方式去做。

我的研究(见: cameronraymond.me )专注于网络以及我们之间的联系如何塑造我们的世界。虽然很多人分析了莎士比亚戏剧中的对话,但很少有人费心去研究人物之间的不同之处。喜剧和悲剧是非常不同的——不仅在对话方面,而且在人物和场景的相互关系上。我要证明的是,只看莎士比亚戏剧的结构,而不看任何对话,我们就能确定哪些戏剧是喜剧,哪些是悲剧。

诚然,这乍一看可能有些奇怪。如果你想区分喜剧和悲剧,为什么不看真实的对话呢?

我认为而不是 依赖语言是一种优势,因为虽然不是每个人都说同一种语言,但大多数故事都是由人物关系驱动的。“网络优先”的方法让我们可以不受语言障碍的阻碍,对讲故事进行更普遍的比较。依赖自然语言处理(NLP)的方法需要知道如何解释不同的语言以及同一种语言的不同用法!一个从《西区故事》中学习的 NLP 模型可能很难解释《罗密欧与朱丽叶》,尽管它们都是英文的,并且遵循相同的故事情节。尽管存在代沟,但网络优先的方法有可能利用两者之间的关系相似性。因此,目的是测试莎士比亚的喜剧和悲剧的结构是否清晰到足以区分两者,而不依赖于对语言的任何理解。

数据

谢天谢地,莎士比亚的大部分戏剧都被数字化了。使用一个 Kaggle 数据集,我们可以下载所有 36 个剧本,并将它们的脚本加载到一个数据帧中。由于脚本包含舞台指示,它没有给出角色如何相互关联的信息,我们将删除这些行。既然我们不会分析莎士比亚的历史,我们也可以放弃这些。

既然数据是可用的格式,我们就可以开始构建我们的网络(每个剧本一个)。因为我们对角色之间的关系感兴趣,我们将在两个角色之间添加一个链接,也称为边,如果他们在同一个场景中说话。经常在一起说话的字符可能以某种有意义的方式连接在一起。这将形成我们的网络优先分类模型的基础。

让我们以莎士比亚 1623 年的戏剧为例,结局好的就都好。首先,我们将为剧中对话进行隔离。然后我们再计算每个角色说话的次数,把那些对剧的结构有意义贡献的(说话超过 5 次的)保留下来。

接下来,我们将创建网络。我们可以通过一个场景接一个场景,如果两个角色在同一个场景中说话,在他们之间添加一个链接来做到这一点。这些角色在不同场景中一起说话的次数越多,他们之间的联系就越紧密。

重复所有 27 个剧本,想象一下,这些就是最终的网络!现在我们需要一种只使用这些网络来区分喜剧和悲剧的方法。

如何比较网络

比较网络的传统方法。

现在我们已经为莎士比亚的戏剧建立了网络,我们需要一些方法来区分它们。不幸的是,没有简单的方法来比较两个网络的结构。传统上,最主要的比较方法被称为图形编辑距离(GED)。该方法计算在两个网络变得相同之前,需要在这两个网络之间重新分配多少条边。这在上图中可以看到。不幸的是,这依赖于每个图都有相同数量的节点——否则它们永远不会相同。因为我们的戏剧有各种各样的角色,我们需要转向别的东西。 GED 也比较简单,很少抓住一个网络的底层结构,所以需要更细致入微的比较方法。

密度不同但结构相似的两个图。来源。

为了解决这个问题,我们求助于 2018 年发表在知识发现国际会议上的一篇论文。这些研究人员定义如果信息在一段时间内以相似的方式流经两幅图,那么这两幅图就是相似的。由此产生的方法称为网络拉普拉斯谱描述符,简称 NetLSD。他们的假设是,信息通过一个网络流动,就像热量通过你的空气管道扩散一样。如果网络中的每个节点随着时间的推移释放一些“热量”,并沿着边缘传递给邻居,那么随着时间的推移,它将产生一个捕捉网络结构的签名。所以在每一个时间步,你要做的就是把网络中的“热量”加起来。这由 h(t) 表示,并在下图中显示为 y 轴。由于每个热追踪信号使用相同数量的时间步长,因此比较不同规模的网络要容易得多。通过使用这种技术,他们能够以较高的准确度(94–95%)识别不同类型的蛋白质和酶。

两个相似图形的 NetLSD 热特征:来源:Tsitsulin 等人。

虽然实现 NetLSD 相对简单(特别是如果你使用他们的 Python 包),但是它需要一些关于计算机如何表示网络和线性代数的背景知识。对那些感兴趣的人来说,这里有原文。由于 NetLSD 的内部工作对我们分类喜剧和悲剧的任务并不重要,我将继续。

把所有东西放在一起

现在我们有了一种有意义地比较网络的方法,我们可以对不同的戏剧进行分类。首先,我们将把这 27 部戏剧分成两个部分:我们的训练数据,我们已知类型的观察戏剧,以及测试数据,将被分类的未知戏剧。我们将利用我们以前观察到的戏剧知识来帮助分类未知的戏剧。由于剧本很少,我将保留三分之一的剧本作为测试数据来验证模型。

接下来,我们将采用我们所有的训练数据,我们知道流派的戏剧,并计算它们的热跟踪签名。

让我们看看喜剧和悲剧之间的热量传递是否有明显的不同。这有点混乱,但似乎悲剧比喜剧*均保留了更多的“热度”。使用这些签名,我们可以开始对未知的戏剧进行分类。

先前观察到的活动的热特征(也称为“训练数据”)

对新剧本进行分类的过程相对简单。我们计算未观察到的游戏的热迹特征,然后找到具有最接*的热迹特征的五个先前观察到的游戏。如果这五部中的大部分是喜剧,那么我们就把这部剧标为喜剧,否则我们就把它标为悲剧。这就是所谓的 k *邻(KNN)分类器。KNNs 并不真正对每一个说法进行建模——这就是为什么我更喜欢先验这个术语而不是训练数据——但是与 KNN 一起工作的游戏如此之少,这是一个合理的选择。

我们表现如何?嗯,在我们的测试数据中,我们能够正确分类 9 部戏中的 7 部,原始准确率为 77.8%。测试集包含 7 部喜剧和 2 部悲剧,我们能够正确分类所有的悲剧5/7 部喜剧。这表明,喜剧和悲剧产生了足够不同的结构,我们可以通过查看网络来区分哪个是哪个。

喜剧/悲剧分类混淆矩阵。

那又怎样?

虽然 27 次播放是一个小样本,但我认为这个实验的原则是合理的。网络优先的方法让我们做一些乍一看似乎不太可能的事情:我们能够在不看一段对话的情况下判断出一部剧的类型。我个人认为这很酷。

你是说这意味着我们不再关心 NLP 了?

显然不是。当像戏剧这样的东西被抽象成一个网络时,语言中有大量丰富的信息丢失了。然而,网络给了研究人员不依赖语言或不受语言束缚的灵活性。这又回到了比较不同语言或不同时期戏剧的例子。这些应用程序不会停留在比较戏剧或电影上。虚假信息通常通过创建社交媒体机器人网络来大量传播,这些机器人转发和放大彼此的假新闻,以给人以合法性的错觉。与其关注一个账户在说什么,不如看看它的连接网络是否与已被关闭的虚假信息机器人网络相似,这可能更有帮助。由于网络的可塑性很强,几乎可以用来表示任何东西,因此有很多潜在的应用。如果你想了解更多关于网络科学/图论及其应用的知识,请访问我下面的网站!它链接到我所有的作品。

最初发布于https://cameronraymond . me

[1]: Tsitsulin、Anton、Davide Mottin、Panagiotis Karras、Alexander Bronstein 和 Emmanuel Müller。"倾听图形的形状."在第 24 届 ACM SIGKDD 知识发现国际会议&数据挖掘的会议记录中,第 2347–2356 页。2018.

歌曲类型分类:使用 Spotify 的内置功能还是提取我自己的功能

原文:https://towardsdatascience.com/classifying-song-genre-using-spotifys-built-in-features-vs-extracting-my-own-a4d5fe448948?source=collection_archive---------24-----------------------

实践教程

我总是很难仅凭声音来区分音乐中真正特定的流派——我正在看着你,金属!所以,我把体裁分类的问题变成了数字信号处理问题。我们去处理吧!

【我应该先说我有音乐表演硕士学位,是一名录音艺术家,并且自己出版我的音乐,所以这是我对这个主题的兴趣开始的地方。]

照片由 Adi Goldstein 在 Unsplash 上拍摄

问题是什么,为什么重要?

流派分类对于音乐分发*台很重要( CD Baby 、 Distrokid 和 Songtradr 是一些你可能很熟悉的)。好的流派分配有助于艺术家和联系新的观众,并让听众高兴(你的朋克摇滚播放列表中没有意外的圣诞歌曲)。如果顺利,大家都赢了!

对于这个项目,我想用下面的问题陈述来调查流派分类的问题:

使用来自音频源的特征,分类模型能否以足够高的准确度预测一个 30 秒音频剪辑的流派,以自动将歌曲组织成它们各自的流派?

我们在讨论多少数据?

好问题,读者!这是我们得到的信息:

  • 总共 9365 首歌曲
  • 10 种类型

我把这些歌曲分成简单的 5 种风格集和复杂的 10 种风格集。难度水*是由体裁的紧密程度决定的。下面的橙色和粉色是相互关联的。

我训练和测试模型的流派列表。图片作者。

我从探索数据中发现了哪些有趣的事情?

当我开始使用 Spotify API 的spotipyPython 库挖掘数据时,我发现 Spotify 让你可以访问一些有趣的预设计功能。我将着眼于舞蹈性、乐器性和节奏,但还有许多其他有趣的功能可用!

可舞性是一首歌的可舞性从 0 到 1 的评级。这结合了从歌曲中提取的许多不同的节奏元素。我们看到前五大适合跳舞的类型正是我们所期待的。对不起古典,你的华尔兹不适合跳舞!图片作者。

乐器性是衡量一个音轨是否不包含人声的指标。我们可以看到,古典音乐、序列音乐和巴洛克音乐的*均器乐化程度远远高于说唱音乐、R&B 音乐和嘻哈音乐。这很有道理!图片作者。

这很有趣,不同流派之间的节奏差异很大,但看起来热带小屋的四分位数范围最窄。朋友们,保持你的热带音乐在 120-130 BPM 之间!图片作者。

现在挑战来了:我能打败 Spotify 的功能吗?

Spotify 已经做了一些很好的工作,从音频来源中提取可解释的、有意义的数据,但我想看看我是否可以创建自己的数据。我花了一些时间四处打听和寻找资源(感谢诺亚·克里斯蒂安森和萨拉·苏伊丹!),研究librosa,通过音乐信息检索网站挖掘一些思路。

我用librosa设计了一些东西:

  • 能量:响度的量度
  • 均方根能量:响度的另一种度量
  • 梅尔频率倒谱系数(MFCC) :特定频率范围内响度(功率)的更详细测量。

我不想深究数学,但 MFCC 类似于声波指纹。每一个声音都是特别而独特的,就像你们每一个人一样。

看看这些独一无二的小曲片段!X 轴是时间,Y 轴是频率范围,颜色越亮,该点的频率范围越强大。图片作者。

建模阶段

因为每一个不正确的估计都同样糟糕,所以我使用准确性(10 种类型集的基线 10%)作为我的模型成功的衡量标准。我测试了几个不同的模型,最后使用了一个支持向量分类器(SVC) ,原因如下:

  • 最终,的表现超过了我测试的其他模型(包括一个卷积神经网络)
  • 用当前数据集来拟合模型只需要几秒钟

所以这是更好更快——你还能要求什么呢?唯一的问题是,如果我有数百万首歌曲,这个模型可能无法很好地扩展。

下面是我的模型——用新设计的功能构建——与另一个用 Spotify 的预设计功能训练的 SVC 相比的表现:

5 个流派的集合是更容易的集合,与 10 个流派的集合相比,它们之间的关联更少。图片作者。

突击测验:你能猜出这些歌曲的流派吗?我的模型不能。

FDVM 再爱

  • 预测类型:流行音乐
  • 实际类型:热带住宅

我唯一能猜到的是,这里的鼓点不像其他热带屋的曲目那样存在。

美利坚合众国总统的小猫

  • 预测类型:热带住宅
  • 实际类型:摇滚

这首每段结尾都有动态滴,类似热带屋歌。

西海岸的派对(壮举。费丝·伊文斯马托马和臭名昭著的 B.I.G

  • 预测类型:热带住宅
  • 实际类型:说唱

这首歌刚刚得到了一个糟糕的 30 秒抽签,Spotify 为我们的 30 秒预览随机选择的歌曲片段是合唱部分,在我的人类耳朵里听起来更像是热带房屋的曲调。

如果你想玩玩,看看更多不正确的猜测,我已经在我的公共 Github 中构建了一些代码,它将从你选择的任何流派中返回一首随机错误预测的歌曲!

结果

图片作者。

我从这个过程中学到的是子流派在声音层面上没有很好的定义。可能还有其他因素影响到次类型的创作。在这一点上,我不能预测足够准确的类型来进行自信的部署,但我希望在未来尝试一些事情:

  • 从音频中提取歌词。我认为对歌词的分析有助于预测子流派。例如,我认为说唱歌曲的歌词总数会比 r & b 歌曲多,这可能是一种在这两种风格之间做出决定的方式。
  • 无监督学习模型。我很想看看当计算机对它们进行分类时会出现多少流派,然后使用迁移学习将结果反馈到另一个模型中。

感谢阅读!如果您想查看该项目,请前往我的公开回购。由于使用的一些数据集超过 100MB,我无法将它们全部上传到 GitHub,但你应该可以用我的代码重建它们。我很乐意更多地谈论这个项目——留下评论或在 LinkedIn 上找到我!

来源和进一步阅读/观看

  • 【https://musicinformationretrieval.com/energy.html
  • https://musicinformationretrieval.com/mfcc.html
  • 医学博士 SahidullahSaha,Gou tam(2012 年 5 月)。“说话人识别 MFCC 计算中基于块的变换的设计、分析和实验评估”。言语交流。54 (4): 543–565.doi:10.1016/j . specom . 2011 . 11 . 004
  • 亚当斯塞斯。“DSP 背景——音频分类的深度学习”。
    https://www.youtube.com/watch?v=Z7YM-HAz-
    IY&list = pl ha 3 b 2k 8 r 3t 2 ng 1 ww _ 7 mixeh 1 pfqjqi _ P

用 Sklearn 对足球运动员的位置进行分类。

原文:https://towardsdatascience.com/classifying-the-position-of-the-football-player-based-on-their-performance-statistics-using-fifa-23f317eeb493?source=collection_archive---------35-----------------------

使用 Fifa 18 比赛数据,将足球运动员分类为 CM、ST、CB 和 GK。

来源 : Freepik

感谢‘Aman Shrivastava’分享数据。

我们将使用所提供的数据集,该数据集已经过处理并准备就绪。

用例:作为一名用户,我有一名球员的统计数据,我想知道这名球员应该打前锋(ST)、中场(CM)、中后卫(CB)还是守门员(GK)。

我用过的数据是 FIFA 2018 比赛中所有球员的属性得分。数据的尺寸是 14045 x 74,但我使用的是专业球员的数据,他们被选择作为前锋、中后卫、中场和门将。经过此筛选后,剩余的数据集有 7587 x 77 个维度。

数据分析

当我们检查过滤后的数据时,我们注意到 CM 位置的样本量与其他位置相比较小。这将影响偏向重样本的预测。

有些球员根据他们踢的位置而擅长一些技能。举例来说,ST 必须进行更少的铲球,所以他们的铲球能力将低于 CB,反之亦然。

通过检查变量“PrefPos”的皮尔逊相关性,也分享了这一证据。

为了自己找到相关性,首先使用 cat.codes 方法将 PrefPos 变量更改为 numeric category。

下面的形象化比较了铲球能力和终结能力,但是有一个可变的“年龄”,一个球员是否有可能在早年成为一个糟糕的终结者,随着时间的推移,他的技能会有所提高?

本页底部有一些更有趣的方框图。

培训和测试取样

我们希望通过为具有训练集和测试集的样本创建子群体来创建具有相等样本比率的样本,以避免出现 CM 位置的样本量更少的情况,然后我们选择并分配变量['PrefPos']作为目标。

训练模型和预测

因为我们的目标是多类分类,所以 sklearn 提供了一些选项。我使用 SGD 分类器,因为它本身能够处理多类,如下所示:

让我们在训练好的模型上拟合测试集,并得到预测

交叉验证和准确性

显然,目前我们得到了 42%的总体精度,但正如我们在上面检查的那样,该精度因位置而异,CM 位置的精度远低于 42%。

多类混淆矩阵

我们现在来看看错误率。

结论:

对模型的初步检验表明,在根据运动员的表现对他们进行分类的情况下,有机会进一步改进模型的性能。有机会做进一步的特征工程,并使用深度学习算法来进一步提高模型的性能。

完整代码:

从这里下载数据。

我还研究了年龄对专业运动员某些技能的影响,结果如下:

ST *均具有较好的整理能力,并随着年龄的增长而提高

反应是任何运动的一个必要方面,但 CM 往往略胜一筹。

CBs 擅长滑铲。

受年龄影响的一个属性是冲刺速度。

感谢您的阅读,我打算在下一篇博客中讨论初级强化学习。

数据来源:再次特别感谢 Aman Shrivastava 分享数据。

在线评论论坛中的毒性分类:端到端项目

原文:https://towardsdatascience.com/classifying-toxicity-in-online-comment-forums-end-to-end-project-57720af39d0b?source=collection_archive---------43-----------------------

利用自然语言处理(NLP)和深度学习对用户提交的评论中的毒性进行分类和跟踪。

脸书评论按钮

一个完整的交互式 Jupyter 笔记本,包含代码和解释,可以在我的 Github 这里找到。

介绍

自从社交媒体和在线对话成为日常生活的一部分以来,在线处理毒性和遏制骚扰已经成为一个日益严重的问题。几乎不可能在没有目睹不必要的骚扰或不尊重等有害行为的情况下进行在线对话。数字世界有潜力成为一个通过向人们学习来促进增长、同情和教育的社区,但受到利用这种面对面断开的用户的阻碍。

我相信每个人都有权利参与在线对话,而不必担心被骚扰或成为无端辱骂的目标,这让我想起了不久前我在脸书无意中发现科林·卡佩尼克帖子上一些关于被邀请回 NFL 试训的粗鲁评论。

谁是科林·卡佩尼克?他是旧金山 49 人队的四分卫,在国歌声中跪下抗议美国的警察暴行和种族不*等。

下面的评论来自他的帖子,并启发我使用我在数据科学方面的技能来有效地检测和跟踪用户评论的毒性,如下所示。

方法学

我使用的数据来自 Kaggle,由用户在在线论坛上提交的大约 180 万条评论组成。每条评论都标有让另一个人退出对话的可能性。我把毒性定义为任何大于 0.5 的概率。数据集可以在这里找到。

在将我的数据转换成分类算法可以使用的格式之前,我对原始文本数据进行了预处理。这包括:

  • 删除不需要的字符

  • 标记化

  • 删除停用词和缩写(我创建了自己的停用词列表)

  • 词性标注

  • 词汇化

为了更多地了解我的数据,我计算了词汇化后的单词数和我们的词汇量(独特的单词)。此外,知道我们最长的句子有多长可能会有帮助。

现在我准备开始建模过程。每个模型都是不同的单词嵌入、维度缩减和分类算法的组合。我使用了如下所示的工具和受试者操作特征(ROC)曲线下面积(AUC)来对每个模型的性能进行评分。

结果

我最好的模型是通过深度学习获得的。我使用了带有 Word2Vec 嵌入的双向 LSTM。这意味着我用预先训练好的单词向量将我所有的单词嵌入到谷歌的 Word2Vec 词汇表中,并让我的模型学习其余的。我的 vocab 有 2 亿个单词在谷歌的 Word2Vec vocab 中,这使得我的模型只剩下 20 万个嵌入来训练。使用预先训练的单词向量大大减少了计算时间并显著提高了性能。

网络架构:双向 LSTM

双向长短期记忆(LSTM)是一种递归神经网络(RNN)架构,围绕记忆更新策略增加了复杂性,允许我们捕捉单词使用的空间模式,这与人类如何交流极其相关。通过将文本视为单词序列,并以明确的顺序方式处理这些单词,我们可以在模型中获得很大的预测能力。

双向仅仅意味着我们从前到后和从后到前处理文本,允许我们捕捉丰富的上下文。利用 Keras,以下是我使用的超参数。

受试者工作特征曲线

利用预先训练的词向量和深度学习的力量,我的最终模型在性能上给了我一个显著的提升,结果是

ROC AUC=0.951

利用深度学习能力的唯一问题是我们在模型中失去了可解释性。我无法看到特性的重要性来检查我的模型是否做出了正确的决策。相反,我去了在线评论论坛,让我的模型实时预测评论,供我观察。

所以回到科林·卡佩尼克之前的帖子,使用我的模型,我们看到毒性评论被标记为 99.5%的毒性。而“头号粉丝”的评论毒性为 0.2%。

结论

回到最初的问题,这种模式如何在我们的在线对话中增加礼貌?嗯,要看*台。例如,在线游戏,你可能想检测有毒用户,然后根据违规次数或违规严重程度实施某种限制。

社会化媒体

然而,对于像脸书和推特这样的社交媒体*台,你可能不想限制人们的言论自由。如果我们在人们打字时在评论上方放置实时毒性追踪器,他们可以对自己说的话更加自觉,并有希望第二次猜测他们将要发布的毒性评论。

毒性追踪器只需要放在有上网不礼貌历史的用户身上。与游戏类似,我们可以使用人工智能来检测这些用户,然后对他们使用毒性跟踪器。进行一项案例研究,看看有中毒史的用户在开启网络聊天时是否会改变他们的行为,这将是一件有趣的事情。

参考

[## 如何解决 90%的 NLP 问题:一步一步的指南

使用机器学习来理解和利用文本。

blog.insightdatascience.com](https://blog.insightdatascience.com/how-to-solve-90-of-nlp-problems-a-step-by-step-guide-fda605278e4e)

分析数据的清晰架构

原文:https://towardsdatascience.com/clean-architecture-of-analyzing-data-7e689da7dd4a?source=collection_archive---------44-----------------------

简化数据分析过程

不管你来自什么职业,如果你的企业或服务有客户,那么你肯定是在分析数据。这种分析是你对未来发展充满信心的根源。在这篇文章中,我将深入分析数据的过程以及从中获取最大收益的方法。让我们深潜。

数据分析不仅仅与在硅谷工作的科技公司相关。它是每个行业的一部分,适用于所有产品。医药、杂货店、大学、气象站以及几乎所有你能想到的行业每天都要处理大量的数据。我将解释并演示一个典型的数据分析过程是如何工作的。但是,这些步骤并不详尽,并且可能不会按照所述的直线顺序发生。这只是勾勒出数据分析作为架构的整体图景。

解决任何分析问题的一般思维模式是通过假设检验的结果来看待它。什么是假设检验?它是验证结果的程序,从结果开始。我们形成一个结果(称为假设),然后解决问题以证明假设是真的。类似地,当解决一个分析问题时,我们从决策开始,从结果中需要什么开始,然后形成我们达到这个结果的策略。不断回顾这一结果并反思方法是否正确总是一个好主意。原始流程是这样的,我们从定义决策和分析所需的输出开始。然后是可以使用的分析方法,项目有什么数据要求。下一步是获取数据,收集数据并将其存储在一个可访问的位置,并进一步使用它来执行分析。这种方法通常回答有关项目可行性的问题。

这是建筑的草图。虽然它并不详尽,可能会根据项目的需求不断变化

1.问题/需求收集

理解需求是关键。重要的是要找出你想要解决的业务问题是什么,以及你想要衡量的 KPI 是什么。下一步是规划前进的道路。我推荐的一个非常有用的策略是花些时间从给你的信息中找出数据指针。这些数据指针是性能指标,它们将构成您需要关注的信息集。问具体的问题很重要。这个阶段也为您的数据分析解决方案奠定了基础。从数据中记下你认为会对决策产生影响的关键领域。一旦完成并理解了这些,你现在就可以进入数据探索和收集了。

2.数据采集

在加载数据和执行前两步的过程中,涵盖了数据采集的主要部分。但是,作为一个过程,获取不仅仅是将数据从一个资源带到您的工作空间。关于这次收购,一直存在术语上的混乱。首先,“数据获取”有时用于指组织产生的数据,而不是(或同时)来自组织外部的数据。这是一个谬论,因为组织产生的数据是已经获得的。因此,我们只考虑从其他来源和流程中获取的数据。

这方面的一个重要步骤是识别数据的来源和类型。数据可以多次从云服务移动到本地主机,或者来自 API 调用。我的建议是,如果数据源在云上,并且是静态数据(在一段时间内保持不变,不会改变),那么在这段时间内在本地下载数据、执行分析并发布报告将是一个不错的选择。另一方面,如果数据是一个连续的流(刷新数据),我建议在云本身上执行分析。

使用 Panda 的 read_csv 函数将本地文件导入为 Panda 数据框[图片由作者提供]

3.数据争论/数据准备

根据 Aberdeen Group 的一份报告,数据准备“是指任何旨在提高数据质量、可用性、可访问性或可移植性的活动。这一步从收集数据并将其导入到您的工作区开始。从现在开始,由于有可能向您展示数据,我将使用一个数据集来解释进一步的步骤。它将首先以大多数代码能够理解的格式接收数据。如果使用基于查询的程序来处理数据,任何 SQL 语法都可以。我将在一个 CSV 文件上继续使用 Python。这里需要注意的一件重要事情是,数据是 CSV 格式,TSV 格式对于 Python,R 来说更容易理解和解释。同时,这些格式可以很容易地在 Excel 上运行以可视化,但同时,XLS 格式的文档很难理解和运行。您可以使用各种语句将数据加载到 Python 环境中。

导入数据的另一种方式。打开文件并创建一个流以写入文件。“打开”函数以只读、二进制读写和其他方式打开文件

一旦数据被导入,一些方法有助于理解存在的变量类型和可用的数据量。这可以使用一些 Pandas 函数来完成,如“数据帧”。Info()”或“DataFrame.head()”。下一步是试图找到这些数据中的异常。这些异常可能是空白列、缺失值,或者在某些情况下是异常值。对于许多分析师来说,处理异常值的概念非常不同,处理异常值也主要取决于手头的问题。有时删除离群值可能是有意义的,有时我们可能希望为所有列创建一个标准化的表格来弥合离群值,这需要标准偏差和方差。这里的一般规则是,如果数据点距离数据的*均值±3 SD,建议忽略该异常值,不考虑进行进一步分析。

4.数据清理

数据清理部分初始化一些可视化库,以检查数据差异。这很重要,因为异常值会极大地导致我们对分析的误解。Pandas、Matplotlib 和 Seaborn 中有一些公共库可用于识别这些异常值。让我们看一些代码,这些代码将确定数据中是否有需要清理的值。我在这里解释实现的两个部分,一个是寻找缺失值,另一个是寻找异常值。

要查找缺失值,我们可以使用下面的函数:IsNull()或 isna()。下面的热图显示了在列中显示为 Null 的数据点。然后,可以使用插补技术(如均值/中值插补)对其进行填充(尽管生产级代码不考虑均值插补)

热图用于查找数据中的空值/缺失值[图片由作者提供]

另一个重要的技术是绘制一个箱线图。这些图通常揭示了我们正在尝试处理的数据的大量信息,并且可以很容易地用于识别风险因素,如数据中的偏斜度和数据中的异常值。

方框图的示例实现[图片由作者提供]

箱线图的解释可通过以下方式完成:

图片由 SimplyPsychology 解释箱线图的解释[1]

最低分:最低分,排除离群值(显示在左须末端)。

下四分位数:25%的分数低于下四分位数(也称为第一四分位数)。

中位数:中位数标志着数据的中点,由将方框分成两部分的线表示(有时称为第二个四分位数)。一半分数大于或等于该值,一半分数小于该值。

上四分位:百分之七十五的分数落在上四分位值以下(也称为第三四分位)。因此,25%的数据高于该值。

最高分:最高分,排除离群值(显示在右须末端)。

触须:上下触须代表中间 50%以外的分数(即较低的 25%分数和较高的 25%分数)。

四分位范围(或 IQR): 这是显示中间 50%分数的箱形图(即第 25 和第 75 百分位之间的范围)。

处理有偏差或缺失的数据将根据不同的业务场景而改变。正如我之前解释的一般规则,任何超过 3 个标准差阈值的数据点都可以被删除,因为这是一个无法解决的直接异常情况。对于插补,使用了许多技术,如均值插补、热箱插补等。

5.数据探索

数据的探索性分析不仅令人着迷,而且也是收集数据中的架构和依赖关系的最佳形式之一。这个阶段可以或者有时不可以与手头的主要问题相关联,但是仍然在我的分析解决方案的干净架构中占有一席之地。这样做的主要原因是从业务需求中形成的用例经常会回答所有的问题,尽管有时我们可能会遗漏一些数据。这些数据对于解决手头的问题可能是必需的,也可能不是必需的,但是对于掌握数据集的结构非常有用。

这可以由各种步骤和图表组成,您可以使用这些步骤和图表来分析数据,并探索不同数据值之间的联系和意义。这里的目标应该是彻底理解表格列的工作方式以及它们所包含的值。这是实际预测分析和得出结论之前的阶段,因此应该利用它来清除与正在分析的数据相关的所有形式的疑问。我列出了几个可以使用的基本图,以及从中可以得出什么样的衍生品。

绘制像箱线图、分布图和散点图这样的图表将向您展示数据曲线的样子,它在哪里是偏斜的,以及可能的异常值是什么。帕累托图和直方图有助于分析数据的置信区间以及最常见的值。如果数据是多维的,并且变化超出了理论上的认识,我们可以使用类似主成分分析(PCA)的东西来执行维数减少。

6.预测和结论

我们正慢慢接*这一进程的尾声,因为大部分分析现已结束,但解决方案仍未完成。这里要考虑的一个重要方面是,对于任何数据分析解决方案;解决方案的交付通常不会交给其他数据分析师。因此,我们从数据中得出的结论需要用大多数人都能理解的语言来表达。这可以在可视化阶段得到完善,但尽管如此,越容易理解的东西,就越容易呈现。对于根据数据做出的预测,请确保仅根据第一步的要求做出预测。很少有分析师喜欢将额外的信息可视化,假设大量的数据是一个好的预测。理论上,这可能证明你在分析过程中做得很好,但最终如果消费者不需要额外的数据,这些数据就没有任何用处。因此,要记住的重要一点是尽可能保持简单明了。

7.数据可视化

我们现在来看架构中的最后一部分,数据可视化更像是为您的数据将要提供的通信添加一个额外的验证层。现在,可视化本身是一个巨大的进步,包含了许多概念,也可以有自己的架构。通过认识到数据可视化本身是一项全职工作,不像架构图的所有其他步骤,可以理解这一点的深度。

我已经创建了一个单独的故事来解释数据可视化实际上有多重要,我建议大家浏览一下。

https://medium . com/analytics-vid hya/the-science-of-data-visualization-995 e 45238354

8.交流(讲故事)

尽管数据可视化涵盖了分析的大部分图形信息,但架构的最后一部分是讲故事的艺术。一旦分析、预测和可视化完成,我们就回到任务的第一步,收集需求并理解我们需要回答的问题。沟通包括给最终用户答案。在得出这个结论的过程中,我们可能会遇到不需要放入结论中的各种数据段。在我看来,给出结果的最好形式是一页或者最多两页的报告仪表板。[2]该报告将包含我们正在解决的问题的准确、中肯的答案,并且只包含最终用户需要的数据。在沟通过程中使用的语言应该从业务用户的角度出发,而不是从数据科学家的角度出发。有一些工具可以用来以创造性的方式传达这一信息——tableau、adobe analytics 是一些非常棒的仪表板工具。我将为如何开始讲故事的艺术写一篇单独的文章。但是,同时让我向您展示几个很棒的仪表板示例。

来自公众的图片 Tableau Viz Gallery 作者尤里·法尔展示了 AAPL(苹果)的股价走势

网络分析仪表板图片在 Unsplash 上由卢克·切瑟拍摄

并不总是需要使用强大的工具和复杂的算法来解决数据分析问题。虽然,当需要时,许多工具将为您提供先进的技术和选择,但代价是过于复杂。将解决方案复杂化的一个缺点是将这些结果呈现给涉众。你的程序越复杂,就越难提出来。

数据驱动项目的当前状态需要改变,因为敏捷框架已经为软件工程创造了奇迹,所以是时候采用迭代方法来解决数据科学问题了。我相信这个提议的解决方案有助于缓解项目的生命周期,并且到目前为止已经为我展示了清晰的结果。

请在下面的评论中让我知道你对遵循一种架构进行数据科学项目的想法,或者在 Twitter 上与我联系。

参考文献:

[1]关于箱形图的细节和解释—https://www.simplypsychology.org/boxplots.html

[2]用于单页报告仪表板的 Tableau 博客—https://www . Tableau . com/about/blog/2017/7/viz-gallery-contest-winners-prove-visual-analytics-art-73390

像专家一样预处理数据:Scikit 简介-学习管道

原文:https://towardsdatascience.com/clean-efficient-data-pipelines-with-pythons-sklearn-2472de04c0ea?source=collection_archive---------7-----------------------

用于估算、缩放、编码和转换数据的可重复使用的函数

20 年代的数据争论照片由马希尔·云萨尔在 Unsplash 上拍摄

"如果没有一个系统的方法来启动和保持数据干净,坏数据就会发生."

—多纳托·迪奥里奥

您编写了所有的查询,收集了所有的数据,并全力以赴实现您在 Medium 上读到的最新机器学习算法。等等!你很快意识到你需要处理缺失数据、插补、分类数据、标准化等。

您可以开始编写可应用于任何数据集的函数和数据管道,而不是“手动”预处理数据。幸运的是,python 的 Scikit-Learn 库有几个类可以让这一切变得轻而易举!

在本文中,您将学习如何:

  • 在任何数据集上轻松再现转换。
  • 轻松跟踪应用于数据集的所有变换。
  • 开始构建您的转换库,您可以在以后的不同项目中使用它。

tl:dr :让我们建立一个管道,在那里我们可以像这样估算、转换、缩放和编码:

**from sklearn.compose import ColumnTransformer**data_pipeline = ColumnTransformer([
    ('numerical', num_pipeline, num_vars),
    ('categorical', OneHotEncoder(), cat_vars),

])airbnb_processed = data_pipeline.fit_transform(airbnb_data)

不需要了解太多,你就可以推断出不同的变换应用于数值变量和分类变量。让我们进入众所周知的杂草,看看我们如何结束这个管道。

数据

对于所有这些例子,我将使用来自 insideairbnb.com 的 airbnb 纽约房源数据集。这是一个真实的数据集,包含从 airbnb 搜集的信息,并拥有与该网站上的一个房源相关的所有信息。

假设我们想要预测一个列表的价格,给定一些变量,如财产类型和邻居。

raw_data = pd.read_csv('[http://data.insideairbnb.com/united-states/ny/new-york-city/2020-07-07/data/listings.csv.gz'](http://data.insideairbnb.com/united-states/ny/new-york-city/2020-07-07/data/listings.csv.gz'),
                        compression='gzip')

让我们从获取我们想要使用的分类变量和数值变量开始。为了简单起见,我们将删除我们感兴趣的分类变量中缺失值的数据,并且不进行审查。

归罪

如果数据集没有丢失数据,它是真实的吗?现实情况是,我们必须一直处理缺失数据。您必须决定如何为您的特定用途处理丢失的数据

  • 您可以删除带有缺失数据的 na()行。可能会丢失太多数据。
  • 删除缺少数据的变量。如果你真的想要那个变量呢?
  • 将 NAs 替换为零、*均值、中值或其他计算值。

Scikit-Learn 为我们提供了一个简单的类来处理缺失值。

让我们用中位数来估算数字变量,如价格或保证金。为简单起见,我们对所有数值变量都这样做。

**from sklearn.impute import SimpleImputer**imputer = SimpleImputer(strategy="median")# Num_vars is the list of numerical variables 
airbnb_num = airbnb_data[num_vars]airbnb_num = imputer.fit_transform(airbnb_num)

SimpleImputer类将用中值替换所有缺失的值。.fit_transform()方法将返回一个很好的 numpy 数组对象,可以用于任何机器学习方法。您可以选择不同的指标,并将其作为参数传递给。

编码分类变量

数字变量非常简单。让我们处理通常以字符串形式出现的分类数据。在这种情况下,我们必须处理诸如邻居、房间类型、床类型等变量。机器学习算法更好地处理数字,所以我们将转换我们的分类变量。

我们的分类数据看起来如何:

我们将转换所有的文本变量。

我们使用OrdinalEncoder将字符串数据转换成数字。变量中的每个唯一值将被映射到一个数字。例如,公寓=0,公寓=1,等等。

**from  sklearn.preprocessing import OrdinalEncoder**ordinal_encoder = OrdinalEncoder()airbnb_cat_encoded = ordinal_encoder.fit_transform(airbnb_cat)
airbnb_cat_encoded[:,1:10]

编码后,我们得到:

我们所有的文本数据现在都被编码了。

完事了吗?还没有。ML 算法可以从字面上理解事物,认为类别 1 和类别 2 比类别 1 和类别 19 更相似。这可能有利于评级或有秩序的事物,但邻里关系呢?这个简单的编码器能区分 SoHo 和 TriBeCa 的氛围吗?它知道所有的正常人都在市中心工作吗?

曼哈顿室友类型 via 6sqft

我们可以将数据转换为每个类别都有一个单一的属性。例如,当“property_type”为“House”时,我们创建一个等于 1 的属性,否则创建一个等于 0 的属性。对所有其他类别重复上述步骤。

这个过程被称为一键编码。如果你来自统计学或计量经济学背景,这些被称为虚拟变量/属性。

**from sklearn.preprocessing import OneHotEncoder**cat_encoder = OneHotEncoder()airbnb_cat_hot_encoded = cat_encoder.fit_transform(airbnb_cat)
airbnb_cat_hot_encoded<48563x281 sparse matrix of type '<class 'numpy.float64'>'
	with 388504 stored elements in Compressed Sparse Row format>

出现一个野稀疏矩阵!编码器返回的不是 NumPy 数组,而是一个稀疏矩阵,这在我们有数百个类别的多个分类属性时非常方便。

除了每行有一个 1 之外,矩阵中全是 0。这将使用大量的存储器来存储,但是备用矩阵是智能的,并且只存储非零元素的位置。

特征缩放

在使用机器学习算法时,缩放我们的数据非常重要。也有例外,但是当变量处于不同的尺度时,我们通常需要对它们进行缩放。你可以在这里阅读全部内容。

有几种方法可以做到:

  • 最小-最大缩放:减去最小值并除以范围(最大-最小)。值的范围从 0 到 1。MinMaxScaler 就是这样做的。
  • 标准化:减去*均值,除以标准差。你最终得到的是 0 均值和单位方差。在这种情况下,值是不受限制的。标准化受异常值的影响较小。我们可以使用 StandardScaler。
**from sklearn.preprocessing import StandardScaler**StandardScaler().fit_transform(airbnb_num)

那很容易!

自定义转换

Scikit-Learn API 非常灵活,允许您创建自己的自定义“转换”,您可以轻松地将它合并到您的流程中。您只需要实现 fit()、transform()和 fit_transform()方法。

添加TransformerMixin作为基类会自动获得 fit_transform()方法。

这里,我们有一个非常简单的转换器,它创建了评级与评论数量的比率。你可以把这些弄得尽可能复杂。

查看 Scikit-Learn 文档了解更多详细信息和示例。

管道

我们终于可以把所有东西放在一起了!一个数据集可能需要多次转换,这些转换根据变量类型而有所不同。这些也必须以正确的顺序执行。

Scikit-Learn 为我们提供了 Pipeline 类来帮助这个无处不在的过程编写干净高效的代码。

把所有的放在一起

我们为数字变量创建了一个管道。管道构造函数接受一个(' Estimator Name ',Estimator())对列表。除了最后一个估算器,所有估算器都必须有 fit_transform()方法。他们一定是变形金刚。名字应该是信息丰富的,但你可以把任何你想要的。

让我们用分类数据完成我们的管道,并创建我们的“主”管道

我们可以组合应用于不同变量集的不同管道。在这里,我们将数字管道(Impute、Transform、Scale)应用于数字变量(num_vars 是列名列表),并对分类变量(cat_vars 是列名列表)进行热编码。

ColumnTransformer类将完全按照名字所暗示的那样去做。它会将管道或转换器应用于指定的变量列表。

它接受一个元组列表(“名称”,转换器/管道,“变量列表”)

一旦我们建立并定义了我们的管道流程,我们就可以使用fit_transform()方法轻松地将其应用于任何数据集或新数据。

结论

现在,您已经了解了实现干净高效的管道的基础知识,这些管道可以在不同的数据集上轻松地组织和利用。有关更多详细信息,请参考 Scikit-Learn 文档

为什么止步于预处理?在管道中,你可以完成你的模型的训练。下面是一个快速完整的示例:

来自 Scikit-Learn 文档的示例

一旦您有了数据转换设置,您就可以将培训作为另一个“评估者”包含在您的管道中。您的管道可以用作任何其他估计器。SVC()包含在末尾,将使用传递给它的缩放数据。然后,您可以将此管道重新用于许多其他机器学习算法或数据集。

希望这个框架能帮助你写出易于维护的简洁代码。你可以在这个 git 库的 jupyter 笔记本中找到这个 airbnb 完整的工作示例。

如果你觉得这是有价值和有帮助的,请鼓掌并分享。关注我或在 linkedin 上随意联系。

更清晰的蒙特卡罗模拟图

原文:https://towardsdatascience.com/cleaner-monte-carlo-simulation-graphs-145b5be7f6ab?source=collection_archive---------35-----------------------

展示 1000 次模拟结果的快速指南

不久前,我在对投资组合配置进行蒙特卡罗模拟,试图找出如何让图表看起来更好。现在,我不是可视化大师的图形专家,但我确实想出了让它看起来更好的方法,这将只是一篇与你分享的短文!

如前所述,我运行了一些蒙特卡洛模拟,以计算出我的投资组合在 30 年后会是什么样子。如果你感兴趣,这是代码片段(但是完整的代码可以在我的 github 上找到)。我在接下来的 30 年里模拟了三种不同的基金(使用它们的均值和标准差)。我对这些基金进行了不同的加权,然后对它们进行了 1000 次模拟。

从最初的剧情可以看出,一点用处都没有。所有的 1000 个结果都是用全彩色绘制的,真的没有任何有用的信息可以从中收集到。我可以说出结果的大致范围,但除此之外,它真的不是特别有用。

我认为最重要的投资组合是(a)第 75 个百分点,(b)中间值,(c)第 25 个百分点,以及(d)基础投资组合(即不投资于市场的纯储蓄)

为了获得第 25、50 和 75 个百分点的投资组合的位置,我结合了分位数和指数函数,将它们转换为字符串,以便以后在绘图时可以使用它们的列名

在绘制图时,我将所有的图都设置为灰色。这将使它们淡出背景,并允许我突出重要的投资组合结果(按照前面提到的重要结果)

显然没有什么值得大书特书的,但现在至少我们有了一张空白的画布来工作!

列表中的quantiles都是字符串。这使得我可以轻松地调用带有.loc的特定投资组合,并引用确切的投资组合。我还创建了一个使用颜色和标签的列表。我还添加了一个图例来显示标签。就这样,这就是最终产品!

与最初的情节相比,最终的结果看起来好了很多。它给出了相同的背景信息(可能结果的大致范围),但也强调了重要的投资组合。或许更好的方法是去除异常结果。然而,出于我研究的目的,我想展示所有的结果,因此就把它留在这里了。

数据可视化是数据故事中如此重要的一部分。虽然我们可以有最酷的结果和发现,但一个传达清晰信息的好图也是至关重要的,尤其是在今天的图形密集型文化中。

最初发表于【zachlim98.github.io/me】

清理、重构和模块化:提高 Python 代码和职业生涯的“必备”基础

原文:https://towardsdatascience.com/cleaning-refactoring-and-modular-the-must-foundations-to-improve-your-python-code-and-carrer-65ef71cdb264?source=collection_archive---------16-----------------------

一些代码习惯如何将您的开发过程和职业生涯带到一个全新的水*

资源: Jonatan Pie at Unplash

“任何傻瓜都能写出计算机能理解的代码。优秀的程序员会写出人类能理解的代码。”

马丁·福勒

我们都知道,在开发过程的高峰期,我们通常更关注于让我们的程序工作,而不是让它完全可读。当我们面临一个前所未有的问题,并且有一个紧迫的期限来交付工作时,这种情况会变得更加复杂。有时,我们不得不求助于堆栈溢出来寻找解决方案,或者花些时间阅读所有类似的问题,直到找到解决问题的新思路。

"当你的程序一塌糊涂,但它做了它的工作."

来源:Tumblr 上的凯凯

我经常使用堆栈溢出,但至少对我来说,我花在寻找解决方案上的时间减少了我通常用来使代码更干净和可读的时间(一个艰难而漫长的时间)。如果这种情况也发生在你身上,也许是因为我们更关注“如何解决”而不是让其他开发人员可以使用它,嗯… 我们想让它工作!尽管我们有时间格式化和记录一些东西,但是谁从来没有花几个小时去理解一些多年前的代码呢?原因很简单,编写干净、可读的代码是一件艰苦而累人的事情,但我们应该经常思考伟大的“鲍勃叔叔”的下面这句话:

“花在阅读和写作上的时间比远远超过 10 比 1。作为编写新代码工作的一部分,我们不断地阅读旧代码。…[因此,]让它易于阅读会让它更易于书写。”

罗伯特·c·马丁, 干净的代码:敏捷软件工艺手册

我们必须放在心里,作为开发者、程序员、软件工程师、数据科学家等等,我们真正的受众不是计算机,而是其他程序员(包括我们自己)。正如鲍勃叔叔的句子所定义的那样,我们通常花更多的时间阅读文档或其他人的代码,而不是制作新的代码,那么为什么不在这部分花更多的时间(不管有多累)并在未来帮助你或其他人呢?

来源:Thom Holwerda at OS news 漫画

这不仅会让你成为一个更好的程序员,而且还会帮助你的产品的可伸缩性和可维护性,同时减少错误的数量(这是真的)和系统复杂性/变更或增加的风险。如果这些对你来说还不够,我可以再给你一个改变旧习惯的想法!

“编写代码的时候,要把最终维护你代码的人想象成一个知道你住哪儿的暴力精神病患者。”

约翰·伍兹

那么让我们看看如何做这件事的一些方法?下面是实现高质量和干净代码的一些方法的总结。

重构

来源:XKCD 的兰道尔·门罗

重构是一种在不改变外部功能的情况下,重构代码以改善其内部结构的方法。这背后的心态是:你设法让它工作了吗?回到开头,把你的程序清晰化,模块化!当您有几个要添加的功能时,一开始就这样做似乎是浪费时间,但是一步一步地这样做将给您带来以下好处:

  • 长期减少工作量;
  • 更容易维护代码;
  • 增加可重用性;
  • 减少在未来或新项目中这样做的时间(做得越多,你在这项活动中就会变得越快)
  • 如果你尝试做得比之前的重构更好,你一定会很快掌握这项技能;
  • 这项技能在就业市场上非常有价值,会突出你的个人资料(看看 LinkedIn 或其他网站上的“渴望拥有”的职位就知道了)

好吧!我理解重构的优势,但是我该怎么做呢?很简单,以下是实现这一点的一些方法:

  • 首先,试着理解代码复杂性在 Python 中是如何工作的,以及度量这种复杂性的度量标准(例如,代码行数、圈复杂度、 Halstead 度量标准、可维护性指数);
  • 知道了复杂性是如何工作的,你现在要做的最多的事情就是重命名名字、模块、函数、类、方法,并检查你是否在你的代码中应用了一些过程编程;
  • 最后,检查一些复杂性反模式 …瞧,你的程序将会大放异彩!

如果你想更深入地了解这个主题,这里有一篇优秀的文章,它一步一步地解释了如何在 Python 中实现重构,这里有一些可用的代码指标的汇编!如果你喜欢书,我建议如下:

  • Python 反模式(AWS);
  • 重构:改进现有代码的设计 (马丁·福勒);

一些不错的视频讲座或研讨会:

  • 用老谋深算 (PyCon 2019)测量 Python 代码复杂度;
  • 重构 Python:为什么以及如何重构你的代码 (PyCon 2016)。

干净的代码

来源:迷因生成器

“干净的代码”不是一种方法或一套规则,而是一种哲学,它带来了一些技术,简化了代码的编写和阅读。再次引用鲍勃叔叔的话:

“干净的代码不是按照一套规则编写的。通过学习一系列启发法,你不会成为一名软件工匠。专业精神和工匠精神来自推动学科发展的价值观。”

罗伯特·c·马丁, 干净的代码:敏捷软件工艺手册

你知道前面提到的想法吗?我们有很紧的时间来执行这项任务,我们把很多注意力放在结果上,而不是可读和干净的代码。他很清楚地说明了如何解决这个问题,编写糟糕代码的错误完全取决于编写代码的人。

“没有什么比糟糕的代码对开发项目的影响更深远、更长期了。糟糕的计划可以重做,糟糕的需求可以重新定义。不好的团队动态是可以修复的。但是糟糕的代码会腐烂发酵,成为拖累团队的不可阻挡的重量。”

罗伯特·c·马丁, 干净代码:敏捷软件工艺手册

那我们怎么解决这个问题?取决于你在哪里(你在路上用坏习惯多久?),这可能很容易,也可能很难,消除坏习惯很复杂,但不要放弃,只要使用以下步骤,随着时间的推移,你会找到窍门的!

  • 一段干净的代码应该是优雅的 到令人愉悦的程度;
  • 一个干净的代码必须是描述性的、隐含的类型, 例如,对布尔使用“is_”或“has_”来明确它是一个条件;
  • 一个干净的代码必须一致但区分清楚, 例如,“age_list”和“age”比“ages”和“age”更容易区分;
  • 干净的代码必须避免缩写,尤其是单个字母, 只对计数器和常见的数学变量使用缩写,但请记住,如果您的团队有不同的角色(例如,全栈工程师与数据科学家一起工作),可能有必要提供更具描述性的名称;
  • 干净的代码必须表明长名称不同于描述性名称 ,仅具有相关信息的描述性名称;
  • 一个干净的代码必须有 79 个字符左右的行,学会换行缩进 一行和/或多行;
  • 一个干净的代码一定是有据可查的, 在我看来, Google 风格的例子 是最完整的,但是你可以找到你最喜欢的,开始使用它;
  • 一个干净的代码必须恰当地使用空格, 用一致的缩进组织你的代码,用空行分隔各个部分;
  • 一段干净的代码必须遵循 PEP 8 准则 进行代码布局;
  • 一个干净的代码必须遵循 法则,对于 OOP 来说

幸运的是,一些工具可以帮助我们保持代码的整洁!我们可以使用 Linters 来分析它,并检测各种类型的“ lint ”,然后能够分析代码错误、危险的代码模式、代码风格和潜在的意外结果。这里是 Python 可用的 Linters 的汇编以及每一个与其他的不同之处。如果你想深入这个话题,我推荐这几本书:

  • 【必备】 干净的代码:敏捷软件工艺手册 (罗伯特·c·马丁&迪安·万普勒);
  • 有效 Python: 59 种具体方法写出更好的 Python(Breet Slatkin);

如果你喜欢视频和研讨会,我建议你:

  • 干净代码—鲍勃大叔
  • 清理 Python 中的代码(PyCon CZ);
  • 将代码转换成漂亮、惯用的 Python(PyCon 2013)

模块化程序设计

来源:疯狂世界的 Manu Cornet

编写模块化代码是软件开发中的一个重要步骤,因为它允许在模块中使用相同的代码,通过引用它来在程序的不同位置执行特定的操作。这种方法方便了大型程序的调试,增加了代码的可重用性和可读性,提高了可靠性,也有助于与多个开发人员或团队一起编程。为了继续,我认为您已经知道如何构建 Python 项目,但是如果您不知道,请在继续之前看一下这里。总之,要制作模块化代码,必须遵循以下提示:

  • 不要重复自己: 概括和巩固函数或循环中重复的代码,不惜一切代价避免 意大利面代码
  • 抽象出逻辑来提高可读性: 这用描述性函数名来提高可读性,但是要小心使用,因为你可能会过度设计;
  • 尽量减少实体数量: 用函数调用代替内联逻辑是有利弊的;
  • 函数应该做一件事: 如果你的函数名中有一个“and”,考虑重构,你的函数也必须有少于 10 行的代码;
  • 任意变量名在某些函数中可以更有效: 一般函数中的任意变量名其实可以让代码可读性更强;
  • 每个函数尽量少用三个参数: 记住我们模块化是为了简化我们的代码,让它工作起来更有效率。如果您的函数有很多参数,您可能需要重新考虑如何将其拆分。

我建议你看一看的一篇优秀文章就是这篇,我认为必须看的一本书是《Python 的搭便车者指南》,可以在这里免费获得。

现在呢?

写好代码的问题,尽管在几个 IT 领域都存在,而且主要是在新手中,但在数据科学领域已经讨论了很多。这在很大程度上是由于数据科学家接触了广泛的学术学科,导致他们在编写干净和高级代码所需的一些技能方面缺乏经验(例如,软件工程原理、范例、干净代码、测试、日志)。尽管有这个问题,我们仍然有时间来解决这个问题,让我们帮助我们和其他人实现新的代码习惯!

我希望这篇文章能为那些不仅想改进代码,还想一起利用团队的人提供指导!

使用 Python 清理文本数据

原文:https://towardsdatascience.com/cleaning-text-data-with-python-b69b47b97b76?source=collection_archive---------2-----------------------

安妮·斯普拉特在 Unsplash 上的照片

你需要的只是 NLTK 和 re 库。

数据格式并不总是表格格式。随着我们进入大数据时代,数据以非常多样化的格式出现,包括图像、文本、图表等等。

因为格式相当多样,从一种数据到另一种数据,所以将这些数据预处理成计算机可读的格式是非常必要的。

在本文中,我想向您展示如何使用 Python 预处理文本数据。如题所示,你所需要的就是 NLTK 和 re 库。

为了向您展示这是如何工作的,我将从一个名为的 Kaggle 竞赛中获取一个数据集。灾难推文 NLP。

我已经创建了一个谷歌 Colab 笔记本,如果你想跟着我一起。要访问,你可以点击这个链接这里

该过程

将文本小写

在我们开始处理文本之前,最好先把所有的字符都小写。我们这样做的原因是为了避免任何区分大小写的过程。

假设我们想从字符串中删除停用词,我们使用的技术是将非停用词组合成一个句子。如果我们不是小写那些,停止字不能被发现,它将导致相同的字符串。这就是为什么降低文本的大小写是必要的。

在 Python 中做到这一点很容易。代码看起来像这样,

**# Example**
x = "Watch This Airport Get Swallowed Up By A Sandstorm In Under A Minute [http://t.co/TvYQczGJdy](http://t.co/TvYQczGJdy)"**# Lowercase the text**
x = x.lower()print(x)**>>> watch this airport get swallowed up by a sandstorm in under a minute** [**http://t.co/tvyqczgjdy**](http://t.co/tvyqczgjdy)

删除 Unicode 字符

一些推文可能包含 Unicode 字符,当我们以 ASCII 格式看到它时,它是不可读的。大多数情况下,这些字符用于表情符号和非 ASCII 字符。为了消除这一点,我们可以使用这样的代码,

**# Example**
x = "Reddit Will Now Quarantine‰Û_ [http://t.co/pkUAMXw6pm](http://t.co/pkUAMXw6pm) #onlinecommunities #reddit #amageddon #freespeech #Business [http://t.co/PAWvNJ4sAP](http://t.co/PAWvNJ4sAP)"**# Remove unicode characters**
x = x.encode('ascii', 'ignore').decode()print(x)**>>> Reddit Will Now Quarantine_** [**http://t.co/pkUAMXw6pm**](http://t.co/pkUAMXw6pm) **#onlinecommunities #reddit #amageddon #freespeech #Business** [**http://t.co/PAWvNJ4sAP**](http://t.co/PAWvNJ4sAP)

删除停用词

这样做之后,我们可以删除属于停用词的单词。停用词是一种对文本意义没有显著贡献的词。因此,我们可以删除这些词。为了检索停用词,我们可以从 NLTK 库中下载一个语料库。这是如何做到这一点的代码,

import nltk
nltk.download()
**# just download all-nltk**stop_words = stopwords.words("english")**# Example**
x = "America like South Africa is a traumatised sick country - in different ways of course - but still messed up."**# Remove stop words**
x = ' '.join([word for word in x.split(' ') if word not in stop_words])print(x)**>>> America like South Africa traumatised sick country - different ways course - still messed up.**

删除提及、标签、链接等术语。

除了我们删除 Unicode 和停用词,还有几个术语我们应该删除,包括提及、标签、链接、标点等等。

要去除这些,如果我们只依赖一个定义好的角色,那是很有挑战性的。因此,我们需要通过使用正则表达式(Regex)来匹配我们想要的术语的模式。

Regex 是一个特殊的字符串,它包含的模式可以匹配与该模式相关的单词。通过使用它,我们可以使用名为 re 的 Python 库搜索或删除那些基于模式的内容。为此,我们可以这样实现它,

import re**# Remove mentions**
x = "[@DDNewsLive](http://twitter.com/DDNewsLive) [@NitishKumar](http://twitter.com/NitishKumar)  and [@ArvindKejriwal](http://twitter.com/ArvindKejriwal) can't survive without referring @@narendramodi . Without Mr Modi they are BIG ZEROS"x = re.sub("@\S+", " ", x)print(x)
**>>>      and   can't survive without referring   . Without Mr Modi they are BIG ZEROS****# Remove URL**
x = "Severe Thunderstorm pictures from across the Mid-South [http://t.co/UZWLgJQzNS](http://t.co/UZWLgJQzNS)"x = re.sub("https*\S+", " ", x)print(x)
**>>> Severe Thunderstorm pictures from across the Mid-South****# Remove Hashtags**
x = "Are people not concerned that after #SLAB's obliteration in Scotland #Labour UK is ripping itself apart over #Labourleadership contest?"x = re.sub("#\S+", " ", x)print(x)
**>>> Are people not concerned that after   obliteration in Scotland   UK is ripping itself apart over   contest?****# Remove ticks and the next character**
x = "Notley's tactful yet very direct response to Harper's attack on Alberta's gov't. Hell YEAH Premier! [http://t.co/rzSUlzMOkX](http://t.co/rzSUlzMOkX) #ableg #cdnpoli"x = re.sub("\'\w+", '', x)print(x)
**>>> Notley tactful yet very direct response to Harper attack on Alberta gov. Hell YEAH Premier!** [**http://t.co/rzSUlzMOkX**](http://t.co/rzSUlzMOkX) **#ableg #cdnpoli****# Remove punctuations**
x = "In 2014 I will only smoke crqck if I becyme a mayor. This includes Foursquare."x = re.sub('[%s]' % re.escape(string.punctuation), ' ', x)print(x)
**>>> In 2014 I will only smoke crqck if I becyme a mayor. This includes Foursquare.****# Remove numbers**
x = "C-130 specially modified to land in a stadium and rescue hostages in Iran in 1980... [http://t.co/tNI92fea3u](http://t.co/tNI92fea3u) [http://t.co/czBaMzq3gL](http://t.co/czBaMzq3gL)"x = re.sub(r'\w*\d+\w*', '', x)print(x)
**>>> C- specially modified to land in a stadium and rescue hostages in Iran in ...** [**http://t.co/**](http://t.co/)[**http://t.co/**](http://t.co/)**# Replace the over spaces**
x = "     and   can't survive without referring   . Without Mr Modi they are BIG ZEROS"x = re.sub('\s{2,}', " ", x)print(x)
**>>>  and can't survive without referring . Without Mr Modi they are BIG ZEROS**

组合它们

在您了解预处理文本的每个步骤之后,让我们将它应用于一个列表。如果你仔细观察这些步骤的细节,你会发现每种方法都是相互关联的。因此,有必要将它应用到一个函数上,这样我们就可以同时按顺序处理它。在我们应用预处理步骤之前,这里是样本文本的预览,

**Our Deeds are the Reason of this #earthquake May ALLAH Forgive us all
Forest fire near La Ronge Sask. Canada
All residents asked to 'shelter in place' are being notified by officers. No other evacuation or shelter in place orders are expected
13,000 people receive #wildfires evacuation orders in California 
Just got sent this photo from Ruby #Alaska as smoke from #wildfires pours into a school**

我们应该做几个步骤来预处理文本列表。他们是,

  1. 创建一个包含所有预处理步骤的函数,它返回一个预处理过的字符串
  2. 使用名为 Apply 的方法应用函数,并用该方法链接列表。

代码看起来会像这样,

**# # In case of import errors
# ! pip install nltk
# ! pip install textblob**import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import re
import nltk
import string
from nltk.corpus import stopwords**# # In case of any corpus are missing 
# download all-nltk**
nltk.download()df = pd.read_csv('train.csv')
stop_words = stopwords.words("english")
wordnet = WordNetLemmatizer()def text_preproc(x):
  x = x.lower()
  x = ' '.join([word for word in x.split(' ') if word not in stop_words])
  x = x.encode('ascii', 'ignore').decode()
  x = re.sub(r'https*\S+', ' ', x)
  x = re.sub(r'@\S+', ' ', x)
  x = re.sub(r'#\S+', ' ', x)
  x = re.sub(r'\'\w+', '', x)
  x = re.sub('[%s]' % re.escape(string.punctuation), ' ', x)
  x = re.sub(r'\w*\d+\w*', '', x)
  x = re.sub(r'\s{2,}', ' ', x)
  return xdf['clean_text'] = df.text.apply(text_preproc)

这是它的结果,

**deeds reason may allah forgive us
forest fire near la ronge sask canada
residents asked place notified officers evacuation shelter place orders expected
 people receive evacuation orders california 
got sent photo ruby smoke pours school**

最后的想法

这就是如何使用 Python 对文本进行预处理。希望你可以应用它来解决文本数据相关的问题。如果有什么想法,可以在下面评论下来。此外,您可以在 Medium 上跟踪我,以便跟进我的文章。谢谢你。

参考

[1]https://docs.python.org/3/library/re.html
【2】https://www.nltk.org/
【3】https://www.kaggle.com/c/nlp-getting-started/overview

在 Azure Databricks 中将模式漂移的 CSV 文件清理并转换为关系数据

原文:https://towardsdatascience.com/cleansing-and-transforming-schema-drifted-csv-files-into-relational-data-in-azure-databricks-519e82ea84ff?source=collection_archive---------13-----------------------

使用 PySpark 增量处理和加载模式漂移文件到 Azure Databricks 中的 Azure Synapse Analytics 数据仓库

由 Unsplash 上的hkon grim stad拍摄的照片

数据是企业的血液。数据以不同的形状和大小出现,这使得寻找处理和消费的方式成为一项持续的挑战性任务,没有这种方式,数据就没有任何价值。

本文将介绍如何利用 Apache Spark 的并行分析功能,将模式漂移的 CSV 文件迭代地清理和转换为可查询的关系数据,并存储在数据仓库中。我们将在 Spark 环境中工作,并用 PySpark 编写代码来实现我们的转换目标。

警告: Microsoft Azure 是一项付费服务,遵循本文可能会导致您或您的组织承担财务责任。

在继续本文之前,请阅读我们的使用条款:https://dhyanintech . medium . com/disclaimer-disclosure-disclosure-terms-of-use-fb3 BF BD 1e 0e 5

先决条件

  1. 有效的 Microsoft Azure 订阅
  2. 带有 CSV 文件的 Azure 数据湖存储第二代帐户
  3. Azure Databricks 工作区(高级定价层)
  4. Azure Synapse 分析数据仓库

如果您还没有设置先决条件,请参考我们以前的文章开始:

[## 使用 Azure 将 CSV 文件转换为 Power BI 视觉效果的权威指南

使用 Microsoft Azure 产品将新冠肺炎数据转化为惊人的 Power BI 视觉效果的分步指南。

medium.com](https://medium.com/@dhyanintech/a-definitive-guide-to-turn-csv-files-into-power-bi-visuals-using-azure-4483cf406eab) [## 使用 Azure Data Factory 基于 HTTP 上的 URL 模式增量复制文件

一个创新的 Azure 数据工厂管道,通过 HTTP 从第三方网站增量复制多个文件…

medium.com](https://medium.com/@dhyanintech/using-azure-data-factory-to-incrementally-copy-files-based-on-url-pattern-over-http-569476b625fc)

登录到 Azure 门户,找到并打开你的 Azure Databricks 实例,然后点击“启动工作区”我们的 Databricks 实例将在新的浏览器选项卡中打开;等待 Azure AD SSO 自动为您登录。

Azure 门户:启动 Databricks 工作区(图片由作者提供)

接下来,我们需要创建一个节点集群,以利用 Apache Spark 无与伦比的并行处理(双关语)能力来处理、清理和转换我们的半结构化数据。

[## Apache Spark 面向大数据的统一分析引擎

闪电般快速的统一分析引擎 Apache Spark 是用于大规模数据处理的统一分析引擎…

spark.apache.org](https://spark.apache.org/)

旋转阿帕奇火花集群

选择左侧菜单上的集群开始创建新的集群。通过选择 +创建集群开始,如图所示继续操作。这里需要注意的两个基本问题是 Databricks 运行时版本以及工作节点的最小和最大数量。我们的集群将在这些节点之间自动扩展以适应负载。等待创建过程完成。

Azure Databricks:建立一个 Apache Spark 集群(图片由作者提供)

点击开始来启动你的集群。Azure 可能需要几分钟来配置和设置您的群集资源。密切关注集群状态指示器,查看实时状态。

Azure Databricks:启动 Spark 集群(图片由作者提供)

数据砖块的真正魔力发生在笔记本中。Azure Databricks 支持用 Python、Scala、SQL 和 r 编写的笔记本,在我们的项目中,我们将使用 Python 和 PySpark 来编码所有的转换和清理活动。让我们开始创建一个 Python 笔记本。

笔记本是一个基于网络的文档界面,其中包含可运行的代码、叙述性文本和可视化效果。

PySpark 是 Apache Spark 的 Python API。Apache Spark 是用 Scala 写的。PySpark 已经发布,支持 Apache Spark 和 Python 的协作。

在左侧菜单中选择工作区,并按照所示步骤操作。你的笔记本创建后会打开;花点时间四处看看,熟悉一下 UI 和我们可用的各种选项。

Azure Databricks:创建一个 Python 笔记本(图片由作者提供)

笔记本的前几行应该告诉数据块我们的数据在哪里以及如何访问它。我们将将我们的存储帐户挂载到 Databricks 文件系统,并将其作为本地存储进行访问。

将存储帐户装载到数据块文件系统

请阅读我们的文章,了解在 Azure Databricks 中安装和访问 ADLS 第二代存储的详细步骤。我们会尽量简短。

[## 使用服务主体和秘密范围在 Azure 数据块中挂载和访问 ADLS Gen2

关于使用 Azure 密钥库支持的秘密范围和服务从数据块访问 Azure 数据湖存储 Gen2 的指南…

medium.com](https://medium.com/@dhyanintech/mounting-accessing-adls-gen2-in-azure-databricks-using-service-principal-and-secret-scopes-96e5c3d6008b)

从数据块连接和访问 Azure Synapse 分析数据仓库

我们的最终目标是将数据加载到数据仓库中,从数据中获得洞察力,并构建报告来做出决策。让我们在继续之前设置连接。

[## 在 Azure Databricks 中连接和访问 Azure Synapse Analytics 的凭据安全方式

关于如何在 PySpark 中使用秘密作用域设置 SQL Server 防火墙和从数据块连接的指南

medium.com](https://medium.com/@dhyanintech/a-credential-safe-way-to-connect-and-access-azure-synapse-analytics-in-azure-databricks-1b008839590a)

我们的连接都设置好了;让我们继续清理刚刚挂载的 CSV 文件。我们将简要解释语句的目的,并在最后展示整个代码。

使用 PySpark 进行转化和净化

首先,让我们将一个文件读入 PySpark 并确定模式。我们将设置一些选项来告诉 PySpark 列的类型和结构。

# Read the csv files with first line as header, comma (,) as separator, and detect schema from the file
csvDf = spark.read.format("csv") \
.option("inferSchema", "true") \
.option("header", "true") \
.option("sep", ",") \
.load("dbfs:/mnt/csvFiles/01-22-2020.csv")csvDf.printSchema()

PySpark:确定文件的模式(图片由作者提供)

这个文件的列数比我们从 GitHub 源得到的少,列名也不同。我们对最终功率 BI 可视化的突出显示列感兴趣。

数据源:预期的架构(图片由作者提供)

让我们读取一个较新的文件并检查其结构。

PySpark:确定文件的模式(图片由作者提供)

这个文件的结构更接*我们的来源的描述。图式的差异并没有让我们的事情变得简单。如果我们所有的文件都有相同的模式,我们可以一次加载并清理所有的文件。我们的例子是典型的模式漂移,我们必须恰当地处理它;否则,我们的 ELT(提取、加载和转换)过程将会失败。我们将设计我们的转换来解释这种漂移,并使它不会因模式改变而出错。

模式漂移是源经常改变元数据的情况。字段、列和类型可能会被更改、添加或删除。

我们将通过重命名列来开始清理,以匹配数据库中表的属性,从而在表和数据之间建立一对一的映射。我们将通过将所有字母转换为小写并删除空格、正斜杠('/')和下划线(' _ ')来实现这一点。

# Function to flatten the column names by removing (' ', '/', '_') and converting them to lowercase letters
def rename_columns(rename_df):
  for column in rename_df.columns:
    new_column = column.replace(' ','').replace('/','').replace('_','')
    rename_df = rename_df.withColumnRenamed(column, new_column.lower())
  return rename_dfcsvDf = rename_columns(csvDf)
csvDf.printSchema()

PySpark:展*列名(作者图片)

我们的列名现在看起来好多了。我们将添加一些新的列来处理我们缺少列的情况;活动经度纬度源文件。我们将使用文件名作为源文件列的值。本专栏将有助于设置数据库中数据的增量加载。

首先,我们将把数据中存在的 latlong 列名重命名为 latitude经度。接下来,我们将使用 PySpark 中的lit()来添加缺少的活动纬度经度列,其中为空值,而源文件的文件名为列值。

# lit() function to create new columns in our datafram
from pyspark.sql.functions import lit# Check dataframe and add/rename columns to fit our database table structure
if 'lat' in csvDf.columns:
  csvDf = csvDf.withColumnRenamed('lat', 'latitude')

if 'long' in csvDf.columns:
  csvDf = csvDf.withColumnRenamed('long', 'longitude')

if 'active' not in csvDf.columns:
  csvDf = csvDf.withColumn('active', lit(None).cast("int"))

if 'latitude' not in csvDf.columns:
  csvDf = csvDf.withColumn('latitude', lit(None).cast("decimal"))

if 'longitude' not in csvDf.columns:
  csvDf = csvDf.withColumn('longitude', lit(None).cast("decimal"))# Add the source file name (without the extension) as an additional column to help us keep track of data source
csvDf = csvDf.withColumn("sourcefile", lit('01-22-2020.csv'.split('.')[0]))csvDf = csvDf.select("provincestate", "countryregion", "lastupdate", "confirmed", "deaths", "recovered", "active", "latitude", "longitude", "sourcefile")csvDf.printSchema()

PySpark:添加额外的栏目(图片由作者提供)

让我们用display(DATAFRAME)来看看我们在清理活动开始时查看的两个文件的数据

PySpark:显示数据框的数据(图片由作者提供)

这两个文件现在都为我们提供了固定结构的格式化数据,并准备好插入到我们的数据库中。我们已经成功地处理了漂移模式。

到目前为止,我们手动运行了两个文件的代码;我们应该自动处理一个接一个的文件。我们可以使用 Databricks 文件系统实用程序来遍历所有文件。

关于 Databricks 文件系统实用程序的进一步阅读:

[## 数据块工具

Databricks 实用程序(DBUtils)使执行强大的任务组合变得容易。您可以使用这些实用程序来…

docs.databricks.com](https://docs.databricks.com/dev-tools/databricks-utils.html#dbutilsfsls-command)

# List all the files we have in our store to iterate through them
file_list = [file.name for file in dbutils.fs.ls("dbfs:{}".format(mountPoint))]for file in file_list:
  print(file)

PySpark:使用 Databricks 工具列出文件(图片由作者提供)

我们只需要处理尚未加载到数据库中的文件(增量加载)。我们可以通过查询数据库找出最后加载的文件的名称,并调整迭代器代码以忽略已经加载的文件。

# Find out the last file we loaded into the database
# This will return *null* if there's no data in the table
lastLoadedFileQuery = "(SELECT MAX(sourcefile) as sourcefile FROM csvData.covidcsvdata) t"lastFileDf = spark.read.jdbc(url=jdbcUrl, table=lastLoadedFileQuery, properties=connectionProperties)lastFile = lastFileDf.collect()[0][0]# List all the files we have in our store to iterate through them
file_list = [file.name for file in dbutils.fs.ls("dbfs:{}".format(mountPoint))]# Find the index of the file from the list
loadFrom = file_list.index('{}.csv'.format(lastFile)) + 1 if lastFile else 0# Trim the list keeping only the files that should be processed
file_list = file_list[loadFrom:]
for file in file_list:
  print(file)

PySpark —概念验证:增量处理(图片由作者提供)

组合和重组我们到目前为止编写的所有代码将允许我们通过增加数据库负载来清理模式漂移文件。试试看。您可以在文章末尾找到 GitHub 的整个笔记本,用于任何故障排除目的。

结论

我们查看了我们的 CSV 文件,意识到它们有不同的模式,需要不同的处理方法,然后才能将它们加载到我们的数据仓库中。我们使用 PySpark 创建了一个创造性的解决方案来增量处理我们的文件,并设计了一个满足我们需求的解决方案。

后续步骤

如果您正在关注我们关于将 CSV 数据转化为 Power BI 视觉效果的系列文章,或者有兴趣了解如何在您的数据工厂管道中添加和执行 Databricks notebook,请阅读我们的下一篇文章继续您的旅程。

[## 使用访问令牌在 Azure 数据工厂管道中执行 Azure Databricks 笔记本

关于如何使用 Azure Key Vault 安全访问在数据工厂管道中添加和执行 Databricks 笔记本的指南…

medium.com](https://medium.com/@dhyanintech/executing-azure-databricks-notebook-in-azure-data-factory-pipeline-using-access-tokens-3326b8703432)

喜欢这个帖子?与 Dhyan 联系

让我们做朋友吧!你可以在 LinkedIn 上找到我或者在 Medium 上加入我。

使用 Matplotlib 清除图表

原文:https://towardsdatascience.com/clear-charts-with-matplotlib-56618b01656a?source=collection_archive---------47-----------------------

如何构建人们真正想要阅读和使用的 Matplotlib 图表?

艾萨克·史密斯在 Unsplash 上拍摄的照片

数据科学就是让人们信服,展示你的发现,你真正理解的模式。在这个练习中,图表走了很长的路。遗憾的是,许多科学图书馆允许你制作开箱即用的图表,如下图所示。

matplotlib 文档中的条形图(1)

你的眼睛是带着好奇心被图表吸引的吗?我个人认为不是,更重要的是,我认为非技术人员、普通公众会很快对此不屑一顾,因为你必须有意识地努力超越视觉上的不清晰

大多数人会忽视美学的重要性,但我真的认为它是连接“技术”和“商业”人士的桥梁之一。

有助于更好的沟通。

图表是关于你想要展示的东西,读者应该提取的最重要的信息是什么?是轴心吗?你几乎不会错过黑色的框架和线条。如果要读取条形值,它会堆叠在误差条形线之间。重要的信息被隐藏了。

在这篇文章中,我以柱状图为例,假装我们测量了不同人群的分数。所需代码在帖子最后分享。

在条形图中,我希望通过重要性递减顺序清楚地看到:

  1. 条和它们各自的比例,
  2. 标题,
  3. 天*,
  4. 以及他们的价值观和团队意识。

你可以直观的传达这个订单。最重要的元素应该是你首先看到的。下面,我建议一个选择去那个方向。条形颜色较深,使其可见,标题和轴图例为中等灰色,不太明显,因为它是次要信息。最后,水*网格线非常浅,如果你想读取数值,这有一点帮助。没什么特别的,第一次花了一个小时,但之后主要是复制/粘贴你喜欢的配置。

plot_1.png(帖子末尾的代码)

关于颜色的一句话。不要使用默认颜色。就是不要。

如今,你有很多工具来真正地选择和谐的调色板,即使你对理论一无所知,它们也能很好地协同工作。例如,我在这里选择的颜色是在网上工具(【coolors.co】T2)上挑选的,但很可能这些工具中的任何一个都可以胜任。如果你想对百叶窗的颜色很敏感,有适合每个人的特殊调色板(【davidmathlogic.com/colorblind/】)。如果下面的图是红色代表男人,蓝色代表女人,这将会让我们很多人感到惊讶。颜色与意义和情感联系在一起,用它们来阐明你的交流。

plot_2.png(帖子末尾的代码)

总之,设计既能带来有价值的信息,又不会混淆视听的图表,可以归结为几个核心原则:

  • 图表上的每个形状都有其用途和意义。如果没有,就把它去掉(如果有必要,也可以用白色/透明来隐藏)。
  • 选择颜色,而不是让图书馆为你做。
  • 选择每个人都能阅读的字体大小(不,不是 6pt)。如果图表的最终尺寸较小,请减小绘图区并增大字体大小。给看不懂的东西贴标签是没有意义的。
  • 我们在 2020 年,使用与我们当前屏幕质量相匹配的分辨率(这里,我指定 300 dpi 或使用矢量图形)。

如果你定义了图表的外观,你可以复制它并保持一致。否则你被你使用的工具所控制,你就不是在讲故事。

这真的很简单,但我一直看到混乱和难以阅读的图表。接下来,你可能会想你通常产生的视觉效果。因为你不会说或写没有意义或目的的话,你可以把同样的逻辑应用到你的图表上。

代码:

来源:

[1]https://matplotlib . org/2 . 0 . 2/examples/API/bar chart _ demo . html

使用 Python 中的模块和包进行清晰的编码

原文:https://towardsdatascience.com/clear-coding-with-overloading-in-python-24df853003d8?source=collection_archive---------29-----------------------

用通俗易懂的例子!

扎克·卡道夫在 Unsplash 上的照片

当您的 Python 代码变大时,随着时间的推移,它很可能变得杂乱无章。随着代码的增长,将代码保存在同一个文件中会使代码难以维护。此时,Python 模块帮助你通过使用文件和文件夹来组织和分组你的内容。

  • 模块是带有的文件。py" 包含 Python 代码的扩展。它们有助于在同一个文件中组织相关的函数、类或任何代码块。
  • 将大型 Python 代码块分割成包含多达 300–400 行代码的模块被认为是最佳实践。
  • 将相似的模块放在一个单独的目录中。它们是包含相关模块和一个 init 的文件夹。py 文件,用于可选的包级初始化。
  • 根据您的 Python 应用程序,您可以考虑将您的模块分组到子包中,例如 doc、core、utils、data、examples、test。

让我们编写一个示例 Python3 代码来进一步理解模块和包:

**""" cvs_get_module.py
This module displays the summary of the tabular data contained in a CSV file 
"""**import pandas as pdprint("cvs_get_module is loaded")*def* **display_file_location(*path, file_name*)**:
 print("File Location: {}".format(path+filename))*class* **CSVGetInfo:**
 *def* __init__(*self*, *path*, *file_name*):
  self.path = path
  self.file_name = file_name

 *def* **display_summary(*self*):**
  data = pd.read_csv(self.path + self.file_name)
  print(self.file_name)
  print(data.info())

丹尼尔在 Unsplash 上的照片

导入模块

为了在外部 Python 代码块中使用一个模块,我们需要将那个特定的模块导入到我们的代码结构中。为此,使用带有"import<module _ name>"语法的 import 语句。这里的模块名是指不带的 Python 文件名。py" 扩展名。一旦我们导入模块,我们使用点符号,.”,访问模块内部的元素。

# **ModulesExample.py
# Importing 'csv_get_module' and accessing its elements**import **csv_get_module****data_by_genres** = **csv_get_module.CSVGetInfo**("/Users/erdemisbilen/
Lessons/", "data_by_genres.csv")**csv_get_module.display_file_location**(data_by_genres.**path**, data_by_genres.**file_name**)data_by_genres.**display_summary()****Output:** cvs_get_module is loaded
File Location: /Users/erdemisbilen/Lessons/data_by_genres.csv
data_by_genres.csv
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2617 entries, 0 to 2616
Data columns (total 14 columns):

通过重命名导入模块

我们可以用‘导入< 模块 _ 名称 >作为< 替代 _ 名称>’语法在导入的同时重命名模块。这可能有助于缩短长模块名。

# **ModulesExample.py
# Importing 'csv_get_module' and accessing its elements**import **csv_get_module** as **cg****data_by_genres** = **cg.CSVGetInfo**("/Users/erdemisbilen/Lessons/", "data_by_genres.csv")**cg.display_file_location**(data_by_genres.path, data_by_genres.file_name)data_by_genres.display_summary()**Output:** cvs_get_module is loaded
File Location: /Users/erdemisbilen/Lessons/data_by_genres.csv
data_by_genres.csv
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2617 entries, 0 to 2616
Data columns (total 14 columns):

照片由亚历克斯·布洛克在 Unsplash 上拍摄

从模块导入特定名称

我们可以导入模块的特定名称,而不是加载模块中包含的所有元素。此外,我们可以通过用逗号分隔名称来导入多个元素。

注意,这里我们不需要使用点符号,因为我们直接从<module _ name>import<element _ name>’语法中导入带有的名称。

from **csv_get_module** import **display_file_location** as **dfl****dfl**("/User/Python/","ModulesExample.py")**Output:** File Location: /UserModulesExample.py

导入模块内的所有名称

我们也可以使用星号(*)直接导入模块的所有名称,尽管这不是一个好的做法。如果导入多个包含同名元素的模块,这可能会导致名称冲突。

from **csv_get_module** import *****

隐藏模块的元素

如果我们想要隐藏模块的一些元素,我们可以用下划线 "_" 开始命名元素。这种元素不能导入到外部文件中,因为这种命名约定使该元素成为模块本身的私有元素。

区分独立脚本运行和模块加载

包含 Python 代码的文件可以作为独立脚本运行,也可以作为模块加载到另一个代码结构中。

有时,考虑到这些用途,需要将 Python 文件中的代码分开。Python 有内置的 name 属性,当文件作为模块加载时,该属性为我们提供了模块名称。当文件作为独立脚本运行时,这次它返回 main 字符串。

**""" cvs_get_module.py
This module displays the summary of the tabular data contained in a CSV file 
"""**import pandas as pdprint("cvs_get_module is loaded")*def* **display_file_location(*path, file_name*)**:
 print("File Location: {}".format(path+filename))*class* **CSVGetInfo:**
 *def* __init__(*self*, *path*, *file_name*):
  self.path = path
  self.file_name = file_name

 *def* **display_summary(*self*):**
  data = pd.read_csv(self.path + self.file_name)
  print(self.file_name)
  print(data.info())if **__name__ == '__main__'**:**data_by_genres** = **CSVGetInfo**("/Users/erdemisbilen/Lessons/", "data_by_genres.csv")**display_file_location**(data_by_genres.path, data_by_genres.file_name)
data_by_genres.display_summary()

只有当脚本作为独立脚本运行时,上述 if 语句中的代码才会运行。

Python 中的包

将相似的模块放在一个单独的目录中。它们是包含相关模块和一个 init 的文件夹。py 文件,用于可选的包级初始化。

init。py 在引用包内模块时执行一次。这个文件可以保留为空,或者可以选择实现包级初始化代码。

根据您的 Python 应用程序,您可以考虑将您的模块分组到子包中,例如 doc、core、utils、data、examples、test。由于相似的模块保存在不同的文件夹中,这使得你的整体结构得到很好的组织和维护。

Lynn Kintziger 在 Unsplash 上拍摄的照片

从包中导入模块

我们可以使用包名(文件夹名)和点“.”来导入包内的模块接线员。

from **utils.csv_get_module** import **display_file_location** as **dfl****dfl**("/User/Python/","ModulesExample.py")**Output:** File Location: /UserModulesExample.py

关键要点

  • 在 Python 中,组织大型代码块由模块包管理。
  • 这简化了代码结构,增加了代码的可重用性,并简化了维护和测试工作。

结论

在这篇文章中,我解释了 Python 中模块和包的基础知识。

这篇文章中的代码可以在我的 GitHub 库中找到。

我希望这篇文章对你有用。

感谢您的阅读!

学习使用 Python 的 Scikit_learn 库通过项目开发 KNN 分类器

原文:https://towardsdatascience.com/clear-understanding-of-a-knn-classifier-with-a-project-for-the-beginners-865f56aaf58f?source=collection_archive---------31-----------------------

杰克·威瑞克在 Unsplash 上的照片

适合机器学习新手

学习机器学习概念的最佳方式是做一个项目。在这篇文章中,我将使用一个项目来描述一些关键的机器学习概念。

在这篇文章中,我将详细解释一个分类模型,它是,一个监督机器学习的主要类型。顾名思义,我们将要研究的模型叫做 KNN 分类器。

KNN 分类器是一种非常流行和众所周知的监督机器学习技术。本文将通过一个简单但完整的项目来解释 KNN 分类器。

什么是监督学习模型?

我来详细解释一下。但是维基百科是这么说的:

监督学习是学习函数的机器学习任务,该函数基于示例输入-输出对将输入映射到输出。它从由一组训练示例组成的标记的训练数据中推断出一个函数。

监督学习模型采用输入特征(X)和输出(y)来训练模型。该模型的目标是定义一个可以使用输入要素并计算输出的函数。

举个例子会更清楚

这是一个数据集,包含一些水果样本的质量、宽度、高度和颜色分数。

该数据集的目的是训练一个模型,这样如果我们向模型输入质量、宽度、高度和颜色分数,模型就可以让我们知道水果的名称。就像如果我们输入一块水果的质量、宽度、高度和颜色分数分别为 175、7.3、7.2、0.61,模型应该输出水果的名称为苹果。

这里,质量、宽度、高度和颜色分数是输入特征(X)。水果的名字是输出变量或标签(y)。

这个例子对你来说可能听起来很傻。但这是在非常高级的监督机器学习模型中使用的机制。

稍后,我将用一个真实的数据集展示一个实际的例子。

KNN 分类器

KNN 分类器是基于记忆的机器学习模型的一个例子。

这意味着这个模型记住了带标签的训练样本,并使用它们来对它以前没有见过的对象进行分类。

KNN 分类器中的 k 是它将检索的训练样本的数量,以便预测新的测试样本。

KNN 分类器分三步工作:

  1. 当给它一个新的实例或例子进行分类时,它将检索以前记忆的训练例子,并从中找到 k 个最接*的例子。
  2. 然后分类器查找这 k 个最接*的例子的标签(上面例子中的水果名称)。
  3. 最后,模型将这些标签结合起来进行预测。通常,它会预测多数标签。例如,如果我们选择我们的 k 为 5,从最*的 5 个例子中,如果我们有 3 个橙子和 2 个苹果,新实例的预测将是橙色。

数据准备

在我们开始之前,我鼓励您检查您的计算机中是否有以下可用资源:

  1. Numpy 库
  2. 熊猫图书馆
  3. Matplotlib 库
  4. Scikit-Learn 库
  5. Jupyter 笔记本环境。

如果您没有安装 Jupyter 笔记本,请使用您选择的任何其他笔记本。我建议用谷歌合作笔记本。跟随此链接开始。只要记住一件事,

谷歌合作笔记本不是私人的。所以,不要在那里做任何专业或敏感的工作。但对练习很有帮助。因为里面已经安装了很多常用的包。

我建议,下载数据集。我在页面底部提供了链接。如果你是为了学习这个而阅读的话,请自己运行每一行代码。

首先,导入必要的库:

%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split

对于本教程,我将使用来自 Kaggle 的 Titanic 数据集。我将这个数据集上传到我笔记本所在的文件夹中。

下面是我如何使用 pandas 在笔记本中导入数据集。

titanic = pd.read_csv('titanic_data.csv')
titanic.head()

#titaninc.head() gives the first five rows of the dataset. We will    #print first five rows only to examine the dataset.

看第二列。它包含了这个人是否幸存的信息。0 表示该人幸存,1 表示该人未幸存。

在本教程中,我们的目标是预测“幸存”特征。

为了简单起见,我将保留几个对算法更重要的关键特征,去掉其余的。

这个数据集非常简单。仅仅从直觉上,我们可以看到有一些列对于预测‘幸存’特征来说是不重要的。

例如,“乘客 Id”、“姓名”、“机票”和“客舱”似乎不能用来预测乘客是否幸存。

我将创建一个具有一些关键特性的新数据框架,并将其命名为 titanic1。

titanic1 = titanic[['Pclass', 'Sex', 'Fare', 'Survived']]

“性别”列具有字符串值,需要进行更改。因为计算机不理解单词。它只懂数字。我将更改 0 的“男性”和 1 的“女性”。

titanic1['Sex'] = titanic1.Sex.replace({'male':0, 'female':1})

这是数据帧 titanic1 的样子:

我们的目标是根据 titanic1 数据图中的其他信息预测“幸存”参数。因此,输出变量或标签(y)是“幸存的”。输入特征(X)是“P 级”、“性别”和“费用”。

X = titanic1[['Pclass', 'Sex', 'Fare']]
y = titanic1['Survived']

KNN 分类器模型

首先,我们需要将数据集分成两组:训练集和测试集。

我们将使用训练集来训练模型,其中模型将记住输入特征和输出变量。

然后,我们将使用测试集来查看模型是否可以使用“P-class”、“Sex”和“Fare”来预测乘客是否幸存。

方法' train_test_split '将有助于拆分数据。默认情况下,此函数将 75%的数据用于定型集,25%的数据用于测试集。如果您愿意,您可以更改它,并且您可以指定“训练大小”和“测试大小”。

如果将 train_size 设置为 0.8,则拆分为 80%的训练数据和 20%的测试数据。但是对我来说,默认值 75%就不错了。因此,我没有使用 train_siz 或 test_size 参数。

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

请记住对“random_state”使用相同的值。这样,每次进行这种拆分时,训练集和测试集都会获得相同的数据。

我选择 random_state 为 0。你可以选择一些你喜欢的。

Python 的 scikit -learn 库已经有了 KNN 分类器模型。我会进口那个。

from sklearn.neighbors import KNeighborsClassifier

将此分类器保存在变量中。

knn = KNeighborsClassifier(n_neighbors = 5)

这里,n_neighbors 是 5。

这意味着当我们要求我们的训练模型预测新实例的生存机会时,将需要 5 个最接*的训练数据。

基于这 5 个训练数据的标签,模型将预测新实例的标签。

现在,我将使训练数据适合模型,以便模型可以记住它们。

knn.fit(X_train, y_train)

你可能会认为,由于它记住了训练数据,它可以正确预测 100%的训练特征的标签。但这并不确定。为什么?

看,每当我们输入并要求它预测标签时,它会从 5 个最*的邻居那里进行投票,即使它已经记住了完全相同的特征。

让我们看看它能给我们多少训练数据的准确性

knn.score(X_train, y_train)

我得到的训练数据准确率是 0.83 或者 83%。

记住,我们有一个我们的模型从未见过的测试数据集。现在检查一下,它能多准确地预测测试数据集的标签。

knn.score(X_test, y_test)

准确率出来是 0.78 或者 78%。

祝贺你!你开发了一个 KNN 分类器!

注意,训练集的精度比测试集的精度高一点。那太合适了。

什么是过度拟合?

有时,模型对训练集的学习非常好,因此可以很好地预测训练数据集标签。但是,当我们要求模型使用测试数据集或以前没有看到的数据集进行预测时,它的表现不如训练数据集。这种现象被称为过度拟合。

用一句话来说,当训练集精度高于测试集精度时,我们称之为过拟合。

预言;预测;预告

如果您想要查看测试数据集的预测输出,以下是如何操作的:

输入:

y_pred **=** knn.predict(X_test)y_pred

输出:

array([0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0,
       0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
       1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
       1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
       0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
       0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
       1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
       1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1,
       1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1,
       0, 1, 1], dtype=int64)

或者你可以只输入一个例子并找到标签。

我想看看当一个人乘坐“P-class”3 旅行时,“性别”是女性,这意味着 1,并且支付了 25 英镑的“车费”,如果她能按照我们的模型生存下来。

输入:

knn.predict([[3, 1, 25]])

记得用两个括号,因为它需要一个 2D 数组

输出:

array([0], dtype=int64)

输出为零。这意味着根据我们训练的模型,这个人不可能存活。

请随意尝试更多像这样不同的输入!

如果你想看一些 KNN 分类器的进一步分析

KNN 分类器对“k”或 n 个邻居的选择非常敏感。在上面的例子中,我使用了 n 个邻居 5。

对于不同的 n_neighbors,分类器会有不同的表现。

让我们看看它在不同 n_neighbors 值的训练数据集和测试数据集上的表现。我选 1 到 20。

现在,我们将计算从 1 到 20 的每个 n_neighbors 值的训练集精度和测试集精度,

training_accuracy  = []  
test_accuracy = []
for i in range(1, 21):
    knn = KNeighborsClassifier(n_neighbors = i)
    knn.fit(X_train, y_train)
    training_accuracy.append(knn.score(X_train, y_train))
    test_accuracy.append(knn.score(X_test, y_test))

运行这个代码片段后,我得到了不同 n_neighbors 的训练和测试精度。

现在,让我们在同一个图中绘制针对 n_neighbors 的训练和测试集精度。

plt.figure()
plt.plot(range(1, 21), training_accuracy, label='Training Accuarcy')
plt.plot(range(1, 21), test_accuracy, label='Testing Accuarcy')
plt.title('Training Accuracy vs Test Accuracy')
plt.xlabel('n_neighbors')
plt.ylabel('Accuracy')
plt.ylim([0.7, 0.9])
plt.legend(loc='best')
plt.show()

分析上图

一开始,当 n_neighbors 为 1、2 或 3 时,训练精度比测试精度高得多。因此,该模型正遭受高度过拟合。

从那以后,训练和测试的准确性越来越接*。这是最佳点。我们希望如此。

但是当 n_neighbors 变得更高时,训练集和测试集的精度都下降了。我们不需要那个。

根据上图,该特定数据集和模型的最佳 n_neighbors 应为 6 或 7。

这是一个很好的分类器!

看上面的图!当 n_neighbors 约为 7 时,训练和测试准确率都在 80%以上。

结论

本文的目的是用一个项目展示一个 KNN 分类器。如果你是一个机器学习初学者,这将帮助你学习一些机器学习和工作流程的关键概念。有这么多不同的机器学习模型。但这是监督机器学习模型的典型工作流程。

这是我在文章中使用的泰坦尼克号:

[## rashida 048/数据集

在 GitHub 上创建一个帐户,为 rashida048/Datasets 开发做出贡献。

github.com](https://github.com/rashida048/Datasets/blob/master/titanic_data.csv)

更多阅读:

[## 完全理解精确度、召回率和 F 分数的概念

机器学习中如何处理倾斜数据集

towardsdatascience.com](/a-complete-understanding-of-precision-recall-and-f-score-concepts-23dc44defef6) [## Python 中从头开始的 k 均值聚类算法:分步指南

并学习使用它来降低图像的维数

towardsdatascience.com](/k-mean-clustering-algorithm-from-scratch-in-python-and-dimensional-reduction-step-by-step-guide-9ebabe5ca433) [## Python Matplotlib 的日常备忘单

完整的可视化课程

towardsdatascience.com](/your-everyday-cheatsheet-for-pythons-matplotlib-c03345ca390d) [## 练习数据科学技能和制作优秀投资组合所需的所有数据集

一些有趣的数据集提升你的技能和投资组合

towardsdatascience.com](/all-the-datasets-you-need-to-practice-data-science-skills-and-make-a-great-portfolio-857a348883b5) [## 数据宁滨与熊猫削减或 Qcut 方法

当你在寻找一个范围而不是一个确切的数值,一个等级而不是一个分数

towardsdatascience.com](/sort-and-segment-your-data-into-bins-to-get-sorted-ranges-pandas-cut-and-qcut-7785931bbfde) [## Pandas 的 Groupby 功能详细,可进行高效的数据汇总和分析

学习对数据进行分组和汇总,以使用聚合函数、数据转换、过滤、映射、应用函数…

towardsdatascience.com](/master-pandas-groupby-for-efficient-data-summarizing-and-analysis-c6808e37c1cb)

清晰理解深度优先搜索算法及其 Python 实现:图算法

原文:https://towardsdatascience.com/clear-understanding-of-depth-first-search-algorithm-and-its-python-implementation-graph-algorithm-3e87d8f1a9ea?source=collection_archive---------33-----------------------

李灿明在 Unsplash 上拍照

借助清晰的视觉效果进行学习。还要学习人们在深度优先搜索算法中常犯的一个错误

什么是深度优先搜索?

这是广泛使用和非常流行的图搜索算法之一。为了理解这个算法,想象一个迷宫。当我们必须解决一个迷宫的时候我们做什么?我们走一条路,一直走,直到我们找到一个死胡同。在到达死胡同后,我们会原路返回,继续前进,直到我们看到一条我们以前没有尝试过的路。走那条新路线。再次继续前进,直到我们找到一个死胡同。再走一次原路…

深度优先搜索几乎以同样的方式工作。使用这种回溯过程。从起点开始,它一直走,直到再也找不到可以走的路。然后沿原路返回,回到有未探索路径的点。它一直这样做,直到遍历完所有的节点和边。

这是我介绍深度优先搜索的最简单的方法。稍后我会更详细地解释它。

为什么深度搜索很重要

深度优先搜索有广泛的使用案例。

  1. 解决我上面描述的迷宫或难题
  2. 安排问题
  3. 图中的循环检测
  4. 网络分析
  5. 映射路线
  6. 拓扑排序

还有很多。深度优先搜索也是许多其他复杂算法的基础。

深度优先搜索如何工作?

在本节中,我们将直观地看到深度优先搜索的工作流程。这是一个图,源节点显示为节点 u。

我们可以从 u 到节点 v 或 x,我们可以去任何方向。我选择去 v,从图中可以清楚的看到,从 v 出来的路由只有一条,那就是 y。

从图中可以清楚地看到,只有一条从 v 发出的路由,即 y。因此,我们现在位于 y 中。

和以前一样,从 y 也有一个输出路径。那是到 x,所以,我们必须到 x,那是唯一的选择。

看,我们被困住了!如前所述,在这种情况下,我们采取回溯。

通过回溯,我们回到了 y。

从这里没有路可走。所以,我们再回溯一下。

现在,我们在 v. Explore v .中,但是没有从 v .再次传出的路径。所以再退一步。

我们又回到了一步,那就是我们的源节点 u。

这里我们可以看到有一条我们没有探索过的外出路径。

我们从 u 到 x,看到 x 已经被访问过了。这种类型的边缘被称为前沿。从 x 到 v 也有一条路径。节点 v 也被访问,v 是 x 的祖先。因此这条路径被称为后边缘。

我们完成了“uvyx”圆中的所有节点和边。这里我们探索一个新的节点 w。

从 w 出发,我们可以去 z 或者 y,我现在选择去 z。

注意,z 使用后沿返回 z。

从 z 处无处可去,所以我们再次回溯,回到 w 处,w 有一条未探索的边通向 y。

这种类型的连接边称为交叉边。

旅行结束了。我们走遍了所有的节点和边缘。

开发深度第五搜索算法

在开发算法之前,将上图表示为邻接表是很重要的。如果你以前没有见过邻接表,它就是一本字典。其中,每个节点都是一个键,并且在该键中用传出路径链接的节点都是列表中的值。

看下面的邻接表。节点‘u’有两条到节点‘v’和节点‘x’的输出链路。因此,“u”是键,一个包含元素“v”和“x”的列表是值。同样,我们必须每隔一个节点做一个键值对。

g = {
    'u': ['v', 'x'],
    'v': ['y'],
    'y': ['x'],
    'x': ['v'],
    'w': ['y', 'z'],
    'z': ['z']
    }

邻接表准备好了。

我将使用递归方法来开发深度优先搜索算法。

这个想法是遍历所有的节点和顶点,就像我们在上一节的图片中遍历的那样。为了跟踪被访问的节点,我们将从一个空列表开始。

class depth_first:
    def __init__(self):
        self.visited = []

现在定义一个循环遍历所有节点的函数,如果有一个未被访问的节点,我们将进入那个节点,找出这个节点将我们带到哪里。

def dfs(self, graph):        
    for ver in graph:
        if ver not in self.visited:
            self.dfs_visit(graph, ver)
    return self.visited

注意,在这个函数中,我们调用了一个函数“dfs_visit”。该函数应该遍历由未访问节点提供的整个未访问路线,并将这些未访问节点添加到“已访问”列表中。我们将递归地实现这个函数。

以下是“dfs_visit”函数:

def dfs_visit(self, graph, vertex):
    if vertex not in self.visited:
        self.visited.append(vertex)
        for nb in g[vertex]:
            self.dfs_visit(g, nb)

仔细看看!如果节点不在“已访问”列表中,此函数将添加一个节点。然后它会转到与其相邻的一个节点并调用自己。

这样,它将遍历以前没有访问过的整个路线,一次一个。

以下是完整的代码:

class depth_first:
    def __init__(self):
        self.visited = []    def dfs(self, graph):
        for ver in graph:
            if ver not in self.visited:
                self.dfs_visit(graph, ver)
        return self.visited

    def dfs_visit(self, graph, vertex):
        if vertex not in self.visited:
            self.visited.append(vertex)
            for nb in g[vertex]:
                self.dfs_visit(g, nb)

现在让我们用之前描述的邻接表来测试一下。

d = depth_first()
print(d.dfs(g))

输出:

['u', 'v', 'y', 'x', 'w', 'z']

看,节点的顺序和我们预想的一样!

人们在 DFS 算法中常犯的错误

我看到许多其他网站和博客解释了深度优先搜索算法。但是他们使用的代码是这样的:

def dfs(graph, vertex, path=[]):
    path += [vertex]    for n in graph[vertex]:
        if n not in path:
            path = dfs(graph, n, path)
    return path

如果你注意到,它没有通过顶点循环。它从源节点开始,并不断遍历相邻节点。它将在一个图上工作,其中每个节点都有一个连接回任何其他被访问节点的传出节点。

但是在我们正在处理的图中,节点“y”没有到“w”的输出链接,这种算法将不起作用。因为它永远不会到达 w。

让我们检查一下

print(dfs(g, 'u'))

输出:

['u', 'v', 'y', 'x']

看,它看不到节点“w”和“z”。

结论

我想尽可能清楚地介绍和解释深度优先搜索的工作过程以及如何开发该算法。希望现在对你来说很容易。

欢迎在推特上关注我,并喜欢我的脸书页面。

更多阅读

[## 数据科学家使用 Python 进行假设检验的完整指南

用样本研究问题、解决步骤和完整代码清楚地解释

towardsdatascience.com](/a-complete-guide-to-hypothesis-testing-for-data-scientists-using-python-69f670e6779e) [## Python 中从头开始的完整异常检测算法:分步指南

基于概率的异常检测算法

towardsdatascience.com](/a-complete-anomaly-detection-algorithm-from-scratch-in-python-step-by-step-guide-e1daf870336e) [## 熊猫数据可视化的终极备忘单

熊猫所有的基本视觉类型和一些非常高级的视觉…

towardsdatascience.com](/an-ultimate-cheat-sheet-for-data-visualization-in-pandas-4010e1b16b5c) [## 使用 Python 从零开始的多类分类算法:分步指南

本文介绍两种方法:梯度下降法和优化函数法

towardsdatascience.com](/multiclass-classification-algorithm-from-scratch-with-a-project-in-python-step-by-step-guide-485a83c79992) [## 想在 12 周内成为数据科学家?

花钱前再想一想

towardsdatascience.com](/want-to-become-a-data-scientist-in-12-weeks-3926d8eacee2) [## 学习机器学习和深度学习的优质免费课程

顶级大学高质量免费课程的链接

towardsdatascience.com](/great-quality-free-courses-to-learn-machine-learning-and-deep-learning-1029048fd0fc)

一劳永逸地澄清困惑:fig,ax = plt.subplots()

原文:https://towardsdatascience.com/clearing-the-confusion-once-and-for-all-fig-ax-plt-subplots-b122bb7783ca?source=collection_archive---------1-----------------------

了解 Matplotlib 中的图形和轴对象

来自像素的只需说出的名字

介绍

通过阅读本文,您将了解 Maptlolib 绘图中的两个核心对象:图形和轴。你终于会明白简单剧情(plt.plot)和用plt.subplots()创造支线剧情的区别了。

当你开始进入数据科学的旅程时,你会被介绍到 Matplotlib 作为你的第一个数据可视化库。大多数初学者教程对学生开了一个残酷的玩笑,首先向他们介绍“初学者友好的”pyplot > plt界面。当这些穷学生冒险进入现实世界时,他们会发现 StackOverflow 上的花花公子和大多数其他人使用更灵活的面向对象方式。他们会感到困惑,很可能会转移到 Seaborn 和 Plotly。或者更糟,到 Tableau 的无代码接口,就像我差点做的那样。(由于这种混乱,我特别记得自己浏览 Quora 和 StackOverflow 线程,想知道人们是否使用 Tableau 而不是 Matplotlib)

本文将向您介绍 Matplotlib 中的图形和轴对象,以及它们相对于其他方法的优势。

[## 通过我的推荐链接加入 Medium-BEXGBoost

获得独家访问我的所有⚡premium⚡内容和所有媒体没有限制。支持我的工作,给我买一个…

ibexorigin.medium.com](https://ibexorigin.medium.com/membership)

获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:

[## 阿尔法信号|机器学习的极品。艾总结的。

留在循环中,不用花无数时间浏览下一个突破;我们的算法识别…

alphasignal.ai](https://alphasignal.ai/?referrer=Bex)

概观

 I. Introduction II. Setup III. plt.subplots(), preliminary understanding IV. Axes methods vs. pyplot, understanding further V. plt.subplots() grid system VI. Doubling axis VII. Sharing a commong axis between subplotsVIII. Working with figure object IX. Conclusion

示例数据和文章的笔记本可在 this GitHub repo 中获得。

设置

>>> climate_change.head()

plt.subplots(),初步了解

这一切都从调用.subplots()命令开始:

>>> plt.subplots()(<Figure size 432x288 with 1 Axes>, <AxesSubplot:>)

如果您注意的话,除了空白图之外,该函数还返回了一个由两个值组成的元组:

:(<图尺寸 432x288 带 1 轴>,<轴 Subplot: >)

每次我们调用subplots()函数时,它都会返回这些类型的元组,这些元组总是有两个值。在 Python 中,有一种技术叫做元组解包。让我给你看一个简单的例子:

如果我们打印上面三个的值:

>>> print(ten)
10
>>> print(eleven)
11
>>> print(twelve)
12

很好,我们把一个大小为 3 的元组分解成三个不同的变量。所以,现在你会更好地理解这段代码:

我们创建了两个变量,figax。请记住,这些是随意的名字,但却是一个标准,我们是好人之一,所以我们将遵循惯例。

这两个变量现在包含用于所有类型的绘图操作的两个核心对象。第一个对象fig,简称figure,把它想象成你剧情的框架。您可以调整框架的大小和形状,但不能在框架上绘图。在单个笔记本或脚本上,可以有多个图形。每个人物可以有多个支线剧情。在这里,支线剧情与轴线同义。第二个对象,ax,axes 的缩写,是你在上面画画的画布。或者换句话说,它是一张你可以绘制和保存数据的空白纸。一个轴对象只能属于一个图形。

轴方法与 pyplot,进一步了解

在帖子的开头,我说过pyplot是一个更加初学者友好的与 Matplotlib 交互的方法。的确,与 axes 方法相比,pyplot提供了一种更快、更简洁的绘图方法。它将有更少的局部变量和语法。但是为什么大多数人更喜欢面向对象的方式呢?

我们来看看pyplot的简洁方式:

嗯,这很简单。我们只用了三句台词。现在让我们看看,如果我们试图在它旁边绘制(完全不相关的)气候变化数据,会发生什么:

在这种情况下,我们得到一个TypeErrorpyplot本身不能创建新轴或新图形,也不能智能地绘制新数据。当我们进行更复杂的绘图时,我们需要一个更灵活的方法。

Mpl 有这个概念叫current figure。默认情况下,pyplot本身会创建一个current figure轴并在其上绘图。例如,如果我们想要聚焦在那个current figure上,并在其上绘制额外的数据,正如我们在上一个例子中所尝试的,pyplot在给出新的绘制命令后,立即将current figure移动到一个新的。

为了避免这种情况,让我们看看我们完全控制每个图形和轴的方法:

我们特别指出我们正在处理这个fig对象。这意味着我们编写的任何绘图命令都将应用于属于fig的 axes ( ax)对象。除非我们用plt.subplots()命令定义一个新图形,否则当前的figure将是变量fig。这种方式非常好,因为现在我们可以在一个图形中创建尽可能多的轴或支线剧情,并使用它们。

从现在开始,我将交替使用支线剧情和轴线术语,因为它们是同义词。

plt.subplots()网格系统

我们看到了一个创建支线剧情的例子。让我们看看如何在单个图形中创建更多:

在其他参数中,.subplots()有两个参数指定网格尺寸。nrowsncols分别用来指出我们需要的行数和列数。如果你注意的话,现在我们的第二个变量包含不是一个而是两个轴。而且现在给的是一个numpy.ndarray。因此,我们必须解包或索引该数组,以使用我们的绘图命令:

专业提示:注意padding设置为 3 的fig.tight_layout()功能。这会给支线剧情一点喘息的机会。

这两种方法完全相似,由您选择一种。让我们再看一个例子,但稍微难一点:

专业提示:正确设置figsize=(width, height)参数。会让你的剧情更加鲜明。

这篇文章不是特别关于绘图,而是给你对图形和轴对象的直觉。但是,让我简要地向您介绍一下 axes 对象的其他一些常用方法:

所有在pyplot API 中可用的方法在ax.set_中都有等价的方法。如果你使用一个通用的,ax.set()方法,当你有多个支线剧情时,你会避免重复。但是,如果您需要为绘图的特定部分指定附加参数,请使用ax.set_:

双重轴

有时候,我们希望一个支线剧情有不止一个XAxis或者YAxis。虽然用普通的pyplot接口是不可能的,但用顶级的figure面向对象的 API 是非常容易的。假设我们想要在一个单独的绘图中绘制climate_changerelative_tempco2列。我们希望它们共享一个XAxis,因为数据是同一时间段的:

我们希望有一个公共的XAxis,即date列,所以我们使用ax.twinx()创建了另一个轴。如果在某些情况下你想要一个通用的YAxis,等效的功能是ax.twiny()

在支线剧情之间共享一个公共轴

假设我们想比较八十年代和九十年代的二氧化碳排放量。理想情况下,我们会希望在一边绘制 80 年代,在另一边绘制 90 年代。因此,让我们对这两个时间段的数据进行分组:

专业提示:如果您正在处理时间序列数据,请将日期列设置为数据帧的索引。使用.set_index()方法或使用pd.read_csv()功能中的index_col参数。这将使时间段的子集设置更加容易。

很好,我们有两个并排的图,但是如果我们仔细看,我们的图是误导的。看起来两个时间段的二氧化碳排放量没有太大的差别。其原因是两个图具有不同的YAxis范围。为了更好的理解,让我们来纠正它:

现在,很明显,随着时间的推移,二氧化碳排放量持续增加(比现在高得多)。我们使用sharey=True来指定我们想要相同的YAxis给所有的支线剧情。

使用体形对象

我想你已经注意到,一旦你使用.subplots()命令或其他方法创建了一个图形对象,几乎所有的事情都会发生在 axes 对象上。figure对象的一个常用方法是savefig()方法。所以,让我们开始探索吧。我们将回到二氧化碳的双轴图。让我们将它保存到本地内存:

我们传递了一个文件名作为要保存的字符串。这将在根目录中保存一个同名的图像。可以使用的图像格式:

  1. .png -如果你想要高质量的图片并且不在乎磁盘空间,使用png。这种格式允许无损压缩。
  2. .jpg -有损压缩。jpg图像将比png的尺寸小,质量也低。如果您希望以后将图像上传到网站或任何其他类似的方式,建议使用这种格式。
  3. .svg -支持动画和图像编辑。如果您想使用编辑软件(如 Adobe Illustrator)修改图形,请使用这种格式。

.savefig()的其他参数允许控制您的图形质量:

结论

我希望您现在对图形和轴对象有了清晰的理解。我们只介绍了 Matplotlib 中的一种绘图方法。如果你想进一步探索,图书馆可以深入到你想要的深度。你可以在 Matplotlib 的官方文档中了解更多关于图形和轴对象的方法。我强烈建议您尝试其他功能并进行实践!(顺便说一句,那是很多 GitHub gists!)

看看我在 Medium 上的其他作品:

[## 掌握 Seaborn 中的 catplot():分类数据可视化指南。

如果你能在锡伯恩做到,那就在锡伯恩做吧,#2

towardsdatascience.com](/mastering-catplot-in-seaborn-categorical-data-visualization-guide-abab7b2067af) [## 掌握 Seaborn 的三分之一:用 relplot()统计绘图

如果你能在锡伯恩做到,就在锡伯恩做吧

towardsdatascience.com](/master-a-third-of-seaborn-statistical-plotting-with-relplot-df8642718f0f)

解释清楚:4 种机器学习算法

原文:https://towardsdatascience.com/clearly-explained-4-types-of-machine-learning-algorithms-71304380c59a?source=collection_archive---------13-----------------------

你是一个涉足机器学习的数据科学家吗?如果是的话,那么你应该看看这个。

定义、目的、流行的算法和用例——都有解释

安迪·凯利在 Unsplash 上的照片

机器学习已经走过了漫长的道路,从仅仅是一个科幻小说的主题到成为最可靠和最多样化的商业工具,增强了每个商业运作的多方面。

它对各种业务表现的影响已经变得如此重要,以至于需要实施顶级的机器学习算法来确保许多行业在这个高度竞争的世界中的生存。

在商业运作中实施 ML 需要大量的资源,并且是一个战略步骤。因此,现在我们理解了机器学习算法为什么重要,下一步就是理解如何使用 ML 算法来拯救你。

实现这一目标的第一步是清楚地了解你希望使用机器学习来解决什么样的业务问题,并清楚地了解不同类型的机器学习算法所需的资源和努力,以便你能够从桌面上的许多算法中选择最佳算法。

在这篇文章中,我们将介绍机器学习算法的主要类型,解释每种算法的用途,并看看它们的好处是什么。

机器学习算法的类型

1.监督机器学习算法

监督学习算法是所有四种 ML 算法中最简单的。这些算法需要模型开发者的直接监督。在这种情况下,开发人员会标记样本数据语料库,并设置算法运行的严格边界。

这是机器学习的一个简单版本:

  • 你选择什么样的信息输出(样本)给【馈送】算法
  • 你决定什么样的结果是想要的(例如“是/否”或“真/假”或“销售额/净信贷损失/房价”等)

从机器的角度来看,这个过程或多或少是一个“连接点”的例行程序。

监督学习的主要目的是扩大数据的范围,并根据标记的样本数据预测不可用、未来或未知的数据。

监督机器学习包括两个主要过程:分类和回归。

  • 分类 是从过去的数据样本中学习并手动训练模型以预测本质上二元结果(是/否,真/假,0/1) 的过程。例如:在接下来的 9 个月里,客户是否会流失,某人是否患有癌症等等。分类算法识别某些类型的对象,并相应地对它们进行分类,以预测两种可能结果中的一种。
  • 回归 是识别模式并计算连续结果的预测的过程。例如:预测房价或下个月的销售预测等。

使用最广泛的监督算法有:

  • 线性回归;
  • 逻辑回归;
  • 随机森林;
  • 梯度增强树;
  • 支持向量机(SVM);
  • 神经网络;
  • 决策树;
  • 朴素贝叶斯;
  • 最*的邻居。

监督学习算法用例

这些算法最常见的应用领域是销售、零售商业和股票交易中的价格预测和趋势预测。这些算法使用输入数据来评估可能性并计算可能的结果。

2.无监督机器学习算法

无监督学习算法不涉及开发者的直接控制。监督机器学习要求的要点是,我们应该事先知道过去数据的结果,以便能够预测未知数据的结果,但是在无监督机器学习算法的情况下,期望的结果是未知的,尚未定义

有时候你不想准确预测结果。您只想执行一个分段或聚类。例如,一家银行希望对其客户进行细分,以了解他们的行为。这个商业问题需要使用无监督的机器学习算法,因为这里没有预测具体的结果。

两者的另一大区别是,监督学习专门使用标记数据,而非监督学习则以未标记数据为食。

无监督机器学习算法用于:

  • 探索信息的结构;
  • 提取有价值的见解;
  • 检测模式;
  • 将这一点落实到运营中以提高效率。

换句话说,无监督的机器学习试图通过筛选和理解提供给它的信息来描述这些信息。

无监督学习算法应用以下技术来描述数据:

  • 聚类:它是一种对数据的探索,用于根据数据的内部模式将其分割成有意义的组(即聚类),而无需任何组凭证的先验知识。凭证由单个数据对象的相似性以及它与其他数据对象的不同之处来定义(这也可以用于检测异常)。
  • 降维:很多时候,传入的数据中有很多噪音。机器学习算法使用降维来消除这种噪声,同时提取相关信息。

最广泛使用的无监督算法有:

  • k-均值聚类;
  • t-SNE(t-分布式随机邻居嵌入);
  • PCA(主成分分析);
  • 关联规则。

无监督学习算法用例

数字营销(用于基于某些凭证识别目标受众群体——可以是行为数据、个人数据元素、特定软件设置或其他)和广告技术(该算法可用于开发更有效的广告内容目标,也可用于识别活动表现中的模式)是采用无监督学习算法的主要行业。这些也用于需要探索客户信息和调整相关服务的情况。

3.半监督机器学习算法

半监督学习算法代表了监督和非监督算法之间的中间地带。本质上,半监督模型将两者的某些方面结合成一个自己的东西。

让我们理解半监督算法是如何工作的:

  1. 半监督机器学习算法使用有限的一组标记样本数据来形成操作的要求(即训练本身)。
  2. 这种限制导致了部分训练的模型,该模型随后得到标记未标记数据的任务。由于样本数据集的限制,结果被认为是伪标记数据
  3. 最后,标记的和伪标记的数据集被组合,这创建了一个独特的算法,该算法结合了监督和非监督学习的描述性和预测性方面。

半监督学习使用分类过程来识别数据资产,并使用聚类过程将其分组为不同的部分。

半监督学习算法用例

法律和医疗保健行业以及其他行业在半监督学习的帮助下管理 web 内容分类、图像和语音分析。

在 web 内容分类的情况下,半监督学习被应用于爬行引擎和内容聚合系统。在这两种情况下,它都使用各种标签来分析内容,并将其排列在特定的配置中。然而,这个过程通常需要人工输入来进一步分类。

在图像和语音分析的情况下,算法执行标记以基于样本语料库提供具有连贯转录的可行图像或语音分析模型。例如,它可以是 MRI 或 CT 扫描。利用一小组示例性扫描,有可能提供能够识别图像中异常的相干模型。

4.强化机器学习算法

强化学习通常理解为机器学习人工智能。

从本质上讲,强化学习就是开发一个自我维持的系统,通过连续的尝试和失败,基于标记数据的组合和与输入数据的交互来改进自己。

加强型 ML 采用了一种叫做勘探/开发的技术。机制很简单——行动发生,结果被观察,下一个行动考虑第一个行动的结果。这是一种迭代算法。

使用这种算法,机器被训练来做出特定的决定。它是这样工作的:机器被暴露在一个环境中,在这个环境中,它不断地通过试错来训练自己。机器从过去的经验中学习,并试图捕捉尽可能好的知识,以做出准确的商业决策。

最常见的强化学习算法包括:

  • q-学习;
  • 时差(TD);
  • 蒙特卡罗树搜索(MCTS);
  • 异步行动者-批评家代理(A3C)。

强化学习算法用例

强化机器学习适用于信息有限或不一致的情况。在这种情况下,算法可以基于与数据和相关过程的交互来形成其操作程序。

现代 NPC 和其他视频游戏大量使用这种类型的机器学习模型。强化学习为人工智能对玩家动作的反应提供了灵活性,从而提供了可行的挑战。例如,碰撞检测功能对移动的车辆使用这种类型的 ML 算法。

自动驾驶汽车也依赖于强化学习算法。例如,如果自动驾驶汽车( Waymo )检测到道路左转——它可能会激活“左转”场景等等。

这种强化学习变化的最著名的例子是 AlphaGo,它与世界上第二好的围棋选手正面交锋,并通过计算当前棋盘位置之外的动作序列击败了他。

另一方面,市场营销和广告技术运营也使用强化学习。这种类型的机器学习算法可以通过紧密适应用户的行为和周围环境,使重定向操作在传递转换方面更加灵活和高效。

此外,强化学习用于放大和调整聊天机器人的自然语言处理( NLP )和对话生成,以:

  • 模仿输入消息的风格
  • 开发更多吸引人的、信息丰富的回答
  • 根据用户反应找到相关回应。

总之,要记住的最重要的 ML 算法类型是监督式、非监督式和强化式。

请继续关注这个空间,了解更多关于统计、数据分析和机器学习的信息!

清楚地解释:集成学习方法,机器学习的核心

原文:https://towardsdatascience.com/clearly-explained-ensemble-learning-methods-the-heart-of-machine-learning-fc4b828601b9?source=collection_archive---------58-----------------------

机器学习基础

去神秘化,非传统的解释,它会值得你花时间。

欧文·比尔德在 Unsplash 上的照片

我曾在“上发表过一个帖子,清楚地解释了:什么是偏差-方差权衡,过度拟合&欠拟合”。如果你是一个新手,或者只是想在全力投入整体建模的海洋之前温习一下 偏差和方差 的概念,这篇全面的文章可以作为这篇文章的重要前传。说到这里,我们继续了解更多关于系综建模的知识。我将引用一些现实生活中的例子来简化系综模型的 什么、为什么和如何的概念,重点是装袋和助推技术

来源: Giphy

场景 1: 你需要一副新耳机。现在,你有可能走进一家商店,买下售货员展示给你的耳机吗?在这个时代,我确信答案是否定的,因为现在我们在购买任何东西之前都非常依赖我们的“研究”。你可以浏览一些网络技术门户网站,查看用户评论,然后比较你感兴趣的不同型号,同时检查它们的功能和价格。你也可能会询问你的朋友和同事的意见。简而言之,你不会直接得出结论,而是在彻底研究之后做出明智的决定。

现在,我们可以看看集成学习方法的技术定义。

来源:灵感来自 SAS 研究所的一张图表

什么是系综法?

机器学习中的集成模型组合来自多个模型的决策,以提高整体性能。他们的操作思路与购买耳机时相似。

学习模型中误差的主要原因是由于噪声、偏差和方差

集成方法有助于将这些因素降至最低。这些方法旨在提高机器学习算法的稳定性和准确性。

场景 2: 假设你开发了一个健康健身 app。在将其公之于众之前,您希望收到重要的反馈,以弥补潜在的漏洞(如果有的话)。你可以求助于以下方法之一,阅读并决定哪种方法是最好的:

  1. 你可以听取配偶或密友的意见。
  2. 你可以问问你的朋友和办公室同事。
  3. 您可以发布该应用的测试版,并接收来自 web 开发社区和非偏见用户的反馈。

没有印象分的猜测答案:D,是的,当然,我们将推出第三个选项。

现在,暂停一下,想想你刚才做了什么。你从足够多的人那里听取了多种意见,然后根据这些意见做出了明智的决定。这也是集合方法所做的。

机器学习中的集成模型将来自多个模型的决策进行组合,以提高整体性能。

场景三:看看下图;我们可以看到一群被蒙住眼睛的孩子在玩“触摸并告诉”的游戏,同时检查一头他们从未见过的大象。他们中的每一个人都有不同的版本来描述大象的样子,因为他们中的每一个人都接触到了大象的不同部位。现在,如果我们让他们提交一份关于大象描述的报告,他们的个人报告将只能根据他们的经验准确地描述一部分,但他们可以结合他们的观察,给出一份关于大象描述的非常准确的报告。

类似地,集成学习方法使用一组模型,与使用单个模型相比,它们的组合结果在预测准确性方面几乎总是更好。

集成是一种分而治之的方法,用于提高性能。

来源: Pixabay

现在,让我们深入一些重要的合奏技巧。

裂一点:D 你做的很棒:)来源:链接

简单的集成技术

  1. 取结果的模式

众数:众数是一个统计术语,指在一组数字中出现频率最高的数字。

在这种技术中,使用多个模型来预测每个数据点。每个模型的预测都被视为单独的投票。我们从大多数模型中得到的预测被用作最终预测。

例如:我们可以通过参考上面的场景 2 来理解这一点。我在下面插入了一张图表来展示我们的健康和健身应用程序的测试版从用户社区获得的评级。(将每个人视为不同的模型

输出=模式=3,因为大多数人都这样投票

2.取结果的*均值

在这种技术中,我们从所有模型中获取预测的*均值,并使用它们进行最终预测。

*均值=总和(评分*人数)/总人数=(1 * 5)+(2 * 13)+(3 * 45)+(4 * 7)+(5 * 2)/72 = 2.833 =四舍五入到最接*的整数将是 3

3.取结果的加权*均值

这是*均法的延伸。所有模型被赋予不同的权重,定义每个模型对于预测的重要性。例如,如果你的回答者中有 25 人是专业的应用程序开发人员,而其他人之前没有这方面的经验,那么这 25 人的回答会比其他人更重要。

例如:为了子孙后代,我将这个例子的规模缩减到 5 个人

加权*均值=(0.3 * 3)+(0.3 * 2)+(0.3 * 2)+(0.15 * 4)+(0.15 * 3)= 3.15 =四舍五入到最接*的整数得出 3

高级集成技术

我们现在将学习装袋和增压技术。但是,要使用它们,你必须选择一个基本的学习算法。例如,如果我们选择一个分类树,打包和提升将由一个我们想要的大小的树池组成。

1.打包(引导汇总)

自举聚合是一种集成方法。首先,我们创建带有替换的训练数据集的随机样本(训练数据集的子集)。然后,我们为每个样本建立一个模型(分类器或决策树)。最后,使用*均或多数投票来组合这些多个模型的结果。

由于每个模型都暴露于不同的数据子集,我们最终会使用它们的集体输出,因此我们会确保通过不要过于依赖我们的训练数据集来解决过度拟合的问题。因此,装袋帮助我们减少方差误差。

多个模型的组合降低了方差,尤其是在模型不稳定的情况下,并且可能产生比单个模型更可靠的预测。

随机森林技术实际上使用了这一概念,但它更进一步,通过在训练时为每个自举样本随机选择一个特征子集来进行分裂,从而进一步减少方差。

来源:链接

2.助推

来源:吉菲

提升是一种迭代技术,它基于最后一次分类来调整观察的权重。如果一个观察被错误地分类,它会试图增加这个观察的权重,反之亦然。

Boosting 总体上降低了偏差误差,建立了强预测模型。Boosting 显示出比 bagging 更好的预测准确性,但它也倾向于过度拟合训练数据。 因此,参数调整成为 boosting 算法避免过拟合的关键部分。

Boosting 是一种顺序技术,其中第一个算法在整个数据集上进行训练,后续算法通过拟合第一个算法的残差来构建,从而为先前模型预测不佳的那些观察值赋予更高的权重。

它依赖于创建一系列弱学习者,每个弱学习者可能对整个数据集都不好,但对数据集的某个部分是好的。因此,每个模型实际上都提高了整体的性能。

装袋和增压之间的差异总结

来源:链接

集合方法的优势/好处

几乎所有的 ML 黑客马拉松都使用集成方法来增强模型的预测能力。

让我们来看看使用系综方法的优势:

  1. 更准确的预测结果——我们可以将集合方法的工作与我们的金融投资组合的多样化进行比较。建议保持债券和股票的混合投资组合,以减少可变性,从而最大限度地降低风险。类似地,在大多数情况下,与单个模型相比,模型集合将在测试用例场景(看不见的数据)上提供更好的性能。
  2. 稳定且更健壮的模型 -多个模型的综合结果总是比单个模型的噪音小。这导致模型的稳定性和鲁棒性。
  3. 集合模型可用于捕捉数据中的线性和非线性关系。这可以通过使用两种不同的模型并形成两者的集合来实现。

集合方法的缺点

  1. 降低模型解释能力- 由于复杂性增加,使用集成方法降低了模型解释能力,使得最终很难得出任何关键的商业见解。
  2. 计算和设计时间高- 对实时应用不利。
  3. 选择模特来创作合奏是一门很难掌握的艺术。

因此,现在我们已经讨论了集合方法的所有基础知识,您可以着手尝试它们来进一步增强您的理解。

你可以看看我下面关于机器学习的其他帖子,这些帖子很受欢迎。

[## 分类 ML 模型的 10 大模型评估指标

解释非常规,这将作为评估分类机器学习模型的详尽列表。

towardsdatascience.com](/top-10-model-evaluation-metrics-for-classification-ml-models-a0a0f1d51b9) [## 解释清楚:4 种机器学习算法

定义、目的、流行的算法和用例——都有解释

towardsdatascience.com](/clearly-explained-4-types-of-machine-learning-algorithms-71304380c59a)

更多关于机器学习、数据科学和统计学的定期帖子,请关注这个空间。

感谢您的阅读!😃

快乐学习:)

解释清楚:基尼系数和洛伦茨曲线

原文:https://towardsdatascience.com/clearly-explained-gini-coefficient-and-lorenz-curve-fe6f5dcdc07?source=collection_archive---------0-----------------------

一个重要的统计经济系数——基尼系数

让我们了解基尼系数是什么和为什么以及洛伦兹曲线的一切

这篇文章将为数据科学专业人士解释基尼系数的用法和相关性,我们还将了解洛伦兹曲线,这是一种用图形确定基尼系数的方法。

基尼指数的传统定义是什么?

基尼指数或基尼系数是由意大利统计学家科拉多·基尼在 1912 年提出的一种分配的统计方法。它被用来衡量经济不*等,测量人口中的收入分配。

系数范围从 0(或 0%)到 1(或 100%),0 代表完全相等,1 代表完全不相等。超过 1 的值实际上是不可能的,因为我们没有考虑负收入。(收入最低时可以为 0,但不能为负)

因此,一个所有居民收入相同的国家,其收入基尼系数为 0。在一个国家,一个居民赚取了所有的收入,而其他人什么都没赚到,那么这个国家的收入基尼系数就是 1。

众所周知,基尼系数是分析一个国家或地区收入或财富分配的重要工具,但是,

基尼不应被误认为是对收入或财富的绝对衡量。

一个高收入国家和一个低收入国家可以有相同的基尼系数,只要收入在每个国家内分配相似:

根据经合组织的数据,2016 年土耳其和美国的收入基尼系数都在 0.39-0.40 左右,尽管土耳其的人均 GDP还不到美国的一半(以 2010 年美元计算)。

基尼指数在数据建模中的应用

基尼系数或基尼指数衡量一个变量的值之间的不*等。指数值越高,数据越分散。或者,基尼系数也可以计算为相对*均绝对差的一半。

基尼指数的图示(洛伦茨曲线)

基尼系数通常是根据洛伦茨曲线在数学上定义的,洛伦茨曲线绘制了人口总收入(y 轴)中最底层 x%的人口累积收入的比例。

因此,45 度线代表收入完全*等。

基尼系数可以被认为是位于*等线和洛伦兹曲线之间的面积(图中标记为 A,下图中标记为 A)与*等线下的总面积(图中标记为 A 和 B)之比;即 G = A / (A + B)。由于 A + B = 0.5(因为坐标轴的刻度从 0 到 1),它也等于 2A 和 1–2B。

基尼系数通常通过洛伦茨曲线来表示,洛伦茨曲线通过在水*轴上绘制收入的人口百分位数,在垂直轴上绘制累积收入来显示收入(或财富)分布。

基尼系数等于完全*等线以下的面积(定义为 0.5)减去洛伦茨曲线以下的面积,除以完全*等线以下的面积。换句话说,它是洛伦兹曲线和完全*等线之间面积的两倍。

另一种思考基尼系数的方式是作为对完全*等的偏离的衡量。洛伦茨曲线偏离完全*等的直线(代表基尼系数为 0)越远,基尼系数越高,社会越不*等。

图:图片来源- 维基百科

示例:以下是如何通过绘制洛伦兹曲线并计算*等线和曲线之间的面积来计算基尼系数的 excel 图示。

所以,现在基尼系数及其使用洛伦兹曲线的计算对你们来说应该是小菜一碟:)

如果你有任何问题或建议,请告诉我。

感谢您的阅读:)

请关注这个空间,了解更多关于分析、统计和机器学习的内容!

快乐学习!!

清楚地解释:机器学习如何不同于统计建模

原文:https://towardsdatascience.com/clearly-explained-how-machine-learning-differs-from-statistical-modeling-967f2c5a9cfd?source=collection_archive---------12-----------------------

它们彼此非常不同,所有的数据科学家都必须理解为什么和如何!

来源:灵感来自于 SAS 研究所的图表

这篇文章提出了一个非常重要的区别,作为数据科学领域的一个活跃部分,我们应该理解这个区别。上面的文氏图最初是由 SAS Institute 发布的,但他们的图没有显示统计学和机器学习之间的重叠,据我所知,这可能是一个疏忽。根据我的信息和理解,我已经重新创建了这个图表。这个文氏图非常恰当地提出了数据科学所有分支的区别和重叠。

我愿意相信数据科学现在是一个总括术语,所有其他科学都可以被描述为数据科学的分支,每个分支都不同,但又与其他分支如此相似!

机器学习 VS 统计建模:这是一个古老的问题,每个数据科学家/ML 工程师或任何开始这些领域旅程的人都会遇到。在研究这些领域时,有时机器学习感觉与统计建模交织在一起,这让我们想知道如何区分这两者,或者哪个标签对哪个模型来说是完美的。当然,如今机器学习已经成为一个时髦词汇,但这并不意味着我们开始将我们的统计模型贴上机器学习模型的标签,因为与流行的观点相反,它们是不同的!下面详细了解一下区别。

这个帖子的流程将是:

  1. 机器学习和统计建模的定义
  2. 机器学习和统计建模的区别
  3. 什么时候用哪个?

定义

机器学习

对开发计算机算法感兴趣的研究领域被称为机器学习,该算法在不依赖基于规则的编程的情况下将数据转换为智能行为。

统计建模

统计模型通常被指定为一个或多个随机变量和其他非随机变量之间的数学关系。因此,统计模型是“一个理论的正式表示”。

既然枯燥冗长的定义已经过时,让我们更深入地理解这两个领域之间的差异。

机器学习和统计建模的区别

1.历史和学术相关性

统计建模在机器学习出现之前几个世纪就已经出现了,大约在 20 世纪 50 年代,第一个 ML 程序——塞缪尔的 checker 程序被引入。

世界上所有的大学现在都在启动他们的机器学习和人工智能项目,但他们并没有关闭他们的统计部门。

机器学习与计算机科学部门和独立的人工智能部门一起教授,这些部门处理构建预测算法,这些算法能够通过学习从数据中“学习”而变得“智能”,而没有任何预先指定的规则,如上文 ML 的定义中所述。

鉴于

统计建模与数学系一起教授,数学系侧重于建立模型,这些模型首先可以找到不同变量之间的关系,然后可以预测可以描述为其他独立变量的函数的事件。

2.不确定性公差

这是这两个领域的重要区别。

统计建模中,我们注意到许多不确定性估计(如置信区间、假设检验),我们必须考虑到,在我们可以信任特定算法的结果之前,必须满足所有假设。因此,它们具有低的不确定性容忍度。

例如:如果我们已经建立了一个线性回归模型,在我们使用该模型的结果之前,我们必须检查以下假设是否得到满足:

  1. 因变量和自变量之间的线性关系
  2. 误差项的独立性
  3. 误差项(残差)需要正态分布
  4. *均独立性
  5. 无多重共线性
  6. 需要同质性

相反,如果我们建立了一个逻辑模型,必须考虑以下假设:

  1. 二元逻辑回归要求因变量是二元的,而有序逻辑回归要求因变量是有序的。
  2. 观察必须是相互独立的。
  3. 无多重共线性
  4. 独立变量和对数优势的线性

鉴于

机器学习算法中,需要考虑的假设很少或者没有。ML 算法比统计模型灵活得多,因为它们对共线性、残差的正态分布等没有严格的要求。因此,它们具有很高的不确定性容忍度。

3.数据要求和方法

统计模型不能在非常大的数据集上运行,它们需要具有较少属性和相当数量观察值的可管理数据集。在统计模型中,属性的数量通常不会超过 10–12,因为它们非常容易过度拟合(在训练数据集上表现很好,但在看不见的数据上表现很差,因为它非常接*训练数据集,这是一个不希望的场景)

同样, 大多数 统计模型遵循参数方法(例如:线性回归、逻辑回归)

鉴于

机器学习算法是学习算法,要学习它们需要大量的数据。因此,它们需要具有大量属性和观察值的数据。越大越好! ML 算法在某种程度上需要大数据。

另外, 大部分的 机器学习模型都遵循非参数方法 (K *邻、决策树、随机森林、梯度推进方法、SVM 等。)

什么时候用哪个?

这主要取决于下面解释的因素。我们将通过例子从理论上讨论这些要点。

统计模型应该是您的首选,如果:

  • 不确定性很低,因为当您开始构建模型时,大多数假设都得到满足
  • 数据的大小不是很大
  • 如果你想隔离少数变量的影响
  • 总体预测的不确定性/边际误差是可以的
  • 各种独立变量之间的相互作用在数量上相对较少,并且可以预先指定
  • 需要高度的可解释性

如果,机器学习可能是更好的选择

  • 当要预测的结果不具有很强的随机性时;例如,在视觉模式识别中,对象必须是E或者不是E
  • 可以在无限数量的精确复制上训练学习算法(例如,字母表中的每个字母或要翻译成德语的某个单词的 1000 次重复)
  • 当整体预测是目标时,不能描述任何一个独立变量的影响或变量之间的关系
  • 人们对估计预报中的不确定性或选定预测因子的影响不太感兴趣
  • 数据的规模是巨大的
  • 人们不需要孤立任何特殊变量的影响
  • 低可解释性和模型是一个“黑盒”是可以接受的

例如:如果你与一家信用卡公司合作,他们希望建立一个模型来跟踪客户流失,那么他们很可能更喜欢一个有 10-12 个预测值的统计模型,他们可以根据自己的业务领域知识来解释和否决这些预测值,他们不会欣赏这种情况下的黑盒算法,因为对可解释性的需求高于预测的准确性。

另一方面,如果你为网飞和亚马逊这样的客户工作,他们想要建立强大的推荐引擎,那么在这种情况下,对结果准确性的需求高于模型的可解释性,因此,机器学习模型就足够了。

这样,我们就到了这篇文章的结尾。

你可以在我的以下帖子中了解更多关于数据挖掘和机器学习之间的区别以及 4 大机器学习算法的完整细节:

[## 清楚地解释:机器学习与数据挖掘有何不同

定义、混淆、区别——都有解释

towardsdatascience.com](/clearly-explained-how-machine-learning-is-different-from-data-mining-4ee0e0c91bd4) [## 解释清楚:4 种机器学习算法

定义、目的、流行的算法和用例——都有解释

towardsdatascience.com](/clearly-explained-4-types-of-machine-learning-algorithms-71304380c59a)

观看此空间,了解更多关于机器学习、数据科学和统计学的信息!

快乐学习:)

清楚地解释:机器学习与数据挖掘有何不同

原文:https://towardsdatascience.com/clearly-explained-how-machine-learning-is-different-from-data-mining-4ee0e0c91bd4?source=collection_archive---------23-----------------------

让我们谈一谈大数据时代以及为什么 ML 变得如此受欢迎!

定义、混淆、区别——都有解释

Artem Sapegin 在 Unsplash 上拍摄的照片

该帖子的流程如下:

  1. 谈论大数据时代以及为什么 ML 现在变得如此流行?
  2. 数据挖掘和机器学习领域的历史和混淆点
  3. 数据挖掘和机器学习的定义
  4. 数据挖掘和机器学习的区别

人类从出生开始就开始分析数据。儿童接触到大量的信号(通过眼睛、耳朵、鼻子、舌头和神经),他的大脑很快学会/适应如何分析这些信号并做出反应。继口语之后,书面语的出现创造了巨大的数据仓库,可以分析至今。

很多人说,我们刚刚进入了大数据 的 时代,但数据总是在我们周围,只是【记录】数据的数量已经开始激增,这是由于 4 个主要因素也导致了机器学习在今天变得如此流行 (以下 4 点是由 AnalyticsVidhya 发布的信息指南提出的):**

  1. 仓储成本低
  2. 低计算成本
  3. 现在连接到互联网的设备数量非常多——到 2020 年大约有 500 亿台联网设备
  4. 当然,云技术给数据世界带来了民主

历史

现在,让我们多谈一点历史,以理解这篇文章所涉及的两个领域——数据挖掘和机器学习

数据挖掘产生于 20 世纪 30 年代,最初被称为数据库中的知识发现,机器学习是在 20 世纪 50 年代第一个 ML 程序 Samuel's Checker 程序发布时引入的。

数据挖掘和机器学习之间的混淆点

  1. 两者都是分析过程,是数据科学行业不可或缺的一部分。
  2. 两者都是从它们在模式识别用例中的最初使用中产生的。
  3. 两者都构成了从大量数据中学习。

数据挖掘和机器学习的定义

数据挖掘

它包括在现有数据中系统地搜寻有价值的情报。

机器学习

对开发将数据转化为智能行为的计算机算法感兴趣的研究领域被称为机器学习。

现在,无聊的定义已经远离我们,让我们跳到最有趣的部分,差异!

数据挖掘和机器学习的区别

数据挖掘是商业分析的一个子集,它专注于教会计算机——如何识别大型数据集中以前未知的模式、关系或异常,然后人类可以使用它们来解决商业问题。这是一个需要人工干预和决策的手动过程。人类可以通过引入智能特征来使该过程变得智能。

例如:如果一家信用卡公司拥有大量关于客户行为的数据,并且他们希望了解客户流失的模式,那么他们将部署数据挖掘技术来找出不同客户特征之间的关联,这可以强烈地表明未来的客户流失。

鉴于

机器学习是人工智能的一个子集,它专注于教计算机——如何学习分析大型数据集,并“学习”其中的模式(从训练数据集)这些模式可以帮助对新数据进行预测。除了初始编程和微调,计算机完全不需要人工干预来学习数据并自行改进其预测。随着机器接收越来越多的数据,它将变得越来越智能。

例如:网飞是机器学习的一个完美例子。我今天在我的建议列表上看到了《金钱大劫案》,那么,网飞怎么知道我接下来想看什么呢?后端有一个强大的机器学习算法,它不断分析与我有类似简档的其他用户的观看偏好,因此它基于此提出建议。随着你看得越来越多,算法会了解你对该类型的偏好,你会得到基于此的建议!

结束语

实际上,所有的数据挖掘都涉及到机器学习的使用,但并不是所有的机器学习都涉及到数据挖掘。

例如:你可以应用机器学习来数据挖掘汽车交通数据,以获得与事故率相关的模式,但另一方面,如果我们谈论无人驾驶汽车,它们是基于纯粹的机器学习,不涉及任何数据挖掘。

作为延续,你可以阅读我下面的帖子来了解 4 种机器学习算法!一定要去看看!

[## 解释清楚:4 种机器学习算法

定义、目的、流行的算法和用例——都有解释

towardsdatascience.com](/clearly-explained-4-types-of-machine-learning-algorithms-71304380c59a)

请继续关注这个空间,了解更多关于机器学习、数据分析和统计的信息!

解释清楚:正态分布

原文:https://towardsdatascience.com/clearly-explained-normal-distributions-and-the-central-limit-theorem-8d7cc5a6052f?source=collection_archive---------28-----------------------

为什么每个 ML 工程师或数据科学家都必须了解这一点

这些概念有什么正常或不正常的地方?

资料来源:Giphy

每个试图踏上数据科学的旅程的人都会遇到正态分布。在这篇文章中,我将以非常清晰的方式解释它到底是什么,我们如何解释它,以及为什么它作为一个每个数据科学家都必须意识到的概念具有巨大的重要性。

什么是正态分布?

正态分布也被称为高斯分布或钟形曲线(因为它看起来像一个钟,哈哈!多么直观!),这是统计学中最重要的概率分布,有点神奇,因为我们在大自然中经常看到它。比如身高、体重、血压、测量误差、智商分数等。都遵循正态分布。

有一个原因使得它对统计学非常有用,它被称为中心极限定理,但是让我们把它留到另一篇博文中。最后给出的链接是这篇文章的完美续篇。

现在,参考上图,理解一个正态变量的值是如何分布的。这是一个对称分布,其中大多数观察值聚集在出现概率最高的中心峰值(*均值/*均值)周围,随着我们在两个方向上远离中心峰值,我们看到曲线尾部值出现的概率变得越来越小。这幅图描绘了一个人群的智商水*,可以理解的是,智商水*很低或很高的人很少出现,大多数人的智商都在*均水*范围内。

我们周围的很多很多变量都可以用这个正态/钟形分布来描述。想想你所有的同事到达办公室所花的时间,只有少数人住在 5 分钟或 2 个多小时的距离内。大多数人会在 20 分钟到 70 分钟的距离内(山顶附*的区域)。当你探索越来越多具有正态分布的变量时,我们会发现它就在我们周围,这就是为什么它如此正常,它的名字非常恰当!!

正态分布的参数

正态分布总是以*均值为中心,曲线的宽度由标准差(SD)决定。

这里有两个正态分布,x 轴是以英寸为单位的身高测量值,y 轴是与特定身高相对应的人数。

  1. 婴儿的分布*均值=20 英寸,标准差=0.6 英寸
  2. 成人的分布*均值为 70 英寸,标准差为 4 英寸

知道正态分布的标准差的重要性在于它遵循一个经验规则(在下一节中详细解释),即大约 95%的测量值落在*均值的+/- 2 标准差之间。

推断:95%的人口落在*均值+/- 2*SD 之间

  1. 95%的婴儿身高在 20 +/- 1.2 英寸之间
  2. 95%的成人身高测量值在 70 +/- 8 英寸之间

正态分布的第一个参数是*均值

*均值是正态分布的中心趋势,它决定了曲线峰值的位置。*均值的变化导致曲线沿 x 轴水*移动。****

正态分布的第二个参数是 SD

标准差是正态分布可变性的度量,它决定了曲线的宽度。SD 的变化会导致曲线变得更紧或更宽,并对曲线的高度产生反比影响。****

曲线越紧(宽度越小)——>高度越高

曲线越展开(宽度越高)——>高度越短

现在,你了解了正态分布曲线的所有基础知识。让我们继续学习与之相关的其他重要信息。

所有正态分布的共同特征

  1. 都是对称(咩!我们现在已经知道了,不是吗?:p)
  2. 均值=中值=众数

解码:*均值(Mean)是将总体/样本分成两半(中值)的值,是样本/总体中最常出现的值(众数)。放轻松,皮斯!

3.经验规则允许我们确定钟形曲线*均值的特定标准偏差范围内的数据百分比。(当然,您必须知道您的发行版的 SD,这样才能工作!)

这将通过一个例子变得更加清楚。请继续阅读!

让我们看一个披萨外卖的例子。假设一家披萨店的*均配送时间为 30 分钟,标准差为 5 分钟。使用经验法则,我们可以确定 68%的交付时间在 25–35 分钟之间(30 +/- 5),95%在 20–40 分钟之间(30 +/- 25),99.7%在 15–45 分钟之间(30 +/-35)。下面的图表用图形说明了这一特性。

看看当我们知道如何将统计学应用于现实生活问题时,事情变得多么直观?继续我的统计学家、数据科学家/工程师、人工智能爱好者或正在阅读这篇文章的好奇的伙伴们吧!这个世界需要更多的你来分析它的怪癖:)

在这篇文章的最后一部分,我们将了解正态分布的一种特殊情况

标准正态分布:正态分布的特例

正如我们在上面看到的,正态分布根据参数值(*均值和标准差)有许多不同的形状。然而,标准正态分布是均值= 0 且标准差= 1 的正态分布的特例。这种分布也称为 Z 分布。

标准正态分布上的值称为标准分数或 Z 分数。标准分数代表特定观察值高于或低于*均值的 SD 数。

例如,标准分数 1.5 表示观察值高于*均值 1.5 个标准差。另一方面,负的分数代表低于*均值的值。*均值的 Z 值为 0。

万岁!!我们已经涵盖了构成正态分布基础的所有内容。我的下一篇博文是关于中心极限定理的。

** [## 解释清楚:强大的中心极限定理

这实际上是最重要的统计定理之一,请继续阅读以获得简单的解释!

towardsdatascience.com](/clearly-explained-the-mighty-central-limit-theorem-b8152b94258)

请留意这一空间中对统计概念的更简单的解释。

待在家里!注意安全!**

解释清楚:皮尔逊 V/S 斯皮尔曼相关系数

原文:https://towardsdatascience.com/clearly-explained-pearson-v-s-spearman-correlation-coefficient-ada2f473b8?source=collection_archive---------0-----------------------

数据科学领域的每个人都应该知道的基础知识

在这篇文章中了解更多何时使用哪个系数

照片由晨酿在 Unsplash 拍摄

我最*遇到一个场景,我自学了皮尔逊和斯皮尔曼相关系数之间的差异。我觉得这是媒体上数据科学领域的许多人可以利用的一条信息。我将彻底解释这两者之间的区别,以及每一种都适用的确切场景。请继续阅读!

这篇文章的内容:

  1. 相关性定义
  2. 皮尔逊和斯皮尔曼相关系数的比较分析

相关性的定义

相关性是两个变量线性相关的程度。这是双变量数据分析中的一个重要步骤。从最广泛的意义上来说,相关性实际上是二元数据中两个随机变量之间的任何统计关系,不管是不是因果关系。

要记住的一条重要规则是,相关性并不意味着因果关系

让我们通过两个例子来理解它实际上意味着什么。

  1. 在夏季的几个月里,冰淇淋的消费量增加了。冰淇淋单位的销售有很强的相关性。在这个特殊的例子中,我们看到了因果关系,因为极端的夏天确实推动了冰淇淋的销售。
  2. 冰淇淋销售也与鲨鱼袭击有很大的关联。现在我们可以很清楚地看到,鲨鱼袭击绝对不是由冰淇淋引起的。所以,这里没有因果关系。

因此,我们可以理解相关性并不总是意味着因果关系!

相关系数是多少?

相关系数是两个变量相对运动之间关系强度的统计度量。值的范围在-1.0 和 1.0 之间。相关性-1.0 表示完全负相关,而相关性 1.0 表示完全正相关。0.0 的相关性表明两个变量的移动之间没有线性关系。

2 个重要的相关系数——皮尔森和斯皮尔曼

1.皮尔逊相关系数

维基百科定义:在统计学中,皮尔逊相关系数(Pearson correlation coefficient)也称为皮尔逊的 r 或二元相关(bivariate correlation),是衡量两个变量 XY 之间的线性相关的统计量。它的值介于+1 和-1 之间。值+1 表示完全正线性相关,0 表示没有线性相关,1 表示完全负线性相关。

要记住的重要推论:皮尔逊相关只能评估两个连续变量之间的线性关系(只有当一个变量的变化与另一个变量的成比例变化相关联时,关系才是线性的)

示例用例:我们可以使用皮尔逊相关来评估年龄增长是否会导致血压升高。

以下是皮尔逊相关系数(r)如何随两个变量之间的关系强度和方向变化的示例。请注意,当无法建立线性关系时(参见第三列中的图表),皮尔逊系数的值为零。

来源:维基百科

2.斯皮尔曼相关系数

维基百科定义:在统计学中,以 Charles Spearman 命名的 Spearman 秩相关系数或 Spearman 的ρ,是秩相关(两个变量的秩之间的统计相关性)的一种非参数度量。它评估使用单调函数描述两个变量之间的关系有多好。

需要记住的重要推论:Spearman 相关性可以评估两个变量之间的单调关系——连续变量或顺序变量,它基于每个变量的排序值,而不是原始数据。

什么是单调关系?

单调关系是执行下列操作之一的关系:

(1)随着一个变量的值增加,另一个变量的值也增加,或者,

(2)随着一个变量的值增加,另一个变量的值减少。

但是,不完全是恒定的速率,而在线性关系中,增加/减少的速率是恒定的。

来源:维基百科

示例用例:员工完成测试练习的顺序是否与他们受雇的月数有关,或者一个人的智商与每周看电视的时间有关

皮尔逊系数和斯皮尔曼系数的比较

  1. 两个相关系数之间的根本区别在于,皮尔逊系数适用于两个变量之间的线性关系,而斯皮尔曼系数也适用于单调关系。

2.另一个区别是,皮尔逊处理变量的原始数据值,而斯皮尔曼处理排序变量。

现在,如果我们觉得散点图直观地表明“可能是单调的,可能是线性的”关系,我们最好的选择是应用 Spearman 而不是 Pearson。即使数据是完全线性的,换成 Spearman 也没有坏处。但是,如果它不完全是线性的,我们使用皮尔逊系数,那么我们就会错过斯皮尔曼可以捕捉到的信息。

让我们来看看我在这个网站上发现的一些信息丰富的例子:

  1. 皮尔逊= +1,斯皮尔曼= +1

2.Pearson = +0.851,Spearman = +1(这是一个单调递增的关系,因此 Spearman 正好是 1)

3.皮尔逊= 0.093,斯皮尔曼= 0.093

4.皮尔逊= 1,斯皮尔曼= 1

5.Pearson = 0.799,Spearman = 1(这是一个单调递减的关系,因此 Spearman 正好为 1)

注:这两个系数都不能捕捉任何其他类型的非线性关系。因此,如果散点图表明不能用线性或单调函数表达的关系,那么这两个系数都不能用来确定变量之间关系的强度。

为了进一步阅读,您可能想看看下面两篇解释等级排序和 PSI,CSI 用于模型验证和监控的文章。

[## 为什么等级排序如此重要?

继续阅读,了解更多关于分类模型中等级排序的概念和用法

juhiramzai.medium.com](https://juhiramzai.medium.com/why-rank-ordering-is-so-important-bc1ff8a48764) [## PSI 和 CSI:前 2 个模型监控指标

种群稳定性指数和特征稳定性指数

towardsdatascience.com](/psi-and-csi-top-2-model-monitoring-metrics-924a2540bed8)

关注本空间了解更多关于数据科学、机器学习和统计学的内容。

快乐学习:)

解释清楚:强大的中心极限定理

原文:https://towardsdatascience.com/clearly-explained-the-mighty-central-limit-theorem-b8152b94258?source=collection_archive---------39-----------------------

为什么每个 ML 工程师/数据科学家都必须了解这一点

这实际上是最重要的统计定理之一,请继续阅读以获得简单的解释!

资料来源:Giphy

中心极限定理(CLT)是统计学中的基本定理之一,好消息是,这是一个非常简单的概念,当你进一步阅读时就会明白。在阅读任何进一步的正态分布之前,您必须理解一个先决概念,以及它们为什么如此重要!阅读我关于正态分布的文章,它是中心极限定理的完美前传。

[## 解释清楚:正态分布和中心极限定理

这些概念有什么正常或不正常的地方?

towardsdatascience.com](/clearly-explained-normal-distributions-and-the-central-limit-theorem-8d7cc5a6052f)

继续,标准定义:

中心极限定理(CLT)指出,给定足够大的样本量,无论变量在总体中的分布如何,变量均值的抽样分布都将接*正态分布。

解读冗长的^定义

让我们直接看一些例子,当我们从另一边出来时,CLT 对我们来说已经非常清楚了。

示例#1

让我们来看一个均匀分布(它从 0 到 1,之所以称为均匀分布,是因为在 0 和 1 之间选择值的概率相等,因此它的概率密度函数(PDF)是直的水*黑线)。现在,假设我们从该分布(绿点)中随机抽取 20 个样本,并计算这些样本的*均值,我们最终得到一个值,在本例中用黑色虚线表示为 0.5。让我们继续在直方图上绘制这个*均值。因为这个直方图到目前为止只有一个*均值,所以它并没有告诉我们什么(下图)。继续从相同的分布中提取更多的随机样本,计算各自的*均值,并再次在直方图上绘制这些*均值,我们开始得到一个有趣的输出(下图)

现在,随着我们继续从均匀分布中取出越来越多的随机样本,并继续在直方图上绘制样本均值,我们会得到以下结果,结果表明这是一个正态分布(见右曲线)。

推论:我们从均匀的数据分布开始,但是从中抽取的样本的*均值导致了正态分布。

实施例 2

在第二个示例中,让我们遵循与示例#1 相同的步骤,唯一的不同是我们这次将从指数分布中抽取样本。

我们将再次随机抽取 20 个样本,计算样本的*均值,并绘制在直方图上。从这个指数数据分布快进到大约 100 个样本,直方图开始看起来像下面这样,它的模式现在对我们来说并不奇怪。是的,你猜对了,样本的*均值是正态分布!

推论:我们从指数数据分布开始,但是从中抽取的样本的*均值导致了正态分布。

因此,在这一点上,CLT 的暗示变得非常直观。

中心极限定理意味着即使数据分布不是正态的,从中抽取的样本的均值分布也是正态的。

知道样本均值总是*正态分布的实际含义是什么?

在分析领域,我们每天都会遇到各种各样的数据,我们并不总是知道源数据的分布,但是,因为我们知道 CLT,所以我们甚至不需要关心这个事实,因为*均值总是正态分布的,我们完全没有必要担心源数据的分布。

(注——为了让 CLT 能够工作,我们必须能够计算出你的样本的*均值。有一种分布称为柯西分布,它没有样本均值,因此 CLT 不适用于它,但除了柯西,我没有遇到任何其他分布是 CLT 的例外,所以在任何其他分布的情况下,你可以肯定中心极限定理非常适用于它。)*

下面是知道均值正态分布的实际含义:

  1. 我们可以使用均值的正态分布来分配置信区间
  2. 我们可以执行 T 检验(即,如果两个样本的*均值之间存在差异)
  3. 我们可以执行 ANOVA 测试(即 3 个或更多样本的*均值之间是否存在差异)
  4. 我们可以进行任何使用样本均值的统计检验。

这就把我们带到了这篇文章的结尾!这篇文章涵盖了我们在处理数据和样本时应该知道的中心极限定理的全部理论。

观看这个空间的更多关于分析、统计和机器学习的帖子!!

快乐学习!😃

前两种决策树的简单指南:CHAID 和卡特

原文:https://towardsdatascience.com/clearly-explained-top-2-types-of-decision-trees-chaid-cart-8695e441e73e?source=collection_archive---------7-----------------------

机器学习基础

理解这些算法的本质

亚当·博斯在 Unsplash 上的照片

基于树的学习算法被认为是最好的和最常用的监督学习方法之一,因为它们使预测模型具有高准确性、稳定性和易于解释。与线性模型不同,它们很好地映射了非线性关系。他们善于解决手边的任何类型的问题(分类或回归)。

CHAID 和卡特是两种最古老的决策树。它们也是当今行业中最常用的决策树类型,因为它们非常容易理解,但彼此又有很大不同。在本帖中,我们将了解理解这两种决策树所需的所有基本信息。

让我们先深入了解一下 CHAID 决策树算法。

CHAID 卡方自动交互检测

这个算法最初是由 Kass 在 1980 年提出的。从这个算法的名字可以看出,它是基于卡方统计的。卡方检验产生一个介于 0 和 1 之间的概率值。卡方值越接* 0,表示正在比较的两个类别之间存在显著差异。类似地,更接* 1 的值表示这两个类别之间没有任何显著差异。

CHAID 算法中使用的变量类型:

待预测的变量,即因变量:连续变量还是分类变量

独立变量:仅限分类变量(可以超过 2 个类别)

因此,如果有连续的预测变量,那么我们需要将它们转换成分类变量,然后才能提供给 CHAID 算法。

用于确定下一个最佳分割的统计测试:

连续因变量:f 检验(回归问题)

分类因变量:卡方(分类问题)

让我们先了解 Bonferroni 调整/校正,然后再继续。

Bonferroni 调整/校正

在统计学中, Bonferroni 校正是用来抵消多重比较问题的几种方法之一。

这种调整解决了这样一个事实,即您执行的测试越多,类型 1 错误(假阳性)的风险就越大,也就是说,看起来好像您偶然发现了一些重要的东西,但实际上您没有。

上图表明,如果我们取α值为 0.05 并进行 1 次测试,那么我们有 95%的置信度,表明我们有 95%的概率能够避免 1 型错误。现在,请注意,当我们开始将测试数量增加到 100 时,我们只剩下 0。^%概率能够避免第一类错误。为了抵消这种影响,我们计算了调整后的 alpha 值和测试次数。现在,只要我们可以使用这个新的调整后的α值,理论上我们就可以处于安全区。

在 100 次测试中观察调整后的 alpha 值,它变得如此之低,以至于树将停止生长,因为它将无法找到任何可以达到超显著水*的变量。

一般来说,所有开发决策树的软件都会给建模者一个关闭它的选项。Bonferroni 调节设置应保持开启。如果我们在树没有生长的情况下关闭它,并且我们想通过关闭 Bonferroni 来进行实验,那么考虑使 alpha 值低于通常的 0.05,以小心我们上面讨论的类型 1 错误的可能性。

另外,一旦建模阶段完成,一定要验证你的树。

CHAID 算法的幕后过程

  1. 逐一循环遍历所有预测器,以确定相对于因变量而言差异最小的一对(预测器)类别。将为分类问题计算卡方统计(因变量也是分类变量),为回归问题计算 f 检验(因变量是连续变量)。
  2. 如果给定预测器类别对的相应测试不具有由α-合并值定义的统计显著性,则它将合并相应的预测器类别并重复第一步(即,找到下一对类别,其现在可以包括先前合并的类别)。
  3. 如果相应预测器类别对的统计显著性显著=小于相应的 alpha-to-merge 值,则在启用设置的情况下,它将为相应预测器的类别集计算 Bonferroni 调整后的 p 值。
  4. 这一步是关于选择分裂变量。具有最小调整 p 值的预测变量,即产生最显著分裂的预测变量,将被考虑用于树中的下一次分裂。如果任何预测值的最小(Bonferroni)调整 p 值大于某个α-to-split值,则不会执行进一步的分裂,相应的节点将成为终端节点。
  5. 这个过程将迭代地继续,直到不能执行进一步的拆分(给定α-合并和α-拆分值)。

CHAID 如何处理不同类型的变量?

标称变量:根据上述第 2 点自动分组数据

序数变量:根据上述第 2 点自动分组数据

连续变量:在执行#2 之前转换成段/十分位

CHAID 算法的本质是创建宽树。

我们现在可以开始理解 CART 决策树算法

CART-分类和回归树

这个算法是由 Breiman 等人在 1984 年向世界介绍的。

这种算法基于“纯度”和“*衡”这两大支柱。如上所述,纯度由基尼系数表示,而*衡由决策树的左和右分区中的相同人口分布表示。一个最佳的车树不会在任何一根柱子上摇摆。

  1. 在 CART 树上强制“*衡”会导致许多不纯的叶节点,这违背了使用决策树进行决策的目的。
  2. 在一个 CART 树上强制“纯洁”会给我们一个片段中很少的人口分布,这又一次违背了健康决策树的目的。

CART 算法中使用的变量类型:

1.回归树

待预测变量,即因变量:连续

自变量:连续或分类(二元)

2.分类树

要预测的变量,即因变量:分类(二元)

自变量:连续或分类(二元)

理解购物车算法的要点:

  • CART 本质上是计算昂贵且缓慢的。
  • CART 总是产生二进制拆分,不像 CHAID,如果需要,它可以产生两个以上的拆分。
  • 回归树基于使用最小*方偏差(LSD)对节点杂质的评估,这意味着节点内的方差。
  • 分类树基于基尼*等指数。基尼系数为 0 表示完全*等,系数为 1 表示最大不*等。
  • 在连续变量的情况下,与 CHAID 相比,CART 能够测试更多的分界点,因为 CHAID 创建了连续变量的分段/十分位数,这些分段/十分位数通常在本质上是任意的,并且有时可以隐藏连续变量中的真实模式。
  • 对于高达 5–15%的缺失数据,CART 比 CHAID 表现得更好,因为缺失值在 CART 中被视为一个单独的组,只有当两个类别之间实际上有很高的相似性时,它才会与任何其他类别组合,这与 CHAID 将缺失值与最*的组组合不同。
  • 卡特形成了一棵比 CHAID 小得多的树
  • 卡特有生产高而瘦的树的天性,不像 CHAID 有生产宽的树的天性。

CART 算法的幕后过程

  1. 选择基于变量值的规则以获得最佳分割,从而根据因变量区分观察值
  2. 一旦选择了一个规则并将一个节点一分为二,相同的过程被应用于每个“子”节点(即,它是一个递归过程)
  3. 当购物车检测到无法继续获利,或符合一些预设的停止规则时,分割停止。(或者,尽可能分割数据,然后在稍后修剪树)。

CHAID 和卡特的停车规则

  1. 父/子样本量
  2. 树的深度: CHAID 一般应保持在 5-6 的深度,而根据树木的性质,应允许大车行走到 7-8。CHAID 可以在树深度 5 捕获信息,因为它将是一个宽树,而 CART 允许一些额外的树深度级别,因为它产生高而瘦的树,需要更多的深度来正确捕获问题的本质。
  3. CHAID 的置信度:分割/合并的默认 alpha 值设置为 0.05。为了防止过度拟合,我们可以将其降低到 0.01,以将置信区间从 95%增加到 99%,在这种情况下,树将增长得更少。类似地,如果我们感觉树没有生长,那么我们可以将阈值增加到 0.1,创建 90%的置信水*,因此,树将生长得更多。这就是你如何试验所有的停止规则,直到你对你的树满意为止。每次实验结束时都要进行验证,以了解你的树在看不见的数据上表现如何。
  4. 【CART 的 Gini 指数:这有助于我们捕捉某个节点的杂质的某种程度的变化。假设基尼值的默认变化保持在 0.0001。如果我们给它加一个零,使它为 0.00001,那么就更容易得到一个更大的树,因为变化阈值已经降低了。另一方面,如果我们去掉一个零,使其为 0.001,那么树将不会生长得那么多,因为很难达到杂质变化的更高阈值。

我不会以这两棵树的比较来结束我的发言。有很多利弊列表可以用来比较卡特和 CHAID,但我相信每一个都适合不同的目的。你必须研究和处理你的数据,用各种各样的方法进行大量的实验,才能真正掌握什么对你试图解决的特定业务问题有好处。

我宁愿在这里列出决策树的常见优点和缺点作为结论部分:

决策树的利与弊

来源:https://rpubs.com/chidungkt/451329

优点:

  1. 通俗易懂: 决策树输出即使对于非分析背景的人来说也是非常通俗易懂的。阅读和解释它们不需要任何统计知识。它的图形表示非常直观,用户可以很容易地联系他们的假设。
  2. 在数据探索中有用: 决策树是识别最重要变量以及两个或更多变量之间关系的最快方法之一。在决策树的帮助下,我们可以创建新的变量/特征,这些变量/特征具有更好的预测目标变量的能力。也可以用在数据探索阶段。
  3. 需要较少的数据清理: 与其他一些建模技术相比,需要较少的数据清理。它在一定程度上不受离群值和缺失值的影响。
  4. 数据类型不是约束: 它可以处理数值和分类变量。
  5. 非参数方法: 决策树被认为是非参数方法。这意味着决策树对空间分布和分类器结构没有任何假设。

缺点:

  1. 过拟合: 过拟合是决策树模型最实际的困难之一。这个问题可以通过对模型参数设置约束和剪枝来解决。
  2. 不适合连续变量: 在处理连续数值型变量时,决策树在对不同类别的变量进行分类时会丢失信息。

在我的下一篇文章中,我将把 QUEST 和 C5 算法作为本指南的一个扩展,来理解决策树算法中实际发生的事情。

请继续关注本空间,了解更多关于数据科学、机器学习和统计学的信息!

快乐学习!!😃

清楚地解释:什么是偏差-方差权衡,过度拟合和欠拟合

原文:https://towardsdatascience.com/clearly-explained-what-is-bias-variance-tradeoff-overfitting-underfitting-7896884dcf2d?source=collection_archive---------41-----------------------

对于数据科学领域的任何人来说,这些概念都是非常重要的!

用例子解释,它将值得你花时间,继续读下去:)

约翰·莫塞斯·鲍恩在 Unsplash 上拍摄的照片

在这篇文章中,我们将以一种即使新手也能毫不费力地掌握的方式来理解这个建模和统计世界中由来已久的重要敲门砖。

每当我们建立一个预测模型时,即使在所有的调整和处理之后,我们的预测通常也是不完美的:在预测值和实际值之间会有一些非零的差异。这个差值被称为预测误差。我们可以把预测误差分解成两个分量:可约和不可约误差。

让我们用现实生活中的类比来理解它。火车司机是否能够成功地将火车引导到期望的目的地,这取决于两个因素,他自己(可减少的错误,如果他是警觉的和意识到的,他将不会犯任何无意的错误)和外部因素(不可减少的错误,如果一只迷路的动物冒险在铁轨上,如果另一个来自相反方向的火车司机失去控制,如果一个自杀的人扑到他的火车前面,等等)。)

看看下面的等式来总结这一切:

现在,我们将了解这个等式的两个组成部分(偏差误差和方差误差),然后再讨论困扰每个模型的两者之间的权衡情况。

考虑一个人 X 先生,他是一名大一新生,是一名有抱负的数据科学家,目前正在与各公司面试,以获得他在这个行业的第一个突破。有一天,他去参加一个面试,回家后感到非常紧张和沮丧,因为面试并不顺利。他的母亲问了他所有的事情,他说面试官问了他太高级的问题,对他的每一点都进行了批判性的判断,没有任何削弱他信心的言辞。然后,他的母亲安慰他说:“今天不是你的好日子,明天会更好的”。

现在,从这个故事中我们可以看出,母亲并没有要求儿子比以前更积极地准备,以增加他成功的机会。原因是她对儿子总是有情感上的偏见,并且总是试图淡化他的失败。如果你试图预测她对一个来找她寻求安慰的人的反应,而不考虑这个人是不是她的儿子(直系亲属),你的预测就会有偏差。现在,以下几点将帮助您理解偏差如何影响统计模型以及可以依赖的技术定义:

  • 偏差的真实例子 -我将在这里引用一个股票市场的例子,假设我建立了一个相当简单的模型,该模型仅基于 3-4 个预测因素来预测股票的股价。该模型预测,2019 年 2 月 3 日,每股股价将为 4 美元,但当天的实际价格为 3.4 美元。因此,总误差将是两个数字之间的差值,即 0.6 美元,偏差分量在这里起主要作用,因为我们只取了几个预测值。

偏差误差是预测数据点和实际数据点之间的差异,这是由于我们的模型过于简化造成的。

  • 具有高偏差的模型过于简单,并且具有低数量的预测器。它缺少一些其他重要的预测因素,因此无法捕捉数据的潜在模式。它忽略了训练数据集中的特征与预期输出之间的关系。它很少关注训练数据,并且过于简化模型。这导致训练和测试数据中的高误差。

现在,让我们再一次回到这个故事。我们看到面试官问了一个大一新生的高难度问题,也就是说,他让看似简单的情况变得复杂,因为不必要的复杂问题让 x 先生目瞪口呆。让我们再来看两个事实-

  • X 先生只准备了初级问题,因此他不能回答高级问题。
  • 面试官只面试过需要 5-6 年工作经验的职位,因此,他从来没有面试过一个新人。

在这种情况下,面试官在自己的舒适区之外会感到不舒服。X 先生对他来说是一个看不见的输入,他不知道如何处理,所以他问了太复杂的问题。这是理解方差的最佳案例。当暴露在他的舒适区(训练数据)的情况下,面试官表现出色,但一个新的经验(测试数据)让他偏离了轨道。现在,您已经准备好理解方差的技术定义及其对模型的影响,如下所述:

  • 任何具有大量预测值的模型最终都将是一个非常复杂的模型,它将为已经看到的训练数据提供非常准确的预测,但是这种复杂性使得将该模型推广到看不见的数据非常困难,即高方差模型。因此,这个模型在测试数据上表现很差。

模型的高方差误差意味着它对小波动高度敏感。这个模型在它的舒适区之外挣扎(训练数据)。方差也有助于我们理解数据的分布。

现在我们必须了解与偏差和方差相关的两个更重要的术语- 过拟合和欠拟合 。我将再次使用现实生活中的类比。对于这个例子,我已经参考了机器学习@Berkeley 的博客。

当机器学习算法试图预测事物时,有一个非常微妙的*衡动作。

一方面,我们希望我们的算法能够非常紧密地模拟训练数据,否则,我们会错过相关的特征和有趣的趋势。

然而,另一方面,我们不希望我们的模型过于接*,并冒着过度解读每个异常值和不规则性的风险。

福岛核电站灾难

福岛核电站灾难是过度适应的一个毁灭性例子。设计发电厂时,工程师们必须确定地震发生的频率。他们使用了一个众所周知的古腾堡-里克特定律,该定律给出了一种从非常弱的地震发生的频率预测非常强的地震的概率的方法。这很有用,因为弱地震——弱到你甚至感觉不到的地震——几乎随时都在发生,并由地质学家记录下来,所以工程师们有相当大的数据集要处理。也许这条定律最重要的结果是,地震的震级和发生概率的对数之间的关系是线性的 T21。

核电站的工程师使用过去 400 年的地震数据来训练回归模型。他们的预测大概是这样的:

来源:布莱恩·斯泰西,福岛:预测模型的失败

菱形代表实际数据,而细线代表工程师的回归。注意他们的模型是如何非常紧密地拥抱数据点的。事实上,他们的模型在 7.3 级附*出现了一个拐点——显然不是线性的。

在机器学习的行话中,我们把这种过拟合 。顾名思义,过度拟合是指我们训练的预测模型过于紧密地“拥抱”训练数据。在这种情况下,工程师们知道这种关系应该是一条直线,但他们使用了一个比他们需要的更复杂的模型。

如果工程师使用了正确的线性模型,他们的结果应该是这样的:

来源:布莱恩·斯泰西,福岛:预测模型的失败

注意这次没有扭结,所以右边的线没有那么陡。

这两款的区别?过度拟合的模型预测大约每 13,000 年发生一次至少 9 级的地震,而正确的模型预测大约每 300 年发生一次至少 9 级的地震。正因为如此,福岛核电站的建造只能抵御 8.6 级的地震。2011 年摧毁该工厂的地震震级为 9 级(约为 8.6 级地震的 2.5 倍)。

来源:布莱恩·斯泰西,福岛:预测模型的失败

过拟合实际上有一个双重问题,叫做欠拟合。在我们减少过度拟合的尝试中,我们实际上可能开始走向另一个极端,我们的模型可能开始忽略数据集的重要特征。当我们选择一个不够复杂的模型来捕捉这些重要特征时,就会发生这种情况,例如在需要二次模型时使用线性模型

现在,从这个例子中,我们必须记住的重要信息如下:

  • 不合身
  1. 当一个模型由于参数数量少而不能恰当地捕捉训练数据的本质时,这种现象称为欠拟合。
  2. 高偏置误差、低方差误差
  3. 当我们只有很少的数据来建立一个精确的模型时,这种情况也会发生。
  4. 如果我们试图用非线性数据建立线性模型,也会出现这种情况。
  5. 示例-线性回归和逻辑回归模型可能会面临这种情况
  • 过拟合
  1. 当使用如此多的预测器来构建模型时,它会捕捉噪声以及潜在的模式,然后它会试图将模型拟合得过于接*训练数据,从而留下非常小的概化范围。这种现象被称为过度拟合。
  2. 低偏置误差、高方差误差
  3. 这是一个简单现实的复杂表现
  4. 示例-决策树容易过度拟合

偏差-方差权衡

我们必须避免过度拟合,因为它给了我们训练数据中的噪声元素太多的预测能力。但是,在我们试图减少过度拟合时,我们也可能开始拟合不足,忽略训练数据中的重要特征。那么我们如何*衡这两者呢?

在机器学习领域,这个极其重要的问题被称为偏差-方差困境。拥有最先进的算法、最快的计算机和最新的 GPU 是完全可能的,但如果你的模型对训练数据过度拟合或拟合不足,无论你投入多少资金或技术,它的预测能力都将是可怕的。

偏差-方差困境这个名称来自统计学中的两个术语:偏差,对应于欠拟合,以及方差,对应于过拟合,如果你一直关注到这里,你一定已经完全理解了:)

解释困境

那么,为什么在偏差和方差之间会有一个权衡呢?为什么我们不能两全其美,拥有一个既有低偏差又有低方差的模型?事实证明,偏差和方差实际上是一个因素的副作用:我们模型的复杂性。

举例- 对于高偏置的情况,我们有一个非常简单的模型。在我们下面的例子中,使用了一个线性模型,这可能是最简单的模型。对于高方差的情况,我们使用的模型非常复杂(想想弯弯曲曲)。

来源:链接:高偏置-欠匹配模型

来源:链接:高方差-过拟合模型

这种权衡有点像原子中的电子和质子*衡,两者同等重要,它们的和谐对整个宇宙都很重要。类似地,偏差和方差是在模型建立期间要最小化的两种误差。但是,要同时最小化这两者是一个挑战,因为如下图所示:

  • 任何低复杂度的模型——由于高偏差和低方差,容易出现欠拟合
  • 任何高度复杂的模型(决策树)-由于低偏差和高方差,将易于过度拟合

我们能做的最好的事情就是试着在光谱中间的某个地方,紫色指针的位置。

来源:链接

最后,看看下面的图表,你会发现它在偏差-方差权衡的上下文中到处都在使用,我们已经详细介绍了所有概念,现在你可以轻松理解了。

来源:链接

感谢您的阅读:)我很乐意听到您的反馈/建议!

【参考:】【https://ml.berkeley.edu/blog/2017/07/13/tutorial-4/】

你可以看看我下面的其他帖子:

[## 解释清楚:4 种机器学习算法

定义、目的、流行的算法和用例——都有解释

towardsdatascience.com](/clearly-explained-4-types-of-machine-learning-algorithms-71304380c59a) [## 清楚地解释:机器学习如何不同于统计建模

它们彼此非常不同,所有的数据科学家都必须理解为什么和如何!

towardsdatascience.com](/clearly-explained-how-machine-learning-differs-from-statistical-modeling-967f2c5a9cfd) [## 清楚地解释:机器学习与数据挖掘有何不同

定义、混淆、区别——都有解释

towardsdatascience.com](/clearly-explained-how-machine-learning-is-different-from-data-mining-4ee0e0c91bd4)

请关注这个空间,了解更多关于数据科学、机器学习和统计学的内容!

快乐学习:)

清楚地解释:什么,为什么和如何特征缩放-规范化和标准化

原文:https://towardsdatascience.com/clearly-explained-what-why-and-how-of-feature-scaling-normalization-standardization-e9207042d971?source=collection_archive---------14-----------------------

特征缩放的重要性以及如何应用它。我的机器学习模型会从规范化中受益吗?

杰克·卡特在 Unsplash 上拍照

为什么要正常化?

你可能会对这篇文章封面图片的选择感到惊讶,但这就是我们理解正常化的方式!当我们的数据具有各种不同测量尺度的特征时,这个强大的概念会帮助我们,因此当我们试图从这些数据中获得洞察力或试图根据这些数据拟合模型时,我们会陷入困境。

就像我们不能在一个共同的尺度上比较上图所示的不同水果一样,我们也不能有效地处理太多尺度的数据。

例如:见下图,观察工资、工作经验和级别。由于属性 Salary 的比例范围较大,因此在定型模型时,它可以优先于其他两个属性,而不管它在预测因变量时实际上是否具有更大的权重。

因此,在数据挖掘和模型开发(统计或机器学习)的数据预处理阶段,对所有变量进行标准化是一个很好的做法,如果它们处于不同的范围,那么将它们降低到一个相似的尺度

并非每个数据集都需要归一化,您必须筛选它,并确保您的数据需要它,然后才继续将这一步骤纳入您的程序。此外,如果您不确定数据分布实际上是否是高斯/正态/钟形曲线,您应该应用归一化。归一化将有助于减少非高斯属性对模型的影响。

什么是正常化?

我们将在这里讨论两种情况:

1.您的数据不符合正态/高斯分布(在有疑问的情况下也喜欢这个)

在这种情况下,数据规范化是将一个或多个属性的范围重新调整为 0 到 1 的过程。这意味着每个属性的最大值是 1,最小值是 0。

这也称为最小-最大缩放。

最小-最大缩放公式—来源:维基百科

来源:维基百科

2.您的数据遵循高斯分布

在这种情况下,可以通过下面描述的公式进行归一化,其中μ是*均值,σ是样本/总体的标准偏差。

当我们使用下面给出的标准分数进行标准化时,它通常也被称为标准化或 Z 分数。

标准化公式/Z 分数—来源:维基百科

来源:维基百科

关于 Z-Score 的更多信息

z 分数告诉我们你的分数离*均值有多少标准差。

比如说—

  • z 得分为 1.5,则意味着它比*均值高 1.5 个标准差
  • z 分数为-0.8 表示我们的值比*均值低 0.8 个标准偏差。**

如上所述,z 分数告诉我们分数在正态分布曲线上的位置。z 值为表示该值正好是*均值/*均值,而+3 表示该值远高于*均值(可能是异常值)

如果你参考我关于正态分布的文章,你会很快明白 Z-score 是把我们的分布转换成均值为 0、标准差为 1 的标准正态分布。

Z 分数的解释

让我们快速了解如何根据 AUC(曲线下面积)来解释 Z 得分的值。

根据经验法则,在上面链接的关于正态分布的文章中有详细的讨论,在这篇文章的结尾也有陈述,陈述如下:

  • 68%的数据位于+1SD 和-1SD 之间
  • 99.5%的数据位于+2SD 和-2SD 之间
  • 99.7%的数据位于+3SD 和-3SD 之间

现在,如果我们想查看一个定制的范围,并计算该细分涵盖了多少数据,那么 Z 分数就可以帮我们解决问题。让我们看看怎么做。

例如,我们想知道左侧负极端和-1SD 之间覆盖了多少百分比的数据(数据点出现的概率),我们必须参考下面链接的 Z 得分表:

Z 分数表

现在,我们必须寻找值-1.00,我们可以从下面的快照中看到,状态 15.8%是我们问题的答案。

类似地,如果我们一直在寻找-1.25,我们将得到值 10.56%(Z 列中的-1.2 和跨列匹配 0.05 得到-1.25)**

来源:链接

常见的 Z 得分值及其来自 Z 得分表的结果,表明在负极端和 Z 得分点之间覆盖了多少,即 Z 得分点左侧的区域:

我们也可以使用这些值来计算自定义范围之间的值,例如:如果我们希望 AUC 在-3 和-2.5 Z 值之间,它将是(0.62–1.13)% = 0.49% ~ 0.5%。因此,当涉及到没有直接的 Z 得分值要解释的问题时,这非常方便。

现实生活解释示例

假设我们有一个样本的智商得分数据,我们已经使用 Z 得分进行了标准化。现在来看一些事情,如果一个人的智商 Z 值是 2,我们看到+2 对应于 Z 值表上的 97.72%,这意味着他/她的智商比 97.72%的人高,或者他/她的智商只比 2.28%的人低,这意味着你挑选的人真的很聪明!!

这可以适用于几乎每一个用例(体重,身高,工资,免疫水*,等等!)

如果规范化和标准化之间出现混淆

如果您有一个用例,在这个用例中,您不容易决定哪一个适合您的模型,那么您应该运行两次迭代,一次使用归一化(最小-最大缩放),另一次使用标准化(Z 分数),然后绘制曲线,或者使用箱线图可视化来比较哪种技术对您来说性能更好,或者更好,使您的模型适合这两个版本,并使用模型验证指标进行判断。

我们应该在使用机器学习算法的同时应用归一化吗?

与普遍认为 ML 算法不需要规范化相反,您应该首先仔细看看您的算法使用的技术,以做出有利于您正在开发的模型的合理决策。

如果您正在使用决策树,或者任何基于树的算法,那么您可以不进行归一化,因为树的基本概念是围绕着每次基于单个特征在节点上做出决策,因此不同特征的尺度差异不会影响基于树的算法。

然而,如果您使用线性回归、逻辑回归、神经网络、SVM、K-NN、K-Means 或任何其他基于距离的算法或基于梯度下降的算法,那么所有这些算法都对您的要素的比例范围敏感,应用归一化将提高这些 ML 算法的准确性。

这就是关于功能扩展的全部内容:)

快乐学习,快乐成长:)

我在这篇文章中提到的关于正态分布的文章:

* [## 解释清楚:正态分布和中心极限定理

这些概念有什么正常或不正常的地方?

towardsdatascience.com](/clearly-explained-normal-distributions-and-the-central-limit-theorem-8d7cc5a6052f)

请关注这个空间,了解更多关于机器学习、数据分析和统计的信息!*

气候变化统计分析的基础

原文:https://towardsdatascience.com/climate-change-analysis-series-volume-1-48708a938fcc?source=collection_archive---------26-----------------------

量化全球变暖的环境和经济影响的基础

林丽安在 Unsplash 上的照片

问问题:当讨论气候变化时,你从哪里开始呢?当海水上升时,你如何量化密西西比三角洲的土壤侵蚀?由于全球变暖和滥伐森林的综合影响,亚马逊雨林的生物多样性丧失怎么办?

答:从最底层开始,一步步往上爬。收集和探索气候数据。揭示趋势。预测未来的变化。地球上的居民越有见识,我们就越有可能采取相应的行动来拯救这个星球,一次拯救一度。

统计学家和数据科学家都使用基本的分析概念从他们的数据中做出可信的推断。本文将使用来自国家气候数据中心的 Berkely Earth 全球陆地和海洋温度数据,以及 Python 编程语言来探索将在未来文章中构建的基础元素。

在我们开始之前,在查看全球气候数据时,有一个重要的概念需要理解。通常情况下,全球温度是通过异常而不是绝对温度来确定的。由于一些地区的气象站可能很少,而另一些地区的气象站可能海拔不同,因此将温度与从更小的、更局部化的尺度上的参考值发展的基线进行比较,并且可以计算异常。这种异常将会解释气候的变化。要了解更多关于为什么使用异常的信息,请阅读这里。

船尾鉴于国家海洋和大气管理局船舶雷尼尔与调查发射旁边。 鸣谢:美国国家海洋与大气管理局雷尼尔船的人员。

为了快速方便地浏览数据,我将它缩减为两列:日期(年和月)和以摄氏度为单位的月异常。数据点范围从 1850 年 1 月到 2014 年 12 月,包含 Berkely 的地表气温异常(相对于 1951-1980 年的*均值)和 HadSST 数据集中的海洋温度数据。

首先,我们来看看四个变量:均值、标准差、分位数和方差。

  • *均值 —样本的*均值,通过将所有数据的总和除以数据点的总数来计算。
  • 方差 —离差的度量,描述数据如何围绕其*均值聚集。方差的计算方法是将每个数据点与*均值的距离*方。
  • 标准差——另一种离散度的度量,作为方差的*方根。68%的值落在均值的右侧或左侧一个标准差,95%落在均值的两个标准差,99.7%落在均值的三个标准差。
  • 分位数—有序分布中的点,受最小值和最大值限制。数据按从小到大排序,样本的中间值为中值。中间的每个点都可以描述为一个百分位数,从 1 到 100。

与我们的最小和最大异常相比,方差表明数据非常紧密地聚集在*均值周围。我们可以预计 68%的异常会落在*均值左右 0.345 摄氏度之内。然而,*均值并不总是提供我们数据的最佳描述,因为它受到异常值的严重影响。例如,当输入数据时,气候科学家可能放错了小数点。她没有打 1.3,而是打了 13。如果大部分数据都聚集在-0.2 附*,那么 13 将使*均值更加向右倾斜。

Python 的 Seaborn 库提供了一些图表,使得研究分位数变得非常容易。

盒图(或盒须图)是一个五位数的插图。蓝框从左到右分别表示第 25 个百分点、中间值(第 50 个百分点)和第 75 个百分点。这可以用经验法则来描述,如上面标准偏差的定义中所述。每一端显示最小值和最大值,菱形表示异常值,或距离中值超过三个四分位数的点。

Seaborn 图书馆的另一个伟大的情节是小提琴情节。与箱线图类似,小提琴图显示数据点的分布,但作为潜在分布的核密度估计。

简单来说,核密度估计(KDE)可视化了数据集中变量的分布。在以后的文章中,我们将更深入地研究这个和其他概率函数。

当我们解释这两个图时,我们现在可以开始进一步考虑数据集的分布。正态或高斯分布以数据集的*均值为中心。

这是一个随机正态分布的例子。根据 Investopedia 的说法,“正态分布是一种关于*均值对称的概率分布,表明接**均值的数据比远离*均值的数据出现得更频繁。”

直觉上,随机值在任一极端的概率远小于随机值接**均值的概率。

Seaborn 的 distplot()函数通过绘制直方图、数据集中值在区间或条柱中出现的频率以及核密度估计来可视化数据的分布。

分布图和 kde 也有助于可视化偏度和峰度。

  • 偏斜度 —偏斜度衡量值的对称性或对称性缺失。正偏斜度表示向右偏斜,或者较大值的集中度较高。负偏斜度表示向左偏斜,或者较小值更集中。完全对称的分布的偏斜度为零。
  • 峰度 —峰度衡量分布的“峰值”程度。较高的峰值导致峰度值> 3,较低的峰值导致峰度值< 3,并且正态分布具有等于 3 的峰度。

对于上面的偏度图,蓝色表示负偏度,黄色表示正常,红色表示正偏度。

峰度有三种类型:

  • 中层大气——遵循正态分布,用绿色表示
  • Platykurtic —分布较短,异常值较少,用红色表示
  • Leptokurtic —分布很长,有许多异常值,用蓝色表示

至于我们的气候数据:

当偏斜度为 0.461 时,我们可以有把握地假设存在具有更高值的更高浓度的异常。这表明气温更有可能高于以前的全球*均水*,导致地球变暖。

峰度为-0.191 时,我们可以假设异常值比正态分布少。更少的异常值意味着更少的极端值。

这些基本概念是理解数据的基础,可以应用于许多行业,而不仅仅是气候学。在本系列的其余部分,我们将使用这些数据和更多的数据来揭示我们数据的真相,以保持可信度,并告知我们自己和他人这个星球所面临的问题。

气候变化和世界上种植最广泛的主要作物

原文:https://towardsdatascience.com/climate-change-and-the-most-widely-grown-staple-crop-in-the-world-9c5319159468?source=collection_archive---------40-----------------------

一种预测农业生产和调查农业保险损失率的精算方法

图片由卡斯滕在的 Unsplash 上拍摄

小麦是世界上种植最广泛的主要作物,占全球消耗热量的五分之一,而且需求还在继续增长。它存在于我们每天消费的许多食物中——从面包和谷类到酱油和罐装汤。

因为小麦是我们饮食中不可或缺的一部分,所以了解它的产量在不久的将来会受到怎样的影响是很重要的。所展示的实验提供了与小麦种植区相关的农业保险公司所遭受的损失和收益的差异,我们分析了损失原因气候变化和小麦产量之间的关系,以便为农民、保险公司和消费者提供更多信息。

我们在调查什么?

在保险行业, 损失率 代表亏损与赚取的保费(收益)的比率。农作物保险政策倾向于在地区和州的水*上解决生产问题,也许是基于相似地理位置的农场产出相似数量的产量的假设。我们建议,在县一级的详细分析可以为农民和保险公司提供更好的收益和更具体的信息。为了评估在保险单开发中鼓励更大的粒度是否有益,我们问:

地理位置相*的县的损失率分布会有显著差异吗?

华盛顿州是美国小麦的主要产地。在这里,我们评估华盛顿各县是否表现出不同的损失率分布。为此,我们随机选择了地理位置相*的两个县——斯卡吉特和惠特曼。在这些县,我们将使用来自美国农业部农业报告生成器和快速统计门户的作物和财务数据。

它们的损失率分布非常不同!

结果相当令人惊讶。从上面的方框图中可以看出,惠特曼和斯卡吉特的分布差别很大。总的来说,斯卡吉特比惠特曼分布更广,方差更大。这些县有明显不同的四分位值、中位数和*均数;他们没有遵循类似的趋势。

为了分析上面的观察和假设是否有效,我们可以进行显著性检验,确定 Skagit 和 Whitman 数据集的显著差异。虽然数据不是正态分布的,但所使用的数据集包含足够的条目来有效地进行 t 检验。

在设置置信区间 α 等于 0.05 后,我们对各县进行双样本、双尾 t 检验。

寻找 t 统计量的过程,然后是 p

遵循传统的双样本 t 检验零假设(𝜇₁ = 𝜇₂)和替代假设(𝜇₁ ≠ 𝜇₂),我们看到斯卡吉特和惠特曼之间的 t 检验产生了 0.0434 的 p- 值,这意味着我们拒绝零假设,并确定他们的损失率值在统计上是不同的。

那么,这告诉我们什么呢?

结果和统计测试(在本例中为 t-测试)旨在检查各对相同州县的赔付率是否不同,这表明它们几乎没有一致性,并且它们证实了在县一级制定更精细的保险政策的必要性,因为更广泛的级别(例如,区域或全州)可能是不够的。

气候变化是如何发挥作用的?

气候以多种方式影响作物生长和产量。任何天气变化都可能威胁到农作物并给农民带来损失,因此探索这些问题中的哪一种影响最大,以及它们如何改变农民和保险公司基于货币的衡量标准非常重要。

在下面的联合图中,显示了 赔偿 (指定事件的作物损失总额)和 总保费 (农民向保险公司支付的承保作物的总额)之间的关系,很明显,这两个变量分布都是右偏的,这意味着在低端有大量聚集,在高端有较长的尾部。我们还看到,对于几个总保费值,赔偿看起来变化很小或没有变化,表明它们的比率(损失率)波动,有时剧烈,有时不剧烈。该图表明,从历史上看,总保费比赔款更有可能发生变化。

绘制关系赔偿和总保费

我们可以按县来分析损失的原因。

该嵌套饼图揭示了县级损失原因的相对构成。即使对华盛顿的更多县进行分析,我们也会发现它们对每一种损失原因的影响程度并不相同,这或许表明,在地区层面上对气候如何影响小麦产量所做的预测与在县一级所做的预测并不相似。

饼状图部门是不均衡的,表明不同县之间很少或没有可预测性

在下面的箱线图中,我们探索了与气候相关的损失原因是如何依赖于*均温度(F)的。中位数较高的损失原因表明与较高的*均温度有关。扩散越大,这种原因对温度的依赖性就越小。例如,热风和火灾表现出相对较小的传播范围,因此它们与温度高度相关,但干旱和过多的雨水传播范围较大,与温度的关系不大,这意味着可能有其他因素与它们相关。

显示温度和损耗原因之间的关系

现在,我们进行时间分析。

我们可以观察惠特曼的*均气温是如何随时间变化的。每年一次的温度分析有助于降低更精细层次的噪声。数据集中有一个明显的上升趋势,表明由于全球变暖和气候变化,温度可能正在上升。这条线正以每年 0.019 华氏度的速度增长。鉴于此,在下面的预测实验中,我们将温度作为影响小麦产量的一个关键因素,它直接影响保险公司设定的保险费率。

*均温度随着时间逐渐升高

在下面的多元回归中,生产图预测趋势是虚线,而地面真实数据是实线。*均温度是模型中六个系数之一;其他五个是县、年份、补贴金额、获得保险费的单位数量(进行保险索赔的单个地块)和获得赔偿的单位数量。该图显示了华盛顿各县在预测产量方面的差异,这进一步强调了州一级制定的政策不足以让保险公司获得最佳收益。

华盛顿各县小麦产量的预测趋势与实际趋势

结论

很明显,华盛顿县,以及其他州的县,通常需要不同的保险计划、建议和保险单。通过创建一种基于位置指定赔付率的方法,保险公司可以更好地根据不同的位置定制保单,并调整决策、保费定价率等。同样,农民可以利用这种预测来预测他们的作物产量,做出改进,并相应地提前计划。

以上实验的数据和代码在 这里 提供。

参考

A.库马尔,了解农业保险赔付率特征 (2020),精算基础

气候是你所期待的

原文:https://towardsdatascience.com/climate-is-what-you-expect-20c57da1ac24?source=collection_archive---------32-----------------------

气候数据科学

研究气候系统的规律

气候系统是其组成部分——大气圈、生物圈、冰冻圈、水圈和岩石圈——之间复杂相互作用的产物,由相当多的强迫机制驱动,如太阳辐射和温室气体浓度。尽管混沌是这个系统的固有特征,但在气候变量的行为中有几个规律和组织层次。

亚马逊的典型景观。 Rodrigo Kugnharski 在 Unsplash 上的照片

以一年中的季节为例。如果你住在北半球,你肯定会希望看到冬天下雪,夏天阳光明媚。在热带地区,气温波动不那么剧烈,夏季多雨寒冷,冬季干燥炎热。所有这些都可以用马克·吐温说过的一句话来概括:

气候是你所期待的,天气是你所得到的。

一个简单的短语,但足以让你深刻理解气候系统中的事情是如何运作的。你认为亚马逊雨林中部会有大雪吗?在这个世界上没有。描述气候的一种分析方法是表达其变量的*均条件,如降雨量和温度,至少在 30 年内。这些是著名的气候*均值,,它们告诉你许多关于你应该期待的气候的重要事情。

一年三月的降水

本教程着重于你可以采取的步骤,做你自己的任何感兴趣的特定地区的气候正常。为此,您将使用来自全球降水气候学中心 (GPCC)的网格降水数据集,该数据集由气候学研究中非常常见的全球数据集组成,因为其质量高且时间跨度长,从 1901 年 1 月到 2013 年 12 月。

首先,像往常一样,您需要导入包:

import xarray as xr 
import proplot as plot 
import matplotlib.pyplot as pltfrom esmtools.stats import*

你已经知道Matplotlib是每一个 Python 程序员的可视化软件包的黄金标准,自从cmip 6快速介绍以来,你已经接触了用于 n 维网格数据的Xarray和数据可视化的下一件大事Proplot。新成员是Esmtools,这是一个软件包,主要用于复杂气候模型的统计分析。最简单的安装方法是使用Pip:

*pip install esmtools*

Xarray的许多优秀功能之一是可以直接加载大量数据集,而无需通过 OPeNDAP 下载。由于 NOAA/PSD 目录中的所有数据都可以通过 OpenDAP 访问,因此您可以轻松获得本教程的 GPCC 产品。

*# OPEenDAP url
url = 'http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/gpcc/full_v7/precip.mon.total.2.5x2.5.v7.nc'
# load dataset
dset = xr.open_dataset(url)
>>> dset*

GPCC 月降水网格数据集元数据。

这种降水在地图上看起来怎么样?

*fig, ax = plot.subplots(axwidth=4.5, tight=True,
                        proj='robin', proj_kw={'lon_0': 180},)
# format options
ax.format(land=False, coast=True, innerborders=True, borders=True,
          labels=True, geogridlinewidth=0,)map1 = ax.contourf(dset['lon'], dset['lat'],
                   dset['precip'][0, :, :],
                   cmap='Dusk',
                   levels=plot.arange(0, 400, 50),
                   extend='both')ax.colorbar(map1, loc='b', shrink=0.5, extendrect=True)plt.show()*

GPCC 数据集的第一个全球降水场。

设置感兴趣的区域(和时间)

在本教程中,您将调查亚马逊流域的年降雨量。这一循环受南美季风系统(SAMS)的起伏影响,表现为两个截然不同的雨季和旱季。为了关注这个特定的区域,Xarray允许使用灵活的.sel()方法。你可以在盆地周围设置一个方框区域,选择 1981 年到 2010 年这一特定时期的纬度和经度,根据世界气象组织(WMO)的说法,这是最*的气候时期。

*amazon_area = dset.sel(lat=slice(5, -22), lon=slice(285, 315),
                       time=slice('1981-01-01', '2010-12-01'))*

对于您在路上找到的每一个数据集,您必须格外小心地知道维度在其中是如何描述的。例如,如果 GPCC 数据集将经度维度命名为longitude而不是lon,那么您必须将维度命名为sel而不是lon。所有这些小细节都与您的代码密切相关,所以如果您不熟悉特定的数据集,请务必检查元数据。

气候学研究中的一个常见做法是创建一个指数,这是一个时间序列,用于描述特定地区特定现象或变量的行为。由于您已经选择了亚马逊盆地周围的区域,Esmtools允许您通过对规则网格进行余弦加权,以最严格的方式从网格数据中创建一个索引。抛开气候学的措辞,用以下公式计算降水指数:

*amazon_index = cos_weight(amazon_area['precip'])
>>> amazon_index.dims
('time',)*

亚马逊的年降雨周期是怎样的?

*fig, ax = plot.subplots(figsize=(5, 5), tight=True)
ax.plot(amazon_index['time'], amazon_index, color='Blue')# format options
ax.format(xlabel='Time', ylabel='Precipitation (mm/month)')plt.show()*

亚马逊地区降水周期的起伏。

虽然这很好地反映了年周期应该是什么样子,但它没有反映亚马逊地区降雨行为的一些重要细节。改善它的一个好方法是做一个简单的月图或者更精细的东西,比如箱线图。好消息是,Proplot环绕着Matplotlib,让你可以很容易地做出一个箱线图:

*# numpy trick: transform from a row vector to a matrix
amazon_index = np.reshape(np.array(amazon_index), (30, 12),
                          order='C')fig, ax = plot.subplots(figsize=(5, 5), tight=True, sharex=False)
# format options
ax.format(ylim=(0, 300))ax[0].boxplot(amazon_index, marker='x', fillcolor='azure',
              labels= months)plt.show()*

一年一度的三月降雨,清晰可见。

箱线图可以让你看到亚马逊盆地降雨的真实情况。从 1 月到 5 月中旬,整个盆地有一个大范围的雨季,主要受 SAMS 活跃期和热带辐合带(ITCZ)向南迁移的驱动。然而,令大多数人惊讶的是,雨林经历了正常的降雨缺乏期,因为它有非常明显的旱季,从六月到九月中旬。在这个明显的旱季,火灾季节也开始了,随着时间的推移,农业边界向亚马逊河推进了一点。

最后的话

在大多数情况下,任何感兴趣的气候变量的年周期都是更详细和更深入研究的第一步。然而,这个有点简单的行为已经足以让你很好地了解温度、降水和其他因素的自然行为。在不断变化的气候中,了解特定地区甚至全球的气候如何波动变得格外重要。

一件好事是大量可靠的数据集可供快速下载,而且Xarray甚至允许基于云的访问。其他优秀的软件包,如Proplot和新的Esmtools,促进了这些通常复杂的网格数据集的可视化和统计分析。通常,本教程的 Jupyter 笔记本可以在我的 气候数据科学 资源库中免费获得。

攀登信息阶梯

原文:https://towardsdatascience.com/climb-the-information-ladder-960da82f62b9?source=collection_archive---------39-----------------------

将您的数据转化为有用的信息

如何将数据转化为信息

许多组织拥有大量数据,却不知道如何处理这些数据。数据科学(泛指商业智能、数据可视化、机器学习等)是一个相对较新的行业,旨在找出如何从这些数据中提取价值。

这篇文章展示了从众多数据源中提取越来越多信息的渐进学习过程。

将这些数据转换成有用的信息是一个旅程。

信息阶梯显示了这个旅程,从简单开始,一直到复杂的机器学习。

数据和信息的区别

在工作场所,我经常听到“数据”和“信息”这两个词被错误地使用。这是两件截然不同的事情,经常会被混淆为同一件事情。

在一个许多组织都在努力变得更加数据驱动的世界里,理解其中的区别很重要。

数据是用来产生信息的原材料。

信息是数据的产物;没有数据,就不会有信息。

数据有多种形式和大小。有些数据经常变化,有些是静态的。

频繁变化的数据包括网站数据;有些网站每秒钟有很多点击量。货币汇率是另一个很好的例子,它们整天都在波动。

静态数据包括一个国家的首都或债券的期限。

数据示例包括:

  • 单击网站上的数据
  • 你的电子邮件的书面内容
  • 一本书的静态属性
  • 带有国家代码的国家列表
  • 事件参与者的姓名
  • 电子邮件收件人是否参与了电子邮件
  • 投资账户的持有量
  • 固定收益产品的静态属性

互联网的爆炸式增长导致了数据的大幅增长。我甚至不会试图量化有多少数据,(别人有),但网上发生的一切都是另一个数据点。每封电子邮件、网络点击、点赞、视频浏览、推文等都存储在某个地方。

从数据中获取价值是一项重大挑战。组织成为数据驱动型组织需要时间。理解数据需要时间,解决如何处理数据也需要时间。

因此,当开始让一个组织更加数据驱动的旅程时,管理期望可能会很困难。

信息阶梯由焦油解决

信息阶梯代表了数据到有用信息的转换。

攀登信息阶梯是一个旅程。

在每一个阶段学到的经验教训能够帮助你进入下一个阶段。

每一级都导致对数据更深入的理解。在阶梯的底部,数据工作通常更简单。向上移动通常会导致更高级的数据工作和更大量的数据源。在高层,数据工作变得更加复杂。

从数据到信息的阶梯是什么?

静态报告

图片来自焦油溶液

这些通常是任何商业信息的起点。通常是表格形式的报告,经常有人获取数据,将其放入 Excel/Powerpoint 中并四处发送。通常,这将是一个或两个数据源的组合,保持在基本 Excel 的限制范围内。有时,数据分布在大的 Excel 文件中,查找信息可能需要接收者做一些工作,要么扫描整个表,要么构建数据透视表,等等。

临时报告

图片来自焦油解决方案

如上所述,只是这一次用户可以拖放有限的数据源来自己查找信息。或者,他们可能要求他们的“数据团队”在特定的基础上提供信息。这个一般是 Excel 的形式。可用的数据往往是有限的。这也很容易出错,因为并非所有用户都知道要应用的正确过滤器/数据的特点/等等,以便有效地使用。对用户来说,积极的一面是,只要数据可用,就能很快得到答案。

交互式仪表盘

图片来自焦油解决方案

一个设计良好的仪表板应该能够回答许多业务问题。仪表板应该为用户提供回答他们最初的标准问题的方法(例如,销售和预算如何?)并回答后续问题(例如,哪些产品最畅销?哪些销售人员表现好/差?).通常,这些都是自动化的和基于网络的,把信息放在那些需要它的人的指尖。

仪表板应该回答成熟的商业智能环境中的大多数标准业务问题。

警报

图片来自焦油溶液

如果有什么事情需要用户注意,用户会在这里得到主动通知。业务规则驱动警报。有些警报需要多个数据源,有些警报只需要一个数据源。它通知某人他们需要了解一些信息,并可能采取一些行动。示例包括:

  • CRM 系统中的一些可疑数据需要清理
  • 交易者违反了交易限额;交易者和他们的管理层需要知道
  • 基金投资组合中的一个项目变动超过了正常水*,基金/投资组合经理应该知道

统计分析

图片来自焦油溶液

这就是数据复杂性开始增加的地方。为了使统计分析有意义,基础数据必须是好的。阶梯的前几级应该确保数据的高质量。如果人们正在使用来自以前的梯级的信息,任何现有的数据问题应该已经被纠正。

这是数据被用来尝试和更好地理解一些东西的地方。例如,在订阅业务中,哪些因素会导致订阅续订?对于 PPI 索赔,贷款的哪些特征会导致赔偿?在资产管理领域,是什么导致了资金外流?

预测

图片来自焦油解决方案

根据我们在统计分析中了解到的情况,什么是可能的结果?例如,我们认为订阅续订率会是多少?有多少 PPI 索赔需要人工调查?在接下来的 12 个月里,AUM 可能会损失多少?

预测分析

图片来自焦油溶液

这就是我们试图在个人层面上预测未来会发生什么的地方。

预测分析在金融服务领域已经广泛使用了很长时间。例如:

  • 您的信用评分用于计算贷款违约风险,然后用于决定 a)是否提供贷款和 b)提供贷款的价格(利率)
  • 汽车保险——你过去的索赔历史、年龄、汽车类型等。分担你的保险费用;他们根据同类人群的事故历史分析事故发生的概率,然后根据索赔的可能性给你的保险定价
  • 人寿保险——在为您的人寿保险定价之前,会收集有关您的年龄、健康和生活方式的数据;利用过去的数据,保险公司正在计算你在投保时死亡的概率。因此,一个 50 多岁的超重吸烟者将比一个 20 多岁的健康运动者付出更多

机器学习

图片来自焦油溶液

这很容易成为一个单独的话题。机器学习是人工智能的一个子集,这篇文章很好地解释了。它应该有助于更好地理解客户,从而使他们的体验更适合他们的需求。它可以进一步细分为:

  • 情景建模——在假设情景下的预测
  • 决策支持—扩展场景建模,目标是优化决策
  • 机器人和推荐系统——使用机器学习来影响客户行为。一些领先的组织在这方面表现出色,比如亚马逊的推荐引擎。例如,这个顾客/客户想要什么?基于相似客户的行为,我们能影响决策过程吗?我们能赢得更多业务/维持他们的业务吗?

信息阶梯的现实

在现实世界中,梯级之间可能存在明显的交叉。此外,没有必要踩在梯子的每个梯级上。例如,许多数据项目从梯级 3“交互式”开始。梯级 4,“警报”,通常是“互动”的副产品。

同样,“预测分析”和“机器学习”也可以非常相似。

然而,真实的情况是,随着一个人在阶梯上的进步,对数据的理解和数据源的数量确实增加了。

不爬下面的梯级,从梯子的顶端开始根本不可能。

攀登信息阶梯

数据到信息项目通常从简单的数据集开始。所有的组织都是不同的,有些是数据驱动的,有些则不然。

然而,一旦这些项目开始获得牵引力,并开始证明其价值,它们可以导致重大的文化变革。

对数据的兴趣往往会在各种规模的组织中迅速蔓延。

扩展到更多数据并以不同方式使用现有数据是一个自然的过程。

仅仅回答一个问题很可能会引出更多的问题。

例如:

  • 哪些交易者最赚钱?
  • 他们和谁交易?
  • 他们在交易哪些产品?
  • 是否有外部事件导致这些交易——例如,通货膨胀率上升?
  • 我们能够预测哪些事件会增加某些产品的活动吗?
  • 哪些客户会从这些产品中受益?
  • 等等

一旦一个组织转向更加数据驱动的道路,业务问题就会变得没完没了。

在 Postgres 中攀爬 B 树索引

原文:https://towardsdatascience.com/climbing-b-tree-indexes-in-postgres-b67a7e596db?source=collection_archive---------27-----------------------

理解并应用 Postgres 中的定位索引进行排序和匹配

您的查询很慢,所以您决定通过添加索引来加快速度。它是什么类型的指数?可能是 B 树。

Postgres 有几种索引类型,但是 B 树是最常见的。它们有利于分类和匹配;一旦你理解了它们在引擎盖下的样子,原因就显而易见了。

我们将深入探究 Postgres 中 B 树实现的内部机制,然后做一些 SQL 示例来展示它们的效果。我已经提供了查询,所以你可以自己运行相同的实验。

这篇文章假设你已经对索引是什么和做什么有了一个大致的概念。如果不是,通常提供的抽象是教科书中的术语表。你可以在词汇表中按字母顺序查找,然后跳到它所在的页面,而不是阅读书中的每一行来找到一个单词/主题。我们很快就会看到现实稍微复杂一些。

什么是 B 树?

b 树代表*衡树。

它不是一个二叉树,它允许每个父节点最多 2 个子节点,并且是为内存搜索而设计的。

来自维基百科,

在计算机科学中, B 树是一种自*衡树数据结构,它维护分类数据并允许在对数时间内进行搜索、顺序访问、插入和删除。B 树概括了二叉查找树,允许节点有两个以上的子节点。【2】与其他自*衡二分搜索法树不同,B 树非常适合读写相对较大数据块的存储系统,比如磁盘。常用于数据库和文件系统。

这是一个非常好的非技术性介绍。要获得真正的技术解释,请参见由 Lehman 和 Yao 撰写的论文,Postgres 的实现就是基于该论文。

B 树的结构

b 树是“*衡的”,因为从根到任何叶节点的距离都是相同的。叶节点是没有子节点的节点。根节点是位于顶部的节点。

一个节点有键。在我们下面的根节点中,[10,15,20]是键。键映射到数据库中的值,但也映射到子节点中的绑定键。

第一个子节点[2,9]的值小于 10,因此指针位于 10 的左侧。

第二个子节点[12]的值介于 10 和 15 之间,因此指针从那里开始。

第三个子节点[22]大于 20,因此指针位于 20 的右侧。

现在,如果我们想查找键 12,我们将 12 与根节点中的值进行比较,看到它在 10 和 15 之间。所以我们使用 10 和 15 之间的指针来查找包含 12 的节点。

这是对 Postgres 实现的抽象,但是你可以想象为什么这比遍历表中的每个数字并检查它是否等于 12 要快。

这就是为什么 B 树可以在 O(logN)时间内进行搜索、插入和删除。

B 树每个节点也有最小和最大数量的键。在插入和删除时连接和拆分节点,以保持节点在范围内。

数字、文本和日期的 b 树

我们将通过三个例子来研究 B 树的巨大影响力:数字、文本和日期。

设置

为每种数据类型创建一个表。每个表有两列,但我们只索引其中一列。

create table numbers(
  val integer,
  val_indexed integer
);
CREATE INDEX numbers_indexed ON numbers using btree (val_indexed); create table strings(
  val varchar,
  val_indexed varchar
);
CREATE INDEX strings_indexed ON strings using btree (val_indexed); create table dates(
  val varchar,
  val_indexed varchar
);
CREATE INDEX dates_indexed ON dates using btree (val_indexed);

数字

我们来生成 1 到~1M 之间的 1M 个随机整数。

**insert** **into** numbers
**select** (**random**()*1000000 +1)::**int**,
  (**random**()*1000000 +1)::**int
from** (**SELECT** * **FROM** **generate_series**(1,1000000)) ser;

我们将重复运行下面的查询,记录查询时间,然后再添加额外的 100 万条记录。

**select** * **from** numbers **order** **by** val **desc**;
**select** * **from** numbers **order** **by** val_indexed **desc**;

我们看到的是,在对索引列和非索引列进行排序时,查询时间存在巨大差异。

500 万条记录对于 Postgres 来说微不足道,但我们已经可以看到效率上的巨大差异。

为什么不同?让我们使用 Postgres 中的explain函数来深入研究一下。

explain select * from numbers order by val desc;
explain select * from numbers order by val_indexed desc;

无索引的

编入索引的

我们看到非索引搜索使用顺序扫描,而索引搜索使用索引扫描。给定上面讨论的 B 树结构,很容易理解为什么搜索索引会比搜索表快得多。

文本

让我们用字符串做同样的比较。不过这次我们将一次增加 100k 行。

**insert** **into** strings
**SELECT
  md5**(**random**()::**text**),
  **md5**(**random**()::**text**)
**from** (
  **SELECT** * **FROM** **generate_series**(1,100000) **AS** id
) **AS** ser;

现在对这些行进行排序。

**select** * **from** strings **order** **by** val **desc**;
**select** * **from** strings **order** **by** val_indexed **desc**;

我们看到了什么?与索引搜索击败非索引搜索的模式完全相同。

偷看explain,我们看到的是同样的原因。

explain select * from strings order by val desc;
explain select * from strings order by val_indexed desc;

日期

让我们用日期再做一次。同样,我们一次只生成 100k 行。

with cte as (
select 
  timestamp '1900-01-10 20:00:00' +
  random() * (
    timestamp '2000-01-20 20:00:00'-timestamp '1900-01-10 10:00:00'
  ) rdate
  from (SELECT * FROM generate_series(1,100000) AS id) ser
)
insert into dates
select 
  rdate,
  rdate
from cte;

查询日期并记录查询时间。

select * from dates order by val desc;
select * from dates order by val_indexed desc;

比较一下区别。

我们已经可以猜到explain将会展示什么,但还是让我们来看看吧。

explain select * from dates order by val desc;
explain select * from dates order by val_indexed desc;

同样,通过索引扫描对行进行排序比顺序扫描快几个数量级。

结论

我的目的是让您对索引的结构有一个大致的了解,这样您就可以直观地理解为什么它们会缩短查询时间。

然后我们讨论了一些例子,在这些例子中,添加一个索引会产生昼夜差异。

我们只讨论了对行进行排序,但是调查查询时间并查看其他类型的查询(如whereexists)会很有趣。

Python 中的闭包和装饰器

原文:https://towardsdatascience.com/closures-and-decorators-in-python-2551abbc6eb6?source=collection_archive---------1-----------------------

你将最终理解什么是闭包和装饰器

闭包是函数式编程中的一个重要工具,一些重要的概念如curringpartial application 可以使用它们来实现。装饰器也是 Python 中一个强大的工具,它使用闭包来实现,允许程序员修改函数的行为,而不用永久地修改它。在本文中,我将首先解释闭包和它们的一些应用程序,然后介绍装饰器。

变量范围

为了更好地理解闭包,我们首先需要了解 Python 中变量的作用域。变量的作用域是指可以看到或访问变量的区域。变量不能在其作用域之外被访问。变量的范围由它在源代码中的赋值位置决定。通常,变量可以在三个不同的地方赋值,对应三个不同的作用域:

全局作用域:当一个变量在所有函数之外被定义时。文件中的所有函数都可以访问全局变量。

局部作用域:当一个变量被定义在一个函数内部时,它是这个函数的局部变量。局部变量只能在定义它的函数内部访问。

非局部作用域:当一个变量在封闭函数中赋值时,它对其嵌套函数来说是非局部的。定义非局部变量的函数及其所有嵌套函数都可以访问非局部变量。

如果你在一个函数内部重新分配一个全局变量,一个新的同名局部变量将被创建,这个全局变量被称为被这个局部变量遮蔽。在函数内部对这个局部变量的任何改变都不会影响全局变量。所以如果你想在函数中改变一个全局值,你必须在 Python 中使用global关键字。

例如在清单 1 中,最初,我们定义了两个全局变量xy。当我们在函数f()内部重新赋值x时,定义了一个新的同名局部变量,它并不影响全局变量x。然而,通过使用global关键字,我们可以访问f()中的全局变量yzf()的局部变量,不能在外部访问。退出f()后,该变量不在内存中,因此不能再访问。

# Listing 1x = 1 # x is a global variable  
y = 5 # y is a global variable 
def f():
    global y 
    x = 2   # x is a local variable
    y += 1  # Reassigning the global variable y
    z = 10   # z is a local variable
    print("Local variable x =", x)
    print("Global variable y =", y)
    print("Local variable z =", z)
f()
print("Global variable  x =", x)
print("Global variable y =", y)

输出:

Local variable x = 2
Global variable y = 6
Local variable z = 10
Global variable  x = 1
Global variable y = 6

在 Python 中,一切都是对象,变量是对这些对象的引用。当你将一个变量传递给一个函数时,Python 会将一个引用的副本传递给该变量所引用的对象。它不发送对象或对函数的原始引用。因此,函数作为参数接收的原始引用和复制引用都引用同一个对象。现在,如果我们传递一个不可变的全局对象(比如一个整数或一个字符串),函数就不能使用它的参数来修改它。然而,如果对象是可变的(比如一个列表),函数可以修改它。这里有一个例子:

Listing 2a = [1, 2, 3]
b = 5
def func(x, y):
    x.append(4)
    y = y + 1func(a, b)
print("a=", a)  #  Output is a=[1, 2, 3, 4]
print("b=", b)  #  Output is b=5

我们将两个变量ab传递给funca是对可变对象列表的引用,而b是对不可变整数的引用。func接收作为xa的副本和作为yb的副本。向x添加新元素将改变a引用的原始对象。但是在func里面给y加 1 并不影响b所引用的对象。它只创建了一个包含 6 的新整数对象,现在y将引用它(图 1)。

图 1

内部函数

内部函数(或嵌套函数)是定义在另一个函数(外部函数)内部的函数。外部函数的局部变量对其内部函数来说是非局部的。内部函数可以访问非局部变量,但不能改变它们。重新分配它们只是在内部函数中创建一个同名的新局部变量,而不会影响非局部变量。所以如果你想在一个嵌套函数中改变一个非局部变量,你必须使用nonlocal关键字。

在本文中,我们可以简单地称外部函数的变量为非局部变量,但这意味着它相对于内部函数是非局部的。如果删除内部函数,它将只是外部函数的一个局部变量。此外,内部函数应该以某种方式访问这个变量或者声明它为nonlocal来调用它作为一个非局部变量。如果内部函数不接触外部函数的局部变量,它就不是内部函数的非局部变量。

在本文中,我们从不试图改变函数参数。因此,只要知道外部函数参数的行为类似于非局部变量,并且也可以被内部函数访问(或读取)就足够了。

在清单 3 中,函数参数x,变量yztf()的局部变量。

# Listing 3def f(x):
    y = 5
    z = 10
    t = 10
    def g():
        nonlocal y
        y += 1
        z = 20
        print("Nonlocal variable x =", x)
        print("Local variable z =", z) 
    print("Local variable t =", t)    
    g()
    print("Nonlocal variable x =", x)
    print("Nonlocal variable y =", y)
    print("Local variable z =", z)
f(5)
# This does not work:
# g()

输出:

Local variable t = 10
Nonlocal variable x = 5
Local variable z = 20 
Nonlocal variable x = 5
Nonlocal variable y = 6
Local variable z = 10

x,和y也是其内部函数g()的非局部变量。它们在g()内部被访问。当你重新赋值z时,在g()内部会创建一个新的同名局部变量,它不会影响f()中的局部变量z,所以z不是一个非局部变量(到g)。然而,通过使用关键字nonlocal你可以改变y,它仍然是一个非局部变量。变量t根本不被g访问,所以它不是一个非局部变量。

变量yz属于f()的局部范围,不能在f()之外访问。内部函数g()也被定义在f()内部,只能在那里被调用。事实上,如果您试图运行清单 3 并在f()之外调用g(),Python 会给出一个错误。

但是如果我们能找到一种方法在g()之外调用它呢?那么我们会有第二个问题。退出外部函数后,其局部变量(不属于g()的局部变量)不再存在于内存中。在这种情况下,内部函数不能再访问它们。闭包使得在外部函数之外调用内部函数并访问其非局部变量成为可能。

关闭

首先,我们需要找到一种在外部函数之外调用内部函数的方法。记住函数总是可以返回值的。在清单 4 中,xyf()的局部变量:

# Listing 4def f():
    x = 5
    y = 10
    return x
h=f()

所以在运行h=f()之后,f()的所有局部变量都消失了,你再也不能访问xy了。但是我们仍然有x的值,它被返回并存储在h中。所以也许我们可以让外层函数返回内层函数。这在 Python 中是可能的,因为 Python 函数是第一类。这意味着 Python 将函数视为值,因此您可以将函数赋给变量,将其作为函数参数传递或由另一个函数返回。在清单 5 中,外部函数f(x)返回其内部函数g

# Listing 5def f(x):
    def g(y):
        return y
    return g
a = 5
b = 1
h=f(a)
h(b)  # Output is 1

现在f(x)返回函数g。所以当我们写h=f(a)时,我们将g赋给h,现在h可以像g一样被对待,并接受g的参数。结果h(b)就像调用g(b)

函数的__name__属性存储定义函数的名称。如果我们写:

h.__name__ 

它回来了

'g'

也就是说h现在指的是函数g

你应该注意到f(x)返回的是函数g而不是一个特定的值。比如说。如果我们写下如下内容:

# Listing 6def f(x):
    def g(y):
        return y
    return g(y)
a = 5
b = 1
h=f(a) 
# This does not work:
# h(b)

然后我们得到一个错误。原因是我们是由f(x)(也就是 1)返回g(y)的值,所以h=1,不能用它来调用函数。

我们不需要将f(a)存储在h中。而是可以直接调用f(a)(b)

# Listing 7def f(x):
    def g(y):
        return y
    return g
a = 5
b = 1
f(a)(b)  # Output is 1

需要注意的是,要区分f(a, b)f(a)(b)。函数f(a, b)有两个参数。然而,f(a)(b)是一系列嵌套函数,每个函数都有一个参数。只有一个参数的函数叫做一元函数。所以f(a)(b)是两个嵌套的一元函数的序列。如图 2 所示,Python 从左到右计算这些嵌套函数。所以首先对f(a)进行评估。为了能够链接这些函数,f(a)应该返回另一个函数。这里它返回内部函数g。所以f(a)(b)=g(b)

图 2

我们可以很容易地扩展这个方法,得到一个更长的一元函数序列。清单 8 展示了我们如何拥有三个嵌套的一元函数。第一个函数f(x)具有内部函数g(y)并且 g(y)具有内部函数h(z)。每个外部函数返回其内部函数。

# Listing 8def f(x):
    def g(y):
        def h(z):
            return z
        return h
    return g
a = 5
b = 2
c = 1
f(a)(b)(c)  # Output is 1

但是如果外层函数有一些非局部变量会怎么样呢?这里有一个例子:

# Listing 9def f(x):
    z = 2
    def g(y):
        return z*x + y
    return g
a = 5
b = 1
h = f(a)
h(b)  # Output is 11

如果我们运行它,我们可以看到它工作正常,并且g(y)可以访问变量xz。但这怎么可能呢?运行f(x)后,我们不再在f(x)的范围内,变量xz应该是不可访问的。为什么g(y)仍然可以访问它们?那是因为内部函数g(y)现在是一个闭包

闭包是具有扩展范围的内部函数,它包含外部函数的非局部变量。所以它记住了封闭范围中的非局部变量,即使它们不在内存中。像y这样在内部函数g(y)局部范围内的变量称为绑定变量。只有有界变量的函数称为闭项。另一方面,像z这样的非局部变量被称为自由变量,因为它可以在g(y)之外自由定义,而包含自由变量的函数被称为开项

图 3

“闭包”这个名字来源于这样一个事实,即它捕获其自由(非局部)变量的绑定,并且是关闭一个开放项的结果。

所以闭包是一个开放项,它通过捕获自由(非局部)变量的绑定而被封闭。在清单 9 中,只要内部函数g(y)有一个尚未绑定的自由变量(xz),它就不是闭包。一旦我们对h=f(a)求值,封闭函数f(x)也被求值,自由变量xz分别被绑定到 5 和 2。所以由f(a)返回的g(y)变成了一个闭包,并且h现在引用了一个闭包(图 4)。

图 4

Python 可以跟踪每个函数的自由变量。我们可以简单地使用__code__.co_freevars属性来查看内部函数捕获了哪些自由变量。例如,对于清单 9 中定义的闭包,我们可以写:

h.__code__.co_freevars

输出将是:

('x', 'z')

您还可以使用closure属性获得这些自由变量的值:

print(h.__code__.co_freevars[0], "=",
      h.__closure__[0].cell_contents) 
print(h.__code__.co_freevars[1], "=",
      h.__closure__[1].cell_contents)

输出将是

x = 5
z = 2

重要的是要注意,为了有一个闭包,内部函数应该访问外部函数的非局部变量。当内部函数中没有自由变量被访问时,它不会捕获它们,因为它已经是一个封闭项,不需要被封闭。例如,如果我们将清单 9 中的内部函数改为:

# Listing 10def f(x):
    z = 2
    def g(y):
        return y
    return g
a = 5
b = 1
h = f(a)
h(b)  # Output is 1

那就不能再认为这是一个终结了。原因是非局部变量xzg(y)内部没有被访问,不需要g(y)去捕获它们。我们可以很容易地通过写下:

h.__code__.co_freevars

输出将是:

()

此外,h.__closure返回None,这意味着h不再是闭包。如果您没有访问一个非局部变量,而是在内部函数中将它定义为nonlocal,它仍然会被闭包捕获。所以在清单 11 中,g(y)是一个闭包,因为它捕获了t

# Listing 11def f(x):
    z = 2
    t = 3
    def g(y):
        nonlocal t
        return y
    return g
a = 5
b = 1
h = f(a)
h(b)  
h.__code__.co_freevars  # Output is ('t',)

另一个例子有点复杂:

# Listing 12def f(x):
    def g(y = x):
        return y
    return g
a = 5
b = 1
h = f(a)
h()  # Output is 5

这里g(y)也不是一个闭包,因为x的值只是用来初始化y,而g不需要捕获x

当您有多个嵌套函数时,每个闭包都能够捕获更高层的所有非局部变量。例如,如果我们有三个嵌套函数:

# Listing 13def f(x):
    def g(y):
        def h(z):
            return x * y * z
        return h
    return g
a = 5
b = 2
c = 1
f(a)(b)(c)  # Output is 10

然后h(z)正在访问fg的非局部变量,所以它将捕获这两个变量,所以

# f(a)(b) refers to h
f(a)(b).__code__.co_freevars

将会是:

('x', 'y')

此外,g(y)也是一个闭包,它捕获x作为一个非局部变量。我们可以很容易的检查出来(记住f(a)指的是g(y)):

f(a).__code__.co_freevars  # Output is ('x',)

但是g(y)不访问x。为什么g(y)要变成关闭并捕获x?原因是它的内在功能是访问它。h(z)需要捕获x,但是一个闭包只能捕获其外部函数的自由变量,在本例中是g(y)。所以首先g(y)应该扩展其范围并捕获x,然后h(z)可以扩展其范围并捕获x作为g(y)的自由变量。如果我们将清单 13 替换为:

# Listing 14def f(x):
    def g(y):
        def h(z):
            return y * z
        return h
    return g
a = 5
b = 2
c = 1
f(a).__code__.co_freevars  # Output is ()

我们可以看到g(y)不再是一个闭包。那是因为h(z)不需要捕捉x。结果,g(y)也没有捕捉到,没有成为闭包。

您可能已经注意到,为了调用闭包,我们不使用内部函数的名称。我们只使用外部函数的名称。所以如果我们只有一个表达式,我们也可以用 lambda 函数代替内部函数。例如,我们可以用一个匿名函数替换清单 10 中的内部函数g:

# Listing 15def f(x):
    z = 2
    return lambda y: z*x+y
a = 5
b = 1
f(a)(b)  # Output is 11

至此,我想总结一下到目前为止我们学到的关于闭包的知识。为了定义闭包,我们需要一个内部函数:

1-它应该由外部函数返回。

2-它应该捕获外部函数的一些非局部变量。这可以通过访问这些变量,或者将它们定义为非局部变量,或者使用需要捕获它们的嵌套闭包来实现。

在定义闭包之后,为了初始化它,您必须调用外部函数来返回闭包。

在函数式编程中,闭包可以将数据绑定到函数,而不需要将它们作为参数传递。这类似于面向对象编程中的类。在清单 16 中,我们比较了这些范例。我们首先创建一个类来计算一个数的 n 次方根。

# Listing 16class NthRoot:
    def __init__(self, n=2):
        self.n = n
    def set_root(n):
        self.n = n
    def calc(self, x):
        return x ** (1/self.n)

thirdRoot = NthRoot(3)
print(thirdRoot.calc(27))  # Output is 3def nth_root(n=2):
    def calc(x):
        return x ** (1/n)
    return calcthird_root = nth_root(3)
print(third_root(27))  # Output is 3

如你所见,外部函数在这里可以为我们扮演一个构造函数的角色。它初始化内部函数将使用的非局部变量。然而,也有一些不同之处。NthRoot类可以有更多可以被对象thirdRoot调用的方法。然而,nth_root返回的是一个函数本身。所以这个方法比类能做的更有限。既然我们已经熟悉了闭包,我们可以看看它们的一些应用。

构图

如果我们有两个函数 fg ,我们可以以这样的方式组合它们,使得 f 的输出成为 g 的输入。在数学上,这种运算叫做合成。因此合成操作采用两个函数 fg 并产生一个函数 h ,使得h(x)=g(f(x)。我们可以使用闭包轻松实现它:

# Listing 17def compose(g, f):
    def h(*args, **kwargs):
        return g(f(*args, **kwargs))
    return h

这里的h是一个闭包,因为它捕获了非局部变量fg。这些非局部变量本身就是函数。这个闭包返回的是fg的组合,也就是g(f(*args, **kwargs))。我们使用了*args**kwargs来传递多个参数或关键字参数给h

这是一个简单的应用程序。假设我们有一些转换单位的函数。例如,假设我们有两个函数,分别将英寸转换为英尺英尺转换为。现在我们可以使用我们的compose()函数将它们合并成一个函数,将英寸转换成:

# Listing 18inch_to_foot= lambda x: x/12
foot_meter= lambda x: x * 0.3048inch_to_meter = compose(foot_meter, inch_to_foot)
inch_to_meter(12)   # Output 0.3048

局部应用

在数学中,一个函数接受的参数数量被称为该函数的 arity。部分应用是减少函数的 arity 的操作。这意味着它允许你固定一些参数的值,并冻结它们来得到一个参数更少的函数。所以它在某种程度上简化了功能。

比如 f ( x,y,z) 的 arity 是 3。我们可以将自变量 x 的值固定在 a 处,得到 f(x=a,y,z) = g(y,z) 。现在 g(y,z) 的 arity 为 2,是 f(x,y,z) 部分应用的结果。所以偏(f)=>g。我们可以使用闭包来实现部分应用程序:

# Listing 19def partial(f, *f_args, **f_keywords):
    def g(*args, **keywords):
        new_keywords = f_keywords.copy()
        new_keywords.update(keywords)
        return f(*(f_args + args), **new_keywords)
    return g

这里,外部函数接收需要修复的ff的位置和关键字参数。内部函数g将这些固定参数添加到f的剩余参数中,稍后它将接收这些参数作为部分函数。最后,它用所有收集到的参数调用f。清单 20 给出了一个使用部分函数的例子:

# Listing 20func = lambda x,y,z: x**2 + 2*y + zpfunc = partial(func, 1)
pfunc(2, 3)  # Output is 8

这里我们将x的值固定为 1,并将func转换为pfunc,现在它有两个参数yz

阿谀奉承

在数学中,currying 意味着将一个具有多个参数的函数转换成一系列嵌套的一元函数。如前所述,一元函数是一个只有一个参数的函数。例如,如果我们有一个函数 f(x,y,z) 。Currying 将其转换为g(x)(y)(z)=((g(x))(y))(z)。清单 21 展示了我们如何用 Python 实现它:

# Listing 21def curry(f):
    argc = f.__code__.co_argcount
    f_args = []
    f_kwargs = {}
    def g(*args, **kwargs):
        nonlocal f_args, f_kwargs
        f_args += args
        f_kwargs.update(kwargs)
        if len(f_args)+len(f_kwargs) == argc:
            return f(*f_args, **f_kwargs)
        else:
            return g          
    return g

现在我们可以将它应用于清单 20 中定义的函数:

cfunc = curry(func)
cfunc(1)(2)# Output:
# <function __main__.curry.<locals>.g(*args, **kwargs)>

cfunc(3)  # Output is 8

我们再次使用了闭包。但是这个闭包g是一个递归函数。图 5 显示了这个函数是如何工作的。首先,外部函数curry接收f作为参数。f是应该进行 currying 的函数。我们使用co_argcount属性获得f接受的参数数量(即f的 arity)并将其存储在argc中。在本例中args =3。我们有两个非局部变量f_argsf_kwargs,它们用于存储g接受的参数。curry返回闭包g并将其分配给cfunc。所以当我们调用cfunc(1)(2) 时,我们是在调用g(1)(2)。首先将对g(1)进行评估。

图 5

g将其参数添加到f_args列表中,由于它没有捕获func的所有参数,因此它递归地返回自身。所以g(1)的结果是gg(1)(2)变成了g(2)。现在g(2)评估。再次g将其参数添加到f_args列表中,并且f_args =[1,2]。g再次返回自身,但此时它没有要计算的参数。因此,cfunc(1)(2)的最终输出是g,在 Python 中显示为<function __main__.curry.<locals>.g(*args, **kwargs)>。然后我们运行cfunc(3)。这次对g(3)求值,给f_args加 3,就等于[1,2,3]。现在len(f_args)等于argc,已经捕获了func的所有原始参数,所以最后func(1,2,3)会被g求值并返回。

您应该知道清单 21 中的curry函数不仅仅可以做数学运算。这个函数的输出并不局限于嵌套的一元函数,我们可以有更高 arity 的嵌套函数。例如,我们也可以写:

cfunc = curry(func)
cfunc(1, 2)
cfunc(3) # Output is 8

得到同样的结果。这就好比把 f(x,y,z) 转换成 g(x,y)(z)

装修和装饰工

在谈论 decorators 之前,我需要提一下 Python 中的函数。当您在 Python 中定义函数时,该函数的名称只是对函数体(函数定义)的引用。所以通过给它赋一个新值,你可以强制它引用另一个函数定义。清单 22 给出了一个简单的例子:

# Listing 22def f():
    return("f definition")
def g():
    return("g definition")print("f is referring to ", f())
print("g is referring to ", g())print("Swapping f and g")
temp = f
f = g
g = tempprint("f is referring to ", f())
print("g is referring to ", g())

输出:

f is referring to  f definition
g is referring to  g definition
Swapping f and g
f is referring to  g definition
g is referring to  f definition

到目前为止,我们已经通过将外部函数的结果赋给一个新变量创建了一个闭包:

h=f(a)

现在假设我们有一个名为deco(f)的外部函数,它在清单 23 中定义:

# Listing 23def deco(f):
    def g(*args, **kwargs):
        return f(*args, **kwargs)
    return gdef func(x):
     return 2*xfunc = deco(func)
func(2)  # Output is 4

这里,外部函数是deco(f),它将函数f作为参数,内部函数g被定义为闭包。我们定义另一个函数func并对其应用deco。但是为了初始化闭包,我们将deco的结果赋给func。所以decofunc作为参数,并再次将其闭包赋给func。在这种情况下,我们说func是由deco修饰,deco的修饰者

图 6

需要注意的是,在将装饰器的结果赋给func之后,func指的是闭包g。所以叫func(a)就像叫g(a)。如果我们写:

func.__name__

输出将是'g'。实际上,变量func只是对函数定义的引用。最初指func(x)定义,装修后指g(*args, **kwargs)

但是对于原始函数func(x)会发生什么呢?我们会失去它吗?记住装饰器接收func作为它的参数。所以它有一个引用func的副本作为它的局部变量f。如果原引用发生变化,并不影响这个局部变量,所以在g内部,f仍然引用func(x)定义(我说的函数定义,是指如图 7 所示的函数func(x)的实际体)。

图 7

所以总结一下,修饰后变量func指闭包g,内部gffunc(x)定义。事实上,g现在充当了被修饰的原始函数func(x)的接口。我们不能在g之外直接调用func(x)。而是先调用func调用g,然后在g内部可以调用f调用原函数func(x)。所以我们使用闭包g调用原始函数func(x)

现在使用这个闭包,我们可以添加更多的代码在调用func(x)之前或之后运行。我举个简单的例子。假设我们想调试清单 23 中的代码。我们想知道func(x)是什么时候被调用的。我们可以简单地在func(x)中放置一个打印语句,让我们知道它何时运行:

def func(x):
    print("func is called") 
    return 2*x

但是它改变了功能,我们必须记得稍后删除它。更好的解决方案是定义一个装饰器来包装函数,并将 print 语句添加到闭包中。

# Listing 24def deco(f):
    def g(*args, **kwargs):
        print("Calling ", f.__name__)
        return f(*args, **kwargs)
    return gdef func(x):
    return 2*xfunc = deco(func)
func(2) 

输出是:

Calling  func
4

这个例子显示了装饰器有多有用。现在我们可以看到亵渎的更多应用:

记忆

内存优化是一种用来加速程序的编程技术。它源自拉丁语备忘录,意思是“被记住”。顾名思义,它是基于记忆或缓存昂贵的函数调用的结果。如果使用相同的输入或具有相同参数的函数调用,则之前缓存的结果将用于避免不必要的计算。在 Python 中,我们可以使用闭包和装饰器自动记忆函数。

清单 25 展示了一个计算斐波那契数列的函数。斐波那契数列是递归定义的。每个数字都是前面两个数字的和,从 0 和 1 开始:

# Listing 25def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)
for i in range(6):
    print(fib(i), end=" ")# Output
# 0 1 1 2 3 5

现在我们定义一个新函数,它可以记忆另一个函数:

# Listing 26def memoize(f):
    memo = {}
    def memoized_func(n):
        if n not in memo:            
            memo[n] = f(n)
        return memo[n]
    return memoized_func

要使用memoize函数,我们可以将其用作fib的装饰器:

fib = memoize(fib)
fib(30) # Output is 832040

所以功能fib现在由memoize()修饰。现在fib指的是关闭memoized_func,所以当我们叫fib(30)的时候,就像叫memoized_func(30)。装饰器接收原始的fib作为它的参数f,所以f引用memoized_func中的fib(n)定义。闭包memoized_func首先检查n是否在备忘录字典中。如果在里面,就简单的返回memo[n],不调用原来的fib(n)。如果n不在 memo 字典中,它首先调用引用原始fib(n).f(n),然后将结果存储在memo字典中,最后将其作为最终结果返回。

追踪递归函数

假设我们想调试清单 25 中定义的递归 Fibonacci 函数。我们想看看这个函数是如何调用自己来计算最终结果的。我们可以定义一个装饰器来跟踪函数调用:

# Listing 27def trace(f):
    level = 1
    def helper(*arg):
        nonlocal level
        print((level-1)*"  │",  "  ┌",  f.__name__,
              "(", ",".join(map(str, arg)), ")", sep="")
        level += 1
        result = f(*arg)
        level -= 1
        print((level-1)*"  │", "  └",  result, sep="")
        return result
    return helper

现在我们可以写:

def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)
fib = trace(fib)
fib(4)

输出是:

 ┌fib(4)
  │  ┌fib(3)
  │  │  ┌fib(2)
  │  │  │  ┌fib(1)
  │  │  │  └1
  │  │  │  ┌fib(0)
  │  │  │  └0
  │  │  └1
  │  │  ┌fib(1)
  │  │  └1
  │  └2
  │  ┌fib(2)
  │  │  ┌fib(1)
  │  │  └1
  │  │  ┌fib(0)
  │  │  └0
  │  └1
  └33

装饰函数trace接收fib作为它的参数。在调用装饰者之后,fib指的是关闭helpertrace有一个名为level的非局部变量,显示递归深度,初始设置为 1。helper首先打印一些填充字符,包括,并显示递归树结构。字符的数量与level的数值成正比。

随着递归深度的增加,更多的被打印。对于第一次调用,因为level-1等于零,所以|不打印。然后打印函数名及其参数的当前值。之后level加 1 以显示下一级递归。然后评估函数f(指传递给装饰器的原始函数)。因此对fib(n)定义进行评估。由于fib是递归定义的,它会在某个时候调用自己。记住装修后 fib 指的是封闭helper。所以递归调用会再次调用helper,下一级递归会被打印出来。

递归继续,直到我们到达递归基本情况,并且函数定义fib(n)返回 1。然后level减一,将返回的结果与返回它的函数名联系起来。返回值被一个接一个地打印出来,直到我们回到第一个调用。

句法糖

语法糖是编程语言中的语法,它使事情更容易阅读或表达。使用语法糖,你可以更清楚或更简洁地编写代码。例如,在 Python 中,+=操作符是语法糖。所以不用写a=a+1,你可以简单地写a+=1

在清单 24 中,我们编写了func = deco(func)来修饰函数func。实际上,我们可以用更简单的方法来做,最终结果是一样的:

# Listing 28def deco(f):
    def g(*args, **kwargs):
        print("Calling ", f.__name__)
        return f(*args, **kwargs)
    return g@deco
def func(x):
    return 2*xfunc(2)

这里我们简单地在应该由deco(f)修饰的函数上写@deco,而不是写func = deco(func)。这是修饰函数的语法糖,有时被称为语法

图 8

关于这个语法的一些注意事项。@deco不能在其他地方,并且在@decofunc(x)的定义之间不允许有更多的语句。你不应该在@deco后面写deco的参数。语法假设deco只接受一个参数,这个参数是应该被修饰的函数。稍后我会解释如果装饰函数有更多的参数该怎么做。

在清单 24 中,一旦编写了func = deco(func),装饰就开始了。但是在清单 28 中什么时候会发生呢?用 pie 语法定义的 decorators 的一个关键特性是它们在被修饰的函数被定义后立即运行。这通常是在 Python 加载模块时的导入时间。

堆叠装饰者

在清单 29 中,我们定义了两个装饰器:

# Listing 29def deco1(f):
    def g1(*args, **kwargs):
        print("Calling ", f.__name__, "using deco1")
        return f(*args, **kwargs)
    return g1def deco2(f):
    def g2(*args, **kwargs):
        print("Calling ", f.__name__, "using deco2")
        return f(*args, **kwargs)
    return g2def func(x):
    return 2*xfunc = deco2(deco1(func))
func(2)

这里我们首先将deco1应用于func,然后将deco2应用于deco1返回的闭包。所以我们有两个堆叠的装饰者。我们先用deco1装饰func,再用deco2装饰。输出是:

Calling  g1 using deco2
Calling  func using deco1
4

事实上g2g1现在充当被修饰的原始函数func(x)的接口。为了达到最初的功能func(x),我们首先调用func,它指的是g2。在g2内部,参数f指的是g1。所以通过调用f,我们在调用g1,然后在g1内部我们可以调用f来调用原来的函数func(x)

图 9

我们还可以使用 pie 语法将堆栈装饰器应用到函数中。为此,我们可以写:

# Listing 30def deco1(f):
    def g1(*args, **kwargs):
        print("Calling ", f.__name__, "using deco1")
        return f(*args, **kwargs)
    return g1def deco2(f):
    def g2(*args, **kwargs):
        print("Calling ", f.__name__, "using deco2")
        return f(*args, **kwargs)
    return g2@deco2
@deco1
def func(x):
    return 2*xfunc(2)

在这里,装饰者按照包装函数的顺序堆叠在函数定义的顶部。

图 10

我们还可以使用清单 17 中引入的函数compose来组合这两个装饰器,然后将它们应用到目标函数。合成操作可以采用两个函数deco1(f)deco2(f),并产生一个函数deco,使得deco(f) = deco2(deco1(f))。这如清单 31 所示。

# Listing 31def deco1(f):
    def g1(*args, **kwargs):
        print("Calling ", f.__name__, "using deco1")
        return f(*args, **kwargs)
    return g1
def deco2(f):
    def g2(*args, **kwargs):
        print("Calling ", f.__name__, "using deco2")
        return f(*args, **kwargs)
    return g2deco = compose(deco2, deco1) 
@deco
def func(x):
    return 2*x
func(2)

现在我们可以看到一个堆叠装饰器的例子。记得我们为记忆定义了一个装饰器。我们还定义了一个装饰器来跟踪递归函数。现在我们可以把它们结合起来,用记忆来追踪一个递归函数。我们首先用清单 26 中给出的memoize装饰器装饰 Fibonacci 函数,然后用清单 27 中给出的trace装饰器。我们先试着算一下fib(5)

# Listing 32@trace
@memoize
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)fib(5)

输出是:

 ┌memoized_func(5)
  │  ┌memoized_func(4)
  │  │  ┌memoized_func(3)
  │  │  │  ┌memoized_func(2)
  │  │  │  │  ┌memoized_func(1)
  │  │  │  │  └1
  │  │  │  │  ┌memoized_func(0)
  │  │  │  │  └0
  │  │  │  └1
  │  │  │  ┌memoized_func(1)
  │  │  │  └1
  │  │  └2
  │  │  ┌memoized_func(2)
  │  │  └1
  │  └3
  │  ┌memoized_func(3)
  │  └2
  └55

由于 memo 字典最初是空的,它将执行所有的递归调用来计算结果。现在,如果我们尝试fib(6),我们会得到:

 ┌memoized_func(6)
  │  ┌memoized_func(5)
  │  └5
  │  ┌memoized_func(4)
  │  └3
  └8
8

这次fib(5)fib(4)的结果已经存储在memo字典中。所以它只需要检索它们来计算结果,不需要更多的递归。你可能已经注意到了,函数fib的名字没有打印在递归树中。相反,我们看到了闭包的名字memoized_func。原因是我们有两个堆叠的装饰器,并且trace装饰器接收memoized_func作为它的参数,而不是函数fib。所以在trace里面现在f是指memoized_funcf.__name__返回memoized_func。在下一节中,我将向您展示我们如何解决这个问题。

装饰者附加参数

到目前为止,我们讨论的装饰器只接受一个参数,即被装饰的函数,然而,我们可以有额外的参数。还记得我们在清单 24 中看到的装饰器吗?它在调用之前打印一条消息,加上被修饰函数的名称。现在我们想创建一个装饰器,它将消息作为一个附加参数。事实上,我们希望向装饰器传递两个额外的参数。一个是在调用修饰函数之前将被打印的消息,另一个是在调用它之后将被打印的消息。

# Listing 33def deco(msg_before, msg_after):
    def original_deco(f):
        def g(*args, **kwargs):
            print(msg_before + " " + f.__name__)
            result =  f(*args, **kwargs)
            print(msg_after + " " + f.__name__)
            return result
        return g
    return original_deco
def func(x):
    return 2*x
func = deco("Starting", "Finished")(func)
func(2)

输出是:

Starting func
Finished func
4

正如您所看到的,要将附加参数传递给装饰器,我们需要用另一个接受这些附加参数的函数来包装原来的装饰器函数。现在,这个函数将是装饰器,而最初的装饰器将是它内部的一个嵌套闭包。为了调用装饰器,我们需要首先将附加参数传递给deco,然后被装饰的函数f将被传递给它返回的闭包(这是最初的装饰器)。我们还可以使用清单 34 和图 11 所示的饼图语法。

# Listing 34def deco(msg_before, msg_after):
    def original_deco(f):
        def g(*args, **kwargs):
            print(msg_before + " " + f.__name__)
            result =  f(*args, **kwargs)
            print(msg_after + " " + f.__name__)
            return result
        return g
    return original_deco@deco("Starting", "Finished")
def func(x):
    return 2*xfunc(2)

然后我们只需要将附加参数传递给@deco

图 11

你可能会问为什么我们不能把额外的参数传递给最初的装饰者?实际上我们可以:

# Listing 35def deco(msg_before, msg_after, f):
    def g(*args, **kwargs):
        print(msg_before + " " + f.__name__)
        result =  f(*args, **kwargs)
        print(msg_after + " " + f.__name__)
        return result
    return g

def func(x):
    return 2*x
func = deco("Starting", "Finished", func)
func(2)
sds

这里我们简单地传递附加参数和函数名。问题是如果你这样定义,你就不能使用 pie 语法。所以如果你写:

# This does not work:
@deco("Starting", "Finished")
def func(x):
    return 2*x
#func = deco("Starting", "Finished")
func(2)

Python 给出一个错误。也可以使用我们在清单 21 中定义的函数curry来简化装饰函数。curry函数将另一个函数作为它的参数,并返回一个闭包,因此它可以用来装饰我们的装饰器。所以我们可以用下面的代码替换清单 34:

# Listing 36@curry
def deco(msg_before, msg_after, f):
    def g(*args, **kwargs):
        print(msg_before + " " + f.__name__)
        result =  f(*args, **kwargs)
        print(msg_after + " " + f.__name__)
        return result
    return g

@deco("Starting", "Finished")
def func(x):
    return 2*xfunc(2)

运行函数deco后,可以单独获取其参数。因此,通过编写@deco(“Starting”, “Finished”),我们提供了deco的前两个参数,现在它只需要一个参数,即修饰函数。所以我们可以使用 pie 语法,我们不需要定义另一个函数来获取额外的参数。

我们现在可以回到上一节中的问题。我们希望在修饰后保留被修饰函数的签名。我们可以为此定义一个新的装饰器:

# Listing 37def wraps(f):
    def decorator(g):
        def helper(*args, **kwargs):
            return g(*args, **kwargs)
        attributes = ('__module__', '__name__', '__qualname__',
                      '__doc__', '__annotations__')         
        for attr in attributes:
            try:
                value = getattr(f, attr)
            except AttributeError:
                pass
            else:
                setattr(helper, attr, value)
        return helper
    return decorator

为了理解这个装饰器,假设我们有一个名为f的函数,我们想用另一个函数g来装饰它。但是我们也希望g(也就是helper)返回的闭包有f的相同签名,这样我们可以在装修后保留f的签名。wraps是一个装饰器,它接收f作为附加参数,并装饰传递给其嵌套闭包decorate的函数g。然后,它使用getattr()获取f的所有属性,并将它们分配给闭包helper,闭包最终作为修饰的结果返回。现在helper有了f的签名。

我们将这个装饰器添加到清单 26 中定义的 memoize 函数中。这里wraps保存了应该被记忆的函数f的签名。

# Listing 38def memoize(f):
    memo = {}
    @wraps(f)
    def memoized_func(n):
        if n not in memo:            
            memo[n] = f(n)
        return memo[n]
    return memoized_func

功能trace保持不变,我们可以和memoize一起使用来正确跟踪fib:

# Listing 39@trace
@memoize
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)fib(5)

现在输出显示了正确的函数名:

 ┌fib(5)
  │  ┌fib(4)
  │  │  ┌fib(3)
  │  │  │  ┌fib(2)
  │  │  │  │  ┌fib(1)
  │  │  │  │  └1
  │  │  │  │  ┌fib(0)
  │  │  │  │  └0
  │  │  │  └1
  │  │  │  ┌fib(1)
  │  │  │  └1
  │  │  └2
  │  │  ┌fib(2)
  │  │  └1
  │  └3
  │  ┌fib(3)
  │  └2
  └55

我们可以通过评估fib(6)来检查记忆。输出是:

 ┌fib(6)
  │  ┌fib(5)
  │  └5
  │  ┌fib(4)
  │  └3
  └8
8

这个例子展示了不同类型的装饰器如何组合在一起产生期望的输出。

装饰器是 Python 中元编程的有用工具。元编程是关于创建以操作代码为主要目标的函数。本文中的例子展示了如何通过用不同的装饰器包装它们来改变函数的行为。我希望你喜欢阅读这篇文章。如果您有任何问题或建议,请告诉我。本文中的所有代码清单都可以作为 Jupyter 笔记本从 GitHub 下载,网址是:【https://github.com/reza-bagheri/Closures-and-decorators

用自然语言处理分析服装评论—第二部分

原文:https://towardsdatascience.com/clothes-reviews-analysis-with-nlp-part-1-bfb8a3a2c4bd?source=collection_archive---------34-----------------------

https://www.klaviyo.com/blog/fashion-apparel-best-practices

从文本评论分析预测项目评分

在我之前的文章中,我一直在分析网上购买女装的文字评论,以推断顾客的情绪。这个想法是调查情绪是否与购买建议一致。

在本文中,我将继续分析文本评论,但现在将重点放在评级列上,该列显示该商品的得分从 1(最差)到 5(最好)。

让我们刷新一下我们正在谈论的数据集(你可以在 Kaggle 上找到它):

import pandas as pd
df = pd.read_csv('Womens Clothing E-Commerce Reviews.csv')#data cleaningdf.dropna(inplace=True)
df.reset_index(drop=True, inplace=True)
import re
for i in range(len(df)):
    #print(i)
    df['Review Text'][i] = df['Review Text'][i].replace("'s", " is").replace("'ll", " will").replace("'ve", " have").replace("'m", " am").replace("\'", "'")

df['Review Text'][1]
df = df.drop('Unnamed: 0', 1)#data preprocessingimport nltk
import en_core_web_sm
import spacy
nlp = spacy.load("en_core_web_sm")
from nltk.corpus import stopwords
def lemmatization(df):
    df["Lemmas"] = [" ".join([token.lemma_ if token.lemma_ != "-    
    PRON-" else token.text.lower() 
    for sentence in nlp(speech).sents for token in sentence if 
    token.pos_ in {"NOUN", "VERB", "ADJ", "ADV", "X"} and  
    token.is_stop == False]) for speech in df.text]

df["Lemmas"] = [" ".join([token.lemma_ if token.lemma_ != "-PRON-" 
                else token.text.lower() for sentence in 
                nlp(speech).sents for token in sentence if 
                token.pos_ in {"NOUN", "VERB", "ADJ", "ADV", "X"} 
                and token.is_stop == False]) for speech in 
                df['Review Text']]df.head()df.head()

(如果您不熟悉代码,请务必查看本系列的第 1 部分,其中解释了代码的每个块!)

为此,我们将使用 Keras (内嵌 Tensorflow)构建一个神经网络。在开始之前,我们需要对数据集做一些进一步的预处理。事实上,由于它是一个多类分类(5 个类组成评级列),我们首先需要对该列进行一次性编码(一旦将其从整数转换为字符串):

from sklearn.preprocessing import MultiLabelBinarizermlb = MultiLabelBinarizer()df_factor = df.join(pd.DataFrame(mlb.fit_transform(df['Rating']),
                          columns=mlb.classes_,
                          index=df.index))
df_factor.head() 

很好,现在让我们导入必要的库,并将数据集分成训练(80%)和测试(20%)集。这对于我们模型的稳健性至关重要:事实上,由于任何机器学习模型的主要目标都是对新的、前所未见的数据进行准确预测,因此根据算法在测试集而不是训练集上的性能来调整算法的参数是至关重要的,以避免过度拟合。

import tensorflow as tf
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import InputLayer, Input
from tensorflow.python.keras.layers import Reshape, MaxPooling2D
from tensorflow.python.keras.layers import Conv2D, Dense, Flatten, Activation, Dropout
from tensorflow.python.keras.optimizers import SGD#diseabling eager execution
from tensorflow.python.framework.ops import disable_eager_execution
disable_eager_execution()import pandas as pd
from sklearn.model_selection import train_test_splittrain, test = train_test_split(df_factor, test_size=0.2)vectorizer_nn = TfidfVectorizer(ngram_range = (1, 2), min_df = 0.001, max_df = 0.25, stop_words = 'english')X_train_nn = vectorizer_nn.fit_transform(train.Lemmas)
X_test_nn = vectorizer_nn.transform(test.Lemmas)y_train = train.drop(["Clothing ID", "Age", "Title", "Review Text", "Rating", "Recommended IND", "Lemmas", "Positive Feedback Count", "Division Name", "Department Name", "Class Name"], axis = 1)
y_test = test.drop(["Clothing ID", "Age", "Title", "Review Text", "Rating", "Recommended IND", "Lemmas", "Positive Feedback Count", "Division Name", "Department Name", "Class Name"], axis = 1)

注意:在导入必要的模块时,我编写了“tensorflow.python.keras.models”代码,因为这是存储模块的路径。在输入之前,请确保检索 TensorFlow 模块的正确路径。

现在让我们建立我们的模型。这个想法是初始化一个空模型,用 Sequential(),然后添加许多隐藏层,用 dropout 选项来避免过度拟合,最后,用 softmax 函数激活层。

(注:如果你有兴趣了解更多关于神经网络的参数和超参数,可以在这里阅读我以前的文章。)

model = Sequential()
model.add(Dense(5000, activation='relu', input_dim = X_train_nn.shape[1]))
model.add(Dropout(0.1))
model.add(Dense(600, activation='relu'))
model.add(Dropout(0.1))
model.add(Dense(200, activation='relu'))
model.add(Dropout(0.1))
model.add(Dense(y_train.shape[1], activation='softmax'))sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy',])score = model.evaluate(X_test_nn, y_test, batch_size = 7000)
score

正如你所看到的,我们最终得到了一个准确率为 54.36%的模型,这并不是那么糟糕,但仍然太低,不能作为一个可靠的预测指标。我们能做得比这更好吗?好吧,如果我们让病人用我们的神经网络玩一点甜菜,我们可以尝试不同的组合,看看结果。

令人欣慰的是,Keras 是一个强大的工具,因为它提供了一个简单的界面。我们还可以绘制培训和测试过程的历史,如下所示:

import matplotlib.pyplot as plthistory = model.fit(X_train_nn, y_train, validation_split=0.25, epochs = 10, batch_size = 7000, verbose=1)# Plot training & validation accuracy values
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()# Plot training & validation loss values
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()

本文并不是关于 Keras 的详尽指南,但它是一个例子,说明了如何轻松实现 Keras 模块来完成这种任务。

如果你有兴趣了解更多关于 Keras 和神经网络的知识,我推荐以下读物:

  • https://keras.io/getting-started/sequential-model-guide/
  • https://www.tensorflow.org/api_docs/python/tf
  • 【https://pathmind.com/wiki/neural-network
  • https://medium . com/data series/understanding-the-maths-behind-neural-networks-108 a4 ad 4d db

用 NLP 分析服装评论—第 1 部分

原文:https://towardsdatascience.com/clothes-reviews-analysis-with-nlp-part-1-d81bdfa14d97?source=collection_archive---------15-----------------------

https://www.klaviyo.com/blog/fashion-apparel-best-practices

如何推断顾客评论的情绪

自然语言处理(NPL)是人工智能的一个领域,其目的是找到计算方法来解释人类语言的口语或书面语。NLP 的思想超越了可以由 ML 算法或深度学习神经网络进行的简单分类任务。的确,NLP 是关于解释的:你想训练你的模型不仅要检测频繁出现的单词,还要统计它们或者消除一些嘈杂的标点符号;你希望它告诉你谈话的情绪是积极的还是消极的,电子邮件的内容是纯粹的宣传还是重要的事情,过去几年关于惊悚小说的评论是好是坏。

好消息是,对于 NLP,我们提供了有趣的 Python 库,它提供了一个预先训练好的模型,能够查询书面文本。其中,我将使用空间和 NLTK 。

在本文中,我将分析女装电子商务数据集,其中包含客户撰写的文本评论(此处可用)。

以下是数据集的描述:

This dataset includes 23486 rows and 10 feature variables. Each row corresponds to a customer review, and includes the variables:Clothing ID: Integer Categorical variable that refers to the specific piece being reviewed.Age: Positive Integer variable of the reviewers age.Title: String variable for the title of the review.Review Text: String variable for the review body.Rating: Positive Ordinal Integer variable for the product score granted by the customer from 1 Worst, to 5 Best.Recommended IND: Binary variable stating where the customer recommends the product where 1 is recommended, 0 is not recommended.Positive Feedback Count: Positive Integer documenting the number of other customers who found this review positive.Division Name: Categorical name of the product high level division.Department Name: Categorical name of the product department name.Class Name: Categorical name of the product class name.

因此,让我们导入并检查我们的数据集:

import pandas as pd
df = pd.read_csv('Womens Clothing E-Commerce Reviews.csv')
df.head()

现在让我们清理它,删除 NaN 值和无用的列,以及清理文本。

df.dropna(inplace=True)
df.reset_index(drop=True, inplace=True)
import re
for i in range(len(df)):
    #print(i)
    df['Review Text'][i] = df['Review Text'][i].replace("'s", " is").replace("'ll", " will").replace("'ve", " have").replace("'m", " am").replace("\'", "'")

df['Review Text'][1]
df = df.drop('Unnamed: 0', 1)
df.head()

这个想法是提取每个评论的情感,看它是否与推荐指标一致(如果评论推荐该项目,则为 1,否则为 0)。

为此,我们将使用 NLTK 包中可用的 Vader 模块(您可以在这里找到官方文档)。但是首先,让我们预处理我们的文本并提取词条:

#importing necessary librariesimport nltk
import en_core_web_sm
import spacy
nlp = spacy.load("en_core_web_sm")
from nltk.corpus import stopwords def lemmatization(df):
    df["Lemmas"] = [" ".join([token.lemma_ if token.lemma_ != "-    
    PRON-" else token.text.lower() 
    for sentence in nlp(speech).sents for token in sentence if 
    token.pos_ in {"NOUN", "VERB", "ADJ", "ADV", "X"} and  
    token.is_stop == False]) for speech in df.text] df["Lemmas"] = [" ".join([token.lemma_ if token.lemma_ != "-PRON-" 
                else token.text.lower() for sentence in 
                nlp(speech).sents for token in sentence if 
                token.pos_ in {"NOUN", "VERB", "ADJ", "ADV", "X"} 
                and token.is_stop == False]) for speech in 
                df['Review Text']]df.head()

现在让我们对 Lemmas 列进行情感分析:

from nltk.sentiment.vader import SentimentIntensityAnalyzersid = SentimentIntensityAnalyzer()
sentiment=[]
for i in range(len(df)):
     ss = sid.polarity_scores(df.Lemmas[i])
     sentiment.append(ss)

compound=[sentiment[i]['compound'] for i in range(len(sentiment))]
df['compound']=compound
df.head()

复合列包含范围从-1(非常负)到 1(非常正)的分数。一般来说,如果一篇文章的分数大于 0.05,我们就给它贴上“正面”标签;如果分数小于-0.05,我们就贴上“负面”标签;如果分数在-0.05 到 0.05 之间,我们就贴上“中性”标签。但是,为了这个研究问题的目的,我们将只区分积极和消极,使用分数=0 作为阈值。

sent=[]
for i in compound:
    if i<0:
        sent.append('negative')
    else:
        sent.append('positive')df['sentiment']=sent
df.head()

第一眼看到情感专栏,似乎大多数评论都是积极的,但它与推荐项目的数量一致:在 19661 篇评论中,有 16087 篇最终得到了推荐。

现在我们来看看有多少推荐商品符合点评的情绪。让我借用假阳性/阴性和真阳性/阴性的统计学术语来完成这个任务:当商品被推荐购买时,我谈论假阳性,然而文本评论却带有负面情绪。

#initializing a 2x2 matrix populated with zerosmat = np.zeros(4).reshape(2,2)
true_neg=0
true_pos=0
false_neg=0
false_pos=0for i in range(len(df)):
    if df['sentiment'][i]=='negative' and df['Recommended IND'][i]==0:
        true_neg+=1
    elif df['sentiment'][i]=='positive' and df['Recommended IND'][i]==1:
        true_pos+=1
    elif df['sentiment'][i]=='positive' and df['Recommended IND'][i]==0:
        false_neg+=1
    elif df['sentiment'][i]=='negative' and df['Recommended IND'][i]==1:
        false_pos+=1

mat[0][0]=true_neg
mat[0][1]=false_neg
mat[1][0]=false_pos
mat[1][1]=true_pos
mat

让我们把它变得漂亮一点:

from pandas import DataFrameIndex= ['Not Recommended', 'Recommended']
Cols = ['Negative', 'Positive']
d = DataFrame(mat, index=Index, columns=Cols)

所以在 16087 个推荐条目中,只有 227 个不符合文本的情感。另一方面,我们并不推荐 3154 个实际上已经获得正面评价的项目。这种结果可能暗示了推荐系统的不同方法,因为如果我们观察评论者的情绪,可能会购买更多的项目。

情感分析在市场研究和类似领域中起着举足轻重的作用。事实上,跟踪顾客的满意度,最重要的是,隔离那些决定他们对商品的情绪的驱动因素,可能会导致赢得营销活动和销售策略。

在这篇文章中,我将我的研究局限于情感分析,但是这个数据集提供了多个研究问题,这将在我的下一篇文章中涉及,所以请继续关注!

参考资料:

  • https://www.nltk.org/
  • https://spacy.io/
  • 【https://medium.com/r/? URL = https % 3A % 2F %2Fwww . ka ggle . com % 2Fnicapotato % 2f 预兆-电子商务-服装-评论% 2f 版本%2F1%23

云采用—企业有选择吗?

原文:https://towardsdatascience.com/cloud-adoption-do-enterprises-have-a-choice-a83656bba8ac?source=collection_archive---------25-----------------------

来源: Freepik 网站

在我们讨论为什么要云这个更大的问题之前,让我们先了解云计算的一些基础知识:

什么是云计算?

简而言之,云计算是指使用通过互联网提供的按需 IT 资源,采用按需付费的定价模式。与购买基础设施的传统模式相比,关键区别在于,提供商(比如谷歌、亚马逊、微软、IBM、阿里巴巴或任何其他提供商)为您维护相同的硬件,而不是购买您自己的硬件。

云提供商提供核心 IT 基础设施服务(例如计算、存储、网络、数据库)以及基于核心基础设施的解决方案(大数据和分析、IOT、SaaS 应用等)。).

对于企业来说,采用云是一种选择还是一种必需?

在回答这个问题之前,我们先来看看采用云技术相对于购买和维护传统 IT 基础设施有什么好处

  1. 收益提升导向效益

a)灵活快速地管理不断增长的客户群:随着业务的增长,您需要更多的 IT 基础设施来管理不断增长的客户需求(例如,更高的计算能力、更多的数据存储、更快的响应时间)。你是怎么做到的?

  • 选择 1(传统):每次现有硬件无法管理工作负载时,购买更多硬件。这更像是一个阶跃函数,您要么购买刚好满足新工作负载的量(并随着需求的增加继续购买),要么购买足以满足未来几个月/几年的峰值需求的量(这意味着浪费了大量容量)。如果您对此管理不当,将会对收入产生影响(例如,由于响应时间短、网站无法加载等原因导致客户不满意。)
  • 选择 2(基于云):借助云的力量,您无需在需求发生变化时购买/销售物理硬件—扩展/缩减可以动态自动发生

b)有效利用数据获得/保留客户/交叉销售/追加销售:利用您周围不同来源的大量数据(企业应用程序、社交媒体、交易数据、IOT 数据等)。),作为一个组织,您有两种选择:

  • 选择 1(传统):使用固定容量的 IT 基础架构,仅处理基础架构允许的数据量
  • 选择 2(基于云):挖掘所有可用的数据,使用云弹性容量将它们存储(或有意识地丢弃)在基于云的数据湖/数据仓库中,并创建真正的 360 度客户视图,以推动客户增长相关的分析用例

c) 以敏捷的方式应对变化:面对如此众多的市场竞争对手和不断变化的客户环境,您需要不断发展以保持相关性。环境要求推出新产品/新功能,并前所未有地挑战客户/员工体验的极限。你如何解决这个问题?

  • 选择 1(传统):在现有硬件和软件解决方案的限制下开发和部署新产品(或者等待很长时间来购买和集成任何新的外部解决方案)。
  • 选择 2(云原生):利用云上可用的大量解决方案来快速开发/部署新产品/功能;利用数据和分析*台,无需从头构建;使用现有的客户服务解决方案(例如支持人工智能的聊天机器人)

d)支持地域增长:您需要扩展到一个新的地域,您会怎么做?

  • 选择 1(传统):在新的地理位置设置整个技术基础设施,并在那里部署您的应用程序
  • 选择 2(基于云):立即在云中为新的地理位置配置基础架构,在新的地理位置部署您的应用程序并投入使用!

2。成本降低导向效益

a)将资本支出转变为运营支出,降低单位运营成本(按使用付费;不需要为未使用的部分付费,这是在传统模式中容量固定的情况下)。

b)避免成本:消除硬件更新、维护、升级等成本。

c)提高生产率:降低停电管理成本;使用云上可用的自动化解决方案提高工作效率

d)提高弹性:减少硬件故障导致的停机时间

考虑到云影响核心业务和关键 KPI(业务收入和利润)的多种方式,说采用云只是众多可用 it 选择之一是不明智的。如果处理得当,它可以为企业带来一些真正的核心业务利益。

一个有趣的例子是网飞,以及它如何使用云来推动其核心业务。详细信息可在 AWS 网站上找到:

https://AWS . Amazon . com/solutions/case-studies/网飞案例研究/

有哪些不同的云迁移策略?

一旦您决定要开始向云迁移,那么在将您的技术环境向云迁移时,您有哪些不同的选择?(请注意,以下策略是从应用程序组合的角度来看的,它最终会影响对运行应用程序的底层基础架构的选择)

a) 保留:一些应用程序可能过于复杂,无法迁移到云(esp。在短期到中期内),所以最好不要碰它。在大型遗留应用程序的情况下,这是最真实的

b)丢弃/整合:一些应用程序可能不再需要(例如,另一个应用程序已经提供的功能)。这些应该从应用程序组合中丢弃

c)主机转移(提升和转移):在大型传统迁移场景中,组织希望快速扩展其迁移以满足业务需求,大多数应用程序都需要进行主机转移。借助工具,可以极大地自动化重新托管,并带来一些立竿见影的效果(但是,不具备云原生应用所具备的长期优势)

d)重新*台:这种方法进行了一些云(或其他)优化,以获得一些切实的好处,但您不会改变应用程序的核心架构。

e)重新架构(云原生):这是由增加功能、规模或性能的强烈业务需求所驱动的,否则在应用的现有环境中很难实现这些功能、规模或性能

f) 回购:这意味着完全放弃你内部应用程序,从其他提供商那里转移到 SaaS 模式

任何云迁移策略都不必只是其中之一。根据产品组合中不同应用的不同需求,可以采用这些策略的组合。

典型云迁移计划的执行阶段有哪些?

如果您决定迁移,您如何计划迁移,涉及哪些不同的阶段?

a)根据您试图通过云迁移计划实现的目标,评估上一节所述的各种迁移策略

b)评估迁移准备情况:此包括查看您的投资组合中的所有应用程序,并针对不同的策略对它们进行评估(例如,重组评估包括查看不同应用程序的代码级别细节,以了解与其他应用程序的依赖性、接口数量等。).

c)确定机会:任何迁移都应该包括在进行实际迁移之前创建业务案例,以确定潜在收益(和所需投资)。

d)迁移:一旦业务案例建立,根据最终确定的策略进行实际的迁移。

e)验证:进行迁移后测试/检查,以验证迁移已按计划完成,没有中断用户旅程,达到了预期的性能水*等。

f)优化:确保云成本得到优化,以最大限度地从您的迁移计划中获益,这一点非常重要。通常对于新的云采纳者来说,这可能是一个很大的挑战,因为他们关注的是保持一段时间的正常运行,并确保云迁移后业务正常运行。然后,他们开始考虑成本合理化(例如,评估计算实例、存储实例等的数量。)用于现有的云设置。

组织面临的典型挑战是什么?他们如何克服这些挑战?

描述了采用云的所有优势、不同的战略和阶段后,同样重要的是要认识到这不是一项简单的任务(尤其是对于那些多年来一直在使用大量传统设置的大型企业)。企业在采用云的过程中面临一些常见的挑战,例如

  • 正确的云知识:理解云的细微差别是最大的障碍之一。通常我们会尽量避免做我们不理解的事情——这是基本的人性。因此,不管云能给企业带来多大的上升潜力,都有一种继续照常经营的趋势。
  • 对变化的恐惧:人们通常害怕变化——“如果事情停止运转了怎么办?”、“实现的收益不如预期怎么办?”。
  • 理解执行之路:像“我从哪里开始?”这样的问题,“路线图是什么?”“时间框架是什么?”、“我以什么顺序移动应用程序”、“我从哪个提供商选择?”等等。在决策者的头脑中是很常见的。

好消息是,在生态系统中可用人才的正确组合的帮助下,可以解决大多数这些挑战,以支持组织的转型之旅。人才组合通常是以下因素的组合

  • 内部人才:您组织中了解云并帮助定义云采用战略的人员。
  • 云提供商:比如亚马逊(Amazon)、谷歌(Google)、微软(Microsoft)。
  • IT 服务提供商他们带来急需的公正观点和客观性(例如选择合适的云提供商),并帮助组织规划/执行云采用计划的不同阶段。

那么,您应该何时开始您的云采用之旅呢?

用马克·吐温的话来说——“二十年后,你会因为你没有做的事情而比因为你做了的事情更失望,所以扔掉缆绳,远离安全的港湾,在你的帆上抓住信风。探索,梦想,发现。”

我也写关于财务规划和投资的话题。在我的网站上查找更多信息 Getmoneyinsights

未雨绸缪的云 API

原文:https://towardsdatascience.com/cloud-apis-for-a-rainy-day-f4f06468346?source=collection_archive---------41-----------------------

使用 Wolfram 语言编写、部署和共享计算从未如此简单

尼克·舍尔巴特在 Unsplash 上的照片

很久以前,我有幸徒步旅行到爱尔兰西部,靠*戈尔韦,穿过康尼马拉国家公园。景色非常壮观,但真正吸引我注意的是不断变化的天气模式。在一天的徒步旅行中,出现多次晴雨循环并不罕见,很多天还会出现午后彩虹。

位于大西洋附*,一股来自墨西哥湾和加勒比海的暖流。这使得爱尔兰的气候比基于纬度的预期要温和,也限制了全年的极端温度波动。但是爱尔兰经常下雨。

为了了解爱尔兰的降雨量,我访问了爱尔兰气象局的网站。他们提供当前的天气信息和历史数据。从历史数据页面,我下载了一个包含 1850 年至 2010 年时间序列数据的数据集。该数据集包括爱尔兰全国 25 个气象站的降雨量。

在这个故事中,我将讨论如何使用这些数据并创建一个可调用的 API 来可视化这些数据。

每个工作站都有自己的 CSV 文件。这是戈尔韦大学学院电视台的一个样本:

(图片由作者提供)

这个原始数据最好用 Wolfram 语言中的一个 TimeSeries 对象来表示。我们从 cvs 表达式中获取数据字段,用 DateRange 生成每月日期,并构造时间序列(快速注意:在本文结尾提供了一个包含完整代码的笔记本):

rain = Flatten[csv[[4 ;;, 2 ;;]]];dates = DateRange[
 DateObject[{1850, 1}, "Month"],
 DateObject[{2010, 12}, "Month"]];ts = TimeSeries[Thread[{dates, rain}]]

使用这个时间序列对象,我们可以创建一个可视化函数:

(图片由作者提供)

这使得我们可以绘制任何时间范围的图,例如,20 世纪 50 年代:

(图片由作者提供)

此时,将该功能部署为 API 是非常容易的。我们定义了一个 APIFunction ,它定义了 URL 参数和函数参数之间的联系:

api = APIFunction[{
 "y1" -> "Integer", "m1" -> "Integer", 
 "y2" -> "Integer", "m2" -> "Integer"},
 ExportForm[GalwayRainPlot[{#y1, #m1}, {#y2, #m2}], "PNG"] &
];

接下来,我们使用 CloudDeploy 将这个 APIFunction 部署到一个 CloudObject:

CloudDeploy[api, CloudObject["GalwayRainPlot.api"], Permissions -> "Public"]

此时,API 是公开可用的。您可以更改权限设置,使您的 API 更加私密。这里有一个的例子,称之为 20 世纪 60 年代高威降雨量的 API。

(图片由作者提供)

当然,你可以按照这个工作流程,与云中的其他人共享任何计算。只用几行代码就可以将任何计算部署到 Wolfram 云的概念非常强大。有关更多信息,请查看以下指南页面:

[## 创建即时 API-Wolfram 语言文档

Wolfram 语言具有在 web 和其他地方创建和部署 API 的内置功能…

reference.wolfram.com](https://reference.wolfram.com/language/guide/CreatingAnInstantAPI.html)

至于在爱尔兰徒步旅行,我希望有一天能再去一次。但是如果你打算去,一定要带一件雨衣和多一双干袜子!

由康纳无法无天——【https://www.flickr.com/photos/conchur/28657824895/】T4,CC 由 2.0,https://commons.wikimedia.org/w/index.php?curid=86922866

这款笔记本的完整代码可从以下网址获得:

[## 戈尔韦雨图

用 Wolfram 语言进行计算思维

www.wolframcloud.com](https://www.wolframcloud.com/obj/arnoudb/Published/GalwayRainPlot-01.nb)

云金融面试准备和技巧

原文:https://towardsdatascience.com/cloud-finance-interview-prep-and-tips-8fe2d6f6b017?source=collection_archive---------39-----------------------

克里斯蒂娜@ wocintechchat.com 在 Unsplash 上的照片

在云金融工作,经常会遇到这样的问题:“面试要做哪些准备?”。这个问题对我来说很难回答,因为在云金融领域工作与我接触过的其他金融角色非常不同。然而,本文旨在为有兴趣申请云财务经理职位的人提供一些重要的学习资源和技巧。

首先,有很多“云”要覆盖,因为空间非常大,无所不包。清单包括:技术技能、行业趋势、云经济、硬件细节、工程管理/合作伙伴关系、核心财务技能以及许多其他主题。幸运的是,所有这些话题都非常有趣,关于它们的数据可以通过 YouTube 或免费培训网站公开获得,如 EdX、Linkedin Learning 或 AWS/Azure 培训网站。

第二,把这些新话题看作是一个增长和展示你认知灵活性的绝佳机会,而不是把它们看作进入的障碍。今天,云金融行业的许多人都没有预装这方面的知识,而云是如此之大,以至于我们都在不断地学习它。这是令人兴奋的挑战!

为什么分享面试准备对我很重要

鉴于这一小型(但快速增长的)行业所需的知识/技能相对小众,可能会偏向于具有云计算/科技金融背景的候选人,他们有在职经验,回答问题时更具技术性。总的来说,我认为这是不公*的,因为它可能会对已经处于孤立网络中的人产生偏见。这种偏见可以通过改变面试官问的问题来应对和纠正,更多地关注行为问题,鼓励更多的通用财务技能,以及从不同的部门/背景招聘员工。

在消除面试偏见的同时,我相信通过提供更多的面试准备资源并使其易于获取,也有助于提升技能水*。为此,我分享了一些我最喜欢的云金融面试准备,以帮助最大限度地减少群体偏见的影响,并让你跟上速度。

警告:我不是招聘经理,我的信念不能反映我的公司。这些是我从非科技金融工作转向云计算工作时使用的有用培训和技巧。除了这些培训和良好的经历,我还有幸进入了这个行业。

第一:要学习、准备和准备的关键技术主题

数据中心如何工作 —云金融的核心是支持运行“云”并提供基于云的产品和服务的数据中心的扩展、运营和优化。因此,了解什么是数据中心以及它是如何工作的,可以为您的工作提供很多信息。每封电子邮件、每台机器、API 调用、面部识别和 SaaS 订阅都可以追溯到 DC(数据中心)中的一台物理机器。深刻理解基础设施成本将有助于理解下游成本。因此,浏览数据中心是我用来学习的最有用的工具之一。我最喜欢的是谷歌数据中心内的视频漫步——YouTube(5 分钟)。

什么是虚拟机——如今云提供商销售的最大产品是虚拟机。这是在理解数据中心和物理成本的基础上更上一层楼,对于掌握云经济学至关重要。以下是我最喜欢的教程,按深度递增排序。

  • 什么是 (1 分钟)中的虚拟机
  • 虚拟化基础知识 (2 分钟)
  • 在白板上解释虚拟化 (5 分钟)

云经济学基础知识 —您应该能够回答“迁移到云有什么财务好处?”为你的立场辩护。虽然答案可以很清楚地列出来,但这是你将与客户、合作伙伴和同事讨论的更有趣、更深奥的话题之一。

  • 简单的答案=将资本支出转化为运营支出在云经济学— YouTube (5 分钟)中有清晰的总结
  • 明智的答案=一些硬成本节约+灵活性、安全性和创新的无形价值。这些在 IBM 关于云迁移的快速文章 (2 分钟)和视频中得到强调。(5 分钟)
  • 超级深度探索= 云经济学——YouTube(50 分钟)

观看云主题演讲 —这对于把握行业热点话题和趋势非常重要。类似于苹果的开发者大会,这些主题活动是云首席执行官发布闪亮新产品并强调他们在业务增长部分的成功的地方。这些是更长的事件,所以请随意快进,以 1.5 倍观看。注意:每次有人说 AI 时,喝一杯饮料来玩这个游戏。

  • AWS re:Invent 2019——Andy Jassy 的主题演讲——YouTube(2 小时)
  • 塞特亚·纳德拉的视觉主题演讲|微软 Ignite 2019—YouTube(1 小时)
  • 此外,如果你了解你申请的群体,观看关于该行业的分组会议,了解关键术语,并观看产品演示。

上网站 —最后,花点时间浏览公司网站,了解他们的产品和服务。这些都是公开信息,所以招聘经理可能会不小心使用一些行话或产品名称(EC2、计算、认知服务、容器、S3、红移),这些可能是常见的行业知识,但不是常见的你的知识。一次快速的现场参观会帮助你用一个会意的点头代替困惑的畏缩。

  • 云计算服务|微软 Azure
  • 亚马逊网络服务(AWS)——云计算服务
  • 如果你正在制作抽认卡,下面包括一些关键术语和速记定义的列表(没有必要),但是查看更多的词汇表中的一个。

接下来:面试技巧和行为问题

展示你的学习能力 —云是一个快速增长的空间,你将要做的许多工作都是新的(对你和这个世界来说)。你需要展示一些处理模糊任务的能力,并成为一个无所不知的人。

  • 分享你以前的学习和发展潜力的例子是一个加分。
  • 关于这里的背景,下载 LIBBY app 并阅读 Carol Dweck 的增长思维,这是过去几年的热门商业书籍,也是 Satya 的书的灵感来源。

关注你不同的经历 —你独特的背景和思维方式是一笔财富!技术和云可能会有很多狭隘的想法,所以让外界的人加入进来带来新的视角总是很重要的。

  • 例如,云投资是非常资本密集型的,云产品有许多不同的产品 COGS 模型。这与标准软件或 SaaS 公司有着显著的不同,因此在雇佣有资本管理经验或产品知识背景的人时会给予额外的奖励。即使产品体验来自于卖肥皂,产品成本就是产品成本。

温习你的技术技能 — Excel power pivot、Power BI 和基本的数据库技能不是必需的,但它们确实有所帮助。能够用技术来管理技术是未来的技能。在快速发展的企业中,运营工作不断增加,这意味着自动化、简化或消除任务的技术技能具有巨大的价值。如果你把这些技能或其他技能带到聚会上,一定要强调它们帮助你产生的影响。如果没有,可以参加一些免费的在线课程和 youtube 培训,继续充实你的简历。

  • 在 excel 中可视化数据 —来自 edX
  • Power BI via linkedin 学习!
  • 超越权力中枢——这可能有些矫枉过正

人际技能非常重要 —这里有很多工程师,所以展示影响有技术头脑的人的能力会有所帮助。这通常表现为成为一名优秀的沟通者,或者能够简单地总结复杂的话题。

  • 准备一些你帮助商业伙伴进行清晰分析的例子,或者你如何影响他们的想法。
  • 读《如何赢得朋友和影响他人》从来没有不好的时候。(还是听听吧)

感谢阅读,更多 AI 财经内容,在 Linkedin 上关注或添加我 David Hall | LinkedIn

数据科学家的云基础

原文:https://towardsdatascience.com/cloud-foundations-for-data-scientists-e9d0c14fc98a?source=collection_archive---------38-----------------------

云技术基础及其在数据科学中的应用指南

在 Unsplash 上由 C Dustin 拍摄的照片

介绍

如今,越来越多的公司开始在基于云的环境中开发和部署应用程序。云计算的主要动机之一是,它消除了与设置和管理所用硬件和软件相关的所有问题。这是通过远程租用云提供商维护的数据中心的计算机资源来实现的。

通过这种方式,公司和个人可以远程使用不同云提供商提供的硬件和软件设置以及工作流,而不必担心购买设备、设置不同的环境以及长期维护它们。因此,使用这种方法,用户可以专注于开发和部署他们的服务(如网站、数据库、应用、数据分析),而不必考虑任何可能的开销。最终,这可以导致更快的/持续的开发和提高的客户满意度,符合共同的 DevOps(开发-操作)原则。

云服务背后的主要思想非常类似于过去开发的任何其他类型的实用程序和基于订阅的系统。例如,在这些日子里,我们每个人都使用公用设施,如电/水/气,而不必担心这些系统是如何工作的,并交付给我们。我们关心的是我们的提供商能给我们一个可靠的服务,我们必须为我们使用多少或不使用所有这些服务付费。

正在使用的一些主要云提供商包括:

  • 微软 Azure
  • 【亚马逊网络服务(AWS)
  • 谷歌云*台(GCP)

所有这些不同的提供者,对于所提供的服务都有他们自己的术语,但是他们都共享本文中概述的相同原则。

云概念

每个不同的云提供商都能够提供各种各样的服务和应用,这些服务和应用可以分为四种主要类型:

  • 计算: 虚拟机、容器等…
  • 联网: 安全服务使不同的服务相互交互。
  • 存储: 为应用程序存储任何类型的文件或数据库的空间。
  • 分析: 数据处理和可视化工具,用于从存储的数据中分析和创建洞察。

为了降低资源租用成本并充分利用可用硬件,云服务严重依赖虚拟化技术,如虚拟机和容器。

虚拟机(VM)

虚拟机是为了模拟计算机硬件而创建的(图 1)。可以创建虚拟机的不同实例来同时在一台计算机上运行,每个实例都有一个首选的操作系统、内存和存储分配。一旦创建了虚拟机,就可以使用桌面客户端远程访问,并像任何其他物理计算机一样使用(例如,安装程序、运行应用程序等)。这样,通过将单台计算机用作不同计算机的集合,可以最大限度地利用硬件。

图 1:虚拟机基础设施

在云中使用虚拟机的另一个巨大优势是我们的系统可以变得易于扩展。因此,如果需要比计划更多的资源来完成项目,云提供商可以即时提供额外的内存和存储能力(从而减少部署中任何可能的停机时间)。

此外,如果我们希望确保我们的服务始终运行,也可以创建一个备份虚拟机,在原始虚拟机出现任何问题时自动运行。当在网站或应用程序等服务上添加新功能时,这可能特别有用,我们希望首先向一部分受众提供服务的新版本,以便获得某种反馈,了解变化是否会给服务带来积极影响(A/B 测试)。

容器

虚拟机的一个问题是每个虚拟机都有自己的操作系统。例如,如果我们有一个复杂的应用程序,它的一些操作需要 Windows,而另一些操作需要 Linux,那么我们就需要运行两个独立的虚拟机,并使这两个虚拟机相互通信。因此,这导致了可以避免的资源开销。此外,当在团队中开发一个应用程序时,当从不同的环境转移时,也可能会出现一些包依赖冲突(例如,“它在我的机器上工作”)。

为了避免这些类型的问题,已经构思了容器。容器,将应用程序及其依赖项打包在一起,并将它们部署到容器主机上(容器主机充当抽象操作系统的服务)。通过这种方式,现在可以避免由于拥有多个操作系统而产生的内存开销,并且与使用虚拟机在单台机器上运行相比,可以在单台机器上运行更多的容器。此外,现在我们的应用程序可以轻松地在不同的操作系统之间移动,而不必考虑管理底层的依赖关系。

虚拟机虚拟化硬件,而容器虚拟化操作系统[1]。

为了创建容器,最常用的服务之一是 Docker (图 2)。

图 2:容器基础设施

创建复杂系统的最有效方法之一是将它们分成不同的容器应用程序,每个应用程序都有不同的角色。例如,如果我们正在做一个网站,将网站分成三个主要容器是一个好主意:前端、后端和数据库。每当我们想要引入新的特性时,我们可以更新我们感兴趣的容器,而不必接触其他的容器。随着我们向应用程序添加更多的组件,应用程序的复杂性增加,可能需要更多的容器。为了有效地维护和组织集装箱,Kubernetes 等服务应运而生。

Kubernetes 的开发是为了使基于容器的应用程序能够在基于云的环境中轻松伸缩。Kubernetes 可以提供的服务包括:

  • 优化容器之间的工作负载,并根据需求扩展所需的资源数量。
  • 如果容器中有任何错误,可以创建一个新实例来替换它。
  • 如果在一个新特性发布后,出现了一些问题,可以很容易地回到我们应用程序的前一个版本。

利用 Kubernetes 来编排我们的应用程序,可以让我们遵循 DevOps 实践,例如 【持续集成】持续交付(CD) 。持续集成的主要焦点是确保代码变更与当前的代码信息结构完美集成,而持续交付的焦点是使代码库始终准备好进行部署(能够自动通过任何适当的构建和测试机制)。

云部署和服务类型

一旦在云中创建了应用程序,就可以使用三种不同的云部署方法进行部署:

  • 公有云: 在这种场景下,所有使用的资源都由云提供商管理和维护。这是目前最具成本效益和最常见的云部署方法。
  • 私有云: 常用于需要高安全性和严格法律合规性的工作任务。在这种情况下,企业可以创建本地云信息结构,从而减少对云提供商的控制(该选项要求企业预先购买自己的硬件设备)。
  • 混合云: 在本例中,应用程序的一部分可能由我们的云提供商管理,而其他部分则由私有企业管理(混合了公共云和私有云部署方法)。

最后,云上有三种不同类型的服务可用:

  • 信息架构即服务(IaaS): 在 IaaS 类型的服务中,我们租用一些硬件并自行配置,以便完成不同的任务。
  • *台即服务(PaaS): 在 PaaS 类型的服务中,我们租用一些预配置的硬件,并使用它来测试和开发一些应用程序或存储业务数据。
  • 软件即服务(SaaS): 在 SaaS,云提供商开发并维护一些软件应用程序,然后使用订阅模式进行分发。SaaS 程序的两个例子是 Skype 和 Office 365。

利用这种基础设施,就有可能轻松地将任何类型的基于软件的业务迁移到云中。

希望你喜欢这篇文章,谢谢你的阅读!

联系人

如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

  • 领英
  • 个人网站
  • 中等轮廓
  • GitHub
  • 卡格尔

文献学

[1]微软学习,Azure 基础。访问:https://docs . Microsoft . com/en-GB/learn/paths/azure-fundamentals/

云运行:谷歌云文本到语音 API

原文:https://towardsdatascience.com/cloud-run-google-cloud-text-to-speech-api-dff308665c70?source=collection_archive---------33-----------------------

使用 Google Cloud Run 提供智能 API

Google 文本到语音转换 API 服务于云运行

目录

  • 在谷歌云上运行
  • 在谷歌云文本到语音转换 API 上
  • 申请代码
  • docker file
  • 使用云构建构建映像,并上传到容器注册表
  • 使用云运行部署容器
  • 结论

在谷歌云上运行

Google Cloud Run 于 2019 年 11 月左右正式发布。它提供了一个完全托管的无服务器执行*台,通过 HTTP 驱动的容器来抽象无状态代码部署的基础设施。Cloud Run 是一个 Knative 服务,利用相同的 API 和运行时环境,可以构建基于容器的应用程序,这些应用程序可以在任何地方运行,无论是在 Google cloud 或 Anthos 上部署,还是在云上。作为一个“无服务器执行环境”,Cloud Run 可以根据正在运行的应用程序的计算需求进行扩展。应用代码的即时执行、可扩展性和可移植性是 Cloud Run 的核心特性。

关于谷歌云文本到语音转换 API

Google Cloud Text-to-Speech 公开了一个 API 来从文本合成听起来自然的语音。文本及其相应的语音输出可以是不同的语言,包括英语、法语、俄语和阿拉伯语,仅举几例。这种最先进的 API 为将逼真的高保真文本到语音交互集成到最终用户应用程序中提供了多种机会。

图 1: 谷歌云文本到语音转换 API。

用于身份验证的服务帐户

要使用 Google Cloud 文本到语音转换 API,我们必须创建一个服务帐户密钥用于身份验证。服务帐户对应用程序或虚拟机(VM)进行身份验证,以在 Google 云*台上进行授权的 API 调用。

使用以下链接为云文本到语音转换 API 创建服务帐户。-> 云语音合成服务账号。

图 2: 为文本到语音 API 创建一个服务账户。

使用以下参数:

  • 服务账号:新服务账号。
    -服务账户名称:【指定名称】。
    -角色:不需要角色。
    点击Create without role,将包含密钥的 JSON 文件下载到您的电脑。

在创建并下载您的安全服务帐户密钥之后,将环境变量GOOGLE_APPLICATION_CREDENTIALS设置为包含您的服务帐户密钥的 JSON 文件的路径(参见图 3)。

应用程序代码

这个项目的代码托管在一个 Github 库中,链接为https://github.com/dvdbisong/text-to-speech-cloud-run。Python 文件app.py包含了通过 HTTP 访问容器化应用程序的逻辑。代码在text-speech目录下,可以在谷歌代码编辑器(云壳的一个组件)上查看。代码有注释,容易理解。

图 3:Python 文件 app.py 中的应用代码。

将服务账户密钥上传至应用程序

上传服务帐户密钥以验证调用文本到语音转换 API 的应用程序。上传后,将密钥放在text-speech文件夹中。

图 4: 上传服务账号密钥。

文档文件

docker 文件概述了创建容器映像的方法。这个项目的 Dockerfile 文件基于官方的 Python 图像。Dockerfile 指定了构建映像时将安装在容器上的相关包。Flask 应用程序使用gunicorn包来公开访问容器内应用程序的端口。在谷歌代码编辑器上,导航至text-speech查看 docker 文件。

图 5: Dockerfile 为应用程序容器构建映像。

为了完整起见,下表总结了 Dockerfile 文件中使用的命令。

+------------+-----------------------------------------------------+
| **Command**    | **Description** |
+------------+-----------------------------------------------------+
| **FROM**       | The base Docker image for the Dockerfile.           |
| **RUN**        | It executes commands on top of the current image as | |              new layers.                                         |
| **COPY**       | Copies files from the local machine to the          |
|              container filesystem.                               |
| **CMD**        | Specifies the command to execute when running the   |   |              container. This command is overridden if another    |   |              command is specified at runtime.                    |
| **WORKDIR**    | Sets the working directory of the container.        |
| **ENV**        | Set Environment variable as a key-value pair that   | |              will be available in the container after building.  |
+------------+-----------------------------------------------------+

使用云构建构建映像,并上传到容器注册中心

Cloud Build 是一项 GCP 服务,用于自动化代码工件的部署,作为 CI/CD 管道的一部分。这里,Cloud Build 用于构建 Docker 映像,作为一系列构建步骤,完成的映像被推送到 Google 容器注册表。

以下代码使用云构建构建映像,并将映像上传到容器注册。从目录text-speech运行这段代码。

gcloud builds submit — tag gcr.io/ekabasandbox/ebisong-text-to-speech

其中,

  • ekabasandbox为 GCP 项目 ID,
  • ebisong-text-to-speech为图像名称。
    确保根据需要修改这些参数。

图 6: 使用云构建构建映像。

使用云运行部署容器

要将存储在 Google 容器注册表上的容器部署到云运行,请执行以下代码:

gcloud run deploy --image gcr.io/ekabasandbox/ebisong-text-to-speech --platform managed

选择要部署托管云运行计算的地区(例如美国中心 1)。当提示输入服务名时,按Enter接受缺省值,并在询问是否允许未认证调用时响应y (yes)。

当容器部署到云运行时,点击 URL 查看正在运行的应用。
你可以继续探索应用了。

图 7: 云 Run 上托管的文本到语音应用。

结论

本文提供了一个在 Cloud Run 上部署进行 API 调用的应用程序的示例。它提供了使用 Google Cloud 文本到语音转换 API 和使用 Flask 运行简单 web 应用程序的示例代码,Flask 被打包成一个容器并部署在 Cloud Run 上。整个应用程序在 https://github.com/dvdbisong/text-to-speech-cloud-run 是开源的。最后,确保删除云运行服务,以及云容器注册表中不再需要的图像。

云 vs 边缘:工业制造的困境

原文:https://towardsdatascience.com/cloud-vs-edge-an-industrial-manufacturing-dilemma-44472f01cf45?source=collection_archive---------37-----------------------

当涉及到为应用程序部署人工智能时,制造商不仅需要深入思考开发什么,还需要思考设想的人工智能系统的物理化身。

图片由 Flickr 提供

通常,深度学习是人工智能应用的子种类,由于在大规模部署现实世界工作人工智能方面的成功,在过去几年中获得了关注,它是作为云支持的软件即服务(SaaS)*台提供的。

这对于许多制造商来说是行不通的,他们倾向于避免依赖云的软件带来的安全性和延迟问题,以管理日常复杂的工作流程。

幸运的是,技术在不断发展,在 2020 年,这个行业将面临挑战,深度学习和人工智能的范式变革将有利于制造业的需求。

但首先,让我们理解我们所说的深度学习是什么意思,以及硬件——云和边缘——如何发挥如此重要的作用。

引擎盖下:艾

人工智能的通用名称隐藏了各种方法,从在分布式云基础设施上处理数据的巨大人工智能模型,到在小型处理器上分析和挖掘数据的小型边缘友好人工智能。

让我们简化一下,把人工智能分成两个主要的类别,重型和轻型。重型人工智能需要大型计算基板来运行,而轻型人工智能可以在小型计算足迹上做重型人工智能能够做的事情。

GPU 等商用处理器的引入,以及后来它们的便携性,使得将人工智能/深度学习/DNN/神经网络算法带到边缘在技术上和经济上都是可行的。

从直觉上来说,边缘人工智能计算是一个伟大的想法:通过将一些生物计算转移到外围,从我们的传感器(例如,眼睛,耳朵,皮肤)到我们的器官和肌肉,大自然母亲已经在亿万年的进化时间里发现了这一点,世界上的大部分“数据”都是在有机体探索他们的世界时产生的。

同样,制造商利用这一特性也很有意义。以工业机器中的质量控制摄像机为例,典型的机器每秒可以处理几十帧,每小时可以处理数百个产品。对于这些相机来说,将所有捕捉到的帧运送到一个中央云进行人工智能处理将是极其浪费和低效的。相反,更智能的策略是在边缘处理它们,并偶尔将相关的、非常重要的帧(例如,具有可能的产品缺陷的帧)发送到中央位置或人工分析师。

带宽、延迟和成本等问题决定了边缘处理的需求。

不过有一个重要的警告:在小型计算边缘运行人工智能——行话中称为推理或“预测”(例如,我看到一个正常的产品与一个有缺陷的产品)不同于在边缘学习。也就是说,使用获得的信息来改变、改进、校正和提炼边缘人工智能不仅困难,而且对于制造商来说,能够快速定制他们的人工智能以实现灵活性是极其重要的

边缘(与云)学习:现实

图片由 Flickr 提供

我第一次意识到与 NASA 的巨大差异是在 2010 年,当时我和我的同事实施了一个小型大脑仿真来控制一个类似火星漫游车的人工智能设备,该设备需要能够在边缘运行和学习。

对于美国宇航局来说,机器人能够完全独立于地球上任何可用的计算能力来学习“新事物”是非常重要的。数据瓶颈、延迟和大量其他问题使得探索自那时以来开发的不同种类的人工智能变得至关重要。能够消化和学习的算法——即,根据可用数据调整人工智能的行为——不需要大量的计算能力、数据和时间。传统的深度神经网络(DNN)模型无法做到这一点。

我们建立了这样的人工智能,我们称之为终身深度神经网络(终身-DNN ),因为它有能力在一生中学习(而传统的 DNN 只能在部署前学习一次)。

我们不知道这个人工智能在地球上会比在火星上更有用。

工业制造商边缘学习的力量

图片由 Flickr 提供

边缘学习解决了当今人工智能实现的一个燃眉之急:它的不灵活性和缺乏适应性。当可用时,人工智能算法可以在大量数据上进行训练,如果所有数据都被事先捕获用于训练,则人工智能算法相当健壮。不幸的是,这并不是制造业的工作方式,因为在生产线上不可预测地出现少数不良产品之前,数据(例如不良产品)通常是不可用的。人工智能需要能够快速利用这些不同的、罕见的数据来适应,但传统的 DNNs 无法在这些现实条件下工作。

诸如 lifetime-DNN(一种能够在计算边缘(如 CPU)进行学习的深度学习范式)等新方法使人工智能相机不仅能够理解向它们提供的数据,而且能够适应和学习。例如,在上述工业机器中,边缘学习将使其数十个摄像机能够学习新产品类型和缺陷,在现实世界的场景中,新产品一直在引入,新的、以前看不到的缺陷出现在生产车间。

不可能存在可以在新创造的产品上预先训练的人工智能。数据根本不存在:AI 需要现场训练!

通过边缘学习,人工智能可以学习识别新的缺陷,而不必从头开始对人工智能进行“重新编程”,直接在需要的边缘进行。

在边缘学习的人工智能是一种范式转变技术,最终将使人工智能真正服务于其目的:将智能转移到需要的计算边缘,其速度、延迟和成本使每个设备都可以负担得起。

这将使制造商能够廉价、快速、直接地在关键位置(边缘)构建其工业 4.0 战略的基石。

云 vs 设备上 AI?也许介于两者之间!

原文:https://towardsdatascience.com/cloud-vs-on-device-ai-maybe-something-in-between-4242af8d3b5c?source=collection_archive---------48-----------------------

只有在需要放在云上的时候,才把你的 AI 放在云上。

云还是设备智能?或者介于两者之间?在这篇短文中,我们展示了一种简单的移动设备和云之间的协作方法如何帮助您将 50%的宝贵数据卸载到云中,同时实现相同的云级准确性。

云与设备上人工智能:利弊

让我们回顾一下云与设备上人工智能的利弊。设备上的人工智能通过提供更好的隐私和独立于远程资源而使我们受益,但由于缺乏强大的计算资源,该模型可能不是非常准确。另一方面,云人工智能通过提供比移动设备上托管的模型更准确的模型而使我们受益,但需要持久的网络连接,这可能使其不可靠。

云与设备上人工智能的利弊。作者照片。

只在需要使用云 AI 的时候使用云 AI

该方法基于对神经网络的置信度设置阈值。如果网络对它的预测有信心,那么我们就没事了,如果没有,我们需要向更智能的云托管模型寻求帮助!假设您想要对 ImageNet 数据集的 1000 个对象进行分类。假设我们使用 MobileNetV2 作为移动设备上的轻量级模型,使用 ResNext101 作为我们最精确的(但是很大!)云上的模型。方法很简单:

嘿手机!如果你对自己的预测不确定,就把推理卸载到云上吧!

如果你对自己的预测不确定,就把推理卸载到云上吧!作者照片。

我们将 softmax 概率视为预测的置信水*,并在移动预测的置信水*上设置阈值。我们在 ImageNet 上使用 torchvision 预训练模型,对于 MobileNetV2 和 ResNext101 分别有 71.88%和 79.31%的准确率。

该曲线示出了分类准确度相对于决定在移动设备上推断的测试输入的百分比。作者照片。

曲线解读:如果所有推理案例都在云服务器上进行,那么准确率将为 79.31%,这是 ResNext101 的准确率。如果所有的推理案例都在移动设备上进行,那么准确率将是 71.88%。如果 49.88%的推理案例在移动设备上执行,其余的在云上执行,那么我们仍然可以实现 79.31%的云级准确率。

简而言之,我们并不总是需要云服务器来为我们的应用程序添加人工智能。我们可以在弱设备上托管小模型,并在预测不确定的情况下调用云服务器。校准神经网络的置信水*可以进一步增加本地执行的推理案例的百分比。

作者要感谢 Mohammad Saeed Abrishami 在这篇博文中的帮助。

Cloudera 认证数据分析师(CCA 159)-指南

原文:https://towardsdatascience.com/cloudera-certified-data-analyst-cca-159-a-guide-cf6fe686e0e6?source=collection_archive---------32-----------------------

照片由威廉·布特在 Unsplash 拍摄

Cloudera 是数据分析领域最知名的公司之一。他们提供服务,尤其是在大数据领域,被认为是主要参与者之一。几乎所有的职业都有各自领域的相关证书。认证本质上是证明你是该领域的专家。来自最高权威的认证在简历上很重要,招聘人员总是试图找到有认证的人。像所有其他科技公司一样,Cloudera 提供了一系列大数据分析相关技术的认证。他们的考试都是全动手,在业内口碑极好。

Cloudera 认证数据分析师(CCA 159)是主流大数据工具(如 Apache Hive 和 Apache Impala)的基础考试。本指南适用于任何准备注册 CCA 159 考试的人。我假设您已经对 Cloudera 的生态系统有了一些经验。如果没有,我会鼓励你练习使用 Cloudera 的 quickstart VM ,可以在他们的网站上下载。最后我会写下一些免费的资源做准备。我按照我认为最适合准备的顺序写下这些步骤。

为了了解你将要进入的领域,请访问 Cloudera 网站上的 CCA 主页并仔细阅读。你将了解教学大纲、考试时间等。需要记住的一些重要事项是:

  • 考试代金券价格 $295 (自购买之日起一年内有效)。
  • 持续时间为 120 分钟,您将在 Cloudera 提供的实时集群上解决8–12动手操作问题。
  • 通过标准为 70 % ,CCA 证书的有效期为两年。

Cloudera 创建了一个免费视频课程,告诉你所有关于各种 Cloudera 考试的信息,并提供备考技巧。在这些视频中也有一些简单的问题场景。只需在 Cloudera 大学注册,然后在 Cloudera: CertPrep 101 注册

请务必阅读 CCA 主页上的常见问题部分。你会得到任何问题的答案。如果您有任何进一步的问题,也可以发电子邮件至 certification@cloudera.com

我会建议在这个时候选择一个考试日期。这样你就会知道你有多少时间准备;所以,可以更好的规划。考试券可以用信用卡直接从 CCA 主页购买。只需点击购买按钮,将其添加到购物车并继续付款。

Cloudera 已经与examples local合作进行考试。所以,你也必须报名参加。很简单:你从 Cloudera 购买考试券,然后在exam local的监督下参加考试。记住你想。使用您在 Cloudera 网站上注册时使用的同一电子邮件地址。

通过 examslocal 选择考试的确切日期非常简单。搜索 CCA 认证,从下拉列表中选择 CCA 159,并选择所需的日期和时间。

****

CCA 考试是在线的,也就是说,你可以在任何地方使用带有网络摄像头和互联网连接的笔记本电脑参加考试。浏览 examslocal 上的FAQ和兼容性检查工具来检查您的笔记本电脑的兼容性。

备考

CCA 考试是实践性的,即你将执行考试中的任务,而不是回答多项选择题。对于 CCA 159,您必须拥有 Cloudera 生态系统的实际知识,特别是 Hive (HQL)、Impala (impala SQL)。你应该非常熟悉数据定义(DDL)、数据查询(DQL)、窗口函数、文件格式等概念。截止到教学大纲的最新变化,Cloudera 已经将 Sqoop 排除在 CCA 159 之外。考试相关信息可以在 CCA 159 主页找到。以下是一些免费的准备资源,供您参考:

  • itversity' s YouTube 播放列表(CCA 159——数据分析师)这是基于他们的 Udemy 课程。您可以免费观看视频,并使用 Cloudera 的 VM 进行练习。
  • Packt 还有一个 CCA 159 课程,可以通过 Oreilly 获得。虽然 Oreilly 是一个付费服务,但你可以从他们的免费试用中受益。
  • 样题可以看这个 YouTube 播放列表由 Hadoop pass。
  • 考试时只能从蜂巢和黑斑羚的官方文档中获得帮助。强烈建议了解如何使用该文档。所以,你必须练习使用它。
  • Cloudera 不为任何考试提供模拟考试设施。但是,有第三方网站提供模拟考试。这些都是付费服务,你应该在继续之前检查其真实性。

像这样的考试,时间管理是最重要的。大多数问题都很简单,但是你没有时间了,所以,请记住这一点。在考试期间把自己隔离起来,这样就没有外界的干扰,你可以真正集中注意力。参加考试后,您将在 3 天内通过电子邮件收到报告。成功完成后,将在一周内通过电子邮件收到数字证书和许可证号。

我希望这本指南能成为一个很好的资源。快乐学习!

Cloudera 的 CCA 175—2020 年更新:什么改变了,什么没有改变,以及如何准备考试

原文:https://towardsdatascience.com/clouderas-cca-175-the-2020-update-what-changed-what-didn-t-and-how-to-prepare-for-the-exam-716413ff1f15?source=collection_archive---------19-----------------------

Cloudera 的 CCA-175 认证的新版本删除了所有的遗留工具,并将重点完全放在 Apache Spark 上

Cloudera 的标志

几周前,Cloudera 重新推出了他们的 Spark 和 Hadoop 开发人员考试(CCA 175),更新了考试环境和一系列考试内容的关键更新。

考试继续采用动手操作的方式,包括一组 8 至 12 项基于性能的任务,考生需要在 Cloudera Quickstart 虚拟机上使用命令行执行这些任务,并可以访问大多数大数据工具(Hive、Spark 和 HDFS 都可以通过相应的命令访问)。

如果您计划在接下来的几周内参加考试,以下是您在备考时需要记住的关键要素以及最重要的工具和功能:

这都是关于火花

由于某种原因,Cloudera 一直在 CCA 175 考试中给出至少两个与 Sqoop 相关的任务,直到 2019 年的最后几个月。尽管 Sqoop 在过去十年开始时被广泛使用,但几年来它更多的是一种遗留工具,只有非常有限的几家公司仍然希望应聘者熟悉它。

幸运的是,Cloudera 接受了提示,从考试中删除了与 Sqoop 相关的任务。与 Flume 相关的任务(不那么频繁)也被删除,使得考试完全集中在大数据最重要的工具 Apache Spark 的使用上。

2020 版本的关键更新是,Spark 2.4 现在默认在考试环境中提供(而不是 Spark 1.6),可以通过 Spark Shell(对于 Scala)和 Pyspark(对于 Python)访问,或者甚至可以通过 Spark-submit(如果您更喜欢在执行考试任务时使用脚本)访问。Spark 2.4 引入了一组非常有用的功能,使得在考试中表现良好变得更加容易,尤其是在处理 Avro 文件时。

熟悉 Hive 和 HDFS

考试中的某些任务仍然需要阿帕奇蜂巢和 HDFS 的基础知识。主要是,您需要熟悉使用与数据位置(如果要求您创建外部表)及其格式相关的不同选项创建 Hive 表的过程。

此外,在某些时候,考生将需要使用 HDFS 命令(主要是lstail)来确定输入数据的存储格式,并初步了解他们将处理的数据。

考试的新版本肯定是面向 Spark 的,但不知道基本的 Hive 和 HDFS 命令会让你付出多项任务的代价,并最终阻止你获得证书。

读取、处理、写入

考试的任务其实是同一个问题的不同版本。您必须使用 Spark 以某种格式(主要是文本或拼花)从 HDFS 读取数据,通过过滤器、映射、聚合和连接对其进行处理,然后以不同的格式并使用某种压缩编解码器将其写回 HDFS。

掌握这一过程将保证你在考试时有一个愉快的体验,因为你会被要求在不同的数据集上做多次。

时间管理也是一个重要的因素,所以确保在考试开始时阅读所有的任务,然后立即开始你最熟悉的任务。

总而言之,Cloudera 摆脱了传统技术,设计了一种面向 Spark 的考试,这种考试与当今时代的数据工程任务相关。虽然该考试不需要处理优化或高级 Spark 知识,但它仍然是证明一个人熟悉 Apache Spark 的好方法。

基于聚类的图像分割-Python

原文:https://towardsdatascience.com/cluster-based-image-segmentation-python-80a295f4f3a2?source=collection_archive---------16-----------------------

个人电脑:Flickr

理解图像分割

最*,我在进行一项图像分类任务,首先我想在将图像输入模型之前,从图像中捕捉感兴趣的区域。我尝试了一种称为基于聚类的图像分割的技术,它帮助我将模型性能提高了一定的水*。让我们看看它是什么和一些样本代码做集群分割,你可以在底部找到 Jupyter 笔记本。

什么是图像分割?

想象一下,你要过马路了,你过马路之前做了什么?

首先,你看到道路的两边,以确定接*的车辆和其他环境物体,然后你对接*速度进行一些惊人的估计,并决定何时以及如何过马路。所有这些都发生在很短的时间内,这是多么不可思议。

  1. 我们的大脑捕捉道路两边的图像
  2. 它检测道路上的车辆和其他物体
  3. 不仅在此之前检测它确定它检测的每个对象的形状==图像分割

我们的大脑是多么神奇,通过确定不同物体的形状,它能够在同一张快照中检测到多个物体。

让我进一步解释,假设我们有图像分类模型,它能够以超过 95%的准确率对苹果和橙子进行分类。当我们输入一个既包含苹果又包含橘子的图像时,预测精度会下降。随着图像中对象数量的增加,分类模型的性能会下降。这就是对象定位发挥作用的地方。

在我们检测图像中的对象并对其进行分类之前,模型需要理解图像中有什么,这就是图像分割有所帮助的地方。它为图像中的对象创建了一个像素级遮罩,这有助于模型在更精细的级别上理解对象的形状及其在图像中的位置。

对象检测与图像分割 PC:中型

细分的类型有哪些?

图像分割大致分为两大类。

  1. 语义分割
  2. 实例分割

检测到的对象—语义段—实例段 PC: mc.ai

在第一幅图像中,我们可以看到被检测的对象都是男性。在语义分割中,我们认为所有这些像素属于一类,所以我们用一种颜色来表示它们。另一方面,在实例分割中,那些像素属于同一类,但是我们用不同的颜色表示同一类的不同实例。

根据我们使用的方法,细分可以分为许多更窄的类别。

  • 基于区域的分割
  • 基于边缘检测的分割
  • 基于聚类的分割
  • 基于 CNN 的分割等等。

正如我在开头所承诺的,在本文中我只给出一个基于集群的分段的例子。

什么是基于聚类的细分?

回忆一下你对聚类算法的理解。聚类算法用于将彼此更相似的数据点从其他组数据点中更紧密地分组。

现在想象一个包含苹果和桔子的图像。苹果里的像素点大部分应该是红/绿,和橙色的像素值不一样。如果我们能把这些点聚集起来,我们就能正确地区分每个物体。这就是基于聚类的分段的工作方式。现在让我们看一些代码示例。

from skimage.io import imread
from skimage.color import rgb2gray
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from scipy import ndimage# Scaling the image pixels values within 0-1
img = imread('./apple-orange.jpg') / 255plt.imshow(img)
plt.title('Original')
plt.show()

个人电脑:Flickr

对于我们的肉眼来说,图像中有五个颜色段

  1. 苹果的绿色部分
  2. 橙子的橙色部分
  3. 苹果和橘子底部的灰色阴影
  4. 苹果顶部和右部的亮黄色部分
  5. 白色背景

让我们看看是否可以使用 scikit-learn 中的 KMeans 算法对它们进行聚类

# For clustering the image using k-means, we first need to convert it into a 2-dimensional array
image_2D = img.reshape(img.shape[0]*img.shape[1], img.shape[2])# Use KMeans clustering algorithm from sklearn.cluster to cluster pixels in image
from sklearn.cluster import KMeans# tweak the cluster size and see what happens to the Output
kmeans = KMeans(n_clusters=5, random_state=0).fit(image_2D)
clustered = kmeans.cluster_centers_[kmeans.labels_]# Reshape back the image from 2D to 3D image
clustered_3D = clustered.reshape(img.shape[0], img.shape[1], img.shape[2])plt.imshow(clustered_3D)
plt.title('Clustered Image')
plt.show()

哇,真管用!!!我们能够将所有五个部分聚集在一起。这就是聚类分割的工作原理。

有许多先进的技术,如 Mask R-CNN,可以进行更细粒度的分割。让我们在其他文章中看到这些主题。我希望您现在对图像分割有了一定程度的了解。

你可以在https://github . com/mathan Raj-Sharma/sample-for-medium-article/blob/master/cluster-based-segmentation-skimage/cluster-based-segmentation . ipynb找到上面例子的笔记本

分类任务的先聚类后预测

原文:https://towardsdatascience.com/cluster-then-predict-for-classification-tasks-142fdfdc87d6?source=collection_archive---------0-----------------------

如何在监督学习问题中利用无监督学习

介绍

监督分类问题需要一个数据集,该数据集具有(a)一个分类因变量(“目标变量”)和(b)一组独立变量(“特征”),这些变量可能(也可能不!)对预测班级有用。建模任务是学习将特征及其值映射到目标类的函数。这方面的一个例子是逻辑回归。

无监督学习采用没有标签的数据集,并试图在数据中找到一些潜在的结构。K-means 就是这样一种算法。在本文中,我将向您展示如何通过使用 k-means 发现数据集中的潜在“聚类”来提高分类器的性能,并使用这些聚类作为数据集中的新特征,或者按聚类对数据集进行分区,并在每个聚类上训练单独的分类器。

资料组

我们首先使用 sklearn 的 make_classification 实用程序生成一个 nonce 数据集。我们将模拟一个多类分类问题,并生成 15 个用于预测的特征。

from sklearn.datasets import make_classificationX, y = make_classification(n_samples=1000, n_features=8, n_informative=5, n_classes=4)

我们现在有一个 1000 行的数据集,有 4 个类和 8 个特征,其中 5 个是信息性的(另外 3 个是随机噪声)。为了便于操作,我们将这些转换成熊猫数据帧。

import pandas as pddf = pd.DataFrame(X, columns=['f{}'.format(i) for i in range(8)])

分为训练/测试

现在,我们可以将数据分为训练集和测试集(75/25)两部分。

from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.25, random_state=90210)

应用 K-均值

首先,您将想要确定给定数据集的最佳 k

为了简洁起见,也为了不偏离本文的目的,我向读者推荐这篇优秀的教程:如何确定 K-Means 的最优 K?如果你想进一步了解这件事。

在我们的例子中,因为我们使用了 make_classification 实用程序,所以参数

n_clusters_per_class

已经设置,默认为 2。因此,我们不需要确定最优的k;然而,我们确实需要识别集群!我们将使用以下函数来查找训练集中的 2 个分类,然后为我们的测试集预测它们。

import numpy as np
from sklearn.cluster import KMeans
from typing import Tupledef get_clusters(X_train: pd.DataFrame, X_test: pd.DataFrame, n_clusters: int) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """
    applies k-means clustering to training data to find clusters and predicts them for the test set
    """
    clustering = KMeans(n_clusters=n_clusters, random_state=8675309,n_jobs=-1)
    clustering.fit(X_train)
    # apply the labels
    train_labels = clustering.labels_
    X_train_clstrs = X_train.copy()
    X_train_clstrs['clusters'] = train_labels

    # predict labels on the test set
    test_labels = clustering.predict(X_test)
    X_test_clstrs = X_test.copy()
    X_test_clstrs['clusters'] = test_labels
    return X_train_clstrs, X_test_clstrsX_train_clstrs, X_test_clstrs = get_clusters(X_train, X_test, 2)

我们现在有了一个新的特性,叫做“集群”,值为 0 或 1。

缩放比例

在我们拟合任何模型之前,我们需要缩放我们的特征:这确保所有的特征都在相同的数字尺度上。对于像逻辑回归这样的线性模型,在训练期间学习的系数的大小将取决于特征的尺度。如果您有 0-1 范围内的要素,而其他要素的范围为 0-100,则无法可靠地比较这些系数。

为了扩展这些特征,我们使用下面的函数来计算每个特征的 z 分数,并将训练集的学习映射到测试集。

from sklearn.preprocessing import StandardScalerdef scale_features(X_train: pd.DataFrame, X_test: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """
    applies standard scaler (z-scores) to training data and predicts z-scores for the test set
    """
    scaler = StandardScaler()
    to_scale = [col for col in X_train.columns.values]
    scaler.fit(X_train[to_scale])
    X_train[to_scale] = scaler.transform(X_train[to_scale])

    # predict z-scores on the test set
    X_test[to_scale] = scaler.transform(X_test[to_scale])

    return X_train, X_testX_train_scaled, X_test_scaled = scale_features(X_train_clstrs, X_test_clstrs)

我们现在准备运行一些实验!

实验

我选择使用逻辑回归来解决这个问题,因为它非常快,并且通过检查系数可以快速评估特征的重要性。

为了运行我们的实验,我们将在 4 个数据集上构建逻辑回归模型:

  1. 没有聚类信息的数据集(基本)
  2. 以“聚类”为特征的数据集(聚类-特征)
  3. df 的数据集["群集"] == 0(群集-0)
  4. df 的数据集[“群集”] == 1(群集-1)

我们的研究是 1x4 组间设计,以数据集[基础、聚类特征、聚类-0、聚类-1]为唯一因素。下面创建我们的数据集。

# to divide the df by cluster, we need to ensure we use the correct class labels, we'll use pandas to do that
train_clusters = X_train_scaled.copy()
test_clusters = X_test_scaled.copy()
train_clusters['y'] = y_train
test_clusters['y'] = y_test# locate the "0" cluster
train_0 = train_clusters.loc[train_clusters.clusters < 0] # after scaling, 0 went negtive
test_0 = test_clusters.loc[test_clusters.clusters < 0]
y_train_0 = train_0.y.values
y_test_0 = test_0.y.values# locate the "1" cluster
train_1 = train_clusters.loc[train_clusters.clusters > 0] # after scaling, 1 dropped slightly
test_1 = test_clusters.loc[test_clusters.clusters > 0]
y_train_1 = train_1.y.values
y_test_1 = test_1.y.values# the base dataset has no "clusters" feature
X_train_base = X_train_scaled.drop(columns=['clusters'])
X_test_base = X_test_scaled.drop(columns=['clusters'])# drop the targets from the training set
X_train_0 = train_0.drop(columns=['y'])
X_test_0 = test_0.drop(columns=['y'])
X_train_1 = train_1.drop(columns=['y'])
X_test_1 = test_1.drop(columns=['y'])datasets = {
    'base': (X_train_base, y_train, X_test_base, y_test),
    'cluster-feature': (X_train_scaled, y_train, X_test_scaled, y_test),
    'cluster-0': (X_train_0, y_train_0, X_test_0, y_test_0),
    'cluster-1': (X_train_1, y_train_1, X_test_1, y_test_1),
}

“基础”数据集

“聚类-特征”数据集

“0 类”数据集

“聚类-1”数据集

为了有效地运行我们的实验,我们将使用下面的函数,该函数遍历 4 个数据集,并在每个数据集上运行 5 重交叉遍历。对于每个数据集,我们获得每个分类器的 5 个估计值:准确度、加权精度、加权召回率和加权 f1。我们将绘制这些图来观察总体性能。然后,我们从每个模型各自的测试集上获得分类报告,以评估细粒度的性能。

from sklearn.linear_model import LogisticRegression
from sklearn import model_selection
from sklearn.metrics import classification_reportdef run_exps(datasets: dict) -> pd.DataFrame:
    '''
    runs experiments on a dict of datasets
    '''
    # initialize a logistic regression classifier
    model = LogisticRegression(class_weight='balanced', solver='lbfgs', random_state=999, max_iter=250)

    dfs = []
    results = []
    conditions = []
    scoring = ['accuracy','precision_weighted','recall_weighted','f1_weighted']for condition, splits in datasets.items():
        X_train = splits[0]
        y_train = splits[1]
        X_test = splits[2]
        y_test = splits[3]

        kfold = model_selection.KFold(n_splits=5, shuffle=True, random_state=90210)
        cv_results = model_selection.cross_validate(model, X_train, y_train, cv=kfold, scoring=scoring)
        clf = model.fit(X_train, y_train)
        y_pred = clf.predict(X_test)
        print(condition)
        print(classification_report(y_test, y_pred))results.append(cv_results)
        conditions.append(condition)this_df = pd.DataFrame(cv_results)
        this_df['condition'] = condition
        dfs.append(this_df)final = pd.concat(dfs, ignore_index=True)

    # We have wide format data, lets use pd.melt to fix this
    results_long = pd.melt(final,id_vars=['condition'],var_name='metrics', value_name='values')

    # fit time metrics, we don't need these
    time_metrics = ['fit_time','score_time'] 
    results = results_long[~results_long['metrics'].isin(time_metrics)] # get df without fit data
    results = results.sort_values(by='values')

    return resultsdf = run_exps(datasets)

结果

让我们画出我们的结果,看看每个数据集如何影响分类器的性能。

import matplotlib
import matplotlib.pyplot as plt
import seaborn as snsplt.figure(figsize=(20, 12))
sns.set(font_scale=2.5)
g = sns.boxplot(x="condition", y="values", hue="metrics", data=df, palette="Set3")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.title('Comparison of Dataset by Classification Metric')

pd.pivot_table(df, index='condition',columns=['metrics'],values=['values'], aggfunc='mean')

一般来说,我们的“基本”数据集,没有聚类信息,创建了性能最差的分类器。通过添加我们的二进制“集群”作为特性,我们看到了性能的适度提升;然而,当我们在每个集群上安装一个模型时,我们会看到最大的性能提升。

当我们查看用于细粒度性能评估的分类报告时,画面变得非常清晰:当数据集按集群分段时,我们看到了性能的大幅提升。

base
              precision    recall  f1-score   support

           0       0.48      0.31      0.38        64
           1       0.59      0.59      0.59        71
           2       0.42      0.66      0.51        50
           3       0.59      0.52      0.55        65

    accuracy                           0.52       250
   macro avg       0.52      0.52      0.51       250
weighted avg       0.53      0.52      0.51       250

cluster-feature
              precision    recall  f1-score   support

           0       0.43      0.36      0.39        64
           1       0.59      0.62      0.60        71
           2       0.40      0.56      0.47        50
           3       0.57      0.45      0.50        65

    accuracy                           0.50       250
   macro avg       0.50      0.50      0.49       250
weighted avg       0.50      0.50      0.49       250

cluster-0
              precision    recall  f1-score   support

           0       0.57      0.41      0.48        29
           1       0.68      0.87      0.76        30
           2       0.39      0.45      0.42        20
           3       0.73      0.66      0.69        29

    accuracy                           0.61       108
   macro avg       0.59      0.60      0.59       108
weighted avg       0.61      0.61      0.60       108

cluster-1
              precision    recall  f1-score   support

           0       0.41      0.34      0.38        35
           1       0.54      0.46      0.50        41
           2       0.49      0.70      0.58        30
           3       0.60      0.58      0.59        36

    accuracy                           0.51       142
   macro avg       0.51      0.52      0.51       142
weighted avg       0.51      0.51      0.51       142

考虑类别“0”,跨四个数据集的 f1 分数为

  • 基数—“0”F1:0.38
  • 聚类特征—“0”F1:0.39
  • 群集-0—“0”F1:0.48
  • 群集 1—“0”F1:0.38

对于“0”类,在 cluster-0 数据集上训练的模型显示 f1 分数比其他模型和数据集相对提高了约 23%。

结论和下一步措施

在本文中,我展示了如何利用“先聚类后预测”来解决分类问题,并梳理了一些表明这种技术可以提高性能的结果。在集群创建和结果评估方面,还有很多工作要做。

在我们的例子中,我们有一个包含两个集群的数据集;然而,在您的问题中,您可能会发现更多的集群。(一旦您在数据集上使用肘方法确定了最佳 k !)

k > 2 的情况下,您可以将“clusters”特性视为分类变量,并应用一键编码在您的模型中使用它们。随着 k 的增加,如果您决定为每个集群拟合一个模型,您可能会遇到过度拟合的问题。

如果您发现 K-Means 没有提高您的分类器的性能,也许您的数据更适合另一种聚类算法——参见本文介绍不*衡数据集上的层次聚类。

和所有数据科学问题一样,实验,实验,实验!对不同的技术进行测试,让数据指导您的建模决策。

参考

python 中统计计算的数据结构,McKinney,第 9 届科学中的 Python 会议录,第 445 卷,2010 年。

@software{reback2020pandas,
    author       = {The pandas development team},
    title        = {pandas-dev/pandas: Pandas},
    month        = feb,
    year         = 2020,
    publisher    = {Zenodo},
    version      = {latest},
    doi          = {10.5281/zenodo.3509134},
    url          = {https://doi.org/10.5281/zenodo.3509134}
}

Harris,C.R .,Millman,K.J .,van der Walt,S.J .等人用 NumPy 进行数组编程。自然 585,357–362(2020)。DOI:10.1038/s 41586–020–2649–2。

sci kit-learn:Python 中的机器学习,Pedregosa 等人,JMLR 12,第 2825–2830 页,2011 年。

J. D. Hunter,“Matplotlib:2D 图形环境”,科学计算&工程,第 9 卷,第 3 期,第 90–95 页,2007 年。

瓦斯科姆,法学硕士,(2021 年)。seaborn:统计数据可视化。《开源软件杂志》,6 卷(60 期),3021 页,https://doi.org/10.21105/joss.03021

将您在 Spotify 上保存的歌曲分成相似歌曲的播放列表

原文:https://towardsdatascience.com/cluster-your-liked-songs-on-spotify-into-playlists-of-similar-songs-66a244ba297e?source=collection_archive---------36-----------------------

默罕默德

探索音频特征,对情绪进行分类,并建立机器学习方法

答几周前,我偶然看到一篇有趣的文章,题为“使用 Spotify API 和 Python 进行音乐品味分析”。在这篇文章中,作者试图分析他和未婚妻的偏好,以确定数据对此有何说明。因此,他同时比较了 Spotify 的 API 提供的两种不同的音乐特征。在阅读它的时候,我很好奇,不仅分析了我自己的偏好,还玩了我的 Spotify 数据。因此,我想将我在 Spotify 上保存的歌曲集中到单独的播放列表中,这些播放列表将代表我在听这些歌曲时的特定心情。

所以为了完成我的任务,我把我的目标分成几个独立的任务:

  • 获取数据
  • 构建聚类模型
  • 找出一个合适的分类器,并根据获得的数据进行训练
  • 对我的歌曲进行分类,并将其分成播放列表
  • 将建议分类

首先,值得一提的是,Twitter、Slack 和脸书 Spotify 也为开发者提供了一个 API,用于探索他们的音乐数据库,并深入了解我们的收听习惯。它提供了各种各样的功能;然而,我用了 8 个特征来描述一首歌。

  • 声学值:描述一首歌曲的声学程度。
  • 可跳舞性 值: " 描述了一首曲目根据音乐元素(包括速度、节奏稳定性、节拍强度和整体规律性)的组合适合跳舞的程度。"因此,我决定不使用 tempo,因为我相信这个特征是从他们那里得到的
  • 活跃度值: “描述歌曲被现场观众录制的概率”。
  • 能量值:代表强度和活跃度的感知度量。通常,充满活力的曲目感觉起来又快又吵。
  • 乐器性值:预测音轨是否不包含人声。”在这种情况下,“Ooh”和“aah”音被视为乐器声。Rap 或口语词轨道明显是“有声的”。“乐器度”值越接* 1.0,轨道不包含声乐内容的可能性就越大。”
  • 响度值:显示音乐的音量有多大。
  • 语音值: “检测音轨中是否存在语音单词。”

关于所提供的不同功能的更多信息: 获取音轨的音频功能| Spotify for Developers

由@sgcdesigncoUnsplash

实验装置

当我提到每个特征所代表的含义时,我想强调一下我在聚类任务中使用的数据集。聚类将使用 KMeans 聚类算法导出,该算法在 Kaggle 上找到的 Spotify 数据集 1921–2020 上进行了训练。Spotify 数据集 1921–2020 包含从 Spotify Web API 收集的超过 160 000 首歌曲,您还可以在数据部分找到按艺术家、年份或流派分组的数据。

正常化响度

考虑到提到的所有其他特征都具有范围为[0,1]的值,重要的是将响度值的范围从 0 降低到-60 dB。因此,我使用 sklearn MinMaxScaler 将响度特性标准化,使其值介于 0 和 1 之间。

****from** **sklearn** **import** preprocessing 
scaler=preprocessing.MinMaxScaler() *#instantiate a scaler*
*#all the feature values are in the range [0,1] ,except of loudnes*
*#so let's scale it to fit the exact same range*loudness=X["loudness"].values
loudness_scaled=scaler.fit_transform(loudness.reshape(-1, 1))**

调整 K-均值聚类算法

我最终使用了 K-Means 聚类算法,该算法用于确定数据的分布。它是一种无监督学习算法,通过计算到质心的距离将相似的数据点分组到 k 组。为了达到这个目标,它寻找预定数量( k )的聚类。

为了得出一个合适的 k 值,我用了一个众所周知的方法叫做肘法。因此,通过对 k 的范围(例如1–20)运行 K 均值,我们可以得到下图:

如果我们将碎石图视为一座山,我们需要找出一个点,在那里我们的“山”结束,而“碎石”开始,因为直到该点,我添加的每个聚类的方差都有一个实质性的减少,在该点之后,有一个边际减少。因此,对于我的情况来说,值 5 似乎是最佳的。但也不是完全生动,所以有时候可以由一个人来决定。

使用主成分分析描述和可视化聚类

考虑到我们的聚类任务使用了 8 种不同的特征,很难将数据可视化。因此,有一些常用的技术来降低数据的维度。我使用了主成分分析(PCA ),它降低了维数,但仍然保留了重要的信息。PCA 同样用于加速学习过程;然而,我不打算在这篇文章中深究细节。

使用 KMeans 获得的聚类

上图显示了我们在 2 维空间中的 5 个集群。很明显,群集 0 可能很难与群集 2 和 1 区分开。同时,衍生出的其他聚类之间也存在明显的差异。

  • 因为所有歌曲的*均值是 0.87,所以聚类 0 在语音方面非常高。如果一首歌曲的语音度高于 0.66,它可能是由口语词组成的,0.33 到 0.66 之间的分数是一首可能同时包含音乐和词的歌曲,而低于 0.33 的分数意味着这首歌曲没有任何语音。此外,它的工具性较低,但舞蹈性相当高。我想大部分是说唱歌曲。
  • 集群 1 的可舞性和能量相对较高,但工具性较低。我期待在那里看到很多充满活力的歌曲。
  • 集群 2 音响性很高,但乐器性和响度较低。此外,它还具有相当高的可跳性。所以,我期待看到更*静,或许悲伤的歌曲。
  • 第 3 簇的声音和乐器性最高,但语音最低。
  • 第 4 组乐器性和能量相当高,可舞性一般。同样,它的语速也相对较低。

识别最佳分类器

考虑到我们获得了标签作为我们的聚类,我们可以轻松地实现一个分类算法,这将有助于对我们在 Spotify 上保存的歌曲进行分类。此外,它将允许我们对推荐的歌曲进行分类,并将它们分成不同的播放列表。

重要提示:同样,只有一个响度特征是用相同的定标器归一化的;然而,重要的是要注意,我们需要使我们的定标器只适合于训练集,并对测试集和训练集都应用转换,以避免数据泄漏。在这篇文章中,我不打算深究细节。

**X_train[:,5]=scaler.fit_transform(X_train[:,5].reshape(-1, 1)).reshape(-1,)
X_test[:,5]=scaler.transform(X_test[:,5].reshape(-1, 1)).reshape(-1,)**

根据准确率得分比较了四种模型,分别是 K *邻分类器、随机森林分类器、支持向量分类器和朴素贝叶斯。支持向量分类器被证明是在准确性分数方面最好的模型,其组成大约为 0.998,因此我们将使用它来进行未来的分类。

SVC 显示的混淆矩阵

预测我在 Spotify 上保存的歌曲

Spotify 的 API 提供了一套有用的功能。因此,我们能够获得我们保存的所有歌曲的数据集。所以,我会用上面提到的分类器对我喜欢的歌进行分类。最后,我们将我的歌曲分成 5 个不同的播放列表,并尝试分析它们。

**offset = 0
songs = []
names = []
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**
**for** i **in** songs:
    names.append(i['track']['name'])
    ids.append(i['track']['id'])
index=0
audio_features=[]
**while** index < len(ids):
    audio_features += sp.audio_features(ids[index:index + 50])
    index += 50
features_list = []
**for** features **in** audio_features:
    features_list.append([features['acousticness'], features['danceability'],
                          features['liveness'],features['energy'],
                          features['instrumentalness'], features['loudness'],
                          features['speechiness']])

mydf = pd.DataFrame(features_list, columns=["acousticness", "danceability", "liveness","energy", "instrumentalness", "loudness", "speechiness"],index=ids)**

分类歌曲

上图显示了我喜欢的歌曲使用 SVC 分类。最后,我听了每个集群的歌曲列表,得出了以下解释:

  • 正如我所料,绝大多数歌曲都是说唱歌曲。因此,如果我想听一些激进的说唱来振奋我的精神,我会听他们。

示例:

  • 集群 1 包含大部分精力充沛且适合跳舞的歌曲。

示例:

有趣的是,肖恩·蒙德兹的所有歌曲都属于这一类。

  • Cluster 2 由很多缓慢而*静的歌曲组成。因此,我给它贴上*静或浪漫音乐的标签。

示例:

我保存的 Joji 的歌几乎都属于这一类,在我看来已经足够公*了。

  • 集群 3 包含了很多通常没有歌词的歌曲。如果我想集思广益或学习,我会听他们。

示例:

  • 第四组主要是一些令人放松的歌曲。

示例:

现在让我们分析一下我对 Spotify 上音乐的偏好。

上图展示了我喜欢的歌曲数据集中使用的所有特征的*均值。因此,我推断我通常会听一些可舞性和能量都很高的歌曲。我从未想到过那件事,但现在我意识到那确实是真的。然而,我也时不时听听浪漫的,也许是悲伤的歌曲。

上面的饼状图证明了我喜欢的歌曲中有 80%属于活力/舞曲类。

最后,我把我的歌分成 5 个不同的播放列表,代表这些类别。

**clustered_songs=list(zip(mydf.index,mydf.iloc[:,-1]))sorted_songs=[[],[],[],[],[]]
**for** i **in** range(len(clustered_songs)):
    sorted_songs[clustered_songs[i][1]].append(clustered_songs[i][0])playlists=[]
**for** i **in** range(5):
    playlist_created=sp.user_playlist_create(username, name="Cluster "+str(i), public=**False**,description='')*#create playlists for a corresponding cluster*
    sp.user_playlist_add_tracks(username,playlist_created['id'],sorted_songs[i][:100]) *#add new songs*
    playlists.append(playlist_created)**

对推荐歌曲进行分类

作为我们实验的最后一步,我将请求根据我保存的歌曲生成 Spotify 推荐。

**rec_tracks = []
**for** i **in** mydf.index.tolist():
    rec_tracks += sp.recommendations(seed_tracks=[i], limit=5)['tracks'] *#get recommendations from Spotify*

rec_track_ids = []
rec_track_names = []
**for** i **in** rec_tracks:
    rec_track_ids.append(i['id']) *#extract id and name of songs*
    rec_track_names.append(i['name'])

rec_features = []
**for** i **in** range(0,len(rec_track_ids)):
    rec_audio_features = sp.audio_features(rec_track_ids[i]) *#extract features*
    **for** track **in** rec_audio_features:
        rec_features.append(track)

rec_playlist_df = pd.DataFrame(rec_features, index = rec_track_ids) *#make a dataframe***

最后,我对我获得的所有歌曲进行分类,并通过添加新的推荐歌曲来更新我创建的播放列表。让我们看看获得的歌曲是否符合我们的播放列表。

新增

集群 0:

集群 1:

有趣的是,还增加了几首法国歌曲。我认为那是他的风格。

集群 2:

尽管这是一首说唱歌曲,但风格和音乐符合播放列表。

集群 3:

集群 4:

注意:使这个集群不同于上面列出的其他两个集群的特征是它的低速度。

结论

总而言之,这些推荐是基于我保存的所有曲目的数据集(而不是基于单个播放列表)生成的。因此,我相信一个分类器可以很好地将它们分成 5 个不同的播放列表。我认为这项工作非常有趣,对于刚刚开始旅程的人来说是很好的实践,因为它同时包括了 ML 的各个领域。在这项工作中,我在公共 Spotify 数据集上训练了 KMeans 聚类算法。此外,我想出了一个分类器,帮助我将喜欢的歌曲分类到播放列表中,每个播放列表代表一种独特的风格/情绪。

源代码在我的 Github 上有。

创建了指向我的播放列表的链接

  • 【https://open.spotify.com/playlist/6dL6xOCcema8DUSzYLfx06】集群 0(说唱):
  • Cluster1(活力/舞蹈):https://open.spotify.com/playlist/0LlIHqBd2zOHemZ3c7j6Ov
  • Cluster2(冷静/浪漫):https://open.spotify.com/playlist/4mF1KkRP5YITPTf1dakXJP
  • 集群 3(学习/思考):https://open.spotify.com/playlist/4xRgEf6Y4iBs3I3WNNofMr
  • Cluster4(寒气)【https://open.spotify.com/playlist/5FtCtZ5QrpQDlXxIB9DMEi】:

参考文献:

****[## 重定向你-媒体

编辑描述

medium.com](https://medium.com/r?url=https%3A%2F%2Ftowardsdatascience.com%2Fa-music-taste-analysis-using-spotify-api-and-python-e52d186db5fc)**** **** [## 我的 Spotify 音乐很无聊吗?涉及音乐、数据和机器学习的分析

使用 Spotify 的音频功能 API、数据和机器学习,我调查了我保存的歌曲有多无聊。

towardsdatascience.com](/is-my-spotify-music-boring-an-analysis-involving-music-data-and-machine-learning-47550ae931de) [## 通过 Python 提取您最喜欢的艺术家的 Spotify 数据

Spotify 是世界上最受欢迎的流媒体*台之一。他们也有一个 API 供开发者使用…

medium.com](https://medium.com/@RareLoot/extracting-spotify-data-on-your-favourite-artist-via-python-d58bc92a4330) [## 获取音轨的音频功能|面向开发者的 Spotify

需要路径参数授权。来自 Spotify 帐户服务的有效访问令牌:参见 Web API…

developer.spotify.com](https://developer.spotify.com/documentation/web-api/reference/tracks/get-audio-features/)****

簇状和重叠条形图

原文:https://towardsdatascience.com/clustered-overlapped-bar-charts-94f1db93778e?source=collection_archive---------35-----------------------

为什么和如何

图片由来自 Unsplash 的 Suyehon Choi 提供

1.- 簇状条形图

又名:分组、并排、多集[条形图、条形图、柱形图]

为什么:聚类条形图(CBC)显示一个主要类别和属于第二分类变量的子类别之间的相对比例的数字信息。类似于堆积条形图,它们应该用于比较和比例,但重点是组成。与堆叠条形图不同,构成子类别的元素可能是广泛相关的。当一个整体被分成多个部分时,CBC 特别有效。它们能够在子类别之间进行比较,而堆叠条形图在子类别内进行比较

它们允许可视化子组如何随时间变化,但是随着时间的延长和子类别数量的增加,图表变得难以阅读。它们不应用于关系或分布分析。

如何:和条形图一样,CBC 是二维的,有两个轴:一个轴显示类别,另一个轴显示数值。指示类别的轴没有刻度来突出它指的是离散的(互斥的)组。带有数值的轴必须有一个带有相应测量单位的刻度。

CBC 由多组矩形条表示,这些矩形条可以水*或垂直定向。每个主要类别被分成代表第二分类变量的子类别的条簇。每个子类别的数量由那些并排放置形成一个簇的矩形条的长度或高度来表示,簇之间的间隙比单个标准条稍宽。

图 1:簇状条形图的示意图。该图形是用 Matplotlib 开发的。

子类别可以是顺序的或名词性的,但同等的子类别在每个聚类中必须有相同的颜色,以免混淆观众。使用合适的调色板、*衡的间距和便于比较的布局是很重要的。因为横条是沉重的视觉标记,仅仅为了改善故事情节,尽量少用网格线。

下图显示了 2016-2019 年期间与销售、费用和利润相关的公司业绩数据。这是一个垂直方向的簇状条形图,以年份作为主要类别。销售额、费用和利润每年表示为一个群集。可视化清晰地突出了 2018 年,即使在费用增加和销售额减少的情况下,利润仍保持相对恒定。

图 2:一家虚构公司在 2016-2019 年期间的经济表现。该图形是用 Matplotlib 开发的。

比较用堆积条形图表示的相同数据是很有趣的。如前所述,当您想要跨子类别进行比较时,CBC 是合适的:2016 年与 2017 年的销售额,2018 年与 2019 年的销售额;2016 年相对于 2017 年相对于 2018 年相对于 2019 年的支出;2016 年与 2017 年、2018 年与 2019 年的利润。相反,堆积条形图只能对接*基线(销售)的部分进行比较,因为费用和利润有不同的初始基线。此外,每个主体条的高度(特定年份的销售总额+费用+利润)没有任何意义。

图 3:具有与图 2 相同数据的堆积条形图。

下一个数字与 2017 年欧盟高等教育统计(EU-28)有关。那一年有 1980 万高等教育学生,其中女性占 54%,尽管获得博士头衔的大多数学生是男性。此外,四分之一的学生从事商业、管理和法律研究。下面的柱状图表明,在教育、社会科学、艺术和人文科学、健康和福利以及商业、管理和法律研究领域,女性人数超过了男性。另一方面,在信息技术和工程、制造和建筑研究领域,男性人数超过女性(欧盟统计局,2020 年)。该图表清楚地显示了广泛教育领域中男女参与高等教育的数字信息。这是一个横向的 CBC,其中教育领域构成主要类别,而性别是第二分类变量。

图 3:2017 年欧洲联盟高等教育学生按领域和性别分布情况。来源(#1)

簇状条形图的主要问题是,它们不能清楚地显示单个部分相对于整体的比例。这样一来,比例就不好评价了。它们的强度与第二分类变量的等价子类之间的直接比较有关。

2.- 重叠条形图

又名:叠加、重叠、叠加【条形图、条形图、柱形图】

为什么:重叠条形图(OVC)用于不同项目或类别之间的比较。OVC 在一张图中对每个项目或类别只比较两个数字变量。数值变量必须密切相关,才能进行比较。它们也用于显示基于相似前提的长期趋势。它们不应用于关系或分布分析。

与 OVC 相关的概念想法是对比两个变量的数值,这两个变量相互重叠,允许用更大的阐释力来描述信息(讲故事)。从这个意义上说,它们比簇状条形图要好,因为这种比较直观上更优越。这种图表非常精确地显示了盈余和短缺,尤其是在添加了适当的网格后。它们通常用于显示相对于目标或基准的进展水*。

图 4:重叠条形图的示意图。该图形是用 Matplotlib 开发的

**How**: it is a two dimensional graph with two axis -similar to every standard bar chart- with rectangular bars that can be oriented horizontally or vertically**.** One axis shows categories, the other axis shows numerical values related with two variables. **Bars representing the same category share the same baseline and the same location on the corresponding axis**. Both numerical variables must be closely related and share the same numerical scale. The width of the bars is different for each numerical variable with the smaller going forward for clarity of reading. The drawback is that for some categories one of the bars is the shorter while it is the longer for others. 

图 5:一家虚构公司在 2012 年至 2019 年期间的实际支出与预算支出。该图形是用 Matplotlib 开发的

一些可视化工具允许部分重叠几个数值变量(多个数据序列),这样代表每个连续数值变量的矩形被位于它们前面的其他矩形部分隐藏。从概念上讲,当代表不同数据集的矩形开始重叠而不是并排放置时,它们相当于聚类(分组)条形图。OVC 意味着一个矩形在另一个矩形前面 100%重叠的极端情况。毫无疑问,观众会发现很难对三个或更多部分重叠的条进行比较。当多个子类别的数据必须在单个图表中进行很长时间的比较时,使用它是合理的。

图 6:部分重叠的条形图,来源珀耳帖技术博客(#2)

总而言之,当你想对整体的各个部分进行直接比较时,你可能会使用一个簇状条形图。另一方面,重叠条形图能够很好地比较两个密切相关的数值变量。

和标准条形图一样,我为这两种类型的图表推荐以下提示和警告:

从 0 开始基线:如果条被截断,实际值不能正确反映;

当存在时序数据(时间序列、时间数据)或负数值时,建议采用垂直方向(柱形图)(图 2 和图 5)。另一方面,当绘制大量类别时,尤其是具有非常长的标签时,优选使用水*方向(图 3);

如果较长的条形图总是在较短的条形图后面,部分重叠的条形图只能显示一个好的信息;

避免所有 3D 效果。尽管它们在美学上令人愉悦,但它们违背了适当的数据可视化的所有规则。

如果你对这篇文章感兴趣,请阅读我以前的:

堆积条形图,为什么和如何,讲故事和警告

[## 堆积条形图,为什么和如何

讲故事和警告

towardsdatascience.com](/stacked-bar-graphs-why-how-f1b68a7454b7)

参考文献:

# 1:https://EC . Europa . eu/Eurostat/statistics-explained/index . PHP/Tertiary _ education _ statistics # Fields _ of _ education

#2:珀尔帖科技博客,【https://peltiertech.com/stacked-vs-clustered/】

聚类算法。第二部分。

原文:https://towardsdatascience.com/clustering-algorithm-part-2-57907f5c0437?source=collection_archive---------51-----------------------

奥拉·米先科在 Unsplash 上拍摄的照片

更快、更聪明、更快乐…

扩展我之前的帖子并尝试解决这个问题,这个问题对于大多数传统的聚类算法来说太难了。

这篇文章在很大程度上遵循了我上一篇文章的思路,你可以在下面找到:

[## 重新审视聚类算法

深入研究一种新的聚类识别方法。

towardsdatascience.com](/a-fresh-look-at-clustering-algorithms-c3c4caa1a691)

我在这里尝试做的是概括它(一点),加速它(很多!)并将其应用于一个数据集,该数据集会使大多数其他聚类算法脱轨。我还将强调这一过程中的一些挑战,并介绍一种新的预分析方法,它应该有助于选择问题的参数,以获得最佳结果。

系好安全带。这将是一段颠簸的旅程。

优化部分本身并不新鲜——我将在这里浏览一下,因为你可能会发现它很有用,并强调从经验中吸取的一些教训。如果您对此不感兴趣(或者如果您是专家),请直接跳到下一节,深入了解真正擅长颠覆经典聚类算法的数据集。在上一篇文章中,我对我的方法进行基准测试的九个算法都失败了。每一个。单身。一个。

优化代码

当我开始查看一个相当复杂和庞大的数据集时,对算法性能的真正测试开始了。这个数据集是伦敦市中心一*方公里的栅格文件表示,显示了不到 250 座(可识别的)建筑物。每个坐标有 4 个 ln 值,这给我的笔记本电脑带来了很大的压力,计算时间为 5 小时 47 分钟。一点都不好。不得不说,这个算法在这个阶段对于一个蛮力方法来说有点痛苦,所以我需要做一些改进。目前的版本能够在 4 分 40 秒内完成同样的计算。同样的笔记本电脑,没有并行化,只是改进了算法。

那么大部分的性能提升来自哪里呢?

该算法的核心是一个基本的 BFS 实现——在我们空间的图形表示中计算不相连的树。它遵循几个简单的步骤:

  1. 选择我们想要检查的节点的 id(在这个阶段任何 id 都可以)。
  2. 识别它的邻居,并检查它们是否属于阈值以上的节点集。
  3. 将邻居添加到“检查”列表中。
  4. 将当前节点标记为已访问。
  5. 更新 id 列表(删除所有“已访问”和“待检查”列表元素)
  6. 从“检查”列表中选择任意节点,并重复步骤 1-4。

我们这样做,直到“tocheck”列表为空,将所有“已访问”添加到集群字典,然后转到列表中的下一个 id(再次执行步骤 1)。

第一个主要的胜利来自于间歇性地去除了列表和 numpy 数组的使用,并且不得不从一个转换到另一个。当处理 4000 个点的数据集时,这并不是一个大问题,当处理 4,000,000 个点的数据集时,这就成了一个大问题。

所以我从中得到的主要教训是——尽量避免类型转换,至少在有潜在瓶颈的代码部分。这一变化导致了计算时间从的 5 小时 47 分下降到 29 分 37 秒。

第二个胜利来自于认识到,即使字典是处理图/树结构的一个相当明显和直观的选择,但在现实中它们是不必要的和耗费资源的。至少在这个算法的框架内。这都是因为我需要一个简单的 id 来处理每个单元格,并且需要一个简单的方法来识别最*的邻居。

如果你参考我以前的文章中的原始算法的主要步骤——我们采用一个包含所有输入参数的 numpy 数组,为了维护和计数所有的单元格,我们从它们中创建一个字符串 id,然后使用它作为检查一切的密钥。放弃字典也允许我们完全放弃对这些索引的需求。这实际上使整个算法更加通用,因为例如,如果我们想处理 word2vec 的文本表示,我们将得到 300 个维度,因为每个文本块(或单个单词)都由一个 300 长的浮点向量表示。现在想象一下,我们必须对每个维度进行 10 次以上的划分,然后将它们全部粘合到一个字符串 id 中。我们将以一个 600 字符长的字符串来表示每个观察结果。不太好。事实证明,我们可以通过使用原始的 numpy 数组、屏蔽和索引来完成整个计算和循环路径。这一变化带来了另一个重大胜利——从 29 分 37 秒缩短到不到 7 分钟。

最终的改进来自于一个非常小的变化,但仍然很重要。树计数过程的一部分涉及使用屏蔽来选择最*的邻居,然后将它们从仍然需要检查的元素数组中移除。这意味着同一个面具被使用了几次。只需将它传递到变量中,然后在多个需要的地方使用该变量,而不是在运行中进行计算,就又获得了 2 分钟的胜利,使最终执行时间达到 4 分 47 秒。

我们需要做的另一个重要改变是积分归属。我将再次向您推荐上一篇文章的内容,但简单地说,我们只使用通过阈值的细胞来构建我们的树结构。这意味着,一旦我们确定了我们的聚类,我们就剩下一大堆没有分配给任何聚类的点。一方面,我们可以让它们保持原样。另一方面,在某些情况下,我们希望将整个数据集划分到围绕集群创建的组中。在我以前的文章中,我通过识别每个聚类的质心,然后比较每个点到每个质心的距离,然后将其分配给最*的一个质心。这在大多数情况下都很好,我们看到结果非常接* KMeans 算法获得的结果。然而,有一整类问题,这种方法是行不通的。出于这个原因,我们将转移到一个不同的方法,这是不太依赖于质心。具体来说,我们将查看到最*的标记邻居的距离。也就是说,我们将测量到每个聚类中所有已识别点的距离,看哪一个是最*的,然后分配给最*的一个。

所有的函数,包括两种属性方法都在本文的最后。

这意味着我们已经准备好解决我们原始 blobs 的一个更复杂的问题:

聚类算法禁区

剧透警告——不再是了:)。

经典数据集是简单的同心圆,它几乎颠覆了所有经典聚类算法。事实上,它是存在非线性边界的任何东西,所以在某种程度上,栅格文件簇识别已经是一个有效的例子。同心圆为我们提供了一个很好的简单示例,它还允许我们突出显示建筑物数据集因其规则模式而不会有的一些细微差别。但是我在这里跳向前。让我们从生成数据集开始,快速看一下标准算法是如何制作的。

我们首先加载需要的库:

%matplotlib inline
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
pd.plotting.backend='seaborn'
from IPython.display import clear_output
import time

注意,我们不再需要最新实现的 itertools

生成数据集:

N=4000 #number of observationsr = np.random.random(N) * 0.1 + 1
theta = np.random.random(N) * 2 * np.pi
x = r * np.cos(theta) + 5
y = r * np.sin(theta) + 5r = np.random.random(N) * 0.1 + 0.5
theta = np.random.random(N) * 2 * np.pi
x1=r * np.cos(theta) + 5
y1 = r * np.sin(theta) + 5r = np.random.random(N) * 0.1
theta = np.random.random(N) * 2 * np.pi
x2 = r * np.cos(theta) + 5
y2 = r * np.sin(theta) + 5r = np.random.random(N) * 0.1
theta = np.random.random(N) * 2 * np.pi
x3 = r * np.cos(theta) + 5.8
y3=r * np.sin(theta) + 5

所以我们正在创建两个圆形斑点和两个同心圆。

让我们快速浏览一下:

plt.figure(figsize=(10,10))
plt.scatter(x, y)
plt.scatter(x1, y1)
plt.scatter(x2, y2)
plt.scatter(x3, y3)

还要注意,外圆有相当多的不规则。这可能会给我们带来一些问题(剧透——会的)。

现在,让我们将所有这些组合成一个数据集并进行归一化:

ttf_circles = pd.DataFrame(
    {
        'x':np.concatenate([x, x1, x2, x3]),
        'y':np.concatenate([y, y1, y2, y3])
    }
)
ttf_norm = ttf_circles.copy()
ttf_norm['x'] = (
    ttf_norm['x'] - np.min(ttf_norm.x)
) / (
    np.max(ttf_norm.x) - np.min(ttf_norm.x)
)
ttf_norm['y'] = (
    ttf_norm['y'] - np.min(ttf_norm.y)
) / (
    np.max(ttf_norm.y) - np.min(ttf_norm.y)
)

快速浏览一下:

ttf_norm.plot(x='x', y='y', kind='scatter', figsize=(10,10))

您可以直接看到为什么这会给基于质心的归因带来问题。我们的三个星团的质心在同一个地方!

让我们首先来看看标准的、现成的方法是如何处理这个问题的。

import sklearn
from sklearn.cluster import AffinityPropagation, AgglomerativeClustering, Birch
from sklearn.cluster import DBSCAN, KMeans, MiniBatchKMeans, MeanShift
from sklearn.cluster import SpectralClustering
from sklearn.mixture import GaussianMixture
  1. 亲和力传播
X = np.array(ttf_norm[['x','y']].values)
model = AffinityPropagation(damping=0.9)
%%time
model.fit(X)
ttf_norm['Affinity'] = model.predict(X) + 1

出局:

CPU times: user 9min 18s, sys: 22.7 s, total: 9min 40s
Wall time: 9min 55s

并绘制结果:

哎唷…我的意思是——它很有色彩……但是完全没用。

失败!

其他方法的代码是相同的,所以我只展示结果。

2.结块的

CPU times: user 8.2 s, sys: 804 ms, total: 9 s
Wall time: 9.04 s

我们有正确数量的集群(因为我们告诉它要寻找多少!),只是不是我们要找的人…

失败!

3.桦树

CPU times: user 827 ms, sys: 50.7 ms, total: 877 ms
Wall time: 752 ms

与#2 相同。相同的结论:

失败!

4.基于密度的噪声应用空间聚类

CPU times: user 382 ms, sys: 370 ms, total: 752 ms
Wall time: 865 ms

的确非常丰富多彩…只是对我们完全没用。

失败!

5.KMeans

CPU times: user 662 ms, sys: 57.3 ms, total: 719 ms
Wall time: 366 ms

好吧,你明白了。让我们直接跳到高斯混合。这是我希望会比其他的好一点的一个,但是,唉,在 164 毫秒内,它达到了:

好,那么我们的密度树算法呢?请击鼓…

计算我们的部门数量:

D = int(np.sqrt(len(ttf_norm)))
D

出局:126

该模型运行时间为 374 毫秒,这已经足够了。然而,我们发现…

44 簇!

失败!

或者是?或者说我们能做点什么吗?

事实上,如果我们仔细观察,这就是正在发生的事情:我们被问题的规模绊倒了。记得我提到过数据中的条纹会给我们带来一些问题。这就是了。我们正在识别由数据稀疏引起的数据中的不规则性。我们可能已经预料到了,因为我们已经尽我们的临界参数所允许的那样细化了。

现在,有没有一种方法可以确定(最好是一种自动化的方法)我们需要进行多少次划分才能得到我们想要的聚类类型。所以对于这样的问题,确定一组参数,让我们得到最细粒度和最小粒度的结果。

事实证明——确实有。

请记住,我们将所有东西都分割成单元,并计算落入每个单元的观察值的数量,以构建我们的聚类。我们可以看看这些划分是多么有效,我们的观察结果是如何被打包到我们的单位细胞中的。所以我们要看的是,有超过 10 个观察值的单胞,与只有一个观察值或根本没有观察值的单胞数量的比率。选择 10 有些武断,用这个数字来看看什么效果最好可能是有意义的,到目前为止,它对我测试的所有问题都足够好了。

我们将浏览多个部门,看看该比例如何变化:

prop=[]
for d in range(1, 500):
    Xint = (X * d).astype(int)
    unique, counts = np.unique(Xint, axis=0, return_counts=True)
    prop.append(
        len(
            counts[counts > 10]
        )/(
            len(
                counts[counts == 1]
            ) + len(Xint) - len(unique)
        )
    )

我在这里使用列表来收集结果,我很抱歉。这一点是快速的,但是的,真的应该翻译成 numpy。

让我们画出这个:

plt.figure(figsize=(12, 8))
plt.plot(list(range(1, 500)), prop)

啊哈!我们有两个峰值。一个 50 左右,一个 200 左右。确切的数字并不重要,我们想知道大概的数字。

只是检查一下:

np.argmax(prop)

出局:49

够*了。

这意味着我们有两个最佳区域。如果我们想尽可能地细化并识别我们观察到的所有聚集点,我们会选择 200。如果我们想要一个更广阔的画面,我们用 49。

然而,这还不完全是结束。我们还想确保我们的宽容也符合我们的意图。对于更广阔的前景,我们希望非常低,否则我们有被过度排斥小数量的单位细胞产生的额外缺口绊倒的风险。对于更详细的前景,我们希望精确,所以我们可能希望使用更高的数字。这部分,暂时来说,还是不够规定性。也许转换到精确计数而不是百分位数会使它更简单。在我的待办事项/实验清单上。

好的,那么如果我们用 50 个除法和第 10 个百分位数会发生什么?

%%time
D = 49
tolerance = 10
ttf_norm['density_trees'] = density_trees_fit_nn(np.array(ttf_norm[['x','y']].values), D, tolerance)

出局:

Processed 100.00% ids
4 clusters found
CPU times: user 76.7 ms, sys: 8.56 ms, total: 85.3 ms
Wall time: 80.7 ms

砰的一声。!!

其他器械包的临界分割值

看一下不同场景下的临界分割值图是有意义的,这样可以获得更多的直觉。

例如,如果我们通过在外环生成更多的数据点来增加外环的密度,会发生什么?理论上,随着我们逐渐填充更多的空间,这应该会使那里的迷你星团不那么明显。

让我们看看会发生什么:

N=4000 #numbner of observationsr = np.random.random(N * 4) * 0.1 + 1
theta = np.random.random(N * 4) * 2 * np.pi
x = r * np.cos(theta) + 5
y = r * np.sin(theta) + 5r = np.random.random(N) * 0.1 + 0.5
theta = np.random.random(N) * 2 * np.pi
x1=r * np.cos(theta) + 5
y1 = r * np.sin(theta) + 5r = np.random.random(N) * 0.1
theta = np.random.random(N) * 2 * np.pi
x2 = r * np.cos(theta) + 5
y2 = r * np.sin(theta) + 5r = np.random.random(N) * 0.1
theta = np.random.random(N) * 2 * np.pi
x3 = r * np.cos(theta) + 5.8
y3=r * np.sin(theta) + 5ttf_circles = pd.DataFrame(
    {
        'x':np.concatenate([x, x1, x2, x3]),
        'y':np.concatenate([y, y1, y2, y3])
    }
)ttf_norm = ttf_circles.copy()
ttf_norm['x'] = (
    ttf_norm['x'] - np.min(ttf_norm.x)
) / (
    np.max(ttf_norm.x) - np.min(ttf_norm.x)
)
ttf_norm['y'] = (
    ttf_norm['y'] - np.min(ttf_norm.y)
) / (
    np.max(ttf_norm.y) - np.min(ttf_norm.y)
)

好吧,很多代码。本质上,这与我们开始时所做的是一样的,但是当我们生成第一个圆时,我们将点数乘以 4。所以我们会有 4 倍多的观测值。

你看现在外环光滑多了…

临界分裂图变成了:

所以峰值现在移到了右边,max 现在是 86。第二个峰值仍然存在,在 200 左右,但不那么明显了。我们添加的观察越多,它就越不明显,最终会完全消失。

让我们也来看看我们在上一篇文章中看到的四个 blobs:

和临界分裂图:

Max 在这里不太明显,实际的绝对最大值是 45,但我们可能可以达到 80。事实上,在文章中我们使用了临界值 63,这显然已经足够好了。

最后是栅格文件数据集。伦敦市中心的像素化建筑:

因为我们有 4mln 个点,所以计算这个需要一点时间,但是我们可以增加步长使它更容易,所以我显示的结果是使用步长 10:

这个恰好很好很简单,临界除法参数恰好在 525 左右。我们在文章中使用了 501,这是我凭直觉得出的,而不是根据严格的计算得出的。现在,在我们开始之前,我们有了一个非常简单的方法来确定我们问题的正确粒度。

这种方法的好处是——现在我们可以测试数据中的条纹,并在试图找到正确的建模方法之前理解比例和联系。我们可以对任意数量的变量这样做。潜在地,这是非常强大的,可以节省很多时间。

正如我所承诺的,我在这里附上所有的主要功能。请随意使用和改进这些。如果你有一个有趣的项目/数据集,你想使用它,并希望我的投入-请让我知道,我不能保证我会接受它,但我会努力。如果你自己在用,请友好地引用这篇文章。谢谢!快乐聚类!

聚类算法:一站式商店

原文:https://towardsdatascience.com/clustering-algorithms-a-one-stop-shop-6cd0959f9b8f?source=collection_archive---------27-----------------------

比较层次聚类、K 均值、K 中值、K 模式、K 原型、DB 扫描和混合高斯模型的快速指南

照片由来自 Pexels 的 Artem Beliakin 拍摄

说到集群,有这么多可供选择的方案。根据任务的不同,理解所有可用技术的结果如何以及为什么会有如此大的差异很容易让人感到困惑。考虑到聚类算法的非监督性质,可能很难区分哪种算法是最合适的。在这篇文章中,我试图揭穿他们之间的主要差异,并强调什么时候一个人可能更适合每项任务。

分层聚类

分层聚类可能是所有算法中最直观的,并且提供了很大的灵活性。

该算法

分层聚类是一种凝聚算法。本质上,在该过程的开始,每个数据点都在自己的群集中。使用相异度函数,该算法在数据集中找到最相似的两个点,并将它们聚集在一起。该算法像这样迭代运行,直到整个数据被聚类。此时,可以使用树状图来解释不同的聚类,并根据需要选择聚类的数量。

根据专门的维基百科页面的树状图示例

最适合…

  • 分类数据
  • 发现异常值和异常组
  • 使用易读的树状图显示结果
  • 灵活性,因为有不同的相异函数可用(即完全连锁、单连锁、*均连锁、最小方差等。)各自给出非常不同的结果

棘手的时候…

  • 随着数据量的增加,它会变得非常耗费时间/资源,因为它会反复处理每个数据点,每次都会遍历整个数据集。分层聚类不能很好地扩展。

k 均值

K-Means 可能是最流行的聚类算法。由于这一点,以及它的简单性和扩展能力,它已经成为大多数数据科学家的首选。

该算法

用户决定结果聚类的数量(表示为 K)。k 个点被随机分配为聚类中心。从那里,该算法通过选择与该点的欧几里德距离最小的聚类,将数据集中的所有其他点分配给其中一个聚类。接下来,通过取每个点坐标的*均值来重新计算聚类中心。该算法将每个点重新分配给最*的聚类,并重复该过程,直到聚类收敛并且不再变化。

注意,由于随机初始化,结果可能取决于随机选择哪些点来初始化聚类。因此,该算法的大多数实现提供了以不同的“随机开始”多次运行该算法的能力,以便选择最小化这些点及其聚类中心的误差*方和(惯性)的聚类。

使用肘图,也很容易选择正确的集群数量(如果这不是由所涉问题预先确定的话)

我为说明目的而生成的肘图示例

最适合…

  • 可能不需要聚类的可解释性的一般情况(即当用作监督问题的特征时)
  • 大多数情况下,快速解决方案足以产生深刻见解的问题。K-Means 算法是相对高效的。
  • 大数据问题,因为算法可以轻松扩展(scikit-learn 甚至提供了一个特别适合大量数据的小批量 K 均值版本

棘手的时候…

  • 数据集包含许多分类变量。K-Means 倾向于聚集在分类变量周围(因为它们在标准化数据集中的方差相对较高)
  • 离群值可能会严重扭曲聚类

k-中间值

如上所述,大量异常值的存在可能会显著阻碍算法的效率。

该算法

K-Means 使用聚类中每个点的*均值来计算每个聚类的中心。然而,*均值并不是一个稳健的指标。因此,异常值的存在会使中心偏向异常值。

K-Medians 基于与 K-Means 相同的算法,不同之处在于它不是计算给定聚类中所有点的坐标的*均值,而是使用中值。因此,聚类将变得更加密集,对异常值也更加稳健。

最适合…

  • 创建紧密/密集的集群,对异常值具有鲁棒性

棘手的时候…

  • 需要一个快速的解决方案,因为它不在 scikit-learn 支持的算法中(需要使用 PyClustering 或自定义代码来实现)

k 模式

当存在分类变量时,K-Means 的表现也不好。至于 K-中位数,存在一个实现来利用分类数据上的 K-均值的效率。

该算法

K-Means 计算两点之间的欧几里德距离,而 K-Modes 试图最小化不相似性度量:它计算不相同的“特征”的数量。使用模式代替手段,K-模式变得能够有效地处理分类数据

最适合…

  • 当数据集只包含分类数据时

棘手的时候…

  • 数据类型是混合的
  • 不一致的重要特征。因为 K-Modes 简单地计算不相似性的数量,所以它与“特征”点不同的算法无关。如果给定的类别特别普遍,这可能会成为一个问题,因为算法在聚类时不会考虑它。

k 原型

K-Prototypes 扩展了 K-Means 和 K-Modes,特别适用于处理包含连续变量和分类变量的混合数据集。

该算法

为了处理分类变量和连续变量,K-Prototypes 使用了一个定制的相异度度量。要最小化的点到其聚类中心(其原型)的距离如下:

用于计算 K 原型中点/簇之间距离的等式

其中 E 是连续变量之间的欧几里德距离,C 是不同分类变量的计数(λ是控制分类变量在聚类过程中的影响的参数)。

最适合…

  • 大型混合数据集(即超出了分层聚类的限制)

棘手的时候…

  • 需要一个快速的解决方案,因为它不在 scikit-learn 支持的算法中(需要使用 PyClustering 或自定义代码来实现)
  • 分类变量的权重可能不清楚

DB 扫描

创建 DB-Scan 是为了解决一个不同的集群问题。它将高密度的簇与低密度的簇隔离开来。

该算法

为了根据密度来划分集群,DB-Scan 首先将数据划分为 n 个维度。对于每个点,该算法在该点周围形成一个形状,计算落入该形状的其他观察值的数量。DB-Scan 迭代地扩展形状,直到某个距离内不再有点,记为 epsilon(为模型指定的参数)

最适合…

  • 分离高密度和低密度的集群
  • 隔离异常值

棘手的时候…

  • 聚类具有相似的密度(因为算法分离密度)
  • 数据具有高维性

高斯混合模型(GMM)

高斯混合模型是第一个“基于模型”的聚类算法。该领域仍然相对较新,但高斯混合模型在某些情况下显示出巨大的前景。

该算法

以前的算法将点“硬”分配给特定的聚类,而 GMM 将每个点“软”分配给多个聚类,其中每个分配由属于给定聚类的概率来定义。

该模型假设数据点是由混合高斯分布生成的,并试图使用期望最大化(EM)来找到后一种分布的参数。

使用贝叶斯信息标准(BIC),GMM 还可以找到最佳数量的聚类来最好地解释数据。

最适合…

  • 当集群是“隐藏的”/不可直接观察的,并且诸如由其他模型提供的“球形表示”是不够的时,复杂的集群。GMM 提供了额外的灵活性,并且可以提供更加复杂/非线性的聚类。

棘手的时候…

  • 处理大量数据(扩展性不好)
  • 数据量非常有限(算法需要能够估计协方差矩阵)

总之,存在大量的可用聚类算法,甚至超出了本文快速介绍的范围。其中绝大多数都可以通过开源 python 库(即 Scikit-learn 的多样化选项)轻松访问。它们之间的选择并不总是显而易见的,但是每种技术都有特定的优缺点,应该相应地加以利用。

Scikit-learn 提供了对大多数聚类算法及其一些优点/缺点的很好的总结,以及对其中大多数算法的易于使用的实现。我强烈建议您查看 it ,进一步了解您可能需要的算法以及如何针对您的具体情况进行调整。

PS:我现在是柏克莱大学的工程硕士,我还在学习这方面的知识。如果有什么需要改正或不清楚的地方,请告诉我。你也可以在这里发邮件给我

使用 INFORM 风险和新冠肺炎数据对国家进行聚类分析

原文:https://towardsdatascience.com/clustering-analysis-of-countries-using-inform-risk-and-covid-19-data-6dbc54e0bd80?source=collection_archive---------42-----------------------

老龄人口、共病和流动是决定国家对新冠肺炎脆弱性的关键因素

图片来源:freepik

作者:纳米塔·希巴&凯瑟琳·洛佩斯

新冠肺炎脆弱性相关风险

自 2020 年初以来,我们一直面临新冠肺炎疫情,它已经蔓延并影响到每个国家。 HDE (人道主义数据交换)产生了 INFORM (风险管理指数)新冠肺炎风险指数,以确定“面临新冠肺炎健康和人道主义影响的国家,这些影响可能超出当前的国家应对能力,因此需要额外的国际援助”。我们将这一综合风险指数与其他新冠肺炎数据(包括病例、检测、卫生能力和人口)结合在一起,以更好地了解不同国家的风险及其管理。

数据

我们使用了新冠肺炎的公共数据集列表,如下所示,我们包括了大约 180 个国家和每个国家大约 40 个不同的特征:

  • 约翰·霍普斯金大学确诊病例、死亡病例、康复病例
  • 测试数据来自我们的数据世界
  • 来自世界人口统计的人口和人口密度数据
  • 来自全球健康安全指数的健康能力得分,该指数是该国诊所、医院和社区中心的健康能力指数
  • 通知来自人道主义数据交换的风险指数数据

基于主成分分析和自组织神经网络的数据预处理

我们在数据预处理中使用主成分分析(PCA)和自组织映射(SOM)来提取独立特征,并将高维数据可视化到低维表示上。

Principal Component Analysis (PCA) is a multivariate analysis technique to transform the original set of multiple features in the data into a smaller set of uncorrelated variables which account for the maximum variance in the data set, and are called principal components. Therefore, PCA is a dimensionality reduction technique for large datasets.

对于我们的数据,我们使用主成分分析来降低维数,同时仍然可以解释数据中 99%的方差。结果我们把变量的数量从 43 个减少到了 24 个。“sklearn”库用于执行 PCA。

Self Organizing Maps (SOM) are unsupervised neural networks that cluster high dimensional data into easy to understand two-dimensional outputs (SOM grid). The SOM grid is a two-dimensional manifold or topological space onto which each observation (row) in the m-dimensional space (where m is the number of variables) is mapped via its similarity with the prototypes (code book vectors) for each node in the SOM grid.

在我们的研究中,数据最初由 178 个观察值和 43 个特征描述,但在应用 SOM 算法时,它被压缩成 7x7x43 的二维图(49 个节点,43 个特征)。高维空间中接*的向量也最终被映射到二维空间中接*的节点。因此,SOM 保留了原始数据的拓扑结构。这意味着在我们的数据中,具有相似性的国家在结果 SOM 中更接*。

图 1: SOM 网格

使用层次聚类的聚类分析

层次聚类是一种聚类分析方法,它试图构建聚类的层次结构。有两种层次聚类策略:

**Divisive Hierarchical clustering**: It is a top-down approach, where all the observations are a part of a single cluster to begin with but then at each iteration the splits are performed and the process is repeated until each cluster contains only a single observation.**Agglomerative Hierarchical Clustering**: It is a bottom-up approach, where in the beginning there are as many clusters as is the number of observations but then at each iteration, the closest pair of clusters are merged and the process is repeated until there is only one cluster left.

我们使用主成分分析和 SOM 结果进行凝聚聚类分析。用这两种方法中的每一种都获得了四个聚类。下面的图 2 示出了使用来自 PCA 的特征作为输入获得的 4 个聚类(A、B、C & D ),图 3 示出了使用来自 SOM 的特征作为输入的 4 个聚类(1、2、3 和 4)。

图 2:作为输入从 PCA 获得的 4 个聚类的树状图

图 3:具有 4 个集群的 SOM 网格

每个国家(分析的 178 个国家中)通过主要成分的等级聚类被分配一个聚类 ID (A、B、C 或 D );和在 SOM 节点上使用分级聚类的另一个聚类 ID (1、2、3 或 4)。

在两种方法中,聚类 A 与聚类 1 一致,聚类 B 与聚类 2 一致,聚类 C 与聚类 3 一致,聚类 D 与聚类 4 一致。在聚类分析所包含的 178 个国家中,有 169 个国家使用 PCA 和 SOM 在聚类分析中显示出一致的结果。总而言之:

  • 集群 4/D 包括澳洲、丹麦、德国、冰岛、纽西兰、英国等 45 个国家;
  • 集群 3/C 包括 50 个国家,包括阿根廷、巴西、印度、伊朗、墨西哥、美国;
  • 第 2/B 组包括 40 个国家,包括印度尼西亚、伊拉克、斐济、利比亚、菲律宾、斯里兰卡、突尼斯;
  • 集群 1/A 包括 40 个国家,包括尼日利亚、肯尼亚、埃塞俄比亚、乌干达、赞比亚和津巴布韦。

从 4 组国家中获得的见解

4 个集群的描述摘要:

表 1:4 个集群的新冠肺炎案例相关指标的热图

从上面的热图可以明显看出,根据新冠肺炎相关指标,与其他国家相比,组成第 4/D 类的国家组的*均卫生能力得分以及的每千人累计检测数更高。这些人口密度相当高的国家与其他聚集地相比累计确诊****每百万新冠肺炎病例数也很高。澳大利亚属于这一类(4/D)。类别 3/C 中的国家具有最高的*均人口数(T14)和最高的累计确认数(T16)。

表 2:基于 4 个集群的信息风险因素的热图

上面的热图显示了与信息风险相关的指数,包括与灾害无关的脆弱性(包括与发展和贫困水*相关的脆弱性)和新冠肺炎脆弱性。群组 1/A 中的国家是一组贫穷的国家,如尼日尼亚、辛巴威、埃塞尔比亚,由于缺乏应对新冠肺炎病毒的能力而具有最高的风险,然而,他们并不是新冠肺炎病毒最脆弱的国家。对新冠肺炎来说,最脆弱的群体是集群 4/D,这一群体包括像澳大利亚、德国和英国这样的发达国家。如下图 4 所示,由于总人口中有高** 至少一种与严重新冠肺炎病(共病)相关的潜在疾病的比例,以及无任何潜在疾病的老年人口(65 岁以上)过度的内部和外部流动,它们具有最高的新冠肺炎脆弱性。**

图 4:4 个集群中 3 个风险指标的分布

我们根据卫生能力、新冠肺炎脆弱性、人口和新冠肺炎病例对 4 个集群进行了命名。

群组 4/D:高新冠肺炎脆弱性和高卫生能力

图 5:群组 4/D 中 6 个代表性国家每日每百万确诊病例的七天*均数

代表国家: 澳大利亚、丹麦、德国、冰岛、新西兰、英国

特征: 因过度的国内国际流动而导致的新冠肺炎脆弱性最高,65+人口中有前提条件的比例最高。最高的国际卫生条例核心能力得分和运营就绪指数,此外还有基于人均公共和私人医疗保健支出的最低风险。这些国家拥有最高的医疗保健能力和最大的每千人检测*均值。尽管每百万人累计确诊病例数很高,但这些国家中的许多国家在大约 2 个月内第一次变*。

群组 3/C:高人口和高病例的高新冠肺炎脆弱性

图 6:群组 3/C 中 6 个代表性国家每日每百万确诊病例的七天*均数

代表国家: 阿根廷、巴西、印度、伊朗、墨西哥、美国

特征: 新冠肺炎脆弱性高,健康能力得分一般,千分检测数高。这一组包括人口极其庞大和每百万人累计确诊病例最高的国家。自新冠肺炎开始以来,这些国家的每日确诊病例持续上升,在超过 3 个月的时间里曲线没有变*。

第 2/B 组:新冠肺炎脆弱性低,新冠肺炎病例低且上升

图 7:群组 2/B 中 6 个代表性国家每日每百万确诊病例的 7 天*均数

代表国家: 印度尼西亚、伊拉克、斐济、利比亚、菲律宾、斯里兰卡、突尼斯

特征: 与其他集群相比,健康容量低,每千人累计检测低;这一群体的新冠肺炎脆弱性低,国内和国际流动有限,并且有先决条件的人口和 65 岁以上年龄组的风险评分低。然而,它们在独立于风险的漏洞方面具有高风险,并且操作准备有限。尽管每日确诊病例数的增长率低于群组 3/C,但由于每日病例数的增加而没有任何曲线弯曲的明确迹象,这一组国家面临着挑战。

群组 1/A:保健能力低,新冠肺炎脆弱性低

图 8:群组 1/A 中 6 个代表性国家每日每百万确诊病例的七天*均数

代表国家: 尼日利亚、肯尼亚、埃塞俄比亚、乌干达、赞比亚、津巴布韦

特征: 卫生容量最低,每千人累计检测最低,每百万人累计确诊病例最低。老龄人口和有先决条件的人口的脆弱性最低,除了最少的旅行活动之外,新冠肺炎的总体脆弱性在这一群体中最低。虽然这些国家在独立应对灾害的能力和有限的运作基础设施方面风险最高,但其中大多数国家每日确诊病例的增长率较低,而且它们也成功地使曲线变*

结论

良好的卫生能力肯定有助于各国应对检测、住院和康复需求。许多国家,特别是发达国家,老年人口和有条件的人口比例很高,由于国内和国际旅行造成的过度流动,比不发达国家更容易受到新冠肺炎的影响。因此,很明显,为了抵御新冠肺炎的严重影响,非常关键的是

“减少和规范运动,保护我们的老年公民”。

我们欢迎您的反馈和评论,在这里 可以随意查看我们之前在数据探索方面的工作,也可以随意访问ka ggle repogithub repo**

来自《走向数据科学》编辑的注释: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

股票选择的聚类分析

原文:https://towardsdatascience.com/clustering-analysis-on-stock-selection-2c2fd079b295?source=collection_archive---------12-----------------------

时间抛开复杂的时间序列模型,简单的基本面分析也可以帮助赚钱!!!

马库斯·斯皮斯克在 Unsplash 上的照片

背景

H 你有没有厌倦过通过查看数百个财务比率来选择一只股票?或者你已经厌倦了打磨你的技术分析技巧或改进你的时间序列模型来更好地预测价格?如果答案是肯定的,那你来对地方了。

在本文中,我们将通过一个实验来看看不同维度的财务比率是否真的增加了股票选择的价值。在这个过程中,我们还将看到聚类分析如何帮助我们摆脱财务指标的海洋。

在此之前,我还将向您展示 (1)下载股票的历史财务比率、(2)股票的每日价格数据,这对许多在不同项目中研究股票数据的人来说非常有用。

下面所有代码的原文,你可以在这里查阅我的 Github 链接。

(1)下载股票的财务指标

首先,我们将使用一个名为 基础分析的库。 关于这个库的详细信息,请参考网站这里。

为了能够使用这个包,我们需要一个来自 FinancialModellingPrep 的 API 密匙,并按照那里的说明获得一个 免费的 API 密匙。请注意,虽然没有时间限制,但这些密钥仅限于每个帐户 250 个请求。所以我强烈建议大家将下载的数据输出为 excel 文件以备将来使用。否则很容易超过限制。

并且由于这个限制,我将实验中的选股范围设定为下面列出的 97 只股票,都是纳斯达克 100 指数的成份股。

上面的代码会生成两个 excel 文件,(a)key _ metrics . xlsx(b)financial _ ratios . xlsx,每只股票在一个单独的表中。这两个文件都存储了过去 10-20 年的各种财务指标,具体取决于公司上市的时间。在后面的步骤中,我们将把它们与收益和价格波动数据结合在一起。

MSFT 关键指标数据的捕获图像

(2)下载股票价格数据

让我们下载价格数据。由于上面的包对请求配额有限制,我们将切换到另一个自由库 yfinance。

上面的代码将下载股票行情表中所有股票的每日价格数据。并选择收盘价来表示价格,输出为 excel 文件, price.xlsx

价格数据的捕获图像

(3)将所有数据结合起来使用

最后我们想将准备好的三个 excel 文件合并成一个文件,每个文件在所选期间(2017–2019)内保存一年。我不打算在这里赘述细节,因为这些都只是使用熊猫和 numpy 的一些基本技巧。本节原代码请参考 Github 链接此处中的cluster _ stocks _ data . py

尽管如此,我还是想提到一些非常有用的关键技巧,即使是在你从事其他项目的时候。

(a) dataframe.at[index,column name]——为了避免混淆 iloc 或 loc 或任何其他类似的函数,您可以尝试使用函数 ”。在" 这些直接指的是 dataframe 内的单个单元格。并且可以很容易地用等号设置它的值。

【b】数据帧。t—如果要交换行和列,转置数据帧。通过应用此功能 。T" ,那么列名就会变成索引,反之亦然。

(c)PD . concat()—*行(水*)组合两个数据帧。

(d)data frame . fillna(value = N)—用特定值的 NaN 填充单元格。

经过繁琐的程序后,最终产品将是三个 excel 文件(2017 年、2018 年和 2019 年)。每个都存储特定年份股票行情表中每只股票的回报、价格波动和其他财务指标的数据。

2017 年最终数据集的捕获图像

(4)实验

F 最后,我们可以回到本文的主要内容——(1)检验 不同维度的财务比率是否真的为选股增值,(2)看看聚类分析如何帮助我们摆脱财务指标的海洋。

与其一行一行地检查冗长的代码,我们不如一起检查这个想法。但是,我还是会举例说明一些关键部分的代码。对于下面介绍的金融概念,如果你有进一步的兴趣,可以参考 Investopedia 了解更多详情。本节代码请参考 Github 链接 中的cluster _ stocks _ analysis . py

第一步:选择一组有代表性的财务指标。 在这个实验中,我选择了以下内容,旨在从不同维度捕捉一家公司的表现或能力。

负债权益比率——公司的杠杆比率

股息收益率——公司支付给股东的持有其股票的金额除以当前股价

企业价值(EV) / EBITDA — 一个常用的倍数来确定公司的价值

净债务与 EBITDA 比率— 表明公司减少债务的能力

净利润率— 净收入占收入的百分比是多少

营业利润率— 在支付可变生产成本(如工资和原材料)后,支付利息或税之前,公司从一美元销售中获得的利润

资产回报率— 通过确定一家公司相对于其总资产的盈利能力,表明该公司利用其资产的情况

第二步:根据以上选择的财务指标进行聚类分析。

不同的人在选择股票时有不同的偏好。有的关心公司盈利能力,有的关心股票是否稳定分红。但是如果我们想了解整体性能呢?那么就很难断定一只股票一定比另一只好,特别是每一个财务指标都代表不同的东西,可能有上百个指标。

在这种情况下,聚类分析可能是拯救我们的一种方法。聚类帮助我们根据财务指标所代表的特征将相似的股票分组在一起。

在本实验中,我们将应用一种最常用的方法——系统聚类,用自底向上的方法、欧几里德距离和沃德方法来计算相似度。对于层次聚类的详细解释,这篇文章给出了一个很好的教训。

(一)标准化

每个财务指标都有自己的标度。例如,EV / EBITDA 可以始终高于资产回报率。因此,为了防止这种规模差异导致无与伦比的权重和不可靠的结论,我们必须首先对数据进行标准化。我们可以通过下面的一行代码简单地做到这一点。

mean() — 计算列内数值的*均值

std() — 计算一列内数值的标准差

(二)执行层次聚类分析

将数据标准化后,我们可以使用一个名为agglomeveclustering的库来执行聚类,这个库的名字意味着自底向上的方法。

为了可视化聚类结果,我们将应用树状图,,它是一个树形图,记录了合并或拆分的顺序。但是,请注意,最终形成的集群数量完全基于您的判断。如果聚类太多,分类可能会太细。如果太少,股票可能无法很好地分类。

树状图绘制如下。

从树状图中,我们可以看到可能有 13 个星团。

有趣的是,四只股票本身形成了独立的集群。

基于该选择,我们接下来对数据集 2017、 应用函数凝聚聚类,通过将 n_clusters 设置为 13,将亲和力设置为欧几里德距离,将链接设置为沃德方法** 。

为了说明这 13 个星团的特征,我们可以画一个线图。

从折线图上我们可以看到,2017 年有四个非常有特色的星团,实际上它们都是单独形成的。它们是:

(i) 【灰线】——高净债务/EBITDA 比率和 EV/EBITDA 比率,这意味着公司偿还债务的能力相对较低,基于其较弱的盈利能力对其进行了高估值,这可能意味着其被高估。

(二) IDXX(深蓝线)—负负债率,主要是公司 2017 年权益余额为负。

(三) DXCM(浅蓝色线)—负 EV / EBITDA 比率,因 2017 年 EBITDA 为负。

(iv) VRTX(橙色线)——负净债务与 EBITDA 比率,因其 2017 年净债务为负值。

我们不打算调查公司业绩的细节,这可能需要为每只股票单独写一篇文章。我只是想给大家展示一下在选取的财务指标中,聚类可以帮助我们将具有相似特征的股票分组在一起,同时也可以检测出一些离群值。

第三步:根据聚类结果构建投资组合,并与不借助聚类构建的投资组合进行比较

截至 2017 年底,13 个集群的*均绩效表如下所示。

在进行聚类分析后,需要一个标准来帮助做出投资组合构建的决策。在这个实验中,收益价格波动率将是唯一遵循的标准。

回报将是一年的回报,通过比较 2017 年末的价格和 2017 年初的价格来计算。价格波动率是 2017 年全年价格的标准差。因此收益-价格波动率比率仅仅是一个收益和风险的度量,也用来模仿夏普比率。

从上表中可以看出,集群 6、8 和 11 将被选中,因为它们的收益价格波动率相对较高。投资组合 A 是通过对这三个组中的所有股票进行相等的加权投资而构建的,总共有 10 只股票。作为比较,我们希望通过选择 2017 年收益价格波动率最高的相应数量的个股来构建另一个投资组合,而无需事先应用聚类分析。

两个投资组合:

(一)2017 年收益率波动率最高的前 3 个集群 ,共 10 只股票(CSCO、FAST、XEL、EXC、COST、QCOM、VRSN、CHKP、FB、VRTX)

(B)2017 年收益价格波动率最高的 10 只股票 (为了公*比较,我们每年都会调整该投资组合的股票数量,以便与投资组合 A 中的股票数量相匹配)(SIRI、、、EBAY、、CSCO、MU、MXIM、NTAP、CMCSA)

现在,我们有兴趣看看哪个投资组合在未来一年(2018 年)产生更高的回报。并且结果显示,投资组合 A 产生了 3.48%的收益,而投资组合 B 遭受了 2.29%的损失。

然而,仍然很难断定基于上述方法的投资组合结构一定会表现更好,尤其是基于一年的数据。因此,我将这个实验延长两个时期(2018–2019 和 2019–2020),结果相当令人惊讶。

两个投资组合在三年内的表现。

显然,在所有三年中,投资组合 A 始终比投资组合 B 产生更高的回报。

(5)性能提高的可能原因

当然,通过财务指标的聚类分析来构建投资组合并不一定能取得优异的业绩。但其持续优于仅考虑回报和风险的选股表现表明,基本面分析确实增加了一些价值,聚类分析有助于简化分析过程。

以下是对更好性能的一些可能解释:

(一)价值基本面分析— 财务指标从不同维度衡量一家公司的业绩。因此,财务表现良好的公司的回报应该比“随机产生的”回报更具可持续性。定期考虑财务指标并更新投资组合配置对其可持续表现非常必要。作为证据,如果我们试图保持 2017 年构建的投资组合的配置在 2019 年前不变,其业绩在 2018-2019 年期间实际上恶化了很多。

(二)多元化收益— 聚类分析有助于区分不同特征的股票。换句话说,所选择的三个集群应该是完全不同的。这种多样化可以防止投资组合在某一时期因某一特定类型公司的低迷而遭受重大损失。

(c)防范一些公司特有的风险— 将具有相似特征的股票聚集在一起。所以一个过去收益率与价格波动率比值高的群体应该比一只比值高的个股更“具体”。通过这种方法构建的投资组合可能防范了一些公司特有的风险。

(6)结论

构建一个持续表现良好的投资组合总是不容易的,尤其是当有成千上万只股票的时候。有时,我们可能会先缩小范围,但在做出决定之前,仍然需要查看数百个财务指标。

本文介绍了一种可能的方法——聚类分析来帮助我们摆脱财务指标的海洋,并通过一些程序构建了一个完整的财务数据集。我希望这将开启关于如何通过利用一些数据科学技术来改善我们的投资组合分配的讨论。

参考

  1. 柴坦尼亚·瑞迪·帕特拉。(2018).理解层次聚类技术的概念。
  2. 达米安·博。(2020).使用 Python 获取最新的股票财务比率。
  3. 小牛顿达科斯塔,杰弗逊库尼亚和塞尔吉奥达席尔瓦。(2005).基于聚类分析的股票选择

[## 用我的推荐链接加入媒体-哈德森高

如果你对我写的东西感兴趣,不要错过成为 Medium 会员的机会。您将可以完全访问所有…

medium.com](https://medium.com/@hudsonko/membership)

如果你对时间序列模型更有信心,我的另一篇关于在 LSTM 模型中定制损失函数的文章可能会对你有所帮助。谢了。

[## 定制损失函数,使 LSTM 模型更适用于股票价格预测

损失函数不要只考虑价格差异,方向损失也很重要!!!

towardsdatascience.com](/customize-loss-function-to-make-lstm-model-more-applicable-in-stock-price-prediction-b1c50e50b16c)

免责声明:请注意,投资可能因任何原因而上涨或下跌,股票的过去表现并不能保证未来表现。

本文对任何内容的及时性、准确性或适用性不做任何陈述,也不对任何不规则或不准确之处承担责任。

本文中的股票推荐和评论不代表我对是否买入、卖出或持有任何特定股票的观点。

建议所有投资者在做出投资决定之前进行独立研究。投资者应该考虑任何投资建议的来源和适合性。您使用本文中的任何内容都要自担风险。

圣保罗的汉堡聚集地

原文:https://towardsdatascience.com/clustering-burger-venues-in-são-paulo-f4bfc0a031cd?source=collection_archive---------53-----------------------

使用 Foursquare API、无监督机器学习、树叶地图和单词云绘制见解

丹·戈尔德通过 Unsplash 拍摄的照片

圣保罗是世界上最大的城市之一,人口超过 1200 万,是巴西的经济中心。它有太多的场地和餐厅,供应来自世界各地的特色美食。特别是,有一类餐馆在这个城市非常受欢迎:汉堡店。圣保罗总共有大约 600 家汉堡店,有些街区每个街区有不止一家。

作为我的 IBM 数据科学专业证书期末项目的一部分,我决定将数据科学和我对汉堡的巨大爱好结合起来,以了解一点关于我目前居住的城市圣保罗的汉堡店的情况。

这里的目标是为那些想开一家汉堡店的人,或者已经有一家店并想获得一些关于市场的新信息的人,或者只是为那些对数据如何改变我们对任何主题的看法感到好奇的人提供见解。

所有这些项目都是使用 Python 语言和 Jupyter 笔记本完成的。详细代码在这个 Jupyter 笔记本链接里。目前,我只打算发布一些重要部分的代码片段。

战略:

我对这个项目的设想是从圣保罗的汉堡店中检索数据,根据它们的特点创建场馆群,对它们进行分类,并得出每个场馆群的见解。

为了实现这一目标,本研究分为 4 个部分:

  • 数据采集
  • 数据预处理和聚类
  • 聚类分析
  • 结论

数据采集:

获取数据的第一步是使用 geopy 库来检索圣保罗的街区,以便稍后我们可以找到每个街区的汉堡店。

照片通过 Geopy

Geopy 是一个库,Python 开发人员可以使用第三方地理编码器和其他数据源轻松定位全球各地的地址、城市、国家和地标的坐标。

使用 geopy 非常简单,只需写下街区的名称,它就会返回该位置的纬度和经度。下面是一段代码,它使用包含所有街区名称的 excel 文件检索每个街区的所有纬度和经度值:

data_hoods= pd.read_excel('Neighborhoods_SP.xlsx')
geolocator = Nominatim()
from geopy.exc import GeocoderTimedOut
lat=[]
lon=[]
for i in data_hoods['Neighborhood']:
    try:
        location_hood = geolocator.geocode("{}, Sao Paulo,Sao Paulo".format(i))
        print(i)
        lat.append(location_hood.latitude)
        lon.append(location_hood.longitude)
    except GeocoderTimedOut:
        location_hood = geolocator.geocode("{}, Sao Paulo,Sao Paulo".format(i))
        print(i)
        lat.append(location_hood.latitude)
        lon.append(location_hood.longitude)

有了这些信息,我们可以使用每个位置来检索每个街区 900 米半径范围内的每个地点。选择该半径是因为较大的半径只会导致场馆重叠,并在数据集中产生更多的重复。下面的代码显示了如何调用 API 来检索所有的场馆、它们的位置、名称和 id:

def getNearbyVenues(names, latitudes, longitudes, radius=900):

    venues_list=[]
    for name, lat, lng in zip(names, latitudes, longitudes):
        print(name)

        # create the API request URL
        url = 'https://api.foursquare.com/v2/venues/explore?&client_id={}&client_secret={}&v={}&ll={},{}&radius={}&limit={}&query={}'.format(
            CLIENT_ID, 
            CLIENT_SECRET, 
            VERSION, 
            lat, 
            lng, 
            radius, 
            LIMIT,
            'burguer'
        )

        # make the GET request
        results = requests.get(url).json()["response"]['groups'][0]['items']

        # return only relevant information for each nearby venue
        venues_list.append([(
            name, 
            lat, 
            lng, 
            v['venue']['name'], 
            v['venue']['location']['lat'], 
            v['venue']['location']['lng'],  
            v['venue']['id']) for v in results])

    burguer_venues = pd.DataFrame([item for venue_list in venues_list for item in venue_list])
    burguer_venues.columns = ['Neighborhood', 
                  'Neighborhood Latitude', 
                  'Neighborhood Longitude', 
                  'Venue', 
                  'Venue Latitude', 
                  'Venue Longitude', 
                  'Venue Id']

    return(burguer_venues)

SP_burguer_venues = getNearbyVenues(names=data_hoods['Neighborhood'],
                                   latitudes=data_hoods['lat'],
                                   longitudes=data_hoods['lon']
                                  )

注意,要复制上面的代码,你需要一个 foursquare 开发者账户:http://developer.foursquare.com/。

现在我们可以使用一张地图来看看圣保罗的汉堡店

map_burguers_neigh = folium.Map(location=[latitudeSP,longitudeSP], zoom_start=11)

for lat, lon, name in zip(SP_burguer_venues['Venue Latitude'], SP_burguer_venues['Venue Longitude'], SP_burguer_venues['Venue']):
    label = folium.Popup(' name ' + str(name), parse_html=True)
    folium.CircleMarker(
        [lat, lon],
        radius=5,
        popup=label,
        color='red',
        fill=True,
        fill_color='red',
        fill_opacity=0.7).add_to(map_burguers_neigh)

map_burguers_neigh

在 Foursquare API 注册的汉堡店

上图描绘了圣保罗的汉堡联合市场有多大。在一些地区,这实际上可能是一个饱和的市场,例如在 Itaim Bibi 社区,我们有超过 30 家汉堡店(仅计算在 Foursquare 注册的汉堡店)。

伊泰姆比比社区的汉堡店

现在,这里的想法是收集信息,这将有助于我们了解这些场馆。

  • 场馆评级:区分场馆的简单方法。我们将通过从 Foursqaure API 请求场馆评级来检索最佳场馆。该数据是一个从 0 到 10 不等的等级。
  • 场馆价格:逗号分隔的价格点列表。目前价格点的有效范围是[1,2,3,4],1 是最便宜的,4 是最贵的。1< 10 USD an entree, 2 is 10 to 20 USD an entree, 3 is 20 to 30 USD an entree, 4 is >主菜 30 美元吗?这个数据也在来自 Foursquare API 响应的 json 中。
  • 场地评论:对参加了在 Foursquare *台注册的特定场地的客户的文本评论。这将有助于我们更深入地了解客户最看重什么。
  • 街区*方米价格:这是特定街区每*方米的房地产价格。如果我们假设某个地区的房地产价格越高,居住在该地区的人的购买力就越高,那么这将有助于获取关于在特定地点维护场地的成本以及参加该场地的客户类型的见解。这一特征以雷亚尔(R$)计量。

为了获得这些信息,我们需要再次调用 Foursquare API,但是这一次我们需要使用另一个端点,因为我们需要每个特定地点的信息。进行呼叫的 url 是https://api.foursquare.com/v2/venues/$VENUE_ID&客户端 id &客户端秘密& v 版本。这将以 json 格式返回我们需要的大部分信息。

def getVenuesinfo(venue,names,venue_id):
    jsondict={}
    for ven,name, v_id in zip(venue,names,venue_id):
        print(name)

        # create the API request URL
        url_venue = 'https://api.foursquare.com/v2/venues/{}?&client_id={}&client_secret={}&v={}'.format(
            v_id,
            CLIENT_ID, 
            CLIENT_SECRET, 
            VERSION 
        )

        # make the GET request
        results_venues = requests.get(url_venue).json()
        jsondict[v_id]=results_venues

    return(jsondict)

在对每个场馆 id 进行调用后,我们将获得每个场馆的评论、价格和评级,只剩下我们要检索的邻**方米价格。我不得不从圣保罗地区(http://proprietariodireto.com.br)的一个房地产网站手动获取这些信息。现在,我们剩下一个数据框,如下图所示:

包含圣保罗汉堡店信息的数据框

现在我们已经有了所有需要的信息,是时候进入数据预处理并最终创建我们的集群了。

数据预处理和聚类:

在我们开始做一大堆聚类并开始得出结论之前,我们需要问自己:我们有什么类型的数据?哪种聚类算法是最佳选择?我们如何准备数据?我们将如何评估聚类结果?

我们有什么类型的数据?

让我们回忆一下,我们感兴趣的是为我们的聚类算法提供信息的 3 个特征:场馆的价格(范围从 1 到 4,以雷亚尔(R$)为单位的每*方米价格)和等级(范围从 1 到 10)。其中两个特征的问题是它们是有序数据。这使得一些无监督算法很难产生相关结果。例如,k-means 使用欧几里德距离来计算一个簇中元素之间的相异度。这意味着分类数据的这项任务是不可能的,因为我们无法确定类别 A 与类别 B 之间的距离,因为它们是不连续的数据。另一方面,有专门对纯分类数据进行聚类的聚类算法,如 k-modes。但是有序数据是一个特例,因为我们的信息被分成不同的类别,但是有一定的顺序。这给我们留下了一个重要的决定:我们应该将我们的数据视为连续的,还是应该将我们的数据视为混合数据,其中一些将是连续的,一些将是分类的?这两个决定都有利弊,这将最终影响聚类算法的选择。

欧几里德距离公式的例子,照片由克里斯·麦考密克拍摄

哪种聚类算法是最佳选择?

将数据视为纯连续数据会假设一个类别和另一个类别之间的距离相同,就好像它们是连续数据一样,但这有时是不正确的。通过将它们视为混合数据,我们将基本上失去有序数据的排序信息,并将它们视为纯粹的分类数据。对于这种方法(混合数据),我们需要使用 k-prototypes 算法,我会在这篇文章的最后解释。由于我们需要两个分类数据的信息顺序,我们将把它们视为连续的,并使用 k-means 算法对它们进行聚类。

我们如何准备数据?

在继续执行以下步骤之前,检查要素中的空值和重复 id 并删除符合任一情况的行非常重要。然后,我们需要对数据进行标准化,这样在聚类过程中就不会有比其他特征更高的权重。为此,我们将使用 sklearn 的标准标尺。这将通过移除*均值并缩放至单位方差来标准化要素。

cluster_df=SP_burguer_venues.filter(['Rating','Price_M2','Price'], axis=1)

Clus_dataSet=StandardScaler().fit_transform(cluster_df) 

现在,我们已经准备好对我们的场馆进行分组:

from sklearn.cluster import KMeans

wcss = []
for i in range(1, 11):
    kmeans = KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=10, random_state=0)
    kmeans.fit(Clus_dataSet)
    wcss.append(kmeans.inertia_)
plt.plot(range(1, 11), wcss)
plt.title('Elbow Method')
plt.xlabel('Number of clusters')
plt.ylabel('WCSS')
plt.show()

这个代码片段中生成的图将在下一步中有用。

我们将如何评估聚类结果?

用于评估聚类准确性的方法将是肘方法。实际上,我们将最小化所有聚类中的每个数据点到它们各自质心的距离。这将为我们的具体情况提供最佳的集群数量。为了使用肘方法,我们使用不同数量的簇绘制了具有惯性值的图形:

聚类数的*方和

通过查看上图,我们可以得出结论,对于这种情况,4 个集群将是我们的最佳选择。

使用 4 个聚类并再次运行该算法,我们在我们的聚类的 3 个特征内获得以下点分布(该图是使用 plotly 生成的):

import plotly
import plotly.express as px
fig = px.scatter_3d(SP_burguer_venues, x='Rating', y='Price', z='Price_M2',
              color='Cluster Labels', size_max=18,
               opacity=0.7)

fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()

有了这 3 个可视化,我们就可以开始了解算法是如何对场馆进行聚类的了。在我们继续之前,让我们创建一个叶子地图来可视化圣保罗地图中按集群分隔的场馆:

map_clusters = folium.Map(location=[latitudeSP,longitudeSP], zoom_start=11)

# set color scheme for the clusters
x = np.arange(kclusters)
ys = [i + x + (i*x)**2 for i in range(kclusters)]
colors_array = cm.rainbow(np.linspace(0, 1, len(ys)))
rainbow = [colors.rgb2hex(i) for i in colors_array]

# add markers to the map
markers_colors = []
for lat, lon, poi, cluster in zip(SP_burguer_venues['Venue Latitude'], SP_burguer_venues['Venue Longitude'], SP_burguer_venues['Neighborhood'], SP_burguer_venues['Cluster Labels']):
    label = folium.Popup(str(poi) + ' Cluster ' + str(cluster), parse_html=True)
    folium.CircleMarker(
        [lat, lon],
        radius=5,
        popup=label,
        color=rainbow[cluster-1],
        fill=True,
        fill_color=rainbow[cluster-1],
        fill_opacity=0.7).add_to(map_clusters)

map_clusters

聚集场馆的圣保罗地图。

红色聚类是聚类 0,紫色聚类是聚类 1,聚类 2 用蓝色编码,聚类 3 用米色显示。

将每个地点分配给每个聚类的独立数据框后,我们可以开始分析每个聚类,并开始从生成的结果中获得洞察力。

聚类分析:

好的,我们有集群,但是他们告诉我们什么?要回答这个问题,我们需要经历 2 个步骤:

  1. 分析每个特征,并在聚类之间进行比较。
  2. 创建每个集群场馆评论的文字云。

让我们从第一步开始,绘制每个聚类中的要素。从每个集群场馆的*均价格开始:

cluster_labels=['Cluster 0','Cluster 1','Cluster 2','Cluster 3']
cluster_mean_price=[cluster1['Price'].mean(),cluster2['Price'].mean(),cluster3['Price'].mean(),cluster4['Price'].mean()]
cluster_price=pd.DataFrame({'Mean Price':cluster_mean_price,'Clusters':cluster_labels})

fig = px.bar(cluster_price, x='Clusters', y='Mean Price',title="Mean Price of each cluster",color="Clusters")
fig.show()

上图显示第 0 类和第 2 类最贵,主菜价格在 10-20 美元之间。如果我们假设价格等级和实际价格之间的线性关系,则集群 0 *均比第二贵的集群 2 贵大约 17%。

这清楚地区分了集群 0 和 2 以及集群 1 和 3。集群 0 和 2 是最贵的场馆,集群 1 和 3 是最便宜的场馆,特别注意集群 3,它不到集群 0 *均价格的一半。这意味着第三组场馆主菜的*均价格低于 10 美元。

现在,我们来看看每个集群的*均房地产价格:

该图显示了将分类 2 与其他分类区分开来的主要分类特征之一是房地产价格。它的*均价格比第二贵的邻居集群(集群 0)高 75%。

考虑到这一点,与其他集群相比,预计参加集群 2 场馆的人有更大的购买力。

最后,我们有场地评级:

几乎所有的群组在评级上没有相关的百分比差异,所有群组的*均值都在 7.6 到 8.1 之间。可以假设,这些集群的场馆都是非常满意的客户。

另一方面,我们在集群 1 上观察到集群之间的另一个清楚的划分。它的*均评分为 5.86,比排名第二低的集群低 23%。

显然,每个集群都有其特点,但现在是时候回顾一些更定性的东西了,这些东西不仅会带来关于场馆的一些见解,还会带来来自这些场馆本身的客户的一些见解。

输入单词云:

萨姆·斯库勒通过 Unsplash 拍摄的照片

词云是非常美学的工具,可以帮助我们根据某个词在某个文本中的出现频率来检索文本信息。单词云会产生一个图像,其中最常见的单词会显得更大。

使用客户的评论,我们可以生成每个客户的词云,并尝试识别关于场地和客户的特征,找到对我们在特征中获得的价值的解释,还可以发现每个集群的场地的哪些方面最能引起客户的注意。

由于这个项目是与一个说葡萄牙语的国家打交道,对于不懂这种语言的人来说,将很难从单词 cloud 整体上保留信息。也正因如此,这个词云中产生的关键点才会得到解释和恰当的翻译。

为了创建单词云,我们从每个聚类的评论中生成单词列表,对于来自 4 个聚类的每个单词列表,我们将创建一个单词云:

#!conda install -c conda-forge wordcloud --yes

# import package and its set of stopwords
from wordcloud import WordCloud, STOPWORDS

list_tip1=[]
list_tip2=[]
list_tip3=[]
list_tip4=[]

for i in cluster1['Review']:
    list_tip1.append(i)
for i in cluster2['Review']:
    list_tip2.append(i)
for i in cluster3['Review']:
    list_tip3.append(i)
for i in cluster4['Review']:
    list_tip4.append(i)

flatList1 = [ item for elem in list_tip1 for item in elem]
flatList2 = [ item for elem in list_tip2 for item in elem]
flatList3 = [ item for elem in list_tip3 for item in elem]
flatList4 = [ item for elem in list_tip4 for item in elem]

import numpy as np 
from PIL import Image
unique_string1=(" ").join(flatList1)
unique_string2=(" ").join(flatList2)
unique_string3=(" ").join(flatList3)
unique_string4=(" ").join(flatList4)

#Importing burguer mask
burger_mask = np.array(Image.open('burger_mask.png'))

burguer_wc1 = WordCloud(
    background_color='white',
    max_words=2000,
    stopwords=stopwords,
    mask=burger_mask
)

burguer_wc2 = WordCloud(
    background_color='white',
    max_words=2000,
    stopwords=stopwords,
    mask=burger_mask
)

burguer_wc3 = WordCloud(
    background_color='white',
    max_words=2000,
    stopwords=stopwords,
    mask=burger_mask
)

burguer_wc4 = WordCloud(
    background_color='white',
    max_words=2000,
    stopwords=stopwords,
    mask=burger_mask
)

# generate the word cloud
burguer_wc1.generate(unique_string1)
burguer_wc2.generate(unique_string2)
burguer_wc3.generate(unique_string3)
burguer_wc4.generate(unique_string4)

绘制每个词云后,我们得到:

fig = plt.figure()
fig.set_figwidth(50) # set width
fig.set_figheight(50) # set height
fig.add_subplot(4, 2, 1)
plt.axis('off')
plt.title('Cluster 0',fontsize=38,loc='left',verticalalignment='baseline',horizontalalignment= 'left')
plt.imshow(burguer_wc1, interpolation='bilinear')
fig.add_subplot(4, 2, 2)
plt.axis('off')
plt.title('Cluster 1',fontsize=38,loc='left',verticalalignment='baseline',horizontalalignment= 'left')
plt.imshow(burguer_wc2, interpolation='bilinear')
fig.add_subplot(4, 2, 3)
plt.axis('off')
plt.title('Cluster 2',fontsize=38,loc='left',verticalalignment='baseline',horizontalalignment= 'left')
plt.imshow(burguer_wc3, interpolation='bilinear')
fig.add_subplot(4, 2, 4)
plt.axis('off')
plt.title('Cluster 3',fontsize=38,loc='left',verticalalignment='baseline',horizontalalignment= 'left')
plt.imshow(burguer_wc4, interpolation='bilinear')

聚类 0,1,2,3 的词云

从视觉检查中,我们可以看到在每个簇中更常见的单词。虽然这已经给我们带来了一些见解,但还是让我们从每个聚类中找出前 10 个单词。我已经翻译了这些单词,以便让那些不会说这种语言的人更容易理解这一部分。

每个聚类中的前 10 个单词

从这一部分中,我们可以确定在前面步骤中收集的信息之间的一些相似之处,同时对集群进行定量分析。

首先让我们来看看他们排名最高的单词中的所有共同点,这些可能是想开一家汉堡店的人的必备词汇。它们都有“atendimento”这个词,在葡萄牙语中是“服务”的意思。因此,从这一信息中得出的第一个关键信息是,无论你的客户群、价格或位置如何,你都必须提供良好的服务,这个词在所有类别中都排在前三位。另一个出现在所有聚类中的单词是“maionese ”,你可能会猜它是蛋黄酱。

香草蛋黄酱,一种在圣保罗几乎每个汉堡店里都很受欢迎的酱料,图片来自 receitasagora.com

现在,我们来看一下集群之间的差异,我们可以看到除集群 2 之外的所有集群都有单词“preç”,在葡萄牙语中是“价格”的意思。这意味着当访问汉堡店时,集群 2 可能对评估价格作为定义类别没有表现出太大的兴趣。基于每*方米的价格,这是有意义的,因为集群 2 被归类为拥有最富有的客户。另一个有趣的事实是,cluster 2 指的是素食(葡萄牙语中的“素食者”)和布里干酪(至于奶酪),表明他们在选择方面也非常苛刻。

另一个有趣的观察点是薯条在聚类 3 中出现了两次。发生这种情况是因为单词“batata”和“fritas”在上下文中都是指薯条,所以薯条实际上对这个集群有很大的影响。聚类 3 还显示了一个没有明确出现在来自其他聚类的任何其他前 10 个单词中的单词,即单词“options”。这可能表明,虽然他们不要求像布里干酪或素食这样的花式调味品,但他们也重视使用传统调味品的不同组合或大小的场所。

既然我们已经对客户谈论最多的话题有了一些了解,我们可以将我们从定量分析和热门词汇中观察到的内容结合起来,得出关于每个集群的一些结论:

群集 0

德韦恩·勒格朗通过 Unsplash 拍摄的照片

高价场地,位于*均价格和高评级的社区。顾客谈论价格、服务和另一个啤酒类别中没有的特点。这向我们表明,参加这些高价场所的人没有集群 2 那么大的购买力,但当他们选择花钱时,很可能是在休闲时间(因此啤酒在热门词汇上),他们重视这种类型的体验(因此评级较高)。

群组 1

菲德尔·费南多通过 Unsplash 拍摄的照片

*均价格(与其他人相比)、低价社区和低评级。这一群人不仅在评分中表现出不满意,而且在价格和昂贵位于前 10 名的热门词汇中也表现出不满意。这可能是因为这个群体中的人没有太多的购买力,当他们试图在*均价格的场馆上多花一点钱时,他们通常不会达到他们的预期。第 1 类场馆应注意的要点可能与成本效益相关,给人们带来了并未真正实现的期望。

群组 2

Jakub Kapusnak 通过 Unsplash 拍摄的照片

高价场地和邻*位置以及高评级。该集群以高标准和差异化为特征。与第 0 类和第 3 类相比,评级较低的一个解释是,购买力较高且去高价场所的人会提高他们的期望,因此,他们在提交评级时会更少怜悯。基于上面的话,我们可以看到这些场馆的客户注重差异化的调味品和选择。第 2 组场馆应该注意的一点是,优先考虑场馆菜单的独特性,而不是价格。

第 3 组

照片由 Pj Gal Szabo 通过 Unsplash 拍摄

高收视率的低价街区的低价场馆。这个集群的目的是降低价格,物有所值。一个没有被讨论但在单词 cloud 上出现在第 15 位的单词是成本效益。这已经向我们表明,顾客正在得到他们所寻找的东西:一顿好饭的好价钱。聚类 3 词云上的价格频率高于所有其他词,从那里,顶部的词突出了汉堡店的基本方面,即薯条、肉、蛋黄酱和许多选择。集群 3 场馆的关键要点是:保持简单和便宜。

结论:

这就结束了对圣保罗汉堡店的研究。虽然有大量的数据可以使这项研究在统计上更加可靠,而且如果我们可以事先标记场地,监督算法可能是一个不错的选择,但这是对圣保罗汉堡联合市场的一个有趣的高水*分析。如果你对所有这些背后的代码感兴趣,我真的建议你看看我为这个项目准备的 Jupyter 笔记本,也可以看看下面的附录,快速概述我使用 k-prototypes 算法而不是 k-means 得到的结果。

欢迎对这里分享的知识提出编辑或补充建议。我希望这篇文章能够启发那些刚开始从事数据科学的人,以及那些只对从数据中得出不同见解感兴趣的人,否则这些数据是不可能用肉眼观察到的。

附录:k 原型算法:

图片来自考古学

k 原型算法基本上是一个 k 均值和 k 模式绑定在一起。k-means 将使用欧几里德距离来计算聚类内元素之间的不相似性,k-modes 将基于每个个体中的特征的模式来计算距离,以计算分类特征之间的不相似性。在处理混合数据时,这是一个非常有用的方法。

k 原型的相异度函数可以表示为:S+γP,其中 S 是数值属性的相异度,P 是类别属性之间的相异度。Gamma (γ)是分类属性在我们的聚类算法中的权重。当然,这是对算法的过度简化,所以请务必查看本节末尾参考书目中黄哲学发表的文章。

我将使用这个算法的 python 实现:https://github.com/nicodv/kmodes

准备数据和运行算法:

对于本节,我们将假设价格层级是明确的,其余的是连续的。为了将*方米价格与评级(我们的连续变量)进行比较,我们需要对它们进行缩放:

scaler = MinMaxScaler(feature_range=(0, 10))
price_M2=cluster_df['Price_M2']
price_M2=price_M2.to_numpy()
price_M2=price_M2.reshape(-1, 1)
price_M2_scaled=scaler.fit_transform(price_M2)
price_M2_scaled=pd.DataFrame(price_M2_scaled)
price_M2_scaled.reset_index(drop=True,inplace=True)
cluster_scaled=cluster_df.filter(['Rating','Price'], axis=1)
cluster_scaled.reset_index(drop=True,inplace=True) 

现在,我们可以对数据运行 k-prototypes,并再次使用肘技术来找到最佳的聚类数:

cost = []
K = range(1,10)
for k in K:
    kp=KPrototypes(n_clusters=k, init='Huang', n_init=1, verbose=2)
    clusters = kp.fit_predict(cluster_scaled, categorical=[1])
    cost.append(kp.cost_)
plt.plot(cost)

使用不同 k 值最小化成本函数

好的,那么这一个将选择 2 个集群。让我们再次运行该算法,并用聚类绘制圣保罗地图。

该图显示了由 k 原型制造的两个集群

让我们分别探索这些特性:

显然,与聚类 0 相比,该算法在聚类 1 上划分为高价场所、高价社区和高评级。让我们看看客户评论是否反映了我们在上面观察到的情况:

两个集群的词云

有趣的是,我们可以观察到,使用 k-means 算法,聚类 1 中的单词与聚类 2 中的单词非常相似。另一方面,群集 0 最终是上一节中所有其他群集的混合。

因此,k-原型给了我们一个关于汉堡店的二元视角,就现实中对它们进行分类的最佳方式而言,这可能不一定是真的或假的,但它给我们带来的关于这些场所的见解比 k-means 更少,特别是在这项研究中。

作为这篇文章的总结,这只是一个示范,展示了一个选定的算法如何改变你使用无监督算法得到结果的方式。尤其是使用 k 原型时,有许多因素会影响我们的结果。其中一个因素是 gamma 乘数,它设置分类变量的权重,在本演示中我们使用默认值 0.5。如果我们开始增加 gamma,该算法将尝试根据场地价格进一步分离聚类,这可能会给我们带来失真的结果。

因此,无监督算法的选择和如何使用取决于我们自己的偏见和我们自己对什么可能是真的概念,而不一定是成立的和可以验证的。这是一个重要的收获,因为尽管它们可以为我们所用,通过显示我们无法清楚看到的模式,无监督算法也可能非常误导人。

参考资料:

——黄,哲学。对具有分类值的大数据集进行聚类的 K-Means 算法的扩展。1998 年 1 月,http://citeseerx.ist.psu.edu/viewdoc/download?doi = 10 . 1 . 1 . 15 . 4028&# 38;rep = rep 1&# 38;type=pdf 。

-一个销售和销售*台。proprietariodireto.com.br/.

  • Foursquare 开发者。developer.foursquare.com/.

聚类:国际象棋开局分类器(第一部分)

原文:https://towardsdatascience.com/clustering-chess-openings-classifier-part-i-6299fbc9c291?source=collection_archive---------71-----------------------

聚类方法

第一部分:下载象棋游戏数据集

在我的库中有完整的代码。

聚类使我们能够在数据样本中发现模式。聚类应用有很多,我们选择的算法取决于我们需要解决的问题类型。在这个系列中,我将解释如何在成千上万的样本(象棋比赛)中找到最受专业人士欢迎的空缺。

为什么要下象棋?

对于初级分析师来说,最令人兴奋的事情之一就是处理任何类型的数据。为什么不处理象棋比赛数据?这些数据是开源的,可以很容易地大量找到,并且可以从多个来源获得。

由菲利克斯·米特迈尔在 Unsplash 上拍摄的照片

什么是棋局开局?

在国际象棋理论中,比赛的初始阶段已经被研究和概念化了。冠军被用来研究以开局开始的游戏发展,而不是即兴发挥:大师们已经给数千个预定义的国际象棋游戏序列命名。职业棋手研究这些序列,以获得对敌人的竞争优势。

lichess.org

玩家玩免费象棋和提高技术的最好网站之一叫做 lichess.org。这个网站允许开发者免费下载他们想要的所有数据,只有很小的数据流限制。

我通过标签找到了lichess.org 排名前 50 的玩家,并将他们的名字保存到一个 python 列表中。这是我开始从服务器下载数据所需的少数信息之一。在本教程中,我将简单地使用 3 个姓名标签,但是在我的代码中(在我的库中)你会找到所有 50 个顶级棋手的姓名标签。

存储变量

我将开始设置主要变量,这些变量将通过我们的算法存储所有主要信息。

#list of nametages
player_list = ['Konevlad','Liem_Chess','Azzaro25']#list of nametags that have been downloading successfully
player_completed = list()#all the downloaded matches
game_dataset = list()

当我们运行我们的算法时,如果查询成功,player_completed 和 game_dataset 将填充我们可以导出的信息。

下载功能

现在我需要创建一个可以执行单个请求的函数。因为要下载信息,我需要访问一个特定的链接,所以我将使用一个函数来创建允许我执行 GET 请求的正确字符串。我将多次运行这个函数,这样我将为我列表中的每个玩家创建一个链接,并且我将一次为每个玩家下载 200 个游戏。

import requests
import time
#get request of single user
def get_request(user):
  print('trying ' + user)
  if user in player_completed:
    print('already existing')
    return 0
  else:
    print(user)
    response = requests.get('[https://lichess.org/api/games/user/'](https://lichess.org/api/games/user/') + user, params={'max':200, 'pgnInJson':False})
    return response

不幸的是,数据是 ndjson 格式的。由于一个错误,我无法用任何已知的库来解码它:我需要即兴创作一个算法,可以提取每一个游戏并将其添加到一个列表中。

#conversion one user response to all games returned as a list
def games_to_list(response):
  a = str(response.content)lines = a.split("\\n");game_list = list()
  for r in lines:
    if "[" in r:
      pass
    else:
      if r == '':
        pass
      else:
        game_list.append(r)
  game_list.remove("'")
  return game_list

开始下载

现在是运行算法的时候了。像往常一样,我们不能过分强调在线数据库,否则,它们可能会让我们在几分钟内无法进行查询。如果每次出现问题时我们都必须手动重启下载算法,那么使用我们的浏览器手动下载这些数据会更舒服(这是我们想要避免的)。

#for each player download a single request
for _ in player_list:response = get_request(_)
  time.sleep(140)
  if response == 0:
    pass
  else:
    player_completed.append(_)
    #download games into a list
    game_list = games_to_list(response)
    #add all games of a user to the final list: game_dataset
    for _ in game_list:
      game_dataset.append(_)

为了解决这个问题,我将尝试每隔 140 秒向数据库发送一次查询(GET 请求)。

    • *如果我们不是第一次运行该算法,并且一些匹配(但不是标签列表中的所有匹配)已经存储在我们的变量中(换句话说,我们之前的尝试已经部分完成),我添加了一个解决方法。该函数将发现它正在尝试下载列表中已经存在的标签:它将返回 0 作为输出,算法将传递它作为避免进行相同查询的措施。您可以为错误添加退出案例,以使其更加*滑。

输出

正如我们所看到的,在为每个请求运行并等待必要的时间之后,算法应该输出每个请求的状态。

trying Konevlad 
Konevlad 
trying Liem_Chess 
Liem_Chess 
trying Azzaro25 
Azzaro25

我已经将成功下载的标签存储在一个特定的列表中。

player_completed
['Konevlad', 'Liem_Chess', 'Azzaro25']

所有匹配都存储在一个名为 final_dataset 的数据帧中。

import pandas as pd
game_dataset = pd.DataFrame(game_dataset)
final_dataset = game_dataset.copy()
final_dataset

解决异常

要提取比赛中的每一步棋,每一行都必须是相同的格式。每一行(至少表面上)都有以下模式:

1\. c4 c5 2\. Nc3 g6 3\. g3 Bg7 4\. Bg2 e6...

但是,我需要检查有问题的行,这些行可能会在我们的数据分析中产生错误。因此,我将向所有不以 1 开头的行发送信号。

    • *这似乎是一种非常*似的发现异常的方法,但是在用聚类算法测试数据集之后,结果证明这已经足够了
#non valid rows
for _ in range(len(final_dataset.values)):
  if final_dataset.values[_][0][0] != '1':
    print(_)
106 
107

行 106 和 107 没有相同的模式,让我们更仔细地看看:

final_dataset[105:110]

因为行 106 和 107 有异常,我们将删除它们。如果您要下载成千上万的匹配,包含异常的行可能会更多。

#drop non valid rows
final_dataset = final_dataset.drop([106, 107], axis=0)

导出数据帧

我们准备好导出数据集以用于分析目的:

#export dataset
final_dataset.to_csv('chess.csv')

- >进行第二部分

聚类:国际象棋开局分类器(第二部分)

原文:https://towardsdatascience.com/clustering-chess-openings-classifier-part-ii-7a4f0ce9dcc9?source=collection_archive---------53-----------------------

聚类方法

第二部分:国际象棋开局分类

在 Unsplash 上由 A. Yobi Blumberg 拍摄的照片

在本文的前一部分,我已经编写了一个 API,允许我从 lichess.org 下载大量的顶级国际象棋比赛。我已经收集了 7000 多个匹配项,今天我将使用聚类技术对它们进行分析。我的目标是:列出这些比赛中最受欢迎的空缺。

我的仓库里有完整的代码。

导入数据集

像往常一样,第一步是使用导入下载的匹配数据集,这是我上一篇文章(第一部分)中的说明。

import pandas as pd
#importing dataset
X = pd.read_csv('/content/drive/My Drive/Colab Notebooks/Projects/20200526_Chess Openings/chess1.csv')

不幸的是,数据根本没有结构化。我需要预处理所有的数据来创建一个数据集,为每一列保存一个单独的棋步。

n_moves = X.shape[0]#break every game in individual moves
moves = [[] for x in range(n_moves-1)]
for _ in range(n_moves-1):
  game = X['0'][_].split(".")
  game.remove('1')
  #print(game)
  for move in game:
    try:
      player_move = move.split(" ")
      #print(player_move)
      moves[_].append(player_move[1]) #add white move
      moves[_].append(player_move[2]) #add black move
    except:
      #if error occurs
      print(_, move)

使用这个算法,我获取了第 0 列的所有内容,并分解了每个字符串(因此每个匹配)。比如我分解了“1。d4 Nf6 2。Nf3 g6 3。Bf4 Bg7 4。e3 O-O 5。H3……”,分成单个动作。它将创建一个名为 moves 的数据集,将每个单独的移动作为一个字符串放在不同的列中。

输出

该函数还将返回将遇到错误的行,并避免将它们添加到数据集中。

3302  
3303  
3304  
3305  
3306   
3307   
3308   
3309  
3310  
5280  
5284  
5285 
    • *对于一个微小的错误,数字会在代码中重复一次,但对我们的结果没有任何影响

现在,让我们看看最终的结果:

moves = pd.DataFrame(moves)
moves

我们的结构化数据集

空缺

如果我将聚类算法应用于 moves,即结构化数据集,我将不只是对空缺进行分类,而是对整个匹配进行分类。我需要设置一个限制,集群将在数据中找到模式。

#only conserve opening columns
list1 = [x for x in range(0, 10)]
list1moves.columns
tot = moves[list1]
tot

我选择选择最多 10 步棋(相当于 5 回合)。这些数据足以为我提供一组常用的组合,但也不会有太多的数据模糊组合的频率。

按名称分组

通过这个简单的 groupby 算法,我可以通过组合对所有 7674 个匹配进行排序。

final = tot.groupby([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], group_keys=True, squeeze=True, sort=True).count()
final = pd.DataFrame(final)
final

这将是我们的输出。如果您查看前 4 行,您会看到它们都包含变体:Nc3、d5、Nf3。现在的问题是我们按字母顺序排列它们。当我们的数据集超过 7000 行时,我们不知道这些开口有多受欢迎。这些只是没有明显重量的随意搭配。

按频率分组

我要用的是另一种形式的 groupby。在 7000 个匹配中,我可以通过统计它们的频率立即看出哪些是最受欢迎的空缺。

final = tot.groupby([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], group_keys=True, squeeze=True, sort=True).size().sort_values(ascending=False)
final = pd.DataFrame(final)
final

我要做的是将这个数据集转换成熊猫数据帧(我找到的唯一方法是将其导出为。csv,然后再次导入)。

#in order to reset it, we need to export it
final.to_csv('chess.csv')#import of already reset dataset
final_ = pd.read_csv('/content/chess.csv')
final_.columns = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'frequency']
final_ = final_[0:20]
final_

最后,我可以按字母顺序对空缺进行分组,以使用第一种分组算法来识别聚类:

final_ = final_.groupby([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], group_keys=True, squeeze=True, sort=True).count()
final_[0:20]

如您所见,这些是按频率选择的前 20 个空缺职位,然后按类别分组。虽然每个聚类的频率都为 1,但它只是随着数据集格式的变化而重置,因此我们可以忽略它。

使用 K 均值机器学习算法对客户进行聚类

原文:https://towardsdatascience.com/clustering-customers-using-k-means-machine-learning-algorithm-fb0a28948975?source=collection_archive---------12-----------------------

这个故事是关于使用 K-means 算法对网上购物者进行聚类的。一种有趣的方法,使用机器学习来细分您的客户,或者检查您的细分是否正确。

拉胡尔·潘迪特来自 Pexels.com 的照片

为什么要细分?

营销人员的工作是找到客户的模式和共同特征,对他们进行细分。这是你想出正确的信息和渠道与客户沟通并建立持久关系的第一步。

对具有共同特征的客户进行细分并不容易,需要大量的人力和智慧。

基于机器学习算法的聚类

机器学习算法的最新发展给了我们一个新的工具集,使用机器在数据集中寻找模式并创建聚类。无监督学习 K-means 算法搜索数据集中的隐藏模式(人类不可见),并将每个观察值分配到相关的聚类。我们将使用 R 进行 K 均值聚类。

关于数据集

数据集是从 Kaggle 中获取的。它包含零售购物网站的客户信息。数据集有 10 个变量和 1000 条记录(在数据清理之前)。让我们看看数据集的详细信息:

数据集中的前几条记录

数据集有 8 个分类变量和 2 个连续变量(薪水花费金额)。由于缺少很多值,我们将删除历史变量,剩下 7 个分类变量。此外,删除缺少值的行会留下 994 条记录。我们还是好好的:)

让我们把分类变量的因子级别变成整数。例如,对于年龄设置年轻=0,中间=1,年老=2。对于性别,设置女性=0,男性=1,其他分类变量也是如此。

变量及其分布:

每个分类变量的数据分布

连续变量的分布

相互关系

cor.matrix <- cor(data, method = "pearson", use = "complete.obs")corrplot(cor.matrix)

分析 9 个变量(7 个分类变量和 2 个连续变量)的相关矩阵,揭示了一些直观的见解,其中一些令人惊讶:

相互关系

结婚工资,以及花费工资都呈高度正相关。

子女(子女数量)与年龄呈负相关,在数据集中,老年人有少量子女——要么是 0,要么是 1。

来自关联矩阵的另一个令人惊讶的发现是结婚了有了孩子——他们没有关联。经过调查,我们发现已婚和单身的孩子数量差不多。

不那么令人惊讶和更直观的是,性别年龄之间没有明显的相关性。

我们有非常有趣的顾客在网站上购物:)

聚类数

我们需要多少个集群?为了确定这一点,让我们使用肘法,轮廓评分和 Calinski-Harabasz 指数。一种方法可能无法给我们一个明确的答案。这就是为什么我们需要尝试另一种方法来决定正确的集群数量。

这些方法在 R 中有现成的公式,我们只需要应用到我们的数据集。

E lbow 方法:

此方法的 r 代码:

上述 R 代码的结果显示在下图中。从图中可以看出,很难决定最佳的集群数量。2 个 3 个还是 4 个?嗯…很难说…我们用另一种方法吧。

肘法

剪影分数:

r 代码:

opt.k.sil <- Optimal_Clusters_KMeans(data, max_clusters=10, plot_clusters = TRUE, criterion = "silhouette")

结果如下图所示。较高的轮廓分数为我们提供了最佳聚类数的指示。在我们的例子中,两个和四个集群都有最高的分数,但是问题是两个都是一样的,我们不能决定选择哪一个:(。让我们转到卡林斯基-哈拉巴斯指数。

剪影配乐

卡林斯基-哈拉巴斯指数:

r 代码:

较高的 Calinski-Harabasz 指数决定了要选择的聚类数。现在很明显,4 个集群是最好的:)

聚类结果

K 均值算法的 r 代码:

KMC <- kmeans(data, centers = 4, iter.max = 999, nstart=50)

应用算法后,让我们看看每个集群中有多少客户:

每个集群中的客户数量

集群 1、2、4 几乎均匀分布,分别有 269、285 和 283 个客户,而集群 3 有 157 个客户。

每个集群中的数据:

聚类结果

C 光泽 1——大多是年轻的单身女性,没有孩子,租房居住。该集群具有最低的*均工资和最低的*均支出额。

C 第二低的*均工资和花费。

C光泽 3——多为中年男性,自有住房。这个群体中的每个人都结婚了。相对于其他群体,他们有 2 到 3 个孩子的比例最高。他们收入最高,花费也最多。

C 买高端产品,花第二高的金额。

米盖尔·Á的照片。来自 Pexels.com 的帕德里纳

每个聚类的特征都是非常独特的,并且产生几乎相同的部分。很容易注意到,集群 1 拥有“最没有价值”的客户,但是,我们没有关于该细分市场购买频率的数据。但是这个片段描绘的是一个年轻的女学生,她没有赚很多钱,也不花钱。

集群 2 是收入不高、消费也不多的中老年客户,几乎类似于集群 4 但是这些中年人确实工资高,确实消费多,大多是高端产品。

集群 3 则是中年男性,收入稳定高,有孩子,消费金额最高。

现在就看我们针对每个集群的策略了。

这个故事是基于我和我的同事诺姆·施穆尔在华沙大学教授亚采克·卢科维兹博士的“无监督学习”课上共同完成的项目

完整的 R 代码和数据库在我的 GitHub 上。

参考资料:

[1] A. Rezaeian,S. Shokouhyar 和 F. Dehghan,使用聚类技术测量电子商务网站的客户满意度:Nyazco 网站的案例研究。国际管理、会计和经济学杂志,(2016),3(1),61–74。

[2] A. Sagar,利用 K 均值聚类进行客户细分 (2019),towardsdatascience.com

[3] Dataflair 团队,数据科学项目— 在 R 使用机器学习进行客户细分(2019)

[4]cran . R-project . org 上的 R 包描述:corr plot 包介绍和 r-blogger 上 R 中的 RFM 分析

[5] 数据集取自 Kaggle.com

具有数字和分类变量的聚类数据集

原文:https://towardsdatascience.com/clustering-datasets-having-both-numerical-and-categorical-variables-ed91cdca0677?source=collection_archive---------1-----------------------

随着现代机器学习的出现,企业已经看到了他们做出决策和推动利润的方式的转变。一个成功公司最重要的属性之一是他们能多好地理解他们的客户,以及他们能多好地迎合他们的个性化需求。对于公司来说,理解“一刀切”的模式不再有效变得越来越重要。

这就把我们带到了集群的话题上。聚类只不过是对实体的分割,它允许我们理解数据集中不同的子群。虽然许多文章回顾了使用具有简单连续变量的数据的聚类算法,但是对具有数字和分类变量的数据进行聚类在现实问题中是常见的情况。本文讨论了一种使用高尔距离、PAM(Medoids 周围划分)方法和剪影宽度的聚类方法,并用 r。

我们使用的'german credit'数据取自 UCI 的机器学习知识库:(https://archive . ics . UCI . edu/ml/Machine-Learning-databases/statlog/german/)。为了导入 R 中的数据,我们使用' read.table' 命令,如下所示:

*set.seed(2020)
german_credit = read.table("[http://archive.ics.uci.edu/ml/machine-learning-databases/statlog/german/german.data](http://archive.ics.uci.edu/ml/machine-learning-databases/statlog/german/german.data)")*

我们注意到这个数据集的列名不是不言自明的,因此我们在文档的帮助下重命名这些列。

*colnames(german_credit) = c("chk_acct", "duration", "credit_his", "purpose", "amount", "saving_acct", "present_emp", "installment_rate", "sex", "other_debtor",                   "present_resid", "property", "age", "other_install", "housing", "n_credits", "job", "n_people", "telephone", "foreign", "response")*

此外,为了使事情更容易,我们只选择几个相对更重要的变量。请注意,我选择这些特定的列只是为了举例说明,读者可以自由尝试不同的列。

*library(dplyr)
german_credit_clean = german_credit %>% select(duration, amount, installment_rate, present_resid, age, n_credits, n_people, chk_acct, credit_his, sex, property, housing, present_emp)*

我们看到这个数据集既包含数字变量(连续变量)也包含分类变量。

连续变量:

期限:期限(月)
金额:贷款的信用额度
分期付款率:分期付款率占可支配收入的百分比
现在 _ 剩余:在现居住地居住的时间
年龄:年龄(年)
n _ 信用:在该银行的现有信用数量
n _ 人数:有责任为
信用提供维护的人数 _his:信用历史
财产:财产
住房:住房
现在 _ 雇员:目前就业自

分类变量:

chk_account:现有支票账户的状态
性别:个人状态和性别
credit_his:信用记录
财产:财产
住房:住房
present_emp:目前就业自

既然我们已经理解并预处理了数据,我们将注意力转向聚类。聚类的基本前提是数据点之间的相似/相异。正如我们所知,K-means 算法反复迭代,直到它达到一种状态,其中一个聚类的所有点彼此相似,而属于不同聚类的点彼此不相似。这种相似性/不相似性由点之间的距离来定义。

度量这个距离的一个流行的选择是欧几里德距离。一些研究人员还提倡对特定类型的问题使用曼哈顿距离。然而,这两种距离度量仅适用于连续数据。在包含混合数据类型的数据中,欧几里德距离和曼哈顿距离不适用,因此,K-means 和层次聚类等算法将无法工作。

因此,我们使用高尔距离,这是一种可用于计算两个实体之间距离的度量,这两个实体的属性是分类值和数量值的混合。该距离的数值范围为 0(相同)和 1(最大差异)。高尔距离的数学细节相当复杂,将在另一篇文章中讨论。

在 R 中,高尔距离可以使用'雏菊 ' 包来计算。在我们使用' daisy '函数之前,我们观察数字变量的分布,以了解它们是否需要转换。我们发现变量的数量需要进行对数变换,因为它的分布存在正偏斜。

**

下面的代码显示了高尔距离计算的实现。请注意,我们已经将“2”指定为类型参数中的参数。这确保了在执行高尔计算之前,第二个变量 amount 经过对数转换。

*library(cluster)
gower_df <- daisy(german_credit_clean,
                    metric = "gower" ,
                    type = list(logratio = 2))*

接下来,我们检查‘gower _ df’数据帧的摘要。类型“I”和“N”告诉我们相应的列是区间标度的(数值)还是标称的。

K-medoids 和围绕 medoids 的划分

现在我们已经有了一个距离矩阵,我们需要选择一个聚类算法来从这些距离中推断相似性/不相似性。就像 K-means 和分层算法与欧几里德距离密切相关一样,Medoids 周围划分(PAM)算法也与高尔距离密切相关。PAM 是一个迭代聚类过程,就像 K-means 一样,但有一些细微的区别。PAM 不是 K-means 聚类中的质心,而是反复迭代,直到质心不再改变它们的位置。分类的中间值是分类的一个成员,它代表所有考虑中的属性的中间值。

剪影宽度选择最佳聚类数

在选择最佳聚类数时,轮廓宽度是非常受欢迎的选择之一。它测量每个点与其聚类的相似性,并将其与该点与最*的相邻聚类的相似性进行比较。该度量的范围在-1 到 1 之间,其中较高的值意味着点与其聚类的相似性较好。因此,较高的轮廓宽度值是可取的。我们计算一系列集群数量的这个度量,并找到它最大化的地方。以下代码显示了 R 中的实现:

*silhouette <- c()
silhouette = c(silhouette, NA)for(i in 2:10){
  pam_clusters = pam(as.matrix(gower_df),
                 diss = TRUE,
                 k = i)
  silhouette = c(silhouette ,pam_clusters$silinfo$avg.width)
}plot(1:10, silhouette,
     xlab = "Clusters",
     ylab = "Silhouette Width")
lines(1:10, silhouette)*

轮廓宽度与聚类数的关系如图所示:

可以看出,该值在 5 处最大。因此,我们可以得出结论,将数据聚类成 5 个簇可以提供最佳的分段。说到这里,我们构建了一个包含 5 个集群的 PAM 模型,并尝试在 medoids 的帮助下解释这些集群的行为。

*pam_german = pam(gower_df, diss = TRUE, k = 5)
german_credit_clean[pam_german$medoids, ]*

上图显示了 medoids 表,其中每一行代表一个聚类。使用该表,我们可以推断出属于聚类 1 的客户具有以下特征:持续时间为 15 个月,信用金额为 1829 美元,分期付款率为 4,他们从 4 个月起就一直住在现居住地,他们的*均年龄为 46 岁,他们有 2 笔贷款未决,他们有 1 个受抚养人,他们没有支票账户(A14),他们的信用历史很重要(A34),他们是男性单身(A93),他们有一辆汽车作为他们的财产(A123),住在他们自己的住房中(A152),并且已经被雇用更多请注意,不是所有的客户都会完全这样;并且 medoids 仅仅是中间值的表示。

对其他集群也可以作出类似的解释。为了更深入地了解每个集群的特征,我们找到了摘要统计信息。下面显示了相同的 R 代码,以及第一个集群的统计数据。

*pam_summary <- german_credit_clean %>%
  mutate(cluster = pam_german$clustering) %>%
  group_by(cluster) %>%
  do(cluster_summary = summary(.))pam_summary$cluster_summary[[1]]*

可视化

最后,让我们用一些奇特的视觉效果来结束这篇文章。为此,我们使用 t-SNE 或 t-分布随机邻居嵌入技术。这项技术提供了一种很好的方式来可视化多维数据集,比如我们正在处理的数据集。

*library(Rtsne)
library(ggplot2)tsne_object <- Rtsne(gower_df, is_distance = TRUE)tsne_df <- tsne_object$Y %>%
  data.frame() %>%
  setNames(c("X", "Y")) %>%
  mutate(cluster = factor(pam_german$clustering))ggplot(aes(x = X, y = Y), data = tsne_df) +
  geom_point(aes(color = cluster))*

正如我们所见,t-SNE 帮助我们将多维数据可视化为一个简单的二维图形。虽然这些集群看起来有一些重叠,但它们总体上非常独特。

这就把我们带到了本文的结尾。总之,我们讨论了高尔距离、PAM 聚类和侧影宽度的概念,以聚类具有数字和分类特征的数据。希望这能帮助周围所有遇到这种数据集的数据人。

快乐聚类!

用 Python 聚类文档

原文:https://towardsdatascience.com/clustering-documents-with-python-97314ad6a78d?source=collection_archive---------2-----------------------

自然语言处理在过去的几年里取得了巨大的进步。目前,神经网络的各种实现是最前沿的,似乎每个人都在谈论它们。但是,有时更简单的解决方案可能更好。毕竟,一个人应该在跑之前先试着走一走。在这篇短文中,我将展示一种用 Python 对文档进行聚类的简单方法。所有代码都可以在 GitHub 获得(请注意,在 nbviewer 中查看代码可能会更好)。

我们将使用 k-means 算法对维基百科文章进行聚类。执行此操作的步骤如下:

  1. 获取一些维基百科的文章,

2.将每篇文章表示为一个向量,

3.执行 k 均值聚类,

4.评估结果。

1.获取维基百科文章

使用维基百科包可以很容易地从维基百科下载内容。对于本例,我们将文章的内容用于:

  • 数据科学
  • 人工智能
  • 机器学习
  • 欧洲中央银行
  • 银行
  • 金融技术
  • 国际货币基金组织
  • 篮球
  • 游泳
  • 网球

每篇维基百科文章的内容存储在 wiki_list 中,而每篇文章的标题存储在变量 title 中。

import pandas as pd
import wikipediaarticles=['Data Science','Artificial intelligence','Machine Learning','European Central Bank','Bank','Financial technology','International Monetary Fund','Basketball','Swimming','Tennis']wiki_lst=[]
title=[]
for article in articles:
   print("loading content: ",article)
   wiki_lst.append(wikipedia.page(article).content)
   title.append(article)print("examine content")
wiki_lstAt the top of the github page there is a button that will allow you to execute the notebook in Google’s Colab. Feel free to experiment!

2.将每篇文章表示为一个向量

因为我们要使用 k-means,所以我们需要将每篇文章表示为一个数字向量。一种流行的方法是使用词频/逆词频(tf-idf) 。简单地说,用这种方法对每个单词 w 和文档 d 我们计算:

tf(w,d)😗*dw 的出现次数除以d中总字数的比值

idf(w): 文档总数除以包含 w. 的文档数的分数的对数

tfidf(w,d)=tf(w,d) x idf(w)

建议排除常见的停用词。所有的计算都可以通过 sklearn 的 tfidf 矢量器轻松完成。

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words={'english'})
X = vectorizer.fit_transform(wiki_lst)

(说实话,sklearn 的计算略有不同。你可以在文档中读到。)

3.执行 k 均值聚类

变量 X 的每一行都是维基百科文章的向量表示。因此,我们可以使用 X 作为 k-means 算法的输入。

首先,我们必须决定集群的数量。这里我们就用肘法。

import matplotlib.pyplot as plt
from sklearn.cluster import KMeansSum_of_squared_distances = []
K = range(2,10)for k in K:
   km = KMeans(n_clusters=k, max_iter=200, n_init=10)
   km = km.fit(X)
   Sum_of_squared_distances.append(km.inertia_)plt.plot(K, Sum_of_squared_distances, 'bx-')
plt.xlabel('k')
plt.ylabel('Sum_of_squared_distances')
plt.title('Elbow Method For Optimal k')
plt.show()

每组数量的距离*方和图

情节几乎是一条直线,可能是因为我们要的文章不多。但是在更*的观察中,当 k=4 或 k=6 时,出现了一个凹痕。我们将尝试分成 6 组。

true_k = 6
model = KMeans(n_clusters=true_k, init='k-means++', max_iter=200, n_init=10)
model.fit(X)
labels=model.labels_
wiki_cl=pd.DataFrame(list(zip(title,labels)),columns=['title','cluster'])
print(wiki_cl.sort_values(by=['cluster']))

聚类结果

4.评估结果

因为我们只使用了 10 篇文章,所以通过检查每个集群中包含哪些文章来评估集群是相当容易的。这对于大型语料库来说是很困难的。一个很好的方法是从每个集群的文章中创建一个词云。

from wordcloud import WordCloud
result={'cluster':labels,'wiki':wiki_lst}
result=pd.DataFrame(result)
for k in range(0,true_k):
   s=result[result.cluster==k]
   text=s['wiki'].str.cat(sep=' ')
   text=text.lower()
   text=' '.join([word for word in text.split()])
   wordcloud = WordCloud(max_font_size=50, max_words=100, background_color="white").generate(text)
   print('Cluster: {}'.format(k))
   print('Titles')
   titles=wiki_cl[wiki_cl.cluster==k]['title']         
   print(titles.to_string(index=False))
   plt.figure()
   plt.imshow(wordcloud, interpolation="bilinear")
   plt.axis("off")
   plt.show()

星团的文字云

  • 集群 0 由关于欧洲中央银行、银行和国际货币基金组织的文章组成
  • 集群 1 由关于人工智能和机器学习的文章组成
  • 第二组有一篇关于游泳的文章
  • 集群 3 有一篇关于数据科学的文章
  • 集群 4 有关于篮球和网球的文章
  • 第五组有一篇关于金融科技的文章

游泳不属于篮球和网球这一类似乎有点奇怪。或者说 AI 和机器学习跟数据科学不是一个群体。这是因为我们选择创建 6 个集群。但是通过查看单词 clouds,我们可以看到关于篮球和网球的文章中有一些单词是相同的,比如游戏、运动员、团队、球,而关于游泳的文章则没有。

在 GitHub 页面的顶部,有一个按钮,可以让你在谷歌的 Colab 中执行笔记本。您可以很容易地尝试不同数量的集群。结果可能会让你大吃一惊!

悉尼的美食聚集地

原文:https://towardsdatascience.com/clustering-food-venues-in-sydney-bf48877650d5?source=collection_archive---------49-----------------------

使用 Foursquare API 和悉尼美食聚集地。

雅各布·卡普斯纳克在 Unsplash 上的照片

背景信息

对一些人来说,很难确定他们想和朋友去哪里吃饭。有这么多不同的食物和菜肴可供选择。一般来说,如果一个地区因某种特定的美食而闻名,那么游客更有可能在那个地区吃那种美食。澳大利亚是一个多元文化的国家,在共同的地方不难找到不同的民族社区。

在这个项目中,我们的目标是帮助人们根据自己的兴趣找到在悉尼就餐的地点。例如,如果你想吃泰国菜,你可以去你周围的郊区。我们将使用 Foursquare APIK-means 聚类来发现地区中最常见的美食,并在地图上可视化聚类。

数据

悉尼火车站附*有很多餐馆。为此,我编制了一个数据集,其中包含了悉尼所有的火车站和地铁站,以及它们各自的经度和纬度坐标。这将使我们能够在悉尼的地图上看到这些地点。车站位置的选择是因为大多数悉尼工人都乘坐公共交通工具。靠*车站的餐馆为居民提供了方便的选择。这里可以查看数据集,共 181 个站。

。悉尼站数据帧的 head()

方法和分析

在将悉尼站读入一个熊猫数据框后,我们现在将使用 Foursquare API 来查找我们站附*的美食地点。然后,我们将使用 K-means 聚类来创建具有相似特征的站点的聚类。

首先,让我们看看我们的站在悉尼地图上是什么样子,以检查我们的坐标是否正确。我们可以使用 geopy叶子库来创建一张悉尼地图,上面叠加我们的站坐标。

悉尼火车/地铁网络——使用笔获得交互式地图

现在,我们使用 four square API T1,这是一种社交定位服务,允许用户发现商业和景点。由于我们试图找到与食物相关的地点,我添加了类别 ID '4d 4b 7105d 754 a 06374d 81259'作为 API 请求 URL 的一部分。我将返回的场地限制为 300 个,半径为 1 公里。

url = 'https://api.foursquare.com/v2/venues/search?&categoryId=4d4b7105d754a06374d81259&client_id=**{}**&client_secret=**{}**&v=**{}**&ll=**{}**,**{}**&radius=**{}**&limit=**{}**'.format(
            CLIENT_ID, 
            CLIENT_SECRET, 
            VERSION, 
            lat, 
            lng, 
            radius, 
            LIMIT)

利用我们提取的附*美食场所的信息,我们可以将其合并到一个包含我们的站点信息的新数据帧中。

。head()提取的悉尼美食场馆数据框

四方共退回 5565 家美食场所。然而,当查看一些站点的场馆数量时,一些站点的数量很少或者根本没有。为了使这些站点不影响聚类,我删除了所有少于 9 个美食场所的站点。这导致了总共 5458 个美食场所和 175 个独特的类别。从 181 个站点,我们的数据帧现在包含 142 个站点,即删除了 39 个站点。

在使用一种热编码并对每个食品场所类别的频率取*均值后,我们可以使用 K-means 聚类,这是一种无监督学习算法,用于根据特征相似性创建数据点的 K 聚类。

sydney_onehot = pd.get_dummies(sydney_venues[['Venue Category']], prefix="", prefix_sep="")

*# add neighborhood column back to dataframe*
sydney_onehot['Station'] = sydney_venues['Station'] 

*# move neighborhood column to the first column*
fixed_columns = [sydney_onehot.columns[-1]] + list(sydney_onehot.columns[:-1])
sydney_onehot = sydney_onehot[fixed_columns]sydney_grouped = sydney_onehot.groupby('Station').mean().reset_index()
sydney_grouped.head()

悉尼分组数据框显示了前 7 个类别的频率*均值

使用这个数据框,我们可以删除第一列并将其用于 K-means 聚类。观察肘方法和剪影评分,我们发现要使用的最佳聚类数是 K = 5

最佳 K 的肘方法和轮廓分数

生成聚类标签后,我们可以将其添加到新的数据框中,合并原始数据框和它们各自的前 10 个场馆。

由于之前已经删除了一些站点,我们获得了一些站点的 NaN 值,因此我们使用 dropna() 方法删除了这些行。
我将每个车站的第一个最常见的场馆可视化,按照它们的集群进行分组,让我们清楚地了解这些集群可能具有的特征。

通过查看条形图,我归纳了每个集群并给它们贴上标签。
我不得不包括几个类别作为标签,因为顶级场所有相似之处,例如咖啡馆:

数据框中每个聚类的标签

我还添加了每个站点的前三个地点,这样我们可以在地图上查看它们。

然后,通过合并先前的数据帧,创建包含所有新信息的最终数据帧。

结果

我们的最终地图显示的信息集群创建。

悉尼车站集群地图

对总共 142 个站(最初来自 181 个)进行聚类的结果展示了 5 个聚类。我们将集群归纳如下:

  • 集群 0(黄色)-23 个车站、面包店/咖啡馆/印度餐馆场馆
  • 聚类 1(青色)-2 个车站,韩国餐馆
  • 第 2 组(紫色)——35 个车站、咖啡厅
  • 集群 3(蓝色)-63 个车站、咖啡馆/咖啡/泰国餐馆场馆
  • 第 4 组(红色)-19 个车站,中国/越南/披萨餐厅场馆

悉尼车站周围的美食场所-使用笔来获得交互式地图版本

虽然有些车站可能与标签描述不同,但它很好地概述了有哪些类型的餐馆或美食场所。

结论

总之,我们使用了 K-means 聚类,为悉尼火车站和地铁站创建了 5 个不同的聚类,这些火车站和地铁站周围至少有 9 个美食场所。下次你出去吃饭的时候(希望快点!),你可以看看这张地图,了解一下悉尼车站和周围的美食广场都提供什么样的食物。

感谢阅读!

这是我的“探索悉尼的地区和场所”文章的第 2 部分,在这里我重点介绍了悉尼的餐馆和其他与食物相关的场所。
你可以在这里查看这个项目的全部代码/笔记本。

欢迎在 LinkedIn 上联系我,或者在 Medium 上关注我。

高级数据聚类教程

原文:https://towardsdatascience.com/clustering-for-data-nerds-ebbfb7ed4090?source=collection_archive---------9-----------------------

K 均值,基尼系数,熵的终极指南。如何用 R 和 Python 从相机图像中对人体手势进行分类?

在本文中,我们使用 R 和 Python 实现了数据分割和数据聚类,用于对相机图像中的手势和人体姿态进行分类。

来源

我们什么时候对数据进行聚类?

当没有明确定义的分类或回归任务时,我们面临着一种无监督学习的情况,在这种情况下,我们基本上探索观察组,并尝试对数据进行聚类,以便有意义。

聚类的目标是将一组对象组合在一起,使得同一聚类中的对象彼此之间比其他聚类中的对象更相似。

来源:加州大学 Jonathan Richard Shewchuk 教授的讲稿

相似度是反映两帧之间关系强度的量。相似性度量的经典方法是欧几里德距离和曼哈顿距离

K-Means 聚类是如何工作的?

K-Means 聚类是最简单也是最常用的聚类方法,用于将数据集分割成一组 K 个(预定义的)组。

  • 该算法从随机选择 K 个称为质心的起始点开始。

来源:马蒂亚·西内利的博客

  • 然后,它计算数据集中每个点与质心之间的距离。被认为最接*给定质心的点被认为是由该质心表示的聚类的一部分。

来源:马蒂亚·西内利的博客

  • 在最后一步中,在每个聚类内计算一个新的质心作为*均点。重复这些步骤,直到质心不再改变。

来源:马蒂亚·西内利的博客

手势识别的数据聚类

手势和手势识别旨在识别特定的人类手势,并使用它们来传递信息。对非语言交流进行正确的分类对于一个熟练的人机交互框架来说是必不可少的。数据聚类有助于解决这个问题。

聚类的工作原理是探索从一组用户记录的视频姿势,并对数据进行分区,使其有意义。

通过将视频帧分成簇,我们可以认识到每个用户都有一个独特的姿势集。我们还可以将姿势分组,显示所有用户以相似的方式执行相同的姿势。

我们可能会发现异常聚类,这些异常聚类识别出执行特定任务的用户与其他人有很大不同。这些发现可能有助于将这些簇与某种疾病状况联系起来,或者突出一些罕见的物理特性。

数据

我们将聚集从运动捕捉相机系统收集的数据,发表在这篇加德纳的论文。该系统被用于记录 14 个不同的用户进行 5 种不同的手部姿势,并在左手手套上附着标记。

来源:安德鲁·加德纳,加州大学

手套背面的一组 4 个标记用于建立手的局部坐标系。它们在手背上形成一个固定的图案,作为手的位置和方向的标识。他们还为附着在手套的拇指和手指上的剩余标记创建了一个局部坐标系。

来源:安德鲁·加德纳,加州大学

基于来自照相机系统的观察,总共收集了 24 个特征。数据集中的另外两个变量是用户的 ID 和用户做出的姿势。

完整的数据集可在这里获得。所有标记都被转换到包含它们的记录的本地坐标系。这些数据已经过预处理,包括转换到记录的本地坐标系统、去除异常值和去除缺失数据。

数据集postures_clean.csv包含 38,943 行和 26 列。每行对应于照相机系统捕获的单个帧。

这些列如下所述。

描述给定观察的手部姿势,具有以下可能值:

1 = Fist (with thumb out)
2 = Stop (hand flat)
3 = Point1 (point with index finger)
4 = Point2 (point with index and middle fingers)
5 = Grab (fingers curled as if to grab)

用户代表贡献记录的用户的 ID。

X0,Y0,Z0,X1,Y1,Z1,…,X7,Y7,Z7 代表八个未标记标记位置的 x 坐标,y 坐标,z 坐标。

下面我们绘制了每个用户和每种姿势记录的帧数。我们可以观察到一些用户缺少一些姿势。此外,为给定姿势记录的帧数因用户而异。我们还注意到,当用户 id 从 0 到 14 时,用户 id 3 丢失了,剩下 14 个用户。

我们观察到,由于自遮挡,许多原始记录缺少标记并被从数据集中删除。由于记录中可见标记的数量总是 8,我们可以得出结论,一些用户的姿势被完全删除了。这将解释例如用户 4 和 7 仅具有少量记录且仅执行了一些姿势。

有些姿势记录的总帧数非常少。例如,类别 1(拇指向外的拳头)只为 3 个用户记录,总共 52 帧。大概是用户握拳的时候大部分标记都被遮挡了。

聚类需要距离的概念

查看数据,我们注意到*均值 X、Y、Z 大致来自同一分布。不存在可见的异常值。这表明坐标代表现实世界中的物理位置。我们还可以观察到,值的范围(最小值和最大值)与 X 的不同,而与 Y 或 z 的不同。如果我们在 3D 空间中绘制数据,它在空间中会显得受到挤压。

这些发现表明,扩大这类数据可能不是一个好主意。缩放会将例如所有用户的 X0 值投影到同一坐标系上,从而失去校准的局部性。因此,我们将在不缩放坐标的情况下继续。

欧几里德距离对于我们数据集中的坐标非常有意义。然而,让 id 为 0 的用户和 id 为 14 的用户之间的欧几里德距离影响聚类是不太合适的。类似地,姿势类别 1 与姿势类别 5 的不同之处与姿势类别 2 的不同之处相同。因此,如果我们将用户和类特性包括在集群任务中,我们可能必须扩展它们。

主成分分析聚类

PCA 可用于从我们的 26 维特征集中导出 2 维特征集,同时努力保留尽可能多的信息(方差)。

我们设想下面的前两个主要部分。虽然有两个组件可以解释数据中的差异,但是它们不能对记录进行聚类。

k-表示 R 中的实现

在本文中,我们演示了如何在 Python 笔记本中使用 R 执行 K-Means 聚类。这多亏了 rpy2 ,一个到 R 语言的 Python 接口。

以下函数对输入数据帧df执行 K 均值聚类。cols_features是我们要考虑的数据帧中的列列表。如果为空,则考虑所有列。cols_to_scale是需要缩放以进行适当相似性测量的特征子集。nb_clusters是我们尝试构建的预定义集群数量。nb_config是这个过程重复的次数。cluster_col_name是用于在原始数据帧中存储聚类标签的列名。

我们首先仅使用坐标特征对数据进行聚类。

现在,我们使用所有列进行聚类。我们感兴趣的是调查用户和类如何影响集群的学习。对于我们的任务来说,这一步没有多大意义,但是我们很好奇 K-Means 是否能够拾取标签。为了保持一致的距离度量,我们在聚类之前对用户和类列进行了标准化。

集群数量的选择很重要

下面,我们使用 R 函数r_factoextra.fviz_cluster可视化 2000 个观察值的随机样本的 K 均值结果。

在主成分分析图上,聚类看起来非常重叠。第一个维度只能解释数据中 13%的差异,而第二个因素只能解释 8%。在那个空间中,一些质心彼此非常接*。我们可能想把一些集群合并在一起。对于我们的探索,使用 14 个集群并不是一个好的选择。

下面,我们通过将聚类的数量设置为 5 来执行用于姿势识别的 K 均值聚类。可视化显示了更好的集群分离。

集群有多好?

重要的是评估我们的聚类有多好,换句话说,这些区域的分离程度有多好。

基尼系数聚类优度

基尼系数是评价集群集合异质性程度的常用方法。对于 K 个总标签,每个聚类的基尼系数如下所示,其中 p 是聚类中带有标签 i 的数据点的比例。

在我们的例子中,基尼系数将测量任何用户是否以高比例出现在所创建的任何一个集群中。如果一个或多个用户以高比例出现在所考虑的集群中,则该集群的*方和将更接* 1,并且基尼系数将更接* 0。

通常,如果类别在聚类中完全混合,基尼系数最大。第 2 和第 4 簇最纯,系数分别为 0.405 和 0.285。第二类具有特定姿势的强比例(0.697),并且很可能是该姿势的强代表。

这为基于手边数据集的姿势识别目的的学习聚类提供了额外的证据。

熵聚类优度

熵也可以用来检验聚类质量。它利用了簇 i 中的记录被分类为类别 i 的概率。

虽然基尼系数旨在将错误分类的概率降至最低,但熵是一种衡量杂质的方法。

如果聚类的所有记录都属于同一姿态,则熵为 0,如果我们具有均匀的姿态分布,则熵最大。换句话说,因为概率是 1log(1) = 0 ,所以分离良好的聚类的熵为零。当集群中的所有姿态具有相等的概率时,熵达到最大值。下面我们可以看到第一、第三和第五簇是最不纯的。

结论

结果表明,我们的数据集更有可能被分为 5 组,而不是 14 组。给定记录标记的位置,我们几乎没有机会识别记录是从哪个用户那里产生的。从同一用户获得的位置在不同的手姿势之间没有足够的相似性,因此它们可以被分组到不同的群中。

然而,用户以相似的方式执行姿势,因此不同用户记录的位置之间的距离最小,允许我们以可接受的纯度将它们分组为姿势群。

这些结果是有意义的,但我们可能需要寻找最佳的聚类数,这将使我们实现更好的分离,并可能了解更多的数据。使用肘方法间隙统计剪影技术我们将能够评估群集数量的最佳选择。

聚类地理空间数据

原文:https://towardsdatascience.com/clustering-geospatial-data-f0584f0b04ec?source=collection_archive---------1-----------------------

使用交互式地图绘制机器学习和深度学习聚类

摘要

在本文中,我将使用数据科学和 Python,展示如何将不同的聚类算法应用于地理空间数据,以解决零售合理化业务案例。

门店合理化 是公司为了提高经营效率,降低成本而进行的重组。由于 T4 和新冠肺炎的危机,世界各地的几家零售企业正在关闭店铺。这并不仅仅是金融危机的症状,事实上许多公司已经将投资集中在使他们的业务更加数字化上。

聚类 是对一组对象进行分组的任务,使同一组中的观察值彼此之间比其他组中的观察值更相似。这是无监督学习(没有目标变量时的机器学习)最受欢迎的应用之一。

地理空间分析 是处理卫星图像、GPS 坐标和街道地址以应用于地理模型的数据科学领域。

在本文中,我将使用地理数据聚类来解决零售合理化问题。我将展示一些有用的 Python 代码,这些代码可以很容易地应用于其他类似的情况(只需复制、粘贴、运行),并通过注释遍历每一行代码,以便您可以复制这个示例(下面是完整代码的链接)。

[## mdipietro 09/data science _ 人工智能 _ 实用工具

github.com](https://github.com/mdipietro09/DataScience_ArtificialIntelligence_Utils/blob/master/machine_learning/example_clustering.ipynb)

我将使用" Starbucks Stores dataset "提供所有正在运营的商店的位置(下面的链接)。我将选择一个特定的地理区域,除了所提供的纬度和经度之外,我将模拟数据集中每个商店的一些业务信息(成本、容量、员工)。

[## 星巴克在全球的位置

运营中的每家星巴克店的名称、所有权类型和位置

www.kaggle.com](https://www.kaggle.com/starbucks/store-locations)

特别是,我将经历:

  • 设置:导入包,读取地理数据,创建业务功能。
  • 数据分析:用叶子geopy 在地图上展示商业案例。
  • 聚类:用 scikit-learn 的机器学习(K-Means / Affinity Propagation),用 minisom 的深度学习(自组织映射)。
  • 商店合理化:构建确定性算法来解决业务案例。

设置

首先,我需要导入以下包。

**## for data**
import **numpy** as np
import **pandas** as pd**## for plotting**
import **matplotlib**.pyplot as plt
import **seaborn** as sns**## for geospatial**
import **folium**
import **geopy****## for machine learning**
from **sklearn** import preprocessing, cluster
import **scipy****## for deep learning**
import **minisom**

然后我将把数据读入一个熊猫数据帧。

dtf = pd.read_csv('data_stores.csv')

原始数据集包含超过 5,000 个城市和 25,000 家商店,但是出于本教程的目的,我将只处理一个城市。

filter = **"Las Vegas"**dtf = dtf[dtf["City"]==filter][["City","Street Address","Longitude","Latitude"]].reset_index(drop=True)dtf = dtf.reset_index().rename(columns={"index":"id"})
dtf.head()

在那个地区,有 156 家商店。为了继续进行业务案例,我将模拟每个商店的一些信息:

  • 潜在能力:员工的总能力(例如,10 表示商店最多可容纳 10 名员工)
  • 员工:当前员工级别(如 7 表示该店目前有 7 名员工在运营)
  • 容量:当前剩余容量(如 10–7 = 3,商店仍可容纳 3 名员工)
  • 成本:公司维持店铺运营的年度成本()
dtf["**Potential**"] = np.random.randint(low=3, high=10+1, size=len(dtf))dtf["**Staff**"] = dtf["**Potential**"].apply(lambda x: int(np.random.rand()*x)+1)dtf["**Capacity**"] = dtf["**Potential**"] - dtf["**Staff**"]dtf["**Cost**"] = np.random.choice(["high","medium","low"], size=len(dtf), p=[0.4,0.5,0.1])dtf.head()

请注意,这只是一个模拟,这些数字是随机产生的,并不真正反映星巴克(或任何其他公司)的业务。

现在一切都准备好了,我将从分析业务案例开始,然后构建一个聚类模型和一个合理化算法。

我们开始吧,好吗?

数据分析

假设我们拥有一家零售企业,我们不得不关闭一些商店。我们希望在不裁员的情况下实现利润最大化(通过成本最小化)。

费用分配如下:

**x = "Cost"**ax = dtf[x].value_counts().sort_values().plot(kind="barh")
totals = []
for i in ax.patches:
    totals.append(i.get_width())
total = sum(totals)
for i in ax.patches:
     ax.text(i.get_width()+.3, i.get_y()+.20, 
     str(round((i.get_width()/total)*100, 2))+'%', 
     fontsize=10, color='black')
ax.grid(axis="x")
plt.suptitle(x, fontsize=20)
plt.show()

目前,只有一小部分商店在满负荷运转(剩余容量= 0),这意味着有些商店的员工数量很少(剩余容量很高):

让我们把这些信息在地图上形象化。首先,我需要获得地理区域的坐标来启动地图。我会用 geopy 来做:

city = **"Las Vegas"****## get location**
locator = geopy.**geocoders**.**Nominatim**(user_agent="MyCoder")
location = locator.geocode(city)
print(location)**## keep latitude and longitude only**
location = [location.latitude, location.longitude]
print("[lat, long]:", location)

我将使用创建地图,这是一个非常方便的包,允许我们绘制交互式地图,而无需加载 shapefile 。每个商店应通过一个点来识别,该点的大小与其当前的员工人数成比例,颜色基于其成本。我还将向默认地图添加一小段 HTML 代码来显示图例。

**x, y = **"Latitude", "Longitude"**
color = **"Cost"** size = **"Staff"** popup = **"Street Address"**
data = dtf.copy() **## create color column**
lst_colors=["red","green","orange"]
lst_elements = sorted(list(dtf[color].unique()))
data["color"] = data[color].apply(lambda x: 
                lst_colors[lst_elements.index(x)])**## create size column (scaled)**
scaler = preprocessing.MinMaxScaler(feature_range=(3,15))
data["size"] = scaler.fit_transform(
               data[size].values.reshape(-1,1)).reshape(-1) **## initialize the map with the starting location** map_ = folium.**Map**(location=location, tiles="cartodbpositron",
                  zoom_start=11)**## add points**
data.apply(lambda row: folium.**CircleMarker**(
           location=[row[x],row[y]], popup=row[popup],
           color=row["color"], fill=True,
           radius=row["size"]).add_to(map_), axis=1)**## add html legend** legend_html = """<div style="position:fixed; bottom:10px; left:10px; border:2px solid black; z-index:9999; font-size:14px;">&nbsp;<b>"""+color+""":</b><br>"""
for i in lst_elements:
     legend_html = legend_html+"""&nbsp;<i class="fa fa-circle 
     fa-1x" style="color:"""+**lst_colors[lst_elements.index(i)]**+"""">
     </i>&nbsp;"""+str(i)+"""<br>"""
legend_html = legend_html+"""</div>"""map_.get_root().**html.add_child**(folium.**Element**(legend_html)) **## plot the map**
map_**

我们的目标是尽可能多地关闭高成本商店(红点),将员工转移到位于同一社区的低成本商店(绿点)。因此,我们将最大化利润(通过关闭高成本商店)和效率(通过让低成本商店满负荷工作)。

我们如何在不选择距离阈值和地理边界的情况下定义邻域?嗯,答案是……集群。

使聚集

有几种算法可以使用,这里列出了主要的和。我将尝试 K-Means、相似性传播、自组织映射。

K-Means 旨在将观察值划分为预定义数量的聚类( k ),其中每个点属于具有最*均值的聚类。它首先随机选择 k 个质心并将这些点分配给最*的聚类,然后用聚类中所有点的*均值更新每个质心。当您需要获得精确的组数时,这种算法很方便(例如,保持最小数量的运营商店),并且它更适合于少量的偶数聚类。

这里,为了定义正确的 k,我将使用肘方法:绘制方差作为集群数量的函数,并选择使曲线变*的 k

**X = dtf[[**"Latitude","Longitude"**]]max_k = **10****## iterations**
distortions = [] 
for i in range(1, max_k+1):
    if len(X) >= i:
       model = cluster.KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=10, random_state=0)
       model.fit(X)
       distortions.append(model.inertia_)**## best k: the lowest derivative**
k = [i*100 for i in np.diff(distortions,2)].index(min([i*100 for i 
     in np.diff(distortions,2)]))**## plot**
fig, ax = plt.subplots()
ax.plot(range(1, len(distortions)+1), distortions)
ax.axvline(k, ls='--', color="red", label="k = "+str(k))
ax.set(title='The Elbow Method', xlabel='Number of clusters', 
       ylabel="Distortion")
ax.legend()
ax.grid(True)
plt.show()**

我们可以尝试使用 k = 5 ,这样 K-Means 算法将找到 5 个理论质心。此外,我还将确定真正的质心(离聚类中心最*的观测值)。

**k = 5
model = cluster.**KMeans**(n_clusters=k, init='k-means++')
X = dtf[[**"Latitude","Longitude"**]]**## clustering**
dtf_X = X.copy()
dtf_X["cluster"] = model.fit_predict(X)**## find real centroids**
closest, distances = scipy.**cluster.vq.vq**(model.cluster_centers_, 
                     dtf_X.drop("cluster", axis=1).values)
dtf_X["centroids"] = 0
for i in closest:
    dtf_X["centroids"].iloc[i] = 1**## add clustering info to the original dataset** dtf[["**cluster**","**centroids**"]] = dtf_X[["**cluster**","**centroids**"]]
dtf.sample(5)**

我向数据集添加了两列:" cluster "表示观察值属于哪个簇,以及" centroids "如果观察值也是质心(最靠*中心),则为 1,否则为 0。让我们计划一下:

****## plot**
fig, ax = plt.subplots()
sns.**scatterplot**(x="Latitude", y="Longitude", data=dtf, 
                palette=sns.color_palette("bright",k),
                hue='cluster', size="centroids", size_order=[1,0],
                legend="brief", ax=ax).set_title('Clustering 
                (k='+str(k)+')')th_centroids = model.cluster_centers_
ax.scatter(th_centroids[:,0], th_centroids[:,1], s=50, c='black', 
           marker="x")**

相似性传播 是一种基于图形的算法,将每个观察值分配给其最*的样本。基本上,所有的观察“投票”给它们想要关联的其他观察,这导致将整个数据集划分成大量不均匀的聚类。当您不能指定簇的数量时,这是非常方便的,并且它适用于地理空间数据,因为它可以很好地处理非*面几何。

**model = cluster.**AffinityPropagation**()**

使用之前的相同代码,您可以拟合模型(找到 12 个簇),并且您可以使用下面的代码来绘图(不同之处在于 k 在开始时没有声明,并且没有理论上的质心):

**k = dtf["cluster"].nunique()sns.**scatterplot**(x="Latitude", y="Longitude", data=dtf, 
                palette=sns.color_palette("bright",k),
                hue='cluster', size="centroids", size_order=[1,0],
                legend="brief").set_title('Clustering 
                (k='+str(k)+')')**

自组织地图 (SOMs)由于使用深度学习而大不相同。事实上,SOM 是一种人工神经网络,它使用无监督学习进行训练,以产生输入空间的低维表示,称为“地图”(也称为 Kohonen 层)。基本上,输入连接到形成映射的 n x m 神经元,然后为每个观察计算“获胜”神经元(最*的),并使用横向距离将神经元聚集在一起。这里,我将尝试使用 4x4 SOM:

**X = dtf[[**"Latitude","Longitude"**]]
map_shape = **(4,4)****## scale data**
scaler = preprocessing.**StandardScaler**()
X_preprocessed = scaler.fit_transform(X.values)**## clustering** model = minisom.**MiniSom**(x=map_shape[0], y=map_shape[1], 
                        input_len=X.shape[1])
model.train_batch(X_preprocessed, num_iteration=100, verbose=False)**## build output dataframe**
dtf_X = X.copy()
dtf_X["cluster"] = np.**ravel_multi_index**(np.array(
      [model.winner(x) for x in X_preprocessed]).T, dims=map_shape)**## find real centroids** cluster_centers = np.array([vec for center in model.get_weights() 
                            for vec in center])closest, distances = scipy.**cluster.vq.vq**(model.cluster_centers_, 
                                         X_preprocessed)
dtf_X["centroids"] = 0
for i in closest:
    dtf_X["centroids"].iloc[i] = 1**## add clustering info to the original dataset** dtf[["cluster","centroids"]] = dtf_X[["cluster","centroids"]]**## plot** k = dtf["cluster"].nunique()fig, ax = plt.subplots()
sns.**scatterplot**(x="Latitude", y="Longitude", data=dtf, 
                palette=sns.color_palette("bright",k),
                hue='cluster', size="centroids", size_order=[1,0],
                legend="brief", ax=ax).set_title('Clustering 
                (k='+str(k)+')')th_centroids = scaler.inverse_transform(cluster_centers)
ax.scatter(th_centroids[:,0], th_centroids[:,1], s=50, c='black', 
           marker="x")**

独立于您用来对数据进行聚类的算法,现在您有了一个多了两列的数据集(" cluster "," centroids ")。我们可以用它来可视化地图上的星团,这次我也要用一个标记来显示质心。

**x, y = **"Latitude", "Longitude"**
color = **"cluster"** size = **"Staff"** popup = **"Street Address"** marker = "**centroids**"
data = dtf.copy()**## create color column**
lst_elements = sorted(list(dtf[color].unique()))
lst_colors = ['#%06X' % np.random.randint(0, 0xFFFFFF) for i in 
              range(len(lst_elements))]
data["color"] = data[color].apply(lambda x: 
                lst_colors[lst_elements.index(x)])**## create size column (scaled)**
scaler = preprocessing.MinMaxScaler(feature_range=(3,15))
data["size"] = scaler.fit_transform(
               data[size].values.reshape(-1,1)).reshape(-1)**## initialize the map with the starting location** map_ = folium.**Map**(location=location, tiles="cartodbpositron",
                  zoom_start=11)**## add points**
data.apply(lambda row: folium.**CircleMarker**(
           location=[row[x],row[y]], popup=row[popup],
           color=row["color"], fill=True,
           radius=row["size"]).add_to(map_), axis=1)**## add html legend** legend_html = """<div style="position:fixed; bottom:10px; left:10px; border:2px solid black; z-index:9999; font-size:14px;">&nbsp;<b>"""+color+""":</b><br>"""
for i in lst_elements:
     legend_html = legend_html+"""&nbsp;<i class="fa fa-circle 
     fa-1x" style="color:"""+**lst_colors[lst_elements.index(i)]**+"""">
     </i>&nbsp;"""+str(i)+"""<br>"""
legend_html = legend_html+"""</div>"""map_.get_root().**html.add_child**(folium.**Element**(legend_html))**## add centroids marker**
lst_elements = sorted(list(dtf[marker].unique()))
data[data[marker]==1].apply(lambda row: 
           folium.**Marker**(location=[row[x],row[y]], 
           popup=row[marker], draggable=False,          
           icon=folium.**Icon**(color="black")).add_to(map_), axis=1)**## plot the map**
map_**

现在我们有了集群,我们可以在每个集群内部开始商店合理化。

商店合理化

因为本文的主要焦点是地理空间数据的聚类,所以我将保持这一部分非常简单。在每个集群中,我将选择潜在目标(高成本商店)和中心(低成本商店),并将目标的员工重新安置在中心,直到后者达到满负荷。当一个目标的所有员工都被转移时,商店可以关闭。

集群内的迭代

**dtf_new = pd.DataFrame()for c in sorted(dtf["cluster"].unique()):
    dtf_cluster = dtf[dtf["cluster"]==c]

   ** ## hubs and targets**
    lst_hubs = dtf_cluster[dtf_cluster["Cost"]=="low"
               ].sort_values("Capacity").to_dict("records")
    lst_targets = dtf_cluster[dtf_cluster["Cost"]=="high"
               ].sort_values("Staff").to_dict("records") **## move targets**
    for target in lst_targets:
         for hub in lst_hubs:
             **### if hub has space**
             if hub["Capacity"] > 0:
                residuals = hub["Capacity"] - target["Staff"] **#### case of hub has still capacity: do next target**
                if residuals >= 0:
                   hub["Staff"] += target["Staff"]
                   hub["Capacity"] = hub["Potential"] - hub["Staff"]
                   target["Capacity"] = target["Potential"]
                   target["Staff"] = 0
                   break **#### case of hub is full: do next hub**
                else:
                   hub["Capacity"] = 0
                   hub["Staff"] = hub["Potential"]
                   target["Staff"] = -residuals
                   target["Capacity"] = target["Potential"] - 
                                          target["Staff"] dtf_new = dtf_new.append(pd.DataFrame(lst_hubs)
                 ).append(pd.DataFrame(lst_targets))dtf_new = dtf_new.append(dtf[dtf["Cost"]=="medium"]
                 ).reset_index(drop=True).sort_values(
                 ["cluster","Staff"])
dtf_new.head()**

这是一个非常简单的算法,可以通过多种方式进行改进:例如,将中等成本的商店纳入等式,并在低成本商店全部满员时重复这一过程。

让我们看看我们用这个基本流程关闭了多少家高成本商店:

**dtf_new["**closed**"] = dtf_new["**Staff**"].apply(lambda x: 1 
                                           if x==0 else 0)
print("closed:", dtf_new["**closed**"].sum())**

我们成功关闭了 19 家商店,但我们是否也保持了该地区的均匀覆盖,这样顾客就不需要去另一个社区逛商店了?让我们在地图上标出关闭的商店( marker = "closed" )来想象一下后果:

结论

这篇文章是一篇关于如何在零售商业案例中使用聚类和地理空间分析的教程。我使用模拟数据集来比较流行的机器学习和深度学习方法,并展示了如何在交互式地图上绘制输出。我还展示了一个简单的确定性算法来提供业务案例的解决方案。

我希望你喜欢它!如有问题和反馈,或者只是分享您感兴趣的项目,请随时联系我。

👉我们来连线👈

本文是用 Python 进行机器学习系列的一部分,参见:

** [## 用 Python 进行机器学习:分类(完整教程)

数据分析和可视化、特征工程和选择、模型设计和测试、评估和解释

towardsdatascience.com](/machine-learning-with-python-classification-complete-tutorial-d2c99dc524ec) [## Python 机器学习:回归(完整教程)

数据分析和可视化、特征工程和选择、模型设计和测试、评估和解释

towardsdatascience.com](/machine-learning-with-python-regression-complete-tutorial-47268e546cea) [## Python 深度学习:神经网络(完整教程)

用 TensorFlow 建立、绘制和解释人工神经网络

towardsdatascience.com](/deep-learning-with-python-neural-networks-complete-tutorial-6b53c0b06af0) [## 基于神经网络的现代推荐系统

使用 Python 和 TensorFlow 构建混合模型

towardsdatascience.com](/modern-recommendation-systems-with-neural-networks-3cc06a6ded2c)**

聚集不稳定性

原文:https://towardsdatascience.com/clustering-instability-486643bb686e?source=collection_archive---------73-----------------------

S 选举集群数量

聚类是一种无监督的学习技术,用于创建数据点的聚类。营销中的客户细分就是一个例子。

有几种聚类算法可用。然而,它们需要给定聚类数(k)作为输入。选择聚类的数量可能是困难的,因为这是一个无监督的问题,没有标签。

给定 k,可以测量聚类不稳定性以确定聚类算法的性能。我们更希望聚类是稳定的,但这意味着什么呢?

假设你有一个观察样本(X),我们可以打乱这个数据集,取三个子样本。这三个子样本来自同一个样本 X,因此它们应该遵循相同的结构。

我们选择聚类算法并训练两个不同版本的模型。首先,在数据集 z1 上拟合模型。第二,在数据集 z2 上拟合不同的模型。在这两种情况下,给定特定的 k 值,即聚类数,应用相同的算法。

型号 1.fit(z1)

模型 2 .拟合(z2)

在两个模型都被训练之后,我们预测第三个(验证)子样本 z3 的标签。

预测模型 1(z3)

预测模型 2(z3)

为了测量该聚类映射的不稳定性,我们计算如下:

  • 对于每一个观察点 i 和它的邻居 j = i+1
  • 检查模型 1 是否同意它们具有相同的标签:标签 1(xi) =标签 1(xj)
  • 检查模型 2 是否同意它们具有相同的标签:标签 2(xi) =标签 2(xj)

如果模型 1 将观察值 I 和 j 标记为相等,但是另一方面,模型 2 将两者标记为不同,这将导致聚类映射的不稳定性。

如果同意 1!=同意 2:

不稳定性+= 1

这就引出了这个 k 和这些子样本的不稳定性分数。我们希望对 k 的范围重复这一过程,以评估所有的聚类不稳定性。

最后,我们选择使不稳定性最小化的聚类数(k)。

如有任何关于理论或如何编码的问题,请随时回复。

我目前正在开发一个应用程序,通过潜在的狄利克雷分配来选择主题建模中的主题数量,如果您想看后续报道,请告诉我!

聚类移动对象轨迹

原文:https://towardsdatascience.com/clustering-moving-object-trajectories-216c372d37e2?source=collection_archive---------22-----------------------

两个端点之间有多少不同的轨迹?

亚历克斯·霍利奥克在 Unsplash 上拍摄的照片

你可能会从家里到办公室走一条你从经验中学到的已知路线,在某种意义上这是最好的。也许它最大限度地减少了你早上的通勤时间或旅行成本,或者也许它是最方便的。每天早上上班的时候,我把车开到公共停车场,然后坐地铁。回家的通勤从地铁开始,然后是汽车,但这次我总是使用不同的路线。使用早上的路线开车回家将意味着严重的交通堵塞,所以我绕道缩短我的通勤时间。我的路线选择反映了我的一些条件,了解这些可能有利于交通部门更好地规划。但是他们怎么能仅仅通过观察交通模式就知道这些呢?

轨迹的研究是理解运动物体行为的基础。我们不仅关心开始和结束位置,而且特别关心对象在端点之间移动的路径。一旦我们知道了轨迹,我们就可以收集关于移动物体行为的统计数据,并将其用于未来的推断。例如,在给定一般路径的情况下,我们确定车辆在两个已知位置之间行驶时的典型燃料消耗。我们可以稍后决定在相同位置之间的新行程是属于已知分布还是过于激进。

轨道

空间轨迹是运动物体在地理空间中产生的轨迹,通常由一系列按时间顺序排列的点表示,例如p1→p2→pn,其中每个点都由一个地理空间坐标集和一个时间戳组成,例如 p = ( xyt [1]

移动的物体产生轨迹,即定义空间曲线的时间位置序列。我们通常使用采样过程收集轨迹信息,以离散的时间间隔收集位置。当你允许你的智能手机收集你的位置信息时,这个过程就会发生。该设备正在收集形成折线的数据,而不是一条连续的线。通过给每个顶点分配一个时间戳,折线呈现一个明确的方向,从较小的时间值到较大的时间值。

聚类轨迹

为什么我们需要对轨迹进行聚类?让我们以轻型车辆在现代城市中行驶为例。理解汽车沿着特定轨迹行驶时的驾驶行为是令人感兴趣的。人们可以简单地把可比较的路线想象成在相同的大致位置开始和结束的路线。但是这种假设是误导性的,因为驾驶员可能会选择不同的路径从 A 到达 b。一天中不同的交通状况可能会影响可用的选项,并且相同的行程可能会使用不同的轨迹。如果我们想以某种方式研究两个端点之间的驾驶性能,我们还必须确保我们沿着相似的路径进行比较。我们需要确保轨迹在相同的地方开始和结束,并遵循相同的一般路径。然而,我们需要给轨迹的实际地理匹配留有余地。轨迹记录存在自然的可变性,我们不想强迫车辆精确地通过相同的位置。我们还必须确保考虑到通过 GPS 传感器测量车辆位置时不可避免的误差。

我们如何让轨迹聚类发挥作用?我们使用聚类算法和距离度量。您可以选择任何一种聚类算法,但是距离度量仅限于一小组选项。我们希望确保相似的轨迹比不相似的轨迹产生更低的距离度量值。幸运的是,我们可以为此使用几个距离度量,例如豪斯多夫距离或弗雷歇距离。在本文中,我们将使用后者和 HDBSCAN 作为聚类算法。

例子

我将说明如何使用车辆能量数据集【2】数据和代码库对车辆轨迹进行聚类,我一直在构建代码库来探索它。我邀请你克隆储存库并打开 Python 笔记本 9 号。请注意,如果这是您第一次使用此代码,您必须按顺序运行所有笔记本,以准备使用数据库。

笔记本从声明几个支持函数开始,主要是读取和显示轨迹和聚类 geofences。真正的行动开始于名为“轨迹显示”的部分我们将查看一组特定的路径,即从第九组开始并在第六组结束的路径。

下图显示了所选簇之间的所有轨迹。从九点到六点有两种不同的方式,左边的阶梯路径和右边的*滑路径。我们还可以看到一些外围轨迹。聚类过程会对它们产生什么影响?

上面的地图显示了从第九个星团到第六个星团的所有轨迹。有两个清晰的集群,左边的阶梯路径和右边的弯曲路径。向左的长轨迹似乎是一个异常值。作者创造的形象。

在我们开始聚类过程之前,我们必须首先计算所有轨迹之间的距离矩阵。HDBSCAN 算法不支持轨迹距离函数,因此我们提供了一个距离矩阵。计算非常简单,有两个嵌套循环,并确保所需的对称性。出于性能原因,我们只计算矩阵的上半部分。

上述函数使用离散弗雷歇距离计算对称距离矩阵。

现在,我们可以继续将数据提供给 HDBSCAN,并收集计算出的集群标识符。通过将这些提供给地图显示函数,我们得到了下面显示的最终结果。

上面的地图显示了先前轨迹集的聚类结果。聚类过程正确地识别了两个主要的聚类和一些用灰色表示的外围路径。作者创造的形象。

我们现在可以确定驾驶员在两个终点之间使用的两个主要轨迹,并使用其特定的数据分布来表征它们。这些信息可能有助于我们回答相关的问题,例如为什么司机选择一个轨迹而不是另一个。

结论

本文使用 HDBSCAN 算法和离散弗雷歇距离作为度量,对轨迹进行了聚类。使用这一对算法,首先要计算所有路径之间的距离矩阵。轨迹聚类是移动对象分析的基本工具,因为它可以帮助揭示数据中隐藏的行为。

笔记

1 —由 Scikit-Learn 包实现的 KMeans 聚类算法由于缺乏对距离矩阵的支持而无法使用。显然,这是有充分理由的。

资源

GitHub 库

相关文章

[## 快速离散弗雷歇距离

如何提高离散弗雷歇距离计算性能?

towardsdatascience.com](/fast-discrete-fréchet-distance-d6b422a8fb77) [## 使用 HDBSCAN 进行地理聚类

如何使用 HDBSCAN、H3、图论和 OSM 探索地理数据。

towardsdatascience.com](/geographic-clustering-with-hdbscan-ef8cb0ed6051)

参考

[1]郑,杨,周,谢.(编者).用空间轨迹计算。斯普林格,2011 年。

[2] 汽车能源数据集(VED),用于汽车能源消耗研究的大规模数据集

使用 Python 和 r 对音乐进行聚类,在 Spotify 上创建您的播放列表。

原文:https://towardsdatascience.com/clustering-music-to-create-your-personal-playlists-on-spotify-using-python-and-k-means-a39c4158589a?source=collection_archive---------26-----------------------

使用 K-Means 在 Spotify 播放列表上按相似性对歌曲进行分组的简单方法。

照片由海蒂·芬在 Unsplash 拍摄

S potify 是发现新音乐的最著名的音乐*台之一。该公司使用许多不同的算法,根据用户的音乐偏好向他们推荐新音乐,这些推荐大多位于播放列表中。这些播放列表是根据各种音乐流派为不同用户创建的,甚至 Spotify 也能够根据心情推荐新音乐。

在我的一生中,音乐一直在我的日常生活中,它是一种药物,当我做家务、在办公室工作、遛狗、锻炼等等时,我都需要它。我在 Spotify 上有很多音乐,我一直想根据歌曲的相似性将其分开,并保存到不同的播放列表中。幸运的是,凭借一点机器学习算法和 Python 的知识,我可以实现这个目标!!!。

为此,首先我会列出构建聚类模型所需的工具和 Spotify 音频功能的一些定义。

工具:

  • 熊猫和 Numpy 进行数据分析。
  • Sklearn 构建机器学习模型。
  • Spotipy Python 库(点击此处了解更多信息)。
  • Spotify 凭证访问 Api 数据库和播放列表修改(点击此处了解更多信息)。

Spotify 音频功能:

Spotify 使用一系列不同的功能对歌曲进行分类。我从 Spotify 网页复制/粘贴信息。

  • 声音:一种置信度,从 0.0 到 1.0,表示音轨是否是声音的。1.0 表示音轨是声学的高置信度。
  • 可跳舞性:可跳舞性描述了一个曲目在音乐元素组合的基础上适合跳舞的程度,包括速度、节奏稳定性、节拍强度和整体规律性。值 0.0 最不适合跳舞,1.0 最适合跳舞。
  • 能量:能量是一个从 0.0 到 1.0 的度量,代表强度和活动的感知度量。通常,高能轨道感觉起来很快,很响,很嘈杂。例如,死亡金属具有高能量,而巴赫前奏曲在音阶上得分较低。对该属性有贡献的感知特征包括动态范围、感知响度、音色、开始速率和一般熵。
  • 乐器性:预测音轨是否不包含人声。“Ooh”和“aah”在这种情况下被视为乐器。Rap 或口语词轨道明显是“有声的”。乐器度值越接* 1.0,轨道不包含人声内容的可能性就越大。高于 0.5 的值旨在表示乐器轨道,但随着该值接* 1.0,置信度会更高。
  • 活跃度:检测录音中是否有观众。较高的活跃度值表示音轨被现场执行的概率增加。高于 0.8 的值很有可能表示该音轨是实时的。
  • 响度:轨道的整体响度,以分贝(dB)为单位。响度值是整个轨道的*均值,可用于比较轨道的相对响度。响度是声音的质量,是与体力(振幅)相关的主要心理因素。值通常在-60 和 0 db 之间。
  • 语速:语速检测音轨中是否存在口语单词。越是类似语音的录音(例如脱口秀、有声读物、诗歌),属性值就越接* 1.0。高于 0.66 的值描述可能完全由口语单词组成的轨道。介于 0.33 和 0.66 之间的值描述可能包含音乐和语音的轨道,可以是分段的,也可以是分层的,包括说唱音乐。低于 0.33 的值很可能代表音乐和其他非语音类轨道。
  • 配价:从 0.0 到 1.0 的一种量度,描述音轨所传达的音乐积极性。高价曲目听起来更积极(例如,快乐、愉快、欣快),而低价曲目听起来更消极(例如,悲伤、沮丧、愤怒)。
  • 速度:轨道的整体估计速度,单位为每分钟节拍数(BPM)。在音乐术语中,速度是给定作品的速度或步调,直接来源于*均节拍持续时间。

出于减少信息的目的,我决定使用响度化合价能量、可跳舞性的特征,因为它们对区分精力充沛的歌曲和放松的歌曲更有影响。

1.获取和分析数据:

我最喜欢的乐队是电台司令,所以我决定获得他们的唱片目录以及他们在个人音乐生涯中创作的所有音乐。

我用 Spotipy 库创建了一些函数来下载电台司令汤姆·约克和*原子约翰尼·格林伍德艾德·奥布莱恩科林·格林伍德菲尔·塞尔维的所有歌曲(是的,我痴迷于他们的音乐呵呵)。你可以在我的 Github 库上访问这些功能(点击这里)。

我获得了以下数据:

具有 423 行 10 列形状的数据框。(图片由作者提供)

我一直想知道为什么我喜欢很多电台司令的音乐,我意识到他们的大多数歌曲都倾向于忧郁。描述了上述特征,数据显示我的化合价和能量小于 0.5,Danceability 倾向于低值,所以我喜欢低能量和负面声音的轨道(仍然有一些我 2000 年的 Emo 边看 MTV 视频)。

数据框的主要统计数据(图片由作者提供)

歌曲特征直方图(图片由作者提供)

2.构建模型:

由于我的数据(423 首曲目)的形状,并考虑到我想创建 2 个播放列表,将放松的曲目与充满活力的曲目分开(K=2),我决定使用 K-means 聚类进行无监督的机器学习。

重要提示 :我没有使用训练和测试数据,因为在这种情况下,我只想将所有的曲目分成 2 个不同的组,以创建包含全部数据的播放列表。

所以让我们开始吧!。我首先导入库:

from sklearn.cluster import KMeansfrom sklearn.preprocessing import MinMaxScaler

然后,我需要定义特性并规范化模型的值。我将使用最小最大缩放器来保持原始分布的形状,并在 0 到 1 的范围内缩放特征。一旦我有了正确格式的值,我只需简单地创建 K-Means 模型,然后将标签保存到名为“df”的主数据框中。

col_features = ['danceability', 'energy', 'valence', 'loudness']
X = MinMaxScaler().fit_transform(df[col_features])kmeans = KMeans(init="kmeans++",
                n_clusters=2,
                random_state=15).fit(X)df['kmeans'] = kmeans.labels_

就这样,我把音乐分成了 2 组!!!

但现在我需要研究这些标注的特征,所以我绘制了 3D 散点图中的轨迹,然后我分析了通过 K 均值结果标注对数据框进行分组的每个特征的各自均值。

使用“能量”、“可跳舞性”和“响度”特征的音轨 3D 散点图(图片由作者提供)

每个 K 均值标签的均值特征(图片由作者提供)

正如我在图上注意到的,这些值被很好地分组,蓝色值位于标签 0 中,红色值位于标签 1 中。查看手段表,标签 0 将具有较少可跳舞性、能量、效价、响度的轨道分组,因此这个对应于放松歌曲,同样地,标签 1 具有能量歌曲

3.带 R 的模型的精度:

我知道,试图评估聚类算法的最佳结果时,聚类准确性有点主观,但同样,我想观察我的模型是否能很好地分离轨迹。所以在 Rstudio 的一点帮助下,我使用了轮廓分析。来衡量我的模型的准确性。

在 Rstudio 中,我使用库“cluster”和“factoextra”来可视化和计算使用欧几里德距离的轮廓分析。完整的代码在我的 Github 存储库中(点击这里):

#Calculate The euclidean distance of my dataframe values.
dd <- dist(df,method="euclidean")#Silhouette Analysis using the K-means model(km) and distance(dd)
sil.km <- silhouette(km$cluster,dd)
fviz_silhouette(sil.km)

结果是:

轮廓分析(图片由作者提供)

轮廓分析是一种测量聚类中的每个点与其相邻聚类中的点的接*程度的方法。轮廓值在[-1,1]范围内。值+1 表示样本远离其相邻聚类,而非常接*其所分配的聚类。类似地,值为-1 表示该点与其相邻的簇的距离比与其所分配的簇的距离更*。因此,在我的例子中,值在 0.25 和 0.60 之间,这意味着大多数值被很好地分组。

4.在 Spotify 上创建播放列表:

为了创建播放列表并添加集群曲目,我使用了本文第一部分中介绍的库 Spotipy。你只需要获得一个客户端 id、客户端密码和用户名代码,就可以使用 Spotify 的 API 并操作你的音乐库。我把信息的链接给你(点击这里)。

我必须将曲目分成 2 个不同的变量,然后有了曲目的 id,我只需创建 2 个新的播放列表,并向它们传递曲目的 id。代码如下所示:

#Separating the clusters into new variables
cluster_0 = df[df['kmeans']==0]
cluster_1  = df[df['kmeans']==1]#Obtaining the ids of the songs and conver the id dataframe column to a list.
ids_0 = cluster_0['id'].tolist()
ids_1 = cluster_1['id'].tolist()#Creating 2 new playlists on my Spotify User
pl_energy = sp.user_playlist_create(username=username,
                                           name="Radiohead :)")pl_relaxed = sp.user_playlist_create(user=username,
                                            name="Radiohead :(")#Adding the tracks into the playlists
#For energetic Playlist
sp.user_playlist_add_tracks(user=username,
                            playlist_id = pl_energy['id'],
                            tracks=ids_1)#For relaxed Playlist
sp.user_playlist_add_tracks(user=username,
                            playlist_id = pl_relaxed['id'],
                            tracks=ids_0)

最后,我有两个电台司令歌曲的播放列表,使用 K-means 聚类算法将充满活力的歌曲从放松的歌曲中分离出来!!!!。

如果你想听这两个播放列表,你可以在下面访问它们。

最有活力的电台司令歌曲(209 首)

作者创建的播放列表

最轻松的电台司令歌曲(214 首)

作者创建的播放列表

结论

机器学习算法在实现与你喜欢的事情相关的想法或项目方面有很多乐趣。就我而言,我非常喜欢音乐,所以我可以用这些知识创造一些很酷的方法来帮助我自动完成一项可能需要很长时间才能完成的任务。我还可以更多地了解这个神奇的数据科学世界,以及我对音乐品味的倾向。

我的文章:

  • 一个友好的公共汽车和分析员在网上到达部门
  • 用深度学习预测一首歌的音乐情绪
  • 一种在网络上进行公共汽车和分析工作的交互形式。
  • 从你电脑的音乐文件夹中自动播放 Spotify 上的播放列表

参考资料:

  • https://towards data science . com/a-music-taste-analysis-using-Spotify-API-and-python-e52d 186 db 5 fc
  • https://towards data science . com/is-my-Spotify-music-boring-an-analysis-containing-music-data-and-machine-learning-47550 AE 931 de
  • https://medium . com/@ rare loot/extracting-Spotify-data-on-your-favorite-artist-via-python-d 58 BC 92 a 4330
  • https://spotipy.readthedocs.io/en/2.9.0/#

聚类技术

原文:https://towardsdatascience.com/clustering-techniques-hierarchical-and-non-hierarchical-b520b5d6a022?source=collection_archive---------13-----------------------

聚类属于 u 监督学习技术。在这种技术中,数据没有标记,也没有定义因变量。这种类型的学习通常是为了识别数据中的模式和/或对相似的数据进行分组。

在这篇文章中,详细解释了集群技术的类型,并提供了一个代码遍历。

什么是集群?

聚类是一种对相似对象进行分组的方法。聚类的目的是从异类的观察中创建同类的组。假设数据来自多个群体,例如,可能有不同行业的人出于不同目的向银行申请贷款。如果这个人是学生,他/她可以申请教育贷款,想买房的人可以申请住房贷款等等。聚类有助于识别相似的群体,更好地满足需求。

为什么要集群?

聚类是一种基于距离的算法。聚类的目的是最小化类内距离和最大化类间距离。

未分类的数据(图片由作者提供)

聚类数据(按作者分类的图片)

聚类作为一种工具可以用来深入了解数据。通过可视化数据可以获得大量的信息。聚类的输出也可以用作其他算法的预处理步骤。这项技术有几个广泛使用的用例,其中一些重要的是市场细分、客户细分和图像处理。

在继续之前,让我们了解一下集群的核心。

距离的度量

聚类完全是关于两点之间的距离和两个聚类之间的距离。距离不能为负。对于聚类问题,该算法使用一些常见的距离度量。

欧氏距离

这是算法使用的默认距离。最好解释为两点之间的距离。如果要测量两点 p 和 q 之间的距离,则欧几里德距离为

欧几里德距离(图片由作者提供)

曼哈顿距离

它是沿轴以垂直角度计算的两点之间的距离。它也被称为出租车距离,因为这代表了曼哈顿市的车辆如何在街道以直角相交的地方行驶。

曼哈顿距离(图片作者提供)

闵可夫斯基距离

在 n 维空间中,两点之间的距离称为闵可夫斯基距离。

闵可夫斯基距离(图片作者提供)

欧几里德距离和曼哈顿距离的推广是,如果 p 的值是 2,则它成为欧几里德距离,如果 p 的值是 1,则它成为曼哈顿距离。

聚类的类型

有两种主要类型的聚类技术

  1. 等级或聚集
  2. k 均值

让我们看看每种类型以及代码走查

分层聚类

这是一种自下而上的方法。基于记录之间的距离以及聚类之间的距离,数据集中的记录被顺序分组以形成聚类。这是一个逐步实现这个方法的方法

  1. 从 n 个群集开始,其中每行被视为一个群集
  2. 使用基于距离的方法,将彼此最接*的两个记录合并到一个聚类中。在图 3 中,对于给定的五个记录,假设 A 和 C 在距离上最接*,它们形成一个聚类,同样 B 和 E 形成另一个聚类,等等

两个最接*记录的聚类(按作者分类的图片)

3.在每一步,两个最接*的聚类将被合并。要么将单个记录(singleton)添加到现有分类中,要么合并两个分类。在至少一个多元素集群形成后,需要为单个元素和一组观察值计算距离的场景,这就是链接的概念出现的地方。有五种主要类型的链接。通过使用下面的一个概念,聚类发生了-

  • 单联动: 它是两个集群中任意两点之间的最短距离
  • 完全联动: 与单联动相反。这是两个集群中任意两点之间的最长距离
  • *均连锁度: 一个聚类中的每一点到另一个聚类中的每一点的*均距离
  • 质心联动: 一个簇的中心点到另一个簇的中心点的距离
  • 沃德连锁: *均法和质心法的结合。通过确定分类的中心点和观察值与中心的距离来计算分类内方差。当试图合并两个聚类时,在聚类之间找到方差,并且与另一个组合相比方差较小的聚类被合并。

需要注意的一点是,每种链接方法都会产生一个独特的结果。当这些方法中的每一种应用于相同的数据集时,它可能被不同地聚类。

4.重复这些步骤,直到出现一个包含所有记录的聚类

形象化

为了形象化聚类,有一个叫做树状图的概念。树状图是总结聚类过程的树形图。记录在 x 轴上。相似的记录由直线连接,直线的垂直长度反映了记录之间的距离。身高差距越大,差异越大。展示了一个样本树状图

树状图(图片由作者提供)

分层聚类代码演练

分层聚类的代码是使用 jupyter notebook 用 Python 3x 编写的。让我们从导入必要的库开始。

#Import the necessary librariesimport numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster

接下来,加载数据集。这里使用了星巴克食物菜单上的一个数据集。

#Read the datasetdf = pd.read_csv('starbucks_menu.csv')#Look at the top 5 rows df.head()

进行必要的探索性数据分析,如查看描述性统计数据,检查空值和重复值。进行单变量和双变量分析,进行异常值处理(如果有的话)。由于这是一种基于距离的算法,因此有必要在适用的情况下执行标准化,以便所有变量都没有任何测量单位。这使得模型能够以最佳状态运行。

from scipy.stats import zscore
df.iloc[:,1:6] = df.iloc[:,1:6].apply(zscore)#Check the head after scalingdf.head()

一旦数据准备好了,让我们开始构建模型。需要分配一个标签列表,它是分类变量的唯一值的列表。这里,标签列表是从食物变量中创建的。

#Before clustering, setup label list from the food variablelabelList = list(df.Food.unique())
labelList

下一步是形成一个链接,将单例集群和另一个集群连接起来。在这种情况下,沃德的法是首选。

#Create linkage method using Ward's methodlink_method = linkage(df.iloc[:,1:6], method = 'ward')

借助树状图可视化聚类。在这种情况下,通过指定显示倒数第二个和倒数第二个聚类的 p 值,得到一个截断的树状图。

#Generate the dendrogramdend = dendrogram(link_method,
                  labels = labelList,
                  truncate_mode='lastp', 
                  p=10)

截断的树状图(图片由作者提供)

一旦创建了树状图,就需要切割树来确定最佳的聚类数。有两种方法可以做到这一点(如图所示)。在这种情况下,选择 3 个集群。可将聚类作为新列附加到数据框中,以获得更多信息。

#Method 1: criterion = 'maxclust' where a cut is defined based on the number of clustersclusters = fcluster(link_method, 3, criterion='maxclust') 
clusters#Method 2: criterion='distance' where a cut is defined based on distance in the y-axis#clusters = fcluster(link_method, 800, criterion='distance')#Apply the clusters back to the datasetdf['HCluster'] = clusters
df.head()

最后一步是进行聚类分析,从算法中提取信息和见解,以帮助做出有效的决策。聚类分析是通过对聚类的*均值进行分组并基于频率进行排序来完成的。

aggdata=df.iloc[:,1:8].groupby('HCluster').mean()
aggdata['Frequency']=df.HCluster.value_counts().sort_index()
aggdata

聚类分析(作者图片)

快速了解一下,第一类食物通常热量较低,因此宏量营养素含量较低。第二类食物的卡路里含量最高,因此宏量营养素含量也较高,第一类和第二类食物的卡路里含量居中,第三类食物的卡路里含量和宏量营养素含量较高。总的来说,简而言之,这种模式聚集得很好。

让我们继续下一个方法

k 均值聚类

K-Means 是一种无层次的方法。这个想法是事先指定集群的数量。根据分类的数量,每个记录根据与每个分类的距离被分配到分类中。当数据集很大时,这种方法是首选的。单词表示 k-means 中的是指数据的*均,也称为寻找质心。这是一个循序渐进的方法

  1. 事先指定 k 值
  2. 将每个记录分配给到质心的距离最小的聚类。默认情况下,K 均值使用欧几里德距离
  3. 重新计算新形成的簇的质心。基于距离,一些数据点可能会移动。
  4. 重新分配可以在迭代的基础上发生,并且形成新的质心。这个过程将停止,直到没有从一个集群到另一个集群的观察跳跃。
  5. 如果有任何重新分配,请返回步骤 3 并继续这些步骤。如果没有,则集群被最终确定。

尽管我们已经事先确定了聚类的数量,但这并不总是正确的,因此有必要确定最优的聚类数量。没有确定集群数量的可靠解决方案,但是有一个通用的方法。对于 k 的每个值,可以识别*方和(WSS)值。不能选择单个聚类,因此重要是找到 k 值,在该 k 值之后 WSS 值没有显著差异。为了提高效率,可以绘制一个肘形图,y 轴为 WSS 分数,x 轴为聚类数,以直观显示最优聚类数。

有一种方法可以了解模型的表现。这是通过检查两个度量来完成的,即轮廓宽度轮廓分数。这有助于我们根据距离标准分析映射到聚类的每个观察值是否正确。轮廓宽度计算如下

轮廓宽度公式(图片由作者提供)

其中 b 是观测值和相邻聚类的质心之间的距离,a 是观测值和自身聚类的质心之间的距离。

轮廓宽度的值可以在-1 到 1 的范围内。如果轮廓宽度的值为正,则观测值到当前聚类的映射是正确的。当 a > b 时,轮廓宽度将返回负值。所有轮廓宽度的*均值称为轮廓得分。如果最终得分是正值,并且接*+1,则*均来说,聚类被很好地分开。如果接* 0,说明分离得不够好。如果是负值,则该模型在聚类中犯了一个错误。

k-均值聚类代码遍历

让我们从导入必要的库开始

#Import the necessary librariesimport numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

接下来,加载数据集。这里使用了用于分层聚类的相同数据集。

进行必要的探索性数据分析,如查看描述性统计数据,检查空值和重复值。进行单变量和双变量分析,进行异常值处理(如果有的话)。K-means 聚类需要扩展。这样做是为了使所有的变量都没有任何测量单位。这使得模型能够以最佳状态运行。在这种情况下,使用 StandardScaler 方法。

#Importing the standard scaler module and applying it on continuous variablesfrom sklearn.preprocessing import StandardScaler 
X = StandardScaler()
scaled_df = X.fit_transform(df.iloc[:,1:6])
scaled_df

下一步是调用 KMeans 方法,预先定义集群的数量。然后将缩放后的数据集拟合到模型中。

# Create K Means cluster and store the result in the object k_meansk_means = KMeans(n_clusters=2)# Fit K means on the scaled_dfk_means.fit(scaled_df)# Get the labelsk_means.labels_

现在是时候通过分析给定 k 范围内的*方和(WSS)值来找到最佳聚类数了

#To determine the optimum number of clusters, check the wss score for a given range of kwss =[] 
for i in range(1,11):
    KM = KMeans(n_clusters=i)
    KM.fit(scaled_df)
    wss.append(KM.inertia_)

wss

WSS 得分(图片由作者提供)

可以看出,在 k=2 之后,WSS 分数有所下降,因此让我们关注 k=3。同样的情况也可以用肘图来显示

#Draw the elbow plotplt.plot(range(1,11), wss, marker = '*')

肘图(图片由作者提供)

决定聚类数量的另一个帮助可以是轮廓分数的值。如前所述,分数越高,聚类越好。让我们检查分数。

#Checking for n-clusters=3k_means_three = KMeans(n_clusters = 3)
k_means_three.fit(scaled_df)
print('WSS for K=3:', k_means_three.inertia_)
labels_three = k_means_three.labels_
print(labels_three)#Calculating silhouette_score for k=3print(silhouette_score(scaled_df, labels_three))

k=3 的 WSS 是 261.67,并且这些标签的轮廓分数是 0.3054。因为分数是正的,所以这是发生了良好聚类的标志。

最后一步是进行聚类分析,以了解聚类是如何发生的,并获得更多的见解。

clust_profile=df.iloc[:,1:8].groupby('KMCluster').mean()
clust_profile['KMFrequency']=df.KMCluster.value_counts().sort_index()
clust_profile

KMeans 的聚类分析(图片由作者提供)

就像分层聚类一样,这三个聚类表示三个级别的食物,它们具有不同的热量和宏观营养范围。

  • 数据集来源:https://www.kaggle.com/starbucks/starbucks-menu?select =星巴克-菜单-营养-食物. csv
  • 要查看 jupyter 笔记本文件,点击这里的这里的

使用凸包的聚类

原文:https://towardsdatascience.com/clustering-using-convex-hulls-fddafeaa963c?source=collection_archive---------17-----------------------

如何在数据聚类中使用凸包

最*在模式识别快报偶然看到哈坎·切维卡普的文章《利用局部仿射/凸包 进行高维数据聚类》。提出了一种利用局部仿射/凸包对高维数据进行聚类的新算法。他们使用凸包进行聚类的方法启发了我。我想尝试使用凸包实现我自己的简单聚类方法。因此,在本文中,我将向您介绍我使用凸包实现聚类方法的过程。在我们进入编码之前,让我们看看什么是凸包。

凸包

根据维基百科,凸包定义如下。

在几何学中,一个形状的凸包或凸包络或凸闭包是包含它的最小凸集。

图一。一组钉子的凸包(图片由作者提供)

让我们考虑一个简单类比的例子。如图 1 所示,假设有几个钉子被钉在木板的中间。你拿一根橡皮筋,把它拉长包住钉子,然后放开它。它将适合最外面的指甲(显示为蓝色),并采取最小化其长度的形状。被橡皮筋围起来的区域叫做这组钉子的凸包

这个二维空间中的凸包(如图 1 所示)将是一个凸多边形,其所有内角都小于 180°。如果是在 3 维或者更高维的空间,那么凸包会是一个多面体

有几种算法可以确定给定点集的凸包。一些著名的算法是礼品包装算法和格雷厄姆扫描算法。

由于凸包包围了一组点,所以它可以充当聚类边界,允许我们确定聚类中的点。因此,我们可以利用凸包和执行聚类。让我们进入代码。

简单的例子

在这个例子中,我将使用 Python。在开始之前,我们需要以下 Python 库。

sklearn
numpy
matplotlib
mpl_toolkits
itertools
scipy
quadprog

资料组

为了创建我们的样本数据集,我将使用 sci-kit learn 库的 make blobs 函数。我将制作 3 个集群。

import numpy as np
from sklearn.datasets import make_blobscenters = [[0, 1, 0], [1.5, 1.5, 1], [1, 1, 1]]
stds = [0.13, 0.12, 0.12]X, labels_true = make_blobs(n_samples=1000, centers=centers, cluster_std=stds, random_state=0)
point_indices = np.arange(1000)

由于这是一个三维点数据集,我将绘制一个 3D 图来显示我们的地面真实集群。图 2 显示了带有彩色聚类的数据集的散点图。

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3Dx = X[:,0]
y = X[:,1] 
z = X[:,2]

# Creating figure 
fig = plt.figure(figsize = (15, 10)) 
ax = plt.axes(projection ="3d") 

# Add gridlines  
ax.grid(b = True, color ='grey',  
        linestyle ='-.', linewidth = 0.3,  
        alpha = 0.2)  

mycolours = ["red", "green", "blue"]# Creating color map 
col = [mycolours[i] for i in labels_true]# Creating plot 
sctt = ax.scatter3D(x, y, z, c = col, marker ='o')plt.title("3D scatter plot of the data\n") 
ax.set_xlabel('X-axis', fontweight ='bold')  
ax.set_ylabel('Y-axis', fontweight ='bold')  
ax.set_zlabel('Z-axis', fontweight ='bold')

# show plot 
plt.draw()

图二。数据集的初始散点图(图片由作者提供)

获得初始聚类

首先,我们需要将数据集分成两部分。一部分将被用作种子,以使用 K-means 获得初始聚类。另一部分中的点将被分配给基于初始聚类的聚类。

from sklearn.model_selection import train_test_splitX_seeds, X_rest, y_seeds, y_rest, id_seeds, id_rest = train_test_split(X, labels_true, point_indices, test_size=0.33, random_state=42)

现在,我们对种子点执行 K 均值聚类。

from sklearn.cluster import KMeanskmeans = KMeans(n_clusters=3, random_state=9).fit(X_seeds)
initial_result = kmeans.labels_

由于生成的标签可能与地面真实标签不同,我们必须映射这两组标签。为此,我们可以使用下面的函数。

from itertools import permutations# Source: [https://stackoverflow.com/questions/11683785/how-can-i-match-up-cluster-labels-to-my-ground-truth-labels-in-matlab](https://stackoverflow.com/questions/11683785/how-can-i-match-up-cluster-labels-to-my-ground-truth-labels-in-matlab)def remap_labels(pred_labels, true_labels): pred_labels, true_labels = np.array(pred_labels), np.array(true_labels)
    assert pred_labels.ndim == 1 == true_labels.ndim
    assert len(pred_labels) == len(true_labels)
    cluster_names = np.unique(pred_labels)
    accuracy = 0 perms = np.array(list(permutations(np.unique(true_labels)))) remapped_labels = true_labels for perm in perms: flipped_labels = np.zeros(len(true_labels))
        for label_index, label in enumerate(cluster_names):
            flipped_labels[pred_labels == label] = perm[label_index] testAcc = np.sum(flipped_labels == true_labels) / len(true_labels) if testAcc > accuracy:
            accuracy = testAcc
            remapped_labels = flipped_labels return accuracy, remapped_labels

我们可以从上面的函数中得到精度和映射的初始标签。

intial_accuracy, remapped_initial_result = remap_labels(initial_result, y_seeds)

图 3 表示种子点的初始聚类。

图三。使用 K-means 对种子点进行初始聚类(图片由作者提供)

得到初始聚类的凸包

一旦我们获得了初始聚类,我们就可以得到每个聚类的凸包。首先,我们必须获得聚类中每个数据点的索引。

# Get the idices of the data points belonging to each cluster
indices = {}for i in range(len(id_seeds)):
    if int(remapped_initial_result[i]) not in indices:
        indices[int(remapped_initial_result[i])] = [i]
    else:
        indices[int(remapped_initial_result[i])].append(i)

现在我们可以从每个集群中获得凸包。

from scipy.spatial import ConvexHull# Get convex hulls for each cluster
hulls = {}for i in indices:
    hull = ConvexHull(X_seeds[indices[i]])
    hulls[i] = hull

图 4 表示代表 3 个聚类中的每一个的凸包。

图 4。每个聚类的凸包(图片由作者提供)

将剩余的点分配给最*的凸包簇

现在我们有了初始聚类的凸包,我们可以将剩余的点分配给最*的凸包的聚类。首先,我们必须将数据点投影到一个凸包上。为此,我们可以使用下面的函数。

from quadprog import solve_qp# Source: [https://stackoverflow.com/questions/42248202/find-the-projection-of-a-point-on-the-convex-hull-with-scipy](https://stackoverflow.com/questions/42248202/find-the-projection-of-a-point-on-the-convex-hull-with-scipy)def proj2hull(z, equations): G = np.eye(len(z), dtype=float)
    a = np.array(z, dtype=float)
    C = np.array(-equations[:, :-1], dtype=float)
    b = np.array(equations[:, -1], dtype=float) x, f, xu, itr, lag, act = solve_qp(G, a, C.T, b, meq=0, factorized=True) return x

寻找凸壳上的点的投影的问题可以使用二次规划来解决。上述功能利用了quadprog模块。您可以使用[conda](https://anaconda.org/omnia/quadprog)[pip](https://pypi.org/project/quadprog/)安装quadprog模块。

conda install -c omnia quadprog
OR
pip install quadprog

关于如何用二次规划解决这个问题,我就不赘述了。如果你有兴趣,你可以阅读更多来自这里和这里。

图五。从一个点到它在凸包上的投影的距离(图片由作者提供)

一旦获得了凸包上的投影,就可以计算从该点到凸包的距离,如图 5 所示。基于这个距离,现在让我们将剩余的数据点分配给最*的凸包的簇。

我将考虑从数据点到它在凸包上的投影的欧几里德距离。那么该数据点将被分配到具有距离该数据点最短距离的凸包的聚类中。如果一个点位于凸包内,那么距离将为 0。

prediction = []for z1 in X_rest: min_cluster_distance = 100000
    min_distance_point = ""
    min_cluster_distance_hull = ""

    for i in indices: p = proj2hull(z1, hulls[i].equations) dist = np.linalg.norm(z1-p) if dist < min_cluster_distance: min_cluster_distance = dist
            min_distance_point = p
            min_cluster_distance_hull = i prediction.append(min_cluster_distance_hull)prediction = np.array(prediction)

图 6 显示了最终的聚类结果。

图六。凸包的最终结果(图片由作者提供)

评估最终结果

让我们评估一下我们的结果,看看它有多准确。

from sklearn.metrics import accuracy_scoreY_pred = np.concatenate((remapped_initial_result, prediction))
Y_real = np.concatenate((y_seeds, y_rest))
print(accuracy_score(Y_real, Y_pred))

我得到了 1.0 (100%)的准确率!棒极了,激动人心,对吧?😊

如果想了解更多关于评估聚类结果的内容,可以查看我之前的文章评估聚类结果。

[## 评估聚类结果

用于评估聚类结果的标准

towardsdatascience.com](/evaluating-clustering-results-f13552ee7603)

我使用了一个非常简单的数据集。您可以在更复杂的数据集上尝试这种方法,看看会发生什么。

高维数据

我还尝试使用我的聚类外壳方法对一个具有 8 维数据点的数据集进行聚类。你可以找到显示代码和结果的 jupyter 笔记本。最终结果如下。

Accuracy of K-means method: 0.866
Accuracy of Convex Hull method: 0.867

我的凸包方法相对于 K-means 有一点改进。

最后的想法

HakanCevikalp 的题为 通过使用局部仿射/凸包 进行高维数据聚类的文章表明,他们提出的基于凸包的方法避免了“孔洞伪影”问题(高维空间中的稀疏和不规则分布会使最*邻距离不可靠),并且与其他最先进的子空间聚类方法相比,提高了高维数据集的准确性。

[## 利用局部仿射/凸包的高维数据聚类

我们提出了一种新的算法,使用局部仿射/凸包的高维数据聚类。*建议的…

www.sciencedirect.com](https://www.sciencedirect.com/science/article/pii/S0167865519302806?via%3Dihub)

您可以找到包含本文所用代码的 jupyter 笔记本。

希望这篇文章是有趣和有用的。

干杯!😃

基于 k-Means 的聚类算法及其实现

原文:https://towardsdatascience.com/clustering-using-k-means-with-implementation-40988620a973?source=collection_archive---------42-----------------------

我们周围的物体来自自然群体

图片来源:https://unsplash.com/@nauleyco

C 聚类是一种在数据中寻找自然群体的技术。

图 1:成群的动物(来源:作者)

如果我们给一个孩子看上面的图片,他可以识别出有四种动物。他可能不知道他们所有人的名字,但他仍然可以识别出有四种不同的类型,他可以独立完成这项工作,不需要成年人的帮助。由于我们不需要一个成年人来监督,聚类是一种无人监督的技术

集群的主要动机是什么?

这三个动机可以列举如下。

底层结构:我们可以理解有不同的组,组内的差异较小,组间的差异较小。

自然分类:我们知道动物有四种不同的类型或类群。

总结:如果我们现在为它们的住所或食物想出一些策略,我不需要为所有的动物考虑,因为一只羊的食物需求与熊猫、长颈鹿或 Rhyno 不同,但与另一只羊没有太大的不同。当任何公司试图提出一个客户群特定的策略时,这可能是非常重要的。

k-means 可以说是最流行的算法,它将对象分成 k 个组。这有许多应用,因为我们想在数据中找到结构。我们希望将学生、客户、患者、产品、电影、推文、图像等等分组。

现在让我们看看它是如何工作的。

k-means 如何工作

输入:将给出数据和“k”的值

数据:输入到 K- Means(图片:作者)

步骤 1:从数据中初始化随机的“k”个点作为聚类中心,让我们假设 k 的值是 2,选择第 1 和第 4 个观察值作为中心。

随机选择的 K (2)点(来源:作者)

步骤 2:对于所有的点,找出距离 k 个聚类中心的距离。可以使用欧几里德距离。下表用作说明。C1 和 C2 列分别表示离中心的距离。

距质心的距离计算(来源:作者)

如果对于第二次观测,我们想要确定距质心 1 的距离,计算将如下所示

欧几里德距离的例子(来源:作者)

  • 请注意,第一个点距中心 1 的距离为 0,第四个点距第二个聚类的距离为 0。原因很简单,这些点被选为质心
  • 同样对于第三次观察,两个中心的距离是相同的。

步骤 3:将每个点分配给它最*的聚类中心,然后使用算术方法再次计算聚类中心

分配给最*中心的点(来源:作者)

对于前两个点,它们被分配给聚类 1,第三个点被随机分配,最后四个点被分配给聚类 2。

集群中心(来源:作者)

重复步骤 2 和步骤 3,直到收敛

  • 每次迭代之间聚类中心没有变化
  • 集群分配的变化非常小

本质上,我们试图找到 k 个最佳代表点。这些是通过取*均值来选择的。因此,我们试图迭代地找到 k 个最佳代表点。因此得名 k-means。

k-means 中的一些问题是什么,如何绕过它们?

k-means 的一些问题是

  • 由于第一步是随机初始化,这关系到聚类的质量(其中一些可以通过 k-means ++来解决,k-means ++递增地选择随机点,确保下一个点仅在远离所选点时被选择)
  • 拾取圆形的圆(可以在一定程度上使用距离度量)
  • 受到异常值的影响(可以删除异常值,然后应用聚类)
  • k 的值需要由用户给定(讨论了找到相同值的方法)

如何求 k 的最优值

WCSS 在*方和聚类内(来源:作者)

好的聚类是那些点靠*中心的聚类。怎样才能找到亲*?我们可以找到该簇中所有点离簇中心的欧几里德距离。现在我们需要将此扩展到所有集群。这种想法用上面的等式表示,称为*方的组内和。理想情况下,我们希望它很小。极端的情况是,我们可以对每个点有一个聚类,结果 WCSS 为 0。事实证明,随着 k 的增加,WCSS 继续减小。从技术上讲,这被称为单调递减。

我们想停下来,当我们在 y 轴上画 WCSS,在 x 轴上画星团数量的时候,有一个最陡的下降或形成一种肘形。

如何应用 k-means

标准化:所有基于距离的算法都需要对数据进行标准化,这样就不会因为值的范围更大而使某个属性变得过于重要。下面给出了代码片段。

# Importing the Standard Scaler
from sklearn.preprocessing import StandardScaler 
#Intializing the Stnadard Scaler
scaler = StandardScaler().fit(df) 
#Standardizing the data
df_scaled = scaler.transform(df)

集群:下面给出了代码片段

#Imporing the Library
from sklearn.cluster import KMeans
# Intialization
kmeans = KMeans(n_clusters = 3, init = 'random', max_iter = 300, n_init = 10, random_state = 0) 
#Applying Clustering
y_kmeans = kmeans.fit_predict(df_scaled)

一些重要参数:

  • n_clusters:簇的数量或 k
  • init: Random 或 kmeans++(我们已经讨论过 kmeans++如何提供更好的初始化)
  • n_init:算法是用这些不同的初始 k 点随机选择来运行的。最好的还回来了。
  • 最大迭代次数:算法将运行多少次迭代
  • tol:代表公差,也用于终止标准。这是一个有趣的概念。如果从一次迭代到另一次迭代,中心的变化很小,那么我们就趋向于收敛。中心是矩阵,我们可以通过检查元素间的欧几里德距离来找出两个矩阵之间的接*度。这被称为弗罗贝纽斯范数。

确定“k”的值

下面给出了代码片段。

# Applying k-means for diffrent value of k and storing the WCSS
from sklearn.cluster import KMeans
wcss = []
for i in range(1, 11):
    kmeans = KMeans(n_clusters = i, init = 'random', max_iter = 300, n_init = 10, random_state = 0)
    kmeans.fit(x_scaled)
    wcss.append(kmeans.inertia_)

用于绘制聚类数

plt.plot(range(1, 11), wcss)
plt.title('The elbow method')
plt.xlabel('Number of clusters')
plt.ylabel('SSE')      #within cluster sum of squares
plt.show()

图 2:通过肘图得出的聚类数(来源:作者)

  • 你可以在这里查看我们的完整实现。
  • 你也可以在这里看一个关于集群的视频教程

结论:

k-means 因其简单、易于实现而一直存在。在任何调查中,它都很容易出现在 ML 的前 10 个算法中。有缺点,也有办法使 k-means 稳健。

参考:

[1]https://towardsdatascience . com/k-means-clustering-13430 ff 3461d

K-均值聚类

原文:https://towardsdatascience.com/clustering-with-k-means-1e07a8bfb7ca?source=collection_archive---------3-----------------------

使用无监督的机器学习在天气数据中寻找模式

图片来源:unsplash-logoNicole Wilcox

无监督学习最常用的技术之一是聚类。顾名思义,聚类是对具有相似特征的数据进行分组的行为。在机器学习中,当没有预先指定的数据标签可用时,即我们不知道要创建哪种分组时,会使用聚类。目标是将数据分组到相似的类中,以便:

类内相似度高

类间相似度低

有两种主要的聚类类型— K 均值聚类层次凝聚聚类。在 K 均值聚类的情况下,我们试图找到 k 聚类中心作为属于这些聚类的数据点的均值。这里,预先指定了聚类的数量,并且该模型旨在为任何给定的聚类,K找到最优的聚类数量。

我们使用的是来自 Kaggle 的分钟天气数据集,其中包含与天气相关的测量值,如气压、最大风速、相对湿度等。这些数据是从 2011 年 9 月到 2014 年 9 月的三年时间里在圣地亚哥采集的,包含以一分钟为间隔采集的原始传感器测量值。

第一步是导入必要的库…

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
import sklearn
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn import metrics
from sklearn.cluster import AgglomerativeClustering

…和数据集

df = pd.read_csv('minute_weather.csv')
df.head()

浏览我们的数据,我们发现有 1,587,257 行和 13 列!由于这个数据集非常大,我们需要随机抽取样本。此外,对于 K-means 方法,首先找到初始质心的位置是必要的,以便该算法可以找到收敛性。为了做到这一点,我们不使用整个数据集,而是起草一个样本,对随机初始化的质心进行短期运行,并跟踪度量的改进。这里给出了这种方法的一个很好的解释。

从每第 10 行抽取一个样本,我们创建一个新的样本数据帧

sample_df = df[(df['rowID'] % 10) == 0]
sample_df.shape

这产生了 158726 行和 13 列(好得多!)

检查空值,我们发现 rain_accumulation 和 rain_duration 可以被删除

df1 = sample_df.drop(columns =['rain_accumulation','rain_duration'])
print(df1.columns)

在最大风速/风向和最小风速/风向这两个值之间,出于聚类的目的,我只选择了最大值,因为我们已经有了这两个值的*均值。如果愿意,最小值也可以包括在分析中。最后,我们对聚类感兴趣的列可以排序到一个新的数据框架中,如下所示

cols_of_interest = ['air_pressure', 'air_temp', 'avg_wind_direction', 'avg_wind_speed','max_wind_direction',
                    'max_wind_speed', 'relative_humidity']

下一步是衡量我们的价值观,给予它们同等的重要性。从聚类的角度来看,缩放也很重要,因为点之间的距离会影响聚类的形成方式。

使用 StandardScaler,我们将数据帧转换成以下 numpy 数组

X = StandardScaler().fit_transform(data)
X

K-均值聚类

如前所述,在 K-means 的情况下,在运行模型之前已经指定了聚类的数量。我们可以为 K 选择一个基准水*数,并迭代找到最佳值。为了评估哪个数量的聚类更适合我们的数据集,或者找到聚类适合度,我们使用两种评分方法— 剪影系数Calinski Harabasz 评分。实际上,根据模型中最重要的指标,有许多不同的评分方法。通常选择一种方法作为标准,但是为了这个分析的目的,我使用了两种方法。

使用每个样本的*均聚类内距离(a)*均最*聚类距离(b) 计算轮廓系数。样本的轮廓系数为 (b-a) / max(b-a)

Calinski Harabasz 得分或方差比率是类内离差类间离差之间的比率

让我们使用 sci-kit learn 实现 K-means 算法。

n 个簇= 12

*#Set number of clusters at initialisation time*k_means = KMeans(n_clusters=12)*#Run the clustering algorithm*model = k_means.fit(X)
model#Generate cluster predictions and store in y_haty_hat = k_means.predict(X)

计算轮廓系数…

from sklearn import metrics
labels = k_means.labels_metrics.silhouette_score(X, labels, metric = 'euclidean')

0.2405

…以及 CH 分数

metrics.calinski_harabasz_score(X, labels)

39078.93

让我们尝试另一个随机选择的值,即 n_clusters = 8

k_means_8 = KMeans(n_clusters=8)
model = k_means_8.fit(X)
y_hat_8 = k_means_8.predict(X)

再次计算轮廓系数和 CV 值

labels_8 = k_means_8.labels_
metrics.silhouette_score(X, labels_8, metric = 'euclidean')

轮廓系数= 0.244

metrics.calinski_harabasz_score(X, labels_8)

CV 值= 41105.01

我们可以看到,对于这两种类型的分数,8 个聚类给出了更好的值。然而,我们必须对不同数量的集群进行多次迭代,才能找到最优的集群。相反,我们可以使用一个叫做肘图的东西来找到这个最佳值。

肘形图显示了 k 值为多少时,一个聚类的*均值与该聚类中的其他数据点之间的距离最小。

这里有两个值很重要— 失真惯性。失真是离各个聚类的质心的欧几里德*方距离的*均值。惯性是样本到它们最*的聚类中心的*方距离之和。

*#for each value of k, we can initialise k_means and use inertia to identify the sum of squared distances of samples to the nearest cluster centre*sum_of_squared_distances = []
K = range(1,15)
for k in K:
    k_means = KMeans(n_clusters=k)
    model = k_means.fit(X)
    sum_of_squared_distances.append(k_means.inertia_)

请记住,我们关心 K-means 中的类内相似性,这是肘图有助于捕捉的。

plt.plot(K, sum_of_squared_distances, 'bx-')
plt.xlabel('k')
plt.ylabel('sum_of_squared_distances')
plt.title('elbow method for optimal k')
plt.show()

这里我们可以看到,距离*方和的下降在 k=5 之后开始放缓。因此,5 是我们分析的最佳聚类数。

我们可以通过计算 k=5 时的轮廓系数和 CH 值来验证这一点。

k_means_5 = KMeans(n_clusters=5)
model = k_means_5.fit(X)
y_hat_5 = k_means_5.predict(X)labels_5 = k_means_5.labels_
metrics.silhouette_score(X, labels_5, metric = 'euclidean')metrics.calinski_harabasz_score(X, labels_5)

轮廓系数= 0.261

CV 值= 48068.32

这两个值都高于我们之前的星团 12 和 8。我们可以得出结论,k=5 是我们的最佳聚类数。

最后,我们可以看到每个集群中包含的值。使用这个函数,我创建了一个效用图。

*#function that creates a dataframe with a column for cluster number*def pd_centers(cols_of_interest, centers):
        colNames = list(cols_of_interest)
        colNames.append('prediction')# Zip with a column called 'prediction' (index)
        Z = [np.append(A, index) for index, A in enumerate(centers)]# Convert to pandas data frame for plotting
        P = pd.DataFrame(Z, columns=colNames)
        P['prediction'] = P['prediction'].astype(int)
        return PP = pd_centers(cols_of_interest, centers)
P

如需进一步阅读:

  1. https://sci kit-learn . org/stable/modules/generated/sk learn . cluster . k means . html
  2. https://www . ka ggle . com/prakharrathi 25/weather-data-clustering-using-k-means/notebook
  3. https://www . datascience central . com/profiles/blogs/python-implementing-a-k-means-algorithm-with-sk learn
  4. https://blog . Cambridge spark . com/how-to-determine-the-optimal-number-of-k-means-clustering-14f 27070048 f

具有两个以上特征的聚类?尝试用这个来解释你的发现

原文:https://towardsdatascience.com/clustering-with-more-than-two-features-try-this-to-explain-your-findings-b053007d680a?source=collection_archive---------1-----------------------

使用 Plotly 的 line_polar 将向我们展示一种令人兴奋的聚类解释方法。此外,我们将看到,算法只是可以从集群中获得的商业价值过程的一小部分。

Aditya Chinchure 在 Unsplash 上拍摄的照片

我们的案子

想象一下,你在一家主要销售男装的新电子商务公司获得了一份很棒的工作,担任数据科学团队的负责人。你注意到,在面试过程中,问题主要集中在聚类上,现在你知道为什么了:公司的首席执行官迫使营销部门精心策划针对最具代表性群体的活动。营销团队知道你是最适合帮助他们的人!所以他们开了一个会议来解决这个问题,在简单介绍了情况后,他们很快就谈到了重点。“当我们了解我们试图接触的人群时,我们是定义合适活动的专家,但我们需要首先通过描述他们的行为来确定最具代表性的客户群。此外,我们知道这 6 个特征——它们向您展示了 csv 文件——最能描述我们的客户,您能帮助我们吗?”。

你觉得在你的幸运日,一个项目,由首席执行官推动,很容易符合标准集群的例子!看起来像是速战速决!但是有两件事困扰着你,那就是“描述他们的行为”和“6 个特征”这两句话一直在你的脑海中回响,这两句话的组合在追捕你。你能解决这个问题吗?。

动机

那么,我为什么要写这个呢?因为我对所有机器学习应用的商业用例充满热情,在这个特定的主题中,我已经看到了降维方面良好的直觉和可解释性。尽管如此,这些技术并不意味着解释每个群体的特征。

尽管我们将训练一个模型,但这不会是解释数学和讨论更好的误差测量的标准文章。这里的重点是思考集群的业务用例!

来自https://imgflip.com/memegenerator

重要的是要认识到集群只是实现确定的业务目标的一种工具。问题不在于创建最好的集群(尽管这很有帮助),或者仅仅是使用机器学习。在我们的案例中,问题在于我们想了解谁是我们的客户。

对我们客户的理解可能会有很大的影响,甚至对我们公司的战略也有影响,但是今天,让我们将这一点用于我们的目标案例。

一些基本概念

在上面的章节中,我假设您知道一些基本的概念,如果您不知道,我想向您展示我对它们的简短定义。如果你想深入了解,我将在下面嵌入一些资源(这里这也是一个好的开始)。

如果你认识他们,就跳过这一节。

  • 无监督学习 : 是在没有目标向量的情况下,理解数据中模式的魔力。
  • 聚类 : 是试图在一组对象(在我们的例子中是人)中定义组。目标是属于同一组的对象共享一些关键特征。
  • K-Means:是一种迭代算法,其中每个观测值属于具有最*均值(质心)的聚类。

把所有这些概念结合起来的力量,在识别甚至我们都不知道存在的群体时,会遭到诋毁。算法的工作是给实体分配一个标签,当所有实体都得到一个标签时,我们的工作就是让这些信息变得有价值。

我们的数据

我会马上向你们展示我们的数据集,因为我想很快展示一些关于它的东西。它是使用 Numpy 和熊猫创建的,但我不会透露构建过程,直到最后。你知道,了解我们的客户最激动人心的部分是一开始并不了解他们。我知道你不喜欢剧透!

正如我们所讨论的,这个数据集被认为是复制了一个电子商务,每一行都指向一个特定的客户。让我们从了解特征开始(这里是回购)。

ID: 客户的唯一标识符。

n_clicks: 产品的总点击次数。

n _ 访问量: 页面的总访问量。

金额 _ 花费: 在产品上花费的美元总额。

amount _ discount:在打折产品上花费的美元总额(该值应小于或等于amount _ spend)。

days _ since _ registration:客户注册后经过的天数。

profile _ information:一个介于 0 和 100 之间的指标,衡量客户填写了多少个人资料信息。

太好了!现在我们觉得已经准备好开始我们被赋予的使命。Plotly 这是伟大的可视化!图表的简单性、美观性和专业性对于更好地理解我们的数据来说是完美的。px.scatter_matrix 方法可以让我们了解整个数据集。

超过 3D?

如你所知,当有 2 个甚至 3 个特征时,很容易解释结果。这可能是因为我们的标准空间表示适合二维或三维图表,这对我们来说完全有意义。

当特征的数量超过我们三维思维的能力时,仍然有一些选择。我们可以定义图表中的颜色或大小等特征。例如,为了捕捉我们的 6 个特征中的 4 个,我将使用 Plotly express 向您展示一个示例。

有意思!我们可以看到最大的圆圈在左下方。此外,我们可以注意到“n 次点击”和“n 次访问”之间的正相关关系对于“days_since_registration”功能,颜色似乎与任何东西都没有明显的关联。

你觉得四个特征是可以管理的吗?让我们试试五个!

现在我们可以在下方的角落看到一个组,但是试图理解三维图加上颜色和大小信息有点让人不知所措。想象一下在创建集群之后的这个过程,由于标签(通常作为颜色添加)而增加了一个维度,我们可以肯定地知道这不是以正确的方式进行的。

这种使用多维方法进行探索性数据分析的非常规途径是为了向您展示这是可能的,但不建议遵循。很难扩展到六个以上的功能,更难理解实际发生的一切。

现在很清楚为什么拥有太多的特性是一个问题了。如果我们很难看到这些图表,想象一下向其他人展示这些图表有多难。我们该进入下一步了:聚类!

寻找我们的集群

因为找到最佳的集群数量或尽可能减少惯性不是今天最重要的部分,所以我将向您展示仅基于惯性和“肘规则”的集群数量的决定。

这个过程可以在很多方面进行改进,我不会在这里列出来,但你知道创建你的模型应该是一个更严格的过程,这很好。

如果你对惯性的细节感兴趣,请访问 Scikit-learn 的文档。

K-Means 是一种基于距离的算法。因此,规范化、标准化或选择任何其他选项,使距离对所有列具有某种可比较的意义,这是非常重要的。MinMaxScaler,对它来说是个极好的工具。

在缩放我们的数据集之后,我们可以在不同的聚类数上评估我们的惯性。如果我们看到图表,我们可以说肘部在 3 或 4 上。为了简单起见,我们将使用 3。

为什么不是 PCA 或 t-SNE?

我们将谈到我们刚刚发现的群体,但首先,描述一下向团队解释您的发现与其他领域的发现之间的一些差异是很重要的。

PCA 和 t-SNE 是减少数据集中要素数量的技术。这两种算法会有所帮助,因为我们可以在更少的维度上获得可视化,这将引导我们进行解释,以确定这些组是否确实被很好地分开。它在其他情况下也有帮助,在这个特殊的例子中,再次在 Scikit-learn 的文档中有很好的解释。

“惯性不是一个标准化的指标:我们只知道值越低越好,零是最佳值。但是在非常高维的空间中,欧几里德距离往往会变得膨胀(这是所谓的“维数灾难”的一个实例)。在 k-means 聚类之前运行主成分分析(PCA)等降维算法可以缓解这个问题,并加快计算速度。”

来自 https://imgflip.com/memegenerator的

那么,为什么主成分分析和 SNE 霸王龙无法解释我们的发现呢?因为那些算法是给我们的!如果我们使用它们,我们可以改进我们模型的结果!他们创造的新变量不能直接与我们的原始特征相比。因此,尽管这可以帮助我们,但它无助于将你的发现传达给他人。

为了避免离题太远,我们不会将这些算法应用到我们的数据集,但我鼓励您尝试一下,看看集群会发生什么!

描述我们的客户

在这一部分,我们将最终以一种对组织中的每个人都容易的方式描述我们的发现。简单来说,我们可以通过客户的特征来了解他们,因为我们知道每个变量都是可变的。

Line_polar 非常适合这个任务,因为一个圆可以处理任意多的变量,而且里面的线条非常直观!

首先,重要的是要看到全局,了解真正使我们的客户与众不同的变量。很明显,每个组的“注册天数”和“个人资料信息”都是相似的。这意味着我们可以从分析中减去它们。

看看每组有多少人也很重要。

之后,了解我们的客户在每个变量上的行为是很重要的。例如,如果你看标签 0,你可以理解他们点击、访问和花费的折扣比其他组少得多,但他们是——到目前为止——购买最多的客户。

我将把其他顾客的描述留给你,因为我知道你已经明白了。不过,我们的工作还没有结束,除非你把这个交给你的营销同事。

营销会议

好了,现在您已经分享了结果,并且每个人都很好地理解了它,业务用例可以蓬勃发展了。

分析“挥金如土”的群集,营销团队可以说,对他们来说,一个好的策略是首先在他们的主页上推荐最赚钱的产品!他们似乎只花很少的时间来选择他们的产品,我们可以断言,这是因为他们真的很珍惜他们的时间。你可以用描述顾客主要特征的插图来支持你的新朋友(line_polar )!

从https://www.flaticon.com。参考资料: 1 、 2 & 3

此外,简化生产环境以避免项目停留在“重要信息”和 Excel 表格满天飞的状态也很重要!将这些信息流与你公司网站的推荐整合起来是很重要的。

如果你感兴趣,我可以用这些相同的图表制作一个简单的 Dash 应用程序的教程。这是一个将模型结果货币化并帮助产品采用的好工具。

还有更多的话要说,但现在,重要的是你必须考虑如何创建一个有利可图的活动,并尝试部署它。

一个好的数据魔术师总是会展示他的技巧

如果您仍然对数据是如何创建的感到好奇,我将与您分享创建每个集群的技巧。你可以有趣地发现,很多本应成为某种类型客户的客户,最终却出现在了另一个集群中!

我认为,做一些更好的功能工程和尝试其他算法可能会成功。如果你有更好的效果,就在评论里分享吧!

代码如下:

最终想法

您可能已经注意到,这些组应该属于定义明确的特征。现实生活中会有很多嘈杂的数据,客户聚类可能会高很多。

此外,模型开发和客户的描述过于简单。*均值不是你应该关注的唯一指标。

不过今天就到这里吧!我希望这篇文章能对你有所帮助。回头见!

如果你喜欢,就在 Medium 和 Linkedin 上关注我。如果你想给我写信,我最*在推特上。我很乐意与你交谈!

如果你想多读一点,看看我最*的帖子:

[## 交易的概率机器学习方法+ MACD 商业理解

在文章的最后,你会知道如何用概率的方式构造一个交易问题。此外,您将…

towardsdatascience.com](/probabilistic-machine-learning-approach-to-trading-macd-business-understanding-6b81f465aef6) [## 优化…目标变量(?)交易机器学习模型的例子

我们的目标是静态的吗?

towardsdatascience.com](/optimizing-the-target-variable-an-example-for-trading-machine-learning-models-48a1587d7b9a)

CNN 应用程序-检测汽车外部损坏(完整的可实现代码)

原文:https://towardsdatascience.com/cnn-application-detecting-car-exterior-damage-full-implementable-code-b28fca70b20f?source=collection_archive---------57-----------------------

检测汽车外部损坏

深度学习和计算基础设施(云、GPU 等)的最新进展。)已经使计算机视觉应用实现了飞跃:从用我们的面部解锁办公室通道门到自动驾驶汽车。几年前,图像分类任务,如手写数字识别(伟大的 MNIST 数据集)或基本对象(猫/狗)识别,被认为是计算机视觉领域的巨大成功。然而,计算机视觉应用背后的驱动程序卷积神经网络(CNN)正在快速发展,采用先进的创新架构来解决几乎所有与视觉系统相关的问题。

汽车外部损坏的自动检测和随后的量化(损坏严重程度)将有助于二手车经销商(市场)通过消除损坏评估的手动过程来准确和快速地对汽车定价。这一概念同样有利于财产和意外伤害(P&C)保险公司,因为它可以加快理赔速度,从而提高客户满意度。在本文中,我将一步一步地描述使用 CNN 迁移学习利用 Tensorflow 后端检测汽车划痕(最常见的外部损坏)的概念。

汽车损坏检测——实例分割的典型应用

在详细讨论业务问题和实现步骤之前,我将讨论用于对象检测这一特殊应用的技术及其背后的基本原理。像大多数真实世界的计算机视觉问题一样,我们也将利用来自合适的预训练 CNN 的迁移学习来节省重新训练整个权重矩阵的大量时间。作为目标检测技术的一个典型应用,我们有几种选择技术——R-CNN、快速 R-CNN、快速 R-CNN、SSD 等。为了获得这些技术的概述,我鼓励阅读这篇文章。简而言之,像每个对象检测任务一样,这里我们也有以下 3 个子任务:

a)提取感兴趣的区域(RoI):图像被传递到 ConvNet,该 conv net 基于诸如选择性搜索(RCNN)或 RPN(区域建议 N/W,用于更快的 RCNN)的方法返回感兴趣的区域,然后在提取的 ROI 上的 ROI 汇集层,以确保所有区域具有相同的大小。

b)分类任务:区域被传递到完全连接的网络,该网络将它们分类到不同的图像类别中。在我们的情况下,它将是划痕(“损坏”)或背景(车身无损坏)。

c)回归任务:最后,使用边界框(BB)回归来预测每个识别区域的边界框,以收紧边界框(获得精确的 BB 定义相对坐标)

然而,在我们的案例中,仅达到方形/矩形的 BBs 是不够的,因为汽车划痕/损坏是无定形的(没有明确定义的形状或形式)。我们需要确定边界框中对应于类别(损坏)的确切像素。划痕的精确像素位置只会有助于确定位置和准确量化损坏。因此,我们需要在整个管道中添加另一个步骤——语义分割(感兴趣类别的逐像素着色),为此,我们将使用基于掩蔽区域的 CNN(Mask R-CNN)架构。

屏蔽 R-CNN:

Mask R-CNN 是一个实例分割模型,允许识别我们感兴趣的对象类的逐像素描绘。因此,Mask R-CNN 有两个广泛的任务——1)基于 BB 的对象检测(也称为定位任务)和 2)语义分割,其允许在场景内的像素处分割单个对象,而不管形状如何。将这两个任务放在一起,Mask R-CNN 可以对给定的图像进行实例分割。

虽然关于 R-CNN 面罩的详细讨论超出了本文的范围,但我们还是来看看基本组件,并对不同的损耗有一个概述。

屏蔽 R-CNN 组件(来源

因此,本质上掩模 R-CNN 具有两个组件——1)BB 对象检测和 2)语义分割任务。对于对象检测任务,它使用与更快的 R-CNN 类似的架构,掩模 R-CNN 的唯一区别是 ROI 步骤——它不使用 ROI 合并,而是使用 ROI align 来允许 ROI 的像素到像素保留,并防止信息丢失。对于语义分割任务,它使用完全卷积 n/w(FCN)。FCN 通过创建每个区域(不同的感兴趣对象)的逐像素分类,在 BB 对象周围创建遮罩(在我们的例子中是二进制遮罩)。因此,总体上,R-CNN 最小化了总损失,包括实例分割中每个阶段的以下损失。在进入不同的损失定义之前,让我们先介绍一些重要的符号。

  1. rpn_class_loss:为每个 ROI 计算 rpn 锚分类器损失,然后对单个图像的所有 ROI 求和,并且网络 rpn_class_loss 将对所有图像的 rpn_class_loss 求和(训练/验证)。所以这只是交叉熵损失。

  1. rpn_bbox_loss:网络 RPN BB 回归损失聚合为 rpn_class_loss。边界框损失值反映了真实框参数(即,框位置的(x,y)坐标、其宽度和高度)与预测值之间的距离。它本质上是一种回归损失,并且它惩罚较大的绝对差异(对于较小的差异以*似指数的方式,对于较大的差异以线性的方式)。

给定图像,该 RPN 阶段提取图像中可能的对象位置的许多自下而上的区域提议,然后它抑制具有≥ 0.5 IoU(交集/并集)标准的区域提议,并计算 rpn_class_loss(这些细化区域的正确性的度量)以及它们有多精确(rpn_bbox_loss)。精确的损失计算方法需要在中心(预测与地面实况)之间以及宽度(预测与地面实况)和高度(预测与地面实况)之间进行有点复杂的非线性转换。准确地说,网络减少了预测的 BB 坐标:(tx,ty,th,tw)-建议区域的位置与目标:(Vx,Vy,Vh,Vw)-该区域的地面实况标签之间的 SSE。因此,在为类“u”和预测边界框 t 结合了*滑 L1 损失函数之后,rpn_bbox_loss 将是:

因此,在 RPN 步骤中,总网络损耗为:

  1. mrcnn_class_loss:该损失的计算原理与 rpn_class_loss 相同,然而,这是在语义分割任务的逐像素分类期间在完全卷积 n/w(FCN)步骤的分类损失。

  2. mrcnn_bbox_loss:该损失的计算原理与 rpn_bbox_loss 相同,然而,这是在用于语义分割任务的掩模 R-CNN 边界框细化期间在完全卷积 n/w(FCN)步骤的 BB 回归损失。

  3. mrcnn_mask_loss:这是在掩蔽精确的对象位置(非晶外部汽车损坏位置)期间掩蔽头部的二进制交叉熵损失。相对于真实的类标签,它惩罚错误的每像素二元分类-前景(损坏像素)/背景(车身像素)。

虽然第一个损失是在 BB 对象检测步骤期间产生的,但是最后三个损失是在语义分割任务期间产生的。因此,在训练期间,使总损耗最小化的网络包括 5 个组件(对于每个训练和验证)。

商业问题

在二手车行业(市场和实体经销商),除了只能通过试驾/人工检查来评估的汽车功能和设备可用性和健康状况外,车身外部损坏(划痕、凹痕、重新喷漆等)。)在决定车辆的准确定价方面起着至关重要的作用。在大多数情况下,这些损坏是在汽车评估过程中从汽车图像中人工检测和评估的。然而,最新的计算机视觉框架可以检测车身上的损坏位置,并帮助定价者量化损坏,而无需太多的人工干预。这一概念还将帮助汽车保险公司自动评估损失,并更快地处理索赔。

在下一节中,我将简要讨论数据准备以及使用 Mask R-CNN 在真实汽车图像上实现这一概念。详细代码以及所有输入(内容视频和样式图像)和输出(生成的图像帧)可以在我的 GitHub 库的这里找到。

第一步:数据收集——尽管我们将利用来自适当的预训练(权重)CNN 架构的迁移学习,但我们需要为我们的特定用途定制网络,以最大限度地减少应用特定的损失——由于地面真实值和预测值之间的像素级损坏位置不匹配而造成的损失。因此,我们将使用从谷歌收集的 56 张汽车损坏图像运行 n/w 训练,其中 49 张图像用于训练,7 张用于验证目的。

第二步:数据标注——由于这个概念属于制度监督学习,我们需要标记数据。在计算机视觉对象检测或对象定位上下文中,这种标记称为注释。准确地说,对于我们的应用来说,它是识别图像中的损伤区域,并沿着划痕的边界精确地标记它们。出于注释的目的,我在这个链接处使用的是 VGG 图像注释器(VIA)。使用这个工具,我上传了我所有的图片,并沿着每个图片的损坏边界绘制了多边形遮罩,如下所示。

注释完所有图像后,我们将注释下载到。json 格式,我对训练和验证图像分别进行了处理。

第三步:环境设置-这是在收集的图像和注释(标签)上训练模型之前的重要步骤之一,因为我将使用“matter port Mask R-CNN”存储库来利用一些预先训练的 CNN n/w 权重矩阵,这些矩阵建立在不同的标准数据集上,如 COCO 数据集、 ImageNet 等。以及定制功能,例如数据处理和准备、配置设置、模型训练、创建日志文件以保存迭代方式权重矩阵对象& n/w 损失、对象检测、屏蔽检测到的局部区域等。为了在图像和注释上运行定制的训练功能,我们需要首先克隆存储库,遵循存储库中描述的精确的文件夹结构。这个 Matterport Mask R-CNN 建立在 Tensorflow 对象检测 API 之上。以下是开始培训前的步骤。

a)将训练和验证图像以及各自的注释文件保存在数据文件夹内名为“train”和“val”的单独子文件夹中。我把它命名为“定制”。

b)基于计算基础设施、期望的对象检测精度、训练步骤,我们需要定义训练配置。

class CustomConfig(Config):
    """Configuration for training on the toy  dataset.
    Derives from the base Config class and overrides some values.
    """
    *# Give the configuration a recognizable name*
    NAME = "scratch" *# We use a GPU with 6GB memory, which can fit only one image.*
    *# Adjust down if you use a smaller GPU.*
    IMAGES_PER_GPU = 1 *# Number of classes (including background)*
    NUM_CLASSES = 1 + 1  *# Car Background + scratch* *# Number of training steps per epoch*
    STEPS_PER_EPOCH = 100 *# Skip detections with < 90% confidence*
    DETECTION_MIN_CONFIDENCE = 0.9

c)最后,我们需要选择起点-预训练的权重矩阵对象,以开始训练过程。我选的是 mask_rcnn_coco.h5,是在 coco 数据集上预训练的。

步骤 4:加载数据集:在这里,我们加载训练和验证图像,并将单个图像标记到相应的标签或注释。这里我定制了 baloon.py 代码,按照应用程序(类标签、目录路径、形状标准化等)为 Mask R-CNN 编写。)来准备 custom_1.py,它加载图像和注释并将它们添加到 CustomDataset 类中。代码可以在我的 GitHub 库中找到。

class CustomDataset(utils.Dataset): def load_custom(self, dataset_dir, subset):
        """Load a subset of the dataset.
        dataset_dir: Root directory of the dataset.
        subset: Subset to load: train or val
        """
        *# Add classes. We have only one class to add.*
        self.add_class("scratch", 1, "scratch") *# Train or validation dataset?*
        assert subset in ["train", "val"]
        dataset_dir = os.path.join(dataset_dir + subset)

步骤 4:网络训练——现在我们需要用真实图像上的模型训练来改进基础‘mask _ rcnn _ coco . H5 ’,并且在每次迭代(历元)之后,更新的权重矩阵被保存在‘log’中。此外,迭代/历元方式的损失统计被保存以在 TensorBoard 中监控它。

def train(model):
    """Train the model."""
    *# Training dataset.*
    dataset_train = CustomDataset()
    dataset_train.load_custom(args.dataset, "train")
    dataset_train.prepare() *# Validation dataset*
    dataset_val = CustomDataset()
    dataset_val.load_custom(args.dataset, "val")
    dataset_val.prepare() *# *** This training schedule is an example. Update to your needs ****
    *# Since we're using a very small dataset, and starting from*
    *# COCO trained weights, we don't need to train too long. Also,*
    *# no need to train all layers, just the heads/last few layers should do it.*
    print("Training network heads")
    model.train(dataset_train,dataset_val,
                learning_rate=config.LEARNING_RATE,epochs=15,layers='heads')
                layers='hea

我们需要运行训练代码(。py 文件),使用以下命令

*### Train the base model using pre-trained COCO weights(I ran using these weights,Download 'mask_rcnn_coco.h5' weights before starting the training)*
py custom_1.py train --dataset=C:/Users/Sourish/Mask_RCNN/custom --weights=coco*### Train the base model using pre-trained imagenet weights(for this to download imagenet weights))*
py custom_1.py train --dataset=C:/Users/Sourish/Mask_RCNN/custom --weights=imagenet*## We can even resume from the latest saved callback(latest saved weights)*
python3 custom.py train --dataset=C:/Users/Sourish/Mask_RCNN/custom --weights=last

奇迹开始了。

步骤 4:模型验证——每次迭代(历元)时更新的权重矩阵保存在“日志”中。此外,迭代/历元损失统计数据保存在张量板中进行监控。

虽然大部分模型训练部分是标准化的,超出了我们的控制范围,我们无法控制,但我们可以在 TensorBoard 中查看不同的训练和验证损失组件(如前面部分所述)。同样从保存的回调(保存的权重矩阵),我们可以检查权重和偏差的直方图。

步骤 5:模型预测——在令人满意和期望的损失监控之后——理想地,训练和验证损失都单调衰减,我们可以在随机选取的验证图像上测试模型对象,以查看预测(汽车损坏掩蔽)的准确性。

image_id = random.choice(dataset.image_ids) *#select a random image from validation dataset*
image, image_meta, gt_class_id, gt_bbox, gt_mask =\
modellib.load_image_gt(dataset, config, image_id, use_mini_mask=False) *#image loading**# Run object detection*
results = model.detect([image], verbose=1)*# Display results*
ax = get_ax(1)
r = results[0]
visualize.display_instances(image, r['rois'], r['masks'], r['class_ids'], 
                            dataset.class_names, r['scores'], ax=ax,
                            title="Predictions")
log("gt_class_id", gt_class_id)
log("gt_bbox", gt_bbox)
log("gt_mask", gt_mask)*#Showing damage polygon on car body*
print('The car has:{} damages'.format(len(dataset.image_info[image_id]['polygons'])))

这是预测。

这个预测看起来还不错。

业务实施和未来之路

二手车经销商/汽车保险公司可以在合适的角度和位置安装带有高分辨率摄像机的基础设施,以点击不同车身部分(前部、后部、侧面等)的标准化(尺寸)图像。)并且可以检测汽车中所有可能的外部损坏。这个概念可以作为一个移动应用程序作为一个 API 解决方案,它可以简化汽车评估过程

此外,在检测和掩蔽汽车损坏之后,该过程可以帮助汽车评估者/索赔处理人员根据损坏的尺寸和*似相对面积(w.r.t .汽车表面积)来量化损坏的严重程度。最重要的是,由于我们利用了迁移学习,我们不必收集许多图像和后续注释,并且由于模型训练是从训练的权重开始的(“coco”),我们不需要训练太长时间。此外,这一概念还可以扩展到检测其他类型的可见汽车损坏/故障。

从海报预测电影类型的 CNN 方法!

原文:https://towardsdatascience.com/cnn-approach-for-predicting-movie-genre-from-posters-95f122f88bc2?source=collection_archive---------35-----------------------

深度学习

卷积神经网络是一类深度神经网络,用于图像或视频识别和分类任务。

图片由天鬃发自 Pixabay

我们能建立一个模型来了解是什么让一种类型的电影海报与其他类型的不同吗?电影海报描绘了关于这部电影的许多事情。它对激发观众对电影的兴趣起着至关重要的作用。在上面的例子中,颜色主要是红色和黑色,因此在训练时,我们的模型可以学习将这种类型的图像分类为“恐怖”或“惊悚”。这将是一项有趣的任务。当你看不同类型的海报时,你会注意到它们在某些方面是不同的。比如看下面的海报。本文使用的所有电影海报图片均收集自 IMDB

我们是米勒夫妇(2013)

它代表了一部喜剧电影。现在看看下面的动作片海报。我们可以看到海报代表了电影类型的一个重要方面。

量子的慰藉(2008)

在这个项目中,我们将建立一个神经模型,可以区分三种电影类型的海报,并预测任何随机海报的类型。我们将从零开始一步一步建立这个模型!本项目使用的数据集是在 IMDB 的帮助下自行创建的。它包含超过 3900 张各种类型的海报图片——动作片、喜剧片、戏剧片。

让我们进入编码部分。

1.使用数据集

我们的数据集结构如下所示。我们将训练图像和测试图像保存在不同的目录中。每个目录包含三个子目录——动作、喜剧和戏剧。这些子目录进一步包含我们的电影海报的图像。

数据集的目录组织

我们将使用 ImageDataGenerator 进行标记。来自 Keras API 的 ImageDataGenerator 通过自动标记数据来帮助我们。在代码中实现数据扩充时,这也很有用。让我们看看这是如何在编码中实现的。

import tensorflow as tf
import keras_preprocessing
from keras_preprocessing import image
from keras_preprocessing.image import ImageDataGeneratorTRAINING_DIR = "/images2/Training"training_datagen = ImageDataGenerator(rescale = 1./255,
    width_shift_range=0.2,
    height_shift_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest')VALIDATION_DIR = "/images2/Validation"validation_datagen = ImageDataGenerator(rescale = 1./255)train_generator = training_datagen.flow_from_directory(
    TRAINING_DIR,
    target_size=(150,150),
    class_mode='categorical',
    batch_size = 256
)validation_generator = validation_datagen.flow_from_directory(
    VALIDATION_DIR,
    target_size=(150,150),
    class_mode='categorical',
    batch_size= 64
)

我们将首先创建一个 ImageDataGenerator 实例,用于训练和验证目的。由于像素值的范围是从 0 到 255,我们将在 0 到 1 的范围内对它们进行归一化。为此,我们将传入参数( rescale = 1)。/255) 创建 ImageDataGenerator 的实例时。在这之后,我们将使用。实例的 flow_from_directory() 方法来标记两个目录的图像,并将结果存储在 train_generatorvalidation_generator 中,用于训练和验证目的。调用该方法时,我们将传入 target_size 属性,以确保数据集中的图像大小相同。这里我们有 3 个类,所以我们必须传入 class_mode 参数作为分类。训练和验证的批量大小取决于我们数据集中的图像数量。

我们将数据分为三类——动作片、喜剧片和剧情片。现在我们可以继续创建我们的 CNN 模型。

2.创建 CNN 模型

我们将使用 Keras 的序列模型来构建我们的模型。我们将添加 3 对 Conv2D 和 MaxPooling2D 层。然后我们将添加扁*化层,使我们有我们的数据在一个维度。最后,我们将添加一个具有 1024 个隐藏单元的完全连接的密集层,后跟一个 softmax 层。下面是实现这一点的代码。

from tensorflow.keras.optimizers import RMSpropmodel = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),
    tf.keras.layers.MaxPooling2D(2,2),
    tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(1024, activation='relu'),
    tf.keras.layers.Dense(3, activation='softmax')
])model.compile(loss='categorical_crossentropy',
    optimizer=RMSprop(lr=0.001),
    metrics=['acc'])

创建模型后,我们将在编译模型时使用 RMSprop 优化器,它允许我们根据需要调整学习速率。这里,学习率是在用模型进行多次测试之后选择的。我们需要传递分类交叉熵作为我们的损失函数,因为我们有两个以上的类。

我们的模型可以开始训练了。让我们用之前标注的数据来训练它。

3.训练模型

我们将传入我们之前创建的带有正确值的 train_generatorvalidation_generator 变量。

history = model.fit(
    train_generator,
    steps_per_epoch = 36,
    epochs = 100,
    validation_data = validation_generator,
    validation_steps = 36
)

在 100 个时期之后,我们的模型给出了 69.8%的训练准确度,而验证准确度仍然是 53.3%。下面是为两个准确性指标绘制的图表。

正如我们可以看到的,最高验证精度约为 0.53,训练精度约为 0.70。现在让我们在一些图像上尝试我们的模型。

4.测试我们的模型

我们将使用 Google Colab 的内置库来上传图像,然后我们将它们传递给我们的模型,看看是否可以获得正确的类型。

# predicting for random images
import numpy as np
from google.colab import files
from keras.preprocessing import imageuploaded = files.upload()for fn in uploaded.keys():
    path = '/content/' + fn
    img = image.load_img(path, target_size=(150, 150))
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    images = np.vstack([x])
    classes = model.predict(images, batch_size=256)
    print(classes)

我们将传递三种不同类型的电影海报—动作片、喜剧片和剧情片。

上面代码的输出如下所示。

我们正在对这三种情况进行正确分类,但这不会每次都发生。请记住,我们的验证准确率仍然在 53%左右,所以在一半的情况下,我们的预测可能会出错。

你可以在 GitHub 的这里找到所有的代码,在 LinkedIn 的这里找到我。

未来的范围和限制

这里使用了非常小的数据集,因此精度较低。在未来,一个更大的数据集可能被用来提高准确性,甚至预测同一部电影的多个流派。这里,该模型仅预测 3 种类型的流派,但在未来,可以使用 ResNet 建立一个更复杂的模型,预测超过 10 或 20 种类型的流派。机器学习算法 K-最*邻也可以用于此目的。

结论

上面我们看到了如何建立一个模型,从海报中预测电影类型。还有一些海报会很难归类。比如下面这个。下面展示的海报是一部剧情片。我们可以看到它只包含文本。因此,我们的模型很难预测正确的类型。

类型预测领域还没有被完全探索。使用 CNN 进行图像识别任务可以证明对于从电影海报的图像中预测类型是有用的。CNN 可能会发现喜剧电影海报与动作电影海报的不同之处。

CNN 方法:利用聚合物图像预测其玻璃化转变温度

原文:https://towardsdatascience.com/cnn-approach-using-image-of-the-polymer-to-predict-its-glass-transition-temperature-4a64ee450450?source=collection_archive---------23-----------------------

在本文中,我们将使用聚合物结构的图像来预测其玻璃化转变温度。这篇文章采用了与西班牙材料物理中心和 Donostia 国际物理中心的 Luis.A. Miccio 最*发表的一篇研究论文相似的方法。

简介:

玻璃化转变温度是聚合物的重要性质之一。它标志着过冷液体的原子在冷却时暂时冻结(不结晶)的温度范围。预测玻璃化转变温度(Tg)提供了对聚合物性质的有价值的见解,否则其合成可能是昂贵和耗时的。科学家总是更热衷于定性地开发机器学习模型(例如,使用其他几个属性来预测其拉伸强度)。在过去的几年里,主要的重点已经放在定量结构-性质的关系。这使得仅用分子(即仅图像)化合物的结构预测各种性质成为可能,避免了任何额外的实验性质或繁琐的计算。在本文中,我们将使用卷积神经网络,利用聚合物的图像来预测未知聚合物化合物的 Tg。这听起来很酷,这实际上意味着如果你在白板上画出单体单元的图像,就足以预测它的 Tg。我们不需要聚合物的任何其他外部信息或性质。

导入相关包

数据集

我们研究中使用的数据集来自一个流行的聚合物数据库。这项研究的数据集包括 351 种聚合物及其 smiles 代码,分子名称作为输入属性,玻璃化转变温度作为输出变量。300 种聚合物及其 Tg 值的子集用于训练验证数据集,而其余 51 种未知聚合物用于测试模型、CNN 和提议的 ANN 的结果。下图显示了数据集的前 5 行。这项研究的数据集可以在这里找到。

读取和清理数据集

数据框的前 5 行

聚合物分类

该数据集使用 python 中的 Pandas 库进行手动探索,并被分类为八种不同类别的聚合物——丙烯酸酯、苯乙烯、酰胺、烯烃、醚、酰胺、碳酸酯等。

探索性数据分析

饼图:图中的饼图显示了数据集的确切组成,其中丙烯酸酯和苯乙烯的贡献最大

箱线图:箱线图用于显示各类聚合物的基本 Tg 分布散点图。可以看出,苯乙烯倾向于具有较高的 Tg,而丙烯酸酯具有相当混合的分布。

单体中的电荷分布

开源的 RDKit [1] python 包用于将聚合物的分子结构从数据集可视化到绘图中。RDKit[1]模块中的一个这样的函数用于计算单体单元的 Gasteiger 部分电荷。

特色工程

工程特征在为建模准备数据和以机器可读的形式呈现属性方面至关重要。根据问题陈述,Tg 预测是基于使用微笑线符号以编码形式输入 CNN 架构的聚合物化学结构的图像。这个问题的特征工程的主要目的是结合单体单元的化学结构和化学组成来预测 Tg。这是通过使用微笑线符号[2]实现的。

SMILES 符号介绍: SMILES 代表简化的分子输入行输入系统。这基本上是一种用不同的字符以线符号的形式描述化学结构的方法。下图显示了给定化学结构的 SMILES 符号。

分子结构到图像编码:我们首先定义了一个列表,该列表包含了可以出现在聚合物的任何给定 SMILES 线性串中的所有独特字符。

此外,作为第二步,通过使用该独特微笑字符列表,通过二进制图像以机器可读形式对线符号形式的聚合物线性串进行 1-hot 编码。最终的转换是一个由二进制图像组成的 n 维矩阵,可以输入 CNN 架构。每个二进制图像是 m × n 维的矩阵,其中 n 表示唯一微笑列表中的字符数,m 是具有最长微笑代码的聚合物中存在的字符数。下图直观地描述了一个聚合物实例-聚(4-联苯丙烯酸酯)的编码过程。

分子结构到图像编码过程

生成的一个热编码图像考虑了化学结构和单体单元的组成。我们可以看到,编码图像告诉我们以二进制形式存在于单体结构中的每种原子的数量,以及聚合物链中原子相对于彼此的排列结构。

下图生成了数据框中前五种聚合物的编码图像。

数据框中前五种聚合物的编码图像

模型实现

图像编码的分子结构作为输入输入到 CNN,目标变量是给定聚合物的 Tg,这是一个连续变量。这个模型是使用 Keras 库实现的,Keras 库是 Tensorflow 的应用程序编程接口(API)。

CNN 示意图[2]

提议的架构:通过整合所有不同超参数的各种组合,选择了最终超参数。最佳观察配置在第一层中使用大小为 64 的滤波器,窗口大小为(5,5),在第二层中使用大小为(3,3)的 32 个滤波器。接下来是窗口大小为(3,3)的 max-pooling 层。在最大池层之后,我们有三个密集层,分别具有 32、10 和 1 个神经元,最终的密集层是我们提出的 ANN 模型的输出。ReLu 激活函数由具有 l2 正则化的所有层使用。该模型通过训练多达 180 个时期达到其最佳泛化能力,批次大小为 64,学习率为 0.03。0.1 的验证分割和 0.1 的退出概率用于训练网络以执行交叉验证。

结果

下面给出的图显示了训练集和未知测试集的玻璃化转变温度的实验值和预测值。对于一个理想的模型,我们期望真实值完全等于预测的 Tg 值。这将导致一条直线穿过原点。

训练集的实际与预测 Tg 值

看不见的测试集的实际与预测 Tg 值

可以看出,与真实的 Tg 值相比,大多数实施例显示出非常精确的预测。然而,由于缺乏足够的训练数据,有一些聚合物在预测中造成了很大程度的不确定性。这些聚合物属于酯类和醚类的少数类别,由于对这两类聚合物的培训不足,这些聚合物的 Tg 没有得到有效的了解。

损失度量:我们在训练神经网络时使用了*均绝对损失函数。但是对于我们的最终评估,我们使用*均相对误差%作为我们模型的评估标准。这可以表示如下-

其中 Ai 是实际的 Tg 值,Pi 是预测的 Tg 值。这一相对%误差的*均值取自 m 聚合物的全部数据集。在训练过程之后,我们计算各自的训练和测试*均相对误差。

下表显示了 4 种未知聚合物的真实和预测 Tg 值。我们可以看到,我们的预测非常接*实验 Tg 值,表明所提出的模型显示出优异的泛化能力。

结论

在这项研究中,我们通过考虑聚合物中单体单元的分子结构和化学组成,证明了 CNN 预测聚合物 Tg 的可行性。我们能够分别实现 6%和 7%的训练和测试集的相对误差。在我的下一篇文章中,我将使用完全连接的神经网络来预测玻璃化转变温度。这个新模型将结合所有种类的分子内相互作用以及化学组成和分子结构来预测 Tg。

学分

特别感谢[丹麦](http://Special Thanks to Danish for contributing in this project')对这个项目的贡献。

参考文献:

1-G. Landrum 等人,“Rdkit:化学信息学和机器学习软件”。ORG,2013。

2-Luis A. Miccio,Gustavo A. Schwartz,“通过卷积神经网络从化学结构到定量聚合物性质预测”,聚合物,2018 年

感谢您的阅读!!!!

如果你喜欢我的工作并想支持我:

1-支持我的最好方式是跟随我上

2-在LinkedIn上关注我。

卷积神经网络——概要

原文:https://towardsdatascience.com/cnn-cheat-sheet-the-essential-summary-for-a-quick-start-58820a14d3b4?source=collection_archive---------24-----------------------

图片作者。

每年都有成千上万的新论文在顶级计算机视觉会议上发表,如 ECCV、CVPR、ICCV 等。跟随每一项新技术和方法真的会让人不知所措。然而,当新技术被放到一个环境中时,消化它们要容易得多。本文重点关注卷积神经网络(CNN),它构成了图像和视频处理的深度模型的主干。

很多时候,我的学生问我:“从哪里开始?”。类似地,有各种各样的方法可用,许多从那些更有经验的人的头脑中溜走。由于上述原因,这篇文章是 CNN 学生的快速入门指南,希望也是那些有更多专业知识的人的参考点。我还把它做成一个 CNN 备忘单,让未来的自己在需要的时候回去修改。

我确实错过了一些宝贵的东西,所以请让我知道,我们可以把这篇文章做得更好!

概观

CNN 的成功和广泛采用始于 AlexNet 在 ImageNet 竞赛中的惊人成绩。与第二好的方法 74.9%的分数相比,提出的 CNN 有 84.7%的准确率。性能随后被更深层次的模型(如盗梦空间、谷歌网、 VGG 等)提升。).

CNN 在计算机视觉任务上的优异表现可以归因于其针对图像数据定制的结构。最*一篇名为深度图像先验的论文显示,CNN 的架构已经整合了自然场景统计的先验。与全连接网络不同,CNN 能够利用这样一个事实,即相邻像素之间的关系比相距较远的像素之间的关系更重要。

在全连接网络中,每个输入都与下一层中的每个神经元相连,与此相反,CNN 使用卷积作为基本运算。只有被卷积核覆盖的像素被传递到下一层中的神经元。因此,CNN 捕捉图像内的局部空间关系。对图像数据使用 CNN 允许较少数量的权重,因此,对于基于图像的任务,使用与完全连接的网络相同或更好的预测能力,训练模型是轻量级的和更容易的。

然而,尽管专门针对图像数据的架构考虑显著改善了结果,但 CNN 仍然会遇到问题,这是深度模型的典型问题。其中一些是消失梯度、缺乏稳健性、收敛缓慢、内部协方差漂移等等。更具体的问题也出现了:如果本地关系假设不成立呢?如果用 CNN 生成图像,如何保证生成的结果在感知上令人愉悦?使用 CNN 进行分类或回归时,如何处理大小不一的图像?

在这篇文章中,我将谈论:

  • 消失梯度问题
  • 收敛速度
  • 如何实现不变性和等方差
  • 去除由去卷积引起的伪像
  • 图像到图像转换的损失函数
  • 不同尺寸图像的进给方式
  • 如何考虑遥远的空间关系
  • CNN 更复杂的模型扩展

如果你想重温卷积如何应用于机器学习的一般知识,请查看这篇文章。

消失梯度

随着网络的规模增大,训练难度也随之增加。在训练期间,任何权重的更新都与偏导数的大小成比例。由于隐藏层中存在饱和非线性(例如,sigmoid ),梯度在向网络的早期层传播时会变得无限小。在极端情况下,网络完全停止训练。这个问题被称为消失梯度。

这可以通过跳过连接来弥补。总体思路是将通过非线性的信号与旁路非线性的信号相结合。一篇更深入解释这些的优秀文章是这里。在这里,我给出了两个最常用的使用跳过连接的架构的例子。我还谈到了一个更好的替代 ReLU 激活的方法,它也会影响性能。

残留层

残留块。图片由明凯何

正如 ResNet 的成功所显示的,剩余层是相对简单的修改,然而它显著地改进了训练和性能。对于每个残差层,输入 x 通过该层(如同对于标准层一样),产生输出(F( x ),然后将该输出与原始输入 x 相加。在反向传播期间,梯度因此经由非线性 F( x )和原始 x 通过网络反向传播,从而产生更高效的更新。

U 网

图片由 Olaf Ronneberger 提供。

U-NET 是一个完全卷积的网络,最初是为图像分割而设计的。该架构由一个捕获上下文的收缩路径和一个支持精确定位的对称扩展路径组成。U-NET 背后的想法类似于剩余层。然而,来自网络中较早层的信号在信号扩展部分被链接到对称层,而不是绕过单个层。

激活功能

具有不同参数的 Swish 激活功能示例。图片由 Prajit Ramachandran 提供。

另一种改善反向传播的方法是改变激活函数。在 CNN 中,sigmoid 或 softmax 的使用已被 ReLU 广泛取代,但还有更好的选项可用— SWISH 。Swish 修复了 ReLU 激活中的不连续性,显著改善了梯度更新。

趋同;聚集

网络的收敛(达到预期精度所需的迭代)会受到许多因素的影响。其中之一是内部协方差偏移。由于训练是一个动态过程,对于给定的隐藏层,随着训练的进行,其输出的分布随时间而变化。反过来,后续层必须在学习其权重的同时适应变化的分布。通过标准化每一层的输出,训练可以得到显著改善,因为消除了内部协方差偏移的问题。这里我给出了两个常用的归一化方法的例子。在这里可以找到一篇关于内部协方差转变补救措施的优秀文章。

批量归一化

算法 1:应用于小批量激活 x 的批量规范化转换。

在批量标准化中,除了网络中的权重之外,我们还针对比例和移位参数(γ和β)进行训练。

训练的稳定性是通过用批次的*均值和标准偏差标准化每次激活的层输出来实现的。为了确保模型也捕获输入的比例,还为 a 层训练了两个全局参数γ和β。无论批次如何,这些都可以稳定活化程度。由于我们无法访问测试时间内的每批统计数据,因此我们可以使用训练数据集中的层输入的*均值和标准差来标准化测试期间的层输出。

按照原始论文的步骤,对于卷积图层,我们还希望归一化遵循卷积属性-因此,相同要素地图中不同位置的不同元素将以相同的方式进行归一化。为了实现这一点,小批量中的所有激活在所有位置上被联合标准化。在上面的算法中,参数对γ和β是根据特征图学习的,而不是根据激活学习的。

*滑梯度更新还允许使用大得多的学习率。

层规格化

像批量归一化一样,我们也给每个神经元它自己的自适应偏置和增益,在归一化之后但在非线性之前应用。与批处理规范化不同,层规范化在训练和测试时执行完全相同的计算。

不变性和等方差

****

反射不变性:我们期望网络将该图像分类为猫的图像,而不管其方向如何。图片作者。

不变性和等方差经常被混淆和互换使用,然而,两者定义了不同的性质。

简单来说,不变性是一个函数的属性,其中对于扰动的输入,输出保持不变。例如,我们希望分类网络是输入旋转不变的,其中,无论输入倾斜如何变化,预测的类都不会改变。不变性通常还与去噪鲁棒性相关,即模型即使在输入被破坏的情况下也能保持高性能的能力。

另一方面,等方差意味着函数以输出的类似变化来响应输入的变化。这个属性对于图像转换任务是理想的,例如,考虑图像分割,其中,如果我们旋转图像,该图像的分割图也将旋转。我发现 StackExchange 上的这个答案对于理解不变性和等方差特别有用。

统筹

最大池操作。图片由计算机科学维基提供。

在 CNN 中实现不变性的方法之一是池化。预测对要素的局部位置很敏感。通过汇总各个层中的局部信息,可以实现局部不变性或对局部扰动的鲁棒性。例如,考虑对图像中的像素进行*均,如果一个像素改变,则所有像素的*均值不会经历显著的变化。类似的操作可以在隐藏层中完成,并通过池化来实现。通常,使用加权和最大池。一个独立的研究方向与改进的池操作有关,因为这是学习池操作。

这个想法是总结当地的信息。当在每一层中汇总信息时,影像中相距较远的要素会在网络的更深层中靠拢。因此,在隐藏层中,表示被压缩,并且在后续层上的卷积是在信息上完成的,该信息先前是全局的,但是由于汇集而变得局部化。汇总本地特征的优势也是它的弱点,因为大部分信息丢失了。

它提供不变性(丢弃位置信息)而不是等方差(解开信息)。

胶囊网络

池丢弃了大量的信息,并提供了不变性而不是等价性。此外,CNN 有一个显著的缺点,即不能在图像特征之间建立关系。这种失败的一个例子是“毕加索问题”,一张脸的嘴和眼睛交换后仍然会被归类为一张脸。

胶囊网络是 CNN 的替代方案,并且也可以作为其架构的一部分并入更深的层中,这弥补了上述局限性。胶囊是一组神经元,对特征的方向及其概率进行编码。这些通常需要更少的数据来训练;然而,不同于标准反向传播的训练过程要耗时得多。胶囊网络是一个单独的话题,在博客、论文 1 和论文 2 中有很好的解释。

谐波网络

CNN 滤波器被径向重新加权和相移的圆形谐波所取代。这使得每个滤波器响应对于 m 阶输入旋转是等变的。丹尼尔·沃拉尔。

谐波网络通过将滤波器限制为圆形谐波族,将逐片 360°旋转等变设计成深度图像表示。圆形谐波是可操纵滤波器,这意味着我们可以表示滤波器的所有旋转版本,只需使用操纵基底的有限线性组合。这克服了在 CNN 中学习多个滤波器副本的问题,保证了旋转等方差,并产生了在输入旋转下可预测地变换的特征图。

每一层都是不同旋转阶数的特征映射的集合,这些特征映射在网络输入的旋转下可预测地变换,并且 360°旋转等方差通过有限的计算来实现。

解决去卷积问题

去卷积导致棋盘图案的一个例子。

通过全卷积网络的瓶颈后,信号通过解卷积逐渐扩展到其原始维度。应用去卷积通常会产生棋盘图案,即步幅小于滤波器大小,导致滤波器重叠。

对于棋盘模式有几个标准和直观的解决方法:使用除以步幅的内核大小,避免重叠问题;或者例如通过使用双线性或最*邻上采样,从卷积中分离出较高分辨率的上采样来计算特征。更多解决问题的方法,请看这篇文章。

损失函数

图像超分辨率与 EDSR 网络训练有素。从左到右:地面真相,输出的模型训练与 SSIM 作为一个损失,输出的模型训练与 L2 作为一个损失。图片来自 DIV2K 数据集

当针对 L2 损失(即,所生成的图像和地面真实情况之间的均方差)进行优化时,所得到的图像通常是模糊的,因此在视觉上不是很令人愉快。相反,我们想要一个感知损失函数,为它进行优化我们可能不会获得最低的 PSNR,但产生的图像将是清晰的和在感知上令人愉快的。

对于地面真实图像可用的情况,与主观人类评分高度相关的图像质量度量,例如 FSIM 和 SSIM,可以用作损失函数。通过优化来最大化他们的分数,我们正在学习生成高质量的图像。另一种方法依赖于预先训练的深度架构在深度表示中学习图像统计的假设。

这样,在分类或图像质量(LPIPS) 上预先训练的 VGG 网络可以被用作损失。这里,地面真实图像和生成的图像通过预先训练的网络,并且基于某种距离度量(例如 L2)来比较生成的网络特征。当不需要图像之间的精确匹配时,可以使用上下文丢失。

训练仅仅依赖于从深度网络中提取的特征,因为丢失是不稳定的。由于实现该函数的网络通常不是双射的,由于隐藏层中的汇集操作,对该函数的不同输入可能导致相同的潜在表示。因此,损失通常是 VGG 和 MSE 或 L1 的组合。

对于图像生成,或者在参考图像不可用的情况下,常见的方法是使用生成模型,最先进的方法是生成对抗网络( GANs )。目标是训练一个生成器,一个用于图像任务的 CNN,它将生成复制那些在预先指定的分布中的图像。这里的鉴别者充当了法官的角色。

允许不同尺寸的图像

在不同大小的图像上使用相同的 CNN 是可取的。然而,当使用 CNN 作为分类器时,卷积层之后通常是全连接层。虽然卷积层的大小不变,但全连接层是在预先指定的输入数上运行的。

空间金字塔汇集

图片由明凯何提供。

在全连接层之前,我们使用一个固定的池层,它只从卷积层中选择预定义数量的元素,而不考虑其大小。关键思想是从不同分辨率的图像中提取特征,并使用自适应池,其中我们调整池元素的数量,以实现固定数量的总输出神经元。

建造“金字塔”的想法对成像来说并不新奇。众所周知的 SIFT 算法可以说是其应用中最成功的例子之一。实现比例不变性的另一种方法是将图像的表示改变为点云,并使用*移和比例不变的点网或变体。点云的更多细节可以在这里找到。

考虑到空间关系

典型的 CNN 能够提取图像中相距很远的部分之间的关系。由于卷积和汇集层,通过网络传播的输入信号在其宽度和高度上被压缩,并且通常在深度上被扩展。这样,信号的高级上下文被学习。然而,由于更深层的网络压缩了信息,弱关系可能会丢失。

因此,当图像内的关系更复杂时,基于局部像素关系假设的 CNN 的预测能力会大大降低。例如,在一场足球比赛中,将球从图像中移除,训练网络根据球员的目光来预测球的位置。这是一个局部假设不再成立的问题的例子。为了解决这种问题,卷积运算可以扩展到具有更大感受野的核。

扩张的脑回

图片由 Fisher Yu

在一个简单的卷积中,我们会有一个 3×3 的核,现在让我们假设这个核的所有 9 个元素都是非零的。在 2-扩张卷积(下图中的 b)中,我们将对一个大小为 5x5 的稀疏内核进行操作,该内核包含九个非零元素。

可变形卷积

图片由季枫戴提供。

可变形卷积背后的思想与膨胀卷积背后的思想相同,然而这里我们让网络在训练期间学习偏移方向和幅度。

非局部神经网络

非局部块中的计算。I 是要计算其响应的输出位置(在空间、时间或时空中)的索引,j 是枚举所有可能位置的索引。x 是输入信号(图像、序列、视频;通常是它们的特征),而 y 是与 x 大小相同的输出信号。成对函数 f 计算 I 和所有 j 之间的标量(表示关系,例如亲和力)。一元函数 g 计算位置 j 处输入信号的表示。响应由因子 C(x)归一化。

考虑空间关系的另一种机制是学习图像中位置之间关系的重要性。在非局部神经网络中,非局部块被注入网络的深层。2018 年,该模型在静态图像识别方面取得了最先进的成果。非局部模型也改进了对象检测/分割和姿态估计。

这种方法的缺点是计算复杂度高。妨碍了早期(网络的较浅层)非本地块的使用,降低了空间关系的精度。

学习复杂关系

卷积是底层数据块上的线性操作符,只允许学习低级别的抽象。用更具表达性的非线性函数代替简单卷积可以增强局部模型的抽象能力。当潜在概念的样本是线性可分的,即概念的变体都位于由卷积滤波器定义的分离*面的一侧时,线性算子可以实现良好程度的抽象。

传统的 CNN 隐含地假设潜在的概念是线性可分的。然而,相同概念的数据通常存在于非线性流形上,因此捕捉这些概念的表示通常是输入的高度非线性函数。在“网络中的网络”中,卷积被一个“微型网络”——一种非线性多层感知器——所取代。

网络中的网络

图片由林敏

线性卷积层和 mlpconv 层都将局部感受野映射到输出特征向量。mlpconv 使用多层感知器将输入局部面片映射到输出特征向量,该感知器由具有非线性激活函数的多个全连接层组成。多层感知器在所有局部感受野之间共享。特征映射是通过滑动感知器获得的。这种架构的训练要慢得多,但是效果更好。

摘要

在这篇短文中,我介绍了提高 CNN 性能的常用策略。我已经谈到了消失梯度问题,改善收敛、不变性和等方差特性的方法,考虑了空间关系,谈到了将不同大小的图像馈送到 CNN 的方法,还谈到了图像转换算法的损失,这些算法用于实现感觉上令人满意的结果。

本文旨在缩短篇幅,省略了许多技术。如果你认为我错过了一些重要的东西,请不要犹豫给我留言或留下评论。

进一步阅读

为了进一步阅读,我推荐这篇综述文章,并且一定要仔细看看提出这些总结想法的原始论文。

由于深度学习方法越来越多地部署在移动设备上,因此出现了不同的技术来使这些设备变得更小,例如深度方向可分离卷积。

附:非常感谢 帕拉姆·韩吉 阿利亚克桑德拉·什谢亚 的反馈:)

喜欢作者?保持联系!

我错过了什么吗?不要犹豫,直接在 LinkedIn 或 Twitter 上给我留言、评论或发消息吧!

** [## 深度图像恢复的感知损失

从均方误差到 GANs——什么是好的感知损失函数?

towardsdatascience.com](/perceptual-losses-for-image-restoration-dd3c9de4113) [## 深度图像质量评估

深入研究全参考图像质量评估。从主观画质实验到深层客观…

towardsdatascience.com](/deep-image-quality-assessment-30ad71641fac) [## 深度视频修复

用深度神经网络去除视频中不需要的对象。问题设置和最先进的审查。

towardsdatascience.com](/deep-video-inpainting-756e60ddcaaf)**

CNN 图像分类:猫还是狗

原文:https://towardsdatascience.com/cnn-classification-a-cat-or-a-dog-568e6a135602?source=collection_archive---------17-----------------------

基于 Keras 的卷积神经网络在图像分类中的应用

Img 改编自 pixabay 通过链接

在本文中,我将一步一步地介绍如何创建一个基于卷积神经网络(CNN)的图像分类模型。它分为 7 个部分。

  1. 问题陈述
  2. 数据概述
  3. 模型结构
  4. 模型编译
  5. 模型拟合
  6. 模型改进
  7. 示例预测

让我们开始旅程吧🏃‍♂️🏃‍♀️.

1。问题陈述

我们得到了一组狗和猫的图片。任务是建立一个模型来预测一种动物的类别:狗还是猫?

2。数据概述

我们收集的数据是 Kaggle 狗/猫数据集的子集(链接)。总共有 10,000 幅图像,80%用于训练集,20%用于测试集。在训练集中,有 4000 张狗的图像,而测试集有 1000 张狗的图像,剩下的都是猫。

所有图片都保存在专门的文件夹结构中,便于 Keras 理解和区分每张图片的动物类别,如图 1 所示。

图 1.1 创建培训/测试文件夹

图 1.2 分别在训练/测试文件夹中创建狗/猫文件夹

不需要对变量进行编码,因为独立变量是像素值。

3。模型构建

一般来说,构建 CNN 需要 4 个步骤:卷积、最大池化、扁*化和全连接。让我们详细了解一下每一个。

3.1 卷积

从概念上讲,卷积是在输入图像上应用特征检测器。为了简化概念,取一张笑脸作为输入图像,在图 2 中表示为 0 和 1 的数组。

图 2 Simile face——输入图像(作者创建的 Img)

特征检测器也是一个数字数组。对于每个特征检测器,我们在图像上滑动它,产生一个新的数字数组,代表图像的一个特征。因此,输入图像和产生特征图的特征检测器之间的操作是卷积,如下图 3 所示

图 3 卷积图(作者创建的 Img)

如果用不同的特征检测器重复上述卷积,我们产生与特征检测器一样多的特征图,获得卷积层。

具体来说,我们使用【Conv2D()函数从 Keras 构建第一个卷积层。

*classifier = Sequential()
classifier.add(Conv2D(32, (3, 3), input_shape = (64, 64, 3), activation = 'relu'))*

注意,特征检测器的数量设置为 32,其维数为(3,3)。在大多数 CNN 架构中,通常的做法是从 32 个特征检测器开始,如果需要,增加到 64 或 128 个。**

input_shape 是我们通过卷积对其应用特征检测器的输入图像的形状。问题是图像可能有不同的格式和图像大小。所以,我们需要将图像转换成相同的格式和固定的大小。我们稍后将处理图像,现在让我们设置它为 (64,64,3)。这里,3 是彩色图像的通道数,(64,64)是每个通道的图像维数,这足以获得良好的精度。如果你在一个像样的 GPU 上工作,请随意尝试(128,128)。

最后一个参数是激活函数。我们使用 ReLU 去除特征图中的任何负像素值。这是因为根据卷积中使用的参数,我们可能会在特征图中获得负像素。移除负像素增加了非线性分类问题的非线性。

3.2 最大池化

最大池化是通过滑动表格,例如(2,2),并取表格中的最大值,来减小特征图的大小。如果我们在(5,5)的 1 个特征映射上滑动一个跨度为 2 的表,我们会得到一个大小为(3,3)的特征映射,如图 5 所示。

图 5 最大汇集图(作者创建的 Img)

在每个要素地图上重复最大池化会产生一个池化图层。从根本上说,max pooling 是为了减少完全连接的图层中的节点数量,而不会丢失图像中的关键特征和空间结构信息。具体来说,我们使用MaxPooling2D()函数来添加池层。一般来说,我们使用 2x2 过滤器进行合并。

***classifier.add(MaxPooling2D(pool_size = (2, 2)))***

3.3 扁*化

如图 7 所示,展*是将所有汇集的特征地图作为完全连接的图层的输入,放入单个矢量中。

图 7 展*图(作者创建的 Img)

现在问题* : 为什么不直接将输入图像展*成单一矢量🤔?答案是这样做只会保留图像的像素值,而不会保留空间结构。换句话说,它将丢失每个像素与其周围像素的空间连接方式。但是用卷积,我们得到很多特征图,每个特征图代表图像的一个特定特征。因此,展*向量中的每个节点将表示输入图像的特定细节。***

具体来说,

**classifier.add(Flatten())**

3.4 完全连接

通过以上操作,我们将图像转换为一维向量。现在我们将使用这个向量作为输入层来构建一个分类器。首先,创建一个隐藏层。 output_dim 是隐藏层的节点数。作为惯例,我们选择 128 开始,并使用 ReLU 作为激活函数。

**classifier.add(Dense(output_dim = 128, activation = ‘relu’))**

然后添加一个输出层。对于二元分类, output_dim 为 1,激活函数为 Sigmoid

**classifier.add(Dense(output_dim =1, activation = ‘sigmoid’))**

我们最终的模型结构如下所示🎉🎉。

图 8 CNN 架构(作者创建的 Img)

4。模型编译

添加完所有层后,让我们通过选择一个 SGD 算法、一个损失函数和性能指标来编译 CNN。我们使用二元交叉熵进行二元分类,使用分类交叉熵进行多重分类问题。

**classifier.compile(optimizer = ‘adam’, loss = ‘binary_crossentropy’, metrics =’accuracy’)**

5。 模型拟合

这里我们有 8000 幅图像用于训练集,这不足以避免过度拟合。因此,我们执行图像增强,如旋转、翻转或剪切,以增加图像的数量。它将训练图像分成几批,每批将对随机选择的图像应用随机图像变换,以创建更多不同的图像。

具体来说,我们将使用来自 Keras 官网的 flow_from_directory(目录) 方法加载图片并应用增强。这就是我们以特定方式构建数据文件夹的原因,这样就可以根据文件夹名称来识别每个图像的类别。下面的代码片段允许我们放大图像,并适合和测试 CNN。

**train_datagen = ImageDataGenerator(rescale=1./255, 
     shear_range=0.2, zoom_range=0.2, horizontal_flip=True)test_datagen = ImageDataGenerator(rescale=1./255)train_set = train_datagen.flow_from_directory(‘dataset/training_set’, target_size=(64, 64), batch_size=32, class_mode=’binary’)test_set = 
test_datagen.flow_from_directory(‘dataset/test_set’, target_size=(64, 64), batch_size=32, class_mode=’binary’)classifier.fit_generator(train_set, steps_per_epoch=8000/32, epochs=25, validation_data=test_set, validation_steps=2000/32)**

上面,目标尺寸设置为(64,64),与我们创建卷积层时的形状相同。现在,让我们来拟合和测试模型。

最终,我们获得了 86% 的训练精度和 76% 的测试精度,✨✨.有轻微的过拟合

6。型号改进

显然,还有提高精度和减少过拟合的空间。有两种选择,要么增加更多的卷积层,要么增加更多的密集层。我们再加一个卷积层。

**classifier.add(Conv2D(32, 3, 3, activation = ‘relu’))
classifier.add(MaxPooling2D(pool_size = (2, 2)))**

根据上面的内容,再次在训练集和测试集上运行模型。最终,我们获得了提高后的测试精度 91% 和测试精度 82% 🧨🧨

7.示例预测****

首先,我们为要预测的图像创建一个文件夹' single_prediction ,如图 9 所示。

图 9 待预测图像

其次,我们使用中的图像模块来加载测试图像。注意,将图像的 target_size 设置为(64,64)。

**import numpy as np
from keras.preprocessing import imagetest_image = image.load_img(‘dataset/single_prediction/cat_or_dog_1.jpg’, target_size = (64, 64))**

记住 CNN 需要一个三维的输入图像。因此,我们需要为通道添加一个维度,从 2D 阵列到 3D 阵列。

**test_image = image.img_to_array(test_image)**

然而,这还不够,因为 CNN 期望第有另一个维度。Axis 用于指定我们正在添加的维度的位置。因此在索引 0 处添加了批次维度。

**test_image = np.expand_dims(test_image, axis = 0)**

太好了,该预测了。

**result = classifier.predict(test_image)**

我们得到的结果是 1。要了解动物及其相关数值之间的映射,我们使用:

**training_set.class_indices**

这样,我们知道 0 是猫,1 是狗。太好了。我们 CNN 做了一个正确的预测!

太好了!如果你觉得这篇文章有帮助,请随意点击👏s!如果需要源代码,请访问我的 Github 页面🤞🤞。

用于逆向工程的 CNN:一种功能识别的方法

原文:https://towardsdatascience.com/cnn-for-reverse-engineering-an-approach-for-function-identification-1c6af88bca43?source=collection_archive---------19-----------------------

为什么 CNN 对函数识别有用,如何实现

在将二进制文件部署到第三方环境之前,通常会删除二进制文件正常运行所不需要的任何信息。这样做是为了使二进制文件的逆向工程更加困难。从二进制文件中删除的一些信息是每个函数的边界。对于想对二进制文件进行逆向工程的人来说,这些信息非常有用。

函数识别是逆向工程领域中的一项任务,其中给定一个编译后的二进制文件,就应该确定每个函数边界的地址。函数的边界是函数的起始地址和结束地址。

为什么是神经网络?

  • 识别边界没有简单的规则,尤其是对于在编译期间已经优化的二进制文件。
  • 大量的数据——在互联网上很容易找到要编译的代码或已经用调试信息编译过的二进制文件来创建我们的数据集。
  • 几乎不需要领域知识!神经网络(尤其是深度神经网络)的一大优势是它们能够很好地处理原始数据,并且不需要特征提取。

使用神经网络进行功能识别的想法并不新鲜。它首先在 Eui 等人撰写的名为用神经网络识别二进制函数的论文中介绍。作者使用双向 RNN 来学习函数边界。根据他们的论文,他们不仅取得了类似或更好的结果,而且将计算时间从 587 小时减少到 80 小时。我认为这项研究真正展示了神经网络的力量。

那么为什么是 CNN 呢?(CNN vs RNN)

CNN(卷积神经网络)在关于计算机视觉的任务中非常流行。原因之一是 CNN 只能捕捉到的地方特色

局部特征描述输入面片(输入中的关键点)。例如,对于图像,它可以是关于图像中特定区域的任何特征,例如点或边缘。

全局要素是从整体上描述输入的要素。

另一方面,RNN 是一个“更强”的模式,因为它可以学习 T4 的本地和全球特色。

但是更强并不总是更好。对于只需要学习局部特征的任务,使用能够学习局部和全局特征的模型可能会导致过度拟合并增加训练时间。

对于函数标识,对于二进制中的每个字节,只需查看它前面的 10 个字节和后面的 10 个字节,就可以确定它是函数的开始还是结束。这一特性使得 CNN 似乎应该为这一任务取得好的结果。
也就是说,有一些全局特征可以帮助确定函数的边界。例如,call操作码可以帮助我们确定一个函数的开始。然而,即使是 RNN 也将很难了解这些特征,因为 RNN 在长序列上表现不佳(这就是为什么在 Eui 等人的论文中,他们用来自二进制而不是整个二进制的随机 1000 字节序列来训练他们的网络)。

此外,与顺序模型的 RNN 不同,CNN 可以并行运行,这意味着网络的训练和测试都应该更快。

我们介绍完了。我们来编码吧!

密码

代码使用 PyTorch 库在 Python3.6 中实现。

为了简单起见,我们将实现一个模型来标识每个函数的开始,但是同样的代码也可以用于标识结束。

完整代码可从以下网址获得:

[## 单独/功能识别

通过在 GitHub 上创建一个帐户,为独立/功能识别开发做出贡献。

github.com](https://github.com/alonstern/function-identification?)

数据

我们将使用 Eui 等人在论文中使用的相同数据集。
该数据集最初是为一篇名为 ByteWeight:学习识别二进制代码中的函数的论文创建的。Eui 等人使用相同的数据集将他们的结果与原始论文中报告的结果进行了比较。

数据集可在http://security.ece.cmu.edu/byteweight
获得。数据集由一组编译有调试信息的二进制文件组成。

我们将使用 elf_32 数据集,但是相同的代码也可以应用于 elf_64 数据集(和 PE_dataset,但是需要不同的调试信息解析过程)。

可以通过运行以下命令来下载数据集:

wget — recursive — no-parent — reject html,signature [http://security.ece.cmu.edu/byteweight/elf_32](http://security.ece.cmu.edu/byteweight/elf_32/)

预处理数据

首先,我们需要从每个二进制文件中提取它的代码段和函数地址。

Elf 文件由部分组成。每个部分包含不同的信息。我们感兴趣的部分是.text部分和.symtab部分。

  • .text包含运行二进制文件时执行的操作码。
  • .symtab包含关于二进制文件中函数的信息(以及更多)。

请注意,.symtab部分中的信息可以从二进制文件中剥离出来。这个项目对于那些情况是有用的。

为了解析二进制文件中的部分,我们将使用 Pyelftools 库。

首先,让我们提取.text部分的数据

对于二进制代码中的每个字节,我们需要提取它是否是函数的开始。

现在让我们迭代数据集中的二进制文件。
我们将使用 tqdm 库为我们的预处理制作一个漂亮的进度条,不费吹灰之力!

太好了!我们有自己的数据和标签。

要将数据输入到模型中,我们不应该只是一个文件一个文件地输入。相反,我们应该确定每次训练模型所需的数据大小,并将我们的数据分割成具有该大小的块。

此外,如果我们希望 CNN 输出一个大小为tags的向量,我们需要根据 CNN 内核大小填充输入。

让我们把它总结在一个torch.utils.data.Dataset类下:

构建模型

模型的输入将是一个向量,其中每个值都在 0 到 257 之间(0–255 表示字节值,256 表示文件的开始,257 表示文件的结束)。
模型的输出将进入一个矩阵,其中每一行包含两个值——一个字节作为函数开始的概率和一个字节不作为函数开始的概率(这些值的总和为 1)。

因为每个字节值代表一个不同的符号,我们想把每个值转换成一个向量。这样做的方法是使用嵌入层。

嵌入指南:

[## 解释了神经网络嵌入

深度学习如何将战争与和*表现为一个向量

towardsdatascience.com](/neural-network-embeddings-explained-4d028e6f0526)

在嵌入层之后,我们准备添加具有 Relu 激活功能的卷积层。
注意,我们希望卷积对整个字节起作用,因此内核大小应该是:我们要查看的字节数 X 每个字节的大小(嵌入层的输出维度)。

现在我们添加一个具有 softmax 激活功能的全连接输出层。

整个架构:

这就是整个模型!

训练和测试模型

首先,我们需要拆分数据进行训练和测试。我们将使用 90%的数据进行训练,10%的数据进行测试。

现在,为了创建模型,我们可以简单地实例化CNNModel

在培训中,我们将使用负对数似然损失函数和 Adam optimizer。同样,我们为我们的培训添加了一个漂亮的进度条 tqdm。

对于测试,我们看四个参数。准确度、精确度、召回率和 f 分数。

由于大部分字节不是函数的开始,因此仅用精确度不足以衡量该任务的性能。因此,即使是一个将所有事情都归类为“不是函数的开始”的模型也会获得很高的准确性。

结果

我在配备英特尔酷睿 i7–7500 u CPU、2.70GHz 和 16 GB 内存的个人笔记本电脑上对该模型进行了培训和测试。

定时性能

整个数据的预处理耗时 43 秒。
在 90%的数据集上训练模型耗时 33 分 43 秒。
其余 10%用 24 秒检测模型。

Eui 等在他们的论文中报道,每种模型的训练时间为 2 小时。我们的训练花了将*四分之一的时间!

预测性能

测试集的结果:

准确度:99.9981%
精密度:99.6905%
召回:99.4613%
f1-评分:99.5758%

Eui 等人论文中报告的对函数起始地址进行分类的最佳模型获得了 99.24%的 f1 分数。

就是这样,一个有线电视新闻网,它可以在二进制文件中找到每个函数的开始。这是我写的第一篇媒体文章。
希望你喜欢!如需更多信息/问题,请随时与我联系。

感谢您的阅读!

[1] E. C. R. Shin,D. Song 和 R. Moazzezi。用神经网络识别二进制文件中的函数(2015)。第 24 届 USENIX 安全研讨会论文集。

[2] T. Bao,J. Burket,M. Woo,R. Turner,D. Brumley .BYTEWEIGHT:学习识别二进制代码中的函数(2014)。第 23 届 USENIX 安全研讨会论文集。

CNN-LSTM:预测每日酒店取消

原文:https://towardsdatascience.com/cnn-lstm-predicting-daily-hotel-cancellations-e1c75697f124?source=collection_archive---------9-----------------------

用 LSTM 和 CNN 进行时间序列预测

来源:图片由 GDJ 从 Pixabay 拍摄

背景:LSTMs 与 CNN

LSTM(长短期记忆网络)是一种递归神经网络,允许对时间序列中的顺序依赖性进行核算。

假设给定时间序列中的观察值之间存在相关性(一种称为自相关的现象),标准神经网络会将所有观察值视为独立的,这是错误的,会产生误导性结果。

卷积神经网络是一种应用称为 卷积 的过程来确定两个函数之间的关系的网络。例如,给定两个函数 f ag,卷积积分表示一个函数的形状如何被另一个函数修改。这种网络传统上用于图像分类,并且不像递归神经网络那样考虑顺序依赖性。

然而,使其适合预测时间序列的 CNN 的主要优势是 扩张卷积——或使用过滤器计算每个细胞之间扩张的能力。也就是说,每个细胞之间的空间大小,这反过来允许神经网络更好地理解时间序列中不同观察值之间的关系。

因此,在预测时间序列时,LSTM 层和 CNN 层经常会结合在一起。这允许 LSTM 层考虑时间序列中的顺序依赖性,而 CNN 层通过使用扩张卷积进一步通知该过程。

也就是说,独立的 CNN 正越来越多地用于时间序列预测,几个 Conv1D 层的组合实际上可以产生非常令人印象深刻的结果-与同时使用 CNN 和 LSTM 层的模型相媲美。

这怎么可能呢?让我们来了解一下!

下面的例子是使用来自 Udacity 的深度学习课程 TensorFlow 简介中的 CNN 模板设计的——这个特定的主题可以在 Aurélien Géron 的第 8 课:时间序列预测中找到。

我们的时间序列问题

以下分析基于来自 Antonio、Almeida 和 Nunes (2019)的数据:酒店预订需求数据集。

想象一下这个场景。一家酒店很难预测每天的酒店预订取消情况。这给预测收入和有效分配酒店房间带来了困难。

该酒店希望通过构建一个时间序列模型来解决这个问题,该模型可以以相当高的准确度预测酒店每日取消预订的波动。

以下是每日酒店取消预订量波动的时间序列图:

来源:Jupyter 笔记本输出

模型配置

神经网络的结构如下:

来源:图片由作者创建

以下是必须考虑的重要模型参数。

内核大小

内核大小设置为 3,这意味着每个输出都是基于前面的三个时间步长计算的。

下面是一个粗略的例子:

来源:图片由作者创作。采用自 Udacity 的模板—深度学习 TensorFlow 简介:时间序列预测

设置正确的内核大小是一个实验问题,因为低内核大小会降低模型性能,而高内核大小会有过度拟合的风险。

从图中可以看出,采用了三个输入时间步长来产生单独的输出。

填料

在这种情况下,使用因果填充以确保输出序列具有与输入序列相同的长度。换句话说,这确保了网络从序列的左侧“填充”时间步长,以确保序列右侧的未来值不会用于生成预测-这显然会导致错误的结果,最终我们会高估模型的准确性。

大步

步长设置为 1,这意味着在预测未来值时,过滤器一次向前滑动一个时间步长。

然而,这可以设置得更高。例如,将步长设置为 2 意味着输出序列的长度大约是输入序列的一半。

较长的步长意味着模型在生成预测时可能会丢弃有价值的数据,但在捕捉长期趋势和消除序列中的噪声时,增加步长会很有用。

以下是模型配置:

model = keras.models.Sequential([
  keras.layers.Conv1D(filters=32, kernel_size=3,
                      strides=1, padding="causal",
                      activation="relu",
                      input_shape=[None, 1]),
  keras.layers.LSTM(32, return_sequences=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200)
])
lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-8 * 10**(epoch / 20))
optimizer = keras.optimizers.SGD(lr=1e-8, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])

结果

首先,让我们使用上述模型对不同的窗口大小进行预测。

重要的是,窗口大小要足够大,以考虑跨时间步长的波动性。

假设我们从窗口大小为 5 开始。

window_size = 5

培训损失如下:

plt.semilogx(history.history["lr"], history.history["loss"])
plt.axis([1e-8, 1e-4, 0, 30])

来源:Jupyter 笔记本输出

以下是预测值与实际每日取消值的对比图:

rnn_forecast = model_forecast(model, series[:,  np.newaxis], window_size)
rnn_forecast = rnn_forecast[split_time - window_size:-1, -1, 0]
plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, rnn_forecast)

来源:Jupyter 笔记本输出

*均绝对误差计算如下:

>>> keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()
9.113908

整个验证集的*均值为 19.89,模型的准确性是合理的。然而,我们确实从上图中看到,该模型在预测更多极值方面存在不足。

窗口大小= 30

如果窗口大小增加到 30 会怎样?

*均绝对误差略有下降:

>>> keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()
7.377962

如前所述,如果我们希望*滑预测,可以将步长设置得更高—注意,这样的预测(输出序列)将比输入序列具有更少的数据点。

无 LSTM 层预测

与 LSTM 不同,CNN 不是递归的,这意味着它不保留以前的时间序列模式的记忆。相反,它只能根据模型在特定时间步长输入的数据进行训练。

然而,通过将几个 Conv1D 层堆叠在一起,卷积神经网络实际上可以有效地学习时间序列中的长期相关性。

这可以使用 WaveNet 架构来完成。本质上,这意味着该模型将每一层定义为 1D 卷积层,步长为 1,核大小为 2。第二个卷积层使用的膨胀率为 2,这意味着系列中的每第二个输入时间步长都会被跳过。第三层使用的膨胀率为 4,第四层使用的膨胀率为 8,依此类推。

其原因是,它允许较低层学习时间序列中的短期模式,而较高层学习较长期的模式。

波网模型定义如下:

model = keras.models.Sequential()
model.add(keras.layers.InputLayer(input_shape=[None, 1]))
for dilation_rate in (1, 2, 4, 8, 16, 32):
    model.add(
      keras.layers.Conv1D(filters=32,
                          kernel_size=2,
                          strides=1,
                          dilation_rate=dilation_rate,
                          padding="causal",
                          activation="relu")
    )
model.add(keras.layers.Conv1D(filters=1, kernel_size=1))
optimizer = keras.optimizers.Adam(lr=3e-4)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])model_checkpoint = keras.callbacks.ModelCheckpoint(
    "my_checkpoint.h6", save_best_only=True)
early_stopping = keras.callbacks.EarlyStopping(patience=50)
history = model.fit(train_set, epochs=500,
                    validation_data=valid_set,
                    callbacks=[early_stopping, model_checkpoint])

窗口大小 64 用于训练模型。在这种情况下,我们使用比 CNN-LSTM 模型更大的窗口大小,以确保 CNN 模型拾取更长期的相关性。

注意 提前停止 是在训练神经网络时使用的。这样做的目的是确保神经网络在进一步训练会导致过度拟合的点上停止训练。手动确定这是一个相当随意的过程,因此尽早停止会对此有很大帮助。

现在,让我们使用刚刚构建的独立 CNN 模型来生成预测。

cnn_forecast = model_forecast(model, series[..., np.newaxis], window_size)
cnn_forecast = cnn_forecast[split_time - window_size:-1, -1, 0]

这是预测数据与实际数据的对比图。

来源:Jupyter 笔记本输出

*均绝对误差略高,为 7.49。

注意,对于这两个模型, Huber 损失被用作损失函数。这种类型的损失往往对异常值更稳健,因为它对较小的误差是二次的,对较大的误差是线性的。

这种类型的损失适合这种情况,因为我们可以看到数据中存在一些异常值。使用 MSE(均方误差)会过度夸大模型产生的预测误差,而 MAE 本身可能会低估误差的大小,因为它没有考虑这些异常值。Huber 损失函数的使用允许一个满意的中间值。

>>> keras.metrics.mean_absolute_error(x_valid, cnn_forecast).numpy()
7.490844

即使具有稍高的 MAE,CNN 模型在预测每日酒店取消方面表现得相当好,而不必为了学习长期依赖性而与 LSTM 层结合。

限制

当然,任何模式都有局限性,CNN 也不例外。

假设模型正在进行单步预测,这意味着需要相关窗口中直到时间 t 的所有观察来预测时间 t+1 的值。在这方面,一步到位的 CNN 不能用来做长期预测。虽然多步 CNN 可以用于此目的,但是这种模型是否优于 ARIMA 模型是有争议的。

此外,CNN(或任何神经网络模型)需要大量数据来有效训练。虽然许多时间序列模型都带有预先构建的参数来模拟一系列时间序列,但 CNN 完全是从零开始学习。在这方面,研究人员可能会花费大量时间来配置 CNN 模型,并最终获得相对于其他标准时间序列模型而言较低的精度,或者仅获得与所需的额外训练时间和数据资源不相称的边际精度。

在此特定示例中,使用 80%的数据作为训练数据来训练模型,然后根据 20%的验证数据来筛选预测准确性。但是,对模型是否有效的真正测试包括将预测准确性与测试数据或模型完全看不到的数据进行比较。这里没有这样做,因为本文只讨论了模型训练组件。

然而,神经网络很容易过度拟合——即模型在它以前见过的数据上表现良好——而在预测看不见的数据上表现不佳。

结论

在本例中,我们看到:

  • CNN 和 LSTMs 在时间序列预测中的异同
  • 膨胀卷积如何帮助 CNN 预测时间序列
  • 用 CNN 预测时间序列时核大小、填充和步长的修正
  • 使用 WaveNet 架构使用独立 CNN 层进行时间序列预报

具体来说,我们看到了通过使用膨胀,与 CNN-LSTM 模型相比,CNN 如何能够产生同样强的结果。

非常感谢您的宝贵时间,非常感谢您的任何问题、建议或反馈。

如前所述,这个主题在 Udacity 课程的深度学习课程 TensorFlow 简介中也有涉及,我强烈推荐关于时间序列预测的章节,以了解关于这个主题的更多详细信息。通常免责声明适用—这只是个人建议,我与本课程的作者没有任何关系。

你也可以在这里找到我用来运行酒店取消这个例子的全部 Jupyter 笔记本。

原 Jupyter 笔记本(版权 2018,TensorFlow 作者)也可以在这里找到。

免责声明:本文是在“原样”的基础上编写的,没有任何担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。这些发现和解释是作者的,不被本文中提到的任何第三方认可或附属于任何第三方。

CNN 迁移学习和微调

原文:https://towardsdatascience.com/cnn-transfer-learning-fine-tuning-9f3e7c5806b2?source=collection_archive---------0-----------------------

了解如何应用这些强大的技术,将您的深度学习模型提升到一个全新的水*!

图片来自 Unsplash

介绍

正如我们在之前的文章中看到的,我们可以使用研究团队开发的架构,并利用他们的力量进行预测,并在我们的深度学习模型中获得更好的结果。

训练一个神经网络需要时间,幸运的是现在有一些方法可以避免:

  • 定义神经网络的架构
  • 从一开始就训练她

我们已经看到了避免定义架构的方法,这里是,它包括使用已知工作良好的预定义架构:ResNet、AlexNet、VGG、Inception、DenseNet 等。

那如何避免从头开始训练它呢?我这么说是什么意思?

神经网络用随机权重初始化(通常),在一系列时期后达到一些值,允许我们正确地分类我们的输入图像。

如果我们可以将这些权重初始化为我们事先知道的某些值,这些值已经可以很好地对某个数据集进行分类,会发生什么?

通过这种方式,我们不需要像我们从零开始训练网络一样大的数据集(从几十万甚至几百万的图像,我们可以到几千个),也不需要等待大量的历元来获得分类的好值,由于它们的初始化,它们会更容易。

让我们探索如何利用迁移学习和微调技术来实现这一点:

迁移学习

我们以在 ImageNet 数据集上训练的 VGG16 网络为例。让我们看看它的架构:

按作者分列的数字

我们知道,ImageNet 由大约 120 万幅图像组成的数据集用于训练,5 万幅用于验证,10 万幅用于测试,属于 1000 个类别。

现在假设我们想要将 ImageNet 上训练的 VGG16 应用到另一个数据集,假设我们选择了 CIFAR-10 。我们怎么做呢?

记住 CNN 的一般方案,我们在第一阶段有一个特征提取器,然后是一个分类器:

按作者分列的数字

如果我们删除 VGG16 的最后一层,它只是为 ImageNet 中的 1000 个类中的每一个类取一个概率,并用一个取 10 个概率的层来替换它,会怎么样?这样,我们可以利用 VGG16 在 ImageNet 上训练的所有知识,并将其应用于我们的问题!

正如我们所看到的,我们要做的是改变分类阶段,以便最后一层是 10 个神经元之一(我们的 CIFAR 10 有 10 个类),然后我们将重新训练网络,允许完全连接的层的权重被改变,即分类阶段。

为此,我们将使用来自 ImageNet 的权重初始化我们的网络,然后冻结所有卷积层和最大池层,以便它们不会修改自己的权重,只留下完全连接的层空闲。

一旦完成,我们将开始重新训练。通过这种方式,我们设法利用我们网络的特征提取阶段,并且只调整最终的分类器来更好地与我们的数据集一起工作。这就是所谓的迁移学习,因为我们利用另一个问题的知识来解决我们正在处理的问题。

这种方法也可以通过保存最大池最后一层给出的特征,然后将该数据放入任何分类器(SVM、logreg 等)来实现。

让我们看看我们该如何做:

Keras 实施

**# We first load the necessary libraries, the dataset and reshape its dimensons to the minimum allowed by the VGG16 --> (48,48,3)**
import tensorflow as tf
from keras import callbacks
from keras import optimizers
from keras.engine import Model
from keras.layers import Dropout, Flatten, Dense
from keras.optimizers import Adam
from keras.applications import VGG16
from keras.datasets import cifar10
from keras.utils import to_categorical
import numpy as npinput_shape = (48, 48, 3)(X_train, y_train), (X_test, y_test) = cifar10.load_data()
Y_train = to_categorical(y_train)
Y_test = to_categorical(y_test)**# resize train set**
X_train_resized = []
for img in X_train:
  X_train_resized.append(np.resize(img, input_shape) / 255)

X_train_resized = np.array(X_train_resized)
print(X_train_resized.shape)**# resize test set**
X_test_resized = []
for img in X_test:
  X_test_resized.append(np.resize(img, input_shape) / 255)

X_test_resized = np.array(X_test_resized)
print(X_test_resized.shape)

**# We build the base model**
base_model = VGG16(weights='imagenet', include_top=False, input_shape=input_shape)
base_model.summary()

**# We freeze every layer in our base model so that they do not train, we want that our feature extractor stays as before --> transfer learning**
for layer in base_model.layers: 
  layer.trainable = False
  print('Layer ' + layer.name + ' frozen.')**# We take the last layer of our the model and add it to our classifier**
last = base_model.layers[-1].output
x = Flatten()(last)
x = Dense(1000, activation='relu', name='fc1')(x)
x = Dropout(0.3)(x)
x = Dense(10, activation='softmax', name='predictions')(x)
model = Model(base_model.input, x)**# We compile the model**
model.compile(optimizer=Adam(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy'])**-**
model.summary()

**# We start the training**
epochs = 10
batch_size = 256**# We train it**
model.fit(X_train_resized, Y_train,
          batch_size=batch_size,
          validation_data=(X_test_resized, Y_test),
          epochs=epochs)

**# We evaluate the accuracy and the loss in the test set**
scores = model.evaluate(X_test_resized, Y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

我们根本不需要训练,而且我们取得了不错的成绩!请记住,如果我们随机进行,正确的概率将是 1/10=0.1 或 10%,因为我们有 10 个类。

最初训练网络的数据集和我们的问题的数据集越相似,我们得到的结果就越好。

如果我们的数据集与 ImageNet 的无关,或者我们想进一步改善结果呢?

为此,我们使用微调。

微调

通过微调,我们首先改变最后一层以匹配我们数据集中的类,就像我们之前对迁移学习所做的那样。但是除此之外,我们也重新训练我们想要的网络层次。

请记住 VGG16 的架构:

按作者分列的数字

我们在前面的示例中所做的只是改变分类阶段的层,保留网络在前面的任务中提取特征(模式)时获得的知识,我们从该任务中加载权重(ImageNet)。

通过微调,我们不局限于只重新训练分类器阶段(即完全连接的层),我们还将重新训练特征提取阶段,即卷积和汇集层。

重要的是要记住,在神经网络中,第一层检测更简单和更通用的模式,我们在体系结构中发展得越快,就越针对数据集,它们检测的模式就越复杂。

因此,我们可以允许卷积和池层的最后一个块被重新训练。

我什么时候做微调和迁移学习?我如何选择从哪一层重新培训?

一般来说,我们要做的第一件事是转移学习,也就是说,我们不会重新培训我们的网络。这将为我们提供一个必须克服的底线。然后,我们将只重新训练分类阶段,然后我们也可以尝试重新训练一些卷积块。

摘要

  • 进行迁移学习,即只修改最后一层,使其输出数量与我们的类(基线)相同
  • 尝试重新训练分类阶段,即密集层
  • 试图重新训练一些卷积阶段

大多数情况下,遵循这些步骤,你会得到适合你的问题的结果

这也取决于你遇到的问题的类型。如果:

  • 新的数据集很小,和原来的相似:微调的时候要小心,也许选择卷积阶段最后一层的特征,使用 SVM 或者线性分类器更好。
  • 新的数据集很大,与原来的相似:拥有更多数据我们可能不会过度适应,所以我们可以更有信心地进行微调。
  • 新的数据集很小,与原来的非常不同:最好使用卷积阶段早期层的特征,因为这将被设置为比后期层更通用的模式,然后使用线性分类器。
  • 新的数据集很大,与原来的大不相同:我们将从头开始训练它!但是,仍然建议您使用 ImageNet 的权重来初始化权重。

注意

使用这些技术时,您必须考虑预训练模型的可能限制。例如,它们可能需要最小的图像尺寸。

此外,当重新训练网络时,我们通常选择比从头开始更低的学习速率,因为我们从假设为好的权重的初始化开始。

微调 Keras 实现

**# Fine Tuning Example, classification VGG16 with CIFAR 10, we import the necessary libraries**
import tensorflow as tf
from keras import callbacks
from keras import optimizers
from keras.engine import Model
from keras.layers import Dropout, Flatten, Dense
from keras.optimizers import Adam
from keras.preprocessing.image import ImageDataGenerator
from keras.applications import VGG16
from keras.datasets import cifar10
from keras.utils import to_categorical
import numpy as np**# We first load the dataset and reshape its dimensions to the minimum allowed by VGG16 --> (48, 48, 3)**input_shape = (48, 48, 3)(X_train, y_train), (X_test, y_test) = cifar10.load_data()
Y_train = to_categorical(y_train)
Y_test = to_categorical(y_test)# resize train set
X_train_resized = []
for img in X_train:
  X_train_resized.append(np.resize(img, input_shape) / 255)

X_train_resized = np.array(X_train_resized)
print(X_train_resized.shape)# resize test set
X_test_resized = []
for img in X_test:
  X_test_resized.append(np.resize(img, input_shape) / 255)

X_test_resized = np.array(X_test_resized)
print(X_test_resized.shape)

**# We build the base model** base_model = VGG16(weights='imagenet', include_top=False, input_shape=input_shape)
base_model.summary()

**# We allow to the last convolutional andthe classification stages  to train**
for layer in base_model.layers:
  if layer.name == 'block5_conv1':
    break
  layer.trainable = False
  print('Layer ' + layer.name + ' frozen.')**# We add our classificator (top_model) to the last layer of the model**
last = base_model.layers[-1].output
x = Flatten()(last)
x = Dense(1000, activation='relu', name='fc1')(x)
x = Dropout(0.3)(x)
x = Dense(10, activation='softmax', name='predictions')(x)
model = Model(base_model.input, x)**# We compile the model**
model.compile(optimizer=Adam(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy'])**# We see the new structure of the model**
model.summary()

**# We start the training**
epochs = 15
batch_size = 256**# We train the model**
model.fit(X_train_resized, Y_train,
          batch_size=batch_size,
          validation_data=(X_test_resized, Y_test),
          epochs=epochs)

**# We evaluate the accuracy and the loss in the test set**
scores = model.evaluate(X_test_resized, Y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

因此,当我们面临深度学习问题时,我们应该总是使用微调并建立一个基线模型,我们稍后会尝试改进它。

最后的话

一如既往,我希望你喜欢这篇文章,并且你获得了关于如何实现和开发具有迁移学习和微调的卷积神经网络的直觉!

如果你喜欢这篇文章,那么你可以看看我关于数据科学和机器学习的其他文章 这里

如果你想了解更多关于机器学习、数据科学和人工智能的知识 请在 Medium 上关注我,敬请关注我的下一篇帖子!

CNN vs. Prophet:预测铜生产者价格指数

原文:https://towardsdatascience.com/cnn-vs-prophet-forecasting-the-copper-producer-price-index-af4da63bd93d?source=collection_archive---------44-----------------------

哪个模型在预测铜价方面做得更好?

来源:图片由 3844328 发自 Pixabay

免责声明:本文是在“原样”的基础上编写的,没有担保。它旨在提供数据科学概念的概述,不应被解释为投资建议或任何其他类型的专业建议。

在之前关于数据科学的文章中,我试图使用 Prophet 模型来预测铜生产者价格指数。数据来源于弗雷德经济数据使用 Quandl。

以下是数据图(请注意,在本练习中,所有价格均以对数表示):

资料来源:弗雷德经济数据

所讨论的时间序列从 1985 年 9 月到 2020 年 7 月,*均绝对误差为 0.257(相比之下,整个测试集的*均值为 5.917)。

以下是预测值与实际值的对比图:

来源:Jupyter 笔记本输出

虽然 Prophet 模型在检测季节性和趋势成分方面很有效,并且还提供了通过适当修改变化点来改进预测的能力,但在捕捉一个时间段到下一个时间段的波动性时,这种模型不一定做得特别好。

在这方面,我决定使用卷积神经网络(以下简称为 CNN)模型对这些数据进行建模,以研究该模型是否能更有效地预测这一时间序列。

作为一个警告,下面的例子更多的是一个学术练习,而不是预测资产价格的真实尝试。鉴于我们正在处理月度数据,神经网络更容易捕捉到不同时间段的波动。

在现实生活场景中,时间序列是在更短的时间间隔(小时、分钟甚至秒)内预测的,数据集中存在更多的随机性(或随机性)。这可能会显著影响预测的准确性。此外,多变量时间序列可能证明在更普遍地预测经济时间序列方面更有效——假设这种时间序列受到许多干预。

然而,使用 CNN 来模拟月度数据将被用作这一目的的起点。

请注意,下面的示例使用了来自 Udacity 的深度学习课程 TensorFlow 简介中的模型模板——这一特定主题可在 Aurélien Géron 的第 8 课:时间序列预测中找到。

此外,原 Jupyter 笔记本(版权 2018,TensorFlow 作者)也可以在这里找到。

CNN 模型的背景

我之前在我的上一篇文章中详细阐述了 CNN 模型的构建模块,标题是“CNN-LSTM:预测每天的酒店取消量”。因此,我在此不再赘述,但可以说,CNN 通过使用先前的时间步长(基于特定的窗口大小)来产生数据点输出,如下所示:

来源:图片由作者创作。采用自 Udacity 的模板—深度学习 TensorFlow 简介:时间序列预测

CNN 具有通过 WaveNet 架构学习时间序列中短期和长期相关性的内在能力,其中几个 Conv1D(一维卷积)层堆叠在一起。这允许较低层学习短期依赖性,而较高层学习长期依赖性。在我们试图预测的时间序列的背景下,这个模型似乎适合这个目的。

模型配置

对数据执行 80/20 分割,时间序列中的前 334 个数据点用于训练 CNN 模型,其余的点用于验证目的。

split_time = 334
time_train = time[:split_time]
x_train = series[:split_time]
time_valid = time[split_time:]
x_valid = series[split_time:]

选择窗口大小为 64,批量大小为 128,训练超过 500 个时期。使用 Huber 损失作为损失函数,以确保模型精度读数不受异常值的影响。

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)window_size = 64
train_set = seq2seq_window_dataset(x_train, window_size,
                                   batch_size=128)
valid_set = seq2seq_window_dataset(x_valid, window_size,
                                   batch_size=128)model = keras.models.Sequential()
model.add(keras.layers.InputLayer(input_shape=[None, 1]))
for dilation_rate in (1, 2, 4, 8, 16, 32):
    model.add(
      keras.layers.Conv1D(filters=32,
                          kernel_size=2,
                          strides=1,
                          dilation_rate=dilation_rate,
                          padding="causal",
                          activation="relu")
    )
model.add(keras.layers.Conv1D(filters=1, kernel_size=1))
optimizer = keras.optimizers.Adam(lr=3e-4)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])model_checkpoint = keras.callbacks.ModelCheckpoint(
    "my_checkpoint.h6", save_best_only=True)
early_stopping = keras.callbacks.EarlyStopping(patience=50)
history = model.fit(train_set, epochs=500,
                    validation_data=valid_set,
                    callbacks=[early_stopping, model_checkpoint])

请注意,在上述示例中,训练模型时使用了双倍膨胀率。如上所述,这样做的原因是为了让网络学习时间序列中的短期和长期模式。

cnn_forecast = model_forecast(model, series[..., np.newaxis], window_size)
cnn_forecast = cnn_forecast[split_time - window_size:-1, -1, 0]
plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, cnn_forecast)

来源:Jupyter 笔记本输出

*均绝对误差计算如下:

>>> keras.metrics.mean_absolute_error(x_valid, cnn_forecast).numpy()
0.027838342

先知

脸书的 Prophet 时间序列模型的一个有用特性是能够识别变点,或者时间序列中重大结构变化的时期。准确识别这些点反过来可以改进时间序列预测。

如上所述,先知模型的 MAE 值为 0.257 ,与 CNN 得出的结果相比,这个值要高得多。

12 在 Prophet 模型中定义了变化点(或趋势的显著偏差)。

pro_change= Prophet(n_changepoints=12)
forecast = pro_change.fit(train_dataset).predict(future)
fig= pro_change.plot(forecast);
a = add_changepoints_to_plot(fig.gca(), pro_change, forecast)future_data = pro_change.make_future_dataframe(periods=43, freq = 'm')

#forecast the data for future data
forecast_data = pro_change.predict(future_data)
pro_change.plot(forecast_data);

来源:Jupyter 笔记本输出

也就是说,让我们再次看看预测值与实际值的对比图:

来源:Jupyter 笔记本输出

显然,CNN 在预测铜价的月度波动方面表现得更好——尽管 Prophet 模型的 MAE 相对于*均值仍然较低——我们看到实际时间序列中的波动性明显更大,而预测的时间序列没有显示出来。

结论

如前所述,预测这样的时间序列确实存在固有的局限性,因为不知道 CNN 是否会在更短的时间内表现得更好,具有更大的随机性。此外,这种时间序列受到各种干预的影响,而这些干预是过去的值所不能解释的。

然而,在这种情况下,CNN 在捕捉铜价的月度波动方面做得相当好。

在本文中,我们介绍了:

  • 如何配置 CNN 来预测时间序列
  • CNN 和 Prophet 模型之间的差异
  • 这种模型在预测经济时间序列中的局限性

非常感谢您的宝贵时间,非常感谢您的任何问题或反馈。

免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为投资建议或任何其他类型的专业建议。

美国有线电视新闻网解释说:给予人工智能视觉

原文:https://towardsdatascience.com/cnns-explained-giving-sight-to-artificial-intelligence-304f252346bc?source=collection_archive---------33-----------------------

视觉——这很复杂

现在在房间里四处看看。你看到的一切,所有的颜色、阴影、你能认出的物体——所有这些都是可能的,因为我们人类可以 看到 。事实证明,我们很擅长这个。人类的视力清晰、专注、远视,是地球上最好的视力之一,只有像猎鹰这样的食肉鸟类才能与之匹敌。我们真的很擅长观察,这是因为能够准确快速地识别物体对我们来说极其重要——比如说草原上的那个小身影是另一个人还是一头饥饿的狮子。

照片由丹尼尔·库切列夫在 Unsplash 拍摄

然而,我们从来没有真正想过如何理解不断涌入我们眼睛的光子。简单地说,眼睛实际上不做任何事情。视网膜收集流入它的光线,并简单地将这些信息作为电化学信号传输到大脑。另一方面,大脑负责所有的工作。它处理这些信号,并理解眼睛实际看到的东西。你能从正在阅读的信件中分辨出一团光子的原因是因为你的大脑🧠.

计算机以类似的方式工作。这就是为什么你不能把一台电脑和一台相机连接起来,然后期望它知道它在看什么。计算机首先需要学习如何 理解 它看到的东西。为此,我们需要卷积神经网络或 CNN。

但首先,计算机实际上是如何看到图像的?

计算机看图像的方式与人类(或任何其他生物)不同。计算机将图像视为数字矩阵,每个数字代表一个特定像素的强度。因此,对于灰度图像,低数值意味着更多的黑色,高数值意味着更多的白色。

用数字矩阵表示的亚伯拉罕·林肯的简化图像

然而,如果只有黑和白,这个世界会很无聊,对吗?所以它变得更辣。 通过混合使用红色、绿色和蓝色,计算机可以创造出几乎所有人眼可以感知的颜色。因此,当计算机查看彩色图像或 RGB 图像时,这些图像不是以二维数字矩阵表示,而是以三维数字矩阵表示🤯,每种颜色一个通道:红色、绿色和蓝色。

计算机抽象艺术。嗯,是的…看看这些数字

CNN 中的“卷积”

因此,现在我们有了一种可以量化的方法来定义计算机图像,我们可以深入研究 CNN 实际上是如何工作的。细胞神经网络包括两个主要部分:特征学习和分类。

CNN 的一般形式

在特征学习中,卷积层用于从给定图像中提取特征。每个卷积层使用一个滤波器或内核来滑过输入图像。在每个位置,执行矩阵乘法,并将结果加到新的特征图上——卷积原始图像。新的特征图本质上告诉计算机内核中的特征也在图像的同一部分的可能性有多大。

卷积层在三个不同的 RGB 通道上传递三个不同的内核,以创建新的特征图

对于不同的过滤器,这种操作被反复重复,最终产生许多不同的特征图。然后将卷积层应用于已经卷积的特征地图。这个想法是,卷积层数越多,计算机可以识别的高级特征就越多。因此,如果网络接收到一张狗的图像,在最初几层,CNN 只会检测边缘,然后在下一层,可能是耳朵,然后是整个头部,最后它将能够对狗进行整体分类。

使用池层降低计算能力

虽然卷积神经网络的核心是卷积层,但它并不仅仅由它们组成。有了所有这些特征地图,并且必须在每个地图上滑动内核,网络在能够准确提取特征之前可能需要大量的计算能力。这就是为什么大多数 CNN 使用池层来减少这些特征地图的大小。

max-pooling 层将 4x4 图像缩小为 2x2 图像。

这些池图层获取输入影像的一部分,并获取这些部分的*均、最大或最小像素值,以创建更小的要素地图。由于这些地图更小,它们不需要太多的计算能力来处理。尽管如此,它们仍然准确地再现了原始图像。

人工神经网络分类

在特征学习中,每个卷积层之后是另一个池层,并且这被多次进行以从图像中提取所有必要的更高级特征。下一步包括将特征图发送到完全连接的层或人工神经网络(ANN)中。然而,这一层只能接受一维数据,正如我们之前所说,计算机将图像视为三维形状。

因此,CNN 获取特征地图,并将其展*成一维数字数组(实际上只是一个列表),然后将其发送到人工神经网络。人工神经网络中的神经元是完全连接的,这意味着它们都与前一层神经元的激活相关联。每个连接都有一个特定的权重,这个权重精确地决定了该连接会对下一个神经元产生多大的兴奋。

这些连接最终都汇聚到几个神经元,最兴奋的神经元作为 CNN 的输出给出——这是 CNN 对给定图像的分类。通过将 CNN 的给定答案与实际答案进行比较来训练 CNN,调整全连接层中的所有权重和偏差,以使输出更接*实际答案。这被重复数千次,直到该模型能够准确地分类,即使是新的图像。

CNN 的力量

卷积神经网络已被证明在理解图像方面极其有效和高效。CNN 现在被用于无数的应用中,从无人驾驶汽车到面部识别。他们分析和分类图像的能力甚至已经超过了人类,世界各地的人们越来越认识到人工智能的力量。酝酿了很长时间,但毫无疑问,现在人工智能终于睁开了眼睛。由我们来决定这是好是坏。

来源

协变:直观的解释!

原文:https://towardsdatascience.com/co-variance-an-intuitive-explanation-a67f6c80674d?source=collection_archive---------26-----------------------

一个全面而简单的指南,更侧重于公式背后的思想,而不是数学本身——开始用期望值、均值、方差构建模块,最终理解大图,即协方差

共方差计算的所有荣耀!

介绍

与普遍的看法相反,公式不仅仅是数学符号。它试图表达一种想法,这种想法隐藏在数学之下,除非你真正去寻找,否则是不明显的。这种表述的主要问题(正如我经常遇到的那样)是,过一段时间后,你会忘记这个公式。因此,这里是我试图解释一个话题,使它坚持与观众。在深入讨论之前,我将尝试解释一些先决话题。如果您已经熟悉它们,请随意跳过。如果没有,一起骑:)

期望和均值

让我们从一个相对简单的主题开始,这是理解协方差所需的一个基本块。概率论中,期望代表“一个 离散随机变量 的期望值,它是其所有可能值的的概率加权*均值,并形式化为,

对 X 的所有值及其概率 p_i 的期望

这里的“X”是一个变量,它可以有多种形式——“X _ I”,每种形式都有自己的出现概率“p_i”。请注意,期望值是一个变量在考虑概率时可以取的所有值的单一数字表示。要记住的一个特例是当所有‘p _ I’相等时,即所有值出现的概率相等。在这种情况下,期望值转换为*均值。举个例子,假设一个变量模拟一个无偏骰子的滚动,那么它可能取的值可以是 1 到 6。这些数字出现的概率也是相等的。回到概括上来,下面展示了期望值到*均值的转换,

期望意味着——如何使概率常数成为可能

请注意,由于所有变量值的权重相等,*均值与值本身成比例,因此它倾向于更密集的点。请参见下面的点(蓝点)分布模拟,以及它们位置的变化如何导致*均值(红点)本身的变化。

换分追贱!

也有各种各样的实际模拟,因为大多数时候它们不同于理论模拟。考虑掷骰子的例子,我们很容易地说它们有相等的概率,但是编程模拟可能显示一些变化。下面,我模拟了一万次不偏不倚的掷骰子。看骰子面的出现分布。

模拟一个无偏的骰子滚动一万次!

现在比较一下理论计算和实际计算的*均通知是有差别的,尽管很小,但在实际情况下这就行了。

比较理论和实际*均值计算

差异

观察下面的情节,你能发现其中的共同点吗?

换点然后…等一下!?

答案是——所有的都是一样的意思!但是他们看起来很不一样,对吧?它们之间有什么不同呢?似乎他们有不同的“传播”或“宽度”。方差基本上是对数据分布或宽度的度量。在统计学中,方差是 期望 的*方 偏差 中的一个的意思是 让我们试着用这个定义来解释我们对期望的理解,****

差异-公式的定义

就这样,我们有了方差公式!请注意,我们首先计算所有“X”值的*均值。然后我们找到分子,它是每个值与*均值之差的*方。需要正方形部分,因为我们不关心传播的方向,因此我们不希望相反方向的传播,即具有不同的极性,彼此抵消。有人可能会说,如果我们求*方来求分子,为什么不后来求*方根呢?而这种想法恰恰是用标准差来表示的。换句话说,方差是标准差的*方。考虑到这一点,让我们来看看与之前相同的图(现在是分离的和静态的),但是现在计算了方差和标准差。

变点,静态均值但是变方差和标准差!

现在我们已经准备好讨论主要话题,但在此之前,还有一个更有趣的方差推导。这不是理解协变所必需的,但好奇的读者可能无论如何都想看到它。它代表了一种思想,“变量的方差是变量的期望的*方减去期望本身的*方”。是下面推导出来的,

又一个有趣的方差推导!

现在,让我们计算之前掷骰子模拟的方差。同样,让我们用三种不同的方法计算相同的方差,一种方法代表懒惰 python 方法,剩下的两种方法代表我们讨论过的公式。

用 python 计算方差——很抱歉我的 python 一线性代码:)

请注意,方差与某个十进制值相同,微小的差异是由于浮点误差造成的。此外,在 Python 偏好方式 1 中,我编写了方式 2 和方式 3,只是为了展示我们讨论过的公式。

协方差

到目前为止,我们一次只研究一个变量,即我们的数据是 1D 或一维的。共方差是为高维数据定义的。因此,顾名思义,它不是只考虑一个变量,而是考虑多个(正好 2 个)变量并计算方差。在进一步讨论之前,我们先讨论一下数据。当我说 2D 时,我的意思是每个数据实例都由两个数字表示。在基本的*面几何中,我们知道两个数字可以与一个点相关联,因此每个实例由一个点表示。具有 10 个实例的样本数据及其在 2D *面上的可视化如下所示,

现在回到它,更正式的协变是“两个 随机变量 的联合变异性的度量”。这个想法是,如果两个变量遵循相同的增长行为,我们有很高的协方差。我的意思是,如果一个变量增加,另一个也会增加。在所有其他情况下,比如一个增加(或减少)而另一个减少(或增加),我们会有负的协方差。作为方差的扩展,我们可以将方差公式表示为:

方差到共方差

请注意微妙的第二行,它说同一个变量的协方差等于该变量的方差。后来我们所做的就是把分子第二部分中与 x 相关的项替换为 y,这样我们就有了协方差公式!还要注意,‘UX’和‘uy’分别是变量‘X’和‘Y’的*均值。

如果我们仔细观察分子,就会发现一个有趣的直觉。但是为了概括这一点,假设我们计算两个变量的*均值(‘UX’和‘uy’),并从 dateset 中选取任意一个点(‘x _ I’和‘y _ I’),并将它们都绘制为 2D *面中的点。然后,我们可以用这两个位置完全相反的点形成一个矩形。朝这个方向走会导致,

协方差是所有矩形区域的*均和

所以,它基本上代表了一个矩形的面积,绘制在*均值和数据点之间。因此,数据集的每个点将构成一个具有*均点的矩形。但是当我们为整个数据集绘制矩形并使用上面的公式找到它们的面积时,我们观察到一些矩形有负的面积!没什么好担心的,因为它只是展示了这个数据点(我们得到-ve 区域)的变量有不同的行为,即一个是高的,而另一个是低的,这违背了协变的思想。现在,当我们对所有数据点的矩形面积求和时,我们要做的是加上+ve 面积,减去-ve 面积。最后,*均后的结果值表示协方差的大小。下面显示了一个样本数据集的例子,其中我们展示了数据集的不同点以及由它们的面积形成的矩形。

由中点和其余点形成的矩形。负区域用红色表示,正区域用绿色表示。

让我们看看不同的数据集和形成的矩形。另请注意,图 1 代表面积最大的情况,因此协方差也最大(我们讨论了它们是如何成比例的)。如前所述,此处显示的行为是理想的,即当一个变量增加时,另一个变量也会增加。随着点开始偏离该直线行为,如随后的图中所示,红色矩形的数量增加,因此协变的幅度减小。

从图 1 到图 4 的面积减少,因为“两个变量应该显示相似的行为”的预期发生了变化

结论

当我们以一种易于理解的形式,如图表或绘图来表示公式时,它变得更容易理解,也更容易抓住隐藏的洞察力。例如,如果我们以矩形及其面积的形式表示协方差,我们可以快速回答这样的问题:哪个协方差更高,是指数衰减图还是增长图?或者说如果均值附*有很多+ve 点,但是远离均值有一个-ve 点会怎么样?(阅读,离群值)。试着用矩形和面积的形式来思考这些问题,答案很快就会出来。希望如此:)

参考

[1]https://en.wikipedia.org/wiki/Expected_value

[2]https://en.wikipedia.org/wiki/Standard_deviation

[3]https://en.wikipedia.org/wiki/Variance

[4]https://en.wikipedia.org/wiki/Covariance

[5]https://stats . stack exchange . com/questions/18058/how-would-you-explain-co variance-to-someone-who-only-understand-the-mean

如有任何问题,请随时在 LinkedIn上与我联系,或者在我的网站 T10 上访问更多类似的文章。

干杯。

COBUD-19——我在社交上疏远的机器人朋友

原文:https://towardsdatascience.com/cobud-19-2c7e18369615?source=collection_archive---------77-----------------------

构建酷的东西:

以及如何建立你自己的

来源:壁纸耀斑

亲爱的你,

隔离,我说的对吗?

你知道我的意思——在你知道是什么击中了你之前体重翻了一倍,不断预测(你的卫生纸库存),当然,开始忘记你的朋友长什么样。

这不是突发新闻——一级防范禁闭并不好玩。老实说,它有时会变得相当

我的意思是,即使你可以在 Zoom 电话上看到你的朋友和偶尔出现的老师,但这与像往常一样面对面和他们一起出去玩是不一样的。或者,如果你像我一样,你可能想知道你的朋友在哪里。

我说过一级防范禁闭会变得很无聊和孤独吗?

考虑到我(希望)不是唯一一个情绪低落的人,我想要一个解决方案,还有什么比创造一个永远在那里的人更好的方法呢?

一个 24/7 都醒着的人,他的存在只是为了成为一个好的、支持的、忠诚的朋友——同时又是一个冰冷的、没有生命的机器人,被编程为表现良好。不过,你可以忘记第二部分——至少比一个人好,对吧?

不管怎么说,作为一个合格的发明家,我承担了自己的责任,把一个新的聊天机器人带到了这个世界。一个聊天机器人帮助我(和我们所有人)感觉有一个“人”会一直在那里。

但最好的是,它只配有最时髦的名字——COBUD-19(请不要评价我)。

现在我已经构建了 COBUD,让我们进入我是如何做到的——以及你如何构建你自己的人工智能朋友,它有时间和你说话,并且每隔一段时间就引用令人作呕的疫情。**

NLPs 和聊天机器人

但在我们创建一个让你质疑作为人类意味着什么的聊天机器人之前,让我们快速了解一下聊天机器人的一般工作方式,这样我们就可以在构建它时了解事情的要点:

聊天机器人运行在一种被称为自然语言处理器(NLP)的特殊模型上。就像大多数其他形式的机器学习一样,它们的任务是为输入创建有意义的输出。在这种情况下,输入可以是这样的问题:“你愿意做我的朋友吗?”

但是自然语言处理面临着更复杂的挑战——他们的任务不是试图在冰冷的数字数据中寻找模式,而是寻找人类语言中的含义和上下文,这很难实现:

想想 Siri 或 Google Home 会有多复杂……来源 (CC0)

这正是背景重要的原因。当然,如果没有人工智能,你将不得不预测并硬编码你可能想要谈论的每一件可能的事情。残忍。

我在这里跳过了很多步骤,但是当谈到机器人时,这里是独家新闻:

自然语言处理可以让我们普通人的生活变得容易得多——它们查看我们一些硬编码的问题和回答,并生成一组人们可能尝试表达相同短语的方式。

每个人都有他们说话的方式,我们想确保我们的聊天机器人知道足够多,可以将它们注册为具有相同含义的问题。再说一遍——在聊天机器人的世界里,一切都与模式识别有关。

“酷,但是你怎么交一个虚拟朋友呢?”

很高兴你问了。

F̶i̶n̶d̶i̶n̶g̶交朋友

令人惊讶的是,仅仅在几年前,没有计算机科学学位的人建造一个聊天机器人几乎是不可能的——但是现在,任何人都可以在不到一个小时的时间内完成。

为了构建 COBUD-19 背后的(极其基本的)功能,我将使用 Dialogflow (之前被称为 Api.ai,直到被 Google 收购)。与其他人工智能聊天机器人相比,我使用它并从它的文档中获得帮助是最容易的。

首先,你需要前往dialogflow.com用你的谷歌账户登录。如果你没有谷歌账户,忘记这篇文章的存在,继续浏览微软 Edge。

要开始创建您的“朋友”,请点击“新代理”按钮创建一个聊天机器人。我将我的代理命名为“朋友”,这既是为了提醒我聊天机器人的用途,也是为了提醒我我没有任何聊天机器人:****

确保选择你希望你的机器人交互的语言,并编辑你的时区到正确的区域。完成后,点击“创建”。****

还记得我说过聊天机器人是模式识别机器吗?嗯,这就是他们的模式来源。在这一步,我们本质上是为谷歌预先构建的 NLP 创建数据来训练自己,以便它可以在未来识别类似的句子。****

就像它的声音一样,意图帮助我们的 NLP 理解用户在说话时的意图,并学习寻找对它们的回应。幸运的是,在创建第一个代理后,您会被自动重定向到“Intents”部分。****

现在,让我们给谷歌的 NLP 一些基本的想法。也许是对“最*怎么样?”这条短信的一些听起来像人的回应。,而不是通常的回答“今天我能为您做些什么?”首先创造一个新的意图,并用你可能经常使用的词语来填充它:****

确保有大量的投入和回应,这样你的朋友就不会总是以同样的方式回应。

我将 COBUD 设计得与一个朋友非常相似,所以我添加了一些随意的短语作为输入,并附带了一些随意的回答。现在是时候给你的机器人一点幽默和个性了——所以你可以随意地和你的机器人玩玩。保存你的更改,Dialogflow 的 AI 应该会根据你刚才给它的数据进行自我训练。

现在您的模型已经更新,继续使用屏幕右侧的控制台进行测试。Dialogflow 让您能够同时使用文本和语音进行交流——尝试两种方式,看看哪种效果更好:

这才是我要说的。

现在只需冲洗并重复这个过程来准备你的朋友要回答的任何短语。你想让你的虚拟朋友谈论生活?编程吧!你想进行一场政治辩论吗?为什么不呢?你想讨论即将上市的新冰箱?去做吧(好吧,也许不是)。****

相信我,对话有很多途径,但你不必涵盖每个可能的问题或某人可能有的评论——只需基本的即可。记住,我们是在努力交朋友,而不是 IBM Watson。****

从技术上来说,你可以说我们已经完成了——你已经创建了一个聊天机器人,它可以识别你说的话,并按照你想要的方式回复。这里只缺少一样东西——语境。

请记住,运行我们的 NLP 代码的处理器不知道这些单词是什么意思。他们所擅长的就是识别句子中的模式并输出集合结果。****

如果你让你的机器人专门回应“禁闭进行得怎么样了?”,如果你漏掉了一个字母或撇号,它可能会给你同样的回答,比如“一级防范禁闭进行得怎么样?”。

但是由于 NLP 实际上不知道一个单词的字典定义,他们不会理解任何类似于“隔离进行得怎么样了?”。如果你用一个单词或短语训练你的模型,它将不知道另一个具有相同信息的短语是什么意思。****

如果没有添加上下文或对词汇的基本理解,你的机器人将会像… 机器人一样。

为了解决这个问题,我们必须向模型输入常用词及其同义词,这样它就能更好地识别你的短语。转到“实体”选项卡并添加一行。把每一行想象成一个单词,把它的内容想象成它的同义词。填写完表格后,你应该会看到这样的内容:****

现在,你将会看到对话中最常用的单词和它们的同义词

当填写新实体时,最佳实践是几乎总是启用‘模糊匹配’‘定义同义词’,,因为它们允许您的机器人识别您输入的同义词以及某些人可能犯的一些常见打字错误或语法错误。****

******** 专业提示:现在是保存更新型号的最佳时机。尽管如此,请确保定期保存)********

最后,你的机器人已经准备好——完全具备理解句子、上下文和(不存在的)幽默感的能力。谢谢谷歌!****

那么,现在你有了一个机器学习模型(还有一个朋友),你实际上是如何使用它的呢?毕竟,一个真正的朋友会在你需要的时候出现——你不应该每次想联系的时候都登录网站。

好消息是你已经完成了艰苦的工作——这只是让你的机器人进入生活的问题!哇——感觉就像几个小时前你开始编写它的响应程序,现在它已经上传到万维网上了。他们成长得太快了…

好了,生活还在继续——是时候把你的朋友发布到网上了。导航至“集成”选项卡,选择网络演示选项。当然,你可以选择把你的朋友作为 Slack、Twitter 或 Viber bot(不管是什么)——但你永远不会错过一个好的老网站:****

创建您自己的本地托管好友只剩下一步了——只需点击您之前启用的“Web Demo”选项。然后,只需选择演示站点链接,您将被重定向到您自己的在线好友!****

演示时间

我是说,拜托,结果不言自明:

看看这种幽默吧——一个(虚拟)朋友拥有的惊人品质。

简而言之,这就是我如何建造 COBUD-19——一个拥有人工智能的人类朋友,以及你如何建造自己的朋友。你可能会说我们会在一起一辈子。我能说什么呢?机器人有一个很酷的特点,那就是不死。

但是说真的,新冠肺炎不是一个笑料——它是一个完全重塑了我们对正常定义的疫情。一切都来了个 180 度大转弯——包括我们的友谊和社交生活。突然间,我们中的许多人在很长一段时间里第一次开始感到孤独——你自己可能也注意到了。

所以,尽管一切都很轻松,但我希望你学到了重要的东西。我们都想感受到被关心、被爱和被支持——疫情不是停下来的借口。是的,我知道这是一个冗长的信息,但我想说的是:****

保持爱,保持生活。

感谢阅读,注意安全,
Aaryan

CoCalc 与 Colab——哪一个更适合实践研讨会?

原文:https://towardsdatascience.com/cocalc-vs-colab-which-is-better-for-a-hands-on-workshop-bb6261e2498?source=collection_archive---------44-----------------------

作者照片

我最*有机会举办了一个关于培训 Keras 深度学习模型的实践研讨会。这个研讨会是我为当地会议所做的一个会议的后续,该会议回顾了我即将为曼宁出版社出版的书中的内容。在介绍性会议之后,与会者有兴趣参加实践会议,在实践会议上,与会者将能够通过培训培训书中介绍的深度学习模型之一的过程进行工作。

深度学习模型培训研讨会的选项

我对交付一个关于培训深度学习模型的实践研讨会的前景感到兴奋。我曾参与在 2018 和 2019 举办介绍性机器学习训练营,但我从未举办过以深度学习为特色的研讨会。我需要一个让研讨会参与者用来训练深度学习模型的环境,我看到有三个选项:

  1. 在本地安装的 Python 实例上运行
  2. 使用 CoCalc ,这是一个专门为数据科学培训设计的基于云的环境
  3. 使用Colab(Google co laboratory),一个功能齐全的笔记本环境

我的深度学习研讨会的参与者将处于各种不同的水*,其中一些人迈出了机器学习的第一步,另一些人已经训练了深度学习模型。研讨会计划进行 3 个小时,我希望参与者将大部分时间花在深度学习模型训练的实验上,所以我需要一个可以在最少摆弄的情况下设置的环境。考虑到这一点,我放弃了选项 1。要让所有参与者都在本地安装了适当级别的 Python 实例,并设置了所有适当的库,这需要的时间太长了。

CoCalc 和 Colab 的比较

我要考虑两个竞争者:CoCalc 和 Colab。我选了哪一个参加研讨会?我会在文末透露我的选择。首先,让我们看看本次研讨会最重要的标准中竞争者的优缺点:

成本:因为实践研讨会是由 meetup 赞助的,对参与者只收取象征性的费用,这意味着成本是一个考虑因素。虽然 CoCalc 提供免费试用,但这种服务不适用于 workshop,因为它不允许从项目内部访问互联网,因此 workshop 参与者不可能方便地将 workshop repo 复制到他们的环境中。适用于研讨会的最低 CoCalc 环境是 14 美元/用户/月。相比之下,Colab 的标准免费访问拥有研讨会所需的一切。

整体的简单性:除了下一个要点中描述的文件系统的不同,Colab 与 CoCalc 相比还有一些额外的特点。例如,将 repo 复制到 CoCalc 中就像在 CoCalc 提供的标准终端中运行这些 git 命令一样简单:

git init
git remote add origin [https://github.com/ryanmark1867/dl_structured_data_hands_on](https://github.com/ryanmark1867/dl_structured_data_hands_on)
git pull origin master

相比之下,要将回购复制到 Colab 中,需要以下步骤:

  1. 通过运行以下命令在笔记本中安装 Google Drive ,然后按照步骤获取 Drive 的访问代码,然后将访问代码粘贴到该单元格生成的输入字段中。
from google.colab import drive
drive.mount('/content/gdrive')

2.将您要复制的目录存储到您的当前目录中:

%cd gdrive/My Drive/dl_june_17

3.克隆回购:

! git clone https://github.com/ryanmark1867/dl_structured_data_hands_on.git

为研讨会克隆回购是研讨会的关键先决条件,因此 Colab 在这一步的复杂性无疑是对它的一个打击。

文件系统 : CoCalc 有一个完全集成到开发环境中的简单文件系统。Colab 奇怪的文件系统设置是它的致命弱点。Google Drive 充当了 Colab 的文件系统,虽然它是一个像 Colab 一样的 Google 产品,但它与 Colab 的集成很弱。一旦我将 workshop repo 复制到 Drive 中,我发现在 Drive 中导航到我想要的笔记本并从那里在 Colab 中打开它是最容易的。从 Colab 开始,如何导航到驱动器中的特定文件并不直观。

性能:在 MNIST 的一个简单的 Keras 模型上运行 5 个时期的训练,在 Colab 上运行需要 12 到 13 秒。在 CoCalc,同样的训练要花三倍多的时间。CoCalc 在 MNIST 模型训练中的较慢性能并不是一个大问题,但对于研讨会的主要部分(根据我的书训练街车延迟预测模型),一个典型的 50 个时期的运行在 Colab 中需要 6 分钟,在 CoCalc 中需要 30 分钟或更长时间,假设 CoCalc 没有因内存不足而崩溃。

访问 GPU:CoCalc 不提供对 GPU 的访问。Colab 在先到先得的基础上提供对 GPU 和 TPU 的访问。事实证明,我的研讨会中的模型训练练习并没有从 GPU 中受益,但让参与者选择在深度学习训练运行中尝试 GPU 仍然是有用的。

你能运行什么 : Colab 是严格针对笔记本的。事实上,《回购》一书中的 MNIST 模型训练示例是普通的 Python 文件。我需要将这些例子转换成笔记本,然后才能在 Colab 中运行它们。相比之下,CoCalc 允许您运行普通的 Python 文件以及笔记本。

下表总结了 Cocalc 和 Colab 之间的比较:

CoCalc 与 Colab 对比摘要

结论

考虑到 CoCalc 和 Colab 的利弊,我应该选择哪一个来参加研讨会呢?我最终选择了 Colab 有两个原因:

  1. 虽然 CoCalc 在简单性方面比 Colab 有优势,但它在训练电车延迟预测模型(研讨会的一个关键目标)方面的较慢性能是一个阻碍。由于整个研讨会只有 3 个小时,我无法负担参与者花费半个小时或更多的时间用 CoCalc 进行一次 50 时代的训练。
  2. 尽管 Colab 有些古怪,但它是一个标准环境,我希望研讨会参与者在研讨会结束后会反复使用它。因此,他们在研讨会期间为解决 Colab 的怪癖而投入的时间是值得的,因为他们可以在将来将这一经验应用到其他项目中。另一方面,CoCalc 实际上是为培训用例优化的,我不期望任何人在正在进行的项目工作中使用它。

虽然我为这个深度学习研讨会选择了 Colab,但 CoCalc 提供了不错的性价比,我会考虑将其用于不需要深度学习的机器学习研讨会。

我在 2020 年 6 月中旬交付了工作坊,进行得很顺利。参与者能够对深度学习模型进行训练运行,并对各种超参数进行实验,以观察它们对训练运行的影响。感谢人工智能极客聚会的组织者,特别是 Dragos Ionel,给我这个机会来举办这个研讨会。

以下是与本文相关的一些附加资源:

  • 研讨会视频
  • 车间回购
  • 针对深度学习模型培训研讨会参与者的 Colab 设置说明
  • 2020 年 4 月原始深度学习结构化数据演示视频
  • CoCalc 现场环境对比
  • 更多细节关于作为研讨会基础的结构化数据深度学习的书籍

编写一个深度神经网络

原文:https://towardsdatascience.com/code-a-deep-neural-network-a5fd26ec41c4?source=collection_archive---------28-----------------------

入门

用 Python 构建深度神经网络的实践操作

timJ 在 Unsplash 上拍照

在上一篇帖子中,我们用 python 构建了一个具有基本功能的单隐层神经网络。为了推广和增强我们的网络,在这篇文章中,我们将建立一个 n 层神经网络来执行二进制分类任务,其中 n 是可定制的(建议重温我上次对神经网络的介绍,因为这里不会重复理论的基础)。

所有图片都是我自己创作的,引用的图片都是添加的。

权重初始化

首先,需要为不同的层初始化权重。请注意,通常情况下,输入不被视为图层,但输出被视为图层。

(详细的培训和测试流程,请点击这里

我们了解到,对于lth层:

其中 n[1]等于数字输入特征。

考虑下面的神经网络:

在这种情况下,第一个 W^[1 会有形状(4, 2),第二个 W^[2 会有形状(1, 4)

如果输入要求是一个列表,对于上面的情况,输入层应该是

[2, 4, 1]

并且我们的初始化权重需要足够小,以便在反向传播过程中梯度会很大,并且学习会更快。

正向传播

转发过程将是直截了当的,如下所示:

其中llth层,而g(x)是激活函数。这里我们将使用 2 个不同激活函数:

以上所有函数都适用于矩阵。

正向传播将遵循上面的等式。注意,在我们的实现中,除了最后一层我们使用sigmoid激活,其余的我们使用relu激活函数。

价值函数

我们仍然认为这是一个二元分类,一批的成本是:

其中a为预测值,y为实际值。

反向传播

既然我们的正向过程已经完成,为了让我们的模型通过迭代得到改进,让我们来看看反向传播的关键。

[ 来源:https://github.com/enggen/Deep-Learning-Coursera]

可以以递归方式计算反向梯度:

首先,需要实现sigmoidrelu的导数。

出于对称的原因,这里两个函数具有相同的输入,尽管这不是必需的。

根据上面的等式,我们实现了反向传播。注意,除了最后一层使用了sigmoid函数,其余的我们都应用relu导数来得到梯度。

现在给定梯度,我们的权重更新如下:

应用于数据集

让我们将我们的模型应用于创建的包含 200 个要素的数据集。

现在让我们训练我们的模型。

这里我们有一个有 200 个输入特征的 3 层神经网络。

要了解详细的训练过程,请查看我的 github 。

探索性数据分析的代码和技术

原文:https://towardsdatascience.com/code-and-techniques-for-exploratory-data-analysis-a44c50953502?source=collection_archive---------25-----------------------

探索性数据分析框架

如何分析数据,测试假设,并为您的客户创造洞察力

免责声明:您正在阅读第 1 部分“探索性数据分析框架”。 第 2 部分 展示了如何基于下面的分析见解构建基线模型。

介绍

当您需要通过生成见解和构建第一个基线模型来向客户展示数据的价值时,这是一种非常常见的情况。作为一名数据科学顾问,您可能每周都会收到这样的请求。因此,建立一个探索性数据分析的基本框架是有意义的,它可以帮助您评估数据质量,并向客户展示第一手见解。在本文中,我描述了我在分析新数据集时使用的探索性数据分析框架。该框架包括:

数据质量评估

数据分布概述

分析不同类型的数据(地理位置、时间序列、名义数据等)。)

关联(或相关)分析

构建基线模型(在分析的第 2 部分中描述)

拟议的框架并非详尽无遗,但涵盖了广泛的案例,可作为制定更详尽框架的起点。在我们着手任何复杂的建模之前,它作为第一个健全检查。完整的笔记本可以在 GitHub 上找到。

在这个例子中,我使用了在data.gov.uk发现的道路事故安全数据。该数据集包含不同类型的数据:地理、时间序列、分类和连续变量,这些变量非常接*真实世界的情况,其中数据来自多个来源,并且可能属于不同的类型。

图片由 Icons8 团队/unsplash.com 提供

问题陈述

最终目标是开发一个模型来预测警察参与事故的概率。我们还应该评估我们所掌握的数据的质量,并展示哪些因素对模型的决策有最大的影响。

数据质量评估

首先,我导入了探索性数据分析所需的包。

将数据读入熊猫数据框

除了像 pandas、numpy 和 matplotlib 这样的标准包,我还导入了 missingno ,这是一个 pandas 兼容的库,用于可视化缺失值。

由于我不希望事故数据帧在数据探索阶段被改变,数据被复制到一个单独的变量中。数据分析的所有后续步骤都将在副本上执行,一旦进入建模阶段,我将返回原始数据集(在第二部分)。

将数据集复制到单独的变量中

数据集包含 32 个变量,其中大部分是分类变量。我们可以在变量查找中检查每个变量的含义及其值。首先,我们需要评估变量数据类型,因为其中一些可能需要类型转换。

事故数据框架中的变量类型

事故数据集中的大多数变量都是数字。还有一个变量是我在读取时显式设置为 datetime 对象的,我们还有四个对象变量:

  • 事故指数 —事故指数
  • 时间 —格式为 HH:mm 的时间
  • Local _ Authority _(Highway)LSOA _ 事故 _ 地点 —包含事故地点信息的分类变量。

在我们对数据集中的变量进行更深入的分析之前,让我们先了解一下缺失值的概况。

缺失值分析

如前所述,为了快速了解缺失值的模式,我使用了 missingno 包,因为它提供了数据集的图形化表示。它允许我们快速检测缺失数据的模式,然后判断数据是否是随机缺失的。基于此,我们需要制定一个策略来处理缺失的值。

在事故数据集中,缺失值用值“-1”表示。首先,我们需要用一个 NaN 值来替换它,这样我们就可以得到一个缺失观测值模式的概述。

绘制缺失值图表

缺失值评估

因为我只对包含缺失值的变量感兴趣,所以我只可视化包含 NaN 值的列。该图显示了每一列中缺失的观察值,右边的栏显示了每一行的数据完整性。

正如我们所看到的,有几列似乎与缺失值相关联:

  • 路口 _ 控制第二 _ 道路 _ 等级
  • 人行横道 - 人控人行横道 - 物理设施
  • 道路 _ 路面 _ 状况特殊 _ 状况 _ 现场行车道 _ 危险

这意味着值不是随机丢失的,我们在处理丢失值的策略时需要记住这一点。

此外,一些变量如 Junction_Control 也会对响应变量产生重大影响。因此,在将来,缺失的值应该是完整的,例如通过车辆传感器数据。

为了更好地理解我们正在处理的数据,我将绘制每个数值的直方图。

数据分布概述

正如我们已经看到的,数据集包含不同类型的数据:地理的、分类的、时间序列的。所有类型的数据都应单独处理和分析。

绘制事故数据的直方图

事故数据集中每个变量的直方图

大多数变量是基数较低的分类变量。我们还看到一些成对的特征,比如

  • 经度位置 _ 东行 _OSGR
  • 纬度位置 _ 北向 _OSGR

有非常相似的分布。他们很可能复制了信息。因此我们现在可以只集中分析纬度经度,而忽略位置 _ 东行 _OSGR位置 _ 北行 _OSGR

让我们简单地看一下我们想要预测的变量,因为进一步的分析将在其上下文中执行。

响应变量概述

响应变量包含三个类。我将把第 2 类和第 3 类合并成一个负类,因为我只对预测警察参与事故的概率感兴趣。

响应变量类别份额

正如我们可以看到的,响应变量中的类是不*衡的,这是在训练模型时要记住的事情。

不同类型数据的分析

地理数据

由于描述事故地点的变量相当多,我将检查地点是否是预测警察是否会参与事故的重要因素。

绘制地理数据

道路事故英国 2017。红色—未出席;灰色—有人值守

红色—未出席

灰色—有人值守

该图显示,在一些区域,警察不太可能参与事故。当我们训练一个模型时,经度纬度可能是具有高分辨能力的变量。

数据集中变量之间的关联

假设检验

在这一节中,我将测试一些关于数据集中变量之间关系的假设。检查变量之间关系的常用方法是计算相关性(如皮尔逊相关系数)。然而,事故数据集中的绝大多数变量都是分类变量。我们不能在数据集上使用相关性,因为相关性衡量两个定量变量之间的线性关系。因此,我使用卡方检验来确定两个分类变量之间的显著关联。

在盯着这些数据看了一会儿后,我提出了一个假设,即对于一名警察来说,决定是否参与一起事故的最重要因素之一是事故的严重程度。那么我们要测试的第一个假设如下:

H0: 事故的严重程度对警察参与事故的概率没有影响。

记住,零假设总是一个没有变化的假设,我们现在的工作是检查我们是否有足够的证据来拒绝它。

绘制事故严重性和事故出勤率

事故严重程度和警察出勤率

我们可以观察到两组之间的差异,但我们需要量化这些关系。为此,我将在 5%的显著性水*上进行卡方检验。

为了进行卡方检验,我使用了 researchpy 包。Researchpy 生成熊猫数据框架,其中包含学术研究通常需要的相关统计测试信息。

假设检验。一名警官是否参加事故的概率取决于事故的严重程度

researchpy 运行的卡方检验为我们提供了三个值:

皮尔逊卡方——通过计算观察频率和预期频率之间的*方差总和(由预期频率归一化)来计算。它用于计算 p 值,这是卡方检验的另一个输出

p 值——假设 H0 正确的观测结果的概率

克莱姆的 V——是一个量化两个分类变量之间关联的效应大小。我们可以使用 Pearson 卡方统计来量化这些关系,但是与协方差一样,它不是有界的,并且倾向于随着维度和样本的数量而增长。因此,要对不同大小的数据集的影响大小进行可比测量,我们需要通过将 Pearson 卡方统计除以样本大小和最小维度减 1 来对其进行归一化。它会给我们一个 0 到+1 之间的值,叫做克莱姆 v。

卡方检验的结果表明,有统计学意义的证据表明 H0 是错误的。鉴于证据和数据,我们拒绝 H0 的说法,即变量事故严重性Did _ 警察 _ 官员 _ 出席 _ 事故现场之间没有关联,p 值<为 0.005,效应大小为 0.1。

既然我们知道这种联系是显著的,我们想测试变量水*之间的关系。为此,我们需要进行多次 2×2 卡方检验。然而,对同一个因变量进行多次测试会增加假阳性的机会,从而增加了偶然获得重要结果的可能性。为了考虑假阳性,我们需要通过校正其级别来调整正在执行的比较数量的重要性。该校正称为 Bonferroni 校正。为此,我将使用 scipy 的统计包,作为 researchpy 的替代方法。需要记住的是,scipy 不提供熊猫数据帧中的结果。

测试事故严重程度与警察参与事故的概率

使用 Bonferroni 调整的显著性水*=0.017 的卡方检验表明所有比较都是显著的。然而,对数据集中的每一对变量进行卡方检验是非常耗时的。因此,我将建立一个关联矩阵作为数据探索的最后一步。

日期和时间

数据集中有日期时间变量,检查时间相关模式并添加一些新变量来帮助我们的模型做出决策总是一个好主意。首先,我们来看一下时间变量。

绘制每小时事故数

每小时事故数及其出勤率

我们可以观察到两个最容易发生事故的高峰时段:上午 7-9 点和下午 15-18 点。我知道警察的数量在一段时间内是不变的,我会假设在高峰时间出勤率会更低,因为可能没有足够的警察来处理所有的事故。因此,我将创建一个表示高峰时间的变量,并测试它与警察发生事故的概率之间的关联。

绘制每个高峰时间的事故数量及其出勤率

每个高峰时间的事故数量及其出勤率

各组之间略有不同。为了量化这些关系,我将运行另一个卡方检验。

H0: 高峰时间对一名警察参与事故的概率没有影响

卡方检验:高峰时间是否影响警察参与事故的概率

卡方检验的结果表明,有统计学意义的证据表明 H0 是错误的。鉴于证据和数据,我们拒绝 H0 的说法,即变量高峰时间Did _ 警察 _ 官员 _ 出席 _ 事故现场之间没有关联,p 值<为 0.005,效应大小为 0.04。

事故数据集中的另一个时间相关变量是日期。我将绘制每天的事故总数,以分析随时间变化的趋势。

绘制每天的事故数量

每天的事故数量

该图显示了一种循环模式,这可能表明存在某种季节性。我将绘制 ACF 图和 PACF 图,以检查时间序列中是否有显著的季节性成分。

绘制每天事故数量的 ACF 和 PACF

ACF 和 PACF 每天事故数量图

ACF 图在滞后 1 处显示了显著的趋势分量,在滞后 6 和滞后 7 处显示了显著的季节分量,这可能指示周末。我将绘制出每个工作日的事故数量,以检查周末的行为是否有所不同。

绘制一周中每天的事故数量

每周事故数和警察出勤率

我们可以观察到整体事故和周末参与的事故略有下降。我将明确地对周末进行建模,然后检查一个新创建的变量是否与警察参与事故的概率有显著关联。

H0:周末对警察处理事故的概率没有影响。

卡方检验:周末是否影响警察参与事故的概率

卡方检验的结果表明,有统计学意义的证据表明 H0 是错误的。鉴于证据和数据,我们拒绝 H0 的说法,即变量周末Did _ Police _ Officer _ Attend _ Scene _ of _ Accident之间没有关联,p 值<为 0.005,效应大小为 0.04。

连接附加数据

我们已经测试了一些假设,并设计了两个新功能,高峰时间和周末。现在让我们加入在 data.gov.uk 得到的其他数据。首先,我将连接伤亡数据集中的数据。

加载伤亡数据集

伤亡数据集主要包含分类变量。从前三行我们可以看出,事故指数对于每一行都不是唯一的,因为一次事故可能导致多人伤亡。对我们来说,这意味着我们不能直接连接事故和伤亡数据表,而是需要做一些特征工程。对于第一次评估,让我们只加入伤亡数据集中的几个变量。

我将形成几个假设,并假设如果下列情况之一为真,警察更有可能参与事故:

  • 伤亡者是行人
  • 18 岁以下或 65 岁以上的人
  • 司机年龄在 18 岁以下,或 65 岁以上

因此,我将创建新的二元变量,将它们加入事故数据集,并测试是否与响应变量有显著关联。

连接伤亡数据集中的新变量,并测试它们与响应变量的显著关联

卡方检验显示,在 0.05 的显著性水*上,新创建的变量与警察参与事故的概率之间存在显著关联,而 p 值【data.gov.uk】是涉及事故的车辆数据集。

加载车辆数据集

从前面几行中,我们可以看到事故指数对于每一行都不是唯一的,因为一次事故可能涉及几辆车。同样,我们需要进行特征工程来连接数据。

我将形成几个假设,并假设如果下列情况之一为真,警察更有可能参与事故:

自行车卷入了事故

  • 出租车、货车或公共交通工具卷入事故
  • 司机年龄在 18 岁以下,或 65 岁以上
  • 与伤亡数据集中的情况一样,我将创建相关的二进制变量,将它们连接到事故数据集中,并测试是否与响应变量有显著关联。

连接车辆数据集中的新变量,并测试它们与响应变量的显著关联

卡方检验显示,在 0.05 的显著性水*上,新创建的变量与警察参与事故的概率之间存在显著关联。

毫无疑问,可以对这三个数据集执行更复杂的特征工程,包括构建分类嵌入。然而,我们的目标是建立第一个原型模型,并通过快速洞察和发现变量之间的有趣关系(例如,带有事故出席率、事故季节性等的地理数据)向客户展示数据的价值。).为了保持务实,我将暂时结束特性工程。在进入建模阶段之前,我想做的最后一件事是为分类变量建立一个关联矩阵,以总结已经完成的分析。我将通过计算跨类别关联的 Cramer V 统计量来构建关联矩阵,这是 researchpy 包提供的效果大小。如前所述,需要记住的一个重要细节是,Cramer 的 V 倾向于高估大样本和高维样本的关联强度。因此,我们需要实现一个偏差校正,这在维基百科的中有描述。我使用了来自 Ziggy Eunicien 的一个非常好的偏差校正实现。

绘制关联矩阵

数据集中分类变量之间的关联

我们感兴趣预测的变量“Did _ 警察 _ 官员 _ 出席 _ 事故现场”位于矩阵的第一列(和第一行)。

不出所料,与时间相关的变量彼此紧密相关:日期星期几周末

数据集中的位置相关变量,警察部队地方当局(地区)地方当局(高速公路)LSOA(事故地点)与警察参与事故的概率有很高的关联。这与根据地理数据分析得出的结论是一致的。

结论

在探索性数据分析的第一部分,我分析了英国的道路交通事故安全数据。进行了数据质量保证,其结果是缺失数据中存在一种模式。应该使用传感器数据来提高预测的准确性。

对地理数据的分析表明,事故发生的地点与警察到场的可能性之间存在联系。这意味着有些地区(例如东伦敦)和地区需要更多的警察。这一点在高峰时间尤为重要,因为此时无人参与的事故比例会增加。

对伤亡人数和车辆数据的变量分析表明,如果行人和 18 岁以下或 65 岁以上的人是伤亡人员,警察更有可能参与事故。此外,特定类型的交通工具(自行车和公共交通工具)导致警察参与事故的可能性更高。对于预测分析来说,这是一个很有价值的见解。

在第 2 部分中,我将展示如何基于我们在探索阶段学到的见解构建基线模型。

编写面试问题

** [## 一个完整的*台,在这里我会教你找到下一份工作所需的一切,以及…

技术开发

skilled.dev](https://skilled.dev)**

代码梯度下降从零开始字节和字节

原文:https://towardsdatascience.com/code-gradient-descent-from-scratch-britts-bytes-e9f4a36a9955?source=collection_archive---------47-----------------------

如何用 python 从零开始编程梯度下降?那段时间你在面试中笨手笨脚。

土壤中的物理和化学梯度很大程度上影响着稻田的生长和小气候

动机

这就是了。你已经通过向完全陌生的人发送大约 10 条 LinkedIn 信息,并通过总结你整个成年职业生涯的 30 分钟电话来吸引招聘人员,从而建立了人际关系网。他们给你送来了……敦敦敦……任务。他们说 4 个小时。短短 4 个小时。“4 个小时”,你对自己说“小菜一碟”。这封电子邮件附带了一个指向谷歌说明文档的链接。“就两个提示”你再想想,“完全没问题。打赌我会有时间。”你打开作业。那里。第一张谷歌表单上写着“请最小化这个未知函数”。你的大脑一片黑暗,你在考虑自己是否能通过五年级的数学考试。

提示

好吧,让我们重新看看面试官想让你做什么:

已知:您从类似 pickle 文件的东西中提取函数,因此您只知道输入(您指定的)和输出(由函数给出的)。

问题:通过计算每个给定点的梯度,找出使该函数输出最小的输入。例如,我们将使用 f(x1,x2)=y

背景

这是梯度下降。它只是被分解成几个部分。你知道的。你以前学过。有这么多关于梯度下降的文章,我不能证明为什么在这里广泛地写它会有价值。一些很好的资源是这篇文章用于快速回顾,经典的吴恩达课程用于你想知道的一切。现在,我将提醒您完成这项挑战所需的基础知识。

什么是渐变?

书中定义:梯度是从一点到另一点的变化幅度和方向。

实际定义:在我转到数据科学之前,我学的是工程学,所以我关于梯度的最好的例子都源于力学和流体动力学。希望这个类比对一个人来说是绝对完美的,对其他人来说是可以忍受的。想象一个湖。通常很*静。

作者照片

好了,现在一艘船正拉着一位非凡的障碍滑雪运动员过来。水仍然有相同的水流吗?

作者照片

水很可能不再以相同的速度和方向流过整个表面,对吗?我们如何在这个湖的不同部分表现水的速度和方向?机械物理学会告诉你用速度矢量。像这样:

图片由可汗学院提供

这代表一种梯度。速度是从一点到另一点的距离(在特定方向上)相对于时间的变化。开始结合这些梯度,我们得到的东西看起来像这样:

大卫·切纳勒通过研究之门拍摄的图片

数学定义:梯度是在你评估的任何一点与曲线相切的直线的斜率。这意味着梯度是代表该点函数的偏导数分量的和。这让我们想到了这样的事情:

我们在下降什么,为什么?

在机器学习中,你通常试图找到预测器和一系列特征之间的某种关系。为了收敛,你优化了一个成本函数,以获得最小的误差。优化的方法是在误差最小的方向上采取步骤,并返回相应的梯度向量。一旦你达到一个阈值最小值,你的梯度向量揭示了优化这个成本函数的系数。

换句话说,也许你以前见过这个:

这是梯度下降方程,假设你有一些已知的成本函数 J(θ)和学习率(α)。例如,线性回归的常见成本函数是误差*方和:

在这个等式中,我们通过目标值(y_pred)和输出值(y_actual)计算*方误差。接下来,我们通过除以我们的观察次数(n)来计算误差*方的*均值。在线性回归的情况下,为了结合我们的两个方程,让我们用*方误差来表示我们的成本函数。

这里我们用 m 而不是 n 来表示这个步骤中的观察次数,我们除以 2 只是为了在计算梯度时更容易微分。h(theta)代表目标(y_pred),y 代表产出(y_actual)。回到第一个等式,你会得到如下结果:

这里,α代表第一个等式中的学习速率,但是我们已经求解了梯度的偏导数。

让我们回到(成本)函数未知的面试问题。我们没有计算偏导数的奢侈。因此,我们必须使用猜测和检查的方法来寻找最大梯度。我将简单地指定一个步长,而不是计算梯度(如下所示)。我将使用这个步长来检查当前点周围的每个梯度。

这是相当蛮力的,但我会假设你的面试官正在测试你是否能潜入你的算法车辆的引擎盖下,进行一点手动维护。

解决方案编码

好吧,让我们开始吧。我做了两个函数来解决这个问题。第一个函数将简单地计算给定 x = (x1,x2)的最大梯度。我假设只有 2 个特征(x1,x2)由提示定义。第二个函数将使用第一个函数,通过遵循第一个函数中的梯度来最小化您的未知(给定)函数 f(x1,x2) = y。从我们的第一个函数开始:

这里发生的是,我抓住了我的给定点(x1,x2)周围的每个点和我的给定点+我实例化的步长(这里是 1)之间的差。然后,我将找到这 4 个差值的最大值,并返回相关的方向(x1+步长,x2+步长)和幅度(差值)。

让我们来看看最小化函数:

这里我使用梯度函数返回的最大值遍历梯度,每次都重新运行循环。一旦我到达一个接*于零的停止差,我就返回该点的坐标(x1,x2)。我还建议将它可视化(如果可能的话),感受一下您的解决方案。我的例子是这样的:

作者图片

如你所见,除了全局最小值之外,还有一个局部最小值。如果我只在(0,0)初始化 minimize()函数,我会陷入局部最小值。我通过在随机不同的起点初始化脚本来避免这个问题,以确保一个真正的最小值。一旦 minimize()函数从~(750,500)开始,我就能找到全局最小值。动量是神经网络中的一个流行概念,它无需在随机点进行初始化就能完成这项任务(可以想象,如果有 100 多个特征,这可能会变得很乏味)。这有点超出了这篇文章的范围,但是可以研究一下达到全局最小值的可扩展方法。

最后

这个问题的某种形式对于机器学习面试来说是非常典型的。我也认为这是一个有趣的谜题,展示一些基本的 python 印章,因为你不需要任何花哨的包。下面是我用来解决这个问题的一些参考资料,我祝你在未来的面试中好运。愿你优雅地走下阶梯。

参考

  • 关于渐变的维基百科
  • Python 中的渐变下降
  • 梯度下降数学
  • 梯度下降方程

原载于 2020 年 4 月 1 日 https://brittanybowers.com**的

Java 中的代码,作为 C++执行。

原文:https://towardsdatascience.com/code-in-java-execute-as-c-921f5db45f20?source=collection_archive---------22-----------------------

如何使用 GraalVM 原生映像从 C++调用 Java 方法而不遭受大量的性能下降。

马克西米利安·魏斯贝克尔在 Unsplash 上的照片

Java 和 C++仍然是两种最流行的编程语言。这两种语言有不同的设计和特点。根据问题的不同,一个可能比另一个更好。然而,在某些时候,我们需要集成这些语言,例如调用用 Java 编写的方法到你的 C++代码中。需要集成 Java 和 C++并不是什么新鲜事。事实上,我们可以在找到一个可以追溯到 1996 年的教程。从那以后,这两种语言有了显著的发展。

在本教程中,我们将学习如何使用 GraalVM 本机映像从 C++调用 java 方法。如果这是你第一次听说 GraalVM,你可能会有兴趣看看他们的主页。GraalVM 是一个虚拟机,可以用来运行 Java 应用程序。它基于 JVM,但是增加了额外的特性,比如提前编译、更少的内存占用和其他高级优化。此外,GraalVM 提供了一个本机映像插件,您可以使用它将 Java 应用程序编译成本机应用程序。这意味着您的应用程序可以在不需要安装和运行 JVM 的情况下运行。有趣的是,您还可以使用这个原生图像插件将您的 Java 应用程序编译成一个共享库,并从您的 C++代码中加载它。当你的代码主要是用 C++写的,但是你想在它的某个部分使用一个现有的 Java 库时,这就非常方便了。

它是如何工作的

在深入教程之前,我们先来看看这个东西是如何工作的。基本上,你要做的如下:

  1. 用 Java 写你的方法。
  2. 使用 GraalVM 将 Java 代码编译成 C++共享库(yourlib.so)和头文件(yourlib.h)。
  3. 将库和头文件加载到 C++项目中。

在这个例子中,我们将模拟一个用例,您想要在您的 C++项目中使用来自第三方 Java 库的函数。特别是,我们有兴趣使用谷歌番石榴库中的一个数学函数。

准备 Java 项目

首先,我们需要为我们的 java 方法建立一个 maven 项目。在 maven 中构建我们的项目有助于我们导入依赖项,并利用 GraalVM 原生映像插件来创建共享库。创建项目后,将依赖项添加到 Guava 库(或您需要的任何库)中,并将 GraalVM 库添加到 pom.xml 文件中:

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.graalvm.nativeimage/svm -->
    <dependency>
        <groupId>org.graalvm.nativeimage</groupId>
        <artifactId>svm</artifactId>
        <version>19.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>28.2-jre</version>
    </dependency>
</dependencies>

接下来,添加以下插件,将 java 代码构建到 C++共享库中:

<build>
 **<finalName>libmymath</finalName>**    <plugins>
        <plugin>
            <groupId>com.oracle.substratevm</groupId>
            <artifactId>native-image-maven-plugin</artifactId>
 **<version>20.0.0</version>**            <executions>
                <execution>
                    <goals>
                        <goal>native-image</goal>
                    </goals>
                    <phase>package</phase>
                </execution>
            </executions>
            <configuration>
 **<buildArgs>--shared -H:Name=libmymath</buildArgs>**            </configuration>
        </plugin>
    </plugins>
</build>

我们来看看加粗的部分。在 < finalName > 标签上,输入您想要的库名。记下您在 <版本> 标签上指定的版本,因为您稍后会需要它。在 < buildArgs >,上,我们指定了 sharedname 参数来指示 GraalVM 本机映像创建一个共享库,而不是一个本机可执行文件。将与 finalName 相同的名称放在 Name 参数上。

现在让我们准备我们的 Java 方法。给定一个整数输入 x ,我们感兴趣的是计算 2 的最大幂 x 。例如,如果 x 是 14,它将返回 16,因为 16 是大于 14 的最接* 2 的幂。下面是我们函数的代码片段。注意,我们需要放置一个额外的第一个参数类型的线程isolate thread 来桥接我们的 Java 和 C++执行。该参数必须在第一个位置。我们还需要在centry pointannotation中指定方法的名称。这是我们将在 C++代码中引用的方法名。你可以在这个库里查看完整的 java 项目。

import **com.google.common.math.IntMath**;
import **org.graalvm.nativeimage.IsolateThread**;
import **org.graalvm.nativeimage.c.function.**CEntryPoint;

public class **MyMath** {
    @CEntryPoint (name = "ceilingPowerOfTwo")
    public static int ceilingPowerOfTwo(**IsolateThread** thread, int x) {
       return **IntMath**.*ceilingPowerOfTwo*(x);
    }
}

设置 GraalVM

设置好项目后,我们现在需要运行 mvn 包来将我们的 Java 方法编译到一个共享库中。然而,由于该功能仅在 GraalVM 上可用,我们需要设置 GraalVM 并将 JAVA_HOME 指向 GraalVM 二进制文件。因此,您需要执行以下操作:

  1. 下载 GraalVM 。确保您选择了正确的架构、java 版本以及您在 pom.xml 中指定的 GraalVM 版本。具体来说,这个示例项目使用 GraalVM 20.0.0 for Java 8。
  2. 使用 gu 二进制文件安装本机映像扩展。在 Unix 环境中,您可以使用:
    graalvm-ce-Java 8–20 . 0 . 0 2bin/gu install native-image
  3. 更新 JAVA_HOME 以指向下载的 GraalVM 目录。在 Unix 环境下,可以使用 export JAVA _ HOME =<path _ to _ graalvm _ directory/graalvm-ce-JAVA 8–20 . 0 . 0 2/>。

编译到共享库

现在您可以调用 mvn 包 install 来构建共享库。会生成几个文件: graal_isolate.hgraal_isolate_dynamic.hlibmymath.hlibmymath_dynamic.h、libmymath.so (或者其他 OS 中的其他格式)。除了我们自己的库,它还会生成 Graal VM C++库。

将库导入到 C++代码中

现在让我们在下面的简单 C++项目中使用我们的共享库。将生成的文件放在这个 C++项目的目录中。例如,您可以创建一个 include 文件夹,并将这些文件放在这个文件夹中。

#include <iostream>
#include <libmymath.h>

int main() {
    graal_isolate_t *isolate = NULL;
    graal_isolatethread_t *thread = NULL;

    if (graal_create_isolate(NULL, &isolate, &thread) != 0) {
        fprintf(stderr, "initialization error\n");
        return 1;
    }

    printf("Result> %d\n",ceilingPowerOfTwo(thread, 14));
    return 0;
}

使用以下命令编译 C++代码,并尝试运行它。

*g++ ceilingPowerOfTwoCpp.cpp -L includes/ -I includes/ -lmymath -o ceilingPowerOfTwoCpp*

注意:您可能需要使用以下命令将 includes 目录设置为 LD_LIBRARY_PATH:

export LD_LIBRARY_PATH=<path_to_includes_directory>

结论

在编程语言同时发展的世界里,互操作性是一个不可避免的要求。GraalVM 原生映像的共享库特性提供了一种将 Java 方法嵌入到 C++程序的简单方法,这使得能够编写复杂的用户定义函数和使用第三方库。一个有希望的例子可能是开发一个以高性能和高级 API 为目标的数据处理框架。

如果你想了解更多关于 GraalVM 的内容,请访问他们的主页。本教程的完整代码可以在这个资源库中找到。

在 scikit-learn 中编写自定义输入程序

原文:https://towardsdatascience.com/coding-a-custom-imputer-in-scikit-learn-31bd68e541de?source=collection_archive---------26-----------------------

Gabriel Crismariu 在 Unsplash 上拍摄的照片

了解如何创建自定义估算器,包括用于更高级用例的 groupby 聚合

处理缺失数据是大多数机器学习项目的固有部分。一个典型的方法是使用scikit-learnSimpleImputer(或者来自sklearn.impute模块的另一个估算器)。然而,通常最简单的方法可能不是最好的,我们可以通过使用更复杂的方法获得一些额外的性能。

这就是为什么在这篇文章中,我想演示如何编写一个自定义的基于scikit-learn的估算器。为了使案例更有趣,估算者将根据各组的*均值/中位数来填充缺失值。

为什么要把自定义的 imputer 写成类?

在直接进入编码之前,我想详细说明一下为什么编写一个定制的估算类(从scikit-learn继承)值得你花时间的几个潜在原因:

  • 它可以帮助你发展编程技能——在编写继承自scikit-learn的估算器时,你可以了解一些贡献者已经使用的最佳实践。此外,通过继承,您可以使用一些已经准备好的方法。这样,您的代码将会更好/更干净,并且对于一些不可预见的问题可能会更健壮。
  • 您的自定义类可以随着时间的推移进一步开发,并可能与其他用户共享(甚至可能集成到scikit-learn!)
  • 更实际的是,通过使用scikit-learn框架创建估算器,你可以使它们与scikit-learnPipelines兼容,这使得项目的流程更加清晰,更容易复制/生产。另一个实际问题是fittransform方法之间的明确区别,因此您不会意外引入数据泄漏——包括在确定用于输入的值的过程中的测试数据。

实现自定义估算器

在这一节中,我们将使用 Python 实现自定义估算器。

设置

首先,我们加载所有需要的库:

为了写这篇文章,我使用了scikit-learn版本 0.22.2。

生成样本数据

在这篇文章中,我们将使用一个玩具数据集。我们假设收集来自两个不同人群(样本AB)的人的身高,因此数据会有一些变化。此外,第一个样品还有一个区别特征,称为variant(值为ab)。这个命名结构背后是什么并不重要,目标是有两个不同级别的可能聚合。然后,我们根据sample_name用不同的比例和位置参数值从正态分布中采样高度(使用numpy.random.normal)。

通过使用sample(frac=1),我们基本上重组了DataFrame,所以我们的数据集看起来不那么人工。下面你可以看到创建的DataFrame的预览。

生成数据的预览

然后,我们使用以下代码用 NaN 值替换 10 个随机高度:

现在,DataFrame已准备好插补。

对估算器进行编码

是时候对估算器进行编码了。您可以在下面找到该类的定义:

如前所述,通过使用从sklearn.base类(BaseEstimatorTransformerMixin)的继承,我们完成了很多工作,同时自定义的估算器类与scikit-learnPipelines兼容。

那么后台到底发生了什么呢?通过从BaseEstimator继承,我们自动得到get_paramsset_params方法(所有的scikit-learn估算器都需要这些)。然后,从TransformerMixin继承提供了fit_transform方法。

注意:还有其他种类的 Mixin 类可供继承。我们是否需要这样做取决于我们想要编码的估计器的类型。例如,ClassifierMixinRegressorMixin让我们可以使用score方法来评估估计器的性能。

__init__方法中,我们存储了输入参数:

  • group_cols —要聚合的列的列表,
  • target —插补的目标列(缺失值所在的列),
  • metric —我们希望用于插补的指标,可以是组的*均值或中值。

此外,我们包含了一组断言来确保我们传入了正确的输入。

fit方法中,我们计算impute_map_,它是一个DataFrame,其聚合指标用于输入。我们还检查用于聚合的列中是否没有缺失值。还有一点非常重要,那就是fit方法应该总是返回self

最后,在transform方法中,我们用适当的值替换每个组中缺失的值(由impute_map_的行指示)。作为额外的预防措施,我们使用check_is_fitted来确保在使用transform方法之前我们已经拟合了估算对象。在实际转换数据之前,我们使用copy方法制作了一个副本,以确保不会修改原始的源数据。关于这个话题的更多信息,你可以参考我之前的一篇文章。

fittransform方法中,我们还在方法定义中指定了y=None,即使GroupImputer类不会使用数据集的y值(也称为目标,不要与表示插补目标的target参数混淆)。包含它的原因是为了确保与其他scikit-learn类的兼容性。

是时候看看自定义估算器的运行了!

运行代码会打印出以下内容:

df contains 10 missing values.
df_imp contains 0 missing values.

scikit-learn中的所有估算器一样,我们首先创建对象的实例并指定参数。然后,我们使用fit_transform方法创建新对象,用通过sample_namevariant计算的*均值替换height列中缺失的值。

为了创建df_imp,我们实际上需要手动将转换的输出转换为pd.DataFrame,因为原始输出是一个numpy数组。scikit-learn中的所有估算器/变压器都是这种情况。

我们可以看到,估算器按照预期工作,并替换了我们的玩具DataFrame中所有缺失的值。

结论

在本文中,我展示了如何通过继承scikit-learn中的一些基类来快速创建一个自定义估算器。这样,编码更快,我们也确保估算器与整个scikit-learn框架兼容。

在进行机器学习项目时,创建自定义估算器/转换器肯定会派上用场。此外,我们总是可以为其他项目重用创建的类,因为我们首先试图使它尽可能灵活。

您可以在我的 GitHub 上找到本文使用的代码。一如既往,我们欢迎任何建设性的反馈。你可以在推特上或者评论里联系我。

参考

[1]https://github . com/sci kit-learn/sci kit-learn/blob/master/sk learn/impute/_ base . py

[2]https://sci kit-learn . org/stable/modules/generated/sk learn . base . base estimator . html

[3]https://sci kit-learn . org/stable/modules/generated/sk learn . base . transformer mixin . html # sk learn . base . transformer mixin

为垃圾邮件发送者编写“Kryptonite”📩~朴素贝叶斯过滤器

原文:https://towardsdatascience.com/coding-a-kryptonite-for-spammers-the-naive-bayes-filter-e533e59a681f?source=collection_archive---------36-----------------------

如何使用 Python 制作一个过滤垃圾短信的应用程序

“如果好得难以置信...很可能是诈骗。”罗恩·韦伯

cattu 在 Pixabay & USGS 在 Unsplash 上的照片

垃圾邮件通常定义为电子垃圾内容,来自各种通信渠道,如电子邮件、短信等。最常见的是,它来自于某种产品的广告,通过邮件列表甚至电话号码列表发送。

如今,真正的问题是双重的;垃圾邮件浪费了人们的时间,而它也“吞噬”了大量的网络带宽。因此,许多组织和个人采取持续和巨大的努力来反击。当然,数据科学也不能置身于战场之外…

这个项目的范围是展示数据科学的工具带如何应对反击垃圾邮件活动,并产生巨大的效果。在这个过程中,我们开发了一个监督 ML(机器学习)的案例研究,其中我们创建了一个朴素贝叶斯分类算法,该算法将消息(短信、电子邮件等)作为输入,检测并过滤掉垃圾邮件。

概念

这篇文章是上一篇文章的前传,在上一篇文章中,我们设法使用 Python 开发并改进了一个 Spotify 播放列表(而且仅仅是!).为了更好地回忆这一概念,当时“数据公司”为其年轻客户举办了一场派对(因此播放列表),以奖励他们在青年营销领域的销售额大幅上升。但是,这家公司是如何赢得这样一座奖杯的——这是它为了在如此苛刻的市场中茁壮成长而取得的成就?请想象以下场景…

Data Corp 董事会决定在“沟通”类别下推出一款新的手机应用,以提高年轻人市场的销量。为了击败竞争对手,一个潜在的竞争优势被仔细审查——反垃圾邮件。特别是,我所属的数据科学部门负责与工程团队联系,并为他们提供一种算法,该算法将用户的短信作为输入,并自动过滤掉垃圾短信。这种算法将被应用到新的应用程序中,并且应该达到至少 90%的准确率(竞争对手达到的标准)。

为了更好地交流结果,提出了一些假设:

1:遵守 GDPR 义务,公司不利用任何客户的信息。相反,它只处理公共可用的数据集,特别是上传到 UCI 机器学习库的垃圾短信收集。

2:我们将分析算法的“核心”,即过滤垃圾内容的机制。【app 开发属于工程团队】。

作案手法

为了完成我们的使命,必须遵循以下路线图:

  1. 简要说明需要的理论,以便必要的方程表达和编码。
  2. 设置执行代码所需的环境。
  3. 探索并准备数据集
  4. 使用熊猫、Numpy、NLTK &和一些额外的 Python 库来开发算法
  5. 执行算法分类新消息并测量其准确度

1.理论

朴素贝叶斯算法有几个变体,根据所做的数学和假设,我们区分以下 3 个最流行的版本:

  • 多项式朴素贝叶斯
  • 高斯朴素贝叶斯
  • 伯努利朴素贝叶斯

这次我们将讨论多项式算法。计算机学习人类如何将消息分类为垃圾邮件/非垃圾邮件(从现在开始称为“ham”),然后使用该知识来估计新(传入)消息的概率,并相应地对它们进行分类。

简而言之,当由w1, w2, …, wn个单词组成的新消息进来时,朴素贝叶斯算法试图通过利用集合#2 来估计和比较集合#1 的等式:

方程组#1

方程组#2

参数和变量的解释

如果P(Spam|w1,w2,…wn) > P(Spam*c*|w1,w2,…wn),该算法将该消息分类为垃圾邮件,反之亦然。要彻底研究以上方程式,你可以参考这个来源。

⚠️限制:

1:每个消息中的单词是有条件独立的,尽管它们经常处于依赖关系中。

2:每当我们处理不属于词汇表的单词时(参见“特征提取”部分),我们在计算概率时会忽略它们。

3:如果单词(即“money”)只出现在一个类别(即垃圾邮件)中,那么补码概率 P(垃圾邮件c|“money”)总是 0。为了避免这种影响,我们应用了一种称为加法*滑的技术,通过增加一个*滑参数α。我们将使用拉普拉斯*滑,通过设置 a=1(在这里阅读更多)。

2.建立

为了开发和运行该算法,以下组件/库是必不可少的:

  • 安装JupyterNotebook——一个开源网络应用程序,用于创建/共享包含实时代码、公式、可视化和叙述性文本的文档。你可以在这里按照步骤进行。
  • 安装NLTK——一个 Python 库,它提供了预处理和清理原始数据的有效模块(删除标点符号、标记等)。您可以使用 CLI(命令行界面)或 Jupyter 笔记本来运行以下命令:

install.py

  • 下载停用词——一组常用词(“the”、“a”、“an”等),它们在句子中频繁出现,但分量不大。
  • 下载Punkt——一个句子分词器,它将文本分成一系列句子。
  • 下载WordNet——一个大型的英语词汇数据库,其结构使其成为计算语言学和 NLP(自然语言处理)的有用工具。

下载. py

  • 导入必要的库:

import.py

3.数据集探索和分割

完整的数据集包括 5.572 条 SMS 消息(已经被人类分类),这些消息分布在垃圾邮件和垃圾邮件类别中,分别占大约 87%和 13%。描绘它的词云,我们可以得到主导词的另一种反映:

原始数据集 Wordcloud

我们将其随机化,以便保留原始的垃圾邮件/火腿比例,然后将其分成两个子集,选择 80% — 20%的分割比例:

  • training_set:用于“训练”计算机如何对信息进行分类
  • test_set:用于最终测试垃圾邮件过滤器的好坏(准确性)

dataset_division.py

数据集的垃圾邮件分布

显然,最初的垃圾邮件/火腿比例被有效地保留了下来。

4.算法开发

在 NLP 中,我们的目标是教会计算机根据上下文理解和操作人类语言,尽管它们只处理数字形式的数据。因为,我们将首先应用一些预处理技术,以便数据集留下具有高有意义权重的伪影。然后,我们将把数据集的消息编码成数字向量(特征提取)【1】。为了更好地了解整个过程,您可以参考以下流程图:

算法流程图

文本预处理

一般来说,根据参考文献[2],我们区分文本预处理的 3 个主要组成部分:规范化、标记化和替换。

(a)正常化

首先,文本需要规范化。这意味着采取一系列的行动,将文本中的单词放在同一个“基线”上,从而形成一个更加统一的过程。这一步包括删除标点符号,将单词转换成相同的大小写(大写/小写)等等。在我们的框架中,我们将通过 3 个不同的步骤来实现这一目标:

1。引理化:根据记号的引理获取其标准形式的过程,即更好。与词干提取相比,它更加复杂,通过考虑单词在句子中使用的上下文,它返回更有意义的单词。我们将利用 WordNet 的英语词汇数据库,它给出了显著的结果。

词条化. py

2。词干化:通过消除任何词缀(通常是带后缀的&前缀),将“屈折”的单词减少到其词根(词干)的过程。比如词干,词干→词干。这样,我们只保留了相似单词的语义,同时也实现了标记(单词)数量的总体减少。后者确保减少词汇量,因此占用更少的计算资源。有几种词干模型,我们将使用波特模型。

词干. py

3。自定义操作:根据具体情况,为进一步清理文本数据而采取的一组操作。对于这一条短信,我们去掉了地址、数字和符号($),去掉了标点(?!等等),折叠空白并将所有标记转换为小写。此外,我们删除了在句子中频繁出现的、对整体意思没有显著贡献的停用词(如 the、for、is 等)。

custom_normalization.py

(b)标记化

将一长串文本分割成小块(段落分割成句子,句子分割成单词)的过程。即文本中使用的标记列表,也被称为语料库。幸运的是,Python 有一个名为 NLTK(自然语言工具包)的包,其中包含大量有用的操作文本的函数。我们将使用 nltk.word_tokenize。

tokenization.py

(c)噪声去除

这是一个更加针对特定任务的步骤,因为它细化了非常特殊的数据集。例如,被网络丢弃的文本,应该被 HTML、XML 和标记“脚印”清理掉。在我们的例子中,我们处理纯短信文本,最终跳过这一部分。

为了更好地感受上述技术的效果,让我们将它们全部应用于一个特定的 SMS,并获得最终结果:

# Original Message
I would but I’m still cozy. And exhausted from last night.nobody went to school or work. Everything is closed.# Normalization
would still cozy exhausted last went school work everything closed# Lemmatization
would still cozy exhaust last go school work everything close# Stemming
would still cozi exhaust last go school work everyth close# Tokenization
[would, still, cozi, exhaust, last, go, school, work, everyth, close]

⚠️:在每一个单独的案例中,应检查实施一种或多种上述技术[停用词、词干等]之间的权衡。例如,我们可能会跳过词汇化而支持词干化。

特征提取

ML 算法不理解纯文本形式的文本,但是期望输入是数字形式的。因此,有必要将我们的最终记号编码成数字——也就是特征向量——这个过程被称为矢量化【3】。在可用的类型中,我们选择使用计数矢量化。这个函数创建了一个矩阵,其中包含每个数据集行(SMS)中每个惟一令牌的计数。

矢量化. py

final_training_set样品

提到普通的文学作品,你可能会想到 BoW 这个词。也就是说,本质上,在分类方法中通常使用的模型,其中每个标记的出现频率被用作训练分类器的特征[4]。

常数和参数计算

首先,我们为每个新消息(红色标记)计算方程组中具有常数值的P(Spam)P(Spam*c*)NSpamNVocabulary:

从方程组#1 计算的常数

从方程组#2 计算的常数和参数

calc_constant.py

接下来,我们计算P(wi|Spam)P(wi|Spam*c*)值(蓝色标记)。注意,由于它们依赖于不变的training_set,它们也保持不变。

calc_params.py

传入消息分类

计算完所有的参数和常数后,我们就可以创建垃圾邮件过滤器了。它可以很容易地被理解为一个函数,它接收一个新消息(w1, w2, …, wn),计算P(Spam|w1, w2, …, wn)P(Spam*c*|w1, w2, …, wn),比较它们并且:

  • 如果P(Spam*c*|w1, w2, …, wn) > P(Spam|w1, w2, …, wn),则将消息分类为 ham
  • 如果P(Spam*c*|w1, w2, …, wn) < P(Spam|w1, w2, …, wn),则将该邮件归类为垃圾邮件
  • 如果P(Spam*c*|w1, w2, …, wn) = P(Spam|w1, w2, …, wn),请求人工帮助

classify_message.py

现在最令人愉快的部分来了;使用新的 SMS 文本测试算法,这些文本还没有在培训部分使用过。第一个来自广告内容,第二个来自私人谈话。

classify_spam_sms.py

# Output
P(Spam|message): 3.2268434039363386e-33
P(Ham|message): 3.3664743866615654e-34
Label: Spam

classify_ham_sms.py

# Output
P(Spam|message): 1.5519061681677179e-27
P(Ham|message): 7.759653287935314e-23
Label: Ham

幸运的是,两者都被成功标记!

5.算法实现和精度测量

最后,我们尝试通过对test_set的 1.114 条消息进行分类来确定过滤器的性能。该功能应用于每个新的 SMS,并将其标签注册到新列sms_predicted

分类测试设置

Results 
-------
Valid: 1087
Invalid: 27
Accuracy: 0.9758

我们的垃圾邮件过滤器检查了 1.114 条未知消息(在培训中未看到),并成功对 1.087 条进行了正确分类。几乎 97.6%的测量精度高于公司的目标(90%),因此,我们的模型将被部署到生产中!

结论

沟通渠道不断受到欺诈机制的攻击,这些机制往往会从个人和组织那里“窃取”时间和金钱。也就是说,数据科学能够提供有价值的解决方案。早在 2015 年,谷歌就已经在阻止垃圾邮件到达收件箱方面达到了 99.9%的准确率[4]。最重要的是,通过使用它的“神经网络”[5],它全速前进以消除剩下的 0.1%的误差!

然而,数据科学的卖点依赖于其提供有价值的解决方案的能力,即使是在业余用户的初级水*上。本文开发了这样一个初步的案例研究,并通过训练一个****分类模型进行求解,最终达到了 97%的显著准确率。总而言之:

(a) 我们处理了一组信息,并通过应用:

✔️ 文本 预处理【规范化、标记化】

✔️ 特征提取【矢量化】

我们设法将它们转换成编码向量——对机器来说是小菜一碟,可以理解并从中推断出有意义的价值。

(b) 我们根据贝叶斯定理,通过计算各自的概率来训练我们的模型。

(c) 最后,通过在test_set上应用该算法,我们评估了该模型有效标记新消息的能力。

最终,尽管我们承认垃圾邮件结构的存在和活动以我们的时间和金钱为代价,但我们应该永远记住,数据科学已经对人类有利…

由 Arian Darvishi 在 Unsplash 上拍摄的照片

此外,正如 Mokokoma Mokhonoana 恰当地引用的那样:

垃圾邮件浪费了接收者的时间,也浪费了发送者的乐观。”

感谢您的阅读!Jupyter 笔记本准备立即运行。如果有任何问题,请在下面留言。祝你度过愉快的一周,呆在家里,保持乐观,保证自己和你的inbox的安全!

参考文献

[1] H. Red,使用自然语言处理构建短信垃圾邮件过滤器 (2017),博客“在我们信任的机器中”

[2] M. Mayo,文本数据预处理的一般方法 (2017),KDnuggets

[3] N. Kumar,NLP 中原始数据的预处理:标记化、词干化、词条化和矢量化(2019),在线出版物“专业人士观点”

[4] G. Starr,谷歌用人工智能对抗垃圾邮件 (2015),独立国际新闻机构《基督教科学箴言报》

[5] A. Mordvintsev,C. Olah,M. Tyka,概念主义:深入神经网络 (2015),谷歌人工智能博客

编写一个 Web 应用程序来模拟赌徒的破产问题

原文:https://towardsdatascience.com/coding-a-web-app-to-simulate-the-gamblers-ruin-problem-1bc82660a878?source=collection_archive---------37-----------------------

如何使用 Plotly Dash 和 Heroku 编写和部署 Python Web 应用程序

我发现模拟是理解数学概念的一种有用的方式,所以我最*编写了一个程序来说明赌徒的破产问题。我做了一个 web app ,用 Python 模拟了一系列游戏及其结局。在这个 web app 中,用户定义一组参数(每轮成功的概率( p)、初始金额 (i)、目标金额( N) 和游戏数),它将返回每场游戏的获胜概率以及每轮结束时的余额,如下所示。

p=0.5,i=10,N=50,n_games=30 的模拟结果

在本文中,我将介绍如何编写一个赌徒破产模拟 web 应用程序,分为 3 个步骤:I)用 Python 编写模拟程序,II)使用 Plotly Dash 可视化结果,III)在 Heroku 上部署 web 应用程序。

浅谈赌徒的破产问题

赌徒的破产是一个著名的统计概念,在现代金融中有实际应用,如股票期权和保险费的定价。其概念如下:

你带着最初的$ I,走进赌场,希望将你的财富增加到$ N。你选择一个赌博游戏,其中你每一轮的成功概率是 p ,我们假设每一轮都是独立同分布的。如果这一轮你成功了,一个单位会加到你的余额中。如果你输了那一轮,你的*衡中会减少一个单位。当您的余额为 0 美元或达到\(N 美元时,游戏结束。您赢得/达到\)N 美元目标的概率是多少?

I)用 Python 编写模拟代码

我写了一个GamblersRuin类用于模拟目的。初始化对象时,以概率 p、初始量 ig oal 量 N 为自变量。

初始化该类后,您可以调用n_simulations函数,在这里您可以指定模拟游戏的数量,如果没有,它将默认为 100。这个函数本质上是一个 for 循环,它玩预定义数量的游戏并调用gamble函数。它返回一个数据帧,显示每局游戏的每轮余额,稍后用于绘制结果。

至于上面提到的gamble函数,如果余额在 0 到 N 之间,它会生成一个 0 到 1 之间的随机浮点数如果这个随机数小于概率 p ,那么你的余额就会增加一个单位,否则你的余额就会减去一个单位。该函数返回一个数据帧,其中包含生成的随机浮点和每轮的余额。

II)用虚线显示结果

在用 Python 编写了赌徒的破产类方法后,我们现在可以调用它并使用 Plotly Dash 可视化结果。这可以分为三个部分:a)启动 Dash 应用程序,b)设置应用程序的布局,c)指定回调函数,也就是要显示的输入和信息。

II.a .)启动 Dash app

Plotly Dash 应用程序使用 Flask 作为 web 框架。虽然您可以将自己的 flask 应用程序实例传递到 Dash 中,但我保持它的简单性,用app.server调用底层 Flask 应用程序。

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] app = dash.Dash(__name__, external_stylesheets=external_stylesheets) server = app.server

III.b .)设置布局

为了编写 Dash 应用程序的布局,我使用了dash-html-components库,该库将 web 框架抽象为 Python 代码。这些组件在幕后被转换为 web 应用程序的 HTML,您也可以向类似style的 Python Dash 类添加 HTML 元素来定制您的布局。

除了使用dash-html-components定制我的应用布局,使用dash-core-components (dcc)库添加不同的组件也很容易。我使用dcc.Markdown格式化文本,dcc.Input将用户指定的输入作为我的GamblersRuin类的参数,使用dcc.Graph显示结果。

III.c .)回调函数

最后一步,我根据用户提交的输入指定了必要的回调。我还编写了update_graph函数来更新获胜计算的概率和要显示的图形,一个 Plotly 线图。

您现在可以在本地机器上测试 Dash 应用程序了!如果您运行 app.py 文件,Dash 应用程序现在应该在您的本地主机上运行。最后一步是将应用程序部署到服务器。

IV)在 Heroku 上部署应用程序

使用 Heroku 的部署过程相对容易。查看这本指南部署到 Heroku。

  • 首先,你需要创建一个 Heroku 账户。
  • 用运行 Dash 应用程序和 python 文件的包定义一个requirements.txt文件。
  • 定义一个Procfile(repo 根目录下的文本文件)来指定启动应用程序的命令。
web: gunicorn app:server
  • 指定要部署的 github repo 和分支。您可以使用 heroku CLI 或他们的 web 界面来实现这一点。
  • 部署!

五)总结

在本教程中,我介绍了如何编码、可视化和部署一个赌徒破产模拟 web 应用程序。以下是这些步骤的简要总结:

  1. 将 Python 中的模拟编码为一个类方法,该方法将 p,I,N 作为参数,并具有运行预定义数量的模拟的函数。
  2. 使用 Plotly Dash 可视化模拟的结果和结局。
  3. 在 Heroku 上部署 Dash 应用程序。

这个网络应用程序从开始到结束花了我大约 5 个小时。我希望这篇文章已经激发了你去思考你可以编码的模拟,请随时在下面评论你的想法!

参考

  • 赌徒的毁灭冲刺 App
  • Github 回购
  • 使用 Python 在 Heroku 上部署
  • 赌徒的破产问题(卡尔·西格曼的笔记)

使用 MySQL 编码和实现关系数据库

原文:https://towardsdatascience.com/coding-and-implementing-a-relational-database-using-mysql-d9bc69be90f5?source=collection_archive---------3-----------------------

Craig Dickson 的 SQL 教程

学习使用 SQL 和免费开源软件创建、更新和查询您自己的全功能关系数据库——第 2 部分

国际语言学校数据库的实体关系图(ERD)

这是一个由 3 部分组成的系列文章的第 2 部分,从零开始,带您了解设计、编码、实现和查询关系数据库的过程。参见第一部分( 设计关系数据库并创建实体关系图 ) 此处 ,以及第三部分(MySQL 中的数据分析—关系数据库中的运算符、联接及更多 ) 此处

本教程的所有代码和信息都可以在相关的 GitHub 资源库 中找到。我使用了lucid chart来制作文章中显示的图表。

在本系列的第 1 部分中,我们介绍了从数据库的概念到制定计划,最后创建一个实体关系图,列出我们需要使用 SQL 代码建模的关系。我们讲述了关系数据库理论的基础,讨论了主键、外键、基数和许多其他真正有用的材料。

在开始这篇文章之前,阅读那篇是个好主意,但是如果你喜欢直接进入代码,你也可以阅读这篇文章。

构建数据库

好吧!现在我们要开始真正有趣的东西了,实际上是使用 SQL 创建和填充我们的数据库。

正在设置

在我们深入研究这个之前,我们需要在我们的系统上安装 MySQL 社区服务器,如果它还没有安装的话。安装比安装典型的应用程序稍微复杂一点(至少在 Windows 上是这样!),但是我们可以处理。

首先进入下载页面,获取您系统的安装程序。如果你使用的是 Windows,这个指南将帮助你进行设置。这里也有针对 Mac 和 Linux 用户的指南(尽管可能会因 Linux 发行版而异)。完整的演练超出了本文的范围,但是您可以这样做。我相信你!

一旦你设置好了,我们就可以在 MySQL 命令行客户端中做接下来的所有事情。

老派。强大。

命令行客户端非常强大,是学习的基础。然而,有时使用 GUI 客户端让我们的生活变得更简单也是件好事。

PopSQL 是 SQL 的一个很好的 GUI 应用程序,它使事情看起来更好,并且提供了非常有用的功能,比如在左侧导航窗口中查看数据库模式的能力,以及一些基本的数据可视化和方便的导出功能。

PopSQL 接口

对于专业用途是收费的,但也有一个免费层,对于学习和使用 SQL 肯定是足够的。还有许多和其他 GUI 选项,所以如果一个替代选项听起来很吸引人,就让他们试一试。然而,在本文的其余部分,我们将使用 PopSQL,所以截屏将来自这里。

要回答关于我们在这里使用的 SQL 语句的任何进一步的问题,最好的步骤是看一下 MySQL 文档。这是由开发人员制作的官方资源,包含详细而全面的信息。虽然一开始看起来有点吓人,但是阅读文档是一个很好的习惯。只需搜索感兴趣的陈述或主题,看看会出现什么。就像所有与代码相关的事情一样,谷歌(或者你选择的搜索引擎)是你的朋友!

最后,一些代码!

设置好环境后,我们需要在 MySQL 服务器上创建一个数据库。这并不太复杂,我们只需使用 CREATE DATABASE 语句即可。请注意,所有 SQL 语句都以分号“;”结束。这让客户端知道这是我们语句的结尾,这在编写更长更复杂的语句时特别有用,比如嵌套查询、,但对于所有语句都是必不可少的(除了 USE 命令,我们接下来将看到它)。

CREATE DATABASE school;

不错!我们刚刚创建了一个名为school的数据库。难以置信的工作。

下次我们登录 MySQL 命令行客户端时,我们将需要输入我们的密码,然后选择我们想要使用的数据库。为此,我们使用使用命令:

USE school

成功!

就这么简单。如果我们使用 GUI 软件,我们通常不必每次都执行这一步。

注意,按照惯例,我们将关键字(如选择,删除并创建数据库)放在大写字母中,其余代码放在小写字母中。SQL 实际上是不区分大小写的,所以代码完全以小写或大写形式运行(如果你真的需要,可以用模仿海绵宝宝 meme 风格)。这个约定是为以后阅读您的代码的人准备的,强烈建议在编写任何风格的 SQL 时遵循这个惯例。

数据类型

我们已经完成了创建 ERD 和定义实体(将在数据库中以表的形式创建)及其关系的工作,这将极大地简化数据库的构建。在用 SQL 创建表之前,我们需要做的最后一步是计划每个属性需要的数据类型。这必须在创建表时声明,所以我们在采取这一步骤之前需要考虑一下。

MySQL 支持广泛的数据类型,从简单的整数和字符串到blob和JSON。我们将在数据库中使用其中的一小部分。

我们将使用以下代码:

  • INT —这是一个整数,一个整数。我们主要将它用于我们的 ID 字段(这将是我们的主键)。
  • VARCHAR —这是一个长度可变的字符串字段,我们将使用它来存储文本。我们需要告诉 RDBMS VARCHAR 有多长,因此我们可以定义 VARCHAR(20)或 VARCHAR(60),前者最多存储 20 个字符,后者最多存储 60 个字符。更大的字符串允许我们存储更多的数据,但会占用更多的内存。
  • 日期 —这是一个日期字段,以 YYYY-MM-DD 格式存储。
  • BOOLEAN —从技术上来说,MySQL 不存储布尔值(真/假),但它允许使用它所谓的“ boolean literals ”来存储它们,对于真,它评估为 1,对于假,评估为 0。

定义表格

记住这一点,我们可以开始为我们的表定义适当的数据类型。

教师表的类型定义

这里我们已经定义了我们的 teacher_id 为 INT,first_name 和 last_name 为 VARCHAR(40),language_1 和 language_2 为 VARCHAR(3),dob 为 DATE,tax_id 为 INT,phone_no 为 VARCHAR(20)。完全可以选择不同的值,甚至不同的数据类型(也许您所在国家的 tax_id 包含文本字符?那么一个 INT 就不适合你),这都是数据库设计艺术的一部分。

为了在我们的数据库中创建这个表,我们将使用下面的代码:

CREATE TABLE teacher (
  teacher_id INT PRIMARY KEY,
  first_name VARCHAR(40) NOT NULL,
  last_name VARCHAR(40) NOT NULL,
  language_1 VARCHAR(3) NOT NULL,
  language_2 VARCHAR(3),
  dob DATE,
  tax_id INT UNIQUE,
  phone_no VARCHAR(20)
  );

我们在这里使用的 SQL 语句就是 CREATE TABLE 。接下来是我们要给表起的名字,然后是括号、属性名和相关的数据类型。

限制

我们还向表中的一些属性添加了一些约束。约束指定了表中数据的规则,并将约束 RDBMS 允许我们对该特定属性做什么。

我们已经将 NOT NULL 添加到 first_name、last_name 和 language_1 中,这意味着该表不接受其中任何属性设置为 NULL 的记录。这些属性要求每个记录都有一个非空值。这是有道理的,因为我们的老师会要求一个名字和一个姓氏,并且在语言学校教学,他们需要能够提供至少一种语言。

我们还将 tax_id 设置为唯一的。这意味着每个记录的这个属性必须有不同的值,这对于一个税号是有意义的,但是对于我们的 language_1 字段就没有意义了,例如,我们可能有多个教师提供相同的语言!

我们还将 teacher_id 字段设置为主键。实际上,这只是 NOT NULL 和 UNIQUE 的组合,但是为每个表定义一个主键(也可以是单个属性或不同属性的组合)是很重要的。

如果我们现在尝试使用一个SELECT * FROM *table*语句来查看我们的表中有什么数据(我们将在第 3 部分中更详细地查看这个语句),我们应该收到“空集”作为我们的响应(在 MySQL 命令行客户端中)或“没有找到结果”(在 PopSQL 中)。这表明我们的表存在(否则我们会收到一个错误),但仍然是空的。正如我们所料!

请注意左侧导航栏中关于该表的详细信息

删除表格

现在我们已经创建了我们的第一个表,让我们删除它!

对破坏的热情也是一种创造性的热情。理解在 MySQL 中删除某个东西是多么容易的一个好方法(以及为什么我过去如此担心会不小心这样做),就是继续去做。这也是克服这些顾虑的最佳方式,因为事实上这是相对难以做到的。

相关的 SQL 语句是 DROP TABLE 。

DROP TABLE teacher;

去吧,做吧!是解放!

请注意,没有有用的“您确定要这样做吗?”弹出的对话框。也没有撤销按钮,只有一个简单的“查询成功”或“成功”消息,表就不见了。当表包含数据时也是如此。所以 SQL 是一种非常强大的语言,但它也是不可原谅的。当你删除或更新表格时,确保你已经考虑清楚你在做什么!

在进入下一部分之前,不要忘记恢复教师表。

创建剩余的表

我们剩下的三个实体剩下的三个表

这里我们再次使用相同的数据类型,唯一的新数据类型是 in_school 的 BOOLEAN。如果课程在国际语言学校进行,则为真,如果在其他地方进行,即在客户的办公室进行,则为假。

创建这些表的代码如下:

CREATE TABLE client (
  client_id INT PRIMARY KEY,
  client_name VARCHAR(40) NOT NULL,
  address VARCHAR(60) NOT NULL,
  industry VARCHAR(20)
);CREATE TABLE participant (
  participant_id INT PRIMARY KEY,
  first_name VARCHAR(40) NOT NULL,
  last_name VARCHAR(40) NOT NULL,
  phone_no VARCHAR(20),
  client INT
);CREATE TABLE course (
  course_id INT PRIMARY KEY,
  course_name VARCHAR(40) NOT NULL,
  language VARCHAR(3) NOT NULL,
  level VARCHAR(2),
  course_length_weeks INT,
  start_date DATE,
  in_school BOOLEAN,
  teacher INT,
  client INT
);

现在我们每个实体都有一个表了,太好了!

下一步是通过设置外键来建立它们之间的关系。令人高兴的是,当创建我们的 ERD 时,我们已经想到了这一点并计划好了这些方法,所以现在我们只需要将它们放入正确的语法中,并使用 ALTER TABLE 语句更新我们的表。

ALTER TABLE participant
ADD FOREIGN KEY(client)
REFERENCES client(client_id)
ON DELETE SET NULL;ALTER TABLE course
ADD FOREIGN KEY(teacher)
REFERENCES teacher(teacher_id)
ON DELETE SET NULL;ALTER TABLE course
ADD FOREIGN KEY(client)
REFERENCES client(client_id)
ON DELETE SET NULL;

注意,这些可以在 CREATE TABLE 步骤中添加,但是这需要对我们的语句顺序进行更好的规划,因为在创建两个表之前,我们不能在 MySQL 中的表之间创建关系。在初始表创建之后使用 ALTER TABLE 有助于我们将这些步骤分开,并且可以稍微宽容一些,但是了解这两种方法都是有用的。

让我们仔细看看第一个命令,看看我们在做什么:

ALTER TABLE participant
ADD FOREIGN KEY(client)
REFERENCES client(client_id)
ON DELETE SET NULL;

在这里,我们更新了参与者表,并创建了一个关系(ADD FOREIGN KEY),其中参与者表上的属性/列客户端引用了客户端表上的客户端 id 属性/列,正如我们计划的一样。这建立了这些表之间的关系,并使我们的数据库成为一个关系数据库。万岁!

我们在这里还有一个新的约束——删除集合 NULL 上的。这告诉 MySQL 当客户端表中的记录被删除时我们想要做什么——在这种情况下,该参与者的客户端属性的值将被设置为 NULL,但是该记录将保留在参与者表中。另一个选项是删除级联上的。如果我们在这里使用它,那么当从客户机表中删除一个记录时,通过这个外键关系链接到该客户机的所有参与者也将被删除。

在这里,ON DELETE SET NULL 是更保守的选择,但是在许多情况下,有很好的理由使用 ON DELETE CASCADE。同样,这是数据库设计的一部分,考虑到这种类型的决策对数据库的影响。当我们处理更大的数据库时,这些决策也会影响数据库的性能,这也是我们在决策时需要考虑的因素。

我们的 N 对 M 关系表

如果您还记得,早在第 1 部分中,当我们设计数据库和创建 ERD 时,我们关注的最后一点是参与者和课程之间的多对多关系。这种关系不同于我们到目前为止遇到的 1 对 N 关系,因此需要在我们的数据库中进行不同的处理。

在 1 对 N 的关系中,比如我们的教师和课程表之间的关系,在关系的 N 端的实体中添加一个外键就足够了。我们在上面这样做了,我们将课程表的教师列引用到教师表上的教师 id 列。

ALTER TABLE course
ADD FOREIGN KEY(teacher)
REFERENCES teacher(teacher_id)
ON DELETE SET NULL;

这意味着任何课程只能由一个老师教,但一个老师可能会教很多课程。外键很好地使我们能够在数据库中捕获这种关系。

然而,就参与者和课程而言,这是行不通的。一门课程可以由多名参与者参加,一名参与者也可以参加多门课程。如果不创建记录的多个副本,向这些表中的一个或两个表添加外键将无法捕获这些表之间的所有关联。

为了捕获关系数据库中的 N-to-M 关系,我们必须创建另一个表,该表连接关系中涉及的实体的主键,以及存储在此处有意义的任何其他有用信息。

对于我们的例子,我们将创建一个名为 takes_course 的新表:

这是我们的第一个组合键!我们将两列 participant_id 和 course_id 一起设置为该表的主键,因为只有这种组合是唯一的。请注意,其中每个都是各自表中的主键。我们在这个表中使用的属性必须唯一地标识各个表中的记录,因此主键列通常是最好的选择。

我们只使用这两列,因为我们没有任何额外的数据存储在这里。情况可能并不总是如此,例如,我们可能有一个客户表和一个销售人员表,我们可能希望将每个销售人员对每个客户的总销售额存储在捕获他们关系的表中。这将是完全有效和可能的,但在我们的例子中,我们感兴趣的是参与者参加课程的事实,关于参与者和课程的所有其他细节更符合逻辑地存储在它们各自的表中。

让我们创建表格!这一次我们将同时添加约束,因为我们所有的其他表都已经存在。

CREATE TABLE takes_course (
  participant_id INT,
  course_id INT,
  PRIMARY KEY(participant_id, course_id),
  FOREIGN KEY(participant_id) REFERENCES participant(participant_id) ON DELETE CASCADE,
  FOREIGN KEY(course_id) REFERENCES course(course_id) ON DELETE CASCADE
);

在这里,我们将两列participant_idcourse_id作为主键,用圆括号括起来,用逗号隔开。这就是我们如何在 MySQL 中创建一个组合键。

还要注意,这次我们使用了 ON DELETE CASCADE。这在这里是有意义的,就好像从数据库中删除了一个参与者,我们不需要继续存储他们正在学习的课程,同样,如果一个课程被删除,我们也不再需要存储参加该课程的参与者。

现在,我们已经在一个全功能的关系数据库中创建了所有的表。非常棒的作品。

ERD 的另一种格式,显示我们的表和我们用 SQL 编码的各种关系

填充数据库

现在我们已经准备好了数据库,定义了关系,就等着开始行动了。我们只需要添加教师、课程、客户和参与者,我们就拥有了一个全功能的关系数据库,这是我们从头开始构建的!

为了用数据填充我们的表,我们需要使用 INSERT INTO 语句,后跟我们希望插入括号中的数据。例如,要填充教师表的第一行,我们将使用以下语句:

INSERT INTO teacher VALUES
(1,  'James', 'Smith', 'ENG', NULL, '1985-04-20', 12345, '+491774553676');

它给出了以下输出:

我们的第一个记录,填充!

这里语法是插入到表的值中(我们要插入的数据,用逗号隔开)。记录的每个属性(表中的每一列)都必须有一个值(或空值),否则 MySQL 会给我们一个错误。它们还需要与表中的列顺序相同,并遵循我们对列设置的任何约束(即,在设置为 NOT NULL 的列中没有 NULL 值,在设置为 UNIQUE 的列中没有重复)。

我们可以通过在列表中包含值来插入多条记录,用逗号分隔,如下所示:

INSERT INTO teacher VALUES
(1,  'James', 'Smith', 'ENG', NULL, '1985-04-20', 12345, '+491774553676'),
(2, 'Stefanie',  'Martin',  'FRA', NULL,  '1970-02-17', 23456, '+491234567890'), 
(3, 'Steve', 'Wang',  'MAN', 'ENG', '1990-11-12', 34567, '+447840921333');

也可以通过指定我们希望填充的列来执行部分插入,如下所示:

INSERT INTO teacher (teacher_id, first_name, last_name, language_1)VALUES (2, 'Stefanie',  'Martin',  'FRA');

运行这条语句会得到:

现在我们有两个记录,一个完整,一个部分。

注意,我们使用了 SELECT 语句来检索表的内容。这是 SQL 中最重要的语句之一,我们会一直使用它,任何时候我们都想从数据库中检索数据。

这里的格式是:

SELECT *FROM teacher;

在这种情况下,SELECT *表示“选择所有列”。例如,如果我们只对检索那些列感兴趣,我们也可以输入SELECT teacher_id, last_name, tax_id

删除记录

在填充其余数据之前,让我们看看如何从表中删除记录。我们使用从语句中删除来实现这一点。这里我们需要非常小心,就像我们没有在 DELETE FROM 语句中包含条件子句(使用 WHERE )一样,我们可以删除表中的所有数据。

DELETE FROM teacher WHERE teacher_id = 2;

这将删除所有满足条件“teacher_id = 2”的记录,因此只删除我们刚才为斯蒂芬妮·马丁创建的记录。果然:

再见,斯蒂芬妮!

这是我们第一次看到 WHERE 子句。当我们使用 SQL 时,我们会发现在 SELECT 语句以及 DELETE 和 UPDATE 语句中会有越来越多的使用。我们将在本指南的第 3 部分中对此进行更深入的探讨。

更新记录

我们还可以使用 UPDATE 子句对记录进行修改。这使用了语法更新 SET column_1 = value_1,column_2 = value_2 ,其中条件。

在我们这一桌,也许詹姆斯·史密斯结婚了,想跟他妻子姓。我们可以在 MySQL 中使用以下语句来进行更改:

UPDATE teacher
SET last_name = 'Jones'
WHERE teacher_id = 1;

记住 WHERE 子句,否则我们会将表中的所有姓氏都改为 Jones!运行该语句将为我们提供:

祝贺你,琼斯先生!

现在我们已经介绍了每一个 CRUD 功能——创建、读取、更新和删除。我们正在朝着能够对数据库做任何我们需要做的事情的方向前进!

让我们开始填充

既然我们已经看到了如何插入、删除和更新记录,那么让我们继续将剩余的数据放入数据库。你可以在这个 GitHub 库中看到我们将以 Excel 格式导入的数据集,以及我们在中使用过的所有 SQL 代码(加上一点我们没有使用过的代码)。SQL 文件在那里。

下面是用值填充数据库所需的 MySQL 代码(记得首先删除教师表中詹姆斯·琼思的条目!):

INSERT INTO teacher VALUES
(1,  'James', 'Smith', 'ENG', NULL, '1985-04-20', 12345, '+491774553676'),
(2, 'Stefanie',  'Martin',  'FRA', NULL,  '1970-02-17', 23456, '+491234567890'), 
(3, 'Steve', 'Wang',  'MAN', 'ENG', '1990-11-12', 34567, '+447840921333'),
(4, 'Friederike',  'Müller-Rossi', 'DEU', 'ITA', '1987-07-07',  45678, '+492345678901'),
(5, 'Isobel', 'Ivanova', 'RUS', 'ENG', '1963-05-30',  56789, '+491772635467'),
(6, 'Niamh', 'Murphy', 'ENG', 'IRI', '1995-09-08',  67890, '+491231231232');INSERT INTO client VALUES
(101, 'Big Business Federation', '123 Falschungstraße, 10999 Berlin', 'NGO'),
(102, 'eCommerce GmbH', '27 Ersatz Allee, 10317 Berlin', 'Retail'),
(103, 'AutoMaker AG',  '20 Künstlichstraße, 10023 Berlin', 'Auto'),
(104, 'Banko Bank',  '12 Betrugstraße, 12345 Berlin', 'Banking'),
(105, 'WeMoveIt GmbH', '138 Arglistweg, 10065 Berlin', 'Logistics');INSERT INTO participant VALUES
(101, 'Marina', 'Berg','491635558182', 101),
(102, 'Andrea', 'Duerr', '49159555740', 101),
(103, 'Philipp', 'Probst',  '49155555692', 102),
(104, 'René',  'Brandt',  '4916355546',  102),
(105, 'Susanne', 'Shuster', '49155555779', 102),
(106, 'Christian', 'Schreiner', '49162555375', 101),
(107, 'Harry', 'Kim', '49177555633', 101),
(108, 'Jan', 'Nowak', '49151555824', 101),
(109, 'Pablo', 'Garcia',  '49162555176', 101),
(110, 'Melanie', 'Dreschler', '49151555527', 103),
(111, 'Dieter', 'Durr',  '49178555311', 103),
(112, 'Max', 'Mustermann', '49152555195', 104),
(113, 'Maxine', 'Mustermann', '49177555355', 104),
(114, 'Heiko', 'Fleischer', '49155555581', 105);INSERT INTO course VALUES
(12, 'English for Logistics', 'ENG', 'A1', 10, '2020-02-01', TRUE,  1, 105),
(13, 'Beginner English', 'ENG', 'A2', 40, '2019-11-12',  FALSE, 6, 101),
(14, 'Intermediate English', 'ENG', 'B2', 40, '2019-11-12', FALSE, 6, 101),
(15, 'Advanced English', 'ENG', 'C1', 40, '2019-11-12', FALSE, 6, 101),
(16, 'Mandarin für Autoindustrie', 'MAN', 'B1', 15, '2020-01-15', TRUE, 3, 103),
(17, 'Français intermédiaire', 'FRA', 'B1',  18, '2020-04-03', FALSE, 2, 101),
(18, 'Deutsch für Anfänger', 'DEU', 'A2', 8, '2020-02-14', TRUE, 4, 102),
(19, 'Intermediate English', 'ENG', 'B2', 10, '2020-03-29', FALSE, 1, 104),
(20, 'Fortgeschrittenes Russisch', 'RUS', 'C1',  4, '2020-04-08',  FALSE, 5, 103);INSERT INTO takes_course VALUES
(101, 15),
(101, 17),
(102, 17),
(103, 18),
(104, 18),
(105, 18),
(106, 13),
(107, 13),
(108, 13),
(109, 14),
(109, 15),
(110, 16),
(110, 20),
(111, 16),
(114, 12),
(112, 19),
(113, 19);

不错!现在,我们所有的表都已经填充了数据,定义并实现了数据之间的所有关系,所有的属性都有适当的数据类型和约束,数据库已经准备好可以使用了。出色的工作!

在本文中,我们已经从 ERD 准备就绪的数据库计划,发展到在 MySQL 中完全实现和填充数据库。我们已经选择了数据类型,定义了所有字段,创建了表,并使用 MySQL 代码定义了它们之间的关系。

下一步是开始分析这些数据。请继续关注本指南的第三部分—MySQL 中的数据分析——关系数据库中的操作符、连接和更多内容 ,查看 MySQL 中功能强大的工具,以提取、操作和更新我们新创建的数据库中的数据。

非常感谢您抽出时间陪我踏上这段旅程。我随时欢迎您的反馈——请通过我的网站与我联系,让我知道我该如何更有效地做这件事!我总是乐于接受建设性的批评,或者你的任何意见。

下次见!

更像这样?访问craigdoedata . de

我作为初级开发人员所犯的编码错误

原文:https://towardsdatascience.com/coding-mistakes-i-made-as-a-junior-developer-e151dd3b3c7d?source=collection_archive---------2-----------------------

如果你的第一份工作是写代码,那么还有希望

照片由阿图尔·乔杜里从派克斯拍摄

你在软件工程或数据科学领域的第一份工作可能会令人沮丧。尤其是你没有后台写代码的情况下。

我经常收到人们的信息,询问如何改进。但他们真正需要的是有人告诉他们——“你能行!”

以下是我个人在第一份软件工程工作中犯的错误。如果你有困难的时候,这会让你感觉好一些。

1.编写聪明的而不是可读的代码

写好代码很难。理解糟糕的代码更加困难。但是当你开始的时候,这不是直觉。

谢天谢地,我有一个高级开发人员,他不止一次地向我提出了以下几点。

  • 同一行上有多个嵌套的if/else语句
  • 过度使用链式方法
  • 从堆栈中复制/粘贴的正则表达式溢出,没有注释
  • 过度抽象

把逻辑压缩到尽可能小的空间里,让我觉得自己很聪明。但这也让我的代码不可读。现在我总是试图在可读性方面做得更好。

调试比一开始写代码要难两倍。因此,如果你尽可能聪明地编写代码,从定义上来说,你没有足够的聪明去调试它。
——柯尼根定律

2.使用没有上下文的变量名

想出一个好的变量名出人意料的困难,我想尽快完成。

所以我会选择第一个出现在我脑海里的名字。

  • 一个用户的姓变成了uln
  • 一系列电子邮件变成了array

这两个都是糟糕的想法,让任何人都很难理解我写的东西(包括我自己)。

3.允许安全漏洞

这是另一种情况,我要感谢一位了不起的高级开发人员,他拯救了我的代码,使之免遭黑客攻击。

我完成了以下所有工作:

  • 被允许的 SQL 注入
  • 允许通过 URL 跳转访问受限页面
  • 仅使用前端验证
  • 带有增量 id 的命名空间 URL

我花了很长时间来建立一个最佳安全实践的心理清单,现在我在审查其他开发人员的代码时会用到它。

4.阅读功能标签后立即编写代码

在一个功能上花了一个星期,然后意识到它是错误的,这是令人尴尬的。我做过不止一次了。

深呼吸,理解业务问题,并围绕它规划代码,这对工程师来说是一个巨大的倍增器。

从这一点上,我让新开发人员在我自己的创业计划开始前详细开出罚单。这种层次的微观规划有助于理清思路,开发更有效的解决方案。

5.评论太多或太少

开始时,我什么也没说。

然后我经历了一个阶段,我评论每一行。一个名为add_two_numbers的方法可能已经用# adds 2 numbers进行了注释。这太过分了。

回想起来,直到我阅读了其他开发人员写的足够多的代码,并注意到我希望他们在哪里添加了注释,我才点击了适当数量的注释。

6.推送重复和未使用的代码

我完成了以下所有工作:

  • 已经存在于应用程序中的编写函数
  • 留下自动生成但未使用的文件(即:测试文件)
  • 添加了未使用的包

有些框架会自动生成大量不必要的文件。当你开始开发一个应用程序时,你也不知道所有现有的代码。

有趣的是,我发现避免这些问题的最好方法是在提交给评审之前仔细检查你写的代码。

7.编写低效的数据库查询

当我开始第一份工作时,我对数据库一窍不通。我大概花了一年时间才弄明白数据库索引。

在那段时间里,我编写了许多 N+1 查询,并创建了 db 表来存储大量没有索引的数据。

这两者都是令人讨厌的缓慢应用的秘诀。

8.使用基于错误的条件逻辑

条件if/else语句是软件的核心部分。

在伪代码中,它们通常是这样的。

if x is true
  do this
else
  do that

但是我为我的作品集写的第一个应用充满了这样的逻辑。

do this
if this fails
  do that

有时我们需要挽救一个错误,比如遇到一个不可靠的 API。但这应该是例外而不是常态。

9.提交结合了多种特性的代码以供审查

我学到的第一件事就是不要在同一个拉请求中组合多个特性。对审查代码的人来说不太好。

超过几百行会让其他人很难在脑海中走过不同的执行路径。

有时,这是由于票证范围不当造成的。因此,我总是告诉新开发人员,如果他们认为一个票证可以进一步细分为子票证,就要推迟开发。越小越好。

结论

学习写软件很难。有一百个动人的片段,你只能通过做来学习。

如果你自己也在挣扎,我希望读到我的失误会让你感觉好一点。

对我帮助最大的是让一位高级开发人员对我提交的每一段代码给出详细的反馈。找一个你能得到它的公司或团队。这是最快的提高方法。

从头开始计算变异系数和协方差

原文:https://towardsdatascience.com/coefficient-of-variation-cv-covariance-cov-from-the-scratch-52600656147f?source=collection_archive---------8-----------------------

永远消除困惑!

阿德里安·斯旺卡在 Unsplash 上拍摄的照片

什么是“变异系数”,为什么我们需要它作为一种统计方法?

到目前为止(直到我的前几篇博客),我们是基于两个数字的总结来总结数据集的——集中趋势和扩散。我想让你思考一下,如果我们想要比较两个或更多数据集的波动性,现在用两个数字的汇总方法来做是否可行?

答案是,如果不在可比较的范围内(标准化),我们无法对它们进行比较。这就是为什么需要一个变异系数(CV)来确定哪个数据集相对来说更不稳定。

*变异系数没有单位。

给定数据集围绕中心趋势的分布与另一个数据集的分布度量不可比,因为后者是参考其自身的中心趋势计算的。为了将它们放在同一个比较尺度上,进行了除以它们的中心趋势,乘以 100 将测量值转换成百分比形式。我们将看到一个适当的例子,说明标准偏差测量本身是如何具有欺骗性的。

数据集:03 家公司的每周股价走势:

公司 1 的股价波动最大,CV 为 31.94 %,其次是公司 2 和公司 3。

单独看三家公司的标准差并进行比较,显然会导致错误解读。我希望现在你已经意识到在比较不同数据集之间的波动性时 CV 及其可靠性的重要性。

什么是“协方差”,为什么我们需要它作为统计指标?

每当数据中有一些信息是我们无法用之前讨论过的方法获取的,我们就会讨论一种新的方法来获取这些信息。根据相同的方法,我们将首先查看与之前讨论的测量方法看起来相同的数据集,但是在可视化时,我们将遇到一些模式,这些模式将使用额外的统计测量方法来捕获。然而,这一措施将导致统计的双变量领域,其中两个变量相互作用,并显示一些模式。到目前为止,我们只处理了一个变量的描述性统计(目标变量,即高度、股票价格),但现在我们将有额外的变量来尝试解释目标变量中存在的变化。协方差的需求和定义将在下面用一个适当的例子来说明:

(图片由作者提供)

如果我们试图获得上述两个数据集的目标变量(Y 轴)或预测变量(X 轴)的 2 个数总和,它们将具有相同的(相同的*均值和标准偏差),但很明显,它们都是不同的数据集。当一个变量相对于另一个变量作图时,斜率因子开始起作用,可见的斜率反映了两个变量递增时的共同运动

让我们将目标变量作为两个数据集的权重(Y 轴),

预测变量-数据集 1 的慢跑速度(X 轴)和数据集 2 的身高(X 轴)。

重量(Y 轴)= 70,80,90,100(千克)

设置 1 —点动速度(X 轴)= 170,180,190,200(单位为厘米/秒)

集合 2 —高度(X 轴)= 170,180,190,200(单位为厘米)

(图片由作者提供)

是时候看看这三个变量的 2 位数总结了:

仅仅通过看上面的总结,人们可能会认为这两套是完全相同的,但事实并非如此。协方差度量的时间投入,它将再次解释图形。

假设 4 个人有 3 个属性(体重、慢跑速度和身高):

(图片由作者提供)

剧情的基本解读:

-右上象限的点的 X 和 Y 值将大于 X *均值和 Y *均值

-左上象限的点的 X 值小于 X *均值,Y 值大于 Y *均值

-左下象限的点的 X 和 Y 值将小于 X *均值和 Y *均值

-右下象限的点的 X 值大于 X *均值,Y 值小于 Y *均值

这意味着,如果我们在将距离计算的参考从原点更改为各自的*均值后乘以一个点的 X 和 Y 坐标,右上象限中的所有点将具有正值(+、+),左上象限将具有负值(-、+),左下象限将具有正值(-、-),右下象限将具有负值(+、-)。

现在我们将对数据集 1 进行同样的计算:

将所有乘积求和= -225 + (-25) + (-25) + (-225) = -500

到目前为止,您可能已经了解了为什么这种方法有助于确定共同运动的方向(产品价值的负和—向下,产品价值的正和—向上)。我们现在也将计算数据集 2 的产品价值,以使事情变得非常清楚:

(图片由作者提供)

所有乘积相加= 225 + 25 + 25 + 225 = 500(正数表示向上的共同运动)

在我们可以正式地将这个度量称为协方差之前,我们需要通过将乘积输出的和除以数据点的数量(本例中 N = 4,假设为总体)来取其*均值。这也意味着你不能计算两个数据点计数不同的变量之间的协方差。协方差单位(Cov)是 X & Y 变量单位的乘积。

(图片由作者提供)

注意—上面完成的所有计算都假设数据集为群体集。讨论和辩论最多的话题与这种测量的分母有关,即为什么样本是 N-1,为什么人口是 N,将在未来的博客中全面解释。

最后,当人们使用它们的简短形式来引用这些统计度量时,最大的困惑出现了,最终混淆了“变异系数”的“Cov”和“协方差”的“CV”。克服这一点的最好方法是记住协方差的简短形式是它的词干版本:

这样你就永远不会混淆这两种不同的统计方法。

我的博客到此结束,希望预期的信息已经传达,与这两项措施相关的困惑已经永远消除。

谢谢!!!

咖啡豆脱气

原文:https://towardsdatascience.com/coffee-bean-degassing-d747c8a9d4c9?source=collection_archive---------24-----------------------

一些精彩的咖啡研究综述

2020 年 7 月,我开始更多地思考脱气和咖啡。我一直在尝试让我的咖啡烘焙品在使用前休息 3 或 4 周,我注意到味道和提取水*有所改善。我怀疑部分提取是由于二氧化碳的减少。

所以我开始了一个实验,看看我是否能使用真空罐更快地给豆子脱气,希望能更快地使用它们。在这个过程中,我开始了解外面有什么研究,我偶然看到了萨莫·斯默克和其他人关于这项研究的伟大论文和视频演示。

作者图片

我太激动了,我想在这里回顾一下这些信息。通常,我认为研究仅限于做研究的人,不做研究的人无法接触到。这些工作都不是我的。我的希望是,也许我可以展示最有趣的作品和一些图表,以不同的方式来看待数据,以帮助他人理解。你总是可以看视频或者读报纸,但是也许你只是想要图表。

对于演示文稿中的每个情节,我都编写了一个脚本来从原始情节中提取数据,以便我可以重新绘制它。我添加了一个额外的图表,显示脱气占总量的百分比,我发现这是一个有用的标准化。所有这些图表都包含原始数据,但有时,我会将 x 轴刻度改为我认为更有意义的位置,或者混合一些以前没有混合的数据。

不同的豆子

不同的豆子以不同的速率脱气。对于浓缩咖啡来说,这一点尤为重要,因为残留在咖啡豆中的气体量关系到一杯咖啡的流量。我怀疑这意味着不同的咖啡豆应该在不同的时间窗口使用,而不是像浓缩咖啡那样在 2 到 4 周内使用。

烘烤水*

烤得越黑,产生的气体就越多!这个数据的有趣之处在于,作为一个百分比,大多数烘焙水*都有相似的速率和模式。正如这项研究的作者指出的,大多数脱气(70%)发生在最初的 24 小时内。

烘烤程度与烘烤速度

他们观察了三种速度下的暗烤、中烤和亮烤。就百分比而言,无论速度如何,它们都遵循相似的趋势。

我混合了一些快速和慢速,给出了三个烘烤级别的概念。

同样,就百分比而言,无论烘焙程度或速度如何,它们都有相似的脱气模式。

烘烤速度

这项研究还关注了烘焙速度,烘焙速度越快,咖啡豆中释放的气体越多。

全豆 vs 烘焙研磨(R&G)

由于研磨过程中气体的损失,焙烧后立即研磨会显著改变气体的释放。

研磨枣 vs 烘烤

他们观察了一段时间内的两次烘烤,并每隔一段时间对烘烤进行取样研磨。他们发现的最有趣的事情是,在第四周,咖啡研磨后产生的气体比整颗咖啡豆产生的气体还多。他们推测这是由于一些气体被截留在咖啡中,这可以解释为什么几个月前的咖啡在煮浓缩咖啡时仍然有一些克莉玛。

不出所料,轻度烘焙的咖啡豆和磨粉在各个阶段的气体含量较低。

我们可以把烘焙后不同的几周排在一起,看着地面脱气是多么的有趣。

冻豆!

他们的研究非常有助于理解冷冻咖啡时会发生什么。演示文稿中的作者指出,他们不认为这个过程是冷冻咖啡豆,因为大多数烘焙咖啡豆中的水分很少。所以他们认为咖啡豆被冷却会减缓脱气。因此,冷冻是延长货架期的有效方法。你也可以冷冻和再冷冻豆子,而你通常不能冷冻其他食物。

他们观察了烘烤过的咖啡豆,并将其分为新鲜的、冷冻 1 周的和冷冻 2 周的。它们在指定的时间研磨,所有三种的脱气量遵循相同的模式,与新鲜相比略微下降 2 周。

他们在冰箱里观察了 70 天,他们发现冷冻的豆子和新鲜的豆子几乎完全一样,只是有一点偏差。

额外奖励:咖啡香味

为了观察咖啡的新鲜度,萨莫还对咖啡豆散发出的不同香味做了一些研究。具体的化学物质有以下几种味道。每一种都有不同的味道成分,因为香味是复杂的。

原来的图显示了下降,我把 X 轴调整为一周间隔。我的目标是得到一个图表和一些表格,帮助清楚地显示一周内还剩下多少香气。所以我也重新归一化了数据的最大值和最小值。

这张图表遗漏了二氧化碳。所以我从上面标题为“烘焙水*与烘焙速度”的章节中选取了一条脱气曲线我用的是中速烤。

相对于香气来说,二氧化碳脱气非常快,这可能解释了为什么制作浓缩咖啡的最佳时间是烘焙后的 7 到 21 天。我已经达到了 21 到 35 天的范围,因为我可以获得更高的提取水*(约 22%的提取率)。

这里有一些表格,带来更简洁的总结。在第一个例子中,你可以看到二氧化碳消耗最快,而甲硫醇消耗最慢。如果你看第二张表,这是每周的数据。2 周后,4 个香气参数中的 3 个仍接*起始点的 50%,而 CO2 非常低。6 周后,这些香味大多处于最低点。这并不意味着它们消失了,因为它们是相对于最大值的归一化值。

总的来说,我非常惊讶地发现了这项研究,我非常兴奋地摆弄着这些数据。这不会改变我对浓缩咖啡的看法,但它确实有助于对我用精确到 0.03 克的天*测量的排气量提供额外的解释。

我希望进一步的研究将有助于改进我烘焙和储存咖啡豆的方式。当然,这一领域的问题在于咖啡豆、烘焙机和冲泡方法的多样性。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。

我的进一步阅读:

解构咖啡:分割烘焙、研磨、分层以获得更好的浓缩咖啡

浓缩咖啡的预浸:更好的浓缩咖啡的视觉提示

咖啡的形状

搅拌还是旋转:更好的浓缩咖啡体验

香辣浓缩咖啡:热磨,冷捣以获得更好的咖啡

断续浓缩咖啡:提升浓缩咖啡

用纸质过滤器改进浓缩咖啡

浓缩咖啡中咖啡溶解度的初步研究

断奏捣固:不用筛子改进浓缩咖啡

浓缩咖啡模拟:计算机模型的第一步

压力脉动带来更好的浓缩咖啡

咖啡数据表

工匠咖啡价格过高

被盗浓缩咖啡机的故事

浓缩咖啡过滤器分析

便携式浓缩咖啡:指南

克鲁夫筛:一项分析

咖啡豆储存:真空与密封容器

原文:https://towardsdatascience.com/coffee-bean-storage-vacuum-vs-sealed-containers-c5d6a2b6f1b0?source=collection_archive---------21-----------------------

咖啡数据科学

通过 espresso 数据解封

当我开始在家里制作浓缩咖啡时,我最好的储存方式是在袋子上放一个夹子。随着时间的推移,我最终使用塑料袋,塑料容器和密封的梅森罐。我真的更喜欢梅森罐,因为它们看起来很好,而且摸起来很结实。然而,我不确定他们是否是储存咖啡的最佳选择,因为大多数咖啡都是用真空密封的袋子运输的。

我设计了一个简单的实验,在这个实验中,我会烘烤咖啡豆,并将烘烤的咖啡豆分别放在密封的梅森罐和真空密封的梅森罐中。然后我会磨出两个镜头,我这样做了几次烧烤的生活。对于每一个镜头,我都专注于味道和提取率(EY)。这对 EY 甚至拍摄时间没有太大的影响,但使用真空罐比密封罐味道肯定会有所改善。

从大部分咖啡用真空密封袋装运的方面来看,这并不是一个新的结论。然而,一旦人们打开袋子,咖啡豆通常不会保存在真空中。虽然我主张他们应该是因为味道更好。

真空罐

已经有一些专门用于咖啡的真空罐,但在调查之前,我决定买一个用于腌制食物的 T2 真空罐。它们是有真空盖的广口瓶,你把空气抽出来。它们既不优雅也不圆滑,当然也不适合垂直堆叠。

所有图片由作者提供;容器有多个塑料袋装的咖啡。

然而,它们工作了,我把它们*放起来,这样我就可以看到密封是否被破坏了。由于二氧化碳的释放,在烘烤后的前两三天,罐子必须每天重新密封一次。

性能指标

我使用了两个指标来评估镜头之间的差异:最终得分和咖啡萃取。

最终得分是 7 个指标(强烈、浓郁、糖浆、甜味、酸味、苦味和余味)记分卡的*均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水*。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难,会影响最终得分。

使用折射仪测量总溶解固体(TDS ),该数字用于确定提取到杯中的咖啡的百分比,并结合一杯咖啡的输出重量和咖啡的输入重量,称为提取率(EY)。

数据

我收集了 40 对样本,它们具有相同的咖啡豆、夯实压力、预浸泡、纸质过滤器等参数。这使我能够同时进行一些其他实验,同时仍然能够采集各种成对的样本。

数据分析

查看这些数据的第一种方法是使用配对数据来绘制散点图。对角线代表密封注射与真空注射相同的时间。

图表中使用的存储罐术语:

  1. 真空密封梅森瓶:真空
  2. 密封梅森瓶:密封(也称为控制)

总的来说,真空密封有更好的口味评分,但 EY 是两者差不多。我们可以根据烘焙来细分这些比较,并没有特别的趋势表明一种烘焙做得更好或更差。

让我们只关注更高的分数。许多较低的分数是在咖啡烘焙非常新鲜的时候,这意味着萃取率有点低且不均匀。

从这张图表中,模式变得非常清楚。通过烘烤和日期来看更多的数据,我们可以看到一些真空罐在最初几个镜头中表现不佳的地方。这里是按密封分数排序的照片,左边是分开的,右边是一起的。

我们还可以查看总溶解固体(TDS)与 EY 的关系,以此进行验证。看起来大多数照片都是用相似的值拍摄的,这意味着实验对变量的控制相当不错。

将最终得分与 EY 和 TDS 进行比较,有一个明显的趋势。味道很大程度上取决于高 EY 和 TDS,因为这是我喜欢的。但即使在这些图表中,真空数据点仍然比对照或密封瓶结果高一点。

将所有数据绘制在一起,我们可以对这些点使用多项式最佳拟合函数,它具有相当高的 R^2 度量。

我使用*均和最大味道分数进一步总结了结果:

在最高水*,从密封分数有统计学意义的分数偏移,但是在 EY 的差异没有统计学意义。

老化分析

衰老似乎很重要,所以我想删掉一部分关于衰老的内容。从散点图来看,除了味道(最终得分)更好之外,似乎没有什么别的了。

但是,当我们备份到统计级别时,我们会看到一些有趣的内容。基于年龄的不同,口味的差异也不同。EY 变化不大。

真空密封的咖啡渣

我还研究了在冰箱中使用真空密封来储存粉末。我以前发现储存在冰箱里的咖啡渣在几天后不会很快变坏,我怀疑真空罐会有帮助。

真空密封研磨料的主要挑战是,如果研磨料被吸入密封中,密封将无法正确关闭。所以我把它们储存在一个有裂缝盖子的容器里,这样空气就可以进出那个小容器,然后把它放进真空容器里。

我发现味道略有改善,EY 的味道有所下降。我的样本更少了,但继续以这种方式储存地面的后勤挑战使我想继续进行其他实验,而不是在接下来的 2 到 3 周内建立一个更大的数据库。

一些异常值是红色的,我在下面一点的一般统计表中把它们去掉了。

我将样本分类,绘制成下面的线条。

以下是包含所有数据的一般统计数据,以及一些被剔除的数据点。根据所有数据,最终得分的变化在统计上并不显著,但 EY 的变化是显著的。通过取出四个数据点来翻转。

总的来说,真空罐比普通的密封罐味道更好。咖啡的储存有很多选择,困难在于如何确定一种方法是否比另一种更好。

詹姆斯·霍夫曼确实制作了一个关于多个容器的视频,他并没有发现密封容器和真空密封容器有太大的区别。我怀疑咖啡拔罐和浓缩咖啡或其他冲泡方法之间有一些差异,但我想把数据放在我使用的储存方法上。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。

我的进一步阅读:

按地区、工艺、等级和价格分类的咖啡

家庭烘焙咖啡的经济学

咖啡豆脱气

解构咖啡:分割烘焙、研磨和分层以获得更好的浓缩咖啡

浓缩咖啡的预浸:更好的浓缩咖啡的视觉提示

咖啡的形状

搅拌还是旋转:更好的浓缩咖啡体验

香辣浓缩咖啡:热磨,冷捣以获得更好的咖啡

断续浓缩咖啡:提升浓缩咖啡

用纸质过滤器改进浓缩咖啡

浓缩咖啡中咖啡的溶解度:初步研究

断奏捣固:不用筛子改进浓缩咖啡

浓缩咖啡模拟:计算机模型的第一步

更好的浓缩咖啡压力脉动

咖啡数据表

浓缩咖啡的咖啡渣新鲜度

原文:https://towardsdatascience.com/coffee-grounds-freshness-for-espresso-78a447f84fcd?source=collection_archive---------29-----------------------

探索研磨和酿造之间的时间差

研磨后,你要等多久才能煮出浓缩咖啡?问一个咖啡师,任何一个咖啡师,我怀疑他们的回答是立即或者几分钟内。关于咖啡渣新鲜度的常识是,大约 30 分钟后,咖啡渣就变味了。即使储存了,大家都知道咖啡渣还是会变味(新鲜的时间长短没有明确定义)。总是这样吗?

我的断奏经验让我相信,关于咖啡渣新鲜度的普遍想法是不正确的。我找过支持数据,并不多,可能是因为它看起来太明显了。因此,我将分享我的经验和数据,这些数据表明,如果储存在密封的容器中,咖啡渣至少可以使用几天而不会失去味道。在过去的几个月里,我一直把我的辣饮料放在冰箱里,但在我定期冷藏咖啡渣之前,我也会回头看看。

以往的经验

有一段时间,我磨新鲜,但当我开始开发断奏浓缩咖啡镜头时,这种情况发生了变化。每一杯咖啡的研磨和筛选都是乏味的,所以我会研磨足够多的咖啡来喝几杯,我会筛选并把咖啡储存在密封的容器里。

我发现这种容器的好处是,即使我不打算筛选咖啡,我也会研磨咖啡并储存在里面。为了让我的过程更有效率,我会在早上研磨,一小时后,我会在上班的路上筛选。研磨和酿造之间的时间开始延长。

说到某些事情,我也很懒,有时我会把磨好的东西放在密封的容器里好几天。我没有注意到品味的下降。

数据收集

我开始记录研磨日期和时间只是当我开始记录研磨时豆的温度。我在 grind 用冷热豆做实验,我想我会跟踪它。我通常会一次磨四杯咖啡,这需要 2 到 4 天,取决于我喝咖啡的时间。

我开始查看这些数据,看看能否告诉我味道或提取率是否有任何变化。当我看不到变化时(如下图所示),我做了几个更长的实验,研磨 8 杯咖啡,喝一周。我还查阅了以前的数据,并根据筛过的粉末标注了估计的研磨日期,这些数据是我单独记录的。

这些镜头中的大部分都是使用第一次破裂后 1 到 1:30 分钟的家庭烘焙咖啡豆。我在 Rok 研磨机或 Lime 研磨机上研磨咖啡豆,这些数据来自于在 Kim Express 上拍摄的照片。此外,如果有人认为有一个更好的研磨机更重要,我会向他们推荐这项工作,我用了一个叶片研磨机和一个筛子来制作一杯好的浓缩咖啡,这表明筛子*衡了研磨机的领域。

绩效指标

我使用了两个指标来评估镜头之间的差异:最终得分和咖啡萃取。

最终得分是 7 个指标(强烈、浓郁、糖浆、甜味、酸味、苦味和余味)记分卡的*均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水*。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难,会影响最终得分。

使用折射仪测量总溶解固体(TDS ),该数字用于确定提取到杯中的咖啡的百分比,并结合一杯咖啡的输出重量和咖啡的输入重量,称为提取率(EY)。

数据结果

首先,看数据,我把所有的数据都标绘出来,我没有注意到任何东西。这几天,口味(最终得分)和 EY 肯定有变化,但我没有看到任何模式。我会注意到,由于辛辣研磨,我将大部分粉末储存在冰箱中,但我没有注意到在室温下储存一两天的粉末味道会变差。

所以我通过烘烤来分解它,我把时间窗口缩短到最多 3 天。同样,每一次烘烤似乎都不会随着时间的推移而变质。一天之内会有一些变化,这与我正在进行的一些实验的变化有关,但是没有一个趋势。

我决定简化数据。我把它切成两半,因为一半的数据的研磨时间不到 1 天 4 小时。我想在一天之内或一天以上完成,但这不是一个均匀的切割。在这种分裂中,有一些差异,但没有一个分布差异具有统计学意义。

我通常会研磨足够四个 18g 的镜头,我想在比以前更长的时间内测试研磨。我做了两次烘烤,我看到了变化,但没有趋势。咖啡渣也储存在冰箱里,这可能有助于保持它们的味道新鲜。

拍摄时间

拍摄时间似乎也不受研磨时间的影响。在不同的烘烤中,拍摄时间保持相当一致,我没有看到任何趋势。

更多数据

我拉了 359 张照片,我有研磨时间和拍摄时间,但随着数据的财富而来的是试图理解它的困难。所以我先把它画成散点图。没有明显的趋势。

然后,我绘制了箱线图,看起来最终得分或品味有所改善,但我怀疑这种改善是由于改善了拍摄参数。此外,分布非常广泛,因为镜头是跨不同的烘烤和技术。中值分数略有变化,但分布仍然非常相似。

为了提取学习,我通过烘焙将数据标准化(Z-norm)。对于每一次烘烤,我计算了*均值和标准偏差(std)。然后,对于每一个度量,每一次烘焙,我通过以下等式得到标准化数据:X _ norm =(X–烘焙 _ 均值)/烘焙 _ 标准。如果由于研磨时间而出现趋势,则应该清楚地显示为归一化分布的变化。

作为散点图,似乎没有太大的变化,所以让我们看看下面的箱线图。对于常规拍摄,最终得分保持不变,而 EY 增加了一点点。

对于断奏镜头,没有任何指标的趋势。在这种情况下,0 表示*均分数,正值表示高于*均值,负值表示低于*均值。

在怀疑研磨年龄并不像建议的那样重要后,我查阅了尽可能多的数据。我没有发现研磨年龄和多个指标(最终得分(味道)、EY 或拍摄时间)之间有任何积极或消极的趋势。研磨咖啡失去味道的主要警告最有可能是基于让它暴露在空气中一段时间,但至少从我的经验来看,研磨得更少并没有对我的浓缩咖啡体验产生负面影响。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。

我的进一步阅读:

解构咖啡:分割烘焙、研磨、分层以获得更好的浓缩咖啡

浓缩咖啡的预浸:更好的浓缩咖啡的视觉提示

咖啡的形状

搅拌还是旋转:更好的浓缩咖啡体验

香辣意式浓缩咖啡:热磨,冷捣以获得更好的咖啡

断续浓缩咖啡:提升浓缩咖啡

用纸质过滤器改进浓缩咖啡

浓缩咖啡中咖啡的溶解度:初步研究

断奏捣固:不用筛子改进浓缩咖啡

浓缩咖啡模拟:计算机模型的第一步

更好的浓缩咖啡压力脉动

咖啡数据表

工匠咖啡价格过高

被盗咖啡机的故事

浓缩咖啡过滤器分析

便携式浓缩咖啡:指南

克鲁夫筛:一项分析

按地区、工艺、等级和价格分类的咖啡

原文:https://towardsdatascience.com/coffees-by-region-process-grade-and-price-7e17c3c44baf?source=collection_archive---------36-----------------------

300+有等级和价格的豆子

我喜欢从甜甜的玛丽亚店里买青豆。我已经烤了 6 年了,我大部分的青豆都是从那里买的。不过,我很好奇 Q 分数与成本的趋势如何,所以我调出了他们所有的数据,做了一些简单的分析。简而言之,我发现一个总的趋势是成本,所以你必须支付更多的钱来获得更高得分的豆子,这应该是更高的质量。我还发现,某些功能与成本的关联比其他功能更大。

主要的警告是,这些豆子是基于甜蜜玛丽亚的采购,这在一定程度上是基于他们的客户群。因此,由于他们的策展,任何分析都不能超越甜蜜的玛丽亚。在多个卖家之间进行这种类型的分析会非常有趣,但他们的数据不像 Sweet Maria 的那样容易获取。

q 分数

Sweet Maria 的与 SCA 标准略有不同,总结如下:

原始数据

从甜甜的玛丽亚那里提取数据并不容易。他们没有数据库可以提取,但是他们有 300 多种豆子的档案。所以我同时观察它们,然后选择每一颗豆子进行比较。这给了我一个初始表,但我没有分数。

经 Sweet Maria's 许可分享的截图

对于 Q 分数,我点击图像进行评分,我对所有 300+的豆子都这样做了。

经 Sweet Maria's 许可分享的截图

从这里,我选择一切,并将其保存到数据表。

蜘蛛图的图像经 Sweet Maria 的许可共享

我使用这些数据将分数手动输入到一个新列中。我有机会看看其他数据,但我主要关心的是分数。这让我有了很好的干净的数据,但它确实需要一些时间和注意力。

作者图片

成本与质量分析

总的来说,我发现了 Q 值和价格之间的趋势,但并不总是这样。我观察了每个地区,非洲咖啡豆在 Q 值中明显占主导地位,但与成本和质量关系更密切的南美咖啡豆相比,它们的成本趋势更宽松。

我还研究了加工和品种类型。这里的主要趋势是干法加工的成本比湿法加工的成本有更强的相关性。波旁威士忌的种类也与价格有很大的关系。

最后,我查看了这些指标中成本和质量之间的相关性。相关性是一种理解两个变量有多么不同以及它们彼此趋势如何的方法。相关性可以是正的(成比例相关),也可以是负的(成反比相关)。

从地区来看,无咖啡因咖啡豆与价格呈负相关,但样本量很小。南美豆的质量和价格的相关性最高。

看看加工和类型,干加工比湿加工在质量和价格之间有更高的相关性,虽然其他一些数字在反弹,但他们几乎没有样本。

对于类型,波旁威士忌符合我们在图中看到的,这种传家宝在质量和价格之间没有相关性。

我查看了甜甜玛丽亚的咖啡库存,发现了等级和价格之间的一般关联。某些地区和加工类型的价格比其他地区高,但我很高兴地发现,当我花费更多时,我通常会获得更多的质量。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。

我的进一步阅读:

家庭烘焙咖啡的经济效益

咖啡豆脱气

解构咖啡:分割烘焙、研磨和分层以获得更好的浓缩咖啡

浓缩咖啡的预浸:更好的浓缩咖啡的视觉提示

咖啡的形状

搅拌还是旋转:更好的浓缩咖啡体验

香辣浓缩咖啡:热磨,冷捣以获得更好的咖啡

断续浓缩咖啡:提升浓缩咖啡

用纸质过滤器改进浓缩咖啡

浓缩咖啡中咖啡溶解度的初步研究

断奏捣固:不用筛子改进浓缩咖啡

浓缩咖啡模拟:计算机模型的第一步

更好的浓缩咖啡压力脉动

咖啡数据表

浓缩咖啡过滤器分析


  1. 0 ↩︎