无人驾驶汽车的道德困境
Photo by Samuele Piccarini
自主机器应该如何决定不杀谁?
我很想拥有自己的无人驾驶汽车。我是说,谁不会呢?
但它们并不完美。如果你想一想,自动驾驶汽车必须像你和我一样做出决定。它们没有消除碰撞的可能性(还)…只是降低了发生碰撞的几率。
在一次事故中,他们不是在选择杀谁,而是在选择杀谁而不是。汽车不会故意出去杀人,但在发生碰撞时,它们会特意避开被告知的任何东西。如果选择是在两个不同的人群之间,那么选择不杀一个对另一个来说是致命的。所以都是优先级和价值观的问题。这是程序员考虑的一个基本问题,因为他们决定计算机做什么,汽车在行驶过程中采取什么行动。那么谁,什么,以及如何决定呢?
如果你看过《网飞》的 Circle (2015),你会对它的发展有所了解。如果你没有,那就看着吧!它真的让你思考人类的未来,以及我们每天做出的心理决定——一个半小时纯粹的“活在当下”,或者他们所说的任何事情。这部电影的前提很简单:50 个人在一个房间里,只有一个人能活着出来——有点像“终极目标”。但是他们作为一个集体(虽然不总是)选择了该死的人。每个人都有不同的故事,可能值得活着出去。所以他们选择。
Photo by Kyle Glenn
道德选择不是普遍的
你认为“正确”的道德选择会与你周围的人所想的不同。因为道德是主观的——每个人的价值观都不同。不管你喜不喜欢,我们都有很多缺点:我们根据过去的经验做出预先判断和偏见。
关于在事故中你应该救 A 还是 B,这也很难争论,因为你是在根据自己制定的一些抽象标准和社会认可的标准来判断一个人的价值。
对我的老师们来说……什么构成了一个好的人的标记方案在哪里?
因此,人们普遍接受的共识是什么“对社会更重要”,而不是普遍的道德。医生、孕妇和儿童是值得重视的普通人,并不难找出原因(我将在下文详细阐述)。即使在泰坦尼克号上,妇女和儿童也是第一个登上安全船只离开的。很明显,其他的事情也起了作用……但是。
Photo by Ümit Bulut
看谁会选择什么其实很有趣。这很能说明他们的性格,就像经典的手推车问题一样。在“圈”的最后,你可以看到不同房间的其他组决定救谁。同样的“生活主题”回归:我们重视哪个:群体还是个人?
顾客(有时)总是对的
当你想到为了保护你怀孕的姐姐和她丈夫(医生或一个随机的陌生人)而发生碰撞时,我会大胆地猜测你会救你的姐姐和姐夫。
但如果是车里的陌生人呢?说为了救你的家人,那个陌生人必须死。你可能还是想继续下去。假设那个陌生人也在自动驾驶汽车里——你可能仍然选择去救你的妹妹、她的丈夫和她的孩子。我明白,因为人们并不总是想救乘客——更多的是关于有多少人和什么类型的人被杀。
Uber is currently working on developing and deploying self-driving cars. Photo by Dan Gold.
汽车可以被编程为“最佳情况”——这很容易。难的是没有人想买一辆万一发生事故会牺牲乘客的汽车。所以这对企业来说是不好的:他们如何营销或销售他们的产品?它们应该被编程为不惜一切代价避免干涉和碰撞吗?如果怀孕的母亲和丈夫违反了横穿马路的法律呢?没有确切的答案。
挑选你的战士…明智地选择
我写这篇文章的灵感来自个人的阴谋,但也因为我参加了一个麻省理工学院的测验,让你选择在不同的无人驾驶汽车场景下的行动路线(以及不要杀死谁)。
在测验结束时,他们会给你一份结果摘要。令人惊讶的是,我的“最被拯救的角色”是一位女医生,而我的“最被杀死的角色”是一位老人。我对此感觉很糟糕…但是从它们的实际功能价值以及基于我个人的偏见和过去的经验来看,这是有意义的。
Photo by Randy Fath.
也请注意,这并不一定是你在那种情况下会做什么的准确指标。显然,理论和实践中的一切都是不同的。
参加终极伦理测试:道德机器 —麻省理工学院
性别偏好
当你看到你的结果时,你也会看到你和其他参加测试的人相比如何。总的来说,人们倾向于拯救女孩而不是男孩。这可以追溯到一些古老的生物学推理,因为女性是繁衍和生儿育女的主体,但不是唯一的主体。人们仍然有一种刻板印象,认为女性是“照顾者”,表现出母性行为,这使得杀害她们更加残忍。
想到当今世界的人们会救一个女人而不是一个男人,真是有趣。显然,这个小样本量不是一个确定的统计数据,但它确实提供了一个暗示性的趋势。如果你想想性别角色和过去的男性优越感,你就能看到事情是如何发展的。我也不是说潜在的偏见不再存在了——只要看看 STEM 领域的性别差异就知道了!
人类>动物
Photo by Alvan Nee.
人们不仅救女孩胜过救男孩,而且他们会一次又一次地救人类胜过救动物。有一种物种偏好,仅仅是因为我们是人类,有自我保护的本能。如果你和那只动物有私人关系,比如它是你的宠物,那就有点不同了。我们天生自私。
辞旧迎新
我之前告诉过你,我的‘被杀最多的角色’是一个老人。我不知道我是否有意识地同意这一点,因为我真的认为老年人有价值。
不可否认,从老一代人那里可以获得基于他们过去经验的智慧和知识。虽然他们可能不会以最典型的方式对社会做出具体的贡献,但谁又能说他们的知识和/或智慧是无效的呢?
诚然,时代变了。10 世纪显而易见的智慧不一定适用于其后的所有世代。基本上,什么都要半信半疑。
对年轻人来说,这是一场赌博。你可能会倾向于拯救他们,因为他们的人生还很漫长。我知道我就是这么做的。这意味着他们有潜力成为伟人,做不可思议的事情,改变世界。这很可能是下一个埃隆·马斯克!另一方面,这也意味着年幼的孩子有可能成为非常非常坏的人…不列举例子,但你会明白的。抱歉。但这是事实,不管你喜不喜欢。
当医生统治的时候
Photo by Artur Tumasjan.
做决定时,我发现自己一次又一次地选择了医生而不是无家可归的人。而且我不是唯一一个,基于平均值。老实说我不认为这是一种好的思维方式,因为社会地位不应该成为你生死的决定因素。
所有国家都表现出一种令人不安的偏好,即拯救地位较高的人(例如,高管比无家可归者更受青睐)。我们发现这与一个国家的经济不平等程度有很大关系。哪里的不平等更严重,哪里就更倾向于牺牲无家可归者。
-让-弗朗索瓦·博纳丰,图卢兹经济学院
我们重视医生,因为我们认为他们的工作对人类的生存至关重要——老实说,的确如此。不是每个人都能做这份工作,你需要大量的教育才能行医。人们没有注意到的事实是,这并不是一个人过上健康而充实的生活所需要的唯一角色。没有农民?吃不下。没有工程师?没有电脑,没有桥梁,没有工厂,凡是你能想到的。没有教育者?你将如何学习?
我意识到测验本身是有缺陷的,因为给出的关于不同选择的信息是有限的——它们只给出性别,偶尔给出工作——但仍然有问题要提出来。
我们对一个人的评价是基于他对社会的贡献,这是一种固有的社会缺陷吗?答案是肯定的。
什么定义了对与错?善与恶?
T 把这个问题想成‘选择不杀谁’是苛刻的。但这是我们的现实情况,我们都知道。
Photo by Azzedine Rouichi.
那么答案是什么呢?你只需要在事故发生前猛踩汽车刹车!哈哈,不——我只是在开玩笑——虽然这是一种可能性,但创造无人驾驶汽车必须考虑大量不同的场景,包括你不能急刹车,汽车必须就杀死谁做出道德决定的情况。这可能很罕见(或者我们希望如此),但这种考虑是不可避免的。
关键是,没有真正的答案。这是一个问题,因为没有“正确”和“错误”的秘诀。我们每个人都会拯救不同的人,那么自动驾驶汽车应该遵循谁的道德规范呢?
自动驾驶汽车决定杀死谁的这一特定场景的概率不是 0.001%,而是风险因素如何分布。对于这些问题,我没有万能的答案,但是我希望我给了你一些思考的东西。
让我知道你的想法!
数据越多未必越好
Prospecting for Data Insights (Vermont Historical Society)
在大数据高峰期前后的一段时间里,有一句口头禅占据了主导地位:只要能够获得足够的数据,任何问题都可以得到解决。当这句咒语被引用时,它似乎经常隐含着这样的信息:担心你是否拥有正确的数据是恐龙的方式。
尽管对大数据的大肆宣传已经过去,但这一思想的回声仍然存在于 2019 年的数据战略中,这些战略将数据收集置于前沿和中心位置,并将不断增长的数据缓存置于对数据科学家预期要解决的问题的清晰理解和对现有数据的深刻分析之上,以确保这些问题发生。
在许多方面,这些旨在利用物联网宣传的战略与最初围绕数据科学的宣传相反,最初的宣传认为,每个组织都坐在未经分析的数据的金矿上,等待被部署来解决组织的大多数问题。如果这个金矿真的存在,为什么还需要收集更多的数据呢?在第一波数据科学炒作中,缺乏分析数据的熟练人员,但现在有一种说法是供过于求——物联网只是数据科学家的一个工作计划吗?
部分问题可能是对不同机器学习技术的相对有效性缺乏广泛的认识——特别是每种技术需要多少数据才能获得可用的结果。这是由于缺乏对“平坦最大效应”的理解,模型往往会达到一个点,在这个点上,增加的复杂性不再导致增加的准确性:建模者版本的经济学“收益递减规律”。
回到缺乏对不同机器学习算法的方式的认识的问题,这些算法对于它们有效工作所需的数据量有不同的要求。最近的研究证实了预期的结论,即针对较小数据开发的算法(如逻辑回归)可以实现与现代技术(如随机森林)同等的准确性,而训练案例却少得多。
记住,逻辑回归相对来说更适合于提供一个内在可解释的模型,如果您有合适的数据集来获得足够的结果,为什么收集大量不同的数据是一个高优先级?
或者,可能更频繁的是,如果你还没有测试你的数据集是否足以从逻辑回归或类似的方法中得到你需要的结果,你为什么还要讨论你想要收集的其他数据呢?
没有明确目标的数据科学是没有意义的。在我居住的澳大利亚,有一个和‘banker’押韵的词,用来形容从事这种追求的人。需要重复的是,对于希望拥有有效数据科学项目的数据科学家和组织来说,投入时间的最重要地方是仔细定义每个数据科学项目的目标。过于关注从所有可能的来源收集大量数据的危险在于,对解决组织真正问题的关注被忽略了。
罗伯特·德格拉夫的书《管理你的数据科学项目》已经通过出版社出版。
更多数据,更多工作表 API
Google Sheets API 如何使用魔法(和代码)用可视数据填充您的表单。
作为马友友的超级粉丝,我一直在用谷歌视觉应用编程接口(API)处理大提琴的图像。获得 API 的标签检测结果后,当屏幕上输出“弦乐器”这样的字眼时,我欣喜若狂。计算机能像我一样识别和欣赏大提琴的图像,这多棒啊。
然而,通过 Vision API 发送图像的点到底是什么?我已经可以看到里面有什么了,那么为什么我还需要一台电脑来重申我已经知道的东西呢?
就像大多数生活中的问题一样,答案是 Google Sheets。
电子表格是组织和可视化数据的好工具。)机器学习是获取这类数据的绝佳方式。当机器学习返回的数据被收集、组织并通过表格可视化呈现时,机器学习(特别是 Vision API)变得非常有用。
“但是使用床单既费时又令人困惑!”
你是对的,它肯定是可以的,这就是为什么我一直在使用 Google Sheets API 和云函数来为我做所有的脏活累活。
获取数据。
调用 Google Vision API 就像几行代码一样简单。
const client = new vision.ImageAnnotatorClient();
const [result] = await client.labelDetection({image:
{content: fileBytes}
});
具体来说,我正在对一个图像执行标签检测,由 Vision API 定义为一种“检测和提取图像中实体的信息,跨越一个广泛的类别组”的方式因为我的大提琴图像是本地的,所以我通过 API 将它的图像字节作为一个 base64 编码的字符串发送。
你可以仔细看看标签检测返回的响应,但是在这里,我只对标签的描述和它的分数感兴趣。
var labelsResult = result.labelAnnotations;
var labels = labelsResult.map(label => label.description);
var labelScores = labelsResult.map(label => label.score);
在这两个变量标签和标签分数中,我保存了 Vision API 返回的每个标签及其对应的分数。然而,光有这些惊人的数据是不够的。事实上,如果你不知道如何处理数据,或者不知道如何理解数据,那么数据实际上毫无用处。
如果你不确定如何继续,这里有一个可行的选择:把它全部发送到工作表。
准备我们的数据。
标签检测规范解释了我们的标签和标签核心变量将只是 JSON 映射。如果在我的工作表中,我希望每一行都包含一个标签及其相应的分数,该怎么办?这里最好的选择是将这个 JSON 映射分割成一个 2D 数组,它反映了我们希望数据在工作表中的显示方式。
首先,我们使用 JSON.stringify 将我们的 JSON 数据转换成一个大字符串。接下来,我们删除任何不需要的字符,最后,我们通过在每次到达逗号时指示一个新的索引,将这个大字符串转换成一个数组。(这是因为我们返回的 JSON 映射中的标签都用逗号隔开)。
var labelData = JSON.stringify(labels, null, 2);
labelData = labelData.replace(/[\[\]'\"]+/g,'');
labelData = labelData.split(',');
我可以对我的 labelScores JSON 响应执行这些完全相同的代码行,以获得包含所有分数的另一个数组。我选择将这个数组命名为 scoreData 。
写在纸上。
为了将我的两个数组发送到一个表中,我需要确保它们遵守表 API 的调用参数。如前所述,我希望发送到工作表的值数组需要反映我希望在工作表中格式化数据的方式。
我知道我的两个数组有相同的长度,因为每个标签都有相应的分数,反之亦然。
var dataLength = labelData.length;
我还知道,对于每一行,我希望在列 A 中有一个标签,在列 b 中有相应的分数。因此,这正是我的值数组需要的样子。
var values = []
for (var i = 0; i < dataLength; i++) {
var dataToPush = [labelData[i], scoreData[i]];
values.push(dataToPush);
}
为了将这些值推送到我的工作表中,我需要调用的方法是…
sheet.spreadsheets.values.update(request, {
…其中我的请求变量如下:
var body = {
values: values
}var request = {
spreadsheetId: 'YOUR_SHEET_ID',
valueInputOption: 'USER_ENTERED',
range: 'A1:B',
resource: body,
auth: // YOUR OAUTH CLIENT
};
无论何时进行 Sheets API 调用,指定spreadsheetId
都是至关重要的,这是为了确保您所更改的正是您想要的工作表。以下是你如何找到自己的。
撇开凭证和数据不谈,也许这个请求变量中最重要的值是 valueInputOption ,在这里您可以指定如何将值输入到工作表中。'USER_ENTERED’
是您的最佳选择,确保数据会出现在您的工作表中,就像您自己花时间输入数据一样*。*
此外,范围值(在 A1 符号中给出)对于确保您的数据按照指定格式编排到您的工作表中至关重要。A1 符号“A1:B”表示从单元格 A1 开始写入,移动到 B 列,然后继续向下移动行并从 A 列切换到 B 列,直到所有数据都被写入。因此,即使你已经将你的值数组设置为你希望数据显示的方式*,你也需要正确地将范围变量设置为你希望它显示的位置。*
Fragment of what your inputted labels and label scores should look like in your Sheet. I hope you like cellos!
生成可视数据。
如果您现在查看一下您的电子表格,您将看到从 Vision API 的标签检测中获得的所有数据。还是那句话——如果你不知道如何理解数据,那数据是什么?
你的下一步是记住每个人都喜欢图表!我们用 GCF 做一个吧。
从现有数据创建柱形图的 Sheets API 调用是一个 batchUpdate 请求,它非常类似于我们之前进行的 values.update 调用。这里的关键区别是我们发送给 batchUpdate 的请求参数:一个冗长的 JSON 主体,其中指定了域、系列、轴标题和位置。
让我们来看看 JSON 主体的一些细节,我们将把它存储在一个名为 chartRequest 的变量中:
*chartType: "COLUMN",
legendPosition: "BOTTOM_LEGEND",
axis: [
{
position: "BOTTOM_AXIS",
title: "Vision Labels"
},
{
position: "LEFT_AXIS",
title: "Score"
}
],*
上面的内容对于我们所拥有的数据来说是不言自明的:我们将创建一个柱形图,其中每一列都是一个标签,其高度由分数决定。同样,这只是我们的 chartRequest 需要的一小部分,但是理解请求体的这些细节将允许您为将来可能有的任何数据布局创建一个图表。
撇开长长的 JSON 正文不谈,batchUpdate 请求本身相当简单…
*sheet.spreadsheets.batchUpdate({
spreadsheetId,
resource: chartRequest,
});*
这就是你所需要的:你的电子表格的 ID(在哪里找到数据)和你的 chartRequest(如何从数据创建图表)。看到了吗?Sheets API 就像魔术一样。
The column chart you just generated from the Vision API data. Personally, I like to set all my columns to #ffe3ee.
化解生存危机。
好了,我们明白了,您可以使用 Sheets API 对数据和图表做一些很酷的事情——但是这有什么意义呢?
问得好。要点是我们生活在大数据时代,这意味着计算机和软件允许我们访问比以往任何时候都多的数据。你好更好的消费者分析,销售报告和市场研究!然而,技术的快速和持续进步使得公司很难跟上,数据经常以丢失、被误解或未被分析而告终。
通过这些简单的 Sheets API 调用,数据可以很容易地被存储、分析和可视化,而不必实际浏览 Google Sheet 本身。通过在后端处理这些实现,生成数据可视化不再令人困惑、乏味或耗时。此外,Google Sheets 使您的数据易于访问和分享。
大数据,小问题。
你可以在我的GitHub上找到我完整的 GCF 代码。
特别感谢我的实习主持人 Anu。
让我知道你是如何使用 GCF 和 Sheets API 来处理数据的,我很想听听你的意见!
更多的关注和寻找答案的渴望,更少的炒作
我认为为什么新人应该关注基础知识和数据工作的真正意义
如果你问我业内最常见的问题是什么,我会说是“如何成为一名数据科学家。“有点道理吧?有了对该领域的大肆宣传、新闻和赞美,有许多潜在客户对沐浴在数据的光辉中感兴趣是合理的。
去谷歌,输入这个时髦的查询,点击输入,你肯定会收到一个有数千个链接的页面,指向很多很多的指南、资源、教程等等。
No. Data Science is not easy.
这对新来者来说可能是个问题。
诸如“你应该学习这个库”、“掌握这个框架”、“成为一个 SQL 大神”、“成为一个 Kaggle 大师”、“背诵所有不同的激活函数”、“与 Yann LeCun 共进晚餐”等评论和说明,以及你可能找到的一些台词(可能不是最后一句)。
外面的信息量可能是巨大的!如此势不可挡,以至于新感兴趣的人会放弃。想要进一步的证据吗?这篇名为“ 的 Reddit 帖子,有没有人看到有这么多东西需要学习而感到不知所措? ”提出了一些这样的例子。
When data was a bit simpler. Photo by Vincent Botta on Unsplash
然而,如果不是把这些蹒跚学步的孩子扔到野外,让他们自己生存,而是我们,社区,导师,导师,朋友和领导者,可以缓和这种下降,并改变我们将这些新手引入数据领域的方式,会怎么样?
如果我今天开始学习数据科学的基础知识,并询问谷歌如何成为一名数据科学家,我可能会关闭浏览器,趴在桌子下哭。首先,“数据科学”这个术语很宽泛,它包含了我不知道它们存在的东西。让我们来看看这个。
http://nirvacana.com/thoughts/wp-content/uploads/2018/01/RoadToDataScientist1.png
看到这个了吗?太多了!这怎么能激励人呢?此外,我在这里看到的——这是我的观点——是某种路线图和带有任意完成百分比的清单,它们应该表明你是一个多么优秀的数据科学家。
旁注:这张图片来自 2013 年,这里展示的一些技术已经过时了。还有,我不是说图片是错的;它提供了关于如何开始的非常有价值的信息。我想说明的是,这可能会让那些想染指数据的人感到沮丧。
事实上,这是我在职业生涯中学到的,如果某人的头衔是“数据科学家”,我敢肯定他们的日常任务或日常业务是关于一个特定的主题,例如,风险评估、流失分析、数据可视化、成为 SQL 神等等。
这就引出了我的第一点。
如果我们不是向新人建议所有这些大量的信息、网站和“如何成为数据科学家”指南,而是试图指出或至少近似地指出,到底是什么促使和激励他们加入我们这个奇妙的数据世界,会怎么样?是数据可视化吗?或者是机器学习?对统计的热爱?–如果这个问题的答案要么是钱,要么是炒作,祝你好运。
通过这样做,通过问这个简单的问题,我们可以引导他们,让他们朝着正确的方向前进。一旦这个人走上了这条道路,并且对基础知识有了清晰的理解,那么就由他们来决定如何补充这些新获得的知识。
现在你可能会问:“这一切都很好,胡安,但是怎么做呢?”好问题,是的,这不是一件容易的事。但是,你注意到我是如何使用代词“我们”的吗?我指的是我们、社区和导师,我在上面提到过几段。因此,我在这里的信息是,如果你,某个目前在这个行业中的人,曾经处于帮助、指导、导师、某人的位置,请考虑这样做。我相信他们会很感激的。
我想提出的第二点是,不管选择的工作领域是什么,我们都应该清楚,数据人员的最终目标是找到问题的答案。我相信,再一次,这是我的观点,在大肆宣传、图书馆、花哨的方法和诸如此类的混乱中,我们已经迷失了这个领域的本质。这种注意力不集中是可以理解的,有时我也会这样。我们不断看到所有这些新的玩具被释放,我们想实现他们的权利,并得到最好的准确性评分。尽管如此,还是要明确一点,在一天结束的时候,你的老板和企业想知道,公司今天会赚或亏多少钱,哪种绿色的基调更好,或者用户是否是垃圾邮件发送者。
所以,下次有人问你成为数据科学家的关键是什么时,请尝试在你的回答中包含“问题”和“答案”这两个词。
对于初学者来说,一步一步来,记住没有人知道所有的事情。
感谢阅读。
大多数人搞砸了百分之几的变化。以下是如何得到他们的权利。
用日常应用解决一个常见的数学问题
令人难以置信的是,经过 16 年的学校教育,大多数美国大学生都弄错了这个问题:
在下列情况下,总的百分比变化是多少?
先减少 40%,然后增加 60%。
A.增长 10%
B.增加 20%
C.下降了 4%
D.以上都不是。
答案当然是 C,一个整体下降 4% 。大多数大学生不仅答错了这个问题,他们甚至没有得到正确的方向,超过一半的人猜测这是增加。常见的错误是将面值的百分比相加,得到总的百分比变化。因此,我们在人们不太擅长的事情的长长列表中又多了一个条目,结合多个百分比变化。
从一系列百分比变动中找出一个总的变化,远不是一个抽象的理论概念,它对我们的日常生活有影响,从消费品价格到国家预算,到我们的退休账户价值,再到我们最新的笔记本电脑的电池寿命。然而,尽管错误无处不在,对我们个人和整个国家都有影响,但我们无法教会人们如何正确地计算百分比。在本文中,我们将讨论错误是什么,如何避免它,为什么这件事,以及为什么人们会犯这种错误。我们的主要资源是论文:“当二加二不等于四:处理多个百分比变化的错误”(链接)。
有什么问题?
错误在于,当我们看到两个连续的百分比变化时,我们本能地认为它们是表面值,并将它们相加得到总变化。因此,50%的折扣加上 25%的折扣被解释为 75%。在等式形式中,对于两个百分比 A 和 B,错误是:
The common error of adding two percentage changes at face value.
问题是百分比是从特定基值计算出来的。在第一个百分比变化之后,基数发生变化,第二个百分比没有相同的基数。**两个基数不同的百分比不能直接相加!**相反,我们必须分别计算出百分比变化。
合并百分比变化时如何避免错误
首先,从第一个百分比变化计算新的基数,然后,使用新的基数,从第二个百分比变化计算百分比。然后,可以根据新值和原始基值计算总百分比变化。在方程式中,总百分比变化为:
The correct method for calculating total changes from two individual percentage changes.
从两个连续的百分比变化 A 和 B 中消去该值,得到更简单的总百分比变化公式:
Total Percentage Change from Two Sequential Percentage Changes
错误出现在末尾的额外项 A * B 中。幸运的是,错误总是发生在系统化的方向上:
- 对于 2 次增长,我们低估了总体增长百分比
- 对于 2 次下降,我们高估了总下降百分比
- 对于导致总体增加的 1 次增加和 1 次减少,我们高估了总的百分比增加
- 对于导致总体下降的 1 次增加和 1 次减少,我们低估了总的下降百分比
让我们看看代表这 4 种情况的例子:
- 2 增加:假设智能手机的电池寿命在一年内增加 40%,在下一年增加 10%。总共变化是多少?
2.2 减少:假设城镇税率先减少 60%,再减少 40%(我们只能愿望)。如果我们把两者加在一起,我们付 0%的税,但是肯定不对!的确不是:
There’s no such thing as a free lunch.
3.1 增 1 减导致整体上升。假设国家支出在某一年增加了 80%,然后在下一年减少了 40%。总共变化是多少?
请注意,更改的顺序并不重要。我们可以先减少再增加,但变化的总体幅度是一样的。如果你不相信我,就从花 100 美元开始。第一阶算出:
先减少后增加给出了相同的答案:
4.1 增 1 减导致整体下降。假设一篇博文的长度在第一次编辑时增加了 60%,在第二次编辑时减少了 40%(这与最初提问的情况相同)。帖子变长了吗?
在最后一种情况下,将两个百分比相加得到 20% 的增长,而实际上是 4% 的下降。如果你犯了常见的错误,你将会站在 0 的错误一边!类似的错误发生在加州的标准化考试分数一年下降了 60%,第二年上升了 72%,导致学生分数从基线上升而受到表扬。我不想扫兴,但是总的变化——等等,你自己解决这个问题——是考试成绩下降了 31%。学生比两年前更差,而不是更好,他们的父母仍然是数学盲(但不是你了!).
为什么这很重要?
以上都是非常真实情况的假设例子。不管是不是故意的,我们一直在处理多种百分比的变化,当我们没有意识到这一点时,通常会对我们不利。就像大多数其他人类错误一样,已经有人为了他们的利益利用它了。(这是一个很好的经验法则,人类逻辑中的错误首先在营销中被发现,然后才在学术期刊上发表。关于这方面的更多内容,请阅读罗伯特·恰尔迪尼的 影响力。)
比如看“双倍折扣”。这种情况经常发生在零售业,你可以在一件商品打五折的基础上再打八五折。销售人员希望您将百分比相加,得出折扣为 75%,而不是实际价值的 62.5%。或者考虑一台计算机,它的显示屏比上一款多 20%的像素,比上一款多 30%。卖家最好不要分别列出这些涨幅——相加就是 50%——而是用一个数字 56%来表示,这是实际的涨幅。
在这种情况下,销售人员会做的是框定:在一个环境中呈现信息,让我们做出对她有利的决定。任何数据都可以以多种方式呈现,例如,一种药物可以被描述为有 10%的机会引起副作用,或者有 90%的机会不引起副作用(这里的框架是积极的对消极的)。多重百分比变化只是框架的一个例子,它对我们的选择有着深远的影响(阅读 Tversky 和 Kahnemann 的“决策框架”中的框架)。
“当二加二”论文中的一项研究着眼于在零售环境中,将折扣呈现为单次降低或两个百分比折扣的效果。毫不奇怪,双重折扣比同等的单一折扣产生了更多的购买、更多的收入和更多的利润。虽然这只是一项单独的研究,但商店中双重折扣的盛行有力地证明了营销人员已经发现并利用了这一认知缺陷。
一个基本原则是,当我们拒绝自己做算术时,我们会被做过算术的人愚弄。
为什么会这样?
有一种理论解释了为什么我们不擅长百分数,以及分数和小数,那就是人类进化出的数学技能只能处理整数。“整数优势”从进化的角度来看是有道理的:自然界中的事物实际上只有整数。我们从来没有被 1.7 头美洲狮追赶过,或者必须有 2.5 个孩子来延续这个物种。几百万年来,我们非常擅长将整数相加。在更复杂的情况下——计算你退休账户的复合百分比回报——我们进化的能力并不能很好地维持。
我认为计算百分比变化的错误是我们对整数的偏好和保持系统一思维的结合。如果你读过像《思考,快与慢》这样的作品,那么你对思维的两个系统的概念就很熟悉了。第一种是使用一套试探法和偏见(思维捷径)做出的快速本能判断。第二种模式是通过对多条信息进行推理而做出的长期、理性的决策。第一个系统适用于我们祖先遇到的情况,但在信息丰富、不断变化的环境中就失效了,这正是我们现在所处的环境。
计算多个百分比变化的总影响需要从 1 切换到 2,这很费力,也是我们试图避免的。不幸的是,系统 1 似乎不会乘法,而是采取了一种捷径——加法——来计算 50%和 25%的增长是 75%,而不是 87.5%。
《当二加二》中的另一项研究着眼于我们何时能正确地计算出倍数百分比。有趣的是,他们发现我们无意识地做出了努力和回报的权衡,只有在回报值得的时候才停下来计算正确的答案(在这项研究中,正确的解决方案需要几美元)。我们应该小心从单个研究中推断,但是外部奖励(有时)会激励我们更加努力。
类似地,当百分比变化很容易计算时,比如减少 20%和减少 10%(总共减少 18%),错误率就下降了。因此,降低努力或增加回报似乎会导致更精确的计算。错误率下降的第三种情况是当百分比相加得出的总数明显不可能时,如 80%和 40%的折扣。人们知道产品的价格不能下降 120%,否则他们将不得不支付购买它!
总之,我们错误地计算了多个百分比变化,因为需要努力通过正确的答案进行推理。我们擅长加法和使用整数,但不擅长乘法和百分比。解决方法和问题一样简单:花时间为自己进行计算,而不是假设答案。世界上最重要的决定需要招聘系统 2,所以你最好抓住每一个机会训练它!
结论
当我们按面值取百分比并把它们加在一起时,就会出现多百分比变化误差。正确的方法是单独进行计算或使用正确的公式:
It’s the details — in this case the A * B — that make the difference.
运用正确的等式会让你看到双倍折扣并不像看起来那么大,双倍上涨比看起来要大,要弥补 60%的下跌需要的不是 60%的上涨,而是 150%的上涨!
是的,这可能需要更多一点的努力,但是当你不愿意自己做数学计算时,这意味着你将输给那些做的人。我希望,即使你的自信在本文开始时遭受了 40%的打击,它也已经增加了 90%,达到了 14%的积极。
一如既往,我欢迎反馈和建设性的批评。联系我的最好方式是在 Twitter 上。
今年,大多数费城学校都在附近发生过枪击事件
截至 2019 年 5 月 1 日,超过一半的费城学校在不到 500 米的地方发生了枪击事件。19 所学校——其中 15 所在费城北部——在不到一个足球场的地方发生了枪击事件。
费城一所学校和最近的枪击案之间的中间距离是 421 米——大约四分之一英里。这一数据包括地区学校、特许学校和私立学校。
| distance (meters) | schools |
|-------------------|---------|
| 0 - 500 | 316 |
| 501 - 1,000 | 114 |
| 1,001 - 1,500 | 39 |
| > 1,500 | 78 |
绝大多数学生就读的学校距离枪击事件发生地不到 500 米(不到一英里)。
| distance (meters) | students |
|-------------------|----------|
| 0 - 500 | 90,896 |
| 501 - 1,000 | 33,715 |
| 1,001 - 1,500 | 12,695 |
| > 1,500 | 31,264 |
Data: Shooting victims and school locations, by provided by the City of Philadelphia. Chart by Jared Whalen.
这是离枪击案最近的五所学校。
| | school | distance (meters) |
|---|------------------------------------|-------------------|
| 1 | Tarbiyatul Ilm Academy Inc. | 26.74 |
| 2 | Mckinley, William School | 31.23 |
| 3 | White Dove Performing Arts Academy | 31.97 |
| 4 | One Bright Ray-Simpson Campus | 36.25 |
| 5 | Independence Charter West | 48.07 |
方法论
费城每所学校都计算了最近的射击次数。拍摄数据仅限于费城和 2019 年发生的事件。每个学校都添加了相应的缓冲区来可视化距离。
工具: QGIS,R (dplyr,ggplot2),Illustrator
预测在线欺诈的鼠标移动模型
创造性地将鼠标移动数据和基本物理概念结合起来用于在线欺诈检测模型
数字化步伐的加快也增加了在线盗窃和欺诈的风险。所有行业,无论是银行、零售还是教育,都会受到这些威胁的影响。企业总是在寻找保护客户的方法。已经使用了不同方式的保护机制,例如数字自动生成密码、指纹或高级技术,例如语音或面部识别。不幸的是,一方面它们是侵入性的,另一方面它们不能提供持续的保护。
最近使用了行为方法,如分析鼠标移动来检测欺诈性或不必要的访问。这涉及到对用户鼠标移动方式的建模,这是本文的重点。这个故事的灵感来自 Margit Antal 和 Elod Egyed-Zsigmond [1]的优秀白皮书《IET 日报使用鼠标动力学进行入侵检测》和 Balabit 鼠标挑战数据集[2]。
一般来说,每个人都有自己的鼠标使用风格。有些人可能很快,有些人很慢。有些人比其他人更喜欢导航。仅仅通过观察鼠标的移动数据,你就可以了解一个人的很多行为
为了说明如何模拟鼠标移动,一个样本数据集[2]如下所示
Sample mouse movement data
鼠标移动数据具有诸如用户、会话、client_timestamp 之类的字段,这些字段给出了关于用户和活动时间的信息。字段 x 和 y 表示屏幕上指示鼠标位置的坐标。鼠标的状态,如移动、拖动、按下也被捕获。“欺诈”字段是基于访问是不想要的还是正常的这一事实的标记字段
模拟鼠标运动是创造性地使用这些领域,以确定哪些行为是正常的,哪些是不必要的。
首先,让我们想象鼠标移动的样子。下面显示的是正常使用时鼠标移动的动画形式
Mouse movement animation for normal access
这里也显示了不需要的访问的类似可视化
Mouse movement for unwanted (fraud) access
为了提取特征,对鼠标运动建模需要关于基本物理学的知识,例如距离、角速度、加速度等。
在这里,我解释了这些特性的含义,以及它们如何有助于识别不需要的访问
屏幕移动距离
屏幕移动距离是鼠标移动到的两个屏幕位置之间的距离。这两个屏幕位置对应于鼠标的释放状态和按下状态
Mouse movement between RELEASE and PRESS
鼠标移动的屏幕距离是点 P1 和点 PN 之间的欧几里德距离
我们可以使用箱线图分析正常访问和欺诈访问的屏幕距离
Screen Distance travelled Box Plot
您将会发现,不需要的访问所经过的屏幕距离范围小于正常访问。这意味着不需要的访问往往集中在屏幕的一小部分。正常的访问倾向于探索屏幕的不同部分
对于热心的读者来说,你一定从上面的动画中注意到了这种行为。如您所见,正常访问的 x 和 y 轴范围比不需要的访问大得多。
运动角度
运动角度表示运动的方向。范围从 0 到 360。角度可以用来了解运动的本质。例如,0°或 180°的移动表示直线移动,而 90°或 270°的移动表示垂直移动。
我们还可以将角度分为 8 个方向,如下所示
我们可以借助雷达图分析运动的角度。此处显示了正常和非正常进入的运动角度雷达图。每个方向上的直线长度是该特定方向运动的平均角度
Radar Chart for Angle of Movement
我们可以观察到,在雷达图中,不需要的访问和正常访问几乎完全重叠。这意味着这两种访问之间的移动角度没有太大差异
速度
一般来说,当我们想到像汽车抢劫或银行抢劫这样的事件时,我们倾向于认为它是快速和迅速完成的。那么,任何在线欺诈或不必要的访问也是高速进行的吗?让我们找出答案
速度是速度的指标。它以一秒钟内鼠标移动的像素距离来度量。我们可以用这个概念来分析鼠标移动的速度。两种访问的速度用方框图表示
Velocity (speed) box plot
你会发现不必要访问的鼠标速度通常比正常访问的速度要快。在不想要的访问中,鼠标在 0 到 250 像素/秒的范围内移动。在不需要的访问中没有观察到异常值。在正常访问中,鼠标在 0 到 100 像素/秒的范围内移动,但是有一些异常值显示为超出范围的黑点。这些异常值代表突然的高速运动。
该分析证实了我们对在线欺诈行为的想法。与汽车抢劫或网上盗窃相比,这是非常相似的行为。欺诈者往往很快完成他或她的活动,但没有任何突然的动作
角速度
我们已经看到,不需要的访问的速度通常高于正常访问,而正常访问确实有一些突然的快速移动。了解这些运动的方向将会很有意思,以便了解它是否揭示了一些额外的见解
我们可以把上面提到的角运动和速度的概念结合成角速度的概念。它表示特定方向的速度。
测量角速度时需要注意的重要一点是,角速度可以是正的,也可以是负的。所有从左到右(任何角度)的运动都有正的角速度。而从右向左的那些将具有负的角速度
Angular velocity can be positive or negative
这里显示了两种访问的角速度直方图
Angular Velocity Histogram
你会观察到,在正常的接近中,大多数突然的快速移动都有负的角速度,这意味着向后移动。这可能意味着要在一些已经填写的字段中进行一些更正,或者单击左侧菜单
除此之外,其他一些可以用角度和速度计算的有趣特征是直线度和曲率。直线度是非常接近 0 的运动。曲率是代表圆或曲线的运动
我们可以使用上述有趣的特征来建立一个可以预测不希望的访问的模型。普遍认为有用的一些特征是
- 屏幕距离
- 运动角度
- 速度
- 角速度
- 直线运动
- 弯曲
- 运动开始时间
- 上述值的最小、最大、标准偏差
有了这个功能,一般都能达到很好的精度。模型预测和准确性可能因每个情况、行业而异。
总之,我们看到了分析鼠标移动行为数据如何有助于预测不必要的或欺诈性的访问。
距离、速度、角度等数据与基础物理知识相结合,可用于创造性的特征工程,以开发预测模型来打击在线欺诈
参考
[1]—《IET 日报》使用鼠标动力学进行入侵检测(https://arxiv.org/pdf/1810.04668v1.pdf)
[2] —巴拉比特老鼠挑战数据集(【https://github.com/balabit/Mouse-Dynamics-Challenge】T2
网站(全球资讯网的主机站)
你可以访问我的网站了解数据科学。你也可以用零编码进行分析。参见下面的链接
[## 体验数据科学
激活您的数据科学思维
experiencedatascience.com](https://experiencedatascience.com)
请订阅每当我发布一个新故事时,请随时关注。
[## 每当 Pranay Dave 发表文章时,您都会收到电子邮件。
每当 Pranay Dave 发表文章时,您都会收到电子邮件。通过注册,您将创建一个中型帐户,如果您还没有…
pranay-dave9.medium.com](https://pranay-dave9.medium.com/subscribe)
你也可以通过我的推荐链接加入 Medium。
[## 通过我的推荐链接加入 Medium—Pranay Dave
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
pranay-dave9.medium.com](https://pranay-dave9.medium.com/membership)
Youtube 频道
这里是我的 Youtube 频道的链接
【https://www.youtube.com/c/DataScienceDemonstrated
使用 Azure 数据工厂将数据从本地 SQL Server 移动到 Azure Blob 存储
虽然有一个将数据从本地 SQL Server 复制到 Azure Blob 存储的官方教程(https://docs . Microsoft . com/en-us/Azure/data-factory/tutorial-hybrid-copy-portal),但本文将重点讨论该教程中没有涉及的一些细节。举个例子,
- 如何手动设置自托管集成运行时(SIR)?
- SQL Server 安全配置提示
- 安全气囊系统连接绳配置提示
- 将 Azure Blob 存储添加为接收器的提示
本教程不会从创建 Azure 数据工厂(ADF)实例开始。如果你还没有,并希望从那里开始,使用上面的官方教程就足够了。
现在,我假设您已经准备好了本地 SQL Server 和 ADF 实例。现在我们应该开始了。
准备数据源
让我们创建一个测试数据库,将本地 SQL Server 中的测试应用表作为数据源。因此,首先转到您的 SQL Server Management Studio (SSMS)来创建数据库和表。
Right-click “Databases” and select “New Database…”
Input a database name
Right-click the database select “New Query”
复制粘贴下面的 SQL 脚本来创建表并插入一些测试条目。这段代码来自上面的官方教程
CREATE TABLE dbo.emp
(
ID int IDENTITY(1,1) NOT NULL,
FirstName varchar(50),
LastName varchar(50)
)
GOINSERT INTO emp (FirstName, LastName) VALUES ('John', 'Doe')
INSERT INTO emp (FirstName, LastName) VALUES ('Jane', 'Doe')
GO
创建 SQL Server 用户
在实践中,我们应该让 SIR 使用 root 用户或任何具有 admin 权限的用户。相反,出于安全原因,我们希望为目标数据库创建一个只读用户。让我们创建这个用户。
Right-click “Security” -> “Logins” under the SQL Server and select “New Login”
Create the user called “adfuser”, input password
请注意,我们不想为此用户选中“用户必须在下次登录时更改密码”,因为它不应该提供给任何“用户”,而只能由 ADF 服务使用。
然后,让我们将这个用户添加到我们刚刚创建的数据库中。
Right-click “Security” -> “Users” under the new database and select “New User”
Input user name and default schema
In the “Membership” tab, enable “db_datareader”
之后,很重要的一步就是检查你的 SQL Server 是否已经启用了“SQL Server 身份验证模式”。否则,您创建的用户将永远无法访问。
Right-click the SQL Server Instance and go to “Properties”
Go to the “Security” tab and make sure “SQL Server and Windows Authentication mode” is enabled
创建 ADF 管道
转到 ADF Author 选项卡,单击Pipelines
旁边的“…”按钮,并在下拉列表中选择New pipeline
。
然后,在右窗格的主视图中,为管道命名。姑且称之为OnPremSQLEmpToBlob_Pipeline
。
展开Move & transform
部分,将Copy data
拖动到右侧主区域,重命名为SQLEmpToBlobEmp_Activity
。
之后,转到Source
选项卡,点击New
创建一个新的数据集作为源。
搜索“sql server”以获取 SQL Server 连接。选择它并点按“继续”。
让我们称这个数据集为OnPremSQLServerEmp
,然后我们需要为这个数据集创建一个新的链接服务。
让我们将新的链接服务命名为OnPremSQLServer_LinkedService
,然后为这个本地资源创建一个集成时间。
手动设置自承载集成运行时
在上一步之后,我们为集成运行时选择“New”。然后,我们选择“自托管”并单击继续。
输入 SIR 的名称。我想将其命名为DevVmIntegrationRuntime
,因为我将在我的开发虚拟机上设置它。
在这里,我们来到了重要的一步。在官方教程中,建议使用快速设置,这样肯定简单很多。然而,这在某些情况下可能是不可行的,例如,系统安全策略不允许这个自动脚本在这台机器上运行,当您想要使用快速设置时,经常会看到这个错误。
因此,我们需要使用选项 2:手动设置。因此,请下载 SIR 安装程序,并在您想要托管 SIR 的机器上运行它。
只需等待 SIR 安装完成,然后复制密钥并粘贴到已安装的 SIR 中,将它们连接在一起。
它应该会自动相互连接,并且运行 SIR 的机器名称会自动填充为节点名称。你可以重命名它,但对我来说,这没关系。
单击“完成”并等待它自己注册,然后单击“启动配置管理器”,您应该能够看到如下屏幕。
然后,我们可以在Diagnostics
选项卡中测试 SQL Server 连接,如下所示。请注意,我在本地 SQL Server 的同一台机器上运行 SIR,因此我使用localhost
作为服务器名称。然而,出于安全考虑,在实践中不建议这样做。如果您将一台独立的机器与 SQL Server 实例连接,只需用机器名和实例名替换localhost
。
另一个重要的提示是,在这个连接测试中,我们需要使用双斜杠\\
来分隔服务器名和实例名。
然后,我们回到 ADF。应用并关闭安全气囊系统配置面板。现在,我们也想测试这里的连接。请看下面的截图。
另一个非常重要的提示是,我在其他地方没有找到任何详细说明。也就是说,这里的服务器名不应该是双斜杠。这将导致您测试连接时出错。相反,您应该使用单斜线\
来分割服务器名和实例名。我相信这是因为 SIR 依靠反斜杠来转义斜杠字符,但 ADF 不需要转义,但它会将双斜杠转换为实际上与服务器名和实例名不匹配的两个斜杠符号。因此,您可能会得到以下错误:
连接失败
无法连接到 SQL Server:“localhost \ \ SQLEXPRESS”,数据库:“testdb”,用户:“adfuser”。实例失败。活动 ID: xxxxxx-xxxxx-xxxxx。
现在,我们已经有了一个到本地 SQL Server 的有效链接服务器。让我们选择要从中提取数据的表,即dbo.emp
,然后单击 OK。
在 Azure Blob 存储中创建接收器数据集
转到管道配置面板上的接收器选项卡,单击“新建”以添加新数据集。
然后,选择 Azure Blob 存储并单击继续。
在这里,我将使用DelimitedText
作为一个例子,因为它是最可读的。
然后,按照说明创建新的链接服务。这次我们将使用AutoResolveIntegrationRuntime
,它将是 Azure Integration Runtime (AIR),因为 Azure Blob 存储是 Azure Cloud 中的原生服务。
在下一步中,将自动选择创建的链接服务。我们需要输入接收器数据集的文件路径。这里我已经创建了一个名为test-container
的 blob 容器,但是文件夹output
和文件emp.csv
目前并不存在。ADF 会自动为我们创建它们。
提示 1:如果您想在 CSV 文件中包含字段标题,请选择“首行作为标题”。
提示 2:我们需要为Import schema
选择“None ”,否则,由于模式导入失败错误,您将无法进入下一步:
架构导入失败:缺少所需的 Blob。container name:https://xxxxx.blob.core.windows.net/test-container,container exist:True,BlobPrefix: emp.csv,BlobCount: 0。\r\n .活动 ID: xxxxx-xxxxx-xxxxx
执行管道
现在,我们应该准备好运行这个数据移动管道了。让发布一切,然后到管道面板顶部工具栏,点击“添加触发器”->“立即触发”。
核实结果
等到通知说管道成功,然后转到 Blob 存储检查输出文件。我更喜欢使用 Azure Storage Explorer,因为我可以直接下载并轻松打开 CSV 文件,但如果你更喜欢 Azure Portal 中的 web 视图,这也没问题。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@qiuyujx/membership)
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)
资源:
Azure 文档,将数据从本地 SQL Server 数据库复制到 Azure Blob 存储:https://docs . Microsoft . com/en-us/Azure/data-factory/tutorial-hybrid-Copy-portal
SQL Server 2017:https://www . Microsoft . com/en-in/SQL-Server/SQL-Server-downloads
Azure 存储浏览器:https://azure . Microsoft . com/en-GB/features/Storage-Explorer/
越过 ROC 曲线,有一个更好的人口健康图
传达模型准确性的新图表
传达预测模型准确性的最佳方式是什么?
在过去的 15 年里,我一直在为医疗保健构建预测模型,这也是我经常回想起的一个问题。测量预测模型的准确性是很简单的,因为大多数建模框架使得计算大量准确性统计数据变得很容易。问题是统计数据本身并不能以一种帮助利益相关者理解如何有效使用模型的方式传达模型的准确性。
这个问题表现在诸如“0.82 好吗?”以及“0.85 比 0.82 好吗,或者这两者非常接近?”当医生、经理或企业用户问这些问题时,他们试图从与他们需要做出的决策相关的角度理解模型的准确性。这并不是说他们不理解统计数据的含义,而是准确性统计数据不足以回答诸如“与我们当前的方法相比,这种模式将为我们节省多少钱?”以及“我们应该在提高准确性方面投入更多,还是这个模型已经足够好了?”
我们发现两个简单的图表在医疗保健环境中交流模型准确性方面非常有效。第一张图,结果捕获曲线,类似于传统的接收器操作特性(ROC)曲线,但是有一些简单但重要的差异,这使得它在向利益相关者传达模型准确性方面非常有效。我们在闭环平台中使用的图表是投资回报(ROI)图表。在这篇文章中,我将描述第一个图表。
成果捕获曲线
我们使用的第一张图是结果捕捉曲线。在任何情况下,当你使用预测来选择一群人来采取行动时,这都是非常有用的。例如,医疗保健提供商使用再入院风险的预测来确定手术后家庭健康访视的目标患者,保险公司使用会员流失的预测来确定再登记激励计划的目标会员。在这两种情况下,预测模型识别出极有可能产生负面结果的人,并针对风险最高的人采取一些干预措施。
对于这些情况,最相关的准确性度量是模型在针对干预方面有多好。在再入院的例子中,如果你有能力干预 5%的患者,那么比较两个预测指标准确性的最佳方法是看哪一个能识别出前 5%人群中更多的再入院。在大多数情况下,这个 5%的门槛并不是固定的。你可能只干预 3%的人,或者最多 10%的人。结果捕获曲线显示了所有不同阈值水平的准确性。再入院模型的图表如下所示:
Image from ClosedLoop Platform
我们使用历史测试集(不是用于建立模型的测试集)来绘制此图,我们可以将模型的预测与实际发生的情况进行比较。横轴表示所选人群的不同百分比,纵轴表示该组中发生的总再入院百分比。图表上的黄线穿过水平轴上的点 10 和垂直轴上的点 22。这意味着具有最高预测风险的 10%的患者占全部再入院的 22%。
使用预测模型进行干预时,最重要的权衡是要做多少干预。进行更多的干预需要更多的资源,但可以避免更多的负面结果。结果捕获曲线直接证明了这种权衡。它不是将模型的准确性作为抽象的统计数据来呈现,而是呈现在关于模型实现的关键决策的上下文中。
在图上画多条线可以让我们比较不同模型的精度。更精确的模型将从相同比例的人群中捕获更高百分比的结果(在图中更高)。在该图中,黄线显示来自机器学习模型的预测,而黑线来自 LACE 指数,这是一种基于规则的再入院风险方法。对于任何级别的干预,机器学习模型都优于 LACE 指数。在 10%的人口中,LACE 指数捕获了 16%的再入院率,而机器学习模型捕获了 22% —提高了 30%。
结果捕获与 ROC
ROC 曲线传统上用于显示模型的准确性。下面的 ROC 曲线比较了与上面的结果捕获曲线相同的两个再入院模型。
Image from ClosedLoop Platform
你可以看到这些图表有很多相似之处。在这两种情况下,黄线在黑线之上,总体形状相似。事实上,两个图中的纵轴是相同的。ROC 曲线中使用的真阳性率(TPR)的定义与二元预测的结果捕获相同。我们更喜欢术语“结果捕获”,因为它更容易被非统计人员理解,并且容易扩展到连续预测,我们将在下面讨论。在这两个图表中,随机预测表示为从图表的左下方到右上方的对角线。
图表之间的关键差异是水平轴。在 ROC 曲线中,横轴是假阳性率(FPR),而在结果捕获曲线中,它是人群的百分比。这很重要,因为结果捕获曲线显示了通过干预不同比例的人群可以达到什么样的准确度。确定要进行的干预的数量,以及执行干预所需的资源,是实现模型时需要做出的关键权衡。
横轴的变化改变了曲线下面积(AUC)指标的含义。对于 ROC 曲线,完美的预测值将具有 1.0 的 AUC。它将有一个 TPR 为 1.0 和 FPR 为 0.0 的点。成果捕获曲线没有这个属性,因为总要包含一些非零百分比的人口,以确定所有的积极成果。可以计算结果捕获曲线的 AUC,但当需要单个准确性统计时,我们更喜欢使用 ROC AUC,因为许多人都熟悉它,它具有 1.0 的良好特性,是一个完美的预测器。
连续结果
与 ROC 曲线不同,可以为预测连续结果的模型绘制结果捕获曲线。同样,如果模型的预期用途是针对那些对某些干预具有最高预测的人,则此图最为相关。在医疗保健领域,一个常见的使用案例是确定可能会产生高额费用的患者,以便为他们提供额外的护理协调。下面我们展示一个医疗费用预测的图表。
Image from ClosedLoop Platform
该图将医疗成本的机器学习预测与现有方法进行了比较,现有方法只是根据患者的历史成本对患者进行排名。如果我们看看机器学习模型预测成本最高的 5%的人口,这些患者占总总成本的 38%。如果我们使用以前的成本作为我们唯一的指南,前 5%只代表总成本的 33%。机器学习模型在相同规模的人群中识别出 15%以上的成本。
ROC 曲线已经存在了 70 多年,并被证明在广泛的应用中非常有用,但没有一个完美的图表适用于所有情况。根据模型的使用方式,不同的决策将是重要的,因此不同的图表将是最相关的。
我们发现,在预测将用于选择高风险人群进行干预的情况下,结果捕捉曲线非常有效。它类似于 ROC 曲线,但以一种有助于直接回答应该进行多少干预的格式呈现信息。它还具有可用于二元和连续预测的优点。
在 ClosedLoop.ai 时,我们的医疗保健预测分析平台将结果捕捉曲线作为模型准确性标准报告的一部分,包括在我们的用户界面和 Python 分析包中。对于二元分类模型,我们也显示 ROC 曲线,但我们一再发现,数据科学家和企业主都发现结果捕获曲线在回答关于准确性的问题时更有用。
作者:Dave DeCaprio,CTO 闭环
Dave 拥有 20 多年将学术研究实验室的先进技术转化为成功业务的经验。他的经验包括基因组研究、药物开发、医疗保险、计算机视觉、体育分析、语音识别、运输物流、运筹学、实时协作、机器人和金融市场。
原载于 2019 年 5 月 21 日https://closed loop . ai。
电影数学:计算机如何理解文本
计算机如何思考:第四部分
令人欣慰的是,尽管人工智能取得了所有的进步,尽管机器无情地进入了曾经是人类专属的工作领域,但总会有一些人类经验的基本方面无法复制。我们喜欢认为,虽然计算机可能擅长机械的、重复的任务,但它总是需要人类的思维来完成任何类型的创造性、艺术性的工作。计算机在数字领域运行。1 和 0,数学运算。算法可以对大量数据进行非常复杂的计算,速度非常快。但是人们在思想、感情和语言的领域里活动。这些概念对计算机来说是难以理解的,就像计算机的内部工作原理对我们来说一样。那么我们能让计算机理解思想和感情吗?我们能把单词的意思翻译成计算机能理解的格式吗?计算机真的能理解我们吗?嗯,也许吧。
在前一章中,我展示了两种非常简单的方法来教计算机理解人类意义:“一键”编码将单词变成一列一列的 1 和 0——单词存在或不存在——机器学习将某些单词的存在或不存在与其他有意义的值相关联。“协同过滤”使用关于人们如何与事物互动的信息,来推断他们与其他事物的相似性。如果同样的人喜欢两件事,那两件事就是相似的。但是这些都是粗糙的方法,效果非常有限。我们可以做得更好。
互联网电影数据库(IMDb)是一个巨大的电影和电视信息资源。我特别感兴趣的是与每部电影相关的用户生成的标签。数百名用户合作将每部电影与单词相关联,为成千上万部电影创建了一个不拘一格、全面但绝不是全面的主题、情节点、图像、人物比喻和电影设备目录。用户兴趣的多样性和特殊性意味着广泛的主题和背景元素,如“枪支暴力”、“父子关系”和“纽约州”,很可能被标记为情节的细节,如“胸口被打一拳”、“噩梦后坐直”和“背景中挥舞美国国旗”。人类奇妙的多样性也是令人不安的特定标签的原因,比如“脚的特写”、“晒黑的裤袜”和“不死的性欲”。
以下是一些随机选择的电影的标签:
Table 1. Movies and their keywords
我们将使用这些标签来构建一个算法,试图理解一些关于电影的东西。我们希望看到的是一种算法,它可以做一些比仅仅找到两部相似的电影更复杂的事情——我们希望它能够真正产生一些关于电影内容的新颖见解。那是一个雄心勃勃的计划!
首先,我们需要将这些信息转化为计算机可以理解的东西。和上一篇文章一样,我们可以通过为每个标签制作一列来以数字格式对这些进行编码,根据电影是否有特定的标签,在列中使用 1 或 0。例如:
Table 2: Encoded keywords
你可以看到 2009 年全球文化的污点,“宿醉”有“老虎”和“好色之徒”的标签。“好色之徒”的标签也出现在 1993 年的《辛德勒的名单》中,尽管我想这两部电影的粉丝都不会欣赏对方对这个主题的处理。相比之下,《辛德勒的名单》是十部电影中唯一一部带有“会计”标签的。可悲的是,关于会计的电影似乎没有关于女权主义者的电影那么常见。
Not enough accountants
标签本身是一部电影情节和主题的不太完美的总结,依靠它们来确定电影的含义这样宽泛的东西似乎是愚蠢的。但是我们可以使用一个技巧来大大增加这些标签的实用性。它们可能是独特的和不完整的,但我们可以有把握地假设,在一定范围内,很可能在任何选择的电影中,它们至少是一贯独特的。换句话说,它们可能很奇怪,但每部电影都一样奇怪。如果我们看看电影之间有什么共同的标签,我们应该会发现相似的电影有相似的标签。看起来是这样的:
Table 3: Selected movies by proportion of all keywords in common
每部电影显然都与自己有 100%的相似性,但电影与其他电影的相似之处更有趣。1967 年的西部片《黄金国》与 1977 年的二战史诗片《一座太远的桥》有许多相同的关键词。暴力冲突的主题,粗犷的男性主角,也许还有其他因素让这些电影有些相似。相比之下,《黄金国》与 2003 年的合家欢假日喜剧《精灵》完全没有关键词。这两部电影一点也不相似。与《精灵》更相似的是 1991 年的《钩子》对彼得潘故事的复述。这些电影有几个共同的主题和人物比喻,用“孤儿”、“家庭关系”、“男孩”和“圣诞节”等关键词来表示。
现在我们有了电影相似性的矩阵,我们可以用它做一些有趣的事情。例如,我们可以根据电影与其他电影共有的关键词数量在图表上绘制电影。以下是所有迪士尼公主系列电影,根据它们与凯文·科斯特纳 1991 年的《英国口音谋杀案》、《罗宾汉:小偷王子》和 1941 年的经典电影《公民凯恩》(坦白地说,被高估了)的相似性进行排序:
Disney Princess films by similarity to Prince of Thieves and Citizen Kane
让他们受益匪浅的是,迪士尼公主系列电影与《公民凯恩》没有太多相似之处。《美女与野兽》与《公民凯恩》的相似之处很容易让人理解,因为它们都有一个富裕而隐居的男性主角,以及看到一个怪物般的人的人性的主题。但 IMDb 标签的特质意味着实际的共同关键词要平淡得多——它们大多与两部电影都有场景在一座古老的大宅邸有关,有壁炉、仆人、图书馆等等。与《小美人鱼》的相似之处同样微不足道,包括两部电影都包含章鱼的简短镜头。
大多数电影与罗宾汉有更多的相似之处。或许并不令人意外的是,2012 年的《勇敢》与其他电影相比,与罗宾汉有更多的共同之处——中世纪的背景和主要是射箭,尽管 IMDb 爱好者的勤奋工作也揭示了这两部电影都有一个裸露臀部的短暂一瞥。与《勇敢》相比,迪士尼 1973 年的关于罗宾汉的麻烦电影类型中,与凯文·科斯特纳版本相同的关键词几乎是两倍,尽管标题角色中有一只狐狸。
有趣的是,《美女与野兽》和《小美人鱼》在这张图表上看起来非常接近,这意味着,至少在与《罗宾汉》和《公民凯恩》的相似性方面,它们有很多共同点。但总的来说,这张图表对这些电影的本质可能不是很有洞察力。
这里有一个非常有用的图表:
Fast and the Furious films by similarity to “The 36th Chamber of Shaolin” and “Kindergarten Cop”
截至发稿时,这是所有的“速度与激情”电影,其情节与阿诺德·施瓦辛格 1990 年涉足喜剧表演的《幼儿园警察》和 1978 年的功夫经典《邵琳·石三·流芳》(更为人所知的是《少林三十六房》)相似。正如我们所料,这些电影都非常接近——它们是非常相似的电影。《东京漂移》是个例外,它是系列电影中被低估了犯罪价值的第三部。有趣的是,就情节、人物和主题而言,《东京漂移》实际上是一部与其他系列完全不同的电影。虽然制作这张图表的许多数据都是琐碎的或虚假的,但它设法捕捉到了这部电影的一些真相及其与兄弟姐妹的关系。
This but his fists are cars
这些图表只在两个相当随意的轴上绘制电影,或多或少是随机选择的,但它们设法传达了一些关于电影内容的少量真实信息。想象一下,通过增加我们检查的轴的数量,可以获得多少信息。
为了有效地做到这一点,我们必须做一些数学技巧。在上一章中,我提到了一些提高我们推荐质量的数学技巧。当时我没有详细介绍它们,但我们将再次使用这些相同的技术,这次我将对它们进行更多的解释。这将会变得有点复杂,但是你不需要理解基本的数学就能知道它是如何工作的。
如果你看上面的表 3,它显示了每部电影与其他电影的相似性。但是你会发现很多电影根本没有共同的关键词。其他电影有很多共同点,但它们的相似性是由它们碰巧有几个非常不寻常的关键词这一事实驱动的。我们的数据集所基于的标签是独特的——单独来看,它们通常没有传达太多的意义,但总体来看,它们更有用。我们需要的是某种方式来消除粗糙的边缘,过滤掉噪音,把数据浓缩成核心信息。我们将使用的技术称为“降维”。我们将获取包含数千部电影的数据集,每部电影都与其他电影进行比较,并将列数从数千列(每部电影一列)减少到仅一百列。当我们这样做的时候,我们会尽可能多地保留列中包含的信息。
当我第一次听说这个技术时,听起来不像是可能的。我们如何减少列的数量,但保留它们包含的信息?这似乎是一个矛盾,从某种意义上说,的确如此。我们确实会丢失信息,但是如果我们聪明的话,我们可以将这种情况最小化。为了理解我们如何能做到这一点,只在二维空间中想象它是有帮助的。
想象你在二维图表上有一系列的点,就像这样:
每个点的位置可以写成两个坐标——两列数据。现在想象我们在这些点旁边画一条直线,并将它们的位置“投影”到这条直线上,就像这样:
线上每个点的位置可以写成一个坐标,即它沿线的位置。我们已经丢失了一些关于原始点的信息,但是,只要我们与这些点成直角画线,我们就保留了一些关于它们彼此相对位置的信息。“降维”是一套为那条线选择正确角度的数学技术,这样我们就能保存尽可能多的信息。这在二维空间很容易想象,但在多维空间也同样适用。在我们的例子中,我们将维度(数据列)的数量从几千减少到一百。
选择 100 列有点武断。减少列数可以减少数据中的“噪音”,即任意和无用的信息。但是如果我们过多地减少列数,我们也会丢失有用的信息。这是我们的电影,只包含两列信息:
Table 4: Films reduced to two dimensions
剩下的两列是不可能解释的——丢失了太多信息,剩下的两列实际上毫无意义。我们需要找到一个平衡点,事实证明,在这种情况下,对于这个数据集,100 列是合适的。下面是一个小例子:
Table Five: Films as “vectors”
每一列的含义甚至比我们之前的单个列更难解释,但总的来说,它们编码了关于我们数据集中的电影的一些非常复杂的信息。根据下表中的数据,我们可以通过查看哪些电影看起来相似来测试这一点。如果我们做得很好,我们表中的类似电影应该是现实生活中非常相似的电影。例如,根据这一数据,与《宿醉》类似的电影包括同样可怕的《40 岁的处女》、《宿醉第二部》和《怀孕》。看起来我们做得很好——所有这些电影都来自类似的愤世嫉俗、厌恶女性的喜剧。他们分享相似的题材和相似的喜剧风格。最接近本·阿弗莱克职业生涯巅峰的电影,1998 年的《世界末日》,是《深度撞击》,这是有意义的,因为它基本上是完全相同的电影,只是(悲惨地)少了《史密斯飞船》。
我们可以用这个数据集做一些其他有趣的事情,但是为了解释它们,我将不得不离题进入一些关于相似性度量的细节。大家深呼吸。
在上一章中,我展示了如何使用“欧几里德距离”来度量两行数字数据的相似性。通过将它们绘制为多维空间中的点,并在两点之间画一条线,可以测量两行的相似性。这种方法也适用于这个数据集。但是还有另一种方法。我们可以测量两点之间的角,而不是测量两点之间的距离。对于那些比我更懂数学的人来说,这被称为“余弦相似性”。例如,这里有两部经典的航海主题电影,《海底总动员》和《大白鲨》,它们的情节与 1990 年的独立电影《忍者神龟》和约翰·卡彭特的低成本动作经典《逃离纽约》相似。
这两个点之间的角度大约为 40 度,表明这两部电影非常不同,至少在这两个轴提供的“泡沫海龟/蛇皮”尺度上是如此。在大多数情况下,余弦相似性和欧几里德距离给出非常相似的结果。但是余弦相似性有一个非常不同的性质。不管两条线的大小如何,余弦相似度保持不变。换句话说,每个值的总量并不重要,重要的是一个值与另一个值的比率。这很有用,因为这意味着我们可以将两部电影的值相加,并且仍然可以有效地测量相似性。这听起来有点深奥,所以让我们进一步研究一下。
约翰·卡彭特的另一部经典电影《神探夏洛克》比《大白鲨》离《海底总动员》更远。如果我们把这个东西和《海底总动员》的价值观加在一起,我们可以创造一部假想的新电影《海底总动员》(出于显而易见的原因,我们将避免称之为《海底总动员的东西》)。《尼莫的事情》,因为它结合了另外两部电影的价值观,比其他任何一部电影都更靠近这两个轴。这实际上是两部电影的总和——《海底总动员》和《忍者神龟》有大约 8%的关键词是相同的,而《神探夏洛克》有大约 3%。这使得《尼莫的事情》在这个轴上的得分约为 11%——8+3 = 11。这意味着我们的新电影与图表上的其他电影相距甚远——它在两个轴上都有更高的值。但是因为我们使用余弦相似度,并测量角度,而不是距离,我们发现“尼莫的事情”,我们两部电影的组合,实际上非常类似于“大白鲨”。
让我们回顾一下这里发生了什么。我们把一部关于水生冒险的电影和一部关于被外星怪物跟踪的电影结合起来,结果和《大白鲨》非常相似,一部关于被水生怪物跟踪的电影。这是巧合吗?这仅仅是数学上的意外,还是我们得到的数值实际上编码了电影的一些有用的意义片段?在上面,我承诺我们会建立一些真正理解电影的东西。我们成功做到了吗?
Never stop swimming
当只使用两个轴时,正如我们上面提到的,这种方法通常会返回无意义的结果。但是当我们使用我们之前创建的完整的 100 列数据集时,结果变得异常深刻。正如当我们使用两个轴时,我们取两个电影的总和,将 100 列中每一列的值相加。然后,使用余弦相似性,我们找到与总计行具有最小角度(最小差异)的电影。使用这种方法,凯瑟琳·毕格罗最好的电影,1991 年的“突破点”,加上迪斯尼的“汽车”,产生了一部电影,根据余弦相似性,最类似于 2001 年的“速度与激情”。这是惊人的准确。《突破点》和《速度与激情》都是关于特立独行的警察,为了逮捕一个与他有着强烈同性恋关系的犯罪大师,他们被卷入了一个诱人的亚文化中。他们之间唯一的区别是斯威兹闪闪发光的眼睛,而点突破侧重于冲浪文化,速度与激情迷恋汽车。
Try this for yourself, at moviemaths.com
通过同样的过程,我们可以尝试将 1979 年的革命恐怖电影《异形》加入到 2004 年的高中喜剧《贱女孩》中。根据我们的余弦相似性计算,得到的值非常类似于斯蒂芬·金改编的“嘉莉”,这是一部关于高中刻薄女孩的恐怖电影。也许为了调和不同朋友群体的利益,我们可以试着找一部结合了《辛德勒的名单》和《宿醉》的电影。该算法告诉我们,我们最好的赌注是明显被遗忘的二战动作片“兵临城下”(2001)。
这种方法也适用于减法。如果我们拿令人不安的种族主义的“印第安纳·琼斯和毁灭之庙”来说,减去它稍微不那么种族主义的前身“夺宝奇兵”,最终的电影最类似于 1941 年的“泰山和豹女”,委婉地说,这部电影对积极的种族关系没有什么贡献。
这里发生了什么事?这些组合并不总是完全有意义,但在它们背后有一些有意义的暗示。在这个过程的最后,我们得到的是一个关于电影的数据编码,它似乎包含了电影的的含义。算法仍然只是数字和数学,但结果,如我们的电影组合技巧,感觉像是真正了解电影的东西的产物。我们所看到的感觉非常接近真正理解我们的计算机的愿景。我们转换数据的方式允许计算机生成关于我们数据集中电影的独特见解。它可以给我们一个新的视角来看待它们。
我们是否成功地制作出了真正理解电影的东西?很容易让人觉得,算法已经对它得到的信息产生了某种理解。像我们上面使用的那些技术越来越多地用于各种领域来解析和过滤大量的信息。能够扫描数百万份文件以找到相关案例法的算法正在彻底改变律师事务所。医生可以调用自动程序来查找最近的相关文献,以帮助治疗患者的疾病。我们甚至可以制作这些文件的连贯和有用的摘要,只需按一下按钮。至少在某些领域,计算机可以阅读和理解文本。
但是在这个有点不可思议的技巧和真正的人类洞察力之间似乎还有很大的差距。计算机可能会得到一个有意义的答案,但它真的知道这个答案意味着什么吗?感觉我们构造了一个精心制作的魔术师的幻像——兔子从帽子里出来,如果我们忘记了帽子的假底和魔术师的灵巧,我们几乎可以相信这是魔术。真实的智能和模拟的智能有区别吗?当我们深入研究更复杂的算法时,我们会再次回到这个问题。目前,感觉我们已经创造了一个有趣的玩具,但它离“真正的”智能还差得很远。在未来的文章中,这条线将变得越来越模糊。我们需要一艘更大的船。
本系列的上一篇文章中, 编织与建议 在这里可以找到。下一篇短文,《游戏中的* 大脑:电子游戏 AI *》这里的。****
要自己尝试电影数学,去moviemaths.com。这篇短文的所有代码都可以在我的 github、 这里 找到。
从喀拉斯到皮托尔彻
为什么?怎么会?没那么难。
Photo by David Clode on Unsplash
Pytorch 很棒。但这对初学者来说并不容易。
不久前,我在 Kaggle 上与一个关于文本分类的竞赛合作,作为竞赛的一部分,我不得不设法转移到 Pytorch 以获得确定性的结果。结果不会随着网络的每次运行而改变,以尝试不同的模型并获得一致的结果。
现在,我过去一直与 keras 一起工作,它给了我相当好的结果,但不知何故,我知道 Keras 中的 CuDNNGRU/CuDNNLSTM 层不是确定性的,即使在设置种子之后。
所以 Pytorch 确实来救援了。我很高兴我考虑过搬家。
作为边注:如果你想了解更多关于 NLP 的知识,我想推荐深度学习专精中关于序列建模的这门牛逼课程。本课程涵盖了自然语言处理中从基础到高级的各种任务:情感分析、摘要、对话状态跟踪等等。
虽然 Keras 在深度学习方面起步很好,但随着时间的推移,你会对它的一些局限性感到不满。
我也想过搬到 Tensorflow。这似乎是一个很好的过渡,因为 TF 是 Keras 的后端。
但是这很难吗?
对于整个session.run
命令和张量流会话,我有点困惑。它根本不是毕氏的。
Pytorch 在这方面有所帮助,因为它更像 Pythonic。一切都在你的掌控之中,在性能方面没有任何损失。也许真的有所收获。
用安德烈·卡帕西的话说:
我已经使用 PyTorch 几个月了,从来没有感觉这么好过。我有更多的精力。我的皮肤更干净了。我的视力提高了。
—安德烈·卡帕西(@卡帕西)2017 年 5 月 26 日
所以事不宜迟,让我为你把 Keras 翻译成 Pytorch。
写你的网络的经典方式?
OOPs: Object-Oriented Programming
让我们首先在 keras 中创建一个示例网络,我们将尝试将它移植到 Pytorch 中。
在这里,我也想给你一个建议。当你试图从 Keras 迁移到 Pytorch 时,使用你拥有的任何网络,并尝试将其移植到 Pytorch 。这会让你更好地理解 Pytorch。
在这里,我试着写一个在 Quora 虚假问题分类挑战中给我很好结果的网络。
这个模型具有至少任何文本分类深度学习网络可以包含的所有功能,包括 GRU、LSTM 和嵌入层,以及元输入层。因此可以作为一个很好的例子。
如果你想了解更多关于比尔斯特姆/GRU 和注意力模型是如何工作的,请访问我的帖子这里。
因此 pytorch 中的模型被定义为一个从nn.module
继承而来的类(因此更优雅一点)。每个类都必须包含一个__init__
程序块和一个forward
通道块。
- 在
__init__
部分,用户定义了网络将要拥有的所有层,但还没有定义这些层如何相互连接 - 在正向传递模块中,用户定义数据如何在网络中从一层流向另一层。
为什么这是经典?
我发现这是有益的,原因如下:
1)它让你对如何构建你的网络有很大的控制权。
2)当您构建网络时,您对网络有很多了解,因为您必须指定输入和输出维度。所以出错的几率更少。(虽然这个真的要看技能水平了)
- 易于调试的网络。任何时候你发现网络有任何问题,只要在正向传递中使用类似
print("avg_pool", avg_pool.size())
的东西来检查层的大小,你就可以很容易地调试网络
4)您可以从正向层返回多个输出。这在编码器-解码器架构中非常有用,因为您可以返回编码器和解码器的输出。或者在 autoencoder 的情况下,您可以返回模型的输出和数据的隐藏层嵌入。
- Pytorch 张量的工作方式与 numpy 数组非常相似。例如,我可以使用 Pytorch Maxpool 函数来编写 Maxpool 层,但是
max_pool, _ = torch.max(h_gru, 1)
也可以。
6)您可以用不同的初始化方案设置不同的层。一些你在喀拉斯做不到的事。例如,在下面的网络中,我改变了我的 LSTM 层的初始化方案。LSTM 图层对于偏差、输入图层权重和隐藏图层权重具有不同的初始化。
7)等到你看到 Pytorch 中的训练环,你会对它提供的那种控制感到惊讶。
现在 Pytorch 中的同一个模型看起来会像这样。请仔细阅读代码注释,了解更多关于如何移植的信息。
希望你还和我在一起。我想在这里强调的一点是,你需要用 Pytorch 编写一些代码来真正理解它是如何工作的。
并且知道一旦你这样做了,你会很高兴你付出了努力。进入下一部分。
高度可定制的训练循环
在上面的部分,我写道,一旦你看到训练循环,你会感到惊讶。这是一种夸张。
第一次尝试时,你会有点困惑。
但是一旦你不止一次地通读这个循环,你会有很多直观的感觉。再次阅读注释和代码以获得更好的理解。
该训练循环对训练数据进行 k 重交叉验证,并输出对测试数据的运行进行平均的非折叠 train_preds 和 test_preds。
如果这个流程看起来像是从 kaggle 竞赛中直接出来的,我很抱歉,但是如果你明白这一点,你就能够为自己的工作流程创建一个训练循环。这就是 Pytorch 的魅力。
所以这个循环的简要总结如下:
- 使用培训数据创建分层拆分
- 循环遍历拆分。
- 使用
X_train_fold = torch.tensor(x_train[train_idx.astype(int)], dtype=torch.long).cuda()
命令将训练和 CV 数据转换为张量并将数据加载到 GPU - 使用
model.cuda()
命令将模型加载到 GPU 上 - 定义损失函数、调度程序和优化程序
- 创建
train_loader
和 valid_loader `来迭代批处理。 - 开始运行纪元。在每个时代
- 使用
model.train()
将模型模式设置为训练。 - 检查
train_loader
中的批次并向前运行 - 运行调度程序步骤以更改学习率
- 计算损失
- 将优化器中的现有梯度设置为零
- 通过网络反向传播损失
- 剪切渐变
- 采取优化步骤来改变整个网络中的权重
- 使用
model.eval()
将模型模式设置为评估。 - 使用
valid_loader
获得验证数据的预测,并存储在变量valid_preds_fold
中 - 计算损失并打印
- 所有历元完成后,预测测试数据并存储预测。这些预测将在分割循环结束时进行平均,以获得最终的
test_preds
- 使用
train_preds[valid_idx] = valid_preds_fold
获得列车组的脱叠(OOF)预测 - 然后,这些 OOF 预测可用于计算模型的局部 CV 分数。
但是为什么呢?为什么这么多代码?
好吧。我明白了。这可能是少数。在 keras 中用一个简单的.fit
就可以完成的事情,在 Pytorch 中需要大量代码才能完成。
但要明白,你也获得了很多权力。您需要了解的一些使用案例:
- 在 Keras 中,你可以像
ReduceLROnPlateau
一样预先指定调度程序(编写它们是一项任务),而在 Pytorch 中,你可以疯狂地尝试。如果你知道如何写 Python,你会过得很好 - 想在两个时期之间改变模型的结构。是的,你能做到。动态更改卷积网络的输入大小。
- 还有更多。只有你的想象会阻止你。
想自己经营吗?
这里还有一个小小的坦白。
上面的代码将不会运行,因为有一些代码工件,我没有在这里显示。我这样做是为了让帖子更具可读性。就像你在上面的代码中看到的seed_everything
、MyDataset
和CyclicLR
(来自杰瑞米·霍华德课程)函数和类,它们并没有真正包含在 Pytorch 中。但别担心,我的朋友。
我试过用完整的运行代码写一个 Kaggle 内核 。 您可以在这里看到代码,并将其包含在您的项目中。
看看 Google Cloud Specialization 上的高级机器学习。本课程将讨论模型的部署和生产。绝对推荐。
我以后也会写更多这样的帖子。让我知道你对这个系列的看法。在 中 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系。
X 先生,网飞,和数据分析中的隐私
Photo by Dayne Topkin on Unsplash
世界各地的隐私监管机构都在关注数据隐私,这对像我们这样完全依赖数据的分析师来说还有什么空间呢?
在过去的几年里,“竞争”已经成为一个时髦词,因为人们的数字存在已经增加到了巨大的数量。随着大量数据出现在互联网上,我们现在比以往任何时候都更加了解彼此。这些数据在很长一段时间里帮助分析者了解我们的社会是如何运作的,并证明有助于为社会中存在的问题提供解决方案。
但是,随着世界各地数据泄露事件的不断出现,个人和当局越来越担心他们的数据是如何被使用的。因此,个人和组织对数据隐私的意识增强了,这给像我们这样的分析师留下了一个大问题,他们完全依赖数据来产生新的有用的见解。
[## 过去 15 年中 15 起最大的数据泄露事件(信息图)
数据泄露是互联网时代最大的问题之一。个人数据变得越来越…
hostingtribunal.com](https://hostingtribunal.com/blog/biggest-data-breach-statistics/)
那么,当我们的工作是从数据中学习统计见解以更好地理解社会趋势时,我们如何提供保护隐私的分析服务呢?保护隐私的数据分析问题跨越多个学科有着悠久的历史(辛西娅·德沃克等人。艾尔。).剩下的唯一选择是从整体数据中了解洞察力,而不是了解个人的任何信息。这就是差分隐私概念发挥作用的地方,这是一个令人兴奋的新的多学科领域,允许我们开发所谓的私人数据分析技术。
随着关于个人的电子数据变得越来越详细,并且随着技术能够更强大地收集和管理这些数据,对隐私的鲁棒、有意义和数学上严格的定义以及满足该定义的计算丰富的算法类的需求增加了。差分隐私就是这样一个定义。
上面给出的摘要摘自《差分隐私的算法基础》,这是一本关于这个主题的必读书籍,作者是辛西娅·德沃克,通常被认为是差分隐私的教母。
差分隐私的正式介绍:
“差异隐私”描述了数据持有人或管理者对数据主体做出的承诺:“无论有什么其他研究、数据集或信息源可用,您都不会因为允许您的数据用于任何研究或分析而受到不利或其他影响。”
上面给出的是差分隐私的正式定义。让我们试着把它分解,理解它试图传达什么。
- 首先,差分隐私是而不是一种算法或技术,当应用时将执行隐私保护数据分析,它更多的是一种承诺,因此这里涉及到一个信任因素。
- 其次,当我们对人群数据应用差异隐私时,我们必须只学习该人群中作为一个整体存在的模式,而不是帮助我们唯一识别该数据中涉及的个人的模式。这听起来可能有些矛盾,但这是非常可能的。
- 因此,我们现在明白,差异隐私是分析师对数据主体的承诺,即我不会使用您的信息,因此不会以任何方式影响您。凭直觉思考,这表明,
如果 没有了解到任何关于个人的新信息 即使在对他参与的数据执行分析之后,我们也可以说我们保留了他的私人信息。
- 定义的最后一部分说了一些非常有趣的事情,它表明即使有其他记录包含您的数据,并且作为一名分析师,保护您的数据免受这些记录中的对手的攻击超出了我的控制范围,我仍然不会让他们使用这些数据来了解您的新情况。
为了理解这个定义的最后一部分,让我们看看网飞。
2000 年代末,网飞举办了一场竞赛,开发一种更好的电影推荐算法。为了推动竞争,他们发布了一个“匿名化”的观看数据集,该数据集已经去除了识别信息。不幸的是,这种取消身份证明是不够的。在一项著名的工作中, Narayanan 和 Shmatikov 展示了这样的数据集可以用来重新识别特定用户——甚至预测他们的政治倾向!—如果您只是知道一点关于给定用户的附加信息。
所以我们现在明白,用某些整数值改变实际值并不是在执行数据分析时保护隐私问题的解决方案。差分隐私在这里发挥了作用,它说即使相关数据存在,我们仍然可以防止任何对手从这个数据集中提取信息,并了解关于个人的任何新信息。
保护隐私的数据分析:
现在我们知道,数据匿名化并不是在进行数据分析时保护隐私问题的解决方案。但这不是这里唯一的关注点,让我们再看几个。
- 数据不能匿名且保持有用。 匿名化的问题在于它为链接攻击留下了空间,正如我们在上面的网飞数据库中所看到的。差异隐私对这些类型的攻击是稳健的,因为它是数据访问机制的一个属性,与对手是否有可用的辅助信息无关。
这意味着您是否是该数据库的一部分并不重要,只要您在数据中的存在与否不会改变对数据库的查询输出。
- 对大集合的查询没有保护性。 另一个主要问题是,正确回答重复查询会迅速利用隐私。你不能指望每次都回答准确,还谈论隐私。现在我们来谈谈X 先生。
假设已知X 先生 在某个医学数据库中。综合起来,对 两个大型查询 的答案是“数据库中有多少人具有镰状细胞性状?”以及“数据库中有多少人(未命名为 X)具有镰状细胞特征?”产镰状细胞状态的 Mr. X.
- 查询审核有问题。 我想到了一个显而易见的想法,如果我能找出所有危及数据库隐私的查询序列,我就能以某种方式提高数据库的安全性。
但是这种方法存在一些问题,首先是这个任务在计算上是不可行的,其次拒绝回答查询本身就是公开的,拒绝回答查询向对手暗示他正在损害隐私的正确道路上。
差异隐私提出了一种方法,可以帮助我们对数据进行分析研究,而不会泄露任何私人信息。
总结:
因此,我们了解了什么是差异隐私,以及为什么要使用它来执行隐私保护数据分析。本文是我将要写的关于数据分析中的隐私的系列文章中的一篇。
在下一篇文章中,我们将深入探讨与差异隐私相关的技术术语,并了解它实际上是如何工作的。
请继续关注。现在祝您学习愉快。
附言。这是我在 medium 上的第一篇文章,所以欢迎任何反馈和建议。
MRNet:用于膝盖 MRI 扫描的深度学习辅助诊断
还有斯坦福 ML 集团主办的类似 kaggle 的比赛
上周,我去了西班牙南部的小镇埃斯特波纳,参加了为期一周的编码务虚会。作为参加 MRNet 竞赛的一部分,我用 PyTorch 从头开始复制了 MRNet 论文。
我已经开源了代码,所以你也可以把它作为参与竞争的起点。您可以从 MRNet GitHub repo 访问所有代码和 Jupyter 笔记本。
让我们帮助推进人工智能在医学成像中的安全使用!
背景
在论文 膝关节磁共振成像的深度学习辅助诊断:MRNet 的开发和回顾性验证中,斯坦福 ML 小组开发了一种预测膝关节 MRI 检查异常的算法,并测量了在解释过程中向放射科医生和外科医生提供该算法预测的临床效用。
他们开发了一个深度学习模型,用于检测:一般异常,前十字韧带撕裂,半月板撕裂。
MRNet 数据集描述
数据集(~5.7G) 随着论文的发表一起发布。您可以通过同意研究使用协议并在 MRNet 竞赛页面提交您的详细信息来下载。
它由 1,370 项膝关节核磁共振检查组成,包括:
- 1104 例(80.6%)异常检查
- 前交叉韧带撕裂 319 例(23.3%)
- 508 例(37.1%)半月板撕裂
数据集分为:
- 训练集(1,130 次检查,1,088 名患者)
- 验证集(120 项检查,111 名患者) —本文中称为调优集
- 隐藏测试集(120 项检查,113 名患者) —本文中称为验证集
隐藏测试集不是公开可用的,用于对提交给竞赛的模型进行评分。
注意事项
探索性数据分析
在尝试训练模型之前,通过探索和熟悉数据来获得领域知识是至关重要的。为此,我对所提供的数据集进行了 EDA。
这里 可以访问笔记本 的公开托管版本。
代码实现
训练和评估模型所需的所有代码都发布在MRNet GitHub repo中。我强烈推荐使用 GPU 进行训练。
模型提交
一旦你有了自己的模型,你就可以按照作者提供的教程提交给官方评估。根据他们的说法,大约需要 2 周时间分数才会出现在排行榜上——我仍在等待我的分数出现!
反馈
在我尝试改进代码和模型时,代码在 MIT 许可下处于积极开发中。随意克隆它,叉它,使用它——如果你这样做了,请让我知道你的想法!
我将非常感谢您的建设性意见、反馈和建议🙏
谢谢,祝编码愉快!
搅浑水:你的用水并没有发生在你认为它发生的地方
循证政策比你或你的感觉更重要——第四部分
你曾经在水的问题上花了很多心思吗?不一定要喝,但是要用。就像我们大量拥有的其他东西一样,我们通常不会想到水。然而它在我们的生活中无处不在,必不可少。我们确实需要它来生活,我们需要它来满足其他基本的卫生需求,当然我们也需要它来做饭和洗衣。然而另一方面,我们敏锐地意识到干旱和水资源短缺甚至被预测为未来战争的主要原因之一。因此,关于用水问题的讨论似乎是适当的。鉴于我们每天都在自由地用水,我们是否需要为了环境重新考虑我们的习惯?不要浪费太多,但是节约用水对环境有很大的好处——即使你可以节约用水的地方可能和你想象的不一样。在某些方面,节约用水甚至会给你带来经济上的好处。但是在我们考虑回报之前,我们首先需要看看我们实际上是如何直接和间接地使用水的。
家庭直接用水
在英国这样的欧洲国家,普通人每天在家中直接使用大约 150 升水,而在 T2,像澳大利亚和美国这样的国家,这个数字要高得多。这听起来可能不多,但在欧洲,家庭用水量占总用水量的 18%(T4)。特别值得注意的是,如果你考虑到 150 升大约是人类基本需求估计量的三倍。人们在家庭中花费这些水的相对比例各不相同,但大约四分之一分别用于淋浴、冲厕所和厨房用品。其他项目比如说包括洗衣服&灌溉。相比之下,像卢旺达或海地这样的贫穷国家,每人每天只有 2 到 15 升水可供使用。甚至不到我们日常用水量的十分之一!
关于水的使用,这里有几个趋势需要解释。首先,显而易见的是:一个国家越富裕,人均日用水量就越高。你可以在二战后不断增加的用水量中清楚地观察到这一点。从简陋的抽水马桶到洗碗机和游泳池,所有这些花哨的用水设备按全球标准来看都很昂贵。另一个明显的趋势是,人均用水量随着家庭规模的扩大而减少(其他条件相同)。将一个人在半空的洗衣机里洗三件衬衫和一条牛仔裤与一个家庭正确地装上洗衣机相比,你很容易明白为什么会这样。我们的许多用水都可以很好地衡量(当然,像厕所这样的项目在家庭中的衡量并不比单身人士好)。直接用水的最后一个趋势是技术。幸运的是,技术在进步,我们的家用电器也在进步。总的来说,这导致了洗碗机、马桶等更高效的版本。使用更少的水(特别是在最近的生态友好年)。当然,定性的新技术是个例外(比如第一个抽水马桶的发明,最初可能增加了用水量),这种技术在开始时需要进一步调整,以达到节能和节水的状态。
鉴于这些趋势,什么主导着家庭用水?总体来说,似乎是财富。尽管有一段时间,尤其是最近,水消耗量增长缓慢,甚至有所下降。那么有没有节约用水的可能性呢?绝对的。出于我们将在下一节探讨的原因,减少直接用水对环境没有多大好处。但它肯定可以帮助你节省一些钱!显而易见的第一步当然是淋浴,而不是洗澡&刷牙时关掉水龙头。稍微不太明显的是,在英国大约有 75%的 T2 家庭经常烧开比他们需要的更多的水,这相当于每年大约 6800 万英镑的不必要开支。在美国,大约 17%的 T4 家庭给花园浇水过多。解决这一问题可以减少约 16%的室外用水量,这对加州等干旱地区尤为重要。不要低估细节:一个滴水的水龙头每天可以浪费大约 15 升的水!总体而言,通过消除浪费,我们大约三分之一的直接用水量可以大幅削减。据估计,如果你每月节约大约 3500 升水,每年可以节省大约 140 美元。一个四口之家的用水量不到平均月用水量的 20%,如果你生活在一个家庭用水量很大的国家,比如美国,这个数字就更低了。
The water footprint is various items is certainly not intuitive and can be incredibly large. Data source.
家庭间接用水量
那么,为什么减少直接用水量不会显著影响环境呢?原因当然是其他因素造成了绝大部分的用水量。在全球范围内,我们每年使用大约 9 万亿立方米的水。一立方米的水等于 1000 升。你算算。与这些数字相比,家庭每天 150 升的用水量相形见绌。此外,大多数国家不像英国那样富裕,因此人均每天用水量较少。因此,从全球来看,这 9 万亿立方米中有惊人的 92%用于农业目的。因此,你吃的食物对全球用水量的影响比你直接用水的影响要大得多。让你看一下这个世界:一公斤牛肉的水足迹超过 15 000 升。对于一个英国公民来说,这超过了他们 3 个月的直接用水量。一根香蕉的间接用水量比普通英国公民每天的直接用水量还要多!
你有很好的理由来关心这个水足迹。通常,水足迹分为三类:蓝色(来自地表或地下水库的水)、绿色(来自降水的水)和灰色(用于稀释污染物的水)。农产品大多有绿色的蓝水足迹,蓝水每年占全球用水量的大约一万亿立方米。你可以把这想象成从土地上借走的贷款,然后以某种形式回到循环中。然而,如果你遇到流动性问题,一笔足够大的贷款可以让任何一家银行破产。因为在郁郁葱葱的风景中或在干旱的沙漠中,一升水肯定是不等价的。在某种程度上,这又回到了种植食物的争论,在那里你有种植食物的最佳条件。由于需要灌溉,在干燥的环境中种植番茄将会有更高的水足迹。
The three types of water footprint. Global usage of water is split into 11% blue, 74% green and 15% grey water.
那么,如果银行破产,到底会发生什么?如果我们从一个地区取了过多的水?一个后果是盐水侵入枯竭的地下水库,把它们变成不适合饮用的咸水。这在海岸线上经常可以看到。地中海沿岸的大片地区都受到这个问题的困扰,因为西班牙或意大利等国家的这些地区一开始就非常干燥,需要从地下水矿床中获得大量贷款。
记住这一点,让我们来谈谈此时此地你可以做些什么来管理你的水足迹。最普遍的一点当然是意识到间接用水,这比你实际的直接用水重要得多。注意你生活中那些水足迹过高的消费品。以牛肉为例。一个 200 克的牛肉汉堡馅饼会有大约 3000 升的水足迹,而同样数量的鸡肉只能携带大约四分之一的水分。就生产所需的水而言,用大豆制成的同等馅饼甚至会更“便宜”。如果你正在寻找的话,有一个支持素食主义的论点。但是即使你不知道,想想你的牛肉馅饼来自哪里,因为来自干旱地区的牛肉必然比来自多雨地区的牛肉有更大的水足迹。它不止于牛肉。基本上,你拥有或消费的所有东西都有水足迹,而且通常比你想象的要大得多。
如果你还不担心,考虑一下这个:根据估计,到 2030 年,全球淡水需求将超过供给 40%。更少的战争爆发了。一年中至少有一个月,水资源短缺已经影响了 T2 数十亿人口。为了对消费者行为产生真正的影响,法律应该要求公开某一特定商品的水足迹。就像卡路里的数量一样,这个数字将有助于功能相当的产品之间的比较,并使水足迹意识变得容易。在我们得到它之前,这里是 TL;博士:
如果你关心你的水/能源账单:减少你家庭的直接用水。如果你关心环境:通过有意识地消费低水足迹的物品来减少间接用水。
多臂土匪与强化学习
用 Python 例子温和地介绍了经典问题
多臂土匪问题是一些最简单的强化学习(RL)问题来解决。我们有一个代理,我们允许它选择行动,每个行动都有一个回报,这个回报是根据一个给定的、潜在的概率分布返回的。这个游戏有很多集(在这种情况下是单个动作),目标是最大化你的奖励。
一个简单的图片是考虑在k-许多 独臂强盗 (即老虎机)或一个带 k 手臂的大老虎机之间进行选择。你拉的每一只手臂都有不同的奖励。你有 1000 个 25 美分的硬币,所以你需要制定一些策略来获得最大的收益。
解决这个问题的一个方法是依次选择每一个,记录你得到了多少,然后继续回到付出最多的那个。这是可能的,但是,如前所述,每个 bandit 都有一个与之相关的潜在概率分布,这意味着在找到正确的样本之前,您可能需要更多的样本。但是,你每花一点时间去找出最好的强盗,都会让你失去最大的回报。这种基本的平衡行为被称为探索-利用困境。这个基本问题的形式出现在 AI 和 RL 之外的领域,例如在选择职业、金融、人类心理学,甚至医学伦理(尽管,我认为我最喜欢的提议用例与建议有关,由于其丰富性,它在二战期间被给予纳粹德国,“作为智力破坏的最终形式。”)。
TL;速度三角形定位法(dead reckoning)
我们按照萨顿和巴尔托的书的框架介绍了多臂强盗问题,并开发了一个解决这些问题的框架和例子。在本帖中,我们将关注ϵ−greedy、ϵ−decay 和乐观策略。一如既往,你可以在这里找到的原帖(适当支持 LaTeX)。
问题设置
首先,让我们用一点技术细节来描述这个问题。我们希望做的是开发一个估计值 Qt (a) :
其中 Qt(a) 是当在步骤 n 采取动作 An 时,估计的期望回报( Rn )。我们将反复构建一个模型,这个模型将向每个行为的真实价值靠拢。我们将对所有潜在的概率分布使用高斯(正态)分布,以便平均值对应于真实值(毕竟,给定足够的样本,我们会期望我们的奖励收敛于所选行动的平均值)。
最简单的方法是采取贪婪的行动或者采取我们认为会在每个时间点最大化我们的回报的行动。另一种写法是:
我们可以将这种最大期望或贪婪行为表示为 An* 。这是我们前面提到的探索-利用困境的利用方面,如果目标是最大化我们的回报,这就很有意义。当然,只有当我们对每个行动的预期回报有了一个很好的感觉时(除非我们相当幸运),重复这样做才会有好的效果。因此,我们需要找出一种算法来探索足够的搜索空间,以便我们可以采取最佳行动。
在开始之前,还有最后一个概念要介绍。在典型的 RL 应用中,我们可能需要数十万次迭代,甚至数百万次以上。运行此类模拟并跟踪所有数据来计算平均回报,很快就会变得非常计算密集型。为了避免这种情况,我们可以使用一个方便的公式,这样我们只需要跟踪两个值:平均值和所走的步数。如果我们需要计算步骤 n,m_n 的平均值,我们可以用前面的平均值m _ n-1和 n 来计算,如下所示:
有了这些,我们就可以开始制定解决 k 线问题的策略了。
ϵ-Greedy 方法
我们简单地讨论了一个纯粹贪婪的方法,我指出它本身不会很好地工作。考虑如果你实现一个纯贪婪的方法,你在 n=1 采取一个行动 A_n=a_1 并得到一个奖励。那么,这就成为了你的最高奖励(假设它是正的),你只需重复 a_1 ∀ n (对所有步骤 n 采取行动 a_1 )。为了鼓励一点探索,我们可以使用ϵ-greedy,这意味着我们探索另一个ϵ.概率的选项这给算法带来了一点噪音,以确保你继续尝试其他值,否则,你会继续利用你的最大回报。
让我们转向 Python 来实现我们的 k- 武装强盗。
建造一个贪婪的 k 臂强盗
我们将定义一个名为eps_bandit
的类来运行我们的实验。该类将臂数、k
、ε值eps
、迭代次数iter
作为输入。我们还将定义一个术语mu
,我们可以用它来调整每支队伍的平均奖励。
首先是模块:
# import modules
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
如果你在 Jupyter 之外建造这个,忽略%matplotlib inline
。
class eps_bandit:
'''
epsilon-greedy k-bandit problem
Inputs
=====================================================
k: number of arms (int)
eps: probability of random action 0 < eps < 1 (float)
iters: number of steps (int)
mu: set the average rewards for each of the k-arms.
Set to "random" for the rewards to be selected from
a normal distribution with mean = 0\.
Set to "sequence" for the means to be ordered from
0 to k-1.
Pass a list or array of length = k for user-defined
values.
'''
def __init__(self, k, eps, iters, mu='random'):
# Number of arms
self.k = k
# Search probability
self.eps = eps
# Number of iterations
self.iters = iters
# Step count
self.n = 0
# Step count for each arm
self.k_n = np.zeros(k)
# Total mean reward
self.mean_reward = 0
self.reward = np.zeros(iters)
# Mean reward for each arm
self.k_reward = np.zeros(k)
if type(mu) == list or type(mu).__module__ == np.__name__:
# User-defined averages
self.mu = np.array(mu)
elif mu == 'random':
# Draw means from probability distribution
self.mu = np.random.normal(0, 1, k)
elif mu == 'sequence':
# Increase the mean for each arm by one
self.mu = np.linspace(0, k-1, k)
def pull(self):
# Generate random number
p = np.random.rand()
if self.eps == 0 and self.n == 0:
a = np.random.choice(self.k)
elif p < self.eps:
# Randomly select an action
a = np.random.choice(self.k)
else:
# Take greedy action
a = np.argmax(self.k_reward)
reward = np.random.normal(self.mu[a], 1)
# Update counts
self.n += 1
self.k_n[a] += 1
# Update total
self.mean_reward = self.mean_reward + (
reward - self.mean_reward) / self.n
# Update results for a_k
self.k_reward[a] = self.k_reward[a] + (
reward - self.k_reward[a]) / self.k_n[a]
def run(self):
for i in range(self.iters):
self.pull()
self.reward[i] = self.mean_reward
def reset(self):
# Resets results while keeping settings
self.n = 0
self.k_n = np.zeros(k)
self.mean_reward = 0
self.reward = np.zeros(iters)
self.k_reward = np.zeros(k)
有很多不同的方法来定义这个类。我这样做是为了一旦我们初始化了我们的问题,我们只需调用run()
方法就可以检查输出。默认情况下,每只手臂的平均奖励来自 0 左右的正态分布。设置mu="sequence"
将使奖励范围从 0 到 k-1 ,以便在评估结果时了解哪些行动提供了最佳奖励,以及采取了哪些行动。最后,您还可以通过将值传递给mu
来设置自己的平均奖励。
让我们用不同的ϵ\epsilonϵ.值来做一些比较对于其中的每一个,我们将设置 k=10,每集运行 1000 步,运行 1000 集。每集之后,我们将重置盗匪,并复制不同盗匪的平均值,以保持事情的一致性。
k = 10
iters = 1000
eps_0_rewards = np.zeros(iters)
eps_01_rewards = np.zeros(iters)
eps_1_rewards = np.zeros(iters)
episodes = 1000
# Run experiments
for i in range(episodes):
# Initialize bandits
eps_0 = eps_bandit(k, 0, iters)
eps_01 = eps_bandit(k, 0.01, iters, eps_0.mu.copy())
eps_1 = eps_bandit(k, 0.1, iters, eps_0.mu.copy())
# Run experiments
eps_0.run()
eps_01.run()
eps_1.run()
# Update long-term averages
eps_0_rewards = eps_0_rewards + (
eps_0.reward - eps_0_rewards) / (i + 1)
eps_01_rewards = eps_01_rewards + (
eps_01.reward - eps_01_rewards) / (i + 1)
eps_1_rewards = eps_1_rewards + (
eps_1.reward - eps_1_rewards) / (i + 1)
plt.figure(figsize=(12,8))
plt.plot(eps_0_rewards, label="$\epsilon=0$ (greedy)")
plt.plot(eps_01_rewards, label="$\epsilon=0.01$")
plt.plot(eps_1_rewards, label="$\epsilon=0.1$")
plt.legend(bbox_to_anchor=(1.3, 0.5))
plt.xlabel("Iterations")
plt.ylabel("Average Reward")
plt.title("Average $\epsilon-greedy$ Rewards after " + str(episodes)
+ " Episodes")
plt.show()
从结果来看,贪婪函数在其他两个函数中表现一致,ϵ=0.01 在这两个函数之间,ϵ=0.1 在这三个函数中表现最好。下面,我们可以看到使用sequence
参数的效果更加清晰,并且可以感受到每集采取最佳行动的频率,因为每集的平均值保持一致。
k = 10
iters = 1000
eps_0_rewards = np.zeros(iters)
eps_01_rewards = np.zeros(iters)
eps_1_rewards = np.zeros(iters)
eps_0_selection = np.zeros(k)
eps_01_selection = np.zeros(k)
eps_1_selection = np.zeros(k)
episodes = 1000
# Run experiments
for i in range(episodes):
# Initialize bandits
eps_0 = eps_bandit(k, 0, iters, mu='sequence')
eps_01 = eps_bandit(k, 0.01, iters, eps_0.mu.copy())
eps_1 = eps_bandit(k, 0.1, iters, eps_0.mu.copy())
# Run experiments
eps_0.run()
eps_01.run()
eps_1.run()
# Update long-term averages
eps_0_rewards = eps_0_rewards + (
eps_0.reward - eps_0_rewards) / (i + 1)
eps_01_rewards = eps_01_rewards + (
eps_01.reward - eps_01_rewards) / (i + 1)
eps_1_rewards = eps_1_rewards + (
eps_1.reward - eps_1_rewards) / (i + 1)
# Average actions per episode
eps_0_selection = eps_0_selection + (
eps_0.k_n - eps_0_selection) / (i + 1)
eps_01_selection = eps_01_selection + (
eps_01.k_n - eps_01_selection) / (i + 1)
eps_1_selection = eps_1_selection + (
eps_1.k_n - eps_1_selection) / (i + 1)
plt.figure(figsize=(12,8))
plt.plot(eps_0_rewards, label="$\epsilon=0$ (greedy)")
plt.plot(eps_01_rewards, label="$\epsilon=0.01$")
plt.plot(eps_1_rewards, label="$\epsilon=0.1$")
for i in range(k):
plt.hlines(eps_0.mu[i], xmin=0,
xmax=iters, alpha=0.5,
linestyle="--")
plt.legend(bbox_to_anchor=(1.3, 0.5))
plt.xlabel("Iterations")
plt.ylabel("Average Reward")
plt.title("Average $\epsilon-greedy$ Rewards after " +
str(episodes) + " Episodes")
plt.show()
bins = np.linspace(0, k-1, k)
plt.figure(figsize=(12,8))
plt.bar(bins, eps_0_selection,
width = 0.33, color='b',
label="$\epsilon=0$")
plt.bar(bins+0.33, eps_01_selection,
width=0.33, color='g',
label="$\epsilon=0.01$")
plt.bar(bins+0.66, eps_1_selection,
width=0.33, color='r',
label="$\epsilon=0.1$")
plt.legend(bbox_to_anchor=(1.2, 0.5))
plt.xlim([0,k])
plt.title("Actions Selected by Each Algorithm")
plt.xlabel("Action")
plt.ylabel("Number of Actions Taken")
plt.show()
opt_per = np.array([eps_0_selection, eps_01_selection,
eps_1_selection]) / iters * 100
df = pd.DataFrame(opt_per, index=['$\epsilon=0$',
'$\epsilon=0.01$', '$\epsilon=0.1$'],
columns=["a = " + str(x) for x in range(0, k)])
print("Percentage of actions selected:")
df
查看算法的平均选择,我们看到为什么较大的ϵ值表现良好,它采取最佳选择 80%的时间。
试验ϵ和 k 的不同值,看看这些结果如何变化。例如,缩小搜索空间可能有利于较小的ϵ值,因为勘探的益处较少,反之亦然。此外,增加迭代次数将开始有利于较低的ϵ值,因为它将具有较少的随机噪声。
ϵ-Decay 战略
ϵ-greedy 策略有一个明显的弱点,那就是无论他们看到多少例子,他们都会继续包含随机噪声。对这些人来说,最好确定一个最佳解决方案,并继续利用它。为此,我们可以引入ϵ-decay,它减少了每一步探索的概率。其工作原理是将ϵ定义为步数的函数 n 。
其中β <1 is introduced as a scaling factor to reduce the scaling rate so that the algorithm has sufficient opportunity to explore. In this case, we also include +1 in the denominator to prevent infinities from appearing. Given this, we can make a few small changes to our previous class of bandits to define an 【 class that works on the same principles.
class eps_decay_bandit:
'''
epsilon-decay k-bandit problem
Inputs
=====================================================
k: number of arms (int)
iters: number of steps (int)
mu: set the average rewards for each of the k-arms.
Set to "random" for the rewards to be selected from
a normal distribution with mean = 0\.
Set to "sequence" for the means to be ordered from
0 to k-1.
Pass a list or array of length = k for user-defined
values.
'''
def __init__(self, k, iters, mu='random'):
# Number of arms
self.k = k
# Number of iterations
self.iters = iters
# Step count
self.n = 0
# Step count for each arm
self.k_n = np.zeros(k)
# Total mean reward
self.mean_reward = 0
self.reward = np.zeros(iters)
# Mean reward for each arm
self.k_reward = np.zeros(k)
if type(mu) == list or type(mu).__module__ == np.__name__:
# User-defined averages
self.mu = np.array(mu)
elif mu == 'random':
# Draw means from probability distribution
self.mu = np.random.normal(0, 1, k)
elif mu == 'sequence':
# Increase the mean for each arm by one
self.mu = np.linspace(0, k-1, k)
def pull(self):
# Generate random number
p = np.random.rand()
if p < 1 / (1 + self.n / self.k):
# Randomly select an action
a = np.random.choice(self.k)
else:
# Take greedy action
a = np.argmax(self.k_reward)
reward = np.random.normal(self.mu[a], 1)
# Update counts
self.n += 1
self.k_n[a] += 1
# Update total
self.mean_reward = self.mean_reward + (
reward - self.mean_reward) / self.n
# Update results for a_k
self.k_reward[a] = self.k_reward[a] + (
reward - self.k_reward[a]) / self.k_n[a]
def run(self):
for i in range(self.iters):
self.pull()
self.reward[i] = self.mean_reward
def reset(self):
# Resets results while keeping settings
self.n = 0
self.k_n = np.zeros(k)
self.mean_reward = 0
self.reward = np.zeros(iters)
self.k_reward = np.zeros(k)
Now running the code:
k = 10
iters = 1000eps_decay_rewards = np.zeros(iters)
eps_1_rewards = np.zeros(iters)episodes = 1000
# Run experiments
for i in range(episodes):
# Initialize bandits
eps_decay = eps_decay_bandit(k, iters)
eps_1 = eps_bandit(k, 0.1, iters, eps_decay.mu.copy())
# Run experiments
eps_decay.run()
eps_1.run()
# Update long-term averages
eps_decay_rewards = eps_decay_rewards + (
eps_decay.reward - eps_decay_rewards) / (i + 1)
eps_1_rewards = eps_1_rewards + (
eps_1.reward - eps_1_rewards) / (i + 1)
plt.figure(figsize=(12,8))
plt.plot(eps_decay_rewards, label="$\epsilon-decay$")
plt.plot(eps_1_rewards, label="$\epsilon=0.1$")
plt.legend(bbox_to_anchor=(1.2, 0.5))
plt.xlabel("Iterations")
plt.ylabel("Average Reward")
plt.title("Average $\epsilon-decay$ and" +
"$\epsilon-greedy$ Rewards after "
+ str(episodes) + " Episodes")
plt.show()
The ϵ-decay strategy outperforms our previous best algorithm as it sticks to the optimal action once it is found.
There’s one last method to balance the explore-exploit dilemma in k-bandit 问题,乐观初始值。
乐观初始值
这种方法与我们之前探索的例子有很大不同,因为它没有引入随机噪声来寻找最佳动作, A_n* 。相反,我们高估了所有行为的回报,并不断选择最大值。在这种情况下,算法会在早期探索,寻求最大化其回报,同时附加信息允许值收敛到其真实均值。这种方法确实需要一些额外的背景知识,因为我们至少需要一些回报的概念,这样我们才能高估它们。
对于这个实现,我们不需要新的类。相反,我们可以简单地使用我们的eps_bandit
类,设置ϵ=0,并为估计值提供高的初始值。此外,我喜欢将每个臂的拉计数初始化为 1,而不是 0,以鼓励稍微慢一些的收敛,并确保良好的探索。
k = 10
iters = 1000oiv_rewards = np.zeros(iters)
eps_decay_rewards = np.zeros(iters)
eps_1_rewards = np.zeros(iters)# Select initial values
oiv_init = np.repeat(5., k)episodes = 1000
# Run experiments
for i in range(episodes):
# Initialize bandits
oiv_bandit = eps_bandit(k, 0, iters)
oiv_bandit.k_reward = oiv_init.copy()
oiv_bandit.k_n = np.ones(k)
eps_decay = eps_decay_bandit(k, iters, oiv_bandit.mu.copy())
eps_1 = eps_bandit(k, 0.1, iters, oiv_bandit.mu.copy())
# Run experiments
oiv_bandit.run()
eps_decay.run()
eps_1.run()
# Update long-term averages
oiv_rewards = oiv_rewards + (
oiv_bandit.reward - oiv_rewards) / (i + 1)
eps_decay_rewards = eps_decay_rewards + (
eps_decay.reward - eps_decay_rewards) / (i + 1)
eps_1_rewards = eps_1_rewards + (
eps_1.reward - eps_1_rewards) / (i + 1)
plt.figure(figsize=(12,8))
plt.plot(oiv_rewards, label="Optimistic")
plt.plot(eps_decay_rewards, label="$\epsilon-decay$")
plt.plot(eps_1_rewards, label="$\epsilon=0.1$")
plt.legend(bbox_to_anchor=(1.2, 0.5))
plt.xlabel("Iterations")
plt.ylabel("Average Reward")
plt.title("Average Bandit Strategy Rewards after " +
str(episodes) + " Episodes")
plt.show()
我们可以看到,在这种情况下,乐观初始值方法优于我们的ϵ−greedy 和ϵ−decay 算法。我们也可以看到,在最后一集里,算法对每个手臂的估计。
df = pd.DataFrame({"number of selections": oiv_bandit.k_n - 1,
"actual reward": oiv_bandit.mu,
"estimated reward": oiv_bandit.k_reward})
df = df.applymap(lambda x: np.round(x, 2))
df['number of selections'] = df['number of selections'].astype('int')
df
除了 977 次拉动之外,所有情况下的估计都与实际回报相差甚远。这突出了我们将在更普遍的强化学习中做的许多事情。我们不一定关心获得我们正在互动的环境的准确描述。相反,我们打算学习在这些情况下的最佳行为,并寻求相应的行为。这将开启一场关于无模型学习和基于模型学习的讨论,我们将不得不推迟到下一次。
还有其他土匪方法我们将探讨,如梯度土匪,上限置信(UCB)方法,和非平稳问题。此外,还有许多其他类似决斗土匪、集群土匪、协同过滤土匪、空间相关土匪、分布式土匪、对抗性土匪、上下文土匪的土匪,这些都是值得探索的。开始怀疑我们是否没有遭到智力破坏……
多阶层失衡
Made this in Microsoft Paint
因此,在我的第一篇文章中,我正在解决我在公司从事第一个数据科学项目时遇到的一个问题。我在 ALC 创新交通解决方案公司担任机器学习研究员,我的问题出现在一个预测建模项目中。多阶层失衡。我以前在课堂项目中遇到过班级不平衡的问题,并且使用过 ROSE 软件包,但是我从来没有接触过多班级不平衡的问题。
Google Images: Binary class imbalance. I’ve dealt with binary class imbalance before and there are plenty of tools and articles about tackling this common data issue.
二进制类不平衡是数据科学中一个常见的头痛问题,但可以很容易地解决(关于它的伟大文章:https://machine learning mastery . com/tactics-to-combat-balanced-classes-in-your-machine-learning-dataset/)。不幸的是,对于我手头的这个项目,没有太多的资源。我想分享我克服多阶级不平衡的思考过程,这样其他人可能会发现它对调解他们自己的分配冲突有用。
分销
我公司的数据不开放共享,但以下是我所面临问题的模拟版本:
这种跨类的数据分布是完全不平衡的。这很糟糕,因为我的项目的目标是建立一个多类分类器,它可以正确地分配一个数据点属于哪个“类”。具体来说,这种不平衡是一个问题,因为无论我最终采用哪种预测模型,都将偏向第 1 类,尽管程度较小,但仍然偏向第 2 类。由于不平衡,通过将大部分训练和测试集分类为类 1 或类 2,它将获得相当好的准确度;这就是“准确性悖论”。我的模型可能在分类上达到很好的准确性,但那是因为模型只会模拟不平衡的分布。
Google Images: Confusion Matrix. If a ML model trains on an imbalanced data set it will over-classify towards the majority class. In the image, for reference, the model would predict all classes to be ‘P’ while some of those should have been ‘N.’ In the case of multi-class imbalance the effects would be even more drastic where the model would predict ‘P’ (because it’s the majority class in this example) when the actual class was ’N’ or ‘O’ or ‘M’ etc.
在我的实际工作项目中,类之间的区别特征很少,这一事实使得这个问题更加复杂。它们的经度/纬度特征只有微小的差异。当然,还有其他预测功能,但地理空间数据构成了该模型预测和解释能力方面的主体。然而,这种类别的差异对公司和模型的决策非常重要,因此必须解决这个问题。
Google Images: Imbalanced Classifier. This image is an example of how an imbalanced data set would create an ‘accurate’ classifier that, in production, would really be a weak classification model hiding behind the guise of ‘High Accuracy.’
玫瑰
ROSE,或者说随机过采样实验,是一个非常棒的 R 包,可以很好地处理类不平衡,但是只能处理二进制类不平衡(两个类)。我如何使用这个包来修复我正在查看的多类数据?
在反复试验、研究各种选项并给我的一位优秀教授发了一封电子邮件之后,我确定了最佳行动方案:编写一个函数,该函数采用整个数据集,使用“Class”作为划分特征将其划分为 6 个子集,然后使用 ROSE 将这些子集平衡到我想要的分布。然后它们将被重新编译成一个合理平衡的数据集。每个子集包含 Class1 和一个少数类。第二类被排除在外,因为它没有被代表。
然后将子集赋予 ROSE 自变量,以对少数类进行过采样。这样做的 ROSE 代码可以在下面看到。将少数类的身份用作公式,参数“p”是对稀有类进行采样的概率,另一种方法是将 N 设置为数据集的所需大小。下面,当 p=0.5 时,这些参数返回一个数据集,其中少数民族类现在占数据的 50%。
library(ROSE)BalancedData <- ovun.sample(MinorityClassi~, ImbalancedData, method="over", p=0.5, subset=options("subset")$subset, na.action=options("na.action")$na.action, seed)index = createDataPartition(y=BalancedData$Class, p=0.7, list=FALSE)
train = BalancedData[index,]
test = BalancedData[-index,]BalTrain <- droplevels.data.frame(train)
BalTest <- droplevels.data.frame(test)
在对少数类进行过采样并将分布从不平衡的地狱中带出来之后,我通过我的分类器运行了这个平衡的数据集。最佳模型是随机森林(与作为基线的逻辑回归模型、基尼标准决策树、信息增益决策树和神经网络相比)。结果很好,但在测试集上就没那么好了。为什么?因为过采样的少数类数据不是新的数据点。所以这个模型无法设想新的点能够适合那个类。
我考虑使用 SMOTE,合成少数过采样技术,但它的结果充其量可以忽略不计。我决定使用 ROSE 包中的 ROSE 公式:“通过扩大少数类和多数类示例的特征空间来创建合成数据的样本”(cran . r-project . org/web/packages/ROSE/ROSE . pdf)。
SynthBalData <- ROSE(MinorityClassi~, ImbalancedData, p=0.5, hmult.majo=1, hmult.mino=1, subset=options("subset")$subset, na.action=options("na.action")$na.action, seed)index = createDataPartition(y=SynthBalData$Class, p=0.7, list=FALSE)
SynthBalTrain = SynthBalData[index,]
SynthBalTest = SynthBalData[-index,]SynthBalTrain <- droplevels.data.frame(SynthBalTrain)
SynthBalTest <- droplevels.data.frame(SynthBalTest)
通过坚持我最初的子集创建方法,并应用 ROSE,我得到了合成但平衡的数据样本,并编译了一个新的数据集。我用一个简单的随机样本对数据进行 70:30 的训练/测试分割来训练所有的模型。精确度很高,模型对来自我公司服务器的新数据点反应很好。
Multi-Class imbalance, initially challenging, ultimately defeated. This is a cluster plot showing the imbalanced data now proportioned into even clusters. My next article will focus on modelling geo-spaital data as seen above.
多类度量变得简单,第一部分:精度和召回率
多类分类中精度和召回率的性能测量可能有点——或者非常——令人困惑,所以在这篇文章中,我将解释精度和召回率是如何使用的,以及它们是如何计算的。其实挺简单的!但是首先,让我们快速回顾一下二进制分类的精度和召回率。(还有第二部分:F1 分数,但我建议你从第一部分开始)。
在二元分类中,我们通常有两个类别,通常称为正类和负类,我们试图预测每个样本的类别。让我们看一个简单的例子:我们的数据是一组图像,其中一些包含一只狗。我们对检测有狗的照片感兴趣。在这种情况下,我们的正类是所有狗的照片的类,负类包括所有其他照片。换句话说,如果样本照片包含一只狗,它就是一个积极的。如果不是,那就是否定的。我们的分类器预测,对于每张照片,如果它是正面的§或负面的(N):照片中有狗吗?
给定一个分类器,我发现考虑分类器性能的最好方法是使用所谓的“混淆矩阵”。对于二元分类,混淆矩阵具有两行和两列,并显示有多少阳性样本被预测为阳性或阴性(第一列),以及有多少阴性照片被预测为阳性或阴性(第二列)。因此,它总共有 4 个单元。每当我们的分类器做出一个预测,表中的一个单元格就加 1。在过程结束时,我们可以确切地看到我们的分类器是如何执行的(当然,只有当我们的测试数据被标记时,我们才能做到这一点)。
这里有一个简单的例子。假设我们有 10 张照片,其中正好有 7 张有狗。如果我们的分类器是完美的,混淆矩阵应该是这样的:
我们完美的分类器没有产生任何错误。所有的正面照片被归类为正面,所有的负面照片被归类为负面。
然而,在现实世界中,分类器会出错。二元分类器会产生两种错误:一些正样本被分类为负样本;一些阴性样本被归类为阳性。让我们来看一个来自更现实的分类器的混淆矩阵:
在这个例子中,2 张有狗的照片被归类为负面的(没有狗!),还有 1 张不带狗的照片被归为正面(狗!).
当一个阳性样本被错误地归类为阴性,我们称之为假阴性(FN)。同样,当一个阴性样本被错误地归类为阳性样本时,它被称为假阳性。下面我们复制混淆矩阵,但是添加 TP、FP、FN 和 TN 来指定真阳性、假位置、假阴性和真负值:
既然我们已经掌握了混淆矩阵和各种数字,我们可以开始查看性能指标:我们的分类器有多好?(在我们内心深处,我们总是需要记住,“好”可以有不同的含义,这取决于我们需要解决的实际现实问题。)
让我们从精度开始,它回答了下面的问题:预测阳性真正为阳性的比例是多少?我们需要查看预测阳性的总数(真阳性加上假阳性,TP+FP),看看其中有多少是真阳性(TP)。在我们的例子中,5+1=6 张照片被预测为阳性,但其中只有 5 张是真阳性。在我们的例子中,精度是 5/(5+1)= 83.3% 。一般来说,精度是 TP/(TP+FP)。注意 TP+FP 是第一行的和。
另一个非常有用的衡量标准是召回,它回答了一个不同的问题:有多少比例的实际阳性被正确分类?查看表格,我们看到实际阳性的数量是 2+5=7 (TP+FN)。在这 7 张照片中,有 5 张被预测为阳性。召回率因此是 5/7 = 71.4% 。一般来说,召回是 TP/(TP+FN)。注意 TP+FN 是第列的和。
人们也可能对准确性感兴趣:有多少比例的照片——包括正面的和负面的——被正确分类?在我们的例子中,总共 10 张照片中有 5+2=7 张被正确分类。因此,准确率为 70.0%。一般来说,在 TP+TN+FP+FN 张照片中,总共有 TP+TN 张正确分类的照片,因此准确度的一般公式为(TP+TN)/(TP+TN+FP+FN)。
精准和召回哪个更重要?这个真的要看你具体的分类问题了。例如,想象一下,你的分类器需要检测人类患者中的糖尿病。“阳性”表示患者患有糖尿病。“阴性”意味着患者是健康的。(我知道,很混乱。但那是医学术语!).在这种情况下,您可能希望确保您的分类器具有高召回率,以便正确检测尽可能多的糖尿病患者。再举一个例子——假设你正在构建一个视频推荐系统,你的分类器对相关视频的预测是肯定的,对不相关视频的预测是否定的。你要确保几乎所有的推荐视频都与用户相关,所以你要高精度。生活中充满了取舍,分类器也是如此。在良好的精确度和良好的召回率之间通常有一个平衡。你通常不能两者兼得。
我们的狗的例子是一个二元分类问题。二分类问题通常集中在我们想要检测的一个正类上。相反,在典型的多类分类问题中,我们需要将每个样本分为 N 个不同类中的一个。回到我们的照片例子,想象现在我们有一个照片集合。每张照片展示一种动物:要么是一只猫,一条鱼,要么是一只母鸡。我们的分类器需要预测每张照片中显示的是哪种动物。这是一个 N=3 类的分类问题。
让我们来看看对 25 张照片进行分类后产生的混淆矩阵示例:
类似于我们的二进制情况,我们可以为每个类定义精度和召回率。例如,猫类的精度是所有预测的猫照片(4+3+6=13)中正确预测的猫照片(4)的数量,总计 4/13=30.8%。因此,只有大约 1/3 的照片,我们的预测分类为猫实际上是猫!
另一方面,猫的召回是实际猫照片数(4+1+1=6)中正确预测的猫照片数(4),即 4/6=66.7%。这意味着我们的分类器将 2/3 的猫照片归类为猫。
以类似的方式,我们可以计算另外两个类的精度和召回率:鱼和母鸡。对于鱼类,这两个数字分别为 66.7%和 20.0%。对于 Hen 来说,准确率和召回率都是 66.7%。继续验证这些结果。你可以用下面两张图片来帮助你。
在 Python 的 scikit-learn 库(也称为 sklearn )中,您可以轻松计算多类分类器中每个类的精度和召回率。这里使用的一个方便的函数是sk learn . metrics . class ification _ report。
下面是一些使用我们的猫/鱼/母鸡例子的代码。我首先创建了一个包含图像真实类别(y_true)和预测类别(y_pred)的列表。通常使用分类器生成 y _ pred 这里我手动设置它的值以匹配混淆矩阵。
在第 14 行,打印混淆矩阵,然后在第 17 行,打印三个类的精度和召回率。
这是输出。注意混淆矩阵在这里被调换了——这就是 sklearn 的工作方式。注意支持列:它列出了每类的样本数量(6 个用于猫,10 个用于鱼,等等)。
classification_report 还报告其他指标(例如,F1 分数)。在接下来的帖子中,我会解释多类情况下的 F1 分数,以及为什么不应该使用它:)
希望你觉得这篇文章有用并且容易理解!
简化多类度量,第二部分:F1 分数
在 的 第一部分 中,多类度量做了简单的 ,我解释了精度和召回,以及如何为一个多类分类器计算它们。在这篇文章中,我将解释另一个流行的绩效衡量指标,F1 得分,或者更确切地说 F1 得分,因为至少有 3 个变量。我将解释为什么使用 F1 分数,以及如何在多班级设置中计算 F1 分数。
但是首先,一个**大警告:**F1-分数被广泛用作一种度量,但通常是比较分类器的错误方式。你经常会在学术论文中发现他们,研究人员使用较高的 F1 分数作为他们的模型比分数较低的模型更好的“证据”。然而,更高的 F1 分数并不一定意味着更好的分类器。慎用,对 F1 成绩半信半疑!稍后将详细介绍。
如同在第一部分中一样,我将从一个简单的二进制分类设置开始。提醒一下:这是使用我们的二元分类器为狗狗照片生成的混淆矩阵。我们在上一部分计算的准确率和召回率分别为 83.3%和 71.4%。
一般来说,我们更喜欢具有更高精度和召回分数的分类器。然而,在精确度和召回率之间有一个权衡:当调整一个分类器时,提高精确度分数通常会导致降低召回率分数,反之亦然——没有免费的午餐。
现在想象你有两个分类器——分类器 A 和分类器 B——每个都有自己的精度和召回率。一个具有更好的回忆分数,另一个具有更好的精确度。我们想谈谈它们的相对性能。换句话说,我们希望将模型的性能总结成一个单一的指标。这就是 F1 分数的用途。这是一种将精确度和召回率结合成一个数字的方法。f1-分数是使用一个平均值(“平均值”)计算的,而不是通常的算术平均值。它使用谐波平均值,该平均值由以下简单公式给出:
F1-score = 2 × (精度 × 召回)/(精度+召回)
在上面的例子中,我们的二元分类器的 F1 分数是:
f1-得分= 2×(83.3%×71.4%)/(83.3%+71.4%)= 76.9%
类似于算术平均值,F1 分数总是介于精确度和召回率之间。但它的表现有所不同:F1 分数给予较低的数字更大的权重。例如,当精确度为 100%,召回率为 0%时,F1 分数将为 0%,而不是 50%。或者比如说分类器 A 的精度=召回率=80%,而分类器 B 的精度=60%,召回率=100%。从算术上来说,两种模型的精确度和召回率的平均值是相同的。但是当我们使用 F1 的调和平均公式时,分类器 A 的得分将为 80%,而分类器 B 的得分将仅为 75%。车型 B 的低精度得分拉低了其 F1 得分。
现在我们知道了如何计算二进制分类器的 F1 值,让我们回到第一部分中的多类示例。快速提醒:我们有 3 个类(猫、鱼、母鸡)和对应的分类器混淆矩阵:
我们现在要计算 F1 的分数。我们如何做到这一点?
记住 F1 分数是精确度和回忆的函数。在第一部分中,我们已经学习了如何计算每类的精度和召回率。下面是我们三个类的精度和召回率的总结:
有了上面的公式,我们现在可以计算每个级别的 F1 分数。例如,卡特彼勒的 F1 分数为:
f1-得分(Cat)= 2×(30.8%×66.7%)/(30.8%+66.7%)= 42.1%
鱼和母鸡也是如此。我们现在已经有了完整的每级 F1 分数:
下一步是将每个类别的 F1 分数合并成一个数字,即分类器的总体 F1 分数。有几种方法可以做到这一点。让我们从最简单的开始:每个班级 F1 分数的算术平均值。这被称为宏观平均 F1 分数,或简称为宏观 F1 分数,并被计算为我们每个级别 F1 分数的简单算术平均值:
宏 F1 =(42.1%+30.8%+66.7%)/3 = 46.5%
同理,我们也可以计算出的宏观平均精度和的宏观平均召回率:
宏精度= (31% + 67% + 67%) / 3 = 54.7%
宏观召回率= (67% + 20% + 67%) / 3 = 51.1%
(2019 . 8 . 20:我刚发现不止一个宏-F1 指标!上面描述的宏 F1 是最常用的,但是请看我的帖子两个宏 F1 的故事
当对宏 F1 取平均值时,我们给了每个类相同的权重。我们不需要这样做:在加权平均 F1 分数或加权 F1 分数中,我们根据每个类别的样本数量对该类别的 F1 分数进行加权。在我们的例子中,我们总共有 25 个样本:6 只猫、10 条鱼和 9 只母鸡。加权 F1 分数的计算方法如下:
加权-F1 =(6×42.1%+10×30.8%+9×66.7%)/25 = 46.4%
类似地,我们可以计算加权精度和加权召回率:
加权精度=(6×30.8%+10×66.7%+9×66.7%)/25 = 58.1%
加权召回=(6×66.7%+10×20.0%+9×66.7%)/25 = 48.0%
最后一个变体是微平均 F1 分数,或微 F1 。为了计算微 F1,我们首先计算所有样本的微平均精度和微平均召回*,然后将两者结合。我们如何「微平均」?我们只是一起看所有的样本。请记住,precision 是预测阳性中真实阳性的比例(TP/(TP+FP))。在多类情况下,我们认为所有正确预测的样本都是真阳性。让我们再次看看我们的困惑矩阵:*
有 4+2+6 个样本被正确预测(沿对角线的绿色单元),总共 TP=12。我们现在需要计算假阳性的数量。因为我们一起查看所有的类,所以对于被预测的类,每个预测错误都是假阳性。例如,如果猫样本被预测为鱼,则该样本对于鱼是假阳性。因此,假阳性的总数就是预测错误的总数,我们可以通过对所有非对角线单元格(即粉色单元格)求和来找到该总数。在我们的例子中,这是 FP=6+3+1+0+1+2=13。因此,我们的精度是 12/(12+13)= 48.0%。
On to recall,这是实际阳性中真阳性的比例(TP/(TP+FN))。TP 和之前一样:4+2+6=12。我们如何计算假阴性的数量?以我们之前的例子为例,如果一个猫样本被预测为鱼,则该样本对于猫是假阴性的。更广泛地说,每个预测错误(X 被误分类为 Y)对于 Y 是假阳性,对于 X 是假阴性。因此,假阴性的总数再次是预测错误(即,粉色单元格)的总数,因此召回率与精确度相同:48.0%。
因为在微平均的情况下,精度=召回,所以它们也等于它们的调和平均值。换句话说,在微型 F1 赛事中:
微 F1 = 微精密 = 微召回
*此外,这也是分类器的整体**准确度:正确分类的样本占所有样本的比例。*总而言之,以下内容始终适用于微型 F1 案例:
微 F1 = 微精度 = 微召回=精度
我之前提到过 F1 分数应该小心使用。为什么?尽管它们确实便于进行快速、高水平的比较,但它们的主要缺陷是对精确度和召回率给予了同等的重视。正如著名的统计学家戴维·汉德向解释的那样,“赋予精确度和召回率的相对重要性应该是问题的一个方面”。将病人归类为健康人与将健康人归类为病人具有不同的成本,这应该反映在使用权重和成本为您试图解决的特定问题选择最佳分类器的方式中。这对于二进制分类器来说是真实的,并且当计算多类 F1 分数,例如宏观、加权或微观 F1 分数时,问题变得复杂。在多类情况下,不同的预测误差有不同的含义。将 X 预测为 Y 可能与将 Z 预测为 W 有不同的成本,依此类推。标准 F1 分数不考虑任何领域知识。
最后,让我们再次看看我们的脚本和 Python 的 sk-learn 输出。
这也是脚本的输出。下面两行显示的是宏观平均和加权平均精度、召回率和 F1 分数。还计算了准确度(48.0%),其等于 micro-F1 分数。
这就结束了我对多类度量的两部分简短介绍。
我希望这些帖子对你有用。
多类度量变得简单,第三部分:Kappa 分数(又名科恩的 Kappa 系数)
韵律学
衡量预测值和真实值之间的一致性
在我之前的多类度量简单的帖子中,我写了关于精度和召回,以及 F1 分数。我从许多读者那里收到了令人鼓舞的反馈。所以首先谢谢大家!在这篇文章中,我写了另一个流行的衡量标准:kappa 评分。你可能会发现 kappa 分数对你的申请很有用。
kappa 分数是一个有趣的指标。它起源于心理学领域:在对受试者(患者)进行评分时,用于测量两个人评价者或评分者(如心理学家)之间的一致度。它后来被机器学习社区“挪用”来衡量分类性能。也被称为科恩的卡帕系数,卡帕分数是以雅各布·科恩的名字命名的,雅各布·科恩是一位美国统计学家和心理学家,他写了关于这个主题的开创性论文。这一指标的其他名称包括科恩的 kappa 和 kappa 统计。在这个帖子里,我简单的用 kappa 评分。
我首先解释什么是一致性以及 kappa 评分如何用于衡量一致性,以提供这一指标背后的直觉。然后我继续分类。
什么是协议?
想象一下,我们在一所著名的音乐学校(出于某种原因,我想到了茱莉亚音乐学院)管理钢琴系。现在是招生季节,25 名紧张的候选人准备在漂亮的斯坦威三角钢琴上展示他们的技能。我们请两位经验丰富的教授——被称为 A 教授和 B 教授——给每位候选人打分,看他们是接受、等待名单(WL)、还是拒绝。
以下是 25 位候选人的试镜结果:
每个候选人现在有两个评级,每个教授一个。自然,教授们对一些候选人意见一致,对另一些人意见不一。教授之间的高度一致增加了我们对评级可靠性的信心。低水平的一致意味着我们不能相信评级。kappa 分数衡量两个评估者之间的一致程度,也称为评分者间可靠性。
为了计算 kappa 分数,首先在矩阵中总结评级是方便的:
列显示教授 a 的评分。行显示教授 b 的评分。每个单元格中的值是两位教授相应评分的候选人数量。
衡量一致程度的一个简单方法是看两位教授之间的一致评级比例。姑且称这个数字为 同意 。计算 同意 很简单:我们只需要查看绿色单元格(对角线)中值的总和,然后除以学生总数:在我们的例子中,25 个评级中有 4+2+6=12 个是一致的,所以:
同意 =12/25 =0.48
就是这样!我们刚刚找到了达成一致的完美标准。或者我们有吗?嗯,我们的衡量标准并不复杂,因为它没有考虑到协议也可能是偶然发生的。这就是 kappa 分数的由来;kappa 评分考虑协议比偶然协议好多少。由此可见,除了 同意 之外,卡帕公式还使用了机会协议的预期比例;姑且称这个数字为chance agree***。*
我们已经知道如何计算 同意 。目前,让我们假设我们知道 ChanceAgree 的值。我们将这两个数字结合起来计算 kappa 分数,如下所示
KappaScore =(Agree-chance Agree)/(1-chance Agree)
注意,分子计算的是 同意和机会同意之间的差值。** 如果 一致 = 1,我们有完美一致,对应一个矩阵,其中所有非对角线(粉色)的单元格都是 0。在这种情况下,kappa 值为 1,不考虑 ChanceAgree 。相比之下,如果Agree = chance Agree,kappa 为 0,表示教授们的约定是偶然的。如果 同意 小于 偶然同意 ,则 kappa 值为负,表示同意程度低于偶然同意*。*
回到我们的例子:在我们的例子中(如我们不久前计算的那样), ChanceAgree 是 0.3024。换句话说,我们预计约 30%的协议是偶然发生的,也就是说约 7-8 名学生(25 名学生中的 30%)。教授们同意 25 名学生中的 12 名,因此 kappa 分数为正:
KappaScore =(同意-机会同意)/(1-机会同意)
=(0.48–0.3024)/(1–0.3024)
= 0.2546
在下一节中,我将解释 ChanceAgree 是如何计算的。
为了计算 ChanceAgree ,我们首先来看看每位教授将学生评为 Accept 的概率。我们再来看看评级矩阵;为了方便起见,我们添加了行和列的总计:
对于 A 教授来说,25 个评分中 4+1+1=6 被接受;对于 B 教授来说,这个数字是 4+6+3=13。因此,对于教授 A 和 B,将学生评定为接受的概率为:
ProbA(接受)= 6/25=0.24
ProbB(接受)=13/25=0.52
两位教授偶然同意一个接受 的概率等于 ProbA 和 ProbB 的乘积:
*chance agree(Accept)= ProbA(Accept)×ProbB(Accept)= 0.24×0.52 =*0.1248
同样,我们计算 WL 和拒绝的同意概率:
*普罗巴(WL)=10/25=0.4
普罗布(WL)=3/25=0.12
钱斯·阿科雷(WL)=普罗巴(WL)×普罗布(WL)= 0.4×0.12 =*0.048*ProbA(Reject)= 9/25 = 0.36
ProbB(Reject)= 9/25 = 0.36
chance agree(Reject)= ProbA(Reject)×ProbB(Reject)= 0.36×0.36 =*0.1188
将上述三种概率相加,我们得到协议中任何一个事件——接受、、拒绝——偶然发生的概率:
*机会同意=
=机会同意(接受)+机会同意(WL)+机会同意(拒绝)
= 0.1248+0.0480+0.1188 =*0.3024
就是这样!我们学习了如何计算 机会一致 。结合 同意 ,我们现在可以使用 kappa 评分来衡量同意程度。但是我们如何用它来衡量分类性能呢?
使用 Kappa 评分进行分类
要了解 kappa 评分如何用作分类器性能的衡量标准,让我们再次查看评级矩阵:
嗯,你可能已经猜到了:它非常类似于一个混淆矩阵!将其与我在其他帖子中使用的混淆矩阵进行比较( Precision and Recall , F1-score ),在那里我们构建了一个分类器,可以检测照片中的:“猫”、“母鸡”或“鱼”。注意到“深刻”的相似性了吗?单元格值完全相同。
要用 kappa 进行分类,我们可以简单的把每个评级看成一个类;另外,A 教授的评分是真实/实际值;B 教授的评分就是预测值(或者反过来——无所谓)。在这个实例中,kappa 分数测量真实值和预测值之间的一致程度,我们将其用作分类器的性能。在我们的例子中,我们可以使用之前计算的 kappa 值,因为单元值是相同的:
KappaScore = 0.2546
因为 1 表示完全一致,0 表示偶然一致,所以我们通常会得到一个介于两者之间的值。如果值小于 0,我们做的*比机会一致(不一致)*差,所以分类器中的某些东西被严重破坏了。
kappa 分数可以使用 Python 的 scikit-learn 库来计算(R 用户可以使用 cohen.kappa()函数,它是 psych 库的一部分)。
以下是我如何证实我的计算:
帖子到此结束。希望你觉得有用!
零售产品的多类对象分类
无摩擦商店体验的计算机视觉。
Photo by Fikri Rasyid on Unsplash
被困在付费墙后面?点击这里阅读完整故事与我的朋友链接!
零售从来都不是一个停滞不前的行业。如果零售商想要成功,他们就不能停滞不前。他们必须适应和创新,否则就有被抛在后面的风险。
计算机视觉在零售业中的应用将从根本上改变客户和零售商的购物体验。在这篇博客中,我将为零售产品制作一个基于计算机视觉的多类对象分类模型。这个项目的灵感来自著名的亚马逊 Go 商店。我希望你喜欢这篇文章,并发现它很有见地。
亚马逊将 Amazon Go 描述为一种不需要结账的新型商店。这意味着当你在 Amazon Go 购物时,你永远不必排队。该商店使用 Amazon Go 应用程序——你进入 Amazon Go,拿走你想要的产品,感谢该应用程序,只需再次离开。它通过使用自动驾驶汽车中发现的相同类型的技术来工作,如计算机视觉、传感器融合和深度学习。这项技术可以检测产品何时被拿走或放回货架,并在您的虚拟购物车中跟踪它们。当你带着商品离开商店时,你的亚马逊账户会被扣款,你会收到一张收据。
如果你想看的话,这是视频。
计算机视觉在零售业中的应用
- 使用面部识别定制体验。
- 让购物更人性化,减少交易。
- 基于计算机视觉的库存管理。
- 模糊了店内和网上的界限。
- 无摩擦商店体验。
数据
我在这个项目中使用了弗赖堡杂货数据集。它由 25 种食物的 5000 张 256x256 RGB 图像组成。论文可以在这里找到,数据集这里找到。
环境和工具
- Numpy
- 熊猫
- sci kit-图像
- Matplotlib
- sci kit-学习
- 克拉斯
图像分类
完整的图像分类管道可以形式化如下:
- 我们的输入是一个由 N 幅图像组成的训练数据集,每幅图像都标有 25 个不同类别中的一个。
- 然后,我们使用这个训练集来训练一个分类器,以学习每个类的样子。
- 最后,我们通过要求分类器预测一组它以前从未见过的新图像的标签来评估分类器的质量。然后,我们将这些图像的真实标签与分类器预测的标签进行比较。
代码在哪里?
事不宜迟,让我们从代码开始吧。github 上的完整项目可以在这里找到。
我从加载所有的库和依赖项开始。
接下来,我制作了一个函数来绘制损失和准确性,这将在训练模型后得到。
我还创建了几个函数来返回特定图像的标签并加载数据集。
我继续为每个产品创建单独的类,并调用函数来加载每个类的所有图像。
接下来,我将图像转换成分类格式,然后再对图像进行归一化。我还将数据集分成两部分——80%的数据用于训练集,20%的数据用于测试集。
由于数据集很小,我使用了旋转、缩放和翻转等数据扩充策略。这几乎使数据集的大小增加了一倍。
让我们展示每个类别中的一些示例。
Sample Products
到目前为止一切顺利。现在到了项目有趣的部分。我们来做深度学习模型。
我已经使用预训练的 VGG 重量转移学习。在此基础上,我使用了一个平均池层来减少前几层的学习权重的差异。此外,我用 30%的辍学,以减少过度拟合。最后,我使用了一个有 25 个神经元的密集层来表示我们数据集中的 25 类食品。我使用 softmax 作为激活函数,因为它是一个多类分类问题。
让我们将模型可视化。
我继续使用 keras 定义一些回调。如果验证准确性增加,我使用模型检查点来节省权重。如果验证损失开始趋于平稳,我还使用了提前停止标准。另外两个回调用于在 tensorboard 中可视化结果,以及在 csv 文件中保存日志。
接下来,我定义了 2 个优化器。第一个是随机梯度下降,学习率为 0.0001,动量值为 0.99。第二个是亚当,学习率略高,为 0.001。
对比两者,我发现亚当给出了更好的结果。我还使用分类交叉熵作为我们模型的损失函数。我对模型进行了 50 次迭代,批量大小为 128,我认为这是解决这个问题的最佳方法。
最后我画出了结果。
结果
损失/精度与历元
Loss/Accuracy vs Epoch
该模型能够达到 60%的验证准确性,考虑到每个类别中有 100-200 张图像的类别数量(25),这已经是相当不错的了。您可以随意使用不同的架构或使用超参数来获得更好的结果。
正确与不正确的样本
Correct vs Incorrect Samples
可以注意到,我们的模型预测了相当多的错误分类样本。但我仍然觉得这应该是这个领域进一步工作的良好开端。
结论
虽然这个项目还远未完成,但在如此多样的现实世界问题中看到深度学习的成功是令人瞩目的。自动对象分类对于食品和杂货零售商非常有帮助,可以减少仓库和零售店的库存管理过程中的人力。无摩擦的商店体验也被认为是购物的未来。
参考资料/进一步阅读
Amazon Go 是一种新型商店,拥有世界上最先进的购物技术。没有排队,没有结账-只是…
www.amazon.com](https://www.amazon.com/b?ie=UTF8&node=16008589011) [## 计算机视觉的主要零售用途及其如何增强购物
网络和网上购物的出现给零售业带来了巨大的冲击。然而,它从来没有…
searchenterpriseai.techtarget.com](https://searchenterpriseai.techtarget.com/feature/252463246/Top-computer-vision-retail-uses-and-how-they-enhance-shopping) [## 零售业计算机视觉初学者指南
任何一个手指未干的人现在都应该听说过席卷发达国家的“零售业末日”…
hackernoon.com](https://hackernoon.com/a-beginners-guide-to-computer-vision-in-retail-b5a31cfd5283)
在你走之前
相应的源代码可以在这里找到。
[## abhinavsagar/杂货-图像-分类
弗莱堡食品数据集由 25 种食品的 5000 张 256x256 RGB 图像组成。我用过迁移学习…
github.com](https://github.com/abhinavsagar/Grocery-Image-Classification)
联系人
如果你想了解我最新的文章和项目,请关注我的媒体。以下是我的一些联系人详细信息:
快乐阅读,快乐学习,快乐编码!
基于 LSTM 的多类文本分类
Photo credit: Pixabay
如何使用 Keras 深度学习库开发 Python 中文本分类问题的 LSTM 递归神经网络模型
自动文本分类或文档分类可以在机器学习中以许多不同的方式完成,如我们在之前看到的。
本文旨在提供一个示例,说明如何使用 Keras 实现使用长短期记忆 (LSTM)架构的递归神经网络 (RNN)。我们将使用与我们使用 Scikit-Lean 的多类文本分类相同的数据源,即源自data.gov的消费者投诉数据集。
数据
我们将使用一个较小的数据集,您也可以在 Kaggle 上找到数据。在该任务中,给定一个消费者投诉叙述,该模型尝试预测投诉是关于哪个产品的。这是一个多类文本分类问题。我们走吧!
df = pd.read_csv('consumer_complaints_small.csv')
df.info()
Figure 1
df.Product.value_counts()
Figure 2
标签合并
第一眼看到标签后,我们意识到我们可以做些事情来让我们的生活变得更轻松。
- 将“信用报告”合并为“信用报告、信用修复服务或其他个人消费者报告”。
- 将“信用卡”合并为“信用卡或预付卡”。
- 将“发薪日贷款”合并为“发薪日贷款、产权贷款或个人贷款”。
- 将“虚拟货币”合并为“货币转移、虚拟货币或货币服务”。
- “其他金融服务”的投诉数量很少,没有任何意义,所以,我决定删除它。
df.loc[df['Product'] == 'Credit reporting', 'Product'] = 'Credit reporting, credit repair services, or other personal consumer reports'
df.loc[df['Product'] == 'Credit card', 'Product'] = 'Credit card or prepaid card'
df.loc[df['Product'] == 'Payday loan', 'Product'] = 'Payday loan, title loan, or personal loan'
df.loc[df['Product'] == 'Virtual currency', 'Product'] = 'Money transfer, virtual currency, or money service'
df = df[df.Product != 'Other financial service']
整合后,我们有 13 个标签:
df['Product'].value_counts().sort_values(ascending=False).iplot(kind='bar', yTitle='Number of Complaints',
title='Number complaints in each product')
Figure 3
文本预处理
让我们看看这些短信有多脏:
def print_plot(index):
example = df[df.index == index][['Consumer complaint narrative', 'Product']].values[0]
if len(example) > 0:
print(example[0])
print('Product:', example[1])print_plot(10)
Figure 4
print_plot(100)
Figure 5
很脏,是吧!
我们的文本预处理将包括以下步骤:
- 将所有文本转换为小写。
- 在文本中用空格替换 REPLACE_BY_SPACE_RE 符号。
- 从文本中删除 BAD_SYMBOLS_RE 中的符号。
- 删除文本中的“x”。
- 删除停用词。
- 删除文本中的数字。
text_preprocessing_LSTM.py
现在回过头来检查我们的文本预处理的质量:
print_plot(10)
Figure 6
print_plot(100)
Figure 7
不错!我们完成了文本预处理。
LSTM 造型
- 通过将每个文本转换成整数序列或向量,对消费者投诉文本进行矢量化。
- 将数据集限制在 50000 个单词以内。
- 将每个投诉的最大字数设置为 250。
# The maximum number of words to be used. (most frequent)
MAX_NB_WORDS = 50000
# Max number of words in each complaint.
MAX_SEQUENCE_LENGTH = 250
# This is fixed.
EMBEDDING_DIM = 100tokenizer = Tokenizer(num_words=MAX_NB_WORDS, filters='!"#$%&()*+,-./:;<=>?@[\]^_`{|}~', lower=True)
tokenizer.fit_on_texts(df['Consumer complaint narrative'].values)
word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))
- 截断并填充输入序列,使它们在建模时长度相同。
X = tokenizer.texts_to_sequences(df['Consumer complaint narrative'].values)
X = pad_sequences(X, maxlen=MAX_SEQUENCE_LENGTH)
print('Shape of data tensor:', X.shape)
- 将分类标签转换为数字。
Y = pd.get_dummies(df['Product']).values
print('Shape of label tensor:', Y.shape)
- 列车测试分离。
X_train, X_test, Y_train, Y_test = train_test_split(X,Y, test_size = 0.10, random_state = 42)
print(X_train.shape,Y_train.shape)
print(X_test.shape,Y_test.shape)
- 第一层是嵌入层,使用 100 个长度向量来表示每个单词。
- SpatialDropout1D 在 NLP 模型中执行变分丢失。
- 下一层是具有 100 个存储单元的 LSTM 层。
- 输出层必须创建 13 个输出值,每个类一个。
- 激活功能是 softmax 用于多类分类。
- 因为这是一个多类分类问题,所以使用 categorical _ crossentropy 作为损失函数。
consumer_complaint_lstm.py
Figure 8
accr = model.evaluate(X_test,Y_test)
print('Test set\n Loss: {:0.3f}\n Accuracy: {:0.3f}'.format(accr[0],accr[1]))
plt.title('Loss')
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.legend()
plt.show();
Figure 9
plt.title('Accuracy')
plt.plot(history.history['acc'], label='train')
plt.plot(history.history['val_acc'], label='test')
plt.legend()
plt.show();
Figure 10
这些图表明,该模型有一点过度拟合的问题,更多的数据可能会有所帮助,但更多的时代不会有助于使用当前的数据。
用新投诉进行测试
test_new_complaint.py
Jupyter 笔记本可以在 Github 上找到。享受这周剩下的时光吧!
参考:
序列分类是一个预测性的建模问题,在这个问题中,你在空间或时间上有一些输入序列,并且…
machinelearningmastery.com](https://machinelearningmastery.com/sequence-classification-lstm-recurrent-neural-networks-python-keras/)