使用数据科学进行行为游戏设计
这是一个关于数据科学专业知识和对人类心理学的理解如何帮助我获得有趣的见解来设计更好的游戏的故事。
Screenshot of a game. (https://www.flickr.com/photos/bagogames/31959169544)
背景
上周,作为谷歌初创公司的导师,我有机会与来自中东欧的 10 家游戏工作室合作。这些初创公司拥有 200 万到 1000 万活跃用户,每月经常性收入在 15 万到 20 万之间,团队规模在 10 到 80 人之间。尽管它们在收入和用户群方面有所不同,但它们都面临着同样的挑战:
- 留存率低:在大多数情况下,95%的用户在 21 天后离开。在一个游戏中,保留时间为 1 天— 50%,7 天— 30%,14 天— 20%,21 天— 10%(见图 1)。
Figure #1: Retention of users in one of the games.
2。应用内货币化水平低:付费用户数量很低,行业标准是 3%左右,而游戏只有 0.15%到 1%。他们的大部分收入来自广告。
**3。无病毒效应:**几乎没有病毒效应,所有新用户都是通过付费渠道获取的。
出了什么问题?
当问到“你真的知道谁是你的用户吗?”,没有一家创业公司自信地说是。游戏工作室有一些想法,但他们不确定用户的动机和用户是如何细分的。
为什么这样他们有大量数据,但没有进行适当的数据分析。例如,他们不分析数据,因此他们可以看到用户行为的模式,这可以帮助他们获得关于用户的有意义的见解,将用户分组,在他们离开之前找到行为模式,等等。
这些创业公司如何提高用户留存率?
我们做某件事总是有理由的——包括玩游戏。关键在于理解心理学玩家如何反应,他们的潜在动机是什么。
第一步:是什么促使人们玩你的游戏?
数据无法完全回答这个问题。人们可以看到用户的某些行为模式,并使用试探法或常识来得出结论。例如,两个常见的动机是
- 感觉像个胜利者
- 消磨时间
太好了!所以这创造了两个用户群。下一步是绘制他们在游戏中的旅程,并与动机联系起来。
第二步:为每个细分市场绘制用户旅程图
每个用户群在游戏中都遵循一条特定的路径,主要是由他们的动机驱动的。通过数据分析,人们可以发现给定的用户群遵循什么样的路径。比如说,在一个游戏中,我们发现那些以获胜为主要动机的人,大体上遵循以下路径
- 跳过教程—开始游戏—赢得—获得免费积分—赢得—进入下一层— …
第三步:描绘动机
尝试将动机与用户旅程联系起来。最有可能的是,在游戏过程中动机会改变,这会导致用户改变行为。例如,在一个游戏中,开发人员发现那些主要动机是想赢的用户在连续输了 3-4 场后最终离开了。看起来用户觉得他们不能再赢了,变得沮丧并离开。
第四步:创建一个激励结构来保持最初的动机
我们如何让他们回到理想的旅程,或者保持用户开始游戏的动机?对于上面提到的情况,其中一个人由于沮丧而离开,如果用户面临多次失败,游戏可以让她赢——这将增加他们留在游戏中的动机。
所有上述步骤都非常直观和简单,但令我惊讶的是,没有一个游戏工作室遵循上述步骤。
但是如何增加应用内购买呢?
当用户看到无法免费获得的价值时,他们愿意付费。**在游戏中,当有人想要打破“游戏规则”或者获得新的体验时,最适合开口要钱。**让我们用一个简单的例子来说明——假设规则是“每个人都必须通过教程来获得一些积分”。如果有人想跳过教程(因为缺乏耐心),开发者可以让用户购买通过完成教程获得的积分,允许他们继续游戏并跳过教程。
为了帮助游戏赚钱,找到那些用户(或部分用户)愿意打破规则的地方。
如何处理那些以消磨时间为主要动机的用户?要求他们“打破规则”进入下一个阶段并不会真正有所帮助。这些用户可能喜欢帮助他们改善体验的东西,比如定制背景,这是他们很可能愿意花钱购买的东西。
创造一种病毒效应怎么样?
这里的规则也是一样的,一旦我们知道了动机,我们就可以要求他们做一些事情,比如在适当的时候邀请朋友。例如,如果我们知道用户很高兴,并给出了积极的评价,那么我们可以要求他们在这一点上邀请其他朋友,并创建一个激励结构(给予免费积分或只是做好事的内在动机)。
结论
我上面解释的是数据科学如何结合对人类心理学的一些理解来帮助游戏工作室解决保留、支付和病毒式传播问题的一个例子。
基于玩家行为,针对每个细分市场,绘制动机地图。然后创建一个激励结构来保持动机,并为他们提供类似支付或邀请朋友来实现目标的行动,这使他们玩游戏。
我还要补充一点,有些试图建立这样一个基于用户地理位置的行为模型,假设是某些地区/国家的人以某种方式行为。这可能是真的,但请记住,你的用户可能不会反映该国人民的平均行为。例如,有富裕的印度人,也有贫穷的印度人。富有的印度人的行为可能与欧洲人相似,完全有可能你所有的游戏用户都属于富有的印度人。
我希望你喜欢这篇文章。随意分享,喜欢。
利用数据科学改善社会
Tech4Good 是技术界的一种趋势。数据科学是这场精彩比赛的领跑者。
越来越多的企业家和学者正在开发技术来解决社会问题,识别和治疗疾病,弥合经济差距,并倡导变革。Tech4good 和 DataScience4Good 是越来越多地被采用的运动。
Photo by Clark Tibbs on Unsplash
技术总是遵循什么和如何的叙述。技术开始回答为什么的时候到了!
每个领域现在都有它的“数据尤里卡时刻”,给使命驱动的组织全新的机会来利用数据。从解决贫困到改善医疗保健到教育民主化,数据科学有可能使天平向弱势群体和穷人倾斜。
许多组织正在迅速利用数据科学做好事。让我们来看看,并从中获得灵感。
1。数据种类
DataKind 将高影响力组织与领先的数据科学家聚集在一起,使用数据科学为人类服务。
Data Kind Home Page
从一小时的活动到长达一年的活动,他们设计了一些项目,让数据科学家和社会变革者能够共同应对严峻的人道主义挑战。它还向数据科学家介绍了“数据换利益”运动,并向他们展示了他们的技能有多么有价值。
他们执行的项目类型
我们一丝不苟地专注于将各种形式的数据科学带给那些与我们共享可持续地球愿景的人,在这个星球上,我们都可以获得我们的基本人类需求。我们设想这样一个世界,解决这些问题的组织可以像华尔街和硅谷一样访问数据科学资源。
—杰克·波威,DataKind 创始人兼执行董事
2。良好数据
“数据为善”是加拿大的一个行善者的集体,他们想用他们的力量为善,而不是为恶,通过数据来帮助社区变得更好。
Data For Good Homepage
他们是一个加拿大非营利组织,在全国各地都有分会,帮助其他非营利和非政府组织,利用他们的数据做出更明智和更好的决定,以促进他们社区的繁荣。
他们通过自己的数据技能帮助了以下事业。
3。民主数据
Data for Democracy 是一个由热情的志愿者组成的全球社区,致力于促进数据和技术领域的信任和理解。它最初是一个小型的基层团队,共同的目标是利用数据推动积极的变革,但很快发展成为一个拥有 4000 多名员工(人数还在增加)的全球性组织。
以下是他们做的一些项目—
4.暑期项目
许多大学与社会组织合作,提供暑期项目来实施数据科学 4Good 项目
数据科学造福社会奖学金是芝加哥大学的一个夏季项目,旨在培训有抱负的数据科学家从事数据挖掘、机器学习、大数据和具有社会影响的数据科学项目。研究员与政府和非营利组织密切合作,解决教育、卫生、能源、公共安全、交通、经济发展、国际发展等领域的现实问题。
在三个月的时间里,他们学习、磨练并应用他们的数据科学、分析和编码技能,在快节奏的氛围中合作,并向来自工业界和学术界的导师学习。
每年大约有 16 名 DSSG 大学的学生研究员被挑选出来从事具有具体相关性和社会影响的数据密集型项目。在为期 10 周(6 月至 8 月)的项目中,学生应在现场与团队成员密切合作。
佛罗里达州 DSSG 实习生从事影响佛罗里达州非营利组织的数据科学项目。DSSG 项目是一个为期 12 周的带薪实习项目。学生被安排在多学科团队中,并与导师一起解决现实世界中的问题。
5.竞争
许多比赛鼓励数据科学家尝试解决一些最大的挑战,以获得高额奖金。Kaggle 是此类比赛的最大中心,但也有其他玩家,如 DrivenData、IBM Challenges 等。
- 在胸片中鉴别肺孢子虫病 — Kaggle
- 更好的求职体验——Kaggle
- 心脏病预测 — 驱动数据
无论媒介是什么,我们都必须通过任何必要的工具参与帮助我们的社会进步。数据科学将对我们的社会产生深远的影响。
利用数据科学进行税收改革
财务压力和人员减少导致税务机构尽可能实现自动化。
最大的创新是将大数据分析应用到现代税收管理中,大数据分析是对大量数据的自动化分析。利用数据科学推动财务运营已经开始改变政府最高层的税收管理。
事实证明,将大数据应用于税务管理可以有效帮助税务人员识别欺诈和预测违规行为。但是一些公司正在努力适应一个比以往任何时候都需要更多数据的世界——以发票、海关申报和账户报表的形式。
数据科学和税收
使用电脑打击税务欺诈并不是什么新鲜事——美国国税局自 1962 年就开始使用电脑了。然而,大数据分析的使用是一项新的发展。这一策略似乎正在发挥作用——尽管自 2011 年以来员工和预算每年都在减少,但美国国税局报告称发现的欺诈案件增加了 400%,从刑事欺诈起诉中获得的收益增加了 1000%。
国税局正在从过去无法访问或无法获得的来源获取数据。政府服务部门正在从社交媒体账户中挖掘数据,以帮助创建纳税人档案,并在此基础上进行分析——这可能被用于在纳税季检测欺诈行为。来自社交媒体帖子和个人资料的信息,以及从其他来源和国税局报税档案中提取的数据,被用来确定某个人不遵守税法的可能性。
这种整理公共和私人数据的做法引起了隐私倡导者的关注,尤其是因为除了国税局以外,没有人能完全确定哪些信息被用来做这些决定。(国税局在法律上没有义务说。)目前,纳税人不能审查或质疑国税局存储的关于他们的数据。然而,很难想象,如果大数据的使用能让国税局更好地发现欺诈,它会逆转这一进程。
大数据和企业税
作为推动使用越来越多的数据来防止欺诈和收税的一部分,税务当局要求企业提供更多信息。在这样做的同时,这些机构也在标准化他们接收客户和供应商发票、报关单和银行对账单等数据的方式。这对公司来说是一个问题,因为公司的税务和财务数据通常分散在多个系统和格式中。
税务部门也在增加他们期望从企业获得的数据量。例如,在巴西,公司必须遵守 29 种不同的提交要求。有些提交必须一个月一次。
某些公司结构可能会比其他公司更好地应对税务管理方面的这些变化。例如,私人员工持股退休计划(或 S 公司员工持股计划)可能比可比的标准普尔 500 指数公司更容易提供税务信息。
100%员工持股计划拥有的 S 公司员工持股计划不直接或间接支付任何当前公司所得税。(标准普尔 500 公司支付 20%到 25%的税率。)由于这些公司纳税的方式,管理层应该少花时间对公司的会计账目吹毛求疵,寻找机会减少纳税。更简单的会计可以使这些公司更容易向税务机关提供他们需要的各种信息。
大数据分析也可能改变企业会计的运作方式。公司可以使用税务管理人员使用的同样的技术来审计他们自己。会计师可能会发现,近似国税局的税务分析将有助于他们确保他们工作的企业的合规性。
大数据将如何改变税收管理
目前还不清楚这些变化对个人和企业的长期影响。受隐私倡导者启发的立法可能会限制国税局从公共和私人来源收集的数据量。企业可能会抵制对财务数据日益增长的需求。
然而,每当预算下降时,自动化就成了一个诱人的前景。更少的员工做更好的工作是难以抗拒的,特别是对于像国税局这样现金短缺的政府服务机构。
企业和个人都应该为未来做好准备,未来税收管理将由来自各种来源的数据驱动。
图像经由 Rawpixel
如何利用数据科学更好地了解您的客户
客户在你的业务布局中占多大比重?这是一个反问句。我们都知道,大多数企业的兴旺只是因为他们的客户。因此,在为客户服务之前,你必须充分了解他们。了解你的客户有助于你提供量身定制的服务。这提高了客户参与度,增加了销售额。
This picture is definitely worth a thousand words. Source: economictimes
你了解你的顾客吗?嗯,这个问题很模糊。如果你不能从客户的某些定性方面来回答这个问题,那么你现在就需要开始工作了。我确信所有的企业主在他们的脑海中都有一个理想顾客的形象,不管这个形象有多模糊。通常这种形象是凭直觉编造出来的。它可能没有任何同义反复的证据支持。
数据从不说谎。它只不过是事实和数字的集合,有时它可以向我们展示一面镜子。本文将解释如何使用数据科学的“魔力”来获得对客户的一致理解。确切地说,我们将学习如何对这个商场客户数据集应用聚类算法。然后,我们将从输出中进行推断,以更好地了解经常光顾商场的顾客。感谢您忍受如此冗长的前奏,并因您的耐心为您获得项目源代码。
什么是客户分桶?
客户细分或客户分组是将公司的客户分成反映每个组中客户相似性的组(也称为分组)的做法。对客户进行细分的目标是决定如何与每个细分市场中的客户建立联系,以最大化每个客户对企业的价值。
吸引客户使您能够以最大化销售的方式迎合每个客户群体。对于营销人员来说,细分目标客户可以让你以一种能产生最大影响的方式来塑造你的沟通。
在这个项目中,我们将使用聚类分析,根据客户的年收入将他们分成不同的类别。为此,我们将使用 Kmeans,这是目前最好的聚类算法之一。K-means 聚类是一种无监督学习算法,它在数据中寻找组。组数用字母 k 表示。
让我们开始吧。
请随意跟随。数据集可以在这里下载。
偷看数据。
商场客户数据集是一个相对较小的数据集,因为它仅包含 199 行和 5 列。如果你看一下这一段下面的图片,你会注意到这五个栏目的标题是客户 ID、流派、年龄、年收入(k$)和支出分数(1-100)。
我们将从导入必要的库开始。
import pandas as pd
import numpy as np
from sklearn.cluster import KMeansimport matplotlib.pyplot as plt
plt.rc(“font”, size=14)
现在,我们将导入数据集。
data_path = "Mall_Customers.csv"
df = pd.read_csv(data_path)
也许这只是我,但我发现出于某种原因,一些列标题令人不安。让我们开始改变这些。
df.rename(columns={'Genre':'Gender',
'Annual Income (k$)':'Annual_Income',
'Spending Score (1-100)':'Spending_Score'
},
inplace=True
)
在这个项目中,我们将使用客户的年收入和支出得分(1 到 100 分)对客户进行聚类。因此,我们将只使用这两列。
X = df.iloc[:, [3, 4]].values
现在我们已经在数据方面做好了准备,是时候开始我们的集群工作了。在我们运行我们的聚类算法之前,有必要确定将我们的客户分成多少个聚类。有几种不同的方法可以确定该数据集的理想聚类数。为此,我们将使用肘法。
肘法
计算集群数量的一种方法是使用肘方法。该方法包括对不同 K 值的数据运行 K 均值聚类算法,并计算每个 K 值的误差平方和(S.S.E .)
然后,将这些值绘制在图表上,我们可以看到,随着 K 值的增加,S.S.E .趋于减小。当 K 的值等于数据点的数量时,S.S.E .变为 0,因为这样每个数据点就是它自己的聚类。我们的目标是找到一个 K 值很小且 S.S.E 很低的点。
在本实验中,我们将对 0 到 10 范围内的不同 K 值运行 K 均值,并将 S.S.E .存储在一个名为 distortions 的列表中。
distortions = []
K = range(1, 10)
for k in K:
kmeansModel = KMeans(n_clusters = k, init = 'k-means++', random_state = 23)
kmeansModel.fit(X)
distortions.append(kmeansModel.inertia_)plt.plot(K, distortions)
plt.title("The Elbow Method")
plt.xlabel("Number of Clusters")
plt.ylabel("S.S.E.")
plt.show()
现在,让我们看一下图表。
在此图中,您可以观察到,在 K 的每次迭代之后,S.S.E .急剧下降。您还可以观察到,在 K 达到 5 之后,它是一个下坡。因此,5 似乎是 K 的最佳值,这意味着我们将把客户分成 5 个集群。
现在我们已经计算出了集群的数量,我们可以继续创建这些集群。
kmeansModel = KMeans(n_clusters = 5, init = 'k-means++', random_state = 23)
Y = kmeansModel.fit_predict(X)
由于数据集很小,所有这些过程都不需要花时间来完成。一旦创建了集群,我们就可以将它们绘制在图表上。每个聚类点使用不同的符号进行标记,每个聚类的质心使用实心红点进行标记。
Just look at them!
只要看一下图表,我们就能了解到经常光顾购物中心的五种不同类型的顾客。如果我们给他们命名,那么他们可以被命名如下:
i .低收入,高消费(红色)。
二。低收入、低消费人群(蓝色)。
三世。平均收入,平均支出(橙色)。
四。高收入,高消费,和(绿色)
诉高收入,低消费(紫色)。
这些群体中的每一个成员都有更多的共同特征,因此我们有一个同质群体。这些群体中的每一个人都可能有相似的需求和愿望。记住这一点,所有的营销/销售活动都可以满足这些需求和愿望,以吸引更多这样的客户。例如,迎合低收入群体的每周折扣销售或迎合高消费群体的购买奖励积分,将他们转变为常客。可能性是无限的,只受我们想象力的限制。
结论
了解一个企业的客户群是至关重要的。深入了解客户行为的方法之一是根据他们的行为(本实验中的收入和支出)将他们分成不同的类别。)相似的人往往行为相似,这就是客户细分的症结所在。因此,通过围绕这些桶计划所有的销售和营销活动,它将承诺更高的投资回报和愉快的客户体验。
用数据科学预测下一个 NBA MVP
统计模型能自信地告诉我们谁将获得每个赛季的 MVP 吗?
Michael Kovac/Getty Images
每年,都会有一名杰出的 NBA 球员被一百多家体育媒体评选为年度最有价值球员(MVP)。正如体育运动中的其他事情一样,围绕谁应该或不应该获奖的讨论可能会非常激烈。把范围缩小到少数几个球员并不难,但是球迷和媒体很难完全同意谁应该是获奖者。除了那次斯蒂芬.库里。
有些人认为 MVP 应该是拥有最令人印象深刻的数据的球员。其他人认为,一个真正的 MVP 没有最好的个人表现,而是提高周围其他人的水平(见勒布朗詹姆斯)。
但是谁是对的呢?我们可以无休止地辩论这个问题,但毫无结果。或者,我们可以看看数字是怎么说的。为了解决这场辩论,我决定使用各种篮球统计数据来模拟谁将成为 2019 年 NBA MVP。
数据收集
用硒网刮篮球参考
任何数据科学项目的第一步都是获取完整的数据集进行分析。幸运的是,我们面对的是篮球,一项充满数据的运动。
NBA 的数据主要有两个来源:NBA 自己的统计网站或球迷喜爱的第三方篮球参考网站。这里的主要区别是数据格式化的方式:虽然 NBA 网站包括一个 JSON API,但 BBallRef 允许您直接下载 CSV 文件。第一个需要 JSON 请求,第二个需要一点聪明的 web 抓取。因为我希望从后者中获得一些经验,所以我选择了 BBallRef。
在 Python 中使用 Selenium——一个自动化浏览器的工具——我构建了一个简单的 scraper,它按照年份列表加载每个页面,切换内置的“下载为 CSV”按钮,并将 CSV 输出保存到磁盘。幸运的是,BBalRef 页面遵循一个模板,允许使用相同的 scraper,而不管年份或数据类型。完整的注释函数如下所示。这似乎也是一个很好的时机来提及我的完整代码库是在 GitHub 上完全可用的。
一旦 scraper 完成,我所要做的就是让它发挥它的魔力,收集从 1976 年到 2018 年的赛季排名,高级,总,每场比赛的个人球员统计数据和奖励数据。下一步是处理数据。
数据处理
使用 R 来加载、清理和合并数据
虽然简单的球员统计数据以及姓名、球队和赛季的数据集并不是特别难处理,但我的数据确实有一些被证明具有挑战性的特点。首先,让我们只看总玩家统计。下面是单个条目可能的样子。
Example row from 2018 total statistics
好吧,这看起来很简单,但是有一个问题:玩家名字的格式。它采用“詹姆斯·哈登\ \哈德贾 01”的格式,而实际上我们只需要“詹姆斯·哈登”这成为在 r 中练习简单文本清理的绝佳机会。
这就是我们需要做的。当然有一种更有效的方法来完成这项任务,但有时最好接受多几行代码,以使代码更易读。
从这里开始,我们需要遍历数据集中的每一年,加载那一年的 CSV,清除球员姓名,进行一些更简单的数据类型管理(为了方便读者,我省略了这些内容),并将结果合并到一个电子表格中。该电子表格保存了自 1976 年以来每个赛季每个球员的总数据,格式如上所示。我也为每场比赛统计,以及高级统计,奖项统计和赛季排名做了同样的事情。
接下来,我想在个人统计中添加一个Team.Wins
栏,因为团队获胜经常会成为讨论个人奖项的因素。因此,我将每个球员的赛季与我的赛季排名数据中适当的球队和赛季进行了匹配。让我们看看积分榜数据是如何格式化的。
Example row from complete standings data
虽然这在技术上只是匹配团队名称和获取Wins
列的问题,但我们在团队名称格式上有问题。球员数据将休斯顿火箭队编码为HOU
,而积分榜数据使用全名Houston Rockets
。这对任何 NBA 球迷来说都不是问题,但我的电脑没有足够的信息来将球队名称与其缩写匹配起来。
解决方案简单而繁琐:手动将每个缩写映射到每个完整的团队名称。考虑到这些年来团队频繁更换名称,这尤其具有挑战性。谁知道有堪萨斯城国王队(KCK)?创建这个映射后,向球员数据添加一个Team.Wins
列只需要遍历每个球员/赛季对并找到合适的值。
如果你想看看这段代码是什么样子,完整的数据处理代码可以在这里找到以及代码库的其余部分。处理完我们的数据后,是时候进行汇总分析了。
数据分析
汇总统计和初步分析
在建模之前,对数据集进行一些汇总分析始终是一种很好的做法。在本文的其余部分,我们将只查看 2000 年以后的数据。首先,让我们用我们的总数据做一个简单的summary()
,具体看看上场时间,投篮得分,3 分,篮板,助攻,抢断,盖帽和得分。我使用 dplyr tibble
格式来轻松选择相关的列。
Summary of individual player season totals
正如所料,我们看到任何给定的统计值都有很大的范围。让我们快速地看看我们使用dim
函数处理了多少个观察值。
Dataset dimensions for individual player season totals
我们看到我们有将近 6500 个 36 列的观察值。因为我们着眼于 18 年的范围,这给了我们每年大约 360 次观察。接下来,为了了解哪些栏目可能是有用的预测指标,我创建了一个关联图,来看看哪些篮球统计数据与 MVP 投票最相关。
Plot of correlation between player season totals and number of first place MVP votes received
第一次尝试的信息并不丰富。上图显示了第一名票数和得分、投篮命中率和罚球命中率之间微弱的正相关关系。这些统计数据本身也是内在相关的(更多的领域目标=更多的点)。这表明得分多会增加你成为 MVP 的几率,但这是一个相当明显的结论。让我们用更高级的数据再试一次。
Plot of correlation between player season advanced stats and number of first place MVP votes received
我们的结果在这里更有趣,表明玩家效率等级(PER)、获胜份额、正负以及对替代玩家(VORP)的价值与收到的投票更密切相关。让我们通过观察过去的 MVP 获得者在这些统计中的排名来进一步形象化这一点。
Plots of VORP vs. PER and BPM vs. WS with MVP recipients shown in red (players with less than 41 games played omitted)
这两幅图用红色显示了 MVP 获得者,清楚地展示了高级统计在捕捉 MVP 级别球员的品质方面的有效性。然而,他们似乎不足以判断获胜者,在这两张图中有几个 MVP 落在了中间位置。
在这个阶段,我在建模之前采取的最后一步是通过将每个统计数据除以该季节的最大值来按季节归一化每个统计数据。也就是说,现在每个统计值都位于(0,1)之间,stat leader 将标杆设置为 1。这主要是为了避免异常低或高的统计数据伤害模型。现在我们终于可以开始建模了。
建模
建立并调整一个模型来预测 NBA MVP
我们从一个简单的逻辑回归模型开始,这个模型使用了球员赛季总数数据集中的一些相关变量和 r。
tot.log <- glm(MVP~G+X3P+DRB+AST+BLK+PF+PTS+Team.Wins,
data=dat_totals,family = binomial(link = "logit"))
为了澄清这里发生了什么,MVP~
告诉 R 使用以下所有变量来预测 MVP 得主。family=binomial
部分告诉 R 我们打算执行一个简单的(0,1)分类。当我们这样做的时候,让我们也用高级统计数据来拟合一个模型。
adv.log <- glm(MVP~PER+TS.+X3PAr+FTr+TRB.+AST.+STL.+BLK.+
TOV.+USG.+WS+BPM+VORP+Team.Wins,
data=adv.shortlist,family = binomial(link="logit"))
和以前一样,我们传递我们想要预测的变量,后面跟着预测值。现在让我们看看我们的模型有多好。为了衡量模型的性能,我们首先必须使用这些模型进行预测,然后看看这些预测有多准确。为了简化这一点,我在 R 中创建了函数来完成这两项任务,如下所示。
第一个函数对每个球员进行预测,然后找出每个赛季预测赔率最高的球员,授予他 MVP。第二个函数将选择的 MVP 与正确的选择进行比较,并返回精确度。现在让我们测试我们的两个模型的准确性。
Accuracy and error count for total stats and advanced stats models
我们的 total stats 模型报告了 84%的准确率,而 advanced stats 模型只能在大约三分之二的时间里正确预测 MVP 冠军。我很惊讶地看到高级统计模型表现更差,因为在我们的初步分析中,高级统计显示与 MVP 投票有更高的相关性。为了更好地判断这里发生了什么,让我们看看高级统计模型的模型系数。
Model coefficients for advanced stats logistic regression classifier
奇怪的是,模型系数表明更高的 PER 和 BPM 实际上会降低一个球员成为 MVP 的几率。这显然是错误的,所以一定是哪里出错了。让我们通过查看每个赛季的 PER leader 来进行调查。
PER leaders by season
虽然这份名单中肯定有一些像沙奎尔·奥尼尔和勒布朗·詹姆斯这样的名字,但其他人像大卫·温盖特和贾内尔·斯托克斯似乎很突出。幸运的是,这里的问题是显而易见的:游戏和上场时间异常低的玩家放弃了高级的统计计算。
这里简单的解决方法是简单地忽略未能满足最低游戏数的观察结果。这绝对是值得的一步,但我们可以走得更远。为什么要考虑那些根本没有机会获奖的球员呢?通过训练我们的模型让每个球员每个赛季都踏上球场,我们淡化了模型突出精英球员的能力,而是让轮换球员充斥其中。
此时,我决定实现一个两阶段的模型管道。首先,我们确定哪些球员甚至在争夺这个奖项,然后我们预测候选名单中的哪个球员最有可能获得 MVP。由于我们的数据集包含每个玩家获得的投票份额,我们可以很容易地编码一个二进制的Shortlist
变量如下。
dat_totals$Shortlist<-dat_totals$Share!=0
然后,我们拟合一个简单的逻辑回归模型,并预测入围的球员。
tot.short.mod <- glm(Shortlist~G+MP+X3P+DRB+AST+
BLK+TOV+PF+PTS+Team.Wins,
data=dat_totals,family = binomial(link = "logit"))
## grab shortlist
tot.shortlist<-dat_totals[which(predict(tot.short.mod,type="response")>.75),]
在对高级统计数据集进行了同样的操作后,我们就可以像之前一样进行建模了。让我们看看我们的准确度是否提高了。
Accuracy and error count for total and advanced stats models
我们的两阶段管道似乎是成功的,因为我们的 total stats 模型保持了相同的准确性,而高级模型显著提高了。在这两种情况下,我们每个赛季在近 400 名球员中有 84%的几率正确预测出 NBA MVP。
当然,我们正在犯一个重大的数据科学错误:仅仅根据训练数据来衡量模型性能。我们的模特完全有可能过于合身,对未来几季毫无用处。为了测试这一点,我们必须采用交叉验证。
交叉验证
按季节留一交叉验证(有很多连字符)
虽然通常您可能只是将数据分成训练集和测试集,但在这种情况下会有点棘手,因为我们每个季度只有一个积极的观察结果。删除季节的子集会创建一个太小而无用的测试集。相反,我采用了留一法交叉验证。
为了做到这一点,我必须将每个模型训练 n 次,其中 n 是季节数。每次,从训练集中选出一个季节,并且仅针对该季节进行预测,结果是模型从未看到过其预测数据的每个季节的预测。为了简化这个过程,我编写了一个 R 函数来快速运行这个过程。
有了这个方便的助手功能,我们现在可以交叉验证以前模型的准确性。
Leave-one-out cross-validation error for each model
不出所料,在应用交叉验证后,我们看到了模型准确性的下降。也就是说,我们仍然有一个相当准确的模型,特别是先进的统计数据。但是为什么要在那里定居呢?
在这一阶段,我们拥有适合和测试各种不同模型所需的所有工具。在我寻找理想模型的过程中,我对总统计数据、每场比赛统计数据和高级统计数据以及由总统计数据和高级统计数据组成的合并数据集进行了线性、逻辑和多项式回归。每个模型的结果如下所示。
Accuracy and leave-one-out cross-validation accuracy for each model fit
我们可以看到,在交叉验证后,许多模型的表现非常相似,我们的最高 CV 准确率为 73%,这是由两个不同的模型共享的,两个模型都使用高级统计数据。我们还可以在其他情况下看到过度拟合,例如三次多项式模型,它具有最高的未验证准确性,但一些验证得分最低。
我们值得信赖的逻辑高级统计模型是与最高准确性相关的模型之一,所以让我们坚持使用它。现在是关键时刻了:预测 2019 年 NBA MVP。
预言;预测;预告
预测 2019 NBA MVP
为了预测 2019 年 NBA MVP,我们必须首先加载该年的高级数据。幸运的是,我们已经在数据加载和处理文件中为此准备了一个函数。
一旦数据被加载,我们只需通过我们的两阶段管道运行数据。首先选出前 10 名候选人组成入围名单,预测每个选手的赔率。下面的代码完成所有这些工作,然后将最终预测转换成百分比。
现在剩下要做的就是焦急地看着我们的结果。
MVP prediction results for 2019
最终,我们的模型几乎未能正确预测 2019 年的 MVP 得主:扬尼斯·阿德托昆博,但哈登和詹尼斯之间的预测得分接近,反映了 MVP 比赛实际上有多接近。事实上,詹尼斯获得了大约 35%的投票份额,而哈登达到了近 30%。
我们的候选名单模型也表现得相当好,几乎完美地预测了前 10 名得票数。候选名单中唯一的错误是将凯里·欧文排除在科怀·伦纳德之外。因此,虽然我们没有准确地做出正确的最终预测,但我们的两阶段流水线仍然表现得相当好。
未来的工作
模型改进和数据可视化
虽然我们已经在本文中介绍了大量不同的技术和方法,但是我仍然想探索许多途径来改进这个项目。最明显的一个是使用更复杂的模型。支持向量机或随机森林可能会大大超过我的简单逻辑回归分类器。
我想做的更有雄心的改进是加入社交媒体数据。围绕 MVP 的争论经常围绕着一个拥有优越数据的球员可能会输给一个更强大的媒体叙事(见 2015 年的詹姆斯·哈登或 2011 年的勒布朗)。找到一种量化社交媒体言论的方法可能是探索这个问题的一个有趣途径,也可能改善我们的结果。
最后,这个项目的最终目的是创建一个网站,通过在每场比赛后更新预测来跟踪整个赛季的 MVP 赔率。虽然我已经部分构建了 HTML 界面,但我仍然需要对系统进行编码,以便在每场比赛后使用 NBA API 更新预测。
感谢阅读!请告诉我您是否喜欢这篇文章,以及我是否应该在将来撰写第 2 部分。如果你感兴趣,我的整个代码库都可以在这里找到。
利用数据科学为我下次墨西哥之行省钱
我如何使用基本的数据工作来确保我的旅行价格合理。
我和妻子在阳光明媚的地方度假已经有 4 年了。上次我们度蜜月时,在墨西哥度过了一段美好时光。我们在里维埃拉玛雅一个非常好的全包式度假村度过了 10 天。从那以后,一栋房子,两个孩子,一份新工作和许多其他事情。经过一番思考,我们决定是时候回到海滩上去了。因此,明年 12 月(2019 年),我们(我的妻子,我们 3 岁的孩子,我们 4 个月大的孩子和我)将再次前往里维埃拉玛雅。
别担心,我不会把我的数据博客变成一个旅游和生活博客。我想和你分享一下我是如何确保这次旅行价格合理的。
这就是有趣的地方,当我们今年夏天(6 月)签署合同时,合同说,如果价格下降,我可以,一次,要求价格匹配。因为旅行是在±6 个月后,这看起来是一个非常有趣的功能。顺便说一句,这是我们选择那家旅游公司的原因之一。坦率地说,我们为整个家庭支付了 4317 加元。
不那么容易的部分
几周后,我回去查看价格。它还是一样的。然后我意识到,我怎么能追踪价格。没有办法我可以花时间去,通过一系列的网络界面点击查询价格。那将意味着一天 2-3 分钟的烦人时间,我真的没有那个时间。
下面是获取价格更新的样子:
The full process of getting the price update.
显然,旅游公司并不认为跟踪价格是一个很好的功能。在搜索了几次之后,没有办法做到这一点。至少用网站上的一个工具。
如果它低于我的原价,我是接受这个价格还是再等一会儿。因为记住,我只能做一次价格匹配。拥有所有历史价格真的很有用,如果/当它低于原价时,我可以观察趋势并做出更明智的决定。
所以现在我需要每天花几分钟获取价格,然后再花几分钟将价格复制到某种电子表格中。任何做过这种事情的人都知道,在最初的几天,甚至几周,这种方法还行。但是在某个时候,你开始错过日期,复制粘贴错误,等等。
手动处理数据或多或少相当于没有处理。
写剧本就行了。
我的想法是,我可以获得 URL,从 python 或其他什么地方下载 HTML,然后用一些 regex 魔术来提取价格。
当然,这不可能那么容易。首先,URL 并没有真正改变。所有价格的东西都是另一页上面的某种模式。所以复制 URL 基本上会把你带回主页。
现在,当我开始查看 Chrome 开发者工具时,我认为我可以在某个地方看到数据。数据必须正确…正确…
在每一个文件中挖掘了几次之后,我找到了金块。
看起来我们走在正确的道路上。我们有一个 JSON 文件和定制的 URL 来获取它。显然,当我在新页面中直接使用这个 URL 时,它不起作用。我收到相当于页面不可用的信息。真的,看起来他们不想让我们这么做。他们设置了许多路障来阻止我们做这件事。谢谢奥巴马。
然后,我在 chrome 开发工具中发现了一个非常简洁的选项。
复制为卷曲。您最终得到一个非常长的命令,可以粘贴到您的终端并获得 JSON。
终于,有东西起作用了。我现在有办法提取价格了。
正如你所看到的,这个查询非常糟糕。真的,就像他们不希望我们自动提取价格一样。至少,现在,我们有一本很好的字典可以使用。
现在如何处理这个值
既然我们可以访问这个值,我们如何提取它。我决定创建一个每 6 小时运行一次的 AWS Lambda 函数来提取价格。以这个价格,一天 4 次,我们做 3 件事:
- 我们检查价格是否合理。由于我支付了 4300 多一点,如果价格低于 4 千美元,我给自己发了一封电子邮件。以确保需要时我能迅速行动。
- 我将值(带有时间戳)存储在数据库中。(DynamoDB)
- 我将该值(带有时间戳)存储在 AWS S3 中
为什么是 2 和 3,我不确定我将如何使用它,因为 AWS 有一些关于什么可以访问什么的规则,而且因为存储非常便宜,我存储了两次。
价格
因此,可悲的是,价格还没有低于 4300 美元,公平地说,我怀疑它会。旅行现在根据天数定价 4700/5000。
为了构建这个视图,我使用了一个叫做 Dash 的神奇工具,它允许你用不到 100 行代码构建这个视图。有兴趣可以去看看这里的 app:https://voyage . coffeeanddata . ca。
发现
有几天,我没有收集到任何新的数据点。事实上,cURL 命令从服务器获得了一个 404。我花了一段时间才意识到我没有新数据。
因为我没有在代码中实现任何验证。Lambda 脚本会无声无息地失败,我也不会收集新的数据点。
因此,我在代码中添加了一个验证,当 cookie 需要更新时,它会向我发送一封电子邮件。我假设 cookie 中加密了一些过期信息。因此,简单地获得一个新令牌似乎就足够了。
明智地选择
到目前为止,我从数据中得到的另一个有趣的点是曲线的这一部分:
连续几天,早上 7 点左右,价格飙升了 200 美元。这是我下次预订旅行时会注意的事情。
结论
可悲的是,我没有存下任何钱…到目前为止。我将在两个多月后飞往墨西哥,所以我会继续在我的网站上跟踪价格。旅游公司似乎正在努力阻止我们自动提取价格。
价格看起来很不稳定,我很想知道是什么影响了每小时的价格。
如果你感兴趣,所有的代码都可以在我的 Github 回购:https://github.com/marcolivierarsenault/triptracker
你可以在这里查看我的 web app:https://voyage . coffeeanddata . ca
原载于 2019 年 10 月 28 日https://coffeeanddata . ca。
利用数据科学揭露 Twitter 上国家支持的巨魔
国家运营商在社交媒体上发起的造谣运动已成为对民主的严重威胁。以下是如何用三步法揭开它们的面纱。
面对越来越大的公众和政治压力,脸书和 Twitter 等社交媒体巨头被迫加大对其平台上国家支持的虚假信息活动的打击力度。
FB 已经对来自伊朗和以色列的违规账户采取了行动,而 Twitter 发布了数百万条被认为是俄罗斯、伊朗、委内瑞拉和孟加拉国政府支持的运营商所为的推文。
然而,尚不清楚的是这些账户和社交媒体帖子的识别过程。比如说,在新加坡,人们该如何在社交媒体上揭露这些经营者?
我在 General Assembly(新加坡)的最后一个项目中解决了这个问题,当时我参加了一个为期 12 周的数据科学训练营。通过结合使用数据科学技术,我相信以下三个步骤可以用来帮助揭示国家运营商在社交媒体上的工作:
- 步骤 1:建立巨魔的“数字指纹”——探索性数据分析和可视化可以有效地识别国家运营商的数字踪迹和作案手法。
- 第二步:建立一个机器学习模型和 web 应用程序,更有效地对可疑的推文/帖子进行分类。
- 第三步:使用 SHAP 获得模型预测正确或错误的详细信息。这一步的见解可以反馈到第一步,在调查中形成良性循环。
更新—2020 年 9 月
我上传了一份新的报告,内容是关于如何使用一个微调过的 transformer 模型来检测 state troll tweets。详细的笔记本和数据可以在这里找到。可以在这篇文章中找到有关步骤和测试的概述。
重要的事情先来
我的项目专注于 Twitter,尽管我认为这种方法可以广泛应用于 FB 或 Instagram 帖子。这篇文章是对结果的概括,我不会深入研究代码。
我的项目回购可以在这里找到:http://bit.ly/StateTrolls(最好克隆或下载笔记本。它们巨大的文件大小使它们在线加载很痛苦。)
我还开发了一个简单的网络应用程序来检测俄罗斯的国家巨魔,你可以在这里试试:【http://bit.ly/TrollDetector
这个项目中使用的国家支持的推文(Twitter 官方发布)可以在这里下载。
背景
那么,这些国家支持的巨魔是谁,他们的目标是什么?迄今为止最知名的肇事者是俄罗斯的互联网研究机构(IRA)及其多年来影响 2016 年美国大选的努力。
对其活动的详细讨论可以占据几个中等篇幅,因此谨慎的做法是在此强调关于这一主题的两个更广泛的报告:
- 在线情报公司新知关于爱尔兰共和军战术和比喻的深度报道。
- 彭博关于世界各地政府控制行动的长篇报告。
爱尔兰共和军的成功引发了世界各地的效仿行为,包括伊朗、委内瑞拉和孟加拉国的类似行动。在一个全面的解决方案出台之前,这个问题只会变得更糟。
本项目中使用的数据
上面的图表总结了我如何为这个项目收集和筛选数据。
这类项目中的关键挑战一直是建立“地面真相”的困难,也就是说,你如何知道哪些是国家巨魔的推文?在我的案例中,我依赖于 Twitter 官方发布的大量 state troll 推文(Twitter 没有披露其识别这些推文/账户的内部流程)。
我过滤掉了非英语的推文,以及转发,这样分类模型就不会以语言为基础区分推文。这显然是我的项目的一个限制,因为语言和 RTs 是 Twitter 上国家巨魔行为的一个关键方面。
对于真实的推文,我使用 Tweepy 到从 35 个经过验证的账户中收集了超过 81,500 条推文——包括新闻媒体、像川普和希拉里这样的政治家,以及美国的活跃用户。
清理后的数据被用于建立两个分类模型(稍后将详细介绍),一个用于检测俄罗斯国家巨魔的推文,另一个用于挑选伊朗的推文。每个模型都接受了 50,000 条推文的训练——真实推文/巨魔推文各占一半。这些模型还在看不见的推文测试集上进行了测试,这些推文包含来自俄罗斯、伊朗和委内瑞拉的不同比例的国家巨魔推文。
第一步:建立“数字指纹”
t-SNE plots of samples tweets from 10 troll and real accounts.
国家巨魔可能在幕后运作,但他们的最终目标迫使他们在社交媒体上留下清晰的数字痕迹,正如 10 个巨魔和真实账户的推文的 t-SNE 图(上图)所示。
从我对俄罗斯爱尔兰共和军推文的探索性数据分析来看,很明显,在时间、活动和伪装方式方面有明确的结构模式。虽然一些特征是爱尔兰共和军在 2016 年选举中的目标所独有的,但我认为在 Twitter 这样的开放平台上运作的国家巨魔会留下大致相似的痕迹。
就其本身而言,这些线索都不符合“确凿证据”的条件。但综合来看,它们描绘了一幅相当一致的行为图景。让我们来看看其中的一些关键特征:
1.1 账户创建日期
现在,Twitter 是一个成熟的平台,用户增长正在放缓。它的受欢迎程度在 2009 年达到顶峰,这意味着在美国等成熟市场,新用户注册量的突然激增应该会敲响警钟。
分析俄罗斯巨魔账户的创建日期,很明显,可疑的大多数是在 2014 年创建的。一个勤劳的国有运营商可以通过缓慢地传播他们的账户创建日期来隐藏他们的踪迹。但是人们不应该低估人类马虎的能力。
1.2 真实与国家支持的推文/账户的结构特征
Comparison of number of followers for top state-backed accounts (left), versus the number of accounts they follow (right).
如果你了解社交平台上的用户体验,辨别可疑行为就会变得更容易。在 Twitter 的背景下(以及该平台内容的消防性质),没有一个正常的真实用户会关注成千上万的其他用户,因为那会完全破坏用户体验。
上面的图表显示了我的数据集中前 10 个俄罗斯巨魔账户的关注者数量(左),以及他们关注的可疑的高数量的账户(右)。因此,关注者对关注者的比率是巨魔账户的一个关键迹象。
这种模式与真实用户形成鲜明对比,如下图所示:
来自国家巨魔的推文也大多较短,由大约 11-14 个单词组成,少于 70 个字符。参见我的笔记本,了解与真实 tweets 相比,巨魔 tweets 更详细的结构特征。
1.3:藏在众目睽睽之下
也许俄罗斯国家巨魔最有趣的一面是他们伪装自己的方式——在这种情况下,他们试图伪装成美国当地的新闻机构。看看真实和巨魔推文的主题建模结果之间的相似之处。
“news” is the No 1 most relevant term in the topic models for real tweets (left) versus troll tweets (right).
乍一看,真实和巨魔推文的主题模型看起来如此相似,这似乎很奇怪。为什么“娱乐圈”、“政治”和“纽约”这些无关痛痒的术语会在巨魔推文主题模型的 30 个最相关术语中占据很高的位置?
但是当你从用户的角度考虑它的时候,它是完全有意义的。突发新闻和信息更新是美国人使用 Twitter 的主要方式。
因此,俄罗斯巨魔潜入目标明确的宣传的最佳方式之一是欺骗真实用户,让他们认为巨魔账户是新闻和有用信息的合法来源(可以是体育或娱乐更新)。最终,他们的目标不是用巨魔内容的弹幕淹没普通用户,而是把假的和真的混在一起,这样用户在很长一段时间后就分不清两者了。
关于俄罗斯虚假信息运动的新知识报告称之为“媒体海市蜃楼”。看看下面这组推文。不知情的用户可能不会立即意识到这些是俄罗斯运营商发布的 troll tweets。
显然需要大量的市场调查来补充这些 EDA 技术和图表。例如,只有在了解政治日历的情况下,对特定年份或月份的账户创建量异常激增的洞察才有意义。
但是底线是清楚的:有一种方法可以让这些国家巨魔疯狂。他们的目标和社交平台的开放性将迫使他们留下数字痕迹和独特的行为模式。
在试图使用机器学习模型将可疑的推文与真实的推文区分开来之前,拼凑他们的“数字指纹”是必不可少的第一步。
步骤 2:构建+训练-测试分类器
你能多快把上面的六条推文整理成巨魔和真实推文?600 或 6000 条推文呢?手动识别和分类巨魔推文在大规模应用中并不现实。这就是分类器发挥作用的地方。
您可以构建一个非常复杂的模型,使用从步骤 1 中收集的特征组合以及 tweet 文本的内容来辨别 troll/real tweet。但是这确实会使配套 web 应用程序的构建变得复杂(稍后将详细介绍)。这也使得在步骤 3 中分析单个预测变得更加困难。
出于这些原因,我已经尽可能简单地保持了我的模型的设计。我只使用了 1 个预测值——一个“干净的 tweet”列,其中原始 tweet 文本已被清除标点符号等——而目标列为“bot _ or _ not”(0 表示真实 tweet,1 表示 troll tweets)。
上图总结了笔记本 3.0 中俄罗斯巨魔探测器模型的工作流程,以及笔记本 3.2 中伊朗巨魔探测器模型的工作流程。这是一个简单的过程,包括对原始的英文推文进行标准化的清理,然后通过一个管道进行处理,其中包括一个 CountVectorizer、一个 TFIDF 转换器和一个分类器。
在这个项目中,我选择了更常见的分类器——朴素贝叶斯、逻辑回归和随机森林。您可以选择更复杂的分类器,但是考虑到训练集的大小(50,000 行),完成管道运行所需的时间可能会呈指数增长。
总的来说,逻辑回归模型是我尝试的三个模型中最好的。它拥有最好的 f1 分数——精确度和召回率的平衡——以及最快的平均拟合时间。
让我们从模型 1 开始,它用俄罗斯巨魔推文训练,看看它在 3 个不同的 100 条推文的看不见的测试集上的表现如何,其中巨魔推文的比例从 50%逐渐减少到大约 10%:
L-R: Confusion matrices for Model 1 Vs 3 unseen test sets with 50% Russian troll tweets, 30% troll tweets and 10% troll tweets.
模型 1 出人意料地善于从真实的推文中挑出新的俄罗斯巨魔推文,即使巨魔推文的比例逐渐减少。在整个测试过程中,它的 f1 分数保持在 0.8-0.9 之间。该模型正确地挑选出了绝大多数看不见的 troll 推文,甚至在 90-10 的测试集中获得了 1.0 的完美回忆分数(上图中最右边)。
但它对其他国有运营商的推文有用吗?我用来自伊朗和委内瑞拉的 troll tweets 对 Model 1 进行了测试,结果很糟糕:
L-R: Confusion Matrices for Model 1 Vs unseen test sets with Iranian troll tweets (L) and Venezuelan troll tweets ®.
Model 1 的召回分数在与伊朗和委内瑞拉 troll tweets 的未知测试集的测试中惨败。虽然它继续很好地挑选出美国用户的真实推文,但它未能捕捉到大多数伊朗或委内瑞拉的推文。
这些结果似乎表明,每个国家支持的运营商的工作都非常具体。与其试图建立一个庞大的“全球”模型来捕捉所有的国有运营商,不如建立更小、更灵活的模型来更好地识别特定的国有运营商。
我建立了第二个模型来测试这一论点,这一次是在伊朗巨魔的推文中结合经过验证的美国和国际用户的真实推文来训练模型。以下是类似测试条件下的结果,伊朗巨魔推文的比例从 50%逐渐降低到 14%左右:
L-R: Confusion matrices for Model 2Vs 3 unseen test sets with 50% Iranian troll tweets, 30% troll tweets and 10% troll tweets.
Model 2 的表现也很出色,它能够挑选出以前从未见过的新的伊朗巨魔推文。它的 f1 分数在所有 3 次测试中都高于 0.9。
正如上面的 3 个混淆矩阵所示,该模型非常善于挑选出巨魔推文,在 90-10 的集合中获得了完美的召回分数,它挑选出了所有 14 条巨魔推文,并且在 100 条推文中仅错误分类了 1 条。
结论是显而易见的(尽管在一开始并不完全明显):一个根据特定国家支持的运营商的推文训练的模型不会很好地推广。为了抓住国家巨魔,你需要为他们所在的每个市场量身定制解决方案。
第 2.1 步:使用网络应用程序快速办理登机手续
Go to http://chuachinhon.pythonanywhere.com/ to try out the web app.
抓住这些国家支持的运营商需要团队的努力,并不是每个参与其中的人都有技能通过机器学习模型运行大量可疑的推文。每当你想检查一些潜在的可疑推文时,运行一个模型也是没有效率的。
为此,我开发了一个简单的网络应用——【http://chuachinhon.pythonanywhere.com/】T2——用户只需要输入一条可疑推文的文本,就可以快速检查它是否是俄罗斯巨魔的推文。该应用程序很简单,如果你需要将它们放在 10 个不同国家或市场的团队手中,你可以轻松地构建 10 个不同的版本。
它不会像数据科学家的计算机上的最新模型那样准确,但它可以作为一种快速诊断工具,可以补充第一步中使用的其他工具,以识别 state troll 的数字指纹。
第三步:用 SHAP 分析预测
至少有两种方法可以进一步分析模型的预测,例如通过检查预测的概率或绘制最常出现的关键词的频率。
但这两种方法都无法提供 SHAP 所能提供的粒度水平,即揭示哪些特征促使模型预测一条推文是真实推文还是巨魔推文。SHAP 还可以用于深入了解模型的预测哪里出错了,以及是什么导致了不正确的分类——这是在巨魔更新他们的策略时更新模型的重要洞察。
SHAP 的详细解释,或者说沙普利附加解释,超出了这篇文章的范围。在这个项目的背景下,用几个例子来说明 SHAP 是如何工作的可能更容易。
SHAP 示例 1
这是一条被 Model 1 准确归类为真正的推文的推文:“特朗普在白宫会见中国副总理刘鹤时,可能会宣布峰会日期。”
根据通过的训练数据集的平均模型输出,每个模型都有一个唯一的基值。在这种情况下,模型 1 的基值是 0.4327。
不同的矢量化要素会将模型的预测推向不同的方向。如果最终输出低于基准值,则被归类为真正的 tweet。如果输出高于基本值,它被认为是一个 troll tweet(在我如何在这个项目中标记输出的上下文中)。
在上面的例子中,我们可以看到像“中国”、“峰会”、“总理”这样的事实词推动模型将推文归类为真实的推文,而有趣的是,“特朗普会见”这些词则推动模型向相反的方向发展。
SHAP 实施例 2 和 3
让我们再看两条推文,其中 Model 正确地分类为俄罗斯巨魔推文:“@HillaryClinton # HillaryForPrison”和“@ HillaryClinton 滚蛋”。
在上面的第一条推文中,“hillaryforprison”标签是推动模型将这条推文归类为巨魔推文的最强特征。在第二条推文中,脏话及其与希拉里·克林顿名字的组合是推动模型将其归类为巨魔推文的最强因素。
虽然该模型对美国政治或新闻没有天生的理解,但它已经获得了足够多的真实和虚假推文的例子,能够区分这两组推文。
当然,这种模式可以被巨魔推文打败。让我们看看模型 1 预测错误的一些例子。
SHAP 实施例 4 和 5
俄罗斯巨魔探测器 Model 1 错误地将这条推文归类为巨魔推文(高于基础值),而实际上它是一条真正的推文:“当克林顿被抓到使用她的私人电子邮件服务器时,我在国家安全委员会社区认识的大多数人都很愤怒……”
“生气”、“私人邮件”和“被抓”这些词促使 Model 1 将这条推文归类为巨魔推文——而事实上,这条推文是弗莱彻学院教授、《华盛顿邮报》专栏作家丹·德雷兹纳(Dan Drezner)写的。
Model 1 在面对伊朗巨魔的推文时也多次失败,因为它没有接受过训练。它将这条推文归类为真实的,而实际上它是一条巨魔推文:“西班牙,意大利警告不要投资以色列定居点。”
像新闻标题一样写的简短而真实的推文似乎会绊倒机器学习模型。同样,该模型似乎很难处理稍微复杂一点的推文,比如涉及电子邮件服务器的推文。
**我对此的看法很简单:**有效识别社交媒体上国家支持的虚假信息活动,需要将人工输入/分析与机器学习和数据分析工具的智能使用很好地结合起来。
对人类来说似乎显而易见的事情,对一台没有地缘政治知识的机器来说可能并非如此,而机器在发现模式方面可能会更有效率,而这需要人类花很长时间来手工整理。
限制
上面的图表总结了我揭露 Twitter 上国家支持的巨魔的方法的一些局限性。从建模的角度来看,语言可能是最棘手的问题。
一方面,您不希望您的 troll 检测器成为一个美化的语言分类器。另一方面,通过只在英语推特上训练模型,你错过了国家支持的巨魔的一个关键特征。
熟悉深度学习技术的数据科学家可能会在这一领域有更好的解决方案。
最大的限制是这个过程完全是反应性和诊断性的。没有办法先发制人的国家巨魔的工作,至少没有公开可用的工具。
这是我第一次尝试将我新生的数据科学技能应用于像在线虚假信息这样的复杂问题。这里和笔记本上的错误都是我的,我欢迎该领域专家的反馈,或者任何一般性的更正。
最后,非常感谢本杰明·辛格尔顿对这个长期遭受折磨的项目的帮助。特别的大声喊出来还要感谢 Susan Li 她出色的 NLP 和 Flask 教程,这对我帮助很大。
以下是该项目的主要资源链接:
Github 回购:【https://github.com/chuachinhon
穆勒报告:《关于俄罗斯干涉 2016 年总统选举的调查报告》
为什么要帮助西雅图 Airbnb 的主人和客人做出更明智的决定?!
Photo by Zhifei Zhou on Unsplash
在任何地方旅行之前,首先想到的问题是住宿。在过去的八年里,Airbnb 证明了它是解决这类旅行问题的最便捷、最便宜的解决方案。它也允许房主通过分享他们的房子来增加他们的收入。
动机:
Airbnb 为短期住宿提供了一个极好的解决方案。但现在是时候利用数据,引导新主机估算更合理的价格,同时实现收入最大化。还有,帮助客人了解哪些住宿因素他们更应该注意,以节省一些钱。西雅图 Airbnb 开放数据可以在这里找到。
我试图集中回答可能对双方都有帮助的问题,因为我们每个人都可以既是主人也是客人。根据 Airbnb 的定价提示,约 60%的主机收入来自物业因素和设施,另外 40%来自全年的需求。所以,我决定应用机器学习和时间序列技术来分析这些关键因素;价格、收入和需求。
利率上升的主要驱动因素是什么?
2016 年的平均挂牌价约为每晚 137 美元。但是它会随着不同的月份和工作日而变化。夏天甚至接近 160 度。西雅图是一个寒冷的地方,由于需求较高,夏季价格较高是有道理的。
西雅图 Airbnb 上列出的周末住宿地点似乎比其他工作日更贵。
因此,时间和市场是主要驱动因素,并应被视为机器学习模型中的回归变量,这在统计上具有重要意义。房屋和公寓是最受欢迎的房产,但价格合理,每晚不到 150 美元。但是,一些功能,如船,阁楼,公寓和联排别墅是昂贵的,很少上市。
为了确定更高评级的驱动因素,我清理、缩放和格式化了数据,为预测分析做好准备,还删除了具有相似性能和多重共线性的变量。然后使用随机森林回归找出最重要的列表特征,以解释是什么导致了价格范围的变化。
随机森林通过测量两个性能指标来选择关键特征,减少节点杂质,提高准确性。所有可能的价格驱动因素都给出了分数。权重越高,列表特征区分价格值的效果越显著。关于随机森林如何排列每个元素的值的细节超出了这个项目的范围。
首先,我拟合了随机森林模型,然后通过调整模型超参数来增强它,以达到最高的精度。
该建议解决方案的结果表明,以下特征有助于在采油树中做出关键决策,并且可以解释价格范围中 90%的变化:
这个回归变量中的基本元素是卧室数量、浴室数量、床位数量、包括的客人数量、评论数量、季节、邻居、取消费用和政策严格性。
一般来说,这些特征可以分为三大类:一个是物业特点,以前的评论,以及季节性和每周的需求,我将在下一部分进行更多的分析。
【2016 年 Airbnb 的需求和价格如何变化?
正如我之前提到的,西雅图从 2016 年 1 月到 2017 年 1 月的平均价格表明,市场是价格范围中的一个重要因素。在夏季,更高的需求导致更高的费率,并在 2016 年初和 2017 年急剧下降。
我分解了每日价格序列,以确定价格趋势和季节性,并了解对 Airbnb 业务至关重要的基于时间的模式。此外,本部分的另一个基本目标是调查是否有可能使用 2016 年的数据确定一个显示未来模式的预测模型。
平均价格有季节性,不是固定不变的。为了检查数据是否满足时间序列预测的平稳性要求,我将数据分成两部分,并比较每组的均值和方差。由于均值和方差差异较大,且差异具有统计学意义;我对这个系列进行了对数变换,使其保持平稳。
自相关图显示了每个滞后时间的价格;它与其过去的值高度相关,并且可以通过使用时间序列技术来预测。但我决定使用 Prophet,而不是经典的统计时间序列方法。
Prophet 是由脸书推出的一种程序,用于基于加法模型预测时间序列数据,其中非线性趋势符合不同的季节性和假日效应。你可以在这里阅读更多关于先知的信息。此外,Prophet 具有易于解释的参数,可以调整这些参数来改进趋势和季节性预测。
在这里,我训练了一个线性趋势的加性回归模型,每周,每天和每年的季节性,考虑了假期的影响。一般来说,我假设我的时间序列有三个主要部分:趋势、季节性和节假日。
成分图显示,周末、夏季和节假日对价格上涨有积极影响。还有,2017 年日均价格会从 137 涨到 150 以上。
在下图中,蓝线代表预测值,而黑点代表每天的实际值。从图中可以看出,该模型似乎可以捕捉季节性,并很好地拟合历史数据,但我建议添加 2017 年和 2018 年的数据集,如果可以验证模型预测能力的话。
Prophet 中趋势变化点和不确定性区间的数量可以调整,以克服过度拟合和欠拟合。
哪些上市更有利可图并带来更多收入?
为了回答这个问题,我定义了一个度量收入的指标,因为原始数据集中没有这样的数据点。我假设主人的收入是通过每晚的价格乘以预定房间的最少次数来估算的。我将所有列出的资产分为两组,一组表现更好,每年带来超过 10000 美元的收入,另一组表现相反。这个阈值可以根据平均收入的分配情况而改变。然后,我比较了顶部和底部上市绩效的具体特征。
结果显示,公寓和房屋带来了更多的收入,但不仅仅是因为价格高,而是因为它们被预订得最多。
下面的条形图比较了不同酒店中表现最佳和表现最差的 5 家酒店的数字汇总。
为了确定哪些因素导致一个列表比其他更有利可图,我应用了不同的分类器梯度推进,逻辑回归,支持向量机和 K 近邻。然后,我调整了梯度增强分类器超参数,以达到更高的精度并克服过度拟合。大约 83%的房源是分类的,收入低于 10000 美元,17%在 2016 年赚得更多。因此,我应用了 SMOTE 等重采样技术,以确保分类器能够捕捉所有不寻常的列表。特性重要图表示,如果一个主机成为超级主机,在一两个小时之间做出响应并增加容纳更多人的能力将比其他人赚更多的钱。
在 520 个低表现者列表中,大约 466 个被该分类器准确预测。ROC 曲线下的面积显示,对于新数据集,对这两组进行分类的模型准确度为 89%。
这个模型可以通过在精确度和召回率之间定义一个更加可释放的折衷来改进。此外,可以使用另一个收入度量或阈值来增强评估度量。
最后,我想说明的是,这个项目是 Udacity 的数据科学纳米学位课程的一部分。如果你对审查代码或改进它感兴趣,我的 Github 的链接是可用的这里。
使用深度学习进行基于手指静脉的生物认证
这个手指静脉识别项目是在 AlgoDeep AI 平台 上完成的。更多详情可以阅读 本文 关于执行这个项目的全过程,从研究到部署。AlgoDeep 的首席运营官 鲁迪·德卢亚 a 是这两篇文章的合著者,也是手指静脉识别项目的合作者。
当伊森·亨特在《碟中谍 5》中使用英国首相的生物识别技术解锁红盒子时,你是否惊叹于认证系统?
Retina Scan (images from Mission Impossible 5)
动作和科幻电影中出现了多种生物认证系统,如视网膜扫描仪、虹膜扫描仪、人脸识别、指纹、声音识别,甚至步态识别。生物认证系统是近年来的研究热点。一些解决方案发展迅速,已经应用于现实生活中的安全场景。
生物认证系统
生物认证系统是通过测量个人身体的特定特征或行为来验证个人身份的实时系统。虹膜扫描仪等生物识别设备收集个人的生物识别数据,并将其转换为数字形式。通过使用算法来匹配模式,生物认证系统可以通过将数据与数据库中其他注册的生物数据进行比较来完成识别或验证个人的任务。设计了生物认证系统的两种主要模式,识别或验证,。在识别模式中,输入数据与数据库中所有注册的模式进行比较。系统可以判断这个人是否来自数据库。当处于验证模式时,将生物测定输入数据与一个人的特定模式进行比较。它旨在确定他们是否是同一个人,并防止多人使用同一身份。
基于手指静脉的生物认证系统
Credit to Teiss
然而,正如我们在电影和科幻小说中看到的那样,一些生物认证系统可以被假冒的资源所欺骗。在丹·布朗的小说《天使与魔鬼》中,哈萨辛挖出了列奥纳多的眼睛,偷走了安装在装有视网膜扫描仪的门后的安全反物质。虽然视网膜的生物特征对每个人来说都是独一无二的,但黑客也可以找到破解认证系统的方法。在不同的生物特征中,安全级别也不同。与其他方法相比,我们今天讨论的基于手指静脉的生物认证系统更难被欺骗,因为它只能识别活人皮肤下的独特手指静脉模式。
手指静脉数据的收集
Finger-vein capture device. From the paper [2]
How to capture finger veins [3]
使用特殊的捕获机器来收集手指静脉数据。该捕捉设备主要由近红外光源、透镜、滤光器和图像捕捉设备组成。由于手指静脉隐藏在皮肤之下,可见光无法看到它们。这种捕捉设备使用可以穿过人体组织的近红外光。此外,血红蛋白和黑色素等色素会阻挡近红外光。
使用深度学习进行手指静脉识别
从第一个著名的神经网络 LeNet 识别 10 个手写数字的图像,到更复杂的神经网络在 ImageNet 中对 1000 类图像进行分类,深度神经网络(DNNs),特别是卷积神经网络(CNN)在计算机视觉中的能力是众所周知的。CNN 通常表现良好,甚至优于传统的计算机视觉方法,因为它们非常擅长自动从图像中提取特征。
手指静脉识别可以看作是一个图像分类问题。用 CNN 处理手指静脉识别问题一定很有趣!我们应该如何设计实验来适应生物认证系统的需求?根据以前使用的手指静脉识别方法,其中许多方法进行特征提取,然后计算两个特征之间的距离。根据特征距离的分布来固定阈值。如果距离值高于阈值,则这两个特征不被归类为来自同一个人。否则,如果距离低于阈值,这两个特征被视为来自同一个人。
CNN-based finger-vein recognition system. Image from [3]
手指静脉数据集
一些研究机构提供公开的手指静脉数据集。我们使用的数据集是来自 SDUMLA-HMT 数据库的手指静脉数据集。感谢山东大学文学硕士实验室提供的山东大学文学硕士-HMT 数据库。该数据集注册了 106 个人的手指静脉图像。双手的三个手指,食指、中指和无名指被抓获。每个手指有 6 张图片。因此,总共由 3,816 个图像组成。图像的格式是 320x240 像素大小的“bmp”。
Image from [5]
图像预处理—感兴趣区域(ROI)提取
正如我们从上面的图像中可以观察到的,捕获的图像不仅包含手指,还包含作为捕获机器的背景。提取 ROI 的目的是保存手指部分,去除背景。必须找到上限和下限来获取 ROI。
Image from [6]
我们通过以下步骤建立了我们的 ROI 系统:
- 将图像裁剪为 240x240 像素
- 将图像分成两半,并应用过滤器来检测上下边界
- 仅保留上限和下限之间的区域
- 做线性拉伸
From left to right: cropping, masking limits, keeping areas and linear stretching
手指静脉识别的迁移学习
通常,在大数据集上提供令人满意的结果的 CNN 模型具有巨大的参数。如果要从零开始训练一个复杂的 CNN 模型,是非常耗时耗资源的。通常,我们没有足够的数据来从头开始训练。我们也应该考虑过度拟合的问题。如果模型太复杂,而我们的数据集又很小,那么它就很有可能过度拟合。
迁移学习就是来解决这个问题的。迁移学习旨在将现有模型(基于大量数据的预训练模型)应用于其他领域或其他类型的任务。例如,我们可以使用在大型猫狗数据集上预先训练的模型来对大象和猴子进行分类,或者对卡通猫狗进行分类。
正如我们可以想象的,将预先训练的模型直接应用到其他领域或其他任务可能不会很好地工作,因为模型在训练时看不到来自该领域的信息。我们通常有两种选择。一方面,预训练的 CNN 模型可以被视为特征提取器。可以通过使用提取的特征作为输入来构建线性分类器。另一方面,经常采用微调的方法对一些高层进行微调。早期图层中的功能更加通用。而后面的层中的特征包含原始数据集的更具体的信息。冻结早期层可以为我们带来许多任务的通用和有用的功能。微调后续图层可以生成更多存在于数据集中的特定要素。
Image from [6]
在我们的案例中,我们尝试微调 VGG-16 预训练模型中的一些后续层。事实证明,当我们从最后一个卷积层进行训练时,我们的结果最好。
手指静脉识别实验
实验主要受本文“使用 NIR 图像传感器的基于卷积神经网络的手指静脉识别”的启发。提出了手指静脉识别的两种情况。第一种情况使用手指静脉图像作为输入。分类器允许对不同的手指进行分类。另一种情况旨在使用不同的图像来分类真实匹配(相同类别的输入和登记的手指静脉图像之间的匹配)和冒名顶替者匹配(不同类别的输入和登记的手指静脉图像之间的匹配)。两种情况都使用预训练的 CNN 模型,并用手指静脉数据集微调该模型。正如我们之前介绍的,生物认证系统有两种可能的模式,识别和验证。实验的第一种情况对应于识别模式,第二种情况对应于验证模式。
进行实验的过程如下:
- 数据预处理:将数据集分成训练、验证和测试数据集,通过小范围地随机平移和旋转图像来扩充训练集,如果我们考虑 ROI,则将 ROI 应用于三个数据集。(对于第二种情况,区别真实和冒名顶替匹配的图像)。
- 设置模型:冻结 VGG-16 中的早期图层,仅微调后面的图层。
数据预处理
第一步是分割数据集。SDUMLA-HMT 数据集包含 636 个类(3 个手指* 2 只手* 106 个人)。每个手指是一个类,它由 6 个图像组成。对于我们实现的两种情况,我们使用了不同的分割策略。
对于第一种情况,在开始时,数据集被分成三个数据集,前三个图像用于训练集,第四个图像用于验证集,其余的用于测试集。然而,使用这种分割数据的方法会导致测试集的过度拟合。通过检查错误分类的类的图像,我们观察到一些类的最后两个图像不同于其他四个图像。这意味着训练集和验证集的分布与测试集的分布不对应!在分割数据集之前,我们已经检查了一些类。但是,碰巧我们只检查了图像相似的类。要学习的一个教训:在训练之前,一定要确保验证集和测试集的分布是相同的。
对于第二种情况,数据集被随机分成两半。每个部分现在包含 318 人。第二部分被分成两半,一部分用于验证集,另一部分用于测试集。
接下来,由于每个类别的三个图像不足以训练合格的模型,所以使用数据扩充方法来增加训练集中的图像数量。数据扩充是一种正则化方法。当数据集的大小不足以概括结果时,可以应用数据扩充来生成更多数据并降低过度拟合的风险。一些动作可以增强图像,例如旋转、平移、翻转、亮度修改。在我们的数据扩充方法中,通过轻微的平移、旋转和亮度修改,为训练集中的每个图像生成 12 个图像。
对于第二种情况,生成差异图像以进行真实和冒名顶替者匹配。从训练集的每个类中,选择一个用于注册的图像。对于剩余图像中的每一个,通过减去该图像和来自相同类别的登记图像来计算真实差异图像,并且通过减去该图像和从不同类别中随机选择的登记的一个图像来计算冒名顶替者差异图像。最后,真实差图像和冒名顶替差图像的数量相同。在第二种情况下,该模型引入了一个二元分类器,用于对真实匹配和冒名顶替匹配进行分类。
模型架构
VGG-16. Image from [6]
VGG-16 是一个非常深的卷积神经网络,它有 1.38 亿个参数。这种深度架构的训练需要大规模数据集。然而,我们的训练数据比 ImageNet 小得多。因此,一个预先训练的 VGG-16 模型在我们的数据集上进行了微调。尝试从不同层面进行训练。如果训练更多的早期层,过度拟合的风险将会上升。然而,如果只训练输出层,性能是不令人满意的。最后,选择从最后一个卷积层微调的模型。
结果和分析
进行了许多实验来找出更好的超参数和更好的训练策略。虽然模型还可以进一步改进,但我们目前得到的结果是有希望的。
分类器的结果
我们对两种模型进行了实验。第一个模型是用于识别的多类分类模型。另一个模型是用于验证的二元分类模型。二元分类模型的输入是差异图像。这两个模型都是从预训练的 VGG-16 模型的最后一个卷积层进行微调的。以下结果来自未经 ROI 预处理的模型。我们也对具有 ROI 数据的模型进行了实验,但是我们发现结果不在预期之内。由于我们使用的数据集是低质量的数据集,并且一些 ROI 预处理图像的质量没有保证,因此该模型不能很好地对类别进行分类。
Results of classifiers
生物认证系统的度量
与通过准确性评估的分类模型不同,生物认证系统有其度量标准来衡量认证的质量。三个最常用的指标是错误拒绝率(FRR)、错误接受率(FAR)和等错误率(EER)[11]。
- 错误拒绝率(FRR): 系统无法检测到输入模式与数据库中匹配模板匹配的概率。它测量被错误拒绝的有效输入的百分比。(#错误剔除样本数/#匹配样本数)
- 错误接受率(FAR): 系统错误匹配输入模式和数据库中不匹配模板的概率。它衡量被错误接受的无效输入的百分比。(#错误接受的样本数/#不匹配的样本数)
- 等误差率(EER): 接受和拒绝误差相等的比率。该值表示错误接受的比例等于错误拒绝的比例。相等错误率值越低,生物统计系统的准确度越高。
对于识别模型,FAR、FRR 和 EER 的计算基于图像之间的欧几里德距离。首先,我们计算类内和类间距离来定义阈值范围。距离阈值的跨度在 83 和 2024 之间。然后,绘制一个远 FRR 图来确定能效比。最终的 EER 是 4.1%。
Intra-class and Inter-class distance of the identification model
FAR-FRR plot of the identification model
对于验证模型,如果直接使用欧氏距离作为特征距离的度量,将会导致难以解释的结果。这里的类间和类内距离是相同类的差异图像的距离和不同类的差异图像的距离。很难解释“两个不同图像的距离”的含义。差异图像提供了关于两个图像之间的差异的信息,并且该差异可大可小,这取决于原始图像。因此,差异图像的特征的欧几里德距离在这里似乎不适合作为度量。此外,在二元分类问题中,FAR 和 FRR 是容易估计的。FAR 是 FP(假阳性)/ #匹配样本,FRR 是 FN(假阴性)/#非匹配样本。我们使用预测的概率作为度量,然后计算 FAR 和 FRR。我们得到的误差是 1.9%。
结论
我们知道深度学习方法是数据驱动的。数据质量是获得成功实验结果的关键因素。我们使用的数据集质量不是很好,但结果仍然很有希望。识别模型和验证模型的最终结果都优于参考文献中的结果。与其他模型不同,深度学习方法实现起来很快,构建起来更简单,无需投入太多复杂的功能处理和工程。然而,与其他论文中的一些结果(不总是使用深度学习方法)相比,我们仍然需要在数据预处理、模型架构和超参数选择方面进行改进。
参考资料:
[1]尹一龙,,孙希伟.SDUMLA-HMT:一个多模态生物特征数据库。第六届中国生物识别大会(CCBR,2011 年),LNCS 7098,第 260-268 页,中国,2011 年。
[2]杨国荣,X,殷玉英.基于个性化最佳位图的手指静脉识别.传感器(巴塞尔)。2012.
[3]沙希德、卡希夫和伊德、韩刚和、刘和杨、和库雷希、伊姆兰和古、杰和尹、一龙。手指静脉识别技术的系统综述。信息(瑞士)。2018.
[4]孟,龚,方,张,张.基于卷积神经网络的手指静脉识别.在 MATEC 网络会议 EDP 科学。2017
[5]http://mla.sdu.edu.cn/info/1006/1195.htm 的 HMT 数据库
[7]范道明、朴 YH、阮道泰、权熙、朴韩国。采用近红外图像传感器的非介入式手指静脉识别系统及各种因素下的精度分析。传感器(巴塞尔)。2015.
[8]费格森、马克斯&阿克、罗内&李缇娜、容村& H .劳、金乔。基于卷积神经网络的铸件缺陷自动定位。2017.
[9]西蒙扬,卡伦&齐塞尔曼,安德鲁。用于大规模图像识别的非常深的卷积网络。2014.
[10]洪亨,李明波,朴元淳。基于卷积神经网络的近红外图像传感器手指静脉识别。传感器(巴塞尔)。2017.
[11]生物统计学原理http://www.biometria.sk/en/principles-of-biometrics.html
使用深度学习进行图像类比
我将回答以下问题:
狗对于狼就像猫对于 __?
通过使用经过训练的深度卷积神经网络对照片进行分类。
A dog is to wolf as a cat is to ___ ?
卷积神经网络
我会使用预先训练好的 VGG16 图像分类模型。该模型由一个接一个堆叠的 CNN 层组成,由 max pooling 层连接。网络的输入是 244×244×3 的图像(即图像宽度和长度是 244 像素,3 个通道),在应用所有卷积层之后,我们得到 7×7×512 的阵列。
(diagram taken from deeplearning.ai course by Andrew Ng, “Convolutional Neural Networks”)
在网络的末端,我们有一个额外的展平层、两个完全连接的密集层和一个 softmax 层,它输出图像属于第 I 个标签的概率 P(x∈i),其中 i=1,…,1000(标签数)。
单词嵌入和类推
另一个与语言处理和深度学习相关的概念是单词嵌入。给定一个大的文本语料库,比方说有 100,000 个单词,我们建立一个嵌入或映射,给每个单词一个在 n=500 维的较小空间中的向量。
这种维度的减少给了我们一个单词的紧凑表示。事实上,单词嵌入对许多任务都很有用,包括情感分析、机器翻译,还有单词类比(例如这里的)也就是说,解决一个类比形式的问题*“一个男人是国王还是一个女人是 ____?”*。特别是,一旦我们有了嵌入,解决类比就非常简单了——我们使用向量空间的结构来应用线性运算。
图像类比
我试图采用使用单词嵌入的概念来解决使用文本的类比,使用预先训练的图像分类模型来解决使用图像的嵌入。想法是使用网络在最后完全连接的层之前,得到一个特征提取地图
这个方法似乎很有效,并为我们的类比提供了一个解决方案:
狗之于狼如同猫之于 猞猁
这也给出了一个不寻常的方法来解决一个单词类比问题,对不对?
下面是代码的概述。我们首先加载 VGG16 模型和图像。
然后,我们以与使用单词嵌入相同的方式解决类比,但是使用我们的 CNN 特征提取图:
这是结果:
我们有将近 50%的可能性得到“山猫”。
总而言之,我们的类比完成得很好!如果你不知道什么是猞猁,这里有一张参考照片:
lynx
使用深度学习来分类具有深度连接的关系状态
《PyTorch》中浪漫情侣的形象分类
Model scheme of DeepConnection.
如果最近深度学习的爆发有一个根域,那肯定是计算机视觉,即图像和视频数据的分析。因此,当你在研究深度学习时尝试一些计算机视觉技术并不令人惊讶。长话短说,我和我的搭档( Maximiliane Uhlich )决定将这种形式的深度学习应用于浪漫情侣的图像,因为 Maximiliane 是一名关系研究员和情侣治疗师。具体来说,我们想知道我们是否能准确地分辨出任何一对特定的情侣,在图像或视频中描绘的,在他们的关系中是否幸福?事实证明,我们可以!由于分类准确率接近 97%,我们的最终模型(我们称之为 DeepConnection)能够清楚地区分不幸福和幸福的夫妇。你可以在我们的预印本中阅读完整的故事,以下是我们所做的粗略概述。
对于我们的数据集,我们做了一些网上搜集(使用这个方便的 Python 脚本)快乐的&不快乐的夫妇的图像。最终,我们得到了一个大约 1000 张图片的训练集。这并不多,所以我们调用了数据增强和转移学习的力量来拯救我们。数据扩充,图像方向,色调和强度的微小变化,以及许多其他事情,会阻止你的模型学习一些不相关的联系。例如,如果幸福夫妇的图像平均比不幸福夫妇的图像更明亮,我们不希望我们的模型描绘出这种联系。我们利用了(great) ImgAug 库,并进行大量的数据扩充,以确保我们的模型是健壮的。基本上,在每批的每个图像上,至少应用一部分增强技术。下面您可以看到一个示例性的数据批次,其中相同的图像有 48 次,具有代表性的数据扩充轮廓。
Exemplary batch with data augmentation.
到目前为止,一切顺利。因为我们在这里讨论图像,所以我们决定使用 ResNet 类型的模型作为 DeepConnection 的基础,并在庞大的 ImageNet 数据集上进行了预训练。在对各种图像进行预训练后,这个模型已经学习了许多有用的形状和形式,并通过这种迁移学习取得了领先。顺便说一下,我们所有的模型都在 PyTorch 中,我们使用了 Google Colab 上的免费 GPU 资源进行训练和推理。该基础模型本身已经是分类的良好开端,但我们决定更进一步,将 ResNet-34 基础模型的最后一个自适应池层替换为空间金字塔池层( SPP )。这里,处理后的图像数据被分成不同数量的方块,只有最大值被传递用于进一步分析。这使得该模型能够关注重要的特征,使其对不同的图像尺寸具有鲁棒性,并且不受图像扰动的影响。之后,我们放置了一个幂平均变换( PMT )层,用几个数学函数来变换数据,以引入非线性,并允许 DeepConnection 从数据中捕捉更复杂的关系。这两项增加都提高了我们的分类准确性,我们最终在单独的验证集上获得了大约 97%的准确率。您可以在下面查看 SPP / PMT 和后续分类层的代码。
class SPP(nn.Module):
def __init__(self):
super(SPP, self).__init__()
## features incoming from ResNet-34 (after SPP/PMT)
self.lin1 = nn.Linear(2*43520, 100)
self.relu = nn.ReLU()
self.bn1 = nn.BatchNorm1d(100)
self.dp1 = nn.Dropout(0.5)
self.lin2 = nn.Linear(100, 2)
def forward(self, x):
# SPP
x = spatial_pyramid_pool(x, x.shape[0], [x.shape[2], x.shape[3]], [8, 4, 2, 1])
# PMT
x_1 = torch.sign(x)*torch.log(1 + abs(x))
x_2 = torch.sign(x)*(torch.log(1 + abs(x)))**2
x = torch.cat((x_1, x_2), dim = 1)
# fully connected classification part
x = self.lin1(x)
x = self.bn1(self.relu(x))
#1
x1 = self.lin2(self.dp1(x))
#2
x2 = self.lin2(self.dp1(x))
#3
x3 = self.lin2(self.dp1(x))
#4
x4 = self.lin2(self.dp1(x))
#5
x5 = self.lin2(self.dp1(x))
#6
x6 = self.lin2(self.dp1(x))
#7
x7 = self.lin2(self.dp1(x))
#8
x8 = self.lin2(self.dp1(x))
x = torch.mean(torch.stack([x1, x2, x3, x4, x5, x6, x7, x8]), dim = 0)
return x
如果你有敏锐的眼睛,你可能已经注意到了最终分类层上的八个变体。看似浪费的计算实际上是相反的。这个概念最近被提出来作为多样本丢失,并且在训练期间极大地加速了收敛。它基本上是防止模型学习虚假关系(过度拟合)和试图不丢弃来自漏失掩码的信息之间的折衷。
我们在项目中做了一些其他的调整,最好在 GitHub 上查看预印本或相关代码以获取更多信息。简单地提几个:我们用混合精度训练模型(使用 Apex 库)来大大降低内存使用,使用早期停止来防止过度拟合,并根据余弦函数学习速率退火。
在达到令人满意的分类精度(相应地具有较高的召回率和精确度)后,我们想知道我们是否可以从 DeepConnection 执行的分类中学习一些东西。因此,我们从事模型解释,并使用一种称为梯度加权类激活映射的技术( Grad-CAM )。基本上,Grad-CAM 采用最终卷积层的输入梯度来确定显著的图像区域,这些区域可以被可视化为原始图像之上的上采样热图。如果你想看看那是什么样子,看看下面的图,然后是 Grad-CAM 代码。
Grad-CAM for all three models, with saliency heatmaps for representative images. Model prediction and confidence are displayed on top of the images.
我们在论文中进一步讨论了这一点,并将其嵌入到现有的心理学研究中,但 DeepConnection 似乎主要集中在面部区域。从研究的角度来看,这很有意义,因为交流和情绪主要通过面部表情来传达。我们还想看看,除了 Grad-CAM 获得的视觉感之外,我们是否可以通过模型解释获得实际特征。为此,我们创建了激活状态图来可视化最终分类层的哪些神经元被任何给定的图像激活。
State-of-activation plot of DeepConnection for representative images. For every neuron in the last layer, its happy and unhappy weights are colored according to the incoming activation. Activation in the bottom right corner are strongly indicative for happy and the top left corner is most relevant for unhappy couples.
与其他模型相比,DeepConnection 还发现了具体的不快乐特征,并且不仅仅使用不快乐特征的缺失作为不快乐的分类。但是需要进一步的研究来将这些特征映射到人类可以理解的方面。我们还在之前模型没有看到的夫妇的视频帧上尝试了 DeepConnection,效果相当好。
总的来说,模型的稳健性是它的一大优势。精确分类适用于同性恋夫妇、有色人种夫妇、图像中有其他人的夫妇、面部不完全可见的夫妇等等。对于图像中出现其他人的情况,DeepConnection 甚至可以识别这些人是否快乐,但仍然将预测集中在这对夫妇本身。
除了进一步的模型解释,下一步将是使用更大的训练数据集,该数据集有可能训练更复杂的模型。将 DeepConnection 用作夫妻治疗师的助手也很有趣,在治疗期间或之后,可以获得关于(拍摄的)夫妻当前关系状态的实时反馈,以供分析。此外,我鼓励你和你的伴侣一起输入你自己的形象,看看 DeepConnection 对你们的关系有什么看法!我很高兴地报告,这至少祝福了我们的关系,所以我想这是一个好的开始!
让我知道你是否有任何想法,如何推进这项工作,或者你希望看到什么添加/改进!
如果你感兴趣的话,这里有预印本和 GitHub repo 的链接。
使用深度学习通过确保驾驶员的注意力来拯救生命
现实生活中的卷积神经网络
Photo by melissa mjoen on Unsplash
仅在 2017 年,就有 3166 人在涉及分心司机的机动车事故中丧生。— NHTSA
驾驶机动车本身就是一项复杂的任务。然而,当我们把注意力分散加入其中时,它会变得更加困难,因为道路上的司机缺乏注意力。那么,如果我们能识别司机何时分心呢?这样做将有助于我们发现它,并提醒司机,以防止事故发生!
在这篇文章中,我将使用卷积神经网络来解决检测分心驾驶员的问题,甚至将他们除安全驾驶之外所做的活动分类。这里有一个 Kaggle 内核的链接。
导入库
我使用 Keras 和 Tensorflow 作为后端来创建卷积神经网络,因此,我导入了必要的库。
os.environ[‘KERAS_BACKEND’] = ‘tensorflow’
将keras
后端设置为tensorflow
,os.environ[‘TF_CPP_MIN_LOG_LEVEL’] = ‘3’
隐藏所有 tensorflow 日志。
导入数据集
csv 文件driver_imgs_list.csv
包含所有训练图像的列表,以及对人和类名的引用。类名是对图像中的人正在进行的活动类型的引用。
Top 5 rows of the dataframe
图像概述
我决定通过显示每个类的图像来查看图像数据集。由于标签classname
不是描述性的,我决定用一张地图给每张图片添加标题。
train
文件夹有 10 个文件夹,每个文件夹对应一类图像。我遍历所有文件夹,并从每个文件夹中绘制第一幅图像。使用plt.subplot(5, 2, image_count)
,我定义将有 10 个图像组成 5 行和 2 列。image_count 定义了从 1 到 10 范围内的图像计数。
Drivers doing different things
构建模型
我将创建一个具有 3 个Conv2D
层(每个层后面是MaxPooling2D
层)、1 个Flatten
层和 3 个Dense
层的卷积神经网络。在进行了几次试验并从其他内核中获得线索后,我最终确定了神经元。因为这是一个多类问题,我有最后一个密集层,有 10 个神经元,损失是使用categorical_crossentropy
识别的。
Classifier model
创建培训数据
使用ImageDataGenerator
,我将增加我可以训练模型的图像数量。此外,我将使用flow_from_directory
方法从相应的文件夹中读取对应于每个类的图像,并创建 80%和 20%的训练和验证分割。请注意,我通过将所有值除以 255 来重新调整每张图像。
现在,我将根据这些数据训练模型,并获得验证准确性和损失。
训练模型
用fit_generator
,我来训练模型。
该模型达到了 97%的验证准确率。
结论
Photo by Maxwell Ridgeway on Unsplash
使用卷积神经网络,我能够以 97% 的准确率识别驾驶员何时分心驾驶。下一步,我们可以通过增加 CNN 的复杂性和层数来进一步改进模型,并实现其对最终输出的影响。
使用深度神经网络进行 YouTube 推荐
这是 Paul Covington、Jay Adams 和 Emre Sargin 在 2016 年发表的论文《用于 YouTube 推荐的深度神经网络》的概述。
YouTube 拥有业内最大、最先进的推荐系统之一。作为世界领先的网站之一,为了让客户满意,它必须推荐相关视频。YouTube 与其他利用推荐系统的服务(如网飞、Hulu、Spotify)略有不同,因为用户每秒钟向该平台上传数千小时的视频。YouTube 的语料库在不断变化,他们无法控制添加的内容。这就需要一个强大的模型,能够处理不断传入的数据,并实时输出质量建议。下面这个模型就是作者对这种需求的回应。
系统概况
The Recommendation System’s Architecture
他们设计的推荐系统有两个阶段。第一个是用于候选生成的神经网络,而后者用于排序。 候选生成 网络事件取自用户 YouTube 历史。这只能使用协同过滤来提供广泛的个性化。然后,通过标识符(如观看的视频数量、人口统计信息和搜索查询标记)对这些用户进行比较。 排名网 的操作稍有不同。它使用“描述视频和用户的丰富特征集”为每个视频分配一个分数。这种两层系统允许系统处理数百万个视频,但也可以向个人用户提供有意义的内容。
阶段 1:候选人生成
作为分类的建议
作者将推荐系统称为“极端多类分类,其中预测问题变成了对特定视频的准确分类”。在这种情况下,嵌入数据是收集的关于视频的稀疏数据:(即用户、交互、观看时间等……)深度神经网络能够处理这些数据,并使用它来区分视频和用户。为了训练具有数百万个类别(视频)的模型,作者使用数据样本,然后使用权重来校正采样。使用这种技术,他们能够以更少的计算能力获得相似的精度。
模型架构
该模型的灵感来自于一个连续单词包语言模型的架构。神经网络被输入学习到的关于每个视频的高维嵌入,这些视频被组织在固定的词汇表中。
为了跟踪用户观看数据,数据被转换成不同的视频 id 数组,这些数组通过前面提到的视频的嵌入被映射成密集向量表示。然后,使用梯度下降反向传播来学习嵌入信息以及其他模型参数。
管理新数据
在这个具体的例子中,神经网络有几个优点。使用神经网络作为矩阵分解的一般化允许将任意数据(连续的和分类的)容易地添加到模型中,这是该模型由于不断变化的语料库而需要的。
可以通过将搜索查询历史标记为一元词和二元词(指定短语中有多少单词的类别)来使用它。然后对这些见解进行平均,并创建一个相当密集的搜索历史,然后该模型可以使用它来帮助预测用户未来可能喜欢的内容。用户的人口统计特征,这在预测没有观看任何视频的用户时是有帮助的,被改变成二元选项[0,1]。
新鲜度:
作为一个平台,最近的相关内容对 YouTube 至关重要,它有助于保持用户参与和更新,然而,模型通常偏向于根据过去的数据进行预测。为了纠正这一点,他们将训练数据的年龄设置为一个特征,并将其设置为非常接近零(基本上使所有数据都来自同一个时间点)。这有助于模型在训练的最新部分进行优化和预测——在数据末尾进行预测意味着它可能会预测下一个视频,因为它最接近当前,这些视频也仍然与用户相关。
语境:
用户以各种方式使用 YouTube。他们可以直接在网站上观看,也可以观看嵌入在网站或其他社交媒体网站上的视频。该模型使用所有这些信息来收集关于用户的数据,并通过其网络传播这些数据。为了提高性能和避免过度拟合,需要从模型中限制几个与上下文和信息有关的关键见解。其中一个见解是为每个用户创建固定数量的输入,因为如果有无限的输入,那么高容量用户将主导损失函数,并且模型将过度拟合他们的倾向。
这个怎么重要?
YouTube 上有各种类型的视频和流派。一些节目有时间顺序,这意味着它们将按顺序观看,而音乐视频通常以该艺术家最受欢迎的歌曲开始,然后用户转向该艺术家较小的利基作品。如果这些推荐受到高流量用户的影响,这可能会中断低流量用户查看其订阅源中的视频的方式。他们可能会错过利基市场或可能不会看到最受欢迎的视频基于行动的较高比率的用户在该空间。
反过来,通过预测用户将要观看的下一个视频,而不是使用历史数据来随机选择使用维持数据的视频,可以获得更多的成功。这种方法有其缺陷,因为它会将未来的数据泄露给模型,从而导致过度拟合。
特性实验:
向试图解决如此重大问题的模型添加质量特性有助于极大地提高准确性和可伸缩性。超过 100 万个视频和 100 万个搜索令牌以小数形式嵌入包中,最多可包含 50 个最近观看的手表和 50 个最近搜索。关键问题是使用这些特性来帮助创建每个用户的每个动作的时间标记。
第二阶段:排名
推荐系统的第二部分涉及视频排名。为了推荐优质内容,YouTube 需要一种方法来确定用户正在观看和欣赏哪些内容。
特色工程
作者观察到,在预测推荐时,之前与一个或多个类似视频的互动非常重要。这很直观,因为如果观众喜欢特定类型的内容,他们可能会观看该细分市场中的许多视频。他们还注意到,来自特定频道的视频对于决定下一步推荐什么也非常重要。另一方面,如果用户被推荐了视频,但忽略了它们——那么下次页面刷新时,视频将会来回移动并改变。作为 YouTube 的用户,我已经经历过很多次了。
其他功能是以典型的机器学习方式设计的。分类变量是一次性编码的,而连续特征是标准化的(神经网络在标准化数据下表现更好),并通过模型学习。在最初的分离之后,模型开始变得复杂。特征被分成单价和多价组,这意味着它们分别贡献单个值或多个值。特征也被分类为印象和查询。印象每秒计算一次,而查询特征每个请求只计算一次。
建模预期观看时间
能吸引观众注意力的视频通常被认为质量较高。为了推荐高质量的视频,需要对模型进行训练,以便它可以预测观众将观看视频的时间。通过预测视频将被观看多长时间,该模型可以对视频进行排名。在它对视频进行排名后,它将能够决定是否推荐该视频。
为了预测观看时间,作者使用了加权逻辑回归。它被加权的原因是只有正面的视频被赋予权重。积极的视频是用户实际上已经点击了视频,这有助于算法只学习用户与之交互的视频。
结论
作者的“深度协同过滤模型能够有效地吸收许多信号,并模拟它们与深度层的交互”,优于 YouTube 之前使用的任何模型。
关键见解:
- 使用训练样本的年龄作为输入特征消除了对过去的固有偏见,并有助于模型表示流行视频的时间相关行为。
- 排名是一种更经典的机器学习方法,但深度学习模型优于经典的线性预测模型。
- 通过用正面例子的观察时间和负面例子的单位加权训练例子来修改逻辑回归,允许更准确地学习赔率。
YouTube、Spotify 和网飞是我生活中的主要玩家。我每天都使用这些服务进行娱乐或教育。我很少在 YouTube 上手动搜索视频,非常依赖他们的推荐系统。学习一个看似简单的概念背后复杂的数学过程让我大开眼界。数据科学每天都在影响着人们的生活,我迫不及待地想要开始。
链接到研究论文:
使用降维来可视化工作极化
PC1 and PC2 extracted from the MDS Embedding using 2003 data. Each point represents a job, and each color represents a job zone. The smaller the job zone, the less education requirement/experience it requires.
在本帖中,我们使用来自美国政府支持的网站 O*NET 的数据,说明了如何使用包括主成分分析(PCA)和多维标度(MDS)在内的降维技术来可视化自 2003 年以来工作活动的两极分化,该网站提供了一组描述工作和员工特征的丰富变量。
让我们简要回顾一下 O*NET 的数据结构,以理解为什么使用降维技术会有用。数据集是围绕每项工作所需的知识、技能、能力和工作活动的不同组合来组织的。它包含了覆盖整个美国经济的近 1000 个职业的数百个标准化和特定职业变量。
为了进行这一分析,我们使用了 2003 年 11 月和 2018 年 8 月的工作活动文件,来比较在这 15 年间不同工作岗位上的活动是如何变化的。有 41 种不同的工作活动,如“处理和移动对象”和“解决冲突和与他人谈判”。在该文件中,为每个职业-活动-规模组合分配一个数据值。一般来说,数据值越高,活动对工作越重要。例如,在 2018 年的文件中(如下面的截图所示),使用“重要性”衡量标准,职务“首席执行官”执行活动“获取信息”的数据值为 4.72。
主成分分析
为了理解在不同工作中执行的工作活动是如何变化的,人们可以随着时间的推移对每项工作分别跟踪这 41 项活动中的每一项,但这可能会很快失控。PCA 可以通过减少我们需要跟踪的活动数量来帮助解决这个问题。具体地,它通过将数据变换到新的坐标系来实现,使得第一坐标(即,第一主分量)表示具有最大数据方差的投影,第二坐标上的第二大方差,等等。
下面的代码使用 scikit-learn 库在 python 中实现了 PCA。它首先标准化特征比例:
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler#df is the dataframe that contains the value of each of the 41 activities for all jobsx = df.loc[:, features].values
x = StandardScaler().fit_transform(x)pca = PCA(n_components=num_dim)
#num_dim is the number of dimension we want to examine. We set num_dim to be 2.
principalComponents = pca.fit_transform(x)
下图描绘了两个时期的主要组成部分。为了说明需要不同教育水平的工作因其所从事的工作活动而有所不同,我们通过工作所属的工作区对每个工作(由散点图上的点表示)进行了颜色编码。教育是确定工作领域的标准之一,其他标准包括经验和培训。工作区域越高,工作需要的准备就越多。属于工作区 1 的职业有时需要高中文凭,而工作区 5 的职业通常需要研究生学位。
如果两极分化日益加剧,高技能群体和低技能群体从事的活动将会更加不同。这正是我们在比较这两个时期的地块时所看到的。2018 年,相似颜色的工作更紧密地聚集在一起,这表明属于不同区域的工作之间的工作活动几乎没有重叠。实际计算显示,需要较少准备的工作(1 区和 2 区的工作)在 2018 年的 PC1 值比 2003 年大。2003 年,第 4 区(黄色部分)的工作要求相对更高的教育水平,更分散在 x 轴上。相反,在 2018 年,他们大多位于 0 的左侧,这意味着他们更多地聚集在一起。
PC1 and PC2 using PCA, 2003 on the left, 2018 on the right
为了更严格地检验这种模式,我们可以计算不同工作区的工作之间的距离。我们首先确定工作区聚类的质心,然后计算工作区质心之间的距离。2003 年,工作区 1 和 5 之间的距离为 5.7,2018 年增加到 6.7。工作区 1 和 4 之间以及工作区 2 和 5 之间距离也有类似的增加。
解读主成分
PCs 本身没有任何实质意义,因此为了将主成分与工作活动联系起来,我们可以检查 PCs 和工作活动本身之间的标准皮尔逊相关性。对于 2003 年,PC1 与“检查设备、结构或材料”以及其他体力劳动密集型活动的相关性最强。另一方面,PC2 与“处理信息”和“获取信息”等认知活动的相关性最强。
请注意,虽然与 PC1 相关性最高的前 3 项活动在 2003 年和 2018 年都是相同的,但 PC2 却不是这样。对于 PC2 来说,“决策和解决问题”在 2018 年出现,但在 2003 年没有,而“获取信息”在 2003 年出现,但在 2018 年没有。因此,尽管这两个维度捕捉了两个时期类似性质的活动,但它们并不完全相同。
PC1 使用 PCA:
使用 PCA 的 PC2:
多维标度
另一个让研究人员可以看到不同工作岗位之间日益增长的两极分化的方法是 MDS。MDS 试图做的是保持工作之间的成对距离,同时将矩阵投影到更低的维度。MDS 模型的输入是作业之间的距离矩阵,而不是 PCA 中每个作业的数据值。因此,要将职务活动数据转换为适合 MDS 的距离矩阵,我们首先将数据值矩阵乘以它本身。下面的代码说明了这一步,以及距离矩阵在嵌入中的拟合:
from sklearn.manifold import MDS### multiplying the x matrix with itself
t = np.dot(x, np.transpose(x))
mds = MDS(n_components= n_dimension, max_iter=3000, eps=1e-9, random_state=12345,
dissimilarity=”precomputed”, n_jobs=1)
pos = mds.fit(t).embedding_
之后,我们可以像以前一样从 MDS 嵌入空间中数据集的位置提取前两个主分量:
# select the top 2 dimensions of data
clf = PCA(n_components=2)
pos = clf.fit_transform(pos)
与 PCA 分析类似,2018 年的第一个主成分捕捉了“搬运和移动物体”等更为体力劳动密集型的活动。然而,在 2003 年,第一个主成分从以前的 PCA 分析中捕捉到不同类型的活动。与体力劳动密集型活动不同,PC1 与更多的管理活动最相关,例如“制定目标和策略”以及“向他人提供咨询和建议”。这种差异揭示了使用 PCA 时不明显的另一个方面。
PC1 使用 MDS:
使用 MDS 的 PC2:
下图显示了 2003 年(左)和 2018 年(右)MDS 植入的主要组成部分。作为第一印象,不同颜色的工作在 2003 年比 2018 年更加相互混合,这意味着沿着两个主要维度,属于不同工作区的工作之间的活动重叠更多。此外,属于作业区 4 或 5 的作业在 2018 年都没有 PC1 < 0(所有红绿点都位于 PC1 中 0 的左侧)。这两种模式表明,随着时间的推移,每项工作所执行的活动变得更加分散。
PC1 and PC2 using MDS, 2003 on the left, 2018 on the right
总之,这两种方法都说明了需要不同准备程度的工作所执行的工作活动的日益两极分化,但它们也可以揭示数据微妙之处的差异。
使用 Docker & Kubernetes 托管机器学习模型
Source: https://www.pexels.com/photo/baltic-sea-blue-sea-cargo-ship-container-port-2945314/
第 4 章“生产中的数据科学”节选
Docker 是在云中部署 ML 模型的绝佳工具。如果你想在云中建立一个生产级的部署,AWS 和 GCP 有很多选择。在我正在编写的书的第 4 章中,我将重点放在为模型服务的 ECS 上,但也将在最后探索 Google Kubernetes 引擎。这篇文章省略了关于部署容器的 AWS 部分,但是包括了本章的其余部分。
从初创公司到数万亿美元的公司,数据科学在帮助组织最大化…
leanpub.com](https://leanpub.com/ProductionDataScience)
第 4 章—可重现模型的容器
部署数据科学模型时,能够重现用于培训和服务的相同环境非常重要。在第 2 章中,我们在两个环境中使用了相同的机器,在第 3 章中,我们使用了一个requirements.txt
文件来确保用于模型服务的无服务器生态系统与我们的开发环境相匹配。Docker 之类的容器系统提供了一种构建可复制环境的工具,并且它们比虚拟机之类的替代方法要轻得多。
容器的概念是,它是一个隔离的环境,您可以在其中设置执行任务所需的依赖关系。任务可以是执行 ETL 工作、服务 ML 模型、建立 API 或者托管交互式 web 应用程序。容器框架的目标是提供轻量级实例之间的隔离。使用容器框架,您可以指定代码需要的依赖项,并让框架处理管理不同执行环境的跑腿工作。Docker 是容器的事实上的标准,有大量的工具建立在这个平台上。
弹性容器环境(如弹性容器服务器(ECS ))提供了与无服务器功能类似的功能,在这种环境中,您希望从托管数据科学模型中抽象出服务器的概念。关键的区别在于,无服务器生态系统受限于特定的运行时,通常具有内存限制,这使得使用深度学习框架具有挑战性,并且是云特定的。使用 ECS,您负责设置用于服务模型的实例类型,您可以使用服务模型所需的任何语言,并且可以根据需要占用尽可能多的内存。ECS 作为一个专有的 AWS 工具仍然有问题,但是更新的选项,比如 EKS,是建立在开源的可移植的 Kubernetes 之上的。
以下是我见过的一些关于容器的数据科学用例:
- **可重复的分析:**容器提供了一个打包分析的好方法,这样其他团队成员可以在几个月或几年后重新运行你的工作。
- web 应用程序:在第二章中,我们用 Dash 构建一个交互式 Web 应用程序。容器提供了一种很好的方式来抽象出部署应用程序的托管问题。
- **模型部署:**如果您想要将您的模型公开为一个端点,容器提供了一种将模型应用程序代码从模型服务基础设施中分离出来的好方法。
本章的重点将是最后一个用例。我们将从第 2 章中取出 web 端点,并将应用程序包装在 Docker 容器中。我们将从在 EC2 实例上本地运行容器开始,然后探索使用 ECS 来创建模型的可伸缩、负载平衡和容错的部署。然后我们将展示如何使用 Kubernetes 在 GCP 上实现类似的结果。
既然我们正在探索可扩展的计算环境,那么在使用 ECS 和 GKE 时关注云成本就非常重要。对于 AWS 来说,关注提供了多少 EC2 实例是很有用的,在 GCP 上,计费工具提供了对成本的良好跟踪。关于编排的部分是专门针对 AWS 的,并且使用了一种不能移植到不同云环境的方法。如果 AWS 不适合您的模型部署,可以直接跳到 Kubernetes 一节。
4.1 码头工人
Docker 和其他平台即服务工具提供了一个称为容器的虚拟化概念。容器运行在主机操作系统之上,但是为在容器内运行的代码提供了一个标准化的环境。这种虚拟化方法的关键目标之一是,您可以为目标环境编写代码,任何运行 Docker 的系统都可以运行您的容器。
容器是虚拟机的轻量级替代,虚拟机提供类似的功能。关键的区别在于,容器的运行速度更快,同时提供了与虚拟机相同的隔离级别。另一个好处是容器可以重用其他容器中的层,这使得构建和共享容器变得更快。当您需要在一台机器上运行 Python 运行时或库的冲突版本时,容器是一个很好的解决方案。
使用 docker,您可以创建一个名为 Dockerfile 的文件,用于定义容器的依赖关系。构建 Docker 文件的结果是一个 Docker 映像,它打包了运行一个应用程序所需的所有运行时、库和代码。 Docker 容器是运行应用程序的实例化映像。Docker 的一个有用特性是新图像可以建立在现有图像的基础上。对于我们的模型部署,我们将扩展ubuntu:latest
图像。
本节将展示如何在 EC2 实例上设置 Docker,为构建第 2 章中 echo 服务的映像创建 Docker 文件,使用 Docker 构建映像,并运行容器。要在 EC2 实例上安装 Docker,可以使用amazon-linux-extras
工具来简化这个过程。下面的命令将安装 Docker,在 EC2 实例上启动服务,并列出正在运行的容器,这将返回一个空列表。
sudo yum install -y python3-pip python3 python3-setuptools
sudo yum update -y
sudo amazon-linux-extras install docker
sudo service docker start
sudo docker ps
我们将部署的应用程序是第 2 章中的 echo 服务。这个服务是一个 Flask 应用程序,它解析 GET 或 POST 中的msg
属性,并返回一个 JSON 有效负载,回显所提供的消息。与之前的应用程序的唯一区别是 Flask 应用程序现在运行在端口 80 上,如下面的echo.py
片段中的最后一行所示。
*# load Flask*
import flask
app = **flask.Flask**(__name__)*# define a predict function as an endpoint*
@**app.route**("/predict", methods=["GET","POST"])
def **predict**():
data = {"success": False} *# get the request parameters*
params = flask.request.json
**if** (params == None):
params = flask.request.args *# if parameters are found, echo the msg parameter*
**if** (params != None):
data["response"] = **params.get**("msg")
data["success"] = True *# return a response in json format*
return **flask.jsonify**(data)
*# start the flask app, allow remote connections*
**app.run**(host='0.0.0.0', port = 80)
现在我们已经安装了 Docker 和一个我们想要容器化的应用程序,我们需要编写一个 Docker 文件来描述如何构建一个映像。执行该任务的 docker 文件如下面的代码片段所示。第一步是使用FROM
命令确定要使用的基本图像。ubuntu
映像提供了一个支持apt-get
命令的 linux 环境。MAINTAINER
命令添加了与图像相关的元数据信息,添加了图像维护者的名字。接下来,使用RUN
命令安装 python,设置一个符号链接,并安装 Flask。对于包含许多 Python 库的容器,也可以使用 requirements.txt 文件。Copy
命令将我们的脚本插入到映像中,并将文件放在根目录中。最后一个命令指定了执行应用程序所要运行的参数。
FROM ubuntu:latest
MAINTAINER Ben Weber RUN apt-get update \
&& apt-get install -y python3-pip python3-dev \
&& cd /usr/local/bin \
&& ln -s /usr/bin/python3 python \
&& pip3 install flask
COPY echo.py echo.py ENTRYPOINT ["python3","echo.py"]
编写 docker 文件后,可以使用 docker 提供的build
命令来创建图像。下面代码片段中的第一个命令展示了如何使用文件./Dockerfile
构建一个标记为echo_service
的图像。第二个命令显示实例上可用的 Docker 图像列表。输出将显示我们用作图像基础的 ubuntu 图像,以及我们新创建的图像。
sudo docker image build -t "echo_service" .
sudo docker images
要将图像作为容器运行,我们可以使用下面代码片段中显示的 run 命令。-d
标志指定容器应该作为守护进程运行,即使关闭终端,守护进程也将继续运行。-p
标志用于将主机上的端口映射到容器用于通信的端口。如果没有这个设置,我们的容器将无法接收外部连接。ps
命令显示了正在运行的容器列表,现在应该包括 echo 服务了。
sudo docker run -d -p 80:80 echo_service
sudo docker ps
为了测试容器,我们可以使用与之前相同的过程,在 web 浏览器中使用 EC2 实例的外部 IP,并将一个msg
参数传递给/predict
端点。因为我们设置了从主机端口 80 到容器端口 80 的端口映射,所以我们可以通过 open web 直接调用容器。来自 echo 服务容器的调用和结果示例如下所示。
http:**//**34.237.242.46/predict?msg=Hi_from_docker{"response":"Hi_from_docker","success":true}
我们现在已经完成了构建 Docker 映像并在 EC2 实例上将该映像作为容器运行的过程。虽然这种方法确实提供了一种在一台机器上隔离不同服务的解决方案,但是它不提供伸缩性和容错性,而这是生产级模型部署的典型要求。
4.3 的 Kubernetes
谷歌云平台提供了一项名为谷歌 Kubernetes 引擎(GKE)的服务,用于服务 Docker 容器。Kubernetes 是一个容器编排系统,最初由 Google 开发,现在是开源的。这个平台有各种各样的用例,但是我们将把重点放在使用托管 Kubernetes 托管我们的 echo 服务的特定任务上。
使用 Kubernetes 托管 Docker 容器类似于 ECS,第一步是将您的图像保存到 Docker 注册表中,该注册表可以与编排系统交互。这个注册服务的 GCP 版本被称为容器注册。为了将我们的映像从 AWS 上的 EC2 实例发送到 GCP 容器注册中心,我们将再次使用 docker login 命令。为了让这个过程工作,您需要我们在第 1 章中设置的 GCP 凭证 json 文件。下面的代码片段展示了如何将 json 文件传递给 docker login 命令,标记图像以将其上传到注册表,并将图像推送到容器注册表。
cat dsdemo.json | sudo docker login -u _json_key
--password-stdin https:**//**us.gcr.io
sudo docker tag echo_service us.gcr.io/[gcp_account]/echo_service
sudo docker push us.gcr.io/[gcp_account]/echo_service
您需要用完整的 google 帐户 ID 替换这个脚本中的gcp_acount
参数。执行完这些步骤后,echo 服务映像应该在 GCP 控制台的注册表视图下可见,如图 4.8 所示。通常,如果您使用 GCP 来服务模型,很可能您将使用 Google Compute instances 而不是 EC2,但是最好练习不同云平台中的组件之间的接口。
图 4.8:GCP 集装箱登记处的回波图像。
与使用 ECS 所需的所有步骤相比,使用 GKE 托管容器的过程得到了简化。我们将首先使用 GCP 控制台在 Kubernetes 上设置一个容器,然后将服务公开给 open web。要部署 echo 服务容器,请从 GCP 控制台执行以下步骤:
- 搜索并选择“Kubernetes 引擎”
- 单击“部署容器”
- 选择“现有容器图像”
- 选择
echo_service:latest
- 将应用程序命名为“echo-kge”
- 单击“部署”
我们现在已经部署了一个 Kubernetes 集群,并准备好为 echo 服务提供服务。在 GKE 上部署 Kubernetes 集群可能需要几分钟的时间。一旦部署完成,您应该会在集群列表下看到 echo 集群,如图 4.9 所示。
图 4.9:通过 Kubernetes 部署的 echo 图像。
要使用该服务,我们需要通过从 GCP 控制台执行以下步骤将集群公开到开放的 web:
- 从 GKE 菜单中,选择您的集群
- 点击“工作负载”
- 选择“echo-gke”工作负载
- 选择“操作”选项卡,然后选择“公开”
- 对于服务类型,选择“负载平衡器”
执行完这些步骤后,集群将配置一个可以用来调用服务的外部 IP,如图 4.10 所示。GKE 将根据需要自动进行负载平衡和扩展服务,以匹配工作负载。
图 4.10:部署到开放 web 的 echo 服务。
http:**//**35.238.43.63/predict?msg=Hi_from_GKE{"response":"Hi_from_GKE","success":true}
上面的代码片段显示了一个使用该服务的示例。我们能够快速获取 Docker 映像,并使用 GKE 将其部署在 Kubernetes 生态系统中。使用 Kubernetes 来托管 Docker 图像是一件好事,因为它是一个可移植的解决方案,可以跨多个云环境工作,并且被许多开源项目所采用。
4.4 结论
使用容器来确保您的分析和模型在不同的环境中是可重复的是非常有用的。虽然容器有助于保持单个机器上的依赖关系干净,但主要的好处是它们使数据科学家能够编写模型端点,而不用担心容器将如何被托管。这种关注点的分离使得与工程团队合作将模型部署到生产中变得更加容易,或者使用本章中显示的方法,数据和应用科学团队也可以拥有模型到生产中的部署。
服务模型的最佳方法取决于您的部署环境和预期的工作负载。通常,在公司工作时,您会受限于特定的云平台,因为您的模型服务可能需要与云中的其他组件进行交互,例如数据库或云存储。在 AWS 中,托管容器有多种选择,而 GCP 在 GKE 上作为单一解决方案。要问的主要问题是,使用无服务器功能技术还是弹性容器技术来为您的模型提供服务更具成本效益。正确答案将取决于您需要处理的流量、最终用户可以容忍的延迟量以及您需要托管的模型的复杂性。容器化的解决方案非常适合服务于复杂的模型,并确保您可以满足延迟要求,但与无服务器功能相比,可能需要更多的 DevOps 开销。
本·韦伯是 Zynga 的一名杰出的数据科学家。我们正在招聘!
使用电子健康记录预测具有门控循环单元的未来诊断代码
背景:AI 医生的详细综述:通过递归神经网络预测临床事件(Choi et.al 2016)
电子病历(EMRs)有时也称为电子健康记录(EHRs ),主要用于以电子方式数字化存储患者健康数据。虽然这些系统的使用在今天似乎很常见,但最显著的是由于 2014 年通过了《经济和临床健康法案卫生信息技术》。美国医疗机构的适应性实施非常缓慢。尽管如此,EMR 电子病历系统现在拥有丰富的纵向患者数据,可以使我们更接近开发以患者为中心的个性化医疗保健解决方案。也就是说,EHR 的数据可能非常杂乱和稀疏。尽管存在这些挑战,但如果利用得当,EHR 数据可以提供关于患者旅程、治疗模式的真实世界数据洞察,预测患者的下一个诊断代码或再入院风险和死亡率等。
截至 2018 年,据报道,仅医疗保健数据就占全球数据产量的 30%左右。因此,众所周知,许多公司和投资者将医疗保健视为下一个重大投资。然而,为了在患者层面提供真正的解决方案,我们需要了解如何利用和处理我们手头的大量数据。为此,本教程将重点讨论如何处理人工智能算法中使用的 EHR 数据。希望有了这种使用模拟数据的洞察力,我们可以让更多的数据科学家和爱好者参与到医疗保健数据的民主化中来,并使 EHR 数据在患者层面上变得可行。
为了这个三部分教程的目的,我们生成了一些人工 EHR 数据来演示 EHR 数据应该如何被处理以用于序列模型。请注意,这些数据与临床无关,仅用于培训目的。
本教程分为以下几个部分:
**第一部分:**生成人工 EHR 数据
**第二部分:**预处理人工生成的 EHR 数据
**第三部分:**艾博士 Pytorch 极简实现
完全实现: 使用快速 AI API
如果你需要快速查看 GRUs 的内部运作,请参见 门控循环单元查看 。
Github 代码:https://github.com/sparalic/Electronic-Health-Records-GRUs
第 1 部分:生成人工电子健康记录(EHR)数据
病人入院表
此表包含患者入院历史和时间的信息。生成的特征有:
PatientID
-永久留在患者身上的唯一标识符Admission ID
-具体到每次就诊AdmissionStartDate
-入院日期和时间AdmissionEndDate
-特定入院 ID 护理后的出院日期和时间
患者诊断表
诊断表非常独特,因为它可以包含同一就诊的多个诊断代码。例如,患者 1 在他/她的第一次就诊(Admission ID
:12)期间被诊断患有糖尿病(PrimaryDiagnosisCode
:E11.64)。但是,这个代码在后续的访问中也会出现(Admission ID
:34,15),这是为什么呢?如果一个病人被诊断出患有不可治愈的疾病,他/她这个代码将会与所有后续的就诊相关联。另一方面,与紧急护理相关的代码会随着PrimaryDiagnosisCode
:780.96(头痛)来来去去。
用于将数据从字典解析到数据帧的辅助函数
DataFrame of artificially generated EHR data
为入场 ID 创建一个哈希键
为什么要做这一步?除非您的 EHR 系统对每个就诊患者都有唯一可识别的入院 ID,否则很难将每个患者 ID 与唯一的Admission ID
相关联。为了证明这一点,我们特意创建了两位数的Admission ID
s,其中一个为两位患者重复(Admission ID
: 34)。为了避免这种情况,我们采取了预防措施,创建了一个散列键,它是唯一PatientID
的前半部分与患者特定Admission ID
的唯一组合。
用假 EHR 数据生成的最终入院和诊断表
Admission table with artificially generated data
Diagnosis table with artificially generated d
将表格写入 csv 文件
第 2 部分:预处理人工生成的 EHR 数据
在本节中,我们将演示如何处理数据,为建模做准备。本教程的目的是详细介绍如何使用 Pytorch 对 EHR 数据进行预处理以用于 RNNs。这篇论文是为数不多的提供代码基础的论文之一,它开始详细研究我们如何建立利用时间模型预测未来临床事件的通用模型。然而,尽管这篇被高度引用的论文是开源的(使用 https://github.com/mp2893/doctorai 的来写),它还是假设了很多关于它的读者。因此,我们在 python 3+中对代码进行了现代化,以方便使用,并提供了每个步骤的详细解释,以允许任何人使用计算机和访问医疗保健数据,开始尝试开发创新的解决方案来解决医疗保健挑战。
重要免责声明:
这个数据集是在本系列的第 1 部分中用两个病人人工创建的,以帮助读者清楚地理解 EHR 数据的基本结构。请注意,每个 EHR 系统都是专为满足特定提供商的需求而设计的,这只是大多数系统中通常包含的数据的一个基本示例。此外,还需要注意的是,本教程是在与您的研究问题相关的所有期望的排除和纳入标准都已执行之后开始的。因此,在这一步,您的数据将会得到充分的整理和清理。
负载数据:快速回顾一下我们在第 1 部分中创建的人工 EHR 数据:
步骤 1:创建患者 id 的映射
在这一步中,我们将创建一个字典,将每个患者与他或她的特定就诊或Admission ID
对应起来。
步骤 2:创建映射到每个独特患者的诊断代码并进行访问
该步骤与所有后续步骤一样非常重要,因为将患者的诊断代码保持在正确的就诊顺序非常重要。
步骤 3:将诊断代码嵌入就诊映射患者入院映射
该步骤实质上是将分配给患者的每个代码添加到字典中,其中包含患者入院 id 映射和就诊日期映射visitMap
。这使我们能够获得每位患者在每次就诊期间收到的诊断代码列表。
步骤 4a:提取患者 id、就诊日期和诊断
在此步骤中,我们将创建所有诊断代码的列表,然后在步骤 4b 中使用该列表将这些字符串转换为整数以进行建模。
步骤 4b:为每个独特的患者创建每次就诊时分配的独特诊断代码的字典
在这里,我们需要确保代码不仅被转换为整数,而且它们以唯一的顺序保存,即它们被用于每个唯一的患者。
步骤 6:将数据转储到一个 pickled list 列表中
完整脚本
第 3 部分:AI Pytorch 医生最小实现
我们现在将把从本系列的 GRUs 教程和第 1 部分中获得的知识应用到一个更大的公开可用的 EHR 数据集。本研究将利用 MIMIC III 电子健康记录(EHR)数据集,该数据集由超过 58,000 例住院病历组成,其中包括 38,645 例成人和 7,875 例新生儿。该数据集收集了 2001 年 6 月至 2012 年 10 月期间在贝斯以色列女执事医疗中心的特护病房住院患者。尽管被去识别,该 EHR 数据集包含关于患者的人口统计学信息、在床边进行的生命体征测量(约 1 次/小时)、实验室测试结果、账单代码、药物、护理人员注释、成像报告和死亡率(住院期间和之后)的信息。使用在(第 1 部分&第 2 部分)中人工生成的数据集上演示的预处理方法,我们将创建一个用于本研究的同伴队列。
模型架构
Doctor AI model architecture
检查 GPU 可用性
这个模型是在支持 GPU 的系统上训练的…强烈推荐。
加载数据
数据预处理数据集将被加载,并按75%:15%:10%
比率分成训练、测试和验证集。
填充输入
输入张量用零填充,注意输入被填充以允许 RNN 处理可变长度输入。然后创建一个掩码来提供关于填充的算法信息。注意这可以使用 Pytorch 的实用程序pad_pack_sequence
函数来完成。然而,考虑到这个数据集的嵌套性质,编码的输入首先被多对一热编码。这种偏离过程创建了高维稀疏输入,然而该维度随后使用嵌入层被投影到低维空间中。
GRU 级
这个类包含开始计算算法的隐藏状态所需的随机初始化的权重。请注意,在本文中,作者使用了使用 skip-gram 算法生成的嵌入矩阵(W_emb ),这优于该步骤中所示的随机初始化方法。
用于处理两层 GRU 的自定义层
这个类的目的是执行最初的嵌入,然后计算隐藏状态并执行层间的分离。
火车模型
这个模型是 Edward Choi 创建的 Dr.AI 算法的最小实现,而功能性的它需要大量的调整。这将在后续教程中演示。
最终注意事项/后续步骤:
这应该作为启动和运行模型的起始代码。如前所述,需要进行大量的调优,因为这是使用定制类构建的。本教程的第 2 部分展示了本文的完整实现。
参考资料:
- 艾医生:通过递归神经网络预测临床事件(【https://arxiv.org/abs/1511.05942】)
利用 Fastai 进行图像分类
了解如何使用 fastai 快速构建最先进的图像分类器
我最近偷看了杰瑞米·霍华德关于深度学习的 2019 课程。我以前从未使用过Fastai
库,所以我对它的抽象程度感到非常惊讶,它允许你在几分钟内用少得可笑的代码创建最先进的神经网络。在接下来的文章中,我将提供一个关于用Fastai
构建 CNN 图像分类器的简短教程,它应该同时作为对我自己的关键概念的有用总结和对新手的清晰概述。
我选择了 Kaggle 的植物幼苗数据集作为示例数据集。
关于 Fastai
如果你还没听说过Fastai
,我推荐你看看他们的主页。在他们的使命声明中,他们不仅希望加速和帮助深度学习研究,还希望降低每个人的准入门槛。这句台词来自他们 2017 年 9 月为Fastai
推出的PyTorch
(来源):
Everybody should be able to use deep learning to solve their problems with no more education than it takes to use a smart phone. Therefore, each year our main research goal is to be able to teach a wider range of deep learning applications, that run faster, and are more accurate, to people with less prerequisites.
即使你看了Fastai
的 2019 年课程,你也可以感受到这种雄心,这让我对该领域的发展方向有了良好的感觉。正如在研究中经常发生的那样,知识和工具只对特定的少数人开放。这在深度学习研究中更加明显,你需要强大的 GPU 中的海量 RAM 来解决大量问题(查看这个无关的视频关于 GPU vs CPU)。
此外,现在许多云计算平台都提供了对Fastai
的支持,比如 Paperspace 、 Cradle 和 AWS 等等。但是由于这些服务都是要花钱的,我将坚持谷歌最近刚刚宣布的Colaboratory
,让你免费使用谷歌的 GPU。是的,零成本!活着真是太不可思议了。
设置 Google Colab
为了避免网上的冗余,请查看由 Manikanta Yadunanda 撰写的这篇中的帖子,快速了解如何使用谷歌合作实验室(Colab)和Fastai
。自从这篇介绍写完之后,Google 已经在 Colab 中包含了官方的Fastai
和PyTorch
支持,所以你可能甚至不需要在连接到运行时之后安装它。您可以使用下面的代码行检查是否所有重要的pip
包都已安装。如果没有,取消最后一行的注释,用它来为 Python 3.6.x 和 CUDA 9.x 安装Fastai
和PyTorch
克隆此笔记本
要访问这个笔记本并自己运行计算,您可以直接从我的 GitHub 库导入它。只需进入文件…打开笔记本…,选择 GitHub 选项卡,并在搜索栏中输入“verrannt/Tutorials”。选择‘fastai-plant-sleeves-classification . ipynb’就大功告成了。就这么简单。检查 Colab 是否配置了Fastai
支持后,您可以继续数据准备部分。
数据准备
获取数据
由于我们使用 Kaggle 作为我们的数据集供应,您需要一个 Kaggle 帐户来下载数据集。如果你有一个,到你的帐户设置中去,获得一个新的 API 密匙,你可以在 Kaggle CLI 中使用。
以下代码安装 CLI 并注册新的 API 密钥。然后我们下载植物幼苗数据集并解压。确保您位于 fastai 目录的/content 文件夹中。
检查数据
让我们检查数据。我们从 Kaggle 下载并提取的文件夹有 12 个子文件夹,每个子文件夹对应一种类型的幼苗,里面有各自的图像。这些将是我们分类任务的标签。
如果我们打印这些文件夹内容的长度,我们可以看到每个文件夹包含不同数量的图像。差异很大,例如,“松散的丝般弯曲”的图像最多(762),而“普通小麦”的图像最少(253)。我们将在后面看到这是否意味着预测精度的不同。
这将输出以下内容:
No. of labels: 12
-----------------
Small-flowered Cranesbill, 576 files
Common wheat, 253 files
Charlock, 452 files
Sugar beet, 463 files
Maize, 257 files
Black-grass, 309 files
Loose Silky-bent, 762 files
Fat Hen, 538 files
Cleavers, 335 files
Shepherd’s Purse, 274 files
Scentless Mayweed, 607 files
Common Chickweed, 713 files
让我们来看看这些图像。对于 12 个标签中的每一个,我们将随机打印一个幼苗。
好吧,他们看起来很好,很容易区分,除了“松散的丝般弯曲”和“黑草”。这些对网络来说可能更难识别,但是我们会看到的。我们开始吧!
创建 Fastai 模型
我们现在可以使用Fastai
库创建 CNN 模型。自从对 v1 进行重大更新以来,它变得更加清晰和一致,因此我们只需要导入我们的度量标准的视觉模块和准确性。
from fastai.vision import *
from fastai.metrics import accuracy
有一个非常好的类来处理与视觉任务的输入图像相关的一切。它被称为[ImageDataBunch](https://docs.fast.ai/vision.data.html#ImageDataBunch)
,具有不同的功能,以不同的方式将数据呈现给网络。由于我们的图像被放在名称与图像标签相对应的文件夹中,我们将使用ImageDataBunch.from_folder()
函数创建一个包含图像数据的对象。这非常有用,可以非常容易地将数据读入我们的模型,稍后您会看到这一点。
更方便的是,Fastai
可以自动将我们的数据分成训练集和验证集,所以我们甚至不需要自己创建它们。
我们现在需要的唯一超参数是指向我们数据集的路径变量、输入的大小和每个梯度下降迭代的批量大小。为了简单起见,ImageDataBunch
对象会将所有图像缩放到一个尺寸*尺寸的平方图像,除非另有指示。
关于图像大小的一个快速提示:图像越大,CNN 能够从中提取的细节就越多。同时,更大的图像意味着更长的计算时间。同样,你的 GPU 可能会因为批量过大而耗尽内存。如果是这种情况,您可以将批量大小减半。
path = “./plant_seedlings-data/”
size = 224
bs = 64
我们将创建一个名为data
的变量,在其中放置ImageDataBunch
对象。我们用上面讨论过的from_folder()
函数创建这个对象。在获取数据、图像和批次大小的路径中,还需要:
- 一个名为
get_transforms()
的函数参数,它在调用时返回可用图像转换的列表。 - 参数
valid_pct
,控制将被随机选择在验证集中的图像的百分比 - 参数
flip_vert
除了控制水平翻转外,还控制垂直翻转和 90°旋转。(因为我们的植物图像是从上面拍摄的,所以我们可以毫无问题地执行这些操作,这在例如面部数据上是不可行的。)
为了规范化我们对象中的数据,我们简单地在对象上调用normalize()
。这里可以使用 ImageNet 、 CIFAR 或 MNIST stats 作为模板,如果留空,这个函数将简单地从我们的对象中抓取一批数据,并计算它的统计数据(平均值和标准差)并相应地归一化数据。因为我们将为在 ImageNet 上训练的模型使用 ResNet 架构,所以我们将使用 ImageNet stats。
data.**normalize**(imagenet_stats)
这将输出一个摘要:
ImageDataBunch;
Train: LabelList
y: CategoryList (4432 items)
[Category Small-flowered Cranesbill, Category Small-flowered Cranesbill, Category Small-flowered Cranesbill, Category Small-flowered Cranesbill, Category Small-flowered Cranesbill]...
Path: plant-seedlings-data
x: ImageItemList (4432 items)
[Image (3, 237, 237), Image (3, 497, 497), Image (3, 94, 94), Image (3, 551, 551), Image (3, 246, 246)]...
Path: plant-seedlings-data;
Valid: LabelList
y: CategoryList (1107 items)
[Category Maize, Category Black-grass, Category Common Chickweed, Category Cleavers, Category Charlock]...
Path: plant-seedlings-data
x: ImageItemList (1107 items)
[Image (3, 529, 529), Image (3, 945, 945), Image (3, 171, 171), Image (3, 125, 125), Image (3, 163, 163)]...
Path: plant-seedlings-data;
Test: None
就是这样,两行代码优化了我们的训练数据集,增加了不同种类的转换和规范化!我想请你在这里停一会儿,花一秒钟来欣赏它的美丽。高水平图书馆之美;仅仅两行代码,我们就极大地增加了数据集的多样性。我已经可以听到我丑陋的 batchnorm 代码在垃圾桶里哭泣。
现在剩下的就是创建实际的网络并训练它,这再简单不过了。
Fastai
在其视觉模块中为我们提供了一个名为[create_cnn()](https://docs.fast.ai/vision.learner.html#create_cnn)
的功能。这个函数创建了一个叫做learner
的对象,我们将把它放入一个适当命名的变量中。请注意,我们将 ResNet 架构指定为迁移学习的基础模型。一旦调用,经过训练的架构将通过Fastai
API 下载并存储在本地。
我们将使用准确性作为我们的衡量标准。如果您查看文档,您可以看到其他可用指标的列表。定义回调函数ShowGraph
只是告诉学习者,无论它做什么,它都应该返回一个图,这对我看模型是否仍在改进非常有用。
learner = **create_cnn**(data, models.resnet18, metrics=[accuracy], callback_fns=ShowGraph)
寻找学习率
我们创建的学习者对象带有一个内置函数,可以为训练找到最佳的学习率或学习率范围。它通过拟合几个时期的模型并保存损失减少最多的学习率来实现这一点。
我们希望选择一个学习率,其损失*仍在减少,*即我们不希望学习率具有最小损失,而是具有最大斜率。
在下面的图中,存储在我们的学习器的记录器对象中,我们可以看到学习率在 0.001 和 0.01 之间的情况。
learner.lr_find()
learner.recorder.plot()
首次拟合和评估
现在让我们用 0.001 到 0.01 之间的学习率来拟合 8 个时期的模型
learner.fit_one_cycle(8, max_lr=slice(1e-3, 1e-2))
看起来已经很好了!在最初几个 1/5 的迭代中,损耗减少了很多,之后会减少,但会持续减少。
让我们看看算法在哪里犯的错误最多:
interpreter = ClassificationInterpretation.from_learner(learner)interpreter.**most_confused**(min_val=2)## OUTPUTS:
[(‘Black-grass’, ‘Loose Silky-bent’, 28),
(‘Loose Silky-bent’, ‘Black-grass’, 7),
(‘Shepherd’s Purse’, ‘Scentless Mayweed’, 4)]
这向我们展示了这种算法最常混淆“黑草”和“松丝弯”这两个类别。我们已经在之前展示的样本图像中看到,这些图像看起来最相似,因此这是有意义的。
改进模型
解冻和微调
在我们解冻层并再次学习之前,我们保存权重,以便在我们搞砸的情况下可以返回。
learner.save(‘stage-1’)
#learner.load(‘stage-1’)learner.unfreeze()
learner.fit_one_cycle(12, max_lr=slice(1e-5, 1e-4))
我们将坚持这一点,因为验证错误比测试错误更严重,看起来这种趋势只会增加。如果我们从这一点开始继续训练,模型将开始过度拟合训练数据!
干得好,我们成功地为一个定制数据集训练了一个最先进的图像分类器,仅用几行代码就达到了 96.5%的准确率!
来源
【1】Fastai MOOC
【2】Fastai 库
【3】植物幼苗数据集
用 FIPS 形象化情节
最近我和 Plotly 一起做了两个可视化项目,可视化加州每平方英尺的平均租金和全美数据科学家的平均工资。我使用了两种不同的方法在地图上显示数据——地图上的散点图和氯普图。
为什么 Plotly?
Plotly 是 Python 中强大的可视化软件包之一。使用 Plotly 的一个好处是你可以用 Python 生成 d3 图,因为 Plotly 是建立在 d3 之上的。学习 d3 需要很长的时间,但 Plotly 可以帮助消除恼人的时刻,并更专注于理解数据。Plotly 有一个丰富的地图可视化库,并且易于使用。地图是 Plotly 中可以缓解你的挫折感的类型之一。
地图散点图
当数据单位是城市时,在地图上制作散点图是个好主意,所以我选择用这种方法来显示美国各地数据科学家的平均工资,因为平均工资是基于城市的。
制作这个可视化的过程和在 Plotly 中制作散点图差不多,只是背景是一张地图。这意味着数据是基于经度和纬度绘制在地图上的,分别对应于 x 和 y 值。
准备好包含平均工资和城市的数据框后,获取每个城市的经度和纬度,并存储在同一个数据框中。下一步是分配你想要的颜色来区分薪水的高低。最后一步是定义可视化的情节和布局。Plotly 很好,因为你可以去 plotly.graph_objs,在那里你可以找到美国地图的散点图。
Figure 1: Data Scientist H1B Base Salary across the United States
这个可视化引用自官方 Plotly 文档中的例子,你可以在文章底部找到链接。如果你想看看我的代码,你也可以在文章底部找到链接。
地图上的散点图最适合基于城市的可视化数据,并且非常容易中断。如果你尝试在美国地图上可视化数据,这没有问题,因为有大量的非美国地图可用。然而,如果数据集中的城市彼此非常接近,效果就不太好。例如,如果在湾区有太多的数据点,一些点会相互堆叠,观众可能很难发现该区域的差异。
Choropleth Map
在地图上可视化数据的另一种方法是 Choropleth Map。Choropleth 地图是一种基于县的地图,它在地图上填充了县的颜色。看起来是这样的:
Figure 2: Choropleth to visualize average income per farm across the US
choropleth 图的一个优点是数据点不会相互叠加。您可能认为这很难绘制,因为您不能使用经度和纬度在地图上绘制,但是您可以使用 FIPS 来定位县。
FIPS 县代码
FIPS 县代码代表联邦信息处理标准,美国联邦政府为全国各县分配一个编号。Plotly 的 choropleth 地图的一个很好的特点是 Plotly 将 FIPS 县代码作为参数。FIPS 县代码有 5 个数字,前 2 个数字代表州,后 3 个数字代表县。例如,旧金山县的 FIPS 县代码是 06075。06 代表加州,075 代表旧金山。由于 FIPS 县代码是为每个县指定的,所以您不会在 Plotly 中错误的数据上绘制数据。你可以在联邦政府网站上找到 FIPS 县代码的列表,我在这篇文章的底部提供了链接。
Choropleth 地图示例 在我研究生院的一个项目中,我的教授给了我一组来自 Craigslist 的加州租金数据,我决定找出加州每平方英尺租金的中位数。数据集包含少量加州以外的数据,FIPS 的好处是我可以排除没有以 06 开始的 FIPS 的观察值,因为 FIPS 以其他值开始不是加州。
一旦数据准备就绪,您可以从 plotly.figure_factory 导入 create_choropleth,并传递 FIPS、值和颜色来创建 choropleth。我最终的视觉效果是这样的:
Figure 3: Median rent per Square foot by county across California
你也可以在这个图像上找到我的代码。
绘制 choropleth 地图的缺点是我只发现它对美国地图有用。有一次我试图在英国地图上绘制一个 choropleth 地图,但是我找不到任何支持它的包或选项。目前的版本在美国非常适合可视化,但在美国以外的地方就不行了。
我已经提到了 Plotly 支持的两种地图可视化方式——地图上的散点图和 choropleth 地图。这两种地图服务器的地图用途不同,取决于您是要按城市还是按县进行绘图。如果你想按城市绘制数据,你应该在地图上用散点图,并准备好经度和纬度。通过韵文,如果你想按县绘制数据,choropleth 地图是一个好方法,你应该准备好 FIPS 县代码。如果数据集来自联邦政府,很可能 FIPS 县代码已经与数据配对。因此,Plotly 的 choropleth map 是一个方便的软件包,可以可视化来自联邦政府的美国国家数据。
参考
地图上的 Plotly 散点图:
https://plot.ly/python/scatter-plots-on-maps/
FIPS 县来源:
https://www . census . gov/geographies/reference-files/2013/demo/popest/2013-geocodes-all . html
https://www . nrcs . USDA . gov/wps/portal/nrcs/detail/national/home/?cid=nrcs143_013697
我的 Github:
https://github.com/jacquessham
全美工资数据(散点图):
https://github.com/jacquessham/ds_salary_opt
加州各县每平方英尺租金中位数(Choropleth 地图):
https://github.com/jacquessham/california_rent
使用正向选择过滤掉机器学习数据集中不必要的特征
在我们之前的帖子中,我们看到了如何执行反向消除作为一种特征选择算法,从我们的数据集中剔除无关紧要的特征。在这篇文章中,我们将探讨特征选择的下一种方法,即前向选择。你可能已经猜到了,这是逆向淘汰的反义词。但在此之前,请确保您熟悉P 值的概念。
类似于逆向淘汰,即使在这里我们也有几个步骤可以遵循。我们像往常一样一个一个去。但是在进入之前,你需要知道这将是一个比逆向消去法更乏味的工作,因为你必须在这里创建一堆简单的线性回归模型。根据数据集中要素的数量,需要创建的线性回归模型的数量可能会很快增长到一个巨大的数字。记住这一点,让我们开始吧。
第一步
第一步非常类似于逆向淘汰。这里,我们选择一个显著性水平或 P 值。正如你已经知道的,5%的显著性水平或 0.05 的 P 值是常见的。所以让我们坚持下去。
第二步
这是一个非常繁琐的步骤。在第二步中,我们为数据集中的每个要素创建一个简单的回归模型。所以如果有 100 个特征,我们就创建 100 个简单的线性回归模型。因此,根据数据集中要素的数量,这可能会变得非常枯燥和复杂。但这也是这个过程中最重要的一步。一旦我们拟合了所有简单的线性回归模型,我们将计算所有模型的 P 值,并确定具有最低P 值的特征。
第三步
在上一步中,我们确定了具有最低 P 值的要素。我们会将该特性添加到所有其他特性的简单线性回归模型中。所以在第二步,我们有简单的回归模型,每个模型有一个特征。在这一步中,我们将少一个线性回归模型,但每个模型都有两个特征。完成后,我们将再次拟合模型并计算 P 值。
第四步
在这一步中,我们获得了上一步中创建的所有模型的 P 值。我们再次识别具有最低 P 值的特征。我们检查这个最低的 P 值是否小于显著性水平,或者在我们的例子中是 0.05。如果是这样,我们将把这个新特性作为一个特性添加到所有其他模型中。所以基本上,我们用一个新的特性重复步骤 3。我们将继续这个循环,直到我们从模型中得到的最低 P 值不再小于显著性水平。一旦我们到达这个阶段,我们就打破了这个循环。
一旦我们打破了这个循环,我们将得到我们想要的模型,这是我们在打破循环的迭代之前的迭代中创建的模型。让我解释一下。假设我们让循环运行 10 次迭代。在第 10 次迭代中,我们发现最低 P 值大于显著性水平。我们将在这个模型之前考虑模型,这个模型是来自第 9 次迭代的模型。我们不考虑最后一个模型,因为它没有意义,因为 P 值大于 0.05。我希望你能理解。
无论如何,你现在有了你要找的模型。这种向前选择方法的唯一问题是迭代的次数和您最终构建的模型的数量,这很容易变得难以维护和监控。但这是这个过程的必要部分。我希望我解释得够清楚了。请在下面的评论中告诉我是否有任何遗漏,或者你是否需要我做更多的解释。
在 SkLearn 中使用 FunctionTransformer 和 Pipeline 预测 Chardonnay 评级
发现管道
学习编码的最大好处之一是,当你发现新概念时,你会掉进无数个兔子洞。我在 DataCamp 上做的一个练习让我接触到了 Scikit-Learn 中的 Pipeline,它让我大吃一惊!管道允许你顺序转换数据,实现 fit() 。做完练习后,我就开始应用我所学到的知识,精心制作了一个管道来清理葡萄酒评论数据集,并预测霞多丽的评级。这是对我在创建管道和预测模型时所经历的过程的深入回顾。我讨论创建特征、应用分类编码器、构建管道和生成预测。完整的代码和 github repo 的链接可以在文章的底部找到:
导入从属关系和数据
数据来源于 Kaggle 上的葡萄酒评论数据集。我已经写了几篇关于探索数据集的文章,并且已经将它存储在一个 SQLite 数据库中。数据仅被部分清理,并且需要被转换,以便可以在机器学习模型中使用。
import numpy as np
import pandas as pd
import sqlite3
import category_encoders as ce
import refrom sklearn.feature_selection import chi2, SelectKBest
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import HashingVectorizerfrom sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.impute import SimpleImputerfrom sklearn import ensemblefrom sklearn.preprocessing import MaxAbsScaler
from sklearn.preprocessing import FunctionTransformerconn = sqlite3.connect('db/wine_data.sqlite')
c = conn.cursor#create Dataframe
df = pd.read_sql("select country \
,description \
,rating \
,price \
,province \
,title \
,winery from wine_data where variety = 'Chardonnay'", conn)
df.head(2)
注意有多少列包含文本数据。
为了在机器学习模型中使用它们,必须使用编码来转换文本。对葡萄酒厂、国家、省份和头衔等分类数据进行编码有几种策略,我将在类别编码器一节中介绍它们。在我对它们进行编码之前,我需要创建一些可能有助于预测模型的特征。
创建新功能
可以进行特征工程来提取数据中潜在的预测质量,并将其作为一列添加到数据集中。我已经创建了一组函数,接收数据帧并输出转换后的数据帧。使用 Scikit Learn 的函数转换器,我可以使用管道中的函数来转换数据帧。我试图使用全局变量让函数动态化。
从标题中提取年份
我想添加的一个特性是在 标题 列中找到的葡萄酒年份。函数 extract_year 接收一个数据帧并返回一个添加了 year 列的数据帧。
def extract_year(dataframe):#set the column name containing the year using a global variable
global year_column
years = dataframe[year_column]
#years.reset_index(inplace=False)
#years.fillna("", inplace=True)
l = []
i = 0 #use for loop to extract the year from each title row
for year in range(len(dataframe)):
temp = re.findall(r'\d+', years[i])
res = list(map(int, temp))
try:
if len(str(res[0])) == 4:
l.append(res[0])
elif len(str(res[0])) != 4:
l.append(0)
except:
l.append(0)
#print(res[0])
i+=1
dataframe['year'] = lreturn dataframe
统计描述中的单词
在浏览数据集时,我注意到评论较短的葡萄酒往往评级较低。正因为如此,我将把描述的字数添加到模型中,看看它是否是一个预测器。我使用一个依赖于名为 word_count_column 的全局变量的函数来指示使用数据帧的哪一列。它接收一个数据帧并返回一个添加了 word_count 列的数据帧。
def word_count(dataframe):
global word_count_column
dataframe['word_count'] = dataframe[text].apply(lambda word: len(str(word).split(" ")))
return dataframe
Data Frame with Added Features
我们可以看到这两个函数工作并产生了所需的数据帧。
添加了这两个数字特性后,就该考虑对列 【国家】省职称 和 酒厂 中的分类数据进行编码了。
类别编码器
由于机器学习模型将数字作为输入,因此必须对文本进行转换。用于编码数据的策略会对模型的准确性产生很大影响。为了轻松尝试各种编码策略,我推荐使用与 Scikit Learn 一起工作的类别编码器包。
包中可用的类别编码器与 Pipeline 兼容,因为它们是转换器。
pip install category_encoders
OR
conda install -c conda-forge category_encoders
包里有 15 个左右的编码器。我建议在模型管道中尝试这些方法,看看不同的策略如何影响模型的准确性。
尽管它们与 Pipeline 兼容,但我创建了一个函数,这样我就可以传入额外的逻辑。我传入一个数据框和两个全局变量来控制在转换中使用哪个类别和目标列。
# encoder = ce.JamesSteinEncoder(cols=[...]) --maybe
# encoder = ce.LeaveOneOutEncoder(cols=[...]) --maybe
# encoder = ce.MEstimateEncoder(cols=[...]) --maybe
# encoder = ce.OrdinalEncoder(cols=[...]) --maybe
# encoder = ce.TargetEncoder(cols=[...]) --maybedef category_encode(dataframe):
global category_columns
global category_target
x = dataframe[category_columns]
y = dataframe[target]
ce_ord = ce.OrdinalEncoder(cols=category_columns)
dataframe[category_columns] = ce_ord.fit_transform(x, y)
return dataframe
Example of Ordinal Encoder
请注意,分类文本列已经转换为数字列。
描述 列可以在模型中使用,但是需要经过不同的转换方法。相反,我将使用一个函数从数据帧中选择数字列,并忽略 描述 列。
get_numeric_data = FunctionTransformer(lambda x: x[numeric], validate=False)
建造管道
管道很棒,因为它们在转换过程中强制执行顺序,使得工作流紧凑且易于理解。这也可以使作品更容易复制。
请记住,pipeline 使用转换器,因此我们需要在函数上使用 FunctionTransformer,以使它们兼容。
创建转换函数
使用 FunctionTransformer,很容易使特征工程和列选择过程中使用的函数与管道兼容。
get_year = FunctionTransformer(extract_year, validate=False)
get_word_count = FunctionTransformer(word_count, validate=False)
get_encoded_text = FunctionTransformer(category_encode, validate=False)
get_numeric_data = FunctionTransformer(lambda x: x[numeric], validate=False)
因为有些函数依赖于索引值,所以我需要创建一个函数来重置索引,以便在将数据分成训练集和测试集之后管道能够正常工作。
def reset_index(dataframe):
dataframe = dataframe.reset_index(inplace = False)
return dataframeget_reset_index = FunctionTransformer(reset_index, validate=False)
设置全局变量
我创建的函数使用全局变量而不是硬编码的值,因此它们更容易重用。因此,需要设置所有的全局变量:
year_column = 'title'
word_count_column = 'description'
category_columns = ['country','province','title','winery']
target = 'price'
numeric= ['price', 'year', 'word_count', 'country', 'province', 'title', 'winery']
选择模型
管道可用于选择您想要使用的模型。要了解更多关于建立模型选择渠道的信息,我推荐阅读 Rebecca Vickery 的这篇文章。为了简单起见,我只使用 Scikit Learn 中的梯度提升回归器。
#create Gradient Boosting Regressor model
model = ensemble.GradientBoostingRegressor(
n_estimators = 100, #how many decision trees to build
learning_rate = 0.5, #controls rate at which additional decision trees influes overall prediction
max_depth = 6,
min_samples_split = 21,
min_samples_leaf = 19,
max_features = 0.9,
loss = 'huber'
)
把它放到管道里
一旦选择了模型,设置了全局变量,并且所有函数都已转换并与管道兼容,就该将这些部分组合在一起了:
pl = Pipeline(memory=None,
steps=[
('reset_index', get_reset_index),
('year', get_year),
('word_count', get_word_count),
('encode', get_encoded_text),
('selector', get_numeric_data),
('model', model)
], verbose=True)
注意,我设置了 Verbose = True。这样打印步骤就完成了。这使得调试过程更加透明。
Verbose = True output
把所有的放在一起
要测试管道,使用列车测试分割来分割数据,并通过管道运行:
features = df.drop(['rating'], axis=1)X = features
y = df['rating']
X_train, X_test, y_train, y_test = train_test_split(X, y
, test_size = .3
#, stratify=y
)
pl.fit(X_train, y_train)
pl.score(X_test, y_test)
Score
完整的代码
import numpy as np
import pandas as pd
import sqlite3
import category_encoders as ce
import refrom sklearn.feature_selection import chi2, SelectKBest
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import HashingVectorizerfrom sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.impute import SimpleImputerfrom sklearn.linear_model import LogisticRegressionfrom sklearn.preprocessing import MaxAbsScaler
from sklearn.preprocessing import FunctionTransformerconn = sqlite3.connect('db/wine_data.sqlite')
c = conn.cursor#create Dataframe
df = pd.read_sql("select country \
,description \
,rating \
,price \
,province \
,title \
,winery from wine_data where variety = 'Chardonnay'", conn)
#df.head(2)def extract_year(dataframe):
global year_column
years = dataframe[year_column]
#years.reset_index(inplace=False)
#years.fillna("", inplace=True)
l = []
i = 0
for year in range(len(dataframe)):
temp = re.findall(r'\d+', years[i])
res = list(map(int, temp))
try:
if len(str(res[0])) == 4:
l.append(res[0])
elif len(str(res[0])) != 4:
l.append(0)
except:
l.append(0)
#print(res[0])
i+=1
dataframe['year'] = lreturn dataframe
#df = extract_year(df)def word_count(dataframe):
global word_count_column
dataframe['word_count'] = dataframe[word_count_column].apply(lambda word: len(str(word).split(" ")))
return dataframe
# df = word_count(df)
# df.head(3)# encoder = ce.JamesSteinEncoder(cols=[...]) --maybe (best score)
# encoder = ce.LeaveOneOutEncoder(cols=[...]) --maybe
# encoder = ce.MEstimateEncoder(cols=[...]) --maybe (good)
# encoder = ce.OrdinalEncoder(cols=[...]) --maybe
# encoder = ce.TargetEncoder(cols=[...]) --maybeyear_column = 'title'
word_count_column = 'description'
category_columns = ['country','province','title','winery']
target = 'price'
combine_text = ['country','province','title','winery', 'description']
numeric= ['price', 'year', 'word_count','country','province','title','winery']def category_encode(dataframe):
global category_columns
global category_target
x = dataframe[category_columns]
y = dataframe[target]
ce_ord = ce.OrdinalEncoder(cols=category_columns)
dataframe[category_columns] = ce_ord.fit_transform(x, y)
return dataframe# df = category_encode(df)
# df.head()get_year = FunctionTransformer(extract_year, validate=False)get_word_count = FunctionTransformer(word_count, validate=False)get_encoded_text = FunctionTransformer(category_encode, validate=False)get_numeric_data = FunctionTransformer(lambda x: x[numeric], validate=False)def reset_index(dataframe):
dataframe = dataframe.reset_index(inplace = False)
return dataframeget_reset_index = FunctionTransformer(reset_index, validate=False)from sklearn import ensemble
model = ensemble.GradientBoostingRegressor(
n_estimators = 100, #how many decision trees to build
learning_rate = 0.5, #controls rate at which additional decision trees influes overall prediction
max_depth = 6,
min_samples_split = 21,
min_samples_leaf = 19,
max_features = 0.9,
loss = 'huber'
)pl = Pipeline(memory=None,
steps=[
('reset_index', get_reset_index),
('year', get_year),
('word_count', get_word_count),
('encode', get_encoded_text),
('selector', get_numeric_data),
('model', model)
], verbose=False)features = df.drop(['rating'], axis=1)X = features
y = df['rating']
X_train, X_test, y_train, y_test = train_test_split(X, y
, test_size = .3
#, stratify=y
)
pl.fit(X_train, y_train)pl.score(X_test, y_test)
开源代码库
[## bend game/wine _ rating _ 预测
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/bendgame/wine_rating_predictions)
谢谢大家!
- 如果你喜欢这个, 在 Medium 上关注我 了解更多
- 通过订阅 获得对我的内容的完全访问和帮助支持
- 我们连线上 LinkedIn
- 用 Python 分析数据?查看我的 网站