TowardsDataScience-博客中文翻译-2019-十七-
TowardsDataScience 博客中文翻译 2019(十七)
为游戏创建语音助手(国际足联教程)
使用深度学*驱动的唤醒词检测引擎,通过语音命令玩游戏。

Enabling the “rainbow flick” skill in the game by giving voice command “flick right”.
像亚马逊 Alexa 和谷歌 Home 这样的语音助手已经广泛流行,用于自动化和简化日常任务的执行。它们允许用户通过使用他们的声音快速完成事情,而不必通过他们的手机或电脑进行多次交互。
它们越来越受欢迎,最*被广泛采用,这在很大程度上要归功于语音识别技术的进步,这是由深度学*的进步推动的。

Popular voice assistants: Amazon Echo, Anki Vector and Google Home Mini.
我想探索我们如何利用这项技术,通过添加一种新的输入方法来配合传统的游戏手柄控制系统,从而改善我们的游戏体验。因此,我为足球/橄榄球模拟游戏 FIFA 创建了一个语音助手,它可以改变你的球队的战术,或者在比赛中表演技巧动作和庆祝进球,所有这些都只需要语音命令。
在本教程中,我将介绍如何为 FIFA 重新创建相同的命令,但你也可以按照相同的步骤创建自己的自定义语音命令,并将其添加到任何你喜欢的游戏中!
动机:我为什么选择国际足联?
如果你像我一样多年来一直在玩国际足联,你会知道为了改变游戏中的战术你需要记住不同的组合键。要在游戏中改变阵型或替换球员,你需要暂停游戏并进入菜单,这打破了游戏的流程,变得非常烦人,尤其是在在线模式下。此外,对于不同的目标庆祝,需要记住更多的组合键,而你通常只能记住其中的几个。游戏技巧也是如此,给出了*百种不同的组合键来记忆。

What if you could simply activate these skill moves by speaking out their names to a voice assistant without having to remember the 100 different key combinations?
我们先来看看语音识别引擎在语音助手中是如何工作的。
语音识别算法是如何工作的?
假设我们想要检测唤醒词“Okay EA”。


Converting raw audio to text using an RNN.
我们从麦克风捕捉原始音频,并将其转换为声音中不同频率频谱的可视化表示,称为声谱图。这是受人类的耳朵如何捕捉音频的启发。接下来,我们将该谱图的薄片作为连续时间步长馈送给递归神经网络模型。该模型被训练来预测在该时间范围内说出字符的可能性。将这样的可能性放在一个序列中给我们音频输入中所说的单词的文本形式,从而将语音转换为文本。
我们将使用名为 Porcupine 的库,该库声称利用这样的深度学*模型来执行对关键词的实时检测,我们可以使用这些关键词来识别语音命令。克隆下面的库,我们将使用它来创建自定义语音命令。
由深度学*支持的设备上唤醒词检测。-皮科语音/豪猪
github.com](https://github.com/Picovoice/Porcupine)
辅导的
在本教程中,我将展示我们如何创建一个自定义的唤醒词(如“Alexa”或“Okay Google”),然后发出命令在游戏中执行技能移动。
步骤 1:创建自定义语音命令
从克隆的 porcupine 存储库的主目录,在终端中运行以下命令,创建一个唤醒词检测文件来激活助手。我用的是“Okay EA”(因为 EA 是 FIFA 系列的发行商)。该命令生成的文件将存储在名为 output 的目录中。
tools/optimizer/windows/amd64/pv_porcupine_optimizer -r resources/ -w "Okay E A" -p windows -o output/
接下来,创建执行“彩虹轻弹”技能移动的命令。
tools/optimizer/windows/amd64/pv_porcupine_optimizer -r resources/ -w "flick right" -p windows -o output/
这会给我们两个。输出目录中的 ppn 文件。
步骤 2:设置豪猪检测引擎
步骤 3:从麦克风设置实时命令检测
从这里下载帮助文件 directkeys.py。这个文件帮助模拟键盘按键,以便 python 脚本可以与游戏交互。然后,执行以下命令,在连续循环中检测关键字,并在检测到适当的命令时激活相关的技能移动。
请注意,第 38 行到第 48 行需要匹配游戏中执行技能移动所需的按键。您可以通过使用它们各自的组合键来添加更多这样的移动。
这就是全部,你现在可以使用这个脚本来检测语音命令并激活游戏中的技能!在我的 GitHub 库下面找到更多支持动作的完整代码。
[## ChintanTrivedi/DeepGamingAI _ FIFA va
国际足联(windows 平台)游戏的语音助手,用你的声音控制游戏动作。…
github.com](https://github.com/ChintanTrivedi/DeepGamingAI_FIFAVA)
结果
进球庆祝:

Using the command “work out” to perform a goal-scoring celebration.
在比赛中改变战术:

Using the command “high pressure” to change tactics of how the team plays the match.
更多这样的结果可以在我的 YouTube 频道上找到,视频嵌在下面。
结论
如果 EA 能在未来的 FIFA 版本中加入这样的语音助手就太好了。发出类似于“用佩德罗替换威利安”或“将阵型改为 4–4–2”的命令,就可以立即看到比赛中的变化,而不必进入暂停菜单。此外,制作这样的语音助手可以简化游戏中的控制,并可以作为休闲游戏玩家的完美补充输入方法。
你们是怎么想的,是想用语音作为一种额外的输入方式来控制游戏,还是对现有的游戏手柄控制感到满意?下面让我知道!
感谢您的阅读。如果你喜欢这篇文章,请在媒体、 GitHub 上关注我,或者订阅我的 YouTube 频道。哦,还有 2019 新年快乐!
用 D3 Observable 创建 Web 应用程序

什么是 D3 可观测?
我以前写过关于将 D3 引入 web 应用程序的文章这里,看看如何将 D3 视觉效果绑定到 UI 元素。目的是鼓励超越独立的视觉效果,让人们原型化更完整的应用程序。真正的应用程序会征求反馈,因为它们使用了,帮助我们验证超出常规统计方法的分析。在我看来,如果你没有在构建一个真正的产品,你就没有真正在学*/做数据科学。
前一篇文章仍然有效,但是 D3 正朝着它所谓的可观察(正式名称为 d3.express)的方向转变。Observable 提供了一个各种各样的游乐场,允许用户在一个笔记本中在线修改 D3 代码。对于那些使用 Jupyter 笔记本的人来说,你会发现体验是相似的。它本质上是一个做 D3 的 REPL。
Observable 向真正的开发开放了 D3,因为它现在提供了下载您定制的 D3 visual 作为一个独立的“包”(一个 tarball 文件),您可以将它嵌入到您的应用程序中。Observable 自带运行时和标准库,为使用 HTML 、 SVG 、生成器、文件和承诺提供了有用的功能。
以下是可观察到的文档:
可观察:用户手册👋欢迎来到 Observable -网络神奇笔记本之家!有很多东西要学…
observablehq.com](https://observablehq.com/@observablehq/user-manual)
…以及一篇关于他们方法的自以为是的文章:
介绍 d3.express:集成发现环境。
medium.com](https://medium.com/@mbostock/a-better-way-to-code-2b1d2876a3a0)
你可以在这里找到视觉效果 的例子,你可以立即在你的浏览器中开始玩。当您希望创建新的可视化效果时,请访问以下网站,选择一个项目,根据需要编辑可视化效果,并嵌入到您的应用程序中。

概观
在本文中,我们将探讨以下主题:
- 创建一个快速的应用程序布局基于 2 个简单的样机;
- 为我们的应用程序的 UI 元素制作组件;
- 在我们的 app 内嵌入 可观察;
- 在我们的 app 和 Observable 之间发送 数据;
- 使用谷歌的图书 API 。
你可以在这里查看简单应用。

Figure 1
让我们开始吧。
应用程序
让我们制作一个简单的应用程序,它使用 Kaggle 上托管的 Goodreads 数据集 来允许人们探索书名。数据集列出了书名、作者、ISBNs 和一些简单的特性,比如评级。
模型
我们将允许用户查看原始数据的表格,并提供过滤功能,以便用户可以通过作者、 ISBN 号和语言来搜索表格。

Figure 2
我们还将从谷歌的图书 API 中获取图书属性,并在条形图中展示它们。该 API 还提供了所选图书的图片 URL,因此当用户通过 ISBN 搜索时,我们将显示图书封面:

Figure 3
我们将使用 Azle 把 Observable 和 Google 的 Book API 缝合成一个真正的应用程序。
目录结构
我们首先为我们的应用程序创建下面的目录结构:
**app**
├── **data** ├── **scripts** ├── **d3_visuals** ── **table** ── **bar_chart** ├── index.html
粗体名称是空文件夹,index.html 文件是通常的 Azle 起点:
codeblock 1
我们将在本文中添加所需的文件。现在通过在终端会话中运行以下命令,在 app 文件夹中启动一个简单的 web 服务器:
python3 -m http.server
…然后将您的浏览器指向本地主机:
[http://localhost:8000](http://localhost:8000/)
步骤 1:创建应用程序布局
我将使用 Azle 来创建我的应用程序的框架。我创建 Azle 是因为它使用快速、易于理解、轻量级、灵活且免费,并且使得将库和框架缝合在一起变得容易。但是你可以使用任何你喜欢的 JS 工具。
1.1 创建应用程序布局
我们使用 Azle 的 az.add_layout 函数创建布局。这就是我们如何在页面上创建网格。我将把我的布局代码放在 Azle 的 index.html 文件中:
codeblock 2
通读上面的代码,你可以很容易地知道页面是如何构建的。每个 Azle 函数都带一个“ target_class 和 target_instance 来向 DOM 添加元素。它还接受一个具有属性的对象。如果我们添加一个元素,它是一个内容对象,如果我们设计一个元素,它是一个样式对象(通常的 CSS 样式)。
上述代码产生以下内容:

Figure 4
我们可以看到布局如何允许我们通过在屏幕上划分区域来定位元素。让我们给我们的主体部分涂上颜色,这样它就能和身体的颜色融为一体了。我们将通常的 CSS 样式作为属性传递给样式对象:
**az**.**style_sections**('my_sections', 1, {
"background": "rgb(51, 47, 47)",
"height": "auto"
})
让我们也添加一个深色的背景,我们的视觉 _ 布局:
**az.style_layout**('visual_layout', 1, {
"align": "center",
**"background": "rgb(39, 36, 36)"**,
"border-radius": "4px",
"height": "460px",
"margin-top": "10px",
"column_widths": ['65%', '35%'],
"border": 3
})
现在我们的应用程序看起来像这样:

Figure 5
我们所有的布局单元格都在等待它们的内容。这就是组件出现的地方。
1.2 创建应用程序组件
组件由 UI 元素、样式和事件组合而成。这就像将创建应用程序特定部分所需的所有代码打包在一起。例如,如果我们想在我们的应用程序中有一个日历,我们将创建一个日历组件,并将其放置在我们的布局单元格之一。
创建组件使我们的代码模块化,易于重用,易于维护,并使我们能够在想法改变时更容易地调整应用程序。虽然我们将在 Azle 中创建我们的组件,但这些也可能是添加到 Azle 布局中的 React 组件。
从上面的模型中,我们知道我们需要搜索栏、图标、下拉菜单、图像和 D3 视觉效果。展示我们如何为这个应用程序创建每个组件超出了本文的范围。你可以在这里查看完整应用代码 。我们将在本文中创建几个主要的。
我们将所有组件代码放在 az.components 对象中:
az.components = {}
创建一个名为 component.js 的文件,并添加以下代码:
codeblock 3
看起来很多,但读起来很容易。注意我们如何首先添加和样式化一个布局,就像我们上面做的一样,这一次是为了保存我们的输入框和搜索图标。然后,我们将一个输入元素添加到第一个布局单元格中,并将搜索图标添加到第二个布局单元格中。最后,我们将一个事件添加到我们的搜索图标中,这样当用户点击它时,就会发生一些事情。
不要担心在我们的点击事件中发生的所有事情,我们会在嵌入 D3 视觉效果后解决这些问题。现在让我们创建组件,为我们的应用程序添加 D3 视觉效果。下面是表的例子:
codeblock 4
它看起来有点回调地狱,但它是可读的。我们添加并样式化一个 iframe,等待 iframe 完成加载,确保完整的数据集可用,然后将我们的消息发送到 iframe。消息发布是我们与 D3 交流的方式。我们将在下一节讨论这一点。
添加组件的方式与 Azle 向应用程序添加元素的方式相同:
**az.components.d3_table**(target_class, target_instance)
因此,上面的代码行将我们的 iframe 添加到我们的应用程序框架的一个单元中。这些框架中还没有任何视觉效果可以显示,所以让我们去抓取我们可观察到的视觉效果,然后我们将它们定位到它们适当的单元格中。
步骤 2:在应用程序中嵌入 D3
嵌入 Observable 非常简单,只需下载所需视觉对象的 tarball,然后在 iframe 中托管其 index.html 文件。这并不是将 Observable 引入应用程序的唯一方法,但是它很快,并且对于快速原型开发很有效。
2.1 从可观察的事物中获得视觉效果
我们需要一张表和条形图。这些都可以在网上搜索到:
普斯塔法的可观察的笔记本。
observablehq.com](https://observablehq.com/@pstuffa/tables-with-nested-data) [## 条形图
条形图这个图表显示了英语中字母的相对频率。这是一个垂直条形图…
observablehq.com](https://observablehq.com/@d3/bar-chart)
我们通过点击右上角的 3 个点下载 他们的 tarballs :

Figure 6
下载完成后,解压压缩包并放入各自的文件夹:

2.2 在 App 和 D3 之间建立通信
我们需要我们的应用程序与我们可观察到的视觉效果进行交流。在上一节中,我们提到了使用az . post _ message _ to _ frame来做这件事。下面是函数:
codeblock 5
Azle 的 post_message_to_frame 允许我们的应用程序在 iframe 内部发送数据和函数(只要所有东西都在同一个服务器上,就不会有 CORS 问题)。
main.redefine 来自可观察本身,也是我们如何在 D3 可视化中重新定义变量和数据。D3 表以名为“fakeData”的数据对象开始,因此我们需要用 Kaggle 数据集中的图书数据替换它。你会注意到我们传入了一个名为 filter_by_author 的父函数,而不是数据本身。我们稍后会讨论这个问题。
这个等式的另一半是 D3 如何接受发布的消息。为了让托管的可观察对象接受来自应用程序的传入消息,我们必须向可观察对象的 index.html 文件中添加一个事件监听器:
codeblock 6
Azle 图书馆在最上面:
<script src='[https://azlejs.com/v2/azle.min.js'](https://azlejs.com/v2/azle.min.js')></script>
所以我们的 D3 表 index.html 文件应该是这样的:
codeblock 7
为了清楚起见,我们将向该文件添加:

这种通信是可行的,因为我们使用 Azle 的 post_message_to_frame 在我们的框架内发送 Observable 的 main.redefine ,此时我们的框架接受该消息并执行 parent.filter_by_author 函数(如代码块 5 所示)。下图描述了这个概念:

Figure 7
在我们讨论父函数之前,让我们来设计一下 D3 的视觉效果。
2.3 向 D3 Observable 添加样式
因为我们下载了整个笔记本,所以会有我们不希望在应用程序中出现的笔记本单元格(它们用于将视觉效果调整为 REPL)。我们应该移除那些细胞。
我们可以通过删除【Observable 的index.html 文件中的下面一行:****
const main = runtime.module(define, Inspector.into(document.body));
包含以下内容:
const main = runtime.module(define, name => {
if (name == 'chart') {
return new Inspector(document.body)
}
});
这样只会画出图表。
我们也可以设计 D3 视觉效果让它看起来更现代。检查表文件夹中的table-with-nested-data . js文件。比较原始文件和我在这里准备的文件,看看我添加的样式。
codeblock 8
区别在于:

如果您检查 bar_chart 文件夹中的 bar-chart.js 文件,您会看到类似的变化。
步骤 3:创建父函数
我们上面提到了父函数的使用。在图 7 中,我们看到了我们的应用程序和 Observable 之间的双向通信。父函数是我们在应用程序中从可观察对象调用函数的方式。
我们希望 Observable 使用新的、经过过滤的数据重新绘制它的 D3 视图。因此,我们将使用从 iframes 内部调用的函数来过滤原始数据集(我们之前读到的)。
创建一个名为 parent_functions.js 的文件,放在 scripts 文件夹中:
**app**
├── **data** ├── **scripts** ── parent_functions.js
├── **img** ├── **d3_visuals** ── **table** ── **bar_chart** ├── index.html
下面是前 3 个父函数,它们返回完整的数据集、按语言过滤的数据或按作者过滤的数据。
codeblock 9
我们的框架将通过代码块 6 调用这些函数,并用新返回的数据替换默认的可视化数据集。
步骤 4:定位我们的组件
至此,我们已经基于实体模型构建了一个简单的应用程序布局,精心制作了组件,在包含 main.redefine 的组件中设置了单击事件,向 D3 索引文件添加了事件处理程序,并创建了用于返回完整和过滤数据的父函数。此外,我们有我们的 D3 可视图像在他们的文件夹中等待。
D3 部分剩下的就是将我们的组件放入它们的目标单元。我们像任何其他 Azle 函数一样,使用目标布局单元格的 target_class 和 target_instance 来定位我们的组件。
首先,我们需要将数据集加载到应用程序中。
我下载了 CSV 格式的 Kaggle Goodreads 数据集,然后使用在线转换器将其转换成 JSON。然后我下载了新的 JSON 文件,并保存到我们的 app 目录的 data 文件夹中,命名为 books.json 。
现在我们可以使用 Azle 的 read_local_file 函数来读取我们的数据集。将它放在 index.html 文件的顶部:
codeblock 10
我将数据保存为一个名为 az.hold_value.full_dataset 的新对象。可以使用 az.hold_value。[变量名称]使任何变量或对象在 Azle 名称空间内全局可用。请注意,我还对数据进行了“切片”,以限制应用程序中显示的行数。您可以删除它以显示所有数据(或者更好的是,创建一个允许用户控制加载多少行的组件:)
现在,我将使用 Azle 的 call_once_satisfied 函数来确保在调用我们的组件之前已经加载了完整的数据集:
codeblock 11
一旦定义了现有数据对象的条件,这将把所有组件添加到我们的应用程序布局中。
我们的表看起来是这样的:

如果你玩应用,你会看到用户可以通过作者姓名和语言过滤 D3 表格。许多 B2B 应用程序受益于这种功能。
当用户通过 ISBN 搜索时,我们仍然需要实现我们的原型承诺,显示信息的条形图和书的封面。让我们现在做那件事。
步骤 5:调用 Google 的图书 API
如前所述,我们可以使用免费的谷歌图书 API 从 ISBNs 获取图书封面。快速的谷歌搜索会让我们看到显示如何使用它的堆栈溢出答案(通常比通常的文档更快):
我试图弄清楚如何使用谷歌图书 API 通过 ISBN 搜索一本书。我需要写一个程序…
stackoverflow.com](https://stackoverflow.com/questions/7908954/google-books-api-searching-by-isbn)
我们希望条形图和图书封面出现在用户通过 ISBN 搜索后。如果您查看 components.js 中的 search_by_isbn 函数并检查它的 click 事件,您将会看到这是如何处理的。
当用户粘贴 ISBN #并点击搜索图标时,会发生 4 种情况:
- 调用 fetch_book_cover 组件;
- 使用 az.style_iframe 中的 CSS display 属性切换 iframe 的显示;
- 一条消息被发送到保存条形图的 iframe,数据由 ISBN 过滤;
- 伴随条形图调用 add_chart_buttons 组件。
fetch_book_cover 组件解析从 Google 的 Book API 返回的数据,并检索所需的图片 URL:
data.items[0].volumeInfo.imageLinks.thumbnail
…在我们将图书封面添加到应用程序时,我们可以将它用作图片 URL:
**az.add_image**('visual_layout_cells', 2, {
"this_class": "cover_img",
"image_path": data.items[0].volumeInfo.imageLinks.thumbnail
})
如果你查看应用程序代码的 component.js ,你会看到一个调用谷歌图书 API 的函数。它使用 Azle 的 call_api 函数以及以下 URL:
"https://www.googleapis.com/books/v1/volumes?q=isbn:" + **az.grab_value**('search_isbn_bar', 1)
这只是将 API url 和用户添加的 ISBN 连接到类名为“search_isbn_bar”的输入字段。
用户因此可以从表格中复制一个 ISBN 号,并将其粘贴到通过 isbn 输入字段进行搜索,这将绘制条形图并显示该书的封面:

用户还可以使用条形图下方的细按钮来更改条形图视图。这里又是 app gif:

如前所述,您可以通读完整的代码库,看看完整的应用程序是如何构建的。如果你看到一个看起来不熟悉的函数,参考 Azle 的文档。本文想要展示将 D3 Observable 引入一个真正的 web 应用程序所需的核心部分。
摘要
在本文中,我们研究了在 web 应用程序中嵌入 D3 Observable。我们使用 Azle 将 Observable 和 Google 的 Book API 结合在一起,创建了一个真正的应用程序,允许用户搜索和浏览书名。
我们的任务包括基于 2 个简单的实体模型创建一个快速布局,制作并定位组件,在框架中嵌入一个可观察的表格和条图表,使用消息 发布和事件 处理程序在应用程序和可观察对象之间进行通信,最后使用 Google 的 Book API
我们只讨论了将 D3 连接到应用程序所需的主要部分。您可以在 GitHub 项目上探索更多代码。我鼓励你创建自己的应用程序,并通过真实的产品将你的分析带给用户。
如果你喜欢这篇文章,你可能也会喜欢:
[## 学*建立机器学*服务,原型真实的应用程序,并部署您的工作…
在这篇文章中,我将向读者展示如何将他们的机器学*模型公开为 RESTful web 服务,原型真实…
towardsdatascience.com](/learn-to-build-machine-learning-services-prototype-real-applications-and-deploy-your-work-to-aa97b2b09e0c) [## 机器学*工作流的 GUI 化:快速发现可行的流水线
前言
towardsdatascience.com](/gui-fying-the-machine-learning-workflow-towards-rapid-discovery-of-viable-pipelines-cab2552c909f) [## 创建 R 和 Python 库的分步指南(在 JupyterLab 中)
r 和 Python 是当今机器学*语言的支柱。r 提供了强大的统计数据和快速…
towardsdatascience.com](/step-by-step-guide-to-creating-r-and-python-libraries-e81bbea87911) [## 用 D3.js 从玩具视觉过渡到真实应用
我们经常孤立地学*技术和方法,与数据科学的真正目标脱节;至…
towardsdatascience.com](/combining-d3-with-kedion-graduating-from-toy-visuals-to-real-applications-92bf7c3cc713)*
用 python 创建单词云

Image from culturacolectiva
在最*的一个 NLP 项目中,我看到了一篇文章,文章中用美国总统就职演说中的单词创建了单词云。虽然我使用了单词云来可视化文档中最常见的单词,但我没有考虑过使用它和遮罩来表示主题。这让我开始思考...
几个月前,在《权力的游戏》第一季到第七季的最后一集播出前不久,我急切地等待着大结局——以至于我去寻找任何我能在网上找到的《权力的游戏》数据,以预测谁将在第八季的混乱中幸存下来,以及 7 个王国将会发生什么。不幸的是,我没有时间了,但我确实找到了人们收集的大量数据和可视化。当我偶然发现就职词云时,我想知道我是否可以使用《权力的游戏》的数据,特别是剧本,以及角色的图像遮罩来创建一些非常酷的可视化效果。在本文中,正如在最*的一次技术会议上展示一样,我将逐步使用 python 创建《权力的游戏》单词云。
入门指南
在处理基于文本的数据时,遵循与就职词云文章相似的方法,涉及几个步骤。这些步骤是:
- 查找相关数据
- 清理数据
- 从图像创建遮罩
- 生成单词云
因为我将在本教程中使用 python,所以有许多库可以帮助完成上述步骤,另外 Dipanjan Sarkar 的自然语言处理指南提供了文本分析技术的全面介绍,如果你正在寻找关于该主题的进一步阅读。
在开始第一步之前,我定义了项目的目标,以确保不会遗漏任何重要步骤,并对目标有一个清晰的愿景。
项目目标:为《权力的游戏》中被图像掩盖的角色创建文字云。
1.查找相关数据
网上有大量的《权力的游戏》数据——我通常首先在 GitHub 和 Kaggle 上搜索数据集——我很快找到了一个包含脚本的数据集。
为了实现项目目标,每个角色的台词都需要陈述——第一个问题是:角色台词可用吗?

Snippet of Game of Thrones script
看第一集的一个片段,人物数据是有的!然而,在探索下一集和下一季时,角色名字的格式会有所不同。
在上面的例子中,只使用了角色的名字,而且都是大写的,而在其他季节中,角色的全名都是大写的。舞台指示也可以包含在角色名称中。
因此,通过字符名称搜索所有行,可以使用带有字符名称的正则表达式:
re.findall(r'(^'+name+r'.*:.*)', line, re.IGNORECASE)
这个表达式搜索一行的开始(^),后面是我们作为变量输入的字符名,后面是任何文本(。),然后是一个冒号,表示这是一个字符行(😃,后跟任何文本(。).通过将该正则表达式包含在括号中,返回整行。最后忽略大小写,所以角色名可以是大写/小写/字母大小写。
使用下面的函数将返回一个字符的所有行:
# final_data taken from:
# [https://github.com/shekharkoirala/Game_of_Thrones](https://github.com/shekharkoirala/Game_of_Thrones)# get data for characters
def get_char_lines(char):
output = []
print('Getting lines for', char)
with open('final_data.txt', 'r') as f:
for line in f:
if re.findall(r'(^'+char+r'.*:.*)',line,re.IGNORECASE):
output.append(line)
f.close()
print(char, 'has ', len(output), 'lines')return output# get lines using
get_char_lines('arya')
2.清理数据
现在我们有了角色的线条,这些需要被清理。
以下技术用于清洁生产线,这些相同的技术也在上面提到的 NLP 指南中详细描述。正则表达式在替换或删除字符时也很有用:
- 移除线路信息,例如
JON:
re.sub(r'.*:', '', text)
- 删除括号—从字符行中删除任何舞台方向
re.sub('[\(\[].*?[\)\]]', ' ', text)
- 删除重音字符,并使用
unicodedata库进行规范化
unicodedata.normalize('NFKD', text).encode('ascii', 'ignore').decode('utf-8', 'ignore')
-
扩展任何缩略词,例如,不要→不要
这里有一个 python 库,将contractions.py复制到您的工作目录,来自:
https://github . com/dipanjanS/practical-machine-learning-with-python/ -
(可选):在一个单词被词干化为字典中的一个词根的情况下应用词条解释,例如 is,are → be
当前没有使用词条解释,因为它不能很好地处理一些单词,例如 Stannis → Stanni -
将所有文本转换为小写
text.lower()
- 删除特殊字符(*、。!?)以及删除数字的选项—默认设置为 false
pattern = r'[^a-zA-Z0-9\s]' if not remove_digits else r'[^a-zA-Z\s]' re.sub(pattern, '', text)
- 删除停用词,使用
nltk库中的停用词,首先对文本进行标记,将字符串分割成子字符串列表,然后删除所有停用词
stopword_list = stopwords.words('english')
tokens = nltk.word_tokenize(text)
tokens = [token.strip() for token in tokens]
' '.join([token for token in tokens if token not in stopword_list])
这些清洁技术基于几种不同的来源,并且这些步骤有许多变化以及可以使用的其他清洁技术。这些初始清洁步骤已经在这个项目中使用。
3.从图像创建遮罩
基于就职词云,PIL库用于打开图像,从图像创建numpy数组以创建遮罩。
char_mask = np.array(Image.open("images/image.jpeg")) image_colors = ImageColorGenerator(char_mask)
可选地,numpy阵列可以和wordcloud.ImageColorGenerator一起使用,然后recolor单词云代表图像的颜色,或者其他。这将在下一节中讨论。
在最初将一幅图像作为文字云遮罩进行测试后,图像中的背景产生了太多的噪声,以至于字符的形状没有被很好地定义。为了避免这种情况,移除图像背景并替换为白色背景会很有用。

Remove background from image and replace with white background
4.生成单词云
最后一步是使用generate()函数创建单词云。
wc = WordCloud(background_color="white", max_words=200, width=400, height=400, mask=char_mask, random_state=1).generate(text)# to recolour the image
plt.imshow(wc.recolor(color_func=image_colors))
单词云将被图像掩盖,文本的大小将基于词频。
可以调整单词云的参数——尝试增加max_words来查看一些不常用的单词,注意,这应该少于文档中唯一单词的数量。
如前一节所述,recolor步骤是可选的,这里用于表示原始图像颜色。
权力的游戏单词云
对《权力的游戏》角色的图像使用上述步骤,生成的单词云呈现如下:
艾莉亚

Arya word cloud
琼恩·雪诺

Jon Snow word cloud
丹妮莉丝

Daenerys word cloud
达沃斯爵士

Ser Davos word cloud
这也被扩展到基于房屋生成单词云:
兰尼斯特家族

Lannister word cloud
史塔克家族

Stark word cloud
有了这些单词云,项目的最初目标就达到了!
丰富
有几种方法可以改进这些单词云。
- 常用字:在所有的汉字书写中,都有一些重复出现的常用字。目前,词云是基于词频生成的,然而,另一种方法是 TFIDF,它基于词在文档中的频率和相对于语料库的频率对词进行加权。或者,可以生成一个自定义停用词表来删除其他常用词
- 词汇匹配/词干匹配:词汇匹配在上面的例子中没有使用,因为一些《权力的游戏》特有的单词在测试时被缩短了(Stannis → Stanni),然而这确实意味着来自同一个词根的单词没有联系,并且在单词云中出现了几次,例如 say,said。可以使用替代的引理方法或词干技术。
- 文本清理:还可以使用进一步的清理或数据准备步骤
- 单词云功能:我用不同的单词颜色、背景颜色和单词云参数进行了测试,灰色和黑色背景在这种情况下可以工作,但一些参数可以进一步优化
- 进一步的文本分析:从这个数据集中可以得出很多真知灼见。更大的分析范围!
延伸——陌生的事物
通过进一步观察,我最终发现了更奇怪的东西脚本——尽管它们缺少字符行,但数据仍可用于生成单词云……

Stranger Things word cloud
摘要
在这篇文章中,我已经完成了生成单词云的基本步骤,这些单词云被一个图像所掩盖。
这只是我尝试单词云的开始!该项目还有很大的发展和改进空间,我希望您能按照步骤使用 word clouds 创建自己的项目!\
感谢您的阅读!如果你喜欢我的文章,那么订阅我的每月简讯,在那里你可以将我的最新文章和顶级资源直接发送到你的收件箱!
你可以在 Medium 上关注我以获取更多文章,在 Twitter 上关注我或者在我的网站上了解更多关于我正在做的事情。
为不在词汇表中(OOV)的单词(如新加坡英语)创建单词嵌入

Photo by Dmitry Ratushny on Unsplash
介绍
在这篇文章中,我将分享我如何创建用于下游自然语言处理(NLP)任务的新加坡英语单词嵌入。
但是为什么你可能会问?
要理解我做这个任务的动机,你首先需要理解什么是单词嵌入以及它们在 NLP 中的重要性。
谈到自然语言理解(NLU),这需要一台机器来理解人类语言。为了完成这样的壮举,机器需要将语言(用文字书写)转换成最能代表特定单词的含义、关系和上下文的数值。
这些“数值”可以被视为 NLP 中的单词嵌入——单词映射到 n 维空间中的实数向量。
简而言之,想象一个三维窗格(x 轴、y 轴和 z 轴)上的散点图。图上的每个点代表一个英语单词,即“国王”、“王后”。您还会注意到,这些点彼此距离非常*。此外,这些点也离“猫”或“狗”之类的词相当远。

Figure 1 — Word Embeddings for “King”, “Queen”, “Cat” and “Dog”. Source: https://arxiv.org/ftp/arxiv/papers/1901/1901.07176.pdf
为了实现这样的情节,单词“国王”、“王后”、“猫”和“狗”被赋予了单词嵌入(X、Y 和 Z 数值),以最好地表示它们之间的关系。
在图 1 中,我们可以看到与人类相关的单词被分组在一起(红色),而与动物相关的单词被分组在一起(蓝色)。为什么?因为生成这些单词嵌入的机器已经通过遍历大型文本语料库的多次迭代学*了这些复杂的关系。它给所有它见过和训练过的单词赋予了数字表示。
这就像一台机器在说:
“哎!我已经多次通读了你的文本语料库,以下是我认为最能代表每个单词的数值!”
作为一个人,我们需要证实这样的说法?一种方法是在向量空间中可视化这些单词,即散点图,以查看这些单词在该窗格中的位置。
如果相关的单词有点像图 1 中那样彼此靠*,那么我们可以验证机器确实已经学*了足够多的知识来将单词表示为数字。
简而言之…
单词嵌入是允许机器基于上下文理解单词之间的关系或含义的值。即“国王”和“王后”彼此相似(更接*),而与“猫”和“狗”等词不同(更进一步)。
当然还有比我上面给出的例子更多的单词嵌入,但是…
我想强调的一点是。
机器和数字而不是单词一起工作。单词嵌入很重要,因为它们是单词的数字表示,这是进行任何下游 NLP 任务之前需要的第一步。
自然语言处理任务,如实体提取、文本摘要、情感分析、文本分类、语音转文本、图像字幕、语音转文本以及许多其他下游任务,都需要良好的单词嵌入来获得准确的结果。
到目前为止有意义吗?
既然我们已经了解了单词嵌入及其重要性,那么我们就来简单谈谈新加坡式英语吧。
新加坡式英语?什么是新加坡式英语?
对于我的非新加坡读者来说,“新加坡式英语”本质上是新加坡口语。它是英语、马来语、泰米尔语、普通话和许多其他方言如闽南语的混合体。
我在网上找到的最好的例子之一就是这个。

Figure 2 — Example of Singlish
我一直以来最喜欢的新加坡式英语的另一个例子是这个。

Figure 3 — The power of “Can”. Source: www.Angmohdan.com
你看,新加坡式英语不仅仅是不同语言的混合。从普通话改编而来的新加坡英语单词的使用可以根据新加坡英语术语的语调改变前一个单词的意思。即“我能吗?”vs“残博?”
在英语中,“Can”这个词就是“能够”。在新加坡式英语中,这个意思根据随后用某种语调说的新加坡式英语术语而变化。
你可以明白为什么在 NLP 中使用新加坡式英语是一件痛苦的事情。
那好吧。
既然介绍已经结束了,让我们进入正题打败匈奴…开个玩笑!(我希望你能从《花木兰》中找到参考文献……)
接下来的几个部分是我所做的事情的基本细节。这可能是也可能不是技术性的,但我会尽力用简单的术语解释我所做的事情。
但首先,总结一下。
摘要
- 机器看不懂文字,但看得懂数字。因此,单词嵌入很重要,因为它们是文本的数字表示。
- 许多下游 NLP 任务都是从单词嵌入开始执行的。
- 新加坡英语是一种非常本地化的语言。据我所知,新加坡英语没有任何现成的单词嵌入。有必要为任何未来的 NLP 作品创建新加坡英语单词嵌入。
方法学
在这一节中,我将首先解释我从哪里得到这些数据。然后,我将谈论用于创建新加坡式英语单词嵌入的模型。之后,我将回顾我的初步发现,我从这些发现中意识到的问题,在最终揭示我的最终结果之前,我是如何纠正这个问题的。
数据收集—硬件区域论坛
这些数据是从我们一直最喜欢的新加坡论坛“硬件区”(【https://forums.hardwarezone.com.sg/】T2)上删除的。这是一个最初以信息技术为导向的论坛,但和所有论坛一样,它已经偏离成为许多新加坡人谈论世界上任何事情的地方。
最精彩的部分?
他们在论坛上大多使用新加坡式英语。非常适合任何深度学*任务。下面是一个线程注释的例子。
['got high ses lifestyle also no use treat chw like a dog if me i sure vote her she low ses man take people breakfast ask people buy ckt already dw pay up tsk tsk tsk\n',
' children big liao no need scare tio pok\n',
'she low ses man take people breakfast ask people buy ckt already dw pay up tsk tsk tsk i dont get her thinking actually if she is rich she could even fly over to kl to eat buffet and then fly back in 1 hour dunno why want to save over this kind of small thing to ppl like her like food\n']
这看起来像是蹩脚的英语,但实际上,对于一个新加坡人来说,我们理解这里使用的俚语和英语的隐含意义。
数据收集方面,废弃了 46 个主线程,共有 7,471,930 个条目。以下是按条目分列的线程数量。
Eat-Drink-Man-Woman 1640025
Travel and Accommodation 688253
Mobile Communication Technology 582447
Mass Order Corner 574927
MovieMania 451531
Gaming Arena 426976
Campus Zone 322146
General Merchandise Bazaar 286604
Money Mind 264523
Internet Bandwidth & Networking Clinic 243543
Music SiG 227198
Hobby Lovers 224994
Headphones, Earphones and Portable Media Devices 206141
Notebook Clinic 184116
HomeSeekers and HomeMakers 153683
Apple Clinic 139810
Hardware Clinic 133421
Cars & Cars 115286
The Tablet Den 108793
Fashion & Grooming 98211
Electronics Bazaar 75192
Degree Programs and Courses 49734
Football and Sports Arena 47769
Software Clinic 43553
Health & Fitness Corner 21665
The "Makan" Zone 21358
National Service Knowledge-Base 16545
Current Affairs Lounge 16259
Home Theatre & Audiophiles 14888
The House of Displays 13987
Employment Office 12146
Other Academic Concerns 9927
Tech Show Central (IT Show 2018) 9895
Parenting, Kids & Early Learning 9756
Pets Inner Circle 6525
Wearable Gadgets and IoT 5735
The Book Nook 5238
Digital Cameras & Photography 4714
Ratings Board 3858
IT Garage Sales 3549
Diploma Programs and Courses 2348
Certified Systems, IT Security and Network Training 1798
Post-Degree Programs & Courses 1724
Online Services & Basic Membership Support/Feedback 876
Design & Visual Art Gallery SiG 246
HardwareZone.com Reviews Lab (online publication) 18
手动审查每个线程的数据质量后,一些线程(如“差旅和住宿”)主要用于销售,因此被排除在分析之外。
我最终只使用了来自“MovieMania”的数据,其中不包括任何形式的广告,也不包括线索中的销售。我还认为 451,532 个条目足以训练合适的单词嵌入。
这个帖子中每个条目的平均长度是 27.08 个单词。这给了我 12,675,524 个单词的语料库。就使用的独特词汇数量而言,这个数字下降到 201,766 个。我根据频率保留了前 50,000 个单词,并用“未知”标记来标记剩余的单词进行训练。
保留不常用的单词用于训练是没有意义的。如果这个单词只出现了几次,就没有学*的机会。因此,我用一个“未知”标记替换了所有不常用的单词。
我还通过简单地删除所有形式的标点符号和通过小写来标准化大小写来清理数据。
技术总结:
字数:12675524
句数:427735
平均。每句话字数:27.05
使用的独特词汇字数:5 万
既然数据已经出来了,下一部分将讨论用于训练单词嵌入的实际模型。
介绍跳跃图!
skip-gram 模型是一种无监督的机器学*技术,用于查找与给定单词最相关的单词。
以这个短语为例,

Figure 4 — Skip-gram example
跳过语法模型试图预测给定输入单词的上下文单词。在这种情况下,给定单词“狐狸”,预测“快”、“棕色”、“跳跃”和“结束”。
现在想象一下,模型从硬件区扫描我的整个训练语料库。对于每个句子(427,735 个句子),遍历每个单词,提取出它的上下文单词,并使用输入单词来预测这些上下文单词。
还计算每个句子的误差函数。该模型的目标是通过缓慢调整模型内的权重,通过多次迭代来最小化该误差函数。
我重复这种“学*”,直到误差函数开始稳定,然后用模型作为我的单词嵌入来提取权重。
我上面试图解释的,本质上是神经网络是如何工作的。从体系结构上来说,它看起来会像这样:

Figure 5- Skip-Gram Architecture.
跟了这么远?
太好了!在得出结果之前,让我们回顾一下模型构建过程中的一些技术规范。
技术规格: Context size = 3
学*率= 0.025
学*率衰减= 0.001
历元数= 25
字数= 100
负样本数= 3
总训练时间= 17 小时 05 分钟(每个历元约= 41 分钟)
对于那些想知道这些技术术语指的是什么的人来说,上下文大小指的是要预测的单词数,即“quick”、“brown”、“jumps”、“over”是 2 的上下文大小,即输入单词前两个单词,“Fox”后两个单词。
学*率和衰减指的是模型如何在迭代过程中调整其权重。即每次迭代学*了多少。
历元指整个训练集要循环多少次。例如,在我的训练集中有 427,735 个句子,我循环了 25 次。
单词尺寸是指最能代表一个单词的数值个数。在我介绍的例子中,我用了 3 个维度来保持简单易懂。事实上,我们可以到达 300 个维度。
负样本量是一个来自负采样的参数,负采样是一种用于训练机器学*模型的技术,与正观测相比,机器学*模型通常具有更多的负观测。回想一下,我保留了 50,000 个词汇以供使用。
如果我用“狐狸”来预测“快”,只有一个正确答案,而有 49,999 个错误答案。获得“快速”正确预测的概率会低得惊人。
因此,为了加快学*过程,使用负采样来减少负水平的数量。即,我只查看 3 个随机错误答案,而不是查看 49,999 个错误答案。因此,阴性样本数= 3。
还和我在一起吗?太棒了。让我们继续吧!
初步结果
这是第一次尝试为新加坡式英语生成单词嵌入的结果。
你在下面看到的是词对之间的相似性得分(0 到 1)和最接*感兴趣的词的前 10 个词。数字越高,单词在上下文方面越相似。
在研究新加坡英语单词之前,作为初步测试,我决定看看像“她”对“她”和“他的”对“他”这样的单词的分数。如果这些分数不高,我会认为模型训练得不够好。
令人欣慰的是,分数相当高,相当不错。
Similarity between 'her' & 'she': 0.9317842308059494
Closest 10:
her 0.9999999999999994
she 0.9317842308059496
who 0.8088322989667506
face 0.7685887293574792
and 0.731550465085091
hair 0.7196624736651458
shes 0.7191209881379563
when 0.7119862209278394
his 0.7107795929496181
that 0.7091856776526962
********************************************************
Similarity between 'his' & 'he': 0.897577672968669
Closest 10:
his 1.0
he 0.8975776729686689
him 0.8446763202218628
who 0.775987111217783
was 0.7667867138663951
that 0.7528368024154157
father 0.749632881268601
son 0.7281268393201477
become 0.7264880215455141
wife 0.711578758349141
********************************************************
Similarity between 'jialat' & 'unlucky': 0.011948430628978856
Closest 10:
jialat 1.0
sia 0.8384455155248727
riao 0.8266230148176981
liao 0.8242816925791344
sibei 0.814415592977946
hahaha 0.8064565592682809
ya 0.8045512611232027
meh 0.7954521439129846
lol 0.7936809689607456
leh 0.7920613014175707
********************************************************
Similarity between 'jialat' & 'bad': 0.6371130561508843
Closest 10:
bad 1.0
quite 0.8823291887959687
good 0.8762035559199239
really 0.8758630577100476
very 0.8731856141554037
like 0.8728014312651295
too 0.8656864898051815
damn 0.8599010325212141
so 0.8486273610657793
actually 0.8392110977957886
********************************************************
Similarity between 'bodoh' & 'stupid': 0.1524869239423864
Closest 10:
bodoh 0.9999999999999998
628 0.4021945681425326
u4e3au56fdu5148u75af 0.3993291424102916
beck 0.39461861903538475
recieve 0.39110839516564666
otto 0.3839416132228821
gaki 0.34783948936473097
fapppppp 0.3418846453140858
bentley 0.3344963328126833
hagoromo 0.3331640207541007
********************************************************
Similarity between 'bah' & 'ba': 0.5447425470420932
Closest 10:
bah 0.9999999999999998
lei 0.7051290703273838
nowadays 0.698360482336586
dun 0.6968374466521237
alot 0.6767383433113785
type 0.6745085658120278
cos 0.6711909808612231
wat 0.6682283480973521
ppl 0.6675756452507112
lah 0.6671682261049516
********************************************************
Similarity between 'lah' & 'la': 0.8876189066755961
Closest 10:
lah 1.0
meh 0.8877331822636787
la 0.8876189066755962
dun 0.8865821519839381
mah 0.8793885175949425
leh 0.8723455556110296
cannot 0.8686775338961492
ya 0.8661596706378043
u 0.8549447964449902
wat 0.8542029625856831
********************************************************
Similarity between 'lah' & 'leh': 0.8723455556110294
********************************************************
Similarity between 'lah' & 'ba': 0.6857482674200363
********************************************************
Similarity between 'lah' & 'lor': 0.8447135421839688
********************************************************
Similarity between 'lah' & 'hor': 0.722923046216034
********************************************************
Similarity between 'lor' & 'hor': 0.6876132025458188
Closest 10:
lor 1.0
u 0.8925715547690672
cannot 0.865412324252327
dun 0.8509787619825337
leh 0.8508639376357423
lah 0.8447135421839689
ya 0.8438741042009468
la 0.8403252240817168
meh 0.8356571743730847
mah 0.8314177487183335
********************************************************
Similarity between 'walau' & 'walao': 0.4186234210208167
Closest 10:
walau 1.0
nv 0.6617041802872807
pple 0.6285030787914123
nb 0.6248358788358526
la 0.6207062961324734
knn 0.6206045544509986
lah 0.6158839994483083
lo 0.6102554356797499
jialat 0.6079250154571741
sibei 0.6076622192051193
********************************************************
Similarity between 'makan' & 'eat': 0.6577461668802116
Closest 10:
makan 1.0
jiak 0.7007467882204779
go 0.6911439090088933
pple 0.65857421561786
eat 0.6577461668802115
food 0.6575154915623017
kr 0.6545185140294344
sg 0.6473315303433985
heng 0.6422265572697313
beo 0.6354614594882941
********************************************************
Similarity between 'makan' & 'food': 0.6575154915623018
********************************************************
Similarity between 'tw' & 'sg': 0.7339666801539345
Closest 10:
tw 0.9999999999999997
tiong 0.7723149794376185
sg 0.7339666801539344
lidat 0.7330705496009475
hk 0.7258329008490501
taiwan 0.7195021043855226
tiongland 0.7171170137971364
pple 0.7130953678674011
yr 0.7017495747955986
mediacorpse 0.6954931933921777
********************************************************
Similarity between 'kr' & 'sg': 0.7889688950703608
Closest 10:
sg 1.0
go 0.7940974860875252
kr 0.7889688950703608
tiongland 0.7675846859894958
ongware 0.7674045119824121
yr 0.7584581119582794
pple 0.7536492976456339
time 0.7533848231714694
buy 0.751509500730294
tix 0.743339654154326
********************************************************
从上述结果中,我想指出一些有趣的发现。
总的来说,我认为这个模型在理解新加坡英语单词方面已经学得很好,表现也相当不错。
不是最好的,但还过得去。
毫不奇怪,模型已经了解到“马侃”(马来语,意为“吃”),“食物”和“吃”被发现是相似的。但有趣的是,“Jiak”这个词——方言中“吃”的意思,常用于新加坡式英语句子中——被选中,并被认为与“食物”、“马侃”和“吃”相似。
在我看来,这位模特确实很好地学会了一些新加坡英语单词。
下一个有趣的结果来自于国家名称的缩写。当人们阅读结果时,我们知道“sg”指的是“新加坡”,“kr”-韩国,“tw”-台湾,“hk”-香港。然而对于一台机器来说,这并不那么简单,模型并没有这些先验知识。然而,它设法将这些术语组合在一起。
确实非常有趣!
这是另一个验证点,告诉我该模型在新加坡英语单词上训练得相当好。
现在,有一种方法可以帮助我们在向量空间中可视化单词,而不仅仅是盯着数字。这被称为 T 分布随机邻居嵌入(TSNE)。
TSNE 可以粗略地看作是一种将高维数据可视化的降维技术。即 100 维的单词嵌入。其思想是以尊重点之间相似性的方式将高维点嵌入到低维中。
用英文把 100 个维度转换成 2 或 3 个维度,尽可能保留嵌入的信息。
如果我在上面的一些结果上运行 TSNE (n=2 维),并在散点图上显示它,它将看起来像这样:

Figure 6 — TSNE results displayed on a scatter plot
不算太差的结果。
虽然上述结果看起来很有希望,但仍有一些问题需要解决。
我只是在回顾结果时才意识到这一点。下一节将进一步解释。
结果有问题
首先,正如你所看到的,像“lah”,“lor”,“meh”这样的新加坡式英语单词看起来很相似,但是…
那又怎样?它实际上意味着什么?!
想了想,也不完全有用。
新加坡式英语短语通常以双字母(2 个单词)的形式出现,它实际上概括了使用它的上下文的意思。一个很好的例子是图 3——“能”的力量。“我能吗?”vs“残啦!”有着非常不同的含义。
如果我们把“can”和“meh”这两个词分开来看,这个意思就失去了。
因此,单独拥有单词“meh”和“lah”的向量表示在这里并不完全有用。
为了解决这个问题,我需要一种方法将这些单词组合在一起。unigrams 上的 Skip-Gram 火车,是的。但是如果我可以先把这些二元模型转换成一元模型呢?即“Can meh”->“Can _ meh”*注意下划线。
第二个问题没有第一个严重,但值得花些时间来讨论。
这是 TSNE 等降维技术的固有问题。你看,从 100 维缩减到 2 维的信息损失是不可避免的。尽管我喜欢视觉效果,但我意识到我只能通过看数字而不是视觉效果来真正理解结果。
这里没什么好解决的。
只是精神食粮。
现在让我们来看看我解决第一个问题的尝试。
问题的纠正:将二元模型转换为一元模型——引入逐点相互索引(PMI)

Figure 7 — Point-wise Mutual Index (PMI) formula
在意识到新加坡式英语的问题后,我需要一种方法将我的双字母词转换成单字母词,同时保留这些词的信息或意思。
我可以通过计算语料库中所有可能二元模型的 PMI 来做到这一点。
例如,图 7 中的公式表示,取成对单词(即“can meh”)出现的概率的对数,除以每个单词出现的概率。
我的流程分解如下:
如果 x = can,y = meh,
- 统计“can meh”一起出现的次数。
- 数一数“can”单独出现的次数。
- 数一数“meh”单独出现的次数。
- 应用 PMI,得到一个分数。
- 设置一些阈值参数。如果分数高于阈值,则将所有出现的“can meh”转换为“can_meh ”,即,将二元语法转换为一元语法。
- 重新训练整个跳格模型。
从 1 到 5 的步骤实际上就是构建一个短语模型。
所以…我开始寻求重新训练我的 17 小时模型…
我计算了所有可能二元模型的 PMI 分数,并设置了一个阈值变量。我实际上使用了上述 PMI 公式的标准化版本进行计算,但概念是相同的。
归一化 PMI 短语模型技术规范:
- 最小字数= 5
- 阈值= 0.5(范围从-1 到 1)
['as we are left with just 2 days of 2017 hereu2019s a song for everyone here edit to weewee especially lol',
'good_morning click here to download the hardwarezone_forums app']['wah_piang . why every time lidat one . siao liao can meh . wah_lau . can lah . can la . of course medicated_oil . can anot']
正如您所看到的,阈值影响了有多少二元模型被转换为一元模型。我没有成功地将“can meh”的二元模型转换为二元模型,因为如果我将阈值设置得更低,许多其他非二元模型相关的单词将开始转换为二元模型。
可悲的是,这里有一个权衡。
新加坡英语单词嵌入的最终结果
在解释了二元模型之后,我重新训练了这个模型(现在我花了 18 个小时来训练),得到了下面的结果。
注意有多少不相关的二元模型变成了二元模型?即“为了她”,“和她在一起”。这就是我所说的建模步骤中的权衡。
因为所有这些不相关的二元模型,一些分数下降了。
Similarity between 'her' & 'she': 0.8960039000509769
Closest 10:
her 1.0
she 0.8960039000509765
when_she 0.7756011613106286
she_is 0.7612506774261273
with_her 0.7449142184510621
who 0.7348657494449988
for_her 0.7306419631887822
shes 0.7279985577059225
face 0.7192153872317455
look_like 0.718696491400789
********************************************************
Similarity between 'his' & 'he': 0.8129410509281912
Closest 10:
his 1.0
he 0.8129410509281914
him 0.804565895231623
he_was 0.7816885610401878
when_he 0.7747444761501758
that_he 0.7724774086496818
in_the 0.7622871291423432
himself 0.7611962890490288
was 0.7492507482663726
with_his 0.7241458127853628
********************************************************
Similarity between 'jialat' & 'unlucky': 0.1258840952579276
Closest 10:
jialat 0.9999999999999996
den 0.7104029620075444
liao 0.7050826631244886
chiu 0.6967369805196841
heng 0.686838291277863
hahaha 0.6860075650732084
riao 0.6810071447192776
la 0.6804775540889827
le 0.676416467456822
can_go 0.6756005150502169
********************************************************
Similarity between 'jialat' & 'bad': 0.4089547762801133
Closest 10:
bad 1.0
but 0.8388423345999712
good 0.8244038298175955
really 0.8192112848219635
i_think 0.7970842555698856
very 0.7965053100959192
like 0.785966214367795
feel 0.783452344516318
too 0.7788218726013071
i_feel 0.7721110713307375
********************************************************
Similarity between 'bah' & 'ba': 0.5793487566624044
Closest 10:
bah 1.0
say 0.6595798529633369
lah 0.6451347547752344
dun 0.6449884617104611
sure 0.627629971037843
bo_bian 0.6251418244527653
this_kind 0.6223631439973716
ppl 0.6196652346594724
coz 0.61880214034487
mah 0.6146197262236697
********************************************************
Similarity between 'lah' & 'la': 0.8863959901557994
Closest 10:
lah 0.9999999999999998
la 0.8863959901557996
meh 0.8739974021915318
mah 0.8641084304399245
say 0.8589606487232055
lor 0.8535623035418399
u 0.8304372234546418
leh 0.8275930224011575
loh 0.8189639505064721
like_that 0.8170752533330873
********************************************************
Similarity between 'lah' & 'leh': 0.8275930224011575
********************************************************
Similarity between 'lah' & 'ba': 0.7298744482101323
********************************************************
Similarity between 'lah' & 'lor': 0.8535623035418399
********************************************************
Similarity between 'lah' & 'hor': 0.7787062026673155
********************************************************
Similarity between 'lor' & 'hor': 0.7283051932769404
Closest 10:
lor 1.0
u 0.8706158009584564
lah 0.8535623035418399
mah 0.8300722350347082
meh 0.8271088070439694
or_not 0.8212467976061046
can 0.8202962002027998
la 0.8136970310629428
say 0.8119961813856317
no_need 0.8113510524754526
********************************************************
Similarity between 'walau' & 'walao': 0.37910878579551655
Closest 10:
walau 0.9999999999999998
liao_lor 0.595008714889509
last_time 0.5514972888957848
riao 0.5508554544237825
no_wonder 0.5449206686992254
bttorn_wrote 0.5434895860379704
hayley 0.5418542538935931
y 0.5415132654837992
meh 0.5397241464489063
dunno 0.5377254112246059
********************************************************
Similarity between 'makan' & 'eat': 0.6600454451976521
Closest 10:
makan 0.9999999999999998
go 0.7208286785168599
den 0.697169918153346
pple 0.6897361126052199
got_pple 0.6756029930429751
must 0.6677701662661563
somemore 0.6675667631139202
eat 0.6600454451976518
lo 0.6524129092703767
there 0.649481411415345
********************************************************
Similarity between 'makan' & 'food': 0.4861726118637594
********************************************************
Similarity between 'tw' & 'sg': 0.6679227949310249
Closest 10:
tw 1.0
tiong 0.7219240075555984
taiwan 0.7034844664255471
hk 0.7028979577505058
tiongland 0.6828634228902605
mediacock 0.6751283466015426
last_time 0.6732280724879228
sg 0.6679227949310248
this_yr 0.6384487035493662
every_year 0.6284237559586139
********************************************************
Similarity between 'kr' & 'sg': 0.7700608852122885
Closest 10:
sg 1.0
in_sg 0.8465612812762291
kr 0.7700608852122885
pple 0.7599772116504516
laio 0.7520684090232901
go 0.7509260306136896
sure 0.7156635106866068
come 0.7116443785982034
tiongland 0.7015439847091592
ongware_wrote 0.6974645318958415
********************************************************
Closest 10:
wah_piang 1.0
tsm 0.4967581460786865
scourge_wrote 0.4923443232403448
aikiboy_wrote 0.4887014894882954
sian 0.4871567208941815
ah_ma 0.48058368153798403
lms 0.4790804214522433
ruien 0.47420796750340777
xln 0.46973552365710514
myolie 0.4682823729806439
********************************************************
简而言之,上面的结果比以前的结果更糟糕。
我认为这个问题来自于数据的缺乏。如果我有更多人们使用“can meh”或“can lah”的例子,短语模型会在没有我设置低阈值的情况下挑选出这些二元模型。0.5 门槛在我看来真的很低。
我抓到了太多的假阳性。
垃圾进,垃圾出,对吗?这正是这里发生的事情。
未来作品
正如你所想象的,我仍然有办法调整我的模型,为我未来的作品获得像样的新加坡式英语单词嵌入。
我会有不同的做法:
- 重新编写我的脚本来运行分布式。或者更好的是,在 GPU 中运行的代码。这将允许我在更大的语料库上运行,即整个硬件区域,而不仅仅是其中的一个线程。
- 训练一个更好的短语模型。即 PMI 部分。
现在,我将为第一个模型保留单词 embeddings。
编辑:如果你对如何处理语料库中的拼写错误、缩写或其他 OOV 单词感兴趣,我已经写了另一篇关于它的文章这里!
结尾注释
我确实从这个练*中学到了很多,我希望通过我分享我的方法、思维过程和结果,你们也能够用你们各自的当地语言做同样的事情。
就这样,再见了,朋友们!
希望你和我一样喜欢阅读这篇文章!
如果这篇文章对任何人有帮助,请随意分享!
如果您正在寻找 NLP 数据集,请点击这里 查看我创建的精选列表!😃
LinkedIn 简介:蒂莫西·谭
创建您独特的图像数据集
使用谷歌视觉和谷歌照片创建我自己的数据集

Photo by Alice Achterhof on Unsplash
在进行我的第一个 ML 项目时,我需要一个特定的数据集。我用我两个女儿的面部图像做了一个二元分类模型。
我开始手工裁剪我的私人照片目录。这既乏味又怪异(坐在会议上剪小女孩的照片)
在网上搜索人脸识别和裁剪代码,我找到了不同的解决方案。但是因为我的主要目标是体验机器学*,所以我寻找一种更优雅的方式——一种利用 ML 能力的方式。
谷歌视觉 API 是我的答案。对我来说,这是一个有点棘手的开始,所以我想我应该写下我的经历。
如果你想直接进入代码,它就在这里。
从阴霾的心到阴霾的心
为了使用谷歌云平台(GCP),我必须设置一些小的环境先决条件:
- 打造一个 GCP 项目——小菜一碟。
- 启用视觉应用编程接口 —简单易行。
- 启用计费 —无需动脑筋。
- 创建服务帐户 —漫步公园。
太好了,一次性搞定。它也可能在未来的项目中有用。时间花得值。
一个有远见的合作实验室
我喜欢使用 google colab,这是一个云中的 jupyter 笔记本,甚至可以给你一个免费的 GPU。它有 Python 2 或 3 和一堆原生预装库,如果你不知道,看看这篇可爱的文章。让我们开门见山:
- 安装视觉
2.安装 google drive:
3.通过设置环境变量 GOOGLE_APPLICATION_CREDENTIALS,为您的应用程序代码提供您之前获得的身份验证凭证。你也可以使用代码来完成,但我发现这种方式更容易,我已经将文件上传到我的驱动器,并且我正在以这种方式使用它,而不是直接上传到 colab。
现在是有趣的部分。

The fun part
像热的一样修剪它
vision 客户端 API 很大,所以我将把重点放在这个任务所需的最低限度上。我的裁剪代码是来自 vision 教程网站的两个例子的合并。人脸检测教程和裁剪提示教程。
我们先来看看人脸检测代码。这非常简单:我们创建一个服务对象,然后从 Image 资源请求 face_detection 注释。
返回值是一个面标注对象的数组。这些对象包含大量的数据,但是我们感兴趣的是BoundingPoly对象(有两个版本:bounding poly 和 fdBoundingPoly,它是多边形的一个更紧凑的版本,只有皮肤)。
现在你已经有了检测到的人脸的坐标,你可以做任何你想做的事情,在它们周围画一个矩形,裁剪它们或者添加胡子。
我们在这里有一个任务,所以让我们看看作物代码。我们使用枕头库根据我们从检测方法中得到的顶点来裁剪面,并将每个面保存在不同的文件中。
用照片标记照片
创建我们的数据集现在很容易,我们需要做的就是将一个装满图片和代码的库流式传输,我们很快就会有一个很好的数据集。
但是,唉,许多不同的面孔。我的照片不仅仅是我孩子的照片。有时候一张友好的脸是你最不想看到的。我们需要把小麦从谷壳中分离出来等等。

I just need this one.
让谷歌再一次帮助我们。使用 Google 相册强大的 ML 功能,我们可以轻松地标记我们的数据。请注意,我将所有裁剪的面保存到了我驱动器上的一个文件夹中。这样,照片可以自动索引并标记它们。
例如,如果我只想要我儿子的微笑,我只需要在搜索框中搜索组合。

name + smile
就这样,你现在可以选择所有带标签的图片并一次性下载。瞧——很快(很短的时间)就有了一组带标签的图像。
你可以很容易地安排它,并把它送给 Keras ImageDataGenerator 例如。
基于主题词表征的句子嵌入的创建
走向普遍语言理解的途径
我研究单词和句子嵌入已经一年多了,最*还写了这方面的硕士论文。我现在正在展示的成果也在这里发表了,并且是与 SAP 和列支敦士登大学合作的成果。在下面的博文中,我不会详细解释嵌入。这篇文章是概念性的,总结了我的发现。

Photo by Romain Vignes on Unsplash
基础
单词向量是高维空间中在语义上表示相应单词的位置。在那个空间里,意思相*的单词彼此靠得更*。因此,同义词具有几乎相同的向量,并且彼此靠*。
相同的概念可以应用于句子,而相似的句子在高维空间中彼此靠*。
为了创建单词向量,存在几种方法。两个非常常见的算法是 Word2Vec [2]和 GloVe [3]。两者都根据单词出现的上下文将单词编码成任意维度。对于 Word2Vec,上下文是周围单词的窗口。对于 GloVe,它是整个句子或文档。
因为一个单词是通过拼写来识别的,所以每个单词只存在一个向量。使用这些方法,同形异义词,即拼写相同但意义不同的词,不会被认为是彼此不同的。因此,当在烹饪食谱中谈论苹果时,如果在大型和多样化的语料库上训练,水果的相应词向量将引入关于技术公司的不适当的知识。
脸书研究中心的一个句子嵌入器,叫做 infer sent【2】,使用了 GloVe,因此也忽略了同形异义词。这种无知方法的使用也给句子向量引入了不适当的知识。因此,我们创造了一种新的方法来解决这个问题。
建议
我们建议使用能够区分同形异义词的话题感知词向量,而不是使用无知的和已经训练好的手套向量。我们的工作面向主题词嵌入论文[3],但稍微修改了他们的方法。
主题感知词向量是使用 Word2Vec 创建的。但是在 Word2Vec 运行训练语料库之前,我们通过使用带有 Gibbs 采样的 LDA [4]主题建模器对其进行了修改。
LDA 用于根据文档中出现的单词,在给定数量的主题中对文档进行聚类。一个很好的副作用是,LDA 不仅给整个文档分配主题,而且给所有单个单词分配主题,然后使用这些单词来区分文档的主题。下面的例子展示了一个语料库经过 LDA 处理后的样子。

LDA assigns topics to words based on their surroundings
因此,我们不再有常用词,而是包括主题的新伪词,例如 apple:1,其中 1 是主题“水果”的 ID。这些新单词充当我们的数据,我们从这些数据中创建单词向量。因此,对于苹果的每个版本——apple:1,apple:2 等等——我们都有自己的向量,而不是只有一个。
我们有两种不同的方法从前面提到的伪单词中创建单词向量…
整体单词嵌入

The vectors for apple:0 and apple:1 do not share any information.
这种创建主题词向量的方式简单地将伪词视为与具有相同词的其他伪词不相关的完整词,例如,apple:1 和 apple:2 被认为没有任何共同点。这是实现起来最简单、最快速的方法,因为大多数 Word2Vec 库只是简单地获取这个转换后的文本,并学*每个单词(这里是每个伪单词)的单词向量。
串联单词嵌入

The vectors for apple:0 and apple:1 share a common vector for apple (black) and have a appended topical vector for topic 0 (blue) and topic 1 (green), respectively.
另一种方法是连接两个向量,一个单词和一个主题向量,以创建主题单词向量。单词向量是通过 Word2Vec 在原始的、无差别的数据集上学*的。然而,主题向量需要一个更复杂的过程。我们提出的所有不同方法都在[1]中进行了描述。最有希望的是使用加权平均值(也称为 Phi 方法)。
加权平均方法获取词汇表中所有单词的向量,并对它们进行平均,以生成主题向量(向量的附加部分)。在平均之前,每个单词向量被加权一个关于该单词对于相应主题的重要性的数字。这个数字是一个介于 0 和 1 之间的值。对于一个主题,所有的重要性数字总和约为 1。这些数字实际上是概率 p(w|t) 告诉我们一个单词在一个主题中被提及的可能性。它们是在训练 LDA 时计算的。
示例:想象一个语料库,其中包含词汇{"apple "," pie "," computer"},主题{"fruits "," technology"},训练词向量{"apple": [1,0,0]," pie": [0,1,0]," computer": [0,0,1]},以及概率{"fruits": [0.5,0.5,0.0]," technology": [0.5,0.0,0.5]}。主题向量的计算如下。v("水果")= v("苹果")0.5 + v("馅饼")0.5 + v("电脑")0.0 = [0.5,0.5,0.0]。v("科技")= v("苹果")0.5 + v("馅饼")0.0 + v("电脑")0.5 = [0.5,0.0,0.5]。因此,主题“水果”中单词“苹果”的向量是[1.0,0.0,0.0,0.5,0.5,0.0],主题“技术”中的向量是[1.0,0.0,0.0,0.5,0.0,0.5],而苹果的基本向量分别与“水果”和“技术”的主题向量连接。
实验
为了对主题和非主题版本的推断方法进行公平的比较,我们创建了一个基线模型,它是一个使用 Word2Vec 单词嵌入的推断模型,该单词嵌入是我们在训练主题单词嵌入的相同数据上创建的。然后将使用这些主题词向量作为基础的主题版本的推断与基线模型进行比较。这确保了在实验开始之前,许多其他的不确定性被消除。
对于基线和局部模型都获得了以下结果:

BASELINE vs. Topical versions — bold is best for the respective task, underlined is better than baseline
如第一个表所示,基线模型在有关联任务(蓝色背景)时明显更好。但是,对于分类任务,主题区分的版本显示出优势。在 9 个分类任务中的 7 个中,我们的主题版本超过了基线。这有力地证明了主题区分确实提高了分类任务的性能。
此外,我们通过向单词向量添加主题部分,扩展了原始推理的手套模型,甚至可以超过最先进的结果:

BASELINE vs. original InferSent (Facebook) vs. extended (topical) GloVe — bold is best for the respective task
即使扩展模型(1hot 和 Phi)采用第一个表中较小数据集训练的主题向量,结合预训练的手套向量,它们也能够在某些任务中取得比脸书的原始推断模型更好的结果。
结论
通过我们进行的实验,有可能表明词向量中的歧义是有问题的,并且降低了大多数分类任务的性能。为了解决真正的机器文本理解,还需要更多的研究。然而,该项目表明人类语言具有更高的复杂性,并为进一步的文本嵌入研究提供了方向。
在从事这个项目的过程中,一些技术,如 ELMo [5]和 BERT [6]已经发布,它们可以创建上下文单词表示。这也是正确理解文本需要语境的证据。
参考
[1] Wenig,P. (2018)。基于主题词表征的句子嵌入的创建。https://doi.org/10.13140/rg.2.2.10288.00007
[2]t .米科洛夫,陈,k .,科拉多,g .,&迪安,J. (2013 年)。向量空间中单词表示的有效估计。ArXiv:1301.3781【Cs】。从 http://arxiv.org/abs/1301.3781取回
[3]j .潘宁顿、r .索彻和 C. D .曼宁(2014 年)。GloVe:单词表示的全局向量。在自然语言处理中的经验方法(EMNLP) (第 1532–1543 页)。借鉴于http://www.aclweb.org/anthology/D14-1162
[4] Conneau,a .,Kiela,d .,Schwenk,h .,Barrault,l .,和 Bordes,A. (2017 年)。来自自然语言推理数据的通用句子表示的监督学*。 arXiv 预印本 arXiv:1705.02364 。从 https://arxiv.org/abs/1705.02364取回
[5]刘,杨,刘,钟,蔡东生,孙,米(2015).主题词嵌入。在 AAAI (第 2418–2424 页)。检索自http://www . aaai . org/OCS/index . PHP/AAAI/aaai 15/paper/download/9314/9535
[6]布雷博士、Ng、A. Y .、乔丹博士(2003 年)。潜在狄利克雷分配。
[7] Peters,M. E .,Neumann,m .,Iyyer,m .,Gardner,m .,Clark,c .,Lee,k .,& Zettlemoyer,L. (2018)。深层语境化的词语表达。ArXiv:1802.05365【Cs】。从 http://arxiv.org/abs/1802.05365取回
[8] Devlin,j .,Chang,m-w .,Lee,k .,& Toutanova,K. (2018 年)。BERT:用于语言理解的深度双向转换器的预训练。ArXiv:1810.04805【Cs】。从 http://arxiv.org/abs/1810.04805取回
创造性自动化是新的媒介
在自动化焦虑的新浪潮中,创意人员开始意识到自动化提高效率的潜力,一些人利用技术创造新的表达形式,反映数字时代及其所有可能性。
自动化创造力
*年来,我们已经看到人工智能能够创造出值得在主流画廊展示的艺术;我们已经看到他们的画被佳士得以高达 432,500 美元的价格拍卖掉,我们也读过他们的获奖小说讲述了一个机器人决定开始写作的那一天(谁不想看呢?).创造性自动化的这些新进展创造了一种独特的“自动化焦虑”,将人类创造力的价值置于其中心——如果人工智能可以创造艺术,那么我们作为有意识的智能生物的价值还剩下什么?
目前,许多创意专业人士认为,创意自动化只会有助于而不是有害的,尤其是艺术家和设计师经常发现自己必须完成的重复过程。在 Adobe 委托的一份 2018 报告中,他们发现超过一半的创意专业人士受访者事实上对人工智能和机器学*能够给他们的创意过程带来什么非常感兴趣,只有 12%的人声称完全不感兴趣。Adobe 人工智能战略发展高级经理克里斯·达菲(Chris Duffey)在接受 CMO.com 采访时说:“就在几个月前,创意人员还在观望。”。“今天,他们意识到人工智能存在于他们已经在使用的许多技术中,它让事情变得比以前更直观。”
Duffey 认为,通过自动化阴影背景、标记文件和一般组织等日常任务,创意专业人士将“能够继续发展——向新的和更复杂的创意学科发展,如 3D 和沉浸式设计,并在原创性和破坏性思维方面取得新的突破。”
尽管人工智能和计算机艺术正在引发关于人类创造力是否可以被取代的讨论,但重要的是要记住,当今大多数创造性自动化工具最常被用于非创造性能力。虽然计算机可以记忆和处理比人脑多得多的信息,但它们并不真正理解它们为什么要做这些事情,也不理解自由意志的概念。尽管它们很快,但 AI 没有认知、意识,也没有同理心来做出人类定期做出的相同决定。
人工智能的发展还有很长的路要走,但计算机算法不太可能在短期内取代我们的创造力和表达能力。相反,在创造力的世界里,机器的价值将是尽可能地补充人脑,而不是取代它。
前面提到的 Adobe 报告的主要作者 Andreas Pfeiffer 同样认为,“创造力不仅仅是你创造了什么,而是你为什么创造它,”而自由艺术总监 Christoph Gey 解释道:“不是因为你知道工具,你才是设计师。”“自动化”这个词有完全取代工作的含义,但它实际上是一种和其他技术一样的技术:它旨在通过加快我们工作中看似平凡和重复的部分,给我们更多的时间。毕竟,就其能力和命令而言,人工智能仍然是由我们设计的,它将帮助我们做我们需要帮助做的任何事情,但它将无法实现真正的人类创造力,这种创造力是由自然的必要性产生的(即。冲动和决策)、经验和情绪。
电脑音乐与电脑辅助音乐
虽然人工智能确实渗透到许多创作领域,但它在音乐领域表现得最为明显。还记得 Spotify 的“假艺人”争议吗?Spotify 被指责为了支付更低的版税而提供假冒艺术家创作的歌曲。据报道,从那时起,这些冒牌艺术家已经在积累了超过 1 亿次的播放量,每个月大约有 100 万的听众。因此,钢琴乐器和爵士乐在这些播放列表中卷土重来,但社区怀疑(并暗示)没有人在另一端接受表扬。如果你是一名音乐家,自然你会担心你辛苦获得的创造力现在被人工智能算法大量取代;你不敢苟同,你是对的。
加州大学圣克鲁斯分校荣誉退休教授、音乐技术公司 Recombinant Inc .的联合创始人 David Cope 早在 1981 年就已经将数千行 LISP 代码编织成音乐生成算法。他的电脑程序涉足“音乐智能”的实验(你可以在 Spotify 上找到他的电脑合成歌曲);具体来说,能够理解音乐风格并相应地复制它,同时仍然遵守音乐规则的程序(关键词:复制)。类似地, MuseNet 是一个深度神经网络,可以生成“用 10 种不同乐器创作的 4 分钟音乐作品,可以结合从乡村音乐到莫扎特到披头士的风格。”这两个例子都非常成功地实现了他们的提议,即复制现有的“真正的”艺术家和作曲家的音乐风格,但还需要一点时间,人们才能期待计算机根据自己的思想和情感创作音乐。
虽然有些人将他们的算法集中在复制音乐风格上,但其他人已经将机器学*的优势作为他们作曲过程的一部分。一个最显著的例子是谷歌的项目 Magenta ,该项目于 2016 年启动,旨在推动“机器学*作为创意过程中的一种工具”的边界。作为一个开源研究项目,Magenta 是音乐家、艺术家和开发人员的游乐场,允许他们将机器学*作为当今和未来最重要的乐器进行探索。通过 Magenta 创建的一些特色项目包括使用神经网络的钢琴转录,用机器学*生成的声音制作音乐,以及用机器学*模型进行交互式干扰。与音乐创作项目不同,Project Magenta 专注于技术和人类创造力之间的整合,强调当我们将两者结合起来时,可能性是多么的无限。
一起更好
在音乐领域之外,当自动化被用作人类创造力的工具或延伸时,仍有许多实例证明其价值。早在 2012 年,威廉和弗洛拉·休利特基金会(William and Flora Hewlett Foundation)发起了一项奖励,欢迎程序员设计一种自动化软件,帮助在州测试中给论文评分。为了降低成本和增加周转时间,这种竞争还将有助于确保学校系统不会在考试中排除论文问题,而采用选择题,选择题更不能体现学生的批判性推理和写作技能。虽然人们可能会认为给论文评分需要人类思维的经验和创造性思维,但这次比赛的结果证明并非如此:159 份参赛作品中最好的一份产生了与人类评分几乎相同的分数。这些评分程序的关键是,他们不评估文章的价值;更确切地说,他们是在模仿一个训练有素的教育者如何给文章打分。通过采用与经验丰富的教育工作者相同的评分标准,该计划在完成任务时充当该教育工作者的扩展或克隆,这直接减少了时间和相关成本。

Sougwen Chung, Drawing Operations Unit: Generation 2 (Mimicry), 2016 — — NTT InterCommunication Center [ICC], Tokyo Japan — — Courtesy of the Artist — — sougwen.com
在创意机器的一个更具协作性的应用中,Sougwen Chung 的机器人绘图臂,称为 DOUG 2(绘图操作单元:第 2 代),利用在 Chung 的绘图手势上训练的神经网络来学*艺术家的绘图风格。在每一次展览中,钟都强调这个项目的合作性,当她在画布上画画时,她把道格 2 放在自己旁边。该项目的核心是“探索人类和机器人合作的艺术潜力”。使用计算机视觉算法,DOUG 2 从艺术家的手中学*,并展示了一种受艺术家启发的风格,以开发一种“人和机器的复合行为”。其结果不仅是复杂线条和图案的惊人集合,也是一台机器与人类一起创造原创和其他无法实现的东西的现场演示。
我们定义创造力
通过了解在创造性过程中机器可以取代什么,我们越来越多地发现,我们所做的许多创造性工作涉及到可以轻松自动化的重复性任务。不能自动化的事情也是我们努力为自己定义的事情。例如,趋势和品味的短暂定义是由人类的方向决定的,即使是最发达的机器算法或数学公式也无法预测。
以亚马逊的时尚助手 Echo Look 为例,根据其亚马逊页面,这是一款“带 Alexa 的免提相机和时尚助手”。通过 Echo Look,用户可以拍摄照片和视频,然后根据当前趋势向 Alexa 寻求造型建议。然而,正如凯尔·查卡发现的,Alexa 缺乏自发性和有助于主导时尚趋势的创新思维。相反,Alexa 试图将你的衣服与盒子里已经有的衣服搭配起来。趋势,尤其是时尚,是由人类随意决定的;其实很多时候,最成功的设计师都是颠覆潮流的人。从这个意义上说,Echo Look 只不过是第二种意见——不是帮助你创造自己的风格,而是确保你看起来不像那么糟糕。
尽管人工智能在排序和组织数据以产生一致的分析结果方面非常有帮助,但它们仍然远远没有创造力——即创造和决定趋势和品味的能力。这并不是说机器在创造力中没有位置——很可能它们很快就会成为我们创造过程的工具,但也许会把创造方向的角色留给人类来完成,因为创造力毕竟是我们自己定义和联系的东西。这是一个谨慎的概念,许多人认为它定义了人类,它已经变得与人类思维如此相关,以至于两者几乎不可分。
当我们赞美艺术时,它证明了人类创造和创新的能力,而不一定是所说的创造力的结果。因此,在我们能够接受和识别非人类创造力的迹象之前,还需要一些时间,我怀疑这场关于机器人和算法是否真的可以“有创造力”的激烈辩论将持续一段时间,但这并不意味着计算机不能在此期间帮助我们进一步实现自我表达的目标。有了明确的目标,自动化可以帮助简化、实现甚至发明曾经不存在的表达形式。通过将技术与艺术相结合,我们正在定义人类创造力的新时代,其标志是科学的进步和我们从环境中创造一切的能力。
原载于 2019 年 7 月 10 日https://automtd.co。
信用卡欺诈检测
在虚拟世界中保持警惕

Photo by Ales Nesetril on Unsplash
本文的代码可以在我的 Github 上找到。
在今天的世界里,我们正坐在通往无现金社会的快车上。根据世界支付报告,2016 年非现金交易总额比 2015 年增长了 10.1%,达到 4826 亿笔交易!这是巨大的!此外,预计未来几年非现金交易将稳步增长,如下所示:

World Payments Report Forecast
现在,虽然这可能是令人兴奋的消息,但另一方面,欺诈交易也在上升。即使实施了 EMV 智能芯片,我们仍会因信用卡欺诈损失大量资金:

CC Fraud Report from CreditCards.com
我们能做些什么来降低风险?虽然有很多方法来限制损失和防止欺诈,但我会向你介绍我的过程,并向你展示我的发现。
收集数据
首先,我从包含 285,000 行数据和 31 列数据的 Kaggle 数据集中收集数据。在所有的列中,唯一最有意义的是时间、金额和类别(欺诈与否)。为了保护用户身份,其他 28 列使用似乎是 PCA 降维的方法进行了转换。
从时间上来说,数据本身很短(只有 2 天),这些交易是由欧洲持卡人进行的。
探索性数据分析
现在我们有了数据,我想对我前面提到的三个列(时间、数量和类别)进行一些初步的比较。
时间

Time Distribution of Credit Card Data
鉴于这个分布是两天的数据,它将遵循我期望看到的普通消费者的趋势。大多数购买是在白天进行的,随着人们下班/放学回家,购买量会减少,直到第二天。
金额

Amount Distribution of Credit Card Data
虽然绝大多数交易量很低,但这种分布也是意料之中的。大多数日常交易不是非常昂贵(大多数是
类(欺诈/非欺诈)

Fraudulent and Non-Fraudulent Distribution
在这个数据集中,只有 492 个欺诈交易。这只占该数据集中所有交易的 0.173%!不过,我的目标是尽可能多地抓住那些讨厌的骗子。
数据处理
我首先通过使用热图来确保数据中没有任何强烈的共线性。

Feature Heatmap
在这个例子中,我没有添加任何数字,因为这对读者来说很难看到。我在寻找任何表现出高度相关性的东西,我发现了一些:
- 时间和 V3 (-0.42)
- 金额和 V2 (-0.53)
- 金额& V4 (0.4)
虽然这些相关性与其他相关性相比相对较高,但我不认为它高到足以冒多重共线性的风险。
标准定标器开启时间和数量
在处理这类分类问题时,StandardScaler 是一个常用工具。它将数据转换为平均值为 0、标准差为 1 的数据,从而将数据标准化为正态分布。特别是在如此大范围的数量和时间上工作,我发现在运行测试之前缩放我的数据提供了更好的结果。
我使用训练集初始化了 fit,然后在将所有内容运行到模型中之前,对训练集、验证集和测试集进行了缩放。
构建模型
现在是有趣的事情!有这么多数据点要处理,我决定在创建任何模型之前进行 60%的训练/20%的验证/20%的测试。
另一个需要解决的问题是高度不平衡的数据集。有了这么多的非欺诈性交易,我实现了随机欠采样来减少非欺诈性交易的数量,并使其与欺诈性交易的数量相匹配。
有了这个设置,我现在可以通过一些模型运行数据了!
逻辑回归和随机森林
通过逻辑回归和随机森林模型,我试图确定这些数据是否有助于模型发现欺诈交易。
逻辑回归是一种统计模型,试图将预测错误的成本降至最低。随机森林是决策树的集合,它们共同预测交易是否是欺诈性的。
我通过这两个模型运行我的数据,得到了一些非常好的结果。
准确度、召回率、精确度和 F1 分数
对于本节,我将在公式中使用这些缩写:
TP =真阳性。欺诈性交易模型预测为欺诈性。
TN =真阴性。正常交易模型预测为正常。
FP =假阳性。正常交易模型预测为欺诈。
FN =假阴性。欺诈性交易模型预测为正常。
准确(性)

准确性是评估分类模型的一个度量标准。这是模型预测正确的比例。虽然准确性似乎是衡量模型性能的一个很好的指标,但在这种不平衡的数据集中使用它会有很大的负面影响。如果我说所有的 285,000 笔交易都是正常交易(没有欺诈),我会有 99.8%的准确率!然而,这里的问题是所有欺诈交易的明显缺失。特别是对于不平衡的数据集,有更有效的指标可以使用。
回忆

Recall 回答问题:在欺诈交易中,我们的模型能正确识别的交易占多少百分比?在我们最好的模型中,我们的召回率是 89.9%。这意味着在所有数据中,它能正确识别 89.9%的欺诈交易。这可以改进,但我觉得这是一个很好的起点。
精度

Precision 回答了以下问题:在所有被预测为欺诈性的交易中,实际上有多少百分比是欺诈性的?在我们的最佳模型中,97.8%的欺诈交易被捕获。这是一个非常好的指标!
F1 得分

F1 分数将召回率和精确度合并为一个指标,作为两者的加权平均值。与召回率和精确度不同, F1 同时考虑了误报和漏报。在像这样的不平衡类中,F1 在确定模型性能方面比准确性更有效。
结果

Model Results
在训练每个模型之后,这些是最终结果。逻辑回归和随机森林模型的所有分数对我们的数据集来说都非常有希望!每个模型都有很高的真阳性率和很低的假阳性率,这正是我们要找的。
最后,我将查看 ROC 曲线、混淆矩阵以及每个模型的累积情况。
接收机工作特性(ROC)和混淆矩阵
接收机工作特性(ROC)
ROC 是在各种阈值下对分类问题的性能测量。它本质上是一条概率曲线,曲线下面积(AUC)得分越高,该模型预测欺诈性/非欺诈性交易的能力就越强。

Out-of-sample ROC Curve
在上图中,逻辑回归和随机森林的 AUC 分数相当高,这是我们希望看到的。值得注意的是,曲线上的每个点都表示一个阈值。随着我们沿着曲线进一步向右移动,我们捕获了更多的真阳性,但也招致了更多的假阳性。这意味着我们捕获了更多欺诈交易,但也将更多正常交易标记为欺诈交易。根据上一节中的表格,找到的理想阈值是:
- 逻辑回归:0.842
- 随机森林:0.421
在这些阈值下,我们捕获欺诈交易的最佳数量,同时尽可能降低误报率。有了这些阈值,我们可以使用混淆矩阵来可视化每个模型的效果:
混淆矩阵—逻辑回归

Logistic Regression Confusion Matrix
在这个样本外测试集中,阈值为 0.842,我们捕获了 98 个欺诈性交易中的 88 个,并错误地将 1,669 个交易标记为欺诈性交易。我可以想象这些情况就像当我去另一个州而没有告诉我的银行我在旅行,当我用我的卡买了一些咖啡后,他们给我发短信要求确认。
混淆矩阵——随机森林

Random Forest Confusion Matrix
在阈值为 0.421 时,随机森林模型与逻辑回归模型具有相似的结果。随机森林模型捕获了 88/98 的欺诈性交易,但与逻辑回归相比,它表明 9 个正常交易是欺诈性的。这两种模式都很有前途,最终都给出了很好的结果。
结论
发现欺诈性信用卡交易非常重要,尤其是在当今社会。有很多方法来捕捉这些实例,看到公司如何在日常基础上处理这些问题真的很酷。
我喜欢发现异常,所以完成这个项目对我来说是一大乐趣。我希望我能够很好地解释我的发现,非常感谢阅读!
信用卡不应该是黑色的

“File:Insert Credit Card (61471) — The Noun Project.svg” by Icons8 is marked under CC0 1.0. To view the terms, visit http://creativecommons.org/publicdomain/zero/1.0/deed.en
信用卡不应该是黑色的。我们在西班牙经历了惨痛的教训,但我们最*发现了另一个黑卡不是好主意的例子。
一位非常知名的软件开发人员在本月早些时候发布了这条推文,他在其中声称,用于信用评分的全新 Apple Card 算法是性别歧视的。
当苹果联合创始人史蒂夫·沃兹尼亚克加入谈话时,事情变得更糟,他基本上声称自己也遭受了同样的问题。
苹果公司和高盛公司对这一病毒式的新闻做出了回应,声明该算法是由第三方针对偏见进行评估的,并且性别没有被用作该算法计算信贷限额的输入数据。
事实是在美国,性别不能作为任何信贷决策的一部分,正如美联储规定的。
“《平等机会信贷法案》在很大程度上禁止在信贷承销、定价、报告和评分中使用人口统计信息,包括性别。”
然而,性别不是一个算法变量并不会使情况变得更好。事实上,正如 Wired 在本文中指出的,这让事情变得更糟:
这个解释有双重误导性。首先,算法完全有可能进行性别歧视,即使它们被编程为对该变量“视而不见”。另一方面,对性别这样重要的事情故意视而不见只会让公司更难发现、防止和扭转对这一变量的偏见。
正如凯茜·奥尼尔在一次关于苹果卡事件的采访中所指出的:
我们必须发展一种新的谈论方式,因为旧的谈论方式已经行不通了。旧的谈论方式是,“让我们不要明确地使用种族或性别。”当我们没有收集关于彼此和我们自己的大量数据时,这种方法就起作用了,而且它不是到处都可以得到的。但是现在我们可以根据各种不同的指标来推断种族和性别。对于那些对推断这类事情感兴趣的公司来说,在 wazoo 上有这类课程的代理。从统计学上来说,它们比随机猜测要好很多。仅仅说“我们不要明确使用种族和性别”已经不够了。
这就是为什么,正如我在上一篇短文(计算机说不,中介绍的,我们不仅需要算法的可解释性,还需要可追溯性和可审计性,将我们从可解释的人工智能转移到可追溯和透明的人工智能,这也是为什么我们不应该让信用卡成为黑色的,黑色的算法盒子。
如果你喜欢阅读这篇文章,请 考虑成为会员 以便在支持我和媒体上的其他作者的同时,获得每个故事的全部信息。
信用风险:无监督的客户聚类

大量使用机器学*解决方案的行业之一是银行业。特别是,让我们暂时集中在消费信贷领域:它是指任何涉及私人行为者(可能是一个人而不是一个家庭)和银行的业务。消费信贷的理念是,只有在客户不会违约(也就是说,不会偿还债务)的情况下,银行才会从提供信贷中获益。事实上,一旦被认为是有信誉的并获得信贷,客户将与银行一起计划一个分期偿还计划,根据该计划,他不仅必须偿还债务,而且必须偿还利息。
因此,如果从一个角度来看,发放信贷是银行的收入之一(因为利息),从另一个角度来看,它包含了相当大的风险。这就是为什么大量的时间和金钱被投入到分析客户的历史,*惯和偿还债务的可能性。
为此,银行一直依赖统计模型(尤其是评分模型),然而今天,在机器学*算法的帮助下,它们对未来还款的预测要可靠得多。
第一种方法可能是根据银行客户的历史数据训练一种算法,看看哪些是偿还债务的人和没有偿还债务的人的特征。然后,当一个新客户来到银行时,算法会对他进行评估,并说明他是否会偿还债务。
正如你所看到的,我们正在讨论监督学*:历史数据带有标签(可信或不可信),因此算法将在训练集上训练,并在测试集上评估。
然而,在这篇文章中,我将提出这个问题的非监督方式:想象我们被提供了相同的数据,但没有任何标签。我们想做的是对我们的客户进行聚类,看看我们是否能从这个过程中获得一些关于他们信誉的相关信息。
那我们开始吧。我要用的数据集是德国信用风险数据集,可以在 Kaggle 这里找到。
import pandas as pd
df=pd.read_csv(r'german_credit_data.csv')
df=df.dropna()
df=df.drop(['Unnamed: 0'],axis=1)

在导入和清理数据集之后,我必须对它进行一点修改。事实上,我们被提供了分类列,如“性别”或“目的”,然而算法是数学运算,它们需要数字,而不是字符串。因此,我将继续进行 OneHot 编码过程。我不打算在这里解释背后的想法,但是我在这篇文章中用例子提供了一个完整的解释。
df=pd.get_dummies(df,prefix=['Sex','Housing','Saving Account','Checking account','Purpose'], drop_first=True)
现在,在应用我们的聚类算法之前,我们必须记住,一般来说,我们喜欢简单的模型。这里,我们有 19 个特征(最初是 10 个,但是随着 OneHot 编码变成了 19 个)来描述我们的客户,但是我们需要所有的特征吗?难道我们不能在不破坏模型的情况下减少特征的数量吗?
这个问题的答案依赖于主成分分析,其目的确实是通过创造新的变量来降低模型的复杂性而不丢失信息。后者有一个特点:信息主要存储在第一个组件中,因此,如果你想减少问题的维数,你可以只使用第一个组件,信息损失最小(如果你想了解更多关于 PCA 的知识,你可以阅读我以前的文章这里)。
现在,假设我们想要将维度从 19 减少到 2:
from sklearn.decomposition import PCA
pca = PCA(2)
projected = pca.fit_transform(features)
print(features.shape)
print(projected.shape)

如你所见,在 19 个特性中,我们现在只处理 2 个。
现在是时候应用我们的算法了。出于这个目的,我将使用 K-means,这是一个强大的无监督算法,我在这里写了关于它的功能的完整解释。此外,为了确定质心的正确数量,因此也是标签的正确数量,我将使用肘方法技术:
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
wcss = []
K = range(1,15)
for k in K:
km = KMeans(n_clusters=k)
km = km.fit(projected)
wcss.append(km.inertia_)plt.plot(K, wcss, 'bx-')
plt.xlabel('Number of centroids')
plt.ylabel('WCSS')
plt.title('Elbow Method For Optimal k')
plt.show()

如你所见,最佳 k 值介于 3 和 4 之间。为了模型简单起见,我们设 k=3。让我们看看我们的算法是如何工作的:
#converting our projected array to pandas df
pca=pd.DataFrame(projected)
pca.columns=['First component','Second Component']#build our algorithm with k=3, train it on pca and make predictions
kmeans = KMeans(n_clusters=3, random_state=0).fit(pca)
y_kmeans = kmeans.predict(pca)#plotting the results
plt.scatter(pca['First component'], pca['Second Component'], c=y_kmeans, s=50, alpha=0.5,cmap='viridis')
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=50)
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=50)

如您所见,我们的算法根据客户的特征(主要存储在前两个主成分中)将客户分为三类。
分析师的下一步可能是询问这种细分背后的原因,以及这些原因可能如何影响银行向特定集群的客户提供信贷的决策。
聚类技术的强大之处在于,它们能够在几秒钟内产生相关的结果,而手动分析所有客户端则需要花费大量的时间。
嘿 Airbnb,你想要更多的美钞吗?
应用 CRISP-DM 流程解决业务问题。

这篇文章围绕 CRISP-DM 过程展开,CRISP-DM 过程是数据挖掘的跨行业标准过程,有助于理解数据。它有 6 个短语:
- 发展商业理解
- 发展数据理解
- 准备要分析的数据
- 数据建模
- 评估结果
- 部署更改
所以在这里,我们将讨论 Airbnb 如何通过使用 CRISP-DM 流程分析他们的蒙特利尔数据来增加利润。
商业理解
Airbnb 是一个住宿提供平台,数百万人可以在这里列出他们的空间并预订世界任何地方的独特住宿。
Airbnb 帮助分享这些住宿。每当有住宿交易时,Airbnb 都会收取一定比例的交易费用,这是他们的主要收入来源。因此,预订数量越多,利润越高。

Business Model [Credit: Dyimage]
我们怎样才能增加利润?
所以,我遇到了这三个问题,基于我对增加利润的直觉。
- 哪个季节最适合 Airbnb 的推广和广告? 由于气候不利、费用高昂或其他未知原因,旅游旺季有时会变得不旺。所以,我们能做的就是在这个淡季推广我们的计划和折扣券。

- 蒙特利尔哪个地区有最好的客房?
人们在游览任何一个地方时,都会寻找最好的地方停留。所以在这里,我们可以向他们展示蒙特利尔最好的区域和最好的房间。

- 有类似的房源吗?
人们不会只着眼于一个住宿选择,他们需要根据住宿选择有几个类似的选择。所以在这里,我们将向他们推荐类似类型的房源。

数据理解
在这篇博客中,我们将讨论蒙特利尔 Airbnb 数据,我在这里使用了 listings.csv 和 calendar.csv 。要查看与分析相关的工作,请查看我的 Github repo 。

Snippet of calendar.csv

Small part of listings.csv
哪个季节最适合 Airbnb 的推广和广告?
在分析 calendar.csv 数据时,我们发现整个赛季的预订需求有两次下降。

正如你所看到的,这个季节在:
- 2019 年 4 月下旬至 2019 年 6 月下旬
- 2019 年 7 月初至 2019 年 9 月末
我们无法判断后一部分,因为我们不知道这是否是一个饱和趋势。我们也需要更多的数据来得出结论。
现在,我们根据预订需求的下降将整个赛季分为 3 个亚赛季。
- 子赛季 1: 2019 年 3 月 11 日至 2019 年 6 月 8 日
- 第二季:2019 年 6 月 9 日至 2019 年 9 月 6 日
- 第三季:2019 年 9 月 7 日至 2020 年 3 月 9 日
现在,我们将使用 violinplot 来查看每个工作日的预订数量分布。

Sub Season 1: The demand for bookings is low for [‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Sunday’]

Sub Season 2: The demand is low for [‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Sunday’]

Sub Season 3: The demand is low for [‘Wednesday’, ‘Thursday’]
总体而言,我们需要重点关注“2019–04”至“2019–09”期间的广告和促销活动。在此期间,我们只能针对['周一','周二','周三','周日']的受众。
蒙特利尔哪个地区有最好的房间?
在这一部分,我从 listings.csv 中发现了以下功能,可以帮助我们提供最好的区域和最好的房间。
- 邮政编码
- 主机是超级主机
- 每月评论数

Snippet of dataframe (reviews_per_month)

Snippet of dataframe (host_is_superhost)
第一法
我们将分析数据,并试图找出拥有最多评论的前 10 个邮政编码。

H2X area has the highest number of reviews
然后,我们将绘制排名前 10 位的邮政编码的点评分数分布图。

H2X area has high review scores
根据柱状图和箱线图,我们可以得出结论,“H2X”具有最高数量的评论(大多数评论被认为是异常值,但它是有用的)。根据评论数量,“H2Y 0A8”位居第二,“H3H”位居第三。
第二种方法
我们将尝试找出排名前 10 位的邮政编码,它们的评论得分总和最大。

‘H2Y 0A8’ has highest sum of review scores
然后,我们将绘制排名前 10 位的邮政编码的点评分数分布图。

‘H2Y 0A8’ has high number of good reviews
在这种情况下,boxplot 将发挥主要作用。在柱状图中,看起来“H2Y 0A8”得分最高,但在箱线图中,“H2X”有一些评论得分很高。“H2Y 0A8”的好评数很高。对于(H2Y 0A6,H2Y 1S6),他们有很多好评。
第三种方法
在这种情况下,我们将考虑超级主机的数量以获得最佳可用区域。随着 superhost 的可用性越来越高,从主人那里获得良好的便利设施和服务的机会也越来越多。

(H2Y, H3B, H2X, H2Y 0A8, H2L) area have high number of superhost.
总的来说,我们可以说 H2X,H2Y 0A8,H3B 是一些邮政编码区,任何人都可以根据客户评论和超高的可用性找到最好的房间。
有没有类似的房源?
这里,我再次使用 listings.csv 来考虑相关的特性,如 listing_id、name、description,以便推荐相似的列表。

Snippet of the Dataframe
首先,我将两列(名称、描述)合并为一列(内容),因为对两个相似类型的内容进行相同的操作是不值得的。

Snippet of new Dataframe
一旦我们有了一列内容,我们就试图找出一个列表的内容与所有其他列表的内容之间的相似性。我们对所有的列表都这样做。

This dictionary helps us to find the best recommended listing
现在,我们将推荐具有最高相似性的前 k 个列表 id。这里,k 是用户想要看到的推荐列表 id 的数量。

First para is user selected listing and rest are the recommended listings
摘要
下面是整个分析的总结:
- 哪个季节最适合 Airbnb 的推广和广告? 整个赛季的预订需求有两次下降,分别是 2019 年 4 月下旬至 2019 年 6 月下旬和 2019 年 7 月上旬至 2019 年 9 月下旬。我们还观察了周一、周二、周三和周日的下跌。所以如果我们在这些时段给 Airbnb 用户一些促销打折券会更好。
- 蒙特利尔哪个地区有最好的客房?
通过分析,我们发现 H2X、H2Y 0A8 和 H3B 是一些邮政编码区,在那里人们可以根据客户评论和超高的可用性找到最好的房间。 - 有类似的房源吗?
从主机列表的描述部分检索洞察,总有一些列表我们可以推荐给用户。
祝你有美好的一天…

作物:使用移动应用程序进行植物病害识别。
植物疾病可以通过利用深度学*的力量来检测。

Demo of Crop App.
在这篇文章中,我将解释我们如何使用深度学*模型来检测和分类植物的疾病,并通过视频指导农民,并给出即时补救措施,以克服植物和田地的损失。首先,我们必须了解
- 原因是什么,如何克服原因?
2.这样做有什么好处?
3.我们能用“深度学*技术”解决这个问题吗?
4.在深度学*“中,哪个算法“被用来解决这个问题?如何做到这一点?。
注意:我只对番茄和土豆植物这样做。你可以通过收集该植物的数据来为其他植物做这件事。
它也部署到 Web 应用程序。查看下面的链接。
想知道你的植物感染了什么类型的疾病,然后上传(番茄、土豆)植物的图片并进入…
cropify.herokuapp.com](https://cropify.herokuapp.com/)
1.起因及简介。
过去和现在,农民通常用肉眼检测作物病害,这使得他们在使用哪种肥料上做出艰难的决定。它需要疾病类型的详细知识和大量经验来确保实际的疾病检测。一些疾病看起来和农民很相似,经常让他们感到困惑。请看下图以获得更多的理解。

Similar Symptoms but different diseases.
他们看起来一样,几乎相似。如果农民作出错误的预测,使用错误的肥料或超过正常剂量(或)阈值或限制(每种植物都有一些阈值肥料喷洒要遵循),它将搞乱整个植物(或)土壤,并对植物和田地造成足够的损害。

Cause of overdose of fertilizers. (Source: jainsusa.com)
那么,如何防止这种情况的发生呢?
为了防止这种情况,我们需要更好和更完善的指导来使用哪种肥料,以正确识别疾病,并能够在视觉上区分两种或更多种相似类型的疾病。
这就是人工神经网络派上用场的地方。简言之,安
2.安是什么?
人工神经网络是基于生物神经网络结构的计算模型,设计用于模拟存在于我们大脑中的生物神经网络的实际行为。

Combining the Multiple ANN’s.
所以我们可以分配一些东西给它,它完成我们的工作。人工神经网络帮助我们对疾病做出正确的识别,并指导正确的施肥量。
只有一个安不能完成我们的工作。因此,我们将它们一个接一个地堆叠起来,形成一个层,我们可以在输入层(给出权重和数据)和输出层(结果)之间形成多个层,这些多个层称为隐藏层,然后它会形成一个 深度神经网络,对它的研究称为深度学*。
2.1 深度神经网络看起来怎么样。

Simple vs Deep Neural Nets.
简单的神经网络善于学*输入和输出层之间的一个隐藏层的权重。但是,它不擅长复杂的特征学*。
另一方面,深度学*神经网络,输入和输出层之间的一系列层被称为隐藏层,可以执行特征识别,并从数据中创建新的一系列特征,就像我们的大脑一样。我们推进的层越多,它将学*和执行复杂操作的功能就越多。输出图层组合所有要素并进行预测。
因此,简单的神经网络用于简单的任务,不需要大量数据来训练自己。而在深度学*中,神经网络可能很昂贵,并且需要大量数据集来训练自己。我不想讨论这个话题,因为它超出了本文的范围。
如果你是深度学*概念的绝对初学者,下面的链接将有助于获得所有基础知识。
随着 Tensorflow 2.0 和 Fastai 等库的兴起,实现深度学*已经变得触手可及
towardsdatascience.com](/the-basics-of-deep-neural-networks-4dc39bff2c96)
2.2 什么类型的深度学*模型最适合这种场景??
这就是卷积神经网络(CNN 或 Conv 网)。它被广泛应用于图像和视频识别、推荐系统和自然语言处理等领域。然而,卷积比更有效,因为它减少了参数的数量,这与其他深度学*模型不同。

CNN Architecture.
为了简单起见,我将仅解释对该模型的简要理解以及用于构建卷积神经网络的步骤。
构建 CNN(或)Conv 网络的主要步骤:
- 卷积运算
- ReLU 层(整流线性单元)
- 池层(最大池)
- 变平
- 全连接层
开始写代码。
1。卷积是从输入图像中提取特征的第一层,它使用输入图像的内核或过滤器来学*特征之间的关系。
- ReLU 层: ReLU 代表非线性操作的整流线性单元。输出为(x)= max(0,x)。 我们用这个是因为要给 CNN 引入非线性。
3.池层:通过下采样减少参数数量,只保留有价值的信息做进一步处理。共有几种类型:
- 最大池(选择此项)。
- 平均池和总和池。
4.展平:我们将整个矩阵展平成一个垂直的向量。因此,它将被传递到输入层。
5.完全连接层:我们将展平后的矢量传递给输入层。我们将这些特征结合起来创建了一个模型。最后,我们有一个激活函数,如 softmax 或 sigmoid 来对输出进行分类。
3.了解了 CNN 的运作。接下来呢??
- 收集数据(图像)
收集尽可能多的数据集,包括受疾病影响的图像和健康的图像。您应该需要批量数据。

2。建设 CNN。
使用一些流行的开源库构建 CNN,用于开发人工智能、机器学*和深度学*。

Open Source Libraries.
3。选择任何基于云的数据科学 IDE。
在云中训练模型是很好的,因为它需要巨大的计算能力,我们的普通机器笔记本电脑和计算机无法承受。如果你有一个好的 GPU 配置笔记本电脑,你可以在你的本地机器上训练。我选择 Google colab 你可以选择任何你喜欢的云。
谷歌 Colab:
Google colab 是一个免费的云服务,提供免费的 GPU(12Gb 内存)。这是训练我们的模型的最便捷的方法,不需要在我们的机器上安装任何库。它完全在云上工作。它预装了所有的依赖项。

Google Colab offers free GPU.
登录 colab 并创建一个新的 python 笔记本(ipynb)切换到 GPU 模式并开始编写代码。
4.开始写代码。
在编写代码之前,数据应该存储在 Google drive 中。
源代码可以在这里找到我的 GitHub 链接。
第一步:从 google drive 挂载数据。
from google.colab import drive
drive.mount(‘/content/your path’)
第二步:导入库。
# Import Libraries
import os
import glob
import matplotlib.pyplot as plt
import numpy as np# Keras API
import keras
from keras.models import Sequential
from keras.layers import Dense,Dropout,Flatten
from keras.layers import Conv2D,MaxPooling2D,Activation,AveragePooling2D,BatchNormalization
from keras.preprocessing.image import ImageDataGenerator
第三步:将训练和测试数据加载到单独的变量中。
# My data is in google drive.
train_dir ="drive/My Drive/train_set/"
test_dir="drive/My Drive/test_data/"
第四步:获取训练和测试数据中图像数量的函数。
# function to get count of images
def get_files(directory):
if not os.path.exists(directory):
return 0
count=0
for current_path,dirs,files in os.walk(directory):
for dr in dirs:
count+= len(glob.glob(os.path.join(current_path,dr+"/*")))
return count
第五步:查看每张中的图片数量。
train_samples =get_files(train_dir)
num_classes=len(glob.glob(train_dir+"/*"))
test_samples=get_files(test_dir)
print(num_classes,"Classes")
print(train_samples,"Train images")
print(test_samples,"Test images")

5.Output.
- 收集 12 类= 12 种类型的疾病图像。
- 14955 张火车图片
- 432 张测试图片(我只错拍了几张图片测试)。
- 为了预测,我只从看不见的数据中提取了一些样本。我们可以使用作为训练数据一部分的验证数据进行评估。
第六步:将我们的原始数据预处理成可用的格式。
# Pre-processing data with parameters.
train_datagen=ImageDataGenerator(rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_datagen=ImageDataGenerator(rescale=1./255)
- 在(0–1)之间重新调整图像值称为标准化。
- 无论您对训练做什么预处理,都应该进行并行测试。
- 所有这些参数都存储在变量“ train_datagen 和 test_datagen”中。
步骤 7:从训练和测试目录中生成扩充数据。
# set height and width and color of input image.
img_width,img_height =256,256
input_shape=(img_width,img_height,3)
batch_size =32train_generator =train_datagen.flow_from_directory(train_dir,
target_size=(img_width,img_height), batch_size=batch_size)
test_generator=test_datagen.flow_from_directory(test_dir,shuffle=True,target_size=(img_width,img_height), batch_size=batch_size)
- 获取一个目录的路径,并生成批量的扩充数据。在无限循环中无限期生成批次。
- 批量指的是在一次迭代中使用的训练样本的数量。

7.Output.
第八步:获取 12 个疾病名称/类别。
# The name of the 12 diseases.
train_generator.class_indices

8.Disease Names.
第九步:建立 CNN 模型
# CNN building.
model = Sequential()
model.add(Conv2D(32, (5, 5),input_shape=input_shape,activation='relu'))
model.add(MaxPooling2D(pool_size=(3, 3)))
model.add(Conv2D(32, (3, 3),activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3),activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(512,activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(128,activation='relu'))
model.add(Dense(num_classes,activation='softmax'))
model.summary()

9.Summary of layers.

9. Output.
CNN 收缩参数,学*特征,存储有价值的信息,输出形状在每一层后递减。我们能看到每一层的输出吗?是啊!!我们可以。
步骤 10:每一层之后的图像可视化。
from keras.preprocessing import image
import numpy as np
img1 = image.load_img('/content/drive/My Drive/Train_d/Tomato___Early_blight/Tomato___Early_blight/
(100).JPG')
plt.imshow(img1);
#preprocess image
img1 = image.load_img('/content/drive/MyDrive/Train_d/
Tomato___Early_blight/Tomato___Early_blight(100).JPG', target_size=(256, 256))
img = image.img_to_array(img1)
img = img/255
img = np.expand_dims(img, axis=0)
- 从训练数据集获取样本图像,并在每一层后可视化输出。
- 注意:新样本图像需要预处理。
from keras.models import Model
conv2d_1_output = Model(inputs=model.input, outputs=model.get_layer('conv2d_1').output)
max_pooling2d_1_output = Model(inputs=model.input,outputs=model.get_layer('max_pooling2d_1').output)conv2d_2_output=Model(inputs=model.input,outputs=model.get_layer('conv2d_2').output)
max_pooling2d_2_output=Model(inputs=model.input,outputs=model.get_layer('max_pooling2d_2').output)conv2d_3_output=Model(inputs=model.input,outputs=model.get_layer('conv2d_3').output)
max_pooling2d_3_output=Model(inputs=model.input,outputs=model.get_layer('max_pooling2d_3').output)flatten_1_output=Model(inputs=model.input,outputs=model.get_layer('flatten_1').output)conv2d_1_features = conv2d_1_output.predict(img)
max_pooling2d_1_features = max_pooling2d_1_output.predict(img)
conv2d_2_features = conv2d_2_output.predict(img)
max_pooling2d_2_features = max_pooling2d_2_output.predict(img)
conv2d_3_features = conv2d_3_output.predict(img)
max_pooling2d_3_features = max_pooling2d_3_output.predict(img)
flatten_1_features = flatten_1_output.predict(img)

10. Visualization
第十一步。开始用参数训练 CNN。
validation_generator = train_datagen.flow_from_directory(
train_dir, # same directory as training data
target_size=(img_height, img_width),
batch_size=batch_size)opt=keras.optimizers.Adam(lr=0.001)
model.compile(optimizer=opt,loss='categorical_crossentropy',metrics=['accuracy'])
train=model.fit_generator(train_generator,nb_epoch=20, steps_per_epoch=train_generator.samples//batch_size, validation_data=validation_generator,nb_val_samples=validation_generator.samples // batch_size,verbose=1)
- Adam 优化器与学*率=0.001 一起使用
- 损失函数 categorical _ crossentropy 用于我们的多类分类问题。度量标准是“准确性”。
- Fit_generator 用于训练 CNN 模型。使用验证数据参数对模型进行微调。

11. Train and validation Accuracy and Loss.
第 12 步:保存模型权重。
- 保存模型权重以防止模型的重新训练。
# Save model
from keras.models import load_model
model.save('crop.h5')
步骤 13:从保存的权重加载模型。
# Loading model and predict.
from keras.models import load_model
model=load_model('crop.h5')# Mention name of the disease into list.
Classes = ["Potato___Early_blight","Potato___Late_blight","Potato___healthy","Tomato___Bacterial_spot","Tomato___Early_blight","Tomato___Late_blight","Tomato___Leaf_Mold","Tomato___Septoria_leaf_spot","Tomato___Spider_mites Two-spotted_spider_mite","Tomato___Target_Spot","Tomato___Tomato_mosaic_virus","Tomato___healthy"]
- 提到这个名字是因为我们的输出是数字格式。我们把它铸造成字符串。
第十四步:预测
import numpy as np
import matplotlib.pyplot as plt# Pre-Processing test data same as train data.
img_width=256
img_height=256
model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['accuracy'])from keras.preprocessing import imagedef prepare(img_path):
img = image.load_img(img_path, target_size=(256, 256))
x = image.img_to_array(img)
x = x/255
return np.expand_dims(x, axis=0)
result = model.predict_classes([prepare('/content/drive/My Drive/Test_d/Tomato_BacterialSpot/Tomato___Bacterial_spot (901).JPG')])
disease=image.load_img('/content/drive/My Drive/Test_d/Tomato_BacterialSpot/Tomato___Bacterial_spot (901).JPG')
plt.imshow(disease)
print (Classes[int(result)])
- 我们需要预处理我们的图像,以传入一个模型来预测
- 首先,我们调整图像的大小=== >图像(150,150)
- 将图像转换为数组,这将增加通道=== >图像(150,150,3) RGB
- Tensorflow 处理批量图像我们需要指定图像样本===>(1,150,150,3)。
- Predict_classes 帮助预测新图像属于各自的类。

14.Predictions on test data, different diseases.
最后一步:将模型转换成“tflite”。
from tensorflow.contrib import lite
converter = lite.TFLiteConverter.from_keras_model_file( 'crop.h5' )
model = converter.convert()
file = open( 'output.tflite' , 'wb' )
file.write( model )
- 为了让我们的模型与应用程序通信,我们必须转换为 TensorFlow lite 版本,tflite 是为移动版本而设计的。
- 因此,您可以构建或创建一个移动应用程序,并使该应用程序与模型进行通信。
你可以使用 Google flutter 框架来构建具有漂亮用户界面的应用程序。

Google flutter framework.
在进入 App 部分之前,您需要从 官网将颤振环境安装到您的机器上。
打开 flutter 编辑器,无论是 Android studio 还是 visual studio,都可以进行编码。

Editors of Flutter.
创建一个新的 flutter 项目,并将项目文件中的一个文件夹拖放到我们的 flutter 项目的 assets 文件夹中。以下名称已更改,但 tflite 文件是相同的。

Flutter Assets folder.
App 用户界面可以自己选择,可以通过学* flutter 做一个,也可以直接用我的 flutter app 界面。可以在我的 Github 里找到。
完成将“output.tflite”放入 assets 文件夹后,开始在仿真器和测试模型中运行应用程序,使用测试文件夹中的一些图片以及模型之前未见过的真实图像。
结论:
植物疾病是主要的食物威胁,在它导致整个田地的进一步损失之前,应该被克服。但是,通常情况下,制定者不能区分相似的症状,但面对不同的疾病。这将误导错误或过量施肥。在这里,我们采用卷积神经网络(CNN)多层人工神经网络,称为深度学*算法,以减少这种损失,并通过视频课程指导农民。这可以通过手机应用程序来完成,“不是所有的农民,但有些人确实使用它。”
我希望以上内容对阅读这篇文章的每个人都有用。我肯定会提出与数据科学、ML 和 DL 相关的话题。快乐学*:)
感谢您的阅读…
假人的交叉熵
为数据科学家简单直观地解释信息、熵和交叉熵

交叉熵通常被用作分类问题的损失函数,但由于历史原因,大多数交叉熵的解释都是基于数据科学家可能并不熟悉的通信理论。不理解熵就不能理解交叉熵,不知道什么是信息就不能理解熵。本文以一种易于理解的方式构建了交叉熵的概念,而不依赖于其传播理论背景。
信息
让我们考虑三个说明性的实验。
(1)我扔硬币,我给你一个信息,硬币正面朝上。这条消息向你传达了一些信息。多少信息?假设您从这条消息中获得了一点信息。
Coin came up heads (probability=0.5) | Information = 1 bit
(2)早上我叫醒你,告诉你太阳升起。你耸耸肩,生气地回去睡觉,因为这个消息很明显,没有给你新的信息。
Sun rose (probability=1) | Information = 0 bits
(3)早上我叫醒你,告诉你太阳没有升起。你震惊地走出来,看到这个世界变得疯狂。这是突发新闻——大量的信息。
Sun did not rise (probability=0) | Information = ∞ bits
从上面的例子中,你可能已经推断出包含在关于事件的消息中的信息与事件的不确定性和惊奇值有关。不太可能发生的事件比可能发生的事件给你更多的信息。克劳德·香农在他关于信息论的开创性著作中正式提出了信息背后的这种直觉。他将信息定义为:
I(x) = -log₂P(x) ..where P(x) is probability of occurrence of x
信息量化了单一事件中的不确定性。
熵
如果你感兴趣的不是一个单一的事件,而是一系列的事件呢?考虑下面的例子。一个箱子里有 2 个红色球、3 个绿色球和 4 个蓝色球。现在,我不是扔硬币,而是随机挑出一个球给你。每次我选一个球,你会收到多少信息?我们将挑选一个球建模为由随机变量 X 表示的随机过程。然后,X 的熵被定义为 X 中的结果所传达的信息的期望值。使用我们上面的信息定义,我们得到:
P(red ball) = 2/9; I(red ball) = -log₂(2/9)
P(green ball) = 3/9; I(green ball) = -log₂(3/9)
P(blue ball) = 4/9; I(blue ball) = -log₂(4/9)Entropy = E[I(all balls)]
= -[(2/9)*log₂(2/9) + (3/9)*log₂(3/9) + (4/9)*log₂(4/9)]
= 1.53 bits.
*随机变量 X 的期望值或期望,记为 E[X],是 X 的所有值按其出现的概率加权后的平均值。
换句话说,每次我从垃圾箱里拣出一个球,你平均可以得到 1.53 比特的信息。
形式上,随机变量的概率分布的熵 H 被定义为:

上式中的 x~P 表示 x 取的值来自分布 P,在我们的例子中,P = (2 红,3 绿,4 蓝)。
交叉熵
交叉熵度量同一组事件的两个概率分布之间的相对熵。直观地说,要计算 P 和 Q 之间的交叉熵,只需使用 P 的概率权重计算 Q 的熵。形式上:

让我们考虑具有两个仓的相同仓例子。
Bin P = {2 红色,3 绿色,4 蓝色}
Bin Q = {4 红色,4 绿色,1 蓝色}
H(P, Q) = -[(2/9)*log₂(4/9) + (3/9)*log₂(4/9) + (4/9)*log₂(1/9)]
作为损失函数的交叉熵
取代上面的人为例子,让我们来看一个机器学*的例子,其中我们使用交叉熵作为损失函数。假设我们建立一个分类器,预测三类样本:A,B,c。
设 P 是真实的标签分布,Q 是预测的标签分布。假设一个特定样本的真实标签是 B,我们的分类器预测 A,B,C 的概率为(0.15,0.60,0.25)

交叉熵 H(P,Q)将为:
H(P, Q) = -[0 * log₂(0.15) + 1 * log₂(0.6) + 0 * log₂(0.25)] = 0.736
另一方面,如果我们的分类器更有信心,预测概率为(0.05,0.90,0.05),我们将得到交叉熵为 0.15,低于上面的示例。
与最大似然的关系
对于分类问题,使用交叉熵作为损失函数相当于最大化对数似然。考虑下面的二元分类情况,其中 a、b、c、d 表示概率:

H(True, Predicted)
= -[a*log(b) + c*log(d)]
= -[a*log(b) + (1-a)*log(1-b)]
= -[y*log(ŷ) + (1-y)*log(1-ŷ)]
..where y is true label and ŷ is predicted label
这与最大似然估计的等式相同。
结论
由于历史原因,您经常会发现交叉熵是在通信理论的背景下定义的,例如:交叉熵是当我们使用模型 q 时,对来自分布为 p 的源的数据进行编码所需的平均位数。大多数数据科学家从未研究过通信理论。我希望他们会发现上面的文章很有用。
如果你喜欢这篇文章,可以在 中型 上查看我的其他作品,在LinkedIn或Twitter,查看我的 个人网页 ,或发邮件给我viraj@berkeley.edu
交叉熵:从信息论的角度

交叉熵是分类问题机器学*中广泛使用的损失函数。信息论被广泛使用,但是在课堂上并没有解释使用它背后的基本原理。在这篇博客中,我们对信息论进行了直观的理解,并最终将其与交叉熵函数联系起来。
历史:电信是通过不可靠的信道实现的,在这些信道中,传输的信号往往不等于接收的信号,因为它们被噪声破坏了。Shannon 提出,可以开发一种编码器-解码器系统,该系统可以在讹误量不确定的情况下以最小讹误检索传输信号。这可以通过冗余来实现,即如果一个信号是 aaa,发送三个信号,每个 aaa,如果一个被破坏为 aab,另两个可以通过共识来确认传输的信号是 aaa。所有这些都是以比特和信道速率发送的r=发送的有用比特数/总比特数。香农证明了误差可能是 0,即使 r 是> > 0(他证明了它是 0.5)。
香农在等式(1)中定义了信号 的信息内容和信息x。他声称这是测量信息内容的正确方法。从(1)可以看出,对于一个不确定事件,信息量是最大的。等式(2)给出了发射信号总体的信息内容。Shannon 声称 h(x)应该是压缩文件的长度,我们应该渴望对信息进行编码,以便唯一识别。他证明了我们不能将信息编码成许多比特。更高的不确定性=更高的 h(x)值,这意味着需要更多的冗余来编码信息(这是有道理的,对吧)。例如,通过研究一座倒塌的桥(一种罕见的事件)来了解为什么一座桥会倒塌,比研究完美的桥要多得多。
(1)x = h(x = a)= ln(1/P(x = a))的信息内容**
(2) h(x) = 求和超过所有 a(P(x = a)ln(1/P(x = a))*)*
当所有人都具有相等的概率时,h(x)对于系综是最大的。在我们继续之前,让我们举一个例子。例如,如果我在想一个介于 1-100 之间的数字,并要求您在最少的尝试次数中猜出它,“最佳优先”问题是“如果该数字小于 50”。这是最好的问题,因为它给出了关于我的号码的最大信息(排除了 50 个选项)。如果你的第一个问题是“这个数字是否小于 1”,如果答案是“否”,你很可能没有获得足够的信息(因为你只剩下 99 个选项)。因此,当我们选择一个数字 50 将集合分成等概率时,信息内容最大化。根据香农原理,提供 1 比特的信息可以将不确定性降低 1/2。
熵:定义为期望的信息量(等式 2)。它也是信息不确定性的一种度量。如果不确定性高,熵就高。例如,如果我们有一个装满的硬币,每次都是正面朝上,那么熵为 0,因为没有来自硬币投掷的信息(因为它总是正面朝上)。
如果我们必须编码一条长度为 l 的信息,我们不能用比熵少的位数来编码它(等式 2)。用于传递信息的代码的平均长度不能少于所发送信息的“比特数”。为了唯一识别长度为 l、的信号,我们可以使用 2^ l 位(0 或 1)。一条信息的理想长度 i 如等式(3)所示。为了唯一可识别(基于 Kraft 不等式),编码的长度如等式(4)所示。
(3)l _ I = h(x _ I)= ln(1/p _ I)**
(4)l _ I = ln(1/q _ I)-ln(Z)其中 Z 为归一化常数> 0 且≤ 1
所需总位数之和 L =所有位之和I(p _ IxL _ I)。使用来自(4)的 l_i 值,我们得到L≥h(X)+KL(p||q)其中 KL(p | | q)是 Kullback-Liebler 散度。因此,KL 散度是表示一组信息相对于理想编码位所需的额外编码长度。现在我们已经了解了 KL 背离背后的历史,让我们来看一下统计数据。
Kullback-Liebler 散度是“P 与 Q 的不同程度的度量”。它是使用等式(2)的两个分布中包含的信息之间的差异,通常从期望分布的角度来测量(分类问题中的真实类别)。从真实分布的角度来看,KL 散度是对新分布进行编码所需的信息内容+额外比特。现在,设置好这些部分后,我们来看看交叉熵。
交叉熵 = 总和超过所有 I(Y _ Ixln(Y _ I/Y _ hat _ I)。对所有 i 求和,并通过分离和丢弃 ln() 项内的常数分子进行简化,交叉熵保持在sum over all I(Y _ Ixln(1/Y _ hat _ I))**
如果真实分布是 Y 预测是 Y_hat,和 Y 不等于 Y_hat,由于我们永远不知道真实分布,交叉熵不等于熵。
差=-对所有 I 求和(Y _ I(log(Y _ hat _ I)-log(Y _ I))= KL(Y | | Y _ hat)****
因此,交叉熵实际上是真实分布和来自机器学*模型的分布之间的熵+ KL 散度的度量。
参考文献:
- 大卫·麦凯教授信息论系列讲座
- 关于 KL 散度+交叉熵的 Youtube 视频
跨平台的设备上 ML 推理
张量流建兴英尺。摆动
随着 Flutter 1.9 和 Dart 2.5 的发布,有太多令人兴奋的东西可以尝试,但真正引起我注意的是 Dart 的 ML Complete。它使用[tflite_native](https://github.com/dart-lang/tflite_native),T0 又通过 Dart FFI 使用 TensorFlow Lite C API 。

它立即在我的脑海中引发了一个疯狂的想法,为多个平台(iOS,Android,Mac,Windows,Linux,甚至 Web)上的应用程序提供一个单一的代码库,可以进行低延迟的本地机器学*推理。
以前,它不是真正的单一代码库,即使是使用 Flutter,因为必须有一些来自使用平台通道的平台端的代码,但是使用 FFI,我们现在可以说它是真正的单一代码库。
经过几次试错迭代,我终于用tflite_native得到了 TensorFlow 物体检测的原型。源代码是 MIT 许可的,可以在 my Github 上找到。请注意,如果您在 iOS 上运行这个源代码示例,请使用来自开发频道(flutter channel dev; flutter upgrade)的 Flutter 1.10.x,因为DynamicLibrary.process()在 1.9.1 稳定版中被删除,后来又被重新添加(参考)。
这是一个漫长而多事的旅程,所以我将把它分成一系列文章,每篇文章都独立工作,没有额外的上下文,可能有不同的受众。
- 这篇文章是关于使用
tflite_native来加载模型和标签,创建解释器,预处理输入到模型,并解释来自模型的输出数据。 - 一篇即将发表的关于制作没有任何平台通道的
tflite_native、Flutter 插件的文章。 - 即将发布的一篇关于 TensorFlow Lite 范围之外的应用程序的 Dart/Flutter 基础架构的文章,例如依赖项隔离以及 FFI 如何用于在 Dart 中执行不安全的并行线程。

1.配置依赖关系和资产
要加载 tflite 模型,首先,我们需要在我们的资产中有这个模型。下载 zip 文件,解压到 Flutter 项目的assets文件夹中,然后在pubspec.yaml中声明资产。
有两个资产,model.tflite和labels.txt,它们都已被重命名。第一个文件是预先训练好的 ML 模型,我们可以用它来做预测,但是输出只是数字,其中有标签的索引,存储在第二个文件中。
对于这个原型,我们还需要path_provider、camera、image,当然还有tflite_native。path_provider用于从资产中加载模型,camera用于获取实时(原始)图像流,image用于将原始图像处理成可用的 RGB 格式,以便提供给我们的模型。对于tflite_native,现在我们将使用我的分叉版本。
我们还需要配置ios/Runner/Info.plist来获得摄像机访问的许可。
2.加载 tflite 模型和标签
现在,直接从资产中加载 tflite 模型并不直接。当前的 C API 既可以从字节加载模型,也可以从可访问的文件加载模型。建议从可访问的文件中加载模型,因为在本质上,TfLite C API 使用了内存映射文件 ( mmap ),如果您需要在内存受限的设备上加载大型模型,这是至关重要的。不幸的是,来自平台端的 Flutter没有任何从 bundle assets中读取的简单方法,所以我们不得不解决这个问题,从assets中读取模型并写入一个临时文件,然后让 TfLite C API 从中读取。之后,我们创建一个InterpreterOption,设置我们想要的线程数量,加载模型并分配张量,然后返回准备使用的解释器。理想情况下,使用InterpreterOption,我们应该可以选择使用 GPU 或 NNAPI,这将显著减少推理时间,但目前这些 API 在 TfLite C API 中还不可用。
加载标签很简单(不一定很简单),只需从labels.txt中读取文本,用新行分隔,然后移位 1 ,(我猜labels.txt中的第一个元素是为列表长度保留的)
3.预处理输入
快速回顾一下,一个模型可能有一个或多个input tensors(例如 SSD MobileNet 有 1 个输入张量,但是艺术风格转移的转移模型有 2 个输入张量,1 个用于风格,1 个用于内容)。每个输入tensor都有一个形状,是一个int的列表,例如[1, 480, 640, 4],以及一个期望的输入类型,通常要么是 float32(非量化),要么是 u_int8(量化),但还有其他。了解形状和预期的输入类型对于为张量准备正确的输入非常重要。在我们的例子中,输入张量期望 300 宽 x 300 高 x 3 通道颜色(RGB),并且输入类型是 u_int8 。
取决于平台,我们从流中得到不同的原始图像。从相机流中获取原始图像的方法将在另一篇文章中介绍。在 Android 上,camera用 YUV_420 返回 raw 图像,转换成 RGB 格式需要一点努力。
在 iOS 上,camera在 BGRA 中返回 raw 图像,在image包的帮助下,我们可以轻松转换成 RGB。
现在我们已经有了 RGB 格式的图像,但不一定是正确的尺寸,因此我们需要调整大小并将其裁剪为 300x300 的正方形(在 Android 上,我们还必须旋转 90°,这是一个已知问题)。最后,获取原始字节和原始图像大小(因为取决于我们是在 Android 还是 iOS 上,在条件旋转后可能是 640x480 或 480x640)
4.输入、调用模型、解释输出
来自前面的原始字节现在可以输入到我们的模型中,这个模型只有一个输入张量。我们只需要掌握它来设置数据和调用模型。
与只有 1 个张量的输入不同,这个模型的输出有 4 个张量:
我们将输出(从原始字节)解析为相应的*似数据类型。通过*似,我的意思是,如果输出是一个类似于List<List<Float32>>的 2D 列表,我们仍然将它解析为一个 1D 列表List<Float32>,并且稍后将解释为 2D 列表。
现在,我们有分散在 3 个不同变量中的检测结果,第一个是矩形位置的有序列表,第二个是分类索引的有序列表,其索引对应于我们的标签图,第三个是概率分数的列表(另外第四个是检测结果的数量)。我们需要将这 3 个有序列表合并成一个新列表,其元素具有检测到的英文类名(例如“bicycle”或“laptop”)、概率分数和矩形位置。
通常,我们只是做一个for循环,处理我们感兴趣的每个元素属性。对于检测到的英文类名,我们只需将其从double转换为int,并在我们之前加载的标签映射中查找它(并且已经移位 1)。对于概率分数,原始输出在 0.0 (0%)到 1.0 (100%)之间,所以我们直接用原始输出就好了。
矩形位置稍微复杂一点。如前所述,这个张量形状是一个矩形位置的列表,它本身是 4 个坐标点(上、左、下、右)的列表,每个坐标点都在 0.0(对应我们 300x300 输入图像的 0)和 1.0(对应我们输入图像的 300)之间。因此,如果其他列表有 10 个元素(10 个检测到的类名和概率分数),这个 2D 列表,当展平到 1D 时,有 40 个元素。
换句话说,假设i是元素的索引
然后嘣💥,你有它!每当有来自相机流的图像时,它都会被处理,我们会得到一个检测结果列表,每个结果都有检测到的对象名称、概率分数(从 0.0 到 1.0)和矩形位置。要叠加这些位置,我们需要缩放它们的坐标以匹配底层图片,但这超出了本文的范围。从技术上讲,这段代码可以在所有的 Flutter 平台(iOS、Android、Linux、Windows、Mac)上运行,除了 web(但是如果 Flutter Web 和 TensorFlow.js 之间存在集成,我们也可以在以后使用)。然而,现在我们受到其他插件而不是tflite_native的限制,比如camera或path_provider。值得提醒的是tflite_native本身是用来给桌面上的 Dart 的 ML Complete 供电的。
最后的想法
机器学*不仅仅是下降梯度、二进制分类、加速线性代数(XLA)或其他令人生畏的术语。其实机器学*工作本身就相当宽泛,这里只做工作流的“产生式推理”。

Credit hydrosphere.io
更具体地说,产生式推理可以在功能强大的服务器上远程进行,和/或在用户的本地设备上进行。具体来说,对于 TensorFlow Lite 和tflite_native,我们做的是后者。尽管本地检测结果的准确性可能不如远程强大的服务器集群上的结果,但是本地推断具有其他优势,例如低延迟、不需要连接、不消耗互联网带宽,并且没有数据(潜在的个人和/或敏感性)离开用户的设备。事实上,本地推理和远程推理可以结合在一起,两全其美。在这个具体的例子中,我们可以进一步扩展,使本地应用程序可以检测到分数相当高的交通标志,并只将图像的这一部分发送到服务器,以进一步识别这是哪个交通标志(停止标志、速度限制、警告等)。)


做这个例子有助于你打开机器学*世界的大门。对于一个新的软件工程师来说,这并不太难,但也不太容易。完成这些,您将了解预训练的 ML 模型的通信方式(输入和输出),并渴望了解更多有关使用 GPU(而不是 CPU)进行推理、量化与非量化训练模型的信息,或者将 a 从一种预训练模型转换为另一种模型( pb vs chkpt vs tflite )。
最后,随着联合学* (FL)越来越受欢迎,让我们希望 TensorFlow 联合 (TFF)将支持移动(FL 已经被用于训练移动键盘的预测模型),或者更疯狂,让我们希望 TFF 首先飘起😉
同样,源代码是 MIT 许可的,可以在 my Github 上找到。
交叉验证—为什么和如何

所以,你已经在一个不平衡的数据集上工作了几天,尝试了不同的机器学*模型,在你的数据集的一部分上训练它们,测试它们的准确性,你欣喜若狂地看到分数每次都超过 0.95。你真的认为你的模型达到了 95%的准确率吗?
需要评估
我假设您已经对数据集执行了顶级的预处理,并且已经移除了任何缺失值或分类值和噪声。无论你用什么最先进的算法来构建你的假设函数和训练机器学*模型,你都必须在前进之前评估它的性能。
现在评估一个模型最简单快捷的方法就是把数据集分成训练集和测试集,用训练集数据训练模型,用精度来检验它的精度。并且不要忘记在执行分割之前打乱数据集。但是这种方法根本不是一种保证,简单地说,在最终确定一个模型时,你不能依赖这种方法。你可能想知道——为什么?
让我们考虑一下,你正在处理一个垃圾邮件数据集,其中包含 98% 的垃圾邮件和 2% 的非垃圾邮件有效邮件。在这种情况下,即使您不创建任何模型,而只是将每个输入分类为垃圾邮件,您也将获得 0.98 的精确度。这种情况叫做 精度悖论 。
想象一下,如果这是一个用于肿瘤细胞或胸部 x 光分类的模型,并且你已经将一个 98%准确的模型推向市场,会发生什么。也许这会杀死数百名病人,你永远不知道。

不要担心,拿一杯热的东西。在下面的文章中,我将解释评估你的机器学*模型的整个过程。作为先决条件,您只需要知道一些基本的 Python 语法。
模型评估
我们最初将获得的全部数据分成两组,一组用于训练模型,另一组作为维持组保存,用于检查模型在完全看不见的数据下的表现。下图总结了执行拆分的整个想法。

A very high-level view of the model building and evaluation pipeline
请注意,训练测试比率可以是 80:20、75:25、90:10 等等。这是机器学*工程师必须根据数据量做出的决定。一个好的经验法则是使用 25%的数据集进行测试。
您可以使用一些 Python 和开源 Sci-kit Learn API 轻松做到这一点。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 42, shuffle = True, stratify = y)
x 是原始的整个要素集,y 是对应的整个真实标注集。上述函数将整个集合分为训练集和测试集,测试集的比率为 0.3。参数shuffle 被设置为真,因此数据集将在分割前被随机打乱。参数stratify是最*从 v0.17、添加到 Sci-kit Learn 中的,它在处理不平衡数据集时是必不可少的,例如垃圾邮件分类示例。它进行了一次分割,使得产生的样本中的值的比例与提供给参数stratify的值的比例相同。
例如,如果变量y是具有值0和1的二元分类变量,并且有 10%的 0 和 90%的 1,stratify=y将确保您的随机分割有 10%的0和 90%的1
正如我们所讨论的,由于类不平衡等因素,仅仅检查测试集中有多少例子被正确分类并不是检查模型性能的有用指标。我们需要一个更强大、更细致的指标。
混淆矩阵
向混乱矩阵问好。诊断模型性能的一种简单而普遍的方法。让我们通过垃圾邮件分类的场景来理解这一点。混乱矩阵会是这样的。

从混淆矩阵中可以推导出几个指标,例如—
**Accuracy** = (TP + TN) /(TP + TN + FP + FN)
**Precision** = (TP) / (TP + FP)
**Recall** = (TP) / (TP + FN)
**F1 Score** = (2 x Precision x Recall) / (Precision + Recall)— where ***TP*** is True Positive, ***FN*** is False Negative and likewise for the rest.
精确基本上是你说的所有相关的事情,而回忆是所有实际相关的事情。换句话说,召回也被称为您的模型的灵敏度,而精度被称为正预测值。这里有一张一页纸的备忘单来总结这一切。
现在您已经掌握了这个概念,让我们了解如何使用 Sci-kit Learn API 和几行 Python 代码轻松地完成它。
from sklearn.metrics import confusion_matrix, classification_report
y_pred = model.predict (X_test)
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
假设您已经使用 准备好了模型。fit() 方法在训练集上(我可能改天再写),然后使用 计算预测的标签集。预测()模型的方法。在 y_test 中有这些数组的原始标签,然后将这两个数组传递给上面的两个函数。您将得到一个二乘二的混淆矩阵(因为垃圾邮件分类是二进制分类)和一个返回所有上述指标的分类报告。
注意:真值作为第一个参数传递,预测值作为第二个参数。

You can pass an extra parameter to the classification report known as target_names. Refer the docs.
交互效度分析
交叉验证是一种评估统计分析如何推广到独立数据集的技术。它是一种评估机器学*模型的技术,通过在可用输入数据的子集上训练几个模型,并在数据的互补子集上评估它们。使用交叉验证,我们很有可能轻松检测到过度拟合。
There are several **cross** **validation techniques** such as :-1\. K-Fold Cross Validation
2\. Leave P-out Cross Validation
3\. Leave One-out Cross Validation
4\. Repeated Random Sub-sampling Method
5\. Holdout Method
在本帖中,我们将讨论其中最流行的方法,即 K 折交叉验证。其他的也很有效,但是不常用。
因此,让我们花点时间问问自己,为什么我们需要交叉验证—
我们已经将数据集分成了训练集和测试集(或维持集)。但是,准确性和指标在很大程度上取决于分割是如何执行的,这取决于数据集是否被打乱,哪一部分被用于训练和测试,多少,等等。此外,它并不代表模型的概括能力。这导致我们交叉验证。
k 倍交叉验证
首先,我要向您介绍一条黄金法则— “永远不要混淆训练和测试数据”。您的第一步应该始终是隔离测试数据集,并仅将其用于最终评估。因此,将在训练集上执行交叉验证。

5 Fold Cross-Validation
最初,整个训练数据集被分成 k 个相等的部分。第一部分保留作为支持(测试)组,剩余的 k-1 部分用于训练模型。然后,在维持集上测试已训练的模型。上述过程重复 k 次,每次我们都不断改变维持组。因此,每个数据点都有同等的机会被包含在测试集中。
通常,k 等于 3 或 5。它甚至可以扩展到更高的值,如 10 或 15,但它在计算上变得极其昂贵和耗时。让我们看看如何用几行 Python 代码和 Sci-kit Learn API 实现这一点。
from sklearn.model_selection import cross_val_score
print(cross_val_score(model, X_train, y_train, cv=5))
我们传递模型或分类器对象、特征、标签和参数 cv ,其指示用于 K-Fold 交叉验证的 K 。该方法将为每次迭代返回 k 个精度值的列表。一般来说,我们取它们的平均值,并将其用作合并的交叉验证分数。
import numpy as np
print(np.mean(cross_val_score(model, X_train, y_train, cv=5)))
尽管交叉验证在计算上可能很昂贵,但它对于评估学*模型的性能是必不可少的。
请随意查看我在本文末尾的参考资料部分中包含的其他交叉验证分数评估方法。
结论
机器学*模型的准确性要求因行业、领域、需求和问题陈述而异。但是,在没有评估所有基本指标的情况下,不应该确认最终模型。
顺便说一下,一旦你完成了评估并最终确认了你的机器学*模型,你应该重新使用最初仅为测试目的而隔离的测试数据,并用你拥有的完整数据训练你的模型,以便增加更好预测的机会。
感谢阅读。这是一个高层次的主题概述,我试图尽最大努力以一种简单的方式解释手头的概念。请随时对文章进行评论、批评并提出改进建议。还有,掌声鼓励我多写!敬请关注更多文章。
用 PyTorch 看看这个 友好的神经网络入门。
参考
[1] 留单交叉验证【2】留单交叉验证【3】重复随机子抽样方法【4】维持方法【5】
交叉验证:初学者指南
LOO、K 折叠和维持模型验证简介
作者:Caleb Neale,Demetri Workman,Abhinay Dommalapati
在开始进入机器学*和数据科学世界的旅程时,经常会有一种诱惑,即跳入算法和模型创建,而不了解如何在真实世界的数据上测试生成的模型的有效性。交叉验证是模型验证的一种形式,它试图通过利用我们的数据子集和对偏差/方差权衡的理解来改进拒绝验证的基本方法,以便更好地了解我们的模型在应用于数据之外时的实际表现。别担心,一切都会解释清楚的!
本文试图成为三种模型验证方法(支持、k-fold 和 LOOCV)及其背后概念的执行指南,并提供链接和参考来指导您进一步阅读。在给出的例子中,我们使用了 scikit learn、pandas、numpy 和其他 python 库。
本文将讨论什么:
- 什么是模型验证?
- 为什么重要?
- 模型验证中的偏差和方差是什么?
- 什么是交叉验证?
- 常见的方法有哪些?
- 不同的方法应该在何时何地实施?
- 各种交叉验证的方法是如何工作的?
- 我们如何利用交叉验证来创建更好的模型?
什么是模型验证?
模型验证是一个过程,通过这个过程,我们可以确保我们的模型在“真实世界”中的表现是可以接受的用更专业的术语来说,模型验证允许您预测您的模型在训练中未使用的数据集上将如何表现(模型验证是为什么防止数据泄漏如此重要的一个重要部分)。模型验证很重要,因为我们实际上并不关心模型对我们训练它的数据的预测有多好。我们已经知道用于训练模型的数据的目标值,因此,当任务是对具有相同分布和特征,但具有来自训练集的不同个体值的新数据集进行建模时,考虑模型的稳健性和能力更为重要。引入的第一种模型验证形式通常是所谓的维持验证,通常被认为是交叉验证的最简单形式,因此最容易实现。下面我们来看一个例子。
维持验证
对于这个例子,我们将在加州住房数据的 scikit-learn 数据库中使用线性回归。
# import scikit learn databases
from sklearn import datasets
# import california housing data from sklearn and store data into a variable
calihouses = datasets.fetch_california_housing()
calidata = calihouses.data
一旦数据存储到一个我们可以更容易处理的变量中,我们将转换成一个熊猫数据帧,这样我们就可以更容易地查看和处理数据。
# import pandas and numpy
import pandas as pd
import numpy as np# define the columns names of the data then convert to dataframe
headers = calihouses.feature_names
df = pd.DataFrame(calidata, columns=headers)# print the df and shape to get a better understanding of the data
print(df.shape)
print(df)
现在我们已经看到了我们正在处理的数据,我们可以开始生成模型和交叉验证的过程。在维持验证中,我们将数据分成训练集和测试集。训练集将是创建模型的基础,测试数据将用于验证生成的模型。虽然使用 pandas 方法有(相当容易的)方法来完成这一点,但我们可以利用 scikit-learns 的“train_test_split”方法来完成这一点。
# first store all target data to a variable
y = calihouses.target# create testing and training sets for hold-out verification using scikit learn method
from sklearn import train_test_split
X_train, X_test, y_train, y_test = train_test_split(df, y, test_size = 0.25)# validate set shapes
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
如您所见,我们使用带有三个参数的“train_test_split ”:输入(X)数据、目标(y)数据以及我们想要删除并放入测试数据集的数据的百分比,在本例中为 25%(常见的分割通常是 70-30,取决于与您的数据相关的众多因素)。然后,我们将分割的 X 和 y 数据分配给一组新变量,以便以后使用。
此时,您的输出应该如下所示:
(15480, 8) (15480,)
(5160, 8) (5160,)
现在,我们已经创建了测试/训练分割,我们可以创建一个模型,并基于训练数据生成一些预测。虽然有其他方法可以创建一个模型来显示更多的细节,但是我们将使用 scikit learn 来使我们的生活变得更加简单。我已经包含了几行来计时函数的运行时间,我们将在后面的比较中使用。
# time function using .time methods for later comparison
from timeit import default_timer as timer
start_ho = timer()# fit a model using linear model method from sklearn
from sklearn import linear_model
lm = linear_model.LinearRegression()
model = lm.fit(X_train, y_train)# generate predictions
predictions = lm.predict(X_test)end_ho = timer()# calcualte function runtime
time_ho = (end_ho - start_ho)# show predictions
print(predictions)
让我们在这里暂停一会儿,看看我们做了什么。到目前为止,一切都只是建立一个线性模型,并使用它对数据集进行预测。这就是没有模型验证的情况下能走多远。换句话说,当与测试数据中的实际目标值进行比较时,我们还需要观察模型对测试数据的预测表现如何。我们之前所做的测试/训练分离是必要的,以便划分数据,这样我们现在可以在训练中没有使用的数据上测试模型(请参见:数据泄漏)。现在我们已经有了一个模型,并且已经创建了一些预测,让我们继续我们的坚持验证。
我们将首先绘制给定目标数据与预测目标数据的对比图,以直观显示模型的表现。
# import seaborn and plotly
mport matplotlib
from matplotlib import pyplot as plt
import seaborn as sns# set viz style
sns.set_style('dark')# plot the model
plot = sns.scatterplot(y_test, predictions)
plot.set(xlabel='Given', ylabel='Prediction')# generate and graph y = x line
x_plot = np.linspace(0,5,100)
y_plot = x_plot
plt.plot(x_plot, y_plot, color='r')
输出:

Scatter plot of given vs predicted data, with y = x line charted in red
在一个完美的模型中(可能过度拟合),我们所有的数据点都在那条红线上,但是当我们的数据点接*那个趋势时,我们可以看到这个模型大致适合测试数据。
现在,让我们为这个模型打分,并与后面的方法进行比较。
start_ho_score = timer()# model score (neg_mean_squared_error)
from sklearn import metrics
ho_score = -1*metrics.mean_squared_error(y_test, predictions)
print(ho_score)end_ho_score = timer()ho_score_time = (end_ho_score - start_ho_score)
输出:
-0.5201754311947533
那就是模型验证!我们使用训练数据创建了一个模型,用它来预测测试数据分割部分的结果,然后使用评分方法来确定模型对测试数据的有效性(负均方误差)的度量。这为我们提供了该模型在其他类似数据集上的表现的*似值。
现在,要考虑一些事情。我们验证了我们的模型一次。如果我们做的拆分恰好非常有利于这个模型呢?如果我们所做的分割在日期中引入了很大的偏差会怎么样?通过这样分割,我们不是极大地减少了训练数据集的大小吗?这些是我们进入交叉验证时要考虑的几个问题,但首先是一些背景概念。
模型验证中的偏差和方差是什么?
为了理解偏差和方差,让我们首先解决过度拟合和欠拟合模型。在过度拟合时,当模型与训练数据如此紧密地拟合,以至于它可能会考虑随机噪声或不需要的趋势,这些噪声或趋势在预测后续数据集的目标时不会出现或没有用时,就会生成模型。当模型不够复杂,无法解释数据的总体趋势时,就会出现欠拟合,这对于预测后续数据集中的目标是有用的,例如对多项式趋势使用线性拟合(AWS 的一个令人惊叹的可视化和对这一概念的进一步解释可以在这里找到)。

Source: https://docs.aws.amazon.com/machine-learning/latest/dg/model-fit-underfitting-vs-overfitting.html
当创建一个模型时,我们考虑几种类型的误差:验证误差、测试误差、由于偏差引起的误差,以及由于偏差方差权衡关系中的方差引起的误差(另一个伟大的视觉在这里)。

Source: http://www.luigifreda.com/2017/03/22/bias-variance-tradeoff/
如前所述,我们想知道模型在“真实世界”中的表现其中一部分是验证误差,它由偏差引起的误差和方差引起的误差组成(训练误差不提供模型在未来数据集上表现如何的信息,现在可以放在一边)。
最小化模型验证误差需要找到模型复杂性的点,在该点偏差和方差误差的组合最小化,如链接的视图所示。随着模型复杂性的增加,偏差导致的误差减少,而方差导致的误差增加,从而形成偏差-方差权衡,我们将在后面通过各种交叉验证方法来解决这一问题。
现在让我们定义偏差和方差:
偏置
偏差是由模型的预期值和实际(或“正确”)值之间的差异导致的误差,我们希望通过多次迭代来预测这些值。在准确度和精确度的科学概念中,偏差与准确度非常相似。
差异
方差被定义为模型中不同数据预测之间的可变性导致的误差。在方差中,正确的值没有预测值之间的差值范围重要。当我们运行多个模型创建试验时,方差也会发挥更大的作用。
更多完整的定义和视觉效果点击这里。

Source: http://scott.fortmann-roe.com/docs/BiasVariance.html
在机器学*中,偏差和方差通常作为“偏差-方差权衡”一起讨论,认为有效地最小化一个错误会使该错误更有可能在创建和评估模型时出现。理想情况下,我们会寻找一个模型,其权衡结果是低偏差和低方差,我们会通过使用交叉验证来实现这一点。根据数据集的特征,在创建和评估模型时,一种交叉验证方法可能是实现偏差-方差权衡的更理想方法。
什么是交叉验证?
如果我们做的拆分恰好非常有利于这个模型呢?如果我们所做的分割在日期中引入了很大的偏差会怎么样?通过这样分割,我们不是极大地减少了训练数据集的大小吗?
交叉验证是一种模型验证方法,它以创造性的方式分割数据,以获得“真实世界”模型性能的更好估计,并最小化验证错误。
还记得我们问的那些关于保持验证的问题吗?交叉验证是我们的答案。
K 倍交叉验证
K-fold 验证是一种流行的交叉验证方法,它将数据打乱并分成 k 个折叠(组)。一般来说,通过将一组作为测试数据集,将其他 k-1 组作为训练数据,拟合和评估模型,并记录选择的分数,来执行 K-fold 验证。然后,将每个折叠(组)作为测试数据重复该过程,并将所有分数进行平均,以获得更全面的模型验证分数。(更多的阅读和有用的可视化在这里)。

Source: http://www.ebc.cat/2017/01/31/cross-validation-strategies/#k-fold
在为 k 选择一个值时,每个折叠(组)应该足够大,能够代表模型的(通常 k=10 或 k=5 )并且足够小,能够在合理的时间内计算出来。根据数据集的大小,有时可以试验不同的 k 值。一般来说,随着 k 的增加,偏差减少,方差增加。
让我们用之前的数据集做一个例子。
我们将再次使用线性模型,但这次使用 scikit learn 的 cross_val_predict 方法进行模型验证,该方法将完成生成 K 倍预测的大部分繁重工作。在这种情况下,我选择设置 k=10 。
# store data as an array
X = np.array(df)# again, timing the function for comparison
start_kfold = timer()# use cross_val_predict to generate K-Fold predictions
lm_k = linear_model.LinearRegression()
k_predictions = cross_val_predict(lm_k, X, y, cv=10)
print(k_predictions)end_kfold = timer()kfold_time = (end_kfold - start_kfold)
输出(或*似值):
[4.22358985 4.04800271 3.75534521 ... 0.14474758 0.29600522 0.49525933]
cross_val_predict 获取数据上使用的模型、输入和目标数据,以及一个“cv”参数(实质上是我们的 k 值)并返回每个输入的预测值。现在我们可以绘制预测图,就像我们用保持法做的那样。
# plot k-fold predictions against actual
plot_k = sns.scatterplot(y, k_predictions)
plot_k.set(xlabel='Given', ylabel='Prediction')# generate and graph y = x line
x_plot = np.linspace(0,5,100)
y_plot = x_plot
plt.plot(x_plot, y_plot, color='r')
输出:

现在,让我们获得 10 个生成模型的分数,并将它们绘制成可视化图形。
kfold_score_start = timer()# find the mean score from the k-fold models usinf cross_val_score
kfold_scores = cross_val_score(lm_k, X, y, cv=10, scoring='neg_mean_squared_error')
print(kfold_scores.mean())kfold_score_end = timer()kfold_score_time = (kfold_score_end - kfold_score_start)# plot scores
sns.distplot(kfold_scores, bins=5)
输出:
-0.5509524296956634

您会注意到分数比 holdout 方法离零稍微远一点(不好)。我们稍后再讨论。
留一交叉验证
留一个交叉验证(LOOCV)可以被认为是一种 K 重验证,其中给定数据集中的行数,k=n。除此之外,这些方法都很相似。然而,您会注意到,运行下面的代码将比以前的方法花费更多的时间。我们稍后将深入探讨这一点。
让我们用相同的数据集做一个例子,遵循相同的过程并修改 k :
生成预测:
start_LOO = timer()# generate LOO predictions
LOO_predictions = cross_val_predict(lm_k, X, y, cv=(len(X)))end_LOO = timer()LOO_time = (end_LOO - start_LOO)
绘制预测图:
# plot LOO predictions against actual
plot_LOO = sns.scatterplot(y, LOO_predictions)
plot_LOO.set(xlabel='Given', ylabel='Prediction')# generate and graph y = x line
x_plot = np.linspace(0,5,100)
y_plot = x_plot
plt.plot(x_plot, y_plot, color='r')
输出:

生成并平均分数:
LOO_score_start = timer()# find the mean score from the LOO models using cross_val_score
LOO_score = cross_val_score(lm_k, X, y, cv=(len(X)), scoring='neg_mean_squared_error').mean()
print(LOO_score)LOO_score_end = timer()LOO_score_time = (LOO_score_end - LOO_score_start)
现在让我们比较三种方法的运行时间和分数:
print("Hold out method took", time_ho, "seconds to generate a model and", ho_score_time ,"seconds to generate a MSE of", ho_score)
print("K-Fold method took", kfold_time, 'seconds to generate a model and', kfold_score_time, 'seconds to generate a MSE of', kfold_scores.mean())
print("Leave One Out Cross Validation method took", LOO_time, 'seconds to generate a model and', LOO_score_time, 'seconds to generate a MSE of', LOO_score)
输出:
Hold out 方法用了 0.03958953900000495 秒来生成一个模型,用了 0.0002666661986
K-Fold 方法生成一个模型需要 0.0780906770000583 秒,生成一个-0 的 MSE 需要 0.1253743699999
遗漏一个交叉验证方法花了 152 秒来生成一个模型,花了 161 秒来生成一个-0 的 MSE。16860.686686866617
让我们深入研究一下这些结果,以及前面提到的一些观点。
不同的方法应该在何时何地实施?
正如我们在比较结果中注意到的,我们可以看到 LOOCV 方法比其他两种方法花费了更长的时间。这是因为该方法为数据集中的每一行创建和评估模型,在本例中超过 20,000 行。尽管我们的 MSE 稍低,但考虑到额外的计算需求,这可能不值得。这里有一些启发,可以帮助选择一种方法。
撑出方法
在非常大的数据集上或者在有限的计算资源上,保持方法可以是有效的并且计算成本低廉。对于初学者来说,它也更容易实现和理解。然而,应用于小数据集很少是好的,因为它会显著减少可用的训练数据并损害模型性能。
K 倍交叉验证
K-Fold 在中等大小的数据集上非常有效,尽管通过调整 K 值可以显著改变验证的结果。让我们添加前面的规则;随着 k 的增加,偏差减小,方差和计算要求增加。由于调整 K 值的多功能性,K 倍交叉验证可能是三种方法中最常见的。
LOOCV
LOOCV 在小数据集中最有用,因为它允许在每次迭代中从训练数据中移除最少量的数据。然而,在大型数据集中,为数据集中的每一行生成模型的过程在计算上非常昂贵,因此对于大型数据集来说是不允许的。
不同交叉验证技术的优缺点是什么?
维持验证
在维持验证中,我们只需执行简单的训练/测试分割,让模型适合训练数据,并将其应用于测试数据以生成预测值。我们“坚持”测试数据只能严格用于预测目的。维持验证不是一种交叉验证技术。但是我们必须讨论模型评估的标准方法,以便我们可以将其属性与实际的交叉验证技术进行比较。
就代码而言,维持验证很容易使用。实现很简单,不需要大量的计算能力和时间复杂度。此外,我们可以更好地解释和理解维持验证的结果,因为它们不需要我们弄清楚迭代在大计划中是如何执行的。
但是,在许多情况下,维持验证不能保持数据集的统计完整性。例如,将数据分为定型段和测试段的维持验证由于没有将测试数据合并到模型中而导致偏差。测试数据可能包含一些重要的观察结果。这将导致损害模型的准确性。此外,除了引入验证和/或训练误差之外,这将导致数据的欠拟合和过拟合。
K 倍
在 K-fold 交叉验证中,我们回答了维持验证中固有的许多问题,例如欠拟合/过拟合以及验证和训练错误。这是通过在某次迭代中使用我们的验证集中的所有观察值来完成的。我们计算在每 k 次迭代中计算的所有准确度分数的平均准确度分数。通过这样做,我们可以最大限度地减少初始模型评估技术(维持验证)中可能存在的偏差和变化。
但是,从计算能力上来说,k 倍交叉验证的代价非常大。计算机必须进行几次迭代才能产生正确的准确度分数。理论上,模型的准确度分数将随着每增加 k 次迭代而增加。这将减少偏差,同时增加变化。在本文的后面,当我们尝试对一个包含大约 580,000 个观察值的非常大的数据集应用 k-fold 验证时,我们将看到一个这样的例子。
LOOCV
LOOCV 非常类似于 K-fold,其特殊情况是 K 等于整个数据集的长度(或样本数/行数)。因此,训练集的长度为 k-1,测试集将是数据的单个样本。当我们的数据集不够大,无法进行合理的折叠时,LOOCV 尤其有用。一般来说,LOOCV 的计算成本也较低,尽管这通常是由于其固有的较小数据集倾向于使用它。
但是,LOOCV 往往会产生较高的方差,因为该方法会通过单个测试值拾取数据中所有可能的噪声和异常值。对于非常大的数据集,LOOCV 在计算上将非常昂贵;在这种情况下,使用常规 k 折会更好。
什么时候你不想使用交叉验证?
当处理大型数据集时,交叉验证成为计算上昂贵且费力的模型评估方法。生成预测值最终需要很长时间,因为验证方法必须在 K-Fold 策略中运行 K 次,遍历整个数据集。因此,就时间复杂性而言,交叉验证成为非常昂贵的模型评估策略。我们将通过对一个大约有 580,000 行的非常大的数据集执行正常的维持验证和 K-Fold 交叉验证来检查这一现象。看看你是否能弄明白,为什么它会这样工作(以及新的数据可视化),并评论任何问题。祝你好运!
# upload dataset from kaggle (we're using google colab here, adapt to your IDE)from google.colab import files
uploaded = files.upload()# initialize data framedf = pd.read_csv("covtype.csv")
print(df.head())print(df.tail())# that's a lot of rows!# notice that we use all features of our dataset so that we can illustrate how taxing cross validation will beX=df.loc[:,'Elevation':'Soil_Type40']
y=df['Cover_Type']# some nan values happen to sneak into our dataset so we will fill them upX = X.fillna(method='ffill')
y = y.fillna(method='ffill')# use a K-nearest neighbhors machine learning algorithmneigh = KNeighborsClassifier(n_neighbors=5)# only with 200 folds are we able to generate an accuracy of 80%neigh.fit(X,y)
kFoldStart = time.time()
y_pred = cross_val_predict(neigh, X, y, cv = 200)
kFoldEnd = time.time()
kFoldTime = kFoldEnd - kFoldStart
print("K Fold Validation Accuracy is ", accuracy_score(y, y_pred))# it takes 16 minutes to run the K-Fold cross validation!!!!print(kFoldTime)
输出:
K 倍验证的准确度是 0.8016822371999202
K 倍验证需要 101 秒钟。56681.66668666661
# generate a heatmap of a confusion matrix with predicted and true values of the type of treeslabels = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
cm = confusion_matrix(y_pred, y, labels)
print(cm)
fig = plt.figure()
ax = fig.add_subplot(111)
cax = ax.matshow(cm, vmin=0, vmax=19000)
fig.colorbar(cax)
ax.set_xticklabels([''] + labels)
ax.set_yticklabels([''] + labels)
plt.xlabel('Predicted')
plt.ylabel('True')
plt.show()
输出:
[[166004 46040 27 0 243 43 1902]
【41396 225886 1919 4 1808 1156 293】
【59 3704 31832 442 143 1591 0】
【0 7 267 2119 0 124 0】
【539 4504 208 0 7251 68 5

维持验证:
# split our dataset into training and testing dataX_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.3, random_state=101)# some nan values happen to sneak into our dataset so we will fill them upX_train = X_train.fillna(method='ffill')
y_train = y_train.fillna(method='ffill')# run the holdout validation and make predictions
# it takes only 30 seconds for a normal validation which is still pretty longneigh.fit(X_train, y_train)
holdOutStart = time.time()
holdOutPredictions = neigh.predict(X_test)
holdOutEnd = time.time()
holdOutTime = holdOutEnd - holdOutStart
print("Hold Out Validation takes ", holdOutTime, " seconds")print(accuracy_score(y_test, holdOutPredictions))# notice how much more accurate the holdout validation is compared to the k-fold cross validation# generate a heatmap of a confusion matrix with predicted and true values of the type of treeslabels = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
cm = confusion_matrix(holdOutPredictions, y_test, labels)
print(cm)
fig = plt.figure()
ax = fig.add_subplot(111)
cax = ax.matshow(cm, vmin=0, vmax=8000)
fig.colorbar(cax)
ax.set_xticklabels([''] + labels)
ax.set_yticklabels([''] + labels)
plt.xlabel('Predicted')
plt.ylabel('True')
plt.show()

关键术语:
模型验证:根据生成模型过程中使用的而非附加数据验证生成模型的任何过程。例如交叉验证、K 倍验证、保持验证等。 ****K 重交叉验证:一种交叉验证,将给定数据集分成 k 个组和 k 个模型生成。选择其中一组作为测试数据,其他 k-1 组作为训练数据,生成模型并评分。这个过程重复 k 次,使得每个 k 倍(组)作为一个测试组。 LOOCV: 一种类似于 K 折验证的交叉验证,其中 k 等于正在执行验证的数据集的长度。 偏差:模型的预期值和实际(或“正确”)值之间的差异导致的误差,我们希望通过多次迭代来预测这些值。在准确度和精确度的科学概念中,偏差与准确度非常相似。 方差:模型中不同数据预测之间的可变性所导致的误差。在方差中,正确的值没有预测值之间的差值范围重要。当我们运行多个模型创建试验时,方差也会发挥更大的作用。 欠拟合:当模型与训练数据拟合得如此紧密,以至于它可能会考虑随机噪声或不需要的趋势,这些趋势在预测后续数据集的目标时不会出现或没有用。 过度拟合:当模型不够复杂,不足以解释数据中的一般趋势时发生,这将有助于预测后续数据集中的目标,例如对多项式趋势使用线性拟合。 偏差-方差权衡:偏差导致的误差减少,方差导致的误差增加,这种权衡在模型验证和其他情况下应该最小化。
crossfit Games-2019 公开赛回顾

Photo by John Arano on Unsplash
过去公开赛的结果概述和新 Kaggle 数据集介绍
你好,今年公开赛的赛季又开始了(又一次!?)所以我写这篇文章是为了:
- 在 kaggle 上引入新的数据集
- 有一个关于上次开放的概述
公开赛是为明年在美国举行的“世界杯”比赛进行选拔的过程。该选拔为期 5 周,由 5 场名为 WoD 的比赛组成,公开赛的每位参与者都必须完成比赛才能获得分数,并进入游戏选拔的排行榜(排行榜中的最佳选手将被选出)。隶属于一家名为 affiliate 的健身房的每个人都可以参加这个比赛,比赛有多个组别,如果你觉得自己没有能力做最初的世界(像我一样),你可以做一个“缩放”版本,最初的会有一点点修改,但练*的精神是一样的。
你可以在这个链接找到上一次公开赛练*的更多细节。
我在之前的文章中开始收集 2018 年公开赛的数据,我决定现在分享我收集的关于赛事的部分数据。数据可以在 kaggle 网站上的这个链接找到,你会找到关于 2019 年公开赛的数据。
数据集已经描述过了,我将在未来尝试添加越来越多关于公开赛和比赛的数据(我计划在未来用这些数据做一些 ML 工作)。
那么现在让我们开始分析。
下图显示了运动员在不同组别中的分布情况。

正如我们所看到的,一些最受欢迎的部门是男子和女子部门,大多数运动员不按比例练*。大约有 355418 名运动员参加了比赛。
对于分析的下一步,我将关注男女组,而不是比例(因此大约有 341875 名运动员)。
现在,让我们来看看这些部门的竞争受欢迎程度。

由于竞争来自美国,不出所料,运动员更重要的堡垒在美国,其次是巴西和英国。其他国家比美国小得多。
这个桶的平价是多少?

看起来男性仍然较多,但男女比例相差不远。
我根据公开赛期间的总分对运动员进行了一些简单的分析,并将其分为不同的类别:
- 他们是前 100 名运动员吗?是或否
- 他们是前 1000 名运动员吗?是或否
所以我们来看看有些国家能不能定义在“冠军窝”。
先来看看 crossfit 最流行的国家。

正如我们所见,受欢迎并不是冠军的同义词。如果我们除了美国,在顶级运动员的数量上顺序是不被尊重的。

仍然是同样的逻辑,但每个国家都是前 1000 名中的冠军。
现在让我们来看看基于顶级运动员数量的顶级国家。

正如我们所看到的,在国家名单中有一些新人,一些像新西兰和冰岛这样的小国(就人口和人口而言)正在成为冠军(今年赢得比赛的前女子来自新西兰)。
现在让我们试着找到以下问题的答案:
我试图从世界银行的数据中添加一些与这个国家相关的数据,但我发现这个国家与拥有一些顶级运动员这一事实之间没有真正的联系(我在 Kaggle 数据集中附上了一些数据)。
在关于运动员的数据中,有些人给出了他们的体重和身高(但这是一个自由的领域,所以里面有很多废话)。如果我只是放弃没有填满场地的运动员,我将失去大约 45%的运动员。我只是用身高、体重和身体质量指数对运动员是否在顶端做了一个快速配对图(我去掉了一些不好的值)。

没有真正的最佳身材,似乎在顶级运动员中,人们看起来非常健康,但他们没有疯狂的身材(像大力士或美国队长),他们是正常体型但非常健康的人。
但是现在一个新的问题出现在我的脑海里,那就是他们在公开赛期间努力了吗?
到目前为止,世界还不知道,所以也许这个家伙试图在整个开放期间保持能量,以便在一些特定的世界上表现得更好。有一个基于运动员在每个世界的得分的配对图。

女性的结果与男性相同,但是我们可以看到,对于男性来说,基于大多数运动员没有完成所有世界记录的事实,确实存在聚类。让我们来放大一下真正完成所有世界纪录的运动员。

似乎对于前 100 名运动员来说,没有“赖泽·阿勒”,他们“全押”,他们总是在每个世界上得分最高。前 1000 名看起来比前 100 名更分散,所以我们可以说这 100 名绝对是机器。

从这个分析中我们可以看出:
- crossfit 看起来是一项受男性和女性欢迎的运动,这可能是因为它总是为男性和女性平等地做广告
- 就体重和身高而言,顶尖运动员没有不正常的体格
- 似乎没有赢得公开赛的策略,只是全押
正如我之前所说,我计划在数据集中添加更多数据,并进行一些其他分析,但老实说,这种分析只是开胃小菜(最多 2 小时),如果您对数据集有任何意见/想法/结果,请随时联系我。

最初发表于【http://the-odd-dataguy.com】。
用克罗斯顿模型预测间歇性需求
预测间歇性需求的产品是复杂的。我在这里介绍克罗斯顿的模型,这是专门设计来预测这些时间序列。
克罗斯顿模型
最初的想法
1972 年,J.D. Croston 发表了“间歇需求的预测和库存控制”一文,介绍了一种预测间歇需求产品的新技术。他的想法可以总结为三个简单的步骤:
——当有需求发生时,评估平均需求水平。
-评估两次需求发生之间的平均时间。
-将需求预测为需求水平(发生时)乘以发生的概率。
让我们通过一个例子来一步步实现这一点:
- 如果我们估计平均每四个周期有一次需求发生(即有 25%的机会发生),
- 我们预计,当出现需求时,平均需求水平为 5 个单位,
- 然后,我们预测未来每期的需求为 5 x 0.25 = 1.25。
克罗斯顿模型回答了这个问题 平均每个时期我们会有多少需求?
型号
既然我们理解了克罗斯顿模型的基本思想,我们需要回答两个问题:
- 我们如何估计需求发生的概率?
- 当发生事故时,我们如何估计需求水平?
克罗斯顿使用了一种接*霍尔特&温特斯所用的技术来回答这些问题。对于不同的指数平滑模型,我们将查看每个变量的先前估计(即,水平&周期性)及其最*观察。
需求水平
让我们记下我们的水平估计 a (类似于指数模型),并记下实际需求观察 d 。只有当我们有了实际的观察结果时,我们才会更新我们的水平估计值

如同在不同的指数平滑模型中一样,我们使用学*参数α(0< alpha < 1) to allocate more or less importance to the most recent observations or the historical ones.
周期性
让我们注意两次需求发生 p (对于 p 周期性)之间的时间估计,以及自上一次需求发生 q 以来经过的时间。我们只会在出现需求时更新 p

注意,我们再次使用 alpha 作为学*参数。这与我们用来估计需求水平的参数相同。
预报
预测很简单:需求水平( a )除以周期( p )。

总结
然后我们可以总结整个模型。

如您所见,当没有需求观察时,没有任何参数被更新。这将是这种模式的局限性之一。
见解
克罗斯顿与简单指数平滑法
与简单指数平滑法相比,克罗斯顿法最重要的优点是能够估计两次需求发生之间的时间。让我们看看下图是否有助于我们预测间歇性产品。
不幸的是,这两种模型的 MAE 值大致相同(简单平滑法略占优势):Croston 为 139%,简单平滑法为 130%。
限制
如上图所示,当没有需求发生时,克罗斯顿预测不会改变。这很奇怪,有两个原因。
- 长时间没有需求出现应该是降低我们预测的一个诱因。
- 当我们在很长一段时间没有需求之后,第一次出现需求时(看第 52-60 期),我们降低预测。
这是反直觉的。假设我们处于第 51 期:您刚刚售出 8 件,因此您将预测更新为 3 件/期。从第 52 期到第 58 期,你一件也卖不出去,但是你不会而不是更新你的预测。实际上,你将不得不等待 59 期间的 2 件销售醒来,并将你的预测减少到 2 件。
这没有任何意义。为什么我们不能在无销售期间更新我们的预测,以反映我们预期我们的需求会降低的想法?
预测低需求和间歇性需求的高利润产品是一项挑战。让我们讨论一下技巧和窍门。
medium.com](https://medium.com/analytics-vidhya/how-to-forecast-high-profit-low-volume-products-3331c20cc4bb)
改进克罗斯顿
2011 年,在他们的论文“间歇性需求:将预测与库存过时联系起来”中,Ruud Teunter、Aris Syntetos 和 Zied Babai 提出了对 Croston 模型的改进。他们的想法是允许模型更新(减少)其周期性估计,即使没有需求观察。
这很完美,因为这个限制是我们对 vanilla Croston 模型的主要限制。
TSB 模式
Level—TSB 模型(用于 T eunter,Syntetos&Babai)与常规 Croston 模型相比,不会改变水平的估算方式。
周期— 周期 p 现在将表示为需求发生的概率。实际上, p 现在将表示范围从 0(需求从未出现)到 1(需求在每个周期出现)的频率(即概率)。我们将在每个周期更新这个周期--即使没有需求发生。
如果没有需求发生,周期将
- 减少(因为您预计需求发生的概率会减少)。这种减少将是指数性的(如同所有的指数平滑模型);
- 如果有需求发生,增加。
然后我们有

这可以简化为

请记住,在传统的 Croston 模型中, p 是两次需求发生之间的预期周期数。
我们现在得到 TSB 模型的这组方程。

注意:
-预测 f 定义为周期 p 乘以水平 a (而不是除以它,如原模型中);
—t+1的预测是基于 t+1 的水平和周期估计(而不是 t )定义的。
见解
让我们比较我们的新 TSB 模型与原来的克罗斯顿模型和一个简单的平滑。正如你在下面两张图中看到的,TSB 和简单模型非常相似。实际上,区别在于 TSB 模型跟踪需求水平和需求概率。相比之下,简单平滑模型仅跟踪水平(包括需求发生的可能性)。
多亏了 TSB,我们现在获得了 134%的平均绝对误差。简单平滑模型似乎仍然比该产品的其他两个模型略好。当然,这只是一个例子,克罗斯顿/TSB 可能会在其他产品上做得更好。
自己做
你可以在下面找到克罗斯顿和 TSB 模型的 python 实现。
def Croston(ts,extra_periods=1,alpha=0.4):d = np.array(ts) # Transform the input into a numpy array
cols = len(d) # Historical period length
d = np.append(d,[np.nan]*extra_periods) # Append np.nan into the demand array to cover future periods
#level (a), periodicity(p) and forecast (f)
a,p,f = np.full((3,cols+extra_periods),np.nan)
q = 1 #periods since last demand observation
# Initialization
first_occurence = np.argmax(d[:cols]>0)
a[0] = d[first_occurence]
p[0] = 1 + first_occurence
f[0] = a[0]/p[0]# Create all the t+1 forecasts
for t in range(0,cols):
if d[t] > 0:
a[t+1] = alpha*d[t] + (1-alpha)*a[t]
p[t+1] = alpha*q + (1-alpha)*p[t]
f[t+1] = a[t+1]/p[t+1]
q = 1
else:
a[t+1] = a[t]
p[t+1] = p[t]
f[t+1] = f[t]
q += 1
# Future Forecast
a[cols+1:cols+extra_periods] = a[cols]
p[cols+1:cols+extra_periods] = p[cols]
f[cols+1:cols+extra_periods] = f[cols]
df = pd.DataFrame.from_dict({"Demand":d,"Forecast":f,"Period":p,"Level":a,"Error":d-f})return dfdef Croston_TSB(ts,extra_periods=1,alpha=0.4,beta=0.4):d = np.array(ts) # Transform the input into a numpy array
cols = len(d) # Historical period length
d = np.append(d,[np.nan]*extra_periods) # Append np.nan into the demand array to cover future periods
#level (a), probability(p) and forecast (f)
a,p,f = np.full((3,cols+extra_periods),np.nan)# Initialization
first_occurence = np.argmax(d[:cols]>0)
a[0] = d[first_occurence]
p[0] = 1/(1 + first_occurence)
f[0] = p[0]*a[0]
# Create all the t+1 forecasts
for t in range(0,cols):
if d[t] > 0:
a[t+1] = alpha*d[t] + (1-alpha)*a[t]
p[t+1] = beta*(1) + (1-beta)*p[t]
else:
a[t+1] = a[t]
p[t+1] = (1-beta)*p[t]
f[t+1] = p[t+1]*a[t+1]
# Future Forecast
a[cols+1:cols+extra_periods] = a[cols]
p[cols+1:cols+extra_periods] = p[cols]
f[cols+1:cols+extra_periods] = f[cols]
df = pd.DataFrame.from_dict({"Demand":d,"Forecast":f,"Period":p,"Level":a,"Error":d-f})return dfdef Croston(ts,extra_periods=1,alpha=0.4):d = np.array(ts) # Transform the input into a numpy array
cols = len(d) # Historical period length
d = np.append(d,[np.nan]*extra_periods) # Append np.nan into the demand array to cover future periods
#level (a), periodicity(p) and forecast (f)
a,p,f = np.full((3,cols+extra_periods),np.nan)
q = 1 #periods since last demand observation
# Initialization
first_occurence = np.argmax(d[:cols]>0)
a[0] = d[first_occurence]
p[0] = 1 + first_occurence
f[0] = a[0]/p[0]# Create all the t+1 forecasts
for t in range(0,cols):
if d[t] > 0:
a[t+1] = alpha*d[t] + (1-alpha)*a[t]
p[t+1] = alpha*q + (1-alpha)*p[t]
f[t+1] = a[t+1]/p[t+1]
q = 1
else:
a[t+1] = a[t]
p[t+1] = p[t]
f[t+1] = f[t]
q += 1
# Future Forecast
a[cols+1:cols+extra_periods] = a[cols]
p[cols+1:cols+extra_periods] = p[cols]
f[cols+1:cols+extra_periods] = f[cols]
df = pd.DataFrame.from_dict({"Demand":d,"Forecast":f,"Period":p,"Level":a,"Error":d-f})return df
关于作者
👉我们在 LinkedIn 上连线吧!
[## Nicolas vande put——顾问,创始人——供应链| LinkedIn
查看 Nicolas Vandeput 在世界上最大的职业社区 LinkedIn 上的个人资料。尼古拉斯有 7 份工作列在…
www.linkedin.com](https://www.linkedin.com/in/vandeputnicolas/)
icolas Vandeput 是一名供应链数据科学家,擅长需求预测和库存优化。他在 2016 年创立了自己的咨询公司 SupChains ,并在 2018 年共同创立了 SKU 科学——一个快速、简单、实惠的需求预测平台。尼古拉斯对教育充满热情,他既是一个狂热的学*者,也喜欢在大学教学:自 2014 年以来,他一直在比利时布鲁塞尔为硕士学生教授预测和库存优化。自 2020 年以来,他还在法国巴黎的 CentraleSupelec 教授这两个科目。他于 2018 年出版了 供应链预测的数据科学(2021 年第 2 版)和 2020 年出版了 库存优化:模型与模拟 。

使用 Python 进行加密货币分析——购买并持有

Bitcoin, Ethereum, and Litecoin
在这一部分中,我将使用买入并持有策略来分析最*两个月哪个硬币(比特币、以太坊或莱特币)最赚钱。我们将通过对这 3 种加密货币的分析,尝试给出一个客观的答案。
如果你错过了我关于这个主题的其他文章:
我用 Python 写的关于股票市场和加密货币分析的文章的精选列表。
romanorac.medium.com](https://romanorac.medium.com/stock-market-analysis-in-python-b71bf50151d9)
这里有几个你可能会感兴趣的链接:
- [Complete your Python analyses 10x faster with Mito](https://trymito.io/) [Product]- [Free skill tests for Data Scientists & ML Engineers](https://aigents.co/skills) [Test]- [All New Self-Driving Car Engineer Nanodegree](https://imp.i115008.net/c/2402645/1116216/11298)[Course]
你愿意多看一些这样的文章吗?如果是这样,你可以点击上面的任何链接来支持我。其中一些是附属链接,但你不需要购买任何东西。
放弃
我不是交易者,这篇博客也不是金融建议。这纯粹是入门知识。当我们分析这一时期的巨大增长时,这个结论可能会产生误导。
要求
其他要求,见本系列我之前的博文。
获取数据
要获得最新的数据,请访问之前的博客文章,在那里我描述了如何使用 Cryptocompare API 下载它。你也可以使用我在这个例子中使用的数据。
首先,让我们从比特币基地交易所下载 BTC、瑞士联邦理工学院和长期资本公司的每小时数据。这次我们使用每小时的时间间隔,因为它具有更高的粒度。Cryptocompare API 限制对 2000 个样本的响应,这是每枚硬币 2.7 个月的数据。
import pandas **as** pd**def** **get_filename**(from_symbol, to_symbol, exchange, datetime_interval, download_date):
**return** '%s_%s_%s_%s_%s.csv' **%** (from_symbol, to_symbol, exchange, datetime_interval, download_date)**def** **read_dataset**(filename):
**print**('Reading data from %s' **%** filename)
df **=** pd**.**read_csv(filename)
df**.**datetime **=** pd**.**to_datetime(df**.**datetime) *# change to datetime*
df **=** df**.**set_index('datetime')
df **=** df**.**sort_index() *# sort by datetime*
**print**(df**.**shape)
**return** df
加载数据
df_btc **=** read_dataset(get_filename('BTC', 'USD', 'Coinbase', 'hour', '2017-12-24'))
df_eth **=** read_dataset(get_filename('ETH', 'USD', 'Coinbase', 'hour', '2017-12-24'))
df_ltc **=** read_dataset(get_filename('LTC', 'USD', 'Coinbase', 'hour', '2017-12-24'))df_btc.head()

Few entries in the dataset.
提取收盘价
我们将分析收盘价,即小时时段收盘时的价格。我们将 BTC、ETH 和 LTC 的收盘价合并到一个数据框架中,以便于分析。
df **=** pd**.**DataFrame({'BTC': df_btc**.**close,
'ETH': df_eth**.**close,
'LTC': df_ltc**.**close})df**.**head()

分析
基本统计
在 2.7 个月的时间里,三种加密货币的波动都很大,如下表所示。
对于每个硬币,我们计算事件的数量,并计算平均值、标准差、最小值、四分位数和最大收盘价。
观察结果
- 在 2.7 个月内,BTC 最高价和最低价之间的差额超过了 15000 美元。
- LTC 从 48.61 美元暴涨到某一点的 378.66 美元,涨幅为 678.98%。
df**.**describe()

让我们更深入地了解 LTC
我们用方框图将上表中的数据可视化。使用四分位间距 (IQR)的方法,箱线图显示了数据集的四分位以及被确定为异常值的点。换句话说,IQR 是第三个四分位数(75%)减去第一个四分位数(25%)。
在下面的方框图中,我们看到在过去的 2.7 个月中,LTC 的小时收盘价大部分时间都在 50 美元到 100 美元之间。所有超过 150 美元的值都是异常值(使用 IQR)。请注意,异常值是特定于这个数据样本的。
import seaborn **as** snsax **=** sns**.**boxplot(data**=**df['LTC'], orient**=**"h")

LTC Boxplot with closing hourly prices
LTC 收盘价直方图
我们来估计一下 LTC 收盘价的频率分布。直方图显示了 LTC 具有特定值的小时数。
观察结果
- LTC 收盘价在许多小时内都没有超过 100 美元。
- 它具有右偏分布,因为自然极限阻止了一边的结果。
- 蓝色虚线(中值)显示一半时间收盘价低于 63.50 美元。
df['LTC']**.**hist(bins**=**30, figsize**=**(15,10))**.**axvline(df['LTC']**.**median(), color**=**'b', linestyle**=**'dashed', linewidth**=**2)

Histogram for LTC with the median
可视化绝对收盘价
下图显示了绝对收盘价。这没有多大用处,因为 BTC 的收盘价远高于瑞士联邦理工学院和长期资本公司的价格。
df**.**plot(grid**=**True, figsize**=**(15, 10))

Absolute closing price changes of BTC, ETH and LTC
可视化收盘价的相对变化
我们感兴趣的是价格的相对变化,而不是绝对价格,因此我们使用三种不同的 y 轴刻度。
我们看到收盘价是一致的。当一枚硬币的收盘价上涨时,另一枚也会上涨。
import matplotlib.pyplot **as** plt
import numpy **as** npfig, ax1 **=** plt**.**subplots(figsize**=**(20, 10))
ax2 **=** ax1**.**twinx()
rspine **=** ax2**.**spines['right']
rspine**.**set_position(('axes', 1.15))
ax2**.**set_frame_on(True)
ax2**.**patch**.**set_visible(False)
fig**.**subplots_adjust(right**=**0.7)df['BTC']**.**plot(ax**=**ax1, style**=**'b-')
df['ETH']**.**plot(ax**=**ax1, style**=**'r-', secondary_y**=**True)
df['LTC']**.**plot(ax**=**ax2, style**=**'g-')*# legend*
ax2**.**legend([ax1**.**get_lines()[0],
ax1**.**right_ax**.**get_lines()[0],
ax2**.**get_lines()[0]],
['BTC', 'ETH', 'LTC'])

Relative closing price changes of BTC, ETH and LTC
衡量收盘价的相关性
我们计算 BTC、瑞士联邦理工学院和长期资本公司的收盘价之间的皮尔逊相关。皮尔逊相关是两个变量 X 和 y 之间线性相关的度量。它的值介于+1 和-1 之间,其中 1 表示完全正线性相关,0 表示没有线性相关,1 表示完全负线性相关。相关矩阵是对称的,所以我们只显示下半部分。
Sifr 数据每日更新许多加密货币的皮尔逊相关性。
观察结果
- 收盘价没有被归一化,参见日志返回,我们在计算相关性之前归一化收盘价。
- BTC、ETH 和 LTC 在过去 2 个月内高度相关。这意味着,当 BTC 收盘价上涨时,ETH 和 LTC 也跟着上涨。
- ETH 和 LTC 的相关系数更高,为 0.9565。
import seaborn **as** sns
import matplotlib.pyplot **as** plt*# Compute the correlation matrix*
corr **=** df**.**corr()*# Generate a mask for the upper triangle*
mask **=** np**.**zeros_like(corr, dtype**=**np**.**bool)
mask[np**.**triu_indices_from(mask)] **=** True*# Set up the matplotlib figure*
f, ax **=** plt**.**subplots(figsize**=**(10, 10))*# Draw the heatmap with the mask and correct aspect ratio*
sns**.**heatmap(corr, annot**=**True, fmt **=** '.4f', mask**=**mask, center**=**0, square**=**True, linewidths**=.**5)

Pearson Correlation for BTC, ETH and LTC
买入并持有策略
买入并持有是一种被动投资策略,投资者买入一种加密货币并长期持有,不管市场如何波动。
让我们用买入并持有策略来分析过去 2.7 个月的回报。我们计算回报百分比,其中 t 代表某个时间段, price0 是初始收盘价:

df_return **=** df**.**apply(**lambda** x: x **/** x[0])
df_return**.**head()

可视化返回
我们显示,在 2017 年 10 月 2 日至 2017 年 12 月 24 日期间,LTC 是最盈利的。
df_return**.**plot(grid**=**True, figsize**=**(15, 10))**.**axhline(y **=** 1, color **=** "black", lw **=** 2)

The factor of returns for BTC, ETH and LTC in 2.7 months
结论
我们分析的加密货币波动很大,但都在给定的 2.7 个月内上涨。
百分比增长呢?

The percentage increase for BTC, ETH and LTC in 2.7 months
1000 美元我们能买多少硬币?

The number of coins we could buy with $1000 a while ago
我们能赚多少钱?

The amount of money we would make if we invested $1000 a while ago
在你走之前
要运行这段代码,请下载 Jupyter 笔记本。
在 Twitter 上关注我,在那里我定期发布关于数据科学和机器学*的消息。

Photo by Courtney Hedger on Unsplash
使用 Python 进行加密货币分析—日志回报

Photo by André François McKenzie on Unsplash
在之前的文章中,我们分析了加密货币的原始价格变化。这种方法的问题是不同加密货币的价格没有标准化,我们无法使用可比的指标。
在这篇文章中,我们描述了使用对数收益分析价格变化的好处。你可以下载这个 Jupyter 笔记本和数据。
如果你错过了我关于这个主题的其他文章:
我用 Python 写的关于股票市场和加密货币分析的文章的精选列表。
romanorac.medium.com](https://romanorac.medium.com/stock-market-analysis-in-python-b71bf50151d9)
这里有几个你可能会感兴趣的链接:
- [Labeling and Data Engineering for Conversational AI and Analytics](https://www.humanfirst.ai/)- [Data Science for Business Leaders](https://imp.i115008.net/c/2402645/880006/11298) [Course]- [Intro to Machine Learning with PyTorch](https://imp.i115008.net/c/2402645/788201/11298) [Course]- [Become a Growth Product Manager](https://imp.i115008.net/c/2402645/803127/11298) [Course]- [Deep Learning (Adaptive Computation and ML series)](https://amzn.to/3ncTG7D) [Ebook]- [Free skill tests for Data Scientists & Machine Learning Engineers](https://aigents.co/skills)
上面的一些链接是附属链接,如果你通过它们购买,我会赚取佣金。请记住,我链接课程是因为它们的质量,而不是因为我从你的购买中获得的佣金。

Bitcoin, Ethereum, and Litecoin Log Returns
要求
其他要求,见我这个系列的第一篇博文。
加载数据
import pandas **as** pddf_btc **=** pd**.**read_csv('BTC_USD_Coinbase_hour_2017-12-24.csv', index_col**=**'datetime')
df_eth **=** pd**.**read_csv('ETH_USD_Coinbase_hour_2017-12-24.csv', index_col**=**'datetime')
df_ltc **=** pd**.**read_csv('LTC_USD_Coinbase_hour_2017-12-24.csv', index_col**=**'datetime')df **=** pd**.**DataFrame({'BTC': df_btc**.**close,
'ETH': df_eth**.**close,
'LTC': df_ltc**.**close})
df**.**index **=** df**.**index**.**map(pd**.**to_datetime)
df **=** df**.**sort_index()df**.**head()

与价格相比,使用回报的好处是标准化:以可比较的度量标准衡量所有变量,从而能够评估两个或更多变量之间的分析关系,尽管这些变量来自不等值的价格序列(有关详细信息,请参见为什么记录回报)。
让我们将回报定义为:

计算日志回报
Why Log Returns 一书的作者概述了使用 Log Returns 代替 Returns 的几个好处,因此我们将 returns 等式转换为 log returns 等式:

现在,我们将对数回报方程应用于加密货币的收盘价:
import numpy **as** np*# shift moves dates back by 1*
df_change **=** df**.**apply(**lambda** x: np**.**log(x) **-** np**.**log(x**.**shift(1)))df_change**.**head()

可视化日志返回
我们绘制了过去 50 小时收盘价的归一化变化。对数差异可以解释为百分比变化。
df_change[:50]**.**plot(figsize**=**(15, 10))**.**axhline(color**=**'black', linewidth**=**2)

Bitcoin, Ethereum, and Litecoin Log Returns for last 50 hours
LTC 价格服从对数正态分布吗?
如果我们假设价格服从对数正态分布,那么 log(1+ri)很方便地服从正态分布(详见为什么 log 返回)
在下图中,我们绘制了 LTC 小时收盘价的分布。我们还估计对数正态分布的参数,并用红线标出估计的对数正态分布。
from scipy.stats import lognorm
import matplotlib.pyplot **as** pltfig, ax **=** plt**.**subplots(figsize**=**(10, 6))values **=** df['LTC']shape, loc, scale **=** stats**.**lognorm**.**fit(values)
x **=** np**.**linspace(values**.**min(), values**.**max(), len(values))
pdf **=** stats**.**lognorm**.**pdf(x, shape, loc**=**loc, scale**=**scale)
label **=** 'mean=%.4f, std=%.4f, shape=%.4f' **%** (loc, scale, shape)ax**.**hist(values, bins**=**30, normed**=**True)
ax**.**plot(x, pdf, 'r-', lw**=**2, label**=**label)
ax**.**legend(loc**=**'best')

Distribution of LTC prices
LTC 日志回报是否正态分布?
在下面的图表中,我们绘制了 LTC 日志回报的分布。我们还估计正态分布的参数,并用红线标出估计的正态分布。
import pandas **as** pd
import numpy **as** np
import scipy.stats **as** stats
import matplotlib.pyplot **as** pltvalues **=** df_change['LTC'][1:] *# skip first NA value*
x **=** np**.**linspace(values**.**min(), values**.**max(), len(values))loc, scale **=** stats**.**norm**.**fit(values)
param_density **=** stats**.**norm**.**pdf(x, loc**=**loc, scale**=**scale)
label **=** 'mean=%.4f, std=%.4f' **%** (loc, scale)fig, ax **=** plt**.**subplots(figsize**=**(10, 6))
ax**.**hist(values, bins**=**30, normed**=**True)
ax**.**plot(x, param_density, 'r-', label**=**label)
ax**.**legend(loc**=**'best')

Distribution of LTC Log Returns
皮尔逊相关性与对数回报
我们从对数收益计算皮尔逊相关。以下相关矩阵的值与 Sifr 数据中的值相似。存在差异是因为:
- 我们不计算成交量加权平均日价格
- 不同时间段(每小时和每天),
- 不同的数据来源(比特币基地和波洛涅克斯)。
观察结果
- BTC 和瑞士联邦理工学院有着适度的正面关系,
- LTC 和 ETH 有很强的正相关关系。
import seaborn **as** sns
import matplotlib.pyplot **as** plt*# Compute the correlation matrix*
corr **=** df_change**.**corr()*# Generate a mask for the upper triangle*
mask **=** np**.**zeros_like(corr, dtype**=**np**.**bool)
mask[np**.**triu_indices_from(mask)] **=** True*# Set up the matplotlib figure*
f, ax **=** plt**.**subplots(figsize**=**(10, 10))*# Draw the heatmap with the mask and correct aspect ratio*
sns**.**heatmap(corr, annot**=**True, fmt **=** '.4f', mask**=**mask, center**=**0, square**=**True, linewidths**=.**5)

The correlation matrix with BTC, ETH and LTC
结论
我们用一个实例展示了如何从原始价格计算对数收益。这样,我们将价格标准化,这简化了进一步的分析。我们还展示了如何估计正态和对数正态分布的参数。
在你走之前
在 Twitter 上关注我,在那里我定期发布关于数据科学和机器学*的消息。

基于 Python 的加密货币分析——MACD

Photo by Thought Catalog on Unsplash
我决定周末学*加密货币分析。我破解了代码来下载每日比特币价格,并对其应用简单的交易策略。
如果你错过了我关于这个话题的其他文章:
我用 Python 写的关于股票市场和加密货币分析的文章的精选列表。
romanorac.medium.com](https://romanorac.medium.com/stock-market-analysis-in-python-b71bf50151d9)
这里有几个你可能会感兴趣的链接:
- [Complete your Python analyses 10x faster with Mito](https://trymito.io/) [Product]- [Free skill tests for Data Scientists & ML Engineers](https://aigents.co/skills) [Test]- [All New Self-Driving Car Engineer Nanodegree](https://imp.i115008.net/c/2402645/1116216/11298)[Course]
您愿意阅读更多这样的文章吗?如果是这样,你可以点击上面的任何链接来支持我。其中一些是附属链接,但你不需要购买任何东西。
传统视图
已经存在执行这种分析的工具,例如 tradingview ,但是在 Python 中进行这种分析可以实现更深入的分析。

Interactive plot showing Bitcoin price
放弃
我不是交易者,这篇博客也不是金融建议。这纯粹是入门知识。当我们分析巨大增长的时期时,这里的结论可能是误导的。
要求
- Python 3
- Jupyter 笔记本
- 熊猫数据分析库
- 散景互动可视化库
- 股票统计/指标计算助手
获取加密货币数据
我们在 Bitstamp exchange 上下载每日以美元计算的比特币数据。也支持其他交换。
from_symbol **=** 'BTC'
to_symbol **=** 'USD'
exchange **=** 'Bitstamp'
datetime_interval **=** 'day'
cryptocompare API 返回以下列:
- 开盘,该时段开盘的价格、
- 高点,期间达到的最高价位,
- 低点,期间达到的最低价,
- 收盘,该时段收盘的价格、
- 成交量从开始,以交易的基础货币表示的成交量,
- 成交量到,交易货币的成交量。
我们下载数据并存储到一个文件中。
import requests
from datetime import datetime **def** **get_filename**(from_symbol, to_symbol, exchange, datetime_interval, download_date):
**return** '%s_%s_%s_%s_%s.csv' **%** (from_symbol, to_symbol, exchange, datetime_interval, download_date) **def** **download_data**(from_symbol, to_symbol, exchange, datetime_interval):
supported_intervals **=** {'minute', 'hour', 'day'}
**assert** datetime_interval **in** supported_intervals,\
'datetime_interval should be one of %s' **%** supported_intervals **print**('Downloading %s trading data for %s %s from %s' **%**
(datetime_interval, from_symbol, to_symbol, exchange))
base_url **=** 'https://min-api.cryptocompare.com/data/histo'
url **=** '%s%s' **%** (base_url, datetime_interval) params **=** {'fsym': from_symbol, 'tsym': to_symbol,
'limit': 2000, 'aggregate': 1,
'e': exchange}
request **=** requests**.**get(url, params**=**params)
data **=** request**.**json()
**return** data **def** **convert_to_dataframe**(data):
df **=** pd**.**io**.**json**.**json_normalize(data, ['Data'])
df['datetime'] **=** pd**.**to_datetime(df**.**time, unit**=**'s')
df **=** df[['datetime', 'low', 'high', 'open',
'close', 'volumefrom', 'volumeto']]
**return** df **def** **filter_empty_datapoints**(df):
indices **=** df[df**.**sum(axis**=**1) **==** 0]**.**index
**print**('Filtering %d empty datapoints' **%** indices**.**shape[0])
df **=** df**.**drop(indices)
**return** df data **=** download_data(from_symbol, to_symbol, exchange, datetime_interval)
df **=** convert_to_dataframe(data)
df **=** filter_empty_datapoints(df)current_datetime **=** datetime**.**now()**.**date()**.**isoformat()
filename **=** get_filename(from_symbol, to_symbol, exchange, datetime_interval, current_datetime)
**print**('Saving data to %s' **%** filename)
df**.**to_csv(filename, index**=**False)
读取数据
我们从文件中读取数据,所以我们不需要再次下载它。
import pandas **as** pd**def** **read_dataset**(filename):
**print**('Reading data from %s' **%** filename)
df **=** pd**.**read_csv(filename)
*# change type from object to datetime
* df**.**datetime **=** pd**.**to_datetime(df**.**datetime)
df **=** df**.**set_index('datetime')
df **=** df**.**sort_index() *# sort by datetime*
**print**(df**.**shape)
**return** dfdf **=** read_dataset(filename)
交易策略
交易策略是一套客观的规则,定义了交易进场和出场必须满足的条件。
我们将采用移动平均线收敛发散(MACD)交易策略,这是技术分析中常用的指标。MACD 计算两条不同长度的移动平均线来确定趋势方向和持续时间。然后,取这两条移动平均线(MACD 线)和这些移动平均线的指数移动平均线(信号线)之间的差值。Tradeview 有一篇关于 MACD 的精彩博文。
正如我们在下面的例子中看到的:
- 当 MACD 线穿过 MACD 信号线时退出交易,
- 当 MACD 线穿过 MACD 信号线时,进场交易(买入)。
计算交易策略
我们使用 stockstats 软件包来计算 MACD。
from stockstats import StockDataFrame
df **=** StockDataFrame**.**retype(df)
df['macd'] **=** df**.**get('macd') *# calculate MACD*
stockstats 向数据集添加了 5 列:
- close_12_ema 是快速 12 天指数移动平均线,
- close_26_ema 是慢 26 天指数移动平均线,
- macd 是 macd 线,
- MACD是信号线,
- macdh 是 MACD 直方图。

Few entries in the dataset.
可视化交易策略
我们使用散景交互式图表来绘制数据。
折线图用蜡烛线显示每日收盘价(放大)。蜡烛图显示特定时期的最高价、最低价、开盘价和收盘价。Tradeview 有一篇关于烛台图的很棒的博文。
在线图下方,我们用 MACD 线(蓝色)、信号线(橙色)和柱状图(紫色)绘制了 MACD 策略。
from math import pifrom bokeh.plotting import figure, show, output_notebook, output_file
output_notebook()datetime_from **=** '2016-01-01 00:00'
datetime_to **=** '2017-12-10 00:00' **def** **get_candlestick_width**(datetime_interval):
**if** datetime_interval **==** 'minute':
**return** 30 ***** 60 ***** 1000 *# half minute in ms*
**elif** datetime_interval **==** 'hour':
**return** 0.5 ***** 60 ***** 60 ***** 1000 *# half hour in ms*
**elif** datetime_interval **==** 'day':
**return** 12 ***** 60 ***** 60 ***** 1000 *# half day in ms* df_limit **=** df[datetime_from: datetime_to]**.**copy()
inc **=** df_limit**.**close **>** df_limit**.**open
dec **=** df_limit**.**open **>** df_limit**.**closetitle **=** '%s datapoints from %s to %s for %s and %s from %s with MACD strategy' **%** (
datetime_interval, datetime_from, datetime_to, from_symbol, to_symbol, exchange)
p **=** figure(x_axis_type**=**"datetime", plot_width**=**1000, title**=**title)p**.**line(df_limit**.**index, df_limit**.**close, color**=**'black')*# plot macd strategy*
p**.**line(df_limit**.**index, 0, color**=**'black')
p**.**line(df_limit**.**index, df_limit**.**macd, color**=**'blue')
p**.**line(df_limit**.**index, df_limit**.**macds, color**=**'orange')
p**.**vbar(x**=**df_limit**.**index, bottom**=**[
0 **for** _ **in** df_limit**.**index], top**=**df_limit**.**macdh, width**=**4, color**=**"purple")*# plot candlesticks*
candlestick_width **=** get_candlestick_width(datetime_interval)
p**.**segment(df_limit**.**index, df_limit**.**high,
df_limit**.**index, df_limit**.**low, color**=**"black")
p**.**vbar(df_limit**.**index[inc], candlestick_width, df_limit**.**open[inc],
df_limit**.**close[inc], fill_color**=**"#D5E1DD", line_color**=**"black")
p**.**vbar(df_limit**.**index[dec], candlestick_width, df_limit**.**open[dec],
df_limit**.**close[dec], fill_color**=**"#F2583E", line_color**=**"black")output_file("visualizing_trading_strategy.html", title**=**"visualizing trading strategy")
show(p)

在你走之前
在 Twitter 上关注我,在那里我定期发布关于数据科学和机器学*的消息。

Photo by Courtney Hedger on Unsplash
基于网络抓取的加密货币市场分析

Photo by Eftakher Alam on Unsplash
自 2009 年推出加密货币——比特币以来,整个加密货币市场前景看好,持续繁荣。迄今为止,已经创造了超过 4000 种替代币(比特币之外的替代加密货币)。此外,加密货币因其波动性而臭名昭著。关注市场可能非常困难,尤其是对新手投资者而言。
网络抓取在加密货币交易中的重要性?
Web 抓取 主要用于电子商务和销售中的价格监控和线索生成。如今,越来越多的投资者开始利用在线金融技术。它自动化了从多个来源提取数据的过程,并以结构化格式存储数据以供进一步分析。
在这篇文章中,我将分解步骤,向你展示如何使用网络搜集来克服投资障碍:
- 提取历史加密市场数据,进行全面的市场分析
- 监控加密定价,这样你就能像专业交易者一样清楚地了解整个市值。
我将使用的工具是 Octoparse 。它是全球领先的 网页抓取提供 r 。通过企业级服务,Octoparse 创建了一个抓取管道,并将数据集成到数据库或云 API 中。另一方面,对于想要更少数据量的用户来说,它是完全免费的。
想投资比特币但不知道时机对不对。我将提取 2019 年 1 月至今的比特币市场数据,以便我可以看到市场趋势,并决定我是否应该投资。在这种情况下,转到 CoinMarketCap 页面并设置一个时间范围
然后,在八分音符中:
第一步:以高级模式启动提取任务,上传 URL。
第二步:点击图表的日期。Octoparse 会找到具有相同属性的相似元素。遵循操作提示,选择所有字段,包括开盘、盘高、盘低、收盘、成交量和市值。
第三步:开始提取,将数据导出到 Excel 中。
你应该能得到这样的整张图表:2019 年比特币市场。

分析
现在我们有了 Excel 中的数据。我们可以用 Excel 瀑布图来绘制。

正如你所看到的,市场遵循一个清晰的模式,价格上涨,下跌,稳定。价格上涨之后将是一个巩固期。急剧下降之后,很快就会出现急剧上升。这是有道理的,因为当市场波动太大时,它需要冷却到一个稳定的水平。6 月份以来的市场波动性较大。与年初相比,合并窗口越来越短,价格快速上涨和下跌。在大多数情况下,波动性越高,投资风险就越大。在图表的末尾,它表明价格波动呈上升趋势,没有任何稳定的迹象。这清楚地表明,现在不是一年中投资比特币的最佳时机。
既然市场价格是波动的,那么对于看客和投资者来说,都需要时刻关注市场。网络抓取可以监控价格变化,并传送到您的数据库,供以后访问。所以每当价格触及某一点时,你可以及时采取行动。
用/建立一个新项目。我将选择每个硬币名称来提取价格数据。Octoparse 通过选择所有列表名称来查找所有相似的元素。我从动作表中单击“TR”命令,告诉 Octoparse 提取行而不是列。

然后,我按照操作提示,单击“选择所有子元素”来定义来自同一行的提取字段。Octoparse 将以相同的模式选择所有数据字段。然后,我按照操作提示上的指导,点击“全选”命令来确认选择。如果选择成功,所选字段将变为绿色。

然后点击“提取循环中的数据”提取数据。现在我们有了所有的数据,包括市值、价格、流通供应量、成交量以及 1 小时、24 小时和 7 天内的变化。

在仪表板上,我将提取计划设置为 30 分钟间隔。因此,我的数据库每 30 分钟更新一次。我可以把手从桌子上拿开,同时还能监控市场。
网络抓取还有什么可以帮助加密货币分析的?
情绪分析:
情感分析通过自然语言处理来衡量人们的观点。这一想法是为了从社交媒体上监控公众对市场的情绪,因为网络是大多数加密货币投资者表达市场情绪的主要门户。Guus 认为“市场情绪的变化将与市场指数价值的变化相关联。”(Guss,2017)此外,Kaminski (2014)还指出,从 Twitter 收集的数据显示,情绪与收盘价、交易量和日内价格之间存在显著的相关性。
新闻聚合器 :
对于管理专业人士来说,关注新闻媒体以了解最新的比特币交易信息是日常工作的基础。网络抓取工具可以从各种平台收集新闻信息并发送到你的邮箱,这样你就可以节省搜索的时间。
来源:
https://lib . ugent . be/full txt/rug 01/002/508/647/rug 01-002508647 _ 2018 _ 0001 _ AC . pdf
加密货币(或加密货币)是一种数字资产,旨在作为一种使用强…
en.wikipedia.org](https://en.wikipedia.org/wiki/Cryptocurrency)
https: //coinmarketcap.com
j . kamin ski(2014 年)。用 twitter 信号预测比特币市场。arXiv 预印本 arXiv:1406.7577。
原载于 2019 年 8 月 1 日https://www.octoparse.com。
使用深度学*的加密货币价格预测
使用 LSTM 神经网络的完整机器学*真实世界应用程序走查

Photo by Chris Liverani on Unsplash
被困在付费墙后面?点击这里阅读完整故事与我的朋友链接!
由于其市值连续几个月呈指数增长,加密货币的受欢迎程度在 2017 年飙升。价格在 2018 年 1 月达到峰值,超过 8000 亿美元。
尽管机器学*已经成功地通过大量不同的时间序列模型预测了股票市场价格,但它在预测加密货币价格方面的应用却受到了很大的限制。这背后的原因很明显,因为加密货币的价格取决于许多因素,如技术进步、内部竞争、市场交付压力、经济问题、安全问题、政治因素等。如果采取明智的发明策略,它们的高波动性导致高利润的巨大潜力。不幸的是,由于缺乏指数,与股票市场预测等传统金融预测相比,加密货币相对不可预测。
在这篇博客中,我将通过四个步骤来预测加密货币的价格:
- 获取实时 crptocurrency 数据。
- 为培训和测试准备数据。
- 用 LSTM 神经网络预测人民币价格。
- 将预测结果可视化。
挑战
使用数据集中的所有交易特征(如价格、交易量、开盘价、盘高、盘低值)来预测加密货币的价格。
数据
数据集可以从 CryptoCompare 网站下载,该网站可以在这里找到。
数据集总共包含 5 个要素。它们的详细信息如下:
- 收盘价——这是当天货币的市场收盘价。
- 高价——这是当天货币的最高价格。
- 低价——这是当天货币的最低价格。
- 开盘价—这是当天货币的市场开盘价。
- 成交量——当天交易的货币量。
代码在哪里?
事不宜迟,让我们从代码开始吧。github 上的完整项目可以在这里找到。
我从加载所有需要的库和依赖项开始。
import json
import requests
from keras.models import Sequential
from keras.layers import Activation, Dense, Dropout, LSTM
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.metrics import mean_absolute_error
%matplotlib inline
我使用了加拿大的汇率,并把实时数据存入了熊猫的数据框。我使用了to_datetime() 方法将字符串日期时间转换成 Python 日期时间对象。这是必要的,因为文件中的日期时间对象被读取为字符串对象。对字符串而不是日期时间对象执行像时差这样的操作要容易得多。
endpoint = 'https://min-api.cryptocompare.com/data/histoday'
res = requests.get(endpoint + '?fsym=BTC&tsym=CAD&limit=500')
hist = pd.DataFrame(json.loads(res.content)['Data'])
hist = hist.set_index('time')
hist.index = pd.to_datetime(hist.index, unit='s')
target_col = 'close'
让我们看看数据集是怎样的,包括所有的交易特征,如价格、交易量、开盘价、最高价、最低价。
hist.head(5)

接下来,我将数据分成两组—分别包含 80%和 20%数据的训练集和测试集。这里所做的决定只是为了本教程的目的。在真实的项目中,你应该总是把你的数据分成训练、验证、测试(比如 60%、20%、20%)。
def train_test_split(df, test_size=0.2):
split_row = len(df) - int(test_size * len(df))
train_data = df.iloc[:split_row]
test_data = df.iloc[split_row:]
return train_data, test_datatrain, test = train_test_split(hist, test_size=0.2)
现在,让我们使用下面的代码来绘制以加元为单位的加密货币价格与时间的函数关系图:
def line_plot(line1, line2, label1=None, label2=None, title='', lw=2):
fig, ax = plt.subplots(1, figsize=(13, 7))
ax.plot(line1, label=label1, linewidth=lw)
ax.plot(line2, label=label2, linewidth=lw)
ax.set_ylabel('price [CAD]', fontsize=14)
ax.set_title(title, fontsize=16)
ax.legend(loc='best', fontsize=16)line_plot(train[target_col], test[target_col], 'training', 'test', title='')

我们可以观察到,在 2018 年 12 月至 2019 年 4 月之间,价格出现了明显的下降。从 2019 年 4 月到 2019 年 8 月,价格持续上涨,7 月和 8 月出现波动。从 2019 年 9 月开始,价格不断下降。从这一价格波动中值得注意的有趣的事情是,价格在冬天很低,而在夏天上涨。虽然这不能一概而论,因为所考虑的数据集只是一年的小样本。同样,对于加密货币,很难一概而论。
接下来,我创建了几个函数来规范化这些值。标准化是一种经常作为机器学*的数据准备的一部分而应用的技术。规范化的目标是将数据集中数值列的值更改为一个通用的比例,而不会扭曲值范围的差异。
def normalise_zero_base(df):
return df / df.iloc[0] - 1
def normalise_min_max(df):
return (df - df.min()) / (data.max() - df.min())
接下来,我创建了一个函数来提取大小为 5 的窗口的数据,如下面的代码所示:
def extract_window_data(df, window_len=5, zero_base=True):
window_data = []
for idx in range(len(df) - window_len):
tmp = df[idx: (idx + window_len)].copy()
if zero_base:
tmp = normalise_zero_base(tmp)
window_data.append(tmp.values)
return np.array(window_data)
我继续创建一个函数,以某种格式准备数据,以便以后输入神经网络。我使用了同样的概念,将数据分成两组—分别包含 80%和 20%数据的训练集和测试集,如下面的代码所示:
def prepare_data(df, target_col, window_len=10, zero_base=True, test_size=0.2):
train_data, test_data = train_test_split(df, test_size=test_size)
X_train = extract_window_data(train_data, window_len, zero_base)
X_test = extract_window_data(test_data, window_len, zero_base)
y_train = train_data[target_col][window_len:].values
y_test = test_data[target_col][window_len:].values
if zero_base:
y_train = y_train / train_data[target_col][:-window_len].values - 1
y_test = y_test / test_data[target_col][:-window_len].values - 1
return train_data, test_data, X_train, X_test, y_train, y_test
LSTM
它通过使用特殊的门来允许每个 LSTM 层从先前层和当前层获取信息。数据通过多个门(如遗忘门、输入门等。)和各种激活功能(如 tanh 功能、relu 功能)并通过 LSTM 细胞。这样做的主要优点是,它允许每个 LSTM 细胞在一定时间内记住模式。需要注意的是,LSTM 可以记住重要的信息,同时忘记不相关的信息。LSTM 架构如下所示:

LSTM architecture
现在让我们建立模型。顺序模型用于堆叠所有层(输入、隐藏和输出)。神经网络包括一个 LSTM 层,后面是 20%的下降层和一个具有线性激活函数的密集层。我用 Adam 作为优化器,用均方差作为损失函数来编译这个模型。
def build_lstm_model(input_data, output_size, neurons=100, activ_func='linear', dropout=0.2, loss='mse', optimizer='adam'):
model = Sequential()
model.add(LSTM(neurons, input_shape=(input_data.shape[1], input_data.shape[2])))
model.add(Dropout(dropout))
model.add(Dense(units=output_size))
model.add(Activation(activ_func))
model.compile(loss=loss, optimizer=optimizer)
return model
接下来,我设置了一些稍后要使用的参数。这些参数是——随机数种子、窗口长度、测试集大小、LSTM 层中的神经元数量、时期、批量大小、丢失、漏失和优化器。
np.random.seed(42)
window_len = 5
test_size = 0.2
zero_base = True
lstm_neurons = 100
epochs = 20
batch_size = 32
loss = 'mse'
dropout = 0.2
optimizer = 'adam'
现在让我们使用输入x_train和标签y_train来训练模型。
train, test, X_train, X_test, y_train, y_test = prepare_data(
hist, target_col, window_len=window_len, zero_base=zero_base, test_size=test_size)model = build_lstm_model(
X_train, output_size=1, neurons=lstm_neurons, dropout=dropout, loss=loss,
optimizer=optimizer)
history = model.fit(
X_train, y_train, epochs=epochs, batch_size=batch_size, verbose=1, shuffle=True)
让我们看看 20 个时期的模型训练期间的快照。

Training of the neural network
我使用平均绝对误差(MAE)作为评估标准。选择 MAE 而不是均方根误差(RMSE)的原因是 MAE 更容易解释。RMSE 没有单独描述平均误差,因此更难理解。因为我们希望这个模型即使对非技术观众也能容易地解释,所以 MAE 看起来是一个更好的选择。
绝对平均误差
它测量一组预测中误差的平均大小,而不考虑它们的方向。它是实际观测值和预测观测值之间的绝对差异在测试样本上的平均值,其中所有个体差异都具有相同的权重。

targets = test[target_col][window_len:]
preds = model.predict(X_test).squeeze()
mean_absolute_error(preds, y_test)
**# 0.027955859325876943**
获得的 MAE 值看起来不错。最后,让我们使用下面的代码绘制实际价格和预测价格:
preds = test[target_col].values[:-window_len] * (preds + 1)
preds = pd.Series(index=targets.index, data=preds)
line_plot(targets, preds, 'actual', 'prediction', lw=3)

结论
在本文中,我演示了如何使用 LSTM 神经网络实时预测加密货币的价格。我经历了四个步骤:获取实时货币数据、为训练和测试准备数据、使用 LSTM 神经网络预测价格以及可视化预测结果。随意使用超参数或尝试不同的神经网络架构以获得更好的结果。
参考资料/进一步阅读
[## 用于比特币价格预测的机器学*模型比较- IEEE 会议出版物
*年来,比特币是加密货币市场中最有价值的。然而,比特币的价格已经非常…
ieeexplore.ieee.org](https://ieeexplore.ieee.org/document/8534911) [## 利用深度学*算法进行比特币价格预测
免责声明:本文中包括算法在内的所有信息都是为教育目的而提供和发布的…
medium.com](https://medium.com/activewizards-machine-learning-company/bitcoin-price-forecasting-with-deep-learning-algorithms-eb578a2387a3) [## 使用机器学*预测比特币的价格- IEEE 会议出版物
本文的目标是确定以美元为单位的比特币价格的走向可以预测到什么样的准确度。的…
ieeexplore.ieee.org](https://ieeexplore.ieee.org/abstract/document/8374483)
在你走之前
相应的源代码可以在这里找到:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/abhinavsagar/Cryptocurrency-Price-Prediction)
联系人
如果你想了解我最新的文章和项目,请关注我的媒体。以下是我的一些联系人详细信息:
快乐阅读,快乐学*,快乐编码!
黑客使用 LSTMs | TensorFlow 进行加密货币价格预测(第三部分)
在 TensorFlow 2 中使用 LSTM 深度神经网络预测比特币价格

Photo by David McBee
TL;DR 在 TensorFlow 2 中为时间序列预测建立并训练双向 LSTM 深度神经网络。利用模型预测未来比特币价格。
这次,您将构建一个基本的深度神经网络模型,根据历史数据预测比特币价格。你可以随心所欲地使用这个模型,但你要为自己的行为承担风险。
你可能会问自己这样的问题:
用加密货币还能发财吗?
当然,答案相当微妙。在这里,我们将看看你可能如何建立一个模型,以帮助你沿着疯狂的旅程。
或者你可能有经济困难?这里有一个可能的解决方案:
计划是这样的:
- 加密货币数据概述
- 时间序列
- 数据预处理
- 在 TensorFlow 2 中建立并训练 LSTM 模型
- 利用模型预测未来比特币价格
数据概述
我们的数据集来自雅虎!金融并涵盖所有可用的(在撰写本文时)比特币-美元价格数据。让我们把它载入熊猫数据框架:
注意,为了以防万一,我们按日期对数据进行排序。以下是我们感兴趣的数据示例:

我们总共有 3201 个数据点,代表 3201 天(~9 年)的比特币-美元价格。我们对预测未来日期的收盘价感兴趣。

当然,比特币让一些人变得非常富有,也让一些人变得非常贫穷。问题是,它会再次发生吗?让我们来看看一个可能的模型是怎么想的。我们走吧。
我们的数据集与之前的例子有些不同。数据按时间排序,并以相等的时间间隔(1 天)记录。这样的数据序列称为 时间序列 。
时间序列
时态数据集在实践中很常见。你的能量消耗和支出(卡路里输入,卡路里输出),天气变化,股票市场,从用户那里收集的对你的产品/应用的分析,甚至你(可能在恋爱中)的心产生时间序列。
你可能会对你的时间序列的许多属性感兴趣,比如平稳性、季节性和自相关性都是最著名的。
自相关是由某个间隔(称为滞后)分开的数据点的相关性。
季节性指的是在某个时间间隔出现某种循环模式(不,不一定是每个春天)。
如果一个时间序列具有恒定的均值和方差,则称其为平稳性。此外,协方差与时间无关。
在观察时间序列数据时,您可能会问自己一个明显的问题:“当前时间步长的值会影响下一个时间步长吗?”又名时间序列预测。
有许多方法可以用于此目的。但我们将建立一个深度神经网络,为我们做一些预测,并用它来预测未来的比特币价格。
建模
到目前为止,我们建立的所有模型都不允许对序列数据进行操作。幸运的是,我们可以使用一类特殊的神经网络模型,称为递归神经网络(RNNs) 来实现这个目的。 RNNs 允许使用模型的输出作为同一模型的新输入。该过程可以无限重复。
RNNs 的一个严重限制是无法捕捉序列中的长期相关性(例如,今天的价格和两周前的价格之间存在相关性吗?).处理这种情况的一种方法是使用长短期记忆(LSTM)RNN的变体。
LSTM 的默认行为是长时间记忆信息。让我们看看如何在 Keras 中使用 LSTM。
数据预处理
首先,我们将把价格数据压缩到[0,1]范围内。回想一下,这将帮助我们的优化算法更快地收敛:

source: Andrew Ng
我们将使用 scikit learn 中的最小最大缩放器:
缩放器期望数据的形状为(x,y ),因此我们在应用之前使用 reshape 添加一个虚拟尺寸。
让我们也去掉 nan,因为我们的模型不能很好地处理它们:
我们用 isnan 作为屏蔽,过滤掉 nan 值。在移除 nan 后,我们再次重塑数据。
制作序列
LSTMs 期望数据是三维的。我们需要将数据分割成预设长度的序列。我们想要获得的形状是:
[batch_size, sequence_length, n_features]
我们还想保存一些数据用于测试。让我们建立一些序列:
构建序列的过程通过在位置 0 创建指定长度的序列来进行。然后我们向右移动一个位置(例如 1)并创建另一个序列。重复该过程,直到使用了所有可能的位置。
我们保存 5%的数据用于测试。数据集看起来像这样:
(2945, 99, 1)
(156, 99, 1)
我们的模型将使用代表 99 天比特币价格变化的 2945 个序列进行训练。我们将预测未来 156 天的价格(从我们的模型视点)。
建立 LSTM 模型
我们正在创建一个 3 层 LSTM 递归神经网络。我们使用 20%的退出率来对抗培训期间的过度配合:
你可能想知道双向和 CuDNNLSTM 是怎么回事?
双向 RNNs 允许您对序列数据进行正向和反向(反向)训练。实际上,这种方法在 LSTMs 中运行良好。
CuDNNLSTM 是一个“由 cuDNN 支持的快速 LSTM 实现”。我个人认为这是一个很好的漏抽象的例子,但是它太快了!
我们的输出层有一个单神经元(预测比特币价格)。我们使用线性激活函数,其激活与输入成比例。
培养
注意,我们不想打乱训练数据,因为我们使用的是时间序列。
经过闪电般的训练(感谢谷歌免费的 T4 图形处理器),我们有以下训练损失:

预测比特币价格
让我们的模型预测比特币价格吧!
我们可以使用我们的缩放器来反转我们所做的转换,这样价格就不再在[0,1]范围内缩放。

我们相当简洁的模型似乎在测试数据上做得很好。想试试其他货币吗?
结论
恭喜你,你刚刚在 TensorFlow 2 中构建了一个双向 LSTM 递归神经网络。我们的模型(和预处理“管道”)非常通用,可以用于其他数据集。
未来研究的一个有趣方向可能是分析不同加密货币之间的相关性,以及这将如何影响我们模型的性能。
【https://www.curiousily.com】最初发表于。
受威胁者的密码学速成班
阅读/观看的概念概述和建议

Rotor Cipher Machine via Pixabay
在我的上一篇帖子中,我谈到了处理数据时保持专注的重要性。谁有权限?你如何保护这些信息?一些法律是如何改变的?学术界和工业界涵盖了什么和没有涵盖什么。
在这篇文章中,我决定给自己上一堂密码学速成课。密码学的概念可能听起来很吓人,数据的安全性和数据的安全传输肯定是需要认真对待的事情。但是我们每天都在创造数据。我认为现在对任何人来说,对什么是密码学有一个基本的了解是很重要的。
目标
- 尝试在一定程度上解除密码学的神秘性。
- 解释密码学的一些基本概念。
- 解释几个密码学在我们日常生活中应用的例子。
- 共享资源!
基本概念
为了开始理解密码学,我从基础开始。当你在网上查找密码学时,这里有一些反复出现的基本概念。我在下面链接了一个 YouTube 视频,如果你更喜欢视觉的话。
一、什么是密码学?密码学是一个专注于通信和数据存储的研究领域,旨在防止不必要的第三方窃取数据。例如,在手机出现之前,两个孩子可能会在课堂上互相传递一张纸条。他们不希望任何其他人(第三方),如老师,阅读笔记。因此,他们可能想出一种方法,用虚构的语言来写,或者对信息进行编码(加密信息),这样,即使老师可以“阅读”信息,他们也无法理解。
对信息进行加密(加扰)和解密(解扰)有不同的方式。它们通常分为两类:
- 对称:加密和解密的密钥是一样的,都使用私钥
- 不对称:加密和解密密钥不同,使用一个私钥(隐喻密钥),和一个公钥(隐喻锁),每个公钥只有一个私钥
为了进行实际的加密和解密,发送方和接收方需要密钥。密钥本质上是字符序列,允许用户随机打乱消息、文件、文件夹、数据、硬盘驱动器等。你试图保护和/或传递的任何信息。需要一个匹配的密钥来解密信息,无论该密钥是相同的(对称加密/解密)还是不同的(非对称加密/解密)。
你试图加密的信息可以叫做明文。一旦用密钥和加密程序对其进行加密,它就可以被称为密文,这就是存储和/或传输的内容。只有当数据处于静止状态(存储)或传输中的时,它才能被修改或窃取。当密文被一个密钥和解密程序解密时,你希望它是你发送的同一个明文。
我要介绍的最后一个概念是加密散列函数。有些人可能熟悉哈希函数的概念。我们可以把散列函数想成任何一种函数(想想 machine,你放入 x,你得到 y)。哈希函数专门输出预先指定长度的值。例如,一个流行的加密散列函数是 SHA 256——SHA 代表安全散列算法——它给定一个输入,输出一个 256 位散列值。常规哈希函数和加密哈希函数之间的区别在于,当两个输入产生相同的输出时,加密哈希函数会尝试最大限度地减少冲突。我们可以看到,如果你有加密数据,冲突会是一个巨大的问题。
现实生活中的例子,你可能甚至没有注意到
既然我们已经讨论了一些基本的加密概念,我想看看我们每天都会遇到的几个加密/解密的例子。
- ****HTTPS(超文本传输协议安全)是您的本地设备与网络浏览器或远程站点安全通信的方式。当你访问 HTTPS 安全服务器(一个以 https 而不是 http 开头的安全网站)时,网络浏览器会检查一个证书,让浏览器知道该网站是合法的,而不是试图伪装成另一个网站的危险网站。HTTPS 安全服务器防止信息在连接过程中泄露。该连接使用 TLS(传输层安全)加密,不要与其过时的前身 SSL(安全套接字层)混淆。
- 我们非常*惯于注册、登录和使用密码。人们不擅长想出和记住密码的想法已经存在一段时间了。事实证明,加密是密码系统工作的重要组成部分。一般来说,大多数系统会加密你输入的密码,只保留密文。下次你登录时,它会对你输入的内容进行加密,然后看看是否与文件上的密文相匹配。如果没有,就不能登录。这也是为什么大部分账号和网站只能重置密码,而不能给你发送旧密码的原因。
进一步的考虑
- 如何安全地存储密码或密钥等敏感信息?
- 您使用的是安全连接吗?
- 如何将密钥传输给需要它们的各方?
- 你怎么知道发送数据的人就是他们所说的那个人?如何验证自己的身份? GPG 密钥签名,签名链****
奖励:从凯撒密码到一次性密码本
在阅读这篇博文的过程中,我偶然发现了可汗学院的一系列有趣的课程,题为“密码学之旅”,可以在这里找到。
这一部分是为有历史头脑的人准备的。课程从凯撒密码开始,这可能是我们小时候读到的东西,而没有理解密码学的概念,甚至可能是我们自己想出来的。
凯撒密码
这种加密方法起源于几千年前。你只需将你信息中的字母移动一定数量的字母,并告诉对方你想解码信息,移动的数量。例如,假设移位是 7,消息是英文的(不清楚这种密码如何适用于基于字符的语言)。
A -> H
B -> I
C -> J
D -> K
E -> L
.
.
.
V -> C
W -> D
X -> E
Y -> F
Z -> G
所以如果你的信息是“密码学很酷”这就变成了“JYFWAVNYHWOF PZ JVVS”这种密码的弱点是,我们可以看到一些字母重复,我们知道所有这些字母都编码为同一个原始字母。事实证明,使用频率分析可以很容易地破解凯撒密码。例如,在英语中,某些字母出现得更频繁,如 s、a、I、e 等。根据频繁加密的信件,我们可以反推,以确定这种转变可能是什么。
多字母移位密码
对凯撒密码的一个改进是给你的朋友不是 1 次移位,而是 n 次移位,然后重复进行。例如,如果你给你的朋友 5,8,13,0,1,1,2,3(这不是一个随机的顺序)。然后第一个字母移动 5 个字母,第二个字母移动 8 个字母,下一个移动 13 个字母,下一个移动 0 个字母,…第八个字母移动 3 个字母,然后重复这个序列。所以第九个字母被移动了 5 个字母,等等。移位序列越长,破解密码就越难。但是这个密码仍然不能完善保密。
一次性密码本
然而,一次性密码本提供了完美的保密性,因为每个字母都被分配了一个随机移位。然而,这是有代价的——大量的数据——你需要转换每封邮件/文件/你所传达的任何信息中的每一个字母。产生随机移位基本上就是德国人在二战中设计的恩尼格玛机在做的事情。然而,有一些人为错误允许模式被确定,恩尼格玛机被破解。
参考资料和资源
2018 年 9 月 16 日教程 python 加密网络安全在这篇文章中,我演示了加密模块的用法…
nitratine.net](https://nitratine.net/blog/post/asymmetric-encryption-and-decryption-in-python/) [## Python 中的加密和解密
2018 年 9 月 16 日 YouTube python encryption cyber-security 在这篇文章中,我将讨论如何在…
nitratine.net](https://nitratine.net/blog/post/encryption-and-decryption-in-python/) [## 加密您的数据
使用 Python 进行对称加密
towardsdatascience.com](/encrypting-your-data-9eac85364cb) [## PyNaCl
PyNaCl 是绑定到 lib 钠的 Python,lib 钠是网络和加密库的一个分支。这些库…
pypi.org](https://pypi.org/project/PyNaCl/) [## 欢迎使用 pyca/Cryptography-Cryptography 2.9 . de v1 文档
密码术包括高级配方和普通密码算法的低级接口,例如…
密码学. io](https://cryptography.io/en/latest/) [## 安全外壳
安全外壳(SSH)是一种加密网络协议,用于在不安全的…
en.wikipedia.org](https://en.wikipedia.org/wiki/Secure_Shell) [## 传输层安全性
传输层安全性(TLS)及其现已废弃的前身安全套接字层(SSL)是加密的…
en.wikipedia.org](https://en.wikipedia.org/wiki/Transport_Layer_Security) [## 密码学基础
R 中的应用程序
towardsdatascience.com](/the-basics-of-cryptography-80c7906ba2f7) [## 密码 101
提供了理解 SSL/TLS 等完整系统所需的一切:分组密码、流密码、哈希…
www.crypto101.io](https://www.crypto101.io/)**
我是一名文化数据科学家
我可能刚刚创造了一个新的工作头衔…?

我用这个头衔已经有几个月了。在我的网站上。在我的作品集里。在的会谈和介绍。甚至当我认识新朋友的时候。
问题是,“文化数据科学家”这个术语并不存在。我刚编的。
我是一名数据科学家,使用统计学、计量经济学、机器学*、城市分析、社交媒体分析和数据可视化来了解和衡量文化在城市中的经济和社会影响。
但这显然有点拗口,尤其是当我在酒吧和一个不知道这些行话术语定义的人聊天时。
所以,我想我应该写一篇关于它的博客。
什么是文化?
文化是其中一个很重要的术语。这个定义被分为“艺术”和“社会行为”两部分,但它是一个泛指我们的术语。
《牛津词典》对文化的定义是:
- 艺术和其他人类智力成就的表现形式的总称。
- 特定民族或社会的观念、*俗和社会行为。
维基百科将文化描述为“人类社会中的社会行为和规范”
英国政府数字、文化、媒体和体育部(DCMS)有一个完整的部门关注“文化和创意”产业,其中包括艺术;电影、电视和音乐;收音机;摄影;工艺品;博物馆和美术馆;图书馆和档案馆;文化教育;历史建筑和类似旅游景点的经营;广告和营销;建筑;工艺品;设计和设计师时尚。
令人恼火的是,商界最*似乎也在劫持这个词。
什么是数据科学?
数据科学是一门使用和分析数据来测试假设、回答问题和理解见解的学科。
数据科学是“统一统计学、数据分析、机器学*及其相关方法的概念”,以便用数据“理解和分析实际现象”。
文化数据科学家
因此,文化数据科学家这个头衔很好地概括了我的工作。
我是一名数据科学家,使用统计学、计量经济学、机器学*、城市分析、社交媒体分析和数据可视化来了解和衡量文化在城市中的经济和社会影响。
我对文化的研究始于艺术,但现在已经相当广泛,跨越了艺术到音乐、时尚和食物。
职称比“我是文化部门的数据科学家”好多了。
可比资产
就像你如何成为一名经济顾问、财务顾问、软件工程师、机器学*工程师、土木工程师或 T21 工程师一样
我是一名文化数据科学家。
也许我太迂腐了?
但事实是,我每天打交道的人——艺术和文化部门的人——甚至不知道什么是数据科学家!
将“文化”放在“数据科学家”的前面,这种区分不仅独特有趣,而且有助于将我的工作置于背景之中。
研究
为了证明职位名称文化数据科学家并不存在,我将链接几个流行且常用的搜索引擎。
根据 Google Trends — 此处 —该术语不存在(2019 年 2 月 11 日检索)。
LinkedIn — 这里 —没有其他人有这个职位(2019 年 2 月 11 日检索)。
天使名单上也没有那个职位的工作——此处此处(2019 年 2 月 11 日检索)。
甚至一个标准的谷歌搜索也不会产生这个词。
文化分析和文化数据科学
不可否认,有人在使用“文化分析”这个术语,也有一些大学在使用“文化数据科学”。
列夫·马诺维奇在 2007 年发明的术语文化分析,是“对视觉材料的海量文化数据集的探索和分析”。
另一方面,加拿大蒙特利尔的麦吉尔大学有一个社会和文化数据科学的 T4 中心。
伦敦帝国理工学院有一个社会和文化分析实验室,但是他们的推特账户自 2015 年 10 月 30 日以来就没有发过一次推特!严重努力差!
我目前就读的位于 UCL 的巴特利特学院将推出一个名为文化遗产数据科学的新硕士学位。
但有趣的是,没有人使用文化数据科学家的头衔。
我是不是创造了一个新的头衔…?
截至 2019 年 3 月,我未能找到任何积极使用职位名称“文化数据科学家”的人。
该术语是必需的。随着我们进入 2020 年,文化变得更加数字化,现在存在更大的数据集,并且有更复杂的方法来分析这些数据。
如果大学正在创建这些研究实验室,那么人们应该对使用文化数据科学家这个头衔来描述他们正在做的工作感到舒服。
在我看来,社会科学家这个词是不行的,数据科学家也太宽泛了。计算社会科学家是个东西,但你指的是哪门社会科学……地理学、经济学、人类学、社会学、心理学、政治学……全部?
无论如何,看起来我刚刚创造了一个新的工作头衔,而且我会坚持一段时间!如果你的工作符合要求,我鼓励你也使用它!
感谢阅读!
Vishal
本文首发于 2019 年 3 月 10 日vishalkumar . London。
Vishal 是一名文化数据科学家,也是伦敦 UCL 的一名研究生。他对城市文化的经济和社会影响感兴趣。你可以在Twitter或者LinkedIn上与他取得联系。在insta gram或他的 网站 上看到更多 Vishal 的作品。
当前使用 Python3.7 的 Google 搜索包:简单教程。
简介
本文总结了当前使用 Python 3.7 实现 Google 搜索查询的常用库和包。写这篇文章的动机来自于我收到或看到的关于这个主题的异常多的请求。这背后的原因是 Google 的 SOAP 和 AJAX API 的停止使用——这两者都是为此目的而设计的。围绕数据科学和 python 的许多领域都有很好的文档记录,但是我发现在 Python 中使用 Google 搜索并没有很好的文档记录,这也是我写这篇文章的动机。

谷歌搜索——现代的必需品
使用谷歌搜索已经成为我们时代的一种主要方式,可以在几毫秒内从互联网的各个角落释放信息。在过去的 20 年中,我们一直依靠这种常见的方法来查询从新闻文章到研究主题的各种信息。随着过去几年数据科学的兴起,搜索互联网的两个最常见的原因是(1)解决堆栈溢出问题,或(2)数据挖掘——第二个原因将是本文的重点。
无论是为了科学目的还是商业智能项目,Python 和数据挖掘都被证明是一对非常有效的工具。数据挖掘被定义为检查大量数据以提取新信息或见解的方法。就本文的目的而言,因为它与 web 有关,所以从 internet 挖掘数据的方法称为 web 挖掘,它可以分为三类:(1) Web 内容挖掘,(2) Web 使用挖掘,以及(3) Web 结构挖掘。

谷歌搜索——事物的大计划
在我最*看到的许多与 web 挖掘相关的项目中,检索数据的首选方法是使用 google search 来检索 URL,以便进行 web 抓取。大部分项目都遵循这种通用格式,在这种格式中,URL 被获取,每个 URL 被解析和抓取,最后所有的数据被组织起来。出于本文的目的,我们现在只关注 URL 检索过程(蓝色部分)。需要强调的是,根据您正在寻找的数据类型;使用 RSS 源可能更合适。这当然很大程度上取决于您考虑的数据源是否提供 RSS 提要。我们稍后将再次讨论这个问题。

The general flow of thought in this article begins with the retrieval of URLs, which are then parsed, and then set up for future analysis.
现在让我们检查一个具体的用例。我们希望识别和量化某个公司的新闻文章或感兴趣的话题。在这种情况下,我想找到所有与人们对苹果公司新款 iPhone 的评论相关的文章
我们开始吧——我们有 3 个选项可以通过 Python 进行搜索
在本文发表之日,有三个公共库允许通过 Python 进行谷歌搜索。我们将从最简单的开始,转到最复杂的,因为我想确保所有数据科学家和所有非技术读者能够跟上。也就是说,你会注意到我概述的代码并不完全是最“皮托尼卡”。在写这篇文章的时候,我试图在“简单”和“高效”之间保持一个健康的平衡,以确保所有的读者都从中受益。
选项 1——谷歌图书馆(免费,但有限制)
我们首先在终端命令行上使用 pip 安装谷歌库:
pip install google
在我们的 Jupyter 笔记本中,我们可以通过以下方式导入库:
from googlesearch import search
然后,我们概述我们想要查询的内容。在我们的案例中,我们希望监控一个特定的公司或话题。让我们以“苹果”公司和“苹果手机”为例,检索关于它们的新闻文章。
query = "apple iphone news 2019"
下一步是创建一个空列表,并使用“for 循环”迭代代码:
my_results_list = []for i in search(query, # The query you want to run
tld = 'com', # The top level domain
lang = 'en', # The language
num = 10, # Number of results per page
start = 0, # First result to retrieve
stop = None, # Last result to retrieve
pause = 2.0, # Lapse between HTTP requests
): my_results_list.append(i)
print(i)
就这样!结束了。
这个库运行良好,并可靠地返回结果(而且是免费的!).然而,有一个方面我们没有考虑。“暂停”参数每次都会将查询延迟 2 秒。如果您要查找更多的结果,这将大大增加您的搜索时间。删除这个参数通常会在几秒钟内导致 403 错误(也就是谷歌阻止你)。除了遵守第二条规则之外,没有任何好的方法来解决这个问题(至少对于这个特定的包来说)。
选项 2-谷歌搜索结果库(非免费,无限制)
这里的第二个选择是使用 SERP 的谷歌搜索功能。
from serpapi.google_search_results import GoogleSearchResultsclient = GoogleSearchResults(
{"q" : "apple iphone news 2019", # The query
"location" : "Boston,MA", # Location
"secret_api_key" : "secretKey"} # API Key from SERP
)
result = client.get_dict()
result
虽然这个库有相关的成本,但它值得研究,因为它允许您避开上面选项 1 中 google 库中 HTTP 请求的 2 秒暂停。总之,这是一个伟大的图书馆,但它不是免费的。
选项# 3——谷歌应用编程接口客户端(大部分免费,大部分无限制)
据我所知,谷歌 API 客户端,,是我们目前为止看到的唯一一个谷歌拥有的项目。当涉及到导航这个空间时,它们提供了广泛的能力。我把它们列在最后,因为对于一些人来说,建立一个完整的账户并准备好是一个困难的过程。但是,如果您正在开发一个严重依赖搜索查询的模型,我建议您优先选择这个选项。
在过去,我试图介绍注册的基本步骤,但是过程中的小步骤似乎经常改变。我建议你首先阅读最新的“入门”页面。一旦你建立了一个帐户,你将需要获得两个项目:(1)一个私人的 API 密钥,和(2)一个自定义搜索引擎 ID。
设置好帐户后,继续安装库:
*pip install google-api-python-client*
安装了库之后,您可以继续运行您的第一个查询:
*from googleapiclient.discovery import build #Import the libraryapi_key = "my-secret-api-key"
cse_id = "my-secret-custom-search-id "def google_query(query, api_key, cse_id, **kwargs):
query_service = build("customsearch",
"v1",
developerKey=api_key
)
query_results = query_service.cse().list(q=query, # Query
cx=cse_id, # CSE ID
**kwargs
).execute()
return query_results['items']my_results_list = []my_results = google_query("apple iphone news 2019",
api_key,
cse_id,
num = 10
) for result in my_results:
my_results_list.append(result['link'])
print(result['link'])*
我认为最后一个选项是检索 URL 列表的最佳方式(甚至是一些原始的 HTML 数据!)以一种可靠且免费的方式。
总结了这三种方法后,我们以一个 URL 列表结束,我们现在可以解析这个列表来检索感兴趣的信息。我想指出的一个方面是,我们运行了 google 搜索来检索关于感兴趣的公司或主题的信息。有人可能会说 RSS 提要更适合检索新闻——事实也的确如此。然而,从商业智能的角度来看,我们希望确保所有相关的结果都出现在我们的查询中,并且数据不会偏向一个新闻源而不是另一个。一种常见的方法是将查询结果和 RSS 提要结合在一起,不过,我们将在另一个时间讨论这个问题。
向前移动&下一步
既然已经设置好了 URL 列表,下一个目标就是解析 URL 并检索数据。抓取 web 是数据科学的一个记录良好的领域,*年来随着文章和教程的出现而蓬勃发展。也就是说,我不会在本文中涉及它。但是,我会提供一些后续步骤的指导。
回头看看前面提到的框架,下一步是从 web 上搜集数据,然后相应地组织内容,为要开发的模型做准备。有几个不同的库可以用于这个过程。Scrapy 是一个优秀的抓取框架,当涉及到网页抓取时,它被称为“一体化”包。另一方面,我们有 Selenium,这是一个为浏览器自动化设计的库,允许您在页面之间导航。最后,我们有 BeautifulSoup ,它是一个更加保守和用户友好的数据检索器。鉴于 BS4 更加直接,非常适合大多数数据科学家的需求,我建议继续使用它。

There are 3 main libraries commonly used for scraping websites. I find BeautifulSoup to be the easiest to learn and follow — therefore we will use this an example for this article and tutorial.
我希望这篇文章对 Python 3.7 中搜索查询的使用有所帮助。我鼓励任何有你希望我报道的话题或想法的人,给我发电子邮件到 salehesam@gmail.com。
使用自定义实体提取解析结构化文档

Let’s talk about parsing structured documents with entity extraction!
网上有很多很棒的教程,解释如何用机器学*对大量文本进行分类。但是,如果不仅仅是对文本进行分类,而是想要对单个单词进行分类,比如:

You can’t make me apologize for loving Comic Sans.
这被称为实体提取(或命名实体识别),它非常有用。例如,您可以使用这种技术来挑选新闻文章中提到的所有人和地点,并将它们用作文章标签(新闻编辑室有时会这样做)。
实体提取(EE)对于解析结构化文档也很有用,比如表单、w4、收据、名片和餐馆菜单(这就是我们今天要用的)。
对于今年的 Google I/O,我想建立一个应用程序,它可以拍摄餐厅菜单的照片并自动解析它——提取所有的食物、它们的价格等等。(你可以在这里看到我在舞台上演示。)
我希望能够上传一张这样的菜单图片:

Step one: upload a photo of a menu
然后使用机器学*从菜单中神奇地提取实体,如餐厅的地址、电话号码、所有的食物标题(“沙拉”、“主菜”)、所有的食物、它们的价格和它们的描述(即“在椒盐卷饼上”)。
这个想法是,如果你是一家想要在像 Seamless 或 Grubhub 这样的应用程序上上市的餐馆,你可以输入你的菜单,而不必手动输入整个菜单。

Step two: identify all the foods, plus the food headings (“Salads,” “Mains”) and their descriptions (“On a pretzel bun”).
那么它是如何工作的呢?
首先,我使用 Google Cloud Vision API 的文本检测功能将我的菜单图片转换成原始文本。
一旦我有了文本格式的菜单,我就使用实体提取来解析它。我用两种技术做到了这一点:
- 我使用 Google Cloud 自然语言 API 提取了餐馆的地址、电话号码和所有列出的价格。
- 为了识别食物项目、标题和食物描述,我必须建立一个定制的机器学*模型(稍后会详细介绍)。
自然语言 API 能够自动检测常见的实体。例如,如果我向它发送字符串:
戴尔想从加利福尼亚州山景城 ampi theatre Pkwy 1600 号的咖啡店购买一个 3.50 美元的羊角面包,该咖啡店的电话号码是(650)253–0000。

The NL API recognizes entities like people, consumer goods, addresses, price, phone numbers, and more.
接下来,我添加了一张餐馆照片、星级评定和一张地图,让我的应用程序变得更加时尚。这些信息都没有直接印在菜单上,但餐馆的电话号码是。使用这个电话号码,我可以查询 Google Places API 来获取照片、星级等。

The restaurant’s star rating, photo, and GPS location come from the Places API.
构建自定义实体提取模型
好的,但是现在是困难的(也是最关键的)部分:我们如何提取菜单上的所有食物?我们如何区分什么是食物(“素食汉堡”),什么是食物标题(“主菜”),什么是食物描述(“在椒盐卷饼上”)?
这些实体完全是特定领域的。在我们的例子中,我们希望认识到“主菜”是一个食物标题。但是如果我们转而解析报纸,我们可能想要识别标题和文章正文之间的区别。或者,如果我们在分析简历,我们会想确定“高级软件工程师”是一个职位,而“Python”是一种编程语言。
虽然我之前提到的自然语言 API 可以识别电话号码和地址等等,但是它没有经过训练来识别这些特定于领域的实体。
为此,我们必须构建一个定制的实体提取模型。有很多方法可以做到这一点,但是我将向你展示一个我认为最简单的方法(代码最少),使用 Google AutoML 自然语言。我们将使用它来训练自定义实体提取模型。
它是这样工作的:
- 上传带标签的菜单数据集
- 训练模特
- 使用您的模型通过 REST API 识别定制实体
标签数据
但是在哪里可以找到一个标签菜单的数据集呢?
方便的是,我找到了这个由托管的菜单扫描的数据集(并贴上了标签!)由纽约公共图书馆提供。太棒了。
首先,我再次使用 Vision API 将数据集中的所有菜单转换为文本。
为了训练一个定制的实体提取模型,我必须将我的数据及其标签转换成 jsonl 格式(AutoML 期望的格式)。看起来是这样的:

jsonl file format
我要诚实地警告你,实际上给这些菜单贴标签是一件痛苦的事情。我使用一些粗糙的 Python 脚本将数据集的标签与 OCR 提取的菜单文本配对,但它们经常不对齐。我不得不进入 AutoML 用户界面,手工标记这些菜单,就像这样:

Hand-labeling menus using the AutoML editor.
回想起来,我可能应该使用一个数据标签服务,这样我就不必浪费时间给自己(或者实*生,也许?).
我还抓取了维基百科的美食页面,生成了一些假菜单来帮助扩充我的数据集。
训练模型
使用 Google AutoML,构建模型最困难的部分是构建带标签的数据集。实际上训练一个有这些数据的模型非常简单——只要跳到“训练”标签,然后点击“开始训练”建立一个定制模型大约需要 4 个小时。

Behind the scenes, Google trains a neural network to extract your custom entities.
做预测
当我们的实体提取模型完成训练后,我们可以在 AutoML UI 中测试它:

The model tagged most of the entities, but missed “Potato Chips” and “Fish and Chips.”
如你所见,它并不完美。
但是你想要什么?!那就是机器学*。不是完美。
也就是说,考虑到我的数据集很小(不到 150 个菜单),这还不错。我也没有考虑到文本的大小或它在页面上的位置(标题通常比菜名大,菜名比它们的描述大)。
但是,如果我是一家餐馆,我可以使用这个工具来导入我的菜单,而不是手动输入每一种食物,我会节省很多时间(尽管我可能需要做一些编辑)。此外,越多假设的餐厅使用我的应用程序,我就能获得越多假设的数据点来改进我的模型。
与此同时,谷歌咖啡馆的午餐来电。再见。
P.S .如果训练自己的实体提取模型,在评论里说说或者 @dalequark 。
快乐造型!
如何使用 R 在 Power BI 中生成定制的视觉效果
在 Power BI 中轻松创建交互式 R powered HTML 视觉效果

您是否曾想通过 Power BI 显示自己定制的视觉效果?如果答案是肯定的,那么这篇文章将帮助您快速了解如何导入在 R 中创建的自定义视觉效果,并在您的常规 Power BI 报告中使用它们。
先决条件:
安装 R and R 工作室:假设已经安装了 Power BI 桌面应用,首先需要安装 R 语言和集成开发环境(IDE)来运行 R 脚本。写这篇文章的时候,我正在使用 R-3.5.1 和 R Studio 。
在 Power BI 中启用 R 视觉功能:从 Power BI 桌面,转到文件>选项和设置>选项,然后在 R 脚本选项卡中选择您的 R 主目录和 IDE。

Enabling R home directory and IDE in Power BI
安装 node.js: 访问这个网站并下载最新的特性安装程序。按照向导中的安装步骤完成该过程。完成后,重新启动计算机开始。
安装包:打开 node.js 命令提示符,执行以下命令:
*npm install -g powerbi-visuals-tools*
要检查安装是否成功,请运行以下命令:
*pbiviz*
您应该会看到如下所示的帮助屏幕。

Successful installation of Power BI visual tools
数据:我使用了来自 UCI 机器学*知识库的老爷车评估数据集。你可以从这里下载,保存成 CSV 格式。根据该数据集,客户对汽车的接受程度取决于 6 个主要标准——购买价格、维护成本、车门数量、载客量、行李箱尺寸和汽车的预计安全性。这是数据集的一瞥:

Car evaluation data set
在本文中,我将创建一个 HTML 100%堆积柱形图来显示汽车的可接受性如何随购买价格而变化。
使用 R 创建 HTML 视觉效果:
连接到数据:首先,使用 Get Data 从 Power BI 桌面应用程序连接到您的数据。数据导入后,在可视化窗格中单击 R script 可视化图标。将要使用的列拖放到值区域。这将自动生成您将要处理的数据集。单击 R script 编辑器角落的小箭头,重定向到 R Studio,这样您就可以尝试和测试您的代码来生成定制的视觉效果。

Step by step instruction on how to create R script visuals
你可以跳过以上所有步骤,直接在 R Studio 中编写 read.csv 代码,连接你保存在本地机器中的数据。
创建常规的非 HTML 视觉效果:一旦 R Studio 打开,您可以看到读取数据集的代码已经存在,您可以立即开始您的分析。写下你的 R 代码,运行到 R studio 中,生成你想要的视觉效果。我在 R 中使用了流行的 ggplot2 库来可视化汽车的购买价格与其可接受性之间的关系。
*library(ggplot2)
g <- ggplot(data=dataset, aes(x=dataset$buying,
fill=dataset$car_acceptibility)) +
geom_bar(stat=”count”) +
scale_x_discrete(name =”Buying Price”) +
scale_fill_discrete(name=”Acceptability”)
g*

Buying price vs. Car acceptability
如果您在 Power BI 的 R 脚本编辑器中直接使用上面这段代码,它会生成一个图表,表明购买价格非常高的汽车很可能不会被大多数客户接受。您可以创建自己的过滤器来分割这个交互式图表。

Filtered chart to display only accepted cars
例如,如果您只想查看各种购买价格类别中接受的汽车数量,您可以创建一个汽车可接受性切片器并过滤数据。请注意,这不是一个 HTML 输出。
创建 rhtml 模板:常规 R-powered 定制可视化将输出绘图显示为静态图像。要生成 HTML 视觉效果,您需要创建 rhtml 模板。在 node.js 命令提示符下,执行以下命令:
*pbiviz new demoCarEvaluation -t rhtml*

rhtml template creation
这个模板包括一个非常基本的可以运行的 R Visual,它可以创建 HTML 输出。
使用 rhtml 模板:导航到您的本地机器,找到创建democare evaluation文件夹的位置。在我的例子中,很明显这个文件夹在 c 盘> Users > username 中。找到新创建的文件夹后,打开它并在 R Studio 中编辑 script.r 文件。将您写下的代码粘贴到 script.r 的“实际代码”部分,以生成静态 R 可视化。
在下图中,你可以看到我已经粘贴了之前用来生成上面的堆积柱形图的全部代码。

运行 rhtml 模板:转到 node.js 命令提示符,使用更改目录命令导航到存储 script.r 的文件夹。若要打包 visual,请执行以下命令:

Changing the directory
*pbiviz package*

Creating the package
打包完成后,返回 Power BI 桌面应用程序,点击可视化窗格中的 从文件 导入。浏览到内的 dist 文件夹,导入 pbiviz 文件在报告中使用。
在 Power BI 中创建自定义可视化:完成上述所有步骤后,您将在可视化窗格中获得一个新图标。点击democare evaluation图标,在 Values 字段下拖动您想要在视觉中使用的列,您将在报告中看到您的 HTML 图表。将鼠标指针悬停在图表上,您将看到图表每个部分的更多详细信息。HTML 视觉支持一系列活动,如放大/缩小、自动缩放等。
如果你想在相同的数据上创建其他的视觉效果,你只需要编辑 script.r 文件,再次运行pbi viz package命令,你将能够非常快速和容易地创建新的视觉效果。

HTML custom visual using R
探索和享受!
<<note: this="" is="" an="" old="" article="" originally="" published="" in="" march="" as="" power="" bi="" changes="" rapidly="" please="" check="" microsoft="" documentation="" site="" for="" the="" latest="" updates.="">></note:>
谷歌人工智能平台上的定制模型部署服务

Source: Google
我在 Wootric 部署定制机器学*模型、管道和预测例程的经验
R 最*,谷歌云人工智能平台服务(CAIP)增加了一个新功能,机器学*(ML)从业者现在可以使用他们喜欢的框架,通过定制的预处理管道和预测例程来部署模型,所有这些都在一个无服务器微服务下。在这篇博文中,我详细解释了我们 Wootric 如何利用这一功能,并提到了一些需要注意的细节,因为该产品仍处于测试阶段。
一点背景知识

https://engineering.wootric.com/all
在我们将焦点完全转移到模型部署之前,我将花一点时间向您介绍一下我们的模型试图解决的问题的背景。在 Wootric,我们使用 NLP 和深度学*来学*和标记不同行业的客户和员工反馈中的主题。一条反馈可以与一个或多个标签相关联,如上图所示。要了解我们产品的更多信息,请访问此链接。这可以看作是一个模型的多标签分类问题,或者是 n 模型的两类多类分类问题。出于这篇博客的目的,我们将把它视为一个多类分类问题,并为 n 个标签部署 n 个模型。
模型概述
当我开始研究这个问题时,我用传统的 ML 分类器进行了实验,这些分类器根据每个标签的数据的不同单词包表示进行训练,作为基线。在这些模型中,总体来说最好的是随机森林模型。在本文中,为了便于解释,我们将部署这些模型,这样我们就可以花更多的时间在 CAIP 上进行定制部署,而不会迷失在模型的复杂性中。在后面的帖子中,我们将在更高的层次上讨论基于 CNN 的模型和基于序列的多标签模型的部署,两者都是用 pytorch 编写的。
人工智能平台服务概述

https://cloud.google.com/ — — The blue boxes highlight where CAIP comes into play
我们将在上图下半部分的 4 个方框中快速谈论 CAIP 的优势。要了解如何使用 CAIP 来训练和评估模型,请访问此链接。
在 CAIP 上,我们可以部署使用直接兼容的框架(sklearn、XGBoost、tensorflow)中的管道训练的模型,或者部署具有所需依赖关系的自定义预测器类和任何自定义的状态相关的预处理和后处理类作为源分发包。这里一个很大的积极因素是原始数据预处理、预测和预测后处理的耦合,所有这些都在一个模型微服务下;一切都发生在服务器端!这有几个值得一提的好处。首先,由于客户端没有进行预处理,这就将客户端从模型中分离出来。因此,我们可以在不更新客户端的情况下更新预处理代码,或者为更新提供新的端点。其次,不存在训练-服务偏斜,因为预处理步骤中的任何变化都会迫使我们重新训练模型。
此外,CAIP 的自定义预测例程允许我们将基于规则的预测与模型预测相集成,还可以从存储在存储桶中的模型集合中聚合预测。
由于这篇博客完全专注于定制部署,如果你对从 CAIP 支持的库中部署预定义的模型管道感兴趣,请参考这篇教程。
进入状态…
在我们继续之前,您应该:
- 熟悉 Google Cloud SDK ,
- 在 GCP 上创建一个项目,启用计费并启用 AI 平台(“云机器学*引擎”)和计算引擎 API。
或者,您可以在谷歌云平台(GCP)控制台上重复这些步骤,或者使用 REST API。
让我们进入代码
我有 11 个随机的森林模型,为 11 个不同的标签训练。这些标签是消费者对 SAAS-y 公司反馈的常见主题。以下是标签:
["Alerts_Notification", "Bugs", "Customer_Support", "Documentation", "Feature_Request", "Onboarding", "Performance", "Price", "Reporting", "UX_UI", "Value_Prop"]
考虑到我们正在处理嘈杂的文本数据,拥有可以清理数据的自定义预处理帮助程序肯定是有用的,如下所示:
简而言之,清理步骤包括数据的小写、使用 wordnet pos 标签的词汇化以及删除标点符号。这里要注意的一个重要步骤就在 gist 的上面,我将所需的 NLTK 资源下载到tmp 文件夹中,我将它附加到 NLTK 可以查找资源的路径列表中。在运行时,这是此类资源的唯一可写文件夹。另一种方法是将这些资源保存在包含所有模型工件的源分发包 tarball 中作为序列化对象,并将它们直接加载到自定义预测器类中(我们将在后面讨论)。
下面是我用来训练每个模型的代码:
在上述要点中需要注意的重要一点是使用了来自 sklearn 的 FunctionTransformer 方法,该方法允许我们向管道添加自定义预处理例程,只要自定义例程的 I/O 与其相邻例程的 I/O 一致。本质上,上面的代码循环遍历数据帧中的每个标签,使用LabelEncoder将标签列转换为标签。然后,使用反馈r_text 和转换后的标签为每个标签训练一个模型,最终导出为酸洗对象。可以认为,在这种情况下,具有包含clean_data_custom 和text_transform ,的公共预处理 sklearn 管道对象将是最佳的,因为每个模型都在相同的数据上被训练,具有相同的配置。然而,为了简单起见,我们允许每个模型都有自己的预处理管道。如果您最终得到了一个公共管道对象,这可以合并到自定义预测器类中,我们将在接下来讨论。
让我们快速回顾一下。到目前为止,我有 11 个酸洗的模型管道(每个标签一个),以及一个预处理模块,带有用于清理的辅助函数。
现在的主要问题是,CAIP 如何将预测的输入反馈导入这 11 个模型?另外,我该如何处理 preprocess.py 以便 CAIP 可以将其链接到模型管道对象,这样他们就可以使用clean 函数了?
为了解决第一个问题,自定义预测例程开始发挥作用。参考下面我写的 CustomPredictor 类:
在我们继续之前,要知道每次我们为一个定制的预测例程创建一个新的模型版本时,我们都会将信息传递给 CAIP,它可以使用这些信息在一个存储桶中寻找模型工件。稍后我们将在代码中看到如何做到这一点,但现在要理解这些信息是使用标志传递给 gcloud 的。因此,当我们创建一个新的模型版本时,我们设置prediction-class gcloud 标志指向 multiTag 模块中的 CustomPredictor 类。GCP 现在所做的是在源代码发行包、中查找这个类,并调用带有模型目录的from_path类方法作为参数,它从另一个名为origin 的标志中选取这个参数,这个标志也是在创建模型版本时传递的。确保 GCP 在这里传递正确的目录路径是至关重要的!该方法将所有加载的模型存储到一个列表中,并用这个列表实例化一个CustomPredictor 对象。
现在,在创建自定义预测器类时,必须遵循以下模板:
基本上,总是让from_path方法返回一个带有相关参数的自定义预测器类的实例,在本例中只是一个模型列表。这也是我们可以加载自定义预处理管道对象并将其传递给自定义预测器的地方,我们在前面简单地讨论过。现在,所有的神奇都发生在 predict 函数中。这将 JSON 反序列化的输入字符串列表作为输入。如代码所示,我简单地循环每个实例,循环每个模型,并将预测结果作为 python 中的 dictionary 对象追加。返回的results必须是 JSON 可序列化的。
现在回到第二个问题(你记得是什么吗?).我如何告诉 CAIP 将 preprocess.py 链接到模型管道对象,以便它们可以使用clean 函数?另外,我如何上传 multiTag.py 模块?
这两个问题的答案都是一样的:作为一个源码分发包 (SDP)!如果您没有记错的话,我曾经提到过几次,所有的工件都必须包含在 SDP 中,并且这两个文件都算作模型工件。
下面是创建 SDP 的模块 setup.py :
scripts 参数应该是每个相关脚本的本地路径的 iterable。要创建 SDP,请运行以下命令:
python setup.py dist --formats=gztar
这里,dist是 SDP 的目标目录。
现在我们已经准备好了所有的脚本和模型,剩下的就是运行几个 gcloud 命令,将模型和 SDP 复制到一个存储桶,创建一个模型资源并将其链接到这个桶,最后创建一个模型版本并进行在线预测!
首先,创建一个具有有效存储段名称和区域的存储段:
$BUCKET_NAME="multiTag_bucket"
gsutil mb -l $REGION gs://$BUCKET_NAME
为了复制模型和 SDP,我使用了gsutil cp命令:
gsutil -m cp *.pkl gs://$BUCKET_NAME
gsutil cp dist/multiTag_custom_package-0.1.tar.gz gs://$BUCKET_NAME/multiTag_custom_package-0.1.tar.gz
更容易将 bucket 放在注册到 AI 平台的同一个项目中,否则 bucket 将需要 显式访问 到 AI 平台服务帐户。
现在我们已经将所有的工件都放在了一个存储桶中,让我们创建一个模型:
gcloud ai-platform models create "multiTag_model"
让我们也设置以下变量
MODEL_DIR="gs://multiTag_bucket"
VERSION_NAME="v0"
MODEL_NAME="multiTag_model"
CUSTOM_CODE_PATH="gs://multiTag_bucket/multiTag_custom_package-0.1.tar.gz"
PREDICTOR_CLASS="multiTag.CustomPredictor"
我将所有的模型和工件直接放入桶中,没有创建任何额外的目录,所以我的MODEL_DIR只是桶 URI(通用资源标识符)。如果您的模型和相关的工件有不同的目录结构,确保您为 MODEL_DIR和CUSTOM_CODE_PATH给出了正确的绝对路径。
现在,创建如下面代码所示的版本。这就是我之前的意思,当我提到在创建一个模型版本时,将所有关于模型工件的位置元数据传递给 CAIP。
gcloud beta ai-platform versions create $VERSION_NAME \
--model $MODEL_NAME \
--origin $MODEL_DIR \
--runtime-version=1.13 \
--python-version=3.5 \
--package-uris=$CUSTOM_CODE_PATH \
--prediction-class=$PREDICTOR_CLASS
一旦创建了版本,您现在可以发送在线预测的输入。用这种格式的输入创建一个. txt 或 JSON 文件(推荐)。
现在,您可以通过运行以下代码获得在线预测:
gcloud ai-platform predict --model $MODEL_NAME --version $VERSION_NAME --json-instances $INPUT_FILE
并得到预测:

Predictions for a feedback
因此..
总的来说,我认为部署过程非常顺利,也很容易理解。然而,当我刚开始使用 CAIP 时,我面临的一个挑战是,对于部署期间或部署后(预测期间)的内部错误,回溯对调试没有太大帮助。我必须仔细检查每一点代码以了解问题,然后再次部署,这可能会让您有点紧张,因为部署过程本身需要几分钟,尤其是对于大型模型。所以调试这里或那里的小错误实际上会花费你很多时间!
等等,什么?你说用 CAIP local predict测试我的模型?对于那些不知道的人,在部署之前,您可以使用 CAIP local predict命令来测试您的模型在部署之前如何服务于预测。该命令使用本地环境中的依赖项来执行预测,并以与[gcloud ai-platform predict](https://cloud.google.com/sdk/gcloud/reference/ai-platform/predict)执行在线预测时相同的格式返回结果。在本地测试预测有助于您在为在线预测请求支付费用之前发现错误。但是,但是..酪这个特性不适用于定制的预测例程,所以在部署之前要非常小心你的代码!
最后,需要注意的最后一点是,CAIP 上模型工件的限制是 250 MB 。你可以向谷歌申请更高的配额来部署更大的型号。
就这样,你成功了!我真的希望你喜欢阅读这篇文章,并发现它是有帮助的。如果您有任何问题,请留下您的评论!
部署愉快!
使用空间的自定义命名实体识别

Figure 1: Source
什么是命名实体识别(NER)?
命名实体识别(NER)是信息提取(IE)的一个子任务,在一个或多个文本主体中找出指定的实体并对其进行分类。NER 也简称为实体识别、实体分块和实体提取。NER 被用于人工智能的许多领域( AI ),包括自然语言处理( NLP )和机器学*。
NER 的空间
SpaCy 是 Python 中高级自然语言处理的开源库。它是专门为生产使用而设计的,有助于构建处理和“理解”大量文本的应用程序。它可以用于构建信息提取或自然语言理解系统,或者用于深度学*的文本预处理。spaCy 提供的一些功能包括标记化、词性(PoS)标记、文本分类和命名实体识别。
SpaCy 为 python 中的 NER 提供了一个非常有效的统计系统,它可以将标签分配给连续的标记组。它提供了一个默认模型,可以识别广泛的命名或数字实体,包括个人、组织、语言、事件等。除了这些默认实体之外,spaCy 还允许我们自由地向 NER 模型添加任意类别,方法是训练该模型,以便用更新的训练样本对其进行更新。
入门指南
装置
SpaCy 可以通过简单的pip安装来安装。您还需要下载您希望使用 spaCy 的语言的语言模型。
pip install -U spacy
python -m spacy download en
我们开始吧!
数据集
我们将要处理的数据集可以从这里下载。我们将使用ner_dataset.csv文件,只训练 260 个句子。

Figure 2: NER Dataset
数据集由以下标签组成-
- 地理=地理实体
- org =组织
- per =人
- 地缘政治实体
- tim =时间指示器
- 艺术=艺术品
- eve =事件
- 自然现象
数据集遵循生物类型标记。
数据预处理
SpaCy 要求训练数据采用以下格式-

Figure 3: spaCy Format Training Data (Source)
所以我们必须把我们的数据从.csv格式转换成上面的格式。(spaCy 也接受其他形式的训练数据。更多细节请参考 文档 。)我们首先删除列Sentence #和POS,因为我们不需要它们,然后将.csv文件转换为.tsv文件。接下来,我们必须运行下面的脚本来获取.json格式的训练数据。
现在数据应该是这样的,

Figure 4: Training Data in Json Format
下一步是将上述数据转换成 spaCy 需要的格式。可以使用以下脚本来完成-
现在我们已经为训练准备好了数据!让我们通过添加自定义实体来训练一个 NER 模型。
使用自定义实体培训空间 NER
SpaCy NER 已经支持像- PERSON人这样的实体类型,包括虚构的。民族、宗教或政治团体。FAC建筑、机场、公路、桥梁等。ORG公司、机关、机构等。GPE国家、城市、州等。
我们的目标是进一步训练这个模型,使其包含我们数据集中的自定义实体。要做到这一点,我们必须经历以下步骤-
- 加载模型,或使用带有所需语言 ID 的
[spacy.blank](https://spacy.io/api/top-level#spacy.blank)创建一个空模型。如果正在使用空白模型,我们必须将实体识别器添加到管道中。如果使用现有模型,我们必须在训练期间使用[nlp.disable_pipes](https://spacy.io/api/language#disable_pipes)禁用所有其他管道组件。这样,只有实体识别器得到训练。
# Setting up the pipeline and entity recognizer.if model is not None:
nlp = spacy.load(model) # load existing spacy model
print("Loaded model '%s'" % model)
else:
nlp = spacy.blank('en') # create blank Language class
print("Created blank 'en' model")if 'ner' not in nlp.pipe_names:
ner = nlp.create_pipe('ner')
nlp.add_pipe(ner)
else:
ner = nlp.get_pipe('ner')
2.使用[add_label](https://spacy.io/api/entityrecognizer#add_label)方法将新的实体标签添加到实体识别器中。
# Add new entity labels to entity recognizerfor i in LABEL:
ner.add_label(i)# Inititalizing optimizerif model is None:
optimizer = nlp.begin_training()
else:
optimizer = nlp.entity.create_optimizer()
3.循环遍历示例,并调用[nlp.update](https://spacy.io/api/language#update),逐步遍历输入的单词。在每一个单词上,它都会做出一个预测。然后,它查阅注释,看看它是否正确。如果它错了,它会调整它的权重,这样下次正确的动作会得到更高的分数。
# Get names of other pipes to disable them during training to train # only NER and update the weightsother_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'ner']
with nlp.disable_pipes(*other_pipes): # only train NER
for itn in range(n_iter):
random.shuffle(TRAIN_DATA)
losses = {}
batches = minibatch(TRAIN_DATA,
size=compounding(4., 32., 1.001))
for batch in batches:
texts, annotations = zip(*batch)
# Updating the weights
nlp.update(texts, annotations, sgd=optimizer,
drop=0.35, losses=losses)
print('Losses', losses) nlp.update(texts, annotations, sgd=optimizer,
drop=0.35, losses=losses)
print('Losses', losses)
4.使用[nlp.to_disk](https://spacy.io/api/language#to_disk)保存训练好的模型。
# Save model
if output_dir is not None:
output_dir = Path(output_dir)
if not output_dir.exists():
output_dir.mkdir()
nlp.meta['name'] = new_model_name # rename model
nlp.to_disk(output_dir)
print("Saved model to", output_dir)
5.测试模型,确保新实体被正确识别。
# Test the saved model
print("Loading from", output_dir)
nlp2 = spacy.load(output_dir)
doc2 = nlp2(test_text)
for ent in doc2.ents:
print(ent.label_, ent.text)
使用此脚本来训练和测试模型-
执行指令-
python spacy_ner_custom_entities.py \
-m=en \
-o=path/to/output/directory \
-n=1000
结果
当对查询- ['John Lee is the chief of CBSE', 'Americans suffered from H5N1']进行测试时,模型识别出以下实体-
John Lee is the chief of CBSE.B-per JohnI-per LeeB-org CBSE Americans suffered from H5N1 virus in 2002.B-gpe AmericansB-nat H5N1B-tim 2002
结论
我希望你现在已经明白了如何在斯帕西 NER 模型的基础上训练你自己的 NER 模型。感谢阅读!😊
面向非数据科学家的自定义对象检测— Tensorflow
使用 Tensorflow 对象检测 API 用您自己的数据集训练模型。

在这篇文章的结尾你能做什么
总的来说,在本教程结束时,你基本上能够拿起你的数据集,将其加载到 jupyter 笔记本上,训练并使用你的模型:)
上面的图片是我们要在这里玩的例子的结果。
警告
这篇文章只是想让你了解一下 TF 物体检测 API,但是这个内容只是我的 jupyter 笔记本的拙劣翻版,如果你想看一些更有结构和更漂亮的东西,去我的 jupyter 笔记本那里有和这里一样的文本,但是在一个更漂亮和更容易理解的媒介中:)
首先,让我们安装依赖项
!pip install pillow
!pip install lxml
!pip install Cython
!pip install jupyter
!pip install matplotlib
!pip install pandas
!pip install opencv-python
!pip install tensorflow
下载 Tensorflow 对象检测 API
首先,让我们下载 tensorflow 模型存储库,这个存储库中的
有对象检测 api,我们将
用它来训练我们自己的对象检测模型。
我们要做的一切,都在路径 models/research/object _ detection 里面。
!git clone [https://github.com/tensorflow/models/](https://github.com/tensorflow/models/)
%cd models/research/object_detection
在这里,我们只是创建一些文件夹,我们稍后将使用它。
命令mkdir创建目录
!mkdir training
!mkdir inference_graph
!mkdir -p images/train
!mkdir -p images/test
选择我们的预训练模型。
在 tensorflow zoo 模型中,我们可以选择一个预训练的
模型来下载并使用它来训练我们自己的数据集。
在 tensorlfow zoo 模型存储库的文件夹中,我们有一个表
,它解释了模型的精度(使用 mAP-mean Average Precision)
以及该模型的速度。
您可以选择您想要的任何型号,本教程中的过程对于其他
型号是相同的。
本教程我选了更快的 _ rcnn _ inception _ v2 _ coco _ 2018 _ 01 _ 28,只是
因为我要:)

!wget [http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz)
!tar -xvzf faster_rcnn_inception_v2_coco_2018_01_28.tar.gz
!rm -rf faster_rcnn_inception_v2_coco_2018_01_28.tar.gz
根据文档,将 PYTHONPATH 环境变量与模型、reasearch 和 slim path 一起导出是很重要的
import os
os.environ['PYTHONPATH'] = "{}/content/obj_detect_api/models:/content/obj_detect_api/models/research:/content/obj_detect_api/models/research/slim".format(os.environ['PYTHONPATH'])
汇编一些东西
这里我们有一些需要编译的原型缓冲区,记住
它是编译的,所以如果你切换机器,你不能只是复制和粘贴
这个家伙生成的文件。
老实说,我无法从“/research/object _ detection”
文件夹中执行这些原型,我尝试了 N 种方法都没用,所以我只是从
“/research”文件夹中编译它们。
理解什么是 proto buffers 对于本教程来说并不是必须的,但是如果你想让
了解更多,我建议你看一下文档,基本上它是包含消息结构的文本
结构(比如 json,xml ),你可以把它转换成一些语言
,比这多一点,但是现在对于你阅读那篇文章已经足够了
。
%cd ..!protoc ./object_detection/protos/*.proto --python_out=.
我不记得 Tensroflow 异议检测 API
要求的确切版本,但我从版本> = 3.0 中得知效果很好。
!protoc --version
所以只要安装它
!python3 setup.py build
!python3 setup.py install
测试安装是否正常。
一旦我们安装了所有东西,我们就可以从
Tensorflow 对象检测 API 运行一些示例脚本来验证一切是否正确。这个代码不是我的,我从 object_detection 文件夹中复制了它,只是为了在 jupyter 笔记本上运行它而做了一些修改。
我不会解释这段代码,因为它只是为了测试安装,我们稍后会看到类似的代码。
只是执行这些细胞。
警告:只需证明你使用的张量流> =1.12.0
Obs:记住你可以离开这篇文章,去 jupyter 笔记本,哪个更好:)

如果在你执行下面的单元格后,你可以看到一只狗的图像和一个海滩的图像,那么一切都正常!

用我们自己的数据集训练模型。
首先,让我们下载数据集。
关于数据集
在这篇文章中,我们将使用 kaggle 的数据集,但是不要担心,在这篇教程中,我将把你引向另一篇教你如何创建自己的分类数据集的文章。
帕斯卡-VOC
在谈论数据集之前,我想提醒大家,在本教程中,我们将使用 Pascal-VOC 格式的
数据集,这是一种著名的格式,其中有:
- jpg、jpeg、png 格式的图像…
- 注释:。以下格式的 xml 文件:
<annotation>
<folder>GeneratedData_Train</folder>
<filename>000001.png</filename>
<path>/my/path/GeneratedData_Train/000001.png</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>224</width>
<height>224</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>21</name>
<pose>Frontal</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<occluded>0</occluded>
<bndbox>
<xmin>82</xmin>
<xmax>172</xmax>
<ymin>88</ymin>
<ymax>146</ymax>
</bndbox>
</object>
</annotation>
对于每个图像,我们都有一个。同名 xml,例子:image001.png-> image 001 . XML
注意这里面。xml 我们有关于图像的其他信息,比如位置、大小、对象以及它们在图像中的位置…
我们在本文中使用的数据集是从 kaggle 中提取的 LISA 交通灯数据集,该
数据集包含交通灯的图像和分类,分类如下:
- 去;
- 停止;
- 警告;
- goLeft
- goForward
- stopLeft
- warningLeft
但是为了使本教程更简单,我修改了类,所以我们只有:
- 去;
- 停止;
- 警告;
下载后,请注意,我们将所有内容都移动到文件夹… / images 中
%cd object_detection
!wget --load-cookies /tmp/cookies.txt "[https://docs.google.com/uc?export=download&confirm=$(wget](https://docs.google.com/uc?export=download&confirm=$(wget) --quiet --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate '[https://docs.google.com/uc?export=download&id=15WlpBbq4EpxUxZeKEAbfI_YJABASpmFs'](https://docs.google.com/uc?export=download&id=15WlpBbq4EpxUxZeKEAbfI_YJABASpmFs') -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=15WlpBbq4EpxUxZeKEAbfI_YJABASpmFs" -O lisa608.zip
!unzip -qq ./lisa608.zip!mv images_output/* images/
!mv annotations_output/* images/
对于这个简单的教程,我不想处理太多的类,你可以这样做,但是为了简化这个过程,我将重命名所有最后有“Left”和“Forward”的类

在这里,我只是在训练和测试中拆分了我的数据集

如果你的注释是 Pascal-VOC 格式的(就像我们的例子),你需要把它们转换成 csv 格式。
我知道,我们的原始数据集之前已经是 csv 格式的了,但是我已经把它转换成了 xml,只是为了向您展示这个场景。
在这里,我们迭代每个文件夹、训练、测试和验证(如果我们有用于验证的数据),因此我们提取数据:
- 文件名
- 宽度
- 高度
- 班级
- xmin
- ymin
- xmax
- ymax
放在我们的 csv 上。

生成 TFRecords
要使用 TF 对象检测 API,我们需要遵循 TFRecord 输入格式。
当我们处理大量数据时,使用轻便快速的格式很重要,一个
选项是处理文档的二进制文件,这正是 TFRecords 所做的,但此外
针对 Tensorflow 进行了优化,因为它是为 Tensorflow 创建的,例如当
处理非常大的数据集并试图将其加载到内存中时, 显然你不会得到它
,因为你没有足够的内存 RAM 来做这件事,所以你必须处理批处理,但是
如果你使用你不需要的 TFRecords 来处理批处理,它为你抽象出如何将数据加载到内存中
,而不需要你自己编程。
这里有一篇文章详细解释了 TFRecords 的工作原理。
对于每个项目,必须在class_text_to_int ()方法中进行一些更改,注意
有一个非常简单的逻辑结构,我们必须根据这个类返回一个整数
Obs:你可以从 jupyter 笔记本上看到完整的代码。

我们还需要在 TF 中输入我们的类。为此,我们将创建一个具有以下结构的. pbtxt 文件:
item {
id: 1
name: 'stop'
}
对于每个类,我们都有一个条目,每个条目都有一个 id 和一个名称,id 指的是我们在 TFRecords 中使用的 Id:
def class_text_to_int(row_label):
if row_label == 'stop':
return 1
elif row_label == 'warning':
return 2
elif row_label == 'go':
return 3
else:
None
记得使用相同的 id。
%%writefile training/labelmap.pbtxt
item {
id: 1
name: 'stop'
}item {
id: 2
name: 'warning'
}item {
id: 3
name: 'go'
}
这里我们将打印图像文件夹的长度,以便以后使用这些信息..
import os
test_path = "images/test"
len_dir = len(os.listdir(test_path))
print("{} imagens inside the {}".format(len_dir, test_path))
下一个单元格有一个文件是神经网络的配置文件,这里我们有一些超参数
,我们必须对其进行修改。
我从 faster _ rcnn _ inception _ v2 _ pets . config 复制了这个配置文件,并根据我各自的
修改进行了编辑,对于您将使用的每个深度学*架构,您需要一个不同的配置文件,
您可以在这里找到所有这些文件[...]/research/object_detection/samples/configs,或者如果您喜欢的话,
通过 github 访问。
在这些文件中,有些东西应该被改变,比如: num_classes
faster_rcnn {
num_classes: 3
微调检查点
fine_tune_checkpoint: "/home/<full_path>/research/object_detection/faster_rcnn_inception_v2_coco_2018_01_28/model.ckpt"
在属性 train_input_reader 中,将 input_path 和 label_map_path 修改为:
tf_record_input_reader {
input_path: "/<full_path>/research/object_detection/train.record"
}
label_map_path: "/<full_path>/research/object_detection/training/labelmap.pbtxt"
}
请记住,train_input_reader 必须是 train.record 文件。
在 eval_config 属性中,在 num_examples 属性中更改您必须测试的实例(图像)数量(在…/images/test 文件夹中)
:
eval_input_reader: {
tf_record_input_reader {
input_path: "/full_path/research/object_detection/test.record"
}
label_map_path: "/full_path/research/object_detection/training/labelmap.pbtxt"
请注意,我使用的是测试记录而不是训练记录
Obs:记住你可以看到代码,并且已经通过 google colabotory 运行过了

%cd ..
!protoc ./object_detection/protos/*.proto --python_out=.%cd object_detection
对此我很抱歉,但我得到了一个非常恼人的错误,它说没有找到 slim lib,
许多人说问题出在我的 PYTHONPATH 环境变量中,但我验证了一千次
并没有发现问题,所以我通过将所有/research/slim 代码复制到/object_detection
文件夹中来解决它,如果任何人发现什么做错了,请在下面评论。
!cp -a ../slim/. .
让我们训练它!
%run legacy/train.py --logtostderr --train_dir=training/ --pipeline_config_path=training/faster_rcnn_inception_v2.config

训练您的模型后,请注意在/training 文件夹中我们有一个 model . ckpt-22230 . data-00000-of-00001 文件,可能在您的
机器上会有另一个编号,该文件是您保存的训练模型!
!ls training

因为我们已经将整个/slim 文件夹复制到/object_detection,我们已经覆盖了
inference_graph.py文件,所以我们在这里重新创建了该文件。
想了解更多关于推论的知识,我推荐这几篇非常好的文章。第一条 第二条

让我们运行我们的推论!记得更改 trained_checkpoint_prefix 属性,以使用/ training 文件夹中的 model.ckpt-XXX
!python3 my_inference_graph.py \
--input_type image_tensor \
--pipeline_config_path training/faster_rcnn_inception_v2_pets.config \
--trained_checkpoint_prefix training/model.ckpt-22230 \
--output_directory ./inference_graph
我们将在这里下载一些测试图像,以查看我们的模型的一些结果。
!wget '[http://marcusquintella.sigonline.com.br/openged/conteudos/1306/001306_59bc593899b85_Semaforos_out_2017.jpg'](http://marcusquintella.sigonline.com.br/openged/conteudos/1306/001306_59bc593899b85_Semaforos_out_2017.jpg') -O semaforo.png
这段代码和测试代码很像!
import os
import cv2
import numpy as np
import tensorflow as tf
import sys# This is needed since the notebook is stored in the object_detection folder.
sys.path.append("..")
让我们定义一些常量和路径,模型的检查点,标签等等。
# Import utilites
from utils import label_map_util
from utils import visualization_utils as vis_util# Name of the directory containing the object detection module we're using
MODEL_NAME = 'inference_graph'
IMAGE_NAME = 'semaforo.png'# Grab path to current working directory
CWD_PATH = os.getcwd()# Path to frozen detection graph .pb file, which contains the model that is used
# for object detection.
PATH_TO_CKPT = os.path.join(CWD_PATH,MODEL_NAME,'frozen_inference_graph.pb')# Path to label map file
PATH_TO_LABELS = os.path.join(CWD_PATH,'training','labelmap.pbtxt')# Path to image
PATH_TO_IMAGE = os.path.join(CWD_PATH,IMAGE_NAME)# Number of classes the object detector can identify
NUM_CLASSES = 3
在这里,我们加载我们的 label_maps,这样我们就可以进行模型预测,例如,
知道数字 3 对应于类别“go”
label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True)
category_index = label_map_util.create_category_index(categories)
让我们加载我们的模型,并从模型中选择一些层
# Load the Tensorflow model into memory.
detection_graph = tf.Graph()
with detection_graph.as_default():
od_graph_def = tf.GraphDef()
with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
serialized_graph = fid.read()
od_graph_def.ParseFromString(serialized_graph)
tf.import_graph_def(od_graph_def, name='')sess = tf.Session(graph=detection_graph)# Define input and output tensors (i.e. data) for the object detection classifier# Input tensor is the image
image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')# Output tensors are the detection boxes, scores, and classes
# Each box represents a part of the image where a particular object was detected
detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')# Each score represents level of confidence for each of the objects.
# The score is shown on the result image, together with the class label.
detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')# Number of objects detected
num_detections = detection_graph.get_tensor_by_name('num_detections:0')
下面我们加载图像并运行我们的模型,如果你对 Tensorflow 一无所知,我建议你
阅读这篇文章(这是一篇快速文章)并阅读文档,如果你想了解 tensorflow 中
如何工作的更多细节。
一些参数在这里是可配置的,例如定义线条宽度的 line_thickness 和对应于您希望 Tensoflow 的置信度百分比的
min_score_thresh:嘿,
在这里有一个对象具有超过 X%的置信度。(在我们的例子中,我们将使用 0.6)
Load image using OpenCV and
# expand image dimensions to have shape: [1, None, None, 3]
# i.e. a single-column array, where each item in the column has the pixel RGB value
image = cv2.imread(PATH_TO_IMAGE)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image_expanded = np.expand_dims(image, axis=0)# Perform the actual detection by running the model with the image as input
(boxes, scores, classes, num) = sess.run(
[detection_boxes, detection_scores, detection_classes, num_detections],
feed_dict={image_tensor: image_expanded})# Draw the results of the detection (aka 'visulaize the results')vis_util.visualize_boxes_and_labels_on_image_array(
image,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
category_index,
use_normalized_coordinates=True,
line_thickness=8,
min_score_thresh=0.6)
让我们用各自的分类打印我们的图像。在实际应用中,没有必要使用 visualize _ boxes _ and _ labels _ on _ image _ array(),可以分别使用盒子、类和分数。
%matplotlib inlineplt.figure(figsize=(20,10))
plt.imshow(image)
这是原始图像
image = cv2.imread(PATH_TO_IMAGE)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)plt.figure(figsize=(20,10))
plt.imshow(image)


现在你可以下载这个推论并在你的机器上,服务器上,任何你想要的地方使用它
!zip -r inference_graph.zip /content/obj_detect_api/models/research/object_detection/inference_graph
所以发到 google drive 上。
!pip install -U -q PyDrive
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from google.colab import auth
from oauth2client.client import GoogleCredentials# 1\. Authenticate and create the PyDrive client.
auth.authenticate_user()
gauth = GoogleAuth()
gauth.credentials = GoogleCredentials.get_application_default()
drive = GoogleDrive(gauth)model_file = drive.CreateFile({'title' : 'inference_graph.zip'})
model_file.SetContentFile('./inference_graph.zip')
model_file.Upload()# download to google drive
drive.CreateFile({'id': model_file.get('id')})
从头开始使用 TensorFlow 进行自定义对象检测
内部 AI
基于自定义数据集的 TensorFlow 对象检测训练

Custom Object Detection source: https://github.com/bourdakos1/Custom-Object-Detection
在本教程中,我们将尝试使用预先训练好的 SSD MobileNet V2 模型来训练我们自己的狗(corgi)检测器。
您无需从头开始训练自己的模型,而是可以在现有模型的基础上进行构建,并根据自己的目的对其进行微调,而不需要太多的计算能力。
1.装置
1.1 张量流
使用以下命令安装 Tensorflow:
$ pip install tensorflow
如果您有可以与 Tensorflow 一起使用的 GPU:
$ pip install tensorflow-gpu
1.2 其他依赖关系
$ pip install pillow Cython lxml jupyter matplotlib
使用自制软件安装 protobuf(你可以在这里了解更多关于自制软件的信息)
$ brew install protobuf
对于在其他操作系统上安装 protobuf,请遵循此处的说明。
1.3 克隆 Tensorflow 模型库
在本教程中,我们将使用 Tensorflow 模型存储库中的资源。因为 Tensorflow 安装没有附带它,所以我们需要从他们的 Github repo 中克隆它:
首先进入张量流目录:
# *For example: ~/anaconda/envs/<your_env_name>/lib/python3.6/site-packages/tensorflow*$ cd <path_to_your_tensorflow_installation>
克隆 Tensorflow 模型库:
$ git clone [https://github.com/tensorflow/models.git](https://github.com/tensorflow/models.git)
从此时起,该目录将被称为 **models** 目录
1.4 设置环境
每当您启动一个新的终端窗口来处理预训练的模型时,编译 Protobuf 并更改您的PYTHONPATH是非常重要的。
从终端运行以下命令:
$ cd <path_to_your_tensorflow_installation>/models/research/$ protoc object_detection/protos/*.proto --python_out=.$ export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
运行快速测试以确认对象检测 API 工作正常:
$ python object_detection/builders/model_builder_test.py
如果结果如下所示,您就可以继续下一步了!
...............
----------------------------------------------------------------------
Ran 15 tests in 0.123sOK
1.5 推荐的文件夹结构
为了使本教程更容易理解,在刚刚克隆的models目录中创建以下文件夹结构:
models
├── annotations
| └── xmls
├── images
├── checkpoints
├── tf_record
├── research
...
这些文件夹将用于存储我们的模型所需的组件。
2.收集图像
您可以收集图像或视频格式的数据。这里我提到了收集数据的两种方法。
2.1 从互联网上收集图像(谷歌搜索)
数据准备是训练自己模型最重要的部分。既然要训练一只柯基探测器,就必须收集柯基的图片!大约 200 个就足够了。
我推荐使用 google-images-download 下载图片。它搜索谷歌图片,然后根据你提供的输入下载图片。在输入中,您可以指定搜索参数,如关键字、图像数量、图像格式、图像大小和使用权。
因为我们一次下载 100 多张图片,所以我们需要在models目录中有一个chromedriver(在这里下载)。一旦您准备好了chromedriver,您就可以使用这个示例命令来下载图像。确保您所有的图像都是 jpg 格式:
*# From the models directory*$ googleimagesdownload --keywords 'welsh corgi dog' \
--limit 200 \
--size medium \
--chromedriver ./chromedriver \
--format jpg
下载后,将所有图像保存到models/images/。为了使后续过程更容易,让我们通过运行以下脚本将图像重命名为数字(例如1.jpg、2.jpg):
import ospath = 'models/images/'
counter = 1
for f in os.listdir(path):
suffix = f.split('.')[-1]
if suffix == 'jpg' or suffix == 'png':
new = '{}.{}'.format(str(counter), suffix)
os.rename(path + f, path + new)
counter = int(counter) + 1
2.2 从视频中收集图像
[## 使用 OpenCV (Python)将视频转换为图像以及将图像转换为视频
使用 Python 中的 OpenCV 库从视频生成图像(帧)和从图像(帧)生成视频
medium.com](https://medium.com/@iKhushPatel/convert-video-to-images-images-to-video-using-opencv-python-db27a128a481)
3.标记您的数据集
一旦你收集了所有你需要的图片,你需要手动标记它们。有许多服务于此目的的软件包。标签是一个受欢迎的选择。
labelImg 提供了一个用户友好的 GUI。另外,它以流行的 Pascal VOC 格式保存标签文件(.xml)。如果你想用这些图片来训练 YOLO(你只看一次),那就用 YOLO。只需设置当前目录,并按照我们的结构保存目录。
以下是标签图像在 labelImg 中的样子:

Example of a labeled corgi in labelImg
仔细检查每个图像是否有相应的.xml文件,并将其保存在models/annotations/xmls/中。
对于大量注释,您可以使用下面提到的不同快捷键:
Ctrl + u - Load all of the images from a directory
Ctrl + r - Change the default annotation target dir
Ctrl + s - Save
w - Create a rect box
d - Next image
a - Previous image
del - Delete the selected rect box
Ctrl++ - Zoom in
Ctrl-- - Zoom out
Ctrl + d - Copy the current label and rect box
Space - Flag the current image as verified
↑→↓←Keyboard arrows to move selected rect box
4.创建标签地图(.pbtxt)
类别需要在标签映射中列出。由于我们只检测地理信息系统,标签地图应该只包含一个项目,如下所示:
item {
id: 1
name: 'corgi'
}
注意id必须从 1 开始,因为 0 是保留 id。
将该文件另存为models/annotations/中的label_map.pbtxt
5.创建trainval.txt
trainval.txt是没有文件扩展名的图像名称列表。因为我们有图像名称的序列号,所以列表应该是这样的:
1
2
3
...
198
199
200
将该文件保存为models/annotations/中的trainval.txt
6.将 XML 转换为 CSV 文件(.csv)
您可以使用这个链接来创建 CSV 格式的 XML 文件。我们有所有的图像和它们的边界框都是 XML 格式。此外,所有的图像都有单独的 XML 文件,所以使用这个代码,我们正在创建一个 CSV 文件,其中包含所有的 XML 文件和他们的边界框坐标到一个 CSV 文件,这是创建 TFrecords 的输入。
7.创建 TFRecord ( .record)
TFRecord 是为 Tensorflow 设计的重要数据格式。(点击了解更多信息)。在训练自定义对象检测器之前,必须将数据转换为 TFRecord 格式。
由于我们需要训练和验证我们的模型,数据集将被分成训练集(train.record)和验证集(val.record)。训练集的目的很简单——它是模型学*的样本集。验证集是在训练期间使用的一组示例,用于反复评估模型准确性。
我们将使用create_tf_record.py将我们的数据集转换成train.record和val.record。在这里下载并保存到models/research/object_detection/dataset_tools/。
根据您的分类,只需更改 if row_label == ‘Label1’: 中的标签名称即可。
该脚本预先配置为进行 70–30 列车阀分割。通过运行以下命令来执行它:
# *From the models directory*$ python research/object_detection/dataset_tools/create_tf_record.py
如果脚本执行成功,train.record和val.record应该会出现在您的models/research/目录中。将它们移动到models/tf_record/目录。
8.下载预先训练的模型
在模型动物园中有很多预先训练好的物体检测模型可用。为了使用我们的定制数据集训练它们,模型需要使用它们的检查点(.ckpt文件)在 Tensorflow 中恢复,这些检查点是以前模型状态的记录。
对于本教程,我们将在这里下载ssd_mobilenet_v2_coco,并将其模型检查点文件(model.ckpt.meta, model.ckpt.index, model.ckpt.data-00000-of-00001)保存到我们的models/checkpoints/目录中。
9.修改配置(.config)文件
每个预训练的模型都有一个包含模型细节的配置文件。为了检测我们的自定义类,需要相应地修改配置文件。
配置文件包含在您一开始克隆的models目录中。您可以在以下位置找到它们:
models/research/object_detection/samples/configs
在我们的例子中,我们将修改ssd_mobilenet_v2_coco的配置文件。先复制一份,保存在models/目录下。
以下是我们需要更改的项目:
- 因为我们只试图检测柯基,所以将
num_classes改为 1 fine_tune_checkpoint告诉模型使用哪个检查点文件。将此设置为checkpoints/model.ckpt- 模型还需要知道训练集和验证集的 TFRecord 文件和标签映射在哪里。由于我们的
train.record和val.record保存在tf_record文件夹中,我们的配置应该反映出:
train_input_reader: {
tf_record_input_reader {
input_path: "tf_record/train.record"
}
label_map_path: "annotations/label_map.pbtxt"
}eval_input_reader: {
tf_record_input_reader {
input_path: "tf_record/val.record"
}
label_map_path: "annotations/label_map.pbtxt"
shuffle: false
num_readers: 1
}
10.火车
此时,您的models目录应该如下所示:
models
├── annotations
| ├── label_map.pbtxt
| ├── trainval.txt
| └── xmls
| ├── 1.xml
| ├── 2.xml
| ├── ...
|
├── images
| ├── 1.jpg
| ├── 2.jpg
| ├── ...
|
├── checkpoints
| ├── model.ckpt.data-00000-of-00001
| ├── model.ckpt.index
| └── model.ckpt.meta
|
├── tf_record
| ├── train.record
| └── val.record
|
├── research
| ├── ...
...
如果您成功完成了之前的所有步骤,您就可以开始训练了!
请遵循以下步骤:
# *Change into the models directory*
$ cd tensorflow/models# *Make directory for storing training progress*
$ mkdir train# *Make directory for storing validation results*
$ mkdir eval# *Begin training*
$ python research/object_detection/train.py \
--logtostderr \
--train_dir=train \
--pipeline_config_path=ssd_mobilenet_v2_coco.config
训练时间因机器的计算能力而异。
11.估价
评估可以与培训同时进行。eval.py脚本检查train目录的进度,并根据最*的检查点评估模型。
*# From the models directory*$ python research/object_detection/eval.py \
--logtostderr \
--pipeline_config_path=ssd_mobilenet_v2_coco.config \
--checkpoint_dir=train \
--eval_dir=eval
您可以使用 Tensorboard 可视化模型训练进度:
*# From the models directory*$ tensorboard --logdir=./
根据 Tensorboard 输出的图表,您可以决定何时停止训练。通常,当损失函数逐渐减少并且不再显著减少时,你可以停止这个过程。在我的例子中,我停止在步骤 3258。
12.模型导出
一旦完成模型的训练,就可以导出模型用于推理。如果您一直遵循文件夹结构,请使用以下命令:
*# From the models directory*$ mkdir fine_tuned_model$ python research/object_detection/export_inference_graph.py \
--input_type image_tensor \
--pipeline_config_path ssd_mobilenet_v2_coco.config \
--trained_checkpoint_prefix train/model.ckpt-<the_highest_checkpoint_number> \
--output_directory fine_tuned_model
13.分类图像
现在你有了一个模型,你可以用它来检测图片和视频中的柯基犬!出于演示的目的,我们将检测图像中的 CORBA。在继续之前,选择一个您想要用来测试模型的图像。
models目录附带了一个笔记本文件(.ipynb),我们可以使用它进行一些调整来获得推论。它位于models/research/object_detection/object_detection_tutorial.ipynb。按照以下步骤调整笔记本:
MODEL_NAME = 'ssd_mobilenet_v2_coco_2018_03_29'PATH_TO_CKPT = 'path/to/your/frozen_inference_graph.pb'PATH_TO_LABELS = 'models/annotations/label_map.pbtxt'NUM_CLASSES = 1- 完全注释掉单元格#5(就在
Download Model下面) - 因为我们只对一个图像进行测试,注释掉单元格#9 中的
PATH_TO_TEST_IMAGES_DIR和TEST_IMAGE_PATHS(就在Detection下面) - 在单元格#11(最后一个单元格)中,删除 for 循环,取消其内容,并添加测试图像的路径:
imagepath = 'path/to/image_you_want_to_test.jpg
完成这些步骤后,运行笔记本,您应该会看到测试图像中的 corgi 被一个边界框高亮显示!

Corgi found by our custom object detector
那里你有你的自定义柯基探测器!
你可能想知道,在训练完你的第一个模型后,我想改进我的模型并跟踪我的实验细节,什么是好工具?
然后就可以结账 https://neptune.ai 了,它提供了清晰简洁的模型追踪。您可以跟踪所有的超参数,并将模型保存在模型注册表中,并实时比较您的模型。Neptune 支持在一个地方记录、存储、查询、显示、组织和比较所有的模型元数据。更多信息,你可以参考下面的博客来训练物体检测模型。
https://Neptune . ai/blog/tensor flow-object-detection-API-最佳实践-培训-评估-部署
更多细节
请访问我的网站:【http://www.khushpatel.com】
基于 Android 和 TensorFlow 的图像分类箱包识别

问题是:
最*,当我在印度东北部旅行时,我不得不等了很长时间,我的包才出现在机场的行李传送带上。旋转木马周围挤满了通勤者。很难把我的包和其他包区分开来,因为大约有一半的包看起来很相似。我不得不亲自检查六个包,以确保没有一个是我的。
我想有人会建立一些东西来解决这个问题。我开始寻找现有的解决方案,但是什么也找不到。我偶然发现了一些使用 TensorFlow 演示自定义对象检测的博客。后来我发现了这个非常有用的资源,并以此为基础开始着手解决问题。
数据:
我首先需要的是数据。我本可以点击我的包的一些图片,但是我决定捕捉对象的所有侧面和边缘的视频。我从视频中提取单独的帧,并手工挑选视觉上不连续的帧。我将选定的帧转换为灰度图像。我用的是 ffmpeg。在命令行或终端上,键入,
ffmpeg -i video.mp4 -qscale:v 2 image_%03d.jpg
video.mp4 是我的包的视频,输出图像以 image_ 为前缀。然后我需要不属于我包的照片。我发现了一个有用的谷歌
Chrome 扩展,名为 Fatkun 。该扩展使我能够下载大量图像。我在谷歌图片上搜索包包,下载了一堆这样的图片。就像我包包的照片一样,我把后来下载的图片转换成了灰度。
我执行了一个 Python 脚本来将图像转换成灰度。下面的 Python 脚本有一个函数 rescale,它获取一个目录并将该目录中的所有图像转换为灰度。该脚本有一个依赖,PIL。
Python script to convert images inside a folder to grayscale
当我编排我的数据时,没有不平衡数据集的风险。我的包和我的包有相同数量的图像。
食谱:
一旦我有了图像,我就从这里下载tensor flow-for-Poets。您需要在计算机上安装 TensorFlow。此外,你需要下载枕头。您可以通过键入以下命令来完成这两项工作。
pip install --upgrade "tensorflow==1.7.*"
pip install PILLOW
在 Tensorflow-for-poets 的根目录中,我执行了,
python3 -m scripts.retrain --bottleneck_dir=tf_files/bottlenecks --how_many_training_steps=500 --model_dir=tf_files/models/ --summaries_dir=tf_files/training_summaries/mobilenet_v2_1.4_224 --output_graph=tf_files/retrained_graph_bags.pb --output_labels=tf_files/retrained_labels.txt --architecture="mobilenet_0.50_224" --image_dir=/Users/sumit/Desktop/carousel/Baggage-Carousel/Labs/data/
scripts.retrain 将一个目录作为输入。对我来说,这是命令的最后一部分。
image_dir=/Users/sumit/Desktop/carousel/Baggage-Carousel/Labs/data/
该文件夹如下所示:

名为 bag 的文件夹保存了我的包的灰度图像。另一个文件夹里有我能拿到的所有其他包的灰度照片。‘架构’论点是我想要重新训练的模型。我鼓励每个人都和模特一起玩耍。型号列表可以在这里找到。
‘重新训练 _ 标签’是一个生成的文本文件,带有两个文件夹的名称。这两个文件夹的行为就像两个类。同样,' rettrained _ graph _ bags . Pb '是该过程的结果。这两个文件都可以在以下位置找到:
tensorflow-for-poets-2/tf_files
该命令应该从 tensorflow-for-poets 的根目录执行。当命令被执行时,它将启动学*过程。

接下来,我必须将生成的图形转换成 tflite。Tflite 或 TensorFlowLite 用于将机器学*模型部署到移动设备上。转换是通过执行以下命令完成的
toco — graph_def_file=tf_files/retrained_graph_bags.pb — output_file=tf_files/bagdroid_graph.tflite — output_format=TFLITE — input_shape=1,224,224,3 — input_array=input — output_array=final_result — inference_type=FLOAT — input_data_type=FLOAT
上面的命令将' rettrained _ graph _ bags . Pb '作为输入,生成一个移动友好的 tflite 文件,命名为 'bagdroid_graph.tflite '。
Android 应用程序:
Android 应用程序只需要一些改动。除了外观上的变化,
- 我必须将' rettrained _ labels . txt '和' bag droid _ graph . TF lite '复制到应用程序的 assets 文件夹中。
- 在 ImageClassifier.java,我必须将模型路径和标签路径指向正确的值。
- 我不得不在检测上添加音频和触觉反馈。

Changes made in ImageClassifer.java
结果是:
完成后的应用程序应该看起来更好,并且能够检测它被训练检测的包。

The final application
最后,应用程序能够检测到我的包,给我声音和触觉反馈。

Detecting bag.
问题是:
一个非常明显的问题是,如果两个包是相同的,应用程序将识别这两个包。这个问题可以通过使用 NFC 芯片来缓解。
但是,实际问题是应用程序经常将随机的东西标记为我的包。我很想知道我能做些什么来减少误报。

用于高级机器学*的定制张量流损失函数
和少量迁移学*示例

在这篇文章中,我们将看看:
- 在高级 ML 应用中使用自定义损失函数
- 定义自定义损失函数并集成到基本张量流神经网络模型
- 知识提炼学*的一个简单示例,使用高斯过程参考应用于少量学*问题
链接到我的其他文章:
【注:tensor flow 2.0现已发布,与本文所基于的 the 1.x 版本有较大不同。我仍在研究 TF 2.0 w.r.t 自定义损失和张量操作,如本文所述,可能会写一篇新文章或更新这篇文章。请记住这一点。】
简介
机器学*的老路包括旅行到熟悉的地标和风景点。一组熟悉的界标是预定义的损失函数,为您试图优化的问题提供合适的损失值。我们熟悉分类的交叉熵损失和回归问题的均方误差(MSE)或均方根误差(RMSE)。包括前端(如 Keras)和后端(如 Tensorflow)的流行 ML 包包括一组用于大多数分类和回归任务的基本损失函数。但是,在常规方法之外,还存在自定义的损失函数,您可能需要使用它们来解决某个问题,这些损失函数只受有效张量运算的约束。
在 Keras 中,技术上你可以创建自己的损失函数,但是损失函数的形式仅限于 some_loss ( y_true,y_pred )并且仅此而已。如果您试图以 some_loss_1 ( y_true,y_pred,**kwargs )的形式向损失中添加额外的参数,Keras 将会抛出一个运行时异常,并且您会丢失用于聚合数据集的计算时间。有一些方法可以解决这个问题,但一般来说,我们希望有一种可伸缩的方式来编写一个损失函数,接受我们传递给它的任何有效参数,并以标准和预期的方式对张量进行操作。我们将看到如何直接使用 Tensorflow 从头开始编写一个神经网络,并构建一个自定义的损失函数来训练它。
张量流
Tensorflow (TF)是一个符号和数字计算引擎,它允许我们将张量*串成计算图形,并对它们进行反向传播。Keras 是一个运行在 Tensorflow 之上的 API 或前端,它可以方便地打包使用 Tensorflow 构建的标准结构(如各种预定义的神经网络层),并从程序员那里抽象出许多 TF 的低级机制(Keras 也可以运行在 Theano 之上,同样的概念也适用)。然而,在使这些构造‘现成’的过程中,粒度级控制和做非常具体的事情的能力丧失了。
*为了简单起见,张量是具有类似(feature_dim,n_features)的形状元组的多维数组
一个例子是定义接受任意数量参数的定制损失函数的能力,并且可以使用网络内部的任意张量和网络外部的输入张量来计算损失。严格来说,TF 中的一个损失函数甚至不需要是 python 函数,只需要对 TF 张量对象进行有效的运算组合即可。前一点很重要,因为自定义损失的能力来自于计算任意张量上的损失的能力,而不仅仅是严格意义上的监督目标张量和网络输出张量,其形式为( y_true,y_pred )。
在我们讨论客户损失之前,让我们简要回顾一个基本的 2 层密集网络(MLP ),看看它是如何在 TF 中定义和训练的。虽然有预定义的 TF 层,但让我们从权重和偏差张量开始定义这些层。我们想熟悉 TF 中的占位符和变量张量。
那真是太酷了。通过用 soft max _ cross _ entropy _ with _ logits 替换损失,并用 tf.nn.softmax 替换最终 sigmoid 激活,可以修改上面的代码以用于多类分类。
接下来,为了演示如何使用具有任意张量的自定义损失函数,让我们实现一个知识提取模型,该模型对二进制分类损失以及正在训练的模型和参考模型之间的损失进行优化。知识提炼是迁移学*的一种形式,我们用目标模型(我们想要训练的模型)学*,但也间接从参考模型迁移知识表示。我们将使用来自 sklearn 的高斯过程分类器 (GPC)作为我们的参考模型。我们还将通过将我们的训练数据减少到 sklearn 乳腺癌数据集… 中 569 个样本的 1%来使问题变得更有趣,无论是参考还是目标,并从头开始训练它们。 这就是所谓的少数镜头学*问题。*
*在传统迁移学*中,参考模型通常是一个广泛和/或深入的网络,在许多示例/类上进行了预训练,而目标是一个较窄/较浅的网络,在少数可用的特定示例/类上进行了训练。
这个知识提炼方案的损失看起来像

二元交叉熵损失只是常规的二元分类损失,第二项涉及目标 f(x) 和参考 g(x)的输出之间的另一个损失 D 。我们设 D 为 f(x) 和g(x)之间的 Kullback-Leibler 散度 (DKL)

DKL 简单地量化了一个分布 f 与 g、在信息(大致信息与确定性成反比)方面的差异;可以认为是分布之间的交叉熵,是可以取负值的不对称损失。通过最小化 f 和 g 之间的 DKL,我们基本上想要增加 f 相对于 g 的信息含量。当 f 和 g 的信息量相同时,上面的对数项为 0,DKL 损失也为 0。当使用 GPC 作为参考模型时,使用 DKL 作为损失是有意义的,因为当从 GPC 进行预测时,我们从它的后验分布(softmax)进行采样,尽管我们的神经网络是这个后验分布的粗略*似,但它也是一个分布。*
*在下面的实现中,我们在组合损失中采用 abs( D_KL( f(x),g(x))。理论上,由于对数和不等式,D_KL 将总是非负的,但是在实际计算机上用舍入误差计算 D_KL 会导致负值。
请注意,我们现在需要将外部输入 g(x) 反馈到我们的损耗中。在 Keras 中,这个过程是人为的,不可扩展的。但在 TF 中,它就像创建一个新的占位张量、向组合损失添加必要的项,以及在运行训练或预测会话时输入一样简单。
由于只有 5 个训练样本,上述示例比 DKL 损失被设置为零(即没有迁移学*)的网络收敛得更快,并且给出更好的原始测试准确度。注意,测试集不平衡未被考虑!精明的读者应该在准确度读数上加上 F1 分数。谢天谢地,我们数据集的伪随机样本给出了正类与负类的 2:3 比例。
同样值得注意的是,迁移学*模型的 softmax 输出在来自维持者的 100 个示例上进行了测试:
*[0.5134167 ]
[0.5767678 ]
[0.5299671 ]
[0.529941 ]
[0.51807505]
[0.4615707 ]
[0.61761355]
[0.5744497 ]
[0.6092696 ]
[0.55092424]
[0.5866923 ]
[0.5522269 ]
[0.5679551 ]*
相对于非迁移学*模式
*[0.44836044]
[0.99457294]
[0.47165167]
[0.573235 ]
[0.94637066]
[0.00909297]
[0.99778605]
[0.99487936]
[0.99742365]
[0.96588767]
[0.99646676]
[0.9843067 ]
[0.99225134]*
迁移学*模型的预测反映了的不确定性,给出了它被训练的有限信息。选择 GPC 作为参考模型有一个很好的理由,毕竟,当我们只从零开始训练 5 个样本的分类器时,我们怎么能如此确定一个新患者患有癌症?
结论
我们看到了如何在 TF 中从零开始实现神经网络,如何将张量运算结合到损失函数中,并触及了迁移学*的一个有趣应用。总的来说,在高级或特殊的监督学*应用中,TF 对于数据科学家来说要灵活得多。如果你喜欢阅读这篇文章和使用代码,请查看我的其他文章!
在 Python 中使用自定义转换器的 ML 数据管道

如何使用继承和 sklearn 为机器学*预处理编写自己的定制转换器和管道
大多数数据科学项目花费的总时间的 80%花费在清理和预处理数据上。我们都听说过,对吧?因此,我们必须找到尽可能自动化预处理和清理的方法。
Scikit-Learn 管道由多个步骤组成,每个步骤都必须是某种转换器,除了最后一个步骤,它可以是转换器或估计器,如机器学*模型。当我说变压器时,我指的是变压器,例如规格化器、标准定标器或单热编码器等等。但是,假设在我使用其中任何一个之前,我想编写 Scikit-Learn 没有提供的我自己的自定义转换器,它将使用我提供的权重向量作为参数,获取我的数据集中第 3、第 7 和第 11 列的加权平均值,用结果创建一个新列并删除原始列,该怎么办?除此之外,最重要的是,如果我还想让我的定制转换器与我现有的 Scikit-Learn 管道及其其他转换器无缝集成,该怎么办?听起来很棒,对我们来说很幸运,Scikit-Learn 允许我们这样做。
Python 中的继承
为了理解我们如何用 scikit-learn 编写我们自己的定制转换器,我们首先必须稍微熟悉一下 Python 中的继承概念。scikit-learn 中的所有转换器和估算器都是作为 Python 类实现的,每个类都有自己的属性和方法。所以每次你写像这样的 Python 语句-
您实际上是在使用类“OneHotEncoder”的类构造函数创建一个名为“one_hot_enc”的实例,并为其参数“sparse”传递参数“False”。OneHotEncoder 类具有诸如“fit”、“transform”和“fit_transform”等方法,现在可以在我们的实例上使用适当的参数调用这些方法,如下所示。
为了让我们的自定义转换器与 scikit-learn 管道兼容,它必须作为具有 fit、transform、fit_transform、get_params、set_params 等方法的类来实现,因此我们要编写所有这些方法……或者我们可以简单地编写我们希望我们的转换器应用的转换类型,然后从某个其他类继承所有其他内容!
要理解 Python 中继承的概念,请看下面这个波巴·费特的乐高进化。

比如我想写一个看起来像右边乐高积木的类。我完全可以从最左边开始,通过写我自己的方法来建立自己的路。然而,如果我能从我正在努力做的那个后面的那个开始呢?我不需要从头开始,我已经有了我需要的大部分方法,而不需要自己编写它们。我可以对它进行添加或修改,直到完成我需要它做的事情。那不是很好吗?这正是遗传允许我们做的。
当我们在下面写出自己的变形金刚时,这个概念会变得更加清晰。如果您想在继续学*之前对 Python 中的类和继承有更多的了解,请查看下面的链接。
Python 是一种面向对象的编程语言。与面向过程的编程不同,面向过程的编程主要强调…
www.programiz.com](https://www.programiz.com/python-programming/class) [## Python 继承
继承是面向对象编程中的一个强大特性。它指的是定义一个新的类,很少或没有…
www.programiz.com](https://www.programiz.com/python-programming/inheritance)
所以现在你可能在想,那太好了!但是我在哪里可以找到这些基类呢?这些基类包含了我需要用来编写 transformer 类的大多数方法。不要担心。Scikit-Learn 为我们提供了两个很棒的基类, TransformerMixin 和 BaseEstimator 。从 TransformerMixin 继承确保了我们所需要做的就是编写我们的 fit 和 transform 方法,并且我们可以免费获得 fit_transform。从 BaseEstimator 继承确保我们免费获得 get_params 和 set_params。由于 fit 方法除了返回对象本身之外不需要做任何事情,所以在从这些类继承之后,我们真正需要做的是为我们的自定义转换器定义转换方法,这样我们就获得了一个功能完整的自定义转换器,它可以与 scikit-learn 管道无缝集成!简单。
说明
我将在这个插图中使用的数据集可以通过这个链接在 Kaggle 上找到。
用回归法预测房价
www.kaggle.com](https://www.kaggle.com/harlfoxem/housesalesprediction)
此图的目标是通过编写我们自己的自定义转换器和管道所涉及的步骤,对数据进行预处理,直到数据被输入到机器学*算法中,以训练模型或进行预测。对于这个特殊的问题,可能有比在这个例子中描述的更好的方法来设计特性,因为我不关注这些特殊特性的有效性。此图的目的是让读者熟悉可用于创建转换器和管线的工具,以便尽可能高效地针对任何数据集以任何方式设计和预处理要素。
那我们开始吧。
该数据集包含分类和数字独立变量的混合,我们知道这些变量需要以不同的方式分别进行预处理。这意味着最初它们必须通过单独的管道进行适当的预处理,然后我们将它们组合在一起。因此,两个管道的第一步都必须提取需要下推进行预处理的适当列。
编写一个类并让 Python 知道它继承自一个或多个类的语法如下图所示,因为对于我们编写的任何类,我们都可以从 TransformerMixin 和 BaseEstimator 基类继承大部分内容。

下面是我们第一个名为 FeatureSelector 的自定义转换器的代码。这个构造函数的 transform 方法只是提取并返回 pandas 数据集,其中只包含那些在初始化过程中作为参数传递给它的列名。
如您所见,我们在声明类时将 BaseEstimator 和 TransformerMixin 放在括号中,让 Python 知道我们的类将从它们继承。像我们要写的所有构造函数一样,fit 方法只需要返回 self。transform 方法是我们真正要写的,让转换器做我们需要它做的事情。在这种情况下,它仅仅意味着返回一个只包含所选列的 pandas 数据框。
既然已经编写了将在两个管道中处理第一步的构造函数,我们就可以编写将在相应管道中处理其他步骤的转换器,从处理分类特性的管道开始。
分类管道
下面是我们的定制转换器将处理的功能列表,以及在我们的分类管道中如何处理。
- date :此列中的日期格式为‘yyyymmddt 000000’,必须进行清理和处理才能以任何有意义的方式使用。这个转换器的构造函数将允许我们为参数“use_dates”指定一个值列表,这取决于我们是想为年、月、日或这些值的某种组合创建一个单独的列,还是简单地通过传入一个空列表来完全忽略该列。通过不对该特性的规范进行硬编码,我们可以随时尝试不同的值组合,而不必重写代码。
- 滨水:房屋是否为滨水物业。转换为二进制—是或否
- 查看:房子被查看了多少次。大部分值都是 0。其余的非常稀疏地分布在 1 到 4 之间。转换为二进制—是或否
- yr _ recruited:房子装修的年份。大多数值都是 0,可能永远不会,而其余的值在几年之间分布得很稀疏。转换为二进制—是或否
一旦我们的定制转换器以上述方式处理了所有这些特性,它们将被转换成一个 Numpy 数组,并被推送到分类管道中的下一个也是最后一个转换器。一个简单的 sci kit-了解一个热编码器,它返回我们预处理数据的密集表示。下面是我们定制的转换器的代码。
数字流水线
下面是我们的自定义数值转换器将处理的特性列表,以及如何在我们的数值管道中处理。
- 卧室:房子里卧室的数量。照原样通过。
- 卫生间:房屋内卫生间数量。这个转换器的构造函数将有一个参数“bath_per_bead ”,它接受一个布尔值。如果为真,那么构造函数将通过计算浴室/卧室来创建一个新列,以计算每个卧室的浴室数量,并删除原始的浴室列。如果为 False,那么它将原样通过浴室列。
- sqft_living :房屋居住面积大小,平方英尺。照原样通过。
- sqft_lot :以平方英尺为单位的总批量。照原样通过。
- 楼层:房屋的层数。照原样通过。
- 条件:描述房屋条件的离散变量,取值范围为 1-5。照原样通过。
- 等级:根据 King County 等级系统给出的住房单元的总体等级,值为 1-13。照原样通过。
- sqft_basement :房屋内的地下室面积,以平方英尺为单位(如有)。没有地下室的房屋为 0。照原样通过。
- ****yr _ build:房子建造的年份。这个转换器的构造函数将有另一个参数‘years _ old ’,它也接受一个布尔值。如果为真,则构造函数将通过从 2019 年减去房屋建造年份来计算 2019 年的房屋年龄,从而创建一个新列,并将删除原始的 yr _ built 列。如果为 False,那么它将直接传递 yr _ built 列。
一旦所有这些特性都由我们的自定义数值转换器在上述数值管道中处理,数据将被转换为 Numpy 数组,并传递到数值管道的下一步,即另一种 scikit-learn 转换器。估算器将计算列中值,并用适当的中值填充任何 Nan 值。从那里,数据将被推送到数字管道中的最终转换器,一个简单的 scikit-learn 标准定标器。下面是自定义数值转换器的代码。
将管道组合在一起
现在,我们已经编写了数字和分类转换器,并定义了我们的管道,我们需要一种方法来横向组合它们。我们可以使用 scikit-learn 中的 FeatureUnion 类来实现。我们可以在 Python 中创建一个特征联合类对象,方法是给它两个或更多由转换器组成的管道对象。为要素联合对象调用 fit_transform 方法会将数据分别推入管道,然后将结果合并并返回。在我们的例子中,因为我们的两个管道的第一步是为每个管道提取适当的列,所以使用特征联合将它们组合起来,并在整个数据集上拟合特征联合对象,这意味着适当的列集将被下推到适当的管道集,并在它们被转换后组合在一起!是不是很牛逼?
我还没告诉你最精彩的部分呢。它将为我们并行计算!没错,它会并行转换数据,并将其重新组合在一起!因此,它很可能比任何处理这种线性预处理的脚本都要快,因为它很可能需要更多的并行工作。我们再也不用担心手动操作了。我们的 FeatureUnion 对象将尽可能多次地处理这个问题。我们所要做的就是在我们的完整特性联合对象上调用 fit_transform。下面是使用我们的自定义转换器和其他工具创建两个管道,然后将它们组合在一起的代码。
现在你可能已经注意到,我没有在完整的管道中包括任何机器学*模型。原因是我不能。FeatureUnion 对象接受仅包含转换器的管道对象。机器学*模型是一个估计器。解决方法是,我可以创建另一个管道对象,第一步传递完整的管道对象,最后一步添加一个机器学*模型。完整的预处理数据集将是第一步的输出,它将简单地传递到我的模型中,使它像您可能编写的任何其他 scikit-learn 管道一样工作!这是代码。
我们只需在未处理的数据集上拟合管道,它会使用我们构建的工具自动进行所有预处理和拟合。适当的柱子被分开,然后它们被推下适当的管道,在那里它们各自通过 3 或 4 个不同的变压器(总共 7 个!)使用我们决定的参数,将预处理的数据放回一起,并向下推至模型进行训练!调用 predict 对未处理的测试数据帧做同样的事情,并返回预测!这是我做的一个简单的图表,展示了我们机器学*管道的流程。

Simple flow diagram for our pipeline
除了因为我们的 transformer 类继承了 TransformerMixin 类而免费获得的 fit_transform 之外,我们还为我们的 transformer 提供了 get_params 和 set_params 方法,而无需编写它们,因为我们的 transformer 类也继承了 BaseEstimator 类。
这些方法会派上用场,因为我们编写转换器的方式允许我们通过为参数(如 use_dates、bath_per_bed 和 years_old)提供不同的参数来操纵数据的预处理方式。仅仅使用简单的乘积规则,我就可以为预处理部分的数据尝试大约 108 个参数组合!我可以使用 set_params 来设置它,而无需重新编写一行代码。由于这个管道的功能类似于任何其他管道,所以我也可以使用 GridSearch 来调优我打算与之一起使用的任何模型的超参数!
这就是了。现在,您知道了如何在自己的机器上编写自己的全功能定制转换器和管道来自动处理任何类型的数据,使用一点 Python 魔法和 Scikit-Learn 就可以实现这一点。显然还有改进的空间,比如验证数据是否是您期望的形式,在数据到达管道之前来自数据源,并让转换器能够处理和报告意外错误。然而,仅仅使用本文中的工具应该会使您的下一个数据科学项目更有效率,并允许您自动化和并行化一些繁琐的计算。
如果我遗漏了什么,或者有什么不准确的地方,或者如果你有任何反馈,请在评论中告诉我。我将非常感激。谢谢你。
Python 中的自定义转换器和管道
数据清理是任何机器学*项目中最重要的部分。事实上,您的数据可能有多种格式,并且分布在不同的系统中,这使得在将数据提供给 ML 模型之前,必须对其进行适当的处理。数据准备是 ML 流程中最繁琐和耗时的步骤之一(一些调查显示,数据科学家将 80%的时间花在数据准备和管理上!).然而,这也是最关键的一步,因为输入数据是模型的主要成分。Python 提供了某些包,这些包提供了不同的工具来简化数据准备过程,其中一个解决方案是使用自定义转换器和管道。在本文中,让我们看看什么是定制转换器,然后深入研究如何在管道中为均值编码和衬衫尺码编码定制转换器。
变形金刚
如果你研究过机器学*问题,你可能知道 Python 中的 transformers 可以用来清理、减少、扩展或生成要素。fit 方法从训练集中学*参数,transform 方法将变换应用于看不见的数据。预定义转换器的示例包括 StandardScaler、LabelEncoder、Normalize 等。在本文中,让我们来探索定制变压器。
为什么使用定制变压器
在不同的 Python 包中有几个预定义的转换器,允许我们轻松地对数据集应用不同的转换。那么,为什么我们需要定制的变压器呢?根据要解决的问题,自定义转换器可以帮助克服预定义转换器带来的一些无能或挑战。例如,让我们考虑标签编码。比方说,您试图用可能的值{East,Central,West}对列“Region”进行标签编码。如果训练数据集不包含区域列值为“中心”的任何数据,那么 LabelEncoder 无法处理测试或验证数据集中出现“中心”
时的转换。
带管道的定制变压器的应用
自定义变压器可用于顺序应用多个变压器的管道中。一个转换器的输出作为输入馈入下一个转换器,您可能会面临不兼容类型的挑战,即第一个转换器可能会输出 NumPy 数组,而后续的转换器可能会将数据帧作为输入。在这种情况下,定制变压器可以挽救局面。在我们进入编码细节之前,让我们快速刷新一下管道、衬衫尺码和编码的概念。
“管道表示一系列级联的数据转换。管道中的每一步都来自前一步,数据从头到尾流经管道。这有助于优化模型构建过程,从而有更多的时间来理解底层数据。定制变压器可用于均值编码和衬衫尺码的流水线。
衬衫尺码,以其最简单的形式,包括创建箱子并给每个箱子分配一个标签,如小号、中号、大号、超大号,因此称为衬衫尺码。通常,当我们考虑数字变量的转换器时,我们默认使用标准标量或对列应用对数转换。这与衬衫尺寸形成对比,衬衫尺寸允许您从数值变量创建分类变量,并且通常遵循其他方法,如均值编码,然后生成更有意义的特征。
类似地,对于转换分类变量,我们通常考虑标签编码或 One Hot。标签编码为数据分配随机值,这可能会使我们的模型误以为某列中的数据具有显式的层次结构,而实际情况可能并非如此。为了避免这种情况,我们倾向于“一次热编码”列。这可能会给我们留下一个包含太多列的结果数据集。例如,如果我们为一个“State”列创建一个热编码,我们可能最终得到 50 列,这不仅处理起来很繁琐,而且可能没有意义。克服上述挑战的一种方法是使用均值编码。均值编码也是分类变量的转换器,其中我们用目标列取一列的均值。如果我们的目标变量是一个二进制变量(分类问题),均值编码可以很好地工作,但它也有自己的缺点,这是另一个讨论的主题。正如大多数数据科学问题一样,没有一个解决所有问题的最佳解决方案。
对管道中的自定义转换器进行编码
我们使用的数据集是 Kaggle 的“家庭信用违约风险”,可以从这里下载。为了简单起见,我们的代码只检查应用程序数据——“application _ { train | test }”。csv”。它侧重于衬衫尺寸的“AMT_INCOME_TOTAL”列和平均值编码的短列“AMT_INCOME_TOTAL”和“CODE_GENDER”。
首先,导入所需的包,并从文件 application_train.csv(进入变量 df_app_train )和 application_test.csv(进入变量 df_app_test )中读取数据。然后让我们定义一个定制的转换器。
定义自定义变压器
下面显示的“ColumnSelector”是一个自定义转换器,它从输入数据框提供的列中创建一个子集数据框。自定义转换器是从 BaseEstimator 和 TransformerMixin 类继承而来的,如下所示。“init”是一个构造函数。
类<类名> (BaseEstimator,transformermixin):
Column Selector — Custom Transformer that extracts required columns
合体在这种情况下什么也做不了。但是一般来说, fit 用于获取和存储参数以执行转换(当我们讨论衬衫尺寸时会用到更多)。变换用于使用拟合函数的参数对输入数据集进行变换。这里,它基于输入列创建了一个子集数据框。
定义衬衫尺寸定制转换器
DFShirtSize (在上面的代码中)允许创建和标记箱子。
Fit 对给定的列应用对数变换,并获取给定值的截集(bin)并将它们存储在字典中,我们在上面的代码中称之为“ settings ”。设置是一个全局变量,允许在转换功能中重用。
在应用配合之前,确保系列数据与验证和测试数据分开。拟合函数应用于训练数据,然后转换函数用于训练、测试和验证数据集。存储参数的能力确保测试或验证数据集中没有数据泄漏。为了处理测试和验证数据集中的新值,添加了-inf 和 inf 作为 cuts(全局变量)的第一个和最后一个元素。
转换从设置字典中读取参数,相应地创建库并标记输入列值。这里,输入列箱根据设置字典中的切割创建并标记。
定义均值编码自定义变压器
均值编码使用要素在数据集中出现的次数占所有次数的比例。当测试或验证数据中出现看不见的标签时,目标变量的平均值会替换看不见的标签,以避免在数据中创建 Nan。
创建管道
管道步骤可以定义为一个单独的列表或直接在管道中。在这里,“列选择器”转换器使我们能够只将所需的子集传递给下一步。让我们创建一个将经历不同转换的列的列表,然后为管道安排步骤的列表。
SSEncoding 是一个变形金刚列表。管道对象是由 Pipeline(SSEncoding)命令创建的。一旦创建了管道对象,就可以调用 fit 和 transform 来执行数据转换。在应用 fit 后,所有的转换函数都应用于调用数据集,并获得和存储参数。上面的 fit 命令在 df_app_train 上同时应用 ColumnSelector 和 DFShirtsize。让我们看一个有数据的例子
改造前数据:

转换后的列车数据:

Train Data upon Transformation
转换后的测试数据:

Test Data upon Transformation
我们使用训练数据来拟合和转换训练和测试数据。这有助于避免数据泄露。如下所示,重复进行均值编码。
调用拟合和变换:

Fit

Transform
对测试数据调用转换:

因此,我们将衬衫尺寸应用于数值,以获得分类值。然后对得到的分类值进行均值编码,以准备可以被 ML 算法使用的数据。使用定制的转换器和管道可以在数据准备过程中为我们提供帮助,让我们有更多的时间进行分析和建模——这是我们应该花费大部分时间的地方。
根据来自Dennis Ignatenko和Brian Monteiro的指导,他们都是我数据科学之旅中令人敬畏的导师。
基于 TensorFlow.js 的神经网络客户流失预测
创建深度神经网络模型来预测客户流失

TL;DR 了解深度学*并使用 TensorFlow.js 创建深度神经网络模型来预测客户流失。了解如何预处理字符串分类数据。
第一天!你在一家大型电信公司获得了这份数据科学家的实*工作。一旦你成为一名高级数据科学家,你就不能停止梦想你将得到的兰博基尼和名牌服装。
连你妈妈都打电话来提醒你把你的统计学博士文凭挂在墙上。这就是生活,谁在乎你 35 岁左右,这是你的第一份工作。
你的团队领导走过来,问你喜欢这份工作吗,并说他可能有任务给你!你开始想象从零开始实现复杂的统计模型,做研究,并添加尖端的方法,但…嗯,现实略有不同。他向您发送了一个 CSV 失败的链接,并要求您预测客户流失。他建议你可以尝试应用深度学*来解决这个问题。
你的梦想现在开始了。该做些工作了!
在你的浏览器中运行本教程的完整源代码:
客户流失数据
我们的数据集电信客户流失来自 Kaggle 。
“预测行为留住客户。您可以分析所有相关的客户数据,并制定有针对性的客户维系计划。”【IBM 样本数据集】
数据集包括以下信息:
- 上个月内离开的客户—这一列称为流失
- 每位客户已注册的服务—电话、多条线路、互联网、在线安全、在线备份、设备保护、技术支持以及流媒体电视和电影
- 客户账户信息——他们成为客户的时间、合同、支付方式、无纸化账单、每月费用和总费用
- 客户的人口统计信息—性别、年龄范围,以及他们是否有伴侣和家属
它有 7,044 个示例和 21 个变量:
- 客户 ID :客户 ID
- 性别:顾客是男性还是女性
- 老年人:客户是否为老年人(1,0)
- 合作伙伴:客户是否有合作伙伴(是,否)
- 家属:客户是否有家属(是,否)
- 任期:客户在公司呆的月数
- 电话服务:客户是否有电话服务(是,否)
- 多线:客户是否有多线(是,否,无电话服务)
- 互联网服务:客户的互联网服务提供商(DSL、光纤、No)
- 在线安全:客户是否有在线安全(是,否,无互联网服务)
- 在线备份:客户是否有在线备份(是,否,无互联网服务)
- 设备保护:客户是否有设备保护(是,否,无互联网服务)
- 技术支持:客户是否有技术支持(是,否,无互联网服务)
- 流媒体电视:客户是否有流媒体电视(是,否,无互联网服务)
- 流媒体电影:客户是否有流媒体电影(是,否,无互联网服务)
- 合同:客户的合同期限(逐月、一年、两年)
- 无纸开票:客户是否无纸开票(是,否)
- 付款方式:客户的付款方式(电子支票、邮寄支票、银行转账(自动)、信用卡(自动))
- 月费:每月向客户收取的金额
- 总费用:向客户收取的总费用
- 客户流失:客户是否流失(是或否)
我们将使用 Papa Parse 来加载数据:
注意,我们忽略了最后一行,因为它是空的。
探测
让我们感受一下我们的数据集。有多少顾客呕吐了?

大约 74%的客户仍在使用该公司的服务。我们有一个非常不平衡的数据集。
性别在流失客户中起作用吗?

似乎没有。我们的女性和男性顾客数量差不多。资历如何?

大约 20%的客户是老年人,与非老年人相比,他们更有可能流失。
客户会在公司呆多久?

似乎你呆得越久,就越有可能留在公司。
月费如何影响流失率?

月租费低($30)的客户更有可能被留住。
每位顾客的总费用是多少?

公司收取的总金额越高,就越有可能留住这个客户。
我们的数据集总共有 21 个要素,我们没有全部看完。然而,我们发现了一些有趣的东西。
我们已经了解到,老年人、任期、每月费用和总费用在某种程度上与流失状况相关。我们将在我们的模型中使用它们!
深度学*
深度学*是机器学*的一个子集,使用深度人工神经网络作为主要模型来解决各种任务。
为了获得深度神经网络,取一个具有一个隐藏层的神经网络(浅层神经网络),并添加更多层。这就是深度神经网络的定义——具有不止一个隐藏层的神经网络!
在深度神经网络中,每层神经元都在前一层的特征/输出上进行训练。因此,您可以创建一个不断抽象的特性层次结构,并学*复杂的概念。
这些网络非常善于发现原始数据(图像、文本、视频和音频记录)中的模式,这些数据是我们拥有的最大量的数据。例如,深度学*可以拍摄数百万张图像,并将其分类为你奶奶的照片、有趣的猫和美味的蛋糕。
深度神经网络是在各种重要问题上拥有最先进的分数。例子有图像识别、图像分割、声音识别、推荐系统、自然语言处理等。
所以基本上,深度学*是大型神经网络。为什么是现在?为什么深度学*之前不实用?
- 大多数深度学*的现实应用需要大量的标记数据:开发一辆无人驾驶汽车可能需要数千小时的视频。
- 训练具有大量参数(权重)的模型需要强大的计算能力:GPU 和 TPU 形式的专用硬件提供大规模并行计算,适合深度学*。
- 大公司储存你的数据已经有一段时间了:他们想利用这些数据赚钱。
- 我们学*了(有点)如何初始化神经网络模型中神经元的权重:主要是使用小的随机值
- 我们有更好的正规化技术(例如辍学)
最后但并非最不重要的是,我们有高性能且(有时)易于使用的软件。像 TensorFlow 、 PyTorch 、 MXNet 和 Chainer 这样的库允许从业者开发、分析、测试和部署不同复杂性的模型,并重用其他从业者和研究人员所做的工作。
预测客户流失
让我们使用“全能”的深度学*机器来预测哪些客户会流失。首先,我们需要做一些数据预处理,因为许多特征是分类的。
数据预处理
我们将使用所有数字(除了 customerID )和以下分类特征:
让我们从数据中创建训练和测试数据集:
以下是我们创建张量的方法:
首先,我们使用函数toCategorical()将分类特征转换成独热码编码向量。我们通过将字符串值转换成数字并使用 tf.oneHot() 来创建向量。
我们从我们的特征(分类的和数字的)中创建一个 2 维张量,然后将其归一化。另一个,一个热编码,张量是由流失列。
最后,我们将数据分成训练和测试数据集,并返回结果。我们如何编码分类变量?
首先,我们提取特征的所有值的向量。接下来,我们获取唯一值,并从中创建一个 string 到 int 的映射。
注意,我们检查丢失的值,并将它们编码为 0。最后,我们一次性编码每个值。
以下是剩余的效用函数:
构建深度神经网络
我们将把模型的构建和训练打包到一个名为trainModel()的函数中:
让我们使用 TensorFlow 中的顺序模型 API 创建一个深度神经网络:
我们的深层神经网络有两个隐藏层,分别有 32 个和 64 个神经元。每层都有一个 ReLU 激活功能。
是时候编译我们的模型了:
我们将使用亚当优化器训练我们的模型,并使用二元交叉熵测量我们的误差。
培养
最后,我们将训练数据传递给我们模型的 fit 方法,并训练 100 个时期,混洗数据,并使用其中的 10% 进行验证。我们将使用 tfjs-vis 可视化培训进度:
让我们训练我们的模型:


看起来我们的模型在前十个时期都在学*,在那之后就稳定下来了。
估价
让我们根据测试数据评估我们的模型:
Tensor 0.44808024168014526
Tensor 0.7929078340530396
该模型对测试数据的准确率为 79.2%。让我们看看使用混淆矩阵会犯什么样的错误:

似乎我们的模型在预测保留客户方面过于自信了。根据您的需要,您可以尝试调整模型,更好地预测保留的客户。
结论
干得好!您刚刚构建了一个深度神经网络,它可以以大约 80%的准确率预测客户流失。以下是你学到的东西:
- 什么是深度学*
- 浅层神经网络和深层神经网络有什么区别
- 预处理字符串分类数据
- 在 TensorFlow.js 中构建和评估深度神经网络
但是深度学*会不会更厉害呢?强大到能看懂图像?
在浏览器中运行本教程的完整源代码:
参考
原载于https://www.curiousily.com。
建立机器学*模型(特别是深度神经网络),可以轻松地与现有或新的 web 应用程序集成。想想您的 ReactJs、Vue 或 Angular 应用程序通过机器学*模型的强大功能得到了增强:
建立机器学*模型(特别是深度神经网络),您可以轻松地与现有或新的网络集成…
leanpub.com](https://leanpub.com/deep-learning-for-javascript-hackers)
在 IBM Watson Studio、AWS 和 Databricks 上使用 PySpark 进行客户流失预测
使用大数据工具和云计算服务预测数字音乐服务的客户流失

Image taken from www.thedj.co.uk
客户流失指的是客户停止与一家公司做生意的情况。
根据《哈佛商业评论》的文章,获得一个新客户比留住一个现有客户要贵 5 到 25 倍。
事实上,贝恩公司的 Frederick Reichheld 进行的一项研究表明,将客户保持率提高 5%可以增加 25%到 95%的利润。
因此,降低客户流失率应该是公司的首要任务。如果我们能够提前成功预测客户流失,我们可以通过提供折扣和激励来吸引他们留下来。
在本文中,我们将解决一个名为 Sparkify 的虚拟数字音乐服务的客户流失预测问题。我们将在客户在服务上的活动的大型数据集(约 12GB)上训练我们的预测模型,并尝试根据客户过去的行为预测他们将流失的客户。
由于这个数据集太大,无法在单台计算机上运行,我们将使用 Apache Spark 来帮助我们分析这个数据集。
Apache Spark 是世界上最流行的大数据分布式处理框架(使用多台计算机)之一。Spark 比 Hadoop MapReduce 快得多,它有一个用户友好的 API,可以通过许多流行的语言访问:Scala、Java、Python 和 r。
对于这个任务,我们将使用 Spark 的 Python API py Spark。
PySpark 提供了两种操作数据帧的方法:第一种类似于 Python 的 Pandas 库,另一种使用 SQL 查询。
Spark ML 是 Spark 机器学*库的基于数据框架的 API,它为用户提供了流行的机器学*算法,如线性回归、逻辑回归、随机森林、梯度增强树等。
在本地计算机上设置 PySpark 可能会很棘手。在云服务上运行 PySpark 将简化设置过程,但可能会产生一些成本。
理想情况下,您应该在本地机器上用较小的数据集创建模型的原型以节省成本,然后在准备好分析较大的数据集时将代码转移到基于云的服务。最终,您将需要一个云服务来充分利用 Spark 的分布式计算框架。
另一种方法是用 Databricks 建立一个帐户,并使用他们的社区版来构建你的模型原型。Databricks 社区版提供免费的 6GB 微集群以及集群管理器和笔记本环境。最棒的是访问不受时间限制!
如果您对复制代码或尝试数据集感兴趣,我已经包括了三个云服务的设置说明,IBM Studio Watson、Amazon AWS 和 Databricks。
问题的简要描述
Sparkify 有一个免费层和一个高级订阅计划,客户可以随时取消或从高级降级到免费层。
我们将客户流失事件定义为从高级降级到免费等级或取消服务。
理想情况下,我们希望使用过去的数据来预测客户流失事件,以避免任何前瞻性偏见。有两个月的客户活动数据可用。因此,我们将建立一个模型,利用用户第一个月的行为数据来预测用户在第二个月是否会流失。
完整的数据集为 12 GB。或者,您可以尝试数据集的较小实例,我已经在我的 GitHub 页面上包含了数据集的下载链接。
我现在将介绍三种云计算服务的设置说明:IBM Watson Studio、AWS 和 Databricks。如果你愿意,可以跳过它们,继续阅读“数据集”。
设置 IBM Watson Studio
设置和运行 Spark 最简单的方法之一是通过 IBM Watson Studio 平台。它有一个用户友好的界面,并有一个免费的“建兴计划”。
您将获得每月 50 个容量单位的“精简计划”。默认的 Spark Python 3.5 环境每小时消耗 1.5 个容量单位,这样您就有大约 33 个小时来处理一个项目。
要设置 IBM Watson Studio,您需要注册一个 IBM Cloud 帐户,如果您还没有的话。
接下来,登录 IBM Watson Studio 主页页面并登录。

Select log in
登录后,您将被带到此页面。选择“创建项目”。

Select “Create a project”
接下来,悬停在“数据科学”周围,并单击“创建项目”。

Select “Data Science” and click “Create Project”
输入项目名称,然后选择“创建”。

Enter a project name and select “Create”
选择“添加到项目”。

Select “Add to project”
为您的资产类型选择“笔记本电脑”。

Select “Notebook”
给笔记本起个名字,选择“默认 Spark Python 3.5 xs(1 个 vCPU 和 4 GB RAM 的驱动,2 个各 1 个 vCPU 和 4 GB RAM 的执行器)”。接下来,单击“创建笔记本”。

这将创建一个新的笔记本,您可以在那里开始编码。

要插入数据文件,选择右上角的“查找和添加数据”图标。只需将您想要的数据文件拖放到框中。

要创建新的 Spark 会话并读入数据文件,选择“插入代码”并点击“插入 Spark Session 数据帧”。

这将生成一个预写单元。

Uncomment the last two lines to read in the data frame
您可以取消最后两行的注释,以读取数据文件。请随意更改数据框的名称。

I uncommented the last 2 lines and changed the name of the data frame from df_data_1 to df
您现在可以构建您的项目了!

This is what you will see after you run the first cell
如果您当前的群集因任何原因终止,您可以随时将先前存在的笔记本重新连接到新的群集,并继续您的工作。
完成项目后,您应该停止您的环境并删除您的笔记本和数据文件,以避免任何意外费用。
要停止您的环境,请单击项目页面顶部的“环境”选项卡。单击活动环境右侧的三个点,然后选择“停止”。

Select “Stop” to stop your environment
接下来,转到项目页面顶部的“资产”选项卡。点击数据文件右边的三个点,选择“删除”。对你的笔记本也重复这个步骤。

“Remove” your data file. Do the same for your notebook as well
您可以通过选择“管理”,然后选择“计费和使用”来查看您的计费信息。

To check your billing details
在 IBM Watson Studio 平台上使用 Spark 的一个优点是,它预装了常用的库,如 Pandas 和 Matplotlib 等。这与 AWS EMR 服务形成对比,后者没有预安装这些库。
如果您的 IBM 托管笔记本停止运行或您的互联网浏览器崩溃(是的,它确实发生了),这可能意味着您当前在 IBM Watson Studio 上的设置没有足够的内存来处理这项任务。您可能需要选择一个更简单的模型,对数据集进行降维,或者购买付费计划来访问一个更强大的实例。
设置 Amazon AWS
我现在将分享 Amazon Web Services Elastic MapReduce(EMR)的设置说明。
开始吧。你需要注册一个 AWS 账户。注册时,您需要提供一张信用卡,但不会向您收取任何费用。
您需要选择一个支持计划,免费的基本支持计划应该足够了。
接下来,进入亚马逊 EMR 控制台,点击“亚马逊 EMR 入门”。使用您的帐户登录后,您就可以创建集群了。

Click “Get Started with Amazon EMR”
在右上角选择合适的位置(选择离你最*的一个)。在左侧菜单中选择“集群”,然后单击“创建集群”。

Location, on the top right hand corner, is currently Ohio. Change this to the most appropriate location. Next you can create your clusters.
使用以下设置配置您的群集:
- 版本:emr-5.20.0 或更高版本
- 应用程序::Spark 2.4.0 在 Hadoop 2.8.5 YARN 上,带有 Ganglia 3.7.2 和 Zeppelin 0.8.0
- 实例类型:m3.xlarge
- 实例数量:6
- EC2 密钥对:没有 EC2 密钥对也可以使用,如果你愿意的话
如果您想按原样运行代码,建议您使用 m3.xlarge 的 6 个实例。当然,您可以尝试更少的实例数(例如 3 个)。但是,如果您遇到诸如“会话不活动”之类的错误,这可能意味着您当前的集群设置没有足够的内存来执行该任务。您将需要创建一个具有较大实例的新集群。
其余的设置可以保持默认值,您可以通过单击“创建集群”来完成设置。
**
This picture shows the setup with 4 instances. You are recommended to use 6 instances for this project to avoid memory issues.

You will get a similar error message when running your code if your cluster has insufficient memory for the task
接下来,您将进入一个页面,显示集群正在“启动”。几分钟后,状态将变为“正在运行”。最后会改成“等待”。整个过程可能需要 3 到 10 分钟。此时,您可以进入下一步。

The first status will state that the cluster is “Starting”

Next, the status will change to “Running”

Finally, it will change to “Waiting”. At this point, you can move on to the next step
最后,你可以创建你的笔记本。
- 在左侧菜单中选择“笔记本”。
- 为您的笔记本命名
- 选择“选择现有集群”,然后选择您刚刚创建的集群
- 使用“AWS 服务角色”的默认设置—如果您以前没有这样做过,应该是“EMR_Notebooks_DefaultRole”或“创建默认角色”。
- 您可以保持其他设置不变,然后单击右下角的“创建笔记本”

接下来,等待笔记本的状态从“正在启动”或“待定”变为“就绪”。此时,您可以“打开”笔记本。

现在,您可以开始编码了。
下面提供了创建新的 Spark 会话并读取完整数据集的起始代码:
**# Starter code* **from** pyspark.sql **import** SparkSession*# Create spark session* spark = SparkSession \
.builder \
.appName("Sparkify") \
.getOrCreate()*# Read in full sparkify dataset* event_data = "s3n://dsnd-sparkify/sparkify_event_data.json"df = spark.read.json(event_data)df.head()*
完整的数据集位于:s3n://dsnd-sparkify/sparkify_event_data.json
这是运行代码后您将看到的内容。

如果您终止了群集并停止了笔记本,并且想要重新运行笔记本,则可以创建一个新的群集,并将笔记本重新连接到新的群集。
按照之前给出的说明设置新的集群,然后返回左侧菜单中的“笔记本”选项。单击您已创建的现有笔记本,选择“更改集群”并选择新创建的集群。最后选择“更改集群并启动笔记本”。

To re-connect your notebook to a new cluster, click on your existing notebook

Select “Change cluster”

Select the newly created cluster with the “Choose” button located under the “Choose an existing cluster” option. Finally select “Change cluster and start notebook”
为了避免 AWS 上的任何意外费用,请在完成分析后终止集群并删除笔记本。您可以在左侧菜单的“集群”和“笔记本”选项中检查这一点。
如果您在多个位置设置了集群,请确保检查所有这些位置,因为每个位置都有自己的集群列表!

Ensure all your clusters are terminated when you are done with your analysis
您可以在您的帐户名称下的“我的账单面板”选项下查看您的账单详情。

在 Amazon EMR 上运行代码时,您可能会遇到以下错误或异常:
- type error:“NoneType”类型的对象没有 len()
- KeyError: 14933 ( 数字可能不同)
如果您看到这些错误,请不要担心。通常,它们并不意味着您的代码有问题,它们的出现不会影响您代码的执行。如果在执行代码块时出现这些错误,可以忽略这些错误信息。代码仍将被执行。

An example of error #1

An example of error #2
在 AWS EMR 上运行分析花费了我大约 20 美元。这是考虑到我不得不多次重新运行分析的事实,因为我的 AWS 托管笔记本由于内存不足而崩溃(我最初尝试使用 3、4 和 5 个 m3.xlarge 实例)。
因此,如果您从一个足够大的集群开始(对于这个项目,有 6 个 m3.xlarge 实例),您可能能够运行一次代码而不会遇到任何问题。这有助于降低你的成本。
此外,我在本地机器上开发了模型的原型,这有助于降低最终成本。
需要注意的是,AWS EMR 集群没有预装 Scikit-Learn、Pandas、Matplotlib 等库。您应该能够在没有这些库的情况下完成项目,尽管您将无法执行任何数据可视化。
如果您确实想使用这些库,您需要按照这个链接提供的说明来安装它们。这个视频也会有帮助。
这是一个有点复杂的过程,模糊地涉及编写一个 bash 脚本来安装这些库,将脚本上传到 S3 桶,然后用脚本实例化一个集群。诚然,在撰写本文时,我还没有尝试过。如果我开始安装这些库,我可能会在将来更新这篇文章。
设置数据块
为了注册 Databricks 帐户,您需要一个 AWS 帐户。Databricks 的平台依赖 AWS 作为云基础设施。你可以在这里注册一个 Databricks 账户。
Databricks 的注册过程比前面提到的其他两个云服务稍长,它涉及到在您的 AWS 帐户和 Databricks 帐户设置页面之间穿梭。
然而,一旦您完成了设置并激活了您的帐户,在 Databricks 中设置工作环境就相对容易了。
此外,社区版为您提供了对 6 GB 集群的免费无限制访问(无需担心时间限制!),使其成为构建模型原型的完美环境。
同样,对于 IBM Watson Studio,Databricks 的工作环境预装了常用的库,如 Pandas、Matplotlib。
一旦你创建了你的账户,请前往 Databricks 社区版的登录页面。

Login to your account
您将看到一个类似的页面。

Home page of Databricks Community Edition
在左侧菜单中选择“集群”。

Select “Clusters”
选择页面左上角的“+创建集群”。

Select the blue “+ Create Cluster” button
为您的集群命名。您可以不修改 Databricks 运行时版本。选择要使用的 Python 版本。我为我的项目选择了 Python 3。您可以将“可用性区域”字段留空。完成后,选择“创建集群”。

等待状态从“待定”变为“正在运行”。

Cluster status is “Pending”

Cluster status is “Running”
接下来,在左侧菜单中选择“工作区”,然后单击“用户”。选择电子邮件旁边的箭头,点击“创建”,然后点击“笔记本”。

Creating a new notebook
为您的笔记本命名,您可以保持其他设置不变。

Give a name to your notebook and select “Create”
接下来,您需要上传数据集。在左侧菜单中选择“数据”,然后点击“添加数据”。
只需将您想要上传的文件拖放到灰色框中。上传文件时不要离开页面,否则你将不得不重新做整个过程。

Drag and drop your desired data file
上传完成后,选择“在笔记本中创建表格”。

这将打开一个带有预写单元的示例笔记本,该单元具有读取数据的代码。复制该单元格中的代码,并返回到您之前创建的原始笔记本,然后将代码粘贴到那里。

The example notebook. Copy the first cell and paste it in the notebook you created earlier
恭喜你,你可以开始编码了!

如果你想保存文件,点击“文件”,然后“导出”,然后“iPython 笔记本”。

如果要将代码单元格更改为降价单元格,请在单元格的第一行键入“%md”。
如果您已终止群集,您可以随时创建新群集,并将现有笔记本重新连接到新群集。
尽管 Community Edition 是一项免费服务,但终止您的集群并删除您的笔记本以避免任何意外的 AWS 费用仍然是一个好主意。您可以通过单击左侧菜单中的“集群”来检查集群的状态。
请注意,如果您选择使用完整的 Databricks 平台而不是社区版,您将会产生 AWS 费用。
资料组
数据集的架构:

数据集中的一行:

在这个数据集中要注意的一个关键列是“page”列。“页面”栏记录了用户访问的页面。
以下是用户可以访问的页面列表:
- 取消:用户已经访问了取消页面。并不意味着取消已完成。
- 提交降级:用户已提交从高级层到免费层的降级
- 拇指向下:用户给了一个拇指向下。
- 主页:用户访问了主页
- 降级:用户访问降级页面。不代表提交降级。
- 滚动广告:播放广告。
- 注销:用户注销。
- 保存设置:用户对设置做了一些更改并保存。
- 取消确认否:用户取消订阅。
- 关于:用户访问了关于页面。
- 提交注册:用户提交注册请求。
- 设置:用户访问设置页面。
- 登录:用户登录。
- 注册:用户访问注册页面。并不意味着注册已经完成。
- 添加到播放列表:用户添加歌曲到播放列表。
- 添加好友:用户添加了一个好友。
- 下一首歌:用户听了一首歌。
- 竖起大拇指:用户竖起大拇指。
- 帮助:用户访问了帮助页面。
- 升级:用户从免费升级到高级等级。
其中,我们应该注意的页面可能是:“下一首歌”,它跟踪用户播放的歌曲,“提交降级”,它跟踪用户何时提交降级请求,以及“取消确认”,它跟踪用户的取消请求何时被确认。
通过搜索“提交降级”或“取消确认”页面,我们可以知道客户何时流失。
请注意,用户可以访问“降级”和“取消”页面,但不能提交降级或取消请求。
其余的页面相对简单。它们表明用户已经访问了相关页面。
如前所述,有 2 个月的数据可用。
数据预处理
根据您使用的云计算服务,读入的数据会略有不同。每个服务在数据集中的读取方法在上面的设置说明中有所分享。
如果您正在读取本地计算机上的数据,这是小型数据集的代码:
**# create a Spark session*
spark = SparkSession.builder \
.master("local") \
.appName("Sparkify") \
.getOrCreate()df = spark.read.json("mini_sparkify_event_data.json")*
此代码假定数据集与您的代码位于同一文件目录中。根据数据集的存储位置,您可能需要更改数据集的路径。
数据清理
有几列的值为空。值得注意的是,缺少firstName、gender、lastName、location、registration和userAgent 的条目属于未登录或未注册的用户。

Number of null values for each feature on the full dataset
由于我们不知道这些条目跟踪的是哪些用户,而且大多数条目都与登录或主页相关联,因此这些条目提供的价值很小。因此,我们可以从我们的分析中丢弃它们。
**# filter out all entries with missing names.*
*# these also removes entries with missing gender, lastName,
# location, registration and userAgent*df = df.filter(df.firstName.isNotNull())*
不是“NextSong”的页面的artist、length和sessionId为空值。所有这些变量只有在播放歌曲时才有效(只有当page ='NextSong ')时它们才不为空,因此我们可以不修改它们。
特征工程
首先,我们将时间戳转换为日期时间。原始时间戳是以毫秒为单位的,所以我们必须在转换它们之前将它们除以 1000。
**# original timestamp in milliseconds, so divide by 1000* adjust_timestamp = udf(**lambda** x : x//1000, IntegerType())
df = df.withColumn("ts_adj", adjust_timestamp('ts')) *# convert adjusted timestamp to datetime*
df = df.withColumn("datetime", from_unixtime(col("ts_adj"))) *# convert registration timestamp to datetime*
df = df.withColumn("reg_adj", adjust_timestamp('registration')) *# convert adjusted registration timestamp to datetime*
df = df.withColumn("reg_datetime", from_unixtime(col("reg_adj"))) *# drop all the timestamp columns. Will not need them*
columns_to_drop = ['registration', 'ts', 'ts_adj', 'reg_adj']
df = df.drop(*columns_to_drop)*
接下来,我们可以从 0 到 N 标记月份,其中 N 表示数据集中可用月份的总数。我们可以估计分析的开始日期为“2018 年 10 月 1 日 00:00:00”。
**# add start date of analysis*
df = df.withColumn('analysis_start_date',\
lit('2018-10-01 00:00:00')) *# number the months starting from the very first month of the # analysis*
df = df.withColumn("month_num",\
floor(months_between(df.datetime,\
df.analysis_start_date)))*
用二进制(0 或 1)变量替换gender和level的字符串变量。
**# engineer free or paid binary variable*
*# free: 0, paid: 1*
df = df.replace(["free", "paid"], ["0", "1"], "level")
*# engineer male and female binary binary variable*
*# male: 0, female: 1*
df = df.replace(["M", "F"], ["0", "1"], "gender")*
我们可以将流失事件定义为用户访问“取消确认”或“提交降级”页面。
***def** define_churn(x):
*"""
Defining churn as cancellation of service or downgrading from premium to free tier.
"""*
**if** x == "Cancellation Confirmation":
** return** 1
**elif** x == "Submit Downgrade":
**return** 1
**else**:
**return** 0 churn_event = udf(**lambda** x : define_churn(x), IntegerType())
df = df.withColumn("churn", churn_event("page"))*
为每个用户生成每月统计数据:
**# number of register page visits*
df_register = df.select('userId', 'month_num', 'page') \
.where(df.page=="Register") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numRegister')*# number of cancel page visits*
df_cancel = df.select('userId', 'month_num', 'page') \
.where(df.page=="Cancel") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numCancelVisits')*# number of upgrade page visits*
df_upgrade = df.select('userId', 'month_num', 'page') \
.where(df.page=="Upgrade") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numUpgradeVisits')*# number of downgrade page visits*
df_downgrade = df.select('userId', 'month_num', 'page') \
.where(df.page=="Downgrade") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numDowngradeVisits')*# number of home page visits*
df_home = df.select('userId', 'month_num', 'page') \
.where(df.page=="Home") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numHomeVisits')*# number of about page visits*
df_about = df.select('userId', 'month_num', 'page') \
.where(df.page=="About") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numAboutVisits')*# number of setting page visits*
df_settings = df.select('userId', 'month_num', 'page') \
.where(df.page=="Settings") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numSettingsVisits')*# number of times user save settings changes*
df_saveSettings = df.select('userId', 'month_num', 'page') \
.where(df.page=="Save Settings") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numSaveSettings')*# number of login page visits*
df_login = df.select('userId', 'month_num', 'page') \
.where(df.page=="Login") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numLogins')*# number of logout page visits*
df_logout = df.select('userId', 'month_num', 'page') \
.where(df.page=="Logout") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numLogouts')*# number of songs added to playlist*
df_addPlaylist = df.select('userId', 'month_num', 'page') \
.where(df.page=="Add to Playlist") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numAddPlaylists')*# number of friends added*
df_addFriend = df.select('userId', 'month_num', 'page') \
.where(df.page=="Add Friend") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numFriends')*# number of thumbs up given*
df_thumbsUp = df.select('userId', 'month_num', 'page') \
.where(df.page=="Thumbs Up") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numThumbsUp')*# number of thumbs down given*
df_thumbsDown = df.select('userId', 'month_num', 'page') \
.where(df.page=="Thumbs Down") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numThumbsDown')*# number of advertisements rolled*
df_advert = df.select('userId', 'month_num', 'page') \
.where(df.page=="Roll Advert") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numAdverts')*# number of songs played*
df_songsPlayed = df.select('userId', 'month_num', 'page') \
.where(df.page=="NextSong") \
.groupBy('userId', 'month_num') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'numSongsPlayed')*# total amount of time user listened to songs*
df_totalListen = df.select('userId', 'month_num', 'length') \
.groupBy('userId', 'month_num') \
.agg({'length':'sum'}) \
.withColumnRenamed('sum(length)', 'totalListenTime')
*# number of songs played per session*
df_songsPerSession = df.select('userId', 'month_num', 'page', 'sessionId') \
.where(df.page=="NextSong") \
.groupBy('userId', 'month_num', 'sessionId') \
.agg({'page':'count'}) \
.withColumnRenamed('count(page)', 'SongsPerSession')*# average number of songs played per session*
df_avgSongsPerSession = df_songsPerSession.groupBy('userId', 'month_num') \
.agg(avg(df_songsPerSession.SongsPerSession).alias ('avgSongsPerSession'))
*# number of singers listened per month*
df_singersPlayed = df.select('userId', 'month_num', 'page', 'artist') \
.where(df.page=="NextSong") \
.groupBy('userId', 'month_num') \
.agg(countDistinct(df.artist).alias('numSingersPlayed'))
*# number of singers per session*
df_singersPerSession = df.select('userId', 'month_num', 'page', 'artist', 'sessionId') \
.where(df.page=="NextSong") \
.groupBy('userId', 'month_num', 'sessionId') \
.agg(countDistinct(df.artist).alias('SingersPerSession'))*# average number of singers per session*
df_avgSingersPerSession = df_singersPerSession.groupBy('userId', 'month_num') \
.agg(avg(df_singersPerSession.SingersPerSession).alias ('avgSingersPerSession'))
*# amount of time spent for each session*
df_userSession = df.groupBy("userId", "month_num", "sessionId") \
.agg(((max(unix_timestamp(df.datetime))-min(unix_timestamp (df.datetime)))/60.0).alias('sessionTimeMins'))*# average time per session*
df_avgUserSession = df_userSession.groupBy('userId', 'month_num').agg(avg(df_userSession.sessionTimeMins).alias ('avgSessionMins'))
*# number of sessions per month*
df_numSession = df.select('userId', 'month_num', 'sessionId').dropDuplicates() \
.groupby('userId', 'month_num').agg({'sessionId':'count'}) \
.withColumnRenamed('count(sessionId)', 'numSessions')*# if user had premium level this month
# if user had premium at any point of the month, assumer he/she has # premium for the whole month for simplicity*
df_level = df.select('userId', 'month_num', 'level') \
.groupBy('userId', 'month_num') \
.agg({'level':'max'}) \
.withColumnRenamed('max(level)', 'level')*# find user's gender
# assuming nobody changes gender midway*
df_gender = df.select('userId', 'month_num', 'gender') \
.groupBy('userId', 'month_num') \
.agg({'gender':'max'}) \
.withColumnRenamed('max(gender)', 'gender')
*# start of each month*
df = df.withColumn("start_of_month", expr("add_months (analysis_start_date, month_num)"))
*# days since registration to start of each month*
df = df.withColumn("daysSinceReg", datediff(df.start_of_month, df.reg_datetime))
df_daysReg = df.select('userId', 'month_num', 'daysSinceReg') \
.groupBy('userId', 'month_num') \
.agg(min(df.daysSinceReg).alias('daysSinceReg'))*# did user churn this month*
df_churn = df.select('userId', 'month_num', 'churn') \
.groupBy('userId', 'month_num') \
.agg({'churn':'max'}) \
.withColumnRenamed('max(churn)', 'churn')*
将这些月度统计数据连接成一个新的数据框架:
*all_data = df_register.join(df_cancel, ['userId', 'month_num'], 'outer') \
.join(df_upgrade, ['userId', 'month_num'], 'outer') \
.join(df_downgrade, ['userId', 'month_num'], 'outer') \
.join(df_home, ['userId', 'month_num'], 'outer') \
.join(df_about, ['userId', 'month_num'], 'outer') \
.join(df_settings, ['userId', 'month_num'], 'outer') \
.join(df_saveSettings, ['userId', 'month_num'], 'outer') \
.join(df_login, ['userId', 'month_num'], 'outer') \
.join(df_logout, ['userId', 'month_num'], 'outer') \
.join(df_addPlaylist, ['userId', 'month_num'], 'outer') \
.join(df_addFriend, ['userId', 'month_num'], 'outer') \
.join(df_thumbsUp, ['userId', 'month_num'], 'outer') \
.join(df_thumbsDown, ['userId', 'month_num'], 'outer') \
.join(df_advert, ['userId', 'month_num'], 'outer') \
.join(df_songsPlayed, ['userId', 'month_num'], 'outer') \
.join(df_totalListen, ['userId', 'month_num'], 'outer') \
.join(df_avgSongsPerSession, ['userId', 'month_num'], 'outer') \
.join(df_singersPlayed, ['userId', 'month_num']) \
.join(df_avgSingersPerSession, ['userId', 'month_num'], 'outer') \
.join(df_avgUserSession, ['userId', 'month_num'], 'outer') \
.join(df_numSession, ['userId', 'month_num'], 'outer') \
.join(df_level, ['userId', 'month_num'], 'outer') \
.join(df_gender, ['userId', 'month_num'], 'outer') \
.join(df_daysReg, ['userId', 'month_num'], 'outer') \
.join(df_churn, ['userId', 'month_num'], 'outer')*
接下来,生成 1 个月的滞后特征。这些将被用作模型的输入特征,而不是本月的统计数据,因为我们不希望有任何前瞻性偏差。
*windowlag = (Window.partitionBy('userId').orderBy('month_num'))
*# generate 1 month lag features*
all_data = all_data.withColumn('numRegister_lastMonth', lag(all_data ['numRegister']).over(windowlag))all_data = all_data.withColumn('numCancelVisits_lastMonth', lag (all_data['numCancelVisits']).over(windowlag))all_data = all_data.withColumn('numUpgradeVisits_lastMonth', lag (all_data['numUpgradeVisits']).over(windowlag))all_data = all_data.withColumn('numDowngradeVisits_lastMonth', lag (all_data['numDowngradeVisits']).over(windowlag))all_data = all_data.withColumn('numHomeVisits_lastMonth', lag (all_data['numHomeVisits']).over(windowlag))all_data = all_data.withColumn('numAboutVisits_lastMonth', lag (all_data['numAboutVisits']).over(windowlag))all_data = all_data.withColumn('numSettingsVisits_lastMonth', lag (all_data['numSettingsVisits']).over(windowlag))all_data = all_data.withColumn('numSaveSettings_lastMonth', lag (all_data['numSaveSettings']).over(windowlag))all_data = all_data.withColumn('numLogins_lastMonth', lag(all_data ['numLogins']).over(windowlag))all_data = all_data.withColumn('numLogouts_lastMonth', lag(all_data ['numLogouts']).over(windowlag))all_data = all_data.withColumn('numAddPlaylists_lastMonth', lag (all_data['numAddPlaylists']).over(windowlag))all_data = all_data.withColumn('numFriends_lastMonth', lag(all_data ['numFriends']).over(windowlag))all_data = all_data.withColumn('numThumbsUp_lastMonth', lag(all_data ['numThumbsUp']).over(windowlag))all_data = all_data.withColumn('numThumbsDown_lastMonth', lag (all_data['numThumbsDown']).over(windowlag))all_data = all_data.withColumn('numAdverts_lastMonth', lag(all_data ['numAdverts']).over(windowlag))all_data = all_data.withColumn('numSongsPlayed_lastMonth', lag (all_data['numSongsPlayed']).over(windowlag))all_data = all_data.withColumn('totalListenTime_lastMonth', lag (all_data['totalListenTime']).over(windowlag))all_data = all_data.withColumn('avgSongsPerSession_lastMonth', lag (all_data['avgSongsPerSession']).over(windowlag))all_data = all_data.withColumn('numSingersPlayed_lastMonth', lag (all_data['numSingersPlayed']).over(windowlag))all_data = all_data.withColumn('avgSingersPerSession_lastMonth', lag (all_data['avgSingersPerSession']).over(windowlag))all_data = all_data.withColumn('avgSessionMins_lastMonth', lag (all_data['avgSessionMins']).over(windowlag))all_data = all_data.withColumn('numSessions_lastMonth', lag(all_data ['numSessions']).over(windowlag))all_data = all_data.withColumn('level_lastMonth', lag(all_data ['level']).over(windowlag))*
将用于我们预测模型的生成特征有:
numRegister_lastMonth:上月用户注册次数numCancelVisits_lastMonth:上月用户访问取消页面的次数numUpgradeVisits_lastMonth:上月用户访问升级页面的次数numDowngradeVisits_lastMonth:上月用户访问降级页面的次数numHomeVisits_lastMonth:上月用户访问主页的次数numAboutVisits_lastMonth:上个月用户访问关于页面的次数numSettingsVisits_lastMonth:上月用户访问设置页面的次数numSaveSettings_lastMonth:上个月用户保存设置更改的次数numLogins_lastMonth:上月用户登录次数numLogouts_lastMonth:上月用户注销次数numAddPlaylists_lastMonth:上个月用户添加到播放列表的歌曲数量numFriends_lastMonth:上个月新增好友用户数numThumbsUp_lastMonth:上月用户点赞数numThumbsDown_lastMonth:上个月用户给出的否决数numAdverts_lastMonth:上个月播放给用户的广告数量numSongsPlayed_lastMonth:用户上个月播放的歌曲数量totalListenTime_lastMonth:用户上个月的总收听时间avgSongsPerSession_lastMonth:上个月用户每次会话平均播放的歌曲数numSongsPlayed_lastMonth:用户上个月播放的歌曲数量avgSingersPerSession_lastMonth:上个月用户平均每次播放的歌手数量avgSessionMins_lastMonth:上个月用户每次会话的平均分钟数numSessions_lastMonth:上月用户会话数daysSinceReg:每个用户从注册到当月第一天的天数level_lastMonth:跟踪用户上个月是付费用户还是免费用户。如果用户在上个月的任何时候都是付费用户,为了简单起见,我们将假设他/她整个月都是付费用户
所有缺少的值都被估算为 0,因为缺少的值通常表示没有页面访问、没有播放歌曲等。
探索性数据分析
以下探索性数据分析是从数据集的中型实例中获得的。在数据集的小实例和大实例中都应该观察到类似的趋势。
下面显示的是第一个月的各种用户统计数据的箱线图,根据这些用户在第二个月是否有变动进行分组。
我们感兴趣的是看看在过去的用户行为中是否有任何可辨别的模式可以表明用户是否会在不久的将来流失。


**
一个令人惊讶的观察是,与没有流失的用户相比,在第二个月流失的用户似乎在第一个月更积极地使用数字音乐服务。被搅动的顾客总是有更高的页面访问量,播放更多的歌曲,听更多的歌手,竖起大拇指和竖起大拇指等等。
或许,这表明下个月有可能流失的用户通常会尝试在当月充分利用他们的服务。
此外,我们注意到注册数、访问取消页面的次数和登录次数都丢失了。
当用户注册或登录时,系统不知道用户的 id。因此,没有为客户记录这些活动的信息。
至于关于取消页面的页面访问的缺失统计,我注意到在第二个月使用该服务的用户中没有一个在第一个月访问过取消页面。很可能在第一个月访问取消页面的用户在第二个月都没有使用该服务。

Premium users are far more likely to churn
与免费用户相比,付费用户似乎更容易流失。请注意,在这种情况下,流失可能意味着从高级级别降级到免费级别,或者完全取消他们的帐户。

Male users are slightly less likely to churn
此外,与女性用户相比,男性用户似乎不太可能流失。

During the second month, the number of users who churned vs number of users who did not churn
第二个月流失的用户数量远远低于没有流失的用户数量。因此,我们将需要使用适当的指标来评估我们的模型,以确保我们的结果不具有欺骗性。我们还可以采用 SMOTE 等技术来处理不平衡的数据集,以提高我们的模型预测用户流失的能力。
总之,大多数流失很可能是由于用户从高级层降级到免费层造成的。我假设,对于新的高级用户,有一个较低的介绍性高级定价计划,不需要任何长期承诺。这些新的高级用户中的许多人可能不愿意对音乐服务做出长期承诺,并且希望在他们拥有高级服务的月份中最大化他们对服务的使用。
或者,也可能是付费用户在长时间使用后发现服务不令人满意,因此想要降低他们的订阅。
系统模型化
从探索性数据分析中,我们注意到,过去的客户活动可以提供一个相对较好的迹象,表明他们是否会在不久的将来流失。我们现在将借助 Spark ML 库建立预测模型,以预测这些客户何时可能流失。
Spark ML 库要求输入特征为数值,所以我们必须将所有字符串变量转换为整数或浮点数。
**# convert userId, gender, level, level_lastMonth to numeric*
convert_numeric = ['userId', 'level', 'gender', 'level_lastMonth']
**for** feat **in** convert_numeric:
featName = feat + "_n"
all_data = all_data.withColumn(featName, all_data[feat].cast ("float"))
all_data = all_data.drop(feat)*
您可以通过运行all_data.persist()来检查特征的数据类型。此外,确保输入要素中没有空值或“NaN”值。
接下来,我们删除第 0 个月的条目,因为我们没有前几个月的统计数据。我们将放弃当月的统计数据,保留上月的滞后功能和流失标签。
**# first month is month 0*
model_data = all_data \
.filter(all_data.month_num>0) \
.select('userId_n', 'month_num',\ 'numUpgradeVisits_lastMonth',\
'numDowngradeVisits_lastMonth',\
'numHomeVisits_lastMonth',\
'numAboutVisits_lastMonth',\
'numSettingsVisits_lastMonth',\
'numSaveSettings_lastMonth',\
'numLogouts_lastMonth',\
'numAddPlaylists_lastMonth',\
'numFriends_lastMonth',\
'numThumbsUp_lastMonth',\
'numThumbsDown_lastMonth',\
'numAdverts_lastMonth',\
'numSongsPlayed_lastMonth',\
'totalListenTime_lastMonth',\
'avgSongsPerSession_lastMonth',\
'numSingersPlayed_lastMonth',\
'avgSingersPerSession_lastMonth',\
'avgSessionMins_lastMonth',\
'numSessions_lastMonth',\
'level_lastMonth_n',\
'gender_n', 'daysSinceReg', 'churn' ).withColumnRenamed('churn', 'label')*
我们将输入要素和标注存储在数据框model_data中。
概括地说,我们将使用第一个月的活动数据来预测第二个月的客户流失率。因此,我们将在第二个月执行简单的训练-测试分割,以获得训练和测试数据集。
*train,test = model_data.randomSplit([0.8, 0.2], seed=50)*
注意,一个数据点的所有输入特征都必须放在一个名为features的向量中。建议也缩放输入要素。列churn应该改名为label。Spark ML 库将寻找features和label向量,所以使用这个命名约定。
*inputColumns = ['userId_n', 'month_num',\ 'numUpgradeVisits_lastMonth',\
'numDowngradeVisits_lastMonth',\ 'numHomeVisits_lastMonth',\
'numAboutVisits_lastMonth',\ 'numSettingsVisits_lastMonth',\
'numSaveSettings_lastMonth',\ 'numLogouts_lastMonth',\
'numAddPlaylists_lastMonth',\
'numFriends_lastMonth',\ 'numThumbsUp_lastMonth',\
'numThumbsDown_lastMonth',\ 'numAdverts_lastMonth',\
'numSongsPlayed_lastMonth',\
'totalListenTime_lastMonth',\
'avgSongsPerSession_lastMonth',\
'numSingersPlayed_lastMonth',\
'avgSingersPerSession_lastMonth',\ 'avgSessionMins_lastMonth',\
'numSessions_lastMonth',\
'level_lastMonth_n', 'gender_n',\
'daysSinceReg']assembler = VectorAssembler(inputCols=inputColumns,\
outputCol="FeaturesVec") scaler = StandardScaler(inputCol="FeaturesVec",\
outputCol="features",\
withMean=True, withStd=True)*
我们将尝试 3 种类型的模型:逻辑回归,线性支持向量分类机和梯度推进树(GBT)。
**# set max_iter to 10 to reduce computation time * *# Logistic Regression*
lr=LogisticRegression(maxIter=10)
pipeline_lr = Pipeline(stages=[assembler, scaler, lr]) *# Support Vector Machine Classifier*
svc = LinearSVC(maxIter=10)
pipeline_svc = Pipeline(stages=[assembler, scaler, svc]) *# Gradient Boosted Trees*
gbt = GBTClassifier(maxIter=10, seed=42)
pipeline_gbt = Pipeline(stages=[assembler, scaler, gbt])*
将报告测试准确度、f1 分数、精确度和召回率。F1 分数将是首选,因为标签是不平衡的。


**
其中:
- TN(真实否定):我们预测不会流失的客户,以及现实中没有流失的客户。
- FP(误报):客户预测会流失,但实际上并没有流失。
- FN(假阴性):我们预测的客户不会流失,但实际上会流失。
- TP(真阳性):客户预测会流失,但在现实中会流失。
精确度告诉我们精确度我们的预测有多精确(在那些预测会流失的客户中,有多少人流失了),而召回表明我们的模型召回*(模型设法找到了多少最初流失的客户)。*
f1 分数可以看作是精确度和召回率的加权平均值。
由于流失的客户数量少于未流失的客户数量,f1-score 将提供更准确的模型表现,而不是准确性。
使用 PySpark 的MulticlassClassificationEvaluator报告二进制分类任务的 f1 分数时,需要注意的是,内置函数会将标签 0 和 1 视为单独的类,并返回两个类的加权 f1 分数。这将产生一个可能具有欺骗性的过于乐观的分数。因此,我选择定义自己的函数来计算 f1 分数。更多细节可在代码中找到。
结果
我们将使用 5 个折叠进行 k-fold 交叉验证,并使用 PR 曲线下的区域优化模型。
使用 PR 曲线下的面积优于使用 ROC 曲线下的面积,因为数据集中类的不平衡意味着使用 ROC 曲线下的面积会导致过于乐观的情况。
以下结果是通过在完整数据集上训练我们的模型获得的。
逻辑回归
*paramGrid = ParamGridBuilder() \
.addGrid(lr.regParam,[0.0, 0.05, 0.1]) \
.build() cv_lr = CrossValidator(estimator=pipeline_lr,\
estimatorParamMaps=paramGrid,\
evaluator=\
BinaryClassificationEvaluator(metricName=\
"areaUnderPR"),\
numFolds=5, seed=42) cvModel_lr = cv_lr.fit(train)lr_results = cvModel_lr.transform(test)evaluate_model(lr_results)*
精确度:0。58660 . 68686868661
F1-分数:0.3467676767767
精度:0.6666666667
召回:0 . 10000 . 486868686865
混淆矩阵:
*TN:2706.0 | FP:124.0
FN:659.0 | TP: 248.0*
线性支持向量分类器
*paramGrid = ParamGridBuilder() \
.addGrid(svc.regParam,[0.0, 0.05, 0.1]) \
.build()cv_svc = CrossValidator(estimator=pipeline_svc,\
estimatorParamMaps=paramGrid,\
evaluator=\
BinaryClassificationEvaluator(metricName=\
"areaUnderPR"),\
numFolds=5, seed=42)cvModel_svc = cv_svc.fit(train)svc_results = cvModel_svc.transform(test)evaluate_model(svc_results)*
精确度:0。46860 . 68868886861
F1-分数:0.18500026767767
精度:0。56660 . 68686888661
召回:0.10000000001
混淆矩阵:
*TN:2793.0 | FP:37.0
FN:808.0 | TP: 99.0*
梯度增强树
*paramGrid = ParamGridBuilder() \ .addGrid(gbt.minInstancesPerNode,[5]) \ .addGrid(gbt.maxDepth,[7])\ .addGrid(gbt.subsamplingRate,[0.75])\
.build()cv_gbt = CrossValidator(estimator=pipeline_gbt,\
estimatorParamMaps=paramGrid,\
evaluator=\
BinaryClassificationEvaluator(metricName=\
"areaUnderPR"),\
numFolds=5, seed=42)cvModel_gbt = cv_gbt.fit(train)gbt_results = cvModel_gbt.transform(test)evaluate_model(gbt_results)*
精确度:0。36860 . 68868686861
F1-分数:0.453638636386
精度:0.50000000001
召回:0 . 35867 . 38888883886
混淆矩阵:
*TN:2614.0 | FP:216.0
FN:618.0 | TP: 289.0*
在测试数据集的 3 个模型中,GBT 模型获得了最高的 f1 值和最高的召回率。但是,它的精度也是最低的。尽管如此,GBT 模型在精确度和召回率之间保持了最佳平衡。
另一方面,线性支持向量分类器具有最高的精度,但 f1 值最低,主要归因于其低召回率。
最终,为预测客户流失而选择的模型将取决于您公司的需求。
如果你优先考虑留住客户,并且不介意在折扣和激励措施上多花一点钱来留住客户,你可以选择 GBT 模式,因为它在召回会流失的客户方面是最好的。
此外,如果您希望在客户维系度与折扣支出和维系客户维系度的激励措施之间保持更好的平衡,GBT 模式也将是理想之选,因为它还拥有最高的 f1 得分。
然而,如果您想要将折扣和激励措施的花费最小化以留住客户,线性支持向量分类器将是理想的,因为它的预测是最精确的。
结论
在本文中,我们实现了一个模型来预测客户流失的时间(从高级层降级到免费层或者取消他们的帐户)。凭借这一款车型,我们成功取得了大约 0.4 的 f1 分数,这是一个相当不错的分数。
可以提高 f1 分数的另一种方法是建立单独的模型来预测这两个事件。这两个事件有可能具有不同的信号,因此使用单独的模型会导致更好的结果。
本文附带的代码可以在 这里 找到。
感谢您阅读本文!如果你有任何想法或反馈,请在下面留下评论或给我发电子邮件到 leexinjie@gmail.com。我很想收到你的来信!
客户细分
深入分析
R 中的实用介绍

Source: Image by the author
顾客是任何企业成功的关键因素。传统智慧告诉我们,留住一个现有客户的成本远远低于获得一个新客户的成本。为了使一个企业有一个可持续的增长,保留它的老客户群和扩大新客户群是非常关键的。这就需要了解与业务相关的客户行为。因此,对于寻求市场竞争优势的企业来说,全方位了解客户至关重要。这方面的一种技术是客户细分,它有助于根据客户与产品的互动来识别相似的客户群,然后针对适当的客户有效地采取不同的营销方案。
本文提供了一个循序渐进的实践介绍,重点介绍了在 r 中执行客户细分的方法之一。为了更好地了解客户细分的思想,本文不包括关于在数据集上执行的探索性数据分析和数据争论的详细信息。
数据来源:超市 aggr。客户
所使用的数据集是意大利最大的零售分销公司之一 Coop 针对一个意大利城市的零售市场数据。
超市 aggr。用于分析的客户数据集包含从客户和商店信息聚合的数据,并透视到新列。因此,数据集包含 40 个特征和 60,366 个实例,大小约为 14.0 MB。
研究问题(RQ): 根据客户的购买行为,有哪些不同的客户群?
选择的算法: K 均值,主成分分析(PCA)
算法选择理由: K-means 聚类是一种非常简单快速的 algorithm⁴.这是用于客户细分的流行方法,尤其是对于数字数据。K-means 还具有计算优势,可以很好地处理大型数据集。分层的和基于模型的聚类方法需要计算全距离矩阵,该矩阵表现出有限的可扩展性和对大数据集计算的大存储器需求。相比之下,K-means 聚类在运行时效率更高。考虑到这些事实,并考虑到输入数据集很大且主要包含数值数据,K-means 是客户细分的理想选择。
PCA 是一种降维算法,它通过数据分解和转换为主成分(PC)来可视化数据集的本质,最大化数据的线性方差(方差越大表示对 data)⁵.的理解越多与 K-means 相比,PCA 不是一个直接的解决方案,因此从不同的角度来看,它可以帮助检测 K-means 没有发现的客户群。主成分分析在这里用于这个研究问题,作为一个有价值的交叉检查,以 K-均值数的集群确定。
选择的特征:RQ 希望根据顾客的购买行为识别顾客群,即顾客喜欢购买的商店。因此,特性 customer_id、amount_purchased_shop_1、amount_purchased_shop_2、amount_purchased_shop_3、amount_purchased_shop_4、amount_purchased_shop_5 非常重要,并被选择用于此 RQ。
分析:
1)估计最佳聚类数
K-means 要求在算法开始之前指定聚类数。确定最佳聚类数对于输出更好的结果至关重要。
**library**(cluster)
**library**(factoextra)
**library**("metricsgraphics")*# Read file contents*
supermarket_data_clean <- read.csv("Input Dataset/Cleaned Dataset/Supermarket_DataCleaned.csv")*# Prepare data frames for clustering*
*# Select only the rows customer_id, amount_purchased_shop_1, 2, 3, 4, 5*
cluster.slice.temp <- supermarket_data_clean[,c(1,29,30,31,32,33)]
*# Remover customer_id from the clustering data frame*
cluster.slice.data <- supermarket_data_clean[,c(29,30,31,32,33)]*# Scale the data and Determine the ideal number of clusters*
cluster.slice.scale <- scale(cluster.slice.data)wssplot <- **function**(data, nc=15, seed=1234){
wss <- (nrow(data)-1)*sum(apply(data,2,var))
**for** (i **in** 2:nc){
set.seed(seed)
wss[i] <- sum(kmeans(data, centers=i)$withinss)}
plot(1:nc, wss, type="b", xlab="Number of Clusters",
ylab="Within groups sum of squares")}wssplot(cluster.slice.scale)

Source: Image by the author
输出图描绘了群集数量从值 1 到 4 的急剧减少,以及从 4 到 5 的轻微减少,这估计了 4-群集或 5-群集解决方案。
2)执行 K 均值聚类,聚类为 4 和 5
*# Perform k-means on cluster values as 4 and 5**# On entire dataset*
set.seed(123) *# fix the random starting clusters*
kclust4 <- kmeans(cluster.slice.data, 4, nstart = 25)set.seed(123) *# fix the random starting clusters*
kclust5 <- kmeans(cluster.slice.data, 5, nstart = 25)
3)执行主成分分析以可视化聚类
pca <- prcomp(t(cluster.slice.data), scale. = T, center = T)
fviz_eig(pca) +
theme_bw() + scale_y_continuous(labels = scales::comma) +
ggtitle(label='Principal Component Analysis')

Source: Image by the author
正如所观察到的,主成分分析 1 和主成分分析 2 共同解释了大部分数据差异,然后从主成分分析 2 下降到主成分分析 3。因此,这推断出用 PCA 1 和 PC 2 的可视化将给出对数据的良好理解,并且在 PCA 2 之后包括更多的 PCA 将仅导致最小的改进。
具有 4 个聚类 K-均值的 PCA
cluster.pc4 <- prcomp(cluster.slice.data, center = FALSE, scale. = FALSE)$x %>% as.data.frame()
cluster.pc4$kmeans.cluster <- factor(kclust4$cluster)p<-ggplot(cluster.pc4,aes(x=PC1,y=PC2,color=kmeans.cluster))
p+geom_point() +
theme_bw() + scale_y_continuous(labels = scales::comma) +
ggtitle(label='PCA with 4 cluster K-means')

Source: Image by the author
具有 5 个聚类 K 均值的 PCA
cluster.pc5 <- prcomp(cluster.slice.data, center = FALSE, scale. = FALSE)$x %>% as.data.frame()
cluster.pc5$kmeans.cluster <- factor(kclust5$cluster)p<-ggplot(cluster.pc5,aes(x=PC1,y=PC2,color=kmeans.cluster))
p+geom_point() +
theme_bw() + scale_y_continuous(labels = scales::comma) +
ggtitle(label='PCA with 5 cluster K-means')

Source: Image by the author
比较以上两个图,确定 5 个聚类的解决方案将是 K-均值聚类的理想估计。
4)可视化数据中不同的可分离聚类
fviz_cluster(kclust5, data = cluster.slice.data, geom = "point",
stand = FALSE, ellipse.type = "norm") +
theme_bw() + scale_y_continuous(labels = scales::comma) +
ggtitle(label='Customer Clusters')

Source: Image by the author
5)聚类分析
确定属于每个集群的不同客户
*## retrieve customer ID's in each cluster*
head(gather(data.frame(cluster.slice.temp[kclust5$cluster == 1,])))*## retrieve customer ID's in each cluster*
head(gather(data.frame(cluster.slice.temp[kclust5$cluster == 2,])))
head(gather(data.frame(cluster.slice.temp[kclust5$cluster == 3,])))
head(gather(data.frame(cluster.slice.temp[kclust5$cluster == 4,])))
head(gather(data.frame(cluster.slice.temp[kclust5$cluster == 5,])))
6)客户细分
*#Customer segmentation through aggeration of results by mean*
cluster.slice.kmeans.aggregate <- aggregate(cluster.slice.data, by = list(kclust5$cluster), mean)cluster<-c(cluster.slice.kmeans.aggregate$Group.1)
shop1<-c(cluster.slice.kmeans.aggregate$amount_purchased_shop_1)
shop2<-c(cluster.slice.kmeans.aggregate$amount_purchased_shop_2)
shop3<-c(cluster.slice.kmeans.aggregate$amount_purchased_shop_3)
shop4<-c(cluster.slice.kmeans.aggregate$amount_purchased_shop_4)
shop5<-c(cluster.slice.kmeans.aggregate$amount_purchased_shop_5)*# Plot a Bar graph*
Legends <-c(rep("Customers Shop 1", 5), rep("Customers Shop 2", 5), rep("Customers Shop 3", 5), rep("Customers Shop 4", 5), rep("Customers Shop 5", 5))
values <-c(shop1,shop2,shop3,shop4,shop5)
mydata <-data.frame(cluster, values)p <-ggplot(mydata, aes(cluster, values))
p +geom_bar(stat = "identity", aes(fill = Legends)) +
xlab("Cluster") + ylab("Total") +
ggtitle("Customer Segmentation") +
theme_bw() + scale_y_continuous(labels = scales::comma)

Source: Image by the author
观察:根据顾客的购买行为对数据进行聚类,即从他们购物最多的商店中,发现了 5 个可分离的聚类进行分析。聚类分析有助于根据客户 id 识别每个聚类中的客户。这有助于了解在每个集群中构建客户群的不同客户。此外,客户细分有助于识别每个细分(聚类)中不同商店的客户。这进一步有助于划分集群并赋予其意义。因此,通过基于他们的购买行为识别五个客户群,并通过确定属于五个不同商店的特定客户来进一步划分这些客户群,来回答研究问题。
应用:集群的检测可以帮助企业为每个集群基础制定具体的策略。聚类还可以用于了解客户的购买行为,方法是跟踪数月的客户,并检测从一个聚类转移到另一个聚类的客户数量。这有助于企业更好地组织战略,以增加不同商店的收入。企业可以进一步利用所获得的客户细分洞察来更好地将他们的营销工作集中在正确的客户上,例如,与特定商店相关的折扣和优惠可以只发送给通常在该特定商店购买的那些客户,而不会打扰其他商店的客户。因此,为正确的交易锁定正确的客户有助于降低营销成本、创造更多收入并提高客户满意度。
客户行为分析作为一个重要领域,利用数据分析在特定于客户的业务数据中发现有意义的行为模式。本文分享的见解来自对数据源进行的端到端市场篮子分析中的一种方法,旨在了解消费者的购买决策以及影响这些决策的因素。
参考
[1]https://bigml . com/user/czuriaga/gallery/dataset/5559 C2 c 6200 d5a 6570000084
[2] Pennacchioli 博士、Coscia m .博士、Rinzivillo s .博士、Pedreschi 博士和 gian notti f .博士,2013 年 10 月。解释购买数据中的产品范围效应。大数据,2013 年 IEEE 国际会议(第 648–656 页)。IEEE。
http://www.michelecoscia.com/?page_id=379
[4]哈迪根、约翰·A 和曼切克·A·王 1979."算法 as 136:一个 K 均值聚类算法."皇家统计学会杂志。【T1 系列 C(应用统计学)】28 (1)。JSTOR:100–108。
[5]丁、克里斯和。2004." K-均值聚类通过主成分分析."在第二十一届机器学*国际会议论文集,29 页。ACM。
用 Python 进行客户细分分析

在这篇文章中,我将探索一个关于购物中心顾客的数据集,尝试看看是否有任何可辨别的细分和模式。客户细分有助于理解在一个商业案例中你的客户中有哪些人口统计学和心理学的亚人群。
了解了这一点,你就能更好地理解如何营销和服务他们。这与 UX 创建用户角色的方法类似,但略有不同:创建你的理想客户、他们的痛点、定义性报价等等,以了解他们的观点。
设置
开始时,您可以写出导入语句并加载数据集,调用head()查看数据预览。
像往常一样,在这里通过克隆回购随意编码。
Import statements

Head call
接下来,您可以对数据调用describe()来查看每个变量的描述性统计数据。在这里花点时间理解这些数字的含义是很重要的。例如,如果支出分数(1-100)列(显然是 1 到 100 之间的一个值范围)的最小值或最大值超过 100,您就会知道有问题。

Describe call
通过调用 describe,您可以看到没有要清理的值。年龄看起来很正态分布,年收入没有太多的异常值。支出分数实际上在 1 到 100 分之间。一切看起来都很好。
探索数据
了解分类变量在整个数据集中是如何分割的总是很有帮助的。这可以通过一个简单的计数图来实现,如下所示:

在这个数据集中,女性略多于男性。他们也许会成为你以后客户细分工作中的一个重要因素。
年龄呢?

Distribution of ages
年龄大多在 30 到 40 岁之间。回想一下describe()的通话结果这是有道理的。平均年龄为 38 岁。老年客户较少,因此这种分布是右偏的,因为它的右尾较长。这可能是因为购物中心的吸引力和倾向于在那里购物的人口类型。
您可以通过叠加两个直方图来添加细节,为每个性别创建一个年龄直方图。

Distribution of age by gender
这个数据集中的男性往往比女性年轻。你可以看到,女性在 30-35 岁之间达到顶峰,这也是她们中大多数人下降的时候。这个数据集中的中年女性也比男性多。65-70 岁年龄段中有相当数量的老年人。
收入怎么样?您可以用下面的代码来看看这个发行版:

Distribution of income
大部分收入介于 60 美元和 85,000 美元之间。性别对此有影响吗?

Distribution of income by gender
这个数据集中的女性比男性挣钱少。他们的支出分数是什么样的,相比之下又如何呢?
男性的平均支出分数为 48.5,女性为 51.5。在这个数据集中,女性挣得更少,但在这个商场花得更多。
您可以进一步增加复杂性,以便更好地理解数据。因为大部分是定量变量和一个干净的二元分类变量,所以做一些散点图是有帮助的。

Age and income, colored by gender
没有明确的相关性。事实上,你可以量化这一点,并且很容易地对所有变量进行量化。就叫sns.heatmap(customers.corr(), annot=True)。

Correlation heat map of each variable
从上面的图中可以看出,唯一稍微相关的变量是支出分数和年龄。这是一种负相关关系,因此在这个数据集中,客户的年龄越大,他们的消费得分就越低。但是因为是 0.33,所以根本不是强相关。它的信息量仍然很少,并且遵循基本的逻辑。
编码下面的图显示了这一趋势。

Age to spending score by gender
你现在可以看到轻微的负相关。你难道不想知道是男人还是女人有更强的相关性吗?您可以通过之前创建的两个按性别划分的数据框中的热图来验证这一点。

Correlation heat map of female customers
将female_customers数据框替换为男士数据框,为他们展示了以下情节:

Correlation heat map of male customers
在这种情况下,年龄对女性的支出分数影响更大。没有任何其他东西真的有足够强的相关性来说明任何事情。
现在你可以用一个漂亮的lmplot放大女性消费分数与年龄的关系。

Scatter of age to spending score for women, with a regression line and bootstrap interval about the line
最后,您可以使用以下代码查看按性别着色的收入支出比得分:

Spending score and income by gender
这里有一些模式。但是零相关。但是你可以把这些看作是客户群:
- 低收入,低支出分数
- 低收入,高支出分数
- 中等收入,中等支出分数
- 高收入,低支出分数
- 高收入,高支出分数
解释和行动
回到这类分析的业务和营销用例,可以测试以下假设。
- 向女性推销更便宜的商品会改变购买频率或数量吗?
- 对年轻女性进行更多的营销会导致更高的销售额吗,因为她们的消费得分往往更高?
- 广告、定价、品牌和其他策略如何影响老年女性(40 岁以上)的消费分数?
要回答这些问题,需要更多的数据。
计划如何收集更多数据以构建具有更多功能的数据集将会很有帮助。功能越多,就越能理解是什么决定了支出分数。一旦更好地理解了这一点,你就能理解什么因素会导致支出分数增加,从而带来更大的利润。
KPI
本着业务用例的精神,我将定义下面的 KPI 作为一个例子来展示你如何知道你的努力是否有回报。
- 在推出更多针对妇女的营销活动后,妇女购买的频率和数量发生了变化。
- 引入针对年轻女性的营销活动后,支出分数的变化。
- 引入针对老年女性的营销活动后,消费得分的变化。
如果你觉得这篇文章很有用或者学到了新的东西,考虑捐赠任何数量的钱来支付给下一个学*者!
感谢阅读和快乐编码!
奢侈的生活
客户细分数据科学
问题公式化、数据建模和聚类分析
在市场营销中,客户细分是将客户按共同特质分组的过程。根据客户类型辨别购买*惯有助于恰当地营销。例如,它揭示了各部分的大小,我们从中赚了多少,等等。这有助于决定如何分配营销预算。
在数据科学中,聚类是根据一些共同特征对对象进行分组的过程。
看到联系了吗?聚类——一种数据科学方法——非常适合客户细分——一种用例。也就是说,正如经常发生的那样,当我们开始挖掘时,还有更多的东西。我们称之为建模。
让我们先来看看我们想要细分的具体特征。我们将区分 B2B 营销和 B2C 营销,因为它们的特点各不相同。
B2B 营销
客户是企业,即公司。一组有用的特征是所谓的公司分类:行业、地点、公司规模(小、中、大等)。
B2C 营销
在这里,顾客是消费者,即人。用于细分的有用特征是所谓的人口统计特征:年龄、性别、地点、种族、收入水平等等。
建模
现在,我们从 B2B 和 B2C 的细节中抽象出来,看看建模本身。虽然我们将使用 B2B 示例进行说明,但是建模更加通用。
单一结构化因素
先考虑单一特质,说行业。我们希望通过这一特点来细分客户。首先,让我们假设行业有少量的干净值。(“干净”是指来自受控的词汇表,例如下拉列表中的词汇表。)
这种分割很容易做到。它只是涉及到分组和聚合。
单一非结构化因素
现在假设行业价值观不干净。它们以自由格式存储在文本字段中。在数据输入时,任何有意义的值都会被输入。
假设我们的数据库中有 100,000 条客户记录,其中大多数都填写了行业字段值。
我们的细分问题立刻变得更加困难。同一个行业在不同的记录中往往会有不同的表达方式。词序可能有所不同;缩略语可能会被使用;连接词可能存在,也可能不存在;可能会用到同义词。
字符串相似性度量 : 解决这个问题的第一个合理尝试是这样的。首先,我们在成对的行业字符串上定义一个相似性度量。我们首先定义一个行业字符串的词集。这只是字符串中的一组不同的标记。熟悉术语单词袋的人可能会认为这是去掉了词频的袋子。接下来,给定两个词集,我们需要一个合理的方法来量化它们的相似性。这里有一个:叫做 Jaccard 相似度。
两个集合的 Jaccard 相似度是它们共有的元素数除以它们的并集中的元素数。
下面是一个例子。
**Jaccard-coefficient({predictive analytics platform},{predictive analytics}) = ⅔**
将记录分组:接下来,我们需要使用这个相似性度量将记录分组。同一聚类中的记录对应该彼此相似;不同簇中的对不匹配。
这说起来容易做起来难。还不清楚我们是否已经完全考虑了我们的问题表述。我会详细说明。“组”这个词似乎意味着一个记录应该恰好属于一个簇。这真的是我们想要的吗?不一定。想象一个行业价值医学信息学。我们可能希望将它放在多个集群中——一些与医疗相关,一些与 IT 相关。
多级行业:行业字段的值可以跨越行业分类的多个级别。例如,一条记录可能将行业字段值设置为一个宽泛的类别,比如说软件。另一个记录可以在更精细的层次上指定行业,比如说数据库软件。所以也许我们要做的是层次聚类,而不是平面聚类。因此,行业为数据库软件的记录可以被分配给一个集群,该集群是行业为软件的集群的后代。
聚类算法:使用哪种算法取决于我们想要哪种类型的聚类——平面的还是层次的?即使在扁平类别中,算法也取决于我们希望聚类是硬的还是软的。(硬表示一个项目恰好属于一个集群。)
平面聚类:适合硬聚类的算法有 k -means 聚类、dbscan、连通分量聚类等。软聚类算法通常是硬聚类算法的软化版本。其中一个是软的,也称为概率的,即聚类的 k 。这是基于一个强大的算法,称为 EM 算法,其中(硬)k 均值聚类是一个特例。
层次聚类:流行的层次聚类算法有凝聚(自下而上)聚类或分裂(自上而下)聚类。在前者中,首先将项目聚集成紧密的簇,然后将簇聚集成较粗糙的簇。重复这个过程,直到我们得到一个聚类树。它的叶子就是物品。树的根是代表整个数据集的单个聚类。
分裂聚类的作用方向相反。它从代表整个数据集的聚类开始;然后,一个集群被一分为二。由此产生的集群成为前者的子集群。因此聚类树是自上而下生长的。
自底向上 vs 自顶向下:有人可能会想,自底向上或者自顶向下,它们是镜像过程,所以为什么要在意使用哪一个呢。选择很重要。合并两个合适的小集群通常比将一个大集群分成两个好的集群更容易。这个事实很大程度上解释了为什么自底向上的聚类比自顶向下的聚类更受欢迎。
另一个重要的考虑是,自底向上和自顶向下的聚类会产生不同的结果。自下而上的建立不同于自上而下的分裂。由于这个原因,当自底向上的结果不够好时,或者我们只想知道在任何特定情况下从自底向上到自顶向下的切换对树的改变有多大时,有时会考虑自顶向下的聚类。
行业名称中的语言结构:回到我们的行业,以非结构化价值为例,我们可能会采取第三种策略。利用行业名称中隐含的语言结构。这可能会更好。下面我们用一个玩具例子来说明这一点。出现的算法可以容易地被增强以适应更现实的场景。
举例:假设只有六个不同的行业价值:软件、数据库软件、分析软件、制造业、汽车制造业、药品制造业。(有些名字是人为的,不要当真。)所寻求的层次结构是
**Software ← Database Software, Software ← Analytics Software
Manufacturing ← Auto Manufacturing, Manufacturing ← Drug Manufacturing**
其中 p ← c 表示父子实例。
利用语言结构的算法:下面是算法。在这个例子中,它将产生我们想要的层次结构。
**Order the set of distinct industry values by the number of words in them. Least number of words first. Denote this ordering as I1, I2, …, Ik.****For each i in 1..k
If Ii does not have a parent, set Ii as a root of the hierarchy.
For each j in (i+1)..k
If Ij’s parent is not set and is_child(Ij,Ii)
parent(Ij) = Ii**
让我们在例子中说明这个算法。首先,我们根据行业名称中的字数对其进行排序。这产生了有序列表
**Software, Manufacturing, Database Software, Analytics Software, Auto Manufacturing, Drug Manufacturing**
接下来,我们将软件作为一个根,扫描到它的右边,找到它的所有子节点。我们发现
**Software ← Database Software, Software ← Analytics Software**
接下来,我们将 Manufacturing 作为这个层次结构的根,并扫描到它的右边,找到它的所有子代,跳过已经有父代的行业名称。我们发现
**Manufacturing ← Auto Manufacturing, Manufacturing ← Drug Manufacturing**
接下来,我们尝试查找每个两个单词的行业名称的子代。我们什么也没找到。所以我们结束了。
多重因素,混合结构
现在考虑结合多种因素对公司进行细分。具体来说,是根据关键的公司分类:行业、地点和公司规模。这些因素中有些可能是结构化的,有些可能是非结构化的。例如,和以前一样,工业是无组织的。公司规模可以是结构化的:用固定数量的值表示,如小、中、大。这使问题变得非常复杂。
多因子相似性度量:首先,让我们定义一个合适的度量,量化一对因子元组的相似程度。这一定义需要仔细考虑,因为这一指标将极大地影响哪些公司被归入哪些细分市场。
序言 1 —因子描述:首先,让我们细化我们的因子描述。我们将像以前一样保持行业的非结构化。我们将使公司规模有序。这只是意味着公司规模将有一套固定的有序值。举个例子,小型,中型,大型。我们以后可以给公司规模添加更多的值,比如非常小和非常大。我们甚至可以通过数据驱动的公司规模宁滨过程来自动确定一组值。结果值可能因数据集而异。只要价值观是有序的,我们就是好的。(顺便说一下,并不是每组值都可以排序。考虑性别=女,男。没有自然的顺序。我们可以强加一些人为的秩序,但这可能不太好。)
对于位置,首先让我们保持简单,只选择 5 位数字的邮政编码。(这确实将我们的公司局限在美国。)
序言 2 —关于指标的一些注释:接下来,我们在这些因素定义下定义一个合适的指标。值得注意的是,如果我们修改我们的因素定义,该指标可能需要大规模的修订。例如,假设我们将位置定义为邮政编码、城市、州、国家的组合。量化两个地点的相似程度现在要复杂得多。
单个因素的相似性度量
我们将采用为每个因素单独定义相似性度量的方法,并在此基础上构建我们的多因素相似性度量。
公共部分— Sigmoid 函数:在我们深入研究各个指标之前,我们想提一下贯穿其设计的一个关键点。在所有这些方法中,我们使用 sigmoid 函数作为阶梯函数的软*似。我们这样做是因为当被比较的值不够相似时,我们希望相似性迅速下降到 0。这使我们能够控制相似性检测和假阳性率。
正因为如此,我们先来定义一下 sigmoid 函数。
**sigmoid_{a,b}(x) = 1/(1+exp(-a*(x-b)))**
a控制 sigmoid 的陡度,即它逼*阶跃函数的程度。b控制软阈值。就像一个阶跃函数,sigmoid 识别b两侧的值。
公司名称相似度指标:让我们从比较两家公司规模的指标开始。让我们将公司规模值按顺序排列为 1 、 2 、 3 、…、 k 。也就是说, 1 表示最小公司尺寸仓位, k 表示最大公司尺寸仓位。我们的相似性度量是
**S(i,j) = sigmoid_{a,b}(-|i-j|)**
这里a>0和b>0让我们控制 s 形的陡度和阈值。我们使用了-|i-j|而不是|i-j|作为参数,因为我们寻求一个反 s 形行为。相反,我们可以将这个负号吸收到a中,但是让它显式更直观。
S(i,j)有一个简单的直觉,即箱 i 和 j 在它们的等级顺序中越接*,即在它们所代表的公司的规模中,相似性值越高。
我们将选择合理的默认值,这样如果你不喜欢自由参数,你可以忘记它们。具体来说,我们将a设置为 1,这是最常用的 sigmoid 陡度。为了更好地揭示什么值b有意义,让我们将 sigmoid 重写为sigmoid(b-|i-j|)。我们将把b设置为 1.5。这将使我们得到当|i-j|<=1.5s 形值从下方越过 0.5 时的行为。当然,我们可以随心所欲地微调这种行为。
zip 相似性度量:接下来,让我们研究 zip 值的度量。我们将利用 5 位数的邮政编码是数字的事实,一般来说,数字越*,邮政编码位置就越*。我们使用
**S(z1,z2) =sigmoid_{a,b}(-|z1-z2|)**
这次我们会选择a>0远小于 1、b到~ 20。直觉是,我们希望 S(z1,z2)大于 0.5,即使|z1-z2|大约为 20,粗略地说。此外,我们希望交叉点有低陡度。当然,我们可以按照自己认为合适的方式微调这些值。
替换和转置错误:虽然这种度量对邻*性建模得足够好,但它没有考虑替换或转置错误。让我们先来看一个例子。(我们有意省略了插入和删除错误,因为它们改变了位数。)
**Substitution error: 21**0**34, 21**8**34, Transposition error: 2**10**34, 2**01**34**
和上面的例子一样,我们将只考虑单个数字替换错误和相邻数字换位错误。下面是一个简单的度量标准,可以容纳一位数的替换错误:
**S2(z1,z2) = sigmoid_{a,b}(-sum_i NE(z1[i],z2[i]))**
这里x等于y时NE(x,y)等于 0,不等于时为 1。我们将a设置为 1,将b设置为 1.5,因为我们寻求的行为(根据 sigmoid 的论证)正是我们寻求的公司规模相似性。
类似地,我们可以定义一个最多容纳一个相邻数字换位的度量。
**T(z1,z2) = sigmoid_{a,b}(-#adjacent-digit-transpositions(z1,z2))**
我们使用与替代计分相同的a和b。
现在,我们可以将所有这些指标合并成一个指标。
**Sim(z1,z2) = Max(S(z1,z2),S2(z1,z2),T(z1,z2) )**
行业相似性度量:最后,对于行业,我们将使用其词集上的 Jaccard 系数作为相似性度量,并通过 sigmoid 进一步转换。即
**S_industry(I1,I2) = sigmoid_{a,b}(Jaccard-coefficient(I1,I2))**
如何选择a和b的默认值我们就不详细描述了。相反,我们将注意到a应该是大于 1 的正数,并且b应该被选择为我们认为处于相似性边缘的 Jaccard 系数的值。请注意,Jaccard-coefficient 返回一个介于 0 和 1 之间的值,其中 0 表示最不相似,1 表示最相似。
多因素指标
我们现在准备在这些指标的基础上构建我们的多因素指标。首先,让我们简单讨论一下这种方法的利弊。这种模块化设计使得添加新的因素或删除现有的因素变得容易。它还使得调整单个因子值的相似性对整体度量的相对影响变得容易。也就是说,它不能自然地模拟因素之间的相互作用。为了对这些进行建模,需要明确地添加特定的交互术语。
我们的多因素指标是
**S(C1,C2) = w_zip*S_zip(C1.zip,C2.zip) + w_industry*S_industry(C1.industry,C2.industry) + w_company_size*S(C1.company_size_bin_rank,C2.company_size_bin_rank)**
因素权重让我们可以控制整体指标中单个因素相似性的相对影响。
专门化:现在我们有了这个指标,我们可以通过简单地将某些因素权重设置为 0 来构建它的各种专门化。因此,我们可以实现所有的单因素版本——基于 zip 的相似性、基于行业的相似性、基于公司规模的相似性——以及所有的双因素版本。有了这些专业化,我们可以更加灵活地构建细分市场。实际上,我们可以根据一个因素、两个因素或所有三个因素进行分组。请注意,“分组依据”实际上意味着“模糊分组依据”。
聚类相似性矩阵:我们想要提出的关键点是,利用我们定义的成对相似性度量的自然方法是计算所有成对公司之间的成对相似性,并在相似性矩阵中捕捉这些相似性。(除非公司的数量太多,以至于计算两两相似度会太慢。我们将假设情况并非如此。)
然后,聚类算法可以直接在这个矩阵上工作。这就分开了顾虑。聚类算法只对相似矩阵进行操作,而不关心它是如何产生的。相似性度量关注量化实际的相似性,而不关心对象将如何在下游聚集。
一个具体的算法——图划分:我们来看一个相似矩阵上的具体聚类算法。我们将把矩阵表示成图形。图中的节点是对象,在我们的例子中是公司。如果对应的对象足够相似,则两个节点由一条边连接。边上的权重捕获实际的相似性值。
该算法的高级思想是将图划分成由稀疏交叉区域连接的稠密子图。密集的子图将表示紧密的聚类。稀疏的交叉区域将确保这些簇被很好地分开。
我们来提炼一下这个描述。我们首先需要一些关键概念。
子图密度:我们将一个子图在 k 个节点上的密度定义为连接这组节点对的边上的权重之和除以 k ( k -1)/2。这假设边上的权重介于 0 和 1 之间。非边可以被视为权重为 0 的边。在这种表示下,子图的密度是其中一条边的平均权重。
节点权重:我们将节点的权重定义为接触该节点的边的权重之和。我们将节点集上的节点的权重定义为接触该节点的边的权重之和,该节点的另一个端点在该集中。例如,节点 b 、 c 和 d 上的节点 a 的权重是边 a - b 、 a - c 和 a - d 的权重之和。
实际算法:接下来,我们描述一个简单的启发式算法,它可以找到一个好的聚类。
**find_one_good_cluster(G)
C = {v}, where v is a highest-weight node in G
Repeat
Find a node v in G-C whose weight on C is
sufficiently high.
If no such v exists
Close C.
remaining_clusters = find_one_good_cluster(G-C)
Return remaining_clusters + {C}
Else
Add v to C
forever**
“足够高”测试使算法偏向于寻找紧密的聚类。(不保证紧密性,因为紧密性检查仅在节点添加到集群时进行。)该算法并不明确倾向于跨簇稀疏。这是隐性的。只要一个集群之外的节点对其中的节点具有足够高的累积权重,该集群就会保持增长。因此,当集群停止增长时,其外部的所有节点相对于其中的节点具有较低的累积权重。这有利于稀疏的跨簇区域。
总结
在这篇文章中,我们看到了聚类作为一种数据科学方法是如何作为一个用例很好地适合市场细分的。我们已经研究了 B2B 和 B2C 细分的因素。我们已经研究了当一个因子有干净的值时出现的问题;当一个因子具有非结构化值的长尾时;当一个因子有一小组序数值时。
我们已经讨论了平面聚类、软聚类和层次聚类。我们已经看到了领域知识如何指导我们使用什么算法,甚至让我们远离标准算法。(我指的是利用行业名称中隐含的语言结构,将行业名称聚集成一个行业层次结构。)
我们已经提出了具体的衡量标准,用于量化我们在 B2B 环境中遇到的某些因素的相似性。然后,我们提出了一个多因素相似性度量,并描述了如何在多因素聚类中使用它(考虑通过多个因素的组合来细分市场)。
基于 K 均值聚类的客户细分
为了更好地了解你的客户

Photo by Roman Arkhipov on Unsplash
被困在付费墙后面?点击这里阅读完整故事与我的朋友链接!
客户细分是将市场细分为具有相似特征的离散客户群。客户细分是识别未满足客户需求的有力手段。利用上述数据,公司可以通过开发独特的吸引人的产品和服务来超越竞争对手。
企业对其客户群进行细分的最常见方式有:
- 人口统计信息,如性别、年龄、家庭和婚姻状况、收入、教育和职业。
- 地理信息,因公司范围不同而不同。对于本地化的企业,这些信息可能与特定的城镇或县有关。对于较大的公司,这可能意味着客户的城市,州,甚至居住国。
- 心理图形,如社会阶层、生活方式、性格特征。
- 行为数据,如支出和消费*惯、产品/服务使用情况、期望收益等。
客户细分的优势
- 确定合适的产品定价。
- 开展定制营销活动。
- 设计一个最优的分配策略。
- 选择要部署的特定产品功能。
- 优先考虑新产品开发工作。
挑战
你欠一个超市购物中心,通过会员卡,你有一些关于你的客户的基本数据,如客户 ID,年龄,性别,年收入和消费分数。你需要了解客户,比如谁是目标客户,这样营销团队才能获得这种感觉,并据此制定策略。
k 均值聚类算法
- 指定集群数量 K 。
- 通过首先混洗数据集,然后为质心随机选择 K 个数据点来初始化质心,而无需替换。
- 继续迭代,直到质心没有变化。也就是说,数据点到聚类的分配没有改变。

K Means Clustering where K=3
数据
该项目是在 Kaggle 上举办的商城客户细分数据竞赛的一部分。
数据集可以从 kaggle 网站下载,该网站可以在这里找到。
环境和工具
- sci kit-学*
- 海生的
- numpy
- 熊猫
- matplotlib
代码在哪里?
事不宜迟,让我们从代码开始吧。github 上的完整项目可以在这里找到。
我从加载所有的库和依赖项开始。数据集中的列是客户 id、性别、年龄、收入和支出分数。

我删除了 id 列,因为它似乎与上下文无关。我还画出了顾客的年龄频率。

接下来,我制作了一个支出分数和年收入的方框图,以更好地形象化分布范围。支出分数的范围明显大于年收入范围。

我制作了一个条形图来检查数据集中男性和女性人口的分布。女性人口明显多于男性。

接下来,我绘制了一个条形图,以检查每个年龄组中客户数量的分布。很明显,26-35 岁年龄组的人数超过了所有其他年龄组。

我继续制作一个柱状图,根据顾客的消费分数来可视化顾客的数量。大多数顾客的消费得分在 41-60 之间。

我还制作了一个柱状图,根据客户的年收入来可视化客户的数量。大多数顾客的年收入在 60000 到 90000 英镑之间。

接下来,我绘制了聚类平方和(WCSS)与聚类数量(K 值)的关系图,以计算出最佳聚类数量值。WCSS 测量观测值到其聚类质心的距离总和,由以下公式给出。

其中易为观察的质心 Xi 。主要目标是最大化聚类数量,在有限的情况下,每个数据点成为其自己的聚类质心。
肘法
计算不同 k 值的类内误差平方和(WSS ),并选择 WSS 最先开始减小的 k。在 WSS-对 k 的曲线图中,这可以看做一个肘部。
这些步骤可以总结为以下步骤:
- 通过在 1 到 10 个聚类之间改变 K 值来计算不同 K 值的 K 均值聚类。
- 对于每个 K,计算总的类内平方和(WCSS)。
- 绘制 WCSS 相对于簇数 k 的曲线
- 图中弯曲(拐点)的位置通常被认为是适当聚类数的指示。

使用肘形法发现最佳 K 值为 5。
最后,我绘制了一个 3D 图,直观地显示了顾客的消费分数和年收入。数据点分为 5 类,用不同的颜色表示,如 3D 图所示。
结果

结论
k 表示聚类是最流行的聚类算法之一,并且通常是从业者在解决聚类任务时应用的第一件事,以了解数据集的结构。K 均值的目标是将数据点分组到不同的非重叠子组中。K 均值聚类的一个主要应用是对客户进行细分,以更好地了解他们,从而增加公司的收入。
参考资料/进一步阅读
背景在当今竞争激烈的世界中,了解客户行为并根据以下因素对客户进行分类至关重要…
towardsdatascience.com](/clustering-algorithms-for-customer-segmentation-af637c6830ac) [## 您需要的最全面的 K-Means 聚类指南
概述 K-Means 聚类是数据科学中一种简单而强大的算法,现实世界中有太多的…
www.analyticsvidhya.com](https://www.analyticsvidhya.com/blog/2019/08/comprehensive-guide-k-means-clustering/) [## 机器学*方法:K 均值聚类算法
2015 年 7 月 21 日撰文:EduPristine k-Means 聚类(又名分割)是最常见的机器学*之一…
www.edupristine.com](https://www.edupristine.com/blog/beyond-k-means)
在你走之前
相应的源代码可以在这里找到。
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/abhinavsagar/Kaggle-Solutions/blob/master/customer_segmentation.ipynb)
联系人
如果你想了解我最新的文章和项目,请关注我的媒体。以下是我的一些联系人详细信息:
快乐阅读,快乐学*,快乐编码。
Python 和 Spark 中的 RFM 客户细分
RFM 细分是根据三个标准(组成 RFM 的首字母缩略词)将客户划分成平等群体的好方法:
- 新*度。客户与公司的最后一次活动或交易已经过去了多长时间?
- 频率。在特定时期内,客户与公司的交易频率如何?
- 货币。在特定时期内,客户在该品牌上花费的金额。
在本文中,我们将看到如何将客户和 Apache Spark 中的代码划分成不同的部分。后者不再需要任何介绍;它已经成为这十年中事实上的大数据平台,我们将利用它的一些基本而深刻的功能。
我们将使用 Python 作为与 Spark 交互的编程语言。Python 正成为数据分析领域的通用语言,因此用这种语言进行 RFM 客户细分是有意义的。
让我们开始吧。
1 设置环境
我们将使用 Jupyter 笔记本应用程序在 Spark 中执行 RFM 细分。为了能够在我们的笔记本中使用 Spark,我们需要导入findspark包以及pyspark模块及其sql模块的组件:
随后,我们创建一个SparkSession来与 Spark 交互。SparkSession 是 Spark 应用程序的统一入口点。
为了确保 spark 连接正常,我们调用 Spark 变量并检查输出:

现在我们应该都准备好了,可以开始我们的 RFM 分析之旅了。
2 关于数据集
在本文中,我们将使用一个电子商务数据集,该数据集包含 2010 年至 2011 年英国注册的无店铺在线零售商的交易,由 UCI 机器学*资源库免费提供。你可以在这里下载数据。
数据集包含我们分析所需的以下相关列:
- 发票号。每次销售的唯一 id。
- 股票代码。每个产品的唯一 id。
- 数量。购买的单位数量。
- 活体内。销售日期。
- 单价。产品的零售价格。
- 客户 ID 。每个客户的唯一 id。
3 数据预处理
数据预处理是数据科学中最重要也是最繁琐的任务之一。大多数数据科学家只花 20%的时间进行实际数据分析,80%的时间用于查找、清理和重组。
我们首先通过使用 Spark 的read方法将 CSV 文件加载到 Spark 数据帧中,在这里我们指定文件的格式以及文件是否包含标题:
通过调用 Spark 的printSchema方法,我们检查 CSV 文件每一列的数据类型:

因为我们没有在列的数据类型上指示 Spark,所以它假设它们都是字符串。但是,有必要更改我们后续分析所需的几个列的数据类型,即:数量、单价、日期。我们还必须添加两个新的计算列:
- 总计,等于单价*数量。需要计算每个客户的货币价值。
- RecencyDays ,这是一个表示 2011 年 12 月 31 日(我们选择的参考日)和发票日期之间天数差异的指标。
Spark 的一个重要特性是能够向现有数据框架添加新的计算列。这是通过它的withColumn方法完成的。这种方法的一个特例是当我们想要改变当前的列时,这是通过将列名放在方法的第一个参数中来实现的。
4 RFM 表的创建
既然我们已经预处理了数据,RFM 表的创建就非常简单了。对于每个客户,我们需要衡量以下指标:
- 新*。我们将把在上一步中计算的最小最*天数视为最*。这是有意义的,因为最*最少天数将为我们提供自客户上次购买以来已经过去的天数。
- 频率。它被计算为顾客购买的次数。
- 货币。它是每个客户所有购买的总价值。
新的 rfm_table 数据帧包含每个客户的 rfm 指标。

我们还没有准备好执行我们的 RFM 分析。在我们深入研究之前,我们应该首先为每个 RFM 指标计算一些有意义的分数。我们为每位客户分配 1 到 4 的分数,其中 1 表示最低分,4 表示最高分。显然,所有指标都达到 4s 的客户被认为是最佳客户。
因为我们从 1 到 4 分配分数,我们可以根据统计四分位数划分 RFM 值。PySpark 中的以下代码执行必要的操作来计算四分位数,并在 RFM 表中创建三个与 RFM 分数相对应的新列。最后,我们将 RFM 分数串联成一列,以便对客户的类型有一个即时的了解。
现在让我们来看看 RFM 表的最终版本:

5 RFM 分析
我们现在可以很容易地查询我们的 RFM 表中的相关业务问题。想知道谁是最好的客户吗?简单的东西:

我们可以根据 RFM 分数对客户进行细分,然后找出每个细分市场中的客户数量。为了可视化这个度量,我们需要将 Spark 数据帧转换成 Pandas 数据帧。这可以通过使用 Spark 的toPandas方法轻松完成。最后,我们将通过使用 Plotly 在条形图中可视化每个细分市场的客户数量,这是一个交互式数据可视化库。

令人惊讶的是,最好的客户构成了最大的细分市场:),但具有讽刺意味的是,最差的客户(所有得分为 1 的客户)是第二大细分市场:(。
除了最佳和最差客户,我们还可以定义以下重要的客户群:
- 高消费新客户 —这个群体由 4-1-3 和 4-1-4 的客户组成。这些客户只交易过一次,但最*他们花了很多钱。
- 消费最低的活跃忠诚客户 —这一群体由细分市场 3–4–1 和 4–4–1 中的客户组成(他们最*交易过,经常交易,但消费最少)。
- 流失的最佳客户 —这个细分市场由第 1-3-3、1-4-3、1-3-4 和 1-4-4 组的客户组成(他们交易频繁,花费很多,但已经过了很长时间)。
决定瞄准哪些客户群以及如何最好地与他们沟通是营销艺术的切入点。
结论
您已到达这篇文章的结尾。在用 PySpark 编写代码的过程中,我们涉及了很多关于客户细分的细节。请到我的 Github 库访问这篇文章的笔记本代码。
感谢您的阅读。
由于这是我第一篇关于媒体的文章,我将非常感谢任何有用的反馈。
基于应用行为分析(逻辑回归)的客户订阅分析和预测
使用 scikit learn library 的逻辑回归算法对客户是否会注册付费订阅进行分类。
我们将根据网站或应用程序上的客户行为进行客户流失分析。我们会对什么样的客户有可能注册一个网站的付费订阅进行分类。在分析
并对数据集进行分类后,我们将能够对有可能注册
付费订阅计划的客户进行针对性的
营销或推荐。
对于这个问题,我们将使用 scikit-learn 库中的逻辑回归算法。这个问题的数据集已经用已经完成的必要特征工程进行了优化。如果你想知道我是如何清理数据以及特征工程是如何完成的,请继续关注,因为我将很快写另一篇关于这个特定数据集的数据清理过程的帖子。
让我们通过首先导入必要的库和数据集来开始这个过程。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from dateutil import parser
%matplotlib qt5
这里%matplotlib qt5 使可视化在一个新窗口中弹出。如果您希望可视化显示在同一个笔记本或控制台中,那么您可以使用%matplotlib inline
dataset = pd.read_csv(“appdata10.csv”)
让我们来看看数据集:

a look at the datset, some of the columns are not seen here cause i couldn’t fit all considering the size
现在,让我们将数据集分为要素和目标变量。目标列名为“已注册”。
X = dataset.drop(columns = 'enrolled')
y = dataset['enrolled']
现在,让我们将数据集分为训练集和测试集。我们需要这样做,以便我们只能在模型训练中使用训练集数据。在有监督的机器学*中,将数据集分成训练集和测试集是很重要的,因为它可以防止过拟合。
为此,我将使用 sklearn 的 train_test_split()函数。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 4)
你可以使用任何随机状态,它只是确保不同环境下的结果是相同的。
现在让我们删除用户列,因为我们不需要它,它也不是一个必需的特性。但是我们需要它来识别用户是否订阅。为此,我们将把用户列保存为标识符。
train_identifier = X_train['user']
X_train = X_train.drop(columns='user')test_identifier = X_test['user']
X_test = X_test.drop(columns='user')
下一步是进行特征缩放。进行要素缩放是为了归一化数据集中不同要素的值。特征缩放帮助我们更快地实现梯度下降。如果数据更加分散,这意味着如果它具有更高的标准偏差,那么与通过要素缩放来缩放数据的情况相比,计算梯度下降将花费相对更多的时间。所以我们把所有的值都放在一定的范围内。这将平等地对待所有列。在上面的数据集中,我们可以看到年龄、星期几、注册的值不在相似的范围内。
为此,我们可以使用 sklearn 的 StandardScaler 函数。
from sklearn.preprocessing import StandardScaler
sc_X = StandardScaler()
X_train2 = pd.DataFrame(sc_X.fit_transform(X_train))
X_test2 = pd.DataFrame(sc_X.fit_transform(X_test))
X_train2.columns = X_train.columns.values
X_test2.columns = X_test.columns.values
X_train.index = X_train.index.values
X_test.index = X_test.index.values
X_train = X_train2
X_test = X_test2
现在让我们进入激动人心的部分;训练数据集。为此,我们将使用来自 sklearn 库的 LogisticRegression。
逻辑回归用于分类问题。当因变量为二分变量(二元变量)时,进行回归分析是合适的。像所有回归分析一样,逻辑回归是一种预测分析。逻辑回归用于描述数据,并解释一个因变量与一个或多个名义变量、序数变量、区间变量或比率水平自变量之间的关系。


from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression(random_state=0, penalty=’l1')
logreg.fit(X_train, y_train)
在这里,我使用惩罚参数来防止一个特征与这个特定数据集中的目标变量高度相关,特别是对于最后一个屏幕变量。
既然模型已经定型,我们就可以在测试集上进行预测了。
y_pred = logreg.predict(X_test)
完成预测后,我们可以进行模型评估,看看我们的模型表现如何。为此,我们将使用五个评估指标,混淆矩阵、准确度分数、f1 分数、精确度分数和召回分数。所有这些评估指标都可以在 scikit learn 中找到。
from sklearn.metrics import confusion_matrix, accuracy_score, f1_score, precision_score, recall_score
cm = confusion_matrix(y_test, y_pred)
accuracy = accuracy_score(y_test, y_pred)accuracy
0.7679
所以我们达到了 79%的准确率。这个模型已经完成了那个精度的下降工作。
现在,让我们绘制混淆矩阵的热图来可视化混淆矩阵。

我们可以看到,正确预测的值比混淆矩阵中的假阳性和假阴性多 3 倍左右。我们可以进一步使用交叉验证来评估模型,并确保我们的模型做了正确的工作。
from sklearn.model_selection import cross_val_score
accuracies = cross_val_score(estimator=logreg, X=X_train, y= y_train, cv=10)
print("Logistic accuracy: %0.3f (+/- %0.3f)(accuracies.mean(),accuracies.std()*2))Output: Logistic accuracy: 0.767 (+/- 0.010)
所以我们可以说,我们的模型正在做正确的工作,并且是一个逻辑回归的下降工作。
我们可以使用不同的参数调整方法进一步优化我们的模型。但是现在我不打算优化这个模型。如果你想了解参数/超参数调整,你可以搜索 GridSearch,随机搜索,向后消除。我将把这些留到下一篇文章。
这是我的第一个媒体帖子,所以请随时给我建议和建设性的批评,还有我做这个分类的方法。谢谢你。
改进默认的 Mac 终端以提高生产率和改进界面
简单的外壳定制技术

Photo by Goran Ivos on Unsplash
如果你像我一样,你从事许多项目,每个项目可能位于不同的目录中,并可能涉及多个分支。最*,我对我的终端进行了定制,使其信息量更大,更易于阅读和使用。
在本文中,我将向您介绍我定制终端的步骤。有很多方法你可以去做,但在这里我提出了一个我发现最简单,最适合我的需要。
在本文中,我们将从这里开始:

Old
到这里:

New
安装 iTerm2
iTerm2 扩展了 MacOS 终端,并添加了许多有用的特性。我喜欢的两个特性包括使用 Shell > Split vertical with Current Profile(水平分割也可以)垂直分割窗格的能力,以及配置该终端的众多选项。你也可以在这里找到的其他功能。
- 前往https://www.iterm2.com/downloads.html。
- 下载位于稳定版本下的版本。它将下载 zip 文件。
- 解压 zip 文件,新文件将显示为 iTerm.app 。只要打开这个应用程序,你就进入了 iTerm2。

Two iTerm2 terminals opened side-by-side
把壳牌改成 ZSH
下一步是将我们的后端从bash改为zsh。
- 在 iTerm2 中,键入命令
which zsh。您应该会看到输出为/bin/zsh。如果没有显示,你需要使用brew install zsh下载它。然后输入which zsh,看看你是否能看到一个新的路径/usr/local/bin/zsh。 - 接下来,键入命令
sudo vi /etc/shells,然后输入您的密码。 - 文件将会打开。按键盘上的
i进入插入模式。在其他外壳后添加线/bin/zsh和/usr/local/bin/zsh。按 escape 退出插入模式。完成后,通过键入:wq保存更改,即写入并退出。我们现在可以切换到 zsh。 - 键入命令
chsh -s $(which zsh)。外壳现在更改为 zsh。 - 关闭 iTerm2,然后再次打开它。

Adding zsh to the list of shells

Changing the shell to zsh
安装我的天啊
哦,我的 zsh 在 Zsh 上面工作。它为我们提供了一个配置文件~/.zshrc,并允许我们应用主题来使 iTerm2 更具吸引力和可用性。GitHub 回购可在这里获得。
- 运行命令
sh -c “$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"来安装包。 - 重新打开 iTerm2,它将启用 Oh My Zsh。

Installing Oh My Zsh
现在不会有视觉上的差异,但是我们会有~/.zshrc文件,在那里我们可以对 iTerm2 进行更新。
使用插件
我将使用三个插件,即 git、语法高亮和建议。
- Git 已经包含在插件列表中,所以我们不会下载它。
- 对于另外两个插件,我们必须将它们克隆到正确的目录中。
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlightinggit clone https://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions

Installing plugins
一旦完成,使用命令open ~/.zshrc打开.zshrc文件。将两个插件名zsh-syntax-highlighting和zsh-autosuggestions添加到括号内,在 git 后各占一行。只要保存文件,我们就可以开始了。

Updated plugins to include the new plugins
重新打开 iTerm2,以便新的更改生效。看看下面的图片,你可以看到文本cd现在是不同的颜色,在写完文本Personal后,它建议下一个文件夹Important Work。插件运行得非常好。

Plugins in action
实施电力线设计
我们需要更新~/.zshrc文件,以显示类似电力线的设计,同时显示我们所在的分支,以及分支中是否有任何更改。
- 使用
open ~/.zshrc打开.zshrc文件。 - 然后把 ZSH 主题改成
agnoster。

agnoster theme
正如我们所看到的,整个路径是可见的,只要我们在 git 存储库中,就会显示当前分支的名称。例如,在上面的例子中,我在master分支中。如果有尚未提交的更改,分支名称后会出现一个点。
更新颜色
考虑到文本由于颜色的选择而可读性不强,我决定将颜色改为预设的配色方案。
- 当在活动 iTerm2 窗口中时,转到顶角,在 iTerm2 中选择首选项>配置文件>颜色。
- 在窗口右下角的
Color Presets...下选择Tango Dark,颜色将会更新。

Select Tango Dark color preset

Updated colors for the shell
添加介绍信息
为了好玩,我们还可以添加一条消息,每次我们都会打开一个新的 iTerm2 窗口或标签。我会显示我的名字,日期和一个随机的财富。
- 安装
fortune包,它将帮助我们使用brew install fortune显示随机财富。 - 使用
open ~/.zshrc打开.zshrc。 - 转到最后,键入以下命令,每当我打开 iTerm2 窗口或选项卡时,该命令将显示我的姓名和当前日期。
echo "
|| NAME: KARAN BHANOT
|| DATE: $(date)
||
|| $(fortune)
"

Intro message added
结论
最后,我们完成了。我们已经对我们的终端做了许多修改,现在它为我们提供信息,并使它看起来更悦目。
希望你喜欢这篇文章。请分享你的想法、想法和建议。
通过回访定制您的培训循环

在我的上一篇文章中,我们学*了如何从头开始编写 PyTorch 训练循环。我们从一个混乱的循环版本开始,如下所示:

我们把它变成了一个更简洁的版本,看起来像这样:

在这个过程中,我们学会了如何通过为数据集、数据加载器、优化器等等编写类来将相似的东西打包在一起。在本文中,我们将进行更多的打包,并学*如何定制我们的训练循环,以实现深度学*中的复杂技术。
先说我们的 fit 函数。
数据群发
我们的fit()函数包括我们的模型、损失函数、优化器和两个数据加载器(训练和验证)。
然而,如果我们仔细想想,训练和验证数据加载器并不是两件独立的事情。他们是我们的 数据 。它们必须是一个东西(或者至少是从它派生出来的)。因此,我们将这两个数据加载器合并成一个名为DataBunch.的类

如果我们可以将数据中的 类的数量 传递给 DataBunch,那也很方便。这是通过传递c参数来完成的。我们现在可以创建一个 DataBunch 对象,如下所示。

然后,我们可以使用这个对象通过自动识别输入和输出单元的数量来方便地创建模型。

学*者
下一步,我们要做的是结合我们的模型,优化和损失函数。
很多时候,在进行深度学*的实验时,我们想要测试这些东西的一个 组合 。这涉及到对我们代码的各个部分进行大量的修改。但是,将它们组合成一个对象并仅对该对象进行更改是有意义的。这将有助于我们轻松做出明智的决定。
因此,我们将模型、优化器和损失函数组合成一个名为Learner.的类

我们现在可以使用learn.model、learn.opt和learn.loss_func.来访问它们。我们在训练循环中进行必要的更改。

需要复试
在这一点上,我们有一个非常整洁的训练循环。然而,这些并不是我们想在训练循环中做的唯一事情。我们可能需要添加一些东西,比如
超参数调度
你如何决定学*速度?如果太慢,你的神经网络将会花很长时间去学*(试着用…
sgugger.github.io](https://sgugger.github.io/how-do-you-find-a-good-learning-rate.html)
或 正规化技术
了解如何使用权重衰减来训练更好的模型
becominghuman.ai](https://becominghuman.ai/this-thing-called-weight-decay-a7cd4bcfccab) [## 神经网络中的正则化
用于正则化神经网络的各种方法的概述
becominghuman.ai](https://becominghuman.ai/regularization-in-neural-networks-3b9687e1a68c)
或者混合精确训练
图像分类是深度学*的 Hello World。对我来说,这个项目是使用胸部检测肺炎…
becominghuman.ai](https://becominghuman.ai/mixed-precision-training-using-fastai-435145d3178b)
或者做更复杂的事情比如甘斯。这些只是我们现在知道的事情。更多的调整不断出现。
为了实现这些调整,我们必须为每个调整编写一个单独的训练循环。当我们想使用它们的组合时,一切都会变得一团糟。
这就是 回调 出现的地方。他们帮助我们有效地实现这些调整。让我们看看怎么做。
了解回访
我们首先创建自己的 回调对象 。在这些对象中,我们决定了在训练的某一点上我们想要做什么。比如 fastai 中有一个回调叫做SaveModelCallback()。该回调检查在每个时期结束时我们是否有更好的模型,如果有,则 保存模型 。这样,我们不仅得到了最后的参数,而且得到了最好的参数。
**
同样,我们可以为学*速率调度、数据增强的混合或梯度裁剪编写回调。点击查看 fastai 回调的详细列表。
下一步是实现一个CallbackHandler()。

这个类有两个主要目的。
- 它给我们一个地方来存储我们所有的回调(cbs)。
- 它允许我们很容易地调用所有单独的回调函数。例如,如果我们有 3 个回调函数在一个纪元结束时做了一些事情,那么
cb.on_epoch_end()将从每个回调对象中调用on_epoch_end()方法。
最后一步是将这些回访纳入我们的培训循环。我们使用和以前一样的循环,只是稍加修改。在我们的 fit 函数中,我们确保我们遍历了all_batches().,并且在所有批次中,我们编写了每个批次要遵循的步骤。

这给了我们很大的灵活性来实现我们想要的东西,而不需要修改原始的训练循环。它让我们在训练期间的不同时间点定义不同的行为,如提前停止或打印各种指标。
最后,我们还需要一个Callback class,我们的回调对象将从其中继承。这个类有所有可能的行为,所以如果我们的对象没有这些行为,我们的代码不会中断。

如果你不喜欢从超类继承,那么你可以使用if hasattr(cb, 'on_epoch_end')让它更加灵活。
我将用一个理解回调的非常简单的例子来结束这篇文章。我们从一个做一些计算的函数开始,然后休眠一段时间。这个函数代表了深度学*中的一个时代。

我们可以在计算前后向该函数添加行为,如下所示。

然后,我们可以写一个回调函数来处理这些行为。

这就是本文的全部内容。
如果你想了解更多关于深度学*的知识,你可以看看我下面的深度学*系列。
我所有关于深度学*的文章的系统列表
medium.com](https://medium.com/@dipam44/deep-learning-series-30ad108fbe2b)
参考文献:
[1]https://docs.fast.ai/callbacks.html#SaveModelCallback
[2]https://medium . com/@ Edward easling/implementing-callbacks-in-fast-ai-1c 23 de 25 b 6 EB*
[CVPR 2017] OpenPose:使用部分亲和力场的实时多人 2D 姿势估计
今天的话题是来自 CVPR 2017 的一篇名为“利用局部亲和场的实时多人 2D 姿态估计”的论文。这项工作对计算机视觉社区有着非凡的贡献,因为:
- 它基于其自底向上的方法而不是其他作品中基于检测的方法,为多人 2D 姿态估计提供了一种实时方法。
- 作者开源并将他们的工作扩展到第一个实时多人系统,以联合检测单一图像上的人体、手、面部和脚关键点(总共 135 个关键点)——open pose。今天,这个库被广泛用于各种研究工作和生产应用。
关于这个 OpenPose 系统有很多帖子。然而,我们的帖子有一些不同之处:
- 我们将回顾作者的期刊文章,2018 年发表在 arXiv 上,比 CVPR 2017 版更准确,速度更快。
- 对于其他博文中通常会跳过的论文后处理步骤,我们会提供一个详细的解释。
该文件的一些要点:
- 通过零件亲缘关系字段(PAF)的关联分数的第一次自下而上展示。
- 不变运行时间到图像中的人数。
- 精细化网络使速度和精度分别提高了 200% 和7%(2018 年期刊版)
- 可以将推广到任何关键点关联任务如车辆关键点检测。
概述
- 介绍
- 体系结构
- 置信图
- 零件亲缘关系字段(PAF)
- 多级 CNN
- 使用 PAFs 的多人解析
- 车辆姿态估计
- 履行
- 参考
介绍

人体姿态估计是理解图像和视频中人的核心问题。在单人姿态估计中,通过假设图像只有一个人,问题被简化。多人姿态估计更困难,因为在一幅图像中有多人。多年来,有许多工作致力于解决这个问题。
一种常见的方法是遵循两步框架,该框架使用人体检测器并解决每个人体的姿态估计。这种方法的运行时间往往会随着图像中人数的增加而增加,并使实时性能成为一个挑战。
在本文中,作者提供了一种自底向上的方法,其中通过模型检测身体部位,并使用最终解析来提取姿态估计结果。这种方法可以将运行时间复杂度从图像中的人数中分离出来。
作者也在 OpenPose 库开源他们的作品。这个 OpenPose 系统提供了易于使用的管道,带有命令行接口、Python API、Unity 插件。系统支持NVIDIA GPU(CUDA)、AMD GPU(OpenCL)和纯 CPU。他们甚至提供可移植的可执行二进制文件,可以简单地下载并在 Windows 上使用。
以下是社区分析的一些视频:
体系结构

Overall Pipeline of the OpenPose architecture. (Source)
上图是 OpenPose 的整体流水线。有几个步骤:
- 首先,图像通过基线网络提取特征图。在论文中,作者使用了 VGG-19 模型的前 10 层。
- 然后,用多级 CNN 处理特征图,以生成: 1)一组零件置信度图和 2)一组零件亲和域(PAF)
- 部位置信度图:一组身体部位位置的 2D 置信度图 S 。每个关节位置都有一张地图。
- 零件亲和域(PAF):一组 2D 矢量场 L ,对零件之间的关联程度进行编码。
- 最后,通过贪婪算法处理置信图和部分亲和场,以获得图像中每个人的姿态。
置信图
置信图是一种 2D 表示法,表示特定身体部位可以在任何给定像素中定位的信念。
设 J 为身体部位(关节)的数量。然后,置信图是:

总之,每个地图对应一个关节,并且与输入图像大小相同。
零件亲缘关系字段(PAF)
部位亲和场(PAF) 是对身体部位之间的非结构化成对关系进行编码的一组流场。
每对身体部位都有一个 (PAF) ,即脖子、鼻子、手肘等。
设 CC 为身体部位的对数。然后,部分关联字段(PAF)是:

如果一个像素在一个肢体(身体部位)上,该像素的 Lc 中的值是从开始关节到结束关节的 2D 单位向量。
多级 CNN

这是来自 OpenPose 的 2018 journal 版本的多级 CNN 的架构。有如下几个步骤:
- 阶段 1 :从基网络的特征图中计算零件亲和域(PAF)、 L1 、 F 。让ϕ1 成为第一阶段的 CNN。

- 阶段 t 到阶段 Tp :使用特征图 F 和先前的 PAFLt 1改进先前阶段的 PAF 预测。让ϕt 成为舞台上的 CNN

- 在 Tp 迭代之后,从最*更新的 PAF 预测开始,对置信图检测重复该过程。设 ρt 为阶段 t 的 CNN。该过程重复进行 Tc 迭代。

- 最后的 S 和 L 是置信图和零件亲和域(PAF),它们将由贪婪算法进一步处理。
注意:
- 这个多级 CNN 来自 2018 期刊版。在最初的 CVPR 2017 版本中,他们在每个阶段都改进了信心图和零件亲和域(PAF)。因此,它们在每个阶段都需要更多的计算和时间。在新方法中,作者发现新方法在速度和精度上分别提高了 200% 和 7% 。
使用 PAFs 的多人解析
在本节中,我们将向您概述贪婪算法,该算法用于从置信度图和零件相似性字段中解析多人的姿势。
许多其他博客帖子显示了这个问题有多难。然而,没有很多详细的解释这个步骤。
幸运的是,我们发现了一个由 tensorboy 完成的这篇论文的优秀实现,它有详细的文档和简单明了的代码。我们建议您检查一下这个存储库,并亲自尝试一下。
解析过程可以总结为三个步骤:
- 步骤 1 :使用置信图找到所有关节的位置。
- 第 2 步:使用第 1 步中的部件亲和域和关节,找到哪些关节组合在一起形成肢体(身体部位)。
- 第三步 : 关联属于同一个人的肢体,得到最终的人体姿态列表。
步骤 1:使用置信图找到所有关节位置。
输入:
- 置信图:

- 上采样比例:输入图像和置信图的宽/高差异。
输出:
- joints_list :大小为 JJ 的关节位置列表,其中每一项都是峰值列表(x,y,概率)。
- 例如,关节 _ 列表长度为 18,对应 18 个关节位置(鼻子、脖子等)。)并且 joints_list 中的项目是不同长度的列表,其存储每个关节位置的峰值信息(x,y 位置和概率分数)。
流程:
对于从 1 到 J 的每个关节:
- 在置信图中获取相应的接头 2D 热图。
- 通过对 2D 热图进行阈值处理找到峰值。
- 对于每个峰值:
- -在堆中的峰周围取一小块
- -使用上采样标尺放大补丁。
- -在放大的补丁中获得最大峰值位置。
- -将峰信息添加到接头的峰列表中
第 2 步:使用第 1 步中的“部分相似性”字段和关节,找到哪些关节组合在一起形成肢体(身体部分)。
输入:
- 关节 _ 列表:第一步的输出。
- 零件关联性字段(PAF):

- 上采样比例:输入图像和 PAFs 贴图的宽高不同。
- 中间点数:获取 PAFs 值的源关节和目的关节之间的中间点数。
输出:
- connected_limbs :大小为 CC 的连通分支列表,其中每一项都是找到的该类型的所有分支的列表。
- 每个肢体信息包含:源关节的 id、目标关节的 id 和连接程度的分数。
流程:

第三步:关联属于同一个人的肢体,得到最终的人体姿态列表。
输入:
- 关节 _ 列表:来自步骤 1
- 连接 _ 肢体:来自步骤 2
输出:
- 姿势:图像中每个人的姿势列表。每个项目都包含该人的关节位置。
流程:
对于每个肢体类型和该类型的连接肢体中的每个连接:
- 找到与当前连接的任一关节相关联的人
- 如果没有人;用当前连接创建一个新的人
- 如果有 01 人:将当前连接添加到该人
- 如果有 02 人:将这 02 人合并成 01 人。
移除关节很少的人。
车辆姿态估计
在 2018 年的期刊版本中,作者证明了这种方法可以应用于任何关键点标注任务。下图是车辆关键点数据集的结果:

履行
- OpenPose 的最佳实现来自用 C++实现的 OpenPose 库。这个 OpenPose 系统提供了易于使用的管道,带有命令行接口、Python API、Unity 插件。系统支持NVIDIA GPU(CUDA)、AMD GPU(OpenCL)和纯 CPU。他们甚至提供可移植的可执行二进制文件,可以简单地下载并在 Windows 上使用。
- 一个优秀的 PyTorch 实现,由 tensor boy实现,有详细的文档和简单的代码。
- ildoonet 提供了具有多个基础网络的 Tensorflow 实现。
参考
- 【1】曹哲,托马斯·西蒙,施-韦恩,亚塞尔·谢赫,利用部分亲和场的实时多人 2D 姿态估计(2017),CVPR 2017 口述
- 【2】曹哲,托马斯·西蒙,施-韦恩,亚塞尔·谢赫,OpenPose:使用部分亲和场的实时多人 2D 姿态估计(2018),a rXiv
- [3] OpenPose:用于身体、面部、手和脚估计的实时多人关键点检测库
- [4] tensorboy 的实现
- [5] ildoonet 的实现
我的评论
图像分类:【NIPS 2012】AlexNet
图像分割:【CVPR 2019】Pose 2 seg姿态估计:【CVPR 2017】open Pose
姿态跟踪:【CVPR 2019】STAF
[CVPR 2019]利用递归时空亲和场的高效在线多人 2D 姿态跟踪
在单个 GPU 上以 30 fps 的速度实时检测和跟踪多人的 2D 姿势。

Real-time detect and track 2D poses of multiple people at 30 fps on a single GPU. Source
在本帖中,我们将回顾来自 CVPR 2019 的一篇名为“具有递归时空亲和场的高效在线多人 2D 姿态跟踪”的新论文。
作者提出了一种在线方法,可以在单个 GPU 上以 30 fps 的速度有效地同时检测和跟踪多人的 2D 姿势。
根据作者的说法,这是“目前最快和最准确的自下而上方法,它对场景中的人数和摄像机的输入帧率都是运行时不变的和准确性不变的。”
该文件的一些要点:
- 鲁棒且对输入帧速率不变,即使在 6Hz 输入时也是如此
- 处理快速移动目标和摄像机移动
- 运行时间对帧中的人数不变。
- 提出时空亲和场(STAF) 编码跨帧的关键点之间的连接。
- 提出一种新颖的跨肢体交叉链接的时间拓扑,可以处理移动目标、摄像机运动和运动模糊。
这里是作者的结果。
概述
介绍
整体管道
- VGG 特色
- PAF 和关键点热图
- 时间亲和场
- 推理
交联拓扑
结果
限制
履行
参考
我的评论
介绍
在过去的几年中,人体姿态和跟踪受到了极大的关注。最*推出的 PoseTrack 数据集是一个大规模的视频数据语料库,使得这个问题在计算机视觉社区中得以发展。
关于这个问题还有其他几部作品。然而,这些方法都不能像通常那样实时运行:
- 遵循自上而下的检测和跟踪任务方法,随着人数的增加,需要更多的计算。
- 需要离线计算或帧堆叠,这降低了速度,并且没有获得比匈牙利算法基线更好的跟踪结果。
在本文中,作者的目标是建立一个真正在线和实时的多人 2D 姿态估计器和跟踪器。作者以循环的方式在视频上工作,以使该方法实时。它们通过组合 1)关键点热图 2)零件亲和场和 3)时空亲和场(STAF)来利用来自前一帧的信息。
作者在 2017 年 CVPR 的 OpenPose 中扩展了零件亲和域。这篇论文的作者也是 OpenPose 的作者。请随意查看我们之前在 OpenPose 上的博文。
整体管道

The overall pipeline of the STAF algorithm. Source
上图是时空亲和场(STAF) 算法的整体流水线。视频帧在时间上以循环方式进行处理,包括:
- 提取 VGG 特征。
- 提取零件亲缘关系字段(PAF)
- 提取关键点热图
- 跨帧提取关键点之间的连接作为时间相似性场(TAFs) 。
VGG 特色

VGG features. Source
每一帧由 VGG 骨干网处理,以提取 VGG 特征。这部分不需要以前的帧信息。
对于视频时间 t 处的帧 It ,它们计算如下:

PAF 和关键点热图

Part Affinity Fields (PAFs). Source

Keypoints Heatmaps. Source
PAFs 和关键点热图与开放姿态方法非常相似,除了它们也将使用来自前一帧的信息。
在本文中,有三种计算 PAFs 的方法:
- 使用 5 个先前的帧数据。
- 仅使用 1 个前一帧。
- 同时估计 PAF 和 taf。
在他们的实验中,他们发现:
- 方法 1 产生了好的结果,但是由于递归阶段,它是最慢的。
- 方法 2 提高速度,而性能没有大的损失。
- 方法 3 最难训练,但速度最快。
在这篇博客中,我们只讨论方法 2。其他方法,请查看原论文。
方法 2 在单次通过中计算 PAF 和关键点,如下所示:

时间亲和场
时间亲合场(taf)编码跨帧的关键点之间的连接。
这些 taf 是向量场,指示每个身体关节将从帧 I t1****移动到帧 It 的方向。Andreas Doering 等人在论文中也介绍了 taf。
在下图中,taf 表示为蓝色箭头 RtRt。

Connections between keypoints across frames. Source

Temporal Affinity Fields (TAFs). Source
taf 依赖于来自前一帧和当前帧的 VGG 特征和 PAF 以及来自前一帧的 taf,如下所示:

推理

Inference. Source
推断出的 PAF 和 taf 在推断出完整的姿势并将它们跨帧与唯一的 id 相关联之前,都按照它们的分数进行排序。
以下是步骤:遍历排序列表中的每个 PAF:
- 如果 PAF 的两个关键点都没有指定,初始化一个新姿势
- 01 关键点已分配:添加到现有姿势
- 两个关键点都被赋值:将姿势中 PAF 的分数更新到同一个姿势。
- 两个关键点被分配到不同的姿态:合并两个姿态
- 使用前一帧中最频繁出现的关键点 id为当前帧中的每个姿势分配 id。
以下是原始论文中的详细算法:

Algorithm: Estimation and tracking of keypoints and STAFs. Source
交联拓扑
STAFs 有三种可能的拓扑变体。

Topology variants for STAFs. Source
拓扑 A 是关键点 TAF,并且当关键点具有最小运动或者当新人出现时缺乏关联属性。
拓扑 B 和拓扑 C 是交联分支拓扑。作者证明了交联肢体拓扑可以解决来自拓扑 A 的运动问题。当有最小的运动时, TAFs 就变成了那个肢体的 PAFs 。

Keypoint TAF vs Cross-Linked Limb TAF. Source
在论文实验中,拓扑 C 与拓扑 B 相比表现不佳。
结果
- 该方法即使在帧率下降到 6Hz 时也能达到高精度。

Camera framerate experiments of STAFs. Source
- 该方法具有较高的准确率和较快的推理速度。

Camera framerate experiments of STAFs. Source
- 该方法可以在 PoseTrack 数据集上取得有竞争力的结果。

Camera framerate experiments of STAFs. Source
限制
这项工作有一些限制:
- 本文没有嵌入重新识别模块来处理人离开和重新出现在场景中的情况。
- 这种方法不能处理场景变化。因此,可能需要检测场景变化,并从该帧重新开始该过程。
履行
本文在作者资源库 soulslicer/openpose 开源。
这是来自 OpenPose 库的一个分支。在不久的将来,它们将被合并,你可以直接从 OpenPose 使用它。
参考
- [1] Yaadhav Raaj,Haroon Idrees,Gines Hidalgo,亚塞尔·谢赫,使用递归时空亲和场的高效在线多人 2D 姿态跟踪,CVPR 2019
- 【2】曹哲,托马斯·西蒙,施-韦恩,亚塞尔·谢赫,使用部分亲和场的实时多人 2D 姿态估计(2017),CVPR 2017
- 【3】Andreas Doering,Umar Iqbal,Juergen Gall,联合流:多人跟踪的时间流场,CoRR,abs/1805.04596,2018
- 项目页面:使用递归时空亲和场的高效在线多人 2D 姿态跟踪
- Github 代码:soulslicer/openpose
我的评论
图像分类:【NIPS 2012】AlexNet
图像分割:【CVPR 2019】Pose 2 seg
姿态估计:【CVPR 2017】open Pose
姿态跟踪:【CVPR 2019】STAF
[CVPR 2019] Pose2Seg:无检测人体实例分割
在本帖中,我们将回顾来自 CVPR 2019 的论文“pose 2 seg:检测自由人体实例分割”。提出了一种新的人体实例分割方法,该方法基于人体姿态而不是提议区域检测来分离实例。
本文的一些亮点:
- 基于姿态的人体实例分割框架比基于 SOTA 检测的方法能够达到更好的准确性并且能够更好地处理遮挡。
- 一个新的基准“被遮挡的人(OCHuman)”,它关注被遮挡的人,带有包括边界框、人姿势和实例遮罩的注释。
概述
- 介绍
- 被遮挡人类基准(OCHuman)
- 体系结构
- 实验
- 履行
- 参考
介绍
人体姿态估计和分割是更好地理解人体活动的重要信息。有很多研究集中在这个主题上。最流行的深度学*方法之一是 Mask R-CNN,它是一个简单而通用的对象实例分割框架。
即使通过像 Mask-RCNN 这样的方法,也可以检测对象并为图像中的每个实例生成分割掩模。这些方法存在一些问题:
- 这些方法首先执行对象检测,然后使用非最大值抑制(NMS) 移除冗余区域,并从检测包围盒中分割出对象。当同一类别的两个对象有较大重叠时,NSM 会将其中一个作为冗余的提议区域,并将其消除。
- “人”是一个特殊的类别,可以基于姿态骨架来定义。目前的方法如 Mask-RCNN 没有利用姿态信息进行分割。
本文提出的方法 Pose2Seg ,是专门为人体实例分割而设计的,具有以下优点:
- 使用自下而上的方法,这种方法基于姿态信息而不是边界框检测。
- 旨在解决每个人类实例被一个或几个其他实例严重遮挡的问题。
被遮挡人类基准(OCHuman)
遮挡人体基准(OCHuman) 本文引入数据集,强调遮挡是研究人员研究的一个挑战性问题,并鼓励算法变得更适用于现实生活情况。
遮挡人体基准(OCHuman) 数据集包含 8110 带有 4731 图像的详细注释人体实例。平均而言,一个人的超过 67%的包围盒区域被一个或几个其他人遮挡。
以下是一些来自 OCHuman 数据集的图片:

Source: OCHuman Dataset
OCHuman 数据集
这是一个比较 COCO 数据集和 OCHuman 数据集的表格。

如你所见,COCO 包含很少的被遮挡的人类案例,当面对被遮挡的人脸时,它不能帮助评估方法的能力。
OCHuman 是为与人类相关的所有三个最重要的任务而设计的:检测、姿势估计和实例分割。
奥乔曼最重要的方面是平均马西欧是 0.67 。这意味着超过 67% 的人的包围盒区域被一个或几个其他人遮挡。这在视频监控或店内人体行为分析等实际应用中是一个非常常见的问题。
体系结构
Pose2Seg 的结构如下图所示:

Source: Pose2Seg project page http://www.liruilong.cn/projects/pose2seg/index.html
该方法的步骤可以描述如下:
- 首先,模型将图像和人体姿态作为输入。人体姿态可以是其他方法的输出,如 OpenPose 或数据集的地面实况。
- 整个图像通过基网络来提取图像的特征。
- 对准模块仿射对准用于将感兴趣区域对准到统一尺寸。可以想象这个模块会从大图中提取多个固定大小的区域。每个固定大小的区域对应于图像中的每个人。然后,仿射对齐区域将执行仿射变换,以将每个姿势与姿势模板之一对齐。
- 仿射对齐的对齐输出将与骨架特征连接,并馈入分割模块以生成分割掩模。
- 骨架特征:简单来说就是零件亲和场(PAF),它是每个骨架的双通道矢量场图。这是 OpenPose 的输出。
- SegModule :是一个 CNN 网络,以 7 x 7 stride-2 conv 层开始,后面是几个标准剩余单元。然后,双线性上采样层用于恢复分辨率,并且1×1 conv 层用于预测遮罩结果。
- 最后,使用来自仿射对齐的仿射变换的逆变换,将每个人的分割掩模组合成一个最终分割掩模。
实验
咬合性能
- Pose2Seg 在 OCHuman 数据集上可以达到比 Mask R-CNN 高* 50%的性能。作者还使用地面实况关键点作为输入进行了测试,准确度提高了一倍多 (GT Kpt) 。

- 关于闭塞情况的一些结果:

一般情况下的性能
- Pose2Seg 在 COCOPersons 数据集上也能达到比其他方法更高的准确率。

履行
这篇论文的官方 PyTorch 代码可以在 https://github.com/liruilong940607/Pose2Seg 找到
参考
- 【1】张,宋海和李,瑞龙和董,辛和松香,保罗 L 和蔡,子希和,韩和杨,丁成和黄,郝智和胡,石世民,Pose2Seg:检测自由人体实例分割(2019),2019
- 【2】曹哲,托马斯·西蒙,施-韦恩,亚塞尔·谢赫,OpenPose:利用局部亲和场的实时多人 2D 姿态估计(2017),CVPR 2017 口述
我的评论
图像分类:【NIPS 2012】AlexNet
图像分割:【CVPR 2019】Pose 2 seg
姿态估计:【CVPR 2017】open Pose
姿态跟踪:【CVPR 2019】STAF
CycleGAN:机器学*如何学*不成对的图像到图像的翻译
我最*读了 CycleGAN 的论文(链接),我发现它非常有趣,因为 CycleGAN 模型具有不可思议的能力,可以准确地将图像变成它们不是的东西(例如,将一匹马的图片变成一匹斑马的图片)。非常凉爽。让我们深入了解它的工作原理。

Some of CycleGAN’s applications (left to right): changing a Monet painting to a real-world picture, changing zebras to horses, changing a picture of a location in the summer to a picture of the same location in the winter. All of the applications can be reversed as well. Image credits to Zhu et al., the authors of the CycleGAN paper.
甘斯。生成对抗网络 CycleGAN 的基本思想。GANs 有一个特殊的损失函数:对抗性损失。基本上,GAN 由两种不同的机器学*模型组成。首先是发电机。这个模型就是生成图像的模型(顾名思义)。还有鉴别器。其目的是试图区分由生成器提供给它的真实图像和虚假图像。对抗损失是整体 GAN 试图优化的函数。可以把它想象成生成器和鉴别器在争夺哪个是更好的模型。当损失被优化时,发生器(理论上)正在创建鉴别器无法从真实图像中区分的图像。
在我们讲述一些机器学*金融应用之前,我们先来了解一下什么是机器学*。机器…
www.datadriveninvestor.com](https://www.datadriveninvestor.com/2019/02/08/machine-learning-in-finance/)
基本原理是,GAN 能够生成任何事物的假图像,只要它有足够多的该事物的现有示例。我之前使用了一个简单的 GAN 来生成鞋子的图像,你可以在这里找到。

Representation of a GAN that generates an image of a shoe.
CycleGAN。cycle GAN 与标准 GAN 的不同之处在于,它不会从随机噪声中生成图像。它使用给定的图像来获得该图像的不同版本;这是图像到图像的转换,允许 CycleGAN 将马变成斑马。然而,图像到图像的翻译并不是 CycleGAN 独有的功能;CycleGAN 是首批允许不成对图像对图像训练的型号之一。这意味着你不需要有一匹马的图片,也不需要知道这匹马作为斑马在数据集中的样子。相反,你可以分别拥有一群马和一群斑马。这在无法获得配对数据的情况下很有用(例如,如果您希望能够从一匹马生成一个斑马版本,那么为了获得配对数据而将每匹马都画成斑马是不合理的)。不成对的图像到图像翻译的特征是非常强大的,并且有许多应用。

The CycleGAN paper’s example of paired vs. unpaired data. Paired data is from changing a pencil sketch of an object to its real life counterpart. Unpaired data consists of images vs. paintings. Image credits to Zhu et al., the authors of the CycleGAN paper.
应用。除了把马变成斑马,还有更多方法可以应用 CycleGAN。这是一个非常通用的型号,如此通用,它还可以用来将一个苹果变成一个橙子!是啊,不是最强的例子,所以这里有一些其他的。
- 根据建筑蓝图生成建筑的真实效果图。
- 创建一个位置在每个不同季节的形象。
- 把画变成真实的图像。
- 根据警方的素描渲染出嫌疑人的真实面部表情。
- 增强照片的各个方面,使它们看起来更专业。
方便。光有一堆应用是不够的,还需要非常好用。CycleGAN 的可用性极高,因为它不需要成对的数据。通常很难获得大量精确的配对数据,因此使用高精度非配对数据的能力意味着无法访问复杂(且昂贵)配对数据的人仍然可以进行图像到图像的翻译。对于像我这样的机器学*爱好者来说,这是个好消息!你可以通过 GitHub 上的官方资源库使用 CycleGAN。
我在下面列出了一些你可能感兴趣的其他资源。
CycleGANs 创造计算机生成的艺术
对 CycleGANs 的解释和计算机生成艺术的演示

CycleGANs 是指能够拍摄图像并生成反映某种类型转换的新图像的 g an 的子集。CycleGANs 最酷的部分是你不需要成对的图像。这对于没有成对图像的情况非常有用。例如,如果你想把斑马的图片转换成马的图片。这种数据可能是不可能收集的,除非我猜如果你画斑马和马…有足够的创造力,它们也可以用于创建计算机生成的艺术,实际上看起来很好!

This figure is taken from the original CycleGAN paper.
在这里,我将概述一下 CycleGANs 是如何工作的。这篇文章假设你至少对 GANs 有一些了解。因此,如果你不熟悉 GANs,可以看看我的博客文章“面向初学者的生成对抗网络(GANs):生成分心司机的图像。”
在这篇文章中,我使用了一个示例 CycleGAN,它被训练来将森林的图片转换成森林的抽象画。我用来生成这些图像的代码可以在我的 GitHub 上找到。
数据集
在这里,数据集由森林图像和抽象画组成。我用关键词“森林”和“著名的抽象画”从谷歌图片上刮下了这些图片为此,我使用了 Python 包谷歌图片下载。下面是几个例子。

车型
训练 CycleGAN 时,需要训练四个神经网络:
- 生成抽象画图片的生成器(抽象画 生成器)。
- 生成森林图片的生成器(森林 图像生成器)。
- 能辨别真假抽象画的鉴别器(抽象画鉴别器)。
- 能够辨别森林真假图片的鉴别器(森林图像鉴别器)。
如果你已经熟悉 GANs,你会发现这里真的没有什么新东西。新颖之处在于如何训练这些网络。
模特培训
在进入细节之前,让我对如何训练 CycleGANs 做一个高层次的概述。首先,我们拍摄一个森林图像,使用抽象画生成器创建一个假的抽象画,然后我们拍摄假的抽象画,使用森林图像生成器重新创建原始的森林图像。我们还从另一个方向出发,拍摄一幅抽象画,使用森林图像生成器创建一幅假森林图像,然后拍摄假森林图像,使用抽象画生成器重新创建原始的抽象画。这个关于循环的巧妙想法是 CycleGANs 的新颖贡献,如下图所示。需要注意的是,相同的生成器用于生成假抽象画和抽象画的重建,相同的生成器也用于生成假森林图像和森林图像的重建。

CycleGAN training. This figure is adapted from the original CycleGAN paper.
下面你可以看到我们的 CycleGAN 实际上是什么样子。你可以看到真实的图像,被生成为假的,然后被重建回真实的。你可能会注意到,尤其是在抽象画中,物体的结构不会发生剧烈的变化。这是对 CycleGANs 的常见观察,可能是因为娱乐步骤。对于一个 CycleGAN 来说,彻底改变一个图像,然后又回到它的原始版本可能太过分了。

好了,现在让我们进入细节!在训练模型时,基本上有两个步骤:训练生成器,然后训练鉴别器。
- 训练发电机
就像任何神经网络一样,我们需要计算损耗(在这种情况下是针对发电机),并使用带有反向传播的随机梯度下降(即链式法则)来计算梯度,并根据这些梯度更新权重。但是我们如何计算发电机损耗呢?这种损失实际上由几个不同的部分组成。
首先,我们计算抽象画生成器的 GAN 损耗(在“循环”中我们在下图的黄色阴影部分)。这是通过拍摄一幅真实的森林图像并将其放入抽象画生成器来生成一幅假的抽象画来实现的。假的抽象画然后通过抽象画鉴别器。我们可以认为鉴别器的输出是这幅画是一幅真正的抽象画的概率。然后用二进制交叉熵损失评估鉴频器的输出。这就是 抽象画 甘损 。

GAN loss for abstract painting generator
第二个,我们计算森林影像 发电机的 GAN 损耗(在循环中我们在下图的黄色阴影部分)。这是通过获取一幅真实的抽象画并将其放入森林图像生成器来生成一幅假的森林图像来实现的。然后,假森林图像通过森林图像鉴别器。我们可以认为鉴别器的输出是照片是真实森林图像的概率。然后用二进制交叉熵损失评估鉴频器的输出。这就是 森林形象甘损 。

GAN loss for forest image generator
很好,现在我们有了一幅假的抽象画和一幅假的森林图像。正如上面的高级概述中所解释的,我们现在必须将这些伪图像重建回相应的真实图像。因此,第三个,我们将假的抽象画放入森林图像生成器,生成重建的原始真实森林图像(在下图的黄色阴影部分)。我们对照具有 L1 损失的真实森林图像来评估该重建的森林图像(注意,这里,L1 损失通常乘以某个常数,因此它与其他计算的损失处于相同的规模)。这就是 森林影像周期一致性损失 。

最后,我们将假森林图像放入抽象画生成器,生成重建的抽象画(在下图的黄色阴影部分)。我们对照具有 L1 损失的真实抽象画来评估该重建的抽象画(注意,这里 L1 损失通常乘以某个常数,因此它与其他计算的损失处于相同的规模)。这就是 抽象画 周而复始的损失 。

现在我们差不多可以计算发电机损耗了(是的,所有这些工作都是为了一个数字)。还有一个损失我们需要计算,就是身份映射。计算身份映射损失是因为,如下图所示,该步骤显示为保留原始图像的颜色。首先,我们将真实森林图像放入森林图像生成器,并计算真实森林图像和生成的森林图像的 L1 损耗。这就是 森林影像身份映射损失 (注意,这个损失也是乘以常数)。第二,我们对抽象画做同样的事情,我们通过抽象画生成器来绘制真正的抽象画。这就是 抽象画 的身份映射损失。

Figure 9 from the CycleGAN paper.
我们终于可以计算发电机损耗了,这是所有损耗分量的总和:
生成器损耗 = 抽象画 甘损耗+森林图像甘损耗+森林图像循环一致性损耗+抽象画 循环一致性损耗+森林图像身份映射损耗+抽象画 身份映射损耗
发电机的重量随后根据这一损失进行更新!
2。训练鉴别员
现在我们已经更新了生成器的权重,接下来我们需要训练鉴别器。
首先,我们更新抽象画鉴别器的权重。我们将一幅真实的抽象画通过抽象画鉴别器,并获取该输出,用二进制交叉熵损失对其进行评估。然后,我们取之前生成的伪抽象画,放入抽象画鉴别器,同样用二值交叉熵损失进行评估。然后我们取这两次损失的平均值。这就是 抽象画鉴别器损耗 。抽象绘画鉴别器的权重相对于该损失进行更新。
第二个,我们更新森林图像鉴别器的权重。我们将一幅真实的森林图像通过森林图像鉴别器,并获取该输出,用二进制交叉熵损失对其进行评估。然后,我们把先前生成的假森林图像,把它通过森林图像鉴别器,也评价它与二值交叉熵损失。然后我们取这两次损失的平均值。这就是 森林图像鉴别器损耗。森林图像鉴别器的权重根据该损失进行更新。
架构
到目前为止,我已经提到了这些生成器和鉴别器的存在,但我还没有说出这些神经网络的确切架构。当然,这些网络的架构有多种选择,但在这里我将简要提及用于生成森林抽象画的架构。这些架构的细节可以在代码中查看。发生器来自以前用于风格转换和超分辨率的架构(见 Johnson et al. )。网络架构基本上由一堆 ResNet 块组成,它们对网格大小进行下采样,保持网格大小不变,然后对网格大小进行上采样。请参见下图中的架构图。

Generator architecture from Johnson et al.
对于鉴别器,我们使用 PatchGAN,它基本上试图分类图像的每个 N×N 块(这里是 70×70)是真是假。参见代码和这张纸以获得更多补丁的细节。
计算机生成的森林图像
训练一段时间后,让我们看看计算机生成的抽象森林图像是什么样子的!下面是一些例子,但是你可以在我的 GitHub 上查看它们。

不算太差!我为杜克大学的人工智能艺术竞赛创作了这些图像,最终赢得了第一名— 点击此处了解更多信息。
结论
正如你在上面看到的,在这种情况下,由于 CycleGANs,计算机确实可以产生艺术。同样,CycleGANs 真正酷的部分是,在数据集中不需要成对的图像。这是相当惊人的,这是可能的!我会很好奇,看看在其他领域,比如自然语言处理领域,不成对的机器学*是否可能存在(可能这个已经存在了!).
*** 2020 年 1 月 26 日更新* * *:我忘了早些时候发布这个,但这里展示的艺术在杜克大学的一次人工智能艺术竞赛中获得了第一名。查看一篇描述这场比赛的文章!
D-ID 正在改变面部识别走向隐私的道路
当世界其他地方都被自拍、面部拍照和上传照片的热潮所吸引时,三个年轻人看到了监控越来越多的未来,现在他们正在寻求改变这种状况。

DEPOSIT PHOTOS
在过去的十年里,我们目睹了网络照片分享的泛滥。从 Pinterest 到 Instagram,再到 Snapchat,照片分享的兴起使得自拍、公民新闻和即时事件的活动激增。 Brandwatch 的最新统计显示,在 18-24 岁的人群中,超过 75%的人使用 Instagram 和 Snapchat,94%的人使用 Youtube:
- 在 Instagram 上,迄今已有超过 400 亿张照片被分享
- 每天有超过 9500 万张照片被上传到 Instagram 上
- 2017 年第三季度,发送了 35 亿次快照。
基于之前的估计,图像和视频的未来一目了然:
- 据估计,2018 年 84%的通信是视觉的。
- 据估计,2018 年 79%的互联网流量内容是视频。
- 包含图片的帖子的参与度提高了 650%

D-ID Co-founders: Eliran Kuta, Gil Perry, and Sella Blondheim
三个来自以色列的年轻人正在见证图像共享的到来。Gil Perry、Sella Blondheim 和 Eliran Kuta 当时在以色列国防军服役。当全世界都沉浸在自拍、面部拍照和上传照片的热潮中时,吉尔、塞拉和埃利兰却有着截然不同的看法。
我们就是做不到【免费上传照片到网上】。我们有不同的观点。即使在那时,我们也知道政府在用照片技术做什么。图像共享的全球涌入的现实,相机无处不在,谷歌眼镜承诺的兴奋——所有这些与新兴的人脸识别技术相结合,我们看到了未来。这种未来正在今天发生,并且正在走向一条越来越危险的道路。
输入 D-ID ,去标识的简称。Sella Blondheim、首席运营官的首席执行官吉尔·佩里(Gil Perry)和首席技术官埃利兰·库塔(Eliran Kuta)很早就意识到,他们需要对面部识别技术的影响采取一些措施。吉尔的职业是程序员,六年前他进入大学学*计算机科学。到那时,他和塞拉已经在面部识别以及保护照片和存储照片的选项方面做了大量的研究。他们开始构建一个解决方案,让人们可以上传照片,在线使用,但内容将无法识别。当时,没有人——无论是公司还是投资者——知道人脸识别技术。
面部识别技术早在 1960 年就开始了。号称面部识别技术之父的 Woodrow Wilson Bledsoe 创造了第一个半自动面部识别系统,被称为 Rand Tablet 。到 20 世纪 70 年代初,戈尔茨坦、哈默和莱斯克能够通过识别 21 个标记,如头发颜色和嘴唇厚度来提高面部识别的准确性。但直到 20 世纪 90 年代初,美国国防高级研究计划局(DARPA)和国家标准与技术研究所(NIST)才推出了人脸识别技术(FERET)项目,以鼓励在情报、执法和安全方面的具体应用创新。吉尔解释道,
我们知道相机无处不在。在伦敦桥上,人们每天被抓 300 多次。几乎每个人都有智能手机,拍照,到处上传,却没有真正了解风险。因此,当你结合三个因素时:人脸识别越来越准确,到处都是照片和图像的市场,以及来自客户、员工和游客的照片的商业存储——这相当于一场完美风暴。意识到隐私没了。我们已经失去了基本的隐私权。任何人都可以跟踪你,认出你,从而窃取你的身份。
吉尔开发了一个工作原型,并接触了面部识别领域的一位顶级专家,他开发了第一个使用深度学*的算法。通过一系列的接触,他积累了足够的知识来了解政府和企业如何使用当前的技术,吉尔、塞拉和埃利兰决定将他们的解决方案带到这个世界,以恢复隐私或至少阻止这种迫在眉睫的未来发生。
如果没有人知道你是谁,你的孩子是谁,他们去哪里,谁是他们的朋友,他们住在哪里,你将无法走在街上。如果我们什么都不做,这将是我们的未来。
不久,塞拉、吉尔和埃利兰成立了 D-ID 公司。日期是 2017 年 1 月。他们向风投和企业推销,但无人理睬。人们认为他们多疑。他们努力解释他们的研究和经验,但没有说服投资者有市场需求。他们被告知面部识别不起作用。该团队还意识到,市场在很大程度上没有意识到面部识别技术的危险。他们毫不畏惧,继续讲述他们的故事和解决方案。
对我们来说,这是一个完美的时刻。我们的背景给了我们独特的个人痛点,这让我们走上了这条道路。我们相互信任,我们继续发展我们的技术,使它变得更强大。没有人以我们的方式思考,这对我们来说是好事。是什么让我们坚持下去?我们就是知道!
然后监管发生了。2018 年 5 月 25 日,欧洲通用数据保护和法规(GDPR) 发布,这是第一套全球公认的法律,旨在从根本上改变欧洲的数据做法。它的触角伸得很远,开始影响全球的政策和隐私变化。团队知道企业最终会感受到它的愤怒。在 GDPR,面部图像被认为是敏感的个人身份信息(PII)。D-ID 意识到他们将能够填补监管和广泛传播的图像共享现实之间的差距。D-ID 计划成为照片和视频的隐私增强技术。
D-ID 使用先进的图像处理和深度学*技术来重新合成任何给定的照片,使照片在人眼看来相似且足够好,但在面部识别算法看来不同。吉尔解释说这不是同一张照片。它看起来像你,但实际上是不同的。典型的降噪滤波器无助于降低保护。然而,通过应用 GANS 攻击(对抗性噪声注入)等解决方案,用户也可以将照片上传到社交网络,打印照片,拍摄截图——照片仍将受到保护。
它足够强大,可以抵御逆向工程的尝试,逆向工程可能试图通过训练新的分类器来忽略 D-ID 变化,从而克服保护。

Left– an original, unprotected image. Right — a resynthesized image of the same person, now deidentified
从隐私到完全匿名
D-ID 最*开发了另一种产品。他们的匿名产品不是保护照片,而是改变面部,使其与原始照片明显不同。当身份被掩盖时,剩下的是重要的属性,包括年龄、性别、种族、情感等。吉尔解释道,
人脸识别无处不在。当我们购物和与他人联系时,公司试图理解我们的意图和情绪。现实是我们无法阻止侧写的发生。这就是钱的所在。我们的身份在未经我们同意的情况下被使用。然而,如果公司想通过聚合数百万人的数据来改进他们的产品,身份就不应该成为这项研究的一个因素。没必要下到个人层面。
在这个充斥着假货的市场中,假货被用于社交网络上的模仿和约会网站上的恶作剧,更不用说不存在的人的发展,吉尔很清楚 D-ID 的意图是保护人们的隐私。然而,风险在于任何人都有可能利用这项技术来维持这个市场。
我们并不专注于防范深层假货。我们的目标是让我们的技术如此强大,没有人可以重新识别照片。我们专注于保护隐私。在监管的同时,我们的目标是利用我们的技术来保护个人隐私。互联网巨头今天正经历着可怕的隐私问题,因为他们很晚才明白他们的决定的含义。他们需要改变他们的隐私策略,以重新赢得用户的信任。
D-ID 的销售对象是对隐私敏感的组织,这些组织拥有大量来自员工、客户/患者或访客的照片,并且需要遵守 GDPR 等隐私法规。这包括学校、医疗保健、金融机构、政府合规部门、KYC 或物理徽章或许可证识别。
D-ID 也在向 B2B2C 销售:处理数百万消费者图像的公司,包括设备制造商、云提供商/安全提供商和安全提供商。照片可以通过它们的 API 立即得到保护。未来,D-ID 还可以安装在手机上,以保护用户照片。吉尔提到了 Ntech labs 和他们现在已经不存在的应用程序, Findface ,它允许你通过简单的照片搜索来识别一个人。D-ID 试图使这些类型的应用变得无用。正如吉尔所说,这是一个市场信任的问题。
Gil 认为 D-ID 优于当前的竞争对手解决方案。匿名化领域的竞争对手使用像素化来扭曲照片的外观,以至于最终图像无法使用。鹦鹉识别用于确定像素化程度,以某种程度的准确性重新识别原始图像。他坚持认为这些方法不是为了防止人脸识别。
我们认为你不应该隐藏。你不必戴眼镜或帽子来阻止别人认出你。您应该能够在众目睽睽之下自由行走,同时保护您最敏感的信息。
迄今为止,D-ID 已经筹集了 1000 万美元,雇佣了 5 名博士,以及以色列顶尖的深度学*和计算机视觉专家。D-ID 的顾问验证了技术和愿景:
Ann Cavoukian 博士,三任加拿大安大略省信息和隐私专员,设计隐私框架的创建者:
人脸已经成为我们的数字标识符,因此必须受到严格保护。随着越来越多的系统采用面部识别,隐私风险急剧上升。这就是为什么每个处理图像的组织(公司、政府、安全机构)都需要快速行动,实施强大的安全、身份识别和隐私措施。D-ID 的面部识别技术是专门设计的,旨在使人工智能难以克服,并将帮助个人和组织摆脱伤害。
Pitango Capital Venture 执行普通合伙人兼联合创始人 Rami Kalish:
越来越多关于我们每个人的数据正以惊人的速度从各种设备中产生。保护我们的隐私免受面部识别带来的风险,需要果断的监管和强大的技术相结合。D-ID 是技术方面的领导者之一,他们开创性的技术帮助组织确保客户、员工和公民的隐私安全。
组织和个人越来越意识到他们的信息是如何被使用和滥用的。虽然脸书等网站上的用户活动已经减少,但人天生就是群居动物。公司和消费者将继续在线交流,创建、上传、分享和存储图像和视频。D-ID 正着手让他们在这个过程中感到安全和安心。
这篇文章最初出现在福布斯上。
基于数据的日常决策:数据驱动的生活
超连接最迷人的结果之一是,网络用户越来越多地基于数据做出决定,尽管这个过程被如此友好的界面所隐藏,以至于最终变得完全自然。

Photo: Franki Chamaki
我们日常使用的搜索引擎和社交平台的算法以我们的数据为食,并以这样一种方式返回聚合、处理和提炼的数据,即我们不再将它们视为数据。
从纠正我们的语法并从一亿个结果中选择十个结果的搜索引擎,到像变魔术一样出现在我们最喜欢的社交网络的时间轴中的内容,一切都基于帮助我们做出日常决策的数据。
专业门户网站推荐算法推荐的音乐、电影、连续剧和书籍,以及虚拟旅行指南推荐的酒店和餐馆,不仅来自我们的历史和社交图谱,还来自数千或数百万用户的数据情报。
正是大规模收集、汇总和提炼数据的能力,赋予了网络和智能手机给我们带来的“社交”服务永久的价值。
根据这些数据,我们每天都在为我们的生活做出决定,根据《纽约时报》杂志的说法,我们的生活已经成为“数据驱动的生活”。
虽然在企业环境中,谈论管理、决策和数据驱动的营销已经很常见,但我们仍然不太认为个人决策过程也越来越多地受到数据的制约和丰富,即使我们与一位聪明的演讲者“交谈”。
随着物联网扩展到智能手表和身体活动追踪器等日常设备,每个用户的个人指标都进入了海量数据的规模,这些数据的处理和可视化指导着睡眠时间、卡路里消耗、体重控制、心率和步行距离等方面的决策。
围绕我们在社交网络中所有活动的统计数据,有时到了痴迷的极端,也是我们做决定的数据。我们从like那里学到了,什么照片效果最好,用什么滤镜和关键词,什么时候发布…
虽然我们将继续根据经验、推理和情感做出个人决定,但认识到数据(伪装在友好的界面下)已经是技术促使我们与环境联系的方式的一部分是件好事。
如果通过超文本链接学*阅读和写作是生活在网络创造的世界中所需的素养,那么学*阅读、可视化和解释数据将是生活在超互联世界中的必要技能。
Jose Luis Orihuela 是一名教授、演说家和作家,生于阿根廷,现居西班牙。他是纳瓦拉大学(潘普洛纳)通信学院的教员。26 个国家的访问学者和演讲者。作家和博客作者关注互联网对媒体、通信和文化的影响。他的最新著作有:《乳齿象手册》(2023 年)、《数字文化》(2021 年)、《互联网时代》(2015 年)、《推特世界》(2011 年)、《未来的 80 克拉维斯》(2011 年)和《博客革命》(2006 年)。从 2002 年开始在 eCuaderno(eCuaderno . com)发布,从 2007 年开始在 Twitter since)发布,从 2022 年开始在 mastosdon(mastosdon . social/@ jlo ri)发布。
数据科学家的舞蹈课

Within These Walls by Lenora Lee Dance, Photo by Robbie Sweeny
数据科学家和现代舞者有什么共同点?对大多数人来说,鱼和花生酱之间几乎没有区别。一个熟人甚至拒绝相信我是他们昨晚刚刚看到的两个男舞者之一(舞台上有 8 个人,我很难错过)。
对于那些不知道的人来说,我已经练*了 6 年的数据科学,并且在我的整个成年期都是半专业地跳舞。尽管跳舞的门槛对男人来说比较低,但我足够优秀,人们愿意让我在旧金山和纽约的舞台上(#privilege)。
两种文化之间的差异是巨大的,但数据科学家需要体验接触即兴创作(又名接触即兴)。接触即兴表演在这段视频的第一分钟得到了最好的解释,视频中伊泰·亚图瓦和一个两岁的跳舞。这是一种舞蹈形式,专注于意识和探索你的身体与他人和环境的关系。
了解自己局限性的最快方法是和另一个人跳舞

Flat Affect by Ellis Wood, Photo by Edward Casati
我的一位舞蹈导师 Lizz Roman 说,了解自身缺陷的最快方法就是与另一个身体共舞。她认为,当我们拒绝其他人的运动质量时,那是因为即使我们尝试了,我们也不能以同样的方式运动。可悲的是,大多数人非但没有被人体的活动范围所激励,反而变得具有防御性。
你是否曾被某人的做法震惊到完全拒绝他们的工作或意见?对我来说,这种反应在拥有丰富经验和直觉的主题专家和拥有丰富数据挖掘技术的数据科学家之间很常见。人们通常会开拓领域,解决不同的短期问题,而不是试图从两个世界中取长补短。从长远来看,这些方法不仅会导致不一致的产品,还会导致支离破碎的文化。这种冲突不太明显,但在数据科学和软件开发、产品管理甚至设计等其他学科之间也很常见。但是为什么不学着像其他学科一样思考呢?
在接触即兴表演中,我通过预测和模仿我的伙伴的动作来获得他们的信任。这样做的目的是发出信号,即使我的眼睛转向别处,我也可以在半空中支撑它们。类似于数据科学,通过问自己是否能猜到 X 会说什么来获得信任。然后验证他们是否真的同意你的总结。如果你失败了,你只是学会了一种等待被发现的新的思维方式!
请注意,我要求你不要只是倾听,而是积极地采用其他人的解决方案框架。如果你同意上述观点,一个有趣的推论是,我们应该积极地与一群不同的问题解决者合作!你们的差异应该相互激励,而不是使你们分离!
让创造力成为一种惯例

Island City Waterway by Epiphany Dance, Photo by Edgar F Zepeta Gutierrez
在接触即兴表演中,我最大的恐惧之一就是让我的搭档感到无聊。当同样的例行公事重复第三次时,它就有可能变成一场乏味的大炮或热身运动。没人想和只会一招的小马跳舞。
Twyla Tharp 在《创造*惯》一书中拒绝接受创造力不是天赋的观点,并主张创造力更接*于需要训练和维护的肌肉。按照她的建议,我总是试图在每次接触中尝试一些不同的东西来给自己带来惊喜:限制一个肢体,跟随我的伴侣的肘部,反转运动,甚至增加停顿。这些微小的变化会对随后的动作产生蝴蝶效应,从而产生新的想法。
我认为这个教训很好地适用于技术。你的领导层上一次没有要求每个人都具有创新性和颠覆性是什么时候?网飞著名的文化甲板阐明了不断激励你的同伴的需要。在孟山都,他们教导经理们通过想法影响他人,而不是通过权威来领导。对创造力的需求同样重要。可悲的是,创造力不是你能得到的。
我见过数据科学家反复应用他们最喜欢的工具(有人说深度学*吗?)到一个小问题上,然后希望别人发现一个有利可图的策略。我也听到许多人抱怨他们的工作变得重复。在这两种情况下,我认为你遇到了同样的问题,现在是时候积极发现和重新想象,而不是希望这次相同的公式会以某种方式产生不同的结果。但是这些新想法并不是突然亮起来的灯泡。
我建议你每天锻炼一点你的创造力,而不是在需要的时候等待缪斯唱歌。如果您由于数据隐私更改而突然失去了某个功能,您会怎么办?您还能如何验证您的假设?您能如何以不同的方式协作?查看数据的其他方式是什么?
这不是《T4》的《神速 T5》吗?可能是,但是我不需要在快速失败上有创造性。如果你发现自己陷入了一个循环,也许是时候和另一个人跳舞了。
通过纪律获得自由

Rehearsal for Ouroboros by Ariel Osterweis, Photo by Ariel Osterweis
我最喜欢的一句话是玛莎·葛莱姆说的“自由只有通过纪律才能实现”。在画室里,你学会顺从,让自己服从你的工艺的要求,这样你才能最终获得自由。”。
对于你们这些不跳舞的人来说,只要听听芭蕾舞中一个简单的动作背后有多少细节就行了。这看起来像一个简单的“膝盖弯曲”,但实际上是每一次跳跃、抬起和重心转移的开始和结束。对我来说,这强调了不断回顾你的手艺的基础和根本的必要性。
数据科学的基础是什么?对我来说,这是重新审视我的指标和假设,了解每个项目的总体与当前样本,在我所做的一切中建立一个反馈系统,在查看数据之前宣布我的预期,了解数据管道,查看我的模型的残差,以及重新阐述公司的数据科学愿景等。
然而,我经常看到数据科学家在很好地理解公司战略、用户甚至数据之前,就专注于学*流行的技术(如生成性对抗网络)。你认识一些人吗,他们想装配模型而不问它如何装配到产品中?你的科技债务削弱了你的创新能力吗?对我来说,你的自由受到了限制,因为你在过去的某个地方忘记了基本的东西。幸运的是,与舞蹈不同,数据科学中的错误很少会过早地终止你的职业生涯。在太晚之前回到演播室。
勇敢一点

在接触式即兴表演中,你需要勇气走进一个满是汗流浃背的陌生人的房间,你需要勇气去信任其他搬家工人,当你感到不安全时,你需要勇气说不。
当我告诉我的舞蹈朋友们我发现编舞是一种可怕的经历时,他们都很震惊。观众是一个高度异质和固执己见的群体,他们要么喜欢你的作品,要么不喜欢。没有空间像数据科学那样捍卫你的工作,在那里你可以(通常)理性地分解一个问题。跳舞,尤其是关于个人话题的,可能是一种非常脆弱的经历。
类似地,数据科学家需要推销他们的想法,挑战他们的同行,并在糟糕的项目变成技术债务之前终止它们。有了数据,我们通常是能够清楚地说出在 5%的产品发布后终止产品的原因的团队。在其他情况下,我们应该在推动特性开发之前,要求我们的同行有可度量的目标(而不是事后寻找度量)。在个人层面上,给人们完全坦诚的反馈。这些看起来像是吃力不讨好的任务,但这是工作的一部分,对士气和表现有着长期的影响。
我的建议和创造力一样,是经常练*。在完美之前进行演示,在大型团队会议上提问,要求参加未被邀请的会议,在午餐时提出想法,或者为安静的同事说话。
然而,一个警告是要知道你的勇敢行为会被如何看待。男人或女人、经理或个人贡献者说的同样的话可能会有很大的不同。理解这种感觉是成为有效沟通者的关键。我看到了非常勇敢和有才华的同事的职业生涯是如何被破坏的。如果您是经理,请确保在建议您的报告遵循您的路径之前仔细检查您的权限。
谢谢
感谢你读到这里,这是我的第一篇媒体文章,非常感谢你的反馈!
ML 中的危险反馈循环
机器学*模型如何改变社会行为

Trefoil Knot, is an impossible knot with no known origin
昨天,一位刚刚加入我团队的数据科学家问了我一个问题。他的问题与我们公司的一个非常具体的问题有关,但答案的含义远不止于此。他问道:
生产中的当前模型决定了我们是否会根据一组给定的功能给销售线索回电。如果之前的模型确定某些特征表明我们不应该给销售线索打电话,那么这些特征现在与结果高度相关,因为我们只给那些销售线索回电。如果我想开发一个新的模型,这些特征会不会成为泄露的数据?
换句话说:如果我们用一组给定的特征训练一个模型,并且我们展示基于这些特征的行为,那么这些特征现在与结果相关,并且所有后续的模型将继续使用它们。
这里有一个简单的例子来说明这是如何工作的:
管道:
- 给定一组输入要素,返回销售线索转换的概率。
- 考虑到潜在客户转化的可能性,给客户回电。
- 如果我们有大量的销售线索,但只有少量的回电,我们该如何优化呢?
假设我们的模型有来自脸书、谷歌和必应的线索。如果我们的第一个模型决定来自这些给定来源的转换概率为 3%、5%和 1%,并且我们可以进行有限数量的回调,我们将只回调 5%的概率。现在快进两个月。第二个模型发现这些概率现在是:0.5%,8.5%,和 0%。发生了什么事?
因为我们开始只给谷歌潜在客户回电,我们增加了转化这些潜在客户的机会,同样,因为我们不再给脸书和必应潜在客户回电,这些潜在客户从未转化过,因为我们从未给它们打过电话。这是一个真实世界反馈循环的例子。
这种反馈循环对一家公司来说很危险,但对一个社会来说可能更危险。
在上面的例子中,反馈循环意味着我们因为历史转换率而放弃了脸书和必应的领先优势。这些潜在利率可能会改变,这意味着这些线索实际上可能会及时变得更好(例如,如果脸书在引导线索到正确的地方方面做得更好)。因为我们有一个强系数的模型,把权重往下拉,我们可能永远不会调用这些线索,即使给出一个变化,我们也会对它们有偏见。
在这种情况下,该模型不再基于来自源头的销售线索转化的实际潜在分布,而是基于来自源头的销售线索转化的行为结果(即召回来源)。
从更广泛的角度理解这一含义
这个反馈循环的著名案例是累犯模型。如果我们训练一个模型,根据一些特征(包括位置、性别、家庭成员入狱的概率和所犯罪行)预测一名囚犯最终是否会回到监狱,我们可能会发现我们的模型对某个种族和性别特别有偏见。这是为什么呢?
如果巴尔的摩的黑人男性更有可能因为肤色和性别而入狱,那么这个模型就会发现这一点。但是为什么他们更容易进监狱呢?是因为他们犯罪的潜在分布高于西雅图的白人女性,还是因为历史上(和目前)司法系统对这个群体更有偏见,并以更高的比率对他们采取行动?
对于大多数读者来说,这可能不会特别深刻,不是因为你不关心正义,而是因为你可能不会控制累犯模型。我要强调的另一个例子是雇佣模特。
假设我们在招聘模型中加入了性别因素。这里的招聘模型是预测一个雇员是否是公司的成功候选人的模型。假设我们正在为谷歌的软件开发人员建立一个招聘模型。我们发现绝大多数“成功”的软件开发人员恰好是男性。那么给定 1k 个候选人,难道不应该只挑男候选人吗?
这种模式可能会因为几个原因而有所偏差,例如,“成功”是由一些固有的更以男性为中心的指标选择的,女性在男性主导的世界中更难“成功”,或者许多其他原因。尽管如此,公司可以根据这种性别划分采取行动,并进一步创造这种划分。
也许今天,大多数公司都意识到了种族和性别不平等,并积极反对它。但也许隐藏在几层深处,它在银行贷款模型、保险价格模型和其他支配我们世界的模型中被遗漏了。
数据科学黑客马拉松的黑暗面

我在前面的三部曲部分中描述了参加黑客马拉松的几个理由。学*和赢得有价值的奖项的动机吸引了几乎所有的人,但由于组织者或赞助公司的错误,活动经常失败,参与者不满意地离开。我提供目前的职位,以避免这种不愉快的事件频繁发生。第二个三部曲部分致力于组织者的错误。
目前的帖子是这样组织的:从最开始,我谈论事件,解释哪里出错了,结果是什么(或者长期可能的结果)。然后,我会提供自己对发生的事情的评估,以及如果我是活动组织者,我会采取的行动方式。我可以假设真正的组织者的动机,因为我是所有活动的参与者。所以,我的评价可以是片面的。有些观点可能看起来是错误的,但事实上,我们就是这样想的。
在读者看来,作者似乎决定在一场战斗后挥舞拳头。但那是假的。我设法在列出的某些黑客马拉松中赢得了一个奖项,然而,这并不妨碍我得出这个活动组织得很差的结论。
出于对组织者和参与者的尊重,帖子中不会指定任何特定的公司。然而,细心的读者可能会猜测(或谷歌)他指的是谁。
黑客马拉松#1。限制
数据分析黑客马拉松是由一家大型电信公司半年前在莫斯科举办的。二十个队奋力争取第一名。该事件提供了一个用于分析的数据集,其中包含对公司支持服务的呼叫数据、社交媒体活动以及编码用户的数据(性别、年龄等)。).最令人感兴趣的数据集部分,即用户的消息和操作员的响应(文本数据),相当嘈杂,因此需要清理才能继续操作。
组织者设定了以下任务:用提供的数据创造一些有趣的东西。禁止使用来自网络的额外开放数据集或自行解析数据。此外,禁止提供与数据集无关的想法。事实上,提供的数据很差,因为很难将它们转化为有趣的产品。很明显,公司已经实施了(或将尽快实施)许多之前提出的想法。
结果,绝大多数团队(20 个中有 15 个)创建了聊天机器人。其中一个团队在发言中提供的决定与之前的略有不同。然后其中一个评委问下一组:好的,伙计们,你们也有聊天机器人吗?。因此,那些没有创建聊天机器人的团队获得了三个可用聊天机器人的第一名和第二名。
好吧,伙计们,你们也有聊天机器人吗?
让我们比较一下两年前由一家国际咨询公司为 Qwerty123 公司组织的黑客马拉松。组织者谈到了 Qwerty123 公司使用的指标,因为大多数黑客马拉松参与者在活动开始前并不知道 Qwerty123 公司的活动细节。然后提供了六个不同方向的数据集,包括文本、表格、地理位置——所有参与者都有回旋的余地。组织者并不禁止额外的数据集使用,但也支持这样的倡议。十个有不同决定的团队在比赛的决赛中争夺主要奖项,所有团队都使用了公司提供的数据(尽管没有禁令),这些数据表明获得高质量产品的良好潜力。
道德
避免限制创造性参与者的流动。作为组织者,你必须提供材料,并信任他们的远见和专业精神。如果你是黑客马拉松的成员,所有的限制或禁令都应该提醒你。通常,这是一个坏的组织证据。做好准备,如果仍然满足限制,你必须在一个大的竞争水平池中创建一个项目。因此,你必须负责任,这意味着在这样的事件中承担风险:要么创造一些全新的东西,要么提供一个不同寻常的杀人功能,以便与类似项目的流有很大的不同。
避免限制创造性参与者的流动。作为组织者,你必须提供材料,并信任他们的远见和专业精神。
黑客马拉松#2。不可能的任务
Amadora 的黑客马拉松一定会很有趣。主要的电话制造商赞助公司在活动日期前 4 个月开始筹备黑客马拉松。公关活动发生在社交网络上,意味着潜在参与者要成功完成一项技术测试,并写下他们过去的项目,才能被选中参加当前的活动。奖金池足够大。在黑客马拉松前几天,导师们举行了一次技术会议,为参与者提供足够的时间来感受行业细节。
组织者在活动中提供了一个巨大的日志数据集(总大小约为 8 GB ),任务是对设备故障进行二进制分类。他们讲述了项目的评估标准,即分类质量、特色创造、创造力、团队合作能力等。但这只是运气不好——在训练中只有 20 个例子,在 8 GB 功能的测试中有 5 个。最后,数据集包含一个漏洞:周三收到的设备日志包含设备操作错误,而周四创建的那些日志不包含错误(顺便说一下,只有两个俄罗斯团队知道,因为这个国家被认为是经验丰富的“数据矿工”的家园)。尽管有真实的测试标签知识可用,但该任务是不可能解决的。组织者未能达到他们努力争取的结果。因此,参与者花了很多时间来解决一个糟糕的任务。所以,现在的黑客马拉松失败了。
最后,数据集包含一个漏洞:周三收到的设备日志包含设备操作错误,而周四创建的日志不包含错误
道德
确保提供任务的技术专业知识,并检查您的任务是否充分。为了避免这种情况,最好为初步检查支付过高的费用(在这种情况下,任何数据科学家都会立即确认不可能解决当前问题)。
为了避免失败的黑客马拉松,最好是为初试多付钱
在这种情况下,除了花费的时间和金钱,公司还失去了潜在候选人的信誉。顺便说一下,参与者和公司都应该告知成功的黑客马拉松结果,这意味着尽可能多地宣传黑客马拉松。不幸的是,许多公司没有做到这一点,并通过在 Twitter 上发布公告和几张活动图片来限制自己。
黑客马拉松#3。要就要
最*,我们的团队参加了在阿姆斯特丹举行的黑客马拉松。能源学科特别适合我们,因为我有能源工程硕士学位(专业是可再生能源)。黑客马拉松在网上举行。我们被提供了任务描述以及一个月的时间来完成。组织者的愿望是得到一个完整的项目,这将有助于提高阿姆斯特丹房屋的能源效率。
我们设法创建了一个应用程序,提供电力消耗预测(我之前参加了一个关于当前主题的竞赛,在那里我收到了与 SOTA 解决方案相*的)以及太阳能电池板发电。然后根据预测优化电池性能(目前的想法部分取自我的硕士文凭)。我们的项目完全符合组织者的任务(在我们看来是这样)和阿姆斯特丹政府在未来几年可再生能源领域的政策。
在项目评估期间,我们的团队和其他团队被告知这与客户的期望相矛盾。因此,我们被告知,为了有机会获奖,我们必须改变产品。但并没有改变它,而是屈服于失败。队伍总数是 40 个,但是我们甚至没能进入前 7 名,尽管组织者的选择看起来很奇怪。他们选择了基于智能手机传感器数据开发风速和太阳辐照度计算应用程序的团队:一个用于风的麦克风和一个用于太阳辐照度的光传感器。最大的特点是将热狗/不热狗分为三类,即太阳、风、水和相应的维基百科文章(演示)。
让我们暂时把道德问题放在一边:用胜利的机会来要挟参与者是不道德的。由于黑客马拉松的参与动机之一是(特别是对于有经验的开发人员来说)自己的想法的实现,所以很多强有力的参与者可能会在听到这样的反馈后离开活动(这种情况发生在我们的团队和其他在听取导师的意见后停止项目页面更新的团队身上)。然而,让我们假设我们同意了组织者的愿望,并设法根据他们的要求改变了我们的项目。接下来会发生什么?
用胜利的机会勒索参与者是不道德的
所有的组织者都有自己的理想项目理念,因此所有的愿望(和变化)都会导致当前的理想。参赛者将花费他们的时间,他们将更难拒绝进一步的参与(因为已经做出了一定的努力,似乎胜利是如此之多)。但事实是,奖项的竞争将会增加,因此参与者将越来越多地根据组织者的编辑来改变项目,同时希望获得奖项。所以,那些没有获奖的人,回头看会意识到他们没有拿到钱就参加了自由职业,因为他们根据客户的意愿做出了改变,但没有得到任何回报(当然,除了相关的经验)。
所以,那些没有获奖的人,回头看会意识到他们没有拿到钱就参加了自由职业,因为他们根据客户的意愿做出了改变,但没有得到任何回报(当然,除了相关的经验)。
道德
通常组织者的愿望和反馈都有助于项目。然而,在这种情况下,参与者不应该依赖导师的建议。得到组织者的负面反馈意味着你的参与完成。因为黑客马拉松不是自由职业者(尤其是没钱的时候)。
得到组织者的负面反馈意味着你的参与完成。因为黑客马拉松不是自由职业者(尤其是没钱的时候)。
如果组织一个有明确项目愿景的黑客马拉松,但没有技能或能力自己实现它,那么最好以自由职业者的规范形式形成您的愿景。否则,你将不得不为黑客马拉松和自由职业者的服务支付两倍的费用。
仪表板设计:关键绩效指标和度量标准

如何为您的仪表板选择正确的数据
设计有效的仪表板最重要的方面是确保您选择了正确的指标和 KPI 来显示。不管一个视觉设计有多棒或多聪明,如果它不能展示与受众相关的有意义的见解和数据,它最终只会是一个没人看的漂亮展示。
本文将向您展示一些行之有效的技术,这些技术将帮助您为管理层和运营层收集和定义适当的性能指标。虽然这里讨论的技术集中在仪表板设计上,但是这些相同的原则可以用于许多不同的商业智能需求收集工作。
随着各种软件产品中仪表板工具和显示的流行,人们对仪表板、指标和关键绩效指标(KPI)的组成有了不同的理解。为了确保我们说的是同一种语言,我将定义一组术语,它们将构成我们设计讨论的基础。

衡量标准和关键绩效指标
度量和 KPI 是许多仪表板可视化的构建块;因为它们是提醒用户相对于他们的目标他们在哪里的最有效的手段。下面的定义构成了仪表板信息设计的基本构件,它们建立在自身的基础上,因此在继续下一个定义之前,充分理解每个定义和所讨论的概念非常重要。
Measure:Measure 是数字上可量化的数据。销售额、利润、留存率,都是具体衡量标准的例子。
维度:维度表示给定度量的不同方面。例如,时间经常被用作分析不同度量的维度。其他一些常见的维度有地区、产品、部门、细分市场等。
层次:维度可以进一步分解为层次。例如 时间 维度也可以形成层次结构,例如年>季>月>日。
粒度:层次结构中的每一层都被称为维度的粒度。例如,如果您正在查看一个地理维度,那么各个颗粒(级别)可能是区域>国家>州/省>城市>邮政编码 e
公制:公制是我们经常在仪表板中显示的数据类型,是一种度量,它表示与一个或多个特定的尺寸和相关的颗粒相关的一段数据。
每周总销售额是一个指标的例子。在这种情况下,度量将是美元(总销售额),而维度将是时间以及相关联的谷物(周。)
查看一个以上维度的度量,如按地域 和 时间划分的总销售额,称为多维分析。设计用于多维分析的仪表板会显著增加仪表板设计和用户体验的复杂性,因为我们使用二维表面(设备屏幕)来表示多维度。虽然有有效的布局和交互模式来处理这种类型的分析,但在设计阶段之前注意这一点很重要,因为这些类型的设计需要更复杂的方法。

关键绩效指标(KPI):KPI 就是一个与目标相关的指标。通常,KPI 表示某个指标高于或低于预定阈值的程度。KPI 通常显示为实际与目标的比率,旨在让业务用户立即知道他们是在计划之内还是之外,而最终用户不必有意识地关注所表示的指标。
例如,我们可能会决定,为了达到季度销售目标,我们需要每周销售 10,000 美元的小部件。衡量标准是每周小部件销售额,目标是10,000 美元。如果我们使用百分比量表可视化来表示这个 KPI,并且到周三为止我们已经销售了 8000 美元的小部件,用户将立即看到他们已经完成了目标的 80%。
为 KPI 选择目标时,您需要记住,对于您想要在指标中查看的每个颗粒,都必须有一个目标。要有一个按日、周、月显示总销售额 KPI 的仪表板,您需要确定每种相关谷物的目标。

记分卡、仪表板和报表
记分卡、仪表板和报告之间的差别是细微的区别之一。这些工具中的每一个都可以结合其他工具的元素,但是在一个高层次上,它们都针对业务决策过程的不同和独立的级别。
记分卡:从业务决策谱的最高、最具战略性的层面开始,我们有记分卡。记分卡主要用于帮助运营执行与业务战略保持一致。记分卡的目标是通过监控现实世界的执行情况并将执行结果映射回特定的战略,使企业专注于共同的战略计划。记分卡中使用的主要衡量标准是关键绩效指标。这些关键绩效指标通常是衡量组织执行战略目标的能力的几个度量标准或其他 KPI 的组合。记分卡 KPI 的一个例子是一个名为“盈利销售增长”的指标,它将几个加权指标(如新客户获得量、销售量和总盈利能力)组合成一个最终得分。
仪表板:仪表板在业务决策过程中比记分卡低一级;因为它较少关注战略目标,而更多地与具体的运营目标联系在一起。运营目标可以直接促成一个或多个更高级别的战略目标。在仪表板中,运营目标本身的执行成为焦点,而不是更高层次的策略。仪表板的目的是以一种既直观又有见地的方式向用户提供可操作的业务信息。仪表板主要以指标和 KPI 的形式利用运营数据。
报告:商业中最流行的商业智能工具可能是传统的报告。报表本质上可以是非常简单和静态的,例如给定时间段的销售交易列表,也可以是更复杂的跨选项卡报表,包括嵌套分组、滚动汇总和动态钻取或链接。当用户需要以易于阅读的格式查看原始数据时,最好使用报告。当与记分卡和仪表板结合使用时,报告提供了一种巨大的方式,允许用户分析其度量和关键绩效指标背后的特定数据。

收集仪表板的 KPI 和指标要求:
传统的 BI 项目通常使用自下而上的方法来确定需求,重点是数据领域和数据中存在的关系。在为您的仪表板项目收集指标和 KPI 时,您将希望采用自上而下的方法。自顶向下的方法从需要首先做出的业务目标和决策开始,然后向下处理支持这些决策所需的数据。
为了采用自上而下的方法,您必须让将利用这些仪表板的实际业务用户参与进来,因为只有这些人能够确定特定业务数据与其决策过程的相关性。
当采访业务用户或利益相关者时,目标是发现引导用户做出特定决策或行动的指标和 KPI。有时用户会非常详细地了解哪些数据对他们很重要,有时他们只会有一个高层次的目标集。通过遵循下面概述的实践,您将能够将用户提供给您的信息提取到您的仪表板的一组特定的 KPI 和指标中。
采访商业用户:
在我们与客户直接合作并收集各种行业中执行和运营仪表板项目的需求的经验中,我们发现面试过程围绕着两个简单的问题:“您需要回答哪些业务问题,一旦您有了这些答案,您将采取什么行动或做出什么决定?”
问题 1:“你需要回答哪些商业问题?”
这里的目的是帮助业务用户定义他们的需求,使我们能够获得他们问题背后的数据。例如,销售副总裁可能会问:“哪些销售人员是我的首席制片人?”或“我们这个月的目标实现了吗?”在“哪些销售人员是我的首席制作人”这个问题上然后,我们可能会问副总裁几个问题,问她“这个衡量标准是基于总销售额吗?您想每天、每周还是每月查看一次?”
我们希望确定构成 KPI 或指标的具体数据组件。因此,我们需要花足够的时间与用户讨论这个问题,直到我们清楚地理解将要表示的度量、维度、粒度和目标(在 KPI 的情况下)。
问题 2:“根据对问题 1 的回答,这会引发哪些其他问题,或者你会采取什么行动?”
一旦我们理解了回答用户问题所需的指标或 KPI,我们就需要找出用户是否想要基于该答案执行进一步的分析,或者他们是否能够采取行动或做出决策。目标是让用户不断分解问题,直到他们有足够的信息来采取行动或做出决定。这个深入问题的过程类似于剥洋葱的皮;我们希望继续深入,直到我们到达核心,在这种情况下是用户做出决定或采取行动的能力。
作为这一由两部分组成的迭代问题流程的结果,我们将快速筛选出对用户决策过程真正关键的指标和 KPI,这些指标和 KPI 可能只是感兴趣的。这种方法与我所说的仪表板开发的“厨房水槽方法”相反,在这种方法中,产品和/或工程团队设计一个仪表板,其中包含所有可用的数据和复杂的小部件,允许用户搜索、过滤和/或钻取他们需要的数据。
在仪表板设计中采用厨房水槽的方法,往往会让你的用户不知所措,有太多的选择和认知负荷,导致仪表板无法使用。相反,提供紧密针对用户需要查看和做出决策以改善业务状况的数据将会产生一个高效且广泛使用的仪表板。
将所有这些放在一起 KPI 轮
为了帮助这个需求面试过程,我创建了一个简单的工具,叫做 KPI Wheel。面试过程很少是结构化的线性对话,更多时候是思想和问题的有机自由交流。KPI 轮允许我们与最终用户进行自然流畅的对话,同时让我们专注于收集具体需求的目标。
我建议把下面的图片打印在几张纸上,放在方便用户访问的地方,你可以直接在滚轮上写笔记。

You can right click on image and open in new browser window to print.
KPI Wheel 是一种工具,可用于收集定义和可视化指标或 KPI 所需的所有特定信息。我们将使用该工具收集以下信息:
- 我们试图帮助用户回答的业务问题。
- 这个问题适用于哪些业务用户。
- 为什么这个问题对业务目标很重要。
- 数据驻留在哪里来回答这个问题。
- 此指标或 KPI 还会引发什么问题。
- 利用这些信息可以采取什么行动或决策
从任何地方开始,但去任何地方
KPI 轮被设计成圆形,因为它体现了一个概念,即你可以从任何地方开始,但可以去任何地方,从而涵盖所有相关领域。在面试过程中,你需要参考转盘,以确保在讨论时,你已经填写了每个区域。随着谈话的进行,你可以在适当的部分简单地记下笔记,如果有些地方没有回答,你可以确保跟进更多的问题。这种方法背后的美妙之处在于,用户可以从非常高的层次开始“我想知道销售情况如何”,或者从非常低的层次开始“我需要看到按地区、时间和毛利润分类的产品销售情况。”在这两种情况下,您都可以从用户感觉舒适的任何点开始,然后在轮子周围移动,填充所需的细节。
问题 1:什么问题?
轮盘的这个区域指的是基本的“你需要回答什么业务问题?”我们通常可以从这个问题开始访谈,或者当用户开始考虑一个特定的指标时,我们可以通过问他们“这个指标能为你回答什么业务问题?”轮盘的这一部分决定了整个指标或 KPI 的整体背景和相关性。
问题 2:谁在问?
对于给定的指标,我们想知道谁将使用这些信息来制定决策和采取行动。了解组织内可能查看此指标的各种用户非常重要。我们可以记下具体的个人,也可以只是指一个有类似业务需求的普通人群。
问题 3:为什么它很重要?
因为真正有效的仪表板可以成为每天都在使用的工具,所以我们希望验证所显示的每个指标和 KPI 的重要性。在这个需求收集过程中,我们经常会收集一长串潜在的度量和 KPI,在某些时候,用户将不得不选择哪些数据对他们来说是真正最重要的。我建议使用 1-10 分,并说明该指标的重要性,这样当您开始制作仪表板原型时,您就可以了解该指标的重要性。
问题 4:数据来源
对于给定的指标或 KPI,我们还想确定支持数据将来自哪里。有时,为了计算一个或多个维度的指标,我们需要从几个不同的来源汇总数据。在“毛利润最高的销售产品”的情况下,我们可能需要从 CRM 系统和 ERP 系统中提取数据。在这个阶段,简单地指出保存数据的业务系统就足够了;此时没有必要深入实际的表/字段名描述。
上半部分:测量、尺寸和目标
我们希望确保已经捕获了创建度量或 KPI 的三个主要属性,并让用户验证任何给定维度的粒度。如果我们无法确定指标的度量和维度,和/或 KPI 的目标,那么在设计仪表板时,我们将无法收集和可视化这些数据。
下半部分:提出的问题
在 KPI 轮的这一部分,我们希望列出在我们回答了主要问题后可能会提出的任何其他问题。该列表可以作为创建后续 KPI 轮的基础,这些 KPI 轮用于定义进一步的指标和 KPI。
下半部分:要采取的行动
对于任何给定的指标或 KPI,我们希望了解根据度量的状态可以做出什么类型的决策或采取什么类型的行动。通过填写此部分,我们还能够帮助验证指标的重要性,并将“必须拥有”的 KPI 与“最好拥有”的 KPI 区分开来。
轮子产生其他轮子
在填写 KPI 轮的过程中,通常会产生对多个 KPI 和指标的需求。这是首先进行初始分析的目的之一;让用户的所有需求浮出水面。当你在收集需求的过程中,你会发现没有正确的方法可以得到你的答案,问题会引出其他的问题,你最终会绕回来,以一种新的方式覆盖已经讨论过的领域。重要的是要有耐心,保持开放的心态,因为这是一个发现的过程。目标是具体了解如何通过使用良好的度量和 KPI 来增强用户的能力。

当您开始收集大量 KPI 轮时,您将开始看到您收集的 KPI 之间的关系。当您觉得您已经达到了一个饱和点,而您和用户都想不出任何更有意义的度量方法时,您将会想要在相互关联的上下文中检查所有 KPI 轮子。汇总 KPI 并创建逻辑分组和层次结构是一个很好的做法,这样您就可以清楚地了解各种指标之间存在的关系。一旦完成这些步骤,您将有一个坚实的基础来开始您的仪表板可视化和设计过程。
关于收集需求和业务用户的一句话
花费必要的时间进行正式的需求收集过程通常不被业务用户所理解,尤其是高级管理人员。这个过程有时会被视为许多不必要的繁忙工作,会打断用户已经忙碌的一天。重要的是要记住,你现在所做的关于什么数据是相关的,什么数据是不相关的决定必须在某个时候做出,唯一能够做出这个决定的人是用户自己。问题是,你是现在就花时间做出这些根本性的决定,而你只是在思考问题,还是以后在你煞费苦心地设计仪表板并围绕它们构建复杂的数据集成服务之后。
和所有的软件开发项目一样,当您经历开发周期的每个阶段时,变更的成本呈指数增长。一个很好的类比是用于房屋建筑的类比。当一面墙是图纸上的一条线时,移动它的成本是多少,而在墙上挂了一幅画后移动它的成本是多少?
把一切都包起来
虽然本文触及了一些可以用于收集仪表板项目需求的基本构建块,但它绝不是一个全面的方法。每个商业智能架构师在创建新的解决方案时都有一套最佳实践和设计模式。我希望我在这里提到的一些过程可以被修改,并用于补充各种利用仪表板技术的解决方案的当前最佳实践。
如果您喜欢这篇文章,并想了解如何应用这些原则来构建数据可视化,以帮助您的产品在市场上获得更多的销量、客户参与度和吸引力,只需 点击此处 。
原载于 2019 年 7 月 22 日http://www.brightpointinc.com。
苏格兰的数据行动主义和争取社会公正

我坐在爱丁堡的一间警察审讯室里。那天早上,我打电话给 101,举报大学校园里的跨性别涂鸦,此事迅速升级。下午三点左右,我和一名警官在手机上浏览照片,并签署了一份证人声明。
几个月后,我正式投诉一所大学违反了其平等、多样性和包容性(EDI)政策。一遍又一遍地阅读官方文件,以准确定位该机构的行动偏离了他们所宣称的政策。
我参与这些进程,意识到它们不太可能带来有意义的变化。我打给 101 的电话不会引发一队官员煞费苦心地审查闭路电视录像以确定罪犯,我的单一投诉也不会改变大学对事件和发言者的态度。然而,作为一个定期收集和分析 EDI 数据的人,有必要接触这些机构系统,并记录一些错误。
当人们讨论 EDI 数据时,话题往往集中在头条统计数据上,比如 T2 男女雇员之间的平均薪酬差距或者 T4 大学里白人和黑人学生之间的成就差距。尽管这些统计数据非常重要,并正确地揭示了这些不平等,但它们只代表了与 EDI 相关的潜在数据的一小部分。
作为一个定期收集和分析 EDI 数据的人,有必要接触这些机构系统,并记录一些错误。
爱丁堡希望将自己定位为欧洲的数据之都。然而,我们仍然对这座城市或整个苏格兰的多样性知之甚少。例如,使用 2011 年人口普查数据,我们知道 4.0%的苏格兰人来自非白人少数民族。这一数字在格拉斯哥(12.0%)、爱丁堡(8.0%)和阿伯丁(8.0%)较高。我们还知道,在苏格兰,30.0%的女性和 26.0%的男性披露了长期的限制性精神或身体健康状况或残疾。然而,分析族裔、性别和残疾等身份交叉的努力仍处于初级阶段。如果没有这些重要信息,可能会有许多我们尚不知道的与不平等和歧视相关的细微差别。
虽然对与性/性别、种族和族裔、年龄和残疾有关的数据的交叉分析不完整,但与一些身份群体的现有数据相比,这些特征的数据质量仍然遥遥领先。
例如,苏格兰 2021 年的人口普查将是第一次全国范围内对人们的性取向和跨性别身份/历史的统计。这一具有里程碑意义的举措将让我们更清楚地认识到我们是一个什么样的国家。然而,在这种进步的背景下,苏格兰政府决定不扩大对性别问题的回答选项,以包括那些认为自己不是双性恋的人。性别问题是人口普查中为数不多的必问问题之一。这意味着许多非二进制受访者将别无选择,只能将自己错误地登记为男性或女性。

National Records of Scotland, Edinburgh (Photo: Alamy)
苏格兰 2021 年人口普查中没有非二进制人口,对完成人口普查的人的记录不准确,这不太可能危及所收集数据的质量。然而,不被计算在内意味着什么。我的工作包括收集平等监测数据,我经常被问到‘我们真的需要询问一个人的跨性别身份/历史吗?或者‘询问一个人的宗教或信仰有必要吗?这些问题有一个明显的潜台词:T4 的身份特征是否足够重要或有足够的价值被计算在内?
诸如此类阴险的评论强调了 EDI 数据的武器化已经降临到我们头上。为了从大型资助机构获得资金,我们需要了解我们的数据。为了证明一个专门与边缘化群体合作的社会或文化团体的存在,我们需要知道我们的数据。数据就是弹药。数据就是力量。
在苏格兰,解决不平等、劣势和歧视的工作经常被原谅,因为“数量太少”。人们通过自己的圈子和网络来了解社会。当这些圈子和网络中的人看起来和听起来相似时,差异就被理解为发生在苏格兰之外的事情。“了解我们的数字”的重要性在今年夏天的边缘活动中变得显而易见,杰西卡·布劳的彩色边缘和哈里·约瑟芬·贾尔斯的 TransFringe 等活动记录了在白人和异性恋占压倒性优势的文化背景下边缘群体的行为和表演者的存在。
我们需要知道我们的数据。数据就是弹药。数据就是力量。
数据不仅仅是数字和文本,它还为我们提供了一种大声呼喊“看这里,有问题存在”的方式!提高数据素养活动人士的技能将使我们能够展示苏格兰普遍存在的不平等、代表性和排斥问题。那些掌握权力的人已经在利用数据作为不作为的借口——我们必须收回这块土地,并确保数据赋予积极分子带来有效变化的工作权力。
凯文·古言博士是爱丁堡的一名平等、多样性和包容性研究员。他以个人身份写作。
使用 R 进行数据分析和可视化
初学者循序渐进指南
你是从数据科学领域开始你的旅程的吗?R 需要知道怎么入门吗?你对数据可视化感兴趣吗?如果是,那么这个教程就是为你准备的!
概述&目的
通过这篇文章,我们将学*如何对一个数据集进行基本的探索性分析,创建可视化并得出推论。
我们将要报道的内容
- R 入门
- 了解您的数据集
- 分析和构建可视化
1。R 入门
1.1 下载并安装 R | R Studio
r 编程提供了一组内置的库,有助于用最少的代码和灵活性构建可视化。
可以从 R 项目网站轻松下载 R。下载时,您需要选择一个镜像。根据您的操作系统选择 R,如 Windows、Mac 或 Linux。

安装 r 非常容易。只要按照基本的安装步骤,你就可以开始了。
对于编写脚本的简单方法,我推荐使用 R Studio 。这是一个开源环境,以其简单和高效而闻名。

Launch Screen after starting R Studio
1.2 安装 R 包
包是由包含可复制的 R 代码的社区创建的基本单元。这些包括可重用的 R 函数、描述如何使用它们的文档和样本数据。
存储软件包的目录称为库。r 带有一套标准的软件包。其他的可以下载和安装。安装后,必须将其加载到会话中才能使用。
要在 R 中安装一个包,我们只需使用命令
install.packages (“所需包的名称”)
1.3 加载数据集
在 R 中已经预装了一些数据集。在这里,我们将使用泰坦尼克号的数据集,它内置于泰坦尼克号软件包的 R 中。
在使用任何外部数据源时,我们可以使用 read 命令来加载文件(Excel、CSV、HTML 和文本文件等)。)
该数据集也可在 Kaggle 获得。您可以下载数据集,包括训练和测试文件。在本教程中,我们将只使用训练数据集。
titanicread . CSV(" C:/Users/Desktop/titanic . CSV ",header=TRUE,sep= ",")
上面的代码将文件 titanic.csv 读入一个数据帧 titanic 。Header=TRUE 时,我们指定数据包含一个标题(列名),sep= ","指定数据中的值用逗号分隔。
2。了解数据集
我们使用了泰坦尼克号数据集,其中包含了所有登上泰坦尼克号的乘客的历史记录。以下是对数据集中 12 个变量的简要描述:
- PassengerId:序列号
- 幸存:包含二进制值 0 & 1。乘客没有生还— 0,乘客生还— 1。
- 机票等级|一等、二等或三等机票
- 姓名——乘客的姓名
- 性别——男性或女性
- 年龄—以年为单位的年龄—整数
- SibSp——兄弟/配偶——兄弟、姐妹和/或丈夫/妻子的数量
- 羊皮纸——父母/子女数量——母亲/父亲和/或女儿、儿子
- 票证—序列号
- 票价——乘客票价
- 客舱—客舱号
- 登船-登船港| C-瑟堡,Q-皇后镇,S-南汉普顿
2.1 偷看你的数据
在我们开始处理数据集之前,让我们好好看看原始数据。
观(泰坦尼克号)
这有助于我们熟悉数据集。

头(泰坦尼克号,n) |尾(泰坦尼克号,n)
为了快速浏览数据,我们经常使用 head()/tail()。

Top 10 rows of the data set.

Bottom 5 rows of the data set.
如果我们没有显式地传递 n 的值,它将采用默认值 5,并显示 5 行。
人名(泰坦尼克号)
这有助于我们检查数据集中的所有变量。

Familiarising with all the Variables/Column Names
str(泰坦尼克号)
这有助于理解数据集的结构、每个属性的数据类型以及数据中的行数和列数。

总结(泰坦尼克号)

A cursory look at the data
Summary() 是帮助汇总数据集中每个属性的最重要的函数之一。它根据变量的类型给出了一组描述性统计数据:
- 如果是数字变量->给出平均值、中值、众数、范围和四分位数。
- 在因子变量的情况下->给出频率表。
- 在因子+数值变量的情况下->给出缺失值的数量。
- 如果是字符变量->给出长度和类。
如果我们只需要数据集中特定变量的汇总统计数据,我们可以使用
summary(dataset name $ VariableName)-> summary(titanic $ p class)
as.factor(数据集$列名)
有时候,数据集中的一些变量是因子,但可能被解释为数字。例如,Pclass(乘客类)显示值 1、2 和 3,但是,我们知道这些不能被认为是数字,因为它们只是级别。为了将这些变量视为因子而非数字,我们需要使用函数 as.factor() 将其明确转换为因子

3.分析和可视化
数据可视化是一种将数据转化为易于理解的洞察力的艺术。在本教程中,我们将分析生存模式,并检查影响相同的因素。
思考要点
现在我们已经了解了数据集和变量,我们需要确定感兴趣的变量。领域知识和变量之间的相关性有助于选择这些变量。为了简单起见,我们只选择了 3 个变量,即年龄、性别和阶级。
存活率是多少?
当谈到泰坦尼克号的数据集时,首先出现的问题是“有多少人幸存?”。让我们用一个简单的条形图来演示同样的情况。
ggplot(泰坦尼克号,AES(x =幸存))+ geom_bar()

在 X 轴上我们有幸存的变量,0 代表没有幸存的乘客,1 代表幸存的乘客。Y 轴代表乘客数量。这里我们看到超过 550 名乘客没有生还,大约 340 名乘客幸存。
让我们通过检查百分比来使它更清楚
道具表(表(泰坦尼克$幸存))

登上泰坦尼克号的乘客中只有 38.38%幸存。
存活率基础性别
据信,在灾害期间的救援行动中,妇女的安全是优先考虑的。那时候也发生过同样的事情吗?


我们看到,与男性相比,女性的存活率明显更高。女性的存活率约为 75%,而男性不到 20%。
存活率基础票类(Pclass)
根据乘客乘坐的舱位等级,分为三个等级,即一等舱、二等舱和三等舱。我们看到超过 50%的乘客乘坐三等舱。

Survival Rate basis Passenger Class

一等舱和二等舱乘客的存活率不成比例,一等舱乘客的存活率超过 60%,二等舱乘客的存活率约为 45-50%,三等舱乘客的存活率不到 25%。
我会让你想一想……是因为对精英舱乘客的优待,还是因为距离*,因为三等舱在下层?
存活率基础票类和性别 (pclass)

我们看到第一和第二组的雌性有很高的存活率。乘坐一等舱和二等舱旅行的女性存活率分别为 96%和 92%,男性为 37%和 16%。乘坐三等舱的男性存活率不到 15%。
到目前为止,很明显,性别和乘客级别对存活率有显著影响。现在让我们检查一下乘客年龄对存活率的影响。
基本年龄存活率


查看图表中年龄< 10 岁的部分,我们可以看到存活率很高。而且存活率很低,超过 45 岁就下降了。
在这里,我们使用的箱宽度为 5,您可以尝试不同的值,看看图形如何变化。
存活率基础年龄、性别和船票等级
考虑到所有这三个变量,这张图表有助于确定生存模式。


上面的 3 个部分描述了三个阶层的女性生存模式,而下面的 3 个部分代表了三个阶层的男性生存模式。在 x 轴上我们有年龄。
很明显,一年级和二年级学生的存活率最高。除一名女童外,乘坐一等舱和二等舱的所有儿童都幸存下来。乘坐三等舱的男性存活率最低。
我希望这篇文章对你有所帮助。不断学*,不断成长!
网络安全 101 数据分析:检测数据泄露

Miguel Á. Padriñán from Pexels,
使用网络流量数据创建基本警报来检测数据盗窃
这既是最* 2019 趋势科技 CTF 中通配符 400 挑战解决方案的演练,也是网络安全监控的一些注意事项。我建议你先试试这里的挑战。所有解决方案的实现都可以在这个内核中找到。
前提
您是中型企业 XYZcorp 的网络安全管理员。你经常使用网络流量数据来发现异常的安全事件。该挑战提供了一些关于流的样本聚合数据,并使用异常事件的答案来构建标记。
这里的数据是合成的,不模拟典型的网络协议和行为。因此,这些挑战不需要深入了解网络协议。
我们要找什么?
本次挑战中的所有问题都与后利用活动有关,这些构成了网络杀戮链的后半部分。
<插入强制杀伤链图和说明>
网络安全的现代方法并不仅仅停留在试图防止利用。利用漏洞只是攻击的第一步,最终目标通常是*数据窃取。
*勒索软件攻击等攻击除外
攻击者如何从最初的立足点获取您的数据?
- 为了获取数据,攻击者需要破解数据
- 为了泄露数据,攻击者需要到达数据 ( 横向移动)
- 为了横向移动,攻击者需要与他们的立足点 ( 命令和控制)相协调

如果我们能够在这些阶段中的任何一个阶段检测并阻止攻击者,那么我们可以认为这是一次胜利!
“预防是理想的,但检测是必须的”——埃里克·科尔博士
当然,这是一系列更复杂事件的简化版本。如果你想在这方面了解更多,你可以浏览一下 ATT & CK 矩阵
数据泄漏

数据泄露是数据盗窃的一种奇特说法。 在某一点上,数据必须从您的网络内部流向攻击者手中*。
*当然也有例外,比如数据的物理泄露
公然渗透
我们的知识产权正大量离开这栋大楼。里面的一台机器被用来发送我们所有的小工具设计。一台主机从企业中发出的数据比其他主机多得多。它的 IP 是什么?
这里我们可以假设攻击者没有试图隐藏。他们试图传输尽可能多的数据,而不对数据传输设置限制。让我们看看出站网络流量最大的主机。

13.37.84.125 looks like a bad IP
我们将13.37.84.125识别为不良 IP,并观察流量出站的分布,发现这是非典型的。

13.37.84.125 is clearly an outlier
这是你能发出的最简单的警告。查看您的出站流量的每日分布,并找到一个阈值进行警告。攻击者在一个小时内将 50GB 数据上传到 Google Drive,三天后才发现数据泄露,这将是一件令人尴尬的事情。
您可能会发现正常的异常值!成为局外人不一定等同于恶意。您可以找到与网络其余部分相比出站流量非常大的主机,但却发现它们是正常的。
假设您的公司使用一个 web 代理服务器,并要求 HTTPS 流量通过该服务器进行代理。那么我们会期望这个代理服务器的流量比网络的其余部分大几个数量级。我们从中观察到的流量是数百个用户的 HTTPS 流量的总和。
在这种情况下,您应该记录这些特殊的服务器,并分别进行分析。同时,您可能还想检查那些产生或试图产生直接出站 HTTPS 流量的台式机,即使它们不消耗高带宽,因为它们应该通过代理。
业余活动
另一个攻击者计划导出我们内部 wiki 的内容。一台主机在非工作时间从企业发送的数据比其他主机多得多。它的 IP 是什么?
通常,我们应该对工作时间和下班时间有一些概念。对于这个挑战,我们首先必须推断出营业时间

Office hours are between 16:00 to 23:00
现在我们已经确定了公司的营业时间,我们只过滤 0:00 到 16:00 之间产生的流量,并查看在非营业时间具有最多出站流量的主机。

12.55.77.96 looks suspicious
这一次,我们看到12.55.77.96可能是我们的坏 IP。我们查看了非工作时间总出站流量的分布,我们也看到这是一个异常值。

12.55.77.96 is an outlier
只查看非工作时间的流量很重要,因为如果只查看整体出站流量,我们可能检测不到这一点。

12.55.77.96 looks normal if we include traffic from “on” hours
很明显,我们应该分别模拟“开”和“关”的时间。这很直观,你期望在白天看到的活动类型和晚上看到的不同。这包括周末和特殊假期。
这些间歇期也是你基线的良好起点。你对你的网络的“背景辐射”有一个概念。
通常在营业时间出现的糟糕交通状况,可能会在非营业时间凸显出来。
你可能还会发现内部威胁。“在休假、生病或在非正常时间远程访问网络”、“在非正常时间工作”是内部威胁的行为指标[1]。人们对身体监视更加敏感;他们更有可能在没有人的时候尝试做坏事,而没有意识到他们的行为在网络层面是显而易见的。
休息时间是有价值的。如果我想识别呼叫总部、文件泄露和其他可疑活动,我喜欢在下班后观察。车流量少了,人也少了……这就是我喜欢记录一家公司自己的特殊下班时间的原因。对于某人来说,通过将所有活动保持在 9-5/M-F 来隐藏他的流量是很容易的,但是如果攻击者不知道公司给圣斯威辛放假,那么他就更有可能坚持到底。
迈克尔·柯林斯[2]
尽管如此,如果你去打猎,你会发现这里有许多合法的(可能没有记录的)业务流程,比如那些试图在晚上卸载以在白天对业务产生最小影响的流程。例如,您可能会发现数据库团队已经安排了每周午夜 12 点将您的数据库备份到您的亚马逊 S3 存储桶。这些绝对是你应该能够察觉的事情。
使用通用协议的隧道
一些攻击者正在获取所有员工和供应商的电子邮件地址,并通过通常用于其他用途的通道发送出去。这类似于攻击者滥用 DNS 进行数据泄露。一台主机在企业的某个端口上发送的数据比其他主机多得多。它的港口是哪里?
DNS 隧道是一种用于通过 DNS 协议的功能来渗透数据的技术。如果一台主机试图通过 DNS 过滤数据,那么我们预计发送到端口 53 的请求数量会比其他只使用 DNS 解析域 IP 地址的主机多得多。因此,我们要寻找的是该特定端口的异常流量。
实际上,我们可以使用与前面部分类似的方法,查看流量的主要来源,并根据端口的单变量分布查看它们是否是异常值。然而,当您现在必须查看许多端口时,这种方法的扩展性不好。
让我们首先看几个端口,我们看到它们中的大多数是“好的”(也许太好了),但是有不同的均值和方差。

Note that these are from synthetic data
在查看了一些分布后,它们看起来是钟形的,因此使用 Z 分数可能是合适的。然而,如果你发现分布是高度倾斜的,那么你可能想做一些变换,比如对数变换,使分布更像钟形。
对于每个端口,我们对出站流量进行标准化,并获得每个端口的最高 z 值。

Clearly port 124 has the “most outlying outlier”
并且调查 124 端口的分布,我们看到12.30.96.87看起来像是我们的不良 IP。

如果我们将端口 124 的总出站流量与其他端口进行比较,我们会发现,如果我们使用全局阈值,这是无法检测到的。

If looking at the long tail, then maybe
那么我们如何检测到这一点呢?与上一节相似,有一个重复出现的主题:分别对不同类型的网络活动建模。如果您可以在网络流量中识别出不同的组,那么尝试分别分析它们。如果数据是同分布的,则更容易检测出异常值。
还有,也要考虑分析长尾。如果您看到不常用端口的出站流量,那么您应该调查它是什么。如果合法使用,请记录下来。否则,你一开始就不应该允许未知端口的出站连接。
然而,对于诸如 DNS 流量之类的协议,它们是必不可少的。所以你会发现这些端口是对外开放的。类似于 web 代理,为了让我们的生活更轻松,您应该要求所有 DNS 查询都通过内部 DNS 服务器,并阻止端口 53 上除内部 DNS 服务器之外的所有出站流量。通过来自 DNS 服务器的日志,您可以获得丰富的信息,使 DNS 隧道等检测技术变得更加容易。您不必将警报限制在频率和连接大小上,因为您可以使用唯一子域的数量或查询的唯一域的数量。
如果你想了解更多关于 DNS 隧道的信息,你可以阅读 Akamai 的博客。另一种类似且有趣的隧道方式是通过 ICMP 。
保护您的数据

想到当您在出站网络流量中检测到数据泄漏时,可能已经太晚了,这有点可笑!为了检测高出站流量,攻击者必须先窃取大量数据。
为了减轻这种情况,您必须考虑您真正关心的数据:
- 数据在哪里?
- 谁可以访问它?
- 他们如何访问它?
假设一个攻击者想要从您的 SQL 数据库中窃取 50GB 的数据。他首先必须将您的 SQL server 的表转储到他的主机,然后将这些表上传到一些外部云存储中。
如果您能够在以下任一方面发出警报,您甚至可以在攻击者泄漏数据之前就检测到他:
- 未经授权的 SQL 数据库转储
- 从 SQL server 传输的异常高的流量
例如,您查看您的 SQL server 通常会做些什么,并发现 SQL server 的大部分流量通常会与 web 应用程序服务器、一些 ETL 进程以及一些备份进程相关联。那么从 SQL server 到 SQL admin 工作站的高数据传输可能被认为是异常的。
通过一点分析,您可以集中精力于网络中的几个组件,而不是监控网络中的所有流量来寻找“任何奇怪的东西”。
将您的一些警报范围缩小到一些关键用户或系统,可以使您的模型更加高效,并且您能够检测到更微妙的高影响攻击。
下一个:横向运动

在下一篇博文中,我们将讨论一些关于寻找 横向运动的问题。
攻击者控制的初始主机可能无法访问攻击者想要的数据。然后,攻击者必须在网络中探索和导航,通过不同的主机和帐户,直到他达到最终目标。如果有合适的有利位置,这可能是我们可以探测到的
参考
[1] US-CERT,对抗内部威胁 (2014)
[2] M. Collins,通过数据分析实现网络安全(2014 年)
[3] SEC511:持续监控和安全操作
10.000 人工智能创业公司的数据分析
从 AngelList 公司提取见解
简介
AngelList 是一个将初创公司与投资者和希望在初创公司工作的求职者联系起来的地方。他们的目标是使投资过程民主化,帮助初创公司筹集资金和人才。无论是找工作、投资创业公司,甚至只是建立联系,这都是一个技术领域的每个人都应该知道的平台。自该网站于 2010 年创建以来,已有逾 400 万家公司、800 万名投资者和至少 100 万名候选人在其网站上注册。

在机器学*呈指数增长的时代,我想看看那些人工智能初创公司,并围绕它们和它们的活动领域进行探索性的数据分析。AI 领域的投资有多大?AI 创业公司如何规模化?哪些市场对他们来说最有前景?
数据析取
为了找到常见的相关单词,一个很好的工具是 explosion.ai 的 SenseToVec ,这是一个神经网络模型,它可以读取 2015 年发布到 Reddit 的每一条评论,并使用 word2vec 和 spaCy 构建一个语义地图。您可以搜索一个单词或短语,并获得与之最相似的单词(我甚至偶尔用它来查找同义词)。所以我输入了机器学*,得出了这样的术语:
- 数据科学
- 自然语言处理
- 计算机视觉
还有几十个。过滤掉一些术语后,我将剩下的作为查询输入到 Angel 的搜索框中。
刮网器是用硒和美汤制成的。它创建一个驱动程序来访问 URL(https://angel.co/companies),点击搜索栏并编写一个特定的查询。然后,它滚动列表中的每个公司并存储其数据。由于网站限制每次搜索 400 家公司,我选择使用过滤器并增加查询次数,以确保我得到几乎所有与每家公司相关的公司。

Angel Scraper
删除重复项后,结果是一个包含 10.139 个唯一数据点的 CSV 文件,包括以下特征:
- 名称’→公司名称
- 加入 ' →公司加入安吉尔的日期
- 类型’→公司类型(初创公司、私营公司、孵化器……)
- 地点 ' →公司所在地城市
- 市场’→公司的活动领域(电子商务、游戏……)
- 推销’→公司口号
- 募集 ' →投资公司募集的金额
- 科技’→主要编程语言(Python,Javascript…)
数据分析
在从数据中寻找见解之前,我必须清理和预处理数据,以便对分析有用。这包括一些步骤,如格式化日期、规范化文本以及将货币字符串转换为浮点数。之后,我导入了 Geopy 库来从 location 列中提取坐标信息,这样我们以后就可以处理纬度和经度了。以下是经过处理的数据帧的示例:

现在,我们可以用这样的数据框做很多事情。让我们从检查那些公司正在使用的编程语言开始。

哇!这是一个巨大的差异。当谈到机器学*时,Python 是使用最多的语言之一,它看起来像是安吉尔人工智能初创公司的一个很大的最爱。请注意,根据他们的说法,我们只是在 AngelList 顶级技术中进行比较,所以其他重要的编程语言没有包括在内。
我们可以按照加入的日期重新排列这些数据,并检查这些技术在过去几年的发展情况:

Tech growth by year
Python 确实在成长。它是一种令人惊叹的高级通用语言,拥有广泛而强大的库,可能是数据科学和机器学*领域最著名的语言。
回到我们的分析,我们来看看现在的市频。最常见的有哪些?

Market distribution
很好。虽然其中一些过于笼统(如 b2b 和 SaaS ),而其他一些可以归入同一类别(如大数据分析和大数据),但我们可以对现有行业进行很好的比较。
让我们试试更有趣的东西。将我们的数据按市场分组,并对增值求和,以查看按行业划分的投资总额:

Total investment by market
这是投资最高的 20 个市场。这并不一定意味着他们拥有最多的投资公司。让我们来看看最大的公司:
Airbnb → 10.3 Bi(酒店)
网景→ 4.2 Bi(新闻)
Nest → 3.3 Bi(物联网)
Palantir → 2.1 Bi(分析)
Grail → 1.7 Bi(诊断)
这解释了在酒店市场的巨大投资。一两家大公司可能在投资总额中占据太大比重。也许取每个市场的投资中值会给我们带来不同的结果:

Median investment by market
这是投资中值最高的 10 个市场。酒店市场已经不复存在。尽管如此,可能还有其他方法让我们得到更有启发性的结果。
让我们用他们的市场来统计被投资公司的数量,而不是得出投资金额。其次,最好能在投资范围之间进行比较。例如,有多少移动广告公司获得了 100 万到 1000 万美元的投资?
为此,我建立了一个交互式图表,在其中你可以点击按钮进行交互(最多 100 万,从 1 到 1000 万,等等)。对于每个按钮,你会看到一个条形图,显示在这个范围内筹集了一定金额的公司数量。

Number invested companies by market
这是一个更复杂的分析,可以让投资者和创始人更深入地了解这些市场与投资规模的关系。如果处于第一阶段(种子),在哪些市场更容易被养起来?哪些公司成为了十亿美元的独角兽?
根据你阅读这篇文章的设备,你可能无法使用下面的图表。否则,请随意与它互动并得出自己的结论。
Interactive Chart — Number of invested companies by market
利用每个行业每年的投资金额,我们甚至可以比较一些市场自 2011 年以来的变化。

Market growth since 2011
然后我们可以按阶段查看平均投资:

Average investment by stage
出于某种原因,A 轮的平均投资低于种子。让我们看看过去几年的投资总额(百万美元):

Total investments
我们清楚地看到,2012 年是 AngelList 爆发的一年,可能还有风险资本融资的增长和全球范围内越来越多的初创公司。下一张图显示了每年在网站上注册的创业公司的数量。

Number of startups per year
最后,我们可以使用从位置提取的坐标和 Geopy 构建一个包含这些创业公司全球分布的聚类图。结果是一个交互式地图,如下所示:

Cluster Map
这是这 10,000 家公司的位置图。即使是一个小样本,它也很好地代表了技术在国家间的分布。
为了制作它,我使用了 leav 库并将输出保存在 HTML 中。如果你想和地图互动,就去我的 GitHub 资源库→ 点击这里,下载cmap.html,在你的电脑里打开。

单击集群打开较小的集群,然后单击这些集群查看公司。如果你点击一家公司,你会得到他们网站的链接。
下图是按投资金额加权的热图( hmap_weighted.html ),或者说:AI 的钱都去哪了?

Heat Map
这还不到我们用这样的数据集所能做的一半。可以从员工数量(公司的规模)、公司的生命周期甚至的广告可以使用 NLP 进行分析。现在,让我们来看看创业口号中最常用的词。

Word Cloud
你还能提取什么?创始人、联合创始人和投资者的联系信息。网络抓取是惊人的,加上数据分析和机器学*,它成为一个令人难以置信的强大工具。
如果你想访问地图、数据或笔记本,只需进入我的 GitHub 知识库→ 点击这里,或者在下面留下评论。随时让任何观察,关注或想法,并感谢您阅读这篇文章。
AngelList 上人工智能创业公司名称的文本挖掘

AngelList 是一个美国网站,面向初创公司、天使投资人和希望在初创公司工作的求职者。在本教程中,我们想看看人工智能初创公司在其名称中使用的最流行的词语。
步骤 1:导入数据
我们使用由 rodrigosnader 跳过的数据。这个数据集包括 AngelList 上人工智能初创公司的 10,151 条数据记录。
第二步:数据争论
我们首先需要构建我们的数据,以更好地满足我们的需求。
分割名称
初创公司的名称存储在name列中。

我们可以看到一些公司的名称是由不带空格的单词构成的(例如 MonkeyLearn、NextDeavor),因此我们希望将这些名称拆分成单独的单词。
为此,我们可以使用我从一个 stackoverflow 线程中找到的 wordninja 包。
我们将把拆分后的名字以小写形式存储在names列表中。
词汇化
为了改进我们的分析结果,我们首先对我们的names列表进行术语化。
词汇化是将一个词转换成它的基本形式的过程。你可以从这篇伟大的作品中了解更多关于文本挖掘的过程。
用单词的基本形式更新名字列表。
停止言语
像the、a这样的停用词没有提供有用的意思,所以我们将把它们从列表中删除。
删除单个字符
同样,我们希望从列表中删除没有意义的单个字符。
第三步:生成单词云
列表中前 20 个不同的单词
现在我们已经完成了数据争论的过程,让我们来看看列表中最受欢迎的词。
我们可以看到lab、技术、data、network、neural是创业公司在名称中使用最多的词汇。

我们可以使用 WordCloud 来绘制结果,以便更好地可视化。
我们现在可以得到一个词云,即:

为了让 WordCloud 看起来更干净,我们可以自定义它的颜色主题。
我们结束了。

结论
所以我们可以从词云上看到,人工智能创业公司名称中使用的最有意义的词是:
- 实验室
- 技术
- 数据
- 网络
- 神经的
- 冒险
- 自然语言处理技术
- 超正析象管
- 技术
- 承认
- 系统
- 解决办法
- 健康
- 空气
- 分析学
- 应用
- 深的
- 云
- 组
我们可以得出这样的结论
- 有很大一部分人工智能初创公司在识别、系统开发、健康、空气、数据分析、云领域工作。
- 初创公司喜欢把他们的公司描述成一个实验室、风险企业和团队。
- 。io 已经成为创业公司的热门域名扩展。为什么?检查这里的和。
参考
数据分析显示,纽约州的地区医疗费用可能相差超过 10 万美元
根据你是住在纽约州西部、北部还是南部,医疗费用从几万美元到几十万美元不等。

Photo by Piron Guillaume on Unsplash
对我们许多人来说,医疗费用是一个谜。没有两个病人是一样的,所以他们的账单也不会一样。了解手术的价格范围有助于消费者进行财务规划。这让他们对自己的共同保险、自付额、共付额和其他医疗保健相关费用有了更好的了解。
如果我们可以只从我们当地社区或地理区域内的医院获得医疗费用,那不是更好吗?不幸的是,这种地方一级的信息很难获得。这部分是因为包含成本信息的文档(数据库表)中可能没有适当的地理位置标签。为了使医疗保健成本与您所在地区的医院相匹配,您需要合并和组合不同来源的信息,这些信息最初并不是设计在一起的。
这正是我在这个分析中所做的。我的目标是按地理区域分离纽约州(NYS)的医疗保健成本信息,并将我的发现报告为最常见医疗条件的价格范围。
为了完成这项任务,我使用了来自 NYS 卫生部的成本透明度数据集,并结合了县和地区级别的地理位置信息,以进行适当的分组。我对这个数据集很熟悉,因为我曾经研究过它之前我绘制了在 NYS 观察到的最常见医疗状况的患者数量、医院成本和定价加价的趋势。
通过这一分析,我发现了纽约西部、北部和南部地区内部和之间医院费用的巨大差异。价格变化可以在几千到几十,有时几十万美元的某些程序,如关节膝关节置换手术。此外,相对于该州的平均水平,纽约西部和北部地区的医院收费较低,而长岛、下哈德逊谷和纽约市的医院收费比该州的平均水平高出 50%,有时甚至高出 100%。这篇文章解释了我的发现。
纽约州是如何划分成区域的?
为了实现我的目标,我将地理位置信息合并到成本透明度数据集中,以便根据 NYS 的地理区域对医院进行分组。我根据环境保护部的县和地区名称将 NYS 分为 9 个地区。每家医院都根据其邮政编码被分配到所在的县。然后将每个县与其对应的地区进行匹配。这些地区是:
- 纽约州西部:纽约州中部,手指湖,纽约州西部
- 纽约州北部:首都区,东阿迪朗达克,西阿迪朗达克
- 纽约南部:纽约市长岛,下哈得孙谷
调查了哪些医疗条件?
最初,概要成本透明度数据库表包含 979,862 行条目,每个条目具有多条信息,其中之一是所有患者精细诊断相关组(APR DRG)分类。广义而言,APR DRG 分类是患者被标记为住院的医疗状况。示例 APR DRG 分类包括膝关节置换手术、慢性阻塞性肺病和心力衰竭等。
根据成本透明度数据集,2016 年患者因 315 种不同的 APR DRG 分类而住院。这意味着,这* 100 万个条目属于这 315 个不同类别中的一个。为了使这个分析有意义,我选择了在 NYS 最有代表性的 APR DRG 分类。因此,我选择了具有以下特征的分类:
- 病人数量最多,因此在 NYS 接受治疗最多,
- 在大多数医院都有发现,因此在 NYS 各地都有治疗。
最终,选择了 15 种疾病,这些疾病出现在 88%以上的医院中,总共占 2016 年住院总人数的 33%。根据他们的生理系统,他们被松散地分组,没有特定的顺序:
- 生殖相关: 1)阴道分娩,2)剖宫产,3)正常新生儿或有条件的新生儿
- 呼吸系统: 4)慢性阻塞性肺病,5)肺水肿&呼吸衰竭,6)肺炎
- 感染: 7) 败血症&播散性感染,8)蜂窝组织炎&其他细菌性皮肤感染
- 肌肉骨骼: 9) 其他肌肉骨骼系统(MSK) &结缔组织诊断,10)膝关节置换
- 心血管:11)心力衰竭,12)心律失常&传导障碍
- 心理:13)精神分裂症
- 胃肠道: 14) 主要胃肠道(GI) &腹膜感染
- 肾脏: 15) 肾脏&尿路感染(UTI)
我如何解读结果?
以个体价值表示的医疗成本用途有限。没有两个病人是一样的,所以他们的账单也不会一样。更确切地说,知道一个程序的潜在价格标签的范围(在这个词的口语意义上不是统计的)对我们的财务计划更有帮助。例如,知道一个程序的中值成本是 15,000 美元是有信息的。但是,如果您还知道价格标签的分布,并且中间的 50%患者的费用在 9,000 美元到 27,000 美元之间,您将对您的共同保险、自付额、共付额和其他医疗保健相关费用的金额有更好的了解。
在我的例子中,短语“中间 50%的患者”指的是医疗费用分布的中心。这被称为四分位距(IQR)。在描述性统计中,使用 IQR 是因为据说它对异常值具有稳健性,换句话说,不受异常值的影响。在医疗保健价格数据集中,异常值可以代表非常昂贵或便宜的病例。使用盒须图绘制 IQR。一个盒须图有三个组成部分:一个盒,盒内的一条线和两条须。方框代表 IQR,中间值内的线,在我的选择中,两个胡须延伸到第 10 和第 90 个百分位数。实际上,我的盒须图占据了 80%的医疗成本。最便宜的 10%案例和最贵的 10%案例被忽略。
如果你需要一个关于解释盒须图的更详细的解释,我在这篇文章的结尾提供了一个小的部分来做进一步的说明。网上有很多资源可以更好地理解百分位数、四分位数和盒须图。 这里有一个 。
现在,根据我的发现。
纽约南部三个地区和该州其他地区的医疗费用相差很大
纽约西部

Figure 1: Box and whisker plots showing the IQR, median (red line), 10th and 90th percentiles (whiskers) hospital charges for each of the 15 most common medical conditions (APR DRG classifications) of hospitals within Central NY, the Finger Lakes and Western NY.
除了几个明显的例外,纽约西部的三个地区的医疗费用大致相同。几乎所有 15 个 4 月 DRG 分类的中位数都严格限制在 9000 美元到 17000 美元之间。三个例外是新生儿护理、阴道分娩和膝关节置换手术。医院新生儿护理费用的中位数徘徊在 3000 美元左右。阴道分娩的平均费用接* 7000 美元。由于剖腹产属于外科手术,因此其费用明显更高是有道理的,平均费用为 12,000 美元,几乎是阴道分娩费用的两倍。
令人惊讶的是,膝关节置换手术具有最高的中位数,最大的 IQR 和最广泛的传播,清楚地观察到胡须最长。根据我的分析,膝关节置换手术具有以下价值:
- 纽约州中部 : IQR 从 29000 美元到 48000 美元不等()中位数为 37000 美元*)。当考虑第 10 和第 90 百分位时,医院成本分别扩展到 27,000 美元和 80,000 美元。*
- 手指湖: IQR 从 30,000 美元到 42,000 美元不等(36,000 美元中位数*)。当考虑第 10 和第 90 百分位时,医院费用分别增加到 23,000 美元和 63,000 美元。*
- 纽约州西部: IQR 的房价从 26000 美元到 36000 美元不等(中位数为 29000 美元*)。当考虑第 10 和第 90 百分位时,医院成本分别扩展到 24,000 美元和 43,000 美元。*
为了客观地看待这个问题,膝关节置换手术 的中位成本高于除精神分裂症之外的所有剩余 APR DRG 分类的第 90 百分位成本。唯一没有观察到这种趋势的地方是纽约西部。
纽约北部

Figure 2: Box and whisker plots showing the IQR, median (red line), 10th and 90th percentiles (whiskers) hospital charges for each of the 15 most common medical conditions (APR DRG classifications) of hospitals within the Capital, Eastern and Western Adirondack regions.
在纽约州西部地区观察到的类似趋势也在纽约州北部地区观察到。 几乎所有 15 个被调查的 4 月 DRG 分类的中位数都在 11,000 美元到 21,000 美元之间。同样,纽约州北部医院的新生儿护理费用中位数在 3000 美元到 5000 美元之间。阴道分娩的费用中位数在 8000 美元至 12000 美元之间,而剖宫产的费用中位数在 15000 美元至 18000 美元之间。
与东部阿迪朗达克相比,首都区和西部阿迪朗达克的 IQR 和医疗费用差距较小。败血症、水肿和呼吸衰竭、肺炎、严重胃肠道感染都属于这种情况。例如,东阿迪朗达克地区的水肿和呼吸衰竭的 IQR 为 22,000 美元(从 15,000 美元到 37,000 美元不等;中位数为 27,000 美元)。在首都地区,IQR 为 13,000 美元(从 18,000 美元到 31,000 美元不等;中位数为 21,000 美元),而西部阿迪朗达克地区的 IQR 为 15,000 美元(从 12,000 美元到 27,000 美元不等;中位数为 17,000 美元)。更高的 IQR 表明医疗成本更加分散。
在纽约州西部,膝关节置换手术的住院费用中值最高,IQR 最大,分布最广。
- 首都地区 : IQR 从 27000 美元到 43000 美元不等(中位数 33000 美元*)。当考虑第 10 和第 90 百分位时,医院费用分别增加到 22,000 美元和 59,000 美元。*
- 东部阿迪朗达克: IQR 从 40000 美元到 51000 美元不等()中位数 42000 美元*)。当考虑第 10 和第 90 百分位时,医院费用分别增加到 32,000 美元和 60,000 美元。*
- 西阿迪朗达克: IQR 从 3 万美元到 5.1 万美元不等(中位数为 3.9 万美元*)。当考虑第 10 和第 90 百分位时,医院费用分别增加到 26,000 美元和 52,000 美元。*
纽约南部

Figure 3: Box and whisker plots showing the IQR, median (red line), 10th and 90th percentiles (whiskers) hospital charges for each of the 15 most common medical conditions (APR DRG classifications) of hospitals within Long Island, New York City and Lower Hudson Valley regions. Note the changes in scale and intervals for healthcare costs. The maximum value in the x-axis now reaches $200,000 and the intervals are in $20,000 increments.
在构成纽约州南部的三个地区,医疗保健价格处于不同的联盟。请注意这些图表和纽约州西部/北部图表在比例和间隔上的差异。纽约州西部和北部的最高费用加起来不超过 8 万美元。在纽约州南部,最高费用接* 18 万美元。为了适应这些变化,间隔现在以$20,000 而不是$10,000 的增量表示。
纽约南部的东西都贵得多。
继续我们对新生儿护理的分析,纽约州南部医院的平均成本在 7000 美元到 12000 美元之间。阴道分娩的费用中位数在 18,000 美元至 20,000 美元之间,而剖宫产的费用中位数在 26,000 美元至 34,000 美元之间。新生儿护理和阴道分娩是纽约州南部最便宜的两项中间成本。* 所有其他 APR DRG 分类的中值成本在 24,000 美元到 53,000 美元之间。 相比之下,在纽约州西部和北部,所有其他 APR DRG 分类的所有中值成本的大部分都没有超过 20,000 美元。纽约州南部的 IQR 也明显更大,最小的为 6000 美元,最大的为 59000 美元。这再次表明,中间 50%的医疗保健费用广泛分布在数万美元之间。*
然而,最惊人的费用还是来自膝关节置换手术。在纽约州南部,费用如下:
- 长岛 : IQR 从 6.5 万美元到 11.6 万美元不等(,中位数 8 万美元*)。当考虑第 10 和第 90 百分位时,医院成本分别扩大到 52,000 美元和 156,000 美元。*
- 纽约州: IQR 从 48000 美元到 107000 美元不等(中位数为 72000 美元*)。当考虑第 10 和第 90 百分位时,医院成本分别扩大到 34,000 美元和 182,000 美元。*
- 下哈得孙谷: IQR 从 5 万美元到 10 万美元不等(中位数为 6.3 万美元*)。当考虑第 10 和第 90 百分位时,医院费用分别增加到 37,000 美元和 130,000 美元。*
I 之前报告过膝关节置换手术是 APR DRG 分类中少数几个实际上在 2009 年至 2016 年间患者出院量显著增加且费用增幅最大的手术之一。
哪些医院收费过高?
接下来,我想确定哪些医院收费比同行高,以及它们位于哪里。
为了比较医院,我为 NYS 的 211 家医院定义了一个新的术语叫做相对价格。医院的相对价格是通过平均所有的 DRG 收费率来计算的。APR DRG 收费比率被确定为医院为特定 APR DRG 分类收费的金额除以 NYS 所有医院的相同 APR DRG 分类的平均收费之间的比率。换句话说,APR DRG 收费比率提供了一家医院在特定条件下的收费是高于还是低于所有医院的信息。
医院相对价格比是一项指标,表明一家医院相对于该州所有其他医院的平均收费。
相对价格比< 1.0 indicates the hospital charges below the average, a relative price > 1.0 表示医院收费高于平均水平,相对收费= 1.0 表示医院收费与该州的平均水平相同。
市场份额越大的医院收费越高吗?
我首先根据医院的出院市场份额来划分医院。他们的出院市场份额是一家医院的住院出院人数除以 NYS 的住院出院总人数。两者呈弱正相关(皮尔逊相关系数= 0.33),表明市场份额较大的医院与高于均值的收费呈弱相关。

Figure 4: Relative Price of hospitals (each dot represents a hospital) plotted against its discharge market share.
没有多少医院一开始就有很高的市场份额(不出所料,因为 NYS 有 200 多家医院)。但是有许多医院的收费高于 NYS 的平均水平。这些医院的相对价格都在 1.0 以上。现在知道医院收费多了,那医院在哪里呢?
在 NYS 的哪个地区有相对价格较高的医院?
我把所有的医院和它们的相对价格比,按它们所属的地区分开。 我发现 纽约州北部和西部的所有医院的相对价格比< 1.0,表明他们的收费并没有高于 NYS 的平均水平。

A relative price ratio > 1 indicates a hospital charges above the median price. A relative price ratio < 1 indicates a hospital charges below the median price.
有趣的是,在哈德逊河谷下游和纽约市的许多医院收费也低于州平均水平。但是,哈德逊河谷下游地区确实有一些医院的收费比平均水平高出 50%(相对价格比= 1.5),在某些情况下,收费比平均水平高出* 100%(相对价格比> 2.0)。
纽约市医院运行了所有的价格比率。相当一部分的相对价格比率为 1.0,有些甚至超过 2.0。
长岛在这方面很有意思,因为除了一家医院之外,所有医院的相对价格比率都大于 1.0。
摘要
智能数据分析与清晰的可视化相结合,有助于更好地了解 NYS 的医疗成本。我的一些主要发现是:
- 住院膝关节置换手术一直是最昂贵的医疗条件治疗。长岛医院的费用最高,中位数为 8 万美元。
- 医疗条件不同,花费的范围也大相径庭。在一些情况下,如新生儿护理,提供者收费在几千美元之间。在其他像膝关节置换手术提供商收费范围接* 150,000 美元。
- 总的来说,纽约州西部医院和纽约州北部医院的医疗费用非常接*。
- 纽约州南部(长岛、纽约市和下哈得孙谷地区)的医院对 15 种医疗状况中的 13 种收取 24,000 美元至 53,000 美元,而纽约州西部和北部的医院不超过 21,000 美元。
- 医院相对价格与其出院市场份额之间存在弱正相关关系。
- 纽约州西部和北部所有医院的收费都低于 NYS 的平均水平。
- 绝大多数长岛医院的收费高于该州的平均水平。与此同时,纽约市和下哈德逊河谷地区的医院根据相对价格收取的费用低于、等于或远远高于该州的平均水平。
感谢阅读!
代码:
使用 pandas、numpy 和 matplotlib 库在 Python 中进行了数据分析。代码可以在我的 github 存储库中找到。附件是我在分析中使用的数据库表。
代码: Github
IQRs 和盒须图上的底漆
四分位数区域(IQR)通常显示为盒须图。网上有大量解释百分位数和盒须图的资源,但为了简单起见,我附上了我最喜欢的之一。
为了理解百分位数,想象一组递增的数字,并将它们分成 4 个大小相等的组。您已经将数字集合分为第 25 个百分位数、第 50 个百分位数和第 75 个百分位数。你的每一组也可以被称为四分位数,用于你进行的四分位数划分。正好在你的集合中间的数字叫做中位数。如果你想看看你的第 25 和第 75 百分位的界限(或者第 2 和第 3 四分位),你会发现“中间”四分位。中间四分位数内的数字被称为内四分位数范围(IQR)* 。实际上,IQR 告诉您关于数据集中心的信息。用来传递这些信息的图表被称为盒须图。*

Example box and whisker plot showing the IQR, median, 10th and 90th percentiles labelled. A histogram of the data is charted above it, with shaded regions highlighting the IQR in blue, the 10th to 25th percentile and 75th to 90th percentile. Clearly, some data points are excluded from the box and whisker plot.
盒须图——顾名思义——有盒须。这个盒子代表我们上面讨论过的 IQR。触须通常延伸到数据的最小值和最大值(有效地显示数据集的范围)。然而,在我的分析中,我将胡须定义为延伸到第 10 和第 90 个百分点,因为我想避免离群值。
左边是直方图下面的方框和须状图。直方图绘制了本例 APR DRG 分类的医疗保健成本的整体分布。请注意直方图中蓝色阴影区域是如何拥有数据集的最高计数(高频)的。如果您将阴影区域从直方图扩展到一个方框和须状图,它将突出显示方框。IQR 的大小会缩小(或拉长)盒子的大小。盒子里面是一条红线,它是中间值,分布的中心点。
在这个例子中,左边的胡须延伸到第 10 个百分位数,而右边的延伸到第 90 个百分位数,每个都用灰色阴影表示。注意直方图中有多少数据点没有被灰色阴影区域覆盖。它们位于第 10 个和第 90 个百分位数之外,没有被盒须图和须图捕获。通过将胡须限制在第 10 和第 90 个百分位数,该信息被忽略。
我使用盒须图来绘制医疗价格的目的是传达大多数人将会陷入的价格范围。那些极其昂贵的案例位于分布的尾端(注意它们在直方图上的频率计数是 1),而不是常态。报道它们会分散我想传达的主要信息。
数据分析实践指南:如何开始?
很多初学者对如何学*数据分析很迷茫。今天我就来介绍一下数据分析的全过程,来解答大家的疑惑,开拓思路。
相信你已经知道数据分析在现代社会的重要性。掌握了数据,就掌握了规律。当你了解了市场数据,并对其进行分析,你就能得到市场规律。当你掌握了产品本身的数据,分析它,你就可以了解产品的用户来源,用户画像等等。数据分析如此重要,既是新时代的“数据结构+算法”,也是企业争夺人才的高地。

1.数据分析的流程是怎样的?
数据分析主要分为三步。
- 数据收集
那就是拿原材料,没有数据我们无法分析。
- 数据挖掘
数据挖掘是整个商业的价值。数据挖掘的核心是挖掘数据的商业价值,也就是我们所说的商业智能。
- 数据可视化
简单来说,让我们直观的了解数据分析的结果。

这样说话可能太简单了,我来详细给你介绍一下这三个步骤。
1.1 数据收集
在数据收集部分,您通常使用不同的数据源,然后使用工具来收集它们。
在网上,你可以收集各种各样的数据集。还有很多工具可以帮你自动抓取数据。当然,如果你写一个 Python 爬虫,效率会更高。掌握 Python 爬虫的乐趣是无穷的。它不仅可以让你在社交媒体上获得热评,自动下载带有关键词的海报,还可以自动为你的账户添加粉丝,给你自动化的快感。

1.2 数据挖掘
第二部分是数据挖掘,可以比作整个数据分析过程的“算法”部分。首先你需要知道它的基本流程,十大算法,以及背后的数学基础。在这一部分,我们会接触到一些概念,如关联分析、Adaboost 算法等。
掌握数据挖掘就像拿着一个水晶球。它用历史数据告诉你未来会发生什么。当然它也会向你展示高可靠性。

1.3 数据可视化
第三是数据可视化,这是我们特别感兴趣的非常重要的一步。数据通常是隐式的,尤其是当数据很大时,可视化是理解数据结构和结果表示的好方法。我们如何将数据可视化?有两种方法。
首先是用 Python。在 Python 中清理和挖掘数据的过程中,可以使用 Matplotlib、Seaborn 等第三方库进行渲染。
二是使用第三方工具。如果已经生成了 csv 格式文件,想用所见即所得的方式渲染,可以使用 Data GIF Maker、 Tableau 、 FineReport 等第三方工具。,可以轻松处理数据,帮你做演示。更多关于 aout 数据可视化工具的信息,可以阅读本文2019你不能错过的 9 款数据可视化工具。

当然这些理论都比较抽象,所以我觉得学*数据分析最好的方法就是在工具中使用,在项目中加深理解。
2.实践指南
刚才我们讲了数据分析全景,包括数据采集、数据挖掘、数据可视化。你可能觉得有很多东西,无从下手,或者觉得数据挖掘涉及很多算法,有些很难掌握。其实这些都是不必要的麻烦。
这里介绍一下 MAS(多维,问,分享)学*法。用这种方法,学*数据分析就是一个从“思考”到“工具”再到“实践”的过程。
今天我将从更多的角度与大家分享我的学*经验。我们可以
把今天的内容称为“实践指南”。我们把知识变成我们自己的语言,它就真的变成了我们自己的东西。这个转化的过程就是认识的过程。
那么如何提高自己的学*能力呢?简单来说,就是“知行合一”。
如果说认知是大脑,工具就像我们的手,数据工程师和算法科学家每天都和工具打交道。如果你开始做数据分析项目,脑子里已经想好了数据挖掘的算法模型,请牢记以下两条原则。
2.1 不要重复生产车轮
我见过很多有数据收集需求的公司。他们认为一些工具不能满足他们的个人需求,所以他们决定招募人员来做这项工作。发生了什么事?经过一年多的实践,他们投入了大量的资金,发现了大量的 bug,最终选择了第三方工具。这时候,其实有了及时评估的需要,就可以及时挽回损失。比如 数据上报 像 FineReport 这样的工具可以为各种行业提供解决方案。这也有助于你的需求评估。
2.2 工具决定效率
“不要重复生产轮子”的意思是你首先需要找到一个可以使用的轮子,这是一个工具。那么我们该如何选择呢?
这取决于你要做的工作。工具没有好坏,只有适合不适合。除了研究型的工作,大多数情况下,工程师会选择最用户友好的工具。比如 Python 有很多处理数据挖掘的第三方库。这些库有大量的用户和帮助文件来帮助您入门。
如果你在寻找合适的数据分析工具,可以参考本文2019 年 6 大数据分析工具 。
选好工具后,你要做的就是积累“资产”。很多知识点很难记住,也无法按照工具的指示去做,但通常能记住的是故事,做过的项目,做过的题。这些课题和项目是你的第一批“资产”。
我们如何快速积累这些“资产”?答案是熟练度。解决问题只是第一步。关键是训练我们工具使用的“熟练程度”。随着熟练度的提高,你的思维认知模式也在逐渐完善,效率自然会提高。
结论
认知三部曲,从认知到工具再到实战,是我最想和大家分享的学*建议。而且希望这篇文章对你有帮助!
您可能也会对…感兴趣
数据分析:使用 Python 预测住房市场

Overlook of Seattle from the top floor of an apartment near Westlake. Photographed by W. Weldon.
自 2008 年房地产泡沫破裂以来,我们进入了衰退,并在 2012 年跌至谷底。房地产市场再次上涨,价格已经超过了 2008 年的峰值。多年来,西雅图的住房市场一直以 85%的年增长率名列增长最快城市的前 3 名。
随着房价推向另一个新的高峰。我对负担能力表示同情,因为在过去的 9 年里,我在西雅图搬了 10 次家,因为房价也会直接影响租赁市场。每个人都想住在一个舒适的地方,尤其是有孩子的家庭。然而,一旦你买了房子,你就有了另一个需要担心的问题。
当抵押贷款还清后,拥有一套房子可能会成为你最大的资产。当房子的价格波动时,因为房子像股票和债券一样是一种投资,它会让人们害怕,因为房子的价值可能会低于抵押贷款余额。在一个完美的风暴场景中,你失去了工作,拖欠月供,你可能会失去房子和欠银行的钱。(他们以低于抵押贷款余额的价格拍卖你的房子。你拥有的钱=抵押贷款余额-出售价格。作为一个被动和脆弱的房主,你怎么能意识到下一场风暴呢?我将分析过去两年的一些房屋销售记录,并告诉你房屋市场的情况。
你买得起房子吗?
使用 Octoparse ,我在 2018 年和 2019 年刮出了大约 6000 套出售的房屋,数据包括卧室数量、出售价格和邮政编码。以下是步骤:
A.搜集数据
第一步:从 Trulia 中抓取一个 URL 列表。
第二步:将列表加载到 Octoparse 中。
第三步:从 Octoparse 中选择提取的数据字段。
第四步:保存并运行提取。
第五步:导出 CSV。
(他们的博客里有更多循序渐进的教程。)

B.使用 Python 分析数据[4]
步骤 1: 读取 CSV 文件,将每一行按照售价、卧室数量、平方英尺和出售日期进行分割。
第二步:将所有数据存储在元组列表中,列表中的每一项都是标有 price、bd、sqft、date 的值。
第三步:我展示的是 2018 年和 2019 年到今天,西雅图一共卖出了 5365 套房子。
第四步:找出 2018 年和 2019 年(到今天)卖出了多少套房子。这表明
2019 年销售的房屋:2309
2018 年销售的房屋:3056
第五步:按卧室数量筛选已售房屋。我想看看卧室数量的价格。特别是,不同数量的卧室的平均价格、中间价格和最高价格是多少?这里有一张数据表格。

Home sales in the second half of 2018 and the first half of 2019 by bedroom size
已售房屋告诉我们什么?
我使用 Python 来计算卧室的数量和销售价格,以便观察数量和价格之间的关系。我想出了三个卖出价格的数字:平均值、中间值和最高值。
三居室的房子是销售中最受欢迎的,售出了 1937 套。虽然三居室房屋的中值价格为 734,000 美元,但平均值略高于中值,这意味着更多三居室房屋的售价更高(意味着它们更难以承受)。)第二受欢迎的住宅类型是两居室住宅,售出 1577 套,中位数价格为 60 万美元。接下来是 4 居室房屋,售出 892 套,中值价格为 823,000 美元。

Home sales by ZIP codes
这些地区的家庭收入情况如何?
在 2018 年和 2019 年(截至今天),西雅图约有 6000 套出售的房屋。当数据通过邮政编码可视化时,我们可以看到 98103 和 98115 销售的房屋最多,分别为 392 和 383 套。它和那个地区的家庭收入有什么关系?由于这些地区靠*谷歌、亚马逊、Adobe campus 等科技巨头的所在地,但不像市中心那样昂贵,高收入和每天通勤的人需要喜欢住在市中心以北,以便方便地到达 I-5 高速公路。
拥有一套价格在中位数的三居室意味着什么?
我们来分解一下数字。有 36%的售出房屋属于 73.4 万美元的中等类别。这意味着,在 2018 年和 2019 年,大多数房主都可以负担得起 73.4 万美元的中位价格。我计算抵押贷款,看看你到底需要赚多少钱才能买得起中等价位的房子。它如下:
尽管每月都有债务,你希望快乐地生活在 36%的债务收入比中,但你的家庭每年需要赚 190,000 美元才能买得起一套售价 734,000 美元的三居室房屋。你击败了多少收入 19 万美元的人?根据 Pew Research[1]的调查,你属于前 25%的人群,这意味着你在大西雅图地区属于高收入阶层。报告还称,底层 90%的人拥有的收入缩水了 12.8%,而顶层 10%的人拥有美国全部收入的 50%。

Home price over time by the number of bedrooms

思想?
从一年前开始,西雅图的房地产市场在价格上相当平稳。对于那些提前买房的人来说,他们的房子正在升值。相比之下,一年内刚买了房子的人,很难说价格何时会再次北上。虽然每周的销售数量略有下降,但看看 2、3、4 居室房屋的中位价格以及你需要多少钱才能买得起一套房子,中产阶级在大都市地区开始节节败退就不足为奇了。收入的增长赶不上房价的上涨。我没有看到蓬勃发展的繁荣,但富人和其他人之间的差距越来越大。我们一直在努力让社会变得平等和文明。然而,从尼克松的禁毒战争,约翰逊的犯罪战争,到特朗普的移民战争,我仍然没有看到美国梦的自由和更大的好处。相反,这些年来,我只看到人们失去了工作和家园。
[1]https://www . pewresearch . org/fact-tank/2018/09/06/are-you-in-the-American-middle-class/
[4]
数据分析很难…以下是你如何超越的方法
我在 Google、Visa 和 Lazada 担任数据分析师/工程师 3 年来学到了什么

Upskill yourself (source)
我真的很喜欢从事数据工作。从大二开始,我就已经知道,我要把技术作为解决现实商业问题的一种方式。这给了我工作的意义,我希望这对你也一样。
每天我都会挤出每一点时间来阅读和学*。我会报名参加本地数据黑客马拉松,组织与分析相关的活动,向行业领袖学*。我在会议和大学活动中发言,指导我的后辈如何在大型科技公司中成为一名成功的数据分析师。我把自己想象成一名学生,去影响尽可能多的低年级学生,让他们学*并回馈社区。
如果你说这个最终目标听起来太高尚,你是对的!
事实上,作为一名基督徒,我懂得了时间是宝贵的,地球母亲是我们暂时居住的终点站。世界上没有什么是永恒的,包括时间。因此,我们需要创造和传递尽可能多的价值,为与上帝同在的永生做准备。对我来说,我的宗教和数据职业给了我超越、学*和贡献的意义。
在我获准进入谷歌担任数据分析师以开发更好的 ML 模型来打击滥用后,我收到了许多分享我的人生旅程和建议的请求,供我的下级思考。因此,我真的希望这个博客能够满足你的需求,给你一个成功的数据专家的起点。
我给有抱负的数据专业人士的最大建议
众所周知,数据科学和分析已经成为一个快速发展的行业,在过去几年中增长最快。短短几年内,许多大学开始提供数据科学专业,来自世界各地的成千上万的注册者。即使在新加坡,5 年前也没有这样的项目,但现在,这个学位的入学已经变得像进法律和商学院一样难了。
然而,尽管有上升的趋势,兴奋的去向还有很多不确定性。数据科学家的就业市场正变得越来越饱和和虚幻。许多创业公司开始意识到他们在数据科学方面进展太快,并开始解雇他们的数据科学家。
因此,为了确保你的未来,你需要迅速成熟起来,让自己与众不同。就像一场战争,你需要开始准备你的军械库。最好的方法,就是多做贡献:多学*,多写作,多说话,多专门化,多放松
学*:数据科学的燃料

不断尝试你的学*方式(动觉、听觉、视觉)。
当我在学*的时候,我的朋友总是给我一些精选的机器学*材料。但在阅读和听了大量视频后,我意识到我是一个动觉型的人(通过做来学*),我从听力中保留的很少。知道这一点后,我创建了自己的项目,并记录在 Github 上。令人惊讶的是,这些项目成了我进入 Visa 和 Google 的关键。
同样,也不要盲目遵循朋友建议的常规学*资料。做好你的尽职调查,并始终有一个试错的心态。很快,你会发现你最好的学*方式来提高你的技能。
对我来说,我通常使用来自 Youtube 和研究论文的许多不同种类的资源。我个人喜欢sendex和 Computerphile 。强烈推荐你观看这些视频,更好的是,复制它们。
此外,我还在佐治亚理工学院攻读兼职在线硕士学位,这让我接触到了机器学*和统计方面更深入的技术严谨性。
为了让你快速了解这个令人兴奋的行业,你需要不断地试验、尝试和犯错。
写:数据科学的遗产

写文章,把你的代码分享给 github,甚至创建一个 Youtube 频道。
在大学期间,我开始为大三学生编写在线教程,以解决他们的大学项目。一旦我形成了这些教程,我就会建立一些小会议来培养一个学*小组,并分享一些机器学*相关的模型。通过分享,我获得了乐趣,并学会了表达自己的想法。
同样,我也相信你写得越多,学到的东西也就越多。每次写一些项目,都会对某个模型/测试/代码评审进行反思。它会让你缩小知识差距,并找出如何提高技能和找到更好的解决方案。
到目前为止,我发现媒体是写作的最佳渠道之一。它为每个人的写作提供了一个圆滑和标准化的外观,这使你摆脱了处理视觉布局的麻烦。Medium 中的所有内容都已经针对您的读写进行了优化。
更好的是,你还可以注册计量收费墙。这将给策展人分发你的文章和提高你的读者群的机会。到目前为止,我已经发布了 30 篇文章,其中一些文章每篇赚了 300 美元。
老实说,我认为写作的最大好处是你有机会清晰地表达你的学*。不管你是在网上写博客还是制作 youtube 频道都没关系。目标是最大化你学*和提升自己的时间。
发言:数据科学的发言人

My dream is to learn and share my thoughts to inspire my fellow juniors. My sharing at SMU
教你的同伴或任何会议!
当你说话时,你是在传播你的知识给别人使用。你在提升你自己和你的公司。从长远来看,你会成为更有价值的数据科学家,因为你已经激励你的下属也这样做了。你将与你的同龄人区分开来。
此外,说会给你进一步的学*意义。你学得越多,说得越多。同样,你说得越多,学得越多。
最初,我会给大学、数据会议和青年团体发电子邮件,看看能否分享我在数据科学方面的一些作品。我很幸运,新加坡管理大学(SMU)和新加坡国立大学(新加坡国立大学)的一些学生团体接受了我的请求。
当我谈论我的知识时,我不仅鼓舞了我的下级,也向他们学*,以更好地交流我的想法。
专攻:数据科学的专业知识

尝试深入钻研某项数据科学技术,以补充你的优势(商业、社会科学、销售等)
有自己的心态,坚持下去。最常见的误解是,由于对技术专业知识的需求,商科学生会输给 IT 学生。这并不准确。我认识的很多超级分析师都有不同的背景,比如社会科学、商业和经济。他们使用分析来补充他们的专业知识。
因此,保持冷静,利用你的优势,而不是沉迷于最新的分析趋势,如 Random Forest,XGBoost 等
例如,如果你有金融背景,你可以开发自己的股票分析项目。如果你来自运营和库存管理,你可以专注于 JIT(准时制)分析,以最小化瓶颈,最大化效率。当你使用分析工具对你的领域进行数据驱动分析时,天空是无限的。
对我来说,我是软件开发出身。但是我通过开发一个简单的产品,用商业分析来交流我的技能。一天,我问学金融的朋友,他们是如何分析股票的。他们的回答让我吃惊,因为他们花了大部分时间将谷歌金融的每一个数据复制粘贴到一个 excelsheet 中并进行分析。作为回应,我创建了一个简单的程序,使用价值投资方法来抓取网页并产生内在价值。这已经成为我的旗舰产品,我和 SMU 的同学们分享。
Chill:数据科学的乐趣

My experience cycling 515 km over 3 days in Java Island Indonesia
避免精疲力尽,享受黑客的乐趣
人际关系在生活中很重要。因此,继续和你的朋友一起黑客吧。利用周六学*,周日放松。
此外,加入学*型公司。Visa 和 Google 就是其中的几个。在科技公司工作最大的好处是,你可以学到未来几年需要的关键技能。
对我来说,我很高兴通过在教堂和朋友们一起放松来避免筋疲力尽。每当我有空的时候,我都会健身。我会为马拉松、铁人三项,甚至海外自行车旅行(最*在印度尼西亚爪哇岛三天跑了 515 公里)进行训练。工作很忙,但也很充实,一旦新的星期一开始,我会重新振作起来。索利·迪奥·格洛丽亚。
最后…
我真的希望这是一本很棒的读物,是你发展和创新的灵感来源。
请在下面评论提出建议和反馈。就像你一样,我也在学*如何成为一名更好的数据科学家和工程师。请帮助我改进,以便我可以在后续的文章发布中更好地帮助您。
谢谢大家,编码快乐:)
关于作者
Vincent Tatan 是一名数据和技术爱好者,拥有在 Google LLC、Visa Inc .和 Lazada 实施微服务架构、商业智能和分析管道项目的相关工作经验。
Vincent 是土生土长的印度尼西亚人,在解决问题方面成绩斐然,擅长全栈开发、数据分析和战略规划。
他一直积极咨询 SMU BI & Analytics Club,指导来自不同背景的有抱负的数据科学家和工程师,并为企业开发他们的产品开放他的专业知识。
文森特还在best top和 10to8 上开设了他的一对一导师服务,指导你如何在谷歌、Visa 或其他大型科技公司获得你梦想的数据分析师/工程师工作。如果你正在寻找良师益友,请在这里预约你和他的约会。
最后,请通过 LinkedIn , Medium 或 Youtube 频道 联系文森特
数据分析:为什么经常达不到预期的结果

那些拥有正确数据和可用的正确分析方法的人将能够使预测更便宜、更具可扩展性。然而,只有少数公司成功地从单个用例走向了大规模部署。这样,经济承诺就无法兑现。
通过在决策过程之前提供基于事实的信息平台,数据分析有助于将错误决策降至最低。换句话说,数据分析可以帮助回答这样的问题:客户和业务合作伙伴需要什么?未来特定产品的需求量是多少,或者我可以预测材料磨损导致损坏之前的时间吗?如果你有正确的数据和适当的分析方法,你今天就可以回答所有这些问题,从而拥有明显的竞争优势。
机会不会耗尽。
2013 年,美国心理学家和行为经济学家丹·艾瑞里宣称:“大数据就像青少年性行为:每个人都在谈论它。没人知道怎么做。每个人都认为其他人在做,所以每个人都声称自己在做。”那是五年前的事了,从那时起,公司发生了很多变化。保持不变的是试点项目的规模。造成这种情况的原因有很多,总体而言,一家公司的数字化水平与公司中分析方法的跨职能扩展之间存在明显的相关性。

遗漏扩展的最常见原因是:最高管理层没有向分析团队提出正确的问题,并且没有在数据分析能力的开发方面进行足够的投资。结果可能令人沮丧:数据分析程序半途而废,达不到管理层的预期。继续学*的必要投资缺失,恶性循环必然开始。
经济利益半途而废。
然而,他们确实存在,这些公司的分析程序是成功的。但是这些公司做的有什么不同,或者其他公司做错了什么?要回答这个问题,您必须详细查看每个案例。
然而,十个典型错误通常与数据分析程序的失败有关:
- 管理层缺乏远见。
高级管理层不清楚他们想通过数据分析实现什么目标。 - 没有计算用例。
没有比较成本和收益,因此没有选择尽快为公司提供附加值的用例。 - 没有分析策略。
各单项工程互不相关。他们的选择是任意的。 - 角色没有明确定义。
专家和人事经理还没有为分析领域制定详细的工作简介。 - 分析专业人员太少。没有足够的员工既懂技术,又了解业务流程和市场,并能在技术专家和商业经济学家之间进行调解。
- 数据分析不是核心业务的一部分。
因此,来自这一领域的发现无法为公司的成功做出贡献,数据专家的工作也是无效的。 - 根据给予每个人平等份额的原则,代价高昂的数据清理。这个不是很有效,但是很贵。
- 分析架构服务于错误的目标。
新的分析架构的开发目的不是交付最佳结果,而是基于公司的旧 IT 系统。 - 数据分析计划的财务收益未知。没有人详细考虑过分析程序对公司的财务影响。
- 未考虑数据分析的道德、社会和法律后果。
注重技术的测试会导致损害形象的问题,甚至会产生法律后果。
尽管这十个错误可能不同,但它们都有一个共同点。它们表明:那些想要利用数据分析提供的机会的人,必须让他们的整个企业文化与这一目标保持一致。仅仅组建一个专家团队,并相信他们正在努力克服困难,同时仍然交付突破性的成果是不够的。数据分析必须是核心业务的一部分,并集成到营销和销售等其他业务流程中。

此外,除了技术人员和策略师之外,“分析专业人员”也必须加入进来。他们代表着数据专家与公司和最高管理层之间的联系。他们将公司管理层的目标和问题转发给数据专家,并将数据分析的结果反馈给公司。
管理层需要专业知识来回答重要问题
除了专家组之外,公司管理层也应该具备一定的这方面的专业知识。虽然“人工智能”和“高级分析”这些神奇的词广为人知,但很少有人知道它们背后的实际含义。然而,没有这些知识,就很难回答这些紧迫的问题。例如,应该从哪里控制数据分析?从一个中心位置?风险在于它不会渗透到单个业务领域。或者,从不同的部门分散?这里存在共存的风险。几个分析程序并行存在,并且不兼容。
特定任务是否应该外包给外部服务提供商?这个决定也应该慎重考虑。当然,战略伙伴关系可以是双赢的。但与此同时,数据分析堪比公司的大脑。那么谁会希望一个陌生人能接触到他所有的记忆和未来计划呢?目标必须始终是能够独自行走。
员工必须依赖数据分析的结果。
然而,其他员工也必须参与到数据驱动型公司的旅程中。毕竟,他们不仅要与数据专家密切合作,还要相信他们的结果。只有这样,数据分析才能成为关键业务决策的基础。很明显,仅仅将首席分析官(CAO)的新职位分配给首席数据官(CDO)是不够的。这只是工作的开始。
AWS 数据分析:简介

分析是对数据的系统化分析。数据分析是应用的特定分析流程。
数据分析对大大小小的企业都至关重要。它帮助决策者基于分析和数字而不是直觉和运气做出决定。关键任务,如:推出新产品、提供折扣或营销新领域,都需要时间敏感的决策和丰富的经验。当大规模经营时,经理们可能会被要做的决策数量淹没,有时,运气是你最好的赌注。
所有这些问题都可以通过数据分析解决方案来解决。然而,随着企业开始实施这些解决方案,他们可能会面临一些挑战:
- 使用什么数据以及如何使用?
- 如何连接我的所有数据源?
- 如何运行全自动化管道?
- 如何管理这些管道?
这些问题适用于小型或大型企业。两者都需要建立一个环境来回答这些问题并预测未来的问题。
组织在数据存储上花费了数百万美元。问题不在于找到数据——问题在于没有对数据做任何事情,AWS
大规模数据分析的优势
客户个性化
根据客户的行为、过去的购买行为和其他人的购买行为,应该向客户展示什么产品或服务?
欺诈检测
哪些交易是欺诈性的,或者哪些用户不是人类(机器人)?
安全威胁检测
我们如何从使用模式中识别潜在的安全风险?
用户行为
基于他的在线行为,我们如何识别一个用户是否是潜在客户,他能花多少钱购买我们的产品/服务?
金融建模和预测
如何根据财务数据预测市场趋势?
实时警报
我们需要接收哪些警报?谁将收到哪个警报?
数据分析解决方案
任何数据分析解决方案的主要逻辑是:
- 原始数据:以原始格式从数据源获取数据。它可以是文本、数字或视频…
- 数据表:将数据组织成存储方案(数据库、数据湖……)
- 数据视图:可视化收集和清理的数据,以执行分析并获得洞察力。
这些步骤需要在类似流水线的结构中执行:
- 连接到任何来源的原始数据
- 以任何形式、任何速度将数据接收到存储解决方案中
- 在可扩展、持久且安全的解决方案中存储各种数据(结构化、半结构化或非结构化)
- 处理这些数据,将其转换为可用的数据集,以供使用和分析(聚合、连接……)。这些数据集需要存储在与原始数据不同的位置
- 使用商业智能工具和仪表盘可视化最终数据,以寻求答案和见解。在这一步中使用机器学*来应用算法,以超越通常的分析。
数据分析的挑战
在现代企业中,数据由大量来源产生:电子商务网站、日志、CRM、ERP、数字营销、社交媒体、物联网...重要的问题是如何管理所有这些数据,以及如何从中产生价值,从而比竞争对手更具优势。
建立这样一个数据分析解决方案的挑战可以归结为我们所说的五对 : 量、速度、多样性、准确性和价值。
需要注意的是,根据业务类型、规模和产品,组织可能会面临上述五项挑战中的几项或全部。比如 Youtube (视频内容)就没有媒体存在的问题(文字内容)。
以下是每个 V 的简要定义:
- 卷:是传入并将被系统接收的数据总量。
- 速度:这是数据流入的速度,挑战在于*乎实时地处理数据并尽快返回结果。示例:验证信用卡交易必须是即时的(接*实时)
- 多样性:系统中接收的数据可以有不同的格式,我们不能像处理视频或文本那样处理数字数据,因此数据分析解决方案必须管理所有这些多样性
- 准确性:就是传入数据的准确性。这是任何分析解决方案的一个非常重要的部分,企业必须确保存储在系统中的数据是干净和准确的,否则产生的所有分析都将是有缺陷的,这可能是危险的
- 价值:任何投资都必须为投资者创造价值,数据分析解决方案也不会偏离这一规则。决策者寻求从这些系统中提取有意义的信息和见解,以获得竞争优势。
AWS 在这一切?
AWS 是亚马逊网络服务的缩写,是电子商务巨头亚马逊的云分支。
AWS 是一家云提供商,提供全面管理、可扩展、耐用且安全的 IT 基础设施解决方案。这意味着只需几次点击,企业就能部署完整的数据分析解决方案,这非常强大。云提供商(谷歌和 GCP,微软和 Azure 等)的主要卖点是“专注于你的业务,让我们做剩下的事情”。
在这个系列中,我们将探索 AWS 提出的解决方案/产品,以设置数据分析解决方案的每个步骤。
接下来呢?
本文是我将介绍使用 AWS 进行数据科学和分析的系列文章的一部分。在本系列结束时,您将对数据分析中使用的 AWS 解决方案有一个概述。
在本系列的下一篇文章中,我将发表一篇关于数据分析解决方案每个组件的具体文章。我们将深入探索 AWS 生态系统,以建立完整的分析基础架构。
MODIS 数据的数据分析

Aerosol Optical Depth imagery over the entire world
MODIS(或中分辨率成像光谱仪)是美国宇航局 Terra 和 Aqua 卫星上的成像传感器,绕地球运行,捕捉图像,以了解和研究地球表面的各种现象。你可以从这些图像中提取一大堆产品,从地理定位、云罩、大气产品到陆地和冰冻圈产品到海面温度产品。这些数据可以极大地帮助理解天气的动态变化、风的模式,甚至是人类在某个地区的影响。
这有什么大不了的?
当我开始时,我发现最大的问题当然是处理这些地理空间数据。我从事大气气溶胶光学厚度(AOD)的研究,试图了解气溶胶通过吸收或散射阳光来减少日照的作用。对于初学者来说,没有一个关于处理这些数据的指南。所以在一个地方编译我找到的那些方法是有意义的,可以对那里的人有所帮助。所以让我们开始吧…
代码可以在这里找到。
但是首先我们得到数据
MODIS 数据可以从这个网站下载。我特别选择了主菜单中的大气产品(因为 AOD 数据应该在那里),然后被重定向到 NASA LAADS 网站。你可以很容易地找到一个 Find Data 选项卡,点击它会导向这里:

从下拉列表中,选择记录您正在寻找的产品数据的传感器。我选择了 MODIS Terra 传感器(尽管 MODIS Aqua 传感器同样适用于我们的目的),并在这里着陆:

看看如何从左侧窗格中的各种收藏中进行选择。我简单地选择了气溶胶,它让我有了两个选择——L2 3 公里长的条带和 L2 10 公里长的条带数据。它只是代表了一张地球快照所覆盖的面积(在这样的项目中,将数据想象成地球的图像总是有帮助的,因为这就是它们的真实面目!)。因此,根据您是希望处理 3 公里×3 公里的区域还是 10 公里×10 公里的区域,您可以选择所需的集合。
其余的选项非常简单。该菜单要求您输入持续时间、位置,并显示您可以下载的文件(HDF)格式。文件名包含文件的基本信息:

What your file name says!
因为我想分析北印度 AOD 一年的情况,所以我下载了一年中每一天所需地区的 HDF 文件。文件可以在我的 Github repo 中找到。
将数据加载到 Python
在经历了许多失败和挫折之后,我遇到了gdal(地理空间数据抽象库),事实证明它在处理上述数据时非常有用。然而,事实证明,使用 Python 的默认包管理器*pip*来安装 gdal 对我来说更加困难。所以我直接将它安装到我的 Anaconda 环境中;只要做好*conda install gdal* 我就好了。
打开单个文件
下面的代码打开路径指向的文件。
下载的每个文件都有许多附属的子数据集。只是为了看看我们得到了什么,*GetSubDatasets()* 可以分别列出不同的数据集,返回一个*path* 打开的数据集和一个*desc* 的数据集。打印描述会告诉您许多关于您正在处理的数据类型的信息:

Just a small number in this screen. There are several others to deal with as well
基本上这些名字都是不言自明的。附加到描述的是数据集被格式化成的矩阵的大小(在左边)和用于存储数据的数据类型(在右边)。因此,让我们编写一些通用函数来加载我们希望加载的任何特定子数据集。
上述函数获取*FILEPATH* 并从该文件加载*SUBDATASET_NAME* 。例如,要打开文件中的第一个子数据集,只需将*FILEPATH* 指向存储 HDF 文件的目录,并将*Scan_Start_Time mod04(64-bit floating-point)* 传递给*SUBDATSET_NAME* 。这段代码将打开子数据集,将其作为数组读取(*subdataset.ReadAsArray()*),并返回一个 pandas dataframe,供以后处理。
在您希望的位置获取数据
如果您需要整个区域的平均乘积,只需对整个矩阵求平均,这将是当天该区域的 AOD(尽管您需要处理缺失数据)。我们一会儿就会谈到这一点)。但是如果你,只需要提取那张 10 公里 x 10 公里图像的一小部分,你需要找到图像上最接*你要找的位置的像素。
那么你是如何做到这一点的呢?尽管我认为有几种方法可以解决这个问题,但我还是采用了以下方法:
- 打开纬度子数据集,找到单元格的行号,它的值最接*我正在寻找的纬度。这个想法来自于这样一种理解,即纬度子数据集中的每个单元代表图像中该像素的纬度。
- 现在,使用步骤 1 中的行号,我将在经度子数据集中找到像素的列号,这是离我要寻找的地方最*的。
- 所以现在我有了离我瞄准的位置(地理坐标)最*的像素的行号和列号。我现在可以打开其他子数据集,比如说Deep _ Blue _ Aerosol _ Optical _ Depth _ 550 _ Land mod 04(16 位整数)并提取该像素的值。这个值给了我当天使用深蓝检索算法在那个地方测量的 AOD。我们可能希望有 9 到 25 个像素的平均值,以我们刚刚找到的像素为中心,以防万一,该像素当天的值为空(当天该像素上的数据无法记录)。**
Finds the latitude pixel
上面的代码找到了像素的行号,该像素的值(本质上是纬度)是距离我们正在寻找的*CITY_LATIDUDE* 最*的。**
Finds the longitude pixel
经度也有类似的故事。请注意,我不是在整个矩阵中搜索,而是在我刚刚在前面的函数中找到的行号所在的行中搜索(用*LATITUDE_ROW_NUMBER*表示)。
上面的函数非常简单,给定一个特定的子数据集,latitude 函数找到的行号和 longitude 函数找到的列号,返回子数据集中该像素的乘积值。
我们需要做的最后一件事是以某种方式处理要打开的子数据集,找到像素坐标,并获得所需的平均值(基本上是将上述所有内容放在一起):
很明显,上面的函数找到了 lat_row 和相应的 lon_column,决定考虑哪些子数据集,并在 9 个单元格的网格上求平均值(我们的像素在网格的中心)。如下图所示:

还要注意,在数据集中, -9999 的值表示未能记录该位置的数据。所以我简单地放了一个 0 在那里。
处理大量数据…
上面的驱动代码简单地在一个月内迭代所有 HDF 文件,提取散射角、AOD(深蓝)、云分数、组合(深蓝和暗目标检索算法组合)、和 A ngstrom 指数、将所有内容添加到一个列表中,通过*pd.Series()* 、将所有内容转换成一个序列,并向*pd.DataFrame()* 添加一个新列。最后,我们可以导出当月的。csv 文件供进一步分析。
分析
以下两个助手例程将帮助我们绘制每个月的数据以及平均参数,以便我们可以查看其在一段时间内的平均变化。我在*plot* 例程中做了几件事情,一个接一个地叠加线条图,检索函数返回的任何内容,从中获取图形(使用*get_figure()*),并将图形保存到文件中,以备将来使用。
这里的一切都很明显。我使用一个字典来跟踪哪个月份以什么顺序被处理(例如,根据文件名,四月是第一个被处理的月份)。它只需要创建一个给定的地图,当追加平均的深蓝 AOD T21 数据时,追加对应于日历中月份的整数。这将有助于排序列表,并返回从 1 月到 12 月的时间变化。
我将分享一些情节和我从中得到的一些感悟。在这里找到其他人。
大势第一

上图描绘了平均气溶胶产品的年变化。y 轴报告从深蓝算法中检索的值。为了得到实际值,检索到的值需要用0.001(MODIS 使用的缩放因子)进行缩放。
上图是全年的平均月 AOD 变化。可以看出,AOD 在夏季上升,在雨季下降,在冬季再次上升
2018 年 1 月趋势(注 MODIS 数据使用 0.001 的比例因子。所以实际值是在数据值乘以比例因子时获得的)

Variation of Deep Blue AOD and Cloud Fraction over January 2018
上图绘制了从深蓝检索算法获得的 AOD 值和获得的云分数数据值。很明显,总的来说,每当云分数高的时候,AOD 往往就低。事实上,这并不低,只是卫星成像传感器无法记录当天的数据。并且在 1 月 2 日左右观察这个峰值。如果你稍微想一想,你就会意识到,新年庆祝活动就是这样!

Variation of Combined AOD and Cloud Fraction over January 2018
这里可以合理地看出,使用暗目标和深蓝的组合 AOD 给出了对 AOD 的有点不正确的估计。据报道,大部分时间 AOD 都在 0 左右。众所周知,事实并非如此,尤其是在新年前后。

Variation of Deep Blue AOD and Scattering Angle over Januray 2018
深蓝 AOD 和散射角都是 放大 0.001 倍 。这里可以看到 AOD 的一个尖峰信号是如何导致散射角突然增加的,这意味着更多的阳光被阻挡。

Variation of Deep Blue AOD and Angstrom Exponent over January 2018
Angstrom 指数是 AOD 相对于光的各种波长如何变化的量度(称为“光谱相关性”)。这与气溶胶粒子大小有关。较高的值表示颗粒较粗,而较小的值表示颗粒较细。这里,通常较高的值~ 0.5(500 0.001[比例因子 ])表示通常较细的颗粒。一项 PM2.5 研究可以补充这一发现。*
在8 月16】数据中发现了奇怪的结果。由于本月持续的高云量,MODIS 未能检索到 AOD 数据。

Variation of Deep Blue AOD and Cloud Fraction over August 2018
正如所料,其他变量也遭受了类似的后果。

Variation of Combined AOD and Cloud Fraction over August 2018

Variation of Deep Blue AOD and Angstrom Exponent over August 2018

Variation of Deep Blue AOD and Scattering Angle over August 2018
摘要
最后,让我们在这里得出一些推论:
- ****在季风期间AOD 水平的下降是由于强日照,促进了气溶胶的柱状混合。
- 冬季AOD 气温上升是由于日照减少和寒冷 气温;由于低大气边界层和在陆地附*捕获气溶胶,气溶胶的反向柱状混合。****
- 夏季更强的对流(主要来自西方扰动和信风)伴随着更深的大气边界层导致夏季坎普尔上空气溶胶聚集。****
- 云量是 AOD 测量中的一个重要因素。较高的云量通常会导致当天没有数据。在本报告中,这样的日子被 替换为所考虑月份的平均值 。**
- 美国宇航局的深蓝检索算法在陆地图像上给出了比暗目标检索更准确的结果。
- 坎普尔的人们在 2018 年的新年和排灯节前夕确实爆了很多饼干!2018 年 1 月 1 日和排灯节的堪普斯太模糊了,甚至无法记录图像来获取数据,2018 年 1 月 2 日看到了约 1.2 AOD。
- 由于持续的高云量,2018 年 8 月出现了记录 AOD 数据的直接失败。
利用 MODIS 数据,你可以做更多的事情。你可以找到森林覆盖数据,研究海洋,等等。我希望这篇文章能帮助你处理任何 MODIS 产品,你可以没有任何麻烦地完成你的学*。
祝你今天开心!玩的开心!
数据作为设计的新媒介
第二部分
上接“设计流程中的数据科学”,该研究旨在帮助服务设计师在设计流程的所有阶段使用数据,从使用数据进行研究和分析,到使用数据作为创意媒介和工具。

Image from HSO.com
企业现在将数据视为原材料,宣称“数据是新的石油”,据称这是数学家 Clive Humby 首先说的,他建立了 Tesco Clubcard 忠诚度计划(Marr,2018 年),King 等人(2017 年)将数据称为“当今的货币”。随着这种即将到来的数据商品化趋势,设计师们现在比以往任何时候都更开始使用数据作为一种工具,进一步告知他们的直觉和对用户的定性研究(黄,2016)。
越来越多的设计机构开始将数据整合到为客户设计新服务和产品的过程中。随着数据量的不断增加,开发利用大数据的方法变得至关重要。 Topp (2016),一家来自瑞典的设计和数据实验室,已经开始超越传统的使用数据的方式,将数据用作收集人口统计见解或监控使用情况的来源。Topp 已经开始使用数据作为设计材料,他们认为这开辟了“创造性和战略性的道路,否则这些道路将会被遗忘。”Topp 举办了一系列研讨会,试验基于设计的构思和用数据绘制草图。有趣的是,他们认为强加人类视角和我们创造叙事的能力使我们能够“从数据中阅读故事”,这意味着激发想法,并给设计师提供不同的视角和观点来工作。他们不相信这些故事需要有统计上的相关性,并把对数据的解释作为整个设计过程中使用数据的基础来讨论。一方面,他们认为你不一定要成为数据科学家才能从数据中获得创造性的见解。然而,另一方面,他们承认,他们通过使用数据作为思维工具产生的想法“如果我们是数据科学家,当然会完全是疯狂的言论”。
已故的汉斯·罗斯林以全球卫生教授的身份开始了他的职业生涯,但他以统计学家的工作以及他在世界各地关于统计力量的演讲和介绍而闻名。他出现在一部名为《统计的快乐》的纪录片中,这部纪录片认为,统计让人们看到了一个我们无法通过任何其他方式获得的世界。“统计数据告诉我们,我们认为和相信的事情实际上是否是真的”(罗斯林,2010)。根据 Claeson 的说法(Watts,2017 年引用),“汉斯具有简化复杂问题并以幽默的方式呈现它们的独特品质,这让我们倾听、提问并进行更知情的辩论”。这句话说明了当以一种创造性的、平易*人的方式传达给不太熟悉数据的人时,数据会有多么强大。
Pamela Pavliscak (2016)是 UX 研究和战略公司 Change Sciences 的创始人,她的方法部分是民族志,部分是心理学,部分是数据科学,“将未来的愿景转化为有形的日常可能性”(Change Science,无日期)。她将算法作为一种新的设计媒介。她表示,体验设计师越来越依赖于用算法进行设计,并认为设计越来越多地通过使用算法来增强人类体验和创造更好的情感意识。
此外,数据正在成为一种新的艺术形式,因为“数字技术和‘传统’创作实践之间的界限正在消失”(Murray,2014)。数据正在成为艺术的媒介,艺术家们将它比作颜料或画布,许多人认为数据不应该只留给统计学家和企业界,这凸显了数据在各个行业和学科中的重要性(Murray,2014)。一个这样的例子是 信息是美丽的 ,一个创建美丽的交互式数据-可视化和信息图表的组织,基于“跨越艺术、科学、健康和流行的模式和联系,揭示隐藏在我们周围的数据、信息&知识中错综复杂、不可见且有时滑稽可笑的故事”(信息是美丽的,没有日期)。

Data visualisation from Information is Beautiful (2018)
随着数据不再仅仅为商业关键决策提供信息,而且还融入到艺术、设计甚至音乐创作中,数据作为一种新媒体的崛起引起了一些评论家的坚定判断,他们认为数据的使用“干扰了创造力”,最好留给商业世界处理(PromptCloud,2017)。
用数据进行设计时道德的作用
“数据本身在最好的情况下是没有意义的,在最坏的情况下是误导性的”——Pardi,2016 年
我们世界的数字化不可否认地被视为一把双刃剑。虽然有些人强调了数字化和数据给我们的社会带来的好处(Sabbagh 等人,2012 年),但其他人强调了它已经并将继续对社会产生的有害影响(Loebbecke,2015 年)。技术进步将继续快速加速(Treseler,2015)。
Murray (2014)认为,大数据不能保证客观真实,因为数据的优势、无处不在和可塑性也是其弱点,人们越来越担心隐私和安全受到侵犯。他指出了“社会实证主义者视野中的缺陷,这些人将数据视为某种治疗世界疾病的灵丹妙药”。
Zwitter (2014 年)认为,大数据产生了重新思考道德规范的需求,特别是在个人道德责任方面,因为个人失去了理解其潜力和做出明智决定的能力。社交网络数据、可穿戴设备生成的健康数据、电子邮件和手机应用程序的数据都有可能被不道德地滥用。个人不熟悉他们的数字足迹的无意后果。“当大数据集的二次使用可以对过去、现在甚至未来的隐私、保密性和身份的破坏进行逆向工程时,专注于管理个人身份信息的现有隐私保护是不够的”(Richards & King,2014)。新闻和媒体经常报道数据安全漏洞的数量。一个比较突出的例子是,剑桥分析公司非法访问了数百万份脸书个人资料,并设法收集了大量个人数据。他们能够理解甚至试图改变人们的政治观点,这可能影响了最*的美国总统选举(Halpern,2018)。
Pavliscak (2016)提请注意情绪感应应用程序和设备的兴起,并鼓励以极简方式使用数据和基于这些数据创建算法。Halpern (2018)也认识到这种敏感数据的危险,因为它为心理控制奠定了基础,“通过直接诉诸他们的情绪,使用越来越细分和细分的人格类型指定以及基于这些指定的精确定向信息。”
*年来,在数据收集和使用方面受到审查的最著名的组织之一是美国政府机构——国家安全局,也称为 NSA。支持者为其侵入性的电话和互联网记录收集辩护,理由是只收集“唯一”元数据——你给谁打电话,什么时候打,打了多长时间——认为由于对话的实际内容没有被记录,对隐私的威胁无关紧要。这是一个充其量是误导的论点。关于数据的道德使用以及当今元数据如何“告诉你关于某人生活的一切”,最有争议的说法之一可能是美国国家安全局前局长迈克尔·海登将军(2014 年在科尔引用)所说的“我们基于元数据杀人”。这一令人震惊的声明证明了数据不仅可以违反道德标准,还会给人类带来更大的风险。当一组数据因其(元)知识而被用于生死决策时,忽略人的作用也就忽略了移情、价值观、情商和人性的价值。
数据解释的不足
霍华德(Pollack,2012 年引用)说“数据没有给胡说留下任何空间。[……]人们会争论,但你可以给他们看数据。它不会说谎。”然而,Topp (2017)认为,没有人类的解释,数据只是对一个现实的描述。
任何数据都没有内在价值,因为所有信息本身都没有意义。为什么?因为信息不会告诉你该怎么做。“
——博·洛托(引自帕迪,2017)
Pardi (2017)对作为设计师、工程师或任何创造事物的角色,单独依赖数据的危险进行了有趣的观察。他指出,数据只是信息,并不代表客观现实。他提出了一个令人信服的事实,即数据是“由进行处理的个人高度语境化的”,因为不同的人会以不同的方式解释数据,而他们的语境对这种解释有很大的影响。他用的例子如下。
朗读课文 :
w at a e y u ea ing n w?
如果你读到“你现在在读什么?”,你做了很多人会用同样的“数据”阅读的事情,尽管句子中没有包含一个英语单词。你的大脑填补了空白,因为你与英语的关系,你被提示“大声朗读课文”,这影响了结果。然而,如果你在吃零食,饿了或者坐在餐馆里,你可能会读到“你现在在吃什么?”。
关键在于,数据科学家的角色正是如此——通过解释来理解数据,并将分析转化为可供组织其他部门采取行动的见解。这个例子表明,数据永远不可能是客观事实,因为它依赖于一个或多个个人的主观解释,所有人都从他们的角度和背景来看待数据。与 King 等人(2017 年)类似,Pardi (2017 年)暗示,当想要创造性地使用数据时,以“驱动”为重点的数据驱动并不是一个好方法。然而,他确实相信数据可以支持创造性和创新性的成果。事实应该通知设计者,以便他们可以试验未来的可能性,发现原始数据无法提供的洞察力。
参考
Cole,D. (2014) 我们根据元数据杀人。可从:https://www . ny books . com/daily/2014/05/10/we-kill-people-based-metadata/(访问时间:2018 年 9 月 27 日)。
黄(2016) 设计师如何利用数据创造出令人惊叹的作品。可在:https://www . invision app . com/inside-design/how-designers-can-use-data/(访问时间:2018 年 10 月 10 日)。
King,r .,Churchill,e .,Tan,C. (2017),用数据进行设计。加利福尼亚州塞瓦斯托波尔:奥莱利媒体公司。
Loebbecke,c .和皮科特,A. (2015),《对数字化和大数据分析引发的社会和商业模式转型的思考:一项研究议程》,《战略信息系统杂志,第 24 卷第 3 期,第 149-157 页。
Marr,B. (2018) 这就是为什么数据不是新的石油。可从:https://www . Forbes . com/sites/Bernard marr/2018/03/05/heres-why-data-is-not-the-new-oil/# b 00 c 0b 23 aa 96(访问时间:2018 年 11 月 13 日)。
Murray,B. (2014) 设计之美:数据与数字艺术的交集。可从:https://TechCrunch . com/2015/02/27/beauty-by-design-the-crossover-of-data-and-digital-art/(访问时间:2018 年 9 月 27 日)获取。
如果你想有创造力,就不要被数据驱动。可从:https://medium . com/Microsoft-design/if-you-want-to-be-creative-don-be-data-driven-55db 74078 EDA(访问时间:2018 年 10 月 4 日)。
Pavliscak,P. (2016) 算法作为设计的新材料。可从 design.php获得 https://www . UX matters . com/mt/archives/2016/06/algorithms-as-the-new-material-of-(访问时间:2018 年 10 月 12 日)。
n . pol lack(2012)万亿字节革命:Kaggle 如何将数据科学家变成摇滚明星。可从:https://www.wired.co.uk/article/the-exabyte-revolution(访问时间:2018 年 10 月 12 日)。
PromptCloud (2017) 大数据。可从:https://towardsdatascience.com/how-big-data-can-impact-creative-in-the-near-future-95e0a 626840 a(访问时间:2018 年 9 月 4 日)获得。
Sabbagh,k .、Friedrich,r .、El-Darwiche,b .、Singh,m .和 Ganediwalla,S. (2012),《数字化影响最大化》,《全球信息技术报告,第 121-133 页。
Topp (2016) 数据作为创意素材。可从:http://datalab.topp.se/news/2016/6/17/data-as-ideation-tool(访问时间:2018 年 10 月 5 日)。
Treseler,M (2015) 作为数据科学家的设计师。可从:http://radar . oreilly . com/2015/05/designers-as-data-scientists . html(访问时间:2018 年 10 月 15 日)。
瓦特,G. (2017),‘汉斯·罗斯林’,柳叶刀,389 (10070),第 694 页。
张,b .,克雷茨,g .,伊萨克森,m .,尤比洛斯,j .,乌尔达内塔,g .,Pouwelse,j .和埃佩马,D. (2013),《理解 spotify 中的用户行为》,IEEE 会议录,第 220-224 页。
用于深度学*的数据增强

Photo by rawpixel.com from Pexels
流行的增强包和 PyTorch 示例概述
拥有大型数据集对于深度学*模型的性能至关重要。然而,我们可以通过增加我们已经拥有的数据来提高模型的性能。深度学*框架通常有内置的数据增强工具,但这些工具可能效率低下或缺乏一些必要的功能。
在这篇文章中,我将概述最流行的图像增强包,专门为机器学*设计的,并演示如何使用这些包与 PyTorch 框架。
对于每一个图像增强包,我都介绍了用二进制遮罩和边界框转换图像、流水线转换以及用 PyTorch 进行转换。我正在使用下面的演示图像:

Demo image
这篇文章的完整代码在这个 Jupyter 笔记本中提供。
imgaug 包
imgaug 是一个强大的图像增强包。它包含:
- 60 多种图像增强器和增强技术(仿射变换、透视变换、对比度变化、高斯噪声、区域缺失、色调/饱和度变化、裁剪/填充、模糊);
- 使用分段遮罩、边界框、关键点和热图增强图像的功能。该功能使得将包应用于包含图像的数据集以解决分割和对象检测问题变得非常容易;
- 复杂的增强管道;
- 许多增强可视化、转换等辅助功能。
此处提供了 imgaug 包的完整文档。
简单的扩充
用 imgaug 包扩充图像就像这样简单:
Augment an image with imgaug
以下是使用 imgaug 软件包增强的几个图像示例:

Demonstration of sample augmentations: rotation, gaussian noise, crop, hue and saturation adjustment, elastic transform, coarse dropout
Imgaug 帮助处理带有分割遮罩的图像。为了训练深度学*模型,我们必须以相同的方式增强图像和掩模。下面是一个使用二进制分割蒙版变换图像的示例:
Augment an image with mask
以下代码有助于可视化增强图像和增强遮罩:
Visualize the augmented image and mask with imgaug helpers
下图是使用二进制分段遮罩的增强图像示例:

The result of augmentation of an image with a mask
Imgaug 包在对带有边界框的图像应用变换时也很有帮助。用边界框增加图像的示例代码:
Transformation of an image with a bounding box
以下代码示例可用于可视化结果:
Visualize the augmented image and bounding box with imgaug helpers
下图展示了增强图像和边界框与原始图像相比的外观:

The result of augmentation of an image with a bounding box
示例中的增强边界框看起来不错,但在某些情况下,增强边界框对旋转的图像不起作用。这里有一个例子:

The example of augmented bounding box for rotated image
流水线扩充
使用 imgaug 包,我们可以构建一个复杂的图像增强管道。来自管道的增强按顺序应用于每个图像,但是我们可以设置:
- 将增强应用于图像的概率。例如,我们可以用它来对 50%的图像进行水平翻转。
- 仅对图像应用增强器的子集。例如,应用列表中的 0 到 5 个增强剂。该功能有助于加速数据生成。
- 以随机顺序应用增强。
让我们看一个复杂增强管道的例子:
Augmentation pipeline example
以下图像取自上述定义的管道:

Examples of images sampled from the augmentation pipeline
使用 imgaug 和 PyTorch
以下代码示例演示了如何在 PyTorch 数据集中使用 imgaug 增强来生成新的增强图像:
Example of using imgaug with PyTorch
白蛋白包装
albuminations包是基于 numpy、OpenCV、imgaug 编写的。这是一个非常受欢迎的软件包,由 Kaggle masters 编写,在 Kaggle 比赛中广泛使用。而且,这个包效率很高。你可以在这里找到基准测试结果,在这里找到这个包的完整文档。
白蛋白包装能够:
- 超过 60 种像素级和空间级转换;
- 用遮罩、边界框和关键点变换图像;
- 将增强组织到管道中;
- PyTorch 集成。
简单的扩充
来自白蛋白包的增强应用于图像,如下所示:
Augment an image with Albumentations
来自白蛋白包的增强器产生的图像的几个例子:

Demonstration of sample augmentations: gaussian noise, elastic transform, random brightness and contrast, random gamma, CLAHE, blur
albuminations包也可以应用于带有遮罩的图像。这里有一个例子:
Augment an image with mask using Albumentations
下图显示了使用二元分割蒙版的增强图像:

The result of augmentation of an image with a mask
albuminations包还提供了用边界框转换图像的功能。这里有一个简单的例子:
Transformation of an image with a bounding box
下面的代码示例演示了如何可视化结果:
Visualize the augmented image and bounding box
这是一个带有边界框的增强图像示例:

The result of augmentation of an image with a bounding box
流水线扩充
Albumentations 包从 numpy、OpenCV 和 imgaug 中提取精华。这就是为什么albuminations包中的流水线与 imgaug 中的流水线相似。下面是一个受Kaggle 内核启发的管道示例:
Augmentation pipeline example
以下图像取自上述定义的管道:

Examples of images sampled from the augmentation pipeline
PyTorch 集成
当使用 PyTorch 时,你可以毫不费力地从 torchvision 迁移到albuminations,因为这个包提供了专门的实用程序供 PyTorch 使用。迁移到albuminations有助于加快数据生成部分,更快地训练深度学*模型。点击查看从 torchvision 迁移到albumations的详细教程。
PyTorch 集成示例:
PyTorch integration
推力增强装置
增强器 包包含的可能增强比本文之前描述的包更少,但它有其突出的特性,如尺寸保持旋转、尺寸保持剪切和裁剪,更适合机器学*。
增强器包还允许组成增强管道,并与 PyTorch 一起使用。你可以在这里找到增强器包的完整文档。
简单的扩充
下面的例子结合了一个简单的增强器流水线应用于一个带有遮罩的图像:
Augment an image with Augmenter
这些图像是在上述代码的帮助下生成的:

The result of augmentation of an image with a mask
使用 PyTorch 增强器
有一些已知的问题与增强器和 PyTorch 有关,但是我们仍然可以一起使用这些库。
这里有一个例子:
Example of using Augmentor package with PyTorch
结论
在本文中,我介绍了专门为机器学*设计的最流行的图像增强包的功能。本文中提供的各种代码示例有助于开始在机器学*项目中使用这些包进行分类、分段和对象检测任务。
用于语音识别的数据扩充
自动语音识别(ASR)

Photo by Edward Ma on Unsplash
这个故事发表在 Dev.to 和 Medium 上。
语音识别的目的是将音频转换成文本。这项技术广泛应用于我们的生活中。谷歌助手和亚马逊 Alexa 就是将我们的声音作为输入并转换成文本以理解我们意图的一些例子。
与其他自然语言处理问题一样,关键挑战之一是缺乏足够数量的训练数据。它导致过多或难以处理看不见的数据。谷歌大脑(Google Brain)和人工智能(AI)团队通过引入几种用于语音识别的数据增强方法来解决这个问题。本故事将讨论 SpecAugment:一种用于自动语音识别的简单数据增强方法 (Park 等人,2019 年),并将涵盖以下内容:
- 数据
- 体系结构
- 实验
数据
为了处理数据,波形音频转换成声谱图,并馈入神经网络产生输出。执行数据扩充的传统方式通常应用于波形。Park 等人采用了另一种方法,即操纵声谱图。

Waveform audio to spectrogram (Google Brain)
给定一个声谱图,你可以把它看作一幅图像,其中 x 轴是时间,而 y 轴是频率。

Spectrogram representation (librosa)
直观地说,它提高了训练速度,因为没有波形数据到频谱图数据之间的数据转换,而是增加了频谱图数据。
Park 等人推出了用于语音识别中数据增强的SpecAugment。有三种基本方法来扩充数据,即时间弯曲、频率屏蔽和时间屏蔽。在他们的实验中,他们将这些方法结合在一起,并引入了 4 种不同的组合,即 LibriSpeech basic (LB)、LibriSpeech double (LD)、Switchboard mild (SM)和 Switchboard strong (SS)。
时间扭曲
将选择一个随机点,并向左或向右弯曲一段距离 W,该距离从 0 到沿该线的时间弯曲参数 W 的均匀分布中选择。
频率掩蔽
a 频道[f0,F0+f]被屏蔽。F 选自 0 至频率掩模参数 F 的均匀分布,f0 选自(0,νF ),其中ν为频率通道数。
时间掩蔽
t 个连续的时间步长[t0,t0+t]被屏蔽。T 选自 0 至时间屏蔽参数 T 的均匀分布,t0 选自[0,τT]。

From top to bottom, the figures depict the log mel spectrogram of the base input with no augmentation, time warp, frequency masking and time masking applied. (Park et al., 2019)
基本增强政策的组合
通过结合频率屏蔽和时间屏蔽的增强策略,引入了 4 种新的增强策略。而符号表示:
- w:时间弯曲参数
- f:频率屏蔽参数
- mF:应用的频率屏蔽数量
- t:时间屏蔽参数
- mT:应用时间屏蔽的次数

Configuration for LB, LD, SM and SS (Park et al., 2019)

From top to bottom, the figures depict the log mel spectrogram of the base input with policies None, LB and LD applied. (Park et al., 2019)
体系结构
听、听、拼(LAS)网络架构
Park 等人使用 LAS 网络架构来验证使用和不使用数据增强的性能。它包括两层卷积神经网络(CNN),注意力和堆叠双向 LSTMs。由于本白皮书的目标是数据扩充,并且利用模型来查看模型的影响,因此您可以从这里深入研究 LAS。
学*费率表
学*率时间表成为决定模型性能的关键因素。类似于倾斜三角形学*率(STLR) ,应用非静态学*率。学*速率将呈指数衰减,直到它达到其最大值的 1/100,并在该点之后保持恒定。一些参数表示为:
- sr:加速步骤(从零学*率开始)完成
- si:指数衰减的步骤开始
- sf:指数衰减的步骤停止。
另一种学*速率调度是统一标签平滑。正确的类别标签被赋予置信度 0.9,而其他标签的置信度相应地增加。参数表示为:
- 噪声:变权噪声
在后面的实验中,定义了三个标准的学*速率表:
- B(asic): (sr,snoise,si,sf ) = (0.5k,10k,20k,80k)
- d(double):(Sr,snoise,si,sf ) = (1k,20k,40k,160k)
- L(ong): (sr,snoise,si,sf ) = (1k,20k,140k,320k)
语言模型
LM 用于进一步提高模型性能。一般来说,LM 被设计成在给定前一个记号的结果的情况下预测下一个记号。一旦预测到新标记,在预测下一个标记时,它将被视为“前一个标记”。这种方法在很多现代的 NLP 模型中都有应用,比如伯特和 GPT-2 。
实验
模型性能通过单词错误率 (WER)来衡量。
从下图中,“Sch”表示学*率计划,而“Pol”表示扩充政策。我们可以看到,具有 6 个 LSTM 层和 1280°嵌入向量的 LAS 表现出最好的结果。

Evaluation of LibriSpeech (Park et al., 2019)
通过将 LAS-6–1280 与 SpecAugment 一起使用,与其他模型和没有数据扩充的 LAS 相比,可获得最佳结果。

Comparing SpecAugment method in LibriSpeech 960h (Park et al., 2019)
在配电盘 300h 中,LAS-4–1024 被用作基准。我们可以看到 SpecAugment 确实有助于进一步提高模型性能。

Comparing SpecAugment method in Switchboard 300h (Park et al., 2019)
拿走
- 时间扭曲并没有显著提高模型性能。如果资源有限,这种方法将被放弃。
- 标签平滑导致训练不稳定。
- 数据扩充将过拟合问题转化为欠拟合问题。从下图中,您可以注意到,没有增强(无)的模型在训练集中表现接*完美,而在其他数据集中没有类似的结果。

- 为了便于语音识别的数据扩充, nlpaug 现在支持 SpecAugment 方法。
关于我
我是湾区的数据科学家。专注于数据科学、人工智能,尤其是 NLP 和平台相关领域的最新发展。欢迎在 LinkedIn 上与 me 联系,或者在 Medium 或 Github 上关注我。
延伸阅读
参考
- D.朴正熙、陈伟雄、张宇人、赵超群、左宗棠、朱布克及黎庆伟。 SpecAugment:一种用于自动语音识别的简单数据扩充方法。2019
- W.Chan,N. Jaitly,Q. V. Le 和 O. Vinyals .听,听,拼。2015
自然语言处理中的数据扩充
文本增强简介

Photo by Edward Ma on Unsplash
我们拥有的数据越多,我们能够实现的性能就越好。然而,注释大量的训练数据是非常奢侈的。因此,适当的数据扩充有助于提高模型性能。增强在计算机视觉领域非常流行。通过图像增强库,如 imgaug ,可以通过翻转、添加盐等方式轻松增强图像。事实证明,增强是计算机视觉模型成功的支柱之一。
在自然语言处理领域,由于语言的高度复杂性,很难对文本进行扩充。不是每一个词我们都可以用其他词代替,比如 a,an,the。另外,不是每个单词都有同义词。哪怕换一个词,语境也会完全不同。另一方面,在计算机视觉领域生成增强图像相对更容易。即使引入噪声或裁剪掉图像的一部分,该模型仍然可以对图像进行分类。
鉴于我们没有无限的资源来通过人类建立训练数据,作者尝试了不同的方法来实现相同的目标,即生成更多的数据用于模型训练。在这个故事中,我们探索了不同的作者如何通过生成更多的文本数据来增强模型,从而利用增强来刺激 NLP 任务。以下故事将涵盖:
- 宝库
- 单词嵌入
- 回译
- 语境化的单词嵌入
- 文本生成
宝库
张等人介绍了用于文本分类的同义词字符级卷积网络。在实验过程中,他们发现一种有效的文本扩充方法是用同义词替换单词或短语。利用现有的词库有助于在短时间内生成大量数据。张等选取一个词,按几何分布用同义词替换。
单词嵌入
王和杨介绍的字相似的算计在真是让人讨厌!!!:基于词汇和框架语义嵌入的数据增强方法,使用#petpeeve Tweets 对恼人的行为进行自动分类。在论文中,王和杨提出利用 k *邻法和余弦相似度来寻找相似词进行替换。

Imp.: relative improvement to the baseline without data augmentation (Wang and Yang, 2015)
或者,我们可以利用预先训练好的经典单词嵌入,比如 word2vec、GloVe 和 fasttext 来执行相似性搜索。

Most similar words of “fox” among classical word embeddings models
回译
英语是具有大量翻译训练数据的语言之一,而一些语言可能没有足够的数据来训练机器翻译模型。Sennrich 等人使用反向翻译方法来生成更多的训练数据,以提高翻译模型的性能。
假设我们要训练一个翻译英语(源语言)→粤语(目标语言)的模型,而粤语没有足够的训练数据。回译是将目标语言翻译成源语言,并混合源语句和回译语句来训练模型。因此可以增加从源语言到目标语言的训练数据的数量。

Performance result with and without text augmentation (Sennrich et al., 2016)
语境化的单词嵌入
Fadaee 等人没有使用静态单词嵌入,而是使用上下文化的单词嵌入来替换目标单词。他们使用这种文本增强来验证用于低资源神经机器翻译的数据增强中的机器翻译模型。
提出的方法是 TDA,代表翻译数据增强。实验表明,通过利用文本增强,机器翻译模型得到了改进。

Performance result with and without text augmentation (Fadaee et al., 2017)
Kobayashi 提出在语境扩充中使用双向语言模型:通过具有聚合关系的单词进行数据扩充。在选择目标单词后,模型将通过给出周围的单词来预测可能的替换。由于目标存在于句子的任何位置,双向结构被用来学*左右语境。
Kobayashi 用 CNN 和 RNN 在六个数据集上验证了语言模型方法,结果是肯定的。文本增强有助于进一步改进 NLP 模型结果。

Performance result with and without text augmentation (Kobayashi 2018)
文本生成
Kafle 等人介绍了一种不同的方法,通过在视觉问题回答的数据增强中生成增强数据来生成增强数据。与以前的方法不同,Kafle 等人的方法不是替换单个或几个单词,而是生成整个句子。
第一种方法是使用模板扩充,即使用预定义的问题,这些问题可以使用基于规则的方法来生成与模板问题配对的答案。第二种方法利用 LSTM 通过提供图像特征来生成问题。

Performance result with and without text augmentation (Kafle et al. 2017)
建议
上述方法旨在解决作者在他们的问题中所面临的问题。如果你了解你的数据,你应该量身定制增强方法。请记住,数据科学的黄金法则是垃圾进垃圾出。
一般来说,你可以在不完全理解你的数据的情况下尝试同义词库方法。由于上述同义词库方法的限制,它可能不会提高很多。
关于我
我是湾区的数据科学家。专注于数据科学、人工智能,尤其是 NLP 和平台相关领域的最新发展。欢迎在 LinkedIn 上与 me 联系,或者在 Medium 或 Github 上关注我。
延伸阅读
参考
- X.张、赵军、李乐存。用于文本分类的字符级卷积网络。2015
- W.王燕和杨丹。真讨厌!!!:一种基于词汇和框架语义嵌入的数据增强方法,使用#petpeeve Tweets 对恼人的行为进行自动分类。2015
- R.森里奇,b .哈多和一棵桦树。用单语数据改进神经机器翻译模型。2016
- 米(meter 的缩写))法达伊,a .比萨沙,c .蒙兹。低资源神经机器翻译的数据扩充。2017
- 南小林。语境扩充:通过具有聚合关系的词语进行数据扩充。2018
- K.卡弗尔、优素福·胡辛和卡南。视觉问答的数据扩充。2017
- C.库伦贝。利用 NLP 云 API 简化文本数据扩充。2018
文本数据扩充库

Photo by Edward Ma on Unsplash
在前面的故事中,您了解了为 NLP 任务模型生成更多训练数据的不同方法。在这个故事中,我们将学*如何只用几行代码就能做到这一点。
在自然语言处理领域,由于语言的高度复杂性,对文本进行扩充是非常困难的。不是每一个词我们都可以用其他词来代替,比如 a,an,the。另外,不是每个单词都有同义词。即使改变一个单词,上下文也会完全不同。另一方面,在计算机视觉领域生成增强图像相对容易。即使引入噪声或裁剪掉图像的一部分,模型仍然可以对图像进行分类。
nlpaug 简介
在计算机视觉项目中使用 imgaug 后,我在想我们是否可以有一个类似的库来生成合成数据。因此,我通过使用现有的库和预先训练的模型来重新实现那些研究论文。nlpaug 的基本要素包括:
- OCR 增强器、QWERTY 增强器和随机字符增强器
Word: WordNet 增强器、word2vec 增强器、GloVe 增强器、fasttext 增强器、BERT 增强器、随机单词字符Flow:序列增强器,有时是增强器
直观来看,Character Augmenters和Word Augmenters分别专注于字符级和单词级的操控。Flow像管弦乐队一样控制增强流。您可以访问 github 获取库。
性格;角色;字母
在字符级别扩充数据。可能的场景包括图像到文本和聊天机器人。在从图像中识别文本的过程中,我们需要光学字符识别(OCR)模型来实现,但是 OCR 会引入一些错误,如识别“0”和“0”。在聊天机器人中,我们仍然会有打字错误,尽管大多数应用程序都有单词纠正功能。为了克服这个问题,你可以让你的模型在在线预测之前“看到”那些可能的结果。
光学字符识别
当处理 NLP 问题时,OCR 结果可能是您的 NLP 问题的输入之一。例如,“0”可能被识别为“O”或“O”。如果你正在使用单词袋或经典单词嵌入作为一个特征,你将会遇到麻烦,因为你周围的词汇(OOV)现在和将来都是如此。如果你使用最先进的模型,比如伯特和 GPT和的话,OOV 问题似乎就解决了,因为 word 将被拆分成子 word。然而,一些信息丢失了。
OCRAug旨在模拟 OCR 错误。它会用预定义的映射表替换目标字符。

增强的例子
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
The quick brown fox jumps over the lazy **d0g**
QWERTY 键盘
你可能参与的另一个项目是聊天机器人或其他信息渠道,如电子邮件。虽然拼写检查将被执行,但一些拼写错误仍然存在。可能会伤害到你之前提到的 NLP 模型。
QWERTYAug旨在模拟关键字距离误差。它会将目标字符替换为 1 个关键字距离。您可以配置是否包括数字或特殊字符。

增强的例子
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
**Tne 2uick hrown Gox jumpQ ovdr tNe <azy d8g**
随机字符
根据不同的研究,噪声注入有时可能有助于推广您的 NLP 模型。我们可能会在您的单词中添加一些干扰,例如从您的单词中添加或删除一个字符。
RandomCharAug旨在给你的数据注入噪音。与OCRAug和QWERTYAug不同,它支持插入、替换和插入。
插入增强的例子
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
**T(he quicdk browTn Ffox jumpvs 7over kthe clazy 9dog**
单词
除了字符增强,单词水平也很重要。我们利用 word2vec (Mikolov 等人,2013)、GloVe (Pennington 等人,2014)、fasttext (Joulin 等人,2016)、BERT(Devlin 等人,2018)和 wordnet 来插入和替换相似的单词。Word2vecAug、GloVeAug和FasttextAug使用单词嵌入来寻找最相似的一组单词来替换原来的单词。另一方面,BertAug利用语言模型预测可能的目标词。WordNetAug用统计的方式找到一组相似的单词。
单词嵌入(word2vec、GloVe、fasttext)
经典的嵌入使用静态向量来表示单词。理想情况下,如果向量彼此靠*,这个词的意思是相似的。其实要看训练数据。比如 word2vec 中的“兔子”类似于“狐狸”,而 GloVe 中的“nbc”类似于“狐狸”。

Most similar words of “fox” among classical word embeddings models
有时,您希望用相似的单词替换单词,以便 NLP 模型不依赖于单个单词。Word2vecAug、GloVeAug和FasttextAug被设计成基于预先训练的向量来提供“相似”的单词。
除了替换之外,插入还有助于在数据中加入噪声。它从词汇中随机挑选单词。
插入增强的例子
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
The quick **Bergen-Belsen** brown fox jumps over **Tiko** the lazy dog
替代增强示例
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
The quick **gray** fox jumps over to lazy dog
语境化的单词嵌入
因为经典的单词嵌入使用静态向量来表示同一个单词。它可能不适合某些场景。因为“福克斯”可以代表动物和广播公司。为了解决这个问题,引入了上下文化单词嵌入来考虑周围的单词,以生成不同上下文下的向量。
BertAug旨在提供此功能来执行插入和替换。与以前的单词嵌入不同,插入是由 BERT 语言模型预测的,而不是随机选取一个单词。替换使用周围的单词作为特征来预测目标单词。
插入增强的例子
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
the **lazy** quick brown fox **always** jumps over the lazy dog
替代增强示例
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
the quick **thinking** fox jumps over the lazy dog
同义词
除了神经网络方法之外,同义词库也可以达到类似的目的。同义词的局限性在于有些词可能没有相似的词。来自一个很棒的 NLTK 库的 WordNet 帮助找到同义词。
WordNetAug提供了替换功能来替换目标单词。不是纯粹地寻找同义词,一些初步的检查确保目标单词可以被替换。这些规则是:
- 不要选择限定词(例如,a、an、the)
- 不要选择没有同义词的词。
增强的例子
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
The quick brown fox parachute over the lazy blackguard
随机词
到目前为止,我们没有在单词级引入删除。RandomWordAug可以帮助随机删除一个单词。
增强的例子
Original:
The quick brown fox jumps over the lazy dog
Augmented Text:
The fox jumps over the lazy dog
流动
到此为止,上述增强器都可以单独调用。如果你想把多个增强器组合在一起呢?为了使用多个增强器,有时会引入管道来连接增强器。一个文本可以通过不同的增强器产生不同的数据。
时序
您可以向该流程添加任意数量的增强器,然后Sequential逐个执行它们。例如,你可以将RandomCharAug和RandomWordAug组合在一起。
有时
如果您不想一直执行同一组增强器,sometimes每次都会选择一些增强器。
建议
上述方法旨在解决作者在他们的问题中所面临的问题。如果你了解你的数据,你应该量身定做增强方法。请记住,数据科学的黄金法则是垃圾进垃圾出。
一般来说,您可以在不完全理解数据的情况下尝试同义词库方法。由于上述同义词库方法的限制,它可能不会提高很多。
关于我
我是湾区的数据科学家。专注于数据科学、人工智能,尤其是 NLP 和平台相关领域的最新发展。欢迎在 LinkedIn 上与 me 联系,或者在 Medium 或 Github 上关注我。
延伸阅读
- 图像增强库( imgaug )
- 文本增强库( nlpaug )
- NLP 中的数据扩充
- 音频数据增强
- 声谱图数据增强
- 你的自然语言处理模型能够防止恶意攻击吗?
- NLP 中的数据扩充:来自 Kaggle Master 的最佳实践
参考
- X.张、赵军、李乐存。用于文本分类的字符级卷积网络。2015
- W.王燕和杨丹。真讨厌!!!:基于词汇和框架语义嵌入的数据增强方法,使用#petpeeve Tweets 对恼人的行为进行自动分类。2015
- 南小林。语境扩充:通过具有聚合关系的词语进行数据扩充。2018
- C.库伦贝。利用 NLP 云 API 简化文本数据扩充。2018
python 中的数据扩充技术
使用 imgaug 一瞥 Python 中的数据扩充技术
在本文中,我们将使用 imgaug 库探索 Python 中不同的数据扩充技术

什么是图像增强?
图像增强是一种非常强大的技术,用于在现有图像中人工创建变化,以扩展现有图像数据集。这从代表可能图像的综合集合的现有图像数据集中创建新的和不同的图像。
这是通过应用不同的变换技术来实现的,如缩放现有图像、将现有图像旋转几度、剪切或裁剪现有图像集等。
为什么我们需要图像增强?
深度学*卷积神经网络(CNN)需要大量的图像来有效地训练模型。这有助于通过更好地概括来提高模型的性能,从而减少过度拟合。大多数用于分类和对象检测数据集的流行数据集具有几千到几百万个图像。
泛化指的是在训练过程中根据以前见过的数据,对照以前从未见过的测试或验证数据来评估模型的性能。
CNN 由于其方差属性,即使在不同大小、方向或不同照明下可见,也可以对对象进行分类。因此,我们可以利用图像的小数据集,通过放大或缩小、垂直或水平翻转或改变亮度(只要对对象有意义),将对象转换为不同的大小。这样我们就创建了一个丰富多样的数据集。
图像增强从一小组图像中创建一组丰富多样的图像,用于图像分类、对象检测或图像分割。
在仔细理解问题域之后,需要应用增加训练数据集大小的增强策略。
如果我们正在对不同类型的汽车进行分类,那么通过垂直翻转汽车来应用图像增强对于问题域是没有用的,或者当我们正在对手写数字进行图像分类时,垂直翻转数字 6 不会增加价值
什么时候应用图像增强?
图像增强可以在我们训练模型之前作为预处理步骤来应用,或者可以实时应用。
离线或预处理增强
扩充作为预处理步骤被应用以增加数据集的大小。当我们想要扩展一个小的训练数据集时,通常会这样做。
在较小的数据集上生成增强是有帮助的,但是在应用于较大的数据集时,我们需要考虑磁盘空间
在线或实时增强
顾名思义,增强是实时应用的。这通常适用于较大的数据集,因为我们不需要将增强的图像保存在磁盘上。
在这种情况下,我们以小批量的方式应用转换,然后将其提供给模型。
在线增强模型将在每个时期看到不同的图像。在离线增强中,增强图像是训练集的一部分,它根据历元数多次查看增强图像。
该模型通过在线增强进行更好的概括,因为它在使用在线数据增强进行训练的过程中看到了更多的样本。
我们将使用 imgaug 类来演示图像增强。
imgaug 支持多种数据增强技术
基本数据扩充技术
- 翻转:垂直或水平翻转图像
- 旋转:将图像旋转指定的角度。
- 剪切:像平行四边形一样移动图像的一部分
- 裁剪:对象以不同的比例出现在图像的不同位置
- 放大,缩小
- 改变亮度或对比度
我们现在将使用 imgaug 库来探索这些数据扩充技术
伊姆高格
imgaug 是一个用于图像增强以及关键点/界标、边界框、热图和分割图的库。
安装库
pip install imgaug
在某些情况下,我们得到一个 Shapely 错误,在这种情况下,我们可以尝试以下命令
pip install imgaug — upgrade — no-deps
我们将拍摄一张图像,并使用基本的数据增强技术对其进行转换。
导入所需的库
import imageio
import imgaug as ia
import imgaug.augmenters as iaa
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib
%matplotlib inline
显示原始图像
我们使用 imageio 显示原始图像
image = imageio.imread(“.\\car2.jpeg”)
ia.imshow(image)

Original Image
旋转图像
我们可以通过指定旋转度数来旋转图像。我们将图像旋转-50 度到 30 度
**rotate=iaa.Affine(rotate=(-50, 30))
rotated_image=rotate.augment_image(image)**
ia.imshow(rotated_image)

Image rotated by -50 to 30 degrees
向图像添加噪声
我们将从高斯分布采样的不同噪声值按元素添加到图像中。
**gaussian_noise=iaa.AdditiveGaussianNoise(10,20)**
**noise_image=gaussian_noise.augment_image(image)**
ia.imshow(noise_image)

Adding Gaussian noise
裁剪图像
裁剪会移除图像两侧的像素列/行。在下面的例子中,我们将图像的一边裁剪 30%
**crop = iaa.Crop(percent=(0, 0.3)) # crop image
corp_image=crop.augment_image(image)**
ia.imshow(corp_image)

cropped one side of the image by 30%
剪切图像
将图像剪切 0 到 40 度
shear = iaa.Affine(shear=(0,40))
shear_image=shear.augment_image(image)
ia.imshow(shear_image)

Sheared image by 0 to 45 degrees
翻转图像
我们可以垂直或水平翻转图像。 Fliplr 水平翻转图像
#flipping image horizontally
flip_hr=iaa.Fliplr(p=1.0)
flip_hr_image= flip_hr.augment_image(image)
ia.imshow(flip_hr_image)

Flipping image horizontally
翻转垂直翻转图像
flip_vr=iaa.Flipud(p=1.0)
flip_vr_image= flip_vr.augment_image(image)
ia.imshow(flip_vr_image)

Flipping image vertically
改变图像的亮度
我们使用 GammaContrast 通过缩放像素值来调整图像亮度。范围gamma=(0.5, 2.0)内的值似乎是合理的。我们可以使用s 形对比度或线性对比度来改变图像的亮度
image = imageio.imread(“.\\img Aug\\car2.jpeg”)
contrast=iaa.GammaContrast(gamma=2.0)
contrast_image =contrast.augment_image(image)
ia.imshow(contrast_image)

adding contrast to the image
缩放图像
我们可以使用缩放来放大或缩小图像。我们将下面的图像缩放到图像高度/宽度的 150%到 80%。我们可以独立缩放每个轴
image = imageio.imread("C:\\Data\\img Aug\\car2.jpeg")
scale_im=iaa.Affine(scale={"x": (1.5, 1.0), "y": (1.5, 1.0)})
scale_image =scale_im.augment_image(image)
ia.imshow(scale_image)

scaling image 150% to 80% of its height/width.
用于目标检测的增强
我们绘制物体检测的包围盒。当我们放大图像时,我们希望边界框也相应地更新。
imgaug 提供了对边界框的支持。当我们旋转、剪切或裁剪图像时,对象周围的边界框也会相应地更新。
从 imgaug 导入边界框
from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage
初始化原图像周围的包围盒
bbs = BoundingBoxesOnImage([
BoundingBox(x1=10, x2=520, y1=10, y2=300)
], shape=image.shape)
在原始图像的顶部显示边界框
ia.imshow(bbs.draw_on_image(image, size=2))

在下面的代码中,我们使用 translate_percentage 移动图像,增大边界框并将其应用到图像上
move=iaa.Affine(translate_percent={"x": 0.1}, scale=0.8)
image_aug, bbs_aug = move(image=image, bounding_boxes=bbs)
ia.imshow(bbs_aug.draw_on_image(image_aug, size=2))

bounding box on the augmented image
在应用图像增强后处理图像外部的边界框
边界框有时可能会超出图像,我们需要额外的代码来处理这种情况
我们旋转图像,并尝试在对象周围绘制边界框
rotate_bb=iaa.Affine(rotate=(-50, 30))
image_aug, bbs_aug = rotate_bb(image=image, bounding_boxes=bbs)
ia.imshow(bbs_aug.draw_on_image(image_aug, size=2))

部分边界框位于图像之外。在下面的代码中,我们将
- 将边框完全或部分移出图像
- 裁剪部分在外的边界框,使它们完全在图像内
我们创建一个填充函数,用 1 个像素的白色和 1 个像素的黑色边框填充图像:
**def pad(image, by)**:
image_border1 = ia.pad(image, top=1, right=1, bottom=1, left=1,
mode="constant", cval=255)
image_border2 = ia.pad(image_border1, top=by-1, right=by-1,
bottom=by-1, left=by-1,
mode="constant", cval=0)
return image_border2
然后,我们在图像上绘制边界框。我们首先通过边界像素扩展图像平面,然后标记图像平面内的边界框
def draw_bbs(image, bbs, border):
GREEN = [0, 255, 0]
ORANGE = [255, 140, 0]
RED = [255, 0, 0]
**image_border = pad(image, border)**
for bb in bbs.bounding_boxes:
** if bb.is_fully_within_image(image.shape):**
color = GREEN
**elif bb.is_partly_within_image(image.shape):**
color = ORANGE
else:
color = RED
**image_border = bb.shift(left=border, top=border)\
.draw_on_image(image_border, size=2, color=color)**return image_border
我们现在对图像应用相同的旋转,并绘制边界框
rotate=iaa.Affine(rotate=(-50, 30))
image_aug, bbs_aug = rotate(image=image, bounding_boxes=bbs)image_after = draw_bbs(image_aug, bbs_aug.remove_out_of_image().clip_out_of_image(), 100)
ia.imshow(image_after)

在下一篇文章中,我们将探讨用于图像增强的 Keras imagedata generator
参考资料:
[## imgaug - imgaug 0.3.0 文档
imgaug 是一个用于机器学*实验中图像增强的库。它支持大范围的增强…
imgaug.readthedocs.io](https://imgaug.readthedocs.io/en/latest/index.html)
https://link . springer . com/content/pdf/10.1186% 2fs 40537-019-0197-0 . pdf
使用 fastai 的数据增强

介绍
数据扩充指的是对数据集中的图像随机应用各种变换。这些转换有助于在我们的数据集中引入更多种类。让我们看看这是什么意思。

考虑一个构建面锁的例子。当你为锁取样时,你现在拍一些照片,你就完成了。然而,当用户想要解锁他的手机时,他并不总是处于相同的照明中(亮度)。或者,用户不会总是以相同的角度面对相机(翘曲)。可能他的照片因为某种原因(抖动)显得模糊。在训练数据时,我们需要考虑这些情况。这正是数据增强带给我们的。
我们从这样一幅图像开始

我们最终得到了一堆像这样的照片

我们所做的是,不是每次都给模型提供相同的图片,而是做一些小的随机变换(一点旋转、缩放、平移等),这些变换不会改变图像内部的内容(对于人眼而言),但会改变其像素值。用数据扩充训练的模型将更好地概括。在训练样本数量相对较少的情况下,数据扩充也很有用。
工作
在这篇短文中,我们不会过多关注实现这些转换的代码。相反,我们将可视化各种可用的转换,以理解当我们执行数据扩充时在幕后发生了什么。
我们从查看 fastai 库中的缺省值开始。这些缺省值是以这样一种方式设置的,它们可以很好地用于大多数任务。

然而,我们可以根据应用程序的性质改变其中的一些(例如do_flip)。
get transforms返回两个转换列表的元组:一个用于训练集,一个用于验证集。但是,我们不想修改验证集中的图片,所以第二个列表仅限于调整图片的大小。

我们使用一个小助手功能来绘制网格。函数如下所示。

**kwargs意味着我们可以传递任意数量的命名参数(并且我们事先不知道数量)。要传递的一些有用的参数可以是size,这样所有的图像都是相同的大小,以及padding,以决定如何处理图像中丢失的像素。
现在我们已经把所有的部分都准备好了,让我们来看看我们能做的一些很酷的转换。
随机缩放/裁剪
随机裁剪或缩放图像。

辐状的
我们在转换中引入的随机性可以通过两种方式实现。
在第一种方法中,我们可以传递一个图像将被变换的概率。基于该概率,图像将被变换或不被变换。在下一个变换中,我们以 50%的概率将图像旋转 30 度。

在第二种方式中,我们不是传递概率,而是传递一系列值(这里是度数)。然后图像将在两个浮动之间随机旋转一个角度。

我们也可以把上面提到的 2 种方法结合起来!
聪明
这个转换需要一个名为change.的参数,设置change为 0 将把我们的图像转换成黑色,设置为 1 将把它变成白色

对比
与亮度类似,对比度可以使用scale参数进行设置。

有两个平面的
该变换结合了翻转(水平或垂直)和 90 度倍数的旋转。

翻转 _lr(镜像)
水平翻转图像。

振动
这种变换通过用来自邻域的像素随机替换图像的像素来改变图像的像素,从而导致抖动。

对称翘曲
更改查看图像的角度。

这些是计算机视觉项目的一些最佳增强。您可以阅读 fastai 文档了解更多信息。
一个有趣的研究领域是其他领域的数据扩充,例如声音数据。
编辑:
混淆:超越经验风险最小化
Mixup 是一个巧妙的数据增强小技巧。在 mixup 中,我们不是提供网络原始图像,而是获取 2 幅图像,并对它们进行线性组合,如下所示:
new_image = t * image1 + (1-t) * image2
其中 t 是介于 0 和 1 之间的浮点数。那么我们分配给该图像的目标是原始目标的相同组合:
new_target = t * target1 + (1-t) * target2
看看下面的图片

狗还是猫?这里的正确答案是 70%狗,30%猫!
当执行 mixup 时,实验表明跨类(而不是同一个类)执行通常是个好主意。此外,mixup 一次应用于一个小批量,而不是整个数据集(同样工作良好)。
使用 mixup 的主要优点是它可以用几行代码实现,几乎没有任何计算开销。mixup 所做的是,它迫使你的神经网络偏向于你的类之间的简单线性行为。它使你的神经网络在对抗敌对样本时更加强健,并帮助它更好地调整。
在 fastai 中,使用 mixup 非常简单:
learner = Learner(data, model, metrics=[accuracy]).mixup()
在我目前看到的例子中,通常是和混合精度训练一起使用。阅读这篇研究论文了解更多信息。
这就是本文的全部内容。
如果你想了解更多关于深度学*的知识,可以看看我在这方面的系列文章:
我所有关于深度学*的文章的系统列表
medium.com](https://medium.com/@dipam44/deep-learning-series-30ad108fbe2b)
~快乐学*。
多伦多的数据职业市场洞察

Figure 0. Indeed Job Search Results on Related Data Career
这是在多伦多寻找梦想职业的绝佳时机。多伦多正在成为大恐龙数码、微软、亚马逊等软件公司的热点(即人工智能中心)。他们希望招聘以下职位的人才,如数据科学家、数据工程师、数据分析师和商业智能角色。对于应届毕业生和转行者来说,最常见的问题是:我如何才能实现我梦想的职业?
在许多情况下,大多数人会去像 Indeed.ca 这样的求职网站,输入一些关键词(例如,职位、一些工具等)。)检索求职结果。然后,人们一个接一个地查看职位描述,尽可能多地提交申请。不对!
有几个原因让你不应该这样做:
1.浪费的努力和时间:你在浪费自己的时间,也在浪费招聘公司的时间,去过滤大量不相关的简历,这些简历可能甚至不适合他们正在看的东西。
2.你想最大化获得面试的机会:你想象过自己不带武器去打仗吗?同样,你必须至少具备招聘经理需要考虑的基本必备工具。
那么我们能做什么呢?不要担心,我们可以通过使用 Python 库(漂亮的汤)有策略地让事情变得简单一点。有了这个库,我们可以创建一个 web scraper 函数来从 Indeed.ca 检索您在多伦多的工作搜索结果。这将有助于我们战略性地提交工作申请,并在未来的工作提交中优先考虑您学*所需工具集的目标。
在我们开始之前,让我们先从 5 个关键问题开始了解一些想法:
1.哪些公司希望聘用这些人才(如数据科学家、数据工程师等)。)
2.这些不同角色的平均工资是多少?
3.这些不同角色所需的前 5 项必备技能是什么?
4.这些不同的角色要求或推荐的教育水平是什么?
5.这些不同角色的首选教育背景(研究领域)是什么?
好了,这里是一个快速概述的数据角力管道,为真正的工作刮刀功能,我建立了回答这 5 个关键问题:

Figure 1. Indeed Scraper Data Pipeline
第一步。连接到职务搜索 URL 页面
第二步。标记化,解析单词,然后做一些文本清理。
第三步。创建主要功能,以收集职位页面描述(职位,地点,工资等)。)
第四步。遍历每个页面的 URL 和存储的信息。
第五步。创建字典来获取所需的技能、教育水平等。计算每个特定术语的频率,然后将结果可视化!
让我们深入探讨从结果中获得的 5 个重要见解。
A .数据科学家职位的招聘公司和平均薪酬列表:
首先,各行各业都有数据科学家的工作。大多数工作都在 金融或保险公司 像道明、丰业银行、阳光人寿金融等。其他职业也有类似的趋势。只不过,不同的公司在招聘人才。

Figure 2. List of Hiring Companies
其次,据报道,多伦多数据科学家的平均年薪为 91,000 美元。然而,这一薪资比 Indeed.ca 的平均薪资低了 5%。这是因为一些调查还包括了高级数据科学家的薪资。实际上,入门级数据科学家的平均年薪应该在 80 到 85K 之间。

Figure 3. Average Salaries of a Data Scientist in Toronto
B .数据科学家与数据工程师的比较:

Figure 4. Bar Charts Comparison between Data Scientist vs. Data Engineer
该图代表了每个职业要求最高的五项技能、DevOps 和云工具。以下是基于共性(相似性)和差异的两个职位之间的总结。
通用性:
在列出的前 5 项技能中,数据科学家和数据工程师都需要了解像 Python、Spark、Hadoop 和 SQL 这样的工具。其中 Python 和 SQL 是基本工具,而 Spark 和 Hadoop 对于拥有大数据存储的公司来说是必不可少的。
差异:
对于数据科学家角色,招聘公司更专注于查看 其他数据分析工具和数据可视化经验(即 SAS、Tableau 等。)。另一方面,数据工程师的角色主要集中在类似 云平台(AWS)和 DevOps (Jenkins,Kubernetes,Docker) 的工具上。
这种差异主要有两个原因。首先,数据工程师创建通向由数据科学家创建的生产 ML 模型的数据管道。他们需要具备 DevOps 工具经验。这有助于他们以最有效的方式在不同的修复和发布之间实现模型/代码部署生命周期的敏捷实践。其次,由于灵活性和成本效益,大多数初创公司或大公司都使用 AWS 等云平台,而不是内部解决方案(内部数据仓库)。不过,这在很大程度上取决于其他因素,如公司的业务/战略路线图、系统架构和环境。
C .数据科学家的教育要求和背景
关于教育水平,数据科学家是大多数招聘公司倾向于聘用具有博士学位的申请人的唯一职业(左图)。对于其他职业,如数据工程师、数据分析师和商业智能角色,学士学位教育足以胜任工作。

Figure 5. Education Levels of Data Scientist Job Posted on Indeed.ca
此外,许多求职者会想知道什么样的教育背景或研究领域对数据科学家来说是理想的?从分析来看,似乎大多数招聘公司都希望应聘者来自科学、技术、工程和数学等科学、技术、工程和数学领域。特别是对于数据科学家来说,很多公司希望应聘者有数学背景,其次是计算机科学、工程等等。

Figure 6. Educational Backgrounds Posted on Data Scientist Positions from Indeed.ca
那么,这为什么会成为数据科学家的一种趋势呢?
我通过自己的研究从出版的期刊、数据科学会议和与数据科学家导师的闲聊中收集了一些见解。让我们从一个问题开始,为什么大多数招聘公司都想要博士级别的候选人。
首先,随着人工智能(AI)趋势的复苏,有很多公司有兴趣建立自己的人工智能/深度学*(DL)研究和产品开发团队。他们想要拥有先进的人工智能和人工智能算法知识的人。不要只使用 R 和 Python 中已有的库和包。这些数据科学家必须能够从零开始调整和实现新的算法,以解决特定的业务问题并构建数据产品。
第二,数学似乎是许多公司希望雇佣的最著名的学科,其次是计算机科学和工程学位。这与数据科学家角色的性质密切相关。作为数据科学家,必须能够很好地理解不同领域的数学,如线性代数、微积分和统计。因为所有的 ML 算法都是关于理解如何将这些算法应用于数据集,并为解决特定的业务问题制定独特的解决方案。还有,数据科学家一定要做好编程。这就是为什么许多公司希望应聘者拥有计算机科学或工程学位的原因。大多数代码/模型开发都是在 Python/R 上完成的,作为一名数据科学家,在生产环境中编写高效且可扩展的代码对于完成工作来说至关重要。
感谢阅读这篇文章。我希望许多读者会对此感兴趣。我强烈鼓励其他行业的读者考虑学* Python,并为您的行业市场洞察力构建一个 web scraper 函数
R 降价中缺失值的数据清理、检测和插补—第 2 部分
使用 2000 年至 2018 年男子巡回赛的澳大利亚网球公开赛数据在 R 中进行数据清洗和转换变量。

Photo by The Creative Exchange on Unsplash
今天是第 4 天,标志着我的# 29 Day 数据科学项目训练营和我在数据科学中学到的东西的继续。这是一个清洗和预处理 2000 年至 2019 年澳大利亚网球公开赛网球数据的教程指南,以使用 r 预测谁可能获胜。昨天,我们合并了多个 csv 文件,并子集化了您可能会关注的数据此处来自博客帖子。
什么是 CRISP-DM 方法论?
如果你在大会上一堂数据分析课,在你的大学上一门数据科学算法的数据科学课程,选修一门感兴趣的 IT 选修课,学*一门在线课程,或者你可能为你的教授做一个数据挖掘项目,你会遇到 CRISP-DM 的数据挖掘原理。
CRISP-DM 是数据挖掘的跨行业流程。这也是我在悉尼大会上学到的一个规定的分析工作流程,当你在政府部门咨询时,这些原则会被用在数据科学项目中。
这是 CRISP-DM 方法的工作流程,用于指导您解决数据分析和数据科学问题。您可以回顾 2018 年欧洲智能视觉展的每个步骤。

Source: Smart Vision 2018
商业理解
这不是一个商业问题,但问题陈述的范围是根据 2000 年至 2018 年的数据预测今年澳大利亚网球公开赛决赛的冠军。
数据理解
以前,我们获得了一个数据字典,并通过分析数据的整体结构来研究数据。
以下是变量的数据字典:
ATP = Tournament number (men)
WTA = Tournament number (women)
Location = Venue of tournament
Tournament = Name of tounament (including sponsor if relevant)
Data = Date of match (note: prior to 2003 the date shown for all matches played in a single tournament is the start date)
Series = Name of ATP tennis series (Grand Slam, Masters, International or International Gold)
Tier = Tier (tournament ranking) of WTA tennis series.
Court = Type of court (outdoors or indoors)
Surface = Type of surface (clay, hard, carpet or grass)
Round = Round of match
Best of = Maximum number of sets playable in match
Winner = Match winner
Loser = Match loser
WRank = ATP Entry ranking of the match winner as of the start of the tournament
LRank = ATP Entry ranking of the match loser as of the start of the tournament
WPts = ATP Entry points of the match winner as of the start of the tournament
LPts = ATP Entry points of the match loser as of the start of the tournament
W1 = Number of games won in 1st set by match winner
L1 = Number of games won in 1st set by match loser
W2 = Number of games won in 2nd set by match winner
L2 = Number of games won in 2nd set by match loser
W3 = Number of games won in 3rd set by match winner
L3 = Number of games won in 3rd set by match loser
W4 = Number of games won in 4th set by match winner
L4 = Number of games won in 4th set by match loser
W5 = Number of games won in 5th set by match winner
L5 = Number of games won in 5th set by match loser
Wsets = Number of sets won by match winner
Lsets = Number of sets won by match loser
Comment = Comment on the match (Completed, won through retirement of loser, or via Walkover)
数据准备—数据清理
通过分类变量的编码、转换变量和缺失值的检测,对数据进行了清理。我们已经合并了原始数据,并使用 StringAsfactors =FALSE 将“aus_open.csv”数据帧读入 R 中,以确保所有变量不会自动作为因子读取。
###################################
# Pre-Processing the Training Data
###################################
# Exported aust_open.csv file was exported and a new column was created in Excel to extract the year from the data with non-standardised formatting
a <- read.csv("aus_open.csv",stringsAsFactors = FALSE,header = TRUE)
探索性数据分析
使用 Hadley Wickham 的 dplyr 包,我使用函数 glimpse()探索了数据的结构。
#############################
# Exploratory Data Analysis
#############################
glimpse(a) # view the structure of the training data
描述统计学
下面提供了描述性统计的输出,是所有数值变量和缺失值的汇总。
summary(a) # descriptive statistics
ATP Location Tournament Date
Min. :6 Length:2413 Length:2413 Length:2413
1st Qu.:6 Class :character Class :character Class :character
Median :6 Mode :character Mode :character Mode :character
Mean :6
3rd Qu.:6
Max. :6
Year Series Court Surface
Min. :2000 Length:2413 Length:2413 Length:2413
1st Qu.:2004 Class :character Class :character Class :character
Median :2009 Mode :character Mode :character Mode :character
Mean :2009
3rd Qu.:2014
Max. :2018
Round Best.of Winner Loser
Length:2413 Min. :5 Length:2413 Length:2413
Class :character 1st Qu.:5 Class :character Class :character
Mode :character Median :5 Mode :character Mode :character
Mean :5
3rd Qu.:5
Max. :5
WRank LRank W1 L1
Min. : 1.00 Length:2413 Min. : 0 Length:2413
1st Qu.: 10.00 Class :character 1st Qu.: 7 Class :character
Median : 28.00 Mode :character Median : 860 Mode :character
Mean : 46.97 Mean : 1863
3rd Qu.: 65.00 3rd Qu.: 2145
Max. :768.00 Max. :16790
NA's :128
W2 L2 W3 L3
Min. :0.000 Min. :0.000 Min. :0.000 Min. :0.000
1st Qu.:6.000 1st Qu.:3.000 1st Qu.:6.000 1st Qu.:2.000
Median :6.000 Median :4.000 Median :6.000 Median :4.000
Mean :5.687 Mean :4.027 Mean :5.743 Mean :3.903
3rd Qu.:6.000 3rd Qu.:6.000 3rd Qu.:6.000 3rd Qu.:6.000
Max. :7.000 Max. :7.000 Max. :7.000 Max. :7.000
NA's :10 NA's :10 NA's :33 NA's :33
W4 L4 W5 L5
Min. :0.000 Min. :0.000 Min. : 0.000 Min. : 0.000
1st Qu.:6.000 1st Qu.:2.000 1st Qu.: 6.000 1st Qu.: 2.000
Median :6.000 Median :4.000 Median : 6.000 Median : 4.000
Mean :5.753 Mean :3.734 Mean : 5.883 Mean : 3.813
3rd Qu.:6.000 3rd Qu.:6.000 3rd Qu.: 6.000 3rd Qu.: 6.000
Max. :7.000 Max. :7.000 Max. :21.000 Max. :19.000
NA's :346 NA's :346 NA's :1440 NA's :1440
Wsets Lsets Comment
Min. : 0.000 Min. : 0.0 Length:2413
1st Qu.: 3.000 1st Qu.: 0.0 Class :character
Median : 3.000 Median : 1.0 Mode :character
Mean : 4.192 Mean : 1.7
3rd Qu.: 6.000 3rd Qu.: 2.0
Max. :22.000 Max. :20.0
NA's :1444 NA's :1444
转换和编码分类变量
从上面的结构可以看出,一些数据属性没有转换成正确的数据类型。这是我最喜欢的部分转换变量的机会!开始了…
# Transform character variables into numeric variables a$W1 <- as.numeric(a$W1)
a$L1 <- as.numeric(a$L1)
a$WRank <- as.numeric(a$WRank)
a$LRank <- as.numeric(a$LRank)
##########################################################
# encoding categorical features
##########################################################
# Convert categorical variables into factors to represent their levels
a$Location <- factor(a$Location)
a$Tournament <- factor(a$Tournament)
a$Series <- factor(a$Series)
a$Court <- factor(a$Court)
a$Surface <- factor(a$Surface)
a$Best.of <- factor(a$Best.of)
a$Round <- factor(a$Round)
a$Winner <- factor(a$Winner)
a$Loser <- factor(a$Loser)
a$Comment <- factor(a$Comment)
glimpse(a) # check that structure of categorical variables have converted with levels
#为两级以上的分类变量创建虚拟变量
library(dummies)
Round <- dummy(a$Round)
Best.of <- dummy(a$Best.of)
Winner <- dummy(a$Winner)
Loser <- dummy(a$Loser)
Comment <- dummy(a$Comment)
head(a) # check that the values are been converted to dummy variables
str(a)
总结描述性统计数据,并检查转换变量的结构
# Descriptive statistics
summary(a)# View the structure of the transformed variables the 'dplyr' way
> glimpse(a)Observations: 2,413
Variables: 27
$ ATP <int> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...
$ Location <fct> Melbourne, Melbourne, Melbourne, Melbourne, Melbourne,...
$ Tournament <fct> Australian Open, Australian Open, Australian Open, Aus...
$ Date <chr> "1/17/00", "1/17/00", "1/17/00", "1/17/00", "1/17/00",...
$ Year <int> 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, ...
$ Series <fct> Grand Slam, Grand Slam, Grand Slam, Grand Slam, Grand ...
$ Court <fct> Outdoor, Outdoor, Outdoor, Outdoor, Outdoor, Outdoor, ...
$ Surface <fct> Hard, Hard, Hard, Hard, Hard, Hard, Hard, Hard, Hard, ...
$ Round <fct> 1st Round, 1st Round, 1st Round, 1st Round, 1st Round,...
$ Best.of <fct> 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ...
$ Winner <fct> Agassi A., Alami K., Arazi H., Behrend T., Bjorkman J....
$ Loser <fct> Puerta M., Manta L., Alonso J., Meligeni F., Stoltenbe...
$ WRank <dbl> 1, 35, 41, 106, 76, 151, 39, 54, 30, 64, 98, 29, 34, 6...
$ LRank <dbl> 112, 107, 111, 28, 81, 57, 22, 66, 51, 155, 119, 257, ...
$ W1 <dbl> 6, 6, 6, 6, 6, 7, 3, 7, 7, 7, 6, 6, 6, 6, 6, 7, 6, 6, ...
$ L1 <dbl> 2, 4, 3, 2, 7, 6, 6, 6, 6, 6, 4, 7, 7, 4, 3, 6, 4, 3, ...
$ W2 <int> 6, 7, 7, 4, 6, 6, 6, 6, 6, 5, 6, 7, 6, 6, 6, 6, 7, 6, ...
$ L2 <int> 2, 6, 6, 6, 4, 1, 1, 4, 4, 7, 4, 6, 3, 4, 3, 3, 6, 3, ...
$ W3 <int> 6, 7, 6, 6, 6, 6, 6, NA, 6, 6, 7, 1, 7, 7, 4, 7, 4, 6,...
$ L3 <int> 3, 5, 2, 7, 4, 4, 4, NA, 4, 3, 6, 6, 5, 6, 6, 6, 6, 2,...
$ W4 <int> NA, NA, NA, 6, 0, NA, 7, NA, NA, 7, NA, 6, 6, NA, 7, N...
$ L4 <int> NA, NA, NA, 3, 6, NA, 6, NA, NA, 5, NA, 3, 1, NA, 6, N...
$ W5 <int> NA, NA, NA, 6, 6, NA, NA, NA, NA, NA, NA, 6, NA, NA, N...
$ L5 <int> NA, NA, NA, 0, 4, NA, NA, NA, NA, NA, NA, 1, NA, NA, N...
$ Wsets <int> 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, ...
$ Lsets <int> 0, 0, 0, 2, 2, 0, 1, 0, 0, 1, 0, 2, 1, 0, 1, 0, 2, 0, ...
$ Comment <fct> Completed, Completed, Completed, Completed, Completed,...
检测缺失值
我们将尝试一些方法来检测缺失值,例如计算每列缺失值的数量、求和并取平均值。
# Sum the number of missing values> sum(is.na(a))
[1] 6810# average of the missing values in each column> mean(is.na(a))
[1] 0.1045264# Count the number of missing values per column> colSums(is.na(a)) ATP Location Tournament Date Year Series Court
0 0 0 0 0 0 0
Surface Round Best.of Winner Loser WRank LRank
0 0 0 0 0 0 5
W1 L1 W2 L2 W3 L3 W4
128 131 10 10 33 33 346
L4 W5 L5 Wsets Lsets Comment
346 1440 1440 1444 1444 0
获取缺失值的百分比
在检测缺失值时,5%是每列的可接受阈值。输出确认了 L4、W4、L5、W5、Wsets 和 Lsets 列的缺失值大于 5%,可以删除或估算。
sapply(a, function(df){
sum(is.na(df) ==TRUE)/length(df);
})
ATP Location Tournament Date Year Series
0.000000000 0.000000000 0.000000000 0.000000000 0.000000000 0.000000000
Court Surface Round Best.of Winner Loser
0.000000000 0.000000000 0.000000000 0.000000000 0.000000000 0.000000000
WRank LRank W1 L1 W2 L2
0.000000000 0.002072109 0.053046001 0.054289266 0.004144219 0.004144219
W3 L3 W4 L4 W5 L5
0.013675922 0.013675922 0.143389971 0.143389971 0.596767509 0.596767509
Wsets Lsets Comment
0.598425197 0.598425197 0.000000000
我们通过 install . packages(“Amelia”)将 Amelia 包安装到控制台中,以帮助绘制可视化缺失值的地图。从该图中,我们观察到从以下各列中检测到缺失值:
- Lsets —失败者集合
- Wsets —获胜者集合
- W5——第五盘的获胜者
- 第五盘输了
- l4——第四盘输了
- W4——第四盘的赢家

Plot of the percentage of missing values
为什么我没有删除所有丢失的值?
我试过了,但是最后一行代码帮助我创建了一个向量来删除 NAs,但是它也删除了我 50%以上的训练数据,所以它没有帮助!
view(a)# view missing values
complete . cases(a)# view missing values
which(complete . cases(a))# view 哪个行有全行值位于
哪个(!complete.cases(a)) #查看哪一行的值为 l
na_vec na_vec < -哪个(!complete.cases(a)) #创建 NA 值的向量
移除 na 行的[-na_vec] # vector。
如何估算缺失数据?—数字变量
绘制估算数据是为了理解原始数据的分布。我遵循的估算技术来自作者 Michy Alice 的博客。这些是步骤:
# Impute missing values with "pmm" - predicted mean matching. m=5 imputed data sets is defaultimputed_Data <- mice(a.mis, m=5, maxit = 50, method = 'pmm', seed = 500)
summary(imputed_Data)
# inspect that missing data has been imputed
imputed_Data$imp$Lsets
# check imputed method
imputed_Data$meth# Plot the imputed data and inspect the distributionxyplot(imputed_Data,WRank ~ W1+L1+W2+L2+W3+L3+W4+L4+L5+W5+LRank,pch=18,cex=1)

探索性数据分析
ggplot 数值变量的密度图
我使用 ggplot 来检查缺失值少于 5%的数值属性,以便通过密度图进行可视化。



Lsets had > 5% of missing values.
p1 <- ggplot(a, aes(x=a$Year)) + geom_histogram() + ggtitle(" Histogram of Year")
p1
p2 <- ggplot(a, aes(x=a$WRank)) + geom_histogram()+ ggtitle(" Histogram of Winner's Ranking")
p2
p3 <- ggplot(a, aes(x=a$LRank)) + geom_histogram()+ ggtitle(" Histogram of Loser's Ranking")
p3
p4 <- ggplot(a, aes(x=a$W1)) + geom_histogram()+ ggtitle(" Histogram of Winner in the first set")
p4
p5 <- ggplot(a, aes(x=a$L1)) + geom_histogram()+ ggtitle(" Histogram of Loser in the first set")
p5
p6 <- ggplot(a, aes(x=a$W2)) + geom_histogram()+ ggtitle(" Histogram of Winner in the second set")
p6
p7 <- ggplot(a, aes(x=a$L2)) + geom_histogram()+ ggtitle(" Histogram of Loser in the second set")
p7
p8 <- ggplot(a, aes(x=a$W3)) + geom_histogram()+ ggtitle(" Histogram of Winner in the third set")
p8
p9 <- ggplot(a, aes(x=a$L3)) + geom_histogram()+ ggtitle(" Histogram of Loser in the third set")
p9
可视化已被虚拟编码或一次性编码的分类变量
p16 <- plot(x = a$Comment,
main = "Distribution of Comment", xlab = "Comment",
ylab = "count")
p16
p17 <- plot(x= a$Winner,main = "Distribution of Winner", xlab = "Winner",
ylab = "count")
p17
p18 <- plot( x = a$Loser, main = "Distribution of Loser", xlab = "Loser",
ylab = "Count")
p18
p19 <- plot( x = a$Best.of, main = "Distribution of Best.of", xlab = "Best Of",
ylab = "Count")
p19
p20 <- plot( x = a$Round, main = "Distribution of Tennis Round", xlab = "Round",
ylab = "Count")
p20
这是估算数字数据的密度图:
密度图(估算数据)
关键:洋红色(估算数据)和蓝色(观察数据)

Density plot of the imputed data containing numeric variables
# View the data as individual points
stripplot(imputed_Data, pch = 20, cex = 1.2)

Stripplot of the individual imputed variables containing numeric variables
这个预处理和 EDA 的 R 脚本在 Github 上分享。
R Markdown 脚本也保存在 Github 中,并且可以通过命令 Knit to html 查看为 Html 文件,这在网络浏览器上对所有数据属性的绘图特别有用。
**给自己的提示:
1。使用 R Markdown 时,根据下面的截图,在 下面插入 R 代码,确切地说是第 23 行:

2。其次,我将与 图表 和 图 相关的第二段 R 代码插入到``` `{r pressure,echo=FALSE}部分下面,代码插入到第 214 行。

在我们可视化了 R 中的数据之后,我们希望使用其他分析工具,比如 Tableau,来探索和可视化澳大利亚网球公开赛的数据。
快乐编码,我们将在第 3 部分继续 Tableau 的下一篇文章!
Python 中的数据清理和要素工程
为预测旧金山房价建立更好的机器学*模型

房价数据为机器学*提供了很好的介绍。任何买了房子甚至租了公寓的人都很容易理解它的特点:更大的空间,更多的房间,通常会导致更高的价格。
因此,开发一个模型应该很容易——但有时并不容易,不是因为机器学*很难,而是因为数据很乱。此外,在同一城市的不同社区,即使只有一英里远,完全相同的房子也可能有明显不同的价格。处理这种情况的最佳方法是设计数据,以便模型可以更好地处理这种情况。
由于寻找数据可能是机器学*中最困难的问题,我们将使用 Github 上另一个数据科学项目的一个很好的样本集,这是旧金山的一组房价,主要是在过去几年里,从旧金山纪事报房屋销售清单中刮出来的。这个数据集可以在这里找到:https://github . com/Ruichang 123/Regression _ for _ house _ price _ estimation/blob/master/final _ data . CSV
首先,我们将从文件的本地副本加载数据。
**import** **pandas** **as** **pd** housing = pd.read_csv("final_data.csv")
现在,让我们来看一下这个数据集的一些图表,这些图表按照最后的销售价格绘制了房间总数。
**import** **matplotlib.pyplot** **as** **plt** x = housing['totalrooms']
y = housing['lastsoldprice']
plt.scatter(x,y)
plt.show()

右下角的那个点是异常值。它的值如此极端,以至于扭曲了整个图表,以至于我们甚至看不到主数据集的任何变化。这将扭曲任何在这个数据集上训练机器学*算法的尝试。我们需要更仔细地观察这个数据点,并考虑如何处理它。如果我们按照房间总数对数据进行排序,这是我们上面的一个轴,应该会很突出。
housing['totalrooms'].sort_values()
结果如下:
7524 1.0
11223 1.0
3579 1.0
2132 1.0
5453 1.0
2827 1.0
...2765 23.0
8288 24.0
9201 24.0
6860 24.0
4802 26.0
8087 26.0
11083 27.0
2601 28.0
2750 28.0
10727 28.0
11175 33.0
8300 94.0
8967 1264.0
Name: totalrooms, Length: 11330, dtype: float64
事实上,这个数据点确实很突出。这是列表中的最后一个值,这是一个有 1264 个房间的房子!这是非常可疑的,特别是因为情节显示它有一个相当低的价格。至少它与其他数据大相径庭。显示 94 个房间的前一个值可能也是这种情况。我们可以用下面的命令仔细看看这两栋房子,通过它们的数字标识符把它们拉出来。
首先让我们来看看这座据称有 1264 个房间的房子:
df = pd.DataFrame(housing)
df.iloc[[8967]]

这个查询显示出更可疑的东西,即“finishedsqft”字段是也是 1264.0 。换句话说,这显然只是一个错误,可能是数据输入上的错误——当创建原始数据集时,有人意外地对 finishedsqft 和 totalrooms 使用了相同的值。
现在,让我们看看它前面的值,有 94 个房间:
df.iloc[[8300]]

这个据称有 94.0 个房间的家只有两个卧室和两个浴室!同样,这是一个错误。不清楚这是怎么进来的,但我们可以非常肯定,如果我们去这所房子,它没有 94 个房间,只有两个卧室和两个浴室。我们需要消除这两个数据点,但首先让我们再来看一张完成的图表:
x = housing['finishedsqft']
y = housing['lastsoldprice']
plt.scatter(x,y)
plt.show()

右下角还有一个异常值。让我们仔细看看这些数据:
housing['finishedsqft'].sort_values()
这是结果
1618 1.0
3405 1.0
10652 1.0
954 1.0
11136 1.0
5103 1.0
916 1.0
10967 1.0
7383 1.0
1465 1.0
8134 243.0
7300 244.0
...
9650 9699.0
8087 10000.0
2750 10236.0
4997 27275.0
Name: finishedsqft, Length: 11330, dtype: float64
第一,出乎意料,有十套房子挂牌 1.0 平方英尺。这显然是错误的。请注意,这些不可能在图表中看到,我们必须查看实际值。此外,上述结果显示最大的房子为 27,275.0 平方英尺。事实证明,这是一栋只有 2.0 间卧室和 2.0 间浴室的房子,尽管它的上市面积为 27,275 平方英尺,所以这几乎肯定是一个错误,或者至少是一个极端的异常值。让我们排除所有这些异常值,再看一下图表。
housing = housing.drop([1618])
housing = housing.drop([3405])
housing = housing.drop([10652])
housing = housing.drop([954])
housing = housing.drop([11136])
housing = housing.drop([5103])
housing = housing.drop([916])
housing = housing.drop([10967])
housing = housing.drop([7383])
housing = housing.drop([1465])
housing = housing.drop([8967])
housing = housing.drop([8300])
housing = housing.drop([4997])
x = housing['finishedsqft']
y = housing['lastsoldprice']
plt.scatter(x,y)
plt.show()

这看起来好多了。这里仍然可能有一些异常值,如果我们真的想的话,我们可以更仔细地调查它们,但在这个视图中没有任何单个数据点会扭曲图表,可能也没有(我们可以看到的)会扭曲机器学*模型。
现在我们已经清理了数据,我们需要做一些特性工程。这包括将数据集中的值转换为机器学*算法可以使用的数值。
以“lastsolddate”值为例。在当前数据集中,这是一个“mm/dd/yyyy”形式的字符串我们需要将它改为一个数值,这可以通过下面的 Pandas 命令来实现:
housing['lastsolddateint'] = pd.to_datetime(housing['lastsolddate'], format='%m/**%d**/%Y').astype('int')
housing['lastsolddateint'] = housing['lastsolddateint']/1000000000
housing = housing[housing['lastsolddateint'].notnull()]
现在让我们为我们的数据创建一个检查点,以便我们以后可以引用它。
clean_data = housing.copy()
此外,还有一些我们不能或不应该使用的字段,因此我们将删除它们。
我更喜欢创建函数来做这种我们可能会反复做的工作,正如我们将在下面看到的,以便在我们尝试不同的假设时简化代码。
出于多种原因,我们删除了 remove_list 中的列。其中一些是我们无法处理的文本值(info,address,z_address,zipcode,zpid)。纬度和经度字段在某种形式上可能是有用的,但是对于这个例子来说,它可能会使事情变得复杂——但是没有理由不在将来尝试它。zestimate 和 zindexvalue 字段实际上是由其他数据科学技术(可能来自 Zillow)生成的,因此使用它们就是欺骗!最后,我们将删除 usecode(例如,house,condo,mobile home ),它可能非常有用,但我们不会在本例中使用它。
**def** drop_geog(data, keep = []):
remove_list = ['info','address','z_address','longitude','latitude','neighborhood','lastsolddate','zipcode','zpid','usecode', 'zestimate','zindexvalue']
**for** k **in** keep:
remove_list.remove(k)
data = data.drop(remove_list, axis=1)
data = data.drop(data.columns[data.columns.str.contains('unnamed',case = **False**)],axis = 1)
**return** data housing = drop_geog(housing)
现在我们已经清理了数据,让我们看看一些算法是如何管理使用它的。我们将使用 scikit-learn。
首先,我们需要将数据分成测试集和训练集,再次使用我们以后可以重用的函数。这确保了当我们测试数据时,我们实际上是在用以前从未见过的数据测试模型。
**from** **sklearn.model_selection** **import** train_test_split
**def** split_data(data):
y = data['lastsoldprice']
X = data.drop('lastsoldprice', axis=1)
*# Return (X_train, X_test, y_train, y_test)*
**return** train_test_split(X, y, test_size=0.2, random_state=30)housing_split = split_data(housing)
我们先试试线性回归。
**import** **sys**
**from** **math** **import** sqrt
**from** **sklearn.metrics** **import** mean_squared_error, mean_absolute_error, r2_score
**from** **sklearn.model_selection** **import** GridSearchCV
**import** **numpy** **as** **np**
**from** **sklearn.linear_model** **import** LinearRegression
**def** train_eval(algorithm, grid_params, X_train, X_test, y_train, y_test):
regression_model = GridSearchCV(algorithm, grid_params, cv=5, n_jobs=-1, verbose=1)
regression_model.fit(X_train, y_train)
y_pred = regression_model.predict(X_test)
print("R2: **\t**", r2_score(y_test, y_pred))
print("RMSE: **\t**", sqrt(mean_squared_error(y_test, y_pred)))
print("MAE: **\t**", mean_absolute_error(y_test, y_pred))
**return** regression_model
train_eval(LinearRegression(), {}, *housing_split)
此train_eval函数可用于任何任意的 scikit-learn 算法,用于训练和评估。这是 scikit-learn 的一大好处。函数的第一行包含一组我们想要评估的超参数。在这种情况下,我们传入{},这样我们就可以在模型上使用默认的超参数。该函数的第二行和第三行执行实际工作,拟合模型,然后对其运行预测。然后打印报表显示一些我们可以评估的统计数据。让我们看看我们如何公平。
R2: 0.5366066917131977
RMSE: 750678.476479495
MAE: 433245.6519384096
第一个得分 R 也称为决定系数,是对模型的一般评估,它显示了可以由特征解释的预测中的变化百分比。一般来说,R 值越高越好。另外两个统计是均方根误差和平均绝对误差。这两个只能与其他模型上相同统计的其他评估相关联地进行评估。话虽如此,一个 0.53 的 R,和其他几十万的统计数据(对于可能价值一两百万的房子来说)并不是很好。我们可以做得更好。
让我们看看其他几个算法的表现。首先,K-最*邻(KNN)。
**from** **sklearn.neighbors** **import** KNeighborsRegressor
knn_params = {'n_neighbors' : [1, 5, 10, 20, 30, 50, 75, 100, 200, 500]}
model = train_eval(KNeighborsRegressor(), knn_params, *housing_split)
如果线性回归是平庸的,KNN 是可怕的!
R2: 0.15060023694456648
RMSE: 1016330.95341843
MAE: 540260.1489399293
接下来我们将尝试决策树。
**from** **sklearn.tree** **import** DecisionTreeRegressor
tree_params = {}
train_eval(DecisionTreeRegressor(), tree_params, *housing_split)
这就更惨了!
R2: .09635601667334437
RMSE: 1048281.1237086286
MAE: 479376.222614841
最后,我们来看看《随机阿甘正传》。
**from** **sklearn** **import** ensemble
**from** **sklearn.ensemble** **import** RandomForestRegressor
**from** **sklearn.datasets** **import** make_regression
forest_params = {'n_estimators': [1000], 'max_depth': [**None**], 'min_samples_split': [2]}
forest = train_eval(RandomForestRegressor(), forest_params, *housing_split)
这个好一点,但我们还可以做得更好。
R2: 0.6071295620858653
RMSE: 691200.04921061
MAE: 367126.8614028794
我们如何改进这些结果?一种选择是尝试其他算法,也有很多,有些会做得更好。但是我们实际上可以通过使用特征工程的数据来微调我们的结果。

让我们重新考虑一下数据中的一些特征。邻居是一个有趣的领域。这些值类似于“波特雷罗山”和“南海滩”这些不能简单地排序(从最贵到最便宜的邻域),或者至少,这样做不一定会产生更好的结果。但是我们都知道,两个不同小区的同一个房子,会有两个不同的价格。所以我们想要这些数据。我们如何使用它?
Python 的 Pandas 库为我们提供了一个简单的工具来创建这些值的“一次性编码”。这将采用“neighborhood”这一列,并为原始“neighborhood”列中的每个值创建一个新列。对于这些新行中的每一行(具有新的列标题名称,如“Portrero Hill”和“South Beach”),如果一行数据在原始列中具有该邻域的值,则它被设置为 1,否则它被设置为 0。机器学*算法现在可以构建与该邻域相关联的权重,如果数据点在该邻域中(如果该列的值为 1),则应用该权重,否则不应用该权重(如果该列的值为 0)。
首先,我们需要检索我们的检查点数据,这次保留“neighborhood”字段。
housing_cleaned = drop_geog(clean_data.copy(), ['neighborhood'])
现在我们可以为“neighborhood”字段创建一个一次性编码。
one_hot = pd.get_dummies(housing_cleaned['neighborhood'])
housing_cleaned = housing_cleaned.drop('neighborhood',axis = 1)
我们将保留“one_hot”值,稍后再添加它。但首先,我们必须做两件事。我们需要将数据分成训练集和测试集。
(X_train, X_test, y_train, y_test) = split_data(housing_cleaned)
最后一步,我们需要对数据进行缩放和居中。
**from** **sklearn.preprocessing** **import** StandardScalerscaler = StandardScaler()
scaler.fit(X_train)
X_train[X_train.columns] = scaler.transform(X_train[X_train.columns])
X_train = X_train.join(one_hot)
X_test[X_test.columns] = scaler.transform(X_test[X_test.columns])
X_test = X_test.join(one_hot)
housing_split_cleaned = (X_train, X_test, y_train, y_test)
让我们稍微解释一下这个步骤。
首先,我们应用 StandardScaler()。对于每列中的所有数据点,此函数通过减去列的平均值并除以列的标准偏差来缩放和居中数据。这将所有数据标准化,使每一列呈正态分布。它还会缩放数据,因为有些字段会在 0 到 10,000 之间变化,如“finishedsqft”,而其他字段只会在 0 到 30 之间变化,如房间数。缩放将把它们都放在相同的比例上,这样一个特性就不会仅仅因为它具有较高的最大值而任意地扮演比其他特性更大的角色。对于一些机器学*算法,正如我们将在下面看到的,这对于获得哪怕是半个像样的结果都是至关重要的。
其次,需要注意的是,我们必须在训练特性 X_train 上“安装”缩放器。也就是说,我们获取训练数据的平均值和标准偏差,用这些值拟合 scaler 对象,然后使用拟合的 scaler 转换训练数据和测试数据。我们不希望在测试数据上安装缩放器,因为那样会将测试数据集中的信息泄露给训练好的算法。我们可能最终得到看起来比实际情况更好的结果(因为算法已经在测试数据上进行了训练)或看起来更差的结果(因为测试数据是根据它们自己的数据集进行缩放的,而不是根据测试数据集)。
现在,让我们用新设计的功能重建我们的模型。
model = train_eval(LinearRegression(), {}, *housing_split_cleaned)
现在,在线性回归下,我们拥有的最简单的算法,结果已经比我们之前看到的任何结果都要好。
R2: 0.6328566983301503
RMSE: 668185.25771193
MAE: 371451.9425795053
下一个是 KNN。
model = train_eval(KNeighborsRegressor(), knn_params, *housing_split_cleaned)
这是一个巨大的进步。
R2: 0.6938710004544473
RMSE: 610142.5615480896
MAE: 303699.6739399293
决策树:
model = train_eval(DecisionTreeRegressor(), tree_params,*housing_split_cleaned)
还是很糟糕,但比以前好了。
R2: 0.39542277744197274
RMSE: 857442.439825675
MAE: 383743.4403710247
最后,随机福里斯特。
model = train_eval(RandomForestRegressor(), forest_params, *housing_split_cleaned)
这又是一个不错的进步。
R2: 0.677028227379022
RMSE: 626702.4153226872
MAE: 294772.5044353021
从额外的特征工程到尝试额外的算法,可以利用这些数据做更多的事情。但是,从这个简短的教程中得到的教训是,寻求更多的数据或在文献中寻找更好的算法并不总是正确的下一步。首先从一个更简单的算法中获取尽可能多的数据可能更好,这不仅是为了比较,也是因为数据清理可能会在未来带来回报。
最后,尽管它很简单,K-最*邻可以是相当有效的,只要我们用适当的方式对待它。
使用 Python 和 Pandas 库进行数据清理
就数据科学而言,数据清理和组织占总权重的 57%

整个数据清理过程分为如下所示的子任务。
- 导入所需的库。
- 从不同的源(Kaggle)获取数据集并显示数据集。
- 删除不使用或不相关的列。
- 根据我们的方便重命名列名。
- 替换行的值并使其更有意义。
尽管这个教程很小,但它是一个从小事开始,以后再动手做的好方法。我将确保所有没有 python 编程经验或者不知道什么是数据科学或数据清理的人都能很容易地理解本教程。我一开始并不了解 python,所以即使对我来说,这也是一个很好的起点。python 的一个特点是代码是不言自明的,你的关注点不应该是代码做了什么,因为代码很大程度上说明了它做了什么,而应该告诉你为什么选择这样做,这个" why 因素比" what 因素更重要。而且完整的源代码可以在我的 GitHub 上找到。
步骤 1:导入所需的库。
这一步包括导入所需的库,它们是熊猫、 numpy 和 CSV 。谈到数据科学,这些是必要的库。

步骤 2:从不同的来源获取数据集并显示数据集。
这一步包括从不同的来源获取数据集,数据集的链接如下。

注意:如果你使用 Jupyter 笔记本 来练*本教程,那么阅读 CSV 文件应该没有问题。但是如果你像我一样是一个谷歌迷,那么你应该使用 谷歌 Colab 这是我认为最好的,用于实践数据科学,然后你必须遵循一些步骤来加载或读取 CSV 文件。所以这篇文章帮你解决这个问题。我个人推荐大家通读这篇文章。我按照第二步阅读了那篇文章中的 CSV 文件。选择最好的一个,开始工作。
步骤 3:删除未使用或不相关的列
这一步包括删除不相关的列,如 cp、fbs、thalach 等等,代码基本上是不言自明的。

步骤 4:为了方便起见,重命名列名。
这一步包括重命名列名,因为许多列名有点混乱,难以理解。

步骤 5:如有必要,替换行的值。
这一步包括替换不完整的值或使值更具可读性,例如这里的性别字段由值 1 和 0 组成,其中 1 为男性,而 0 为女性,但对于第三人来说,这通常看起来不明确,因此将值更改为可理解的值是一个好主意。

以上是总体简单的数据清理过程,显然这不是行业级别的实际清理过程,但这是一个良好的开端,所以让我们从小数据集开始,然后再处理大型数据集,这将涉及更多的清理过程。这只是从初学者的角度给出了数据清理过程的一个概念。感谢你们花时间阅读我的文章,请关注更多更新。请在下面的评论区告诉我你对这个教程的看法。另外,如果你对代码有任何疑问,评论区都是你的。祝你有愉快的一天。
用 R 和 Tidyverse 清理数据:检测缺失值

作为一名数据科学家,你可能会花费 80%的时间清理数据。
在之前的一篇文章中,我使用 Python 和 Pandas 库完成了许多数据清理任务。
这篇文章引起了如此多的关注,我想在 r。
在这篇文章中,你将学*如何使用来自 Tidyverse 的 tidyr 和 dplyr 包来检测缺失值。
Tidyverse 是数据科学的最佳 R 包集合,所以您应该熟悉它。

入门指南
开始任何数据科学项目的一个好方法就是感受数据。
这只是快速查看变量名和预期的变量类型。查看数据的维度也很有用。
探索性数据分析(EDA)极其重要,因此它值得拥有自己的博文。在本文中,我们不会讨论完整的 EDA。
在我们开始之前,先去我们的 github 页面获取一份数据。确保将副本放在 R 代码所在的工作目录中。
下面快速浏览一下我们的数据:

这是一个小型的客户流失数据集。
为了便于学*,该数据集展示了一些丢失值的真实示例。
首先,加载 tidverse 库并读入 csv 文件。
library(tidyverse)# set working directory
path_loc <- "C:/Users/Jonathan/Desktop/data cleaning with R post"
setwd(path_loc)# reading in the data
df <- read_csv("telecom.csv")
通常数据被读入 dataframe,但是 tidyverse 实际上使用了tibles。
这些类似于数据帧,但也略有不同。要了解更多关于 tibbles 的信息,请查看数据科学的 R 中的这一章。
我喜欢使用glimpse函数来查看变量名和类型。
# taking a quick look
glimpse(df)> glimpse(df)
Observations: 10
Variables: 5
$ customerID chr "7590-VHVEG", "5575-GNVDE", "3668-QPYBK", "7...
$ MonthlyCharges dbl 29.85, 56.95, NA, 42.30, 70.70, NaN, 89.10, ...
$ TotalCharges chr "109.9", "na", "108.15", "1840.75", NA, "820...
$ PaymentMethod chr "Electronic check", "Mailed check", "--", "B...
$ Churn chr "yes", "yes", "yes", "no", "no", "yes", "no"...
我们可以看到有 5 个变量。
customerIDMonthlyChargesTotalChargesPaymentMethodChurn
还有对每个变量类型的描述:
customerID: chr代表字符,字符串的别称MonthlyCharges: dbl代表 double,是一种数值类型TotalCharges: chr性格PaymentMethod: chr人物Churn: chr人物
有 10 个观察值,这意味着有 10 行数据。
现在我们已经快速地熟悉了数据,让我们回顾一些基本的数据操作。
数据操作的语法:dplyr
在我们开始处理缺失值之前,让我们先来看一下 dplyr 库。
这只是一个快速介绍,所以请务必查看官方的 dplyr 文档以及来自数据科学的 R 的第 5 章数据转换。
这个库使用了“数据操作语法”,这基本上意味着有一组带有逻辑动词名称的函数来完成你想做的事情。
例如,也许您想只查看顾客。您可以过滤流失率值等于“是”的数据。
我们可以使用 dplyr 中的filter函数快速完成这项工作。
# filter on customers that churned
df %>%
filter(Churn=="yes") # A tibble: 5 x 5
customerID MonthlyCharges TotalCharges PaymentMethod Churn
chr dbl chr chr chr
1 7590-VHVEG 29.8 109.9 Electronic check yes
2 5575-GNVDE 57.0 na Mailed check yes
3 3668-QPYBK NA 108.15 -- yes
4 9305-CDSKC NaN 820.5 -- yes
5 6713-OKOMC NA N/A NA yes
看一看,我们可以看到 R 返回了一个有组织的 tibble,其中只包含了客户。
如果你不熟悉%>%操作员,也被称为“管道操作员”,看看这篇的博文。
管道是 magritter 包中的一个有用的操作符。它允许我们通过消除嵌套括号来组织我们的代码,从而使我们的代码更具可读性。
例如,假设我们有以下计算:
# nested functions
log(sin(exp(2))) > log(sin(exp(2)))
[1] -0.1122118
由于所有的括号,这不是很可读。现在让我们看一个管道示例。
# piped functions
2 %>% exp() %>%
sin() %>%
log()
显而易见,管道示例可读性更好。
好了,回到 dplyr。
我们只是使用了 filter 函数来快速过滤掉流失值等于“yes”的行。
也许我们也想只选择customerID和TotalCharges列。我们也可以使用select功能快速完成这项工作。
# filter on customers that churned,
# select customerID and TotalCharges columns
df %>%
filter(Churn=="yes") %>%
select(customerID, TotalCharges) # A tibble: 5 x 2
customerID TotalCharges
chr chr
1 7590-VHVEG 109.9
2 5575-GNVDE na
3 3668-QPYBK 108.15
4 9305-CDSKC 820.5
5 6713-OKOMC N/A
我们可以看到使用这些 dplyr 函数来操作我们的数据是多么容易。
垂直地将函数链接在一起使得我们的代码非常易读。
这种编码方式一开始可能看起来有点奇怪,但是经过一点实践之后,它会变得非常有用。
标准缺失值
现在我们对管道操作符和 dplyr 稍微熟悉了一些,让我们开始检测丢失的值。
我们将从 R 识别的标准缺失值开始。
去看看MonthlyCharges栏吧。
我们可以看到有三个值丢失了。

有两个空牢房,一个写着“南”。这些显然是缺失的价值观。
我们可以看到 R 如何使用is.na函数来识别这些。
首先让我们打印出该列,然后应用is.na。
# looking at MonthlyCharges
df$MonthlyCharges
is.na(df$MonthlyCharges) > df$MonthlyCharges
[1] 29.85 56.95 NA 42.30 70.70 NaN 89.10 NA 104.80
[10] 54.10
> is.na(df$MonthlyCharges)
[1] FALSE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE FALSE
我们可以看到,两个缺失的单元格被识别为“NA”,另一个带有 NaN 的缺失值被 R 识别为“Nan”。
当我们运行is.na函数时,R 识别两种类型的缺失值。我们可以看到这一点,因为当我们运行is.na时,会返回三个TRUE值。
注意“那”和“南”的区别是很重要的。我们可以使用帮助功能来仔细查看这两个值。
# using the help function to learn about NA
help(NA)

看一下右下方的窗口,我们可以看到“NA”或“不可用”用于表示缺失值。
“NaN”或“非数字”用于数值计算。如果一个值是未定义的,比如 0/0,“NaN”是表示它的合适方式。

还有一个is.nan功能。试着用“NA”和“NaN”来运行它。您将看到它为“NaN”返回 TRUE 值,但为“NA”返回 FALSE 值。
另一方面,is.na函数更加通用,因此它将检测两种类型的缺失值。
让我们继续使用 dplyr 来稍微总结一下我们的数据。
我们可以使用distinct函数来查看显示在MonthlyCharges列中的不同值。
# looking at the distinct values
df %>%
distinct(MonthlyCharges) # A tibble: 9 x 1
MonthlyCharges
dbl
1 29.8
2 57.0
3 NA
4 42.3
5 70.7
6 NaN
7 89.1
8 105.
9 54.1
我们可以看到有 9 个不同的值。有 10 行数据,但是“NA”出现了两次,所以有 9 个不同的值。
如果我们想快速计算不同的值,我们可以使用summarise函数。
# counting unique values
df %>%
summarise(n = n_distinct(MonthlyCharges)) # A tibble: 1 x 1
n
int
1 9
这将返回一个简单的 tibble,其中有一列我们命名为“n ”,用于计算MonthlyCharges列中不同值的数量。
我们真正追求的是丢失值的计数。我们可以使用summarise函数和is.na来计算缺失值。
# counting missing values
df %>%
summarise(count = sum(is.na(MonthlyCharges)))# A tibble: 1 x 1
count
int
1 3
正如我们在上面看到的,缺失值的数量是 3。
也许我们想同时做多件事。假设我们想要得到唯一值、缺失值以及MonthlyCharges的中值的计数。
下面是我们如何使用summarise实现这一点:
# counting unique, missing, and median values
df %>% summarise(n = n_distinct(MonthlyCharges),
na = sum(is.na(MonthlyCharges)),
med = median(MonthlyCharges, na.rm = TRUE)) # A tibble: 1 x 3
n na med
int int dbl
1 9 3 57.0
这产生了我们的汇总数据的一个有组织的小 tibble。
既然我们已经确定了缺失值,让我们用MonthlyCharges的中值来替换它们。为此,我们可以使用 dplyr 的mutate函数。
# mutate missing values
df %>%
mutate(MonthlyCharges
= replace(MonthlyCharges,
is.na(MonthlyCharges),
median(MonthlyCharges, na.rm = TRUE))) # A tibble: 10 x 5
customerID MonthlyCharges TotalCharges PaymentMethod Churn
chr dbl chr chr chr
1 7590-VHVEG 29.8 109.9 Electronic check yes
2 5575-GNVDE 57.0 na Mailed check yes
3 3668-QPYBK 57.0 108.15 -- yes
4 7795-CFOCW 42.3 1840.75 Bank transfer no
5 9237-HQITU 70.7 NA Electronic check no
6 9305-CDSKC 57.0 820.5 -- yes
7 1452-KIOVK 89.1 1949.4 Credit card no
8 6713-OKOMC 57.0 N/A NA yes
9 7892-POOKP 105\. 3046.05 Electronic check no
10 8451-AJOMK 54.1 354.95 Electronic check no
我们可以看到,在三个不同的点上,缺失值被替换为中值 57。
只是为了再次检查这是否有效,让我们再次打印出整个 tibble。
df# A tibble: 10 x 5
customerID MonthlyCharges TotalCharges PaymentMethod Churn
chr dbl chr chr chr
1 7590-VHVEG 29.8 109.9 Electronic check yes
2 5575-GNVDE 57.0 na Mailed check yes
3 3668-QPYBK NA 108.15 -- yes
4 7795-CFOCW 42.3 1840.75 Bank transfer no
5 9237-HQITU 70.7 NA Electronic check no
6 9305-CDSKC NaN 820.5 -- yes
7 1452-KIOVK 89.1 1949.4 Credit card no
8 6713-OKOMC NA N/A NA yes
9 7892-POOKP 105\. 3046.05 Electronic check no
10 8451-AJOMK 54.1 354.95 Electronic check no
看起来所有丢失的值都回来了。发生了什么事?
这引出了重要的一点。dplyr 包不会就地修改数据。
基本上,这意味着如果我们用一个管道操作符将一个mutate应用于一些数据,它将向我们显示数据的一个修改视图,但这不是一个永久的修改。
为了永久地修改数据,我们需要使用赋值操作符<-将 mutate 赋值给原始数据。
我们会这样做:
# mutate missing values, and modify the dataframe
df <- df %>%
mutate(MonthlyCharges = replace(MonthlyCharges,
is.na(MonthlyCharges),
median(MonthlyCharges, na.rm = TRUE)))
现在,如果我们再看一下数据,它应该会被修改。
df # A tibble: 10 x 5
customerID MonthlyCharges TotalCharges PaymentMethod Churn
chr dbl chr chr chr
1 7590-VHVEG 29.8 109.9 Electronic check yes
2 5575-GNVDE 57.0 na Mailed check yes
3 3668-QPYBK 57.0 108.15 -- yes
4 7795-CFOCW 42.3 1840.75 Bank transfer no
5 9237-HQITU 70.7 NA Electronic check no
6 9305-CDSKC 57.0 820.5 -- yes
7 1452-KIOVK 89.1 1949.4 Credit card no
8 6713-OKOMC 57.0 N/A NA yes
9 7892-POOKP 105\. 3046.05 Electronic check no
10 8451-AJOMK 54.1 354.95 Electronic check no
这次MonthlyCharges列被永久修改。请记住,当您想要使用 dplyr 永久地变更您的数据时,您需要将 mutate 分配给原始数据。
非标准缺失值
很多时候,你不会幸运地拥有 R 能马上识别的所有标准缺失值类型。
让我们快速浏览下一篇专栏文章TotalCharges,了解我的意思。

我们可以看到有三个不同的缺失值,“na”、“NA”和“N/A”。
在前面的例子中我们看到 R 将“na”识别为缺失值,但是“NA”和“N/A”呢?
让我们看看这个列,并使用is.na来看看 R 是否将所有这些都识别为缺失值。
# looking at missing values
df$TotalCharges
is.na(df$TotalCharges) > is.na(df$TotalCharges)
[1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
查看结果,我们可以看到 R 只将“NA”标识为缺失值。
让我们使用summarise函数来看看 R 找到了多少个丢失的值。
# counting missing values
df %>%
summarise(count = sum(is.na(TotalCharges)))# A tibble: 1 x 1
count
int
1 1
结果证实 R 只找到一个丢失的值。
我们需要用“na”替换“NA”和“N/A ”,以确保 R 将所有这些都识别为缺失值。
让我们使用mutate函数将这些替换为正确的缺失值类型。请记住,我们需要使用赋值操作符来确保更改是永久性的。
# replacing with standard missing value type, NA
df <- df %>%
mutate(TotalCharges = replace(TotalCharges, TotalCharges == "na", NA)) %>%
mutate(TotalCharges = replace(TotalCharges, TotalCharges == "N/A", NA))
如果我们再看一下这一列,我们可以看到所有缺失的值都被 r 正确地识别出来了。
# taking another look
df$TotalCharges
is.na(df$TotalCharges)> df$TotalCharges
[1] "109.9" NA "108.15" "1840.75" NA "820.5"
[7] "1949.4" NA "3046.05" "354.95"
> is.na(df$TotalCharges)
[1] FALSE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE
现在我们可以看到 R 选择了所有三个丢失的值。
在我们替换丢失的值之前,还有另一个问题。
r 认为列值是字符。我们可以用glimpse函数来确认这一点。
> glimpse(df$TotalCharges)
chr [1:10] "109.9" NA "108.15" "1840.75" NA "820.5" "1949.4" NA ...
让我们把它们改成数字类型。
# changing to numeric type
df$TotalCharges <- as.numeric(df$TotalCharges)
glimpse(df$TotalCharges) > df$TotalCharges <- as.numeric(df$TotalCharges)
> glimpse(df$TotalCharges)
num [1:10] 110 NA 108 1841 NA ...
最后,让我们用中间值替换缺失值。
# replace missing values with median
df <- df %>%
mutate(TotalCharges = replace(TotalCharges,
is.na(TotalCharges),
median(TotalCharges, na.rm = T)))
df$TotalCharges> df$TotalCharges
[1] 109.90 820.50 108.15 1840.75 820.50 820.50 1949.40 820.50
[9] 3046.05 354.95
更改所有缺失值的一个更简单的方法是在执行任何操作之前将列更改为 numeric。
让我们再次导入数据,这样我们就又有丢失的值了。
# importing the data again
df <- read_csv("telecom.csv")
df$TotalCharges> df$TotalCharges
[1] "109.9" "na" "108.15" "1840.75" NA "820.5"
[7] "1949.4" "N/A" "3046.05" "354.95"
现在让我们试着将该列改为数字。
# change TotalCharges to numeric type
df$TotalCharges <- as.numeric(df$TotalCharges)
df$TotalCharges> df$TotalCharges <- as.numeric(df$TotalCharges)
Warning message:
NAs introduced by coercion
> df$TotalCharges
[1] 109.90 NA 108.15 1840.75 NA 820.50 1949.40 NA
[9] 3046.05 354.95
这一次,所有不同的缺失值类型都被自动更改。
虽然这有点短,但我并不总是喜欢这种解决方案。
这对于我们的特定示例是有效的,但是如果您试图检测异常或其他脏数据,这可能不是一个好的解决方案。
请务必阅读 R 控制台上类似这样的警告。它可以提供有价值的信息。
更多缺失值
到目前为止,我们已经查看了像“NA”这样的标准缺失值和像“n/a”和“N/A”这样的非标准值。
还有许多其他方法来表示丢失的数据。
也许我是手动输入数据,并选择使用“—”来表示缺少的值。
另一方面,也许你更喜欢将单元格留空。
让我们了解一下如何检测这些更不常见的缺失值类型。
看一看PaymentMethod栏目:

我们可以看到有三个值丢失了。
两个用“—”表示,一个只是一个空单元格。
看看 R 对这些怎么看:
# looking at PaymentMethod
df$PaymentMethod
is.na(df$PaymentMethod) > is.na(df$PaymentMethod)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
r 只是为了识别一个丢失的值,即空单元格。
让我们继续使用 mutate 将“—”改为 NA。
# replacing "--" with NA
df <- df %>%
mutate(PaymentMethod = replace(PaymentMethod, PaymentMethod == "--", NA))
is.na(df$PaymentMethod)
df$PaymentMethod> df$PaymentMethod
[1] "Electronic check" "Mailed check" NA
[4] "Bank transfer" "Electronic check" NA
[7] "Credit card" NA "Electronic check"
[10] "Electronic check"
现在我们可以看到所有三个缺失的值都显示出来了。
到目前为止,我们要么不考虑缺失值,要么用中间值代替它们。
如何处理字符类型列中的缺失值?
因为PaymentMethod列中的所有条目都是字符串,所以没有中间值。
让我们将 NAs 转换为一个新的类别,称为“不可用”,而不仅仅是排除丢失的值。
# replace NA with "unavailable"
df <- df %>%
mutate(PaymentMethod = replace(PaymentMethod, is.na(PaymentMethod), "unavailable"))df$PaymentMethod> df$PaymentMethod
[1] "Electronic check" "Mailed check" "unavailable"
[4] "Bank transfer" "Electronic check" "unavailable"
[7] "Credit card" "unavailable" "Electronic check"
[10] "Electronic check"
现在我们可以看到,我们的三个缺失值 NA 已经被转换为一个新的类别“不可用”。
有时值丢失是有原因的,所以保留这些信息以查看它如何影响我们的机器学*模型中的结果是很好的。
我们不会在这篇文章中讨论这些细节,但是请记住,丢弃丢失的值可能并不总是一个好主意。
结论
在这篇文章中,我们了解了数据科学中最重要的技能之一数据清理。
具体来说,我们着眼于检测不同类型的缺失值。
我们还学*了如何替换数字和字符类型的缺失值。
您可能会花费高达 80%的时间来清理数据,因此这是一项非常有价值的技能。
有关使用 Python 清理数据和检测缺失值的信息,请查看这篇文章。
使用哈密顿动力学的数据聚类
平面和曲面数据的灵活聚类算法简介。
想象一下,你最终得到了一份数据科学的工作,它需要手动检查、标记和分类添加到数据集的每一个新数据。这样的工作会是一个枯燥乏味的工作!此外,今天收集的数据量超过了大西洋的水量。这不可能由一个人完成,更不用说有一大群数据科学家了。解决办法?您可能听说过数据聚类。这是将数据分组并将其分类到组和簇中的自动化过程。
一个集群可以被认为是数据的集合,这些数据彼此之间有许多相似之处,并且它们与其他集群中的对象不同。在本文中,以简化的方式解释了一种依赖于哈密顿动力学的聚类算法。在数据科学中使用哈密顿动力学并不新鲜,例如在 MCMC 采样中就使用了这种方法。在下一节中,我将非常简单地解释我所说的哈密尔顿动力学以及这种类型的聚类背后的基本思想。在(卡萨格兰德、萨萨诺、&阿斯托尔菲,2012 )中提出了使用哈密顿动力学进行数据聚类的想法。
许多聚类算法基于欧几里德距离的思想,而这里的思想是使用水平曲线来定义聚类的边界。巧合的是,这些边界是哈密顿系统的解。为了说明这一点,考虑将房屋按各自的街区分类的问题。基于距离的算法将通过找到所述房屋与邻*的其他房屋之间的平均距离来识别所述房屋的邻*区域。水平集方法类似于通过周围的街道来定义社区,并测试房子是否在社区的街道回路内。
与其他聚类算法不同,这种方法最大的优点是不需要事先知道聚类数。不仅如此,算法的几何性质和哈密顿动力学允许我们从其他几何信息中获益,如几何矩来分类和聚类,甚至建模时变聚类。遗憾的是,本文没有涉及动态集群的处理。另一个优点是,我们可以在几何对象(如球体)的表面上执行聚类,而无需求助于额外的数据预处理或转换。
哈密顿力学
首先,什么是哈密顿动力学?要回答这个问题,我们需要回到牛顿动力学。从高中物理课上,我们知道牛顿第二定律指出力与质量乘以加速度成正比,牛顿第三定律指出作用在物体上的力需要平衡。从这两条定律我们可以推导出运动方程。
如果我们考虑一个质量为 m (或一组 N 的粒子)沿着轨迹 x(t) 运动,那么根据牛顿第二定律,当施加外力 F 时,运动方程为

这描述了加速度如何随时间变化。同样,这里的轨迹是用笛卡尔坐标描述的。牛顿动力学的描述很大程度上依赖于所使用的坐标系,当试图用几何约束来模拟系统时,就像球体上的钟摆,这变得非常混乱。此外,当我们想研究一个机械系统的对称性,这样的描述成为一个负担。你可能会想,为什么我们关心对称性?它们对应于系统的守恒量,用于简化运动方程。解决牛顿力学缺点的方法是使用一种不同的公式,它考虑到了约束和对称性。
这就引出了哈密顿动力学,哈密顿动力学从哈密顿最小作用量原理推导出运动方程(与牛顿运动方程相同),这是一个极值原理。它指出(在给定的时间间隔内),动力系统在两点之间遵循的轨迹是这样的,动能和势能之间的差的时间积分最小。用数学术语来说,这意味着

被积函数 T - U 称为拉格朗日LL它的量纲是能量。这意味着积分就是作用。最小化意味着动力学采用从初始位置到终点所需能量最少的路径。此外,轨迹解欧拉-拉格朗日方程

这相当于牛顿方程。这个等式的推导超出了本文的范围,但是好奇的读者可以参考以下书籍中的一本来获得详细而严格的阐述 (Arnol'd,1978) 或者 (Abraham,& Marsden,1978) 。此时,我们需要位置和速度 (x(t),v(t)) 来计算拉格朗日量。其中速度 v(t) 是位置 x(t) 的时间导数。我们用动量代替速度,我们把拉格朗日量写成 L = p v — H(x,p) ,其中 H 是哈密顿量。在哈密尔顿公式中,轨迹求解哈密尔顿方程

它等价于欧拉-拉格朗日方程,但是它不是一个二阶时间导数的方程,我们有两个只有一阶时间导数的方程。一种类型的哈密顿动力学是所谓的保守系统,这种系统保持哈密顿量 H ,并且当将 H 视为水平函数时,轨迹定义了水平集。
该算法
第一步
我们要做的第一件事是定义哈密顿函数 H 为

其中 (x ᵢ ,p ᵢ ) for i=1,…,N 是我们采集的数据点。让我们用𝓟.来表示收集的数据集人们可以选择不同的哈密顿量,但这里我们坚持使用高斯函数,因为我们可以将其视为概率密度函数,其中 N 个模式位于 (x ᵢ ,p ᵢ ) 。
第二步
算法的第二步是选择参考值 H ᵣ,它定义了哈密顿量的水平集。从概率密度函数的观点来看,这可以看作是等密度曲线。
第三步
第三步是选择 i ᵗʰ数据点 (x ᵢ ,pᵢ)𝓟,我们找到第一条水平曲线𝓢ᵢ.这是通过求解下面的哈密尔顿方程来实现的

选择 (x ᵢ ,p ᵢ ) 作为初始条件。这里我们选择函数 f 为

函数 f 的思想是,如果轨迹偏离水平曲线,则强制轨迹落在 H ᵣ上。当我们到达一个点 (x ᵢ ,p ᵢ ) 与 H(x ᵢ ,p ᵢ ) = H ᵣ,那么我们就到达了水平曲线,哈密尔顿方程中右手边的第二项变为零。因此,汉密尔顿方程的解将保持在水平曲线上。
第四步
计算出水平曲线𝓢ᵢ后,我们进入第四步,对于𝓟的所有其他数据点,我们检查它们是否在曲线𝓢ᵢ内,这是通过检查绕组数ω (x ᵢ ,C ⱼ ) ,for j= i+1,…,N 是否由沿曲线𝓒ⱼ.的积分定义来完成的数学上是这样的

是一个正数。简而言之,缠绕次数给出了闭合曲线围绕其内部某点缠绕的次数。对于这种算法,正的缠绕数表示一个数据点属于一个集合。当绕组数ω (x ᵢ ,C ⱼ ) > 1 时,我们从集合𝓟.中删除点 (x ᵢ ,p ᵢ )
我们重复步骤 3 和 4,直到𝓟成为一个空集。该算法的结果是由水平曲线𝓢ₐ定义的聚类组,对于 a = 1,…,M ,其中 M 是聚类组的数量。
运行中的算法
在所有这些例子中,我们将数据视为 (x,p) 空间中的元素,例如,二维图像的像素是一个点 (x,p) 。第一个例子是对鸟群使用基于哈密顿的聚类,类似于(卡萨格兰德,萨萨诺,&阿斯特尔菲,2012) 中提出的例子。

Birds are grouped into three different groups using the algorithm described above.
第二个例子是聚类螺旋。有三个螺旋,我们演示了聚类算法如何识别数据点并相应地将它们分组到螺旋组中。

This is a demonstration of how using level sets of a Hamiltonian function can help group points into three distinct spirals.
第三个例子是第二个例子的扩展,它包括在一个球体上聚集一个螺旋。这利用了哈密尔顿函数的对称性,对哈密尔顿函数和哈密尔顿方程进行了修改。该算法的结果如下图所示:

This example demonstrates how Hamiltonian-based clustering can be used for applications when the surface is curved. This does not rely on transforming the points to a suitable space, the algorithm works directly on the surface of the sphere.
结论
使用哈密顿动力学不同于其他方法,它严重依赖于微积分和辛几何,这是我有意避免的,复杂的数学带来了一些不同的优势。这种方法仍处于萌芽阶段,有许多研究途径需要探索,以开发数据挖掘算法。一个未探索的领域是守恒量的作用以及它们如何潜在地用于物体识别。我计划在可预见的未来从事这些工作,所以请关注这个空间!
如果你对这里提到的任何事情有疑问,你可以通过 LinkedIn 联系我。感谢阅读!
参考
- D.卡萨格兰德、m .萨萨诺和 a .阿斯托尔菲,“基于哈密顿的聚类:数据挖掘和图像处理中的静态和动态聚类算法”,载于《IEEE 控制系统杂志》,第 32 卷,第 4 期,第 74–91 页,2012 年 8 月。
原载于 https://nadertg.github.io/MechanicsData/
数据收集可能不像看起来那么容易
深入探索数据收集流程

Photo by Milan Seitler on Unsplash
我在 GitHub 上的一些最受欢迎的存储库都是关于数据收集的,要么通过网络抓取,要么使用应用编程接口(API)。我的方法一直是找到一个可以获取数据的资源,然后直接开始获取数据。收集完数据后,只需保存,得出见解,就可以了。
但是如果你想共享数据呢?如果有人正在寻找这个数据集,但他们不知道如何着手,该怎么办?如果他们有这个数据集,但不知道每一列的含义,或者如果他们需要更多信息,不知道在哪里浏览,该怎么办?出现这些问题是因为数据共享和可用性很重要,但几乎没有人试图努力使其可复制和易于访问。
这是数据收集最佳实践产生的地方。元数据和数据一样重要,因为没有元数据,数据可能毫无用处。让我们深入探讨一下,这是什么,以及每个人都应该做些什么来使数据收集的过程正确!
从弄清楚收集什么开始

Photo by Edho Pratama on Unsplash
一如既往,第一步是寻找已经存在的数据。有人可能已经收集了您想要为他们的问题收集的类似或相同的数据。如果你发现了这样的数据,拿走它(如果他们提供的话),并在你使用该数据进行任何分析的任何时间任何地点适当地引用你的来源。就是这样!
但是,如果你没有找到你需要的数据,你必须自己收集。它可能是你从维基百科的网站上抓取的维基百科页面列表,你可能想要使用 GitHub API 为你的 GitHub 帐户获取的存储库信息,或者从传感器收集的数据。你能收集的东西几乎是无限的。
收集数据

Photo by Markus Spiske on Unsplash
无论你决定收集什么,开始收集你的数据。您可以使用 BeautifulSoup 从 HTML 页面中提取信息,根据需要使用 API 的文档访问它们,或者创建一个 Android 应用程序,从传感器中读取数据并将其保存到 CSV 文件中。
一旦你有了你想要的数据,你可能想和其他人分享你的工作。你会希望其他人理解你收集了什么,你为什么收集,也许通过适当引用你的工作来使用你的数据。然后,以其他人可以理解和使用的适当格式保存数据变得至关重要。
关于您的数据的数据—元数据
现在,我将告诉你一些我们经常使用但经常忽略的数据。是的,我说的是元数据。这些信息告诉您每一列的含义、度量单位、数据收集时间等等。
让我们用一个例子来理解元数据的重要性。UCI 机器学*库包括一长串数据集,您可以使用这些数据集进行分析和预测。让我们挑选一下兽癌数据集。这是数据集的外观:

Breast Cancer Data Set (Data)— UCI Machine Learning
仅仅通过查看数据而没有额外的信息,我们甚至无法弄清楚每一列的含义,更不用说对其进行任何分析了。但就在我展示下图中的列描述时,我们可以使用数据集、提取信息、执行探索性分析和进行预测。

Breast Cancer Data Set (Attributes) — UCI Machine Learning
这就是关于数据的信息非常重要的原因。这一重要步骤可以成就或毁灭你的数据集。
但是我们应该收集什么呢?

Photo by Phad Pichetbovornkul on Unsplash
仔细想想,您会发现有很多东西可以作为元数据收集,比如收集日期、位置、列描述等等。因此,存在一个统一的元数据标准集合,人们可以从中进行选择,以便其他人可以获得完整的信息。一些常见的例子如下:
都柏林核心
Dublin Core 包括一个需要指定的数据元素列表,比如Date Created、Creator和其他信息。
元数据编码和传输标准
元数据编码和传输标准(METS) 是描述性和结构化数据的元数据标准,表示为可扩展标记语言(XML)。
国际标准化组织
ISO 定义了一系列全球通用的标准。标准可能因用途和地区而异。例如,对于表示时间的标准方法——有一个 ISO 8601 标准,它表示如何以一种普遍理解的模式书写日期和时间。
也存在其他标准,但使用情况取决于您试图收集的数据。收集元数据时的基本要点是,如果有人今天或将来某个时候决定处理您的数据,数据和元数据应该足以描述一切。
然而,要做到这一点,除了元数据之外,还有另一个重要信息——出处。
出处包括关于数据收集过程的信息,以及是否对该数据进行了任何转换。在收集数据时,我们跟踪数据收集的时间和方式、测量设备、过程、数据收集器、任何限制以及关于数据处理过程的一切(如果完成)。
结论
完整的数据包以及元数据和出处使数据以可用的格式经得起未来的考验。
欢迎分享你的想法、想法和建议。我很乐意收到你的来信。
数据民主化
简介、定义、利弊&利弊、结论
在当今世界,每家企业都受到来自各个角度的数据轰炸。总是有一种持续的压力,那就是使用我们从数据中获得的各种见解来即兴发挥我们的业务绩效。因此,处理后的数据的使用量令人难以置信地激增了对数据民主化的渴望和需求。所以,如果你想知道上面提到的术语对你来说是完全陌生的,让我们深入研究一下。
“数据民主化”一词的诊断

B 在掌握一些关于数据民主化的知识之前,我们需要了解民主化是什么意思。我们可以将其定义为引入民主制度或民主原则;进一步分解这个术语,我们可以把它解释为让每个人都能得到某物的行为。
正如其名,数据民主化是使数据对每个人都可用的过程,这意味着没有在数据网关处制造瓶颈的看门人。它让人们能够更好地理解数据,从而加快决策过程,并为组织发现绝佳的机会。最终的任务是让任何人在需要时随时使用这些数据,在没有障碍的情况下对决策产生重大影响。
数据被 IT 部门“保留”。各种业务单位,如市场营销、销售、行政人员总是需要数据,以便做出重要的商业决策。不用说,他们总是不得不通过各自的 IT 部门来访问所需的数据。这种方式已经存在了很长一段时间,可能几十年了,仍然有人认为这是应该的方式。
支持:需要数据民主化
关于数据共享,我们应该开放吗?
数据民主化的拥护者认为,为了获得竞争优势,必须将信息分散到所有的工作团队中。越多具有不同专业知识的人能够访问数据,越多的组织将通过识别重要和关键的业务见解并采取行动而受益。许多德高望重的专业人士认为,数据民主化可以改变许多组织的游戏规则。当数据遍布整个组织时,它使每个级别的个人都能够使用数据并参与决策制定。
反对:对数据民主化的担忧
或者我们应该对数据共享持保守态度?
许多组织人员仍然困惑于与非技术人员共享数据可能会导致对数据的误解,从而做出一些糟糕的商业决策。此外,访问数据的用户越多,数据安全风险就越大,从而给维护数据完整性带来更多挑战。
谈到数据,有些数据仍然以筒仓/仓库/归档模式存在;虽然*年来已经进行了几次尝试,并且这些尝试仍在进行中,但是不可否认的现实是,来自不同部门的人们仍然发现很难访问和查看数据。
T 这是另一个不容否认的问题——不同团队之间的重复工作,与集中的分析团队相比,这可能会导致更多的成本以及时间和精力的浪费。
是保守还是自由——应该采取什么方法?
F 为了便于讨论,可能会得出这样的结论:宣扬一种方法优于另一种方法是很容易的,但现实表明,极端保守或自由的观点很少适合数据民主化的环境。企业领导应该仔细权衡数据民主化的利弊,以确定哪种方法最有利于他们的组织。
商业领袖应该决定他们的组织应该有多自由或保守——最终,这都归结于他们的商业或组织的成功。但是,他们总是可以在组织内部传播元数据(并保留原始数据)以获得有意义和有影响力的见解,并可以在自己内部保留敏感数据。
例如,如果我们谈论一个治疗糖尿病患者的特定医疗保健中心,不允许分享患者的个人详细信息(从而侵犯他们的隐私),但总是可以对一般数据进行分析,从而找出糖尿病患者的年龄组或性别比例。
我同意,有时非技术人员或不懂数据的人的看法可能会误导人,但肯定可以尝试在此基础上增加一层额外的审查,以避免任何类型的错误,并确保组织的发展——这就是业务领导者的职责所在。
数据去神秘化—数据质量

Data Quality
从概念上解释它的真正含义,以及它为什么重要
本文概述了一个围绕数据质量组织我们工作的思维框架。参考著名的 DIKW 金字塔,数据质量是使我们能够获取原始数据并从原始数据开始使用它来生成信息的促成因素。
在这篇文章中,我们将回顾一些常见的场景,回顾一些理论,最后为面临这个日益普遍的问题的任何人提供一些建议。
为什么?
每秒钟产生的数据量几乎让人无法理解。目前的估计表明,每天有 2940 亿封电子邮件和 650 亿条 WhatsApp 信息被发送,所有这些都会留下数据痕迹。世界经济论坛估计,到 2020 年,数字世界有望达到 44 千兆字节。为了让你知道这是什么意思,看一下字节前缀,记住每个前缀都要乘以 1000: kilo、mega、giga、tera、peta、exa、zetta。
并非所有这些数据都是平等的。它以不同的形式出现,起初,它只是噪音。原始数据没有任何隐含的模式或意义。需要解读才有用。软件可以通过以不同的方式组织数据来帮助我们解释数据,无论是结构化的还是非结构化的。
但是软件是人做的,它在现实生活中运行。我们知道这意味着什么:人会犯错,世界会改变,程序有 bug,数据有质量问题。一直,一直,一直。
糟糕的数据质量会导致不准确和缓慢的决策。这不会一蹴而就,因为数据质量往往会随着时间的推移而下降。像其他任何东西一样,它遵循热力学第二定律:一个孤立系统的总熵永远不会随时间减少。
这是我们无法避免的。但是我们可以创建流程来识别问题、处理和提高数据质量,并确保我们在流程的每个阶段都增值。请记住,在处理数据时,我们的目标是生成信息,而不是简单地收集随机噪声。
我们在哪里?
第一步是了解我们在哪里,我们想去哪里。我们的衡量标准是什么?我们的商业目标是什么?我们的数据有多好?
为此,我们必须了解以下数据质量标准:
- :当数据符合预期时,认为数据是完整的。我们可能有可选的数据,但是缺乏完整性意味着我们错过了我们想要的信息。
- 一致性 :如果我们在两个或更多不同的地方有关于同一事物的矛盾或冲突的信息,数据被认为是不一致的。
- :信息在预期的时候是否可用?例如,错误发生 5 小时后仍可访问的错误日志报告是不可接受的。
- 完整性 :这个概念指的是设计良好的数据和系统。例如,在关系数据库中,这意味着没有孤立的记录或语义链接数据之间缺乏链接。
- 准确性: 这意味着我们存储的东西应该足够准确,能够反映真实的生活价值。如果我们存储了一个人的错误生日,就会出现准确性问题。
- 在关系模型中,不同的规范化方式是标准。很多时候,您最终会有意识地对数据进行反规范化(例如,为了提高性能),但是应该使用可以纳入您自己的标准中的客观论据来做出决定。日期格式等其他细节在这里很重要。
我们如何测量这些尺寸?
不幸的是,这个问题没有唯一的答案或灵丹妙药。我的建议通常是根据项目的实际情况创建一个算法。该算法可以接受输入并给每个维度打分,例如,在 0 和 1 之间。有多种方法可以处理大量数据并计算这些分数,或者您甚至可以选择数据库中有代表性的部分进行处理。启发式和常识在这里非常有价值。以编程的方式进行测试,可以确保您可以一遍又一遍地运行相同的测试,并比较结果。
理想情况下,在专业流程中,您应该得到如下输出:

Data Score Diagnosis
迈出关键的第一步后,我们可以开始给每个维度分配权重。权重很重要,因为它使数据与我们的现实情况更加相关:一些数据质量标准对实现我们的业务目标可能很重要,而其他标准对我们来说可能不太重要。与利益相关者一起完成的这一分析应该有助于突出通向明智决策的途径。它应该与你的优先事项相关联,并阐明你接下来最重要的步骤。
下一步是什么?
缩小你的关注点后,你就可以决定你是否真的有问题了。如果你这样做了,你可以在每一个维度上一次做一件事,使用任何最适合这种情况的技术。
这方面的一个例子是使用 Python 的 Panda 库来读取和处理文件或关系数据库,创建将增加一个数据质量维度的转换,然后将新数据再次推送到原始数据库。
另一种方法可能是通过跟踪代码中的算法来减轻未来的数据问题,或者甚至意识到数据库设计本身需要调整。
我们将在下一篇文章中探讨其中的一些技术。但是作为一个概念模型,这应该作为任何系统化数据质量工作的基础。
参考
- 拉塞尔·l·阿科夫,“从数据到智慧”,《应用系统分析杂志》16(1989):3–9。
- 哈兰德·克利夫兰,“作为资源的信息”,《未来学家》,1982 年 12 月,第 34-39 页。
- Arkady Maydanchim,“数据质量评估”,2007 年 9 月 15 日
- Bernard Marr,《福布斯》,“我们每天创造多少数据?”
- Jeff Desjardins,WeForum,“每天产生多少数据?”
数据去神秘化——机器学*

机器学*领域的鸟瞰图
本文的主要目标是涵盖机器学*的最重要的概念,并展示其前景。读者将具有理解哪种解决方案与特定类型的问题相匹配的眼光,并且在深入到现实生活的项目中之后,应该能够找到更具体的知识。
什么是机器学*?
我将从一个 60 年前的定义开始,但今天仍然有效:
机器学*是在没有明确编程的情况下赋予计算机学*能力的研究领域。
阿瑟·塞缪尔(1959)
这个名字是不言自明的,它的定义也强化了同样的概念。

Machine learning ecosystem
对于那些喜欢更像数学的定义的人,我们可以参考 1998 年的定义:
如果一个计算机程序在 T 上的性能(由 P 测量)随着经验 E 而提高,则称该程序从关于某个任务 T 和某个性能测量 P 的经验 E 中学*。
汤姆·米切尔(1998)
让我们用一个典型的例子来阐明这些概念:将一封电子邮件分类为垃圾邮件或不是垃圾邮件。我们通常会做的是:
- 任务是对电子邮件进行分类
- 体验 E 正在观察用户如何手动分类他们的电子邮件,将它们标记为垃圾邮件。
- 量度 P 是被正确分类为垃圾邮件的电子邮件的百分比。
如果 P 随着时间的推移而提高,我们就有了机器学*。
我们有两个主要类别的机器学*:监督和非监督学*。
监督学*
这意味着我们有一个训练集:一个“正确价值观”的列表。我们将接收一个输入(通常是一个向量),主要目标是训练算法能够使用这些值训练其学*机制,最终能够预测看不见的情况。

Supervised learning through manual categorization
一些例子可能是:
- 根据房产的大小和舒适程度预测其价格。
- 识别图像中的对象
- 根据以前的考试结果预测学生在考试中的分数。
训练集可能很大,但为了简单起见,这是一个真正简单的训练集的样子。

Real Estate properties
每一列代表训练集的一个输入,价格代表输出。我们的目标是创建一个算法,随着时间的推移(经验 E )将变得更好,预测价格(任务 T ),将误差最小化。
有各种各样的监督学*算法可用,每种算法都有其优点和缺点。没有一种单一的学*算法能最好地解决所有的监督学*问题。
一些最常见的是:
- 线性回归
- 逻辑回归
- 朴素贝叶斯
- 神经网络
- 决策树
无监督学*
它也被称为自组织,允许对给定输入的概率密度进行建模。基本上,它试图检测数据中的模式。
假设我们有一个数据集,但没有人告诉我们每个数据点是什么。相反,我们只是被告知,这是一组数据。我们问我们的算法是否能在数据中找到某种结构。
例如,无监督学*算法可能决定数据位于两个不同的群集中。
使用聚类的一个例子是在谷歌新闻中,如果你以前没看过,你可以去 news.google.com 这个网址看看。谷歌新闻所做的是每天查看网上成千上万或成千上万的新故事,并把它们组合成连贯的新闻故事。
无监督学*中最常用的一些算法有:
- 使聚集
- 异常检测
- 一些种类的神经网络,像 Hebbian 学*或生成对抗网络
- 盲信号分离技术
无监督学*允许我们在很少或根本不知道我们的结果应该是什么样的情况下处理问题。我们可以从数据中推导出结构,而不一定知道变量的影响。
什么是正确的方法?
没有灵丹妙药。了解多种技术和每种技术可以解决的问题是最重要的技能。
下面,我们可以看到许多不同的问题和算法,你应该进一步研究解决它们。
回归和聚类
- 基于大量输入估计数字输出:线性回归
- 基于输入估计类别:逻辑回归
- 将 n 个观察值划分为 k 个聚类:K-均值
- 发现异常值(异常检测):DBSCAN 算法
分类
- 复杂关系,魔法:神经网络
- 基于邻*度的组成员关系:K-NN。
- 非连续数据(if/then/else):决策树
- 随机查找最佳分割:随机森林
- 最大间隔分类器(非常重要):SVM
- 用新信息逐步更新知识:朴素贝叶斯
特征约简
- 可视化高维数据:t 分布随机邻居嵌入
- 将特征空间提取为描述最大方差的分量:主分量分析
- 理解交叉关系矩阵:典型相关分析。
- 区分类别的特征的线性组合:线性判别分析。
一些你必须知道的概念
神经网络
它们受到我们生物神经元的启发。ANN(人工神经网络)基于称为人工神经元的连接单元或节点的集合,其松散地模拟生物大脑中的神经元。每个连接就像生物大脑中的突触一样,可以将信号从一个人工神经元传输到另一个人工神经元。接收到信号的人工神经元可以对其进行处理,然后向与之相连的其他人工神经元发送信号。
简而言之,神经网络是一个决策者。神经网络的拓扑结构有无限多种,有些种类最适合某些问题。这超出了本文的范围。
偏差方差权衡、欠拟合和过拟合
这是一组预测模型的特性,其中在参数估计中具有较低偏差的模型在样本间具有较高的参数估计方差,反之亦然。
偏差是来自学*算法中错误假设的误差。高偏差会导致算法错过特征和目标输出之间的相关关系(欠拟合)。
方差是对训练集中的小波动的敏感性的误差。高方差会导致算法模拟训练数据中的随机噪声,而不是预期的输出(过拟合)。
这种权衡适用于所有形式的监督学*。
准确度函数
在分类算法中,准确度定义了正确预测数与输入样本总数之间的比率。
精确函数
正确识别实际阳性的比例是多少?
回忆功能(也称为敏感度)
实际阳性中有多少比例被正确识别?
特异性函数
多大比例的实际底片被正确识别?
图书馆
以下库可以说是该领域中最重要的库:
- 熊猫
- Scikit
- 张量流
- PySpark
- NumPy
- 散景
- 克拉斯
- SciPy
摘要
本文概述了机器学*领域的许多不同领域。主要目的是激发兴趣,我们的读者可以更深入地了解这些领域。从字面上看,有成千上万个可能的专业化领域,这项技术正在给未来带来令人兴奋的机会。
参考
吴恩达,斯坦福大学的“机器学*”。
密歇根大学应用数据科学课程。
罗纳德·房龙(2018 年 2 月 5 日)。“机器学*解释:理解监督、非监督和强化学*”检索自https://bigdata-madesimple.com。
熊猫数据库,从https://pandas.pydata.org/检索
拉吉夫·乔普拉(2018)。《机器学*》,卡纳出版社。
了解新加坡 HDB 转售价格的数据驱动方法

HDB Housing in Singapore. Image from PixaBay
82%的新加坡人住在 HDB(住房发展局)建造的公寓里。在 HDB 买房是新加坡年轻人谈论最多的话题之一,也可能是年轻人最大的经济负担之一。给定一定的预算,在哪里和什么类型的转售 HDB 单位可以购买?
像大多数想在 HDB 买房的年轻人一样,我和我的团队已经开始着手这个项目,心中有几个目标:
1.了解影响 HDB 转售价格的关键因素
2.建立一个综合定价模型来确定 HDB 转售公寓的内在价格
HDB 在新加坡的转售价格很大程度上基于销售比较法,即你的前邻居的房子卖了多少钱。在这种情况下,HDB 转售单位的最佳价格预测值因此是附*类似公寓的先前交易价格,并根据细微差异进行了一些调整。有时,这种定价也可能是不合理的,因为人们猜测政府可能在不久的将来回购股票,这种定价可能被人为抬高。
这为我们提供了一个非常有趣的数据分析问题:根据客观因素,如公寓条件、单位的可达性以及其他房地产信息,一所房子的真实内在价值是多少。
考虑到这一点,我们开始收集大量数据来构建我们的模型。
特色工程
基于对市场信息的观察以及对购买我们自己的 HDB 公寓的研究,我和我的团队对影响 HDB 公寓价格的关键因素建立了以下假设:
1.可达性:所用时间包括步行到最*的捷运站的时间,以及从最*的捷运站到莱佛士广场捷运站的行车时间。
2.邻*设施:HDB 公寓邻*小学、商场等设施。
3.公寓所在的城镇:公寓所在的城镇越成熟,转售价格越高。
4.户型:平层、剩余租期、面积、户型。
5.遗产构成:如遗产中工作成年人的百分比、老年人的百分比、年轻人的百分比
数据来源
我们使用的数据来源是公开的,很容易在网上找到。特别感谢data.gov.sg、 onemap.sg 、 Singstats 和其他几个在线资源,我和我的团队能够获得我们在模型上构建输入特性所需的大部分信息:
https://data.gov.sg/dataset/resale-flat-prices:从检索 2017 年 8 月至 2018 年 8 月所有转售交易的地址、户型信息、价格等历史数据
地理位置信息:为了提取事务的位置数据,我们构建了一个 python 工具来从 onemap.sg API 中提取纬度和经度信息。我们还提取了新加坡所有捷运站、小学和商场的经度和纬度信息

Extracting Lat and Long from MRT stations
Python Tool to extract lat and long from onemap.sg
Malls :从维基百科手动提取新加坡所有的商场。使用 python 工具提取的纬度和经度。
捷运站:全国各地的捷运站和 LRT 站摘自 LTA 的网站。使用 python 工具提取的纬度和经度。
到名胜古迹的相对距离:利用从 onemap.sg 获取的经纬度信息,我们能够利用毕达哥拉斯定理计算出 HDB 公寓到名胜古迹的距离,比如最*的捷运站、最*的小学、最*的商场。

Calculating the distance between two locations
居民地理人口统计:从新加坡统计局提取的特定规划区域的人口统计数据,如规划区域内工作成年人的百分比。
从莱佛士广场捷运站的行程时间:从各捷运站和 LRT 站到莱佛士广场捷运站的行程时间从https://www.transitlink.com.sg/eservice/eguide/rail_idx.php手动提取
最终数据集
下面显示的最终数据集显示了 2017 年 8 月至 2018 年 8 月期间交易的所有公寓的信息。这些信息包括城镇、交易月份、剩余租赁年限、邻*因素以及转售价格等。

Analytical Sandbox

Analytical Sandbox (cont.)
方法论
利用这些数据,我们使用 SAS JMP Pro 对各种模型(即多元线性回归(标准最小二乘法)、多元线性回归(逐步)、随机森林和自助森林)进行了试验,以选择最佳价格预测模型。最佳模型的选择基于:
验证数据集的拟合质量。
可解释性,即模型不应该使用太多的变量,也不应该是一个黑箱。
我们发现多元线性回归——标准最小二乘模型是首选模型,因为它简单且其性能(调整后的 R 平方)与其他模型相当。最重要的是,利用多元线性回归——标准最小二乘法,我们可以轻松地分解算法,了解每个变量如何影响估计价格。
调查结果
通过线性回归,我们建立了模型来预测 HDB 转售交易的价格,并了解每个变量是否以及如何影响交易价格。
基于我们对影响 HDB 转售公寓价格的关键因素的假设,以及用我们的模型对信息的分析,我们已经为 HDB 的一套四居室公寓的价格推导出以下等式:

4-room HDB resale pricing formula
城镇溢价: HDB 转售成交价由单位所在的城镇决定(如:昂船洲镇)。我们得出的结论是,位于设施更完善的成熟城镇的公寓会获得更高的溢价。上述等式中的“城镇地价”考虑了这一因素。下表概述了将在基础价格上增加或减少的城镇溢价的估计金额。例如,一个买家将不得不支付额外的 7,624.01 美元购买一套位于 Ang Mo Kio 镇的 4 个房间的 HDB 转售公寓。然而,如果他在 Bukit Batok 购买一套公寓,将有 57,446.29 美元的溢价折扣。

Town Premium
扁平模型:我们已经得出结论,HDB 转售交易价格部分由扁平模型决定。例如,如果一个买家购买了 DBSS(一个由私人开发商建造的公共 HDB 公寓的模型),他/她将不得不在 HDB 一套四居室公寓的基础价格上支付 81,226.21 美元的溢价。

Flat Model Premium
楼层范围:我们已经得出结论,一个四居室的 HDB 每增加一层要花费 3749 美元。
占地面积:我们得出的结论是,一个四居室的 HDB 每平方米的成本为 2815 美元。
剩余租期:HDB 所有的公寓都由政府提供 99 年的租约。每过一年,一套四居室的 HDB 就会贬值 4256 美元。
步行至最*的地铁站所需时间(分钟):HDB 的单位离最*的地铁站越远,估计楼价越低。到捷运站每多走一分钟就少花 5744 美元。每分钟相当于 80 米。
前往莱佛士广场捷运站的行车时间(分钟):最*的捷运站离莱佛士广场捷运站越远,估计楼价越低。在捷运上每多行驶一分钟就少花 5429 美元。
步行至最*商场的时间(分钟):单位离最*商场越远,估计单位价格越低。到购物中心每多走一分钟就少花 891 美元。
我们在模型中包括的其他变量,如房地产构成(如工作成人的百分比)和到最*小学的距离,在统计上不显著,因此从公式中排除。

Effect summary — % of working adult and nearest primary school are not statistically significant at 99.9% confidence level
解读分析
这种分析可以在很多方面帮助我们选择理想的家。不同的夫妻,在选房子的时候,预算不同,偏好也不同。如果你喜欢靠*中央商务区的公寓,你可以比较一下城镇地价和到中央商务区的距离。
一些城镇因靠*中央商务区而更物有所值:

Average price premium vs time (mins) to travel from nearest MRT to Raffles Place MRT
距离莱佛士广场 5-10 分钟:卡尔朗/黄埔(溢价 11,000 美元)vs 皇后镇(溢价 89,000 美元)
距离莱佛士广场 15-20 分钟:贝多克(溢价 15000 美元)vs 碧山(溢价 83000 美元)
距离莱佛士广场 30-35 分钟:森巴旺(溢价-125,000 美元)vs 坦皮恩斯(溢价-190,000 美元)
对于一套距离莱佛士广场(Raffles Place)15-20 分钟路程的 HDB 公寓来说,贝多克的房价比璧山便宜大约 8 万美元。
我们也可以用这个公式来理解给定一定预算的权衡。你愿意每增加一层支付 3749 美元,还是每靠*捷运站一分钟支付 5744 美元?
推荐
回答最重要的问题,根据这 9 个因素(城镇地价、公寓型号、楼层范围、建筑面积、剩余租赁年限、步行到最*的地铁站的时间、步行到最*的购物中心的时间、从最*的地铁站到莱佛士广场的旅行时间),我们在哪里可以找到最有价值的 HDB 转售公寓?

Which HDB estate has the best value for 4-room HDB?
结果发现,小印度的巴生巷(Klang Lane)和布法罗路(Buffalo Road)的四居室公寓最有价值,没有考虑其他因素,如直接的邻居、风水、公寓的朝向、景观、装修等。
对于基于 9 个因素的最差价值的遗产:

Which HDB estate has the worst value for 4-room HDB?
结论
根据我们的调查结果,我们得出的结论是,新加坡的 HDB 转售价格在很大程度上受到一些关键因素的影响,如城市的成熟度、公寓是否靠*中央商务区以及购物中心等设施。然而,有些城镇更靠*中央商务区,但城镇地价较低。这些位于这些地区的公寓可能会吸引那些希望靠* CBD 但预算紧张的买家。
希望你找到你的完美房子!
小组成员:孟勇·李、努尔·艾尤尼·胡斯娜·宾提·巴克里、卡·伊
还要特别感谢西尔万·张!
数据驱动架构
建筑世界的下一步是什么?来自建筑用户的数据会告诉我们。
在他的书《超级计算者》中,伊恩·艾尔斯认为直觉和经验已经不足以让企业做出决策。艾尔斯认为,最好的决策是在专业知识和数据的交汇处做出的。建筑界充满了受过高等教育、经验丰富的专业人士,但缺乏来自数据的建筑见解。
在数据科学之前,我学*和实践了将*十年的架构。随着时间的推移,很明显,建筑是一个严重依赖于 T2 经验的职业。在设计建筑时,建筑师通常依靠历史、经验和直觉。人们可能会在办公室听到这样的短语:
“在我参与的上一个项目中,我做了 _____。”
或者类似于:
“过去我总是做 _____。”
即使当建筑师面临一个新的设计问题,我们倾向于尝试和真实的解决方案。在此基础上,我们对建筑设计进行更小的调整和微调,以独特地满足客户的需求和愿望。
话虽如此,我的观点是这样的:架构师应该在设计过程中优先考虑数据驱动的决策。经验和专业知识现在和将来都很重要,但它被赋予了太多的分量。正如艾尔斯所建议的,数据和经验应该携手合作。我们经常依赖于来自过时经验的“最佳实践”,而构建用户的需求和愿望总是随着时间而变化。为了补救这一点,我们应该设计来自用户的连续反馈回路——在设计之前,以及在入住期间。数据可以告诉我们人们在各种建筑类型中真正想要的是什么:工作场所、教室或医院。问题是——反馈没有被视为架构设计过程中的一个重要步骤。
尽管缺乏建筑数据收集,但至少有一种方法是一些建筑师目前正在使用的。这种方法被称为“POE”或“使用后评估”POE 可以采取多种形式,如面对面访谈、电话或现场观察。但最常见的情况是,POE 采取通过电子邮件向建筑用户发送调查问卷的形式。该调查可能会询问有关建筑声学、热舒适、照明、一般美学等方面的问题。问题以不同的形式呈现,如评分表、多项选择或开放式回答。例如:
你觉得你的工作环境如何:
A)太大声
B)有点太大声
C)刚刚好
D)太安静
或者也许:
在 1-10 分的范围内,你在当前工作环境中的注意力有多集中?请解释您的评分。
Poe 通常用于大型建筑类型,如:医疗保健、高等教育和商业建筑。让我们以一个新设计的办公楼为例。建筑完工后,租户搬进去,有时间安顿下来,适应新环境。大约一年后,建筑师会将 POE 发送给业主,业主会将调查结果转发给租户。租户填写这个 POE,它被发送给建筑业主,最后被送回给建筑师。
假设大部分问题都是以结构化的格式呈现的(即多为选择题和评分量表),那么就可以快速分析数据,而不需要太多的清理来识别设计趋势。这些趋势可以按建筑类型进行组织,并可以在内部进行宣传,以帮助设计师做出决策。来自用户反馈的趋势可以识别用户真正喜欢和不喜欢的空间、设计元素和系统。它们可以显示设计在哪里可行,在哪里不可行。
精心策划的调查数据可以洞察常见的用户行为。对于办公室类型,一项调查可以回答:
“员工最常使用哪些空间?”
“这种开放式办公室的概念对这些用户来说真的很好吗,还是会分散注意力?”
“小会议室真的需要自然光吗,为什么?”
从本质上讲,POE 数据可以消除人们对建筑物及其内部空间需求的猜测。听起来很棒,对吧?
问题是:客户经常从工作范围中删除这些 POE 调查,以节省资金。在业主-建筑师协议中,它通常作为“照菜单点菜”的选项呈现给客户。这使得客户在涉及范围和合同谈判时很容易选择退出。大型实践的领导者应该将 Poe 视为每个项目工作范围的一个要求。为了跟上建筑用户的技术和行为变化,建筑服务不应再选择 Poe。
KieranTimberlake是费城的一家建筑公司,在整个设计过程中收集和使用多种形式的数据。它的联合创始人斯蒂芬·基兰曾经说过:
“我们的身体需要定期检查,我们的建筑也应该如此。”
正如身体会随着时间的推移而老化和变化,建筑也是如此。POE 数据收集不应只进行一次,而应在建筑物的整个生命周期内定期进行,以确保其处于最佳工作状态。一座建筑可能会保留*一个世纪甚至更久——然而,变化是不可避免的。建筑物的居住者会改变,人们的行为方式会改变,他们对建筑物的要求也会改变。例如,一个旧飞机库可能会成为下一个谷歌总部!

The Spruce Goose Hangar, before adaptive renovations. Credit: Playa Vista/Brookfield Residential

The Spruce Goose Hangar, after adaptive renovations for Google Los Angeles. Credit: ZGF Architects & Google
数据已经在我们的世界中驱动了如此多的决策:在社交媒体上显示哪些广告,哪个按钮最有吸引力,等等。因此,也许建筑的未来也取决于数据。例如,建筑物中的居住者传感器已经可以收集关于人类活动的数据,以控制建筑物中的照明和 HVAC 的光学和热舒适性。但是,如果建筑从技术世界吸取了教训,并找到了一种方法来持续收集关于居住者对建筑的舒适度和满意度的数据,会怎么样呢?
事实证明,KieranTimberlake 已经开发了一个基于网络的调查应用程序来解决这个问题。这款应用名为“烘焙” 【烤】 跨多个变量测量乘员舒适度,包括:温度、湿度、个人活动水平、空气流动、空气质量、视觉刺激和听觉刺激。

The Roast app offers a simple user interface for building occupants to submit information about their day-to-day comfort through a descriptive and structured gradient scale of comfort levels. Credit: KieranTimberlake

With colored plans and stacked bar graphs, the Roast app can quickly and graphically tell the story of building user comfort. It shows where in the building occupants are uncomfortable, and in what way they are uncomfortable. Credit: KieranTimberlake
KieranTimberlake 的研究员罗德里克·贝茨告诉我们:
有时不舒适的建筑并不是糟糕设计的产物,而是缺乏信息…不了解人们的感受,你就一无所知
这里学到的经验是,建筑用户的最佳表现取决于许多变量。即使最好的建筑师听取了客户的意见,并给出了他们所要求的东西,随着时间的推移,建筑仍然可能无法为用户提供理想的服务。我们需要不断地倾听用户,了解他们需要什么以及他们何时需要。
为了将建筑设计推进到下一个十年,建筑公司应该考虑用数据做以下三件事:
1。在建筑设计服务中,定期用户反馈是强制性的。
2。分析收集的用户数据。确定大趋势以及具体的细微差别,以确定以前设计的建筑中的功能差距。
3。发布关键发现,讲述用户舒适度的“故事”。将经验与数据驱动的见解相结合,设计出更好的建筑。
有效的数据挖掘对于制定有效的营销策略至关重要
营销活动中的有效自动化
使用 ML 算法的营销分析和决策支持系统

数据挖掘方法通常用于预测不同营销产品的销售成功与否。我们听到了许多时髦的词汇,如商业智能、决策科学、数据科学等。等等。;从广义上讲,他们都支持使用工具&技术将数据转化为商业见解的相同目标。在这里,我们将分析与银行客户、确定的产品和社会经济属性相关的 100 多个特征。
用例与问题陈述:
持续的收入损失迫使一家著名银行重新审视其内部流程并进行根本原因分析。他们发现的一个原因是,客户不像以前那样频繁地选择定期存款。因此,银行希望识别现有客户,即那些更有可能选择定期存款的客户,并希望将营销工作集中在这些客户上。
在建模阶段,基于业务知识,我们执行了半自动特征选择,以将维度减少到 17 个相关特征,用于进一步分析。

特征工程:
特征工程是数据挖掘的关键。除了常用的银行客户端和产品属性之外,这里我还在特性中使用了社会和经济指标。为了保持简单,没有显示整个数据挖掘过程。经过预处理后,数据集的统计概要—

平均余额为 1,528.53,但标准偏差(std)为 3,225.41,因此我们可以通过这一点了解到余额在整个数据集内分布很广。期限与潜在客户是否会购买定期存款高度相关。
平均活动持续时间为 372 (~371.99),让我们看看高于该平均值的客户是否更有可能开立定期存款账户。因此,需要删除 duration 列;此外,持续时间是在向潜在客户发出呼叫之后获得的,因此如果目标客户从未收到过呼叫,则该功能就没有多大用处。


超过期限状态的人更有可能开立定期存款账户。持续时间高于平均值的群体中有 77%开立了定期存款账户,而低于平均值的群体中有 32%开立了定期存款账户(应为 77/33 或 78/32 )。显然,一个好主意是瞄准那些在平均水平以上的人。
我们可以进行聚类和数据分析,找出历史上哪个变量对定期存款有最重要的影响,例如-
- 婚姻状况&教育、职业、教育、贷款、期限、住房等。
为了简单起见,我将跳过本文中的所有分析。为了便于理解,我将采用一种简单的方法,通过绘制相关图来检查哪些变量与存款相关。
corr = df.corr()
sns.heatmap(corr, xticklabels=corr.columns.values, yticklabels=corr.columns.values, annot = True, annot_kws={‘size’:12})
heat_map=plt.gcf()
heat_map.set_size_inches(20,15)
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.show()

从上面我们可以看出,“住房”和“贷款”是最重要的两个,分别与存款有-20%和-11%的相关性。这里我们将重点讨论“贷款”;让我们看看贷款分布模式。

ax = (df['deposit'].value_counts() /len(df)*100).plot(kind='bar', stacked = True, rot = 0)
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
ax.set_ylabel('% Customers')
ax.set_xlabel('Personal loan')
ax.set_title('Frequency Percentage by Class')totals = [] # list to collect the plt.patches data# values and append to list
for i in ax.patches:
totals.append(i.get_width())total = sum(totals) # setting individual bar labels using above listfor i in ax.patches:
ax.text(i.get_x()+.15, i.get_height()-3.5, str(round((i.get_height()/total), 1))+'%’, color = 'white’, weight = 'bold')

“贷款”一栏包含 87%(86.9%)的“没有”(没有个人贷款),13%(13.1%)的“有”(有个人贷款)。数据集严重失真。
你可以在这里访问来寻找其他不对称数据集的用例。这里,我们希望确保我们的训练和测试集包含相同的比率,即 87%“否”和 13%“是”。
为了避免过度拟合,分层抽样或交叉验证很重要,但是,我们必须确保至少对标签有最大影响的特征(潜在客户是否会开立定期存款)是平均分布的。
这里,我们将数据分为训练集和测试集,并实现了分层。
*# Creating pipelines
numerical_pipeline = Pipeline([("select_numeric", DataFrameSelector(["age", "balance", "day", "campaign", "pdays", "previous","duration"])),("std_scaler", StandardScaler())])
categorical_pipeline = Pipeline([("select_cat", DataFrameSelector(["job", "education", "marital", "default", "housing", "loan", "contact", "month","poutcome"])),("cat_encoder", CategoricalEncoder(encoding='onehot-dense'))])
preprocess_pipeline = FeatureUnion(transformer_list=[("numerical_pipeline", numerical_pipeline),("categorical_pipeline", categorical_pipeline)])*
**
分类算法:
现在,训练和测试集已经准备好,让我们通过训练如下 8 个模型来检查哪个分类算法适合我们的情况—
*dict_classifiers = {"Logistic Regression": LogisticRegression(solver='lbfgs', max_iter=5000), "Nearest Neighbors": KNeighborsClassifier(),"Linear SVM": SVC(gamma = 'auto'),"Gradient Boosting Classifier": GradientBoostingClassifier(),"Decision Tree": tree.DecisionTreeClassifier(),"Random Forest": RandomForestClassifier(n_estimators=18),"Neural Net": MLPClassifier(alpha=1),"Naive Bayes": GaussianNB()}no_classifiers = len(dict_classifiers.keys())
**def** batch_classify(X_train, Y_train, verbose = **True**):
df_results = pd.DataFrame(data=np.zeros(shape= (no_classifiers,3)), columns = ['classifier', 'train_score', 'training_time'])
count = 0
**for** key, classifier **in** dict_classifiers.items():
t_start = time.process_time()
classifier.fit(X_train, Y_train)
t_end = time.process_time()
t_diff = t_end - t_start
train_score = classifier.score(X_train, Y_train)
df_results.loc[count,'classifier'] = key
df_results.loc[count,'train_score'] = train_score
df_results.loc[count,'training_time'] = t_diff
**if** verbose:
print("trained **{c}** in **{f:.2f}** s".format(c=key, f=t_diff))
count+=1
**return** df_resultsdf_results = batch_classify(X_train, y_train)
print(df_results.sort_values(by='train_score', ascending=**False**))*

我们在这里看到,最有可能的是决策树分类器和随机森林分类器过拟合,因为我们得到了接*完美的分数(100%和 99.62%)准确度分数。
欠拟合(偏差)和过拟合(方差):
如果数据本质上更复杂,我们将永远无法用一条线来很好地描述数据集。这种现象被称为欠拟合。它没有足够的模型灵活性来适当地考虑数据中的所有特征;另一种说法是模型有很高的偏差。
如果模型拟合具有足够的灵活性,几乎完美地解释了数据中的所有特征,并导致数据的过度拟合。过多的模型灵活性最终会导致随机错误和底层数据分布。换句话说,模型有很高的方差。
我们必须采用交叉验证过程来避免这种情况。这样做的主要目的是将数据的整体模式提供给模型。
*# Logistic Regression
log_reg = LogisticRegression(solver=’lbfgs’, max_iter=5000)
log_scores = cross_val_score(log_reg, X_train, y_train, cv=3)
log_reg_mean = log_scores.mean()# SVC
svc_clf = SVC(gamma=’auto’)
svc_scores = cross_val_score(svc_clf, X_train, y_train, cv=3)
svc_mean = svc_scores.mean()# KNearestNeighbors
knn_clf = KNeighborsClassifier()
knn_scores = cross_val_score(knn_clf, X_train, y_train, cv=3)
knn_mean = knn_scores.mean()# Decision Tree
tree_clf = tree.DecisionTreeClassifier()
tree_scores = cross_val_score(tree_clf, X_train, y_train, cv=3)
tree_mean = tree_scores.mean()# Gradient Boosting Classifier
grad_clf = GradientBoostingClassifier()
grad_scores = cross_val_score(grad_clf, X_train, y_train, cv=3)
grad_mean = grad_scores.mean()# Random Forest Classifier
rand_clf = RandomForestClassifier(n_estimators=18)
rand_scores = cross_val_score(rand_clf, X_train, y_train, cv=3)
rand_mean = rand_scores.mean()# NeuralNet Classifier
neural_clf = MLPClassifier(alpha=1)
neural_scores = cross_val_score(neural_clf, X_train, y_train, cv=3)
neural_mean = neural_scores.mean()# Naives Bayes
nav_clf = GaussianNB()
nav_scores = cross_val_score(nav_clf, X_train, y_train, cv=3)
nav_mean = neural_scores.mean()# Create a Dataframe with the results.
d = {‘Classifiers’: [‘Logistic Reg.’, ‘SVC’, ‘KNN’, ‘Dec Tree’, ‘Grad B CLF’, ‘Rand FC’, ‘Neural Classifier’, ‘Naives Bayes’],
‘Crossval Mean Scores’: [log_reg_mean, svc_mean, knn_mean, tree_mean, grad_mean, rand_mean, neural_mean, nav_mean]}result_df = result_df.sort_values(by=['Crossval Mean Scores'], ascending=**False**)
result_df*

我们将以 84.51%的分数进行梯度提升。
*y_train_pred = cross_val_predict(grad_clf, X_train, y_train, cv=3)
grad_clf.fit(X_train, y_train)
print ("Gradient Boost Classifier accuracy is **%2.2f**" % accuracy_score(y_train, y_train_pred))# Gradient Boost Classifier accuracy is 0.85*
分类报告:
此报告显示了每个类别的主要分类指标。
分类报告提供了对分类器行为的更深层次的直觉,而不是全局准确性,这可以掩盖多类问题中某一类的功能缺陷。
该报告显示模型的精确度、召回率、F1 和支持度分数。
*conf_matrix = confusion_matrix(y_train, y_train_pred)
f, ax = plt.subplots(figsize=(10, 6))
sns.heatmap(conf_matrix, annot=**True**, fmt="d", linewidths=.5, ax=ax)
plt.title("Confusion Matrix", fontsize=12)
plt.subplots_adjust(left=0.15, right=0.99, bottom=0.15, top=0.99)
ax.set_yticks(np.arange(conf_matrix.shape[0]) + 0.5, minor=**False**)
ax.set_xticklabels("")
ax.set_yticklabels(['Refused T. Deposits', 'Accepted T. Deposits'], fontsize=12, rotation=360)
plt.show()*
从上表我们可以看出,阳性预测值为{4150 /( 4150+826)} 83.4%,阴性预测值为{3857/(654+3857)} 85.5%。这主要是由我们选择的阈值决定的。
为了确定我们的模型有多好,我们需要度量标准,如真阳性率 ( TPR/即 回忆),假阳性率 ( FPR )和精度,这将 随阈值*变化,我们选择将预测概率转化为预测类别(0 或 1)。***
如果我们检查每一个可能的阈值并计算这些度量,我们可以为给定的模型建立接收器工作特性 ( ROC )曲线和精确召回 ( PR )曲线。
精确度、召回率、f1 分数和排队率的可视化:
辨别阈值是肯定类别被选择超过否定类别的概率或分数。通常,该值设置为 50%,但可以调整阈值,以增加或减少对误报或其他应用因素的敏感度。

在上图中,我们看到可视化工具被调整为寻找最佳 F1 分数,该分数被标注为阈值 0.41。如果概率> 0.41,则选择正类,否则选择负类。然而,该阈值可能不是最佳阈值:相对于区分阈值,精确度和召回率之间通常存在反比关系。通过调整分类器的阈值,可以将 F1 分数(精确度和召回率的调和平均值)调整到最佳拟合,或者调整分类器以针对特定应用表现最佳。通过考虑以下指标来调整分类器:
- 精确度:精确度的提高意味着假阳性数量的减少;当特殊待遇的成本很高时(例如在定期存款市场活动中浪费时间),应优化该指标。
- 回忆:回忆的增加减少了肯定类被错过的可能性;当即使以更多的假阳性为代价也要抓住这个案例时,这个度量应该被优化。
- F1 分数:F1 分数是精确度和召回率之间的调和平均值。fbeta 参数确定计算此指标时精度和召回的相对权重,默认设置为 1 或 F1。优化这一指标可以在精确度和召回率之间取得最佳平衡。
- 排队率:“排队”是存款查询的术语。此度量描述必须审查的实例的百分比。如果定期存款的审查成本很高,则必须根据业务要求尽量降低成本。如果没有,这可以被优化以确保查询框是干净的。
我们看到,该图被调整为寻找最佳 F1 分数,该分数被标注为阈值 0.40。该模型在多个训练/测试分割上运行多次,以说明模型相对于指标的可变性(显示为中间曲线周围的填充区域)。
ROC 曲线(受试者操作特征)AUC(曲线下面积):
如果我们绘制一系列分界点的真阳性结果的分数(真阳性数/有存款的数)与假阳性结果的分数(假阳性数/无存款的数)的关系图,它将生成一条 ROC 曲线。该曲线以图形方式描述了调整截止点时灵敏度和特异性之间的权衡
ROC 测量分类器的预测质量,并可视化模型的灵敏度和特异性之间的权衡。在 Y 轴上绘制的 TP 比率和在 X 轴上绘制的 FP 比率基于全球平均值和每个类别。因此,理想点是图的左上角:FP 为零,TP 为一。这就引出了 AUC,也就是 FP 和 TP 的关系。AUC 越高,模型越好。然而,检查曲线的“陡度”也很重要,因为这描述了 TP 率的最大化同时 FP 率的最小化。

现在我们可以说,如果我们愿意接受 20%的 FPR ,我们将得到 60%以上的 TPR 。
精确召回曲线:
用于评估分类器质量的精确召回曲线度量。精度-召回曲线显示了精度(结果相关性的度量)和召回(返回多少相关结果的度量)之间的权衡。曲线下的大部分区域表示高召回率和高精度,这是分类器的最佳情况,显示了为其选择的大多数类返回准确结果的模型。

在上图中,我们可以看到 y 轴上的精度与 x 轴上的召回率的关系。填充区域越大,分类器越强。红线标注了平均精度,这是整个图的摘要,计算为每个阈值达到的精度的加权平均值,因此权重是与前一个阈值的召回差异。这里说,我们的模型预测平均准确率为 88%。
学*曲线:
学*曲线显示了具有不同数量训练样本的评估者的训练分数与交叉验证测试分数之间的关系。它是一种工具,可以发现我们从添加更多的训练数据中获益多少,以及估计量是更容易受到方差误差还是偏差误差的影响。

随着更多数据的添加,训练和交叉验证分数会聚合在一起,因此模型可能不会从更多数据中受益。曲线以平均分数绘制,但是交叉验证期间的可变性以阴影区域显示,代表所有交叉验证平均值上下的标准偏差。我们可以看到,训练分数和交叉验证分数最后都不是很好。然而,曲线的形状显示,训练分数在开始时非常高(超过 92%),并且下降到接* 86%,交叉验证分数在开始时非常低(接* 82%),并且上升到接* 85%。因此,我们看到训练和验证分数最终几乎相等。添加更多的训练样本很可能会提高泛化能力。
升力/增益曲线:
“累积收益和提升曲线是一种简单而有用的方法,基于使用预测模型锁定最有前景的客户,可以了解我们开展营销活动可能获得的回报以及我们应该联系多少客户”
提升/累积收益图表用于评估结果,因为每个结果都有成本。这些信息还可以用来决定打电话的次数,以平衡营销成本和最终电话的预期回报。每个目标客户都有相关的成本(直接&间接),因此我们的目标是最大限度地增加通过电话营销获得的受访者数量。现实情况是,挖掘模型很可能介于两个极端之间;在随机猜测和完美预测之间。随机猜测的任何改进都被认为是提升。
十分位数组:
使用拟合的模型,我们可以将从历史营销活动中观察到的结果(即,谁回应了谁没有回应)与该活动中联系的每个客户的预测回应概率进行比较。在实践中,该模型适用于数据的子集,并使用该模型来预测“拒绝”样本中每个客户的响应概率,以更准确地评估该模型对新客户的表现。
可以通过按照客户的预测概率从最高(最接* 1)到最低(最接* 0)的降序对客户进行排序来制作图表。将客户分成大小相等的部分,我们创建包含相同数量客户的组。因此,我们预测最有可能做出响应的客户位于十分之一组 1,其次是十分之一组 2,依此类推。检查每个十分位数组,我们可以生成一个十分位数汇总,总结每个十分位数中客户和受访者的数量和比例。
累积收益:
从十分位数汇总,我们还可以计算出模型提供的累计收益。我们将调查对象的累计百分比与营销活动中各组接触的累计百分比进行比较。这是针对使用最高响应概率的客户总数的给定百分比的 【增益】 ,而不是随机针对他们。**
该图显示了该模型将受访者和非受访者区分开来的效果。在这里,人口被分为十分之一,根据他们成功的预测概率递减的顺序。
**skplt.metrics.plot_cumulative_gain(y_test, predicted_probas)
plt.show()**

斜虚线表示随机猜测的结果,是评估 lift 的基线。我们得到另外两行:一行显示训练数据集的理想结果,第二行显示模型的实际提升,或者结果的改进。这里,过滤模型的理想线显示在等级 1 中,而实际升力线显示在等级 0 中。我们可以从图表中看出,理想线的峰值在 60%左右,这意味着我们可以通过向总人口的 60%发送邮件来接触到 100%的目标客户。
根据发送每个电话的相关成本和每个回答者的预期收入,累积收益图可用于决定联系的最佳客户数量。
**skplt.metrics.plot_lift_curve(y_test, predicted_probas, title=’Lift Curve’, ax=None, figsize=None, title_fontsize=’large’, text_fontsize=’medium’)**
电梯:
我们还可以看一下 提升 所实现的目标客户群百分比递增,按概率递减排序。 提升 简单来说就是调查对象的百分比与联系客户的百分比之比。所以,与随机联系客户相比,1 的提升相当于没有收获。然而,举个例子来说,lift 值为 2 时,与我们通过随机联系相同数量的客户所预期的数量相比,联系到的受访者数量是两倍。因此,我们可能只接触了 40%的客户,但我们可能接触到了客户群中 80%的受访者。因此,与联系随机抽样的客户相比,我们将目标群体的受访者数量增加了一倍。
这些数字可以显示在升力曲线上。理想情况下,我们希望提升曲线尽可能高地延伸到图的左上角,这表明我们与一小部分客户的联系有很大的提升。

现在,我们的预测模型已经准备好了,让我们找出影响定期存款认购结果的特征。这可以使用决策树、XGB 或 RandomForest 等来完成。为此我使用了决策树。
决策树分类器:
我们的分类器的三个最重要的特征是-

- 持续时间(销售代表和潜在客户之间的对话持续了多长时间),
- 联系人(同一营销活动中潜在客户的联系人数量),
- 月份(一年中的月份)。
摘要
现在我们已经完成了所有的分析和预测模型,是时候设计一个营销策略了
- 从历史数据中,我们可以找出营销活动水平最高的月份。在此基础上,可以决定活动的时间。
- 从年龄类别中,我们可以向下钻取并找出现有存款人的平均年龄。在此基础上,活动可以在下一次活动中瞄准这个年龄。
- 职业也可能对存款有影响,这也可以从数据中挖掘出来。所以,这也给营销人员一些提示。
- 从统计汇总中我们看到,持续时间越长(> 372 ~371.99),该目标群体开立定期存款账户的可能性越大。这个群体开立定期存款账户的可能性为 78%,这是相当高的。这将使得下一次营销活动的成功率非常高。
数据驱动的在旧金山哪里停车的决策

你有没有觉得在阴凉的地方停车不确定?特别是,您是否曾经在旧金山停车,并想知道,如果我测量 SFPD 去年记录的每起车辆事故的平均平方反比距离,我当前的位置会落在哪个百分点?
如果是这样,我们为那个开发了一个应用。在本帖中,我们将解释我们的方法及其实现。
旧金山停车
众所周知,与车辆相关的入室盗窃和盗窃在旧金山司空见惯。就在上周,价值 50 万美元的物品在一起高调的汽车盗窃案中被盗。甚至还有一个推特账号追踪事件。
旧金山警察局维护着自 2018 年 1 月 1 日以来所有事件的持续数据集(还有一个2003-2018)。《旧金山纪事报》由此创造了一个巨大的地图可视化来追踪非法闯入。我们希望让这些数据更具可操作性,以帮助实时评估特定位置的停车安全性。
因此,激励性的问题是:如果我想在旧金山停车,我怎么能感觉到我现在的位置有多安全?
定义风险分值
当然,停车位的风险可以用许多不同的定性和定量方法来衡量。我们选择了一个量化指标,无可否认是相当武断的,作为过去一年中停车地点和每个闯入地点之间距离的平均平方反比。

这只是给出一个数字分数。然后,我们评估整个旧金山停车点代表性样本的得分,并将当前停车点置于该样本中的百分位数。分数越高,地点离历史事件越*(距离的倒数),风险就越高。
我们将把这一切都包装在一个简单的移动网络应用程序中,这样你就可以从你的车上打开它,按下一个按钮,就可以得到你的停车位有多安全的百分比。

现在,我们只需使用数据来计算风险分值百分比。对于此任务,我们将把 SFPD 数据加载到 Rockset 集合中,并在用户单击按钮时查询它。
加载数据
为了快速开始,我们将简单地下载 CSV 格式的数据,并将文件上传到一个新的集合中。

稍后,我们可以设置一个定期作业,通过 API 将数据集转发到集合中,以便它总是保持最新。
过滤数据
让我们切换到 query 选项卡,尝试编写一个查询来过滤我们关心的事件。我们要检查几个条件:
- 车辆相关事故。每个事件都有一个由警察局犯罪分析股指定的“事件子类别”。我们在这个字段上做一个
SELECT DISTINCT查询,扫描结果,挑选出我们认为与车辆相关的。
机动车辆盗窃
机动车辆盗窃(未遂)
车内盗窃
盗窃罪—汽车零部件
盗窃——从车辆中

- 初次报告。根据数据文档,记录一旦归档就无法编辑,因此一些记录作为现有事件的“补充”归档。我们可以通过在报告类型描述中查找单词“Initial”来过滤掉它们。

- 顺丰内部。文件还规定一些事件发生在旧金山以外,这些事件在“警区”字段中的值为“旧金山以外”。

- 去年。数据集提供了一个日期时间字段,我们可以解析它并确保它在过去 12 个月内。

- 地理定位可用。我们注意到一些行缺少纬度和经度字段,取而代之的是一个空字符串。我们将通过过滤掉这些记录来忽略它们。
将所有这些条件放在一起,我们可以从这个数据集中的 242,012 条记录精简到 28,224 个相关的车辆事故,打包成一个WITH查询。

计算风险分数,一个点
现在我们有了去年的所有车辆事故,让我们看看是否可以计算旧金山市政厅的安全分数,该市政厅的纬度为 37.7793 N,经度为 122.4193 W。
使用一些古老的数学技巧(半径乘以弧度角度得到弧长,将弧长*似为直线距离,以及毕达哥拉斯定理),我们可以计算到每个过去事件的英里距离:

我们用上面的公式计算这些距离,瞧!

对于我们的应用程序,我们将用来自用户浏览器位置的参数替换市政厅的纬度/经度。
旧金山停车点示例
因此,我们可以计算出一个风险分数——市政厅为 1.63——但这毫无意义,除非我们可以将其与旧金山的其他停车点进行比较。我们需要在旧金山找到一组有代表性的所有可能的停车点,并计算每个停车点的风险分数,以获得风险分数的分布。
事实证明,SFMTA 正是我们所需要的——进行实地调查来统计路边停车位的数量,他们的结果作为开放数据集发布。我们也会把这个上传到 Rockset!

让我们看看这个数据集包含什么:

对于每条街道,让我们提取纬度/经度值(只是第一个点,足够接*的*似值)、点的计数和唯一标识符(根据需要转换类型):

计算风险分数,SF 中的每个点
现在,让我们试着计算每个点的分数,就像我们在上面对市政厅所做的一样:

我们做到了!旧金山每个街道段的停车风险分数。这是一个繁重的查询,所以为了减轻负担,我们实际上对每个街道和事件抽取了 5%的样本。
(即将推出 Rockset:geo-indexing——在接下来的几周内,请关注关于这方面的博客文章!)
让我们将这个查询的结果存储在另一个集合中,这样我们就可以用它来计算百分位数。我们首先创建一个新的空集合:

现在我们运行一个INSERT INTO sf_risk_scores SELECT ...查询,对事件和街道进行高达 10%的采样:

以百分位数对风险评分进行排序
现在,让我们根据插入到sf_risk_scores中的样本来获取市政厅的百分比。我们保留了最初的停车位分数计算方法,但现在也计算了比当前停车位更安全的采样停车位的百分比。

停车点风险评分即服务
现在我们有了一个可以说是有用的查询,让我们把它变成一个应用程序吧!
我们将保持简单——我们将创建一个 AWS Lambda 函数来服务两种类型的请求。根据GET的请求,它将服务于一个本地index.html文件,该文件充当 UI。对于POST请求,它将解析lat和lon的查询参数,并将它们作为上面最后一个查询中的参数传递。lambda 代码如下所示:
对于客户端,我们编写一个脚本来获取浏览器的位置,然后调用后端:
我们添加了一些基本的 HTML 元素和样式,它已经准备好了!
最后,我们添加 API Gateway 作为 lambda 的触发器,并将 Rockset API 密钥放入环境中,这些都可以在 AWS 控制台中完成。
结论
总结一下我们在这里所做的事情:
- 我们采用了两个相当简单的数据集——一个是 SPFD 报告的事件,一个是 SFMTA 报告的停车点——并将数据加载到 Rockset 中。
- 经过几次 SQL 迭代之后,我们有了一个 API,可以调用它来获取给定地理位置的风险分数。
- 我们在 AWS Lambda 中编写了一些简单的代码,作为移动 web 应用程序。
唯一需要的软件是一个 web 浏览器(下载数据,在 Rockset 控制台中查询,在 AWS 控制台中部署),从构思到生产,总共不到一天就完成了。lambda 的源代码可在这里获得。
如果你在旧金山停车,请在这里随意试用应用程序。如果不是,请评论你认为哪些现实生活中的决策可以通过更多的数据来改进!
【https://rockset.com】最初发表于。
了解你的衡量标准

使用 Python 实现数据驱动的增长
了解使用 Python 追踪什么以及如何追踪
简介
这一系列文章旨在解释如何以一种简单的方式使用 Python,通过将预测方法应用于您的所有行动来推动您公司的发展。它将是编程、数据分析和机器学*的结合。
我将在以下九篇文章中讨论所有主题:
1-了解你的衡量标准
2- 客户细分
3- 客户终身价值预测
4- 流失预测
文章将有自己的代码片段,使您可以轻松地应用它们。如果你是编程的超级新手,你可以在这里很好地介绍一下 Python 和 Pandas (一个我们会在任何事情上使用的著名库)。但是仍然没有编码介绍,您可以学*概念,如何使用您的数据并开始从中产生价值:
有时候你必须先跑,然后才能走——托尼·斯塔克
作为先决条件,确保你的电脑上安装了 J upyter Notebook 和 P ython 。代码片段只能在 Jupyter 笔记本上运行。
好吧,我们开始吧。
第 1 部分:了解您的度量标准

我们都记得斯派洛船长著名的指南针,它显示了他最想要的东西的位置。没有北极星指标,这就是我们的增长情况。我们想要更多的客户、更多的订单、更多的收入、更多的签约、更高的效率…
在进入编码之前,我们需要理解什么是北极星度量标准。如果你已经知道并跟踪它,这篇文章可以帮助你用 Python 做一个深入的分析。如果你不知道,首先你应该找到你的(可能你已经在追踪它但没有在概念上将其命名为北极星)。这是肖恩·埃利斯对它的描述:
北极星指标是单一指标,最能抓住你的产品向客户传递的核心价值。
该指标取决于贵公司的产品、定位、目标等。Airbnb 的北极星指标是预订的夜晚,而脸书的指标是每日活跃用户。
在我们的例子中,我们将使用在线零售的样本数据集。对于在线零售商,我们可以选择北极星指标作为月收入。让我们看看我们的数据在 jupyter 笔记本上是什么样的。
月收入
让我们从导入我们需要的库开始,并在 pandas 的帮助下从 CSV 读取我们的数据:
我们的数据看起来是这样的:

我们有所有需要的关键信息:
- 客户 ID
- 单价
- 量
- 发票日期
有了所有这些特征,我们可以建立我们的北极星度量方程:
收入 =活跃客户数订单数平均每单收入
是时候把手弄脏了。我们希望看到每月的收入,但不幸的是没有免费的午餐。让我们设计我们的数据:
干得好,现在我们有了一个显示我们每月收入的数据框架:

下一步,可视化。线图就足够了:
Jupyter 笔记本输出:

这清楚地表明我们的收入正在增长,尤其是从 8 月 11 日开始(我们 12 月的数据不完整)。绝对数字没问题,让我们算出我们的月收入增长率是多少:

一切看起来都很好,我们看到上个月有 36.5%的增长(12 月被排除在代码之外,因为它尚未完成)。但我们需要确定 4 月 16 日到底发生了什么。是因为不太活跃的客户还是我们的客户订单少了?也许他们只是开始购买更便宜的产品?没有做深层次的分析,我们什么都不能说。
每月活跃客户
要查看每月活跃客户的详细信息,我们将遵循我们对每月收入所做的步骤。从这一部分开始,我们将只关注英国的数据(记录最多)。我们可以通过计算唯一的客户 id来获得月活跃客户。代码片段和输出如下:
每月活跃客户数量及其柱状图:


4 月份,月活跃客户数量从 923 个下降到 817 个(-11.5%)。
我们也将看到订单数量的相同趋势。
每月订单计数
我们将通过使用数量字段应用相同的代码:
每月订单数量及其条形图:


正如我们所料,4 月份订单数量也有所下降(从 279,000 件降至 257,000 件,降幅为 8%)
我们知道活跃客户数量直接影响订单数量的减少。最后,我们还应该明确检查我们的每份订单的平均收入。
平均每单收入
要获得这些数据,我们需要计算每个月的平均收入:
每个订单的月平均收入及其条形图:


甚至 4 月份的月平均订单量也有所下降(从 16.7 降至 15.8)。我们观察到影响我们北极星的每一个指标都变慢了。
我们已经查看了我们的主要指标。当然,还有更多的问题,并且因行业而异。让我们继续研究一些其他重要的指标:
- 新客户比率:一个很好的指标,表明我们是否正在失去现有客户或无法吸引新客户
- 保留率:指标之王。指明我们在特定时间窗口内保留了多少客户。我们将展示每月保留率和基于群组的保留率的示例。
新客户比率
首先我们应该定义什么是新客户。在我们的数据集中,我们可以假设新客户是在我们定义的时间窗口内第一次购物的人。对于这个例子,我们将每月做一次。
我们将使用。min() 函数查找每个客户的首次购买日期,并基于此定义新客户。下面的代码将应用这个函数,并显示每个组每月的收入明细。
与首次购买日期合并后的数据帧输出:

新客户和现有客户的每月收入:

上面的折线图:

现有客户显示出积极的趋势,并告诉我们,我们的客户群正在增长,但新客户有轻微的消极趋势。
让我们更好地了解一下新的客户比率:

新客户比率如预期下降(我们在 2 月假设所有客户都是新客户),大约为 20%。
每月留存率
留存率应该被密切监控,因为它表明你的服务有多粘,你的产品有多适合市场。为了使每月的保留率可视化,我们需要计算上个月保留了多少客户。
月留存率 =前期留存的客户。月/活跃客户总数
我们将使用 pandas 的 crosstab() 函数,这使得计算留存率变得非常容易。
首先,我们创建一个数据框架,显示每个客户的月总收入:

crosstab() 函数将其转换为保留表:

保留表显示了每个月哪些客户是活跃的(1 代表活跃)。
在循环的简单的帮助下,我们每月计算上个月的保留客户数和总客户数。
最后,我们得到了保留率数据框架和折线图,如下所示:


从 6 月到 8 月,每月保留率显著上升,之后又回到以前的水平。
基于群组的保留率
还有另一种测量保留率的方法,可以让您看到每个群组的保留率。分组被确定为客户的第一个购买年月。我们将测量每个月第一次购买后保留的客户百分比。此视图将帮助我们了解新旧客户群在保留率方面的差异,以及最*客户体验的变化是否会影响新客户的保留率。
就编码而言,这将比其他的要复杂一点。
Tx_retention 对基于群组的保留率有着惊人的看法:

我们可以看到,第一个月的保留率最*变得更好了(不考虑 2011 年 12 月),在* 1 年的时间里,我们有 15%的客户保留了下来。
最后…我们知道我们的指标以及如何使用 Python 来跟踪/分析它们。
你可以在这里找到这篇文章的 jupyter 笔记本。
让我们试着细分我们的客户群,看看谁是我们在第二部分的最佳客户。
需要帮助来发展你的公司吗?点击这里与我一起预订免费课程。
客户细分

使用 Python 实现数据驱动的增长
RFM 聚类分割
简介
这一系列文章旨在解释如何以一种简单的方式使用 Python,通过将预测方法应用于您的所有行动来推动您公司的发展。它将是编程、数据分析和机器学*的结合。
我将在以下九篇文章中讨论所有主题:
1- 了解你的指标
2-客户细分
3- 客户终身价值预测
4- 流失预测
文章将有自己的代码片段,使您可以轻松地应用它们。如果你是编程的超级新手,你可以在这里很好地介绍一下 Python 和 Pandas (一个我们会在任何事情上使用的著名库)。但是仍然没有编码介绍,您可以学*概念,如何使用您的数据并开始从中产生价值:
有时候你得先跑,然后才能走——托尼·斯塔克
作为先决条件,确保你的电脑上安装了 J upyter Notebook 和 P ython 。代码片段只能在 Jupyter 笔记本上运行。
好吧,我们开始吧。
第 2 部分:客户细分
在的上一篇文章中,我们分析了我们在线零售业务的主要指标。现在我们知道了使用 Python 追踪什么以及如何追踪。是时候关注客户并对他们进行细分了。
但是首先,我们为什么要做细分?
因为你不可能用同样的内容,同样的渠道,同样的重要性,来对待每一个客户。他们会找到更理解他们的另一种选择。

使用您平台的客户有不同的需求,他们有自己不同的概况。你应该据此调整你的行动。
你可以根据你想要达到的目标做许多不同的细分。如果你想提高保留率,你可以做一个基于流失概率的细分,并采取行动。但是也有非常常见和有用的分割方法。现在我们将在我们的业务中实现其中的一个: RFM。
RFM 代表*期频率货币价值。理论上,我们将拥有如下细分市场:
- 低价值:不太活跃的客户,不是非常频繁的买家/访客,产生非常低-零-可能是负收入。
- 中值:一切事物的中间。经常使用我们的平台(但没有我们的高价值),相当频繁,产生中等收入。
- 高价值:我们不想失去的群体。高收益,高频率,低闲置。
作为方法论,我们需要计算新*性、频率和货币价值(我们从现在开始称之为收入)并应用无监督机器学*来为每个识别不同的组(群)。让我们开始编码,看看如何进行 RFM 聚类。
崭新
为了计算最*,我们需要找出每个客户最*的购买日期,并查看他们有多少天没有活动。在获得每个客户的非活动天数后,我们将应用 K-means聚类为客户分配一个最*得分*。
对于这个例子,我们将继续使用相同的数据集,可以在这里找到。在进入*期计算之前,让我们回顾一下我们之前做过的数据工作。
现在我们可以计算新*度:
我们的新数据帧 tx_user 现在包含最*的数据:

为了快速了解最*的情况,我们可以使用熊猫的。形容()方法。它显示我们数据的平均值、最小值、最大值、计数和百分位数。

我们看到,即使平均值为 90 天,中位数为 49 天。
我们上面的代码片段有一个直方图输出,向我们展示了最*在我们客户中的分布情况。

现在是有趣的部分。我们将应用 K 均值聚类来分配一个最*得分。但是我们应该告诉 K-means 算法需要多少个聚类。为了找出答案,我们将采用肘法。肘方法简单地告诉最优惯性的最优簇数。代码片段和惯性图如下:
惯性图:

这里看起来 3 是最佳选择。根据业务需求,我们可以增加或减少集群数量。在本例中,我们将选择 4:
我们已经计算了集群并将它们分配给数据帧 tx_user 中的每个客户。

我们可以看到我们最*的聚类有不同的特征。与聚类 2 相比,聚类 1 中的客户是最*的客户。
我们在代码中添加了一个函数 order_cluster() 。K-means 以数字的形式分配聚类,但不是以有序的方式。我们不能说集群 0 是最差的,集群 4 是最好的。order_cluster()方法为我们做到了这一点,我们的新数据帧看起来更加整洁:

太好了!3 代表最*的客户,而 0 代表最不活跃的客户。
让我们对频率和收入应用同样的方法。
频率
为了创建频率聚类,我们需要找到每个客户订单总数。首先计算一下,看看频率在我们的客户数据库中是什么样的:

对频率群集应用相同的逻辑,并将其分配给每个客户:
我们的频率群集的特征如下所示:

与最*聚类的符号相同,高频率数字表示更好的客户。
收入
让我们看看,当我们根据收入对客户进行聚类时,我们的客户数据库是什么样子。我们将计算每个客户的收入,绘制直方图,并应用相同的聚类方法。

我们也有一些负收益的客户。让我们继续应用 k 均值聚类:

总分
厉害!我们有最*、频率和收入的分数(聚类数)。让我们为它们创建一个总分:

上面的评分清楚地告诉我们,得分为 8 的客户是我们最好的客户,而得分为 0 的客户是最差的。
为了简单起见,我们最好将这些分数命名为:
- 0 到 2:低值
- 3 到 4:中间值
- 5+:高值
我们可以很容易地在数据帧上应用这种命名:
现在,这是最好的部分。让我们看看我们的细分市场是如何分布在散点图上的:



就 RFM 而言,你可以看到这些细分市场是如何明显区分开来的。您可以在下面找到图形的代码片段:
我们可以开始对这一细分采取行动。主要策略非常明确:
- 高价值:提高保留率
- 中值:提高保留率+增加频率
- 低值:增加频率
越来越精彩了!在第 3 部分中,我们将计算和预测客户的终身价值。
你可以在这里找到这篇文章的 jupyter 笔记本。
*理想情况下,我们在这里所做的事情可以通过使用分位数或简单宁滨(或 Jenks 自然间断优化以使分组更准确)轻松实现,但我们正在使用 k-means 来熟悉它。
需要帮助来发展你的公司吗?点击这里,与我一起预订免费课程。
客户终身价值预测

使用 Python 实现数据驱动的增长
基于 XGBoost 多分类的 LTV 预测
简介
这一系列文章旨在解释如何以一种简单的方式使用 Python,通过将预测方法应用于您的所有行动来推动您公司的发展。它将是编程、数据分析和机器学*的结合。
我将在以下九篇文章中讨论所有主题:
1- 了解你的指标
2- 客户细分
3- 客户终身价值预测
4- 流失预测
文章将有自己的代码片段,使您可以轻松地应用它们。如果你是编程的超级新手,你可以在这里很好地介绍一下 Python 和 Pandas (一个我们会在任何事情上使用的著名库)。但是仍然没有编码介绍,您可以学*概念,如何使用您的数据并开始从中产生价值:
有时候你得先跑,然后才能走——托尼·斯塔克
作为先决条件,确保你的电脑上安装了 J upyter Notebook 和 P ython 。代码片段只能在 Jupyter 笔记本上运行。
好吧,我们开始吧。
第 3 部分:客户终身价值
在之前的文章中,我们对我们的客户进行了细分,并找出了谁是最好的客户。现在是时候衡量我们应该密切跟踪的最重要的指标之一:客户终身价值。
我们投资客户(收购成本、线下广告、促销、折扣等)。)来产生收入,实现盈利。自然,这些行为使一些客户在终身价值方面变得非常有价值,但总有一些客户会降低盈利能力。我们需要识别这些行为模式,细分客户并采取相应的行动。
计算一生的价值是容易的部分。首先,我们需要选择一个时间窗口。可能是 3 个月、6 个月、12 个月或 24 个月。通过下面的等式,我们可以得到特定时间窗口内每个客户的终身价值:
生命周期价值:总毛收入-总成本
这个等式现在给出了历史寿命值。如果我们看到一些客户在历史上拥有非常高的负终身价值,采取行动可能为时已晚。此时,我们需要用机器学*来预测未来:

我们将建立一个简单的机器学*模型,预测我们客户的终身价值。
终生价值预测
在这个例子中,我们将继续使用我们的在线零售数据集。让我们确定通往荣耀的道路:
- 为客户终身价值计算定义适当的时间框架
- 确定我们将要用来预测未来和创造未来的特征
- 计算用于训练机器学*模型的终身价值(LTV)
- 构建并运行机器学*模型
- 检查模型是否有用
决定时间框架真的取决于你的行业、商业模式、战略等等。对某些行业来说,一年是很长的时间,而对其他行业来说,一年又很短。在我们的示例中,我们将继续进行 6 个月。
每个客户 ID 的 RFM 分数(我们在上一篇文章中计算过)是特征集的完美候选。为了正确地实现它,我们需要分割数据集。我们将采用 3 个月的数据,计算 RFM,并用它来预测未来 6 个月。因此,我们需要首先创建两个数据帧,并将 RFM 分数附加到它们上。
我们已经创建了我们的 RFM 评分,现在我们的功能集如下所示:

我不会重复 RFM 得分的细节,因为我会重复第二部分。
既然我们的功能集已经准备好了,让我们为每个客户计算 6 个月的 LTV,我们将使用它来训练我们的模型。
数据集中没有指定成本。这就是为什么收入直接成为我们的 LTV。
此代码片段计算 LTV 并绘制其直方图:

柱状图清楚地显示我们有负 LTV 的客户。我们也有一些异常值。过滤掉离群值对于拥有合适的机器学*模型是有意义的。
好,下一步。我们将合并 3 个月和 6 个月的数据框架,以查看 LTV 和我们拥有的特征集之间的相关性。
下面的代码合并了我们的功能集和 LTV 数据,并绘制了 LTV 与 RFM 的总得分:

正相关在这里相当明显。高 RFM 分数意味着高 LTV。
在建立机器学*模型之前,我们需要确定这个机器学*问题的类型。LTV 本身就是一个回归问题。机器学*模型可以预测 LTV 的价值。但在这里,我们希望 LTV 部分。因为这样更有可操作性,也更容易与其他人交流。通过应用 K-均值聚类,我们可以识别现有的 LTV 组,并在此基础上构建细分市场。
考虑到这个分析的业务部分,我们需要根据客户的预测 LTV 来区别对待他们。在本例中,我们将应用聚类,并有 3 个细分市场(细分市场的数量实际上取决于您的业务动态和目标):
- 低 LTV
- LTV 中部
- 高 LTV
我们将应用 K-means 聚类来决定细分并观察它们的特征:
我们已经完成了 LTV 聚类,下面是每个聚类的特征:

2 是最好的,平均 LTV 为 8.2k,而 0 是最差的,平均为 396。
在训练机器学*模型之前还有几个步骤:
- 需要做一些功能工程。我们应该将分类列转换成数字列。
- 我们将对照我们的标签“LTV 聚类”来检查特征的相关性。
- 我们将分割我们的特征集并将(LTV)标记为 X 和 y。我们使用 X 来预测 y
- 将创建训练和测试数据集。训练集将用于建立机器学*模型。我们将把我们的模型应用到测试集上,看看它的真实性能。
下面的代码为我们做了这一切:
让我们从第一行开始。 get_dummies() 方法将分类列转换为 0–1 表示法。看看它在这个例子中到底做了什么:

这是我们在 get_dummies()之前的数据集。我们有一个分类列,它被分段。应用 get_dummies()后会发生什么:

段列没有了,但我们有新的数字来表示它。我们已经将其转换为 3 个不同的列,分别为 0 和 1,并使其可用于我们的机器学*模型。
与相关性相关的线条使我们有了下面的数据:

我们看到 3 个月的收入、频率和 RFM 分数将有助于我们的机器学*模型。
因为我们有了训练集和测试集,所以我们可以构建我们的模型。
我们使用了一个非常强大的 ML 库 XGBoost 来为我们进行分类。自从我们有了 3 个组(集群)以来,它已经成为一个多分类模型。让我们来看看最初的结果:

在测试集上准确率显示为 84%。看起来真不错。是吗?
首先,我们需要检查我们的基准。我们拥有的最大集群是集群 0,占总基数的 76.5%。如果我们盲目地说,每个客户都属于聚类 0,那么我们的准确率将是 76.5%。
84%对 76.5%告诉我们,我们的机器学*模型是一个有用的模型,但肯定需要一些改进。我们应该找出模型失败的地方。
我们可以通过查看分类报告来确定这一点:

精度和召回率对于 0 是可接受的。例如,对于聚类 0(低 LTV),如果模型告诉我们该客户属于聚类 0 ,则 100 分中有 90 分是正确的(精度)。该模型成功识别了 93%的实际 0 类客户(回忆)。我们真的需要为其他集群改进模型。例如,我们几乎没有发现 56%的 LTV 中部客户。改进这些要点的可能行动:
- 添加更多功能并改进功能工程
- 尝试 XGBoost 以外的其他型号
- 将超参数调整应用于当前模型
- 如果可能,向模型中添加更多数据
太好了!现在,我们有了一个机器学*模型,可以预测我们客户的未来 LTV 细分市场。基于此,我们可以很容易地调整我们的行动。例如,我们绝对不希望失去 LTV 高的客户。因此,我们将在第 4 部分中重点关注客户流失预测。
你可以在这里找到这部分的 jupyter 笔记本。
需要帮助来发展你的公司吗?点击这里与我一起预订免费课程。
数据驱动的营销归因
基于合作博弈的顾客归因

Working together for the win
营销归因的问题
流行的营销归因模式各有利弊,选择最佳模式取决于个人业务需求。然而,流行模型的一个共同缺点是,它们是基于规则的,用户必须预先决定他们希望如何在渠道之间分配销售转换的积分。受欢迎的型号包括:
- 线性:在所有接触点之间分配相等份额的回报
- 时间衰减:随着时间的推移,接触点与转化日期的距离越远,回报的百分比越低
- 位置:第一次和最后一次触摸占 40%,剩下的 20%平均分配给两次触摸之间的部分
首席营销官使用所选模型的结果来衡量投资回报率,并就未来在何处投资营销资源做出更明智的决策。准确的结果很重要,但这些启发式解决方案不够灵活,无法区分真正的低影响和高影响接触点,导致信用划分不准确。
幸运的是,有更复杂的、数据驱动的方法来解决这些限制。数据驱动归因是一种定制解决方案,能够通过模拟渠道(更重要的是不同渠道组合)如何与买家互动以影响预期销售结果,来捕捉买家旅程的复杂性。数据驱动的模型提供了关于哪些渠道表现最佳的最准确的视图,推动了更好的营销责任和效率。
这篇文章探索了一个数据驱动的归因模型,该模型基于合作博弈理论中的 Shapley 值概念。
博弈论与沙普利值
在一个多玩家的游戏中,他们可以一起工作(形成联盟)以增加期望结果(收益)的可能性,Shapley 值提供了一种在玩家之间公平分配收益的方法。
本质上,Shapley 值是一个玩家对每个联盟的平均边际贡献的度量。考虑到玩家可以在不同的时间点(顺序)加入联盟,拥有不同程度的影响力(价值)。它基于这样一个假设,即每个排序都有相同的发生概率,因此玩家根据他们对所有排列的贡献获得奖励。
Rovira i 维尔吉利大学[1]和加州大学洛杉矶分校[2]发表的论文提供了 Shapley 值及其公理的优秀形式定义,这超出了本文的范围。
在营销分析的背景下,营销渠道是游戏的参与者,渠道在整个购买过程中与客户互动的各种方式形成了联盟。合作博弈理论和 Shapley 值提供了一种稳定的方法来衡量渠道影响力,并根据渠道对总回报的贡献,在渠道之间公平划分销售转化的功劳。
营销优势:
- 更深入地了解渠道绩效
- 根据衡量的贡献公平分配信贷
- 优化营销投资和影响销售结果的能力
- Shapley value 是一个广泛使用的、获得诺贝尔奖的解决方案( Google Analytics 将其用于频道归属)
特征函数
一个游戏由一组玩家 N 和一个特征函数 v 定义。玩家的每个子集被称为联盟 S ,特征函数 v(S) 为每个联盟分配一个值,以表示其值。一个联盟的价值代表了当它的成员一起工作时它所能产生的回报。
定义营销特征函数的选项包括:
- 每个联盟产生的总收入
- 每个联盟产生的销售转换总数
- 每个联盟的转换率(转换率/机会)
- 转换的条件概率—转换给定频道集的可能性
转换率示例
让我们看一个使用通道转换率的例子。假设您的公司在一个财政季度末转化了 100 个机会。在此期间,营销部门通过三种渠道向相关客户做广告:
N = { 脸书,谷歌, LinkedIn }
所有 100 个客户在购买过程中都被一个或多个渠道接触过。换句话说,这些渠道通过形成联盟来增加机会转换的可能性。
下表列出了所有可能的渠道联盟及其转换率:

Example coalition conversion ratios
每个联盟的价值由特征函数决定。在这个例子中,价值被表示为联盟中每个渠道的转换率之和。
- 联盟 S5 =脸书+LinkedIn
- v(S5) =脸书(S1) +领英(S3) +脸书+领英(S5)
- v(S5) = 0.18 + 0.08 + 0.26
- v(S5) = 0.52

Coalition worth
包含所有玩家的联盟被称为大联盟 v(N) 。大联盟的价值应该等于总收益。
现在我们知道了每个联盟的价值,Shapley 值可以通过平均每个渠道对游戏的边际贡献来计算,考虑所有可能的排序。具体来说,Shapley 值为我们提供了一种在三个渠道之间分配大联盟价值(总收益)的方法。
脸书的沙普利值:

Facebook’s Shapley value
- 在命令 1 和 2 中,脸书是第一个到达的,因此它收到了它的全部贡献
- 在顺序 3 中,脸书排在谷歌之后,所以它的边际贡献是包含脸书谷歌 v(S4)的联盟减去没有脸书 v(S2)的联盟
- 在命令 4 和 6 中,脸书最后到达,因此其边际贡献是包含所有信道的联盟 v(S7)减去没有脸书的联盟 v(S6)
- 在顺序 5 中,脸书排在领英之后,所以它的边际贡献是包含脸书领英 v(S5)的联盟减去没有脸书 v(S3)的联盟
所有通道的 Shapley 值:

Shapley values
这个例子的代码可以在 Github 上的这个 Jupyter 笔记本中找到。以下代码片段展示了如何在 Python 中计算 Shapley 值:
结论
从这个例子中的 Shapley 值可以清楚地看出,脸书是表现最好的渠道,而脸书和 LinkedIn 的组合是最有影响力的联盟。CMO 可能会看到这一点,决定向脸书和 LinkedIn 分配更多资源,以优化转化率。他们也可能会质疑为什么谷歌表现不佳,并投入更多资源来改善谷歌广告上的活动。

Shapley values
总之,随着企业努力提高责任性、效率和数据驱动的决策,合作博弈论和 Shapley value 为营销部门提供了准确和定制的归因解决方案,这种解决方案有可能提供比基于规则的模型所能提供的更多的东西。
参考
- [1] S .卡诺·贝朗加和 JM。Giménez Gómez 和 C. Vilella,归因模型和合作博弈理论 (2017),罗维拉维尔吉利大学
- [2] T. Ferguson,博弈论 (2014),加州大学洛杉矶分校数学系
- [3] R. Affane,营销归因 (2018),来自战壕的数据—大台库
虚假数据网络中的数据驱动营销

经理、营销人员和其他未经培训的专业人士滥用数据科学是我这一代人的标志。樱桃采摘、确认偏差、小得离谱的样本量——这些都是显而易见的错误。作为数字营销人员,我们需要开始怀疑互联网时代的数据,而不只是看到我们处理这些数据的错误。
旧学校数据
一代人以前,公司求助于尼尔森等第三方来获得有多少眼球可能会看到广告的数据。像尼尔森这样的公司没有撒谎的动机。另一方面,销售广告的网络,无论是传统的广播公司还是脸书,都有充分的理由增加对自己有利的数字。
即使我们将传统印刷出版物与谷歌或脸书相比,也更容易核实售出了多少份报纸。你永远无法确切知道有多少人看到了你藏在 B3 页面上的广告,但如果你的目标受众倾向于浏览 B3 页面,焦点小组可以给你一个想法。
测量东西过去很贵。你没有雇佣一家市场调查公司来编造一堆无用的数字。这为数据主义提供了一个强大的抑制因素。
没有必要将《广告狂人》时代浪漫化,但我们仍然可以反思一下,旧的数据处理方法对今天的营销人员是否有价值。
不要轻信自我报告的数据
我没有理由指责脸书或谷歌彻头彻尾的谎言——至少在某种意义上,他们的分析报告了一件事,但他们向潜在客户展示了不同的数字。当你控制了叙述,就没有理由如此明目张胆地撒谎。
我的简历没有提到我在 Reddit 上花了多少时间在工作上,我搞砸的项目或者连我妈妈都不会看的博客帖子。我的简历上没有一点是假的;这只是不全面的情况。任何时候你可以选择提供什么数据,你这样做。没有人会拍五张照片,然后选择最差的一张来分享。这没有错,但社交媒体或 CV 就是这样没有给出全貌的。
这就是为什么数据需要通过一个无私的第三方来发布,而不仅仅是亮点。在医学领域,随着制药公司资助研究,这已经成为一个严重的问题。如果你进行足够多的研究,其中一项最终会向展示一些你的营销部门可以使用的东西。当研究没有正面描述你的产品时:不要发表它们。
在循证医学腐败中提出的问题适用于任何领域。例如:
阴性试验(那些显示对药物没有益处的试验)可能会被禁止。例如,在抗抑郁药的情况下,36/37 的有利于药物的研究被发表。但是在不利于药物的研究中,只有微不足道的 3/36 被发表。选择性发表阳性结果(对制药公司而言)意味着对文献的回顾表明 94%的研究支持药物,而事实上只有 51%是阳性的。
即使你相信谷歌或脸书的话,他们也不会向你展示让他们看起来很糟糕的数据。每一家试图用自报数据欺骗你的 SaaS 公司都落入了同样的陷阱。无标记推广内容、原生广告和联盟营销的盛行让我对网上发布的任何数据都持怀疑态度——即使这些数据来自一个“中立”的评论网站。
当公司不赚钱时,利润突然不再是一个重要的衡量标准。看参与度和月活跃用户!你不必撒谎来掩盖真相。这是在控制叙事。
真实数据一塌糊涂
无意义的统计数据很容易获得。统计一个服务器生成一个网页的次数是微不足道的。这个数字不会告诉你你的目标受众中有多少人阅读了你的内容。即使有无穷无尽的验证码,仍然不清楚访客是普通人、机器人还是点击农场工人。
一旦计算机吐出一个数字,就不可能与的管理类型就这个数字进行理性的讨论。你会立刻被指责为勒德分子或者根本不知道自己在说什么。
这与大型科技公司的现实形成了鲜明对比。谷歌、亚马逊和 FB 让世界上最好的数据科学家从在线指标中获得干净的数据,但仍然遇到问题。
越来越接*彻头彻尾的谎言
当你深究互联网有多少是假的时,这些数字令人震惊:
研究普遍表明,年复一年,只有不到 60%的网络流量是人类的;据一些研究人员称,有些年,健康的大多数是机器人。
更荒谬的是:
今年我最喜欢的统计数据是脸书声称 7500 万人每天至少观看一分钟的 Facebook 视频——尽管,正如脸书承认的那样,这一分钟里的 60 秒不需要连续观看。真视频,真人,假分钟。
换句话说,当你浏览你的订阅源时,FB 将后台自动播放的视频算作被观看。广播电视网根本不能这样操纵数字。
风险资本指标对大多数企业来说是不相关的
互联网和风投系统挖掘出了一大堆毫无意义的指标。你真的不应该关心参与度、社交媒体点赞或其他容易被游戏化的指标。如果没人做他们的产品,一家公司有多少 Twitter 粉丝有关系吗?
追逐不会给你带来付费客户的指标太容易了。在更大的公司中,这是可以理解的,因为仍然有远离实际产品的业务关键职位。
在小公司里,自我和错觉扭曲了现实。喜欢被关注和受欢迎是我们大脑的固有属性。因此很容易偏离主题。进化还没有选择偏好利润而不是虚荣的标准。
当创业公司追逐风险投资资金时,数据问题只会变得更加复杂。清除不活跃的用户,忽略 bot 流量,或者过滤掉噪音,这不符合任何人的利益。当你花别人的钱时,盈利能力并不重要,你的数据永远不会被第三方审计。可能会出什么问题?
你真正可以信任的数据
后退一步,看看那些不太可能被捏造的指标。如果有人用你的链接买东西,那可能是真的。如果一个客户告诉你,他们是因为一篇博客帖子找到你的公司的,那就是诚实的反馈。转化率和销售额应该是一天结束时唯一重要的指标。
社交媒体份额和订户可能与一家公司的转化率相关,也可能不相关——我写的博客帖子已经在社交媒体上疯传,但却没有带来任何客户。一个好的企业博客是昂贵的:开发者、内容作者和设计师都不便宜。你必须拉动大量的销售,以证明这些成本的合理性。
对你能衡量的东西和你能衡量的有多好持怀疑态度。重视个人的和定性的反馈。使用数据来确定需要更多关注的问题,而不是使用指标本身。忽略虚荣指标,关注底线会让你成为更好的营销人员。
我不是数据虚无主义者。一旦营销人员穿越了糟糕的数据科学和虚假数据的雷区,他们就可以使用大量有意义的数据。
最初发表于T5【derekkedziora.com】。
影响业务和投资的数据驱动原因
这种影响不仅关系到世界,也关系到你的利润。
企业是现代世界的领导者。创造的文化和采取的行动会对周围的世界产生影响。始终牢记社会影响是非常重要的。采取行动对社区产生积极影响。

Connected world. Photo by Pixabay.
在讨论商业的未来时,我们应该理解 X 世代、千禧世代和 Z 世代在全球经济中的角色。在接下来的 30 年里,估计有 30 万亿美元的财富将从婴儿潮一代转移到 x 一代和千禧一代。— 福布斯【2018 年 8 月
作为工作者和消费者的 X 一代、千禧一代和 Z 一代
X 一代、千禧一代和 Z 一代在工作、建设和消费。X 一代正处于收入和消费能力最高的时代。千禧一代和 Z 世代紧随其后。他们已经占了总人口的一半以上,所以他们以数量取胜。

Photo by Pixabay.
“根据彭博对联合国数据的分析,Z 世代将占 2019 年全球 77 亿人口的 32%,略高于千禧一代,后者将占 31.5%,并以 2000/2001 年为代际划分。”— 彭博2018 年 8 月 20 日。
X 一代、千禧一代和 Z 一代的行动与你的企业成功紧密相连。X 世代和千禧一代让我们开始考虑消费和工作的影响。他们想找到对我们所有人都有益的方法。它已经升级为一场运动。Z 世代现在高度意识到周围的世界和我们的影响。
“除了年龄,区分这两个群体的关键因素是自我意识和自我中心,”安永 LLP 公司执行董事玛茜·梅里曼的报告《Z 世代的崛起:零售商的新挑战》称。千禧一代“更关注对他们有什么好处。他们也会向其他人寻求解决方案,比如与他们有业务往来的公司,而年轻人自然会寻找自己的解决方案。”— 彭博2018 年 8 月 20 日。
注意!
人们分成不同的群体。这是简化学*和研究的典型方式。然而,请记住,我们都是独立的个体。
数据显示了正在兴起并影响整个世界的趋势。变量使我们能够总结和衡量。
“公司应该适应这一代人的三个含义:消费是获取而非占有,消费是个人身份的表达,消费是道德问题。”— 麦肯锡 2018 年 11 月
很容易从技术、政治和商业的变化中看出。有必要向透明和诚实转变。言语是不够的。
你可能熟悉这样的新闻:
“为什么标签透明度对千禧一代的品牌忠诚度至关重要”——福布斯 2017
“他们知道这些 YouTubers 是付费的,但品牌/名人互动的个人联系和透明度可以抵消营销人员对使用这一工具的任何担忧。— 福布斯2018 年 7 月 23 日。
“商业透明度:建立信任的 5 种方法”——福布斯2019 年 4 月 3 日。

当连接到网络世界时,信息流是持续的。信息和数据正在流逝。年轻一代就像代理人,可以接触到以前作为商业秘密的知识。以身作则,言出必行。信息是,要诚实。
从下面的文章中了解更多关于千禧一代和 Z 世代及其行动的信息:
千禧一代想要透明度和社会影响力。 — 企业家
千禧一代与 Z 世代:他们与众不同的 5 个方面: — 销售队伍
“他们可能看起来都很年轻。但是千禧一代和后来的 Z 世代成员在购物、与品牌互动和看待金钱的方式上有明显的不同
这与千禧一代青睐的影响力和品牌有关系。
千禧一代公布他们喜爱的 100 大品牌——商业内幕
他们的消费能力预计今年将超过 3.39 万亿美元

没有社会影响力的公司会落后
消费者可以投票。他们离开自己不喜欢的企业。互联网的最大好处是,你的声音在几分钟内就能被听到。一个受到特定业务影响的人通过社交媒体大声疾呼。结果往往是大规模的检查。迫于公众采取相应的行动。
始终牢记影响来获取利润。数据证明了你的商业影响力。它会让你在未来的几年里继续做生意。这里有一个词送给你,冲击。
让你知道社交媒体有多难。以下是 2017 年至 2019 年的几个失败案例:
2017 年 7 大社交媒体败笔——企业家2017 年 5 月 30 日
2018 年最大社交媒体败笔 —福布斯2018 年 11 月 27 日
2019 年商业错误最糟糕的社交媒体(以及你可以从中吸取的教训)——重振社交2019 年 3 月 18 日
25 大社交媒体失败:互联网上最严重的罪行——社交能力2019 年 10 月 29 日
我们要求人们善良并互相帮助。对于企业,我们全球社会的领导者来说,这更为重要。除此之外,随着当局提高企业的领导地位,政府和政治参与者似乎正在失去兴趣。比特币等技术的兴起告诉我们,人们正在寻找世界范围内的社区。比特币(又名加密货币)就像一个拥有价值观的全球社区。自由、个人数据所有权和开放数据等价值观。我们看到一些社区的崛起,在这些社区中,国家的边界并不意味着什么。另一方面,人们正在追随,例如,脸书、亚马逊、苹果和所有公司的行动。人们跟随、讨论、甚至证明他们的行为是基于企业如何引领潮流。例如,最*的法律案件和围绕脸书数据使用的噪音告诉我们,大公司是领导者,他们的行为很重要。嗯,社交媒体平台有更大的责任,因为他们展示的东西可以影响人们的想法。
政府和政治正失去对社区和企业的一些权力。周围所有的噪音表明,作为一个公司,你是一个领导者。当世界联系在一起的时候,你对人们的影响更大了。要负责任。未来需要我们进行有影响力的业务和投资。
参见: 9 个在你的企业中适应社会影响和环境思维的有利可图的想法
数据高效的分层强化学*— HIRO
介绍
*年来,传统的强化学*算法取得了令人鼓舞的成功。然而,它们在原子尺度上推理的本性使得它们很难扩展到复杂的任务。 H 层次结构 R 强化 L 收益(HRL)引入了高层抽象,代理人能够以不同的规模进行计划。
在这篇文章中,我们讨论了 Ofir Nachum 等人在 NIPS 2018 的谷歌大脑中提出的 HRL 算法。这种算法被称为带有 O ff 策略修正(HIRO)的 HI 层次 R 强化学*,是为目标导向的任务而设计的,在这种任务中,代理试图达到某种目标状态。
注意,这篇文章是连续两篇文章中的第一篇。在下一篇帖子中,我们将讨论由同一团队提出的另一种 HRL 算法,作为对 HIRO 的改进,名为分层强化学*的*优表示学*。
为了更好的数学表达可读性,你可以参考我的个人博客
具有偏离策略校正的分层强化学*
我们首先介绍关于 HRL 的三个基本问题:
- 应该如何训练较低级别的策略来诱导语义不同的行为?
- 应该如何定义高级策略操作?
- 应该如何培训多种政策而不招致过多的经验收集?
HIRO 可以通过回答上述问题得到很好的解释(从这里开始,我们只关注一个两级 HRL 代理):
1.除了状态观察之外,我们将由较高级别的策略产生的目标提供给较低级别的策略,以便较低级别的策略学会针对它试图实现的不同目标表现出不同的行为。因此,为了指导低层策略的学*过程,我们将目标条件奖励函数定义为

Eq.1 low-level reward function
2.高级策略操作被定义为目标,低级策略试图在某个时间段内实现这些目标。当 t≡0(mod c) 时,目标或者每 c 步从高层策略中取样,或者通过固定的目标转移函数计算。数学上,目标被定义为

Eq.2 goal function
3.为了提高数据效率,我们使用非策略算法(如 TD3)分别训练高级和低级策略。具体来说,对于两级 HRL agent,下层策略是用经验 (sₜ,gₜ,aₜ,rₜ,s_{t+1},g_{t+1}) 训练的,其中内在报酬 rₜ 和目标 gₜ 分别由等式 1 和等式 2 计算;更高级别的策略是基于时间扩展的经验 (sₜ,\蒂尔德·gₜ,∑ R_{t:t+c-1},s_{t+c}) ,其中\蒂尔德·gₜ是重新标记的目标(我们将在下一节中讨论),而 R 是环境提供的奖励。因为所有附加变量都是由代理本身定义的,所以从环境中收集的经验可以与两种策略共享。
HIRO 的目标
由高级策略产生的目标描述了状态空间中期望的相对变化。这使得在等式中定义的目标转换函数的定义有意义。2

goal transition function
其中 sₜ+gₜ 计算期望状态 s_{t+c} 。它还为等式 1 中定义的目标条件奖励函数带来了一个很好的解释:奖励函数简单地根据期望状态 sₜ+gₜ 和代理通过采取行动 aₜ 到达的下一个状态 s_{t+1} 之间的欧几里德距离进行惩罚。注意,在这个定义中,目标的形式与状态观测值相同。
到目前为止,我们已经了解了低层策略的目标,现在让我们考虑它在高层策略中的作用。首先,我们注意到,虽然目标是由高层次的策略作为高层次的行动产生的,但它们实际上是由低层次的策略执行的。随着较低级别策略的发展,为实现相同目标而采取的较低级别操作会发生变化,这反过来会导致不同的状态和沿途收集的奖励。这使得旧的高层转换 (sₜ,\颚化符 gₜ,∑ R_{t:t+c-1},s_{t+c}) 无效。为了恢复这种体验,我们重新标记了目标,使得它很可能用较低级别策略的当前实例化来诱导相同的较低级别行为。数学上, \tilde gₜ 被选择来最大化概率y μ^{low}(a_{t:t+c-1}|s_{t:t+c-1},\tilde g_{t:t+c-1}) ,其中中间目标 \tilde g_{t+1:t+c-1} 使用等式中定义的目标转移函数来计算。2.实际上,我们通常取而代之的是最大化其对数概率、,如果动作是从高斯分布中采样的,则可以如下计算

Eq.3 measure for relabeled goals
为了*似最大化这个数量,我们计算了许多目标的对数概率\蒂尔德 gₜ ,并选择最大的目标来重新标记体验。例如,我们对从以 s_{t+c}-sₜ 为中心的高斯分布中随机抽样的八个候选目标计算这个数量,还包括原始目标 gₜ 和对应于候选集中差异 s_{t+c}-sₜ 的一个目标,总共有 10 个候选。因此,最大化等式 3 的那个被选择为重新标记的目标。
算法
从论文中摘录的下图完美地阐释了该算法,其中高层和低层策略都由 TD3 训练。

from Data-Efficient Hierarchical Reinforcement Learning
注意,我们实际上必须使用经验 (s_{t:t+c},gₜ,a_{t:t+c-1},∑ R_{t:t+c-1}) 来训练高层策略,因为我们必须重新标记目标。
结束
欢迎在下面留下评论和思考来讨论话题:)
参考
Ofir Nachum 等.数据有效的分层强化学*
Scott Fujimoto 等人在演员-评论家方法中解决函数*似错误
数据工程 Apache 气流的基础——构建您的第一条管道
从多个数据源提取数据

如果你从事技术工作,你很有可能需要管理数据。
数据随着公司规模的扩大,数据会快速增长,变得更加复杂和难以管理。管理层希望从他们拥有的数据中提取洞察力,但他们没有这样做的技术技能。然后他们雇佣你,友好的邻里数据科学家/工程师/分析师或者任何你现在想称呼自己的头衔(不管怎样,所有的工作都由你来做)来做。
你很快意识到,为了让你提供洞见,你需要某种观想来解释你的发现,并随着时间的推移监控它们。为了使这些数据保持最新,您需要在固定的时间间隔内(每小时、每天、每周、每月)从多个数据源提取、转换这些数据,并将其加载到您的首选数据库中。
公司有工作流程管理系统来处理这样的任务。它们使我们能够在内部创建和安排工作。每个人都有自己的偏好,但我要说的是,许多旧的 WMS 往往效率低下,难以扩展,缺乏良好的用户界面,没有重试的策略,更不用说可怕的日志,使故障排除/调试成为一场噩梦。它会造成不必要的精力和时间浪费。下面是一个用于调度作业的传统 crontab 示例。

不要再烦恼了。阿帕奇气流来拯救世界了。
阿帕奇气流可以作为贵公司的 WMS,然后一些。 Airflow 最初是由 Airbnb 的人创建的,是开源的。Airbnb 将其用于:
- 数据仓库:提取、转换并加载到数据仓库中
- 增长分析:计算客人和主人参与度以及增长核算的指标
- 实验:计算我们的 A/B 测试实验框架逻辑和集合
- 电子邮件定位:应用触发器,通过电子邮件活动吸引客户
- 会话化:计算点击流和耗时数据集
- 搜索:计算搜索排名相关指标
- 数据基础设施维护:数据库抓取、文件夹清理、应用数据保留策略
(来源:https://medium . com/Airbnb-工程/气流-a-工作流-管理-平台-46318b977fd8 )
Airflow 还能够与 Hive、Presto、MySQL、HDFS、Postgres 和 S3 等热门技术进行交互。airflow 的基本模块也设计为易于扩展,因此如果不包括您的堆栈(这不太可能),可以重写模块以与您所需的技术交互。
我不打算详细说明气流后端是如何工作的。你可以在这里 了解更多。
Airflow 还有一个闪亮的用户界面,允许您管理和监控您的工作流程。依赖关系更容易建立,日志更容易访问,代码更容易阅读,每项任务花费的时间,完成时间,点击一个按钮触发/暂停工作流等等,都可以通过 UI 完成。


还没卖出去?
你现在可以把气流带到云端。谷歌已经推出了
Google-Cloud-Composer,这是一个基于云的 Apache Airflow 托管服务。这为您省去在公司的本地服务器上运行 Airflow 的麻烦。你再也不用担心气流崩溃了。
正如你所看到的,数据管道只是触及了表面。气流的可能性是无限的。
但你不是来闲聊的。你想弄脏你的手。好吧,我们就这么做吧。

Photo by Amaury Salas on Unsplash
入门指南
我不打算安装 Airflow,因为我正在使用 Google-Cloud-Composer,请研究如何安装它,因为这是一个相当复杂的过程。
完成之后,让我们来看看如何创建你的第一个工作流。气流通过 Dag(有向无环图)创建工作流。Dag 是一个高层次的概要,它定义了可以排序和调度的从属和独占任务。不要被这个吓倒,因为它只是一个你自己修改的 python 文件。
打开你喜欢的文本编辑器,开始编码。
进口
# airflow related
from airflow import models
from airflow import DAG# other packages
from datetime import datetime, timedelta
设置默认参数
default_dag_args = {
# Setting start date as yesterday starts the DAG immediately when it is
# detected in the Cloud Storage bucket.
# set your start_date : airflow will run previous dags if dags #since startdate has not run
#notify email is a python function that sends notification email upon failure
'start_date': datetime(2019, 5, 1, 7),
'email_on_failure': True,
'email_on_retry': True,
'project_id' : 'your_project_name',
'retries': 1,
'on_failure_callback': notify_email,
'retry_delay': timedelta(minutes=5),
}with models.DAG(
dag_id='your_dag_name',
# Continue to run DAG once per day
schedule_interval = timedelta(days=1),
catchup = True,
default_args=default_dag_args) as dag:
在这个块中,我们定义了我们的 default_args 参数,你可以在默认参数这里中读到所有的参数。然后我们实例化一个 DAG 对象,将schedule_interval设置为每日,将start_date设置为
May 1st 2019, 7am ,如default_dag_args中所示。这是定义 DAG 执行日期/时间的两个重要参数。
DAG 将在start_date + schedule_interval上第一次运行。
在这种情况下,DAG 将在May 2nd 2019, 7am第一次运行,执行日期= May 1st 2019, 7am。这里有一个例子,****

此 DAG 的执行日期为 2019–06–12 17:00,DAG 运行于
2019–06–13 17:00,导致此任务在 2019–06–13 18:02 运行,因为 DAG 的schedule_interval是一天。
这几乎建立了你的狗的脊梁。接下来,我们必须定义要执行的任务以及如何执行这些任务。
定义我们的 DAG、任务和运算符
首先,为了提取数据,您需要知道运行哪种任务。让我们以我的管道为例。它包括从 Postgres 数据库中提取数据,并将其加载到我们的数据仓库中(Google Bigquery)。根据我的经验,事情是这样的。
按顺序的任务:
- 连接到 Postgres 数据库从属服务器,检查任何复制延迟。
- 将数据写入 CSV,存储在某个地方。
- 将 CSV 数据加载到数据仓库
- 检查数据仓库中负载的任何复制
- 写日志
- 发送通知电子邮件
有许多方法来设计你的管道,但这是我怎么做的。我选择为每个包含运行它的代码的任务创建新的定制操作符。然后,我将它导入 DAG,并根据操作符设置依赖项和顺序。优势对此有几点:
- DAGs 代码看起来更干净,可读性更高(重要)。
- 操作符可以在多个 Dag 中重用,不需要重新键入代码。
- 如果任务有任何变化,对操作符代码进行修正会修正使用该特定操作符的所有 Dag
- 更少的代码操作和工作,团队只需更改 DAG 中的一个参数,操作员的工作就会不同
操作员做所有真正的工作。
让我们从我的管道中写一个操作符:
任务 2。将数据从数据源
写入 CSV 在你的文本编辑器中打开第二个窗口,并开始编码你的操作符。
进口
**# airflow related
from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults# other packages
from datetime import datetime, timedelta
from os import environ**
定义您的操作员
除传感器操作符外,所有操作符都来自 BaseOperator 。传感器操作符派生自 BaseSensorOperator 从 BaseOperator 派生而来,我们在这里不讨论传感器。BaseOperators 有一个执行函数和您自定义的函数。作为操作符主干的操作符的一般格式可以在下面找到。
**class AirflowOperatorName(BaseOperator):
@apply_defaults
def __init__(
self,
param1,
param2,
*args, **kwargs):super(AirflowOperatorName, self).__init__(*args, **kwargs)
self.param1 = param1
self.param2 = param2
def execute(self, context):
# task to be executed**
将主干应用到我们的任务 2 中。将数据从数据源写入 CSV
**class DataSourceToCsv(BaseOperator):
"""
Extract data from the data source to CSV file
"""[@apply_defaults](http://twitter.com/apply_defaults)
def __init__(
self,
bigquery_table_name,
extract_query,
connection = #connection,
*args, **kwargs):super(DataSourceToCsv, self).__init__(*args, **kwargs)
self.bigquery_table_name = bigquery_table_name
self.extract_query = extract_query
self.connection = connection
self.file_path = #filepath_to_save_CSVdef __datasource_to_csv(self, execution_date):
# code to execute def execute(self, context):
self.__datasource_to_csv(execution_date)**
这基本上涵盖了你的大部分操作文件。在这段代码中,我们:
- 定义要从 DAG 传递给运算符的参数
- 设计完成工作的代码
- 用特定的参数值初始化该运算符的实例
设计您的代码
关于你希望任务如何执行,有许多方法。
下面是我从数据源编写 CSV 的步骤和代码:
- 运行 sql 代码(在 DAG 中定义),以最少的转换提取结果。
- 将结果写入一个具有正确命名的 CSV 文件,保存在数据仓库可以访问的地方。
以下是我对操作符整体使用的完整代码:
恭喜你!
您已经成功编写了第一个运算符。将其另存为“DataSourceToCsv”。现在让我们将它导入到您的 DAG 中。我在 Google Cloud Composer 中导入操作符的方式是将操作符文件存储在 DAG 文件夹中一个名为“operators”的文件夹中。然后通过以下方式将其导入 DAG:
回到 DAG 的代码:
进口经营者
**# import operators from the 'operators' file
from operators import DataSourceToCsv**
让我们调用 DAG 中的操作符,并传入我们的参数:
呼叫接线员
**task2 = DataSourceToCsv.DataSourceToCsv(
task_id='task name',
bigquery_table_name = 'tablename',
extract_query = """ Select * from tablename """,
connection = #my defined postgres db connection)**
定义完所有任务后,就该设置执行它们的顺序和依赖关系了。要设置执行的顺序,就像在你的任务上加上“> >”一样简单。
举例如下:
**task1 >> task2 >> task3 >> task4**
要设置任务的依赖关系,就像给任务加上 'set_upstream()' 一样简单。下面的示例显示 task1 和 task2 并行执行,task3 依赖于 task2 的完成,然后执行 task4。
**task1
task2
task3.set_upstream([task2])
task3 >> task 4**
恭喜你!
你已经准备好了。将您的 DAG 文件另存为“DAGNAME.py ”,并将其上传到 airflow 中的 DAG 文件夹。等待几分钟,您应该会在 UI 中看到一个新的 DAG 弹出。
建立你的第一个管道是什么感觉?
我必须说,我在建筑材料中找到了满足感,管道只是其中之一。当 DAG 从头到尾工作时,有一种非常满意的感觉。
然而,它并不总是按照你想要的方式工作。完成你的第一个 DAG 后,你必须开始调试它,因为总会有问题。一旦你有了经验,错误就会减少。这只是旅程的一部分。
下面是我刚才描述的 DAG 的完整代码:
我希望我至少激发了您对数据工程的兴趣,如果不是帮助您建立了第一条管道的话。
最后,让我引用一段话。
如果没有大数据,你会又瞎又聋,还在高速公路上。
—杰弗里·摩尔
订阅我的时事通讯,保持联系。
也可以通过 我的链接 注册中等会员来支持我。你将能够从我和其他不可思议的作家那里读到无限量的故事!
我正在撰写更多关于数据行业的故事、文章和指南。你绝对可以期待更多这样的帖子。与此同时,可以随时查看我的其他 文章 来暂时填补你对数据的饥渴。
感谢 阅读!如果你想与我取得联系,请随时联系我在 nickmydata@gmail.com 或我的 LinkedIn 个人资料 。也可以在我的Github中查看之前的撰写代码。**
数据工程——如何在 Apache Airflow 上构建 Gmail 数据管道
黑掉你的 Gmail 收件箱。

我的读者朋友们,你们好。你今天过得怎么样?好吗?
我将向你解释我是如何为我的公司制作了一个
Gmail 数据管道来让它变得更好。
围拢过来。你不会想错过的。
那是什么?你说你为什么要在乎这个?
嗯,考虑到谷歌通过整合大量技术(如 Google Drive、Google Photos、Google Calendar、Google Everything)在电子邮件服务提供商领域占据了主导地位,它也被许多评论网站评为顶级电子邮件服务提供商。这里有一个的例子。
这也是大多数公司和临时用户的首选。我不会深究为什么 Gmail 是最好的。
这里谁用雅虎邮箱?正是我的观点。
还不信服?
只需看看谷歌趋势中电子邮件提供商之间的比较。

结果不言自明。
Github 上也有对 Gmail 相关代码的巨大支持。仅 Gmail 一项,Github 就有超过 16k 个知识库和 1.8 亿个代码。因此,任何你能想到的用 Gmail 实现的东西,可能已经有人做过类似的事情了。

话虽如此。你登上 Gmail 火车了吗?最好快点。在你告诉你的老板你能够从 Gmail 中自动提取、转换和加载数据到你的数据仓库后,他可能会立即大幅提升你到数据科学的领导位置。请给我捐点钱,我的银行账号是
I-M-K-I-D-D-I-N-G,来自讽刺银行。让我们进入正题。
Gmail 数据管道
- 每天自动从您的 Gmail 收件箱提取、转换数据并将其加载到您的首选数据仓库中
- 一个自动化系统,可以更好地将您的 Gmail 附件组织到您的数据库中。只保留你需要的,其余的都扔掉
- 易于使用,没有麻烦。停止下载附件并手动上传到数据仓库
我将使用由 Google Cloud Composer 管理的 Apache Airflow 来执行管道。在我的公司里,我也使用 Google Bigquery 作为我的数据仓库。你可以使用任何你喜欢的 WMS 和数据仓库,无论如何 Google API 应该是可调用的。
我写过一篇如何从零到英雄操作阿帕奇气流的文章。
你可以在这里找到: 阿帕奇气流基础
第一步。设置您的 Gmail 收件箱
管道的第一步是设置要提取的标签收件箱。提取收件箱中的所有附件是可能的,但是请考虑一下。
其中一些将是图片、文本文件、视频、pdf 等。我们只想要 csv 文件,因为数据通常是这样发送的。我们还想标准化每个管道的格式,这样我们就不会在未来遇到任何不一致的情况。
例如,詹姆斯发给你的每周财务报告的数据和莫尼卡发给你的每周活动结果的数据是不同的。
因此,我们需要通过添加标签来区分数据。
进入您的 gmail 收件箱。在右上角,
设置 > 过滤器并屏蔽 地址 > 创建新过滤器
设置标签的条件。例如,unknown@gmail.com 每周给我发一份产品性能报告。
电子邮件的主题通常是“每周报告产品 A”
因此,我将从:unknown@gmail.com
主题:周报产品 A
中设置
,并勾选有附件框。
选择创建过滤器,你会看到一堆复选框:
重要的有
- 应用标签:选择一个标签
- 还将过滤器应用于匹配对话
务必选中这两个框,并为这批特定的电子邮件创建一个新的标签名称。我通常也会勾选“跳过收件箱”,这样电子邮件就会被直接发送到标有“收件箱”的地方。让我们为我们正在使用的示例创建一个标签收件箱,命名为“每周报告产品收件箱”。
您现在应该会看到所有的电子邮件,包括关于产品 A 的周报告的现有邮件,这些邮件在
周报告产品 A 收件箱中包含附件。
祝贺您,您已经完成了这个流程中最简单的一步。
第二步。在 Apache Airflow 中创建 DAG 文件
如果你现在迷失了,给自己两个耳光,集中注意力。我们即将进入本质,也就是代码。我假设您知道如何在 Apache Airflow 中创建 Dag 和操作符,如果您不知道,请节省一些时间,阅读我的文章中关于如何创建 Dag 和操作符的内容。这样一来,打开你最喜欢的文本编辑器,开始编码。
进口
# airflow related
from airflow import models
from airflow import DAG# other packages
from datetime import datetime, timedelta
设置默认参数
default_dag_args = {
# Setting start date as yesterday starts the DAG immediately when it is
# detected in the Cloud Storage bucket.
# set your start_date : airflow will run previous dags if dags #since startdate has not run
#notify email is a python function that sends notification email upon failure
'start_date': datetime(2019, 7, 1, 10),
'email_on_failure': True,
'email_on_retry': True,
'project_id' : 'your_project_name',
'retries': 1,
'on_failure_callback': notify_email,
'retry_delay': timedelta(minutes=5),
}with models.DAG(
dag_id='your_dag_name',
# Continue to run DAG once per day
schedule_interval = timedelta(weeks=1),
catchup = True,
default_args=default_dag_args) as dag:
我们将 DAG 设置为每周一上午 10 点运行。假设每周报告将在每周一上午 10 点前发送。如果在上午 10 点以后收到电子邮件,将不会有任何数据加载到数据仓库中。请记住,当你设置这个的时候,你总是想要设置一个你确定数据会出现在收件箱的时间。我选择每周一次的报告也没有特别的原因。如果您正在处理的是每日报告,只需将 schedule_interval 更改为 timedelta(days=1)。
定义我们的 DAG、任务和运算符
让我们设计要在这个特定 DAG 中运行的任务。
我的做法是:
- 浏览 Gmail 收件箱并将所有附件下载到 GCS
- 检查是否有要加载的附件
- 将所有附件加载到 Google Bigquery
- 检查 Google Bigquery 中的任何重复加载
- 写日志
- 发送电子邮件
让我们为操作员编写执行这些任务的代码。
不用担心。我将和你一起浏览所有重要的操作符。
在文本编辑器中打开第二个窗口,开始编写操作符。
操作员:给 GCS 发邮件
进口
# airflow related
from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults# other packages
from datetime import datetime, timedelta
from os import environ
import csv
import getpass, imaplib
设计
class ExtractAttachment(BaseOperator):
"""
Extract data from Gmail into GCS
"""[@apply_defaults](http://twitter.com/apply_defaults)
def __init__(
self,
inbox_name,
*args, **kwargs):super(DataSourceToCsv, self).__init__(*args, **kwargs)
self.inbox_name = inbox_name
self.file_path = #filepath_to_save_CSVdef __extract_email_attachment(self, execution_date):
userName = 'your_user_name'
passwd = 'your_password' imapSession = imaplib.IMAP4_SSL('imap.gmail.com')
typ, accountDetails = imapSession.login(userName, passwd)
if typ != 'OK':
print('Not able to sign in!') imapSession.select(self.inbox_name)
typ, data = imapSession.search(None, 'Unseen')
if typ != 'OK':
print('Error searching Inbox.')# Iterating over all emails
for msgId in data[0].split():
typ, messageParts = imapSession.fetch(msgId, '(RFC822)')
if typ != 'OK':
print('Error fetching mail.') raw_email = messageParts[0][1]
raw_email_string = raw_email.decode('utf-8')
email_message = email.message_from_string(raw_email_string)
for part in email_message.walk():
if part.get_content_maintype() == 'multipart':
# print part.as_string()
continue
if part.get('Content-Disposition') is None:
# print part.as_string()
continue
fileName = part.get_filename() if bool(fileName):
filePath = self.file_path + fileName
print(filePath)
if not os.path.isfile(filePath) :
print(fileName)
fp = open(filePath, 'wb')
fp.write(part.get_payload(decode=True))
fp.close()
imapSession.uid('STORE',msgId, '+FLAGS', '\SEEN')
imapSession.close()
imapSession.logout()def execute(self, context):
execution_date = (context.get('execution_date')
self.__extract_email_attachment(execution_date)
在这段代码中,我们:
- 定义数据所在的 Gmail 收件箱名称
- 设置我们的 Gmail 用户名和密码(是的,不幸的是,任何查看代码的人都会看到)
- 设置 IMAP 会话,使用凭证登录
- 搜索名为 inbox_name 参数的 Gmail 标签收件箱
- 只选择不可见的邮件,这很重要,因为我们不想两次加载相同的数据
- 提取这些邮件的附件并保存在由 file_path 参数定义的文件路径中
- 提取后,将电子邮件标记为已读,这样下次就不会被提取了
- 注销我们的 IMAP 会话
请记住,操作员只考虑那些未看到/未读的电子邮件。因此,如果您不小心点击了该邮件,它将自动标记为已读,完全忽略来自管道的邮件。如果你不希望这种情况发生,记得在阅读完邮件后,从 Gmail 用户界面将它标记为未看/未读。
以下是操作符本身的完整代码:
这是完成的第一个操作符。继续前进。
操作员:短路操作员
由于电子邮件可能会不可避免地丢失,DAG 有时会返回一个错误,因为没有数据加载到 BQ 中。对于这一部分,我们使用 ShortCircuit 操作符检查是否有任何文件要加载到 Google Bigquery 中。如果没有要加载的文件,则跳过该操作符的所有下游任务,DAG 成功完成。否则,DAG 照常进行。没有必要为此编写自定义操作符,我们将使用 Airflow 提供的默认操作符。跳过任务时,气流将显示粉色指示器。下面是一个例子。

操作员:GCS 到 Google Bigquery
进口
# airflow related
from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults# other packages
from datetime import datetime, timedelta
from os import environ
import csv
from google.cloud import bigquery
import pandas as pd
import os
设计
class StorageToBigQuery(BaseOperator):
"""
Extract data from Google Cloud Storage to Google Bigquery
"""[@apply_defaults](http://twitter.com/apply_defaults)
def __init__(
self,
dataset_name,
bigquery_table_name,
write_mode,
*args, **kwargs):super(StorageToBigQuery, self).__init__(*args, **kwargs)
self.dataset_name = dataset_name
self.bigquery_table_name = bigquery_table_name
self.write_mode = write_mode
self.local_path = 'File Path of CSV'def __StorageToBigQuery(self, execution_date):
for file in os.listdir(self.local_path):
filename = self.local_path + file
df=pd.read_csv(filename,error_bad_lines=False)
#Using pandas to clean data
df.to_csv(self.local_path + 'cleaned_' + file,index=False) file_path_to_load = self.local_path + 'cleaned_' + file
logging.info("FILE PATH TO LOAD : %s" % file_path_to_load)
print('loading_file_to_BQ')
client = bigquery.Client()
dataset_ref = client.dataset(self.dataset_name)
job_config = bigquery.LoadJobConfig()
job_config.autodetect = False
if(self.write_mode == 'overwrite'):
job_config.write_disposition = 'WRITE_TRUNCATE'
elif(self.write_mode == 'empty'):
job_config.write_disposition = 'WRITE_EMPTY'
else:
job_config.write_disposition = 'WRITE_APPEND'
job_config.skip_leading_rows = 1
job_config.field_delimiter = ','
job_config.quote = ''
job_config.allow_quoted_newlines = True
with open( file_path_to_load, 'rb' ) as source_file:
load_job = client.load_table_from_file(
source_file,
dataset_ref.table(self.bigquery_table_name),
job_config=job_config) assert load_job.job_type == 'load'
load_job.result()
assert load_job.state == 'DONE'def execute(self, context):
execution_date = (context.get('execution_date')
self.__StorageToBigQuery(execution_date)
重要提示:由于我默认使用 Google Cloud Composer,所以在调用 Google Bigquery API 时,我不需要通过任何身份验证。如果你正在使用香草气流,请在这里找到如何调用谷歌大查询 API。在加载数据之前,您还需要在 Google BQ 中创建表格。
在这段代码中,我们:
- 在 BQ 中定义要加载到的数据集和表格
- 浏览 local_path 目录下的所有文件
- 用熊猫做一些清洁工作
- 使用由 write_mode 参数设置的写入模式,在清理的文件上启动到 BQ 的加载作业
用熊猫清洁是为了避免任何人为错误。但是,您可以在该部分进行某种转换,然后加载该文件。我还使用它来添加一个名为气流执行日期的列,将 DAG 的执行日期作为输入。这对以后删除重复项很有用。
以下是操作员的完整代码:
又一个倒下了,坚持住。
运算符:检查 Bigquery 中的重复项
每当我们将数据加载(追加)到 BQ 中时,
数据中可能会有重复。
这可能是由以下几个原因造成的:
- 数据组有人启动了 DAG 两次
- 数据中有重复项(可以通过清除上一个运算符来删除)
- 有人中途停止了 DAG 并重新启动了它
- 代码中的缺陷
- 许多其他原因
因此,为了确保万无一失,我们总是运行重复检查,以确保我们只加载数据一次。我们不想报告 2 倍的收入,只是为了发现这是重复加载,不是吗?
进口
# airflow related
from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults# other packages
from datetime import datetime, timedelta
from os import environ
import csv
from google.cloud import bigquery
import pandas as pd
import os
设计
class CheckBQDuplication(BaseOperator):
"""
Check if a specific table in BigQuery contains duplicated data after the load
"""[@apply_defaults](http://twitter.com/apply_defaults)
def __init__(
self,
dataset_name,
bigquery_table_name,
bigquery_table_key,
date_column,
*args, **kwargs):super(CheckBQDuplication, self).__init__(*args, **kwargs)
self.dataset_name = dataset_name
self.bigquery_table_name = bigquery_table_name
self.bigquery_table_key = bigquery_table_key
self.date_column = date_column
self.file_path = 'File Path of CSV'def __check_BQ_duplication(self, execution_date):
# Check if the table contains duplicated data check_query = """
SELECT MAX(count) FROM (
SELECT $ID_COLUMN, COUNT(*) as count
FROM `$DATASET_NAME.$BIGQUERY_TABLE_NAME`
WHERE CAST($DATE_COLUMN AS DATE) = $EXECUTION_DATE
GROUP BY $ID_COLUMN)
"""
check_query = check_query \
.replace("$ID_COLUMN", self.bigquery_table_key)\
.replace("$DATASET_NAME", self.dataset_name) \
.replace("$BIGQUERY_TABLE_NAME",self.bigquery_table_name)\
.replace("$EXECUTION_DATE", \
"'" + execution_date + "'") \
.replace("$DATE_COLUMN", self.date_column)
logging.info("CHECK QUERY : %s" % check_query)check_query_job = bigquery.Client().query(query = check_query)
logging.info("job state : %s at %s" % \(check_query_job.state, check_query_job.ended))check_query_result = 0
for row in check_query_job:
check_query_result = int(row[0])
break
if check_query_result > 1:
logging.error('(ERROR): DUPLICATION EXISTS IN TABLE ' + self.bigquery_table_name)
# Duplication exists, proceed to delete the data in Big Query delete_query = """
DELETE FROM `$DATASET_NAME.$BIGQUERY_TABLE_NAME`
WHERE CAST($DATE_COLUMN AS DATE) = $EXECUTION_DATE
"""
delete_query = delete_query \
.replace("$DATASET_NAME",self.dataset_name) \
.replace("$BIGQUERY_TABLE_NAME",self.bigquery_table_name)\
.replace("$EXECUTION_DATE", "'" +execution_date + "'")\
.replace("$DATE_COLUMN", self.date_column)
logging.info("DELETE QUERY : %s" % delete_query)delete_query_job = bigquery.Client().query(query=delete_query)
logging.info("job state : %s at %s" % (delete_query_job.state, delete_query_job.ended))# Reload the file from Cloud Storage
print('going through the folder')
for file in os.listdir(self.local_path):
file_path_to_load = self.local_path + 'cleaned_' + filelogging.info("FULL FILE PATH TO LOAD : %s" % file_path_to_load) client = bigquery.Client()
dataset_ref = client.dataset(self.dataset_name)
job_config = bigquery.LoadJobConfig()
job_config.autodetect = False
job_config.write_disposition = 'WRITE_APPEND'
job_config.skip_leading_rows = 1
job_config.field_delimiter = ','
job_config.quote = ''
job_config.allow_quoted_newlines = True
with open( file_path_to_load, 'rb' ) as source_file:
load_job = client.load_table_from_file(
source_file,
dataset_ref.table(self.bigquery_table_name),
job_config=job_config) assert load_job.job_type == 'load'
load_job.result()
assert load_job.state == 'DONE'def execute(self, context):
execution_date = (context.get('execution_date')
self.__check_BQ_duplication(execution_date)
在这段代码中,我们:
- 定义查询以检查重复并在 BQ 中执行它
- 如有重复,删除 BQ 中所有数据,其中
date _ column=execution _ date - 从 file_path 参数中重新加载所有清理过的文件
考虑到机制,我们需要在表中有一个日期列。如果默认情况下没有,我们必须显式地创建一个。这就是为什么我在前一个操作符中创建了 Airflow 执行日期列。
再一次,这里是操作员的完整代码:
将您编写的所有操作符保存在 Airflow 可访问的文件夹中。对我来说,它是 DAG 文件夹中一个名为 Operators 的文件夹。最后两个操作符只是通知您作业的状态,并在 BQ 中写入日志。在 writing logs 操作符中还有一个部分删除了 file_path 目录中的文件。我将不会进入最后 2 个运营商,因为我们已经可以实现我们想要的,直到这个阶段。我会写一篇写日志和发邮件的文章,因为它适合我所有的日志。这篇文章已经写了太久了。和我在一起。
第三步。最终确定 DAG
完成所有操作后,剩下的就是将它们调用到我们的 DAG 文件中。
让我们回到 DAG 文件中。
导入运算符
# import operators from the 'operators' file
from operators import GmailToGCS
from operators import StorageToBQ
from operators import CheckDupBQ
from operators import WriteLogs
from operators import SendEmail
from airflow.operators import ShortCircuitOperator
呼叫短路接线员
def checkforfile():
file_path = 'File Path to Load'
if os.listdir(file_path):
return True
else:
return Falsecheckforfile = ShortCircuitOperator(
task_id='checkforfile',
provide_context=False,
python_callable=checkforfile)
ShortCircuit 操作符通过传递返回真或假输出的 python 函数来工作。如果输出为真,DAG 照常进行。否则,DAG 会跳过所有下游任务并继续完成。因此,我们编写一个函数来检查文件路径目录中的文件,如果文件存在,则返回 True。
DAG 的完整代码:
注意,要传递给操作符的所有参数都是在 DAG 的顶部定义的。这样做是为了让 DAG 可以用作 Gmail 收件箱中其他标签的模板。这也更容易向你的同事解释,他们可能想使用它。
第四步。孩子身上的弹性
放松点。没有更多的代码要读。
将 DAG 文件弹出到 Airflow DAG 文件夹中,就可以了。
恭喜,您已经成功构建了您的个人
Gmail 数据管道。如何处理这些数据现在完全取决于你。你可以自动化仪表盘来显示报告,制作显示趋势的网页,甚至向你的机器学*模型提供数据。
所以,现在去吧,我的数据从业者伙伴们。传播消息。给自己一个鼓励..继续做下一个项目。至少这是我接下来要做的。
最后,让我引用一段话:
你可以有没有信息的数据,但你不能有没有数据的信息。—丹尼尔·凯斯·莫兰
订阅我的时事通讯,保持联系。
感谢 的阅读!如果你想与我取得联系,请随时通过 nickmydata@gmail.com 或我的 linkedIn 个人资料 联系我。也可以在我的Github中查看代码。
数据工程——如何使用 Google App 脚本构建 Google Drive 数据管道
通过 3 个简单的步骤从您的 Google Drive 获取数据

另一天,另一个管道。亲爱的读者朋友们,我是你们友好的邻里数据从业者,为你们带来了另一条数据管道,以满足你们所有的工程需求。让我们跳过关于如今数据有多重要的闲聊,因为我已经在我之前的所有文章中多次提到它。底线,如果你不使用数据,你就错过了。现在,如果除了一张凌乱的桌子,还有什么东西是每个人都讨厌的,那就是一个凌乱的数据仓库。
拥有一张漂亮干净的桌子是没有意义的,因为这意味着你什么也没做。—加来道雄
请不要将这个概念应用于数据仓库。它实际上与引用的内容相反。
拥有一个混乱的数据仓库是没有意义的,因为这意味着你什么也做不了。真的,你可以以后再感谢我。
因此,当涉及到通过组织数据仓库的架构来为科学家和分析师保持数据仓库的功能时,数据工程师是至关重要的。必须构建干净的管道来维护来自各种来源的正确数据摄取,无论是来自组织本身还是来自外部来源。如果你自己是一名数据工程师,你今天会有所收获。即使你不是,也要倾听并欣赏工程师所做的事情。毕竟,正是他们为您提供了清晰易读的数据,并将其输入到您的复杂模型中。
在我的上一篇文章中,我谈到了如何建立一个 Gmail 数据管道。这是假设你通过 gmail 收件箱从外部来源获得常规数据。然而,如果出于某种原因,您的客户拒绝每周向您发送一封附有数据的电子邮件,该怎么办呢?相反,你的客户更喜欢通过一个共同的
Google Drive 文件夹定期与你分享数据。就像将文件拖放到他/她的浏览器中一样简单。在一分钟内,数据将被接收到您的数据仓库中,并生成有意义的见解/报告。那不是很酷吗?没有吗?就我一个人出去玩?那很有趣,因为我过去常常嘲笑那些讨厌的人的工作。看来我们没什么不同。
我经常被问到的问题是— 我为什么要关心?
好吧,即使你现在没有得到这个问题,谁又能说你将来不会得到呢?此外,如果出现这种情况,你可以向你的客户提出这样的解决方案。这可能会也可能不会增加你的专业感。此外,它还会增加你的酷点,我们数据从业者对此无法满足。
考虑到谷歌通过整合大量技术(如谷歌文档、谷歌表单、谷歌照片、谷歌日历、谷歌一切)而几乎主导了在线存储市场,它也被许多评论网站评为顶级云存储服务之一。这里有一个的例子。
拜托,你得承认。你自己用谷歌驱动,不是吗?
但是不要相信我的话。以下是谷歌趋势统计数据的支持。

在 Github 中也有一堆与 Google Drive 相关的支持代码。我们正在寻找大约 6k 个存储库和价值 800 万的代码。所以不要担心开发人员,我们支持你。

更不用说它可以与 Google Bigquery 顺利集成,如果您已经将它用作数据仓库,这是一个优势。Google Drive 是愚蠢的证明,用户友好,几乎每个人都在使用。很快,您将能够使用这个云服务作为您的来源之一,通过 3 个简单步骤将数据摄取到您的数据仓库中。
Google Drive 数据管道
- 自动从 Google Drive 文件夹中提取、转换和加载数据到您的首选数据仓库,最多一分钟即可完成
- 一个自动化系统,可以更好地将你的 Google Drive 文件组织到数据库中。只保留你需要的,其余的都扔掉。
- 愚蠢的证明。拖放。停止下载附件并手动上传到数据仓库。
我将使用谷歌应用程序脚本来执行这个管道。这样做的缺点是谷歌应用程序脚本无法与任何真正的工作管理系统相比,如 Apache Airflow 。因此,你无法真正监控你的管道。但是,默认情况下,Google App Script 会在失败时向您的 Gmail 帐户发送电子邮件。Google App 脚本 UI 还提供了一个简单的脚本摘要来监控错误。

因此,我建议将这种解决方案用于具有最小复杂性的简单管道。这里没有任何写日志或发邮件的步骤。它只是抓取数据,执行一些转换,并将其加载到您的数据仓库中。简单快捷。
我也在用谷歌大查询作为我公司的数据仓库。这里的好处是 Google App Script 能够用 2-3 行代码连接到 Google Bigquery。你可以选择使用任何你喜欢的 WMS 和数据仓库,无论如何 Google Drive API 应该是可调用的。
第一步。设置您的 Google Drive
管道的第一步是建立一个 Google Drive 文件夹,我们从中提取数据。提取你的 Google Drive 中的所有文件是可能的。然而,我们只想要与管道本身相关的 csv 文件。因此,我们必须为每个管道创建一个 Google Drive 文件夹,以便数据可以与其他数据区分开来,这是为了避免将来出现任何不一致。相当直接。
进入你的谷歌硬盘。右键单击空白区域的任意位置,然后选择新建文件夹。将该文件夹命名为您想要命名的管道,这样就设置好了。在该文件夹中,创建一个名为“Processed”的文件夹来存储所有已处理的数据。现在,您应该有一个专门用于管道的文件夹,其中包含一个名为“已处理”的文件夹。

第二步。在 Google App 脚本中创建管道
戴上你的学*帽,因为我们将深入一点代码。我有没有提到谷歌应用脚本只使用 Javascript 作为他们的语言?如果这让你厌烦,克服它,因为你不会永远用 Python 编码。
进入你的谷歌应用脚本主页。
选择新脚本并开始编码。
在键入任何代码之前,我们必须启用一些 API,以允许 Google App 脚本连接到 Google Drive 和 Google BQ。在菜单栏上,选择:
资源->高级 Google 服务->打开 Bigquery 并驱动 API

// Replace this value with the project ID listed in the Google
// Cloud Platform project.
var projectId = 'your project ID';
// Create a dataset and table in the BigQuery UI ([https://bigquery.cloud.google.com](https://bigquery.cloud.google.com))
// and enter its ID below.
var datasetId = 'your dataset name';
var tableId = 'your table name';
var location = 'your bigquery location'// defining the folder to store the csv file
// folder for files to be saved in
var processedfolderId = 'your processed folder ID';
var folderId = 'your folder ID';
在这段代码中,我们定义了 GCP 项目、Google BQ 数据集、表和位置以及 Google Drive 文件夹。Google Drive 文件夹的 ID 可在此处找到:

function scan_files() {
var folder = DriveApp.getFolderById(folderID);
var processedfolder = DriveApp.getFolderById(processedfolderId);
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
if (file.getName().substring(0, 10) != 'processed_' || file.getName().substring(0, 7) != 'loaded_') {
loadCSVfromdrive(file);
}
}
};
在这个块中,我们定义了一个由 Google App Script 定期运行的函数来扫描我们指定的文件夹中的文件。如果有已存在的文件,我们将把它传递给函数 loadCSVfromdrive。但是,如果文件以名称“processed_”或“loaded_”开头,我们将忽略该文件。我们将用这个前缀命名我们已经处理/接收到 BQ 中的所有 csv 文件,以区别于尚未加载的数据。我们也会将它们放入“已处理”文件夹中。
function loadCSVfromdrive(file) {
var fileId = file.getId();
var filename = file.getName();
var csvData =Utilities.parseCsv(file.getBlob().getDataAsString());
var headers = [list of headers];
var length = csvData.length;// defining an array for each row of data
abstractarray = [headers];
//looping through each row of data from the raw file , transforming //it and append it to the array
for (var a = 1; a < csvData.length; a++){
// Transformation of data begins here (example below)
var description = csvData[a][14] + " " + csvData[a][25] + " " + csvData[a][26] + " " + csvData[a][27];
var reference = csvData[a][15];
var transaction_type = csvData[a][21];
// Transformation of data ends here contents = [description, reference, transaction_type];
abstractarray.push(contents);
}// defining the contents of the csv, should be an array of arrays
var csvContent = ''; abstractarray.forEach(function(infoarray, index) {
dataString = infoarray.join(',');
csvContent += index < abstractarray.length ? dataString + '\n' :dataString;
});// create a csv file which contents are the csv contents we defined //in the processed folder
filename_new = 'processed_' + filename
file_new = processedfolder.createFile(filename_new, csvContent);
在这段代码中,我们定义了将要加载到 Google BQ 中的 CSV 文件的内容。这仅适用于在接收到数据仓库之前需要清理/转换的数据。如果您计划从 Google Drive 文件夹加载原始数据,您可能会忽略大部分代码块。与使用 Python 的 Pandas 相比,Javascript 必须逐行转换数据,这就是为什么我们使用 for 循环来转换每行的所有数据,并将该行附加到我们的 csvcontent 中。如果你知道如何在 Javascript 中像熊猫一样操作,请在我的 DMs 中滑动,因为我对 JS 相当陌生。我们还在转换后在处理过的文件夹中创建一个名为“processed_filename”的文件,以便与原始数据区分开来。
// Create the data upload job.
var job = {
configuration: {
load: {
destinationTable: {
projectId: projectId,
datasetId: datasetId,
tableId: tableId
},
maxBadRecords: 0,
fieldDelimiter: ',',
skipLeadingRows: 0,
writeDisposition: 'WRITE_APPEND',
sourceFormat: "CSV"
}
}
}; job = BigQuery.Jobs.insert(job, projectId, file_new);
while(true) {
jobstatus = BigQuery.Jobs.get(projectId, job.getJobReference().getJobId(), {location : location})
if(jobstatus.getStatus().getState() == "DONE") {
break;}
}// saving the loaded file as the name 'loaded' so that it
//doesnt get loaded the next time
DriveApp.getFileById(fileId).setName('loaded_' + file.getName());
Logger.log(file_new)
}
我们正在这里设置装载作业。如果您在 Google Drive 中接收累积数据以覆盖您的数据仓库中的数据,您可以将 writeDisposition 参数更改为‘WRITE _ TRUNCATE’。成功加载数据后,我们将文件重命名为“loaded_filename ”,这样它就不会在将来被加载。如果出现任何问题,您也可以返回到该文件夹检查您的原始数据。这是管道的完整代码。
第三步。在 Google App 脚本中设置触发器
恭喜你,你已经到达了数据管道的最后一步。
别担心,从这里开始不会再有代码了。我们现在要做的就是设置触发器来运行我们的管道。
进入你的谷歌应用程序脚本主页。
我的项目->悬停在您的项目上->触发器

这应该会把你带到触发页面。
点击新建触发器并设置您的触发器。

如你所见,这是一个快速、简单且愚蠢的证明。像我承诺的那样。
记得选择 scan_files 功能作为要运行的功能。您可以选择函数运行的时间间隔。在 Google App Script 中,目前可用的最短时间间隔是以分钟为单位。即使对你来说,这也太短了。她就是这么说的。
恭喜
您已经成功构建了您的个人 Google Drive 数据管道。
数据团队是否要求将 Google Drive 中的一些数据输入到他们的模型中?你现在每分钟都可以这样做!自豪地拍拍自己的背。
我希望你今天学到了新东西。如果我激发了你对数据的兴趣,那么我就帮了大家一个忙,为数据行业招募了一名新人才。我们又不是已经饱和了。作为结束,我将再一次放弃引用。
成功没有秘诀。它是准备、努力和从失败中学*的结果。—科林·鲍威尔
订阅我的时事通讯,保持联系。
感谢 的阅读!如果你想与我取得联系,请随时通过 nickmydata@gmail.com 或我的 LinkedIn 个人资料 联系我。也可以在我的Github中查看代码。
数据工程——如何在 Apache Airflow 中设置数据管道之间的依赖关系
使用传感器设置数据管道之间的有效依赖关系,为数据团队建立坚实的基础

Photo by Rodion Kutsaev on Unsplash
亲爱的读者朋友们,又是我。那个写自己生活经历和一点点数据的家伙。
就一点点。
文章在文章之后,我总是从数据在一个强大的组织中有多重要开始。大公司如何利用数据来影响他们的业务,影响我们的社会,进而为他们带来利润。数据可以用来拯救生命,看看这篇关于数据如何被用来预测癌细胞的文章就知道了。所以,请让我们跳过关于数据有多重要以及它如何总是正确的闲聊,就像你的妻子一样。
我们相信上帝。所有其他人必须带数据。爱德华·德明
在世界开始之前,只有黑暗。然后不知何故,有人说要有光,突然间所有的生命都活了过来。
同样,在没有任何数据之前,只有黑暗。然后,不知何故,数据工程师建立了惊人的管道将数据输入数据仓库,突然间,所有的数据科学家都提出了他们的模型。可用数据的基础很大程度上取决于工程师编写的管道结构。因此,我们使用正确的工具来优化我们的管道非常重要。
在我之前的文章中,我谈到了 CRONJOBS 在为适当的数据仓库调度管道时不再可行。相反,工程师应该研究记录良好的工作流管理系统,这些系统可以提供巨大的好处,如监控、设置依赖关系,更不用说可以使用良好的 UI 了。我选择的 WMS 是 Apache Airflow,因为在研究了所有可用的 WMS 后,我的公司认为 Apache Airflow 是目前最适合我们仓库的。
我有没有提到 twitter 也在使用 Apache Airflow 进行数据仓储?
我还引导读者建立他们的第一个管道,谈论 Apache Airflow 的基础以及它是如何工作的。这篇文章是对它的延伸,因为我将讨论设置管道之间的依赖关系,以及为什么它对您的数据仓库如此重要。
为什么重要?
每个数据仓库都各不相同。作为工程师,我们能做的最好的事情就是建立有效的管道来适应数据仓库本身的优点和缺点。每个数据仓库都从一个数据源获取数据,这个数据源可能是您的主数据库或它的克隆。根据您的公司,您可能会运行 ETL 管道将数据接收到您的数据仓库中。然而,使用 Google Bigquery 作为我公司的数据仓库,我们认为运行 ELT 管道速度更快,成本更低,因为 Google BQ 可以很好地处理转换。它还减轻了主数据库的从数据库的负担。我相信这不是我们公司独有的,现在在数据领域这是一种非常普遍的做法。通常的议程是从午夜开始的 X 个小时的原始表的纯数据提取,导致这些表的转换再持续 X 个小时,以完成整个流水线。这是我做的一个形象化的展示。

之所以这样设计管道,是因为第一级转换后的表来自并依赖于原始表提取的完成。然后,第二级转换表依赖于这些第一级转换表。因此,我们设置这些任务之间的依赖关系是很重要的。如果一个表的一个源表还没有被提取/转换,我们就不想执行这个表的转换。这将导致不正确的数据,而这正是数据工程师应该受到指责的地方。
传感器
如前所述,我们将使用阿帕奇气流作为我们的主要 WMS。通过这样做,我们可以有效地监控我们的工作流,能够跟踪失败的任务,如果有的话。如果你不知道如何操作气流,那么下面的内容对你来说就像是谜题,请先阅读阿帕奇气流的基础知识。
传感器预装在气流中。顾名思义,它们感知气流中任何任务的完成状态,就这么简单。我们将使用传感器来设置我们的 does 管道之间的依赖关系,以便在依赖关系完成之前不会运行。不需要为此编写任何自定义操作符。以下是传感器的默认格式:
check_dependency =
ExternalSensor.ExternalTaskSensor(
task_id='name of the sensor',
external_dag_id='name of dag to be checked',
external_task_id= 'name of task to be checked',
execution_delta = time difference between execution times,
timeout = maximum running time of sensors)
我们将此代码块作为 DAG 中的一项任务来编写。
需要注意的两件事:
- 执行增量可能很棘手,它取决于任务的执行时间,而不是运行时间。这里有一个例子。

这里,执行时间是 2019 年 8 月 30 日晚上 9 点(不要与运行时间混淆,运行时间在这里也称为“开始”:2019 年 8 月 31 日晚上 9 点)。如果我们将传感器的执行时间(传感器 DAG 中的' start_date 的设置)设置为 2019-08-30 晚上 10 点,那么在这种情况下,执行增量将为 timedelta(小时=1)。
- 超时参数是必需的。当您的管道扩展时,将会有许多传感器运行来检查完井情况。如果没有设置超时,并且我们的一些依赖关系失败,传感器将无限期运行,并导致您的气流暂停。这是因为 airflow 只允许在一个实例上运行一定最大数量的任务,并且传感器被视为任务。如果你不知何故达到这个数字,气流将不会处理进一步的任务。因此,我们需要为传感器设置超时参数,这样,如果我们的依赖关系失败,我们的传感器就不会永远运行。
例子
在这个例子中,假设表 1 是来自原始数据源的一些连接的结果。在运行表 1 的转换之前,我们需要创建 4 个传感器来感知提取 Dag 的完成。

为了消除所有疑问,让我们假设所有提取 Dag:
- 每天运行
- 在晚上 9 点运行
我们将在表 1 的转换 DAG 中编写以下代码:
check_data_source_1 =
ExternalSensor.ExternalTaskSensor(
task_id='check_data_source_1',
external_dag_id='dag of data source 1',
external_task_id= 'last task of the dag',
execution_delta = timedelta(hours=1),
timeout = 300)check_data_source_2 =
ExternalSensor.ExternalTaskSensor(
task_id='check_data_source_2',
external_dag_id='dag of data source 2',
external_task_id= 'last task of the dag',
execution_delta = timedelta(hours=1),
timeout = 300)check_external_data_source_1 =
ExternalSensor.ExternalTaskSensor(
task_id='check_external_data_source_1',
external_dag_id='dag of external data source 1',
external_task_id= 'last task of the dag',
execution_delta = timedelta(hours=1),
timeout = 300)check_external_data_source_2 =
ExternalSensor.ExternalTaskSensor(
task_id='check_external_data_source_2',
external_dag_id='dag of external data source 2',
external_task_id= 'last task of the dag',
execution_delta = timedelta(hours=1),
timeout = 300)transform_table_1 = /* code for transformation of table 1 */check_data_source_1
check_data_source_2
check_external_data_source_1
check_external_data_source_2transform_table_1.set_upstream([check_data_source_1,check_data_source_2,check_external_data_source_1,check_external_data_source_2])
写完上面的代码后,在我们设置的四个依赖项完成之前,任务“transform_table_1”不应继续。一旦传感器启动,它们将在 5 分钟内感知依赖关系的完成。如果上述依赖关系未能完成,传感器将显示为故障,等待重试。
这是 DAG 的完整代码。
恭喜
您在实现干净数据方面又前进了一步。即使你不是数据工程师,这些知识对所有数据从业者都是有益的。此外,传感器可用于所有气流任务。
想在查询运行后发送电子邮件吗?使用传感器
想要在插入新文件后扫描某个共享的 google drive 吗?使用传感器
可能性是无限的。
结束注释:原谅我最*的缺席,我正在 Apache Beam 上实现流管道。有趣的东西!一旦完成,我会写下来的!
订阅我的时事通讯,保持联系。
感谢 的阅读!如果你想与我取得联系,请随时通过 nickmydata@gmail.com 或我的 LinkedIn 个人资料 联系我。也可以在我的Github中查看代码。
数据工程金块
介绍杂乱无章的轮胎火灾,这是我的数据驱动思维

Photo by Pedro Henrique Santos on Unsplash
编写有意义的数据工程内容时的一个挑战是,受众来自如此多样化的背景。一个拥有数据工程师头衔的人很可能在到达这个职位时戴着各种不同的帽子,却发现“数据工程师”是他们所有人的最佳绰号。当面对一群这样的人时,什么能被认为是“常识”,什么不能被认为是“常识”,这是非常复杂的考虑到这一点,我在这里开始为收集数据团队相关的知识奠定基础。对于数据团队,我通常指的是组成大多数组织知识结构的分析师、数据工程师、数据科学家和商业智能专家的集合(尽管我的观点通常会偏离重点)。我们从实践中学到的一些经验教训是非常有价值和相关的,不管我们如何去理解它们,这就是我在这里要阐述的观点。我的希望是,它为我对那些使数据团队在今天取得成功的事情的理解描绘了一幅广阔的底漆。
数据团队必须有一个使命
在您签署任何供应商合同、启动您的第一个数据库或键入单个git init之前,您需要了解数据团队在您组织中的目的。像“成为一个数据驱动的组织”或可怕的“使用机器学*来减少辛劳”这样模糊的目标和令人恐惧的模糊指令,往好里说是毫无意义的,往坏里说是对你职业生涯的威胁。组织想要关注内部分析(围绕自身的准确报告)还是商业智能(围绕客户的准确报告)?预测(预测未来业务)或者趋势分析(寻找长期统计模式)?R 实时个性化(应用速度数据科学)或根本原因分析(数据取证)?一个定义良好的任务是你的工作范围和支持用例的基础。当你选择使用什么技术,雇佣谁,以及如何定义你的工作“完成”时,这些将作为你的最高要求。
Meh 使命:数据团队将支持组织变得更加数据驱动的目标,并使用机器学*将我们的业务提升到一个新的水平。
是啊!使命:数据团队将构建和维护我们的“单一真实来源”数据系统,为企业利益相关方提供绩效洞察、产品和客户分析以及内部预测。该团队将制作和认证用于内部绩效指标、产品路线图&决策、产品和效率问题根本原因分析以及费用和销售预测的资产。
现在我们知道了我们要实现的目标。例如,这项任务的所有组成部分要么属于决策支持,要么属于监测或预测——高度容忍延迟的活动。在这种情况下,我们希望从数据团队获得的商业价值可以通过每天甚至每周的批处理来实现,因此在流式 NRT 解决方案上投入巨资可能是错误的答案。尽早并经常更新你的任务。当有疑问时,迫切要求澄清,尤其是当那些变化无常的高管人物给你模糊的指示时。如果你不清楚成功意味着什么,你就不会成功。
把你所做的当作软件
我绝不是这种思想流派的先驱,你可以阅读很多由比我聪明得多的人写的关于数据工程即代码开发的文章。简而言之,我们生产的转换、提取负载、工件、模型和可视化是以编程方式应用业务逻辑的结果。这和驱动网站、商业软件、操作系统和 IOT 牙刷的代码是完全一样的功能。在其他应用业务逻辑所在的软件世界中,某些工具和技术(通常)被认为是该行业的基石,例如:
我们称之为“最佳实践”,现代软件开发人员已经将类似于git commit和tox -e test的命令深深地烙进了他们的记忆中。非常奇怪的是,数据团队经常被认为不受这些实践的约束,并且遵循一套完全不同的开发标准(或者根本没有标准)。这可能部分是由于这些不同的起源;沿着这条线的某个地方,一个业务用户变成了一个分析师,然后他变成了一个数据工程师,并且从来没有一个明确的时刻,软件团队的某个人觉得有必要说“你现在是工程软件,你需要开始使用 Git。”
所以这就是,毫不含糊地说。作为 2019 年现代数据团队的一员,你需要测试你的代码和你的数据工件。您需要使用源代码控制,并让您的工作接受同行评审。您需要学*如何以小增量提供价值,并在挖掘主题数据的细微差别时进行调整。你需要对真正的 SLA 负责。这些不是手动版本控制、单枪匹马的数据工程/科学、眼球问答和从 sql 终端直接对生产数据库运行代码的“替代方案”;在越野旅行中,骑马并不是飞机的“替代”交通方式。
当然,这个主题还有很多,在不久的将来,我将尽我所能在更多的文章中深入探讨如何最好地处理数据团队软件工作流。
数据不会告诉你它是错的
故事大概是这样的
善意的首席技术官(或首席执行官、首席信息官或其他人)决定公司需要数据驱动。“我们将不再在数据黑暗中运营!”这位高管英雄大声疾呼,于是他们召集了一支精锐的工程师团队,设计了一个包括数据湖、提取-加载管道、数据仓库和商业智能工具在内的架构。来自所有主要应用程序的原始数据被泵入湖中,一些SELECT * FROM查询被用来生成扁平的“分析表”,然后砰!你有这些中的一个:

We have graphs so we must be data driven, right?
这个团队做了其中一件事:

We did it! This data stuff is so easy.
太好了!
除了在“发布”后不久,用户开始注意到数据与之前的报告不太匹配。他们如何理解数据或提供许多超出他们已知的真实见解也没有意义。几件事情都是以的方式进行的。很快各部门开始比较笔记,差异的积压建立起来。在短短的几个月内,用户的信心直线下降,你的商业智能解决方案的光荣的白衣骑士变成了另一个“真理”,伴随着它应该取代的资源大军。
简而言之,可视化中的数据不等于任务完成。从数据源→数据湖→数据仓库→可视化移动数据可能会让你走完 10%的路程;另外 90%是解密部落知识、神奇的数字、与用户理解(或需要理解)输出的方式相冲突的交易和操作结构,开发和实现质量测试,以及跨数据集整合结构。这项工作就是将数据转换成信息。这是数据团队的大部分价值所在,也是我们如何真正定义“完成”的。
我们可以使用一些不同的工具来解决另外 90%的问题(即取证、转换、质量测试和数据整合)。我是 Kimball 总线矩阵的忠实粉丝——它坚持下来是因为它有效。对于取证工作,我发现 DBA 伙伴关系和开发人员/利益相关者访谈是非常有价值的,但是没有什么比亲自阅读应用程序代码更有价值。事实是,你的数据产品交付在迭代框架中是最成功的,这就是为什么对数据团队使用 UAT 开发过程绝对是我最喜欢的内部分析解决方案之一。
无论您如何着手,请记住,您向组织提交的最早的数据产品将决定您的团队的信心水平。从长远来看,尽最大努力开发经过适当研究、测试和“强化”的数据产品将会获得回报。
数据现在非常热门

Everybody wants a piece of that data goodness
截至 2019 年秋季,数据是商业术语的南瓜调味品。公司想要它,消费者担心谁拥有它,内容创建者不知道如何处理所有这些数据,政府想要监管它,谷歌和脸书交易大量的数据……每个重要人物都以这样或那样的方式与数据有关,或者看起来是这样。对于数据团队成员来说,这既创造了一些惊人的机会,也带来了一些令人严重担忧的趋势。
首先,好消息是: 大家都喜欢你 。由于最*对数据的痴迷,我们正享受着来自商业同行的兴趣和支持的爆炸式增长。不是每天都有一个主要的成本中心能够在不被削减的情况下使他们的运营费用翻两三倍;然而,此时此刻,如果你说这是为了你一直在做的所有“机器学*”,有很多首席信息官会很乐意支付那个庞大的 AWS EMR 账单。由于这种数据狂欢的阴霾,我们从来没有像现在这样容易获得我们需要的东西,来构建能够以正确的方式影响业务的正确的东西。
现在是不太好的消息:所有的繁荣都会破灭。这种对数据的迷恋具备了 2005 年佛罗里达房地产泡沫的所有要素。如果你还记得很久以前,那是在英国《金融时报》做房地产经纪人的一段非常美好的时光。劳德代尔。到 2009 年末,就没那么多了。掌权的人,无论是首席执行官还是董事,甚至是董事会成员,他们都希望成为当今数据党的一员。因此,他们可能会参加数据团队会议,推动具有大量视觉 flash 的超大计划(通常没有与公司 okr 的关联),并关注技术而不是结果(特别是当它涉及到他们听到其他高管提到的工具时)。来自整个组织的风险承担者可能会指出数据团队似乎是每个问题的显而易见的解决方案,但通常没有相应的行动计划。最终,分析水晶球和人工智能推动的大规模增长的承诺将恶化为巨额资本投资的后遗症,而这些投资几乎没有有形回报。当另一只鞋最终掉下时,数据团队会感到手头拮据,而此时游客早已转向下一个商业时尚。
那么有哪些方法可以防范繁荣呢?一个是肯定要 掌握自己的命运 (后面会详细说明)。另一个警告你潜在危险情况的简单技巧是听这个单词:

That’s right. Beware the data.
奇怪的是,可操作的、有价值的数据团队工作很少在描述中包含“数据”这个词。请考虑以下情况:
可操作: 我们需要 salesforce 中的用户群组,通过我们新的 RFM 断点进行划分,这样我们就可以在假期到来之前重新调整我们发送的电子邮件。
绒毛: 我们的销量下降是因为我们没有 salesforce 的数据,所以我们没有任何见解。

可操作的工作有一个清晰的策略,说明数据(salesforce 用户群)如何变成信息(应用新的 RFM 断点)如何变成功能知识(更新电子邮件以提高转化率)。
试图用绒毛做出同样的价值路径要困难得多,因为它依赖“数据”来解决问题。数据不能解决问题或做出决策。人们做那些事情。如果有疑问的人没有数据可以告知的行动计划,这些数据对他们没有任何用处(你也一样)。
掌握你的命运
很多年前,我卖摩托车,在我工作的经销店,我们有一种说法:“你是销售员,还是接单员?”接单员只是简单地传达他们的想法,盲目地将顾客带到那周广告中的自行车前,读出价格标签,填写文件,然后送他们离开。另一方面,销售人员倾听顾客的意见,利用他们对机器和库存的了解,向顾客推荐最适合他们的自行车。接单员执行订单,销售人员 增值 。
同样的规则也适用于现代数据团队。人们很容易相信利益相关者的话,认为填充几个 Adwords 数据表将解决所有营销困境,并保证任何有股权的人都能提前退休,这是很危险的。鉴于我们工作的复杂性(创建 EL 流程、建模、部署、可视化等),我们可能会被一种虚假的安全感所迷惑——因为我们正在努力做一些很难做的事情,我们必须做得很好,对吗?
这是它分解的地方。当你致力于交付数据产品时,毫无价值的工作通常感觉就像是超级有价值的工作。这意味着您可以夜以继日地辛勤工作,应用最前沿的分析并深入研究数据质量,以交付一组壮观的工件,这些工件(不是您自己的错)可能对业务完全没有价值。毫无疑问,这个价值就是你和你的工作将如何被企业所评判。对于成功的现代数据团队来说,不仅要投资工作质量,还要投资工作价值,这种转变是关键的一步。
因此,一定要质疑你的利益相关者。恳求他们帮助你理解需要完成数据工作的原因,以及它如何让业务变得更好。我曾经共事过的一位杰出的数据科学家会将这种宝石用于分析/报告工作:
您能告诉我您将根据此请求的结果做出什么决定吗?了解这些决策将有助于我们确保我们为您构建的(报告/数据/模型)能够满足您的需求。
对于产品化的数据也可以提出同样的问题(“我们如何从这种产出中赚钱?”)和指标(“哪些组织 KPI 与这些指标相关联,如何相关联?”).最重要的是,如果答案不是你可以合理推后的,那就推后。没错——数据团队有责任确保他们在增加价值,即使这意味着(非常委婉地)告诉 CEO / CIO /总监你不同意的任何事情。有理由说,你的自我评估中的“胜利”一栏今年需要一些重大胜利,而“本小时发货的 10 大产品”仪表板不会让我们成为一个组织。你可能仍然需要制造一些无用的东西,偶尔运送一些低价值的工件来维持和平可能是很好的策略。但是,如果你发现自己在应该是“销售人员”的时候却一直扮演着“接单员”的角色,那么是时候开始寻找更好的工作了。在预算季节,没有人会急于为数据团队辩护,不管你在前一年按照要求成功完成了多少低价值的请求(这被称为高管健忘症)。
还有更多这是从哪里来的
到目前为止,我已经浏览了一些来之不易的现代数据课程;我的希望是在未来的几个月里,从过程和技术的角度进行更深入的探索,并分享一路上觉得有价值/有趣/值得的事情。
数据工程-熊猫 101
Pandas 是数据分析和工程的一个很好的工具。我已经使用它大约三年了——在那之前,它是 Python 库的大杂烩,有点令人生厌。
熊猫= Python 数据分析库

那么,熊猫是什么,它为什么伟大?
- 你可以很容易地从本地文件或连接到数据库远程加载数据。
- 你可以用探索的方式处理数据,很容易地总结和查看数据的统计数据;
- 你可以清理、操纵(切片、切块)重塑并组合数据,更轻松地沿着链条往下处理;
- 您可以更有效地处理大型数据集——减少使用它们时使用的内存,非初学者的深入文章在此。
- 您可以访问数据帧——一种类似表格的数据结构(我们在 R 语言中已经有很长时间了);
- 您可以对结果数据集进行基本的绘图——相当不错;
熊猫官方网站在这里:
[## Python 数据分析库- pandas: Python 数据分析库
pandas 是一个开源的、BSD 许可的库,提供高性能、易于使用的数据结构和数据…
pandas.pydata.org](https://pandas.pydata.org/)
安装熊猫非常简单:
mkdir panda-playground
cd panda-playground
pip install pandas
我们还可以访问大量样本数据集
数据集
您可以通过 URL 或本地文件以多种不同的方式在 Pandas 中加载数据集:
iris = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
来自 MySQL 等数据库,如下所示:
cn = MySQLdb.connect(host='localhost', port=3306,user='un', passwd='pass', db='zoo')
pd.read_sql('select * from animals;', con=cn)
我们将在以下示例中使用的数据集与视频游戏销售相关,可在此处找到:
[https://github.com/jph98/pandas-playground/blob/master/vgsales-12-4-2019.csv](https://github.com/jph98/pandas-playground/blob/master/vgsales-12-4-2019.csv)
注意:为了创建下面的表格,我最后从 Pandas 获取表格输出,并将其转换为 CSV 格式,格式为:
import pandas as pd
import numpy as nadf = pd.read_csv('vgsales-12-4-2019.csv')
overview = df.describe()
print(overview.to_csv())
然后作为 Github gist 上传,再嵌入到这里。非常好用。
汇总数据
开箱后,我们可以查看一些数据来了解我们正在处理的内容,前 n 名的结果集:
df.head()
Output of calling the head() function on the dataframe
注意:当你这样做的时候,你会注意到列会根据 Pandas 中的设置被截断,为了避免这个限制,你可以如下设置 display.max_columns:
pd.set_option('display.max_columns', None)
或者获取有用的数据摘要,以了解我们在做什么:
df.describe()
Summary statistics output from the describe() function
您还可以使用以下方法从数据集中获取一组随机的样本行:
df.sample(n=100)
或者看看数据框的一些特定属性:
# Get the number of rows
len(df.index)# Get column names, get number of columns
df.columns
len(df.columns)
好了,我们来看提取一些数据。注意:当您使用 Pandas 提取数据时,它将包含一个初始列号作为列以及所选数据。
子集数据(观察值和变量)
您可以通过以下方式提取一个特定游戏或游戏范围:
df.loc[0]
df.loc[0:3]
抓取第一个和第二个视频游戏的名称和流派栏
df.loc[0:1,['Name']]
或者实际上提取整个特定列,在本例中,游戏流派,但只有唯一值:
df['Genre'].unique()

使用表达式过滤数据
我们还可以在提取中包含表达式,这样我们就可以更加优雅和复杂地查询数据。
让我们显示由 vgchartz.com 计算的所有视频游戏评分。在这种情况下,我对所有得分在 9.2 或以上的精彩游戏感兴趣:
So, no surprises there, Super Mario Galaxy, Grand Theft Auto, Zelda, Metal Gear Solid and the truly excellent World of Goo (well done Kyle Gabler) all feature here in the results.
现在,我们可以使用以下内容按降序排列这些内容:
df[df.Vgchartzscore > 9.2].sort_values(['Vgchartzscore'])
并将结果数量限制为前五个游戏:
df[df.Vgchartzscore > 9.2].sort_values(['Vgchartzscore']).head(5)
注意,你可以修改我们加载到内存中的数据帧。然而,这个并不修改底层的 CSV 文件。如果我们想要将数据保存/持久保存到文件中,我们必须使用以下命令显式地将数据帧写入内存:
df.to_csv('outputfile.csv')
这将保存整个数据框。要将它限制到子集化的数据,只需用 to_csv 再次链接:
df[df.Vgchartzscore > 9.2].sort_values(['Vgchartzscore']).head(5).to_csv('outputfile.csv)
处理缺失数据
我们在数据工程中遇到的一件常见的事情是需要通过清理来修改传入的数据——毕竟我们不想接收坏数据,并希望在某种程度上使事情正常化。
删除空行或空列 删除空行(指定轴=0)或空列(指定轴=1),如下所示:
df.dropna(axis=**0**, how='any', thresh=None, subset=None, inplace=False)
您可以通过以下方式删除特定列:
df.drop(['Rank'], axis=1)
缺少值
上述数据集中的某些特定于行的列具有 NaN(非数字)值。要获得至少有一列为空的行的计数,我们可以使用:
df.isnull().sum().sum()
或按列细分,包括:
df.isnull().sum()
我们有几个选择:
- 我们可以排除包含空列的行
- 我们可以通过标准化到已知值来清理行
例如,对于 NaN 的 Vgchartzscore,我们可以使用以下公式将这些值归一化为 0.0:
df['Vgchartzscore'].fillna(0, inplace=True)
这应该会留下大约 799 行非 0.0 值。

分组数据
有很多方法可以重塑 Pandas 中的数据。我们来看看 groupby 函数。我们可以将此与按流派分类的视频游戏数据集一起使用,以了解符合该类别的视频游戏的数量:
df.groupby('Genre').first()
我们可以通过以下方式获得每个组别的游戏数量:
df.groupby('Genre').size()
然后,我们还可以应用 sum()或其他支持的函数,如下所示:
df.groupby('Genre').first().sum()
好了,接下来我们要按平台(PS4、XBox 等)对这一类型进行分类..)来理解游戏,所以:
df.groupby(['Genre', 'Platform']).first()
注意:如果您想遍历每个组,要做一些后处理,您可以执行以下操作:
by_genre = df.groupby(['Genre', 'Platform'])
for name, group in by_genre:
print(name)
最后,我们可以利用聚合函数对我们分组的数据进行一些计算。在下文中,我们将查看各类型平台中 Global_Sales 的平均值和总和:
g = df.groupby(['Genre', 'Platform'])
res = g['Global_Sales'].agg(["mean", "sum"])
结论
因此,Pandas 为您提供了丰富的功能来帮助您进行数据分析和操作。你不会想在 SQL 或原始 Python 中这样做,主要是因为它更复杂,你必须编写更多的代码,或者你受到所提供的功能的限制。聚集和分组是我选择熊猫的两个关键场景。
您可以在下面的 Github 示例库中找到本文提到的代码的 Python 3 示例:
我的媒体文章的熊猫乐园存储库- jph98/pandas-playground
github.com](https://github.com/jph98/pandas-playground/)
这是关于数据工程的一系列帖子中的第一篇,我将在后续的帖子中涉及以下内容(写好后从这里链接):
- 用 matplotlib 实现熊猫数据框的可视化
- 熊猫使用自动气象站的元素进行大型数据处理——SQS 进行协调,然后构建管道进行处理等…
不要脸的塞: )
- 我通过 hwintegral 运行关于 Python 和 Pandas 的教练会议;
- 我提供数据团队指导、数据架构审查和转换(团队和技术)。
如果您的公司需要帮助,请通过以下方式联系:
尽职调查和技术审计在 HW Integral,我们为风险投资和私募股权公司的投资提供尽职调查服务…
www.hwintegral.com](https://www.hwintegral.com/services)
数据工程——数据科学的表亲,很麻烦
如何让你的分析师意识到扩展他们工具包的重要性?我想我已经找到答案了。
我们一直认为数据科学是“21 世纪最性感的工作”。当谈到从传统公司到分析公司的转型时,无论是公司还是数据科学家都希望尽快进入分析的奇妙世界。但是,总是这样吗?
麻烦的开始
自从我们,一个来自加州大学戴维斯分校的实*团队,开始与电动工具和相关服务的领先制造公司 Hilti 合作以来,我们已经提供了几个精彩的蓝图:定价自动化,倾向模型…与这样一个伟大的公司合作对我们来说是如此宝贵的机会,我们迫不及待地利用我们的分析技能来创造商业价值。但当我们开始挖掘数据时,我们发现,与电子商务公司等数据驱动型公司相比,我们很难直接从传统公司获得干净和结构化的数据。
由于我主要负责该项目的数据清理和工程设计,我目睹了我们如何因未准备好的数据而阻碍了分析进度。

I witnessed how we were hindered in the analytical progress due to the unready data
我们直接与财务团队合作,但另一个团队——定价运营团队——实际上负责数据库。一开始,这个过程严重滞后,因为我们几乎不能及时请求和查询数据或人员。此外,由于喜利得的销售数据非常敏感,并且该公司缺乏安全的数据传输方式,因此每次请求数据时都需要执行耗时的屏蔽过程。第三,薄弱的数据工程导致了几个参考表之间的不一致,我们几乎不能得到一个可靠的模型或结论。最后,我们必须处理各种数据类型:csv、JSON、SQLite 等……尽管这是一个很好的学*机会。
大约两个月后,我们准备好了所有的数据,讨论并解决了每一个异常情况。
潜水时间!
我们开发良好的可视化框架和模型迫不及待地想要品尝新鲜的数据。然而,最尴尬的事情发生在我们拿出第一份提案的实际数字时。

你猜怎么着,大数字似乎不匹配。快速讨论后,我们意识到我们根本没有收到完整的数据。我们只关注数据的细节,如异常和数据源之间的关系,但我们忘记了做基本的检查,如总和和计数。这是我将终生铭记的一课。真的!
为什么数据工程如此重要
我从数据工程戏剧中学到的最重要的事情是,在幕后工作的角色,例如数据工程师,实际上掌握着创新的大门。当传统公司考虑利用他们的数据时,最有效的第一步行动应该是改进数据工程过程。有了优秀的数据工程师,公司可以建立一个健康、可扩展的数据管道,让数据分析师更容易进行数据挖掘和寻找商业见解。

我还了解到,除了 SQL 和 Excel 等分析工具之外,为什么许多公司要求他们的数据分析师具备 Python 和 Scala 等编程相关工具的知识。通常,我们不能指望一个“全栈”的分析师,但我们必须有一个既能与工程人员沟通又能与管理人员沟通的人。虽然明确的工作分配对高效率很重要,但一个精通每一种数据工具的人确实是有吸引力的。

Full-stack…makes sense!
我期望自己在未来学*的是正面和背面的知识,如 Java、JavaScript、Kafka、Spark 和 Hive,我相信最终它们将成为我经验中的闪光点。
使用 Python、Django 和 PostgreSQL 的数据工程

今天的帖子将讨论数据科学中最困难的方面之一,它不涉及分析,而只是试图让数据科学的后端工作。我所说的后端指的是大多数数据科学家在工作中会用到的数据库系统。
我将回顾以下内容:
- 用关系数据库管理系统(RDBMS)构建一个绝对的准系统 Django 应用程序
- 演示附加到 Django 应用程序的 PostgresSQL 数据库的使用
- 如何在不同格式和平台之间移入和移出数据
虽然阅读本文不需要任何 Django 知识,但我认为重要的是要认识到大量数据收集是通过 web 应用程序进行的。
对于不熟悉 Django 的数据科学家来说,可以把它看作是一个构建 web 应用程序的框架,同时坚持“控制反转”的哲学。这意味着 Django 负责 web 应用程序的框架,而您负责在框架上充实实际内容。
对于不喜欢 Django 的读者,你可以跳到这篇文章末尾的标题为“回报:Django 的对象关系映射器”的部分。
我们的 Django 应用程序:“DoubleBagger”
我感兴趣的应用程序将被称为“DoubleBagger”,这是一个投资博客,人们可以在这里自助发表对苹果(股票代码:AAPL)或微软(股票代码:MSFT)等上市公司的买入/卖出意见。
这次我们将主要使用命令行+一个像 Sublime Text 这样的文本编辑器,而不是像我以前的文章那样使用 Jupyter 笔记本。
因为这是针对数据科学家的,我们将使用 conda 环境:
# I like to do things on my desktop
# From the terminal:$ cd desktop && mkdir doublebagger && cd doublebagger$ conda create -n doublebagger
$ conda activate doublebagger# You should now have the (doublebagger) conda environment activated
现在我们安装两个主要的包:Django 和 psycopg2,用于连接 PostgreSQL 数据库。Django 已经发布了 SQLite,它实际上可能适合许多组织和爱好者,但是我们将使用 Postgres 来代替。此外,我们将使用 Django 的旧版本(当前版本是 Django 2.1)。
$ (doublebagger) conda install Django==1.9.6 psycopg2
在验证您拥有这些包及其依赖项之后,创建一个源目录,我们将所有与“Doublebagger”相关的源代码放在这个目录中。
$ (doublebagger) mkdir src && cd src
我们用相同的命令以几乎相同的方式启动每个 Django 项目:
# Inside of src:
# don't forget the space and period at the end$ (doublebagger) django-admin startproject doublebagger_blog .
django-admin startproject 命令为我们的项目创建了框架,现在如果您查看 src 文件夹中的内容,您应该会看到:
- doublebagger_blog:包含我们项目的项目配置,包括 settings.py 文件。
- manage.py:实用函数
现在我们可以在 Sublime Text 或您选择的任何其他编辑器中打开我们的 DoubleBagger 项目。您应该会看到完全相同的目录结构:

假设您的机器上已经安装了 postgres 数据库,我们实际上需要为 django 应用程序创建一个 postgres 数据库:
# from the command line:$ psql -d postgrespostgres=# CREATE DATABASE doublebagger;# That's it!
# quit by:postgres=# \q
*如果您没有 postgreSQL,您可以遵循这些指令。
然后在 settings.py(使用 Sublime Text)中,我们更改默认配置以考虑我们刚刚创建的数据库。改变这一点:
# settings.pyDATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
对此:
# Your 'User' is probably different depending on how you set up
# postgres. In many cases, it's just 'postgres'.
# Also depends if you set up a password with you postgres.DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'doublebagger',
'USER': 'WhoeverOwnsTheDatabase',
'PASSWORD': '',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
*确保在文本编辑器中保存您的更改
现在,如果你回到命令行,我们可以像这样连接应用程序和 postgres 数据库:
# Still inside of your src where manage.py lives:$ (doublebagger) python manage.py migrate
如果一切顺利,您应该会看到类似这样的内容:

现在从同一个命令行:
$ (doublebagger) python manage.py runserver
并将您的浏览器指向:
127.0.0.1:8000
您应该会看到类似这样的内容:

image sponsored by fakedoors.com
这实际上是从 Django 的本地开发服务器上显示的假想 web 应用程序的主页,旨在模拟真实的 web 服务器。你可以用
这几乎是这个应用程序的全部了——甚至没有基本框架,甚至没有 hello world。
那么,我们为什么要费这么大的劲来做这件事呢?
因为 Django Models 为其用户提供了一个对象关系映射器(ORM ),允许我们在连接到 postgresSQL 数据库时使用 python 操作模型对象。这只是构成 pythonic 数据科学生态系统的绝对动物园的一个更复杂的层次和意识。
此时,我们需要考虑数据的组织。
我们的 web 应用程序将主要有两个主要组件或模型类:
- 帖子:关于是否投资特定公司的博客帖子
- 公司:博客文章中提到的公司的信息
帖子将包含以下信息:
- 帖子的标题
- slug:基于标题的博客文章的唯一标识符)
- 文本:实际的博客文章文本
- 发布日期:文章发布的时间
公司将包含以下信息:
- 公司名称
- slug:公司的唯一标识符
- 描述:该公司做什么
- 市盈率:公司相对于市场价格的估值指标
在我们将其转化为 Django 模型代码之前,我们需要先创建一个 Django 应用程序。
# We're going to call our app 'post'
# Inside of src from terminal:$ (doublebagger) python manage.py startapp post
现在,您应该可以在文本编辑器中看到 post 应用程序:

之后,我们必须在 settings.py 中的 INSTALLED_APPS 下添加我们的“post”应用程序:
# settings.pyINSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'post',
]# Don't forget to save changes
现在,我们可以通过 post/models.py 为我们的数据编写组织代码:
# post/models.pyfrom __future__ import unicode_literalsfrom django.db import models# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField()
text = modelsle.TextField()
pub_date = models.DateField()class Company(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField()
description = models.TextField()
pe_ratio = models.DecimalField(max_digits=7, decimal_places=2)

在这一点上,我们需要考虑的模型的最重要的方面之一是这两件事是如何相互关联的。如果我们看看上面所做的,从 SQL 的角度来看,Post 是一个表,Company 是它自己的表,下面的字段如 title、slug 和 pub_date 代表两个表的列。
对于 SQL,我们需要考虑我们的两个模型之间的关系是否是:
- 一对多或多对一
- 多对多
如果你考虑一下,我们这里有一个岗位和公司之间的多对一关系:
“一篇博文只能是一篇关于一家公司的投资论文,但一家公司可以有很多篇关于它的博文。”
因此,我们的 Post 模型将有一个外键指向 Company。
Django 的美妙之处在于,它为我们处理了通常从零开始创建数据库模式的所有困难工作——不需要显式创建主键,不需要索引或多对多关系的连接表。
我们可以像这样添加外键:
# post/models.pyclass Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField()
text = modelsle.TextField()
pub_date = models.DateField()
company = models.ForeignKey('Company') # Here
添加一些额外的参数和字符串方法,允许我们的模型对象被字符串名称引用,这是我们最终更新的 post/models.py:
# post/models.pyfrom __future__ import unicode_literalsfrom django.db import models# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=50, unique_for_month='pub_date')
text = models.TextField()
pub_date = models.DateField('date_published', auto_now_add=True)
company = models.ForeignKey('Company')def __str__(self):
return "{} on {}".format(
self.title,
self.pub_date.strftime('%Y-%m-%m'))class Meta:
verbose_name = 'investment thesis'
ordering = ['-pub_date', 'title']
get_latest_by = 'pub_date'class Company(models.Model):
name = models.CharField(max_length=100, db_index=True)
slug = models.SlugField(max_length=50, unique=True)
description = models.TextField()
pe_ratio = models.DecimalField(max_digits=7, decimal_places=2)def __str__(self):
return self.nameclass Meta:
ordering = ['name']# Don't forget to save your changes
现在神奇的部分来了,Django 为我们创建了一个 postgreSQL 数据库。
回到命令行:
$ (doublebagger) python manage.py makemigrations# Then $ (doublebagger) python manage.py migrate# That's it!
您应该会看到类似这样的内容:

回报是:Django 的对象关系映射器(ORM)
现在,我们可以像数据科学家一样,只使用 Python 与 PostgreSQL 数据库进行交互!
从命令行:
$ (doublebagger) python manage.py shell
您应该看到一些让您想起 Python 解释器的东西,除了这个允许您使用 Django 的数据库:
>>> from datetime import date
>>> from post.models import Post, Company
让我们创建一个公司对象:
>>> Company.objects.create(name='Apple', slug='Apple-incorporated-nasdaq', description='One of the greatest companies in the world created by the amazing Steve Jobs.', pe_ratio=14.43)
您应该会看到这样的内容:
<Company: Apple>
不需要:
INSERT INTO company
(name, slug, description, pe_ratio)
VALUES
('Apple', 'Apple-incorporated-nasdaq', description='...', pe_ration=14.43);
更多公司:
>>> Company.objects.create(name='Amazon', slug='amzn-nasdaq', description='AWS baby!', pe_ratio=81.48)>>> Company.objects.create(name='Microsoft', slug='msft-nasdaq', description='I love GitHub!', pe_ratio=26.02)>>> Company.objects.create(name='MongoDB', slug='mdb-nasdaq', description='JSON in the cloud', pe_ratio=100)>>> Company.objects.all()# Output:[<Company: Amazon>, <Company: Apple>, <Company: Microsoft>, <Company: MongoDB>]
我们还可以做一些很酷的查询:
>>> Company.objects.get(slug__contains='mdb')# Output:<Company: MongoDB>
谁喜欢元组:
>>> Company.objects.values_list()# Output:[(3, u'Amazon', u'amzn-nasdaq', u'AWS baby!', Decimal('81.48')), (1, u'Apple', u'Apple-incorporated-nasdaq', u'One of the greatest companies in the world created by the amazing Steve Jobs, not so currently under Tim Cook.', Decimal('14.43')), (2, u'Microsoft', u'msft-nasdaq', u'I love GitHub!', Decimal('26.02')), (4, u'MongoDB', u'mdb-nasdaq', u'JSON in the cloud', Decimal('100.00'))]
我们还可以创建一个 Post 对象:
>>> Post.objects.create(title='I heart Apple', slug='i-heart-apple', text='I heart Apple', company_id=1)>>> Post.objects.create(title='Buy more Microsoft', slug='buy-more-microsoft', text="Nadella leads the way", company_id=2)>>> Post.objects.create(title='Buy more Amazon', slug='buy-more-amazon', text="Jeff Bezos acquires Mars from Elon Musk", company_id=3)>>> Post.objects.create(title='Time to sell MongoDB', slug='time-to-sell-mongdb', text="MongoDB seems a little overvalued", company_id=4)
我们还可以验证这些对象确实存在于 postgreSQL 数据库中:
$ psql -d postgres
postgres=# \c doublebagger
doublebagger=# \dt
doublebagger=# SELECT * FROM post_company;
您应该会看到类似这样的内容:

和一个 SQL 连接:
doublebagger=# SELECT * FROM post_company
doublebagger=# JOIN post_post ON
doublebagger=# post_company.id = post_post.company_id;

从 postgreSQL 到熊猫
使用 sqlalchemy 我们可以直接从 pandas 访问 postgreSQL 对象:
如果你启动一台 Jupyter 笔记本:
import pandas as pd
from sqlalchemy import create_engineengine = create_engine('postgresql://sammylee@localhost:5432/doublebagger')posts = pd.read_sql('select * from post_post', engine)companies = pd.read_sql('select * from post_company', engine)posts_companies = pd.merge(companies, posts, left_on='id', right_on='company_id')posts_companies

现在,作为数据科学家,我们可以充分利用熊猫的力量和魔力做我们想做的任何事情。
虽然 web 开发框架的知识对于数据科学家来说不是必需的,但我认为退一步看看亚马逊森林是非常酷的,从 Django 到 postgreSQL 再到 pandas 都是数据科学。
还有一点
Django 有一个“包含电池”的理念,它提供了一个管理界面,基本上作为 postgreSQL 数据库的 GUI。我们所要做的就是在 post/admin.py 中添加一些行
# post/admin.pyfrom django.contrib import admin
from django.db import models# Register your models here.
from .models import Post, Companyclass MyCompanyAdmin(admin.ModelAdmin):
model = Company
list_display = ('name', 'slug', 'description', 'pe_ratio',)admin.site.register(Company, MyCompanyAdmin)
并在命令行上创建超级用户:
$ (doublebagger) python manage.py createsuperuser# Then create your credentials
# start up your local development server and head to your local host
# and append "/admin" to it

双重行囊者生活的代号在这里。
用对立的自动编码器进行数据探索
使用对立的自动编码器模型研究多元时间序列数据的非监督聚类的不同方法。

L 和许多人一样,我用自动编码器开始了我的深度学*之旅,因为它们是开发人工神经网络直觉的一个很好的切入点,并让你的头脑围绕你选择的深度学*框架。起初是一个没有多少相关用例的练*,如今已经找到了许多应用,结果是一把深度学*的瑞士军刀。自动编码器通常用于检测异常,学*可以输入到其他神经网络的低维表示,生成数据等等。在这篇文章中,我想向你介绍一种叫做对抗性自动编码器的特殊架构,以及随之而来的自动编码器的新应用,数据的无监督聚类,这将是我在这篇文章中主要关注的。我想讨论这样一个编码器的工作原理,并向您展示如何使用 Keras 轻松构建和训练它们,以及如何使用它们在一个涉及外汇市场数据的例子中聚类和探索时间序列。
引入对抗性自动编码器
当我第一次开始进入深度学*时,自动编码器让我着迷,我喜欢它的设置如此简单,以及用它们进行实验如何快速让你培养对人工神经网络的感觉。这个想法非常简单,训练一个神经网络模型,在网络的中间有一个信息瓶颈太小,数据无法通过的约束下,复制它的输入。这种限制要求自动编码器找到适合该瓶颈的输入数据的表示,以便随后尝试从该表示尽可能精确地再现它。它必须找到一个简洁的代码——因此得名自动编码器——它通常也被称为潜在向量。虽然这听起来非常简单,这种类型的神经网络允许你为任何类型的数据找到一个几乎任意低维的表示,同时你也获得了一个数据样本的不寻常或异常程度的度量,这取决于它的重建误差。
抽样问题
有一件事你不能用普通的自动编码器来做,那就是生成新数据。理论上,你可以把一些随机向量输入到解码器部分,得到新的猫图片或者你已经编码的任何东西。这不起作用的原因是,编码器产生的潜在表示不以任何方式分布,它可以通过简单的参数分布来表示,就像正态分布一样,您可以从中进行采样。相反,潜在空间经常出现分裂成代码空间中的不同域,如果你只是向解码器提供从像正态分布这样的随机变量中采样的随机向量,你很可能最终只生成垃圾。换句话说,你不知道如何为解码器选择随机值来产生高质量的猫图像。
为了解决这个问题,发明了变分自动编码器来训练编码器学*潜在分布的参数,然后可以从解码器采样并馈入解码器。问题仅仅在于它们往往难以训练,并且即使变型自动编码器在捕捉随机性质的数据的属性方面更好,并且在采样和生成数据方面具有更好的属性,但是它们产生的样本仍然经常包括较差的示例,主要是在输入数据的类别之间的边界上,如果存在的话。潜在空间仍然没有得到理想的分布,特别是如果数据不能被在变分推断过程中使用的高斯分布很好地*似。此外,有一件事你不能用变分编码器做,那就是让潜在的代表采取任何任意分布的性质,因为他们只学*多重高斯分布的性质。但是如果你想从贝塔分布或者均匀分布中取样呢?

规范代码
自从生成对抗网络和对抗训练方法的出现,神经网络不仅能够学*分类,还能够产生照片级的图像,现在甚至是视频。事实证明,在 GANs 中用于训练发电机的相同技术也可以用于调整你的神经网络的属性。敌对自动编码器背后的想法是,你训练编码器产生一个潜在的空间,看起来像你选择的先验分布。如果您的先验是正态高斯分布,那么编码器产生的潜在向量也将假定该分布,值的平均值为 0,标准偏差为 1,您可以轻松地从中进行采样。你基本上可以把任意的分布强加到潜在空间上,甚至是那些不能用参数化的方式表达的,只要你有一个从它们中取样的方法。
由于这是对抗训练中的传统,这是通过一个鉴别器实现的,这是一个不同的神经网络模型,它学*从目标的现实世界示例中辨别网络的输出;但是,鉴别器不是学*从真实的猫图片中辨别噪声,而是学*从先验分布中辨别潜在空间,并向编码器反馈潜在空间的分布有多错误。根据这种梯度形式的反馈,编码器学*按需要分配代码。
如何用它对数据进行聚类?
在关于对抗性自动编码器的原始论文的第 5 章和第 6 章中,作者概述了如何将他们的架构用于无监督聚类的方法。其思想是,除了潜在向量之外,编码器还将生成一个分类向量或所谓的一个热编码的向量,因此该向量的一个值为 1,所有其他值为 0。该向量也将被馈送到解码器中用于重建,使得它也可能携带关于输入数据的相关信息,基本上是用于重建的某种附加提示。这个想法是,这个提示对于相似的输入数据也是一样的,这些相似性是我们希望编码器找到并编码到类别中的。与经典的聚类算法相反,在经典的聚类算法中,预定义的参数决定了如何分离数据,编码器和解码器只在不同类别之间的界限上达成一致,这可能会导致有趣的结果。
为了让编码器产生一个分类向量,而不仅仅是另一个任意值的潜在向量,作者还使用了带有鉴别器的对抗训练,该鉴别器被教导来辨别向量是否是分类的。以与潜在向量相同的方式,鉴别器根据编码器产生的向量以及“先验分布”来训练,在这种情况下,先验分布是一束随机的独热向量。该鉴别器还将向编码器反馈向量遵循规则的程度,并通过使其成为编码器目标的一部分来满足鉴别器,它将学*创建分类向量。

Schematic of an Autoencoder that produces a regularized latent vector z and a categorical vector c with the help of two Discriminators
数据
因为 MNIST 手写数字数据集已经由作者在论文中完成了,并且在这一点上可能超出了理性的探索,所以我想尝试一些更有趣的东西。不,不是猫的照片,很不幸。我想在这里讨论的实验是我最*研究的,作为我个人探索建立算法交易系统的一部分,就像我之前的许多人一样,因为它提出了一个有趣的挑战。对于这个例子,我将使用外汇数据,特别是 2018 年的欧元兑美元汇率,以 5 分钟为间隔。你可以从互联网上的很多地方获得这种数据。我从一家名为 Oanda 的在线经纪商那里获得了我的 API,主要是因为我喜欢他们的 REST API,你可以通过一个免费的票据交易账户来使用它,以访问可用工具的所有历史市场数据。最后,无论您是使用外汇数据,还是将本文概述的相同策略应用于股票或加密或非金融时间序列数据,如物联网传感器或医疗数据,都无关紧要。你可以在文章末尾附上的笔记本中找到下载和预处理数据的代码。

One day of EUR-USD exchange data in 5-minute intervals with open, high, low and close prices.

The volume data for the above time period
我们将从原始交易数据中创建并最终提供给自动编码器的特征将是最高价、最低价和收盘价的对数回报、交易量,以及一些统计数据,如收盘价的滚动平均值、方差和偏斜度以及一些技术指标、相对强弱指数、平均真实范围和移动平均收敛发散信号线。除了包括包含关于趋势强度和方向、波动性和与平均值的偏差的指示的特征,以及具有编码器必须找到潜在相关性、模式和简洁表示的一些特征之外,没有做出选择的特定原因,并且仅在有噪声的回报数据上进行聚类不会产生最有意义的结果。当然,你还可以加入许多其他特性,或者添加信号和数据源。

An excerpt from one of the data frames with the raw features.
为了让编码器创建一个较低维度的表示,并将其重新创建为一个序列,我们需要处理固定长度的信号,因为时间分量在压缩中丢失,并且在构建编码器时需要明确定义序列长度,因此我决定将数据切割为 32 个时间步长的窗口。由于我们的数据特征在不同部分具有非常不同的值范围,如价格、交易量和指标,我们在每个窗口的基础上为每个通道执行 z 得分标准化,以将所有特征纳入相同的值范围,并保留局部模式,但不混合过去或未来的极值。这将是编码器将尝试聚类的数据,即以 5 分钟时间间隔的 32 个读数的序列中的局部运动,如下图所示。

A few frames of preprocessed data, as they will be fed to the autoencoder. The values are in a comfortable range for ANNs. We expect the encoder to find patterns in this mess of squiggly lines.
注意:我已经讨论了很多关于如何预处理数据的问题,通过对整个数据或每个窗口进行标准化或规范化。我发现这很大程度上取决于。如果你要建立一个算法交易系统,你想把编码器的结果输入到一个分类器中。那么您可能也会在每个窗口的基础上进行标准化。这意味着编码器将通过这些局部模式对数据进行聚类。如果您有一个从不更改的数据集,并且您不关心未来的值,并且希望按全局属性对数据进行聚类,那么对整个数据进行规范化或标准化可能会更有意义。在进行了一些实验之后,我可以说这两种方法对于这种类型的数据会产生非常不同的聚类类型。
构建对抗性自动编码器
因为我们处理的是时间序列数据,所以我们将主要从循环层构建编码器和解码器。简单的 LSTM 不适用于对时间序列数据进行编码的特定任务。经过一些试验后,我惊讶地发现,双向 LSTMs,另一方面,比我迄今为止尝试过的任何东西都要好,包括卷积网络。有些人还推荐卷积网络和递归网络的组合,无论如何,可能需要一些实验来找到适合您的数据类型的架构。
编码器和解码器都主要由两个堆叠的双向 LSTM 组成,其中编码器的第二 LSTM 不返回序列,而仅返回其最后状态作为潜在表示和分类向量的基础。解码器由一个类似的反向设置组成,输入潜在向量和分类向量的组合,并尝试将其解码为尽可能匹配输入的序列。
鉴别器被设计成简单的多层感知器,由几个密集连接的层组成,因为它们只需要区分相当低维的向量的分布,所以你不能把它们做得太好,否则编码器将不能欺骗鉴别器,也不能正确地收敛于任何任务。这也考虑到对每个任务的损失进行加权,输入的重建将不得不产生梯度的大部分,否则编码器将不会学*到任何有用的东西。另一方面,你也不能让他们太弱,否则他们不会学会区分样本,也不会发生正则化。
下面是为编码器/解码器和鉴别器创建网络的代码。你可以看到,正如论文作者所建议的,我们只在编码器部分使用了批量标准化。我也尝试在解码器部分使用它,对于无监督聚类应用程序,我可以确认它只会恶化性能。您可以看到,分类向量将 softmax 作为激活函数,这有助于形成分类分布,而潜在向量是用线性函数激活的,因为它需要能够输出未绑定范围内的值,以便能够满足指定的分布。然后,简单地将潜在向量和分类向量连接起来,并对输入序列的长度重复该结果,从而解码器部分可以再次将其重建为相同长度的序列。我还在输入后添加了一个线性变换,因为我发现当使用整流器和负值输入时,它在一定程度上加快了训练速度。
The code to create the autoencoder model.
The code to create the discriminator models.
当训练自动编码器时,两个鉴别器的输出成为自动编码器损耗的一部分。他们的目标只是简单地总是 1 ,对于实数,因为我们希望编码器学*一个表示,使鉴别器输出 1 ,基本上是说“啊,是的,这是来自先前分布的样本”。二进制交叉熵作为损失函数工作得很好,如果编码器产生不满足鉴别器的潜在向量或分类向量,损失将产生梯度,这将使编码器学*理论上遵循规则的潜在和分类表示。

The Encoder is updated with gradients from the Decoders reconstruction loss (Mean Squared Error) and the Discriminators loss (Binary-Crossentropy)
下面是用于将编码器和鉴别器粘合到模型中的代码,然后我们可以按照上面的示意图进行训练。请注意我们是如何将鉴别器权重设置为trainible = false的,这在此时是必要的,因为我们将它们连接到自动编码器中,并且在训练编码器时,我们不希望鉴别器的权重发生变化。稍后在训练鉴别器时,我们会更改这个设置,但你必须在给它们接线时进行,否则 Keras 或者更确切地说 TensorFlow 会抱怨说组可训练重量已经改变。
The code to wire the autoencoder together with the discriminators and create an adversarial autoencoder.
训练编码器
为了训练这种设置,我们首先从训练鉴别器开始。为此,让编码器创建一批潜在的和分类的向量,并创建第二批向量,从先前的分布中取样。接下来,使用各自的标签训练这些批次的鉴别器,编码器输出的标签为 fake (o) ,先前样本的标签为 real (1) 。然后,您冻结该训练迭代的鉴别器的权重,因为您不希望在训练编码器时更新鉴别器,因为误差将流经它们。接下来,您对一批数据训练编码器,目标是尽可能准确地再现输入,同时还有另外两个目标来满足两个鉴别器,也就是 aka。对于真实样本,使它们输出 1。在重复了大约几千次随机小批量训练迭代之后,自动编码器开始在所有三个任务上收敛。
在训练过程中,您可能会注意到,只要编码器仍在学*编码和解码,鉴别器的精度只会缓慢下降,只有当来自该目标的梯度变得较浅或平稳时,编码器才会学*重新分配潜在空间和分类向量,这反过来会再次恶化编码/解码性能,这将使梯度朝着较低的重建误差变陡。这意味着学*过程通常遵循某种反馈循环,这也是由于鉴别器随着时间的推移变得越来越好,随着训练的进行,这种反馈循环将变得不那么强烈。
现在,当我们让上面代码中所示的训练循环总共运行 10,000 次迭代时,您可能会得到类似下图中的损失图,这些图来自我用包含的代码为本文训练的示例运行,我想在下面进一步讨论。
在图像中,您可以看到测试和验证集的重建损失在适当减少,因此看起来我们并没有过度拟合。分类向量鉴别器的精度在 50%到 70%之间,其中 50%是非常理想的,因为在这一点上,鉴别器在区分向量方面的表现并不比随机猜测好,这意味着编码器能够成功地欺骗它。潜在向量鉴别器的精度看起来并不那么高,在下降到大约 60 之后,我们可以看到它再次上升,因此看起来鉴别器比编码器略胜一筹,但我们也可以看到它再次缓慢下降,因此随着更多的训练,它可能会收敛。不理想,但对我们的目的来说足够好了。

From left to Right: Loss of Discriminators for Prior and Category and the Encoder/Decoder, including validation loss, after 10.000 iterations, So far we cannot see any overfitting, but loss for the discriminator converges slowly.
尽管我们可以看到准确性和重建损失降低,但在训练完成后直观地确认结果仍然是一个好主意,并查看几个示例以了解自动编码器是否真正学会了以合理的方式再现输入,以及我们是否实现了生成实际分类向量的目标。
下面是编码器产生的输入和输出的几个例子,以及潜在和分类向量。在示例中,您可以看到重构是输入的强去噪版本,几乎就像编码器将捕获输入的主要频率成分,这似乎是在代码空间的约束下*似再现此类噪声数据的最佳方法。此外,我们还可以确认,我们成功地尝试了正则化编码器,以产生用于我们的聚类的分类向量;它们似乎有一个接* 1 的值,其余的值在 0 附*徘徊,更好的是,分类向量似乎对于大多数示例都不同,这意味着编码器确实为不同的输入分配了不同的类别。误差图主要是来自未通过的输入信号的噪声,尽管我们可以看到,当突发尖峰出现且未得到正确编码时,一个通道或通道组合的误差有时会特别高,因为这可能是一种不常见或异常的模式。

A few samples from the autoencoder model. From left to right: Input data, latent vector, categorical vector, reconstructed data, and error.
数据聚类
既然 autoencoder 已经被训练好了,我们就可以用它来为我们所有的数据生成类标签,我们终于可以看看我一直向您保证的这些聚类了。首先,让我们看看这些类是如何分布的。分配给分类向量的大小不一定对应于自动编码器将识别的分类数。我经常会上 6 到 7 节课,有时是全部 8 节,有时只有 4 节。在运行了几个实验之后,我可以报告,聚类的数量和聚类的属性高度依赖于自动编码器超参数和架构、小批量大小和所使用的优化器,并且还显著地依赖于与编码器相比的鉴别器的强度。它似乎更依赖于网络的超参数,而不是数据本身。

The Histogram of classes discovered by the encoder.
在这个例子中,编码器似乎使用了所有 8 个类别来对输入数据进行分类,并且被识别的类别似乎或多或少地均匀分布,尽管我们已经可以看到,在我们的数据集中,类别 0 有点过多,而类别 1 有点不足。现在,让我们看看当我们在 2D 平面上对输入数据执行 t-SNE 时,编码器发现的类是否实际上形成了某种聚类。下图显示了编码器分配的类别标签实际上形成了大部分一致的聚类,但是一些聚类看起来更加分散,并且具有远离其聚类中心的样本,这在一定程度上可能是所使用的嵌入算法的副作用。

t-SNE scatterplot of input data, labeled by categories. From left to right with Perplexity of 10, 30 and 50
这里有一个替代方案,在相同的网络设置下,使用了稍大的批量,并使用 Adam 代替 Nadam 作为优化器。它在训练中以非常相似的方式收敛,但结果却大不相同。编码器只将数据分为 6 类,而不是全部 8 类。下面是直方图,我们可以看到所有的类分布相当均匀。
我发现分类向量的鉴别器越强,编码器对数据进行分类的类别就越少,而鉴别器越松,分类就越分散。这两个结果本身都是有效且有趣的。
我将继续主要关注产生 8 个类的示例,因为我喜欢这些结果,但我想展示对超参数进行小的更改,结果会有怎样的不同。

The Histogram of classes discovered by the encoder with a slight variation to the hyperparameters.

t-SNE scatterplot of alternative clusters, labeled by categories. From left to right with Perplexity of 10, 30 and 50
所以,现在我们有了一些类别,它们形成了彩色的斑点,但是这些类别是什么意思呢?好问题!与许多无监督聚类算法一样,这些结果更像是一种启发,让你思考和困惑你的数据,以及该算法在这里可能发现了什么,以便你可以进一步研究它们。我只想快速浏览一下这些类的一些属性。如果我们在处理和标准化之前将类别映射回原始输入窗口,并查看一些统计属性,我们可能会发现类别之间有趣的差异。
每类值的分布
首先,我想看看每一类的值在该类的所有帧上的直方图,看看我们是否能够发现分布中一些有趣的差异。当我们为输入数据的最重要通道创建直方图时,就像我们计算的指标一样,我们可以看到分布、不同均值、标准差和偏斜的属性之间的一些差异。
取决于聚类的结果,有时在一些信号的分布中存在很大差异,而有时在其他信号中差异更大。在这个例子中,ATR 信号通常具有有趣的形状分布,其中许多看起来有点像高斯混合,并且似乎每一类都有不同的均值。MACD 信号线似乎是不同类别的强大驱动力,因为我们可以看到,大多数值位于一个狭窄的范围内,每个类别的均值完全不同,类似的情况也可以说是关于收盘价格回报的分布。RSI 信号的分布看起来非常像适当的高斯分布,模式和偏斜在类别之间也略有不同。

Histograms of some channels of the input data, each row representing one of the 8 classes.
平均信号形状
在本文的前面,当谈到预处理输入数据时,我提到了我们希望编码器找到并压缩的局部模式。考虑到上面的直方图,看看每一类是否真的出现了共同的模式会很有趣。所以基本上,我想看看每个类的单个通道的值是如何随时间发展的。
为此,我们只需计算一个类中所有信号在每个时间步长上每个通道的平均值。实际上,我希望得到的大多是没有明显方向的曲线,对于原始的高、低和收盘回报信号来说,大多数情况都是这样,尽管对于某些类别来说,这些信号似乎平均起来是某种“波动”运动。更有趣的是滚动统计的信号和我们添加到原始数据中的技术指标,我们可以看到每个类别的明显趋势。对于一些类,一些通道向相反的方向移动,而在另一些类中,它们一起移动。此外,我们可以看到,对于数据中发现的所有类别,MACD 信号线和收盘价的移动平均值似乎高度相关,并以某种方式一起移动,ATR 和收盘价收益的方差似乎也是如此,这是有意义的,因为在这两种情况下,这些统计数据和指标对相同的事情,波动性和趋势做出了略有不同的陈述。

一个错误胜过千言万语
不管你打算用你的自动编码器做什么,你总是可以免费得到一件东西,那就是异常检测。一旦编码器被成功训练,重建中的大多数误差应该是输入数据中的噪声的结果。如果观察每个通道的重构误差值的分布,可以看到分布以零为中心,大多数通道的标准偏差相当低,但我们可以看到某些部分也有一些极值。因为编码器在潜在空间的约束和限制值范围的强加分布的附加约束下,学会最好地重构出现在大多数样本中的模式。仅在少数样本中出现的模式可能没有适当的编码,因为根本没有用于它们的空间,这导致重建质量差。这种重构误差可以很好地检测这些不常见的事件,并将其归类为数据集中的异常值或异常值。
下面你可以看到每个通道的误差值直方图。我们可以看到,所有通道的误差都集中在 0 附*,但具有不同的标准差。如果您想要检测数据中的异常或识别异常事件,您可以为每个通道选择一个阈值,当重构误差的峰值超过该阈值时,就表示发生了异常事件。

Histograms of reconstruction error for each channel.
由于我们还尽了最大努力对我们的数据进行聚类,因此您可以在更细粒度的规模上这样做,并为每个类中的每个通道选择不同的阈值。当我们查看按类划分的每个通道的误差直方图时,我们可以看到误差的分布在类之间有轻微的差异。

结论
使用对立的自动编码器对数据进行聚类无疑是您可以采用的更奇特的方法之一,即使对于包括时间序列和表格数据在内的许多问题可能有更传统的算法,当然也有许多其他情况可能不是这样,例如在处理图像数据时。
通常对于传统算法,您必须试验某些参数,如邻域需要多少个点,它们应该有多远,或者您期望有多少个聚类,然后检查结果是否令人满意。使用这种方法,您必须对架构和超参数进行试验,找到合适的方法比使用更简单和优化的算法要耗时得多。
考虑到您在这里做一些深度学*,整个例程比常规的非工厂聚类算法花费更长的时间,消耗更多的计算能力,但这种方法肯定非常有趣。更有可能的是,您构建 autoencoder 是出于不同的原因,而不仅仅是为了聚类,但是现在您应该知道如何不仅可以正则化潜在的表示,还可以让您的 autoencoder 同时对您的数据进行聚类。
代码空间怎么了?
一开始,我谈了很多关于编码器产生的代码空间,以及我们需要如何正则化它们等等,但是在所有关于分类向量的争论中,我从来没有向你们展示过在所有这些训练之后代码空间实际上发生了什么。查看来自潜在空间的值的直方图,我们可以清楚地看到构造良好的高斯分布。看起来正规化运作得很好。看起来,即使歧视者并不十分满意,但它似乎变得太挑剔了。潜在空间的 t-SNE 看起来也有点像 2D 高斯分布,如果你将标签添加到代码空间,你可以看到这里的聚类不太一致。

The distribution of activation values of the latent space, after the training. It looks like the regularization worked just fine since we wanted a normal distribution.

t-SNE of latent vectors labeled with the discovered classes. Looks like a 2D Gaussian Distribution.
非常感谢您的阅读,下面是如约而至的 jupyter 笔记本。
以数据为中心的组织决策:DSI 案例研究
2018 年春末,我被选为 DSI (数据科学与信息学)的主席,这是佛罗里达大学的数据科学学生团体。我们教授研*班(Python、R、NLP、ML,你能想到的都有)并发展数据科学社区。我当选后不久,就发生了这样的事:
“如果我管理一个数据科学组织却不将数据科学应用于其中,我会是什么样的白痴呢?”
这篇文章的其余部分阐述了我们如何在整个 2018 年秋季,将组织从“我们只有很少的数据,我们确实拥有的数据无法使用”的状态带到“我们有一个有组织的有用的数据来源,并开始根据我们产生的见解采取行动。”在阅读数据科学相关帖子的这些年里,我经常感觉这种数据工程/收集/综合工作被低估了,所以我们开始吧!
数据来源
幸运的是,DSI 有为我们的研讨会创建签到表的历史,签到表上有参与者的详细信息,包括姓名、电子邮件、专业以及他们的编程经验。然而,我们在过去 3 年中保留的数据并没有考虑到分析,在数据清理过程之前,自动生成的 google sheets 看起来有点像这样。
有一年,DSI 以字符串形式跟踪参与者的班级(大一、大二等),另一年以整数形式(1、2 等)跟踪参与者在佛罗里达大学的年数,但无法区分一年级研究生和一年级本科生,甚至还有三分之一保持学术地位(本科生对研究生)。我们跟踪了电子邮件 5(!!!!)不同的方式:email、Email、e-mail、Contact(电子邮件)和 E-mail。这些差异显然是多年来随着执行委员会的更替和新人创建签到表而产生的,这是有道理的,并且来自良好的直觉!但是最终数据是部分不可用的,因为它不是分析优先。
这里的教训是:随着数据的增长,任何花费在数据意图上的时间都会以 10 倍的速度增长。 DSI 已经成为佛罗里达大学分析和教学的支柱,随着该组织的成熟,每年为一千多名学生提供服务,数据问题也随之增长。
标准化和自动化
标准化数据相当于问,在我们的例子中,我们想了解来我们工作室的人什么?要知道一个组织/公司关心什么,最简单的方法就是找出他们跟踪的内容。是用户增长吗?复读?人口特征?一旦您的组织走到一起并弄清楚了这一点,标准化就会自然而然地到来。
我们的解决方案?模板化表单和 R 包。该表单确保一次又一次地保存相同的数据,并且该包自动地抓取这些表并将数据拉在一起。新的执行委员会正在使用登录信息和数据库创建一个更好的解决方案,但 google sheets 和几个好的 R(或 Python)脚本应该可以在必要时使用。
最后,在这一点上,我们有了一个相对干净的数据集,其中包含了 DSI 多年来的历史,我们可以尝试使用这些数据来进行使命驱动的组织变革。在我看来,这是数据科学中最难的部分,因为你永远不知道你正在研究的东西是否有用。如果我们花了这么多时间,这么多努力,却一无所获呢?没有先验的方法知道数据的价值,只有后验的。这就是为什么讨论数据收集和清理如此重要,因为它占工作流程的 80%。DSI 没有业务端;我们教书和提供帮助是因为我们喜欢它,并发现它很有成就感,我们作为一个组织正在学*的这些课程,无论现在多么便宜,对工作中的年轻数据科学家来说都是无价的
探索性数据分析
回到分析:使用数据推进 DSI 的使命。这就引出了一个问题,DSI 的使命是什么?在 DSI 的最初几年,我们尽可能快地学*和教学。这种方式效果很好,因为在 DSI 的历史上,有大约 2500 名与会者。

这张图表是累计出席人数,但很明显,随着我们的内容和范围不断改善(由于反馈循环比佛罗里达大学几乎任何其他小组都短),学生们将希望学*编程技能。DSI 与会者的细分是预期的,多数是技术专业,但也有大量来自社会研究,工程(正规科学),商业等。

下一个可视化图是返回与会者的直方图,它真正打动了 DSI 执行委员会。很大一部分参加 DSI 的人只参加了一两次研讨会(总共 85%)。

这并不奇怪,因为没有足够的理由多次参加 Python 入门研讨会。作为一个在佛罗里达大学学*数据科学的地方,DSI 已经做了大量的工作,但还没有解决一个不同的问题:创建一个数据科学社区。这为用户保留创建了一个泄漏的用户桶,这不是我们的意图。
创建社区
创建一个社区是困难的,原因有几个,一个是只有好的度量标准的代理。拥有高回报率是一个社区所需要的全部证据吗?当然不是。对于社区来说,这似乎是一个必要但不充分的代理。
我们在 2018 年秋季采取了三项主要举措来尝试建立这个社区。首先,我们创建了 Data Gator ,这是用友与用友图书馆合作举办的第一次数据科学竞赛(如果你是用友学生,你应该参加!!).然后,每隔一周,我们想出了一个叫数据科学星期三的活动,我们的理论是:社区=数据+咖啡+食物+时间。我们会给学生提供食物和有趣的数据集,看看他们能想出什么。一个数据集专注于检测毒蘑菇,另一个数据集专注于玩堡垒之夜,其他数据集专注于自行车共享骑行,甚至还有一个数据集专注于口袋妖怪的统计数据,这产生了下图。

最后,在查看了我们研讨会的分类后,我们发现特定行业的研讨会首次参与者的比例更高(在我们的第一个自然语言处理研讨会中,我们有许多语言学博士学生,因为该部门没有教授 Python 课程)。然后,我们继续开发更多的利基研讨会,如数据科学的统计,一个精彩的 Tableau 研讨会,甚至一个精算师研讨会,以吸引校园的不同部分。
结果
在不到一个学期的时间里评估一个组织的变化是很困难的,而且可能是个坏主意。考虑到这一点,上学期出现了一些数字。【2018 年秋季是有史以来参加人数最多的 DSI 学期,参加两次以上研讨会的学生比例翻了一番。
我真的对这些结果感到兴奋,不仅因为这是一个在组织层面上以数据为中心的决策的潜在成功故事,而且因为在我的任期结束后,执行委员会必将把组织带向何方。
无论如何,这是一个更完整的故事,讲述了我们如何在一个学生组织中完成所有枯燥但最有效的数据工作,并通过查看一些漂亮的图表、明确定义我们想要的东西以及做出一些我们可以衡量的改变,看到了一些很好的初步结果。高管团队正在进行其他一些项目,包括为组织建立一个登录系统和适当的数据库,并尝试制作一个“我们应该订购多少披萨”模型来优化我们的预算。在用友没有比这更让我兴奋的群体了(在这里找到他们并在月底参加年度研讨会,请关注这个空间!作为一名推销员,如果你是一名招聘经理,并且已经做到了这一步,那么恭喜你!你的奖励是这条建议:早点雇用这些人,因为我相信很快就会有一场对他们所有人的竞购战。
特别感谢 Delaney Gomen,他在数据清理中发挥了重要作用,也是许多可视化工作的幕后推手。这篇文章的一些内容,以演示的形式,可以在这里找到,来自我 2018 年秋天在用友的一次演讲,部分代码可以在这里找到。
在我的网站上查看更多类似的分析工作,或者在 twitter 上关注我。
公益数据
利用数据促进社会和环境变化

首先,我将说明我所说的“公共利益的数据”是什么意思,因为这里有几个术语(例如#data4good ),没有一个能准确表达这个概念。这是一个正式的定义:
“公共利益数据:通过应用商业应用中常用的工具和技术,将数据用于公共利益”。
如果你对“公共利益”——一个在经济学中广泛使用的术语——感到好奇,这里有一个来自维基百科的定义:
“在经济学中,公益(……)是一种既不可排斥又不可竞争的商品,因为个人不能被排除在使用范围之外,也不能免费享用。”
公益数据一直都有,有什么新鲜的?随着最*大数据和数据科学的革命,在过去几年中,该领域发生了一些重大变化:
- 数据本身
- 方法和工具
- 政策应用
数据种类
公共部门组织大多是在特定的基础上收集和分析数据。只有指定的国家机构(如统计局)每年持续收集、主持和传播数据(如人口普查、家庭调查、农业普查等)。).通常,这些数据集的要素和观测值数量有限,并且通常是从大量人群中获取的小样本。
这些数据收集工作几乎完全是政府的努力。由于缺乏必要的资源和专业知识,非政府组织很少涉足这一领域。他们的商业决策通常依赖于准定量方法或政府报告。
最*情况发生了变化。正在改变商业领域的大数据、数据科学和机器学*领域也在冲击公共部门企业。现在,人们越来越意识到数据在决策过程中的力量。数据的性质也在发生变化——例如,现在可以在社会、经济和环境系统的许多方面获得免费的遥感数据集。然而,公共部门管理和分析这些大量数据的能力没有多大变化,主要是因为缺乏计算资源和人力。
方法和工具
公共部门正在发生的第二种转变是采用新的方法和技术来收集、存储、访问和分析数据。最*,政府机构收集的有价值的数据和信息没有得到充分利用。只有少数人,大部分是学术研究人员,能够获得这些数据并将其用于学术研究。

Photo by William Iven on Unsplash
应用的工具和技术也受到限制。数据分析师会做基本的分析(一些描述性统计、频率/计数表、柱状图、时间序列图或饼状图),并把它们放入年度报告中,然后就完事了!政策决定充其量是由数据告知的,而不是由数据驱动的。
公共服务中的应用案例
数据科学在公共服务中的应用目前非常广泛,并且每天都在扩大。这里只是许多例子中的几个:
犯罪管理:当地执法机构往往没有社区犯罪管理的必要资源。新颖的算法现在正被用于结合历史犯罪率、社会经济数据和其他数据源来预测犯罪热点。这些新工具正在帮助执法部门有效地分配有限的资源,以确保社区的公共安全。
农业:机器学*一直是农业领域的游戏规则改变者。通过自动化灌溉系统减少用水量、疾病检测、产量预测和杂草检测是改变农业的众多应用中的几个。
医学:斯坦福大学的生物医学数据科学计划总结了数据科学在医学领域的巨大潜力:
“从治愈单个患者到治愈整个人群,我们正在引领一个更健康的世界。在这个世界中,您的数据不仅会改变您的健康,还会改变世界各地人们的健康。在那里,医生可以立即搜索数百万份医疗记录,找到对像你这样的病人有效的方法。在那里,新药被快速开发出来,现有药物的新用途被发现。我们最终可以预测人类和人群中的疾病,并在疾病发生前预防它。通过利用大规模计算和数据分析的力量,我们将让这个世界成为现实。”
人道主义项目:从地震后重建海地,到在利比里亚抗击埃博拉病毒,数据科学工具已经帮助世界各地的弱势人群从危机中恢复过来。预警系统也正在接受测试,以主动应对人类面临的一些重大挑战。
社会项目:查看数据种类项目,找出数据科学可以帮助解决的广泛问题的巨大可能性。绘制贫困地图,预测人权侵犯,寻找失踪儿童,防止家庭火灾伤害,预测水需求…什么都不是!
环境管理:数据科学工具和技术正在为可持续环境管理带来巨大的潜力——从通过分析大型数据集获得洞察力,到预测下一场森林火灾将在哪里发生,到使用遥感和深度学*对土地使用系统进行分类。

野生动物监控:野生动物研究人员和管理人员正在野外安装自动相机,拍摄动物照片。使用机器学*和云计算技术,数百万张这些图像正在被处理和分析,用于分类学分类、测量多样性和监测野生动物运动。
全球发展:数据和技术不断改变我们对一些最大的全球问题以及如何解决这些问题的认识。衡量和监测可持续发展目标等全球议程的进展就是一个典型的例子。
结束注释
应对重大挑战需要付出巨大努力,但数据科学在公益领域的应用大多是由组织和个人资助的。但是有太多的问题等待解决。例如,联合国难民署的数据科学家概述了人道主义组织面临的 10 大数据科学挑战。这只是一个部门的 10 个大问题。社会各行各业还有很多人需要帮助。
使用 Pandas 处理数据:清理和处理
掌握熊猫处理“脏数据”

Keep calm, learn Pandas! (Source: Pixabay)
在为一些老的 Kaggle 项目实践时,我意识到在应用机器学*算法之前准备数据文件要花很多时间。这篇文章回顾了一些关于熊猫的初级到高级水平的数据处理技术,作为另一篇文章的前奏,在另一篇文章中,我使用了全球恐怖主义数据和熊猫的一些高级功能进行数据分析。这篇文章是关于数据清理和处理的。让我们立即开始吧!
在这篇文章中,我使用了 IMDB movie-dataset 来介绍最相关的数据清理和处理技术。我们可以从了解以下数据集开始
movies_df = pd.read_csv("movie_metadata.csv")
print "data-frame shape: ", movies_df.shape >>> data-frame shape: (5043, 28)
因此数据集有 5043 行,28 列,我们可以用
print "column names: ", movies_df.columns.values>>> column names:
['color' 'director_name' 'num_critic_for_reviews' 'duration'
'director_facebook_likes' 'actor_3_facebook_likes' 'actor_2_name'
'actor_1_facebook_likes' 'gross' 'genres' 'actor_1_name' 'movie_title'
'num_voted_users' 'cast_total_facebook_likes' 'actor_3_name'
'facenumber_in_poster' 'plot_keywords' 'movie_imdb_link'
'num_user_for_reviews' 'language' 'country' 'content_rating' 'budget'
'title_year' 'actor_2_facebook_likes' 'imdb_score' 'aspect_ratio'
'movie_facebook_likes']
在我们应用一些最大似然算法进行预测之前,比如说“imdb_score”,我们需要对数据集进行更多的调查,因为它不像波士顿大厦数据集那样得到很好的处理。首先,我将讨论如何处理丢失的数据。
处理缺失数据:DataFrame.isna(),DataFrame.fillna()
我们可以使用[pandas.DataFrame.isna](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.isna.html)()来检测数组类对象的缺失值。这将返回一个大小相同的布尔对象,其中 NA 值(如 None 或numpy.NaN)被映射为 True,而其他所有值都被映射为 False。这与[pandas.DataFrame.isnull(](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.isnull.html))如出一辙。
print "null values: \n",
print movies_df.isna()
上述命令返回以下输出

Looking For Missing Data in data-frame
我们可以通过在前面的命令中添加一个.sum()来提取相关信息,而不是打印出带有 True/False 条目的数据帧。这样我们就可以找到每一列缺失值的总数。
print movies_df.isna().sum()>>>color 19
director_name 104
num_critic_for_reviews 50
duration 15
director_facebook_likes 104
actor_3_facebook_likes 23
actor_2_name 13
actor_1_facebook_likes 7
gross 884
genres 0
actor_1_name 7
movie_title 0
num_voted_users 0
cast_total_facebook_likes 0
actor_3_name 23
facenumber_in_poster 13
plot_keywords 153
movie_imdb_link 0
num_user_for_reviews 21
language 12
country 5
content_rating 303
budget 492
title_year 108
actor_2_facebook_likes 13
imdb_score 0
aspect_ratio 329
movie_facebook_likes 0
dtype: int64
添加另一个。sum()返回数据集中空值的总数。
print "total null values: ", movies_df.isna().sum().sum()>> total null values: 2698
移除包含 NA 的行的最简单的方法之一是 删除 它们,当所有列都包含 NA 或任何列都包含 NA 时。让我们从删除任何列中包含 NA 值的行开始。
clean_movies_df = movies_df.dropna(how='any')print "new dataframe shape: ", clean_movies_df.shape
print "old dataframe shape: ">>> new dataframe shape: (3756, 28)
old dataframe shape: (5043, 28)
因此,删除任何列中包含 NA 值的行都会减少将* 1300 行。这对于行数较少的数据集非常重要,因为删除所有缺少值的行会导致我们丢失必要的信息。在这种情况下,我们可以使用指定的方法使用[pandas.DataFrame.fillna(](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.fillna.html))方法向 填充 NA/NaN 值 。用某个固定值(例如 0)填充所有 NA/nan 的最简单方法。我们可以简单地通过
movies_df.fillna(value=0, inplace = True)
我们可以选择一些特定的列,然后使用下面的DataFrame.fillna()方法,而不是用零填充所有缺失的值
movies_df[['gross', 'budget']] = movies_df[['gross', 'budget']].fillna(value=0)
对于“object”数据类型的列,例如“language”列,我们可以使用“no info”这样的词来填充缺少的条目。
movies_df['language'].fillna("no info", inplace=True)
填充缺失值的另一个method可以是**ffill**方法,它将上一个有效的观察传播到下一个。类似地**bfill**方法使用下一个观察来填补空白。
movies_df['language'].fillna(method='ffill', inplace=True)
另一个有效的方法是用 列的平均值来填充缺失值 如下
movies_df['budget'].fillna(movies_df[budget].mean(), inplace=True)
关于如何使用 Pandas 处理缺失值的更多细节,您可以查看 Pandas 用户指南中关于缺失数据的文档。
数据帧中的重复数据:DataFrame.duplicated()
除了丢失的数据,在一个数据帧中还可能有重复行。为了确定一个数据集是否包含重复的行,我们可以使用 Pandasdata frame . duplicated()来处理所有的列或一些选定的列。**pandas.Dataframe.duplicated()**返回表示重复行的布尔序列。让我们首先找出这个电影数据集中有多少重复行。
duplicate_rows_df = movies_df[movies_df.duplicated()]print "number of duplicate rows: ", duplicate_rows_df.shape>>>
number of duplicate rows: (45, 28)
因此,每列中有 45 行存在重复的元素。我们也可以对单个列进行检查—
duplicated_rows_df_imdb_link= movies_df[movies_df.duplicated(['movie_imdb_link'])]print duplicate_rows_df_imdb_link.shape>>>
(124, 28)
所以有 124 种情况下 imdb 链接是相同的,检查相同的另一种方法是使用[**pandas.Series.unique()**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.unique.html)方法。让我们看看:
print len(movies_df.movie_imdb_link.unique())>>>
4919
因此,唯一链接的总数是 4919,如果您注意到重复链接是 124,将它们相加得到(4919 + 124 = 5043)总行数。为了更好地分析,有必要选择唯一的行,这样至少我们可以删除所有列中具有相同值的行。我们可以简单地使用下面的[**pandas.DataFrame.drop_duplicates()**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop_duplicates.html)来完成
print "shape of dataframe after dropping duplicates", movies_df.drop_duplicates().shape >>>
shape of dataframe after dropping duplicates (4998, 28)
宁滨数据:pandas.cut()
另一个非常重要的数据处理技术是 数据分桶或数据宁滨 。我们将在这里看到一个例子,宁滨·IMDb 使用[**pandas.cut()**](https://pandas.pydata.org/pandas-docs/version/0.23.4/generated/pandas.cut.html)方法得分。基于分数[0。,4., 7., 10.],我想把电影放在不同的桶里['shyyyte ','适中','好']。如你所知,得分在 0-4 之间的电影将被放入“shyyyte”桶中,以此类推。我们可以用下面几行代码做到这一点
op_labels = ['shyttte', 'moderate', 'good']
category = [0.,4.,7.,10.]movies_df['imdb_labels'] = pd.cut(movies_df['imdb_score'], labels=op_labels, bins=category, include_lowest=False)
这里创建了一个包含标签的新列“imdb_labels ”,让我们来看看——
print movies_df[['movie_title', 'imdb_score', 'imdb_labels']][209:220]>>> movie_title imdb_score imdb_labels209 Rio 2 6.4 moderate
210 X-Men 2 7.5 good
211 Fast Five 7.3 good
212 Sherlock Holmes:.. 7.5 good
213 Clash of the... 5.8 moderate
214 Total Recall 7.5 good
215 The 13th Warrior 6.6 moderate
216 The Bourne Legacy 6.7 moderate
217 Batman & Robin 3.7 shyttte
218 How the Grinch.. 6.0 moderate
219 The Day After T.. 6.4 moderate
为了充分利用**pandas.cut()**方法,您可以查看文档。
检测数据集中的异常值:
大多数时候对于探索性数据分析(EDA), 离群点检测 是一个重要的环节,因为,特定特征的离群点可能会扭曲真实情况,所以我们需要忽略它们。具体来说,当我们想要应用机器学*算法进行预测时,离群值可能会造成严重破坏。同时异常值甚至可以帮助我们进行异常检测。因此,让我们看看如何使用熊猫来检测这个特定数据框架中的异常值。
海风盒子剧情:
箱线图是一种基于中位数、四分位数和异常值的可视化数据分布的标准方法。也许你已经知道这些量到底是什么,但我还是在下图中做了简短的回顾。

Figure 1: Schematic of Box Plot (Source: Author)
我们可以使用 python 数据可视化库 Seaborn 来绘制这样的箱线图。让我们用方框图来描绘电影海报中的演员数量分布。
sns.boxplot(x=movies_df['facenumber_in_poster'], color='lime')
plt.xlabel('No. of Actors Featured in Poster', fontsize=14)
plt.show()
上面的代码产生了下面的图

Figure 2: Too many outliers in number of faces featured in movie poster
让我们检查一下电影海报中演员(面孔)数量最多的电影。
print movies_df[['movie_title', 'facenumber_in_poster']].iloc[movies_df['facenumber_in_poster'].idxmax()]>>>
movie_title 500 Days of Summer
facenumber_in_poster 43
因此,电影'夏日 500 天'中出现了最多的面孔(43 张)。让我们用[**pandas.DataFrame.describe()**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.describe.html)的方法来看看这个专栏‘face number _ in _ poster’的一个基本统计细节。
print movies_df['facenumber_in_poster'].describe()>>>count 5030.000000
mean 1.371173
std 2.013576
min 0.000000
25% 0.000000
50% 1.000000
75% 2.000000
max 43.000000
有了这个,可能盒子的情节对你来说更有意义。
另一种检测异常值的方法是使用 Z 分数。让我们看看它是如何工作的。
Z 得分和离群值:

Figure 3: 1σ and 3σ Standard deviation on a normal distribution with 0 μ. (Source: Author)
Z 得分是一个数字(无量纲的),表示一个数据点相对于平均值的标准偏差。 Z 得分可以简单地定义为—
Z =(X-μ)/σ,其中μ为总体均值,σ为标准差,X 为总体中的一个元素。
为了绘制下图,我使用了正态分布[numpy.random.normal(](https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.normal.html)),在正态分布中,几乎所有的值(约 99.7%)都落在平均值的 3 σ偏差范围内(此处的图μ = 0)。我们可以使用 Z 得分来拒绝异常值的方法是考虑 Z 得分 3 个单位以内的数据点。可以使用下面的scipy.stats为所有包含“非对象”类型数据的列完成此操作。
1.检查数据框(data frame . dtypes)中所有列的数据类型。
print "data types: \n", movies_df.dtypes>>>
data types:
color object
director_name object
num_critic_for_reviews float64
duration float64
director_facebook_likes float64
actor_3_facebook_likes float64
actor_2_name object
actor_1_facebook_likes float64
gross float64
genres object
actor_1_name object
movie_title object
num_voted_users int64
cast_total_facebook_likes int64
actor_3_name object
facenumber_in_poster float64
plot_keywords object
movie_imdb_link object
num_user_for_reviews float64
language object
country object
content_rating object
budget float64
title_year float64
actor_2_facebook_likes float64
imdb_score float64
aspect_ratio float64
movie_facebook_likes int64
2.创建一个新的数据框,排除所有“对象”类型列data frame . select _ dtypes
print "shape before :", movies_df.shapemovies_df_num = movies_df.select_dtypes(exclude=['object'])print "shape after excluding object columns: ", movies_df_num.shape>>>shape before : (3756, 28)
shape after excluding object columns: (3756, 16)
3.从每列中选择位于 Z 得分 3 个单位内的元素
movies_df_Zscore = movies_df_num[(np.abs(stats.zscore(movies_df_num))<3).all(axis=1)]print "shape after rejecting outliers: ", movies_df_Zscore.shape>>>shape after rejecting outliers: (3113, 16)

Figure 4: Box plot of number of faces featured in a movie poster. After applying the Z score method.
我们可以通过再次绘制“facenumber_in_poster”的方框图来检查上述步骤的效果。这里我们可以看到与图 2 相比的不同之处,在图 2 中,我们有一个考虑了“facenumber_in_poster”列中所有元素的方框图。
这些是人们可以准备用于分析的数据和应用机器学*算法进行预测的一些方法。有效地准备数据集对全面分析有很大帮助,我希望这篇文章能帮助你更有条理地准备数据集以供进一步分析。根据问题和数据集的不同,你可能需要决定、选择和重复这些过程来解释什么是影响,所以,祝你好运探索你的数据集。
保持坚强,干杯!!
这篇文章使用的代码可以在我的 Github 上找到。
在Linkedin找我。
数据快乐
测量抽象

联合国可持续发展解决方案网络为他们的年度世界幸福报告积累了大量关于世界情感状态的数据。但是,谁在乎我们的感受呢?有的人只在乎事实;不是感情。那些资本主义猪可能有兴趣听听这些由咨询机构 happy Places to Work 的首席执行官西尔维娅·加西亚吹捧的事实,该机构向通过更快乐的员工寻求更高利润和增长的企业提供文化审计服务。

Hapine$$ is Profitable
量化一种情绪
许多研究都在努力衡量幸福,但如何衡量呢?情感是不可估量的,主观的,抽象的。幸福可以通过一个人的幸福状态、生活满意度,甚至是他们大脑中特定神经递质的水平来感知——T4、血清素和多巴胺。我读过的几乎所有报告都采用了最常见的方法,即总结自我报告的幸福度调查。谁比你自己更了解你的幸福状态?在 2017 年,盖洛普在全国范围内进行了 160,498 次电话采访,他们声称他们的数据反映了 95%的美国成年人口。
根据盖洛普的 2017 年美国福祉状况报告,幸福是可以衡量的(也是可以预测的?)具有 5 个属性:
目的:享受你的日常生活,并有动力去实现目标
社交:拥有支持和爱的关系
财务:管理预算以减轻压力
社区:感到安全、自豪,并喜欢你居住的地方
身体:健康、精力充沛
虽然这些措施可能是有效的,但从调查中收集的自我报告的信息可以预见是不可靠的。问题和答案都是主观的。Michael Argyle 等人(1999)解释说,从调查中获得的数据无法解释超过 15%的生活满意度变化。这意味着我们无法用调查数据预测个人的健康状况。更客观的衡量标准,如整个人口的健康状况,显示了预测未来福祉的能力。根据克拉克等人(2018 年)的说法,心理健康“比身体健康更能解释福祉的变化”,这一发现得到了《世界幸福报告》的证实。
尽管为了衡量幸福而对自我报告的调查提出了批评,Ortiz-Espina 和 Roser 的认为,“基于调查的幸福和生活满意度的测量确实提供了一个合理一致和可靠的主观幸福感的画面。”看看那篇文章。有一些豪华的可视化,和全球范围的数据。
Kahneman 和 Krueger (2006)的下表列出了研究人员发现的与自我报告的幸福和生活满意度有关的其他变量。

国家调查结果
我住在布鲁克林,所以这是从纽约的角度来看。查看完整的 报告 找到你所在州的排名。
盖洛普的结果将纽约市置于第二分位数的底部,总体排名第 75 位(满分 186 分)。纽约市的优势在于身体素质,在社会和社区方面做得相当好,但在财务和目标方面有困难。

More butta, more betta.
25 个最幸福社区中,有 17 个在五个州:加利福尼亚、科罗拉多、佛罗里达、北卡罗来纳和弗吉尼亚。
惊讶吗?
幸福感得分最低的社区是:史密斯堡、阿肯色州-俄克拉荷马州;俄亥俄州坎顿-马西隆;北卡罗莱纳州的山核桃树-雷诺尔-摩根顿;密西西比州格尔夫波特-比洛克西-帕斯卡古拉;密歇根州的弗林特。

并不奇怪。
你住在哪里不会导致你快乐或不快乐,但有一些不可否认的相关性。生活在人口高度密集的地区会让你在身体和财务方面的幸福感得分更高,但农村居民通常比城市居民的社区得分更高。
在盖洛普幸福评估的所有衡量指标中,佛罗里达州那不勒斯-伊莫卡利-马可岛排名前 2。该镇已团结起来,在学校和企业中推广更健康的饮食*惯,举办大型锻炼活动,城市健康计划已使医疗费用远低于全国报告的医疗通货膨胀率。他们发现了预防肥胖、阻止吸烟、减少孤独感、解决精神疾病和早期戒毒等先发制人的方法,这些方法比任何反应性治疗都更有效。

下图显示了墨西哥一段时间内的平均生活满意度。生活满意度的飙升可以归因于全国性的事件,如 2015 年与免费长途电话重合的幸福感急剧增加,以及 2017 年汽油价格飙升时生活满意度的下降。

用数据科学预测幸福
数据科学正在创新新的更客观的方法来衡量人类的情感和健康。新技术已经开发出通过测量与表情、步态和声音相关的面部特征来远距离检测身份和情绪状态的方法。一个名为 FaceReader 的新商业软件使用一种在超过 10,000 张脸上训练的人工神经网络算法来预测愤怒或快乐等情绪,准确率很高(这两种情绪的准确率超过 90%)。
情感分析也可以用来从书面文本中预测幸福感和情绪。对一篇文章中表达的观点进行计算机识别和分类,尤其是为了确定作者对特定主题或产品的态度是积极的、消极的还是中立的。情绪分析已被用于股票市场价格的预测建模,但也有可能被用于预测福祉。

我们的工作场所文化、我们居住的地方以及时事都对我们的整体幸福感有影响。但是许多人认为他们的快乐来自内心的控制。他们相信他们对自己的幸福负责。大卫·斯坦德尔·拉斯特在他的 TED 演讲中说,感恩是幸福的关键。“如果我们心存感激,我们就不会害怕。如果我们不恐惧,我们就不暴力。”他强调正念和总是活在当下是幸福的秘诀。丹尼尔·吉尔伯特说,“我们内心有我们一直在追逐的商品。”在他的演讲中,他比较了合成幸福和自然幸福,并揭示了两者之间没有太大的区别。他认为幸福可以自我制造。听听他的 TED 演讲,了解一些关于幸福和生活满意度的有趣事实。
不管你目前在世界幸福报告上的排名如何,我希望有一天你能发现自己带着微笑醒来,并让自己无条件地快乐。
Wednesday emerging from the Harmony Hut
m .阿盖尔、d .卡内曼、e .迪纳和 n .施瓦茨(1999 年)。快乐心理学的基础。拉塞尔·塞奇基金会。
克拉克、A. E .、弗雷什、莱亚德、r .和沃德(2018 年)。幸福的起源:生命过程中的幸福科学。普林斯顿大学出版社。
卡尼曼博士和克鲁格博士(2006 年)。主观幸福感测量的发展。《经济展望杂志》,第 20 卷第 1 期,第 3-24 页。
数据英雄:你对你的数据提出了正确的问题吗?
为什么问题比答案更重要
企业花费大量时间试图从数据中获得正确的答案。数字化转型和大规模数据收集的能力向他们承诺了可操作的见解,这将使他们更有利可图,更具竞争力,并且在流行词汇的土地上,有更多的未来证明。
如果你不知道问什么问题,你必须回到你正在解决的业务问题。您目前如何解决这个问题,是否需要任何数据?如果是,那么您需要确定数据驱动的方法是否可以增强您现有的解决方案。这可以通过机器学*模型来实现,但它们的价值取决于你对数据提出的问题。
问题比答案更重要,原因如下:
- 除非你理解或知道问题,否则答案是没有意义的。
- 只有当你想到问题时,你才能找到答案,因此,答案的价值与你的问题质量息息相关。
- 一个问题往往会引出更多的问题,如果你没有问第一个问题,你甚至不会想到这些问题。
出于这些原因,Mind Foundry 设计了其软件来帮助您解决问题,并帮助您从数据中找到更有意义的答案。然而,有时可能很难找到最初的问题,这就是为什么我们分享一些出发点。
为什么提问很难?这通常是因为我们不知道机器学*可以解决什么问题,或者可以实现什么。机器学*算法识别输入特征和目标变量之间的数据关系。在这个过程中,他们回答这个问题:我的数据中有关系吗?有结构吗?作为一个领域专家,你可能已经意识到或者有一些关系的直觉,但是你可能无法证明或者量化它们。机器学*可以有所帮助。
你也可以试着理解你为什么要收集这些数据。为什么对你有价值?你在找什么信息?机器学*将帮助你找到数据中丢失的信息。
回到你已经问过和回答过的问题,即使你没有使用数据,问问你自己是否真的没有可用的数据?或者你能找到一些数据来丰富你的答案吗?问你的同行他们在问什么问题,他们在用哪些数据?
想了解更多信息吗?你可以在这里找到的提问指南。
更新:我开了一家科技公司。你可以在这里找到更多
提高模型性能的数据插补
最终是小步骤产生巨大的影响
在执行任何机器学*项目时,数据插补是数据准备阶段的重要部分。用纯粹的统计学术语来说,它是一个用一些有意义的替代物代替缺失数据的过程。
例如,请看下面的样本数据,它是从 Kaggle 中选取的自动数据集的子集。

Source: https://www.kaggle.com/uciml/autompg-dataset
有 6 行缺少马力特性的数据。所以作为数据准备阶段的一部分,我们需要去掉缺失的数据。
有两种方法可以消除丢失的数据:
- 我们可以从实际数据集中删除所有 6 行。
- 为丢失的数据添加一些有意义的替代数据。
随便猜!!!哪种方法可以提高模型的准确性?保持你的好奇心,让我们来探索答案。
是时候让我们的手接触代码,并找出哪一个在模型准确性方面更好。
加载数据集:
auto_data = pd.read_csv('/kaggle/input/auto-mpg.data', delim_whitespace = True, header = None,
names = [
'mpg',
'cylinders',
'displacement',
'horsepower',
'weight',
'aceeleration',
'model',
'origin',
'car_name'
])
下面是数据集的样子:
auto_data.head()
Out[4]:

我们可以检查每一列的数据类型:
auto_data.info()

如您所见,马力列有数值,但数据类型为对象。所以稍微偏离一下主题,我们需要改变列的数据类型。
auto_data['horsepower'] = pd.to_numeric(auto_data['horsepower'], errors='coerce')auto_data.info()

此外,car_name 特性并没有增加多少价值,因为在 398 行数据集中有 305 个唯一值。因此,为了简单起见,我们将从数据集中删除特征 car_name。
auto_data = auto_data.drop(['car_name'], axis=1)
auto_data.head()

现在,我们都可以尝试这两种方法来处理缺失数据。
首先,我们将删除马力特性缺少值的行,并评估模型性能指标。
auto_data_final = auto_data.dropna(axis=0)
auto_data_final[auto_data_final.isnull().any(axis=1)]

执行上述步骤后,我们已经成功地从数据集中删除了所有丢失的值。
下一步是将数据集分成训练/测试集,并进行模型训练。
from sklearn.model_selection import train_test_split
from sklearn.svm import SVRX = auto_data_final.drop('mpg', axis=1)
y = auto_data_final['mpg']X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, test_size=0.2, random_state =0)model = SVR(kernel='linear', C=1.0)
model.fit(X_train, y_train)

这是捕捉模型训练的一些重要性能指标的好时机。
获得预测的均方误差(MSE)。
model.coef_

获得对测试数据的预测:
y_predict = model.predict(X_test)
计算均方误差:
from sklearn.metrics import mean_squared_errormodel_mse = mean_squared_error(y_predict, y_test)
print(model_mse)

将数字记在某处,我们将对处理缺失数据的第二种方法(即数据插补)重复相同的步骤。
为清晰起见,让我们总结一下第一种方法中包含的所有步骤:
a.已加载数据集。
b.已将马力特征转换为数字。
c.已删除汽车名称功能。
d.删除所有丢失数据的行。
e.将数据集分成训练/测试集。
f.使用训练集训练模型。
g.评估训练集和测试集上模型性能度量。
对于方法 2(数据插补),除步骤(d)外,我们需要重复上述所有步骤。步骤(d)将由几个子步骤代替。
假设前 3 个步骤很容易重做,那么我们可以直接从步骤(d)替换开始。
数据插补有多种方式,下面是其中一种。我们需要考虑到解释的方式并不完美,只是试图传达数据插补的重要性。
正如我们所知,完整数据集中只有 6 行缺少马力特性的数据。

Source: https://www.kaggle.com/uciml/autompg-dataset
我们可以寻找相关矩阵,并检查是否可以从现有特征中导出马力值。
# Check the correlation matrix to derive horsepower feature by help of other feature
corr = auto_data.corr()
corr.style.background_gradient(cmap='coolwarm').set_precision(4)

相关矩阵表明,马力特征与气缸、排量和重量等特征有很强的相关性。
为简单起见,我们将考虑气缸特性来推导马力特性下的所有缺失值。
丢失的数据行有 4 列或 6 列。因此,计划将实际数据集中所有行分别分组为 4/6 缸,然后得出马力特性任何缺失值。
auto_data_4_cylinders = auto_data[auto_data['cylinders'] ==4]
print(len(auto_data_4_cylinders))
auto_data_4_cylinders.head()

绘制直方图以了解功能马力的数据分布:
%matplotlib inlineauto_data_4_cylinders['horsepower'].plot.hist(bins=10, alpha=0.5)

由于直方图似乎是正态分布,我们可以选择平均值作为我们的插补策略。
import numpy as np
from sklearn.impute import SimpleImputerimp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')auto_data_4_cylinders['horsepower'] = imp_mean.fit_transform(auto_data_4_cylinders[['horsepower']])
检查是否已移除所有 NaN:
auto_data_4_cylinders[auto_data_4_cylinders.isnull().any(axis=1)].head()

对 6 缸自动数据下的缺失数据重复相同的技术。
auto_data_6_cylinders = auto_data[auto_data['cylinders']==6]
auto_data_6_cylinders.head()

绘制直方图以了解功能马力的数据分布:
%matplotlib inline
auto_data_6_cylinders['horsepower'].plot.hist(bins=10, alpha=0.5)

似乎如果我们从我们的数据集中排除 160,那么其余直方图将看起来像正态分布,我们可以选择平均值作为插补策略。
打印插补的目标行:
auto_data_6_cylinders[auto_data_6_cylinders.isnull().any(axis=1)].head()

现在将插补策略应用于目标行:
import numpy as np
from sklearn.impute import SimpleImputermean_imp = SimpleImputer(missing_values=np.nan, strategy='mean')mean_imp.fit(auto_data_6_cylinders[auto_data_6_cylinders['horsepower'] < 160][['horsepower']])auto_data_6_cylinders['horsepower'] = mean_imp.transform(auto_data_6_cylinders[['horsepower']])
检查是否已移除所有 NaN:
auto_data_6_cylinders[auto_data_6_cylinders.isnull().any(axis=1)]

由于我们删除了所有 NaN,所以现在是时候将所有数据集合并在一起了。
auto_data_others = auto_data[~auto_data['cylinders'].isin((4,6))]
print(len(auto_data_others))auto_data_final = pd.concat([auto_data_others, auto_data_4_cylinders, auto_data_6_cylinders], axis=0)
print(len(auto_data_final))

下一步是将数据集分成训练/测试集,并进行模型训练。
from sklearn.model_selection import train_test_splitX = auto_data_final.drop('mpg', axis=1)
y = auto_data_final['mpg']X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, test_size=0.2, random_state =0)from sklearn.svm import SVRmodel = SVR(kernel='linear', C=1.0)
model.fit(X_train, y_train)
现在,我们将使用插补策略准备的数据来捕获模型再训练的一些重要性能指标。
model.coef_

获得预测的均方误差(MSE)。
y_predict = model.predict(X_test)from sklearn.metrics import mean_squared_errormodel_mse = mean_squared_error(y_predict, y_test)
print(model_mse)

如我们所见,我们能够通过简单的数据插补技术最小化均方误差(MSE ),这是评估训练模型时的性能指标之一。
所以回到方法的问题,我们可以说数据插补方法在给定的数据集上效果更好。
你可以参考下面 kaggle 上的笔记本以获得完整的代码示例:
使用来自[私有数据集]的数据
www.kaggle.com](https://www.kaggle.com/shshankar1/svm-for-linear-regression-predict-automobile-mpg)





浙公网安备 33010602011771号