TowardsDataScience-博客中文翻译-2020-一百二十-
TowardsDataScience 博客中文翻译 2020(一百二十)
在 Power BI 中将平面矩阵变成全新的视觉
使用这个简单的技巧快速将传统矩阵转换为更“定制化”的视觉效果
图:截图
Power BI 的最*优势之一是极其庞*的视觉效果集合,包括来自 AppSource 的内置和自定义视觉效果。老实说,几乎每个商业场景都可以通过适当的方式进行可视化处理。
然而,有一些真正简洁而简单的技巧可以将原生 Power BI 视觉效果转换为自定义视觉效果。不仅如此,你还可以称赞它是个人创作的:)
普通旧矩阵
让我们切换到 Power BI,我将快速向您展示如何将普通的矩阵视觉转换为看起来像自定义视觉的东西。
像往常一样,我将使用 Contoso 示例数据库进行演示。假设我们想显示每个品牌每月的总销售额:
如你所见,这个矩阵没什么特别的。一堆数字,所以不深入分析就很难一眼看出高点和低点。
自定义视觉效果
为了使我们的用户能够立即发现正在发生的事情,我们将转到格式窗格下的条件格式选项,并选择销售金额作为应用格式的度量。
我们首先格式化的是背景色。所以,我打开它,进入高级控制:
你可以看到 Power BI 自动应用了一些渐变着色,但是我们想进一步定制它。因为我的整个报告是基于蓝色的,所以我也将应用“蓝色”吨。
您可以注意到单元格的背景颜色发生了变化,以反映销售额数值的差异,但是数字仍然存在。因此,下一步我们应该做的是在字体颜色属性上应用相同的格式。
再次,进入高级控制,设置相同的颜色饱和度作为背景:
看看我们刚刚得到的——“树形图”一样的视觉效果,但是没有与“树形图”视觉效果相关的限制,也不需要应用复杂的格式:
因此,只需点击几下,我们就将普通的旧 Matrix 转变成了现代外观的定制视觉效果!多酷啊!
这样,我们使我们的用户能够很容易地发现,例如,Contoso 和 Fabrikam 的销售额在 5 月和 7 月之间*幅增长,并且 Contoso 最差的月份比除 Fabrikam 以外的任何其他品牌的任何月份都要好!
最重要的是——我们可以在 2 秒钟内轻松发现这些趋势,而无需深究数字。当然,数字仍然在那里,如果我们想检查一个特定的数字,工具提示将像一个魔咒:
结论
不要对你报告中的每一个矩阵都盲目使用这一招。仍然会有更多的场景需要你坚持使用传统的方式来显示数据。
然而,当这种技术有益时,你肯定会面临一些情况(特别是在有很多数字的“宽”矩阵中)。应用这个技巧无疑会丰富你的用户体验。
感谢阅读!
将 Excel 工作簿转换为 SQLite 数据库
图片来自推特:@jankolario 在 Unsplash
将电子表格转换成可查询的数据库表格
介绍
关系数据库 是数据表的集合——存储单个数据的行和列的集合——它们可以相互连接。这样,关系数据库与 Excel 工作簿并不完全不同,Excel 工作簿中的相关数据集存储在多个工作表中。考虑到这一点,本文通过一个例子,使用 Python 将 Excel 电子表格转换成可以使用结构化查询语言(SQL) 查询的数据库。
数据
这篇文章中的例子使用了来自超市销售数据集的数据,可以在这里找到。该数据集存储为 Excel 工作簿,包含存储在以下四个表中的示例销售交易数据:销售、订单、客户和产品。
用 Python 读取数据
首先,如果您没有使用conda
或pip
安装xlrd
包,请在启动 Python 会话之前安装,否则当您试图读取 Excel 文件时会遇到以下错误(即使安装了 pandas )。
现在让我们启动一个 Python 会话,并使用import pandas
和import sqlite3
导入熊猫和 sqlite3 。我们将使用read_excel
将每个 Excel 电子表格中的数据读入一个单独的 pandas 数据框,如下面的代码所示。
在每个代码块中,read_excel
中的第一个参数标识要处理的文件的名称。如有必要,包括文件所在的路径。例如,当我运行这段代码时,我的数据存储在data
目录中,但是我的工作目录在上面一个目录中。第二个参数sheet_name =
,指定工作簿中要处理的电子表格的名称。最后一个参数header=0
告诉 Python 正在读取的电子表格中的第一行包含列标题。记住 Python 是零索引的,这就是为什么我们使用0
而不是1
来标识第一行。通过明确标识标题行,每个电子表格第一行中的值将被视为其各自数据框的列名。
让我们看一下每个数据框的前几行,以确保一切看起来都是正确的。
太好了!现在让我们创建数据库。
创建 SQLite 数据库
有多种关系数据库产品可用,这里我们将使用的具体产品是 SQLite 。这是一个轻量级的 SQL 数据库引擎,可用于在个人计算机上创建存储为文件的数据库。我们可以启动一个新的 SQLite 数据库连接对象,并将该对象赋给一个变量。下面,我把这个变量命名为db_conn
。
db_conn = sqlite3.connect("data/superstore.db")
当执行上述代码时,将在数据目录中创建一个名为 superstore.db 的新文件(假设 superstore.db 在该目录中尚不存在)。新创建的 superstore.db 文件此时是一个空的 SQLite 数据库(即,它没有表)。db_conn
也指向 superstore.db 数据库,可以认为是使用这个数据库的接口。酷!但是等等…我们实际上如何运行创建数据表的 SQL 代码呢?很高兴你问了。我们现在需要建立一个 游标 对象,它是一个针对感兴趣的数据库执行 SQL 代码的工具。这里,光标被分配给一个名为c
的变量。
c = db_conn.cursor()
下一步是创建将包含在数据库中的表,尽管需要强调的是,创建表的下一步将导致空表。稍后,我们将用之前创建的四个数据框中的数据填充我们的表。然而,在我们继续之前,让我们看一下将用于创建销售表的实际 SQL 代码,以便更好地理解表的创建过程。
上面代码中的第 1 行提供了创建名为 sales 的表的命令,第 2–8 行在 sales 中创建了 7 个新列:salesID、OrderID、ProductID、Sales、Quantity、Discount 和 Profit。每列的数据类型在相应列名的右侧指定。关于 SQLite 数据类型的更多信息可以在这里找到。请注意,列的顺序与关联数据框中列的顺序相匹配。
CREATE TABLE
语句中的列顺序是有意安排的,因为这确保了数据框中的适当值进入数据库表中的预期列。例如,如果我将 OrderID 作为第一列,将 SalesID 作为第二列,那么 SalesID 值将被写入到 sales 表中的 OrderID 列,并且该表中的 SalesID 列将包含 OrderID 值。
第 9 行建立了表的主键,这是一个包含唯一标识每行的值的列。在销售表中,SalesID 满足主键的要求,因为没有两行具有相同的 SalesID 值。
“销售”表的快照
上一组代码中的第 10 行为销售建立了两个外键中的第一个。外键是一个表中的列,它是另一个表中的主键。例如,请注意,在销售表中,不同的行可以共享相同的 OrderID 值,这使得 OrderID 不再是该表中的主键。然而,订单表中的每一行确实包含一个唯一的 OrderID 值。因此,OrderID 可以作为订单的主键。
“订单”表的快照
让我们回到前面的CREATE TABLE
语句中第 10 行的实际代码。代码的第一部分FOREIGN KEY(OrderID)
确定 sales 中的 OrderID 列是一个外键。第二部分,REFERENCE orders(OrderID)
指定 OrderID 引用的表和主键。CREATE TABLE
语句的第 11 行遵循相同的 ProductID 列逻辑。
通过指定主键和外键,我们能够创建一个“映射”,显示数据库中的表是如何相互关联的。例如,熟悉 SQL 概念但不熟悉该特定数据的人可以查看创建销售表的CREATE TABLE
语句,并认识到通过匹配两个表的 OrderID 列中的值,可以将销售和订单表中的数据放在一起,或者将 连接到 。为了简要说明联接是如何工作的,考虑我们如何通过在 OrderID 上“联接”来将 OrderDate 添加到 sales 表中,如下所示。
联接如何工作的基本视觉演示
正在完成数据库
建立了表创建过程的基本概述后,让我们创建所有的数据库表。这是我们放置光标的地方,c
开始工作。我们可以运行c.execute()
并将所需的 SQL 代码作为字符串包含在括号中,以针对我们当前连接的数据库运行所述 SQL 代码(即data/superstore.db
)。
现在是时候用我们之前创建的四个数据框中的相关数据填充数据库中的表了。幸运的是,在每个数据帧上使用pandas’to_sql
可以轻松完成这一步(更多细节见to_sql
这里)。下面块中的代码将数据从四个数据框中的每一个传输到数据库中适当的表中。
上面每一行代码中的第一个参数标识数据框中的值将被写入的数据库表的名称,后面的参数指定实际的数据库连接。接下来,if_exists='append'
告诉to_sql
如果表已经存在(在本例中确实如此),那么应该将数据框中的值插入到表中。最后,通过index=False
,我们告诉to_sql
不要将索引作为附加列包含在表中。
结论
现在,我们已经有了一个准备就绪的 SQLite 数据库!可以用 SQLite 命令行 shell 或者其他支持 SQLite 的数据库软件,比如 DbVisualizer 来查询 superstore.db 数据库文件。您还可以使用 pandas 中的read_sql
在数据库上运行查询(参见更多信息此处)。例如,如果我们希望看到来自 sales 的前五行,我们可以在我们的 Python shell 或笔记本中运行如下所示的代码。
*pd.read_sql("SELECT * FROM sales LIMIT 5", db_conn)*
当您用 Python 处理完这个数据库后,您可以通过运行db_conn.close()
来关闭连接。如果您想在关闭后再次使用该数据库,只需重新建立数据库连接
*db_conn = sqlite3.connect("data/superstore.db")*
并使用read_sql
运行查询。如果不将数据写入数据库,则不需要建立新的游标。
在我们结束之前,有一句话需要澄清。在这篇文章的开始,我提到了 Excel 工作簿的结构类似于 SQL 数据库,因为两者都可以由多个相关的表组成。为此,本练*旨在演示如何将 Excel 工作簿轻松转换为 SQLite 数据库文件。尽管如此,我们只是触及了数据库设计的表面,我想强调的是,一个设计良好的数据库不仅仅是表的集合。的确, 规范化的过程在关系数据库设计中非常重要,其目的是消除数据冗余,促进数据完整性。请注意,我们创建的数据库可以进一步规范化。如果您有兴趣了解更多关于关系数据库和跨多个表存储数据背后的基本原理,我推荐您阅读更多关于规范化的内容。
好吧,这将是这个职位。感谢您的阅读,如果您有任何问题和/或建设性反馈,请联系我们。
把你的爱好数据分析项目变成一个投资组合
让人力资源经理喜欢你的简历!尤利娅·卢卡希娜
如果你之前的工作与数据科学技能没有什么关系,你仍然可以通过在线主持你的爱好项目来说服未来的雇主。
我来到研讨室,在那里我们有安装了 SPSS 的电脑,世界消失了几个小时。我以为生活可以永远这样。我相信我已经属于数据科学社区了。
但后来我知道我错过了一堆数据科学家应该知道的事情。我仍然从事着一份资质一般的数据分析师工作,因为我下定决心要获得缺失的技能。
两个在线学*平台的结业证书是不够的。在德国,数据科学行业仍在兴起,而且还不稳定。
我必须拿出看得见的证据来证明我可以做数据科学。我把我的爱好学*项目放到网上,公之于众,并把链接附在我的求职信和简历上。
这引起了一些雇主的兴趣,尽管几乎没有人真正使用我发布的公共平台。
这只是让我在展示之前稍微润色了一下我的结果。例如,给图表添加更多的注释。绘制更多图表。在求职信中简单解释一下,这个平台是怎么回事。
卡格尔
我使用 Kaggle 作为投资组合和免费的云计算机器。
这个项目是以减价的形式进行的。它对数据进行了详细的概述,并解释了为什么对这些数据感兴趣。
报告从原始数据的一个例子开始,有前十行。然后,它列出了面临的挑战,并清楚地表明精心的数据清理是必要的。
下一部分由几个代码块组成,每个代码块都做一些数据整理工作。代码前面是注释,然后是简短的输出,接着是数据样本。
在数据变得可靠之后,描述性的部分开始了。通常,我也会进行一些分组和简单的计算,然后用另一个数据样本来突出原始数据和处理后数据之间的差异。
脚本中的最后一个命令通常是保存 CSV 文件的命令,我后来用它来制作 Tableau 可视化效果。
描述应该有多详细
作为一名博士生,我经常遇到数据可视化或其他呈现结果的方式,如平面表格,它们并不总是自我解释的。
因此,当我自己写报告时,我倾向于过多地关注细节。我使用 Markdown 格式来比较每个数据处理步骤和每个自定义函数的输入和输出。
高水平的细节可以帮助你吸引不是数据科学家的人的注意。例如,想想人力资源经理。即使你的报告对一个非专业人士来说仍然过于“科学”,你的读者也会感谢你关心他的理解。
我特别推荐解释每个自定义函数。写一行注释来启发你对 if 语句的选择也是很好的。你需要弄清楚,你想解决什么样的问题。
如果你不提供至少一个简短的句子,任何超出简单过滤的转换都可能会使读者困惑。由于你有时有很多选择来处理你的数据以达到你的目标,如果你解释了你的决定,你会得到一个额外的分数。
Tableau 公共
尽管我的简历中有一个 Tableau 记录,但我更喜欢把我的公开资料链接发给雇主。我想尽我所能向他们展示,但也想让他们知道我对雄心勃勃的数据即项目感兴趣。
对于不熟悉这个工具的人来说,有时并不清楚 Tableau 不仅仅是 Excel 图表更好的替代品。
我创建了一个 Tableau 在线演示,并在面试中浏览了一遍(当然,如果有人问我的话)。我解释了我的研究问题,以及我将通过可视化实现的目标。基本上,Tableau 仪表板是一种交互式报告,允许用户关注特定的信息或特定的视角。
开源代码库
在我的一个数据科学爱好项目中,我想到了编写一个简单的单功能 Python 应用程序的想法。它在我的笔记本电脑上运行,应该是为手机发布的。反正在这段编程经历中,我学会了用 Visual Studio Code 2,免费的那个。我把它和一个 GitHub 库连接起来。当我必须从下载切换到 API 调用时,我创建了第二个分支。
了解 GitHub 并与分支机构合并和合作对于 IT 团队来说至关重要。
另一个好处是,你可以把它公之于众,给你潜在的雇主看。
社交机会
总的来说,所有这些平台——ka ggle、Tableau Public、GitHub——都提供了社交机会。我对此并不积极,但我确实检查了人们在 Kaggle 或 Tableau 上做的项目。我不时得到一些观点和喜欢,或者看到有人开始跟踪我。
你可以加入其中一个论坛讨论,询问任何技术问题或分享你对找工作的想法。
你简历的另一个加分点是分享解决方案、自定义功能或变通方法的社区帖子。也许你甚至会发布一个新的包,谁知道呢!
利用每一个机会给你未来的雇主一个惊喜,不要等着他们用谷歌搜索你的名字,只找到你在脸书的照片。你拥有的不止这些。
使用 Python 和 OpenCV 让自己隐形
将一个人从背景中移除的确是一项有趣又尴尬的任务。在本指南中,我想一步一步地展示如何使用 OpenCV 和 Python 从直播流中删除一个人。
照片由阿索格蒂在 unsplash.com 拍摄
几天前,在浏览黑客新闻时,我对谷歌工程师杰森·梅斯的T3 项目印象深刻。从我第一次看到这个项目的那一刻起,我就决定一定要(至少尝试)复制它。然而,由于我的随身工具主要是 R 和 Python,所以我决定用后者来实现它。
你需要什么:
- Python 3.x.x(我用的是 Python 3.7.4)
- OpenCV(我使用的是版本 4.1.2)
本项目中描述的不同阶段的分解。
头脑风暴:
在做了一些头脑风暴后,我意识到,要从稳定的图像中删除一个对象,我很可能需要一个锚点作为起点,然后复制粘贴每一帧,就像一个遮罩一样,应用于包含一个人的每一个后续帧。
然后我想,如果我有我想要隐藏的区域的坐标,我可以简单地从锚帧复制它,并在我想要隐藏对象的当前帧上替换它。
一个我被检测到的帧的例子。
要解决的第二个问题是找到一种方法来检测我想要移除的感兴趣的对象。幸运的是,OpenCV 提供了一种简单的方法:基于支持向量机的梯度方向直方图检测器。这是一个必去的人探测器,不是最快的,不是最准确的,也不是最好的,但它就是工作。
工作流程:
因此,在头脑风暴之后,我决定坚持以下工作流程:
- 实例化
HOGDescriptor
- 获取视频的第一帧以用作遮罩
- 迭代每一帧,对于每个检测到的人,用第一帧中相应的“空”区域替换该区域
- 保存输出
代码:
按照之前描述的工作流程,我在我的 GitHub 库下找到了下面的代码。
来测试一下吧!
像老板一样。把手放在口袋里,然后消失!
然而,引用埃隆·马斯克的话:
“仍有改进的空间”
实际上没有这么精确,尤其是当我靠近镜头的时候。
测试完这段代码后,我意识到我可能是在正确的道路上,但是,检测不够精确,整个输出看起来有问题且不稳定。
所以我意识到我需要找到一种方法来改进它:用第一帧替换每个检测到的人似乎是一个好方法,所以我可能需要找到一种更好的方法来检测物体,一个更好的模型!
我记得不久前我写了一篇简短指南关于如何开始使用脸书的 Detectron2 模型,那么为什么不实现它而不是 HOG 检测器呢?
改进之处:
在 COCO 的模型动物园上搜索我发现了一个实例分割模型,每张图片的推理时间为 0.07 秒,这是最快的可用模型之一(可能不是最准确的)。然后我决定使用它。
要在我的管道中插入一个定制模型,必须安装所有必需的依赖项,如pytorch
、torchvision
和detectron2
:
# install dependencies:!pip install -U torch==1.4+cu100 torchvision==0.5+cu100 -f [https://download.pytorch.org/whl/torch_stable.html](https://download.pytorch.org/whl/torch_stable.html)!pip install cython pyyaml==5.1!pip install -U ‘git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'import torch, torchvision
以下代码和指令是在 Google Colab 实例上测试的,做出这一选择是为了让这个实验更容易复制,而不会有缺少依赖、版本冲突和所有经常发生的无聊事情。
然后我们需要安装检测器 2:
# install detectron2:!git clone [https://github.com/facebookresearch/detectron2](https://github.com/facebookresearch/detectron2) detectron2_repo!pip install -e detectron2_repo
现在我们可以导入所有需要的库并加载模型(完整代码请参考 Google Colab 笔记本或 GitHub repo ):
cfg = get_cfg()cfg.merge_from_file(model_zoo.get_config_file(“COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_1x.yaml”))cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5 # set threshold for this modelcfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url(“COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_1x.yaml”)predictor = DefaultPredictor(cfg)
不可能使用我们的predictor
来推断类,predictor
返回一个需要转换成numpy
数组的张量数组,然后可以像我之前做的一样迭代它:
outputs = predictor(frame)outputs = outputs[“instances”].pred_boxes
.to(‘cpu’)
.tensor
.numpy()
.astype(int)
让我们来看看最终结果:
探测器 2 与全息探测器
从 gif 中可以观察到 Detectron2 如何更准确地检测到一个人,然而,需要指出的是,当然,它需要一些更“深入”的配置(依赖关系有时是一种斗争)。但是,最后的结果自己说话!
几个最后的考虑:
尽管最终获得的结果相当令人惊讶,但总有改进的余地。仔细观察我身后的滑雪者,可以发现当我从他们面前经过时,面具使他们自己消失了(这种现象被称为遮挡)。此外,当前帧上的“切割”矩形之间的插值非常明显。为了解决这两个问题,能够执行全景分割的模型可能会有所帮助。
最后的对比视频:
**I have a newsletter 📩.**Every week I’ll send you a brief findings of articles, links, tutorials, and cool things that caught my attention. If tis sounds cool to you subscribe.*That means* ***a lot*** *for me.*
[## 米尔斯形式
编辑描述
无情-创造者-2481.ck.page](https://relentless-creator-2481.ck.page/68d9def351)
将位置数据转化为地理信息
纽约星巴克的位置、人口密度和家庭收入
丹尼尔·冯·阿彭在 Unsplash拍摄的照片
介绍
如果你搜索“我附近的杂货店”,谷歌地图会显示附近商店的位置。谷歌地图使用位置数据在地图上显示附近的商店。这些数据可能包含其他信息,如商店详细信息、地址、电话号码,但地理位置本身是关键。位置数据的使用有很多原因-就像查找杂货店一样简单,但也可以用于更复杂的决策,例如在哪里新开一家餐馆。
这篇文章是对上一篇关于点模式分析的文章的补充,目的是演示一些人可以用位置数据做的基本事情。没有太多复杂的分析,但我将重点关注如何在地图上显示位置数据,并解释这些数据点对于周围和潜在变量(如人口和收入)的意义。
“我附近的杂货店”的谷歌地图搜索结果
星巴克选址:案例研究
在这个案例研究中,我使用的是星巴克的位置数据。这些数据有点过时了,但我怀疑这些年来地点有很*变化。
此外,我使用县级人口普查数据中的一些变量来理解可能与星巴克位置相关的潜在过程:
- 家庭收入
- 总人口
- 人口密度
目的是看这些变量中的哪一个与星巴克店的位置相关(没有暗示相关性)。我们没有进行任何花哨的地质统计分析,只是在数据层上叠加数据层,并观察它们以发现是否有任何有趣的模式出现。
位置与底层流程的关联
为了映射位置数据和人口普查变量,我用 r 实现了这个练*。Python 有很好的库来做这种工作,但是我想利用tidycensus
包来处理人口普查数据。首先,我们需要安装一些库。
# To access census variables and polygons
library(tidycensus)# To work with spatial polygons
library(sf)# To work with the dataframe associated with the spatial data
library(tidyverse)# To make maps
library(mapview)
要使用人口普查局 API 访问数据,您需要一个 API 密钥。这是一个简单的过程,只需去人口普查局网站申请一个 API 密匙,并把它发送到你的邮箱。一旦有了密钥,就将它加载到您的环境中。
# load API key
census_api_key("YOUR API KEY")
现在我们可以加载星巴克的位置数据了。该数据集包含全国范围的星巴克位置,您可以过滤您感兴趣的任何州/地理位置。我在过滤纽约州。
# load data
data = read_csv("[https://raw.githubusercontent.com/libjohn/mapping-with-R/master/data/All_Starbucks_Locations_in_the_US_-_Map.csv](https://raw.githubusercontent.com/libjohn/mapping-with-R/master/data/All_Starbucks_Locations_in_the_US_-_Map.csv)")# filter Starbucks locations in New York State
df = filter(data, State == "NY")# plot the locations
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE, legend = FALSE, alpha = 0.5, cex = 1)
纽约的星巴克店
下一步是通过感兴趣的变量的tidycensus
从人口普查局导入人口普查数据。我正在获取家庭收入和人口变量。
让我们先来看看纽约州的家庭收入是如何分布的。
# get income data via `tidycensus``
income = get_acs(geography = "county", variables = "B19013_001", state = "NY", geometry = TRUE)
纽约州各县的家庭收入分布情况
毫不奇怪,一些收入最高的家庭位于纽约市附近的县。我会放*那个区域。
但是让我们先得到其他变量。
# get population data via `tidycensus``
pop = get_acs(geography = "county", variables = "B01003_001", state = "NY", geometry = TRUE)# add population density
dens = as_Spatial(pop) # convert `sf` object (pop) to SpatialPolygonDataFrame # add area column
dens$Area_sqmile <- area(dens)/2.59e+6# add population density columns
dens$popdensity = dens$estimate/dens$Area_sqmile
绘图和解释
首先,让我们将星巴克的位置放在收入数据的顶部,看看高收入的县是否有*量的星巴克门店。我再一次放*了纽约市的区域。
# map starbucks on income
mapview(income, zcol = "estimate") +
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE,legend = FALSE, alpha = 0.5, cex = 1)
纽约市各县(多边形)和星巴克所在地(圆点)的家庭收入
所以*多数星巴克店都位于曼哈顿地区。但这并不是家庭收入最高的地方,收入最高的家庭在拿骚县(黄色多边形)。我们可以推断,星巴克的门店不一定位于富人区,意味着存在一种微弱的(如果有的话)关联。
现在让我们试一下总人口,看看人口多的县是否有很多星巴克店。
# map starbucks on population
mapview(pop, zcol = "estimate") +
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE,legend = FALSE, alpha = 0.5, cex = 1)
纽约市各县(多边形)和星巴克所在地(圆点)周围的总人口
就人口而言,金斯县(黄色多边形)和皇后县是纽约市人口最多的县,但这也不是*多数星巴克店的所在地。这意味着,像收入一样,总人口似乎也是星巴克门店位置的一个弱预测因素。
最后,我们来看看人口密度是否有关系。
# map starbucks on population density
mapview(dens, zcol = "popdensity") +
mapview(df, xcol = "Longitude", ycol = "Latitude", crs = 4269, grid = FALSE,legend = FALSE, alpha = 0.5, cex = 1)
纽约市各县(多边形)和星巴克所在地(圆点)周围的人口密度
这就对了。
人口密度最高的是曼哈顿地区,那里也是星巴克店最多的地方。因此,地理位置和人口密度之间似乎存在某种联系,这种联系强于各县的总人口或家庭收入。
结论
从这些关联分析中,我们发现人口密度是一个更好的预测因素,可以预测附近是否有星巴克,而收入则不是一个因素。然而,作出这样的概括是相当简单的,因为还有我没有考虑的其他因素,如人口的年龄,*学生的数量,种族等等。此外,我们还可以测试这种关联在美国的其他州和地区是否成立。
在本例中,您学*了如何绘制位置数据和使用人口普查地图。你还学会了如何提出问题,测试一个假设,甚至产生一个新的假设。在以后的文章中,我将进一步分析点数据,敬请关注。
教程:为数据科学构建您自己的*数据基础架构
Justin Jairam 拍摄的照片来自 @jusspreme
从事自己的数据科学项目是学*新技能和磨练现有技能的绝佳机会,但如果您想使用 Hadoop、Spark on a distributed cluster、Hive 等行业中使用的技术,该怎么办呢?并把它们整合在一起。在构建自己的基础设施时,这就是价值的来源。
*数据格局
您熟悉了这些技术,了解了它如何运行的细节,调试并体验了不同类型的错误消息,真正了解了技术的整体工作原理,而不仅仅是与它进行交互。如果您也在处理自己的私有数据或机密数据,出于隐私或安全原因,您可能不希望将其上传到外部服务来进行*数据处理。因此,在本教程中,我将介绍如何在自己的计算机、家庭实验室等设备上设置自己的*数据基础架构。我们将设置一个单节点 Hadoop & Hive 实例和一个与 Jupyter 集成的“分布式”spark 集群。
编辑:多亏了 @Daniel Villanueva 你现在可以部署一个预先配置了 Hadoop、Spark 和 Hive 的虚拟机,并准备好通过他的流浪映像。你可以在他的 Github 这里查看。
本教程不适用于工业生产安装!
先决条件
- 基于 Debian 的发行版——Ubuntu,Pop-os 等
- 基本的命令行知识会有所帮助,但对于安装来说不是必需的
步骤 1 —下载 Hadoop 和 Hive
Hadoop 无疑是当今行业中最常用的*数据仓库平台,是任何*数据工作的必备知识。简而言之,Hadoop 是一个开源软件框架,用于以分布式方式存储和处理*数据。你可以从这里下载最新版本。
Hive 通常添加在 Hadoop 之上,以类似 SQL 的方式查询 Hadoop 中的数据。Hive 使作业易于执行操作,如
- 数据封装
- 即席查询
- *型数据集的分析
Hive 速度较慢,通常仅用于批处理作业。一个更快的 Hive 版本可能类似于 Impala,但对于家庭使用来说,它可以完成任务。你可以在这里下载最新版本的 Hive。
确保您下载的是二进制(bin)版本,而不是源代码(src)版本!
将文件解压缩到/opt
cd ~/Downloads
tar -C /opt -xzvf apache-hive-3.1.2-bin.tar.gz
tar -C /opt -xzvf hadoop-3.1.3.tar.gz
将它们重命名为hive
和hadoop
。
cd /opt
mv hadoop-3.1.3 hadoop
mv apache-hive-3.1.2-bin hive
步骤 2 —设置授权(或无密码)SSH。
我们为什么需要这样做?Hadoop 核心使用 Shell (SSH)在从属节点上启动服务器进程。它要求主节点和所有连接节点之间的无密码 SSH 连接。否则,您必须手动转到每个节点并启动每个 Hadoop 进程。
由于我们运行的是 Hadoop 的本地实例,我们可以省去设置主机名、SSH 密钥以及将它们添加到每个机器中的麻烦。如果这是一个分布式环境,最好也创建一个hadoop
用户,但是对于单个节点设置和个人使用来说,这是不必要的。
真正简单的,只适合在家里使用,不应该在其他地方使用或完成的方式是:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
现在运行ssh localhost
,你应该可以不用密码登录了。
无密码 SSH 登录
为了了解在分布式环境中设置网络和 SSH 配置需要做些什么,您可以阅读这个。
步骤 3 —安装 Java 8
本教程最重要的步骤之一。
如果做得不正确,将会导致耗费*量时间调试模糊的错误消息,只是为了意识到问题和解决方案是如此简单。
Hadoop 有一个主要需求,这就是 Java 版本 8。有趣的是,这也是 Spark 的 Java 需求,也非常重要。
sudo apt-get update
sudo apt-get install openjdk-8-jdk
验证 Java 版本。
java -version
Java 版本
如果由于某种原因,您没有看到上面的输出,您需要更新您的默认 Java 版本。
sudo update-alternatives --config java
更新 Java 版本
选择与 Java 8 相关的数字。
再次检查版本。
java -version
正确的 Java 版本
步骤 4 —配置 Hadoop + Yarn
Apache Hadoop YARN(又一个资源协商器)是一种集群管理技术。在非常基本的层面上,它帮助 Hadoop 管理和监控其工作负载。
初始 Hadoop 设置
首先让我们设置我们的环境变量。这将告诉其他组件每个组件的配置位于何处。
nano ~/.bashrc
将此添加到您的.bashrc
文件的底部。
export HADOOP_HOME=/opt/hadoop
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export PATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/binexport LD_LIBRARY_PATH=$HADOOP_HOME/lib/native:$LD_LIBRARY_PATHexport HIVE_HOME=/opt/hive
export PATH=$PATH:$HIVE_HOME/bin
保存并退出 nano CTRL + o
,CTRL + x
。
然后我们需要通过运行source ~/.bashrc
来激活这些更改。您也可以关闭并重新打开您的终端来达到同样的效果。
接下来,我们需要制作一些目录和编辑权限。创建以下目录:
sudo mkdir -p /app/hadoop/tmp
mkdir -p ~/hdfs/namenode
mkdir ~/hdfs/datanode
编辑/app/hadoop/tmp
的权限,授予其读写权限。
sudo chown -R $USER:$USER /app
chmod a+rw -R /app
配置文件
所有的 Hadoop 配置文件都位于/opt/hadoop/etc/hadoop/
中。
cd /opt/hadoop/etc/hadoop
接下来,我们需要编辑以下配置文件:
- core-site.xml
- hadoop-env.sh
- hdfs-site.xml
- mapred-site.xml
- yarn-site.xml
core-site.xml
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>/app/hadoop/tmp</value>
<description>Parent directory for other temporary directories.</description>
</property>
<property>
<name>fs.defaultFS </name>
<value>hdfs://YOUR_IP:9000</value>
<description>The name of the default file system. </description>
</property>
</configuration>
hadoop.tmp.dir
:不言自明,只是一个 hadoop 用来存储其他临时目录的目录fs.defaultFS
:你的文件系统通过网络访问的 IP 和端口。如果这是一个分布式系统,它应该是您的 IP,以便其他节点可以连接到它。
要查找您的 ip,请在命令行中键入ip addr
或ifconfig
:
hadoop-env.sh
- 确定 Java 8 JDK 的位置,它应该与
/usr/lib/jvm/java-8-openjdk-amd64/
相似或相同 - 在
hadoop-env.sh
中添加以下一行:export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
hdfs-site.xml
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
<description>Default block replication.</description>
</property>
<property>
<name>dfs.name.dir</name>
<value>file:///home/YOUR_USER/hdfs/namenode</value>
</property>
<property>
<name>dfs.data.dir</name>
<value>file:///home/YOUR_USER/hdfs/datanode</value>
</property>
</configuration>
dfs.replication
:在多少个节点上复制数据。
dfs.name.dir
:NameNode 块的目录
dfs.data.dir
:数据节点块的目录
mapred-site.xml
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobtracker.address</name>
<value>localhost:54311</value>
</property>
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=$HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>mapreduce.map.memory.mb</name>
<value>4096</value>
</property>
<property>
<name>mapreduce.reduce.memory.mb</name>
<value>4096</value>
</property>
</configuration>
mapreduce.framework.name
:执行 MapReduce 作业的运行时框架。可以是本地、经典或纱线。
mapreduce.jobtracker.address
:MapReduce 作业跟踪器运行的主机和端口。如果是“本地”,则作业作为单个映射和简化任务在进程中运行。
yarn.app.mapreduce.am.env
:纱线图减少环境变量。
mapreduce.map.env
:贴图减少贴图环境变量。
mapreduce.reduce.env
:贴图减少减少环境变量。
mapreduce.map.memory.mb
:Hadoop 允许分配给映射器的内存上限,以兆字节为单位。默认值为 512。
mapreduce.reduce.memory.mb
:Hadoop 允许分配给 reducer 的内存上限,以兆字节为单位。默认值为 512。
yarn-site.xml
<configuration>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>localhost</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>16256</value>
</property>
<property>
<name>yarn.app.mapreduce.am.resource.mb</name>
<value>4096</value>
</property>
<property>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>4096</value>
</property>
</configuration>
yarn.resourcemanager.hostname
:RM 的主机名。也可能是远程 yarn 实例的 ip 地址。
yarn.nodemanager.aux-services
:选择运行 MapReduce 需要设置的随机播放服务。
yarn.nodemanager.resource.memory-mb
:可以分配给容器的物理内存量,以 MB 为单位。作为参考,我的机器有 64GB 的内存。如果这个值太低,您将无法处理*文件,得到一个FileSegmentManagedBuffer
错误。
yarn.app.mapreduce.am.resource.mb
:该属性指定为特定作业选择资源标准。任何具有相同或更多可用内存的节点管理器都将被选择来执行作业。
yarn.scheduler.minimum-allocation-mb
:RM 上每个容器请求的最小分配量,单位为 MBs。低于这个值的内存请求将不会生效,指定的值将被分配为最小值。
启动 Hadoop
在开始 Hadoop 之前,我们必须格式化 namenode:
hdfs namenode -format
现在我们可以开始 Hadoop 了!运行以下命令:
start-dfs.sh
start-yarn.sh
要确保一切都已启动,请运行以下命令:
ss -ln | grep 9000
端口 9000 网络信息
运行jps
运行 Java 程序
您现在还可以在 http://localhost:9870 访问 Hadoop web UI。
Hadoop Web 用户界面
您也可以在 localhost:8088 访问 Yarn web UI。
纱网用户界面
步骤 5 —设置配置单元
既然我们已经启动并运行了 Hadoop,让我们在它的基础上安装 Hive。
首先,让我们在 Hadoop 中创建一个目录,我们的 Hive 表将存储在这个目录中。
hdfs dfs -mkdir -p /user/hive/warehouse
配置权限。
hdfs dfs -chmod -R a+rw /user/hive
设置 Metastore
配置单元 Metastore 是配置单元元数据的中央存储库。它存储了配置单元表和关系(模式和位置等)的元数据。它通过使用 metastore 服务 API 提供对此信息的客户端访问。有 3 种不同类型的元存储:
- 嵌入式 Metastore:一次只能打开一个配置单元会话。
- 本地 Metastore:多个配置单元会话,必须连接到外部数据库。
- 远程 metastore:多个 Hive 会话,使用 Thrift API 与 Metastore 交互,更好的安全性和可扩展性。
为了更详细地了解每种类型的 metastore 之间的区别,这是一个很好的链接。
在本指南中,我们将使用 MySQL 数据库设置一个远程 metastore。
sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation
运行以下命令:
sudo mysqlCREATE DATABASE metastore;
CREATE USER 'hive'@'%' IDENTIFIED BY 'PW_FOR_HIVE';
GRANT ALL ON metastore.* TO 'hive'@'%' WITH GRANT OPTION;
在 MySQL 中将PW_FOR_HIVE
替换为您想要用于 hive 用户的密码。
下载 MySQL Java 连接器:
wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.19.tar.gz
tar -xzvf mysql-connector-java-8.0.19.tar.gz
cd mysql-connect-java-8.0.19
cp mysql-connector-java-8.0.19.jar /opt/hive/lib/
编辑 hive-site.xml
现在编辑/opt/hive/conf/hive-site.xml
:
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://YOUR_IP:3306/metastore?createDatabaseIfNotExist=true&useLegacyDatetimeCode=false&serverTimezone=UTC</value>
<description>metadata is stored in a MySQL server</description>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
<description>MySQL JDBC driver class</description>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hive</value>
<description>user name for connecting to mysql server</description>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>PW_FOR_HIVE</value>
<description>password for connecting to mysql server</description>
</property>
</configuration>
用本地 ip 地址替换YOUR_IP
。用您之前为 hive 用户初始化的密码替换PW_FOR_HIVE
。
初始化架构
现在让我们让 MySQL 可以从网络上的任何地方访问。
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
将bind-address
改为0.0.0.0
。
重新启动服务以使更改生效:sudo systemctl restart mysql.service
最后,运行schematool -dbType mysql -initSchema
来初始化 metastore 数据库中的模式。
启动配置单元 Metastore
hive --service metastore
测试蜂箱
首先通过调用hive
从命令行启动 Hive。
让我们创建一个测试表:
CREATE TABLE IF NOT EXISTS test_table
(col1 int COMMENT 'Integer Column',
col2 string COMMENT 'String Column')
COMMENT 'This is test table'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE;
然后插入一些测试数据。
INSERT INTO test_table VALUES(1,'testing');
然后我们可以查看表中的数据。
SELECT * FROM test_table;
步骤 6 —设置火花
Spark 是一个通用的分布式数据处理引擎,适用于各种环境。在 Spark 核心数据处理引擎之上,有 SQL、机器学*、图形计算和流处理的库,它们可以在应用程序中一起使用。在本教程中,我们将使用 Docker 设置一个独立的 Spark 集群,并让它能够启动任意数量的工作线程。这背后的原因是我们想要模拟一个远程集群以及它所需的一些配置。
在生产环境中,Spark 通常被配置为使用 Yarn 和已经分配给 Hadoop 的资源。
首先,我们需要创建 Docker 文件。在本教程中,我们将使用 Spark 版本 2.4.4,但如果您想要最新版本,可以将其更改为 2.4.5,它还附带了 Hadoop 2.7 来管理节点之间的持久性和簿记。在生产设置中,spark 通常配置有 Yarn,以使用现有的 Hadoop 环境和资源,因为我们只有一个节点上的 Hadoop,我们将运行 Spark 独立集群。将 Spark 配置为与纱线一起运行只需要很少的改动,您可以在这里看到设置的不同。
设置独立集群
nano Dockerfile
# DockerfileFROM python:3.7-alpineARG SPARK_VERSION=2.4.4
ARG HADOOP_VERSION=2.7RUN wget -q https://archive.apache.org/dist/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz \
&& tar xzf spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz -C / \
&& rm spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz \
&& ln -s /spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION} /sparkRUN apk add shell coreutils procps
RUN apk fetch openjdk8
RUN apk add openjdk8
RUN pip3 install ipythonENV PYSPARK_DRIVER_PYTHON ipython
现在,我们要建立一个星火*师和 N 个星火工作者。为此,我们将使用 docker-compose。
nano docker-compose.yml
version: "3.3"
networks:
spark-network:
services:
spark-master:
build: .
container_name: spark-master
hostname: spark-master
command: >
/bin/sh -c '
/spark/sbin/start-master.sh
&& tail -f /spark/logs/*'
ports:
- 8080:8080
- 7077:7077
networks:
- spark-network
spark-worker:
build: .
depends_on:
- spark-master
command: >
/bin/sh -c '
/spark/sbin/start-slave.sh $$SPARK_MASTER
&& tail -f /spark/logs/*'
env_file:
- spark-worker.env
environment:
- SPARK_MASTER=spark://spark-master:7077
- SPARK_WORKER_WEBUI_PORT=8080
ports:
- 8080
networks:
- spark-network
对于主容器,我们公开了端口 7077 供应用程序连接,以及端口 8080 供 Spark 作业 UI 连接。对于 worker,我们通过环境变量连接到我们的 Spark master。
对于配置 spark worker 的更多选项,我们将它们添加到spark-worker.env
文件中。
nano spark-worker.env
SPARK_WORKER_CORES=3
SPARK_WORKER_MEMORY=8G
在此配置中,每个工作人员将使用 3 个内核和 8GB 内存。由于我的机器有 6 个内核,我们将启动 2 个工人。更改这些值,使其相对于您的机器。例如,如果您的机器只有 16GB 的 RAM,那么一个合适的内存值可能是 2gb 或 4GB。关于环境变量的完整列表和关于独立模式的更多信息,你可以在这里阅读完整的文档。如果你想知道 executor 的内存,那是在提交或启动应用程序时设置的。
docker-compose build
docker-compose up -d --scale spark-worker=2
现在 spark 已经启动并运行,您可以在 localhost:8080 查看 web UI!
Spark Web 用户界面
本地安装 Spark
在您的本地机器上,或者任何将要创建或使用 Spark 的机器上,都需要安装 Spark。因为我们正在设置一个远程 Spark 集群,所以我们从源头上安装它。在本教程中,我们将使用 PySpark,因为我*部分时间在我的个人项目中使用 Python。
你可以从这里下载 Spark。
确保您下载的版本与您在主服务器上安装的版本相同。对于本教程,它的版本是 2.4.4
wget https://archive.apache.org/dist/spark/spark-2.4.4/spark-2.4.4-bin-hadoop2.7.tgz
tar -C /opt -xzvf spark-2.4.4-bin-hadoop2.7.tgz
设置 Spark 环境变量,nano ~/.bashrc
export SPARK_HOME=/opt/spark
export PATH=$SPARK_HOME/bin:$PATHexport PYSPARK_DRIVER_PYTHON="jupyter"
export PYSPARK_DRIVER_PYTHON_OPTS="notebook"
export PYSPARK_PYTHON=python3
如果您更喜欢 Jupyter Lab,请将 PYSPARK_DRIVER_PYTHON_OPTS 的“笔记本”更改为“实验室”。
配置文件
要配置 Spark 来使用我们的 Hadoop 和 Hive,我们需要在 Spark config 文件夹中有两者的配置文件。
cp $HADOOP_HOME/etc/hadoop/core-site.xml /opt/spark/conf/
cp $HADOOP_HOME/etc/hadoop/hdfs-site.xml /opt/spark/conf/
nano /opt/spark/conf/hive-site.xml
<configuration>
<property>
<name>hive.metastore.uris</name>
<value>thrift://YOUR_IP:9083</value>
</property>
<property>
<name>spark.sql.warehouse.dir</name>
<value>hdfs://YOUR_IP:9000/user/hive/warehouse</value>
</property>
</configuration>
hive.metastore.uris
:告诉 Spark 使用 Thrift API 与 Hive metastore 交互。spark.sql.warehouse.dir
:告诉 Spark 我们的蜂巢桌在 HDFS 的位置。
安装 PySpark
或者用安装在 spark master 上的任何版本替换 2.4.4。
要运行 PySpark 连接到我们的分布式集群运行:
pyspark --master spark://localhost:7077
,您也可以用您的 ip 或远程 ip 替换localhost
。
这将启动一个带有预定义的 Spark 上下文的 Jupyter 笔记本。因此,我们现在有一个单一的环境来分析有或没有 Spark 的数据。
默认情况下,executor 内存只有*约 1GB (1024mb),要增加内存,请使用以下命令启动 pyspark:
pyspark --master spark://localhost:7077 --executor-memory 7g
在 Spark 中,每个执行器有 10%的开销,所以我们最多可以分配 7200mb,但是为了安全起见,我们选择 7。
测试集成
默认情况下,会自动创建一个 SparkContext,变量是sc
。
从我们之前创建的配置单元表中读取。
from pyspark.sql import HiveContexthc = HiveContext(sc)hc.sql("show tables").show()hc.sql("select * from test_table").show()
要从 Hadoop 读取文件,命令应该是:
sparksession = SparkSession.builder.appName("example-pyspark-read-and-write").getOrCreate()
df = (sparksession
.read
.format("csv")
.option("header", "true")
.load("hdfs://YOUR_IP:9000/PATH_TO_FILE")
)
实际的 Hadoop 使用案例
除了存储数据,Hadoop 还被用作一个特性库。假设你是一个团队或组织的一部分,他们有多种模型。每个模型都有一个数据管道,用于接收原始数据、计算数据并将数据转换为特征。对于一个或两个模型来说,这完全没问题,但是如果有多个模型呢?如果跨这些模型重用特性(例如,记录标准化股票价格)会怎样?
我们可以创建一个只计算一次要素的数据管道,并将其存储在要素库中,而不是每个数据管道都重新计算相同的要素。该模型现在可以从要素库中提取要素,而无需任何冗余计算。这减少了数据管道中冗余计算和转换的数量!
功能存储还有助于解决以下问题:
- 不会重复使用特征。数据科学家面临的一个常见障碍是花费时间重新开发功能,而不是使用以前开发的功能或其他团队开发的功能。要素存储允许数据科学家避免重复工作。
- 功能定义各不相同。任何一家公司的不同团队可能会以不同的方式定义和命名特性。此外,访问某个特定特性的文档(如果存在的话)通常是一个挑战。特征存储通过保持特征及其定义的组织性和一致性来解决这个问题。功能库的文档有助于您围绕整个公司的所有功能创建一种标准化的语言。您确切地知道每个特征是如何计算的,以及它代表什么信息。
- 培训和生产功能不一致。生产和研究环境通常使用不同的技术和编程语言。流入生产系统的数据流需要实时处理为特征,并输入到机器学*模型中。
如果你想看一看一个功能商店并免费开始,我推荐 StreamSQL 。StreamSQL 允许您从各种来源传输数据,如 HDFS、本地文件系统、Kafka 等。并创建一个数据管道,可以养活你的模型!它能够保存在线或本地 HDFS 上的特征存储,供您训练模型。它还为您创建测试(保持)集。他们有一个很好的 API 文档,并且一直在改进它。
反馈
我鼓励所有关于这个帖子的反馈。如果你有任何问题或者需要任何帮助,你可以给我发邮件到 sidhuashton@gmail.com 或者在帖子上留言。
您也可以通过 Twitter 联系我并关注我,地址是 @ashtonasidhu 。
【教程】:在 Azure 上部署 ML 模型作为 REST API,用 SSL 保护
将机器学*模型投入生产的分步指南
你会学到什么:
TL;博士:
数据科学家经常忘记,如果他们的新模型不能在生产中使用,它们就没有多*用处。在本教程中,我将向您展示如何将 Tensorflow 模型部署到 Azure 容器实例,并使用 SSL 保护它。
将有 3 种部署类型:
- 部署#1:本地部署: 模型将被放在 docker 容器中,并作为 API 在 localhost 上公开。
部署#1:本地部署
- 部署#2:不加密的全局部署: 包含模型的 Docker 将上传到 Azure Container Registry。存储的容器将通过您可以调用的 API 公开。这样做的缺点是,如果每个人都有 IP 地址并且没有加密,他们就可以访问它。
部署#2:不加密的全局部署
- 部署#3:用 SSL 加密的全局部署: 包含模型的 Docker 将被上传到 Azure Container Registry。我们将为 API 的使用创建证书颁发机构(CA)和自签名证书。
部署#3:使用 SSL 加密的全局部署
先决条件:
- 带有 sudo 选项的 Linux 机器
- 安装在 Linux 上的 Docker
- 安装在 Linux 上的 Azure CLI
- 安装在 Linux 上的 openssl
- Azure 的有效订阅
注意:
如果你没有有效的订阅,你可以为 Azure 创建一个免费帐户,并获得 2 周的试用期。
第一步:我们需要一个模型
训练 Tensorflow 模型并用
tf.saved_model.save(model, "path / to / model")
保存
或者:
这里有一个演示模型,表示 f(x)= 2*x -1。它被保存到”。/demo_model/"
import os
import numpy as np
import tensorflow as tf xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float) model = tf.keras.Sequential([tf.keras.layers.Dense(units=1, input_shape=[1])])
model.compile(optimizer='sgd', loss='mean_squared_error')
history = model.fit(xs, ys, epochs=500, verbose=0)
print("Finished training the model") MODEL_DIR = "./demo_model" version = 1
export_path = os.path.join(MODEL_DIR, str(version)) model.save(export_path, save_format="tf")
print('\nexport_path = {}'.format(export_path))
Tensorflow 发球的细节我就不赘述了,因为有很棒的 Coursera 课程。上面使用的代码是本课程中使用的代码,但仅针对本演示进行了压缩。你可以从这里了解更多 TFServing。
步骤 2:将模型放入 TFServing 容器中
打开“终端”,导航到保存模型的目录。
$ MODEL_NAME=**demo_model** #**REPLACE with your model**
$ MODEL_PATH="./$MODEL_NAME"
$ CONTAINER_NAME="$MODEL_NAME"$ sudo docker run -d --name serving_base tensorflow/serving
$ sudo docker cp "$MODEL_PATH" serving_base:"/models/$MODEL_NAME"
$ sudo docker commit --change "ENV MODEL_NAME $MODEL_NAME" serving_base "$CONTAINER_NAME"$ sudo docker run -d -p 8501:8501 "$MODEL_NAME"
现在,您已经通过本地主机上的 API 公开了您的模型,您可以从终端调用它:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "[http://localhost:8501/v1/models/$MODEL_NAME:predict](http://localhost:8501/v1/models/$MODEL_NAME:predict)"
或者来自 python:
import requests
import json api_url = "[http://localhost:8501/v1/models/demo_model:predict](http://51.138.56.160/v1/models/use:predict)" data = json.dumps({"signature_name": "serving_default", "instances": [[10.0],[9.0]]}) headers = {"content-type": "application/json"} json_response = requests.post(api_url, data=data, headers=headers) print(json_response.text)
#STOP docker image
sudo docker container stop IMAGE_ID
#DELETE docker image
sudo docker container rm IMAGE_ID
部署#1:已完成!
您现在可以在 localhost 中使用您的模型了。
额外提示:另一个选择是端口转发。您使用 SSH 连接到机器。
ssh -N -f -L localhost:8501:localhost:8501 user@IP.ADDRESS
缺点:
- 你只能在本地使用它
- 您需要 VPN 访问才能使用端口转发
- 不可扩展
步骤 3:创建容器注册表
用于存储带有模型的容器。
转到 Azure portal 并找到容器注册表。点击添加新注册表按钮创建新注册表。
重要提示:
- 选择有效的订阅和资源组。
- 注册表名称必须是唯一的,我们以后还会用到它,所以请注意。(提示:仅使用小写字母)
- 位置是必须的,你应该选择离你最近的一个
- *多数情况下,基本 SKU 应该足够了。
你现在可以点击查看和创建这个注册表按钮。
重要提示:导航到您的资源并启用管理员用户。记下用户名和密码。
步骤 4:将 Docker 容器推送到容器注册中心
$ CONTAINER_REGISTRY_NAME=**blogdemo** #**REPLACE (lower letters only)**$ sudo az login #login to Azure$ sudo az acr login --name "$CONTAINER_REGISTRY_NAME" $ sudo docker images
$ VERSION_NAME="$CONTAINER_REGISTRY_NAME.azurecr.io/version1"$ sudo docker tag **2b458f67dac3** "$VERSION_NAME" #**REPLACE**$ sudo docker push "$VERSION_NAME"
恭喜:
您已经成功地存储了带有模型的容器
步骤 5:用容器实例公开模型
在存储容器之后,暴露模型是相当容易的。
最简单的方法是登录 Azure portal,导航到容器实例并点击添加按钮。命名容器后,选择一个想要的区域,并选择您上传模型的注册表。
注意:
尺寸真的取决于你的模型尺寸。这个例子中使用的尺寸对于这个模型来说已经足够了。我已经部署了*约 150M 参数(600MB)的模型,它需要 2 个 vcpu 和 4 GB 内存。
现在点击下一步:联网按钮。
注意: DNS 名称是可选的,它需要是唯一的。
点击审查并创建,您的模型就可以使用了。(要删除实例,只需导航至资源并点击删除按钮)
用法:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "[http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict)"
#**REPLACE** [blogdemo.westeurope.azurecontainer.io](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH DNS**#OR:$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -X POST "[http://IP.ADDRESS:8501/v1/models/$MODEL_NAME:predict](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict)"
#**REPLACE** [IP.ADDRESS](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH IP**
使用模型与使用本地公开的模型是一样的,唯一的区别是 URL。您可以使用 DNS URL 或 IP URL。
部署#2:已完成!
您已经部署了一个可以在互联网上使用的模型。您可以监视模型的使用情况,并在需要时升级容器的*小,这使得它具有可伸缩性。
缺点:
- 每个知道 IP 地址的人都可以访问这个模型
- 未加密
步骤 6:创建证书颁发机构和用户
为了保护 API,我们需要加密。如果您使用商业证书颁发机构,可以跳过这一步。我们将创建自己的自签名证书。
$ mkdir certs
$ cd certs $ openssl ecparam -genkey -name secp256r1 | openssl ec -out ca.key $ openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
系统会提示您填写表格。请填写您的信息。
注: 为简单起见,这是椭圆加密的一个例子。我建议使用 RSA 密钥。这是一个很棒的要点,解释了如何去做。
$ CLIENT_ID=”client”
$ CLIENT_SERIAL=01
#when creating new user make sure that **serial is unique**$ openssl ecparam -genkey -name secp256r1 | openssl ec -out “${CLIENT_ID}.key”$ openssl req -new -key “${CLIENT_ID}.key” -out “${CLIENT_ID}.csr”
#password should be empty$ openssl x509 -req -days 3650 -in “${CLIENT_ID}.csr” -CA ca.pem -CAkey ca.key -set_serial “${CLIENT_SERIAL}” -out “${CLIENT_ID}.pem”$ cat “${CLIENT_ID}.key” “${CLIENT_ID}.pem” ca.pem > “${CLIENT_ID}.full.pem”#**OPTIONAL**:
$ openssl pkcs12 -export -out “${CLIENT_ID}.full.pfx” -inkey “${CLIENT_ID}.key” -in “${CLIENT_ID}.pem” -certfile ca.pem#remember passoword and you will pass it with pfx file
步骤 7:配置 NGINX
我们为什么需要 NGINX?
拒绝所有没有有效证书的请求。
创建 nginx.config 文件,如下所示:
$ cd ..$ cat > nginx.conf
复制所有内容并粘贴到一个文件中。粘贴后,使用 CTRL+D 关闭编辑器。
user nginx;worker_processes auto;events {
worker_connections 1024;
}pid /var/run/nginx.pid;http {server {
listen [::]:443 ssl;
listen 443 ssl;server_name localhost;ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
ssl_prefer_server_ciphers on;ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions
ssl_session_timeout 24h;keepalive_timeout 300; # up from 75 secs defaultadd_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';ssl_certificate /etc/nginx/server.pem;
ssl_certificate_key /etc/nginx/server.key;
ssl_client_certificate /etc/nginx/ca.pem;
ssl_verify_client on;
location / {
proxy_pass http://localhost:8501; # TODO: replace with correct port
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
}
NGINX 的细节我就不赘述了,下面是详细的链接。这个配置是这个博客和这个文章的混合体。简而言之,它只监听 https(端口 443)并验证它收到的证书。如果证书有效,它会将解密的请求重定向到具有模型的容器,并对发送回客户端的响应进行加密。
步骤 8:为 Azure 准备设置文件
为 Azure 创建 test-deploy.yaml 文件:
$ cat > test-deploy.yaml
复制所有内容并粘贴到一个文件中。粘贴后,使用 CTRL+D 关闭编辑器。
location: LOCATION
name: NAME
properties:
containers:
- name: model
properties:
image: IMAGE.azurecr.io/version1:latest
ports:
- port: 8501
protocol: TCP
resources:
requests:
cpu: 1.0
memoryInGB: 1.5
- name: nginx-with-ssl
properties:
image: nginx
ports:
- port: 443
protocol: TCP
resources:
requests:
cpu: 1.0
memoryInGB: 1.5
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
imageRegistryCredentials:
- server: SERVER.azurecr.io
username: USERNAME
password: PASSWORD
volumes:
- secret:
server.pem: PEM
server.key: KEY
ca.pem: CA
nginx.conf: CONF
name: nginx-config
ipAddress:
ports:
- port: 443
protocol: TCP
type: Public
dnsNameLabel: DNS
osType: Linux
tags: null
type: Microsoft.ContainerInstance/containerGroups
我们将使用 sed 命令替换该模板的部分内容。这些是无需编辑即可运行的命令。
$ sed -i -e "s/name: NAME/name: $MODEL_NAME/" azure.yaml$ sed -i -e "s/image: IMAGE/image: $CONTAINER_REGISTRY_NAME/" azure.yaml$ sed -i -e "s/server: SERVER/server: $CONTAINER_REGISTRY_NAME/" azure.yaml$ sed -i -e "s/username: USERNAME/username: $CONTAINER_REGISTRY_NAME/" azure.yaml$ sed -i -e "s/server.pem: PEM/server.pem: $(cat ./certs/ca.pem | base64 -w 0)/" azure.yaml$ sed -i -e "s/server.key: KEY/server.key: $(cat ./certs/ca.key| base64 -w 0)/" azure.yaml$ sed -i -e "s/ca.pem: CA/ca.pem: $(cat ./certs/ca.pem | base64 -w 0)/" azure.yaml$ sed -i -e "s/nginx.conf: CONF/nginx.conf: $(cat ./nginx.conf | base64 -w 0)/" azure.yaml
这些命令必须进行编辑。用注册表中的位置和密码替换粗体部分,并用您的 DNS 替换 DNS_NAME。
$ sed -i -e "s/location: LOCATION/location: **westeurope**/" azure.yaml$ sed -i -e "s/password: PASSWORD/password: **REGISTRY_PASS**/" azure.yaml
#TIP: If your generated password has some special characters
#like / you will have to manually put **\**/ infront$ sed -i -e "s/dnsNameLabel: DNS/dnsNameLabel: **DNS_NAME**/" azure.yaml
步骤 9:部署模型
用您的有效资源组替换 < AZURE 资源组> ,您就可以开始了。
$ az container create --resource-group **<AZURE RESOURCE GROUP>** --name "$MODEL_NAME" -f azure.yaml
第十步:用法
确保您拥有 client.key 和 client.pem 的正确路径,并且您可以继续调用 API,如下所示:
$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -v --key "./certs/client.key" --cert "./certs/client.full.pem" -X POST -k [https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict](https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict)#**REPLACE** [blogdemo.westeurope.azurecontainer.io](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH DNS**#OR:$ curl -d '{"signature_name": "serving_default", "instances": [[10.0],[9.0]]}' -v --key "./certs/client.key" --cert "./certs/client.full.pem" -X POST -k [https://IP.ADDRESS/v1/models/demo_model:predict](https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict)
#**REPLACE** [IP.ADDRESS](http://blogdemo.westeurope.azurecontainer.io:8501/v1/models/$MODEL_NAME:predict) **WITH IP**
如果一切正常,您将会得到这样的响应:
重要提示:由于这是自签名证书,所以必须在 cURL 中添加 -k 标志。
Python 版本:
import requests
import jsonapi_url = "[https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict](https://blogdemo.westeurope.azurecontainer.io/v1/models/demo_model:predict)"data = json.dumps({"signature_name": "serving_default", "instances": [[10.0],[9.0]]})headers = {"content-type": "application/json"}json_response = requests.post(api_url, data=data, headers=headers,verify=False,cert=("./certs/client.pem","./certs/client.key"),)print(json_response.text)
重要提示:由于这是一个自签名证书,您必须设置 verify=False
无有效证书调用时:
部署#3:已完成!
第十一步:监控
您可以在容器实例中监控模型的使用,并且可以在 nginx 日志中看到请求。
常见问题:
问:我可以部署一个定制模型(没有 TF 服务)吗?是的。你必须对接型号,如果你使用正确的端口,其他一切都应该按预期工作。
问:我能把它部署到 AWS 吗?
答:是的,通过使用 nginx.config 和这篇博客里看到的 CA 配置。
问:我可以在 Docker Hub 上存储容器吗?答:是的。只需用 Docker Hub 中的用户名/repo:version 替换图像,并从 YAML 文件中删除 imageRegistryCredentials。
资源:
[## TensorFlow | Coursera 的高级部署场景
将机器学*模型带入现实世界涉及的不仅仅是建模。这种专业化将…
www.coursera.org](https://www.coursera.org/learn/advanced-deployment-scenarios-tensorflow/) [## 用 TensorFlow 服务训练和服务 TensorFlow 模型| TFX
警告:这款笔记本被设计为只能在 Google Colab * *上运行。它在系统上安装软件包,并需要…
www.tensorflow.org](https://www.tensorflow.org/tfx/tutorials/serving/rest_simple) [## 向 Azure 容器实例添加 SSL/TLS
如今,SSL/TLS 是任何在互联网上运行的网站或应用程序的基本要求。不幸的是…
medium.com](https://medium.com/@samkreter/adding-ssl-tls-to-azure-container-instances-1e608a8f321c) [## 在 Ubuntu 上通过 NGINX 使用基于客户端证书的认证
经过身份验证的 SSL/TLS 反向代理是保护您的应用程序免受攻击的强*方法。用户和坏…
www.ssltrust.com.au](https://www.ssltrust.com.au/help/setup-guides/client-certificate-authentication) [## 使用 nginx 进行客户端证书认证
应用程序中的身份验证非常困难。如果你决定推出自己的,安全问题几乎是肯定的。*多数…
fardog.io](https://fardog.io/blog/2017/12/30/client-side-certificate-authentication-with-nginx/) [## 使用 Docker 实时生成机器学*预测-生产中的 ML
如果我们想要实时生成预测,我们需要公开我们训练过的模型。展示我们模型的一种方式是…
mlinproduction.com](https://mlinproduction.com/docker-for-ml-part-4/)
https://miro . medium . com/max/800/1 * dikrcflfp 3 _-yqcysditw . png
https://miro.medium.com/max/600/0*7eCQeU5D86SQeFHa.png
https://cdn 0 . icon finder . com/data/icons/home-security-2/45/security-06-512 . png
集成 Prefect 和 Databricks 来管理您的 Spark 作业
Justin Jairam 的照片来自 @jusspreme (经允许)
Prefect 是一个工作流管理系统,使用户能够轻松地使用数据应用程序并添加重试、日志记录、动态映射、缓存、故障通知、调度等功能,所有这些都使用功能性 Python API。Prefect 允许用户将他们现有的代码转换成 DAG(有向无环图),并且依赖关系已经确定[1]。它简化了 ETL 管道和依赖项的创建,并使用户能够严格地关注应用程序代码而不是管道代码(看着你的气流)。Prefect 甚至可以创建分布式管道来并行化您的数据应用程序。
Databricks 的核心是一个 PaaS(平台即服务),它可以提供完全托管的 Spark 集群、交互式和协作式笔记本电脑(类似于 Jupyter)、生产流水线调度程序以及为基于 Spark 的应用提供支持的平台。它集成在 Azure 和 AWS 生态系统中,使处理*数据变得简单。Databricks 使用户能够在其管理的 Spark 集群上运行定制的 Spark 应用程序。它甚至允许用户将他们的笔记本电脑安排为 Spark 作业。它完全简化了*数据开发和围绕它的 ETL 过程。
Databricks 已经成为一个不可或缺的*数据 ETL 工具,一个我每天在工作中使用的工具,所以我为 Prefect 项目做了一个贡献,使用户能够将 Databricks 作业与 Prefect 集成。在本教程中,我们将讨论这一点——如何将正在运行的数据块笔记本和 Spark 作业整合到您的完美流程中。
先决条件
这个帖子不需要任何先验知识,但是推荐一个免费的府尹账号来实现这个例子。虽然这篇文章会涉及到级长的基础知识,但它不是一个深入的级长教程。
完美基础
任务
Prefect 中的任务相当于数据管道中的一个步骤。它就像应用程序或脚本中的 Python 函数一样简单。对任务的简单或复杂程度没有限制。也就是说,最好遵循编码最佳实践并开发您的功能,这样它们只做一件事。级长自己推荐这个。
一般来说,我们鼓励小任务而不是*任务,每个任务应该执行工作流程中一个独立的逻辑步骤,但不能更多。[2]
通过保持小任务,您将最*限度地利用 Prefect 的引擎,例如高效的状态检查点。
流
流程是将所有任务及其依赖关系联系在一起的东西。它描述了任务、它们的顺序和数据流之间的依赖关系。流将任务集合在一起,并使其成为一个管道,从而完善您的数据应用程序。
完美的流动可视化(图片由作者提供)
Prefect 中的本机数据块集成
我通过实现任务DatabricksRunNow
& DatabricksRunSubmit
来实现 perfect 和 Databricks 之间的无缝集成,从而为 perfect 项目做出了贡献。通过这些任务,用户可以从外部触发一个已定义的 Databricks 作业或一个 jar、Python 脚本或笔记本的单次运行。一旦任务被执行,它就使用 Databricks 本地 API 调用来运行笔记本或 Spark 作业。当任务运行时,它将继续轮询运行的当前状态,直到任务完成。一旦任务完成,如果成功,它将允许下游任务运行。
使用数据块任务创建流
在开始编写任何代码之前,我们必须创建一个完美的秘密来存储我们的数据块连接字符串。从你的提督云账户,点击左侧菜单中的Team
,进入Secrets
部分。这个部分是你管理完美流程的所有秘密的地方。
要生成 Databricks 连接字符串,您需要 Databricks 实例的主机名以及 Databricks 帐户的 PAT。要创建数据块 PAT,请遵循数据块文档中的步骤。连接字符串必须是有效的 JSON 对象。秘密的标题必须是DATABRICKS_CONNECTION_STRING
。
数据块连接字符串的完美秘密(图片由作者提供)
创建任务
让我们从定义一些运行 Databricks 笔记本和 Spark 作业所需的常见任务开始我们的流程。
from prefect import task, Flow
from prefect.tasks.databricks.databricks_submitjob import (
DatabricksRunNow,
DatabricksSubmitRun,
)
from prefect.tasks.secrets.base import PrefectSecretconn **=** PrefectSecret("DATABRICKS_CONNECTION_STRING")
*# Initialize Databricks task class as a template
# We will then use the task function to pass in unique config options & params* RunNow **=** DatabricksRunNow(conn)
SubmitRun **=** DatabricksSubmitRun(conn)
我们定义了两个任务对象RunNow
和SubmitRun
,作为模板来运行我们的数据块作业。我们可以通过不同的配置重用这些相同的任务,从而轻松创建新的数据块作业。让我们创建一些助手任务来动态创建我们的作业的配置。
**@**task
**def** **get_submit_config**(python_params: list):
"""
SubmitRun config template for the DatabricksSubmitRun task, Spark Python Task params must be passed as a list.
"""
**return** {
"run_name": "MyDatabricksJob",
"new_cluster": {
"spark_version": "7.3.x-scala2.12",
"node_type_id": "r3.xlarge",
"aws_attributes": {
"availability": "ON_DEMAND"
},
"num_workers": 10
},
"spark_python_task": {
"python_file": "/Users/ashton/databricks_task/main.py",
"parameters": python_params,
},
} **@**task
**def** **get_run_now_config**(notebook_params: dict):
"""
RunNow config template for the DatabricksSubmitRun task, Notebook Task params must be passed as a dictionary.
"""
**return** {"job_id": 42, "notebook_params": notebook_params}
get_submit_config
任务允许我们动态地将参数传递给 DBFS (Databricks 文件系统)上的 Python 脚本,并返回一个配置来运行一次性 Databricks 作业。您可以通过在 Databricks 作业配置中创建更多映射到配置选项的参数来增加灵活性。get_run_now_config
执行相同的任务,除了它为DatabricksRunNow
任务返回一个配置来运行一个预配置的数据块笔记本任务。get_run_now_config
和get_submit_config
的模式分别匹配现在运行和运行提交 API。
Python 文件参数必须作为列表传递,而笔记本参数必须作为字典传递。
现在让我们创建一个可以运行我们的任务的流。
创造流动
我们将创建一个流程,在 Databricks 上运行一个预配置的笔记本作业,然后是两个后续的 Python 脚本作业。
**with** Flow("Databricks-Tasks", schedule**=**None) **as** flow: run_now_config **=** get_run_now_config({"param1": "value"})
submit_config_a **=** get_submit_config(["param1"])
submit_config_b **=** get_submit_config(["param2"]) run_now_task **=** RunNow(json**=**run_now_config) submit_task_a **=** SubmitRun(json**=**submit_config_a) submit_task_b **=** SubmitRun(json**=**submit_config_b) *# Since Databricks tasks don't return any data dependencies we can leverage,
* *# we have to define the dependencies between Databricks tasks themselves
* flow.add_edge(run_now_task, submit_task_a)
flow.add_edge(submit_task_a, submit_task_b)
我们首先需要通过使用我们的get_run_now_config
和get_submit_config
任务来创建数据块作业配置。通过json
参数将立即运行配置传递给RunNow
任务,并将提交运行配置传递给SubmitRun
任务。json
参数接受一个与上面提到的Run Now
和Submit Run
API 匹配的字典。为了运行更多的数据块作业,我们实例化我们创建的RunNow
或SubmitRun
模板,并传入一个新的 json 作业配置。
Prefect flow 的一个令人惊叹的特性是,它可以从您的任务中自动构建一个 DAG。它将任务输入视为数据依赖,并由此推断出在其他任务运行之前需要完成哪些任务。例如,由于我们的run_now_task
有输入run_now_config
,流程构建 DAG,知道get_run_now_config
任务必须在run_now_task
之前运行。
有些任务不返回可用作下游任务输入的数据。例如,数据块任务只返回一个作业 ID。我们仍然可以通过使用.add_edge
函数来定义流程的任务间依赖关系。这将增加任务之间的相关性,这些相关性不会用作后续下游任务的输入。例如,flow.add_edge(run_now_task, submit_task_a)
表示submit_task_a
是run_now_task
的下游任务,在run_now_task
完成之前submit_task_a
不能运行。通过将边添加到剩余的数据块任务中,我们得到了最终的流程,您也可以在 Prefect schematics 选项卡中查看。
我们流程的 DAG(图片由作者提供)
为了运行流,我们调用流对象的.run()
方法— flow.run()
。最终的流程如下所示:
from prefect import task, Flow
from prefect.tasks.databricks.databricks_submitjob import (
DatabricksRunNow,
DatabricksSubmitRun,
)
from prefect.tasks.secrets.base import PrefectSecret **@**task
**def** **get_submit_config**(python_params: list):
"""
SubmitRun config template for the DatabricksSubmitRun task, Spark Python Task params must be passed as a list.
"""
**return** {
"run_name": "MyDatabricksJob",
"new_cluster": {
"spark_version": "7.3.x-scala2.12",
"node_type_id": "r3.xlarge",
"aws_attributes": {
"availability": "ON_DEMAND"
},
"num_workers": 10
},
"spark_python_task": {
"python_file": "/Users/ashton/databricks_task/main.py",
"parameters": python_params,
},
} **@**task
**def** **get_run_now_config**(notebook_params: dict):
"""
RunNow config template for the DatabricksSubmitRun task, Notebook Task params must be passed as a dictionary.
"""
**return** {"job_id": 42, "notebook_params": notebook_params} conn **=** PrefectSecret("DATABRICKS_CONNECTION_STRING")
*# Initialize Databricks task class as a template
# We will then use the task function to pass in unique config options & params* RunNow **=** DatabricksRunNow(conn)
SubmitRun **=** DatabricksSubmitRun(conn)**with** Flow("Databricks-Tasks", schedule**=**None) **as** flow: run_now_config **=** get_run_now_config({"param1": "value"})
submit_config_a **=** get_submit_config(["param1"])
submit_config_b **=** get_submit_config(["param2"]) run_now_task **=** RunNow(json**=**run_now_config) submit_task_a **=** SubmitRun(json**=**submit_config_a) submit_task_b **=** SubmitRun(json**=**submit_config_b) *# Since Databricks tasks don't return any data dependencies we can leverage,
* *# we have to define the dependencies between Databricks tasks themselves
* flow.add_edge(run_now_task, submit_task_a)
flow.add_edge(submit_task_a, submit_task_b)flow.run()
*# flow.register("YOUR_PROJECT") to register your flow on the UI*
结论
现在,作为 ETL 流程的一部分,您已经掌握了运行 Databricks 笔记本和 Spark 作业所需的所有知识。要了解更多关于 Prefect 和 Databricks 作业的信息,我推荐阅读它们的文档,在这里找到在这里找到和在这里找到。
反馈
一如既往,我鼓励对我的帖子的任何反馈。如果你有任何问题或者需要任何帮助,你可以给我发邮件到 sidhuashton@gmail.com 或者在帖子上留言。
你也可以通过 Twitter 联系我并关注我,地址是 @ashtonasidhu 。
参考
- https://docs.prefect.io/core/,提督文件
- https://docs .提督. io/core/Getting _ Started/first-steps . html,提督入门
教程:Python 中 Networkx 和 Plotly 的网络可视化基础
从莎士比亚的剧本到网络图
人物网络图仲夏夜之梦
对于这个项目,我想结合我对文学、社会学和数据科学的热爱。更具体地说,我从*学时代起就对社交网络感兴趣,鉴于最近的事件,代表不同代理如何连接变得越来越重要。所以我开发了一个仲夏夜之梦的人物网络,这是我最喜欢的莎翁剧。如果你还没有读过它,并且喜欢一些相当异想天开的东西,我会推荐它!
在下面的帖子中,我将首先介绍网络的基础知识。然后,我将简要回顾一下我用来以我需要的格式获取数据的代码。然后,我检查了我用来创建和定制上述图表的代码。我使用的所有资源都在底部链接。希望这篇教程有帮助!如有任何问题,欢迎留言!制图快乐!
所有的代码都可以在my GitHub repo上找到。
(这篇博客的灵感来自马丁·格兰让的惊人之作《将莎士比亚悲剧中的网络可视化》,你可以在这里找到。)
目标
- 解释如何使用 Python 的 Networkx 包和 Plotly 创建视觉上吸引人的网络图
- 为了举例说明一个网络绘图的应用程序和我采取的一些数据清理步骤(因为我处理的是自然语言数据,所以数据清理比我在这篇文章中介绍的要复杂得多)
- 提供一些技巧、示例代码和资源
01:网络由哪些部分组成?
在我们可视化任何东西之前,我们需要理解我们如何定义一个网络。出于本练*的目的,我们将使用以下定义:
一个网络由一组节点组成,这些节点通过一组边相互连接。
我们可以将节点视为我们试图连接的任何类型的代理或对象。例如,在我的例子中,我的节点是仲夏夜之梦中的人物,但是如果您想要显示纽约市的地铁网络,您的节点可能是地铁站。
你可以把边想象成节点之间的连接。在我的项目中,我认为连接剧中角色的是他们一起出现的场景。我认为,如果两个角色(节点)一起出现在一个场景中(一条边),它们就是相连的。对于地铁网络的例子,它可能是地铁线路。所以两个地铁站(节点)是相连的,如果一条地铁线在两个站(一条边)之间运行。
02:让我们看一个 NLP 的例子
既然我们已经了解了网络的基本知识,我们可以开始将我们的理解转化为代码。我们需要以下东西来创建网络图:
- 最原始的数据——一个文本文件的脚本的《仲夏夜之梦》
- 节点——T4《仲夏夜之梦》中的人物列表
- *边缘——一种数据结构,告诉我们每个角色何时与另一个角色一起出现在场景中 (注意:这是项目中最耗时的部分)
既然我们已经在高层次上建立了我们所需要的东西,我们就可以开始编码了。
导入包
对于这个项目,我需要两套不同的包。第一个是收集和清理我的数据,这样我就可以在 Python 数据结构中拥有一组节点和边。第二组包用于可视化。
# Import packages for data cleaning
import numpy as np
import pandas as pd
import re # For finding specific strings in the text# Import packages for data visualization
import plotly.offline as py
import plotly.graph_objects as go
import networkx as nx
加载数据
在安装和加载包之后,您可以加载原始数据。我在这里找到了古腾堡计划的仲夏夜之梦的文本。
古腾堡计划 是一个令人惊叹的免费电子书图书馆。有超过 60,000 本电子书,如果你想探索自然语言处理(NLP)的世界,我强烈推荐你把它作为一种资源。
# Load the data
f = open("midsummer.txt", "r")
whole_text = f.read()
检查和清理数据:简要概述
因为这不是一篇 NLP 文章,所以我只强调我在处理自然语言数据时必须考虑的一些问题。
如果你对我的数据清理代码的细节部分感兴趣,请随意查看我的 GitHub,上面和下面都有链接。
第一步:查看你的数据
我知道这听起来很基本,但是如果你想可视化你的数据,最好的做法是真正理解你的数据。例如,下面是我问我的数据的一些问题:
- 有我能得到我的节点的一个字符列表吗?我怎样才能找到它?(是的。从“戏剧人物”开始)
- 有没有一条线清楚地把每个场景分开?(是的。“场景”)
- 在这个关键字之前或之后,是否有多余的字符需要我处理或删除?(是的。)
- 我怎样才能知道一个场景中有哪些角色?(特定格式的阶段说明!“进入”、“退出”和许多不同的变化,我不会在这里进入,因为它是非常特定的 NLP。)
第二步:从你的数据中提取一个简单的样本,并尝试处理它
如果您试图获取整个脚本并对其进行处理,这将非常容易出错,并且您可能会错过模块化代码的机会。所以我拍了第一幕第二场,因为所有的角色都在场景的开始进入,没有其他的角色进入,也没有人在场景结束前离开。
- 什么样的数据结构最适合以最符合逻辑的方式获取最多的信息?(我主要用了一本字典的字典。)
第三步: 模块化你的流程
我用两个主函数和几个辅助函数创建了一个 process.py 文件。这绝不是唯一的方法,但这是我解决问题的方法,我的数据是复杂多样的。
- 1 函数为节点和边创建了一个空数据结构
- 1 函数接收一个场景,并获取在给定时间内出现的所有角色
- 当一个新的角色出现或者一个角色退出时,辅助函数进行处理,等等。
下面是我的数据结构的一个例子:
# Create the empty data structure using custom function
appearance_counts = get_empty_appearances(characters, True)# Show a part of the data structure
# Dictionary of dictionaries where each key is each character and each value is a dictionary with all other characters as keys, and values as 0 or [].
appearance_counts
{'Theseus': {'Hippolyta': 0,
'Egeus': 0,
'Hermia': 0,
'Helena': 0,
'Lysander': 0,
'Demetrius': 0,...
第四步:使用你的函数来处理数据
现在我有了这些数据结构,我需要用重要的信息填充这个结构——每个字符与另一个字符(边)一起出现了多少次。
还有很多代码支持下面的代码片段。如果你有问题,你可以看到我的 GitHub 回购链接以上和以下的更多细节。
# For each character that appears, get how many scenes the character # appears in and how many times each pair of characters appears
# together
for character in all_appearances:
scene_counts[character] = []
for co_char in all_appearances[character]:
appearance_counts[character][co_char] = len(all_appearances[character][co_char])
scene_counts[character].extend(all_appearances[character][co_char])
scene_counts[character] = len(set(scene_counts[character]))
终于!你有你的优势!
****注意:如果你对记录两个节点是如何连接的不感兴趣,而只关心两个节点是否连接,你可以只使用一个布尔值而不是一个整数。
# If the number is greater than 0, then the characters appeared in a
# scene together.appearance_counts
{'Theseus': {'Hippolyta': 3,
'Egeus': 2,
'Hermia': 3,
'Helena': 2,
'Lysander': 3,
'Demetrius': 3,
'Philostrate': 2,
'Quince': 1,
'Snug': 1,
'Bottom': 2,
'Flute': 1,
'Snout': 1,
'Starveling': 1,...
不要忘记你的节点!
****注意:如果您对跟踪每个节点的重要性不感兴趣,而只关心节点的存在,那么您可以只使用一个列表来跟踪节点,而不是字典。
# Number of scenes that each character appeared in
scene_counts
{'Theseus': 3,
'Hippolyta': 3,
'Egeus': 2,
'Hermia': 5,
'Helena': 6,
'Lysander': 5,
'Demetrius': 6,...
创建您的网络图
我们将从创建图形、添加节点和添加边开始。有几行代码可以让你自定义你的图表,我会在这篇博文的最后一节把它们说出来。
添加节点&边
****注意:我分别根据一个角色出现的场景数和两个角色共享的场景数,定制了节点的 size 变量和边的 weight 变量。这是可选的。
midsummer = nx.Graph()# Add node for each character
for char in scene_counts.keys():
if scene_counts[char] > 0:
midsummer.add_node(char, size = scene_counts[char])# For each co-appearance between two characters, add an edge
for char in appearance_counts.keys():
for co_char in appearance_counts[char].keys():
# Only add edge if the count is positive
if appearance_counts[char][co_char] > 0:
midsummer.add_edge(char, co_char), weight = appearance_counts[char][co_char])
您可以使用属性仲夏.节点和仲夏.边查看 Networkx 图中的节点和边。
获得职位
然后我们需要得到图中节点的位置。有几种不同的布局可供选择。我认为春季的布置看起来是最好的。您可以在图形布局部分的 Networkx 包的开发说明中看到更多选项。
# Get positions for the nodes in G
pos_ = nx.spring_layout(midsummer)
制作边缘痕迹
因为我是根据两个角色一起出现的场景数量来定制每条边的宽度/重量,所以我创建了一个定制函数,使用 Plotly 为每个节点对创建一个唯一的边轨迹。
该函数接受两个节点(字符), x 和 y ,当您将鼠标悬停在边缘上时想要显示的任何文本,以及边缘轨迹的宽度。
# Custom function to create an edge between node x and node y, with a given text and width
def make_edge(x, y, text, width):
return go.Scatter(x = x,
y = y,
line = dict(width = width,
color = 'cornflowerblue'),
hoverinfo = 'text',
text = ([text]),
mode = 'lines')
现在您已经使用您的规范创建了一个函数,迭代您的边,并为指定宽度(由边的权重决定)的每条边创建一个边迹。
# For each edge, make an edge_trace, append to list
edge_trace = []
for edge in midsummer.edges():
if midsummer.edges()[edge]['weight'] > 0:
char_1 = edge[0]
char_2 = edge[1]x0, y0 = pos_[char_1]
x1, y1 = pos_[char_2]text = char_1 + '--' + char_2 + ': ' + str(midsummer.edges()[edge]['weight'])
trace = make_edge([x0, x1, None], [y0, y1, None], text,
width = 0.3*midsummer.edges()[edge]['weight']**1.75)edge_trace.append(trace)
制作节点轨迹
对于节点,您可以坚持只进行单个节点跟踪—单个散布对象—并仍然自定义每个节点的*小。
# Make a node trace
node_trace = go.Scatter(x = [],
y = [],
text = [],
textposition = "top center",
textfont_size = 10,
mode = 'markers+text',
hoverinfo = 'none',
marker = dict(color = [],
size = [],
line = None))
# For each node in midsummer, get the position and size and add to the node_trace
for node in midsummer.nodes():
x, y = pos_[node]
node_trace['x'] += tuple([x])
node_trace['y'] += tuple([y])
node_trace['marker']['color'] += tuple(['cornflowerblue'])
node_trace['marker']['size'] += tuple([5*midsummer.nodes()[node]['size']])
node_trace['text'] += tuple(['<b>' + node + '</b>'])
03:绘制和定制您的网络
现在我们有了包含所有节点的节点轨迹,以及我们的边轨迹,我们可以把它们放在一个图表中,看看它看起来像什么。
自定义您的布局
为了定制我的布局,我修改了网格线、图例和刻度标签的背景颜色和外观。
# Customize layout
layout = go.Layout(
paper_bgcolor='rgba(0,0,0,0)', # transparent background
plot_bgcolor='rgba(0,0,0,0)', # transparent 2nd background
xaxis = {'showgrid': False, 'zeroline': False}, # no gridlines
yaxis = {'showgrid': False, 'zeroline': False}, # no gridlines
)# Create figure
fig = go.Figure(layout = layout)# Add all edge traces
for trace in edge_trace:
fig.add_trace(trace)# Add node trace
fig.add_trace(node_trace)# Remove legend
fig.update_layout(showlegend = False)# Remove tick labels
fig.update_xaxes(showticklabels = False)
fig.update_yaxes(showticklabels = False)# Show figure
fig.show()
自定义您的颜色和文本
在最基本的层面上,您总是可以自定义节点和边的颜色,以及静态显示在节点/边旁边或当您将鼠标悬停在节点或边上时显示的任何文本。
你可以很容易地访问颜色和文本,只需使用节点和边缘轨迹中的属性。您会注意到我在本文分享的代码片段中调用这些属性的地方。但是我用来使我的文本更加突出的一个巧妙的技巧是学*如何加粗我的节点旁边的静态文本:
# Add <b> and </b> at the beginning and end of your string to bold
# the text in question!
node_trace['text'] += tuple(['<b>' + node + '</b>'])
自定义边缘宽度和节点*小
最后,我想在视觉上突出我所做的最重要的定制。这涉及到我认为最好的尝试和错误。以下代码行分别位于关于创建边跟踪和节点跟踪的部分:
# Line that customized the width of each edge trace
trace = make_edge([x0, x1, None], [y0, y1, None], text,
width = 0.3*midsummer.edges()[edge]['weight']**1.75)# Line that customized the size of each node in the node trace
node_trace['marker']['size'] += tuple([5*midsummer.nodes()[node]['size']])
正如你所看到的,我硬编码了标量和指数,以确定在剧中 x 和 y 场景中出现的差异有多*(节点的*小),或者在 x 和 y 场景中出现的角色相同(边缘的宽度)。你所使用的具体数字会极*地改变你的图表的外观。所以和他们一起玩,直到你感到满意为止!
奖金:未来网络项目的想法
- 创建一个以时间为变量的动画网络
- 创建一个基于进出链接的维基百科文章网络
- 根据地铁线路在城市中创建地铁站网络
- 在一群人之间建立一个社交网络(注意,你需要某种封闭的系统或方法来监控网络成员在特定时间的变化)
资源
古登堡计划是一个拥有超过 60,000 本免费电子书的图书馆。在免费的 epub 和 Kindle 电子书中选择,下载它们或…
www.gutenberg.org](https://www.gutenberg.org/) [## 网络图
Dash 是一个用于构建分析应用程序的开源框架,不需要 Javascript,而且它是紧密结合的…
plotly.com](https://plotly.com/python/network-graphs/) [## 网络可视化:映射莎士比亚的悲剧
莎士比亚的悲剧结构都一样吗?这些角色是相当孤立的,成组的,还是都有联系的…
www . martingrandjen . ch](http://www.martingrandjean.ch/network-visualization-shakespeare/) [## NetworkX - NetworkX 文档
NetworkX 是一个 Python 包,用于创建、操作和研究…的结构、动力学和功能
networkx.github.io](http://networkx.github.io/) [## rweng 18/仲夏 _ 网络
创建字符网络图。在……上创建一个帐户,为 rweng 18/summers _ network 的发展做出贡献
github.com](https://github.com/rweng18/midsummer_network)**
LSTMs 教程:计算观点
杰克·纳格兹在 Unsplash 上的照片
目录
1\. [Introduction](#6d31)2\. [Why Do we need RNNs?](#95b4)3\. [RNN Training and Inference](#7194)4\. [Structure of an RNN](#8ca2)5\. [Time Unrolling](#3226)6\. [Vanishing Gradient](#aa74)7\. [Long Short-Term Memory (LSTM)](#0d00)8\. [LSTM equations](#b10c)9\. [Understanding the LSTM dimensionalities](#da46)10\. [Time Unroll and Multiple Layers](#ada8)11\. [Example: Sentiment Analysis using LSTM](#4019)12\. [Testing your knowledge](#c240)
介绍
最近,人们对将深度学*模型嵌入硬件非常感兴趣。当涉及到深度学*模型部署时,尤其是在边缘,能量是至关重要的。Pete Warden 在“为什么机器学*的未来很小”上发表了一篇关于为什么能源对 AI@Edge 很重要的博文。程序(或模型)的能量优化只能通过对底层计算的良好理解来完成。在过去几年与深度学*人员——硬件架构师、微内核程序员、模型开发人员、平台程序员和受访者(尤其是受访者)的合作中,我发现人们从定性的角度理解 LSTMs,但从定量的角度理解不好。如果你不能很好地理解某事,你就不能优化它。缺乏了解导致 LSTMs 开始失宠。本教程试图通过方程解释 LSTMs 所需的计算来弥合定性和定量之间的差距。同时,这也是我从计算的角度巩固对 LSTM 的理解的一种方式。希望它也能对以不同身份使用 LSTMs 的其他人有用。
注意:这里的免责声明是,我既不声称自己是 LSTMs 方面的专家,也不声称自己的理解完全正确。如果有不正确或令人困惑的地方,请随意发表评论。
为什么我们需要 RNNs?
需要递归神经网络(RNNs ),因为我们希望设计能够识别(或操作)序列的网络。卷积神经网络(CNN)不关心它们识别的图像的顺序。另一方面,RNN 用于视频、手写识别等序列。这在图 1 中用一个高层次的卡通图来说明。
图 1:反馈网络和前馈网络之间区别的卡通图示。原图来自 维基百科 。
简而言之,如果我们试图识别视频、手写或语音等序列,我们就需要 RNNs。注意,我们仍然没有谈论 LSTMs。我们仍在试图了解 RNN。稍后我们将讨论 LSTMs。
悬崖笔记版本
当我们试图处理序列时,需要 rnn。
RNN 训练和推理
如果你跳过了前一部分,我们首先试图理解香草 RNN 的工作原理。如果你试图理解 LSTMs,我鼓励并敦促你通读这一部分。
在本节中,我们将了解以下内容:
- RNN 的结构。
- 时间展开
我们将基于这些概念来更好地理解基于 LSTM 的网络。
RNN 的结构。
图 2 显示了一个简单的 RNN 结构。该图的灵感来自于深度学*书籍(具体是第 10 章第 373 页的图 10.3)。
图中需要注意一些事情:
- 我在括号中用红色表示了每个节点的变量。在下一张图和下一节中,我将使用变量(在等式中),所以请花几秒钟时间理解它们。
- 从预期产量到损失的箭头方向不是错别字。
- 变量 U,V,W 就是这个网络的权重矩阵。
- 反馈路径中的黄色斑点(由绿色箭头指示)表示单位延迟。如果你是 DSP 的,就把这个当成(z^-1)
- 反馈(用绿色箭头表示)使这个玩具例子有资格成为 RNN。
图 2:RNN 结构的例子。这个形象的灵感来源于 深度学*书籍
在我们进入方程之前。让我们看看图表,了解发生了什么。
- 在宇宙之初。输入 'x(t=0)' 与矩阵 U 相乘得到 x(t=0)*U 。
- 上一时间步的反馈乘以矩阵 W. 由于这是初始阶段,反馈值为零(为简单起见)。因此,反馈值为 h(t=-1)W = 0。因此,乘积是 0+x(t=0)U = x(t=0)*U
- 现在,它与矩阵 V 相乘,得到 x(t=0)UV。
- 对于下一个时间步长,这个值将存储在 h(t)中,并且不是一个非零值。
因此,上述也可以总结为以下等式:
在上述方程中,我们忽略了非线性和偏差。将它们添加到等式中,如下所示。不要担心这些看起来很复杂。
悬崖笔记版本
rnn 的结构中有一个反馈环。这使得他们能够处理序列。
时间展开
时间展开是理解 RNNs 和 LSTMs 的一个重要概念。
rnn 也可以表示为其自身的时间展开版本。这是表示它们的另一种方式,方程没有变化。时间展开只是另一种表现,而不是一种转换。我们之所以要用这种方式表示它们,是因为这样更容易推导出正向和反向传递方程。时间展开如下图所示:
展开的 RNN 画像。形象的灵感来源于 深度学*书籍
在左上图中,RNN 的结构和我们之前看到的一样。右边是时间展开的表示。
从该图中可以得出一些重要结论:
- 权重矩阵 U,V,W 不随时间变化。这意味着一旦训练了 RNN,权重矩阵在推断期间是固定的,并且不依赖于时间。换句话说,相同的权重矩阵(U,V,W)用于每个时间步。
- 浅色阴影的 h(..)两边分别表示 h(t-1)之前和 h(t+1)之后的时间步长。
- 上图显示了 RNN 的向前(或推断)传球。在每个时间步,都有一个输入和一个相应的输出。
- 在正向传递中,“信息”(或内存)通过变量 h. 传递到下一级
悬崖笔记版本
rnn 可以表示为时间展开的版本。这只是一个表示,而不是转换。在前向传递中,权重矩阵 U、V、W 不依赖于时间。
消失梯度
rnn 存在为长距离序列保留上下文的问题。换句话说,rnn 不能处理非常长的序列(想想长句或长演讲)。给定输入对隐藏层(以及输出)的影响作为时间(或序列长度)的函数呈指数衰减(或爆发和饱和)。消失梯度问题如下图所示,来自亚历克斯·格雷夫斯的论文。节点的阴影表示网络节点在给定时间对输入的敏感度。阴影越暗,灵敏度越高,反之亦然。如图所示,当我们从时间步长=1 快速移动到时间步长=7 时,灵敏度会衰减。网络会忘记第一次输入。
来自 Alex Grave 论文的图显示了隐藏节点对梯度的敏感性。
这是使用 LSTMs 的主要动机。消失梯度问题导致研究人员多次尝试提出解决方案。其中最有效的是 LSTM 或长短期记忆,由 Hochreiter 于 1997 年提出。
悬崖笔记版本
传统的 RNNs 对于长序列(序列长度*约*于 10 个时间步长)的输入不敏感。1997 年提出的 LSTMs 仍然是克服 rnn 这一缺点的最流行的解决方案。
长短期记忆(LSTM)
LSTMs 是由 Hochreiter 在 1997 年提出的,作为一种减轻与普通 rnn 相关的棘手问题的方法。
一些博客和图片描述了 LSTMs。如您所见,在描述 LSTMs 的方式上有很*的差异。在这篇文章中,我想通过方程式来描述它们。我发现它们通过方程式更容易理解。有很多优秀的博客可以让你直观地理解它们,我强烈推荐你去看看:
(b) LSTM 来自维基百科
阎石的博客上媒
(d)来自深度学*书籍的 LSTMs
描述 LSTMs 的流行博客和论文中的不同图片。(一)克里斯多夫·奥拉赫的博客。 (b) LSTM 来自维基百科 阎石的博客关于中(d)lstm 来自深度学*书籍 (e) 英伟达的博客关于加速 lstm(f)LSTM 图来自关于人体活动检测的会议论文
LSTM 方程
下图显示了单个时间步长的 LSTM 的输入和输出。这是一个时间步长的输入、输出和时间展开表示的方程。LSTM 有一个输入 x(t) ,它可以是 CNN 的输出或者直接是输入序列。 h(t-1) 和 c(t-1) 是来自前一时间步 LSTM 的输入。o(t) 是该时间步长的 LSTM 的输出。LSTM 还生成 c(t) 和 h(t) 用于下一时间步 LSTM 的消耗。
LSTM 输入输出和单个时间步长的相应方程。
注意,LSTM 方程也产生 f(t),i(t),c'(t),这些是 LSTM 的内部消耗,用于产生 c(t)和 h(t)。
从上面可以看出一些要点:
- 上述等式仅适用于一次性步骤。这意味着这些方程必须在下一个时间步重新计算。因此,如果我们有 10 个时间步长的序列,那么对于每个时间步长,上述等式将分别计算 10 次。
- 权重矩阵(Wf,Wi,Wo,Wc,Uf,Ui,Uo,Uc)和偏差(bf,bi,bo,bc)不依赖于时间。这意味着这些权重矩阵不会从一个时间步长改变到另一个时间步长。换句话说,为了计算不同时间步长的输出,使用相同的权重矩阵。
下面的伪代码片段显示了十个时间步长的 LSTM 时间计算。
说明 10 个时间步长的 LSTM 计算的代码片段。
悬崖笔记版本
LSTM 网络的权重矩阵从一个时间步长到另一个时间步长不变。LSTM 由 6 个方程式组成。如果 LSTM 正在学*长度为“seq_len”的序列。然后,这六个方程将被计算总共‘seq _ len’。基本上,每一步都要计算方程。
理解 LSTM 维度
沃洛季米尔·赫里先科在 Unsplash 上的照片
理解了 LSTM 的单个时间步长所需的计算之后,我们进入下一个方面——维度。根据我的经验,LSTM 维度是 lstm 混乱的主要原因之一。另外,这是我最喜欢问的面试问题之一;)
让我们再看看下图中的 LSTM 方程。正如你已经知道的,这些是单个时间步长的 LSTM 方程:
单时间步长的 LSTM 方程
让我们从一个简单的 x(t)开始。这是输入信号/特征向量/CNN 输出。我假设 x(t)来自一个嵌入层(想想 word2vec),输入维数为[80x1]。这意味着 Wf 的维数为[某个值 x 80]。
至此我们已经:
x(t)是[80 X 1] —输入假设
Wf 是[Some_value X 80 ] —矩阵乘法法则。
让我们做另一个假设,LSTM 的输出维数是[12×1]。假设这是输出类的数量。因此,在每个时间步长,LSTM 产生*小为[12×1]的输出 o(t)。
既然 o(t)是[12x1],那么 h(t)必须是[12 x 1],因为 h(t)是通过一个元素接一个元素的乘法来计算的(看看最后一个关于如何从 o(t)和 c(t)计算 h(t)的等式)。因为 o(t)是[12x1],那么 c(t)必须是[12x1]。如果 c(t)是[12x1],那么 f(t),c(t-1),i(t)和 c'(t)必须是[12x1]。为什么?因为 h(t)和 c(t)都是通过逐元素乘法来计算的。
因此我们有:
o(t)是[12 X 1] —输出假设
h(t)和 c(t)是[12x1] —因为 h(t)是通过公式中 o(t)和 tanh(c(t))的逐元素乘法计算的。
f(t),c(t-1),i(t)和 c’(t)是[12x1]——因为 c(t)是[12 x1],并且是通过要求相同*小的元素方式运算来估计的。
因为 f(t)的维数是[12×1],那么 Wf 和 x(t)的乘积必须是[12×1]。我们知道 x(t)是80x1那么 Wf 必须是[12x80]。再看 f(t)的等式,我们知道偏置项 bf 是[12x1]。
因此,我们有:
x(t)是[80 X 1] —输入假设
o(t)是[12 X 1] —输出假设
h(t)和 c(t)是[12x 1]——因为 h(t)是通过等式中 o(t)和 tanh(c(t))的逐元素相乘来计算的。
f(t),c(t-1),i(t)和 c’(t)是[12x1]——因为 c(t)是[12 x1],并且是通过要求相同*小的元素式运算来估计的。
Wf 是[12x80] —因为 f(t)是[12x1],x(t)是[80x1]
bf 是[12x1] —因为所有其他项都是[12x1]。
以上内容看起来可能比实际情况要复杂一些。花点时间自己解决它。相信我,没那么复杂。
现在进入令人困惑的部分:)开个玩笑!
在 f(t)的计算中,Uf 和 h(t-1)的乘积也必须是[12x1]。现在根据前面的讨论我们知道 h(t-1)是[12x1]。h(t)和 h(t-1)将具有相同的维数[12×1]。因此,Uf 将具有[12×12]的维数。
所有 Ws (Wf,Wi,Wo,Wc)将具有相同的[12x80]尺寸,所有 bias(BF,bi,bc,bo)将具有相同的[12x1]尺寸,所有 Us (Uf,Ui,Uo,Uc)将具有相同的[12x12]尺寸。
因此:
x(t)是[80 X 1] —输入假设
o(t)是[12 X 1] —输出假设
Wf、Wi、Wc、Wo 的尺寸均为【12x 80】
Uf、Ui、Uc、Uo 每个都有[12x12]的尺寸
bf、bi、bc、bo 的尺寸均为【12 x1】
ht,ot,ct,ft,it 各有一个维度【12 x1】
LSTM 的总权重矩阵*小为
权重 _ LSTM = 4 *[12x 80]+4 *[12x 12]+4 *[12x 1]
= 4 *[输出尺寸 x 输入尺寸]+4 *[输出尺寸]+4 *[输入尺寸]
= 4[960] + 4[144] + 4*[12] = 3840 + 576+48= 4,464
让我们验证将以下代码粘贴到您的 python 设置中
请注意,LSTM 的参数数量是 4464。这也是我们通过计算得到的结果!
在我们进入下一部分之前,我想强调一个关键的方面。LSTMs 有两个定义它们的东西:输入维度和输出维度(以及我稍后将谈到的时间展开)。在文献(论文/博客/代码文档)中,术语有很多模糊之处。有些地方称之为单位数、隐藏维数、输出维数、LSTM 单位数等。我没有争论哪个是正确的或者哪个是错误的,只是在我看来这些通常意味着同样的事情——输出维度。
到目前为止,我们已经查看了权重矩阵的*小。*多数框架将权重矩阵合并存储为单个矩阵。下图说明了这个权重矩阵和相应的维度。
注意:根据您使用的框架,权重矩阵将以不同的顺序存储。例如,Pytorch 可以在 Wf 或 Caffe 可以首先存储 Wo 之前保存 Wi。
LSTM 权重矩阵维度。
悬崖笔记版本
有两个参数定义时间步长的 LSTM。输入维度和输出维度。权重矩阵*小为:4 * Output _ Dim *(Output _ Dim+Input _ Dim+1)[感谢 Cless 捕捉到错别字]。当谈到 LSTMs 时,有很多模糊性——单位的数量、隐藏维度和输出维度。请记住,有两个参数定义了 LSTM-输入维度和输出维度。
时间展开和多层
下图中有两个独立的 LSTM 网络。两个网络都显示为展开三个时间步长。图(A)中的第一个网络是单层网络,而图(B)中的网络是双层网络。
两个 LSTM 网络(A)展开三个时间步长的单层 LSTM 网络(B)展开三个时间步长的双层 LSTM 网络
在第一个单层网络的情况下,我们初始化 h 和 c ,并且每个时间步产生一个输出,连同 h 和 c 一起被下一个时间步消耗。注意,即使在最后的时间步 h(t)和 c(t)被丢弃,为了完整起见,我还是显示了它们。正如我们之前讨论的,对于三个时间步长,权重(Ws、Us 和 bs)是相同的。
双层网络有两个 LSTM 层。第一层的输出将是第二层的输入。它们都有自己的权重矩阵和各自的 hs、cs 和 os。我通过使用上标来表明这一点。
示例:使用 LSTM 的情感分析
让我们来看看一个非常简单但很现实的 LSTM 网络,看看这是如何工作的。任务很简单,我们必须想出一个网络来告诉我们一个给定的句子是否定的还是肯定的。为了简单起见,我们假设句子是固定长度的。如果实际句子的字数少于预期长度,则填充零;如果实际句子的字数多于序列长度,则截断句子。在我们的例子中,我们将句子长度限制为 3 个单词。为什么是 3 个字?只是一个我喜欢的数字,因为它对我来说更容易画出图表:)。
严肃地说,您可以绘制数据集中一个句子的字数直方图,并根据直方图的形状选择一个值。*于预定单词数的句子将被截断,单词数较少的句子将用零或空单词填充。
不管怎样,回到我们的例子。我们坚持三个词。我们将使用一个嵌入层,将一个英文单词转换成*小为[80x1]的数字向量。为什么是 80?因为我喜欢 80 这个数字:)不管怎样,网络如下图所示。
使用 LSTM 的情感分析网络示例。
我们将尝试对一个句子进行分类——“我很快乐”。在 t=0 时,第一个字“I”被嵌入层转换成长度为[80x1]的数字向量。并且穿过 LSTM,随后是完全连接的层。然后在时间 t=1,第二个单词通过网络,接着是在时间 t=2 的最后一个单词“happy”。我们希望网络等待整个句子让我们了解感情。我们不希望它过于急切,一字不漏地把感悟告诉我们。这就是为什么在下图中,LSTM 的输出只显示在最后一步。Keras 将这个参数称为 return_sequence。将此项设置为“假”或“真”将决定 LSTM 和随后的网络是否在每个时间步长或我们示例中的每个单词生成输出。我想在这里强调的一个关键点是,仅仅因为你将返回序列设置为假,并不意味着 LSTM 方程被修改了。他们仍然在计算每个时间步长的 h(t),c(t)。因此计算量不会减少。
以下是 Keras 在 IMDB 数据集上进行情感分析的示例:
https://github . com/keras-team/keras/blob/master/examples/IMDB _ lstm . py
测试你的知识!
安吉丽娜·莉文在 Unsplash 上的照片
让我们试着巩固一下到目前为止所学的知识。在这一部分,我列出了一些样本/玩具网络的问题。如果我能测试我的理解是否正确,这将有助于我感觉更好,因此这一部分采用了这种格式。或者,您也可以使用这些来准备与 LSTMs 相关的面试:)
样本 LSTM 网络# 1
LSTM 网络# 1 示例
- 这个网络中有多少个 LSTM 层?— 网络有一层。不要与多个 LSTM 盒混淆,它们代表不同的时间步长,只有一层。
- 如图所示,序列长度或时间步数是多少?— 时间步长的数量为 3。看看时间索引。
- 如果输入维数,即 x(t)是[18x1],o(t)是[19x1],那么 h(t),c(t)的维数是多少?— h(t)和 c(t)的尺寸为【19x 1】
样本 LSTM 网络# 2
示例 LSTM 网络# 2
- 这个网络中有多少个 LSTM 层?—LSTM 的总层数为 5 层。
- 如图所示,序列长度或时间步数是多少?— 这个网络的序列长度为 1。
- 如果 x(t)是[45x1],h1(int)是[25x 1]——C1(int)和 o1(t)的维数是多少?— c1(t)和 o1(t)将与 h1(t)具有相同的尺寸,即[25x1]
- 如果 x(t)是[4x1],h1(int)是[5x1],o2(t)的*小是[4x1]。LSTM0 和 LSTM1 的权重矩阵的*小是多少?—LSTM 的权重矩阵为[4 * output _ dim (input _ dim+output _ dim+1)]。LSTM0 的输入尺寸为[4x1],LSTM0 的输出尺寸为[5x1]。LSTM1 的输入是 LSTM0 的输出,因此 LSTM1 的输入 dim 与 LSTM0 的 output_dim 相同,即[5x1]。LSTM1 的输出 dim 为[4x1]。因此,LSTM0 是[46(5+4+1)]=288,而 LSTM1 是[44*(5+4+1)] = 160。
- 如果 x(t)是[10x1],h1(int)是[7 x1]lst m1 的输入维数是多少?— 看上面的解释。在计算之前,我们需要知道 LSTM0 的输出维数。
- 如果 x(t)是[6x1],h1(int)是[4x1],o2(t)是[3x1],o3(t)是[5x1],o4(t)是[9x1],o5(t)是[10x1]网络的总重量*小是多少?— *单个时间步长的 LSTM 的权重矩阵为[4 * output _ dim (input _ dim+output _ dim+1)]。通过估计单个层的输入输出维度来工作。
样本 LSTM 网络# 3
样本 LSTM 网络# 3
- 这个网络有多少层?— 有两层
- 这个网络中显示的序列长度是多少?— 每层展开 2 次。
- 如果 x(t)是[80x1],h1(int)是[10x1],那么 o(t),h1(t),c1(t),f(t),i(t)的维数是多少?这些在图中没有显示,但是您应该能够标记出来。— o(t),h1(t),c1(t),f1(t),i1(t)将具有与 h1(t)相同的尺寸,即【10x 1】
- 如果 x(t+1)是[4x1],o1(t+1)是[5x1],o2(t+1)是[6x1]。LSTM0 和 LSTM1 的权重矩阵的*小是多少?—LSTM 的权矩阵由 4 * output _ dim (input _ dim+output _ dim+1)给出。LSTM0 将是 45(4+5+1),即 200。LSTM2 将是 46*(5+5+1) = 264。
- 如果 x(t+1)是[4x1],o1(t+1)是[5x1],o2(t+1)是[6x1]。乘法和累加运算的总数是多少?——我把这个留给读者。如果有足够多的人问,我会回答:)
样本 LSTM 网络# 4
样本 LSTM 网络# 4
- 有多少层?— 有 3 层。
- 如图所示,这个网络展开了多少时间步?— 每层展开 3 次。
- 这个网络总共要执行多少个方程?— 每个 LSTM 需要 6 个方程(某些文本合并为 5 个方程)。然后是 6 个方程/时间步长/LSTM。因此,6 个方程 3 个 LSTM 层 3 个时间步长= 54 个方程**
- 如果 x(t)是[10x1]估计 LSTM1 的权矩阵还需要哪些信息?LSTM2 呢?— *一个 LSTM 的权重矩阵由 4 * output _ dim (input _ dim+out _ dim+1)给出。因此,对于每个 LSTM,我们都需要 input_dim 和 output_dim。LSTM 的输出是 LSTM1 的输入。我们有输入维度[10x1],因此我们需要输出维度或 o1(int)维度以及 LSTM1 的输出维度,即 o2(t)。同样,对于 LSTM2,我们需要知道 o2(t)和 o3(t)。
摘要
围绕 LSTMs 的博客和论文经常在定性的层面上讨论它。在本文中,我试图从计算的角度解释 LSTM 运算。从计算的角度理解 LSTMs 是至关重要的,尤其是对于机器学*加速器的设计者来说。
参考资料和其他链接
来自深度学*书籍的 LSTMs
Pete Warden 关于“为什么机器学*的未来很小”的博客。
教程:用 R 为 Python 难民绘图
来自 Pixabay 的背景图片。
了解 R 惊人的绘图能力
虽然 Python 和 R 都是用于数据分析和机器学*的伟*语言,但*多数人都同意的一点是,R 在数据可视化方面占上风。虽然 Python 可能更适合快速绘图,但 R 具有可定制性控件,可以创建 Python 可以创建的几乎所有东西,甚至更远。最重要的是,R library ggplot2
遵循“图形语法”,比 Python 有更有组织和结构化的方式来绘制图表。
如果您是一名 Python 难民,正在寻找一种更好、更有条理的方法来绘制数据,那么本教程就是为您准备的!
我们将使用的绘图库是 ggplot2。基于图形的语法,它的工作原理是每个图形都可以由三个部分组成:
- 数据集
- 坐标系
- 几何图形,或表示数据点的可视标记
为了显示数值,数据中的变量需要映射到几何、美学的属性,如尺寸、颜色或x
和y
轴位置。
让我们从数据集开始。ggplot2
有一些内置功能。这
options(scipen=999) # turn off scientific notation like 1e+06
library(ggplot2)
data("midwest", package = "ggplot2") # load the data
现在我们有了数据,是时候指定坐标系了。aes()
功能可用于指定x
和y
轴。area
和poptotal
是midwest
表格中的列。
ggplot(midwest, aes(x=area, y=poptotal))
这里有一个空白坐标系,最后,是时候添加几何图形了。在这种情况下,由于我们正在制作一个散点图,将使用geom_point()
,它将点放到坐标系中。
ggplot(midwest, aes(x=area, y=poptotal)) + geom_point()
我们还可以添加其他层。例如,如果我们想添加一个线性回归,我们可以添加一个geom_smooth(method=’lm’)
geom。它对由ggplot
公式的初始数据部分指定的相同数据进行操作。
ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm")
我们还可以添加改变图形的层。例如,要放*图形的一部分,可以添加接受两个向量的coord_cartesian
层——一个描述x
轴范围,另一个描述y
轴范围。注意,在 R 中,c()或 concatenate 函数创建一个向量(类似于一个NumPy
数组或列表)。
ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm") + coord_cartesian(xlim=c(0,0.1), ylim=c(0, 100000))
除了添加几何图形和缩放,+
操作符还允许标记标题和轴。在这种情况下,+ ggtitle(“Title”,subtitle = 'subtitle')
可用于为x
和y
轴添加标题和+ xlab(‘axis’)
和+ ylab(‘axis’)
。
ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm") + coord_cartesian(xlim=c(0,0.1), ylim=c(0, 100000)) + ggtitle("Area Vs Population", subtitle="From midwest dataset") + xlab("Area") + ylab("Population")
为了以一种更加结构化的方式组织所有的层,而不是把它们都塞进一行,在+
之后创建一个新行。
ggplot(midwest, aes(x=area, y=poptotal)) +
geom_point() +
geom_smooth(method="lm") +
coord_cartesian(xlim=c(0,0.1), ylim=c(0, 100000)) +
ggtitle("Area Vs Population", subtitle="From midwest dataset") +
xlab("Area") +
ylab("Population")
每个图层都有自己的参数,例如,我们可以根据 geom 类型指定颜色和*小。
ggplot(midwest, aes(x=area, y=poptotal)) +
geom_point(col="purple", size=0.5) +
geom_smooth(method="lm", col="firebrick")
如果我们想根据类改变颜色,只需将颜色参数(col
)设置为与颜色应该区分的列名相同。aes()
需要指定引用的数据。
g <- ggplot(midwest, aes(x=area, y=poptotal)) +
geom_point(aes(col=state), size=3) + # Set color to vary based on state categories.
geom_smooth(method="lm", col="firebrick", size=2)
plot(g)
可以通过添加另一层来设置调色板。
gg + scale_colour_brewer(palette = "Set1")
在本教程中,您学*了:
- 图形的语法
- 向图中添加数据
- 向地块添加坐标
- 向图中添加散点图几何
- 向图中添加其他形式的几何图形,如回归
- 更改绘图的比例
- 调整几何图形的*小和颜色
- 更改调色板
感谢阅读!
教程:使用 CatBoost 进行泊松回归
如何使用泊松回归和 CatBoost 来提高基于计数的数据的准确性…并预测一条推文获得的赞数。
在本教程中,我们将讨论以下主题:
- 基于计数的数据的概念
- 什么是泊松回归,为什么它适用于基于计数的数据
- 如何用 CatBoost 包建立泊松回归模型
- 如何预测埃隆·马斯克的推文会获得多少赞
什么是基于计数的数据?
根据定义计数数据是一种数据类型,其中:
- 观察值只能取非负整数值
- 这些整数来自计数
更具体地说,在我们的教程中,我们将关注基于计数的数据,其中每个点都是在某个固定时间单位 t 内发生的事件数量。我们还将假设在时间单元 t 期间平均发生事件 λ 的数量。 λ 则称为事件率。事件发生率可能会随着时间的推移而变化,也可能会随着观察的不同而变化。以下是此类数据的一些示例:
- 每周看医生的人数
- 过桥车辆数量
- 每天发布在 Instagram 上的照片数量
- 乔治·r·r·马丁每两年写的“冰之歌”的数量
值得注意的是,基于计数的数据的分布可能是相当偏斜的,因为*多数数据点具有完全相同的值。
泊松回归
泊松分布
泊松分布的概率质量函数如下:
泊松分布的 PMF
其中 P(k) 是在给定事件率的时间单位内看到 k 个事件的概率(=每时间单位的事件数) λ 。
我们可以用泊松分布来模拟基于计数的数据。
常数为 λ 的泊松分布示例如下:
生成泊松分布样本的代码:
事件率λ作为因变量
泊松回归的思想是说事件率 λ 是因变量。
例如,每天过桥的自行车数量取决于天气、一年中的时间、一周中的日期等。我们可以建立一个普通的 RMSE 回归模型,但是,这样的模型不能解释基于计数的数据属性。所以在泊松回归中,我们希望最*化泊松分布的对数似然。假设 X , y 是我们用于泊松回归任务的数据集(意味着 y 是基于计数的),那么我们想要最*化对数似然性:
数据集(X,y)上泊松分布的对数似然性
自日志(咦!)不依赖于 X 我们去掉这一项,尽量减少以下损失:
这种损失可以用不同的模型和优化方法来优化。在我们的教程中,我们使用 CatBoost 包。
定型泊松回归模型
资料组
我们的数据集是用 Twitter API 收集的。它由埃隆·马斯克的推文组成。我们将使用 2019 年 3 月至 2020 年 1 月的推文作为训练集,2020 年 2 月上半月的推文作为验证集,2020 年 2 月下半月的推文作为测试集。我们的目标是推特收到的赞数。在实践中,*多数推文在发布后的前两周内收到了所有的赞,所以我们假设我们的时间单位是两周。我们收集了至少两周前的推文。
特征
我们为我们的任务提取了一些非常简单的特征:
- 推文长度
- 其他 Twitter 用户提及的次数
- 推文中的 URL 数量
- 月
- 一天
- 小时
- 星期几
- *小为 500 的 TF-IDF 特征向量(用
stop_words="english"
提取sklearn.feature_extraction.text.TfidfVectorizer
)
现在,我们将使用客观泊松来训练 CatBoost 模型。我们将使用我们的验证数据集来选择迭代次数。
bestIteration = 972
Shrink model to first 973 iterations.
R2 score: 0.555
RMSE score: 39844.35
56%的决定系数 R2 值表明,我们的模型对测试数据集的预测解释了超过一半的目标可变性,考虑到任务的复杂性和我们方法的简单性,这并不坏。
此外,为了进行比较,我们将训练一个具有标准 RMSE 目标的 CatBoost 模型:
bestIteration = 44
Shrink model to first 45 iterations.
R2 score: 0.483
RMSE score: 42936.22
鉴于 RMSE 模型的最佳迭代次数为 45,而泊松回归的最佳迭代次数为 972,我们可能会怀疑 CatBoost 自动选择的学*率 0.055185 对于 RMSE 模型来说太*了。为了解决这种可能的不公平,我们还训练了一个学*率小 30 倍的 RMSE 模型:
bestIteration = 748
Shrink model to first 749 iterations.
R2 score: 0.470
RMSE score: 43470.15
然而,较小的学*率无助于 RMSE 模型。
可以看出,考虑数据集性质的损失比标准 RMSE 损失产生更好的结果。
感谢您的关注!希望这篇教程对你有用。
教程:用 23 行 Python 在几秒钟内刮出 100 个标题
Scrapy 库的网页抓取快速,简单,而且非常强*。
如果你需要做任何类型的网页抓取,Scrapy 几乎是不可能错过的。借助并行请求、用户代理欺骗、robots.txt 策略等内置特性,您只需几行代码就可以构建一个强*的 web scraper。
在本教程中,我将展示如何创建一个基本的 Scrapy 蜘蛛,收集新闻文章的标题。在*多数网站上,你可以做到这一点,而不用担心付费墙、僵尸检测或奇怪的解析技巧,所以我将把这些担心留给另一个教程。
数据
这里的是一个有 100 个随机 URL 的文件,来自我从 NewsAPI 收集的数据集。前几行如下所示:
Python 库
我们将使用三个开源库(及其依赖项): Pandas 、 Scrapy 和 Readability 。假设您已经安装了pip,您可以通过在终端中运行以下命令来确保您的计算机已经安装了这些:
pip install scrapy pandas readability-lxml
然后,创建要组织项目的文件夹,并导航到该文件夹:
mkdir headline_scraper
cd headline_scraper
现在,我们可以创建蜘蛛。Scrapy 有一个startproject
命令可以设置整个项目,但是我发现它可能会很臃肿。我们将通过制作我们自己的蜘蛛来保持简单,正如所承诺的,它只有 23 行(包括注释和空白)🙂).以下是一个名为headline_scraper.py
的文件的内容:
与普通的 python 脚本不同,我们需要使用 scrapy 的runspider
命令来运行文件。使用-o
标志选择保存输出的位置:
scrapy runspider headline_scraper.py -o scraped_headlines.csv
就这些了!下面是对代码中发生的事情的解释。
遍历代码
初始化
除了导入库和声明PATH_TO_DATA
(指向存储 100 个 URL 的要点的链接)之外,下面是最初几行要做的事情:
class HeadlineSpider(scrapy.Spider)
创建了一个新的类“HeadlineSpider”,它继承了 scrapy 蜘蛛的基本功能name="headline_spider"
将新蜘蛛命名为“头条 _ 蜘蛛”,供 Scrapy 参考start_urls=read_csv(PATH_TO_DATA).url.tolist()
使用 pandas 的 csv 阅读器下载包含 100 个 url 的文件,然后获取“URL”列并将其转换为 python 列表。start_urls
是任何蜘蛛都需要的,但在这种情况下,它们是唯一会被访问的网址。
(如果您想使用具有自己的 URL 的不同文件,只需用您文件的位置替换PATH_TO_DATA
。然后,确保 url 列被标记为“URL”,或者将.url
替换为.name_of_your_column
,它可以在您的机器上,即PATH_TO_DATA=/Users/Jack/Desktop/file.csv
现在我们已经初始化了蜘蛛,我们需要告诉它当它到达一个网页时实际要做什么。
从语法上分析
要真正提取标题,我们必须实现parse
方法。每当 Scrapy 从列表start_urls
中检索到一个 url 时,它会自动向这个方法发送 http 响应,然后完全由您决定如何处理它。这个版本的功能如下:
doc=Document(response.text)
这一行使用可读性的功能来解析 html。你也可以使用其他的库,比如 BeautifulSoup 或者报纸来获取这个部分。yield
本质上是一个创建输出的 return 方法,您可以把它想象成生成的电子表格中的一行。'short_title':doc.short_title()
而它下面的几行只是简单的设置了返回对象的不同属性。您可以将'short_title'
视为结果电子表格中的一列,内容将是doc.short_title()
的输出(例如,“冠状病毒爆发如何影响全球经济”)
同样,这部分是非常可定制的。如果您想尝试获得文章的全文,只需更改 yield 行以包含如下的doc.summary()
。
yield {
'full_text': doc.summary(),
'short_title': doc.short_title(),
'full_title': doc.title(),
'url': response.url
}
这将在结果中增加一个名为full_text
的列。csv 文件,可读性最好的提取文章文本的尝试。
结果
下面是运行scrapy runspider headline_scraper.py -o scraped_headlines.csv:
的输出文件
嘣!至少*部分情况下。Scrapy 是非常容错的,所以只有 95/100 的 URL 返回。没有回来的 5 遇到了某种 http 错误,就像你有时在浏览网页时看到的 404 错误。此外,一些行(我数了 7 行)写着“警告”和“你是机器人吗?”而不是提供真实的标题。(彭博似乎有非常有效的机器人检测。)
有许多策略来处理 bot 检测,其中一些已经在 Scrapy 中实现。如果您感兴趣,请告诉我,我可以在以后的教程中介绍这些策略。
照片由 Divyadarshi Acharya 在 Unsplash 上拍摄
如果您对代码有任何疑问、意见或问题,欢迎回复本文或在Twitter上给我发消息。谢谢!
教程:停止从命令行运行 Jupyter 笔记本
将您的 Jupyter 笔记本作为独立的网络应用程序运行
Justin Jairam 的照片来自 @jusspreme (经允许)
Jupyter Notebook 提供了一个很好的平台来生成包含代码、等式、分析及其描述的人类可读文档。一些人甚至认为它与 NBDev 结合是一个强*的发展。对于这样一个完整的工具,开箱启动并不是最好的。每次使用都需要从命令行启动 Jupyter web 应用程序,并输入您的令牌或密码。整个 web 应用程序依赖于打开的终端窗口。有些人可能会“混淆”这个过程,然后使用 nohup 将它从他们的终端中分离出来,但这不是最优雅和可维护的解决方案。
幸运的是,Jupyter 已经提出了一个解决这个问题的方案,推出了 Jupyter 笔记本的扩展,作为一个可持续的网络应用程序运行,并具有内置的用户身份验证。为了在上面添加一个樱桃,它可以通过 Docker 管理和维护,允许隔离的开发环境。
在这篇文章的最后,我们将利用 JupyterHub 的能力来访问一个 Jupyter 笔记本实例,该实例可以在没有终端的情况下从网络中的多个设备访问,并且是一种更加用户友好的身份验证方法。
先决条件
关于 Docker 和命令行的基本知识将有助于设置这一功能。
我建议在你拥有的最强*的设备上这样做,并且一天的*部分时间都是开着的,最好是一整天。这种设置的一个好处是,你可以在网络上的任何设备上使用 Jupyter Notebook,但是所有的计算都在我们配置的设备上进行。
什么是 Jupyter Hub
JupyterHub 将笔记本电脑的强*功能带给用户组。JupyterHub 背后的想法是将 Jupyter 笔记本的使用范围扩*到企业、教室和*型用户组。然而,Jupyter Notebook 应该作为一个本地实例,由一个开发人员在一个节点上运行。不幸的是,JupyterHub 的可用性和可伸缩性与运行本地 Jupyter 笔记本的简单性之间没有折中。也就是说,直到现在。
JupyterHub 有预建的 Docker 映像,我们可以利用这些映像心血来潮地生成一个笔记本,在技术复杂性方面几乎没有开销。我们将使用 Docker 和 JupyterHub 的组合,从同一网址随时随地访问 Jupyter 笔记本。
体系结构
我们的 JupyterHub 服务器的架构将由两个服务组成:JupyterHub 和 JupyterLab。JupyterHub 将是入口点,并将为任何用户生成 JupyterLab 实例。这些服务中的每一个都将作为 Docker 容器存在于主机上。
JupyterLab 架构图(图片由作者提供)
构建 Docker 图像
为了构建我们的家用 JupyterHub 服务器,我们将使用 JupyterHub & JupyterLab 的预构建 Docker 映像。
码头文件
JupyterHub Docker 图像很简单。
FROM jupyterhub/jupyterhub:1.2# Copy the JupyterHub configuration in the container
COPY jupyterhub_config.py .# Download script to automatically stop idle single-user servers
COPY cull_idle_servers.py .# Install dependencies (for advanced authentication and spawning)
RUN pip install dockerspawner
我们使用预先构建的 JupyterHub Docker 映像,并添加我们自己的配置文件来停止空闲的服务器,cull_idle_servers.py
。最后,我们通过 Docker 安装额外的包来生成 JupyterLab 实例。
Docker 撰写
为了将所有的东西放在一起,让我们创建一个docker-compose.yml
文件来定义我们的部署和配置。
version: '3'services:
# Configuration for Hub+Proxy
jupyterhub:
build: . # Build the container from this folder.
container_name: jupyterhub_hub # The service will use this container name.
volumes: # Give access to Docker socket.
- /var/run/docker.sock:/var/run/docker.sock
- jupyterhub_data:/srv/jupyterlab
environment: # Env variables passed to the Hub process.
DOCKER_JUPYTER_IMAGE: jupyter/tensorflow-notebook
DOCKER_NETWORK_NAME: ${COMPOSE_PROJECT_NAME}_default
HUB_IP: jupyterhub_hub
ports:
- 8000:8000
restart: unless-stopped # Configuration for the single-user servers
jupyterlab:
image: jupyter/tensorflow-notebook
command: echovolumes:
jupyterhub_data:
需要注意的关键环境变量是DOCKER_JUPYTER_IMAGE
和DOCKER_NETWORK_NAME
。JupyterHub 将使用环境变量中定义的图像创建 Jupyter 笔记本。有关选择 Jupyter 图像的更多信息,您可以访问以下 Jupyter 文档。
DOCKER_NETWORK_NAME
是服务使用的 Docker 网络的名称。这个网络从 Docker Compose 自动获得一个名称,但是集线器需要知道这个名称来连接 Jupyter 笔记本服务器。为了控制网络名,我们使用了一个小技巧:我们将一个环境变量 COMPOSE_PROJECT_NAME 传递给 Docker Compose,网络名是通过向它附加 _default 获得的。
在与docker-compose.yml
文件相同的目录下创建一个名为.env
的文件,并添加以下内容:
COMPOSE_PROJECT_NAME**=**jupyter_hub
停止空闲服务器
因为这是我们的主设置,所以我们希望能够停止空闲的实例来保留我们机器上的内存。JupyterHub 有一些服务可以和它一起运行,其中之一就是 jupyterhub-idle-culler 。该服务停止任何长时间空闲的实例。
要添加这个服务,创建一个名为cull_idle_servers.py
的新文件,并将 jupyterhub-idle-culler 项目的内容复制到其中。
请确保“cull_idle_servers.py”与 docker 文件位于同一文件夹中。
要了解更多关于 JupyterHub 服务的信息,请查看他们的官方文档。
Jupyterhub 配置
最后,我们需要定义配置选项,如卷装载、Docker 映像、服务、身份验证等。对于我们的 JupyterHub 实例。
下面是我使用的一个简单的jupyterhub_config.py
配置文件。
import os
import sysc.JupyterHub.spawner_class **=** 'dockerspawner.DockerSpawner'
c.DockerSpawner.image **=** os.environ['DOCKER_JUPYTER_IMAGE']
c.DockerSpawner.network_name **=** os.environ['DOCKER_NETWORK_NAME']
c.JupyterHub.hub_connect_ip **=** os.environ['HUB_IP']
c.JupyterHub.hub_ip **=** "0.0.0.0" *# Makes it accessible from anywhere on your network*c.JupyterHub.admin_access **=** Truec.JupyterHub.services **=** [
{
'name': 'cull_idle',
'admin': True,
'command': [sys.executable, 'cull_idle_servers.py', '--timeout=42000']
},
]c.Spawner.default_url **=** '/lab'notebook_dir **=** os.environ.get('DOCKER_NOTEBOOK_DIR') **or** '/home/jovyan/work'
c.DockerSpawner.notebook_dir **=** notebook_dir
c.DockerSpawner.volumes **=** {
'/home/sidhu': '/home/jovyan/work'
}
请注意以下配置选项:
'command': [sys.executable, 'cull_idle_servers.py', '--timeout=42000']
: Timeout 是空闲 Jupyter 实例关闭之前的秒数。c.Spawner.default_url = '/lab'
:使用 Jupyterlab 代替 Jupyter 笔记本。注释掉这一行以使用 Jupyter 笔记本。'/home/sidhu': '/home/jovyan/work'
:我将我的主目录挂载到 JupyterLab 主目录,以便访问我桌面上的任何项目和笔记本。这也允许我们在创建新笔记本的情况下实现持久性,它们被保存到我们的本地机器,并且不会在我们的 Jupyter Notebook Docker 容器被删除时被删除。
如果您不希望挂载您的主目录,请删除这一行,并且不要忘记将sidhu
改为您的用户名。
启动服务器
要启动服务器,只需运行docker-compose up -d
,在浏览器中导航到localhost:8000
,您应该能够看到 JupyterHub 登录页面。
JupyterHub 登陆页面截图(图片由作者提供)
要在网络上的其他设备(如 asva 笔记本电脑、iPad 等)上访问它,请通过在 Unix 机器上运行ifconfig
在 Windows 上运行& ipconfig
来识别主机的 IP。
Ipconfig(图片作者提供)
从您的另一台设备上,导航到您在端口 8000: http://IP:8000
上找到的 IP,您应该会看到 JupyterHub 登录页面!
鉴定
这就给我们留下了向服务器进行认证的最后一项任务。因为我们没有设置 LDAP 服务器或 OAuth,所以 JupyterHub 将使用 PAM(可插拔认证模块)认证来认证用户。这意味着 JupyterHub 使用主机的用户名和密码进行身份验证。
为了利用这一点,我们必须在 JupyterHub Docker 容器上创建一个用户。还有其他方法可以做到这一点,例如在容器上放置一个脚本,并在容器启动时执行,但我们将手动完成它作为一个练*。如果您拆除或重建容器,您将必须重新创建用户。
我不建议将用户凭证硬编码到任何脚本或 docker 文件中。
1)找到 JupyterLab 容器 ID: docker ps -a
JupyterLab 容器 ID(图片由作者提供)
2)“SSH”进入容器:docker exec -it $YOUR_CONTAINER_ID bash
3)创建用户,按照终端提示创建密码:useradd $YOUR_USERNAME
4)使用凭证登录,一切就绪!
现在,您已经拥有了一台随时可用的 Jupyter 笔记本电脑服务器,可以从任何设备访问,尽在您的掌握之中!编码快乐!
反馈
我欢迎任何关于我的帖子和教程的反馈。你可以在 sidhuashton@gmail.com 的推特上给我发消息,或者发电子邮件给我。
教程:使用 CatBoost 进行不确定性估计
理解为什么你的模型是不确定的,以及如何估计不确定性的水平
数据不确定性示例(图片由 Yandex 提供)。
本教程涵盖以下主题:
- 什么是预测不确定性,为什么要关心它?
- 不确定性的两个来源是什么?
- 如何使用 CatBoost 梯度增强库估计回归问题的不确定性
你可以使用这个 Jupyter 笔记本遵循所有步骤。
什么是不确定性?
机器学*已经广泛应用于一系列任务。然而,在某些高风险应用中,如自动驾驶、医疗诊断和金融预测,一个错误可能会导致致命的后果或巨*的经济损失。在这些应用中,重要的是检测系统何时出错并采取更安全的措施。此外,还需要收集这些“故障场景”,标记它们,并通过主动学*教会系统做出正确的预测。
预测不确定性估计可用于检测误差。理想情况下,模型表明在可能出错的情况下存在高度的不确定性。这使我们能够发现错误并采取更安全的行动。至关重要的是,行动的选择可能取决于为什么模型是不确定的。不确定性的来源主要有两种:数据不确定性(也称任意不确定性)和知识不确定性(也称认知不确定性)。如果我们的目标是检测错误,就没有必要分离这两种不确定性。然而,如果我们的目标是主动学*,那么我们希望检测新的输入,而知识不确定性可以用于此。
数据的不确定性源于数据固有的复杂性,如附加噪声或重叠类别。在这些情况下,模型知道输入具有多个类的属性或者目标有噪声。重要的是,数据不确定性不能通过收集更多的训练数据来减少。
当模型的输入来自训练数据稀疏覆盖的区域或远离训练数据的区域时,知识不确定性出现。在这些情况下,模型对这一地区知之甚少,很可能出错。与数据不确定性不同,知识不确定性可以通过从了解不多的区域收集更多的训练数据来减少。
这篇教程文章详细介绍了如何在 CatBoost 中量化数据和知识的不确定性。
CatBoost 中的数据不确定性
为了说明这些概念,我们将使用一个简单的合成示例。
假设我们有两个分类特征 x₁和 x₂,每个有 9 个值,因此有 81 个可能的特征组合。目标取决于以下特征
y = mean(x₁,x₂) + eps(x₁,x₂)
其中均值 (x₁,x₂) 是某个未知的固定值,eps (x₁,x₂) 是均值为 0、方差为 var (x₁,x₂) 的正态分布噪声(即数据不确定性)。在我们的示例中,mean (x₁,x₂) 是随机生成的,var (x₁,x₂) 有两个值(0.01 和 0.04),分布如下:
数据中的差异。白色区域没有训练示例。
因此,心脏上的点比心脏外的点在目标中具有更多的噪声。请注意,我们为更好的可视化列举了类别,但是在数据集中,两个特征都是分类的,即没有给出顺序。
当我们生成具有这种分布的数据集时,我们假设心脏内部没有任何训练样本,这些特征组合被视为数据集的异常值。
用 RMSE 损失优化的标准模型只能预测 mean(x₁,x₂).好吧,但是如果我们想估计 y 的方差,也就是数据不确定性呢?换句话说,如果我们想了解哪些预测是有噪声的呢?为了估计数据的不确定性,人们不得不使用概率回归模型预测均值和方差。为此,CatBoost 中有一个新的损失函数,称为 RMSEWithUncertainty。对于这种损失,CatBoost 估计正态分布的均值和方差,优化负对数似然,并使用自然梯度,类似于 NGBoost 算法[1]。对于每个示例,CatBoost 模型返回两个值:估计平均值和估计方差。
让我们试着将这个损失函数应用到我们的简单例子中。我们得到以下方差:
用 RMSEWithUncertainty 猫靴损失估计的方差(数据不确定性)。
我们可以看到,CatBoost 成功地预测了心脏内外的方差。在心脏内部,我们没有训练数据,所以任何事情都可以在那里预测。
CatBoost 中的知识不确定性
好,我们知道如何估计数据中的噪声。但是,如何衡量某个特定地区缺乏训练数据所带来的知识不确定性呢?如果我们想检测异常值,该怎么办?估计知识的不确定性需要一个模型集合。如果所有模型都理解一个输入,它们将给出相似的预测(低知识不确定性)。然而,如果模型不理解输入,那么它们很可能提供不同的预测,并且彼此强烈不一致(高知识不确定性)。对于回归,知识不确定性可以通过测量跨越多个模型的平均值的方差来获得。注意,这不同于单个模型的预测方差,后者捕捉数据不确定性。
让我们考虑如下生成的 GBDT 模型的集合:
使用选项后验采样生成模型,因为这允许获得的(随机)预测很好地分布(具有良好的理论特性,这里我们参考[2]了解细节)。
然后,为了估计知识的不确定性,我们只需计算模型预测的平均值的方差:
knowledge = np.var(ens_preds, axis=0)[:, 0]
我们得到以下结果:
通过模型集成估计知识不确定性。
该模型正确地检测了心脏内部的知识不确定性(我们看不到原始心脏边界的痕迹)。这说明了如何通过估计知识的不确定性,我们可以检测异常输入。
在实践中,训练几个 CatBoost 模型的集合可能过于昂贵。理想情况下,我们希望训练一个单一的模型,但仍然能够检测异常值。有一个解决方案:我们可以使用从单个训练模型获得的虚拟集合:
由于只有一个训练好的模型,CatBoost 会为每个示例返回几个预测。这些预测是通过截断模型获得的:
虚拟合奏(图片由作者提供)
同样,我们使用选项后验采样来保证裁剪预测的理想分布。让我们看看我们得到了什么:
由虚拟集合估计的知识不确定性。
注意,知识不确定性的预测绝对值现在小得多,因为虚拟系综元素是相关的。但是,它仍然可以成功地检测未被占用的区域(异常值)。
我们可以使用prediction _ type = ' total uncertainty ',而不是prediction _ type = ' virt ensembles '返回几个模型的预测,这样更容易得到相同的结果。对于这种预测类型,CatBoost 使用虚拟集合计算所有类型的不确定性。也就是说,对于 RMSEWithUncertainty,它返回以下统计数据:[均值预测、知识不确定性、数据不确定性]:
感谢您的关注!我希望这篇教程能帮助你更好地理解不确定性的概念,以及如何用 CatBoost 估计它。我们将在以后的文章中告诉你更多关于不确定性的应用。敬请期待😺
[1] T. Duan 等, NGBoost:概率预测的自然梯度推进 (2020),2020
[2] A. Ustimenko,L. Prokhorenkova 和 A. Malinin,通过系综进行梯度增强的不确定性(2020),arXiv 预印本 arXiv:2006.10562
辅导强化学*
强化学*代理从零开始,什么都不知道,凭经验学*,有效但慢。我们能给他们一些提示让他们开始吗?
这个故事基于我与人合著的一篇论文,你可以在这里找到。
近年来,强化学*在视频游戏、机器人和推荐系统等任务中取得了令人惊讶的结果。然而,这些成功的结果是在训练 RL 代理数百万次迭代之后得到的。在那之前,经纪人的表现将远远谈不上伟*。事实上,当智能体开始时,它的行为在探索它可以采取的不同行动时是随机的,即使经过几次迭代并获得一些经验,智能体也会因为其在环境中的行动的可变性和随时可能出现的看不见的状态而经常出错。
在像视频游戏这样的环境中,这可能不是问题(尽管这意味着您需要时间和一些重要的计算资源),但在现实世界的系统中,这是一个严重的挑战。你可能会想到一个使用 RL 来学*如何移动的机器人:机器人可能需要几天来学*如何正确移动,如果移动是危险的,它可能会损坏自己。
你不得不爱失败,虽然[视频来自 IEEE。]
作为人类,我们不会从头开始学*一切,如果我们会,我们离穴居人就不远了。我们取得的所有进步都是通过学*他人的成就并加以改进而实现的。我们不能用 RL 做类似的事情吗?我们能不能教代理一些通用的规则,让它在从自己的经验中学*时可以应用?
我和我的同事们一直在研究这个想法,我们开发了一种叫做 Tutor4RL 的方法。我们已经在今年的 AAAI 制造:在实践中结合机器学*和知识工程上发表了它,你可以在这里看到它(不幸的是,今年的春季会场被取消了,但论文发表了。)Tutor4RL 仍在开发中,所以我在这里介绍我们方法和目前为止的初步结果。
常见方法
正如我所说,学*速度慢是学*者面临的一个普遍挑战,许多方法已经出现来解决这个问题:
- Simulations :一种常见的方法是创建一个模拟环境,在将代理部署到真实环境之前,代理可以在其中进行实验和培训。这种方法非常有效,但是创建模拟环境需要很*的努力,并且我们通常最终会对真实环境做出假设来创建模拟,这可能并不总是正确的。这些假设会影响代理在真实环境中的表现。
- 基于模型的 RL :与无模型的 RL 相反,基于模型的 RL 创建其环境的模型,这允许代理更快地学*。在机器人的例子中,我们可以把我们的 RL 模型建立在机器人的物理学和力学的基础上。然而,为了制作这个模型,我们需要*量来自环境的知识,同样,我们通常会做出伤害我们的假设。此外,生成的代理是特定于其环境的,如果我们想将其用于不同的环境,我们需要修改其模型,这涉及到额外的工作。
- 从演示中学*:在 Hester et al. (2018)中,作者开发了一种方法,使智能体可以有效地从人类的演示中学*。学*速度**加快,并且代理能够达到最先进的结果。但是,如果我们事先无法接触到环境,因此无法提供演示,会发生什么情况呢?这种方法可以与 Tutor4RL 结合使用,当代理已经部署在其环境中时,使用 Tutor 来提供演示。
Tutor4RL
我们希望我们的代理从一开始就表现得好(或者至少体面),但是当我们事先无法访问环境时,这是非常困难的,如果我们对环境做出任何假设,而这些假设是错误的,那么代理的表现将会受到影响,不仅是最初,而是贯穿其整个生命周期。但是,这并不意味着我们不能提供一些有用的信息,代理一旦部署到环境中就可以使用这些信息。
最后,当我们学到一些东西时,我们并没有被告知我们将会遇到的每一种情况的所有细节。相反,我们可以从理论观点和其他人给我们的提示中学*。一个 RL 特工能做到这些吗?
为了做到这一点,我们修改了 RL 框架,如下图所示:
与 Tutor4RL 框架相比的标准 RL 框架[摘自原始出版物。]
我们添加了一个组件,我们称之为 Tutor,它包含了外部知识,是一个知识函数的集合。这些函数是普通的可编程函数,将状态和奖励作为输入,并输出一个向量,该向量带有每个动作的值,其方式类似于策略如何将状态映射到带有 Q 值的动作。有两种类型的知识函数:
- 向导功能:这些功能向代理表达向导或提示,并被解释为建议,即使它们是错误的,代理也会根据自己的经验得知它们不好,不会遵循它们。
- 约束函数:这些函数限制了智能体的行为,当我们确定智能体在某个场景中不应该做某件事的时候,它们会很有用,也许是因为这可能会伤害到它自己,或者可能会导致一些危险的情况。约束函数告诉代理不要做什么,输出一个向量,其中 1 表示可以采取的每个动作,0 表示不可以采取的每个动作。
当代理不确定要做什么时,例如在最初的步骤中,它可以向导师询问它的知识。指导者将回复它的功能集合,然后代理将使用它来选择要执行的动作,并从这个经验中学*。以这种方式,指导者指导代理,但是代理总是能够发现来自指导者的建议是否是错误的,或者通过使用一些探索机制,例如ε-贪婪,还能够发现是否存在比指导者的选择更好的行动。然而,约束函数总是应用于向导函数和代理的策略,因此它提供了一个安全层来避免可能使代理处于危险中或对任务有很*负面影响的严重错误。
估价
外部环境:从 OpenAI 健身房突围。
我们已经在 Python 上使用 Keras-RL 实现了 Tutor4RL 的原型。我们已经将 Tutor4RL 应用到一个 DQN 代理(Mnih et al. 2015) ,在 OpenAI Gym 上玩突围。我们使用了一个简单的引导函数,如果球不在横杠的正上方,它告诉代理向球所在的方向移动(不是说在这个测试中,我们没有使用约束函数)。此外,我们实现了一个非常简单的方法来控制代理何时使用导师的指南:我们定义了一个名为τ的参数,其使用方式与ε在ε-贪婪探索中的使用方式相同。当τ*于取自 U(0,1)分布的样本时,我们使用导师的输出,否则,我们使用策略的输出。我们将代理初始化为τ=1,并随时间线性减少,因此当代理启动时,导师的输出被*量使用,但随着代理积累更多经验,这种情况会减少。
下面,你可以看到 DQN 代理与 Tutor4RL 相比标准的普通 DQN 代理所获得的回报。突围中的奖励直接是游戏上达到的分数,所以你突破的格挡越多,分数越高,奖励也越高。正如你所看到的,在普通的 DQN 代理苦苦挣扎的时候,由于导师的指导,带 Tutor4RL 的 DQN 代理显示了最初的良好表现。普通 DQN 代理需要*约 130 万次迭代才能赶上,在 150 万次迭代中,导师完全停止使用。请注意,在此之前,导师是间歇性使用的,取决于每次迭代中减少的τ值。在那之后,我们可以看到两个代理有相似的表现,显示出被辅导的 DQN 代理也学会了但避免了普通 DQN 代理的粗糙开始。
与普通 DQN 代理人相比,使用 Tutor4RL 的 DQN 代理人的平均报酬【摘自原始出版物。]
结论
Tutor4RL 已被证明能够帮助代理从其环境的一些知识开始,提高其初始步骤的性能。然而,Tutor4RL 仍在发展中,有几个方面可以改进:
- 我们只测试了引导函数,所以接下来的步骤是实现约束函数的机制以及一些示例约束函数并测试它们。
- 用τ管理不确定性是一种幼稚的方法,可以通过使用其他方法得到很*改善,如自举法( Kahn 等人(2017) )或贝叶斯法(Clements 等人(2020))。
- 可能有更好的方法来集成和使用来自知识函数的输出向量,利用函数之间的相关性,如通气管(Ratner et al. (2019)) 。
一如既往的感谢阅读!我希望你对这种方法感兴趣,并期待听到你的反馈。我相信还有更多方法可以改进 Tutor4RL。
电视节目对人工智能感到害怕是正确的——因为所有错误的原因
我们已经多次看到好莱坞对人工智能灾难的处理,但这个行业及其消费者实际上应该担心什么?
去年夏天,当福克斯公司宣布他们的新“科幻小说”击中“下一个”时,该节目的描述听起来类似于黑镜或菲利普·k·迪克(Philip K. Dick)的《电梦》(Electric Dreams),这是一个关于控制技术对其用户的侵蚀的警示故事:
“接下来是一部推进性的、基于事实的惊悚片,讲述了一种致命的、流氓式的人工智能的出现,它结合了脉冲冲击行动和对技术如何入侵我们的生活并以我们尚不了解的方式改变我们的分层检查。”
然而, 从它的预告片 来看,这部剧的故事情节离这个总结太远了。预告片以一个类似 Alexa 的东西开场,一切看起来都很好,直到它获得了自己的意志,以邦德超级反派的微妙和冗长肆虐并反抗其用户和人类创造者。这种试图用“入侵者”的形象来描绘社会恐惧的赫伯特·乔治·威尔斯方法可能对世界之战或哥斯拉有用。然而,在这种情况下使用人工智能会严重误导我们对人工智能和决策算法的现实担忧,其中许多都严重影响了我们生活在其中的非虚构现实中的人类生活。
这不仅仅是数据隐私
要明确的是,数据隐私是一个组成部分。你应该关心你的数据,人工智能的支柱,去哪里了。越来越不可避免的是,作为社会的积极参与者,你将被迫把你的品味和行为的很*一部分交给数据收集公司。这些公司的唯一目的是从数据中榨取尽可能多的价值,如果不成为数字隐士,我们对此几乎无能为力。
你知道吗,不仅你可以通过点击和在线搜索被跟踪和识别,你的身体动作也可以被跟踪和识别。那些开始覆盖笔记本电脑网络摄像头的人可能在正确的道路上,但可能没有听说过行为生物测定学:通过这些技术,你在键盘上打字的节奏或你鼠标的移动模式等细节可以将你与你的身份联系起来(了解如何在这里停止)。
现在,我不是说你不应该尝试最小化你的数字数据足迹。此外,围绕数据隐私工作的对话也应该关注公众对更严格的安全和隐私政策的强烈要求。在 Vox 播客“Recode Decode 的最近一集,DuckDuckGo 的首席执行官 Gabe Weinberg 认为,选择加入数据跟踪应该是默认的,而不是许多数据公司实施的模糊的选择退出方法。这是许多欧洲国家通过政策认同的一个流行观点,需要在全球范围内采纳。
行为转变策略
数据公司已经意识到,他们不仅可以使用他们的资产和工具进行数据收集和行为预测,而且他们现在可以部署他们的洞察力来转变行为,有效地操纵我们未来的行动,为他们带来最佳利益。在《监视资本主义的时代》一书中,作者肖莎娜·祖博夫认为 资本主义行为分析的最终结果可能不仅仅是对我们隐私的威胁,而是对我们非常自由的意志的威胁——预设决策和自我实现预言的决定性景观。祖博夫认为:
“监视资本主义单方面宣称人类经验是转化为行为数据的免费原材料。虽然这些数据中的一些被用于服务改进,但其余的被宣布为专有的行为盈余,被输入到被称为“机器智能”的先进制造流程中,并被制作成预测产品,预测您现在、不久和将来会做什么。最后,这些预测产品在一种新的市场中交易,我称之为行为期货市场。监视资本家从这些交易操作中变得非常富有,因为许多公司愿意对我们未来的行为下注”
在资本主义思维控制下,不受监管的数据跟踪和操纵的影响应该是显而易见的。从对我们行为的威胁到民主本身,我们不需要一个电脑化的超级恶棍来想象一个真实存在的人工智能威胁。如果我们想在自主性完好无损的情况下走出这个新的数字黄金时代,一种稳健且基于道德的数据政策实施方法是根本。
没有无偏见这回事
偏见无处不在,算法于事无补,它们模糊了问题,加剧了问题。数据和算法不是我们世界的客观反映;他们带着来自创造他们的文化和组织的同样的偏见、成见和误解。除了它们隐藏在鼓励对这些偏见缺乏问责的包装中,使得更容易证明将决定论误认为客观性是正确的。通过实施决定谁优先接受医疗护理、谁涉嫌犯罪或向儿童推荐什么在线内容的系统,我们正在增加问责制的缺乏,而没有解决紧迫的问题。
Rachel Thomas 是旧金山*学数据研究所的教授,也是 Fast.ai 的联合创始人,她曾就这个主题发表过长篇*论:
“我们的算法和产品影响着世界,是反馈回路的一部分。考虑一种预测犯罪并确定向哪里派遣警察的算法:向特定的街区派遣更多的警察不仅是一种效果,也是一种原因。在给定的街区,更多的警察可以导致更多的逮捕,这可能会导致算法向该街区派遣更多的警察(这种机制在关于失控反馈循环的论文 中有所描述)。
与欧洲血统的人相比,计算机视觉算法 在有色人种 上表现不佳,因为绝*多数面部数据来自欧洲血统的人。 M ost 收集的行为数据来自发达国家的富裕地区 ,自动填充算法更有可能为男性建议 STEM 相关的职业术语,为女性建议家政术语。这些刻板印象不是恶意的结果,而是反映在数据广度和宽度上的社会和个人偏见。然而,不管来自何方,这些问题都需要解决。
算法和数据远非客观真理的仲裁者,而是我们偏见的载体,这种偏见非常丰富。缺乏对那些执行、部署、消费和管理它们的人的理解,导致我们被我们创造的有偏见的、可操纵的机器推下悬崖。自人工智能诞生以来,伦理考量就一直是其核心的重要组成部分。因此,等到损害已经造成,或者相信巨额罚款的威胁不仅仅是解决更*问题的权宜之计,都是错误的。
认知偏差法典,设计:约翰·马诺吉安三世,类别和描述:巴斯特·本森,实现:蒂尔曼纳[CC BY-SA]
我们能做些什么呢?
可悲的是,解决这些问题不会像“下一个”电视节目让我们相信的那样,像号召邋遢顽固的侦探打败 Alexa 的邪恶表弟那样容易。这里没有“其他人”,没有来自错误实验的流氓人工智能,只是对我们社会偏见和疑虑的认可,包装成闪亮的代码行。
我们已经知道如何处理偏见。人类以前已经足够聪明,能够(在不同程度上)认识到并纠正它们。挑战在于我们是否有能力将这些解决方案应用到我们曾经认为公平或客观的系统中。这样做的下一步是为负责数据流程中每个步骤(数据的收集、存储、探索、建模和部署)的人员建立限制和问责制。此外,我们需要始终意识到偏见是多么容易被忽略,并理解算法可能会犯错误——频繁,而且比预期的影响更*。最终取得成功意味着准备好主动纠正问题,如果不能,在损害不可逆转之前放弃算法。
在 InfoQ 的一次介绍会上 ,Rachel Thomas 概述了一些我们上面讨论的问题的解决方案,比如:
- 不要最*化指标 —想要数字增加很容易,但我们越是将数据简化为数字,就越容易遗漏错误并奖励我们提供的服务中存在的负面行为,或者启用不公正地歪曲所述服务的限制性反馈循环。
- 雇佣多元化的团队 —研究表明多元化的团队表现更好,从分析角度来看,他们有助于更好地理解数据集所包含的主题,带来更好的、可操作的见解,并确保偏见不是来自你的团队。
- “先进的技术不能代替好的政策,”托马斯说。偏见很难捕捉;不管你的算法或者管道有多好。需要制定法规和协议,以便用户可以对算法做出的决定提出上诉。
- 永不放弃 —我们永远不会达到人类和我们的算法没有偏见的地步。我们一开始就是这么过来的!我们有责任训练人们时刻警惕反馈循环、偏见和我们数据中隐含的偏见。
最后,把黑白场景留给周末科幻电视节目。
使用 Python 对网飞和亚马逊 Prime Video 等在线流媒体服务上的电视节目进行可视化比较
使用 Python 的 Seaborn 和 Matplotlib 库创建关于流行在线流媒体服务的信息图的比较研究
简介:
在疫情期间不会倒闭的为数不多的服务中,有像网飞和亚马逊 Prime Video 这样的在线流媒体服务。看看下面的谷歌趋势结果,从 1 月 1 日到 6 月 12 日。在 2020 年 3 月中旬左右,新冠肺炎和网飞的网络搜索量明显上升,当时新冠肺炎病例在全球范围内飙升,企业因不可避免的封锁而关闭。还有一个有趣的每周模式,搜索数量在周末增加。
网飞和科维德在谷歌上的搜索次数时间表。谷歌表示,这些数字代表了在给定地区和时间,相对于图表最高点的搜索兴趣。这种明显的增长在 3 月中旬尤为明显,当时新冠肺炎病例在全球范围内飙升,企业纷纷关门。此外,还有一个很好的周线模式。
谷歌搜索的受欢迎程度:
我想,到目前为止,最受欢迎的服务是网飞。为了支持我的假设,这里有一张亚马逊 Prime Video、网飞、Disney+和 Hulu 日均搜索量的柱状图。
在左边的分布中,从年初到 6 月 12 日,平均每天有 56 次对网飞的搜索,其次是 9 的亚马逊 Prime Video、迪士尼+4和 4 的 Hulu。
如果我们用日均谷歌搜索量来衡量受欢迎程度,那么网飞无疑以较*优势高居榜首。看起来,网飞的受欢迎程度是亚马逊 Prime Video 的六倍。迪士尼+和 Hulu 的受欢迎程度只有亚马逊 Prime Video 的一半,不及网飞的 14 倍。唷!
订户比较:
每个在线流媒体服务的用户都以百万计。(数据来源:https://en.wikipedia.org/wiki/Streaming_service_provider)
网飞的用户数量几乎是亚马逊 Prime Video 的 2.5 倍。两者都是全球服务提供商,而 Disney+和 Hulu 都有特定的目标国家。
Disney+和 Hulu 属于同一个母公司,华特·迪士尼公司。Disney+成立还不到一年,其订阅用户比 Hulu 多 80%,尽管 Hulu 有更多的节目和更好的目标观众分布(参见以下部分的可视化)。Hulu 于 2007 年推出,同年网飞也推出了。亚马逊 Prime Video 比他们早一年在 2006 年推出。
令人惊讶的是,Walk Disney 公司在线流媒体服务的用户总数无法超过网飞的用户总数。更确切地说,亚马逊 Prime Video、Hulu 和 Disney+这三家网站都无法与网飞的用户总数相提并论。
数据来源:
以下是所用 Kaggle 数据集的链接:
[## 网飞、Prime Video、Hulu 和 Disney+上的电视节目
这些流媒体平台上的电视节目集
www.kaggle.com](https://www.kaggle.com/ruchi798/tv-shows-on-netflix-prime-video-hulu-and-disney)
步骤 1:清理数据
数据集看起来像左边的表。
该数据集有 5611 个缺少年龄、IMDb 和烂番茄评分的实例。我没有删除这些行,而是仅用字符串“N/A”填充了“Age”。我还使用 str.replace 删除了烂番茄评级中的“%”符号。评级网站的绘图不需要 NaNs 进行屏蔽,尽管我必须为烂番茄对它们进行一些预处理。因为这些是数字列,所以在浏览和可视化数据时,零会导致数据集中不必要的偏斜,除非经过过滤,这种情况发生在我身上(是的..傻)。详细的代码可以在我的知识库中找到(博客底部的链接)。
我还将所有的电视节目标题转换成小写字母,并去掉标点符号,以查看名称的重叠部分。此外,我创建了另一个包含电视节目名称和发行年份的列,以区分 ABC Show 1991 和 ABC Show 2020。
步骤 2:创建可视化效果
对于视觉效果,我选择了网飞、亚马逊 Prime Video、Disney+和 Hulu 的颜色代码。
# My Custom Palette for the Services
pallete_dict = {
'Netflix': ['#A9A9A9', '#E50914'], # Red
'Prime Video': ['#A9A9A9', '#00A8E1'], # The Lighter Blue
'Hulu' : ['#A9A9A9', '#3DBB3D'], # Green
'Disney+' : ['#A9A9A9', '#113CCF'] # Darker Blue
}
颜色:#E50914,#00A8E1,#3DBB3D,#113CCF
Viz 1:每个在线流媒体服务上电视节目数量的条形图
ax = sns.barplot(y=counts_list, x=list(pallete_dict.keys()),
palette= ['#00A8E1', '#E50914', '#3DBB3D',
'#113CCF'],
order=['Prime Video', 'Netflix', 'Hulu', 'Disney+'] )
*reset_bar_width(ax, 0.3)*
plt.ylabel('Number of TV Shows')
plt.grid(True, axis='y', color='grey')
plt.savefig('demo6.png', transparent=True)
我创建了 reset_bar_width 函数来动态设置条形宽度,因为我喜欢右边的条形图。
这里的代码用于设置条形宽度:
def reset_bar_width(ax, new_width):
for patch in ax.patches:
old_width = patch.get_width()
patch.set_width(new_width)
shift = old_width - new_width. # To re-align the labels
patch.set_x(patch.get_x() + shift * .5)
Viz 2:散点图,用于沿着数据集中的整个时间线绘制电视节目
为了生成这个图表,我过滤了 1930 年以后的电视节目(它们是不正确的),然后根据每个在线流媒体服务中的存在或不存在进行分组。在这些组中,我将电视节目按字母顺序排序,以注入随机性——不确定是否需要。这些图表看起来不错:
col_names_online_media = ['Netflix', 'Prime Video', 'Disney+',
'Hulu']for col_name, num in zip(col_names_online_media,
range(1,len(col_names_online_media) + 1)):
ax = fig.add_subplot(2, 2, num)
sns.scatterplot(x="Year", y="Title Unique Labels",
palette = pallete_dict[col_name],
hue=col_name,
data=df[df.Year > 1930].sort_values(by=
[col_name,
'Title Unique Labels']),
ax=ax)
sns.despine
ax.set_title('TV Shows on ' + col_name, fontsize=25)
handles, labels = ax.get_legend_handles_labels()
ax.legend(loc='upper left',
frameon=None,
edgecolor='black',
fontsize=15,
framealpha=0.2,
handles=[handles[2]],
labels=['On ' + col_name])
ax.set_xlim(1930, 2022)
ax.set(yticklabels=[])
ax.set(ylabel=None, xlabel=None)
fig.tight_layout()
for ax in fig.get_axes():
ax.label_outer()
plt.savefig('demo1.png', transparent=True)
plt.show()
即 3:所有在线流媒体服务的目标年龄组
有五个明确界定的目标年龄组:7 岁以上、13 岁以上、16 岁以上、18 岁以上和所有年龄组。几乎 44%的数据是未知的,所以我把它们标为“不适用”。有趣的是,Disney+拥有最多适合儿童观看的节目,而且收视率相当稳定,我们将在下面的可视化中看到这一点。
palette_list=['Reds_r',
['#0BB5FF', '#000000', '#63D1F4', '#98F5FF',
'#C1F0F6', '#E0FFFF'],
['#00009C', '#000000', '#000000', '#000000',
'#003EFF', '#CAE1FF'],
'Greens_r' ]
**for** col_name, num **in** zip(col_names_online_media,
range(1,len(col_names_online_media) + 1)):
data = df[df[col_name] ==1].groupby(by='Age')
.count()
.reset_index()[['Age', col_name]]
data[col_name] = data[col_name]*100/len(df)
ax = fig.add_subplot(2, 2, num)
sns.barplot(x="Age",
y=col_name,
data=data,
order=['7+', '13+', '16+', '18+', 'all', 'N/A'],
palette=palette_list[num-1])
reset_bar_width(ax, 0.3)
ax.set_title('Age Restriction Distribution of ' + col_name,
fontsize=15)
ax.set_ylabel('Percentage of TV Shows')
ax.set_ylim(0, 25)
ax.grid(**True**, color='grey', axis='y')
fig.tight_layout()
plt.savefig('demo3.png', transparent=**True**)
plt.show()
正确图形
即 4:可视化每个电视节目分级网站对每个在线流媒体服务的分级的集中和范围
箱线图是可视化连续变量分布的最佳方式。正如我之前提到的,Disney+的一致性在这里显而易见。网飞、亚马逊和 Hulu 以相似的评分分布相互竞争。总的来说,网飞的范围更广。
palette_colour_from_dict = [v[1] **for** v **in** list(pallete_dict.values())]
sns.boxplot(x='Variable',
y='Value',
palette=palette_colour_from_dict,
order=list(pallete_dict.keys()),
data=df_box_plot_prep_imdb)
*# plt.xlabel('Online Streaming Media')*
plt.xlabel(xlabel=**None**)
plt.ylabel('IMDb Ratings')
plt.grid(**True**, axis='y', color='grey')
plt.savefig('demo4.png', transparent=**True)**
Viz 5:电视节目标题中最常见的词
我试图创建一个三角形的文字云来适应信息图。我很少用这个,但我真的很想用。我的意思是我用了一个透明的版本,因为颜色、间距和样式不匹配。
mask_image = 'path_to_my_jpg'
image_mask = np.array(Image.open(mask_image))
image_colors = ImageColorGenerator(image_mask)
wordcloud = WordCloud(width = 2000, height = 1200, max_words = 500,
normalize_plurals = **True**, colormap='YlOrBr',
mask=image_mask,
background_color = 'white', font_step = 1,
relative_scaling = 0.8,
collocations = **False**,
include_numbers = **False**)
.generate(' '.join([each_token.upper()
**for** each_token **in** token_list]))
plt.figure(figsize = (20,13))
plt.imshow(wordcloud)
plt.axis("off")
plt.savefig('demo7.png', transparent=**True**)
plt.show()
Viz 6:电视节目标题中十*常用词的条形图
这将生成一个带有标签和值的条形图。我对实际信息图中的图表进行了一点格式化。
我首先从我的信息图模板(#FFC000)中选择十六进制代码。这次我使用了 power-point,颜色选择器工具对获取自定义十六进制代码非常有帮助。单词的文本位置很容易。我选择了 x 的一个常数函数,并画出了单词。对于数字,我选择了一个常数来绘制第一个文本,并根据当前和以前的条形高度之差(或者换句话说,字典中当前和以前的字数之差)来减少它,以绘制其余的文本。我根据剩下的数字调整了图中的第一个数字(102 ),因为它多了一个数字,而且没有完全对齐。
这是代码,下面是图表:
ax = sns.barplot(x=list(word_freq_dict.keys()),
y=list(word_freq_dict.values()),
palette = ['#404040'] * 10)# Set up labels
for i, xlabel_text in enumerate(list(word_freq_dict.keys())):
# text placement for the top 10 words
ax.text(i - .095 , 5, xlabel_text, color='#FFC000',
fontweight='bold', rotation=90, fontsize=20)
if i == 0:
ax.text(i - .095 , 93, word_freq_dict[xlabel_text],
color='#FFC000', fontweight='bold', rotation=90,
fontsize=20)
else:
ax.text(i - .095 ,
95-(list(word_freq_dict.values())[0] -
list(word_freq_dict.values())[i]),
word_freq_dict[xlabel_text], color='#FFC000',
fontweight='bold', rotation=90, fontsize=20)
plt.yticks([])
plt.xticks([])
reset_bar_width(ax, 0.5)
plt.savefig('demo8.png', transparent=True)
前 10 个词是爱情、世界、表演、生活、战争、故事、女孩、冒险、男人和美国人。我原以为《爱》、《生活》和《世界》会进入前 10 名,现在它们出现了——“爱”一路领先。
然后我制作了一个信息图:
我用 Microsoft Power Point 把所有的图表和计算累积成一个信息图。我给*纲加了框,在标题和首页文字上使用了“百老汇”字体。
下载: PDF 链接
这是我第一次尝试创建一个信息图,很快就会使用 Tableau。我很想听到一些关于这个创作的反馈,因为这是我的第一次尝试,并希望改进。
存储库链接:
[## royn5618/Medium_Blog_Codes
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/royn5618/Medium_Blog_Codes/blob/master/Online Streaming Services Comparison.ipynb)
感谢光临。我希望你喜欢阅读这篇博客。
调整中国的冠状病毒技术策略以击败西方的病毒
一个高科技系统,类似于中国的系统,可以有效地抗击新冠肺炎病毒,同时保护个人隐私
编者按: 走向数据科学 是一份以数据科学和机器学*研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
中国政府利用先进技术建立了世界历史上最有效的流行病控制系统。最近几周,中国的疫情疫情得到控制,每天只有 50 例报告。对于一个拥有 14 亿居民的国家来说,这是一个巨*的成功。然而,该系统受到西方世界人士的广泛批评,因为它要求获得有关中国人行踪的私人信息;他们的聊天记录、社交联系、购物记录和其他信息。这些数据随后被存储在中央数据库中,并通过先进的人工智能(AI)算法来对抗病毒的传播。在下面的文章中,我们将提出一个同样有效的类似系统,但它解决了个人的隐私问题。个人的个人数据永远不会离开这个人的设备,但它仍然有助于中央系统识别潜在的热点。
这种系统的唯一要求是人们在他们的移动设备上安装一个应用程序。与中国的系统不同,它不需要分布在全国各地的视频监控摄像头或安装在住宅小区门口的生物扫描仪。人们也不需要进出他们的生活区。
该系统采用移动应用程序的形式,类似于中国现有的微信,并且它还集成了不同的服务。因此本地化、社交媒体、聊天和电子钱包都将可用。然而,这是相似之处的终点。用户的手机存储了所有的个人信息,而不是一个中央服务器。用户将拥有对数据的完全访问权,他可以选择保留或删除数据。此外,该系统将使用两个 AI 组件,一个位于远程,另一个在设备本身上,如下所述。
就电子钱包而言,第一个优势是使用非接触式虚拟卡进行支付,无需兑换真实货币。因此,这种物理交换的缺乏自动减少了病毒的传播。
电子钱包还包括购物时所逛商店的信息。该系统存储商店的位置,并在用户的设备中记录时间。这条信息然后在稍后阶段由定位模块处理。从购买中,本地人工智能可以收集关于个人健康的信息。它可以很容易地推断出,如果购买了某些物品(如药品),那么这个人或他身边的人很可能生病了。然后,人工智能开始与用户聊天,询问他或他身边的人是否有一些症状,并提出一种前进的方式。这些可以包括开始自我隔离,预约拭子或采取任何适当的措施。重要的是选择权仍在用户手中,任何部门都不能共享任何信息。这样的人工智能系统是可能的,因为今天,我们有人工智能引擎,它们可以在移动设备上有效地工作,而不必与服务器通信。一个这样的工具是专门为设备上的机器学*设计的 TensorFlow Lite。
此外,移动医疗的应用也在不断增加。有各种各样的症状追踪应用程序帮助用户做出适当的决定,他们是否需要制度化的护理。它还可以缓解他们可以自己处理的小病的症状。如果用户需要特定的帮助,it 可以直接将他与适当的提供商联系起来。无国界医生组织甚至更进一步,创建了第一个移动分诊应用。卡耐基梅隆*学走得更远,刚刚推出了第一个 COVID 语音检测器,它声称只需通过手机通话就可以测试用户是否携带病毒。当然,这个系统仍然是实验性的,他们不能保证它的准确性。
照片由 henry perks 在 Unsplash 上拍摄
定位模块提供关于人的行踪的精确信息(误差在几米之内)。当然,这些信息只存储在设备上,不会与任何人共享。我们可以通过一种叫做联合学*的新颖人工智能方法,在不泄露用户身份的情况下使用这些信息。该系统的工作原理如下:该设备从服务器下载 AI 模型,并通过从手机上的数据中进行学*,在本地对其进行改进。新模型的概要被创建并发送到云。没有任何个人信息会离开设备,也不可能从发送的更新中提取任何信息,因为它只是一个摘要。通过设备上的人工智能模型,系统知道城市的哪些区域是危险的。如果用户密切接触到有人感染病毒的地方,人工智能会立即提醒他,并让他避开该地区。
通过社交媒体模块,如果用户被感染,他可以很容易地向他的社交朋友圈发送警告消息,并提醒他们,以便他们采取必要的预防措施。
系统还可以自动计算健康代码,该代码可以是:红色、琥珀色或绿色。这种编码是为了帮助用户做出保护他人安全的选择。绿色意味着这个人可以不受限制地自由走动。刚从国外回来或可能到过疫区的人会有黄色代码,系统会建议他们在几天内限制活动。那些可能被感染的人属于红色类别,他们将不得不被隔离。
这种应用的范围不是取代手动系统,而是通知人们并指导他们采取适当的行动。它还通过利用人工智能的力量来接触更多的人,从而缓解了医疗保健系统的压力。最后,没有一个系统是完美的,实现平衡是不容易的。然而,通过将中国系统的好处与*多数西方国家期望的隐私标准结合起来,我们将拥有一个新的强*工具,可以自动拯救无数生命。
这篇文章最初发表在 https://www.businesstoday.com.mt请在下面留下你的想法,如果你喜欢这篇文章,请随时关注我🐦推特,🔗 LinkedIn 或😊脸书。
新冠肺炎如何对我们的经济产生负面影响以及可能的解决方案
towardsdatascience.com](/the-domino-effect-of-the-coronavirus-66fdd72fc9fd)*
Alexei DingliProf 是马耳他*学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他出版了几本同行评审的出版物,并且是马耳他的成员。人工智能工作组是由马耳他政府成立的,旨在使马耳他成为世界上顶尖的人工智能国家之一。
特威迪损失函数
长尾右偏数据的损失函数
如今,观察具有右偏分布和长尾的数据并不罕见,例如,来自电子商务网站、广告点击&购买、保险索赔等的数据。
在这种情况下,如果使用其他分布的损失函数(例如,高斯分布的 MSE ),预测模型可能不会被很好地训练。在本文中,我们介绍了一种使用基于 Tweedie 的损失函数的方法,用于在这种长尾右偏数据上训练的模型。
特威迪分布
Tweedie 分布是带有幂参数 p 的指数离差模型(EDM)的特例,其分布均值和方差之间的幂关系如下
其中 μ 和 ϕ 分别是均值和离差参数。
功率参数 p 决定了族内分布的子类。例如, p =1 链接到泊松分布, p =3 链接到逆高斯分布,而本文的重点, p∈ (1,2)链接到复合泊松-伽马分布。
特威迪分布族的普通成员。
假设是复合分布,随机变量 X ~ Tw ( p,μ,ϕ )可以刻画为
x 的概率密度函数(pdf)为
其中 α ( x,ϕ,p )是一个归一化常数,确保这是一个有效的 pdf。
作为损失函数的特威迪
为了将 Tweedie 分布转换为损失函数,我们需要通过模型训练来最*化样本数据的可能性。一种常见的方法是通过负对数似然
如果我们使用分布均值 μ 作为估计量(即预测),那么损失函数变成
履行
给定预定义的功率参数 p (超参数),Tweedie 损失函数可用于各种基于梯度的模型,如梯度提升树、神经网络等。下面给出了一个实现的例子。
import tensorflow as tf
def tweedie_loss_func(p):
def tweedie_loglikelihood(y, y_hat):
loss = - y * tf.pow(y_hat, 1 - p) / (1 - p) + \
tf.pow(y_hat, 2 - p) / (2 - p)
return tf.reduce_mean(loss)return tweedie_loglikelihood
参考
鹤舟先生,杨熠,钱伟。"对于极度不平衡的零膨胀数据的梯迪梯度提升."2019.
阿里特拉·哈尔德,沙里克·穆罕默德,陈坤,迪帕克·戴伊。" Tweedie 复合泊松双广义线性模型中的空间风险估计."2020.
用神经网络生成推文:LSTM 和 GPT-2
我们的递归神经网络能在多*程度上近似政治人物和艺人的推文?
来源: PhotoMIX-Company ,via: pixabay
witter 是数百万人就任何话题发表意见的地方,它也成为名人和他们的粉丝互动的地方。这启发我们探索递归神经网络的局限性,看看它们是否可以用于 Twitter 等社交网络的自动文本生成。作为我们人工神经网络和深度学*课程的最终项目,我们的目标是为 Twitter 上的 10 个受欢迎的帐户生成推文,使用 NLP 技术分析它们,并比较结果。此外,我们还考察了不同的网络,并评估了各种可能性。我们决定观察政治家和艺人,自己建立一个网络,并找到另一个预先训练好的网络来比较结果。
数据收集
我们使用在 Github 上找到的这个库从 10 个公众人物的账户中挖掘推文。出于对群体差异及其对训练我们网络的影响的兴趣,我们选择了五位政治人物和五位艺人:
政治人物:唐纳德·特朗普(@realDonaldTrump),伯尼·桑德斯(@BernieSanders)。伊丽莎白·沃伦(@ewarren)、亚历山*·奥卡西奥-科尔特斯(@AOC)、比尔·盖茨(@比尔·盖茨)
艺人:贾斯汀比伯(@justinbieber)、爱莉安娜·格兰德(@arianagrande)、凯文·哈特(@KevinHart4real)、勒布朗·詹姆斯(@KingJames)、克莉茜·泰根(@chrissyteigen)
对于每个公众人物,我们挖掘了他们最近的 10,000 条推文(请注意,对于少数人物,他们完整的推文历史总计不到 10,000 条,所以我们只是使用了可用的内容)。对于有额外推文的数字,我们收集了另外 1 万条来训练 LSTM 网络。
数据清理:对于我们的训练数据,我们删除了任何只包含“pics.twitter”链接的帖子(在 twitter 上,这些链接显示为照片和视频)。我们还想分析我们的训练数据,并识别推文中的任何模式,这需要额外的清理。对于词频分析,我们删除了标点符号和停用词(由 nltk 库定义)。此外,我们用关键字“http”替换了剩余的链接,以跟踪它们的频率。
通过 NLP 技术评估 Twitter 数据
词频分析
在所有 10 个人中,“http”是使用频率最高的术语之一,这表明他们都相对频繁地在推文中发布链接。因为我们只使用关键字“http”作为链接频率的指标,所以在我们的可视化中没有考虑它。
政治人物:常用词
五位政治人物的前三名都是“人民”这些政治人物深入参与社会事务,并做出影响*多数人的决策。他们的工作围绕着人,所以“人”被他们所有人共同使用是有道理的。
十个最常用的单词象征着每个人的修辞和说话风格。例如,人们已经开始将某些词与特朗普的浮夸风格联系起来——“伟*”、“让美国再次伟*”、“*”和“许多”。列表中还有“假”和“新闻”——特朗普创造的另一个标志性术语。对桑德斯来说,“健康”+“护理”排在第五和第六位。对沃伦来说,“打架”和“打架”都在前十名。
有趣的是,特朗普第四*常用词是“民主党”,即反对党。这说明特朗普在推特上花了多少时间谈论两极化、党派问题。同样,桑德斯和沃伦的十*词汇中都有“特朗普”。
AOC 的前十个单词比前三个更通用。有趣的是,她第三个最常用的词是“谢谢”,这(我们接下来会看到)是艺人推特的典型特征——也许是因为她是迄今为止最年轻、最懂社交媒体的政治人物。虽然比尔·盖茨没有直接参与政治,但他也参与了塑造我们的社会和社区。他的人道主义观点体现在他的前十个词中,包括“世界”、“一”、“帮助”和“进步”。
艺人:常用词
艺人使用社交媒体作为与粉丝互动的平台,他们*量使用“感谢”和“爱”这两个词就是明证。像政治人物一样,他们常用的术语体现了他们的个人风格和方言。哈特是最明显的例子,他创造了两个术语——“dope pic”(如#DopeEpic)和“comedicrockstarshit”——在用法上排名第六和第八。一般来说,艺人的词汇云提供的洞察力不如政治人物,因为他们经常使用 Twitter 进行宣传和简短的粉丝互动。然而,政治人物使用 Twitter 来传达关于他们的立场、政策、时事等更复杂的信息。
政治人物与艺人:平均推文长度
我们发现,平均而言,艺人的推文长度*约是政治人物推文长度的一半。我们可以看到政治人物的训练数据和艺人的训练数据之间的差异如何影响我们的网络生成的推文。随着训练的推文越来越长,我们的网络为政治人物生成的推文的质量和相似度也越来越高。
情绪分析
我们使用 VADER(用于情感推理的效价感知词典)工具对训练数据进行情感分析。我们采用复合得分——一种计算所有词汇评分总和的指标,这些词汇评分在-1(最极端的负面)和+1(最极端的正面)之间标准化——来比较各组的情绪。
与政治人物相比,艺人的推文数量远高于 0.0(中性)。从柱状图来看,艺人有更多的轻尾分布。许多艺人使用社交媒体平台进行推广(中性)和粉丝互动(积极)——最明显的是比伯和格兰德。詹姆斯显示了*量积极情绪的推文,这可能与他的激励信息有关。Teigen 在所有艺人中有更多负面情绪的推文,这可能归因于她直言不讳的本性。
另一方面,政治人物箱线图的 IQR 通常比艺人的宽,即中间 50%的数据落在更*的范围内。对于政治人物来说,可能需要通过表达批评(消极情绪)或争取支持(积极情绪)来吸引公众。
川普拥有最*的 IQR,从-0.50 到 0.75。条形图表示在他的推文中显示了双峰分布情绪。情感模式证实了他对戏剧和表演的天赋;他倾向于强烈批评或高度赞扬。沃伦和盖茨拥有最偏右的分布,这证明了他们高度乐观和积极推动社会变革的声誉。
为推文生成选择神经网络
我们希望创建我们自己的 LSTM 网络,此外还有一个最新的最先进的网络,以便比较结果并了解它们如何工作的特点。我们还考虑开发一个用于文本生成的 GAN 网络,因为我们试图生成看起来像特定用户的推文的推文,所以我们创建了一个鉴别器,并做了一个反向投影,帮助生成器生成更好的输出,看起来像我们的初始训练数据会产生更好的结果。以下是我们发现的每个网络(Transformer XL、CTRL 和 GPT-2)的利与弊的简要概述,以及我们最终选择 LSTM 和 GPT-2 的原因。
LSTM
LSTM(长短期记忆)是一种递归神经网络(RNN)。我们选择创建一个 LSTM 模型来为 10 位政治家和艺人生成新的推文,因为这个特定的模型学*并掌握风格、语言和语法,因为 LSTM 能够检测和学*长期的依赖性。在学*输入文本的序列时,模型能够生成新的文本。
LSTMs 使用一种可以被描述为循环效应的学*过程,这意味着网络解释输入并产生输出。该输出及其信息随后被传递到网络中的下一个步骤。这是很重要的,因为它允许模型拾取文本中的模式,以便生成与所讨论的文本风格相似的更好的输出。
变压器-XL
我们考虑实施 Transformer-XL 网络的主要原因是它的效率。与其他广泛传播的语言模型相比,Transformer-XL 不能捕获文本中的长期依赖关系,它能够识别单词级和字符级的上下文连接。它通过将变压器网络的概念与相对位置编码相结合来实现这一目标。传统的转换器能够捕获固定长度的片段的上下文。因此,上下文变得支离破碎。相对位置编码有助于 Transformer-XL(代表“超长”)在更长的段中捕获上下文,使模型在生成文本时更有效。
由于我们希望以特定艺人和政治家的风格生成推文,我们希望网络能够了解不同推文之间的依赖关系,而不仅仅是一个推文内部的依赖关系。然而,由于 Transformer-XL 是不久前开发的,即使它的代码是开源的,我们也无法找到足够的代码遍历来帮助我们实现该模型。关于该模型的更多信息可以在这些文章中找到: Transformer-XL:释放注意力模型的潜力作者谷歌人工智能是该模型的作者Transformer-XL 的简单介绍— dair.ai 来自 Medium 上的一个博客。
开始
当我们考虑使用 GAN 生成文本时,我们想到了相关讲座中提到的由 GAN 生成的图像示例。由于结果令人印象深刻,我们想知道尝试将相同的概念用于文本生成是否会很有趣。然而,随着我们开始做更多的研究,我们发现使用 GAN 进行文本生成是非常有问题的,因为如果我们使用 RNN 进行文本生成,我们将面临 backprop 的问题,因为 RNN“挑选”下一个单词的操作是不可微的。因为有办法解决这个问题,所以我们寻找了一些已经测试过的用于文本生成的 GANs 的例子。我们在 GitHub 上找到了几个例子,包括 LeakGAN 和 SeqGAN ,但我们没有找到证据表明这些网络会比常规的 LSTM 网络或一些先进的网络如 GPT-2 或 CTRL 更好。在进一步阅读下,你可以看看我们发现的一些有趣的阅读,关于 gan 在文本生成中的表现。
计算机的 Ctrl 按键(控制键)
CTRL 是 Salesforce 在 2019 年展示的*规模文本生成网络。它代表条件转换器语言模型。创建这个模型是为了让用户对生成的文本有更多的控制,这是其他*规模模型通常所缺乏的。这个模型是开源的,可以在 GitHub 上获得。在我们研究的所有模型中,CTRL 拥有最多由创建者提供的文档。然而,使用 CTRL 主要是为了从链接、文本等的提示中产生故事。因为我们的目标是创建推文,而不是故事,所以我们决定继续使用 GPT-2,尽管用用户资料的链接来测试 CTRL 会很有趣。这是关于 CTRL CTRL:可控生成的条件转换器语言模型的原始文章,作者是 Nitish Shirish Keskar、Bryan McCann、Lav R. Varshney、Xiong、Salesforce Research 的 Richard Socher 和的 GitHub repo 并附有文档。
GPT-2
GPT-2 是 Open AI 在 2019 年发布的最新文本生成网络。它基于 transformer 网络,并在来自互联网的 40GB 文本上进行训练。因为创作者担心使用这种模型产生假新闻的可能性,他们决定发布一个小得多的模型。这个模型生成的文本很容易被误认为是人类的,而且语法和拼写几乎完美无缺。在我们观察的所有模型中,这一个是研究人员采用最多的,因此有更多关于其使用的说明和解释。对于我们的文本生成,我们使用了 Max Woolf GPT-2 Simple 的模型,并使用了他各自的 Colab 笔记本来检查模型。第一次尝试的结果令人印象深刻,所以我们决定停止这一次,因为它有最佳的训练时间和最佳的结果质量。关于该模型的更多信息可以在 Open AI 的文章更好的语言模型及其含义中找到。
建立网络和发微博
LSTM
为了使用 LSTM 网络生成推文,来自 CSV 的 10,000 条推文被转移到熊猫文件中。推文中存在差异,因此我们尽最*努力清理数据,同时不减少用于训练模型的数据量。正因为如此,我们决定尝试训练模型来学*链接。我们只删除了长度小于 60 个字符的推文。通过删除低于一定长度的推文,它会删除只有一个单词的推文以及 CSV 文件中的空推文。接下来,推文被分成等长的序列,放入一个叫做句子的列表中。这些句子将用作模型的训练示例。
矢量化是下一步。在这里,我们将推文平均分成 x 和 y 两部分。 x 是一个 3D 矩阵,具有句子总数、步长(40)和唯一字符数的形状。 y 是一个 2D 矩阵,它包含句子的总长度和唯一的字符,这个向量的目的是检索x给出的句子后面的下一个字符。现在,数据已经收集完毕,准备用于训练,是时候构建 LSTM 模型了。
在花了几天时间试图找到合适且高效的完美模型后,我们偶然发现了以下模型。特别是这个模型,在我们训练的 10 个不同的模型中表现最好。该模型与其他模型的不同之处在于,额外的神经元、激活函数、SELU 和常规批量标准化适合于所使用的训练数据的*小。
在模型的训练期间,每个历元花费*约 30 分钟,并且在 10 个历元之后,模型停止学*,每个数据集的损失*约为 1.5。在训练期间,模型基于概率分布输出下一个字符。这就是所谓的多样性。多样性是一个决定生成的句子有多随机的值。出于训练目的,我们将多样性设置为 0.2、0.5、0.7、1.0 和 1.2,以便直观显示哪种多样性生成了最有意义的推文。
最后一步是为用户生成新的推文。generate_tweets 函数接受一个随机种子,它是来自 tweet 列表的一个随机 tweet,以及一个集合多样性。我们选择了 0.7 的多样性,因为与其他相比,它表达了最容易理解的推文。在收到一个随机种子和一个多样性值后,该函数将种子长度减少到 40 个字符。这样做是因为使用一条已经公式化的推文来创建另一条推文是良性的,因为一旦一条新的推文生成,它将超过 twitter 上允许的最*字符长度。
**
在将句子设置为随机种子的前 40 个字符后,我们创建了由生成的变量,该变量将存储生成的 tweet 的开头、随机种子的前 40 个字符以及由模型预测提供的 120 个其他生成的字符。生成 2000 条推文所需的时间从 45 分钟到 2 小时不等。下面,我为下面的四个人提供了 2000 条推文中的一条:
伯尼·桑德斯:
2018 年你可能错过的好消息。感谢世界的承诺我们已经完成的事情考虑:【http://b-gat.es/2vwmyaw】pic.twitter.com/eyfhwch8gpngw
比尔·盖茨:
老师们正在做着不可思议的工作来保持世界上最贫穷的:【http://b-gat.es/1u6yecf】T4pic.twitter.com/h4g6vdscdhoamgetpiwel108heepsang it:http://b-gat.es/2dp
凯文·哈特:
我有更多突发新闻抽搐!现在就去电影院吧!!!!# hustlehart # hustlehart #创造历史#喜剧明星…#想想电影中的阿曼斯顿
贾斯汀比伯:
我的专辑出来了,但我*哥的@ jesicnolker @ showmybirthdi @ americcaem @ dankan ter 和@ its beliebers @ spendwer @ changelhttps://twitter.com/smazurgop/status也出来了
结果
为了找到一个适合将准确率提高到 40%以上的模型,花了几天时间。就这个特定模型的准确性而言,政治家伯尼·桑德斯和比尔盖茨在他们的推文中分享了相同的推文风格和语法,用于训练该模型。这种一致性提供了 62%及更高的准确度结果。因为艺人经常发微博,而且他们的微博从一个话题到另一个话题,这影响了他们训练的准确性。这使得该模型能够为伯尼·桑德斯和比尔盖茨生成更好的推文。
总的来说,还有一些可以改进的地方。首先,我们可以从推文中排除链接,但问题是,因为所有被选中的人都使用*量链接来推广自己。这将导致数据集的*小*幅下降。但是,通过收集每个用户*约五万条推文,它会给模型更多的句子来训练和学*。然而,这将需要几周的时间来实现和产生每个人。
GPT-2 简单
为了用 GPT-2 生成推文,我们使用了马克斯·伍尔夫的教程如何用 GPT-2 制作定制的人工智能生成的文本。至于推文生成的这一部分,我们使用了无法定制的网络,我们可以试验的主要内容是学*率和时代数量。当 eta 设置为 0.0001,epochs 的数量为 1000 时,生成的 tweets 的质量最好。我们还能够在 GPT-2 模型的尺寸版本之间进行选择——124 米、355 米和 774 米模型。我们决定运行最小的一个,因为我们需要在 1000 个时期内训练模型 10 次,然后生成 20000 条推文(每个用户 2000 条),所以时间是我们关心的问题。我们使用 Google Colab notebook 来运行代码,以使用 Google 的 GPU 并使其更快,但即使是最小的模型也需要*约 1 小时来为每个用户进行训练,*约 20 分钟来产生推文。由于 Google Colab 会频繁断开运行时,我们决定不冒险运行较重的模型,所以我们可以在截止日期前生成推文。即使使用最小的模型,推文的质量也令人印象深刻。
我们注意到艺人和政客之间的推文质量有很*差异。总的来说,政治家的推文要长得多,有更多的内容,因此,对于网络来说,学*起来容易得多,因为当提供 10000 条推文时,它会得到更多的单词。与此同时,艺人们会有很多宣传片和短信,它们并不是很独特,比如“爱你”等。因此,当网络处理艺人的推文时,结果质量较低,包含更多只有空格的空推文。当我们标准化推文的长度时,我们用空格增加了它们,所以推文越短,空格占书面文本的比例就越高。
GPT-2 的制造商还提到,网络的一个缺点是重复。我们看到,在每个用户的*量推文中,网络会停在某个词上,并生成 10 次或更多次。这样的推文的一个例子是为贾斯汀比伯生成的:“XXL 回来了。你知道该怎么做。@ scooter Braun @ scrappy @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris @ luda cris”。
我们看到的重复的另一个问题是,当网络生成同一条推文的几个变体,只是改变时间,链接的结尾,结尾的几个词,等等。例如,网络为盖茨生成的推文改变了“更新”时间:
更新,上午 19:45:这篇文章已经更新,澄清我最近得到的脊髓灰质炎疫苗不是“脊髓灰质炎疫苗”确实是。
更新,2:58:这篇文章已经更新,澄清我最近得到的脊髓灰质炎疫苗不是“脊髓灰质炎疫苗”确实是。
更新,凌晨 3:58:这篇文章已经更新,澄清我最近得到的脊髓灰质炎疫苗不是“脊髓灰质炎疫苗”确实是。
更新,凌晨 4:55:这篇文章已经更新,澄清我最近得到的脊髓灰质炎疫苗不是“脊髓灰质炎疫苗”确实是。
更新,上午 5:45:这篇文章已经更新,澄清我最近得到的脊髓灰质炎疫苗不是“脊髓灰质炎疫苗”确实是。
更新,上午 6:30:这篇文章已经更新,澄清我最近得到的脊髓灰质炎疫苗不是“脊髓灰质炎疫苗”确实是。
更新,上午 7:45:这篇文章已经更新,澄清我最近得到的脊髓灰质炎疫苗不是“脊髓灰质炎疫苗”确实是。
另一个例子是链接结尾的 10 种变化:
这个世界比以往任何时候都好,但我很惊讶很少有人知道这一点。https://b-gat.es/2BZcNFk
这个世界比以往任何时候都好,但我很惊讶很少有人知道这一点。https://b-gat.es/2E3zEMw
这个世界比以往任何时候都好,但我很惊讶很少有人知道这一点。https://b-gat.es/2EJzX1V
这个世界比以往任何时候都好,但我很惊讶很少有人知道这一点。https://b-gat.es/2Ft7tBS
这个世界比以往任何时候都好,但我很惊讶很少有人知道这一点
这个世界比以往任何时候都好,但我很惊讶很少有人知道这一点。https://b-gat.es/2HfkiQi
这个世界比以往任何时候都更好,但我很惊讶很少有人知道这一点。https://b-gat.es/2I0xWEWT
这个世界比以往任何时候都好,但我很惊讶很少有人知道这一点。https://b-gat.es/2I4m0JT
这个世界比以往任何时候都更好,但我很惊讶很少有人知道这一点。https://b-gat.es/2I9cVMr
这个世界比以往任何时候都更好,但我很惊讶很少有人知道这一点。https://b-gat.es/2JsNJoh
还有,当推文被拉出来的时候,一些像撇号这样的符号还没有读好,所以在生成的推文中,我们可以看到一些奇怪的符号或者符号的组合,而不是撇号,在句子的结尾等等。下面是一些用 those:"iрір‚в„ўm”代替“我是”、“iвђ™m”、“рвђрёр‡рёпїѕ”等的例子。
GPT-2 文本生成的积极方面是,该网络能够抓住所有用户谈论的主要话题,包括他们的签名标签、他们共享链接的网站、他们从事的项目和个人谈话风格。例如,对于特朗普的虚假推文,该网络发现特朗普经常从福克斯新闻频道转发,而亚历山德拉·奥卡西奥-科尔特斯生成的推文来自《纽约时报》。比伯生成的推文中有很多关于新歌和专辑的公告,而桑德斯的推文中有很多关于全民医疗保健的内容。盖茨在推特上发表了很多关于经济发展、抗击小儿麻痹症、疟疾等文章的评论,还提到了梅林达·盖茨的书和评论。
以下是我们制作的一些最佳推文:
款待者
贾斯汀比伯:
你可以发送短信 SUMMER 至 22837 预订#AsLongAsYouLoveMe,当我们推出独家原声专辑时,应用程序会通知你。#AsLongAsYouLoveMe 和#LoveYourself 将出现在 ITUNES 上,也将进入我们的前十名。#BIEBERBLAST
爱莉安娜·格兰德:
今晚是#危险女人之旅的最后一晚,我们很荣幸能在巴克莱中心表演。非常感谢你们今晚所有的爱、能量、兴奋和爱。https://www.instagram.com/p/BRiT3YZrH6R/
凯文·哈特:
我刚刚在我的“不负责任的旅行”中增加了另一个节目…我等不及让你们看这部电影了!!!https://Twitter . com/originalcinema/status/924093577757350208
勒布朗·詹姆斯:
NBA 季后赛终于来了!我很期待今晚的比赛,我也会期待下一场比赛。
克莉茜·泰根:
《向前看》全文明天出!在我的简历中预订!!#渴望系列#克里斯·库#克里斯·库#克里斯·布利斯·pic.twitter.com/aI1oIOyCEI
政治家
亚历山德拉·奥卡西奥·科尔特斯:
14 年来,国会和共和党一直致力于解散患者保护与平价医疗法案。他们不能在不废除法律的情况下废除 ACA,这意味着将保险市场减半。我们需要用更多的东西来代替 ACA。
比尔·盖茨:
许多人认为非洲是一个饥饿的*陆。它实际上是世界上最*的经济体之一。这里有 5 个惊人的统计数据:【http://b-gat.es/1o5YKV7】pic.twitter.com/wZ8EFmYJFl
伯尼·桑德斯:
几周前,特朗普总统签署了一项行政命令,削减对某些项目和活动的资金,包括计划生育。特朗普的行动必须结束并立即停止对计划生育的所有资助和乳腺癌筛查的所有供应。https://Twitter . com/realDonaldTrump/status/11668362477942539
唐纳德·特朗普:
你认为管理我们国家和经济的人会允许这个荒谬的弹劾骗局继续下去吗?我们的经济是有史以来最好的,民主党正在追杀你们!
伊丽莎白·沃伦:
一年前的今天,奥巴马总统签署了一项行政命令,强制关闭私人监狱。今天,在我写这篇文章的时候,46 个州已经实施了他们自己版本的计划,国会也签署了,这要感谢@KamalaHarris 和@ AyannaPressley。
结果
总的来说,GPT-2 Simple 在为我们选择的用户生成推文方面做得很好。文本是相同的风格,相同的主题,包括每个用户特有的细节。我们还看到,结果高度依赖于输入数据的质量。对于几乎所有艺人来说,与政客相比,网络产生的推文质量要低得多,重复和空洞的推文也多得多。对于政治家来说,由于网络有更多的数据可以处理,因此产生的推文有意义,错误少得多,并且对每个用户来说更容易识别。总而言之,GTP-2 是一个用相对少的数据快速生成高质量文本的好方法。对于那些一开始我们无法获得所有 10000 条推文的用户,即使有 5000 条推文,结果也令人印象深刻。根据我们使用的 Max Woolf 的教程,几乎任何人都可以使用这个模型快速生成高质量的文本。
比较实际的推文和生成的推文
LSTM 只为四位公众人物(比伯、哈特、盖茨和桑德斯)发布了推文,而 GPT 2 号为所有十位公众人物发布了推文。因此,我们首先对所有十个人的 GPT-2 与训练数据进行了分析,然后我们将两个网络与训练数据进行了比较。
GPT-2 与实际推文
政治人物生成的推文的十个最常见的词与训练数据中的 6-8 个匹配,而艺人的匹配数量是一半(3-4 个)。
对于*多数公众人物来说,GPT-2 网络产生的推文的链接频率类似于训练数据的链接频率。在新 GPT 协议生成的 twitter 数据中,“http”是 10 个最常见的术语之一。我们可以通过比较爱莉安娜·格兰德常用词的分布来观察链接用法的差异:
比较艺人的实际推文和生成的推文的词云,我们可以看到网络仍然生成与个人的一般语气相匹配的词。作为一个例子,这里有一个爱莉安娜·格兰德的词云的对比:
我们还绘制了训练数据和 GPT-2 并排生成的推文的推文长度和情绪。有趣的是,GPT-2 网络持续生成的推文长度是它被训练的推文长度的一半,甚至更少。此外,他们的情感得分远比训练数据更加中立。我们可以从每个人的情绪得分的轻尾分布中看到,生成的推文中的情绪变化要小得多,我们将在下一节回到这一点。
**
比较 LSTM 和 GPT-2 生成的推文
当谈到推文长度时,LSTM 网络的表现类似于 GPT-2 网络。一个例外是沃伦,LSTM 网络实际上平均生成了略长的推文。
**
与 GPT-2 网络相比,LSTM 产生了更积极的情绪得分,更接近实际的推特数据。然而,正如我们在桑德斯身上看到的那样,生成的推文和实际推文之间的情感分布仍有明显差异。对于两个网络,生成推文的分布都是非常轻尾的,分数都集中在 0.0 左右(中性)。显然,网络可以令人信服地模仿推文风格,但要获得正确的情感需要更多的细微差别。更短的推文长度和生成的推文中更多的链接的组合可能有助于更高的中性情绪。
结论
总的来说,我们在这个项目中学到了很多。我们的两个网络都为政治家生成了质量更好、相似度更高的推文。这可能归因于这两个群体在推特上的不同方式。政治家倾向于发布更长、更复杂的信息来反映他们的平台,而*多数艺人使用 Twitter 来发布宣传片并与粉丝互动。
关于我们的网络,我们的主要结论是,GPT-2 简单是更容易和更快地使用比 LSTM 网络。它产生了相当不错的推文,看起来很像真的。作为这个项目的延伸,将我们使用的模型的结果与更*版本的 GPT-2 进行比较将会很有趣。此外,我们观察到生成文本的质量与训练网络的数据质量直接相关。因此,如果你想产生更好的结果,花更多的时间清理和调整你的数据。
从我们的网络和生成的推文中,我们看到了 OpenAI 对发布完全训练有素的网络的担忧的证据——如果创建看起来几乎与真实政治家的推文完全一样的推文那么容易,那么滥用这种模型生成假新闻的威胁就令人担忧。然而,我们注意到,我们的网络在模仿政治人物和艺人的推特上的各种情绪方面甚至都不接近。也许这些网络还没有发现人类说话的模式。
参考
* [## Transformer-XL:释放注意力模型的潜力
为了正确理解一篇文章,有时需要参考几千年前出现的一个词或一个句子…
ai.googleblog.com](https://ai.googleblog.com/2019/01/transformer-xl-unleashing-potential-of.html) [## Transformer-XL 简介
一种支持长期依赖的注意力语言建模新技术概述。
medium.com](https://medium.com/dair-ai/a-light-introduction-to-transformer-xl-be5737feb13) [## CTRL:用于可控生成的条件转换器语言模型
*规模语言模型显示了有前途的文本生成能力,但是用户不能容易地控制特定的…
arxiv.org](https://arxiv.org/abs/1909.05858) [## 销售力量/控制
作者:Nitish Shirish Keskar,Bryan McCann,Lav Varshney,Xiong,和 Richard Socher
github.com](https://github.com/salesforce/ctrl#generations) [## 更好的语言模型及其含义
我们已经训练了一个*规模的无监督语言模型,它可以生成连贯的文本段落,实现…
openai.com](https://openai.com/blog/better-language-models/) [## Twitter 数据挖掘:挖掘没有 API 键的 Twitter 数据。
使用一行命令获取旧的 Twitter *数据进行分析。
medium.com](https://medium.com/@IrekponorVictor/twitter-data-mining-mining-twitter-data-without-api-keys-a2a2bd3f11c)
进一步阅读
由于语义的模糊性和长文本建模的困难,生成高质量的文本是一个具有挑战性的问题
www.semanticscholar.org](https://www.semanticscholar.org/paper/Evaluating-Generative-Models-for-Text-Generation-Kawthekar/0a4e66bd2ba65a53b11684250f2adc76eb768222) [## 使用知识蒸馏和生成对抗网络的文本生成…
文本生成在许多自然语言处理应用中有着特殊的意义,如机器翻译、语言建模和语义分析等
www.semanticscholar.org](https://www.semanticscholar.org/paper/TextKD-GAN%3A-Text-Generation-Using-Knowledge-and-Haidar-Rezagholizadeh/abcbfa04b14ee6cc76afe8c5e504daf815d14fa2) [## 文本生成的生成性对抗网络——第一部分
用于文本生成的 GANs 的问题以及解决这些问题的方法
becominghuman.ai](https://becominghuman.ai/generative-adversarial-networks-for-text-generation-part-1-2b886c8cab10) [## 文本生成的生成性对抗网络第 2 部分:RL
使用强化学*解决文本中的不可微性问题。
becominghuman.ai](https://becominghuman.ai/generative-adversarial-networks-for-text-generation-part-2-rl-1bc18a2b8c60)*
推文,使用 Q 块的表情符号情感分析
自然语言处理
我第一次尝试租 GPU 加速软件。在我的回购中可获得全部代码。
在下面的文章中,我将租用 Q Blocks GPU 来运行一个计算要求苛刻的 AI 模型。我将运行一个名为 DeepMoji 的深度学*模型,给定一个句子,该模型将估计出可以描述该句子的前 n 种情绪(单击非链接进行尝试)。我将租用 GPU 的平台仍然处于早期访问阶段,所以你可以在购买之前试用它(这个平台为你提供 20 小时的免费 GPU,一个很好的开始),看看你是否适应它。
租 Q 块 GPU
为了进行这个实验,我想从替代提供商而不是亚马逊 AWS 租赁 GPU,我被告知这很贵。,只有在运行巨*的模型时才变得方便。
到目前为止,我发现一个很好的提议尝试 Q 块 GPU。GPU 不是用使用量买的,是用时间买的。这项服务每小时只需 0.05 美元。是一个非常合理的价格,你将基本上支付相同的 CPU 成本的 GPU。Q Blocks 使用的分布式计算技术允许该提供商在不降低质量的情况下保持低价。
买 GPU 有必要吗?
如果你是初学者,肯定不是。常见的在线编译器,如 Google Colab 或 Kaggle,提供了免费(但不强*)的计算能力。如果您像每个初学者一样处理小数据集,它不需要很高的计算能力,您可以使用免费版本。
相反,如果你需要调整你的模型(就像每个专业人士需要的那样),你将不得不在电脑前等待无数个小时,等待可能会让你失望的结果。建立更具挑战性的模型的前一步是增加你的计算能力。
例如,在不使用 GPU 的情况下运行这个算法将需要我几个小时才能完成。我的实验在于更快地完成这个项目。
如何高效使用 QBlocks:不浪费 GPU!
我将使用此服务的截图来简化事情。然而,我假设每个供应商都根据你的需求提供不同的服务。因为我不需要过多的计算能力,所以我可以每小时使用 100 个块,相当于每小时 0.10 美元。
如果这是你第一次租用 GPU,你可能会感到沮丧。当你启动模型时,它将开始消耗。因此,至关重要的是,你知道你在做什么,你不要在服务提供的笔记本上做实验。如果有错误,你需要在付费的工作簿中调试你的代码,你的时间将会是昂贵的。当你意识到调试时间花费了你多少钱时,你可能会转向另一家 GPU 提供商。
-
- *确保您在另一个工作簿中有一个工作模型的副本。您可以简单地导入并运行该副本,而不会浪费宝贵的时间。
Q Blocks 与在线 Jupyter 笔记本有关联。例如,如果你在 Google Colab 中开始你的模型,可能会有一些问题。访问文件或下载库的语法略有不同,当您的笔记本正在运行时,您可能会浪费时间在网上搜索解决方案。因为我在制作这个模型时遇到了类似的问题,所以我将详细说明如何克服解决方案。
因此,在免费 Jupyter 工作簿中编写您的模型之前,如果它可以工作,那么在您使用的平台上导入该模型(在我的例子中是 Q 块)以节省时间。
提供商可能会启动一个加载了 GPU 的空笔记本。我们可以开始编码了…
导入数据集
您要做的第一件事是导入您可以访问的数据集。每个笔记本都有一个上传文档的部分,我稍后会创建一个到那个文档的连接,用熊猫导入它。
火炬装置
很可能,在尝试这个实验时,最困难的事情是安装 torchMoji。我将把这个装置分成两部分。首先,我将安装运行 torchMoji 所需的库:
!pip3 install torch==1.0.1 -f [https://download.pytorch.org/whl/cpu/stable](https://download.pytorch.org/whl/cpu/stable)
!git clone [https://github.com/huggingface/torchMoji](https://github.com/huggingface/torchMoji)
import os
os.chdir('torchMoji')
!pip3 install -e .
#if you restart the package, the notebook risks to crash on a loop
#if you managed to be curious and to make it stuck, just clic on RunTime, Factory Reset Runtime
#I did not restart and worked fine#se questo funziona, poi crasha in future linee di codice, anche se chiudiamo e riapriamo dovrebbe essere a posto per 12 ore
第二步,我将下载并安装权重,这将允许神经网络基于任何文本选择表情符号。
-
- *非常小心,Jupyter Notebook 中有一个错误(我不知道我是否可以将其归类为错误),它阻止您输入是或否之类的答案。要解决此问题并防止您的 GPU 在循环中迭代,从而消耗您的可用处理能力,请使用以下代码行解决 Jupyter Notebook 中的问题。
#!python3 scripts/download_weights.py
! yes | python3 scripts/download_weights.py
在传统的笔记本中,你应该被允许在选项是和否之间进行选择。然而,Jupyter Notebook 没有为您提供任何输入选择的可能性。我使用上面的代码绕过了它,并立即声明了 yes 选择。
定义转换函数
我现在将创建一个函数,作为输入,它将接受一个文本字符串,并根据我们想要提取的情绪数量,输出相应的表情符号。
#si connette a DeepMoji per una request, non posso modificare i parametri, credo
!python3 examples/text_emojize.py --text f" {Stay safe from the virus} "!pip3 install --upgrade numpy!pip install numpy==1.18
!pip install scipy==1.1.0
!pip install scikit-learn==0.21.3import numpy as np
import emoji, json
from torchmoji.global_variables import PRETRAINED_PATH, VOCAB_PATH
from torchmoji.sentence_tokenizer import SentenceTokenizer
from torchmoji.model_def import torchmoji_emojis
EMOJIS = ":joy: :unamused: :weary: :sob: :heart_eyes: :pensive: :ok_hand: :blush: :heart: :smirk: :grin: :notes: :flushed: :100: :sleeping: :relieved: :relaxed: :raised_hands: :two_hearts: :expressionless: :sweat_smile: :pray: :confused: :kissing_heart: :heartbeat: :neutral_face: :information_desk_person: :disappointed: :see_no_evil: :tired_face: :v: :sunglasses: :rage: :thumbsup: :cry: :sleepy: :yum: :triumph: :hand: :mask: :clap: :eyes: :gun: :persevere: :smiling_imp: :sweat: :broken_heart: :yellow_heart: :musical_note: :speak_no_evil: :wink: :skull: :confounded: :smile: :stuck_out_tongue_winking_eye: :angry: :no_good: :muscle: :facepunch: :purple_heart: :sparkling_heart: :blue_heart: :grimacing: :sparkles:".split(' ')
model = torchmoji_emojis(PRETRAINED_PATH)
with open(VOCAB_PATH, 'r') as f:
vocabulary = json.load(f)
st = SentenceTokenizer(vocabulary, 30)def deepmojify(sentence, top_n=5, return_emoji=True, return_prob=False):
#converte lista probabilità in emoticon più probabili
def top_elements(array, k):
ind = np.argpartition(array, -k)[-k:]
return ind[np.argsort(array[ind])][::-1]tokenized, _, _ = st.tokenize_sentences([sentence])
#print(tokenized)
#lista di probabilità
prob = model(tokenized)[0]
#se ci sono errori parte da qui: too many values to unpack (expected 2), non riesce a trovare prob
#trova le n emoticono più alte
emoji_ids = top_elements(prob, top_n)#converte questi numeri in emoticons
emojis = map(lambda x: EMOJIS[x], emoji_ids)
if return_emoji == False and return_prob == False:
return None
elif return_emoji == True and return_prob == False:
return emoji.emojize(f"{sentence} {' '.join(emojis)}", use_aliases=True)
elif return_emoji == True and return_prob == True:
return emoji.emojize(f"{sentence} {' '.join(emojis)}", use_aliases=True), prob
elif return_emoji == False and return_prob == True:
return prob
deepmojify('ciao, come stai?', top_n=3, return_emoji=True, return_prob=False)
输入字符串的输出如下:
'ciao, come stai? 💓 💛 ❤'
定义我们的主要功能
我现在将创建一个函数,将一个列表转换为一个数据集,其估计的表情符号位于不同的列中。正如你在上面的字符串中看到的,这个函数将表情符号和输入字符串连接在一起,我将把它们分开,分别放在数据集的不同列中。
def emoji_dataset(list1, n_emoji=3, only_prob=False):
emoji_list = [[x] for x in list1]for _ in range(len(list1)):
for n_emo in range(1, n_emoji+1):
print(_)
if only_prob == False:
emoji_list[_].append(deepmojify(list1[_], top_n=n_emoji, return_emoji=True, return_prob=False)[2*-n_emo+1])
else:
emoji_list[_].append(deepmojify(list1[_], top_n=1, return_emoji=False, return_prob=True))emoji_list = pd.DataFrame(emoji_list)
return emoji_listdf_ = emoji_dataset(list1, 3)
df_
下载数据集
现在我已经准备好了,我可以运行整个项目了。我会下载数据集,快速预处理,然后启动算法。
import pandas as pd
X = pd.read_csv(open('tweets.csv'))
如果你还没有注意到,我正在使用 read_csv 里面的函数 open 。如果没有这个函数,代码将返回一个错误。这种行为是朱庇特笔记本所特有的。
X.pop('Unnamed: 0')
X = pd.DataFrame(X)
X.columns = ['tweets']
Xdf = X.copy()
df
作为最后一步,我将把 25000 条推文的列表变成一个列表。我可以使用这个列表作为主函数的输入。
list1 = df['tweets'].to_list()
我终于可以开始模型了。因此,我将拥有 25000 条带有相应情绪的推文列表。
list1 = list1[0:25000]
df_.to_csv('25k_emotions.csv')
df_
输出的示例
当您的模型设置好后,不要忘记删除笔记本以保存您剩余的学分。
结论
该模型的执行速度相对较快。与 GPU 相比,免费的云编译器可以达到快 10 倍的速度。在 Google Colab 上运行这个算法需要 1 个多小时,相比之下,我使用 Q 块*约需要 10 分钟才能达到相同的结果。如果你打算租用更强*的 GPU,根据你的需求,这个数字只会增加。
云计算很快将成为新的规范。有了区块链和像素流技术等革命性创新,人们将不再需要购买 GPU 硬件。像 Q Blocks 这样使用对等计算的提供商,通过使计算能力更容易获得,加速创新,做出了贡献。
二十五个 SQL 练*题
这些问题和示例解决方案将保持你的技能敏锐。
图片来源: Unsplash
介绍
结构化查询语言(SQL)用于检索和操作存储在关系数据库中的数据。精通 SQL 是许多技术工作的重要先决条件,需要一些实践。
为了补充网上可用的 SQL 培训资源(pg exercise、 LeetCode 、 HackerRank 、 Mode ),我整理了一个我最喜欢的问题列表,您可以手动解决这些问题,也可以用 PostgreSQL 实例来解决。
这些问题涵盖以下关键概念:
- 基本检索(选择,从)
- 创建和别名(使用,AS,GENERATE_SERIES)
- 过滤 (DISTINCT,WHERE,HAVING,AND,OR,IN,NOT IN)
- 聚合(分组依据,计数、总和、平均值)
- 连接(内连接、左连接、一个或多个等式上的全外连接、交叉连接、联合和联合所有)
- 条件语句****(CASE-WHEN-THEN-ELSE-END)
- 窗口函数 (RANK,DENSE_RANK,ROW_NUMBER,SUM with PARTITION BY - ORDER BY)
- ****格式化(限制、排序、整型、浮点型或日期型、串联、合并)
- 算术 运算和比较 (+,-,,/,//,^,<,>,=,!=)*
- ****日期时间操作(提取(月/日/年))
你自己试试
你可以下载 PostgreSQL 和 PSequel (参见本教程的分步安装指南),然后运行下面文本中灰色框中显示的查询,自己尝试一下。PSequel 只适用于 Mac——如果你用的是 PC,你可以试试这些 Windows 替代品中的一个。
使用 PSequel 和下面提供的输入表自己尝试这些查询。
下面显示的每个查询中的第一个文本块建立输入表,并遵循以下格式:
WITH input_table (column_1, column_2)
AS (VALUES
(1, 'A'), (2, 'B'))
您可以使用 PSequel(如上所示)查询输入表,并使用该模板为您自己的问题轻松构建新表。
基于 Web 的 SQL 培训资源在几个方面存在不足。例如,LeetCode 不支持使用窗口功能,并将其最有趣的问题隐藏在付费墙之后。此外,在浏览器中运行 SQL 查询可能会非常慢,因为数据集很*,对于非高级用户来说,检索速度通常会受到限制。另一方面,本地执行查询是即时的,并允许通过语法错误和中间表进行快速迭代。我发现这是一次更令人满意的学*经历。
下面列出的问题包括证实在 PostgreSQL 中有效的示例解决方案。请记住,通常有多种方法可以获得 SQL 问题的正确答案。我更喜欢使用公共表表达式(cte)而不是嵌套子查询——cte 可以更线性地说明数据争论的顺序。然而,这两种方法可以产生相同的解决方案。我也喜欢遵循将 SQL 操作符全部*写的惯例(SELECT、FROM、WHERE 等。),小写的列名(user_id,date 等。),以及简单的表别名(t1,t2 等。)可能的话。
下面显示的代码片段可以按原样在 PSequel 中运行,以产生显示的结果。注意 Postgres 的一个怪癖:分数必须乘以 1.0 才能从整数转换成浮点格式。这在 SQL 的其他实现中是不需要的,在访谈中也是不期望的。
欢迎在评论中留下你的备选答案!
问题
1.取消率
根据下表中的用户 id、操作和日期,编写一个查询来返回每个用户的发布率和取消率。
WITH users (user_id, action, date)
AS (VALUES
(1,'start', CAST('01-01-20' AS date)),
(1,'cancel', CAST('01-02-20' AS date)),
(2,'start', CAST('01-03-20' AS date)),
(2,'publish', CAST('01-04-20' AS date)),
(3,'start', CAST('01-05-20' AS date)),
(3,'cancel', CAST('01-06-20' AS date)),
(1,'start', CAST('01-07-20' AS date)),
(1,'publish', CAST('01-08-20' AS date))), *-- retrieve count of starts, cancels, and publishes for each user*t1 AS (
SELECT
user_id,
SUM(CASE WHEN action = 'start' THEN 1 ELSE 0 END) AS starts,
SUM(CASE WHEN action = 'cancel' THEN 1 ELSE 0 END) AS cancels,
SUM(CASE WHEN action = 'publish' THEN 1 ELSE 0 END) AS publishes
FROM users
GROUP BY 1
ORDER BY 1)*-- calculate publication, cancelation rate for each user by dividing by number of starts, casting as float by multiplying by 1.0 (default floor division is a quirk of some SQL tools, not always needed)*SELECT
user_id,
1.0*publishes/starts AS publish_rate,
1.0*cancels/starts AS cancel_rate
FROM t1
2.净值的变化
从下面两个用户之间的交易表中,编写一个查询来返回每个用户的净值变化,按净变化递减排序。
WITH transactions (sender, receiver, amount, transaction_date)
AS (VALUES
(5, 2, 10, CAST('2-12-20' AS date)),
(1, 3, 15, CAST('2-13-20' AS date)),
(2, 1, 20, CAST('2-13-20' AS date)),
(2, 3, 25, CAST('2-14-20' AS date)),
(3, 1, 20, CAST('2-15-20' AS date)),
(3, 2, 15, CAST('2-15-20' AS date)),
(1, 4, 5, CAST('2-16-20' AS date))), *-- sum amounts for each sender (debits) and receiver (credits)*debits AS (
SELECT
sender,
SUM(amount) AS debited
FROM transactions
GROUP BY 1 ),credits AS (
SELECT
receiver,
SUM(amount) AS credited
FROM transactions
GROUP BY 1 )*-- full (outer) join debits and credits tables on user id, taking net change as difference between credits and debits, coercing nulls to zeros with coalesce()*SELECT
COALESCE(sender, receiver) AS user,
COALESCE(credited, 0) - COALESCE(debited, 0) AS net_change
FROM debits d
FULL JOIN credits c
ON d.sender = c.receiver
ORDER BY 2 DESC
3.最常见的项目
从包含日期和订购项目列表的下表中,编写一个查询以返回在每个日期订购的最频繁的项目。在平局的情况下返回多个项目。
WITH items (date, item)
AS (VALUES
(CAST('01-01-20' AS date),'apple'),
(CAST('01-01-20' AS date),'apple'),
(CAST('01-01-20' AS date),'pear'),
(CAST('01-01-20' AS date),'pear'),
(CAST('01-02-20' AS date),'pear'),
(CAST('01-02-20' AS date),'pear'),
(CAST('01-02-20' AS date),'pear'),
(CAST('01-02-20' AS date),'orange')),*-- add an item count column to existing table, grouping by date and item columns*t1 AS (
SELECT
date,
item,
COUNT(*) AS item_count
FROM items
GROUP BY 1, 2
ORDER BY 1),*-- add a rank column in descending order, partitioning by date*t2 AS (
SELECT
*,
RANK() OVER (PARTITION BY date ORDER BY item_count DESC) AS date_rank
FROM t1)*-- return all dates and items where rank = 1*SELECT
date,
item
FROM t2
WHERE date_rank = 1
4.最新行动之间的时间差
根据下表中的用户操作,编写一个查询,按用户 ID 的升序为每个用户返回最后一个操作和倒数第二个操作之间经过的时间。
WITH users (user_id, action, action_date)
AS (VALUES
(1, 'start', CAST('2-12-20' AS date)),
(1, 'cancel', CAST('2-13-20' AS date)),
(2, 'start', CAST('2-11-20' AS date)),
(2, 'publish', CAST('2-14-20' AS date)),
(3, 'start', CAST('2-15-20' AS date)),
(3, 'cancel', CAST('2-15-20' AS date)),
(4, 'start', CAST('2-18-20' AS date)),
(1, 'publish', CAST('2-19-20' AS date))), *-- create a date rank column, partitioned by user ID, using the ROW_NUMBER() window function* t1 AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY action_date DESC) AS date_rank
FROM users ),*-- filter on date rank column to pull latest and next latest actions from this table*latest AS (
SELECT *
FROM t1
WHERE date_rank = 1 ),next_latest AS (
SELECT *
FROM t1
WHERE date_rank = 2 )*-- left join these two tables, subtracting latest from second latest to get time elapsed* SELECT
l1.user_id,
l1.action_date - l2.action_date AS days_elapsed
FROM latest l1
LEFT JOIN next_latest l2
ON l1.user_id = l2.user_id
ORDER BY 1
5.超级用户
一家公司将其超级用户定义为至少进行过两次交易的用户。从下表中编写一个查询,为每个用户返回他们成为超级用户的日期,首先按最早的超级用户排序。不是超级用户的用户也应该出现在表中。
WITH users (user_id, product_id, transaction_date)
AS (VALUES
(1, 101, CAST('2-12-20' AS date)),
(2, 105, CAST('2-13-20' AS date)),
(1, 111, CAST('2-14-20' AS date)),
(3, 121, CAST('2-15-20' AS date)),
(1, 101, CAST('2-16-20' AS date)),
(2, 105, CAST('2-17-20' AS date)),
(4, 101, CAST('2-16-20' AS date)),
(3, 105, CAST('2-15-20' AS date))), *-- create a transaction number column using ROW_NUMBER(), partitioning by user ID*t1 AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY transaction_date) AS transaction_number
FROM users),*-- filter resulting table on transaction_number = 2*t2 AS (
SELECT
user_id,
transaction_date
FROM t1
WHERE transaction_number = 2 ),*-- left join super users onto full user table, order by date* t3 AS (
SELECT DISTINCT user_id
FROM users )SELECT
t3.user_id,
transaction_date AS superuser_date
FROM t3
LEFT JOIN t2
ON t3.user_id = t2.user_id
ORDER BY 2
6.内容推荐(硬)
使用以下两个表,编写一个查询,根据社交媒体用户的朋友喜欢但尚未标记为喜欢的页面,向他们返回页面推荐。按用户 ID 升序排列结果。来源。
****
WITH friends (user_id, friend)
AS (VALUES
(1, 2), (1, 3), (1, 4), (2, 1), (3, 1), (3, 4), (4, 1), (4, 3)),likes (user_id, page_likes)
AS (VALUES
(1, 'A'), (1, 'B'), (1, 'C'), (2, 'A'), (3, 'B'), (3, 'C'), (4, 'B')), *-- inner join friends and page likes tables on user_id*t1 AS (
SELECT
l.user_id,
l.page_likes,
f.friend
FROM likes l
JOIN friends f
ON l.user_id = f.user_id ),*-- left join likes on this, requiring user = friend and user likes = friend likes* t2 AS (
SELECT
t1.user_id,
t1.page_likes,
t1.friend,
l.page_likes AS friend_likes
FROM t1
LEFT JOIN likes l
ON t1.friend = l.user_id
AND t1.page_likes = l.page_likes )*-- if a friend pair doesn’t share a common page like, friend likes column will be null - pull out these entries* SELECT DISTINCT
friend AS user_id,
page_likes AS recommended_page
FROM t2
WHERE friend_likes IS NULL
ORDER BY 1
7.移动和网络访问者
使用下面的两个表,返回只访问移动设备、只访问 web 以及两者都访问的用户的比例。
****
WITH mobile (user_id, page_url)
AS (VALUES
(1, 'A'), (2, 'B'), (3, 'C'), (4, 'A'), (9, 'B'), (2, 'C'), (10, 'B')),web (user_id, page_url)
AS (VALUES
(6, 'A'), (2, 'B'), (3, 'C'), (7, 'A'), (4, 'B'), (8, 'C'), (5, 'B')), *-- outer join mobile and web users on user ID*t1 AS (
SELECT DISTINCT
m.user_id AS mobile_user,
w.user_id AS web_user
FROM mobile m
FULL JOIN web w
ON m.user_id = w.user_id)*-- calculate fraction of mobile-only, web-only, and both as average of values (ones and zeros) specified in case statement condition*SELECT
AVG(CASE WHEN mobile_user IS NOT NULL AND web_user IS NULL THEN 1 ELSE 0 END) AS mobile_fraction,
AVG(CASE WHEN web_user IS NOT NULL AND mobile_user IS NULL THEN 1 ELSE 0 END) AS web_fraction,
AVG(CASE WHEN web_user IS NOT NULL AND mobile_user IS NOT NULL THEN 1 ELSE 0 END) AS both_fraction
FROM t1
8.按产品活动列出的升级率(硬)
给定以下两个表,返回在注册后的前 30 天内访问功能二(事件表中的类型:F2)并升级到高级版的用户比例(四舍五入到两位小数)。
****
WITH users (user_id, name, join_date)
AS (VALUES
(1, 'Jon', CAST('2-14-20' AS date)),
(2, 'Jane', CAST('2-14-20' AS date)),
(3, 'Jill', CAST('2-15-20' AS date)),
(4, 'Josh', CAST('2-15-20' AS date)),
(5, 'Jean', CAST('2-16-20' AS date)),
(6, 'Justin', CAST('2-17-20' AS date)),
(7, 'Jeremy', CAST('2-18-20' AS date))),events (user_id, type, access_date)
AS (VALUES
(1, 'F1', CAST('3-1-20' AS date)),
(2, 'F2', CAST('3-2-20' AS date)),
(2, 'P', CAST('3-12-20' AS date)),
(3, 'F2', CAST('3-15-20' AS date)),
(4, 'F2', CAST('3-15-20' AS date)),
(1, 'P', CAST('3-16-20' AS date)),
(3, 'P', CAST('3-22-20' AS date))), *-- get feature 2 users and their date of feature 2 access*t1 AS (
SELECT
user_id,
type,
access_date AS f2_date
FROM events
WHERE type = 'F2' ),*-- get premium users and their date of premium upgrade*t2 AS (
SELECT
user_id,
type,
access_date AS premium_date
FROM events
WHERE type = 'P' ),*-- for each feature 2 user, get time between joining and premium upgrade (or null if no upgrade) by inner joining full users table with feature 2 users on user ID and left joining premium users on user ID, then subtracting premium upgrade date from join date*t3 AS (
SELECT t2.premium_date - u.join_date AS upgrade_time
FROM users u
JOIN t1
ON u.user_id = t1.user_id
LEFT JOIN t2
ON u.user_id = t2.user_id )*-- calculate fraction of users with upgrade time less than 30 days as average of values (ones and zeros) specified in case statement condition, rounding to two decimal places*
SELECT
ROUND(AVG(CASE WHEN upgrade_time < 30 THEN 1 ELSE 0 END), 2) AS upgrade_rate
FROM t3
9.最友好的
给定下表,返回用户列表及其相应的朋友计数。按照朋友数量降序排列结果,如果出现平局,则按照用户 ID 升序排列。假设只显示唯一的友谊
(即【1,2】不会再次显示为【2,1】)。来自李码。
WITH friends (user1, user2)
AS (VALUES (1, 2), (1, 3), (1, 4), (2, 3)), *-- compile all user appearances into one column, preserving duplicate entries with UNION ALL* t1 AS (
SELECT user1 AS user_id
FROM friends
UNION ALL
SELECT user2 AS user_id
FROM friends)*-- grouping by user ID, count up all appearances of that user*SELECT
user_id,
COUNT(*) AS friend_count
FROM t1
GROUP BY 1
ORDER BY 2 DESC
10.项目汇总(硬)
“项目”表包含三列:“任务标识号”、“开始日期”和“结束日期”。表中每一行的结束日期和开始日期相差 1 天。如果任务结束日期是连续的,则它们是同一项目的一部分。项目不重叠。
编写一个查询来返回每个项目的开始和结束日期,以及完成项目所用的天数。按项目持续时间升序排序,如果出现平局,则按开始日期升序排序。来自黑客排名。
****
WITH projects (task_id, start_date, end_date)
AS (VALUES
(1, CAST('10-01-20' AS date), CAST('10-02-20' AS date)),
(2, CAST('10-02-20' AS date), CAST('10-03-20' AS date)),
(3, CAST('10-03-20' AS date), CAST('10-04-20' AS date)),
(4, CAST('10-13-20' AS date), CAST('10-14-20' AS date)),
(5, CAST('10-14-20' AS date), CAST('10-15-20' AS date)),
(6, CAST('10-28-20' AS date), CAST('10-29-20' AS date)),
(7, CAST('10-30-20' AS date), CAST('10-31-20' AS date))), *-- get start dates not present in end date column (these are “true” project start dates)* t1 AS (
SELECT start_date
FROM projects
WHERE start_date NOT IN (SELECT end_date FROM projects) ),*-- get end dates not present in start date column (these are “true” project end dates)* t2 AS (
SELECT end_date
FROM projects
WHERE end_date NOT IN (SELECT start_date FROM projects) ),*-- filter to plausible start-end pairs (start < end), then find correct end date for each start date (the minimum end date, since there are no overlapping projects)*t3 AS (
SELECT
start_date,
MIN(end_date) AS end_date
FROM t1, t2
WHERE start_date < end_date
GROUP BY 1 )SELECT
*,
end_date - start_date AS project_duration
FROM t3
ORDER BY 3, 1
11.生日出席率
给定下面的两个表,编写一个查询来返回学生的分数,四舍五入到两位小数,他们在生日那天上学
(出勤= 1)。来源。
****
WITH attendance (student_id, school_date, attendance)
AS (VALUES
(1, CAST('2020-04-03' AS date), 0),
(2, CAST('2020-04-03' AS date), 1),
(3, CAST('2020-04-03' AS date), 1),
(1, CAST('2020-04-04' AS date), 1),
(2, CAST('2020-04-04' AS date), 1),
(3, CAST('2020-04-04' AS date), 1),
(1, CAST('2020-04-05' AS date), 0),
(2, CAST('2020-04-05' AS date), 1),
(3, CAST('2020-04-05' AS date), 1),
(4, CAST('2020-04-05' AS date), 1)),students (student_id, school_id, grade_level, date_of_birth)
AS (VALUES
(1, 2, 5, CAST('2012-04-03' AS date)),
(2, 1, 4, CAST('2013-04-04' AS date)),
(3, 1, 3, CAST('2014-04-05' AS date)),
(4, 2, 4, CAST('2013-04-03' AS date))) -- join attendance and students table on student ID, and day and month of school day = day and month of birthday, taking average of attendance column values and roundingSELECT ROUND(AVG(attendance), 2) AS birthday_attendance
FROM attendance a
JOIN students s
ON a.student_id = s.student_id
AND EXTRACT(MONTH FROM school_date) = EXTRACT(MONTH FROM date_of_birth)
AND EXTRACT(DAY FROM school_date) = EXTRACT(DAY FROM date_of_birth)
12.黑客得分
给定以下两个表,编写一个查询来返回黑客 ID、姓名和总分(完成的每个挑战的最高分之和),按分数降序排序,在分数相等的情况下按黑客 ID 升序排序。不显示零分黑客的条目。来自 HackerRank 。
****
WITH hackers (hacker_id, name)
AS (VALUES
(1, 'John'),
(2, 'Jane'),
(3, 'Joe'),
(4, 'Jim')),submissions (submission_id, hacker_id, challenge_id, score)
AS (VALUES
(101, 1, 1, 10),
(102, 1, 1, 12),
(103, 2, 1, 11),
(104, 2, 1, 9),
(105, 2, 2, 13),
(106, 3, 1, 9),
(107, 3, 2, 12),
(108, 3, 2, 15),
(109, 4, 1, 0)), *-- from submissions table, get maximum score for each hacker-challenge pair*t1 AS (
SELECT
hacker_id,
challenge_id,
MAX(score) AS max_score
FROM submissions
GROUP BY 1, 2 )*-- inner join this with the hackers table, sum up all maximum scores, filter to exclude hackers with total score of zero, and order result by total score and hacker ID*SELECT
t1.hacker_id,
h.name,
SUM(t1.max_score) AS total_score
FROM t1
JOIN hackers h
ON t1.hacker_id = h.hacker_id
GROUP BY 1, 2
HAVING SUM(max_score) > 0
ORDER BY 3 DESC, 1
13.有等级无等级(硬)
编写一个查询,在不使用窗口函数的情况下对下表中的分数进行排序。如果两个分数相等,则两者应该具有相同的等级。平局之后,下面的排名应该是下一个连续的整数值。来自 LeetCode 。
WITH scores (id, score)
AS (VALUES
(1, 3.50),
(2, 3.65),
(3, 4.00),
(4, 3.85),
(5, 4.00),
(6, 3.65)) *-- self-join on inequality produces a table with one score and all scores as large as this joined to it, grouping by first id and score, and counting up all unique values of joined scores yields the equivalent of DENSE_RANK() [check join output to understand]*SELECT
s1.score,
COUNT(DISTINCT s2.score) AS score_rank
FROM scores s1
JOIN scores s2
ON s1.score <= s2.score
GROUP BY s1.id, s1.score
ORDER BY 1 DESC
14.累计薪金总额
下表保存了几名雇员的月薪信息。编写一个查询,获取一名雇员在 3 个月内(不包括最近一个月)每月的工资总额。结果应该按雇员 ID 和月份升序排序。来自 LeetCode 。
WITH employee (id, pay_month, salary)
AS (VALUES
(1, 1, 20),
(2, 1, 20),
(1, 2, 30),
(2, 2, 30),
(3, 2, 40),
(1, 3, 40),
(3, 3, 60),
(1, 4, 60),
(3, 4, 70)), *-- add column for descending month rank (latest month = 1) for each employee*t1 AS (
SELECT *,
RANK() OVER (PARTITION BY id ORDER BY pay_month DESC) AS month_rank
FROM employee )*-- filter to exclude latest month and months 5+, create cumulative salary sum using SUM() as window function, order by ID and month*SELECT
id,
pay_month,
salary,
SUM(salary) OVER (PARTITION BY id ORDER BY month_rank DESC) AS cumulative_sum
FROM t1
WHERE month_rank != 1
AND month_rank <= 4
ORDER BY 1, 2
15.团队排名
编写一个查询,在 matches 表中显示所有比赛之后,返回 teams 表中每个队的得分。计分如下:输了得零分,平了得一分,赢了得三分。结果应该包括球队名称和分数,并按分数递减排序。如果出现平局,按字母顺序排列球队名称。
****
WITH teams (team_id, team_name)
AS (VALUES
(1, 'New York'),
(2, 'Atlanta'),
(3, 'Chicago'),
(4, 'Toronto'),
(5, 'Los Angeles'),
(6, 'Seattle')),matches (match_id, host_team, guest_team, host_goals, guest_goals)
AS (VALUES
(1, 1, 2, 3, 0),
(2, 2, 3, 2, 4),
(3, 3, 4, 4, 3),
(4, 4, 5, 1, 1),
(5, 5, 6, 2, 1),
(6, 6, 1, 1, 2)), *-- add host points and guest points columns to matches table, using case-when-then to tally up points for wins, ties, and losses*t1 AS (
SELECT
*,
CASE WHEN host_goals > guest_goals THEN 3
WHEN host_goals = guest_goals THEN 1
ELSE 0 END AS host_points,
CASE WHEN host_goals < guest_goals THEN 3
WHEN host_goals = guest_goals THEN 1
ELSE 0 END AS guest_points
FROM matches )*-- join result onto teams table twice to add up for each team the points earned as host team and guest team, then order as requested*SELECT
t.team_name,
a.host_points + b.guest_points AS total_points
FROM teams t
JOIN t1 a
ON t.team_id = a.host_team
JOIN t1 b
ON t.team_id = b.guest_team
ORDER BY 2 DESC, 1
16.没有购买产品的顾客
根据下表,编写一个查询来显示购买了产品 A 和 B 但没有购买产品 C 的客户的 ID 和姓名,按客户 ID 升序排序。
****
WITH customers (id, name)
AS (VALUES
(1, 'Daniel'),
(2, 'Diana'),
(3, 'Elizabeth'),
(4, 'John')),orders (order_id, customer_id, product_name)
AS (VALUES
(1, 1, 'A'),
(2, 1, 'B'),
(3, 2, 'A'),
(4, 2, 'B'),
(5, 2, 'C'),
(6, 3, 'A'),
(7, 3, 'A'),
(8, 3, 'B'),
(9, 3, 'D')) *-- join customers and orders tables on customer ID, filtering to those who bought both products A and B, removing those who bought product C, returning ID and name columns ordered by ascending ID*SELECT DISTINCT
id,
name
FROM orders o
JOIN customers c
ON o.customer_id = c.id
WHERE customer_id IN (SELECT customer_id
FROM orders
WHERE product_name = 'A')
AND customer_id IN (SELECT customer_id
FROM orders
WHERE product_name = 'B')
AND customer_id NOT IN (SELECT customer_id
FROM orders
WHERE product_name = 'C')
ORDER BY 1
17.中纬度(硬)
编写一个查询来返回下表中每个州气象站的中值纬度,四舍五入到最接近的十分之一度。注意,SQL 中没有 MEDIAN()函数!来自黑客排名。
WITH stations (id, city, state, latitude, longitude)
AS (VALUES
(1, 'Asheville', 'North Carolina', 35.6, 82.6),
(2, 'Burlington', 'North Carolina', 36.1, 79.4),
(3, 'Chapel Hill', 'North Carolina', 35.9, 79.1),
(4, 'Davidson', 'North Carolina', 35.5, 80.8),
(5, 'Elizabeth City', 'North Carolina', 36.3, 76.3),
(6, 'Fargo', 'North Dakota', 46.9, 96.8),
(7, 'Grand Forks', 'North Dakota', 47.9, 97.0),
(8, 'Hettinger', 'North Dakota', 46.0, 102.6),
(9, 'Inkster', 'North Dakota', 48.2, 97.6)), *-- assign latitude-ordered row numbers for each state, and get total row count for each state*t1 AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY state ORDER BY latitude ASC) AS row_number_state,
count(*) OVER (PARTITION BY state) AS row_count
FROM stations )*-- filter to middle row (for odd total row number) or middle two rows (for even total row number), then get average value of those, grouping by state*SELECT
state,
AVG(latitude) AS median_latitude
FROM t1
WHERE row_number_state >= 1.0*row_count/2
AND row_number_state <= 1.0*row_count/2 + 1
GROUP BY 1
18.最*分隔城市
从问题 17 的同一个表中,编写一个查询,返回每个州中相距最远的一对城市,以及这两个城市之间的相应距离(以度为单位,四舍五入到小数点后两位)。来自黑客排名。
WITH stations (id, city, state, latitude, longitude)
AS (VALUES
(1, 'Asheville', 'North Carolina', 35.6, 82.6),
(2, 'Burlington', 'North Carolina', 36.1, 79.4),
(3, 'Chapel Hill', 'North Carolina', 35.9, 79.1),
(4, 'Davidson', 'North Carolina', 35.5, 80.8),
(5, 'Elizabeth City', 'North Carolina', 36.3, 76.3),
(6, 'Fargo', 'North Dakota', 46.9, 96.8),
(7, 'Grand Forks', 'North Dakota', 47.9, 97.0),
(8, 'Hettinger', 'North Dakota', 46.0, 102.6),
(9, 'Inkster', 'North Dakota', 48.2, 97.6)), *-- self-join on matching states and city < city (avoids identical and double-counted city pairs), pulling state, city pair, and latitude/longitude coordinates for each city*t1 AS (
SELECT
s1.state,
s1.city AS city1,
s2.city AS city2,
s1.latitude AS city1_lat,
s1.longitude AS city1_long,
s2.latitude AS city2_lat,
s2.longitude AS city2_long
FROM stations s1
JOIN stations s2
ON s1.state = s2.state
AND s1.city < s2.city ),*-- add a column displaying rounded Euclidean distance* t2 AS (
SELECT *,
ROUND(( (city1_lat - city2_lat)^2 + (city1_long - city2_long)^2 ) ^ 0.5, 2) AS distance
FROM t1 ),*-- rank each city pair by descending distance for each state*t3 AS (
SELECT *, RANK() OVER (PARTITION BY state ORDER BY distance DESC) AS dist_rank
FROM t2 )*-- return the city pair with maximium separation*SELECT
state,
city1,
city2,
distance
FROM t3
WHERE dist_rank = 1
19.周期
编写一个查询来返回每个月的平均周期时间。周期时间是一个用户加入和他们的被邀请者加入之间经过的时间。未经邀请而加入的用户在“邀请者”一栏中有一个零。
WITH users (user_id, join_date, invited_by)
AS (VALUES
(1, CAST('01-01-20' AS date), 0),
(2, CAST('01-10-20' AS date), 1),
(3, CAST('02-05-20' AS date), 2),
(4, CAST('02-12-20' AS date), 3),
(5, CAST('02-25-20' AS date), 2),
(6, CAST('03-01-20' AS date), 0),
(7, CAST('03-01-20' AS date), 4),
(8, CAST('03-04-20' AS date), 7)), *-- self-join on invited by = user ID, extract join month from inviter join date, and calculate cycle time as difference between join dates of inviter and invitee*t1 AS (
SELECT
CAST(EXTRACT(MONTH FROM u2.join_date) AS int) AS month,
u1.join_date - u2.join_date AS cycle_time
FROM users u1
JOIN users u2
ON u1.invited_by = u2.user_id )*-- group by join month, take average of cycle times within each month*SELECT
month,
AVG(cycle_time) AS cycle_time_month_avg
FROM t1
GROUP BY 1
ORDER BY 1
20.连续三次
出席表记录了每天举行活动时人群中的人数。编写一个查询来返回一个显示高出勤率时段的日期和访问者计数的表,高出勤率时段定义为三个连续的条目(不一定是连续的日期),访问者超过 100 人。来自 LeetCode 。
WITH attendance (event_date, visitors)
AS (VALUES
(CAST('01-01-20' AS date), 10),
(CAST('01-04-20' AS date), 109),
(CAST('01-05-20' AS date), 150),
(CAST('01-06-20' AS date), 99),
(CAST('01-07-20' AS date), 145),
(CAST('01-08-20' AS date), 1455),
(CAST('01-11-20' AS date), 199),
(CAST('01-12-20' AS date), 188)), *-- add row numbers to identify consecutive entries, since date column has some gaps*t1 AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY event_date) AS day_num
FROM attendance ),*-- filter this to exclude days with > 100 visitors*t2 AS (
SELECT *
FROM t1
WHERE visitors > 100 ),*-- self-join (inner) twice on offset = 1 day and offset = 2 days*t3 AS (
SELECT
a.day_num AS day1,
b.day_num AS day2,
c.day_num AS day3
FROM t2 a
JOIN t2 b
ON a.day_num = b.day_num - 1
JOIN t2 c
ON a.day_num = c.day_num - 2 )*-- pull date and visitor count for consecutive days surfaced in previous table*SELECT
event_date,
visitors
FROM t1
WHERE day_num IN (SELECT day1 FROM t3)
OR day_num IN (SELECT day2 FROM t3)
OR day_num IN (SELECT day3 FROM t3)
21.通常一起购买
使用下面两个表,编写一个查询来返回最常一起购买的前三对产品的名称和购买频率。两种产品的名称应该出现在一列中。来源。
****
WITH orders (order_id, customer_id, product_id)
AS (VALUES
(1, 1, 1),
(1, 1, 2),
(1, 1, 3),
(2, 2, 1),
(2, 2, 2),
(2, 2, 4),
(3, 1, 5)),products (id, name)
AS (VALUES
(1, 'A'),
(2, 'B'),
(3, 'C'),
(4, 'D'),
(5, 'E')), *-- get unique product pairs from same order by self-joining orders table on order ID and product ID < product ID (avoids identical and double-counted product pairs)*t1 AS (
SELECT
o1.product_id AS prod_1,
o2.product_id AS prod_2
FROM orders o1
JOIN orders o2
ON o1.order_id = o2.order_id
AND o1.product_id < o2.product_id ),*-- join products table onto this to get product names, concatenate to get product pairs in one column*t2 AS (
SELECT CONCAT(p1.name, ' ', p2.name) AS product_pair
FROM t1
JOIN products p1
ON t1.prod_1 = p1.id
JOIN products p2
ON t1.prod_2 = p2.id )*-- grouping by product pair, return top 3 entries sorted by purchase frequency*SELECT *,
COUNT(*) AS purchase_freq
FROM t2
GROUP BY 1
ORDER BY 2 DESC
LIMIT 3
22.平均治疗效果(硬)
从总结研究结果的下表中,计算平均治疗效果以及 95%置信区间的上限和下限。将这些数字四舍五入到小数点后 3 位。
WITH study (participant_id, assignment, outcome)
AS (VALUES
(1, 0, 0),
(2, 1, 1),
(3, 0, 1),
(4, 1, 0),
(5, 0, 1),
(6, 1, 1),
(7, 0, 0),
(8, 1, 1),
(9, 1, 1)), -- get average outcomes, standard deviations, and group sizes for control and treatment groupscontrol AS (
SELECT
AVG(outcome) AS avg_outcome,
STDDEV(outcome) AS std_dev,
COUNT(*) AS group_size
FROM study
WHERE assignment = 0 ),treatment AS (
SELECT
AVG(outcome) AS avg_outcome,
STDDEV(outcome) AS std_dev,
COUNT(*) AS group_size
FROM study
WHERE assignment = 1 ),-- get average treatment effect sizeeffect_size AS (
SELECT t.avg_outcome - c.avg_outcome AS effect_size
FROM control c, treatment t ),-- construct 95% confidence interval using z* = 1.96 and magnitude of individual standard errors [ std dev / sqrt(sample size) ]conf_interval AS (
SELECT 1.96 * (t.std_dev^2 / t.group_size
+ c.std_dev^2 / c.group_size)^0.5 AS conf_int
FROM treatment t, control c )SELECT round(es.effect_size, 3) AS point_estimate,
round(es.effect_size - ci.conf_int, 3) AS lower_bound,
round(es.effect_size + ci.conf_int, 3) AS upper_bound
FROM effect_size es, conf_interval ci
23.滚动工资总额
下表显示了给定年份中前九个月员工的月薪。在此基础上,编写一个查询来返回一个表,该表按时间顺序显示上半年每个月雇员当月和接下来两个月的工资总额。
****
WITH salaries (month, salary)
AS (VALUES
(1, 2000),
(2, 3000),
(3, 5000),
(4, 4000),
(5, 2000),
(6, 1000),
(7, 2000),
(8, 4000),
(9, 5000)) -- self-join to match month n with months n, n+1, and n+2, then sum salary across those months, filter to first half of year, and sortSELECT
s1.month,
SUM(s2.salary) AS salary_3mos
FROM salaries s1
JOIN salaries s2
ON s1.month <= s2.month
AND s1.month > s2.month - 3
GROUP BY 1
HAVING s1.month < 7
ORDER BY 1
24.出租车取消率
从出租车服务的给定 trips 和 users 表中,编写一个查询,返回 10 月份前两天的取消率,四舍五入到两位小数,返回不涉及被禁乘客或司机的行程。来自 LeetCode 。
****
WITH trips (trip_id, rider_id, driver_id, status, request_date)
AS (VALUES
(1, 1, 10, 'completed', CAST('2020-10-01' AS date)),
(2, 2, 11, 'cancelled_by_driver', CAST('2020-10-01' AS date)),
(3, 3, 12, 'completed', CAST('2020-10-01' AS date)),
(4, 4, 10, 'cancelled_by_rider', CAST('2020-10-02' AS date)),
(5, 1, 11, 'completed', CAST('2020-10-02' AS date)),
(6, 2, 12, 'completed', CAST('2020-10-02' AS date)),
(7, 3, 11, 'completed', CAST('2020-10-03' AS date))),users (user_id, banned, type)
AS (VALUES
(1, 'no', 'rider'),
(2, 'yes', 'rider'),
(3, 'no', 'rider'),
(4, 'no', 'rider'),
(10, 'no', 'driver'),
(11, 'no', 'driver'),
(12, 'no', 'driver')) -- filter trips table to exclude banned riders and drivers, then calculate cancellation rate as 1 - fraction of trips completed, filtering to first two days of the month
SELECT
request_date,
1 - AVG(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS cancel_rate
FROM trips
WHERE rider_id NOT IN (SELECT user_id
FROM users
WHERE banned = 'yes' )
AND driver_id NOT IN (SELECT user_id
FROM users
WHERE banned = 'yes' )
GROUP BY 1
HAVING EXTRACT(DAY FROM request_date) <= 2
25.保留曲线(硬)
从下面的用户活动表中,编写一个查询来返回在加入后给定天数内保留(显示一些活动)的用户比例。按照惯例,用户在其加入日(第 0 天)被认为是活跃的。
WITH users (user_id, action_date, action)
AS (VALUES
(1, CAST('01-01-20' AS date), 'Join'),
(1, CAST('01-02-20' AS date), 'Access'),
(2, CAST('01-02-20' AS date), 'Join'),
(3, CAST('01-02-20' AS date), 'Join'),
(1, CAST('01-03-20' AS date), 'Access'),
(3, CAST('01-03-20' AS date), 'Access'),
(1, CAST('01-04-20' AS date), 'Access')), *-- get join dates for each user*join_dates AS (
SELECT
user_id,
action_date AS join_date
FROM users
WHERE action = 'Join' ),*-- create vector containing all dates in date range*date_vector AS (
SELECT CAST(GENERATE_SERIES(MIN(action_date), MAX(action_date),
'1 day'::interval) AS date) AS dates
FROM users ),*-- cross join to get all possible user-date combinations*all_users_dates AS (
SELECT DISTINCT
user_id,
d.dates
FROM users
CROSS JOIN date_vector d ),*-- left join users table onto all user-date combinations on matching user ID and date (null on days where user didn't engage), join onto this each user's signup date, exclude user-date combinations falling before user signup*t1 AS (
SELECT
a.dates - c.join_date AS day_no,
b.user_id
FROM all_users_dates a
LEFT JOIN users b
ON a.user_id = b.user_id
AND a.dates = b.action_date
JOIN join_dates c
ON a.user_id = c.user_id
WHERE a.dates - c.join_date >= 0 )*-- grouping by days since signup, count (non-null) user IDs as active users, total users, and the quotient as retention rate*SELECT
day_no,
COUNT(*) AS n_total,
COUNT(DISTINCT user_id) AS n_active,
ROUND(1.0*COUNT(DISTINCT user_id)/COUNT(*), 2) AS retention
FROM t1
GROUP BY 1
附录
一个常见问题:如果您在使用 CTE 时看到语法错误,请检查 cte 之间是否有逗号,并且在最后一个 CTE 后面没有逗号。
WITH input_table (column_1, column_2)
AS (VALUES
(1, 'A'), (2, 'B')), -- comma between CTEst1 AS (
SELECT *
FROM input_table
WHERE column_2 = 'A') -- no comma after last CTESELECT *
FROM t1
感谢本·拉卡尔和叶敏婷。
推特分析白俄罗斯当前的政治局势
在白俄罗斯没有办法进行独立的社会学研究,所以在分析政治偏好时,Twitter 和 Python 就派上了用场。
语境
亚历山*·卢卡申科担任白俄罗斯总统已经超过 26 年。他因侵犯人权、绑架政治对手并使其失踪以及滥用权力而被禁止进入欧盟和美国。
今年八月,当他面临“改选”时,社会动荡和抗议已经激增,一股前所未见的社会团结浪潮也随之而来。
反对派领导人和和平抗议者现在面临前所未有的镇压。活动家、政治家和记者在监狱里被绑架和折磨。
禁止进行和发表独立的社会学研究。
一些互联网调查显示,卢卡申科拥有不超过 3%的白俄罗斯民众的支持,而他公开声称这一数字约为 72-78%。我决定使用 Python 和 NLP 库来检查这些声明。
本次简短研究的目标:
–衡量 Twitter 用户对总统候选人的真实忠诚度。
–确定主要作者、标签和主题。
跟踪国际社会对当前事件的反应。
工具包:Python,Pandas,Numpy,Matplotlib,Seaborn,SpaCy,Scikit-learn,Vader,LDA。
检索 Twitter 数据
从 6 月初开始,我搜集了超过 24 万条与白俄罗斯和总统候选人相关的推文。我获得了 49 种不同语言的推文,平均每天有 3 条 4K 推文。
由于官方 Twitter API 不允许获取超过 1 周的推文,所以我使用 GetOldTweets3 来解析历史数据(我强烈建议查看@noahberhe 的代码,它修复了许多错误)。如果有兴趣,你可以在这里下载一个原始数据集。
下面的代码用俄语(红色)、英语(蓝色)和其他语言(黄色)创建了从 6 月 1 日到 7 月 26 日的三个每日推文图。
#Importing some basic librariesimport pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from datetime import date, timedelta, datetime# Converting the created_at column to np.datetime objectdf['date'] = pd.to_datetime(df['date'])#Creating separate dataframes in Russian, English and other languagesdf_ru =df[df.language == 'ru']
df_en =df[df.language == 'en']
df_else =df[(df['language'] != 'ru') & (df['language'] != 'en')]#Plotting tweets dynamics in different languagesdf_ru.resample('D', on='date').text.count().plot(color='r',figsize=(16,6), label = 'russian')
df_en.resample('D', on='date').text.count().plot(color='b',figsize=(16,6), label = 'english')
df_else.resample('D', on='date').text.count().plot(color='y',figsize=(16,6),label = 'other languages')
plt.ylabel('number of daily tweets')
plt.legend()
最受欢迎的推文
下面是打印最受欢迎的 tweet 的代码示例。
fav_max = np.max(df_ru['favorites'])
fav = df_ru[df_ru.favorites == fav_max].index[0]print("Tweet with most likes: \n{}".format(df_ru['text'][fav]))print("Number of likes: {}".format(fav_max))
print("Username: {}".format(df_ru['username'][fav]))
print ("Translation: {}".format(translator.translate(df_ru['text'][fav]).text))
在指定时间段内最受欢迎的俄语推文:
下面翻译
简要介绍一下今天白俄罗斯的情况:
候选人 Babariko 收集了 425,000 个签名,但他们在去选举委员会的路上拘留了他和他的儿子,不允许律师进入,房子被搜查。
候选人 Tikhanovskaya 被威胁要绑架她的孩子。警方拒绝调查。"
最受欢迎的英语推文:
用俄语发推文的前 25 名用户
下面是绘制前 25 名最活跃用户的直方图代码示例。
users_count = df_ru['username'].value_counts()
users_count = users_count[:25,]
plt.figure(figsize=(16,8))
sns.barplot(users_count.index, users_count.values, alpha=0.8)
plt.title('Top 25 users')
plt.ylabel('Number of Occurrences', fontsize=12)
plt.xlabel('Users', fontsize=12)
plt.xticks(rotation=65, horizontalalignment='right')
plt.show()
用英语发推文的前 25 名用户
热门标签
白俄罗斯,#白俄罗斯,#明斯克,#特比,#布雷斯特比,#新闻,#选举,#卢卡申科,#特比邦,#乔维德 19,#冠状病毒,#乔维德,#乔维德 19,#fcdm1927,#人权,#人权暴力,#OSCE,#明斯克集团,# 2020 年幸存者,#军事
衡量对总统候选人的忠诚度
然后,我使用库 SpaCy 分离出与卢卡申科及其主要对手 Babariko、Tsepkalo 和 Tikhanovskaya 有关的最近推文(2020 年 7 月 18 日至 26 日)。为了使用情感分析和实体提取库,我必须将推文翻译成英语。我用了谷歌翻译 API。下面是一个简单的方法:
#Importing librariesimport json
from apiclient.discovery import build
import os
import time
pd.options.mode.chained_assignment = None
import requests
import socket
from googleapiclient import discovery#Creating GoogleAPI function def translation_googleAPI(query, target_language):socket.setdefaulttimeout(600) # set timeout to 10 minutes
service = discovery.build('translate','v2',developerKey='YOURKEY')
collection = service.translations()
request = collection.list(q=query, target=target_language)
response = request.execute()
response_json = json.dumps(response)
translation = ((response['translations'][0])['translatedText'])
print (translation)
print('....')
return translation#Separating tweets related to a particular candidatedf_lukashenko = df[df.topical_persons == 'lukashenko']#Lambda function to create a new column with translationdf_lukashenko['translated'] = df_lukashenko['text'].apply(lambda x: translation_googleAPI(x,'en'))
让我们探索一下每个候选人的数据集。
卢卡申科
8023 条推文
在此期间,卢卡申科在推特上被提及的次数比任何其他候选人都多至少 8 次。7 月 20 日的高峰与宣布候选人登记有关。
下面是使用 VADER 识别推文情绪的代码:
#Let's import the library and create a functionfrom vaderSentiment.vaderSentiment import SentimentIntensityAnalyzerdef vader_scorer(df):
analyzer = SentimentIntensityAnalyzer()
vader_scores = df.loc[:,'translated'].map(analyzer.polarity_scores)dvec = DictVectorizer()
vader_scores = dvec.fit_transform(vader_scores)
vader_scores = pd.DataFrame(vader_scores.toarray(),columns=dvec.feature_names_)
return vader_scores#Adding scores to our datasetvader_scores = vader_scorer(df_lukashenko)
df_lukashenko = pd.concat([df_lukashenko,vader_scores], axis=1)
df_lukashenko = df_lukashenko.drop_duplicates(subset='tweet_id', ignore_index=True)# Creating a new column called 'sentiment_class'
sentimentclass_list = []for i in range(0, len(df_lukashenko)):
# current 'compound' score:
curr_compound = df_lukashenko.iloc[i,:]['compound']
if (curr_compound <= 1.0 and curr_compound >= 0.55):
sentimentclass_list.append(5)
elif (curr_compound < 0.55 and curr_compound >= 0.10):
sentimentclass_list.append(4)
elif (curr_compound < 0.10 and curr_compound > -0.10):
sentimentclass_list.append(3)
elif (curr_compound <= -0.10 and curr_compound > -0.55):
sentimentclass_list.append(2)
elif (curr_compound <= -0.55 and curr_compound >= -1.00):
sentimentclass_list.append(1)# Adding the new column 'sentiment_class' to the dataframedf_lukashenko['sentiment_class'] = sentimentclass_list#Plotting the distribution of sentiment_classimport seaborn as sns
plt.figure(figsize = (6,4))
sns.set_palette('coolwarm')
sns.countplot(df_luk['sentiment_class'])
plt.title('Countplot of sentiment class')
plt.xlabel('Sentiment class: from negative to positive')
plt.ylabel('No. of tweets')
plt.show()
提到卢卡申科的推特的情绪分布均匀。如果我们看看最负面的推文,它们通常是在谴责政权的暴力。如果我们看看积极的推文,它主要与团结白俄罗斯人民反对独裁主义有关。
下面是一个负面推文的例子:
下面翻译
“卢卡申科不再隐藏他的法西斯本质、仇恨和对人民的蔑视,正在为他的终身权力与人民进行战争。失去权力后,法西斯分子卢卡申科将被定罪。法西斯分子卢卡申科因谋杀、恐怖主义和贪污而受到的唯一惩罚是被枪毙”。
下面是一条被 VADER 定性为正面的推文:
提到卢卡申科的最受欢迎的推文:
下面翻译
“支持无畏的白俄罗斯妇女斯韦特兰娜·蒂哈诺夫斯卡娅的最*规模集会现在正在戈梅尔举行,她向残酷和卑鄙的独裁者卢卡申科发出了挑战。人山人海——灯笼的海洋。他们的光芒照亮了我们通往自由的道路!白俄罗斯万岁!”
以下是卢卡申科数据集的代码和单词 cloud 月 18-16 日,翻译成英文):
#Transforming translated tweets to the plain textdf_lukashenko.translated.to_csv('lukashenko_tweets.txt', header=None, index=None, sep=' ', mode='a')# Importing packagesimport matplotlib.pyplot as plt
%matplotlib inline# Defining a function to plot word clouddef plot_cloud(wordcloud):
# Set figure size
plt.figure(figsize=(40, 30))
# Display image
plt.imshow(wordcloud)
# No axis details
plt.axis("off");# Extracting the plain text contenttext = open("lukashenko_tweets.txt", 'r', encoding='utf-8').read()
# Clean text
text = re.sub(r'==.*?==+', '', text)
text = text.replace('\n', '')# Importing packagefrom wordcloud import WordCloud, STOPWORDS
stop_words = ["https","html","youtu", "via", "d0","d1", "will", "quot","amp","alexander","ru", "lukashenka", "lukashenko", "belarus", "youtube", "video"] + list(STOPWORDS)# Generating word cloudwordcloud = WordCloud(width = 3000, height = 2000, random_state=1, background_color='white', colormap='Set2', collocations=False, stopwords = stop_words).generate(text)# Plotplot_cloud(wordcloud)
采普卡洛
1024 条推文
7 月 24 日的高峰与 Valeriy Tsepkalo 在收到内部消息后带着两个小孩逃离该国有关,他将被逮捕,孩子们将被送往孤儿院。
Valeriy Tsepkalo 离开后,他的妻子 Veronika Tsepkalo 接管了政治活动,与其他反对派领导人共同努力。因此,当前数据集的情感主要与 Veronika 相关。
VADER 将带有负面情绪的推文归类为:
下面翻译
“威胁不在候选人身上。威胁在于人民本身。厌倦了生活在屈辱和恐惧中的人。在前往第一次集会的路上,Babariko、Tsepkalo 和 Tikhanovskaya 联合总部的代表讲述了他们计划如何在选举中进一步斗争”。
正面推文的例子:
下面翻译
“三个女孩完美地互补。科列斯尼科娃——最善良的动物,带着富有感染力的微笑和真正的希望之光。tikhanovskaya——激发胜利信念的意志力。tsepkalo——最聪明和理性思考的领导者”。
提到 Tsepkalo 的最受欢迎的推文:
下面翻译
“继 Tikhanovskaya 之后,Tsepkalo 也被迫带着孩子出国。仿佛来自战区。多么令人沮丧。(他们还试图把孩子从被投进监狱的 2010 年候选人 Sannikov 身边带走)”。
Tsepkalo 数据集的词云:
巴巴里科
684 条推文
从 18 年 7 月到 26 年 7 月,与巴巴里科相关的推文
最近,有两个明显的高峰:
7 月 19 日,中央选举委员会表示,巴巴里科没有登记为总统候选人。7 月 24 日,一项新的指控(行贿)被曝光。
提到 Babariko 的推文的情绪主要是负面的,尽管这种负面情绪来自于人们对不公平的指控和拘留感到愤怒的事实。
下面是一条带有负面情绪的推文:
下面翻译
“卢卡申科说,他被捕的竞争对手巴巴里科“失声痛哭”,“开始就 Belgazprombank 案作证”;我可以想象那个人在那里是如何被折磨的”。
这是这段时间最正面的推文(关于 Babariko 总部负责人 Maria Kolesnikova):
下面翻译
“聪明、美丽、有才华——不可思议的玛丽亚·科列斯尼科娃!!!维克多有着惊人的团结最优秀者的能力,巴巴里科总部的团队就是最好的证明。玛丽亚,我个人非常感谢你为我们国家的美好未来所做的一切!”
这是提到巴巴里科的最受欢迎的推文:
下面翻译
“维克多和爱德华·巴巴里科被捕已经一个月了。我们非常想念你!我们怀念维克多·德米特里耶维奇(Viktor Dmitrievich)愉快的声音,他向来到办公室的每一个人打招呼。通过他在会议上的笑话和亲密的溪流,通过他在会议室里的咖啡味道——一切”。
下面是巴巴里科的词云:
季哈诺夫斯卡娅
1015 条推文
联合总部(从左至右):韦罗妮卡·采普卡罗、斯维特拉娜·季哈诺夫斯卡娅和玛丽亚·科列斯尼科娃(维克多·巴巴里科总部负责人)。图:卡纳普列夫+莱迪克
Svetlana Tikhanovskaya 是一位 37 岁的家庭主妇,有两个小孩,当她的丈夫 Sergey Tikhanovskiy 在授权的 piquet 期间受到挑衅而被监禁时,她决定注册成为总统候选人。Tikhanovskaya 是仍留在白俄罗斯的*规模支持的候选人,没有坐牢。
采普卡罗、巴巴里科和季哈诺夫斯卡娅总部联合起来反对卢卡申科。
Tikhanovskaya 的受欢迎程度在她注册成为候选人后急剧上升,并一直保持稳定。
联合总部的支持率很高。白俄罗斯在其现代史上从未见过对反对派候选人的如此支持。不管负面背景如何,推文的整体情绪相当积极。
带有负面情绪的推文示例:
下面翻译
“总统候选人斯维特拉娜·季哈诺夫斯卡娅因受到威胁带着她的孩子离开了这个国家。6 月中旬,Tikhanovskaya 说,一个不知名的人威胁说,如果她不停止总统竞选,就把她送进监狱,并带走她的孩子”。
正面推文示例:
最受欢迎的推文来自账户 sadmikalai ,讽刺地代表卢卡申科 15 岁的儿子发推文:
下面翻译
“爸爸非常喜欢季哈诺夫斯卡娅所说的话,他甚至决定为她投票”。
下面是 Tikhanovskaya 的词云:
国际社会对当前事件的反应
使用 SpaCy,我分离了提到美国和欧盟的推文(从 6 月 1 日到 7 月 26 日的推文)。显然有更多与美国相关的推文,尽管其中*多数与政治不太相关。我没有注意到高级官员对白俄罗斯局势表达了激进的立场。虽然欧盟的推特更少,但立场更具决定性。
例如,转发量最*的帖子是唐纳德·图斯克强烈谴责白俄罗斯当局的行为。
我注意到美国相关数据集中有一个不寻常的话题(Elon Musk — Space — Space Suite)。我决定更深入地研究它与白俄罗斯和白俄罗斯选举有什么关系。我们开始吧:
简要结论
- 卢卡申科在推特上被提及的次数比最接近的竞争对手至少多 8 次。负面情绪是激进的(呼吁暴力),而*多数正面推文是讽刺性的或假阳性的。
- Tsepkalo 和 Babariko 数据集中的负面情绪主要与被捕和受到威胁有关。
- Svetlana Tikhanovskaya 比任何其他候选人获得了更多积极的推文。她的人气增长极快,并保持稳定。
- 推特上没有国际社会对白俄罗斯局势的决定性反应。唐纳德·图斯克是为数不多的积极表达担忧的外国政治家之一。
- 白俄罗斯人正在*量写信给埃隆·马斯克,要求尽快将卢卡申科送入太空,并提供卫星互联网 Starlink。人们有理由担心互联网会在选举日被封锁。
后续步骤
一系列受控行为实验表明,当人们对某个问题进行道德化时,他们更有可能支持针对该问题的暴力抗议( Nature ,2016)。
目前,我正在研究一个应用程序,通过识别推文中道德和伦理的百分比,并将其与 Twitter 位置数据进行匹配,来实时预测暴力抗议。如果你愿意合作,请随时联系我。
南方公园第十六季第十三集
更多代码在 Github 上。
这里你可以下载数据集。
请继续关注并实时跟踪这场革命。
领英脸书 推特
Twitter 分析:“WeRateDogs”
一个数据争论和分析博客
来源:WeRateDogs 推特
一个好的数据管理者知道如何集成来自多个数据源的信息,解决常见的转换问题,以及解决数据清理和质量问题。
来自 Udacity 的数据分析师 Nanodegree 项目向我介绍了当今业界正在使用的各种数据争论方法。在其中一个争论不休的项目中,我有机会经历了整个数据分析过程——收集数据、清理数据、分析数据,最后将数据可视化。
该数据集是 Twitter 用户 @dog_rates 的推文档案,也称为 WeRateDogs。WeRateDogs 是一个推特账户,用幽默的评论给人们的狗打分。
来源:WeRateDogs 推特
这个项目的*部分焦点都集中在数据争论上。那么到底什么是数据角力呢?
数据争论指的是将可用的原始数据清理、重组和丰富成更有用的格式的过程。
我在这个项目中使用了各种 python 库,下面是我开始使用的。
**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**
**import** **matplotlib.pyplot** **as** **plt**
%**matplotlib** inline
收集数据:
这个项目的数据有三种不同的格式:
1。Twitter 存档文件——WeRateDogs:WeRateDogs 下载了他们的 Twitter 存档文件,并专门分享给本项目使用。这个档案包含基本的 tweet 数据(tweet ID、时间戳、文本等。)他们在 2017 年 8 月 1 日发布的所有 5000 多条推文。
这是由 Udacity 以编程方式提取的,并作为 csv 文件提供给用户使用。
pd.read_csv('twitter-archive-enhanced.csv')
2。图像预测文件:tweet 图像预测,即根据神经网络在每个 tweet 中出现什么品种的狗,存储在该文件中。它以 tsv 格式托管在 Udacity 的服务器上,必须使用 Url 以编程方式下载。
Python 的 request 库用于从 web 上收集这些数据。 请求 是 python 中一个多功能的 HTTP 库,具有各种应用。它的应用之一是使用文件 URL 从 web 下载或打开文件。
3。Twitter API — JSON 文件:通过使用 WeRateDogs Twitter 档案中的 tweet IDs,我使用 Python 的 tweepy 库查询了 Twitter API 中每条 tweet 的 JSON 数据。
Tweepy 是一个开源的 Python 包,给你一个非常方便的方法来用 Python 访问 Twitter API。你可以在 twitter 和使用 Python 访问 Twitter API 的 中找到关于 设置应用的更多细节。
此外,每条 tweet 的全部 JSON 数据都存储在一个名为 tweet_json.txt 的文件中。
每个 tweet 的 JSON 数据都被写到自己的行中,然后。txt 文件被逐行读入熊猫数据帧。
评估数据
收集数据后,这三个表被保存,并以可视化和编程的方式进行评估。在这两次评估中,我在所有三个数据框架中寻找不干净的数据,即整洁性和质量问题。
质量: 低质量数据通常被称为脏数据。脏数据的内容有问题。数据质量维度是完整性、有效性、准确性和一致性。
整洁: 凌乱的数据通常被称为“凌乱”的数据。杂乱的数据在结构上有问题。整理数据是哪里:
1。每个变量形成一列。
2。每个观察值形成一行。
3。每种类型的观测单位形成一张表。
视觉评估为我提供了一些问题,如三个数据集中的非描述性列标题和重复列。
程序性评估实际上给了我*部分质量问题,例如三个数据集中出现的不正确的数据类型和重复数据。然后,我在 python 笔记本中记录了我的评估要点。
清理数据
清洁意味着根据我们所做的评估来提高质量和整洁度。
提高质量并不意味着改变数据,让它说一些不同的东西——这是数据欺诈。质量改进是指不准确时纠正,不相关时删除,缺失时更换。
类似地,改善整洁意味着转换数据集,使得每个变量是一列,每个观察是一行,每种类型的观察单元是一个表。
我遵循程序化的数据清理流程,即 定义、编码和测试。 我将我在评估步骤中的观察转换成定义的问题,将这些定义翻译成复杂的代码来修复这些问题,然后测试三个数据集以确保操作有效。
存储数据
清理完数据后,我发现不需要三个数据集。所有的数据都可以很容易地转换成一个文件。因此,我将三个数据帧连接在一个公共属性 twitter_id 上,以创建 twitter_archive_master.csv。
分析数据
使用这些新清理的 WeRateDogs Twitter 数据,创建了有趣和可信的分析和可视化,以传达调查结果。
WeRateDogs 是一个 Twitter 账户,用幽默的评论给人们的狗打分。这些评级的分母几乎都是 10。但是分子数几乎总是*于 10。11/10、12/10、13/10 等。为什么?因为“他们是好狗布伦特”。WeRateDogs 认为几乎所有的狗都应该得到 10 分或者更多。每条推文的转发次数和收藏(即“喜欢”)次数是另一个有趣的参数。
来源:推特
那么,如何处理这些评级呢?对于狗,最常见的问题是哪只狗最受欢迎?我们能得到转发、收藏和收视率之间的任何关系吗?我已经分析了这些数据来得到这些问题的答案。
matplotlib 和 seaborn 等 Python 库为我的分析提供了一些很酷的可视化工具。
最受欢迎的狗品种
根据第一次预测分析我的数据,我得到了十*最受欢迎的狗品种。
作者图片
金毛猎犬是最受欢迎的狗。这些狗有最多的推文,这使它们在我们最受欢迎的名单上。
我们的数据中有狗的 jpg_url。Pythons 的 io 模块用于管理与文件相关的输入输出操作。我使用 Pillow,友好的 PIL fork-Python 图像库,得到了我们最受欢迎的狗金毛猎犬的图像,它在统计中名列榜首。
下面是结果。
作者图片
转发、收藏和评级
令人惊讶的是,许多推文被转发。一些推文有近 40000 次转发。
我试图通过关联图(python 中最酷的图之一)来找出转发、评级和收藏之间是否有任何关系。我用 seaborn 库生成了一个热图。
作者图片
转发量和收视率之间没有明确的关系。但是收藏和转发之间有很强的正相关性,即转发次数和收藏次数之间的线性关系。下面的散点图也证实了这一点。
作者图片
因此,这里的见解是转发通常是最受欢迎的,但我们不能说所有的转发都是最受欢迎的,反之亦然。
在我的分析中,我发现给定推文的收藏数和转发数之间有很强的线性关系。金毛寻回犬是最受欢迎的犬种,它与拉布拉多寻回犬、彭布罗克犬、吉娃娃犬和哈巴狗一起构成了数据中最常见的五个犬种。
结论
可以分析该数据集中的许多其他参数,以获得不同的洞察力和可视化效果。因为这个项目的主要焦点是数据争论,所以在这个部分花了更多的时间。
为了 Udacity ,我不得不提交两份报告以及Python 笔记本。第一份报告是内部文档 the wrangle_report ,它简要描述了争论的结果;第二份报告是外部文档 the act_report ,它传达见解并显示从争论的数据中产生的可视化结果。
数据鼓吹者非常了解他们的数据,并且总是在寻找丰富数据的方法。我已经用令人惊奇的 Python 库做了同样的事情。这个项目是我最有趣的数据体验之一,以至于我决定写我的第一篇博客。您可以在我的 Github 上查看项目和我的报告。
感谢您的阅读!
使用 R 进行 Twitter 数据获取和预处理的深度教程
在 Unsplash 上由 Carlos Muza 拍摄的照片
实践教程
如何通过数据洞察印尼*选
作为微博和社交网络服务的 Twitter
Twitter 是一种社交媒体,用于传播特定主题的信息或观点,可以是社会中发生的社会或政治现象。Twitter 用户可以使用 280 个字符、照片、声音、视频、链接等来发布他们的观点。
在我看来,Twitter 最有趣的元素之一是一个 趋势话题 。它指的是在特定领域中经常讨论的词或话题,在同一日期和时间内,它的音量最高。
这一分析是在印度尼西亚公共选举期间进行的,当时总统候选人的竞选团队通过社交媒体进行了*量的政治活动,其中之一就是 Twitter。这就像是热门话题的在线战争。每个团队都宣传自己的总统候选人,甚至使用了黑人竞选。作为一名数据科学家,这就像是可以分析的数据宝藏,所以我们通过分析找出一种模式。但是,我们如何获得 Twitter 数据呢?下一步是什么?这将在本故事中涉及。尽情享受吧!
如何使用 API 从 Twitter 获取数据
好吧,一般来说,Twitter API 和网络抓取可以轻松获取数据。Twitter API 可以从我们的 Twitter 帐户中提取数据——特定日期和时间的推文、我们帐户的关注者数量等。
API 代表应用程序编程接口是连接两个应用程序之间数据的工具,正式格式为 JSON。
这个端点允许我们发出一个特定的请求,例如,某个时间段的 tweets,并将其返回给我们的应用程序。在这种情况下,R 的行为类似于应用程序。为了开始使用 Twitter API,我们需要一个 Twitter 帐户,它将使我们能够访问 API 密钥(消费者密钥)。所以获取 Twitter API 有几个步骤。
- 创建一个 Twitter 帐户。请前往 Twitter 平台 注册一个免费账户。它允许我们访问开发者网站。
用于登录和注册的 Twitter 页面(图片由作者提供)
- 前往 Twitter 开发者网站,创建一个新的应用程序。开发商所在地是 这里是
未创建任何应用程序的开发人员网站(图片由作者提供)
- 您需要说明使用 Twitter 开发者工具的一个主要原因。是为了个人项目、教育、研究还是其他?你所需要的是回答和填写某些问题,并同意条款和政策
- 该过程可能需要一些时间。我们应该能够在“Keys and Access Tokens”选项卡下找到所有 Twitter API 密钥细节的所有内容
Twitter API 已经在开发者网站上生成(图片由作者提供)
如何使用 R 连接 Twitter API
现在,我们将通过 API 连接我们的应用程序(R)和 Twitter。在我们深入讨论这个之前,请确保我们已经在 r 中安装了**twitterR**
和**tm**
库。
# Install libraries
install.packages('twitteR') # Library for Twitter API
install.packages('tm') # Library for text mining
需求完成后,我们开始使用 API 将应用程序与 Twitter 连接起来。
# Import the library
library(twitteR)
library(tm)
# Crawling data from Twitter
api_key = 'xxxxxxxxxxxxxxxxxxxxxxxx'
api_secret = 'xxxxxxxxxxxxxxxxxxxxxxxx'
access_token = 'xxxxxxxxxxxxxxxxxxxxxxxx'
access_token_secret = 'xxxxxxxxxxxxxxxxxxxxxxxx'
# Connect with Twitter API
setup_twitter_oauth(api_key, api_secret, access_token, access_token_secret)
# Search any tweets
data_twitter = searchTwitter(searchString = 'bromo OR ijen',
since = '2020-02-26',
until = '2020-02-28',
n = 100,
lang = 'id')
# Convert data to dataframe
data_twitter_df = twListToDF(data_twitter)
View(data_twitter_df)
使用**searchTwitter**
功能,我们可以搜索某个时间段的任何推文,推文数量,以及语言。你可以在文件上找到任何参数。请记住,使用**twitterR**
库搜索推文有一些规则。
- 查询
**"politics"**
搜一个字政治 - 查询
**"indonesia politics"**
来搜索词语印尼和政治(忽略顺序) - 查询
**"\"indonesia politics\""**
以搜索短语印度尼西亚政治 - 查询
**"indonesia OR politics"**
以搜索一个词印度尼西亚或政治或两者 - 查询
**"indonesia -politics"**
搜索印尼无政治一词 - 查询
**"#politics"**
搜索标签政治 - 查询
**"@politics"**
来搜索提及政治
如何进行数据预处理
最后,我们把手伸向这个故事的主题!但是,在我们进行数据预处理之前,我们必须知道在文本预处理中有三个主要任务要做:
**case-folding**
是指将数据转换成小写格式的过程**filtering**
表示从数据中移除未使用的字符,如数字、标点符号、标签、提及、URL、HTML 或 CSS 元素**tokenization**
从文本数据中生成一组单词(称为标记)的过程**normalization**
分为两个任务,即**lemmatization**
和**stemming**
**stopwords removal**
指的是提取最常见的字典单词的过程——称为停用词(英语、印度尼西亚语或其他语言,具体取决于我们的主要分析)
为了简化我们的任务,我们想创建一个包含一些文本预处理功能的 R 文件。所以,我们只需要在主 R 文件中需要它的时候调用或者导入它。为此,创建一个名为**cleansing.R**
的 R 文件,包含下面的脚本。
# Import libraries
library(tm)
library(xml2)
library(stringr)
library(dplyr)
library(katadasaR) # Indonesia corpus for text mining
# URL removal
removeURL <- function(x){
gsub("http[^[:space:]]*", "", x)
}
# Mention removal
removeMention <- function(x){
gsub("@\\w+", "", x)
}
# Hashtag removal
removeHashtag <- function(x){
gsub("#\\S+", "", x)
}
# Carriage removal
removeCarriage <- function(x){
gsub("[\r\n]", "", x)
}
# Emoticon removal
removeEmoticon <- function(x){
gsub("[^\x01-\x7F]", "", x)
}
# Retweet removal
removeRT <- function(x){
gsub("(rt|via)((?:\\b\\W*@\\w+)+)", "", x)
}
# Invoice removal
removeInvoice <- function(x){
gsub("inv/[0-9]+/+[xvi]+/[xvi]+/[0-9]+", "", x, ignore.case = T)
}
# HTML removal
unescapeHTML <- function(str) {
xml2::xml_text(xml2::read_html(paste0("<x>", str, "</x>")))
}
# Function to make a whitespace
toSpace <- content_transformer(function(x, pattern){
gsub(pattern, " ", x)
})
# Spell Normalization Function
spell.correction = content_transformer(function(x, dict){
words = sapply(unlist(str_split(x, "\\s+")),function(x){
if(is.na(spell.lex[match(x, dict$slang),"formal"])){
x = x
} else{
x = spell.lex[match(x, dict$slang),"formal"]
}
})
x = paste(words, collapse = " ")
})
# Stemming Words
stemming = function(x){
paste(sapply(unlist(str_split(x,'\\s+')),katadasar),collapse = " ")
}
开始了。我们将运行 R 的主脚本来进行文本预处理。同样,这个分析是用印度尼西亚语进行的。因此,对于标准化和停用词删除,它将不同于英语,但我们只需要调整数据。
原始数据样本(数据预处理前)(图片由作者提供)
首先,我们只是执行案例折叠和过滤任务。我们走吧!
# Import libraries
library(tm)
# Import external function
source(file = 'path-to/cleansing.R')
# Read the data
data_tweet = data_twitter_df$text
data_tweet = unique(data_tweet)
# Work with corpus
tweet_corpus = VCorpus(VectorSource(data_tweet))
# Case folding
tweet_corpus = tm_map(tweet_corpus,content_transformer(tolower))
# Retweet removal
tweet.corpus = tm_map(tweet.corpus,content_transformer(removeRT))
# Hashtag removal
tweet_corpus = tm_map(tweet_corpus,content_transformer(removeHashtag))
# URL removal
tweet_corpus = tm_map(tweet_corpus,content_transformer(removeURL))
# HTML removal
tweet_corpus = tm_map(tweet_corpus,content_transformer(unescapeHTML))
# Mention removal
tweet_corpus = tm_map(tweet_corpus,content_transformer(removeMention))
# Carriage removal
tweet_corpus = tm_map(tweet_corpus,content_transformer(removeCarriage))
# Emoticon removal
tweet_corpus = tm_map(tweet_corpus,content_transformer(removeEmoticon))
# Invoice removal
tweet_corpus = tm_map(tweet_corpus,content_transformer(removeInvoice))
# Remove additional symbols to white space
# punctuation
tweet_corpus = tm_map(tweet_corpus,toSpace,"[[:punct:]]")
# numbers
tweet_corpus = tm_map(tweet_corpus,toSpace,"[[:digit:]]")
# Eliminate extra white spaces
tweet_corpus = tm_map(tweet_corpus,stripWhitespace)
运行上面的脚本后,我们很容易看到案例折叠和过滤过程前后的差异。
折叠后的数据(图片由作者提供)
过滤过程后的数据(图片由作者提供)
接下来,我们在检查站见面。在进行文本规范化和停用词移除之前,我们需要下载两种数据作为我们的词典:
**colloquial-indonesian-lexicon.txt**
包含一对印尼语中的非正式和正式词汇。在这里下载**stopwords-id.txt**
包含印度尼西亚语中最常见的单词,这些单词在文本挖掘中没有足够的重要意义。下载 这里
# Continued from the previous script
# Normalization - lemmatization
spell_lex = read.csv(file = '../colloquial-indonesian-lexicon.txt',
header = TRUE,
sep = ',',
stringsAsFactors = FALSE)
tweet_corpus = tm_map(tweet_corpus,spell.correction,spell_lex)
# Normalization - stemming
tweet_corpus = tm_map(tweet_corpus,content_transformer(stemming))
# Stopwords removal
df_tweet = data.frame(text = sapply(tweet_corpus,as.character),
stringsAsFactors = FALSE)
rm_stopword = VCorpus(VectorSource(df_tweet$text))
# Using edited stopword list
stopwords_id = readLines('path-to/stopwords-id.txt')
rm_stopword = tm_map(rm_stopword,removeWords,stopwords_id)
# Save the data
df_clean = data.frame(text = sapply(rm_stopword,as.character),
stringsAsFactors = FALSE)
View(df_clean)
我们数据的最新版本如下:
归一化和停用词删除后的数据(图片由作者提供)
结论
数据预处理是文本挖掘中至关重要的任务。它决定了整个分析的输出,就像 GIGO(垃圾输入垃圾输出)一样。社交媒体数据太乱了,所以为了让我们的分析更可靠,分析师必须正确地进行数据预处理。请记住,预处理没有系统的顺序,停用词删除可以是第一个要做的任务,并且这个过程可以根据我们的需要重复进行。
请前往我的 GitHub repo 查看完成的代码。
这个故事和前面的 Twitter 数据可视化使用 R 的故事有关。让我们编码,分析,重复!
参考
[1]囟门梭菌。 如何获取、使用、&受益于 Twitter 的 API (2020)、https://blog.hubspot.com/。
[2] N .塞蒂亚布迪。 rtweet:抓取数据 Twitter 孟古那坎 R (2019),https://www . nurandi . id/blog/rtweet-Crawling-Data-Twitter-孟古那坎-r/ 。
Twitter 数据挖掘——测量用户的影响力
追随者和影响力;科学见解
当有人说一个人在 twitter 上有影响力时,首先想到的是‘他/她可能有很多粉丝’;这可能对每个人都不一样,但我确实有过这样的想法,我也遇到过几个这样想的人。
这些影响者到底是谁?这个网站给出了详细的定义,对于这个项目,我们将考虑使用 twitter 的社交媒体影响者。
社交媒体中的影响者是那些因其在特定主题上的知识和专长而建立了声誉的人。他们在自己喜欢的社交媒体渠道上定期发布关于这个话题的帖子,并产生了*量热情参与的人,这些人密切关注他们的观点。
作为在 10 学院受训的一个好处,我开始意识到有很多追随者并不等于有影响力。这个项目展示了我测量用户在 twitter 上的影响力的科学方法,采用的指标包括:popularity_score 和 reach_score,同时考虑到 【跟随者谬误】 如本文所讨论的。
数据收集和数据争论
网页抓取
我采取的第一步是使用 BeautifulSoup(一个 python 库)从下面的网站获取一些非洲有影响力的人和政府官员的 twitter 账号。这些网站使用关注者计数和其他指标对有影响力的人和政府官员进行排序。
影响者
#importing libraries
import requests
from bs4 import BeautifulSoup#getting the page
page_inf = requests.get("https://africafreak.com/100-most-influential-twitter-users-in-africa")soup = BeautifulSoup(page_inf.content, 'html.parser')
inf_list = soup.find_all('h2')
政府官员
#getting the page
page_gov = requests.get("[https://africafreak.com/100-most-influential-twitter-users-in-africa](https://www.atlanticcouncil.org/blogs/africasource/african-leaders-respond-to-coronavirus-on-twitter/#east-africa)")soup = BeautifulSoup(page_gov.content, 'html.parser')
gov_list = soup.find_all(class='wp-block-embed__wrapper')
BeautifulSoup 总是以列表形式返回对象,并且可能包含不需要的额外数据,所以我必须做一些清理工作来获取这些影响者和政府官员的 twitter 句柄,将其转换为 Pandas DataFrame,并将其保存为 csv 文件。
#import the necessary libraries
import re
import pandas as pd#cleaning the data
inf_list = re.findall(r"@[\w]*", inf_list)
gov_list = re.findall(r"@[\w]*", gov_list)#converting to dataframe and saving as csv file
inf_df = pd.DataFrame(inf_list, columns=['Handles'])
inf_df.to_csv('influencers.csv')gov_df = pd.DataFrame(gov_list, columns=['Handles'])
gov_df.to_csv('officials.csv')
Twitter API
下一步是通过使用开发人员的帐户凭证访问 Twitter API 来收集关于这些影响者和政府官员的信息。
import tweepy#Variables that contains the user credentials to access Twitter API
consumer_key = 'XXXXXXXXXX'
consumer_secret = 'XXXXXXXXX'
access_token = 'XXXXXXXXX'
access_token_secret = 'XXXXXXXXXXX'#This handles Twitter authentication and the connection to Twitter Streaming API
auth = OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
以下是收集的用户信息列表:
- 网名
- 追随者计数(追随者数量)
- 朋友计数(关注人数)
- 状态计数(推文数量)
- 最喜欢的计数(喜欢的数量)
- 转发计数(转发次数)
收集这些信息的方法如下所示
#getting the followers count, following, number of tweets, and screen name
followers_count = {}
following_count ={}
num_of_tweets = {}
screen_name = {}for count in range(len(inf_list)):
try:
user = api.get_user(inf_list[count])
followers_count[inf_list[count]]= user.followers_count
following_count[inf_list[count]] = user.friends_count
num_of_tweets[inf_list[count]] = user.statuses_count
screen_name[inf_list[count]] = user.screen_name
except Exception as e:
pass
详细代码可以在这个 GitHub 库中找到。下面是一个为非洲影响者获取的数据示例。
作者图片
作者图片
数据分析
在收集的数据中,为每个用户计算了以下指标:
- population _ score = Retweets+赞
- Reach_score = Followers —关注
#calculating the popularity score
new_inf_df['Popularity_score'] = new_inf_df['Retweet_count'] + new_inf_df['Favorite_count']#calculating the reach score
new_inf_df['Reach_score'] = new_inf_df['Follower_count'] - new_inf_df['Following_count']
结果—数据可视化
在计算了每个用户的指标之后,我利用 seaborn(一个 python 库)通过按照如图所示计算的每个指标对数据帧进行排序来可视化结果。
##data visualization - government officials#importing library for data visualization
import seaborn as sns#bar plot - reach score
plt.figure(figsize=(20,10)) #customizing the size of the plot
sns.set(style="darkgrid") #customizing the style of the plot#visualizing the data using bar plot
ax = sns.barplot(x='Screen_name', y='Reach_score', palette="Greens_d",
data=new_gov_df.sort_values(by='Reach_score', ascending=False)[0:10]
)#getting the values of the data
for p in ax.patches:
ax.annotate(format(p.get_height()), (p.get_x() + p.get_width() / 2.,
p.get_height()), ha = 'center', va = 'center',
xytext = (0, 10), textcoords = 'offset points')#setting the parameters for the title, x and y labels of the plot
ax.set_title("Reach Score for Top African Government Officials", size=40, weight='bold')
ax.set_xlabel("Twitter Screen Names", size=20, weight='bold')
ax.set_ylabel("Reach Score(Followers-Following)", size=20, weight='bold')#changing the rotation of the x axis tick labels
for item in ax.get_xticklabels():
item.set_rotation(45)
plt.savefig('reach.png') #saving the figure
plt.show()
详细代码可以在这个 GitHub 库中找到。
人气评分
作者图片
作者图片
到达分数
作者图片
作者图片
观察和见解
从上面显示的可视化分析结果可以看出,前 10 名非洲影响者和政府官员的受欢迎程度得分和影响力得分有所不同。
例 1:
世卫组织(政府官员)
- 得分= 8,114,444
- 人气评分= 275,723
新闻 24 (影响者)
- 得分= 3,579,984
- 人气评分= 39,718
查看上面的示例 1,我们可以看到到达分数和流行分数之间的巨*差距。这些用户有很多追随者;然而,与他们的追随者相比,参与他们推文的人数并不多。
例 2:
鲍里斯约翰逊(政府官员)
- 得分= 2,914,656
- 人气评分= 4,930,514
MbuyiseniNdozi (影响者)
- 得分=不在前 10 名
- 人气分= 248228
在示例 2 中,我们看到了这样一种情况,用户的可及性得分低于他们的受欢迎度得分。这里看到的影响者的影响力得分甚至不在前 10 名之列,但受欢迎程度得分在图表上排名第二。
结论
该项目旨在纠正拥有*量追随者就等同于成为有影响力的人的观念,从分析中获得的结果支持了这一目标。流行度分数显示了参与用户推文的人数,与到达分数相比,它被证明是更好的影响力衡量标准。
深造
除了通过人气得分和到达得分来衡量用户的影响力,还可以考虑相关性得分(用户提及+评论)和他们在推文中使用的标签。
关于这个项目的详细信息可以在这个 GitHub 资源库中找到。
感谢您的阅读。
参考
使用气流的 Twitter 调度程序
鸣谢:https://dribbble . com/shots/5370290-Website-for-bZx-Metaphor-Design
为什么你甚至需要一个 Twitter 调度程序?
所有的 Twitter 名人或你在 Twitter 上认识的任何企业,可能都在使用某种调度程序来准时发布他们的推文。
如果你有一堆各种主题的推文草稿,并且想自动发布它们而不用担心时间问题。这个日程表会让你的生活更轻松。
因此在本文中,您将使用 Apache Airflow 构建一个 Twitter 调度器。
最后,你会有一个运行中的管道,你的推文会在预定的时间段发布。此外,如果您想亲自体验运行在 Docker 上的 Apache Airflow,那么这可能是一个不错的起点。
先说这个 bot 话题。😄
气流到底是什么?
Apache Airflow 是一个工作流调度器,本质上是一个 python 框架,允许运行 Python 可以执行的任何类型的任务。例如发送电子邮件、运行 Spark 作业、Hadoop 任务以及在 Postgres 数据库上运行 SQL 查询等
设置计划程序的先决条件
在 docker 上运行 Airflow 是运行应用程序的最佳方式,它消除了各种环境设置错误。我假设你对 docker 的概念比较熟悉,并且你的机器上已经安装了 Docker。在互联网上找到的最好的气流 docker 图像之一是 puckel/docker-airflow ,你可以克隆这个 repo 并开始构建你的数据管道,但如果你想完成这个项目,那么我会建议你克隆这个 repoVJ GPT/Twitter-pipeline,在这里我对之前的 repo 的 docker 文件和 docker-compose 文件进行了更改,以符合这个项目。这个 repo 还包括 Twitter 调度 DAG,这是你对这个项目的所有要求。
如果你还没有克隆回购协议,👇
在这里,我们将建立一个 Twitter 调度数据管道,这个想法是收集数百条推文在一个文件中…
github.com](https://github.com/vjgpt/twitter-pipeline)
但是在你开始这条管道之前,你需要做两件事:
- 创建一个 Twitter 开发者 API 账户。(申请访问— Twitter 开发者| Twitter 开发者)
- 启用 Google Drive v3 备份你所有的数据。( Python 快速入门| Google Drive API | Google 开发者)
创建完 Twitter 开发者账户后,确保保存了所需的密钥和凭证,并将其放在**topic_tweet.py**
文件中
consumer_key = '' # Add your API key here
consumer_secret = '' # Add your API secret key here
access_token = '' # Add your Access Token key here
access_token_secret = '' # Add your Access Token secret key here
要设置 Google Drive API,您需要在本地机器上创建一个 python 环境,并遵循上面的链接。在你允许你的应用后,你会得到两个文件**credentials.json**
和**token.pickle**
。复制这两个文件,放入 repo *twitter-pipeline/dags/daglibs*
文件夹路径。
├── dags
│ ├── daglibs
│ │ ├── **credentials.json**
│ │ ├── etl_job.py
│ │ ├── **token.pickle**
│ │ ├── topic_tweet.py
│ │ └── upload.py
│ └── post_tweet.py
├── data
让我们开始码头集装箱🚢
我们将在本地执行器模式下运行 Airflow,这意味着合成文件不会构建您的图像,在继续之前,您可以自己在本地构建它。
cd twitter-pipelinedocker build -t aflatest .
下面是docker-compose-LocalExecutor.yml
的文件。
你可以在上面的 compose 文件中看到 volume 部分,它基本上在本地机器和 docker 容器之间创建了一个桥梁。它有助于在本地卷和 Docker 路径之间持久化数据。您将 dag 文件添加到本地机器,它将反映在您的 docker 容器中,并最终反映在 Airflow UI 中。
现在您已经准备好启动这些容器并运行 Apache Airflow,确保您位于twitter-pipeline
repo 的主路径中。
docker-compose -f docker-compose-LocalExecutor.yml up -d
docker-compose up -d 在后台启动你的容器(即分离模式)
您可以使用拆除合成设置
docker-compose -f docker-compose-LocalExecutor.yml down
您可以使用以下命令检查在后台模式下运行的服务的日志
docker-compose -f docker-compose-LocalExecutor.yml logs
点击 的网页界面 http://localhost:8080
您应该可以看到您放在./dags
目录中的任何 Dag,尽管有时它们可能需要一分钟才能显示出来。
一旦 DAG 出现,您对 python 文件所做的任何更改将在下次触发 DAG 时立即生效。
PS:打开 dag 激活它。
Twitter 调度程序管道
好了,现在你的气流在你的码头集装箱上流动,让我们了解一下这条管道是如何工作的。
在data/tweets
文件夹中有一些文件,用户把他/她所有起草的推文放在那里。我们把所有的推文都放到一个 CSV 文件中,这是所有推文文件的合并。参照这个 CSV 文件,我们发布推文并做出相应的更改。最后,所有的 CSV 文件和文本文件都备份在 google drive 中,以防发生致命的事情。
这整个过程可以在预定的 DAG 运行时发送你起草的推文。
图片来源:【https://dribbble.com/shots/3166602-Twitter
post_tweet.py
这是主 DAG 文件,显示在你的气流用户界面中,它包含你在这个 python 文件中定义的任务。
# DAG is scheduled to run every 8 hoursdag = DAG('PostTweet',schedule_interval=timedelta(hours=8), default_args=default_args)
该 DAG 计划每 8 小时运行一次,您可以根据需要更改运行时间。
etljob.py
这是*部分数据管道发生的地方。它把你从不同文件中起草的推文合并成一个文件。
该文件中的每个函数都被视为 DAG 中的一个任务。
topic _ tweet.py
在这里,我们连接到 Twitter API 并发布推文。这个文件需要在 Twitter 开发人员 API 控制台登录时生成的所有密钥和秘密。
上传. py
此文件将所有与此管道相关的数据备份到您的 Google Drive 帐户。
现在,让我们测试这条管道。
在data/tweets
文件夹中的 txt 文件中添加一些 tweet,你可以添加多个 tweet 到那个文件中,或者创建一个新文件并在其中添加 tweet。然后触发管道,你会发现你的推文发布在你的 Twitter 账户上,所有文件都备份在 Google Drive 上。
干杯!!!🥂
结束了!!
这只是您刚刚制作的一个演示管道,但是您可以使用同一组基础设施,通过几个用例来制作更多这样的管道。
如果你有兴趣更深入地研究这个话题,那么试着创建一个比我们现在做的更好的可扩展架构。在这个架构中,我们使用了 LocalExecutor,并且有一个不可伸缩的 singletons 调度程序。尝试使用 CeleryExecutor 模式,在这种模式下,工作从任务队列中分发出去,所有任务都是分布式的。
哦,等等..如果这对你有所帮助,请支持我的工作。
这是这个项目的回购👇
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/vjgpt/twitter-pipeline)
初学者使用 ggplot2 的 Twitter 数据可视化介绍
动手教程
如何使用 R (ggplot2)交互式地可视化您的 Twitter 数据
概观
F 对于这个项目,我们使用的是 2019 年 5 月 28 日—29 日抓取的 Twitter 的原始数据。此外,数据为 CSV 格式(逗号分隔),可在此处 下载 。它涉及两个主题,一个是佐科·维多多的数据,包含关键字【佐科·维多多】,另一个是 Prabowo Subianto 的数据,包含关键字【Prabowo subi anto】。这些包括几个变量和信息,以便确定用户情绪。实际上,数据有 16 个变量或属性和 1000 多个观察值(两种数据都有)。表 1 列出了一些变量。
导入模块
数据的变量(图片由作者提供)
数据可视化
数据探索旨在从 Twitter 数据中获取任何信息和洞察力。应该指出的是,数据进行了文本预处理。对被认为很有兴趣讨论的变量进行探索。例如,变量**created**
。
为 tweet 活动创建条形图(佐科·维多多和普拉博沃·苏比安托)
图 1 佐科·维多多(左)和普拉博沃·苏比安托(右)的推文总数直方图(图片由作者提供)
根据图 1,我们可以得出结论,通过数据抓取(对于关键字“佐科·维多多”和“普拉博沃·苏比安托”)的推文数量并不相似,即使是在同一天。例如图 1(左),直观地表示仅在 WIB 2019 年 5 月 28 日 03:00-17:00 期间获得了带有关键词“佐科·维多多】的推文。而在图 1(右)上,我们得出结论,在 2019 年 5 月 28 日至 29 日 WIB 12:00-23:59(2019 年 5 月 28 日)和 WIB 00:00-15:00(2019 年 5 月 29 日)期间获得的带有关键字 "Prabowo Subianto" 的推文。
为 tweet 活动创建条形图(Prabowo Subianto)
图 Prabowo Subianto(左)2019 年 5 月 28 日和(右)2019 年 5 月 29 日的总推文直方图(图片由作者提供)
基于图 2,我们得到了使用关键字“佐科·维多多”和“Prabowo subi anto”发推文的用户之间的显著差异。带有关键词【佐科·维多多】的推文往往会在某个时间(07:00-09:00 WIB)激烈地谈论佐科·维多多,其中 08:00 WIB 的推文数量最多。它有 348 条推文。而带有关键字“Prabowo Subianto”的推文倾向于在 2019 年 5 月 28 日至 29 日期间不断谈论 Prabowo subi anto。2019 年 5 月 28 日至 29 日上传的带有关键词“Prabowo subi anto”的平均每小时推文数为 36 条。
创建情绪得分的密度图(佐科·维多多和普拉博沃·苏比安托)
图 3 情绪得分密度图(左)佐科·维多多和(右)普拉博沃·苏比安托(图片由作者提供)
图 3 是 2019 年 5 月 28 日—29 日,以【佐科·维多多】**【Prabowo subi anto】为关键词的多条推文的柱状图。根据图 3(左),可以得出结论,正在谈论 Prabowo Subianto 的用户每小时上传的平均推文数少于每小时 30 条推文的两天推文总数。Twitter 用户在 WIB 时间 19:00–23:59 不太热衷于谈论 Prabowo Subianto。是印尼的休息时间造成的。然而,由于居住在国外的用户或仍然活跃的用户,带有其主题的推文总是在午夜更新。然后,用户在 WIB 时间 04:00 开始活动,在 WIB 时间 07:00 达到高峰,然后下降,直到 WIB 时间 12:00 再次上升。
创建一个情绪得分条形图(佐科·维多多和普拉博沃 Subianto)
图 4 情绪得分柱状图(左)佐科·维多多和(右)普拉博沃·苏比安托(图片由作者提供)
图 4 是包含关键词【佐科·维多多】**【Prabowo subi anto】的情感得分的密度图。推文的得分是由组成推文的词根的平均得分获得的。因此,它的分数是为每个词根给出的,值在-10 到 10 之间。如果分数越小,那么对推文中的词的负面情绪就越多,反之亦然。根据图 4(左),可以得出结论,包含关键词“佐科·维多多”的推文的负面情绪范围为-10 到-1,中间值为-4。这也适用于积极的情绪(当然是积极的分数)。根据图 4(左)中的密度图,可以发现积极情绪的得分具有相当小的方差。因此,我们得出结论,对包含关键词“佐科·维多多”的推文的积极情绪并不太多样化。
图 4(右)显示了包含关键字“Prabowo subi anto”的情感得分的密度图。它与图 4(左)不同,因为图 4(右)中的负面情绪范围从-8 到-1。暗示推文没有太多负面情绪(推文有负面情绪,但不够高)。此外,负面情绪得分的分布在 4 和 1 的范围内有两个峰值。然而,积极情绪的范围是从 1 到 10。与图 4(左)相反,图 4(右)中的积极情绪具有很高的方差,并且在 3 和 10 的范围内具有两个峰值。这表明包含关键字“Prabowo subi anto”的推文具有高正面情绪。
为情感类创建一个饼图(Jowo Widodo 和 Prabowo Subianto)
图 5 情感类饼图(左)佐科·维多多和(右)普拉博沃·苏比安托(图片由作者提供)
图 5 是已被分类为负面、中性和正面情绪的推文的情绪得分汇总。负面情绪是得分小于零的情绪。中性是得分等于零的情绪。反之,积极情绪的得分*于零。根据图 5,可以得出带有关键字【佐科·维多多】的推文的负面情绪百分比低于带有关键字【Prabowo subi anto】的推文。它有 6.3%的百分比差异。还发现,与具有关键字“Prabowo subi anto”的推文相比,包含关键字“佐科·维多多”的推文具有更*百分比的中性情绪和积极情绪。通过对饼状图的研究发现,与关键字为“Prabowo subi anto”的推文相比,关键字为“佐科·维多多”的推文往往有更*比例的正面情绪。但通过密度图发现,正面和负面情绪得分的分布显示,与“佐科·维多多”相比,包含关键词“Prabowo subi anto】的推文往往具有更*的情绪得分。它必须进行进一步的分析。
图 6 推文中的 Wordcloud(左)佐科·维多多和(右)普拉博沃·苏比安托(图片由作者提供)
图 6 显示了用户在 2019 年 5 月 28 日-29 日经常上传的推文中的术语或单词(关键词“佐科·维多多”和“普拉博沃·苏比安托”)。通过这种 WordCloud 可视化,可以发现针对关键词进行讨论的热门话题。对于包含关键词【佐科·维多多】的推文,发现词条【tuang】【petisi】【negara】【aman】**【NUS antara】是每条推文中出现次数最多的前五个词条。然而,包含关键字“佐科·维多多”的推文发现,术语“普拉博沃”、“Subianto”、“kriminalisasi”、“selamat”、“T13”和“迪拜”是每条推文中出现次数最多的前五个术语。这间接显示了使用关键字“Prabowo subi anto”上传的推文的模式:几乎可以肯定,每条上传的推文都直接包含了名字“Prabowo subi anto”,而不是通过提及(@)。这是因为,在文本预处理中,提示音(@)已被删除。
请前往我的 GitHub repo 查看完成的代码。
参考
[1] K .博劳,c .,j .冯,r .沈 【微博语言学*:利用推特培养交际和文化能力 (2009),网络学*的进步——ICWL 2009,第八届国际会议,德国亚琛,2009 年 8 月 19 日至 21 日。
Twitter JSON 数据处理
使用 python 库清理和润色用于社交媒体分析的推文数据帧。
witter 允许使用用于访问 Twitter API 的 Python 库 tweepy 来收集 tweepy。在这里,我不打算给出如何收集 tweet 的教程,因为已经有一些好的评论了(看看下面的参考资料),而是给你一个完整的例子,说明如何处理 tweet 对象,以便建立一个干净的数据框架,我们可以在这个框架上进行社交媒体分析。
TL;TR: 在这个过程中,我们将展平 Twitter JSON,在几个选项(主推、转发、引用等)中选择文本对象。),清理它们(删除非字母字符),翻译非英语推文,计算文本的情感,以及关联给定用户定义的位置或自动地理定位的位置。
要使用的库: 熊猫国家转换器GeoPyspaCyGoogle transNLTK。
每个 tweet 对象 以 JSON 格式出现,混合了“根级”属性和子对象(用 *{}*
符号表示)。Twitter 开发者页面给出了以下例子:
{
"created_at": "Wed Oct 10 20:19:24 +0000 2018",
"id": 1050118621198921728,
"id_str": "1050118621198921728",
"text": "To make room for more expression, we will now count all emojis as equal—including those with gender and skin t… https://t.co/MkGjXf9aXm",
"user": {},
"entities": {}
}
当然,这只是组成每条推文的庞*字典中的一小部分。另一个流行的例子是这个 Twitter 状态对象图。
对于*多数类型的分析,我们肯定需要属性,如 tweet 文本、用户屏幕名称或 tweet 位置。不幸的是,正如您所看到的,这些属性没有一个清晰的格式,相反,它们分布在 JSON 的各个层次上——例如,tweet 位置坐标位于
tweet_object['place']['bounding_box']['coordinates']
正是由于这一事实,收集的推文需要一个*的清理和转换过程,这就是这篇文章的目的。
推特数据
我最近进行了一个语言本地化项目,我需要在 Twitter 上做一个社交媒体分析。为此,我在几天的时间里收集了 52830 条包含以下关键词的推文: '#FIFA20' ,' #FIFA21' , 'FIFA20' , 'FIFA21' , 'FIFA 20' , 'FIFA 21' 和' # easporter 然后,为了对它们进行正确的分析,我必须事先清理每个 tweet 对象,这样我才能得出有意义的结论。
由于该项目的性质,我主要感兴趣的是关于推文位置的数据(国家和坐标),英文版本文本的情感,以及推文使用的语言。加工步骤的目标是完善和发现这些属性。您可以在以下存储库中找到该项目的详细信息:
这是一个端到端的项目,我们的目标是执行国际足联视频游戏的语言本地化,只有公众…
github.com](https://github.com/hectoramirez/Language-localization_FIFA)
让我们用这个数据集来举例说明 tweets 处理的步骤!
处理 JSON
正如我们看到的,在包含文本数据的 Twitter JSON 中有多个字段。在典型的 tweet 中,有 tweet 文本、用户描述和用户位置。在超过 140 个字符的 tweet 中,还有扩展 tweet 子 JSON。在引用的推文中,有原始推文和引用推文的评论。
为了*规模分析 tweet,我们需要将 tweet JSON 扁平化为一个层次。这将允许我们以数据帧格式存储推文。为此,我们将定义函数flatten_tweets()
,该函数将接受几个关于文本和位置的字段(该字段存储在place
中)。看一看:
现在,您可能想要研究所有的文本字段(主字段、转发字段或引用字段),但是,为了简单起见,这里我只保留一个文本字段。为此,我们现在定义一个函数select_text(tweets)
来选择主要文本,无论该推文是主要推文还是转发推文,我们决定删除引用的文本,因为它通常是重复的,可能没有信息。
我们现在构建数据框。请注意,我们选择了与社交媒体分析相关的主要列(字段)。这包括 tweet 语言、lang
和由用户手动设置的user-location
。我们还保留了place
中的country
、country_code
和coordinates
字段。当推文被地理标记时,这些字段就会出现,并且通常包含在不到 10%的推文中。以下代码块构建了数据帧:
**import** **pandas** **as** **pd**
*# flatten tweets*
tweets = flatten_tweets(tweets_data)
*# select text*
tweets = select_text(tweets)
columns = ['text', 'lang', 'user-location', 'place-country',
'place-country_code', 'location-coordinates',
'user-screen_name']
*# Create a DataFrame from `tweets`*
df_tweets = pd.DataFrame(tweets, columns=columns)*# replaces NaNs by Nones*
df_tweets.where(pd.notnull(df_tweets), **None**, inplace=**True**)
数据帧的头部看起来像这样:
df_tweets.head()
df_tweets.info()
请注意,几乎只有一半的推文包含手动设置的用户位置字段,甚至 1%的推文都没有地理标记,即,它们没有提供位置字段。这凸显了收集尽可能多的推文的重要性!
在下文中,我们感兴趣的是清理和抛光每个 dataframe 列。
语言
在流程的这一部分,我们将用语言标准名称替换lang
中的语言代码。如文档中所述:
如果存在,[
lang
]表示与机器检测到的 Tweet 文本语言相对应的 BCP 47 语言标识符,如果没有检测到语言,则为*und*
。
我们使用这个库中的辅助languages.json
文件来执行这个步骤。该文件将语言代码映射到语言标准名称。下面的代码将完成这个任务:
**with** open('languages.json', 'r', encoding='utf-8') **as** json_file:
languages_dict = json.load(json_file)names = []
**for** idx, row **in** df_tweets.iterrows():
lang = row['lang']
**if** lang == 'und':
names.append(**None**)
**elif** lang == 'in':
name = languages_dict['id']['name']
names.append(name)
**elif** lang == 'iw':
name = languages_dict['he']['name']
names.append(name)
**else**:
name = languages_dict[lang]['name']
names.append(name)
df_tweets['language'] = names
df_tweets.drop(['lang'], axis=1, inplace=**True**)
位置
现在我们开始处理位置。我们将首先处理place
字段,然后处理user-location
字段。
地方
很明显,place
对象中的数据比user-location
更可靠。因此,虽然它构成了我们推文的 0.91%,但我们会照顾它。首先,place-country_code
中的国家代码以 ISO 2 形式出现,为此我们将使用国家转换器将其转换为 ISO 3 形式。然后,我们将执行同样的操作,将place-country
名称改为标准的简称。这是有利的,因为,例如, Plotly 地图使用 ISO 3 代码来定位国家。
用户位置
在这里,我们将手动设置的user-locations
翻译成国家名称和代码——这涉及到对用户的信任。我们使用 GeoPy 库来识别一个位置(可能是一个地址)并为其分配一个国家。同样,我们使用country_converter
来查找 ISO 3 表格中的国家代码。
提醒一句 : GeoPy 连接到一个 API,不幸的是,每次调用几乎要花一秒钟。这使得计算~ 50 K tweets 的过程相当慢。
注意: tqdm 是一个 python 库,对 pandas 有很好的实现,在代码运行时输出进度条。这会让你的生活更轻松!
最后,我们将place-country
和user-country
列减少为一列,当前者存在时保留前者,否则保留后者。我们对代码列进行同样的操作:
countries, codes = [], []
**for** idx, row **in** df_tweets.iterrows():
**if** row['place-country_code'] **is** **None**:
country = row['user-country']
code = row['user-country_code']
countries.append(country)
codes.append(code)
**else** :
countries.append(row['place-country'])
codes.append(row['place-country_code'])
df_tweets['location'] = countries
df_tweets['location_code'] = codes
*# drop old columns*
df_tweets.drop(columns=['place-country', 'place-country_code',
'user-country', 'user-country_code'], inplace=**True**)
此时,我们的数据集如下所示:
df_tweets.head()
文本清理
现在是处理推文文本的时候了。这将涉及删除非字母字符和翻译非英语推文。然而,我们将保留这两个选项,并实际使用带有表情符号和其他字符的文本进行分析,因为我们的情感分析器可以很好地处理它们。
要删除非字母字符,我们使用 spaCy ,因为它非常简单,我们不需要指定正则表达式。请记住,下面的代码块删除了带有撇号的表情符号和单词,如“我是”、“你们都是”、“不要”等。
**import** **spacy**
nlp = spacy.load('en_core_web_sm')
**def** cleaner(string):
*# Generate list of tokens*
doc = nlp(string)
lemmas = [token.lemma_ **for** token **in** doc] *# Remove tokens that are not alphabetic*
a_lemmas = [lemma **for** lemma **in** lemmas **if** lemma.isalpha()
**or** lemma == '-PRON-'] *# Print string after text cleaning*
**return** ' '.join(a_lemmas)
df_tweets['text_cleaned'] = \
df_tweets['text'].progress_apply(cleaner)
翻译
为了翻译非英语推文,我们使用 googletrans ,作为 GeoPy,它连接到它的 API,然而它要快得多。
另一个警告:存在一个讨论过的记录不良的错误,例如,这里:https://stack overflow . com/questions/49497391/Google trans-API-error-expecting-value-line-1-column-1-char-0,它会断开您的连接并阻止您的 IP。为了避免这个错误,我使用np.array_split()
将数据帧分成几个块,在一个循环中一次处理一个块。通过这样做,错误不会发生,但是我仍然将每个块的翻译保存到一个csv
中,这样如果在任何迭代中出错,我可以只重新计算一个块。我每次都会实例化Translator()
。
最后,我们将原始的、未经处理的英文文本添加到text_english
:
*# replaces NaNs by Nones*
df_english.where(pd.notnull(df_english), **None**, inplace=**True**)
*# add original English tweets to text_english by replacing Nones*
texts = []
**for** idx, row **in** df_english.iterrows():
**if** row['text_english'] **is** **None**:
text = row['text']
texts.append(text)
**else** :
texts.append(row['text_english'])
df_english['text_english'] = texts
此时,数据帧看起来像这样:
df_english.head()
情感分析
我们最终计算每条推文的情感。为此,我们使用nltk.sentiment.vader
库中 NLTK 的SentimentIntensityAnalyzer
对象。
VADER (Valence Aware 字典和情感推理器)是一个基于词典和规则的情感分析工具,专门针对社交媒体中表达的情感。T22【参。】
这个库使用起来非常简单,如你所见:
请注意,polarity_score
输出文本为负、中性或正的概率以及一个复合分数。然后,我们提取后者并将分数附加到数据帧中。
结束
为了便于展示,我们对各列进行了重新排序。
*cols_order = ['text', 'language', 'location', 'location_code',
'location-coordinates', 'sentiment', 'text_english',
'text_cleaned', 'user-screen_name']df_final = df_sentiment[cols_order]*
最终数据集应该如下所示:
df_final.head()
附加:一个简单的分析
为了举例说明可以用这个数据集做什么,让我们按国家建立一个平均推文情绪得分的可视化:
请注意,我们使用了一个国家/语言数据框架,它可以在这个库中找到。上面的代码输出以下 Plotly 地图:
这张世界地图看起来不太乐观😕
这篇文章中使用的全部代码可以在我的知识库中找到:
* [## hectoramirez/语言-本地化 _FIFA
EA Sports 的 FIFA 本地化端到端研究。通过以下方式为 hectoramirez/Language-localization _ FIFA 的发展做出贡献…
github.com](https://github.com/hectoramirez/Language-localization_FIFA/blob/master/Tweets processing and sentiment.py)
关于作者
我最近获得了物理学博士学位,目前正在进入数据科学领域。非常感谢对这篇文章的任何评论和/或建议。另外,看看我的其他故事:
加载数据,用散景制作出色的可视化效果,将它们放在 GitHub Pages 网站上,让气流自动流动…
towardsdatascience.com](/your-live-covid-19-tracker-with-airflow-and-github-pages-658c3e048304)
最后,请随时在 LinkedIn 与我联系:
[## héctor ramírez-西班牙巴伦西亚地区|职业简介| LinkedIn
我最近获得了物理学博士学位,专攻实验数据分析和数学建模。我领导了…
www.linkedin.com](https://www.linkedin.com/in/harr/)
参考
Datacamp 用 Python 分析社交媒体数据:
登录 DataCamp 帐户
learn.datacamp.com](https://learn.datacamp.com/courses/analyzing-social-media-data-in-python) [## 我的第一个 Twitter 应用
如何使用 Python 和 Tweepy 创建自己的数据集
towardsdatascience.com](/my-first-twitter-app-1115a327349e) [## 适合初学者的 Tweepy
使用 Twitter 的 API 建立你自己的数据集
towardsdatascience.com](/tweepy-for-beginners-24baf21f2c25) [## 如何使用 Tweepy 访问 Twitter 的 API
使用易于使用的 Python 库获得*型 Twitter 数据集的分步指南(包含代码和技巧)
towardsdatascience.com](/how-to-access-twitters-api-using-tweepy-5a13a206683b) [## 用 Tweepy Python 抓取推文
这是一个使用 Python 库 Tweepy 抓取 Twitter tweets 的逐步指南。
medium.com](https://medium.com/@leowgriffin/scraping-tweets-with-tweepy-python-59413046e788)*
Twitter 政治罗盘机器:自然语言处理方法与分析
如何使用 Twitter 和机器学*模型评估政治家的党派关系并监控政治分歧
随着 2020 年*选的进行,了解一个政治家的从属关系比以往任何时候都更重要。今天,我将教你如何建立一个机器学*模型,根据他们的推文预测政党归属。
数据争论
为了收集数据/推文,我们将使用 Twitter API。所有参议员的 Twitter 句柄都在这里:(https://www . sbh4all . org/WP-content/uploads/2019/04/116 th-Congress-Twitter-handles . pdf)
我还生成了一个两党领袖的列表,我们将使用它来训练我们的模型。
民主党:乔·拜登(总统候选人),卡玛拉·哈里斯(副总统候选人),伯尼·桑德斯,伊丽莎白·沃伦
共和党人:唐纳德·川普(总统),迈克·彭斯(副总统),米奇·麦康奈尔,特德·克鲁兹
数据/特色工程
为了收集有用的特征,我们必须将推文转换成某种向量。在下图中,我将展示我们将在该模型中使用的 3 个主要特性以及如何获得它们。
作者图片
现在我将详细解释为什么我选择这些特性,为什么我认为它们是必不可少的。
可读性分数
可读性分数表明一个人的写作水平,并将他或她的写作转化为评估一个人教育水平的数字。可读性是至关重要的,因为*量的报告显示,研究生或*学毕业生更有可能保持自由的观点。因此,政治家可能会迎合他们的基础,我们希望捕捉到这一点。
下面是我们用来增加可读性分数的代码;我们使用 textstat 包,把句子变成数字。(请参考他们的网站了解这种转换的细节/科学)。在这里,我们必须删除超链接和转发,因为超链接是一个名词,而不是一个名词,算法不会识别,转发,虽然可能分享他们的情绪,但不是他们的词。
import textstatdate2_df[‘retweet’] = date2_df[‘tweet’].str.contains(‘RT’)
date2_df[‘tweet_web_r’] = date2_df.tweet.str.replace(r’http\S+’,’’)
date2_df[‘TS’] = date2_df.tweet_web_r.apply(lambda x:textstat.text_standard(x,float_output =True))
date2_df = date2_df[date2_df[‘retweet’] == False]
date2_df = date2_df.reset_index(drop=True)
情感得分
我相信我们都听说过“让美国再次伟*!”。你*概可以猜到这句话有积极的情绪。情感是我们在写作中的感受。找到对一个特定主题的情感组合,将会给我们一个政治家可能保持的党派关系的概念。
例如:
‘让美国再次伟*’具体来说,可能是共和党人或川普会说的话(对美国的积极情绪)。
“昨晚,乔·拜登说,没有一个拥有私人保险的人在奥巴马医改下失去了他们的计划。”*概是共和党人会说的话。(对奥巴马医改的负面情绪)。
昨晚川普说我们正在疫情“度过难关”。“真的吗,”可能是民主党人会说的话(对特朗普的负面情绪)。
假设我们来看看政治领袖的推文百分比是如何分类的。(如下图)非常有趣的是,在这段时间里,*多数共和党领导人的推文都是正面的,而民主党人的推文*多是负面的。如果非要我猜的话,民主党人可能是在批评共和党人,或者在反思疫情带来的危险。这种区别应该有助于我们的模型。
作者图片
下面是我们用来给每条推文添加情感复合得分的代码。我们使用 VADER(化合价感知词典和情感推理机)包来做这件事。关于他们算法的细节,请参考他们的网站。此外,在得到分数之前,我们删除了超链接和转发。
from nltk.sentiment.vader import SentimentIntensityAnalyzerdate2_df[‘sentiment_score’] = date2_df[‘tweet_web_r’].apply(analyser.polarity_scores)
compound_score = []
sentiment_score = list(date2_df[‘sentiment_score’])for i in range(len(date2_df[‘sentiment_score’])):
compound_score.append(sentiment_score[i][‘compound’])
date2_df[‘sentiment_score’] = compound_score
TF-IDF(术语频率逆文档频率)向量
最后,也是最关键的,功能是将一条推文的文字转化为 TF-IDF 向量。为了做到这一点,我们将共和党和民主党领导人的所有推文结合起来,并将其转化为一个语料库/文档。TF-IDF 的中心概念是我们将一个语料库转换成一个单词包向量(一个单词出现了多少次!)考虑到语料库的长度。如果你想了解更多细节,请参考 TF-IDF 维基百科页面。在这种情况下,当我们对政党领导人的话语使用频率进行矢量化,并查看其他参议员是否使用领导人正在使用的相同词语时,就可以想到这一点。
为了生成这些 TF-IDF 向量,我们必须删除超链接和转发,将所有单词转换为小写,标记化,删除停用词,并对之前的单词进行旅顺。一旦句子被清理干净,我们就可以将语料库放入 Sklearn 包中的 TF-IDF 矢量器。下面是清理操作的目的,下面是我的代码!
- 降低*小写—防止因*小写不同而重复计算单词
- 把单词的变体变成一个单词。例如:“cover”“covering”“covered”都将被视为同一个词
- 去掉无用的词——去掉无意义的词,比如命题
rep_Tfidf = TfidfVectorizer(stop_words=stop_words, ngram_range=(1,3))
rep_Tfidf.fit_transform(df[df[‘politican’].isin(rep_leaders)].len_sentence)dem_Tfidf = TfidfVectorizer(stop_words=stop_words, ngram_range=(1,3))
dem_Tfidf.fit_transform(df[df[‘politican’].isin(dem_leaders)].len_sentence )
# Put the result into a dataframe
rep_vectorize = pd.DataFrame(rep_Tfidf.transform(date2_df[‘len_sentence’]).toarray(), columns= rep_Tfidf.get_feature_names())#.sum(axis = 1)
dem_vectorize = pd.DataFrame(dem_Tfidf.transform(date2_df[‘len_sentence’]).toarray(), columns= dem_Tfidf.get_feature_names())rep_vectorize[‘politican’] = date2_df.politican
dem_vectorize[‘politican’] = date2_df.politicanrep_vectorize[‘Party’] = date2_df.Party
dem_vectorize[‘Party’] = date2_df.Party# Add in the tf-idf vectors to the sentiment score and readability score
date2_df_final = date2_df[[‘politican’,’Party’,’TS’,’sentiment_score’]]rep_vectorize = rep_vectorize.drop([‘politican’,’Party’],axis =1)
dem_vectorize = dem_vectorize.drop([‘politican’,’Party’], axis = 1)
date2_df_final = pd.concat([date2_df_final,rep_vectorize,dem_vectorize ], axis = 1 )
为了证明 TF-IDF 的重要性,以下是领导们最常用的词语。正如你在这里看到的,唐纳德·特朗普*量使用 MAGA 和 great。难怪他的很多推文都有积极的情绪。相反的趋势和用词反映了民主党人的负面情绪。他们使用“covid”、“疫情”和“火灾”(这些都是自然灾害)的次数明显多于共和党人。也许这就是为什么他们的推文中有如此多的负面情绪。
作者图片
建模
在这一点上,你可能已经注意到,我们将训练我们的模型只使用党的领导人。这背后的原因是
- 我们想用这个模型来衡量党内的不和谐。
- 如果我们使用所有的参议员,我们的模型将不堪重负,因为推特的数量太多了。
我们选择的模型是随机森林分类器。下面是我们进行训练测试分割并使用交叉验证来生成模型的代码。
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV
from sklearn.metrics import plot_confusion_matrix
import seaborn as sns
# Change Party code to 0 and 1
party_code = {‘R’:1, ‘D’:0}
date2_df[‘Party’] = date2_df[‘Party’].replace(party_code)date2_df_leaders = date2_df[date2_df[‘politican’].isin(leaders) == True]
date2_df_nonleaders = df[df[‘politican’].isin(leaders) == False]# Split data into train and test set
X = date2_df_leaders.drop([‘politican’,’Party’], axis = 1)
y = date2_df_leaders.PartyX_train, X_test, y_train, y_test = train_test_split(X,y , train_size = 0.75, test_size = 0.25)# Scale the data
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)param_grid = {‘n_estimators’:stats.randint(100,400), ‘criterion’:[‘entropy’]
, ‘min_samples_leaf’:stats.randint(1,6), ‘max_depth’: stats.randint(100,400)}
# Make the model with 5 fold cross validation
RF_model = RandomForestClassifier()
RF_model_cv = RandomizedSearchCV(RF_model, param_grid, cv = 5, n_iter = 60, n_jobs = 4, scoring = ‘accuracy’)
RF_model_cv.fit(X_train, y_train)
y_pred = RF_model_cv.predict(X_test)
print(plot_confusion_matrix(RF_model_cv,y_test, y_pred))
作者图片
根据我们的混淆矩阵,你可以看到我们的模型在根据他们的推文猜测领导人的政党归属方面的准确率高达 90%,这非常好。
我们现在将使用这个模型来预测我们的参议员发出的所有推文,并单独汇总他们的得分。一旦我们收集了分数,我们将在散点图的 xy 轴上绘制类似共和党的推文数量和类似民主党的推文数量
作者图片
在这里你可以看到两党之间的明显分歧。你现在可以使用同样的代码,删除你选择的一个政治家,看看他/她在这个图上的位置。这将会给你一个政治家的从属关系的定量评估,并允许你监控任何党内的不和谐。恭喜你,你刚刚建立了你自己的推特政治指南针机器。
关于我如何构建机器、我所做的数据探索以及这个模型如何处理另一个日期的数据的更多细节。请参考我的 GitHub ,直接在 minglwu427@gmail.com 给我发邮件,或者在 LinkedIn 联系我。
Twitter 情感分析:流处理的故事
如果我们可以通过人们在 Twitter 上留下的面包屑来了解他们的情绪,会怎么样?
在这篇文章中,我们将着手构建一个微服务架构,包括建立一个流处理管道,从 Twitter 的公共 API 获取推文,将其排队到一个 Kafka 主题中,并用自然语言处理对其进行消化,以获得每条推文的极性。最终的结果将在一个仪表板上可视化,上面有我能想到的所有花哨的图表;-)
快速访问链接
- 仪表板:grafana.redouaneachouri.com。
- 完整的源代码可以在 GitHub 上找到:推特-情绪-分析。
- 预处理、特征提取和模型化在本笔记本中有详细说明。
技术总结
- 概念:自然语言处理,流处理,数据可视化。
- 技术: Python v3.8,Kafka,InfluxDB,Grafana。
- 部署平台:谷歌云平台。
目录
- 介绍
- 创建一个 Twitter 开发者帐户来访问 API
- 微服务架构:
1。Twitter API
2。制作人
3。动物园管理员&卡夫卡
4。消费者。时间序列数据库。Grafana 仪表板 - 深入研究预处理和特征提取
1。记号化
2。正常化
3。去噪或降噪
4。确定数据集中的单词密度 - 数据建模
- 结论
- 参考
介绍
witter 是一个人们分享他们对现实趋势的想法和情感的地方。这也是各种营销活动发生的战略场所,包括公司和公众人物在内的品牌可以通过分析人们在推特上发布的关于他们的信息,获得对受众看法的深刻见解。
通过情感分析,我们可以检测出一段文字传达的是积极的 T2 信息还是消极的 T4 信息。它被称为极性,当涉及到在短时间内从*量数据中收集消费者反馈时,这是一个游戏规则的改变者。
分析情绪有助于获得反馈,发现企业各方面的优势和弱点— 来源
要了解更多关于情感分析及其应用的信息,请查看这篇关于 MonkeyLearn 的优秀文章。
没有进一步的到期,让我们深入到技术细节!
GitHub 上有完整源代码:Twitter-情操-分析 。
创建一个 Twitter 开发者帐户来访问 API
为了开始收集所有这些推文,我们需要一个 Twitter 开发者账户。在这里,我将简要描述从创建帐户到为我们的第一个应用程序获得 OAuth2 载体令牌所采取的步骤。
- 前往 Twitter 开发者页面和申请开发者账户。标准 API 已经足够好了。请随意使用您的实际 Twitter 帐户(如果有的话),或者创建一个新帐户。
- 我们的下一步是进入 Twitter 实验室——所有有趣的事情都发生在这里。为此,我们需要注册一个开发环境并创建一个应用程序。我们的应用程序将有一组名为 API key 和 API secret 的凭证,我们必须严格保密,因为它们用于授权访问与我们的开发人员帐户相关的应用程序。
- 一旦获准进入开发人员实验室,我们就可以开始使用采样流端点
https://api.twitter.com/labs/1/tweets/stream/sample
:
采样流端点通过流连接实时提供所有 Tweet 数据的*约 1%的随机样本。
4.官方 API 样本流文档提供了一个坚实的基础来学*如何认证,并提供样本代码片段来开始获取实时推文流。
微服务架构
这个项目的架构,从左到右。
这个项目的架构非常简单,从 Twitter API 到 Grafana 仪表板,经过 Kafka 管道和生产者/消费者对。
所有架构都有其弱点,包括这一个,这包括网络故障、内存问题、错误、文本格式错误……我试图通过采取以下措施使主要组件(如生产者和消费者)尽可能地适应故障来解决这些问题:
- 启动时,在几秒钟的间隔内持续轮询 Kafka 代理的连接,直到代理可用。
- 在 infite
while True
循环中运行主程序,该循环捕捉异常,执行日志记录或尝试重新连接,并优雅地继续程序。
注意:实时部署在 GCP计算引擎—谷歌云平台中的虚拟机上完成。
以下是每个组件的描述:
Twitter API
- 描述:这是 Twitter 免费提供的一个 API 端点,目的是获取全球约 1%的实时推文。访问细节请参考前面的部分“创建 Twitter 开发者账号访问 API”。
- 端点:
https://api.twitter.com/labs/1/tweets/stream/sample
- 先决条件:一个 API 密钥和 API 秘密,将用于从 Twitter OAuth2 认证端点
[https://api.twitter.com/oauth2/token](https://api.twitter.com/oauth2/token.)
获取访问令牌。 - 示例:这是一条基本推文及其元数据的样子。对我们来说最重要的部分是
id
、lang
(语言)、和text
:
{
"data": {"attachments": {"media_keys": ["xxx"]},
"author_id": "xxx",
"context_annotations": [{"domain": {"description": "xxx",
"id": "123",
"name": "xxx"},
"entity": {"id": "xxx",
"name": "xxx"}}],
"created_at": "1970-01-01T00:00:00.000Z",
"entities": {"mentions": [{"end": 42,
"start": 123,
"username": "xxx"}],
"urls": [{"end": 42,
"start": 123,
"url": "https://t.co/xxx"}]},
"format": "detailed",
"id": "123456789",
"lang": "en",
"possibly_sensitive": False,
"referenced_tweets": [{"id": "123",
"type": "retweeted"}],
"source": "<a href='http://twitter.com/download/android' rel='nofollow'>Twitter for Android</a>",
"stats": {"like_count": 0,
"quote_count": 0,
"reply_count": 0,
"retweet_count": 0},
"text": "here comes the tweet !!!"}
}
生产者
- 描述:生产者或发布者从 Twitter API 获取推文,并将推文的
id
和text
(只有英语,没有元数据)发送到 Kafka 主题,然后由消费者消费。此外,我们将每条推文的id
和语言lang
存储在一个时间序列数据库中,用于可视化目的(见下面的 Grafana 仪表板)。 - 语言:Python(3.8 版本)。
- 实现细节:我们创建一个继承自 Kafka-python 类
KafkaProducer
的类TweetsProducer
,以方便与我们的 Kafka broker 的连接和交互。这个类将与 Twitter API 建立一个永久的连接,以获取连续的 tweets 流。
动物园管理员&卡夫卡
- 描述:使用 Kafka 的目的是拥有一个队列,在等待消费者处理消息(tweets)时,消息可以安全地存储在这个队列中,因为处理部分可能比从 Twitter 的 API 获取相对较慢。它充当 FIFO —先进先出— 数据存储。Zookeeper 用于管理*型分布式系统的状态和配置,但在我们的例子中,它不需要太多关注,因为我们的集群中只有一个 Kafka 实例。
- 需求:Docker images forZookeeper:3 . 4 . 6和 Kafka:2.4.0 (基于 Scala 版本 2.12)。
- 实例化细节:在尝试启动和运行我的第一个dockeredKafka 实例时,这似乎不是一件容易的事情,因为它需要一些内部和外部容器网络配置,并且通过对官方 Kafka 文档和 Wurstmeister 的 Kafka 文档的一些搜索,我发现下面的配置最适合这个项目:
KAFKA_ZOOKEEPER_CONNECT
:Zookeeper 的地址和端口。由于 Zookeeper 和 Kafka 在同一个 Docker 网络上,我们可以将其设置为 Zookeeper 服务名和默认端口“zookeeper:2181”。
KAFKA_LISTENERS
:地址列表( 0.0.0.0:9093 和 0.0.0.0:9092 )及其关联的侦听器名称(内的和外的),Kafka 代理将在这些地址上侦听传入的连接。内的表示与我们的 Kafka 实例在同一个 Docker 网络内的所有客户端,例如生产者和消费者,而外的表示主机上的所有客户端,例如 Kafka 的 CLI 工具。
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
:监听器名称和安全协议之间的映射。我们用“内:明文,外:明文”来保持简单。*KAFKA_ADVERTISED_LISTENERS
:指向 Kafka 代理的可用地址列表(将发送给客户端进行初始连接)。我们将其设置为“内://kafka:9093,外://localhost:9092 ”。
KAFKA_INTER_BROKER_LISTENER_NAME
:经纪人之间通信使用的监听器名称。*
卡夫卡的完整配置可以在这个项目的docker-compose . YAML文件中找到。
消费者
- 描述:消费者简单地从 Kafka 一条接一条地加载推文,执行预处理和特征提取步骤,然后推断每条推文的极性——正或负。这种极性存储在时间序列数据库中,与推文的 ID 一起,用于可视化目的(也可以进一步处理,或用于检测趋势……)。
推断或预测步骤中使用的机器学*模型被生成并存储在二进制文件中。事先腌制格式。 - 预处理和特征提取:请参考下面深入预处理和特征提取部分的详细描述。
- 语言:Python(3.8 版本)。
- 实现细节:我们创建一个继承自 Kafka-python 类
KafkaConsumer
的类TweetsConsumer
,以方便与我们的 Kafka broker 的连接和交互。
获取 tweets 之后是预处理步骤和分类。一旦获得每条推文的极性,我们就将其存储到我们的时间序列数据库中,用于可视化目的。
时间序列数据库
- 描述:时序数据库是一个使用时间戳来索引记录的数据存储。这意味着对于每个新记录,DB 引擎都会关联一个时间戳,这是从 Unixepoch—1970 年 1 月 1 日 00:00:00 UTC 开始的以纳秒为单位的时间量。 这对于手头的问题来说再好不过了,这个问题就是存储高速到来的依赖于时间的序列数据,并且还需要作为时间序列进行快速检索。
Grafana 将访问该数据库,以输入我们的分析仪表板。 - 技术:我们使用的是InfluxDB这是一个开源的时间序列数据库,在过去的几年里已经成为了行业标准。
Grafana 仪表板
- 链接:【grafana.redouaneachouri.com】T42。
- 描述: Grafana 被全球各种规模的公司用来监控任何可能随时间变化的事物:)从计算机基础设施到*气测量,经过工厂和仓库库存。
我们用它来创建一个仪表板,以便分析推文的极性及其随时间的变化。我们还想分析推文的其他方面,如一天或一年中不同时段的语言分布。 - 使用细节:为了创建我们的仪表板,我们需要将 Grafana 插入一个数据源,在我们的例子中是 InfluxDB ,我们的时间序列数据库。一旦连接上,我们就可以根据需要开始添加面板,并且为每个面板定义一个查询来获取将要显示的数据。
极性的分布。随着时间的推移,我们可以看到一个对称的趋势。
这个架构的完整描述可以在项目的docker-compose . YAML文件中找到。
深入研究预处理和特征提取
为减少数据中的维数、复杂性和噪声而采取的步骤。这是必需的,因为推文 1)是连续的多行文本,不能由简单的模型处理,2)包含不相关的信息,如连接单词和 URL,这会增加复杂性,并可能使模型产生偏差
有关实施细节,请点击查看笔记本。
标记化
文本是由空格和标点符号分隔的字符序列,其含义可以被人脑理解,但对计算机来说毫无意义。然而,我们可以根据空格和标点符号分隔符将文本分割成更小的字符串或单词,称为记号。
我们使用 Punkt 包中提供的预先训练好的 NLTK 模型,该模型考虑了诸如 Dr 和 Mr 这样的头衔,以及诸如 J.Doe 这样的名字中的句点。
正常化
文本中的单词可以有不同的形式。动词可以变化——“出现”、“出现”、“出现”,名词可以设置为阴性和复数——“作者”、“女作家”、“作者”、“女作家”。因此,规范化有助于将具有相同含义的单词组合在一起,使它们达到规范形式。
有两种流行的标准化类型:
- 词干提取:最简单的形式是从单词中去掉词缀,并在查找表中找到词干。
- 词汇化:这是一个更高级的过程,因为它试图通过从句子的上下文中推断词义来找到单词的词汇,或字典形式——例如,“meeting”在“我正在准备与客户的一个重要的会议”和“我明天将与客户的会议”中可能有不同的含义。
变元化比词干化慢,但更准确,而且由于这种慢对我们的实时处理不是一个*的障碍,我们可以使用变元化进行规范化。
然而在使用 lemmatizer 之前,我们需要确定文本中每个单词的上下文,为此我们使用了一个词性标注器。点击此链接查看可能标签的完整列表。
去噪或降噪
噪音是所有不增加信息,但消耗时间和资源,并可能增加模型偏差的数据。以下是本项目中我们认为的噪音:
- 停用词:一种语言中最常见的词,如“a”、“the”、“it”,一般不传达一个意思,除非另有说明。
- 超链接: Twitter 使用t.co来缩短超链接,这不会在转换为 URL 的信息中留下任何值。
- 提到:以
@
开头的用户名和页面。 - 标点:它增加了上下文和意义,但使文本处理起来更复杂。为简单起见,我们将删除所有标点符号。
我们使用英语的正则表达式和停用词和标点的字典来执行过滤。
确定数据集中的单词密度
通过快速绕道,我们可以找到在我们的推文中哪些词与积极或消极情绪最相关:
- 正面:表情符号:)和:),感谢,感谢,追随,爱,和好的。
- 否定:表情符号:(和:-(,去,得到,请,要,和错过。**
N.B. 单词密度是我们的建模算法隐式计算的,所以我们不需要将此作为预处理的一部分包含进来。
数据建模
有关实施细节,请点击查看笔记本。
接下来的部分,我们建立了一个监督学*模型,可以将推文分类为正面或负面,这是我们对数据的两个标签。
为了简单起见,对于我们所拥有的有限数量的数据(10k 条记录),我们将使用一个朴素贝叶斯分类器。**
这里是培训和测试指标(准确性),以及 10 个最具信息性的特性的表格。每行代表一个单词在正面和负面推文中出现的比率。
*Training Accuracy: 0.99957
Testing Accuracy: 0.995Most Informative Features
:( Negati : Positi = 2081.8 : 1.0
:) Positi : Negati = 1656.3 : 1.0
sad Negati : Positi = 23.6 : 1.0
sick Negati : Positi = 19.2 : 1.0
arrive Positi : Negati = 18.8 : 1.0
poor Negati : Positi = 15.9 : 1.0
community Positi : Negati = 15.5 : 1.0
x15 Negati : Positi = 13.2 : 1.0
idk Negati : Positi = 12.5 : 1.0
unfortunately Negati : Positi = 12.5 : 1.0*
改进建议
通过在定制推特上测试我们的模型,我们可以看到它未能对讽刺进行分类,因为它认为这是一种积极情绪,而不是消极情绪。* 这是因为我们的模型缺乏识别更多进化情感的数据复杂性,因此我们可以通过使用更高级的分类算法来处理这方面的工作,该算法可以处理包含进化情感的数据集的复杂性,例如快乐、兴奋、悲伤、和*恐惧。**
结论
在这篇文章中,我就如何设计和创建一个微服务架构向您提供了我的 2 美分,该架构可以从 Twitter 的实时 API 获取连续的推文流,然后通过 Kafka 进行处理,然后提取每条推文的极性。然后我们看到了如何将结果存储在一个时间序列数据库中,以便进一步分析和可视化。
我们还看到了如何在建立分类模型以推断每条推文的极性之前,对数据进行预处理并提取特征。
作为最终结果,我们得出结论,为了能够区分更多进化的情绪,如喜悦和讽刺,我们需要一个更复杂的数据集和一个能够处理这种复杂性的分类算法。
如果你觉得这个教程很有用,并且你想支持高质量文章的创作,考虑给我买杯咖啡吧!
你可以点击“关注”按钮来获取我的最新文章和帖子!
参考
- 情感分析及其应用— MokeyLearn
- Twitter API 文档— Twitter 开发者门户
- Kafka 概念和文档— 阿帕奇 Kafka
- 词干— 维基百科
- 引理满足— 维基百科
- 用 Python NLTK 进行情感分析— 数字海洋
- 图片来源在各自的标题中。
使用 R 的 Twitter 情感分析和可视化
如何衡量 Twitter 用户对任何特定话题的感受
图片来源:unsplash.com
对于除了 Python 或 SQL 之外已经开始进入 R 领域的数据科学家(像我一样),我们很欣赏通过许多内置包快速分析和可视化数据的容易程度。然而,当我开始寻找从外部来源提取数据的有效方法时,我了解到了与 RFacebook(脸书)、rtweet (Twitter)和 Rblpapi(彭博)等主要平台交互的 R 包。虽然用 Python 完成同样的任务是可能的,但 RStudio 的可视化界面和绘图工具已经赢得了加分。
如果您使用的是 RStudio,您可以快速连接并提取公开可用的数据。在 Twitter 的例子中,你可以获得用户列表、不同地区的热门话题以及关注者列表。这种类型的评估将证明对企业分析师、政界人士或我们这些只是对某些数据科学工具的范围感到好奇的人很有帮助。有无数种方法可以分析这种类型的数据(比如箱线图、直方图和文本挖掘等等!).
在通过 Twitter API 认证后,我想知道如何评估和可视化社交媒体网站的用户对给定主题的看法,不仅是说的话,还可能是 T2 的感觉。作为这项工作的一部分,我随机选择了两个国家(加拿*和苏格兰),然后从每个地区抽取了 100 条推文。我记录了我连接 Twitter 的 API、搜索推文、使用 Bing 进行情感分析,然后绘制结果的步骤。
第一步:在 RStudio 中加载需要的包(包括 rtweet)
步骤 2: 通过创建一个访问令牌,使用您的凭证对 Twitter 的 API 进行认证。获取 Twitter 访问令牌的步骤:
https://cran . r-project . org/web/packages/rtweet/vignettes/auth . html
第三步:根据自己选择的话题搜索推文;减少你认为合适的推文数量,并决定是否包括转发。我决定包括加拿*和苏格兰各 100 条推文,并决定不包括转发,以避免重复推文影响评估。
第四步:将每组推文处理成整齐的文本或语料库对象。
第五步:使用预处理文本转换清理推文;这包括词干词。词干提取的一个例子是将单词“计算机”、“计算的”和“计算”滚动到词根“comput”。
额外的预处理包括将所有单词转换为小写字母、删除网页链接(http 元素)以及删除标点符号和停用词。tidytext 包包含超过 1000 个英语停用词的列表,这些停用词对确定文本主体的整体情感没有帮助;这些是“我”、“我自己”、“他们自己”、“存在”和“拥有”等词。我们使用带有反连接的 tidytext 包从 tweets 中删除在步骤 3 中提取的停用词。
第六步:找出两国推特集合中最常用的 10 个词;这将使人们全面了解他们最关心的问题,以及他们参与这些问题的程度。
下面的输出显示了加拿*和苏格兰的前 10 个单词。
如您所见,Scotland 的输出返回的单词远不止 10 个,因为这些顶级单词中的许多都出现了相同的次数。
步骤 7: 使用 Bing lexicon 和 tidytext 包中的 get _ opinions 函数进行情感分析。R 中有许多库、字典和软件包可以用来评估文本中普遍存在的情感。tidytext 和 textdata 包就有这样的词到情感的评估库。三个通用词典是 Bing、AFINN 和 nrc(来自 textdata 包)。
要查看每个包包含的内容,您可以在 R 中运行以下命令:
get _ opinions 函数返回一个 tibble,因此要查看哪些是“积极的”和“消极的”情绪,您需要进行相应的过滤。因为我想*致了解一下,所以我不需要提取整个数据集,但是根据您的需要,您可能需要这样做。
与 Bing 相反,AFINN 词典为其词典中的每个单词分配一个“肯定”或“否定”的分数;然后,进一步的情感分析将把情感分数加起来,以确定整体表达。*于零的分数表示积极的情绪,而小于零的分数意味着消极的整体情绪。计算得分为零表示中性情绪(既不积极也不消极)。
为了使用 Bing 对加拿*的 tweets 执行情感分析,我运行了以下命令,它返回一个 tibble。
然后,为了直观地描述字数,您可以过滤并并排绘制单词,以比较积极情绪和消极情绪。
在下面的例子中,你会看到“trump”被标注为阳性。鉴于这些推文可能指的是现任美国总统,你可能希望仔细看看这个词在这些推文中实际上是如何使用的。
您可以对苏格兰的推文进行类似的分析;为了避免重复的 R 代码,我没有在这里包括这些。
第 8 步:获取每条推文的情感得分(您可以为此创建一个函数,如下所示)。
将该函数应用于两组推文:
以下是我们收集的加拿*第 91 条推文的正面情感评分示例。单词“帮助”和“快乐”的得分为 2。
这里我们创建了一个 tibble,它指定了国家、分数和类型:
最后,我们可以看看每组情绪的一些特征。这是两组推文情绪的并列直方图,以及一些关键的汇总统计数据。
正如您在下面看到的,我们收集的推文中的观点总体上是中立的,最高观点得分为“0”。
希望上面关于从 Twitter API 收集数据、使用 tidytext 进行情感分析以及使用 ggplot 可视化您的发现的步骤能够帮助您踏上数据科学之旅!
新冠肺炎期间基于新闻话题的推特情感分析
—网络公众如何回应疫情
距离美国首例新冠肺炎病例已经过去了 100 多天,距离加州最早的封锁令也已经过去了 45 天,在这段特殊的时间里,你感觉如何?你知道其他人对疫情的反应吗?
engin akyurt 在 Unsplash 上拍摄的照片
冠状病毒确实危及我们的身体健康,但同时,社交距离也对我们的情绪稳定性构成威胁。因此,理解新冠肺炎治下的公众情绪至关重要。
为了分析这些,我们组成了一个四人小组( Willa Yu 、 Nora Luo 、星璇张、李艾杰)参加了加州*学戴维斯分校的新冠肺炎挑战赛。我们在推特上部署了情感分析,在新闻上部署了主题建模,以帮助理解情感趋势。基于这些,我们构建了仪表板作为日常情绪监控产品来展示结果。
目录
- 分析处理
- 数据源
- 总体看:推特上发生了什么
- 进一步分析:提取多维情感
- 话题分析:当我们谈论新冠肺炎时,我们谈论什么
- 进一步的步骤和结论
分析处理
为了研究公众情绪,我们选择 Twitter 作为我们的目标领域。作为世界上最*的社交网络平台之一,Twitter 拥有*量用户生成的帖子,这些帖子以低延迟的方式密切反映了公众对这一疫情的反应。通过在其上部署自然语言处理(NLP)方法,我们能够提取并量化一段时间内的公众情绪。我们使用的工具是 TextBlob、IBM Watson Tone Analyzer、BERT 和 Mallet。
起初,我们使用 TextBlob 来探索公众情绪,这显示了一种稳步变得更加积极的上升趋势。
图一。产生 5 种情感的过程。
然后,我们以多维的方式在更详细的层次上分析情绪,以更全面地揭示趋势。我们使用 IBM Watson Tone analyzer 和手动标签来标记具有 5 种情绪的采样推文,然后使用 BERT 建立分类模型来分类具有 5 种情绪的所有推文。有了这个,我们能够:1)识别推文中更微妙的情绪;2)定义一个度量情感密度,它可以代表推文情感的复杂性。
图二。产生新闻话题的过程。
为了进一步了解情感的趋势,我们决定引入使用 Mallet 的新闻主题建模,为情感添加一层上下文。通过构建一个仪表板来比较每个主题的情绪,我们可以更具体地了解趋势。
数据源
这项分析使用的两个主要数据源是从 1 月 20 日到 4 月 26 日每天的推文和新闻。我们还从约翰霍普金斯 CSSE 医院获得了确诊病例的统计数据,以补充情绪和主题的上下文。下面是我们所用数据的概览:
表 1。数据源
总体看:推特上发生了什么
自 2020 年 1 月报告首例确诊病例以来,#新冠肺炎和其他类似标签一直在推特上流行。收集了 130 多万条与新冠肺炎相关的推文(每天*约 1 万多条),我们想知道人们在 Twitter 上对这些推文的反应。首先,我们探索了一些推文的参与度指标,如赞数。下图显示了每天每条推文的平均点赞/回复/转发次数:
图 3。一段时间内每条推文的平均赞数/回复数/转发数。对于一些数字异常高的日子,我们检索了收到最多赞/回复/转发的推文内容。
从图表中,我们可以看出,从 1 月到 3 月的几天里(例如 1 月 29 日、2 月 26 日和 3 月 9 日),人们对一些#新冠肺炎推文的反应很激烈。获得最多赞/回复/转发的推文内容从中国的科罗纳啤酒和新冠肺炎变成了美国的新冠肺炎和政府的行动。在 3 月下旬和 4 月份,每条推文的平均赞数/回复数/转发数趋于持平,这表明 Twitter 上的人们对新冠肺炎推文的反应或参与比以前少了。
Twitter 不仅是一个让人们回复他人微博的地方,也是一个发布你的微博和分享你的感受的平台。因此,除了喜欢/回复/转发,我们还挖掘了新冠肺炎相关推文的内容,以了解人们的感受和表达如何随着时间的推移而变化。在 Python 中的情感分析库 TextBlob 的帮助下,我们提取了每条推文的主观/客观(主观 ) 内容是怎样的,内容是正面还是负面(极性 ) 。平均主观性和极性的数字如下所示:
图 4。新冠肺炎相关推文的主观性(客观[0–1]主观)和极性(消极[-1 — +1]积极)如何随时间变化。虚线代表平均主观性和平均极性的简单线性回归。
根据上面的图表,随着新冠肺炎的发展,相关推文的表达平均变得更加主观(从*约 0.33 到*约 0.35),人们的感受平均变得更加积极(从*约 0.04 到*约 0.06)。为什么会这样?为什么随着越来越多的人感染冠状病毒,相关推文的情绪走向积极?带着这样的问题,我们深入研究了推文反映的实际情绪,以及人们在提到这种疾病时谈论的话题。
进一步分析:提取多维情感
我们利用伯特模型进行了进一步的分析。BERT 是谷歌预先训练的模型,可以针对广泛的 NLP 任务进行微调(了解更多)。在我们的例子中,它与 IBM 的 Watson Tone Analyzer ( 了解更多)结合使用,用 5 种情感类型来标记推文。下面是我们如何生成它们的:
第一步 :我们准备了一个训练数据集供模型学*,我们利用 Watson Tone Analyzer 为每条推文标注 5 种情绪。数据看起来像什么:
表二。推文如何被标记的例子。
第二步 :之后引入了 BERT Base 无壳模型,并进行了微调。我们的实现深受 Chris McCormick [1]的启发。在每个情感类型下建立了一个二元分类模型,然后用于为未标记的推文产生 1/0 标签。下面是我们提取的五个情感类别以及它们下面的典型推文。
图 5。典型的推文及其观点。
第三步 :标签就位后,我们定义了一些指标来帮助进一步理解公众情绪的变化。我们首先提出了一个称为情绪水平的指标,使用具有特定情绪的推文占一天总推文的比例。由于一条推文可以拥有多种情绪,我们还计算了情绪密度,以显示一条推文在一天内平均有多少种不同的情绪。这个数字会给我们一个直观的印象,那就是一天中有多少推文“充满”了不同的情绪。然后,我们计算这些指标的每日变化,并形成增量指标。下表总结了我们的指标。
表 3。指标定义和公式。
调查的结果
在将我们的模型结果放回到疫情背景下的时间线(我们使用累计确诊病例数的增长率来反映疾病的传播)后,我们总结了一些有趣的发现。
情绪等级:
图 6。情感等级的定义和 5 种情感的趋势。
●在美国首例确诊病例被报道之前(1 月 21 日),情绪“分析型”在推特中被发现最多,其他情绪水平仍然较低。
在报道第一个病例后,出现了复杂的情绪,这表明社会对疫情的认识不断提高。
●“Sad”波动较*,但在 1 月份上升后仍相对较高。
●2 月下旬,不同的情绪趋于分化,“自信”增加,“恐惧”下降。信息超载似乎让人们变得不那么敏感了。
情绪密度:
图 7。按天定义情感密度和趋势。
●通过上图中情绪密度的总体趋势,我们可以推断,从 2 月下旬到 3 月中旬,人们的情绪最密集,尤其是负面情绪,其次是 1 月下旬到 2 月中旬。
●4 月份情绪密度降低,停留在较低位置,但仍高于年初。
当我们谈论新冠肺炎时,我们谈论什么
在研究了调查期间情绪的总体趋势后,我们想添加另一层信息来剖析总体趋势。我们打算提取一些人们在谈论新冠肺炎时讨论的热门话题,以及每个话题下的极性(积极/消极)如何变化,所以我们首先从新冠肺炎相关新闻中提取几个话题,然后利用这些话题中的关键词对推文进行分类。
使用新闻文本而不是推文进行主题建模的优势在于,推文很短、不正式且高度感性,这对于主题模型来说很难处理,而新闻文本将以正式和中立的方式捕捉新冠肺炎统治下的重要事件。
提取新闻话题
我们利用自然语言处理工具包 Mallet 进行潜在狄利克雷分配(LDA)主题建模【2】,总结了 8 个主题。我们通过总结模型返回的主题关键词来命名这些主题,它们如下(按频率降序排列):新冠肺炎期间的生活,中国新冠肺炎,封锁令,医学测试&分析,政府行为,游戏季节,经济影响,医疗供应。配备了 TextBlob 的情感分析,这些主题随时间的趋势如下:
不同的主题涵盖了不同的时期,而且*多数都与事实相符。例如,在 3 月份之前,只有少数像中国新冠肺炎这样的话题出现在冠状病毒相关新闻中。三月之后,由于新冠肺炎的广泛传播,相关新闻的数量开始激增,尤其是像医学测试这样的话题。一个有趣的发现是,随着自 3 月中旬以来封锁令的执行,关于新冠肺炎期间生活的新闻达到顶峰,成为*多数平均极性得分最高的新闻。
对于新闻中这些话题的情绪来说,在新冠肺炎期间的生活无疑是所有话题中最积极也是最客观的话题,其次是游戏赛季、医疗供应和医疗测试与分析。然而,另一方面,中国的新冠肺炎这个话题得到了最负面和主观的描述。
推文中的新闻话题
根据新闻话题建模总结出的话题,使用相应的关键词对推文进行分类。按关键词过滤推文(如图所示)后,由 8 个话题提示,话题趋势如下:
图 9。8 个主题下的推文数量如何随时间变化。用于主题分类的关键词显示在底部。
同样的新闻话题趋势也适用于此,因为在中国提到新冠肺炎的推文趋势在 3 月前达到顶峰,自美国出现首例病例后开始下降。如图所示,随着时间的推移,公众越来越关注政府的行为。医疗相关、经济影响和 COVID 期间的生活主题缓慢增加。至于游戏季、面具和呆在家里的话题并没有随着时间的推移呈现出明显的上升趋势。
图 10。每个话题的情绪如何随时间变化(蓝线)。绿色背景显示了每个主题下推文的放*趋势。
当分析情绪时,我们可以看到*多数话题的积极性都在增加。
●积极度最高的话题仍然是关于新冠肺炎时期的生活。乐观情绪增长最快的一个趋势是对呆在家里的看法,这与上面提到的人们在隔离期间变得不那么敏感的观点相呼应。
●对于关于口罩和呆在家里的辩论话题,我们可以看到,在新冠肺炎疫情爆发之初,极性首先下降,但在 3 月份晚些时候上升。
●谈论政府相关问题的推文倾向于具有非常波动的情绪趋势线,极性整体上下降。
●最近,越来越多的推文谈论经济影响,如裁员和失业,但整体情绪趋向积极。
●对于游戏赛季,由于冠状病毒,许多游戏被取消,所以那些推文的情绪不是很积极。
●最后,随着时间的推移,提到“中国”的推文变得更加负面。
进一步的步骤
我们的分析显示了确诊病例的增长与情绪趋势之间的一些关系。有了地理数据、人口统计信息等更细粒度的数据,就可以产生进一步的洞察,比如监测受灾最严重地区的公众情绪。有了更具体的目标,分析将对机构或政府采取行动更有价值。
结论
在这个项目中,我们用几种方法分析了新冠肺炎相关推文的情绪。总体趋势表明,随着时间的推移,公众变得更加乐观。深入多维度的情绪分析,我们发现随着时间的推移,情绪“自信”上升,“恐惧”下降。此外,情绪密度表明,公众原来是不那么情绪化的。最后,情绪背后的话题展现了更多的细节。
抗击冠状病毒不仅需要政府的指导,还需要公众的积极态度。我们的分析提供了一种潜在的方法来揭示公众的情绪状态,并帮助机构及时做出反应。
最后但并不是最不重要的
我们真诚地感谢加州*学戴维斯分校 MSBA 项目的教师和学生行政助理为推动新冠肺炎挑战赛所做的努力。此外,我们非常感谢 Ashwin Aravindakshan 教授和 rn Boehnke 教授提出的宝贵建议。
也请查看我们创建的 GitHub 页面和 Tableau 仪表盘,以获得更具交互性的视图和更多细节。
参考文献:
[1]麦考密克、克里斯和尼克·瑞安。2019.“伯特微调教程与 Pytorch 克里斯麦考密克”。【Mccormickml.Com】T4。https://mccormickml.com/2019/07/22/BERT-fine-tuning/.
[2]普拉巴卡兰,塞尔瓦。2018.“用 Gensim (Python)进行主题建模”。机器学*加。https://www . machine learning plus . com/NLP/topic-modeling-gensim-python/。
Python 中的 Twitter 情感分析
计算 Twitter 账户的主观性和极性得分
卢克·切瑟在 Unsplash 上的照片
情感分析是数据科学和人工智能中最常见的任务之一。在本文中,我们将使用 Python、Tweepy 和 TextBlob,使用 Twitter API 和自然语言处理对选定的 Twitter 帐户进行情感分析。
介绍
我们将使用 Twitter 对书面文本进行情感分析。在这个例子中,我们将使用 Twitter,但这也可以用于商业环境中,以分析不同的社交媒体账户、对贵公司的评论、对贵公司产品和服务的评论,分析支持票、电子邮件或来自调查的自由文本,从而了解在线参与贵公司业务的人们的情绪。
你会学到什么?
您将学*如何使用 TextBlob 进行基本的情绪分析;用于 Python 的强*的自然语言处理库。我们还将使用 WordCloud 库来可视化我们的一些发现,我们还将使用 Twitter API 。熟悉 API 对于数据科学家来说是一项有用的技能。这是从互联网上获取数据的一种非常常见的方法。
问题定义
我们的任务是根据主观性和极性来分析个人推特账户的推文。我们会将每条推文区分为正面、负面和中性,并计算正面推文的百分比。我们将使用 WordCloud 库来展示来自推文中最积极的词汇。
逐步解决方案
创建虚拟环境
我们希望将特定于 Twitter 的 Python 库与我们计算机上的其他库分开。这就是为什么我们将为此项目创建一个虚拟环境。
您的计算机上应该有一个文件夹,用于存储所有虚拟环境。这个文件夹中应该没有其他东西,没有项目文件等。,只是虚拟环境不同。您可以将该文件夹称为“虚拟环境”。
有了该文件夹后,创建您的虚拟环境:
$ virtualenv twitter_venv
然后激活它:
$ source twitter_venv/bin/activate
然后安装 Tweepy 等库:
(twitter_venv) $ conda install tweepy(twitter_venv) $ conda install textblob(twitter_venv) $ conda install wordcloud
成功安装库后,您可以停用它:
(twitter_venv) $ deactivate
然后,您需要将这个虚拟环境添加到您的 jupyter 笔记本电脑配置中:
$ python -m ipykernel install — user — name=twitter_venv
一旦你完成了这一步,你就可以继续这个项目了。
创建项目文件夹
在你的电脑上为一个名为“推特-情绪-分析”的项目创建一个文件夹:
$ mkdir “Twitter-Sentiment-Analysis”
创建 Twitter 应用密钥
对于这个项目,我们将使用 Twitter API 从 Twitter 获取信息。为了做到这一点,我们需要一个 Twitter 开发者账户。
Twitter 为开发者引入了一些新的要求,完成这个过程需要几个步骤。
首先,你需要去 developer.twitter.com。如果您没有标准的 Twitter 帐户,您需要创建一个并登录。
标准 Twitter 账户将允许你登录,但不允许你做任何其他事情,你需要通过访问这个地址developer.twitter.com/en/apply-for-access并点击申请开发者账户来申请开发者访问。
你必须回答几个问题。出于使用 Twitter 开发者工具的原因,您可以选择探索 API 。
你还需要回答几个关于如何使用 Twitter API 的问题。您可以解释说,您将学*数据科学,不打算使用 API 构建任何产品(主题:您只是出于本练*的目的创建一个 Twitter 开发人员帐户,如果您打算将 Twitter 开发人员帐户用于任何其他用途,您需要提供更多详细信息)。
最后,你必须同意开发商协议和政策,你可以提交你的申请。
提交申请后,您需要点击 Twitter 发送给您的链接来确认您的电子邮件地址。确认您的电子邮件地址后,您的 Twitter 开发人员帐户的访问权限应该会立即被授予。如果你从 Twitter 上收到一封邮件,询问关于你的申请的更多信息或澄清,你需要提供所有必要的信息。
一旦你的访问权限被授予,进入这个网址https://developer.twitter.com/en/apps和创建一个应用。
你必须提供一个名称,描述,网址和你创建应用程序的原因。
一旦创建了您的应用程序,请转至密钥和令牌选项卡。这里已经创建了 API 密钥和 API 秘密密钥,您需要生成一个访问令牌和访问令牌秘密。点击生成按钮即可。你的 Twitter 情感分析项目需要这四个值。现在把它们都复制下来,放在文件中安全的地方。我们稍后会用到它们。
开始新的笔记本
通过在终端/命令提示符下键入命令,进入项目文件夹并启动 Jupyter Notebook:
$ cd “Twitter-Sentiment-Analysis”
然后
$ jupyter notebook
点击右上角的新建,选择 twitter_venv 虚拟环境。
作者图片
这将在您的浏览器中打开一个新的 jupyter 笔记本。将未命名的项目名称重命名为您的项目名称,您就可以开始了。
作者图片
如果你正在使用 Google Colab ,打开一个新的笔记本。
装载库
正在导入 Tweepy、TextBlob、WordCloud、Pandas、Numpy 和 Matplotlib。
让我们加载配置文件(确保编辑配置文件并输入简介中描述的 Twitter API 细节)。
配置文件 config.csv 的格式如下:
twitterApiKey,twitterApiSecret,twitterApiAccessToken,twitterApiAccessTokenSecret
enter-your-value,enter-your-value,enter-your-value,enter-your-value
你可以在这里找到一个例子。
现在我们需要设置 Tweepy 认证所需的所有 Twitter API 配置值。这些值将从 config.csv 文件读取到 Python 变量中。
我们正在使用 Tweepy 进行身份验证调用,这样我们就可以调用一个函数来从指定的 Twitter 帐户中检索最新的 tweets。
我们使用 elonmusk Twitter 账户作为例子,但可以随意将 Tweeter 账户更改为不同的账户;甚至是你自己的推特账号。
现在我们将从指定的 Twitter 账户中检索最后的 50 条推文&回复。
我们将从它创建熊猫数据帧。
我们通过调用 head() 函数来看看数据帧里有什么。
作者图片
在我们开始情绪分析之前,最好先清理掉每条推文中不必要的数据。
我们将创建一个clean up weet函数,它将:
- 删除提及
- 移除标签
- 删除转发
- 删除 URL
现在,我们将把它应用到熊猫数据框中的所有推文中。
我们还将使用 TextBlob 构建几个函数来计算我们推文的主观性和极性。
现在,我们将这些函数应用到我们的数据框架中,并在数据框架中创建两个新特征主观性和极性。
现在,让我们看看我们的数据框现在是什么样子。
作者图片
我们这里有一些空行,所以在进一步处理之前,让我们删除它们。
下面的命令将删除 Tweet 列等于""的所有行。
作者图片
我们可以看到,我们已经为数据框架中的主观性和极性计算了分数。
现在让我们建立一个函数,将我们的推文分为负面、中性和正面。
应用这个函数,在我们的数据框中创建另一个名为 Score 的特征。
这是我们的数据框架,包含我们所有推文的主观性、极性和得分。
作者图片
现在让我们取所有正面推文,并计算我们的数据框架中所有推文中正面推文的百分比。
57.446808510638306 % of positive tweets
我们现在可以使用 Matplotlib 可视化正面、负面和中性的推文。
作者图片
我们可以看到负面、中立和正面的推文是如何在这个账号上传播的。
我们也可以通过在图表上显示主观性和极性的精确值来可视化相同的信息。
我们还可以计算客观推文的百分比。
27.659574468085108 % of objective tweets
最后,我们还可以生成一个词云来查看我们正在分析的推文中使用的主题和最常用的词。
作者图片
对推文进行情感分析使我们能够计算主观性和极性的数值。
这可以帮助我们更好地理解这个 Twitter 账户所使用的语言。
从营销的角度来看,将这一点与关于喜欢和评论的额外信息结合起来非常有用,可以让我们发现主观性、极性和特定 Twitter 账户用户参与度之间的一些相关性。
我们鼓励你对这个例子进行更多的实验,并想出更多关于如何在实践中使用它的想法。
如果你想了解更多并尝试 Python 和数据科学,你可以看看我的另一篇文章用 Python 分析药品销售数据、用 MNIST 介绍计算机视觉和用 Python 进行图像人脸识别。
为了巩固您的知识,请考虑从头开始再次完成任务,不要查看代码示例,看看您会得到什么结果。这是巩固你的知识的一件极好的事情。
Jupyter 笔记本的完整 Python 代码可在 GitHub 上获得:
https://GitHub . com/pj online/Basic-Data-Science-Projects/tree/master/8-Twitter-情操分析
编码快乐!
还没有订阅媒体?考虑报名成为中等会员。每月只需 5 美元,你就可以无限制地阅读媒体上的所有报道。订阅 Medium 支持我和其他作家在 Medium 上。
Kaggle Twitter 情感分析:自然语言处理和文本分析
使用 Python 中的计数向量器和支持向量分类器对微博是否与仇恨相关进行分类
你好 Medium 和 TDS 家族!
在这篇文章中,我将谈论如何使用 Python 中的 CountVectorizer 来分类推文是否是种族主义/性别歧视相关的推文。在本教程中,我将使用 Google Colab 进行编程。
查看视频版这里:https://youtu.be/DgTG2Qg-x0k
你可以在这里找到我的完整代码:https://github.com/importdata/Twitter-Sentiment-Analysis
数据收集
我们将使用 Kaggle.com 来查找数据集。使用下面的链接访问 Kaggle 上的数据集。
检测仇恨推文,由分析 Vidhya 提供
www.kaggle.com](https://www.kaggle.com/arkhoshghalb/twitter-sentiment-analysis-hatred-speech)
1。了解数据集
让我们阅读数据集的上下文来理解问题陈述。
在训练数据中,如果推文与种族主义或性别歧视情绪相关联,则推文被标记为“1”。否则,推文将被标记为“0”。
2.下载数据集
现在您已经了解了数据集,接下来下载两个 csv 文件——训练和测试数据。只需点击“下载(5MB)”
下载数据集后,确保解压文件。
现在让我们转到 Google Colab!
数据探索(探索性数据分析)
让我们看看训练和测试数据是什么样的。
检查训练和测试数据
注意这里有一些特殊的字符,比如@、#、!,等等。我们将在稍后的数据清理步骤中删除这些字符。
检查是否有任何缺失值。训练和测试数据都没有缺失值。
检查训练数据的缺失值
检查测试数据的缺失值
数据清理
我们将使用 tweet 预处理器库清理数据。下面是链接:https://pypi.org/project/tweet-preprocessor/
这个库删除网址,标签,提及,保留字(RT,FAV),表情符号和表情符号。
我们还将使用正则表达式库来删除 tweet-preprocessor 库没有的其他特殊情况。
数据清理功能
比较原始推文和清理过的推文
测试和训练分离
现在我们已经清理了数据,我们将使用 train_test_split 函数进行测试和训练分割。
我们将使用 70%的数据作为训练数据,剩余的 30%作为测试数据。
使用 CountVectorizer 对推文进行矢量化
现在,我们将把文本转换成数字形式,因为我们的模型不能理解人类语言。我们将使用 CountVectorizer 对推文进行矢量化。CountVectorizer 提供了一种简单的方法来标记一组文本文档并构建已知单词的词汇表。
例如,假设我们有一个如下所示的文本文档列表。
计数矢量器示例
CountVectorizer 组合所有文档并对它们进行标记。然后计算每个文档中出现的次数。结果如下所示。
计数矢量器示例结果
模型结构
现在我们已经对所有的推文进行了矢量化,我们将建立一个模型来对测试数据进行分类。
我们将使用监督学*算法,支持向量分类器(SVC)。它广泛用于二元分类和多类分类。
您可以在 scikit-learn 文档页面上找到更多解释:https://sci kit-learn . org/stable/modules/generated/sk learn . SVM . SVC . html
准确(性)
开始了。准确率竟然是 95%!
SVC 准确度分数
关于原钻的推文分析
原钻的推特情感分析:去除假阴性
《原钻》是一部犯罪/惊悚片,由亚当·桑德勒、茱莉亚·福克斯、勒凯斯·斯坦菲尔德和前 NBA 球员凯文·加内特主演。由乔希和本尼·萨夫迪编剧和导演的《原钻》是一部历时十年制作的电影。故事情节讲述了一个纽约珠宝商和赌博成瘾的人,他必须找回一颗在埃塞俄比亚开采的黑欧泊未切割宝石,以便出售和偿还债务。这部电影是 A24 迄今为止票房最高的电影,在撰写本文时票房收入为 4000 万美元。
以我个人的经验来看,抛开专业影评人对这部电影的好评如潮不谈,我的同行对这部电影的评价褒贬不一。这启发了我对关于原钻的推文进行情感分析。在本文中,我们将使用 python Twitter API 包装器 Tweepy 来检索关于电影的推文,然后使用另一个名为 textblob 的 python 库对这些推文进行情感分析。
我们开始吧!
首先,你需要申请一个 Twitter 开发者账户:
在您的开发人员帐户获得批准后,您需要创建一个 Twitter 应用程序:
申请 Twitter 开发者账户和创建 Twitter 应用程序的步骤在这里有所概述。
为了访问 Twitter API,我们将使用免费的 python 库 tweepy。tweepy 的文档可以在这里找到。
- 安装
首先,确保您已经安装了 tweepy。打开命令行并键入:
pip install tweepy
2.导入库
接下来,打开您最喜欢的编辑器,导入 tweepy 和 pandas 库:
import tweepy
import pandas as pd
3.认证
接下来,我们需要我们的消费者密钥和访问令牌:
请注意,该网站建议您保持您的密钥和令牌私有!这里我们定义了一个假的密钥和令牌,但是在创建 Twitter 应用程序时,您应该使用真正的密钥和令牌,如上所示:
consumer_key = '5GBi0dCerYpy2jJtkkU3UwqYtgJpRd'
consumer_secret = 'Q88B4BDDAX0dCerYy2jJtkkU3UpwqY'
access_token = 'X0dCerYpwi0dCerYpwy2jJtkkU3U'
access_token_secret = 'kly2pwi0dCerYpjJtdCerYkkU3Um'
下一步是创建 OAuthHandler 实例。我们传递上面定义的消费者密钥和访问令牌:
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
接下来,我们将 OAuthHandler 实例传递给 API 方法:
api = tweepy.API(auth)
- TWITTER API 请求
接下来,我们为我们有兴趣分析的字段初始化列表。现在,我们可以查看推文字符串、用户和推文时间。接下来,我们在一个 tweepy“Cursor”对象上编写一个 for 循环。在“Cursor”对象中,我们传递“api.search”方法,为我们想要搜索的内容设置查询字符串,并设置“count”= 1000,这样我们就不会超过 Twitter 的速率限制。在这里,我们将搜索关于“星球*战”的推文。我们还使用“item()”方法将“Cursor”对象转换为 iterable。
为了简化查询,我们可以删除转发,只包含英文推文。为了了解该请求返回的内容,我们还可以打印附加到每个列表的值:
twitter_users = []
tweet_time = []
tweet_string = []
for tweet in tweepy.Cursor(api.search,q='Uncut Gems', count=1000).items(1000):
if (not tweet.retweeted) and ('RT @' not in tweet.text):
if tweet.lang == "en":
twitter_users.append(tweet.user.name)
tweet_time.append(tweet.created_at)
tweet_string.append(tweet.text)
print([tweet.user.name,tweet.created_at,tweet.text])
为了实现可重用性,我们可以将它封装在一个函数中,该函数将关键字作为输入。我们还可以将结果存储在数据帧中并返回值:
def get_related_tweets(key_word):twitter_users = []
tweet_time = []
tweet_string = []
for tweet in tweepy.Cursor(api.search,q=key_word, count=1000).items(1000):
if (not tweet.retweeted) and ('RT @' not in tweet.text):
if tweet.lang == "en":
twitter_users.append(tweet.user.name)
tweet_time.append(tweet.created_at)
tweet_string.append(tweet.text)
print([tweet.user.name,tweet.created_at,tweet.text])
df = pd.DataFrame({'name':twitter_users, 'time': tweet_time, 'tweet': tweet_string})
return df
当我们可以用关键字“原钻”调用函数时:
get_related_tweets('Uncut Gems')
我们也可以传入关键字“亚当·桑德勒”:
get_related_tweets('Adam Sandler')
我们也可以传入关键字“Julia Fox”:
get_related_tweets('Julia Fox')
还有《萨夫迪兄弟》:
get_related_tweets('Safdie Brothers')
为了获得情感分数,我们需要导入一个名为 textblob 的 python 包。textblob 的文档可以在这里找到。要安装 textblob,请打开命令行并键入:
pip install textblob
下次导入 textblob:
from textblob import TextBlob
我们将使用极性得分作为积极或消极情绪的衡量标准。极性得分是一个从-1 到+1 的浮点数。
例如,如果我们定义一个 textblob 对象并传入句子“原钻是最好的!”:
sentiment_score = TextBlob("Uncut Gems is the best!").sentiment.polarity
print("Sentiment Polarity Score:", sentiment_score)
我们也可以试试“亚当·桑德勒太棒了!”:
sentiment_score = TextBlob("Adam Sandler is amazing!").sentiment.polarity
print("Sentiment Polarity Score:", sentiment_score)
我在使用 textblob 时注意到的一个缺陷是,尽管存在积极的形容词,但它对消极单词的存在给予了更*的权重,这可能会夸*错误的否定。电影标题中出现“未剪辑”一词会显著降低情感值。例如,考虑“这部电影太棒了”vs“原钻太棒了!”:
sentiment_score = TextBlob(“This movie is amazing”).sentiment.polarity
print("Sentiment Polarity Score:", sentiment_score)
sentiment_score = TextBlob(“Uncut Gems is amazing!”).sentiment.polarity
print("Sentiment Polarity Score:", sentiment_score)
我们可以看到,对于“原钻太棒了!”,在情绪依然积极的同时,明显低于前一句“这部电影太棒了”时两者应该价值接近或相等。我们解决这个问题的方法(作为一个快速解决方案)是,我们将从 tweet 中删除单词“Uncut ”,并从结果中生成情感评分。
让我们获得关于“原钻”的推文的情感极性分数,并将它们存储在一个数据框中(在删除单词“未切割”之前):
df = get_related_tweets("Tesla Cybertruck")
df['sentiment'] = df['tweet'].apply(lambda tweet: TextBlob(tweet).sentiment.polarity)
print(df.head())
我们也可以计算积极和消极情绪的数量:
df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Tweets", len(df_pos))
print("Number of Negative Tweets", len(df_neg))
正如我们所看到的,关于“原钻”的负面推文明显多于正面推文,但这可能是因为电影名称中出现了“未剪辑”这个词,这可能给了我们错误的否定。
让我们修改数据框架,从推文中删除“未剪切”一词:
df['tweet'] = df['tweet'].str.replace('Uncut', '')
df['tweet'] = df['tweet'].str.replace('uncut', '')
df['tweet'] = df['tweet'].str.replace('UNCUT', '')
df['sentiment'] = df['tweet'].apply(lambda tweet: TextBlob(tweet).sentiment.polarity)
print(df.head())
df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Tweets", len(df_pos))
print("Number of Negative Tweets", len(df_neg))
我们可以看到,当我们删除“未切割”这个词时,积极的推文明显更多。
对于代码重用,我们可以将其全部封装在一个函数中:
def get_sentiment(key_word):
df = get_related_tweets(key_word)
df['tweet'] = df['tweet'].str.replace('Uncut', '')
df['tweet'] = df['tweet'].str.replace('uncut', '')
df['tweet'] = df['tweet'].str.replace('UNCUT', '')
df['sentiment'] = df['tweet'].apply(lambda tweet: TextBlob(tweet).sentiment.polarity)
df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Tweets about {}".format(key_word), len(df_pos))
print("Number of Negative Tweets about {}".format(key_word), len(df_neg))
如果我们用“原钻”调用这个函数,我们得到:
get_sentiment(“Uncut Gems”)
如果我们能以编程方式可视化这些结果,那将会很方便。让我们导入 seaborn 和 matplotlib 并修改我们的 get _ 情操函数:
import seaborn as sns
import matplotlib.pyplot as pltdef get_sentiment(key_word):
df = get_related_tweets(key_word)
df['tweet'] = df['tweet'].str.replace('Uncut', '')
df['tweet'] = df['tweet'].str.replace('uncut', '')
df['tweet'] = df['tweet'].str.replace('UNCUT', '')
df['sentiment'] = df['tweet'].apply(lambda tweet: TextBlob(tweet).sentiment.polarity)
df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Tweets about {}".format(key_word), len(df_pos))
print("Number of Negative Tweets about {}".format(key_word), len(df_neg))
sns.set()
labels = ['Postive', 'Negative']
heights = [len(df_pos), len(df_neg)]
plt.bar(labels, heights, color = 'navy')
plt.title(key_word)
get_sentiment("Uncut Gems")
我们也可以用“亚当·桑德勒”来调用函数:
get_sentiment( “Adam Sandler”)
和“朱莉娅·福克斯”:
get_sentiment(“Julia Fox”)
还有“凯文·加内特”:
get_sentiment(“Kevin Garnett”)
还有《勒凯斯·斯坦菲尔德》:
get_sentiment(“Lakeith Stanfield”)
正如你所见,关于《原钻》及其主演的推文正面情绪多于负面情绪。
概括地说,在这篇文章中,我们讨论了如何使用 python twitter API wrapper (Tweepy)从 Twitter 中提取推文。我们还回顾了 python 情感分析包 textblob,以及如何使用它从推文中生成情感得分。最后,我们展示了如何通过删除“未剪切”这个词来修改推文,这个词会人为地降低情感分数。收集几天的数据来观察情绪如何随时间变化会很有趣。也许我会把它留到以后的文章里。
感谢您的阅读。这篇文章的代码可以在 GitHub 上找到。
如果你喜欢这篇文章,你可以在我的 patreon 链接这里做一点贡献。
祝好运,机器学*快乐!
对新型冠状病毒(新冠肺炎)的推特情感分析
自从围绕冠状病毒的阴谋论爆发以来,脸书、推特和 Instagram 等社交媒体平台一直在积极进行审查和事实核查,以打击错误信息。随着更多可靠的消息来源被放*,Twitter 变得比疫情爆发初期更支持。我认为听到真正的公众声音和发现关于冠状病毒的真实情绪会更有趣。
不要被“刮擦”这个词吓倒。如果你能浏览网页,即使你是个新手,你也能像专业人士一样进行网页抓取。所以请容忍我。
了解态度的最简单方法是收集所有包含冠状病毒这个词的推文。我甚至通过将语言设置为英语和美国境内的地形来缩小研究范围。这将确保样本数据集与搜索主题保持一致,并提高预测的准确性。
研究范围定下来之后,现在就可以开始刮了。我更喜欢使用 八分解析 说到挑选最好的网页抓取工具,它有自动检测功能,这为我节省了很多手工挑选和选择数据的时间。
Twitter 更加动态,因为它有无限的滚动,这意味着一旦我们继续向下滚动页面,推文就会显示出来。为了获得尽可能多的 tweets,我构建了一个循环列表来维护获取信息时的滚动动作。这确保了刮擦工作流保持一致,不会中断。
接下来,我创建一个提取操作。Octoparse 在我们输入搜索 URL 时呈现网页。它会将网页结构分解成子组件,这样我就可以轻松地点击目标元素来设置命令,并告诉机器人——帮我获取信息。当我点击其中一条推文时,提示面板会弹出,建议选择子元素。
在那里!相应的事件会自动添加到工作流中。它还会找到其他推文。遵循提示指南,并单击“全选”命令。最终的工作流程应该是这样的:
八解析工作流
逻辑很简单:刮刀将首先访问页面。然后,它开始提取推文,直到完成循环中的所有推文。它将重复滚动动作来定位另一组推文,并再次继续提取,直到所有信息都被成功提取。
这是我得到的最终结果:
决赛成绩
用于情感分析的自然语言处理:
NLP 是自然语言处理的缩写。它被广泛用于分析文本的情感。这个想法是建立一个分类器模型来计算单词,并理解单词所代表的含义。例如,如果我输入一条推文,它应该会告诉我句子是肯定的还是否定的。显然,更细粒度的情感分类是一项更具挑战性的任务。
我已经有了一个训练有素的模型,所以我只是使用 FastText 来预测这种情况下的推文。我得到的结果是这样的,
情感分析
正如你所看到的,这些推文被分为两组——正面的和负面的。也有概率得分。得分越高,预测就越准确。至于 0.5 左右的分数,则表现出不积极也不消极的中性情绪。
我过滤掉得分低于 0.7 的推文,做个图:
使用 Tableau 可视化
如图所示,42.2%的推文对新型冠状病毒持肯定态度,而 57.8%的推文持否定态度。获得最多回复的推文往往更积极。然而,最受欢迎的推文似乎更加负面。这一结果显示了一种具有讽刺意味的情况,因为一般公众的态度显示出一种二分法而不是统一性。这解释了为什么有人一方面抗议经济重新开放,另一方面却担心医疗工作队的解散。我们正处于矛盾或不确定的境地。
新闻媒体总是用最响亮的声音告知公众。但我们知道,*多数主要参与者持有对我们的决策有深远影响的政治观点。尤其是当阴谋论搅进来,把一切都搅得水泄不通的时候——这是危机期间的典型现象。
除了战胜疾病,我们都应该尽自己的一份力量来遏制恐惧和厌恶的蔓延。如何保持头脑清醒?不要只看故事的一面,要多听一些声音。当我们阅读新闻时,我们应该采取批判性的做法和更负责任的态度,这样我们就永远不会在数百万人面前犯类似于"围巾比口罩更好"的错误。最重要的是,我们不会指责而是团结起来共同治愈。
原载于 2020 年 6 月 12 日http://www . data extraction . io。
使用 Tensorflow.js 进行 Twitter 情感分析
使用 NLP 技术来了解人们是如何谈论你的品牌的
来源:bensonruan.com
情感分析是分析一篇在线文章(社交媒体帖子、评论)是正面、负面还是中性的过程。在过去的几年中,情感分析领域取得了重*进展,这种技术已经广泛应用于商业和政治领域。
在本文中,我们将连接到 Twitter API,通过 hashtag 收集推文,计算每条推文的情感,并构建一个实时仪表板来显示结果。下面是这个应用程序的最终结果,你可以键入你的品牌名称或你感兴趣的东西,看看人们在 Twitter 上是如何谈论它的。
点击下面的链接亲自尝试一下:
[## 使用 Tensorflow.js - Benson 技术进行 Twitter 情感分析
情感分析是分析一篇在线文章(社交媒体帖子、评论)是否积极的过程…
bensonruan.com](https://bensonruan.com/twitter-sentiment-analysis-with-tensorflowjs)
履行
这个工具对于企业监控和理解他们的品牌、产品或服务的社会情绪非常有用。我用 Tensorflow.js 情感模型制作了这个演示。如果你对它是如何建造的感到好奇,请跟随我,我会一步一步地告诉你。
#第一步:创建一个 Twitter 应用
由于我们希望从 Twitter 中提取推文来分析情绪,因此,我们需要在 Twitter 的开发者平台中创建一个应用程序。
- 登录/注册一个 Twitter 账户
- 去 https://developer.twitter.com/en/apps
- 单击创建应用程序
- 填写应用信息表单并创建
- 创建应用程序后,导航到密钥和令牌选项卡
- 生成消费者 API 密钥和访问令牌&访问令牌秘密
Twitter API 密钥和令牌
#第二步:从 Twitter 上获取推文
一旦您创建了 Twitter 应用程序并生成了 API 密钥和令牌,就该利用 Twitter 的搜索 API 来下拉一些与您正在搜索的标签相匹配的推文了。
PHP
为了使事情更简单,这里我引用了 Twitter API v1.1 调用的 PHP 包装器 twitter-api-php 。从上面的库下载 TwitterAPIExchange.php,用下面的代码创建一个 queryTwitter.php,确保用步骤 1 中生成的代码替换 Twitter API 键和令牌。
Javascript
然后我们编写一个 javascript 函数向上面的 php 页面传递一个请求,并检索 tweets。
每条 tweet 都是一个对象,具有 id、created_at、user…等属性,我们感兴趣的是"full_text"
属性
#第三步:加载情感模型
只需在 html 文件的<头>部分包含tfjs
和<的脚本。
<html>
<head>
<script src="[https://cdn.jsdelivr.net/npm/@tensorflow/tfjs](https://cdn.jsdelivr.net/npm/@tensorflow/tfjs)"></script>
</head>
或者您可以通过 npm 安装它,以便在 TypeScript / ES6 项目中使用
npm install @tensorflow/tfjs
为了进行情感分析,我们首先需要通过调用tf.loadLayersModel(url)
的 API 来加载预先训练好的情感模型和元数据。
该模型在 IMDB 的 25000 条电影评论上进行训练,这些评论被标记为积极或消极情绪。这个数据集是由 Python Keras 提供的,模型也是基于 imdb_cnn 示例在 Keras 中训练的。
#第四步:情绪分析推文
对于每条推文,我们调用 Tensorflow.js 中的model.predict(input)
API。这将对每条推文文本执行情感分析,返回 0 到 1 之间的存储,这表明它是中性的、积极的还是消极的。
#步骤 5:在表格和图表中显示结果
现在我们有了推文的情感结果,为了让它看起来更好,更容易捕捉信息,我们把它放在一个表格和一个饼状图中。
现在我们有了推文的情感结果,为了让它看起来更好,更容易捕捉信息,我们把它放在一个表格和一个饼状图中。
对于饼图,我使用的是 jquery 图表库 canvasjs
当按下回车键或点击搜索按钮时,上述函数被调用
这就是代码,恭喜你,你已经建立了你的 Twitter 情绪分析应用。
GitHub 知识库
您可以通过下面的链接下载上述演示的完整代码:
使用 npm 情绪模块的 Twitter 情绪分析连接到 Twitter API,通过标签收集推文,计算…
github.com](https://github.com/bensonruan/Sentiment-Analysis)
在 Unsplash 上由 Carlos Muza 拍摄的照片
结论
仅使用 Tensorflow.js 情感 CNN 模型,这是一种简单的情感分析方法,但无法达到高准确率,约 70%。为了提高准确性,您可以研究更复杂的模型,如 LSTM。但是,我仍然认为这是一个很好的和方便的模型,并且在某种程度上表明了情绪是积极的还是消极的。
感谢您的阅读。如果你喜欢这篇文章,请在脸书或推特上分享。如果你有任何问题,请在评论中告诉我。在 GitHub 和 Linkedin 上关注我。
R 中的 Twitter 文本分析
实践教程
帮助通信从业者通过 Twitter 等开放数据源获得可操作的见解
开放数据源是数据科学家或分析师的最佳礼物之一,因为它们允许他们免费获得有价值的见解,而不必担心数据许可证。Twitter 是世界上最受欢迎的社交媒体应用之一,因为它是免费的,并且允许用户就他们想到的任何话题发表推文。本文将关注我们如何通过 R 编程使用 Twitter 来提取有价值的见解,并使用 Tableau 将这些发现传达给相关的利益相关者。
问题陈述
“我们如何帮助传播从业者从 Twitter 获得可行的见解,以便他们能够创造更有效的沟通,满足公众的需求和关注”
本问题陈述选择的目标用户是军事通信从业者,他们渴望了解公众对新加坡军队(即国民兵役)的担忧。
Twitter 数据源的特征
对于每个数据源,我们可以用*数据的四个 V做一个简单的表示,主要是量、速度、多样性和准确性,以便对该数据源有一个总体的了解。
- 体积 —数据的规模
- 速度 —流数据分析
- 多样性 —不同形式的数据
- 准确性 —数据的不确定性
按作者分类的图片 Twitter 数据中的 4 个 V
连接到 Twitter API
我们可以在 R 编程中使用twitteR
库包来访问 Twitter API。请注意,我们需要注册一个 Twitter 开发者账户来访问 API,因为每个用户都将获得一组唯一的消费者密钥、消费者密钥、访问令牌和访问密钥。一旦我们建立了与 Twitter API 的连接,我们将通过说明搜索词(即"国服")、最*推文数量(即 n = 1000 )、搜索半径为英里的新加坡经纬度(即地理编码= '1.3521,103.8198,279 米')和语言(即【T11
# import necessary library for Twitter API, data manipulation and text cleaning
library("twitteR")
library("ROAuth")
library("dplyr")
library("tidytext")# Set up Twitter Connection
consumer_key <- '' # removed due to confidentiality
consumer_secret<- '' # removed due to confidentiality
access_token <- '' # removed due to confidentiality
access_secret <- '' # removed due to confidentiality
setup_twitter_oauth(consumer_key ,consumer_secret,access_token ,access_secret)# extract english tweets using 'National Service' tweeted in Singapore, with retweets removed
tweets <- strip_retweets(searchTwitter('National Service',n=1000, geocode='1.3521,103.8198,279mi',lang='en'))# print length of tweets
length(tweets)
请注意,对于 Twitter 公共 API 的免费版本,我们只能请求过去 7 天的推文。结果,我们的请求中只有 17 条推文。
数据清理
当我们通过 Twitter API 提取推文时,结果不会很清晰,因为推文可能使用多种语言,还包含像表情符号这样不可读的单词。看下面的截图,要采取的明显的数据清理步骤是删除 Twitter 用户名,删除表情符号(由表示),并删除推文的 URL 链接(以蓝色突出显示)。
作者图片——通过 Twitter API 在 R
正如在上面的数据特征部分提到的,返回的 tweets 是 JSON 格式的,我们必须先将其转换成 R 格式的数据帧,然后才能使用像dplyr
这样的通用数据处理库。在我们的分析中,我们不希望任何标点符号、数字或长度小于 2 的单词。因此,在我们的文本清理代码中,我们将删除它们。
# convert tweets to df
df <- twListToDF(tweets)# text cleaning
df$text <- as.character(df$text)
df$text <- gsub("\\$", "", df$text)
df$text <- gsub("@\\w+", "", df$text)
df$text <- gsub("[[:punct:]]","", df$text)
df$text <- gsub("http\\w+", "", df$text)
df$text <- gsub("[ |\t]{2,}", "", df$text)
df$text <- gsub("^ ", "", df$text)
df$text <- gsub(" $", "", df$text)
df$text <- gsub("RT","",df$text)
df$text <- gsub("href", "", df$text)
df$text <- gsub("([0-9])","", df$text)
字频率
在每个文本分析问题中,将*块文本分解成单个单词(或标记)本质上是要采取的第一步,我们称之为标记化,可以使用tidytext
库来完成。对于我们的分析,一旦我们将文本分解成单独的令牌,我们将合计每个唯一单词的计数,以便我们可以知道用户在推特上发布的一些常见单词。
# split the text into individual tokens, remove stopwords,'National Service', sort by descending count
tokens <- data_frame(text = df$text) %>%
unnest_tokens(word, text) %>%
anti_join(stop_words) %>%
count(word, sort = TRUE)# output to csv for further usage
write.csv(tokens, './bukitmerah.csv',row.names = FALSE)
按作者分类的图像—标记及其频率的 csv 输出
如上所述,我们已经在一个 CSV 文件中输出了包含单个单词/单词及其频率的结果,稍后我们将把这些结果加载到 Tableau 中以实现可视化。
情感分析
我们可以对推文进行的另一种可能的分析是情绪分析,即对数据中的情绪进行解释和分类。这种情绪分析可以在 R 中使用nrc
词典来容易地进行,该词典将每个单词/标记分类为 10 种情绪类别中的一种,主要是愤怒、期待、厌恶、恐惧、快乐、消极、积极、悲伤、惊讶、信任。然后会进行汇总,对积极情绪和所有消极情绪进行分类。每一个正面单词将被加上分数 +1 ,每一个负面单词将被加上分数 -1 。一旦完成,每条推文的情感得分将被合并,如果得分高于 0 的,该推文将被归类为正面。如果是低于 0 的,则为负。如果分数正好为 0** ,则中立。**
# get sentiment score for each tweets by using the sentiment lexicon "nrc"
text_df <- data_frame(id=df$id, text = df$text) %>%
unnest_tokens(word, text) %>%
anti_join(stop_words) %>%
inner_join(get_sentiments("nrc")) %>%
mutate(score = ifelse(sentiment=='positive',1,ifelse(sentiment=='joy',1,ifelse(sentiment=='anticipation',1,ifelse(sentiment=='trust',1,ifelse(sentiment=='surprise',1,-1)))))) %>%
group_by(id) %>%
summarise(total_score = sum(score)) %>%
mutate(sentiment = ifelse(total_score>0,'positive',ifelse(total_score<0,'negative','neutral')))# get the dataframe which contains tweet message, id and it's sentiment
sentiments <- df %>% inner_join(text_df, by='id') %>% select('text','id','created','sentiment')
# output to csv for further usage
write.csv(sentiments, './sentiments.csv',row.names = FALSE)
按作者分类的图片—推文及其观点的 csv 输出
如上所述,我们已经在一个 CSV 文件中输出了包含整个 tweet 及其情感评分(即正面/负面/中性)的结果,我们稍后会将该结果加载到 Tableau 中以供可视化之用。
调查的结果
在前面的章节中,我们已经将两个文件输出为 CSV 格式,我们将把它们加载到 Tableau 中,通过数据可视化来发现可能的见解。
使用包含单个单词/令牌的第一个文件以及所有用户在其推文中的推文频率,我们可以在 Tableau 中绘制一个单词云,单词越*表示频率越高。
按作者分类的图片—词频的 Tableau 词云
从单词 cloud 中,我们可以识别出一些常用词,如、【线程】、【强制】、【志愿者】、【二月】、、【美国军团】、,这些词在军事背景下没有太多含义。
让我们看看是否可以通过包含推文及其情感评分的第二个 CSV 文件发现更多有用的见解。由于正在发推文的时间也被捕获,我们可以绘制一个随时间变化的条形图,以查看一周内人们对国家服务的情绪(请记住,Twitter 公共 API 只允许长达 7 天的数据请求)。
按作者分类的图片-情绪分析的 Tableau 条形图
在 17 条相关推文中,10 条是正面的,5 条是负面的,2 条是中性的。我们还观察到,情绪在一周内朝着消极的一面发展。
缺点和限制
在使用 Twitter 公共 API 从普通公众收集关于国家服务的见解的分析中,可以发现三个主要缺点。
- Twitter API
免费帐户仅允许从 API 中提取过去 7 天的推文
超过 7 天的数据不能提供公众对国民兵役观点的全面分析
无法提供对用户人口统计数据的深入分析
2。情感分析的准确性
应该进行更深入的情感分析,因为当前的软件不考虑整个推文的情感,导致推文根据几个词被归类为错误的情感
不恰当的词语或言论无法过滤
3。无关的推文
在其他国家的“国家服务”上无关的推文可能会被捕获,因为 Twitter 对每个人开放,无论他们在哪里,都可以发布任何推文
摘要
在本文中,我们讨论了如何使用 Twitter 这样的开放数据源,从公众那里收集特定利益相关者感兴趣的任何问题的宝贵见解,在我们的案例中,这些利益相关者是军队/国家服务的通信从业者。我们还讨论了使用 R 中内置的情感分析包来解释文本中包含的情感。在数据科学领域的*部分时间里,数据科学家经常构建他们的情感分析模型,因为每个问题都需要不同的模型来获得更合适和准确的情感。希望你们现在有一个想法,我们可以考虑如何使用 Twitter API 和内置的情感分析库来启动你的文本挖掘问题!
Twitter 主题建模
使用机器学*( Gensim 线性判别分析 — LDA)来探索你的追随者最关注的话题是什么。
经过 24 轮超参数调整后,从 LDA 基本模型到模型 6.3
我是一个机器学*极客,我想把机器学*应用到我能做的一切事情上,只是为了看看结果。另一方面,我在 SoMe(社交媒体管理平台)开始了一个新的角色,在数据科学团队中,我们不断围绕使用机器学*来为我们的用户创造更多价值,并帮助他们扩*粉丝群。我们想了很久,想出了最好的主意,在这个过程中,我们意识到第一步是让用户对他们的粉丝群有一个整体的了解。
有许多数据科学和机器学*技术可以用来更好地了解你的粉丝,从在 Instagram 上应用 CNN(卷积神经网络)到在 Twitter、Linkedin 或任何其他基于文本的数据上应用自然语言处理技术。我们的*多数用户使用 SoMe 来排序和安排他们未来在 Twitter 上的帖子,所以作为第一步,我们决定向我们的用户提供关于他们的 Twitter 追随者最关注的话题的见解。要做到这一点,我们首先必须定义和记录追随者的参与度。经过长时间的讨论,我们将关注者参与度定义和评分为“当一个关注者与用户发布的内容互动 1。转发(5/5) — 2。转发评论(4/5) — 3。喜欢(3/5) —4。评论(2/5) — 5。提及(1/5)”。
下一步实际上是从 Twitter api 获取数据,我将在未来的帖子中写申请 Twitter api 并使用 Tweepy 和其他工具来提取数据和提炼数据,以获得您需要的 api 数据。您可以对任何类型的文本数据使用以下技术,并找出数据集中讨论的最重要的主题。
加载所需的包
根据您选择的 python notebook,您将需要安装和加载以下包来执行主题建模。在一些数据科学团队中,我们使用各种笔记本电脑选项,从 Azure 到 Jupyter labs 和 notebook。然而,我个人最喜欢的是 Google Colab。我建议在 Google Colab 或 Jupyter notebook 上运行以下所有代码片段。我还将链接到这个项目的 Github 库,以及一个链接到我们用于参考的最终笔记本。
数据清理
在从 Twitter api 中提取和提炼数据并导入所需的包之后,我们必须从表情符号和 URL 中清理数据,以便我们可以在接下来的步骤中对其进行标记。您可以在下面的代码片段中找到我们使用的清理语法:
正如您在下表中看到的,在进行令牌化之前,您需要删除*量表情符号和 URL。数据清理对于最终提供准确的结果至关重要,我们不希望来自网站或表情符号的单词出现在我们的主题建模结果中,因为它们在弄清楚一袋单词的一般主题方面几乎没有价值。
数据预处理
我们在数据预处理阶段的目标是将句子转换为单词,将单词转换为其词根,并删除过于常见或与主题建模项目的目的无关的单词。我们使用了以下技术来实现我们的目标,我将分享代码并带您经历每个阶段:
- 分词:将文本拆分成句子,再将句子拆分成单词。将单词小写,去掉标点符号。
- 少于 3 个字符的单词将被删除。
- 所有的停用词都被移除。
- 单词被词条化和词干化——第三人称的单词被改成第一人称,过去时态和将来时态的动词被改成现在时态,并被还原成它们的词根形式。
符号化
标记化永远是我们做任何文本数据处理之前的第一步。这意味着 spaCy 将通过对每种语言应用特定的规则来将句子分割成单词、标点符号、符号等。Spacy 是一个预先训练的自然语言处理模型,能够计算出单词之间的关系。你可以在这里了解更多关于 Spacy 的信息。
词汇化
词汇化是我们将单词转换成其词根的过程。比如:'学*'变成了'学*','见面'变成了'见面','更好','最好'变成了'好'。这样做的好处是,我们可以减少字典中唯一单词的总数。因此,文档-单词矩阵中的列数将随着列数的减少而增加。引理化的最终目的是帮助 LDA 模型最终产生更好的主题。
主题建模
基础模型
我们首先生成一个基础模型,用于在我们经历超参数调整阶段时跟踪我们的进展。LDA 主题模型算法需要文档单词矩阵和字典作为主要输入。对于运行 LDA 模型之前要采取的前几个步骤,我们创建了一个字典,过滤了极端情况,并创建了一个语料库对象,它是 LDA 模型需要作为主要输入的文档矩阵。
我们准备了训练 LDA 模型所需的一切。除了语料库和词典,我们还需要提供主题的数量。我们选择 5 作为基本模型。在超参数调整阶段,我们将达到要使用的主题的最佳数量。
通过打印出 LDA 模型产生的主题,我们可以粗略地猜测与每袋单词相关的主题。值得一提的是,这个单词包是按照与每个主题最相关到最不相关的顺序排列的。
模型困惑和主题连贯性提供了一个方便的衡量标准来判断一个给定的主题模型有多好。以我的经验来看,话题连贯性评分尤其有用。
我们的一致性得分为 0.17,这对于几乎所有 LDA 模型来说都是非常低的分数,但请记住这只是基础模型,我们将对其进行*幅改进。
既然构建了 LDA 模型,下一步就是检查生成的主题和相关的关键字。没有比 pyLDAvis package 的交互式图表更好的工具了,它的设计可以很好地与 Google Colab 和 Jupyter 笔记本配合使用。
那么如何推断 pyLDAvis 的输出呢?
左侧图中的每个气泡代表一个主题。泡沫越*,这个话题就越流行。一个好的主题模型会有相当*的、不重叠的气泡分散在整个图表中,而不是聚集在一个象限中。一个有太多主题的模型,通常会有许多重叠,小气泡聚集在图表的一个区域。
好的,如果我们将光标移到其中一个气泡上,右边的单词和条将会更新。这些词是构成所选主题的突出关键词。我们已经成功地构建了一个好看的主题模型。接下来,我们将通过使用 Scikit-learn 的网格搜索来改进该模型(超参数调整),然后我们将关注如何在 LDA 模型中获得主题和其他变量的最佳数量。
超参数调谐
网格搜索
LDA 模型最重要的调整参数是 n_components(主题数)。此外,我们还将搜索 learning_decay(控制学*速率)。除此之外,其他可能的搜索参数可以是 learning_offset(降低早期迭代的权重)。应该> 1)和 max_iter。如果你有足够的时间和计算资源,这些都是值得尝试的。
注意,网格搜索为 param_grid 字典中参数值的所有可能组合构建多个 LDA 模型。所以,这个过程会消耗*量的时间和资源。
我们知道主题的数量很有可能远远超过 10,但是通过网格搜索,我们发现 10 个主题比其他数量的主题表现得更好。这让我们对结果进行了深入思考,我们发现 Scikit-learn 的网格搜索跟踪的是困惑而不是一致性值,对于我们的用例,一致性值提供了最好的结果。下一步,我们将获得最佳数量的主题。
最佳主题数量
我们寻找最佳主题数量的方法是建立许多具有不同主题数量值的 LDA 模型,并选择一个给出最高一致性值的模型。
选择一些标志着话题连贯性快速增长结束的话题通常会提供有意义和可解释的话题。选择更高的值有时可以提供更细粒度的子主题。如果你看到相同的关键词在多个主题中重复出现,这可能是“主题数量”太*的信号。通过遵循这些原则,我们选择了 68 个主题作为我们用例的最佳主题数量。
最佳通过次数
Passes、chunksize 和 update_every 是具有 EM/variable 关系的参数。我们不打算在这里深入 EM/variable Bayes 的细节,但如果你好奇,可以看看这个谷歌论坛帖子和它引用的论文这里。对于我们的用例,考虑到 chuncksize 不是实质性的,update_every 在最终结果中不会有太*变化,超参数调整次数就足够了。
我们尝试了许多遍,20 遍似乎能产生最好的结果。虽然不是很*,只有几个小数点。
阿尔法类型
α是狄利克雷先验的超参数。狄利克雷先验是我们从中得出θ的分布。θ成为决定主题分布形状的参数。所以本质上,alpha 影响了我们绘制主题分布的方式。这就是为什么我们要尝试超参数调整,以选择最佳的阿尔法类型,给你一个更好的主题分布。
在我们的用例中,对称 alpha 提供了一个更好的结果,不是很好,但是我们将为我们的 LDA 模型使用对称 alpha。
衰退
学*衰减控制模型的学*速率。由于您只能选择 0.5、0.7 和 0.9,我们将尝试所有三个选项,看看哪个选项能提供最佳的一致性值。
在我们的使用案例中,0.5 衰减提供了最佳的相干值。事实上,在我们的用例中,衰减和一致性值似乎是负相关的。
最佳迭代次数
迭代在某种程度上是技术性的,但本质上它控制了我们对每个文档重复特定循环的频率。将“迭代”次数设置得足够高是很重要的。
在我们的用例中,我们尝试了 50 到 100 次迭代,但是因为我们想防止模型过度拟合,所以我们选择了 70 次,这表明一致性分数有了相当*的提高。
最小概率
该超参数忽略分配概率低于分配概率的主题。不能低于 0.01,也不能高于 0.1。
我们尝试了 0.01 到 0.1 的整个范围,coherence 值在整个范围内保持不变,因此我们将其保留为默认值 0.01,以获得尽可能多的主题。
进度跟踪表
从一开始就跟踪你的进步总是一个好*惯。我们已经通过 24 次模型超参数调整迭代跟踪了我们的进展,我们的最佳模型提供了 0.47 的一致性值,考虑到用户追随者群体中可以讨论的广泛主题,这是一个非常好的数字。
最终模型和结果
我们终于完成了我们的最终模型。我们目前使用下面片段中的模型在 SoMe 向我们的用户提供他们的追随者互动最多的话题。
如果我们仔细看看最终模型产生的主题,我们可以以很高的准确度猜测每个主题是什么。现在我们必须记住,它们是按重要性从高到低排序的。例如,如果我们看第一个主题,我们认为它是关于新冠肺炎危机的,因为前三个词是“防病毒封锁”,而不是关于“黄金”,因为“黄金”是单词包中第四个没有反映高概率的单词。
正如我们之前所介绍的,一个好的主题模型将为每个主题提供非重叠的、相当*的 blob,如果我们根据用例增加主题的数量,我们将拥有更小的 blob,并且在某些情况下会有一些重叠,但总的来说,我们可以在最终的模型中看到,分布和重叠有了显著的改善。这里好像是这样的。所以,我们没事了。
结论
在这篇文章中,我们讨论了一些前沿的主题建模方法。我们不断努力为用户构建新功能,并利用机器学*的力量为用户提供更好的体验。为了改进这个模型,你可以通过使用 gensim LDA Mallet 来探索修改它,这在某些情况下会提供更准确的结果。对于那些在构建主题模型时关心时间、内存消耗和主题多样性的人,请查看 LDA 上的 gensim 教程。
最后,我必须感谢数据科学团队的其他成员,尤其是雅各布·帕吉特和劳伦斯·金姆西。在某些情况下,我们坚信开源软件,您可以在这里找到我们所有的分析模型笔记本。
使用 Python 进行 Twitter 趋势分析
让我们尝试使用 Python 和 twitter API 来理解世界各地的 Twitter 趋势。
使用 Canva 设计
Twitter 于 2006 年推出,是当今最受欢迎的社交媒体平台之一。它有助于洞察流行趋势和重要的文化和政治时刻。在数据科学行业,twitter 分析可以用于市场营销或产品分析等任务。Twitter 数据已被用来分析政治极化和抗议运动的蔓延。在本文中,我们将了解收集 twitter 数据、处理 twitter 文本和绘制 twitter 数据地理地图的过程。我们将处理包含关键字 #python 和 #javascript 的数据子集。
收集数据
在收集数据时,我们受到两方面的限制。
1。无法收集过去(一年前等)的数据。)
2。Twitter 只免费提供其数据样本(比如其数据的 1%)。
然而,1%的数据样本相当于每天几百万条推文。许多社交媒体公司都有可供第三方开发者和研究人员使用的 API。Twitter 有许多可用的 API。
根据 tweepy docs,twitter 流 API 用于实时下载 Twitter 消息。这对于获取*量的 tweets,或者使用站点流或用户流创建实时提要非常有用。它有两个端点过滤器和样本。使用过滤器端点,用户可以使用几百个关键字、几千个用户名和 25 个位置范围请求数据。使用样本端点,twitter 将返回所有 twitter 数据的 1 %。为了从流式 API 收集数据,我们将使用一个名为tweepy
的包,它抽象了建立稳定的流式 API 连接的工作。我们需要有自己的 twitter 账户和 API 密钥进行认证。
tweepy
需要一个名为 SListener 的对象,告诉它如何处理传入的数据。这个 SListener 对象打开一个新的时间戳文件来存储 tweets,并带有一个可选的 API 参数。下面的代码将一直运行,直到显式停止(执行时间越长,文件中 Twitter JSON 对象的数量就越多)。本文中的数据集由 685 条 tweets(Twitter JSON 对象)组成,这些 tweet 是从 IST 时间晚上 7:39 到 IST 时间晚上 7:44 获取的。
现在一个名为tweets.json
的文件将包含 JSON 对象。JSON 对象的数量取决于上述连接的打开和关闭。
了解 Twitter JSON
数据格式是一种特殊的数据格式,便于人们阅读,并且易于在机器之间传输。它是字典和列表的结合。每个 JSON 对象都有许多子对象。在我们的例子中,主 JSON 对象描述了主 tweet、favourites_count、retweet_count 等。并且具有描述用户、位置等的嵌套字典。
处理数据
为了*规模分析推文,最好将这些推文存储在熊猫数据帧中。这使我们能够跨行和列应用分析方法。然而,对于嵌套字典,JSON 对象是复杂的。为了克服这个问题,我们将把 JSON 对象扁平化(将所有属性保持在一个层次上,而不是嵌套)。
让我们将 tweets 列表加载到一个数据帧中。
df_tweet = pd.DataFrame(tweets)
现在让我们比较一下拥有#python 和#javascript 的推文数量。让我们编写一个函数,它将检查所有文本列中给定的 hashtag,并返回一系列布尔值,指示每一行是否有该关键字。
我们可以注意到, #python 略微领先于 #javascript 。现在,让我们试着了解一下上述两个关键词的提及率是如何随时间变化的。关于产品和公司的推文变化很*。让我们试着捕捉随时间的变化。当数据标有日期和时间时,它被称为时间序列数据。
首先让我们将created_at
列转换为 DateTime 类型。我们需要创建一个可以随时间变化的指标。让我们创建由布尔值组成的两列python
和js
。
现在让我们创建两个标签每分钟的平均提及次数,并绘制它们的时间图。我们将使用一系列方法resample()
,这将允许我们在我们选择的时间窗口内进行汇总,并对其应用聚合函数。
resample(“1 min”).mean()
将按分钟对数据进行分组,并对分组后的数据应用均值函数。
正如我已经提到的,我们从下午 7:39 到 7:44 获取数据,我们可以注意到分钟轴的值从 39 到 44,我们可以注意到频率如何随时间变化。
情感分析
情感分析是一种从文本中获取意义的方法。它是一种自然语言处理方法,用来判断一个单词、句子、段落、文档是肯定的还是否定的。基于正面和负面单词的数量,给每个文档一个正面或负面的分数。这可以用来分析对产品、公司等的反应。
我们将使用自然语言工具包或nltk
中包含的VADER感知语义分析器。这对于分析短文档特别是推文很有用。也考虑表情符号和单词的*小写。
来自 VADER 分析器的每个情感分数提供 4 个值。阴性、阳性、中性和复合。前 3 个是不言自明的,范围在 0 到 1 之间,而复合值的范围在-1 到 1 之间。越接近 1 表示正,越接近-1 表示负。我们将再次对每分钟的数据进行重新采样,并找到每分钟的情感分数。
我们可以注意到,在我们拥有的一小部分数据中,没有负面的推文,而与 python 相比,javascript 有更多正面的推文。
根据我们收集的数据,我们可以按天、按月对数据进行采样,并执行类似的分析,以获得更多的见解。
完整的代码可以在这里找到。
结论
在本文中,我们学*了如何从 API 获取信息,还学*了如何处理 JSON 数据并对其进行时序分析。我没有把重点放在情感分析的内部解释上,这将在我以后的文章中讨论。作为即将到来的网络分析系列的一部分,我将使用图数据结构来说明 Twitter 数据的网络分析。
感谢阅读,并随时分享反馈!呆在家里,注意安全。
Twitter 美国航空公司情绪分析
用 Lightgbm 分类器进行反馈分析。
Szabo Viktor 在 Unsplash 上拍摄的照片
这里的目标是分析旅行者如何在 2015 年 2 月在 Twitter 上提及他们的感受。对于航空公司来说,利用这些免费数据为客户提供更好的服务将是非常有趣的。这个数据集可以从这里下载。
怎么分析呢?
我已经用 Python 上传了保存在本地目录中的数据:
tweets = pd.read_csv('Tweets.csv')
让我们看看数据集中包含的要素:
tweets.head()
我们在这里寻找的是名为“航空公司情绪”的列,以及我们如何根据旅行者的推文预测它。这叫做情感分析。
为了更好地了解观察结果和特征,我们可以运行以下命令,它将为我们提供每个特征的特征。
tweets.info()
让我们把表达出来的感觉想象成消极的、中性的和积极的。
plt.figure(figsize=(3,5))
sns.countplot(tweets['airline_sentiment'], order =tweets.airline_sentiment.value_counts().index,palette= 'plasma')
plt.show()
*多数人持否定态度,如果航空公司能提供适当的回应,这将是一个很好的/免费的反馈。我们还可以展示对每家航空公司的看法。
g = sns.FacetGrid(tweets, col=”airline”, col_wrap=3, height=5, aspect =0.7) g = g.map(sns.countplot, “airline_sentiment”,order =tweets.airline_sentiment.value_counts().index, palette=’plasma’) plt.show()
为了进行情感分析,我们需要导入一些库。由于这是一个分类问题,所以我使用 LGBMClassifier。
**from** **lightgbm** **import** LGBMClassifier
我们需要将这些推文(文本)转换成令牌计数矩阵。
**from** **sklearn.feature_extraction.text** **import** CountVectorizer
下一步是使用 tf-idf 表示来规范化计数矩阵。
**from** **sklearn.feature_extraction.text** **import** TfidfTransformer
我使用管道功能来完成所有步骤。
twitter_sentiment = Pipeline([('CVec', CountVectorizer(CountVectorizer(stop_words='english'))),
('Tfidf', TfidfTransformer()),
('norm', Normalizer()),
('tSVD', TruncatedSVD(n_components=100)),
('lgb', LGBMClassifier(n_jobs=-1))])
最后,CROSS_VALIDATE 与 ROC_AUC 指标一起使用。
%%time
cv_pred = cross_validate(twitter_sentiment,
tweets[‘text’],
tweets[‘airline_sentiment’],
cv=5,
scoring=(‘roc_auc_ovr’))
我们使用 ROC_AUS 测量的结果如下。
完整的代码可以通过这个链接访问。
展示项目成果的两个令人惊叹的图表(静态与交互)
在 Unsplash 上由 Adeolu Eletu 拍摄的照片
分享你的项目成果总是令人兴奋的,但更令人兴奋的是看到突出你成就的结果。选择正确的图表来展示您的项目结果可能会很棘手。可视化应该简单、全面、切中要点,当然,最好是用一种新的设计来替代条形图、饼图、折线图和散点图。
使用 Tableau 或 Power BI 软件,创建可视化的过程不需要编码来生成各种图形。但是,如果你没有这两个工具,你有什么选择?另一种选择是使用免费资源。
在非营利部门工作多年后,我发现 ggplot2 和 plotly 可以成为有用的工具。我经常要向捐赠者展示结果,我发现斜率和哑铃图非常有效。这些图表显示了两个时间段之间以及组内的比较。
这里有一个例子来说明它是如何工作的。我正在使用 希望之翼 项目的成果,该项目旨在消除处于危险中的妇女遭受的暴力。该项目为制定安全计划提供了干预措施,随后是前后测量。
我在用哑铃图可视化项目干预前后的数据。我的目的是展示服务价格在项目开始和结束时的不同。首先,我将创建一个数据集,然后使用 geom_dumbell 函数来绘制我的结果。在我的数据集中,我有以下服务名称(法律服务、HIV/STD 咨询、心理健康、自助团体和宗教咨询)、显示项目受益人使用这些类型服务频率的基线调查结果(14、5、15、7、37),以及关于这些数字如何变化的 3 个月跟踪调查结果(29、13、29、16、31)。我还计划添加关于差异的列,因为我也想在图中使用它们。要理解 geom _ dumbling 函数的逻辑,我建议你查阅一下关于 ggalt 包 的文档。
这是我得到的情节。它具有之前/之后的比较,并且在右侧,它以百分比的形式说明了变化。
用同样的数据,我正在用 plotly 制作交互式哑铃图。
单击下面的输出,在单独的窗口中打开它:
[## 希望之翼:服务利用率|阿普加切夫制作的折线图| plotly
阿普加切夫的“希望之翼:服务利用率”的交互图和数据是一个折线图,显示了之前与…
chart-studio.plotly.com](https://chart-studio.plotly.com/~apugachev/7.embed)
斜率表可以作为哑铃的替代品。显示两个时间点之间的变化方向令人印象深刻。我再次使用希望之翼项目的成果,但现在我的意图也是为了表明方向。我想展示我的项目指标是如何变化的。在我的例子中,我指的是暴力程度,暴力程度的降低证明了项目的有效性。首先,我将创建数据集并使用允许我用线连接两点的 geom_segment 函数。在我的情况下,两点将代表基线和 3 个月的随访。
通过使用 ggplotly 函数,还有一种方法可以将 ggplot2 输出更改为 plotly 输出。请注意,这并不适用于所有图表。
单击下面的输出,在单独的窗口中打开它:
[## 亲密伴侣暴力(IPV)和基于性别的暴力(GBV)的发生率|由…制作的折线图
阿普加切夫关于“亲密伴侣暴力(IPV)和基于性别的暴力(GBV)的流行程度”的互动图表和数据…
chart-studio.plotly.com](https://chart-studio.plotly.com/~apugachev/9.embed)
总而言之,如果你的目标是展示项目的影响,考虑斜率或哑铃图是值得的。上面的例子展示了如何使用 ggplot2 和 plotly 从头开始创建静态和交互式可视化。这些图表可以为您关于项目影响的讨论增加价值。它们是不言自明的,并且准确地展示了观众需要了解的内容。
[1]希望之翼项目,吉尔吉斯斯坦,2014–2016 年
[2]鲍勃·鲁迪斯(2017 年 2 月 15 日),包“gg alt”【https://cran.r-project.org/web/packages/ggalt/ggalt.pdf】T2
[3]Hadley Wickham,线段与曲线https://gg plot 2 . tidy verse . org/reference/geom _ segment . html # details
K-均值聚类的两个挑战
如何“明智地”选择 k 和初始质心
K-means 聚类算法旨在将数据划分为 k 个聚类,使得同一聚类中的数据点相似,而不同聚类中的数据点相距较远。两点的相似性是由它们之间的距离决定的。在这篇文章中,我将讨论在使用 k-means 聚类时需要记住的两个关键点。如果您不熟悉 k-means 聚类,您可能希望先阅读 k-means 算法的详细说明。
详细的理论解释和 scikit-learn 实现
towardsdatascience.com](/k-means-clustering-explained-4528df86a120)
为了充分利用 k-means 聚类算法,需要明智地应对两个挑战:
- 定义聚类的数量
- 确定初始质心
定义集群数量
在运行 k-means 聚类算法之前,我们需要声明聚类的数量。它不能确定聚类的最佳数量。K-means 将数据集划分为我们预先确定的聚类数。对于我们来说,找到最佳的集群数量也是一项具有挑战性的任务。我们不能只看数据集就找出我们应该有多少分区。
K-means 聚类试图最小化聚类内的距离。该距离被定义为聚类内距离和(WCSS)。
让我们看看 WCSS 随着不同的集群数量而变化。假设我们有以下非常简单的数据集:
让我们现实一点,有一个集群:
如果我们有一个集群,质心将是红色方块。WCSS 计算如下:
每个数据点和平均值之间的平方距离之和(红色方块)。随着聚类数量的增加,数据点和质心之间的平均距离减小,因此 WCSS 减小。
让我们看看两个集群的情况:
正如你所看到的,数据点和质心之间的平均距离减小了。请记住,距离的数量与集群的数量无关。无论存在多少个聚类,我们都将计算 n 个距离,其中 n 是数据点的数量。所以只能着眼于平均距离。
WCSS 在 3 个集群中进一步下降:
到目前为止,我们已经看到了 1、2 和 3 簇的 WCSS,其计算如下:
- k=1 > WCSS = 35,16 单位
- k=2 > WCSS = 20,91 单位
- k=3 > WCSS = 2,95 单位
WCSS 还会下降多远?嗯,最终会是零。我们可以拥有的最*聚类数等于数据点的数量。虽然没有用,但是我们可以为每个数据点建立一个单独的集群。那么数据点和它的质心之间的距离变为零,因为数据点的质心就是它本身。
那么,为什么不对每个数据点进行聚类,并使 WCSS 为零呢?因为,在这种情况下,我们将有一个更*的问题,即过度拟合。过了一段时间后,我们将通过增加集群的数量来获得一点点 WCSS。下图显示了 WCSS 随着集群数量的增加而发生的变化。
正如你所看到的,在某一点之后,WCSS 并没有减少多少。我们应该寻找斜率急剧变化锐边。红色方块标记的边是我们的最佳聚类数。
如果我们选择*于该点的 k,WCSS 仍会降低,但不值得冒过度拟合的风险。
确定初始质心
K-means 是一个迭代过程。它建立在期望最*化算法的基础上。确定集群数量后,它通过执行以下步骤来工作:
- 为每个簇随机选择质心(簇的中心)。
- 计算所有数据点到质心的距离。
- 将数据点分配给最近的聚类。
- 通过取聚类中所有数据点的平均值,找到每个聚类的新质心。
- 重复步骤 2、3 和 4,直到所有点收敛并且聚类中心停止移动。
我们现在专注于第一步。根据数据集的底层结构,不同的初始质心可能最终形成不同的聚类。除此之外,完全随机选择质心可能会增加运行时间,因此算法需要更多的时间来收敛。
我们可能想寻找一种聪明的方法,而不是以完全随机的方式选择初始质心。那个聪明的办法就是 k-means++ 。
K-means++确保了一种更智能的方式来初始化集群。如维基百科所述,
k -means++ 是一种为 k -means 聚类算法选择初始值(或“种子”)的算法。
k-means 和 k-means++的区别只是选择初始质心。剩下的步骤完全相同。K-means++从数据集中的数据点随机均匀地选择第一个质心。每个随后的质心从剩余的数据点中选择,其概率与其到该点最近的现有质心的距离的平方成比例。
幸运的是,在 scikit-learn 中实现了 k-means++。由 sklearn.cluster.KMean s 的“ init 参数指定,默认值为 k-means++。init 参数的另一个选项是“random ”,它随机初始化质心。
感谢您的阅读。如果您有任何反馈,请告诉我。
参考文献
偏差和方差:改进每个模型的两个重要的机器学*概念
克里斯蒂娜·特里普科维奇在 Unsplash 上的照片
了解偏差和方差如何提高模型的准确性
如果你不明白自己做得对或错,那么训练任何新模型都是困难的。*多数时候,模型是黑匣子,它吸入数据,吐出精确的数字。理解为什么你的模型表现不佳是知道你如何改进它的关键。
- 通过识别偏差和方差,了解为什么你的模型表现不佳。
- 了解如何通过减少偏差和方差来改进您的模型。
识别偏差和差异
先说误差。误差是你的模型在测试数据上有多不准确。
如果你的模型在测试集上达到 86%的准确率,那么就有 14%的误差。这种误差一部分是偏差,一部分是方差。
上图两个要点:
1。偏差是训练集
2 的误差。方差是训练和测试准确度之间的差距
偏见
偏差描述了模型从训练数据中学*的能力。较*的偏差意味着模型很难从训练数据中学*。
如果模型对训练数据有 90%的准确度,那么模型有 10%的偏差。这种偏见有些是可以避免的,有些是不可避免的。
不可避免与可避免的偏差
不可避免的偏差被称为最优误差率。这是模型性能的上限。它认识到,一些任务,如字幕或股票预测,是不可能 100%准确预测的,即使对人类来说也是如此。因此,我们可以预期,即使在一个完美的世界中,我们的模型至少在某些时候是错误的。
如果你决定你的模型至少有 4%的时间是错误的,那么这个模型就有 4%不可避免的偏差。
可避免偏差是最优错误率与训练误差之差。这是我们可以尝试减少的误差,以实现最佳误差率。
差异
方差描述了您的模型对其尚未见过的数据的泛化能力。我们将方差定义为训练精度和测试精度之间的差异。
偏差与方差的权衡
*多数用来减少偏差或方差的方法都是以牺牲一个为代价来减少另一个。有一些例外,但是*多数时候构建最佳模型意味着最小化偏差和方差。
减少偏差和差异
减少可避免的偏差
- 增加模型尺寸 增加模型尺寸是减少可避免偏差的一种方法。
模型越*,需要调整的参数越多。更多的参数允许模型学*更复杂的关系。您可以通过向模型添加更多的层或节点来增加模型的*小。模型从数据中学*得越好,就越接近最佳错误率。
- 减少规则 减少模型的规则允许模型更好地拟合训练数据。然而,较少的正则化意味着您的模型不会同样概化,从而增加了方差。这是偏倚与方差权衡的经典例子。
- 更改模型架构 更改模型架构可以帮助它更好地适应数据集。这类似于增加模型的*小,但是有更多的自由度。您可以更改以下任何内容,但要谨慎。
- 这些技术可以改变偏差和方差。
1。层激活函数(tanh,relu,sigmoid,…)
2。模型正在学*什么(安,CNN,RNN,KNN…)
3。模型是如何学*的(Adam,SGD,RMSprop,…)
4。更改其他超参数(学*率、图像*小等)
- 添加新特征 向训练数据添加新特征可以向模型提供更多信息,模型可以利用这些信息进行学*。这可以通过称为特征工程的过程来完成。在此过程中,您还可以将在开发早期剪切的功能添加回去。
减少差异
- 添加更多的数据 添加更多的数据是最简单的方法,几乎总是,增加你的模型的性能。添加更多数据的效果可以在 Andrej Karpathy 的文章Un reasonable Effectiveness of Data中看到。此通常不会影响偏差,因此是减少方差的首选方法。
- 增加正则化 增加正则化可以防止模型在数据上过度拟合。虽然这减少了方差,但总会增加偏差。除了减少方差之外,加入正则化还可以产生显著的积极影响。我最喜欢的是使用 drop out 来实现蒙特卡罗辍学。
- 减小模型尺寸 减小模型尺寸有助于减少训练数据的过度拟合。尽管这种技术是最简单的,但它降低了模型学*数据集复杂模式的能力。通过添加正则化通常可以看到相同的结果,因此该方法是更优选的。
- 特征选择 通过删除不需要的特征来减少数据集的维数是减少模型方差的一个好方法。你可以用主成分分析(PCA) 过滤掉特征或者组合成几个主成分。
完整的画面
将所有这些放在一起,你应该能够识别偏差和方差,并知道如何减少它。
摘要备忘单
减少偏差
- 增加模型尺寸
- 减少正规化
- 改变模型架构
- 添加功能
减少方差
- 添加更多数据
- 减小模型尺寸
- 添加正规化
- 功能选择
资源
所有这些概念以及更多内容都包含在吴恩达的《机器学*的渴望》一书中。它可以免费阅读、打印和分发。我强烈建议你去看看。
所有的图表都是作者创作的:
阅读米基安·穆瑟在媒体上的文章。数据科学家 https://mm909.github.io/Mikian/.·天天、米基安·穆塞尔和…
medium.com](https://medium.com/@mikianmusser)
为了在 Python 中轻松操作数据,您必须知道两个熊猫函数
掌握这些 pandas 函数(和方法)可以缩短代码,提高性能,避免麻烦。
熊猫——其中一只与众不同(右上图由斯坦·w .在 Unsplash 上拍摄)
我做*量的数据工作——一些 it 数据科学,一些 it 数据可视化,一些 it 数据邻近——比如摆弄体育分析。所有这些都是用 Python 完成的,而且几乎所有的数据操作都会用到 Pandas。
我喜欢认为在这段时间里,我学到了一两件关于使用熊猫的事情。我使用熊猫的方式与几年前我刚开始涉足熊猫时*不相同。我使用 Pandas 越多,我发现自己越少走出它进行数据操作。我的印象是,我的代码变得更加简洁,结果节省了*量时间。
所以在这篇文章中,我想和你们分享一些我现在离不开的功能——我相信你会发现它们和我一样有用。
是呆在室内的时候了,所以今天我使用来自 Kaggle 的网飞电影数据集。如果没有别的,你可能已经发现了一些值得看的东西:)。
在开始之前
Packages
我假设您熟悉 python。即使你相对较新,这个教程也不应该太难。
你需要pandas
和plotly
。用一个简单的pip install [PACKAGE_NAME]
安装每一个(在您的虚拟环境中)。
内容,内容无处不在
使用以下命令将网飞csv
数据文件加载到熊猫数据帧中:
nf_df = pd.read_csv('srcdata/netflix_titles.csv')
让我们用.head()
方法检查数据帧。
实际上,这引出了我的第一个建议。
在熊猫中设定显示选项
如果你像我一样,不使用 Jupyter notebook 或其变体,你可能会对 Python 外壳中熊猫有限的显示宽度感到沮丧。
这是运行nf_df.head()
时输出的样子。
这并不能告诉你太多…
没那么有用。但是等等!Pandas 有一个.set_option
功能,您可以使用它来调整要显示的列数,以及显示的总宽度。将您的参数设置为如下所示:
pd.set_option('display.max_columns', desired_cols)
pd.set_option('display.width', desired_width)
现在您的 shell 将输出更有用的东西。
更有用的输出
还有其他可以通过.set_option
功能设置的选项——如果您有兴趣,可以在这里查看文档。
。分配
你曾经看到过这条信息吗?
<input>:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
如果这从未发生在你身上,你比我更好——你可能已经通过文件或一本书正确地了解了熊猫。
而熊猫官方的解释可以在这里找到;但是 TL;dr 版本是,Pandas 警告您,无法确定您是否正在操作数据帧的副本(在这种情况下,您可能不会更改任何值)。
这也是使用.assign
的原因之一。使用.assign
,返回一个新对象,以避免由于SettingWithCopyWarning
而导致的潜在混乱,不管实际问题是否存在,该问题经常被提出。
接下来,一个更明显的原因是创建一个新的数据帧,现有的数据帧由于某种原因被保留。正如我提到的,.assign
返回一个新的对象,所以我们可以避免类似这样的问题:
第一个例子打印 13,因为我们不小心修改了原始数据帧(nf_df
),而在第二个例子中,原始数据帧保持不变,有 12 列。
现在,让我们继续讨论.apply
——这可能是熊猫最被低估的功能之一:)。
。应用
如果你和我一样,你可能尝试过在一个循环中操作熊猫数据——可能会做类似这样的事情:
for i in range(len(nf_df)):
nf_df.iloc[i, 1] = ...some function here
停下来。没必要这样。
这就是.apply
方法的作用。它沿着数据帧的轴对每个元素应用一个函数。让我们来看一些例子。
示例 1 —按发布年份统计的内容数量
我们的数据框架包括一个发布年份的列(release_year
)。分布可以绘制如下,如下所示:
fig = px.histogram(nf_df, x='release_year')
fig.show()
按年份划分的网飞图书发行数据直方图
如果我们想看十年的数据呢?简单—创建一个新列‘decade’
,如下所示:
nf_df['decade'] = nf_df.release_year.apply(lambda x: str(x)[:3]+'0s')
这里,我们通过nf_df.release_year
获取release_year
列数据,并应用 lambda 函数lambda x: str(x)[:3]+’0s’
。
Lambda 函数看起来令人困惑,但是一旦你*惯了,它就非常简单。在这里,它抓取每个元素(x
),首先应用转换str(x)[:3]
获得年份字符串的前 3 个字母,并在末尾添加’0s’
。
所以,看看我们新十年专栏的柱状图:
fig = px.histogram(nf_df, x='decade', category_orders={'decade': np.sort(nf_df.decade.unique())})
fig.show()
按十年划分的网飞图书发行数据直方图
看到这有多简单了吗?
另外,你可能已经注意到我使用了.unique()
方法。这是一个非常有用的方法,它将返回该列中唯一实体的数组——不要错过它!
好吧。我们再来看一个使用.apply
的例子。
示例 2 —按来源国统计的内容数量
DataFrame 还包括一个列(“country
”),该列(据我所知)包括内容的来源。
快速看了一下数据(nf_df[‘country’].nunique()
)发现有 555 个(!)唯一实体在country
列中。这是怎么回事?
事实证明,许多列表包含多个国家名称(例如'United States, India, South Korea, China
') —因此,让我们保留前 20 个名称,并将所有剩余的名称更改为“其他”。
用.apply
很容易做到。为了清楚起见,我将使用两行代码,但也可能只用一行。准备好了吗?
top_countries = nf_df.groupby('country')['title'].count().sort_values().index
nf_df['country'] = nf_df.country.apply(lambda x: 'Others' if (x not in top_countries[-20:]) else x)
就是这样!再次运行nf_df[‘country’].nunique()
,我们看到现在只有 21 个“国家”。
这里的 lambda 函数是一个简单的 if… else 语句,根据具有最高计数的列表检查'country
'实体。
假设每个列还包括内容类型列,我们可以通过按内容类型给每个栏着色来可视化内容数据的细分。
fig = px.histogram(nf_df, x='country', color='type', category_orders={'country': top_countries},
color_discrete_sequence=px.colors.qualitative.Safe, template='plotly_white')
fig.show()
按来源国家和类型划分的网飞标题直方图
那不是很容易吗?想象一下用一些疯狂的循环函数来做这件事——通过.loc
或.iloc
索引器找到正确的索引器行/列号。不了,谢谢你。
整洁,对不对?
仅仅通过这两个简短的函数,您就可以完成以前可能需要一行又一行代码才能完成的事情。尝试一下——我敢打赌你会感到惊喜,并提高你的工作效率。
如果你喜欢这个,比如说👋/关注 twitter ,或点击此处获取更新。ICYMI:我还写了这篇关于用 Plotly Dash 构建 web 数据仪表板的文章。
[## 使用 Python 在几分钟内构建一个 web 数据仪表板
通过将您的数据可视化转换为基于 web 的仪表板,以指数方式提高功能和可访问性…
towardsdatascience.com](/build-a-web-data-dashboard-in-just-minutes-with-python-d722076aee2b)
这篇文章也是关于将数据可视化来讲故事的:
使用 Python 和 Plotly 构建清晰易读的时序数据可视化,以支持您的叙述。
towardsdatascience.com](/effectively-visualize-data-across-time-to-tell-better-stories-2a2c276e031e)
对 Python 字典进行排序的两种简单方法
以及 sort()和 sorted()的区别
字典是一种重要的数据结构,它通过将键与值进行映射来存储数据。Python 中默认的字典是无序的数据结构。像列表一样,我们可以使用 sorted()函数按键对字典进行排序。然而,它只会返回一个排序的键列表,这通常不是我们想要的。我们可能希望它按值而不是键排序,或者我们可能希望它返回一个排序的字典而不是一个列表。在本文中,我将讨论两种简单的方法,我们可以用它们来对 Python 字典进行排序并返回您想要的内容。
排序列表
既然我们在讨论排序,我想首先指出两个排序函数之间的一个非常重要的区别。让我们在列表中显示不同之处。假设我们有两个列表:
我们有两种对列表进行排序的方法,一种是使用 sort()进行就地排序,另一种方法是使用 sorted(),它不是就地排序。不同的是,当使用 sort()时,你会改变原来的列表,而 sorted()会返回一个新的列表而不改变原来的列表。如下所示:
sort()改变了原始列表
sorted()不改变原始列表
选择哪个用的高,要看实际情况。例如,如果你想保留原始记录,那么你应该使用 sorted()。如果你想节省空间和内存,那么你应该使用 sort()。
整理字典
让我们创建一个字典:
这里使用“zip”函数非常方便,我们将两个长度相同的列表中的元素映射在一起。
要对字典进行排序,我们必须使用 sorted(),因为字典没有嵌入 sort()函数:
如果我们直接使用 sorted(),该函数将只从字典中返回一个键列表:
或者一个值列表:
我们可以调用字典中的 items()函数,按键对其进行排序,并返回元组列表:
如果我们想对键进行逆序排序,我们可以在 sorted 函数中指定它:
如果我们想按值排序呢?有两种方法可以做到这一点。一种是使用 sorted(),但使用 lambda 函数指定排序的键;另一种方法是不使用默认的字典类型,而是使用不同的字典类型,直接按值对字典进行排序。
对于第一种方法,排序函数是一个键参数,用于指定在进行比较之前对每个元素调用的函数(或其他可调用函数)。我们可以在这里使用 lambda 函数来告诉 sorted()函数使用元素的哪一部分来进行比较。具体来说:
Python 是零索引的,所以 x[1]指定每个元素的第二部分是一个字典,也就是值。如果想要一个逆序,除了在 sorted()函数中添加一个逆序参数,我们还可以在 lambda 函数中使用一个负号:
然而,这个技巧只在值是数字变量时有效。如果是字符串变量,就要用 reverse 参数。
如果您有一个值是数字的字典,您也可以在创建字典时使用不同的字典类型来直接对字典进行排序。字典类型可以是集合库中的计数器:
一个
[**Counter**](https://docs.python.org/2/library/collections.html#collections.Counter)
是一个[**dict**](https://docs.python.org/2/library/stdtypes.html#dict)
子类,用于计数可散列对象。它是一个无序的集合,其中的元素存储为字典键,它们的计数存储为字典值。
创建计数器时,使用以下代码:
我们不是创建一个默认字典,而是创建一个计数器。它很有用,因为当我们想按值对字典排序时,我们可以只使用 most_common()函数:
当您希望返回一个按值降序排列的元组列表时,这非常有用。
collections 库还有其他有趣的数据类型,比如 OrderedDict,它保留了插入的每个项目的顺序。更多信息,请点击查看网站。
返回排序后的字典
通常,排序后,我们会得到一个元组的排序列表。如果你想以字典的形式拥有它,你可以使用字典理解,这很像列表理解,来完成这项工作:
在本文中,我将介绍两种对 Python 字典进行排序的方法,以及 sorted()和 sort()函数之间的区别。救命这有帮助!感谢您的阅读!
这是我所有博客帖子的列表。如果你感兴趣的话,可以去看看!
我快乐的地方
zzhu17.medium.com](https://zzhu17.medium.com/my-blog-posts-gallery-ac6e01fe5cc3) [## 阅读朱(以及媒体上成千上万的其他作家)的每一个故事
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
zzhu17.medium.com](https://zzhu17.medium.com/membership)
创建色盲友好数据可视化的两个简单步骤
你的剧情可能很多人都无法理解。以下是解决这个问题的方法。
托马斯·伊万斯在 Unsplash 上拍摄的照片。
根据色盲意识网站,色觉缺失影响着世界上*约 8%的男性和 0.5%的女性,这意味着全球*约有 3 亿人患有某种程度的色盲。当谈到数据可视化时,颜色和图形元素的粗心选择会使图表模糊不清,甚至许多人无法理解。
事实上,不仅仅是色盲的人在解释用不小心选择的配色方案绘制的数据时会有问题。像黑白打印或阳光照射在设备屏幕上这样的常见事情也可能影响没有色盲的人的颜色感知。
虽然有各种类型的色盲,但创建每个人都可以轻松理解的数据可视化实际上非常简单——归根结底就是做出正确的选择。尽管如此,现在*量的内容并不是色盲友好的。表面上看,主要问题是*多数数据科学家在设计他们的数据可视化时仍然不*惯考虑这个因素。
在设计数据可视化时,您通常会考虑色盲吗?如果你没有,也许你应该开始做了。在这里,您将找到一些简单的提示,帮助您创建每个人都可以完全理解的数据可视化,而不管色盲状况或外部色觉改变。
动机:对于色盲的人来说,看似正常的图表可能无法理解
当我在攻读博士学位时,我经常使用 Google Sheets 快速生成临时图。但事实证明,Google Sheets plots 的默认配色方案并不是真正的色盲友好。它看起来是这样的:
用谷歌工作表创建的虚构图表。
如果您没有色盲,您*概可以区分图表中五个不同的数据系列。但是让我们来看看色盲的人是如何看到它的。我用科布利斯——色盲模拟器来模拟患有八种不同色盲的人会如何感知这张图片。你可以查看下面的结果。(每个模拟条件的名称可以在图的标题中找到。)
第一:Protanomaly(红弱)。第二:Deuteranomaly(绿弱)。用谷歌工作表和科布利斯创建。
第一:Tritanomaly(蓝弱)。第二:红盲。用谷歌工作表和科布利斯创建。
第一:多盲症(绿盲)。第二:Tritanopia(蓝盲)。用谷歌工作表和科布利斯创建。
第一:色盲。第二:蓝锥单色。用谷歌工作表和科布利斯创建。
在其中一些模拟中,很难甚至不可能区分不同数据系列使用的不同颜色,这使得许多人完全无法理解该图表。实际上,任何在黑白打印件上看到它的人都不会理解。
第一步:选择正确的颜色
所以现在的问题是:有没有可能不考虑色盲,挑选出*家都能轻易分辨的颜色?鉴于有这么多不同的色盲状况,这可能不是一件容易的事情。一般建议是避免有问题的颜色组合,如红/绿、绿/棕、绿/蓝、蓝/灰等。
但幸运的是,其他人已经经历了这个过程,并找出了适合*多数色盲的颜色组合,所以你不必自己做这些。你可以在网上搜索这样的组合,使用你最喜欢的。甚至有一些在线工具可以根据你的要求帮助你选择一个色盲友好的配色方案,比如 ColorBrewer 和 Coolors 。
我已经使用 ColorBrewer 为上面的示例图表选择了一个更好的配色方案。这是一个很棒的工具,可以根据用户选择的各种标准生成配色方案,包括色盲友好性。我让它用五种色盲安全、易于打印的颜色来创建配色方案。这是我选的一个:
使用 ColorBrewer 建议的颜色的新地块。使用的颜色:#d73027、#fc8d59、#fee090、#91bfdb、#4575b4。用谷歌工作表和科布利斯创建。
这是科布利斯的色盲模拟结果:
第一:Protanomaly(红弱)。第二:Deuteranomaly(绿弱)。用谷歌工作表和科布利斯创建。
第一:Tritanomaly(蓝弱)。第二:红盲。用谷歌工作表和科布利斯创建。
第一:多盲症(绿盲)。左:Tritanopia(蓝盲)。用谷歌工作表和科布利斯创建。
第一:色盲。第二:蓝锥单色。用谷歌工作表和科布利斯创建。
这样好多了吧?几乎在所有情况下都可以很容易地区分这五种颜色。唯一的例外是色盲的模拟,这是一种影响*约 30,000 人中有 1 人的情况。患有完全色盲的人不能感知任何颜色,并且会发现在这个图中几乎不可能区分 A 和 E 以及 B 和 D。同样,同样的事情也可以说发生在任何一个把这个情节印成黑白的人身上。
虽然上面使用的配色方案并不适用于所有的色盲情况,但仍然有可能找到合适的配色方案。你会发现*部分都是单色调色板,由单一颜色的不同强度组成。下面是这样一个调色板的例子,也是用 ColorBrewer 生成的:
用 ColorBrewer 生成的色盲友好调色板。来源:ColorBrewer。
然而,虽然这种方法可以安全地用于五种不同的颜色,如本例所示,但如果您需要使用更多的颜色变化,颜色强度可能会变得越来越相似。即使对于没有色盲的人来说,这也可能是一个问题。
这表明,虽然使用色盲友好的调色板可以让*多数人更容易理解您的数据可视化,但它可能仍然不是一个完美的解决方案。一般来说,不建议只使用颜色的变化来编码信息,即使它们是可访问的颜色。这就是为什么你也需要第二步。
第二步:使用不同的形状、图案、纹理或标签
确保人们能够完全理解您的绘图的最佳方法是,通过使用其他东西来区分其中的数据系列,从而完全消除对可区分颜色的需求。(这并不意味着你不应该关心在你的绘图中使用哪些颜色——除了使用色盲友好的调色板之外,你还应该这样做,以防万一。)
例如,我们可以通过对每个数据系列使用不同的形状来改进上面的图表,如下所示:
对不同的数据系列使用不同的形状。用谷歌工作表创建。
通过这种设计,我们可以确保色盲读者能够完全理解绘制的数据,不管他们的具体情况如何。即使出于某种原因,有人严格使用黑色和白色(没有灰色阴影)打印该图表,该图表仍然是明确的。
对于其他类型的情节,您可以使用其他策略。对于线图,除了不同的颜色之外,还可以使用不同的线型和/或不同的线宽。不幸的是,我无法找到如何在 Google Sheets 中使用虚线或点线模式,但我经常用 LaTeX/PGFPlots 和 Python/Matplotlib 这样做,所以我确信其他广泛使用的绘图软件也支持它们。下面是一个用 Matplotlib 创建的示例:
在线图中使用不同的线型。用 Matplotlib 创建。
对于条形图或饼图,我建议应用不同的填充纹理。同样,我在 Google Sheets 上找不到这个选项,但许多最流行的绘图软件可能都支持它。这里有一个我几年前创建的真实例子(使用 PGFPlots):
在条形图中使用不同的填充纹理。使用 PGFPlots 创建。
如果由于某种原因,您不能在绘图中使用不同的形状、图案或纹理,请考虑向数据系列添加标签(前提是您有足够的空间)。最重要的是确保每个人都能够理解你绘制的数据。
摘要
许多人仍然在创建色盲不友好的数据可视化的主要原因不是因为这很难避免,而是因为他们可能从一开始就没有想过这个问题。一旦我们意识到这一点,并有意创建可访问的图表,它实际上是非常简单的。基本上,这归结为两件事:
- 选择色盲友好的调色板:像 ColorBrewer 和 Coolors 这样的在线工具使得现在很容易找到合适的配色方案。你也可以使用在线色盲模拟器,比如科布利斯和来检查你的情节在其他人看来会是什么样子。
- 但不要只依赖颜色:为你的地块添加不同的几何形状、线条图案、填充纹理甚至标签。这将提高每个人的可读性,包括没有色盲的人。
致谢
我感谢我用来创建本文所示示例的免费工具的创建者,即: 科布利斯 ,color brewer,Google Sheets,Matplotlib和
推荐阅读
- 娜奥米·戴维斯的《色盲——不成文的准则:色盲父母指南》。
- 关于色盲的一切:儿童(和成人)色觉缺失指南,作者凯伦·瑞·莱文。
出自同一作者
这是一个简单的策略,可以让你在任何项目中更有效率。
medium.com](https://medium.com/swlh/do-this-before-you-start-working-on-any-project-14fd7bfa7327) [## 要达到 1,000,000 美元,您需要投资多少以及投资多长时间
本文中的数学和交互式工具将为您提供这些问题的答案。你可能会感到惊讶。
themakingofamillionaire.com](https://themakingofamillionaire.com/how-much-and-for-how-long-you-need-to-invest-to-reach-1-000-000-19a0ce7d4c67) [## 80/20 法则如何影响你的长期投资
你投资的一半资金将产生你投资组合的*部分结果。但是哪一半呢?
themakingofamillionaire.com](https://themakingofamillionaire.com/how-the-80-20-rule-affects-your-long-term-investments-96f6577b59bc)
披露:此帖子包含一个或多个亚马逊服务有限责任公司协会计划的链接。作为代销商,我从通过这些链接购买的商品中获得佣金,客户无需支付额外费用。
在 Python 中更有效地循环的两种简单方法
使用枚举和压缩编写更好的 Python 循环
许多循环。由 David Streit 在 Unsplash 上拍摄的照片
Python range
函数非常强*,但它通常可以被其他内置函数所替代,使您的循环更容易编写和阅读。在本文中,我将向您展示何时可以用enumerate
或zip
来替换range
。
使用 enumerate(object)而不是 range(len(object))
问题 1 :你经常有像列表这样的对象需要迭代,同时还要跟踪每次迭代的索引。给定下面的列表,您将如何使用 for 循环来生成所需的输出?
my_list = ['apple', 'orange', 'cat', 'dog']# desired output
Item 0: apple
Item 1: orange
Item 2: cat
Item 3: dog
方案一:使用for i in range(len(my_list))
for i in range(len(my_list)):
print(f"Item {i}: {my_list[i]}")Item 0: apple
Item 1: orange
Item 2: cat
Item 3: dog
更好的解决方案:使用for i, value in enumerate(my_list)
for i, value in enumerate(my_list):
print(f"Item {i}: {value}")Item 0: apple
Item 1: orange
Item 2: cat
Item 3: dog
解释 : enumerate
在迭代器my_list
上循环,并在迭代对象时将条目及其索引作为索引条目元组返回(参见下面的代码和输出以查看元组输出)。当我们将循环构造为for i, value in enumerate(my_list)
时,我们解包索引项元组。
for i in enumerate(my_list):
print(i)(0, 'apple') # tuple, which can be unpacked (see code chunk above)
(1, 'orange')
(2, 'cat')
(3, 'dog')
问题 2 :给定与上面相同的列表,编写一个循环来生成所需的输出(确保第一个索引从 101 开始,而不是从 0 开始)。
my_list = ['apple', 'orange', 'cat', 'dog']# desired output
Item 101: apple
Item 102: orange
Item 103: cat
Item 104: dog
解决方案二:使用for i, value in enumerate(my_list, 101)
函数enumerate(iterable, start=0)
让您从任何想要的数字开始计数索引(默认为 0)。
for i, value in enumerate(my_list, 101):
print(f"Item {i}: {value}")Item 101: apple
Item 102: orange
Item 103: cat
Item 104: dog
外卖
enumerate
内置函数在迭代器上循环,并从迭代器中返回索引和条目作为索引条目元组- 使用
enumerate(object)
而不是range(len(object))
来获得更简洁易读的代码 - 提供第二个参数来指示开始计数的数字(默认值为 0)
使用 zip 并行迭代多个对象
问题 3: 你有多个想要并行迭代的列表或者对象。给定下面的三个列表,你将如何产生期望的输出?
my_list = ['apple', 'orange', 'cat', 'dog']
my_list_n = [11, 12, 25, 26]
my_list_idx = [1, 2, 3, 4]# desired output
1\. apple: 11
2\. orange: 12
3\. cat: 25
4\. dog: 26
解决方案 3 :使用range(len(my_list))
获取索引
for i in range(len(my_list)):
print(f"{my_list_idx[i]}. {my_list[i]}: {my_list_n[i]}")1\. apple: 11
2\. orange: 12
3\. cat: 25
4\. dog: 26
更好的解决方案:使用zip(my_list_idx, my_list, my_list_n)
for i, obj, count in zip(my_list_idx, my_list, my_list_n):
print(f"{i}. {obj}: {count}")1\. apple: 11
2\. orange: 12
3\. cat: 25
4\. dog: 26
说明:可以使用zip
同时迭代多个对象。zip
返回可以在循环中解包的元组。参见下面的例子来理解这个函数是如何工作的。
zip
返回元组:
for i in zip(my_list_idx, my_list, my_list_n):
print(i)(1, 'apple', 11) # 3-item tuple
(2, 'orange', 12)
(3, 'cat', 25)
(4, 'dog', 26)
元组中的每个元素都可以手动提取:
for i in zip(my_list_idx, my_list, my_list_n):
print(f"{i[0]}. {i[1]}: {i[2]}") # i is a 3-item tuple1\. apple: 11
2\. orange: 12
3\. cat: 25
4\. dog: 26
外卖
zip
内置函数可以同时迭代多个迭代器。zip
创建一个惰性生成器来生成元组
结论
使用内置的 Python 函数enumerate
和zip
可以帮助您编写更好的 Python 代码,可读性更强,更简洁。
如果您对提高数据科学技能感兴趣,以下文章可能会有所帮助:
[## 在 Python 中重塑 numpy 数组—一步一步的图形教程
本教程和备忘单提供了可视化效果,帮助您理解 numpy 如何重塑数组。
towardsdatascience.com](/reshaping-numpy-arrays-in-python-a-step-by-step-pictorial-tutorial-aed5f471cf0b) [## 使用终端多路复用器 tmux 提高编码和开发效率
简单的 tmux 命令来提高您的生产力
medium.com](https://medium.com/better-programming/code-and-develop-more-productively-with-terminal-multiplexer-tmux-eeac8763d273) [## 真实或虚假的关联:你约会的有魅力的人更令人讨厌
使用 Python 模拟数据、测试直觉并提高数据科学技能
towardsdatascience.com](/real-or-spurious-correlations-attractive-people-you-date-are-nastier-fa44a30a9452) [## 新冠肺炎危机期间的免费在线数据科学课程
像 Udacity、Codecademy 和 Dataquest 这样的平台现在免费提供课程
towardsdatascience.com](/free-online-data-science-courses-during-covid-19-crisis-764720084a2)
更多帖子, 订阅我的邮件列表 。
使用 Colab 的两步面部识别
上传你的图片,并说服自己使用谷歌 Colab
这是周一的早晨,我正在引导我内心的杰瑞米·霍华德(又一次)。在过去的几个月里,我看到了*量的面部识别指南,其中一些确实非常好。
问题是,每个深度学*或机器视觉的学生都通过不同的途径变得感兴趣。也许你是一个对声音感兴趣的音乐家,或者是一个对表格数据感兴趣的统计学学生。
我知道当我开始阅读 TensorFlow 和图像分类时,我个人变得很感兴趣——这种想法是,如果我有一个精心策划的数据集,我可能能够训练一个架构来以高精度对图像进行分类。
Google Colab 那时还不存在,我最近把类似的东西移植到了 Colab 上,我正在进行一次演练。点击此处查看第 1 部分:
[## Google Colab 中 ResNet 的端到端改编—第 1 部分
只需点击几下鼠标,就能训练出一个深度神经网络
towardsdatascience.com](/end-to-end-adaptation-of-resnet-in-google-colab-part-1-5e56fce934a6)
我最初的尝试是在我的 iMac 上使用 Anaconda、Spyder 完成的,没有使用 GPU,只是让我的 Core i5 在数千张图像中跋涉,而我离开去做其他事情(阅读:为急救医学委员会学*)。当然,在我开始训练之前,我必须通读二十多篇使用不同风格编写代码的人的文章。结果是,一个代码的嵌合*杂烩,当我几周后看它的时候很难理解。
然后 Colab 进入了这个场景,虽然我仍然在我的机器上使用 Jupyter 和 Spyder,但我使用 Colab 与您交流并有效地共享我的代码。它并不完美,但他们在让*众获得高端计算方面做得很好,你所需要的只是一个谷歌账户。
闲话少说。让我们浏览一下笔记本:
Colab 笔记本
代表性图像
来源:Unsplash.com
两步面部识别
我在这里有点开玩笑——这两个步骤是“全部运行”和上传你的面部照片。
笔记本被设计成隐藏*部分代码,这样你就可以清楚地看到结果。首先看看输出,让自己相信我们不仅可以挑出(希望是*多数)照片中的人脸,然后使用情绪检测器功能分配一种情绪。
我还留了一些代码来尝试计算过程的时间。
面部识别库
这个使用了“人脸识别”库,在这里找到的。我们也会计算这需要多长时间。
这条生产线承担了所有繁重的工作:
face_locations = face_recognition.face_locations(image)
代码的其余部分使用 for 循环遍历面数,并使用以下两条线在面周围画一个框:
rect = patches.Rectangle((left,top), (right - left),
(bottom-top),linewidth=3,edgecolor='r',facecolor='none')
ax.add_patch(rect)
作者图片
注意:并不是所有的人脸都被检测到;帽子和太阳镜会干扰检测。
检测到的人脸数量:24
耗时:2.43 秒
加入情感检测
除了一行(如果在第 11 行阻塞)之外,代码几乎与上面的相同:
正如我们将在下图中看到的那样,出现“if”语句是有原因的。“检测情绪”这条线并不适用于所有的脸。
结果如下:
作者图片
注:情绪检测*多管用。并不是所有的脸都会表现出一种情绪,它认为婴儿生气了。我会让你仔细看看,看你是否觉得标签完全匹配——我不认为它们完全匹配。
检测到的人脸数量:24 个
耗时:2.83 秒(稍长以添加情绪检测)
使用开放式 CV 的哈尔叶栅
基于 Haar 级联的识别是进行面部识别的另一种方式。关于这些工作的讨论超出了本文的范围。网上有很多讨论这种方法的技术资源。
结果如下:
作者图片
再次,使用哈尔级联是另一种方法,这里也有一些失误。特别是,它错过了一些脸,并认为裤腿,脚踝,鞋面的组合是一张脸。
然而,在我运行的每次迭代中,使用 Haar cascades 和 OpenCV 比使用 face_recognition 库要快得多——有时几乎快 20%—30%。这种速度优势非常显著,尤其是在浏览数百张图像(例如视频)时。它还涉及一种计算成本较低的过程,并且可能运行在需要较少功率的较便宜的硬件上。
当然,这不是仅有的两种面部检测方法,专有的方法可能更好。如果你必须在两者之间选择,我不认为有明确的答案——一个更擅长检测(人脸识别),但另一个更快(哈尔瀑布)。
检测到的人脸数量:24 个
耗时:1.95 秒(更快)
参考
[1] 维基百科-杰瑞米·霍华德,2020 年 10 月访问
[2] 面部识别库,PyPi,2020 年 10 月访问
[3] 打开 CV 哈尔叶栅,2020 年 10 月进入
有两种方法可以检验你是否完全不擅长编程
编程;编排
因为你可能不知道。
由 siscadraws 创作的艺术
学*编码很难。
我浏览了无数的 YouTube 视频、在线课程和个人项目。然而不知何故,我觉得我没有比开始时更进一步。
当我继续阅读关于编程、数据科学和云计算的文章时,很难不对其他人正在做的酷事情感到不知所措和害怕。甚至看着 Python subreddit 都让我想知道,才学了几个月的人怎么能构建如此复杂的项目。
我在哈佛开设了CS50 课程,我强烈推荐给任何想了解计算机科学的人。每次讲座结束后,你的任务是根据那周的主题编写代码来解决一个作业。我最近完成了“算法”的第三周,并完成了第二个作业。
我花了将近六个小时。
当我的代码最终通过所有测试时,我更多的是松了一口气,而不是欣喜若狂。盯着一个看似简单的问题,却不知道如何修复一个又一个 bug所以龙让我质疑自己是否有任何解决问题的技巧可言。我也很好奇别人用了多久才解决。当然,*多数人(从哈佛的学生到数千名注册在线课程的学生)不需要在一项作业上花半天时间。
谢天谢地,在我陷入自我怀疑的黑洞(轻度夸张)之前,两件事让我回到了正轨。他们帮助我认识到,虽然总是想学更多东西很好,但相信自己的能力也很重要。你可能会比你想象的走得更远。
让我们深入了解一下你应该做的两件事,看看你是否不擅长编程!
1.为某人建造某物
我最近看了几个月前为工作写的一些代码,心想“哇,这太糟糕了。”我摇摇头,开始整理我创建的混乱的函数和变量,一直在想为什么我没有给它添加更多的注释。
当我打字的时候,我的女朋友看到我在自言自语,问我怎么了。当我告诉她我的代码很烂时,她问:“有用吗?”
“是的,它能工作,但是它太乱了,我几乎不记得我当初是怎么写的了。”我说。
"嗯,你现在已经差不多修好了,所以这意味着你确实记得。"她回答道。
然后她又问:
“管用吗?”
这是一个简单的问题,是的,但它可以揭示这么多。首先,我将在本节的剩余部分开始说,是的,你构建的东西构建得好(可伸缩性、可读性、避免深度嵌套、DRY 等)是很重要的。).我并不是说,如果你写的垃圾在技术上实现了它的预期功能,你应该感到高兴。
然而,如果你所构建的服务于它的目的,并且在你需要解释它是如何工作的时候没有完全混淆,你应该感到自豪!
这让我想到了如何通过为某人构建东西来测试自己的编程技能。看着自己写的代码帮助别人,你会有一种难以置信的满足感。
我女朋友最近需要翻译 PDF 文件中的文本。然而,她正在处理的 pdf 是扫描图像。她实际上不能复制任何文本,所以她不得不在谷歌翻译中手动输入段落。此外,由于她只需要翻译 PDF 的几个段落,使用在线服务来翻译整个文档(甚至一页)不会有很*帮助。
我看到她在做什么,心想“嗯,我也许能帮上忙”。
然后我为她写了一个非常简单的 CLI 应用程序,它将一个图像作为参数,用 T0 从图像中获取所有文本,然后将文本复制到剪贴板。
在谷歌翻译中输入段落并不是一件非常耗时的任务。也就是说,即使我每天只减少了她 30 分钟的工作时间,那也是她额外得到的 30 分钟!
奇怪的是,找到一个解决方案并建立它来帮助她,帮助我意识到我到目前为止学到的东西并不局限于我的头脑。我可以用它来改善其他人的生活。
你也可以!环顾你身边的人,无论是你的家人还是同事,我相信你能找到一些你能帮助他们自动化的东西。能够想到并开发出一个解决方案,即使是一个微不足道的问题,就是编程。
2.教某人某事
这是你以前可能见过的建议。教别人你刚学到的概念是你记忆信息的好方法。这是因为不首先理解主题就不可能教学。如果你试图在没有掌握的情况下教某人一个新概念,你可能能够背诵一个基本的总结,但你将无法回答他们的任何后续问题。
不久前,我第一次在 Python 中学*了列表理解,此后我在各种场景中使用了它们。然而,当我帮助我的女朋友完成她的一个剧本时,我才真正知道我理解了他们。
她在寻找一种方法,根据文件名中与文件夹名称匹配的部分,自动将文件移动到不同的文件夹中。她首先想根据文件名创建所有的文件夹。例如,目录中有一个名为12345_file.pdf
的文件,这意味着她需要创建一个名为12345
的文件夹。
为此,她想实现一个 for 循环来获取所有文件名的第一个数字,并将它们放在一个列表中,然后遍历该列表来创建所有文件夹。然而,在某些情况下,一些第一个数字是重复的。可能还有另一个名为12345_newfile.pdf
、12345_oldfile.pdf
的文件,以此类推。这将导致一个包含重复值的列表,这是行不通的。
为了同时避免使用 for 循环创建列表和删除列表中的所有重复项,我为她写了以下代码:
folder_name_list = set([i[:5] for i in original_filepath_list])
我们已经有了一个文件路径列表,所以我向她解释说,您可以使用列表理解来创建一个列表。您可以使用 list comprehension 来定义列表的组件和任何条件,而不是创建一个空列表,循环遍历并向其追加新元素。在本例中,我将采用现有文件路径列表中每个元素的前 5 个字符。然后,为了从列表中删除所有重复的值,我将列表转换成一个集合,因为 Python 中的集合不能有重复的值。
经历这个过程感觉很棒,因为:
- 从列表中删除重复项是一项非常常见的任务,所以能够解释如何快速完成这项任务,并且不需要查找任何资料,这意味着我已经做了足够多的练*。
- 能够解释一个(相对)更高级的操作,比如列表理解,意味着我所做的不仅仅是连接字符串之类的东西。
- 我又要去帮助别人了!我看到有人学到了新的东西,并在自己的代码中实现了它,这意味着我的学*对别人的生活产生了积极的切实影响。
Sidenote: I know there are many other ways to accomplish the above process- she ended up using shutil to move everything into folders by checking if each file keyword was present in the list of folder names. If you've got another solution you'd like to share, please do. I'll send them to her and you can say you helped some random person on the internet with your code.
我并不是说,如果你能构建一些东西并教给别人一些东西,你就是一个了不起的程序员。事实上,有时通过建设和教学,我意识到(再一次)我还有很多东西要学。我可能永远也学不完新的东西,但通过帮助别人,我知道一件事是肯定的:如果别人从我为他们做的事情中受益,我不会完全失败!
如果你发现自己陷入了困境,读一读这份来自一位非常明智的朋友的鼓舞人心的摘录:
一些编程(和生活)建议
“…[编程]就像数学侠。你可以用一生的时间来学*数学,而且你总能学到更多……每个人都对它有一定的亲和力,但不管怎样,它主要是实践……学*永远不会太晚。”
仅此而已!
我希望我已经说清楚了,你很可能并不完全不擅长编程。你可能不是一个神童,一个专家,或者仅仅是有能力,但是坚持练*和帮助别人,你会走得很好。
编码快乐!
**More stuff by me**:
- [Using One Line of Code to Write to a Relational Database](/using-just-one-line-of-code-to-write-to-a-relational-database-3ed08a643c5f?source=friends_link&sk=1d54a968dc2fda2fc190af8bfdec5287)
- [An Introduction to the Cohort Analysis With Tableau](https://medium.com/swlh/how-to-group-your-users-and-get-actionable-insights-with-a-cohort-analysis-b2b281f82f33?source=friends_link&sk=8b672c3feb79e194804f2a1c33660e19)
- [How to Quickly Create and Unpack Lists with Pandas](/how-to-quickly-create-and-unpack-lists-with-pandas-d0e78e487c75?source=friends_link&sk=32ea67b35fe90382dc719c1c78c5900c)
- [Rename Screenshots on Mac with 21 Lines of Python](/how-to-rename-screenshots-on-mac-with-21-lines-of-python-c3c2366f6eec?source=friends_link&sk=ee7e2c04f068855b5b7dfffa7e54152b)
SQL 中的类型转换
软件开发
如何转换 SQL 中的数据类型及其对数据库&查询性能的影响
从一种类型转换到另一种类型在 SQL 查询中非常常见。这种使用基本上源于糟糕的数据库设计、不断变化的需求、遗留的应用程序和懒惰的数据库维护。不是有意的,但是这些事情确保了数据消费者的需求永远不会被直接满足。消费者必须想出变通的解决方案来获取他们的数据。类型转换是主要的工具之一,尽管很少被提及。
我们将使用下面的样本数据集来浏览在查询中进行转换的不同示例。
资料组
对于本文的范围,假设当我们谈论造型时,它包括所有 SQL 风格中所有与造型相关的函数,如 Oracle 中的to_date
、MySQL 中的cast(something as int)
等等。然而,这些例子是基于 MariaDB (MySQL)的。
隐式铸造
下例中的所有查询都会给出完全相同的输出——这意味着数据库引擎隐式地理解了我们给它的where
子句输入。它根据自己的要求在where
子句中把'1'
内部转换为1
,反之亦然。以下示例中的所有四个查询将产生相同的输出。
这同样适用于从字符串到浮点数的转换,反之亦然。但是,并非所有数据库都在相同程度上支持隐式类型转换。对于本文的范围,我使用了 MariaDB。
当数据库能够隐式转换值时,我们可以假设它会对性能产生影响。隐式转换不是没有成本的,因为根据定义,它会为查询引擎增加更多的工作。事实上,隐式转换最终会比显式转换花费更多的成本,因为在显式转换中,您可以控制成本,而在隐式转换中,数据库处于控制之中,它决定采取什么行动,这反过来又会产生成本。
显式造型
这是人类指定铸造什么和铸造成什么的地方。因为字符串-整数、字符串-浮点转换由数据库引擎本身负责,所以当我们讨论日期时,转换的下一个最重要的用例就出现了。日期是数据库系统中最不同的存储信息。这里有很多变体,其中两个主要的是——时区和日期格式。*多数时候,日期不是以日期或时间戳的形式存储的,而是以字符串的形式存储的。这就是一切变得太有趣的地方。😃
下面的示例显示了数据库引擎为将字符串转换为日期提供的许多选项。
仔细查看数据库,当涉及到用字符串存储日期时,它是非常可靠和容易理解的。它可以解释几乎所有的日期格式,只有一个例外——2019 12 12
——这是一个明显的数据库不能识别空间的例子。我猜也没人会这样写日期。但你无论如何也不能这么肯定。如果你的日期是这种格式,你可以做一个replace(_date,' ','')
并把它转换成日期。
注意,数据库已经返回了结果集,但是有 26 个警告。让我们看看它们是什么。
警告不是错误。这里的警告是一个通知,表明数据库怀疑可能有问题。当您试图将一个副本insert ignore
到一个表中时,也会发生类似的情况。如果你insert
,它不会抛出警告,它会抛出错误。对于警告,数据库会假设您会在需要时查看这些警告。如果出现错误,除非您修复它们,否则无法继续。
就像我们从字符串转换日期一样,许多其他的转换也是可能的,但是要小心。
对性能的影响
如前所述,添加到查询引擎的任何工作都会对性能产生一些影响。对于合理*小的数据集,隐式和显式转换(类型转换)可能而不是本身会**降低查询速度。但是,您的查询很慢完全有可能是因为类型转换引起的其他问题。
类型转换(强制转换)会使查询的一个或多个索引变得无用。您可以使用explain
或explain extended
来检查查询计划中是否使用了索引。如果索引没有被使用,我们可以在where
子句或join
子句中显式类型转换列,等等。
让我们看看这是如何影响索引的。
运行下面的两个查询告诉我们,当我们在where
子句中传递一个要与整数字段进行比较的字符串时,它会选择索引idx_val_int
,但是在下一个查询中,当我们在where
子句中传递一个要与字符串字段进行比较的整数时,它不会使用索引。它在可能的关键字中显示了索引的名称。
还有关于为什么没有选择索引的附加信息。
连强行索引idx_val_int_char
都不管用
唯一的解决方法是以字符串的形式提供值,即'1'
而不是1
或cast(1 as char)
,1
是应用程序的输入,因此是可参数化的。
结论
每个数据库都有许多将一种数据类型转换成另一种数据类型的选项,这些选项可以解决我们日常面临的*多数用例。尽管类型转换是一个很好的特性,但它确实伴随着性能成本,无论是隐式转换还是显式转换。
虽然并不总是可行,但是避免这一切的一个很好的方法是以这样一种方式设计和维护数据库和查询,即需要最少的类型转换。如果这不可能,请确保您的查询使用了正确的索引,这样性能就不会受到更*的影响。还有更多关于字符集和排序的内容。我们将在另一个时间谈论那件事。
第一类和第二类错误
当统计测试结果与现实不符时
当执行统计测试时,目标是查看在给定数据的情况下,某个陈述是否明显不太可能。用更简单的语言来说,你试图确定你认为一个陈述是真的还是假的。这种说法被称为零假设 (H₀),相反的说法被称为替代假设 (H₁).在某种程度上,零假设是默认的陈述,因为它可能是真的,测试的工作就是挑战这一点。很像在法庭上,你是“无罪的,直到被证明有罪”,不幸的是,有时无辜的人被判有罪,而有罪的人却被释放,因为他们实际上犯了罪。这两个错误分别对应于统计测试中已知的第一类和第二类错误。
在高中,我的 AP 统计老师教我们记住,在第二类错误中,你会拒绝失败。
第一类错误
当你拒绝零假设时,第一类错误就发生了。在前面提到的法庭例子中,第一类错误是给一个无辜的人定罪——无罪的零假设被拒绝,当它不应该被拒绝的时候。第一类错误也可以被认为是假阳性,因为你错误地声称是手头变量之间的统计显著差异,而实际上不是。
第二类错误
相反,当没有拒绝零假设,而应该拒绝零假设时,就会发生第二类错误。在高中,我的 AP 统计老师教我们记住,在第二类错误中,你会拒绝失败。在我们一直在处理的例子中,第二类错误将是未能给那些是 有罪的人定罪,因此应该被定罪。第二类错误也可以被认为是假阴性,因为你错误地声称不是手头的变量之间的统计显著差异,而实际上是。
维基百科:第一类和第二类错误
第一类和第二类误差彼此成反比,因为减少一种误差会增加另一种误差。在不同的情况下,一种类型的错误可能更有害,因此最小化更重要。例如,在法庭上,让一个有罪的人走,比把一个无辜的人送进监狱要好。因此,在这种情况下,I 型错误会更糟。然而,在医疗情况下,对于你没有的东西,最好有一个阳性的检测结果,而不是对于你有的东西,有一个阴性的检测结果。通过对你实际上患有的疾病的测试呈阴性,你的生命可能由于缺乏治疗而处于危险之中。因此,在这种情况下,第二类错误会更糟。虽然完全消除这些错误的可能性对于非确定性方程来说是不可能的,但是最小化它们是统计学和数据科学的一个*目标。
参考资料:
在统计假设检验中,第一类错误是拒绝一个真的零假设(也称为“假…
en.wikipedia.org](https://en.wikipedia.org/wiki/Type_I_and_type_II_errors)
作为数据科学家,您需要了解的数据类型
数据科学
了解数据科学和数据类型(结构化、非结构化……)
背景由hello queue上的 Unsplash
目录
- 什么是数据科学?
- 数据科学是一个新领域吗?
- 数据类型
- 数据格式/来源
- 总结与结论
什么是数据科学?
数据科学是对*量数据的研究。就像生物科学是对生物学的研究一样,物理科学是对物理反应的研究。这是使用数据来理解不同事物,理解世界的过程。
数据科学是关于从各种形式中提取数据的方式的领域,无论是非结构化还是结构化形式。这是一个多学科领域,汇集了来自计算机科学、数学/统计学和数据分析的概念。
数据科学的核心是总是问问题。数据科学家总是需要对这个世界保持好奇。
- 我们能从这些数据中学到什么?
- 一旦我们找到了我们要找的东西,我们可以采取什么行动?
“在未来 10 年,数据科学和软件对医学的贡献将超过所有生物科学的总和”。维诺德·科斯拉
数据科学是一个新领域吗?
早在第二次世界*战后的几年里,因肺癌死亡的人数有所上升,科学家和医生没有就具体原因达成一致,没有人认为香烟可能是原因的假设,因为那时每个人都吸烟。在英国,所有医生的数据和细节都写在一个中央登记簿上(包括:吸烟者?;活着/死亡?,如果他死了,死亡的原因是什么?)所以无意中,我们有了他们可以操纵的庞*数据,来预测和理解这种现象。于是布拉德福德·希尔和理查德·多尔开始操纵这些数据,提取死于肺癌的医生,看看他们是不是烟民。结果很明显,在过去的 29 个月里,死于肺癌的人 100%都是吸烟者。因此数据科学并不新鲜,新鲜的是从各种来源获得的*量数据。
在数据科学领域有很多职业道路;*多数,但不是全部,涉及一点数学,一点科学,以及对数据的好奇心。
数据类型
*数据:*数据没有精确或通用的定义...海量数据集,或包含更多种类的数据,以不断增加的量和更高的速度(3V 法则)到达。
图片来源:作者
- 数据量:巨*的数据量,TB—Pb。
- 数据速度:数据流动、数据变化、数据处理的高速。
- 数据种类:各种数据源(社交媒体、移动、结构化数据、非结构化数据……)。
“世界是一个*数据问题”。安德鲁·迈克菲
结构化数据:具有预定义结构的数据,这些数据已经以有序的方式存储在关系数据库或电子表格中(传统的行列数据库)。结构化数据有两个来源:
- 机器生成的:从传感器、网络日志和金融系统接收的所有数据,包括医疗设备、GPS 数据、服务器捕获的使用统计数据。
- 人为生成的:主要包括人类输入计算机的所有数据,如姓名和其他个人信息、访问的网站、观看的电影类型(公司可以利用这些数据来了解其客户行为并做出适当的商业决策,例如网飞、Youtube 或 Medium 的推荐系统)…
非结构化数据:没有预定义结构的数据,以任何*小或形式出现(它们在存储中没有明确的格式),不容易存储在表中。它还根据其来源进行分类:
- 机器生成:说明了所有的卫星图像、安全摄像头、雷达数据捕捉以及其他更多信息。
- 人为生成:在互联网上*量存在,因为它包括社交媒体数据、移动数据、电子邮件和网站内容。这意味着我们上传到脸书或 Instagram 的图片,我们在 Youtube 上观看的视频,甚至我们发送的短信。
半结构化数据:非结构化数据和半结构化数据之间的界限一直不清楚,因为*多数半结构化数据一眼看上去都是非结构化的。不像结构化数据那样采用传统数据库格式,但包含一些易于处理的组织属性的信息。例如,NoSQL 文档被认为是半结构化的,因为它们包含可用于轻松处理文档的关键字。
其他数据类型
定量数据:数值。比如:身高,体重,工资,物价…
分类数据:可以标记或分组的数据。比如:性别、发色、种族、水果、动物…
数据格式/来源
最常见的数据格式:
- CSV -逗号分隔值:顾名思义,是一个使用逗号分隔值的分隔文本文件。
- XML -可扩展标记语言
- SQL 结构化查询语言
- JSON - JavaScript 对象符号
- 协议缓冲区
数据来源:公司、API、政府、学术、网络抓取/抓取…
摘要
- 数据科学是对*量数据的研究,可以揭示帮助组织做出战略选择的洞察力。
- 数据科学并不新鲜,新鲜的是来自各种来源的*量数据:博客、社交媒体、销售数据、GPS 数据、服务器捕获的使用统计数据、电子邮件、患者信息文件、运动表现数据、传感器数据、安全摄像头等等。
- 术语“*数据”描述了海量数据的集合,无论是结构化、半结构化还是非结构化的数据,都可以被处理和利用,以获得可理解的相关信息。我们用 3V 法则总结*数据的问题:量、速度、多样性。
结论
除了解决今天的问题,数据科学家也是未来项目的核心。你肯定听说过自动驾驶汽车。事实上,通过更好地利用数据,我们能够建造更强*、更智能的机器人。而这是数据科学家的责任。
资源:
- https://www . goodreads . com/book/show/7170627-万病之王
- https://www.goodreads.com/book/show/705365.现代医学的兴衰
- https://www . the guardian . com/news/2005/jun/02/thisweekssciencequestions . cancer
- https://en.wikipedia.org/wiki/Comma-separated_values
- https://www . edvancer . in/50-amazing-big-data-and-data-science-quotes-to-inspire-you/
感谢阅读!😄
查看我的其他文章,关注我的 中型
机器学*中集成方法的类型
弗拉德·希利塔努在 Unsplash 上拍摄的照片
在我之前的文章中,我们讨论了机器学*中不同类型的回归。因此,今天我将重点讨论数据科学中的各种集成方法。
我在职业生涯中经历的一件事是,在学*任何技能时,当我们试图将概念与我们日常生活中面临的事件联系起来时,事情会变得更容易。当我们有一个真实的生活类比时,记住事情是毫不费力的。因此,记住这一点,我们将通过与日常场景的比较来研究各种集合技术。
目录
1.介绍
2.集合方法的类型
3.装袋和增压方法之间的相似性
4.两种方法之间的差异
5.投票的类型
6.对概念的详细理解
- 制袋材料
- 助推
7.结论
1。简介
什么是合奏?
在简单的英语中,ensemble 指的是一组项目。例如:一群牧师、一群舞蹈演员等。
什么是机器学*中的集成方法?
集成方法是一种使用多个独立的相似或不同的模型/弱学*器来获得输出或做出一些预测的技术。
例如
随机森林是多个决策树的集合。
集合也可以通过不同模型的组合来构建,如随机森林、SVM、逻辑回归等。
现在,让我们用一个真实的例子来比较一下。
假设议会通过了一项法案,并召开了部长会议。
而不是总统或总理独自做出决定可能不是一个很好的主意,因为这说明了独裁统治,结果可能不是那么有利于公众。因此,这种类比可以指机器学*中的单一模型,如决策树、逻辑回归等,它们的性能总是略低于集成方法。
然而,如果在不同的部长之间举行会议,比如说四个部长,他们每个人都会提供一个有正反两面的意见。最后,最佳意见将在多数投票的基础上选出。这正是系综方法。
部长 1 :基于他的赞成和反对意见,认为该法案应该通过。
部长 2 :基于他的支持和反对,他说这个法案没有用,可能会引发很多挑战。因此不应通过。
部长 3: 表示议案可以通过。
部长 4: 也表示议案可以通过。
*多数人说“通过议案”。因此,该法案在议会获得通过。
类似地,我们可以在下面看到使用了不同的模型,并且基于每个模型的输出,最终的决定将通过多数投票做出。
来源:谷歌
为什么我们需要合奏技巧?
众所周知,任何机器学*模型中的错误和预测都会受到偏差、方差和噪声的不利影响。为了克服这些缺点,使用了集合方法。
2。集成方法的类型
有许多可用的集成技术,但我们将讨论以下两种最广泛使用的方法:
1。装袋
2。增压
让我们首先了解这两个术语之间的异同。
3。装袋和增压方法的相似性
这两种方法都可以用于分类(离散输出)和回归(连续输出)问题。
与单一模型相比,这两种方法在性能上都更好,因为它们通过投票聚集了所有弱学*者的输出,从而导致预测更准确。
4。两种方法的区别
装袋和增压的区别
5。投票类型
有两种类型的投票用于汇总所有弱学*者的输出。
硬投票 —如果我们收到每个弱学*者在类中的输出(例如:0/1),最后我们选择多数返回的输出类,那么这种方法称为硬投票。
例如:在我们的部长小组的例子中,由于 3 名部长赞成该法案,最终决定通过该法案。这是艰难的投票。多数获胜。
软投票 —有许多算法也为我们提供预测概率,如果我们收到概率输出,我们会取每个类别概率的平均值,平均值最高的类别将成为最终预测。这种预测方式被称为软投票,它比硬投票更好,因为我们在概率方面获得了更多的信息。
另一件要记住的重要事情是,这种投票方法被用作随机森林等群体的默认技术。
让我们使用下图来理解这两个概念,其中 1 是类 1,0 是类 0。
因为类 1 的平均概率在软投票中更高(63%),所以我们选择类 1 作为最终输出
6。概念的详细理解
装袋:
也称为 Bootstrap aggregating,因为训练集被分成多个子样本,并被提供给每个弱学*器(例如:每个决策树)。并且每个子样本包含几组特征以及随机选择的几个观察值。
既然这篇文章的主旨是用通俗的语言来理解概念,那么让我们举一个简单的例子来理解装袋。
我们都参加面试,面试过程是我能遇到的最简单的场景,类似于装袋。
假设你去参加一个面试,一进房间,你就看到五个表情严肃的人坐在那里,向你提出所有的问题。你是做什么的?感到紧张,冲出房间!!没有对吗?你去那里是因为你需要这份工作,你知道你足够勇敢,足够聪明,可以面对任何事情,无论如何你都会和他们打招呼,坐在他们面前。所以,让我们面对现实吧。
现在考虑一下,五位面试官中的每一位都将从五个不同的方面对应聘者进行面试,比如能力、基本技能、高级技能、沟通和管理技能。
此外,公司可能已经准备了一套基于不同方面的问题来测试候选人的技能。我们称之为“问题集”。
那么,采访现在开始。我们的目标变量是候选人是否被选中。
面试官 1 — 面试官 1 开始测试你的能力倾向技能从“问题集”中抽取一个基于能力倾向的问题样本,并设定你是否应该被选中的概率。
面试官 2 — 面试官 2 也开始测试你的基本技能,再次从“问题集”中抽取初步技术问题的样本,并设定你是否应该被选中的概率。
面试官 3—同样,面试官 3 开始测试你的高级技能从“问题集”中抽取另一个稍微困难的技术问题样本,并再次设定你是否应该被选中的概率。
现在,他也有可能问你一些从“问题集”中随机挑选的问题,但这些问题是相似的,并且已经在基本技术测试或任何先前的测试中被问过。这种情况在 bagging 方法中被称为 bootstrap,因为由于使用替换随机抽样,可能会再次选取相同的样本。
面试者 4 &面试者 5: 面试者 4 和面试者 5 重复上述相同的过程,从“问题集”中抽取相关问题的样本,测试候选人的沟通和管理技能,并相应地设定一些选择或不选择的概率。
结果:最后,候选人面试结束,他走出房间,让面试官根据“问题集”中对他进行评分的问题的子样本来分析他们各自对他技能的评分。最终,他们汇总所有个人意见,做出是否选择该候选人的最终决定。
这与合奏的方法完全相似。
让我们使用 python 尝试几种集成方法,并比较它们对应于几种单一模型的准确性。
注:jupyter 笔记本完整代码在 github 中。
为此,我使用 Kaggle 的广告成功数据集,并希望预测广告的成功。
1. 单款
a .决策树
*# Decision tree classifier with grid seacrh CV and model evaluation using accuracy score, precision score and AUC/ROC curve.*
**from** **sklearn.tree** **import** DecisionTreeClassifier
**from** **sklearn.model_selection** **import** GridSearchCV
parameters = {'max_features': [0.5,0.6,0.7,0.8,0.9,1.0], 'max_depth': [2,3,4,5,6,7],'min_samples_leaf':[1,10,100],'random_state':[14]}
clf = GridSearchCV(DecisionTreeClassifier(), parameters, cv=5, scoring='roc_auc')
clf.fit(X_train, y_train)
应用于示例数据的决策树评估指标:
AUC 评分为 85.84%。
决策树的精确度和准确性
决策树总是容易出现 过拟合 如果我们没有选择正确的参数,如树叶的最小样本、节点的最小样本、树的最*深度,那么随着深度的增加,模型将更精确地捕捉训练集的数据点,从而在训练数据集本身中产生出色的预测,但在新数据上却失败了。因此,集成方法是有助于减少方差从而解决单个决策树遇到的 过拟合 的技术之一。
b .单一逻辑回归分类器
*#Single Logistic Regression*
**from** **sklearn.linear_model** **import** LogisticRegression
log = LogisticRegression(random_state=0, solver='lbfgs')
log.fit(X_train, y_train)
y_pred = log.predict(X_test)
单个 Logistic 回归分类器应用于实例数据的评价指标:
AUC 评分为 83.84 %。
单一逻辑回归分类器的精度和准确度
2.相同分类器的集成
几个例子是随机森林、额外树分类器/回归器、线性回归器集成、逻辑回归分类器集成、SVM 集成等。
a. 随机森林——多个决策树的集合
来源:谷歌
**from** **sklearn.ensemble** **import** RandomForestClassifier
parameters = {'n_estimators':[700],'n_jobs':[-1], 'max_features': [0.5,0.7,0.9], 'max_depth': [3,5,7],'min_samples_leaf':[1,10],'random_state':[14]}
clf1 = GridSearchCV(RandomForestClassifier(), parameters, cv=5, scoring='roc_auc')
clf1.fit(X_train, y_train)
随机森林评价指标应用于实例数据:
AUC 得分为 86.53 %。
随机森林的精度和准确度
b.逻辑回归分类器集成。
当我们使用相同分类器的集合时,Sklearn 的 BaggingClassifier 用于获得 OOB(袋外)分数,这是在 bagging 中检查分类器性能的一个非常重要的分数,因为它给我们提供了在测试集上要达到的准确度的概念。众所周知,在训练期间,子样本被馈送到每个独立的分类器,但是在 bagging 技术中,同样的样本也可能被传递两到三次到任何其他单独的分类器。因此,在这种情况下,对于特定分类器来说是新的样本,即还没有看到的样本被称为袋外样本。它与测试集的目的相同,因为 OOB 实例是以前没有见过的东西。在使用 Bagging 分类器构建集成之后,我们可以使用‘Classifier . OOB _ score _’函数来获得 OOB 分数。
*# Multiple logistic regression classifiers using bagging Classifier.
# Number of logistic regression classifiers we are using here are 400.*logbagClf = BaggingClassifier(LogisticRegression(random_state=0, solver='lbfgs'), n_estimators = 400, oob_score = **True**, random_state = 90)
logbagClf.fit(X_train, y_train)
bagging 分类器(多重逻辑回归分类器)的评价指标:
AUC 评分为 84.44 %。
bagging 分类器(多重逻辑回归分类器)的精度和准确度
3。几种不同类型模型的集合
对于这个技术,我们可以使用 Sklearn 的 VotingClassifier 。
不同机器学*模型的集成
为了我的测试目的,我使用了 RandomForestClassifier、支持向量机和逻辑回归的集成。
**from** **sklearn.ensemble** **import** RandomForestClassifier, VotingClassifier
**from** **sklearn.svm** **import** SVC
**from** **sklearn.linear_model** **import** LogisticRegression
**from** **sklearn.naive_bayes** **import** GaussianNB
rfClf = RandomForestClassifier(n_estimators=500, random_state=0) *# 500 trees.*
svmClf = SVC(probability=**True**, random_state=0) *# probability calculation*
logClf = LogisticRegression(random_state=0)
*#nbclf = GaussianNB(random_state=0)*
*# constructing the ensemble classifier by mentioning the individual classifiers.*
clf2 = VotingClassifier(estimators = [('rf',rfClf), ('svm',svmClf), ('log', logClf)], voting='soft')
*# train the ensemble classifier*
clf2.fit(X_train, y_train)
投票分类器(多模型集成)的评价指标:
AUC 得分为 84.92 %
投票分类器的精度和准确度(多个模型的集成)
增压:
boosting 背后的主要思想是在连续迭代中将弱学*者转化为强学*者。
让我们再次以面试为例来理解助推的概念。
假设一个应聘者在五家不同的公司参加了五次面试。根据他在这五次面试中的表现,他想对自己的技能进行评估。
面试 1 —公司 1 —他只能回答几个问题,但他知道可能会再次被问到什么类型的问题。他回家学*。他的学*速度提高了。
面试 2 —公司 2 —在这里,他比第一家公司做得更好,因为他学会了他在第一次面试中错过的所有概念,但仍然没有被选中。他回到家,进行了更多的学*,从而纠正了他在前两次面试中犯的所有错误。
面试 3 和面试 4 同样,他面临着同样的问题,当他参加第 5 次面试时,他成了专家,几乎能回答所有的问题。
最终,我们在这里看到的是候选人接触到不同的问题样本,他逐渐学*和提高自己,直到成为专家。结果是,在从错误中吸取教训并改正自己之后,他可以给自己一个最好的评价。每一步都有一个学*率。同样是助推的概念。
我将在数据集上实现 boosting 算法 XGBoost。
**import** **xgboost** **as** **xgb**
xgb_clf = xgb.XGBClassifier(max_depth=3,n_estimators=300,learning_rate=0.05)
xgb_clf.fit(X_train,y_train)
XGBoost 的评估指标应用于示例数据:
AUC 得分为 86.46 %。
XGBoost 算法的精度和准确度
7。结论
让我们比较一下单一模型和集合模型的准确度、精度和 AUC 得分。
- 准确率: Boosting 算法‘XGBoost’在准确率上领先。
- 我们还可以注意到,集成分类器'随机森林'和'多重逻辑回归分类器'分别比单一分类器'决策树'和'逻辑回归'具有更好准确性。
各种算法的准确率评分。
2。精度分数: Boosting 算法' XGboost '在精度分数上领先。
- 我们可以注意到,集成分类器'随机森林'和'多重逻辑回归分类器'分别比单一分类器'决策树'和'逻辑回归'具有更好精度。
各种算法的精度评分。
3。 AUC 得分: Bagging 算法Random forest 在 AUC 得分上领先,尽管与 XGboost 的 AUC 得分有非常微小的差异。
- 我们可以注意到,集成分类器'随机森林'和'多重逻辑回归分类器'分别比单一分类器'决策树'和'逻辑回归'具有更好 AUC 分数。
各种分类器的 AUC 分数。
因此,我们可以得出结论,boosting 方法在我们的数据集上表现最佳,我们最终可以部署 XGBoost 模型,因为它将最精确地预测广告活动的成功,这是我们问题陈述的主要议程。
然而,这取决于我们正在处理的数据的类型。
此外,我们可以推断,与单一模型相比,装袋和增压方法总是表现得更好,精确度更高。
请添加任何额外的想法,评论,因为这将有助于我更好地理解事情。
感谢阅读!!
机器学*的类型
探索如何根据人类互动和训练对 ML 算法进行分类。
作者图片
自从我在上写了第一篇关于什么是机器学*(ML)以及编程范式如何随着时间的推移而改变的文章并描述了一些用例/应用以来,已经有一段时间了。这一次,我将分享如何从不同的角度看待机器学*和人工智能,具体涵盖以下两个领域:
1.不同 ML 算法的训练过程中涉及到多少人为的交互。
2.培训是如何进行的。
在进入每个领域之前,让我们澄清一些关于机器学*过程的概念。如果你熟悉机器学*的工作原理,你可以跳过这一节。
机器学*的高层次定义可以被视为:
给定一些你正在分析的代表一个领域(销售、政治、教育)的 数据 和一个 算法 ,计算机能够 从这些数据中学* 并在其上检测某些 模式 的能力。其次是能够告诉( 预测 )你并确定新数据的类型或者至少是它的近似值。
对这个概念要有所保留,因为有太多关于 ML 实际上是如何执行的背景知识。
换句话说,计算机能够从输入数据中通过 训练 (学*)来检测 模式 。这个过程是高度迭代的,需要*量的调整。例如,它需要检查预测值与实际值有多远或多近,然后通过调整其参数来进行自我修正,直到达到某个点,在该点上模型肯定足够精确,可以使用。
好了,现在我们对这个过程有了一个*概的了解。让我们进入机器学*的类型。
ML 算法和人工干预。
这一领域的机器学*系统可以被视为训练过程中的 【监督】即人类互动 的数量。这些分为 3 个主要类别,我将尝试用例子来说明以下定义。
1。监督学*
假设你是当地一家书店的老板。
照片由作者拍摄
您的女儿 Ana 是一名数据科学家,她提出采用您库存系统中记录的图书数据集,并实施一个新系统来加快销售新书的登记。
安娜知道这些书的一些特点:
- 类型(小说、非小说、奇幻、惊悚)
- 精装书,如果有精装书
- ISBN,这本书的商业标识
- 标题,页数
- 书籍或封面图片的摘录
- 作者
现在,想象一下必须在书架中分配一本书。如果你在网上查找元数据,把书放在小说书架上,这就很容易了。
然而,在现实中,如果你每天收到多达 200 本书,你不能独自完成这项工作,在系统上手动输入数据很容易出错,你可能会把书放错书架。这种错位可能会导致收入减少,因为当新顾客进来时并不满意,因为他们找不到这本书,即使它实际上在商店里。
Ana 的新系统非常简单,只需给系统输入你要找的书的摘录或封面,它就能告诉你这本书应该放在哪个书架上。
这个特殊的例子被称为 分类 ,因为系统只是在帮助你根据你作为用户提供给它的某些特征来分类(组织)一些数据。
这种系统的一个真实例子是谷歌照片。
另一种监督算法是在给定一组称为预测器的特征或特性的情况下,预测一个数值。
现在,你可能会问这是怎么发生的?
一个例子就是使用 回归 。一种常见且广泛使用的算法称为线性回归,其目的是预测一个连续值作为结果。
这实际上转化为具有一组特征或变量,以及一组匹配该输入特征的标签,并且您想要算法做的只是学*如何" 拟合 " 与这些特征相关联的权重(参数),以给出更接近真实值的近似值。
2。无监督学*。
无监督 算法涵盖了我们没有标签或真实值可以对比的所有情况。相反,我们确实有数据集,模型将根据数据如何 分组 在一起,它如何检测模式,以及它是否显示某些行为来训练和预测。
这种类型的机器学*的一个典型例子是 聚类 ,根据相似性对数据进行分组。真实的例子包括推荐系统,例如:
- 零售商网站,如亚马逊和 Zalando。
- 媒体/流媒体系统,如网飞、Youtube 等。
在这种类型中使用了许多其他算法,例如,异常检测可能涵盖信用卡欺诈或帐户盗用情况,这些情况可以通过使用 ML 来预防和预测。
我最近发现的另一种我最喜欢的无监督学*算法是那些用于数据可视化的算法,如 t-SNE 或 t-分布式随机邻居嵌入。
我在几个宠物项目中使用 t-SNE 进行 情感分析可视化 ,你可以实际看到集群在高维空间中形成,同时使用降维。
3。强化学*。
很多书和网站都提到这些算法,比如 野兽 但是我喜欢想到冰淇淋的顶部。由于该领域正在取得的成就,目前成为一个热门话题,我稍后会提到这一点。
在这种情况下你会怎么做?最有可能的是,你会玩几次,试图研究什么是最好的动作和最好的路线,你可以使用,最终从庇护所救出狗。这类似于强化算法要做的事情,你可以这样想:
1.给你一个 环境 。(视频游戏环境中的空间),代表空间上某些变量的状态。就像路障或蟋蟀从天而降。
2.事件期间要执行的操作。(动作,像向上、向下、向建筑者扔球),值得注意的是,有些动作可能会被奖励为“好”,而其他一些则被奖励为“坏”,这意味着它会降低你实现目标的能力。
3.代理人(视频游戏中的角色,救援者,或建设者),他们将采取行动以尽可能低的成本实现目标。
它的工作方式是代理观察环境,试图执行行动,并根据这些行动将获得奖励或没有。潜在的情况是,代理将自己学*什么是追求的最佳方法/策略,以获得最*数量的积分(奖励)。
迄今为止,我见过的用它构建的最好的系统之一是:
- 捉迷藏,由 OpenAI 开发。在那里他们使用多智能体的方法教孩子们玩捉迷藏。有两个代理“隐藏者”和“搜索者”,通过观察环境,他们能够学*甚至没有提供的行为/行动,如使用障碍通过障碍。如果你想深入了解它是如何工作的,你可以看看下面的视频和 论文
来源:多智能体捉迷藏
- 视频游戏,如 StarCraf 或 AlphaStar。代理人与人类对弈,事实上它在几秒钟内就击败了世界上最好的玩家或其他代理人。为了实现这一目标,开发 AlphaStar 的团队不仅使用了强化学*,还使用了监督学*来训练神经网络,这些网络将创建玩游戏的指令。如果你好奇是怎么做到的,可以查看他们的博文 这里 。
到目前为止,我们已经学*了不同类型的机器学*系统,这些系统基于人类与这些系统的互动的应用程度。现在我们可以继续探索接下来的 2。
ML 算法和训练过程。
在前面的部分中,我们概述了训练过程是您的算法将如何“学*”最佳参数来进行预测。话虽如此,培训本身是一门艺术,可以根据用例、可用资源和数据本身以不同的方式进行。
这分为两种:
- 批量学*。
- 在线学*,也称为增量学*。
让我们看看这些是如何工作的:
1。批量学*
批量学*是指用一次获得的所有数据训练模型,而不是增量学*。通常执行这个动作需要很多时间。想象一下,如果不能一次训练更多的数据,也要训练数 TB 的数据。但是你为什么需要这么做呢?
例如,由于业务或用例的性质,以特定频率(如每周/每月)交付的报告或决策可能不需要实时培训。
这种类型的培训通常是离线完成的,因为它需要很长时间(几小时/几天/几周)才能完成。
培训是如何进行的?
- 模型被训练。
- 模型被部署并投入生产。
- 模型在没有进一步“学*”的情况下持续运行。
这个过程叫做离线学*。现在,您可能想知道,如果我的数据或用例发生了变化,会发生什么?您需要针对合并新数据或特征再次训练您的模型。
一个实际的例子,在我以前的工作中,一些模型以批量学*的方式部署到生产中,一些模型不需要如此频繁地刷新,所以它可以*约每周发生一次。生成新模型的另一个原因是查看监控系统中部署的指标,并检查模型的性能是否正在下降。
这些类型的模型与 IO、CPU、内存或网络等资源密切相关,因此在决定采用哪种方法之前,记住这一点非常重要。如今这可能真的很昂贵,但与此同时,你可以利用云平台提供的现成解决方案来做到这一点,例如 AWS Batch 、Azure Machine Learning——Batch Prediction,或谷歌云人工智能平台。
接下来,让我们谈谈在线学*
2。在线学*
你有没有想过网飞、迪士尼、亚马逊 Prime Video 是如何推荐你看什么的?或者为什么 Zalando 和亚马逊一直告诉你买这些惊人的裤子,可能会搭配一双白色的鞋子?你可能会说,是的,推荐系统,但更重要的是,它怎么能做得这么快?系统是如何快速适应变化的?
您可能又答对了,这是因为训练是动态进行的,这意味着数据在到达系统时就被处理。这种方法适用于持续接收数据的系统,如零售商,或者需要快速适应变化的系统,如新闻网站或股票市场。
就如何进行培训而言,如下所示:
- 数据被分成小块或小批。
- (连续地)训练模型。
- 持续评估/监测。
- 部署到生产。
进行在线学*的一个好处是,如果你没有足够的计算资源或存储,你可以在训练时丢弃数据。为您节省*量资源。另一方面,如果您需要重放数据,您可能希望将其存储一段时间。
正如我们处理的每一个系统一样,这种类型的培训有其优点和缺点。使用这种方法的一个缺点是,模型的性能可能会很快下降,或者最终由于数据可能会快速变化,您可能会注意到在某个时间点预测准确性的下降。出于这个原因,也是我很少看到的一个经常谈论的话题,就是建立良好的监控系统。这些将帮助您的团队或公司预防和理解何时开始改变或调整模型以使它们有效。
亮点
你已经走了这么远,我希望你喜欢它。最初,我们讨论了机器学*如何工作,然后深入研究了机器学*如何根据人类与算法交互的角度进行划分。最后,我试图用我在周围看到的很酷的例子和应用程序来尽可能多地描述这些类型的监督、非监督和强化学*。
我可能会开始张贴我在旅途中学到的更多实用的东西,并愿意分享。#分享伤疤。
保重,很快再见。
PySpark 3 中的采样类型
Spark 中采样技术的解释及其在 Pyspark 中的具体实现步骤
抽样 是为特定的案例研究从数据集中确定一个有代表性的亚组的过程。抽样代表重要的研究和商业决策结果。因此,在所提供的技术中使用最合适和最有用的取样方法是至关重要的。本文主要面向希望在采样子领域使用 Apache Spark 最新增强功能的数据科学家和数据工程师。
Spark 支持的采样方法有两种:sample和sample by详见后面的章节。
1.样本()
如果使用 sample() , 简单随机抽样 ,数据集中的每个元素都有相似的机会被优先选择。以随机指定的分数比率从数据集中选择变量,而不基于任何变量进行分组或聚类。
该方法使用 3 个参数。with replacement参数默认设置为False,因此该元素只能被选作样本一次。如果该值被更改为 真 ,则可以再次在同一采样中选择一个样本值。由于可以多次选择元素,因此with replacement = True和with replacement = False的数量可能略有不同。
另一个参数是 分数 字段,需要填写,在 Spark 的官方文档 中有说明,不能除以指定的百分比值。
如果任何数字被分配给 种子 字段,它可以被认为是分配一个特殊的 id 给那个采样。这样,每次运行脚本时都会选择相同的样本。如果该值保留为 无 ,则每次创建不同的采样组。
下面我用 Kaggle 数据集 添加一个我在本地 Jupyter 笔记本上编码的例子。
在下面的例子中,with replacement值被设置为 True ,的 分数 参数被设置为 0.5 , 种子 参数被设置为 1234 ,这是一个可以赋值的 id
图一。 sample()方法同【with replacement = True】(图片由作者提供)****
在下面的例子中,with replacement值被设置为 False ,的分数被设置为 0.5 , 种子 参数被设置为 1234******
图二。 sample()方法同【with replacement = False】*(图片由作者提供)*****
下面,有对 sample() 方法的详细解释。
样本(替换=无,分数=无,种子=无)
该方法返回一个 数据帧 的采样子集。
参数:
有替换的 —有替换或没有替换的样本(默认值设置为假)。**(可选)**
— withReplacement=True: 同一个元素在样本的最终结果集中有被重现多次的概率。
— withReplacement=False :数据的每一个特征只被采样一次。
分数 —要生成的行的分数,范围[0.0,1.0]。**(必选)**
种子 —抽样的种子(默认为随机种子)**(可选)**
注: 不保证准确提供给定 数据帧 的总计数的指定分数。
2.抽样依据()
另一种可以作为采样方法的技术是sample by()。******
第一个参数是 col 字段,它决定在采样过程中对哪个变量进行子分组和采样。
例如,如果位置写在该域中,将根据位置值进行采样。位置下的值将包含在采样中的百分比在 分数 字段中确定,这是另一个参数。不是强制填充,如果不是,则设置为 0,没有指定分数率的值将不包括在抽样中。********
****图三。位置特征在数据集中的分布(图片由作者提供)
在下面的例子中,选择了数据集字段中具有 CA 的元素的 50% ,具有 TX、的元素的 30% ,以及具有 WI 的元素的最后 20% 。在本例中, 1234 id 被分配给 种子 字段,也就是说,每次运行脚本时都将选择用 1234 id 选择的样本。如果 种子 值保留为 无 ,则在执行过程中每次都会选择不同的样本。
****图 4。所有“位置”变量的值都在“分数”参数中指定(图片由作者提供)
下面再举一个例子,dataset 字段中带有 CA 的元素的 60% ,带有 TX 的元素的 20% 被选中,由于没有指定所有其他元素的百分比,所以它们没有包含在最终的采样结果集中。在本例中,再次将 1234 id 分配给 种子 字段,也就是说,每次运行脚本时都将选择用 1234 id 选择的样本。如果 种子 值保留为 无 ,则在执行过程中每次选择不同的样本。
****图 5。在“分数”中只指定了“位置”变量的两个值(图片由作者提供)
sampleBy(列,分数,种子=无)
这种方法返回一个分层样本,而不根据每个层上给出的分数进行替换。
参数:
col —定义地层的列
分数 —每个地层的取样分数。如果未指定地层,其分数将被视为零。
****种子—随机种子 id。
非常感谢您的提问和评论!
参考
每个数据科学爱好者都应该知道的结构化数据类型
了解数据类型的分类
艾萨克·史密斯在 Unsplash 上拍摄的照片
在我的上一篇博客中,T4 对数据科学中的统计数据的需求变得很明显,现在是时候深入了解统计方法了。这将是一系列的博客文章和视频(在我的 YT 频道)。
你是谁?
我开始了这一系列关于统计和概率的博客,以帮助所有的编码人员和分析人员理解这些概念和方法。你熟悉 Python 编程,并试图更好地掌握统计学,以掌握数据科学技能。
数据和数据分析的增长
我们知道,数据分析的发展已经超出了其最初的预期范围,这是因为技术的快速发展,越来越多的数据的产生,以及各种学科对定量分析的积极使用。
由于这种基础设施的发展,我们可以达到一个阶段,我们有多种数据源,如传感器,CRM,事件,文本,图像,音频和视频。
非结构化数据
现在,*部分生成的数据是非结构化的,即没有预定义模型/结构的数据。例如,图像是像素的集合,文本数据是没有特定预定义存储模型的字符序列,以及来自用户在 web 应用上的操作的点击流。
非结构化数据面临的挑战是,需要将其预处理为结构化数据,以便对其应用统计方法,并获取原始数据中包含的信息。
结构数据
当我们谈论结构化数据时,我们经常谈论表格数据(矩形数据),即数据库中的行和列。
这些表还主要包含两种类型的结构化数据:
- 数值数据
用数字表示的数据。它进一步表现为两种形式:
- 连续— 可以在一个区间内取任意值的数据。比如车速、心率等。
- 离散— 只能进行整数值的数据,如计数。例如,抛 20 次硬币的正面数。
2。分类数据
只能接受代表可能类别的一组特定值的数据。这些也被称为枚举,枚举,因素,或名义。
- 二进制— 分类数据的一种特殊情况,其特征是二分的,即只能接受 0/1 或真/假。
- 序数— 具有明确排序的分类数据。例如,餐厅的五星评级(1、2、3、4、5)
但问题来了,为什么需要了解数据?答案是,如果不知道数据的类型,你将无法运用正确的统计方法来处理这种类型的数据。
例如,如果 dataframe 中的一列有序号数据,我们就必须对它进行预处理,在 python 中,scikit-learn 包提供了一个OrdinalEncoder
来处理序号数据。
下一步是深入研究结构化数据,以及我们如何使用第三方包和库来操作这种结构。我们主要有两种类型的结构或数据存储模型:
- 矩形的
- 非矩形
矩形数据
数据科学中的*多数分析都是用矩形二维数据对象完成的,如数据帧、电子表格、CSV 文件或数据库表。
这主要由代表记录(观察)的行和代表列(特征/变量)的行组成。另一方面,Dataframe 是一种特殊的数据结构,具有表格格式,提供超高效的操作来操作数据。
数据帧是最常用的数据结构,在这里介绍一些定义很重要:
数据帧
矩形数据结构(像电子表格一样),用于统计和机器学*模型的高效操作和应用。
特征
数据帧中的一列通常被称为特征。
同义词-属性、输入、预测值、变量
胜负
许多数据科学项目涉及预测结果——通常是/否结果。
同义词—因变量、响应、目标、输出
记录
数据帧中的一行通常被称为记录。
同义词—案例、例子、实例、观察、模式、样本
举例:
板球比赛数据的数据帧
关系数据库表有一个或多个列被指定为索引,本质上是一个行号。这可以极*地提高某些数据库查询的效率。在 pandas dataframe
中,根据行的顺序创建一个自动整数索引。在pandas
中,还可以设置多级/分层索引,以提高某些操作的效率
非矩形数据
除了矩形数据之外,我们还有其他几种属于非矩形数据的数据结构。
地理位置分析中使用的空间数据结构更复杂,不同于矩形数据结构。在对象表示中,数据的焦点是一个对象(如一个公园)及其空间坐标。相比之下,字段视图关注的是小空间单元和相关度量值(例如像素亮度)。
图形数据结构用于表示物理的、社会的和抽象的关系。例如,脸书或 Twitter 将网络中人们之间的联系表示为社会关系图。图结构对于某些类型的问题是有用的,例如网络优化和推荐系统。
在数据科学中,每种数据类型都有一套特定的方法。本系列的重点是矩形数据,它构成了预测建模的基础构件。
接下来…
现在,我们已经清楚地了解了我们必须经常处理的数据类型,我们现在可以开始研究处于统计和数据科学核心的基本数据分布。
在下一篇博客的中,我们将讨论高斯分布和数据中位置和可变性的其他基本估计。我们将学*如何使用 NumPy 生成正态分布数据,并使用 matplotlib 之类的库绘制数据。
数据科学与 Harshit
通过这个渠道,我计划推出几个涵盖整个数据科学领域的系列。以下是你应该订阅频道的原因:
- 本系列将涵盖所有必需/要求的高质量教程,涉及每个主题和子主题,如 Python 数据科学基础。
- 解释了数学和推导为什么我们在 ML 和深度学*中这样做。
- 与谷歌、微软、亚马逊等公司的数据科学家和工程师以及*数据驱动型公司的首席执行官的播客。
- 项目和说明实施到目前为止所学的主题。了解新的认证、训练营以及破解这些认证的资源,例如 Google 举办的 TensorFlow 开发者证书考试。
请随时在 Twitter 或 LinkedIn 上与我联系。
更好的正态分布的变换类型
正态分布的必要性
统计分析中最常见的假设之一是正态性。你同意吗?
你好,世界,这是我为数据科学社区写的第一篇博客。在这篇博客中,我们将看到各种类型的数据转换,以更好地适应正态分布(高斯分布)。
我们知道,在回归分析中,响应变量应该是正态分布的,以获得更好的预测结果。
*多数数据科学家声称,当他们转换自变量时,他们会得到更准确的结果。这意味着独立变量的偏斜校正。偏斜度越低,结果越好。
变换无非是取一个数学函数,并将其应用于数据。
让我们开始吧!
概观
- 对数变换
- 平方根变换
- 相互转化
- 博克斯-考克斯变换
- Yeo-Johnson 变换(奖金)
为了更加清晰,请访问我的 Github repo 这里
1.日志转换:
数值变量可能具有由异常值、高指数分布等引起的高偏斜和非正态分布(高斯分布)。因此,我们选择数据转换。
在对数变换中,x 的每个变量将被以 10 为底、以 2 为底或自然对数的 log(x)代替。
import numpy as np
log_target = np.log1p(df["Target"])
上图是原始数据和对数转换数据的比较。在这里,我们可以看到转换数据中的偏斜度降低了。(最佳偏斜值应该接近零)
2.平方根变换:
这一转变将对分配产生适度的影响。平方根变换的主要优点是,它可以应用于零值。
这里 x 将被平方根(x)代替。它比对数变换弱。
sqrt_target = df["Target"]**(1/2)
3.相互转换:
在这个变换中,x 将替换为 x 的倒数(1/x)。
互易变换对分布的形状几乎没有影响。此转换只能用于非零值。
reciprocal_target = 1/df["Target"]
变换数据的偏斜度增加。
4.Box-Cox 变换;
这是我最喜欢的转换技术之一。
Box-cox 变换对许多数据性质都非常有效。下图是 Box-cox 变换的数学公式。
考虑从-5 到 5 变化的所有λ值,并选择数据的最佳值。“最佳”值是导致分布的最佳偏斜度的值。当λ为零时,将发生对数变换。
from scipy.stats import boxcox
bcx_target, lam = boxcox(df["Target"])
#lam is the best lambda for the distribution
博克斯-考克斯变换
在这里,我们注意到 Box-cox 函数降低了偏斜度,它几乎等于零。工作良好;)
对于这种转换,值严格地为正。
5.杨-约翰逊变换;
这是一种较老的变换技术,非常类似于 Box-cox 变换,但不要求值严格为正。
这种转变也有能力使分布更加对称。
from scipy.stats import yeojohnson
yf_target, lam = yeojohnson(df["TARGET"])
约-约翰逊变换
Yeo-Johnson 变换工作得很好,比 Box-cox 变换更好。
结论:
在这篇博客中,我们看到了不同类型的变换(有一个额外的最古老的变换技术)来更好地适应常态。杨永强永远不会是正确的选择。对于某些类型的数据,Box-cox 会工作得更好,所以我们必须使用最好的转换技术。
在下一篇博客中,我们将看到均匀分布。😃
通过 LinkedIn 联系我;)
欢迎评论:)
PySpark 分类
分类问题如何使用 PySpark?
照片由 Jair Lazaro 在 Unsplash 上拍摄
这是一个如何在分类问题中使用 PySpark 的快速学*。这里的目的是根据不同的特征对患者进行分类,以预测他们是否患有心脏病。对于此示例,使用了 LogisticRegression,它可以导入为:
**from** **pyspark.ml.classification** **import** LogisticRegression
让我们来看看这个数据集。它被命名为 UCI 心脏病数据集,你可以从这里下载。
上面显示了前五行。目标特征被用作我们的输出,它或者是 1 或者是 0。我们希望根据其他 13 个可用特征来计算,即年龄、性别、cp 等。
为了找出每个特征的类型,我们可以使用。printSchema() 函数为:
下一步是将这些特征转换成矢量;我们需要导入一个合适的库。
from pyspark.ml.feature import VectorAssembler
在 PySpark 中,除了目标之外的所有列都需要转换成一个向量,我们称之为 features。
assembler = VectorAssembler(
inputCols=[‘age’, ‘sex’, ‘cp’, ‘trestbps’, ‘chol’, ‘fbs’, ‘restecg’, ‘thalach’, ‘exang’, ‘oldpeak’, ‘slope’, ‘ca’, ‘thal’],
outputCol=”features”)output=assembler.transform(heart)
我们可以在下面看到,features 列是一个基于所有 13 列的向量。
为了训练我们的模型,我们将“特征”和“目标”组合起来作为输入/输出。
final_data = output.select(“features”, “target”)
然后,我们可以将 final_data 拆分为训练和测试,如下所示:
train, test = final_data.randomSplit([0.7, 0.3])
LogisticRegression 被用作我们的分类方法,labelCol 需要被称为目标特征,而 featuresCol 是已定义的特征,它已经被表示为向量。
lr = LogisticRegression(labelCol =”target”, featuresCol =”features”)
在下一步中,我们可以训练我们的算法,这非常类似于 scikit,并查看我们的测试用例的预测输出。
model=lr.fit(train)
predict_train = model.transform(train)
predict_test = model.transform(test)
predict_test.select("target", "prediction").show(10)
下图显示了我们前十行中的目标和预测;1 例预测不准。
为了评估我们的模型,可以导入 BinaryClassificationEvaluator。
from pyspark.ml.evaluation import BinaryClassificationEvaluatorevaluator = BinaryClassificationEvaluator(rawPredictionCol ='rawPrediction', labelCol ='target')predict_test.select("target", "rawPrediction", "prediction", "probability").show(5)
该评估器使用 AUC 并比较预测列“预测”和“目标”训练和测试的值分别为 93%和 87%。
print(“The area under ROC for train set is {}”.format(evaluator.evaluate(predict_train)))print(“The area under ROC for test set is {}”.format(evaluator.evaluate(predict_test)))
对于这个问题,我只使用了默认的超参数,例如 maxIter 和 regParam,但是我们也可以调整它们以获得更好的模型。
完整的代码可以通过这个链接访问。
UDAF 和聚合器:Apache Spark 中数据集的定制聚合方法
Apache Spark 中的聚合
数据记录的聚合是数据分析工作的必要组成部分,因此 Spark 旨在提出一个强*的聚合框架,以满足用户在各种情况下的需求。
使用 rdd 作为分布式数据收集的表示,Spark 中的聚合更早地与(Hadoop 世界的)map-reduce 框架的缩减部分的概念相一致。然而,随着 Dataframe 和最新数据集表示的引入,聚合框架现在更类似于人们更熟悉的 SQL 世界中的聚合。
数据集的类型化和非类型化视图
在数据帧和数据集之间,前者是带有模式定义的数据记录的非类型化视图,而后者是类型化视图。在 Dataframe 中,每条记录都被视为值的集合(类似于表中的一行),并公开一个类似 SQL 的接口来执行转换。而在 Dataset 中,每条记录都被视为某种类型的对象,并公开一个编程接口来执行转换。但是,为了保持非类型化数据帧的概念以方便 SQL 和提高效率,Dataset 还通过编码器框架支持隐藏的非类型化视图。在我之前的博客中阅读更多关于数据集编码器框架的内容: Apache Spark 数据集编码器揭秘。任何 T 类型数据集的显式非类型化视图都由“Row”类型的数据集表示。
类型化和非类型化聚合:概述
考虑到类型化和非类型化表示,Spark 也提供了非类型化和类型化聚合。对于非类型化的聚合,只需提到可选的分组列和一个或多个列上的聚合表达式(由聚合函数组成)。这与 SQL 中完成聚合的方式非常相似。非类型化聚合的执行会导致显式的非类型化视图。Spark 使用户能够对属于任何类型的数据集执行非类型化聚合。另一方面,对于类型化聚合,用户必须提供一个聚合器对象(处理 T 类型数据集的 T 类型对象),以便在整个数据集上聚合,或者在返回聚合类型数据集的分组数据集上聚合。
数据集上的非类型化聚合:
数据集的无类型视图上的无类型聚合
/* Untyped View - A is collection of Rows with two columns col1 and col2 */
=> Dataset<T> A;/*Sum Aggregation on column col1 of A over entire collection*/
=> A.agg(functions.sum(A("col1")))/*Custom Aggregation on col1 of A over entire collection*/
=> A.agg(functions.udf(MyUDAF(A("col1"))))/*Sum Aggregation on col1 of A over grouped collection of A grouped via col2 */
=> A.grouby(A("col2")).agg(functions.sum(A("col1")))/*Custom Aggregation on col1 of A over grouped collection of A grouped via col2*/
=> A.grouby(A("col2")).agg(functions.udf(MyUDAF(A("col1"))))*All above aggregations return an untyped view (Dataset<Row>)*
数据集上的类型化聚合:
通过聚合器对数据集
/* Typed View - A is collection of type T objects where type T consists of two fields col1 and col2 */
=> Dataset<T> A;/* B an Aggregator instance aggregating multiple objects of type T to perform desired aggregation on T's field col1, The aggregated value is returned back via a field inside object of type V */
=> Aggregator<T,U,V> B;/*Aggregation on field col1 of A over entire collection*/
=> A.select(B.toColumn()) returns Dataset<V>/*Aggregation on field col1 of A over grouped collection grouped by Key Object derived from Type T */
=> A.groupbykey(KeyFunc, Encoder<K>).agg(B.toColumn())
===> Here KeyFunc is of type MapFunction<T,K> which output grouping Key Object of type K for a given record of type T
===> This grouped aggregation outputs Dataset of Tuple consisting of Key Object of type K and Aggregated object of type V, viz., (Dataset<Tuple<K,V>>)
自定义非类型化聚集:UDAF
虽然,针对非类型化聚合的支持,Spark 已经提供了多种这样的聚合函数,但是也支持构建一个定制的非类型化聚合函数,称为 UDAF。通过扩展“org . Apache . Spark . SQL . expressions”包中的“UserDefinedAggregationFunction”类,并在基类中覆盖以下方法的实现,可以在 Spark 中创建 UDAF:
/*Return schema for input column(s) to the UDAF, schema being built using StructType*/
=> public StructType inputSchema()/*Return schema of aggregation buffer, schema being built using StructType */
=> public StructType bufferSchema()/*DataType of final aggregation result*/
=> public DataType dataType()/*Initialize aggregation buffer*/
=> public void initialize(MutableAggregationBuffer buffer)/*Update aggregation buffer for each of the untyped view (Row) of an input object*/
=> public void update(MutableAggregationBuffer buffer, Row row)/*Update current aggregation buffer with a partially aggregated buffer*/
=> public void merge(MutableAggregationBuffer buffer, Row buffer)/*Evaluate final aggregation buffer and return the evaluated value of DataType declared earlier */
=> public Object evaluate(Row buffer)
你可以在定制的 UDAF 类中声明额外的字段(在 UDAF 构造函数中可选地初始化这些字段)和额外的方法,以便使用这些内部的覆盖方法来实现聚合目标。此外,需要注意的是[只有某些数据类型](http://BooleanType ByteType ShortType IntegerType LongType FloatType DoubleType DecimalType TimestampType DateType StringType BinaryType)被允许用于最终结果类型以及定义输入和缓冲模式。
UDAF 示例:假设有一个如下声明的类型‘raw data wrapper ’,每个包装器由一个键字段和一个反映射字段组成。
对于“RawDataWrapper”类型的数据集,这里有一个 UDAF 声明,它聚集了 RawDataWrapper 类型的记录之间的反映射。
上述申报的 UDAF 的用途如下所示:
自定义类型聚合:聚合器
类型化聚合通过abstract generic ' Aggregator<IN,BUF,OUT > '类(存在于包' org . Apache . spark . SQL . expressions '中)来支持。用户可以定义自己的自定义聚合器,方法是使用为 IN(输入记录类型)定义的类型、为 BUF(聚合缓冲区)定义的类型和为 OUT(输出记录类型)定义的类型对其进行扩展,并在基类中覆盖以下方法的实现:
/* return Encoder for aggregation buffer of type BUF. This is required for buffer ser/deser during shuffling or disk spilling */
=> public Encoder<BUF> bufferEncoder()/* return Encoder for output object of type OUT after aggregation is performed */
=> public Encoder<OUT> outputEncoder()/* return updated aggregation buffer object of type BUF after aggregating the existing buffer object of type BUF with the input object of type IN*/
=> public BUF reduce(BUF buffer, IN input) ()/* return updated aggregation buffer of type BUF after merging two partially aggregated buffer objects of type BUF */
=> public BUF merge(BUF buffer1, BUF buffer2)/* return output object of type OUT from evaluation of aggregation buffer of type BUF */
=> public OUT finish(BUF arg0)/* return buffer object of type BUF after initializing the same */
=> public BUF zero()
与 UDAF 类似,用户可以在自定义的聚合器类中声明额外的字段(在聚合器构造函数中可选地初始化这些字段)和额外的方法,以便使用这些内部的覆盖方法来实现聚合目标。
聚合器示例:下面是一个聚合器声明,它将在 RawDataWrapper 记录上实现与 UDAF 示例所描述的相似的聚合目标
上面声明的聚合器的用法如下所示:
比较:UDAF 和聚合器
如果聚合字段不涉及 ArrayType 或 MapType 等复杂类型,UDAF 和聚合器的效率是相似的。但是,聚合器在聚合复杂数据类型方面比 UDAF 效率更高。这是因为在 UDAF 的每次更新操作中,scala 数据类型(特定于用户)会转换为 catalyst 数据类型(catalyst 内部数据类型),反之亦然。对于聚合缓冲区中的复杂数据类型,这种转换在效率和内存使用方面变得非常昂贵,使得 UDAF 在这些情况下与对等聚合器相比运行缓慢。在这篇博客中,为了实现相似的聚合目标,提供了 UDAF 和聚合器的示例,当两者都在 180 GB 的样本数据上运行时,UDAF 需要*约 40 分钟,而聚合器只需要*约 7 分钟就可以在集群上完成一组相似的资源。
然而,尽管 UDAF 提供了类似 SQL 的易用性,但聚合器的聚合方式在编写聚合逻辑时提供了更*的灵活性和编程优雅性。
此外,与*量可供使用的非类型化聚合函数相比,类型化聚合函数的库很少(基于聚合器概念)。因此,非类型化聚合流仍然是广泛使用的一种。但是,在未来的 Spark 版本中,类型化聚合器将被集成到非类型化聚合流中,以便用户可以通过聚合器使用已经可用的非类型化聚合函数库以及高效灵活的定制聚合函数。很明显,这将最终导致普遍使用 UDAF 来定义自定义聚合函数的做法遭到反对。
如果您对 Spark 聚合框架有更多的疑问,请随时在评论区提问。
视频对象检测终极指南
20/20 年前的一切(计算机视觉)
在过去的十年中,在机器学*领域,尤其是计算机视觉领域,已经做了*量的工作。从 Google 的 Inception 等高级分类算法到 Ian Goodfellow 在生成对抗网络方面的开创性工作,以从噪声中生成数据,世界各地许多专注的研究人员已经处理了多个领域。有趣的是,在这十年的前五年,计算机视觉领域最具开创性的工作*多涉及图像处理,如分类、检测、分割和生成,而视频处理领域则没有得到深入探索。
Ibrahim Rifath 在 Unsplash 上的照片
轻微不平衡的一个明显原因是因为视频本质上是一系列图像(帧)的组合。然而,这个定义不能概括视频处理的全貌,这是因为视频处理给这个问题增加了一个新的维度:时间维度。视频不仅仅是一系列图像,而是一系列相关的图像。虽然这看起来是一个很小的差异,但研究人员能够以多种方式利用这个维度,而不是应用于单个图像。此外,由于视频数据(*小、相关注释)的复杂性以及训练和推理的昂贵计算,在该领域的突破一直较为困难。然而,最近随着 ImageNet VID 和其他*规模视频数据集的发布,越来越多的视频相关研究论文浮出水面。在本指南中,我们将主要探讨在视频检测中已经完成的研究,更准确地说,研究人员如何能够探索时间维度。
目录
后处理方法
最先出现的方法是应用于对象检测流水线的后处理步骤的修改。这是因为它需要更少的基础设施,并且不需要改变模型的架构。后处理方法仍然是每帧检测过程,因此没有性能提升(处理时间可能稍长)。然而,它可以实现相当*的准确性提高。
序列选择的图示。我们构造一个图,其中相邻帧中的框是链接的,如果它们的 IoU > 0.5。来源:Seq-NMS 研究论文(链接)
一种值得注意的方法是序列-NMS (序列非最*抑制),其通过动态编程基于“轨迹”上的其他检测对检测置信度进行修改。例如,由于遮挡、运动模糊或其他缺陷,可能导致对阳性对象的预测较弱,但是由于它将出现在从先前帧中提取的“轨迹”(重叠标准)中,因此置信度将会提高。这将有效地减少帧之间的错误检测或随机跳跃检测的数量,并稳定输出结果。
使用序列 NMS 在 mAP 中的绝对改善(%)。这种改进是相对于单图像 NMS 而言的。来源:Seq-NMS 研究论文(链接)
从上图可以看出,精确度已经有了相应的提高:相对于单图像 NMS,使用 Seq-NMS 的 mAP (%)的绝对提高超过了 10%,有 7 个类别的提高超过了 10%,而只有两个类别显示精确度下降。虽然这项工作是实现更好的视频检测的最初工作之一,但它并没有证明在准确性和性能方面是最好的。然而,明显的好处是,这种方法不需要训练本身,更像是一个可以插入任何对象检测器的附件。
多框架方法
三维卷积
例如,具有图像分类经验的开发人员的第一本能会想到某种 3D 卷积,这是基于对图像进行的 2D 卷积。这种架构的可能性似乎是合理的:遍历 n 个帧作为模型的输入,并输出对连续帧的顺序检测。这无疑是一个潜在的检测方向,因为它可以提取时空数据的低级特征,但具有 3D 卷积的卷积神经网络已被证明在处理 3D 图像(如 3D MNIST 或 MRI 扫描)时非常有用且富有成效。这就是为什么这些模型更多的是医学成像领域的突破,而不是视频检测。
尽管如此,探索在视频处理中使用 3D 卷积的研究论文的一个例子是,一个用于视频中动作检测和分割的端到端 3D 卷积神经网络。在研究论文中,首先将视频分成等长的剪辑,然后根据 3D CNN 特征为每个剪辑生成一组管道建议。然后,不同剪辑的管提议被链接在一起,并且使用这些链接的视频提议来执行时空动作检测。尽管该论文主要讨论了分割和动作检测,但是可以训练该体系结构的衍生物来执行对象检测。
就性能而言,由于多维矩阵的高计算量,在当前状态下,处理时间无法与实时(30 fps 或更高)一样快。在这一领域的进一步改进和研究可以改变方向,但扩展 3D 卷积的性能并不是一件容易的事情。说到准确性,我相信肯定能受到正面影响。检测的稳定性以及精度可以通过 3D 卷积来提高,因为该架构可以有效地利用时间维度(帧之间特征的集合)。然而,它目前只是基于其他最先进的 3D 卷积模型的一种推测。还没有一篇深入研究视频检测的论文。
递归神经网络
对于其他对序列数据有更多经验的人来说,人们可能倾向于考虑使用递归神经网络,如 LSTM。RNN 是一种特殊类型的网络,用于处理时序数据,包括时态数据。从这种架构中获益匪浅的一个领域是自然语言处理。例如,AWD-LSTM 的表现与最先进的伯特变压器模型不相上下,而参数却少得多。那么,它是否适用于帧完全连续的视频检测呢?像的具有时间感知特征图的移动视频对象检测和的看起来快和慢:记忆引导的移动视频对象检测已经取得了一些进展。
提高性能的 LSTM 层。来源:"Mobile Video Object Detection with Temporally-Aware Feature Maps", Liu, Mason and Zhu, Menglong, CVPR 2018.
在前者中,本文将快速单幅图像目标检测与称为瓶颈 LSTM 的卷积长短期记忆(LSTM)层相结合,以创建一种交织递归卷积架构。LSTM 层降低了计算成本,同时仍然在帧间改进和传播特征图。该白皮书旨在低功耗移动和嵌入式设备上实时运行,在移动设备上实现 15 fps。
使用 LSTM 层的*型和小型神经网络。来源:"Looking Fast and Slow: Memory-Guided Mobile Video Object Detection", Liu, Mason and Zhu, Menglong and White, Marie and Li, Yinxiao and Kalenichenko, Dmitry
在后者中,研究人员建议通过依赖相关的先验知识来利用场景的“要点”(短时间内复杂环境的丰富表现),这些先验知识是受人类如何识别和检测物体的启发而产生的。该模型的架构是通过将传统的特征提取器与仅需要识别场景要点(最小计算)的轻量级特征提取器交错。这有效地从捕获“要点”的关键帧为架构创建了长期记忆,该要点指导小型网络检测什么。本文还结合了强化学*算法来实现自适应推理策略。该论文提供了有希望的结果,例如在移动设备上 70 fps,同时在 ImageNet VID 上仍然实现了小型神经网络的最先进结果。
一个关键要点是,该架构是端到端的,这意味着它获取图像并输出屏蔽数据,需要对整个架构进行训练。因为我们正在处理视频数据,所以模型需要在*量数据上进行训练。
光流方法
为什么不能在视频上使用图像物体检测器?我们可以。另一种可能的处理视频检测的方法是对视频文件的每一帧应用最先进的图像检测器,如 YOLOv3 或面部检测器,如 RetinaFace 和 DSFD 。每一帧都将被用作模型的输入,视频结果可以像它们在图像上的平均精度一样精确。然而,将这些检测器直接应用于视频文件的每一帧面临着来自两个方面的挑战:
- 就速度而言,对所有视频帧应用单个图像检测器效率不高,因为主干网络通常又深又慢。将它应用于每个单个帧还会导致*量冗余计算,因为来自视频文件的两个连续帧通常不会有很*差异。
- 对于精度,检测精度受到在静止图像中很少观察到的视频中的劣化外观的影响,例如运动模糊、视频散焦、罕见姿势。
因此,在每个文件上应用检测器不是解决视频检测挑战的有效方法。然而,通过探索视频的时间维度,我们可以实现不同的方法来解决一个或两个问题。
什么是光流?
光流估计是一种估计由摄像机(背景)或对象运动引起的两帧视频之间的物体的表观运动的方法。输出通常是 2D 矢量场,其中每个矢量代表一个像素从第一帧到第二帧的位移矢量。
光流算法的彩色编码输出示例。来源:辛特尔光流数据集(http://sintel.is.tue.mpg.de/)
光流一直是计算机视觉的一个研究领域,自 20 世纪 80 年代以来一直在探索,最近又作为深度学*的一个有趣领域重新浮出水面,由 Flownet 开创。在此之前,最初的方法是微分的。例如,Lucas-Kannade 方法假设在所考虑的像素的局部邻域中流动基本上是恒定的,并且通过最小二乘法准则求解该邻域中所有像素的基本光流方程。但随着新的进展和新的光流数据集,如 Sintel,越来越多的架构浮出水面,一个比另一个更快更准确。
有多种架构可以利用这项技术。例如,迈向高性能以及许多其他使用光流来建立跨帧的对应关系(稀疏特征传播)。光流是当前开发视频对象检测的时间维度最多的领域,因此,出于某种原因。光流的结果越来越快,越来越准确。
稀疏特征传播提高性能
该体系结构以稀疏关键帧的概念运行。由于光流网络可以相对较小,因此这种网络所需的处理时间和计算能力小于物体检测器。因此,流水线的功能相当于 n 帧的循环。第一帧称为关键帧。这是对象检测器检测到的帧。因为现在检测器给出了所有对象的精确检测,所以检测将服从光流算法。在获得位移向量之后,下 n-1 帧的检测是已知的,并且循环重复。
稀疏特征在纸上传播开来:【https://arxiv.org/pdf/1611.07715.pdf
精确的多帧特征聚合
提高视频检测准确性的一种方法是多帧特征聚合。有不同的实现方式,但都围绕着一个想法:密集计算的每帧检测,同时从相邻帧到当前帧进行特征扭曲,并通过加权平均进行聚合。因此,当前帧将受益于直接帧以及一些进一步的帧,以获得更好的检测。这可以解决视频帧中的运动和裁剪主题问题。
论文多帧特征聚合:https://arxiv.org/pdf/1703.10025.pdf
一个这样的例子是研究论文流引导特征聚合(FGDA) 。流动引导的特征聚集聚集来自附近帧的特征地图,这些特征地图通过估计的流动很好地对齐。该架构是一个端到端的框架,在功能级别上利用时间一致性。
比较
在介绍完所有这些方法之后,我们可以通过一个比较表来总结这些方法的要点,以帮助理解这些方法与使用图像检测器的简单逐帧方法相比有何不同:
以上方法对比图。*这些方法在处理关键帧时可能会产生一点延迟。
虽然所介绍的方法是目前已经发表的,但随着视频对象检测成为一个更容易理解的话题,目前肯定有更多的研究正在进行。希望随着即将到来的会议,越来越多的突破可以观察到。从 2020 年开始的新十年,人们对更好的视力充满了希望!干杯!
UIUC 在线计算机科学硕士:个人剖析
伊利诺伊*学厄巴纳-香槟分校数据科学计算机科学在线硕士课程综述
米利安·耶西耶在 Unsplash 上拍摄的照片
在一个后 COVID 的反乌托邦或乌托邦取决于你看的是一杯水的哪一半,通过它可以管理、完成和授予学位的在线媒体的相关性不可能被夸*。除了百年一遇的疫情,还有很多其他原因可以让你考虑参加在线课程。也就是说,作为一个工作时间有限的专业人士,作为一个有孩子要照顾的家长,或者仅仅是不能在家门口进入世界一流的*学。上述理由的任何组合都可以使在线学位成为一个诱人的选择,甚至是唯一的选择。
介绍
我的故事始于 2018 年初。在钻研了毕业后职业生涯的第四年后,我意识到下一个合乎逻辑的步骤是获得硕士学位。考虑到我所在的竞争激烈的行业(研发)和不断变化的就业市场,我知道我必须承受更多的*脑损伤才能保持相关性。虽然我的学士学位是机械工程,但我当时作为开发工程师的工作要求我开发数据挖掘和数据可视化软件。结果,我对面向对象编程、数据结构和算法有了*量的了解和喜爱。再加上我意识到数据科学将会像互联网一样无处不在,影响深远,这让我毫不犹豫地去追求它。
我有一份全职工作,而且附近没有提供数据科学硕士学位的*学,所以我被要求在互联网上搜索在线课程。虽然我起初对在线学位持保留意见,主要是因为负面的含义和与之相关的耻辱,但我很快意识到这种冲动是没有根据的,事实上有一个快速增长的成功专业人士社区,他们完成了在线学位,取得了切实的积极成果。请注意,并不是所有的在线项目都是被认可的,甚至是合法的,因此强烈建议进行尽职调查。
应用程序
当时只有少数几个在线数据科学项目,而不是目前迅速扩*的清单。鉴于我实际上不会去任何一个校园,地点显然不是我考虑的因素。我主要担心的是该机构在计算机科学、学生评论和学费方面的声誉。因此,唯一入围的候选项目是伊利诺伊*学香槟分校提供的 MCS-DS 项目。鉴于 UIUC 在计算机科学方面排名全国第五,它的积极评价和负担得起的学费(600 美元/学分/小时,当时只是校园费用的一小部分),我没有进一步考虑,申请了夏季学期的入学。虽然 UT Austin 的在线数据科学硕士项目当时还不存在(首次提供-2021 年春季),但如果我面对两种选择,这将是一个非常困难的决定。也许会有一种微妙的倾向倾向于德州*学奥斯汀分校,因为它的学费低得多(约 300 美元/学分)。
然而,令我非常失望的是,几周后我被拒绝了。经过询问,我得到了一个相对笼统的答复,说明在面向对象编程、数据结构、算法和线性代数方面背景不足的申请人不适合这个项目,除非他们能够证明并非如此。不可否认,收到拒绝让整个项目对我更有吸引力;如果你愿意,可以称之为心理学,但在那之后,我更坚定地获得了录取。因此,我决定在这四个领域中的每一个领域注册几门短期 MOOCs 课程,并在秋季学期重新申请,申请中包含证书链接。这似乎起到了作用,我很高兴地收到了 UIUC 2018 年秋季 MCS-DS 队列的迟到录取通知(*约在学期开始前 4 周)。
体验
学术顾问:
学术顾问对注册、课程选择和其他与行政事务相关的后勤工作给予了极*的帮助和响应。我特别感激的是,他们经常增加容量,让学生注册他们想注册的课程,并总是增加容量,让学生参加他们需要按时毕业的课程。
学术严谨:
与任何*学一样,学术严谨性和教育质量因课程而异。有了 MCS-DS,提供它的课程和教员实际上与校园版相同。所研究的一些主题是详尽的,而其他的则处于入门或中级水平。就任务和项目而言,挑战的程度相当激烈;样板文件对程序或语法错误是不宽容的,项目提交被严格评分。同样,关于学生在团队项目上偷懒的报道也受到了重视,没有人获得毕业的免费通行证。然而,小考和考试的情况就不一样了。感觉通过测验太容易了——有些课程允许多次重考,而且考虑到*多数问题都是选择题,通过测验有时真的需要反复试验。考试更难,不允许重考,但*多数问题都是选择题,这意味着测试的理解水平有点浅。也许项目负责人应该设计一种考试形式,更严格地衡量学生对主题的掌握程度。
Coursera:
Coursera 充当了用于提供该计划的所有云基础设施之间的焦点。总的来说,MCS-DS 和 Coursera 之间的整合是相当无缝和用户友好的。作业可以轻松提交,样板文件运行良好,内容以高度结构化的方式组织。然而,从第一学期一直持续到最后一学期的一个难题是,在每学期开始时,注册课程添加到 Coursera 平台的延迟(长达一周)。虽然我不知道谁该为这种不便负责,但奇怪的是,它从未得到解决。
课程设置:
MCS-DS 最明显的问题是每学期提供的课程数量有限。在当时,每个核心领域的选项通常限于两个,甚至在机器学*广度课程的情况下限于一个。因此,我别无选择,只能休学一整个学期(2019 年秋季),一个学期后毕业。我明白这是一个项目在起步阶段的预期,也注意到随着它的成熟,额外的课程已经增加。
广场&松弛:
Piazza 被有效地用作学生、助教和教授可以互动的虚拟教室。与 Coursera 不同,课程总是在学期开始时就设置好并准备好加入。类似地,Slack 被用作一个更非正式的论坛,主要供学生在每个班级的单独频道和整个项目的一个总频道中进行互动。团队项目使用两种工具进行,这两种工具在促进沟通和丰富整体体验方面非常有效。
普罗克多:
考试由 ProctorU 进行,ProctorU 向学生单独收费,并为学生参加在线考试提供灵活的时间。他们的监考老师是专业的、辅助的和精通技术的,这使得这是一次总体上积极的经历。在每次考试之前,监考人会远程连接到您的计算机,然后打开您的麦克风和网络摄像头,并要求您彻底向他们展示您的即时环境,以减少作弊的可能性。有些人认为在线考试的完整性是虚假的,但老实说,在校园里作弊可能比在网上更容易。ProctorU 有相当多的技术故障,有时会导致考试推迟开始,甚至完全错过,不得不重新安排时间。随着在线考试越来越普遍,我希望他们将来能解决这些问题。
编程堆栈:
MCS-DS 的主力是,是的,你猜对了——备受喜爱的 Python。*多数课程只有 Python 版本,而其他课程允许使用 C++和 Java。Python 中使用的主要包有:Numpy、Matplotlib、Plotly、Pandas、OpenCV、Scikit-learn、NLTK、spaCY 和 SciPy。在必要的地方,其他课程提供了 Java、Javascript、R 和 SQL 的编程,但仅限于一些精选作业。此外,还不时使用 Docker、Jupyter Notebooks、Anaconda 和 GitLab。
教授:
在线学位有点像一种超现实的体验。不言而喻,你真的错过了宝贵的学生-教授互动。幸运的是,MCS-DS 项目的教师通过定期与学生接触,在很*程度上弥补了这一点。通信很少通过电子邮件进行,相反,Piazza 和 Zoom 经常被用来回答问题和举行现场会议。如果说有什么不同的话,那就是我实际上觉得我遇到我的教授的次数比获得一个校园学位还要多。也有很多同理心;教授们经常应要求适应学生们繁忙的日程安排。
助教:
我在 MCS-DS 的四个学期中最美好的经历可能就是与助教们的互动。UIUC 每 50 名学生分配一名助教,老实说,这已经足够了,因为我们的*部分作业都是用样板评分的,助教主要是回答学生的问题。每当课程报名人数过多时,项目协调员甚至会好心地分配额外的助教,我发现他们非常负责,这表明他们不愿意在教育质量上妥协。助教都非常聪明,他们中的许多人都是在相同领域广受好评的研究者。他们经常主动提供帮助,在许多情况下,平均响应时间不到一个小时(Piazza 证实了这一点)。
时间管理:
做出的个人牺牲是这个项目的决定性特征。周末几乎是不存在的,你可以期望平均每周花十个小时在每门课程上(在学期结束时会更多)。我正在冲刺四个学期的课程,结果我花了两个学期上了两个班,一个三个班,另一个一个班。对于像我这样的在职人士,我不建议每学期选一门以上的课,假设你也参加夏季学期,以便按时毕业(你必须在五年内毕业)。调整好自己的节奏,尽量提前安排好考试,以避免考试名额被占满,不得不安排一个不合适的时间。熟悉课程,如果你一个学期要上多门课,那么就尽量避免艰深的课程(这样的课程不多)。
课程
因为我显然不能复*我没有学过的课程,所以我强烈建议在你报名参加同一个科目之前,仔细聆听其他学过这个科目的学生。从某种程度上来说,一个糟糕的开始可能会让你很快醒悟。所有课程都是四个学分,然而有些课程感觉更像是三个甚至两个学分。在这个项目中通常有两个方向可以遵循,即云计算和数据挖掘。我选择了后者,但回想起来,我希望我选择了云计算,因为它与我的工作更相关。
文字信息系统(2018 年秋季):
这是数据挖掘专题讲座中提供的主题之一,可以用作数据挖掘顶点课程的先决条件。您将初步接触到文档排序、自然语言处理和文本挖掘算法——所有这些都是非常实用且在行业范围内普遍使用的技术。难度适中,课程的高潮是一个将几个学过的概念结合在一起的项目。
数据监管基础(2018 年秋季):
这个科目其实是信息科学学院开设的,不是计算机科学系。这绝对是那些感觉更像两个学分的科目之一,并且只涉及关于数据的来源、模式和生命周期的最抽象的概念。就难度而言,这个课程简直是小菜一碟,如果是我的转世,我可能会三思而后行。
数据挖掘简介(2019 年春季):
这是数据挖掘轨道中的另一个主题,感觉像是文本信息系统的逻辑继承者。它提供了很*的深度,并侧重于几个关键概念,如频繁模式挖掘,监督/非监督学*和朴素贝叶斯分类器。你有机会立即将学到的概念应用到编程作业甚至在线竞赛中。难度适中,获得的知识是一个宝库。
云计算应用(2019 年春季):
这显然是云计算专题之一,也可以说是该专题中最简单的一个(另外两个是云网络和云计算概念)。您将获得关于 IaaS、MapReduce、Hadoop 和 Apache Spark 以及许多其他*数据框架的宝贵见解,但主要缺点是本课程牺牲了深度以换取广度。有时,你会被你所学的众多主题压垮,而你并没有在任何一个单独的领域真正发展出强*的能力。此外,有几个工具在教授时已经过时了,这个学科需要一次*的改革,据我所知,改革已经开始了。
数据清洗理论与实践(2019 年夏季):
可能是 MCS-DS 中最有用的主题之一。你很快钻研了 SQL、Regex 和 OpenRefine,这些对我的工作都是至关重要的工具,我真诚地感谢这门课程提供的相关性。整堂课由编程作业和一个期末项目组成,都相对简单而有趣。
数据挖掘顶点(2019 年夏季):
这是数据挖掘方向的可选课程,只有在完成该方向的两门课程后才能参加。老实说,这是一个很*的失望,给了我很高的期望。作业是非常开放的,重复利用了文本信息系统和数据挖掘导论中的早期编程作业,这些作业应用于一个*型 Yelp 数据集。还有一个文献审查任务和一个项目,要求你将一个数据挖掘应用程序部署到云中,这可能是唯一有用的任务。一切都是同行评分的,因此它更像是一门 MOOC(实际上也是),而不是一门学分课程。
数据可视化(2019 年夏季):
这是你熟悉 Tableau、D3.js、DOM 和 Vega 的程序中最好的主题之一。当时,数据可视化是强制性的,因为在这一核心领域没有其他选择,而且只在夏季学期提供,这对许多人来说并不理想,但话说回来,它可以从婴儿计划中得到。最终项目要求您开发一个交互式仪表盘并部署到云中,这可能是整个 MCS-DS 项目中最具影响力的一项任务。
计算摄影(2020 年春季):
由于 2020 年春季不提供应用机器学*,我们可以选择注册计算摄影作为替代品或推迟一年毕业。我不能说很多人在意识到他们将在没有参加任何机器学*课程的情况下完成一个数据科学课程时感到高兴,但幸运的是,我在早些时候的数据挖掘介绍中已经参加了*量的课程。这门课的独特之处在于它聚焦于计算机视觉中一个非常狭窄的主题,但却深入到了它的最深处。我们被教导如何应用摄影拼接,变形,纹理合成和混合。这可能是 MCS-DS 中对我来说最难的科目,学*曲线相当陡峭。这与我的工作不太相关,但我相信很多人能够在其他领域利用这一点。
最终的分析
对于那些仍在考虑在线学位是否可以替代校园教育的人来说,我必须说没有放之四海而皆准的解决方案。换句话说,没有最好的*学这种东西,但是有最适合你的*学这种东西。鉴于一个人的情况,除了在线教育,可能没有其他选择。诚然,校园里的社交机会并不是真的可以在网上获得的,这本身对许多人来说就是一个交易破坏者。对我个人来说,UIUC 的 MCS-DS 让世界变得不同;甚至在毕业之前,当我把 LinkedIn 添加到我的个人资料中时,我惊讶地看到*量招聘人员在 LinkedIn 上联系我。在我的最后一个学期,我找到了一份新工作,并在毕业后立即开始工作,仅仅是因为这个学位。我很感激它向我敞开的*门,我经常向家人和朋友推荐这个项目。在线学位正变得越来越普遍,我希望 UIUC 能在未来保持同样的质量和严谨水平。
附注:对于那些缺乏实质性接触入学所需能力的人来说,以下(附属链接)MOOCs 可以帮助弥合差距: 【面向对象编程】数据结构&算法统计 ,**
另外,随意订阅 Medium,在这里 探索更多我的故事 。
英国高价值客户识别与 K-均值聚类。
引言。
一家总部位于英国的在线零售店捕捉了一年期间(2016 年 11 月至 2017 年 12 月)不同产品的销售数据。该组织主要通过在线平台销售礼品。购物的顾客直接为自己消费。有些小企业批量购买,然后通过零售渠道销售给其他客户。
作为项目目标?我们努力为企业寻找*量购买其喜爱产品的重要客户。该组织希望在确定细分市场后向高价值客户推出忠诚度计划。我们将使用聚类方法将客户分成不同的组。
数据争论
在我的分析中,我转向了提供的数据集,我们将首先检查它,看是否需要任何争论和清理。
因为数据集中有太多的行,所以查看结构并查看是否有任何值丢失是有意义的。如果数据集中有丢失的值,最好看看哪些列受到了影响,这对我们的分析是否是个问题
我们过滤该表,以查看 NaN 值的主要位置及其对输出的影响。在 SQL 的帮助下,我们过滤数据,找出每个客户购买了多少东西,以及他们为公司创造的总收入。如果我们的主要目标是找到最*的消费群体,那么最好的办法就是比较他们的*宗订单,以确定高消费群体
在制作好表格并将所有 NaN 值分组后,我们将删除它们以避免分类列直方图中的偏差。通过这种方式,所有客户的代表性更加均匀,我们将不会出现异常值,这些异常值归因于缺少信息的客户。
之后,我们创建一个散点图,看看我们的客户购买的商品产生了多少收入。该图显示了占据图中 0 点附近空间的*部分销售,除了少数例外,这些销售似乎购买了*量的商品,但产生的收入很少,或者购买了少量的商品,但收入巨*。为了确保我们的分类以最公平的方式表示数据,我们将对最高收入和数量设置约束。
探索性分析
为了找到聚类,我们将使用 K-means 算法。
K-means 算法是一种迭代算法,它试图将数据集划分为 K 个预定义的不同非重叠子组(聚类),其中每个数据点仅属于一个组。它试图使簇内数据点尽可能相似,同时保持簇尽可能不同(远)。它将数据点分配给一个聚类,使得数据点和聚类质心(属于该聚类的所有数据点的算术平均值)之间的平方距离之和最小。聚类中的差异越小,同一聚类中的数据点就越相似。
kmeans 算法的工作方式如下:
- 指定聚类数 k。
- 初始化质心,首先洗牌的数据集,然后随机选择 K 个数据点的质心没有替换。
- 继续迭代,直到质心没有变化。也就是说,数据点到聚类的分配没有改变。
- 计算数据点和所有质心之间距离的平方和。
- 将每个数据点分配给最近的聚类(质心)。
- 通过取属于每个聚类的所有数据点的平均值来计算聚类的质心。
因此,为了找到最佳的聚类数 K,我们将使用“肘方法”。该方法包括将解释的变化绘制为集群数量的函数,并选取曲线的弯头作为要使用的集群数量。同样的方法也可以用来选择其他数据驱动模型中的参数个数,比如描述一个数据集的主成分的个数。
接下来,我们从数据集的列中创建一个 numpy 数组,并根据找到的最佳 K 值绘制聚类图。完成后,我们根据我们选择的集群数量和我们计算的中心绘制数据框架。为了突出结果,我们使用明亮的颜色来清楚地看到每个聚类的边缘。
为了准确起见,我们还将制作一个树状图。树状图是显示对象之间层次关系的图表。它通常是作为层次聚类 的输出而创建的。树状图的主要用途是找出将对象分配到集群的最佳方式。关于我们如何以及为什么使用树状图的更深入的描述,包括它们背后的数学,请随意使用超链接获取更多信息。
结果
一旦找到聚类,我们使用指定的数字与每个客户相关联,显示哪些属于收入最高的客户。在筛选出特定集群编号的记录后,我们可以创建一个需要关注的顶级客户列表。使用它来查看未来几年是否会有新客户占据相关集群也很有用,现有客户的动态可能有助于调整未来几年的优秀客户名单。
结论
感谢您花时间阅读上面的分析,我希望它是有趣的,并为集群和探索性分析提供了一些见解。该项目的所有代码都可以在我的 GitHub 上找到,请检查一下,如果你有任何意见或建议,请随时发表评论。毕竟,我们无法学*我们认为已经知道的东西。
使用 Python 为自然语言处理(NLP)收集文本的入门指南— Twitter、Reddit、Genius 等等
通过 API 和 Web 抓取收集文本
进入自然语言处理领域
自从一年多前进入数据科学以来,我一直对自然语言处理(NLP)着迷。由于迁移学*的进步,该领域已经取得了一些爆炸性的进展,像 Alexa 和 Siri 这样的 NLP 产品已经成为家喻户晓的名字。由于我的背景是技术写作和修辞理论,我立即被涉及文本的项目吸引,如情感分析和主题提取,因为我想了解机器学*如何提供对书面语言的洞察力。我的第一个数据科学项目是使用谷歌的通用句子编码器来产生葡萄酒推荐。
我一直想要一个像这样的指南,分解如何从流行的社交媒体平台提取数据。随着对 BERT 和 ELMo 等强*的预训练语言模型的可访问性增加,了解在哪里查找和提取数据变得非常重要。幸运的是,社交媒体是收集 NLP 数据集的丰富资源,只需几行 Python 代码就可以轻松访问。在文章的最后,我还提供了一个流行的 Kaggle NLP 数据集列表,并链接到新的搜索引擎 Google Dataset Search。
先决条件
本文教你如何从 Twitter、Reddit 和 Genius 中提取数据。我假设你已经知道一些 Python 库 Pandas 和 SQLite 。
管理您的 API 密钥
在进入代码之前,强调 API 键的值是很重要的。如果您是管理 API 密钥的新手,请确保将它们保存到 config.py 文件中,而不是在您的应用程序中硬编码它们。确保不要将它们包含在任何在线代码共享中。 API 密钥可能非常有价值,有时非常昂贵,必须加以保护。如果您担心您的密钥被泄露,*多数提供商允许您重新生成密钥。
把 config 文件添加到你的 gitignore 文件中,防止它也被推送到你的 repo 中 !
Twitter API
Twitter 提供了*量的数据,很容易通过他们的 API 访问。使用 Tweepy Python 库,可以根据所需主题轻松获取持续的推文流。Twitter 非常适合挖掘趋势和情绪,
对于本教程,你需要在 Twitter 上注册一个应用程序来获得 API 密匙。如果你不熟悉 Twitter 的开发者门户,请查看官方的 Twitter 文档!
使用 pip 安装 Tweepy 和 unidecode。
pip install tweepy
pip install *unidecode*
将以下密钥保存到配置文件中:
使用 Tweepy
将 Tweepy 连接到 Twitter 使用了 OAuth1 。如果您是 API 认证的新手,请查看官方 Tweepy 认证教程。
为了保存来自传入流的数据,我发现最简单的方法是将其保存到 SQLite 数据库中。如果您不熟悉 SQL 表或者需要复*,请查看这个免费网站的示例或者查看我的 SQL 教程。
函数 unidecode() 获取 Unicode 数据,并尝试用 ASCII 字符表示它。
#import dependencies
import tweepy
from tweepy import OAuthHandler
from tweepy.streaming import StreamListener
import json
from unidecode import unidecode
import time
import datetime#import the API keys from the config file.
from config import con_key, con_sec, a_token, a_secret sqlite3conn = sqlite3.connect("twitterStream.sqlite")
c = conn.cursor()
我需要创建表来存储葡萄酒数据。我使用 SQLite 是因为它是轻量级的和无服务器的。另外,我喜欢把所有的数据都放在一个地方!
def create_table():
c.execute("CREATE TABLE IF NOT EXISTS Tweets(timestamp REAL, tweet TEXT)")
conn.commit()create_table()
注意,如果不存在,我使用来确保该表在数据库中不存在。记住使用conn . commit()调用提交事务。
创建一个 StreamListner 类
下面是一些样板代码,用于从流 twitter 数据中提取 tweet 和时间戳,并将其插入数据库。
class Listener(StreamListener):
def on_data(self, data):
**try:**
data = json.loads(data)
tweet = unidecode(data['text'])
time_ms = data['timestamp_ms']
#print(tweet, time_ms)
c.execute("INSERT INTO Tweets (timestamp, tweet) VALUES (?, ?)", (time_ms, tweet))
conn.commit()
**time.sleep(2)**
**except KeyError as e**:
print(str(e))
return(True)
def on_error(self, status_code):
if status_code == 420:
#returning False in on_error disconnects the stream
return False**while True**:
try:
auth = OAuthHandler(con_key, con_sec)
auth.set_access_token(a_token, a_secret)
twitterStream = tweepy.Stream(auth, Listener())
**twitterStream.filter**(track=['DataScience'])
except Exception as e:
print(str(e))
time.sleep(4)
请注意,我使用 time.sleep() 来减缓流的速度。
注意,代码被包装在 try/except 中,以防止潜在的中断中断流。此外,文档建议使用一个 on_error() 函数,在应用发出过多请求时充当断路器。
注意,我将流对象包装在一个 while 条件中。这样,如果遇到 420 错误,它就会停止。
注意twitterstream . filter使用 track 在推文中查找关键词。如果你想关注特定用户的推文,使用 。过滤(follow=[""]) 。
从 SQLite 数据库中提取数据
sql = '''select tweet from Tweets
where tweet not like 'RT %'
order by timestamp desc'''
tweet_df = pd.read_sql(sql, conn)
tweet_df
示例推文数据帧
Reddit API
像 Twitter 一样,社交网络 Reddit 包含了令人瞠目结舌的*量信息,很容易收集。这是一个像互联网论坛一样工作的社交网络,允许用户发表任何他们想要的话题。用户组成名为 subreddits 的社区,他们对社区中的帖子投赞成票或反对票,以决定哪些帖子先被浏览,哪些帖子沉到底部。
我将解释如何获得 Reddit API 键,以及如何使用 PRAW 库从 Reddit 提取数据。虽然 Reddit 有一个 API ,但是 Python Reddit API 包装器,或者简称为 PRAW,提供了一个简化的体验。PRAW 支持 Python 3.5+版本
Reddit API 入门
需要 Reddit 的用户帐户才能使用 API。这是完全免费的,只需要一个电子邮件地址!
为钥匙注册应用程序
如果有办法使用新的 Reddit 用户界面,请给我留言!如果您是第一次登录 Reddit,请按照以下步骤获取 API 密钥。如果您已经有了密钥,使用此链接转到您的应用页面。
reddit.com 用户帐户按钮
点击用户账号下拉列表。显示用户选项。
从用户选项中点击访问旧 Reddit 。页面会改变,网址会变成 https://old . Reddit . com/
偏好;喜好;优先;参数选择
点击注销按钮旁边的首选项链接。
点击首选项屏幕上的应用标签。
点击你是开发者吗?创建 am 应用程序 …按钮。
注册 Reddit 应用程序
输入一个名。
选择 app 的类型。
输入一个描述。
使用 http://localhost:8080 作为重定向 uri 。
填写完字段后,点击创建应用。
Reddit API 客户端
将显示连接所需的 API 信息。当我开始写代码时,我将通过 PRAW 连接到 API。
祝贺你开始收集 Reddit 数据!
使用 PRAW 提取 Reddit 数据
安装 PRAW 的推荐方法是使用 pip 。安装以下软件包来创建仪表板。
**pip install praw**
首先导入库和配置文件:
import praw
import pandas as pd
from config import cid, csec, ua
创建 Reddit 实例
创建一个只读 Reddit 实例。这意味着我不需要输入用于发布回复或创建新主题的 Reddit 凭据;该连接只读取数据。
PRAW 使用 OAuth 认证来连接 Reddit API。
#create a reddit connection
reddit = praw.Reddit(client_id= cid,
client_secret= csec,
user_agent= ua)
识别子记录
以下是我认为值得探究的一些例子:
新闻,数据科学,学*机器学*,游戏,搞笑,政治
探索对象和属性
使用 PRAW 中的 Subreddit 类从所需的 subreddit 中检索数据。可以根据以下 Reddit 选项对数据进行排序:
- 热门——按访问量最*的帖子排序
- 新 —按帖子最新的帖子排序
- 置顶 —按投票最多的帖子排序
- 上升 —按帖子人气排序
如果您想要包含多个子编辑,请使用 + 符号:
#single subreddit new 5
subreddit = reddit.subreddit('news').new(limit = 5)#multiple subreddits top 5
subreddit = reddit.subreddit('news' + 'datascience').top(limit = 5)
这将返回一个对象,它将数据保存在一个属性中。该属性就像 字典 中的键。
数据链接到对象所拥有的属性。如果属性为键,则数据为值。属性是动态生成的,所以最好使用 Python 的内置 vars() 函数来检查哪些是可用的。
使用这个样板代码来查看代表 reddit 帖子的对象所拥有的所有属性。这是一个很长的列表!
subreddit = reddit.subreddit('news').new(limit = 1)
for post in subreddit:
pprint.pprint(vars(post))
发布对象属性的示例
请注意列表中感兴趣的属性:
标题 —返回文章标题。
得分 —返回赞成票或反对票的数量。
num_comments —返回线程上的注释数量。
selftext —返回帖子的正文。
创建的 —返回文章的时间戳。
钉住 —表示线程是否被钉住。
total _ awards _ received—返回帖子获得的奖项数。
保存 Reddit 数据
既然已经确定了属性,就将它们的数据加载到 pandas 数据帧中,或者保存到 SQLite 数据库中,就像 Twitter 示例中那样。在这个例子中,我将把它保存到一个熊猫数据帧中。
#list for df conversion
posts = []#return 100 new posts from wallstreetbets
new_bets = reddit.subreddit('wallstreetbets').new(limit=100)#return the important attributes
for post in new_bets:
posts.append([post.title, post.score, post.num_comments, post.selftext, post.created, post.pinned, post.total_awards_received])#create a dataframe
posts = pd.DataFrame(posts,columns=['title', 'score', 'comments', 'post', 'created', 'pinned', 'total awards'])#return top 3 df rows
posts.head(3)
天才歌词
我一直是音乐迷,尤其是重金属。在重金属中,歌词有时很难理解,所以我去 Genius 破译它们。网站Genius.com是一个注释歌词、收集关于音乐、专辑和艺术家的琐事的平台。Genius 允许用户注册一个 API 客户端。
https://genius.com/api-clients
API 客户端注册
要么报名,要么签到。
点击开发者链接。
点击创建 API 客户端。
输入一个应用程序名称。
如果有网址就输入网址,否则 http://127.0.0.1 就可以了。
点击保存。将显示 API 客户端。
点击生成访问令牌,生成访问令牌。
API 客户端注册工作流
提取歌词
令人惊讶的是,由于法律原因,Genius API 并没有提供下载歌词的方式。可以搜索歌词,但不能下载。对每个人来说幸运的是,Medium 作者 Ben Wallace 为我们提供了一个方便的抓取歌词的包装器。也可以在 GitHub 上找到他的原始代码:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/benfwalla/MusicAnalysis)
我修改了他的包装器,以便更容易下载艺术家的完整作品,而不是对我想要包含的专辑进行编码,并且我添加了一个艺术家列来存储艺术家的名字。
包装器使用 API 获取链接到歌词的 URL。从那里,BeautifulSoup 用于解析每个 URL 的 HTML。该过程产生包含标题、URL、艺术家、专辑和歌词的数据帧:
查看 GeniusArtistDataCollect 包装
包装器是一个名为geniusartisdatacollect()的类。使用它连接到 API 并检索指定艺术家的歌词。在这个例子中,我使用了我最喜欢的金属乐队之一,黑色*丽花谋杀。
要使用geniusartisdatacollect(),实例化它,传入客户端访问令牌和艺术家名字。
g = GeniusArtistDataCollect(token, 'The Black Dahlia Murder')
从geniusartistdata collect对象中调用 get_artists_songs() 。这将作为熊猫数据帧返回。
songs_df = g.get_artist_songs()
songs_df = g.get_artist_songs()
包装纸
下面是我在示例中使用的修改后的包装器:
import os
import re
import requests
import pandas as pd
import urllib.request
from bs4 import BeautifulSoup
from config import token class GeniusArtistDataCollect:
"""A wrapper class that is able to retrieve, clean, and organize all the album songs of a given artist
Uses the Genius API and webscraping techniques to get the data."""def __init__(self, client_access_token, artist_name):
"""
Instantiate a GeniusArtistDataCollect object
:param client_access_token: str - Token to access the Genius API. Create one at [https://genius.com/developers](https://genius.com/developers)
:param artist_name: str - The name of the artist of interest
THIS HAS BEEN REMOVED :param albums: list - A list of all the artist's albums to be collected
"""self.client_access_token = client_access_tokenself.artist_name = artist_name#self.albums = albumsself.base_url = '[https://api.genius.com/'](https://api.genius.com/')self.headers = {'Authorization': 'Bearer ' + self.client_access_token}self.artist_songs = Nonedef search(self, query):
"""Makes a search request in the Genius API based on the query parameter. Returns a JSON response."""request_url = self.base_url + 'search'
data = {'q': query}
response = requests.get(request_url, data=data, headers=self.headers).json()return responsedef get_artist_songs(self):
"""Gets the songs of self.artist_name and places in a pandas.DataFrame"""# Search for the artist and get their id
search_artist = self.search(self.artist_name)
artist_id = str(search_artist['response']['hits'][0]['result']['primary_artist']['id'])print("ID: " + artist_id)# Initialize DataFrame
df = pd.DataFrame(columns=['Title', 'URL'])# Iterate through all the pages of the artist's songs
more_pages = True
page = 1
i = 0
while more_pages:print("page: " + str(page))# Make a request to get the songs of an artist on a given page
request_url = self.base_url + 'artists/' + artist_id + '/songs' + '?per_page=50&page=' + str(page)
response = requests.get(request_url, headers=self.headers).json()print(response)# For each song which the given artist is the primary_artist of the song, add the song title and
# Genius URL to the DataFrame
for song in response['response']['songs']:if str(song['primary_artist']['id']) == artist_id:title = song['title']
url = song['url']df.loc[i] = [title, url]
i += 1page += 1if response['response']['next_page'] is None:
more_pages = False# Get the HTML, Album Name, and Song Lyrics from helper methods in the class
df['Artist'] = self.artist_name
df['html'] = df['URL'].apply(self.get_song_html)
df['Album'] = df['html'].apply(self.get_album_from_html)
#df['InAnAlbum'] = df['Album'].apply(lambda a: self.is_track_in_an_album(a, self.albums))
#df = df[df['InAnAlbum'] == True]
df['Lyrics'] = df.apply(lambda row: self.get_lyrics(row.html), axis=1)del df['html']self.artist_songs = dfreturn self.artist_songsdef get_song_html(self, url):
"""Scrapes the entire HTML of the url parameter"""request = urllib.request.Request(url)
request.add_header("Authorization", "Bearer " + self.client_access_token)
request.add_header("User-Agent",
"curl/7.9.8 (i686-pc-linux-gnu) libcurl 7.9.8 (OpenSSL 0.9.6b) (ipv6 enabled)")
page = urllib.request.urlopen(request)
html = BeautifulSoup(page, "html")print("Scraped: " + url)
return htmldef get_lyrics(self, html):
"""Scrapes the html parameter to get the song lyrics on a Genius page in one, large String object"""lyrics = html.find("div", class_="lyrics")all_words = ''# Clean lyrics
for line in lyrics.get_text():
all_words += line# Remove identifiers like chorus, verse, etc
all_words = re.sub(r'[\(\[].*?[\)\]]', '', all_words)# remove empty lines, extra spaces, and special characters
all_words = os.linesep.join([s for s in all_words.splitlines() if s])
all_words = all_words.replace('\r', '')
all_words = all_words.replace('\n', ' ')
all_words = all_words.replace(' ', ' ')return all_wordsdef get_album_from_html(self, html):
"""Scrapes the html parameter to get the album name of the song on a Genius page"""parse = html.findAll("span")
album = ''for i in range(len(parse)):
if parse[i].text == 'Album':
i += 1
album = parse[i].text.strip()
breakreturn album
另外两个网页抓取例子
天才歌词示例使用美丽的汤从网站上刮歌词。Web 抓取是一种有用的技术,它使得收集各种数据变得容易。我在上一篇文章中介绍了一个额外的 web 抓取示例。如果你想多加练*,就去看看吧!
我是如何从网上搜集棋盘游戏描述的。
towardsdatascience.com](/web-scraping-board-game-descriptions-with-python-7b8f6a5be1f3)
尽管我还没有使用过他的方法,媒体作家威尔·科尔森已经为搜集和解析维基百科做了一个演示。看看他的作品!
如何以编程方式下载和解析维基百科
towardsdatascience.com](/wikipedia-data-science-working-with-the-worlds-largest-encyclopedia-c08efbac5f5c)
Kaggle 和谷歌数据集搜索
虽然我认为收集和创建自己的数据集很有趣,但 Kaggle 和谷歌的数据集搜索提供了便捷的方法来找到结构化和标签化的数据。Kaggle 是一个流行的竞争数据科学平台。下面是用于 NLP 项目的流行数据集列表。
https://www.kaggle.com/datasetshttps://datasetsearch.research.google.com/
确定新闻故事是否来自洋葱:
Youtube 排名和描述:
网飞展示和说明:
葡萄酒评论。我在几篇文章和项目中使用了它:
亚马逊美食评论集:
用于假新闻检测的新闻标题:
实体识别任务语料库。
用于情绪分析的航空公司情绪推文
Yelp 评论数据集
包扎
随着 NLP 变得越来越主流,了解如何轻松收集丰富的、基于文本的数据集变得非常重要。进入自然语言处理领域可能很难,所以我想分享一个指南,简化收集文本数据的方法。只需几行 Python 代码,Reddit、Twitter 和 Genius 上的惊人数据量人人唾手可得!感谢您的阅读,如果您想使用您知道如何收集的数据,请查看我的其他 NLP 相关文章:
谢谢*家!
[## 使用 Python 中的自然语言工具包分析葡萄酒描述
用什么词来形容酒?
towardsdatascience.com](/analyzing-wine-descriptions-using-the-natural-language-toolkit-in-python-497ac1e228d5) [## Python 中的仪表盘,适用于初学者和使用 Dash 的其他人
使用 Python 中的 Dash 初学者教程构建一个基本的和高级的仪表板
medium.com](https://medium.com/swlh/dashboards-in-python-for-beginners-and-everyone-else-using-dash-f0a045a86644) [## 在 SkLearn 中使用 FunctionTransformer 和 Pipeline 预测 Chardonnay 评级
一个将函数转换成可用的管道代码,然后通过传递数据帧来预测葡萄酒评级的例子…
towardsdatascience.com](/using-functiontransformer-and-pipeline-in-sklearn-to-predict-chardonnay-ratings-9b13fdd6c6fd)
分类的最终功能
使用自定义函数评估分类器
贝勒·格拉姆林在 Unsplash 上的照片
在这篇文章中,我将回顾我是如何用 Python 创建一个函数的,这个函数可以轻松地显示一个训练好的分类模型的一些性能指标。因为这个功能是作为一个更*的任务的一部分出现的,所以我将在分享我的过程的图像和代码时提供上下文来帮助澄清。我将主要使用 Sci-Kit Learn 和 Matplotlib 包,但有一个来自 Pandas 绘图方法、 Yellowbrick 的分类器模块的特殊外观。
背景
我得到了一个简单的项目:使用预先制作的数据集来创建一个分类模型的用例,然后创建这个模型。基本上是从一张白纸开始,我面对的是一整个互联网价值的可能的数据集,作为的一个。最终我决定做一些与体育相关的事情,在所有我预料会遇到的运动中,我找到了一个关于终极格斗赛的。
如果你不熟悉这项运动,终极格斗锦标赛( UFC )是一个混合武术推广机构。混合武术,通常被称为 MMA,是一种由多种传统格斗学科非特定混合而成的格斗风格。混合格斗风格的一个很好的例子是拳手乔恩·琼斯。他从全国*专摔跤冠军到获得巴西柔术(BJJ)紫带和跆拳道黑带;跆拳道本身是 1992 年由 Greg Jackson 发展起来的一种格斗方式,使用了摔跤、BJJ、跆拳道和柔道的技术。
在 MMA 比赛中,除了在比赛方式上有更*的自由外,比赛结构通常遵循拳击或摔跤等其他格斗项目的结构。如果你对这一切是如何在更精细的层面上运作感到好奇,我推荐你阅读这个维基百科页面上的“规则”部分。
在 21 世纪初,我是一个超级粉丝;我玩电子游戏,看表演,了解拳手,等等。如果说我对观看这些比赛有什么印象的话,那就是他们感受到的不可预测的。似乎直到有人被击倒或投降,无论情况如何,任何一方都有可能获胜。不管某个拳手在统计上是多么的不被看好,他们仍然有机会获胜,并且偶尔会震惊 MMA 社区。正是这些不可预知的时刻激发了我将这个数据集用于我的项目。我想回答的*问题是:
我能根据他们之前的表现预测 MMA 比赛的获胜者吗?
数据集
如前所述,我在这个项目中的几个限制之一是数据集需要在我开始使用它之前已经编译好。我首先去了 Kaggle,花了很多时间查找数据集。在使用“体育”标签时,我发现了 Rajeev Warrier ( 链接)发布的“UFC-Fight 1993 年至 2019 年的历史数据”。这个数据集有超过 5000 场比赛的*量信息,包含了比赛各个方面的 145 个特征。
有以下信息:
- 拳击手在比赛前赢/输/平的记录
- 一个拳击手赢得多场比赛的方法
- 首选战斗姿态
- 物理属性
- 战斗相关平均值
** 这些平均值处理高级统计数据,例如:重*打击、提交、地面传球等。**
带什么
您需要安装这些软件包才能继续操作:
- Sci-Kit 学*分类器和*多数指标
- 绘图方法的 Matplotlib
- 熊猫为标图方法
- 罗考克级的黄砖
我将根据我在 UFC 上的项目来完成这个函数的创建,尽管这个函数应该在任何为机器学*准备的干净数据集上工作。如果你需要关于如何做到这一点的任何信息和一些好的策略,请查看 Omar Elgabry 的数据清理指南(链接)。
否则,为了简洁起见,我将直接跳到可以创建分类器评估函数的地方。此时,我有了数据集:
- 填充和/或删除任何缺失的数据
- 独热编码分类特征
- 数字要素的最小-最*缩放
- 训练测试分为 X 和 y 数据
- 使用 SMOTE 对训练数据进行综合重采样(修复目标类不平衡)
这里有一个小窥视数据帧的样子:
详细显示红角战斗机缩放信息的列
拟合和预测
这是容易的部分!Sci-Kit Learn 使模型适应训练数据和预测新输入变得非常简单。你需要做的就是首先实例化你选择的 Sci-Kit Learn 机器学*模型,并使用它的。fit() 和。预测()方法。
使用多类逻辑回归模型进行设置、拟合和预测
这段代码的一个独特之处是使用了multi_class=
参数,只有当您试图预测更多的这两个类时,这个参数才有意义。在我的项目中,对于每一个拳击手,我都希望预测他们的比赛是否会以如下方式结束:
- 胜利
- 失去
- 画
这个重要的细节就是为什么我们稍后在预测结果时可视化我们的模型的性能时需要使用 Yellowbrick 包,但是现在让我们看看如何将这个过程函数化。
广义 Sci-Kit 学*分类器的拟合与预测
如果你仔细看看,这里真的没有太*的变化。变量(包括分类器本身)需要一般化,并作为必需的参数放置。但是除了为预测添加一个“返回”语句之外,这就是我需要做的全部工作!
获得结果
所以现在我有了一个可以预测 UFC 比赛获胜者的模型,我需要一种方法来确认它的准确性。我可以通过将输入的数据用作与地面事实相同的预测的输入来做到这一点,因为我们已经有了模型可以学*的正确结果。这就是为什么这种类型的任务被称为 监督学* 的原因,因为我们将能够调整(或监督)我们的模型如何学*,希望提高它在预测未知数据时的性能。
再一次,Sci-Kit Learn 使得一旦一个模型在一些数据上被训练,这变得非常容易。指标模块中的 分类报告 功能可以方便用户生成四种常用的分类指标。所有需要做的就是为数据输入真实的和预测的标签来得到结果!
这是我项目中的一个例子:
K-Neighbors 分类器的分类报告示例
对于这样一个简单的实现,当试图评估分类器的性能时,Sci-Kit Learn 的分类报告带来了很多东西。它提供了聚合和单个标签级别的主要分类指标,有助于确定调整模型或解释模型性能的策略。一般来说,这些指标是:
- 精度——真实阳性与所有预测阳性的比率;具有高精度的模型很少预测不正确的标签,但是经常有许多假阴性
- 召回——真实阳性与所有可能阳性的比率;召回率高的模型通常会捕获许多误报
- F1 分数—使用精确度和召回分数计算;用于尽量减少假阴性和假阳性
- 准确性—真阳性与所有预测的比率;简单地说,预测正确的百分比
如果这些东西看起来有点过时,你想知道更多,我推荐你看看克莱尔·刘的帖子。
炫耀
虽然看起来分类报告已经包含了所有的内容(实际上是这样),但是实际上还有更多的内容可以轻松地添加到这个函数中,以帮助完善所显示的信息。接下来我将展示如何创建一个混淆矩阵和一个 ROC 曲线,然后将它们放入一个函数中。
混淆矩阵
混淆矩阵是可视化分类模型性能的一个很好的工具。简而言之,它是一个分成小方块的正方形,这些小方块除了表示真阴性和假阴性之外,还表示真阳性和假阳性的值。视觉上更容易理解:
二元分类器的示例混淆矩阵
从左上角开始,沿对角线向下,这些值代表真值;或者,它们是该特定类别的召回值。关于他们的更多信息可以在 Sarang Narkhede ( 链接)的帖子中找到。
由于 Sci-Kit Learn,混淆矩阵也很容易实现,以下是实现方法:
*鹏曲线
我添加到函数中的第二个图有点高级,但是可以在评估模型性能时提供一些有趣的见解。ROC ( 接收器工作特性)曲线,简单来说,说明了真阳性率 (TPR)和假阳性率 (FPR)之间的权衡。如果你想更深入地了解这些比率背后的数学,维基百科做了一个密集但很棒的工作来详述这些公式。
理想的 ROC 曲线看起来像一个更平滑、倾斜的倒“L”型。希望这在看完一个之后会更有意义:
ROC 曲线示例(不良表现)
上面的例子来自我在项目期间测试的一个性能较差的模型。代表每个类别的 TPR 和 FPR 的两条线相对靠近黑色虚线;这条黑线代表“随机机会”线,其中正确的机会基本上是 50%。因此,这个模型并不比抛硬币选择哪个战士会赢好多少。理想的 ROC 曲线应该有一个比 FPR 增长更快的 TPR,这导致了我所说的倒“L”形。
通常,如果一个人正在训练一个二元分类模型,这个图可以使用 Sci-Kit Learn 生成。然而,因为我预测了三个不同的类,所以我将展示如何使用允许多类曲线的 Yellowbrick 包创建 ROC 曲线(不需要指定任何一个)。
我是这样做的:
黄砖 ROC 曲线的单一实现
包装它
现在是时候把上面的所有代码放到一个可重用的函数中了,这个函数可以在一行代码中产生前面讨论过的所有内容!
我会一步一步地分解它:
- 拟合模型并创建预测
- 使用预测生成分类报告
- 为绘图创建图形和轴
- 生成并格式化混淆矩阵
- 生成和风格化 ROC 曲线
- 显示绘图
- 返回预测
开始了。(马里奥的声音):
先前章节的功能化版本
附注:在第 48 行,我使用了一个名为 plot_importance() 的自定义函数。作为一个附件,它的目的是作为一个选项显示一个只与决策树分类模型相关的附加图。更多关于特征重要性的信息;代号为【剧情 _ 重要性() 。
这是一个输出结果的例子:
根据 UFC 数据训练的标准逻辑回归示例
现在怎么办?
因此,如果您一直在跟进,您应该有一个功能完整的函数函数,它将很容易地显示一个总报告和两个关于监督分类模型性能的图。通过使用该函数,可以快速评估模型学*训练数据的程度;在比这三个工具单独提供的更深的层次上这样做。当选择一个分类器在特定数据集上进行训练时,我打算将这个函数用作多个分类器的一个评判标准,并作为一种在调优过程中提供洞察力的方式。我很想知道它的其他用途是什么,或者被改变了(请告诉我!).
当然,为了创建这个函数,确实需要做一些准备,但是总体来说,在这个项目中使用它节省了我很多时间。我希望它能为其他人做同样的事情!
强化学*终极指南第 2 部分——培训
在这个全面的文章系列中,我们将构建自己的环境。稍后,我们将使用强化学*来训练一个神经网络。最后,我们将创建一个视频,展示人工智能在环境中的表现。
环境、培训和展示的完整代码可以在 https://github.com/danuo/rocket-meister/的 GitHub : 上找到
我们将涵盖的内容:
第 1 部分——用 Pygame 创建一个可玩的环境
- 创造一个健身房的环境。Env 子类。
- 通过
step()
函数实现环境逻辑。 - 用 Pygame 获取用户输入,使环境适合人类玩。
- 用 Pygame 实现一个
render()
函数来可视化环境状态。 - 用 Matplotlib 实现交互级设计。
第 2 部分—开始培训
- 在理解可能性和挑战的同时,定义合适的观察。
- 定义合适的奖励。
- 用
gym
环境训练神经网络。 - 对结果的讨论
这是该系列的第二部分,涵盖了神经网络的训练。在我们开始训练之前,我们必须进一步指定环境和人工智能之间的 API。
要求
由于我们要训练的模型相对较小,因此可以在合理的时间内(不到一天)在消费级桌面 CPU 上进行训练。你不需要一个强*的 GPU 或访问云计算网络。本指南中使用的 python 包如下所示:
**Python** 3.8.x
**ray 1.0**
**tensorflow** 2.3.1
**tensorflow-probability** 0.11
**gym 0.17.3
pygame 2.0.0**
观察
观察是从环境反馈给代理或神经网络的反馈。这真的是唯一的事情,代理可以看到,以推导出它的下一步行动。更重要的是,代理没有记忆。它的决定将完全基于对当前状态的观察。
定义合适的观察对于获得良好的培训效果至关重要。在我们当前的例子中,定义观察可能是一个微不足道的任务,但是我们将探索几个选项。这可能不是其他机器学*项目的情况,在其他机器学*项目中,开发合适的观测值可能是一项具有挑战性的关键任务。
在我们讨论合适的观测的要求之前,让我们先用最直观的方法来研究:由于火箭不会撞向边界,使用间隙作为观测是有意义的。所以我们计算了各种角度下火箭与环境的距离(-90,-60,...,+90),如下图所示。
归一化现在,我们需要确定每个观测值的取值范围是[-1,1]。这个过程称为规范化,不是强制性的。但是,*多数神经网络将受益于标准化值。这是因为*多数神经网络在计算结束时都有一个反正切函数。在这种情况下,归一化值范围在数值上更合适。
观察的标准化。
实现标准化的一种方法是应用线性插值。实现这一点的简单方法是使用下面的 numpy 函数:
obs_norm = np.interp(obs, [0, 1000], [-1, 1])
观察值的唯一性用数学术语来说,把模型想象成一个确定性函数 f ,它根据观察值【o】计算动作【a】。在本例中,有 n 个观察值和 2 个动作:
这里有很多理论要探讨,但重要的含义如下:如果两种不同的情况或状态需要两种不同的行动才能成功,那么它们各自的观察也必须不同。只有当观察结果不同时,代理才能产生两种不同的动作。那么这到底意味着什么呢?让我们来看一个例子:
下图中显示的两个场景显示火箭处于完全相同的位置。因此,距离l1,…,l7是相同的。然而,左边场景中的火箭速度要高得多。因为速度不是观测的一部分,代理人不知道火箭太快了。
具有相同观测值的不同状态(观测值集 7)
要执行的适当行动分别是:
- 左图:减速,右转。
- 右场景:增加/保持速度,右转。
由于对两种情况的观察是相同的,神经网络将不可避免地对两种情况执行相同的动作。因此,它根本无法在两种情况下执行适当的操作,最多只能完成其中一种。因此,单独使用距离作为整个观测值 o=[l1,…,l7]不是一个好主意。出于测试的目的,我们将把它作为观测的第一次迭代。
观测值 o7 的第一次迭代:
接下来,我们将扩展这个集合。从最近的考虑中,我们已经得出结论,神经网络需要知道火箭的速度。因此,速度*小将起作用:
观测值 o8 的第二次迭代:
然而,我们可以很容易地想出两个场景,这两个场景需要不同的操作,尽管产生了相同的观察结果。速度转向的方向不能从这组观测值中扣除。因此,移动方向未知,需要另一次观察。
具有相同观测值的不同状态(观测值集 8)
显然,我们需要使观测范围内的速度有方向。作为一个小注释,简单地分别传递 x 和 y 方向的速度是行不通的。火箭的绝对方位也不知道,所以这个问题可以简单地转换成另一个问题。因此,建议将火箭方向与其速度之间的相对角度作为附加观测值:
使用角度有点棘手。首先我们需要决定是要有度的工作还是要光芒四射的工作。其次,如果角度不在-180°范围内< α
第三次迭代观测 o9:
最后但同样重要的是,我们将提供某种导航辅助。对于奖励功能,我们稍后将沿着轨迹定义目标,当达到时将获得奖励。下一个目标的方向是垂直于下一个目标的向量,因此指示到所述目标的直接路线。火箭方向和目标向量之间的角度差是第 10 次也是最后一次观察:
第四次迭代观测 o10:
在我们评估不同组的观察值之前,请记住,神经网络不知道已定义观察值的含义或上下文。然而,也不尽然。机器学*的目标是找到观察和成功行动之间的数字相关性。对于这一点,数据的上下文无关紧要。
对观察结果的评估
所有四种变体都已经通过用 SAC 代理进行总共 200 万步的训练进行了测试。不要担心,我们稍后将经历开始培训的步骤。现在,让我们先看看观测值的选择所导致的后果。通过查看图表,我们可以看到 o9 & o10 比其他两个表现好得多。设置 o9 产生最好的结果,这是一个小惊喜。然而,我们不应急于下结论,并记住以下几点:首先,200 万步并不多。虽然 o7 和 o8 的曲线看似收敛,但本试验无法确定 o9 和 o10 的最终性能。经过更长时间的训练, o10 很有可能超过 o9 。甚至 o7 和 o8 的停滞都不确定。如果这不是一个有趣的项目,更长时间的训练应该被考虑。
四组不同观察值之间的性能比较。
奖励函数
如前所述,奖励功能是环境的一部分,据说在强化学*中被代理人最*化。想出一个好的奖励函数比你想象的要难得多。当最*化奖励函数并不完全符合你实际上想要 AI 做的事情时,问题就出现了。拥有一个只重视符合你意图的行为的奖励功能,实际上比预期的要困难得多。
Rob Miles [Youtube]有一个非常好的视频,讲述了一个被设计用来收集邮票的人工智能,它可能会在收集邮票的过程中引发一场世界*战。该视频非常有趣,同时也很有见地。
回到奖励函数的定义:假设我们在更高的速度下给予更多的奖励。预测代理最有可能以尽可能高的速度撞上墙并不需要太多的创造力。当然,一个更慢的轨迹,实际上是穿过整个过程,会产生更高的总回报。然而,放弃目前最成功的策略,探索一种完全不同的方法,总是令人乏味的。在数学术语中,给定的高速碰撞进近是所有可能进近空间中的局部最*值。
摆脱局部极值并进一步优化其策略是智能体的基本能力。但是,不能保证代理真的做到了这一点。因此,明智的做法是不要在奖励函数中包含速度,或者不给它很*的权重,这样就不会产生这种障碍。相反,我们选择设立检查站,在通过时给予奖励。
设置检查点
为了计算奖励,我在赛道上一共设置了 40 个检查站。这是一种跟踪赛道进展的简单方法,也可以计算完成的圈数。
此外,我们提出了三个变量来推断火箭通过的检查站的回报。变量如下所列,并在之后进行比较:
变体 1:每个关卡的静态奖励
静态变量将以固定的数量奖励每个到达的检查点。
# for each goal 1 point
reward_total += 1
变体 2:每个关卡的动态奖励
动态变量将奖励每一个达到略低于一点的关卡。到达下一个关卡花费的时间越长,奖励的一分被扣除的就越多。根据所用时间的不同,奖励在 1 到 0.9 分之间不等。变量 steps 保存自达到最后一个目标以来执行的步数。这个变量间接依赖于火箭的速度。
# for each goal give:
reward_total += max(1, (500 - steps)) / 500
变体 3:对每个目标的持续奖励
类似于变体 1,这种连续变体将奖励每个目标恰好一分。但是,在执行的每一步中,奖励都是连续分配的。如果火箭正好位于目标 3 和目标 4 之间,那么到目前为止获得的总奖励是 3.5 分。后退时,奖励分别减少。这个奖励变量的计算相当繁琐,但是可以看看 GitHub 库中的代码。
比较
正如我们所看到的,在长时间的训练之后,所有的奖励函数都给出了相似的好结果。所有的变化都会产生一个神经网络,它可以相当好地完成课程。这并不奇怪,因为这些奖励函数非常相似。我们可以看到,在很*一部分训练中,有动态奖励的训练有更高的平均奖励。
培养
为了在我们的环境中训练神经网络,我们将使用强化学*(RL),机器学*的领域之一。虽然有很多方法可以部署 RL,但是我们将使用一个叫做 Ray 的框架。
Ray 的核心功能是提供一个多处理框架,允许代码在多个 CPU 核心甚至多个机器上并行运行。这非常有帮助,因为它使我们能够同时用多个代理/环境来训练我们的神经网络。Ray 还包含了一个强化学*库,因此我们可以在很少甚至没有编程的情况下进行训练。我们只需要知道 API,不可否认的是,它很少被记录,而且有时很复杂。
在 Windows 10、OSX 和 Linux 上安装 Ray
Ray 刚刚发布了 1.0 版本,终于添加了期待已久的 Windows 10 支持。现在,您可以通过运行简单的 pip 安装在任何主要平台上安装 Ray:
pip install --upgrade ray
雷入门
我们使用 Ray 的主要原因是包含了专用于 RL 的库 RLlib 。它实现了*量先进的机器学*代理。正如下面的概述所示,*多数代理都支持 TensorFlow 2 和 Pytorch。你想用哪个框架完全取决于你。如果你不确定,就用 TensorFlow。
RLlib 提供的机器学*代理。
ray 的伟*之处在于已经实现了许多训练代理,并且所有的代理都可以以相同的方式使用。这使得我们可以用 14 个不同的代理来训练网络,只需要在它们之间改变一个字符串。
Ray 的文档可能有点令人不知所措,并且由于普遍缺乏示例而令人困惑。尽管如此,这个库非常强*,完全值得学*,因为繁琐的任务可以通过少量的步骤来完成。如果我们使用算法 PPO 在健身房环境 CartPole-v0 上开始训练,我们所要做的就是执行这两行代码:
from ray import tune
tune.run('PPO', config={"env": "CartPole-v0"})
如果您遇到一个错误,您很可能会丢失包tensorflow-probability
包。要安装,请运行:
pip install --upgrade tensorflow-probability
为了在一个定制的环境中训练网络(例如,一个不属于gym
包的环境),我们需要在配置字典中修改 env 关键字。我们可以传递环境类,而不是环境的名称字符串。参考见 start_ray_training.py: 中的代码
https://github . com/danuo/rocket-meister/blob/master/start _ ray _ training . py
结果
最终,9 个不同的代理被用来训练不同的神经网络。并不是前面列出的所有代理都可以被使用,因为一些代理经常崩溃,而另一些代理只适合于不连续的动作(记住,我们使用的是连续动作)。以下是培训的结果:
我们看到一些代理人表现很好,比人类更好地通过了课程。还要记住,所有代理都是未调整的,也就是说,它们使用默认参数运行。当选择其他参数时,它们的性能可能会提高很多。
结论
还有其他一些因素使得很难在两种药物之间进行直接比较。在这个测试场景中,每个代理接受了总共 1500 万步的训练。请注意,并非所有代理的计算速度都一样快。如果我每次训练的时间相同,结果可能会不同。此外,对于*多数代理来说,培训可能不一致,因此更长时间的培训可以进一步改进策略。快速学*代理是伟*的,但有人可能会说,长期训练后的最终表现可能更重要。
另外请注意,SAC 并不像测试中显示的那样优越。SAC 代理的得分明显高于其他代理。然而,人们不得不相信,这些有争议的伟*成果是受制于过度拟合。这意味着,代理人实际上记住了轨道,而不是实际学*如何控制火箭。如果环境被改变,代理人被认为是失败的,因为没有在未知环境中机动的一般知识。为了防止过度拟合,训练应该在动态环境中进行,在每次迭代中都会发生变化。rocketmeister 环境有一个级别生成器,也许你想尝试一下!有关更多信息,请查看自述文件:
RocketMeister 是一个广泛而复杂的健身房环境,用于开发和比较强化学*…
github.com](https://github.com/danuo/rocket-meister)
感谢阅读!
强化学*终极指南第 1 部分——创建游戏
入门
在这个全面的文章系列中,我们将构建自己的环境。稍后,我们将使用强化学*来训练一个神经网络。最后,我们将创建一个视频,展示人工智能播放环境。
环境、培训和展示的完整代码可以在GitHub:https://github.com/danuo/rocket-meister/上找到
我们将涵盖的内容:
第 1 部分——用 Pygame 创建一个可玩的环境
- 创造一个环境作为健身房。Env 子类。
- 通过
step()
函数实现环境逻辑。 - 用 Pygame 获取用户输入,使环境适合人类玩。
- 用 Pygame 实现一个
render()
函数来可视化环境状态。 - 用 Matplotlib 实现交互级设计。
第 2 部分——用强化学*训练神经网络
- 在理解可能性和挑战的同时,定义合适的观察。
- 定义合适的奖励。
- 用
gym
环境训练神经网络。 - 对结果的讨论
这是这个系列的第一部分。我们将实现游戏逻辑,为控件获取用户输入数据,并实现渲染,使人类有可能玩游戏。为此,我们将使用一个叫做 Pygame 的流行 python 包。
要求
由于我们要训练的模型相对较小,因此可以在合理的时间内(不到一天)在消费级桌面 CPU 上进行训练。你不需要一个强*的 GPU 或访问云计算网络。本指南中使用的 python 包如下所示:
**Python** 3.8.x
**ray 1.0**
**tensorflow** 2.3.1
**tensorflow-probability** 0.11
**gym 0.17.3
pygame 2.0.0**
环境
在强化学*的背景下,环境可以被看作是一个互动的问题,需要以最好的方式来解决。
代理与环境的相互作用。
为了量化成功,在环境中定义了一个奖励函数。代理可以看到所谓的观察,这些观察给出了关于环境当前状态的信息。然后,它可以采取特定的行动,返回观察结果和下一个环境状态的标量奖励。代理人的目标是在有限的步骤中获得最*的回报。
从技术角度来看,有许多不同的方式来构建环境。不过最好的方法是采用gym
包中定义的结构。gym
包是一个现成环境的集合,为强化学*提供了事实上的标准 API。所有的gym
环境共享相同的函数和变量名称,这使得环境和代理很容易互换。为了采用gym
结构,我们将使我们的环境成为体育馆的子类。Env 类。该类的基本和强制元素如下所示:
这些函数和变量中的*部分将在后面进行更深入的讨论。下面是一个简短的总结,首先列出了最重要的项目:
- 动作(对象):在
step()
功能中要执行的动作。在国际象棋比赛中,这个动作是由一个玩家执行的特定的、合法的移动。 - 观察(对象):这是可供代理选择下一个动作的所有信息。该观察仅基于环境的当前状态。
- 奖励(浮动):分别是上一次执行动作或上一步得到的奖励。人工智能将试图最*化总回报。回报也可能是负的。
- done (boolean): 如果设置为 true,则环境到达终点。无法执行更多操作,需要重置环境。
- info (dict): 允许提取环境数据用于调试目的。数据对代理不可见。
- env_config(dict): 这个可选的字典可以用来配置环境。
- 观察 _ 空间和行动 _ 空间:正如你所想象的,对于特定的环境,只有某些行动和观察是有效的。为了定义格式,需要将
observation_space
和action_space
变量分配给各自的 gym.space 类。空间可以有不同的维度和值范围。连续空间和离散空间都是可能的。有关健身房空间的更多信息,请查看文档和健身房 GitHub 。
self.observation_space = <gym.space>
self.action_space = <gym.space>
示例:动作空间的定义
正如在视频中看到的,我们想要控制一个可以向前/向后加速(动作 1)和向左/向右旋转(动作 2)的火箭。因此,我们将动作定义为*小为 2 的线性向量。
每个数组单元格的值都是连续的,并且必须在[-1,1]的范围内。相应的健身房空间在下面一行代码中定义:
gym.spaces.Box(low=-1., high=1., shape=(2,), dtype=np.float32)
Pygame 实现
Pygame
是一个为创建简单游戏而设计的 Python 库。主要特性是 2d 渲染能力、用户输入采集和音频输出选项。下一节将介绍一个非常基本的Pygame
实现,只包含最少的功能。如果你更有野心,你可以考虑实现动态帧速率或动态分辨率等功能。
翻译
为了在 Pygame 中渲染,我们需要创建一个窗口(也称为表面)来绘制视觉输出。
window = pygame.display.set_mode((window_width, window_height))
接下来,我们可以为创建的窗口排队 draw 调用。你可以在 Pygame 纪录片中找到可用抽奖呼叫的概述。我们将在添加到我们的 CustomEnv 类的新函数中实现几个示例性的绘制调用。该函数名为 render() ,如下所示:
在进行绘制调用后,需要用pygame.display.update()
命令更新并实际呈现窗口。
基本渲染循环
现在是时候通过创建一个渲染循环例程来让我们的环境保持运行了。我们用pygame.init()
初始化 Pygame,然后创建一个时钟对象,它可以与tick(fps)
一起保持静态帧速率。我们为视觉输出创建一个*小为 1000*500 像素的窗口。然后我们开始一个 while 循环,在用update()
生成一帧之前,执行一次step()
和render()
。显然,这个渲染循环只有在render()
实际上反映了step()
引起的变化时才有意义。
用户输入
Pygame 提供了两种从键盘获取用户输入数据的方法:
-
第一个名为 pygame.event.get() 和将在按键状态从未按下变为按下时生成一个事件,反之亦然。其他事情,比如关闭 Pygame 窗口,也会创建一个事件。后者(
event.type == pygame.QUIT
)使我们能够在不崩溃的情况下结束 while 循环和 Python 脚本。在 Pygame 文档中可以找到密钥常量列表。 -
第二个方法叫做py game . key . get _ pressed()和将返回一个布尔类型的元组,每个条目代表键盘上的一个键。未按下的键的值为 0,按下的键的值为 1。为了评估键状态,我们需要知道哪些键映射到元组的哪个索引。例如,向上箭头键位于索引 273 处。
运动学
接下来,我们将实现火箭的运动学。当我们用一个简单的方法来处理旋转时,平移运动将有惯性。数学上,火箭的轨迹就是运动方程的解,是平滑的。位置不能跳,反而需要不断变化。
因为我们对近似解很满意,所以我们可以用欧拉向前法进行时间离散来计算轨迹。下面的代码显示了一个简单的二维实现:
现在我们将所有东西(游戏逻辑、输入和渲染)合并到我们之前定义的CustomEnv
类中。我们还将把所有与 Pygame 相关的东西移到render()
和一个单独的init()
函数中。这样,我们可以用step()
和reset()
执行机器学*例程,而不用加载更重的 Pygame 包。如果为 AI 训练加载了环境,则不需要渲染,并且可以提高性能。
下面是上面运行的代码,带有一些键盘输入:
水平设计
现在,我们将使用手动创建的静态级别。创建一个可能是一项单调乏味的任务。我们将使用 Matplotlib 让我们的生活变得更简单。使用 plt.ginput() 函数,可以通过在图形内部点击来获取坐标。
这些坐标将被打印到控制台中,您可以从那里将它们复制到您的代码中。稍微重新格式化应该可以将它们包含到我们的环境中,例如将它们存储在 numpy 数组中,如rocket_gym.py
所示。
碰撞检测
假设我们将级别边界存储为*小为 n*4 的数组,每行保存一个段的点:
两条直线是否相交可以通过下面的函数来检查。如果线相交,则返回交点的坐标。如果没有交集,则不返回任何交集。
现在,我们可以将公式应用于我们的问题。我们通过检查是否有任何环境边界与运动向量相交来做到这一点
继续第 2 部分
在第二部分中,我们将讨论并实现环境返回的观察和奖励。之后,进行实际训练。请在这里阅读:
选择合适视觉效果的终极指南
从“用数据讲故事:商业专家数据可视化指南”中吸取的经验教训
威廉·艾文在 Unsplash 上的照片
说到有效的数据可视化,第一步也是最关键的一步是为您想要呈现的数据选择正确的图形/视图。由于有各种各样的可视化软件可以提供*量不同的图表,选择正确的软件通常是一项具有挑战性的任务,它以尽可能简单的方式解释数据和见解。我最近读了一本关于数据可视化的非常著名的书——《用数据讲故事:商业人士的数据可视化指南》,作者是 Cole Nussbaumer Knaflic。这本书是迄今为止我所见过的关于数据可视化的最佳资源,在本文中,我将解释书中的一个主题——选择有效的可视化。
*多数数据可以使用我将在本文中讨论的 12 种视觉效果中的任何一种来可视化。视觉效果可分为:
- 简单文本
- 表格(表格、热图)
- 点(散点图)
- 线(线形图、斜率图)
- 条形图(水平、垂直、堆积、瀑布)
- 面积
注:
- 所有显示的图表都是使用 Google Sheets 制作的。 链接到文档 。
- 用于生成图表的数据完全是虚构的,并非取自任何来源。
所以让我们开始探索列表中的每一个。
简单文本
你不必总是用图表来显示数字。如果只有一些数字和一些支持文本,直接显示数字可能是最好的方法。让我们看一个例子来更好地理解。
作者图片
在上面的例子中,图表没有为解释提供太多的帮助,只是占据了很*的空间。所以,当你只有几个数字的时候,直接展示出来。
表
如果你想用多种计量单位进行交流,表格可能是最合适的选择。创建一个表格很容易,但是一定要确保设计淡出背景,数据是主要焦点。下面是一个将设计淡化到背景并专注于数据的例子:
作者图片
你能观察到每次迭代后的改进吗?这就是它如此重要的原因。
热图
热图只是一个表格的升级版本,我们在其中添加颜色来更好地解释数据或数字。在一个简单的表格中,读者必须浏览每一个元素,才能对其中的内容有所了解。通过添加颜色,我们让读者直接关注感兴趣的区域,从而更好地理解数据。
作者图片
像 Excel 这样的绘图应用程序具有创建热图的条件格式选项。为了更好地理解,加入图例也是一种很好的做法。
散点图
散点图用于显示两个变量之间的关系,其中每个变量分别用 X 轴和 Y 轴编码。这在解释相关性时特别有用。
作者图片
线图
折线图是绘制连续数据(如日期和时间)的最佳方式。由于所有的点都用一条线连接起来,所以很容易解释连续的数据,但同时,这对于绘制分类变量没有意义。折线图可用于显示单个系列或多个系列的数据,如图所示。
作者图片
斜率图
斜率图只是线形图的一个特例,非常适合比较两个不同点或时间段的指标变化。这对于直观地显示变化率(增加或减少率由线条的斜率表示)以及绝对值非常有用。
作者图片
接下来,我们将看看条形图的一些变化,这是分类变量的理想选择。条形图往往被避免,因为它们是常见的,但由于它们是常见的,与其他类型的视觉效果相比,读者很容易理解条形图。这使得条形图成为最重要的视觉形式之一。
竖线
这是一个普通的条形图,每一列代表一个类别。与折线图类似,条形图也可以包含多个系列。
作者图片
堆叠竖条
堆积条形图可用于比较不同类别的子组件。它可以使用 100%堆积图来保存实际数字或百分比。
作者图片
同样,你不能用太多的子组件来填充类别,因为这会变得难以理解和比较。
瀑布
瀑布图是垂直条形图的另一种特殊情况,它可以用于拉动堆积条形图的子组件,一次聚焦一个组件,或者显示起点、增加和减少以及最终的终点。
作者图片
单杠
水平栏通常是分类数据的首选项,因为它比垂直栏更容易阅读,并且还可以容纳*型类别名称。与垂直条形图类似,它也可以有单个或多个数据系列。
作者图片
堆叠的水平条
这类似于堆叠垂直条形图,但由于水平条形图所讨论的原因,相对更好。
面积图
应尽可能避免面积图,因为人眼不太擅长比较二维空间中的值。但是,如果您非常想包含多个指标,那么面积图可能行得通。
至此,我已经介绍了可以用来可视化*部分可用数据的图表。因此,选择一个图表,可以清楚地解释你试图传达的信息。
我们已经讨论了最佳实践,现在是时候看看一些应该避免的实践了。
要避免的视觉实践
避免使用饼图,因为读者必须比较弧线的区域,这变得非常困难且不直观。使用一个标准的条形图会使它更容易解释。看看下面的例子可以更好的理解。
作者图片
永远不要使用三维图表。3D 图表会产生不必要的干扰,使其难以解读。所以千万不要用 3D 。
结论
我希望这篇文章能让你很好地理解不同的视觉效果,以及在什么地方使用每种视觉效果。所以,一定要选择能充分传达你想要展示的信息的视觉效果。说到您可以使用的应用程序/软件,这完全取决于您。Excel,Tableau,Power BI,Google Sheets 是一些可用的应用程序,你可以使用任何你觉得舒服的东西。请记住,图形应用程序并不知道视觉对象的实际用途,而是由您根据需要定制它。我希望它有帮助。
神经网络中输入形状和模型复杂性的最终指南
计算神经网络输入形状和复杂性的直观指南
在构建神经网络时,许多初学者和非初学者似乎都陷入了计算需要输入到神经网络中的输入形状的困境。
但是我们为什么要知道输入的形状,又为什么要喂它呢?神经网络不能自己算出来吗? 这个问题的答案在于矩阵乘法的基础知识。
假设我们有两个矩阵 A 和 B,设 B 的维数为 m 行 x n 列。现在,为了使两个矩阵在乘法上兼容,A 的列维度应该与 b 的行维度相同,这意味着 A 的维数应该是 k x m,其中 k 可以是任何数字。
现在,图片 A 作为输入张量(一组图像、一组输入特征样本、特定词汇*小的文本数据等)。)和 B 作为神经网络中的第一个隐藏层。k 是输入样本的数量,m 是每个输入样本的维数。m 的形状取决于输入的类型和隐藏层的类型。
让我们来看看最常用的神经网络类型,并弄清楚输入形状应该是什么:
- (DNN)深度神经网络:
一个 4 层神经网络(承蒙:http://alexlenail.me/NN-SVG/index.html)
这些是用于分类和回归任务的完全连接的神经网络。这些有时也会附加到某些更高级架构的末端( ResNet50 、 VGG16 、 AlexNet 等)。)
让我们看一个这样的神经网络:
model = Sequential()
model.add(Dense(units=12, activation='relu', input_shape=(32,)))
model.add(Dropout(0.5))
model.add(Dense(units=8, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(units=6, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(units=1, activation='softmax'))
该模型由三个隐藏层和一个输入层组成。在每对致密层之间添加脱落层,以实现规整化。Dropout 层采用参数“rate ”,该参数指定前一密集层中应取零值的神经元的比例。在这个模型中,比率设置为 0.5,这意味着隐藏层中 50%的神经元的权重为 0。
在 Keras 中,需要给出输入维度,不包括批量*小(样本数)。在这个神经网络中,输入形状被给定为(32,)。32 是指每个输入样本中的特征数量。不要提批量*小,甚至可以给出一个占位符。另一种给出上述模型中输入维度的方法是(None,32,)。
如果数据是多维的,比如图像数据,那么输入数据必须以(m,n)的形式给出,其中 m 是高度维度,n 是宽度维度。
因为 32 是特征*小,所以它是输入矩阵的列维数。这意味着隐藏层的行尺寸也是 32。现在我们已经对输入游戏进行了分类,让我们看看这个模型并理解它的复杂性。这里,复杂性是指可训练参数的数量(权重和偏差参数)。可训练参数的数量越多,模型越复杂。
深度神经网络模型综述
由于每个输入特征都连接到隐藏层中的每个神经元,因此连接总数是输入特征*小(m)和隐藏层*小(n)的乘积。由于每个连接与一个权重参数相关联,权重参数的数量为 m×n。每个输出神经元与一个偏置参数相关联,因此偏置参数的数量为 n。可训练参数的总数= m×n+n
第一密/隐层有 12 个神经元,这是它的输出维度。这在模型摘要中作为第二个输出参数出现,与第一个隐藏层相对。后续的脱落层不会改变输出的尺寸。它只是改变了神经元的重量。第二个隐层有 8 个输出神经元,下一层有 6 个。最终输出层有 1 个神经元。让我们验证这个模型的全部可训练参数。
第一隐藏层(m = 32,n = 12):32 x 12+12 =396
396第二隐藏层(m = 12,n = 8) : 12 x 8 + 8 = 104 第三隐藏层(m = 8,n = 6) : 8 x 6 + 6 = 54 输出层(m = 6,n = 1) : 6 x 1 + 1 = 7总可训练参数= 396 + 104 + 54 + 7 = 561
- 卷积神经网络(CNN): 这些*多用于处理各种计算机视觉应用的图像数据,如图像检测、图像分类、语义分割等。由于图像数据是多维数据,因此需要不同类型的处理层来检测图像的最重要特征。这就是卷积神经网络的作用。
一个示例性的 CNN 模型(承蒙:http://alexlenail.me/NN-SVG/index.html)
图像被表示为一个三维元组——水平维度(宽度)、垂直维度(高度)和通道数量。如果图像是灰度的,那么通道参数取值为 1,如果是彩色的,那么取值为 3,红色、绿色和蓝色通道各一个。
Input_shape=(None, 284, 284, 3)
cnn=Sequential()
cnn.add(Conv2D(16, kernel_size=(2,2), padding="same", activation='relu', input_shape=Input_shape[1:]))cnn.add(MaxPooling2D(4))
cnn.add(Conv2D(32, kernel_size=(2,2), padding="same", activation='relu', input_shape=Input_shape[1:]))cnn.add(MaxPooling2D(2))
cnn.add(Flatten())cnn.add(Dense(10, activation='softmax'))
卷积神经网络有两种特殊类型的层。卷积层(模型中的 Conv2D)和池层(MaxPooling2D)。维度为 k 的 2-D 卷积层由一个 k x k 过滤器组成,该过滤器通过图像中的每个像素。因为 k×k 滤波器覆盖 k×k 个像素,所以当它经过一个像素时,它的 k -1 个邻居也被覆盖。执行滤波器矩阵和覆盖图像矩阵的逐元素乘法。这些值相加并填充到相应的输出像素中。
例如,如果 2×2 维的卷积滤波器通过位置(1,1)处的图像像素,则它也覆盖(0,0)、(0,1)和(1,0)。滤镜的(0,0)值乘以图像的(0,0)值,依此类推。我们得到四个值,它们相加并填充到输出的(1,1)位置。
请注意,这会缩小图像的*小。如果滤波器要通过一个边界像素,比如(0,1),那么就没有输出,因为这个像素没有相邻像素可供卷积通过。这将在宽度为 k/2 的图像周围显示为黑色边框。
当*小为 k x k 的卷积滤波器通过*小为 n x n 的图像时,输出*小变为 n-k+1。
为了防止缩水,边框周围加了衬垫。参数 padding 设置为“same”,这意味着以不改变原始图像*小的方式在图像周围添加填充。
池层就是这样做的;它汇集图像中一定数量的像素,并捕获最显著的特征(最*汇集)或像素的集合(平均汇集)作为输出。该模型包含一个*小为 2 x 2 的最*池层,它捕获每个 4 像素聚类的最*像素值。这将输出的*小减少到其原始*小的 1/4(或者对于*小为 k x k 的池层,减少 1/k)。
让我们仔细看看这个模型。输入尺寸为 284 x 284 x 3。它通过 16 个 2×2 滤波器的第一卷积层,带有填充。因此,该层的输出尺寸为 284 x 284 x 16。随后的层是尺寸为 4 x 4 的最*池层,它将图像缩小 16 倍,高度方向缩小 4 倍,宽度方向缩小 4 倍。所以输出尺寸是 71 x 71 x 16。下一个卷积层也有填充和 32 个滤波器,输出为 71 x 71 x 32。维度为 2 x 2 的下一个池层将输入缩小到维度 35 x 35 x 32。
该输出被馈送到输出层(全连接/密集层)。但是,在将数据提供给密集层之前,需要将数据重新整形为一维。这是通过展平层实现的。
对于 m 个输入通道和 n 个滤波器/内核*小为 k×k 的滤波器(输出通道)的卷积层,内核单独检查图像的每个通道,并产生每个输出通道的输出。因此,对于输入-输出通道的每个组合,我们需要分配 k×k 个权重。因此,权重参数的数量为 m×k×k×n,偏差的数量等于通道的数量 n。参数的总数为m×k×k×n+n。
池层只不过是聚合像素值。因此这里没有可训练的参数。
让我们验证卷积神经网络的模型参数的吻合。
第一 Conv 层(m = 3,k = 2,n = 16):3×2×2×16+16 =208 第二 Conv 层(m = 16,k = 2,n = 32):16×2×2×32+32 =2080 输出层(m = 39200,n = 10):39200×10+10 =392010
总可训练参数= 208+2080+392010 =394298
3。递归神经网络(RNN): 这些神经网络用于处理顺序数据,或者当前输出不仅取决于当前输入,还取决于先前输入的数据。它用于时间序列预测、自然语言处理等。
RNNs 的一个独特特征是它包含门,允许或省略来自先前隐藏状态的输入添加到当前输入,完全可由用户配置。
有三种主要的 RNN。最基本的是“香草”RNN,其中包含一个门。另外两个是长短期记忆单元(LSTM)和门控循环单元(GRU)。LSTM 有 4 个门,而 GRU 有 3 个,这使得它在计算上比 LSTM 单位更快。
由 Michael Phi 提供:https://towardsdatascience . com/illustrated-guide-to-lstms-and-gru-s-a-step-by-step-a-step-explain-44e 9 EB 85 BF 21
上面是一幅区分 LSTM 和 GRU 建筑的插图,由 Michael Phi 的博客提供,你可以在这里参考。
model=Sequential()
model.add(LSTM(32, input_shape=(100,2)))
model.add(Dense(1))
我们这里有一个简单的 LSTM 模型(4 门),它馈入一个密集输出层。该模型接受三维输入:批量*小、时间戳和特征。与所有 Keras 层的情况一样,批量*小不是一个强制参数,但需要给出其他两个参数。在上述示例中,输入包含 100 个时间步长和 2 个要素。每一个时间步都是一个观察序列(例如一个单词序列)。特征类似于卷积神经网络中的通道。在该模型中,有 2 个输入通道。
在具有 g 个门、m 个输入特征和 n 个输出单元的递归神经网络中,每个门与当前输入以及前一个单元的隐藏状态(输出)有联系。因此,对于每个门,权重参数的数量为 n x n+ m x n。每个输出单元都有一个偏置参数,因此偏置参数的数量为 n。单个门的总参数为 n x n + n x m + n。对于 g 个门,总参数为 g x (n x n + n x m + n)。
LSTM 模式概述
对于上述模型,让我们验证参数吻合。
LSTM 层(g = 3,m = 2,n = 32):3 x(32 x 32+32 x 2+32)=4480 输出层(m = 32,n = 1) : 32 x 1 + 1 = 33
总可训练参数= 4480 + 33 = 4513
知道输入形状对于建立神经网络非常重要,因为所有的线性代数计算都是基于矩阵维数的。然而,这篇文章试图解决围绕它的谜团。此外,Keras 对每个模型和输入形状都有非常全面的文档,这使得处理这个挑战变得稍微容易一些。
参考:
嗨,欢迎来到长短期记忆(LSTM)和门控循环单位(GRU)的图解指南。我是迈克尔…
towardsdatascience.com](/illustrated-guide-to-lstms-and-gru-s-a-step-by-step-explanation-44e9eb85bf21)
https://keras . io/API/Layers/#:~:text = Layers % 20 are % 20 basic % 20 building,variables%20(层的%20weights)。
Python 调试终极指南
让我们探索使用 Python 日志记录、回溯、装饰器等等进行调试的艺术…
即使您编写了清晰易读的代码,即使您用测试覆盖了您的代码,即使您是非常有经验的开发人员,奇怪的错误还是会不可避免地出现,您需要以某种方式调试它们。许多人求助于仅仅使用一堆print
语句来查看他们的代码中发生了什么。这种方法远非理想,还有更好的方法来找出代码中的问题,其中一些我们将在本文中探讨。
记录是必须的
如果您编写的应用程序没有某种日志设置,您最终会后悔的。没有来自应用程序的任何日志会使排除任何错误变得非常困难。幸运的是——在 Python 中——设置基本记录器非常简单:
这就是开始将日志写入文件所需要的全部内容,文件看起来会像这样(您可以使用logging.getLoggerClass().root.handlers[0].baseFilename
找到文件的路径):
这种设置可能看起来足够好了(通常如此),但是拥有配置良好、格式化、可读的日志可以让您的生活变得更加轻松。改进和扩展配置的一个方法是使用由记录器读取的.ini
或.yaml
文件。例如,您可以在配置中执行以下操作:
在 python 代码中包含这种*量的配置将很难导航、编辑和维护。将东西保存在 YAML 文件中使得用非常特殊的设置来设置和调整多个记录器变得更加容易。
如果你想知道所有这些配置字段是从哪里来的,这些在这里有记录,其中*部分只是第一个例子中所示的关键字参数。
所以,现在文件中有了配置,意味着我们需要加载 is。对于 YAML 文件,最简单的方法是:
Python logger 实际上并不直接支持 YAML 文件,但它支持字典配置,可以使用yaml.safe_load
从 YAML 轻松创建。如果你倾向于使用旧的.ini
文件,那么我只想指出,根据文档,使用字典配置是新应用的推荐方法。更多例子,请查看日志食谱。
伐木装饰工
继续前面的日志记录技巧,您可能会遇到需要记录一些有问题的函数调用的情况。您可以使用 logging decorator 来代替修改所述函数的主体,它会用特定的日志级别和可选的消息来记录每个函数调用。让我们来看看装潢师:
不骗你,这可能需要一点时间来理解(你可能想直接复制粘贴并使用它)。这里的想法是,log
函数获取参数,并使它们对内部的wrapper
函数可用。然后,通过添加附属于装饰器的访问函数,使这些参数变得可调整。至于functools.wraps
装饰器——如果我们不在这里使用它,函数名(func.__name__
)将被装饰器名覆盖。但是这是一个问题,因为我们想打印名字。这由functools.wraps
解决,因为它将函数名、文档字符串和参数列表复制到装饰函数上。
无论如何,这是上面代码的输出。很漂亮,对吧?
repr 获取更多可读的日志
使代码更易调试的简单改进是在类中添加__repr__
方法。如果你不熟悉这个方法,它所做的只是返回一个类的实例的字符串表示。使用__repr__
方法的最佳实践是输出可用于重新创建实例的文本。例如:
如果如上所示表示对象不可取或不可能,好的替代方法是使用<...>
表示,例如<_io.TextIOWrapper name='somefile.txt' mode='w' encoding='UTF-8'>
。
除了__repr__
之外,实现__str__
方法也是一个好主意,默认情况下,当print(instance)
被调用时使用这个方法。有了这两种方法,你就可以通过打印变量获得*量信息。
用于字典的 missing Dunder 方法
如果你出于某种原因需要实现自定义字典类,那么当你试图访问一些实际上并不存在的键时,你会发现一些来自KeyError
的错误。为了避免在代码中摸索并查看哪个键丢失,您可以实现特殊的__missing__
方法,该方法在每次KeyError
被引发时被调用。
上面的实现非常简单,只返回并记录丢失了键的消息,但是您也可以记录其他有价值的信息,以提供更多关于代码中哪里出错的上下文。
调试崩溃应用程序
如果您的应用程序在您有机会看到发生了什么之前就崩溃了,您可能会发现这个技巧非常有用。
用-i
参数(python3 -i app.py
)运行应用程序会导致程序一退出就启动交互式 shell。此时,您可以检查变量和函数。
如果这样还不够好,可以带个更*的锤子—pdb
—Python 调试器。有相当多的特性足以保证一篇独立的文章。但这里有一个例子和最重要的位的纲要。让我们先看看我们的小崩溃脚本:
现在,如果我们用-i
参数运行它,我们就有机会调试它:
上面的调试会话非常简要地展示了您可以用pdb
做什么。程序终止后,我们进入交互式调试会话。首先,我们导入pdb
并启动调试器。此时,我们可以使用所有的pdb
命令。作为上面的例子,我们使用p
命令打印变量,使用l
命令列出代码。*多数时候,你可能想设置断点,你可以用b LINE_NO
来设置,然后运行程序直到断点被命中(c
),然后用s
继续单步执行函数,也可以用w
打印堆栈跟踪。关于命令的完整列表,您可以查看[pdb](https://docs.python.org/3/library/pdb.html#debugger-commands)
文档。
检查堆栈跟踪
比方说,你的代码是运行在远程服务器上的 Flask 或 Django 应用程序,在那里你不能获得交互式调试会话。在这种情况下,您可以使用traceback
和sys
包来更深入地了解代码中的失败之处:
运行时,上面的代码将打印最后一次引发的异常。除了打印异常,您还可以使用traceback
包来打印堆栈跟踪(traceback.print_stack()
)或提取原始堆栈帧,将其格式化并进一步检查(traceback.format_list(traceback.extract_stack())
)。
调试期间重新加载模块
有时,您可能正在调试或试验交互式 shell 中的某些功能,并对其进行频繁的更改。为了使运行/测试和修改的循环更容易,您可以运行importlib.reload(module)
以避免每次更改后都必须重启交互会话:
这个技巧更多的是关于效率而不是调试。能够跳过一些不必要的步骤,让你的工作流程更快更有效率,这总是很好的。一般来说,不时地重新加载模块是一个好主意,因为它可以帮助您避免试图调试同时已经修改了很多次的代码。
调试是一门艺术
结论
*多数时候,编程的真正含义只是*量的尝试和错误。另一方面,在我看来,调试是一门艺术,精通它需要时间和经验——你对你使用的库或框架了解得越多,就越容易。上面列出的提示和技巧可以让您的调试更高效、更快速,但是除了这些特定于 Python 的工具,您可能还想熟悉一些通用的调试方法——例如,Remy Sharp 的调试艺术。
如果你喜欢这篇文章,你应该看看我下面的其他 Python 文章!
从零开始任何项目都可能是一项艰巨的任务…但如果您有这个最终的 Python 项目蓝图就不会了!
towardsdatascience.com](/ultimate-setup-for-your-next-python-project-179bda8a7c2c) [## 自动化 Python 项目的各个方面
每个 Python 项目都可以从使用 Makefile、优化的 Docker 映像、配置良好的 CI/CD、代码…
towardsdatascience.com](/automating-every-aspect-of-your-python-project-6517336af9da) [## 让 Python 程序快得惊人
让我们看看我们的 Python 程序的性能,看看如何让它们快 30%!
towardsdatascience.com](/making-python-programs-blazingly-fast-c1cd79bd1b32)
熊猫终极指南——创建数据框架
劳拉·伍德伯里摄于佩克斯
用 Python 从头开始构建数据
任何数据分析工作的第一步,或许也是最重要的一步,是获取你的原材料;你的数据。
根据项目的成熟度,这个阶段可能非常简单——向同事请求 csv,查询结构良好的数据库,等等。或者更复杂一些,比如构建一个定制的 web scraper。
但并不是所有项目所需的数据都来自外部来源。有时候你需要自己编造。
在这篇文章中,我将从头开始介绍创建数据帧的过程。
理解“数据框架”方法
创建新数据帧的最简单方法是使用“DataFrame”方法。如果你熟悉面向对象编程,你会注意到这实际上是一个构造函数调用,它实例化了一个新的 DataFrame 对象。
所有参数都是可选的,这意味着您可以通过传入… nothing 来创建一个空的数据帧:
import pandas as pd
empty_df = pd.DataFrame()
如果您希望稍后用数据填充一个空的数据帧,这可能会很有帮助。例如,您可以存储几个机器学*模型的汇总结果,方法是从一个空的数据帧开始,然后编写一个循环来填充每个模型的行。
然而,在*多数情况下,您会希望从一开始就用数据填充数据帧。幸运的是,Pandas 非常灵活,允许程序员将各种数据结构转换成数据帧。下面是一个综合指南:
从列表的列表中创建数据帧
将一个列表列表传递给 DataFrame 方法的第一个参数,其中每个内部列表都将是未来的一行。您通常还需要包含一个“columns”参数,它是您想要使用的列名的简单列表。
data = [[‘panda’, ‘bamboo’, 100],
[‘koala’, ‘eucalyptus’, 100]]pd.DataFrame(data, columns = [‘animal’, ‘favorite_food’, ‘cuteness_level’])
从字典创建数据帧—方法 1
传递一本字典作为第一个参数。字典中的键将成为数据帧中的列。字典中的值应该是将填充列的列表:
data = {‘animal’: [‘panda’, ‘koala’],
‘favorite_food’: [‘bamboo’, ‘eucalyptus’],
‘cuteness_level’: [100, 100]}pd.DataFrame(data)
从字典创建数据帧—方法 2
还可以使用 DataFrame 类的类方法中的 from_dict 方法从字典中创建 DataFrame。
这样做的好处是,您可以将“orient”参数设置为“index”,这将改变字典填充数据帧的方式。在这种情况下,字典的键将成为行索引,其值将成为行:
data = {‘animal’: [‘panda’, ‘koala’],
‘favorite_food’: [‘bamboo’, ‘eucalyptus’],
‘cuteness_level’: [100, 100]}pd.DataFrame.from_dict(data, orient = ‘index’, columns = [‘first_animal’, ‘second_animal’])
从字典列表创建数据帧
将字典列表传递给第一个参数。对于每个字典,键对应于列名,值对应于数据帧每一行中的值:
data = [{'animal': 'panda',
'favorite_food': 'bamboo',
'cuteness_level': 100}, {'animal': 'koala',
'favorite_food': 'eucalyptus',
'cuteness_level': 100}]pd.DataFrame(data)
从 numpy 数组创建数据帧
将 numpy 数组直接传递给 DataFrame 方法:
data = np.array([(‘panda’, ‘bamboo’, 100),
(‘koala’, ‘eucalyptus’, 100)],
dtype=[(‘animal’, ‘U6’),
(‘favorite_food’, ‘U6’),
(‘cuteness_level’, int)])pd.DataFrame(data)
我的建议
在这篇文章中,我们介绍了创建新数据框架的各种方法。
当我从头开始构建一个表时,我更喜欢使用列表列表方法,因为它有最直观的翻译。每个内部列表都是水平呈现的,实际上看起来就像它在数据帧中变成的行。编码快乐!
终极熊猫指南——像专家一样检查数据
劳拉·伍德伯里摄于佩克斯
无论您正在进行简单的分析还是复杂的机器学*模型,能够快速回答关于数据性质的探索性问题都有很*的价值。
幸运的是,熊猫让这变得容易了。在本文中,我将介绍几个 DataFrame 属性和方法,它们使数据检查变得轻松而高效。
头/尾
用例:快速浏览我们的数据框架。
默认情况下,head 方法返回数据的前 5 行:
#returns the first five rows
df.head()
但是我们也可以指定不同的行数来返回:
#returns the first six rows
df.head(6)
tail 方法以同样的方式工作,但是从数据帧的底部而不是顶部开始返回行:
df.tail(2)
列
用例:从我们的数据框架中获取列名。
df.columns
当我需要记住列名的确切拼写或者当我发现哪些数据点可用时,我发现这很有用。
如果这个对象更方便,我们也可以将索引转换为列表:
list(df.columns)
形状
用例:确定数据帧中的行数和列数。
此方法返回一个元组,其第一个值是行数,第二个值是列数:
df.shape
为了只获得行数,我们简单地索引元组以提取第一个元素:
df.shape[0]
我喜欢使用这个属性来查找符合特定条件的数据的百分比。
例如,我们可以筛选数据帧,以获取一列中小于 70 的值和另一列中小于 1000 的值的百分比:
df[ (df.ints < 70) & (df.floats < 1000) ].shape[0] / df.shape[0]
唯一/值计数
用例:获取 DataFrame 列中的不同值(即系列对象)。
df[‘strings’].unique()
Value_counts 类似于 unique,但它不是返回唯一值的数组,而是返回每个值的频率序列:
df[‘strings’].value_counts()
信息
用例:获取关于我们的索引、列和内存使用的汇总信息。
df.info(verbose = True)
我们将“True”传递给“verbose”参数,以确保我们在处理更*的数据集时能够获得完整的列摘要。
形容
用例:获取数据中列的汇总统计信息。
默认情况下,describe 方法只包含数字列:
df.describe()
我们可以用几种方式定制我们的输出。
首先,我们可以通过向“percentiles”参数传递一组介于 0 和 1 之间的值来获得不同的百分点值:
df.describe(percentiles = [.1, .99])
我们还可以使用“include”和“exclude”参数来指定我们希望在输出中包含哪些类型的列。
如果我们将“all”传递给“include”参数,那么除了数字列之外,我们还将获得非数字列的摘要。
对于非数字列,我们将获得计数、唯一计数、top(最常见值)和 freq(最常见值的频率):
df.describe(include = 'all')
最后,如果我们想在输出中进一步定制列,我们可以将数据类型列表传递给 include 或 exclude 参数。
奖金-最*行数和列数
默认情况下,pandas 将输出中的行数和列数分别限制在 60 和 20。
如果我们试图返回一个 70 行的数据帧,我们将得到如下结果:
当我们想要查看所有数据时,这可能会很烦人。
我们可以通过将“max_rows”或“max_columns”传递给 pandas 的“set_option”方法并指定一个值来解决这个问题:
pd.set_option(‘max_rows’, 70)
当我想灵活地查看我的数据的一小部分时,我发现这个技巧非常有用。
结束语
Pandas 通过许多用户友好的方法和属性使数据检查变得相当简单。您还可以编写自己的检查和探索方法来扩展功能。这是使用 Python 这样的脚本语言进行数据分析的优势之一。
编码快乐!
熊猫终极指南-使用 Python 连接数据
劳拉·伍德伯里摄于佩克斯
掌握“合并”和“加入”的区别
每个从事数据工作的人都知道这一点:在你建立机器学*模型或产生令人惊叹的可视化之前,你必须让你的手弄脏数据争论。
数据争论的核心技能之一是学*如何将不同的数据源连接在一起。
在这篇文章中,我将通过介绍两种核心连接方法——连接和合并,为 pandas 中的连接提供一个全面的指南
如果您需要复*加入数据的含义,我建议您在继续之前查看我的加入数据初学者指南:
左接合。内部联接。外部联接。啊?
towardsdatascience.com](/a-beginners-guide-to-joining-data-935c8e579fb7)
“合并”方法
与连接的情况一样,进行 Pandas 合并需要三个基本要素:
1.你的两个数据集
2.要执行的联接类型
3.您的连接密钥
数据源
Merge 是 DataFrame 类的一个方法。这意味着无论何时你调用一个合并,它实际上是被一个数据帧调用的。
“调用”数据帧是您的左侧表。
该方法的第一个参数是您的右表。
再次注意,table1 是您的左表,table2 是您的右表。
连接类型
你需要的第二个关键因素是你想要使用的连接类型。
在 merge 函数中,您可以通过“how”参数来实现这一点:
table1.merge(table2, **how = ‘left’**)
“How”可以取值为:left、right、inner 或 outer——类似于*多数版本的 SQL。
连接键
在选择连接键时,Pandas 提供了高度的灵活性。
下面是传递连接键的三种主要方法:
- 使用“on”参数 —这是最直观的解决方案,但是它要求您试图连接的列具有相同的名称。
companies.merge(stock_price, how = 'left', on = 'Stock Ticker')
2.使用“left_on”和“right_on”参数 —对于此方法,您将“left_on”设置为您想要在左侧表格中使用的列,将“right on”设置为您想要在右侧表格中使用的列:
companies.merge(stock_price, how = ‘left’, left_on = ‘Stock Ticker’, right_on = ‘Ticker’)
3.使用“left_index”和“right_index”参数:这些是布尔参数——将它们设置为 True 以表明您想要使用索引作为您的连接键:
companies.merge(stock_price, how = ‘left’, left_index = True, right_index = True)
注意,如果您想要连接多个键,您可以简单地将列名列表传递给“on”、“left_on”或“right_on”参数。
另请注意,根据您的使用情况,您可以混合搭配“left_on”、“right_on”、“left_index”和“right_index”。
“加入”方法
join 方法实际上非常类似于 merge 方法。关键的区别在于连接方法强制使用右连接键的索引,默认使用左连接键的索引:
companies.join(stock_price, how = ‘left’)
如果您总是喜欢索引连接,您可能会发现这更容易,因为它需要的参数更少。
另一个区别非常小 join 方法默认将“how”参数设置为“left ”,而 merge 方法默认为“inner”。
我的偏好
就个人而言,我更喜欢使用 merge 方法并用“on”参数连接列。如果我需要重命名列,就简单性和可读性而言,这是值得的。
但这只是我的看法。关于 pandas 的一个伟*的事情是,它为您提供了很*程度的灵活性来选择最适合您的语法。编码快乐!
熊猫终极指南——驾驭群体
劳拉·伍德伯里摄于佩克斯
加深对“分组”和“聚集”的理解
数据分析中最常见的练*之一是将数据分组并执行聚合。
例如,假设您有几个不同维度的客户销售数据:
一个自然的问题可能是问——各州的总销售额是多少?还是按性别?还是按月?
在这篇文章中,我将介绍熊猫“groupby”的来龙去脉,帮助你自信地回答 Python 中的这类问题。
“分组”数据的可视化表示
记住“分组”是做什么的最简单的方法是把它分成三个步骤:“分割”、“应用”和“组合”。
- Split: 这意味着基于数据中的一列创建单独的组。例如,我们可以将销售数据分成个月。
2.应用:这意味着我们对每个组执行一个功能。例如,我们可以合计每个月的销售额。
3.合并:这意味着我们返回一个新的数据表,包含“应用”阶段的每个结果。
“groupby”的神奇之处在于它可以帮助你在非常紧凑的代码中完成所有这些步骤。
在熊猫中进行“分组”
为了获得每月的销售额,我们可以简单地运行以下命令:
sales_data.groupby('month').agg(sum)[['purchase_amount']]
了解熊猫的“分裂”步骤
首先要指出的是,当我们运行上面的代码时,我们实际上是在运行两个不同的函数——groupby 和 agg——其中 group by 处理“拆分”阶段,agg 处理“应用”阶段。
例如,下面的代码实际上将我们的数据分成“月”组:
sales_data.groupby('month')
虽然这个输出并不特别有趣,但是在到达聚合阶段之前,我们还可以对这个对象做一些事情:
- 查看“组”属性:
grouped = sales_data.groupby('month')
grouped.groups
注意,“groups”属性返回一个字典,它的键是组,值是每个组的行索引。
2。使用“获取组”方法检查单个组:
grouped.get_group('August')
3。遍历每个组:
for name, group in grouped:
print(name, group)
了解熊猫的“agg”步骤
现在我们来探讨一下“agg”函数。
我们可以传递给“agg”的最简单的东西是我们想要在每个组上执行的聚合的名称:
sales_data.groupby('month').agg(sum)
请注意,这种方法将返回数据帧中所有可用数字列的总和。
但是,在本例中,返回“year”或“customer_id”列的总和没有任何意义。我们可以通过使用我们希望在 agg 调用结束时看到的列列表进行索引来解决这个问题:
sales_data.groupby('month').agg(sum)[['purchase_amount']]
我们也可以用一个单独的列进行索引(与 list 相反):
sales_data.groupby('month').agg(sum)['purchase_amount']
在这种情况下,我们得到的是一个 Series 对象,而不是 DataFrame。我倾向于使用数据框架,所以我通常选择第一种方法。
高级“分组依据”概念
现在我们已经有了基本的东西,让我们来看看我们能做的一些更高级的事情。
多重聚合
首先,假设我们想要总销售额和每月平均销售额。为此,我们可以向“agg”传递一个函数列表:
sales_data.groupby(‘month’).agg(**[sum, np.mean]**)[‘purchase_amount’]
这是有帮助的,但是现在我们被以聚合函数命名的列(即总和及平均值)。
当我们想要为多个列返回多个聚合时,这就变得更加困难了:
sales_data.groupby(‘month’).agg(**[sum, np.mean]**)[[‘purchase_amount’, 'year']]
在这种情况下,我们坚持使用多索引作为列名:
为了解决这个问题,我们可以利用 Pandas 提供的“NamedAgg”对象。这里的语法有点不同,但是我们的输出非常清楚地表明了这里发生的事情:
sales_data.groupby(“month”).agg(
total_sales=pd.NamedAgg(column=’purchase_amount’, aggfunc=sum),
avg_sales=pd.NamedAgg(column=’purchase_amount’, aggfunc=np.mean),
max_year=pd.NamedAgg(column=’year’, aggfunc=max))
这是一种非常有用的机制,可以在不同的列上执行多种功能,同时保持对输出中列名的控制。
我们还可以向 agg 函数传递一个字典,但是这并没有给我们提供相同的灵活性来命名我们得到的列:
sales_data.groupby(“month”).agg( {‘purchase_amount’: [sum, np.mean],
‘year’: [max]})
多列分组
我们可能想做的另一件事是按月份和州获取总销售额。
为了按多列分组,我们只需将一个列表传递给 group by 函数:
sales_data.groupby(["month", "state"]).agg(sum)[['purchase_amount']]
您还会注意到我们的“分组关键字”——月份和州——已经成为我们的索引。通过运行“reset_index”函数,我们可以很容易地将它们转换成列:
g = sales_data.groupby([“month”, “state”]).agg(sum) g[[‘purchase_amount’].reset_index()
结束语
一旦你对“分割-应用-组合”方法有了坚实的直觉,在 Pandas 中运行“分组”就相当简单了。
尽管如此,当您第一次熟悉这些函数时,仍然可能会遇到语法问题。如果您面临错误,我建议您更仔细地查看您传递的数据类型。
例如,如果您正在运行类似于下面的代码,请确保您正在将一个函数列表的传递给 agg,并且您正在将一个列列表的放置在另一组括号内以进行列索引:
sales_data.groupby(‘month’).agg(**[sum, np.mean]**)[**[‘purchase_amount’, ‘year’]**]
关于熊猫索引的快速回顾,请查看我下面的直观指南。编码快乐!
熊猫终极指南——重塑您的数据
劳拉·伍德伯里摄于佩克斯
通过掌握“pivot_table”和“melt”来控制您的数据
概括地说,数据可以用两种形状来组织——长型或宽型。
例如,我们可以用两种方式表示总统选举结果:
当我们的数据很长时,这意味着每条记录代表我们测量的变量的一个实例。在上面的例子中,每个记录都有候选人、州和他们收到的票数。
当我们的数据很宽时,这意味着我们测量的变量是跨行和列分布的。在上面的例子中,每条记录是一个州,我们有为每个候选人投票的列。
Pandas 最有用的功能之一是能够快速轻松地重塑数据。
在这篇文章中,我将介绍如何毫不费力地在各种形状之间转换,以最好地满足您的分析需求。
为什么我们需要不同的形状?
最终,“正确”的形状将总是取决于用例。不同的场景需要不同的形状——这就是为什么我们需要在它们之间切换的功能。
比方说,我们的选举数据有一个“长”形:
如果我们想建立一个模型来预测希拉里·克林顿获得总票数的百分比,这种形状是行不通的。为了创建州级预测模型,我们需要州级数据。
另一方面,如果我们想使用 seaborn 可视化软件包绘制一个条形图,我们将需要长格式。这并不是因为任何高度技术性的原因,只是因为 seaborn barplot 方法期望数据是这种格式。
长到宽—“数据透视表”
“pivot_table”方法是一种将数据形状从长变宽的简单方法。
这是一个 DataFrame 方法,因此它会被您想要整形的 DataFrame 调用:
data.pivot_table()
有四个主要参数要传递给此方法:
- Index :我们希望在输出表中用作索引的列的名称
- 列:其唯一值将成为输出表中的列的列名
- 值:我们想要聚合的列的名称
- Aggfunc :我们要使用的聚合函数的名称
为了让这一点变得生动,下面是一个使用“pivot_table”方法将数据从长变宽的例子:
long_data.pivot_table(index = ‘state’, columns= ‘candidate’,
values = ‘candidatevotes’, aggfunc = ‘sum’)
注意,如果我们的索引/列对有重复的值,那么只需要传递“values”和“aggfunc”参数。
换句话说,如果我们有希拉里·克林顿在阿拉巴马州的两个计票记录,我们将希望传递“values”和“aggfunc ”,以便告诉该方法如何处理这些重复的记录(即将它们相加、平均等等。).
默认方法是使用 np.mean 函数。这可能导致潜在的陷阱,因为我们不总是想要在上面的例子中取平均值。为了避免问题,您应该确保知道您的数据中是否有重复项。
还有另一种方法——“pivot”——类似于 pivot_table。
但是,“pivot”不允许您拥有副本。如果我们尝试使用“pivot ”,并且我们有两个阿拉巴马州希拉里·克林顿的记录,此方法将返回 ValueError:
因为“pivot”限制性更强,所以我建议在需要从 long 转换到 wide 时,只需使用“pivot_table”即可。
从宽到长—“融化”
Melt 是我在 Pandas 中最喜欢的方法之一,因为它提供了“反透视”功能,这比它的 SQL 或 excel 等价物要简单得多。
与我们的“pivot_table”方法类似,我们将使用几个主要参数来取消数据透视:
- id_vars :您希望在输出中保持不变的列的名称
- value_vars: 要在输出中折叠成单个分类列的列名
- var_name: 输出表中 value_vars 的列名。
- value_name :输出表中值的列名。
下面是一个使用我们的选举数据的例子:
wide_data.melt(id_vars = 'state',
value_vars = ['Clinton, Hillary','Johnson, Gary','Stein, Jill','Trump, Donald J.'],
var_name = 'candidate',
value_name = 'candidatevotes')
请注意,默认情况下,该方法会将“value_vars”参数设置为表中不在“id_vars”中的列的完整列表。因此,如果我们确实想使用所有这些列,从技术上讲,我们不需要向“value_vars”传递任何东西。
另一方面,通过在“value_vars”参数中仅包含一个候选项,很容易获得该候选项的数据:
wide_data.melt(id_vars = 'state',
value_vars = ['Clinton, Hillary'],
var_name = 'candidate',
value_name = 'candidatevotes')
在帖子的开头,我提到了 seaborn 函数,barplot 要求我们的数据是长格式的。
如果你感到好奇,下面的代码可以比较佛罗里达州和德克萨斯州候选人的投票结果:
sns.barplot(x=”candidate”, y=”candidatevotes”, hue=”state”, data=long_data[long_data.state.isin([‘Texas’, ‘Florida’])])
同样,请注意,如果我们的数据是宽格式的,这是行不通的,因为我们没有一个单独的数字列传递给“y”参数。
结束语
与 SQL 或 Excel 等其他分析工具相比,Pandas 在重塑数据方面更胜一筹。
通过两个简单的函数 pivot _ table 和 melt 您可以有效地重塑您的数据,以帮助促进分析管道中接下来的连接、可视化或模型训练。编码快乐!
终极熊猫指南:时间序列窗口函数
劳拉·伍德伯里摄于佩克斯
掌握时间序列分析的“移位”、“滚动”和“扩展”
在我的上一篇文章中,我演示了如何基于列值在 Pandas 中运行窗口函数。
这种方法在我们想要知道关于单个记录 和它们所属的 组的信息时非常有用。
例如,如果我们有客户级别的交易数据,像这样的方法可以为我们提供关于每笔交易的信息,以及交易发生当月的总销售额:
在本文中,我将介绍另一种类型的窗口函数——我们根据行的位置而不是分类列的值来执行计算。
关于数据的注释
对于我下面的例子,我将使用来自 Kaggle 上的 NCAA *联盟比赛的一些游戏级别的篮球数据。
如果你不喜欢篮球,不要害怕。
这里有一个快速数据字典,告诉你所有你需要知道的变量:
- DayNum: 我们对时间的度量。它计算游戏进行到赛季的第几天。
- 游戏发生的年份。
- Team1/Team2: 参赛队伍的 id。
- 效率:衡量团队 1 在游戏中的表现。
- 结果:团队 1 是否赢得比赛的标志(1 表示赢,0 表示输)。
最后,让我们假设我们的目标是预测谁赢得了每场比赛。我们不会在这里做任何机器学*,但这将有助于激发用例。
创建“移位”列
让我们从做一个简单的“转移”开始。
这个方法确实像它听起来那样。它将一列中的值向前或向后移动。
下面我们将游戏效率提高 1:
game_data[‘prior_game_outcome’] = game_data[‘Outcome’].shift(1)
现在,对于每场比赛,我们都知道团队 1 在前一场比赛中表现如何。也许这能帮助我们预测结果。
请注意,我们可以传递正整数或负整数来向前或向后移动:
game_data['NextEfficiency'] = game_data['GameEfficiency'].shift(-1)
还要记住,当我们对第一行进行正移位时,我们会得到一个空值,因为前一行中没有可用的数据。我们可以通过设置“fill_value”参数用我们选择的不同值替换空值来解决这个问题。
在滚动窗口上执行聚合
虽然 shift 方法很有用,但它不允许我们对之前或未来的行执行任何功能。
例如,我们可能想要找出团队 1 在前三场比赛中的平均效率。
这是我们可以利用滚动方法的地方。
基本语法非常简单——我们只需要传递我们想要查看的先前行数,然后执行聚合:
game_data[‘AvgEfficiency’] = game_data[‘GameEfficiency’].rolling(3).mean()
请注意,这里有两个技术步骤:“rolling”方法创建一个滚动对象,然后“mean”方法返回新列。
我们可能想在这里再做一次调整。默认情况下,每个记录都包含在自己的窗口平均值中。如果我们想预测每场比赛,这是不好的,因为这意味着我们有关于结果的信息编码在平均值中。
为了解决这个问题,我们可以添加一个移位函数,以便包含前 3 行的数据(而不是当前行和前 2 行):
game_data[‘AvgEfficiency’] = game_data[‘GameEfficiency’].shift(1).rolling(3).mean()
在扩展窗口上执行聚合
扩展方法与滚动方法非常相似,只是它会根据给定的数据创建尽可能*的窗口。
这里有一个例子:
game_data[‘AvgEfficiency’] = game_data[‘GameEfficiency’].shift(1).expanding().mean()
请注意,倒数第二行中的 AvgEfficiency 值与我们运行上面的滚动方法(12.02)时相同,但最后几行不同(2.41 对 13.46)。
这是因为当我们在倒数第二行时,我们只有 3 个先前的记录可用,但是当我们到达最后一行时,我们有 4 个先前的记录。同样,扩展方法使用尽可能多的数据。
最后,如果我们希望确保扩展窗口至少有一定数量的记录,以便应用聚合,我们可以使用“min_periods”参数:
game_data[‘AvgEfficiency’] = game_data[‘GameEfficiency’].shift(1) .expanding(min_periods = 4).mean()
将所有这些放在一起:一个高级用例
我们从区分基于列的窗口和基于位置的窗口开始这篇文章。
我们的最终用例利用了这两者。
以上所有工作的问题是:我们的数据集中实际上有多个球队和赛季,所以我们在分类数据的边缘遇到了问题:
为了解决这个问题,我们需要将我们的数据分成团队/赛季组 和 执行扩展窗口聚合:
game_data[‘AvgEfficiency’] = game_data.groupby([‘Season’, ‘Team1’])[[‘GameEfficiency’]].transform(lambda x: x.shift(1).expanding().mean())
现在我们的扩展平均计算开始为每个队的时间表新鲜!
上面的代码中有很多内容,但我是这样想的:
- 首先,我们将数据分成季节组和团队组 1
- 然后,我们将一个定制的匿名函数(λ)应用到每一组,该函数会移动数据并计算一个扩展平均值
- 最后,我们将这些值返回到原始索引
结束语
在这篇文章中,我们浏览了熊猫的时间序列窗口函数。其中一些方法的语法可能有点复杂,但是当你把它分解成更小的步骤时,一切都会变得更加清晰。花些时间尝试每种方法,直到你掌握了窍门,这也没有坏处。编码快乐!
熊猫终极指南——窗口功能
劳拉·伍德伯里摄于佩克斯
掌握您对数据中的组的理解
窗口函数是一种有效的方法,可以更好地了解数据中的每条记录与其所属的组之间的关系。
它们也是常见的数据科学面试问题,因此很好地理解它们是件好事。
在这篇文章中,我将对窗口函数以及如何在 Pandas 中实现它们进行直观的解释。
了解窗口功能
在标准的“分组”中,我们将数据分成组,应用聚合,然后将每个结果合并到一个新表中。
这有助于我们回答关于数据中群体特征的问题。
例如,如果我们有每月客户级别的销售数据,我们可以使用 groupby 来了解我们每月的总销售额:
如果你对这个功能有点生疏,我建议你看看我在 groupby 函数上的帖子:
数据分析中最常见的练*之一是将数据分组并执行聚合。
towardsdatascience.com](/ultimate-pandas-guide-mastering-the-groupby-104306251739)
如果我们只关心月销售额,上面的输出是有用的。
但是这个结果的缺点是,我们的信息现在分布在两个表中——第一个表给出销售明细,第二个表给出每个月的汇总信息。
窗口函数通过将组级别的聚合返回到初始表来帮助我们弥合这一差距:
如果不清楚这为什么有用,让我们在输出中添加一个百分比列:
现在我们可以看到每个记录占每月总销售额的百分比。
窗口函数很有用,因为它帮助我们运行这些类型的计算,而不必执行任何单独的连接。
使用变换方法的 pandas 中的窗口函数
Pandas 中窗口函数的语法非常简单,非常类似于我们在 groupby 聚合中使用的语法。
关键区别在于,要执行窗口功能,我们使用“transform”方法,而不是“agg”方法:
transform 方法返回一个序列而不是一个数据帧,所以如果我们希望它成为原始数据帧的一部分,我们需要将它作为一列添加:
sales_data[“monthly_sales”] = sales_data.groupby(“month”).transform(
sum)[“purchase_amount”]
就是这样!不算太坏。
让我们再举一个例子。下面我们按州对每笔交易进行排名:
sales_data['state_rank'] = sales_data.sort_values(by = 'purchase_amount', ascending = False).groupby("state")
.transform('rank', method = 'min')["purchase_amount"]
请注意以下几点:
- 我们使用“sort_values”函数来确保我们按照购买金额的顺序进行排序。
- 我们将一个参数(“min”)传递给转换中的“method”参数。这是 rank 方法的一个参数,在执行时传递给它。
- 当我们想要返回组内的“顶部”值时,这种类型的分析往往很有用。例如,我们可以筛选每个州内前 2 笔交易的结果,然后将这些结果传递给覆盖每个州的销售代表。
Pandas 中的窗口函数与 SQL
对于那些有很强 SQL 背景的人来说,这个语法可能有点奇怪。
在 SQL 中,我们通过从聚合开始执行窗口函数,然后将它应用于可选的“partition by”和“order by”之上:
select rank() over (partition by state order by purchase_amount desc)
为了帮助协调这两种方法,我将上面的 Pandas 代码的元素转换成它们的 SQL 等价物:
结束语
在 Pandas 中,窗口函数功能强*、高效,并且实现起来相当简单。
但是,值得注意的是,“transform”方法并不是执行基于位置的聚合的正确方法,比如对前 5 条记录的列值求和。为了解决这个问题,我们将在以后的文章中更深入地研究熊猫的“转变”和“扩展”功能。
掌握时间序列分析的“移位”、“滚动”和“扩展”
towardsdatascience.com](/ultimate-pandas-guide-time-series-window-functions-a5362b782f3e)
编码快乐!
终极 PySpark 备忘单
照片由Genessa pana intet在 Unsplash 拍摄
数据科学
PySpark 数据帧 API 的简短指南
S park 是当今数据工程、数据科学领域的主要参与者之一。随着处理更多数据的需求不断增加,企业经常将 Spark 集成到数据堆栈中,以解决快速处理*量数据的问题。由 Apache 维护,Spark 生态系统中的主要商业参与者是 Databricks(由 Spark 的最初创建者所有)。Spark 已经被各种公司和机构广泛接受——本地的和云中的。一些最受欢迎的底层使用 Spark 的云产品有 AWS Glue 、 Google Dataproc 、 Azure Databricks 。
没有一种技术,没有一种编程语言对所有用例都足够好。Spark 是用于解决*规模数据分析和 ETL 问题的众多技术之一。在 Spark 上工作了一段时间后,我想到用真实的例子来编写一份备忘单。尽管有很多关于在 Scala 中使用 Spark 的参考资料,但除了 Datacamp 上的之外,我找不到一个像样的备忘单,但我认为它需要更新,需要比一页纸更广泛一点。
首先,一个关于 Spark 如何工作的体面介绍—
回想一下之前的 Spark 101 博客,您的 Spark 应用程序是作为一组并行任务运行的。在这篇博文中…
mapr.com](https://mapr.com/blog/how-spark-runs-your-applications/)
配置和初始化
在开始编写哪几行代码来启动和运行 PySpark 笔记本/应用程序之前,您应该了解一点关于SparkContext
、SparkSession
和SQLContext
的知识。
SparkContext
—提供与 Spark 的连接,能够创建 rddSQLContext
—提供与 Spark 的连接,能够对数据运行 SQL 查询SparkSession
—包罗万象的上下文,包括对SparkContext
、SQLContext
和HiveContext
的覆盖。
我们将在一些例子中使用 MovieLens 数据库。这是那个数据库的链接。你可以从 Kaggle 下载。
超过 45,000 部电影的元数据。超过 270,000 名用户的 2,600 万次评分。
www.kaggle.com](https://www.kaggle.com/rounakbanik/the-movies-dataset)
读取数据
Spark 支持从各种数据源读取数据,比如 CSV、Text、Parquet、Avro、JSON。它还支持从 Hive 和任何具有可用 JDBC 通道的数据库中读取数据。以下是如何在 Spark 中阅读 CSV 的方法—
在您的 Spark 之旅中,您会发现有许多方法可以编写相同的代码来获得相同的结果。许多函数都有别名(例如dropDuplicates
和drop_duplicates
)。下面的例子展示了在 Spark 中读取文件的几种方法。
写入数据
一旦你完成了数据转换,你会想把它写在某种持久存储上。这里有一个例子,展示了将一个拼花文件写入磁盘的两种不同方式—
显然,基于您的消费模式和需求,您也可以使用类似的命令将其他文件格式写入磁盘。写入 Hive 表时,可以用bucketBy
代替partitionBy
。
世界上有许多不同的工具,每一种都可以解决一系列问题。他们中的许多人是如何判断…
luminousmen.com](https://luminousmen.com/post/the-5-minute-guide-to-using-bucketing-in-pyspark)
bucketBy
和partitionBy
背后的思想都是拒绝不需要查询的数据,即修剪分区。这是一个来自传统关系数据库分区的老概念。
创建数据框架
除了您在上面的读取数据部分看到的直接方法df = spark.read.csv(csv_file_path)
之外,还有一种创建数据帧的方法,那就是使用 SparkSQL 的行构造。
还有一个选项,您可以使用 Spark 的.paralellize
或.textFile
特性将文件表示为 RDD。要将其转换成数据帧,显然需要指定一个模式。这就是pyspark.sql.types
出现的原因。
我们将在 PySpark 中使用*量类似 SQL 的功能,请花几分钟时间熟悉下面的文档。
修改数据帧
数据帧抽象出 rdd。数据集做同样的事情,但是数据集没有表格、关系数据库表那样的 rdd 表示。数据帧有。因此,DataFrames 支持类似于您通常在数据库表上执行的操作,即通过添加、删除、修改列来更改表结构。Spark 提供了 DataFrames API 中的所有功能。事情是这样的—
除了创建新列之外,我们还可以使用以下方法重命名现有的列
如果我们必须删除一列或多列,我们可以这样做—
连接
Spark 使用类似 SQL 的接口背后的整个想法是,有许多数据可以用一个松散的关系模型来表示,即一个没有 ACID、完整性检查等的表模型。考虑到这一点,我们可以预期会发生很多连接。Spark 完全支持连接两个或多个数据集。这里是如何—
过滤
过滤器就像 SQL 中的子句一样。事实上,你可以在 Spark 中互换使用filter
和where
。下面是一个在 MovieLens 数据库电影元数据文件中过滤分级在 7.5 和 8.2 之间的电影的示例。
过滤器支持所有类似 SQL 的特性,例如使用比较运算符、正则表达式和按位运算符进行过滤。
过滤掉空值和非空值是查询中最常见的用例之一。Spark 对列对象提供了简单的isNULL
和isNotNull
操作。
聚集
聚合是处理*规模数据的巨*工作的核心,因为这通常归结为 BI 仪表板和 ML,这两者都需要某种聚合。使用 SparkSQL 库,您几乎可以实现在传统关系数据库或数据仓库查询引擎中所能实现的一切。这里有一个例子展示了 Spark 是如何进行聚合的。
窗口功能和排序
与*多数分析引擎一样,窗口函数已经成为rank
、dense_rank
等的标准。,被*量使用。Spark 利用传统的基于 SQL 的窗口函数语法rank() over (partition by something order by something_else desc)
。
请注意sort
和orderBy
在 Spark 中可以互换使用,除非是在窗口函数中。
这些是我收集的一些例子。很明显,除了一张小抄,还有很多东西值得一试。如果您感兴趣或者在这里没有找到任何有用的东西,可以去看看文档——它相当不错。
下一个 Python 项目的最终设置
从零开始任何项目都可能是一项艰巨的任务…但如果你有这个终极的 Python 项目蓝图就不会了!
Unsplash 上 @sxoxm 的原图
无论您是在从事一些机器学*/人工智能项目,在 Flask 中构建 web 应用程序,还是只是编写一些快速的 Python 脚本,为您的项目提供一些满足您所有需求的模板总是有用的,即:预定义的目录结构,所有必要的配置文件,如pytest.ini
或requirements.txt
,测试,林挺和静态代码分析设置,CI/CD 工具,应用程序的 dockering,以及在自动化之上使用 Makefile 。因此,在这里,我为您带来的正是这个“终极”通用 Python 项目设置。
TL;博士:这是我的储存库,里面有完整的源代码和文档:【https://github.com/MartinHeinz/python-project-blueprint】T21
目录结构
当我为 Golang ( 此处为)写这类文章时,我很难用 Python 弄清楚理想的项目结构,不过,这很简单:
让我们从顶部开始,概述一下我们这里有什么:
blueprint
-这是我们的源代码目录,应该根据你正在使用的应用程序或软件包来命名。在里面,我们有通常的__init__.py
文件,表明它是一个 Python 包,接下来是__main__.py
,当我们想用python -m blueprint
直接运行我们的应用程序时会用到它。这里的最后一个源文件是app.py
,这里只是为了演示。在真实的项目中,你会有更少的顶级源文件和更多的目录(内部包),而不是这个app.py
。稍后我们将讨论这些文件的内容。最后,我们这里还有resources
目录,用于您的应用程序可能需要的任何静态内容,例如图像、密钥库等。- 这个目录中存放着我们的测试套件。我不会在这里讲太多的细节,因为我们将把整个部分用于测试,但只是简单地说:
test_app.py
是源目录中app.py
对应的测试文件- 如果你曾经使用过 Pytest ,那么
conftest.py
可能对你来说很熟悉——这是一个用于指定 Pytest fixtures ,钩子或者加载外部插件的文件。 context.py
通过操纵类路径,帮助从blueprint
目录导入源代码文件。我们将在稍后看到它是如何工作的。
- 这是我们在这个项目中的最后一个目录。它保存了我们用于 CI/CD 的 GitHub 动作的配置。我们有两个文件,第一个-
build-test.yml
负责构建、测试和林挺我们每次推送的源代码。第二个文件-push.yml
在每次我们在 GitHub 上创建标签/发布时,将我们构建的应用程序推送到 GitHub 包注册表。在另一篇博客文章中有更多关于这方面的内容。 Makefile
-除了目录之外,我们的项目中还有一些顶级文件,其中第一个——Makefile
包含目标,帮助我们自动化通常执行的任务,如构建、测试、林挺或清理我们的项目- 这是一个方便的脚本,为你建立一个项目。它实际上重命名并替换了这个项目模板中的虚拟值来代替真实值,比如项目名或包名。很方便,对吧?
这里的其余文件是我们将在这个项目中使用的所有工具的配置文件。让我们跳到下一部分,探索它们的功能和内容。
配置文件
在设置 Python 项目时,有一件事可能会变得相当混乱,那就是当你使用一堆工具时,你最终会得到的配置文件汤,比如 pylint 、 coverage.py 、 flake8 等等。这些工具中的每一个都希望有自己的文件,通常是像.flake8
或.coveragerc
这样的文件,这会在你的项目的根目录中产生许多不必要的混乱。为了避免这种情况,我将所有这些文件合并成一个文件- setup.cfg
:
如果您不熟悉这里使用的所有工具,我将快速描述一下:
- flake 8——是一个在你的项目中强制代码风格的工具——换句话说——它是类似于 pylint 的 linter,我们也将使用它。为什么两个都用?它们确实有重叠,但是它们都有一些规则,而另一个没有,所以以我的经验来看,它们都值得使用。
- Bandit —是一个在 Python 代码中寻找常见安全问题的工具。它的工作原理是从你的代码中创建 AST(抽象语法树),并针对其节点运行插件。开发人员通常不是安全专家,而且我们所有人都会犯这样或那样的错误,所以有一个工具能够为我们发现至少一些安全错误总是很好的。
- Coverage.py —是一个测量 Python 程序代码覆盖率的工具。当我们用 Pytest 运行测试套件并从测试运行中生成覆盖率报告时,它被触发。这些报告可以是终端输出的形式,也可以是 XML 格式,然后可以被 CI 工具使用。
说完这些,让我们来回顾一下setup.cfg
中的内容。对于 Flake8 我们定义了排除模式,这样我们就不会忽略我们不关心的代码。下面是一个空的ignore
部分,以防我们需要全局忽略一些规则。我们还将最*线长度设置为 120,因为在我看来,对于今天的屏幕尺寸来说,将线长度保持为 80 是不合理的。Final line 将 McCabe 复杂度阈值设置为 10,如果你不熟悉圈复杂度你可以在这里找到更多。
接下来是 Bandit ,我们在这里配置的只是目标目录,这是我们的包的名称。我们这样做是为了避免在命令行上指定目标。
之后是 Coverage.py 。首先,我们启用分支覆盖,这意味着在程序中的一行可能会跳转到多个下一行的地方, Coverage.py 会跟踪实际访问了那些目的行中的哪些。接下来,我们省略了一些不应该或者不能包含在覆盖率测量中的文件,比如测试本身或者虚拟环境文件。我们也排除特定的行,例如标有pragma: no cover
注释的行。最后一个 Coverage.py 配置行告诉工具将生成的报告存储在reports
目录中。如果该目录不存在,将自动创建。
我们需要配置的最后一个工具是 Pylint ,不过这个配置非常广泛,*概有 100 多行……所以,我就不写这个了,在这里给你指出源代码以及在 Pylint 仓库这里的注释和解释pylintrc
。
我们检查了setup.cfg
中的所有工具,但是还有一个工具不能添加到setup.cfg
中,那就是Pytest——尽管 Pytest 医生告诉你可以使用setup.cfg
,但这并不完全正确...根据本期,使用setup.cfg
的选项已被否决,并且存在一些错误,如插值错误,这些错误不会被修复,因此我们还需要pytest.ini
文件来配置 Pytest :
我们在这里做的第一件事是设置一组命令行参数——我们在终端输出中启用颜色,然后我们为blueprint
目录启用覆盖报告,之后我们启用 XML 和 stdout ( term
)覆盖报告的生成。最后两个参数(-ra
)告诉 Pytest 输出未通过测试的简短摘要。
在下一行,我们有filterwarnings
选项,它允许我们禁用输出中一些烦人的警告,例如,来自我们无法控制的某个库的不赞成警告。
配置的其余部分设置日志记录。第一个只是打开它,其他 3 个配置级别,格式和日期时间格式。比解释格式配置更容易的是查看输出本身,这将在下一节中显示。
有了pytest.ini
中的所有配置,我们运行测试套件所需要做的就是运行pytest
,甚至不需要包参数!
我们拥有的最后一个实际配置文件是requirement.txt
,它包含了我们的依赖项列表。在这个文件中你能找到的就是一个 Python 包的列表,每行一个可选版本的包。如上所述,包版本是可选的,但是我强烈建议您在requirements.txt
中锁定版本,以避免在构建和部署期间下载更新的、不兼容的包,并最终破坏您的应用程序。
还有两个文件实际上不是配置文件——我们的docker 文件,即dev.Dockerfile
和prod.Dockerfile
,分别用于开发和生产映像。我将暂时不讨论这些内容,因为我们将在另一篇文章中探讨这些内容,在那篇文章中,我们将讨论 CI/CD 和部署。但是,您可以在这里的 GitHub 存储库中查看这些文件-https://GitHub . com/Martin Heinz/python-project-blue print/blob/master/dev。文档文件。
实际源代码
我们已经做了很多,甚至没有提到我们的应用程序的源代码,但是我认为是时候看看项目框架中的几行代码了:
这个蓝图中唯一实际的源代码就是这个带有静态方法的类。这真的是不需要的,这样我们就可以运行一些东西,得到一些输出并测试它。这也是整个应用程序的入口点。在实际项目中,您可以使用run()
方法来初始化您的应用程序或 web 服务器。
那么,我们实际上如何运行这段代码呢?
在一个特别命名的文件__main__.py
中的这一小段是我们在项目中需要的,这样我们就可以使用python -m blueprint
运行整个包。这个文件和它的内容的好处是,它将只用那个命令运行,因此,如果我们想从这个包的源代码导入一些东西而不运行整个东西,那么我们可以这样做而不触发Blueprint.run()
。
我们的包中还有一个特殊的文件,那就是__init__.py
文件。通常,你会让它为空,只用来告诉 Python 这个目录是一个包。然而,在这里,我们将使用它从我们的包中导出类、变量和函数。
如果没有上面的这一行,你将无法从这个包的外部调用Blueprint.run()
。这样我们可以避免人们使用我们代码中不应该公开的内部部分。
这就是我们软件包的全部代码,但是测试呢?首先,我们来看看context.py
通常当你使用某人的包时,你像import blueprint
或from blueprint import Blueprint
一样导入它,为了在我们的测试中模仿它,因此使它尽可能接近真实使用,我们使用context.py
文件将包导入到我们的测试环境中。我们还将项目根目录插入到系统路径中。当用pytest
运行测试时,这实际上是不必要的,但是如果你用python ./tests/context.py
直接运行context.py
,或者可能用unittest
而不包括sys.path.insert...
,那么你将得到ModuleNotFoundError: No module named 'blueprint'
,所以这一行有点像保险单。
现在,让我们来看看测试示例:
我们这里有一个简单的测试,它使用内置的 Pytest 夹具capsys
(捕获系统输出)来检查Blueprint.run()
的标准输出。那么,当我们运行测试套件时会发生什么呢?
我从输出中删除了几行,这样您可以更好地看到相关的部分。这里有什么要注意的?嗯,我们的测试通过了!除此之外,我们还可以看到覆盖率报告,我们还可以看到该报告按照pytest.ini
中的配置写入coverage.xml
。输出中还有一件事是来自conftest.py
的 2 条日志消息。这是怎么回事?
您可能已经注意到,除了capsys
夹具,我们还在小测试的参数中使用了example_fixture
。该夹具驻留在conftest.py
中,我们制作的所有定制夹具也应如此:
顾名思义,这真的只是一个示例设备。它所做的只是记录一条消息,然后让测试运行,最后再记录一条消息。关于conftest.py
文件的好处是它被 Pytest 自动发现,所以你甚至不需要将它导入到你的测试文件中。如果你想了解更多,那么你可以看看我以前关于Pytest这里或者 docs 这里的帖子。
一个命令搞定一切
如果我们分别运行我们的每个工具,并且必须记住它们的参数,即使它们总是相同的,这将是非常费力的。同样,如果后来我们决定将所有这些工具放入 CI/CD(下一篇文章!),对吧?所以,让我们用Makefile
来简化事情:
在这个Makefile
中,我们有 4 个目标。首先,- run
使用我们在源文件夹的根目录下创建的__main__.py
运行我们的应用程序。接下来,test
只是运行pytest
。感谢pytest.ini
中的所有配置,就是这么简单。这里最长的目标- lint
-运行我们所有的林挺工具。首先,它对项目中的所有.py
文件运行pylint
,包括测试文件。之后,它运行flake8
并最终运行bandit
。对于这两个,它只针对blueprint
目录中的源运行。如果这些工具中的任何一个发现我们的代码有问题,它会以非零代码退出,这意味着目标会失败,这在 CI/CD 中很有用。该文件中的最后一个目标是clean
,哪口井...清理我们的项目——它删除所有由前面提到的工具生成的文件。
结论
在本文中,我们已经构建了 project skeleton,它可以用于您可能正在从事或正在考虑的任何类型的 Python 项目,因此,如果您想尝试或更深入地挖掘,请查看我的资源库中的源代码:【https://github.com/MartinHeinz/python-project-blueprint】。Repo 还包括关于如何使用方便的脚本设置您的项目的信息,以及一些更多的文档。如果你喜欢这种内容,请随时以问题的形式留下反馈/建议,或者直接开始。🙂
在未来,我们会考虑将 CI/CD 加入到 GitHub 动作和 GitHub 包注册表的组合中。我们还将对我们的项目进行 Docker 化,创建可调试和优化的生产就绪 Docker 映像,并使用 CodeClimate 和 SonarCloud 添加更多代码质量工具。
资源
本文最初发布于martinheinz . dev
机器学*算法的最终对决
有线电视新闻网、移动网络、KNN、兰登森林和 MLP。哪种算法最好?
Jaime Spaniol 在 Unsplash 上拍摄的照片
故事
gif 由 Giphy
这一切都是从我年轻的表弟开始的,他迷失在自己的世界里,在他的画本上涂鸦。我问他在做什么。他回答说他正在做一只猫;它看起来一点也不像猫。他让我和他一起玩一个游戏,在这个游戏中,我可以认出他在画什么。有一段时间很有趣,但很快,我就厌倦了。我不想因为不和他玩而伤害他的感情,所以我用我的计算机视觉和 python 技巧做了一个涂鸦分类器。现在的问题是我将如何实现它;对涂鸦进行分类的方法有数百种,我必须选择最准确的一种,它需要最少的训练时间,占用更少的内存,需要更少的处理能力,并且不需要 TB 的数据来给出有意义的结果。
在网上冲浪后,我找到了能以最佳方式完成这项任务的前 5 种算法,但我访问的每个网站都讲述了不同的故事。有人说 CNN 是最好的,有人说移动网络是最好的。我想——好吧,让我们全部测试一下。我发现了一个很棒的数据集,其中包含了很多涂鸦,它们的标签都在一个 Kaggle 竞赛中,可以免费下载。
图像分类是一个庞*的主题,因为有*量的算法可用于各种应用。图像分类是如此庞*和不断变化,以至于每天都有新的算法被创造出来,新的应用程序不断涌现。因此,对我来说,挑选几个算法是很困难的,因为它们有数百种变化。因此,这篇文章将专门研究哪种算法对涂鸦分类效果最好。
我还将测试这些算法在其他情况下的可靠性,如手写字符分类、车牌识别等。
涵盖哪些内容
- 研究中使用的 ML 技术简介
- 评估指标
- 为研究选择的参数详情
- 结果
- 局限性和结论
让我们先简单介绍一下所使用的机器学*算法
gif by Giphy
涂鸦分类的算法有成千上万种,这里我列出了几个著名的算法,我将探索它们
1)随机森林
我们可以使用随机森林算法进行分类和回归。它就像决策树,只不过它使用数百棵决策树来得出一个结论。决策树根据相似的特征将数据分成不同的类别。对于每个数据点,它检查它是否具有某个特征,最常见的数据属于同一类。在随机森林算法中,我们采用许多决策树,并随机给它们较少的特征来检查,例如,如果我们有 100 个特征,我们可能给每棵树 10 个随机特征。一些树会分配不正确的类,但许多将是正确的!我们取*多数并创建我们的分类模型。
随机森林算法研究论文;
利奥·布雷曼
饶彤彤关于随机森林算法的一篇很棒的文章:
该算法如何工作以及为什么如此有效
towardsdatascience.com](/understanding-random-forest-58381e0602d2)
2) KNN
k-最近邻(KNN)既可用作分类算法,也可用作回归算法。在 KNN 中,数据点被分成几类以预测新样本点的分类。为了实现这一任务,它使用距离公式来计算各种数据点之间的距离,基于该距离,它然后为每个类定义区域边界。任何新的数据点都将落入这些区域中的一个,并将被分配到该类别。
关于 KNN 的研究论文:
雷努·汉德尔瓦尔的一篇关于 KNN 的精彩文章:
在这篇文章中,我们将了解什么是 K-最近邻,这个算法是如何工作的,有什么好处和…
medium.com](https://medium.com/datadriveninvestor/k-nearest-neighbors-knn-7b4bd0128da7)
3) MLP
多层感知(MLP)是一种前馈人工神经网络。MLP 有许多层,但在其隐藏层只有一个逻辑函数,在输出层只有一个 softmax 函数。该算法将单个*向量作为输入,并在输入层和隐藏层上执行矩阵运算,然后结果通过逻辑函数,其输出通过另一个隐藏层。重复此过程,直到网络到达输出层,在输出层使用 softmax 函数产生单个输出。
关于 MLP 的研究论文:
Jorge Leonel 的一篇关于 MLP 的精彩文章:
我们在这里已经看到感知器,这个神经网络的名字唤起了人们对未来的看法…
medium.com](https://medium.com/@jorgesleonel/multilayer-perceptron-6c5db6a8dfa3)
4) CNN
卷积神经网络(CNN)是最容易实现深度学*的计算机视觉算法之一。首先,它采用给定*小的输入图像,并为其创建多个滤波器/特征检测器(最初是给定*小的随机生成的矩阵),滤波器旨在识别图像中的某些模式,滤波器在图像上移动,矩阵和图像之间进行矩阵乘法。该滤波器在整个图像中滑动以收集更多特征,然后我们使用激活函数(通常是校正的线性单位函数)来增加非线性或仅保留重要特征,然后我们使用 max-pooling 函数将给定矩阵*小中的所有值相加(例如,如果我们选择 4 个矩阵,则它将所有 4 个值相加以创建 1 个值),从而减小输出的*小以使其更快。最后一步是展平最终矩阵,该矩阵作为输入传递给基本 ANN(人工神经网络)并获得类别预测。
CNN 的研究论文:
由纳格什·辛格·肖汉在 CNN 上发表的一篇精彩文章:
本文重点介绍与 CNN 相关的所有概念及其使用 Keras python 库的实现。
levelup.gitconnected.com](https://levelup.gitconnected.com/introduction-to-convolutional-neural-networks-cnn-1ee504bc20c3)
5)移动网络
移动网络架构使用深度方向可分离卷积,其包括深度方向卷积和点方向卷积。深度方向卷积是通道方向 Dk * Dk 空间卷积,假设我们在图像中有 3 个通道(R,G,B ),那么我们将有 3DkDk 空间卷积。在逐点卷积中,我们的内核*小为 11M,其中 M 是深度卷积中的通道数,在本例中为 3。因此,我们有一个*小为 113 的内核;我们通过我们的 3DkDk 输出迭代这个内核,得到 DkDk1 输出。我们可以创建 N 个 113 内核,每个内核输出一个 DkDk1 图像,以获得形状为 DkDkN 的最终图像。最后一步是将深度方向卷积添加到点方向卷积。这种类型的架构减少了训练时间,因为我们需要调整的参数较少,同时对准确性的影响较小。
关于移动网络的研究论文:
安德鲁·霍华德
Sik-Ho Tsang 的一篇关于移动网络的文章:
[## 复*:MobileNetV1 —深度方向可分离卷积(轻型模型)
在这个故事中,来自 Google 的 MobileNetV1 被回顾。深度方向可分离卷积用于减少模型尺寸
towardsdatascience.com](/review-mobilenetv1-depthwise-separable-convolution-light-weight-model-a382df364b69)
评估指标
用于研究的涂鸦样本
以上是用于这项研究的涂鸦样本。
我在ka ggle quick draw 数据集上训练我的机器学*模型,该数据集包含 5000 万张不同类型涂鸦的图像。我把这个庞*的数据集分成两部分:35000 张图片用于训练,15000 张图片用于测试。然后,我在随机选择的 5 种不同类型的涂鸦上计算了每种算法的训练时间。在测试集上,我计算了每个算法的平均精度、准确度和召回率。
评估指标-
训练时间
平均精度
准确(性)
回忆
由 Shashwat Tiwari 16MCA0068 提供更多评估指标
投入探索吧!
medium.com](https://medium.com/analytics-vidhya/complete-guide-to-machine-learning-evaluation-metrics-615c2864d916)
此外,Shervin Minaee 的一篇好文章
[## 20 个流行的机器学*指标。第 1 部分:分类和回归评估指标
介绍评估分类,回归,排名,视觉,自然语言处理和深度…
towardsdatascience.com](/20-popular-machine-learning-metrics-part-1-classification-regression-evaluation-metrics-1ca3e282a2ce)
所选参数的详细信息
gif by Giphy
1)随机森林
n_estimators —一个森林中决策树的数量。[10,50,100]
max_features —分割时要考虑的特性['auto ',' sqrt']
max_depth —树中的最*层数[2,4,6,8,10]
n_jobs —并行运行的进程数,通常设置为-1,一次执行最多的进程。
标准 —这是一种计算损失并因此更新模型以使损失越来越小的方法。['熵','交叉验证']
我用‘auto’作为max _ feature; 8 为max _ depth; -1 作为 n_jobs 和 【熵】 作为我的准则因为它们通常给出最好的结果。
寻找最佳树数的图表
然而,为了找出最佳的树数,我使用了 GridSearchCV。它尝试所有给定的参数组合,并创建一个表来显示结果。从图中可以看出,80 棵树后的测试分数并没有明显的提高。因此,我决定在 80 棵树上训练我的分类器。
2)K-最近邻(KNN)
n_neighbors —要比较的最近数据点的数量[2,5,8]
n_jobs —并行运行的进程数,通常设置为-1,一次执行最多的进程
我没有改变这个模型的任何默认参数,因为它们会给出最好的结果。
然而,为了找到最佳数量的 n_neighbors ,我使用了 GridSearchCV,这是我得到的图表:
寻找最佳 N-邻居数量的图
根据图表,测试分数在528】n _ neighbors 之后下降,这意味着 5 是最佳邻居数。
3)多层感知器(MLP)
alpha——俗称学*率,它告诉网络调整梯度的速度。[0.01, 0.0001, 0.00001]
hidden_layer_sizes — 它是一个值元组,由每层的隐藏节点数组成。[(50,50), (100,100,100), (750,750)]
激活 —为图像中的重要特征赋予价值,删除无关信息的功能。['relu ',' tanh ',' logistic']。
解算器— 也称为优化器,该参数告诉网络使用哪种技术来训练网络中的权重。['sgd ',' adam']。
batch_size — 一次要处理的图像数量。[200,100,200].
我选择了激活为‘relu ’,选择解算器为‘Adam ’,因为这些参数给出了最好的结果。
然而,为了选择隐藏层和 alpha 的数量,我使用了 GridSearchCV。
查找最佳 N 邻居数量的表
从表中可以看出,当 alpha 为 0.001、和 hidden_layer_size 为 (784,784) 时,得到的结果最好。因此,我决定使用这些参数。
4)卷积神经网络(CNN)
l earning_rate -它告诉网络调整梯度的速度。[0.01, 0.0001, 0.00001]
hidden_layer_sizes — 它是一个值元组,由每层的隐藏节点数组成。[(50,50),(100,100,100),(750,750)]
激活 —为图像中的重要特征赋予价值,删除无关信息的功能。['relu ',' tanh ',' logistic']。
解算器— 也称为优化器,该参数告诉网络使用哪种技术来训练网络中的权重。['sgd ',' adam']。
batch_size — 一次要处理的图像数量。[200,100,200]
时期 —程序应该运行的次数或模型应该训练的次数。[10,20,200]
我选择了激活函数作为“relu ”,选择解算器作为“adam ”,因为这些参数通常会给出最佳结果。在网络中,我添加了 3 个卷积层,2 个 maxpool 层,3 个 dropout 层,最后还有一个 softmax 激活函数。我在这里没有使用 GridSearchCV,因为可以尝试很多可能的组合,但是结果不会有太*的不同。
5)移动网络
Input_shape- 它是一个由图像的维度组成的元组。[(32,32,1),(128,128,3)].
Alpha- 是网络的宽度。[ < 1,> 1,1]
激活 —为图像中的重要特征赋予价值,删除无关信息的功能。['relu ',' tanh ',' logistic']。
优化器— 也称为解算器,该参数告诉网络使用哪种技术来训练网络中的权重。['sgd ',' adam']。
batch_size — 一次要处理的图像数量。[200,100,200].时期 —程序应该运行的次数或模型应该训练的次数。[10,20,200]
类别- 要分类的类别数量。[2,4,10]
损耗- 它告诉网络使用哪种方法计算损耗,即预测值和实际值之间的差异。['分类交叉熵',' RMSE']
首先,我将 2828 的图像调整为 140140 的图像,因为移动网络要求最少 3232 的图像,所以我使用的最终 input_shape 值是(140,140,1),其中 1 是图像通道(在本例中是黑白的)。我将 alpha 设置为 1 ,因为它通常会给出最好的结果。激活功能被设置为默认,即' relu' 。我使用了'Adadelta'optimizer,因为它给出了最好的结果。 batch_size 被设置为 128 以更快地训练模型。为了更准确,我使用了20*历元。类别被设置为 5 ,因为我们有 5 个类别要分类。
结果
gif by Giphy
最终结果(祈祷)
以上是使用的所有机器学*技术的性能。衡量标准包括准确度、召回率、精确度和训练时间。看到移动网络的训练时间是 46 分钟,这让我感到震惊,因为它被认为是一个轻型模型。我不确定为什么会这样,如果你知道为什么,请告诉我。
局限性和结论
- 在这项研究中,只使用了 28*28 *小的黑白涂鸦,而在现实世界中,不同的颜色可以描绘或表示不同的事物,图像*小可能会有所不同。因此,在这些情况下,算法的行为可能会有所不同。
- 在所有讨论的算法中,有许多可以改变和使用的超参数,它们可能给出不同的结果。
- 训练这些算法的训练集仅限于 35000 幅图像,添加更多图像可以提高这些算法的性能。
结果表明,移动网络实现了最高的准确度、精确度和召回率,因此就这三个参数而言,它是最好的算法。然而,移动网络的培训时间也是最高的。如果我们将其与 CNN 进行比较,我们可以看到,CNN 花了更少的时间进行训练,给出了类似的准确性、精确度和召回率。因此,根据这项研究,我会得出结论,CNN 是最好的算法。
在做了这项研究后,我得出结论,像 mobile-net 和 CNN 这样的算法可以用于手写字符识别、车牌检测和世界各地的银行。像 mobile-net 和 CNN 这样的算法达到了超过 97%的准确率,这比人类 95%的平均表现要好。因此,这些算法可以在现实生活中使用,使困难或耗时的过程自动化。
您可以在此处找到代码:
编辑描述
colab.research.google.com](https://colab.research.google.com/drive/1aefccgDjDIPW6RVImtFG5fAlaI91ysbg?usp=sharing)
参考
深度方向可分离卷积 (YouTube)
[## 20 个流行的机器学*指标。第 1 部分:分类和回归评估指标
介绍评估分类,回归,排名,视觉,自然语言处理和深度…
towardsdatascience.com](/20-popular-machine-learning-metrics-part-1-classification-regression-evaluation-metrics-1ca3e282a2ce) [## 卷积神经网络(CNN)简介
本文重点介绍与 CNN 相关的所有概念及其使用 Keras python 库的实现。
levelup.gitconnected.com](https://levelup.gitconnected.com/introduction-to-convolutional-neural-networks-cnn-1ee504bc20c3) [## 用深度学*涂鸦!
草图识别之旅
towardsdatascience.com](/doodling-with-deep-learning-1b0e11b858aa) [## k-最近邻(KNN)
在这篇文章中,我们将了解什么是 K-最近邻,这个算法是如何工作的,有什么好处和…
medium.com](https://medium.com/datadriveninvestor/k-nearest-neighbors-knn-7b4bd0128da7)
Unity3D 中 ML-agent 的终极演练
从头到尾通过机器学*训练人工智能
嘿!这将是一个快节奏的,完整的用 Unity 的 ML-agent 构建人工智能的演练。像一个优秀的电视厨师一样,我已经准备了一个简单的游戏,你可以从 GitHub 中克隆。如果你想继续下去,一定要做到!🙂
储存库 : 人工智能跳车
目前,它只是一个人类控制的游戏,没有涉及机器学*,还没有!按下空格键,你可以让汽车跳跃,以躲避驶来的车辆。
我们将通过机器学*来训练人工智能做同样的事情,希望比我们— 或至少是我能做得更好。如果你更喜欢看视频,这是给你的:
步骤 1:将我们的玩家转化为代理
这是一个简单的游戏,我们可以在以后增加复杂性。但是从简单开始会减少潜在的错误数量。我总是试图简单地开始,这几乎是无聊的。
如果你打开主场景,你可以看到位于环境中的 Spawner 对象。在这里你可以决定被繁殖的敌人汽车以及它们被繁殖的间隔。但是我们可以让它保持原样。接下来,在我们的产卵器中,我们的玩家被一个叫做 Jumper 的简单脚本定位,它负责跳井。
Jumper.cs 剧本是我们真正关心的唯一剧本,因为这是我们的人工智能将控制的。顾名思义,我们的 ML-Agents 世界中的主角是特工——所以让我们把我们的 jumper 改造成一个。它应该从代理继承,而不是从monobehavior继承。
public class **Jumper** : Agent
哒哒🧙♂️,它现在是一个代理。接下来,我们向我们的玩家添加一个行为参数sscript,因为没有它,我们的代理是无脑的。姑且称之为“Jumper”之类的有用的东西吧。
现在,对于困难的部分,我们必须考虑我们的代理需要收集的观察结果——意味着它需要什么信息以及它可以采取什么行动。先从简单的开始,动作。
我们的代理可以跳转并且它可以不做任何事情。就是这样。所以我们有两个选项,这意味着我们有一个*小为 2 的离散动作空间,并且只有一个分支。
如果我们想把运动和跳跃结合起来,多分支是很有用的,例如:我们可以用一个分支运动,一个分支跳跃。一个单独的分支只能允许移动或跳跃,但不能同时允许两者。如果你想了解更多,可以看看 跳墙 的例子。在我们的情况下,一个单独的分支就可以了。
接下来,观察。在这里,没有正确或错误的答案。试着想一想一个代理可以观察环境的几种可能的方法,每种方法需要多少数据,一个代理要做多少过滤,才能推断出有用的信息。真的,现在暂停阅读,好好想想…
…我的想法是非常简单地开始,我认为我们的代理人只需要到下一辆车的距离,就可以正确地选择跳跃时间。
所以我会用一个射线投射,你可以想象它就像从代理射出的激光束,直到它击中某个东西。它可以记录是否有东西被击中,什么被击中,以及被击中的东西离它有多远。由于这是一种非常常见的观察方式,Unity 已经方便地准备了一个简单的脚本,我们可以使用它,很好👌。
让我们将 RayPerceptionSensor3D 组件添加到我们的播放器中。我们已经可以在场景视图中看到光线的预览。首先,我们必须添加要检测的标签。在我们的例子中,产生的敌人有移动者标签,所以让我们添加它。接下来,每个方向的光线描述了中心左右的光线数量。因为我们只需要一条中心光线,所以可以将该值设置为 0。下一个相关值是光线长度。在我们的情况下,它至少应该走到路的尽头,为了安全起见,让我们在这里使用 50。为了确保光线不会碰到地面,让我们在起点和终点给它们一个 0.5 的垂直偏移量。
就是这样。我的建议是,只是玩玩滑块,看看会发生什么,以了解更多。光线投射真的很强*,是除了视觉信息之外观察环境最常见的方式。
如果我们现在开始游戏,如果前面有车,我们可以看到光线变成红色,太好了。起作用了。这个传感器的优点是它会自动将自己添加到观察中,因此不需要额外的代码,我们可以将观察*小设置为 0,因为它是为我们处理的。这个框架是不是很奇妙?
步骤 2:向我们的代理添加逻辑
好了,现在让我们进入 Jumper.cs 脚本。代理类中有一些基本的方法。让我们将它们添加到我们的代理中。初始化(),on action received(),以及 OnEpisodeBegin ()。这一次,我们实际上不需要 CollectObservations() ,因为我们只使用了传感器。但是我们应该添加启发式()方法,因为这是可以处理人类输入的地方。
总是先手动测试你的代理,这样你可以更快更容易地找到 bug。
public override void Initialize(){...}
public override void OnActionReceived(float[] vectorAction){...}
public override void OnEpisodeBegin(){...}
public override void Heuristic(float[] actionsOut){...}
首先,让我们把所有东西从唤醒()移到初始化()。简单。把 Awake ()方法删掉就行了,我们已经不需要了。
public override void Initialize()
{
rBody = GetComponent<Rigidbody>();
startingPosition = transform.position;
}
现在我们必须处理行动。 OnActionReceived ()方法有一个浮点数组参数。因为我们只有一个分支,所以我们只对第一个元素感兴趣。这个值要么是 0,要么是 1,所以也很简单。我们来确定 0 是什么都不做,1 是跳跃。不编码的好处是我们不需要编码任何东西,这意味着我们只需要检查 1 的情况。为了安全起见,在等式检查之前,我将把 float 底数设为 int。嘭,这是我们的动作功能:
public override void OnActionReceived(float[] vectorAction)
{
if (Mathf.FloorToInt(vectorAction[0]) == 1)
Jump();
}
现在,我们将在启发式()函数中检查输入,而不是在更新()中检查输入。现在只需将更新()中的所有内容移动到启发式()中,并删除更新()。由于调用这个函数的学院运行在 Unity 的固定更新循环中,如果我们试图使用 KeyDown 或 MouseDown ,输入检查可能会有点问题,所以使用 GetKey ()或 GetAxis ()来确保没有输入丢失。
现在,我们不调用跳转()函数,而是必须修改 aactions out[],因为动作处理是由on actions received()函数管理的。无论我们放入actions out[]中的是什么,都会被on actions received()方法接收到。首先,我们将 aactions out[0]设置为零,如果某个键被按下则为 1。
public override void Heuristic(float[] actionsOut)
{
actionsOut[0] = 0;
if (Input.GetKey(jumpKey))
actionsOut[0] = 1;
}
现在如果我们开始游戏…我们可以看到...什么都没发生😢。
这是因为现在不需要做出任何决定。改变这种情况的一个方法是给我们的播放器添加一个决策请求器组件。它与学院相关联,要求对学院的每一步做出决定。如果该值设置为默认的 5,则每隔 5 步。如果我们添加这个组件并将其设置为 1,它就可以工作了。但是我不会用。
为什么?因为我们可以优化它。当汽车在空中时,没有什么可做的,因为只有在路上才允许跳跃。简单来说,当汽车还在空中时,我们甚至不要请求做出决定。想象一下*脑在这段时间里是关闭的。让我们添加 FixedUpdate ()方法和 Request a Decision 如果jump ready。
private void **FixedUpdate**()
{
if(jumpIsReady)
RequestDecision();
}
同样,这只是做这件事的一种方式。如果你想到别的,试试看。
我们几乎准备好训练了,但是最重要的部分仍然缺失。奖励⭐️ !每个人都喜欢奖励,尤其是 A.Is。这一部分又很简单,但如果你做得不好,你真的会把一切都搞砸。不过不要担心😄。*多数情况下,简单直接的方法就可以了。一个基本的经验法则是输的时候惩罚-1,赢的时候奖励+1。由于这是一个高分游戏,没有输赢,所以我只是决定每辆车加 0.1 奖励。
这绝不是设计得最好的奖励系统,但它确实有效。如果您不确定—通过多次运行和预定义的步骤数进行测试,然后比较结果可能是您的最佳选择。但是稍后会有更多的测试。
在 OnTriggerEnter ()方法内部的脚本中,我们将调用 AddReward ()方法。
private void **OnTriggerEnter**(Collider collidedObj)
{
if (collidedObj.gameObject.CompareTag("score"))
{
AddReward(0.1f); //New
score++;
ScoreCollector.Instance.AddScore(score);
}
}
这个游戏中的计分方式是这样的:每辆车后面都有一个隐藏的立方体,在碰撞时触发 OnTriggerEnter() 方法。
接下来,在 OnCollisionEnter ()方法中,我们发现玩家被传入的流量击中的情况。首先,我们用结束模式()方法替换复位()方法。接下来,我们将在这里添加一个负奖励。
private void **OnCollisionEnter**(Collision collidedObj)
{
if (collidedObj.gameObject.CompareTag("Street"))
jumpIsReady = true;
else if (collidedObj.gameObject.CompareTag("Mover") || collidedObj.gameObject.CompareTag("DoubleMover"))
{
AddReward(-1.0f);
EndEpisode();
}
}
步骤 3:培训代理
我们现在终于可以开始训练了!为了优化训练,我们应该添加多个代理并构建一个独立的应用程序,但是我总是先用一个代理在编辑器中测试,以发现任何错误。现在,这个游戏有希望没有 bug,但是正如你所想象的,情况并不总是这样。
我假设你的系统上已经安装了带 ML-Agents 的 Python,如果没有,请点击查看安装指南。
我们只需打开终端,将 cd 放入存储库,然后放入 TrainerConfig 文件夹。现在我们开始培训,输入:
mlagents-learn trainer_config.yaml --run-id="JumperAI_1"
您可以将 run-id 更改为您最喜欢的。第二个参数引用位于存储库内部的 trainer_config 文件。只有当您在下载的库的 TrainerConfig 文件夹中时,这个命令才有效!
如果你看到团结标志,一切都很好。现在我们切换到编辑器,按下播放,让人工智能训练足足 10 分钟。现在是观察训练过程的时候了,看看你是否能发现任何明显的错误。
通过放入
tensorboard --logdir=summaries
进入另一个终端选项卡,我们可以进一步检查它。左边的图表显示了一段时间内累积的奖励。
*约 10 分钟后。
对我来说,在 8 分钟左右,第一次真正的改善开始出现,然后很快就变好了。这很好,它证实了我们的环境正在起作用。
下一步是用多个代理进行训练,以加快和稳定训练。在我们的例子中,这就像多次复制环境游戏对象一样简单。
同样,我们首先按 play 并测试是否一切正常。如果是的话,那太好了。现在,让我们再次在编辑器中开始第二个培训过程,以确保多个代理按预期工作。我们将使用相同的命令,但改变 ID,以便我们可以比较运行。
mlagents-learn trainer_config.yaml --run-id="JumperAI_2"
10 分钟后。
在我的情况下,它工作得非常好。我们可以看到第二张图要*得多。这是因为由于我们拥有的代理数量,我们在相同的时间内处理了更多的步骤。这也是我们从简单开始的原因—我们能够在几分钟的培训时间内验证我们的环境正在工作,而不是几个小时。
现在,为了正确和完整的培训,我们可以构建应用程序。再次执行 mlagents-learn 命令,这一次引用我们的构建文件,将时间刻度设置为 10,意思是 10 倍的速度,这在我的系统上运行良好,将图形质量级别设置为 0,将窗口宽度和高度设置为 512 或您喜欢的任何值。
mlagents-learn trainer_config.yaml --run-id=JumperAI_3 --env=../Build/build.app --time-scale=10 --quality-level=0 --width=512 --height=512
60 分钟后。
如果我们对结果满意,我们按 control+c,然后一个模型就生成了。我们可以拖动它。 nn 文件到我们的项目中,通过在层级中搜索玩家选择所有代理,并将其放入模型槽中。如果我们现在按 play,我们可以看到它正在执行我们的模型。自豪吧,你培养了一个人工智能,有多少人这么做过?
如果你在途中的某个地方迷路了,你可以随时查看库 中的【tutorial completed】分支,在那里你可以找到带有训练好的模型和完成的脚本的项目。
我鼓励你尝试这个项目,创造你自己的风格。
和平!查看我的 Youtube 频道的人工智能相关内容。✌️
新冠肺炎的超声波——深度学*方法
关于一个利用超声波自动检测新冠肺炎的开放倡议
您将从本文中学到什么:
- CT 和 X 射线是新冠肺炎诊断过程中常用的评估方法,但医学界也提倡超声成像
- 超声波是优选的,因为它便宜、便携、易于消毒、无辐射且随处可得
- 我们的深度学*方法首次证明了在超声上自动检测新冠肺炎可能是可行的
- 有了可解释的机器学*方法,我们向医生的决策支持工具迈进了一步
- 在 GitHub 上开始了一项开放存取数据的倡议——贡献吧!
https://github.com/jannisborn/covid19_ultrasound
迄今为止,已有超过 1 . 4 亿人感染了新冠肺炎病毒,300 多万人已经死亡。这种疾病仍在全球蔓延,几乎所有国家都受到影响。努力控制病毒的一个关键因素是快速可靠的检测。检测病毒 RNA 的新冠肺炎 RT-聚合酶链式反应测试(逆转录聚合酶链反应)被认为是最可靠的,但它的灵敏度在各国之间差异很*,有时假阴性高达 30%[1],在发展中国家几乎没有。此外,PCR 测试需要几个小时,这是有问题的,例如在分诊的情况下,医生必须立即决定是否隔离病人。
这就是医学成像的用武之地。迄今为止最突出的是 CT,它已经在疫情早期用于新冠肺炎的快速诊断。研究表明,在 CT 上可以看到明显的不同模式,例如,空气间隙实变和所谓的(多灶性)毛玻璃阴影[2]。从报告来看,CT 似乎是一个非常有前途的工具,有时甚至在聚合酶链式反应测试失败时也很敏感[3]。但是它有很*的缺陷:CT 辐射*,价格昂贵,难以消毒。显然,这些缺点阻碍了 CT 在诊断中的广泛应用。作为一种替代方法,研究人员考虑了 X 射线,但预测能力较差。但是还有另一种医学成像工具,尽管它很受欢迎,但在新冠肺炎的环境中却没有得到太多的关注。我们在谈论超声波。
护理点超声波(POCUS)是
- 便宜:虽然一次 x 光检查估计要花费 370 美元左右,CT 从 500 美元到 3000 美元不等,但超声波(US)很便宜,只需*约 140 美元。此外,该设备本身很便宜,因此易于销售,便携式设备的起价为 2000 美元。
- 易于使用:几乎所有的医生都知道如何进行超声波检查。没有辐射那样的安全措施,而且设备很方便。
- 快速:使用一台设备,每小时可以进行 4 到 5 次肺部筛查
- 便携式:“护理点”说明了一切。病人不必移动,这节省了*量的时间和精力。
- 安全:我们不使用任何辐照元素。句号。任何 X 射线或 CT 检查都会略微增加患癌症的终生风险,尤其是对年轻患者而言。
尽管有这些优点,但超声仅在过去几年中被整合到肺部疾病的诊断过程中。可以分析某些特征病理模式,例如所谓的 B 线、A 线和条形码标记,以诊断所谓的胸腔积液、肺泡实变、间质综合征和气胸。由于新冠肺炎以类似的方式改变肺部超声模式,超声对新冠肺炎的适用性现在在几个出版物中进行了研究,其敏感性与 CT 进行了比较[4]。结果很清楚:根据[5,6,7],[8](发表在权威杂志《柳叶刀呼吸医学】 上)的作者主张 us 在新冠肺炎诊断中发挥更突出的作用,并提供证据表明 US 检测新冠肺炎的灵敏度与 ct 非常相似。
那么这是否意味着超声将很快取代 CT 作为诊断工具呢?不幸的是,事情没那么简单。一个值得注意的缺点是,在美国,医生必须经过培训才能识别 COVID,并且观察 COVID 特定的模式不是一项容易的任务,而是需要一些经验。在目前的情况下,进行广泛培训的时间有限。
我们相信,一个用于医生的辅助系统给出了一个解决方案,该系统利用计算机视觉技术对超声记录进行自动分类。这种系统可以支持医生的决策,并对病人被感染的可能性进行初步评估。在我们的研究中,我们朝着这个系统迈出了第一步,从各种在线来源收集了一组美国记录,对它们进行预处理,并训练一个神经网络对图像进行分类。我们在 arXiv“poco vid-Net:从新的肺部超声成像数据集(POCUS)中自动检测新冠肺炎”的预印本实际上是 COVID 在超声方面的第一项工作。在我们发表在应用科学杂志“使用可解释的超声图像分析加速肺部病理检测”的文章中,我们随后提供了一项对用于对肺部 US 数据进行分类的(可解释的)深度学*方法的综合研究。
让我们开始更详细地讨论我们的工作:
一个新的 POCUS 数据集
一个主要的贡献是收集了目前(92 个新冠肺炎,73 个细菌性肺炎和 90 个健康对照)的 250 多个数据集。该数据集包含来自各种来源的数据,包括我们的合作者在诺森比亚(英国)的一家医院收集的未发表的临床数据,以及在 Neuruppin(德国)扫描的健康志愿者的记录,以及来自其他出版物和教育网站的数据。请注意,预处理数据需要做更多的工作——简而言之,在从视频中选择帧来创建图像数据集之前,我们手动裁剪了视频并移除了伪影。我们甚至在团队中一名医生的帮助下检查了数据的质量,他可以对每个视频中的可见模式提供宝贵的意见。最后,这产生了一个超过 3000 张图像的干净数据集。当然,这绝不是详尽无遗的,还有许多数据需要发现和处理。然而,我们认为在我们的 GitHub 页面上提供数据和预处理管道是我们工作的主要部分,在这里您可以为我们的开放访问计划贡献数据。****
一名新冠肺炎患者的肺部超声波检查(取自 http://www.thepocusatlas.com/covid19(第 4 天),根据许可归属-非商业性 4.0 国际版提供)
深度学*方法
但是这些数据有用吗,神经网络可以学*从 US 图像中检测新冠肺炎吗?为了回答这个问题,我们训练了各种神经网络来区分新冠肺炎、肺炎和健康患者。我们的最佳性能模型利用预训练的 VGG-16 模型作为主干,后面是一个完全连接的层。诸如旋转、移动和翻转之类的增强有助于防止数据集上的过度拟合,对于具有两百万可训练参数的模型来说,数据集仍然相当小。
结果非常有希望——我们的模型实现了 88%的准确率来分类为新冠肺炎、肺炎和健康患者。下面你可以看到混淆矩阵,分别沿每个轴标准化。这些结果是在分层的 5 折叠交叉验证中获得的,确保来自同一视频的帧不在不同的折叠中。如下图所示,不同类别之间的性能是平衡的,这意味着对新冠肺炎和细菌性肺炎的敏感性和特异性都很高。这也表明,深度学*方法不仅对新冠肺炎来说,而且对一般的肺部病理学来说,都可能是一项值得努力的工作。
结果:新冠肺炎的特异性为 0.9,敏感性为 0.88
可解释人工智能——一个决策支持系统
当我们提出这些结果时,我们经常被问及我们是否试图用这些方法取代医生。明确的答案是否定的!相反,我们希望提供第二种意见,一种可以在诊断过程中帮助医生的系统。例如,如果一个软件可以在你可以在超声波上看到有趣的东西的特定时间点停止视频,然后在图像上突出显示异常,这不是很好吗?
********
这样做的一种方法称为类激活映射(CAM)[9],它突出显示图像中对神经网络预测最有决定性的部分。我们为我们的分类网络实现了 CAM,并观察到有趣的结果:通常,热图集中在超声上可见的所谓实变,这表明肺部有液体。此外,如示例所示,通常健康肺部的超声水平“A 线”也经常被摄像头拾取。
现在,为了找出这些激活图是否真的对临床实践有用,我们请两位医学博士对覆盖在一段视频上的 50 个激活图例子进行评价。这些凸轮总的来说是有用的。因此,我们认为这可能是一个非常有趣的研究方向,以改善特定病理模式的检测,并利用医生可以使用的实际软件的实施。
下一步是什么?
那么如何将这项初步但有前景的工作转化为实际应用呢?还有很长的路要走,但是为此,我们需要你的帮助。作为第一步,我们需要更多的数据来验证我们的模型——如果你有任何对我们有用的数据:通过我们的 GitHub 页面贡献给我们的数据集。我们也渴望支持任何旨在用我们的数据集改进机器学*模型的工作。
有了这些初步结果,重要的一步是在真实的临床数据上评估该方法。为此,我们目前正与一家医院合作进行一项临床研究,以验证我们的方法。进一步的研究可以在临床研究中系统地比较 CT、X 射线和超声波以及它们各自的自动检测方法。这将是一个巨*的飞跃。
但是,如果你不知道任何数据来源,也不是机器学*专家,仍然有许多可能性可以做出贡献。一件非常简单的事情就是提高对使用 POCUS 作为诊断工具的可能性的认识。分享我们的出版物,观看我们的宣传视频或查看我们的开发帖子,并访问我们的 GitHub 登录页面,其中提供了所有代码。如果您想投稿或有问题,请联系我们,欢迎任何反馈。感谢您阅读我们的工作!
参考
[1]杨,杨,等,“2019-nCoV 感染的病毒脱落的实验室诊断和监测。”(2020) MedRxiv 。
[2]杰弗里·P·凯恩。中国武汉 2019 年新型冠状病毒(2019-ncov)感染的胸部 ct 发现(2020 年)放射科医师的要点。
[3]艾,陶,等.“冠状病毒病 2019(新冠肺炎)胸部 CT 与 RT-PCR 检测相关性:1014 例报告”(2020) 放射学 : 200642。
[4]菲亚拉,M. J .“超声在新冠肺炎:与 CT 相关的超声发现时间表。”(2020) 临床放射学。
[5] Smith,M. J .等人,“COVID‐19 患者的床旁肺部超声检查-叙述性综述。”(2020) 麻醉。
[6]Sofia,Soccorsa 等,“胸部超声和 SARS-新冠肺炎:一篇图片论文。”(2020) 超声杂志:1–5。
[7] Soldati,Gino 等人,“肺部超声在 COVID‐19 疫情期间有作用吗?."(2020) 医学超声杂志。
[8]布昂森索、达尼洛、达维德·帕塔和安东尼奥·基亚雷蒂。"新冠肺炎爆发:少用听诊器,多用超声波."(2020) 柳叶刀呼吸内科。
[9]周,,等.“学*深层特征用于鉴别性定位”IEEE 计算机视觉和模式识别会议论文集。2016.**
UMAP 和 K-意味着对角色进行分类——以及它为什么有用(英雄联盟)
利用降维和聚类算法将英雄联盟“冠军”进行分类。
截至今天(2020 年 10 月)的,在线游戏《英雄联盟》中有 151 个“冠军”(可玩角色),每个人都提供独特的个人游戏风格,这使其成为世界上最受欢迎的电子竞技。尽管这种多样性提供了一个引人入胜的竞争环境,但同时也给新玩家和分析师带来了复杂性。如果每个冠军都是独一无二的,一个新手怎么可能理解每场比赛的复杂性,或者一个分析师怎么可能总结一个球员的表现?
2016 年 4 月,Riot(游戏背后的公司)试图通过引入“冠军课程”来帮助解决这个问题。这由 12 个子类组成,分属 6 个类。这些是 Riot 的开发团队手工制作的,为新玩家*惯游戏提供了一个很好的起点。你开始玩几个游戏,看到一个叫“Thresh”的角色,快速检查会发现他的职业是“Catcher”,另一个角色也是:“Blitzcrank”,你以前和他交过手。虽然你错过了一些细节,但你明白总体思路是避免被它们抓住。
然而,这些课程很快就被搁置了,现在很少在任何内容中提到它们。这促使我回答了一个问题:AI 会如何班每个冠军?这与暴乱的解释一致吗?如果是这样,我们还能了解到什么?为此,我们将该方法分为四个阶段。
- 数据收集、清理和特征创建。
- 通过提取关键信号来降低数据的维数。
- 使用聚类算法将冠军分成几类。
- 分析课程以确定趋势和主题。
关于游戏背景的预先说明(如果您熟悉游戏,请忽略):
如果你不熟悉《英雄联盟》,你会漏掉一些内容。如果你想变得更熟悉,那么你可以阅读这个游戏的 介绍 。否则,最起码要知道这是一场 5v5 游戏,每个队都有一名球员扮演以下角色之一:顶级球员、中层球员、Jungler、ADC、支援。
如果你希望在文章中加入商业元素,那么将“冠军”转化为“顾客”,将“死亡”等数据转化为“购买”和“网站反弹”可能会有所帮助。你可以跳到文章的底部,获得一些在“真实世界”中使用的例子。
数据收集、清理和特征创建
通常,第一种方法是启动一个 Riot API 会话,并开始查询他们庞*的数据湖。 注意: 我打算创建一个完整的指南来说明如何做到这一点,但同时请查看 GitHub 以了解详细信息。我们首先决定将结果限制在钻石(接近顶级)玩家,因为它提供了*量被认为质量相对较高的游戏。总的来说,我们拉了 68,000 多场比赛,其中每一行都包含关于冠军的信息和一系列关键统计数据,从杀戮,死亡和助攻到最*的致命一击,抑制剂和龙的安全。
除了“标准”数据点,我们还可以考虑从可用数据中提取的替代特征。例如,可以查看一个名为“时间线事件”的数据集,并查看每次杀戮的详细信息。这包括地点、时间、凶手和被杀者。从这里,我们可以创建两个新功能:单杀:在一场游戏中有多少次玩家在没有帮助的情况下获得了一次杀戮& 早期漫游:在一场游戏的前 10 分钟里有多少次玩家在不是自己的车道上获得了一次杀戮或帮助。
在没有过多阐述单调的情况下,数据被清理、转换,然后聚合到每一行都包含每个角色中冠军的平均统计数据(如果有的话)。这意味着 Ekko 在中间车道和丛林中有单独的一行,两者都包含每个统计数据的相关平均值。最终特征如下:
分析的每场比赛的所有统计列表。
通过提取关键信号降低数据的维度
我们现在的问题是,有太多的数据,很容易对每个冠军进行分类。重要的是,数据中存在*量的多重共线性,其中许多列高度相关。例如,一个冠军平均杀死的数量和玩家连续杀死的次数,或者他们减少的伤害和他们受到的伤害之间有明显的关系。
幸运的是,降维提供了一个可靠的解决方案。无需在这一部分花费太多精力,这种技术采用所有已知的变量,并试图将数据“缩小”成更少的特征。为了帮助描绘一幅心理图画,想象你只有两个变量:杀死和获得的黄金。如果你把这些标绘成二维空间,你会得到类似如下的东西:
用回归线绘制的获得的黄金和杀死的总数的二维相关图。
现在,注意贯穿数据的最佳拟合线。如果你把它分离出来,并把它翻转过来,你会得到一条单一的一维线!实质上,1D 线是两个变量综合结果。左边是“穷不杀人”,右边是“富不杀人”。尽管您丢失了粒度数据,但您保留了它们在单一维度中的本质。虽然这是对幕后发生的事情的一种过于简单化的描述,但这可能有助于你的直觉。
至于降维工具包,有三个标准选项:
为了这个项目,我决定和 UMAP 一起去。我有两个原因:当试验各种技术和参数的组合时,我发现 UMAP 提供的类与我的直觉一致(是的,这使它有偏见!);在我以前的项目中,使用有正确或错误答案的标记数据(想想:癌症检测或数字识别),UMAP 提供了比 t-SNE 和 PCA 更好的准确性结果。
应用这项技术非常简单,只用三行代码就可以完成。唯一值得一试的属性是数据将被简化成多少个部分。
UMAP 包的 Python 实现。
但是,请注意,在应用 UMAP 变换之前,数据应该标准化,为此,请在数据帧上使用 SKLearn 函数 StandardScaler。GitHub 可以看到更多关于这方面的信息。
作为总结,本节将 39 个原始特性转化为 5 个核心组件。每个组件都“包含”来自原始数据集的信息,但是已经根据它们的底层关系进行了分组。如果这有助于你的理解,想象一下一个组件被称为“杀死”,虽然包含了所有特征的一部分,但它与和杀死相关的变量关系最*(想想:黄金,杀死条纹,高伤害,等等..).你可能会问,5 从何而来?实验和测试!它似乎提供了最可行的结果(再次,偏见警告)。
使用聚类算法将冠军分成几类
我们现在有了每个角色中每个冠军的 5 维数据集,是时候尝试将他们分为四类了。4 也是相对任意的,实际上可以使用任意数量的类。然而,从实验中我发现,当分成四个类时,简单性和多样性得到了很好的平衡。尽管这再次在过程中产生了偏差,但鉴于我们正在处理无监督学*,没有“正确”的答案或基于结果的方法来优化。在实践中,这些决定最终留给了那些具有领域知识的人(或者更常见的是,基于需求)。
为了创建这四个类,我们可以使用一种叫做 k 均值聚类 的技术。该技术着眼于将数据放入围绕中心点的聚类中,其中每个数据点将根据它们与这些中心中的哪一个最近而被分类。为了做到这一点,该算法首先随机选择四个质心,并根据最接近的质心对每个冠军进行分类。一旦它们都被分类,它就找到这些新分配的簇的中心,然后使用它们作为更新的质心。这样一直持续下去,直到这些中心点稳定下来。
这里有一个可视化的算法来帮助你:
k-means 算法的可视化示例,从随机的三个点开始,然后慢慢向每个类的最终质心移动。
分析课程以确定趋势和主题
一旦 k-means 在减少的数据上运行,我们现在将所有冠军分成四个单独的集群。由于我们是在一个 5 维空间中操作的,所以不可能可视化输出来帮助我们理解这些类,所以我们可以基于这四个类聚集所有的原始统计数据。因此,我们将有一个表,其中包含四个类以及我们从 Riot 数据湖中获取的每个要素的平均值。以下是摘录:
显示 ADC 角色中四个类的平均聚合统计信息的表。
现在是研究这些的时候了,以便更好地感受它们之间的不同。为此,我运行了一个脚本,将每个统计数据的类平均值与车道平均值进行比较,然后根据百分比差异对它们进行排序。
例如,在《丛林》中,我发现一个职业使得在他们的游戏中有 18.23%的第一次杀人,而的角色平均只有 12.77%。同样的职业也比平均水平少清理了 6%的丛林营地。被归类到这个集群的冠军?沙可,李辛,伊莉斯,赵信。这帮助我描绘了一个团队,我后来将它命名为“3 营地 Gankers”,参考这些冠军采取的共同丛林路径,他们在那里做红 Buff,蓝 Buff,Gromp,然后立即攻击一条车道或入侵敌人的丛林营地(感谢 jung.gg 提供的数据)。
您可以在这里找到每条赛道所有级别的列表、简短描述、哪些 meta Champions 适合它们,以及超过平均水平的 3 个关键统计数据和表现不佳的 3 个关键统计数据:
关键点:我在这里强调的一点很关键,提供这些优点和缺点是为了给每个职业一个快速的直觉。对于个人冠军而不是来说,拥有和我选择的人一样的实力是很常见的。他们仍然属于这个班级的原因是,平均而言,在所有的统计数据中,他们比任何其他人都更接近于那个,但他们不一定是这个班级的完美孩子!**
例如,中间车道的冠军 Kassadin 被归类为漫游者他们的优势在于他们经常在游戏早期攻击敌人的车道。卡萨丁本人并没有分享这种特殊的品质,但是他和这个职业分享了比其他职业更多的其他属性。**
那又怎样?
正如我经常说的,为人工智能而人工智能是浪费时间。将冠军分段成班有什么好处?这是一个非常有效的问题,也是在研究任何技术、方法或项目时都应该提出的问题。这里有几个例子(虽然有些夸张和虚构)可能有助于回答这个问题(警告:除非你熟悉这个游戏,否则你可能很难理解,如果不熟悉——第三个例子适用于商业问题):**
你是一家领先的欧洲职业电子竞技团队的分析师。在一次前所未有的事件转折中,对方球队的首发中场球员在走上舞台时戏剧性地摔倒受伤,并被一名第一次上场的新秀取代。离冠军评选还有 10 秒钟,你的主管蔻驰跑过来对你说“我需要了解他什么?!"。根本就没有时间开始列出他的共同冠军和独奏队列性能统计数据。你需要一句俏皮话来给教练提供足够的信息,让他有优势。幸运的是,你已经有了早期人工智能生成的冠军职业,所以你对玩家最近 50 场单人队列游戏进行了快速分析,并说:“他最常见和表现最好的冠军职业是安全定标员!”。你已经向教练简单介绍过了,他知道你的意思。他的目标是 bans Azir 和 Corki,然后首先选择 Orianna。这个家伙窒息了,因为他要么被迫玩一个较低层次的安全定标器,要么被迫离开他的舒适区,在那里他不断被抓到并喂食。
这是一个过于感性化的例子,但却传达了一个重要的信息:现在的总结有时比以后的细节更好。****
一个更快且(稍微)不太引人注目的例子是,如果你在第 10 季中以毛凯的身份统治第一车道,却在 10.22 补丁中读到他将被遗忘,他的所有基础数据都减少到-40。你喜欢他的踢球风格,想找个人来代替他。你会发现毛凯被认为是一个推动者和参与者,就像马尔菲特和奥恩一样。Ornn 目前有 98%的胜率,所以你决定他将是你最后几周攀登的合适人选。
寄语:知道如何宽泛地描述你喜欢的东西有助于你找到你可能喜欢的其他东西。事实上,我写了一篇文章使用类似的技术来推荐冠军。**
我还将提供一个业务示例,因为我认为欣赏这些技术的“真实世界”使用是很重要的:
假设您是一家小配件公司的营销主管,最近您获得了一笔预算,要创建五个营销活动发送给当前的客户。如果你执行上述相同的步骤,但使用人口统计、购买和网站互动等数据,你可以将你的客户分为五个“类别”。通过研究这些,您将能够针对特定类型的客户量身定制您的活动,从而提高其有效性。例如,您可能会发现一组客户是“重度研究人员”,他们不定期购物,在做出决定之前会在网站上花很长时间,通常会将物品放在篮子里几个星期。另一方面,有些“自发”顾客点击了 Instagram 链接,并在 30 秒内结账。第一类中的所有客户都会收到一封电子邮件,其中包含关于您最新产品的详细报告、优点、缺点和更详细的信息。二班收到一段 Instagram 视频,视频中有一只可爱的狗狗在玩你的产品。
信息?个体层次的分析是强*的,但是对于用例来说通常是不可行的。在这种情况下,为了完成工作,可以“扔掉”一些更精细的细节。
你已经看到文章的结尾了!我叫 Jack J,是一名将人工智能应用于竞技游戏和电子竞技的专业数据科学家。我是iTero的创始人。GGT5【和jung . GG。你可以在 Twitter 上关注我,加入 iTero Discord 或者给我发邮件到 jack@itero.gg 。下一场见。
原文发表于:【https://itero.gg/blog】
结合 Knime 和 Python 的嵌入和聚类
实践教程
KNIME 中聚类 MNIST 数据库的 UMAP 降维和 DBSCAN
聚类。橄榄和树叶。形状和颜色。(图片由作者提供)
Knime 是一个免费的开源数据分析、报告和整合平台。KNIME 通过其模块化的数据流水线概念,集成了用于机器学*和数据挖掘的各种组件。对于像我这样没有很强编码背景的人来说,Knime 是端到端数据科学体验的*门。在过去两年与 Knime 的合作中,我已经能够理解数据科学的全球图景,我将我的恐惧抛在身后,现在我在充满挑战和令人惊叹的数据科学世界中越走越深。
几天前,我在《走向数据科学》上读到了一篇文章,作者是罗莎丽亚·西里波和米莎·利索夫伊,展示了一个非常好的 Knime 工作流,其中涉及应用于 MNIST 数据集的 t-SNE 。我曾经和 UMAP 一起做过类似的工作流程,效果不错。
受到 Linkedin 上的 Rosaria Silipo 的鼓励,我决定发表我的第一个故事。
这个故事的提议是什么?
本文的原始资料是闫乐存的从 0 到 9 的手写数字的 OpenML 的 MNIST 数据集。该数据集包含 70000 个 28x28 像素的手写数字,灰度等级为 0-255。也就是说,我们有 70000 个向量,维数是 784。
该数据在 Knime 内的 Python 脚本中读取,并在该 Python 脚本上应用 UMAP 降维算法,因此我们可以将数据集的每个图像映射到低维空间的一个点上。出于表现结果的目的,逻辑上,我使用 2D 和 3D 作为目标尺寸。
之后,为了更有趣,由于生成的 UMAP 嵌入数据忽略了标签,我在 Knime 中应用了一个 DBSCAN 节点来对结果进行聚类。我们稍后将检查结果。
UMAP:降维的一致流形逼近和投影
均匀流形近似和投影(UMAP) 是一种降维技术,可用于类似于 t-SNE 的可视化,也可用于一般的非线性降维。该算法基于对数据的三个假设
-数据在黎曼流形上均匀分布;
-黎曼度量是局部常数(或者可以近似为常数);
-歧管是局部连接的。
什么是黎曼流形?等等!!什么是流形?流形是 n 维的拓扑对象,在一个点的邻域上,它的局部行为类似于 n 维欧几里得空间。一个好的外行例子是地球。Rieamannian 流形是光滑的可微流形,其中可以定义函数的长度、曲线、角度、体积、梯度等概念。更多关于流形或黎曼流形的信息,试试这里的和这里的。
维基百科中的一个黎曼流形。(斯特拉特,公共领域,通过[维基共享](http://StuRat, Public domain, via Wikimedia Commons))
UMAP 有一些参数来控制其性能。
n _ neighbors:n _ neighbors 越*,算法寻找的邻域越*,并且趋向于全局整体结构。n_neighbors 越低,UMAP 将关注的细节结构越多。
最小距离 :定义算法将考虑在最终缩减空间中将点打包在一起的最小距离。这意味着较小的最小距离值将导致较小的聚类嵌入。另一方面,最小距离的高值将导致 UMAP 算法指向全局结构。
度量 :我们可以定义算法在原始空间中计算距离的度量。
难以消化!我知道。对工程师来说近乎神奇。从黑暗中走出来,明确地(简单地!!),UMAP 通过 n 维点(n 低至 2)对每个高维对象进行建模,使得相似的对象通过附近的点进行建模,而不相似的对象通过远处的点进行建模的概率很高。
基于密度的噪声应用空间聚类
DBSCAN, 基于密度的具有噪声的应用的空间聚类 ,是在无监督的 lerning 方法中考虑的一种聚类算法。基于三个输入参数,该算法能够通过将紧密地打包在一起的点(具有一定数量的附近邻居的点)分组在一起,将单独且远离地位于低密度区域(其最近的邻居太远)中的那些点标记为噪声,来将一组数据群集化。
可以想象,支配 DBSCAN 算法的性能的参数与决定一个点是否彼此靠近的距离 epsilon 有关,并且还与认为一个点是“邻域”或核心点所必需的附近点的最小数目最小点。集群是所有核心点及其邻居的总和。最后,在 Knime 扩展 KNIME 距离矩阵中包含的 DBSCAN 的 KNIME 节点中,我们可以看到需要一个度量输入,因此算法具有必要的距离模型来计算数据集上点之间的 de 距离。
正在配置 DBSCAN 节点。(作者图片)
总结一下:
epsilon :指定点之间的距离,因此它们可以被认为是集群的一部分。如果两个点之间的距离小于 epsilon,则这两个点将被视为属于同一集群的邻居。
最小点数 :从点 p 可达的最小点数,使得点 p 可被视为核心点。
距离模型 如前所述,需要用距离模型通知 DBSCAN 节点对数据集中的点间距离进行评估。为此,我们使用 Mumeric Distance 节点。
DBSCAN 算法的显著优点是:
- DBSCAN 不需要用户指定群集的数量。
- DBSCAN 管理噪声概念,并丢弃和标记噪声点
Knime 工作流
这两个 Knime 工作流均在 Knime Hub 中发布和提供:
UMAP DBSCAN MNIST 2D 巨蟒。UMAP 2D 维嵌入
UMAP DBSCAN MNIST 3D 巨蟒。UMAP 三维嵌入
我将只关注 2D 解释,因为两个工作流实际上是相同的。不过,我会在最后展示两者的比较结果。
遵循你可以看到整个工作流程,稍后我会解释它逐块。
2D UMAP 嵌入的工作流程。(图片由作者提供)
Python 脚本
接下来,您可以找到第一个数据块的详细图片,使用该数据块从 Knime Python 扩展的 Python 源节点中提取数据并计算 UMAP。
Python 抓取和 UMAP。(图片由作者提供)
中间的橙色块是 Python 源节点,包括用于从 OpenML 获取数据和执行 UMAP 技术的脚本。两个变量节点“Metric”和“Number of rows”被合并并像 UMAP 算法的输入一样被传递。对于最终的结果,我使用了欧几里德度量,关于“行数”整数输入节点,我只是在测试时用它来改变低数值。只是时间问题。在右边,我使用列重命名节点在工作流中用可理解的名称命名列。
在这里,您可以找到 Python 源节点中的代码:
from pandas import DataFrame
from sklearn.datasets import fetch_openml
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns# Dimension reduction and clustering libraries
import umapin_batch = flow_variables[‘input_batch’]
umap_metric = flow_variables[‘metric’]#Varias metricas posibles
# mnist_784
# Fashion-MNIST
mnist = fetch_openml(‘mnist_784’, version=1, as_frame=False)
mnist.target = mnist.target.astype(int)standard_embedding = umap.UMAP(
n_neighbors=30,
min_dist=0.0,
n_components=2,
metric=umap_metric,
random_state=42
).fit_transform(mnist.data[:in_batch, 1:])#Añadimos los target
array2D_1=standard_embedding
array2D_2=mnist.target[:in_batch]
array2D_2=np.reshape(array2D_2,(in_batch,1))
array_to_save=np.concatenate((array2D_1,array2D_2),axis=1)# Output table for the node
output_table = DataFrame(array_to_save)
第一次绘图
在执行了 UMAP 算法之后,我们有了 MNIST 数字的 2D 映射。为了比较结果,我使用了两种不同的绘图方法。
策划 UMAP。(图片由作者提供)
UMAP 输出散点图(Plotly)结合色彩管理器
来自的散点图 Plotly Knime 扩展是一个非常好的节点。结果是交互式的,输出颜色可通过颜色管理器节点进行配置,因此缩减的 2D 空间中的数据集点可根据其在嵌入前在原始空间中的位数进行标注。
用散点图绘制的 UMAP 算法定义的 2D 簇。(图片由作者提供)
或者,在 3D 中,我们可以使用散点图节点,而不是散点图节点,结果如下。
用散点图 3D 绘制的 UMAP 算法定义的 3D 聚类。(图片由作者提供)
请注意,UMAP 算法已经在 2D 和 3D 中检测和绘制了相同的 10 个清楚区分的簇。
用 Python 视图输出 UMAP
使用 Python 源节点和一段代码,也可以找到类似的结果。
用 Python 绘制的 UMAP 算法定义的 2D 聚类。(图片由作者提供)
用 Python 绘制的 UMAP 算法定义的三维聚类。(图片由作者提供)
接下来,您可以在 Knime 的 Python 视图节点中找到执行这两个绘图的代码片段。
为了 2D
from io import BytesIO
import matplotlib as mplt
import matplotlib.pyplot as plt
import numpy as nplabel_array=input_table['Col2']
colors = ['red', 'blue', 'green', 'yellow', 'orange', 'peru', 'lime', 'pink', 'steelblue', 'olive']plt.scatter(input_table['Col0'], input_table['Col1'],
c=label_array, alpha=0.5, marker='o',
cmap=mplt.colors.ListedColormap(colors),
s=0.1,
label="Label")
plt.xlabel("X")
plt.ylabel("Y")
plt.legend(loc='best')buffer=BytesIO()
plt.savefig(buffer,format="svg")
output_image = buffer.getvalue()
对于 3D:
from io import BytesIO
import matplotlib as mplt
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3Dlabel=input_table['Col3']colors = ['red', 'blue', 'green', 'yellow', 'orange', 'peru', 'lime', 'pink', 'steelblue', 'olive']fig = plt.figure(figsize=(12,12))
ax = fig.add_subplot(111, projection='3d')ax.scatter(input_table['Col0'],input_table['Col1'] ,input_table['Col2'],
c=input_table['Col3'],
cmap=mplt.colors.ListedColormap(colors),
marker='o',
s=1)ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')buffer=BytesIO()
plt.savefig(buffer,format="svg")
output_image = buffer.getvalue()
DBSCAN 节点和 sorroundings
因此,到目前为止,我们有一组 2D(或 3D)点,代表一组 700000 个书面图像。我们知道哪个原始数字对应于缩减空间的每个点,因为原始标签已经添加到 UMAP 输出中,正如您在此处看到的:
第 0 列:X,第 1 列:Y,第 2 列:对应于原始数字的标签。(图片由作者提供)
但是想象一下我们没有原始标签。我们会有*量没有进一步信息的点,如下所示:
未标记的点,在灰色物质下面有什么结构吗?。(图片由作者提供)
现在是时候使用 DBSCAN 并尝试找到在这个分散的无意义的图下面是什么结构了。
DBSCAN 节点。(图片由作者提供)
我们将 UMAP 的输出驱动到 DBSCAN 节点。此外,我们为其余的 DBSCAN 输入提供变量,尽管它们可以在节点内部配置。
DBSCAN 的配置屏幕。参见注释“Eps 和 Minpts”由变量控制。(图片由作者提供)
对于 2D DBSCAN,我使用了ε= 0.15和最小点数=50 。数值距离节点中的公制被配置为欧几里得。
经过一些字符串操作后,输出是一个包含聚集数据的表。
请参见包含集群数据的最后一列。请参阅高亮显示的行,其中一个点被认为是噪点。(图片由作者提供)
如果我们在 2D 或 3D 的缩减空间内绘制点,并用 DBSCAN 产生的聚类进行标记,我们会发现什么?
让我们看看。
总体结果
在下图中,您可以看到 DBSCAN 识别的群集与 UMAP 根据原始映像交付的群集相同。这是非常令人满意和美好的。
2D 数据库扫描聚类。(图片由作者提供)
3D DBSCAN 聚类。(图片由作者提供)
DBSCAN 的结果是否足够好与原始标签有关?
与原始标签相关的 DBSCAN 聚类的准确率为 95.9%。
所有这些工作都是在 Knime 中完成的,结合了原生 Knime 节点,并在其工作流中嵌入了 Python 代码,从而展示了 Knime environement 的功能。
我正在思考的下一个故事,将是关于如何执行一个类似的分析,但用一个神经自动编码器达到嵌入状态。会随时通知你。
数据集成 UMAP
生命科学的数理统计和机器学*
基于图形的单细胞组学与 UMAP 的集成
作者图片
这是来自 生命科学的数理统计和机器学* 专栏的第二十篇文章,我试图以简单的方式解释生物信息学和计算生物学中使用的一些神秘的分析技术。数据整合是下一个重要的步骤,通过结合多种信息来源,利用协同效应来提高分析准确性。在计算生物学和生物医学中,数据集成在单细胞研究领域取得了特别的进展。去年, 自然 认定单细胞多模态组学集成为 2019 年度 方法。在本文中,我将展示如何通过使用图交集方法和图和 UMAP 来执行单细胞组学( scOmics )数据的基于图的跨模态集成。
数据集成背后的理念
当组合来自不同统计分布的数据时,简单地将它们连接在一起而不考虑它们各自的分布可能不是一个好主意,因为它们中的一些可能是二元的、分类的或连续的。使用 scOmics 数据,人们经常处理连续的 scRNAseq (转录组)/ scProteomics (蛋白质组)和二元的 scBSseq (甲基化)/ scATAseq (开放染色质区域)数据。假设个体数据类型能够以 78%、83%和 75%的准确率(数字是编造的)区分细胞与患病个体和健康个体。然后,将多个 scOmics 组合在一起的一个期望将是预测精度的提升(例如高达 96%),这是由于特定技术噪声的降低和 scOmics 信号一致性的增强。
作者图片
组合单个 scOmics 可以有不同的方法。一种可能的方法是将 scOmics 转换到一个公共的非参数空间,在那里它们失去了关于其原始技术的记忆。这就是人工神经网络(ANN)如何能够压缩多个信息源,我在之前的一篇文章中详细描述了这一点。【SNF】相似度网络融合也属于这一类方法。另一种方法是明确地对单个数据分布建模,并使用贝叶斯规则将它们组合成一个 联合概率。最后,人们可以尝试提取共同的变化(即理清数据中特定技术和共享的变化)并将其分解(分解成正交分量)以便更好地解释,这是 PLS 、 CCA 和因子分析使用的方法。
基于图形的数据集成
基于图表的数据整合属于上一节中的第一种方法,即当转换为图表时,单个数据集表示数据点之间的成对连接,而不需要任何关于生成单个数据集的统计过程的“记忆”。在图形空间中,通过保持各个数据集的图形上的数据点之间的边缘一致,可以直接找到各个数据集的各个图形之间的交点。这种方法的优势在于,当将原始数据转换为图形时,可以应用适当的 距离度量,即,在处理二进制数据时,可能希望使用汉明距离来计算数据点之间的成对连接,而在处理连续数据时,应用欧几里德距离可能就足够了。
为了展示基于图的数据整合的思想,我们将把它应用于 scNMT 单细胞组学数据集,该数据集包含:1)基因表达(scRNAseq),2) DNA 甲基化(scBSseq),以及 3)来自相同生物细胞的开放染色质区域(scATACseq)信息。我们将从读取 scNMTseq 数据开始,即 scRNAseq、scATACseq 和 scBSseq,过滤数据,例如通过去除 scRNAseq 中低表达的基因,应用对数变换以使数据看起来更正态分布,并检查数据分布。
作者图片
单细胞基因表达(scRNAseq)数据在某种程度上是零膨胀的,并且明显是连续的,因此使用欧几里得距离计算细胞之间的成对相似性可能是合适的。然而,当我们绘制 scBSseq 和 scATACseq 数据分布时,很明显它们遵循双峰分布,因此使用欧几里得距离来测量使用这种类型的二进制数据的细胞之间的相似性是不合适的。因此,我们将对 scBSseq 和 scATACseq 使用汉明距离,而骰子和 Jaccard 相似度也是一个可能的选择。
作者图片
如果我们查看 scBSseq 数据矩阵,元素似乎在 0%和 100%之间变化,这意味着某个 CpG 位点可以是甲基化的(100%)或未甲基化的(0%)。实际上,并非矩阵的所有元素都是严格的 0%或 100%,这可能是由于技术采样和排序的原因。但是,我们可以将 scBSseq 数据(同样适用于 scATACseq 数据)视为二进制数据。为了使数据显式为二进制,我们将把所有低于 50%的计数编码为 0,高于 50%的计数编码为 1。然后对 scATACseq 数据应用类似的二值化过程。您不一定要这样做,但为了解释方法,使用图相交方法演示连续数据 scRNAseq 如何与完全二进制数据 scBSseq 和 scATACseq 集成是很有趣的。
现在,我们将使用来自 scRNaseq 数据集的原始基因表达数据构建一个 KNN 图。我们将在单元之间应用欧几里得相似性度量,并为每个单元保留 30 个最近的邻居。事实上,当从全连通图开始时,scikit-learn 中的 KNN 方法将检查每一对数据点的边的权重,并为每个数据点选择 30 个权重最*的邻居。结果我们会得到一个 KNN 邻接矩阵 ,其中 1 表示单元格连通,0 表示不连通。要了解图形构造的其他技巧,请查看来自瑞本菲塔斯的本精彩笔记本。
作者图片
这里,我们选择了一对随机的点,例如 ESC_F04 和 ESC_F07 ,它们似乎在 scRNAseq 数据集中是相连的。我们将在 scBSseq 和 scATACseq 数据集中跟踪这种联系。下面我们将使用 MDS 布局可视化 KNN 图,并通过红边突出显示 ESC_F04 和 ESC_F07 单元格之间的连接。我们现在已经可以观察到 ESC 细胞和 EB 细胞彼此分开的某种聚集。现在,我们将对 scBSseq 数据进行相同的图形构建,详情请查看完整的 jupyter 笔记本。在这种情况下,由于 scBSseq 是一个二进制数据,因此像我们对 scRNAseq 数据所做的那样,用欧几里德距离来度量细胞之间的相似性不是最佳的。这里我们选择汉明距离,尽管 Dice 和 Jaccard 距离可能也适用于分类/二进制数据。
作者图片
作者图片
太好了!现在,我们已经从 scRNAseq、scBSseq 和 scATACseq 构建了 3 个 KNN 图,并准备相交这些图。正如在Wolfram Math Worldpublic resource 中完美展示的那样,图交集的思想是保持多个 scOmics 数据集之间的边一致。我们将使用在 igraph 库中实现的交集函数。在我们执行了图形交集之后,一些节点将被隔离。这里,为了简单起见,我们将忽略/删除这些节点,并且在稍后运行基于 Leiden 图的集群时不将它们视为单独的集群。
来自Wolfram Math World Public Domain的图形交集插图
让我们将通过与相交获得的共有图可视化,这 3 个图来自单独的 scOmics,并证明 ESC_F04 和 ESC_F07 细胞之间的连接/边缘仍然存在,我们将用红色突出显示该边缘。现在我们可以运行 Leiden 基于图的聚类算法(对 Louvain 算法的修改),该算法在共识图上找到 4 个聚类。
作者图片
我们可以看到,EB 细胞似乎形成了一个单独的(绿色)簇,而 ESC 细胞现在分裂成至少两个(蓝色和红色)簇。有一小组黄色细胞被 Leiden 发现为第四个群,但是这可能应该用 Leiden 算法的分辨率参数来调整,因为黄色细胞在视觉上似乎不是一个单独的群。另一方面,这也可能是布局的问题。尝试另一种图形布局可能会证明黄色细胞确实形成了一个独特的集群。
单细胞组学整合的 UMAP
事实证明,手动构建 KNN 图,然后按照我们在上一节中所做的方式对各个 scOmics 的图进行交集,并不是真正必要的,因为这个选项似乎基本上包含在 UMAP 降维算法中。的确,正如原 UMAP 文章中所说:
利用数据 UMAP 的模糊单纯集表示可以潜在地扩展到支持具有不同数据类型的数据集的降维。每种数据类型都可以被视为底层结构的另一种视图,每种数据类型都有不同的关联度量,例如,分类数据可能使用 Jaccard 或 Dice 距离,而顺序数据可能使用哈曼距离。每个视图和度量都可以用来独立地生成模糊单纯集,然后可以将这些模糊单纯集相交在一起,以创建单个模糊单纯集用于嵌入。
作者图片
因此,UMAP 允许快速构建单个 scOmics 数据集的模糊单纯集(类似于构建 KNN 图)及其交集,只需几行代码,参见关于使用 UMAP 混合不同数据类型的有趣讨论此处。
为了演示 UMAP 如何用于整合 scOmics 数据,我们将交叉来自单细胞 RNAseq 和来自 8617 个血液单核细胞(CBMCs)的蛋白质水平数据集的图,这些数据是通过CITE-seq 技术测序的,我们将制作一个一致性 scOmics UMAP 图。同样,想法是将两个 scOmics 数据集转换到非参数图形空间(模糊单纯集),其中它们忘记它们的技术差异,并在该非参数空间中相交图形。产生的共识/整合图将用于构建 UMAP 嵌入。
然而,我们将从显示两个单独的 scOmics 数据集的 UMAP 嵌入开始。作为最近邻的最佳数量,我们将选择 sqrt(N) = sqrt(8617) = 93,这一选择背后的动机在这里中已经涉及,并且与格子上的随机漫步的最佳*小有关,超过这个*小,随机漫步的“记忆”就会丢失。
作者图片
作者图片
UMAP 维数缩减图上的颜色对应于血液单核细胞(CBMCs)的 12 个细胞群。现在我们将构建两个图/ 模糊单纯集的交集,并可视化由此产生的嵌入。
作者图片
在生成的嵌入图中,从两个 scOmics 的相交中,可以得到一些有趣的观察结果。例如,先前在 scRNAseq UMAP 图上同质的灰色聚类分裂成三个子聚类。另一方面,以前在 scRNAseq UMAP 图上不可区分的紫色细胞群现在形成了与蓝色细胞分开的明显的簇。这两个结果似乎都是通过将蛋白质水平的信息纳入到所得的共有 UMAP 嵌入中来驱动的。
摘要
在本文中,我们了解到基于图形的数据集成是一种有趣的方式,它可以将多个信息源结合起来,以提高分析的准确性。将原始数据转换到具有适当的个体相似性度量的非参数图形空间中,允许仅保留成对连接信息,同时忽略数据中的特定于技术的噪声。以下图形的交集(或 UMAP 术语中的模糊单纯集)提供了数据集成的直观可视化和评估。UMAP 似乎是数据集成的理想之选,因为它只需几行代码就可以指定各个距离度量和图形的交集,即混合不同种类的数据。
在下面的评论中,让我知道哪些来自生命科学的分析技术对你来说似乎是 T2 特别神秘的,我会在以后的文章中介绍它们。在我的 Github 上查看帖子中的代码。在 Medium 关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我。在下一篇文章中,我们将讨论如何在 UMAP 空间聚集,敬请关注。
PyTorch 中多任务学*的不平衡数据加载
在多个不平衡数据集上训练多任务模型的 PyTorch 实用指南
解决多任务学* (MTL)问题需要独特的培训设置,主要是在数据处理、模型架构和绩效评估指标方面。
在这篇文章中,我将回顾数据处理部分。具体来说,如何在多个数据集上训练多任务学*模型,以及如何处理高度不平衡数据集的任务。
我将分三步描述我的建议:
- 将两个(或更多)数据集合并成一个 PyTorch 数据集。该数据集将成为 PyTorch 数据加载器的输入。
- 修改批制备过程,以在每批中产生一个任务,或者在每批中混合来自两个任务的样品。
- 通过使用批量取样器作为数据加载器的一部分来处理高度不平衡的数据集。
我只审查了数据集和数据加载器的相关代码,忽略了其他重要模块,如模型、优化器和指标定义。
为了简单起见,我使用一个通用的两个数据集的例子。然而,数据集的数量和数据的类型不应该影响主设置。我们甚至可以使用同一个数据集的几个实例,以防同一组样本有多组标签。例如,具有对象类别和空间位置的图像数据集,或者具有每个图像的面部情感和年龄标签的面部情感数据集。
一个 PyTorch 数据集 类需要实现__getitem__()
函数。该函数处理给定索引的样本获取和准备。当使用两个数据集时,就可能有两种不同的方法来创建样本。因此,我们甚至可以使用单个数据集,获得具有不同标签的样本,并改变样本处理方案(输出样本应该具有相同的形状,因为我们将它们作为批量张量进行堆叠)。
首先,让我们定义两个数据集:
我们定义两个(二进制)数据集,一个有 10 个 1 的样本(平均分布),第二个有 55 个样本,50 个数字 5 的样本和 5 个数字 5 的样本。这些数据集仅用于说明。在真实的数据集中,你应该既有样本又有标签,你可能会从数据库中读取数据或者从数据文件夹中解析数据,但是这些简单的数据集足以理解主要概念。
接下来,我们需要定义一个 数据加载器 。我们用我们的 concat_dataset 提供它,并设置加载器参数,比如批量*小,以及是否重排样本。
这一部分的输出如下所示:
tensor([ 5., 5., 5., 5., -5., 5., -5., 5.])
tensor([5., 5., 5., 5., 5., 5., 5., 5.])
tensor([-1., -5., 5., 1., 5., -1., 5., -1.])
tensor([5., 5., 5., 5., 5., 5., 5., 5.])
tensor([ 5., 5., 5., 5., -5., 1., 5., 5.])
tensor([ 5., 5., 5., 1., 5., 5., 5., -1.])
tensor([ 5., 5., 5., 5., -1., 5., 1., 5.])
tensor([ 5., -5., 1., 5., 5., 5., 5., 5.])
tensor([5.])
每一批都是来自我们的 concat_dataset 的 8 个样本的张量。顺序是随机设置的,样本是从样本池中选择的。
直到现在,一切都相对简单。这些数据集被组合成一个数据集,并从两个原始数据集中随机选取样本来构建小批量。现在让我们试着控制和操作每批样品。我们希望在每个小批量中只从一个数据集获取样本,每隔一批在它们之间切换。
这是一个 BatchSchedulerSampler 类的定义,它创建了一个新的样本迭代器。首先,通过为每个内部数据集创建一个 RandomSampler 。第二种方法是从每个内部数据集迭代器中抽取样本(实际上是样本索引)。因此,构建一个新的样本索引列表。使用 8 的批量意味着我们需要从每个数据集获取 8 个样本。
现在让我们使用一个新的数据加载器运行并打印样本,它将我们的 BatchSchedulerSampler 作为输入采样器(使用采样器时,shuffle 不能设置为 True )。
输出现在看起来像这样:
tensor([-1., -1., 1., 1., -1., 1., 1., -1.])
tensor([5., 5., 5., 5., 5., 5., 5., 5.])
tensor([ 1., -1., -1., -1., 1., 1., -1., 1.])
tensor([5., 5., 5., 5., 5., 5., 5., 5.])
tensor([-1., -1., 1., 1., 1., -1., 1., -1.])
tensor([ 5., 5., -5., 5., 5., -5., 5., 5.])
tensor([ 1., 1., -1., -1., 1., -1., 1., 1.])
tensor([5., 5., 5., 5., 5., 5., 5., 5.])
tensor([-1., -1., -1., -1., 1., 1., 1., -1.])
tensor([ 5., -5., 5., 5., 5., 5., -5., 5.])
tensor([-1., 1., -1., 1., -1., 1., 1., -1.])
tensor([ 5., 5., 5., 5., 5., -5., 5., 5.])
tensor([ 1., -1., -1., 1., 1., 1., 1., -1.])
tensor([5., 5., 5., 5., 5., 5., 5.])
万岁!!!
对于每个小批量,我们现在只获得一个数据集样本。为了对更重要的任务进行降采样或升采样,我们可以尝试这种调度方式。
我们批次中剩下的问题来自第二个高度不平衡的数据集。这是 MTL 经常出现的情况,有一个主任务和一些其他的卫星子任务。一起训练主任务和子任务可能会提高性能,并有助于整体模型的一般化。问题是子任务的样本通常非常稀疏,只有几个正(或负)样本。让我们使用我们以前的逻辑,但是也强制在每个任务中关于样本分布的平衡批处理。
为了处理不平衡的问题,我们需要用一个ImbalancedDatasetSampler替换 BatchSchedulerSampler 类中的随机采样器(我使用了这个资源库中的一个很好的实现)。这个类处理数据集的平衡。我们也可以混合使用 RandomSampler 用于一些任务,使用不平衡数据采样器用于其他任务。
我们首先创建ExampleImbalancedDatasetSampler,它继承了 ImbalancedDatasetSampler ,只修改了 _get_label 函数来适应我们的用例。
接下来,我们使用BalancedBatchSchedulerSampler,它类似于前面的 BatchSchedulerSampler 类,只是将 RandomSampler 用于不平衡任务的用法替换为examplembanceddatasetsampler。
让我们运行新的数据加载器:
输出如下所示:
tensor([-1., 1., 1., -1., -1., -1., 1., -1.])
tensor([ 5., 5., 5., 5., -5., -5., -5., -5.])
tensor([ 1., 1., 1., -1., 1., -1., 1., 1.])
tensor([ 5., -5., 5., -5., -5., -5., 5., 5.])
tensor([-1., -1., 1., -1., -1., -1., -1., 1.])
tensor([-5., 5., 5., 5., 5., -5., 5., -5.])
tensor([-1., -1., 1., 1., 1., 1., -1., -1.])
tensor([-5., 5., 5., 5., 5., -5., 5., 5.])
tensor([ 1., -1., 1., 1., 1., -1., 1., -1.])
tensor([ 5., 5., 5., -5., 5., -5., 5., 5.])
tensor([-1., -1., -1., -1., 1., 1., 1., 1.])
tensor([-5., 5., 5., 5., 5., 5., -5., 5.])
tensor([-1., 1., -1., 1., 1., 1., 1., 1.])
tensor([-5., -5., 5., 5., -5., -5., 5.])
不平衡任务的小批量现在更加平衡了。
这个设置还有很*的发挥空间。我们可以以平衡的方式组合任务,通过将 samples_to_grab 设置为 4,这是批量*小的一半,我们可以获得一个混合的小批量,每个任务中有 4 个样本。为了对更重要的任务产生 1:2 的比率,我们可以为第一个任务设置 samples_to_grab=2 ,为第二个任务设置 samples_to_grab=6 。
就是这样。完整的代码可以从我的库下载。
疏通 Power BI——向 Power BI 部署渠道(BIOps)的飞跃(Ep。6)
使用 Power BI 自动化报告生命周期(BIOps)
Power BI 最近在 preview 中推出了一项名为部署管道的新功能,以自动化 Power BI 报告和内容创建(Ft。Narisorn Limpaswadpaisarn
上一集
- 解除 Power BI 封锁—Power BI 报告服务器(内部部署)可以导出 CSV 和 Excel 格式的数据吗?(Ep。1)
- 一天内解除电力 BI-仪表板阻塞(Ep。2)
- 疏通 Power BI —如何获得 Power BI 桌面本地与云?(Ep。3)
- 疏通 Power BI —让我们明确 Power BI 产品组合!(Ep。4)
- 解除 Power BI-Power BI Premium 作为分析服务的超集(Ep。5)
Power BI Service 有一个工作区的概念,您可以在其中创建仪表板、报告和分页报告的集合,并与工作区内的任何人进行协作。点击了解有关 Power BI 工作区[的更多详情。在过去,我听说过客户需要为 BI 项目生命周期管理分离出开发、测试和生产工作空间的需求。我们将这个概念称为“分阶段部署”,这是一个“隔离适合流程每个阶段的数据、用户和内容”的框架。根据](http://Create the new workspaces in Power BI)【掌握权力毕】,分阶段部署的步骤包括:
- 创建开发应用工作区,并将 Power BI 内容构建到工作区中。
- 一个应用程序被发布或更新,并提供给少数用户审阅。
- BI 经理或项目负责人审查正在开发的内容的状态,并向开发人员提供反馈。
- Power BI REST API 用于将完整的内容从开发工作区迁移到测试工作区。
- 发布或更新测试应用程序,并提供给少量用户进行审查。
- 用户验收测试(UAT)用户或团队审查与需求相关的内容,并提供反馈。
- Power BI REST API 用于将批准的内容从测试工作区迁移到生产工作区。
- 发布或更新生产应用程序,并提供给用户组使用。
- 业务用户组通过生产应用程序从任何设备访问和使用仪表盘和报告。
下图和九步流程描述了分阶段部署生命周期的主要元素。来源:掌握权力 BI(布雷特鲍威尔)
然而,我对 Powell 方法的经验是,高级 BI 管理员需要创建个人工作区来定义开发、测试和生产,这在技术上是可能的,但还不够直观。我很高兴看到“ Power BI 部署管道的公开预览版,这是 BI 团队在 Power BI Premium 中管理内容生命周期的一种新的改进方法。”简而言之,我将这一特性称为“ BIOps ”,因为 Power BI 可以自动化商业智能报告生命周期,并以前所未有的速度提供更新。
先决条件
您必须:
- 使用 Power BI Premium 许可证。
- 成为新工作空间体验的管理员。简单来说就是不支持经典工作空间。
要开始使用,请前往https://app.powerbi.com/并使用您的 Power BI 帐户登录。创建一个工作空间并获取一些 Power BI 样本。许多样品都很容易获得:
- 电力 BI 的客户盈利能力示例
- 用于功率 BI 的分析样本
- Power BI人力资源样本
- Power BI 的机会分析样本
- 电力 BI 采购分析样本
- Power BI销售和营销样本
选择一些样本到选定的工作区。
创建部署管道(在预览中)
- 创建管道
- 分配您的工作空间
- 开发和测试你的内容
- 与您的用户分享
这是“创建部署管道(预览)”的登录页面
对于部署管道,不需要为开发、测试和生产分别创建 3 个工作区。相反,Power BI 将为您自动完成这一过程。只需单击“部署到测试”和“部署到生产”。单元测试相应地运行。
我只需要为 1 个管道附加 1 个工作空间。当您确认部署到测试或部署到生产时,将自动创建后面的阶段。您的新工作区将保持相同的命名约定,除了末尾的标签指定它是开发工作区、测试工作区还是生产工作区。
观察
- 工作空间 必须 是新的工作空间体验。经典的工作经验是行不通的,没有例外。
- 仅为 1 个部署管道分配 1 个工作空间。
- 只有在您愿意的情况下,您才能部署数据集。如果存在依赖关系,报告可能无法部署。我建议部署一组数据集和报告。
- 在撰写和发布本文时, Power BI 数据流尚不支持部署管道。
- 此功能仍在公开预览中;因此,还不建议将其用于生产。我会密切关注这个空间,因为它绝对很棒。
参考
免责声明:以下内容并非本人雇主官方背书。本文表达的观点和意见仅代表作者的观点和意见,不一定反映当前或以前的雇主、组织、委员会、其他团体或个人的官方政策或立场。本文中进行的分析是基于有限日期的开源信息。分析中所做的假设并不反映任何前任或现任雇主的立场。
用近似梯度法解套索正则化
迭代软阈值算法
我首先告诉你,我不是数学家,因为这个话题需要一些数学知识(振作起来!)我想说明的是,它不会完全严谨。
本文旨在总结应用 ISTA(迭代软阈值算法)*解决梯度下降优化套索正则化问题的整个过程。*
当然,我假设如果你正在阅读,至少你对我们将要谈论的内容有所暗示,我希望我的帖子会有所帮助。
让我们首先构建一个数学难题,它将引导我们理解如何使用梯度下降计算 lasso 正则化,即使成本函数不是完全可微的,如 Lasso 的情况。
子梯度
首先要掌握的概念是凸函数的定义。
一个完全可微的函数 f 被说成是凸的如果:
换句话说,其局部 线性逼近 (泰勒展开)总是低估 f 。这种凸函数的一个例子是我们钟爱的均方误差。
让我们更进一步,从凸函数直接得出次梯度,当我们处理一个在每一点都不可微的函数时,这个工具是必要的,而且由于套索使用了我们变量(绝对值)的 L1 范数 我们知道这个函数在零点 不可微 。
次梯度 是一种概括凸函数在不可微点处梯度的方法。凸函数 f 在 x 点的次梯度是任意的 g 这样:
该表达式也是对 f 的线性近似,其中我们使用次梯度来代替梯度,次梯度在满足上述条件的值的 集合 内。
凸函数 f(x)的次梯度。点 x1 是可微的,我们有一个唯一的梯度,而在点 x2(不可微的)我们有一组符合上述条件的可能的子梯度值
g 1 ,g 2 ,g3是* f 在x1,x 2 的子渐变。***
在函数可微的点(x 1 )上,次梯度是 唯一的 确切地说是:g=∇f(x)、 相反,在 x 2 上,我们可能有无限数量的次梯度(在上图中,我们只能看到其中的几个)
在 f(x)=|x|的情况下,x = 0 处的子梯度 g 如下图所示,它们的范围在[-1,1]中包含的所有可能值之间。任何一条斜率在此范围* 内穿过 x = 0 的直线,都会对函数 进行下界。***
x=0 时 f(x)=|x|的一些次梯度
凸 f 的所有次梯度的集合称为次微分:
次微分定义可以推广到各种凸函数,无论可微与否。主要的一点是,当一个函数在它的定义域中的每一点都是完全可微的,那么次微分是由整个梯度集合(分别是唯一的值)组成的,相反,当一个函数不是处处可微时,次梯度就来了。
再次考虑单变量 x 的 L1 范数:
绝对值函数(左)及其次微分∂f(x)作为 x 的函数(右)
f(x) = |x|的次微分;这种情况下 k=1,2,3
给定次微分,则任意 f (可微或不可微)的最优性条件为:
*x 是极小元当且仅当 0 是fat***x **的次梯度。这叫做 次梯度最优性条件 。
为什么?简单,g= 0 是次梯度意味着对于所有 y:
注意一个凸且可微的函数 f 的含义:
近端标测
现在我们已经熟悉了次梯度,我们需要了解的其他工具有:近似算子和软阈值算子。
近端操作符 定义:
这里我们搜索的是点【x 】,它最小化了一个通用凸函数 f*****但是 同时保持接近一个参考点 我们通过α选择接近多少作为这些术语之间的相对权重或权衡参数****
软阈值算子 定义:
或者更简洁的形式:
例如给定λ= 1,如果 x = 3,sign(3) = 1,max((| 3 |–1),0) = max(2,0)= 2->1 * 2 = 2;如果 x = -0.5,sign(-0.5) = -1,max((|-0.5 |–1),0) = max(-0.5,0)= 0->-1 * 0 = 0**;如果 x = -3,sign(-3) = -1,max((|-3 |–1),0) = max(2,0) = 2 - > -1 * 2 = -2**
好吧,我知道,你在想: 为什么是他们???我要啤酒不要操作员!****
我理解你的兄弟姐妹。但是在你喝啤酒之前,你需要经历最艰难的部分。继续战斗!
那么,让我试着解释一下所有这些,并开始应用近似运算符。
定义 f 为局部线性逼近在xk对于一个通用函数,我们假设* 凸且可微,我们把它代入最接近的算子:***
已知,点 x 是局部极小点如果:*
我敢打赌这提醒了你一些事情……等等,我用一种更熟悉的符号来写:
现在好些了吗?是的,这是一个梯度下降步骤,仅用线性近似的近似映射来计算。事实上,梯度下降和近似算子是非常亲密的朋友。
下一轮我们将使用不同的函数:
同样,我们可以将它插入到 prox 操作符中,我们得到:
从 L1 和 L2 范数的定义我们得到:
这些是可分离的函数,因此我们可以将它们作为组件式标量问题来解决:
由于 h 是凸的,但在 x = 0 处不可微,我们用它的次微分来工作并回忆:
于是我们得到了最后的结果:
或者用上面介绍的软阈值运算符重新排列:
最后这个结果可能有点棘手,让我解释一下是怎么回事。根据绝对值的次微分和上面获得的最优性条件,我们有 3 种可能的情况:
所以最后我们可以概括为:
****
图片来源皮埃尔·阿布林
开始吧,最后一轮…阿德里亚娜!
现在考虑:
作为一个一般的凸可微函数 g(即最小二乘)加上一个凸不可微函数 h 的和,在我们的情况下是 L1 范数,我们想最小化它。为此,我们首先将一个 局部二次近似 应用于 g,并保留 h 不变。
****
在 x=1 时,具有不同 alphas 的一般 g(x)的局部二次近似
注意,我们在没有 Hessian 的情况下获得的局部二次近似只是我们之前计算出的线性近似的近似算子。
为了我们的目的,我们还需要重新排列 g:
在去掉不依赖于 x 的常数项并加上 h 后,我们可以最小化整个 f:
经过*量的数学计算,最终我们可以将 ISTA (迭代收缩阈值算法)定义为:
对于我们的 n 变量可分函数的每个分量。
是的,看起来很糟糕,但并不像看起来那么糟糕。您只需像往常一样计算梯度下降,唯一的区别是在将值传递给下一次迭代之前,使用软阈值操作符应用进一步的变换。
然后,对于梯度下降,你可以定义一个规则来停止迭代并非常接近函数的最小值。
值得一提的是,这个结果不依赖于 g,它可以像我们想要的那样复杂,我们只需要能够计算它的梯度。
搞定!从现在开始,不再有数学,只有啤酒。我们应得的。
打开解析的盒子!
图片来源:Pixabay
句法分析是分析句子中所有单词并确定它们的句法和/或语义角色的过程。就像潜意识里我们用母语中的语法来组成和理解一个句子。类似地,我们在 NLP 中使用语法来破译一个句子是如何构造的,并理解它的句法和语义角色。主要有两种解析: 选区解析 和 依存解析 。
依存解析 顾名思义就是侧重于将一个句子分解成单词或记号或短语,然后分析它们之间的二元依存关系,了解它们的语法作用。它有助于解释句子中的语义关系。
依赖关系分析能够识别的一些二进制关系如下所示:
依存分析通常使用有向弧来产生这种关系,其中弧的一端是中心词,弧指向的一端是词本身。短语(一组词)中的中心词仅仅是一个词,如果去掉这个词,短语就失去了意义。
一个短语(一组词)中的中心词就是一个词,一旦这个词被去掉,这个短语就失去了意义。
例如:
短语: 《好邻居》
在这里,如果你去掉“邻居”这个词,你就失去了这个短语的任何可能的逻辑意义,因为“好”本身没有任何意义。因此,这个短语的标题是“邻居”。
现在,有向弧和节点不会让你想起有向图吗?如果是的话,那么你是绝对正确的!我们在执行依存解析之后获得的依存树只不过是一个有向图,其中所有的单词都是顶点,并且除了根顶点之外,每个顶点或单词都只有一条引入弧。这是因为,只有依存关系树的根顶点(根单词)是没有引入弧的节点。此外,从根到图中的任何其他节点总是有唯一的路径。
现在,让我给你看一个依赖解析的例子。
罗斯取消了去迪拜的航班
现在,看看这个句子的依存解析。
这里的小句关系如 nsubj 和 dobj 把主语标识为“Rose”,把谓语“cancelled”的直接宾语标识为“flight”。请注意,“cancelled”是这两个短语中的中心词,它是图中唯一没有引入弧的节点,因此它是根。像 prep 这样的关系表明介词关系“到”的中心是“飞行”。关系 poss 是一个所有格修饰语,表明“her”是中心词“flight”的所有格修饰语,而 pobj 表明介词“Dubai”的宾语与其中心词“to”相连。
简而言之,我们现在可以看到给定句子中的每个单词是如何在语义上构造的,并且我们对给定句子中所有可能的语义上可推导的二元关系的信息是有限的。因此,依赖解析的一个巨*优势是它提供了谓词和它们的参数之间的语义关系的近似[1]。依存句法分析的另一个优势在于分析语序更灵活的语言的句子。换句话说,在语言中,人们可以更自由地放置某些词类,依存句法分析帮助我们获得关于文本的重要语义信息。
请注意,这只是一个包含少量依赖项的简短示例。如果你想了解更多关于这种关系的知识,请看看斯坦福*学的类型依赖手册。
现在,我们理解了依赖解析的作用,下一个重要的问题是理解它可以用在哪里。这种句法分析技术对于理解句子中的语义关系至关重要的应用,如共指消解、问题回答、信息抽取等,具有重要意义。
下面给出了一个使用 spacy 查找句子依存解析的例子。
依存性分析说明
选区解析 另一方面涉及到考虑关于句子的句法信息。它将一个句子分解成短语,并根据预先确定的语法和词典(或词汇)组合短语以形成一棵树,称为解析树。这些短语可以是名词短语(NP)、动词短语(VP)、状语短语、形容词短语和/或介词短语(PP)。因此,选区分析具有作为语法分析器的角色。
在我们继续之前,让我快速地告诉你什么是 NLP 中的语法。自然语言处理中的 语法 是任何语言中所有规则的正式规范。在本文中,我们将使用英语作为我们的主要语言,以帮助简化解释!
例如:
名词短语(NP): “牛逼的车”
但是这个短语不也是由其他词类构成的吗?
"the": 冠词/限定词;“牛逼”:形容词;【汽车】:名词
我们如何用语法规则来表达这一点呢?
我们说, NP → Det Adj 名词
在这里,名词、形容词和名词被称为词尾,因为它们直接表示词典或词汇中的单词。在词典中可能是这样的:
Det → the
Adj →牛逼
名词→汽车
因此,我们英语语法中的所有规则都可以用类似的方式来表示。那么,语法类型是只有一种还是有多种呢?答案是肯定的!乔姆斯基的分类主要给了我们 4 种语法。
乔姆斯基的分类
-类型 0:无限制语法
格式:x → y,其中 x 和 y 可以是非终结符和/或终结符。完全没有限制。
-类型 1:上下文敏感语法
格式:xAy → xzy,其中 x,y,z 可以是任意值,A 在上下文 xAy 中是非终结符。
-类型 2:上下文无关语法
格式:A→ x,其中 A 是非终结符,x 可以是终结符或非终结符的序列。
-类型 3:常规语法
格式 1 : A→ bt,其中 A 和 b 为非终结点,t 为终结点。
或者
格式 2 : A→ t,其中 A 为非终结符,t 为终结符。
因此,我们可以根据需要创建属于任何类型的语法,并使用它来解析句子。为了方便起见,让我们假设在本文中使用常规语法,除非另有说明。
现在,让我们回到理解选区解析上来!
选区分析器为每个句子创建一个分析树。让我们看一个例子。
句子 : “玫瑰是一种花”
语法:
- S → NP VP
- NP → NNP
- 副总裁→ VBZ NP
- NP → DT NN
词汇:
- NNP →玫瑰,百合,兰花
- VBZ →是,是
- DT → a,the,an
- NN →花
这个句子的解析树如下所示。
您可以在上面的图片中看到,解析树包含所有单词作为叶或终端,S 作为根。现在,主要有两种解析方式: 自下而上和 。在 自下而上解析 中,解析器首先在内部执行词性标注,开始确定每个单词的句法角色。然后,它使用预先确定的语法来建立两个单词之间的语法关系,并递归地组合短语,直到到达词根:S。然而,在 自上而下解析 中,解析器从 S 开始,并使用语法规则来解开每个短语,直到到达句子中的所有单词。
请记住,我们在这里讨论的是一棵 有效解析树 和一棵 有效解析树 总是包含 S 作为根,并且只包含给定句子的单词作为叶。例如,如果你的解析树包含的单词比你的句子中出现的单词多,那么它就是一个无效的解析树。
一个 有效解析树 总是包含 S 作为根,并且只包含给定句子的单词作为叶。
另一个需要记住的重要事情是,一个句子可以有多个解析树,但是其中只有一个在语法和语义上都有效。让我们看一个例子:
句子:我拍了一头穿着睡衣的*象。
你的预定语法和词汇或词汇如下:
L1 微型英语语法和词汇
现在,您的句子可以有两个可能的解析树,如下所示。
这两棵树看起来在语法上是有效的,但在语义上并不都是有效的。评估语义有效性时要记住的重要事情是看短语是如何组合的。在左边的树上,由于名词短语“穿着睡衣的*象”与“shot”相连,听起来作者想说他或她射杀了一头穿着睡衣的*象。然而,如果你仔细观察右边的解析树,动词短语(VP)“射杀一头*象”与介词短语(PP)“穿着我的睡衣”组合在一起,这传达了作者穿着他或她的睡衣射杀一头*象的意思。因此,句子的正确含义仅由右边的解析树传达,并且只有该解析树才是正确的解析树。
在 NLP 文献中,这个问题被称为结构歧义问题,其中一个句子可以有多个解析树,但其中只有一个是准确的。
现在,让我们看看如何使用 python 来实现这一点。我使用 nltk 的 CoreNLP 解析器,它的后端有斯坦福解析器。请注意,为了让这段代码工作,你必须在你的本地计算机上下载并启动斯坦福 NLP 服务器。
选区分析说明
您一定已经注意到,我只讨论了单独利用语法和词典来形成解析树的解析技术。使用其他可能给我们带来更好结果的方法怎么样?使用自底向上或自顶向下解析的动态编程的选区解析的两个例子是 CKY 解析和厄尔利解析。
CKY 语法分析器使用上下文无关语法和自下而上的动态编程方法为任何给定的句子构建一个语法分析树。
而 Earley Parser 使用任意上下文无关语法和动态编程自顶向下的方法为任何给定的句子构建解析树。
现在,你一定想知道为什么不把概率的概念引入我们的方法中。我们都知道,一些语法规则比其他规则出现的频率更高,因此,与它们的对应规则相比,我们可以给它们更高的概率,从而形成一个概率语法,然后可以用来执行解析。现在,我们可以简单地通过乘以用于构建所述树的所有规则的概率来计算最终解析树的概率,并且可以选择具有最高概率的解析树作为给定句子的精确解析结构。
概率解析 使用出现概率较高的语法规则进行解析,找到最可能的解析树。
这个想法仍然有它的缺陷。这种解析对它展开的词的种类不敏感。
例如:
第一句:我吃了健怡可乐炸鸡。
第二句:和朋友吃了炸鸡。
在第一句中,“用健怡可乐”是 NP(名词短语),由另一个 NP:“健怡可乐”和 PP(介词短语):“用”。换句话说:
规则 1 : NP → NP PP
规则 1 的概率: 0.5。(让我们假设)
在第二句中,“with friends”是 VP(动词短语),由另一个 NP:“friends”和 PP(介词短语):“with”。换句话说:
规则二: VP → VP PP (0.5)
规则 2 的概率: 0.5(假设)
现在,想象你是一台计算机,你正试图找到哪条规则来解析你的句子。因为两个规则的概率是相等的,所以您可以选择其中任何一个并得到一个语法上有效的解析树。但是,第一句和第二句的意思不一样。第一句暗示你吃了炸鸡和健怡可乐,而第二句暗示你和你的朋友吃了炸鸡。因此,概率分析器仍然缺乏区分两个规则的概率是否相等的能力,从而使它对单词的词汇信息不敏感。
因此,下一步是使用一个考虑了概率解析器忽略的词汇信息的解析器。答案是使用词汇化的概率分析器。
词汇化概率解析器使用关于单词的词汇信息(例如,使用关于每个短语的头部的信息)以及规则出现的概率来解析句子。
还有其他种类的解析器,它们利用自然语言的不同方面来解析一个句子,但是在本文中,我只关注最流行的几种。本文中使用的 python 代码可以在这里找到。
我希望这篇文章能帮助您更好地理解解析!
感谢您的阅读!
附言:如果你想让我写一些特定的话题,请在下面评论!谢谢*家!😃
参考文献:
不确定性感知强化学*
窥探知道自己在做什么的建筑代理
基于模型的强化学*(RL)最受样本效率的青睐。它对期望的输入量要求不高,并且对我们期望模型实现的目标有一个上限。
这个模型不太可能完美地再现环境。当通过训练有素的代理与现实世界互动时,我们可能会遇到与训练中看到的不同的状态和奖励。为了让基于模型的 RL 发挥作用,我们需要克服这个问题。这至关重要。这有助于我们的特工知道自己在做什么。
第一,无模型 RL 怎么样?无模型 RL 总是在训练和测试代理时使用地面实况环境转换。除非有我们引入的偏移,比如模拟到真实的转移,在这种情况下,我们不能责怪算法。因此,不确定性不是一个*问题。对于像 Q 函数 Q(s,a)这样优化动作的东西,我们可以尝试在动作选择上集成确定性意识。但是既然它工作得很好,就目前而言,闭上我们的眼睛,假装我们没有看到,这并没有什么坏处。
内容
1.基于模型的 RL
2 的不确定性来源。不确定性意识的好处
3。构建不确定性感知模型
-什么看起来可行
-什么可行
4。结论
不确定性的来源
模型不确定性是由模型在测试期间看到的数据和用于训练模型的数据之间的分布不匹配造成的。我们在不同于培训期间看到的分布上测试代理。
确切地说,不确定性意识会产生什么样的影响?
在训练开始时,模型p(sₜ₊₁|sₜ、 a 、ₜ已经暴露了自己相当小的真实世界数据。我们希望函数不要过度适合这个小数量,因为我们需要它足够有表现力来捕捉后面时间步骤中的转换。然后,将积累真实数据来学*精确的模型。
这在基于模型的 RL 中很难实现。为什么?RL 的简单目标是最*化未来的累积回报。计划者以此为目标,试图遵循模型预测高回报的计划。因此,如果模型高估了特定行动序列将获得的回报,规划者将很高兴遵循这一闪闪发光但错误的估计。在现实世界中选择这样的行为会导致滑稽的行为。简而言之,计划者有动机利用模型的积极错误。
(我们可以把计划器看作是我们在给定世界状态的情况下选择最佳行动的方法)。
这还能更糟吗?在高维空间中——比如输入是一幅图像——由于潜在变量,模型会犯更多的错误。在基于模型的 RL 中,通常通过使用策略上的数据收集来缓解分布不匹配,在现实世界中观察到的转换被添加到训练数据中,并用于重新规划和纠正模型中的偏差。然而,在这种情况下,错误太多,政策上的修正无法赶上失去的模式。*量的错误可能会导致我们每次重新计划时都改变策略,结果,模型可能永远不会收敛。
我们可以选择为模型可能犯的每一个错误收集数据,但如果我们能够检测到模型可能出错的地方,那么基于模型的规划者就可以避免可能导致严重后果的行动,这不是更好吗?
估计不确定性
首先,让我们用一个简单的故事来表达我们所知道的。
一对恩爱的夫妇得到了一个婴儿和一个机器人的祝福——不一定同时得到。作为一名保姆,机器人的目标是让小朱丽叶开心。虽然实现这一目标受到奖励的激励,但机器人避免任何损坏或可能伤害婴儿的事情也是可取的。
现在,婴儿变得喜欢在指着虫子的时候哭泣——因为好孩子会这样做——机器人的最佳奖励计划变成压扁虫子,让婴儿朱丽叶看着它把害虫喂给猫。
不过,为了改变,假设机器人在电视上指着一些可怕的东西时遇到了婴儿哭泣——这是一种不熟悉的状态,似乎接近婴儿朱丽叶的哭泣指行为。不确定动态,机器人的最佳计划可能是压扁电视,喂猫。我们不确定这是否会让宝宝开心,但这肯定会造成伤害。
然而,如果模特因为不自信而回避了这个动作,那么最好不要碰电视,以一个悲伤的朱丽叶为代价来避免伤害。
一个不确定性感知模型可以让代理人知道哪里出现不良结果的可能性高,哪里需要更加小心。但是如果模型对采取行动后的结果没有信心,那么利用它来达到目标可能是好的。
如果我们的机器人确信泡菜能让朱丽叶平静下来,而不会带来任何风险,那么它可能会考虑跑到厨房,让她咀嚼一个,因为这样一来,它就能达到让她开心的目的。
一个能够获得其不确定性的精确估计的模型,给予基于模型的计划者避免有不小可能导致不希望的结果的行动的能力。渐渐地,模型将学会做出更好的估计。不确定性意识也将告知模型它需要探索更多的状态。
有什么解决方案吗?
利用熵
我们知道熵是随机性的一种度量,或者随机变量的概率分布的扩散程度。
离散随机变量的熵 X
当每一个结果(【xᵢ】)以相等的概率发生时,熵将达到峰值,即最*不确定性,并且当存在具有高概率 p (几乎为 1)的单个结果时,熵将最小,而其余结果共享接近于零的概率( 1 — p ),即最*确定性。
这里,A 比 b 具有更低的不确定性,对于 A 来说 单峰 高斯分布 ,熵和方差(围绕均值扩散)倾向于在相同的相对方向上移动。
然而,问题就在这里——数据的不确定性会影响我们模型的不确定性吗?为了更好地表达这个问题,这里有两个图表:
你将会看到手工制作的数据的可视化图表。把注意力放在我们要用它们传达的概念上,而不是它们代表什么,好吗?谢了。
左 : 模型预测为线性函数——模型拟合数据,但是“太好了”。————————右 :拟合多项式回归函数*
这两个情节有助于我们区分两个相关的概念。在它们之间,你认为哪一个是不确定的?抓住一个轻松的时刻再次观察它们,并尝试想出一个答案。
你选好了吗?让我给你一些思考的空间。
在第一个图中,模型对几千个样本进行了线性回归(LR)。数据似乎是确定的。过度拟合显示确定性,对吗?当然,模型对数据很有信心。红线预测的方差将非常接近于零。但是,这个估计是否给出了关于观察结果的最佳解释呢?
为此,模型需要通过在数据和可能的协变量之间建立良好的线性关系来解释观察结果。但是不确定包括哪些和省略哪些,它包括了(几乎)所有的东西!这影响了模型的不确定性,因为我们对模型没有信心。
在多项式回归(PR)中,数据中存在噪声。它在观测值中产生了不确定性。样本显示了与平均值的显著偏差,但模型相对来说拟合得很好——我们仍然可以在没有提供牛奶的情况下从心理学课上获得一些笑声,这似乎足够真实。与通过过度拟合线性函数接收的误差相比,这将给出更*的 MSE。
所以回到我们最初的问题,数据中的不确定性不会揭示我们的模型-不确定性。这使得熵,作为模型不确定性的度量,并不总是有效的,因为当数据的方差接近零时,即使模型仍然不确定,熵也是低的,如 LR 图所示。
不确定性感知模型——有效的解决方案
a)学*预测不良行为的函数
考虑无人机在雨林中学*飞行。我们希望它能学会在环境中导航,同时避免与树木相撞。在 RL 中,代理通过尝试一个动作来学*该动作的结果。所以要学会躲避树,它必须经历几次撞击。但是高速撞击肯定会造成破坏。
我们可以通过让无人机经历温和、低速的撞击来训练它,这样它就能学*森林环境。当它遇到训练分布中不存在的森林部分时,它需要了解其策略的不确定性,以便在收集新的训练数据时能够与该部分进行安全交互。一旦对该部分有信心,它就可以在未来高速飞行。这是一个安全探险的例子。
为了实现这一点,我们在 RL 成本函数 c(st,at) 中集成了击中树的成本,以使 c(st,at) + C_bad。 C_bad 是分配给导致不良行为(冲突)的行为的新成本。它影响着无人机何时可以快速飞行,何时应该小心翼翼。
为了估计 C_bad,我们使用一个不良行为预测神经网络 P ,带权重θ。它将无人机的当前状态 st 作为输入,它是观测值oₜt19】加上一系列动作aₜ,aₜ₊₁…ah无人机计划执行 e 并估计碰撞发生的概率。**
动作序列由模型预测控制 (MPC)在从当前时间步长 t 直到 t + H 的后退时间范围内选择和优化。不良行为模型P**θ输出一个伯努利分布(二进制 0 或 1),指示在该时间范围内是否发生碰撞。**
为每个水平 h 记录碰撞标签。这意味着对于标签 1,在时间步长 t 和t+h之间的子序列中发生了不良行为。利用以上述输入为条件的这个概率标签,不良行为模型可以简单地表示为:
使用 P_theta 模型估计不良行为 C_bad 的成本
类似地,一个简单的实现应该是这样的:
然而,你可能已经注意到Pθ输出的是不良行为的概率分布,而不是该行为的实际费用。因此,实际的不良行为成本将乘以这个概率 p 得到 pC_bad 。最后,我们用一个标量 λ 来调整它,这个标量决定了与实现目标相比,代理避免风险结果的重要性。**
RL 成本由目标成本和代理的不良行为加权成本构成****
值得注意的是,虽然我们想要一个预测不安全行为的函数,但一个接受输入并给出安全估计的判别模型可能并不总是让我们高兴——它的预测在不熟悉的状态下可能毫无意义。最好是,在其预测中加入模型不确定性是有益的。
b)贝叶斯网络
神经网络可以被称为条件模型 P(y|x,w),,其给定输入 x,使用权重 w,将概率分配给每个可能的输出 y 。对于贝叶斯神经网络,不是每个神经元具有单个输出,而是将权重表示为可能值上的概率分布。
右图:贝叶斯神经网络。代替每个权重的单个固定标量,概率分布被分配给所有可能的值( 源 )
这是如何工作的?
使用一组训练样本 D ,我们在模型权重上找到一个后验,以这些样本 P ( w |D)为条件。为了预测特定标签或的分布,由后验分布缩放的权重的每个可行组合对相同的输入 x 进行预测。
如果一个单位对观测值不确定,这将在输出中表示出来,因为不确定性越高的权重在预测中引入的可变性越*。这在模型发现极少数据或没有数据的地区很常见,这将鼓励勘探。随着观察越来越多,模型做出的决策也越来越确定。
对权重P(w| D)的后验分布进行近似。这是通过试图找到权重 q(w|θ)上不同分布的参数θ来完成的,通过使其尽可能接近真实的后验分布P(w|| D)。这是变分推论;有点超出我们目前的范围:)。
c)辍学
在 RL 辍学是一个坏主意。不过,这不是我们要冒的风险。还记得我们说过,一个有区别的模型不会总是让我们快乐,除非它能在不良行为预测中包含不确定性?辍学是一个简单的方法。
丢弃是一种正则化技术,它以概率 p、随机丢弃神经网络中的一个单元,或者以概率 1 — p 保留它。它经常在训练中使用,以防止神经元过度依赖彼此。这在每次训练迭代期间创建了新的但相关的神经网络。
在实践中,已知仅在训练期间应用漏失,并在测试时移除,以实现高测试精度。然而,通过保留测试时的漏失,我们可以通过寻找不同前向传递的样本均值和方差来估计不确定性。这是估算不确定性的简单方法。
它的警告是,作为一种变分推理方法,由于变分下限而严重低估了不确定性。
那是什么意思?
为了理解这一点,我们需要引入 KL 散度——一种对同一随机变量的两个概率分布之间差异的度量。
有时,在*的实值分布上寻找真实概率是昂贵的。因此,取而代之的是使用该分布的近似值,并且两者之间的 KL 散度(差)被最小化。
p(x)与 q(x)之间 KL 发散的一个例证——DKL(p | | q)(出处 )
在上图中, q(x) 是精确分布 p(x) 的近似值。这种近似的目的是在 p(x) 具有高概率的情况下放置高发生概率。在插图上,注意 q(x) 是单高斯,而 p(x) 是两个高斯的混合?为了在 p(x) 的概率高的地方放置高概率, q(x) 使 p 中的两个高斯分布相等,以在两者上相等地放置高概率质量。
类似地,在输入 x 和标签 y 的条件下,漏失在模型的权重 w 上具有真实的后验概率 p(w| x,y)。q(w)被用作该后验概率的近似分布。然后我们降低 q(w) 和实际后验概率 p(w| x,y) 之间的 KL 散度,使它们尽可能接近。然而,这样做将对 q(w) 在 p(w) 没有概率质量的情况下放置概率质量进行惩罚,但是忽略 q(w) 在 p(w) 实际上具有高概率的情况下没有放置高概率质量。这就是低估模型不确定性的原因。
c)引导程序集合
训练多个独立的模型,并对它们的预测进行平均。如果这些模型近似一个几乎相似的输出,这将表明他们同意,表明他们的预测的确定性。
平均 N 个集合模型
为了使模型相互独立,每个模型的权重θᵢ用数据的子集训练,数据用来自训练集的替换采样。然而,已知训练期间权重的随机初始化θᵢ和随机梯度下降使它们足够独立。
作为不确定性的一种度量,漏失可以被假定为集合方法的廉价近似,其中每个采样漏失充当不同的模型。贝叶斯神经网络也有集成概念——通过对权重(w| D)取后验分布下的期望,贝叶斯网络变得相当于无限个集成——越多意味着越好。
d)好奇 iLQR
将好奇心视为解决代理环境中不确定性的灵感。让我们看看如何将好奇行为添加到代理的控制循环中。
一些 LQR 的背景
在 RL 中,线性二次型调节器(LQR)输出一个用于开发模型的线性控制器。当处理非线性动力学时,我们使用线性回归在每个时间步拟合模型(sₜ₊₁|sₜ,aₜ)。这种迭代控制过程被称为迭代 LQR (iLQR),是差分动态规划(DDP)的一种形式。
系统动力学由以下等式表示:
系统动态学
f 代表学*到的动力学模型而 xₜ ₊₁ 是下一时间步的状态,表示为当前状态加上模型对当前状态的预测变化 x ₜ 当采取动作 u ₜ 时。例如,如果状态是机器人的速度, x ₜ 将是当前速度,而 f(x ₜ ,uₜ)δt将是选择 u ₜ 时的预测变化,从而产生新的速度****
让人好奇
对于上述系统动力学中不确定性的积分,它被写成一个高斯分布,由一个均值向量μ和一个协方差矩阵σ表示。
高斯策略具有将状态和动作对映射到状态平均变化的神经网络。这种状态的变化就是模型μ(f)表示的均值向量。**
我们可以通过从正态分布绘制模型 f 来将系统动力学实现为高斯过程(GP),其中我们试图学*使成本函数最小化的最佳均值向量 μ 。GP 随后使用以下等式进行预测:
作为高斯过程的系统动力学
其中 f(x ₜ ,u ₜ ) 为可训练动力学函数表示的均值向量,σₜ₊₁为当前状态和动作下 GP 预测的协方差矩阵。****
这个 GP 等同于一个普通的非好奇的 LQR 随机动力学方程。有什么不同?在非好奇的 iLQR 中,由于高斯的对称性,我们将忽略方差参数σₜ₊₁。然而,好奇 iLQR 需要预测分布的协方差来确定模型不确定性。高模型不确定性等于高方差。σₜ₊₁代表当前状态和动作下模型对预测 xₜ ₊₁ 的不确定性( x ₜ ,u ₜ).
来自 GP 模型的这种不确定性随后被用于恳求代理采取行动来解决模型在这种状态下的未来不确定性。简而言之,鼓励代理人选择减少模型方差的行动。这是通过奖励代理人包含一定程度的不确定性的行为,同时仍然最*化特定目标的奖励来实现的。
山地车实验:目标是到达山顶。由 开始寻求负回报 (向后移动到对面的山上)汽车获得足够的动力到达顶部。看到只有好奇的特工才知道 探究 这个。( 来源 )
理解 基于模型的 RL 中的 LQR 优化 可能有点杂耍,但对于掌握好奇号算法是如何导出的是必不可少的。不过,我们现在不要用这些方程来吓唬自己。
奖励好奇的行为使代理比使用标准的 iLQR 更快地达到目标。防止模型陷入局部最优,在短时间内找到更好的解。
结论
不确定性意识可用于影响代理的学*策略,这取决于它如何添加到培训成本中。它可以鼓励探索或悲观行为,在这种情况下,代理人避免可能有风险的结果。后者是 AI 安全感兴趣的。为了更加稳健,增加不确定性意识的解决方案可以一起使用。
参考文献
[1] A. Nagabandi,G. Kahn,S. Fearing,S. Levine,无模型微调的基于模型的深度强化学*的神经网络动力学 (2018),ICRA 2018。
[2] S. Daftry,S. Zeng,J. A. Bagnell,M. Hebert,内省知觉:学*预测视觉系统中的故障 (2016),IROS,2016。
[3] C. Blundell,J. Cornebise,K. Kavukcuoglu,D. Wierstra,神经网络中的权重不确定性 (2015),ICML 2015。
Y. Gal 和 Z. Ghahramani。作为贝叶斯近似的辍学:表示深度学*中的模型不确定性。ICML,2016。
[5]李彦宏和李彦宏。具有α发散度的贝叶斯神经网络中的脱落推理,2017。
[6]基于模型的强化学*,深度 RL 决策与控制 (2019), Berkley 。
[7]卡恩、维拉弗洛、庞和莱文。碰撞避免的不确定性感知强化学*,2017。
[8] S .贝克特尔,a .拉伊,y .林,l .里盖蒂,f .迈耶。好奇 iLQR :解决基于模型的 RL 中的不确定性。ICML,2019。
插补的不确定性
你在预测中考虑到它了吗?
几周前,我用 DataCamp 开发的处理 R 中缺失数据的课程上线了。从目前为止我从学生那里得到的反馈来看,他们从课程中受益最*的部分是关于如何将插补的不确定性整合到建模中。在本文中,我将在使用机器学*模型进行预测的背景下简要讨论它。
为了演示这个主题,我们将分析来自fivethirtyeight
R 包的活检数据集。我们将使用原始数据的一个小的经过处理的版本。让我们来看看吧。
传记片数据包含一些传记电影的信息。这些特征包括电影的生产国、发行年份、票房收入记录、电影中出现的主题数量、主题类型或认可原因以及它们的性别。这里的任务是根据剩余的特征预测对数收益。我们正在考虑拍摄我们自己的电影。这部电影将于明年在美国上映,讲述一个单一角色的故事:一名女运动员。我们想知道我们可能挣多少钱。
不幸的是,数据集并不完整:在每个特性列中都有一些缺失值。
一种方法是以某种方式估算(即填充)缺失的数据,一旦数据完整,就将其输入模型。这其实是*部分人都会做的,那我们也来做吧。我们将使用VIM
包中的热卡插补对数据进行插补,并使用ranger
包训练一个随机森林。如果你对 hot-deck 的工作原理、其他可用的插补方法以及如何调整这些方法使其更好地工作感兴趣,请随时查看本文底部链接的课程。
0.01210784
我们得到了 0.01210784 的单一预测。一旦我们反转对数变换,我们将会看到我们的电影有望获得 exp(0.01210784)≈100 万美元。我们是百万富翁!但是我们能有多确定呢?
上述预测完全忽略了随机森林模型适合估算数据的事实。
值得注意的是,估算值并不是一成不变的。它们只是估计值,而估计值带有不确定性。
用统计学术语来表达就是:估算值是根据观测值估算出来的,后者是从某个人群中抽取出来的;因此,存在双重不确定性:与数据采样相关的不确定性和来自插补方法的不确定性。根据这样一个不确定的预测采取行动是不明智的!
虽然没有任何方法可以神奇地将统计学中的不确定性转化为确定性,但有一些方法可以捕捉这种不确定性,并在根据模型的预测采取行动时将其考虑在内。
在我们的例子中,一种方法是自举。我们需要做的是从原始的、不完整的数据中抽取一个 bootstrap 样本。这意味着我们正在对替换的数据行进行采样。然后,我们对 bootstrap 样本进行估算,对估算的数据建立模型,最后进行预测。我们重复这个过程很多次,比如说 1000 次,随机抽取不同的样本。因此,我们获得了我们计划的电影的预测收入分布,这种分布捕捉了插补的不确定性。
由于有了boot
包,这在 R 中很容易实现。我们只需要定义一个接收原始数据和要采样的行的索引作为输入的函数。然后,该函数应该选择这些行,估算 bootstrap 样本,训练模型,并返回预测值。剩下要做的就是将这个自定义函数传递给boot
函数。
该输出告诉我们,与自举分布的平均值相比,我们最初的单个预测值 0.01210784 有些偏差,并且该分布具有相当*的标准误差,这意味着相当*的不确定性。
现在让我们来计算我们预测的 95%置信区间。
因此,我们有 95%的把握我们的电影将获得-1.02 到 1.18 之间的收入。把它转换回美元,我们就有了$362𝑘和$3.2𝑚.的范围相当*的差距。让我们将预测的分布、单个预测和置信区间都绘制在一个图表中。
0.01 处的橙色线表示我们根据仅估算一次的数据训练单个模型得到的预测。这一枪连最有可能的结果都没给我们!这是由于从可视化中变得清晰的偏差:最有可能的收益位于分布的峰值,比我们最初的预测稍微偏右。然后:你看分布多广!
拍摄这部电影可能仍然是值得的,但取决于你的风险厌恶,你可能会想采取预防措施,以防它没有回报,这不是不可能的。
感谢阅读!我希望你已经学到了对你的项目有益的东西🚀
你可以找到这篇文章的数据集和所有代码,包括绘图,在这里。如果您有兴趣了解更多关于缺失数据、各种插补方法以及其他将插补不确定性纳入建模的方法,请查看我的课程。
如果你喜欢这篇文章,试试我的其他文章。不能选择?从这些中选择一个:
揭秘著名的竞赛获奖算法。
towardsdatascience.com](/boost-your-grasp-on-boosting-acf239694b1) [## 线性分类器:综述
本文讨论了四个流行的线性函数的数学性质和 Python 的实际应用
towardsdatascience.com](/linear-classifiers-an-overview-e121135bd3bb) [## 用有限混合模型和 EM 算法估计经济状态
towardsdatascience.com](/estimating-the-state-of-the-economy-with-finite-mixture-models-and-the-em-algorithm-d975d280dbc6)
用贝叶斯推理评估预测的不确定性
计算贝叶斯统计导论。
Maksym Kaharlytskyi 在 Unsplash 上的照片
介绍
频数统计 有几个局限性,比如在预测中缺少(我们通常只对期望建模),没有内置的 正则化 ,或者没有包含先验知识。一个可以从过去的信息中获得,例如以前的实验,但是也可以从一个训练有素的 主题专家 的纯粹主观的评估中产生。****
贝叶斯统计 是统计学领域的一种替代理论,基于概率的贝叶斯解释,其中概率表示关于事件的信念或信息(知识)的程度。这与频率主义者的解释不同,后者认为概率是经过多次试验后某一事件相对频率的极限。许多贝叶斯方法需要*量的计算来完成,这就是为什么在上个世纪广泛使用的*多数方法都是基于频率主义者的解释。然而,随着功能强*的计算机和新算法的出现,贝叶斯方法在 21 世纪的统计中得到了越来越多的应用。
贝叶斯定理
贝叶斯统计围绕着 贝叶斯定理 的用法,以【托马斯·贝叶斯】命名,以及 和 概率的乘积规则 p(y) 在获得更多证据 p(x|y )后,产生一个 后验概率分布 p(y|x )。
方程式 1。
等式 1 中分布 p(x,y) = p(x|y)p(y) 的变量 Y 的所有可能配置的总和导致 X 的 边际分布 ,即p(X);这种计算叫做边缘化。在连续变量的情况下,等式 1 的分母中的和变成了通常难以处理的积分。此外, p(x) 是归一化常数,其缩放后验分布,使得其在整个空间上的积分再次变为 1——这是 概率分布 的属性之一。(但是,如果省略归一化,等式 1 就变成了 比例 。)**
原则上,任何参数模型(线性回归、逻辑回归,甚至是神经网络)都可以通过陈述参数 β 的先验来公式化为相应的贝叶斯版本。使用训练数据( X , y )和贝叶斯定理,参数的后验概率分布计算如下
方程式 2。
一般来说,通过分析得出后验分布是不可能/不切实际的。但是,通过使用专用算法可以获得数值结果,这将在下面的章节中解释。
蒙特卡罗方法
首先需要介绍几个关键概念,一个是 伪随机数生成 。随机数通过采样 算法产生,例如拒绝采样。这些算法的目的是生成作为一个集合遵循某种特定分布(例如高斯分布)的数。然而,拒绝采样依赖于一个更简单的构建模块,该模块在 0 和 1(U(0,1))之间的区间内生成均匀分布的随机数,例如 线性同余生成器 。拒绝抽样适用于维数很少的非常简单的分布;在高维中,概率质量集中在很小的体积内,导致*量样本被拒绝(效率低下)。对于这样的场景,已经开发了更先进的算法;然而,它们仍然利用更简单的采样算法,例如上面提到的那些。
另一个重要概念是 随机数值积分 利用 蒙特卡罗方法 (或称蒙特卡罗实验)。蒙特卡罗方法是一种广泛的计算算法,它依靠随机数的生成来获得数值结果。例如,通过从期望的分布中生成随机样本,等式 2 的分母中的积分变成了正则期望(平均值的最*似然估计),其精度与抽取的样本数成比例。
一类被称为 马尔可夫链蒙特卡罗 的采样算法已经被开发出来,用于从复杂的概率分布中抽取样本。一个 马尔可夫链 是一个可能事件(结果)的序列,其中每个事件的概率只取决于前一个事件达到的状态。它是满足 马尔可夫性质 (马尔可夫过程)的随机过程的结果,即过程未来状态的条件概率分布仅取决于最当前的状态,而不取决于之前的事件序列。在马尔可夫链蒙特卡罗算法类中,有几种变体,例如,【Gibbs 采样】 或Metropolis-Hastings;早期的变体之一,Metropolis 算法,将在下一节简要描述,因为它非常容易理解。
贝叶斯计算的目标是从后验分布中获得一组独立的绘图,以合理的精度估计感兴趣的量。更准确地说,这种随机抽取用于总结模型参数的后验分布,例如,通过报告抽样分布的 2.5%、25%、50%、75%和 97.5%的点。然而,绘图也可以用散点图或直方图来可视化。
最后,一旦模拟了模型参数的后验分布,就可以通过忽略条件后验分布中的参数来进行新样本的预测。等式 3 本质上是另一个积分,可以使用数值方法通过随机采样来计算。
方程式 3。
*都会算法
如上所述,从多维(可能不是归一化的)概率分布 p(z) 中抽取样本具有挑战性。采样算法的思想是从更简单的建议分布中反复抽取样本,例如,一维高斯分布的乘积。一种这样的蒙特卡罗方法被称为 Metropolis 算法 ,其生成马尔可夫链并最终收敛到期望的分布 p(z) 。更精确地说,随机数是使用建议分布生成的,新样本是被接受还是被丢弃,取决于它们是比前一个样本更有可能还是更不可能——并且仅仅是前一个样本(因此具有马尔可夫特性)。在实践中,由于计算能力增加,并行模拟几个链以检查 收敛 和 混合 。
图 1: Metropolis 算法。(来源:作者)
具有低方差的建议分布导致小的步长和高度相关的样本;相反,高方差导致较*的步长,但接受率较低,因此许多样本将被拒绝,从而减慢算法。由于这个原因,已经开发了解决这个问题的更高级的算法。关于这个话题的更多理论,可以推荐 Andrew Gelman 的书《贝叶斯数据分析》,这本书有免费的 PDF 文件。
这是本书的 pdf 格式,可以下载用于非商业目的。
www.stat.columbia.edu](http://www.stat.columbia.edu/~gelman/book/)
贝叶斯逻辑回归
让我们看一个使用葡萄酒数据集的具体例子。逻辑回归是一种对变量对二元类标签( y =0, y =1)的影响进行建模的算法,即解决监督分类问题。后验概率分布给出了模型的每个权重或输出的区间估计。在数据分析中,不仅要提供一个好的模型,还要提供结论的不确定性估计,这一点至关重要。
我们收集了一批酒瓶;我们希望为一种特定类型的葡萄酒开发一个分类模型,这种葡萄酒由于价格高而经常被模仿。此外,只有当我们(不)足够确信这是(不是)模仿时,我们才会采取行动。为了表征一款酒,我们用它的化学成分(由 高效液相色谱 测定)相对于 黄酮类脯氨酸 。
图 2: 不同葡萄酒的小提琴图和散点图(深色部分)。(来源:作者)
使用 Python 库 PyMC 通过采样开发和推断后验概率分布 p(y=1|x,β) 的线性模型。从上图可以看出,线性决策边界可能就足够了。此外,由于还没有任何先验知识,我们必须设置 无信息先验 ,这相当于逻辑回归的一个 集合 。请注意,变量在分析前没有标准化,因此参数的*小没有任何意义。
使用葡萄酒数据集的 PyMC 示例。
下面的图中给出了产生的链,显示了收敛和混合,但应评估相关的统计数据。请注意,每条链的样本总量(通常总共四个)是 3000,但是每条链的前三分之一被丢弃。
图 3: 马尔可夫链的推理程序;trace 显示了良好的混合和收敛(至少在视觉上)。(来源:作者)
为了评估采样算法的成功,开发了几个统计数据。下表是推理过程的总结。
最重要的统计数据是 R-hat ,它是一种诊断方法,通过比较多个链之间的方差和每个链内的方差来测试缺乏收敛。如果收敛已经实现,链间和链内的差异应该是相同的。为了最有效地检测不收敛的证据,每个链应该已经被初始化为相对于目标分布分散的起始值。在这种情况下, R-hat 为 1,因此实现了收敛和混合。
另一个统计是有效样本*小,它基本上是由序列内的的量校正的样本总量(样本乘以链数),即总量除以校正因子。如果这个指标(太)低,可能需要更多的样本。
表 1: 模型和推理总结。如指标所示,链已经收敛。
让我们来看看我们的估计参数。94%贝叶斯可信区间不包括零,因此我们似乎有信心两个变量都与模型相关。
图 4: 估计参数的联合分布。(来源:作者)
让我们看看变量的部分相关性。两者都是随着值的增加而增加概率。然而,由于线的分散性较*,黄酮类化合物的含量有更多的不确定性。
****
图 5: 通过不透明度和带宽可视化预测的不确定性。与脯氨酸相比,黄酮类化合物的分类包含更多的不确定性。(来源:作者)
现在,我们有两瓶新酒。我们有多确定这是(或者不是)我们要找的酒?让我们做一个预测,评估后验分布。
****
图 6: 两个新样本后验概率的带宽预测不确定性。(来源:作者)
第一瓶 94%可信区间[0.90,1.00],第二瓶[0.50,1.00]。对于第二个,我们不太有信心,事实上,模型在推断过程中从未见过这样的瓶子。
变分贝叶斯
值得一提的是,还有另一种解决整个问题的方法,称为 变分贝叶斯 ,其中后验概率用一些更简单的参数分布来近似,即所谓的变分分布,由此可以直接计算积分。该分布通常被公式化为单个单变量概率分布(平均场近似)的乘积,并且其参数通过最小化真实后验分布和变分分布之间的 Kullback-Leibler 散度 来找到。变分法精度较低,但速度更快,可以作为基于模拟的方法的起点。
摘要
贝叶斯统计是一个强*的框架,它解决了频繁主义方法不能解决的问题,如预测中的不确定性。这种不确定性是由于训练数据不足或测试数据中的异常样本造成的。此外,它还是质量控制的一种形式,通过 PyMC3 和 TensorFlow Probability 等库,这些方法已经变得人人都可以使用。
概率模型不确定性预测的逐步指南。
towardsdatascience.com](/bayesian-neural-networks-with-tensorflow-probability-fbce27d6ef6)******
不相关与独立随机变量——定义、证明和例子
它们不是一回事…
莎伦·麦卡琴在 Unsplash 上的照片
关于技术知识,我通常支持对正在使用的方法有基础的理解。我通常不喜欢记忆任何东西,并尽可能避免这样做。相反,我专注于发展强有力的概念基础,从这些基础中我可以用数学方法推导出我可能需要的任何东西。
在概率论和数理统计领域,利用方法/定理通常依赖于通用的数学假设和约束条件。两个这样的数学概念是随机变量(RVs)是“不相关的”,以及 RVs 是“独立的”。我已经看到了很多关于这些概念的混乱(包括在媒体平台上)。这些是数学上明确规定的术语,它们并不意味着同一件事。对于任何对统计学、数据科学或机器学*感兴趣的人来说,这些概念绝对值得理解。
本文旨在:
- 从数学上规定 RVs 不相关和 RVs 独立的定义
- 证明独立的 RVs 根据定义也是不相关的
- 证明 RVs 可以不相关但不独立(举例)
1.数学定义:
出于演示的目的,让我们假设我们有连续的 RVs X 和 Y ,它们都是在一些实值支持上定义的。
不相关的 RVs:
两个 RVs X 和 Y 是不相关的,如果它们的联合分布的期望值等于它们各自的边际分布的期望值的乘积。从数学上来说,这是:
这相当于说明 X 和 Y 之间的协方差为零:
独立的房车 :
两个 RVs X 和 Y 是独立的,如果它们的联合分布值等于它们各自的边际分布值的乘积,对于沿着它们各自的支撑的 X 和 Y 的任何可能范围。从数学上来说,这是:
这相当于陈述了 X 和 Y (互为条件)的条件分布等价于它们各自的边际分布。换句话说,拥有关于 X 的信息不会提供关于 Y 的额外信息,反之亦然:
2.根据定义,独立的 RVs 是不相关的:
如上所述,根据定义,独立的 RVs 也是不相关的。让我们使用我们的示例 RVs X 和 Y 以及上面的数学定义来证明这一点:
3.RVs 可以是不相关的,但不是独立的:
如上所述,RVs 可以是不相关的,但不是独立的。下面是一个经典的玩具例子来说明:
附录:标准正态分布 RV 的奇矩为零的证明
在第 3 节展示 RVs 不相关的例子时,我利用了标准正态分布的一个特殊属性(奇数阶矩都为零)。对于那些感兴趣的人来说,这个属性的证明如下所示:
最后的想法
希望以上推导和举例有见地。我认为没有足够的人花时间去做这些类型的练*。上述概念的知识对于理解高斯-马尔可夫定理、恢复有效方差估计量的条件以及该领域的更多概念至关重要。
希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练*。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。我打算在未来写一些基础作品,所以请随时在 LinkedIn 上与我联系,并在 上关注我在 上的更新!
用统计模型揭示政府偏见
照片由 Unsplash 上的 Aditya Joshi 拍摄
对澳*利亚“体育丑闻”的数据驱动分析
你可以跑,但你躲不了(从统计数据上)。澳*利亚自由党很快就会发现这一点。
最近几周,自由党被指控在 2019 年*选前使用 1 亿美元的体育拨款来赢得选票。作为一名数据科学家,我决定通过分析数据来验证这一指控。因此,我着手研究我们能从公开的数据中了解到什么,这些数据与现在众所周知的“体育流氓”丑闻有关。
我的想法是,如果自由党根据选民的投票行为来分配补助金,那么我们应该能够使用统计模型来揭示他们的策略。
在我继续讲下去之前,这里有一些背景资料,以防你还没有看新闻…
背景
澳*利亚自由党自 2013 年联邦选举以来一直执政。在去年选举之前,政府宣布了一项社区体育基础设施计划,为当地体育组织提供高达 50 万美元的拨款。作为促进全国体育参与的一项举措,各组织必须提交一份申请,由澳*利亚体育局进行独立评估和评分。体育部长应该用这些分数来决定批准哪些拨款。
今年,澳*利亚国家审计署发布了一份审计报告,该报告暗示,但没有断言,自由党优先考虑“边缘”和“目标”选民的申请,而不是遵循澳*利亚体育局的建议。本质上,自由党被指控为目标地区提供更多拨款,以赢得 2019 年选举的选票。
澳*利亚选区地图内 684 个体育资助的位置。
回到行动
我分析的最终目的是看是否有证据表明拨款是根据上次选举中选民的投票行为分配的。本质上,有没有一个潜在的策略?如果是的话,自由党针对的是什么样的投票模式?
为了进行这一分析,我需要结合三个关键的数据源,所有这些数据源都是公开的:
- 关于批准的每项资助的信息(地点和金额)
- 上一次选举(2016 年)中每个选民的选举结果,以及
- 控制其他可能起作用的社会人口因素的人口普查数据。
有了这些信息,我将能够生成一个数据集,其中包含分配给每个选民的拨款总额,以及上一次选举的相应投票结果和人口普查的其他控制。
(注意所有的数据收集、争论和分析都是使用 *R*
)
获取数据
澳*利亚体育局在他们的网站上发布了一份 684 项批准拨款的列表,所以我用rvest
抓取了这份列表。接下来,这个列表被提供给一个 Google Maps API,以便获得每笔赠款的经度坐标(使用来自 dkahle 的 GitHub 的 T2)。这适用于几乎所有的资助。一些快速的谷歌搜索有助于找到这些组织的其余部分的位置。
为了将每个体育组织分配给其相应的选民,我需要 2016 年联邦选举的选民地图。方便的是,这可以在eechidna
包中获得,还有选举投票数据。使用来自sp
和rgeos
的一些漂亮的函数来遍历选举人,每笔拨款都被成功分配。
现在,补助金已经汇总,因此得到的数据集包含 150 个联邦选区中每个选区的补助金总额。然后,这将与 2016 年选举中的两党优先投票和摇摆投票以及 2016 年人口普查中的社会人口变量集合结合起来,这些变量也是从eechidna
中获得的。两党首选投票是倾向于自由党而不是工党的投票百分比,摇摆投票是 2013 年至 2016 年两党首选投票的百分点变化。
这是结果数据集的快照。
LiberalVote 为 2016 年两党优先投票百分比, TotalGrantAmount 为给予选民的拨款总额, Swing 为 2013 年至 2016 年的百分点摇摆投票。其他变量是 2016 年人口普查的社会人口特征。
现在是分析的时候了
我真正想揭示的是选民在 2016 年*选中对自由党的两党偏好投票与获得的拨款金额之间的关系。首先,让我们看看拟合了线性回归线的散点图是什么样子(使用ggplot2
)。
赠款总额(TotalGrantAmount)和两党优先投票(LiberalVote)散点图,有回归线和 95%置信区间。观察到正相关。
这里有一些证据表明,更支持自由党的选民更有可能获得体育拨款。但实际上,如果自由党真的实施了有针对性的策略,这两个变量之间的关系可能不是线性的。向最安全的地区提供最多资金的策略不会是一个非常好的策略——因为在 2019 年的选举中,安全的自由派选民不太可能容易易手。
因此,我没有拟合线性回归线,而是绘制了一条黄土曲线——一条平滑的非参数回归曲线,为局部数据点赋予更高的权重。这有助于更好地了解 2016 年投票和拨款金额之间的关系。
拟合黄土曲线而不是线性回归线。在两党的优先投票中,拨款明显飙升,略高于 50%。
哇!黄土曲线揭示了一种重要的关系,这种关系在最初的散点图中并不明显。
在 2016 年,自由党赢得的选民获得的拨款金额明显增加,但没有以巨*优势获胜。这些人被称为“边缘”自由党选民。这一阴谋本身就是资助计划中自由主义偏见的令人信服的证据。
为了更进一步,我决定拟合一个广义加性模型(GAM ),以拨款额作为响应,两党偏好投票(从 2016 年开始)、摇摆投票(从 2013 年到 2016 年两党投票的百分点变化)和人口普查特征作为协变量。每个协变量都作为平滑项包含在模型中。在这种情况下,我使用了惩罚回归样条——一种类似黄土的灵活的非参数回归。
GAM 与mcgv
拟合,造型效果的剧情由visreg
产生。
两党优先投票如何影响每个选民的总拨款的拟合效果。高于 50%的峰值显然是显著的。
可以清楚地看到,即使在考虑了摇摆投票和人口普查变量后,边缘自由党选民的拨款金额仍有巨*的飙升!
在其他条件相同的情况下,一个获得 54%自由党支持的选民和一个获得 54%工党支持的选民所获得的资金差异是惊人的 50 万美元。
GAM 回归输出证实了这是我着手发现的决定性证据。在 p 值< 0.001 的情况下,数据证明两党优先投票对分配给每个选民的总拨款具有统计上的显著影响。
为了检验我的发现对异常值的稳健性,我在去除了两个拥有巨额赠款(180 万美元和 200 万美元——给布斯比和道森的选民)的选民后,重新估计了模型,发现观察到了同样的自由主义偏差。此外,模型假设似乎是有效的,因为残差是近似正态的(使用分位数-分位数图观察)。对于那些想知道的人来说,完全指定的 GAM 解释了 46.3%的偏差——在这种情况下这是一个很好的解释。
最后
我的统计模型提供了经验证据,证明自由党利用体育拨款试图巩固对 2019 年*选的支持。数据清楚地表明,边缘自由选民是政府的目标,因此获得了更多的拨款。
虽然这一分析从政治角度来看非常有趣,但它也展示了如何利用公开数据产生强有力的见解。
对于任何对探索澳*利亚选举和人口普查数据感兴趣的人,我强烈推荐查看eechidna
R
包(我是该包的作者和维护者)。该软件包可以轻松访问 2001 年至 2019 年的澳*利亚选举和人口普查数据(包括地图),可在 CRAN 上获得。
所有用于分析的代码都可以在这个 GitHub repo 中找到。如果你有任何想法,想要联系,这是我的 LinkedIn 。
滚动跨期分析揭示动量效应
作者图片
扩展滚动窗口预测以发现经验策略的洞察力
这篇文章展示了跨期方法,该方法扩展和概括了滚动时间序列技术的范围,用于导出过渡过程和经验策略的模型。该方法是在解释动量溢价的背景下说明的,动量溢价是一个长期的持续挑战。
Jegadeesh 和 Titman [1]在 1993 年对动量效应进行了记录,表明动量效应从 1965 年到 1989 年为美国普通股带来了异常的正回报。自那以后,传统的横截面动量策略被概括为根据过去一年的回报滞后一个月对资产进行排序,并做多过去赢家资产的子集,同时做空过去输家资产的子集。多篇论文指出,在广泛的资产类别、市场和时间范围内存在动量,这种观点认为动量因素几乎无所不在。然而,动量溢价的来源仍然是一个悬而未决的问题,没有一个单一的模型来主导叙事。为了解释这一现象,人们提出了许多基于风险的行为模型,其中 Clifford Asness 和 Eugene Fama 对这一主题进行了一次值得注意的讨论[2]。
最近,动量效应被揭示动量论文[3]中提出的滚动跨期分析所解决。在这种方法中,选定的十分位数在时间范围内向前滚动,同时在组合的 11 个月排名、1 个月滞后和相对于组合形成日期的持有间隔期间收集每个月的组合回报。类似于滚动窗口预测,排名周期可以与样本内区间相关联,而持有周期构成样本外区间。下面是揭示动量文件的图表,展示了对 2006 年 8 月至 2017 年 8 月牛市状态的顶部动量十分位数运行滚动跨期方法的结果。
图:样本内和样本外动量最高的 10%月度回报的箱线图。
箱线图突出显示了样本内和样本外间隔之间的凸起。先前的研究主要致力于分析保持期。这些图把我们的注意力转移到了转变过程上。这个凸起的左侧可以用随机抽样模型来解释。根据这一模型,动量效应(样本外)应该为零,因此定义了评估基本动量理论和模型的标准。本文扩展了跨期方法,将动量效应解释为表现强劲的股票组合。该实现基于 Quantopian 平台,包括三个步骤:运行 Quantopian pipeline、选择特性和实施跨期方法。
量子管道
Quantopian 提供了一个数据科学平台,用于在交互式 Python 研究环境中开发和测试量化金融投资策略。这个框架的主要组件之一是一个充当不同异构数据集的公共接口的因子,因此可以在复合管道中统一处理这些数据集。特色应用需要技术和基本特征。
如下面的代码片段所示,技术因素,如动量和已实现的波动性,可以通过 CustomFactor 类使用股票价格来实现。
import numpy as np
from quantopian.pipeline.factors import CustomFactor, DailyReturns
from quantopian.pipeline.data.builtin import USEquityPricingclass Momentum(CustomFactor):
inputs = [USEquityPricing.close]
window_length = 252
def compute(self, today, assets, out, prices):
out[:] = (prices[-21] - prices[-252])/prices[-252]
class RealizedVolatility(CustomFactor):
inputs = [DailyReturns()]
window_length = 126
def compute(self, today, assets, out, returns):
ret_r2 = returns**2
out[:] = np.sqrt(np.nansum(ret_r2, axis=0))
基本面因素是通过晨星和 FactSet 数据集的 Quantopian 内置包装器定义的。
from quantopian.pipeline.factors import MarketCap, Latest
import quantopian.pipeline.data.factset as fset
import quantopian.pipeline.data.morningstar as mstarcap = MarketCap()
bp = 1/Latest([mstar.Fundamentals.pb_ratio]) # book-to-price ratio
ebit = fset.Fundamentals.ebit_oper_af.latest
shldrs_eq = fset.Fundamentals.shldrs_eq.latest
op = ebit/shldrs_eq # operating profitability
ag = fset.Fundamentals.assets_gr_af.latest # asset growth
此外,Quantopian 平台提供了一种机制,用于根据一组约束条件(如市值、美元交易量等)定义不同的股票领域。具体来说,QTradableStocksUS 宇宙包含*约 1600-2100 只股票,其市值*于 5 亿美元,在过去 200 天内的日均美元交易量中值*于或等于 250 万美元,收盘价高于每股 5 美元,以及过去 200 天的可用数据。然后,宇宙用于选择股票,以计算管道内的系数。
from quantopian.pipeline.filters import QTradableStocksUS
universe = QTradableStocksUS()
一旦定义了因素和领域,所有因素都可以合并到一个管道中,并在 Quantopian 引擎中运行。对于这种应用,这些因素被额外扩展用于生成四分位数等级。
from quantopian.pipeline import Pipeline
from quantopian.research import run_pipelinedef make_pipeline():
pipe = Pipeline()
pipe.add(Momentum(mask=universe), "mom")
pipe.add(RealizedVolatility(mask=universe).quartiles(), "rv")
pipe.add(cap.quartiles(mask=universe), "cap")
pipe.add(bp.quartiles(mask=universe), "bp")
pipe.add(op.quartiles(mask=universe), "op")
pipe.add(ag.quartiles(mask=universe), "ag")
pipe.set_screen(universe)
return pipestart, end = '2010-01-01', '2019-06-30'
pipe_data = run_pipeline(make_pipeline(), start, end)pipe_data.replace(-1, np.nan, inplace=True)
pipe_data.dropna(inplace=True)
最后,使用 Alphalens 模块将远期收益和因子合并到一个多指数数据框架中,该框架具有两个指数级别:日期和资产。
import pandas as pd
from alphalens.utils \
import get_clean_factor_and_forward_returns as add_returnsassets = pipe_data.index.levels[1]
prices = get_pricing(assets, start, end, fields='open_price')ad = add_returns(pipe_data['mom'], prices, quantiles=10)
ad.rename(columns={'factor_quantile': 'mom_decile'}, inplace=True)factor_names = ['rv', 'cap', 'bp', 'op', 'ag']
asset_data = pd.merge(ad, pipe_data[factor_names], \
left_index=True, right_index=True)
筛选功能
本文假设动量效应是表现强劲的公司的副产品,而不是投资者的行为。因此,作为下一步,我们需要筛选不同的公司特征(由晨星和 FactSet 数据集提供),以捕捉动量十分位数内的分布异常。由于可用特征的数量很*,本文将范围缩小到 Fama-French 五因素模型[4]中使用的特征:规模、市净率、营业利润率和总资产增长率。根据之前的动量研究,这个基本面列表进一步增加了一个技术特征,即已实现波动性。
asset_flat = asset_data.reset_index()df_ranks = pd.DataFrame(index=range(0,4))
df_ranks.index.name = 'rank'm10 = asset_flat.query('mom_decile == 10')
m10_count = len(m10)
for n in ['rv', 'cap', 'bp', 'op', 'ag']:
q_counts = \
[len(m10.query('{}=={}'.format(n, q))) for q in range(0,4)]
df_ranks[n] = [q_count/m10_count for q_count in q_counts]
df_ranks
表:动量上十分位数中每个因素四分位数的分数。
上表显示了动量前十分位数中每个因素的概率分布。基于输出,筛选发现了两个主要异常:已实现波动率(rv)和市净率(bp)。对于全面的研究,特征重要性分析可以通过机器学*方法(例如,分类树的平均减少杂质)进行扩展,López de Prado 在“资产管理器的机器学*”[5]中对此进行了概述。交互影响可以通过“HHP 多维模型的交互分析”[6]中介绍的技术进一步增加。
滚动跨期分析方法
根据上述特征筛选分析,动量上十分位数的 83%和 76%分别由已实现波动率的上半部分和市净率四分位数的下半部分组成。然后,这些结果促使我们对从动量前十分之一和整个股票宇宙中选择的相应样本所表现出的跨期行为进行比较。
下面的代码片段实现了每 21 天运行一次的函数,并在投资组合形成日之前和之后收集样本内和样本外窗口(wdw1 和 wdw2)的每日等权重投资组合回报。
def rolling_inout_rtns(df_input, # factors and returns
wdw1, wdw2, # in- and out-of-sample windows
query): # query of selected factor deciles
days = []; dfs = []
ds = df_input.index.levels[0].unique() for t in range(wdw1*21, len(ds) - wdw2*21, 21):
df_t = []
# select assets in the formation date t
xs_t = df_input.xs(ds[t])
assets = xs_t.query(query).index
df_t.append(len(assets))
# calculate means of selected assets for [-wdw1, wdw2]
for dt in range(-wdw1*21, wdw2*21+1):
xs_dt = df_input.xs(ds[t+dt]).loc[assets.values]
df_t.append(xs_dt['1D'].mean())
days.append(ds[t])
dfs.append(df_t)
columns = []
columns.append('count')
for dt in range(-wdw1*21, wdw2*21+1):
columns.append(str(dt))
df_output = pd.DataFrame(dfs, index=days, columns=columns)
df_output.index.rename('date', inplace=True)
return df_output
在定义了样本内和样本外窗口之后,该过程可以随后应用于具有高波动性账面价格比股票的样本。
wdw1 = 3 # in-sample months
wdw2 = 1 # out-of-sample monthsquery = '2 <= rv <= 3 and 0 <= bp <= 1'
asset_rtns = rolling_inout_rtns(asset_data, wdw1, wdw2, query)query += ' and mom_decile == 10'
mom10_rtns = rolling_inout_rtns(asset_data, wdw1, wdw2, query)
最后,绘制结果。
import datetime
from pandas.tseries.offsets import BDay
import matplotlib.pyplot as pltmkt_start = datetime.datetime.strptime(start, '%Y-%m-%d') - BDay(1)
mkt_prices = get_pricing('SPY', mkt_start, end, fields='open_price')
mkt_rtns = mkt_prices.pct_change(periods=1)[1:]t0 = -wdw1*21
t1 = wdw2*21 + 1mkt_means = [mkt_rtns.mean() for dt in range(t0, t1)]mom10_means = [mom10_rtns[str(dt)].mean() for dt in range(t0, t1)]
asset_means = [asset_rtns[str(dt)].mean() for dt in range(t0, t1)]mom10_sma = pd.Series(mom10_means).rolling(window=21).mean()
asset_sma = pd.Series(asset_means).rolling(window=21).mean()x = range(t0, t1)plt.figure(figsize=(9,4))plt.plot(x, mkt_means, label='mkt', color='y')asset_label = 'rv[2,3] & bp[0,1]'
plt.plot(x, asset_means, label=asset_label, color='grey')
plt.plot(x, asset_sma, label=asset_label+' SMA', color='green')mom10_label = 'mom 10 & ' + asset_label
plt.plot(x, mom10_means, label=mom10_label, color='grey')
plt.plot(x, mom10_sma, label=mom10_label+' SMA', color='red')plt.xlim(-40,20)
plt.ylim(-0.003,0.006)
plt.xlabel('Days')
plt.ylabel('Average Daily Returns')
plt.legend(loc='upper right')
图:动量赢家和高波动性成长股的样本内和样本外结果。
如图所示,样本内和样本外区间由三个时期组成:排名、一个月跳票、持有。动量赢家(红色)和高波动性增长四分位数(绿色)之间的排名周期差异是由动量过程采样的高特质回报决定的。在跳跃区间,当排名周期在图上的第 21 天结束时,动量赢家接近高波动成长股的平均水平。在形成日期之后,它们都经历了类似的短期反转。
有趣的是,虽然滞后一个月之前被记录为经历短期反转,但图中的结果显示,跳过最近一个月并因此将其从常规排名区间中排除不再是必要的。然而,在这项研究中,将月份排除在排名之外,有助于通过捕捉动量赢家和高波动性成长股之间的一致性,深入了解动量效应的主要驱动因素。
76%的动量赢家是成长股,因此价值股的贡献并不*。尽管如此,同样的过程也可以应用于相应的高波动性值子集,并解释这种影响。
摘要
本文提供了基于滚动跨期方法的应用程序的分步演示,以解释动量策略的优势。该方法通过连接用于导出过渡过程模型的样本内和样本外区间,扩展了滚动窗口预测技术。具体来说,对于 2010 年至 2019 年,文章揭示了动量策略作为对高波动性成长股进行采样的程序,并通过排名、跳跃和持有区间来跟踪它们的转变。这个应用补充了两篇基于量子理论的关于动量的论文,包括波动时机和揭示动量。如果感兴趣,可以在 Quantopian 论坛的以下主题中找到更多信息和笔记本:
参考
[1] N. Jegadeesh 和 S. Titman,回报买入赢家和卖出输家:对股票市场效率的影响 (1993),金融杂志
[2] C .阿斯内斯,法玛论动量 (2016),AQR 见解
[3] Y. Malitskaia,揭开势头 (2019),SSRN 工作文件
[4] E. Fama 和 K. French,一个五因素资产定价模型 (2015),金融经济学杂志
[5] M.M. López de Prado,资产管理者的机器学* (2020),剑桥量化金融要素
[6] Y. Malitskaia、W.H. Greene 和 Y. Malitsky,HHP 多维模型的相互作用分析 (2012),ERCIM 2012
网站
Quantopian 是一个免费的在线平台和社区,用于教育和投资算法的创建。量子乌托邦…
www.quantopian.com](https://www.quantopian.com) [## 晨星|让投资者成功
我们的独立研究、评级和工具正在帮助整个投资生态系统的人们撰写他们自己的…
www.morningstar.com](https://www.morningstar.com) [## FactSet |面向投资专业人士的集成金融数据和软件解决方案
了解 FactSet 与众不同的最佳方式是自己去看。我们与投资部门合作过…
www.factset.com](https://www.factset.com)
揭开宇宙:寻找系外行星候选者和其他异常的机器学*方法
演示如何使用机器学*快速准确地搜索*量天文数据
图片来源:spaceengine.org
灯塔的目的是在黑暗中给别人指路。当我们观察浩瀚的夜之海洋而没有人点燃它时,会有一些令人不安的暗示。—伊萨克·阿瑟
简介
T 天文学领域正日益成为一个*数据问题,随着更*的调查和更精确的仪器,数据进入了万亿字节的范围并呈上升趋势。在更广阔的物理领域更是如此。*型强子对撞机每小时获得的数据比脸书一整年获得的数据还多。因此,反馈可能会很慢,在发现之前,发现可能会在数据集中存在多年。
我概述了一种基于机器学*的方法,它允许我们:
- 天文时间序列数据的快速目标搜索
- 自动检测最异常的测量值
- 这样做有合理的计算要求
然后,我将这种方法应用于从最近的卫星观测中获得的真实天文数据。
光曲线
我们目前的分辨率还不足以从视觉上分辨邻近太阳系的细节。然而,我们对物理学有很好的理解,可以通过间接测量来理解正在发生的事情。其中之一是恒星的亮度。
如果一种现象(自然的或其他的)正在恒星周围发生,我们可以预期它的间接影响会随着时间的推移出现在恒星的亮度中。如果一个物体挡住了恒星的去路,那么恒星就会变暗,变暗的程度与该物体相对于恒星的表观尺寸成正比。根据亮度曲线的轮廓,我们可以推断出物体的属性,例如它的*小和它离恒星有多近。这是用来寻找系外行星的主要方法,被称为凌日法。
中转方法。图片来源:NASA
在分析这类数据时,有几件事要记住。尤其是寻找像行星这样的自然现象。
- 你需要观察恒星足够长的时间才能看到变暗的重复。例如,如果一个外星文明正在使用这种方法观察太阳,他们将不得不观察它一年,以观察地球重复造成的变暗。理想情况下,你会希望它发生三次。
- 你需要恒星轨道平面向我们倾斜。人们倾向于认为所有的行星都以与银道面相同的角度运行,但事实并非如此。例如,我们自己的太阳系与银道面呈 60 度倾斜,这就是为什么我们在南半球能更好地看到银河系中心的原因。不幸的是,如果一颗恒星的两极直接指向我们,我们将不会因为它的行星的运动而观察到亮度的任何变化。
- 1)和 2)的组合以及多年来光度计灵敏度的变化导致了测量偏差。如果你只是在短时间内观察恒星,并让现象在这段时间内重复出现,那么你多半是在识别恒星周围的短年天体。现在,我们可以敏感地注意到地球*小的物体造成的亮度变化,但很长一段时间以来,我们所知道的唯一行星是木星*小的物体,其轨道比水星更近(因为内轨道的年数更短)。
最终目标是分析来自 transit 方法测量的时间序列信息,以找到感兴趣的对象。
挑战
尽管受限于与太阳系处于同一平面的物体,银河系中恒星的数量是如此之多,以至于仅从一次观测中仍会产生数十万或数百万条光变曲线。每个测量还会有数千个数据点和附加信息。仪表板是为公民科学家开发的,以帮助天文学家处理数据。现在著名的 KIC 8462852(虎斑星)就是这样被发现的。这种方法确实意味着数据可以存储在后端,将计算需求缩小到一个地方。但是仍然存在以下问题:
- 这仍然是一种蛮力方法
- 寻找特定的现象仍然是耗时的,并且可能仍然需要不能通过前端完成的编程方法
- 如果影响微弱,现象可能会被忽略
- 很难快速获得对数据的整体理解
给我们快速反馈的方法非常有用,尤其是当我们想用其他仪器进行后续测量时。如果调查中的数据量呈上升趋势,上述问题的解决方案需要廉价、快速且可扩展。
这里可以引入几个有用的机器学*概念。
相关机器学*概念
*多数非该领域的人都理解机器学*模型的概念,即进行分类预测,无论是二元(是或否)还是多分类。不幸的是,在许多实际情况下,这些标签可能不可用或不可靠。即使您有时间和资源,数据的正确标记可能也没有那么有用,并且不是每个对数据感兴趣的从业者都会对相同的标记感兴趣。我们想要的是利用深度学*架构的潜力来理解复杂的结构,并以一种无人监管的方式返回见解。
时间序列编码
请考虑以下情况。如果我问一个人两幅画是否相似,通常不会(只是)有一些不同的特征使它们如此。我们的语言有时不足以描述为什么它们是相似的,但你知道它们是一种手动波浪形的方式,这与风格等有关。这是因为我们对我们所看到的东西有一种复杂的更高层次的理解(或感觉),而不是我们必须写下来或交流的。
深度学*模型通过理解数字阵列或向量表示形式的输入来工作。即使我们做一个分类问题,我们所做的只是将网络的最后一层从这个向量表示转换成一个显式输出。由于网络中的所有非线性,这些表示通常不是人类可以解释的。
对于我们的光曲线,我们不想做预测,而是让模型将光曲线变成这些矢量表示中的一个,捕捉它的所有结构。当我们需要洞察力或进行搜索时,我们最终将只是与这种表征一起工作。
既然我们有了一种以相同方式表示所有光线曲线复杂性的方法,我们可以执行聚类并评估相似度。我们还可以决定我们希望这个表示有多*,从而使它有利于压缩。
类似
如果我们将每个向量想象成空间中的一个点,其中每个数字对应一个坐标,如纬度或经度,我们可以通过其位置以及相对于其他点的距离和方向来提取信息。这本质上是相似的基础。
余弦相似度
通常,两个数据点相对于彼此的方向比它们的欧几里德(L2)距离更能说明问题。如果我们有数据的矢量表示,两个矢量的方向就是它们的归一化(按*小)点积。
余弦相似度。图片来源:维基百科
由于操作简单,即使在高级语言中计算也非常快,因为它们经常在 C 中运行算术运算。
对于纯软件思维的人来说,有一种诱惑,即逻辑上认为是一组 For 循环的总和。但处理器实际上在非常低的级别上有特殊的算术运算,称为融合乘加,它将运算结合在一起,比单独做乘法和加法更快。因此,对于相似性搜索,如果我们想最*化速度,我们希望利用这种算法。这样做比用高级语言手工实现要快几个数量级。这是我们想要将数据转换成潜在向量表示的另一个原因。理解这一点是用合理的硬件进行数十亿规模的相似性搜索的关键。
然而,这一指标有一些警告需要讨论,因为时间序列中相似性的概念不像其他数据类型那样简单明了。有相似性度量可以证明更适合时间序列相似性,包括:
- 复杂性不变相似性
- 基于相关性的相似性
- 基于自相关的相似度
在以后的工作中记住这一点,我稍后证明余弦相似性对于我们试图解决的问题仍然是足够的。
建筑与培训
为了获得一个给我们矢量表示的模型,我们需要训练一个神经网络。即使我们是在无人监督的情况下做这件事,我们仍然需要给模型一个任务来解决。
一个自动编码器是一种由两部分组成的架构——编码器和解码器。编码器将数据还原为我们选择的矢量表示,解码器尝试从该表示中重建信号。因为它们被一起训练,所以编码器将试图找到最有意义的表示,使得解码器可以创建最准确的重建。
由于我们处理的是时间序列,因此有两种类型的输入重构可用于训练:
插值 —给定一个值在区间【t₀,tₙ】内的时间序列,我们以概率为 10–50%的点子集为条件,然后尝试在相同范围内重建完整信号。
外推 —给定一个值在区间[t₀,t₂ₙ]we]的时间序列,编码信号的前半部分[t₀,tₙ]并尝试重建信号的后半部分[tₙ,t₂ₙ].]像插值一样,我们可以以第一个区间上的点的子集为条件。
为了获得光曲线的编码,我选择了插值方法,因为我们的目标是比较信号,而不一定是预测。
ODE-RNNs
递归神经网络(RNNs)是时间序列的主要结构类型,因为它们具有捕捉长期相关性的能力。但如果时间序列是不规则采样,一般会采用各种变通方法。直到最近,处理该问题的一种常用方法是将时间步长信息作为输入要素与信号一起传递,在缺失点处估算值,或者以*于最*间隙的分辨率进行固定时间离散化并聚合值。
神经常微分方程领域的最新发展使我们能够通过将 RNNs 转化为连续时间模型来更好地拟合缺失时间点。这在论文中探讨了不规则采样时间序列的潜在常微分方程,并被称为常微分方程-RNN。
奥德-RNN 插值缺失数据。图片来源:潜颂
处理不规则采样时间序列的能力对于我们的编码器来说是非常有用的能力,因为我们的向量表示将更能指示光曲线的真实动态。如果有几个间隔时间的后续测量,这是特别有用的。我们可以用它作为我们的网络编码器。
凌日系外行星调查卫星
TESS 于 2018 年发射,是首个在整个天空中搜寻系外行星的太空任务。它通过将天空划分为每个半球的 13 个观测扇区来实现这一点。每个扇区观察 27 天,在重新定向以对下一个扇区重复该过程之前,每 2 分钟记录一次测量。重点是附近的恒星,每个扇区观测*约 200,000 颗恒星。在编写本报告时,主要任务的 26 个预定部门中的 17 个已经完成,并向公众提供了数据。每个扇区的时间序列数据*约为 40GB,因此我将分析重点放在最近的两个扇区(16 和 17)。
TESS 相机的视野和半球分区。图片来源:TESS 天文台指南。
方法
TESS 数据包含对象 id 和各种带有相关时间戳的测量值。我将时间戳和流量(亮度)按对象 id 分组。然后实施以下预处理步骤:
- 离散化——尽管每两分钟测量一次,但飞船上的时钟分辨率很高。为了批量求解常微分方程,我们必须将所有时间步长的并集输入到模型中。如果时钟分辨率很高,这会产生许多独特的时间点,即使实际上它们非常接近。因为我们希望减少所有时间步长的并集*小,所以我将时间步长四舍五入到两位小数。从检查来看,这对于光变曲线没有明显的差异,并且聚集没有显著改变每个样本的观察数量。
- 归一化方案 —乍一看,通过每次测量最*值进行归一化似乎是合适的,因为这是人们通常描述亮度下降的方式,例如 0.99 相当于亮度下降 1%。然而,该模型很难同时适用于亮和暗的物体,因为亮和暗物体的量值变化可能要低几个数量级。为了使样本比例不变,I 使用每个样本的平均值和标准偏差对每个样本进行 z 归一化(非全局)。本文后面的所有结果都显示了标准化的光线曲线。
我训练了一个多层 ODE-RNN,但是对潜在 ODE 库做了以下修改,因为我发现它们为我的任务提供了更好的结果:
- 损失函数 —我把标准的 L2 损失换成了 L1 损失。我发现它总体上更好地捕捉了局部动态,而不是试图适应*峰和*谷。
- 微分方程解算器 —默认使用欧拉法。尽管这种方法训练速度更快,但随着步数的增加,误差会累积。我把它改成了自适应龙格-库塔解算器,这是 torchdiffeq 包中的标准配置。
- 禁用偏置 —对于所有层,我禁用了偏置参数,因为我发现它给出了更好的结果,并且训练更快。
使用 10 的批量*小对来自总数据集的 5000 个随机子集进行训练。Adamax 优化器以 1e-3 的固定学*速率使用。选择的潜在维度为 40。在具有 12GB GDDR5 RAM 的 NVIDIA GTX 1070 上进行训练。
推论
训练之后,目标是将我们所有的光线曲线转换成我们的潜在表现形式。
虽然一起训练编码器和解码器,但我们在推断时只使用编码器计算每条光曲线的潜在表示。这只需要做一次,并且在评估结果时比训练时快得多,因为我们不需要更新任何梯度,也不需要将所有梯度保存在内存中。尽管如此,评估的速度将是编码器有多少参数的函数。
在将结果保存到磁盘之前,我们只需一次将一批数据加载到内存中。在我的情况下,我能够在笔记本电脑 CPU 上每秒评估*约 2500 个潜在表示。完成后,我们就可以进行主要的分析了。
可视化结果
尽管将每条光变曲线简化为 40 个数字比成千上万个数据点需要处理的信息要少得多,但乍看之下,给定值的含义肯定不明显。能够一次看到所有的数据给了我们一个*概的概念,我们可以期望看到不同类型的测量。
t-分布式随机邻居嵌入(t-SNE)是一种有用的算法,可以将数据的维数降低到我们选择的*小。这个算法是而不仅仅是高维空间到低维空间的投影。简而言之,通过估计一个点选择另一个点作为其邻居的条件概率来赋值。通过这样做,你基本上保存了尽可能多的来自更高维度的信息。
选择二维输出意味着我们现在可以将完整的光变曲线测量视为 x-y 平面上的单点。彼此靠近的点是彼此相似的测量值。出现的是可以研究的集群。
苔丝 16 和 17 区的每颗恒星编码的 t-SNE 可视化。每个点都是一条光曲线。灯光曲线是 z 规格化的。
从我们的 t-SNE 图中我们可以看到,有清晰的星团,你可以自己在它们周围画一个圈。有些似乎包含常见的测量值,而另一些则比较罕见。在小岛上磨砺显然有利于快速识别更独特的观测类型。然而,从计算的角度来看,我们可能希望完全跳过可视化,直接从潜在表示进入集群标签。
使聚集
尽管 t-SNE 在将测量值组合在一起方面做得很好(以及提供可视化的明显好处),但我们有时确实必须小心它的解释。减少到二维可能仍然隐藏测量的真实变化。通常,直接在 40 维潜在空间向量上聚类是有意义的。
有几种不同类型的聚类算法,每种算法处理问题的方式都非常不同。不幸的是,*多数类型的聚类算法对相似性度量、聚类*小或数据集的几何形状做出假设。令人烦恼的是,只有少数算法不需要指定集群的总数。
如果我们不得不做出任何妥协,那么这几乎就违背了我们一开始就试图做这件事的目的。这是因为我们不一定知道我们期望看到什么。由于数据集很*,我们希望尽快获得有用的反馈。
考虑到这些挑战,我们对天文观测的有用聚类算法的标准应该是能够:
- 自动检测簇的总数
- 创建具有不同*小和分布的集群
- 识别无关的数据点,并且不强制它们进入聚类
- 提供一致的结果,对超参数调节不敏感
尽管有几十种聚类算法,但基于层次密度的带噪声应用空间聚类 (HDBSCAN)算法几乎没有做出妥协,并且达到了上述所有标准。
在 TESS 数据集上应用该算法返回了 300 个聚类。有些似乎与微妙的差异密切相关,但总的来说是非常明显的。每次的计数从几个到几百个不等。下面是来自三个不同集群的三条光变曲线的示例。
不同聚类中的光变曲线样本。每行是一个簇。
如果我们想对一个候选名单进行后续测量,从聚类水平开始可以使这一决定更容易,因为我们现在可以量化一种类型的观察因其聚类的*小而有多常见。
使用快速人工智能相似性搜索(Faiss)进行快速搜索
如果我们利用前面讨论的相似性度量,可以实现快速搜索相似亮度曲线的能力。Faiss 是一个有用的库。
Faiss 是一个为快速高效的相似性搜索而优化的库。它是用 C++和 python 包装器编写的,因此使它易于访问和使用。它假设相似性可以通过 L2 距离或点积来评估,并且具有 GPU 支持。
Faiss 提供多种索引选择,以满足内存消耗、速度和精度要求。在这种情况下,我可以将所有向量放入内存中,并希望得到精确的结果,因此我选择了一个称为“扁平内积”的索引。平面索引不会简化计算,也不会对数据库的结构做任何假设。后者不是必需的,因为搜索是彻底的。
内存消耗是每个向量 4 x d 字节,其中 d 是我们潜在表示的维度(在我的例子中是 40)。这种方法的一个附带好处是,通过选择正确的 d 并进行适当的权衡,可以设计一个在任何硬件上工作的完整流水线。Faiss 文档中有一个关于索引选择的有用指南这里是。但是作为补充说明,如果我们要在此分析中包括整个 TESS 数据集,它可能不适合内存,并且可能需要(但不一定)使用 GPU 来提高速度,并选择一个索引来优化内存。
Faiss 只会做两个向量的点积。为了获得余弦相似性,我们需要在将样本添加到数据库时对每个样本进行 L2 归一化。然后,我们也对输入向量执行此操作,这将导致两个 L2 归一化向量的点积,从而给出它们的余弦相似性。
# Obtain a vector representation of our curve and normalize
input_vec = input_vec/np.linalg.norm(input_vec) # L2 normalization
input_vec = input_vec.reshape(1,-1)# Search our faiss db with the vectors from all TESS observations
probs, ixs = db.search(input_vec, k=5) # return top 5 results
因为目标是快速获得结果,所以注意这一过程的时间复杂性是很重要的。在整个数据库中找到前 5 个最相似的光变曲线用了 不到 1 毫秒 。此外,无论我们想要多少结果,这都可以很好地扩展。切换到前 1000 个结果不会对查询时间产生很*影响。
%timeit db.search(input_vec, k=5)
**762 µs** ± 32.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)%timeit db.search(input_vec, k=1000)
**1.41 ms** ± 264 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
基本用法和结果
获得查询结果后,可以通过查找对象 id 或使用模型的解码器组件从潜在表示中重建原始光曲线来绘制结果。我手动选择了一些表面上看起来有点独特的曲线,并进行了搜索。我在下面显示了输入曲线和前 3 个结果。尽管我上面提到了余弦相似性的警告,但我们可以看到,潜在的表示捕捉了许多我们用来将两个时间序列视为相似的属性。
输入光曲线(红色)和查询结果(黑色)。结果是 z 归一化的。
定向搜索
值得注意的是,我们的输入向量不一定来自数据库内部。例如,我们可能已经有了一个特定现象的理论模型,但是无法在数据库中找到一个现有的向量来找到类似的对象。如果我们知道如何用数学方法表达预期的光变曲线或者从模拟中得到一个结果,我们只需将时间序列输入到编码器中就可以得到表示。这成为我们查询数据库的输入向量。
顺便提一下,提到这如何能帮助寻找外星智慧是很有用的。通常搜索寻找不寻常的无线电或光学激光脉冲发射。然而,考虑到太阳帆、遮阳篷、轨道栖息地或任何故意设计来宣布它们存在的结构的潜在*小,这开辟了凌日方法作为这种搜索的有效方法。关于这些不自然的光变曲线看起来会是什么样子,已经有了研究和建议。
吕克的论文。F. A. Arnold 题为人造物体的凌日光变信号研究了我们期望从不期望自然存在的物体中看到的东西,包括 1)等边三角形 2)两个屏幕和 3)类似百叶窗的六个屏幕。它提醒我们,模拟存在单个对象、多个对象以及它们是否旋转的情况也很重要。值得注意的是,如果测量值低于一定的分辨率,仍然有可能将一些物体与环形行星等事物混淆。因此,给出的结果是与同等*小的球形物体相比的幅度差,而不是原始光曲线。
人造物体的例子(左)和亮度*小差异(右)。图像来源:人造物体的过境光变特征(2005)
外星文明可能有意制造一个明显是人造的重复信标。这种信号的候选者有很多,但一个例子是二进制中圆周率的前几个数字。然而,这可能不太可能在可见光谱中实现,因为这不是能量有效的。但有争议的是,一个试图吸引注意力的文明可能会通过在其他人的常规天文观测中试图被他们看到来做到这一点。如果是这种情况,那么在凌日法观测过程中,一系列产生明显不自然光变曲线的物体就值得寻找。
用二进制信号表示的圆周率的前几个数字。
要点是,给定一个理论化的光变曲线(自然的或人工的),我们可以使用这种方法在可接受的时间内快速找到候选者。
最不相似的物体
除了将一个向量与整个数据库进行比较,还可以有一批输入向量。可以预见,这比一次做一个要快,并且伸缩性好。
发现奇怪光曲线的一种方法是在整个数据库中进行完全的成对相似性搜索,这是可能的,因为我们可以进行批量输入。由于 Faiss 返回顶部结果的相似性分数,我们可以推断最奇怪的光曲线是那些顶部相似性分数仍然很低的(找到最小的最*值)。这可以通过 thresh-holding 来完成,或者简单地根据数据库批处理查询的得分从最低到最高排序,返回 k =1 个最相似的结果。
下面是 TESS 扇区 16 和 17 的前 20 个最不相似的光变曲线。该操作耗时 1.1 秒:
Tess 部门 16 和 17 中最不相似的 20 个结果
关于这个结果的一些观察结果——有 4 或 5 个观察结果可能是在测量开始时具有*尖峰的小故障。除此之外,我们还有各种短周期和高变化曲线的组合。
更广阔的电磁频谱和未来的工作
对于那些不熟悉物理的人来说,我们用眼睛看到的只是更广泛的电磁(EM)光谱的一小部分。本质上,我们看到的是不同波长(或能级)的光子,每个波长有不同的颜色。在长波长,我们有无线电频谱,而可见光,红外线,X 射线和伽马射线组成了较短的波长。用专用的硬件来观察不可见的波长是可能的,这在天文学中是很常见的。这是因为电磁辐射揭示了许多物理过程,有些物体只有在这些波长下才可见。下面是对猎户座的可见光和红外观测的比较。
可见光(左)和红外(右)中的同一个星座。图片来源:NASA
TESS 卫星在可见光和近红外范围(600 纳米-1000 纳米)进行观测,因为这被认为是寻找类地系外行星的良好范围。上述方法的真正潜力在于更广泛的电磁频谱。
机器学*方法的一个优点是,我们可以训练编码器接受许多输入,并为每个对象提供一个组合的矢量表示。使用这种编码器的矢量数据库可以按照相同的过程建立。如上所述,我们可以根据我们对整个电磁光谱的预期来查询预期的光变曲线。例如,一颗红外辐射很强但没有可见光的恒星可能被尘埃包围,或者被一个结构包围。
我们可以将多个输入组合成相同的表示,并遵循相同的流程
另一个好处是,如果某个源的观测测量值不可用,还可以处理某些输入中的缺失数据。如果输入来自不同的观测站,而这些观测站的访问受到限制,或者观测是在不同时间协调的,就可能出现这种情况。尽管输入的不同部分存在差异,但网络可以概括为具有足够的表达能力,ODE-RNNs 有可能很好地处理这一点。
虽然可能需要更长的训练时间,但这不应该降低推理时间的性能或增加内存消耗,因为我们在可视化、聚类和搜索过程中仍然使用相同*小的向量。
摘要
我已经证明,如果我们使用自动编码器将凌日光变曲线测量结果编码成潜在空间表示,就有可能以合理的硬件要求对*型数据集进行快速有效的分析。神经常微分方程的最新发展使我们能够以精确的方式处理不规则采样的时间序列和可变长度的测量。
即使我们的硬件有所改进,我们开始精确地积累数十亿(或数万亿)的对象,我们肯定有能力快速查询数据。在编码器和解码器架构以及聚类算法方面可以继续进行*量的改进。
此外,在时间序列分析是关键的更广泛的物理学和宇宙学领域,有很*的机会应用这样的方法。
如果你想联系我,请随时在 glenn.kroegel@gmail.com 联系我,或者在 LinkedIn 上给我发消息。
揭开魔法:解读机器学*黑盒模型
你是否曾经开发出一个机器学*模型,它具有很高的准确性和惊人的 AUC,但却不知道如何解释这些预测?
在预测能力和可解释性之间进行权衡是使用黑盒模型时面临的一个常见问题,尤其是在必须向非技术受众解释结果的业务环境中。可解释性对于质疑、理解和信任人工智能和人工智能系统至关重要。它还为数据科学家和工程师提供了更好的方法来调试模型,并确保它们按预期工作。
动机
本教程旨在介绍在黑盒模型中处理模型解释的不同技术。
免责声明:本文旨在向普通数据科学家介绍可解释机器学*领域的一些有用技术,并推动其采用。他们中的*多数已经从 Christoph Molnar 的这本非常值得推荐的书中总结出来:可解释的机器学*。
本文中使用的完整代码可以在我的 GitHub 中找到
内容
- 可解释性方法的分类
- 数据集和模型培训
- 全球重要性
- 本地重要性
1.可解释性方法的分类
- 内在还是事后?该标准区分可解释性是通过限制机器学*模型的复杂性(内在)还是通过在训练后应用分析模型的方法(事后)来实现。
- 特定于模型还是与模型无关?线性模型有特定于模型的解释,因为回归权重的解释特定于该类模型。同样,决策树拆分也有其特定的解释。另一方面,模型不可知的工具可以用在任何机器学*模型上,并且在模型已经被训练(后期)之后被应用。
- 本地还是全球?局部可解释性是指解释单个预测,而全局可解释性与解释预测任务中的模型一般行为相关。这两种类型的解释都很重要,并且有不同的工具来处理它们。
2.数据集和模型培训
本文使用的数据集是来自 UCI 机器学*知识库的 成人普查收入 。预测任务是确定一个人的年收入是否超过 5 万美元。
由于本文的重点不在 ML 管道的建模阶段,所以执行了最小特征工程,以便用 XGBoost 对数据进行建模。
为该模型获得的性能指标如下:
图 1:训练和测试装置的接收操作特性(ROC)曲线。
图 2: XGBoost 性能指标
这个模型的性能似乎还可以接受。
3.全球重要性
用于评估模型整体行为的技术包括:
3.1 -特征重要性(由 XGBoost 模型和 SHAP 评估)
3.2 -汇总图(SHAP)
3.3 -排列重要性(ELI5)
3.4 -部分相关图(PDPBox 和 SHAP)
3.5 -全局代理模型(决策树和逻辑回归)
3.1 -功能重要性
- XGBoost(特定型号)
feat_importances = pd.Series(clf_xgb_df.feature_importances_, index=X_train.columns).sort_values(ascending=True)
feat_importances.tail(20).plot(kind='barh')
图 3: XGBoost 特性的重要性
使用 XGBoost 时,在解释特性的重要性时必须小心,因为结果可能会产生误导。这是因为该模型计算了几个具有不同解释的重要性指标。它创建了一个重要性矩阵,这是一个表,第一列包括提升树中实际使用的所有特征的名称,另一列是用不同的度量标准计算的结果“重要性”值(增益、覆盖、频率)。在这里可以找到更全面的解释。
增益是解释每个特征的相对重要性(即准确性的提高)的最相关属性。
- SHAP
总的来说,SHAP 库被认为是一个解决可解释性的模型不可知的工具(我们将在本地重要性章节中讨论 SHAP 的直觉)。但是,对于基于树的机器学*模型,如决策树、随机森林和梯度提升树,该库有一种特定于模型的方法。
explainer = shap.TreeExplainer(clf_xgb_df)
shap_values = explainer.shap_values(X_test)shap.summary_plot(shap_values, X_test, plot_type = 'bar')
图 4: SHAP 特征重要性
XGBoost 特征重要性用于评估训练数据集模型输出中预测值的相关性,SHAP 特征重要性用于评估测试数据集模型输出中预测值的相关性,以评估最重要的特征在两种方法和集合中是否相似。
据观察,模型中最重要的变量得到了保留,尽管其重要性顺序不同(年龄似乎在 SHAP 方法的测试集中具有更*的相关性)。
3.2 剧情概要(SHAP)
SHAP 汇总图是评估模型特性的一个非常有趣的图,因为它提供了比传统的特性重要性更多的信息:
- 特征重要性:变量按重要性降序排列。
- 对预测的影响:横轴上的位置表示每个特征的数据集实例的值对模型输出的影响是多还是少。
- 原始值:颜色表示每个特性的高值或低值(在每个特性的范围内)。
- 相关性:特征与模型输出的相关性可以通过评估其颜色(其取值范围)和对水平轴的影响来分析。例如,观察到年龄与目标正相关,因为对输出的影响随着特征值的增加而增加。
shap.summary_plot(shap_values, X_test)
图 5: SHAP 汇总图
3.3 -排列重要性(ELI5)
评估预测值全局重要性的另一种方法是随机排列数据集中每个要素的实例顺序,并使用训练好的模型进行预测。如果通过在顺序中进行这种干扰,评估度量没有实质上改变,则该特征不那么相关。相反,如果评估度量受到影响,那么该特征在模型中被认为是重要的。这个过程是为每个特征单独完成的。
为了评估训练的 XGBoost 模型,ROC 曲线的曲线下面积(AUC)将被用作性能度量。将在训练和测试中分析排列重要性:
# Train
perm = PermutationImportance(clf_xgb_df, scoring = 'roc_auc', random_state=1984).fit(X_train, y_train)
eli5.show_weights(perm, feature_names = X_train.columns.tolist())# Test
perm = PermutationImportance(clf_xgb_df, scoring = 'roc_auc', random_state=1984).fit(X_test, y_test)
eli5.show_weights(perm, feature_names = X_test.columns.tolist())
图 6:训练集和测试集排列重要性。
尽管最重要的功能的顺序发生了变化,但看起来最相关的功能保持不变。有趣的是,与 XGBoost 特征重要性不同,训练集中的年龄变量具有相当强的影响(如测试集中的 SHAP 特征重要性所示)。此外,根据排列重要性的 6 个最重要的变量被保持在训练和测试中(顺序的差异可能是由于每个样本的分布)。
估算全球重要性的不同方法之间的一致性使模型输出的解释更加可信。
3.4 -部分相关图(PDPBox 和 SHAP)
部分相关图(PDP) 表示某一特性对预测输出的边际影响。为此,在其他条件不变的情况下,修改特征,并观察平均预测的变化。进行的过程如下:
1)选择特征
2)定义网格值
3)为每个网格值:
3.1)用网格值替换特征
3.2)平均预测值
4)绘制曲线
PDP 可以指示特性和输出之间的关系是线性的、单调的还是更复杂的。值得注意的是观察到的关系是与预测的关系,而不是与目标变量的关系。然而,根据模型的性能,可以产生目标对所评估特征的依赖性的直觉。
PDP 的优势是非常 容易实现 并且 非常直观 :如果所有数据点都被强制采用每个特定值,那么特定特征中的函数代表平均预测。
我们将使用 PDPBox 和 SHAP 分析部分相关图。
- PDPBox
例如,将分析两个最相关的观察特征的 PDP:
# Create the data that we will plot
pdp_education = pdp.pdp_isolate(model=clf_xgb_df, dataset=X_test, model_features=X_test.columns, feature='education.num')
pdp_age = pdp.pdp_isolate(model=clf_xgb_df, dataset=X_test, model_features=X_test.columns, feature='age')# Plot it
pdp.pdp_plot(pdp_education, 'education.num',figsize=(12, 6))
pdp.pdp_plot(pdp_age, 'age', figsize=(12, 6))
plt.show()
图 7:教育的部分相关图
看起来受教育年限(从 7 年开始)和收入超过 5 万美元的概率之间存在线性关系。这一特征在模型输出中的影响被证明高达 0.6(满分 1)。
图 8:年龄的部分相关性图
人们似乎更有可能在 50 多岁的时候赚到超过 5 万美元。
- SHAP 依赖情节
将使用 SHAP 方法生成相同的 PDP。该库除了指示特征对模型输出的边际影响之外,还通过颜色指示与最常交互的特征的关系。
shap.dependence_plot('education.num', shap_values, X_test)
shap.dependence_plot('age', shap_values, X_test)
图 9:教育的部分相关图
尽管 y 轴刻度不同于 PDPBox 图(我们将在本地可解释性部分中了解原因),但“education.num”的趋势似乎与之前的方法相同。此外,SHAP 已经识别出特征【已婚 _1】是与它交互最多的特征(这意味着,对于该模型来说,具有高教育年限的已婚人士更有可能收入超过 5 万美元)。
图 10:年龄的部分相关性图
该方法中的年龄趋势与 PDPBox 方法一致。和它互动最多的功能是“education.num”。
在陈述了 PDP 对可解释性领域的优势后,也值得(也公平地)陈述一下劣势:
- 它没有考虑特征 的分布:它可能会产生误导,因为它可能会错误地解释数据非常少的区域(通过给所有数据点分配这些值,特征值被过度表示,这可能会导致错误的结论)。
- 特性独立性假设 :这是 PDP 最*的弊端之一。假设为其计算部分相关性的特征与其余预测值不相关。
- 异质效应可能被隐藏 :这是因为只计算平均边际效应。在极限情况下,PDP 可能是一条水平线,值均匀分布在上下,表明该特征对预测没有影响。
为了克服 PDP 的一些缺点,可以使用个体条件期望(ICE) 和累积局部效应(ALE) 图。尽管这些方法的实现没有包含在本文中,但是我们将简要地解释它们,以展示它们如何改进 PDP 方法。
个人条件期望(ICE)
ICE 图是单个数据点的 PDP 等效图。该图为数据集的每个实例显示一条线,指示该实例的预测如何随着要素值的变化而变化。PDP 是 ICE 图中所有线条的平均值。ICE 图允许可视化边际效应的变化,能够检测异质效应。
累积局部效应(ALE)图
当一个特征与其他预测因子高度相关时,PDP 会出现严重的问题,因为实际上不太可能发生的合成实例预测会被平均化(例如,年龄为 16 而教育程度同时为 10 是不太可能的)。这可能会在估计该特征的效果时产生显著的偏差。ALE 图除了计算速度更快之外,还是计算特征对模型预测的影响的无偏解决方案,因为它们评估的是其条件分布。也就是说,对于网格的值 x1,他们仅使用值与 x1 相似的实例的预测进行估计,从而避免使用现实中不可能的实例。
此外,为了估计特征对预测的影响,他们不使用平均值(将特征的影响与所有相关预测因子的影响相混合),而是计算预测之间的差异。
冰和啤酒的区别
ICE 图解决了 PDP 可能呈现的异质效应的问题,但是没有解决由于相关特征引起的偏差。相反,ALE 图解决了偏倚问题,考虑了特征的条件分布及其与其余预测因子的相关性。
3.5 -全球代理模型
全局代理模型是一种可解释的模型,它被训练成近似黑盒模型的预测。我们可以通过解释代理模型得出关于黑箱模型的结论。用 Christoph Molnar 的话说:“通过使用更多的机器学*来解决机器学*的可解释性!”
我们将尝试使用逻辑回归和决策树作为全局代理模型来近似 XGBoost。
- 逻辑回归
# Train
log_clf = LogisticRegression().fit(X_train, y_train)# Predictions
y_pred_train_log = log_clf.predict(X_train)
y_proba_train_log = log_clf.predict_proba(X_train)[:, 1]
y_pred_test_log = log_clf.predict(X_test)
y_proba_test_log = log_clf.predict_proba(X_test)[:, 1]# R-squared
print('R-squared Train RL-XGB: ', r2_score(y_proba_train_log, y_proba_train))
print('R-squared Test RL-XGB: ', r2_score(y_proba_test_log, y_proba_test))
图 11:逻辑回归和 XGBoost 预测之间的 R 平方。
训练集和测试集的 R 平方都是负的。当拟合比简单地使用平均值更差时,就会发生这种情况。所以得出结论 Logistic 回归不是一个好的替代模型。
- 决策树
# Train
tree_clf = tree.DecisionTreeClassifier(random_state=0, max_depth=4).fit(X_train, y_train)# Predictions
y_pred_train_tr = tree_clf.predict(X_train)
y_proba_train_tr = tree_clf.predict_proba(X_train)[:, 1]
y_pred_test_tr = tree_clf.predict(X_test)
y_proba_test_tr = tree_clf.predict_proba(X_test)[:, 1]# R-squared
print('R-squared Train DT-XGB: ', r2_score(y_proba_train_tr, y_proba_train))
print('R-squared Test DT-XGB: ', r2_score(y_proba_test_tr, y_proba_test))# Metrics
clf_metrics(y_pred_train_tr, y_proba_train_tr, y_train, y_pred_test_tr, y_proba_test_tr, y_test)
图 12:决策树和 XGBoost 预测之间的 R 平方,以及前者的性能度量。
XGBoost 模型预测中的方差与决策树非常接近,因此它可以作为解释主模型的代理模型。事实上,性能指标也非常接近原始模型。
值得注意的是,虽然 XGBoost 预测的方差可以由决策树 很好地解释,但不能保证 后者会以与前者相同的方式使用这些特性。可能发生的情况是,树在输入空间的某些区域中正确地逼近 XGBoost,但是在其他区域中表现得完全不同。
将对结果树进行分析,以便评估所使用的特征是否对应于迄今为止已经检测到的最重要的特征:
# Plot tree
fig, ax = plt.subplots(figsize=(30, 10))
tree.plot_tree(tree_clf, feature_names= X_train.columns.to_list(), ax=ax, filled=True)
plt.show()
图 13:经过训练的决策树
该树用于估算收入的 5 个特征,按重要性排序为:
1.已婚 _1
2。教育编号
3。资本收益。资本损失
5。年龄
这些特征对应于其他方法检测到的最重要的特征。
4.本地重要性
局部代理模型是用于解释黑盒机器学*模型的个体预测的可解释模型。
4.1 -局部可解释的模型不可知解释(LIME)
LIME 分析当输入数据发生变化时,模型预测中会发生什么。它生成一个新的数据集,其中包含置换样本及其来自原始模型的相应预测。在这个合成集上,训练可解释的模型(逻辑回归、决策树、套索回归等)。),然后通过采样实例与感兴趣实例的接近度对其进行加权。
对实例 X 的解释将是最小化损失函数(代理模型的预测和原始模型的预测之间的性能测量,例如 MSE)的代理模型的解释,保持模型的复杂度低。
# Generate explainer
explainer = lime.lime_tabular.LimeTabularExplainer(X_train.values, mode='classification',feature_names=X_train.columns.tolist(), discretize_continuous=False, random_state=1984)# Generate explanation for instance i
i = 546
exp = explainer.explain_instance(X_test.values[i], clf_xgb_array.predict_proba)# Plot
fig = exp.as_pyplot_figure();
图 14:每个特征在个体预测中的相对重要性。
# Generate explanation for sample
sp_obj = submodular_pick.SubmodularPick(explainer, X_test.values, clf_xgb_array.predict_proba, sample_size=3, num_exps_desired=3)[exp.show_in_notebook() for exp in sp_obj.sp_explanations]
图 15:对抽样实例的单独解释。
据观察,在所有区分阶级的个人解释中,最有影响力的特征是资本收益。接下来,根据具体情况,最相关的预测值是已婚、教育编号、年龄和性别。这些特征与在全局重要性算法中识别的特征相同。
4.2 -沙普利添加剂解释(SHAP)
SHAP 是一种解释基于沙普利值计算的个人预测的方法,一种来自联合博弈论的方法。它试图回答问题“相对于平均预测,每个特征值对预测的贡献有多*?”。为此,Shapley 值根据“玩家”对“总支付”的贡献将“支付”分配给“玩家”。玩家在联盟中合作,并为此类合作获得一定的奖励。
在机器学*上下文中,“游戏”是数据集实例的预测任务。 “总支付额”是对该实例的预测,减去对整个数据集的平均预测。 “玩家”是实例的特性值,它们联合起来接受“报酬”(预测)。Shapley 值是所有可能组合的特征值的平均边际贡献。它表示“总支出”(预测)如何在所有“玩家”(特征值)之间分配。
SHAP 带来的一项创新是,Shapley 值解释被表示为一种附加特征归因方法,即线性模型。就这样, SHAP 把石灰的好处与沙普利值 联系起来。
# Create explainer
explainer = shap.TreeExplainer(clf_xgb_df, model_output='probability', feature_dependence='independent', data=X_test)# Generate explanation for instance i
i= 150
data_for_prediction = X_test.iloc[i, :]shap_values = explainer.shap_values(data_for_prediction)
shap.initjs()
有几种方法可以把 SHAP 的解释形象化。我们将在本文中讨论其中的两个:力图和决策图。
力图
shap.force_plot(explainer.expected_value, shap_values, data_for_prediction)
图 16:单个实例的 SHAP 力图解释
力图显示了每个特征对预测的影响。有两个相关值需要注意:输出值(实例的模型预测)和基本值(整个数据集的平均预测)。更*的条意味着更高的影响,颜色指示特征值是否将预测从基础值向 1 (红色)或 0 (蓝色)移动。****
决定情节
**shap.decision_plot(explainer.expected_value, shap_values, data_for_prediction)**
图 17:单个实例的 SHAP 决策图解释
决策图显示了与力图基本相同的信息。灰色垂直线是基值,红线表示每个特征是否将输出值移动到比平均预测值更高或更低的值。
这个图可以比前一个图更清晰和直观一点,尤其是当有许多特征要分析的时候。在力图中,当预测因子的数量很高时,信息可能看起来很浓缩。
结论
这篇文章旨在帮助数据科学家更好地理解他们的机器学*模型是如何工作的,并能够以一种清晰的方式解释这些结果。它对于调试模型和确保它们按预期工作也很有用。
我们已经介绍了可解释性方法的不同分类(内在的/事后的,模型特定的/模型不可知的,局部的/全局的)并且我们使用了几个库和技术来评估全局和局部的重要性。**
总之,使用的库和技术有:
- XGBoost :特性重要性
- ELI5 :排列重要性
- PDPBox :部分依赖图
- 全局代理模型:逻辑回归,决策树
- 石灰:当地的重要性
- SHAP :特征重要性、概要图、部分依赖图、局部重要性
那么,哪一个库是解决 ML 模型可解释性的最好的库呢? 在我看来,使用几个库和技术有助于建立模型输出的可信度(前提是结果一致)。然而,如果我必须选择一个,我肯定会去 SHAP。**
SHAP 在可解释机器学*领域做出了巨*贡献。这是因为在这里全局解释与个体解释是一致的,因为 Shapley 值是全局解释的“原子单位”(在博弈论中有坚实的理论基础)。例如,如果 LIME 用于局部解释,而 PDP 或排列重要性用于全局解释,则这两种方法之间没有共同的理论基础。
我希望这篇文章能达到它的目的,作为破解黑盒模型的一般指南。完整代码可以在我的 GitHub 中找到
在我的下一篇文章中,我将讨论模型公平性、,它在过去的几年中已经获得了越来越多的关注。该领域旨在评估该模型在处理数据中预先存在的偏见时的公平性: 职位匹配系统在 CEO 面试中偏向男性候选人,因为这与历史数据相匹配,这公平吗?
敬请期待!
揭秘印度尼西亚顶级电子商务——来自 Twitter 的观点
印度尼西亚顶级电子商务的 Twitter 分析(Tweets,Followers,和 Following)
封面由作者创作,标识取自 Tokopedia.com、Shopee.co.id、Bukalapak.com、Lazada.co.id、Blibli.com 和 Twitter.com
印度尼西亚
印度尼西亚,一个截至 2020 年 5 月 9 日(星期六)总人口为 273,097,566 的国家来源是一个电子商务初创公司成长的充满希望的地方。根据统计,2020 年印尼电子商务市场的收入约为 269.22 亿美元,预计年增长率(2020–2014)为 16.9%,到 2024 年市场规模将达到 503.61 亿美元。市场最*的部分是时装(2020 年为 67.71 亿美元)。由 Cuponation 进行的一项研究报告了 2019 年印尼排名前 5 的电子商务网站,分别是 Tokopedia、Shopee、Bukalapak、Lazada、 和bli bli(Shopee 和 Lazada 分别指 Shopee Indonesia 和 Lazada Indonesia)。
数据
使用的数据是从 5 家电子商务公司的 Twitter 账户收集的推文,从他们的第一条推文开始,直到 2020 年 4 月 30 日以及截至 2020 年 4 月 30 日的关注者和追随者列表。一系列预处理技术已经应用于收集的推文。在本文的其余部分,电子商务的名称指的是它的 Twitter 账户。
免责声明: 每个电子商务数据都是从每个电子商务 Twitter 账户中收集的,并以相同格式的代码平等对待。本文中的所有数字都是基于收集的数据。与 Twitter 上实际数字的差异是由于 Twitter 在数据采集过程中的一些政策限制。
概观
就推文数量而言,Shopee 以 266,233 条推文领先,是 Tokopedia 的 9 倍,尽管 Shopee 是该国的最新玩家。排名前 5 的电商,关注的账号都不到 10 个。与此同时,Blibli 拥有最多的追随者。下面将提供更详细的分析。
表 1 各电商概况统计。包括转发、提及和回复*
第一条推特
千里之行始于足下——老子
让我们回到他们都开始的时候。
图 1 各电商的第一条推文;tokopedia(2010 年 3 月 25 日)、Blibli(2011 年 4 月 30 日)、Bukalapak(2012 年 2 月 11 日)、laza da(2012 年 3 月 15 日)和 Shopee Indonesia(2015 年 5 月 12 日)
这条时间线不仅显示了他们发布第一条推文的时间,还显示了他们的内容。Tokopedia 正在宣布来自 East Ventures 的投资,Bukalapak 正在他们的市场上推广一种产品,Shopee 正在发送介绍性的推文,通知他们在该国的存在。
值得注意的追随者
如表 1 所示,所有电子商务只有一位数的追随者。一些电子商务跟随其创始人/联合创始人/首席执行官,而一些电子商务跟随知名公众人物或团体,如印度尼西亚共和国总统和克里斯蒂亚诺罗纳尔多。表 2 列出了以下著名账户的详细情况
表 2 各电商列表如下
我们来谈谈这个数字
收集的推文包括一般推文、回复、提及和转发。为了简化分析,我将它们分为 3 类,即一般推文、提及(回复+提及)和转发。关于他们差异的全面解释可以在推特网站上看到。
趋势
这一小节将详细阐述一般推文、提及和转发的趋势。图 2 显示,在 2010 年至 2012 年期间,就一般推文而言,Bukalapak 是 Twitter 中最活跃的。自 2013 年以来,Blibli 一直保持着最活跃的地位,尽管自 2015 年以来所有电商(Shopee 除外)都呈下降趋势。很有可能在 2020 年底,Shopee 将拥有最多的通用推文。
图 22010 年至 2020 年各电子商务的一般推文趋势
图 3 显示了每个帐户与其追随者之间的活跃程度。2015 年,Lazada 在提及其他 Twitter 账户/其关注者方面最为活跃。这包括回答其追随者的问题或与追随者的简单互动。然而,在接下来的一年中,这种参与水平急剧下降。与此同时,自进入中国以来,Shopee 与粉丝的互动逐年增加,并在 2019 年达到峰值,约有 12.7 万次提及。
图 32010 年至 2020 年各电商的提及趋势
就转发而言,Bukalapak 在最初几年非常活跃,但此后数量呈下降趋势。Tokopedia 在 2015 年也曾一度领先,但之后数量也*幅下降。虽然其他人在转发中变得不那么活跃,但 contrasty Shopee 的转发数量在过去三年中呈上升趋势。
图 42010 年至 2020 年各电商的转发趋势
比例
为了简化分析并确保数据的相关性,从这一小节开始,我将只使用 2015 年至 2020 年的数据。
图 52005 年至 2020 年间来自各电子商务的推文类型比例
圆圈的*小代表推文总数。Tokopedia 和 Blibli 显示了类似的趋势,因为自 2015 年以来,它们的总推文数量逐年下降。有趣的是,Shopee 和 Lazada 有相反的趋势,Shopee 显示推文增加,Lazada 减少。与此同时,总体而言,Bukalapak 的趋势正在下降,尽管与前几年相比,2019 年他们更加活跃。
圆圈还显示了每个账户的推文类型的比例。自 2016 年以来,超过 90%的 Shopee 推文内容被提及,这一比例每年都在增加,2019 年达到 97.66%。与此类似的是 Lazada,他也花费了*约 80-87%的推文进行提及。这一发现表明 Shopee 和 Lazada 分配了相当多的时间与他们的追随者交流。相比之下,Tokopedia 和 Blibli 的推文*约有 75%是普通推文。从 2015 年到 2018 年,Bukalapak 显示一般推文的比例越来越高。然而,自 2019 年以来,他们转向更高比例的提及,而不是一般的推文。
点赞、回复和转发
从这一小节开始,我将只使用一般推文的数据,以便简化和确保分析的相关性。
由于有许多推文的粉丝 0 赞和/或 0 回复和/或 0 转发,我决定排除符合这些标准的推文。图 6 显示了 2015 年至 2020 年间每个电子商务推文的点赞、回复和转发的中位数。
很明显,在过去的 3 年中,Shopee 在所有变量中一直领先,其次是 Tokopedia,而 Blibli 和 Tokopedia 一直是垫底的两个。前 2 名和后 2 名之间的差距非常*。
图 62015-2020 年各电商一般推文的点赞、回复、转发的中位数
内容
理解推文的内容很重要。就内容而言,有 3 种不同类型的推文,即纯文本推文、带文本和照片的推文以及带文本和视频的推文。
图 7 显示 Tokopedia 和 Lazada 有几乎相似的偏好。他们更喜欢发布带照片的推文,除了 2020 年 Tokopedia 在只有文字的推文中占了更*的比例。与此同时,布卡拉帕克在过去四年里一直专注于带照片的推特。另一方面,Shopee 从 2019 年开始从带照片的推文转向文字。自 2015 年以来,Blibli 更喜欢只有文本的推文,并继续每年增加其比例,并在 2020 年 4 月达到 86.10%。同样清楚的是,他们都在试图增加带有视频的推文的比例。
图 72015 年至 2020 年间,基于各电子商务内容的推文比例
内容与印象的关系
内容会影响推文的印象吗?
图 8 各电商基于内容的点赞分布
根据 likes_count 的中位数,带有照片的推文获得了更多的喜欢。然而,这并不适用于 Shopee,它对所有不同类型的内容都获得了几乎相似的印象。
图 9 每个电子商务基于内容的回复分布
从图 9 来看,根据 replies_count 的中位数,如果推文包含视频,Bukalapak、Lazada 和 Blibli 会获得更多回复。另一方面,Tokopedia 和 Shopee 在只有文字的推文中获得了更好的参与度。
图 10 基于每个电子商务内容的转发分布
就转发而言,如果推文中包含视频,所有电子商务都会获得更多的转发。与 replies_count 类似,如果推文仅由文本组成,Bukalapak、Lazada 和 Blibli 获得的转发量最少。
从图 8、9 和 10 中可以发现一个有趣的现象,Shopee 倾向于在只有文本的 tweet 上有更好的参与度。他们的推文可能包含吸引粉丝注意的有趣内容。然而,还需要进一步的分析来证实这一点。
日期和时间
一个有趣的发现,每个电子商务都有自己最喜欢的日子。与此同时,周日是他们所有人发微博数量最少的一天。这可能是因为周末。
图 11 每个电子商务工作日的推文数量
上午 10 点(GMT +7)是 Shopee 和 Lazada 最喜欢的时间,而晚上 7 点(GMT +7)是 Bukalapak 和 Lazada 最喜欢的时间。然而,Tokopedia 和 Bukalapak 的分布模式相似,Shopee 和 Lazada 的分布模式相似。
图 12 每个电子商务每小时的推文数量
可以对此做进一步的分析,例如,哪一天/时间会获得更多的关注,但现在,我将限制在这里
让我们来谈谈推文
长度
为了分析推文的长度,我删除了停用词。图 13 显示了*多数 tweets 的长度在 12-14 个单词之间。然而,Shopee 和 Bukalapak 似乎具有双峰分布。
图 13 各电商的推文长度分布
标签
图 14 显示从 2015 年到 2020 年,Shopee 与他们的 hashtag ( #shopeeid )一致。对于 Tokopedia 本身来说,在 2018 年和 2019 年之间,他们在 #mulaiajadulu 上开展了活动,但在 2020 年,他们一直在推广 #tokopediasaja 。在过去的两年里,Bukalapak 一直专注于他们的标签。在过去的 5 年里,Lazada 几乎在所有的推文中都加入了#lazadaid。最后,对于 Blibli 来说,在 2020 年,他们将推出 #karenakamuno1 ,这与他们的客户满意度品牌有关。
图 142015-2020 年各电商年度热门标签
表情符号
Shopee 倾向于在几乎一半的推文中包含表情符号,而其他的仍然低于 5%。一个有趣的未来分析是看到表情符号的存在与印象之间的关系,但不是在这个写作中😀。
图 15 各电子商务中使用和不使用表情符号的推文比例
我们来谈谈追随者
在概述部分,我已经提到了每个电子商务的总关注者。在这里,我合并了所有关注者,唯一关注者总数为 1,317,933 。然后,我创建了一个维恩图来查看每个电子商务追随者之间的交集,如图 16 所示。所有电子商务共有 63 080 名关注者。从数量变量来看,Blibli 的忠诚者最多,而 Bukalapak 的忠诚者最少。Lazada 和 Blibli 共有 94060 名共同关注者,是其他两对中最多的。另一个发现是,Tokopedia、Bukalapak、Lazada 和 Blibli 共有 71,568 名共同追随者。这些追随者似乎是电子商务的早期追随者,这可能是他们不跟随 Shopee 的原因,Shopee 是印度尼西亚最新的电子商务玩家。更多信息可以在图 16 中看到
图 16 所有电商的追随者分布维恩图。
有很多更深入的分析可以在追随者身上进行,例如,通过执行图形分析来找出一个电子商务的忠诚者到其他电子商务的距离,然而,对于这篇文章,我将把它限制在这里。也许在未来,我可以更深入地了解这一点:)
评论
本文无意偏袒任何一方,不做任何判断,也不提供任何指导或建议。这篇文章的目的仅仅是分析和描述印度尼西亚五*电子商务的 Twitter 账户。我希望你喜欢并发现这篇文章有用!😀欢迎反馈。 Linkedin
引擎盖下—决策树
通过观察选择正确分支的数学过程,理解决策树的工作原理,从而得到最好的树
弗拉季斯拉夫·巴比延科在 Unsplash 上的照片
在后台
这是一系列文章中的第三篇,在这一系列文章中,我们将理解各种 ML 算法的“幕后”工作,使用它们的基本数学方程。
有这么多优化的实现,我们有时太关注库和它提供的抽象,而太少关注进入模型的底层计算。理解这些计算往往是一个好模型和一个伟*模型的区别。
在本系列中,我将重点放在手工实现算法上,以理解其背后的数学原理,这将有望帮助我们训练和部署更好的模型。
决策图表
决策树是最受欢迎的机器学*算法之一,因为它们简单直观。它形成了许多其他算法的基础,如随机森林和梯度提升机器,这些算法是*多数数据科学竞赛的主要内容,直到几年前,仍然是最通用、最容易理解的 ML 算法之一。
决策树的工作方式非常类似于我们人类通过问一系列问题来做决定的方式。例如,我们经常预测(或者曾经预测,直到我们可以在手机上查看当天的天气预报)外出时是否要带伞,基于各种决策点,例如—
- 天气多云吗?
- 现在是几月?
- 过去几天的这个时候下雨了吗?
- 我要走多远?
- 我有雨伞吗?(这应该是第一个问题)
基于对其中一个或多个问题的回答,我们出门时可能带伞,也可能不带伞。
决策树(用于分类)以类似的方式工作,一次询问一个变量,以找到数据中不同类别之间的最佳分割。
考虑以下数据,具有两个输入特征— X1、X2 和一个二元(0/1)目标特征— Y
有 10 行的示例数据
该算法遵循以下步骤来找到这种数据的最佳分割。
0.两个类的样本数据
- 对于每个输入变量,计算不同阈值下的数据分割。
1.计算不同阈值下的分割
2.选择给出最佳分割的阈值。
2.选择能产生最佳分割效果的分割
3.重复步骤 1 和 2,直到收敛。
3.计算剩余数据的拆分
4.组合所有选定的阈值,形成一个用于数据分类的规则链(树)。
4.组合选择的阈值以形成决策树
让我们使用示例数据更深入地研究这些步骤。
1.对于每个输入变量,计算不同阈值下的数据分割
这是决策树算法的第一步,有两个主要的决策点
- 我们如何选择阈值变量?
- 我们如何选择阈值?
对于第一种,最常见的策略是考虑所有变量,并检查哪个变量+阈值组合给出了数据的最佳分割。
第二点是事情变得更有趣的地方。可以有各种方法来选择每个变量的阈值—
- 在每个变量的范围内选择随机值。例如,对于变量 X2 ,值的范围在 3 和 9 之间(假设最后两行被保留用于验证我们的“模型”)。我们可以选择 3、4、5、6、7、8 和 9 的整数阈值。
很明显,当变量的范围增加到数百个时,或者在处理浮点特性时,这将不能很好地伸缩。在 3.6 和 3.7 之间,我们可以适应多少个浮点阈值? - 另一种方法是对变量中的唯一值进行排序,并在每个连续值之间选择一个阈值—使用最*值/最小值/平均值。这将把我们的搜索空间缩小到数据中存在的值的子集,因为阈值是基于数据中存在的值而不是整个范围的值来选择的。
对于变量 X2 ,阈值看起来像这样
可变 X2 的阈值策略
类似地,对于 X1 —
有一种更好的方法来选择进一步降低搜索空间的阈值。我们将在理解“最佳分割”以及如何使用这些阈值形成决策节点之后再来看它。
让我们继续选择阈值(平均值)值作为我们的阈值。
2。选择给出最佳分割的阈值
为了选择给出最佳分割的阈值,我们首先需要定义什么是“最佳分割”。
为了直观地理解它,我们可以看到在下面的图像中, X1=t2 看起来像是最佳分割,因为分割的左半部分只包含蓝色点(纯节点)。换句话说,可以说如果随机选择的点有 X1 < t2 ,那么这个点就是蓝色的。我们不能在 t1 或 t3 做出同样的推断。
因此,在每个阈值处,我们检查阈值两侧的点集的分布是否比分割前更好。我们选择能够提供最佳数据分布的阈值—理想情况下,该阈值将单个类(在我们的示例中为蓝色)的*多数点放在一边,同时在分割的那一边保留其他类(在我们的示例中为红色)的少数(最好是 0 个)实例。
从数学上来说,我们在拆分之前检查数据的杂质测量值(每类点的密度)与拆分的平均杂质测量值,并选择产生最*差异的阈值。对于我们的情况,让我们考虑“基尼系数”,定义为
其中 k 为类数,pᵢ*t5】为类概率 i.*
对于我们的数据,
为了计算我们在第一步中选择的基尼系数,我们首先将数据按照 X2 变量排序
按要素 X2 排序的数据
我们的数据在 0 类和 1 类各有四个点。因此,分割前的基尼系数可以计算为
如果我们考虑阈值为 X2=5 的分割,数据分布看起来像—
我们最终在拆分的每个部分都有 4 行。我们可以计算两种分裂的基尼系数——
因此,我们可以将杂质的总体变化(减少)计算为—
这种增益变化(δG)也称为“信息增益”。
我们对 X2 的所有阈值进行相同的计算
上表显示,使用特征 X2 进行决策时,最佳可能增益为阈值 7。
我们需要为另一个特征重复这个过程— X1 。按照我们为 X2 所做的一系列步骤,我们得到—
我们注意到,如果我们在 X1=5.5 处分割数据,我们会得到相同的最高增益值(0.340)。
在这种情况下,我们为多个特征获得相同的最*增益,选择左边的第一个特征(如在原始数据中)。根据我们案例的规则,“最佳分割”发生在 X1=5.5 。
因此,当我们的决策节点在 X1=5.5 时,我们可以开始创建一个“决策树”,就像—
3.重复步骤 1 和 2,直到收敛
我们对每个拆分数据重复步骤 1 和 2,直到我们的所有拆分都包含纯样品(杂质为零)。
根据上一步中在 X1=5.5 处的分割生成的数据集之一(分割 1)已经是纯的——它只有来自类 0 的样本。因此,我们可以说,这个分支已经达到了最佳点,我们不需要再分裂它了。
我们使用来自 Split 2 的数据来选择进一步的阈值—
来自分割 2 的数据
如果我们遵循对特征的唯一值进行排序并在连续值的平均值处选择阈值的策略,我们将最终得到 X1 的阈值为 6.5、7.5 和 8.5,而 X2 的阈值为 3.5、5 和 7.5。
快速目测显示,对于这两个变量,前三次分割都是次优的。最佳分割在 X1=8.5。
这使我们回到了选择最佳策略来选择阈值的问题上。
现在我们已经看到了最佳分割是如何计算的,并且考虑到我们一次只查看一个特征,我们可以说理想的阈值候选位于类别之间的过渡点即当特征被排序时,只有那些目标从 0 变化到 1 或相反的点才是好的决策点,因为任何其他位置都意味着从相同类别中分割点,这将导致我们两个分割中杂质的增加。
因此,我们可以看到,当选择 X1 的阈值时,我们的理想候选值是 8.5,因为当从 X1=6 移动到 X1=7 或从 7 移动到 8 时,目标值(1)没有变化。
因此,我们在 X1=8.5 — 的增益
同样,对于 X2 ,我们唯一的阈值候选是 X2=6 ,给我们一个 0.32 的等价增益值。
这给了我们在 X1=8.5 的第二个决策节点,使我们的决策树呈现为—
因为我们所有的节点都是纯的,所以不需要进一步的分裂。
因此,我们的终端节点是具有类 0 的
Split-1、具有类 1 的
Split-3 和具有类 0 的
Split-4。
我们可以通过遍历树来预测新变量的类别,并评估它属于哪个节点。
让我们通过测试我们在训练中没有使用的两行数据来验证我们的决策树的准确性
对于第一个数据点(3,5),由于 X1 小于 5.5,该点以 Split-1 结束,预测类别为 0;与我们实际的目标类相同。
类似地,对于另一个端点,我们看到样本以 Split 3 结束,预测值为 1,这与实际的类相匹配。
仅此而已。在其核心,这就是决策树算法所做的一切。
这真的是决策树所做的全部吗?
“在引擎盖下”是本系列的焦点,我们一次一个节点地查看决策树构建的基础,以适应我们的数据。
虽然这确实是决策树的核心功能,但是有一些复杂的地方我已经跳过了,比如—
- 为什么基尼不纯?
- 还有哪些其他杂质指标可用于选择最佳分离度?
- 决策树过度拟合数据的趋势。
- 特征重要性——我们可以看到,我们的两个决策点都只依赖于变量 X1 而不是 X2。这是否意味着我们可以抛弃 X2?
- 修剪和聚集节点。
- 预测概率是如何计算的?
我将在一个平行系列中介绍这些概念,重点是我们在这个系列中介绍的不同 ML 算法的各种复杂性。
下一步是什么?
在本系列的下一篇文章中,我们将继续讨论决策树,并在决策树的引擎盖下寻找回归,并了解如何使用决策树进行拟合,并输出连续的目标值。
幕后——逻辑回归
理解逻辑回归背后的数学原理,手动创建分类模型
乔尔·罗兰在 Unsplash 上拍摄的照片
这是一系列文章中的第二篇,在这一系列文章中,我们将使用各种 ML 算法的基本数学方程来理解它们的“幕后”工作。
有这么多优化的实现,我们有时太关注库和它提供的抽象,而太少关注进入模型的底层计算。理解这些计算往往是一个好模型和一个伟*模型的区别。
在本系列中,我将重点放在手工实现算法上,以理解其背后的数学原理,这将有望帮助我们训练和部署更好的模型。
注意——本系列假设您了解机器学*的基础知识以及我们为什么需要它。如果没有,请阅读本文来了解我们为什么以及如何利用 ML。
逻辑回归
逻辑回归建立在线性回归的概念上,其中模型产生一个将输入特征(X)与目标变量(Y)相关联的线性方程。
逻辑回归算法的两个主要区别特征是—
- 目标变量是离散值(0 或 1 ),与连续值不同,线性回归在计算线性方程的输出后增加了一个额外的步骤来获得离散值。
- 该模型建立的方程侧重于分离 target 的各种离散值,试图确定一条线,使所有 1 都落在该线的一侧,所有 0 都落在另一侧。
考虑以下数据,具有两个输入特征— X1,X2 ,和一个二进制(0/1)目标特征— Y
逻辑回归将试图找到参数 w1 、 w2 和 b 的最佳值,因此—
这里,函数 H ,也称为激活函数,将 y 的连续输出值转换为离散值。这将确保等式能够输出类似于输入数据的 1 或 0。
该算法通过以下步骤找到这些最佳值—
0.包含二进制(0,1)目标的样本数据
- 将随机值分配给 w1 、 w2 和 b.
将随机值分配给 w1、w2 和 b
2.选取数据的一个实例,并计算连续输出(z)。
选择一个实例并计算 z
3.使用激活功能 H()计算离散输出(ŷ)。
使用 H() 计算ŷ
4.计算损失——当实际目标是 1 时,我们的假设是否让我们接近 1?
计算损失
5.计算 w1 、 w2 和 b 的斜率——我们应该如何改变参数以更接近实际输出?
6.更新 w1 、 w2 和 b 。
计算梯度并更新参数
7.重复步骤 2–6,直到收敛。
用另一个实例重复
让我们详细看一下这些步骤。
1。给 w1 、 w2 和 b 分配随机值
我们从给模型参数分配随机值开始—
2.选取一个数据实例,计算连续输出(z)
让我们从数据的第一行开始——
输入我们假设的参数值,我们得到—
3.使用激活函数 H() — 计算离散输出( ŷ )
如果您一直在关注“幕后”系列,您会注意到步骤 1 和 2 与线性回归中的步骤完全相同。这一点会经常出现,因为线性回归形成了*多数算法的基础——从简单的 ML 算法到神经网络。
都是 Y = MX + c 供电
有了我们的基础,我们现在关注的是什么使逻辑回归不同于线性回归,这就是“激活函数”的作用。
激活函数在具有连续目标值的线性世界和“逻辑”世界之间架起了一座桥梁(就像我们现在工作的这个世界!)具有离散的目标值。
激活函数的简单形式是阈值函数(也称为“阶跃函数”)—
但是,和所有 ML 的事情一样,“不可能那么简单”。
像这样的简单阈值函数的一个主要缺陷是,每次我们建立分类模型时,我们必须手动选择正确的阈值(基于输出的范围)。根据输入变量和权重,我们的值可以有任意范围。
另一个不利于这样一个简单函数的事实是,它在 z=threshold 处不可微。我们需要遵循损失的管道- > 梯度 - >权重更新,因此我们需要将这里的事情变得复杂一点,这样当我们计算损失的导数(梯度)时,我们的生活就简单了。
这就是稍微修改过的阈值版本 Sigmoid 函数——
就像阈值处理一样,sigmoid 函数将实数转换为 0 和 1 之间的值,中点在 z=0 处(这样就解决了我们的第一个问题)。从图中可以明显看出,该函数在所有点上都是平滑的,这在计算梯度时将是有益的(从而解决了我们的第二个问题)。
sigmoid 函数的另一个优点是,它告诉我们估计值有多接近 0 或 1。这有助于我们更好地理解模型损失(错误)—对于实际值为 1 的行,预测值为 0.9 比预测值为 0.7 要好。当使用阶跃函数时,这种复杂性就消失了。
让我们使用 sigmoid 函数来计算我们的估计输出—
4.计算损耗
基于线性回归,我们可以选择平方误差来表示我们的损失,但这总是会使误差变小,因为我们的值在 0-1 范围内。我们需要一个函数,当我们的假设提供一个接近于 0 的值,而实际是 1 时,输出一个*的损失,反之亦然。
一个这样的函数是 log-loss,它使用我们的预测输出(z)的对数变换来计算损失。
我们将损失函数定义为—
当输入接近 0 时,对数函数的输出接近负无穷*,当输入为 1 时,输出为 0。负号反转对数值,以确保我们的损失位于 0 到无穷*之间。
我们可以把这个函数写成一个方程—
我们现在可以计算我们的假设给我们带来的误差—
5.计算 w1 、 w2 和b的斜率
现在,我们通过计算每个参数对损耗的梯度来计算每个参数对预测输出(和损耗)的影响。
这就是管道中的微分函数使我们的生活变得简单的地方,为我们提供了每个参数的导数
6.更新 w1 、 w2 和b
梯度告诉我们,我们应该改变多少参数假设,以减少损失,并使我们的预测输出更接近实际输出。
因为我们一次“训练”一个实例的模型,所以我们希望限制这个单独实例的损失对我们的参数的影响。因此,在更新参数之前,我们使用“学*率(η)”—
这完成了训练的一个迭代。
为了获得最佳参数值,我们重复上述步骤固定次数,或者直到我们的损失停止减少,即收敛。
让我们通过另一个迭代,使用更新的参数—
看来这次我们的损失增加了。这显示了二进制实例如何将权重拉向相反的方向,以在两个类之间生成决策边界。
重复这些步骤,再进行几次迭代*,从前 4 行中随机采样数据,我们得到最佳权重如下—
对我们最后一行数据(我们在训练时没有使用)使用这些值,我们得到—
这非常接近我们的实际类“1”。
选择截止值为 0.5(我们的 sigmoid 曲线的中点),对于这种情况,我们的预测值为 1。
仅此而已。在其核心,这是所有的逻辑回归算法。
这真的是逻辑回归所做的“全部”吗?
“引擎盖下”是本系列的焦点,我们看了看逻辑回归的基础,一次取一个样本,并更新我们的参数以适应数据。
虽然这确实是逻辑回归的核心,但是一个好的逻辑回归模型还有很多其他的东西,比如
1.正规化——L1 和 L2
2.学*率调度
3.为什么选择乙状结肠活化?
4.缩放和标准化变量
5.多类分类
我将在一个平行系列中介绍这些概念,重点是我们在这个系列中介绍的不同 ML 算法的各种复杂性。
下一步是什么?
在本系列的下一篇文章中,我们将继续分类任务,并了解另一类算法— 决策树 ,,它们的工作方式非常类似于您和我如何使用推理来得出关于数据的结论。
*经过 5 次迭代,这是我们的损失、梯度和参数值的变化方式—
** 在本文中,我还没有介绍我们如何获得损失函数相对于参数的导数,因为推导过程非常广泛,需要单独撰写一篇文章(将在另一篇文章中介绍:)
在深度学*的掩护下
理解神经网络的循序渐进教程
我用 Alexlenail 网站创建了这张图片
上图是深度神经网络的简单架构。这篇文章的目标是了解深度学*的细节并建立自己的网络,而不是将现有的模型用作黑箱!
准备输入数据
在这篇文章中,我们将介绍一个简单的神经网络,它可以学*识别手写数字(MNIST 数据集)。目前,有各种类型的神经网络,但为了简单起见,我们将从香草形式开始(又名“多层感知器”)。
请注意,之前图表中的圆圈称为神经元。每个神经元包含一个范围在 0 和 1 之间的数字,这就是所谓的“激活”。另外,请注意这些神经元之间的连线称为权重。每个权重是一个随机生成的数字,其值介于-1 和 1 之间。
MNIST 数据集中的每幅图像仅由黑白颜色组成,尺寸为(28 x 28 = 784 像素)。如果你仍然想知道为什么神经元激活的范围在 0 和 1 之间,那是因为它保持了图像的灰度,其中 0 是完全黑色的,1 是完全白色的。最后,要将图像输入到我们的神经网络中,我们需要通过获取每行像素并将其附加到上一行像素来将其转换为像素向量,请看以下示例:
图层说明
如上所述,网络的输入将是被转换成像素矢量(确切地说是 784)的图像,这是我们的神经网络的第一层。
输出层神经元的数量应该与我们试图预测的类的数量相关联。在我们的例子中,类的数量是 10 (0,1,2,…,9)。因此,最后一层(输出层)应该由 10 个神经元组成。
隐藏层是中间的层。现在,我们姑且说我们随机选择了隐藏层的数量和神经元的数量。
目标是什么?
我们希望建立一个模型,能够预测输入图像的数字(图像分类)。但是,很难展示整个网络的逐步示例。因此,我们将从一个更简单的神经网络开始,它由三个输入节点、一个具有单个神经元的单个隐藏层和一个单个输出组成。我们的网络将会是这样的
正向传播
前向传播是从输入开始,经过神经网络及其计算,并以做出单个预测结束的过程(用 y^ 表示,读作 y-hat)。让我们从下面的向量[0.6,0.4,0.2]开始,实际输出(y)是 1。
这些数字都是从哪里来的?好了,输入节点已经给定, Z 是输入激活与权重相乘并相加的结果, y^ 是 Z 与其权重相乘的结果。最后,也给出了 y,这是期望的输出。通常,为了计算任何输出,我们将激活与权重相乘,并将它们相加。这是第一层的公式。
很明显,我们在所有的输入和权重上重复相同的乘法。因此,我们只计算总和,因为我们可能有 3 个以上的输入。
偏见
每一个神经元都会在某个时候发光。当一个神经元触发时,意味着这个神经元检测到图像中的特定特征。例如,每当一个数字为 7 的图像进入网络,几乎相同的神经元就会激活并放电(这意味着它们触发了类似的事件、类似的角度等。).然而,当激活*于 0 时,你不希望每个神经元都触发(否则所有的正激活将保持触发)。你想让它们在某个阈值后启动,比如 10,这就是所谓的偏差( b )。我们将 b 加到求和中,以控制神经元何时触发。
激活功能
到目前为止,我们的方程将产生良好的结果。但是,结果可能小于 0 或*于 1。如前所述,每次激活应该在 0 到 1 的范围内,因为这是每个图像的灰度。因此,我们需要一个函数 (f) 将 y ^的结果挤压在 0 和 1 之间。这个函数被称为激活函数,特别是 Sigmoid 函数。在 y^上调用 sigmoid将会以如下结果结束
或者你可以这样写
注:我将左侧尺寸命名为 (Z) ,这是应用激活函数后隐藏层输出的约定名称。另外,请注意,我调用了 f 而不是 Sigmoid,因为我们有许多不同的激活函数可以应用。以下是常用激活功能的列表:
- Sigmoid: 是一个在 0 和 1 之间传递输出的函数,它*量用于概率,因为这是概率的范围。
图片来自 WolframMathWorld
- 双曲正切:在某种程度上类似于 Sigmoid,范围从-1 到 1。
图片由 WolframMathWorld 提供
- 整流线性单元(RELU): RELU 是最常用的激活功能。它的范围是 0 到无穷*。如果输入为负,RELU 返回 0,否则返回实际输入: max (0,x)
图片由刘丹青 —中:拍摄
- Softmax: 是一个独特的激活函数,它采用一个由 k 个数字组成的向量,并将其归一化为 k 个概率。换句话说,它不是选择单个输出类,而是列出每个类的概率。
还有许多其他的激活函数,如漏 RELU,参数 RELU,SQNL,ArcTan 等。
计算损失
损失是什么?简单来说,损失就是模型离预测的正确答案有多远。如前所述,输出为 1,而预测输出为 0.3,因此本例中的损耗为 0.7。如果模型预测完美,那么损失为 0。因此,可以使用以下公式计算损耗( L 表示损耗)
就这样吗?基本上,是的,但有几件事有助于改善损失,对我们未来更有利:
- 绝对值:损失是模型预测与输出之间的差距。考虑有两个错误。第一个错误是 100,第二个错误是-100。将这些误差相加并求平均值得到 0,这意味着你的预测是 100%正确的,而不是。因此,我们只对正误差感兴趣。
- 平方值:考虑有两个误差(一个小误差和一个*误差)。你会更关注哪个错误?当然是更*的误差!因为更影响结果。因此,计算损失的平方值有助于我们摆脱符号,让*误差变*,小误差变小。考虑误差为 0.01 和 100。将这些误差平方,我们得到 0.0001 和 10000。区分这些错误的优先级对于了解是什么导致了错误的预测非常重要。
- 总结:在我们之前的例子中,我们计算了预测和输出之间的损失,但是如果我们有几个输出神经元呢?因此,我们计算神经网络中所有损失值之间的总和。(D 表示包含许多示例的数据集)。
- 平均值:数据集中样本的平均值。在我们的例子中,我们只有一个例子。但是现在,我们需要用它除以 d 中的例数(N)。
这个损失函数被称为均方差(MSE) ,它是最常用的损失函数之一。还有许多其他损失函数,如交叉熵、铰链、MAE 等。但是你想过成本函数是什么吗?损失函数和成本函数的区别是什么?区别在于,损失函数用于单个训练示例,而成本函数是整个训练数据集的平均损失。
恭喜你!我们完成了正向传播。但是,我们刚刚做出的预测可能不是很准确(考虑输出 1,但模型预测 0.7)。怎样才能做出更好的预测?我们不能改变输入值,但是我们可以改变权重!!维奥拉。现在你知道深度学*的秘密了。
反向传播
我们不能改变输入。然而,我们可以增加权重,然后将其乘以输入将得到更*的预测输出,比如 0.8。不断重复这个过程(调整权重),会得到更好的结果。向相反的方向改变权重称为反向传播!但是具体怎么做呢?这可以通过优化算法来实现。有梯度下降、随机梯度下降、Adam 优化器等不同的优化器。
梯度下降
梯度下降是一种优化算法,旨在通过调整权重来减少损失。当然,手动改变权重是不可能的(我们在单个神经网络中有几十甚至几百个权重)。那么,我们怎样才能使这个过程自动化呢?以及如何告诉函数调整哪个权重,何时停止?
让我们开始调整权重,检查它如何影响损失,并绘制所有结果(检查下图)。正如你所看到的,在一个特定的点(红线)是最小的损失。在红线的左边,我们必须增加重量来减少损失,而在红线的右边,我们显然需要减少重量来减少损失。主要的问题仍然是:我们如何知道一个给定点是在红线的左边还是右边(为了知道我们应该增加还是减少重量)?以及我们应该增加或减少多少重量才能更接近最小损失?一旦我们回答了这个问题,那么我们就可以减少损失,获得更好的准确性。
请注意,在简单的 2D 维度中,很容易快速到达最小点。然而,*多数深度学*模型处理的是高维度。
幸运的是,数学上有一种方法可以回答这些问题,导数(你在高中忽略的东西:)。利用导数,我们可以用损失对重量的导数来计算图上切线的瞬时变化率。
如果你不熟悉导数,前面的句子听起来像是胡言乱语,那么就把它想象成一种测量在特定点接触图形的直线的斜率和方向的方法。根据给定点的倾斜方向,我们可以知道该点是在红线的左边还是右边。
在上图中,您可以看到 1 号切线的右侧向上,这意味着它是一个正斜率。而其余的线指向下方,负斜率。此外,请注意,斜率编号 2 的梯度*于斜率编号 6 的梯度。这就是我们如何知道我们需要多少来更新权重。梯度越*,该点离极小点越远。
学*率
将点定位在红线的左侧或右侧后,我们需要增加/减少权重,以减少损失。然而,让我们说对于红线左边的一个给定点,我们应该增加多少重量?请注意,如果您*幅增加重量,那么该点可能会将最小损失传递给另一方。然后你要减轻体重,等等。随机调整权重是没有效率的。相反,我们添加了一个称为“学*率”的变量,用“η”表示,以控制如何调整权重。一般你都是从小的学*率开始,避免过了最小损失。为什么叫学*率?那么,减少损失和做出更好预测的过程基本上就是模型学*的时候。在那时,学*率是控制模型学*速度的因素。
调整重量
最后,我们将斜率乘以学*率,并从旧的权重中减去该值,以获得新的权重。看看下面的等式,你会更好地理解它
随机梯度下降
梯度下降使用整个数据集来计算梯度,而 SGD 在每次迭代中使用训练数据集的单个示例。SGD 通常比批量或标准梯度下降更快地达到收敛。批量梯度下降每次迭代使用一批训练样本。
过度拟合
当一个神经网络很深的时候,它有太多的权重和偏差。当这种情况发生时,神经网络往往会过度拟合它们的训练数据。换句话说,模型对于特定的分类任务将是如此精确,而不是一般化。该模型在训练数据中得分较高,而在测试数据中得分较低。退学是解决办法之一。
拒绝传统社会的人
避免过度拟合的一个简单而有效的方法是使用 dropout。对于每一层,都有一个脱失率,这意味着与该脱失率相关的神经元数量被停用。这些神经元将被随机选择,并在特定的迭代过程中被关闭。下一次迭代,另一组随机选取的神经元将被停用,以此类推。这有助于概括模型,而不是记住具体的特征。
我希望这篇文章是有帮助的。如果您有任何问题,请告诉我!
资源
线性回归,岭回归,主成分分析有什么联系?
引擎盖下
从保存病态回归问题到启用正则化路径的快速计算,这是引擎盖下的链接。
我正在写一个新的系列(相对较短)帖子,围绕统计学*中的基础话题。特别是,这一系列将会有意想不到的发现,很少被谈论的联系,和统计学*的幕后概念。
我的第一篇文章从岭正则化开始,数据科学中的一个基本概念。普通最小二乘(OLS)估计、岭估计和 PCA 之间简单而优雅的关系可以通过光谱分解的透镜找到。我们通过多元分析的练* 8.8.1 看到了这些关系。
这篇文章改编自我的一篇博文,省略了所有的证明。如果你喜欢 LaTex 格式的数学和 HTML 风格的页面,你可以在我的博客上阅读这篇文章。
设置
给定以下回归模型:
考虑一下 **XX** (在本文中,
和上标 T 都表示转置)的列已经标准化为均值为 0,方差为 1。那么的岭估计为
其中对于给定的 X ,λ≥0 为小的固定脊正则化参数。注意,当λ=0 时,它只是 OLS 公式。另外,考虑 var-cov 矩阵的谱分解 XX** = **GLG
。设 W = XG 为原始数据矩阵的主成分变换。
结果 1.1
如果α=g`β表示主分量的参数向量,那么我们可以表明,通过简单地用岭正则化参数对它们进行缩放,岭估计 α 可以从 OLS 估计 hat( α )中获得:*
这个结果向我们展示了两个重要的见解:
- 对于 PC 转换的数据,我们可以通过 OLS 估计值的简单元素式缩放来获得岭估计值。
- 脊正则化的收缩效果取决于λ和相应 PC 的特征值:
-较*的λ对应于每个参数的较*收缩。
——然而,给定相同的λ,对应于较*特征值的主成分得到最小的收缩。
形象化
为了演示这两种收缩效应,我绘制了收缩百分比(1-hat(α)与有序主分量以及脊正则化参数值的函数关系。从该图中可以清楚地看到两种收缩效应。
结果 1.2
从结果 1.1 可以得出,通过 var-cov 矩阵的谱分解,我们可以在 OLS 估计 hat(【β】)和岭估计【β*之间建立直接联系(因此有了本文的标题)。具体来说,我们有
结果 1.3
估计量【β】*质量的一个度量是跟踪均方误差(MSE):
现在,从前面的两个结果可以看出,岭估计的迹 MSE 可以分解为两部分:方差和偏差,并得到它们的显式公式。MSE 的精确公式的可用性允许诸如正则化路径之类的东西被容易地计算。具体来说,我们有
其中第一个分量是方差之和:
第二部分是偏差平方和:
结果 1.4
这是从结果 1.3 得出的一个快速但有启发性的结果。对轨迹 MSE 函数对λ取偏导数,并取λ=0,我们得到
请注意,当λ为 0 时,跟踪 MSE 函数的梯度为负。这告诉我们两件事:
- 我们可以通过采用非零λ值来降低轨迹 MSE。特别是,我们用一点偏差换取方差的减少,因为方差函数在λ中单调递减。然而,我们需要在方差和偏差之间找到正确的平衡,以便使总的跟踪 MSE 最小。
- 当一些特征值(l_i)较小时,通过脊正则化的道 MSE 的减少较高。也就是说,当预测量之间存在相当*的共线性时,岭正则化可以实现比 OLS 小得多的迹 MSE。
形象化
使用sklearn.metrics.make_regression
函数,我生成了一个包含 50 个样本和 10 个特征的噪声回归数据集。特别是,我要求*多数方差(在 PCA 意义上)只能由这 10 个特征中的 5 个来解释,即最后 5 个特征值相对较小。这是正则化路径和系数误差图。
从图中,我们可以清楚地看到:
- 增加λ会使每个系数向 0 收缩。
- OLS 过程(两个图的左侧)产生错误的(并且具有*的方差)估计。估计量 MSE 明显*于岭回归的 MSE。
- 在岭估计系数的均方误差最小的 1 附近发现一个最佳λ。
- 另一方面,*于和小于 1 的λ 值是次优的,因为在这种情况下它们会导致过度正则化和欠正则化。
绘制该图的脚本附后。
摘要
通过光谱分解的镜头,我们看到线性回归估计、岭回归估计和 PCA 之间有一个简单而优雅的联系。特别是:
- 结果 1.2 表明,当你有数据 var-cov 矩阵的谱分解和通常的线性回归估计时,你可以通过简单的矩阵计算获得每个岭正则化参数值的岭估计。我们刚刚避免了很多矩阵求逆!
- 我们对岭估计的迹 MSE 进行了偏差-方差分解。很明显,我们总是可以通过增加正则化强度来减小估计量的方差。
- 虽然轨迹 MSE 函数在λ=0 处的梯度是负的,但是我们上面的可视化表明λ的值需要仔细选择,因为它可能导致过度正则化或欠正则化。
感谢您阅读本文!如果你有兴趣了解更多关于统计学*或数据科学的知识,你可以看看我下面的其他文章。尽情享受吧!
直觉、插图和数学:它如何不仅仅是一个降维工具,为什么它在现实世界中如此强*…
towardsdatascience.com](/linear-discriminant-analysis-explained-f88be6c1e00b)*
低估了 Tableau 的组合功能—点、线串和多边形映射
Tableau 中的地理空间分析-无需地理编码/底图服务即可工作+在单一设置中渲染混合几何类型
完成了我的一个空间分析项目后,我已经准备好在 Tableau 中展示我的发现,但是面临着一个非常规的问题,那就是完全没有互联网接入。由于我们*多数人通常认为 Tableau 中内置的地理编码和底图服务是理所当然的,这比我最初预期的要复杂得多。
作者提供的图片|由空间数据文件以 Tableau 形式呈现的地图|按规划区域展示新加坡公民(65 岁及以上)的分布情况
基本上,当互联网不可用时,上面的地图会变成这样
按作者分类的图像|在没有互联网连接的情况下由空间数据文件以 Tableau 形式呈现的地图-所有地理编码和底图服务均不可用。
哎呀…显然,这有一些毁灭性的影响。更糟糕的是,在没有内置地图功能的情况下,渲染空间数据的深入教程少得可怜。所以我开始自己做一些探索,主要是,我必须解决一些问题来完成我的项目:
- 确保新加坡的地图多边形按比例渲染和缩放。
- 显示缺失的底图详细信息-高速公路、街道名称等。因为它们不再自动显示在背景中。
解决问题 1 —绘制地图多边形并渲染其实际比例
基于上述背景,我必须处理的只是 GeoJSON 格式的空间数据文件(出于本教程的考虑,我已将一个示例上传到 GitHub 的SG _ planning _ areas _ 2020 _ eldery _ distribution . geo JSON)并继续研究它。
从根本上来说, GeoJSON FeatureCollection 对象是一个 JSON 对象,它包含所有必要的坐标(纬度和经度)以将其几何形状渲染到任何地图上。因此,我决定利用这些信息,用“多边形”代替“地图”来制作几何图形。这需要一种 Tableau 容易理解的数据格式,在这种情况下,我选定的格式是 CSV 文件:
Image by Author |要在 Tableau 中使用“多边形”而不是“地图”渲染几何图形,必须首先将 GeoJSON 数据转换为 CSV 格式。
幸运的是,我在https://tableau-data-utility.onrender.com/创建了一个工具来加速这个过程。
作者图片| GeoJSON 文件上传到 https://tableau-data-utility.onrender.com/生成 CSV 输出文件|“导出 CSV 输出”然后选择保存 CSV 文件
基本上,这是一个基于浏览器的应用工具,我创建了所有的指令和实施所需的信息。我现在需要做的就是上传上面的 GeoJSON 文件,通过选择“生成 CSV 输出”导出转换后的输出,并使用“多边形”在 Tableau 中渲染它:
按作者分类的图像 CSV 文件中生成的字段用于在 Tableau 中渲染地图多边形-不依赖于任何地理编码服务
然后奇迹发生了!问题 1 已解决-地图多边形现在以正确的比例渲染,无需地理编码服务。
图片作者| ( 左)用“地图”渲染的地图多边形| ( 右)用“多边形”渲染的地图多边形
然而,随着问题 1 的解决,第二个问题很快取代了它——这就是缺少底图的,将我们直接引向问题 2。
解决问题 2-绘制在 Tableau 中渲染的空间数据的底图图层
乍看之下,这似乎不是什么*事,但当我受命调查和优化岛上的各种交通路线时,这很快就成了问题:
作者提供的图片|展示了从出发地到目的地的各种交通路线|以上示例展示了在新加坡驾车从中巴鲁市场到 Vivo City 的两条可能路线
显然,虽然国家/州/省的轮廓可以很容易地从其地图多边形中推断出来,但由(多)点和(多)线串表示的交通路线在没有如上图所示的详细地图图层的情况下并不明显。用于渲染上述的样本数据可以在我的 GitHub 上找到:SG _ tiong _ Bahru _ mkt _ to _ vivo _ city . geo JSON。使用相同的工具,GeoJSON 文件现已转换为 CSV 格式(SG _ tiong _ Bahru _ mkt _ to _ vivo _ city . CSV)。
作者图片|我在https://tableau-data-utility.onrender.com/部署的工具的截图,该工具用于从用户上传的 JSON 文件中为 Tableau 输出 CSV 文件
因此,这为我们带来了一个简单但非常有用和方便的应用程序功能——导出 Tableau 的背景地图图像的能力:
作者图片|展示了网络应用将地图导出为背景图片的功能
这里需要注意几个特殊的子功能:
- ****可更改底图选择-默认情况下,使用 OpenStreetMap (OSM)的底图。但是,用户可以通过输入有效的底图 URL 来更改底图,如下所示:
作者提供的图片| ( 左)使用默认底图| ( 右)使用备选地图服务,即 OneMap —请注意,用户的输入必须与小册子兼容— XYZ 底图切片服务又称滑动地图
2.地图边界的自动显示和更新** —在地图的正下方,有一个坐标表,当地图被缩放或移动时,它会自动更新。**
按作者分类的图像|将背景图像绘制为底图时,这些坐标对应于 Tableau 中的精确坐标
因此,要为运输路线导出适当的地图图像,必须确保在地图查看端口中捕获的地图的缩放级别** l 和边界都包括您的几何形状的所需边界。这最终将我们引向:**
作者图片|导出的地图图像和渲染的坐标直接输入到 Tableau 中
图片由作者提供| ( 左侧)不带底图的路线| ( 右侧)带有导入的地图图像的路线|清楚地显示了地图图层的细节,上面的起点-目的地路线向查看者显示了哪些道路、街道、建筑物、设施等。就在附近,为进一步的空间分析提供了更多的背景
因此,问题 2(缺少底图详细信息)现已解决。
最后,虽然我可以在这里结束这篇文章,但我也想分享我从这里得到的另一个启示— 通过 Tableau 中标准功能的组合绘制混合几何类型(“多边形”和“直线”)。
额外的发现—从单个 CSV 文件在 Tableau 中绘制混合几何图形类型
尽管 Tableau 声称有局限性
我对 Tableau 已经存在的功能的探索表明情况并非如此。Tableau 已经具有渲染混合几何类型的固有功能,我将使用这个空间文件来证明这一点,该文件是通过合并之前的两个 GeoJSON 文件创建的(实际的文件在我的 GitHub 中)。重复相同的步骤— GeoJSON 被转换为 CSV 输出,底图图像通过 https://tableau-data-utility.onrender.com/的工具导出,这为我们提供了:
作者提供的图片|展示 Tableau 中具有“多边形”和“线”功能的混合几何类型的绘图|多边形和交通路线都在同一视图中呈现
需要注意的是,要仔细查看“标记”面板,因为每个必填字段都有专门的后缀,带有“(点+线)”或“(多边形)”。这是为了区分哪些字段应该由“线”——即(点+线)渲染,哪些字段应该由“多边形”——即(多边形)** (是的,我部署的工具确实基于几何类型生成字段)。**
简而言之,我试图通过其他方式在 Tableau 中呈现空间数据集,这向我展示了使用现有的多种功能进一步可视化的可能性。希望你们觉得这很有用,可以在 https://tableau-data-utility.onrender.com/使用这个工具来生成你们需要的数据集!
我之前发表的关于渲染网络图数据集的教程使用了部署在https://tableau-data-utility.onrender.com/的工具。如果你想在 Tableau 中用最少的努力来绘制网络图,请查看它。感谢阅读!
获得李思欣·崔和其他作家在媒体上的所有帖子!😃您的会员费直接…
geek-cc.medium.com](https://geek-cc.medium.com/membership)**
被低估的机器学*算法——APRIORI
使用 Python 从头开始构建分步模块
先验导论
Apriori 是一种用于关联规则挖掘的算法。它在数据集中搜索一系列频繁出现的项目集。它建立在项目集之间的关联和相关性上。这是你在推荐平台上常见的“你可能也会喜欢”背后的算法。
什么是关联规则挖掘?
关联规则挖掘是数据科学中的重要技术之一。在 ARM 中,在项目集中识别数据集中模式和关联的频率,然后用于预测集合中的下一个相关项目。这种 ARM 技术主要用于根据客户购买情况做出商业决策。
例如:在沃尔玛,如果 Ashok 购买牛奶和面包,他购买黄油的机会是通过关联规则挖掘技术预测的。
在本模块中,我们将在给定的数据集上从头开始构建一个 apriori 算法,并将其用作客户购买的推荐系统。
有些定义需要记住
在我们开始之前,先看一下下面解释的一些术语。
SUPPORT _ COUNT 项目集出现的事务数。
MINIMUM _ SUPPORT _ COUNT 数据集中项目集的最小频率。
CANDIDATE_SET —数据集中每个项目的 C(k) support_count。
ITEM _ SET-L(k)将候选集支持计数中的每个项目与 minimum_support_count 进行比较,并过滤出不频繁项目集。
支持-数据库中的事务百分比遵循规则。
Support(A->B) = Support_count(A U B)
信心——购买了 A 的顾客也购买了 b 的顾客的百分比
Confidence(A->B) = [Support_count(AUB)/Support_count(A)]*100
获取数据
在这个实验中,我们考虑了来自 Kaggle 的名为杂货店数据集的数据集。它由一家超市的 20 笔一般商品交易组成。这个数据集更容易理解模式和关联。
在这里,我们可以看到 20 组食品杂货交易,即牛奶、面包、饼干、玉米片、茶、BOURNVITA、果酱、MAGGI、咖啡、公鸡和糖。
尽管这个数据集很小,但我们不需要生成参数数据集。这足以开发 Apriori 算法。
准备数据
需要处理这些数据以生成记录和项目列表。将 minimum_support_count 视为 2。Pandas 库用于导入 CSV 文件。
先决条件:PYTHON 中级
Geeksforgeeks: Apriori 算法(基于理论)
import pandas as pd
import itertoolsdata = pd.read_csv('GroceryStoreDataSet.csv')minimum_support_count = 2
records = []
for i in range(0, 20):
records.append([str(data.values[i,j]) for j in range(0, 4)])items = sorted([item for sublist in records for item in sublist if item != 'nan'])
构建算法
让我们在构建算法之前修改 Apriori 的关键属性,
频繁项目集的所有子集必须是频繁的。如果一个项目集是不频繁的,那么它的所有超集都是不频繁的。
第一步
在阶段 1 中,通过测量数据集中每个项目的 support_count 来生成候选集 C1。通过比较 C1 支持计数和最小支持计数,生成项目集 L1。这里 k=1。
def stage_1(items, minimum_support_count):
c1 = {i:items.count(i) for i in items}
l1 = {}
for key, value in c1.items():
if value >= minimum_support_count:
l1[key] = value
return c1, l1c1, l1 = stage_1(items, minimum_support_count)
步骤 1 的结果是项目集 L1,
项目集 L1
在这种情况下,candidate_set 中不存在最小支持计数的低频率项目。因此,候选人集 C1 =项目集 L1。
第二步
在此阶段,使用上一步中的项目集 L1 生成候选集 C2。检查项目集中的所有子集是否是频繁的,如果不是,从列表中删除相应的项目集。项目集 L2 是通过将候选集 C2 与最小值支持计数进行比较而生成的。这里 k=2。
def stage_2(l1, records, minimum_support_count):
l1 = sorted(list(l1.keys()))
L1 = list(itertools.combinations(l1, 2))
c2 = {}
l2 = {}
for iter1 in L1:
count = 0
for iter2 in records:
if sublist(iter1, iter2):
count+=1
c2[iter1] = count
for key, value in c2.items():
if value >= minimum_support_count:
if check_subset_frequency(key, l1, 1):
l2[key] = value
return c2, l2c2, l2 = stage_2(l1, records, minimum_support_count)
为了检查一个项集的子集是否频繁,我们应该传递当前阶段的项集、前一阶段的项集(在本例中是 L1)和 k-1。
def check_subset_frequency(itemset, l, n):
if n>1:
subsets = list(itertools.combinations(itemset, n))
else:
subsets = itemset
for iter1 in subsets:
if not iter1 in l:
return False
return True
步骤 2 的结果是项目集 L2,
项目集 L2
在这种情况下,由于低频率类别,即低于 minimum_support_count,需要消除项目集中的 31 个项目。
第三步
在此阶段,使用上一步中的项目集 L2 生成候选集 C3。检查项目集中的所有子集是否是频繁的,如果不是,从列表中删除相应的项目集。通过将候选集 C3 与最小支持计数进行比较,生成项目集 L3。这里 k=3。
def stage_3(l2, records, minimum_support_count):
l2 = list(l2.keys())
L2 = sorted(list(set([item for t in l2 for item in t])))
L2 = list(itertools.combinations(L2, 3))
c3 = {}
l3 = {}
for iter1 in L2:
count = 0
for iter2 in records:
if sublist(iter1, iter2):
count+=1
c3[iter1] = count
for key, value in c3.items():
if value >= minimum_support_count:
if check_subset_frequency(key, l2, 2):
l3[key] = value
return c3, l3c3, l3 = stage_3(l2, records, minimum_support_count)
步骤 3 的结果是项目集 L3,
项目集 L3
步骤 4
在此阶段,使用上一步中的项目集 L3 生成候选集 C4。检查项目集中的所有子集是否是频繁的,如果不是,从列表中删除相应的项目集。项目集 L4 是通过将候选集 C4 与最小支持计数进行比较而生成的。这里 k=4。
def stage_4(l3, records, minimum_support_count):
l3 = list(l3.keys())
L3 = sorted(list(set([item for t in l3 for item in t])))
L3 = list(itertools.combinations(L3, 4))
c4 = {}
l4 = {}
for iter1 in L3:
count = 0
for iter2 in records:
if sublist(iter1, iter2):
count+=1
c4[iter1] = count
for key, value in c4.items():
if value >= minimum_support_count:
if check_subset_frequency(key, l3, 3):
l4[key] = value
return c4, l4c4, l4 = stage_4(l3, records, minimum_support_count)
步骤 4 的结果是项目集 L4,
项目集 L4
我们可以在这里停下来,因为没有进一步发现频繁子集。
生成关联规则
为了生成数据集的关联规则,我们需要计算每个规则的置信度。
''' Rule generation of itemset ''''''
Confidence:Confidence(A->B)=Support_count(A∪B)/Support_count(A)Confidence((COCK, COFFEE)->CORNFLAKES) = Support_count(‘COCK’, ‘COFFEE’, ‘CORNFLAKES’)/Support_count(‘COCK’, ‘COFFEE’)'''
让我们考虑关联规则的项目集 L3。
sets = []
for iter1 in list(l3.keys()):
subsets = list(itertools.combinations(iter1, 2))
sets.append(subsets)
在 L3 生成的集合上实现关联规则
def support_count(itemset, itemlist):
return itemlist[itemset]list_l3 = list(l3.keys())
for i in range(0, len(list_l3)):
for iter1 in sets[i]:
a = iter1
b = set(list_l3[i]) - set(iter1)
confidence = (support_count(list_l3[i], itemlist)/support_count(iter1, itemlist))*100
print("Confidence{}->{} = ".format(a,b), confidence)
假设最小置信度为 50%
结果
信心('饼干','公鸡')--> { '玉米片' } = 100.0
信心('饼干','玉米片')- > { '公鸡' } = 66.666666666666666
信心('饼干','玉米片')- > { '饼干' } = 100.0
信心('饼干','马奇')- > { '茶' } = 100.0
信心('饼干','茶')-【信心 奶')- > { '饼干' } = 50.0
信心('面包','咖啡')- > { '糖' } = 66.66666666666666666
信心('面包','糖')- > { '咖啡' } = 50.0
信心('咖啡','糖')- > { '面包' } = 50.0
信心('面包','糖')-【MAGGI)-> 茶')- > { '面包' } = 100.0
信心('面包','茶')- > {'BOURNVITA'} = 50.0
信心('饼干','公鸡')- > { '咖啡' } = 100.0
信心('饼干','咖啡')- > { '公鸡' } = 100.0
信心('公鸡','咖啡')- > { '饼干' } = 66.66
结论
这种关联规则挖掘技术是由亚马逊、网飞、谷歌、Flipkart 和 Spotify 等巨头在其推荐平台中实现的。
这种算法也用作*多数销售产品组合的折扣营销技术。
今天,我们已经学*了如何构建 Apriori 算法,并在超市的一般杂货数据集上实现关联规则挖掘。
数据集和全部代码可以在我的 Git 仓库中找到。
理解并在 Python 中构建 FP-Growth 算法
Python 中使用 FP-树和条件 FP-树的频率模式挖掘
卢克·理查森在 Unsplash 上的照片
什么是 FP 增长
FP-growth 是 Apriori 算法的改进版本,广泛用于频繁模式挖掘(也称为关联规则挖掘)。它被用作从数据集中发现频繁模式或关联的分析过程。例如,杂货店交易数据可能有一个频繁模式,即人们通常一起购买薯条和啤酒。Apriori 算法通过生成项目集并发现超过阈值“最小支持计数”的最频繁项目集来产生频繁模式。它通过一个简单的原则极*地减小了数据库中项目集的*小:
如果一个项集是频繁的,那么它的所有子集也一定是频繁的。
然而,Apriori 算法有一个主要的不足。使用 Apriori 需要多次扫描数据库,以检查每个项目和项目集的支持计数。当数据库很*时,这将消耗*量的磁盘 I/O 和计算能力。因此,FP-Growth 算法被创建来克服这个不足。它只扫描数据库两次,并使用一个树结构(FP-tree)来存储所有的信息。根代表空,每个节点代表一个项,而节点的关联是在形成树时保持顺序的项集。FP-tree 简洁,用于直接生成*项目集。一旦 FP-tree 被构建,它就使用递归的分治方法来挖掘频繁项目集。
FP-tree 伪代码及解释
- 第一步:推导出有序的频繁项。对于频率相同的项目,按字母顺序排列。
- 步骤 2:从上面的数据构建 FP 树
- 步骤 3:从上面的 FP-tree 中,为每个项目(或项目集)构建 FP-conditional 树。
- 第四步:确定频繁模式。
让我们看一个如何生成 FP 树的例子。查找支持度≥2 的所有频繁项集。首先,找到支持计数≥ 2 的所有项目。
查找项目频率
在构建 FP 树之前,根据项目频率对事务进行重新排序。当开始构建 FP-tree 时,我们需要创建一个标题表,用一个链表记录所有项目节点的位置。每次向树中添加新节点时,都需要将它链接到具有相同项目的最后一个节点。标题表对于在后面的步骤中构建条件 FP-tree 是必不可少的。
构建 FP 树
现在我们已经扫描了数据库两次,并建立了 FP 树。所有交易信息都包含在树中。剩下要做的唯一事情是递归地构造条件 FP-tree,以找到支持计数*于 2 的所有频繁项目集。使用我们在上一步中创建的标题表,从最少次数到最多次数(E,D,C,B,A)的项目开始。
构建一棵条件 FP 树
最后,我们可以很容易地从那些条件 FP 树{E}、{A,D,E}、{A,E}、{D,E}、{D}、{A,D}、{C,D}、{C}、{A,C}、{B}、{A,B}、{A}中生成所有频繁项集。
生成频繁项目集
Python 实现
下面是一些示例代码,用于从头开始构建 FP-tree,并在 Python 3 中找到所有的频率项集。
总之,FP-tree 仍然是挖掘数据集中完整的频繁模式集的最有效和可伸缩的方法。*多数编程语言包括 Python、R 甚至 Pyspark 都有很好支持的库来生成 FP-tree。
感谢您的阅读,我期待听到您的问题和想法。如果你想了解更多关于数据科学和云计算的知识,可以在 Linkedin 上找我。
杰克·洛里菲茨在 Unsplash 上拍摄的照片
参考
https://en.wikipedia.org/wiki/Apriori_algorithm
https://www . software testing help . com/FP-growth-algorithm-data-mining/
从零开始的逻辑回归
在 NumPy 中从零开始建立逻辑回归。这比你想象的要容易得多!
在许多方面,逻辑回归模型是数据科学家工具箱中最简单的机器学*分类工具之一。理解这些如何直接工作有助于我们理解用于分类任务的深度神经网络。
在这里,我们将讨论模型的数学,如何训练它,以及如何使用 numpy 在 Python 中构建它。
模型背后的数学
让我们首先想象一下,我们有一个像上面这样的数据集,其中黄色点对应于类 0,紫色点对应于类 1。为了预测类标签,我们需要一个模型,它将两个数据维度作为输入,并预测一个介于 0 和 1 之间的数字。考虑这是给定数据和模型参数的标签的概率:ŷ = p( y = 1| X , w )。
逻辑回归模型旨在拟合两个类别之间的直线(或平面/超平面),以便线上的点的概率为 0.5,而远离线的点的概率为 0 或 1,具体取决于它们位于线的哪一侧。
现在我们理解了这个概念,让我们试着稍微形式化一下。
我们的直线/平面/超平面可以表示为:x̂ = w ᵀ x + b 其中 w 是权重的向量,b 是偏差。
“这很好,但我如何获得分配给标签的概率的封闭形式?”,我听到你问。
很棒的问题!
好了,我们现在需要的是一种方法来映射我们的线性模型到一个有效的概率。这意味着该函数必须具有以下属性:
- 在给定连续输入的情况下,它只能输出[0,1]范围内的值
- 它必须是光滑的和可微的(其原因将在后面变得更加清楚)
这就是乙状结肠或逻辑功能发挥作用的地方。sigmoid 函数的图形及其函数形式如下所示。这意味着我们有了一个表示某个点属于某个特定类的概率的表达式。
Sigmoid 激活函数
所以我们整个模型可以表述为:ŷ = σ( w ᵀ x + b)。
训练我们的模型
我们知道模型有能力为两类中的每一类输出一个概率,但是我们如何选择模型的权重( w ,b)?
为此,我们需要为模型定义一个成本函数,并根据模型参数最小化该函数。
为我们的模型选择成本函数是为了最*化指定类别的可能性:
从对数似然到二元交叉熵。x 是数据,w 是可训练的权重,ŷ是模型输出,y 是标签。
这种损失也被称为二进制交叉熵。
好消息是,我们就快成功了!我们现在需要使用梯度下降来优化基于成本函数的参数。这种优化过程是训练网络式机器学*模型的最常见方式。它包括计算成本函数相对于每个模型参数的导数——因此,sigmoid 函数的平滑和可微性非常重要。
对于那些懂一点微积分的人来说,你可能知道这是链式法则的一个应用,但多亏了 Geoff Hinton [1],在机器学*领域,我们称之为反向传播。
成本函数相对于模型参数的梯度。如果你想自己证明一个 sigmoid 函数的导数是σ(1-σ),我推荐这篇文章。
既然我们有了关于模型权重的梯度,我们可以采取小的步骤来降低成本函数。μ是指算法的学*速率。如果学*率太小,模型的训练时间就会太长,并且可能会陷入局部极小值。如果它太*,那么训练过程可能根本不收敛或者它可能收敛到一个差的最小值。
更新模型的权重。I 表示反向传播算法的第 I 次迭代。
如果我们有很多数据,我们可以创建小批量,并为训练数据中的每个批量采取一个步骤。或者,我们可以选择更简单的方法,计算整个数据集的成本函数,并在每个时期采取一个梯度步骤。我们重复计算成本、梯度,然后更新权重的过程,直到成本函数收敛。
从逻辑回归到深度神经网络
正如我们刚刚看到的,逻辑回归将线性模型拟合到数据,并将输出传递给 sigmoid 激活,以预测每个类别的概率。
如果我们想要从输入维度到输出维度进行更复杂的映射,那么我们可以在逻辑回归层之前添加额外的转换。为了训练这些层的权重,我们可以使用相同的反向传播原理来找到这些权重的梯度,并以与我们之前完全相同的方式更新它们。
代码
这是理论,但我们如何把它付诸实践呢?
让我们实现向前传递,用模型进行预测。
import numpy as npdef _sigmoid(x):
return 1/(1 + np.exp(-x))def predict(X,W):
X = np.asarray(X)
if len(X.shape) != 2:
raise ValueError("Shape must be (dims,n_data_points)")
X = np.concatenate([X,np.ones([1,X.shape[-1]])],axis=0) X_hat = np.matmul(W,X)
y_hat = _sigmoid(X_hat)
return y_hat
在实施过程中,有两件事需要特别注意:
首先,我们没有包括单独的偏差,而是做了一个小小的简化,增加了一个额外的权重,并将 1 附加到特征维度的数据上。这使得我们有一个单一的权重向量,而不是两个。对于读者来说,这是一个练*(因为你已经读得太久了),以检查简化后的梯度是否仍然正确。
其次,请注意,在数据点上没有循环,相反,计算是矢量化的,以提高性能。这通常在机器学*中很重要,以便在训练期间可以利用 GPU 的能力,但对于这个更简单的模型来说不是必要的。
现在让我们定义成本函数…
cost = -np.sum(y*np.log(y_hat) + (1-y)*np.log(1-y_hat))
…梯度下降步骤:
dc_dw = -np.sum((y-y_hat)*X,axis=-1)[np.newaxis,:]
self.W = self.W - dc_dw * lr
我们现在可以用一个类中的fit
和predict
方法来模仿 sk-learn 类型的 API。
class LogisticRegressor():
def __init__(self):
self.loss = []
self.W = None
def _sigmoid(self,x):
return 1/(1 + np.exp(-x))def fit(self,X:np.ndarray,y:np.ndarray,epochs: int=100,lr: float=0.01):
self.epochs = epochs
self.lr = lr
X = np.asarray(X)
y = np.asarray(y)
if len(X.shape) != 2:
raise ValueError("Shape must be (dims,n_data_points)")
X = np.concatenate([X,np.ones([1,X.shape[-1]])],axis=0)
dims, n_data_points = X.shape
if self.W is None:
self.W = np.random.randn(1,dims)
for i in range(epochs):
X_hat = np.matmul(self.W,X)
y_hat = self._sigmoid(X_hat)
cost = -np.sum(y*np.log(y_hat) + (1-y)*np.log(1-y_hat))
self.loss.append(cost)
dc_dw = -np.sum((y-y_hat)*X,axis=-1)[np.newaxis,:]
self.W = self.W - dc_dw * self.lr
def plot_loss(self):
plt.scatter(list(range(len(self.loss))),self.loss)
def predict(self,X:np.ndarray)-> np.ndarray:
X = np.asarray(X)
if len(X.shape) != 2:
raise ValueError("Shape must be (dims,n_data_points)")
X = np.concatenate([X,np.ones([1,X.shape[-1]])],axis=0) X_hat = np.matmul(self.W,X)
y_hat = self._sigmoid(X_hat)
return y_hat
让我们来测试一下!以本文开头所示的模拟数据集为例,我们已经准备好测试我们的模型…
在这种情况下,我们已经可视化了两个类之间的决策边界。这可以在分类器预测 0.5 概率的地方找到。换句话说,就是 w ᵀ x + b = 0 的那条线。所以我们的努力看起来是有回报的,我们的模型可以正确识别 0.89%的数据点,并在对接近决策边界的点进行分类时具有表达不确定性的能力!
那么,我们学到了什么?
如果你已经做到了这一步,那么希望你能更好地理解逻辑回归是如何工作的,以及为什么二元交叉熵只是表达模型预测的对数似然性的一种不同方式。
训练逻辑回归模型允许我们尝试反向传播,这为理解如何训练更复杂的深度神经网络提供了基础。
[1]:鲁梅尔哈特博士、辛顿博士和威廉斯博士,1986 年出版。通过反向传播误差学*表征。自然,323(6088),第 533–536 页。
理解并使用 TensorFlow 2.0 实现 ResNet-50
基于深度神经网络的图像分类
京都的清晨:来源(作者)
我们的直觉可能表明,较深的神经网络应该能够捕捉更复杂的特征,因此与较浅的神经网络相比,它们可以用于表示更复杂的功能。应该出现的问题是——如果学*一个更好的网络就等同于越堆越多的层?这种方法有什么问题和好处?这些问题和一些非常重要的其他概念在 2017 年 K. He 等人的用于图像识别的深度残差学*论文中讨论过。这种架构被称为 ResNet,本文介绍了许多与深度神经网络(DNN)相关的重要概念,这些都将在本文中讨论,包括在 TensorFlow 2.0 中实现 50 层 ResNet。你可以从这篇文章中学到什么—
- 深度神经网络的问题。
- ResNet 背后的数学直觉。
- 剩余块和跳过连接。
- 构造 ResNet 和 1×1 卷积的重要性。
- 用 TensorFlow 实现 ResNet。
我们开始吧!
**使用tf.data
的图像分类管道的更快版本在这里讨论。
退化问题:
ResNet 最初工作的主要动机是解决深层网络中的退化问题。向足够深的神经网络添加更多层将首先看到精度饱和,然后精度下降。何等人展示了以下使用普通网络的 Cifar-10 数据集的训练和测试误差图
图 1:在平原 DNN,Cifar-10 数据的分类误差随着训练(左)和测试数据(右)的层数的增加而增加。参考:[1]
正如我们可以看到的,较深网络(56 层)的训练(左)和测试误差(右)高于 20 层网络。深度越*,历元越多,误差越*。首先,似乎随着层数的增加,参数的数量也增加,因此这是一个过拟合的问题。但其实不是,我们来理解一下。
思考这个问题的一种方式是考虑深度 DNN,它可以计算手头任务所需的足够强的特征集(例如:图像分类)。如果我们在已经非常 DNN 的基础上再增加一层网络,这一层会有什么作用呢?如果网络已经可以计算强特征,则该附加层不需要计算任何额外的特征,而是,仅复制已经计算的特征,即执行身份映射(添加层中的内核产生与先前内核完全相同的特征)。这似乎是一个非常简单的操作,但在一个深层的神经网络中,这与我们的预期相差甚远。
ResNet 背后的数学直觉:
让我们考虑一个包括学*率和其他超参数的 DNN 架构,它可以达到一类函数 F 。因此,对于所有的 f∈ F,存在参数 W ,我们可以在针对特定数据集训练网络之后获得这些参数。如果 f* 表示我们真正想要找到的函数(最佳可能优化的结果),但是如果它不在 F 内,那么我们试图找到在 F 内的最佳情况 f1,,如果我们设计一个更强*的架构 G,我们应该会得到更好的结果 g1 ,这比 f1 更好。但是如果 f·⊈g,那么就不能保证上述假设会成立。事实上 g1 可能比 f1 更差,这是退化问题。所以要点是——如果更深的神经网络函数类包含更简单和更浅的网络函数类,那么我们可以保证更深的网络将增加原始浅网络的特征发现能力。一旦我们在下一节介绍残差块,这将变得更加清楚。
剩余块:
剩余块的想法完全基于之前解释的直觉。较简单的函数(较浅的网络)应该是复杂函数(较深的网络)的子集,以便可以解决退化问题。让我们考虑输入 x ,并且从输入到输出的期望映射由 g(x) 表示。我们将处理一个更简单的函数 f(x) = g(x)-x ,而不是处理这个函数。然后,原始映射被重新转换为 f(x)+x 。在 ResNet 论文中 He et al. 假设优化残差 f(x)比优化原 g 本身更容易。 优化残差还考虑到了这样一个事实,即我们不需要担心在非常深的网络中可怕的身份映射 f(y)→ y 。让我们看看下面剩余部分的示意图—
图 2:用于身份映射的剩余块和跳过连接。已重新创建以下引用:[3]
残差学*公式确保当恒等式映射是最优的(即 g(x) = x )时,该优化将使残差函数的权重趋向于零。ResNet 由许多残差块组成,其中残差学*被采用到每几个(通常 2 或 3 层)堆叠层。构建模块如图 2 所示,最终输出可以认为是 y = f(x,W) + x 。这里的 W 的是重量,这些是在训练中学会的。运算 f + x 通过快捷方式(“跳过”2/3 层)连接和元素相加来执行。这是一个最简单的块,跳过连接中不涉及任何附加参数。只有当 f 和 x 的尺寸相同时,元素相加才是可能的,如果不是这种情况,那么我们将输入 x 乘以投影矩阵 Ws,使得 f 和 x 的尺寸匹配。在这种情况下,输出将从之前的等式变为 y = f(x,W) + Ws * x 。投影矩阵中的元素也是可训练的。
构建 ResNet 和 1× 1 卷积:
我们将按照何在原论文中所采用的方法建立一个 50 层的 ResNet。ResNet-50 采用的体系结构不同于 34 层体系结构。快捷连接跳过了 3 个街区而不是 2 个,下面的示意图将帮助我们澄清一些问题-
图 3:左:跳过 2 层,ResNet-34。右图:跳过 3 层,包括 ResNet-50 中的 1× 1 卷积。参考:[1]
在 ResNet-50 中,残差块中的堆叠层将始终具有 1×1、3×3 和 1×1 卷积层。1×1 卷积首先降低维度,然后在瓶颈 3×3 层中计算特征,然后在下一个 1×1 层中再次增加维度。使用 1×1 过滤器来减少和增加瓶颈层前后的特征图的维度,如 Szegedy 等人在其初始论文中的 GoogLeNet 模型所述。由于残差块内没有汇聚层, 用步长 2 进行 1×1 卷积降维。记住这几点,让我们使用 TensorFlow 2.0 构建 ResNet-50。
建筑 ResNet-50:
在编码之前,让我们看看原始论文中呈现的 ResNet-34 架构—
ResNet-34(摘自 K. He 等人的《深度剩余学*》)
仅池化层放置在架构末端的最开始和密集连接之前。如前所述,使用 1×1 卷积来改变其他地方的尺寸。对于过滤器的数量和其他参数,我遵循了 Keras 的例子。现在是编码的时候了。首先,我们定义一个最简单的单位块,其中输入的维度不变,只有深度,下面是代码块-
最简单的残余块,尺寸没有任何变化,只有深度。
通过使用步长为 2 的 1×1 卷积,另一个残差块将包括输入维度的变化。因此,跳过连接也将经历尺寸变化——
与步幅 2 卷积。输入变化的维度
结合这两个剩余模块,我们现在可以构建完整的 50 层 ResNet,如下所示
使用 64,160 个时期的批量*小和数据扩充,在训练数据和测试数据上实现了 85%和 82%的准确度。下面是训练和验证曲线—
示出了 Cifar-10 数据 50 层 ResNet 的训练和验证精度/损失。(来源:作者)
此外,可以绘制 Cifar-10 数据中所有 10 个类别的混淆矩阵
使用 ResNet-50 训练的用于 Cifar-10 数据的 CM。(来源:作者)
讨论:
在这里,我们看到了一个使用 TensorFlow 实现 ResNet-50 并使用 Cifar-10 数据训练模型的示例。一个重要的讨论点是卷积-批处理-激活的顺序,这仍然是一个争论点。许多人认为最初的批次标准文件中使用的顺序不是最好的。看 GitHub 的一个问题这里。我建议您尝试不同于笔记本中使用的参数,以了解它们的效果。
你能从中得到的几个要点是—
- 退化和过度拟合之间的区别以及为什么退化发生在非常深的网络中。
- 使用 1×1 卷积来增加和减少特征图的维数。
- 残余块如何有助于防止退化问题?
暂时就这样吧!希望这能帮到你一点,保持坚强!!
页(page 的缩写)如果你想使用tf.data
建立一个更快的图像分类管道,那么查看这篇文章。
参考:
[1] ResNet 原创论文:何等。
[2] Keras 示例实现。
[3] Alex Smola: ResNet 直觉讲座
[4]笔记本所用代码: GitHub Link 。
如果你对更深入的基础机器学*概念感兴趣,可以考虑加盟 Medium 使用 我的链接 。你不用额外付钱,但我会得到一点佣金。感谢*家!!
更多来自 Saptashwa(以及 Medium 上的许多其他作者)。你的会员费直接支持 Saptashwa 和其他…
medium.com](https://medium.com/@saptashwa/membership?source=publishing_settings-------------------------------------)
了解并可视化色彩空间,以改进您的机器学*和深度学*模型
解释、分析和实验 14 种流行的颜色空间以及它们对我们模型的准确性的影响。
不同色彩空间的模型训练(自制测试)
介绍
“为什么我们在训练模型中使用 RGB 颜色空间作为标准?当然,这是最简单的颜色空间,因为它是默认的颜色空间。但是有没有其他可能更合适的色彩空间呢?它能改善我们的模型吗?”
这些问题浮现在我的脑海中,我必须找到答案。于是我调查了,做了一些实验。我愿意与你分享我的成果。💭
起初,我从探索不同的色彩空间开始,我发现启发了。所以在这篇文章的第一部分,我将向你简要介绍这些颜色空间以及它们在机器学*和深度学*中的可能的应用。
有*量(无限)的颜色空间,所以我为你选择了最有趣的颜色空间。😇
- RGB - CMYK
- 国际照明委员会 XYZ -国际照明委员会 Lab -国际照明委员会 Luv
- HSV- HSL- HSI
- YCbCr - YDbDr
- C1C2C3 - I1I2I3
- 皮肤单位剂量
在这篇文章的第二部分,我用相同的型号和相同的配置体验了这些色彩空间。我们会看到,从一个颜色空间到另一个颜色空间,我们的模型的精度可以从简单到两倍。
RGB — BGR —CMYK
那么一张 RGB 的图像是如何构造的呢?基本上是通过加上不同“比例”的红绿蓝。但是我想我不会告诉你比你已经知道的更多的东西。你添加的颜色越多,你得到的颜色就越浅。这是因为它们发出光(这是同样的原理,我们可以通过仔细观察屏幕来观察)。
RGB 和 CMYK —转换
这与原色反光不同。它是反向机制,即减法。你把颜色加在一起越多,你得到的颜色就越暗。这是用于打印、 CMYK (青色、洋红色、黄色和黑色)的系统。
那么为什么是 RGB 呢?事实是,你想要多少颜色空间就有多少颜色空间。我们将看到我们如何建造它们。但是 RGB 是关于简单的。我们的电脑硬件就是这样组成的。
RGB 分解(来源: Pixabay
RGB 是默认的颜色空间,即使在机器学*和深度学*中也是如此。但是看一看的替代品。
XYZ 国际照明委员会—国际照明委员会 Lab —国际照明委员会 Luv
我们看到 RGB 是面向设备的。国际照明委员会 CIE 因其法语名称“Commission International e de l ' eclairage”而设立了色度学标准。它设计了更抽象的色彩空间来打破 RGB 标准的界限。****
CIE XYZ 分解(来源: Pixabay
编码在 3 个字节上的 RGB 空间允许表示人眼能够感知的 40% 的颜色。这就是为什么 CIE 建议用颜色空间来扩展人类实际感知的可能性领域。因此有了颜色空间 CIE XYZ 。它将颜色空间的边界扩展到包含所有可见的。如果我们简化一下:
- x *致对应于红色刺激
- y 或多或少对应于亮度****
- z *致对应于蓝色刺激
RGB 和 CIE XYZ——转换和模式(来源:维基百科
看一看原理图和我们从一个色彩空间切换到另一个色彩空间的方式,你就会理解两个关键要素:
- 三种“原色”的任何选择只能产生一个可用颜色的子集。****
- 有一个矩阵通道有无限个不同的颜色空间
CIE XYZ 空间是一个工具空间 e,作为其他空间的支持:CIE Lab 和 CIE L * u * v将会很有趣,因为它引入了亮度的概念。**
RGB 和 CIE Lab*—转换和模式(来源:维基百科)
眼睛有三个不同的视锥细胞来感知颜色。一个红色,一个绿色,一个蓝色。但是这些视锥细胞没有同样的反应。所以对颜色的感知不同于真实的颜色(用波长来说)。CIE Lab*颜色空间试图扭曲 CIE XYZ 空间以更好地代表人眼的颜色感知:
- L 为亮度黑色→白色*
- a 表示轴上的值绿色→红色;*
- b 表示轴上的数值蓝色→黄色。*
CIE Lab*分解(来源: Pixabay
为了训练学*模型,CIE Lab 可能是合适的。这一点可以从威尔逊·卡斯特罗的论文中看出,他们试图根据成熟程度对好望角醋栗进行分类。他和他的团队尝试了 SVM,安,DT 和 KNN。在这些模型中的每一个上,CIE Lab*色彩空间被证明比 RGB 色彩空间更有效。****
RGB 和 CIE Luv —转换和模式(来源:维基百科*
最后,CIE Luv*空间是接近人眼感知的另一种尝试。它的优点是善于表现自然场景的图像。颜色距离更容易估计,尤其是绿色之间的距离。在 M.P. Rico-Fernándeza 的论文中,他们使用 SVM 方法对栽培物种进行分类,CIE Luv*颜色空间使得精确度更高。
CIE Luv*分解(来源: Pixabay )
HSV- HSL- HSI
其他颜色空间基于心理学方法。这是 HSV 、 HSL 和 HSI 空间的情况。所有这些都基于色彩心理学的概念,这是解释你所看到的的最佳方式:
- ****色调:主色调
- ****饱和度:颜色的纯度
- ****亮度:颜色的亮度
模式 HSV-HSL-HSI(来源: mathworks )
这些颜色空间被称为圆柱形,因为它们由围绕色调的圆柱形或圆锥形形状表示。所有这些空间都有相同的基础:代表主要波长的色调。****
RGB —色调转换
但是这些空间偏离了亮度的定义。
- 值对应最强震级
- 亮度对应于星等的范围
- ****强度对应于平衡的星等
RGB 值亮度强度转换
****饱和度因此也根据亮度的定义而有不同的定义。
RGB —饱和度转换
这三个色彩空间都有这个基础:色彩心理学。这有助于轻松设定亮度阈值。在机器人学中,可以使用这个颜色空间。作为例子,我们可以看看 L. Nalpantidis 写的论文。他们使用 HSV 颜色空间来设计立体摄像机系统,并考虑到了非理想照明条件。
HSV 分解(来源: Pixabay )
HSL 分解(来源: Pixabay )
HSI 分解(来源: Pixabay )
Y'UV — Y'IQ — Y'CbCr — Y'DbDr
这些色彩空间生来就有野心压缩视频传输所涉及的带宽。我们以 Y'CbCr 为例。该色彩空间是一种标准,旨在确保与黑色&白色和彩色电视兼容。
寻找色度的比例校正思想
所以你有一个信号Y’,它代表黑白的亮度。然后使用两个 chromas 传输颜色信息的另外两个组件:蓝色 Cb 和红色 Cr 。它允许从 B & W 彩色图像中恢复丢失的信息。但实际上,磷光体(为电视屏幕着色)的效率不是线性的,因此我们应用校正系数。
RGB 和 YCbCr——转换和模式(来源:维基百科
几个标准都有相同的亮度 Y’和校正基础,有两种色度:
- Y'UV (模拟格式 PAL)
- Y'IQ (模拟格式 NTSC)
- Y'CbCr (数字格式,JPEG……)
- Y'DbDr (模拟格式 SECAM)
RGB — Y'DbDr - Y'UV - Y'IQ 转换
因此,所有这些空间都或多或少有些相似,但也能在我们的模型中扮演重要角色。A. M. Aibinu 的论文表明,与 RGB 空间相比,基于 YCbCr 色彩空间的人工神经网络对于皮肤检测具有最佳性能。
Y'IQ 分解(来源: Pixabay )
Y'CbCr 分解(来源: Pixabay )
Y'DbDr 分解(来源: Pixabay )
Y'UV 分解(来源: Pixabay )
C1C2C3 — I1I2I3
I1I2I3 由 Y-I. Ohta 推出用于图像分割。媒体整合与传播中心(MICC)也提出了另一种形式,它建议不变,以突出效果。
RGB — I1I2I3 Otha 和 MICC 转换
MICC 还建议 C1C2C3 颜色空间对于阴影效果是不变的。****
RGB — C1C2C3 转换
I1I2I3 和 C1C2C3 有时被用作与其他颜色空间的补充或混合组合,如你在 A. Rasouli 的论文中所见。他和他的团队评估了*量的颜色空间,以测量它们检测和区分不同颜色物体的适用性。在所研究的组合中, C1C2C3 和 XYZ 的组合效果最好。
I1I2I3 分解(来源: Pixabay )
C1C2C3 分解(来源: Pixabay )
然后我决定测试 MICC 提出的色彩空间。
皮肤单位剂量
最后一个。 HED 用于苏木精-伊红-DAB** 是由 A.C.C. Ruifrok 和 D. A. Johnston 设计的颜色空间,用于分析医学领域的特定**组织。****
RGB-HED 转换
这种颜色空间可以通过图像用于血液分析,例如 K. Ben-Suliman 和 A. Krzyżak 的论文处理在显微镜下看到的图像。
HED 分解(来源: Pixabay )
方法
现在小旅行结束了,建议你在相同条件下和尝试这些色空间评估对同型号的影响。
模型
我决定训练一个 CNN ,更准确的说是受到 Yann Le Cun 的 LeNet5 的启发。有趣的是,这种模型多年来一直被用来分拣邮件。它被用来识别美国邮政服务手写的邮政编码。
模型摘要
将使用 CIFAR-10 数据库建立分类模型。
培训将在相同的条件下进行:
- 预处理中输入图像的通道归一化****
- 批量:128 个
- 损失:分类交叉熵
- 优化器:SGD
- 学*率:1e-2
- 动量:0.9
- 混洗数据集:假
- 相同的训练和测试集
- 度量:准确性
- 在测试集上每个时期的度量计算
- 每次度量增加时,模型被保存
- 只有当在 5 个时期内没有增加,并且如果度量已经达到 0.11 阈值时,训练循环才停止,以避免在启动太慢的情况下停止训练 (0.11,因为有 10 个等级)
结果和讨论
根据颜色空间训练模型的进化
所以我在完全相同的条件下为这个模型训练了不同的颜色空间。我不想对你撒谎。我暗暗希望 RGB 色彩空间会比其他几个色彩空间更远。😐
完全没有。在我的例子中,RGB 空间仍然是满足色彩空间的。然而,我们可以注意到Y ' uv 颜色空间对于我在 CIFAR-10 数据集上的模型是最合适的。👼****
不同颜色空间下的全局和类精度
在分析了我们的颜色空间的平均精度之后,我想知道我们的模型在依赖于类别的颜色空间上的精度是否有变化。
我们的两个最佳颜色空间(RGB 和 Y'UV)非常擅长对车辆进行分类,但在对动物的分类方面相对较弱。
基于 RGB 颜色空间的模型精度混淆矩阵
让我们仔细看看 RGB 空间:
- 船和车很少有误报或漏报…
- 假阳性和假阴性对鸟、猫、狗、鹿和青蛙来说是很重要的。
好了,现在是时候看看另一个颜色空间对分类动物是否有意思了。
在我们的例子中,颜色空间 CIE XYZ、CIE Lab、CIE Luv非常有趣:
- CIE Luv*:根据不同等级,其精度分布非常均匀(即使它仍然不是最佳的)
- CIE Lab*和 CIE XYZ: 鸟是永远预测不到的!它们在这个颜色空间里*概很难分辨。
我不知道你怎么想,但在这种情况下,我会尝试为我的模型提供 Y'UV 图像输入,以获得其整体精度和 CIE Luv 颜色空间,以获得其稳定性的精度。*
你也可以看看其他的混淆矩阵👊
最后,我想以最后一个小图来结束,它总结了我们的模型在不同颜色空间和这些点下的训练:
- 可以用几个色彩空间来馈给你的模型。这就是 S.N .高达在他的论文中所做的,其中他提出了具有 RGB+HSV+YUV+LAB+HED 的分类模型。注:多不代表好。通过增加空间 CIE XYZ,他获得了较低的精度。
- 在深度学*的情况下,网络越深,就越会扭曲空间。因此,色彩空间的影响将最小化。但是研究颜色仍然非常有趣,主要是在机器学*或者用定义的规则中。
- 从一个型号到另一个型号,色彩空间的对准确度的影响不一定相同。换句话说,对于同一数据集,RGB 空间可能更适合 SVM,而 HSL 空间更适合 KNN。
****
知识就是分享。
支持我,获得 中我所有文章的访问一键点击此处 。
来源和参考
- 国际照明委员会
颜色通道表征对可转移性的影响 - 卷积神经网络,J. Diaz-Cely,C. Arce-Lopera,J.C. Mena,L. Quintero
- 使用机器学*技术和不同的颜色空间根据成熟程度对鹅莓果进行分类,W. Castro,J. Oblitas,M. De-la-Torre,C. Cotrina,k .巴赞,H. Avila-George
- 不同作物品种叶片分割的情境化方法,M.P. Rico-Fernándeza,R. Rios-Cabreraa,M. Castelána,H.-I. Guerrero-Reyesa,A. Juarez-Maldonado
- 非理想光照条件下机器人应用的立体视觉,L. Nalpantidis,A. Gasteratos
- 给动画世界上色:通过动画电影探索人类对颜色的感知和偏好。
- 基于人工神经网络的 YCbCr 皮肤检测
算法的性能分析,A. M. Aibinu,A. A. Shafie 和 M. J. E. Salami - 用于区域分割的颜色信息,Y-I. Ohta,T. Kanade T. Sakai
- 颜色空间选择对有色物体的可检测性和可辨别性的影响,A. Rasouli 和 J.K. Tsotsos
- 色彩,媒体整合与传播中心(MICC)
- ColorNet:探讨颜色空间对图像分类的重要性,S.N .高达,袁春华
- 通过颜色去卷积对组织化学染色进行定量,
A.C.C. Ruifrok,D. A. Johnston - 用于显微血液图像中急性淋巴细胞白血病检测的基于计算机计数的系统,K. Ben-Suliman,A. Krzyżak
- 机器学*:人工神经元的进化和研究进展
- 所有的色彩空间图片都是从维基百科和 Mathworks 图片中创建的
- 方程式是自制的:用乳胶写的
- 所有其他没有来源的内容都是自制的,可以免费使用
为什么凸性是最优化的关键
机器学*
使用凸成本函数是很容易的
凸函数。来源维基百科。
当开始机器学*时,你首先会遇到的最有趣的事情是优化算法,具体来说,就是梯度下降,这是一种一阶迭代优化算法,用于最小化成本函数。
梯度下降背后的直觉是收敛到一个解,这可能是邻域中的局部最小值,或者在最好的情况下,是全局最小值。
一切似乎都很好,直到你开始质疑自己的收敛问题。对凸性有很好的理解有助于你证明梯度下降思想背后的直觉。所以让我们讨论同样的问题。
希望*家对梯度下降有很好的理解。查看这篇文章的摘要。
“让我们达到全局最小值。”
nvsyashwanth.github.io](https://nvsyashwanth.github.io/machinelearningmaster/understanding-gradient-descent/)
凸集
简单来说,把凸集想象成这样的形状,其中连接该集合中两点的任何线都不会在该集合之外。这叫做凸集。
看看下面的例子。
理解凸集。图片由作者提供(使用 Adobe Xd 制作)。
很明显,任何连接圆或正方形(最左边和中间的形状)上的两点的线,将具有该形状内的所有线段。这些是凸集的例子。
另一方面,上图中最右边的形状有一部分线在形状外面。由此可见,这不是凸集。
凸集 C 可以表示如下。
凸集条件。作者图片
碑文
考虑一个函数的图形 f.
题图是位于函数的图形之上或上方的一组点。
一个函数的词表。图片由作者提供(使用 Adobe Xd 制作)。
凸函数
好了,现在你明白了什么是凸集和上图,我们可以谈谈凸函数了。
凸函数及其题图。来源维基百科。
一个函数 f 称之为凸函数 如果它的词牌是一个凸集(如下图左边绿色图所示) 。
这意味着在这个图 上画的每一条线段总是 等于或高于函数图。暂停一分钟,自己检查一下。
理解凸函数。图片由作者提供(使用 Adobe Xd 制作)。
这意味着函数 f 不是凸的,如果存在两个点 x,y 使得连接 f(x)和 f(y)的线段在函数 f 的曲线下面 。 这导致词牌的凸性丧失(如上图右侧红色部分所示)。 这意味着在这个图 上画的每一条线段都是 不总是等于或高于函数图 。通过在弯曲处取点也可以证明这一点。
凸性测试
在神经网络的情况下,*多数成本函数是非凸的。因此,你必须测试函数的凸性。
若函数 f 的二阶导数*于或等于 0,则称该函数为凸函数。
凸函数的条件。
凸函数例子: y=eˣ,y=x .这两个函数都是两次可微的。
如果-f(x)(减去 f(x))是凸函数,那么这个函数叫做凹函数。
凹函数的条件。
凹函数的例子: y=-eˣ.这个函数可微分两次。
让我们通过绘制指数函数 eˣ.来检查凸性
用于绘制凸凹函数的代码。作者写的。
代码输出:
凸凹函数。图片由作者提供(代码输出)。
梯度下降优化中的凸性
如前所述,梯度下降是一种一阶迭代优化算法,用于最小化成本函数。
为了理解凸性如何在梯度下降中起关键作用,让我们以凸和非凸成本函数为例。
对于线性回归模型,我们定义了成本函数均方误差(MSE ),它衡量实际值和预测值之间的平均平方差。我们的目标是最小化这个成本函数,以便提高模型的准确性。 MSE 是一个 凸函数(两次可微)。这意味着没有局部最小值,只有全局最小值。因此梯度下降将收敛到全局最小值。
MSE 方程。图片由作者提供。
现在让我们考虑一个非凸的成本函数。在这种情况下,取一个任意的非凸函数,如下图所示。
非凸函数上的梯度下降。图片由作者提供(使用 Adobe Xd 制作)。
您可以看到,梯度下降不会收敛到全局最小值,而是会停止在局部最小值,因为该点的梯度为零(斜率为 0)并且在邻域中最小。解决这个问题的一个方法是使用动量的概念。
结论
凸函数在优化中起着巨*的作用。优化是机器学*模型的核心。理解凸性对于 same 来说非常重要,我相信你从这篇文章中已经了解到了。
谢谢你。下一场见。
了解如何在 Python 中使用 NamedTuple 和 Dataclass
创建一个超出 init 的 Python 对象
如果我要求你在 10 秒内为交易记录创建一个 Python 类,你会怎么做?可能*多数人都会用__init__
创建一个类。在本文中,我想分享 Python 中构造一个类的两个替代方案:名为 Tuple 和 Dataclass 。最后,我将比较这三个选项的性能,并就何时使用哪一个给出一些建议。
我将在本文中把带有 *__init__*
的类称为“常规”Python 类。请让我知道它是否有正式名称。如果您想深入了解命名元组和数据类,请查看参考链接。
每笔支付交易都有发送者、接收者、日期和金额。如果我使用常规的 Python 类,它可能看起来像这样:
很直白。但是说实话,代码很多,至少很多行。在一个用例中,我从一个源(例如 Kafka)消费实时交易记录,我不希望有修改记录的灵活性。我如何以更干净的方式实现这一点?
命名元组
命名元组是构造类的一个很好的选择。命名元组基本上是 Python 内置元组数据类型的扩展。Python 的 tuple 是一种简单的数据结构,用于对不同类型的对象进行分组。它的定义特征是是不可变的。
不可变对象是在创建后其状态不能被修改的对象。
在 Python 中,不可变类型有 int , float , bool , str , tuple 和 unicode 。
然而,内置元组类型的一个缺点是它给程序员增加了很多责任。当访问内置元组的一个属性时,需要知道它的索引。如果你正在开发一个将被成千上万的用户使用的库,这会引起一些混乱。
另一种可能的方法是使用内置字典,像{"sender":"","receiver":"","date":"","amount":""}
。但是这里的问题是你需要正确的拼写键名。而且根本不是一个档次。
我们可以使用命名元组来避免这些问题。命名元组允许我们给元素命名,所以我们可以通过属性名和它的索引来访问属性。
Python3.8 中的命名元组实际上有两种类型,一种来自已经存在很久的collections
包,另一种来自从 Python3.6 开始引入的typing
包。
collections . named tuple
我先从[*collections*](https://docs.python.org/3.8/library/collections.html)
包 。这个包提供了 Python 通用内置类型的替代方案,如字典、列表、集合和元组。
这就是如何创建一个namedtuple
类。哇,就一行代码!
我们刚刚创建的Transaction
本质上是一个类。它有属性发送者、接收者、日期、金额和_fields
,允许我们通过名称和索引来访问属性。
创建一个对象
创建一个 namedtuple 对象和创建类一样简单。我还喜欢的是 namedtuple 对象的表示。您也可以通过覆盖__repr__
方法在常规 Python 类中定制它。
为属性分配默认值也是可能的。
named tuple-create-default . py
⚠️ ️ 注意了!
named tuple-create-default-career . py
在这个例子中,我只将None
作为默认值分配给一个属性。分配了哪个属性?想想吧。
None
被分配给属性amount
,它是元组的最后一个属性。
这里发生了什么事?我鼓励你在进一步阅读前思考一分钟。
好吧。本质上,命名元组的创建遵循与常规 Python 类相同的语法。
神奇的是,当我们创建一个类时,非默认参数不能跟在默认参数后面。在常规的 Python 类中,我们会立即注意到这个错误。这个语法规则也适用于命名元组,,所以默认值将从最后一个元素开始分配。
named tuple-create-default-regular-class . py
回到子主题:创建对象。也可以使用_make()
方法创建一个namedtuple
对象。但是你需要记住属性的顺序。
访问属性
访问namedtuple
中的属性也非常简单。事实上,它是一个元组,我们可以像访问基本元组一样访问属性。一个namedtuple
可以用_asdict()
转换成一本字典。
分配属性
等等?你刚才说 tuple 是一个不可变的数据类型,你怎么能在一个不可变的对象中分配属性呢?
这取决于元组属性的数据类型。如果属性像 str,float,int 一样是不可变的,那么我们就不能修改状态,比如("1.0","xiaoxu","2019-01-01")
。
但是如果属性本身像 list 一样是可变的,你可以改变列表中的元素,而不让元组知道这种改变。
具有可变属性的 namedtuple
在这个例子中,发送者是一个可变对象的列表。在修改属性之前,我使用 Python 内置函数id
来检查链表的内存地址。
然后,我将列表的第二个元素更改为“gaga ”,并打印出同一列表的 id。原来 id 是一样的!因此,对于命名的元组,状态没有改变。
具有可变属性 2 的 namedtuple
但是如果我们用相同的元素创建一个新的列表:“jojo”和“gaga”,并试图将它分配给 namedtuple ,这是行不通的,因为它们有不同的 id。
继承类
有时我们想要继承一个类并扩展其属性。在namedtuple
中,您可以使用@property
添加一个新属性。就像你看到的一样简单。
分配字段名导致 csv 或 db 查询返回元组
我喜欢namedtuple
的另一个很酷的特性是,它可以将 csv 记录或 db 查询结果映射到一个 namedtuple 对象。它可以减少样板代码的数量。
named duple-read-CSV-database . py
打字。名为的一对
创建一个名为的元组的第二个选项是使用typing.NamedTuple
。它来自typing
包,所以每个属性都绑定到一个类型。在我看来,这就是collections.namedtuple
和typing.NamedTuple
的唯一区别。
这种格式看起来有点像常规的 Python 类,但是带有类型。确保你继承了typing.NamedTuple
。
从typing.NamedTuple
创建的类有一个额外的字段_field_types
,显示每个属性的定义类型。
有时候,如果你真的不想为某些属性定义类型,你可以使用 type Any
。静态类型检查器会将每个类型视为与任何类型兼容。
创建一个对象,访问属性&分配属性
在创建对象、访问属性和分配属性方面,typing.NamedTuple
与collections.tuple
相同。
数据类
Dataclass 是 Python 3.7 中引入的新特性。它被用作装饰器。它为我们实现了__init__
、__repr__
等。
命名元组的行为类似于元组,而数据类的行为更像常规的 Python 类。我为什么这么说?因为默认情况下,属性都是可变的,只能通过名称访问,而不能通过索引访问。
继承类
当继承一个数据类时,子类的属性跟随父类,所以要小心默认值。如果父类中的一个字段被赋予了默认值,那么子类中的所有属性都应该有默认值。
不可变数据类
命名元组的关键特性之一是不可变的。您也可以通过frozen=True
在 dataclass 中设置不可变的属性。
在冻结的数据类中,如果试图修改状态,将引发FrozenInstanceError
异常。
性能比较
最后,我想比较一下常规 Python 类、collections.namedtuple
、typing.NamedTuple
和dataclass
的性能。比较内容包括:
- 对象的*小
- 创建对象的时间
- 检索属性的时间到了
我已经创建了 5 个不同的类。最后一个是带有字段__slot__
的优化的数据类。槽用于使类更快,使用更少的内存。
named tuple-data class-comparison-init . py
比较物体的*小
第一步是使用sys.getsizeof
比较每个创建对象的*小。
named tuple-data class-size-comparison . py
比较时间创建一个对象
然后我用 Python 内置函数[timeit](https://docs.python.org/3.8/library/timeit.html)
对比对象创建时间。
named tuple-data class-creation-comparison . py
比较时间访问属性
named duple-data class-access-comparison . py
结果
named tuple-data class-comparsion-result . CSV
基于元组的对象通常较*,因为它们包含属性名和索引。具有槽的数据类具有最少的对象创建时间。常规 Python 类似乎擅长访问属性。
结论
在做决定之前,你必须了解你的要求。
- 您希望对象是不可变的吗?
- 您希望在 Python 代码中显式输入吗?
- 你在乎记忆吗?
- 你喜欢简洁的风格还是易读的风格?
……
选择常规 Python 类,如果你……
- 用于创建任何带有
__init__
的类。拥有__init__
让我感觉很安全。 - 每个人都能理解代码。
- 正在使用 3.6 以下的 Python 版本。
- 不需要不可变的对象。
- 不喜欢显式打字。
选择 **collections.namedtuple**
类如果你……
- 想要不可变的对象。
- 不喜欢显式打字。
- 想要更少的代码。少即是多!
选择 **typing.NamedTuple**
类如果你……
- 想要不可变的对象。
- 确实需要显式输入。
- 想要更少的代码。少即是多!
选择 **dataclass**
如果你……
- 希望灵活地使它既可变又不可变。
- 确实需要显式输入。
- 想要更少的代码。少即是多!
- 想要好的速度表现。
希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。
资源
[## 用命名元组编写干净的 Python-dbader.org
命名元组是手动定义类的一个很好的选择,它们还有一些其他有趣的特性…
dbader.org](https://dbader.org/blog/writing-clean-python-with-namedtuples) [## Python 3.7 中数据类的终极指南——真正的 Python
Python 3.7 中一个令人兴奋的新特性是数据类。数据类通常包含…
realpython.com](https://realpython.com/python-data-classes/) [## 数据类-数据类- Python 3.8.3 文档
这个模块提供了一个装饰器和函数,用于自动添加生成的特殊方法,比如和…
docs.python.org](https://docs.python.org/3/library/dataclasses.html) [## 类型-支持类型提示- Python 3.8.3 文档
源代码:Lib/typing.py 注意 Python 运行时不强制函数和变量类型注释。他们可以是…
docs.python.org](https://docs.python.org/3/library/typing.html) [## 集合-容器数据类型- Python 3.8.3 文档
源代码:Lib/collections/init。py 3.3 版本新增。该类可以用来模拟嵌套的作用域,并且是…
docs.python.org](https://docs.python.org/3/library/collections.html)
理解 Python 中的继承
一个简单但重要的设计模式
In 继承是任何面向对象编程语言的关键特性,包括 Python。当您在 Python 3 中编写代码时,您可能已经在不知不觉中使用了继承特性。与 Java 等其他面向对象语言不同,Python 支持单继承和多继承。
在本文中,我将讨论 Python 中不同类型的继承,并解释这个概念是如何集成到 Python 3 中的,以及我们如何在日常编程中使用它。我们开始吧!
什么是继承?
继承是基于现有的类创建一个类,新的类继承现有类的属性和方法。新的类通常称为“子类”,现有的类称为“父类”。这一概念与现实世界的运作方式相同,在现实世界中,孩子从父母那里继承了一些特征,但同时,他或她也可以拥有独特的特征。
任何面向对象编程语言中的继承都应该遵循 Liskov 替换原则 其中说:
如果 S 是 T 的子类型,那么 T 类型的对象可以用 S 类型的对象替换
这意味着子类将从父类继承属性、方法和实现。允许修改和添加新功能,但不能从父功能中删除功能。
例如,我有一个父类Student
和子类HighSchoolStudent
和PrimarySchoolStudent
。两个子类都继承了来自Student
的所有属性,但是它们中的每一个对于grade
都有不同的值。例如,如果你想创建一个不需要school
属性的对象,那么你可能不应该在这里继承。
每个类的祖先:Object
如果你在 Python 中创建了一个新类,那么它已经从某个地方继承了。我们从一个虚拟类BaseClass
开始,使用__bases__
方法检查它的父类。我们得到了<class 'object'>
,它有一些带有双前导和尾随下划线的方法,所以从object
继承的类也实现了这些方法。它们在 Python 中被称为魔法方法。
类 **object**
是 Python 中每个类的祖先。如果做object.__bases__
,会得到一个空值。
在 Python 2.x 中,仍然像 *class BaseClass(object)*
一样强制显式继承 *object*
,但是从 Python 3.x 开始,已经没有必要了。
对于 Python 中定制的异常,它们应该从类BaseException
或其子类Exception
扩展而来,尽管BaseException
的父类也是object
。不能提升不是从 **BaseException**
类继承的类。
raise BaseClass
# TypeError: exceptions must derive from BaseException
单一遗传
单一继承是指类只从一个类继承。根据在子类中做什么,子类可能有不同的结构。这里我们使用 UML 来显示类之间的关系。在每个例子中,我将首先说明用例,然后画出 UML 图。最后,我将展示代码。请在检查答案之前考虑解决方案。
例 1 :我有一个父类Job
,它有一个属性person_name
和一个方法task
。我想创建一个继承自Job
的子类Teacher
,并用“教学生”覆盖task
。
UML 图看起来像这样。这是一个非常简单的例子,我们唯一需要做的就是覆盖方法task()
。
UML 图
让我们检查一下代码。
例 2: 我们仍然使用Job
作为父类,但是这次除了重写task()
之外,我还想创建一个带有额外属性school_name
的子类Teacher
。
在 UML 图中,我们会在类Teacher
中找到一个新的属性。
UML 图—新属性
school
是一个新属性,所以这意味着我们需要覆盖__init__
方法来添加属性。在这个例子中,我们将使用一个内置函数[super()](https://docs.python.org/3.8/library/functions.html#super)
。
简而言之,super()
返回一个将方法调用委托给父类的对象。它允许您重用父类的属性和行为。在下面的代码中,super().__init__
将执行Job.__init__
中的所有内容,以避免重复代码。
super()
可以在其他方法中使用,所以也可以在子类中调用super().task()
。
例 3: 除了例 2,我想防止类Job
被实例化,因为没有名为Job
的作业。
UML 图将与示例 2 相同。
UML 图—新属性
为了实现这一点,我们将讨论 Python 中抽象类的概念。抽象类是打算被继承但从不实例化的类。 Python 提供了abc
模块定义抽象基类,提供了@abstractmethod
decorator 定义抽象方法。
所以新的Job
类是从ABC
类扩展而来的,方法task()
有装饰器@abstractmethod
。通过这样做,它告诉程序员这个类不允许被实例化。如果您试图创建一个对象,它将引发一个异常。此外,将任何有意义的逻辑放入抽象方法是没有意义的,尽管你仍然可以这样做。
继承-抽象. py
job = Job("xiaoxu")
# TypeError: Can't instantiate abstract class Job with abstract methods task
子类Teacher
将与前面的例子相同。
多重遗传
多重继承是指类从多个父类继承。在某种程度上,这可以减少冗余,但也会增加代码的复杂性。你应该清楚自己在做什么。
例子:我有一个父类Dad
和另一个父类Mum
。子类Kid
扩展了两个父类。父类如下所示。有些属性具有相同的值(例如城市),但有些属性不具有相同的值(例如眼睛颜色)。
好吧,那么问题来了。一个kid
对象的默认eye_color
是什么?当涉及到多重继承时,子类将首先在它自己的类中搜索属性,如果没有,那么在它的父类中以深度优先、从左到右的顺序搜索。这在 Python 中叫做 方法解析顺序 (MRO)。MRO 定义了 Python 如何搜索继承的方法。
幸运的是,我们可以通过执行Kid.__mro__
来检查这个订单。由于Kid
类第一次访问Dad
,那么它默认会有蓝眼睛。此外,kid
将会兼具游泳和舞蹈“技能”。
如果你只想从类Mum
继承属性,那么你可以在Kid
的__init__
中明确提到。
结论
继承是任何面向对象编程语言中极其常见的设计模式。它有助于程序员重用代码,节省*量的时间和精力。这种模式在 Python 中得到了很好的实现,既有单一继承的可能性,也有多重继承的可能性。
我希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。
参考
[## 用 Python super() -真正的 Python 来增强你的类
免费奖励:关于 Python 精通的 5 个想法,这是一个面向 Python 开发者的免费课程,向你展示了路线图和…
realpython.com](https://realpython.com/python-super/)
理解卷积神经网络中的局部感受野
技术和解释
想过为什么卷积神经网络中的所有神经元都是相连的吗?
这篇文章面向所有水平的练*机器学*或更具体地说深度学*的个人。
C 旋转神经网络(CNN)具有对通过网络输入的图像的仿射变换保持不变的特性。这提供了识别图像中偏移、倾斜或轻微扭曲的图案的能力。
由于 CNN 架构的三个主要属性,引入了仿射不变性的这些特征。
- 局部感受野
- 共享权重
- 空间子采样
在这篇文章中,我们将探索局部感受野,理解它们的目的和它们在 CNN 架构中的优势。
介绍
在 CNN 架构中,有几个层的组合,其中有一组单元或神经元。
这些单元接收来自前一层中类似子部分的相应单元的输入。在传统的全连接前馈神经网络中,层内的单元/神经元接收来自前一层的所有单元的输入。
曾经想知道为什么卷积神经网络中的所有神经元没有连接起来吗?
将前一层的所有单元连接到当前层的单元是不切实际的。由于连接的增加,训练这种网络的计算资源将是巨*的。此外,这样的网络将需要一组更广泛的训练数据来利用网络的全部容量。
但更重要的是,CNN 中的每个神经元负责输入数据的一个定义区域,这使神经元能够学*构成图像的线条、边缘和小细节等模式。
神经元或单元在输入数据中暴露的这一限定的空间区域被称为局部感受野。
感受野
感受野是空间或空间结构的定义部分,包含向相应层内的一组单元提供输入的单元。
感受野由卷积神经网络中一层的滤波器*小来定义。感受野也是一个层内的神经元或单元可以接触到的输入数据范围的指示(见下图)。
例子
下图显示了输入体积为 32x32x3 的输入数据(红色)。
输入体积基本上告诉我们,输入数据中的图像具有 32×32(高/宽)的尺寸,沿着三个颜色通道:红色、绿色和蓝色。
图像中的第二个对象(蓝色)代表一个卷积层。conv 层的滤波器*小为 5×5,对应于该层中每个神经元对输入数据的局部感受野面积。
感受野不仅作用于输入体积的面积,而且也作用于深度,在本例中是 3。
对于下图中的示例,我们可以根据输入量得出每个神经元具有的可训练参数的数量。这是感受野乘以输入体积的深度(5x5x3 = 75 个可训练参数)。
假设我们有(32,32,3)的输入量,卷积层的感受野是 5×5,那么卷积层中的每个神经元将具有 5×5×3 区域的权重,这是神经元内的 75 个权重。
卷积图层的输出是特征地图,图层中特征地图的数量是一个定义的超参数,通过将特征地图的维度乘以可训练参数的数量,可以推导出特征地图中的连接数。
局部感受野是由卷积层中的神经元在卷积过程中暴露的输入数据内容所占据的定义的分段区域。
LeNet 论文介绍了利用卷积神经网络进行字符识别的第一个用例。它还介绍了 CNN 中局部感受野的概念和实现。
照片由 Cole Wyland 在 Unsplash 上拍摄
但是,局部感受域或者更确切地说是仅暴露于一段输入数据的后续单位——局部连接——的概念早在 20 世纪 60 年代就在一项研究中引入了,该研究由探索猫的视觉皮层。
优势
局部感受野在识别视觉模式方面的优势在于,层内的单元或神经元直接负责从一小块输入数据区域中学*视觉特征——这不是全连接神经网络的情况,在全连接神经网络中,单元接收来自前一层内单元的输入。
在 CNN 内的较低层中,单元/神经元学*图像内的低级特征,例如线条、边缘、轮廓等。较高层学*图像的更多抽象特征,例如形状,因为较高层内的单元暴露的图像区域较*,这是先前较低层的感受野累积的结果。
下面的代码片段展示了如何使用 TensorFlow 深度学* python 库定义卷积层。
Conv2D 类构造函数接受参数“过滤器”,该参数对应于过滤器产生的输出数量,也是特征映射的数量。参数“kernel_size”采用一个表示内核/筛选器的高度和宽度的整数;在这种情况下,整数 5 对应于尺寸 5×5。
simple_conv_layer = tf.keras.layers.Conv2D(filters=6, kernel_size=5, activation='relu', input_shape=(28,28,1))
我希望这篇文章对你有用。
要联系我或找到更多类似本文的内容,请执行以下操作:
- 订阅我的 YouTube 频道 即将发布的视频内容 这里
- 跟着我上 中
- 通过 LinkedIn 联系我
理解深度神经网络中使用的一种常见转换技术
towardsdatascience.com](/batch-normalization-explained-algorithm-breakdown-23d2794511c) [## 作为机器学*工程师你需要的 5 个软技能(以及为什么)
包括成为任何劳动力的有用组成部分的提示
towardsdatascience.com](/5-soft-skills-you-need-as-a-machine-learning-engineer-and-why-41ef6854cef6)