TowardsDataScience 博客中文翻译 2020(七百零七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

利用 PySpark 和机器学习预测客户流失

原文:https://towardsdatascience.com/predict-customer-churn-with-pyspark-and-machine-learning-981d1eedb00b?source=collection_archive---------45-----------------------

客户流失是指客户终止与公司的关系。它对企业的健康和长期成功有相当大的影响,因为它可能会大大降低收入和利润。根据 Forrester research 的统计,获得新客户的成本是保持现有客户的 5 倍。因此,公司投入时间和资源来识别可能流失的特定客户,并在这些客户决定停止使用公司的服务之前解决他们的问题总是值得的。

在本文中,我们将探索一个名为 Sparkify 的虚构音乐流媒体服务的用户日志数据,并建立有监督的机器学习模型来预测客户是否有可能流失。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的数据集

这个项目有两个数据集:大小为 12GB 的完整数据集,以及大小为 128MB 的完整数据集的一个子集。我们将使用 128MB 版本的数据集,它足够小,可以放入本地机器的内存中,就像本文中的例子一样。在其他情况下,为了使用完整的数据集进行模型训练,我们可能需要在像 AWS 这样的云服务上部署一个集群。

让我们快速浏览一下数据集中的所有字段:

df.printSchema()root
 |-- artist: string (nullable = true)
 |-- auth: string (nullable = true)
 |-- firstName: string (nullable = true)
 |-- gender: string (nullable = true)
 |-- itemInSession: long (nullable = true)
 |-- lastName: string (nullable = true)
 |-- length: double (nullable = true)
 |-- level: string (nullable = true)
 |-- location: string (nullable = true)
 |-- method: string (nullable = true)
 |-- page: string (nullable = true)
 |-- registration: long (nullable = true)
 |-- sessionId: long (nullable = true)
 |-- song: string (nullable = true)
 |-- status: long (nullable = true)
 |-- ts: long (nullable = true)
 |-- userAgent: string (nullable = true)
 |-- userId: string (nullable = true)

页面字段为我们提供了更多关于用户行为的详细信息:

df.select('page').dropDuplicates().sort('page').show()+--------------------+
|                page|
+--------------------+
|               About|
|          Add Friend|
|     Add to Playlist|
|              Cancel|
|Cancellation Conf...|
|           Downgrade|
|               Error|
|                Help|
|                Home|
|              Logout|
|            NextSong|
|         Roll Advert|
|       Save Settings|
|            Settings|
|    Submit Downgrade|
|      Submit Upgrade|
|         Thumbs Down|
|           Thumbs Up|
|             Upgrade|
+--------------------+

因为 userId 是我们的目标变量,可以帮助我们唯一地识别用户,所以带有空 userId 的日志对于我们预测哪个客户会流失没有帮助。我们将从数据集中删除那些缺少 userId 的记录。

df = df.where(col('userId').isNotNull())
df.count()278154

探索性数据分析

在我们继续比较频繁用户和不频繁用户之前,我们首先需要考虑一下什么是频繁用户。一个狭义的定义可能只包括那些已经删除了他们的帐户的人,这在我们的数据中被捕获为页面特征取值为“取消确认”的情况。

我们将使用页面的取消确认事件来定义流失。我们将创建一个新的列“churned ”,为我们的模型标记 Churned 用户。

Number of users who have churned 52
Number of users who have not churned 173

我们可以看到,在这个数据集中,流失类和非流失类的数量不平衡。在这种情况下,我们稍后将使用 F1 分数来评估我们的模型,因为与准确度和精确度相比,它对类别不平衡不太敏感。

性别影响

there were 19 percent female users churned
there were 26 percent male users churned

影响等级(付费/免费)

there were 23 percent free users churned
there were 21 percent paid users churned

用户听歌时间的影响

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

播放歌曲数量的影响

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

登记后天数的影响

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

被浏览页面的影响

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显示每个用户页面事件数量的数据透视表

特色工程

现在我们已经熟悉了数据,我们构建了我们认为有前途的功能,并结合页面事件来训练模型:

user_df.printSchema()root
 |-- userId: string (nullable = true)
 |-- churn: long (nullable = true)
 |-- n_act: long (nullable = false)
 |-- n_about: long (nullable = true)
 |-- n_addFriend: long (nullable = true)
 |-- n_addToPlaylist: long (nullable = true)
 |-- n_cancel: long (nullable = true)
 |-- n_downgrade: long (nullable = true)
 |-- n_error: long (nullable = true)
 |-- n_help: long (nullable = true)
 |-- n_home: long (nullable = true)
 |-- n_logout: long (nullable = true)
 |-- n_rollAdvert: long (nullable = true)
 |-- n_saveSettings: long (nullable = true)
 |-- n_settings: long (nullable = true)
 |-- n_submitDowngrade: long (nullable = true)
 |-- n_submitUpgrade: long (nullable = true)
 |-- n_thumbsDown: long (nullable = true)
 |-- n_thumbsUp: long (nullable = true)
 |-- n_upgrade: long (nullable = true)
 |-- playTime: double (nullable = true)
 |-- numSongs: long (nullable = false)
 |-- numArtist: long (nullable = false)
 |-- active_days: double (nullable = true)
 |-- numSession: long (nullable = false)
 |-- encoded_level: double (nullable = true)
 |-- encoded_gender: double (nullable = true)

多重共线性增加了系数的标准误差。增加的标准误差,反过来,意味着一些独立变量的系数可能会发现没有明显不同于 0。换句话说,通过过度夸大标准误差,多重共线性使得一些本应显著的变量在统计上变得不显著。如果没有多重共线性(因此标准误差较低),这些系数可能很重要。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,有一些变量对的相关系数超过 0.8,这意味着那些变量是高度相关的。为了处理多重共线性,我们将尝试两种方法:

  • 手动移除相关要素
  • 主成分分析

手动移除相关特征

我们手动删除上一个热图中相关系数高的变量,并保留其余特征:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

 user_df_m.printSchema()root
 |-- userId: string (nullable = true)
 |-- churn: long (nullable = true)
 |-- n_about: long (nullable = true)
 |-- n_error: long (nullable = true)
 |-- n_rollAdvert: long (nullable = true)
 |-- n_saveSettings: long (nullable = true)
 |-- n_settings: long (nullable = true)
 |-- n_submitDowngrade: long (nullable = true)
 |-- n_submitUpgrade: long (nullable = true)
 |-- n_thumbsDown: long (nullable = true)
 |-- n_upgrade: long (nullable = true)
 |-- active_days: double (nullable = true)
 |-- numSession: long (nullable = false)
 |-- encoded_level: double (nullable = true)
 |-- encoded_gender: double (nullable = true)

用 PCA 变换特征

主成分分析(PCA)是一种统计分析技术,它将可能相关的变量转换为正交线性不相关的值。我们可以将它用于数据压缩等应用,以获得主导的物理过程,并获得机器学习的重要特征。在不丢失太多信息的情况下,它减少了原始数据中的要素数量。

应用 PCA 时,我们需要首先将特征组合成一个向量,并标准化我们的数据:

*# Vector Assembler*
user_df_pca = user_df
cols = user_df_pca.drop('userId','churn').columns
assembler = VectorAssembler(inputCols=cols, outputCol='Features')
user_df_pca = assembler.transform(user_df).select('userId', 'churn','Features')

*# Standard Scaler*
scaler= FT.StandardScaler(inputCol='Features', outputCol='scaled_features_1', withStd=**True**)
scalerModel = scaler.fit(user_df_pca)
user_df_pca = scalerModel.transform(user_df_pca)
user_df_pca.select(['userId','churn', 'scaled_features_1']).show(5, truncate = **False**)pca = PCA(k=10, inputCol = scaler.getOutputCol(), outputCol="pcaFeatures")
pca = pca.fit(user_df_pca)

pca_result = pca.transform(user_df_pca).select("userId","churn","pcaFeatures")

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

型号选择

为了选择一个好的模型进行最终调优,我们将在 Spark 的 ML 中比较三个不同的分类器模型:

  • 逻辑回归
  • 决策图表
  • 随机森林

将数据分为训练集和测试集

从上一节展示的流失分布中,我们知道这是一个不平衡的数据集,只有 1/4 的用户被标记为流失。为了避免随机分裂中的不平衡结果,我们首先使用标签抽样建立一个训练集,然后从整个数据集中减去它们得到测试集。

*# prepare training and test data, sample by label*ratio = 0.7train_m = m_features_df.sampleBy('churn', fractions={0:ratio, 1:ratio}, seed=123)
test_m = m_features_df.subtract(train_m)train_pca = pca_features_df.sampleBy('churn', fractions={0:ratio, 1:ratio}, seed=123)
test_pca = pca_features_df.subtract(train_pca)

逻辑回归

*# initialize classifier*
lr = LogisticRegression(maxIter=10)*# evaluator*
evaluator_1 = MulticlassClassificationEvaluator(metricName='f1')*# paramGrid*
paramGrid = ParamGridBuilder() \
    .build()crossval_lr = CrossValidator(estimator=lr,
                          evaluator=evaluator_1, 
                          estimatorParamMaps=paramGrid,
                          numFolds=3)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用测试数据集评估:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

决策树

*# initialize classifier*
dtc = DecisionTreeClassifier()*# evaluator*
evaluator_2 = MulticlassClassificationEvaluator(metricName='f1')*# paramGrid*
paramGrid = ParamGridBuilder() \
    .build()crossval_dtc = CrossValidator(estimator=dtc,
                          evaluator=evaluator_2, 
                          estimatorParamMaps=paramGrid,
                          numFolds=3)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用测试数据集评估:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机森林

*# initialize classifier*
rfc = RandomForestClassifier()*# evaluator*
evaluator_3 = MulticlassClassificationEvaluator(metricName='f1')*# paramGrid*
paramGrid = ParamGridBuilder() \
    .build()crossval_rfc = CrossValidator(estimator=rfc,
                          evaluator=evaluator_3, 
                          estimatorParamMaps=paramGrid,
                          numFolds=3)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用测试数据集评估:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

在所有三个模型中,主成分分析的结果优于手动降维后的特征。虽然逻辑回归模型为测试数据集提供了完美的 F1 分数和准确性,但它对训练数据集的表现远不如人意。由于逻辑回归模型在完整数据集上的表现也不尽如人意,这一完美的 F1 分数可能是由于模型的偶然性或简单性。因此,我将选择随机森林分类器用于未来的实现,其测试和训练 F1 分数都在 97%左右。

尽管将来我们可以尝试许多其他模型,如朴素贝叶斯和线性 SVM,但随机森林模型在这种情况下表现得相当好(小数据集的 F1 值为 97%,完整数据集的 F1 值为 99.992%)。

还有一些其他的改进,我们可以在未来继续努力:

  • 训练和评估模型的更加自动化和健壮的方法
  • 考虑那些降低服务等级的用户
  • 更注重用户行为特征

感谢阅读!

用神经网络预测日用电量。

原文:https://towardsdatascience.com/predict-daily-electric-consumption-with-neural-networks-8ba59471c1d?source=collection_archive---------19-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个简单的三维结构如何减少错误,战胜更复杂的模型,并加倍节省。

2019 年初,我们建立了一个深度学习模型,以每小时为基础预测电力消耗。因为最小的误差都会让电力公司损失数万美元,所以我们研究了许多更复杂的预测工具。最后,我们发现一个简单的全天方法是最有效的,通常可以将错误减半。

该结构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在我们之前的模型中,我们输入了所有我们认为与给定小时负载相关的特性:日期、天气数据等。然后,神经网络输出单小时负荷预测。这个过程重复了 72 次,给出了一个 3 天的预报。想要更深入的解释,可以考虑阅读原文博文

新的结构有效地结合了 24 小时模型。但是,我们没有计算一个小时,而是将所有重量合并到一个平坦的、完全连接的密集层中(我们决定大约。900 个节点)。该层然后完全连接到一个 24 小时向量。然后,我们在 3 天内重复这一过程,给出 72 小时的预测。

为什么这应该有效?

主要的要点应该是不同的时间互相“通知”。在我们的旧模型中,我们有一个非常直接的方法:给定所有这些因素,这一小时的预测是什么?但是在我们的新模型中,我们可以让所有对 4pm 的负载预测有贡献的因素影响 5pm 的负载预测。如果早上 6 点的温度是 30 华氏度,这难道不会影响到 9 点时加热器是否还在工作吗?神经网络可以识别这些复杂的相关性,并提供更明智的预测。

这是怎么建的?

正确准备三维训练数据可能很棘手。下面是一个不太完美的函数,它将数据适当地分组到所需的维度。

然后,它被送入以下网络:

为什么不是 RNN?

一个递归神经网络,或 RNN,将类似于上面概述的网络运行。但是我们对 LSTMs 和 GRUs(两个流行的 RNN 模型)的测试并不成功。我们无法制造出超越我们最简单的逐时结构的模型。简而言之,传统的 RNN 结构似乎让事情变得更糟。

为什么一天 24 小时?

在我们的短期预测分析中,我们通常关心三天增量的负载(更远的预测很快变得无用)。那么为什么不在 72 小时的向量上训练呢?我们在技术上可以,但成本不会超过收益。在我们的 24 小时预测中,日模型的运行速度比小时模型慢三倍,但是回报(我们将在下面看到)非常高。但是,当我们增加到 48 或 72 小时的预测时,模型会严重变慢,但几乎没有改善。至少就我们的目的而言,分开训练三个 24 小时模型更好。

结果呢

我们在德克萨斯州的“中北部”地区测试了新模型。数据可以在这里找到。虽然实际上模型会每天训练,但在这次测试中,我们严格训练了前 16 年的数据(2002 年至 2017 年),并在最后一年(2018 年)进行了测试。为了模拟天气预报的不确定性,我们向历史天气数据中添加了噪声,即 24、48 和 72 小时分组的标准偏差分别为 2.5、4 和 6 度的正态分布。

准确(性)

新模型的平均绝对百分比误差(MAPE)为 3,而旧模型的前 24 小时 MAPE 为 4。但是每小时的结果更令人信服。

最重要的问题不仅是 MAPE,而且是误差的传播(下面表示为四分位距,或 IQR)。在开发我们的第一个模型时,我们发现当我们的模型是错误的时候,它通常是非常错误的。新模型中减少的方差可以帮助我们更有信心地向公用事业公司传达我们的不确定性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些模型假设用户将在晚上 11 点预测第二天的电力消耗。因此,下图中的“提前 0 小时”意味着“凌晨 12 点”,“提前 30 小时”类似于“两天后的凌晨 5 点”,等等。

节省的资金

而且最重要的是,省下来的钱!误差减少 1 个百分点可能看起来微不足道,但在 2018 年,这将使德克萨斯州的调峰储蓄增加一倍。(如果你对调峰不熟悉,可以考虑看看这位讲解者)。

假设电池的充电功率为 700 千瓦,额定功率为 500 千瓦,我们可以通过完美的预测计算出可能出现的调峰量。使用我们的每小时神经网络模型,您可以捕获 36%的最佳值。通过替换新模型(不确定性分析下没有任何花哨的优化),我们能够捕获 64%的成本,几乎使我们的节约翻倍!

欢迎建议

我们无法开发出比我们的模型更好的 RNN,但这并不意味着它不存在。精度的微小提高可以极大地帮助电力公司,所以如果你认为有我们没有考虑到的结构,请随时联系我们!

疑问?更正?数据科学笑话?联系我或者在 我的网站 上看更多项目。

合作研究开放式建模框架

这是关于神经网络调峰的三部分系列的更新。考虑在这里阅读更多:

[## 基于神经网络的⚡️负荷预测和调峰

预测技术给了公用事业单位一个机会来拉平他们的负荷曲线,提出了一个全新的家庭…

www.kmcelwee.com](https://www.kmcelwee.com/load-forecasting/)

用 R 预测世界大赛的本垒打

原文:https://towardsdatascience.com/predict-home-runs-in-the-world-series-with-r-d8c0bb2e6f02?source=collection_archive---------38-----------------------

使用历史棒球数据和带 R 的逻辑回归来预测世界职业棒球大赛的得分。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

蒂姆·高在 Unsplash 上拍摄的照片

介绍

你有没有想过美国棒球、机器学习、统计学有什么共同点?嗯,你今天很幸运!

在这个项目中,我们将使用一些免费的历史棒球数据和 R 中的 glm() 函数,来看看哪些变量在预测世界职业棒球大赛中有多少本垒打发生时起作用。

这个项目有几个假设。首先,您有一个 R 环境设置。对于这个项目,我将在 Mac 上的 RStudio 中使用 R Markdown (RMD)文件,但代码在其他操作系统和环境下也能正常工作。第二,你至少对棒球有一个基本的了解。我绝不是棒球专家,但也不需要什么重要的知识。只要你知道什么是本垒打,我们就没事了!

为了您的方便,RMD 的文件可以在我的 GitHub 这里获得

现在,让我们进入目标,开始编写代码吧!

目标

  1. 了解如何在不使用本地文件或手动将数据加载到环境中的情况下导入数据
  2. 将数据分成训练集和测试集,用于机器学习
  3. 使用非正态数据分布进行机器学习
  4. 使用逐步回归方法创建逻辑回归模型
  5. 使用模型进行预测,并检查我们的模型的准确性

数据

在我们进入代码之前,我们需要谈谈肖恩·拉赫曼的惊人工作。他和他的团队已经创建并免费提供了数量惊人的棒球历史数据。他们还做了大量的工作,用其他几种编程语言制作了一个 R 包和易于使用的实现。我会经常引用他们的话,把数据归功于他们。他们的整个项目只靠他们网站上描述的捐赠来运作。如果你真的想帮助他们,体育分析领域的许多人会用这个数据集来以一种有趣的方式学习机器学习。

这是肖恩的网站:http://www.seanlahman.com/baseball-archive/statistics/

现在该编码了!

代码

加载库

我们将在整个项目中使用 tidyverseLahman 包。

代码如下:

# Uncomment the commands below if you have not installed either the tidyverse or Lahman packages
# install.packages("tidyverse")
# install.packages("Lahman")# Load Libraries
require(tidyverse)
require(Lahman)

如果您以前使用过 R,请特别注意它明显缺少导入本地数据和命名对象的功能。我们只需要使用 data() 函数从拉赫曼的数据集中拉出我们想要的表。默认情况下,对象将以数据框的形式出现,并被称为表的名称,对于我们的目的来说就是 BattingPost。

代码如下:

# Imports the BattingPost data set from the Lahman database. Notice how there is not a file to read in. The data is part of the package!
data(BattingPost)# Check the data import
# Notice again that we did not have create and object and assign it data like we normally do with R. It just knows to create a data frame called "BattingPost"
head(BattingPost)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从拉赫曼的数据库[1]导入 BattingPost 表

过滤数据

我们现在已经把数据输入 R 了。我们需要稍微清理一下。首先,由于这是一个关于世界职业棒球大赛的项目,我们需要确保我们的 round 列中只有“WS”值。其次,我们需要确保 yearID 列大于或等于 1920。我不是棒球历史学家,但那是现代“实况球时代”开始的时候[2]。如果你对那种历史感兴趣,可以看看 Bradford Doolittle 关于 ESPN 的文章。

代码如下:

# Filter down the data a bit. We went the year to be 1920 and later because that's when the "Live Ball Era" starts. We also want only WS in the round column because that's for the World Series.SlimBattingPost <- BattingPost %>%
  filter(yearID >= 1920) %>%
  filter(round == "WS")# Check the data
head(SlimBattingPost)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从拉赫曼的数据[1]中筛选出大于或等于 1920 年的数据和仅等于世界系列的舍入数据

让我们为跟随的人澄清一下。你经常看到的“%>%”就是所谓的“管道”字符[3]。当使用 tidyverse 时,特别是其中的 dplyr 部分,处理数据时,我喜欢把它理解为管道字符后面的每个函数或条件都会使假设的数据漏斗变小一点。

我们很少需要一个数据集中的所有数据,因此通过一个周长越来越小的“漏斗”来“倾倒”它是一种可视化这里正在发生什么的好方法。仅仅通过应用这两个过滤器,我们就从原始数据中的 14,750 行增加到了新数据中的 4,280 行。您可以通过查看 RStudio 中的对象或者使用 nrow() 函数来获得这些数字。

代码如下:

# Print number of rows in each data set
nrow(BattingPost)
nrow(SlimBattingPost)

机器学习简介

让我们用我们的例子用简单的英语来解释机器学习的含义。我们将要做的事情被称为“监督机器学习”,因为我们作为人类正在“监督”什么数据被输入到机器中[3]。

当像这样使用真实数据时,我们想要做的是将整个数据集的一部分分割成“训练”数据,而将其余部分分割成“测试”数据[3]。这是出于一个非常合理的原因。如果你想让一台机器能够做出预测,你需要对照现实对它进行测试,以确保它实际上工作正常。

传统上,我可以补充一个很好的理由,即 80%的训练数据和 20%的测试数据分开。我见过各种其他组合,如 70/30 和 75/25,但我喜欢 80/20 的分割,因为我通常会得到最好的结果,听起来与帕累托原则相同[4]。拜托,我控制不了自己。我的学士学位是经济学。

因此,在接下来的几段代码中,我们将把数据分成训练集和测试集,使用 glm() 函数创建一个广义线性模型,然后随着时间的推移对其进行改进,以获得更好的结果。你会看到,我们可以让机器“学习”什么变量重要,什么不重要,以便随着时间的推移做出越来越好的预测。

播种

在我们分割数据之前,我们真的应该设定一个种子[3]。我们将使用随机(嗯,伪随机,如果你想精确)分裂。这意味着代码每次都会将数据分成不同的集合,除非我们使用带有数字的 set.seed() 函数,这样我们就能确保可再现性[3]。对于那些跟随的人来说,这意味着只要我们使用相同的数字,你的代码将与我的代码一致。

代码如下:

# Set Seed
set.seed(1337)

设置种子没有任何输出。它只是确保幕后参数设置正确的东西[3]。

培训和测试数据

有很多方法可以做到这一点,但这是我在实践中发现或看到的最简单的方法。这是秘密。我们将创建一个任意 ID 号的列,并将其添加到现有的数据中。这让我们可以轻松地使用 R 中最隐蔽的函数之一 tidyverse 中 dplyr 包中的 anti_join() 函数。这样做的目的是从一个表中获取在另一个表中没有匹配项的所有行。太偷偷摸摸了。我喜欢它,你可以在这里的文档中阅读所有关于它的内容

代码如下:

# Creates an ID column so we can more easily sort train from test
SlimBattingPost$id <- 1:nrow(SlimBattingPost)# Creates set of data randomly sampling 80% of total data
train <- SlimBattingPost %>% dplyr::sample_frac(.80)# Creates set of data with other 20%
test <- dplyr::anti_join(SlimBattingPost, train, by = 'id')# Check the data
head(test)
paste("The test data has this many rows:", nrow(test))
head(train)
paste("The train data has this many rows:",nrow(train))

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自拉赫曼数据集[1]的过滤数据的前六行测试数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们从拉赫曼数据集[1]中筛选出的前六行训练数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

过滤后的拉赫曼数据[1]中每个测试和训练数据集中的行数

确定分布

大多数时候,机器学习处理的是正态分布的数据[3]。当你想到一个我们都见过很多次的正态钟形曲线时,它被称为“高斯”分布,因为数学家这么说[3]。看起来是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

泰勒·哈里斯绘制的正态钟形曲线

在正态分布中,有一个平均值(μ,蓝线)和该平均值的标准偏差(σ,绿线)。进入细节并不是非常重要,但这意味着 95%的数据都在平均值的 2 个标准差(2 sigma)以内[3]。机器学习想要正常数据的原因有很多,但有时我们会得到不正常的数据。

另一种分布被称为“泊松”分布[3]。当然,它是以另一位数学家的名字命名的,但关键是,它是一种标准的分布类型,其中大多数数据都严重倾斜[3]。大多数时候,它看起来像这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

泰勒·哈里斯绘制的基本泊松分布

对于机器学习,我们真正关心的是我们试图预测的变量的分布[3]。在我们的例子中,游程®将是因变量。让我们制作一个超级快速的直方图来检查我们是否有正态或泊松分布。

代码如下:

# Visually determine type of distribution of Runs (R) in dataset
hist(SlimBattingPost$R)# Classic Poisson distribution. Almost all data is 0 or 1, heavily skewed

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

过滤后的拉赫曼数据的基本运行直方图[1]

对我来说,游程®变量看起来像一个非常经典的泊松分布!

建立模型

有趣的部分来了!我们现在开始建立逻辑回归模型!让我们先看看代码,然后再讨论它。

代码如下:

# Start with everything else as independent variables with runs (R) as the dependent variable
# For a data dictionary about what each abbreviation means, go to: [http://www.seanlahman.com/files/database/readme2017.txt](http://www.seanlahman.com/files/database/readme2017.txt)
# Search for "BattingPost" in that document and the third match should be the table where the abbreviations are defined. G = Games, AB = At Bats, etc.# Create the logistic regression with everything in it, make sure to include the Poisson distribution as the family. If we had normal data, we would use Gaussian in its place.fitAll <- glm(R ~ G + AB + H + X2B + X3B + HR + RBI + SB + CS + BB + SO + IBB + HBP + SH + SF + GIDP , data = train, family = "poisson")summary(fitAll)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

包含所有独立变量的第一个 glm()模型的输出

这里有很多东西需要打开。

首先,我们创建了一个名为“fitAll”的模型对象,该对象使用了 glm() 函数,其中 R 作为因变量,用~字符与所有用 a +分隔的自变量分隔开,同时还使用了我们的训练数据(还记得前面的 80%吗?)并使用泊松分布来获得更精确的结果[3]。

一旦我们创建了模型, summary() 函数将以简单的形式给出上面的输出。

摘要的第一部分是“Call:”它只是说实际使用了什么模型。这里没什么可说的。

“偏差残差:”试图告诉我们在处理错误时现实和期望之间的差异[3]。实际上,在这一点上,这与学习逻辑回归并不完全相关,所以我们将继续前进。

“系数:”部分是我们需要关注的地方。第一列是所有变量加上截距。还记得代数一课的公式“ y = mx + b ”吗?这是截距[3]的 b 值的极端版本。

“估计”栏告诉我们每个值对运行次数的影响是积极的还是消极的,以及它的系数有多大[3]。这类似于一堆不同的 m 值,其中 x 值是实际数据集中的数字。一个等式可能是这样的y = mx+mx+m₃x… + b 具有所有不同的系数和值【3】。

“性病。“错误”栏是我们错误程度的统计指标[3]。z 值在统计学中用于给出分布曲线上的 z 值[3]。现在对我们来说没那么重要。

与我们最相关的是优雅地标记为“Pr(>|z|)”的列,这是 p 值的一种非常复杂的表达方式[3]。对于 p 值,我们关心的是我们所说的是有意义的一定程度的信心[3]。此外,p 值的范围在 0 和 1 之间,所以如果有负数或大于 1 的数字,数学在某个地方是错误的[3]。

例如,如果我们要至少 95%确定一个自变量是因变量的一个统计上显著的预测因子,p 值将小于 0.05[3]。如果我们希望 90%确定变量是显著的,那么我们需要看到一个小于 0 . 10 的 p 值[3]。要 99%确定,我们需要看到小于. 01 的 p 值[3]。明白我的意思了吗?

r 为我们提供了一个方便的小功能,它标记了每个变量的显著性水平。放一个单曲“.”像 SBSF 这样的变量意味着我们有 90%到 95%的把握这些变量在统计上是显著的[3]。变量旁边的“*”表示我们有 95%到 99%的把握该变量具有统计显著性,p 值越小,其余变量的预测能力越强[3]。

目标是最终得到一个只有统计上显著的独立变量的模型。那么我们该怎么做呢?我们使用一种叫做“逐步回归”的方法[3]。

逐步回归

这是一个听起来很有趣的术语,简单地说就是我们取出一个最不重要的变量,然后重新运行我们的模型,重复这个过程,直到我们只剩下具有统计意义的独立变量[3]。我们来做一个例子。在继续之前,找到具有最大 p 值的变量。

代码如下:

# Start step-wise regression.
# Looks like G is the least significant
# Make sure to create a new object name so we do not overwrite our models as we go along!fit1 <- glm(R ~ AB + H + X2B + X3B + HR + RBI + SB + CS + BB + SO + IBB + HBP + SH + SF + GIDP , data = train, family = "poisson")summary(fit1)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

删除最不重要变量后逻辑回归的新结果表

那么,你注意到了什么?在我们去掉了最不重要的变量 G 之后,很多 p 值都变了!这很正常。当所有的数字都被处理后,那些被认为是统计上显著变化的变量就被拿走了[3]。这也有道理。当你想到这一点时,不管是第一场还是第三场还是第六场比赛都会影响得分,这是没有意义的。

注意:当你删除一个变量时,确保也从公式的自变量侧删除“+”so,以避免不必要的错误!此外,请务必将您的模型重命名为不同的对象名称,以避免在学习时覆盖过去的工作!

然而,看起来我们仍然有不具有统计显著性的独立变量。看起来是下一个要去的变量。

代码如下:

# Looks like SO is the least significant
# Make sure to create a new object name so we do not overwrite our models as we go along!fit2 <- glm(R ~ AB + H + X2B + X3B + HR + RBI + SB + CS + BB + IBB + HBP + SH + SF + GIDP , data = train, family = "poisson")summary(fit2)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

移除 SO 后的第二个逐步回归摘要

希望在这一点上,你得到了模式。直到模型只剩下统计上显著的变量达到 95%的置信阈值(p 值< 0.05), we will look this process.

Rather than take up a ton of space, I am going to skip ahead to the final model after going through the process of removing non-significant independent variables. On your end, do it until you match up with my answer for learning purposes.

Here’s the code:

# Final Fit
fitFinal <- glm(R ~ AB + H + X2B + X3B + HR + CS + BB + IBB + HBP + GIDP , data = train, family = "poisson")summary(fitFinal)

Here’s the output:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Final logistic regression model after completing the step-wise regression process

Make Predictions

Now is the time where we see how well we did with our model. Fortunately for us, R has an easy-to-use tool for this — the *预测()*函数。我们现在将使用我们的最终逻辑回归模型,对照我们的测试数据(我们之前保留的 20%)进行测试,然后看看我们有多准确!

代码如下:

# Create predictions
predictions <- predict(fitFinal, test, type = 'response')# Check the output
head(predictions)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

逻辑回归的前 6 个预测

现在,我知道我将要向你们展示的过程并不是做到这一点的捷径。然而,我发现这是最容易一步一步形象化的,这更好地支持了我们学习技术的目标。

我们现在要做的是将我们刚刚做出的预测添加到我们的测试数据中,选择我们实际上想要生成较小数据集的列,在新列中对预测进行舍入,并创建一列真布尔值和假布尔值,以便您可以轻松地看到幕后。

代码如下:

# Add predictions to test data and create new data frame
predictDF <- data.frame(test, predictions)# Create new data frame with less columns
SlimPredictDF <- select(predictDF, "yearID", "round", "playerID", "teamID", "R", "predictions")# Add rounded predictions as a column
SlimPredictDF$roundedPredictions <- round(SlimPredictDF$predictions, 0)# Create Boolean Column to see if real and predictions match
SlimPredictDF$TFmatch <- SlimPredictDF$R == SlimPredictDF$roundedPredictions# Check data structure 
head(SlimPredictDF)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拉赫曼数据测试部分的四舍五入预测表[1]

结果

我们可以使用 tidyverse 中的几个函数创建一个简单的表来计算匹配和未匹配的次数。

代码如下:

# Get the results!
results_table <- SlimPredictDF %>%
  group_by(TFmatch) %>%
  summarise(count = n())
results_table

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

逻辑回归模型预测值的结果表

嗯,66%还不算太糟,对吧?只要将 564 除以 856 就可以得到结果。

结果—奖励积分

我们可以检查结果的另一种方法是使用 lm() 函数制作一个快速线性模型,并查看我们的 R 平方值。我们会得到 R 平方的两个变量,但它们会很接近。这将告诉我们自变量(roundedPredictions)解释了因变量(R,runs)的多少。

代码如下:

# Simple linear model to get p-vale for whether real Runs (R) are significantly prediction by predictionsfitLM <- lm(R ~ roundedPredictions, data = SlimPredictDF)
summary(fitLM)

以下是输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用线性模型的结果版本

当以这种方式检查时,我们得到了大约 63%正确的类似答案。

结论

这个项目旨在教育,有趣,真实。我们可以做很多实验,比如使用不同的分布,包括不仅仅是世界职业棒球大赛的季后赛,改变数据开始的年份等等。

在一天结束的时候,我们使用真实的数据进行真实的预测,并获得了大约 2/3 的正确率。我们还逐步回归,并在此过程中学习了一些非标准的 R 技巧。

如果您有任何问题、意见或对未来探索的想法,请告诉我。享受使用你新发现的逻辑回归技巧来预测其他数据吧!

参考

[1] S .拉赫曼,下载拉赫曼的棒球数据库 (2020),http://www.seanlahman.com/baseball-archive/statistics/

直播球时代已经过去了一百年,棒球是一项更好的运动吗? (2019),https://www . ESPN . com/MLB/story/_/id/27546614/100 年-直播球时代-棒球-更好-游戏

[3] R. Kabacoff, R 在行动(第二版。) (2015),纽约州谢尔特岛:曼宁出版公司

[4].K. Kruse,80/20 法则及其如何改变你的生活 (2016),https://www . Forbes . com/sites/kevinkruse/2016/03/07/80-20-Rule/# 230185973814

用机器学习预测房价

原文:https://towardsdatascience.com/predict-house-prices-with-machine-learning-5b475db4e1e?source=collection_archive---------4-----------------------

对 1,883 个家庭进行回归模型训练

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:格雷戈·卡特大律师&事务律师。

财产估价是一门不精确的科学。个人评估师和估价师把他们自己的经验、标准和技能带到工作中。一致性是很难的,英国和澳大利亚的研究表明,两个专业人士的估值 相差高达 40% 。哎呀!

也许一台训练有素的机器可以代替人类来完成这项任务,而且一致性和准确性更高。

让我们将这一想法原型化,并使用关于房屋的 特征成本邻居简介 的数据来训练一些 ML 模型,以预测其价值。我们的目标变量房地产价格是数字,因此 ML 任务是回归。(对于分类目标,任务变为分类。)

我们将使用来自 elitedatascience.com 的数据集来模拟属于房地产投资信托基金的 1883 处房产的投资组合。有 26 列。这里有一小段:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原始数据集的快照。

这些步骤是:

  1. **EDA &数据处理:**探索、可视化和清理数据。
  2. **特性工程:**利用领域专长,创造新特性。
  3. **模型训练:**我们将训练和调优一些屡试不爽的分类算法,比如岭和套索回归。
  4. **性能评估:**我们将看看常见的回归任务指标,如 R 分数和均方平均值。
  5. **部署:**批量运行还是弄几个数据工程师/ ML 工程师来建一个自动化流水线?

加入 Medium 这里并获得无限制访问互联网上最好的数据科学文章。

1.数据探索和处理

探索性数据分析(EDA) 帮助我们理解数据,为数据清洗特征工程提供思路和见解。数据清理为我们的算法准备数据,而特征工程是真正帮助我们的算法从数据集中提取潜在模式的神奇调味汁。请记住:

更好的数据总是胜过更好的算法!

我们首先将一些标准数据科学 Python 包加载到 JupyterLab 中。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sbfrom sklearn.linear_model import LinearRegression
from sklearn.linear_model import Lasso, Ridge, ElasticNet
from sklearn.ensemble import RandomForestRegressor,
                             GradientBoostingRegressorfrom sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCVfrom sklearn.metrics import r2_score,
                            mean_absolute_error import pickle

导入数据集:

df = pd.read_csv('real_estate_data.csv')

这是我们数据帧的快照。形状是(1,883,26)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原始数据集的快照。单击以展开。

目标变量是 tx_price ,它表示房产最后出售的价格。

共有 25 个栏/特征。

  • 房产特征: tx_year —最后出售的年份, property_type (独栋与公寓/公寓/联排别墅)。
  • 物业费:每月财产税保险
  • 物业特点:床、浴池sqrt (建筑面积)、 lot_size (含套外面积)、year _ build&地下室(是/否)。
  • 邻里生活方式:数量餐厅杂货夜生活咖啡馆购物艺术 _ 娱乐美容 spa、&、活跃 _ 生活(健身房、瑜伽馆等。)半径 1 英里以内。
  • 邻里人口统计:中位年龄已婚 (%)、大学毕业生 (%)。
  • 邻里学校:数量 _ 学校(区内)& 中位数 _ 学校(区内公立学校 10 分中的中位数)。

1.1 数字特征

我们的数字特征列表可以通过代码获得:

df.dtypes[df.dtypes!=’object’]

我们将在这里跳过它。相反,让我们直奔所有数字特征的直方图:

df.hist(figsize=(20,20), xrot=-45)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这看起来基本没问题。我用红色留下了一些评论。

目标变量(tx_price)的分布:

我们有一个右尾分布,也可以通过看一个小提琴图来看。

sns.violinplot(data=df, x=’tx_price’)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

df.tx_price.median()**Output: 392000**

2020 年 10 月,美国房价中值为 325,000 美元,因此平均而言,房地产投资信托组合中的房屋似乎更靠近住宅区。

缺失值:

df.select_dtypes(exclude=[‘object’]).isnull().sum()**Output:
tx_price              0
beds                  0
baths                 0
sqft                  0
year_built            0
lot_size              0
basement              223
restaurants           0
groceries             0
nightlife             0
cafes                 0
shopping              0
arts_entertainment    0
beauty_spas           0
active_life           0
median_age            0
married               0
college_grad          0
property_tax          0
insurance             0
median_school         0
num_schools           0
tx_year               0**

基底特性的进一步研究表明基底=0 的行是用 NaN 编码的。因此,这实际上是一个标签不正确的问题。我们需要把 NaN 转换成 0 来进行运算。

df.basement.fillna(0, inplace=True)

注意:如果这些 NaN 是真正缺失的值,我们应该在将 基底 中的 NaN 转换为 0 之前,创建一个指示变量 基底 _ 缺失 (当基底=NaN 时值为 1)。

异常值:lot _ sizelot _ size的直方图表明我们有一些相当大的豪宅!

df.lot_size.sort_values(ascending=False)**Output:
102     1220551
1111     436471
1876     436035
1832     436035
1115     435600
Name: lot_size, dtype: int64**

实际上,只有一处房产比其他的大很多。让我们把它看作一个离群值,并为我们的建模过滤掉它。

df = df[df.lot_size < 500000]

这里有一张关联热图来展示我们的数字特征。

# mask out upper triangle
mask = np.zeros_like(df.corr(), dtype=np.bool)
mask[np.triu_indices_from(mask)] = True# heatmap
sb.heatmap(df.corr()*100, 
           cmap='RdBu_r', 
           annot = True, 
           mask = mask)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

红色的三角形块表明邻近地区的生活方式特征往往相互关联得很好。例如,活跃生活、美容水疗、咖啡馆、夜生活餐馆都高度相关。这种多重共线性可能会影响模型性能,因为回归要素应该独立于

1.2 分类特征

我们的分类变量可以用代码列出:

df.dtypes[df.dtypes==’object’]

这些是:物业 _ 类型外墙屋顶。

这些分类特征中的每一个的类都可以用下列方式列出:

df.property_type.value_counts()**Output:
Single-Family                    1080
Apartment / Condo / Townhouse     802
Name: property_type, dtype: int64**df.exterior_walls.value_counts()**Output:
Brick                  686
Siding (Alum/Vinyl)    503
Metal                  120
Combination            107
Wood                    72
Wood Siding             49
Brick veneer            48
Stucco                  26
Other                   10
Concrete                 8
Concrete Block           7
Block                    7
Asbestos shingle         6
Rock, Stone              5
Masonry                  3
Wood Shingle             2
Name: exterior_walls, dtype: int64**df.roof.value_counts()**Output:
Composition Shingle      1179
Asphalt                   132
Shake Shingle              55
Other                      49
Wood Shake/ Shingles       30
Gravel/Rock                30
Roll Composition           12
Asbestos                    9
Slate                       9
Composition                 5
asphalt                     5
Metal                       4
composition                 4
shake-shingle               3
Built-up                    2
asphalt,shake-shingle       1
Name: roof, dtype: int64**

由此,我们可以稍微清理一下类。我们将

  • 将稀疏类合并在一起(那些观察值太少的类)
  • 合并具有相似含义的类(例如将混凝土砌块归入更一般的混凝土砌块类。
  • 修正标签错误(例如混凝土应为混凝土)。
df.exterior_walls.replace(['Wood Siding', 'Wood Shingle', 'Wood'],
                           'Wood', inplace=True)df.exterior_walls.replace(‘Rock, Stone’, ’Masonry’, inplace=True)df.exterior_walls.replace([‘Concrete’,’Block’], ’Concrete Block’,
                            inplace=True)df.exterior_walls.replace(['Concrete Block', 'Stucco', 'Concrete',
                           'Block', 'Masonry', 'Other', 
                           'Asbestos shingle', 'Rock, Stone'],
                           'Other', inplace=True)df.roof.replace(['Composition', 'Wood Shake/ Shingles', 
                 'Composition Shingle'], 'Composition Shingle',
                  inplace=True)df.roof.replace(['Other', 'Gravel/Rock', 'Roll Composition',
                 'Slate', 'Built-up', 'Asbestos', 'Metal'], 'Other',
                  inplace=True)df.roof.replace(['asphalt','asphalt,shake-shingle',
                 'shake-shingle'], 'Shake Shingle', inplace=True)df.roof.replace('composition', 'Composition',inplace=True)

缺失值:

df.select_dtypes(include=[‘object’]).isnull().sum()**Output:
property_type       0
exterior_walls    223
roof              353
dtype: int64**

我们想告诉我们的算法,这些值是缺少。这比简单地删除行更有指导意义。

for feat in df.select_dtypes(include=[‘object’]):
    df[feat] = df[feat].fillna(“Missing”)

让我们画出三个分类特征的柱状图。

for feat in df.dtypes[df.dtypes==’object’].index:
   sb.countplot(y=feat, data=df)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后,对于我们的算法,分类特征必须是一次性编码的。我们现在就去做。

df = pd.get_dummies(df, columns = [‘exterior_walls’, 
                                   ‘roof’, 
                                   ‘property_type’])

1.3 细分

分割结合了数字和分类特征。

让我们根据我们的目标 tx_price 来分割我们所有的分类变量( property_type外墙屋顶)。这将提供一个可能驱动财产价值的细节。

for feat in df.dtypes[df.dtypes==’object’].index:
   sb.boxplot(data=df, x = ‘tx_price’, y = ‘{}’.format(feat))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们通过使用 groupby 来看看单户住宅和公寓/共管公寓/联排别墅之间的房产和街区特征有何不同。

df.groupby('property_type').agg(['mean','median'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总的来说,独栋房子更贵,面积更大,而公寓/公寓/联排别墅更靠近娱乐场所/商店,吸引更年轻的居民。这并不奇怪。家庭住宅往往位于郊区,而公寓和更高密度的居住往往更靠近市中心。

2.特征工程

许多特征工程依赖于领域专业知识。如果你有一个房地产方面的主题专家(SME)来提供指导,你将有更好的机会设计一些令人敬畏的功能,这些功能将真正使你的建模发光。

在这里,我们将设计四个新功能:

  • 两房两卫:两房两卫房产。为什么?通过领域专业知识,你可能知道这些财产受投资者欢迎。这是一个指示变量。
  • 衰退期间:美国房地产市场在 2010 年至 2013 年间陷入衰退。因此,处于这一时期可能会在很大程度上影响定价。
  • property_age :这是一种常识。较新的物业应该吸引较高的价格,对不对?(回答:会但不一定。)
  • school_score :我们将这个交互特征定义为 num_schoolmedian_school 的乘积,它给出了代表该地区学校教育质量的单一分数。

以下代码创建了这些新功能。

df[‘two_and_two’] = ((df.beds == 2) & (df.baths == 2)).astype(int)df['during_recession'] = ((df.tx_year >= 2010) & 
                          (df.tx_year <= 2013))
                          .astype(int)df['property_age'] = df.tx_year - df.year_built
df.drop(['tx_year', 'year_built'], axis=1, inplace=True)df['school_score'] = df.num_schools * df.median_school

新的 property_age 功能可以说取代了原来的 tx_yearyear _ build,因此我们将删除它们。

此外,出于兴趣,我们可以看到大约 9%的房产有两张床/两个浴室,26%是在 2010 年至 2013 年的房地产衰退期间出售的:

df.two_and_two.mean()
df.during_recession.mean()**Outputs:
0.09458023379383634
0.2635494155154091**

分析基表:应用所有这些数据清洗步骤和特征工程后的数据集就是我们的分析基表。这是我们训练模型的数据。

我们的 ABT 有 1863 个属性和 40 列。*回想一下,我们的原始数据集只有 26 列!*通常,abt 比原始数据集有更多的列,因为:

  • one-hot 编码,其中为每个分类特征中的每个类创建一个新列;和
  • 特征工程。

与此相关,ML 中的一个问题是维数灾难,其中你的 ABT 有太多的列/特征。这是深度学习中的一个主要问题,在深度学习中,数据处理可能会产生具有数千个或更多特征的 abt。主成分分析(PCA) 是一种降维技术,将高维相关数据转化为一组低维的不相关成分,称为主成分 (PC)。好消息是,低维的 PCs 捕获了高维数据集中的大部分信息。

我计划写一篇关于无监督学习技术的文章,包括 PCA。睁大你的眼睛!

3.系统模型化

我们将训练四个屡试不爽的回归模型:

  • 正则化线性回归(山脊套索&弹性网)
  • 随机森林
  • 梯度增强树

首先,让我们拆分我们的分析基表。

y = df.status
X = df.drop('tx_price', axis=1)

然后我们将分成训练集和测试集。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234)

我们将设置一个管道对象来训练。这将简化我们的模型训练过程。

pipelines = {
    'lasso' : make_pipeline(StandardScaler(),
              Lasso(random_state=123)),
    'ridge' : make_pipeline(StandardScaler(),
              Ridge(random_state=123)),
    'enet' :  make_pipeline(StandardScaler(),
              ElasticNet(random_state=123)),
    'rf' :    make_pipeline(
              RandomForestRegressor(random_state=123)),
    'gb' :    make_pipeline(
              GradientBoostingRegressor(random_state=123))
}

我们还想调整每个算法的超参数

对于所有三个正则化回归,我们将调整 alpha (L1 & L2 惩罚强度),以及弹性网的 l1_ratio (即 L1 & L2 惩罚之间的权重)。

lasso_hyperparameters = {
    ‘lasso__alpha’ : [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]}ridge_hyperparameters = {
    ‘ridge__alpha’ : [0.001, 0.005, 0.01, 0.1, 0.5, 1, 5, 10]}enet_hyperparameters = { 
    ‘elasticnet__alpha’: [0.001, 0.005, 0.01, 0.05, 0.1, 1, 5, 10], 
    ‘elasticnet__l1_ratio’ : [0.1, 0.3, 0.5, 0.7, 0.9]}

对于我们的随机森林,我们将调整估计器的数量( n_estimators )和分割期间要考虑的最大特征数量( max_features ),以及作为一片叶子的最小样本数量( min_samples_leaf )。

rf_hyperparameters = {
     ‘randomforestregressor__n_estimators’ : [100, 200],
     ‘randomforestregressor__max_features’ : [‘auto’, ‘sqrt’, 0.33],
     'randomforestregressor__min_samples_leaf' : [1, 3, 5, 10]}

对于我们的梯度增强树,我们将调整估计器的数量( n_estimators )、学习速率,以及每棵树的最大深度( max_depth )。

gb_hyperparameters = {
      ‘gradientboostingregressor__n_estimators’ : [100, 200],
      ‘gradientboostingregressor__learning_rate’ : [0.05, 0.1, 0.2],
      ‘gradientboostingregressor__max_depth’ : [1, 3, 5]}

最后,我们将拟合和调整我们的模型。使用 GridSearchCV ,我们可以用几行代码在我们声明的所有超参数上对所有这些模型进行交叉验证训练!

fitted_models = {}
for name, pipeline in pipelines.items():
    model = GridSearchCV(pipeline, 
                         hyperparameters[name], 
                         cv=10, 
                         n_jobs=-1)
    model.fit(X_train, y_train)
    fitted_models[name] = model

4.估价

我写了一篇关于流行的机器学习指标的专门文章,包括下面使用的那些。

4.1 绩效得分

我们将从打印交叉验证分数开始。这是 10 个保留折叠的平均性能,是仅使用您的训练数据获得模型性能的可靠估计的一种方式。

for name, model in fitted_models.items():
    print( name, model.best_score_ )**Output:
lasso 0.3085486180300333
ridge 0.3165464682513239
enet 0.34280536738492506
rf 0.4944720180590308
gb 0.48797200970900745**

移动到测试数据,我们将输出平均绝对误差 (MAE)

R -score 代表模型解释的总方差的比例,范围从 0 到 100。如果 R -score = 100,则因变量( tx_price )与特征完全相关。

MAE 是预测值和实际值之间的平均误差。

for name, model in fitted_models.items():
   pred = model.predict(X_test)
   print(name)
   print(‘ — — — — ‘)
   print(‘R²:’, r2_score(y_test, pred))
   print(‘MAE:’, mean_absolute_error(y_test, pred))
   print()**Output:
lasso
--------
R^2: 0.4088031693011063
MAE: 85041.97658598644

ridge
--------
R^2: 0.4092637562314514
MAE: 84982.89969349177

enet
--------
R^2: 0.40522476546064634
MAE: 86297.65161608408

rf
--------
R^2: 0.5685576834419455
MAE: 68825.53227240045

gb
--------
R^2: 0.5410951822821564
MAE: 70601.60664940192**

胜出的算法是随机森林,R 分最高 0.57,MAE 最低。我们实际上可以做得更好。

还记得之前我们移除了 tx_yearyear _ build特性后的工程 property_age 吗?事实证明这是个错误的选择。如果将它们包括在内,模型的性能将大幅提升,达到 R = 0.81。此外,省略一些高度相关的邻居简档特征(即活跃 _ 生活、美容 _ 水疗、咖啡馆、夜生活餐馆**)会进一步提高性能。这突出了特征工程和**特征选择的重要性。****

仅供参考,这里是获胜随机森林的超参数,使用 GridSearchCV 进行了调整。

RandomForestRegressor(bootstrap=True, 
                      criterion='mse',
                      max_depth=None,
                      max_features='auto',
                      max_leaf_nodes=None,
                      min_impurity_decrease=0.0,
                      min_impurity_split=None,
                      min_samples_leaf=10, 
                      min_samples_split=2,
                      min_weight_fraction_leaf=0.0,
                      n_estimators=200, 
                      n_jobs=None,
                      oob_score=False, 
                      random_state=123,
                      verbose=0, 
                      warm_start=False))],

4.2 特性重要性

考虑下面的代码。

coef = winning_model.feature_importances_
ind = np.argsort(-coef)for i in range(X_train.shape[1]):
    print("%d. %s (%f)" % (i + 1, X.columns[ind[i]], coef[ind[i]]))x = range(X_train.shape[1])
y = coef[ind][:X_train.shape[1]]plt.title("Feature importances")
ax = plt.subplot()
plt.barh(x, y, color='red')
ax.set_yticks(x)
ax.set_yticklabels(X.columns[ind])
plt.gca().invert_yaxis(

这将打印按重要性排序的特性列表和相应的条形图。

**1\. insurance (0.580027)
2\. property_tax (0.148774)
3\. sqft (0.033958)
4\. property_age (0.031218)
5\. during_recession (0.027909)
6\. college_grad (0.022310)
7\. lot_size (0.020546)
8\. median_age (0.016930)
9\. married (0.015506)
10\. beauty_spas (0.013840)
11\. active_life (0.011257)
12\. shopping (0.010523)
13\. school_score (0.010032)
14\. restaurants (0.009975)
15\. median_school (0.007809)
16\. baths (0.007009)
17\. cafes (0.005914)
18\. groceries (0.005578)
19\. nightlife (0.004049)
20\. arts_entertainment (0.003944)
21\. beds (0.003364)
22\. exterior_walls_Siding (Alum/Vinyl) (0.001808)
23\. exterior_walls_Brick (0.001348)
24\. roof_Composition Shingle (0.001239)
25\. roof_Missing (0.000778)
26\. roof_Shake Shingle (0.000750)
27\. exterior_walls_Missing (0.000632)
28\. num_schools (0.000616)
29\. exterior_walls_Metal (0.000578)
30\. basement (0.000348)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

到目前为止,前两个预测值是

  • 每月房主保险的费用
  • 每月财产税**。**

这并不完全令人惊讶,因为保险费通常是由保险公司根据重置建筑物的成本来计算的。这需要——令人惊讶的是——对建筑物的价值进行良好的估价。同样,财产税的金额通常与房产价值挂钩。

接下来的两个最强的预测器是资产的大小( sqft )和它有多老( property_age )。较大和较新的房产往往在市场上卖得更多,因此这些结果也符合预期。

5.部署

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 ThisisEngineering RAEng 提供。

此模型的可执行版本(。pkl)可以从 Jupyter 笔记本中保存:

with open('final_model.pkl', 'wb') as f:
    pickle.dump(fitted_models['rf'].best_estimator_, f)

房地产投资信托基金可以在将新的住房数据输入训练模型之前对其进行预处理。这称为批量运行。

在一个大型组织中,REIT 可能希望通过与数据工程师机器学习工程师合作,将模型部署到生产环境中。这些专家将围绕我们的模型建立一个自动化的管道,确保新的属性数据可以通过我们的清洗和功能工程逻辑进行预处理,并自动和定期地将预测推送到下游决策者。

最终意见

我们从一个业务问题开始:一家从事购买、持有和出售大型投资物业组合业务的公司希望为其物业估值带来一致性和更高的表现。

我们在包含 1800 多项过去房地产交易的历史数据负载上训练了一个获胜的随机森林模型。

人力资源部可以在我们训练有素的员工身上运行新数据。pkl 文件,或者可以由他们的工程部门构建自动管道。

我们的模型是一个回归模型**,其中目标变量是数字。**

监督学习硬币的另一面是分类模型**,其目标变量是分类的。在这里,我训练了一个二元分类模型,预测员工是否有离开公司的风险。**

最后,我在这里写了一篇关于机器学习在数学建模领域的位置的文章。

在 YouTube 和 Twitter 上关注我。

无限制媒体访问

提升你的知识和技能到一个新的水平。

加入 Medium 享受无限制访问互联网上的最佳分析&数据科学文章**。你可以在这里加入来支持我和其他顶级作家。**

我的数据科学文章

  • 微分方程与机器学习——此处
  • 新冠肺炎的数学建模与机器学习— 此处
  • 回归预测房价— 此处
  • 分类预测员工流失— 此处
  • 流行的机器学习性能指标— 此处
  • Jupyter 笔记本与 Dataiku DSS — 此处
  • Power BI —从数据建模到令人惊叹的报告— 此处

使用机器学习和 NLP 预测 IT 支持票

原文:https://towardsdatascience.com/predict-it-support-tickets-with-machine-learning-and-nlp-a87ee1cb66fc?source=collection_archive---------4-----------------------

通过 Python 中的监督分类技术。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:alenesetril通过 unsplash (CC0)

处理大量 IT 服务请求?对降低运营成本感兴趣?希望提升用户体验?别再看了。

本文可作为数据科学爱好者在 IT 服务管理(ITSM)环境中部署生产级机器学习解决方案的指南。规定的 ML 解决方案将帮助我们深入了解 IT 服务请求的黑匣子,并跨业务管理孤岛整合 ITSM 战略。

我们将使用受监督的分类算法,根据输入文本对新票据进行分类。我采用了 PythonRESTful API 框架、 Scikit-LearnSpaCy 来完成这个任务;但是,有许多解决方案更适合您的组织。我将尽最大努力解决分歧的机会,并为我为什么选择特定的方法提供专门的理由。

Susan Li 提供了关于使用 SpaCy 进行文本分类的机器学习的出色概述。我的过程、代码和演示(如本文所述)都受到了她的影响。如果你觉得我的任何内容有帮助/有趣,我强烈推荐订阅她的频道。

最终模型对流入生产环境的所有票据的预测准确率超过 85%。SLA 响应时间缩短了一半,每年节约成本近 60 万美元。

背景

IT 服务管理(ITSM)是一项重要的企业职能,负责利用创新来增加价值,最大限度地提高用户生产力,提供端到端的技术服务,等等。尽管企业责任如此重大,但前端 IT 交互通常是通过与支持专家进行长时间艰苦的对话(通过网络或电话)来定义的。无论您是在申请新密码、提交应用程序的配置更改,还是只是寻求帮助,您都将经历一段令人沮丧的前路。这种耻辱依然存在,因为 IT 领导很难为服务于整个企业的全面帮助台管理解决方案配备人员和提供支持。

尽管有良好的意图,支持组织经常错过高效 ITSM 的标志。在为我最近的一个全球医疗设备客户做项目时,我的任务是纠正企业事故单分类带来的令人沮丧的结果。领导层决定使用 ServiceNow(一个受欢迎的 ITSM 工作流平台)和外部供应商将资源投入到高成本的传统解决方案中,以缩短传入票证的响应时间,但收效甚微。普遍的使用和严格的 SLA 限制导致了全面的不准确性,其中业务组用落在他们队列中的票据玩跳房子游戏。用户被激怒了,支持专业人员冷酷无情,领导层被难住了。是时候换个新的视角了!

在 2019 年,超过 60,000 张票被提交 到我客户的 ServiceNow 平台,意图到达近 15 个商业团体。每张票花费 IT 组织 13 美元,尽管平均准确率(达到预期目标的机会)只有 40%。在到达正确的地方之前,错误分配的票据在业务组之间平均反复 21 天。C ost延迟 准确性受到巨大关注,并导致用户体验不佳。

当用户提交支持票时,它通过电子邮件、电话或嵌入式门户流入平台。每张票据都包含一些关于问题或请求的文本,由支持专业人员快速审阅并发送出去。一旦正确的分配组拿起工作单,一些工作就完成了,事故状态恢复为已关闭。

通过 监督机器学习 进行多项分类,根据固定数量的业务组对支持票进行分类的时机似乎已经成熟。我们可以轻松地从每张票上抓取文本和类别,并训练一个模型将特定的单词和短语与特定的类别关联起来。我的假设很简单:机器学习可以提供即时的成本节约,更好的 SLA 结果,以及比人类更准确的预测。我们开始吧!

数据收集和探索

在选择和训练机器学习模型之前,我们需要查看数据,以更好地了解事件票证中的趋势。ServiceNow 提供了一个健壮的 Table API 框架让我们直接从平台抓取数据。

**# Import Statements* **import** requests
**import** json
**import** pandas **as** pd
 *# Initialize url* **url** = "https://{instance.service-now.com}/api/now/table/incident"*# Set simple authorization* **user** = "{username}"
**pwd** = "{password}"*# Set proper headers* **headers** = {"Content-Type" : "application/json", 
"Accept" : "application/json",
"accept-encoding" : "gzip, deflate, br"}# Initialize GET response
**response** = requests.get(url, auth=(user, pwd), headers=headers)
**data** = json.loads(response.text)**dataframe** = pd.DataFrame(data['result'])*

ServiceNow 为您提供了一个绝佳的机会,让您通过其嵌入式 API Explorer 探索 RESTful API 调优的细微差别。这个工具帮助用户从头开始构建定制的 API 请求,减少查询参数、字段等。变成容易理解的增量。此外,您可以点击流行的表(事件、任务等。)或创建复杂的查询。对于任何数据专业人员来说,这都是一个非常棒的工具!

让我们看看我们的数据框架:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于我们感兴趣的是将文本与相关的分类器关联起来,我们可以使用一个分类变量,比如“u_portfolio ”,来标记数据帧中的每一行。尽管存在非常严重的类别不平衡(“全球支持服务”,几乎占所有记录的 65%)和超过 2,000 个缺失值,我们希望消除那些少于 100 个票证的特定类别,以减少噪音并确保我们只使用相关类别。让我们通过连接“short_description”和“description”来创建一个名为“text”的纯文本列。我们肯定希望可视化更新的数据帧!

***import** matplotlib.pyplot **as** plt
**import** seaborn **as** sns# Eliminate categories with fewer than 100 tickets
classifier = "u_portfolio"
ticket_threshold = 100**df_classifiers** = df[df.groupby(classifier[classifier].transform(len) > ticket_threshold]# Print number of relevant categories & shape
print(**"Categories: " + str(df_classifiers[classifier].nunique())**)# Plot the classifiers
fig = plt.figure(figsize=(10,6))
sns.barplot(df_classifiers[classifier].value_counts().index, df_classifiers[classifier].value_counts())
plt.xticks(rotation=20)
plt.show()*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在将阈值设置为 100 张票后,我们似乎删除了 5 个以上的类别,只返回那些具有相关商业价值的类别。在深入研究了数据并询问了一些人之后,我确认这些被删除的类别已经有一年多没有被使用了,可以轻松地被删除。

阶级失衡与企业咨询的精彩世界:

全球支持服务(GSS)占总支持票证的 60%以上。这意味着我们可以编写一个简单的程序来**将 GSS 分配给每一个传入的票证,这样我们就有一半的时间是正确的!

来源:戈登·拉姆齐经由cbs.com/corden

在没有做任何深入分析的情况下,我们发现了一个主要问题。第三方供应商对每张票的交互收费 13 美元,平均准确率为 40%,比我的客户不采取任何行动的情况还要糟糕……想象一下向首席信息官透露这个消息!**

剩余的类别将被用作标签* 来训练/测试模型。让我们将它们保存为一个列表:***

*****category_labels = list(df_classifiers[classifier].value_counts().index)*****

现在我们有了 category_labels ,我们需要更好地理解每种票据的文本模式。通过窥视 ServiceNow 平台,我可以很快按类别收集到几个主题: GSS 处理大量密码重置和硬件问题;商业智能涵盖报告功能和数据问题;客户处理 SalesForce 和其他客户应用; SAP S/4 Security 管理所有与 ERP 相关的访问/配置。如果你以前在这个公司工作过,这些主题听起来很熟悉。通过研究数据,人类很容易识别每个类别的几个关键词——让我们看看计算机是否也能做到这一点!

一旦我们运行代码,我们可以检查输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不幸的是,这里没有太多,因为最常见的单词在类别上几乎没有区别。我调查发现,邮件占了 75%以上的门票提交量;所有内部员工的签名下面都有某种版本的保密通知,这扭曲了类别之间的重大差异。我们可以尝试更改 N,看看是否会出现其他模式,或者将电子邮件签名硬编码到非索引字表变量中,以防止它出现,但这不会修复根本原因。而不是,我们想要找到与我们类别列表中的每个标签相关联的单词/短语。这被称为术语选择,可以帮助我们通过标签为数据集识别最相关的术语。**

让我们探索一些测量和评估相关性的 ML 解决方案!

构建模型

自然语言处理(NLP)位于计算机科学和语言学的结合点,定义了机器和人类语言如何相互交互的解决方案。在功能上,NLP 通过分析和操纵数据(通常以文本形式)来获取意义,从而消耗人类语言。为此,我们需要将人与人之间传递的数据转换成机器可读的数字格式。这种编码文本的过程被称为矢量化*,并催化计算过程,如应用数学规则和执行矩阵运算,从而产生有价值的见解。***

尽管有一些超级酷的新兴方法可以为 NLP 矢量化文本数据,如迁移学习和高级神经网络,但我们将使用一种更简单的技术,称为词频——逆文档频率*。Tf-idf 值与单词/短语(n-gram)在文档中出现的次数成比例增加,偏移量为文档总数。虽然听起来很复杂,但它基本上反映了 n-gram 对文档的重要性,而不偏向出现频率更高的单词。这对于处理像我们这样存在类不平衡的文本文档来说尤其强大!如果你的文本平衡良好,你可以使用计数矢量器。***

既然我们已经了解了计算机是如何消耗文本数据的,我们可以使用不同的模型进行实验!下面是一些测试几个选项的起始代码:

让我们使用逻辑回归作为我们的最佳拟合模型。作为一名数据科学家,您应该能够将多种技术应用于一个项目,并从中选择一种最适合的技术。根据我的经验,人类心理学在用例的成功中起着重要的作用;指导企业接受新兴的、颠覆性的技术需要时间和精力!推广、品牌化和销售您的解决方案与构建优雅的算法一样重要。让我们建立我们的模型吧!

在这个项目中,我有足够的机会系统地将概念社会化,以实时解决问题。对于这个用例,我的成功标准的一个主要部分是领导层理解并在上下文中传播这些数据科学概念。因为有许多其他算法/模型可以基于数据优化模型性能,所以我鼓励您进行试验。**

生产部署

当用户提交一张票时,很容易获取文本并通过模型进行处理。这样做,我们可以确定…

a)如果模型发现文本相关

b)哪个类别最适合该文本

***# Save the model to variable 'model'
model = pipe.fit(X_train, y_train)# Save array of predictions for given TEXT
predict_category = model.predict(TEXT) # Save array of prediction probabilities for given TEXT   
predict_probability = model.predict_proba(TEXT)***

我们的两个变量预测将返回一个与类别列表长度成比例的数字数组。如果我们打印 predict_category,我们期望一个 8 个数字的数组,对应于我们的 8 个类别,用 0 或 1 来表示相关性。如果文本字符串是“我需要一台新的公司笔记本电脑”,那么除了对应于“全球支持服务”的第 n 个位置的 1 之外,我们应该期待一个 0 的数组。我们可以使用“predict_probability”来查看在上下文中对 GSS 的预测结果有多强;对于这个特定的文本字符串,98%的概率是可信的,可以说我们信任这个模型😃。**

我们可以使用我们用来抓取数据的同一个表 API,用 PUT 请求替换 GET 响应,来更新 ServiceNow 中的票据。实时地,用户提交一张票,模型在不到一分钟的时间内用预测的类别更新 ServiceNow。让我们为实现了一个有效的机器学习解决方案而沾沾自喜吧!

在生产环境中部署模型取决于您的特定企业所订阅的技术堆栈。我的客户是一家 AWS 商店,通过访问全套 AWS 工具管理着良好的关系。

我使用了 LambdaSageMaker 在一个无服务器的 AWS 环境中自动分配支持票。然而,创建一个 EC2 实例来托管模型并与 ServiceNow 直接交互要容易得多。ServiceNow 具有内置的“业务规则”,可以对其进行配置以触发模型上的 API 调用并执行更新。最终的部署稍微便宜一些,而且在 EC2 服务器上更容易更新,并且依赖于 AWS 和 ServiceNow 的有效通信。AWS 文档以其深度和广度而闻名;我强烈建议在投入之前咨询适当的资源。

如果这些术语对你毫无意义,不要烦恼!基本上,机器学习管道需要被托管在与所涉及的人员和技术无关的环境中。如果新的开发人员加入进来,票证数量一夜之间增加了两倍,或者领导层选择使用 R 中的 KNN 而不是 Python 中的 LogReg,环境需要适应可变的规模。整个管道是在我的本地机器上开发的,但是从长远来看,生产部署不能依赖于我的计算资源和/或可用性。将它保存在服务器上(托管在云中)可以确保可持续性和有效性。这是构建阶段和实际部署之间的关键转变。

评估模型

在所有这些工作之后,我们完成了什么?首先,我们有一个很棒的 ML 解决方案,它使用自然语言处理对一家全球公司的所有事故单进行分类。通过自动化票证分配和规避第三方供应商,我们每年为企业节省近 60 万美元。我们将平均准确率从 40%提高到 85%,并将 SLA 响应时间缩短了一半!有趣的是,用户体验明显更加积极,对 ITSM 环境的信任度也直线上升。**

出乎意料的是,我对 ServiceNow 中数据和流程改进的高度关注有助于整合部门战略并提供更好的决策。由副总裁、董事和高级经理组成的 IT 领导层对节省资金、缩短响应时间和提升用户体验非常兴奋。尽管最初对支持新技术、在生产中操作模型、向供应商重构平台工作流等感到不安。领导层最终接受了改变。部署提供了民主化决策和传播复杂数据主题的机会。我相信,领导层能够更好地制定未来数据科学项目的战略并为其配备人员,将此使用案例展示为传达预测分析和数据科学成功的跳板。**

如果你对文章中概述的方法有任何问题或意见,请在下面留言或直接给我发消息!我很乐意收到你的来信😄。

学习 PyTorch 的同时预测英雄联盟比赛(第二部分)

原文:https://towardsdatascience.com/predict-league-of-legends-matches-while-learning-pytorch-part-2-38b8e982c7ea?source=collection_archive---------40-----------------------

学习在 PyTorch 中实现一个简单的前馈网络,并使用 GPU 为一个合适的用例场景进行训练,同时学习一些理论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个小系列的第二部分,手绘版!

读者朋友,你好!如果你还没有读过这个由 2 部分组成的“系列”的第一部分,我强烈推荐你在阅读之前阅读它。您可以在此处或下方进行操作👇

[## 在学习 PyTorch 基础知识的同时预测英雄联盟中的比赛

请跟我来,我将使用 PyTorch 实现一个逻辑回归模型来预测英雄联盟中的比赛

towardsdatascience.com](/predict-matches-in-league-of-legends-while-learning-pytorch-basics-3dd43cf8d16f)

上一次,我们停止了用 PyTorch 做一个逻辑回归器来达到同样的目的。这一次,我们将事情推进了一步:创建一个前馈神经网络(只有完全连接的层)。如果你想知道更多关于这样做的意图,这个迷你项目将使用的数据集,和/或数据集的数据准备过程,那么你应该看看我的第一篇文章这里或以上。

《英雄联盟》是我一直以来最喜欢的游戏之一,尽管我真的很不擅长。LOL 是一个极具竞争力的 MOBA,两个由 5 人组成的队伍*(蓝队和红队)*相互对抗,以摧毁对方的基地(nexus)。获胜通常需要大量的团队合作、协调,或者对于一个倾斜的玩家来说,“运气”。不管怎样,对于一个联盟玩家(即使他们是相当新的)来说,根据游戏记录的死亡人数和许多其他数据来判断哪个队可能会赢并不太难。神经网络可以预测的东西……

等等,什么是神经网络?

嘶!如果你不想学习一些理论,可以跳过这一部分。你会错过一些我自己的画:(

上次我们已经看到了逻辑回归模型如何在预测方面做得相当好(它在测试数据集上实现了高达 74%的准确性)。事实上,逻辑回归变量几乎完全是一个线性回归变量,它本身就是一个 T2,一个一批输入之间的矩阵点积,一个权重矩阵,外加一个偏差向量。 *可变的权重和偏差使模型能够训练并更好地做它正在做的事情。*线性回归和逻辑回归之间的唯一区别是,对于涉及简单的是或否问题(就像比赛中的一支球队赢得了比赛)或分类问题的预测,存在一个将输出“挤压”成一系列值(通常从 0 到 1)的函数。逻辑回归变量就是使用这种“挤压”函数的变量,它通常以 sigmoidsoftmax 函数的形式出现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

线性回归的基本数学,其中线性回归的矩阵运算用 sigmoid 函数包装。我画的😬 🔥

那么,神经网络如何设置自己以获得进一步的成功呢?简单地说,一个普通的神经网络是多个线性回归器堆叠在一起。 *理论上,这应该允许神经网络拾取数据之间的更多关系/趋势,以帮助预测。*但不可能这么简单!不做任何额外的事情,链接矩阵乘法和加法只会让我们一无所获。看一看:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这就是当你试图直接链接线性回归操作时会发生的情况。

您可以看到,将两个线性回归链接起来与仅一个线性回归同义,只是权重和偏差不同。那么,我们如何解决这个问题呢?我们引入一个非线性激活函数,它将环绕线性回归操作的每个实例。它不仅解决了上面普遍存在的问题,而且还模仿了(在某种意义上)生物神经元的工作方式。例如,神经元确定信号是否超过设定的阈值,以将信号向前传递到下一个神经元。类似地,激活函数将决定并调整神经网络层的最终输出。我可以详细说明激活功能,但我们会潜水太深!

顺便说一下,为了使我们对词汇的用法更趋向于约定俗成,从现在开始让我们把输入和输出之间的线性回归的每一个实例都称为一个 神经网络的隐藏层 ,而每一个个体的权重和偏差都称为一个 节点 *。*考虑到这一点,这就是神经网络的“样子”:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

或者,你可以谷歌搜索“神经网络”,你会看到更好的图像!

好了,现在让我们回到编码上来!如果你想要一个更直观的方法来研究神经网络&更多,看看 3blue1brown 关于深度神经网络的视频系列

制作前馈神经网络

TL;对于我们刚才所说的博士:一个神经网络基本上是多个线性回归操作(隐藏层)链接在一起,在每一层之后有一个 激活函数 *。*下面是定义模型时的样子:

每个特性的输入大小将是 29(参见第一篇文章),输出大小将是 2,每一个都是对团队输赢的预测。

当我们初始化模型时,我们现在有多个“nn.Linear”实例,我们将通过每一层和“F.relu()”传递输入(稍后将详细介绍)。

酷,但是什么是 *F.relu()*?整流线性单元(ReLU)是深度学习中使用的许多激活函数之一,与其他替代方法(例如 Sigmoid)相比,它的性能非常好。如果你想知道更多关于 ReLU 和其他激活功能的信息,去看看这篇文章。PyTorch 在torch.nn.functional(通常作为F导入)中提供了过多的激活函数,所以一定要检查他们的文档,看看你有哪些选项可以自己使用。

我们将使用 SGD 优化器和交叉熵损失函数来训练模型。我们将训练循环定义如下:

我们在这里定义了很多函数来形成训练循环。这里提供了注释,向您展示大多数代码行的用途。

在 GPU 上训练

随着神经网络模型变得越来越复杂,训练这些模型的计算需求也急剧增加。图形处理单元,被称为 GPU 或显卡,是专门设计来进行大规模矩阵运算的。如果你还不知道,除非启用,PyTorch 总是使用你的 CPU 进行计算,这肯定不如 GPU 有效。这一次,我们将发现如何利用 GPU 来为我们的神经网络处理数据。

在我们开始之前,只支持 NVIDIA GPUs,对不起 AMD 粉丝😢。

PyTorch 提供了一个函数torch.cuda.is_available(),它输出一个布尔值,表明安装了 CUDA 的兼容(NVIDIA) GPU 的存在。如果你有一个受支持的 GPU,你可以完成设置过程,或者你可以创建一个 kagglegoogle colab 帐户,并访问免费的 GPU 以进行深度学习(当然有一些限制)。让我们使用is_available()函数来设置 GPU 的使用,但是如果没有 GPU,就退回到 CPU:

Torch.device(…)是指 PyTorch 中可用的硬件。

使用 PyTorch,您可以通过使用任何张量或模型的.to()方法将数据移入和移出 GPU 设备。因此,要开始使用 GPU,您首先必须将您的模型移动到 GPU 上:

我们初始化模型“LOLModelmk2()”,并通过使用方法“to(device)”将其移动到 GPU,其中 device =“torch . device(“cuda”)”

现在,我们开始训练:

在训练之前用测试数据测试模型。损耗徘徊在 16%左右,准确率 50%。

您可以看到验证损失急剧下降,准确性出现峰值。

这种趋势在很小的范围内继续

下面是一些漂亮的图表😁:

这里是我们从测试数据集得到的结果:

嗯嗯…

嗯… 与线性回归模型(74%)相比,我们的模型看起来表现完全相同。现在,我们对这个结果有一些可能性:

  1. 某段代码不正确
  2. 神经网络通常比逻辑回归模型差
  3. 神经网络过拟合
  4. 逻辑回归模型在其训练中是幸运的(这是可能的,因为数据集被随机分为回归器和神经网络的训练集、验证集和测试集)
  5. 在这种情况下使用神经网络可能没有优势,我们正在经历收益递减。

好吧,我们用排除法,好吗?

经过长时间的调试,我在代码中没有发现任何错误(如果你发现了什么,请告诉我!!!),所以#1 出局了。#2 可能不是这种情况:我们之前建立了神经网络如何基于线性回归模型,线性回归模型基本上是没有 sigmoid/softmax 函数的逻辑回归。他们应该能够从数据中得出更多的关系,这需要更好的准确性,而不是相反。

#3 比其他两个更有可能,因为神经网络比逻辑回归更复杂,因此更容易接受这类问题。通常,过度拟合可以通过使用丢弃来解决,这仅仅意味着在训练时禁用随机选取的模型节点的一部分。对于 PyTorch 来说,这意味着在__init__()中初始化一个nn.Dropout()层,并用 ReLU 把它放在层之间。下面是实现过程:

我们只需初始化“nn.Dropout”的一个实例,因为它可以在模型类的 forward 函数中多次使用。

尽管如此,该模型在测试数据集上的准确率仍保持在 70%左右。

令人惊讶的是,即使这样也不起作用,这意味着模型没有过度拟合训练数据。最后,为了测试我们的假设#4,我在逻辑回归器上重新查看了我的旧笔记本,并用这个模型进行了几次试验。**事实证明,逻辑回归器上次 74%的准确率是相当幸运的。**事实上,让我们再来看看#个时期的精度图:

准确性在很大程度上是相当不稳定的,但总体来说,它徘徊在 70%左右,这更类似于我后来在逻辑回归和本文中的神经网络上运行的试验。

结论

通过这个例子,深度学习学科可以学到很多东西。主要是,深度学习不是巫毒魔法;它不能神奇地解决你给它的每一个分类问题。它不能预测每一场英雄联盟的比赛;在许多情况下,比赛的前 10 分钟不足以决定哪支球队会赢(我可以通过我的经验证明)。尽管如此,从这种经历中还是有很多收获的,比如学习神经网络的概念并在 PyTorch 中实现它,利用 GPU,以及在模型过拟合的情况下退出。在这一点上,我希望你喜欢和我一起为这个英雄联盟数据集构建 PyTorch 模型的旅程。编码快乐(还有继续打联赛)!

如果你想知道这个迷你项目使用的 jupyter 笔记本的来源,请看这里:https://jovian.ml/richardso21/lol-nn

预测勒布朗詹姆斯与 RNN 的比赛结果

原文:https://towardsdatascience.com/predict-lebron-jamess-game-results-with-rnn-1709f364d4d4?source=collection_archive---------43-----------------------

机器学习

众所周知,RNN 擅长分析序列数据。但是我们能应用它根据游戏历史预测游戏结果吗?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

埃德加·恰帕罗在 Unsplash 上拍摄的照片

A 递归神经网络(RNN)是序列处理中最常用的深度学习算法之一。RNN 和前馈神经网络之间的区别在于,前者可以“记忆”序列中的时序信息,而后者可以一次性处理整个序列。

因此,当数据的时间顺序对问题很重要时,我们最好使用 RNN,例如时间序列数据分析或自然语言处理(NLP)。

然而,一个球员之前的比赛是否能够影响即将到来的比赛的结果仍在争论之中。所以,我想探究一下 RNN 是否适合篮球场上的比赛预测问题。

在这篇短文中,我试图使用一个深度学习模型,根据之前的比赛统计数据和结果来预测勒布朗·詹姆斯未来的比赛结果。

数据集

我用 Python 刮了勒布朗从 赛季 2003–04赛季 2019–20场 1258 场的比赛统计。对刮痧程序感兴趣的可以参考我之前的一篇帖子

这是数据的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据头

我的另一个帖子中,我已经说明了“GmSc”(玩家的游戏分数)、“+/-”(加减分)和“Minutes”(上场时间)是解释相应游戏结果的三个最重要的特征。因此,为了减少输入数据的维度,我只在我们的模型中保留这些游戏统计数据和游戏结果(“赢”)。对于每个数据点,我提取过去 10 场比赛的统计数据(t-9,t-8,…,t-1,t),目的是预测下一场比赛(t + 1)的比赛结果。

我用前 800 场作为我们的训练数据,后 300 场作为验证数据,后 158 场作为测试数据。

数据的整个预处理如下所示。

data_u = df[["GmSc","+/-","Minutes","Win"]].to_numpy()
mean = data_u[:800].mean(axis=0)
data_u -= mean
std = data_u[:800].std(axis=0)
data_u /= std

标准化参数(平均值和标准偏差)仅从训练数据(前 800 场比赛)中生成,然后将这些参数应用于整个数据集。

加载整个数据集非常消耗内存,有时甚至是不可能的。所以使用数据生成器(在 Keras 中实现)向模型提供数据是处理这个问题的一个好选择。即使这里我有一个非常小的(1,258 个数据点)数据集,我仍然希望在我的管道中使用一个数据生成器,这将有利于将来的扩展。

def generator(data, lookback, delay, start, end, batch_size = 64):
    if end is None:
        end = len(data) - delay - 1
    i = start + lookback
    while True:
        if i + batch_size >= end:
            i = start + lookback
        rows = np.arange(i, min(i + batch_size, end))
        i += len(rows)
        samples = np.zeros((len(rows),
                           lookback,
                           data.shape[-1]))
        res_s = np.zeros((len(rows),))

        for j, row in enumerate(rows):
            indices = range(rows[j] - lookback, rows[j])
            samples[j] = data[indices]
            tar_v = data[rows[j] + delay][3]
            if tar_v > 0:
                res_s[j] = 1
            else:
                res_s[j] = 0
        yield samples, res_s

我将回看设置为 10,这意味着使用之前的 10 场比赛作为输入。我把延迟设为 1,表示预测下一场比赛结果。

lookback = 10
delay = 1
batch_size = 128
steps_per_epc = int(800/batch_size)

为了定义训练、验证和测试数据生成器,我只需要将开始和结束值提供给生成器函数。

train_generator = generator(data_u,
                           lookback = lookback,
                           delay = delay,
                           start = 0,
                           end = 800,
                           batch_size = batch_size)val_generator = generator(data_u,
                           lookback = lookback,
                           delay = delay,
                           start = 801,
                           end = 1100,
                           batch_size = batch_size)test_generator = generator(data_u,
                           lookback = lookback,
                           delay = delay,
                           start = 1101,
                           end = None,
                           batch_size = batch_size)

相应地,我需要指定检查验证和测试数据集所需的步骤数量。

val_steps = (1100 - 801 - lookback)
test_steps = (len(data_u) - 1101 - lookback)

接下来,我将构建模型结构。

建模结果

首先,我构建了一个具有密集连接层的人工神经网络作为我的基线模型,以便与我的其他模型进行比较。

from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSpropmodel_ann = Sequential()
model_ann.add(layers.Flatten(input_shape = (lookback, data_u.shape[-1])))
model_ann.add(layers.Dense(32,activation = 'relu'))
model_ann.add(layers.Dropout(0.3))
model_ann.add(layers.Dense(1,activation = 'sigmoid'))
model_ann.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

人工神经网络模型结构

然后,我编译模型,记录拟合过程。

model_ann.compile(optimizer = RMSprop(lr = 1e-2),
                 loss = 'binary_crossentropy',
                 metrics = ['acc'])
history = model_ann.fit_generator(train_generator,
                                  steps_per_epoch=steps_per_epc,
                              epochs = 20, 
                              validation_data = val_generator,
                              validation_steps = val_steps)

为了检查验证数据集的性能,我绘制了损失曲线。

acc_ = history_dic['loss']
val_acc_ = history_dic['val_loss']
epochs = range(1,21)
#plt.clf()
plt.plot(epochs,acc_, 'bo', label = "training loss")
plt.plot(epochs, val_acc_, 'r', label = "validation loss")
plt.xlabel('Epochs')
plt.ylabel('loss')
plt.legend()
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练和验证人工神经网络损失

正如所料,模型在几个时期后变得过度拟合。为了客观地评价该模型,我将其应用于测试集,得到的准确率为 60%。

scores = model_ann.evaluate_generator(test_generator,test_steps) 
print("Accuracy = ", scores[1]," Loss = ", scores[0])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

人工神经网络测试集性能

接下来,我通过使用一个 LSTM 层后跟两个紧密连接的层来实现 RNN。

model_rnn = Sequential()
model_rnn.add(layers.LSTM(32,
                        dropout=0.2,
                        recurrent_dropout=0.2,
                        input_shape=(None,data_u.shape[-1])))model_rnn.add(layers.Dense(32,activation = 'relu'))
model_rnn.add(layers.Dropout(0.3))
model_rnn.add(layers.Dense(1,activation='sigmoid'))
model_rnn.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一层 LSTM 结构

模型训练类似于上面的 ANN。

model_rnn.compile(optimizer = RMSprop(lr = 1e-2),
                 loss = 'binary_crossentropy',
                 metrics = ['acc'])
history = model_rnn.fit_generator(train_generator, 
                                  steps_per_epoch=steps_per_epc,
                              epochs = 20, 
                              validation_data = val_generator,
                              validation_steps = val_steps)

训练集和验证集的性能如下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1 层 LSTM 损耗

过拟合不像人工神经网络那样严重。我还在测试数据上评估了该模型,其准确率为 62.5%。尽管在测试集上的性能优于具有密集连接层的人工神经网络,但是改进是微小的。

为了获得更好的性能,我试图通过增加一个递归层来增加模型的复杂性。然而,为了减少计算成本,我用门控递归单元(GRU)代替了 LSTM 层。模型如下所示。

model_rnn = Sequential()
model_rnn.add(layers.GRU(32,
                        dropout=0.2,
                        recurrent_dropout=0.2,
                         return_sequences = True,
                        input_shape=(None,data_u.shape[-1])))
model_rnn.add(layers.GRU(64, activation = 'relu',dropout=0.2,recurrent_dropout=0.2))
model_rnn.add(layers.Dense(32,activation = 'relu'))
model_rnn.add(layers.Dropout(0.3))model_rnn.add(layers.Dense(1,activation = 'sigmoid'))
model_rnn.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2 层 GRU 结构

训练集和验证集的性能如下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二层 GRU 损失

在图上没有检测到严重的过度拟合。即使测试数据的准确性已经提高到 64%,改进仍然很小。我开始怀疑 RNN 能否胜任这项工作。

然而,我通过进一步增加模型的复杂性来做最后的尝试。具体来说,我使递归层成为双向的。

model_rnn = Sequential()
model_rnn.add(layers.Bidirectional(layers.GRU(32,
                        dropout=0.2,
                        recurrent_dropout=0.2,
                         return_sequences = True),
                        input_shape=(None,data_u.shape[-1])))
model_rnn.add(layers.Bidirectional(layers.GRU(64, activation = 'relu',dropout=0.2,recurrent_dropout=0.2)))model_rnn.add(layers.Dense(32,activation = 'relu'))
model_rnn.add(layers.Dropout(0.3))
model_rnn.add(layers.Dense(1,activation='sigmoid'))
model_rnn.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

双向 RNN 结构

这一次,训练集和验证集的性能如下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

双向损失

实际上,在模型开始过拟合之前,这个模型和上一个模型在验证损失上没有太大区别。测试集的准确率也达到了 64%。

通过探索上面的所有模型,我有点意识到 RNN 可能不太适合 NBA 比赛结果预测问题。确实有几十个超参数可以调整,但是人工神经网络和 RNN 之间的差异太小。

数据诊断

我不想被测试集准确性的值所迷惑,所以我进一步诊断数据集,以检查模型是否真的有效。

我在训练、验证和测试数据集中检查勒布朗的詹姆斯胜率。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

胜率的数据诊断

哎呀!测试数据集中的胜率是 62.7%,这意味着如果我猜测勒布朗会赢得所有比赛,我仍然得到 62.7%的准确率。

这些结果表明,我的模型并不比随机猜测好多少。伤心…

可能是我没有全面探索超参数空间,也可能是勒布朗的比赛不可预测。但有一点是肯定的,大约 1000 个数据点远远不够。

讨论

在本文中,我展示了一个为游戏结果预测问题开发失败模型的例子。如果你认为你没有从中学到什么,我想强迫自己列出一些要点。至少你可能会在建模中遇到这些细节。

  1. 首先将模型推向过度拟合是很重要的,因为一个具有弱表示能力的模型永远不会解决问题。
  2. 如果你的模型在大量的时期后仍然不合适,比如说 40 个,你可能需要增加学习率。
  3. 如果你只接触到有限的数据,没有什么是新奇的。你的复杂模型可能比随机猜测更糟糕。

参考资料:

  1. 弗朗索瓦·乔莱。用 Python 进行深度学习。
  2. 篮球参考。

谢谢你的时间。如果你喜欢读这篇文章,请关注我的媒体。以下是我之前的一些文章。

[## 我以前从未注意到的过度拟合的一个潜在原因

当训练数据中的性能比测试数据中的性能好得多时,就会发生过度拟合。默认…

towardsdatascience.com](/one-potential-cause-of-overfitting-that-i-never-noticed-before-a57904c8c89d) [## 谁是本赛季 NBA 的最有价值球员?

一个案例研究,展示一个机器学习项目从开始到结束的样子。

towardsdatascience.com](/whos-the-mvp-of-nba-this-season-3e347c66a40a) [## 是否在我的模型中考虑多重共线性?

简要讨论是否有必要修复特征空间中的多重共线性。我希望它会…

towardsdatascience.com](/consider-multicollinearity-in-my-model-or-not-7aca16e74773) 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

皮特拉·施瓦兹勒在 Unsplash 上的照片

如何使用机器学习模型预测贷款资格

原文:https://towardsdatascience.com/predict-loan-eligibility-using-machine-learning-models-7a14ef904057?source=collection_archive---------0-----------------------

构建预测模型,以自动确定合适的申请人。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

纽约公共图书馆拍摄于 Unsplash

介绍

贷款是银行的核心业务。主要利润直接来自贷款利息。贷款公司在经过严格的验证和确认后发放贷款。然而,他们仍然没有保证,如果申请人能够毫无困难地偿还贷款。

在本教程中,我们将建立一个预测模型来预测申请人是否有能力偿还贷款公司。我们将使用 Jupyter Notebook 准备数据,并使用各种模型来预测目标变量。

[## mridulrb/Predict-loan-eligibility-using-IBM-Watson-Studio

贷款是贷款公司的核心业务。主要利润直接来自贷款利息。贷款…

github.com](https://github.com/mridulrb/Predict-loan-eligibility-using-IBM-Watson-Studio)

注册一个 IBM Cloud 账号来试试这个教程——

[## IBM 云

使用 190 多种独特的服务立即开始建设。

ibm.biz](https://ibm.biz/BdqQBT)

目录

  1. 准备好系统并加载数据
  2. 理解数据
  3. 探索性数据分析(EDA)
    一、单变量分析
    二。双变量分析
  4. 缺失值和异常值处理
  5. 分类问题的评估标准
  6. 模型构建:第 1 部分
  7. 使用分层 k 倍交叉验证的逻辑回归
  8. 特征工程
  9. 建模:第二部分
    一、逻辑回归
    二。决策树
    三。随机森林
    四。XGBoost

准备好系统并加载数据

我们将在本课程中使用 Python 和下面列出的库。

规格

  • 计算机编程语言
  • 熊猫
  • 海生的
  • sklearn
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings(“ignore”)

数据

对于这个问题,我们有三个 CSV 文件:训练、测试和样本提交。

  • 训练文件将用于训练模型,即我们的模型将从该文件中学习。它包含所有的自变量和目标变量。
  • 测试文件包含所有自变量,但不包含目标变量。我们将应用该模型来预测测试数据的目标变量。
  • 样本提交文件包含了我们提交预测的格式

读取数据

train = pd.read_csv(‘Dataset/train.csv’)
train.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

test = pd.read_csv(‘Dataset/test.csv’)
test.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们制作一份训练和测试数据的副本,这样即使我们必须在这些数据集中进行任何更改,我们也不会丢失原始数据集。

train_original=train.copy()
test_original=test.copy()

理解数据

train.columnsIndex(['Loan_ID', 'Gender', 'Married', 'Dependents', 'Education',
       'Self_Employed', 'ApplicantIncome', 'CoapplicantIncome', 'LoanAmount',
       'Loan_Amount_Term', 'Credit_History', 'Property_Area', 'Loan_Status'],
      dtype='object')

我们有 12 个自变量和 1 个目标变量,即训练数据集中的 Loan_Status。

test.columnsIndex(['Loan_ID', 'Gender', 'Married', 'Dependents', 'Education',
       'Self_Employed', 'ApplicantIncome', 'CoapplicantIncome', 'LoanAmount',
       'Loan_Amount_Term', 'Credit_History', 'Property_Area'],
      dtype='object')

除了 Loan_Status 之外,测试数据集中的特性与训练数据集中的特性相似。我们将使用利用训练数据构建的模型来预测 Loan_Status。

train.dtypesLoan_ID               object
Gender                object
Married               object
Dependents            object
Education             object
Self_Employed         object
ApplicantIncome        int64
CoapplicantIncome    float64
LoanAmount           float64
Loan_Amount_Term     float64
Credit_History       float64
Property_Area         object
Loan_Status           object
dtype: object

我们可以看到有三种格式的数据类型:

  • 对象:对象格式意味着变量是分类的。我们数据集中的分类变量是 Loan_ID、性别、已婚、受抚养人、教育、自雇、财产面积、贷款状态。
  • int64:代表整数变量。ApplicantIncome 是这种格式。
  • float64:它表示包含一些小数值的变量。它们也是数字的
train.shape(614, 13)

我们在训练数据集中有 614 行和 13 列。

test.shape(367, 12)

我们在测试数据集中有 367 行和 12 列。

train[‘Loan_Status’].value_counts()Y    422
N    192
Name: Loan_Status, dtype: int64

Normalize 可以设置为 True 来打印比例而不是数字

train[‘Loan_Status’].value_counts(normalize=True) Y    0.687296
N    0.312704
Name: Loan_Status, dtype: float64train[‘Loan_Status’].value_counts().plot.bar()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

614 人中有 422 人(约 69%)的贷款获得批准。

现在,让我们分别把每个变量形象化。不同类型的变量有分类变量、顺序变量和数值变量。

  • 分类特征:这些特征有类别(性别、已婚、自雇、信用记录、贷款状态)
  • 顺序特征:分类特征中的变量,具有某种相关的顺序(家属、教育、财产面积)
  • 数字特征:这些特征有数值(申请收入、共同申请收入、贷款金额、贷款金额期限)

独立变量(分类)

train[‘Gender’].value_counts(normalize=True).plot.bar(figsize=(20,10), title=’Gender’)
plt.show()
train[‘Married’].value_counts(normalize=True).plot.bar(title=’Married’)
plt.show()
train[‘Self_Employed’].value_counts(normalize=True).plot.bar(title=’Self_Employed’)
plt.show()
train[‘Credit_History’].value_counts(normalize=True).plot.bar(title=’Credit_History’)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从上面的柱状图可以推断出:

  • 数据集中 80%的申请者是男性。
  • 数据集中大约 65%的申请者已婚。
  • 数据集中大约 15%的申请者是个体经营者。
  • 大约 85%的申请者打消了他们的疑虑。

独立变量(序数)

train[‘Dependents’].value_counts(normalize=True).plot.bar(figsize=(24,6), title=’Dependents’)
plt.show()
train[‘Education’].value_counts(normalize=True).plot.bar(title=’Education’)
plt.show()
train[‘Property_Area’].value_counts(normalize=True).plot.bar(title=’Property_Area’)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从上面的柱状图可以得出以下推论:

  • 大多数申请人没有任何家属。
  • 大约 80%的申请者是毕业生。
  • 大多数申请者来自半城市地区。

独立变量(数字)

到目前为止,我们已经看到了分类变量和顺序变量,现在让我们来看看数字变量。我们先来看一下申请人收入的分布。

sns.distplot(train[‘ApplicantIncome’])
plt.show()
train[‘ApplicantIncome’].plot.box(figsize=(16,5))
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以推断,申请人收入分布中的大部分数据是向左的,这意味着它不是正态分布的。我们将在后面的章节中尝试使其正常化,因为如果数据呈正态分布,算法会工作得更好。

箱线图证实了大量异常值/极值的存在。这可以归因于社会的收入差距。部分原因可能是因为我们关注的是不同教育水平的人。让我们通过教育把他们分开。

train.boxplot(column=’ApplicantIncome’, by = ‘Education’) 
plt.suptitle(“”)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,有更多的高收入毕业生,他们似乎是局外人。

让我们看看共同申请人的收入分布。

sns.distplot(train[‘CoapplicantIncome’])
plt.show()
train[‘CoapplicantIncome’].plot.box(figsize=(16,5))
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们看到与申请人的收入分布相似的分布。大多数共同申请人的收入范围从 0 到 5000 英镑。我们也看到申请人的收入中有很多异常值,而且不是正态分布的。

train.notna()
sns.distplot(train[‘LoanAmount’])
plt.show()
train[‘LoanAmount’].plot.box(figsize=(16,5))
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们在这个变量中看到许多异常值,并且分布相当正常。我们将在后面的章节中处理异常值。

双变量分析

让我们回忆一下我们之前提出的一些假设:

  • 高收入的申请者应该有更多的贷款批准机会。
  • 偿还了以前债务的申请人应该有更高的贷款批准机会。
  • 贷款审批也应取决于贷款金额。贷款金额少的话,贷款获批的几率应该高。
  • 每月偿还贷款的金额越少,贷款获得批准的机会就越大。

让我们尝试使用双变量分析来检验上述假设。

在单变量分析中单独研究了每个变量后,我们现在将再次研究它们与目标变量的关系。

分类自变量与目标变量

首先,我们将找到目标变量和分类自变量之间的关系。现在让我们看看堆积条形图,它将给出已批准和未批准贷款的比例。

Gender=pd.crosstab(train[‘Gender’],train[‘Loan_Status’])
Gender.div(Gender.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以推断,无论是批准的贷款还是未批准的贷款,男女申请人的比例大致相同。

现在让我们把剩下的分类变量和目标变量形象化。

Married=pd.crosstab(train[‘Married’],train[‘Loan_Status’])
Dependents=pd.crosstab(train[‘Dependents’],train[‘Loan_Status’])
Education=pd.crosstab(train[‘Education’],train[‘Loan_Status’])
Self_Employed=pd.crosstab(train[‘Self_Employed’],train[‘Loan_Status’])
Married.div(Married.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Dependents.div(Dependents.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Education.div(Education.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Self_Employed.div(Self_Employed.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 对于已批准的贷款,已婚申请人的比例更高。
  • 有 1 个或 3 个以上受抚养人的申请人在两种贷款状态类别中的分布相似。
  • 从自营职业者与贷款者的对比图中,我们无法推断出任何有意义的东西。

现在我们来看看剩余的分类自变量和 Loan_Status 之间的关系。

Credit_History=pd.crosstab(train[‘Credit_History’],train[‘Loan_Status’])
Property_Area=pd.crosstab(train[‘Property_Area’],train[‘Loan_Status’])
Credit_History.div(Credit_History.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True,figsize=(4,4))
plt.show()
Property_Area.div(Property_Area.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 看起来信用记录为 1 的人更有可能获得贷款批准。
  • 与农村或城市地区相比,半城市地区获得批准的贷款比例更高。

现在,让我们想象一下相对于目标变量的独立变量的数值。

数字自变量与目标变量

我们将尝试找出贷款已被批准的人的平均收入与贷款未被批准的人的平均收入。

train.groupby(‘Loan_Status’)[‘ApplicantIncome’].mean().plot.bar()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里的 y 轴代表申请人的平均收入。我们看不到平均收入有任何变化。因此,让我们根据申请人收入变量中的值为其创建箱,并分析每个箱对应的贷款状态。

bins=[0,2500,4000,6000,81000]
group=[‘Low’,’Average’,’High’,’Very high’]
train[‘Income_bin’]=pd.cut(train[‘ApplicantIncome’],bins,labels=group)
Income_bin=pd.crosstab(train[‘Income_bin’],train[‘Loan_Status’])
Income_bin.div(Income_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘ApplicantIncome’)
P=plt.ylabel(‘Percentage’)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以推断,申请人的收入不影响贷款批准的机会,这与我们的假设相矛盾,我们假设如果申请人的收入高,贷款批准的机会也高。

我们将以类似的方式分析共同申请人的收入和贷款额变量。

bins=[0,1000,3000,42000]
group=[‘Low’,’Average’,’High’]
train[‘Coapplicant_Income_bin’]=pd.cut(train[‘CoapplicantIncome’],bins,labels=group)
Coapplicant_Income_bin=pd.crosstab(train[‘Coapplicant_Income_bin’],train[‘Loan_Status’])
Coapplicant_Income_bin.div(Coapplicant_Income_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘CoapplicantIncome’)
P=plt.ylabel(‘Percentage’)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它表明,如果共同申请人的收入较低,贷款批准的机会很高。但这看起来不对。这背后可能的原因是,大多数申请人没有任何共同申请人,因此这些申请人的共同申请人收入为 0,因此贷款审批不依赖于此。因此,我们可以创建一个新的变量,将申请人和共同申请人的收入结合起来,以可视化收入对贷款审批的综合影响。

让我们结合申请人收入和共同申请人收入,看看总收入对贷款状态的综合影响。

train[‘Total_Income’]=train[‘ApplicantIncome’]+train[‘CoapplicantIncome’]
bins=[0,2500,4000,6000,81000]
group=[‘Low’,’Average’,’High’,’Very high’]
train[‘Total_Income_bin’]=pd.cut(train[‘Total_Income’],bins,labels=group)
Total_Income_bin=pd.crosstab(train[‘Total_Income_bin’],train[‘Loan_Status’])
Total_Income_bin.div(Total_Income_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘Total_Income’)
P=plt.ylabel(‘Percentage’)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,与平均收入、高收入和非常高收入的申请人相比,低总收入的申请人获得贷款批准的比例非常低。

让我们想象一下贷款金额变量。

bins=[0,100,200,700]
group=[‘Low’,’Average’,’High’]
train[‘LoanAmount_bin’]=pd.cut(train[‘LoanAmount’],bins,labels=group)
LoanAmount_bin=pd.crosstab(train[‘LoanAmount_bin’],train[‘Loan_Status’])
LoanAmount_bin.div(LoanAmount_bin.sum(1).astype(float), axis=0).plot(kind=”bar”,stacked=True)
plt.xlabel(‘LoanAmount’)
P=plt.ylabel(‘Percentage’)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看出,与高贷款额相比,低贷款额和平均贷款额的批准贷款比例更高,这支持了我们的假设,即我们认为当贷款额较低时,贷款批准的机会将会较高。

让我们删除为探索部分创建的垃圾箱。我们将把从属变量中的 3+改为 3,使它成为一个数字变量。我们还会将目标变量的类别转换为 0 和 1,以便我们可以找到它与数值变量的相关性。这样做的另一个原因是像逻辑回归这样的模型很少只接受数值作为输入。我们将用 0 代替 N,用 1 代替 Y。

train=train.drop([‘Income_bin’, ‘Coapplicant_Income_bin’, ‘LoanAmount_bin’, ‘Total_Income_bin’, ‘Total_Income’], axis=1)
train[‘Dependents’].replace(‘3+’, 3,inplace=True)
test[‘Dependents’].replace(‘3+’, 3,inplace=True)
train[‘Loan_Status’].replace(’N’, 0,inplace=True)
train[‘Loan_Status’].replace(‘Y’, 1,inplace=True)

现在让我们看看所有数值变量之间的相关性。我们将使用热图来可视化这种关联。热图通过不同的颜色将数据可视化。颜色越深的变量表示相关性越大。

matrix = train.corr()
f, ax = plt.subplots(figsize=(9,6))
sns.heatmap(matrix,vmax=.8,square=True,cmap=”BuPu”, annot = True)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们看到最相关的变量是(申请收入—贷款金额)和(信用记录—贷款状态)。贷款金额也与共同申请人收入相关。

缺失值插补

让我们列出缺失值的特性计数。

train.isnull().sum()
Loan_ID               0
Gender               13
Married               3
Dependents           15
Education             0
Self_Employed        32
ApplicantIncome       0
CoapplicantIncome     0
LoanAmount           22
Loan_Amount_Term     14
Credit_History       50
Property_Area         0
Loan_Status           0
dtype: int64

性别、已婚、受抚养人、自营职业、贷款金额、贷款金额期限和信用历史记录要素中缺少值。

我们将逐一处理所有特性中缺失的值。

我们可以考虑用这些方法来填补缺失值:

  • 对于数值变量:使用平均数或中位数进行插补
  • 对于分类变量:使用模式插补

性别、已婚、受抚养人、信用记录和自营职业要素中很少有缺失值,因此我们可以使用要素的模式来填充它们。

train[‘Gender’].fillna(train[‘Gender’].mode()[0], inplace=True)
train[‘Married’].fillna(train[‘Married’].mode()[0], inplace=True)
train[‘Dependents’].fillna(train[‘Dependents’].mode()[0], inplace=True)
train[‘Self_Employed’].fillna(train[‘Self_Employed’].mode()[0], inplace=True)
train[‘Credit_History’].fillna(train[‘Credit_History’].mode()[0], inplace=True)

现在让我们尝试找到一种方法来填充 Loan_Amount_Term 中缺少的值。我们将查看贷款金额期限变量的值计数。

train[‘Loan_Amount_Term’].value_counts()
360.0    512
180.0     44
480.0     15
300.0     13
84.0       4
240.0      4
120.0      3
36.0       2
60.0       2
12.0       1
Name: Loan_Amount_Term, dtype: int64

可以看出,在贷款金额期限变量中,360 的值是重复最多的。所以我们会用这个变量的模式来替换这个变量中缺失的值。

train[‘Loan_Amount_Term’].fillna(train[‘Loan_Amount_Term’].mode()[0], inplace=True)

现在我们将看到 LoanAmount 变量。由于它是一个数值变量,我们可以使用均值或中值来估算缺失值。我们将使用中值来填充空值,因为之前我们看到贷款金额有异常值,所以平均值不是正确的方法,因为它受异常值的影响很大。

train[‘LoanAmount’].fillna(train[‘LoanAmount’].median(), inplace=True)

现在,让我们检查数据集中是否填充了所有缺失的值。

train.isnull().sum()
Loan_ID              0
Gender               0
Married              0
Dependents           0
Education            0
Self_Employed        0
ApplicantIncome      0
CoapplicantIncome    0
LoanAmount           0
Loan_Amount_Term     0
Credit_History       0
Property_Area        0
Loan_Status          0
dtype: int64

正如我们所看到的,所有缺失的值都已经被填充到测试数据集中。让我们用同样的方法填充测试数据集中所有缺失的值。

test[‘Gender’].fillna(train[‘Gender’].mode()[0], inplace=True)
test[‘Married’].fillna(train[‘Married’].mode()[0], inplace=True)
test[‘Dependents’].fillna(train[‘Dependents’].mode()[0], inplace=True)
test[‘Self_Employed’].fillna(train[‘Self_Employed’].mode()[0], inplace=True)
test[‘Credit_History’].fillna(train[‘Credit_History’].mode()[0], inplace=True)
test[‘Loan_Amount_Term’].fillna(train[‘Loan_Amount_Term’].mode()[0], inplace=True)
test[‘LoanAmount’].fillna(train[‘LoanAmount’].median(), inplace=True)

异常值处理

正如我们在前面的单变量分析中看到的,LoanAmount 包含异常值,因此我们必须将它们视为异常值的存在会影响数据的分布。让我们来看看有离群值的数据集会发生什么。对于样本数据集:
1,1,2,2,2,2,3,3,3,4,4
我们发现如下:均值、中值、众数和标准差
均值= 2.58
中值= 2.5
众数=2
标准差= 1.08
如果我们向数据集添加一个异常值:
1,1,2,2,2,2,2,2,3,3,4,4400
我们必须采取措施消除数据集中的异常值。
由于这些异常值,贷款金额中的大部分数据位于左侧,右尾较长。这叫做右偏度。消除偏斜的一种方法是进行对数变换。当我们进行对数变换时,它不会对较小的值产生太大的影响,但会减少较大的值。所以,我们得到一个类似于正态分布的分布。
我们来可视化一下 log 变换的效果。我们将同时对测试文件进行类似的修改。

train[‘LoanAmount_log’]=np.log(train[‘LoanAmount’])
train[‘LoanAmount_log’].hist(bins=20)
test[‘LoanAmount_log’]=np.log(test[‘LoanAmount’])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,分布看起来更接近正常,极端值的影响已经大大减弱。让我们建立一个逻辑回归模型,并对测试数据集进行预测。

模型构建:第一部分

让我们用第一个模型来预测目标变量。我们将从用于预测二元结果的逻辑回归开始。

  • 逻辑回归是一种分类算法。它用于预测给定一组独立变量的二元结果(1 / 0,是/否,真/假)。
  • 逻辑回归是对 Logit 函数的估计。logit 函数仅仅是对事件有利的概率的记录。
  • 该函数使用概率估计值创建一条 S 形曲线,这与所需的逐步函数非常相似

要进一步了解逻辑回归,请参考本文:https://www . analyticsvidhya . com/blog/2015/10/basics-logistic-regression/
让我们删除 Loan_ID 变量,因为它对贷款状态没有任何影响。我们将对测试数据集进行与训练数据集相同的更改。

train=train.drop(‘Loan_ID’,axis=1)
test=test.drop(‘Loan_ID’,axis=1)

我们将使用 scikit-learn (sklearn)来制作不同的模型,这是 Python 的一个开源库。它是最有效的工具之一,包含许多内置函数,可用于 Python 建模。

想进一步了解 sklearn,参考这里:http://scikit-learn.org/stable/tutorial/index.html

Sklearn 需要单独数据集中的目标变量。因此,我们将从训练数据集中删除目标变量,并将其保存在另一个数据集中。

X = train.drop(‘Loan_Status’,1)
y = train.Loan_Status

现在我们将为分类变量制造虚拟变量。虚拟变量将分类变量转化为一系列 0 和 1,使它们更容易量化和比较。让我们先了解一下假人的流程:

  • 考虑“性别”变量。它有两个阶层,男性和女性。
  • 由于逻辑回归只将数值作为输入,我们必须将男性和女性转换为数值。
  • 一旦我们对这个变量应用了虚拟变量,它就会将“性别”变量转换为两个变量(性别 _ 男性和性别 _ 女性),每个类一个,即男性和女性。
  • 如果性别为女性,则性别 _ 男性的值为 0,如果性别为男性,则值为 1。
X = pd.get_dummies(X)
train=pd.get_dummies(train)
test=pd.get_dummies(test)

现在,我们将在训练数据集上训练模型,并对测试数据集进行预测。但是我们能证实这些预测吗?一种方法是,我们可以将训练数据集分为两部分:训练和验证。我们可以在这个训练部分训练模型,并使用它对验证部分进行预测。这样,我们可以验证我们的预测,因为我们有验证部分的真实预测(我们没有测试数据集的真实预测)。

我们将使用 sklearn 的 train_test_split 函数来划分我们的训练数据集。所以,首先让我们导入 train_test_split。

from sklearn.model_selection import train_test_split
x_train, x_cv, y_train, y_cv = train_test_split(X,y, test_size=0.3)

数据集分为训练和验证两部分。让我们从 sklearn 导入 LogisticRegression 和 accuracy_score 并拟合逻辑回归模型。

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
model = LogisticRegression()
model.fit(x_train, y_train)LogisticRegression()

这里,C 参数表示正则化强度的倒数。正则化是应用惩罚来增加参数值的幅度,以便减少过度拟合。C 值越小,正则化越强。要了解其他参数,请参考这里:http://sci kit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html

让我们预测验证集的 Loan_Status 并计算其准确性。

pred_cv = model.predict(x_cv)
accuracy_score(y_cv,pred_cv)0.7891891891891892

因此,我们的预测几乎 80%准确,也就是说,我们已经正确识别了 80%的贷款状态。

让我们对测试数据集进行预测。

pred_test = model.predict(test)

让我们导入我们必须在解决方案检查器上提交的提交文件。

submission = pd.read_csv(‘Dataset/sample_submission.csv’)
submission.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们只需要最终提交的 Loan_ID 和相应的 Loan_Status。我们将用测试数据集的 Loan_ID 和我们做出的预测(即 pred_test)分别填充这些列。

submission[‘Loan_Status’]=pred_test
submission[‘Loan_ID’]=test_original[‘Loan_ID’]

记住我们需要 Y 和 n 的预测,所以让我们把 1 和 0 转换成 Y 和 n。

submission[‘Loan_Status’].replace(0, ’N’, inplace=True)
submission[‘Loan_Status’].replace(1, ‘Y’, inplace=True)

最后,我们将把提交转换成。csv 格式。

pd.DataFrame(submission, columns=[‘Loan_ID’,’Loan_Status’]).to_csv(‘Output/logistic.csv’)

使用分层 k 倍交叉验证的逻辑回归

为了检查我们的模型对看不见的数据有多稳健,我们可以使用验证。这是一种涉及保留数据集的特定样本的技术,您不需要在该样本上训练模型。稍后,在最终确定之前,您将在这个样本上测试您的模型。下面列出了一些常用的验证方法:

  • 验证集方法
  • k 倍交叉验证
  • 遗漏一项交叉验证(LOOCV)
  • 分层 k 倍交叉验证

如果你希望了解更多的验证技术,那么请参考这篇文章:https://www . analyticsvidhya . com/blog/2018/05/improve-model-performance-cross-validation-in-python-r/

在本节中,我们将了解分层 k-fold 交叉验证。让我们了解它是如何工作的:

  • 分层是重新排列数据的过程,以确保每个折叠都是整体的良好代表。
  • 例如,在二进制分类问题中,每个类包含 50%的数据,最好安排数据,使得在每个文件夹中,每个类包含大约一半的实例。
  • 在处理偏差和方差时,这通常是一种更好的方法。
  • 随机选择的倍数可能不足以代表小类,特别是在存在巨大的类不平衡的情况下。

我们从 sklearn 导入 StratifiedKFold,拟合模型。

from sklearn.model_selection import StratifiedKFold

现在,让我们制作一个具有分层 5 层的交叉验证逻辑模型,并对测试数据集进行预测。

i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1)
for train_index,test_index in kf.split(X,y):
 print (‘\n{} of kfold {} ‘.format(i,kf.n_splits))
 xtr,xvl = X.loc[train_index],X.loc[test_index]
 ytr,yvl = y[train_index],y[test_index]
 model = LogisticRegression(random_state=1)
 model.fit(xtr,ytr)
 pred_test=model.predict(xvl)
 score=accuracy_score(yvl,pred_test)
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))1 of kfold 5 
accuracy_score 0.8048780487804879

2 of kfold 5 
accuracy_score 0.7642276422764228

3 of kfold 5 
accuracy_score 0.7804878048780488

4 of kfold 5 
accuracy_score 0.8455284552845529

5 of kfold 5 
accuracy_score 0.8032786885245902

Mean Validation Accuracy 0.7996801279488205

该模型的平均验证精度为 0.80。让我们想象一下 roc 曲线。

from sklearn import metrics
fpr, tpr, _ = metrics.roc_curve(yvl, pred)
auc = metrics.roc_auc_score(yvl, pred)
plt.figure(figsize=(12,8))
plt.plot(fpr, tpr, label=”validation, auc=”+str(auc))
plt.xlabel(‘False Positive Rate’)
plt.ylabel(‘True Positive Rate’)
plt.legend(loc=4)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们得到的 auc 值为 0.70

submission[‘Loan_Status’]=pred_test
submission[‘Loan_ID’]=test_original[‘Loan_ID’]

记住我们需要 Y 和 n 的预测,所以让我们把 1 和 0 转换成 Y 和 n。

submission[‘Loan_Status’].replace(0, ’N’, inplace=True)
submission[‘Loan_Status’].replace(1, ‘Y’, inplace=True)pd.DataFrame(submission, columns=[‘Loan_ID’,’Loan_Status’]).to_csv(‘Output/Log1.csv’)

特征工程

基于领域知识,我们可以提出可能影响目标变量的新特性。我们将创建以下三个新功能:

  • 总收入 —正如在双变量分析中所讨论的,我们将合并申请人收入和共同申请人收入。如果总收入很高,贷款批准的机会也可能很高。
  • EMI — EMI 是申请人每月偿还贷款的金额。这个变量背后的想法是,高 EMI 的人可能会发现很难偿还贷款。我们可以通过贷款金额与贷款金额期限的比率来计算 EMI。
  • 余额收入 —这是支付 EMI 后剩下的收入。创建这个变量的想法是,如果这个值高,一个人偿还贷款的机会就高,因此增加了贷款批准的机会。
train[‘Total_Income’]=train[‘ApplicantIncome’]+train[‘CoapplicantIncome’]
test[‘Total_Income’]=test[‘ApplicantIncome’]+test[‘CoapplicantIncome’]

让我们检查总收入的分布。

sns.distplot(train[‘Total_Income’])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到它向左移动,也就是说,分布是右偏的。所以,我们来取对数变换,使分布呈正态分布。

train[‘Total_Income_log’] = np.log(train[‘Total_Income’])
sns.distplot(train[‘Total_Income_log’])
test[‘Total_Income_log’] = np.log(test[‘Total_Income’])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,分布看起来更接近正常,极端值的影响已经大大减弱。现在让我们创建 EMI 特征。

train[‘EMI’]=train[‘LoanAmount’]/train[‘Loan_Amount_Term’]
test[‘EMI’]=test[‘LoanAmount’]/test[‘Loan_Amount_Term’]

让我们检查一下 EMI 变量的分布。

sns.distplot(train[‘EMI’])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

train[‘Balance Income’] = train[‘Total_Income’]-(train[‘EMI’]*1000)
test[‘Balance Income’] = test[‘Total_Income’]-(test[‘EMI’]*1000)
sns.distplot(train[‘Balance Income’])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,让我们放弃用来创建这些新功能的变量。这样做的原因是,那些旧特征和这些新特征之间的相关性会非常高,而逻辑回归假设变量之间的相关性并不高。我们还希望从数据集中移除噪声,因此移除相关要素也有助于减少噪声。

train=train.drop([‘ApplicantIncome’, ‘CoapplicantIncome’, ‘LoanAmount’, ‘Loan_Amount_Term’], axis=1)
test=test.drop([‘ApplicantIncome’, ‘CoapplicantIncome’, ‘LoanAmount’, ‘Loan_Amount_Term’], axis=1)

模型构建:第二部分

创建新特征后,我们可以继续模型构建过程。因此,我们将从逻辑回归模型开始,然后转向更复杂的模型,如 RandomForest 和 XGBoost。在本节中,我们将构建以下模型。

  • 逻辑回归
  • 决策图表
  • 随机森林
  • XGBoost

让我们准备输入模型的数据。

X = train.drop(‘Loan_Status’,1)
y = train.Loan_Status

逻辑回归

i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
 print (‘\n{} of kfold {} ‘.format(i,kf.n_splits))
 xtr,xvl = X.loc[train_index],X.loc[test_index]
 ytr,yvl = y[train_index],y[test_index]
 model = LogisticRegression(random_state=1)
 model.fit(xtr,ytr)
 pred_test=model.predict(xvl)
 score=accuracy_score(yvl,pred_test)
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))1 of kfold 5 
accuracy_score 0.7967479674796748

2 of kfold 5 
accuracy_score 0.6910569105691057

3 of kfold 5 
accuracy_score 0.6666666666666666

4 of kfold 5 
accuracy_score 0.7804878048780488

5 of kfold 5 
accuracy_score 0.680327868852459

 Mean Validation Accuracy 0.7230574436891909submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/Log2.csv')

决策图表

决策树是一种监督学习算法(具有预定义的目标变量),主要用于分类问题。在这种技术中,我们根据输入变量中最重要的分割器/区分器将总体或样本分成两个或多个同类集合(或子总体)。

决策树使用多种算法来决定将一个节点拆分成两个或多个子节点。子节点的创建增加了结果子节点的同质性。换句话说,我们可以说节点的纯度随着目标变量的增加而增加。

详细解释请访问https://www . analyticsvidhya . com/blog/2016/04/complete-tutorial-tree-based-modeling-scratch-in-python/# six

让我们用 5 重交叉验证来拟合决策树模型。

from sklearn import tree
i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
    print ('\n{} of kfold {} '.format(i,kf.n_splits))
    xtr,xvl = X.loc[train_index],X.loc[test_index]
    ytr,yvl = y[train_index],y[test_index]
    model = tree.DecisionTreeClassifier(random_state=1)
    model.fit(xtr,ytr)
    pred_test=model.predict(xvl)
    score=accuracy_score(yvl,pred_test)
    mean += score
    print ('accuracy_score',score)
    i+=1
    pred_test = model.predict(test)
    pred = model.predict_proba(xvl)[:,1]
print ('\n Mean Validation Accuracy',mean/(i-1))1 of kfold 5 
accuracy_score 0.7398373983739838

2 of kfold 5 
accuracy_score 0.6991869918699187

3 of kfold 5 
accuracy_score 0.7560975609756098

4 of kfold 5 
accuracy_score 0.7073170731707317

5 of kfold 5 
accuracy_score 0.6721311475409836

 Mean Validation Accuracy 0.7149140343862455submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/DecisionTree.csv')

随机森林

  • RandomForest 是一种基于树的自举算法,其中一定数量的弱学习器(决策树)被组合以形成强大的预测模型。
  • 对于每个单独的学习者,随机的行样本和一些随机选择的变量被用来建立决策树模型。
  • 最终预测可以是由单个学习者做出的所有预测的函数。
  • 在回归问题的情况下,最终预测可以是所有预测的平均值。

详细解释请访问本文https://www . analyticsvidhya . com/blog/2016/04/complete-tutorial-tree-based-modeling-scratch-in-python/

from sklearn.ensemble import RandomForestClassifier
i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
 print (‘\n{} of kfold {} ‘.format(i,kf.n_splits))
 xtr,xvl = X.loc[train_index],X.loc[test_index]
 ytr,yvl = y[train_index],y[test_index]
 model = RandomForestClassifier(random_state=1, max_depth=10)
 model.fit(xtr,ytr)
 pred_test=model.predict(xvl)
 score=accuracy_score(yvl,pred_test)
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))1 of kfold 5 
accuracy_score 0.8292682926829268

2 of kfold 5 
accuracy_score 0.8130081300813008

3 of kfold 5 
accuracy_score 0.7723577235772358

4 of kfold 5 
accuracy_score 0.8048780487804879

5 of kfold 5 
accuracy_score 0.7540983606557377

 Mean Validation Accuracy 0.7947221111555378

我们将通过调整该模型的超参数来提高精确度。我们将使用网格搜索来获得超参数的优化值。网格搜索是一种从一系列超参数中选择最佳参数的方法,这些参数由参数网格来确定。

我们将调整 max_depth 和 n _ estimators 参数。max_depth 决定树的最大深度,n_estimators 决定将在随机森林模型中使用的树的数量。

网格搜索

from sklearn.model_selection import GridSearchCV
paramgrid = {‘max_depth’: list(range(1,20,2)), ‘n_estimators’: list(range(1,200,20))}
grid_search=GridSearchCV(RandomForestClassifier(random_state=1),paramgrid)from sklearn.model_selection import train_test_split
x_train, x_cv, y_train, y_cv = train_test_split(X,y, test_size=0.3, random_state=1)
grid_search.fit(x_train,y_train)GridSearchCV(estimator=RandomForestClassifier(random_state=1),
             param_grid={'max_depth': [1, 3, 5, 7, 9, 11, 13, 15, 17, 19],
                         'n_estimators': [1, 21, 41, 61, 81, 101, 121, 141, 161,
                                          181]})grid_search.best_estimator_RandomForestClassifier(max_depth=5, n_estimators=41, random_state=1)i=1
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True)
for train_index,test_index in kf.split(X,y):
    print ('\n{} of kfold {} '.format(i,kf.n_splits))
    xtr,xvl = X.loc[train_index],X.loc[test_index]
    ytr,yvl = y[train_index],y[test_index]
    model = RandomForestClassifier(random_state=1, max_depth=3, n_estimators=41)
    model.fit(xtr,ytr)
    pred_test = model.predict(xvl)
    score = accuracy_score(yvl,pred_test)
    mean += score
    print ('accuracy_score',score)
    i+=1
    pred_test = model.predict(test)
    pred = model.predict_proba(xvl)[:,1]
print ('\n Mean Validation Accuracy',mean/(i-1))1 of kfold 5 
accuracy_score 0.8130081300813008

2 of kfold 5 
accuracy_score 0.8455284552845529

3 of kfold 5 
accuracy_score 0.8048780487804879

4 of kfold 5 
accuracy_score 0.7967479674796748

5 of kfold 5 
accuracy_score 0.7786885245901639

 Mean Validation Accuracy 0.8077702252432362submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/RandomForest.csv')

现在让我们找出特征的重要性,即对于这个问题哪些特征是最重要的。我们将使用 sklearn 的 feature_importances_ attribute 来这样做。

importances=pd.Series(model.feature_importances_, index=X.columns)
importances.plot(kind=’barh’, figsize=(12,8))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,信用历史是最重要的特征,其次是余额收入、总收入、EMI。因此,特征工程帮助我们预测我们的目标变量。

XGBOOST

XGBoost 是一种快速高效的算法,已经被许多数据科学竞赛的获胜者使用。这是一个 boosting 算法,你可以参考下面的文章来了解更多关于 boosting 的信息:https://www . analyticsvidhya . com/blog/2015/11/quick-introduction-boosting-algorithms-machine-learning/

XGBoost 只适用于数字变量,我们已经用数字变量替换了分类变量。让我们看看我们将在模型中使用的参数。

  • n_estimator:这指定了模型的树的数量。
  • max_depth:我们可以使用这个参数指定一棵树的最大深度。

GBoostError:无法加载 XGBoost 库(libxgboost.dylib)。如果你在 macOS 中遇到这个错误,运行Terminal中的brew install libomp

from xgboost import XGBClassifier
i=1 
mean = 0
kf = StratifiedKFold(n_splits=5,random_state=1,shuffle=True) 
for train_index,test_index in kf.split(X,y): 
 print(‘\n{} of kfold {}’.format(i,kf.n_splits)) 
 xtr,xvl = X.loc[train_index],X.loc[test_index] 
 ytr,yvl = y[train_index],y[test_index] 
 model = XGBClassifier(n_estimators=50, max_depth=4) 
 model.fit(xtr, ytr) 
 pred_test = model.predict(xvl) 
 score = accuracy_score(yvl,pred_test) 
 mean += score
 print (‘accuracy_score’,score)
 i+=1
 pred_test = model.predict(test)
 pred = model.predict_proba(xvl)[:,1]
print (‘\n Mean Validation Accuracy’,mean/(i-1))1 of kfold 5
accuracy_score 0.7804878048780488

2 of kfold 5
accuracy_score 0.7886178861788617

3 of kfold 5
accuracy_score 0.7642276422764228

4 of kfold 5
accuracy_score 0.7804878048780488

5 of kfold 5
accuracy_score 0.7622950819672131

 Mean Validation Accuracy 0.7752232440357191submission['Loan_Status']=pred_test
submission['Loan_ID']=test_original['Loan_ID']submission['Loan_Status'].replace(0, 'N', inplace=True)
submission['Loan_Status'].replace(1, 'Y', inplace=True)pd.DataFrame(submission, columns=['Loan_ID','Loan_Status']).to_csv('Output/XGBoost.csv')

SPSS 建模器

要创建 SPSS Modeler 流程并使用它构建机器学习模型,请遵循以下说明:

[## 使用 IBM Watson Studio 预测贷款资格

本教程向您展示了如何创建一个完整的预测模型,从导入数据,准备数据,到…

developer.ibm.com](https://developer.ibm.com/tutorials/predict-loan-eligibility-using-jupyter-notebook-ibm-spss-modeler/)

注册一个 IBM Cloud 账户来试试这个教程

[## IBM 云

使用 190 多种独特的服务立即开始建设。

ibm.biz](https://ibm.biz/BdqQBT)

结论

在本教程中,我们学习了如何创建模型来预测目标变量,即申请人是否能够偿还贷款。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值