简而言之的 SQL:第 1 部分—基本的真实场景
本系列关注现实世界中最常见的数据科学和分析问题,旨在用 SQL 解决这些问题。
Photo by Carlos Muza on Unsplash
更新(到原帖):
- SQLFiddle 上的查询链接已被移除。未来的更新包括嵌入 SQL 的 Jupyter/IPython 笔记本的链接。
- 这里的大多数查询都是用 ANSI SQL 编写的。为了便于执行,PostgreSQL 9.6 和 MySQL 5.6 包含在一起,以展示窗口函数等特性(MySQL 不支持这些特性)。您可以在本地安装其中一个或两个来测试我们的查询。
- 本文已经扩展为一个由 3 部分组成的系列,第三部分涵盖了 SQL 中典型的、重要的任务。
如果您熟悉 SQL 的语法和编写基本的选择查询,您可能会发现本系列有助于快速复习或学习更多的技巧和诀窍,以编写更清晰和优化的查询,如如何计算六周的滚动平均销售额。
这是本系列的第一篇文章*,将回顾 SQL 概念以及一些基本问题,这些问题将通过基本的检索、连接、过滤、聚合和转换来回答。
第二篇文章将涵盖更复杂的场景,以及如何使用窗口函数、子查询、移动平均、常用表表达式和排序来处理这些场景。
第三篇文章将涵盖 SQL 中典型的、重要的任务——例如旋转、从每个组中检索顶级结果的不同方法、处理重复项以及性能查询调优。*
SQL 简介
SQL(结构化查询语言)是为管理数据库中的数据而设计的通用编程语言。由于它的标准语法,它支持所有的关系数据库以及一些 NoSQL(不仅仅是 SQL)。SQL 之所以受欢迎,是因为它的用法像英语一样易于理解,还因为它可以直接访问数据的存储位置,而不是将数据复制到其他应用程序,这使得分析相对方便。
借助 SQL,我们可以:
- 定义数据(DDL),例如,将缩写值标准化为完整的状态名
- 处理数据(DML ),例如用一个单词替换一个空白或空值
- 从数据库(DQL)中检索数据,例如获得本月的销售数字
在本系列的范围内,我们将只处理用于检索的查询( SELECT )。对于我们的大多数查询,我们还将使用 SQL:2011 版本的 ANSI SQL——一种在大多数数据库上兼容的标准。为了演示,我们将使用 MySQL 5.6 和/或 PostgreSQL 9.6。
注意:针对 SQL 发布的最新标准(ISO/IEC 或 ANSI)是 SQL:2016 ,对 JSON 功能有额外的支持。
创建数据库并插入数据
在开始查询数据之前,我们需要创建一个数据库并将数据加载到其中。
数据库 是一个有组织的数据集合,由表格等结构和其他元素组成。一个表在行(元组)和列(属性)中存储数据。模式指的是如何构建数据库(或划分成数据库表)的蓝图。
下面的命令集为我们的数据库构建了模式:
Build Schema (Create tables and Insert data)
在这里,我们创建两个表,分别是由PRODUCT _ ID(产品的主键,销售的外键)关联的产品、销售
检索数据
**SELECT**
命令用于从数据库中检索数据。
以下是一些获得销售数据的不同方法的基本查询:
- 所有细节来自一张表
- 将结果限制在前 N 行(用于测试或取样)
- 表中的特定列
- 列名的别名(使信息可读)
- 不同的列值(一列或多列中的唯一值)
Basic SELECT queries on Sales table
连接表格
连接表允许我们组合多个数据集,以获得更完整的结果集和全面的分析。
在数据建模(数据库设计)规范化是一个过程,用于将数据组织在单独的结构良好的表中,以最大限度地减少冗余,并避免插入、更新和删除异常。这种分离也有助于简化逻辑,提高精确装配特定用途所需物品的灵活性。这种装配是通过“连接”操作来完成的。
假设我们有一个单独的表 ProductSales 存储我们所有的数据:
Raw Data into ProductSales table
比如说,企业希望通过产品名称来了解产品,而不仅仅是品牌名称。这意味着他们可以要求我们将“诺基亚”更新为“诺基亚 Lumia”,将“三星”更新为“三星 S5”。在这种情况下,如果有一百万行,改变每一行 Product_Name 是“诺基亚”或“三星”的操作在计算上是相当昂贵的。
相反,如果我们以这样一种方式构建我们的数据,即在一个单独的表中取出产品数据,每种产品有一行信息,那么销售表中的每笔销售都可以与一个产品 ID 相关联。这叫做一对多关系。现在对 Product_Name 的任何更改都只是一行操作,所有与更新产品相关的销售数据都可以通过 Product_ID 上的join
轻松访问。
连接的用例:
- 为了组合相关的数据集,例如,为了找到已售出产品的名称,我们将把销售表和产品表连接起来
- 例如,为了执行比较——为了执行同比分析,我们可以将相似数据的分区按年份值进行组合
- 使用表格作为查找资源,例如,为了获得运输交付类型的状态,存储状态代码的枚举器以映射到相应的文本会更有效。
连接的类型
- 内部连接:内部连接是一个连接,它只返回两个表中那些连接条件为真的结果。
例如— 获取已销售产品的详细信息:
Inner Join
在本例中,从结果集中可以观察到,没有产品“LG”的销售,因为在 Sales 表中没有产品“LG”的列表(或者没有 Product_ID ) )。
连接条件通过一个**ON**
子句指定。我们还可以使用AND
操作符向它添加多个条件。例如,获取 2017 年销售的产品明细:
Inner Join with condition
2.外部联接:外部联接是返回一个或两个表的所有结果的联接(取决于联接类型——左、右或全),并且不满足联接条件的行返回空值。
为了更好地解释这一点,我们以前面的例子为例,稍微修改了一下需求— 获取所有产品及其相应的销售信息,如果有的话:
Left Join
在这里,我们有每一个产品清单以及每一笔销售。对于“LG”,结果集显示该产品存在于我们的数据库中,但是还没有销售。这是一个**LEFT JOIN**
的例子。
一个RIGHT JOIN
以类似的方式工作,但是相反,即返回右边表格的所有行,左边不匹配的行作为空值返回。尽可能使用左连接(右连接之上)是一种标准做法。
注意: FULL JOIN
在 MySQL 中不支持,但通过UNION
可以完成类似的查询。这篇文章的下一次更新将包括关于在 PostgreSQL 等其他数据库中使用的完全连接和自连接的主题。工会和工会所有将在下一篇帖子中涉及。
过滤和排序数据
通过过滤数据集,我们可以根据搜索条件限制我们的结果。
- 集中分析,例如,将销售趋势的结果缩小到特定区域
- *要限制集合条件的数据集,例如,*获取平均评分为 4 或更高的产品,其总评论数至少为 10
- 减少用于计算的数据集大小,例如——对一个假设进行采样
以下是过滤中常用的一些运算符:
过滤数据的基本方法是使用WHERE
子句以及一个或多个适合您的搜索条件的操作符。
以下是一些常用的运算符:
- 比较运算符 :
=
(等于)、<>
或!=
(不等于),以及其他一些不言自明的运算符—>
、<
、>=
、<=
- 逻辑运算符:、
AND
、OR
、NOT
、IN
(在一组值中查找)、BETWEEN
(具有开始和结束范围)、LIKE
(匹配相似的值)
整理
SQL 查询中的排序是可选的。默认情况下,如果主键存在,则结果按主键的顺序返回,否则按随机顺序返回。如果我们希望以特定的方式对结果进行排序,我们必须显式地添加一个ORDER BY
子句,后跟一个列名列表进行排序,最后是订单类型(ASC — 升序*,DESC —* 降序 ) 。
让我们从一个简单的例子开始— 获取一部“iPhone”的所有销售细节:
Filter Data
请注意,在第一个查询中,我们已经按 Product_ID(如果已知)进行了筛选。在第二个查询中,我们将 Sales 与 Products 表连接起来,并筛选出以“-Phone”结尾的产品。注意“%”是一个通配符,它可以搜索其位置上的任何文本。
再举一个例子— 获取 2015 年 11 月 1 日到 2017 年 10 月 1 日之间的销售明细,按日期排序:
Filter and Sort
这里,在第一个查询中,我们使用了一组比较运算符。在第二个查询中,我们使用BETWEEN
来搜索相同范围内的日期值。
现在,如果我们需要*获得产品的销售细节——“诺基亚”、“三星”和“LG”,该怎么办?*或者说这是我们需要搜索的大约 20 种产品的列表。与其包含 20 个AND
条件,不如使用IN
操作符来编写这个查询:
Filter with IN
聚集
当我们沉浸在周围数以百万计的细节中时,我们经常会失去上下文,并希望从中寻找有意义的信息。通过汇总数据,我们可以观察到每月总计或按类别划分的销售订单平均值等洞察。这有助于我们更深入地了解我们的数据,并允许我们提出更深入的问题。在高层次上,我们还可以识别数据中的模式和趋势。
为此,我们将使用 SQL 中内置函数的集合,这些函数汇总或汇总我们的值。一些基本的包括最小值、最大值、平均值、总和、计数,更具分析性的包括标准差、方差和等级。
让我们从使用一些基本功能开始,例如—
Basic Aggregation
这对初学者来说很好,但是除了告诉我们表包含多少行或者价格的最小/最大值是多少之外,它实际上没有多少业务洞察力。如果我们想知道每件产品的销售额是多少呢?小组来了,由来救援!
**GROUP BY**
允许您将数据分成组,这些组可以彼此独立地进行聚合。
例如— 获取我们数据库中每种产品的总销售额、平均价格和总销售量:
Aggregate by Product
现在这给出了一个更全面的图片!
注:使用COALESCE
、IFNULL
(或等效物)可以用指定值替换空值。
让我们考虑另一个示例— 对于从 2016 年 10 月 1 日开始销售的每件产品,获取每年的总销售额、平均价格和总数量,并只返回那些至少销售了 10 件商品的产品,按产品名称和年份排序。
现在可能有很多要处理,但下面是对其解决方案的分解分析:
Aggregation with multiple conditions
注意:HAVING
子句在执行聚合后应用过滤器。
聚合是 SQL 分析的核心部分,是任何优秀的数据科学家/工程师都必须掌握的。随着本系列的继续,我们将学习更多类似的复杂场景以及如何处理它们。
内置函数的数据转换
在收集的数据质量差、需要清理的情况下,通常需要进行数据转换;这种过程被称为数据争论(或蒙骗)。还需要转换来以更易于阅读的格式呈现数据报告。
以下是使用内置函数的季度和月度销售报告的一些示例:
Quarterly and Monthly Sales
第一个查询显示季度销售报告。这里,MONTH
和YEAR
函数将日期值转换成整数形式的相应月份和年份值,而CAST
用于价格以从整数转换成小数。CASE
函数(一个IF-ELSE
的变体)检查然后检查条件并将每个日期值映射到季度桶中,之后结果按季度分组。类似地,为了显示月度销售报告,我们需要对年和月进行分组,如第二个查询所示。
SQL 中有许多更方便的函数,您可以在您正在使用的 SQL 版本的文档中找到它们。更多的内容将在本系列的后续文章中讨论。
参考
在您等待下一篇文章的时候,请查看 SQL 上一些有趣的链接以获取更多知识:
[## 结构化查询语言- Wikibooks,开放世界的开放书籍
这本 Wikibook 提供了对 SQL、其起源、基本概念和组件的简短描述,以及大量示例…
en.wikibooks.org](https://en.wikibooks.org/wiki/Structured_Query_Language) [## 数据分析的 SQL 教程
本教程是为想用数据回答问题的人设计的。对许多人来说,SQL 是“肉和土豆…
community.modeanalytics.com](https://community.modeanalytics.com/sql/tutorial/introduction-to-sql/) [## 数据科学的 SQL 提示和技巧
获取 Ben Sullins 为数据科学专业人士(工程师、开发人员、数据挖掘人员、程序员和其他人员)提供的 12 个必备 SQL 技巧
bensullins.com](https://bensullins.com/sql-tips-tricks-data-science/)
希望这篇文章对你有帮助!如果你喜欢这篇文章,请为它鼓掌。有什么想法吗?把它们写在下面的评论里。
SQL:所有非技术人员都需要知道的一项技术技能
技术领域的商业、营销或战略人员指南。
如果你想从事技术工作,你会被大量的数据包围,无论是营销活动的打开率,应用内的用户活动,还是网络性能。根据经济学家的说法,数据已经成为新的石油,各公司都在加强他们的数据收集和分析团队,以优化他们的平台并创建“数据驱动的战略”。
在 Tumblr 全球增长战略品牌团队的实习期间,我成为了这一趋势的一部分。我需要了解我们的用户如何在国际上与平台互动,以便推动用户增长、保留和货币化。更具体地说,我需要一种方法来浏览、组织和理解每天来自全球 3 . 5 亿多个博客的数据,为此我需要 SQL。
简而言之,如果你想使用大量的数据,你需要知道如何使用 SQL 。
首先,我想说我不是 SQL 方面的专家。事实上,科技公司的大多数人不需要成为专家。重要的是,我们了解足够多的 SQL,能够提取、组织和利用我们的角色所需的数据,无论是作为业务分析师、营销经理还是产品经理。换句话说,我们需要知道“足够危险”。
什么是 SQL?
SQL ,表示结构化查询语言,是一种访问和操作数据库的语言。不要让这个定义吓倒您——使用 SQL 实现基本功能并不需要太复杂。可以把它看作是通过提供一系列标准来选择您想要访问的数据的一种方式。如果你想知道有多少用户在万圣节登录并发布了他们的服装照片,你的列表可能包括:
- 2016 年 10 月 31 日登录
- 张贴的图片
- 图片描述包含单词“服装”或“#服装”
然后,您将生成类似如下的查询:
一旦您理解了语法,您将开始看到提取数据是多么的逻辑和简单。然而,SQL 真正令人兴奋的地方是,当你开始将整个公司的数据拼凑在一起,以便形成新的见解。例如,您想了解您的电子邮件营销活动如何为 2017 年 Q2 世博会在墨西哥的日活跃使用量做出贡献。如果收集了正确的数据,您可以将整个组织的数据表拼接在一起,回答这个问题并优化墨西哥的电子邮件活动。
为什么这很重要?
学习 SQL 很重要有两个原因。首先,它允许您直接处理原始数据,而不是要求其他人向您提供有组织的数据集。这允许你在战略上更快地行动,独立地领导项目,并且成为你的团队的技术资产。
其次,SQL 增加了你的经济护城河——沃伦·巴菲特用这个术语来描述你相对于同行业其他人的竞争优势。SQL 是目前市场上最受欢迎的技能之一,它肯定会让你从你的同行中脱颖而出。
怎样才能学好 SQL?
有大量免费资源可以帮助您学习 SQL 的基础知识:
- 可汗学院——这是我曾经入门的一个。他们使用的慢节奏和沙盒系统对我们这些很少或没有编码经验的人特别有帮助。
- Code Academy — Code Academy 因提供高质量的课程计划和项目而在技术社区享有盛誉。虽然你可以从一些免费的模块开始,但最终你会看到每月 20 美元——这对他们提供的内容来说并不算太坏。
- W3 Schools —如果你有一些技术背景,我会推荐 W3 Schools 的命令和 SQL 特性 DIY 列表。他们用例子清楚地解释概念,甚至有一个 SQL 接口供你使用。
一旦你掌握了基础知识,请查看本指南中的 Google BigQuery ,这是一款工具,可以让你在任何你想要查询的数据集上运行类似 SQL 的查询。对于少量的处理能力来说,它非常简单而且免费。
这是所有的乡亲。
随时联系并在 Twitter 或 LinkedIn 上聊天!
数据科学家的 SQL 技巧—检查数据质量
所有的数据科学家都知道一些 SQL,但是除了将数据放入“真实”的分析环境中,它还可以有更多的用途。
在某种程度上,SQL 是数据科学中被遗忘的秘密——被认为是从它经常驻留的数据库中获取数据的必要但有点不酷的手段,没有熊猫或 tidyverse 的缓存。
从某些方面来说,这也很公平,因为大多数 SQL 实现中提供的函数范围往往无法满足数据准备人员的需求,他们需要将表连接在一起,并应用过滤器来减少要传输到执行分析的环境中的数据量——通常是在 R 或 Python 中。
关于 SQL 的书籍使问题变得更加复杂,因为很难找到比简单的 SELECT 语句和连接的核心范围更远的书籍,可能还增加了一些聚合函数。
然而,许多人经常使用 SQL,因为我们使用的数据存储在一个符合 SQL 的数据库中,如果我们想用它做些什么,我们必须需要编写一个查询,如果我们要编写一个查询,我们最好把它做好。
类似地,对于许多分析来说,第一步是将一堆数据移动到 R 或 Python 中,我们可能会这样做——至少,我们可能会尝试移动最小但最有用的数据表。因此,确定每一列的有用性,并突出可能导致该行或列从最终模型中排除的缺失值和极值的分析可能是有用的。
确定特定列中缺失值的比例似乎应该是现成的。它不是,但仍然可以用最少的麻烦来实现,尽管需要一些技巧。
SELECT CAST
(SUM (CASE WHEN column1 is NULL THEN 1 ELSE 0 END)
as float) / COUNT(*) AS ProportionMissing
FROM YourDB.YourTable
实际上,我们正在做的是通过 CASE 语句实现 Excel 的 SUMIF。显然,我们需要对 float 进行强制转换,因为 SUM 返回一个整数,如果我们忘记了强制转换,查询在几乎所有情况下都会返回 0。
这个查询的要点是,如果任何特定列中的缺失程度意味着该列是无用的,那么将该列移到您的建模环境中就是浪费时间。
另一个基本的数据质量检查是寻找极端情况。很明显,在 SQL 中有 MAX()和 MIN()函数,但是进一步研究多个极端变量也是有用的。检测极值的一种合理的常见方法是寻找相对于平均值有过多标准偏差的值——下面使用 4 个标准偏差作为基准。
WITH STATS (Col1_AVG,Col1_SD) AS
(SELECT STDEV(Col1),AVG(Col1)
FROM Db1.Tbl1)
SELECT Col1,DWT_AVG,DWT FROM STATS JOIN Tbl1
ON 1=1
WHERE ABS(Col1-Col1_AVG)/Col1_SD > 4
我们使用公共表表达式(以关键字‘WITH’开始的部分),因为我们要将聚合函数的结果与它们所基于的值进行比较。上面的版本返回了极值本身,因此可以研究它们的合理性(当数据是手动收集的时,极值来自转录错误并不罕见),但是反转大于号显然只是修剪了值。
罗伯特·德格拉夫最近的一篇关于媒体的文章是 【解释:最后一英里 。
他还是即将出版的《管理您的数据科学项目 》一书的作者
在推特上关注他:https://twitter.com/RobertdeGraaf2
SQL 教程:如何编写更好的查询
结构化查询语言(SQL)是数据科学行业中不可或缺的技能,一般来说,学习这项技能相当容易。然而,大多数人忘记了 SQL 不仅仅是编写查询,这只是前进道路上的第一步。确保查询是可执行的,或者它们适合您正在工作的环境,这完全是另外一回事。
这就是为什么本 SQL 教程将为您提供一些步骤,您可以通过这些步骤来评估您的查询:
- 首先,你将从学习 SQL 对于数据科学工作的重要性开始;
- 接下来,您将首先了解更多关于 SQL 查询处理和执行的知识,以便您能够正确理解编写定性查询的重要性:更具体地说,您将看到查询被解析、重写、优化并最终被评估;
- 记住这一点,你不仅会复习一些初学者在编写查询时会犯的查询反模式,还会学到更多关于那些可能错误的替代方案和解决方案;您还将了解更多关于基于集合的查询方法与基于过程的查询方法的对比。
- 您还将看到,这些反模式源于性能考虑,并且除了改进 SQL 查询的“手动”方法之外,您还可以通过使用一些其他工具来帮助您查看查询计划,以更结构化、更深入的方式来分析您的查询;而且,
- 在执行查询之前,您将简要地更深入地了解时间复杂性和大 O 符号,以了解执行计划的时间复杂性;最后,
- 您将简单地得到一些关于如何进一步调优您的查询的提示。
你对 SQL 课程感兴趣吗?参加 DataCamp 的数据科学 SQL 简介课程!
为什么要学数据科学的 SQL?
SQL 远未消亡:它是数据科学行业工作描述中最受欢迎的技能之一,无论你申请的是数据分析师、数据工程师、数据科学家还是其他任何角色。2016 年 O’Reilly Data Science 薪酬调查的 70%受访者证实了这一点,他们表示在自己的专业背景下使用 SQL。此外,在这项调查中,SQL 远远超过 R (57%)和 Python (54%)编程语言。
你明白了:当你想在数据科学行业找到一份工作时,SQL 是一项必备技能。
对于 20 世纪 70 年代早期开发的语言来说,这已经不错了,对吗?
但是为什么它被如此频繁地使用呢?为什么它已经存在了这么长时间却没有死?
有几个原因:第一个原因是,公司大多将数据存储在关系数据库管理系统(RDBMS)或关系数据流管理系统(RDSMS)中,您需要 SQL 来访问这些数据。SQL 是数据的通用语言:它让您能够与几乎任何数据库进行交互,甚至在本地构建自己的数据库!
似乎这还不够,请记住,有相当多的 SQL 实现在供应商之间是不兼容的,并且不一定遵循标准。因此,了解标准 SQL 是你在(数据科学)行业中找到出路的一项要求。
最重要的是,可以肯定地说,SQL 也已经被更新的技术所接受,例如 Hive,一个用于查询和管理大型数据集的类似 SQL 的查询语言接口,或者 Spark SQL,您可以使用它来执行 SQL 查询。同样,您在那里找到的 SQL 将不同于您可能已经学习过的标准,但是学习曲线将会容易得多。
如果你确实想做一个比较,就把它当作学习线性代数:通过把所有的努力投入到这个科目中,你知道你也能够用它来掌握机器学习!
简而言之,这就是为什么您应该学习这种查询语言:
- 这很容易学,即使对完全的新手来说也是如此。学习曲线非常简单且循序渐进,因此您很快就可以编写查询了。
- 它遵循“学一次,用在任何地方”的原则,所以这是一个伟大的投资你的时间!
- 这是对编程语言的极好补充;在某些情况下,编写查询甚至比编写代码更受欢迎,因为它更具性能!
- …
你还在等什么?😃
SQL 处理和查询执行
为了提高 SQL 查询的性能,您首先必须知道当您按快捷键运行查询时内部发生了什么。
首先,将查询解析成“解析树”;分析该查询以查看它是否满足语法和语义要求。解析器创建输入查询的内部表示。这个输出然后被传递给重写引擎。
然后,优化器的任务就是为给定的查询找到最佳的执行或查询计划。执行计划确切地定义了每个操作使用什么算法,以及如何协调操作的执行。
为了找到最佳的执行计划,优化器会列举所有可能的执行计划,确定每个计划的质量或成本,获取有关当前数据库状态的信息,然后选择最佳的执行计划作为最终的执行计划。因为查询优化器可能并不完美,所以数据库用户和管理员有时需要手动检查和调整优化器生成的计划,以获得更好的性能。
现在您可能想知道什么被认为是“好的查询计划”。
正如你已经读到的,计划成本的质量起着巨大的作用。更具体地说,评估计划所需的磁盘 I/o 数量、计划的 CPU 成本、数据库客户端可以观察到的总响应时间以及总执行时间等都是必不可少的。这就是时间复杂性概念的由来。稍后你会读到更多这方面的内容。
接下来,执行选择的查询计划,由系统的执行引擎进行评估,并返回查询结果。
从上一节中可能还不清楚的是,垃圾输入、垃圾输出(GIGO)原则在查询处理和执行中自然地显现出来:制定查询的人也掌握着 SQL 查询性能的关键。如果优化器得到一个错误的查询,它只能做同样多的事情…
这意味着当你写一个查询时,有些事情你可以做。正如您在简介中已经看到的,责任是双重的:不仅要编写符合特定标准的查询,还要收集查询中潜在性能问题的想法。
一个理想的起点是在您的查询中考虑问题可能潜入的“点”。一般来说,有四个条款和关键字,新手可能会遇到性能问题:
WHERE
条款;- 任何
INNER JOIN
或LEFT JOIN
关键字;而且, HAVING
条款;
诚然,这种方法简单而幼稚,但作为初学者,这些子句和语句是很好的指针,可以肯定地说,当你刚刚开始时,这些地方就是错误发生的地方,讽刺的是,它们也是很难发现的地方。
然而,您还应该认识到,性能需要一个上下文才有意义:当您考虑 SQL 性能时,简单地说这些子句和关键字不好是不可取的。在您的查询中有一个WHERE
或HAVING
子句并不一定意味着它是一个糟糕的查询…
请看下一节,了解更多关于反模式和构建查询的替代方法。这些提示和技巧是作为指南。实际上,如何以及是否需要重写查询取决于数据量、数据库以及执行查询的次数等。这完全取决于您的查询目标,并且对您想要查询的数据库有一些先验知识是至关重要的!
1.仅检索您需要的数据
当您编写 SQL 查询时,“数据越多越好”的思维定势并不是您必须遵循的:您不仅会因为获取了超过实际需要的数据而有模糊见解的风险,而且您的性能可能会因为查询提取了太多数据而受到影响。
这就是为什么寻找SELECT
语句、DISTINCT
子句和LIKE
操作符通常是个好主意。
当您编写查询时,首先可以检查的是SELECT
语句是否尽可能紧凑。你的目标应该是从SELECT
中删除不必要的列。通过这种方式,您可以强迫自己只提取符合查询目标的数据。
如果您有包含EXISTS
的相关子查询,您应该尝试在该子查询的SELECT
语句中使用一个常量,而不是选择一个实际列的值。当您只检查存在性时,这尤其方便。
记住相关子查询是使用外部查询中的值的子查询。请注意,尽管NULL
在这种情况下可以作为一个“常量”工作,但这非常令人困惑!
考虑下面的例子来理解使用常数的含义:
SELECT driverslicensenr, name
FROM Drivers
WHERE EXISTS (SELECT '1' FROM Fines
WHERE fines.driverslicensenr = drivers.driverslicensenr);
提示:知道拥有一个相关子查询并不总是一个好主意是很方便的。你总是可以考虑去掉它们,比如用一个INNER JOIN
重写它们:
SELECT driverslicensenr, name
FROM drivers
INNER JOIN fines ON fines.driverslicensenr = drivers.driverslicensenr;
SELECT DISTINCT
语句仅用于返回不同的值。DISTINCT
是一个如果可以的话,你绝对应该尽量避免的条款;与您在其他示例中看到的一样,只有在查询中添加这个子句,执行时间才会增加。因此,考虑一下你是否真的需要这个DISTINCT
操作来获得你想要完成的结果总是一个好主意。
当您在查询中使用LIKE
操作符时,如果模式以%
或_
开头,则不会使用索引。它将阻止数据库使用索引(如果存在的话)。当然,从另一个角度来看,您也可以认为这种类型的查询可能会检索太多不一定满足您的查询目标的记录。
同样,您对存储在数据库中的数据的了解可以帮助您制定一个模式,该模式将正确地过滤所有数据,只找到对您的查询真正重要的行。
2.限制你的结果
当你无法避免过滤掉你的SELECT
语句时,你可以考虑用其他方式限制你的结果。这就是诸如LIMIT
子句和数据类型转换等方法的用武之地。
您可以在查询中添加LIMIT
或TOP
子句来设置结果集的最大行数。以下是一些例子:
SELECT TOP 3 * FROM Drivers;
注意您可以进一步指定PERCENT
,例如,如果您通过SELECT TOP 50 PERCENT *
更改查询的第一行。
SELECT driverslicensenr, name FROM Drivers LIMIT 2;
此外,您还可以添加ROWNUM
子句,这相当于在查询中使用LIMIT
:
SELECT *
FROM Drivers
WHERE driverslicensenr = 123456 AND ROWNUM <= 3;
您应该总是尽可能使用最有效的,也就是最小的数据类型。当您提供一个较大的数据类型,而一个较小的数据类型就足够了时,总会有风险。
但是,当您将数据类型转换添加到查询中时,只会增加执行时间。
另一种方法是尽可能避免数据类型转换。这里还要注意,并不总是能够从查询中删除或省略数据类型转换,但是在包含它们时一定要小心,并且当您这样做时,要在运行查询之前测试添加的效果。
3.不要让查询变得不必要的复杂
数据类型转换把您带到了下一点:您不应该过度设计您的查询。尽量保持简单高效。这可能看起来太简单或太愚蠢,甚至不能作为提示,尤其是因为查询可能会变得复杂。
然而,您将在下一节提到的示例中看到,您可以很容易地将简单的查询变得比实际需要的更复杂。
当您在查询中使用OR
操作符时,很可能您没有使用索引。
记住索引是一种数据结构,可以提高数据库表中数据检索的速度,但这是有代价的:需要额外的写操作和额外的存储空间来维护索引数据结构。索引用于快速定位或查找数据,而不必在每次访问数据库表时搜索数据库中的每一行。可以通过使用数据库表中的一列或多列来创建索引。
如果您不利用数据库包含的索引,您的查询将不可避免地需要更长的时间来运行。这就是为什么最好在查询中寻找使用OR
操作符的替代方法;
考虑以下查询:
SELECT driverslicensenr, name
FROM Drivers
WHERE driverslicensenr = 123456 OR driverslicensenr = 678910 OR driverslicensenr = 345678;
您可以通过以下方式替换操作员:
SELECT driverslicensenr, name
FROM Drivers
WHERE driverslicensenr IN (123456, 678910, 345678);
- 两个
SELECT
语句带一个UNION
。
提示:这里,你需要小心不要不必要地使用UNION
操作,因为你要多次遍历同一个表。同时,您必须意识到,当您在查询中使用一个UNION
时,执行时间将会增加。UNION
操作的替代方法是:重新制定查询,将所有条件放在一个SELECT
指令中,或者使用OUTER JOIN
代替UNION
。
提示:这里也要记住,即使OR
——以及下面几节将要提到的其他操作符——可能不使用索引,索引查找也不总是首选!
当您的查询包含NOT
操作符时,很可能没有使用索引,就像使用OR
操作符一样。这将不可避免地降低您的查询速度。如果您不知道此处的含义,请考虑以下查询:
SELECT driverslicensenr, name FROM Drivers WHERE NOT (year > 1980);
这个查询的运行速度肯定会比您预期的要慢,这主要是因为它的表述比实际情况要复杂得多:在这种情况下,最好寻找一种替代方法。考虑用比较运算符代替NOT
,如>
、<>
或!>
;上面的例子可能真的会被改写成这样:
SELECT driverslicensenr, name FROM Drivers WHERE year <= 1980;
看起来已经整洁多了,不是吗?
AND
操作符是另一种不使用索引的操作符,如果以过于复杂和低效的方式使用,会降低查询速度,如下例所示:
SELECT driverslicensenr, name
FROM Drivers
WHERE year >= 1960 AND year <= 1980;
最好重写这个查询并使用BETWEEN
操作符:
SELECT driverslicensenr, name
FROM Drivers
WHERE year BETWEEN 1960 AND 1980;
另外,ALL
和ALL
操作符是一些你应该小心使用的操作符,因为将它们包含在你的查询中,索引将不会被使用。这里有用的替代方法是聚合函数,如MIN
或MAX
。
提示:在使用建议的替代方法的情况下,您应该意识到这样一个事实,即许多行上的所有聚合函数,如SUM
、AVG
、MIN
、MAX
,都可能导致长时间运行的查询。在这种情况下,您可以尽量减少要处理的行数,或者预先计算这些值。您再次看到,当您决定使用哪个查询时,了解您的环境、您的查询目标……是非常重要的!
此外,在计算或标量函数中使用列的情况下,不使用索引。一个可能的解决方案是简单地隔离特定的列,使其不再是计算或函数的一部分。考虑下面的例子:
SELECT driverslicensenr, name
FROM Drivers
WHERE year + 10 = 1980;
这看起来很时髦,是吧?相反,尝试重新考虑计算,并将查询重写为如下形式:
SELECT driverslicensenr, name
FROM Drivers
WHERE year = 1970;
4.没有暴力
最后一个技巧实际上意味着您不应该过多地限制查询,因为这会影响它的性能。对于连接和HAVING
子句来说尤其如此。
当连接两个表时,考虑连接中表的顺序是很重要的。如果您注意到一个表比另一个表大得多,您可能希望重写查询,以便将最大的表放在连接的最后。
- 连接上的冗余条件
当您向连接中添加太多条件时,您基本上是在迫使 SQL 选择某个路径。然而,这可能并不总是更有效的方法。
最初将HAVING
子句添加到 SQL 中是因为WHERE
关键字不能用于聚合函数。HAVING
通常与GROUP BY
子句一起使用,将返回的行组限制为仅满足特定条件的那些行。但是,如果在查询中使用这个子句,就不会使用索引,正如您已经知道的那样,这可能会导致查询的执行效果不太好。
如果你正在寻找一个替代方案,考虑使用WHERE
条款。考虑以下查询:
SELECT state, COUNT(*) FROM Drivers WHERE state IN ('GA', 'TX') GROUP BY state ORDER BY stateSELECT state, COUNT(*) FROM Drivers GROUP BY state HAVING state IN ('GA', 'TX') ORDER BY state
第一个查询使用WHERE
子句来限制需要求和的行数,而第二个查询对表中的所有行求和,然后使用HAVING
丢弃它计算的总和。在这些类型的情况下,带有WHERE
子句的替代选项显然是更好的,因为您不会浪费任何资源。
您会看到,这不是限制结果集,而是限制查询中的中间记录数。
请注意这两个子句的区别在于,WHERE
子句引入了单个行的条件,而HAVING
子句引入了聚合或选择结果的条件,其中单个结果,如MIN
、MAX
、SUM
、…是从多个行中产生的。
你看,当你考虑到它们需要尽可能高的性能时,评估质量、编写和重写查询并不是一件容易的工作;当您编写希望在专业环境中的数据库上运行的查询时,避免反模式和考虑替代方案也将是您的职责的一部分。
这个列表只是一些反模式和技巧的一个小概述,希望对初学者有所帮助;如果您想深入了解更多高级开发人员认为最常见的反模式是什么,请查看本讨论。
基于集合和过程的查询方法
上述反模式中隐含的事实是,它们实际上归结为基于集合的方法与构建查询的过程方法之间的差异。
查询的过程化方法是一种很像编程的方法:您告诉系统做什么以及如何做。
这方面的一个例子是连接中的冗余条件或滥用HAVING
子句的情况,就像上面的例子一样,其中通过执行一个函数然后调用另一个函数来查询数据库,或者使用包含循环、条件、用户定义函数(UDF)、游标等的逻辑来获得最终结果。在这种方法中,您经常会发现自己在请求数据的一个子集,然后从数据中请求另一个子集,以此类推。
毫不奇怪,这种方法通常被称为“逐步”或“逐行”查询。
另一种方法是基于集合的方法,您只需指定要做什么。您的角色包括为希望从查询中获得的结果集指定条件或要求。如何检索数据,由决定查询实现的内部机制决定:让数据库引擎决定执行查询的最佳算法或处理逻辑。
由于 SQL 是基于集合的,所以这种方法比过程方法更有效就不足为奇了,这也解释了为什么在某些情况下,SQL 比代码运行得更快。
提示基于集合的查询方法也是数据科学行业大多数顶级雇主要求你掌握的方法!您经常需要在这两种方法之间切换。
注意如果你发现自己有一个过程化的查询,你应该考虑重写或者重构它。
从查询到执行计划
知道反模式不是静态的,而是随着您作为 SQL 开发人员的成长而发展的,并且当您考虑替代方案时有许多要考虑的事实也意味着避免查询反模式和重写查询可能是一项相当困难的任务。任何帮助都可以派上用场,这就是为什么用一些工具优化查询的更结构化的方法可能是可行的方法。
还要注意上一节提到的一些反模式源于性能问题,比如AND
、OR
和NOT
操作符以及它们缺乏索引使用。思考性能不仅需要更结构化的方法,还需要更深入的方法。
无论如何,这种结构化和深入的方法将主要基于查询计划,正如您所记得的,查询计划是查询的结果,它首先被解析为“解析树”,并准确地定义了每个操作使用什么算法以及如何协调操作的执行。
正如您在简介中所读到的,您可能需要检查和调优由优化器手动生成的计划。在这种情况下,您需要通过查看查询计划来再次分析您的查询。
为了掌握这个计划,您需要使用数据库管理系统提供的工具。您可能拥有的一些工具如下:
- 有些包具有可以生成查询计划的图形表示的工具。看一下这个例子:
- 其他工具将能够为您提供查询计划的文本描述。一个例子是 Oracle 中的
EXPLAIN PLAN
语句,但是该指令的名称根据您使用的 RDBMS 而有所不同。在其他地方,你可能会发现EXPLAIN
(MySQL,PostgreSQL)或者EXPLAIN QUERY PLAN
(SQLite)。
请注意如果您正在使用 PostgreSQL,您会发现EXPLAIN
与EXPLAIN ANALYZE
的不同之处,前者只是得到一个描述,说明计划者打算如何执行查询而不运行它,而后者实际执行查询并返回预期与实际查询计划的对比分析。一般来说,实际的执行计划是您实际运行查询的计划,而估计的执行计划是在不执行查询的情况下计算出它将做什么。虽然在逻辑上是等价的,但是实际的执行计划更有用,因为它包含了关于执行查询时实际发生的事情的更多细节和统计信息。
在本节的剩余部分,您将了解更多关于EXPLAIN
和ANALYZE
的内容,以及如何使用这两个工具来了解更多关于您的查询计划和查询的可能性能的内容。
提示:如果你想更多地了解EXPLAIN
或者更详细地看例子,可以考虑读一下 Guillaume Lelarge 写的书“理解解释”。
时间复杂性&大 O
现在,您已经简要地检查了查询计划,您可以开始更深入地研究,并借助计算复杂性理论以更正式的术语来考虑性能。理论计算机科学中的一个领域,主要是根据计算问题的难度对其进行分类。这些计算问题可以是算法,也可以是查询。
然而,对于查询,您不一定要根据它们的难度来分类,而是根据运行它并获得一些结果所花费的时间来分类。这具体被称为时间复杂性,为了清楚地表达或度量这种类型的复杂性,您可以使用大 O 符号。
使用大 O 符号,您可以根据运行时相对于输入的增长速度来表示运行时,因为输入会变得任意大。大 O 符号排除了系数和低阶项,这样您就可以专注于查询运行时间的重要部分:增长率。当以这种方式表达时,去掉系数和低阶项,时间复杂度被认为是渐近描述的。这意味着输入大小趋于无穷大。
在数据库语言中,复杂性衡量的是随着数据表和数据库大小的增加,一个查询需要运行多长时间。
注意数据库的大小不仅会随着更多数据存储在表中而增加,而且数据库中存在索引这一事实也会影响数据库的大小。
正如您之前看到的,执行计划定义了每个操作使用的算法,这使得每个查询执行时间都可以在逻辑上表示为查询计划中涉及的表大小的函数,这被称为复杂性函数。换句话说,您可以使用大 O 符号和您的执行计划来估计查询复杂性和性能。
在下面的小节中,您将对四种类型的时间复杂性有一个大致的了解,并且您将看到一些示例,说明查询的时间复杂性如何根据您运行它的上下文而变化。
提示:索引是故事的一部分!
不过请注意,不同的数据库需要考虑不同类型的索引、不同的执行计划和不同的实现,因此下面列出的时间复杂度非常笼统,可以根据您的具体设置而有所不同。
在这里阅读更多。
总而言之,您还可以查看备忘单后面的来根据时间复杂度和查询的执行情况评估查询的性能:
SQL 优化
考虑到查询计划和时间复杂性,您可以考虑进一步调优 SQL 查询。你可以从特别注意以下几点开始:
- 用索引扫描替换不必要的大表全表扫描;
- 确保您应用了最佳的表连接顺序;
- 确保您正在以最佳方式使用索引;和
- 缓存小表全表扫描。
恭喜你。您已经完成了这篇博文的结尾,这篇博文只是让您对 SQL 查询性能有了一点了解。希望您对反模式、查询优化器以及用于检查、评估和解释查询计划复杂性的工具有了更多的了解。然而,还有更多的发现!如果你想了解更多,可以考虑读读 R. Ramakrishnan 和 J. Gehrke 写的《数据库管理系统》这本书。
最后,我不想隐瞒 StackOverflow 用户的这句话:
“我最喜欢的反模式不是测试您的查询。
这适用于以下情况:
-您的查询涉及多个表。
-您认为您有一个针对查询的最佳设计,但不想测试您的假设。
-您接受第一个有效的查询,不知道它是否接近优化。"
如果你想开始学习 SQL,可以考虑参加 DataCamp 的数据科学 SQL 入门课程!
最初发表于T5【www.datacamp.com】。
SQLAlchemy-Python 教程
我们经常遇到作为关系数据库的数据。为了使用它们,我们通常需要编写原始的 SQL 查询,将它们传递给数据库引擎,并将返回的结果作为普通的记录数组进行解析。
SQLAlchemy 提供了一种很好的与数据库交互的“Pythonic 式”方法。因此,您可以利用 SQLAlchemy 的 Pythonic 框架来简化您的工作流并更高效地查询数据,而不是处理传统 SQL(如 MySQL、PostgreSQL 或 Oracle)的特定方言之间的差异。
关于数据科学的其他故事可以在这里找到
安装软件包
pip install sqlalchemy
连接到数据库
为了开始与数据库交互,我们首先需要建立一个连接。
import sqlalchemy as db
engine = db.create_engine('dialect+driver://user:pass**@host**:port/db')
连接到各种数据库的一些例子可以在 这里 找到
查看表格详细信息
SQLAlchemy 可以用来通过反射从数据库中自动加载表。反射是读取数据库并基于该信息构建元数据的过程。
示例
询问
Table
和MetaData
已经被导入。元数据以metadata.
的形式提供
ResultProxy: 由 *.execute()*
方法返回的对象。可以通过多种方式使用它来获取查询返回的数据。
ResultSet: 在 ResultProxy 上使用诸如 *.fetchall()*
之类的获取方法时,查询中要求的实际数据。
处理大型 ResultSet
我们使用.fetchmany()
来加载最佳行数,并克服大数据集情况下的内存问题
while flag:
partial_results = ResultProxy.fetchmany(50)
if(partial_results == []):
flag = False
//
code
//
ResultProxy.close()
转换为数据帧
df = pd.DataFrame(ResultSet)
df.columns = ResultSet[0].keys()
过滤数据
让我们看一些原始 SQLite 查询和使用 SQLAlchemy 的查询的例子。
其中
**SQL :**
SELECT * FROM census
WHERE sex = F**SQLAlchemy :** db.select([census]).*where*(census.columns.sex == 'F')
中的
**SQL :**
SELECT state, sex
FROM census
WHERE state IN (Texas, New York)**SQLAlchemy :**
db.select([census.columns.state, census.columns.sex]).where(census.columns.state.*in_*(['Texas', 'New York']))
与,或,非
**SQL :**
SELECT * FROM census
WHERE state = 'California' AND NOT sex = 'M'**SQLAlchemy :**
db.select([census]).where(db.*and_*(census.columns.state == 'California', census.columns.sex != 'M'))
排序依据
**SQL :**
SELECT * FROM census
ORDER BY State DESC, pop2000**SQLAlchemy :**
db.select([census]).*order_by*(db.desc(census.columns.state), census.columns.pop2000)
功能
**SQL :**
SELECT SUM(pop2008)
FROM census**SQLAlchemy :**
db.select([db.*func.sum*(census.columns.pop2008)])
其他功能包括平均值、计数、最小值、最大值 …
分组依据
**SQL :**
SELECT SUM(pop2008) as pop2008, sex
FROM census**SQLAlchemy :**
db.select([db.func.sum(census.columns.pop2008).label('pop2008'), census.columns.sex]).*group_by*(census.columns.sex)
截然不同的
**SQL :**
SELECT DISTINCT state
FROM census**SQLAlchemy :**
db.select([census.columns.state.*distinct*()])
案例&投
case()
表达式接受匹配的条件列表和条件匹配时返回的列,如果没有匹配的条件,则接受一个else_
。
cast()
将表达式转换成特定类型的函数
举例
当结果只包含单个值时,我们对结果使用.scalar
加入
如果您有两个已经建立了关系的表,那么只需将每个表中需要的列添加到 select 语句中,就可以自动使用这种关系。
select([census.columns.pop2008, state_fact.columns.abbreviation])
例子
创建数据并将其插入表格
通过将不存在的数据库传递给引擎,sqlalchemy 会自动创建一个新的数据库。
更新数据库中的数据
db.update(table_name).values(attribute = new_value).where(condition)
删除表格
db.delete(table_name).where(condition)
放下一张桌子
table_name.drop(engine) #drops a single tablemetadata.drop_all(engine) #drops all the tables in the database
这个故事的 IPython 笔记本和其他资产可以在 这里 找到
再见😃
我的其他故事
SQS 消费者设计:在 Go 中管理并发性的同时实现高可伸缩性
Photo by ThisisEngineering RAEng on Unsplash
最近我不得不重新设计一个异步排队系统。在研究了几个选项之后,我选择了 AWS 简单队列服务(SQS) 和简单通知服务(SNS) 。通过这两个服务的组合,我能够复制和改进以前的(失败的)RabbitMQ 设置。AWS 产品的伟大之处在于它们是为微服务而从头开始构建的。它们抽象了复杂的异步消息传递系统的大部分复杂性,从通过消息等待期处理水平扩展到死信队列和许多其他特性,开箱即用。
根据 AWS 关于伸缩 **、**的建议,他们建议通过利用多线程在多个服务之间以及单个服务内进行水平伸缩。在这篇博文中,我们将重点讨论在一个服务中利用多线程使用 Golang 检索和处理来自 SQS 的消息的最佳方式。
在整个示例中,我们将处理一个包含 20,000 条消息的 AWS 队列。我们将测量 Golang 中管理并发工作者的三种不同技术,以及一个同步控制测试。我们将测量 3 分钟内消耗的项目总数,并取平均每分钟消耗的邮件数(mpm)。
所有示例都将使用 go aws-sdk 来长轮询 sqs 服务器。消息的成功接收将由一个基本处理程序处理,该处理程序在返回和完成请求之前向另一个微服务发出 http 请求。完成后,我们从队列中删除消息,这是 SQS 表达消息已被消费的方式。
控制
在这个例子中,我们将有一个控件。同步消费者
func (c *consumer) Consume() {
for {
output, err := receiveSQSMessages(c.QueueURL)
if err != nil {
//log error
continue
} for _, m := range output.Messages {
c.run(newMessage(m)) if err := h(m); err != nil {
//log error
continue
}
c.delete(m) //MESSAGE CONSUMED
}
}
}
对于同步消费者,我们得到的平均处理时间为 160mpm
基本并发
让我们添加一些 goroutines。虽然 goroutines 很便宜,但我们还是想在一定程度上控制它们。随着时间的推移,允许一百万个 goroutines 产生可能会导致我们的程序耗尽内存,运行速度比我们控制的要慢。在本例中,我们将使用一个简单的原子计数器来跟踪和添加允许的工作人员总数的限制。我们使用原子包,因为并发使用它是安全的。虽然简单,但不建议在基本应用中使用原子包,因为它们的工作方式往往与预期不同。
在下面的例子中,我们现在将允许 SQS 为每个请求批处理 10 条消息
func (c *consumer) Consume() {
c.workerPool = 50 // should be configurable
c.workerCount = 0
maxMessages := 10 for {
if atomic.LoadInt32(&c.workerCount) < c.workerPool {
output, err := receiveSQSMessages(c.QueueURL, maxMessages)
if err != nil {
continue
} for _, m := range output.Messages {
atomic.AddInt32(&c.workerCount, 1)
go c.run(newMessage(m))
}
}
}
}func (c *consumer) run(m *message) error {
defer atomic.AddInt32(&c.workerCount, -1) if err := h(m); err != nil {
return err
}
return c.delete(m) //MESSAGE CONSUMED
}
对于一个基本的并发消费者,我们得到的平均处理时间为2700 MPM。这是一个巨大的进步。
有趣的是,尽管我们允许一次运行 50 个 go routine,但是一次只能运行 2 个响应(总共 20 个 go routine)。这是因为处理消息的速度比长轮询从 SQS 检索消息的速度快。我们现在发现来自 SQS 的 http 请求是主要的瓶颈
工人池
工作池是管理应用程序中 goroutines 的惯用方法。它们在内存和速度方面都更高效,并在所有准备好的工作人员之间合理地分配工作负载。在大多数情况下,这将是最终的解决方案(提示:它不是)
func (c *consumer) Consume() {
maxMessages := 10
jobs := make(chan *message)
for w := 1; w <= c.workerPool; w++ {
go c.worker(w, jobs)
} for {
output, err := retrieveSQSMessages(c.QueueURL, maxMessages)
if err != nil {
//log error
continue
} for _, m := range output.Messages {
jobs <- newMessage(m)
}
}
}// worker is an always-on concurrent worker that will take tasks when they are added into the messages buffer
func (c *consumer) worker(id int, messages <-chan *message) {
for m := range messages {
if err := h(m); err != nil {
//log error
continue
} c.delete(m) //MESSAGE CONSUMED
}
}
首先,我们创建一个工作池池(本例中有 50 个)。这些工人正在寻找工作。消息通道上的范围是一个阻塞操作,因此工作者将处于休眠状态,直到有消息可用。如果您以不同的方式处理错误或消息处理程序,您可以在 For 循环中使用 select 以相同的方式处理各种选项。
此外,在使用者中发送消息也是一种阻塞操作。只有在有工作人员可用的情况下,它才会继续。这确保了如果没有可用的工作线程,使用者将不会继续检索消息。
这种方法的伟大之处在于,它在不同的工作人员之间实现了一种近乎循环的方式,并且在大多数情况下速度更快。
对于一个工人池消费者,我们得到的平均处理时间为 **2,766 mpm。**与前面的例子相比,这并不是一个巨大的改进。在这种情况下,虽然代码更加习惯和可靠,但我们仍然有相同的单点故障和瓶颈,因为对 AWS 的长轮询请求只是不够快。
最终解决方案
下一个合乎逻辑的步骤是通过将 http 请求本身合并到 worker 中来消除瓶颈。我们脱离了工人池的概念,因为我们不再需要一个调度程序(即一个通过通道向工人池发送作业或工作负载的系统)。我们只需要一组 goroutines,它可以无限期地查询 sqs 服务器上的消息并处理它们。
func (c *consumer) Consume() {
for w := 1; w <= c.workerPool; w++ {
go c.worker(w)
}
}func (c *consumer) worker(id int) {
for {
output, err := retrieveSQSMessages(c.QueueURL, maxMessages)
if err != nil {
continue
} var wg sync.WaitGroup
for _, message := range output.Messages {
wg.Add(1)
go func(m *message) {
defer wg.Done()
if err := h(m); err != nil {
//log error
continue
}
c.delete(m) //MESSAGE CONSUMED
}(newMessage(m))
wg.Wait()
}
}
}
在 6750 mpm 时,我们成功地将消息处理速度提高了 200%,比同步示例提高了 4000%。
在本例中,我们根据可配置的数量创建了一组工作线程,它们都负责自己的长轮询和消息处理。当收到一组消息时,waitgroup 用于同步一组额外的 goroutines,以便在再次查询之前管理消息负载
虽然这种解决方案偏离了工人池的优雅,但它消除了瓶颈和单点故障。最终结果是消费者的速度成倍提高,只需一个实例就可以完成 50 个实例。
该解决方案可以通过添加额外的工作线程来扩展 CPU 容量,也可以通过添加额外的消费者实例来无限扩展。如果成本是一个问题,可以在休眠期间缩减工作人员,并在有更多消息需要处理时自动增加工作人员
有别的解决方法吗?请留下评论
更新:我已经在这里开源了我用来创建这个解决方案的库https://github.com/qhenkart/gosqs
SquadAI:用于构建、管理和评估机器学习工作流的群体驱动平台
由于近年来非结构化数据的爆炸式增长,企业缩小准确数据的监管和提取可操作的见解之间的差距变得越来越具有挑战性。随着众包的出现,现在更容易收集和注释训练机器学习(ML)模型所需的前所未有的大量数据。建立一个高效的机器学习模型需要不断的迭代和修正。
ML 工程师构建和部署机器学习模型所遵循的典型流程包括:
- 训练、验证和测试数据集的管理,
- 数据集的清理和预处理,
- 特征选择和参数调整,
- 比较各种验收指标(准确性、敏感性、特异性、精确度等)和
- 将模型移至生产环境,以进行大规模预测。
当一个人需要为不同的业务问题训练、部署和管理数百个这样的模型时,就会出现令人畏惧的情况。
在 Squad ,我们正在构建 SquadAI,这是一个人在回路中的机器学习平台,用于管理 ML 模型的数据管理、培训、测试、评估和部署的整个过程。在这篇博文中,我们将讨论 SquadAI 的架构和各个模块,以及它对扩展业务流程和满足我们的客户和 Squad 机器学习团队的速度、准确性、灵活性和成本 SLA 的影响。
小队的诞生
在每一个行业中,为了达到规模,都需要执行某些流程和数字操作。例如,像阿里巴巴和亚马逊这样的大型电子商务市场维护着数百万种产品。这些产品经过质量控制操作,如产品分类和标签,内容审核和丰富。所有这些操作都需要特定的数据工作流来将庞大的非结构化数据转换为更有用的格式,以便于分析和业务洞察。由于深度、密集和多样化数据的爆炸式增长,企业采用不同的方法使准确和可用的数据可用于数据驱动的流程变得至关重要。
在 Squad,我们正在使用一个智能工作流构建器来颠覆业务流程外包行业,该构建器在单个平台上结合了众包和机器学习的力量。众包利用高质量分布式劳动力的经验来执行定制的微任务。在对从这种微任务获得的注释数据进行严格检查之后,它可以用于训练机器学习模型。
机器学习团队的挑战
为现实世界的微任务创建 ML 模型需要不断的迭代、评估,当然还有修正。为了建立有效的 ML 模型,机器学习团队为给定的任务收集数据,为精选的数据构建假设,预处理数据以提取特征,运行算法以学习模型,对照假设验证模型,并基于它们的评估来改进模型和假设。
一旦密集的实验完成,模型就被部署到生产环境中进行实时推理。团队通常必须执行几个这样的实验,以便为给定业务流程的自动化在各种微任务上建立 ML 模型的优化工作流。
存在各种挑战,包括:
- 无法追溯模型构建的整个过程和不同实验的见解,
- 无法在团队中重用和共享公共模型管道,
- 花费在维护培训、发布和管理新模型的基础设施上的大量时间,
- 存储和分析不同阶段模型性能的效率低下,导致重复昂贵的实验。
Figure 1. Optimized Workflow for Data Quality Control with Human-in-the-Loop Machine Learning for Our Trust and Safety Product
小队概述
我们介绍了正在进行的 SquadAI 工作,这是一个用于构建、管理和评估机器学习工作流的人在回路平台。我们讨论了小队的不同组成部分。
SquadAI 是一个平台,旨在大规模地简化跨 Squad 的数据管理和模型构建过程。我们的愿景是构建一个产品,只需点击几下鼠标,就可以轻松地将新构建的 ML 工作流集成到生产环境中并进行管理。图 1 展示了使用 SquadAI 进行数据质量控制的优化工作流程。它由不同级别的各种块组成,使用 SquadAI 固有的共享代码块和管道构建。“人在回路”模块用于获取不同现实世界任务的学习模型的注释数据,以及基于专家反馈对这些模型的持续改进。这个平台的三个主要组件是构建器、自动机和内核。
Figure 2, illustrates major components of SquadAI training architecture. The Builder, Automator and Kernel.
建造者
构建器用于配置我们的机器学习工作流。一个工作流是一个带有边的图,只有在满足特定条件时才会被激活。例如:如果其中一个级别的答案是 A ,则可能流向节点 Na 或者答案是 B ,则应流向节点 Nb 。就像一个图一样,一个工作流由许多节点 ( 级 )组成。这些级别具有不同的复杂性,类似于现实世界的任务。例如,验证假货、NSFW、服装和模糊检查的列表的 C2C 问题的典型工作流程可以包括四个级别,每个级别用于假货检测、NSFW、布料分类和模糊检测。
对于任何复杂的 C2C 问题,我们都有一个由许多级别组成的工作流,机器学习团队在这些特定的级别上工作,并试图通过建立相关的机器学习模型来实现自动化。为了实现这一点,图中有一个节点将数据和级别名称发送给内核、小队骨干。内核根据为该级别设计的工作流异步运行数据。 自动机 对该数据调用一系列集成模型,并将预测返回给内核。所有这些预测都通过一个网络挂钩发送回构建器。整个过程必须满足特定的 SLA,时间从几秒钟到几分钟不等。如果内核没有在定义的 SLA 内回传结果,数据流向**【HIL】模块进行判断。在满足 SLA 的情况下,针对特定级别超过 HIL 块,并且针对内容调节,将传入的样本【SKU】**路由到进一步的级别。图 3 示出了一个典型的工作流构建,使用构建器来检测输入的 SKU 是否模糊以及它是否包含宠物玩具。
Figure 3, a typical two level workflow build using Builder for detecting if incoming SKU is blur or not and contains a Pet toy. Data Points are sent to each level, and corresponding model workflow at each level is used for predicting the target label, if the prediction score is above a threshold it is routed to next level, if the socre is below thresold, the data unit is routed to Human-in-the-loop.
自动机
Automator 由 BaseNode 组成,原生支持 python 中各种机器学习算法的开发。它还支持外部框架和库,如 TensorFlow 、 Tesseract 等。BaseNode 可以灵活地重复使用代码,并为不同的微观任务构建模型,例如检测假货、NSFW、基于价格和描述的可疑列表、基于文本分类的类别校正或图像中的对象检测。然后,可以将多个模型集成在一起,以对工作流所代表的复杂任务进行预测。任何新的工作流都可以轻松配置,相应的信息存储在内核中,这使得我们的机器学习团队可以轻松地将任何工作流投入生产。下面是我们的BaseTrainingPipeline的一些方法的代码片段。
内核
内核用于存储自动机和构建器的每个阶段。SKU 是由构建器消耗的非结构化数据,然后被转换成单个单元 数据点 并存储在表中的单独行中。每当数据单元 DU 被加载时,一个 DU_workflow_prediction 请求被创建并存储在相应的表中。这些预测请求 id 随后由自动机一起缓冲。每次从自动机接收到一个预测,它被保存在不同的表DU _ Workflow _ Prediction中。我们还节省了每个模型和模型管道每个阶段所花费的时间,包括预测时间。集合模型系列的所有预测都存储在表DP _ ML _ model _ prediction中,用于评估和分析。我们将从生成器获得的数据缓冲一段时间,然后进行批处理,而不是对每个数据单元进行单个预测。我们使用 R edis 队列来缓冲数据的预测请求 id,在定义的时间限制(可配置)后,我们弹出队列,获取它们的预测,并将其返回给构建器*。我们使用类似于 芹菜 和 兔子 MQ 的工具用于异步基础设施。我们已经使用 Docker 设置了 RMQ 和芹菜工人。*
Figure 3, illustrates interaction of Automator with Kernel. Automator is used to configure machine learning model workflow at a particular level of our C2C Data Quality Control workflow.
未来
正在进行的 SquadAI 工作是一个单一的平台,利用群体的智慧提取、预处理非结构化数据并将其转化为可用的形式,这将使我们能够解决复杂的问题,如 C2C 市场的内容审核。由于该平台不依赖于任何特定的领域,它可以用于任何类型的数据,无论是文本、图像还是语音。我们使用通过我们的劳动力获得的带注释的数据来学习一系列机器学习模型,这些模型可以通过最少的代码更改轻松配置。内核为 SquadAI 的每个阶段建立了索引,以便于查询和评估。SquadAI 的不同模块有助于管理模型构建过程中涉及的不同阶段,从而提高了 Squad 的数据科学团队的工作效率。squad 的 ML 工程师现在可以轻松地配置新的实验,将它们与基线进行比较,并且只需点击几下鼠标就可以概括见解。
感谢 Vedvasu Sharma 令人印象深刻的数字和整个 ML 团队( Aniket Bhatnagar 、 Medha Katehar a、 Pragya Jaiswal 、 Priyank Jain 和 Ketan Bhatt )为 SquadAI 做出的贡献。
点击链接阅读更多关于进化“内核”的进化:小队的骨干。
Squad 的机器学习团队最近冒险为我们的主要资格产品 SquadVoice 构建了一个令人惊叹的质量评分框架。请继续关注我们如何利用 SquadAI 进行语音数据快速实验的更多更新。
压缩和激励网络
在 ImageNet 上建立一个新的艺术状态
挤压和激励网络( SENets )为 CNN 引入了一个构建模块,以几乎没有计算成本的方式改善了通道的相互依赖性。它们被用于今年的 ImageNet 比赛,并帮助将结果比去年提高了 25%。除了这种巨大的性能提升,它们可以很容易地添加到现有的架构。主要想法是这样的:
让我们为卷积块的每个通道添加参数,以便网络可以自适应地调整每个特征图的权重。
虽然听起来很简单,但就是这样。所以,让我们更仔细地看看为什么这工作得这么好,以及我们如何用五行简单的代码潜在地改进任何模型。
“为什么”
CNN 使用他们的卷积滤波器从图像中提取等级信息。下层可以发现边缘或高频等琐碎的上下文,而上层可以检测人脸、文本或其他复杂的几何形状。他们提取有效解决任务所需的任何东西。
所有这些都是通过融合图像的空间和通道信息来实现的。不同的过滤器将首先在每个输入通道中找到空间特征,然后在所有可用的输出通道中添加信息。我在另一篇文章中详细介绍了这个操作。
您现在需要了解的是,在创建输出要素地图时,网络会对其每个通道进行同等加权。SENets 通过添加内容感知机制来自适应地加权每个频道来改变这一点。在最基本的形式中,这可能意味着给每个通道添加一个参数,并给它一个线性标量,表示每个通道的相关程度。
然而,作者把它推进了一点。首先,他们通过将特征图压缩为单个数值来获得每个通道的全局理解。这导致大小为 n 的向量,其中 n 等于卷积信道的数量。之后通过一个两层的神经网络进行馈入,输出一个同样大小的向量。这些 n 值现在可以用作原始特征图上的权重,根据重要性缩放每个通道。
“如何”
在最后一段中,你可能已经失去了一点信心,这真的像我承诺的那样简单。因此,让我们直接开始实施 SE-block。
def se_block(in_block, ch, ratio=16):
x = GlobalAveragePooling2D()(in_block)
x = Dense(ch//ratio, activation='relu')(x)
x = Dense(ch, activation='sigmoid')(x)
return multiply()([in_block, x])
- 给该函数一个输入卷积块和它所拥有的当前通道数
- 我们使用平均池将每个通道压缩为一个数值
- 一个全连接层后接一个 ReLU 函数,增加了必要的非线性。它的输出通道复杂度也降低了一定的比例。
- 第二个完全连接的层之后是 Sigmoid 激活,为每个通道提供平滑的门控功能。
- 最后,我们根据侧网络的结果对卷积块的每个特征图进行加权。
这五个步骤几乎不会增加额外的计算成本(不到 1%),并且可以添加到任何模型中。
Vanilla ResNet Module vs the proposed SE-ResNet Module
作者表明,通过向 ResNet-50 添加 SE-blocks,您可以期望获得与 ResNet-101 几乎相同的精度。这对于一个只需要一半计算成本的模型来说是令人印象深刻的。本文进一步研究了其他体系结构,如 Inception、Inception-ResNet 和 ResNeXt。后者引导他们修改版本,在 ImageNet 上显示 3.79%的前 5 名错误。
How SENets improve existing architectures
SENets 最让我惊讶的是它们是如此简单而有效。能够以几乎零成本的方式将这种方法添加到任何模型中,应该会让您跳回绘图板,重新训练您曾经构建的所有东西。
这是我计划写的一系列论文摘要的第一篇。我想强迫自己阅读和理解新的论文,以跟上最近的 AI 趋势。如果你想投稿或者在我的文章中发现错误,请联系我。
SRGAN,TensorFlow 实现
(查找代码关注本帖此处。)
我们都在一部犯罪惊悚片中看到过这样的时刻,主人公让技术人员放大并增强图像,车牌变得可读,像素化的面孔变得清晰,破案所需的任何证据都被找到了。
我们都嘲笑过、笑过、小声嘀咕过丢失的信息是如何无法恢复的。
不再是了。嗯,算是吧。原来信息只是部分丢失了。类似地,作为人类,我们可能会根据我们对世界的了解来推断模糊图像的细节,现在我们可以成功地将相同的逻辑应用于图像,以恢复因分辨率效果而丢失的“照片级真实感”细节。
这是超分辨率的本质,通过对低到高分辨率图像转换的复杂理解,在亚像素尺度上解锁信息。
撇开 CSI 的陈词滥调不谈,超分辨率在现实生活中的应用数不胜数,而且利润丰厚。
缺乏细节的旧家庭照片可以被恢复和增强,以看到人们的脸,你手机上的相机现在可以像单反相机一样捕捉图像,一直到医学成像或自动驾驶汽车的传感器数据。
然后是商业方面,数据是新的石油。不管这种陈词滥调有多老套,可以肯定的是,高质量的数据是昂贵的,人们会为此付出高昂的代价。就数据科学项目而言,高质量的数据可能意味着燃煤和火箭燃料之间的差异。因此,有可能简单地“T6”增强“T7”公司已经拥有的形象集的想法?这是一个非常诱人的提议。
在过去的十年里,相机技术不断进步,我们现在希望我们看到的任何东西都有像素完美、丰富的图像。这听起来可能很有趣,但这种技术的早期采用者是用户策划的食谱网站,其图像可以追溯到十多年前。通过增强旧图像,他们希望保留旧食谱的价值。(此处)
将智能手机变成单反相机暗示了超分辨率方法的一个更微妙的细节,正在学习的是从一个空间到另一个空间的映射,在我们的情况下是从低分辨率到高分辨率。但是没有什么说这就是它所能做的,为什么不包括风格转换呢?将图像增强到高分辨率,同时调整曝光和对比度,增加一些深度,也许会让人们大开眼界?这些都是相同方法的例子。一篇涵盖更多“香草”式转会例子的精彩文章,请看这里。
在许多方面,最有趣的例子是传感器技术。大量的时间和金钱花费在开发用于医疗成像、安全和监控的传感器上,然后这些传感器通常被部署在具有挑战性的条件下,而没有预算来利用尖端硬件。
这在最近这篇关于 MRI 数据(这里是)或用于实验室显微镜检查(这里是)的 3D SRGANs 的论文中以多种方式达到高潮。将来,医院或实验室可以花必要的钱购买一台最先进的机器,或者购买几台较便宜的型号,雇用更多的员工,看更多的病人,但结果却是一样的?
不管应用程序如何,超分辨率将会一直存在,但事实是它已经存在很长时间了。
自从图像处理出现以来,各种方法就一直存在,(双三次,线性等)…)最近发展到一些非常有前途的神经网络方法来描述 LR 空间到 HR 空间之间复杂的多维转换矩阵。
然而,所有这些方法都遇到了最重要的绊脚石。它们都不能始终如一地产生人眼看起来自然的图像。
2017 年,Twitter 的一个小组取得了突破性进展(此处为),他们没有在神经网络中做任何与同行完全不同的架构,而是将注意力转向了谦逊的损失函数。
他们实现了一种叫做感知损失函数的东西,这种函数可以更好地调整网络,以产生人眼满意的图像。
部分是通过使用一个聪明的表示技巧,其中预先训练的最先进的 CNN 模型( VGG 来自牛津大学的小组)基于生成的图像与其高分辨率真相相比的特征映射来计算损失。
这些改进产生了惊人的结果。细节并不总是完美的,但与大多数尝试不同的是,细节是存在的,图像的整体感觉非常好。
From the SRGAN paper, the proposed image is almost identical to the original even with a four times downsampling factor. Look at the details around the eyes or the whiskers.
与纯生成相比,当有一个参考图像开始时,为什么生成真实的图像会更难,这不是很直观,所以为了探索这个想法,让我们回到我们的伪造者和专家,考虑这个新的范式对他们意味着什么。
他们没有出售任何珍贵的旧艺术品,而是展出了几件著名的艺术品,预计会以创纪录的价格售出。
我们的伪造者对艺术界知之甚少(喜欢把工作和家庭生活分开),也不知道这些画应该是什么样子。然而,就在他们坐下来画他们提交的作品之前,他们看到了传单上的一个小图像,上面有将要拍卖的画。太好了,现在他们有参考图像了!
唯一的问题是传单很小,而真正的画很大,他们知道专家会非常仔细地观察。他们必须弄清楚细节。
所以现在,画一幅伟大的作品不仅足够好,而且必须是那种特定的作品。尽管对细节一无所知,但伪造者并没有因此却步。
最初,伪造者决定最好的方法是改变细节,这样平均来说,在传单的像素大小上,伪造品与像素的颜色相匹配,并且作为一种现实主义的尝试,试图避免颜色的明显不连续,并确保笔触(像素)平滑地运行在一起。他们让一个朋友溜进拍卖行,对照真实图像逐一检查每一笔。
第一种方法类似于 MSE(均方误差,有艺术许可。)
这在最初对伪造者来说效果很好,但是遇到了一个绊脚石,专家不能很好地指出它,但是这些图像似乎有些不对劲。有一种与画布大小不匹配的模糊或缺乏清晰度,这些图像大多被视为赝品而被拒绝。通过匹配笔触,伪造者失去了整个图像的感觉,笔触的技术是完美的,但作为一个集合,笔触没有完全捕捉到风格,从图像到图像的概括是很难的工作。
因此,伪造者采取了不同的方法,而不是担心单独的笔触与图像匹配,他们希望这幅画类似于世界上的真实物体。于是他们请另一位朋友描述他们创作的画,这位朋友不是艺术家,所以不在乎技巧,但却一丝不苟地描述画中的物体。
然后,他们让同一位朋友去拍卖行,在伪造者试图复制的画作上做笔记。然后伪造者比较这些笔记,伪造品与真实图像的描述相符吗?这是 VGG 的感性损失。
因此,最终通过结合这两个朋友的反馈,伪造者了解到图像的细节,如此精致的细节,以至于他们可以仅仅从传单上的小图像和几个内部人士反馈的信息中制作出杰作的复制品,这样做让他们都赚了很多钱。
如果我们从技术角度考虑一下。在我之前的文章中,我们使用条件网络从 MNIST 数据集生成数字,即指定所生成图像的类别。这是一个约束,将产生的图像限制在学习分布的特定区域内。然而,通过选择一个特定的图像来填充,我们更明显地限制了生成器的自由。
我们现在需要一个长范围的连续性和细节,这样才能在大量信息丢失的情况下看起来令人信服。
突破来自感知损失函数的出现。这是上面伪造者使用的第二种方法。
这个令人惊讶的简单想法只是将内容损失(VGG)与适当加权的对抗性损失以 1000:1 的比例结合起来。这足以鼓励生成器在自然图像的 PDF 中找到解决方案,而不会过度限制网络进行复制而不是生成。
虽然再现正确的像素很重要,但通过 MSE 学习此制图表达缺乏上下文,使用 VGG 网络的想法是它对要素总体上有很好的“感觉”,并且像素之间的空间关系具有更大的权重,因此通过比较 VGG 网络每一层要素制图表达的潜在空间,高级别和低级别的要素都可以以现实的方式得到鼓励,并可以指导生成的图像的样式。
值得花一点时间来看看这些损失函数背后的数学原理,以理解其实现方式,但对于那些不感兴趣的人,可以直接跳到结果部分。
第一个等式显示了由鉴别器和发生器进行的标准最小/最大游戏。这是调节 gan 的标准方法,依赖于找到某种平衡,但相信鉴别器是发电机上的导向力。
The expectation values are minimised with respect to the generator parameters, and maximised w.r.t. the discriminator parameters, until an equilibrium is reached
感知损失在第二个等式中描述,这是本文的关键原创贡献,其中内容损失(本例中为 MSE 或 VGG)与试图欺骗鉴别器的标准发生器损失配对。
The Super Resolution loss is a sum of content loss (either MSE or VGG based) and the standard generator loss (to best fool the discriminator.)
在 MSE 损失(第三个等式)的情况下,它只是生成图像和目标图像的差和,当生成的图像接近目标时,这显然被最小化,但是使一般化变得困难,因为没有任何东西明确地鼓励上下文感知生成。
The MSE loss is summed over the width (W) and height (H) of the images, this is minimised by perfectly matching the pixels of the generated and original image
第四个等式显示了 SRGAN 论文中的突破,通过采用来自 VGG 网络的特征空间的差和而不是像素,特征被匹配。使得发生器比单纯的像素匹配更能产生看起来自然的图像。
The VGG loss is similar to MSE, but instead of summing over the image pixels, summing over the feature mapping of the image from the VGG network.
网络的结构在大多数方面类似于典型的 GAN,鉴频器网络只是一个标准的 CNN 二进制分类,末端有一个密集层,发生器的标准程度稍低,有去卷积层( conv2d_transpose )和增加的 skip 连接,以产生 4 倍的放大。
跳跃连接是网络循环块中的常规特征,本质上它意味着矩阵的状态在块开始时被保存,并在块结束时被添加到结果中。对于前五个块中的每一个,以及绕过整个前五个块的跳过连接,都会发生这种情况。最后一层的输出 deconv5 是期望的图像尺寸。请注意,由大约 15 个反卷积层组成,每一层都有一个批量规格化层(除了第一层是典型的),并且始终有 relu 激活。
VGG 网络也是 15 个卷积层深(具有三个密集层),但在其他方面是相当标准的,唯一的附加是通过三层提取不同阶段的矩阵状态,以馈入感知损失。
实施细节可以在这里找到。
可以看出,训练进行得很快,仅在几个批次后,逼真的图像开始出现,然而,图形的长尾表明,找到照片级的细节是一个缓慢的过程。这个网络显然没有产生最先进的结果,但对于训练时间(几个小时的 CPU)来说,结果是惊人的。下面的三张 gif 展示了图像被打磨和细节显现的过程。
Example 1. From initial haze a clear face starts to emerge quickly, then fluctuates in exact tone before settling on a stable solution. Impressive detail reconstruction can be seen in the glasses frame, largely invisible in the input image. (Although less well recovered off the face.)
Example 2. The detail here is subtle, lines around the eyes and shapes of features. Features like mouth shape are close, bags under the eyes come out appropriately, and the appearance becomes much sharper.
Example 3. Details like the dark makeup and clean smile come out fairly well, but finer grain details like teeth and eyebrows are less well recovered. This may reflect the training data, this face is more of a portrait and closer up than typical, and shows the limitations of this implementation shown here. Despite this the result is still a significant improvement.
稳定基线:OpenAI 基线的一个分支——强化学习变得简单
统一的结构(scikit-learn like 接口)和单一的代码样式以及文档
Image Credit: L.M. Tenkes
经过几周的努力,我们很高兴地宣布稳定基线的发布,这是一组基于 OpenAI 基线的具有公共接口的强化学习(RL)算法的实现。我们关注易用性和一致性。在本文中,我们将展示各种例子(基本用法、保存/加载代理、简单的多重处理、Atari 游戏培训等等)以及 fork 的起源。
更新(2020 年 5 月):稳定-基线 3 (PyTorch 版)现已上线!https://github.com/DLR-RM/stable-baselines3
更新:文档现已在http://stable-baselines.readthedocs.io/上线,并增加了 Tensorboard 支持
**更新:**我们增加了一个 rl 基线动物园,集合包含 70+训练有素的特工https://github.com/araffin/rl-baselines-zoo
更新:我们写了一个完整的教程:https://github.com/araffin/rl-tutorial-jnrr19
稳定基线 OpenAI 基线的一个分支,强化学习算法的实现
github.com](https://github.com/hill-a/stable-baselines)
TL;博士:
有了稳定的基线,培训 PPO 代理就像以下一样简单:
Basic Usage: training an agent with stable baselines
但是我们能不能简单点?当然,我们可以!有了稳定的基线,你现在可以用一行代码定义和训练一个强化学习代理:
Train a RL agent in one line of code!
可以使用 Colab 笔记本在线试一试 。
叉子的历史
当我们开始使用 OpenAI 基线进行研究时,我们很高兴地发现强化学习算法正在工作 (1)。然而,从我们试图稍微调整它的那一刻起,例如用学习到的功能而不是图像工作,它就变成了地狱,我们必须修补未注释的代码。
在我们经历的困难中,有缺少注释,缺少有意义的变量名和一致性(没有共同的代码风格)以及大量重复的代码(2)。
Example of issue found in the OpenAI Baselines repo
当我们在 Github 库上查看问题时,我们并不孤单: #285 、 #400 、 #413 、 #445 、 Reddit 。
因此,在基线存储库的主分支上的一个新提交破坏了我们的代码之后,我们决定创建一个 fork,并牢记两个想法:
- 注释代码和单一代码样式
- 每种算法的通用接口
(1)在我们的实验中,与其他代码库相比,基线代码库提供了最好的结果(就性能而言)。此外,在 重要的深度强化学习 ,中,他们显示基线 DDPG 实现优于其他代码库。最近玩 Dota 2 的 OpenAI 5 在核心使用 PPO。
(2)在撰写本文的时候,OpenAI 似乎在改进他们的基准上付出了一些努力,然而仍然缺少很多。
包括什么?
OpenAI 基线(以及稳定的基线)包括 A2C、PPO、TRPO、DQN、ACKTR、ACER 和 DDPG。你可以在自述文件中找到一个关于支持的内容(动作空间、多处理)的总结表。
基线还带有有用的包装器,例如用于预处理或多重处理。我们将在示例中展示它们的效用。
有什么新鲜事?
统一界面
所有算法都遵循相同的结构,我们希望有一个 scikit-learn like 接口,正如你将在示例中看到的,这使事情变得容易得多!
我们为所有算法提供了通用的方法,如 train (相当于 fit) 、save、load 和 predict (与 sk-learn 中相同)。
支持加载、保存等
我们为所有算法添加了保存和加载功能,传递回调的能力(用于实时绘图),全面的 tensorboard 支持和一个额外的输出动作概率的方法。
任何类型功能的培训
我们增加了对任意特征的 RL 训练的支持,也就是说 RL 算法可以在像素之外的其他东西上训练(当前的 OpenAI 基线只支持不使用图像作为输入时的连续动作)。
事实上,解耦 状态表示学习 ,从策略学习中提取特征是我们研究的主要课题,也是近期工作的重点(例如世界模型和好奇心驱动学习)。因此,我们认为这是我们 fork 的一个重要特性。
更多测试和覆盖范围
当我们开始重构 OpenAI 基线时,只有 16%的代码覆盖率。也就是说,只测试了所有代码语句的 16%。在重构过程中,我们添加了更多的测试,并且达到了 65% 的覆盖率!(大部分未被覆盖的代码来自 Mujoco 相关特性,而且由于这是一个商业物理引擎,很难与之有持续的集成)。
编辑:经过更多的重构(盖尔和她),覆盖率现在是 85%!
错误修复
我们利用注释代码来修复一些问题。例如,用于帧堆叠的包装器只能处理灰度图像(完整的故事是,OpenAI 之前的一个错误修复被他们的新提交 …)之一删除了)。
例子:“空谈是廉价的。给我看看代码”
在下一节中,我们将通过不同的例子来说明如何使用稳定的基线。我们将涵盖 基本用法 ,保存/加载,多重处理,绘图,雅达利游戏上的训练等等!
Colab 笔记本列表
基本用法:训练、保存、加载
在下面的例子中,我们将在月球着陆器环境中训练、保存和加载一个 A2C 模型。
关联 Colab 笔记本: 在线试用 !
Lunar Lander Environment
多重处理:释放矢量化环境的力量
关联 Colab 笔记本: 在线试用 !
CartPole Environment
使用 Tensorboard 监控训练
v 2 . 0 . 0 中的新功能
OpenAI 提供了基本的 tensorboard 支持(参见文档中的 传统集成 ),我们添加了完整的 tensorboard 集成(图形可视化、学习曲线等)。
要启用 tensorboard 日志记录,只需用有效路径填充tensorboard_log
参数:
Tensorboard integration
Cartpole does not seem so easy after all…
重现实验的代码:https://gist . github . com/araffin/ee 9 daee 110 af 3b 837 b 0 e 3 a 46 a6 bb 403 b
使用回拨:监控培训
您可以定义一个将在代理内部调用的自定义回调函数。当你想监控训练时,这可能是有用的,例如在 Tensorboard(或 Visdom)中显示实时学习曲线或保存最佳代理。
关联笔记本(含标图): 在线试用 !
雅达利游戏
关联笔记本: 在线试用 !
Pong Environment
多亏了make_atari_env
助手功能,在 Atari 游戏上训练一个 RL 代理变得很简单。它会为你做所有的预处理和多重处理。
Mujoco:规范化输入要素
规范化输入特征可能是成功训练 RL 代理的关键(默认情况下,图像被缩放,但其他类型的输入不被缩放),例如在 Mujoco 上训练时。为此,存在一个包装器,它将计算输入要素的运行平均值和标准差(它可以对奖励做同样的事情)。
注意:我们无法为此示例提供笔记本电脑,因为 Mujoco 是一个专有引擎,需要许可证。
复古索尼克
自定义策略网络
稳定基线为图像(CNN 策略)和其他类型的输入(MLP 策略)提供默认策略网络。但是,您也可以轻松地为策略网络定义自定义架构:
额外收获:持续学习
你也可以从一个学习环境转移到另一个环境进行持续学习 (PPO2 在DemonAttack-v0
上,然后转移到SpaceInvaders-v0
):
结论
我们提出了稳定的基线,这是一个旨在使强化学习面向广大受众的分叉。我们简化并统一了不同的算法,现在提供了一个类似 scikit-learn 的接口来进行 RL 实验。
对于任何对改进基线感兴趣的人来说,仍然有一些文档需要完成。因此,您可以随意在存储库上创建问题和提出请求。
当前 WIP:添加对 ACER/ACKTR 连续操作的支持
这篇文章是与阿什利·希尔共同撰写的。
感谢
稳定的基线是在 ENSTA ParisTech 的机器人实验室 U2IS ( INRIA 弗劳尔斯团队)创建的。
这项工作由梦想项目通过欧盟 Horizon 2020 FET 研究和创新计划根据第 640891 号拨款协议提供支持。
关于作者
我们都在研究机器人的强化学习。我们的研究重点是状态表示学习(RL 的特征提取)。在业余时间,我们喜欢做 DIY 项目的实验,例如建造一辆自主赛车。
Colab 笔记本列表
PS:如何制作一个训练有素的特工的 gif?
One line RL gif using terminalizer
稳定匹配,作为游戏
长期以来,我一直对选举和拍卖的数学很感兴趣。它们是经济学中更广阔领域的一部分,社会选择理论充满了有趣的组合问题和悖论。
最近,我(再次)偶然发现了稳定匹配的主题,这个主题显然也属于社会选择理论,它有一些相同的有趣方面。
在这篇文章中,我讨论了这个问题是否可以看作一个游戏。
问题的定义
我们有一组要配对的元素。每个元素/代理都有一个优先级列表。这种匹配必须以一种*稳定的方式来完成。*如果存在元素 A 和 B,则匹配不稳定,因此
- a 和 B 当前彼此不匹配
- a 更喜欢 B 而不是它当前的配对
- b 也更喜欢 A 而不是它当前的配对
这种情况意味着 A 和 B 可能会忽略其他元素的偏好,彼此跑掉。这种情况并不稳定。
这个问题有两种变体:
- 有两组大小相等的元素,S 和 T,并且配对必须由来自 S 的一个元素和来自 T 的一个元素组成。考虑到男性 S 和女性 T,这种变体也被称为稳定婚姻问题*。*
- 只有一个组具有偶数个元素。这叫稳定室友问题。
这两种变体有一些关键的要点,我在这里不详细介绍,只是总结一下:
(1)对于稳定的婚姻问题,总有一个稳定的匹配,并且有一个有效的算法来找到它:1962 年发明的 Gale-Shapley 算法。这个算法在这个 YouTube 视频中有介绍。令人惊讶的是,可以有多个稳定的匹配。Gale-Shapley 算法找到对“左集合”中的所有元素来说是最佳的匹配:左集合中的所有元素将在所有稳定的匹配中获得最佳可能的伙伴。这意味着(令我惊讶的是),从左边集合的角度来看,没有权衡。这个解同时对左边集合中的每个人都是最优的。对正确的人来说,情况正好相反:每个人都可能得到最坏的结果。注意:这并不意味着他们都得到了他们最不喜欢的选择——但是在所有可能的稳定匹配中,他们得到了他们最不喜欢的伙伴。
(2)对于稳定的室友问题,并不总是存在稳定的匹配!这个问题是 D.E. Knuth 提出的稳定婚姻变异的 11 个难题之一。他寻求多项式解,而第一个构建这种算法的是 R. W. Irving,他在论文中提出了“稳定室友”问题的有效算法 (1984)。如果存在稳定的匹配,该算法将找到一个稳定的匹配。它比 Gale-Shapley 更复杂,证明正确性也更复杂。该算法在几个 YouYube 的视频中都有讲解,比如【1】【2】。
这里有一个例子(视频)稳定的室友问题无解
No solution to stable roommate
例如,匹配的 AB CM 不稳定,因为 A 和 C 都更喜欢对方而不是他们当前的匹配,这使得我们切换到 AC BM,AC BM 也因为类似的原因不稳定,等等。(这种情况类似于社会选择理论中的阿罗悖论:多数人可以偏好 B 而非 A,C 而非 B,A 而非 C,一个群体的多数偏好可以形成一个循环)。
作为游戏的变现?
事实上,每个元素都有一个排序的偏好列表,这使得这个问题显然可以被视为一个多代理人的游戏,每个元素都是一个自主代理人,寻求最大化他的自私的游戏结果。
匹配稳定的标准让我产生了疑问:
稳定真的是目标本身吗,还是不稳定更像是一种症状?也就是说,如果一场比赛不稳定,这只是一个信号它不好,出了问题。相反,仅仅是稳定,就足以获得“最佳”解决方案吗?当有多个解决方案时,我们应该如何选择——以及当没有稳定的匹配时该怎么办。随机化是一种解决方案吗?如果是,如何解决?如果问题有随机的一面,我们是否应该用数字分数代替排名,这样我们就可以表达如何权衡不同的概率?(举例:用排名 A B C,“100% B”比“50% A,50% C”好还是差?)
关于一个可能的博弈出现了几个问题:
这个博弈的正式规则是什么,当一个博弈存在时,这个博弈的最优博弈总是导致稳定的匹配吗?这能导致帕累托最优吗?如果没有,是不是有些悖论?是回合制游戏吗,每个回合会发生什么?两个或两个以上的代理人可以合谋来改善他们的游戏结果吗?公布一个人的排名会在多大程度上损害你的结果(反过来,对你的排名撒谎会改善你的结果吗)?规则中需要随机化吗,或者我们可以让代理决定随机化吗?
纳什均衡的框架适用于这个博弈吗?纳什均衡是一个 T2 玩家改变他的策略,而其他人保持他们的策略,在这个游戏中,至少有两个玩家同意改变匹配。这个游戏中的策略是什么?排名榜是一种策略吗?可能吧,不过要看游戏是怎么进行的。
这让我很困惑,所以我尝试了不同的方法。
从这里开始,我只考虑稳定的室友问题,假设稳定的婚姻问题是这个问题的一个更简单的子问题。我并不完全清楚稳定的室友是不是一个合适的超集,因为对于稳定的婚姻来说,你只排了一半的元素。
方法 1。结果分布
不是玩家同意一个匹配,也许他们可以同意匹配的概率分布?
事实证明并非如此。在室友匹配的反例中,我们可以假设这种分布:
**Probability Paring**
x AB, CM
y AC, BM
1-x-y BC, AM
但事实证明,无论我们如何初始化 x 和 y,没有任何更新可以得到所有四个玩家的同意。这意味着( x , y ,1- x - y )的任何组合都是帕累托最优的。这不会让我们更进一步。
方法 2。基于回合的提议
游戏是这样进行的:在第 0 轮中,所有玩家同时公布他们的偏好列表。在每一回合,随机(统一)挑选一名玩家。然后,他按照自己的偏好列表向其他玩家求婚,第一个接受的玩家被选中。成对的玩家离开游戏,即被选中的伙伴没有机会求婚。游戏后期是没有办法改变自己的想法的。如果没有提议的玩家接受,提议的玩家将被移到休息组。提议玩家可以向休息组的成员提议。当只剩下休息组时,这些是随机匹配的。
让我们以上面 ABCM 的例子来玩这个游戏:
如果 A 被选为第一求婚者,他会向 B 求婚,B 会接受(为什么?因为如果 B 拒绝,他知道 C 会接受,然后 B 会和 M 结束)。通过对称性,B 或 c 会发生类似的匹配,如果 M 是第一个提议者,所有人都会拒绝,然后游戏照常进行。总的来说,我们会有这样的结果概率分布:
**Prob. Matching**
1/3 AB CM
1/3 AC BM
1/3 BC AM
问题
- 假设存在稳定匹配,假设每个人都最优博弈,结果会是稳定匹配吗?
- 在某些情况下发布错误的偏好会有帮助吗?似乎很明显,应该按照偏好的顺序提出建议,但是公布真实偏好的缺点是其他人会看到它们,并且他们的策略可能会依赖于它们。
- 玩家能在多大程度上成功合谋以提高结果?
我很乐意在评论中听到你的想法或主意。
用于预测的堆叠神经网络
机器学习和深度学习在金融机构中找到了自己的位置,因为它们能够高度准确地预测时间序列数据。有大量的研究正在进行中,以改进模型,使它们能够预测更高精度的数据。这篇文章是对我的项目 AIAlpha 的一个总结,它是一个堆叠神经网络架构,可以预测各种公司的股票价格。这个项目也是新加坡大学生黑客马拉松 iNTUtion 2018 的决赛选手之一。
工作流程
该项目的工作流程主要包括以下步骤:
- 获取股票价格数据
- 使用小波变换对数据去噪
- 使用堆叠自动编码器提取特征
- 列车 LSTM 使用功能
- 预测准确性的测试模型
在这篇文章中,我将详细介绍每一步的细节,以及为什么我选择做出某些决定。
数据采集
得益于雅虎财经的 API,股票价格数据很容易获得。因此,简单地用stock_data = pdr.get_data_yahoo(self.ticker, self.start, self.end)
就完成了。
去噪数据
由于股票市场动态的复杂性,股票价格数据经常充满噪声,这可能会分散机器学习对趋势和结构的学习。因此,在保留数据中的趋势和结构的同时,去除一些噪声是我们感兴趣的。起初,我想使用傅立叶变换(不熟悉的人应该阅读本文),但我认为小波变换可能是更好的选择,可以保留数据的时间因素,而不是产生仅仅基于频率的输出。
小波变换
小波变换与傅立叶变换非常相似,只是用于变换的函数不同,并且变换的方式也略有不同。
过程如下:使用小波变换对数据进行变换,然后(在所有系数中)移除超过全标准偏差的系数,并对新系数进行逆变换以获得去噪数据。
以下是小波变换如何对时间序列数据进行降噪的示例:
可以看到,初始信号中存在的随机噪声在去噪版本中并不存在。这正是我们希望用股票价格数据做的事情。
以下是如何对数据进行降噪的代码:
x = np.array(self.stock_data.iloc[i: i + 11, j])
(ca, cd) = pywt.dwt(x, "haar")
cat = pywt.threshold(ca, np.std(ca), mode="soft")
cdt = pywt.threshold(cd, np.std(cd), mode="soft")
tx = pywt.idwt(cat, cdt, "haar")
库pywt
是优秀的小波变换工具,极大地减轻了我的负担。
提取特征
在通常的机器学习环境中,提取特征将需要专业领域知识。这是我没有的奢侈品。我也许可以尝试使用某种形式的技术指标,如移动平均线或移动平均线收敛发散(MACD),或动量指标,但我觉得盲目使用它可能不是最佳选择。
然而,自动特征提取可以通过使用堆叠自动编码器或其他机器学习算法(如受限玻尔兹曼机器)来实现。我选择使用堆叠式自动编码器,因为与受限玻尔兹曼机器的概率相比,编码具有可解释性。
堆叠自动编码器
本质上,堆叠式自动编码器非常擅长压缩数据并再次将其复制回来。我们感兴趣的是压缩部分,因为这意味着再现数据所需的信息都以某种方式编码为压缩形式。这表明,这些压缩数据在某种程度上可以是我们试图从中提取特征的数据的特征。以下是堆叠式自动编码器的网络结构:
输入数据被压缩成任意数量的神经元,网络被迫使用自动编码器重建初始数据。这迫使模型提取数据的关键元素,我们可以将其解释为特征。需要注意的一个关键点是,该模型实际上属于无监督学习,因为没有输入输出对,但输入和输出是相同的。
我们可以使用keras
来构建这样一个模型,使用函数式 API 比顺序式 API 更有用。
class AutoEncoder:
def __init__(self, encoding_dim):
self.encoding_dim = encoding_dim
def build_train_model(self, input_shape, encoded1_shape, encoded2_shape, decoded1_shape, decoded2_shape):
input_data = Input(shape=(1, input_shape))
encoded1 = Dense(encoded1_shape, activation="relu", activity_regularizer=regularizers.l2(0))(input_data)
encoded2 = Dense(encoded2_shape, activation="relu", activity_regularizer=regularizers.l2(0))(encoded1)
encoded3 = Dense(self.encoding_dim, activation="relu", activity_regularizer=regularizers.l2(0))(encoded2)
decoded1 = Dense(decoded1_shape, activation="relu", activity_regularizer=regularizers.l2(0))(encoded3)
decoded2 = Dense(decoded2_shape, activation="relu", activity_regularizer=regularizers.l2(0))(decoded1)
decoded = Dense(input_shape, activation="sigmoid", activity_regularizer=regularizers.l2(0))(decoded2)
autoencoder = Model(inputs=input_data, outputs=decoded)
encoder = Model(input_data, encoded3)
# Now train the model using data we already preprocessed
autoencoder.compile(loss="mean_squared_error", optimizer="adam")
train = pd.read_csv("preprocessing/rbm_train.csv", index_col=0)
ntrain = np.array(train)
train_data = np.reshape(ntrain, (len(ntrain), 1, input_shape))
# print(train_data)
# autoencoder.summary()
autoencoder.fit(train_data, train_data, epochs=1000)
我用 2000 年到 2008 年去噪后的股价数据来训练自动编码器。经过 1000 个周期的训练后,RMSE 下降到 0.9 左右。然后,我使用该模型将我剩余的股票价格数据编码成特征。
LSTM 模型
LSTM 模型无需介绍,因为它在预测时间序列方面已经变得非常广泛和流行。它从细胞状态的存在中获得其特殊的预测能力,这允许它理解和学习数据中的长期趋势。这对我们的股价数据尤其重要。下面我将讨论我认为重要的设计选择的一些方面。
【计算机】优化程序
所使用的优化器类型会极大地影响算法收敛到最小值的速度。此外,重要的是有一些随机性的概念,以避免陷入局部最小值,而不是达到全局最小值。有几个伟大的算法,但我已经选择使用亚当优化。Adam 优化器结合了其他两个优化器的优势:ADAgrad 和 RMSprop。
ADAgrad 优化器本质上对每个参数和每个时间步使用不同的学习率。ADAgrad 背后的推理是,不频繁的参数必须具有较大的学习率,而频繁的参数必须具有较小的学习率。换句话说,ADAgrad 的随机梯度下降更新变为
在哪里
学习率是基于已经为每个参数计算的过去梯度来计算的。因此,
其中 G 是过去梯度的平方和的矩阵。这种优化的问题在于,随着迭代次数的增加,学习率会很快消失。
RMSprop 考虑通过仅使用一定数量的先前梯度来固定递减的学习速率。更新变成了
在哪里
既然我们理解了这两个优化器是如何工作的,我们就可以研究 Adam 是如何工作的了。
自适应矩估计或 Adam 是另一种通过考虑过去平方梯度的指数衰减平均值和过去梯度的指数衰减平均值来计算每个参数的自适应学习率的方法。这可以表示为
v 和 m 可以分别被认为是梯度的一阶和二阶矩的估计,因此被命名为自适应矩估计。第一次使用这种方法时,研究人员观察到存在一种固有的偏向 0 的倾向,他们通过使用以下估计值对此进行了反驳:
这将我们带到最后的梯度更新规则
这是我使用的优化器,其优点总结如下:
- 对于每个参数和每次迭代,学习速率是不同的。
- 学习并不像阿达格勒那样减少。
- 梯度更新使用权重分布的矩,允许更统计上合理的下降。
正规化
训练模型的另一个重要方面是确保权重不会变得太大,并开始专注于一个数据点,从而过度拟合。因此,我们应该始终包括大重量的惩罚(大的定义将取决于使用的正则化类型)。我选择使用吉洪诺夫正则化,这可以被认为是以下最小化问题:
函数空间在再生核希尔伯特空间(RKHS)中的事实确保了范数的概念存在。这允许我们将规范的概念编码到我们的正则化器中。
辍学者
一种防止过度拟合的新方法考虑了当一些神经元突然不工作时会发生什么。这迫使模型不要过度依赖任何一组神经元,而要考虑所有的神经元。辍学者发现他们的用途是使神经元更加健壮,从而使他们能够预测趋势,而不用关注任何一个神经元。以下是使用辍学的结果
可以看出,存在压差时,误差继续下降,而不存在压差时,误差处于平稳状态。
模型实现
由于有了keras
和它们的功能 API,上述所有分析都可以相对容易地实现。这是模型的代码(要查看完整的代码,请查看我的 GitHub: AlphaAI )
class NeuralNetwork:
def __init__(self, input_shape, stock_or_return):
self.input_shape = input_shape
self.stock_or_return = stock_or_return
def make_train_model(self):
input_data = kl.Input(shape=(1, self.input_shape))
lstm = kl.LSTM(5, input_shape=(1, self.input_shape), return_sequences=True, activity_regularizer=regularizers.l2(0.003),
recurrent_regularizer=regularizers.l2(0), dropout=0.2, recurrent_dropout=0.2)(input_data)
perc = kl.Dense(5, activation="sigmoid", activity_regularizer=regularizers.l2(0.005))(lstm)
lstm2 = kl.LSTM(2, activity_regularizer=regularizers.l2(0.01), recurrent_regularizer=regularizers.l2(0.001),
dropout=0.2, recurrent_dropout=0.2)(perc)
out = kl.Dense(1, activation="sigmoid", activity_regularizer=regularizers.l2(0.001))(lstm2)
model = Model(input_data, out)
model.compile(optimizer="adam", loss="mean_squared_error", metrics=["mse"])
# load data
train = np.reshape(np.array(pd.read_csv("features/autoencoded_train_data.csv", index_col=0)),
(len(np.array(pd.read_csv("features/autoencoded_train_data.csv"))), 1, self.input_shape))
train_y = np.array(pd.read_csv("features/autoencoded_train_y.csv", index_col=0))
# train_stock = np.array(pd.read_csv("train_stock.csv"))
# train model
model.fit(train, train_y, epochs=2000)
结果
以上是我对各个公司预测的结果。
很明显,使用这种神经网络架构的结果是不错的,并且如果在策略中实施,可以是有利可图的。
在线学习
除了从历史数据中学习模型之外,我还想让模型一直学习,甚至从预测中学习。因此,我把它变成了一个可以学习和预测的在线模型。换句话说,它学习历史数据,预测明天的价格,明天,当实际价格已知时,它也学习使用这些数据。所以模型一直在改进。
除了使用实际价格进行改进,我还考虑过制作一个二级模型,使用关于公司的新闻和 Twitter 的情感值。我将首先概述这些数据是如何获得的。
推特数据
除了股票价格数据,我还想尝试一些自然语言处理。因此,我尝试利用 twitter 和新闻中的情绪数据来改进股票预测。
第一个主要挑战是免费获取推文,因为获取整个档案的 Twitter API 是付费的。然而,我发现了一个 API,它允许我获取过去 10 天的推文,然后我可以实现某种形式的 NLP 来从推文中提取情感数据。这不是最佳的,但对我的在线学习模式仍然有用。
twitter api 用于收集过去 10 天的数据,情绪得分是使用 TextBlob 计算的,并对大量推文进行平均。
新闻数据
与 Twitter 类似,获取新闻数据非常困难。我试图分析彭博文章的网址,但意识到从 2000 年开始一直手动删除网站几乎是不可能的。因此,我选择了 Aylien API,它有一个非常强大的抓取模型。
新闻文章被删除,条件是它们只包括股票和金融新闻,过滤到 Alexa 网站的前 150 名,情绪得分使用指数加权移动平均进行平均,以更多地考虑最近的新闻而不是旧新闻。
在线模型
鉴于我的情绪得分,我使用了一个额外的神经网络层来纠正我预测的错误。然而,在撰写本文时,还没有得到这方面的结果,因为产生一个数据点需要一天的时间。
结论
神经网络非常擅长预测时间序列数据,当与情绪数据相结合时,确实可以做出实用的模型。虽然这里的结果令人印象深刻,但我仍在寻找改进的方法,也许真的能从中发展出一套完整的交易策略。目前,我正在研究使用强化学习来开发一个交易代理,使用预测模型的结果。
斯坦 vs PyMc3 (vs 爱德华)
神圣的三位一体说到贝氏。我将提供我使用前两个包的经验和我对第三个包的高水平看法(在实践中没有使用过)。当然,还有疯子(变得无关紧要的老教授),他们实际上做了自己的吉布斯采样。
My reaction to people writing their own samplers
这是我对这三个部分的 30 秒介绍。您可以为数据指定创成式模型。您将数据作为观察值输入,然后它从数据的后面为您取样。神奇!
Stan 是我使用的第一种概率编程语言。如果你来自统计学背景,这将是最有意义的。可以做 mu~N(0,1)这样的事情。文档是绝对惊人的。就个人而言,我不介意使用 Stan 参考作为贝叶斯学习的介绍,因为它向您展示了如何对数据建模。例子相当广泛。
另一方面,PyMC3 是专门为 Python 用户设计的。如今,大多数数据科学社区都在向 Python 迁移,所以这根本不是问题。您可以在下面看到一个代码示例。语法没有 Stan 那么好,但仍然可行。我真的不喜欢你不得不再次命名变量,但这是在后端使用 theano 的副作用。pm.sample
部分只是从后面取样。我喜欢这样一个事实,即使我有一个离散变量要采样,它也不会被扰乱,这是 Stan 到目前为止做不到的。
PyMC3 sample code
就文档而言,在我看来没有 Stan 那么广泛,但是的例子真的很好。结合 Thomas Wiecki 的博客,你就有了一个完整的 Python 数据分析指南。
PyMC3 成为我的(贝叶斯)工具的原因只有一个,那就是pm.variational.advi_minibatch
函数。当贝叶斯模型必须处理相当大量的数据(大约 10000 多个数据点)时,它真的很难处理。变分推理是进行近似贝叶斯推理的一种方式。Stan 和 PyMC3 都有这个。但 PyMC3 采取了额外的步骤来扩展它,以便能够使用小批量的数据,这使我成为了它的粉丝。
数学(可选)
稍微了解一下数学,变分推理所做的是最大化数据 log p(y)的对数概率的下限。
Variational Bayes Equations
我们试图通过改变建议分布 q(z_i)和 q(z_g)的超参数来最大化这个下限。z_i 指的是数据实例 y_i 本地的隐藏变量,而 z_g 是全局隐藏变量。上次我用 PyMC3 检查时,它只能处理所有隐藏变量都是全局变量的情况(这里我可能错了)。
本质上,我觉得 PyMC3 做得还不够,它让我把它当作一个真正的优化问题。第二项可以近似为
其中 N 是小批量的大小,N 是整个集合的大小。这是 Matthew Hoffman 在这篇论文中所写内容的精髓。我想指定模型/联合概率,让 theano 简单地优化 q(z_i),q(z_g)的超参数。这就是 GPU 加速真正发挥作用的地方。 Stan 在这方面确实落后了,因为它没有使用 theano/ tensorflow 作为后端。
爱德华
到目前为止,我对爱德华一直保持沉默。我没有在实践中使用过爱德华。我觉得主要原因是它没有好的文档和例子来轻松地使用它。的确,我可以将 PyMC3 或 Stan 模型直接提供给 Edward,但是听起来我需要编写 Edward 特定的代码来使用 Tensorflow 加速。我有一种感觉,Edward 可能在做随机变量推理,但遗憾的是文档和例子没有达到 PyMC3 和 Stan 那样的水平。然而,我必须说,当谈到贝叶斯学习的未来时,Edward 是最有前途的(由于在贝叶斯深度学习方面所做的大量工作)。
总之,PyMC3 对我来说是这些天的明显赢家。如果我不必时不时接触到 theano 框架,那就太好了,但除此之外,它确实是一个很好的工具。我希望看到 Edward 或 PyMC3 迁移到 Keras 或 Torch 后端,因为这意味着我们可以建模(和调试得更好)。快乐造型!
看这里是我的关于机器学习和深度学习的课程(使用代码 DEEPSCHOOL-MARCH 到 85 折)。
星巴克的顶点挑战
DSND uda city 顶石项目
这是 Udacity 数据科学家课程的顶点项目。该数据集包含模拟星巴克奖励移动应用程序上的客户行为的模拟数据。每隔几天,星巴克就会向手机应用程序的用户发出一次报价。当收到优惠的顾客的累计消费超过设计的阈值时,他/她将获得奖励。数据集包括 3 个文件,它们是:
- 投资组合,记录的报价类型
- 个人资料,客户资料
- 文字记录,记录人们收到报价、查看报价、完成报价和消费的时间。
它打算弄清楚顾客对这些广告的反应,以及什么样的人对每种类型的优惠反应最大?
问题陈述
我总是很好奇广告是否真的会促进生意。星巴克咖啡等产品曾经是我生活的一部分。即使没有任何折扣和 BOGO(买一送一),我也会继续购买星巴克。我相信有很多人和我一样,所以我想验证我的假设,即那些发送给顾客的优惠对星巴克没有意义。因此,两个问题都解决了。
- 谁会对咖啡报价做出反应?
- 根据人口统计和优惠类型,人们会花多少钱?
数据探索和清理
1.文件夹文件
portfolio
- id(字符串)—优惠 id
- offer_type (string) —优惠的类型,如 BOGO、折扣、信息
- 难度(int) —完成报价所需的最低花费
- 奖励(int) —为完成一项提议而给予的奖励
- 持续时间(整数)
- 频道(字符串列表)
通过手机、电子邮件、社交网站和网络等渠道发送给客户的报价有 3 种类型,即 BOGO、折扣和信息。每个报价都有不同的难度和奖励。例如,用户可以收到一个买 10 美元减 2 美元的折扣优惠,此后,如果客户累积至少 10 美元的购买,则该优惠完成,并且将给予奖励。然而,信息稍有不同。这种类型的出价没有奖励,所以我们不会在抄本文件中找到任何出价已完成的记录。
2.概要文件
Head of profile
- 年龄(整数)—客户的年龄
- 成为会员日期(整数)—客户创建应用程序帐户的日期
- 性别(str) —客户的性别(请注意,有些条目包含“O”代表其他,而不是 M 或 F)
- id (str) —客户 id
- 收入(浮动)—客户的收入
17,000 名客户包含在个人资料中,当他们注册他们的 app 帐户时,他们的个人资料会被记录下来。有些信息不准确。有 2,175 名 118 岁人没有与性别和收入有关的信息。
Distributions
剔除这些无效资料后,14,825 名客户中女性占 41.3%,男性占 57.2%。超过 60%的人年龄在 45 到 80 岁之间。而他们的平均收入在每年 6.5 万左右。
3.抄本文件
Head of transcript
- 事件(str) —记录描述(即交易、收到的报价、查看的报价等。)
- 人员(字符串)—客户 id
- time (int) —以小时为单位的时间。数据开始于时间 t=0
- value —(字符串字典)—报价 id 或交易金额,具体取决于记录
该文件的 306,534 条记录来自应用程序。每个人可能会多次收到每种类型的报价。发送了 76,277 份报价,12,774 人至少完成了一份报价,422 人收到了没有任何消费的报价,16,834 人没有查看他们收到的任何报价,6 人购买了咖啡,而他们没有收到任何报价。
数据清理
- 从抄本文件的值字典中提取值。
- 替换包括自定义 id 在内的 id,并将 id 提供给更容易识别的 id。
- 根据事件的 4 种类型,将抄本文件分成 4 个表格。
- 将个人资料文件中的年龄和年龄的数字数据转换为类别数据。
Q1:谁会对咖啡有反应?
我们知道,当累计购买量超过阈值时,它将被记录为要约完成。然而,我们无法知道顾客购买咖啡是因为广告还是仅仅出于自然。也许我们可以设定标准来证明谁会对广告做出回应。
数据预处理
收到报价、查看报价并及时购买达到阈值金额的客户应被视为对三种报价类型之一做出响应的人。
然后,我用内部方法合并从人员和报价 id 的记录文件中分离出来的报价已接收、报价已查看和报价已完成表,并将新表命名为 response。然后根据报价 id 从投资组合表中添加持续时间变量,以确保每条记录都符合标准。
识别收到信息的人的方法略有不同。由于没有报价完成记录,另一个标准被定了下来。一个人在收到要约的时间加上持续时间内收到并查看了购买量大于零的信息性要约,则推断此人对该要约做出了响应。
探索数据分析
有 1,973 条记录符合标准。总体回答率为 2.59%。折扣要约的回复率最高,为 3.18%,信息要约的回复率最低,为 1.91%,BOGO 的回复率为 2.32%。
除了年轻人之外,每个年龄层的顾客对这些产品的态度都是一样的。他们喜欢打折,尤其是老年顾客。
3 年会员更容易受到折扣和 BOGO 的影响。也许他们更熟悉晋升的规则。然而奇怪的是为什么最早的成员不关心推广这么多。可能是样本小的原因。但是他们更容易受到信息的影响。
显然,收入较高的人如果得到促销优惠,会更愿意购物。当然,有钱人不在乎升职。为什么低收入人群较少受到星巴克促销的影响?一个合理的解释是,他们负担不起更频繁地去星巴克购物,因为只有 13%的顾客年收入低于 4 万英镑。
Q2:根据人口统计和产品类型,人们会花多少钱?
可承受的收入应该与消费有较高的正相关性。事实上,在这个给定的数据集中,收入和平均购买量之间的相关性是 0.80。因此,很容易得出结论,高收入的人平均会购买更多的星巴克咖啡。
然而,结果并不是我想要知道的。我更关心其他因素是否会对星巴克的消费产生积极影响。
数据预处理
内生变量是购买总量,外生变量是人口统计和优惠类型因素。
客户及其人口统计因素由档案文件提供。优惠类型因素来自响应表。这些广告只对有响应的人有意义。通过使用 subtotal on person 从交易表中得出购买的总金额。
其他变量是分类数据。它们将被转换成虚拟变量。
数据处理
- received_offer 表通过 left 方法合并到 person id 上的 profile 表中,以便标识身份客户收到的优惠类型。
- 消除他们没有收到任何报价的 id。
- 计算按每个人分组的交易金额的总和,然后用 left 方法加入到人员 id 的配置文件表中。
- 将分类数据转换为虚拟变量。
- 将数据分为训练集和测试集。
度量和模型选择
选择有监督的机器学习模型来解决这个问题。由于购买总额是连续数据,因此选择了三个回归模型作为候选模型。它们是多元线性回归模型、随机森林回归和支持向量回归。
三个度量是 con:
- R 平方,衡量预测值在多大程度上解释了实际值。
*过拟合,训练集和测试集的 R 平方之差。
*误差分布为线性回归模型。
结果
三个模型具有相似的 R 平方,但是都是欠拟合,没有过拟合。结果是可以预料的。如上图所示,收入与金额相比,数据集非常嘈杂。
改进
由于金额和收入是高度正偏的,最好将其转换为接近正态分布。
此外,应该消除异常值,并对数据集进行缩放。
r 平方比原始模型稍好,特别是对于推断过拟合较少而欠拟合仍然难以解决的测试数据集。此外,多因素线性回归模型的误差分布更接近正态分布,偏度为 0.3,峰度为 2.8。由于度量标准相似且易于理解,选择线性模型进行分析。
参数分析
尽管 R2 还不够好,但从模型中得到的因素参数提供了更有价值的信息。
- 收入因素、性别因素、成为会员因素和提供类型因素都显著不同于零,而年龄因素则不显著。
- 所有基于优惠类型的因素都有积极的贡献。这意味着广告真的可以帮助星巴克增加收入。
- 正参数值意味着女性比男性更有可能消费。
深入观察人口统计因素。
- 年龄大于 30 岁小于 80 岁的人更有可能消费星巴克。这是有道理的,因为他们有收入。因此,年龄并不重要。
- 与上述可视化分析结果类似,2015 年注册会员的客户贡献了更多的消费。
基于优惠类型的因素告诉我们:
- BOGO 和折扣比信息广告更能刺激消费。
- 持续时间越长,记录的购买量越多。这就产生了另一个问题,如果证明对这些类型的优惠做出反应的客户的标准过于严格。但是这个问题我不想深究。
- 更高的奖励吸引更多的消费。
结论
机器学习模型表明,广告确实能帮助增加收入,但不一定能增加利润。信息性报价的效果不如 BOGO 和折扣。然而,如果星巴克打算吸引更多的消费,促销成本可能会抵消收入,而没有足够的顾客反应。
然而,可视化分析显示回复率较低。结合可视化分析和机器学习模型的结论表明,广告 3 年会员的高收入女性似乎是一个好主意,因为她们很可能在星巴克花费更多。
用单个神经元启动神经网络
image from — https://unsplash.com/photos/ZiQkhI7417A
什么是分类问题?
分类是 ML 中一个重要的中心话题,它与训练机器如何按照特定的标准将数据分组有关。分类是计算机根据预定特征将数据分组的过程——这被称为监督学习。有一种无监督版本的分类,称为聚类,当类别未被指定时,计算机找到共享特征来对数据进行分组。
例如:
- 垃圾邮件:你的目标是预测一封邮件是否是垃圾邮件,是否应该发送到垃圾文件夹。(垃圾邮件/非垃圾邮件)。文本是输入(X)的简单单词序列。目标是预测二元响应 Y:是否是垃圾邮件。
- 手写数字识别:你的目标是正确识别 0-9 的图像。每个条目都是从 0(黑色)到 255(白色)像素强度范围内的整数。这个原始数据(X)将被输入,每个图像将被识别为(Y) 0 或 1 或 2 …或 9。
当你考虑第一个问题时,它被称为二项式逻辑回归,其中响应变量有两个值 0 和 1 或通过和失败或真和假。我们可以解释第二个问题,但是通过复杂性,我避免了对它的解释。对了,叫多类分类问题。
什么是逻辑回归?
如果你拿任何一个神经网络来说,它最基本的概念将依赖于一个分类算法**逻辑回归。**给定输入,输出为 1 的概率。它可以表示为条件概率:
例如,如果 x 是一封电子邮件,那么这封电子邮件是垃圾邮件的可能性有多大。
假设输入 x 只有一个值,我们需要 ŷ。通过下面的等式,我们推导出 ŷ.**在该等式中,w(称为权重)和 b(偏差)是决定输出 ŷ精确度的参数。**请注意, **ŷ可以是任何值(100 或 103 或 3002.5 或-50)。**为了使它在 0 到 1 之间,我们使用 Sigmoid 函数。不仅仅是我们使用的函数,还有许多具有不同特性的函数。为了教程的简单,我不打算深入讲解。
通常输入不是一个单一的值。因此,我们将所有输入表示为矩阵 X. 相应的参数值为矩阵 w 和 b 。将输入 x 作为 n 维矩阵。这里西格玛表示西格玛函数
让我们深入一点…
从这里你可能会对神经元中发生的事情有一种直觉。在继续下一步之前,请看下图。
Pic has been taken from Andrew Ng’s Data Science Course
这是一个简单的图表,显示了单个神经元对 cat 的预测。这里发生的是每一张猫的图像都被转换成一个矩阵向量。例如,RGB 图像将包含红色、蓝色和绿色的 3 个矩阵。如果我们取一个矩阵为 6464 像素,那么一个矩阵将包含 6464 个值。我们将所有三个向量组合成一个矩阵,并作为 1*(64643)矩阵输入。如果你拍摄 n 张照片,那么矩阵就是 n*(64643)。如图所示,最终输出将在 0 到 1 之间,如果值大于 0.5,我们可以区分猫图像。
就是这样……😄
.
.
.
等等……那么参数呢——权重和偏差。谁给它赋值?
是的,神经网络的主要部分是通过训练模型使用损失函数来微调参数。我们使用损失函数来微调参数。
构建神经网络模型包括以下重要步骤。
- 首先,你必须初始化模型的参数。
- 通过最小化成本来学习模型的参数(w,b)。
- 使用学习到的参数进行预测(在测试集上)。
- 分析结果并得出结论。
以下函数表示 m 幅图像的总损失。其中 ŷ 是作为输出给出的值,而 y 是原始值。举个例子,如果你拍摄一张猫的照片,ŷ就是上面的逻辑回归函数给出的输入 x 的输出。y 是实际的标签,其中 y 表示标签(cat 与否)— 0 或 1
这个说不通。这些损失你打算怎么处理?
是的,通过减少损耗,我们提高了精确度。为了减少损失,必须更新参数。如果参数更新正确,那么只有输出 ŷ会自动靠近 y 轴。我们使用梯度下降来实现。
“梯度衡量的是,如果你稍微改变输入,函数的输出会改变多少。”——莱克斯·弗里德曼(麻省理工学院)
如果你想了解更多关于渐变得体的 请参考本教程 。
最后,我们将更新参数,以尽量减少损失,通过这一点,我们得到了良好的准确性。
我觉得,我已经给了你一点关于一个神经元的直觉。下次我会试着想出代码。如果你想了解神经网络,请参考我以前的教程。
感谢阅读。如果这是一本好书,请鼓掌。尽情享受吧!
创业生活:5 天 5 种编程语言
影响巨大的旋风项目
两年前,我开始在创业公司做开发人员。很明显,作为开发人员,最重要的技能是快速学习新技术的能力。不管是什么技术,我学习新技术的方法都非常相似。我从阅读“关于”页面开始,试图找出该技术试图解决的问题。然后,我查看用例并浏览快速入门指南。最后,我将开始编写我需要的代码。首先,我将在网上搜索一个例子,然后可能尝试修改现有的代码,或者猜测并检查我自己的实现。然后,我将运行代码并跟踪错误消息。这些年来,我非常擅长阅读回溯,这让我成为了一名更好的程序员。还有, stackoverflow , stackoverflow 和 stackoverflow。在最近的一个项目中,我发现自己在 5 天内用 5 种不同的语言编写代码,这提醒了我快速学习新技术的技能是多么重要。我的任务是建立一个数据管道,在图像被用于训练卷积神经网络之前对其进行处理。
第一天:Ruby on Rails
尽管我在 R&D 团队工作,后端团队使用 Ruby on Rails 进行开发。他们开发了一个 ruby 脚本,将数据匿名化,然后转储给 R&D 使用。我们开会决定 R&D 可以使用生产数据库中的哪些表/列来构建神经网络,但我注意到数据匿名化程序从未更新过。我将此事提请工程部门注意,他们建议我更新程序,因为我确切地知道需要什么样的色谱柱。因此,第一天要求我用 Ruby 编写代码,这是一种我没有经验的语言。这并不太具有挑战性,因为我只是简单地更新了一个现有的脚本。
第二天:SQL
我现在有了需要为神经网络处理的原始图像的 URL。除了 URL,图像还需要与它们的类型和大小调整参数相关联,但是这些信息分散在数据库的六个表中。我需要编写一个 SQL 过程,将数据整理到一个表中。我以前在开发 Django web 应用程序时使用过数据库,但是 Django 的一个非常好的地方是它的对象关系模型(ORM)。这意味着我从未编写过原始 SQL,而是使用了 python API。然而,对于我当前的项目,我不再被 ORM 的便利所宠坏,而不得不写原始的 SQL。SQL 不是最复杂的语言,但它确实有一些我没有立即发现的行为。例如,如果没有对列进行聚合,就不能在 group by 过程中选择列(仔细想想,这是有意义的)。相反,您必须基于一个惟一的字段连接回原始表(有效地聚合该列)。此外,当在两个表之间进行连接时,可以返回比单个表中更多的行。最后,我对声明式编程有了一种欣赏,甚至开始喜欢它。
第 3 + 4 天:C++ & Python
在成功地修改了数据转储过程并编写了 sql 清理脚本之后,我获得了需要处理的数据。这个项目侧重于图像分割,需要进一步处理图像,并将它们与元数据相关联,然后才能输入到神经网络中。这是用 C++编写的,这是另一种我从未使用过的语言,老实说我很害怕(我从未使用过编译语言)。也许我的一半处理脚本用 python 写,另一半用 C++写是没问题的,但是我发现修改 C++脚本很有挑战性,所以我决定把 C++代码翻译成 python。为此,我必须编译并运行 C++代码,理解它,然后用 python 写它的等价物。这真的很有挑战性,但是我的一个同事帮我用 xcode 建立了我的构建环境。老实说,我发现调试 C++真的很有挑战性,主要是因为我无法打开一个解释器来测试我的逻辑。当我和我的同事谈到这一点时,他提出了它的优点,因为它避免了运行时错误。我注意到的一件事是 python 代码慢了多少。
第五天:哈希公司配置语言(HCL) — Terraform
现在我已经有了处理脚本(python)和适当的数据(postgres 表),是时候实际处理图像了,这项任务可能需要几个小时,甚至几天。唯一的问题是,这些图像是在私人 S3 桶。在我看来,每个公司都需要双倍的 DevOps 工程师。让 DevOps 的小团队从维护和开发生产应用程序中抽出时间,我感到不舒服,因此,在他们的指导下,我进入了 terraform,一个作为代码框架的基础设施。使用这个框架,我编写了代码来为机器提供可以处理图像的适当模块。这非常具有挑战性。我知道如何编写程序,但是配置 VPCs、安全组、S3 资源完全超出了我的能力范围。多亏了预建模块的 terraform 注册表和 DevOps 的帮助,我才能够拼凑出一些东西。旁注:基于云的数据科学是一个被称为数据操作的新兴领域。这非常有趣,这次经历让我离它最近。
希望这篇文章对你自己的编码冒险有所启发,或者对你不是唯一一个不断走出舒适区的编码员有所启发。无论如何,小心编码,祝你好运!
https://pixabay.com/en/mario-luigi-figures-funny-colorful-1557974/
从问题开始您的数据探索
大多数已经经营了一段时间的公司已经积累了大量的数据,这些数据通常分布在多个数据库或数据存储中,并且具有多种格式,有些是结构化的,有些是自由格式的。
在数据分析和机器学习提供新工具和方法的时代,现有数据显然代表着一个机会。可以收集到哪些新的见解?可以从哪些机构流程中吸取经验并加以改进?哪些运营效率或机会被忽略了?
但是在数据收集过程中还存在许多障碍。很难知道哪些数据是最重要的。即使在结构化数据库中,数据的格式也可能随着需求的变化而改变。正如我在之前的文章中所描述的,数据挖掘——注意差距,数据之间的关系可能并不明显,可能无法通过加载和规范化过程。
在加载任何数据之前,应该有一个活跃的步骤,我称之为头脑风暴提问。一群知识渊博的人应该提出他们可能想知道的关于数据的问题。这里的重点不是回答问题,甚至不是预测什么问题真的会被问到。相反,我们的目标是探索回答每种类型的问题需要哪些联系。
如果项目的目的主要是数据分析,问题通常应该是分析性的。与劳动密集型项目相比,我们在资本密集型项目上取得了哪些成功?这种成功会随着时间而改变吗?某些人参与了最成功或最不成功的项目吗?与收入增长相比,每个部门的员工增长率是多少?
如果目的更多的是认知性的,问题可能更多的是对话性的和定性的?我们团队中的哪些人主动提出了建议?与那些沉默寡言的人相比,他们有什么样的建议?当某个特定产品的销量下降时,什么样的信息会向公众发布,什么样的信息会在内部发布?在我们公司内使用任何特定的短语是否倾向于暗示某项计划的成功或失败?
下面的简短视频展示了在数据分析之前使用的这一过程的简化快照。该演示基于一组真实的 Lotus Notes 数据库,其中包含业务合作伙伴可以向 IBM 报告的可能的错误报告,IBM 将对这些报告做出响应。注意,即使对于这个简单的系统,每个 bug 和 IBM 的响应都在由父子层次结构关联的单独文档中。修复列表数据库是一个独立的数据库,没有以任何明显的方式链接,但可以通过关键字进行搜索。有丰富的文本与自由形式的问题和答案。
关键的想法是,通过在开始挖掘之前提出问题,可以弄清楚哪些关系需要保留。您还可以决定在数据分析中将来自不同地方的信息汇总到一个“记录”中。
请记住,在实际准备和清理数据的过程中,任何可以节省的时间都是值得的。
因此,在你开始将数据加载到你的数据或认知分析系统之前,计划时间问一些自由形式的问题。它们可以帮助你理解你所拥有的数据,并确保你加载、规范化和准备数据的努力将导致一个系统来回答你还没有想到的问题。
开始一个数据科学项目
上周六,我在一次网络研讨会上谈到了如何进入数据科学领域。其中一个问题是“典型的一天是什么样子的?”我认为在大型项目的任何机器学习发生之前,有一个很好的机会来解释真正发生了什么。
我以前写过关于为功能工程进行创造性思维的文章,但为数据科学项目做准备还有更多,你需要从业务的其他领域获得对项目的支持,以确保你提供业务想要和需要的见解。
通向机器学习算法的道路看起来像这样:
- 会议
- 更多会议
- 数据采集
- 特征工程
- 然后是机器学习
在本文中,我们将只讨论前 3 个要点。研究最佳解决方案也可能是这个过程的一部分,但我知道我是在做细分。
在我为一个大项目写一行 SQL 之前,会有很多会议。如果你阅读了足够多关于数据科学的评论/博客,你会看到人们说 95%的数据聚合和 5%的建模(或其他类似的划分),但这也不是全部。当你成为一名数据科学家的时候,我希望你能完全理解你所签署的协议。
正如我提到的,第一步是真正参与你的项目。作为分析部门,我们致力于解决业务需求,这一点很重要。我们希望通过在与这些利益相关者的会议上推销这一想法,帮助企业的其他部分理解细分可以带来的价值。另外,我也不是独角戏。只要有机会,我的老板就会抓住机会谈论我们可以从这个项目中学到什么,并采取行动。我们现在已经在多个团队中达成共识,他们希望我们提供行为客户细分。
但是我仍然不仅仅是一头扎进 SQL。我的团队和前面提到的业务领域中的一些人可以帮助我们集思广益,看看我们可能有哪些数据可以帮助我们了解我们的客户。在我们的案例中,存在我们以前没有机会分析的数据。
第一步是和我的团队开会,讨论我们能想到的所有相关数据。思考这样的事情:
- 如果某种东西可以代表更“精通技术”的客户。也许这是一个商业电子邮件地址,而不是谷歌地址,或者也许你正在利用我们更先进的功能
- 人口普查数据可以告诉我们一个客户的邮政编码是在农村还是在城市?它们可能是不同的。
- 大数据环境中有什么可用的?在数据仓库里?公司内部的其他数据源。当你真的想列出所有事情时,你会发现这可能是一个大事业。
接下来,我在不同的会议上会见了营销和运营部门,以确保我们没有遗漏任何东西,并看看他们是否对输入有更多潜在的想法。在整个项目过程中保持持续的沟通也是很好的实践。
在我们有了一个潜在数据列表后,会议开始帮助我们追踪所有的数据。你肯定不想在这里重新发明轮子。没有人会因为自己编写所有的 SQL 而得到加分,如果您利用以前编写的查询,这将花费您一半的时间。
如果我知道某个项目中有人已经创造了一些很酷的功能,我会给他们发电子邮件,询问他们的代码,我们是一个团队。
最后,在我的团队之外,我需要联系 6 个不同的人,他们比我的团队成员更了解这些表或数据源。所以是时候向其他人询问这些桌子了,这意味着安排更多的会议。
老实说,我很喜欢这个过程,这是一个了解我们所拥有的数据、与他人合作以及为功能工程考虑很酷的机会的机会。
同样值得注意的是,这些与团队以外的人的会议可能不是他们最优先考虑的事情,时间安排可能会变得棘手,不要气馁。坚持就好。你对自己需要从别人那里得到什么越清楚、越简洁,可能有助于你更快地获得帮助。如果他们在同一个办公室,你可以试着去他们的办公桌旁。
脑海中的画面通常是数据科学家独自坐在角落里几个月,然后带着一个无人问津的模型回来。但是通过获得认同,与其他团队和你的团队成员合作,这并不是必须的。您可以成为思想伙伴,主动提供解决方案,更好地锁定客户群,个性化客户体验。
如果你想订阅我未来的文章,你可以在这里订阅。
原载于 2018 年 7 月 3 日【datamovesme.com。
从生物信息学开始——DNA 核苷酸和链
在我介绍生物信息学的第一篇文章中,我提到我们将会学到很多关于 DNA、RNA 和蛋白质序列的知识。因为我对所有这些 DNA/RNA 术语都不熟悉,所以我决定先学习它们,然后尝试一些编码问题。所有的测序问题好像都有一些和遗传学有关的词。首先,让我们开始吧。😊
**注:**我假设你有基本的化学知识,从而假设你知道诸如氢键、磷酸基团、羟基等术语的含义。
什么是 DNA?
DNA 或脱氧核糖核酸,是一种携带所有生物遗传密码的分子。DNA 和它的朋友 RNA 或核糖核酸一起被称为核酸。DNA 是储存生物所有生物信息的结构。
什么是核苷酸?
DNA 是一种长的链状分子,由两条拧成双螺旋的链组成。这两条链由更简单的分子组成,叫做核苷酸 T21。每个核苷酸由四个含氮的核碱基 *、*中的一个组成
DNA chemical structure (https://en.wikipedia.org/wiki/Nucleobase)
- 胞嘧啶*(C)*
- 鸟嘌呤*(G)*
- 腺嘌呤 ( 一 )
- 胸腺嘧啶*(T)*
连同一种糖叫做*,还有一种 磷酸 基团 。*
这些核碱基通过在一个核碱基的糖和下一个核碱基的磷酸之间形成共价键而以链状结构相互连接,从而产生交替的糖-磷酸骨架。你可以参考上面给出的图表来清楚地理解。
代表一条 DNA 链
如上所述,通过连接核碱基形成的单个 DNA 链可以简单地表示如下。您可以将此表示映射到之前的图像。这将是上图中分子的一面。
Chained nucleotides constituting a DNA strand (Wiley: Bioinformatics For Dummies, 2nd Edition)
你可能会注意到,最左端有一个未使用的 磷酰基 (也叫5’-末端* )和最右端有一个未使用的 羟基 (也叫3’-末端 )。单链的 DNA 序列总是被定义为一系列组成它的核苷酸,按从未使用的磷酰基到未使用的羟基的顺序排列。上述 DNA 序列可以表示为:*
***TGACT** = **Thymine-Guanine-Adenine-Cytosine-Thymine***
DNA 序列的两个部分
根据碱基配对规则,两条独立链的核碱基连接在一起; A 带 T 和 C 带 G ,连同 氢键 。DNA 分子由两条互补链组成,如下图所示。也标记了核苷酸的阅读方向。
The two complementary strands of a complete DNA molecule (Wiley: Bioinformatics For Dummies, 2nd Edition)
互补性意味着两条链遵循碱基配对规则。一条链上的胸腺嘧啶总是面对腺嘌呤,反之亦然;鸟嘌呤总是面对着胞嘧啶,反之亦然。当你知道一条 DNA 链的核苷酸序列时,你可以自动推断出另一条链的序列。
上述 DNA 分子的相对链的表示将是,
***TGACT** and **AGTCA***
该练习了
我偶然发现了这个名为 Rosalind 的有趣的编程平台,在这里你可以通过解决现有的问题来学习生物信息学和编程。我将讨论与我在本文中讨论的内容相关的两个问题,并解释我是如何解决它们的。我将用我自己的例子来解释。你可以从上面的链接中尝试一下。
计算 DNA 核苷酸
给定的一个 DNA 序列可以被认为是一个带有字母表的字符串{“A “、” C “、” G “、” T”}。我们可以计算每个字母在字符串中出现的次数。
下面是我的 Python 解决方案。
Counting DNA Nucleotides solution in Python
我的 sample_dna.txt 文件包含以下 dna 字符串。
***GTAAACCCCTTTTCATTTAGACAGATCGACTCCTTATCCATTCTCAGAGATGTGTTGCTGGTCGCCG***
下面给出的是输出。
Counting DNA Nucleotides output
我们可以从文件中读取 DNA 字符串,然后使用 Python 的字符串计数方法来计算每个字母出现的次数。您还可以在循环中迭代字符串,并分别维护每个字母的计数。
补充一条 DNA 链
回忆一下我们所了解的 DNA 分子的互补链。这个问题是关于寻找另一个面对的链的序列。
下面是我的 Python 解决方案。
Complementing a Strand of DNA solution in Python
我的 sample_dna.txt 文件包含以下 dna 字符串。
***AAAACCCGGTGTCTTATATCGAGTCATGCAATTTTGGG***
下面给出的是输出。
Complementing a Strand of DNA output
在这个解决方案中,我反向遍历字符串,将 A 替换为 T,T 替换为 A,G 替换为 C,C 替换为 G,以获得互补的 DNA 字符串。
最后的想法
DNA 序列和相关数据存储在巨大的数据库中,并用于不同的领域,如法医学、系谱学和医学。这些简单的技术将成为开发更复杂问题的解决方案的基础。
希望你喜欢读这篇文章,并学到一些有用的东西。
因为我对这个领域还很陌生,所以我想听听你的建议。😇
感谢阅读…😃
从生物信息学开始——RNA 转录和翻译
在我之前的文章中,我已经介绍了 DNA 以及如何将 DNA 变成蛋白质。别忘了也去看看。在本文中,我们将涉及以下主题。
- 什么是 RNA?
- 如何从 DNA 中获取 RNA?
- 如何把 RNA 变成蛋白质?
在本文的最后,我们将尝试用 Python 编写一些代码来涵盖我们所学的内容。
什么是 RNA?
Nucleobases in an RNA molecule (https://en.wikipedia.org/wiki/RNA)
RNA 或核糖核酸是一种核酸,类似于 DNA。然而,与非常稳定的 DNA 不同,RNA 是核酸家族中更活跃的成员。在生物信息学的背景下,RNA 和 DNA 之间只有两个重要的区别:
- RNA 与 DNA 相差一个核苷酸。
- RNA 由单链(一个核苷酸)组成,不是螺旋。
RNA 由下列核碱基组成,
- 胞嘧啶 ©
- 鸟嘌呤 (G)
- 腺嘌呤(一)
- 尿嘧啶 (U)
连同一种糖叫做 核糖 ,还有一种 磷酸 基团 。 尿嘧啶 是在 RNA 中发现的碱基,不同于在 DNA 中发现的胸腺嘧啶。这些核碱基通过在一个核碱基的糖和下一个核碱基的磷酸之间形成共价键而以链状结构相互连接,产生了交替的糖-磷酸骨架,类似于 DNA 链。你可以在我之前的文章中读到更多关于链形成过程的信息。
尽管 RNA 分子是单链的,但它们仍然强烈要求与互补序列配对。单链 RNA 分子与其序列的不同区域配对,形成稳定的双螺旋结构。每个 RNA 分子试图配对尽可能多的核苷酸,同时保持它们的形状。发夹形状如下图所示,是 RNA 结构的基本元素。它们由环(图中未配对的 C-U)和茎(图中配对的区域)组成。
How RNA turns itself into a double-stranded structure (Wiley: Bioinformatics For Dummies, 2nd Edition)
DNA 转录成 RNA
转录是一个过程,其中一条 DNA 链被用作模板,通过一次复制一个核苷酸来构建一条 RNA 链,其中尿嘧啶被用于替代胸腺嘧啶。
以下面的 DNA 字符串为例。
**AATTTGCTTGGCAAATCGTATGCCTTGGG**
通过将所有出现的 T 替换为 U ,可以获得如下相关的 RNA 字符串。
**AAUUUGCUUGGCAAAUCGUAUGCCUUGGG**
你可以在这里阅读关于转录的从一个更生物学的角度。
将 RNA 转化成蛋白质
如果你知道一个 RNA 序列,可以用遗传密码翻译成相应的蛋白质序列。这与细胞自身产生蛋白质序列的方式相同。
RNA 的遗传密码(也称为 RNA 密码子表)展示了我们如何将一个 4 核苷酸序列(A、U、G、C)与一组 20 个氨基酸唯一地联系起来。这和我上一篇文章讨论的 DNA 密码子表很像,只是我们用了“U”而不是“T”。下图以图表的形式显示了 RNA 的遗传密码。
Genetic code chart for RNA (https://stackoverflow.com/)
如何使用遗传密码图进行 RNA 到蛋白质的翻译
首先你应该得到你的 RNA 字符串。
**AUGGCCAUGGCGCCCAGAACUGAGAUCAAUAGUACCCGUAUUAACGGGUGA**
然后开始一次读取 3 个核苷酸的序列(一个三联体)。
**AUG GCC AUG GCG CCC AGA ACU GAG AUC AAU AGU ACC CGU AUU AAC GGG UGA**
现在用遗传密码图读出当前三联体对应的是哪一个氨基酸(技术上称为密码子)。从中心开始的第一个圆圈代表三元组的第一个字符,第二个圆圈代表第二个字符,第三个圆圈代表最后一个字符。翻译后,你会得到与上述 RNA 序列相对应的蛋白质序列。
**M A M A P R T E I N S T R I N G STOP
M A M A P R T E I N S T R I N G**
UAA , UAG 和 UGA 被称为终止信号,在此停止翻译过程。
该练习了
将 DNA 转录成 RNA
RNA 字符串可以用字母表示为{“A “,” U “,” G “,” C”}。通过用“U”替换 DNA 串中所有出现的“T ”,可以将 DNA 串转换成其相应的转录 RNA 串。下面给出了从给定的 DNA 字符串中获取 RNA 字符串的示例代码。请随意尝试代码,看看会发生什么。
Transcribing DNA in to RNA code in Python
我在 sample_dna.txt 文件中输入了以下 DNA 字符串。
**ATATATCCCGGGAATTTTCGTAGTTAGGCTGATTTTATTGGCGCGAAAATTTTTT**
下面给出了获得的输出。
Transcribing DNA in to RNA output
请注意,所有出现的“T”都被替换为“U”。
使用 Python 进行 RNA 到蛋白质的翻译
下面给出的 Python 代码获取一个 RNA 序列,并将其转换为相应的蛋白质序列。我已经创建了一个字典来存储遗传密码图的信息。请随意尝试代码,看看会发生什么。
Translating RNA into Protein code in Python
我使用了上面提到的同一个例子,作为对 sample_rna.txt 文件的输入。
AUGGCCAUGGCGCCCAGAACUGAGAUCAAUAGUACCCGUAUUAACGGGUGA
下面给出的是结果。
Translating RNA into Protein output
最后的想法
生物信息学中有一个独立的分支,名为 RNA 生物信息学,主要研究 RNA。目前有许多工具和技术可用于分析 RNA 序列。
希望你喜欢读这篇文章,并学到一些有用的东西。
因为我对这个领域还很陌生,所以我想听听你的建议。😇
请继续关注我关于生物信息学的下一篇文章。
感谢阅读…😃