TowardsDataScience 博客中文翻译 2020(九百四十八)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

使用机器学习来预测车祸

原文:https://towardsdatascience.com/using-machine-learning-to-predict-car-accidents-44664c79c942?source=collection_archive---------14-----------------------

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

来源:朱利安·刘

一个用例

R 道路交通事故在每年报告的严重伤害事故中占有相当大的比例。然而,确定导致此类事件的具体条件往往具有挑战性,这使得地方执法部门更难以应对道路事故的数量和严重程度。我们都知道,车辆的某些特性和周围环境起着关键作用(发动机容量、路况等。).然而,许多问题仍然悬而未决。这些因素中哪些是主导因素?与司机的技术相比,有多少是外部因素造成的?

我们利用机器学习和英国的道路事故数据库来澄清这些问题,并具体提供对两个主要领域的影响:

  1. 首先,我们开发了一个风险评分,该评分仅基于从个人和车辆数据中收集的信息,量化了驾驶员发生致命/严重事故的可能性。该分数可用于影响驾驶规则和规定,并告知驾驶员增加其事故风险的因素。
  2. 其次,我们分析了情境信息(如道路类型、天气状况等。)来估计事故的严重程度。这些见解将有助于政府更好地理解事故的根源,并采取措施减少事故。

数据

我们使用英国交通部的 22 万+事故报告,涵盖 2018 年。对于每份报告,我们都有在事故现场收集的信息,包括:

  • 伤亡特征(如性别年龄所属地区类型)
  • 情境变量(如天气道路类型光照条件)
  • 事故描述符(例如严重性警察在场)
  • 车辆描述符(如年龄动力类型型号)

总的来说,运输部提供的数据可以分为驾驶员信息和外部信息(例如事故位置光线条件),驾驶员信息可以进一步细分为车辆和个人数据。

驾驶员得分

为了了解驾驶员风险因素,我们使用每位驾驶员的独特特征创建了一个驾驶员得分。每个司机都可以输入信息,包括他们的年龄和车辆类型,以获取描述他们发生严重事故风险的值。此外,该模型能够告知驾驶员有关其风险的主要因素。

例如,对于一些司机来说,风险的主要原因可能是拥有一辆旧车,而对于其他人来说,可能是他们生活在农村地区,那里的道路条件通常很差。有了这些信息,个别司机可以做出更明智的决策,例如,购买风险较低的车辆。

为了开发一个估算该分数的模型,我们只关注事前特征,这些特征在事故发生之前是已知的。如果观察到的事故严重性较低或没有造成人员伤亡,我们将目标变量定义为 0,如果事故造成严重或致命后果,我们将目标变量定义为 1(有关如何制定风险评分的更多信息,请查看本文)。我们针对驾驶员和车辆特征训练了多个模型,以便能够比较它们的性能。

使用的模型有:

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

模型性能的比较。由于类别分布严重不平衡,我们使用 ROC 曲线下面积(AUC)来比较模型的性能。

从上表可以看出,最优分类树实现了最高的样本外性能。此外,与 Random Forest 和 XGBoost 不同,oct 提供了近乎完全的可解释性,因为它不是一种集合方法。在下图中,我们可以看到 OCT 决策树的一个分支,它提供了合理的标准,并且与人类的直觉预期相似。在这个例子中,确实,模型显示如果事故涉及发动机容量高于 200cc 的摩托车,并且车辆的车龄超过 20 年,那么事故很可能是严重的。

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

决策树的最高分支之一。预测 1 表示致命事故,0 表示非致命事故。

通过预测发生严重事故的概率,我们可以将该概率用作风险得分,该得分将成为驾驶员得分。通过这个分数,我们能够突出风险较高和风险较低的驱动因素。通过使用高度可解释的模型,我们可以通过检查决策树和变量重要性来理解驱动大部分得分的特征。

描述统计学

然而,驾驶员风险只是故事的一部分。然后,我们继续分析与事故相关的描述性统计数据。特别是,我们使用了事故发生时的天气状况、道路上的照明状况和道路状况本身等事后信息,以更好地了解英国各地发生事故的司机。通过这样做,我们不仅能够了解驾驶员如何降低风险(部分使用他们的驾驶员得分),还能够了解外部因素如何发挥作用。

通过了解驱动事故风险的外部因素,政府可以通过首先瞄准事故的主要驱动因素来优先考虑支出。例如,如果我们发现照明条件比道路条件本身更重要,交通部可以分配其有限的预算,优先考虑照明条件,然后是道路质量。

我们使用了相同的目标变量,但这次不是在预测设置中,也使用了不同的模型。与上一节类似,我们在使用 5 重交叉验证训练模型时进行了分层采样:

逻辑回归

  • 套索正则化器,超参数网格在 1.0 和 3.0 之间,步长为 0.1。

手推车

  • 最小样本分割在 3 和 11 之间变化
  • 最小样本叶数在 5 和 13 之间
  • 每次分割的最大特征数“无”、“sqrt”或“log2”。

随机森林

  • 自举等于“真”
  • 最大特性或者“sqrt”或者“log2”
  • 最小样本叶数5 或 10
  • 树木数量400、800 或 1000。

XGBoost

  • 学习率在 0.001,0.01 和 0.1
  • 树木数量分别为 2500、2000 和 1500 棵
  • 最小样本叶数在 4、8 或 12。

在训练模型之后,我们选择使用梯度增强分类器,其样本外 AUC 为 0.72,准确度为 0.87,这实现了最佳性能。分析决策树和从中得出的见解,我们可以突出一些关键方面。首先,从下图可以看出,光事故因一天中的时间不同而不同,在高峰时段发生的频率更高。不同的是,无论一天中的哪个小时,严重或致命事故的分布都比较均匀。

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

全天事故严重程度直方图。

此外,我们报告了英国每个警察辖区的严重事故与轻微事故的比率,蓝色的农村地区和红色的大都市地区突出显示了这一比率。相对于轻微事故的数量,圆圈越大的位置发生的严重事故越多,反之亦然。直观地说,像伦敦这样的城市比一般的圈子要大,因为在这样一个大城市,交通和拥堵水平更高,导致更严重的事故。

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

英国严重事故与轻微事故的比率。圆圈越大,比率越高。城市用红色标出,农村用蓝色标出。

然而,有趣的是注意到英国不同的地方,尽管是农村,却有和伦敦一样高的严重到轻微事故的比率。这可以为地方政府和更广泛的政府提供有用的见解,显示英国哪些地区风险最高,哪些地方的改善和政策需要优先考虑。例如,靠近英格兰边境的兰开夏郡和威尔士北部地区。我们的模型显示,这些地区是最容易发生严重事故的地区,最近不同的新闻媒体也证实了这些地区是最危险的地区。

摘要

  • 我们分析了英国各地的交通事故数据,以寻找能够推动旨在拯救生命的决策的见解。
  • 我们解决了这个问题,首先开发了一个驱动因素分数,给每个驱动因素分配一个风险等级。然后,每个司机能够了解他们是否处于危险中,最重要的是,什么特征是主要因素。
  • 其次,我们分析了驾驶员无法控制的外部信息,比如路况。我们能够突出全英国应该优先考虑的领域,将政府资金集中在这些高度优先的领域。
  • 这两项分析让我们进一步了解事故的根本原因,让司机和政府能够防患于未然。

要阅读更多类似的文章,请关注我的Twitter*LinkedIn或我的 网站 *

使用机器学习预测国家人口

原文:https://towardsdatascience.com/using-machine-learning-to-predict-country-population-550f9e5f3e24?source=collection_archive---------23-----------------------

基于 50 多年的人口记录来预测下一年人口的简单线性回归模型。

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

照片由岩田良治Unsplash 上拍摄

机器学习已经成为最近的热门话题之一。有很多开发和研究正在进行,以保持这一领域向前发展。在本文中,我将演示一个简单的线性回归模型,该模型将预测一个国家未来几年的人口。

我不打算在这里详细解释线性回归,因为在 Medium 中有许多文章和许多在线课程提供了对线性回归的深入讨论。我将简单地展示和解释一个使用 python 中的线性回归开发的小项目。这个项目中使用的数据集是从世界银行收集的。

此项目需要以下库。

import pandas as pd
import numpy as np
*from* sklearn.linear_model *import* LinearRegression
*import* re
*import* json

要忽略代码输出中的警告,可以使用警告模块,但这是可选的。

*import* warnings
warnings.filterwarnings("ignore")

导入必要的库之后,现在是将数据加载到熊猫数据报的时候了。下载后我把人口文件保存为 pop.csv。

df = pd.read_csv('pop.csv')
df.head()

这将产生以下结果:

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

df.head()

从上图可以看出,在将数据传递给线性回归模型之前,还需要进行一些预处理。有几个不必要的列,如指标代码、指标名称和本项目不需要的其他列。在此之前,我想根据国家名称列选择一个国家。

bd=df.loc[df['Country Name']=='Bangladesh']
bd.drop(['Country Name','Country Code','Indicator Name','Indicator Code'],axis=1,inplace=True)
bd = bd.T
bd.head()

我以前在这里。loc 选择孟加拉国,选择后,我删除了四列,这样我就有了所有年份的列和人口的行。我调换了数据框架,这样我就只有两列,即年份和人口。下图是上述代码的结果。

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

但是没有显示列名,而是显示 18。此外,年份显示为一个索引。对于自回归(AR)模型来说,如果年份被显示为指数,那就很好了。但是对于线性回归,我希望 year 作为一个不同的列,而不是一个索引。因为

y = mx + c

这是一个简单的线性回归公式,其中 y 是预测变量或因变量, x 是自变量, m 是斜率或系数, c 是截距。在这个项目中, x 是年份, y 是预测人口。因此,下面的代码将帮助我准备过程和重命名列。

bd.dropna(inplace=True)
bd=bd.reset_index().rename(columns={18:'population','index':'year'})
bd.head()

这将产生以下结果:

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

现在,我可以使用这个数据框架来训练线性回归模型,并获得所需的输出。

x = bd.iloc[:, 0].values.reshape(-1, 1)
y = bd.iloc[:, 1].values.reshape(-1, 1)
model = LinearRegression().fit(x, y)
y_pred = model.predict([[2019]])
y_pred

在这段代码中,我将我的年份和人口转换为 2D 数组,这是 LinearRegression 中需要使用的。然后我简单的调用了模型,在模型中拟合 x 和 y。之后我用 model.predict()预测了下面这个结果。

array([[1.65040186e+08]])

现在,所有这些都很好,但我有 100 多个国家的人口信息,我只限于一个国家使用上面的代码块。上面的代码块将作为下面代码块的主干,它将显示原始项目的实现。这个项目将接受国家和年份的用户输入,之后,我可以像以前一样做一些预处理,稍微调整一下,并使用线性回归模型来显示预测的结果。

def main():
    country = input("Please input the country name: ").lower()
    year = int(input("Please input the year to predict: "))
    df = pd.read_csv('pop.csv')
    lists, df = country_list_gen(df)
    if country in lists:
        df = selecting_country(df, country)
        model = prediction_model(df)
        result = prediction(model,year)
        print(f"\n Result: {country.upper()} population in {year} will be {result:,d}")
    else:
        print('kindly check country name spelling from country_list.json')

if __name__ == "__main__":
    main()

上面的代码是我的脚本的主要功能。首先,它接受用户输入的国家名称,并将其转换成小写字符串。之后它也会以年份为输入,转换成整数。然后使用 pandas read_csv()将原始 CSV 文件加载到 dataframe 中。然后,它执行下面的函数。

def country_list_gen(df):
    df.rename(columns={'Country Name':'country_name'},inplace=True)
    df['country_name'] = df['country_name'].apply(lambda row: row.lower())
    lists = df['country_name'].unique().tolist()
    with open('country_list.json','w', encoding='utf-8') as f:
        json.dump(lists, f, ensure_ascii=False,indent=4)
    return lists, df

这是一个将所有唯一的国家名称存储在 JSON 文件中的功能,这将帮助用户检查该国家是否可用,以及是否有任何拼写错误。该函数将原始数据帧作为输入参数,并重命名 country_name 列。之后,它将使用。应用()和 lambda 函数。然后,所有唯一的国家名称被转换成列表,并保存为 country_list.json 文件。最后,它将列表和修改后的数据帧返回给主函数。为了便于阅读,我再次给出了主要的函数代码。

def main():
    country = input("Please input the country name: ").lower()
    year = int(input("Please input the year to predict: "))
    df = pd.read_csv('pop.csv')
    lists, df = country_list_gen(df)
    if country in lists:
        df = selecting_country(df, country)
        model = prediction_model(df)
        result = prediction(model,year)
        print(f"\n Result: {country.upper()} population in {year} will be {result:,d}")
    else:
        print('kindly check available country name and thier spelling from country_list.json')

现在,返回的列表包含可用的国家名称,因此执行一个简单的 if-else 语句。如果用户输入的国家名称在列表中不可用,或者如果我的列表中有拼写错误,那么它会指示用户查看 country_list.json 文件。但是,如果名称已经匹配,那么它将执行以下功能。

def selecting_country(df,country):
    df = df.loc[df['country_name']==country]
    df.drop(['country_name','Country Code','Indicator Name','Indicator Code'],axis=1,inplace=True)
    df = df.T
    df.dropna(inplace=True)
    df = df.reset_index()
    return dfdef prediction_model(df):
    x = df.iloc[:, 0].values.reshape(-1,1)
    y = df.iloc[:, 1].values.reshape(-1,1)
    model = LinearRegression().fit(x,y)
    return modeldef prediction(model, year):
    return int(model.coef_[0][0] * year + model.intercept_[0])

selecting _ country 函数获取国家名称并过滤数据帧,然后丢弃不必要的字段,转置并重置数据帧的索引,并将其返回给主函数。然后,主函数调用 prediction_model,并将数据帧作为参数发送。在这里,我不关心重命名列名,因为我将它们转换为 x 和 y 的 2D 数组。之后,调用 LinearRegression()并用 x 和 y 拟合模型。然后,拟合的模型被发送到主函数。然后,主函数将这个模型与用户在预测函数中提示的年份一起传递。该预测函数只需从模型中获取系数和截距,并使用“y=mx+c”来导出预测的人口。

下面给出了两种不同场景的示例输出,一种是正确的国家名称,另一种是错误的拼写。

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

正确名称

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

拼写错误

这是我在一小时内完成的项目。这是一个非常简单和容易实现的项目,并且对线性回归模型有实际的理解。我来自孟加拉国,这就是我在这篇文章中使用孟加拉国作为国名的原因。你可以从 GitHub 访问并查看完整代码。你可以在这里看到我以前的文章,它是关于使用免费 API 从公共 IP 地址获取地理位置信息的。

非常感谢您阅读我的文章。我希望,当我做这个项目和写这篇文章的时候,你学到了一些新的东西。

利用机器学习预测梦幻足球积分

原文:https://towardsdatascience.com/using-machine-learning-to-predict-fantasy-football-points-72f77cb0678a?source=collection_archive---------16-----------------------

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

一个幻想足球贸易分析仪使用 RNN LSTM,ARIMA,XGBoost 和破折号

NFL 梦幻足球是一种游戏,其中足球迷扮演职业足球队的教练或总经理的角色。参与者起草他们的初始球队,选择每周比赛的球员,并交易球员,以便在一个赛季中每周与其他球队比赛。获胜队是由职业运动员的真实统计数据决定的。

交易分析器的目标

交易分析器的目标是确定在 2019 年足球赛季的任何一周,一名或多名球员的拟议交易是“好”还是“坏”交易。一笔好的交易被定义为用你队中的一名球员交换一名有着更高期望的梦幻足球分数的球员。因此,目标是确定 2019 赛季每个星期每个职业足球运动员的预期梦幻足球积分。然后可以在玩家之间比较期望的分数。

点击这里看 2019 NFL 奇幻足球交易分析

积分制

交易分析器使用 ESPN 标准联盟规则来确定梦幻足球点数。一支球队由 7 个位置的 9 名先发球员组成。这 7 个位置是四分卫(QB),跑卫(RB),外接员(WR),紧逼端(TE),踢球者(K)和团队防守(DF)。在虚拟足球中,一支球队的防守阵容被认为是一个“球员”。

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

积分制

数据

用于建模的数据来自 、ArmchairAnalysis.com。该数据由 28 个不同的 excel 表格中的 700 多个数据点组成,提供了自 2000 年以来 NFL 中每个球员的 20 年详细统计数据。

原始数据最初在 Excel 中进行处理,以便链接和提取所有 2019 年 NFL 球员的相关数据。经过处理的数据构成了初始建模数据的基础。根据初始建模数据,为每个 2019 年球员的每个赛季的每场比赛计算实际的梦幻足球积分。模型的训练数据使用了截至 2018 赛季每个球员的所有可用数据,2019 年的数据用作测试数据。例如,如果一名球员在 2014 年开始职业生涯,那么 2014-2018 年的数据用于训练数据,2019 年的数据用于测试数据。

建模

共有四个模型被用于预测 653 名 2019 年 NFL 球员的预期梦幻足球积分。这些数据本质上是时间序列数据,玩家拥有 0 到 20 年(20 个赛季 x 16 场比赛= 320 个数据点)的数据。

**平均值:**使用每个 2019 年球员的平均值建立基线测量。第一周的平均值是使用 2018 赛季球员(不包括新秀)的平均每场比赛分数建立的。第 2 周到第 17 周是使用他们前几周实际绩效的平均值计算的(例如,第 2 周使用第 1 周的实际绩效,第 3 周使用第 1 周和第 2 周实际绩效的平均值,等等)。).这将被认为是天真的方法,也是普通的梦幻足球参与者经常使用的方法。

XGBoost: 一个 XGBoost 模型被用来预测 2019 赛季第一周新秀球员的表现。2019 年的 106 名新秀很难预测,因为他们没有专业经验。使用 515 名非新秀球员(不包括防守)的新秀年统计数据作为训练数据,计算 2019 赛季第 1 周预期幻想点数,验证 MAE 为 28.93,R 平方为 0.49,测试 MAE 为 30.75,R 平方为 0.43。这些是微弱的结果,但需要一个起点。第 2 周到第 17 周是使用他们前几周实际绩效的平均值计算的,与基线相同(例如,第 2 周使用第 1 周的实际绩效,第 3 周使用第 1 周和第 2 周实际绩效的平均值,等等。).

**ARIMA:**ARIMA 模型最初用于预测 2019 赛季每周的老将表现。只有经验超过 3 年且在这 3 年中至少获得 50 分的资深玩家才使用 ARIMA 运行,因为模型需要大约 50 个数据点(3 个赛季 x 16 场比赛= 48 个数据点)才能表现良好。使用 ARIMA 对大约 246 名资深玩家进行建模,虽然结果非常好(参见评估选项卡)并且超过了基线,但神经网络模型显示出更好的结果。

**递归神经网络,长短期记忆(RNN-LSTM)😗*RNN-LSTM 模型用于预测 2019 赛季每周的老将表现。只有拥有 3 年以上经验和 3 年内至少 50 分的资深球员才使用 RNN-LSTM 进行比赛,因为该模型需要尽可能多的数据,3 年似乎是一个很好的平衡。大约 246 名经验丰富的球员使用 RNN-LSTM 模型,并将结果与 ARIMA 模型进行比较。RNN-LSTM 模型优于基线平均值和 ARIMA 模型,是最终应用程序中使用的模型。

备注:

**最终结果:**最终结果综合了新秀成绩(XGBoost+平均— 106 人)、3+年的老将(RNN-LSTM —包括防守在内 278 人)和剩下的有< 3 年经验的老将(平均— 269 人)。这在“评估”选项卡中被称为“神经网络”模型,而“ARIMA”是对 3 年以上的退伍军人仅使用 ARIMA 的相同结果组合。

**每周预测:**每个模型都计算本季度剩余几周的预测。然后将剩余的每一周相加,得出玩家期望的梦幻足球点数。例如,在第 1 周
中,预期的梦幻足球分数是所有 16 场比赛预测(整个赛季)的总和,在第 2 周中,分数是剩余 15 场比赛的总和,等等。

**再见周:**一个足球赛季实际上有 17 周,每个球队在赛季中都有一个随机的再见周。在“再见周”,每个玩家前一周的预测都会被简单地继续下去。

**伤病:**伤病在梦幻足球中起着巨大的作用。当一名球员受伤时,他的分数基本上降到零。没有可靠的方法来预测运动员是否或何时会受伤。虽然原始数据确实提供了一些关于伤病的细节,但它仍然没有提供球员何时从伤病中恢复的良好信息。在所有的模型中,当一名球员处于受伤的预备队状态时,他被认为是缺席了整个赛季。

代码

该项目的所有代码、数据和相关文件都可以在我的 GitHub repo 中访问。自述文件提供了 repo 目录和文件的详细信息。

RNN-LSTM 模型

虽然预测梦幻足球积分的完整代码相当复杂,但让我们以一名球员为例,仔细看看核心的 RNN-LSTM 代码。我们要用艾伦·罗杰斯,绿湾包装工队的四分卫。Aaron 是一个优秀的测试案例,因为他在 2005 年开始了他的 NFL 职业生涯,提供了 15 年的数据。此外,他在 2019 赛季没有受到任何重大伤害。

每个 2019 NFL 球员的实际积分存储在 original_df 数据帧中。数据帧的前五列包含每个玩家的描述性特征。接下来的 320 列包含每个球员从 2000 年到 2019 年每场比赛的实际梦幻足球积分(每个赛季 16 场 x 20 个赛季= 320 个数据点)。这些值是 NaN 直到球员的第一个 NFL 赛季。所以 Aaron Rodgers 的实际数据直到第 85 栏才开始(16x 5 = 2005 赛季前的 80 场比赛加上 5 个描述栏)。

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

原始 _ 测向数据帧

对 RNN-LSTM 预测函数 lstm_pred 的初始调用发生在名为 main 的函数中。该功能的输入包括:

player=‘Aaron Rodgers’(这是玩家的名字)
n_periods = 16(在本例中,我们从赛季初开始,预测 2019 赛季的所有 16 场比赛)
col = 85 (Aaron 的 2005 年数据从数据帧的第 85 列开始)

pred_points = lstm_pred(player, n_periods, col)

利用这三个输入,让我们仔细看看 lstm_pred。

def lstm_pred(p, np, c):
    series = make_series(p, c)
    X = series.values
    supervised = timeseries_to_supervised(X, 1)
    supervised_values = supervised.values

    # Split data into train and test-sets
    train, test = supervised_values[0:-np], supervised_values[-np:]

    # Transform the scale of the data
    scaler, train_scaled, test_scaled = scale(train, test)

    # Fit the model
    lstm_model = fit_lstm(train_scaled, 1, 100, 1)

    # Forecast the entire training dataset to build up state
    train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
    lstm_model.predict(train_reshaped, batch_size=1)

    # Walk-forward validation on the test data
    yhat_sum = 0
    for i in range(len(test_scaled)):
        # Make one-step forecast
        X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
        yhat = forecast_lstm(lstm_model, 1, X)
        # Invert scaling
        yhat = invert_scale(scaler, X, yhat)
        # Sum the weekly forecasts
        yhat_sum = yhat_sum + yhat

    return yhat_sum

让我们浏览一下这段代码,以便更好地理解它是如何工作的。前四行只是为模型准备数据。

series = make_series(p, c)
X = series.values
supervised = timeseries_to_supervised(X, 1)
supervised_values = supervised.values

函数 make_series 只是将 Aaron 从 2005 年到 2019 年存储在 dataframe 中的实际点数转换成一个 python 序列。然后,系列值存储在 X 中。现在, X 值代表了 Aaron 的梦幻足球点数的时间序列。函数 timeseries_to_supervised(X,1) 获取时间序列 X 并创建一个包含 X 我们的监督学习输入模式和 y 我们的监督学习输出模式的数据帧。 y 值只是将 X 系列向后移动一个周期。然后将 Xy 值存储在 numpy 数组 supervised_values 中。我们对这四行代码(及其功能)所做的是从原始 _df 数据帧中提取 Aaron 的 fantasy football point production,并将其转换为监督学习的形式。代表 2019 赛季的 ssupervised _ values的尾端在下面。第一列代表 X 值,第二列代表 y 值。

....
 [ 1.04 12.92]
 [12.92 14.36]
 [14.36 14.3 ]
 [14.3  25.48]
 [25.48  9.42]
 [ 9.42 18.32]
 [18.32 44.76]
 [44.76 28.1 ]
 [28.1  12.94]
 [12.94 10.02]
 [10.02  9.46]
 [ 9.46 28.12]
 [28.12 11.4 ]
 [11.4  14.42]
 [14.42  9.34]
 [ 9.34 19.02]]

下一行代码将 supervised_values 拆分为 traintest 将最近 16 场比赛(2019 赛季)拆分为测试集,并将 2005-2018 赛季作为训练集。

train, test = supervised_values[0:-np], supervised_values[-np:]

一旦代码被拆分,使用 scale 函数对数据进行标准化。scale 函数使用范围为(-1,1)的 MinMaxScaler。建议对数据进行规范化,因为这将使神经网络的学习更容易,并应确保值的大小或多或少相似。

scaler, train_scaled, test_scaled = scale(train, test)

我们现在准备通过函数 fit_lstm 拟合 RNN-LSTM 模型,参数如下:
**train:**train _ scaled
**batch _ size:**1
**nb _ epochs:**100
神经元: 1

lstm_model = fit_lstm(train_scaled, 1, 100, 1)

fit_lstm 函数是 RNN-LSTM 魔术发生的地方。简而言之,该函数使用一个连续的 Keras API,包括一个 LSTM 层和一个密集层。使用的损失函数是具有“adam”优化算法的“均方误差”。该函数的实际机制相当复杂,对于那些希望获得进一步信息的人,请参见杰森·布朗利 的文章 中的 LSTM 模型开发部分中关于该函数的详细描述。

为了获得更好的结果,文章建议使用更大数量的历元(1000–4000)和神经元(1–5),但是由于运行数百个玩家的预测需要时间和资源,100 个历元和 1 个神经元以资源较少的方式提供了非常好的结果。

我们现在准备使用模型的 lstm_model.predict 函数进行预测。

train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
lstm_model.predict(train_reshaped, batch_size=1)

该函数需要一个 3D numpy 数组作为输入,在我们的例子中,这将是一个一个值的数组(前一个时间步长的观测值),并输出一个一个值的 2D numpy 数组。在预测测试数据之前,我们需要通过预测训练数据中的所有值来播种初始状态。上面代码的第一行将数据重新整形为单值 3D numpy 数组,第二行使用训练数据设置网络的初始状态。

我们所剩下的就是逐步通过测试数据,对 16 场比赛进行单独的预测,并反转比例以得出最终的单独预测。然后将个人预测相加,以创建亚伦 2019 赛季的预测幻想足球积分。

yhat_sum = 0
for i in range(len(test_scaled)):
    # Make one-step forecast
    X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
    yhat = forecast_lstm(lstm_model, 1, X)
    # Invert scaling
    yhat = invert_scale(scaler, X, yhat)
    # Sum the weekly forecasts
    yhat_sum = yhat_sum + yhat

亚伦罗杰斯赛季的预测结果如下。本赛季的总预测是 293 分(四舍五入),他的实际得分是 282 分——本赛季 4%的误差还不错!(每次运行代码时,结果会略有不同)

yhat =  13.550041856765747
yhat =  15.615552995204924
yhat =  17.195858060121534
yhat =  18.015674024820328
yhat =  20.544238206744193
yhat =  18.375679302811623
yhat =  19.83470756918192
yhat =  18.937192922234534
yhat =  21.3260236287117
yhat =  20.56778145074844
yhat =  18.166402292251586
yhat =  16.316439254283903
yhat =  19.98121999800205
yhat =  18.90507374048233
yhat =  18.95989200234413
yhat =  16.812043523192404

yhat_sum =  293.10382082790136

对模型预测的评估

递归神经网络— LSTM 模型(NN)优于基线平均值和 ARIMA 模型。虽然 NN 和 ARIMA 模型都具有较低的 MAEs,但它们都在赛季中途开始收敛,因为所有模型都包含当前赛季的实际点,并且未来预测的数量减少,从而增加了所有模型的准确性。

模型的真实准确性显示在正确预测百分比图中。每个星期,每个玩家的“好”或“坏”交易的预测结果都要与其他玩家的交易结果进行比较,并与实际结果进行比较。神经网络显然比其他模型做出了更好的预测,2019 赛季的总体平均正确率为 85.59%,而 ARIMA 模型为 84.72%,基线平均模型为 83.17%。

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

Dash App

由于本文侧重于该项目的机器学习方面,Dash 应用程序的细节将不被涵盖。有关创建 Dash 应用程序的详细信息,请参见我的文章 如何创建交互式 Dash Web 应用程序

我欢迎建设性的批评和反馈,请随时给我发私信。

在推特上关注我

这篇文章最初出现在我的 GitHub Pages 网站上

梦幻足球原始数据来源:*ArmchairAnalysis.com
RNN-LSTM 模型开发:用 Python 中的长短期记忆网络进行时间序列预测作者:杰森·布朗利图片来源: Pixabay*

使用机器学习来预测 Fitbit 睡眠分数

原文:https://towardsdatascience.com/using-machine-learning-to-predict-fitbit-sleep-scores-496a7d9ec48?source=collection_archive---------8-----------------------

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

凯特·斯通·马西森在 Unsplash 上的照片

随机森林、极端梯度推进以及它们在预测 Fitbit 睡眠分数时的表现

在本文的第 1 部分的中,我解释了我们如何从 Fitbit 获取睡眠数据,将其加载到 Python 中,并对数据进行预处理,为进一步的分析做好准备。在这一部分中,我将解释我们如何以及为什么将数据分为训练集、验证集和测试集,我们如何为我们的机器学习模型选择特征,然后训练三个不同的模型:多元线性回归、随机森林回归器和极端梯度推进回归器。我将简要解释这些模型是如何工作的,并定义性能度量来比较它们的性能。让我们开始吧。

将数据分为训练集、验证集和测试集

在我们使用我们的数据做任何进一步的分析之前,我们需要将整个数据集分成三个不同的子集:训练集、验证集和测试集。下图很好地展示了这一过程:

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

培训、验证和测试数据

测试集也被称为保留集,一旦我们从剩余数据中分离出来,我们就不会再接触它,直到我们训练和调整我们的机器学习模型,达到我们认为它们会在他们从未见过的数据上表现良好的程度。

我们将剩余的数据分成训练集和验证集。这允许我们根据训练数据训练我们的模型,然后根据验证数据评估它们的性能。理论上,我们可以调整我们的模型,并根据验证数据再次评估它们,从而找到提高模型性能的方法。这一过程通常会导致过度拟合,这意味着我们过于关注训练我们的模型,使其在验证集上表现良好,但在从未见过的数据集(如测试集)上表现不佳。

在本文的第 3 部分,我解释了我们如何减少过度拟合,同时确保模型仍然表现良好。现在,我们将遵循上述方法,将数据集简单地分为训练集、验证集和测试集。

我希望以这样的方式分割数据,即训练集占总数据集的 60%,验证集和测试集各占 20%。此代码实现了正确的百分比拆分:

在第一次测试拆分中,test_size 参数设置为 0.2,这将数据拆分为 80%的训练数据和 20%的测试数据。为了将 80%的训练数据分成训练和验证数据,并确保验证数据是原始数据集大小的 20%,test_size 参数需要为 0.25 (20%是 80%的四分之一,即 0.25)。

在继续之前,我想强调一件重要的事情。在执行任何进一步的转换(例如缩放数据)之前拆分数据是至关重要的,因为我们希望防止任何关于测试集的信息溢出到我们的训练和验证集中。数据缩放通常使用关于数据集整体的统计数据来完成,例如均值和标准差。因为我们希望能够衡量我们的机器学习模型在他们从未见过的数据上的表现,所以我们必须确保来自测试数据的信息不会影响缩放或任何其他转换的完成方式。

扩展功能、定义性能指标和基线

虽然对于本项目中的机器学习模型,不需要进行特征缩放,但在比较不同模型及其性能时,缩放特征被认为是最佳实践。

在这段代码中,我使用了 MinMaxScaler,它适合于训练数据,然后用于缩放训练、验证和测试数据:

绩效指标

接下来,让我们定义一些可以用来评估和比较我们的模型的性能度量。因为睡眠分数是一个连续的变量(虽然只有整数睡眠分数是可能的),手头的问题是一个回归问题。对于回归问题,有许多不同的性能指标,在此分析中,我将使用平均绝对误差、均方误差和 R 平方。此外,我计算模型预测的准确性。

准确性通常用作分类问题中的性能度量,而不是回归问题中的性能度量,因为它指的是模型做出的正确预测的比例。在这个分析中,我使用回归模型的准确性的方式是不同的。回归模型的准确性是预测的睡眠得分与实际睡眠得分平均相差多远(以百分比计)的量度。例如,如果实际睡眠得分为 80,模型的准确度为 96%,这意味着平均误差为 4%,则模型预计会对睡眠得分做出 76.8(80-(80 x 0.04))到 83.2 (80 + (80 x 0.04))的预测。

下面是评估模型性能的函数,它将手头的模型、测试特征和测试标签作为输入:

但是我们如何知道这些不同的度量标准的分数是好是坏呢?比如 90%的准确率是好是坏?R 平方呢?为了有一个参考点,我们将首先提出一个基线模型,我们可以比较所有后来的模型及其性能。

基线性能

为了评估我们将要建立的机器学习模型,我们希望有一个基线,以便我们可以比较它们的性能。通常,基线是一种基于简单规则生成预测的简单方法。对于我们的分析,基线模型总是预测训练集的中值睡眠分数。如果我们的机器学习模型不能超越这个简单的基线,它将是相当无用的。

让我们看看基线的性能是什么样的:

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

虽然准确性看起来不错,但看看其他绩效指标就会发现一个非常不同的故事。R 平方为负,这强烈表明模型性能极差。

现在,我们已经将数据分成不同的子集,扩展了功能,定义了性能指标,并提出了一个基线模型,我们几乎准备好开始训练和评估我们的机器学习模型。在我们继续我们的模型之前,让我们首先选择我们想要在那些模型中使用的特性。

使用套索回归的特征选择

阅读完标题后,您可能会有两个问题:为什么我们需要选择要素以及 Lasso 回归到底是什么?

特征选择

仅选择可用功能的子集有多种原因。

首先,特征选择使机器学习算法能够更快地训练,因为它使用更少的数据。其次,它降低了模型的复杂性,使解释模型变得更容易。在我们的案例中,这很重要,因为除了准确预测睡眠得分,我们还希望能够了解不同的特征如何影响睡眠得分。第三,特征选择可以减少过拟合,从而提高模型的预测性能。

在本文的第 1 部分中,我们看到睡眠数据集中的许多特性是高度相关的,这意味着我们使用的特性越多,模型中出现的多重共线性就越多。一般来说,如果我们只关心模型的预测性能,这不是问题,但如果我们希望能够解释模型,这就是问题。特征选择也将有助于减少一些多重共线性。

有关功能选择的更多信息,请参见这篇文章。

套索回归

在我们继续讨论套索回归之前,让我们简要回顾一下线性回归的作用。拟合线性回归通过为每个特征变量选择系数来最小化损失函数。这样做的一个问题是,大系数会导致过度拟合,这意味着该模型在训练数据上表现良好,但在从未见过的数据上表现不佳。这就是正规化发挥作用的地方。

Lasso 回归是一种正则化回归,它通过损失函数中的附加项来惩罚回归系数的绝对大小。套索回归的损失函数可以写成这样:

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

Lasso 回归的损失函数

损失函数的第一部分相当于线性回归的损失函数,它使残差平方和最小。附加部分是惩罚项,它惩罚系数的绝对值。从数学上讲,这相当于最小化残差平方和,同时约束绝对系数值之和必须小于预先指定的参数。此参数决定正则化的量,并导致某些系数收缩到接近或完全为零。

在上面的等式中,λ是决定罚分强度的调整参数,即收缩量。设置λ=0 将导致线性回归的损失函数,并且随着λ增加,越来越多的系数被设置为零,因此剩余的系数被 Lasso 回归“选择”为重要的。

对训练数据拟合套索回归,并绘制结果系数,如下所示:

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

Lasso 回归算法将床上时间和浅睡分钟的系数降低到接近于零,认为它们没有其他四个特征重要。这很方便,因为如果我们在模型中包含所有要素,我们将面临主要的多重共线性问题。让我们从数据集中去掉这两个特征:

现在我们已经选择了一组四个特征,我们可以继续建立一些机器学习模型,这些模型将使用这四个特征来预测睡眠分数。

多元线性回归

总之,多元线性回归(MLR)用于估计一个因变量和两个或更多自变量之间的关系。在我们的例子中,它将被用来估计睡眠分数与睡眠分钟数、清醒分钟数、快速眼动睡眠分钟数和深度睡眠分钟数之间的关系。注意,MLR 假设这些变量之间的关系是线性的。

让我们训练一个 MLR 模型并评估它的性能:

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

所有的性能测量都比基线模型好得多(感谢上帝)。尤其是精度似乎非常高,但这可能会产生误导,这就是为什么考虑多种测量方法非常重要。回归性能最重要的度量之一是 R 平方。一般来说,R 平方度量的是由自变量解释的因变量方差的比例。因此,在我们的情况下,这是一个衡量睡眠分数差异有多少是由我们的特征解释的。大约 0.76 的值已经很不错了,但是让我们看看是否可以通过使用不同的模型做得更好。

回归统计

在我们继续学习其他机器学习模型之前,我想看一下我们训练数据的多元线性回归的回归输出:

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

关于回归输出,需要注意以下几点:

  1. 所有 p 值都具有统计学意义。
  2. 睡眠分钟数、快速眼动睡眠分钟数和深度睡眠分钟数具有正系数,这意味着这些变量的增加会增加睡眠分数。
  3. 醒着的分钟数有一个负系数,表明醒着的时间越长,睡眠得分越低。
  4. 根据系数的大小,快速眼动睡眠似乎比深度睡眠对睡眠分数有更大的积极影响。

回归输出为理解不同的睡眠统计如何影响睡眠得分提供了一个良好的起点。睡眠时间越长,睡眠得分越高。这是有道理的,因为更多的睡眠(直到某一点)通常是有益的。同样,更多的快速眼动和深度睡眠时间也会增加睡眠得分。这也是有意义的,因为这两个睡眠阶段都提供了重要的恢复益处。对于睡眠分数的计算,Fitbit 似乎认为 REM 睡眠比深度睡眠更重要(系数的数量级更高),这对我来说是回归分析最有趣的结果之一。最后,醒着的时间越长,睡眠得分越低。再说一遍,这完全有道理,因为在一个人的睡眠窗口期花更多的时间醒着表明坐立不安,并剥夺了睡眠时间提供的恢复能力。

对于那些有兴趣了解不同睡眠阶段和睡眠重要性的人,我强烈推荐马修·沃克的《我们为什么睡觉》。这是一本写得非常精彩的书,有着引人入胜的实验和见解!

尽管如此,需要注意的是,由于要素之间的相关性,上述输出的可解释性有些有限。在多元线性回归中,系数告诉你当自变量增加一个单位时,在所有其他自变量保持不变的情况下,因变量预计会增加多少。在我们的例子中,因为独立变量是相关的,我们不能期望一个变量改变而其他变量不变,因此不能以这种方式可靠地解释系数。在解释你的模型时,总是要注意多重共线性!

让我们看看其他机器学习模型的表现是否优于多元线性回归。

随机森林回归量

随机森林是最受欢迎的机器学习模型之一,因为它们能够在分类和回归问题上表现良好。简而言之,随机森林是一种通过引导聚合利用多个决策树的集成技术,也称为“装袋”。那到底是什么意思?

为了更好地理解这一点,我们首先需要理解决策树回归是如何工作的。

决策树回归

顾名思义,决策树以树状结构的形式构建预测模型,可能如下所示:

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

预测游戏时间的决策树

在上面的例子中,决策树基于各种特征迭代地分割数据集,以便预测玩游戏将花费多少小时。但是树如何知道首先分割哪些特征,以及在树的更下面分割哪些特征呢?毕竟,如果我们改变用于进行分割的特征的顺序,预测可能会不同。

在回归问题中,决定在特定结点上分割数据集的最常用方法是均方误差(MSE)。决策树尝试使用不同的特征来分割数据集,并计算得到的 MSE。导致最低 MSE 的特征被选择用于手边的分割。这个过程一直持续到树到达一片叶子(一个端点)或预定的最大深度。最大深度可用于减少过度拟合,因为如果允许决策树继续直到它找到一片叶子,它可能会严重过度拟合训练数据。以这种方式使用最大深度被称为树的“修剪”。

决策树有两个主要限制:

  1. 贪婪——决策树并不总是全局最优的,因为我们假设创建决策树的最佳方式是找到将导致 MSE 最大程度降低的特征,而不考虑次优分割是否会导致更好的分割(“贪婪”策略)。
  2. 过度拟合-树的结构通常过于依赖于训练数据,经常修剪树不足以克服这个问题。

随机森林解决了这两个限制。

随机森林

正如随机森林中的“森林”所暗示的那样,它们由许多决策树组成,并且它们的预测是通过对森林中每个决策树的预测进行平均而得到的。把这想象成一种民主。在一个重要问题上只有一个人投票可能不能代表整个社区的真实感受,但从社区中随机选择的许多成员那里收集投票可能会提供准确的代表。

但《随机森林》中的“随机”到底代表了什么?

在随机森林中,每个决策树都是使用训练集中随机选择的数据点子集创建的。这样,每棵树都是不同的,但是所有的树仍然是从相同的训练数据的一部分创建的。子集通过替换随机选择,这意味着数据点被“放回袋子”中,并且可以再次被挑选用于另一个决策树。

除了为每棵树选择不同的随机子集,随机森林中的决策树在每次分裂时只考虑随机选择的特征的子集。为即将进行的分割选择最佳特征,并且在下一个节点,评估一组新的随机特征,等等。

通过使用这些“打包”技术构建决策树,随机森林很好地解决了单个决策树的局限性,并设法将孤立的弱预测器转变为组中的强预测器,类似于投票示例。

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

Python 中的随机森林回归

使用 Python 中的 scikit-learn 库,大多数机器学习模型都是以同样的方式构建的。首先,您初始化模型,然后在训练集上训练它,然后在验证集上评估它。代码如下:

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

与多元线性回归类似,随机森林的表现远远好于基线模型。也就是说,它的 R 平方和精度低于 MLR。那么,围绕随机森林的所有宣传是关于什么的呢?

这个问题的答案可以在这里找到(提示:超参数优化):

[## 交叉验证和超参数调整:如何优化你的机器学习模型

非常准确地预测 Fitbit 的睡眠分数

medium.com](https://medium.com/@bennerjonas10/cross-validation-and-hyperparameter-tuning-how-to-optimise-your-machine-learning-model-13f005af9d7d)

极端梯度推进回归器

与随机森林类似,梯度增强是一个集成学习器,这意味着它基于一组单独的模型(通常是决策树)来创建最终模型。与随机森林相比,梯度增强的不同之处在于集合方法的类型。随机森林使用“Bagging”(前面描述过),梯度增强使用“Boosting”。

梯度推进

梯度推进背后的一般思想是,通过对具有错误预测和高误差的实例赋予更多权重来依次构建各个模型。因此,该模式“从过去的错误中吸取教训”。

该模型通过梯度下降最小化成本函数。在每一轮训练中,弱学习者(决策树)做出预测,并与实际结果进行比较。预测和实际结果之间的距离代表模型的误差。然后,误差可用于计算梯度,即损失函数的偏导数,以计算出在哪个方向上改变模型参数以减小误差。下图显示了这是如何工作的:

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

梯度下降

这些调整的速率(上图中的“增量步幅”)可通过超参数“学习速率”进行设置。

极端梯度推进

极端梯度增强通过计算成本函数的二阶偏导数来改进梯度增强,这有助于获得成本函数的最小值,并使用类似于使用 Lasso 回归描述的高级正则化,这改进了模型的泛化。

在 Python 中,训练和评估极端梯度增强回归变量遵循与随机森林回归变量相同的拟合和评分过程:

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

性能指标非常接近随机森林,也就是说,它的表现还不错,但仍然不如我们过去的多元线性回归。

从这里去哪里?

到目前为止,我们还没有在随机森林或极端梯度推进回归中提供任何超参数。各自的库为每个模型的超参数提供了合理的默认值,但没有一个尺寸适合所有情况。通过调整一些超参数,我们有可能极大地提高这两个模型的性能。

此外,对于我们的性能评估,到目前为止,我们只依赖于模型在一个相对较小的验证集上的性能。因此,性能在很大程度上取决于该验证集在整体上对睡眠数据的代表性。

在本文的第三部分,我解决了这两个问题,并提高了随机森林和极端梯度推进回归器的性能。看这里:

[## 交叉验证和超参数调整:如何优化你的机器学习模型

非常准确地预测 Fitbit 的睡眠分数

medium.co](https://medium.com/@bennerjonas10/cross-validation-and-hyperparameter-tuning-how-to-optimise-your-machine-learning-model-13f005af9d7d)

利用机器学习预测未来比特币价格

原文:https://towardsdatascience.com/using-machine-learning-to-predict-future-bitcoin-prices-6637e7bfa58f?source=collection_archive---------5-----------------------

如何利用深度学习预测未来

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

克里斯·利维拉尼在 Unsplash 上的照片

有没有可能预测明天的比特币价格?或者如果这个跳跃太远,那么 20 分钟后的价格呢?

我将使用长短期记忆 (LSTM) RNN 机器学习模型来预测 20 分钟后的比特币价格,仅仅依靠简单的历史金融数据。

我写这篇文章,部分是作为一个指南,部分是作为一个练习,探索 LSTM 模型在比特币价格预测中的潜在用途。因此,我可能会跳过一些基本原理,因为这些很容易在其他地方找到。

免责声明:我的经验主要来自好奇心、个人和专业能力的实际应用,以及对有效市场的兴趣。这可能意味着使用的一些术语和方法可能与其他术语和方法不同。

LSTM 模式简介

LSTM 模式到底是什么?简而言之,这是一种能够学习长期依赖性的递归神经网络。与我们使用以前的经验来告知(最好是更好的)未来结果类似,LSTM 模型使用更新门和遗忘门来随机记住和忘记历史信息片段,以告知他们的预测。

像这样使用历史“背景”的能力使得这些模型在许多其他应用中特别适合于预测目的。

为了建立更深入的理解,请看一看这个伟大的资源,它深入研究了这种类型的神经网络的确切机制。

我们将使用什么工具

对于这个练习,我使用 NumpyPandas 来处理数据,使用 Keras / Tensorflow 来实现机器学习功能。为了调试和更好地呈现代码,我使用了 Jupyter 笔记本。

收集所需的数据

为了训练我们的模型,我们需要训练数据。任何金融定价数据在这里都足够了,只要它可以以分钟为间隔获得,并且大小合理。我使用的是大约 23 天的财务数据,时间间隔为一分钟。

我自己使用北海巨妖 API 收集了这些数据,所以它很可能包含缺口。但这只是为了举例说明。你可以在这里找到我的数据和完整的源代码。

数据准备

首先,我们从导入所有必需的包开始,加载数据集并删除我们不感兴趣的行。

我们将数据集分成训练集和测试集,并标准化其特征。标准化是很好的实践,因为它减少了在某些特征的方差可能高于其他特征的情况下的过度拟合。

LSTM 模型要求我们按块组织数据。我们的数据以一分钟为间隔进行分组,我们将使用 50 分钟的数据块来预测下一个数据块。

模型

现在是时候训练我们的模型了。我们选择我们想要使用的模型类型;在这种情况下是连续的,我们决定超参数。

我使用的模型相对简单,包含 5 个隐藏层,每个隐藏层有 50 个神经元,每个隐藏层之间有一个漏失。我们使用均方误差损失函数,Adam 优化器,将批量大小设置为 32,并遍历该网络 10 个时期。

决定超参数更多的是艺术而不是科学,值得测试出多种选择,以了解什么最适合您的测试数据和生产。优化你的超参数超出了本文的范围,但是网上有一些很好的资源。

现在我们有了一个可以用来建立预测的模型,我们可以看看它在测试数据中的表现。下图绘制了要价和出价的测试数据(y_test)预测(y_hat)。正如我们可以看到的,预测值与训练数据非常匹配。

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

酷!所以这意味着我现在可以预测比特币的价格了?

不,不是真的。不幸的是。

这些模型的预测能力绝对令人印象深刻,但如果你仔细观察预测,你会发现它与测试数据非常接近。这种预测似乎密切跟踪价格的变化,但往往不能正确预测这些变化发生时的价格。这对于任何你想用它的交易策略来说都不是非常有用。

预测未来

如果我们想预测 20 分钟后的价格,一种选择是在最近的历史数据间隔内运行模型,将预测连接到历史数据数组的末尾,然后将该数组反馈到模型中,继续这一过程,直到我们有 20 个预测的价格预测块。

下面的代码正是这样做的,并根据测试数据绘制了买价和卖价。不出所料,它并没有真正告诉你太多,这个特定的预测错误地预测了价格上涨。

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

但是对于剩余的测试数据来说,情况会是这样吗?由于测试数据由 6,661 行组成,我们有 133 个窗口可以预测交易。

让我们预测剩余的 20 分钟,并对 n=4 和 n=0 时的 ask_price 的累积差值求和。

下图显示了预测的有利可图的交易和实际交易的累积收益,其中一个人以 n=0 的要价买入,以 n=4 的要价卖出。

有趣的是,给你带来实际净收益的交易似乎超过了会导致亏损的交易。有了 100 美元的本金,你将获得 31 美元。仅仅过了 4.5 天。那可是天文数字的回报!

好得难以置信。

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

任何曾经在交易所购买 BTC 或任何金融工具的人现在都会告诉我,你实际上不能在市场上以要价卖出。买卖价差会阻止我们从价格上涨中获利,只有当资产的增值高于价差时,我们才能从交易中获利。

一旦我们把这个模型应用到我们的预测中,我们的金鹅开始失去一些羽毛。我们同样的 100 美元仍将升值约 3.40 美元!仍然是令人印象深刻的 1318%的年回报率。

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

现在让我们加入交易成本。我们将采取自由的假设,我们可以在市场上交易,只收取 0.1%的低费用——这是为交易量非常大的交易者保留的。

很快就会发现,我们的金鹅根本不是金子。我们的模型只能识别 3 个潜在的盈利交易,同样的 100 美元现在会变成 2.26 美元的亏损。

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

结论

上面的练习表明,简单的金融数据在预测价格的短期变化方面具有一定的预测能力,但是由于没有从这些信息中获利的实际机会,所以从交易的角度来看,这种特定的模型是相对无用的。

LSTM 模型等工具正变得越来越容易获得,大型团体和机构正在通过更好的数据和卓越的处理能力来拓展这些模型的能力。这导致市场将越来越多的信息整合到资产价格中,使得套利机会变得很少。

建立一个具有战胜市场的预测能力的模型不是一件简单的事情,如果有人能够轻松地使用这些工具赚钱,我肯定不会在这里与你分享。

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

使用机器学习预测 NBA 全明星赛,第 1 部分:数据收集

原文:https://towardsdatascience.com/using-machine-learning-to-predict-nba-all-stars-part-1-data-collection-9fb94d386530?source=collection_archive---------49-----------------------

一个端到端的深度调查,以定量研究 NBA 全明星选拔。

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

照片由 Abhishek Chandra 拍摄(Unsplash)

成为 NBA 全明星到底需要什么?作为一个长期的篮球迷,这是一个有趣且值得探究的问题。

本文将介绍我用来构建解决这个问题所需的历史数据集的各种数据搜集技术。

链接查看建模、预测和解释!

如果你想开门见山,跳过数据收集过程,直接去 第二部分:建模 看预测!

链接到回购!

点击 此处 查看我的 Github 上的完整回购(附自述)!

背景和动机

NBA 全明星赛是一年一度的表演赛,展示了联盟中的 24 名顶级球员。炫目而迷人,这一事件是全明星周末的最大亮点,一线名人经常成群结队地前来观看这一事件。

鉴于该联盟由全球排名前 450 位的球员组成,入选全明星是一项重大荣誉——许多球员甚至在被选中之前就为自己设定了一个雄心勃勃的目标。当我们在一个球员的职业生涯结束时评估他的遗产时,我们经常把他们的全明星选择带入谈话中,作为衡量他们伟大的一种方式。

考虑到这一点,除了潜在的工资奖金影响之外,人们还希望选拔系统是公平、公正的,并植根于某种客观性。然而,可悲的是,事实并非如此。

首发由球迷、球员和媒体投票决定,而主教练选择替补。这种主观过程尽最大努力选择在当前赛季有精英个人表现记录的候选人,但它也不是没有缺陷。像球队记录和社交媒体存在这样的事情会泄露到这个决定过程中,每年公布名单后,球迷们都会不可避免地抗议,抱怨他们最喜欢的球员被错误地冷落。

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

从本质上来说,我们可能会期望被选中的球员仅仅是大家一致认同的*“联盟 24 名最佳球员”…* 但是我们到底如何定义*“最佳”*?显然,像得分和篮板这样的比赛数据很重要,但是比赛场地并不总是公平的。对于任何一个长期的 NBA 球迷来说,球队排名在我们如何评价球员个人方面起着很大的作用,这不是什么秘密;很明显,如果你的球队发现自己排名垫底,就很难获得球迷和同行的尊重和认可。

这种对失败球队的负面偏见让许多想成为全明星的人无法入选,比如 2016-2017 赛季的卡尔-安东尼·唐斯,但这种影响很难量化描述。与好球队相比,差球队的球员的球门柱要窄多少?同样的担忧也出现在伤病上。错过延长时间的玩家将会发现很难得到一个位置,即使他们在实际比赛时给出了很高的数字。但是什么才算是*“延长时间”*?我们用来做这个决定的强制截止值到底在哪里?

为了试图揭开一些迷雾,客观地了解成为 NBA 全明星需要什么,我去挖掘相关数据,并应用机器学习技术来具体模拟这一选择过程。

蓝图

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

在机器学习中,有一个被称为“ 垃圾输入,垃圾输出” 的概念——意思是一个模型的输出只会和驱动它的数据一样好。如果我们想要准确而有洞察力的预测,我们必须确保我们提供的数据集是一致的、有用的,并且来源可靠。

我们将需要收集几个赛季的历史数据,但在我们拿出任何 Jupyter 笔记本之前,我们需要坐下来思考一下全明星选择中需要考虑的所有可能的特征。对于一个给定的玩家,什么信息可以帮助我们做出决定?

首先,我们需要传统的数据,比如得分,篮板,助攻等等。我们可以增加一些高级的统计数据,包括使用率、真实命中率、防守胜率和 PIE。PIE 代表玩家影响力评估器nba.com的 PER(玩家效率等级)版本,作为玩家表现的总括指标。

投票时的团队排名也很重要,所以我们也会用到它。对于受伤和错过的时间,我们可以比较个人和团队的比赛次数,并提取适当的百分比。

至关重要的是,我们只考虑前半个赛季。

声誉和粉丝偏爱是需要考虑的重要因素,但在这里很难实现。在 Instagram 和 Twitter 等平台上抓取玩家的社交媒体存在是(a)一个非常复杂的数据争论挑战,( b)由于社交媒体的疯狂变化,不可能可靠地向后扩展——此外,它直到 2010 年才真正存在。

在这个地方,我们可以用一个便宜的声誉代理:这个球员在职业生涯中有多少次入选全明星赛。它将与年轻和未经证实的球员斗争,但似乎这个功能可以做足够的工作来获得球迷和媒体的长期欢迎。

我们的目标是构建一个数据集,包含自 1996 年以来每个赛季的每个 NBA 球员的所有这些特征(最早的赛季是在stats.nba.com追踪的)。至关重要的是,我们只考虑赛季的前半段,因为这是挑选球员的时候,之后的一切都无关紧要。我选择每年的 1 月 21 日作为截止日期,因为这大概是开始做出选择决定的时间。

我们的输出标签将简单地表示该球员是否被选中(1)参加那年的全明星赛(0)。这是我们的基本事实。尽管 2020 年 ASG 已经发生,并且名单也早已确定,但我将从标记的数据集中忽略当前赛季(2019-20),以便我们可以稍后运行模型,并将预测与已知结果进行比较。

一旦我们有了一个满意的模型,我们就可以窥视黑盒子,定量地探索全明星标准的内部运作,以及它们之间到底是如何相互作用的,希望能够平息我们的好奇心。

构建数据集

获取数据

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

Stats.nba.com 的将是我们满足需求的首要资源。它支持在自定义日期范围内查询统计数据的功能,这对于我们想要做的事情是必不可少的。他们的网页本质上是动态的(AJAX ),所以从他们那里抓取并不简单——稍后会有更多的介绍。

来自这个资源的数据可以追溯到 1996-97 赛季,为我们的模型提供了一个不错的训练数据池。

例如,来自两个不同时代的 NBA 观众可能对防守有不同的侧重。因此,1996-1997 年的临界值在这里是完全可以接受的。

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

2018-2019 赛季前半段球员的“传统”表。在这里链接到表

传统*、高级、防御牌桌之间,我们可以得到我们所需要的大部分。接下来是团队排名和团队游戏(我们需要设计游戏百分比)。我决定使用团队会议排名而不是总排名,因为出于讨论的目的,会议内部排名通常是人们最关心的。所有这些都可以在团队表中找到。*

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

2018-2019 赛季中途的东部“球队”表。在此链接到表格

请注意,在球员表中,球队以他们的简写符号(例如,CHI)列出,但是在球队表中使用全名(例如,芝加哥公牛)。当我们将这些合并在一起时,我们需要创建某种查找结构来链接它们。

剩下的就是球员的全明星选择历史,这是我们的输出标签和“之前全明星选择的数量”功能所需要的。幸运的是,这不依赖于赛季中的任何日期过滤器——所以我们可以使用另一个很好的篮球统计资源:篮球参考 这些网页是静态的 HTML,这意味着从这些网页中抓取会更加轻量和简单。

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

2015 年 ASG 花名册(东部),也列出了入选但因伤不能参赛的球员。在此链接到表格

这里值得注意的是,我们不应该忽视那些入选全明星但由于受伤而无法参加的球员。即使他们没有正式占据一个花名册的位置,他们最初被选中,并将在整个分析中被视为全明星,以及他们的替代者。

随着游戏的发展,联赛节奏在过去几年里稳步增长,所以我们将通过除以节奏来标准化适用的游戏统计数据。我们可以很容易地从篮球参考资料中得到每年的联盟平均速度。

由于停摆,1999 年没有全明星赛,我们在构建数据集时将完全跳过 1998-1999 赛季。

抓取网页:美丽的 Soup 和硒

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

由于位于 Basketball Reference 的网页是静态的(意味着内容直接嵌入到其 HTML 中),我们可以简单地使用requests库来加载 HTML 文档。

然后我们将使用BeautifulSoup来解析文档,并有选择地提取我们感兴趣的数据。使用pd.read_html( … )方法,我们可以将数据加载到一个熟悉的熊猫数据框架中,然后我们开始比赛。

即使我们的标签数据集将从 1996-1997 赛季开始,我们也需要比这更早的全明星阵容数据。例如,哈基姆·奥拉朱旺在 96 年是一名活跃的球员,但他的第一次全明星选择是在 85 年。我们用 1970 年来初始化我们的 ASG 刮擦,以确保我们绝对没有错过任何人的机会。

我们在这里构建的数据结构将是一个集合字典,将球员姓名映射到他们入选全明星的所有年份的集合。我们pickle这个字典(序列化它)并导出它以备将来调用。

对于那些好奇的人来说,下面是实现这一切的代码:

用于从篮球参考中抓取历史全明星出场数据的脚本。

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

此导出数据结构的预览。

下一步,我们需要从 stats.nba.com 收集表格数据,这是事情变得有点复杂的地方。这些网页有一个使用 AJAX (异步 JavaScript 和 XML)的动态实现,所以显示的表格在 HTML 文档中是找不到的。

相反,网页会向 API 端点发送一个请求(附带数据过滤器),并返回一个 JSON 响应。我们必须经历识别相关端点的过程,然后自己手动管理数据。这将变得令人难以置信的乏味,因为现在我们讨论的是在给定一个日期范围内的每个玩家的高级统计数据,而不是给定一大堆盒子分数。

所以我们有两个真正的选择:

  • 使用一个预建的实用程序来做这件事,像[nba-api](https://pypi.org/project/nba-api/) 阅读所有的文档来学习功能
  • 使用自动化浏览器软件,如 Selenium WebDriver

尽管这是一种更暴力的方法,但为了节省时间,我还是选择了使用 Selenium。

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

使用 Selenium ChromeDriver 进行网页抓取。

Selenium 是一个允许我们调用自动化浏览器(WebDriver)的框架,使用 Python 中的 API,我们可以控制这个浏览器导航到相关网页。经过一段延迟时间后,一旦从后端收到响应,我们就可以最终加载 HTML 文档(包含重要数据)并像以前一样使用BeautifulSoupPandas

在这一过程中,我还遇到了一些困难,但大多数问题都与保管有关(就像夏洛特黄蜂队在前山猫队时代被称为 CHH,后来被称为查),这些问题都通过评论得到了解释。

剧本将从 1996 年到今天的每个赛季为每个 NBA 球员刮stats.nba.com*。数据集经过适当的处理和合并,然后使用我们之前创建的全明星查找字典进行扩充。两个数据集以 csv 格式导出,一个包含从 1996-97 赛季到 2018-19 赛季的所有带标签的数据,另一个包含当前赛季的未带标签的数据。*

超过 200 行,这个脚本有点太长了,不能在这里发布,但是可以在回购的这里找到。

下面是这两个数据集的前几行,其中许多要素隐藏在视图之外:

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

带标签的数据集(9595 条记录),为了适合此处,许多要素隐藏在视图之外。

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

2019–2020 年的未标记数据集(486 条记录)。

原始特征的完整列表:

  • 年份(季节开始的日历年)
  • 平均值。速度(该赛季联盟平均速度)
  • 运动员
  • 团队会议排名
  • GP(玩过的游戏)
  • 团队 GP(团队参加的比赛)
  • w(赢的游戏-玩家必须玩过)
  • 每场比赛得分
  • 每场篮板数
  • AST(场均助攻数)
  • 每场比赛抢断次数
  • BLK(每场比赛盖帽数)
  • 每场比赛失误数
  • TS%(真实拍摄百分比)
  • 下午 3 点(每场比赛 3 分)
  • 防御赢股
  • USG%(使用率)
  • 玩家影响力评估器
  • 之前在 ASG 出现过
  • 和去年一样?

这些游戏统计的正式定义可以在 https://stats.nba.com/help/glossary/找到。

接下来…

在本次深度探讨的第二部分中,我们将使用我们在此构建的数据集来研究模拟NBA 全明星选拔流程。感谢阅读!

使用机器学习预测 NBA 全明星赛,第 2 部分:建模

原文:https://towardsdatascience.com/using-machine-learning-to-predict-nba-all-stars-part-2-modelling-a66e6b534998?source=collection_archive---------43-----------------------

一个端到端的深度调查,以定量研究 NBA 全明星选拔。

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

照片由科利昂·布朗(Unsplash)拍摄

回顾

这是由两部分组成的系列文章的第 2 部分*,我们的目标是将数据科学方法应用于 NBA 全明星选拔流程。在第一部分中,我们使用BeautifulSoupSelenium使用网络搜集技术来收集这些数据。*

在这一部分,我们将探索模型的构建、评估和解释。虽然建立一个准确而精确的模型是我们的主要任务,但是我们最终还是希望能够从系统中获得某种程度的可解释性,也就是说看到它所看到的

我将在本文中介绍许多关键概念,但为了简洁起见,我不会覆盖每一行代码。完整的笔记本(有详细的评论)可以在我的 GitHub 上的这里找到。

预处理

处理异常值

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

我们的首要任务是解决训练数据中的统计异常值。在这种情况下,一个离群值是任何球员的选择决定不被他们在那个赛季的表现所证明,经常导致球迷和媒体的广泛反对。

如果留在训练集中,这些记录可能会混淆模型,并对我们的预测和洞察力产生负面影响。

这些玩家分为以下两类(或两类都有):

**答:**这位球员有着漫长而辉煌的职业生涯,在暮年被选为告别球员,尽管他没有达到全明星级别(例如,德克·诺维茨基入选 2019 年 ASG,这是一个非常尊重/致敬的选择)

B: 该球员在赛季中缺席了太多比赛,只是因为压倒性的球迷偏爱才被选中(例如,姚明在 2010-2011 赛季,在可能的 44 场比赛中只参加了 5 场)

训练数据中符合这些标准中的任何一个的全明星(由异常低的 PIE 分数或所玩游戏的数量来表示)被从训练集中清除。由于数据集非常小,这是我们可以手动执行的事情——否则我们将求助于类似于 Tukey 异常值检测的东西。

总共, 13 个球员赛季被取消——完整的名单(包括理由)可以在笔记本中找到

调整游戏统计数据

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

本质上,这不是一个时间序列预测问题,但我们仍然有一些不随时间静止的特征。如果任其发展,这有可能扭曲模型并导致更差的结果。

平均速度由球队每场比赛使用的控球次数来定义,这在不同的 NBA 时代有所不同(例如,04 年的 90 比 18 年的 100)。因此,2018 年场均 30 分的球员应该和 2004 年场均 27 分的球员保持同样的标准。

因此,我们将所有速度相关的特征(得分、篮板、助攻、抢断、盖帽、失误、3pm)除以该赛季的联盟平均速度,以纠正这一问题。这样,我们就不会拿苹果和橘子做比较了。

在这一点上,我们创建了 *Play Pct。*特征,通过将个人在赛季中参加的比赛除以团队的比赛。我们还删除了赛季中少于 7 场比赛的所有记录,因为这些会导致其他比赛数据的极端值,可能会破坏模型。

我们现在有 15 个候选特征用于建模。作为一个剧透,并不是所有这 15 个都被证明是有帮助的预测者,但为了完整起见,我会把它们都列在这里。未绑定的特性最终在最终模型中被删除。

  • 调整后的分数(调整后的点数)
  • 调整后的篮板球
  • 调整后的 AST(调整后的助攻)
  • 调整后的 STL(调整后的抢断)—丢弃
  • 调整后的 BLK(调整后的块)
  • 调整后的 TOV(调整后的失误)—下降
  • 调整后的下午 3 点(调整后的 3 个指针)—丢弃
  • DEFWS(防御性赢股)
  • TS%(真实投篮命中率)—下降
  • USG%(使用率)
  • PIE(玩家影响估计器)
  • 打百分之。
  • 团队会议排名
  • ASG 之前的亮相
  • 和去年一样?—已丢弃

探索性数据分析

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

在我们开始将机器学习算法应用于这个问题之前,了解我们的数据很重要。这包括比较全明星和非全明星之间的属性分布,以及查看不同变量如何相互作用。

为了孤立地查看每个特性,我们可以使用来自seaborn库中的violinplot方法。我将展示 15 个图中的 2 个,以及用于生成它们的代码。

为每个要素生成分类分隔的小提琴图。

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

这里最有趣的是团队等级绝对重要。入选全明星赛的球员更有可能来自一支好球队,而不是一支差球队。

理论上,这似乎是合理的。一个“明星”球员应该能够将他们的天赋转化为团队的成功,但这种思路可能会让我们对一个球员过于挑剔,因为有些事情超出了他们的控制,比如队友和教练。如何处理个人和团队成功的分歧是 NBA 球迷争论的话题,尤其是在试图就联盟 MVP 达成一致的时候。

稍后,我们将进一步探讨这一点。

我们可以使用成对散布矩阵来查看我们的特征如何相互作用。为此,我们使用pairplot

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

最后,我们可以使用主成分分析来粗略地看看我们的两个类实际上有多不同。我们应用主成分分析将特征空间的维数从 15 降低到 2,同时保留尽可能多的信息和可变性。绘制这两个主要组成部分揭示了我们数据中的一些内在结构和类别分离。

我们使用来自sklearnpca方法来实现这一点。为了减少主成分的随意性,我们将输入特征缩放到单位方差并移除平均值。

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

仅使用两个主成分,我们仍然能够保留来自 15 个原始特征的总可变性的一半以上。从这个二维投影来看,这两个类别似乎彼此合理地分开了。

为了澄清,我们在这里仅使用 PCA 进行数据可视化——我们不会使用这些主要成分进行建模。PCA 可以用于提高模型训练速度,因为特征较少,但由于我们的数据集相对较小,训练运行时间在这里不是问题。

系统模型化

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

对标记数据集进行分区

我们将标记的数据集分成 3 组:训练、验证和测试。

模型将直接使用训练数据来调整其内部参数。验证数据将用于指导该过程,监控学习过程,并帮助避免过度拟合。然后,一旦模型被冻结,我们可以转向测试数据来评估模型并计算不同的基准。

关于 SMOTE 的快速注释

在处理任何分类问题时,我们必须确保我们的类是平衡的。如果我们不考虑阶级的不平衡,这个模型将会对大多数阶级产生固有的偏见——这是我们绝对不想看到的。

在我们数据集的训练分区中,我们有 368 名全明星5409 名非全明星。随之而来的是,给每个球员贴上非全明星标签的粗略的一揽子策略仍然可以达到 93.6% 的准确率。很明显,我们想劝阻模型不要这样做,所以我们应该平衡这两个类。

为此,我们有三个选择:

  • 欠采样多数类(丢弃数据点)
  • 对少数民族类进行过采样(创建合成数据点)
  • 欠采样和过采样的组合

就性能而言,这些技术实际上没有太大区别,但过采样是实践中最常见的方法。

对于这种情况,我们将采用一种叫做 SMOTE 的方法——合成少数过采样技术。SMOTE 通过在随机数据点和邻近数据点之间进行线性插值,在一个类内创建合成数据点。重复这一过程,直到类别达到平衡。实际上,我们将使用 SMOTE 的一个变体,称为 Borderline SMOTE,它专注于类边界附近的过采样数据点。

这将有望使模型更精确地辨别边缘情况。

至关重要的是,我们只对训练数据进行过采样,而不去碰验证和测试集。分割之前的过采样将导致信息在我们的三个分区之间泄漏,并导致模型具有夸大的准确性指标。

调整 XGBoost 超参数

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

对于这个监督学习问题,我们将调用的机器学习算法是 XGBoost,它是梯度推进决策树的一种实现。对于涉及非结构化数据的数据集,人工神经网络通常会优于其他算法。但是对于包含表格数据的较小数据集(就像这个),像 XGBoost 这样的决策树框架通常占主导地位。XGBoost 的文档可以在这里找到。

与许多其他 MLA 一样,XGBoost 需要几个不同的超参数来控制学习过程,如学习速率、最大树深度和树的数量。我们将应用一个简单的网格搜索来找到最优的学习率,并使用提前停止轮次来对抗过度拟合。对于这个问题,我们将耐心参数设置为 10。如果验证 AUC 分数达到 10 个周期而没有改善,我们终止该学习率的训练并记录最高 AUC。

AUC 是二元分类问题的综合性能度量。AUC 代表曲线下面积,特别是 ROC (受试者操作特征)曲线。ROC 曲线比较了不同阈值设置下的 TPR(真阳性率)和 FPR(假阳性率),说明了分类器在不同区分水平下的诊断能力。

AUC 可以采用[0,1]范围内的值。AUC 为 1 表示该模型能够很好地区分阳性和阴性类别,而 AUC 为 0.5 是我们从随机猜测的模型中预期的值,它没有区分能力。AUC 为 0 意味着模型一直在反转类别(完全区分,但方向错误)。

我们希望我们的模型实现尽可能接近 1 的 AUC。

模型性能和评估

该模型在对训练集中的数据点进行分类方面做得更好,因为这是直接从中学习的数据。通过验证性能,可以了解模型在处理以前从未见过的数据时表现如何。

然而,验证集仍然影响我们在模型调整过程中所做的决定(通过早期停止回合)。当我们计算 F1 分数等分类指标时,我们希望使用在训练期间完全隔离的数据,这就是测试集发挥作用的地方。这些指标将代表模型如何推广到“野生”数据。

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

在测试数据集上运行该模型,我们获得以下混淆矩阵、ROC 曲线和分类度量:

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

蓝色虚线表示预测能力为零(随机猜测)的模型的预期性能。红色曲线说明了我们调优的 XGB 模型在不同阈值水平下的诊断能力。由于我们的 AUC 非常接近 1,我们的模型在区分这两个类别方面非常强大。

指标:

  • 精度: 0.975151
  • 精度: 0.784314
  • 召回: 0.842105
  • F1 得分: 0.812183
  • 日志。损失: 0.103545
  • *ROC AUC:*0.989187

这些指标是使用二进制分类阈值 0.835 计算的(针对最高 F1 分数进行了优化)。

2019-2020 赛季全明星预测

现在我们有了一个工作模型,我们可以将它应用于当前赛季的球员数据集。这里使用的分类模式略有不同——我们将从每场会议中选择 12 名预测当选概率最高的球员,而不是使用我们的静态阈值 0.835。

**小细节:**实际上,这 12 个花名册位置的细分是有限制的:6 个前场球员(F/C),4 个后卫(G),2 个通配符。然而,有了这两个通配符,再加上不断增加的球员位置之间的流动性,这个限制是相当不重要的。安排 12 名球员扮演这些角色几乎总是可能的。

事不宜迟,以下是预测:

东部联盟:

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

西部联盟:

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

我们做得怎么样?

由于 2020 年全明星赛早已过去,我们可以看看我们有多少选择是正确的。

在每次会议中,我们有 11 个选择是正确的,一个选择是错误的。

在东部,我们的模型错误地选择了扎克·拉文作为全明星球员,这将是他有史以来的第一次选择。相反,特蕾·杨被选中了。有趣的是,我们的模型真的不喜欢 Trae 被选中的前景,他在会议中排名第 21 位,甚至低于托拜厄斯·哈里斯和尼古拉·武切维奇。我们将在下一节研究这个问题。

看看西方的预测,这个模型选择了保罗·乔治作为花名册上的一个位置,却错过了克里斯·保罗。实际上,如果我们看一下克里斯·保罗的预计概率,模型 真的 没有想到他会被选中。同样,我们将在下一步探讨这些决策。

所以总的来说,我们得到了正确的 22/24 选秀权,这对 91.67% 有利。不可怕,考虑到我们没有纳入社交媒体指标或球员对那个赛季的叙述。

模型解释

我们有一个满意的模型,它似乎能做出相当准确的预测。但到目前为止,模型本身相当不透明。我们可以观察它的最终输出概率,但是这个模型实际上是如何工作的呢?哪些特性在决定输出方面起着最大的作用?为什么模特不喜欢克里斯·保罗和特蕾·杨的全明星前景?

本质上,有些模型比其他模型更容易直接解释。例如,对于线性回归模型,每个特性的影响由其 beta 系数表示。但是对于更复杂的模型,比如 XGBoost,就没有这么简单了。

幸运的是,已经有成熟的技术来评估每个特性在这些模型中的影响。

这里我们将使用的是 SHAP 值。SHAP(SHapley Additive explaints)是“一种解释任何机器学习模型输出的博弈论方法”。我们将用 PDP(部分相关性图)来补充这一分析,以了解当我们保持其他输入不变时,不同的特征究竟如何改变预测。

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

毫不奇怪,(玩家影响评估者)的分数对预测有最大的平均影响。得分紧随其后,这正说明了得分对于在 NBA 获得“明星”地位是多么重要。

但是这里真正有趣的是团队会议等级特性。对于 NBA 球迷来说,众所周知,一个球员在团队中的成功在他们的印象中扮演着重要的角色,尤其是当他们和其他球员比较时。如果球员 X 和球员 Y 都有相同的数据,但是 X 的球队在联盟中排名第一,而 Y 的球队排名第 13,那么很难证明 Y 是更好的球员。显然这是一个广泛的概括,但这一点仍然成立。

最吸引人的部分(至少对我来说)是这个特征在决定全明星地位上的绝对份量。也许我们不应该谴责表现不佳的球队中的明星球员,就像我们谴责他们希望为更好的球队效力一样。对粉丝的忠诚是重要的,但这真的应该优先于一个球员努力争取他们无可否认应得的认可吗?

这一发现(以及其他发现)在 SHAP 细节和部分依赖图中得到了进一步的支持。

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

测试数据集中的每个玩家在图中的每一行添加一个点。圆点的颜色表示该玩家的属性是高还是低,横向位置表示该属性值对预测概率的影响程度。

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

PIE 得分在 10 到 13 之间的模型影响增加非常显著。这个范围可以说是成为全明星的一个“驼峰”。

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

随着球队排名越来越差,全明星入选概率很快就会减少。

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

颜色代表基于饼图和团队会议等级交互的模型影响。

上面的情节真的很能说明问题。控制所有其他特征,该模型将以大致相同的方式处理以下两个玩家:

  • 馅饼值为 20 的玩家(例如卢卡·东契奇),团队排名为 15
  • 一个馅饼值为 11(例如德文特·格雷厄姆)而团队排名为 1 的玩家

当然,这两个特性并不是完全独立的;举起更高的馅饼应该有助于你的球队攀升排名,我认为这是选民和记者在忽视糟糕球队的球员时下意识坚持的逻辑——“如果他们真的是明星,他们的球队就不会在第 15 名。”我认为这个论点是正确的,但它有可能被过度应用。最近记忆中的许多冷嘲热讽都属于这一类,一个主要的例子是过去几年的德文·布克。(是的,他参加了今年的比赛,但最初没有入选——只是作为伤病替补。)

回到我们的模型,这就是为什么 Trae Young 在我们的东部联盟预测中排名如此不利。亚特兰大老鹰队在东部排名倒数第一,历史上像这样的垫底球员很少出现全明星。

然而,Trae Young 是这个模型没有考虑到的一个非常独特的例子。老鹰队在赛季前半段饱受伤病困扰,约翰·科林斯因 PED 禁赛 25 场也于事无补。所有这些都加上一个潜在的事实,老鹰队是一支年轻,缺乏经验的球队,压倒性的失败似乎是不可避免的。用联盟中几乎任何一个非一线队的球星来交换特蕾·杨,他们都不太可能做得更好。

Trae Young 今年的全明星选择得到了几乎一致的认可,但出于上述原因,这就是为什么模特错过了他。

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

如果一名球员在赛季中长时间缺阵,他们被选中的可能性开始下降。

特定玩家的 SHAP 力情节

我们可以进一步扩展 SHAP 分析,研究特定玩家的模型决策。通过使用 force plots,我们可以为联盟中的每个球员生成个性化的成绩单,概述他们赛季的哪些方面帮助或阻碍了他们的全明星概率。

这些是最有见地的边缘案例——在全明星边界附近的球员,他们有可能以任何一种方式做出案例。

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

菲尼克斯太阳队在联盟中排名第 11 位,这对德文布克的模型输出产生了重大的不利影响。

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

探索克里斯·保罗的独特情况

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

不可否认,克里斯·保罗是一名伟大的球员。虽然他已经接近职业生涯的暮年,但他仍然不断地为他适合的任何球队增加重要的价值。许多分析家将保罗列入了西部全明星预备队的候选名单,而他们是对的——他得到了一个当之无愧的位置。但他的表现和价值在历史上很难跨越到统计表上——所以,在纸面上,他的数据通常不会非常令人印象深刻。因为“数字”几乎是我们的模型所看到的全部,所以他的选择概率很低也就不足为奇了。

此外,本赛季围绕克里斯·保罗有一个引人注目的故事。当他在 7 月份从休斯顿火箭队被交易到 OKC 雷霆队以换取拉塞尔·维斯特布鲁克时,每个人都立即否定了雷霆队的这个赛季。人们预计雷霆队将进入一个重建和重组的时代,并将在接下来的几年里萎靡不振。

但这并没有发生。相反,克里斯·保罗带领雷霆成为了臭名昭著的西部联盟中最好的球队之一,并得到了广泛的赞誉。当然,整个团队和组织都应该为这一成就而受到赞扬,但保罗无疑在翻转雷霆队本赛季的剧本中扮演了首席指挥官的角色。

我们在这里开发的预测模型显然无法捕捉像这样的失败者的叙述。虽然该模型通常是准确的,但重要的是要意识到它的缺点,并找出哪里可能出错。

总之…

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

这整个项目仅仅源于好奇。作为一个长期的 NBA 球迷,宣布全明星阵容是我每个赛季都期待的事情。但是这些年来,一些精选(和非精选)让我挠头,想要答案。这种好奇心最终达到了一个临界点,于是我决定将这个问题框定为一个数据科学问题,并揭示一些情况。

这个项目对我个人来说是一次很好的学习经历,尤其是在模型解释和可解释性方面。应用技术将“黑盒”模型分解成更有意义和更容易理解的东西是一项非常有价值的努力,并真正丰富了它所提供的见解。从动态网页中抓取数据也是一个有趣的挑战。

感谢您花时间阅读这篇文章,并跟随我从头到尾研究这个问题。如果您对这个项目有任何问题,或者对潜在的改进有任何建议,请在评论部分告诉我-我总是渴望拓宽我的数据科学技能集,探索新的想法。

使用机器学习预测莱茵河水位

原文:https://towardsdatascience.com/using-machine-learning-to-predict-rhine-water-levels-44afce697074?source=collection_archive---------11-----------------------

LSTM 模型如何极大地改善了我的预测

长短期记忆(LSTM)模型是一种功能强大的神经网络,非常适合预测与时间相关的数据。莱茵河的水位正好属于这一类:它们随着时间的推移而变化,取决于一系列变量,如雨水、温度和阿尔卑斯山的积雪。

莱茵河是欧洲的命脉。几个世纪以来,它一直是将货物运往德国、法国、瑞士和中欧的主要通道。然而,随着气候变化,河流水位可能会变得更加多变。因此,准确预测河流水位是从航运公司到大宗商品交易商和工业集团等一系列行为者的首要关切。

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

一艘满载煤炭的驳船在莱茵河航行(来源:https://commons . wikimedia . org/wiki/File:Coal _ barge _ Chilandia _ on _ Rhine - looking _ south . jpg

与经典的基于回归的模型不同,LSTMs 能够捕捉不同变量之间的非线性关系;更准确地说,这些变量之间的序列相关性。这篇博客关注的是使用 LSTMs 预测莱茵河的问题,而不是这些模型背后的理论。

眼前的问题

我们在这里寻求解决的问题如下:我们希望尽可能精确地预测德国西部关键阻塞点 Kaub 的第二天水位。

我们有从 2000 年 1 月 2 日到 2020 年 7 月 27 日的历史每日数据,相当于 7513 次观测。数据集包括 15 个不同的类别,显示为列:

  • “日期”:观察的日期
  • “Kaub”:Kaub 水位每天的差异,以厘米为单位——这是我们试图预测的“y”值(来源:WSV)
  • 莱茵费尔登:瑞士莱茵费尔登的水流量的绝对值,单位为立方米每秒(来源:符拔)
  • “Domat”:靠近莱茵河源头的 Domat 的水流量的绝对值,单位为立方米每秒(来源:符拔)
  • “precip_middle”:莱茵河沿岸 20 个气象站记录的平均日降雨量,单位为毫米(来源:DWD)
  • “avgtemp_middle”:在相同站点记录的平均温度,单位为摄氏度
  • “最高温度 _ 中间值”:在相同站点记录的最高温度
  • “mintemp_middle”:相同站点记录的最低温度
  • “precip_main”:莱茵河主要支流美因河沿岸 8 个气象站记录的平均日降雨量,单位为毫米(来源:DWD)
  • “avgtemp_main”:在相同站点记录的平均温度,单位为摄氏度
  • “maxtemp_main”:在相同站点记录的最高温度
  • “mintemp_main”:在相同站点记录的最低温度
  • precip _ neck ar:neck ar 沿岸 7 个气象站记录的平均日降雨量,也是莱茵河的主要支流,单位为毫米(来源:DWD)
  • “avgtemp_neckar”:在相同站点记录的平均温度,单位为摄氏度
  • “maxtemp_neckar”:在相同站点记录的最高温度
  • “mintemp_neckar”:在相同站点记录的最低温度

请注意,变量的选择完全是我的,是基于我处理莱茵分析的经验。无论是使用经典回归模型还是神经网络,选择正确的输入是时间序列分析中最重要的步骤之一。如果选择的变量太少,模型可能无法捕捉数据的全部复杂性(这称为欠拟合)。相比之下,如果选择太多输入,模型很可能会过度适应训练集。这很糟糕,因为这可能意味着模型很难归纳到一个新的数据集,而这个数据集对于预测是必不可少的。

首先,让我们加载本练习所需的所有库:

import datetime
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import joblib

下面是数据集前几行的示例。我们可以通过熊猫图书馆轻松加载它:

# first, we import data from excel using the read_excel function
df = pd.read_excel('RhineLSTM.xlsx')
# then, we set the date of the observation as the index
df.set_index('date', inplace=True)
df.head()

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

加载后,我们可以使用 Matplotlib 库绘制数据集:

# specify columns to plot
columns = [0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
i = 1
values = df.values# define figure object and size
plt.figure(figsize=(9,40))
# plot each column with a for loop
for variable in columns:
     plt.subplot(len(columns), 1, i)
     plt.plot(values[:, variable])
     plt.title(df.columns[variable], y=0.5, loc='right')
     i += 1
plt.show()

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

绘制变量直方图通常也是一个好主意:

# histograms of the variables
df.hist(figsize=(9,18))
plt.show()

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

使用 Seaborn 库,您可以创建一个 violin 图来了解每个变量的分布:

# calculate dataset mean and standard deviation
mean = df.mean()
std = df.std()
# normalise dataset with previously calculated values
df_std = (df - mean) / std
# create violin plot
df_std = df_std.melt(var_name='Column', value_name='Normalised')
plt.figure(figsize=(12, 6))
ax = sns.violinplot(x='Column', y='Normalised', data=df_std)
_ = ax.set_xticklabels(df.keys(), rotation=90)

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

评估不同的模型

我在莱茵数据集上训练了不同类型的模型,以确定哪一个最适合:

  • 基线模型,也称为持久性模型,返回 Kaub 水位的当前变化率作为预测(本质上预测“无变化”)。这是一个合理的基线,因为莱茵河水位通常会因更广泛的天气现象而在几天内发生变化(例如,阿尔卑斯山的缓慢融化逐渐向下游移动)。
  • 您可以训练的最简单模型假定输入变量和预测输出之间存在线性关系。与更复杂的模型相比,它的主要优点是易于解释,但是它的性能只比基线网络稍好。
  • 密集网络更强大,但看不到输入变量如何随时间变化。多步密集和卷积神经网络解决了这一缺点,它采用多个时间步作为每次预测的输入。
  • LSTM 模型名列前茅,在验证和测试集上具有较低的平均绝对错误率。

创建这个图表所需的 Python 代码对于这个博客来说太长了,但是你可以在这里访问它,应用于不同的数据集。

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

LSTM 回归模型

LSTM 网络是一种可以学习长数据序列的递归神经网络。它们不是神经元,而是由通过层相互连接的记忆块组成。内存块包含管理其状态和输出的门(输入、遗忘、输出),使其比典型的神经元更聪明。

它们在学术界被广泛用于预测河流高度,并被证明在某些情况下优于经典水文模型。

让我们从本文开头的代码重新开始加载 Rhine 数据库:

import datetime
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import joblib# first, we import data from excel using the read_excel function
df = pd.read_excel('RhineLSTM.xlsx', sheet_name='Detailed4_MAIN’)
# then, we set the date of the observation as the index
df.set_index('date', inplace=True)

数据准备

要构建正常运行的 LSTM 网络,第一步(也是最困难的一步)是准备数据。

我们将把问题框定为根据今天和前 6 天的天气和瑞士上游流量(backward_steps = 7)预测今天 Kaub 水位(t)的变化率。

使用 Scikit-Learn 库中的 StandardScaler()函数对数据集进行标准化。对于数据帧的每一列,该列中的每个值减去平均值,然后除以整列的标准偏差。对于大多数机器学习模型来说,这是一个非常普通的步骤,并允许整个网络更快地学习(下面将详细介绍)。

然后,数据帧通过一个转换函数。对于每一列,我们为前 7 天的值创建一个副本(15 * 7 = 120 列)。得到的数据帧的形状是 7506 行×120 列。

很多代码都是受这篇精彩的博客文章的启发。

# load dataset
values = df.values
# ensure all data is float
values = values.astype('float32')
# normalise each feature variable using Scikit-Learn
scaler = StandardScaler()
scaled = scaler.fit_transform(values)
# save scaler for later use
joblib.dump(scaler, 'scaler.gz')# specify the number of lagged steps and features
backward_steps = 7
n_features = df.shape[1]# convert series to supervised learning
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
     n_vars = 1 if type(data) is list else data.shape[1]
     df = pd.DataFrame(data)
     cols, names = list(), list()
     # input sequence (t-n, ... t-1)
     for i in range(n_in, 0, -1):
          cols.append(df.shift(i))
          names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
     # forecast sequence (t, t+1, ... t+n)
     for i in range(0, n_out):
          cols.append(df.shift(-i))
          if i == 0:
                names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
          else:
               names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
      # put it all together
      agg = pd.concat(cols, axis=1)
      agg.columns = names
      # drop rows with NaN values
      if dropnan:
          agg.dropna(inplace=True)
      return agg# frame as supervised learning
reframed = series_to_supervised(scaled, backward_steps, 1)

定义训练和测试数据集

我们必须将准备好的数据帧分成训练和测试数据集,以便对我们的结果进行公正的评估。训练数据集代表 80%的值,我们将使用剩余的 20%进行评估。当我们处理按时间顺序排列的数据时,打乱数据集的顺序是一个非常糟糕的主意,所以我们让它保持原样。接下来,我们将训练和测试数据集重塑为三维,以备后用。

# split into train and test sets
values = reframed.values
threshold = int(0.8 * len(reframed))
train = values[:threshold, :]
test = values[threshold:, :]
# split into input and outputs
n_obs = backward_steps * n_features
train_X, train_y = train[:, :n_obs], train[:, -n_features]
test_X, test_y = test[:, :n_obs], test[:, -n_features]
print(train_X.shape, len(train_X), train_y.shape)
# reshape input to be 3D [samples, timesteps, features]
train_X = train_X.reshape((train_X.shape[0], backward_steps, n_features))
test_X = test_X.reshape((test_X.shape[0], backward_steps, n_features))
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)

拟合模型

最后,我们能够适应我们的 LSTM 网络。多亏了 TensorFlow/Keras 库,这只需要几行代码。我选择了 64 个内存块,批量大小为 72。我使用 Adam 优化算法,它比经典的梯度下降法更有效。

# design network
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.LSTM(64, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(tf.keras.layers.Dense(1))
model.compile(loss='mae', optimizer='adam')
# define early stopping parameter
callback = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3)
# fit network
history = model.fit(train_X, train_y, epochs=25, callbacks=[callback], batch_size=72, validation_data=(test_X, test_y), verbose=2, shuffle=False)
# plot history
plt.figure(figsize=(12, 6))
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.ylabel('mean absolute error [Kaub, normalised]')
plt.legend()
plt.show()

模型结果

模型拟合后,我们可以启动预测并反转比例以获得最终结果。然后,我们可以计算模型的误差分数。在这里,该模型实现了 6.2 厘米的均方根误差(RMSE),这是很好的,但可能还可以改进。

# make a prediction
yhat = model.predict(test_X)
test_X = test_X.reshape((test_X.shape[0], backward_steps*n_features))
# invert scaling for forecast
inv_yhat = np.concatenate((yhat, test_X[:, -(n_features - 1):]), axis=1)
inv_yhat = scaler.inverse_transform(inv_yhat)
inv_yhat = inv_yhat[:,0]
# invert scaling for actual
test_y = test_y.reshape((len(test_y), 1))
inv_y = np.concatenate((test_y, test_X[:, -(n_features - 1):]), axis=1)
inv_y = scaler.inverse_transform(inv_y)
inv_y = inv_y[:,0]
# calculate RMSE
rmse = np.sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)

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

我还检查了该模型在极端天气事件中的性能,例如 2016 年 5 月至 6 月在欧洲记录的大范围洪水,仅在巴伐利亚就造成了超过 10 亿欧元的损失。该模型通常能够跟踪水位的上升,但是在两个特定的高峰期间(一个在 4 月中旬,另一个在 6 月初),它给出的数字很低。

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

经过训练的 LSTM 网络在 2019 年 5 月的阿克塞尔风暴期间也表现良好,该风暴导致连续两天水位高度快速上升超过 1 米。然而,当洪水达到峰值时,它再次给出了比实际数字略低的估计。

在这两种情况下,我怀疑低的数字是由于一个主要的莱茵河支流,摩泽尔河,它被排除在分析之外,因为缺乏可靠的气象数据。摩泽尔河在考布以北的科布伦茨与莱茵河汇合。这是未来可能改进的领域。

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

模型性能

寻找最佳超参数是机器学习中最重要(也是最耗时)的任务之一。为了找到最佳设置,我运行了许多不同版本的网络:

  • *预处理:*对于那些熟悉 Scikit-Learn 的人来说,我发现 StandardScaler()比 MinMaxScaler()更适合莱茵数据集,后者将所有值归一化到 0 到 1 之间。使用 MinMaxScaler()对值进行归一化会产生不稳定的性能。快速浏览一下本文开头绘制的直方图可以发现,大多数莱茵输入变量遵循高斯分布,因此这是合乎逻辑的。
  • *倒退:*我发现 7 天是天气分析的合理时间框架,并给出了水从阿尔卑斯山流下所需的时间。增加超过这个数字的步骤对模型的性能没有有意义的改进。
  • *神经元:*由 16 个记忆细胞组成的小型网络表现不佳,但由 32 个或更多神经元组成的大型网络则相当相似。我为我的模型选择了 64 个记忆单元。

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

  • Epochs: 一旦算法遍历数据集 25 次,我就选择停止训练。超过这个阈值,性能开始趋于平稳,模型开始过度适应训练集。

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

我已经在网上部署了这个模型的一个版本。其性能可以在这里进行监控。

我将在未来通过添加/删除变量和进一步调整超参数来尝试改进这个模型。如果你有任何想法,不要犹豫与我联系

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

使用机器学习解决俄罗斯贸易数据中的武器扩散问题

原文:https://towardsdatascience.com/using-machine-learning-to-tackle-arms-proliferation-in-russian-trade-data-e457f44002c0?source=collection_archive---------27-----------------------

理解大数据

我们如何构建和迭代一个机器学习模型来识别俄罗斯非法武器交易的实例。

作者:埃利奥特·冈恩

数据科学团队:Sean Antosiak、Elliot Gunn、Andrew Mikol、Jason Nova

项目负责人:Elan Riznis

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

简介
在本帖中,我们描述了我们如何构建一种新颖的方法,在大型贸易数据集上使用机器学习来识别向外国军队发货的俄罗斯方,或代表俄罗斯国有军事公司行事的各方。

俄罗斯是世界第二大武器出口国。现有的大量贸易数据给武器不扩散领域的研究人员和政府机构带来了挑战和机遇。最近,我们的数据科学家团队与 DC 华盛顿州的非营利组织 C4ADS 合作,研究我们是否可以将机器学习技术应用于他们拥有的非常大的贸易数据集,并找到潜在的俄罗斯武器扩散的实例。

我们的任务是双重的:ML 解决方案在这个用例中是否可行,如果可行,我们将如何构建我们的方法?在我们的研究中,我们发现只有一个其他项目试图将机器学习技术应用于类似的用例。伦敦大学国王学院的阿尔法项目(Project Alpha)正致力于为防扩散分析师创建一个大数据平台。

我们的第一次尝试是成功的:在 8 周内,我们能够识别高风险公司,这表明数据科学是一种可行的方法,对那些从事军备控制研究和/或出口控制的人来说非常有用。

在亚马逊网络服务(AWS)工作 这是我们团队第一次接触 AWS,所以学习曲线相当陡峭。我们学习了拼花文件以及模式信息是如何工作的。我们花了一周时间探索不同的选项,包括花一些时间在 AWS Glue 上,在那里我们没有成功地编写生命周期脚本。

我们最终选择了 EMR (Spark & Livy)和 SageMaker,因为它非常适合我们的用例。我们选择这两个是因为它们在协作工作环境中易于使用,并且能够使用多个集群来处理我们的大型数据集。设置过程是实质性的,需要一些所谓的系统管理员技能。简而言之,在设置 SageMaker 笔记本之前,我们在 EMR 服务中创建了集群。我们使用 SageMaker 笔记本的根目录中的一个笔记本作为中心位置,为 conda_python3 环境 pip 安装相关的库。

数据集的样本使用 Dask(拼花文件)进行探索,然后使用 Spark 进行清理。

数据探索&清理 “贸易”数据集大于 170 GB,包含 2016 年至 2018 年超过 6500 万行俄罗斯进出口数据。这包括 59 家被列为我们的目标变量的公司,它们是一家大型俄罗斯武器出口商和已知代表其转运货物的各方。

读取数据集有一些重大障碍,因为它们没有数据字典;有些柱子是神秘的。一个关键的专栏,Description Good,是用西里尔语写的——虽然我们很幸运有两个团队成员具有西里尔语能力,但是这个专栏应该被翻译成在未来的模型构建迭代中允许 NLP 派生的特性。这些公司在其税号(INN)中包含名称和唯一标识符。

我们只过滤出口商,并大量使用正则表达式来清理 PySpark 中的“交易”。我们必须将一些俄语(русскийязык语)转换成等价的 unicode,以便 regex 引擎进行处理。我们要么采用我们需要的精确单词(例如,пао、ооо、оо),要么必须采用单词的变化来匹配(例如,таможни、пост等),以确保这些色谱柱达到最基本的清洁标准。我们也考虑过将正则表达式应用于描述 Good,但是试图弄清楚俄语中动词/名词/形容词变化的复杂性被证明是相当困难的。

这将交易数据集从 170+ GB 减少到了 4GB 以下,即大约 900 万行。虽然我们担心减少的太多,但很难确保我们的正则表达式在清理数据时不会太严格或太宽松。这种减少确实让我们可以使用 Pandas 进行机器学习,而不是必须学习另一种新工具,Apache Spark 的 MLlib。导致两个数据帧的清理过程的示例代码片段:

功能工程 我们主要使用 UDF 来应用 lambda 函数,以仅允许指定的值(例如,对于浮点列,nones 和 nulls 被设置为 0.0)。在第一次传递数据时,我们确实手工输入了所有的方法和参数,但是在以后的迭代中,更加程序化的方法将会更加清晰。

每一列都有字符串或非序号值,所以它们都是编码的。此处是工作流程的一个示例:

我们试图将一些领域知识融入到特征工程中。我们的研究发现,军队和安全部队一直是俄罗斯非法武器转让的主要来源,要么通过直接参与地区冲突,要么通过腐败官员与犯罪组织合作进行销售。

我们创建了一个新的专栏,试图用一个虚拟变量“active-russian-military-ties”来捕捉这一过程。该标准被定义为具有当今或最近的历史(过去 10 年)联系,包括军事干预、维和以及签署的军事或技术合作协议。我们通过自己的研究手工编码,找到了 106 个符合标准的国家。

然而,稍后运行特征重要性分析将证实我们的直觉,即唯一重要的列是商品描述列。

原型和训练
我们同时追求监督和非监督学习方法。

受监督的
以 59 家出口商的列表作为我们的目标变量,我们在选定 scikit-learn 的GradientBoostingClassifier之前尝试了几个分类器。相对于深度决策树,GBC 让我们建立一个低偏差和低方差的预测函数。Boosting 在每一步按顺序拟合树,新的树根据先前树的残差进行训练。GBC 通过使用学习率(在 0,1 之间)来衡量新树每一步的贡献,解决了过度拟合问题。

因为它在最后组合它们之前按顺序训练了许多树,所以我们可以小心地控制方差并避免过度拟合,这与拟合单个深度决策树不同。决策树也是非参数化的,这对我们的数据集特别有用,因为我们的很多列都无法识别,因此无法与领域知识联系起来。

我们最初的迭代严重依赖于一个特性,所以我们删除了它并重新训练了模型。为了解决高度不平衡的类别,我们对数据集的子集进行了训练,其中我们分别对 25,000 和 250,000 个已知和非出口商进行了采样(这个 1:10 的比例最适合我们的模型)。我们的指标针对召回进行了优化,因为由于法律分歧,过度识别合法交易(误报)比错过非法交易(误报)要好。监督方法产生了 50 个预测的旅馆,它们不包含在 59 个原始列表中。我们有一种感觉,在俄罗斯注册网站上查找旅馆后,我们已经获得了一个很好的基线,其中许多旅馆都有与军事有关的描述。

无监督的 我们使用 scikit-learn 的最近邻的库,它实现了无监督的最近邻学习。

在最近邻算法中,参数是 n_neighbors 和使用的算法类型。BallTree 被用于该算法,因为它用于“快速广义 N 点问题”n_neighbors 用于对簇中的邻居进行分组。选择 10,000 这个数字来产生一个预测集,这个预测集足够宽,可以确定一个已知出口商的内部贸易交易的分组,并使用该交易来识别其他感兴趣的公司。在 37,467 个已知的 INN 预测器行中,前 1000 行用于构建 10,000,000 个邻域的预测数据框架。该数据帧然后过滤掉已知的 INN 邻居,并且对结果进行值计数以发现新的潜在 INN。在查看了最初的结果后,这种方法的成功被认为是不确定的。

方法 我们在方法中做了几个假设:

  • 非法和合法武器出口商在其贸易交易中表现出相似的特征(例如,在贸易伙伴、空间、货物、数量方面)。我们的目标变量是俄罗斯 59 家已知武器出口商的名单。使用这份合法出口商名单是了解非法武器出口商如何也出现在贸易数据中的一种方式。由于我们实际上不知道非法出口商和合法出口商有什么不同,这可能是一个巨大的飞跃。
  • 交易数据包含准确和真实的信息。更有可能的情况是,非法武器出口商混淆了他们的路线、出口目的地、货物数量,甚至货物实际上是什么。一个众所周知的例子是俄罗斯武器经纪人 Victor Bout,他利用许多幌子公司,通过飞机运输武器,并重新注册飞机,试图通过合法和非法手段逃避管制。其他出口商可能会采取迂回路线前往其真正的目的地,在空壳公司之间转移货物,这些过境国或公司不会被数据捕获。

吸取教训&展望未来

我们有 8 周的时间来学习新工具,并为 C4ADS 的项目利益相关者提供概念证明。听说我们的基线结果是进一步探索的有效 MVP,并得到了利益相关方的积极认可,我们非常兴奋。

  • 首先,文档是至关重要的:其他团队以前曾与他们合作应对这一挑战,但是没有任何文档(特别是关于架构设置),我们花了大量时间尝试不同的设置,然后选择了最适合项目独特需求的设置。为此,我们确保尽早并经常记录文档,并传递一份广泛详细的 22 页文档,以确保可重复性,并使下一个团队能够更快地发展。
  • 第二,项目管理让我们走上正轨:我们一直作为一个远程团队工作,并且能够从 0 到一个有希望的基线结果如此之快,因为我们有一个了不起的项目经理(感谢 Elan!)与我们紧密合作,跨越所有时区。我们每天站立,提交定期发布的画布,评估里程碑,并定期与涉众进行视频通话,以更好地理解业务问题和期望。
  • 结果,我们很早并且经常迭代:有了这个清晰的工作结构,并且我们的项目经理的焦点是在第 12 周交付 MVP,我们被鼓励尽早尝试,抛弃那些很快被证明没有结果的工具或工作流。我们无法在短时间内对关键的西里尔列进行超参数调整或 NLP,但我们很高兴我们的文档和基线结果将使下一个团队能够推动机器学习在这一领域的发展。

特别感谢 C4ADS 给我们这个难以置信的机会来解决这个新奇的数据科学问题。

使用 MALLET LDA 了解玩家讨厌神奇宝贝剑/盾的原因

原文:https://towardsdatascience.com/using-mallet-lda-to-learn-why-players-hate-pokémon-sword-shield-23b12e4fc395?source=collection_archive---------23-----------------------

简单介绍 MALLET LDA 如何用于主题模型,解释玩家不喜欢神奇宝贝剑/盾的原因。

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

Unsplash 上由 Kamil S 拍摄的照片

任天堂 Switch 最新的神奇宝贝游戏大张旗鼓地推出了,承诺用令人惊叹的视觉效果和开放世界的冒险让粉丝们惊叹不已。尽管受到了评论家的赞扬,但这场比赛还是让大多数玩家失望了。在这篇文章中,我将带你了解我如何使用槌潜狄利克雷分配(LDA) 来找出玩家不喜欢这个游戏的关键原因。

数据收集

我用 BeautifulSoup 从 Metacritic 上报废了 Pokémon 用户评论(我已经在这里详述了步骤)。Metacritic 评论有评级分数,表明用户对游戏的总体评价。这让我们的生活更轻松,因为我们不需要运行一个单独的情绪分析来寻找负面评论。

我总共收集了 2019 年 11 月 15 日至 28 日发布的 3,261 条用户评论。数据集包含以下特征:

  • 姓名:审核人的用户名
  • 日期:审核日期
  • 评分:从 0 到 10 的数字分数,给出游戏的总体评价。较高的分数意味着更积极的体验。
  • 评审:实际评审文本

包装

以下软件包用于数据清理和分析:numpy、pandas、regex、string、nltk、langdetect、sklearn、spaCy、gensim、pprint(可选)、matplotlib 和 collections。

你还需要安装 gensim 的 MALLET LDA 的包装器,如果你还没有的话。

导入日志记录也是可选的,但这是一个最佳实践,因为它使您能够了解模型运行时幕后发生的事情。

import numpy as np 
import pandas as pdimport re 
import stringimport nltkfrom langdetect import detectfrom sklearn.feature_extraction.text import CountVectorizer import spacy
from spacy.lang.en.stop_words import STOP_WORDSimport gensim
from gensim import corpora, models, matutils
from gensim.models import CoherenceModelimport os
from gensim.models.wrappers import LdaMalletmallet_path = '/Users/adelweiss/mallet-2.0.8/bin/mallet'from pprint import pprint #optionalimport matplotlib.pyplot as plt
%matplotlib inlinefrom collections import Counterimport logging #optional
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

特征工程

  1. 给评论贴上正面、负面或褒贬不一的标签。Metacritic 将低于 5 的分数视为负面,将 5 到 7 的分数视为混合分数,将高于 7 的分数视为正面。在下面的代码中,我定义了一个函数来根据这些规则标记评论。
def sentiment(x):
    if x > 7:
        return 'positive'
    if x < 5:
        return 'negative'
    else: return 'mixed'df['sentiment'] = df['rating'].apply(lambda x:sentiment(x))

数据清理

  1. **删除重复的用户评论。**一些用户对《神奇宝贝之剑》和《盾牌》发表了相同的评论(这是意料之中的,因为这两款游戏在内容上仅略有不同)。
df.drop_duplicates(subset='name', keep = 'first', inplace = True)

2.过滤掉非英文评论。 为了做到这一点,我使用了 langdetect 包,它依赖于 Google 的语言检测库,支持 55 种语言。 detect 函数读取文本输入,计算文本使用支持语言的概率,并返回概率最高的语言。

def language_detection(x): 
 result = detect(x)
 if result == 'en':return x 
 else: return np.NaN 

df['review'] = df['review'].apply(lambda x:language_detection(x))

3.删除停用词。 我结合了 nltk 和 spaCy 的停用词,加入了‘游戏’、‘口袋妖怪’、‘神奇宝贝’等自定义停用词。

在检查每个单词的评论频率后添加自定义停用词(例如,频率为 5 表示该单词出现在 5 次评论中。在第 6 步中会有更多的介绍。).自定义停用词出现在大多数评论中。它们在评论中的高频率意味着它们对主题建模没有用。因此,我在后续运行中删除了它们,以提高模型性能。

该代码创建了一个停用词列表。这些词将在下一步从评论中删除。

nltk_stop_words = nltk.corpus.stopwords.words('english')stop_words = list(STOP_WORDS)
stop_words.extend(['game','pokemon','pokémon']) 
### these are common words that appear in almost all reviewsfor word in nltk_stop_words:
 if word in stop_words: continue
 else: stop_words.append(word)

4.**文字清理。**这是主要使用正则表达式的标准文本清理步骤列表:

  • 将所有单词转换成小写
  • 从列表中删除单词(我们将使用它来删除停用词,以及出现在少于 4 条评论中的罕见单词)
  • 删除数字
  • 删除标点符号
  • 替换单词(标准化我注意到的拼写不一致,如“游戏狂”和“游戏怪胎”)
  • 移除“\r”字符串文字
  • 从单词中去除多余的空格(即一行中有两个空格),这是由删除数字、标点符号和字符串文字的函数产生的
def make_lower(text):
 return text.lower()def remove_words(text,wordlist):
 for word in wordlist:
 if word in text.split():
     text = re.sub(r'\b{}\b'.format(word), '', text) 
 return textdef remove_digits(text):
 return re.sub('\d', ' ', text)def remove_punctuation(text):
 text = re.sub('[%s]' % re.escape(string.punctuation), ' ', text) 
 return re.sub(r'[^\w\s]', ' ', text)def strip_extraspace(text):
 return re.sub('\s\s+',' ', text)def replace_word(text,word,replacement):
 return text.replace(word,replacement)def remove_r(text):
 return text.replace('\r',' ')
#df['review'] = df['review'].apply(lambda x:remove_punctuation(x))def clean_text(text):
 text = make_lower(text)
 text = remove_punctuation(text)
 text = remove_digits(text)
 text = replace_word(text,'game freak','gamefreak') 
 text = replace_word(text, 'game play', 'gameplay')
 text = remove_words(text,stop_words)
 text = remove_r(text)
 text = strip_extraspace(text)
 return textdf['review'] = df['review'].apply(lambda x:clean_text(x))

5.词汇化。我使用 spaCy 的词类标注分类器来减少对名词和动词的评论。只使用名词和动词更容易辨别主题是关于什么的。

下面的代码遍历评论中的每个单词,并返回标记为“名词”或“动词”的单词。它会跳过已被词条化为“-PRON-”的单词(当单词是代词时,spaCy 会返回这个词),以及停用词列表中已被词条化的单词。

sp = spacy.load('en_core_web_sm')def lemmatize_words(text, allowed_postags=['NOUN', 'ADJ', 'VERB', ‘ADV’]):
 text = sp(text)
 lemmed_string =''
 for word in text:
     if word.pos_ in allowed_postags:
         if word.lemma_ == '-PRON-' or word.lemma_ in stop_words: 
 ### skip words that are not in allowed postags or becomes a    stopword when lemmatised 
             continue 
         else: lemmed_string = lemmed_string+' '+word.lemma_
     return lemmed_string.lstrip()df['review'] = df['review'].apply(lambda x:lemmatize_words(x, allowed_postags=['NOUN', 'VERB']))

6.**去除生僻字。**没有出现在大量评论中的罕见单词对于主题建模也是无用的。与停用词一样,我删除了它们以提高模型性能。

这段代码计算一个单词在评论中出现的次数,并将出现次数少于 4 次的评论添加到一个列表中。

word_frequency = Counter()for text in df.review:
 text = text.split()
 word_frequency.update(set(text))rare_words = []for key, value in word_frequency.items():
 if value < 4:
 rare_words.append(key)df['review'] = df['review'].apply(lambda x:remove_words(x,rare_words))

7.仅选择负面评论,并创建令牌。最后,由于我们只对负面评论感兴趣,我根据评论的情感标签过滤了评论,并应用 sklearn 的计数矢量器来创建单词标记。

CountVectorizer 的 token_pattern 参数指定只包含至少 3 个字符的单词。我的假设是 1 和 2 个字母的单词信息不多,对主题建模用处不大。另外, ngram_range 参数被设置为(1,2)以包含二元模型作为标记。

negative = df[df['sentiment']=='negative']vectorizer = CountVectorizer(stop_words=stop_words, ngram_range = (1,2), token_pattern="\\b[a-z][a-z][a-z]+\\b") 

为 MALLET LDA 奠定基础

要使用 MALLET LDA,我们需要使用矢量器拟合和转换数据,并创建模型需要的一些变量。首先,我们将计数矢量器与负面评论相匹配。然后,我们创建一个文档-单词矩阵,并将其从稀疏矩阵转换为 gensim 单词语料库。接下来,我们创建 word2idid2word 变量,将单词与其数字令牌 id 进行匹配,反之亦然,并将这些变量保存到字典中。

vectorizer.fit(negative.review)
doc_word = vectorizer.transform(negative.review).transpose()corpus = matutils.Sparse2Corpus(doc_word)word2id = dict((v, k) for v, k in vectorizer.vocabulary_.items())
id2word = dict((v, k) for k, v in vectorizer.vocabulary_.items())dictionary = corpora.Dictionary()
dictionary.id2token = id2word
dictionary.token2id = word2id

选择主题的数量

为了选择主题的数量,我们将计算每个指定数量的主题的一致性分数。

连贯性分数通过检查每个主题的热门单词之间的语义相似度来评估主题的质量。分数越高,模型越好。

为了计算每个模式的一致性分数,我使用了下面的代码,我在第 17 节的中找到了。代码循环遍历一系列数字,代表应用于模型的主题数量,计算每个模型的一致性分数,保存分数并绘制它们。

def compute_coherence_values(dictionary, corpus, texts, limit, start=2, step=3):
    """
    Compute c_v coherence for various number of topics

    Parameters:
    ----------
    dictionary : Gensim dictionary
    corpus : Gensim corpus
    texts : List of input texts
    limit : Max num of topics

    Returns:
    -------
    model_list : List of LDA topic models
    coherence_values : Coherence values corresponding to the LDA model with respective number of topics
    """
    coherence_values = []
    model_list = []
    for num_topics in range(start, limit, step):
        model = gensim.models.wrappers.LdaMallet(mallet_path, corpus=corpus, num_topics=num_topics, id2word=id2word)
        model_list.append(model)
        coherencemodel = CoherenceModel(model=model, texts=texts, dictionary=dictionary, coherence='c_v')
        coherence_values.append(coherencemodel.get_coherence())

    return model_list, coherence_values# Can take a long time to run.
model_list, coherence_values = compute_coherence_values(dictionary=id2word, corpus=corpus, texts=data_lemmatized, start=2, limit=40, step=6)# Show graph
limit=40; start=2; step=6;
x = range(start, limit, step)
plt.plot(x, coherence_values)
plt.xlabel("Num Topics")
plt.ylabel("Coherence score")
plt.legend(("coherence_values"), loc='best')
plt.show()

根据图表,2 或 5 是可供选择的好数字,因为它们的一致性得分最高。然而,选择 2 个主题可能会过于简单,所以我们选择 5 个。

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

描述 MALLET LDA 在多个主题上的一致性分数的图表

探索主题

为了查看与每个主题最相关的前 10 个单词,我们重新运行指定 5 个主题的模型,并使用 show_topics。你可以使用一个简单的 print 语句来代替,但是 pprint 让事情更容易阅读。

ldamallet = LdaMallet(mallet_path, corpus=corpus, num_topics=5, id2word=id2word, random_seed = 77)# Show Topics
pprint(ldamallet.show_topics(formatted=False))

下面是每个话题的热门词汇,以及我给它们的标签。大部分话题都和游戏性有关,除了最后一个。因为我们只看了负面评论,我们可以假设这些是问题领域。

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

表格描述了每个主题的前 10 个单词,以及建议的主题标签

检查完评论后,下面是每个主题的快速概述:

  • **时间:**上场时间太短。许多玩家在 10 到 25 小时内完成了游戏
  • 体验:故事缺乏,整体游戏太简单。此外,大肆宣传的野生区域缺乏活力,而且大多是空的。
  • **视觉效果:**动画、图形和模型缺乏质量和质感。对视觉效果的期望非常高,因为游戏狂认为这是从游戏中删除这么多神奇宝贝的原因。
  • 内容:玩家经常将 switch 游戏与 3DS 游戏相比较,后者有更多的功能、内容和可用的神奇宝贝。
  • 开发者:玩家们感觉被 Game Freak 背叛了,因为它并没有兑现其承诺的惊人的视觉效果。他们觉得《游戏怪胎》欺骗了他们,而且《神奇宝贝》的删减是不合理的。

结论

LDA 是一种在文本中寻找主题的好方法,尤其是当它用于探索性目的时。我认为 MALLET LDA 很好地概括了为什么玩家讨厌这个游戏。我快速浏览了一下正面评论,发现了两个广泛的话题:游戏性和正面感受。也许,这可能是在 switch 上开始玩神奇宝贝的新玩家(因此使用 Let’s Go 作为基准)与更习惯复杂游戏的经验丰富的玩家之间的情况。

*代码可在此处找到 *

在 Python 中使用地图和过滤器

原文:https://towardsdatascience.com/using-map-and-filter-in-python-ffdfa8b97520?source=collection_archive---------11-----------------------

如何使用 python 中内置的地图和过滤功能

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

凯文·Ku 在 Unsplash 上的照片

在本教程中,我们将学习何时以及如何使用 Python 中内置的地图和过滤功能。

地图功能

假设您想使用已经有的列表创建一个列表。这意味着您希望使用现有的列表,对每个元素应用某种操作或函数,并使用这些输出来创建一个新的列表。

例如,如果我们有一个数字列表,我们想创建一个包含它们的方块的新列表。一种方法是使用 for 循环遍历数字列表,并应用一个函数返回每个数字或元素的平方。当我们遍历列表时,我们可以在新列表中添加或附加平方值。

让我们看看如何在代码中做到这一点:

我们有一个数字列表, num_list ,我们想要创建一个新列表, num_list_squared ,它包含了 num_list 的平方。我们使用 for 循环遍历 num_list ,并将每个数字的平方或 num 添加到我们的 num_list_squared 列表中。

另一种实现方法是使用名为 map 的内置 python 函数。

map 函数接受两个参数:**我们想要应用的函数和我们想要应用它的 iterable 对象或序列(比如本例中的 list)。**换句话说,map 函数将这个函数映射或应用到我们传入的序列的每个元素。

我们将用于地图功能的格式如下:

map(要应用的函数,要应用的元素序列)

这个 map 函数将返回一个 map 对象,它是一个迭代器。如果我们想从这个 map 对象创建一个列表,我们需要将 map 对象传递给内置的 list 函数,如下所示:

列表(映射(功能,顺序))

让我们看看如何使用内置的 map 函数来完成上面的代码:

请记住,我们可以将 map 函数应用于任何可迭代对象或序列中的每个元素,而不仅仅是列表。

那么让我们来分析一下这行代码中发生了什么:

num_list_squared = list(map(squared, num_list))

map 函数从 num_list 中获取第一个元素,即 1,并将其作为参数传递给平方函数(因为我们将该函数作为第一个参数传递给 map 函数)。然后, squared 函数返回 1 的平方,也就是 1,它被添加到我们的地图对象中。然后,map 函数从 num_list 中取出第二个元素,即 2,并将其作为参数传递给 squared 函数。 squared 函数返回 2 的平方,也就是 4,然后它被添加到我们的地图对象中。在它完成了对 num_list 元素的遍历,并且我们的其余平方数被添加到地图对象之后, list 函数将这个地图对象转换成一个列表,并且该列表被分配给变量 num_list_squared

使用λ表达式

我们可以通过传入一个 lambda 表达式作为我们的函数来进一步缩短代码:

如果你不熟悉 lambda 表达式,你可以看看这个教程:

[## Python 中的 Lambda 表达式

如何用 python 写匿名函数

towardsdatascience.com](/lambda-expressions-in-python-9ad476c75438)

:我们传入 map 的函数可以是 python 中的内置函数。例如,如果我们有一个字符串列表,我们想要创建一个包含这些字符串长度的新列表,我们可以像下面这样传递内置的 len 函数:

list(map(len, list_of_strings))

了解更多关于可迭代、迭代器和迭代的知识:

[## Python 中的迭代器和迭代器

Python 中的可迭代对象、迭代器和迭代

towardsdatascience.com](/iterables-and-iterators-in-python-849b1556ce27)

过滤功能

同样,假设我们想用一个已经有的列表来创建一个列表。但是这一次我们希望我们的新列表只包含满足给定条件的元素。例如,我们有一个数字列表,我们想创建一个只包含偶数的新列表。我们可以使用 for 循环来完成这项任务,如下所示:

我们有一个数字列表, list_of_nums ,其中包含数字 1、2、3、4、5 和 6。我们想要创建一个新的数字列表, list_of_even_nums ,它只包含来自 list_of_nums 的偶数。所以我们创建了一个函数, is_even ,它接受一个输入,如果输入是偶数,则返回 True,否则返回 False。然后,我们创建了一个 for 循环,该循环遍历 list_of_nums ,并通过将该元素传递给 is_even 函数来检查列表中的每个数字是否都是偶数。如果 is_even 函数返回 True,则该数字被追加到 list_of_even_nums 中。如果 is_even 返回 False,则该数字不会追加到 list_of_even_nums 中。

另一种实现方法是使用名为 filter 的内置 python 函数。 filter 函数接受两个参数:检查特定条件的函数和我们想要应用它的序列(比如本例中的列表)。filter 函数从我们的列表中取出每个元素,并将其传递给我们给它的函数。如果将该特定元素作为参数的函数返回 True,filter 函数将把该值添加到 filter 对象中(然后我们可以从该对象中创建一个列表,就像我们对上面的 map 对象所做的那样)。如果函数返回 False,那么该元素将不会被添加到我们的 filter 对象中。

换句话说,我们可以认为过滤函数是基于某种条件过滤我们的列表或序列。

我们用于过滤功能的格式如下:

过滤器(检查条件、我们想要应用的元素序列的函数)

这个过滤函数将返回一个过滤对象,它是一个迭代器。如果我们想从这个过滤器对象创建一个列表,我们需要将过滤器对象传递给内置的 list 函数(就像我们对 map 对象所做的那样),如下所示:

列表(过滤(功能,顺序))

现在让我们使用内置的过滤函数创建与上面相同的列表:

filter 函数从 list_of_nums 中获取第一个元素,即 1,并将其作为参数传递给 is_even 函数(因为我们将该函数作为第一个参数传递给 filter 函数)。然后 is_even 函数返回 False,因为 1 不是偶数,所以 1 没有被添加到我们的过滤器对象中。然后,filter 函数从 list_of_nums 中获取第二个元素,即 2,并将其作为参数传递给 is_even 函数。 is_even 函数返回 True,因为 2 是偶数,因此 2 被添加到我们的过滤器对象中。在遍历完 list_of_nums 中的其余元素并且将其余偶数添加到我们的 filter 对象中之后, list 函数将这个 filter 对象转换为一个列表,并且该列表被分配给变量 list_of_even_nums

使用λ表达式

我们可以通过传入一个 lambda 表达式作为我们的函数来进一步缩短代码:

如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。

[## 通过我的推荐链接加入媒体——卢艾·马塔尔卡

阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…

lmatalka90.medium.com](https://lmatalka90.medium.com/membership)

结论

在本教程中,我们学习了 python 内置的地图和过滤函数是如何工作的。我们也看到了一些使用它们的例子。最后,我们看到了 lambda 表达式如何作为参数传递给这些函数。

利用数学操纵选举

原文:https://towardsdatascience.com/using-maths-to-rig-elections-e4e88b694c60?source=collection_archive---------51-----------------------

为什么你需要知道本福特定律

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

唐在 Unsplash 上的照片

声明:我不赞成用数学来操纵选举!但是为了争论的缘故…

想象一下,你不是一个道德高尚的人,你的任务是编造一组数字。你需要对这些数据进行加工,让它们代表一个有利的结果,同时不引起任何可能碰巧看到它们的人的怀疑。

你决定随机选择它们——或者更好——使用随机数发生器来消除你可能无意中带来的任何无意识的偏见。带着肆无忌惮的窃笑,你用更合适的结果取代了令人不快的结果。销毁证据后,那天晚上你上床睡觉,安全地知道你不可能被抓住。

不幸的是,我可能有些消息要告诉你。

令人不安的分布

我们上面的歪门邪道的同谋者认为他们随机选择的数字将是均匀分布的,这是正确的,因为每个结果都是同样可能的,因此是无模式的。然而,他们犯了一个错误:人口、街道地址、出现在英国《金融时报》上的数字、树上的叶子数量——任何自然的数字群体——也遵循一个不太直观的规则,这需要更多的工作来伪造。

为了说明这一点,让我们以世界各国的人口为例,这些国家的人口从几千到十多亿不等。现在,让我们把所有这些数字的第一个数字切掉。像这样:

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

数据来源: Worldometer

重要的问题来了:你觉得这些前导数字会怎么分布?

认为没有一个数字会比其他任何一个数字出现得更频繁似乎是合乎逻辑的。假设有 9 个可能的数字,每个数字出现的概率应该是九分之一,对吗?让我们看看这个预测是否会在实践中得到证实:

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

现在,这有点令人惊讶——等可能性的看似直观的结论实际上是完全错误的!这种模式不仅仅在人群中观察到——你会在房价、河流长度、建筑高度中发现它…

这种现象被称为本福特定律,在任何人群中都可以发现:

  • *不受人为约束;*和
  • 跨越足够数量的数量级

违反上述第一点,电话号码必须以某些数字(区号)开头。家庭中洗衣机的数量不倾向于取大范围的值,违反了第二条(如果我冒犯了任何收藏者,对不起)。结果是这些群体不一定会表现出这种行为。

究竟为什么

为了帮助你对为什么会发生这种情况有一点直觉,让我们举一个例子。考虑一个均匀分布的人口(从 1 到某个上限的数字),观察随机选择的数字以某个数字开头的概率如何随着人口规模的增加而变化。

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

在最左边,人口仅仅是{1},因此选择一个前导数字为 1 的数字的概率是 100%。随着人口规模的增长,获得前导数字 1 的概率会降低,直到达到九分之一,约为 0.11,此时人口规模为 9,每个数字都有一个。

但是当我们开始数到十的时候——获得前导 1 的可能性又开始变大了!当我们数到 99 时,概率像以前一样降低,其中每个前导数字都有同样的可能性再次出现——然后模式重复。

我们可以观察到,当人口规模为 9、99、999 等时,每个前导数字的概率确实是相等的。但是对于所有其他的人口规模,你得到 1 的可能性比其他任何数字都大。整洁!

回到我们的世界各国人口的例子,在数字上没有模式——一些国家很大,像印度,一些国家很小,像图瓦卢。一个典型国家的人口将介于 0 和最大国家(中国,有 14 亿居民)的人口之间——随机选取一个国家的人口基本上就像从 1 到 1,400,000,000 中随机选取一个数字。如果我们上面的图表延伸到 14 亿,我们会发现领先 1 的概率是 36.5%!在此范围内,前导 1 比 2 多,前导 2 比 3 多,依此类推。这就是为什么我们在本文的第一张图中发现了这种特殊的模式。

如果我们为所有不同的前导数字绘制一条类似于上图的曲线,我们将得到如下结果:

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

我们可以看到,随着我们越往上走,我们得到它的机会越来越小。可怜的 9 号最多有九分之一的几率,1 号最差也有九分之一的几率!

如何不操纵选举

那么这又和操纵选举有什么关系呢?所有这些在现实世界中很重要的原因是,除非他们非常仔细地准备,否则虚构的数字不会遵守本福特定律——它们不会在前导数字中显示这种频率模式。这给了我们一个工具来检测那些为了欺骗人们而伪造的数字…这里有几个例子:

  • 法务会计师和审计师利用本福德定律来发现社会经济数据中的欺诈行为。过去会计丑闻的数据被发现不符合这一规则。
  • **2004 年,希腊政府承认伪造经济数据以加入欧洲货币联盟。**这种数据的欺诈性质可以从其违反本福特法则中看出。
  • **在 2009 年的伊朗选举中,**一名候选人在伊朗不同选区的票数并不遵循本福特定律。事实上,以 7 开头的数字太多了。这种反常现象发生在六个最大的投票区中的三个。

值得一提的是,单凭本福特法则不足以证明一组数字是否被伪造——就像任何统计分析一样,它只能告诉我们这些数字不太可能是真实的。尽管有这样的警告,起初似乎是一个有趣而无害的数学规则,结果却成为识别和防止犯罪和欺诈行为的强大而有用的工具。

如果你正在监狱里读这篇文章,因为你被一个特别倾向于数字的侦探抓到在你的纳税申报单上说猪肉馅饼——祝你下次好运。

更多信息和积分

Andrew Hetherington 是英国伦敦的一名见习精算师和数据爱好者。

  • LinkedIn 上与我联系。
  • 看我在 GitHub 上摆弄什么。
  • 用来制作本文情节的笔记本可以在这里找到。

图片:大本钟照片由丹尼尔·h·唐Unsplash 上拍摄。

各国人口数据来自世界人口统计。2020 年 5 月 16 日访问。

IBM 人力资源流失案例研究

原文:https://towardsdatascience.com/using-ml-to-predict-if-an-employee-will-leave-829df149d4f8?source=collection_archive---------10-----------------------

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

Unsplash:马尔泰克·比约克

预测某个员工是否会离开组织?

在任何组织中,自然减员都是一个主要问题。在培训新员工上投入的时间、金钱和精力、工作依赖性和其他因素会导致公司在员工离职时遭受巨大的整体损失。此外,自然减员会导致现有员工之间的不信任,这本身会成为组织管理的一个主要困难。

IBM 人力资源流失案例研究是一个虚构的数据集,旨在确定可能影响决定哪些员工可能离开公司的重要因素。本文提供了深入的分析和预测模型,以了解重要的因素并做出准确的预测。

目录

  1. 数据准备和理解。
  2. 特征工程。
  3. 特征选择。
  4. 模型拟合
  5. 模型比较
  6. 建议和结论。

数据准备和理解

IBM 人力资源流失案例研究可以在 Kaggle 上找到。 Python 3.3 用于分析和模型拟合。使用的 IDE 是 Spyder 3.3.3

为了正确理解数据集,让我们看看它的一些基本特性。这包括数据集的形状和数据中存在的特征/变量的类型。此外,我们还将查看丢失的值(如果有的话)。

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

数据集的形状

损耗数据集有 1470 个观察值和 35 个变量。在 35 个变量中,存在一个目标变量损耗,可能的结果。其他 34 个变量是独立变量,只有一个变量是独立变量,即员工号,它表示员工号或标识号。

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

消耗

上图显示了目标变量的分布。在总共 1470 个观察结果中,1233 个是否定的,而 167 个是肯定的。我们将在将数据分成训练测试集后处理这种不平衡。

数据集没有缺失值。因此,不需要对缺失值进行进一步处理。让我们用箱线图来观察异常值。

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

箱形图(1)

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

箱形图(2)

年龄日工资率离家距离小时工资率月工资率百分比工资率倾向于没有任何异常值。

NumCompaniesWorkedTrainingTimesLastYearYearsWithCurrManageryears incurentrole有中等数量的异常值。

月收入总工作年数年就职年晋升有大量异常值。

解决这个问题的一种方法是缩放变量,以减少其对模型的影响。Python 的 Scikit-learn 库中的 StandardScaler() 可用于此目的。

继续之前的最后一步是检查多重共线性。为此,我们绘制了一个相关矩阵。

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

相关矩阵

上图显示了独立变量之间的相关性。我们这样做是为了检查多重共线性。多重共线性遵循的经验法则是,如果相关系数® 接近 0.80 。基于此,我们确定以下变量具有高度相关性:

月收入工作级别的相关性为 0.95。这是非常高的相关性。

总工作年限工作级别的相关性为 0.78,也非常接近 0.80。

所有其他变量似乎具有小于 0.80 的相关性。

了解了数据和初步分析后,我们现在可以更深入地了解特征工程。

特征工程

特征工程是指在使用机器学习或统计建模(如深度学习、决策树或回归)创建预测模型时选择和转换变量的过程。该过程包括数据分析、应用经验法则和判断的结合。

出于模型的目的,从现有的独立变量中创建了两个特征:

整体性:

环境满意度工作参与度工作满意度、人际关系满意度四个因素,其总和即为整体满意度。最高分“16”表示完全的整体满意度,而“4”被认为是最低的整体满意度。该变量的设计基于给定的图表:

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

流失比例与整体满意度

横轴表示整体满意度,纵轴表示对应于给定水平的比例损耗。可以清楚地得出结论,随着年级的增加,自然减员的比例减少,即从 5 年级的 0.56 减少到 16 年级的 0.00。

BelowAverageIncome

另一个特性,即 BelowAverageIncome 是根据员工所在的部门、该部门的平均收入以及员工的百分比工资收入创建的。如果员工的月收入低于该部门的平均收入,且加薪百分比小于 16,则该员工的等级为 1,否则为 0,表明该员工最有可能离职。

特征选择

基于探索性数据分析和描述性统计分析,我们选择和取消某些对我们的模型没有显著贡献的变量。根据以下情况取消选择变量:

  1. 可变类型
  2. 数据点的不变性
  3. 目标变量和自变量之间的独立性
  4. 多重共线性

变量类型:

分析中不使用标称变量,因为它不为模型构建过程提供任何输入。然而,它被保留,以便识别对其进行研究的员工。

数据点的不变性:

某些变量没有任何可变性。这些变量是:

  1. 员工计数:这只是对员工的计数,其取值始终为 1。
  2. 18 岁以上:该变量描述员工是否超过 18 岁。在所有情况下,它都采用值“Yes”。
  3. 标准小时数:员工一周工作的标准小时数。它的常数值是 80

目标变量和自变量之间的独立性:

检查目标变量和自变量之间的相关性是很重要的。如果两者之间的关系不显著,就不应该选择那个自变量进行建模。

为了确定自变量和目标变量之间的相关性,我们使用卡方检验。我们设定假设:

H0:这两个变量之间没有联系

H1:变量之间有关联。

如果 p 值小于显著性水平(a)或计算值大于表中值,我们拒绝零假设。出于本研究的目的,我们选择 LOS (a)为 5%。

基于卡方检验,取消选择以下变量:

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

从模型构建中移除的变量

多重共线性:

多重共线性是指输入变量之间的强关系或相关性。如果两个变量之间的相关系数大于 0.80,则称之为多重共线性。去除这些变量是很重要的,因为这会导致模型中的方差膨胀,从而增加模型中的误差。

基于我们的分析,我们去除了以下变量:

  1. 工作级别:与月收入的相关系数为 0.95,与总工作年限的相关系数为 0.78。
  2. 总工作年数 : 0.77 与月收入的相关性。

主成分分析

对于特征提取,我们也可以使用主成分分析(PCA)。特征提取是一个降维的过程,通过该过程,原始数据的初始集合被减少到更易管理的组以便处理。

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

主成分数量对比例变化的解释

创建了两个主要组件来解释所考虑的 26 个变量的可变性。运行一个循环,从 1 到 26 取成分,并测量所解释的可变性。只有一个主成分,解释的可变性约为 68%。这在两个主成分的情况下增加到大约 99%,在三个主成分的情况下接近 99.99%。任何进一步的增加都增加了解释不明显的百分比。

模型拟合

经过预处理后,我们将数据分为训练、验证和测试数据集。从总共 1470 个观察值中,我们选择:

  1. 训练数据集的 80%观察值。
  2. 验证数据集的 14%观察值。
  3. 测试数据集的 6%观察值。

选择的算法是线性核的支持向量机*。*选择的参数如下:

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

在这种情况下,内核等重要参数选择为“线性”。 random_state 取 42 作为种子,模型拟合 1176 个观测值。

下一个过程是根据验证和测试集的拟合模型来预测值。为了计算准确度、精确度、召回率、真阳性和真阴性,我们创建了混淆矩阵。

验证集的混淆矩阵如下所示:

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

混淆矩阵(验证集)

测试集的混淆矩阵如下所示:

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

混淆矩阵(测试集)

我们现在计算 2 个数据集的准确性参数:

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

验证和测试集的评估指标

第一组信息属于验证集。我们得到的加权准确度为 1.00,精度为 1.00,召回率为 1.00。

第二组信息与测试集有关。我们得到的加权准确度为 1.00,精度为 1.00,召回率为 1.00。

我们现在来看看科恩的卡帕评分。Cohen 的 kappa 系数(κ)是一个统计量,用于测量评分者对定性(分类)项目的一致程度。一般认为这是一个比简单的百分比一致计算更稳健的方法,因为κ考虑了偶然发生一致的可能性。

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

科恩的卡帕评分

训练、验证和测试集的 Kappa 分数表明,绝对不存在偶然预测的可能性。因此,该模型的结果是完全可信的,也可用于进一步的分析。

:还使用了其他几个分类器,这里解释其中最好的一个。其他分类器包括支持向量机(RBF 核)随机森林(1000 棵树)逻辑回归

模型比较

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

如上所述,使用了几个分类器,并选择了最好的一个。然而,看看每个分类器的优点和缺点是值得的。 SVM(线性核)给出的准确率最高(100%),其次是随机森林 (87%)和 SVM *(RBF 核)。*过度拟合可能是最佳模型的一个问题,因为它可能屈服于新的数据点。此外,模型拟合需要时间。另一方面,随机森林有助于分类中的新数据点。

建议和结论

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

根据上面的图表,我们可以得出结论,股票期权级别在决定员工流失方面起着非常重要的作用。除此之外,月收入、工作满意度、工作投入度也位列前茅。另一方面,诸如绩效等级、性别、部门之类的因素往往影响不大(卡方检验也证明了这一点)。

基于整个项目,为了从模型中获得最佳输出,需要保持某些点。

人力资源部门可以关注那些在决定一个员工是否离开一个组织时起重要作用的重要变量。这些变量是:

StockOptionLevel

每月收入

工作满意度

工作投入

工作生活平衡

环境满意度

基于上述变量,人们可以清楚地注意到一种模式。员工更关心他们直接拿到手的物质物品。然后是决定员工是否会离开组织的心理变量。

因此,HR 可以关注这些方面,从员工的角度去理解。一旦遵循了这一点,被称为流失项目的项目就可以被用作保留项目。这可以极大地帮助组织。

其次,当收到新的数据集时,需要不时地调整模型。如果引入任何新的输入变量,重要的是检索参与初始研究的员工的信息。

我们因此结束了这个项目。

感谢您的阅读。我真诚地希望你觉得这篇文章很有见地,我一如既往地欢迎讨论和建设性的反馈。

给我发邮件:icy.algorithms@gmail.com

你可以在 LinkedIn 上找到我。

用 SHAP 的模型解释来理解泰坦尼克号上发生的事情

原文:https://towardsdatascience.com/using-model-interpretation-with-shap-to-understand-what-happened-in-the-titanic-1dd42ef41888?source=collection_archive---------37-----------------------

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

作者图片

注意:这是关于分析和理解泰坦尼克号数据集的两部分中的第二部分。请在这里找到第一部分。

介绍

在过去的帖子中,我对泰坦尼克号数据集进行了统计分析,以回答乘客的社会经济阶层是否对他们的生存概率有影响的问题。统计显著性检验表明,“Pclass”变量,即我用来表示社会经济地位的每个个体的类别,对人们的生存有显著影响,p 值为 2.24e-147。你可以在这里阅读的帖子。

在这篇文章中,我将建立一个机器学习模型,并用 SHAP 来解释这个模型。这将揭开泰坦尼克号上发生的一些隐藏的秘密,并帮助我们想象社会经济阶层对存活率的影响。

数据清理和预处理

让我们从查看数据集开始,看看是否需要任何数据预处理。

df = pd.read_csv('train_titanic.csv')
df.head(10)

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

作者图片

让我们看看是否有大量数据丢失:

cols = df.columns 
colours = ['darkblue', 'red'] 
sns.heatmap(df[cols].isnull(), cmap=sns.color_palette(colours))

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

作者图片

在上面的热图中,x 轴显示列名,y 轴显示行的索引。红色小条表示该行的特定列中缺少某个值。似乎“年龄”和“客舱”这两个特性缺少很多值。让我们看看百分比:

pct_list = []
for col in df.columns:
    pct_missing = np.mean(df[col].isnull())
    if round(pct_missing*100) >0:
        pct_list.append([col, round(pct_missing*100)])
    print('{} - {}%'.format(col, round(pct_missing*100)))

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

作者图片

特征“小屋”丢失了 77%的数据。所以我要去掉这个特征。然而,年龄丢失了 20%的数据。在这种应用中,年龄应该是一个重要的变量,因为它肯定影响了存活的概率(例如,老年人或儿童可能被给予优先权)。通常,我会用其他人的平均年龄来填充缺失的值。然而,在这个特定的数据集中,人们来自不同的阶层,因此将他们作为一个群体来对待并不是一个好主意。数据集具有特征“姓名”,该姓名具有人的头衔(例如“先生”、“小姐”…等)。这个标题应该是这个时代的一个重要标志。此外,我应该记住,在事件发生时(1912 年),社会经济地位影响人们的头衔,而不考虑年龄(例如,年轻的富人可以获得头衔,而同龄的普通穷人则不能)。因此,我将根据人们的头衔和级别对他们进行分组,然后将每组的平均年龄分配给每组中的缺失年龄。

# extracting the title from the name:
Title = []
for name in  df.Name:
    Title.append(name.split(",")[1].split(".")[0])

df["Title"] = Title#grouping people with pclass and title
df.groupby(["Pclass", 'Title'])['Age'].agg(['mean']).round(0)# adding the mean of the age of each group to the missing values
df["Age"] = df.groupby(["Title", "Pclass"])["Age"].transform(lambda x: x.fillna(x.mean()))

现在,我还可以删除不需要的特征,如姓名(从中提取标题之后)、机票 ID、乘客 ID。

df = df.drop(columns = ["Name"])
df = df.drop(columns = ["PassengerId"])
df = df.drop(columns = ["Ticket"])

最后一步,我将把分类特征编码成数字:

df.Sex = pd.Categorical(df.Sex)
df.Embarked = pd.Categorical(df.Embarked)df["Sex"] = df.Sex.cat.codes
df["Embarked"] = df.Embarked.cat.codes

我将从 x 数据集中删除“生存”结果变量

target = df.Survived.values
df = df.drop(columns =["Survived"])

构建线性模型

最后,我要建立模型。我将使用一个简单的逻辑回归模型,因为这里的目标是看特征如何影响结果,而不是在预测中获得高分。

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df, target, test_size=0.2, random_state=0)
from sklearn.linear_model import LogisticRegression
LR = LogisticRegression()
LR.fit(x_train, y_train)
LR.score(x_test, y_test)

该模型达到了 82%的准确率,这对于我们的目标来说是合理的。

现在,我们可以开始有趣的部分了。我将用 SHAP 来解释这个模型,看看这些特征是如何影响泰坦尼克号的发生的。

SHAP 的模型解释

SHAP 是一个伟大的模型解释工具。尽管这是一个复杂的模型,但理解起来很直观。SHAP 的目标是提供每个特征对结果变量影响的可视化。为了做到这一点,SHAP 将建立一个模型,使用所有的功能,除了一个感兴趣的功能,看看这个模型没有这个功能会怎么样。然后,它将再次构建模型,并使用该特征进行预测。那么特征的效果将是两个值之间的差。但是将特征传递给模型的顺序会影响输出(尤其是在基于树的模型中,其中模型遵循由特征排序的示意图方法)。因此,SHAP 计算了所有可能的排列,不同的特征以这些排列传递给模型。这似乎有巨大的计算成本,但 SHAP 优化了算法,使其对特定的机器学习模型更快。

让我们看看 SHAP 的地图:

import shap
explainer = shap.LinearExplainer(LR, x_train, feature_perturbation="interventional")
shap_values = explainer.shap_values(x_test)
shap.summary_plot(shap_values, x_test)shap.summary_plot(shap_values, x_train, plot_type="bar")

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

作者图片

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

作者图片

正如我进行的统计推断所预期的,Pclass 对乘客的存活率有显著的影响。这是仅次于“性”的第二大特征。我们从上面的图中看到,对应于 1 级(较富裕的人)的 Pclass 的低值(蓝色)对人的存活率有积极影响,而对应于第三级的较高值(红色)对存活率有消极影响。我们还可以看到,“性别”是最重要的特征,表明作为“女性”(蓝色)对存活率有积极的影响。特征“年龄”也显示较低的值(蓝色)对存活率有积极的影响。

让我们来看看可变票价,即每个人支付的票价。这个变量应该是对人们财富的连续描述:

shap.dependence_plot("Fare", shap_values, x_test)

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

作者图片

我们看到,人们付出的多少和他们生存的机会之间存在线性关系。他们越富有,生存的可能性就越大。

最后,让我们更仔细地看看几位乘客:

shap.force_plot(explainer.expected_value, shap_values[0,:], x_test.iloc[0,:], link="logit")

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

作者图片

那是一个没有活下来的乘客的情节。情节表明,他的“性别”(作为男性)和他的“阶级”(处于第三阶级)正在降低他的存活率。该图还显示,兄弟姐妹的数量(“SibSp”)为 0 会略微增加他的机会。似乎没有家人陪伴独自在船上的人能够不受干扰地跑得更快。

让我们来看看一个幸存的人:

shap.force_plot(explainer.expected_value, shap_values[3,:], x_test.iloc[3,:], link="logit")

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

作者图片

不出所料,她是一班付了高价车费的女生。这给了她更高的生存机会。此外,她有一个兄弟姐妹,而且年纪大了,这也减少了她的机会。

这些结果可以用下面的力图来总结:

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

作者图片

该图显示了积极(蓝色)或消极(红色)影响每位乘客存活率的特征。

结论

根据我进行的统计分析和模型解释,我可以对泰坦尼克号上发生的事情做出以下归纳。当船开始下沉时,富人有优先离开船的权利。那些兄弟姐妹较少的人速度更快,因为他们不必寻找他们的家人。当他们发现救生艇数量有限时,他们决定优先考虑儿童和妇女。所以优先顺序如下:有钱的女人和孩子,有钱的男人,然后是其他人。非常有趣的是,这种见解是如何从数据集中完全提取出来的。

请在这里找到我的笔记本。你必须下载并运行笔记本来载入 SHAP 的地图,因为它们依赖于 Javascript

参考文献

在本章中,我们将深入探讨基于块匹配的全景图像拼接技术,这是一种广泛应用于计算机视觉和图像处理领域的技术。在深度学习和机器学习的背景下,这种方法的实现与整合显得尤为重要,因为它们能够提升图像处理的效率和精度。下面,我们将会详细阐述相关知识点。 我们要了解什么是全景图像拼接。全景图像拼接是一种将多张有限视角的图像合并成一个宽视角或全方位视角图像的技术,常用于虚拟现实、地图制作、监控系统等领域。通过拼接,我们可以获得更广阔的视野,捕捉到单个图像无法覆盖的细节。 块匹配是全景图像拼接中的核心步骤,其目的是寻找两张图片中对应区域的最佳匹配。它通常包括以下几个关键过程: 1. **图像预处理**:图像的预处理包括灰度化、直方图均衡化、降噪等操作,以提高图像质量,使匹配更加准确。 2. **特征提取**:在每张图像上选择特定区域(块)并计算其特征,如灰度共生矩阵、SIFT(尺度不变特征变换)、SURF(加速稳健特征)等,这些特征应具备旋转、缩放和光照不变性。 3. **块匹配**:对于每一张图像的每个块,计算与另一张图像所有块之间的相似度,如欧氏距离、归一化互信息等。找到最相似的块作为匹配对。 4. **几何变换估计**:根据匹配对确定对应的几何关系,例如仿射变换、透视变换等,以描述两张图像之间的相对位置。 5. **图像融合**:利用估计的几何变换,对图像进行融合,消除重叠区域的不一致性和缝隙,生成全景图像。 在MATLAB环境中实现这一过程,可以利用其强大的图像处理工具箱,包括图像读取、处理、特征检测和匹配、几何变换等功能。此外,MATLAB还支持编程和脚本,方便算法的调试和优化。 深度学习和机器学习在此处的角色主要是改进匹配过程和图像融合。例如,通过训练神经网络模型,可以学习到更具鲁棒性的特征表示,增强匹配的准确性。同时,深度学习方法也可以用于像素级别的图像融合,减少拼接的失真和不连续性。 在实际应用中,我们需要注意一些挑战,比如光照变化、遮挡、动态物体等,这些因素可能会影响匹配效果。因此,往往需要结合其他辅助技术,如多视图几何、稀疏重建等,来提高拼接的稳定性和质量。 基于块匹配的全景图像拼接是通过匹配和融合多张图像来创建全景视图的过程。在MATLAB中实现这一技术,可以结合深度学习和机器学习的先进方法,提升匹配精度和图像融合质量。通过对压缩包中的代码和数据进行学习,你可以更深入地理解这一技术,并应用于实际项目中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值