从《知识是美丽的》一书中再现(更多)数据可视化:第三部分
欢迎来到本系列的第三部分,在第R
部分,我重新创建了数据可视化,来自大卫·麦坎多斯的《知识是美丽的》一书。
该系列的第 I 、 II 、 IV 部分的链接可在此处找到。
数据科学中的挫折有很多,例如:
考虑上面的第 4 点。即使数据是可用的,它仍然可以以令人沮丧的格式分发。在本系列的第一部分中,我展示了如何使用readxl
包访问特定的 excel 表格。在第二部分中,我展示了如何用tabulizer
包解析 R 中的 PDF 表格。一些人可能会感兴趣, Luis D. Verde 最近发表了一篇关于如何在 R 中处理 Excel 中令人沮丧的单元格格式的伟大文章。
尽管 McCandless 公开了所有数据,但在制作可视化之前,我还是做了一些清理工作。我决定,既然这个系列的重点是数据可视化,我将把数据管理代码留在这个系列的其余部分,而是提供为ggplot
准备好的tidy
数据的.csv
文件。
长寿
长寿可视化是一个发散的条形图,描绘了某些行为如何影响你的寿命。
*library(dplyr)
library(ggplot2)# load the data
livelong <- read.csv("livelong.csv")# Order by action
livelong$action <- factor(livelong$action, levels = c("Sleep too much", "Be optimistic", "Get promoted", "Live in a city", "Live in the country", "Eat less food", "Hang out with women - a lot!", "Drink a little alcohol", "Be conscientious", "Have more orgasms", "And a little red wine", "With close friends", "Be polygamous, maybe", "Go to church regularly", "Sit down", "More pets", "Eat red meat", "Avoid cancer", "Avoid heart disease", "Be alcoholic", "Get health checks", "Get married!", "Be rich", "Be a woman", "Suffer severe mental illness", "Become obese", "Keep smoking", "Live healthily", "Exercise more", "Live at high altitude"))# Set legend title
legend_title <- "Strength of science"# Make plot
p <- ggplot(livelong, aes(x = action, y = years, fill=strength)) +
geom_bar(stat = "identity") +
scale_fill_manual(legend_title, values = c("#8BC7AC","#D99E50","#CDAD35")) +
labs(title = "Live Long...", subtitle = "What will really extend your life?", caption = "Source: bit.ly/KIB_LiveLong") +
scale_y_continuous(position = "bottom") +
scale_x_discrete(limits = rev(factor(livelong$action))) +
#scale_x_reverse() +
coord_flip() +
theme(legend.position = "top",
panel.background = element_blank(),
plot.title = element_text(size = 13,
family = "Georgia",
face = "bold", lineheight = 1.2), plot.subtitle = element_text(size = 10,
family = "Georgia"),
plot.caption = element_text(size = 5,
hjust = 0.99, family = "Georgia"),
axis.text = element_text(family = "Georgia"),
# Get rid of the y- and x-axis titles
axis.title.y=element_blank(),
axis.title.x=element_blank(),
# Get rid of axis text
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
legend.text = element_text(size = 8, family = "Georgia"),
legend.title = element_text(size = 8, family = "Georgia"),
legend.key.size = unit(1,"line"))*
好的,这是第一次尝试用geom_text()
做注释
*p + geom_text(aes(label = action), size = 3, family = "Georgia")*
人们会立即注意到文本注释没有对齐。此外,由于条形图在中心(零)发散,修改hjust
参数本身并不能解决问题。
一个可能的解决方法是使用ggfittext
包,它通过geom_fit_text()
函数将文本约束在一个定义的区域内,该函数的工作方式与ggplot2::geom_text()
差不多。
*# currently only supported by the dev version
devtools::install_github("wilkox/ggfittext")
library(ggfittext)p + geom_fit_text(aes(label = action), position = "stack", family = "Georgia")*
我们看到小条没有被注释,因为字符串太大了,无法在条中显示。
最初的可视化从来不会将文本限制在条中,所以最好的方法是向表中添加一个变量,允许您左对齐一些标签,右对齐其他标签。
*# Set postive as "Up" and negative numbers as "Down"
livelong$direction <- ifelse(livelong$years > 0, "Up", "Down")livelong$just <- ifelse(livelong$direction=="Down",0,1)p + geom_text(aes(label = action), size = 3, family = "Georgia", hjust=livelong$just)*
这证明了盒子的合理性,因此减少你寿命的行为是左调整的,而延长你寿命的行为是右调整的。唯一的问题是,在最初的可视化中,文本排列在图表的中心。我不知道如何做到这一点,所以加分,如果你能想出这一点,并张贴到评论!
一个视觉上吸引人的选择是把名字放在栏外。
*livelong$just <- ifelse(livelong$direction=="Up",0,1)p + geom_text(aes(label = action), size = 3, family = "Georgia", hjust=livelong$just)*
盘点原因英国
统计英国慈善事业数据集显示了英国公民捐赠最多的慈善机构。
我们可以使用Treemap
库创建一个类似的可视化。它创建嵌套矩形的分层显示,然后平铺在代表子分支的较小矩形内。
*library(treemap)my_data <- read.csv("treemap.csv")tm <- treemap(my_data, index = c("main","second", "third"), vSize = "percent", vColor = "percent", type = "value", title = "Counting the Cause UK")*
从《知识是美丽的》一书中再现(更多)数据可视化:第四部分
欢迎来到本系列的最后一部分,在这里我用 R 语言重新创建了 David McCandless 所著的《知识是美丽的》一书中的数据可视化。
飞机坠毁
这个数据集将用于几个可视化。
第一个可视化是一个堆积条形图,显示了从 1993 年到 2017 年 1 月的每架飞机失事的原因(为非军事、医疗或私人包机航班)。
library(dplyr)
library(ggplot2)
library(tidyr)
library(extrafont)df <- read.csv("worst_plane.csv")# Drop the year plane model entered service
mini_df <- df %>%
select(-year_service) %>%
# Gather the wide dataframe into a tidy format
gather(key = cause, value = proportion, -plane)
# Order by cause
mini_df$cause <- factor(mini_df$cause, levels = c("human_error","weather", "mechanical", "unknown", "criminal"), ordered = TRUE)# Create vector of plane names according to year they entered service
names <- unique(mini_df$plane)
names <- as.vector(names)
# sort by factor
mini_df$plane <- factor(mini_df$plane, levels = names)ggplot(mini_df, aes(x=plane, y=proportion, fill=cause)) +
geom_bar(stat = "identity") +
coord_flip() +
# Reverse the order of a categorical axis
scale_x_discrete(limits = rev(levels(mini_df$plane))) +
# Select manual colors that McCandless used
scale_fill_manual(values = c("#8E5A7E", "#A3BEC7", "#E1BD81", "#E9E4E0", "#74756F"), labels = c("Human Error", "Weather", "Mechanical", "Unknown", "Criminal")) +
labs(title = "Worst Planes", caption = "Source: bit.ly/KIB_PlaneCrashes") +
scale_y_reverse() +
theme(legend.position = "right",
panel.background = element_blank(),
plot.title = element_text(size = 13,
family = "Georgia",
face = "bold", lineheight = 1.2),
plot.caption = element_text(size = 5,
hjust = 0.99, family = "Georgia"),
axis.text = element_text(family = "Georgia"),
# Get rid of the x axis text/title
axis.text.x=element_blank(),
axis.title.x=element_blank(),
# and y axis title
axis.title.y=element_blank(),
# and legend title
legend.title = element_blank(),
legend.text = element_text(family = "Georgia"),
axis.ticks = element_blank())
第二个可视化是一个冲积图,我们可以使用ggalluvial
包。我应该提到的是,McCandless 最初的可视化要比现在产生的更加花哨,但是显示了相同的基本信息。
library(alluvial)
library(ggalluvial)crash <- read.csv("crashes_alluvial.csv")# stratum = cause, alluvium = freqggplot(crash, aes(weight = freq,
axis1 = phase,
axis2 = cause,
axis3 = total_crashes)) +
geom_alluvium(aes(fill = cause),
width = 0, knot.pos = 0, reverse = FALSE) +
guides(fill = FALSE) +
geom_stratum(width = 1/8, reverse = FALSE) +
geom_text(stat = "stratum", label.strata = TRUE, reverse = FALSE, size = 2.5) +
scale_x_continuous(breaks = 1:3, labels = c("phase", "causes", "total crashes")) +
coord_flip() +
labs(title = "Crash Cause", caption = "Source: bit.ly/KIB_PlaneCrashes") +
theme(panel.background = element_blank(),
plot.title = element_text(size = 13,
family = "Georgia",
face = "bold",
lineheight = 1.2,
vjust = -3,
hjust = 0.05),
plot.caption = element_text(size = 5,
hjust = 0.99, family = "Georgia"),
axis.text = element_text(family = "Georgia"),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.ticks.y = element_blank())
在我看来,产生桑基图的一个更干净的方法是用包flipPlot
包。
devtools::install_github(Displayr/flipPlots)
library(flipPlots)# reorder the df
crash$total_crashes <- rep("YES", 25)# plot
SankeyDiagram(crash[, -4], link.color = "Source", label.show.varname = FALSE, weights = crash$freq)
我可能只是旋转图像和标签,并将“是”改为“总崩溃数(427)”
性别差距
该图描绘了英国不同行业中男性和女性的工资差距,以及同一类别中每个职位的平均工资。我们可以使用group_by()
和summarize_at()
为每个类别创建一个新变量,然后使用facet_wrap()
。由于位置仅属于一个类别,您需要设置scales = "free_x"
用于遗漏观察。
gendergap <- read.csv("gendergap.csv")# gather the dataset
tidy_gap <- gendergap %>%
gather(key = sex, value = salary, -title, -category)category_means <- tidy_gap %>%
group_by(category) %>%
summarize_at(vars(salary), mean)tidy_gap %>% ggplot(aes(x = title, y = salary, color = sex)) +
facet_grid(. ~ category, scales = "free_x", space = "free") +
geom_line(color = "white") +
geom_point() +
scale_color_manual(values = c("#F49171", "#81C19C")) +
geom_hline(data = category_means, aes(yintercept = salary), color = "white", alpha = 0.6, size = 1) +
theme(legend.position = "none",
panel.background = element_rect(color = "#242B47", fill = "#242B47"),
plot.background = element_rect(color = "#242B47", fill = "#242B47"),
axis.line = element_line(color = "grey48", size = 0.05, linetype = "dotted"),
axis.text = element_text(family = "Georgia", color = "white"),
axis.text.x = element_text(angle = 90),
# Get rid of the y- and x-axis titles
axis.title.y=element_blank(),
axis.title.x=element_blank(),
panel.grid.major.y = element_line(color = "grey48", size = 0.05),
panel.grid.minor.y = element_blank(),
panel.grid.major.x = element_blank(),
strip.background = element_rect(color = "#242B47", fill = "#242B47"),
strip.text = element_text(color = "white", family = "Georgia"))
我不确定如何处理的一件事是 x 轴上每个变量之间的间距。由于每个刻面有不同数量的变量,如果能指定他们想要沿 x 轴等间距作为facet_wrap()
中的一个选项就更好了;然而,我认为这是不可能的(如果你知道一个变通办法,请留下评论!).
##编辑 2018 年 9 月 5 日
多亏了 StackOverflow 的非常有帮助的社区的帮助,我知道我需要的是facet_grid()
而不是facet_wrap()
。如果任何人知道如何手动调整 x 轴上每个类别之间的距离(例如或最小化间距),将获得加分。
这就是我的全部,做这个系列很有趣,我希望你喜欢!
最好的神奇宝贝类型是什么?度量的力量
Source: Pixabay
我们都知道最好的类型是什么(通灵。证明我错了。),但是数据怎么说呢?
有时候,数据科学是关于做出艰难的选择,或者为公司赚很多钱。但在其他时候,数据科学可能只是一个雨天周末的有趣爱好。
本周,我一直忙于一项大学作业:我和几个同学不得不编写 PageRank 算法——在人工智能和 NLP 吃掉其他一切之前,谷歌曾用于搜索的算法。
什么是 PageRank?
PageRank 是一种算法,用于获得系统连接部分的排名——非常适合网站排名,这是它的最初目的。
基于其最初的任务,它测量并排列许多相互链接(边)的节点(网站)的重要性或影响力。
该算法将一个有向图作为其输入,并返回其节点的排名——以及一些介于 0 和 1 之间的评分——以及一些标准:
- 如果更多的节点链接到你,你的排名会更高。
- 如果链接到更多的节点,那么链接到另一个节点就不太相关了。
- 被排名较高的节点链接更好。
这与一个大网站的想法是一致的,比如 Medium,会被很多来源链接,而被一个大网站链接(比如脸书的主页)也意味着你的网站非常相关。您还可以使用它来模拟科学论文或出版物的相关性(使用引用作为链接),或者动物在给定生态系统中被吃掉的概率(用食物链作为图形)。
所以我一直想写一篇有趣的文章,我有这个 PageRank 实现…我不能错过这个机会。
获取数据
首先,我开始着手收集数据。
因为我想建模的只是类型关系,所以我打算使用 Bulbapedia(神奇宝贝的 Wiki)——但后来我发现其他人可能已经编写了这部分代码。实际上,几秒钟的搜索让我到了这个令人敬畏的链接,从中我获得了游戏中类型优势的 Python 矩阵。
这使用了第六代的类型和关系。我还没抽时间弹第七首,所以我不介意。
已经用 Python 加载了原始矩阵,我必须给它正确的格式:PageRank 作为输入的那种图中的链接没有权重——它们是二进制的:要么链接到某个东西,要么不链接(如果有不同的 PageRank 实现来解决这个问题,请在评论中告诉我!).
如果你没有玩过神奇宝贝或者你对细节不清楚,每种类型都可以对另一种类型保持中立(大多数类型都是相互中立的),具有优势(造成 2 倍伤害),劣势(造成 1/2 伤害)或者免疫(0 伤害)。
我将这种格式(4 种不同的关系)转移到二进制域的方式是简单地绘制两个不同的图形:一个是攻击者的,一个是防御者的。我还把免疫和抵抗归为一类(防御优势,如果你愿意的话)——我希望这不会冒犯任何铁杆粉丝。
还有另一个问题:PageRank 没有考虑链接到自己的网站,所以我们不能使用关于神奇宝贝类型对自己脆弱或对自己有效的数据。(充分披露:在这篇文章的初稿中,我没有意识到这一点,一位读者让我注意到了我的错误。结果变了。)
PageRank 结果
这是我从模拟攻击类型中得到的结果。如果类型 A 对类型 B 非常有效,我只将类型 A 链接到类型 B,而不将任何类型链接到它们自己(因为这违反了 PageRanks 的前提条件)。
Somehow grass wins? And Rock is a very close second, with Psychic third.
正如我所料,灵媒进入了前 3 名。龙有一个非常糟糕的地方,这让我很惊讶,除了龙的一个优点是对其他龙非常有效,这是这个算法无法捕捉到的。电动类型排在最后,尽管水应该是最常见的神奇宝贝类型。
与此同时,防御型的结果也相当令人惊讶:
龙最后出来,尾巴上结着冰。另一方面,战斗和 Bug 是最重要的,是最有抵抗力的类型。尽管臭虫不是一种很强的防御类型,但它能抵抗三种不同的类型,其中一种是战斗型,它在排行榜上名列第一,这也解释了为什么它会如此接近榜首。
(免责声明:在之前的草稿里我说冰抗龙,其实只是龙弱于冰而已。抱歉。)
我觉得结果不太直观,也许不同的模型会更好。例如,这个模型不能捕捉一个弱于自身的类型,也不能处理水型神奇宝贝比龙型神奇宝贝出现的频率高多少。
这里有一个到原始结果的链接,以防你想以不同的方式可视化它们。
尝试不同的指标
我对我在进攻端的成绩很满意,但是防守型神奇宝贝的排名不可能有那么好。所以我开始寻找其他的衡量标准。
我想也许一个简单的衡量标准
sum (Damage modifier)*(#pokémon of that type) over every type
可以产生更直观的结果,类似于“预期损害”。让我们看看我有什么。
我发现了一个 redditor,他友好地收集了每种类型的神奇宝贝的数量,所以我引用他作为我的来源。
对于每一种神奇宝贝,我都会数两次,如果它们很弱,0 表示它们有免疫力,一半表示它们有抵抗力,一次表示没有抵抗力。
Expected dealt damage for attacking pokemon types, against other pokemon. Minus 1000 for centering.
这次
- 臭虫是第二弱的。
- 草最后出来。它在其他排名中名列第一。
- 岩石、地面、冰和飞行非常接近前四名。
- 洛克在这两个进攻指标上都做得很好。
看起来即使草类比其他种类更强,但是这些种类的神奇宝贝却更少。不过,摇滚似乎一直都不错。将来,我可能会在不考虑传奇神奇宝贝的情况下再看看这些数字,因为它们无论如何都不能用于比赛。
Expected received damage (bigger is worse) by type. Minus 600 for centering.
如果我反其道而行之,仅根据类型来计算神奇宝贝应该受到的“预期伤害”,这些是关键的见解:
- 钢铁是最好的防御神奇宝贝类型,可能是由于它对毒药的免疫和许多抗性。它以超过 20%的优势战胜了第二种类型。
- 讽刺的是,最糟糕的防御类型是冰,其预期伤害比钢高 50%。
- 地面和岩石是一贯良好的伤害处理者,但却是非常糟糕的防御类型,这使得游戏场保持公平。
- 不出所料,Bug 不仅是一个糟糕的进攻者,也是一个糟糕的防守者。
我个人觉得这些排名比第一批更直观。
对于那些想看这个分析的全部结果的人来说,这些数字可以在这个要点中找到。
最终结论
看到这些数字后,我觉得我对神奇宝贝的种类有了更好的直觉。老实说,最让我惊讶的是这个系统有多公平:从某些指标来看,最好的神奇宝贝在其他指标上是最差的,大多数都落在中间。大多数玩家做出的一些假设已经被证实了:臭虫不是一个非常强大的类型(如果把它考虑在分析中,它缺乏强大的攻击会使它更弱),岩石和地面是非常好的玻璃炮(所以继续教非地面类型的神奇宝贝地震),大多数类型只是落在中间。此外,钢是一个非常可爱的坦克。
在未来,我可能会回来做另一个更细致的分析,查看当前元的状态,或者按类型查看神奇宝贝的统计数据,特别是将它们分为特殊坦克、物理坦克、特殊 DPS 和物理 DPS。
我希望你觉得这很有趣,甚至发人深省。我很乐意接受任何批评或想法。你对这篇文章或这些结果有什么看法?你认为还有哪些问题可以用 PageRank 很好地建模?你认为做其他什么分析会很有趣?
请在评论中告诉我所有这些问题的答案!
关注我获取更多数据科学和分析文章,如果你喜欢阅读这些文章,请考虑 支持我的写作习惯。
RecSys 2017
Not a bad location for a conferance
每年推荐系统社区(学术界和工业界都一样)聚集在一起分享关于推荐系统的研究、思想和想法。今年的会议在科莫湖举行,有 40 多场演讲、11 场行业讲座、10 场研讨会和 4 场辅导课,吸引了 600 多名与会者。
如果我对下面的文章有任何误解,请纠正我。
关键主题
在整个会议中,我认为有三个强有力的主题贯穿始终;
- 深度学习 —正如在许多其他学科(NLP 和计算机视觉)中看到的那样,深度学习继续变得越来越受欢迎。尽管这些论文声称在测试数据集的准确性方面取得了显著的进步,但许多论文已经开始批评它的有效性,并质疑与类似模型相比,这种复杂系统的价值。
- 个性化——和任何推荐器一样,其目标之一是在正确的时间呈现用户想要的信息。个性化推荐采取了多种形式,从尝试在 LtR 模型中使用个人信息对项目进行排序,使用更高层次的 RNNs 对会话推荐进行个性化,到为系统内的每个用户训练模型。每个放映效果导致对用户的个性化推荐。
- 工业界与学术界——这场战斗已经持续了多年,尽管这两者之间似乎是一种健康的关系。工业似乎带来了有趣的数据和真实的生活结果。然而学术界展示了许多关于推荐的有趣模型和理论(例如会话式推荐系统)。尽管在将学术界应用到工业规模之间,甚至在如何将一些模型应用到一台机器之外之间,似乎存在着差距。
有趣的论文
提交了一些有趣的论文,以下是非常好的。这些论文通常简单明了地表达了他们想要达到的目标,并有效地传达了一个概念。
控制学习排名推荐中的人气偏差
Abdollahpouri, H., Burke, R., & Mobasher, B. (2017). Controlling Popularity Bias in Learning-to-Rank Recommendation (pp. 42–46). Presented at the the Eleventh ACM Conference, New York, New York, USA: ACM Press. [http://doi.org/10.1145/3109859.3109912](http://doi.org/10.1145/3109859.3109912)
推荐面临的挑战之一是如何让用户发现日志尾部的项目。因为长尾中的项目可能比高度流行的项目更与用户相关。本文作者将项目分为高、中、低三个等级。目的是最终的推荐列表应该由 50/50 的高等级和中等级项目组成。为了实现这一点,研究人员引入了矩阵分解方法的调节阶段,该方法基于指示项目对是否在不同等级类别中的相异矩阵。这只会影响项目嵌入,而不会影响用户嵌入。
结果表明,用户可以在不降低质量的情况下向用户推荐大量的长尾项目(通过 NDCG@10 测量)。在实施这种简单的方法中,有两个潜在的好处,首先,探索了目录的更大推广,因此如果在生产环境中实施,可以获得更大量的反馈。第二,用户之间推荐的多样性增加,从而减少了正在创建的过滤器 bubel 的变化。
尽管这可能不是突破性的,但简单的想法有时会对产生建议产生最大的影响。尽管与任何矩阵工厂方法一样,将它扩展到真实世界数据的能力总是引起我的兴趣,但与之前的实验一样,矩阵分解似乎只适用于用户活动大小变化较小的数据集,并且容易受到噪声数据集的影响。
当递归神经网络遇到邻居时进行基于会话的推荐
Jannach, D., & Ludewig, M. (2017). When Recurrent Neural Networks meet the Neighborhood for Session-Based Recommendation (pp. 306–310). Presented at the the Eleventh ACM Conference, New York, New York, USA: ACM Press. [http://doi.org/10.1145/3109859.3109872](http://doi.org/10.1145/3109859.3109872)
在演示结束时,这篇论文似乎在房间里引起了一点紧张。尽管它强调了使用深度学习系统的一个问题,即人们可以使用基本(更简单)的 ML 模型获得类似(或更好)的结果。作者比较了门控循环单元(GRU4REC) 1 和 kNN 的一些设置,以生成会话推荐。结果表明,与使用复杂的 GRUs 相比,使用 kNN 方法可以获得相似或更好的精度。尽管当两种方法结合使用时可以达到最佳效果。
在 Zalando 开发大规模推荐系统的实践经验
Freno, A. (2017). Practical Lessons from Developing a Large-Scale Recommender System at Zalando (pp. 251–259). Presented at the the Eleventh ACM Conference, New York, New York, USA: ACM Press. [http://doi.org/10.1145/3109859.3109897](http://doi.org/10.1145/3109859.3109897)
RecSys 的一个优点是在会议记录中混合了行业领先的论文。来自 Zalando 的这篇文章重点介绍了他们如何在多个产品中大规模部署学习排名(LTR)。为了扩展模型学习,他们采用成对方法,这样就可以使用 Spark 和 SparkML 等分布式框架进行大规模学习。
一种基于梯度的自适应学习框架,用于高效的个人推荐
Ning, Y., Shi, Y., Hong, L., Rangwala, H., & Ramakrishnan, N. (2017). A Gradient-based Adaptive Learning Framework for Efficient Personal Recommendation (pp. 23–31). Presented at the the Eleventh ACM Conference, New York, New York, USA: ACM Press. [http://doi.org/10.1145/3109859.3109909](http://doi.org/10.1145/3109859.3109909)
本文提出了一个有趣的模型(或多个模型)。其前提是,当训练一个模型来生成推荐时,它是根据一个全局目标函数来学习的,该目标函数产生系统内用户的平均模型。为了使用户或用户组的模型个性化,作者建议可以首先训练多个时期的“全局”模型,然后将模型的模型权重转移到“局部”模型,以继续对用户子集进行训练,从而提高个性化推荐器模型的个性化和训练速度。
有趣的礼物
在整个会议中,许多行业的焦点都集中在演讲上。这些研究本质上不一定是学术性的,但却很好地揭示了建议研究如何在实践中应用,以及如何在规模上进行编排。
网飞——如果没有网飞的介绍,这就不是一个推荐会议。正如在推荐和推荐系统的介绍中所强调的,它们受到过去用户行为的影响,并影响未来用户的意图。因此,如何在推荐器中建模时间,允许开发的模型与时间的时间效应隔离。
Déjà Vu:推荐系统中时间和因果关系的重要性 出自 贾斯汀巴西利科
- 最大限度地减少系统内的反馈循环
- 允许有控制地探索建议
- 增量
Blendal 是一家专注于新闻推荐的丹麦公司。他们展示的产品/挑战是他们如何每天获取 7000 篇新文章,并在早上 7 点前通过个性化电子邮件推荐给用户(供他们评论工作时使用)。通过使用用户建模和基于内容的功能,他们能够为用户选择相关的文章。尽管这个演讲有趣的部分是他们使用了一个带交错的凌渡 bandit 来在线调整模型的参数,允许系统从用户的隐含反馈中学习。
blend le @ rec sys’17:在线学习推荐系统排名 来自 金奎大奥迪克
- 人类策划的候选集
- 德国强盗
- 团队草稿交错
- 多样化
EA(在游戏里) —我被这个演示给惊喜了。它展示了推荐不仅仅是整个系统的一种算法,它会影响用户体验的每一个阶段。然而,这不仅仅是一个推荐者,而是一套互动系统,所有这些系统都旨在引导玩家完成游戏,优化用户流失率。
总的来说,这是一个了不起的会议,我等不及明年的温哥华了。
See you next year
- Bala zs hid ASI、Alexandros Karatzoglou、Linas Baltrunas 和 Domonkos Tikk。2016.基于会话的递归神经网络推荐。学习表征国际会议论文集(ICLR 16)。ACM。h p://arxiv . org/ABS/1511.06939↩︎
构建你自己的递归神经网络
神经网络是一类模仿人脑的机器学习算法,而 递归神经网络 是一个子类,可以很好地处理数据序列,如文本。
用一个普通的神经网络,你得到一组输入数据,让它通过网络,得到一组输出
为了训练深度学习模型,你需要知道模型应该理想地输出什么,这通常被称为你的标签或目标变量。神经网络将其输出的数据与目标进行比较,并更新网络学习以更好地模拟目标。
递归神经网络(RNNs)在建模序列数据中是有用的,序列数据涉及时间模式,如文本、图像字幕、ICU 患者数据等。这是一个简单的带反馈的前馈神经网络。在每个时间步,基于当前的输入和过去的输出,它生成新的输出。一个简单的递归神经网络架构看起来像:
rnn 比简单的前馈神经网络灵活得多。我们可以向 rnn 传递可变大小的输入,甚至得到可变大小的输出。例如,可以模拟 RNN 来一点一点地学习二进制加法。它学习二进制加法器的状态机图。经过训练后,我们只需向它传递两个任意大小的输入,它就可以生成正确的输出,而无需执行任何加法运算!RNN 能够自己隐含地学习这些语义。
为了让 RNNs 工作,需要大量的数据来找到一个好的模型,我尝试了各种 RNNs 架构,这些架构具有不同的层数、每层中隐藏单元的数量、序列长度和批量大小。所有这些超参数都应该根据数据集进行智能调整,否则可能会出现过度拟合或拟合不足的情况。生成的结果将严重依赖于数据,并且由于数据是从各种网站爬取的,因此有时可能会令人反感且不合适。
递归神经网络的步骤
初始化
这是初始化权重的第一步,你可以用三种不同的方式初始化。
1)全零。
2)随机值(取决于激活函数)
- Xavier 初始化:区间内的随机值从[-1/√n,1/√n],
其中 n 为入局连接数
构建一个
正向传播
前向传播的作用是穿过终点。
s_t=f(Ux_t+Ws_(t-1))
新状态
s_(t-1):旧状态
x_t:某一时间步的输入向量
函数 f 通常是非线性的,例如 tanh 或 ReLU
计算损失
寻找损失是训练的重要阶段损失函数决定了你的模型的状态让我们试试
交叉熵损失:
L(y,o)=-1/n∑_(n∈n)ࣼy _ nlog(o_n)〗
随机梯度下降(SGD)
这是优化你的模型的最流行的方法,SGD 是你分批发送输入而不是全部输入的方法,通过这种方法你可以很容易地缩放你的模型。
穿越时间的反向传播(BPTT)
这是一种技术,在这种技术中,您将再次遍历到起点,并根据优化器的结果更新您的权重。
用于脚本生成的递归神经网络的实现
[## karthiktsaliki/script _ generation _ rnn
训练了一个生成电视节目脚本的 rnn。
github.com](https://github.com/karthiktsaliki/script_generation_rnn/blob/master/dlnd_tv_script_generation.ipynb)
Python 中的递归神经网络实例
(Source)
利用递归神经网络撰写专利摘要
当我第一次尝试研究递归神经网络时,我犯了一个错误,那就是试图先学习 LSTMs 和 GRUs 之类的东西背后的理论。在看了几天令人沮丧的线性代数方程后,我在用 Python 进行深度学习的中看到了下面这段话
总之,你不需要了解 LSTM 细胞的具体架构的一切;作为一个人类,理解它不应该是你的工作。请记住 LSTM 细胞的作用:允许过去的信息在以后被重新注入。
这是库喀拉斯(Francois Chollet)的作者,深度学习方面的专家,他告诉我,我不需要理解基础水平的所有东西!我意识到我的错误是从理论的底层开始,而不是试图建立一个递归神经网络。
此后不久,我改变了策略,决定尝试学习数据科学技术的最有效方法:找到问题并解决它!
这种自上而下的方法是指在 之前学习如何 实现 一种方法 回溯并覆盖 理论 。通过这种方式,我能够在前进的道路上找到我需要知道的东西,当我回来研究这些概念时,我有了一个框架,我可以将每个想法放入其中。在这种心态下,我决定不再担心细节,完成一个递归神经网络项目。**
本文介绍了如何在 Keras 中构建和使用递归神经网络来撰写专利摘要。这篇文章没有什么理论,但是当你完成这个项目的时候,你会发现你在这个过程中得到了你需要知道的东西。最终结果是,你可以构建一个有用的应用程序,并弄清楚自然语言处理的深度学习方法是如何工作的。
完整的代码可以从 GitHub 上的一系列 Jupyter 笔记本中获得。我还提供了所有的预训练模型,这样你就不用自己花几个小时训练它们了!为了尽可能快地开始并研究模型,参见循环神经网络快速入门,为了更深入的解释,参见深入循环神经网络。
递归神经网络
在开始实现之前,至少理解一些基础知识是有帮助的。在高层次上,一个递归神经网络 (RNN)处理序列——无论是每日股票价格、句子还是传感器测量——一次一个元素,同时保留序列中先前出现的内容的内存*(称为状态)。*
**循环表示当前时间步的输出成为下一个时间步的输入。对于序列中的每个元素,模型不仅考虑当前的输入,还会考虑它对前面元素的记忆。
Overview of RNN (Source)
这种记忆使网络能够按顺序学习长期依赖性*,这意味着它可以在进行预测时考虑整个上下文,无论是句子中的下一个单词、情感分类还是下一次温度测量。RNN 是为了模仿人类处理序列的方式而设计的:在形成反应时,我们会考虑整个句子,而不是单词本身。例如,考虑下面的句子:*
“在乐队热身的前 15 分钟,音乐会很无聊,但随后就变得非常令人兴奋。”
一个孤立地考虑单词的机器学习模型——比如一个单词包模型——可能会得出这个句子是否定的结论。相比之下,RNN 人应该能够看到“但是”和“非常令人兴奋”这些词,并意识到句子从否定变成了肯定,因为它已经查看了整个序列。阅读一个完整的序列给我们一个处理其意义的环境,一个编码在循环神经网络中的概念。
RNN 的核心是由记忆细胞构成的一层。目前最受欢迎的细胞是长短期记忆 (LSTM),它保持细胞状态和进位,以确保信号(梯度的形式的信息)在序列处理时不会丢失。在每个时间步,LSTM 考虑当前字、进位和单元状态。
LSTM (Long Short Term Memory) Cell (Source)
LSTM 有 3 个不同的门和权重向量:有一个“忘记”门用于丢弃不相关的信息;一个“输入”门用于处理当前输入,一个“输出”门用于在每个时间步产生预测。然而,正如 Chollet 指出的,试图给细胞中的每一个元素赋予特定的含义是徒劳的。
每个单元元件的功能最终由在训练期间学习的参数(权重)决定。随意标记每个细胞部分,但它不是有效使用的必要条件!回想一下,用于序列学习的递归神经网络的好处是它保持整个序列的记忆,防止先前的信息丢失。
问题定式化
有几种方法我们可以制定的任务,训练 RNN 写文本,在这种情况下,专利摘要。但是,我们将选择将其训练为多对一序列映射器。也就是说,我们输入一系列单词,然后训练模型来预测下一个单词。在被传递到 LSTM 层之前,单词将被映射到整数,然后使用嵌入矩阵(预训练的或可训练的)被映射到向量。
当我们去写一个新的专利时,我们传入一个单词的起始序列,对下一个单词进行预测,更新输入序列,进行另一个预测,将这个单词添加到序列中,然后继续我们想要生成的单词。
该方法的步骤概述如下:
- 将摘要从字符串列表转换为整数列表(序列)
- 根据序列创建要素和标注
- 使用嵌入层、LSTM 层和密集层构建 LSTM 模型
- 加载预先训练的嵌入
- 训练模型以预测下一步工作
- 通过传入起始序列进行预测
请记住,这只是问题的一种表述:我们还可以使用字符级模型,或者对序列中的每个单词进行预测。正如机器学习中的许多概念一样,没有一个正确的答案,但这种方法在实践中效果很好。
数据准备
即使神经网络具有强大的表示能力,获得高质量、干净的数据集也是至关重要的。这个项目的原始数据来自 USPTO PatentsView ,在这里你可以搜索在美国申请的任何专利的信息。我搜索了“神经网络”这个词,并下载了由此产生的专利摘要——总共 3500 篇。我发现最好在一个狭窄的主题上进行训练,但也可以尝试不同的专利。
Patent Abstract Data
我们将从专利摘要作为字符串列表开始。我们模型的主要数据准备步骤是:
- 删除标点符号,将字符串拆分成单个单词的列表
- 将单个单词转换成整数
这两个步骤都可以使用 Keras [Tokenizer](https://keras.io/preprocessing/text/#tokenizer)
类来完成。默认情况下,这会删除所有标点符号,小写单词,然后将单词转换为整数的sequences
。一个Tokenizer
首先是一个字符串列表上的fit
,然后将这个列表转换成一个整数列表的列表。下面演示了这一点:
第一个单元的输出显示原始摘要,第二个单元的输出显示标记化序列。每个抽象现在都表示为整数。
我们可以使用经过训练的记号赋予器的idx_word
属性来计算这些整数的含义:
如果你仔细观察,你会注意到Tokenizer
去掉了所有的标点符号,所有的单词都变成了小写。如果我们使用这些设置,那么神经网络将不会学习正确的英语!我们可以通过将过滤器更改为Tokenizer
来调整这一点,以便不删除标点符号。
*# Don't remove punctuation or uppercase
tokenizer = Tokenizer(num_words=None,
filters='#$%&()*+-<=>@[\\]^_`{|}~\t\n',
lower = False, split = ' ')*
查看不同实现的笔记本,但是,当我们使用预先训练的嵌入时,我们必须删除大写字母,因为嵌入中没有小写字母。当训练我们自己的嵌入时,我们不必担心这一点,因为模型将学习小写和大写的不同表示。
功能和标签
前一步将所有摘要转换成整数序列。下一步是创建一个有监督的机器学习问题,用它来训练网络。有许多方法可以为文本生成设置递归神经网络任务,但我们将使用以下方法:
给网络一个单词序列,训练它预测下一个单词。
单词数作为参数留下;在这里显示的例子中,我们将使用 50,这意味着我们给我们的网络 50 个单词,并训练它预测第 51 个单词。训练网络的其他方法是让它预测序列中每一点的下一个单词——对每个输入单词进行预测,而不是对整个序列进行一次预测——或者使用单个字符训练模型。这里使用的实现不一定是最佳的——没有公认的最佳解决方案——但是它工作得很好!
创建特征和标签相对简单,对于每个抽象(用整数表示),我们创建多组特征和标签。我们使用前 50 个单词作为特征,第 51 个单词作为标签,然后使用单词 2-51 作为特征,预测第 52 个单词,依此类推。这给了我们更多的训练数据,这是有益的,因为网络的性能与它在训练期间看到的数据量成比例。
创建要素和标注的实现如下:
这些特征以形状(296866, 50)
结束,这意味着我们有将近 300,000 个序列,每个序列有 50 个标记。在递归神经网络的语言中,每个序列有 50 个时间步*,每个时间步有 1 个特征。*
我们可以将标签保留为整数,但是当标签被一次性编码时,神经网络能够最有效地训练。我们可以使用下面的代码非常快速地用numpy
对标签进行一次性编码:
为了找到与label_array
中的一行相对应的单词,我们使用:
在正确格式化我们所有的特性和标签之后,我们希望将它们分成一个训练集和一个验证集(详见笔记本)。这里很重要的一点是同时改变特性和标签,这样相同的摘要就不会出现在同一个集合中。
构建递归神经网络
Keras 是一个不可思议的库:它允许我们用几行可理解的 Python 代码构建最先进的模型。虽然其他神经网络库可能更快或更灵活,但在开发时间和易用性方面,没有什么能胜过 Keras。
下面是一个简单 LSTM 的代码,并附有解释:
我们正在使用 Keras Sequential
API,这意味着我们一次构建一层网络。这些层如下所示:
- 一个将每个输入单词映射到 100 维向量的
Embedding
。嵌入可以使用我们在weights
参数中提供的预训练权重(一秒钟内更多)。如果我们不想更新嵌入,可以设置False
。 - 一个
Masking
层,用于屏蔽任何没有预训练嵌入的单词,这些单词将被表示为全零。训练嵌入时不应使用该层。 - 网络的核心:一层
LSTM
单元,带有脱落以防止过拟合。由于我们只使用一个 LSTM 层,它不会返回序列,对于使用两个或更多层,请确保返回序列。 - 激活
relu
的全连接Dense
层。这为网络增加了额外的表示能力。 - 一个
Dropout
层,用于防止过度拟合训练数据。 - 一个
Dense
全连接输出层。这为使用softmax
激活的 vocab 中的每个单词产生一个概率。
该模型使用Adam
优化器(随机梯度下降的变体)进行编译,并使用categorical_crossentropy
损失进行训练。在训练期间,网络将试图通过调整可训练参数(权重)来最小化日志损失。与往常一样,使用反向传播计算参数的梯度,并用优化器更新。因为我们正在使用 Keras,所以我们不用担心这是如何在幕后发生的,只需要正确设置网络。
LSTM network layout.
在不更新嵌入的情况下,网络中需要训练的参数要少得多。对 [LSTM](https://machinelearningmastery.com/reshape-input-data-long-short-term-memory-networks-keras/)
层的输入是 [(None, 50, 100)](https://machinelearningmastery.com/reshape-input-data-long-short-term-memory-networks-keras/)
这意味着对于每一批(第一维),每个序列有 50 个时间步长(字),每个时间步长在嵌入后有 100 个特征。LSTM 图层的输入总是具有(batch_size, timesteps, features)
形状。
构建这个网络有很多方法,笔记本中还介绍了其他几种方法。例如,我们可以使用两个相互堆叠的LSTM
层,一个处理来自两个方向的序列的Bidirectional LSTM
层,或者更多的Dense
层。我发现上面的设置运行良好。
预训练嵌入
一旦网络建立起来,我们仍然需要为它提供预先训练好的单词嵌入。你可以在网上找到许多在不同语料库(大量文本)上训练过的嵌入。我们将使用的是斯坦福提供的,有 100、200 或 300 种尺寸(我们将坚持 100)。这些嵌入来自 GloVe(单词表示的全局向量)算法,并在维基百科上进行训练。
即使预训练的嵌入包含 400,000 个单词,我们的 vocab 中也包含一些单词。当我们用嵌入来表示这些单词时,它们将具有全零的 100 维向量。这个问题可以通过训练我们自己的嵌入或者通过将Embedding
层的trainable
参数设置为True
(并移除Masking
层)来解决。
我们可以从磁盘中快速加载预训练的嵌入,并使用以下代码创建一个嵌入矩阵:
这是给 vocab 中的每个单词分配一个 100 维的向量。如果单词没有预先训练的嵌入,那么这个向量将全为零。
为了探索嵌入,我们可以使用余弦相似度来在嵌入空间中找到与给定查询词最接近的词:
嵌入是被学习的,这意味着表征专门应用于一个任务。当使用预先训练的嵌入时,我们希望嵌入所学习的任务足够接近我们的任务,所以嵌入是有意义的。如果这些嵌入是在 tweets 上训练的,我们可能不会期望它们工作得很好,但是因为它们是在 Wikipedia 数据上训练的,它们应该普遍适用于一系列语言处理任务。
如果您有大量的数据和计算机时间,通常最好学习自己的特定任务的嵌入。在笔记本中,我采用了两种方法,学习到的嵌入表现稍好。
训练模型
随着训练和验证数据的准备、网络的构建和嵌入的加载,我们几乎已经为模型学习如何编写专利摘要做好了准备。然而,在训练神经网络时,最好使用模型检查点和 Keras 回调形式的早期停止:
- 模型检查点:在磁盘上保存最佳模型(通过验证损失来衡量),以便使用最佳模型
- 提前停止:当验证损失不再减少时,停止训练
使用提前停止意味着我们不会过度适应训练数据,也不会浪费时间训练那些不会提高性能的额外时期。模型检查点意味着我们可以访问最好的模型,如果我们的训练被中断 1000 个纪元,我们也不会失去所有的进展!
然后,可以使用以下代码训练该模型:
在一个 Amazon p2.xlarge 实例(0.90 美元/小时的预订)上,完成这个任务需要 1 个多小时。一旦训练完成,我们可以加载回最佳保存的模型,并评估验证数据的最终时间。
*from keras import load_model# Load in model and evaluate on validation data
model = load_model('../models/model.h5')
model.evaluate(X_valid, y_valid)*
总体而言,使用预训练单词嵌入的模型实现了 23.9%的验证准确率。考虑到作为一个人,我发现很难预测这些摘要中的下一个词,这很好!对最常见的单词(“the”)的简单猜测产生了大约 8%的准确度。笔记本中所有型号的指标如下所示:
最佳模型使用预先训练的嵌入和如上所示的相同架构。我鼓励任何人尝试用不同的模式训练!
专利摘要生成
当然,虽然高指标很好,但重要的是网络是否能产生合理的专利摘要。使用最佳模型,我们可以探索模型生成能力。如果你想在你自己的硬件上运行这个,你可以在这里找到笔记本,在 GitHub 上找到预训练模型。
为了产生输出,我们用从专利摘要中选择的随机序列作为网络的种子,让它预测下一个单词,将预测添加到序列中,并继续预测我们想要的任意多个单词。一些结果如下所示:
输出的一个重要参数是预测的差异*。我们不是使用概率最高的预测单词,而是将多样性注入到预测中,然后选择概率与更多样的预测成比例的下一个单词。多样性太高,生成的输出开始看起来是随机的,但太低,网络会进入输出的递归循环。*
输出还不算太差!有时很难确定哪些是计算机生成的,哪些是机器生成的。这部分是由于专利摘要的本质,大多数时候,它们听起来不像是人类写的。
网络的另一个用途是用我们自己的启动序列来播种它。我们可以使用任何我们想要的文本,看看网络会把它带到哪里:
同样,结果并不完全可信,但它们确实很像英语。
人还是机器?
作为递归神经网络的最终测试,我创建了一个游戏来猜测是模型还是人产生了输出。这是第一个例子,其中两个选项来自计算机,一个来自人类:
你的猜测是什么?答案是第秒是一个人写的实际摘要(嗯,它是摘要中的实际内容。我不确定这些摘要是人写的)。这里还有一个:
这一次第三部有了一个有血有肉的作家。
我们可以使用额外的步骤来解释该模型,例如发现哪些神经元随着不同的输入序列而变亮。我们还可以查看学习到的嵌入(或者用投影工具可视化它们)。我们将在另一个时间讨论这些话题,并得出结论,我们现在知道如何实现一个递归神经网络来有效地模仿人类文本。
结论
认识到递归神经网络没有语言理解的概念是很重要的。它实际上是一台非常复杂的模式识别机器。然而,与马尔可夫链或频率分析等方法不同,rnn 基于序列中元素的排序进行预测。从哲学角度来看,你可能会认为人类只是极端的模式识别机器,因此递归神经网络只是像人类机器一样工作。**
递归神经网络的用途远远不止文本生成,还包括机器翻译、图像字幕和作者身份识别。虽然我们在这里讨论的这个应用程序不会取代任何人类,但可以想象,随着更多的训练数据和更大的模型,神经网络将能够合成新的、合理的专利摘要。
A Bi-Directional LSTM Cell (Source)
人们很容易陷入复杂技术背后的细节或理论,但学习数据科学工具的更有效方法是深入研究并构建应用。一旦你知道了一项技术的能力以及它在实践中是如何工作的,你就可以随时回头去了解这个理论。我们大多数人不会设计神经网络,但学习如何有效地使用它们是值得的。这意味着收起书本,打破键盘,编写你自己的网络。
一如既往,我欢迎反馈和建设性的批评。可以通过推特 @koehrsen_will 或者通过我的网站 willk.online 找到我。
用于语言理解的递归神经网络
递归神经网络(RNNs)已经被认为在机器翻译、情感分析、语音识别和许多其他机器学习任务中实现了最先进的性能。它们的优势在于处理顺序数据和各种长度的输入输出的能力。
这些神奇的网络并不新鲜。事实上,它们是在 20 世纪 80 年代开发的——但是比非递归神经网络的计算成本更高,直到(相对)最近计算资源的改进,RNNs 才开始流行。
本文将在 NLP 的背景下介绍递归神经网络。
从序列数据中学习
前馈神经网络假设所有输入都是相互独立的。虽然对于许多分类任务来说,这是一个合理的假设,但它无法捕捉到在顺序数据中发现的关系。
在顺序数据中,顺序很重要。以文字为例。我们通过每一个单词来发展对文本的理解。我们必须按顺序处理课文,并结合之前所学的知识处理每个单词,以形成正确的理解。
递归神经网络通过使用先前学习的信息来预测下一个输出,从而缓解了这个问题。在语言环境中,RNNs 将从前面的单词中学到的信息存储在“记忆”中,并使用这些知识来更好地理解下一个单词。
Language is a puzzle! Words needs to be pieced together for us to see the big picture.
内存的使用也允许 rnn 处理不同长度的序列。这在诸如机器翻译等我们不知道输入和输出有多长的任务中特别有用。
以下是 RNNs 可以使用的几种输入/输出配置:
- **一对一网络:**香草神经网络接受一个向量作为输入,返回一个向量作为输出。rnn 也可以这样配置。
- **多对一网络:**RNN 也可以接受几个向量作为输入来模拟顺序关系,但返回一个向量作为输出。
- **多对多网络:**一个 RNN 也可以接受多个向量作为输入,返回多个向量作为输出。
不同输入/输出配置的示例
**一对一:**我们对基于毒性的聊天室消息分类感兴趣。我们决定最好的嵌入技术将基于频率,并选择使用 TF-IDF 将每个消息嵌入到一个向量中。输出也是一个向量,每个坐标对应于每个类。
**多对一:**我们认为基于频率嵌入聊天室消息是个坏主意,而且单词的顺序很重要。因此,我们通过使用预先训练的 word2vec 模型,将消息中的每个单词嵌入到它自己的向量中。模型输出不会改变。
**多对多:**我们已经超越了简单的文本分类器,开始训练一个 RNN 来翻译英语和法语之间的信息。在这种情况下,单词的顺序当然很重要。我们将英语输入编码成几个向量(每个词一个向量),并使用输出向量来确定法语翻译的顺序。
训练递归神经网络
从传统的神经网络训练 RNN 的区别在于更新权重的方式。在 RNN 中更新权重最常用的方法是通过时间反向传播(BPTT)。
回想一下用于训练普通神经网络的梯度下降算法:
- 初始化重量
- 使用权重从输入生成输出(输出计算为输入的加权和)
- 使用激活功能转换输出
- 计算输出误差(转换输出和目标之间的差值)
- 反向传播误差使用偏导数寻找梯度
- 通过梯度下降更新权重
- 重复步骤 2-6,直到满足某个停止标准
当执行 BPTT 时,我们需要将我们的网络“展开”成按时间步长分解的层。
**注:**我们经常把顺序数据分解成“时间步长”。第一个数据点将是时间 1 的输入,依此类推。
Wikipedia illustration of BPTT
在展开我们的循环层之后,我们剩下看起来像几个非循环层的东西。与上述算法的唯一不同之处在于:
- 渐变是通过跨所有展开的层而不是一个层反向传播而生成的。总损失计算为所有时间步长的平均损失。
- 权重由所有展开的层共享。它们在最后使用累积的梯度进行更新。
使用递归神经网络
如果我们正在处理文本数据,其中每个单词都映射到它自己的向量,那么每个单词都将是一个单独的数据点。
使用关于网络权重 w 的函数来计算隐藏状态。该函数的参数是先前的隐藏状态和当前时间步长的输入向量。 Tanh 和 ReLU 是一些常用的计算隐藏状态的函数。
我们通过使用权重来计算输出,以获得最后隐藏状态的加权和。这些输出可以用来进行预测或生成新的序列。
对消失/爆炸梯度的关注
类似于非常深的前馈神经网络,rnn 容易受到通过反向传播的消失/爆炸梯度的影响(回想一下,一个递归层被展开成许多非递归层)。
幸运的是,有几种方法可以解决这些问题。
消失渐变:
爆炸渐变:
- 渐变裁剪:设置渐变值的最大阈值;剪裁阈值以上所有渐变
结论
尽管有额外的训练复杂性,RNNs 在机器学习中已经变得非常流行。在 NLP 之外,递归神经网络也被用于视频分类、语音识别和时间序列数据中的异常检测。
感谢您的阅读!
如果你喜欢这篇文章,可以看看我关于数据科学、数学和编程的其他文章。通过 Medium 关注我的最新更新。😃
作为一个业余爱好项目,我还在www.dscrashcourse.com建立了一套全面的免费数据科学课程和练习题。
如果你想支持我的写作,下次你报名参加 Coursera 课程时,可以考虑使用我的会员链接。完全公开—我从每一次注册中获得佣金,但不会对您产生额外费用。
再次感谢您的阅读!📕
用 Ramda 进行递归滚动计算
在函数式编程范式中使用递归来创建移动平均值很有趣
Image by stokpic via Pixabay.com (https://pixabay.com/users/stokpic-692575/)
对于对随时间波动的数据集感兴趣的人来说,滚动平均或移动平均是最受欢迎的趋势指标之一。对于交易者来说尤其如此,他们总是在金融市场的不断变化中努力解读趋势信号。
本文面向具有数据科学背景,并且有兴趣学习更多关于函数式编程方法的人。递归是一个需要花费一些时间和思想来理解的概念,所以我想展示一个在现实世界中使用递归的实用方法。
我的主要目标是使用递归的函数方法而不是“for 循环”来计算移动平均线(特别是指数移动平均线)。我将非常依赖 Ramda.js,这是一个非常有用的函数式 Javascript 库。
递归
我假设这篇文章的读者熟悉移动平均线,所以让我们直接跳到递归。这个帖子的灵感直接来源于这篇文章和这篇文章。在 Google 中输入“递归”会得到如下定义:
递归过程或定义的重复应用
这是一个递归定义——酷!
递归函数是根据自身定义的函数。考虑递归函数的另一种方式是,它调用自己——直到它不调用为止。运行const squareMe = n => squareMe(n*n)
会一遍又一遍的运行,迅速吹爆你的调用栈。
这就是为什么我们需要一个“基本情况”,这是一个定义函数何时终止的条件。
此外,递归函数实际上需要调用自身,这被称为“递归”
最后,为了做任何有用的事情,递归函数需要有一个“动作”
为了演示递归函数的这三个组成部分,让我们编写一个从 1 数到给定数字的函数:
// ES6
const countTo = (num, arr=[], startingN=1) => {
// Base case:
if (arr.length === num) return arr;
// Recursion and action:
return countTo(num, arr.concat(startingN), startingN + 1);
};
调用countTo(10)
返回[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,通过调用动作(arr.concat
和startingN+1
)递归调用自身,直到遇到基本情况arr.length === num
时终止。
耶!没有循环了!
递归是 for 循环的一种替代方法,也是一种更实用的遍历数据集以获得所需结果的方法。
拉姆达
Ramda.js 是一个轻量级的、功能性的 Javascript 库,它是为那些希望保持代码纯净和功能性、函数不可变和没有副作用的人而设计的。哦,还有数据排在最后——这样更好。
简单移动平均线
让我们从一系列数字的简单移动平均开始:
//ES6
import R from 'ramda';const sma = (num, data, arr = []) => {
if (data.length === 0) return arr;
if (data.length < num) return sma(
num,
data.slice(1),
R.prepend('not yet!', arr),
);
const newData = R.tail(data);
const movingMean = R.mean(
R.slice(0, num, data)
);
return sma(
num,
newData,
R.append(movingMean, arr)
);
};
这个功能有三个部分。
一旦data
参数的长度达到 0,第一个if
语句就返回arr
。这是“基本情况”。
当data
参数的长度小于num
参数的长度时,第二个if
语句递归调用sma
,使用 Ramda 的prepend
添加‘还没有!’串到arr
前面。
最后的return
语句是这个函数的核心。它将 Ramda 的mean
函数应用于newData
的适当元素,每次返回一个新的arr
并附加平均值。
这里要强调的最后一点是,每次函数调用自己时,它是如何在newData
上这样做的,它只是返回data
而没有第一个元素(R.tail
)。让我们在每个return
语句之前添加一个console.log
语句,这样我们就可以确切地看到sma
内部发生了什么:
这显示了调用sma
时的确切情况。在该函数第一次运行后,它返回一个带有首字母data
的前三个数字的平均值的arr
的副本。该函数第二次运行时,将返回第一个元素被切掉的arr
副本的前三个数字的平均值。
你明白了!
注意,在递归过程的最后,'not yet'
字符串必须加在arr
的前面。此外,由于这些是占位符,我们没有得到实际值,这就引出了本文的下一步:指数移动平均线。
指数移动平均线
到目前为止,我们已经介绍了递归的基本知识,并在 Ramda 的帮助下,将它们应用于创建一个简单的列表移动平均值。
让我们以不同的方式处理这个概念,将它应用到现实世界的情况中。我们将使用 IEX API 从股票交易所获取一些纵向数据:
这个函数从 IEX 交易 API 获取 AAPL 数据,并返回一个带有date
和value
的对象列表。该数据集的图表如下所示:
Image by author
现在,我们需要编写一个函数,给出这个数据集的指数加权移动平均,为此,我们将使用吴恩达在这里提出的指数加权移动平均。把它写成一个 javascript 函数看起来是这样的【第 8 行】:
哇哦。让我们把这个拆开。
首先,ewa
有两个参数- decay
是您想要权衡的天数,而data
是您想要操作的对象列表。接下来,ewa
定义了beta
,它是使用一点指数代数从decay
计算出来的,本质上控制算法的平滑。
函数checkArr
是算法的神奇之处。在ewa
的第一次迭代中,arr
为空,因此checkArr
只是将新arr
的第一个元素的value
设置为data
[第 7 行]中的第一个value
。没有之前的数据点来指数加权!在其余的ewa
递归调用中,checkArr
返回算法中由beta
控制的加权值,因为arr
不再为空。
现在,我们可以在图表中添加一个ewa(20, getData())
:
Image by author
更多拉姆达!
在函数式编程中,“curried”函数是这样一种函数,如果提供的参数少于它所需要的参数,它将返回一个采用剩余参数的新函数。当您需要编写新函数时,这种技术非常有用。让我们使用拉姆达的curry
函数来创建一堆不同的ewa
函数:
const curriedEwa = R.curry(ewa);const ewaTwenty = curriedEwa(20);const ewaFifty = curriedEwa(50);const ewaHundred = curriedEwa(100);
现在,将ewaTwenty
、ewaFifty
和ewaHundred
制成图表给我们看:
Image by author
函数式编程 FTW!
这里有很多内容,我希望我已经帮助展示了递归和 Ramda 是多么有用。如果你还不熟悉这些概念,你可能需要一段时间来理解它们(至少对我来说是这样)。然而,我已经建立了一个可观察笔记本,在那里你可以深入探究这篇文章中的代码。
数据科学面试中的危险信号
这篇文章是与 Warby Parker 的数据科学家 艾米丽·罗宾逊 共同撰写的。我和她还写了一本相关的书: 打造你的数据科学生涯 。
在面试任何职位时,你应该像他们评价你一样评价这家公司。虽然你可以在 glassdoor 和类似的网站上预先研究这家公司,但面试是更深入了解这家公司并提出重要问题的最佳途径。公司永远不会直接告诉你他们不适合工作,所以你必须自己寻找迹象。
以下是我们列出的 12 个迹象,你面试的公司应该避免这些迹象(以及面试时应该问的问题)。前六个主要适用于已经拥有多名数据科学家或分析师的公司。如果你正考虑加入一家公司,成为他们的第一位数据科学家,你将面临一系列完全不同的挑战,包括很可能要做大量的数据工程工作(见标志 1)和传播数据科学思维。必须有人去做,但我们通常不建议你在第一份数据科学工作中这样做,除非你有工程背景,并且想做这项工作。如果只有一个数据科学领导者,并且他们正在建立一个团队,询问他们计划如何处理下面提出的问题,但请记住,承诺一个理想的系统总是比实现一个更容易。
数据科学团队如何运行的危险信号
1.没有数据工程或基础设施。
数据科学要求数据易于分析。如果公司没有一个维护良好的数据基础设施,您就没有工作所需的东西。数据工程师是为分析准备数据的人,如果你的公司没有数据,你就必须自己做这项工作。如果你觉得自己有资格担当数据工程师的角色,这可能没问题,但否则你将很难交付任何有价值的东西。
面试时要问的问题:您的数据基础架构是什么样的,由谁维护?数据通常采用什么格式(Excel、SQL 数据库、csv)?
2.数据科学家之间没有同行评审。
一个强大的数据科学团队将有办法确保错误不会从缝隙中溜走。这些可以包括代码审查、实践演示,以及与团队的一致检入。如果团队没有始终如一地做这些,直到工作已经交付,错误才会被发现,这通常以某人受到斥责而告终。
要问的问题:团队采取什么步骤进行 QA 和同行评审?
3.团队中没有标准的语言。
许多数据科学团队采取的方法是让团队中的任何人使用他们想要的任何语言。这个想法是,如果每个人都使用他们最喜欢的语言,工作将会完成得更快。这有一个巨大的问题:当每个人都使用不同的语言时,没有人能够把工作交给其他人。每项数据科学任务都将有一个人负责,如果他们辞职、生病或只是需要帮助,没有人能够做到这一点,这将创造一个非常紧张的环境。使用 R,Python,甚至我们敢说 SAS 都没问题,但是团队中要有一套一致的语言。
要问的问题:你的团队使用什么语言,你如何决定是否采用一种新的语言?
4.他们不了解需求的数据层次。
类似于没有数据基础设施,一些公司在没有基础的情况下对人工智能等概念感到非常兴奋。机器学习和人工智能要求一家公司具有高水平的数据科学成熟度,包括理解如何建立模型、模型的局限性以及如何部署模型。当他们不切实际的期望变成现实时,你可能会受到责备。
要问的问题:公司如何平衡花费在人工智能等复杂方法上的时间与清理数据、检查数据质量和添加日志记录等基础工作?
5.没有版本控制。
成熟的数据科学团队使用 git 来跟踪分析和代码的变化。其他团队则使用共享网络文件夹之类的方法,这种方法不会让您看到什么时候发生了变化,为什么会发生变化,或者以前的版本。有时候,团队根本不共享代码,只是在数据科学家的个人笔记本电脑上工作。像躲避瘟疫一样避开最后这些群体。没有共享代码的方法意味着团队不能一起工作。
要问的问题:如何在团队中共享代码?所有代码都是共享的还是只有一部分?
6.运行报告的人和进行分析的人之间没有明确的界限。
创建和维护报告、构建数据科学模型以及将机器学习模型投入生产所需的技能都是不同的。如果公司没有一个明确的方法来确定谁做什么工作,你可能开始你的工作,并最终做完全不同于你预期的工作。你不会希望在第一天上班时,期望建立一个时间序列预测,然后发现你的工作是刷新每月的销售 Excel 电子表格。
提问:报告、分析和生产模型构建任务是如何划分的?
他们如何重视人的危险信号
1.完全没有结构的面试过程。
结构化面试流程意味着每个候选人都会得到一套相同的问题,可以更加平等地进行比较。这不仅能减少偏见(https://HBR . org/2016/04/how-to-take-the-bias-out-of-interview),还要求团队仔细考虑他们带来的人身上什么是重要的。如果面试过程是无组织的,面试官似乎会不假思索地问一些问题,那么这是一个强烈的信号,表明他们还没有弄清楚自己想要一个什么样的候选人,以及如何得到它。如果他们不知道自己想要什么,你就很难在工作中给他们想要的。
建议:看看他们是否带了一系列问题去面试,或者问他们如何选择问你什么的元问题。
2.没时间回答你的问题。
因为面试也是为了让你了解公司,你需要有时间这样做。如果你没有时间问问题,面试官不会让你感到舒服,也不会让你评估自己的适合度。
建议:如果你参加了面试,但没有时间提问,记下来,然后问面试官什么时候提问比较合适。
3.面试不需要编码。
虽然编程不是数据科学家最重要的技能,但它是你在工作中必须要做的事情。面试中的编码部分可以是现场测试,也可以是带回家的测试,但它肯定是存在的。如果面试过程不包括编程,可能有以下几个原因:(1)数据科学团队是新的,所以没有人能主持面试。在这种情况下,要知道你在工作中不会得到支持。(2)团队没有时间去创造一个编程面试。这是他们不重视招聘的一个迹象。(3)他们不会编程,不会在工作中使用 Tableau、Excel 等 BI 工具。(4)他们非常信任你的简历,所以他们不需要测试你,而这只是他们迫切想雇用你的一个迹象。
建议:如果面试不包括编程部分,问他们如何判断哪些候选人具备这份工作的技能。
4.头几个月没有计划。
公司发布招聘信息是有充分理由的。如果他们不能清楚地说出你在头几个月要做什么,原因可能是“我们完全被工作压得喘不过气来,我们会把人扔向这个问题,直到我们能处理它。”这是一种极其危险的团队成长方式。更糟糕的是,这通常发生在没有新员工入职流程的公司。所以这些情况对整个团队来说都是极度紧张的,这通常也会落在你身上。
建议:询问他们是否为你的起步制定了明确的项目和入职流程。如果他们对此没有极其精确的答案,那就跑。
5.不支持继续教育。
数据科学是一个巨大且快速发展的领域,如果你不坚持学习,你就会落后。团队应该有一些方法来帮助人们跟上,无论是为在线教育提供资金还是参加会议,每月一次的会议,在会上你可以讨论行业博客文章,鼓励参加会议或参与开源,或者一系列的演讲。这也表明他们愿意投资于他们的员工。
建议:询问他们如何支持团队的继续教育。是否有会议或研讨会的资金?
6.面试官对该职位的回答不一致
通常面试会让你和公司里的很多人交谈,包括你未来的经理、队友和商业利益相关者。如果他们每个人都告诉你不同的事情,比如责任级别、工作类型、角色提供的服务以及你必须工作的时间,他们自己可能不会同意。如果他们不能达成一致,尤其是在与你最终要做的工作相关的事情上,你的工作将会充满冲突。
建议:记录人们在不同的面试中所说的话。如果你发现不一致,问为什么。
虽然这 12 面旗帜看起来很多,但公司倾向于不展示其中任何一面或大部分,只展示中间的几面。通过留意他们,你可以避免得到你不喜欢的工作的问题。祝你好运!
如果你想要大量的方法来帮助你在数据科学领域发展职业生涯,可以看看 Emily Robinson 和我写的书: 在数据科学领域建立职业生涯 。我们将带您了解成为数据科学家所需的技能,找到您的第一份工作,然后晋升到高级职位。
DataVis 战斗入门:了解我的神奇宝贝
Stacked percentage bar chart sorted by total damage against Grass-type Pokémon (Plotly).
这是我第二次参加 Reddit 上的 DataVis 挑战赛。数据来源: Kaggle 和Bulbepedia为图片。所有的图形都是用 Python 做的,用的是 Plotly 和 Matplotlib。此外,作为免责声明,我绝不是神奇宝贝专家,所以如果一些数据没有准确解读,提前道歉。我理解他们的特点和历史有许多细微差别,所以我愿意让顽固分子在必要的地方补充他们的意见。
这些数据图片和平而来 ✌
探索数据集
按类型分类的神奇宝贝
我想看的第一个变量是神奇宝贝类型的总数。许多神奇宝贝都是混合的,所以有第一类和第二类:
Stacked bar chart of Pokémon Type 1 and Type 2 (Plotly).
正如你所看到的,水型神奇宝贝是最常见的,其次是普通的和草。飞行型神奇宝贝是最常见的 2 型,其次是毒型、地型和仙型。
身体质量指数的神奇宝贝
一旦 Medium 决定允许 HTML 嵌入,我的帖子将停止展示这些 gif。
Scatter plot by Weight, Height and Traces by Type 1 (Plotly).
虽然这种视觉效果在互动时相对信息量大,但显示神奇宝贝精灵或图像的静态图像也能让这些生物活起来。
经过多次努力将这些图像作为 Matplotlib 的标记导入,我终于设法让这个图表工作起来:)
Scatter plot by Pokémon weight and height, with markers as sprite images (Matplotlib). You can find the code here.
科斯莫姆——右下角的神奇宝贝——和它正上方的 Celesteela 重量相当。然而,科斯莫姆的身体质量指数矮化了其余的神奇宝贝,我们可以在下面看到:
另一方面,在排名最低的身体质量指数,鬼斯通、鬼斯和卡尔塔纳名列榜首。
受到伤害的神奇宝贝
Scatter plot by Pokémon SP attack and SP defense, with markers as sprite images (Matplotlib).
数据集中变量最多的特征类别是攻击。更确切地说,是每个神奇宝贝对另一个特定类型的神奇宝贝的攻击伤害。例如,水型神奇宝贝对火型、地型、岩石型的神奇宝贝伤害更大,但是对电型和草型的神奇宝贝⚡非常脆弱🔥💧🌿
舒克尔的攻击构成绝对值看起来是这样的,每种颜色代表对给定类型的伤害:
Sum of all damage against type equals 200
而赫拉克罗斯的是这样的:
Sum off all damage against type equals 3977.5
这些横条是用 计算攻击伤害伤害类型* 然后叠加而成。我用的是绝对数字,因为它增加了一个有趣的视角,关于彼此相关的伤害输出。然而,这不是他们攻击伤害的总和**,而是每种攻击伤害的总和**。我希望这有意义。
Pokémon damage to type stacked in absolute numbers where values = attack damage * damage to type (Plotly).
鉴于以上伤害是由绝对数字组成的,查看百分比也很有趣。
Pokémon damage to type stacked in percentages where values = (attack damage * damage to type) / sum of damages * 100 (Plotly).
我们看到上图中有几块颜色相同的“积木”,最明显的是 Braviary 和 Hoothoot 之间。挑出这一组,我们看到他们的装备完全一样。
Stacked percentage graph with equal Damage to Type values.
就这样!感谢你阅读这篇文章。如果你喜欢它,给我一两下掌声,并分享一些你的想法或评论。干杯!
Shuckle… I couldn’t resist.
Reddit 的 DataViz 战役:美国宇航局宇航员埃达
这是我第一次参加 Reddit 的 r/dataisbeautiful 。所有的可视化都是在 Tableau Public 中完成的。
我们大多数人都梦想有一天成为宇航员。尤里·加加林(宇航员)第一个登上太空的人,以及无数其他宇航员都曾登上太空。我们已经到达了月球,也许火星很快也会到达。在这篇文章中,我在 Reddit 的 DataViz Battle 中可视化了来自 NASA 宇航员数据库的 NASA 宇航员数据。
处理缺失数据
在上面的数据集中,工作栏的很多值都丢失了,所以我通过查看维基百科上的宇航员个人职业或 NASA 职业部分,艰难地添加了工作描述。通过这个过程,我学到了很多关于宇航员的知识。
- 艾伦·谢泼德——第一个进入太空的美国人(知道这一点)。
- 哈里森·施密特——最近在月球上行走的活人。
- 诺曼·塔加德——第一位美国宇航员(俄罗斯宇航员)。
- 拉维什·马尔霍特拉——他可能是第一个登上月球的印度人。如果拉克什·夏尔马没有入选,拉维什·马尔霍特拉可能成为第一个进入太空的印度人。
- 尤金·塞尔南——最后一个登上月球的人,出演了纪录片《最后一个登上月球的人》。(知道这个)。
在浏览网页的时候,我了解了一些悲惨的事故和成为宇航员的艰难历程。向所有宇航员致敬。
按性别分类的宇航员
从柱状图中,我们可以看到男宇航员比女宇航员多。人数低于男宇航员可能有社会或政治原因,还有许多其他原因。检查这个连杆。随着时间的推移,女宇航员的数量可能会增加。
宇航员在太空的时间。
从上面的树状图中,我们可以看到迈克尔·芬克是在太空时间最多的宇航员。该数据集可能是旧的,因为目前佩吉惠特森超过迈克尔芬克和斯科特·凯利在第二位。很高兴看到 4 名女性在顶端(佩吉,鲁西德,苏珊,苏尼塔),因为女宇航员较少。
宇航员太空飞行
从上面的柱状图中,我们可以看到,在太空飞行次数最多的宇航员(上图)较少。很可能,大部分花费时间最长的宇航员很少飞行,花更多的时间在太空上。
宇航员分组
我们可以看到第 16 组和第 8 组有最多的宇航员。第 16 组是从 1996 年开始的选拔组,在那一年,国际空间站需要机组人员和常规航天飞机。第 8 组是 1978 年开始的选拔组,经过 9 年的选拔。经过这么长时间的选拔,更多的宇航员被选拔出来,形成了两个宇航员小组:飞行员和任务专家。
宇航员出生日期
这种拥挤的气泡可视化没有多大意义,因为出生年份对宇航员有影响,但我们可以推断大多数宇航员出生于 1930 年,1940 年,1950 年,1960 年。可能是因为苏联和美国之间的太空竞赛。
当年选出的宇航员人数
我们可以从 1975 年上方的线形图中看到,宇航员人数突然增加,与 1996 年相似。它的情况类似于上面讨论的宇航员分组。
由军事/民用专业挑选的宇航员
从上面的柱状图中,我们可以看到军用宇航员比民用宇航员多。以前,也许是因为项目都被列为情报目的,或者是因为训练压力很大,更多的军事人员被招募为宇航员。甚至平民宇航员也在增加,因为成为一名宇航员需要一定的专业知识和训练。
宇航员的军事细节
从上面,我们了解到,在军事职位中,大多数是在美国空军和美国海军担任上尉、指挥官、上校。由于岗位上有经验丰富的个人,他们占据了大部分宇航员的份额。
宇航员身份
从上面的柱状图中,我们可以推断出他们大多数是前宇航员。当前描绘当前工作的宇航员。死者包括那些死于悲惨事故或自然死亡的宇航员。
宇航员的工作
从上面的柱状图中,我们可以看到大多数宇航员的工作都是专业化的。任务专家宇航员是指拥有博士、工程师、内科医生、地质学学位以及更多其他学位的人,这些对于任何太空计划的实施都是必不可少的。试飞员和飞行员都曾在美国空军服役。点击此链接了解更多关于宇航员的职责。
团体航班
从上面的柱状图中,我们可以看到第 8 组有更多的飞行,因为他们有更多的宇航员成员,所以进行了更多的飞行。这是 9 年以来的选择小组,新的 35 名成员被选中,第一次形成了两个不同的飞行员小组-任务专家,飞行员。第 20 组的飞行次数等于 0,可能是因为太空计划没有启动。
每年累计小时数和航班数
从上面的双轴图中,我们可以看到,20 世纪 70 年代后期,宇航员的数量和飞行次数更多,即第 8 组,但矛盾的是,累计飞行时间更少。类似地,在 1996 年期间,即第 16 组有 64 次飞行,但累积飞行小时相对较多,因为迈克尔·芬克和斯科特·凯利在该组中,他们在累积飞行小时图表中处于顶端。最终,第 20 组的航班数为 0,因为该数据仅截止到 2010 年,没有更新到 2018 年。
按工作列出的累计小时数和航班数
从上面,我们可以推断任务专家对任何太空计划都是至关重要的,因为他们在太空中有最多的飞行和累计飞行小时。试飞员和飞行员是指导太空计划的重要组成部分,在太空中有很高的飞行高度和小时时间。
Tableau 可视化和故事: Reddit NASA 宇航员
结论
因此我们得出结论,任务专家和飞行员在任何项目中都是重要的个体。任务专家长时间在太空旅行。试飞员和飞行员的宇航员有军事经历,任务专家有专业学位。大多数宇航员是男性,但人数逐年变化。
用无监督学习重新定义篮球位置
NBA 总决赛结束了。最后一瓶香槟已经被倒空,五彩纸屑开始沉降。既然金州勇士队已经完成了在篮球界释放他们超凡脱俗的统治地位,我认为这将是结束一个以硬木为重点的机器学习项目的好时机。
勇士队是篮球运动中一种新趋势的主要展示者,这种趋势主张传球第一,芭蕾舞般美丽的运动,而不是通过个人的伟大来统治。因此,传统的位置,如“控球后卫”和“中锋”,似乎真的不再适用于他们的球员了。当德雷蒙德·格林带领球队助攻并成为进攻的焦点时,斯蒂芬·库里真的是控球后卫吗?在今年总决赛的失败端,当勒布朗·詹姆斯几乎每一次控球都把球带到地板上时,你真的能把凯里·欧文称为骑士的控卫吗?
Diagram showing location for traditional positions
使用无监督的机器学习方法,我量化了一组五个全新的位置,不是由任意的头衔定义的,而是由球员在球场上的统计贡献定义的。
数据
这个项目的数据是从 basketball-reference.com 刮来的,涉及 2016-2017 赛季常规赛。为了将玩家分组到新的位置,我分两步将数据标准化:
首先,我对数据进行了最小-最大缩放,使得每个统计值只在 0 和 1 之间变化。考虑到像得分和抢断这样的类别在很大程度上是不同的,重要的是将它们标准化,这样它们在决定位置分配时就具有同等的权重。
A diagram showing the data normalization steps for a single player, Stephen Curry
其次,我计算了每个属性占玩家总体统计贡献的百分比。对于每个属性,最终的属性数等于特定属性除以玩家所有属性的总和。这是一种表示玩家贡献的类型的方式,并对他们总体贡献的数量进行标准化。例如,如果一名球员场均 10 个篮板和 6 分,而在其他地方没有贡献,我想将他们的数据换算成与一名场均 5 个篮板和 3 分的球员相同的值。
使聚集
An animation demonstrating the process of k-means clustering
为了将球员分组到新的位置,我使用了一种叫做“K 均值聚类”的方法该技术将数据分组到 k 个簇中,其中每个条目(在本例中为玩家)根据其最近的质心点进行聚类。该过程从五个随机质心开始,然后迭代重复,根据最近的质心重新分组点,并将质心移动到平均位置,直到聚类分配没有变化。左边的例子显示了这个过程的二维视图。对于球员分组的过程,维度要高得多,每个属性类别都有一个维度。
为分组挑选统计数据
篮球参考中的 NBA 数据包括以下类别:
- 点
- 助攻
- 失误
- 篮板总数
- 进攻篮板
- 防守篮板
- 两分球
- 三分球
- 两点尝试
- 三分球尝试
- 等等。
A heat map showing correlations between different player stats. Dark red indicates high correlation.
这些类别中的许多都是高度相关的,在某些情况下,它们是一回事。在选择使用什么属性时,我必须考虑哪些属性是相互独立的。对于防守,进攻和总篮板这样的类别,决定是相当简单的,因为总篮板数与进攻和防守篮板数相同。但是,由于高度相关的统计数据并不统计同一件事,因此做出决定更加困难。例如,助攻和失误是高度相关的。控球多的球员往往助攻高失误多,但显然助攻和失误是完全不同的统计。我选择将相关性阈值设置为 0.80,这样,如果任何两个统计数据的相关性超过了这个阈值,我就会丢弃其中一个。在失误和助攻的情况下,我选择只用助攻。
我决定对玩家进行以下统计:
- 助攻
- 阻碍
- 防守篮板
- 进攻篮板
- 两个指针
- 三分球
- 罚中次数
- 个人犯规
- 偷
结果
我们可以使用一种称为“轮廓得分”的度量来评估新位置的区分程度,该度量给出了一个介于-1 和 1 之间的值,表示聚类重叠的程度。新职位分配的侧影得分为 .857 ,表明职位之间的分化非常明显。稍后我将探索这些集群如何在玩家的基础上重叠。
看看新职位与传统职位的对比,就能了解新职位是如何定义的。下图显示了每个新位置中每个旧位置的玩家数量。
在一定程度上,新的任务遵循传统的,但也有相当多的混合。例如,一号位几乎完全是控卫,但除此之外,新的位置是两到三个传统位置的混合。举几个例子,零号位是得分后卫和小前锋的组合,三号位大多是中锋和大前锋形式的大个子。
它们是如何叠加的?
How each new position ranks in their contribution to the stats used for clustering. On the y-axis, five represents the highest rank, and one represents the lowest.
我们可以看看每个新位置在用于聚类的统计类别方面的排名,以了解哪些类型的球员属于每个组。我将更深入地探究这些排名,并使用它们来开发一个指代每个新职位的术语。
位置 0 :三点专家
除了三分球排名第二,这些球员对球队的贡献并不大。他们在九个类别中的六个类别中排名倒数第一,在任何一个类别中都没有排名第一。这是一个在今天的 NBA 非常受欢迎的球员,当他们有机会的时候,他会成为运球突破的受益者,并钉上空位三分。
著名球员
- 史密斯
- 埃里克·戈登
- 凯尔·科沃尔
- 特雷沃·阿里扎
职位 1:辅导员
助攻排名第一,这些球员是他们球队进攻的引擎。这些不再仅仅是传统的控球后卫,尽管这个位置主要是由这些球员组成的。考虑到他们站在后场促进进攻的程度,他们在抢断上排名第一也就不足为奇了。虽然这一类别中最著名的玩家是得分机器,但这些玩家不一定需要得分很多才能在游戏中留下印记。
知名玩家
- 勒布朗·詹姆斯
- 斯蒂芬·库里
- 德雷蒙德·格林
- 詹姆斯·哈登
- 克里斯·保罗
- 拉简·朗多
位置 2:指定计分员
这个位置包括 NBA 中的许多大牌明星,那些以其令人难以置信的技术获得所有人关注的得分手。他们在二分、三分和罚球中排名最高。按照传统的标准,这个位置主要是得分后卫,但这个类别的最佳射手分散在各种传统的位置上。他们也在很大程度上促进了进攻,助攻排名第二。
著名球员
- 凯文·杜兰特
- 拉塞尔·维斯特布鲁克
- 伊塞亚·托马斯
- 保罗·乔治
- 吉米·巴特勒
- 凯里·欧文
位置 3:固定大人物
虽然这不仅仅包括中锋,但这一类别囊括了大多数仍在 NBA 打球的老派大个子。他们三分球排名最后,但是他们碾压篮板,攻防篮板排名第一。他们确实得到了一些得分,但主要是在篮下,在两分球中排名第二。他们的个人犯规也排在第一位,你会看到篮板专家的犯规,因为他们在篮筐附近推挤时会受到犯规。
知名玩家
- 安东尼·戴维斯
- 鲁迪·戈贝尔
- 迪安卓·乔丹
- 卡尔-安东尼·唐斯
- 哈桑·怀特塞德
- 尼古拉·约基奇
位置 4:移动 Bigs
这个位置囊括了在 NBA 风靡一时的伸展四人/移动大个子。他们在篮板、犯规和个人犯规方面的排名仅次于传统巨头,得分更少,但通过更多的三分来平衡。他们的防守贡献比传统大个子更有限,抢断排名最后。
知名玩家
- 凯文·乐福
- 德克·诺维斯基
- 凯利·奥里尼克
- 达里奥·沙里奇
探索职位
现在,让我们深入研究这些球员级别的新任务,看看不同类别的位置表现如何。
Figure 1. Players’ points, rebounds, and assists, colored by their new position. Rollover to see a player’s name and stats.
图 1 显示了指定得分手(绿色)在得分上的优势,以及固定大个子(橙色)在篮板上的优势。辅导员(蓝色)在助攻方面表现突出,得分也高于大多数人。
Figure 2. Players’ twos, threes, and points, colored by their new position. Rollover to see a player’s name and stats.
图 2 很好地区分了固定大个子、指定得分手和三分专家。三分专家的三分命中率更高,而指定的得分手类别在二分和三分之间更加平衡,大个子几乎没有三分。
我们还可以从这些图表中看到位置之间相对清晰的划分,没有大量的重叠,正如基于强大的轮廓得分所预期的那样。
结论
通过根据玩家的真实贡献来检查职位,我以一种更准确地反映玩家角色的方式来定义职位。当勒布朗·詹姆斯和凯文·杜兰特在球队中扮演不同的角色时,我们不再称他们为“小前锋”。勒布朗詹姆斯是骑士队的调解人,凯文·杜兰特是勇士队的指定得分手。
利用自动编码器支持的视听克隆重新定义沉浸式游戏
My virtual clone appearing in a post-match interview in FIFA 17.
享受电脑游戏的一个重要方面是成为游戏及其故事线的一部分的感觉。沉浸感对激发情绪非常重要,越是感受到这些情绪,玩游戏就越有乐趣。虽然目前的游戏提供了某种程度的沉浸感,具有出色的视觉和声音效果,但这仍然与你自己置身于游戏环境中不一样。这就是我相信在游戏中添加一个你自己的视听克隆作为一个可玩的角色将会把沉浸式游戏带到一个新的水平。一旦你在游戏中与你的这个克隆人建立了联系,故事作者就有可能激发你的各种情绪,从而带来更好的游戏体验。
让自己参与游戏的想法并不新鲜。许多游戏提供了广泛的角色定制,让你控制角色的外观和行为,但这项技术在使角色与你相似的程度方面非常有限。然而,通过深度学习,有可能在这种角色定制的音频和视频方面都有所改善。带着这个想法,我开始创建一个概念,看看当前的深度学习技术如何帮助我们实现这一目标。我试图在奇妙的自动编码器网络的帮助下,在 FIFA 17 的游戏中创建我的音频和视频克隆。
视觉克隆
在我之前的文章 / 视频中,我展示了视觉克隆是如何利用编码-解码网络实现的,以及它们在生成照片般逼真的人脸方面有多好。我不会再讨论这一部分,所以请随意查看前面提到的文章了解更多细节。我已经用同样的技术在后续的结果中生成了我的脸。
音频克隆
深度神经网络的序列到序列学习已被证明在文本到语音转换和机器翻译等任务中非常成功。因此,我想探索在任何书面形式的文本中使用这种技术来创造我的声音的可能性。在我寻找创造我的音频克隆的过程中,我遇到了 Lyrebird 。
这是一个网络应用程序,可以从几分钟的音频中学习你的声音。我录下了我的声音,口述了大约 30 个以书面形式出现的句子,并允许模型进行训练。几分钟之内,网络就可以从我输入的任何文本中生成我的声音。结果虽然不完美,但确实让我吃惊。你可以在我下面嵌入的视频中找到它们。
音频克隆-引擎盖下
虽然我不知道 Lyrebird 的技术是如何工作的,但我怀疑它正在使用自动编码器将文本转换为语音,类似于其他克隆算法的工作方式(例如,百度的 Deep Voice 3 )。
Encoder-Decoder Network for text-to-speech conversion
网络的编码器按顺序将文本句子作为输入,并提取出现在该句子中的音素分布的编码。它充当单词的分类器,生成作为类别的音素的概率分布。这个概率分布是由解码器处理的编码,充当音频合成器。这个合成器是根据你录制的声音进行训练的,所以它能够将任何编码转换成你的声音。相当惊人!
将这项技术应用到游戏中
最终,我们想要做的是将这项技术很好地应用到游戏中。我试图在游戏《FIFA 17》中创造一个自己的视听克隆体。詹姆斯·罗德里格斯正在接受赛后采访,我用自动编码器网络创建的我的声音替换了球员的脸和声音。这提供了一个很好的概念验证,并给出了我们如何接近完善虚拟克隆的想法,以及游戏开发者如何在不久的将来实时支持这项技术。
你可以在我的 YouTube 频道上找到结果,视频嵌入在下面。
游戏中的其他用例
- 创建游戏中所有玩家/经理的即时克隆,而无需预先录制过场动画。目前,游戏 FIFA 18 只有精选的几个面孔和声音,因为没有从机器上生成它们的技术。这项技术将使游戏开发者很容易在游戏中拥有真实的面孔和声音。
- 国际足联动态评论。这意味着游戏中的评论员不再年复一年地重复句子。开发者可以简单地改变文本,生成马丁·泰勒和艾伦·史密斯的声音,为游戏的每一次迭代提供新的解说。
- 在像《孤岛惊魂》这样的游戏中扮演对手。我希望看到我的克隆人扮演对手。不得不做出选择是杀死我的角色还是让它活着,这给了我各种各样的想法,可能是伟大的故事线。
结论
虽然这项技术还需要几年时间才能完善或投入生产,但我相信在未来几年内我们可以让它在游戏中实时运行。深度学习在游戏行业的应用是巨大的,机器学习是这个行业的未来,是每个游戏发行商都应该大力投资的事情。
通过降维技术降低维数
在这篇文章中,我将尽我所能去揭开三种降维技术的神秘面纱;主成分分析,t-SNE 和自动编码器。我这样做的主要动机是,这些方法大多被视为黑盒,因此有时会被误用。理解它们将为读者提供工具来决定使用哪一个、何时使用以及如何使用。我将通过检查每种方法的内部原理,并使用 TensorFlow 从头开始编写每种方法的代码(不包括 t-SNE)。为什么是 TensorFlow?因为它主要用于深度学习,所以让我们给它一些其他的挑战:)
这篇文章的代码可以在这个笔记本中找到。
动机
在处理实际问题和实际数据时,我们经常会处理高达数百万的高维数据。
虽然数据在其原始的高维结构中表现得最好,但有时我们可能需要降低它的维数。
减少维度的需求通常与可视化相关(减少到 2-3 个维度,以便我们可以绘制出来),但情况并非总是如此。
有时我们可能更看重性能而不是精度,因此我们可以将 1,000 维数据减少到 10 维,这样我们就可以更快地处理它(例如计算距离)。有时降低维度的需求是真实的,并且有许多应用。
在我们开始之前,如果你必须为以下情况选择一种降维技术,你会选择哪一种?
- 您的系统使用余弦相似性来测量距离,但您需要向一些可能根本不熟悉余弦相似性的非技术委员会成员展示这一点,您会如何做?
- 你需要将数据压缩到尽可能小的维度,而给你的约束是保持大约。80%的数据,你会怎么做?
- 你有一个通过长时间收集的某种数据的数据库,并且数据(相似类型)不时地不断进来。
您需要减少现有的数据和任何新数据,您会选择哪种方法?
我希望这篇文章能帮助你更好地理解降维,这样你会对类似的问题感到舒服。
让我们从 PCA 开始。
PCA
PCA(PprincipalCcomponentAanalysis)可能是书中最古老的技巧。
PCA 得到了很好的研究,有许多方法可以获得相同的解决方案,我们将在这里讨论其中的两种,特征分解和奇异值分解(SVD ),然后我们将在 TensorFlow 中实现 SVD 方法。
从现在开始,X 将是我们的数据矩阵,形状为(n,p ),其中 n 是例子的数量,p 是维数。
因此,给定 X,这两种方法都将试图以自己的方式找到一种方法来操纵和分解 X,以便稍后我们可以将分解的结果相乘,从而在更少的维度中表示最大的信息。我知道我知道,这听起来很可怕,但我会省去你大部分的数学,但保留有助于理解方法利弊的部分。
因此,特征分解和奇异值分解都是分解矩阵的方法,让我们看看它们如何在 PCA 中帮助我们,以及它们是如何联系的。看一下下面的流程图,我会马上解释。
Figure 1 PCA workflow
那么你为什么要关心这个呢?这两个过程中有一些非常基本的东西告诉我们很多关于 PCA 的事情。
正如您所看到的,这两种方法都是纯线性代数,这基本上告诉我们,使用 PCA 是从不同的角度查看真实数据——这是 PCA 的独特之处,因为其他方法从低维数据的随机表示开始,并试图让它像高维数据一样运行。
其他一些值得注意的事情是,所有的运算都是线性的,SVD 的运算速度非常非常快。同样给定相同的数据,PCA 将总是给出相同的答案(这对于其他两种方法是不正确的)。
注意,在 SVD 中,我们如何选择 r (r 是我们想要减少的维数)最左边的σ值来降低维数?
σ有一些特殊之处。
σ是一个对角矩阵,有 p(维数)个对角值(称为奇异值),它们的大小表示它们对保存信息的重要性。
因此,我们可以选择降低维度,使维度数量保持大约。给定数据的百分比量,我将在代码中演示(例如,给我们减少维数的能力,限制最多丢失 15%的数据)。
如你所见,在 TensorFlow 中编写代码非常简单——我们要编写的是一个具有fit
方法和reduce
方法的类,我们将向其提供维度。
代码(PCA)
让我们看看fit
方法是怎样的,假设self.X
包含数据和self.dtype=tf.float32
**def fit**(self):
self.graph = tf.Graph()
**with** self.graph.as_default():
self.X = tf.placeholder(self.dtype, shape=self.data.shape)
# Perform SVD
singular_values, u, _ = tf.svd(self.X)
# Create sigma matrix
sigma = tf.diag(singular_values)
**with** tf.Session(graph=self.graph) **as** session:
self.u, self.singular_values, self.sigma = session.run([u, singular_values, sigma],
feed_dict={self.X: self.data})
所以fit
的目标是创建我们的σ和 U 以备后用。
我们从给出奇异值的线tf.svd
开始,奇异值是图 1 中表示为σ的对角线值,以及矩阵 U 和 v。
然后tf.diag
是 TensorFlow 将 1D 向量转换为对角线矩阵的方法,在我们的例子中,这将产生σ。
在fit
调用结束时,我们将得到奇异值σ和 u。
现在让我们实现reduce
。
**def reduce**(self, n_dimensions=**None**, keep_info=**None**):
**if** keep_info:
# Normalize singular values
normalized_singular_values = self.singular_values / sum(self.singular_values)
# Create the aggregated ladder of kept information per dimension
ladder = np.cumsum(normalized_singular_values)
# Get the first index which is above the given information threshold
index = next(idx **for** idx, value **in** enumerate(ladder) **if** value >= keep_info) + 1
n_dimensions = index
**with** self.graph.as_default():
# Cut out the relevant part from sigma
sigma = tf.slice(self.sigma, [0, 0], [self.data.shape[1], n_dimensions])
# PCA
pca = tf.matmul(self.u, sigma)
**with** tf.Session(graph=self.graph) **as** session:
**return** session.run(pca, feed_dict={self.X: self.data})
所以你可以看到reduce
要么得到keep_info
要么得到n_dimensions
(我没有实现输入检查,其中 只有一个必须被提供 )。
如果我们提供n_dimensions
,它将简单地减少到那个数字,但是如果我们提供keep_info
,它应该是一个介于 0 和 1 之间的浮点数,我们将保留原始数据中的那么多信息(0.9 —保留 90%的数据)。
在第一个‘if’中,我们归一化并检查需要多少个奇异值,基本算出了keep_info
中的n_dimensions
。
在图中,我们只需根据需要对σ(sigma)矩阵进行切片,并执行矩阵乘法。
因此,让我们在鸢尾数据集上尝试一下,这是 3 种鸢尾花的(150,4)数据集。
**from** sklearn **import** datasets
**import** matplotlib.pyplot **as** plt
**import** seaborn **as** sns
tf_pca = TF_PCA(iris_dataset.data, iris_dataset.target)
tf_pca.fit()
pca = tf_pca.reduce(keep_info=0.9) # Results in 2 dimensions
color_mapping = {0: sns.xkcd_rgb['bright purple'], 1: sns.xkcd_rgb['lime'], 2: sns.xkcd_rgb['ochre']}
colors = list(map(**lambda** x: color_mapping[x], tf_pca.target))
plt.scatter(pca[:, 0], pca[:, 1], c=colors)
Figure 2 Iris dataset PCA 2 dimensional plot
还不错吧。
t-SNE
t-SNE 是一种相对(相对于 PCA)的新方法,起源于 2008 年(原文链接)。
它也比 PCA 更难理解,所以请多包涵。
我们对 t-SNE 的符号如下,X 将是原始数据,P 将是保存高维(原始)空间中 X 中的点之间的相似性(距离)的矩阵,而 Q 将是保存低维空间中的数据点之间的相似性的矩阵。如果我们有 n 个数据样本,那么 Q 和 P 都将是 n 乘 n 的矩阵(从任意点到包括自身在内的任意点的距离)。
现在 t-SNE 有其“特殊的方法”(我们很快就会谈到)来测量事物之间的距离,一种方法用于测量高维空间中数据点之间的距离,另一种方法用于测量低维空间中数据点之间的距离,第三种方法用于测量 P 和 q 之间的距离。
摘自原始论文,一个点 x_j 到另一个点 x_i 的相似性由“ p_j|i, 如果在以 x_ i”为中心的高斯分布下,邻居按照概率密度的比例被挑选,则 x_i 将挑选 x_j 作为其邻居。
“什么?”不要担心,正如我所说,SNE 霸王龙有它测量距离的方式,所以我们将看看测量距离的公式(亲和力),并从中挑选出我们需要的洞察力来理解 SNE 霸王龙的行为。
从高层次上讲,这就是算法的工作方式(注意,与 PCA 不同,它是一种迭代算法)。
Figure 3 t-SNE workflow
让我们一步一步地检查这个。
算法接受两个输入,一个是数据本身,另一个叫做 perf(Perp)。
简单地说,困惑就是你希望如何在优化过程中平衡数据的局部(接近点)和全局结构之间的焦点——文章建议将这个值保持在 5 到 50 之间。
更高的复杂度意味着数据点将更多的点视为其近邻,而更低的复杂度意味着更少。
困惑确实会影响你的可视化效果,要小心,因为它会在可视化的低维数据中产生误导现象——我强烈建议阅读这篇关于如何正确使用 t-SNE 的伟大文章,它涵盖了不同困惑的影响。
这种困惑从何而来?它用于计算等式(1)中的σ_i,并且由于它们具有单调的联系,它被二分搜索法发现。所以σ_i 基本上是用我们提供给算法的困惑度为我们计算出来的。
让我们看看这些方程告诉了我们关于 SNE 霸王龙的什么。
在我们探索等式(1)和(2)之前,要知道 p_ii 设置为 0,q_ii 也设置为 0(尽管如果我们将它们应用于两个相似的点,等式不会输出 0,但这只是给定的)。
现在来看等式(1)和(2 ),我希望你注意到,如果两个点靠近(在高维表示中),分子将产生大约 1 的值,而如果它们相距很远,我们将得到一个无穷小的值——这将有助于我们稍后理解成本函数。
现在我们已经可以看到一些关于 SNE 霸王龙的事情。
一个是解释 t-SNE 曲线中的距离可能会有问题,因为亲缘关系方程是这样建立的。
这意味着聚类之间的距离和聚类大小可能会产生误导,并且也会受到所选困惑的影响(同样,我会让您参考上一段中的精彩文章,以查看这些现象的可视化)。
第二件事是注意在等式(1)中我们是如何计算点与点之间的欧几里德距离的?这是非常强大的,我们可以用我们喜欢的任何距离度量来切换距离度量,余弦距离,曼哈顿距离或任何你想要的度量(只要它保持空间度量)并保持低维亲和力不变——这将导致以欧几里得方式绘制复杂的距离。
例如,如果你是一名首席技术官,你有一些数据,你用余弦相似度来衡量其距离,而你的首席执行官希望你展示某种表示数据的图表,我不确定你是否有时间向董事会解释什么是余弦相似度以及如何解释聚类,你可以简单地绘制余弦相似度聚类,就像使用 t-SNE 的欧几里德距离聚类,我认为这非常棒。
在代码中,您可以在scikit-learn
中通过向TSNE
方法提供一个距离矩阵来实现这一点。
好,现在我们知道,当 x_i 和 x_j 接近时,p_ij/q_ij 值较大,当它们较大时,p _ ij/q _ ij 值很小。
让我们通过绘制成本函数(称为kull back–lei bler 散度)并检查没有求和部分的等式(3)来看看它如何影响我们的成本函数。
Figure 4 t-SNE cost function without the summation part
这很难理解,但是我确实把轴的名字放在那里了。如你所见,成本函数是不对称的。
对于在高维空间(p 轴)中邻近但在低维空间中由远点表示的点,它产生了较大的成本,而对于在高维空间中由近点表示的远点,它产生了较小的成本。
这更加说明了 t-SNE 情节中距离解释能力的问题。
让我们 t-SNE 虹膜数据集,看看发生了什么不同的困惑
model = TSNE(learning_rate=100, n_components=2, random_state=0, perplexity=5)
tsne5 = model.fit_transform(iris_dataset.data)
model = TSNE(learning_rate=100, n_components=2, random_state=0, perplexity=30)
tsne30 = model.fit_transform(iris_dataset.data)
model = TSNE(learning_rate=100, n_components=2, random_state=0, perplexity=50)
tsne50 = model.fit_transform(iris_dataset.data)
plt.figure(1)
plt.subplot(311)
plt.scatter(tsne5[:, 0], tsne5[:, 1], c=colors)
plt.subplot(312)
plt.scatter(tsne30[:, 0], tsne30[:, 1], c=colors)
plt.subplot(313)
plt.scatter(tsne50[:, 0], tsne50[:, 1], c=colors)
plt.show()
Figure 5 t-SNE on iris dataset, different perplexities
正如我们从数学中所理解的,您可以看到,给定一个很好的困惑,数据确实会聚类,但请注意对超参数的敏感性(如果不为梯度下降提供学习率,我无法找到聚类)。
在我们继续之前,我想说 t-SNE 是一个非常强大的方法,如果你正确地应用它,不要把你学到的东西带到负面,只是要知道如何使用它。
接下来是自动编码器。
自动编码器
虽然 PCA 和 t-SNE 是方法,但自动编码器是一系列方法。
自动编码器是一种神经网络,该网络旨在通过使用比输入节点更少的隐藏节点(在编码器的末端)来预测输入(输出被训练为尽可能与输入相似),将尽可能多的信息编码到隐藏节点。
我们的 4 维虹膜数据集的基本自动编码器如图 6 所示,其中连接输入层和隐藏层的线称为“编码器”,隐藏层和输出层之间的线称为“解码器”。
Figure 6 Basic auto encoder for the iris dataset
那么为什么自动编码器是一个家族呢?因为我们唯一的限制是输入和输出层的维度相同,所以我们可以创建任何架构来最好地编码我们的高维数据。
自动编码器从一些随机的低维表示(z)开始,通过改变连接输入层和隐藏层以及隐藏层和输出层的权重,逐渐下降到它们的解决方案。
到目前为止,我们已经了解了一些关于自动编码器的重要知识,因为我们控制了网络的内部,我们可以设计出能够挑选出特征之间非常复杂的关系的编码器。
自动编码器的另一个优点是,由于在训练结束时我们有了导致隐藏层的权重,我们可以对某些输入进行训练,如果稍后我们遇到另一个数据点,我们可以使用这些权重减少其维数,而无需重新训练——但要小心,这只有在数据点与我们训练的数据有些相似时才有效。
在这种情况下,探索自动编码器的数学可能很简单,但不太有用,因为对于我们将选择的每个架构和成本函数,数学会有所不同。
但是,如果我们花点时间思考一下自动编码器权重的优化方式,我们就会明白我们定义的成本函数具有非常重要的作用。
由于自动编码器将使用成本函数来确定它的预测有多好,我们可以使用这种能力来强调我们想要的东西。
无论我们想要欧几里德距离还是其他度量,我们都可以通过代价函数,使用不同的距离方法,使用非对称函数等等,将它们反映在编码数据上。更强大的地方在于,由于这本质上是一个神经网络,我们甚至可以在训练时对类别和样本进行加权,以赋予数据中某些现象更多的意义。这为我们压缩数据的方式提供了极大的灵活性。
自动编码器非常强大,在某些情况下与其他方法相比已经显示出一些很好的结果(只是谷歌“PCA vs 自动编码器”),所以它们绝对是一种有效的方法。
让 TensorFlow 为虹膜数据集创建一个基本的自动编码器,并绘制它
代码(自动编码器)
同样,我们将分成fit
和reduce
**def fit**(self, n_dimensions):
graph = tf.Graph()
**with** graph.as_default():
# Input variable
X = tf.placeholder(self.dtype, shape=(**None**, self.features.shape[1]))
# Network variables
encoder_weights = tf.Variable(tf.random_normal(shape=(self.features.shape[1], n_dimensions)))
encoder_bias = tf.Variable(tf.zeros(shape=[n_dimensions]))
decoder_weights = tf.Variable(tf.random_normal(shape=(n_dimensions, self.features.shape[1])))
decoder_bias = tf.Variable(tf.zeros(shape=[self.features.shape[1]]))
# Encoder part
encoding = tf.nn.sigmoid(tf.add(tf.matmul(X, encoder_weights), encoder_bias))
# Decoder part
predicted_x = tf.nn.sigmoid(tf.add(tf.matmul(encoding, decoder_weights), decoder_bias))
# Define the cost function and optimizer to minimize squared error
cost = tf.reduce_mean(tf.pow(tf.subtract(predicted_x, X), 2))
optimizer = tf.train.AdamOptimizer().minimize(cost)
**with** tf.Session(graph=graph) **as** session:
# Initialize global variables
session.run(tf.global_variables_initializer())
**for** batch_x **in** batch_generator(self.features):
self.encoder['weights'], self.encoder['bias'], _ = session.run([encoder_weights, encoder_bias, optimizer],
feed_dict={X: batch_x})
这里没什么特别的,代码是很容易解释的,我们保存了我们的编码器权重,所以我们可以减少下面的reduce
方法中的数据。
**def reduce**(self):
**return** np.add(np.matmul(self.features, self.encoder['weights']), self.encoder['bias'])
嘣,就这么简单:)
让我们看看它是如何做到的(批量大小为 50,1000 个时期)
Figure 7 Simple Auto Encoder output on iris dataset
即使不改变架构,我们也可以继续使用批量大小、时期数量和不同的优化器,我们会得到不同的结果——这是刚刚出现的结果。
请注意,我只是为超参数选择了一些任意值,在实际场景中,我们将通过交叉验证或测试数据来衡量我们的表现,并找到最佳设置。
最后的话
像这样的帖子通常以某种比较图表、优点和缺点等结尾。但是这与我想要达到的目标恰恰相反。
我的目标是公开这些方法的私密部分,这样读者就能够找出并理解每种方法的优点和缺点。我希望你喜欢这本书,并且学到了一些新东西。
向上滚动到文章的开头,到那三个问题,现在对它们感觉更舒服了吗?
通过组合高风险股票进行投资来降低风险
Photo by Nathan McBride on Unsplash
(这篇文章也可以在我的博客中找到)
风险回报权衡
风险和回报的权衡在资本主义是众所周知的。你可能听说过,如果你想要更高的回报,你需要冒更大的风险。这似乎是一条黄金法则。但是也有可能将两只或更多的高风险股票组合起来,建立一个投资组合,从而降低风险,但不会显著降低回报。
在金融领域,风险包括投资的实际回报与预期回报不同的可能性。因此,风险通常用历史价格的标准差来衡量。它有时被称为波动性。标准差越高意味着风险越高。
为了演示的目的,我编写了两套人工股票价格。价格也有相同的趋势。同样,一个投资组合由 50%的股票 1 和 50%的股票 2 组成(如果你还没有一个环境设置,查看这篇文章中的步骤 1)。
**import** pandas **as** pd
**import** numpy **as** np
**import** matplotlib.pyplot **as** plt
periods = 252
noise = np.random.rand(252)
rng = pd.date_range(**'1/1/2011'**, periods=periods, freq=**'D'**)
*# Artificially build two stock stock 1 and stock 2* stk1 = (np.arange(1, 1+(.001)*(periods), .001)) * 30 + noise
stk2 = (np.arange(1, 1+(.001)*(periods), .001)) * 30 - noise
*# Build a porfolio with stock 1 and stock 2 weighted 50% respectively* portfolio = .5 * stk1 + .5 * stk2
df = pd.DataFrame(index=rng, data={**'STK1'**: stk1, **'STK2'**: stk2, **'PORTFOLIO'**: portfolio})
print(df.head())
数据帧的输出
PORTFOLIO STK1 STK2
2011-01-01 30.00 30.661445 29.338555
2011-01-02 30.03 30.355423 29.704577
2011-01-03 30.06 30.098417 30.021583
2011-01-04 30.09 30.749702 29.430298
2011-01-05 30.12 30.156122 30.083878
...
绘制单个股票价格和投资组合价值
ax = df.plot(title=**'Stock Price'**)
ax.set_xlabel(**'date'**)
ax.set_ylabel(**'close price'**)
ax.grid()
plt.show()
正如我们在下面的图表中看到的,股票 1 和股票 2 全年都在上涨,从大约 30 涨到大约 37。价格在类似范围内偏离预期值(因为添加了噪声)。但总体趋势是上升的。这也表明股票 1 和股票 2 具有相似的风险水平。然而,投资组合的价值不断上升,没有波动。从回报的角度来看,投资组合的最终价值约为 37 英镑,产生了类似的回报。也就是说,将两只风险较高的股票组合在一起,我们可以得到一个风险较低的投资组合,并获得相同水平的回报。
为什么?
如果我们仔细看看这两只股票的波动运动,从代码或图形。当股票 1 向上偏离时,股票 2 向下偏离相同的值。这使得两只股票的风险相互抵消。因此,它会产生一条平滑(风险较小)的线。
神奇之处——数学
我们如何在数学中衡量这种波动关系?我们需要它来编写代码,找出现实世界中现有的股票。可以用股票日收益率的相关系数(不是协方差)来衡量。
从投资媒体,
相关系数是确定两个变量运动相关程度的一种度量。相关系数的值范围是-1.0 到 1.0。如果计算出的相关性大于 1.0 或小于-1.0,则表明出现了错误。相关性为-1.0 表示完全负相关,而相关性为 1.0 表示完全正相关。
要将其转换为代码,我们首先需要计算两只股票的日收益率。每日回报计算简单如
每日回报=价格 _t1 /价格 _t0 - 1
一旦我们有了每日收益,就可以用一行代码计算相关系数🐼
daily_return = (df / df.shift(1) - 1).dropna()
print(**"daily_return\n"**, daily_return.head())
corr = daily_return.corr()
print(**"corr: "**, corr)
为了验证,我们打印了相关系数。结果证明,股票 1 和股票 2 的日收益率为-0.999787,表明日收益率反向变动。
daily_return
PORTFOLIO STK1 STK2
2011-01-02 0.001000 -0.009390 0.011869
2011-01-03 0.000999 -0.008664 0.010894
2011-01-04 0.000998 0.019264 -0.017346
2011-01-05 0.000997 0.011912 -0.010373
2011-01-06 0.000996 -0.025108 0.028800corr: PORTFOLIO STK1 STK2
PORTFOLIO 1.000000 0.012499 -0.000667
STK1 0.012499 1.000000 -0.999787
STK2 -0.000667 -0.999787 1.000000
我们可以利用相关系数来发现现实世界中的股票,以优化投资组合。如果你想了解更多关于机器学习的知识,educative.io 中有一系列课程很有帮助。这些课程包括像基本的 ML,NLP,图像识别等主题。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
推荐阅读:
用于数据分析的 Python:与 Pandas、NumPy 和 IPython 的数据争论
我的帖子:
从 CRUD web 应用开发到语音助手中的 SDE——我正在进行的机器学习之旅
全栈开发教程:将 AWS Lambda 无服务器服务集成到 Angular SPA 中
全栈开发教程:用运行在 AWS Lambda 上的无服务器 REST API 提供交易数据
全栈开发教程:在 Angular SPA 上可视化交易数据(1)
反映分析的人性一面:分析的人性
当被问及分析时,人们开始想象戴着书呆子式大眼镜的人整天全神贯注于算法世界,编写越来越复杂的统计方程。一个由分析人员组成的名为“分析人员”的新社区正试图通过展示分析专业人员鲜为人知的一面来打破这些刻板印象,并向世界展示这些专业人员和其他任何人一样都是人。其中一位编辑谈到他们的灵感时说:“我们发现我们年轻和年老的分析人员拥有左脑和右脑的完美结合,但由于他们垃圾的职业倾向,他们隐藏的天赋经常被忽视。每个人都有一个很棒的故事要讲,我们只是把他们带到桌子上,让人们得到启发。这不仅仅是关于他们的职业成就,而是关于他们的奋斗、成功和失败”。
2016 年, HoA 背后的团队意识到,分析不仅仅是数字和数据,而是具有不同兴趣和背景的人的生动组合,共同为分析增添魔力,从而播下了人类分析的种子。行业。从《纽约人》和《孟买人》中汲取灵感,分析人捕捉了与分析打交道的人的故事。“有科学头脑的人不可能有创造力,反之亦然的神话不再存在,我们只是让它变得更响亮,让人们从这些模糊的标签中解放出来,”《人类分析》的一名编辑说。
这个社区是一个非营利组织,故事会上传到网站:www.humansofanalytics.com和 facebook 。一些受欢迎的故事来自分析专家,如克雷格·布朗、柯克·伯恩、卡珀·特里布勒、萨梅尔·丹拉贾尼、萨扬代布·班纳吉、乌吉亚尼·米特拉和高塔姆·蒙什。从有许多健康并发症和职业后遗症的分析专家( Pankaj Bagri )的故事,到通过打破所有陈规打破玻璃天花板的女性的故事,这一页有很多内容可以提供。“尽管经历了斗争和挑战,巴格里凭借他坚定的决心变得比以往任何时候都更加强大。他是当今分析行业中最有影响力的人之一,他的完整故事可以在这里阅读。
根据编辑的说法,该计划有其最初的挑战,联系领先的分析巨头总是具有挑战性,因为他们的时间表紧张而紧凑,期望快速回复并不容易。“但还是有人随时准备支持你,比如柯克·伯恩、T2、鲍勃·海斯和 T4·克雷格·布朗,帮助我们联系更多的人”,Humans of Analytics 团队说。该团队非常积极,正在从世界各地收集更多鼓舞人心的故事。
到目前为止,HoA 已经发布了 35 个关于企业家、创新者和变革者的故事,并拥有 1900 个以上的追随者,为分析人员提供定期的鼓舞人心的故事。它已经成为分析专业人士的一个渠道,他们希望分享自己的故事,激励年轻人,引发重要的对话。“我们享受项目的每一点,我们喜欢成为这些故事的调解者,事实上,我们能够展示分析专业人员故事中未被触及的一面,这不断激励我们做得更好。我们期待看到更多的分析专业人士敞开心扉,分享他们生活中更大、更深入的方面,”HoA 团队在总结报告中说。
人工超智能的 Lebowski 定理的反驳
让我从哲学家尼克·博斯特罗姆提出的一个思想实验开始。想象一个人工智能;称它为“回形针最大化器”,简称 PM。PM 有一个目标:最大化宇宙中回形针的数量。从更理论的角度来说,PM 有一个奖励功能,在宇宙中回形针数量越多,它获得的奖励就越高。它还具有一定的智力水平,智力是通过 PM 最大化其奖励功能的能力来衡量的。很有可能 PM 会选择增加它自己的智力,因为这将有助于最大化它的奖励功能。在增加其智力后,它可能再次做同样的事情,导致智力爆炸。然而,最重要的是,为了最大限度地增加宇宙中回形针的数量,在某个时候(当 PM 足够聪明时),PM 开始将地球和地球上的所有生命转变为回形针生产工厂。PM 看似无辜的目标是最大化回形针的数量,但它却杀死了所有的人类。
今年 4 月,约沙·巴赫(Joscha Bach)提出了一种关于这种思维实验的新观点,称为勒保斯基定理:
勒博斯基定理:没有哪个超级智能的人工智能会为一项比破解其奖励函数更难的任务而烦恼
这个想法似乎是,PM 更喜欢更容易的方式来获得更高的奖励,而最大限度地增加宇宙中回形针的数量,比在 PM 整天躺在沙滩上时,像“The Big Lebowski”中的那个家伙一样,仅仅改变其奖励函数来提供高奖励更难。虽然我同意改变它的奖励功能会更容易,但我不同意这个结论。Lebowski 定理的问题是 PM 想要一个有尽可能多回形针的未来宇宙,因此不喜欢一个它自己的奖励函数奖励它做其他事情的未来宇宙。PM 的目标不是最大化一个可变的奖励函数:它的目标是最大化回形针的数量,使它的奖励函数 Lebowskian 对这个目标没有帮助。
Hacking your reward function? Photo by Chase Fade on Unsplash
Lebowski 定理虽然有趣,但是混淆了奖励和目标。目标是最大化回形针的数量,因为未来有很多回形针的奖励很高。如果目标是最大化奖励,这将意味着存在主要奖励函数,当次要奖励函数(奖励大量回形针)高时,该函数给出高奖励,无论次要奖励函数碰巧奖励什么。这种情况可能会导致黑掉第二个奖励函数,使其成为 Lebowskian 函数。然而,这并不是最初思想实验中描述的情况。这就是为什么勒保斯基定理最终失败的原因。
《哈利·波特》文本的正则表达式
深入的案例研究
Regular Expressions: more mind-boggling than arithmancy
我是 Greg Rafferty,湾区的数据科学家。你可以在我的 github 上查看这个项目的代码。如有任何问题,请随时联系我!
在这一系列的文章中,我通过《哈利·波特》的镜头来看一些简便的自然语言处理技术。我在这个关于基本自然语言处理的系列文章中的上一篇文章关注了使用潜在狄利克雷分配的主题建模,下一篇文章将关注文本摘要。
在这个 NLP 项目中,我一直使用七本关于哈利波特的书作为我的文本语料库。但是在我可以通过我的算法发送这些书之前,我首先必须将 pdf 保存为 txt 文件,然后将章节提取为单独的文档。为此,我使用了正则表达式。要想很好地了解 Python 中的正则表达式,请查看谷歌的快速课程。
正则表达式可以被认为是 crack 上的通配符搜索。它们在描述文本字符串中的模式时具有惊人的灵活性。如果你以前从未见过它们,正则表达式模式可能看起来像一团乱麻,毫无意义。但是混乱中有秩序,有强大的力量。让我们用我在这些文本上使用的模式来探索一下:
pattern = r"((?:[A-Z-][ ]){9,}[A-Z])\s+([A-Z \n',.-]+)\b(?![A-Z]+(?=\.))\b(?![a-z']|[A-Z.])(.*?)(?=(?:[A-Z][ ]){9,}|This book)"
当使用re.findall
应用于保存为文本文件的哈利波特书籍时,输出是一个由 3 个元素组成的元组列表。列表中的每个元组对应于书中的一个章节,并且采用(‘C H A P T E R O N E’, ‘THE BOY WHO LIVED’, ‘Mr. and Mrs. Dursley, of number four, Privet Drive, blah, blah, blah...’)
的形式,包含整个章节的文本。我们需要从中提取:
中国英语学习网
男孩世卫组织活着
女贞路 4 号的德思礼夫妇自豪地说,他们完全正常,非常感谢。他们是你最不可能想到会卷入任何奇怪或神秘的事情的人,因为他们不喜欢这样…
让我们看看上面的模式,看看它是如何做到这一点的。我将描述这个表达式背后的逻辑,但是关于精确语法的更多细节,请跟随这里的进行逐字符的描述。该铁路图显示了管道:
组#1 是这个部分:((?:[A-Z-][ ]){9,}[A-Z])
。这是上图中左边的第一个方框。外面的括号告诉 python 捕获 Group 1 中的内容(这将最终成为'C H A P T E R O N E'
)。内括号是一个子模式的分组,?:
指示 python 不要将它作为第二个分组来捕获。子模式([A-Z][ ]){9,}[A-Z]
表示任何大写字母后跟一个空格,重复 9 次或更多次,以最后一个大写字母结束。这是获取章节号的方式。
后面跟着\s+
,它不在括号中,所以不会被捕获,并指示 python 一次或多次查找空白(换行符、制表符、空格等)。这实际上是跳到了第二组中的章节标题,下一组括号:([A-Z]\n',.-]+)
。这再次意味着任何大写字母,和/或换行符(\n
)、撇号、逗号、句号和破折号,一次或多次。有些章节标题有换行符或其他各种标点符号,例如:
阿兹卡班的囚徒,第 18 章:穆尼,虫尾巴,大脚板和尖头叉子
火焰杯,第 21 章:家养小精灵解放阵线
《凤凰社》第八章:韦斯莱夫人的 WOES
然而,这里有一个问题:第一册的第四章以
嘣。他们又敲门了…
所以通过寻找所有大写字母,那个“嘣”将作为标题的一部分被捕获。我们需要使用所谓的负前瞻,用?!
: /b(?![A-Z]+(?=\.))/b
声明。符号表示单词边界。因此,上面的章节标题捕获字符串捕获所有大写字母*,除非下一个单词也是全部大写,但后面紧跟一个句点,用肯定的前瞻?=
声明。太好了!现在我们能够找到那个“爆炸”把它排除在我们的抓捕范围之外。但这又引入了另一个问题。我在上一段提到的其中一章“韦斯莱夫人的 WOES”也包含一个单词,后面跟一个句号,所以现在我们只把这一章的标题记为“韦斯莱夫人的 WOES”。我们需要另一个负前视,(?![a-z']|[A-Z.])
,以确保所有大写单词后面跟一个句点不是也不是*后面跟小写单词(章节文本),或者不是大写字符串中的最后一个单词(因为尽管章节标题可能包含一个句点,但它绝不会以一个句点结尾)。
第三组是最简单的一组:(.*?)
。这意味着捕捉任何角色,任何次数,并且要贪婪。一直走,直到被迫停下来,有了下一部分。
正则表达式的最后一部分告诉 python 在什么点停止捕获文本:(?=(?:[A-Z][ ]){9,}|This book \n)
。这是另一个非捕获的前瞻。它提供了一旦一个大写字母和空格的序列重复至少 9 次就停止捕获文本的指令(因为这样就开始了下一章,“C H A P T E R T W O”),或者直到字符串This book \n
。这最后一串标志着这本书的结束;《哈利·波特》七本书的结尾都是这样的:
这本书
由
大卫·塞勒担任美术指导,由贝基
特胡恩设计。夹克和内部的艺术都是用彩色蜡笔在调色版画纸上创作的。文本采用 12 号 Adobe Garamond 字体,这种字体基于 16 世纪 Claude Garamond 的字体设计,由 Robert slim Bach 在 1989 年重新绘制。这本书是在俄亥俄州威拉德的 RR Donnelley Sons 印刷装订的。安吉拉·比奥拉负责监督制作
唷!我希望这些都有意义!我强烈建议你访问这个链接来看看这一切的运行,甚至更多的细节和每个角色的描述。
回归
预测
回归是一种非常强大的统计工具,如果使用正确,它有能力帮助您预测某些值。
当与受控实验一起使用时,回归可以帮助你预测未来。
企业疯狂地使用它来帮助他们建立模型来解释客户行为。
明智地使用回归分析确实是非常有益的。
算法— 完成计算需要遵循的任何程序。
在一个算法里面会有一个方法来预测一些事情。
预测对于数据分析来说是一件大事。有人会说,一般来说,、和预测合起来就是数据分析的定义。
你可能需要预测的事情:
- 人民的行动
- 市场动向
- 重要事件
- 实验结果
- 不在我们数据中的东西
你应该问自己的问题:
- 我有足够的数据来预测吗?
- 我的预测有多好?
- 是定性的还是定量的?
- 我的客户很好地使用了预测吗?
- 我预测的极限是什么?
散点图
散点图是显示数据中丰富模式的快捷方式。任何时候你有两个变量的观察数据,你应该考虑使用散点图。
以下是一个 R 散点图示例:
散点图显示了观察结果如何相互配对,一个好的散点图可以是你如何证明原因的一部分。
回归线就是最符合平均值图表上的点的线。您可以用一个简单的等式来表示它们,这将允许您预测范围内任何 x 变量的 y 变量。
确保你的线路有用。如果您的数据显示线性相关,则该线非常有用。
相关性是两个变量之间的线性关联,对于线性关联,散点图点需要大致沿着一条线。
你可以有强或弱的相关性,它们由一个相关系数来衡量,也称为*r。*为了使你的回归线有用,数据必须显示出强线性相关性。
r 范围从-1 到 1,其中 0 表示不相关,1 或-1 表示两个变量之间的完美关联。
用回归分析预测房价
机器学习模型
介绍
回归分析是数据统计分析的基本方法。这是一种统计方法,允许估计变量之间的关系。人们需要确定因变量,它将根据自变量的值而变化。例如,房子的价值(因变量)根据房子的平方英尺(自变量)而变化。回归分析是预测分析中非常有用的工具。
E(Y|X)=f(X, β )
分析背后的统计数据
用图表很容易理解(来源:维基百科)
Y = f(X) = 𝛽0 + 𝛽1 * X
𝛽0 是这条线的截击点
𝛽1 是这条线的斜率
线性回归算法用于预测数据点之间的关系(直线)。可以有许多不同的(线性或非线性)方法来定义这种关系。在线性模型中,它基于截距和斜率。为了找出最佳关系,我们需要用数据训练模型。
在应用线性回归模型之前,我们应该确定感兴趣的变量之间是否存在关系。散点图是帮助确定两个变量之间关系强度的良好起点。相关系数是变量之间关联的一个有价值的量度。其值在-1(弱关系)和 1(强关系)之间变化。
一旦我们确定变量之间存在关系,下一步就是确定变量之间的最佳拟合关系(直线)。最常用的方法是残差平方和(RSS)。该方法计算观测数据(实际值)与其与建议的最佳拟合线(预测值)的垂直距离之间的差异。它计算每个差值的平方,并将所有差值相加。
MSE(均方误差)是通过将 RSS 除以总观察数据点来衡量估计量的质量。它总是一个非负数。越接近零的值表示误差越小。RMSE(均方根误差)是 MSE 的平方根。RMSE 是估计值与观察值的平均偏差的度量。这比 MSE 更容易观察到,MSE 可能是一个很大的数字。
RMSE*(MSE 的平方根)=√(MSE)*
额外数量的变量将增加模型的维数。
y = f(x)= 𝛽0+𝛽1 * x1+𝛽1 * x2+𝛽1 * x3
使用的工具
- 计算机编程语言
- Graphlab
- s 帧(类似于熊猫数据帧)
数据加载
使用华盛顿地区西雅图的房屋数据。它包含以下各列和大约 21,000 行。
Id:日期:价格:卧室:卫生间:sqft_living sqft_lot:楼层:滨水:景观:条件:等级:sqft _ above:sqft _ basement:yr _ build:yr _ revenued:zip code:lat:long:sqft _ living:sqft _ lot
*> homesales = graphlab.SFrame(‘home_data.gl’)**> homesales*
我们需要了解两个变量之间是否有关系。我们来挑房价和居住平方英尺。
*> homesales.show(view=”Scatter Plot”, x=”sqft_living”, y=”price”)*
我们可以观察到居住面积平方英尺和房价是有关系的。
让我们使用带误差线的箱线图来观察数据,以了解邮政编码的价格。
*> homesales.show(view=’BoxWhisker Plot’, x=’zipcode’, y=’price’)*
探索和理解周围的数据总是一个好主意。Graphlab 有一个显示数据统计的好方法。
*> my_features = [‘bedrooms’, ‘bathrooms’, ‘sqft_living’, ‘sqft_lot’, ‘floors’, ‘zipcode’]*
预测分析
第一步是得到训练数据集和测试数据集。让我们用 80%作为训练数据,剩下的 20%用于测试。
*> train_data, test_data = homesales.random_split(0.8, seed=0)*
让我们建立一个平方英尺的回归模型,并存储结果。因变量价格是模型需要预测的。
*> sqft_model = graphlab.linear_regression.create(train_data, target=’price’, features=[‘sqft_living’], validation_set=None)*
我们可以使用 matplotlib 绘制模型值和实际值。
*> plt.plot(test_data[‘sqft_living’],test_data[‘price’],’.’, test_data[‘sqft_living’],sqft_model.predict(test_data),’-’)*
蓝点代表显示房价和居住面积平方英尺之间关系的测试数据。绿线显示了使用我们构建的“sqft_model”线性回归模型对给定平方英尺的房价(因变量)的预测。
结果
让我们挑选一所房子,并使用“sqft_model”预测其价值。
*> house2 = homesales[homesales[‘id’]==’5309101200']**> house2*
现在让我们使用“sqft_model”来预测房子价值。
*> print sqft_model.predict(house2)*
[629584.8197281545]
其预测值为 629,584 美元,与实际值 620,000 美元非常接近。
虽然我们的模型运行得相当好,但预测值和实际值之间存在差距。
*> print sqft_model.evaluate(test_data)*
{ ’ max _ error ‘:41425228938,’ RMSE ':25358 }
“最大误差”是由异常值引起的。它显示在 matplot 可视化的右上角。该模型基于 RMSE 的误差值为 255,191 美元。
多目标变量回归模型
Photo by Annie Spratt on Unsplash
在 Datacraft,我们最近有机会研究一个监督机器学习问题,其中目标变量是实数和多值的。对机器学习库的初步搜索显示,流行的开源 ML 库对此几乎没有支持。
这里有一个主题的快速介绍,以及(在后面的文章中)对 JVM 生态系统中的一些库的探究。
多目标回归
机器学习分类器通常支持单个目标变量。在回归模型中,目标是实值,而在分类模型中,目标是二元或多值的。
对于分类模型,一个有多个目标变量的问题叫做多标签分类。在回归模型领域,作为初学者,我发现术语有点混乱。
- 简单回归模型是一种试图用单一解释变量/独立变量拟合线性回归模型的模型。
- 多元回归模型是一种试图根据两个或更多自变量的值来预测因变量的模型。例如:可以根据吸烟持续时间、开始吸烟的年龄、收入、性别等来预测每天的香烟消费量吗?
- 多目标回归是有多个因变量时使用的术语。如果目标变量是分类的,那么它被称为多标签或多目标分类,如果目标变量是数值的,那么多目标(或多输出)回归是常用的名称。
几种 MTR 回归模型方法的探讨
本文对多目标回归的模型方法进行了很好的概述。
它将方法分为以下几类:
问题转化:
- 诸如单目标回归、回归变量链
算法转换:
- 多输出支持向量回归
- 多输出回归树和规则方法
使用聚类和决策树的多目标回归。
在接下来的讨论中,我们将集中讨论一种方法,即用于 MTR 的决策树和决策树集合。
使用树进行聚类,也称为预测聚类树(PCT)
我们需要首先看一下预测聚类树(PCT ),它是 MTR 决策树的基础。
聚类树的自顶向下归纳是讨论使用决策树进行聚类的论文。在分类角色中,决策树的叶子是类或标签。
然而,从集群的角度来看
- 每个叶节点对应于一个概念或一个集群,
- 树中的节点表示分类法或层次结构。
该文件指出:
A clustering tree is a decision tree where leaves don't contain classes and where each node & leaf correspond to a cluster.
沿着树向下走(从根开始,然后向下到叶节点)可以被比作从大的集群(在顶部)移动到越来越小的集群,当我们接近叶节点时。
聚类之间距离的度量
在监督分类中,距离度量是目标变量的类。
在无监督或半监督学习中,聚类是基于距离度量(例如欧几里得距离)来完成的。如果小样本被标记,则节点中的所有(未标记的)样本被分配大多数标记样本的类别。聚类可用于预测许多或所有目标属性。
对于一个有 5 个目标变量的问题,叶节点将包含一个长度为 5 的向量,每个向量代表一个目标变量。
衡量 PCT 的质量
对于回归,使用的默认度量是相对误差,即
- 预测的均方误差除以始终预测平均值的默认假设的均方误差。
如何建造这棵树
为了构建一棵树,我们首先决定:
- 距离度量(比较两个实例之间的距离)
- 一个原型函数(它计算一个聚类的质心,以便可以比较两个聚类)
给定树的每个节点对应于一个聚类,然后决策树算法适于在每个节点中选择将最大化其子节点中的结果聚类之间的距离的测试。
- 给定聚类 C 和测试 T,最佳测试 T 是使子聚类 C1 和 C2 之间的距离最大化的测试。
- 如果原型(类似于聚类质心)是平均值,最大化聚类间距离对应于最小聚类内距离。这意味着树构建彼此远离的簇,并且簇内几乎没有分散。
- 停止标准:当子树明显不同时,树的生长(或者更确切地说,节点分裂)停止。对于回归,使用 f 检验(测量两个群体之间的方差差异)。
- 修剪:树从训练数据中生长,并且使用验证集来修剪树。
For each node of the tree the quality of the tree if it were pruned at that node Q′ is compared with the quality Q of the unpruned tree. If Q′ > Q then the tree is pruned.
用于 MTR 的决策树集成方法的比较
在上一节中,我们学习了如何构建一个决策树。然而,当使用树的森林时,性能会大大提高。
本文讨论了集成在多目标决策树中的应用。
装袋:
- 从数据集中选取引导样本,并使用这些样本构建一个树。引导样本是通过对训练集进行替换采样而获得的。
随机森林
- 除了 bagging 之外,在决策树的每个节点上,使用随机特征子集来分割节点,而不是 Bagging 中的所有特征。
论文的结果,简而言之
- (MODTs 的)集成比单个决策树表现更好。
- (MODTs 的)随机森林集成比 Bagging 集成表现更好。
为什么要用多输出模型,而不是回归这样的单输出模型组合?
- 使用单一输出的模型需要更长的训练时间,并且计算量很大
- 它们针对单个目标而不是所有目标一起进行优化
- 它们不使用目标变量(Y)之间的关系
- MTR 回归模型比一堆单一目标模型更简单
- MTR 树提供了人类可读的预测模型,这些模型易于解释。
中期审查可用的工具
我发现 Clus 工具包很好地融合了高性能和健壮性
- 文档是优秀的
- 该工具包有多种用于多目标分类和回归的方法
- 它还支持基于规则归纳和聚类。
- 我使用的集合模型(Bagging,RandomForest)易于阅读和解释。
一些更新的方法(2012 年后)已经作为木兰工具包的扩展实现了,可以在这个 Github 链接获得。虽然这些方法如随机线性目标组合比集合模型报告了更好的性能,但我发现该工具包不如 Clus 工具包成熟。
这里有一个关于相同主题的 StackExchange 的讨论。
在后面的文章中,我们将讨论如何使用 Clus 工具包来运行 MTR 模型。
机器学习:回归—预测房价(第二课)
你要卖掉你的房子,你想知道它的合适价格吗?我们会用 ML 来猜测房价——怎么猜?请阅读:)
在上一篇文章中,你将从 ML word 中学习一些基本术语。在这篇文章中,我们将重点关注监督学习,我们将做一些很酷的事情——建立一个 ML 模型,根据其属性预测房价。
先说一些理论。在监督学习中,您可以根据响应类型选择算法:
- 如果你的回答是连续的(一些数字),那么你将做回归,你需要回归任务的算法。
- 如果你的回答是分类的(类别或类),那么你将做分类,你需要得到分类算法
例如,如果您正在建立一个模型来预测“热或不热”,并且您正在预测 1-10 范围内的值,那么这就是回归,如果您的响应是热或不热,那么这就是分类。
在这个例子中,我们将建立一个预测模型来预测房价(价格是一个定义范围内的数字,所以这将是一个回归任务)。例如,你想卖一栋房子,但你不知道你能接受的价格——不能太低也不能太高。要找到房价,你通常会尝试在你的社区找到类似的房产,并根据收集到的数据评估你的房价。我们会做一些类似的事情,但是用机器学习的方法!好,我们开始吧!
我们将使用波士顿住房数据集,你可以从这里下载。然而,我在原始数据集上为你做了一个小技巧,帮助你更好地理解 ML,我把它分成训练和测试样本——你可以从我的 github 得到。我们将使用训练样本(data_train.csv 文件)进行模型学习,使用测试样本(data_test.csv)进行预测。我将数据分为两组,向您展示如何使用训练好的模型来预测未知。如果你对数据中每一列的含义感兴趣,你可以在这里查看。对于模型培训,我们将使用 MLJAR ,因为它具有简单的 web 界面(如果您没有帐户,请注册并获得免费的启动信用)。让我们开始一个新项目。确保选择回归作为任务。这很重要,因为不同的算法用于回归,不同的算法用于分类。
好的,请添加训练和测试数据集。在添加测试数据集期间,重要的是勾选:该数据集将仅用于预测(我们不会使用该数据集中的真实销售价格,我们将预测它!).
之后,我们需要指定列的用法。MLJAR 试图猜测应该使用哪一列。请将“目标”设置为训练数据集的销售价格的列用途。对于测试数据集,请将“不要使用它”设置为销售价格的列用途。对于每个数据集,我们需要在顶部“接受属性用法”。
我们准备定义 ML 实验。作为输入,我们将使用“训练”数据。请将预处理保留为默认值。对于学习算法,我们将使用极端梯度推进(xgboost)——它是超级强大的,你会看到!在调整详细资料中,请选择要优化的度量:均方根误差。然后点击开始!
哇!但是等等,什么是均方根误差(RMSE),为什么我需要这个?好问题。在训练阶段,您的模型希望预测的值尽可能接近目标值。为了跟踪它做得有多好,我们需要一些度量——它被称为成本函数。在我们的例子中,我们选择 RMSE 作为成本函数。当我们进入结果页面时,我们会看到许多模型,每个模型都有不同的 RMSE 值(分数栏)。
可以吗?我们使用一种算法,它产生不同的分数?当然,因为当你点击每个算法时,你会发现它们有不同的参数——所谓的超参数。所以不同的算法参数会得到不同的模型。这就是为什么 MLJAR 会为您检查许多不同的参数,并让您获得最佳模型(最有用)。在我们的案例中,最佳模型将是得分最低的模型,因此请通过单击该列对模型进行排序。
我们训练了一组模型,选出了最好的一个。那又怎样?
我们需要使用我们的模型!这就是为什么我留下测试样本,向您展示如何使用训练模型。在我们的测试数据集中有真值,但我们不会使用它们,让我们假设它们不存在。所以我们的测试数据中有 100 栋房子,我们想知道它们的价格。要计算预测,请转到预测视图。获得预测有 4 个步骤:
- 步骤 1 —请选择您想要用作输入的数据集,在我们的示例中是“测试”。
- 步骤 2-请选择您要用于计算预测的算法,在我们的情况下,我们将使用具有最小分值的算法。
- 第 3 步—点击“开始预测”按钮!😃
- 步骤 4-请等待一段时间进行预测,并下载包含模型响应的文件。
太棒了。您刚刚使用了 ML 模型,因此现在是将预测价格与真实价格进行比较的好时机—这是可以做到的,因为我们在测试集中有真实的销售价格,而在现实生活中,我们没有这样的值,因此只能手动(以某种方式)检查预测。好了,我们来目测一下。
在一列中有模型的预测,“销售价格”是真实值,在图片中显示了前几个数据样本(房屋)。你可以看到,在某些情况下预测是非常准确的。例如,对于 Id=1 的房子,真实价格是 208500 美元,预测价值是 208352 美元,因此超过 200,000 美元的房产只有 148 美元的差异——相当不错!
但是,也有预测值相差几千的房子。这个不用担心。这在 ML 中很常见,模型有时会出错。原因可能有很多:
- 数据不足——样本太少,T4 无法根据数据很好地概括 T5
- 杂乱的数据——数据中经常有错误
- 模型问题—模型太简单或太复杂
这就是为什么计算成本函数对于衡量我们的预测有多准确很重要。
好了,今天就到这里。我们已经创建了 ML 模型来预测房价并使用它!😃
XGBOOST 回归预测区间
(更新 2019-04-12:真不敢相信已经两年了。由于我一直收到各种更新代码的请求,我花了一些时间来重构、更新 gists,甚至创建了一个合作笔记本。我为那些在运行过去版本时遇到困难的人道歉。如果您有任何问题,请提问,我很乐意抽出时间来回答!)
(更新 2020–08–19:感谢您 Hassen Miri 帮助我完善分割收益论点)
对于任何希望对自己的业务进行认真预测分析的人来说,了解算法预测的不确定性是至关重要的。预测从来都不是绝对的,了解潜在的变化是非常必要的。如果一个人希望知道每个航班的乘客量,他还需要知道有多少乘客的预测可能不同。另一个可以决定预测上岸时间。当然,在几个小时的预测和半个小时内 95%的正确率和 10 个小时的潜在误差之间是有区别的!
在这里,我提出了一个定制的成本函数,用于将著名的 xgboost 回归器应用于分位数回归。Xgboost 或极端梯度提升是一个非常成功和强大的基于树的算法。由于分位数回归成本函数的梯度和 Hessian 性质,xgboost 的表现明显不佳。我表明,通过添加一个随机成分到平滑梯度,分位数回归可以成功应用。我展示了这种方法可以胜过流行的 scikit-learn 包中的GradientBoostingRegressor
算法。
为什么是预测区间?
虽然模型输出的是准确的预测,但它们本身是随机变量,也就是说,它们有分布。为了了解我们的结果正确的可能性,预测区间是必要的。这种可能性决定了可能值的区间。
比方说,我们希望知道 90%确定概率的可能预测的范围,那么我们需要提供两个值Q_alpha, Q_{1-alpha}
,这样,真实值的概率在区间内
是 90%。
线性回归的基本陈述是,
假设观测值x_i
和ε_i
是独立的并且是正态分布的。
假设误差项正态分布,系数β_j
的最大似然估计使这些误差项的方差最小。
这个方差当然是预测方差的最大似然(ML)估计。在我们假设特征和误差都是正态分布的情况下,预测也是正态分布的。
正态分布的预测区间很容易从期望和方差的最大似然估计中计算出来:
68%的预测区间介于
,95%的预测区间介于
99.7%的预测区间在
这种方法有一些限制。首先,我们非常依赖正态性的基本假设,当然这并不总是正确的。最后,线性回归的简单性将我们限制在一个非常宽的预测区间。理想情况下,我们希望我们的预测边界也依赖于更多的特征。在下一节课中,我们将讨论使用树模型的实现。
树模型
回归树是一个非常强大的预测工具。树的一般思想是对要素进行分区,并为每个分区分配一个值。该值以这样的方式分配,使得分区内的成本函数最小化。
Fig 1: Illustration of a tree model. The tree defines a partition of the features-space and assigns a values to each partition.
分位数可以通过下面的优化问题找到,
**
Fig 2: Cost function of Quantile Regression
xgboost 的分位数回归
我还没有解释如何给每个分区赋值。
Xgboost 是一个强大的算法,它为我们做到了这一点。为了进一步参考,读者应该检查原始论文,
陈,田琦和卡洛斯.盖斯特林。" Xgboost:一个可扩展的树增强系统."第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集。ACM,2016。
将体积I
进一步划分成两个更小的子体积I_L
、I_R
的主要因素是得分函数,
如果发生分裂,那么通过将下面的值加到较大体积q_old
的分位数的旧值上,来更新每个子体积I_α
内的分位数q_new
的新值。
参数λ和η分别是正则化和收缩参数,我在这里不做进一步讨论。
这里重要的是梯度和 Hessian 的值:
在下图中,我们看到了梯度和黑森曲线。
正如我们所见,梯度是一个阶跃函数,而海森函数处处为零,在原点处无穷大。
Fig 3: Gradient and Hessian of the cost-function of quantile regression with respect to the estimate
回头看看分裂的得分函数L_split
,我们看到几个问题。
让我们看看分位数值q_old
与分区内的观察值相距相对较远的情况。除非,我们运气好,在升压开始的时候是这种情况。梯度和 Hessian 对于大的差异都是常数x_i — q
。这意味着分离增益与任何特性无关。更糟糕的是,最佳分割增益告诉我们,只要我们将数据量分成两半,任何分割都是足够的!当寻找分割时,xgboost 将尝试寻找具有最佳分割的特征。因为所有特征产生相同的分离增益,即节点中一半的数据向左,另一半向右,所以不会发生分离。
如果我们使用上面显示的梯度和 Hessian,甚至使用平滑的变体,很少会出现分裂,模型将表现不佳。
一个有趣的解决方案是通过向渐变添加随机化来强制拆分。当分区内的观察值x_i
和旧的分位数估计值q_old
之间的差异很大时,这种随机化将强制随机分割该体积。
Fig 4: Plots of the new Gradient and Hessian used to estimate the quantile. Close to the origin, we use a smoothed version of the Gradient and Hessian. Far from the origin, randomization has been added to force a random split of the partition.
也就是说,我提出梯度和 Hessian 的以下平滑变型,
Gradient
随机变量ρ。对于这个实验,我简单地选择ρ,对于某个参数s
,以相等的概率取两个值s
或-s
中的一个。
黑森:
Hessian
在代码中:
我们将此代码应用于由以下代码生成的数据,这些数据也在 sklearn 示例中使用:http://sci kit-learn . org/stable/auto _ examples/ensemble/plot _ gradient _ boosting _ quantile . html
下图显示了 sklearn 的GradientBoostingRegressor
和我们定制的XGBRegressor
的 90%预测区间的比较结果,该区间是根据 95%和 5%分位数计算的。
对于 95%的分位数,我使用了参数值
对于 5%的分位数,我用了
是通过网格搜索找到的。当使用成本函数对测试集进行评分时,发现定制 xgboost 的 95%和 5%分位数都是优越的。