TowardsDataScience 博客中文翻译 2020(三百三十八)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

搜索(第三部分)——弹性变压器

原文:https://towardsdatascience.com/elastic-transformers-ae011e8f5b88?source=collection_archive---------26-----------------------

让 BERT 变得有弹性——Jupyter 笔记本上的可扩展语义搜索

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是一个关于如何通过 transformers 建立和添加语义搜索作为弹性搜索索引的技术教程。我们将经历所有需要的步骤,并将介绍实用程序类 ElasticTransformers。最后,我们将考虑一些速度比较。

这是关于搜索的三部分系列的最后一部分。对于这如何适应更广泛的搜索主题的非技术讨论,请考虑前两部分。

  • Pt 1 是对搜索引擎典型构件的一个温和的介绍
  • Pt 2 中,我们对搜索的关键词和上下文嵌入进行了并排比较

在第 1 部分中,我们已经看到了 Elasticsearch 如何提供一系列现成的搜索功能。在本文中,我们将展示如何再添加一个——上下文语义搜索

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由作者制作,用 gifox

“如何做”

我将在这里简要概述这种方法。也可以关注 github

体系结构

我们将在本地部署 Elasticsearch 作为 docker 容器。数据将存储在本地。使用 Jupyter notebook,我们将使用句子转换器库将数据分块并迭代地嵌入记录批,并提交给索引。最后,我们还将执行笔记本外的搜索。为了实现这一切,我准备了一个实用程序类 ElasticTransformers

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

积木

所有这些步骤也记录在 github repo 中,并作为随附笔记本的一部分执行。

  • 数据我用的是 Rohk 的百万新闻头条。这些数据代表了美国广播公司新闻大约 18 年的头条新闻。长时间跨度以及数据集的大小为实验提供了良好的基础。
  • **Elasticsearch Docker 容器。**在本地开始使用 Elasticsearch 的一个非常快速简单的方法是将其部署为 Docker 容器。按照步骤这里对于本教程,您只需要运行两个步骤:
  • 拉动图像
  • 使用 Docker 启动单节点集群
  • 对于弹性上的向量搜索,我们使用密集向量搜索功能,这要感谢这些优秀的资源
  • 如前所述,我们使用(SBERT) 句子转换器进行嵌入,作为一种高性能且非常容易访问语义相似性的方法

使用这些实用程序,我们依次:创建一个索引,编写几个不同大小的数据集以嵌入索引,并执行一些实验

对于懒惰的人…嗯,忙碌的人…

最后,所有这些步骤过去执行起来都很复杂。值得庆幸的是,现在是 2020 年,很多事情都变得简单了:我们可以在 Docker 上部署一个只有两行代码的 Elasticsearch(实际上是从 2016 年开始的), transformers 预先训练有一些先进的库,我们可以在我们舒适的 Jupyter 笔记本上完成以上所有工作…

所有这些步骤也记录在 github repo 中,并作为随附笔记本的一部分执行。

使用弹性变形器

ElasticTrasnformers 构建在 elasticsearch 库的基础上,它简单地包装了一些实例化步骤,添加了简单的索引创建实用程序&映射(我个人一直在努力解决这个问题),重要的是,它是一个易于使用的大文档分块工具,可以将嵌入内容写入搜索索引

初始化类以及(可选)要使用的索引的名称

et = ElasticTransformers(url = ’http://localhost:9300', index_name = 'et-tiny')

为索引创建规格。可以基于关键字搜索或语义(密集向量)搜索是否需要相关字段来提供相关字段的列表。它也有密集向量大小的参数,因为这些参数可以变化

et.create_index_spec(
  text_fields=[‘publish_date’,’headline_text’],
  dense_fields=[‘headline_text_embedding’],
  dense_fields_dim=768
)

创建索引 —使用之前创建的规范创建一个索引以备搜索

et.create_index()

写入大文件 —将一个大的 csv 文件分解成块,并反复使用预定义的嵌入实用程序为每个块创建嵌入列表,然后将结果提供给索引。在笔记本中,我们展示了一个如何创建 embed_wrapper 的例子,注意实际的嵌入器可以是任何函数。我们使用带 distilbert 的句子转换器,因为它在速度和性能之间提供了一个很好的平衡。然而,这可能因您感兴趣的用例而异。

et.write_large_csv(‘data/tiny_sample.csv’,chunksize=1000,embedder=embed_wrapper,field_to_embed=’headline_text’)

搜索 —可以选择关键字(弹性中的“匹配”)或上下文(弹性中的“密集”)搜索。注意,它需要 write_large_csv 中使用的相同嵌入函数。这只检查 type = 'dense ‘是否作为嵌入搜索处理,否则可以采用常见的:’ match ‘,‘通配符’,’ fuzzy '等。

et.search(query = ’search these terms’, field = ’headline_text’, type = ’match’, embedder = embed_wrapper, size = 1000)

我们将以几种不同的索引大小(小型:1k,中型:100k 和大型:110 万(所有)标题)索引来自一百万个新闻标题的数据。最后,我们将使用这些来执行一些速度比较。

速度…

我们刚刚索引了大约 110 万个句子,但是所有这些真的有规模吗?自然,扫描海量指数时总会有减速,但减速多少呢?

我进行了几次测试(每次重复 10 次):

  • 不同长度的查询:1、2 和 5 个令牌
  • 结果大小不同的查询 1、10 和 100 个结果

结果如下图所示:

  • 关键词搜索—索引大小增加 1000 倍,速度降低约 8 倍;结果大小增加 100 倍,速度降低约 4 倍。所有这些都不到 10 毫秒
  • 然而,在上下文搜索方面,性能会显著降低,从一个大(1mn)索引进行单独搜索需要 8 秒。与 100k 的中等规模指数相比,这显然是一个明显的放缓,仍然远远小于 1 秒

值得注意的是,交互式搜索应用程序的常见要求是在 1 秒内检索到结果,在某些情况下不到 300 毫秒,因此这是一个显著的减速。下面探讨了一些解决方案

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

提高速度

搜索速度是一个至关重要的方面,因为搜索通常需要交互,文档大小通常也很重要。在这种情况下,我们可以看到,随着数据集大小的增加,速度呈指数级下降。这是因为我们将所有文档与查询进行比较,而不是在关键字搜索方法中使用倒排索引。有多种方法可以解决这个问题,但不幸的是,它们都需要在精度方面进行权衡。

例如,近似最近邻 (ANN)方法将使用更少的空间和更快的执行速度,代价是有时返回的结果并不完全相关——对于实现,请查看 Faissairy以及这个关于混合嵌入、关键字搜索和使用 Faiss 的很好的例子。大谈规模人工神经网络的不同权衡以及如何选择一个— 十亿级近似最近邻搜索。另一种方法可能是执行聚集聚类并仅在检索时收集相关数量的结果。查看 fastcluster 的实现。

Haystack 为结合变形金刚和搜索提供了一个广泛的框架。然而,查询被认为是自然语言问题,并且采用了问答框架。不同之处在于,在 QA 框架中,查询和每个单独的候选文档之间的匹配质量是成对评估的。这本身就是一个计算量大得多的问题,与我们在这里所做的相反,在这里每个文档都由一个嵌入来表示,该嵌入在运行时与查询进行比较。较大的计算负荷最初是通过将搜索空间减少到较少数量的相关候选项来处理的。

冗长的文件

我们看到的上下文解决方案也受到文档大小的限制——受到 transformers 模型的令牌限制的约束。在这种情况下,我们可以容纳多达 512 个标记(类词块,例如单词 look 是一个标记,look是两个标记— look & ing,等等。).这种约束适用于标题或甚至段落,但不适用于全尺寸文档,例如新闻文章正文、研究论文等。

这些模型可以扩展到更长的序列,但是随着长度的增加,它们的内存使用量呈二次方增长。常见的解决方案倾向于保持标准长度,但使用变通方法,例如在相关的地方对嵌入进行一些平均。这仍然是一个活跃的研究领域,最近一个值得注意的新发现是big bird——一个基于 Google Research transformer 的模型,它可以处理更长的序列,同时保持内存使用量与文档大小成线性增长

结论

我们已经看到了如何轻松地部署一个 Elasticsearch 容器,并用一些我们可用的最强大的上下文嵌入来索引它。评估表明,对于大型索引,语义搜索可能会变慢,但是,我们也考虑了一些替代解决方案。

这个领域的未来仍然充满希望,并且越来越容易实现。

希望这是有用的。感谢您的阅读。如果你想和我打招呼或者只是想告诉我我错了,请随时通过 LinkedIn 联系我

弹性搜索分析教程

原文:https://towardsdatascience.com/elasticsearch-analytics-tutorial-7d47029804a0?source=collection_archive---------40-----------------------

使用 Knowi 连接到 Elasticsearch 数据源,通过基于搜索的分析查询您的数据,并创建可视化效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

安德烈斯·西蒙在 Unsplash 上拍摄的照片

目录

介绍

Elasticsearch 是一个可伸缩的全文搜索引擎,具有 HTTP web 接口和基于模式的 JSON 文档。当 Elasticsearch 作为基础引擎在后台使用时,它会大放异彩,为具有复杂搜索功能和许多需求的应用程序提供动力。

目前,Kibana 在 Elastic 广受欢迎的 ELK 堆栈中的位置使其成为可视化和分析 Elasticsearch 数据的最常用工具。虽然 Kibana 肯定有其自身的优点,但它也有一些缺点;也就是说,它不支持与任何其他数据源的集成,也不提供用户管理功能或对发出主动警报的支持。

在某些时候,Elasticsearch 用户可能会认为这些缺点中的一个或多个是交易的破坏者,并选择不同的可视化平台来解决这些缺点。这就是知识发挥作用的地方。除了 Elasticsearch、大量用户管理功能、对警报的支持以及基于搜索的分析和机器学习之外,Knowi 还提供了对 35 个其他数据源的广泛原生集成。如果您有兴趣了解如何使用 Knowi 来可视化来自 Elasticsearch 的数据,那么您来对地方了。

连接到 Elasticsearch

登录免费的 Knowi 试用账户后,您需要按照以下步骤设置您的 Elasticsearch 数据源:

  1. 前往屏幕左侧面板的中上方,点击“数据源”
  2. 转到列出的 NoSQL 数据源的右上方,点击 Elasticsearch。
  3. 点击屏幕底部的“测试连接”。
  4. 确保连接成功后,点击“保存”

图片来自作者

使用基于搜索的分析进行查询

既然我们已经建立了一个数据源,那么是时候对我们的数据进行查询了。我们的目标是分析来自佐治亚州的交通数据,并特别关注与元旦某个数据集的西行车辆相关的数据。为此,请遵循以下步骤:

  1. 当您保存数据源时,您应该会在屏幕顶部收到一个警告,提示“Datasource Added”。配置查询。点击“查询”开始一旦你这样做了,你将被带到一个查询生成器,你将在你的屏幕上方看到一个新的提示,上面写着“获取索引”这意味着 Knowi 会从您刚刚连接的 Elasticsearch 数据源中自动索引表。在执行任何其他操作之前,将您的查询命名为“报告名称*”下的“元旦西行公交”
  2. 向下悬停到“索引”点击栏内,您将看到您的 Elasticsearch 数据库中存在的每个索引。点击“运输”这将促使 Knowi 的广泛原生集成生成一个 Elasticsearch JSON 查询,该查询从 transit 表的所有列中调用前 10,000 行。
  3. 选择屏幕左下角的“预览”。这将向您显示预览数据,但是如果您查看可视化,您会注意到我自动选择了一个地图视图。因为我们的预览数据包含经度和纬度坐标,Know 足够智能,可以自动将预览可视化转换为地理聚类/自定义地图可视化。如果你点击可视化中的一个点,它将扩展地图。这正是我们想要看到的可视化类型,但是请记住,我们只对元旦开始的西行活动感兴趣。
  4. 为了过滤我们的数据,我们将使用 Knowi 基于搜索的分析功能,该功能简单、直观,通过允许非技术用户用简单的英语提问并实时接收结果来吸引他们。只需输入“告诉我 2017-01-01 西行”并稍等片刻。
  5. 如果你回头看看你的可视化,你应该看到在你的地图上有更少的观察,因为你把它过滤到一个特定的子集。这是对你的提示,你已经做了所有正确的事情,现在你只需要点击“保存并立即运行”来完成你的查询。

图片来自作者

祝贺您使用 Knowi 设置了您的第一个 Elasticsearch 查询和可视化!

分析和可视化您的数据

当您保存并运行您的查询时,Knowi 将原始数据作为数据集保存在 Knowi 的弹性数据仓库中,它还将您的预览可视化保存为 Knowi 帐户中的一个小部件。为了进一步分析和可视化您的数据,请按照下列步骤操作:

  1. 小部件被构建为存在于仪表板上。现在,你有一个没有家的部件。移动到左侧面板,点击“仪表板”,给你的小部件一个家然后,单击“+”图标创建一个新仪表板,并将其命名为“Westbound Transit Dashboard”单击“确定”保存此仪表板;一旦你这么做了,你会立刻被带到那里。
  2. 回到屏幕左侧的面板,在“仪表板”的正下方,点击“Widgets”在这里,您将看到您刚刚制作的“元旦西行过境”小部件。只需将它拖过并放开,就可以将其添加到您的仪表板中。
  3. 现在,您的小部件就在仪表板上,您可以对数据进行更多的分析和可视化。单击您的小部件右上角的 3 点图标,然后选择“分析”,以便转到当前为您的小部件提供支持的原始数据集。
  4. 我们的目标是在数据中添加不同路线分布的可视化。换句话说,我们想知道在元旦那天西行车辆最常经过的路线。因此,从列名所在的左窗格中,我们将把“route_name”(确保您选择了它而不是“route”)拖到“Grouping/Dimensions”中然后,即使它已经存在于“字段/指标”中,我们也要将它再次拖动到那里,但这一次,我们要将第二个“路由名称”字段中的“操作”更改为“计数”如果操作正确,您将看到六个路由名称以及它们出现的频率。
  5. 将“路由名称计数”字段从“字段/指标”拖到“排序依据”上,然后确保按降序对此字段进行排序。
  6. 现在,到你的屏幕顶部,在“数据”旁边,点击“可视化”然后,将“可视化类型”更改为“饼图”
  7. 因为我们想与地理聚类图并排查看新的饼图,所以我们不打算保存它;我们要克隆它。要做到这一点,前往你的屏幕右上角,找到“克隆”图标,它看起来像一张纸被放在另一张纸的上面,然后点击它。然后,将您的新部件命名为“元旦西行公交——路线频率”单击“克隆”完成此过程,然后单击“添加到仪表板”将新的小部件添加到您的仪表板。

如你所见,Glenwood 路是我们穿越最多的路,Moreland/Candler Park 紧随其后。在 Moreland/Cander Park 和 Perry Blvd/West highlights 之间有一个相当大的下降,然后在 Perry Blvd/West highlights 和接下来的三条路线之间有一个巨大的下降。

摘要

总之,我们在本教程开始时成功连接到 Elasticsearch 数据库,并使用 Knowi 基于搜索的分析功能来查询数据库中的运输数据集的特定子集,以使查询更容易。当我们成功运行这个查询时,Knowi 将查询的结果作为数据集存储在 Knowi 的弹性数据仓库中,并将我们用作小部件的预览可视化存储在 Knowi 帐户中。然后,我们创建了一个新的仪表板作为新部件的主页,并创建了另一个部件来进一步分析我们的数据集并回答一个重要的问题。

数据科学的弹性研究变得更加容易

原文:https://towardsdatascience.com/elasticsearch-for-data-science-just-got-way-easier-95912d724636?source=collection_archive---------18-----------------------

弹性搜索功能与熊猫的便利

Eland 是一个全新的 python 包,它在 Elasticsearch 和数据科学生态系统之间架起了一座桥梁。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

扎克里·佩里Unsplash 上拍摄的照片

Elasticsearch 是一个功能丰富的开源搜索引擎构建在 **Apache Lucene 之上,**Apache Lucene 是市场上最重要的全文搜索引擎之一。

Elasticsearch 最为人所知的是它提供的丰富多样的 REST API 体验,包括用于全文搜索、排序和聚合任务的高效包装器,这使得在现有后端中实现这些功能变得更加容易,而无需复杂的重新设计。

自从 2010 年推出以来,Elasticsearch 在软件工程领域获得了很大的吸引力,到 2016 年,根据 DBMS 知识库 DB-engines ,它成为最受欢迎的企业搜索引擎软件栈,超过了行业标准 Apache Solr (也是基于 Lucene 构建的)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自 2010 年发布以来,弹性搜索的谷歌趋势数据

Elasticsearch 如此受欢迎的原因之一是它所积累的生态系统。世界各地的工程师开发了开源的 Elasticsearch 集成和扩展,其中许多项目被 Elastic(Elastic search 项目背后的公司)吸收为其堆栈的一部分。

其中一些项目是 Logstash (数据处理管道,通常用于解析基于文本的文件)和 Kibana (构建在 Elasticsearch 之上的可视化层),导致了现在广泛采用的 ELK (Elasticsearch,Logstash,Kibana)堆栈。

ELK stack 很快获得了恶名,因为它在新兴和巩固的技术领域中有一系列令人印象深刻的可能应用,如 DevOps、站点可靠性工程、以及最近的数据分析。

但是数据科学呢?

如果你是一名数据科学家,正在阅读这篇文章,并且拥有 Elasticsearch 作为你雇主的技术堆栈的一部分,你可能会在尝试使用 Elasticsearch 为数据分析甚至简单的机器学习任务提供的所有功能时遇到一些问题。

数据科学家通常不习惯将 NoSQL 数据库引擎用于日常任务,甚至不习惯依赖复杂的 REST APIs 进行分析。例如,使用 Elasticsearch 的低级 python 客户端处理大量数据也不是那么直观,对于来自与 SWE 不同领域的人来说,学习曲线有些陡峭。

尽管 Elastic 在增强 ELK 堆栈以用于分析和数据科学用例方面做出了巨大努力,但它仍然缺乏与现有数据科学生态系统(pandas、NumPy、scikit-learn、PyTorch 和其他流行库)的简单接口。

2017 年,Elastic 向数据科学领域迈出了第一步,作为对软件行业日益流行的机器学习和预测技术的回答,它为 ELK stack 发布了第一个 ML 功能的 X-pack (扩展包),在其功能中添加了异常检测和其他无人监管的 ML 任务。之后没多久,回归和分类模型(1**)**也被加入到麋鹿栈可用的 ML 任务集合中。

上周,Elasticsearch 在数据科学行业获得广泛采用的另一个步骤是发布了 Eland ,这是一个全新的 Python Elasticsearch 客户端和工具包,具有强大的(和熟悉的)类似熊猫的 API,用于分析、ETL 和机器学习。

Eland:弹性和数据

Eland 使数据科学家能够有效地使用已经很强大的 Elasticsearch 分析和 ML 功能,而不需要深入了解 Elasticsearch 及其许多复杂性。

Elasticsearch 的功能和概念被转化成一个更容易识别的环境。例如,一个包含文档、映射和字段的 Elasticsearch 索引变成了一个包含行和列的数据框架,就像我们在使用 pandas 时看到的那样。

# Importing Eland and low-level Elasticsearch clients for comparison
import eland as ed
from eland.conftest import *
from elasticsearch import Elasticsearch
from elasticsearch_dsl import Search, Q

# Import pandas and numpy for data wrangling
import pandas as pd
import numpy as np

# For pretty-printing
import json

典型的数据科学用例,例如将整个弹性搜索索引读入 pandas 数据框架进行探索性数据分析或训练 ML 模型,通常需要一些效率不高的快捷方式。

# name of the index we want to query
index_name = 'kibana_sample_data_ecommerce' 

# instantiating client connect to localhost by default
es = Elasticsearch()

# defining the search statement to get all records in an index
search = Search(using=es, index=index_name).query("match_all") 

# retrieving the documents from the search
documents = [hit.to_dict() for hit in search.scan()] 

# converting the list of hit dictionaries into a pandas dataframe:
df_ecommerce = pd.DataFrame.from_records(documents)# visualizing the dataframe with the results:
df_ecommerce.head()['geoip']0    {'country_iso_code': 'EG', 'location': {'lon':...
1    {'country_iso_code': 'AE', 'location': {'lon':...
2    {'country_iso_code': 'US', 'location': {'lon':...
3    {'country_iso_code': 'GB', 'location': {'lon':...
4    {'country_iso_code': 'EG', 'location': {'lon':...
Name: geoip, dtype: object# retrieving a summary of the columns in the dataset:
df_ecommerce.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4675 entries, 0 to 4674
Data columns (total 23 columns):
 #   Column                 Non-Null Count  Dtype  
---  ------                 --------------  -----  
 0   category               4675 non-null   object 
 1   currency               4675 non-null   object 
 2   customer_first_name    4675 non-null   object 
 3   customer_full_name     4675 non-null   object 
 4   customer_gender        4675 non-null   object 
 5   customer_id            4675 non-null   int64  
 6   customer_last_name     4675 non-null   object 
 7   customer_phone         4675 non-null   object 
 8   day_of_week            4675 non-null   object 
 9   day_of_week_i          4675 non-null   int64  
 10  email                  4675 non-null   object 
 11  manufacturer           4675 non-null   object 
 12  order_date             4675 non-null   object 
 13  order_id               4675 non-null   int64  
 14  products               4675 non-null   object 
 15  sku                    4675 non-null   object 
 16  taxful_total_price     4675 non-null   float64
 17  taxless_total_price    4675 non-null   float64
 18  total_quantity         4675 non-null   int64  
 19  total_unique_products  4675 non-null   int64  
 20  type                   4675 non-null   object 
 21  user                   4675 non-null   object 
 22  geoip                  4675 non-null   object 
dtypes: float64(2), int64(5), object(16)
memory usage: 840.2+ KB# getting descriptive statistics from the dataframe
df_ecommerce.describe()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面描述的过程将引导我们以字典列表的形式获取索引中的所有文档,然后将它们加载到 pandas 数据框架中。这意味着在进程中的某个时刻,在内存中同时拥有文档本身和结果数据帧**(2)**。对于大数据应用程序来说,这一过程并不总是可行的,在 Jupyter 笔记本环境中探索数据集可能会变得非常复杂和快速。

Eland 使我们能够执行与上面描述的操作非常相似的操作,而没有任何涉及使它们适应 Elasticsearch 上下文的摩擦,同时仍然使用 elastic search 聚合速度和搜索功能。

# loading the data from the Sample Ecommerce data from Kibana into Eland dataframe:
ed_ecommerce = ed.read_es('localhost', index_name)# visualizing the results:
ed_ecommerce.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作为一个额外的特性,pandas 方面需要更多的争论,字段geoip(它是索引中的一个嵌套 JSON 对象)被无缝地解析成我们的数据帧中的列。我们可以通过调用 Eland dataframe 上的.info()方法看到这一点。

# retrieving a summary of the columns in the dataframe:
ed_ecommerce.info()<class 'eland.dataframe.DataFrame'>
Index: 4675 entries, jyzpQ3MBG9Z35ZT1wBWt to 0SzpQ3MBG9Z35ZT1yyej
Data columns (total 45 columns):
 #   Column                         Non-Null Count  Dtype         
---  ------                         --------------  -----         
 0   category                       4675 non-null   object        
 1   currency                       4675 non-null   object        
 2   customer_birth_date            0 non-null      datetime64[ns]
 3   customer_first_name            4675 non-null   object        
 4   customer_full_name             4675 non-null   object        
 5   customer_gender                4675 non-null   object        
 6   customer_id                    4675 non-null   object        
 7   customer_last_name             4675 non-null   object        
 8   customer_phone                 4675 non-null   object        
 9   day_of_week                    4675 non-null   object        
 10  day_of_week_i                  4675 non-null   int64         
 11  email                          4675 non-null   object        
 12  geoip.city_name                4094 non-null   object        
 13  geoip.continent_name           4675 non-null   object        
 14  geoip.country_iso_code         4675 non-null   object        
 15  geoip.location                 4675 non-null   object        
 16  geoip.region_name              3924 non-null   object        
 17  manufacturer                   4675 non-null   object        
 18  order_date                     4675 non-null   datetime64[ns]
 19  order_id                       4675 non-null   object        
 20  products._id                   4675 non-null   object        
 21  products.base_price            4675 non-null   float64       
 22  products.base_unit_price       4675 non-null   float64       
 23  products.category              4675 non-null   object        
 24  products.created_on            4675 non-null   datetime64[ns]
 25  products.discount_amount       4675 non-null   float64       
 26  products.discount_percentage   4675 non-null   float64       
 27  products.manufacturer          4675 non-null   object        
 28  products.min_price             4675 non-null   float64       
 29  products.price                 4675 non-null   float64       
 30  products.product_id            4675 non-null   int64         
 31  products.product_name          4675 non-null   object        
 32  products.quantity              4675 non-null   int64         
 33  products.sku                   4675 non-null   object        
 34  products.tax_amount            4675 non-null   float64       
 35  products.taxful_price          4675 non-null   float64       
 36  products.taxless_price         4675 non-null   float64       
 37  products.unit_discount_amount  4675 non-null   float64       
 38  sku                            4675 non-null   object        
 39  taxful_total_price             4675 non-null   float64       
 40  taxless_total_price            4675 non-null   float64       
 41  total_quantity                 4675 non-null   int64         
 42  total_unique_products          4675 non-null   int64         
 43  type                           4675 non-null   object        
 44  user                           4675 non-null   object        
dtypes: datetime64[ns](3), float64(12), int64(5), object(25)
memory usage: 96.0 bytes# calculating descriptive statistics from the Eland dataframe:
ed_ecommerce.describe()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们还可以注意到,内存使用从 pandas 数据帧中的大约 840 Kbs 变成了 Eland 数据帧中的仅 96 字节。我们不需要将整个数据集保存在内存中来从索引中检索我们需要的信息。大部分工作负载留在弹性搜索集群(3**)**中作为聚合或特定查询。

对于如此小的数据集,这并不重要。尽管如此,当我们扩展到千兆字节的数据时,不把所有东西都保存在内存中进行简单计算和分析的好处更加明显。

数据框架的弹性搜索功能

Eland 抽象了 Elasticsearch 中许多已经存在的 API,数据科学家不需要学习 Elasticsearch 的特定语法。例如,可以获得索引的映射(相当于检索熊猫数据帧的dtypes属性)。尽管如此,目前还不清楚该如何做。使用 Eland DataFrame 对象,我们可以像在常规 pandas DataFrame 上一样检索dtypes属性。

# getting the dtypes from pandas dataframe:
df_ecommerce.dtypescategory                 object
currency                 object
customer_first_name      object
customer_full_name       object
customer_gender          object
                          ...  
total_quantity            int64
total_unique_products     int64
type                     object
user                     object
geoip                    object
Length: 23, dtype: object# retrieving the Data types for the index normally would require us to perform the following Elasticsearch query:
mapping = es.indices.get_mapping(index_name) 

# which by itself is an abstraction of the GET request for mapping retrieval
print(json.dumps(mapping, indent=2, sort_keys=True)){
  "kibana_sample_data_ecommerce": {
    "mappings": {
      "properties": {
        "category": {
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          },
          "type": "text"
        },
        "currency": {
          "type": "keyword"
        },
        "customer_birth_date": {
          "type": "date"
        },
        "customer_first_name": {
          "fields": {
            "keyword": {
              "ignore_above": 256,
              "type": "keyword"
            }
          },
          "type": "text"
        },
        "customer_full_name": {
          "fields": {
            "keyword": {
              "ignore_above": 256,
              "type": "keyword"
            }
          },
          "type": "text"
        }
...# Eland abstracts this procedure into the same pandas api:
ed_ecommerce.dtypescategory                         object
currency                         object
customer_birth_date      datetime64[ns]
customer_first_name              object
customer_full_name               object
                              ...      
taxless_total_price             float64
total_quantity                    int64
total_unique_products             int64
type                             object
user                             object
Length: 45, dtype: object

有了这些抽象,Eland 允许我们使用核心的 Elasticsearch 特性,这些特性不是 pandas 的一部分(或者至少不是那么高效),比如全文搜索,Elasticsearch 最突出的用例。

# defining the full-text query we need: Retrieving records for either Elitelligence or Primemaster manufacturer
query = {
        "query_string" : {
            "fields" : ["manufacturer"],
            "query" : "Elitelligence OR Primemaster"
        }
    }# using full-text search capabilities with Eland:
text_search_df = ed_ecommerce.es_query(query)

# visualizing price of products for each manufacturer using pandas column syntax:
text_search_df[['manufacturer','products.price']]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

更多集成的可能性

本文只是触及了 Eland 为数据科学家和其他数据专业人员在日常操作中使用 Elasticsearch 所带来的可能性的表面。

特别是在 DevOps 和 AIOps 环境中,基于 ML 的工具还不是很成熟,数据专业人员可以从 Python 现有的机器学习生态系统中受益,以分析大量的可观测性和度量数据,这将是另一篇文章的主题。

Eland 无疑是向 Elasticsearch 迈出的一大步,我期待 ELK stack 的未来版本会带来什么。

如果你喜欢这篇文章

查看本次网络研讨会,其中塞斯·迈克尔·拉森(Eland 的主要贡献者之一)介绍了 Eland 的主要功能。

如果你想看到更多关于弹性搜索、数据科学、信息检索、可观察性背景下的 NLP 的内容,请随时在 LinkedIn 上 与我联系阅读我关于这些主题的其他文章

脚注:

  1. 截至 ELK stack 7.8 版本,回归和分类机器学习作业仍处于试验阶段。
  2. 对于这些任务,可以使用更熟悉的类似 SQL 的查询界面,比如 Elasticsearch 的 JDBC 驱动程序。然而,这仍然需要对弹性搜索概念(例如,索引模式和分页)有一定的了解。
  3. 这类似于其他分布式计算模块,如 Dask。Eland 的 case 本质上在内部维护了一个查询构建器和任务图,只有在请求数据时才运行这些延迟的任务。

参考资料:

基于 Avro 模式的类固醇弹性研究

原文:https://towardsdatascience.com/elasticsearch-on-steroids-with-avro-schemas-3bfc483e3b30?source=collection_archive---------30-----------------------

如何应对大型企业设置中的接口版本爆炸

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Brandon Mowinkel 在 Unsplash 上拍摄的照片

它是关于什么的

下面的文章解释了一种使用现代数据存储和序列化技术来分离组件和停止企业的大规模数据消费者应用程序中固有的服务接口版本爆炸的方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

高层互动之旅

在一家大型金融机构负责一个大量使用的数据提供商系统时,我总是面临新服务版本的无休止的部署迭代,以及多个消费者无法在适当的时间框架内迁移到新版本的情况。随着时间的推移,这导致了为一个服务运行多个并行版本的情况。

提醒一句:这种方法是基于 NoSQL 数据存储基础设施的,我充分意识到这种数据存储可能不适合所有类型的业务应用。然而,有足够多的用例非常适合这种类型的架构。

互动之旅

我们的教程涵盖了以下场景:
我们有一个服务组件,它处理通过基于 React 的浏览器应用程序提供的用户输入,并将其数据持久存储在基于 ElasticSearch 集群的持久性存储中。

  • (3):ReactUIServiceComponent发送本地化的 JSON 数据负载(用户输入)
  • (4)ServiceComponent通过引用代码值替换本地化的文本来解本地化 JSON 数据有效负载,并准备一个 Avro 二进制消息(序列化),然后将其发送到BackendComponent
  • (5)BackendComponent将 Avro 二进制消息解串,转换成 Avro JSON 消息,存储在ElasticSearch Cluster

Avro 模式

Avro 消息的一个关键特征是,它通过相关的 Avro 模式进行自我描述。Avro 模式是基于 JSON 的消息结构定义。

参考下面简单的 Avro 模式。

这个例子已经概述了 Avro 模式定义语言的一些细节。

  • personidlastnamelongstring类型的强制属性
  • surname是一个联合属性,也就是说,它可以是null或者具有类型为string的值。默认情况下,其值为null

可选值总是被表示为联合,并且为了准备无缝的模式演化(稍后将详细介绍),您应该总是定义可选属性的缺省值。

Avro 模式解析器和客户端绑定生成器

Avro 序列化程序和反序列化程序使用该模式将 Avro 二进制消息(或 Avro JSON 消息,取决于您选择的配置)解析为 Java 数据访问对象(DAO)。这些 DAO 可能是通用的或预编译的强类型模式 Java 类。

也就是说,Maven 目标generate-sources生成一个类Person,它有三个属性personIdlastNamefirstname,然后可以使用 Avro 解析器从这种类型的二进制消息中实例化出 person 对象。

为了自我描述,Avro 消息可以用模式本身或模式指纹来补充。

完整模式 JSON 的供应通常是以基于文件的方式完成的,这捆绑了大量的 Avro 消息。在我们的交互之旅中——具有单个 Avro 消息的请求-响应风格——这样的方法开销太大。对于这个场景,模式指纹是正确的方法。

Avro 指纹和模式目录

Avro 模式指纹是一个全局唯一标识符,用于引用中央模式注册中心中的正确模式。

下面的抽象SchemaRegistry类描述了这种注册中心所需的方法。

  • 方法registerSchema允许发送者(发布者)注册他发送给读者(消费者)的消息的 Avro 模式。作为返回值,Avro 指纹将被返回,它唯一地标识这个模式。对模式本身的任何更改都会产生新的指纹。
  • 使用getSchemaFingerprint方法计算指纹
  • 方法getSchema将返回与fingerprint中传递的相关联的 Avro JSON

就这样,建立了一个全局模式注册中心,Avro 消息的读者或作者可以使用它来交换相关 JSON 模式的模式指纹,这样就可以充分利用 Avro 的全部功能了。

本教程提供了模式注册表的两种实现:

  • 轻量级测试的基于文件的实现,以及
  • 基于弹性搜索(ES)的实现。如果您的公司实例化了一个高效的 ES 集群,您可以通过引入一个专用的 ES 索引,轻松地将它作为公司范围的模式注册表。
  • 有现成的模式注册中心可用,特别是在 Kafka 领域,例如,融合 Kafka 产品的模式注册中心。但我们尽可能保持简单,并利用 ElasticSearch,它被用作我们的目标数据存储。

下面的例子显示了索引avroschema中指纹8354639983941950121下的 ElasticSearch 注册模式。任何消息的作者或读者都将使用 Avro 指纹来引用其处理中使用的底层模式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 ElasticSearch,您将获得一个现成的模式定义中央存储库,业务分析师、设计人员或开发人员在系统开发期间可以使用查找和查询功能轻松增强它。

如果您认为实现模式注册中心需要大量的编码,那么您就错了。

下面你可以看到功能完整的ElasticSearchSchemaRegistry java 类。

持久性管理器

非常简单紧凑。你现在可能会争辩说ESPersistencyManager类隐藏了复杂性(作为教程的一部分被使用和实现)。

不完全是,这个类是围绕 Jest 的一个超轻层,Jest 是一个用于 Elasticsearch 的 HTTP Java 客户端。虽然 Elasticsearch 提供了自己的原生 Java 客户端,但 Jest 提供了更流畅的 API 和更自然的工作界面。对 JEST 感兴趣,可以看看 Blaedung 的教程文章

我们的ESPersisencyManager只是保护我们的补习班免受直接的笑话曝光。一种封装技术曾经能够在未来版本中替代持久性管理器。

同样非常紧凑,可以持久化 Avro 模式。值得一提的是,您可以提供 ElasticSearch 自己的主键(在 ES 中称为_id)。在我们的教程中,我们使用全局唯一指纹作为 ES 中的主要标识符,这使得查找变得简单明了(见上面的截图)。提供主键是一个关键特性,尤其是在数据复制场景中使用 ElasticSearch 时,ES 只是另一个主系统的从系统,而这个主系统已经生成了主键。

可以想象,检索一个 JSON 对象甚至更简单 Jest 为我们完成了这项工作。

后端组件启动序列

我们现在应该对后端组件使用我们的方法必须准备和配置的内容有了很好的理解。

下图概述了最重要的步骤:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

后端组件的启动顺序

  • 我们创建了一个与我们的弹性搜索集群的连接
  • 10,11:我们创建(如果不存在的话)两个 ES 指数,businessmodelavroschema。第一个用于我们的域数据记录,后一个用于 Avro 模式
  • 20,30,31:我们为我们的avroschema加载并编写一个定制的 ES 映射。这需要一些解释。ES 需要一些关于其持久化 JSON 文档的模式信息(这是一个 ES 文档模式,不应该与 Avro 模式混淆)。默认情况下,ES 将通过分析提供的 JSON 文档自动分配一个 ES 映射模式。在我们的例子中,我们必须根据需要调整 ES 映射,并告诉 ES 不要解释属性avroschema中提供的 JSON 部分(通过将类型切换到object)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的索引“avroschema”的弹性搜索索引元信息

  • 40–46:我们现在从文件系统加载 Avro 消息模式,并将其注册到 ES 模式注册表中。作为回报,我们得到了 Avro 模式指纹,这是我们的通用接口契约标识符,用于提供/消费我们的消息。该模式定义了我们的一个业务域数据对象(DO),即“业务模型策略 DO”,它收集关于公司的策略数据。除了简单类型之外,该模式还引入了一个用于说明的枚举。doc属性允许提供文本解释,这在人们浏览注册表中的模式时很有帮助。

用于收集公司战略数据的 Avro 模式

服务组件启动序列

让我们看看服务组件需要准备什么。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

启动序列服务组件

  • 10–11:我们创建一个到 ES 集群的连接对象(ESPersistencyManager)
  • 20,21,22:我们初始化一个基于 ES 的模式注册中心,传递我们的连接对象
  • 30,31:我们基于指纹版本来检索模式,服务组件就是基于指纹版本构建的。

消息版本随时间演变

必须详细理解最后一步——基于“预先配置的静态”指纹解析模式。

模式指纹表示 ServiceComponent 或 BackendComponent 能够处理的消息版本号。

在我们简单的 Hello world 示例中,后端和服务组件的模式指纹是相同的。但实际上,后端组件以及一个或多个服务组件可能会随着时间的推移以不同的变化速度发展。

下图说明了这种情况。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

模式(接口)版本随时间的演变

  • 想想一个BackendComponent,它在带有模式版本(指纹)100的 Release 1 中与它的ServiceRWComponent1一起发布。然后还有两个只读组件ServiceROComponent2ServiceROComponent3,正在使用版本100的数据。
  • 三个月后,BackendComponent发布了新的版本 2,它用模式版本101支持增强的数据模型(一些额外的可选属性)。它的ServiceRWComponent1也支持这个新版本。
  • 对于两个独立的数据消费者ServiceROComponent2ServiceROComponent3——它们可能以不同的变化速度发展——情况如下:ServiceROComponent2没有自己的版本并停留在版本100上,ServiceROComponent3需要版本101的附加数据元素并发布其ServiceROComponent3的新的整体版本。

接口版本爆炸

这就是现实生活中的我们。如果你是一个BackendComponent的所有者,它提供对许多独立数据消费者(ServiceROComponent2ServiceROComponent3)来说很重要的数据集,你将面临支持多个接口版本的需求。这个版本支持需求可能会随着时间的推移而急剧增长,并影响您的交付时间、灵活性和组件成本的增加。

作为第三方组件(不在您的控制之下)的数据提供商,您可能只有有限的权力来强制它们升级到更新的发布接口版本号。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

亚历克斯·霍利奥克Unsplash 拍摄的照片

对于许多数据提供者来说,服务版本及其在组件整个生命周期中的管理起着至关重要的(有时甚至是麻烦的)作用。

运行由 WSDL 接口定义语言支持的 SOAP-Webservices 或由开放 API 规范 3(即 Swagger)支持的 JSON-Services(两种非常流行的请求-响应交互技术)将使您面临不断的版本更改,以防您增强您发布的接口。

版本变更是显式的,必须由您的使用者采用,这对于允许从您的生产组件中删除旧版本的单一协调版本升级来说可能是力所不及的。

在这里,Avro 凭借其开箱即用的模式演进支持大放异彩。

Avro 模式演变

Avro 支持模式进化,这意味着您可以同时拥有不同模式版本的 Avro 消息的生产者和消费者。这一切都继续工作(只要模式兼容)。模式演化是大型生产系统中的一个关键特性,它可以分离组件,并允许组件在不同时间通过接口变化来运行系统更新。

Avro 算法实现要求 Avro 消息的读者与消息的作者拥有相同版本的模式。那么 Avro 是如何支持模式进化的呢?如果读取器在另一个模式版本上,读取器可以向 Avro 解析器传递两个不同的模式。然后,解析器使用它的解析规则将数据从写模式翻译到读模式。

让我们用我们的BackendComponent.convertAvroBinarToJSON方法来检查一下。这是本教程的主要方法之一,它将 Avro 二进制消息转换成 Avro JSON 消息格式,之后保存在 ES 中。

backendcomponent convertAvroBinaryToJSON 方法

  • 该方法在 Avro 单对象编码的二进制消息中传递,定义如下:

单个 Avro 对象编码如下:一个双字节标记,C3 01,表示消息Avro 并使用这个单个记录格式(版本 1)。对象的模式的 8 字节小端 CRC-64- AVRO 指纹。使用 Avro 的二进制编码Avro 对象编码。(链接)

  • 我们从消息中提取模式指针
  • Line 3:我们从模式注册中心获取相关的模式
  • Line 4:我们获得消息有效载荷
  • Line 7:我们检查收到的信息的指纹是否与BackendCompontent中使用的指纹相同
  • Line 8:如果指纹相同,我们用BackendComponent模式创建一个 Avro GenericDatumReader(用于解码消息)
  • Line 10:如果没有,我们用BackendComponent模式和检索消息模式创建一个 Avro GenericDatumReader。读者现在将在解码二进制消息期间应用模式演化逻辑。
  • Line 12:我们还创建了一个 Avro GenericDatumWriter来产生消息的 JSON 表示。如前所述,Avro 支持紧凑的二进制编码,以及基于 JSON 的编码。你可以用一些简单的 Avro 助手类从一种编码转换到另一种编码。
  • Line 14:我们为二进制文件payload创建一个binaryDecoder,以及
  • Line 18: a jsonEncoder基于我们的schema
  • Line 19-23:最后,我们对二进制消息进行解码,并将其编码成 JSON 消息格式,这样就可以持久化了。

方法BackendComponent.persist负责这项任务,这很简单。

  • 我们传入我们对象的弹性搜索index(“业务模型”)、弹性搜索type(“策略”),以及唯一的id

后端组件持久方法

在现实场景中,persist 方法将由BackendComponent作为 JSON-、Webservice 或任何其他远程接口公开。

我们持久化的 Avro JSON 文档被存储为_source属性的一部分。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

想想我们现在取得的成就。

  • 我们的BackendComponent现在可以服务于 JSON 文档结构,它完全符合 Avro 消息规范。根据ServiceComponent接收到的二进制消息,到 JSON 持久性格式的转换完全自动进行。
  • JSON _source文档结构(有效载荷)是完全自描述的。我们注入了主键index_ipid,以及模式指纹avro_fingerpint作为有效负载的一部分
  • 这意味着 JSON 消息文档的消费者可以通过 ES 解析模式来处理消息,并且知道文档在 ES 中的惟一存储标识符。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 我们还注入了可能相关的附加公共属性,即last_update_timestamplast_update_loginid,以保存更新文档的时间和人员的信息。这将允许我们通过引入例如基于 Kafka 的事件接收器(将是另一篇文章的主题)来建立时间序列和审计追踪

图式进化再探

数据管理的一个重要方面是模式演变。定义了初始模式后,应用程序可能需要发展,如上图所示。当这种情况发生时,下游消费者必须能够无缝地处理用新旧模式编码的数据。

我们区分两种兼容性类型:

  • 向后兼容意味着使用较新模式的用户可以读取基于较旧模式的数据
  • 向前兼容意味着使用旧模式的用户可以根据新模式读取数据

如果模式演化是向后兼容的,以及向前兼容的,则取决于改变是否符合 Avro 模式解析约束

以下是分辨率规格中的三个基本规则:

  • 可以对字段进行重新排序,而不会对读取器或写入器产生任何影响
  • 向记录中添加一个字段是可以的,前提是您还要一个默认值。默认值的提供允许具有新模式的读取器从基于旧模式的写入器读取消息。
  • 只要您最初给了一个默认定义,从记录中删除一个字段是可以的。即旧模式上的读取器接收默认值。

这意味着在数据消息中添加或删除可选字段会保持消息完全兼容,只要已经或将要为删除或添加的属性定义默认值。所以当您开始定义您的模式时,您应该从一开始就考虑到这一点。

在数据消息中添加或删除可选的数据属性是一种场景,这在生产系统中经常发生,生产系统不断发展以满足新的业务需求。

解释了增强的版本控制策略

让我们通过一个简单的例子来看看 Avro 将如何帮助我们减少版本复杂性和组件耦合,通过比较一个经典的应用程序设置,以及我们使用 ElasticSearch 和 Avro 的增强设置。

经典应用

假设在生产环境中有以下组件设置。

  • 我们有一个应用程序,它覆盖了一个特定的有界上下文(例如,一个 CRM client profiling 应用程序),由一个 React 维护 UI ReactMaintainUI组成,该 UI 由一个呈现服务组件ServiceComponent支持,该组件为 UI 提供一个 JSON 本地化的有效负载接口。我们假设它是一个招摇风格的 OpenAIP 接口。它将连接到一个后端组件BackendComponent,该组件提供了一个 web 服务接口,并将其数据保存在关系数据库中RelaionalDB
  • 应用程序本身不仅为其 UI 提供 API,还通过其 JSON API 集成了第三方 UI JSONConsumer1。以及一个第三方数据消费者,它通过 Webservice 接口读取数据BackendComponent

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

版本示例 Classic 1

  • 当应用程序最初发布时,以下接口和数据库模式版本被冻结,供消费者使用。JSON_V100WSDL_V100以及DBSchema_V100。所有这些工件都代表了消费应用程序的约定契约,无论是在 BoundedContext 中管理的应用程序(通常在您的控制之下),还是第三方组件。
  • 现在假设我们的有界上下文应用程序在短时间内有多个发布周期,引入了新的特性和可选的数据属性。这是敏捷设置中的一个典型例子,您可以从一个最小可行产品(MVP)开始,并在一个短的发布周期内增强应用程序。
  • 在我们的例子中,我们发布了一个新版本V101,它通过ReactMaintainUI请求额外的可选数据属性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

版本示例经典 2

  • 我们为有界上下文应用程序添加了一个新的接口和模式版本:JSON_V101WSDL_V101DBSchema_V101。这导致红色成分的变化。
  • 我们现有的第三方消费者JSONConsumer1WSDLConsumer1(绿色的),对当前的接口很满意(不需要新数据),并且没有任何发布计划,仍然停留在初始版本JSON_V100WSDL_V100
  • 你现在在这里。作为有界上下文应用程序的所有者,您现在必须开始管理您的接口版本,并向您的数据消费者提供潜在的多个版本。
  • 尤其是如果多个其他应用程序需要您的数据(即 CRM 数据),这种需求将随着应用程序的变化而快速增长。这可能导致同一服务的多个版本,相信我,迫使第三方消费者迁移到新版本是一项艰巨的任务。
  • 迁移到新的接口版本意味着代码更改(至少重新编译您的客户端绑定)、测试和组件发布。从消费者的角度来看,他们试图尽可能避免这种情况

新一代应用

现在让我们用新的有界上下文应用程序来检查这个场景。在它的BackendComponent中使用了支持模式进化的 Avro 消息接口,以及 NoSQL 数据存储。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

新一代应用程序版本示例

  • 我们仍然有一个显式的接口契约JSON_V100,它作为一个基于 REST 的接口暴露给 UI。
  • BackendComponent为消费者提供了一个简单的 JSON 接口,以二进制编码的单对象格式传递 Avro 消息。
  • 一个非常简单的 JSON 接口将允许我们传入一个 Base64 编码的字符串,它代表我们的二进制单对象编码的 Avro 消息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Avro 消息的简单 JSON 接口(输入)

作为回报,服务将返回 Avro 对象的唯一标识符,以及BackendComponent作者的指纹。这将允许接口消费者检测他是否在同一个版本上工作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Avro 消息的简单 JSON 接口(输出)

  • 考虑到本教程已经足够了,我们将在后续文章中研究这种接口的细节。
  • 正如您在上面的图表中所看到的,BackendComponent接口以及EsIndex1模式没有被明确地版本化。只要我们的接口增强在 Avro 模式演进规则的约束范围内,版本控制将被透明地管理。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的后端组件不需要显式版本控制

  • 现在添加额外的可选属性只会导致ReactMaintainUI组件和相应的ServiceComponent组件的变化,这提供了一个更新的符合 OpenAPI 的 JSON 接口。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

版本示例新一代 2

  • Out BackendComponent和 persistence schema Avro_JSON_Store可能不受影响(或者最低限度,我们必须在 ES 上运行一个重建索引,以防我们需要一些特定的模式设置)。
  • 我们最终可以将我们的BackendComponent从演进的ServiceComponent中分离出来,减少开发和测试的工作量,以及我们应用程序的整体复杂性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由张秀坤·贝德纳尔兹Unsplash 上拍摄

这最终会导致更快的交付周期,以及在组件中引入新特性的更少的变更成本。

把它包起来

所以你该去看看教程了,可以在 Github 上找到:https://github.com/talfco/tutorial-apache-avro(一个基于 Maven 的教程)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • HelloWorld类开始,它建立并模拟了ServiceComponentBackendComponent之间的交互流。
  • 从那里,您可以深入到轻量级支持类,这为您提供了一个整体方法的良好概览。
  • 唯一的先决条件是存在一个弹性搜索测试集群。简单来说就是在 https://www.elastic.co/网站上创建一个 14 天的测试账户,这个账户能满足你的所有需求(还能让你体验 ElasticSearch 的一些漂亮功能)
  • HelloWorld程序排除了 ElasticSearch 实例端点,以及用户和密码(可以在 ElasticSearch 管理 GUI 中创建)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

必需的“Hello World”程序参数

玩得开心!

候选资格& 2020 年的参议院

原文:https://towardsdatascience.com/electability-the-senate-in-2020-a69622a04881?source=collection_archive---------31-----------------------

模拟民主党竞争者之间的差异

目前,有 12 个主要竞争者争夺民主党的总统候选人。压在选民心头的一个重要问题是:那些候选人在大选中能有多出色?

第二个同样重要的问题:*参议院的组成会发生什么变化?*这可能是民主党总统政策变化的主要限制因素,而且无论哪个政党控制总统职位,这也可能对司法部门的权力平衡产生重大影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个图表,显示每个候选人的候选资格的证据平衡(正面是好的,负面是坏的),以及根据可用信息量计算的证据强度。

我将尝试用一个简单的数据驱动的开源模型来回答这些问题,使用民调数据(来自 FiveThirtyEight 的民调跟踪)和过去的选举数据(参议院和总统数据来自莱普的图集);来自维基百科、Ballotpedia 和各种州网站的同州内比较数据)。

即使对民主党赢得 2020 年总统大选的机会持乐观态度的人,也有理由对民主党获得参议院多数席位的机会持悲观态度。民主党人要么需要一个有巨大影响力的总统候选人,要么需要一个比他们的总统候选人表现更好的参议院候选人。

建立选举的基线模型

模拟选举的第一步是建立一个基线估计,粗略估计 2020 年选举在一个通用的民主党候选人的情况下会是什么样子。为了做到这一点,我将把以前选举的数据放在一起,创建一组合理的基线模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

六个基线模型,总结了这些模型对民众投票百分比和选举人投票总数的预测。“最民主党”和“最共和党”的模型是基于从 1992 年到现在最强的总统差额。

模拟 2020 年选举的第一个也是最明显的方法是假设它将与 2016 年选举一样。加权平均模型根据近期(每四年选举权重减半)和票数(平方根)对过去的选举进行加权。因此,投票率较高的选举权重更大,该模型给出的预测与 2016 年相似。

趋势模型基于五次选举(1996 年至 2016 年),使用线性回归对每个州的前六次选举进行推断。2018 年波浪模型将 2018 年众议院选举添加到趋势模型中。当我们从左到右,模型从更多的共和党转向更多的民主党。

普遍接受的观点是,长期趋势有利于民主党;另一方面,美国历史充满了周期性和反周期性的潮起潮落。

针对候选人之间的差异调整模型

下一步是为每个候选人调整模型*。基线模型被视为对民主党候选人表现的估计。*

为了进行这种调整,我使用了同一州、同一年或同一民意调查中候选人和典型(中间)民主党人之间的差异。每一个都被认为是对该候选人和普通民主党候选人之间差异的估计。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个州的候选人的预测投票份额的基本控制方程。这是基于选举中的投票份额、同一投票或选举中的中值投票份额,并基于投票或选举的规模、2020 年选举前的时间以及地理相关性(s_i)进行加权。

然后,使用投票的大小及其日期对这些投票进行加权,经过加权平均模型中用于过去选举的相同的精确加权函数。然后,基线模型以固定的权重重新加入,如果缺乏证据,这使得预测稍微保守一些。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

已处理数据的快照。“奖金”是指贝内特增加的民主党份额,而“惩罚”是指贝内特的对手增加的共和党份额。

因此,例如,2010 年麦克·班尼在科罗拉多州获得了 48.1%的选票,而普通民主党人获得了 44.7%的选票,这一事实让我们估计,与普通民主党人相比,麦克·班尼将获得额外的 3.2%的选票。这些信息与所有其他可用信息相结合——在这种情况下,是 2016 年的另一次选举和最近的三次全国民调——以产生对麦克·班尼和普通民主党人之间差异的特定州估计。

为了模拟科罗拉多州的总统选举,Bennet 以前在科罗拉多州的选举和民意调查都被计算在内,以及我们在科罗拉多州的基线模型。全国民意测验的权重被打了 0.25 的折扣。对于邻近的州,如新墨西哥州,该模型将全国民调和科罗拉多州选举的权重都乘以 0.25。对于更远的州,如佛罗里达州,科罗拉多州的选举乘以 0.05 的权重。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在麦克·班尼的例子中,我们仅有的一点信息是积极的,因此模型预测他将比一般的民主党人获得更多。

包括麦克·班尼在内的许多候选人在他们的家乡州比典型的民主党人做得更好,因此在模型中获得了家乡州的优势——这符合传统智慧(例如,见本文)。

候选人

一般来说,我们可以根据信息的强度将候选人分成三组。

拥有许多民意测验的候选人

首先,我们有一些候选人已经在许多面对面的投票中表现突出:乔·拜登、伯尼·桑德斯、伊丽莎白·沃伦和皮特·布蒂吉格。根据上述方法,对这些候选人来说,投票数据比他们过去的选举表现起着更大的作用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

与同一民调中的民主党中值相比,这些群体的紧张程度相当明显。Buttigieg 和 Biden 之间的加权平均差异在 5 个点的数量级。民意测验用具有较高权重的较暗阴影绘制;权重依次基于样本大小和新近性。

拜登和桑德斯的民意测验比沃伦和布蒂吉格好。这种差异随着时间的推移一直非常一致——远远超过民主党候选人和特朗普之间的差距,这种差距随着时间的推移和不同众议院效应的民意调查而显著变化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

顶行中的上一个选举模型;最下面一行是加权平均模型。

由于拜登和桑德斯在过去的选举中也比典型的民主党人做得更好,而沃伦和 Buttigieg 做得更差,该模型毫不含糊地预测拜登和桑德斯会比沃伦和 Buttigieg 做得更好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

顶行中的趋势模型;底排 2018 款 wave 车型。

任何民主党候选人都可能赢得或输掉选举,值得注意的是,即使是我们最好的估计也有很多不确定性,特别是在这种情况下。可能这些候选人中的一些人有隐藏的弱点或隐藏的优势,而这些还没有在投票中显现出来。选举团是混乱的,这里和那里的几个点可以产生非常大的差异。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可能这些候选人中的一些人有隐藏的弱点或隐藏的优势,而这些还没有在投票中显现出来。同样值得注意的是,在皮特·布蒂吉格表现的模型中,大量未承诺的投票者与较低的知名度成对出现。相应地,我们应该减少对模型根据民意测验预测其表现的能力的信任:许多选民尚未形成对 Buttigieg 的看法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这种差异在趋势模型中最为显著。

然而,虽然名字认知度在扭曲民调数字方面发挥了作用,但名字认知度类似或更低的其他候选人在面对面的民调中比皮特·布蒂吉格获得了更好的民调——最明显的是贝托·奥罗克(Beto O’Rourke),尽管全国的名字认知度类似,但他的面对面民调显然更好。

竞选全州公职的候选人

在 12 名主要候选人中,有 7 人过去竞选过全州的公职,包括在许多激烈的民意调查中表现突出的四名领先者。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

民主党候选人在过去选举中的表现。更久远以前的选举逐渐变得透明。

第二,我们有一些候选人没有在很多全国性的民意调查中出现过,但他们竞选过全州的公职:艾米·克洛布查尔、麦克·班尼和德瓦尔·帕特里克。对于这些候选人,我的模型主要依靠他们家乡州过去的选举表现来推断全国的表现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如我们在上面的图表中看到的,这产生了对麦克·班尼有利的结果,对 Amy Klobuchar 非常有利的结果,对 Deval Patrick 不利的结果。Klobuchar 在明尼苏达州选举中的表现确实非常出色,大大超过了与她一起竞选的所有其他民主党人。此外,明尼苏达州靠近几个战场州——爱荷华州、威斯康星州和密歇根州。

其他候选人

还有另外五位著名的候选人,他们以前从未竞选过州级职位,也没有在很多面对面的民意调查中出现过。这包括杨安泽,塔尔西·加巴德,汤姆·斯泰尔,迈克尔·彭博,约翰·德莱尼。在这种情况下,我们的模型没有说太多——没有足够的信息。更糟糕的是,我们仅有的一点点信息可能会受到低知名度的严重影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在同一次民意调查中,与典型的民主党人相比的加权平均民意调查,结合了支持和反对票数的变化,根据进行的民意调查的数量绘制。前候选人包括在内,以显示一个更完整的画面。

从历史上看,提名一个从未竞选过州级职位的候选人是不寻常的。我个人认为,杨安泽和塔尔西·加巴德最有能力吸引那些通常不会投票给民主党人的人。由于她所吸引的负面媒体和她在党内地位显赫的敌人,加巴德可能很难保住民主党的选票。

添加统计噪声以增强鲁棒性

现在,我们所看到的是一个非常大而复杂的可能结果空间中的一个非常稀疏的样本。这些结果有多可靠?也就是说,在对单个模型进行小改动的情况下,它们的表现如何?选举团是一个混乱的系统,一小部分选票的变化可以产生惊人的大变化。

我们可以进行多次模拟,对数据进行随机调整。如果这些调整将邻近的州联系起来,我们就能捕捉到这些地区经常一起移动的方式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

选举人投票结果的小提琴图,每个候选人在六个基线模型中各有 100 个模拟。基准选举人票总数包括 270 张(获胜)以及麦凯恩、罗姆尼、希拉里、川普和奥巴马最近的选举表现。前候选人贝托·奥罗克、卡玛拉·哈里斯和科里·布克都包括在此图中,以创建一个更广泛的比较基础。

看待这个问题的一个方法是观察每个候选人的结果分布。艾米·克洛布查尔被评为更强的事实并不是基线选择的一个怪癖;这是因为该模型包括了过去的选举记录,而 Klobuchar 有着非同寻常的选举历史。

事实上,当我们在模拟中加入噪音时,我们在六个特定模型中观察到的差异并没有消失。可能的全国总支持度只有几个百分点的差异,就可能导致选举团选举结果的巨大差异。

参议院

总统任期内的参议院选举受到总统提携的强烈影响。民主党总统候选人将对参议院的构成产生影响;从逻辑上讲,一个更强大的候选人将有助于赢得更多的参议院席位。

从基线模型中选择。

有 35 个席位等待选举,包括乔治亚州的两个席位;大多数席位的上一次选举是在 2014 年。共和党拥有其中的 23 个席位。原则上,这意味着共和党是脆弱的;然而,大多数共和党现任者强烈支持连任。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作为一个群体,四个中间基线模型(共和党赢得 51 至 55 个席位范围内的大多数席位)符合由拉里·萨巴托政治库克政治报告和大多数其他专家预测者提供的专家预测。然而,只有 2018 年波浪模型避免了任何不寻常的基线预测;西弗吉尼亚、阿拉巴马和阿拉斯加对民主党来说都是困难的州。

值得注意的是,这个模型也是关于总统选举最乐观的模型之一:2018 年 wave 模型预测,即使是最弱的民主党总统候选人也可能赢得总统竞选。

2018 年波浪模型的预测

即使民主党人在与 2018 年中期选举类似的选举中赢得总统大选——这一点绝不是确定的——他们也很难在参议院赢得哪怕是微弱的多数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有一个重要的通配符值得注意:伊丽莎白·沃伦和伯尼·桑德斯代表着共和党州长的州——这意味着如果他们离开参议院,他们可能会被共和党任命的人取代,至少暂时是这样。这两个州都有可能在特别选举中选出民主党的替代者,但这种特别选举特别难以提前预测(参见马萨诸塞州 2010 年,阿拉巴马州 2017 年);为了报告选举结果,我把这算成了两大政党各占一半的席位。

这些预测非常可信——尽管投票总数有显著差异,但就赢得的席位而言,所有候选人都非常相似。为了赢得参议院多数席位,民主党需要超越近期历史建立的预期,特别是在南方。

给参议院模式增加噪音

有了合理基线模型的明确选择,很容易给参议院模型添加噪声——这最终会在候选人之间产生更明显的差异。大多数候选人的中值结果略高于噪声前的基线预测,这是一个值得解释的事实。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们注意到的差异都没有消失;然而,加入噪音会让新的差异出现。

在 2018 年的基线模型中,预计大多数战场州将以微弱优势再次选举共和党参议员。给模型增加噪音表明民主党很可能在某个地方获得额外的席位——也许是蒙大拿州,也许是缅因州,也许是北卡罗来纳州。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在系统中具有数据的所有候选者上的平均噪声添加预测。

值得重复的是,总体而言,2018 年的 wave 模型对民主党人来说是一个相当乐观的模型。这一模型表明,民主党有机会在德克萨斯州等州赢得参议院席位,在德克萨斯州,几十年来没有民主党人赢得过全州选举。

这不是唯一的——也不是最复杂的——可能的选举结果模型;但是它强调了在整个国家投票中仅仅几个百分点的差异的巨大影响。

结束语

我们试图比较民主党竞选者赢得选票的能力。选举人团和参议院都是由州一级的赢家通吃选举决定的,因此全国范围内几个百分点的微小变动都会产生巨大影响。

领先者之间有一个清晰的模式。乔·拜登和伯尼·桑德斯在他们家乡的选举中表现得比典型的民主党人更好,在面对面的民意调查中也比典型的民主党人表现得更好。伊丽莎白·沃伦和皮特·布蒂吉格在过去的全州选举和民调中表现更差。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

强度被估计为投票者或投票应答者数量的时间衰减平方根之和。证据平衡是一个加权和。前候选人包括在内,以提供一个更完整的画面。

对于其他候选人,我们掌握的信息更少。Amy Klobuchar 在明尼苏达州的全州选举中一贯表现强劲,确实非常出色,但这与全国民调结果并不相符。麦克·班尼当选的证据是积极的,尽管有限。

对于那些以前没有竞选过州级职位,也没有在许多全国性民调中出现过的候选人,比如杨安泽,我们只是还没有太多好的信息。

选举审计:发现重新计票中的错误

原文:https://towardsdatascience.com/election-audit-finding-errors-in-recounts-3886685a9948?source=collection_archive---------38-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者插图

选举是我们民主的基石,对我们社会的发展具有深远的影响。这都是关于数据的。这是一个故事,讲述了二十年前我们如何在丹麦解决了一个关键的数据质量问题,我们的解决方案直到今天仍在使用。

我们认为我们应该使用复杂的分析,但最终归结为正确指标的设计。

问题是

在丹麦,选举用纸质选票卡进行。投票站从早上 8 点到晚上 8 点开放。投票结束后立即开始计票。在选举当晚,随着计票的进行,每个投票站都会报告每个政党的票数,从而可以计算出每个政党获得的授权数,并由此生成选举产生的政治格局的图像。第二天进行重新计票,确定候选人的人数,从而确定哪些候选人获得了哪些授权。

显然,将一个政党的候选人的候选人票相加,应该会给出该政党在选举当晚的数字,从而提供对计票的检查。然而,负责选举的内政部怀疑存在未被发现的错误。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者插图

独立政党计票员很容易发现选票卡从一个政党的候选人转移到另一个政党的候选人(图中的 1)。但如果候选人来自同一个政党(图中为 2),则情况并非如此。计票时,选票卡以 25 张为一捆,4 捆在一起,100 张为一捆。束可以在党内的候选人之间移动(图中的 2)。当你投票时,你可以为一个政党或候选人投票。政党的选票可能会转移给政党的候选人(图中的 3),反之亦然。空白选票也可能被篡改,变成候选人(图中的 4)或政党的选票。

我当时在一家小型咨询公司 BusinessMinds(已经不存在)工作,我们的任务是开发一种方法来检测这种错误。首先,我们认为我们应该引入来自分析或数据挖掘的奇特方法,这在当时是一个大肆宣传的术语。我们手头有前几次选举的选举数据,但每次选举都有很大的变化。在原子层面上,即每个投票站的每个候选人,根本没有可识别的趋势。因此,只能在一次选举中进行分析。

方法

不仅投票会随着时间的推移而改变,而且在地理上也会有所不同:一些政党在城市中的地位更高,而其他政党则在农村地区。通过研究这些数据,我意识到,在缩小投票区范围时,一个相当稳健的模式将是总体指标变化的单调性。为了澄清这一点,我们需要看看丹麦的选举制度。

丹麦选举按地理等级组织(括号中的描述参见下图):

—国家
— — 3 个选举省(蓝线和文字)
——10 个多议员选区(红线和文字)
——92 个提名区(绿色区域,黑色文字)
———1384 个投票区(未描绘)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自丹麦的议会选举制度(一个选区没有画出来:博恩霍尔姆岛)

党组织在提名区一级提名候选人。地方组织将在他们的提名区宣传他们自己的候选人,但候选人可以在整个选区,即在许多提名区当选。

为了应对政党受欢迎程度的地区差异,候选人票数与政党票数的比率按不同的地理级别计算:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里的政党选票指的是投给该党的所有选票,或者投给该党的个别候选人,或者投给该党。这个比例叫做候选比例。如果一个候选人在整个选区中同样受欢迎(或不受欢迎),那么这个比例在各个地理级别上是不变的。

与此同时,候选人在党内的突出地位与该党在地区的受欢迎程度无关:在该党获得许多选票的地区,20%的政党选票与在该党获得很少选票的地区一样具有代表性。

然而,候选人的受欢迎程度通常因地域而异。通常候选人住在提名区,离家近的人比离家远的人更出名。因此,在投票区一级观察候选人比例,通常会发现候选人在当地投票站的比例最高。为了衡量对候选人的额外关注,候选人的比例在不同的地区进行了比较:投票地区的候选人比例除以提名地区的候选人比例。

如果候选人在各个地区的受欢迎程度相同,那么这个比率在任何地方都是 1。因此,偏离 1 表示候选人受欢迎程度的地理变化。为了获得更对称的测量,应用对数。在这里,这个度量被称为亲和力:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

亲和力与 0 的偏差是候选人以及政党的受欢迎程度的局部变化的度量。可以在分母中使用选区/地区候选人比率,这将产生更清晰的候选人衡量标准。但这里的目的是确定来自同一政党的候选人之间,即同一选区的候选人之间可能的选票转移。

显然,亲和力将分布在 0 附近。这种模式相当稳健,在候选人的家乡投票区有一个峰值,从那里开始下降。这是最近一次大选中所有候选人在丹麦的分布情况(分成 50 个箱):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者插图

可以看出,分布被避开,这是一个边界问题。许多候选人获得的选票相对较少,因此负面亲和力的变化有限。

人们发现,这种避开的钟的尾部末端是误差的良好指示器。钟的反面更敏感。如果在一个投票区的一个政党中的两个候选人之间存在投票偏移,则可以识别出两个相应的异常值:一个是一个候选人的钟形负侧,一个是另一个候选人的钟形正侧。

我们查看了电子表格中的简单描述,并创建了一个列表,列出了所有投票区一级的投票,按亲缘关系的平方降序排列。然后,我们从顶部开始搜索同一投票区同一政党的记录对。就这么简单。

调查的结果

这个方法证明是成功的。我们参与了两次大选和一次欧洲议会选举。从那以后,整个地区都被内政部外包出去了,我们再也不参与了。

在每次选举中,我们都会发现潜在的错误,然后教育部会评估这些错误,以决定是否应该再次重新计票。如果是这样的话,当地的市长将被联系并被告知重新计票。在所有选举中,这种重新计票导致数百张选票的数量级调整。

尽管我们不再是这一过程的一部分,但这一方法成为了丹麦选举的标准工具。当我们开始时,点票的法律规定是政党选票应在投票区一级进行,而候选人选票应在选区一级进行。实际上,候选人的投票是在投票区一级报告的,这使我们能够进行分析。但是为了确保这些数字的可用性,《选举法》进行了修订。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者插图

从这个项目中主要学到的是,在分析数据时选择合适的指标至关重要。这项任务并不简单,但是开发亲和力测量使分析变得尽可能简单。数据准备的目的是获得尽可能强的信号以进行探索,事实证明,亲和力检查足以识别选举重新计票中的错误

细节

亲和力的定义

更具体地说,由于 log(0)以及 0/0 和∞/∞未定义,有必要处理一些异常:

  1. 如果投票区中的政党计票为 0,即候选人计票也为 0,则亲和力设置为 0。
  2. 如果提名区中的政党计票为 0,即候选人计票也为 0,则亲和力设置为 0。这不太可能发生。
  3. 如果提名区中的候选人票数为 0,这不是错误,因为这涵盖了多个投票区。因此,亲和力被设置为 0。
  4. 如果投票区中的政党计票不为 0,并且候选人计票为 0,则:如果政党计票 > β(其中β是某个阈值),则候选人计票被设置为 1,否则亲和度被设置为 0。

第三个规则背后的推理是在候选人在投票区没有获得任何选票的情况下获得亲和力的指示。不能忽视候选人投票数为 0,但我们需要处理 log(0)问题。这是通过使用可能的最低票数 1 来实现的。使用小于 1 的(十进制)数是没有意义的。所得的相似性将是替换规则的特征,而不是低投票计数的特征。然而,如果政党票数非常低,这种替换将导致不成比例的大候选人比例。为了避免引入阈值,低于该阈值的情况被忽略,即相似性被设置为 0。阈值通常设置为 10。

投票站

如上所述,共有 1384 个投票站。下图显示了每个投票点的投票数。这反映了人口密度的明显变化。本文中的所有数字都是基于上次大选。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者插图

政党名单投票和个人投票

一个选民可以投票给一个政党(政党名单投票)或一个候选人(个人投票)。两者在各缔约方的分布情况有所不同。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者插图

对于两个大党 A(社会民主党)和 V(自由党)来说,大多数选票是个人选票。这些是传统的政府政党。通常总理来自这两个政党中的任何一个。他们有强大的地方组织。丹麦政府几乎总是少数党政府,通常是两到四个政党的联盟,然后依靠支持他们的政党来确保他们的政治的多数。实际上,上次大选的结果是产生了一个极不寻常的一党政府(A ),三个支持党(F 和 B)在议会中占了一半以上的席位。

与两大政党相反,还有更理想主义的政党,如(大多数左翼社会主义者)和 D(右翼)。政党名单投票占优势。

e 基本都是单人党。然而,它拥有最大比例的政党名单选票。原因是,这个人只是十个选区中的一个选区的候选人,因此在另一个选区,有一些鲜为人知的地方“替身候选人”。因此,在这些地区,绝大多数选票是政党名单选票。

无论是 K(基督教民主党),E(单一牧师,中间自由派)还是 P(民粹主义,反移民)都没有获得足够的选票来赢得议会席位。

亲和力

下图显示了以上定义的八名候选人(此处称为 a-h)在多个以颜色标识的投票区的相似性。因此,每个颜色段代表一个投票区,八个条形代表特定政党的八名候选人。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者插图

该图给出了α值变化的印象。但事实上,我篡改了数据:对于用粉色标识的投票区,我将候选人 a 的 20 张选票转移到了候选人 T2 的 g 的 T3。有明显的迹象表明, a 现在得到的选票太少,而不清楚哪个候选人得到了太多的选票。有时只能识别非自愿捐献者,有时可以区分捐献者和接受者。

延伸阅读

丹麦的议会选举制度,内政和卫生部与丹麦议会,哥本哈根,2011 年

修正法(丹麦语)

选举特辑:使用变形金刚检测假新闻

原文:https://towardsdatascience.com/election-special-detect-fake-news-using-transformers-3e38d0c5b5c7?source=collection_archive---------36-----------------------

用数据做很酷的事情

介绍

美国 2020 年大选在即。在选举前后,社交媒体上发布的假新闻是一个巨大的问题。虽然有些假新闻是为了歪曲选举结果或通过广告赚快钱而故意制作的,但虚假信息也可以由被误导的个人在其社交媒体帖子中分享。这些帖子可以迅速成为病毒模糊。大多数人相信被很多人喜欢的帖子一定是真的。

对于机器学习模型来说,检测假新闻并不是一件容易的事情。这些故事中有许多写得非常好。机器学习仍然有帮助,因为:

  • 它可以检测到该书写风格与它的数据库中被标记为假的相似
  • 故事中事件的版本与已知的真实情况相矛盾

在这篇博客中,我们使用 BERT 和 T5 变压器模型构建了一个假新闻检测器。T5 模型在检测 2016 年选举前后发布的真实假新闻方面表现非常好,准确率高达 80%。

“假新闻检测器”的完整代码在我的 Github 这里 公开。

深度学习分析,我们非常热衷于使用数据科学和机器学习来解决现实世界的问题。请联系我们,与我们讨论 NLP 项目。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 的免版税—https://unsplash.com/photos/EQSPI11rf68

数据集

对于这篇博客,我们使用了 Kaggle 数据集——了解假新闻的真相。它包含被 BS Detector 标记为虚假或有偏见的新闻和故事,BS Detector 是 Daniel Sieradski 的 Chrome 扩展。BS 检测器将一些网站标记为假网站,然后从这些网站上抓取的任何新闻都被标记为假新闻。

数据集包含大约 1500 个新闻故事,其中大约 1000 个是假的,500 个是真的。我喜欢这个数据集的原因是,它包含了在 2016 年大选最后几天捕捉到的故事,这使得它与检测选举期间的假新闻非常相关。然而,给假新闻贴上标签的挑战之一是,需要花费时间和精力来审查故事及其正确性。这里采用的方法是认为所有来自标有 BS 的网站的故事都是假的。情况可能并不总是如此。

标记为真实的故事的一个例子是:

ed state  \nfox news sunday reported this morning that anthony weiner is cooperating with the fbi which has reopened yes lefties reopened the investigation into hillary clintons classified emails watch as chris wallace reports the breaking news during the panel segment near the end of the show \nand the news is breaking while were on the air our colleague bret baier has just sent us an email saying he has two sources who say that anthony weiner who also had coownership of that laptop with his estranged wife huma abedin is cooperating with the fbi investigation had given them the laptop so therefore they didnt need a warrant to get in to see the contents of said laptop pretty interesting development \ntargets of federal investigations will often cooperate hoping that they will get consideration from a judge at sentencing given weiners wellknown penchant for lying its hard to believe that a prosecutor would give weiner a deal based on an agreement to testify unless his testimony were very strongly corroborated by hard evidence but cooperation can take many forms  and as wallace indicated on this mornings show one of those forms could be signing a consent form to allow   the contents of devices that they could probably get a warrant for anyway well see if weiners cooperation extends beyond that more related

一个假的故事是:

for those who are too young or too unwilling to remember a trip down memory lane \n  debut hillary speaks at wellesley graduation insults edward brooke senates lone black member \n  watergate committee says chief counsel jerry zeifman of hillarys performance she was a liar she was an unethical dishonest lawyer she conspired to violate the constitution the rules of the house the rules of the committee and the rules of confidentiality \n  cattlegate as wife of arkansas governor she invests  in cattle futures makes  \n  whitewater clintons borrow money to launch whitewater development corporations several people go to prison over it clintons dont \n  bimbo eruptions bill and hillary swear to steve kroft on  minutes bill had nothing to do with gennifer flowers \n  private investigators we reached out to them hillary tells cbs steve kroft of bills women i met with two of them to reassure them they were friends of ours they also hire pis to bribe andor threaten as many as twodozen of them \n  health care reform hillary heads secret healthcare task force sued successfully for violating open meeting laws subsequent plan killed by democraticcontrolled house \n  ....

这些故事平均有 311 个单词,有些故事超过 1000 个单词。

模特培训

在这个练习中,我们训练了来自 HuggingFace — 1 的两个模型。具有序列分类头的 BERT 和具有条件生成头的第二个 T5 变换器模型。BERT 模型在验证集上检测假新闻的准确率为 75%,而 T5 模型能够达到 80%的准确率。因此,我们将在博客的其余部分集中讨论如何训练 T5 模型

T5 是文本到文本模型,这意味着它可以被训练成从一种格式的输入文本到一种格式的输出文本。这使得该模型非常通用。我个人用它来训练文本摘要。在这里查看我的博客。并使用它来建立一个琐事机器人,它可以在没有任何上下文的情况下从内存中检索答案。点击查看这篇博客

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

T5 —文本到文本转换转换器。图片来自 T5 纸。

将我们的分类问题转换为文本到文本的格式

Huggingface T5 实现包含一个条件生成头,可以用于任何任务。对于我们的任务,我们的输入是真实的新闻故事,输出是文本——真实的/虚假的。

对于输入文本,我们将最大标记长度设置为 512。如果故事小于 512,那么将在结尾添加一个标记。如果故事比较大,那么它们会被截断。对于输出,令牌长度设置为 3。下面是它的代码片段。请在这里找到我的 Github 上的完整代码。

source = self.tokenizer.batch_encode_plus([input_], max_length=self.input_length, 
                                                padding='max_length', truncation=True, return_tensors="pt")

targets = self.tokenizer.batch_encode_plus([target_], max_length=3, 
                                                     padding='max_length', truncation=True, return_tensors="pt")

定义 T5 模型类

接下来,我们定义 T5 模型微调类。模型正向传递与其他变压器模型相同。因为 T5 是文本-文本模型,所以输入和目标都被标记化,并且它们的注意力屏蔽被传递给模型。

**def** forward(self, input_ids, attention_mask=**None**, decoder_input_ids=**None**, decoder_attention_mask=**None**, lm_labels=**None**):
        **return** self.model(
                input_ids,
                attention_mask=attention_mask,
                decoder_input_ids=decoder_input_ids,
                decoder_attention_mask=decoder_attention_mask,
                labels=lm_labels
            )

在生成步骤中,解码器的输出被限制为令牌长度 3,如下所示:

**def** _generative_step(self, batch) :

        t0 = time.time()
        *# print(batch)*
        inp_ids = batch["source_ids"]

        generated_ids = self.model.generate(
            batch["source_ids"],
            attention_mask=batch["source_mask"],
            use_cache=**True**,
            decoder_attention_mask=batch['target_mask'],
            max_length=3

        )
        preds = self.ids_to_clean_text(generated_ids)
        target = self.ids_to_clean_text(batch["target_ids"]

该模型通过检查生成的标签(假/真)是否与实际标签匹配来测量准确度分数。

模型训练和结果

使用 8 的批量大小训练 t5 small 30 个时期。这个模型花了大约一个小时来训练。权重和偏差用于监控训练。在线 Github 代码已经将 wandb 集成到 Pytorch Lightning 中用于培训。

我在令牌长度 512 和 1024 上训练了一个 T5 small。这两个模型表现相似,准确率接近 80%

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

T5 小型假新闻检测器— Val 集合准确度分数

在 val 集上测试 T5 表明,该模型具有令人印象深刻的检测假新闻的能力。

Input Text: classify: russias most potent weapon hoarding gold shtfplancom this article was written by jay syrmopoulos and originally published at the free thought project editors comment he who holds the gold makes the rules fresh attempts at containing russia and continuing the empire have been met with countermoves russia appears to be building strength in every way putin and his country have no intention of being under the american thumb and are developing rapid resistance as the us petrodollar loses its grip and china russia and the east shift into new currencies and shifting
world order what lies ahead it will be a strong hand for the countries that have the most significant backing in gold and hard assets and china and russia have positioned themselves very
well prepare for a changing economic landscape and one in which selfreliance might be all we have russia is hoarding gold at an alarming rate the next world war will be fought with currencies by jay syrmopoulos with all eyes on russias unveiling their latest nuclear intercontinental ballistic missile icbm which nato has dubbed the satan missile as tensions with the us increase moscows most potent weapon may be something drastically different the rapidly evolving geopolitical weapon brandished by russia is an ever increasing stockpile of gold as well as russias native currency the
ruble take a look at the symbol below as it could soon come to change the entire hierarchy of the international order potentially ushering in a complete international paradigm shift...

Actual Class: Fake

Predicted Class from T5: Fake

结论

T5 是一款很棒的车型。有了足够的数据,针对任何 NLP 问题微调转换器变得很容易。这篇博客表明 T5 可以很好地检测假新闻。

我希望您尝试一下代码,并训练自己的模型。请在下面的评论中分享你的经历。

深度学习分析,我们非常热衷于使用机器学习来解决现实世界的问题。我们已经帮助许多企业部署了创新的基于人工智能的解决方案。如果你看到合作的机会,请通过我们的网站这里联系我们。

参考

选举特辑:训练一台 GPT-2 生成唐纳德·川普的演讲

原文:https://towardsdatascience.com/election-special-train-a-gpt-2-to-generate-donald-trump-speeches-b66fc3aa92b9?source=collection_archive---------39-----------------------

用数据做很酷的事情

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自 Unsplash 的免版税—https://unsplash.com/photos/ls8Kc0P9hAA

介绍

在这篇关注美国 2020 年大选的第二篇博客中,我们根据唐纳德·特朗普的演讲训练了一个 GPT-2 模型。通过最少的训练,GPT-2 模型成功地复制了他的风格,并开始以他的风格写作。参见 GPT-2 生成的 Donald Trump 演讲样本。听起来确实像他!

As president, I kept my promise. Nobody else did. We set records. We set records. Thank you very much. Great job. We set records. And by the way, there's two really greats, right? There's Sonny Perdue and there's Barack Hussein Obama. The two greats. Really great. One has become the most powerful president in the history of our country. I said, "How powerful is that?"

GPT-2 模型去年由 Open AI 发布。这是一个已经在 40GB 互联网文本上训练过的语言模型。该模型可以在任何文本样本上进行微调,以根据输入文本调整其生成样式。在这篇博客中,我们使用 HuggingFace repo 的实现 GPT-2,看看你如何调整它来为 DT 写演讲稿。

我们还写了一篇关于使用 T5 模型检测假新闻的选举专题博客。点击查看

深度学习分析,我们非常热衷于使用数据科学和机器学习来解决现实世界的问题。请联系我们,与我们讨论 NLP 项目。

数据集

对于这篇博客,我们使用了 Kaggle 数据集——唐纳德·特朗普的集会。这个数据集有他的 35 次演讲。一半的演讲来自去年年底,其余的来自今年。最新的演讲来自他 2020 年 9 月在塞勒姆、费耶特维尔和亨德森的集会。

在我们开始根据这些数据训练语言模型之前,我们需要以 GPT 新协议要求的格式获取数据。GPT-2 需要逐句传递数据。

作为数据预处理的一部分,我们执行以下操作:

  1. 把演讲分成句子
  2. 将前 85%的句子保留在训练集中,其余的放在验证集中。验证集将用于测量困惑分数

实现这一点的代码片段如下。NLTK 库用于将语音句子标记成句子。

train_sentences = []
val_sentences = []
for filename in sorted(glob.glob(os.path.join('DT_speeches', '*txt'))):
    print(filename)
    f = open(filename, 'r')
    file_input = f.readlines()
    for cnt, line in enumerate(file_input):
        sentences = nltk.sent_tokenize(line)
        for i , sent in enumerate(sentences):
            if i <= len(sentences)*0.85:
                train_sentences.append(sent)
            else:
                val_sentences.append(sent)

训练和赋值语句列表被写入文本文件。GPT-2 模型将使用这些文本文件进行训练。

微调 GPT-2 语言建模

让我们从 Giphy 训练它 GIF—https://media.giphy.com/media/g0Kzu8bTbGPPEH0hSm/giphy.gif

GPT-2 是一种语言模型,可以在许多下游任务上进行微调,包括语言建模、摘要等。要了解更多关于语言建模的知识,请阅读我的这个博客

要微调你的模型,首先克隆最新的拥抱脸变形金刚回购。

git clone [https://github.com/huggingface/transformers](https://github.com/huggingface/transformers)

语言模型微调脚本位于 examples/language-modeling 下。使用以下命令开始训练:

python run_clm.py \
    --model_name_or_path gpt2 \
    --train_data_file <path to your train text file> \
    --eval_data_file <path to your val text file> \
    --do_train \
    --do_eval \
    --output_dir output-gpt2 \
    --block_size=200\
    --per_device_train_batch_size=1\
    --save_steps 5000\
    --num_train_epochs=5 \

上面的主要超参数是:

  • model_name_or_path: gpt2 将训练 gpt2 小模型。如果要训练中等型号,请指定 gp T2-中等
  • 块大小:输入训练集将被截断成这个大小的块用于训练。该参数是注意窗口。在我个人的 GPU 上,200 的块大小是我可以训练的最大值。
  • save_steps:保存检查点之前的持续时间

除此之外,我们还有批量大小和次数。

我能在大约 20 分钟内在 GTX-1080 Ti 上用上述配置训练 GPT-2 小型飞机。经训练的模型在验证集上具有 14 的困惑分数。这还不错。我认为,如果你有更多的计算,你可以尝试更长的块大小和 GPT-2 中等模型,以进一步提高困惑分数。

测试训练好的 GPT-2 模型

为了测试训练好的模型,我们导航到 transformers repo 中的 examples/text-generation 文件夹。为了生成文本,我们需要将路径传递给训练好的模型。我们也可以通过一个提示来给生成指明方向。运行生成的命令如下。

python run_generation.py \
 --model_type gpt2 \
 --model_name_or_path output-gpt2/ \
 --length 300 \
 --prompt "It's a fine Tuesday morning."

我微调的模型生成了以下有趣的片段。

It's a fine Tuesday morning. Democrats want to give illegal immigrants free healthcare, free education, and even free healthcare to their children. Republicans want to give all citizens the right to self-defense. Today, we've added a great new provision to the US healthcare law called Obamacare. That means if you go to the doctor, you're protected from having to pay a fortune for the plan of your choice. You're not going to have to worry about premiums.

可以看出,GPT-2 了解到民主党人更喜欢开放移民和免费医疗。

I think we are going to have the greatest year we've ever had. We're going to have the greatest year in the history of our country. Can you believe it? And I say that because for years and years, these guys in the back were saying, "We love this guy." I'm talking about Donald Trump, he's great. Remember all those debates when you couldn't see the crowd? Well, it was like the H1NI infected people getting small amounts of water. He was very weak. It wasn't quite as strong. I guess he should have never been given the treatment. Remember all those debates? I was in the middle of debating him and he went, "Well, this is going to be short." He was just beginning. Now they have him talking all over. I was watching him. He was talking about himself. "Well, it's true. I'll never be able to compete with you, Donald Trump."

在这里,GPT-2 正在复制他自我鼓掌的风格——“我们爱这个家伙。”,“嗯,是真的。我永远无法和你竞争,唐纳德·特朗普。”

结论

在唐纳德·特朗普的演讲上微调 GPT2 模型既有趣又容易。令人鼓舞的是,只需很少的训练,该模型就能够复制 DT 词汇表中的风格和关键词。

我希望您尝试一下代码,并训练自己的模型。请在下面的评论中分享你的经历。

深度学习分析,我们非常热衷于使用机器学习来解决现实世界的问题。我们已经帮助许多企业部署了创新的基于人工智能的解决方案。如果您看到合作的机会,请通过我们的网站这里联系我们。

参考

伊莱克特拉:掩蔽语言模型的发展

原文:https://towardsdatascience.com/electra-developments-in-masked-language-modelling-3cf1c25fc61a?source=collection_archive---------43-----------------------

这是 ELECTRA 的主要功能概述,此处描述了一个模型:

https://arxiv.org/pdf/2003.10555.pdf

有了 BERT 和 BERT 衍生的变形金刚(XLNet,RoBERTa,ALBERT,任何以芝麻街人物命名的变形金刚),我清楚地知道,作为一个拥有单个 GPU 和没有行业支持的个人爱好者,我自己不可能训练一个深度学习语言模型。当我在 XLNet 上为 AISC 准备现场演示时(在 YouTube 上这里,我看到了埃利奥特·特纳的这条推文:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

埃利奥特·特纳发的这条推文的截图

我训练自己模特的模糊梦想破灭了。这是回到 2019 年,即之前的时代。我指的是在 GPT-3 之前,它使用了如此惊人的计算资源,以至于目前只有在你身后有八位数的情况下才能训练。这个模型引起了我的兴趣,理论上,在单个 GPU 上训练四天之后,ELECTRA-small 可以胜过 GPT-1。他们认为完整模型(ELECTRA-base)的性能与 XLNet 和 RoBERTa 相当,而使用的资源只有它们的大约四分之一。很有趣。这是什么方法论?

该模型的架构和大多数超参数与 BERT 中的相同,因此这里不再赘述。

替换令牌检测是他们选择的预训练方法。BERT 用[MASK]有选择地替换序列中的记号,而 ELECTRA 使用生成器用看似合理的替代单词替换记号。

输入序列是记号列表x=【x1,… xn】。MLM 试图用其他似是而非的词来替换 k 个符号,得到一个 k 个掩码的列表[m1,…,mk]。对于这篇论文,他们建议屏蔽掉大约 15%的输入令牌。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个过程产生了 x ^corrupt,这是一个在 m 个位置插入了看似合理的备选单词的记号列表。

然后,鉴别者的工作就是确定句子中的单词是原词还是改词。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MLM 和鉴别器的损失函数如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然这看起来很像一个甘,发电机是最大的可能性,而不是敌对训练。另一个区别是,如果生成器生成一个与原始令牌相同的令牌,该令牌将被标记为原始令牌,而不是生成令牌。

实验

这里的基本模型的预训练数据与 BERT 相同,是来自维基百科和图书语料库的 33 亿个标记。伊莱克特-拉奇在 XLNet 数据上接受训练,XLNet 数据通过 ClueWeb、CommonCrawl 和 GigaWord 添加到 BERT 数据中。

模型的评估是在 GLUE benchmark 和 SQuAD 上完成的,两者都在这里解释了。

车型扩展

伊莱克特做了一些改进,提高了模型的准确性。

**权重共享:**生成器和鉴别器中使用的嵌入是共享的。这在训练相同数量的时期时,在没有重量约束的情况下,使用相同的模型参数,在准确性上产生了小的提升。只有嵌入权重是共享的,共享所有权重具有要求两个网络大小相同的显著缺点。为什么这在这里工作得如此好的一个理论是,利用屏蔽语言建模,要被区分的输入记号和被破坏的记号驻留在相同的向量空间中。在这种情况下,生成器学习一个嵌入空间,鉴别器有效地学习一个嵌入空间和嵌入空间之间的变换是没有意义的。

小型发电机:

这在很大程度上是前面扩展的必然结果,因为如果生成器和鉴别器共享所有权重并且大小相同,则模型每个训练步骤需要的计算量是仅使用纯[掩码]令牌时的两倍。在这个模型中,他们探索使用一个 unigram 生成器来生成屏蔽令牌。由于使用了各种尺寸的发生器,他们选定的发生器大约是鉴频器尺寸的 0.25-0.5 倍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练算法:

Clark 等人从整体上为 ELECTRA 尝试了多种高级训练算法,并确定了以下内容:

  1. 仅训练发生器(最大可能性)进行 n 步。
  2. 用生成器的权重初始化鉴别器的权重。然后在 n 步中用鉴频器损失函数训练鉴频器,保持发电机的重量不变。

大型型号:

为了与典型尺寸的 SOTA 模型相比较,基本的 ELECTRA 必须相当大。他们使用与 ELECTRA-400k 相同的超参数和训练时间来训练自己的 BERT-Large 模型。

我很好奇的一点是,在这篇论文中,模型相对于彼此需要多长时间来训练是一个有点困惑的问题。他们说的是 ELECTRA-Large 和 BERT-Large 大小一样,但是他们也用和 ELECTRA-400k 一样的超参数和训练时间训练了自己的 BERT。他们描述的 ELECTRA 的卖点是使用更少的物理计算资源,但我希望他们在其他大型模型旁边更明确地概述这些物理和时间资源,以便进行比较。他们确实列出了 train FLOPs(每秒浮点运算次数),这通常可以作为衡量他们的 GPU 计算速度的指标——但在附录中,他们澄清了“运算”被算作数学运算,而不是机器指令(通常是这样)。如果能对所使用的资源以及培训前和培训所花费的时间进行比较,我们将不胜感激。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

胶水开发组的结果。

他们自己的模型使用 ELECTRA-400k 中使用的相同超参数进行训练。

效率分析:

他们怀疑,必须分析字符串中的所有记号,而不是填补显式掩码造成的空白,这导致了伊莱克特的效率提高。他们创造了伊莱克特 15%,它只计算被屏蔽掉的 15%令牌的鉴别器损耗。基地伊莱克特在胶水任务上得分 85%,而伊莱克特 15%得分 82.4%,与伯特的 82.2%不相上下。

阴性结果:

在这项研究中,他们尝试了许多训练范式,如模型大小、生成器架构、重量捆绑,有些范式普遍不成功,因此没有出现在论文的主体中。

  1. 战略性地对稀有令牌应用屏蔽。与常规的 BERT 相比,这并没有导致任何显著的加速。
  2. 提高发生器的温度,或不允许正确的字输出并没有改善结果。
  3. 添加一系列句子级别的对比标记,就像 SpanBERT 的蒙面语言建模版本。这实际上降低了胶水和小队任务的模型分数。

结论:

虽然这不是对抗性训练,但它是对比学习如何有效应用于语言的一个例子。从广义上讲,对比学习包括区分观察到的数据点和虚构的例子。BERT、Ernie、XLNet、ELMo、RoBERTa 和 SpanBERT 都引入或扩展了屏蔽语言建模范例,其中模型猜测单个屏蔽标记的正确标记,伊莱克特通过引入屏蔽可能是鉴别器接收的序列中的任何标记的可能性来进一步扩展它。伊莱克特拉现在是拥抱脸和简单变形金刚的一部分。

冠状病毒疫情期间维多利亚州的电力需求

原文:https://towardsdatascience.com/electricity-demand-in-victoria-during-coronavirus-pandemic-30c23ff4e0d?source=collection_archive---------60-----------------------

新冠肺炎·疫情执政期间,澳大利亚第二大州的电力需求模式发生了变化吗?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

约书亚·希伯特在 Unsplash 上拍摄的照片

外国媒体普遍将澳洲视为成功遏制新冠肺炎疫情的发达国家的典范。2020 年 7 月,在每日新增病例大幅上升后,澳大利亚人口第二大州维多利亚州推出了一些世界上最严格的流动控制措施。被广泛认为有助于降低病毒传播率的旅行限制,对商业产生了不利影响。在其他指标中,电力需求被认为反映了经济活动的水平。疫情时期经济活动下滑得越厉害,恢复到疫情之前水平的时间就可能越长,也可能越长。本文旨在从 2015 年开始,在近期历史背景下,探讨维多利亚州 2020 年的电力需求,从而阐明这个问题。

数据和异常

来自澳大利亚能源市场运营商( AEMO )的数据包含关于电力需求和价格的信息,以 30 分钟为间隔进行汇总。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有趣的是,在相当多的日子里,电力至少在一天的部分时间里以负价格报价。当这种情况发生时,能源生产商向批发客户支付费用,以获取他们生产的能源。以负价格报价的每日总需求的分数绘制如下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

2020 年期间,在一些相关事件和政策公告之前,往往会出现需求比例较高且价格为负值的日子。值得注意的事件之一是维多利亚最大的发电站黑兹尔伍德 (1600 MW)的退役。与其他燃煤发电站一样,它提供了近乎恒定的输出。然而,在 2018 年和 2019 年,几个大型太阳能发电厂 (480 MW 组合标称功率)上线。截至 2020 年 9 月,维多利亚屋顶太阳能总功率相当于现已关闭的黑兹尔伍德。此外, 25 个总标称功率为 2350 MW 的大型风能项目在维多利亚州运营。因此,依赖天气的可再生能源的规模和可变性足以产生过剩的电力供应。然而,这些异常的程度在 2020 年明显增加,并且不能用维多利亚州新的可再生能源安装率来解释,与 2019 年相比有所下降。

预测电力需求

在历史背景下,预计 2020 年期间电力需求的潜在异常会降低符合旧数据的预测模型的准确性。为了检验这一假设,使用 2015 年 1 月至 2019 年 1 月的数据拟合了一个预测模型。通过与 2019 年的实际需求进行比较,其预测得到了验证,但没有明确拟合这一数据。预测模型基于一个序列到序列机器学习模型,为翻译(Google translate)等自然语言处理任务开发。应用于时间序列预测,这个序列对序列模型在由 Kaggle 主办的网络流量预测挑战赛上展示了的优异表现。预测模型源代码可以在 my Github repository 中找到。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

该模型用于预测 2020 年 1 月至 2020 年 10 月的测试期。下图显示了 2019 年和 2020 年预测的百分比误差。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

就预测模型百分比误差而言,2020 年与 2019 年相似。这可以通过用平均绝对百分比误差 (MAPE)将信息浓缩成一个数字来进一步说明。此指标去掉符号,并对一段时间内的百分比误差进行平均。

上表显示,7 天电力需求预测在 2020 年的 MAPE 与 2019 年基本相同,但进行了优化。因此,根据 AEMO 每日电力需求数据,2020 年和 2019 年一样可预测。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

目前的分析表明,与 2019 年相比,维多利亚州 2020 年至 10 月的每日总电力需求模式没有出现重大中断。远程工作安排将用电点从办公室转移到了家中。主要电力消费者,如美国铝业公司的铝冶炼厂在 2020 年基本上继续运营。如果电力需求是经济活动的一个良好指标,那么一旦 Covid 限制取消,持续的电力需求模式是维多利亚州经济复苏步伐的一个令人鼓舞的迹象。

然而,在 2020 年期间,大部分(10%及以上)日常电力需求以负价格交易的天数显著增加。2019 年,随着工业和屋顶太阳能的大幅扩张,出现了类似的模式,尽管规模较小。相比之下,在 2020 年期间,新的可再生能源的速度明显放缓。这可能表明这些价格异常幅度的上升与疫情相关的维多利亚州电力需求中断之间的联系。

确认

我非常感谢 Melbourne Datathon 2020 的组织者设立了关于电力消耗模式的挑战,这是这篇文章的动机。特别感谢 Phil Brierly 澄清了无数关于挑战的问题,并组织了出色的墨尔本数据科学会议!

数据源

[1] AEMO 汇总价格和需求数据
【2】维多利亚州公共假期
【3】学校开学日期,维多利亚州教育
【4】维多利亚州 PM Daniel Andrews tweeter
【5】澳大利亚气象局

使用 GeoPandas 和传单的 Python 和 R 语言的优雅地理图

原文:https://towardsdatascience.com/elegant-geographic-plots-in-python-and-r-using-geopandas-and-leaflet-27126b60bace?source=collection_archive---------22-----------------------

如何使用 GeoPandas 和传单?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由марьянблан| @ marjanblanUnsplash 上拍摄

有几个地理库可用于在地图上绘制位置信息。我以前在这里写过相同的主题,但是从那以后,我更广泛地使用了这些库,并且了解了新的库。在使用和检查了几个库之后,我发现 GeoPandas 和 Leaflet 库是两个最容易使用和高度可定制的库。该代码可作为 GitHub repo 获得:

[## kb22/地理绘图

这个库描述了使用 Python 中的 GeoPandas 和 R 中的 Leaflet 作为两种非常优雅(和简单)的绘图方式…

github.com](https://github.com/kb22/geo-plotting)

资料组

数据集取自 Kaggle 。它包括世界各国的人口密度(每平方公里的人口数)。该数据集包括从 1961 年到 2015 年的值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

资料组

上图显示,在读取数据集时,我们需要跳过前 4 行,所以我们直接从数据开始。像“1960”这样的列是空的,因此可以删除。此外,比利时等一些国家的数据缺失,因此我们将从收集中删除这些记录。

使用 GeoPandas 的静态图(Python 语言)

导入库

我们将导入库pandas来读取数据集,然后使用geopandas绘制地图。注意,为了删除不必要的警告,我添加了特定的命令。

导入数据集

接下来,我们将导入数据集。如前所述,我跳过了前 4 行。

我用drop()的方法去掉所有多余的不需要的列(“指标名称”、“指标代码”、“1960”、“2016”、“未命名:61”)。我使用dropna()删除所有具有空值的记录,最后计算 1961 年到 2015 年的平均人口密度,放入新列avgPopulationDensity

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果数据集

绘图数据

我们首先直接从 geopandas 加载世界基图,然后将我们上面细化的数据集与这个基图数据合并。然后,我们简单地用avgPopulationDensity列中的值绘制数据world。我们在Reds色图中给国家加阴影,这意味着平均人口密度较高的国家比其他国家更暗。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

平均人口密度图(使用 GeoPandas)

我们可以清楚地看到,亚洲国家有更多的人口密度值,特别是印度,孟加拉国,韩国和日本。

使用传单的互动情节®

导入库

我们将加载用于生成绘图的leaflet库和用于读取 shapefiles 的sf库。

导入数据集

就像在 Python 中一样,我们跳过前 4 行导入数据集,删除多余的列,删除缺少值的行,并创建avgPopulationDensity列。

重要的是,我们还要加载绘图所需的 shapefile。因此,我们使用st_read方法,将带有这个 shapefile 的数据集合并到一个名为shapes的变量中。

绘图数据

在绘制数据之前,我们首先定义想要使用的调色板。我为不同的箱子定义了红色的阴影,基于此,我将对结果图进行着色。由于这是一个自定义调色板,它的颜色将不同于我们在 GeoPandas 图中看到的颜色。

接下来,我们绘制传单 geoplot。leaflet()开始创建地图,addTiles()添加国家地图。接下来,我们通过定义纬度和经度值以及缩放比例来设置地图视图,这样我们就可以一次看到整个地图。如果需要,我们可以更改这些值,但由于它是一个交互式地图,我们也可以动态地这样做。

最后,基于avgPopulationDensity列,我们对整个地块进行颜色分级,并附上国家名称作为标签。我们通过添加笔画(STROKE = TRUE)来定制每个国家的外观,然后用weight = 1将它们设置为black,定义每个边界的宽度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

平均人口密度图(使用传单)

我们观察到亚洲国家如印度、孟加拉国、中国、日本和其他国家的人口密度比其他国家高。

结论

从上面的两个例子中,我们可以看到创建漂亮的静态和动态地理图是多么容易。此外,我们还可以添加大量定制内容。

如果您有任何建议、问题或想法,请在下面告诉我。

让数据说话的优雅方式:探索性数据分析

原文:https://towardsdatascience.com/elegant-way-to-make-data-talk-stories-exploratory-data-analysis-783e68837a2?source=collection_archive---------53-----------------------

大数据无所不知

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上拍照

介绍

数据可以讲述伟大的故事,让数据传达正确的故事是一门艺术。获得这门艺术的手段是探索性数据分析(EDA) 。探索性数据分析只不过是使用统计和概率方法来理解数据试图传达给我们的信息。

作为一名数据科学家,大部分工作将主要集中在理解数据上,并试图只获得必要的特征,以发送到机器学习模型。只有当输入数据有意义时,模型才能够发挥其最大功效。

真正困难的事情之一是弄清楚该问什么问题。一旦你想通了这个问题,那么答案就相对容易了——埃隆·马斯克

从一张白纸中找到正确的问题通常是一项具有挑战性的任务。但是,通过不断地问 为什么, 我们将能够理解数据的行为并获得洞察力

现在,我们可以深入了解一些在执行 EDA 时可以使用的常见起点。我从小就是口袋妖怪的粉丝,我将使用口袋妖怪🌟来自 Kaggle 的数据集用于逐步执行 EDA

来吧,让我们抓住他们:)

超级工具:熊猫

在描述执行 EDA 的一般步骤之前,让我们看一下一个重要的工具。

Pandas 库是建立在 python 之上的一个快速、强大且简单的工具。从我的个人经历来看,作为一名数据科学家,我每天的面包和黄油完全依赖于熊猫。只用一两行代码就可以轻松实现所有的编程逻辑,这使得这个库如此受欢迎。它可以处理成千上万的数据,而没有很多计算要求。此外,这个库提供的功能很简单,而且非常有效。

甚至对于我们的口袋妖怪数据集 EDA,我们将使用熊猫来理解数据,也用于可视化

探索性数据分析的常见步骤:

当我收到数据时,我会执行下面的一些步骤来掌握我实际处理的内容。

  1. 了解您的数据
  2. 了解数据中的特征/列
  3. 数据的因果分析

了解你的数据:

了解我们将要处理的数据点的数量是很重要的。这是因为,一旦数据的大小增加,代码必须以这样一种格式编写,即它是高效的,并且在更短的时间内执行。

#Import library
import pandas as pd
import numpy as np#Read data
data = pd.read_csv('../pokemon.csv')

shape() 函数在熊猫格式中给出行数和列数(rows,cols)

data.shape()
#Output: (801, 41)

了解数据中的特征/列

pandas 中的 info() 函数提供了完整的列分割,包括数据类型在内的每一列中总共有个非空的项。该函数用于确定是否有任何列必须被 估算 以处理缺失数据

data.info()

口袋妖怪数据集中有一些有趣的字段,比如世代、能力、类型*。*使用熊猫的可视化功能,并通过使用 seaborn 的美学外观,我们可以生成有助于提供基线故事的良好图表。

pandas 中的 value_counts() 函数对字段中的唯一值进行分组,并提供组中每个唯一值的频率份额(如果 normalize 参数设置为 *True,*提供出现份额的百分比)。使用直方图图可以很好地理解频率分布。

import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib import cmcolor = cm.Pastel1(np.linspace(.4, .8, 30))fig, ax = plt.subplots(2,2, figsize=(18,18))data.generation.value_counts(normalize=False).sort_index().plot(kind='bar', color=color, ax=ax[0,0])
ax[0,0].set_xlabel('Generation')
ax[0,0].set_ylabel('Frequency of occurrence')data.type1.value_counts(normalize=False).plot(kind='bar', color=color, ax=ax[0,1])
ax[0,1].set_xlabel('Type1')
ax[0,1].set_ylabel('Frequency of occurrence')data.type2.value_counts(normalize=False).plot(kind='bar', color=color, ax=ax[1,0])
ax[1,0].set_xlabel('Type2')
ax[1,0].set_ylabel('Frequency of occurrence')data.is_legendary.value_counts(normalize=False).plot(kind='bar', color=color, ax=ax[1,1])
ax[1,1].set_xlabel('Legendary - Yes/No')
ax[1,1].set_ylabel('Frequency of occurrence')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:频率分布(图片由作者提供)

上图提供了数据集中一些特征(列)的频率分布。从图 1 中的可以推断,非传奇类型的 1、3、5 代口袋妖怪占了大多数。还有,大部分属于 1 型的和 2 型的飞*。*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2:类型 1 和类型 2 的特定值的频率分布(图片由作者提供)

图 2 图是分别有类型 1 =水类型 2 =飞的过滤行得到的。结果表明,水(1 型)的主要 2 型成分是地面和空中的。同样,大多数飞行(2 型)属于正常和错误类型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3:属于一种或两种类型的口袋妖怪的分割(图片由作者提供)

在类型领域更深入一点,图 3 描述了不是所有的口袋妖怪都属于这两种类型,而且,如果一个口袋妖怪属于两种类型,实力也没有太大差异。

因此,从最初的分析中,我们可以了解到数据中有相当有影响力的分类变量,如类型、世代、传奇等,可能会影响建模结果。还有一些 连续变量 如攻击点、防御点、捕获率、base_egg_steps、sp_attack 和 sp _ defence 点,它们与分类变量的相关性对所研究的口袋妖怪的捕获和强度有一些影响。

因果分析

在开始因果分析之前,让我们弄清楚一些可能有助于构建口袋妖怪数据集大纲的问题

  • 捕捉一个给定的口袋妖怪有多难?
  • 它值得被捕获吗?它将如何在战斗中对我们有用?
  • 给定口袋妖怪的优点和缺点是什么?

现在,让我们深入了解各列之间的关系,揭开一些隐藏的故事,并得到一般的假设。

捕捉一只给定的口袋妖怪有多难?

可以使用数据集中的 capture_rate 和 hp(hit-point) 列来定义此指标。捕获率无非是在给定时间内,如果发现口袋妖怪,多久能捕获一只。所以如果捕获率少,那就意味着口袋妖怪很难被捕获。生命值是口袋妖怪可以承受的伤害量。所以如果生命值高,那么口袋妖怪就是强大的。所以大多数捕捉的口袋妖怪都有高 hp 。下图给出了十大难以捕捉的口袋妖怪

**#clearn outlier data
cap = data[~(data.capture_rate == '30 (Meteorite)255 (Core)')]
cap['capture_rate'] = cap['capture_rate'].astype(int)cap[['name','capture_rate','hp']].sort_values(by=['capture_rate','hp'], ascending=[True,False]).head(10)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4:难以捕捉的口袋妖怪(图片由作者提供)

它值得被捕获吗?它在战斗中对我们有什么用处?

一旦一只口袋妖怪被捕获,我们需要明白我们能从中获得什么。所以知道它需要增长多少点就好了,这样它才能变得更强大。这可以从一个叫做 experience_growth 的专栏中推断出来。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5:口袋妖怪所需的经验增长点(图片由作者提供)

近 65%的口袋妖怪需要 1-106 万经验值才能成长。因此,如果一个高 hp 的口袋妖怪被捕获,那么它真的是一个头奖,否则需要更多的点数来增加它的价值。

像身高,体重,hp(生命值),攻击,防御,特殊攻击和特殊防御等领域定义了口袋妖怪的实力,了解它们之间的相互联系是很好的。

**power=data[['height_m','weight_kg','hp','attack','defense','speed','sp_attack','sp_defense']]sns.heatmap(power.corr(), annot=True, cmap='Pastel1', cbar=False)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6:相关热图(图片由作者提供)

口袋妖怪的身高和体重似乎有适度的相关性,其次是防御和特殊防御栏。然而,在功率、尺寸列之间没有发现显著的相关性。

一些类型的口袋妖怪有更好的生命值。这种关系可能有助于确定被捕获或将要被捕获的口袋妖怪的价值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 type1 和 HP 的小提琴图(图片由作者提供)

小提琴图中的点是总体的中间值(第 50 个百分位数),长尾表明该组中有一小部分人具有较高的值(例如正常值)。可以推断出飞行、精灵和龙类型的口袋妖怪有更高的生命值,因此捕捉任何这种类型的口袋妖怪可能在战斗中有用:)**

给定口袋妖怪的优点和缺点是什么?

在人口中,有将近 9%的口袋妖怪是传说中的。人们常说传说中的口袋妖怪有更好的攻击和防御策略,我们可以用方块图来证实数据是否说了同样的事情**

**fig, ax = plt.subplots(1,2, figsize=(10,5))
sns.boxplot(x=data.is_legendary, y=data.attack, ax = ax[0])
sns.boxplot(x=data.is_legendary, y=data.defense, ax = ax[1])**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8:传奇 Vs 攻击/防御的方框图(图片由作者提供)

因此,数据显示传奇口袋妖怪的攻击和防御值比普通口袋妖怪高,证明了我们的假设。

接下来,看看每种类型的口袋妖怪都有怎样的攻防策略会很有趣。为了得到这个结果,我根据口袋妖怪的类型 1 对它们进行了分组,并计算了每种类型的攻击和防御的平均值,从而得出一个推论。**

**fig, ax = plt.subplots(1,2, figsize=(10,5))data.groupby('type1')['attack'].mean().plot(kind='bar',color=color, ax = ax[0])
ax[0].set_xlabel('Type1')
ax[0].set_ylabel('Total Attack points')data.groupby('type1')['defense'].mean().plot(kind='bar',color=color, ax = ax[1])
ax[1].set_xlabel('Type1')
ax[1].set_ylabel('Total Defense points')**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9: Type1 Vs Mean 攻击/防御(图片由作者提供)

龙,战斗和地面型口袋妖怪有最高的攻击行动计划,同时钢铁,岩石和龙有最高的防御行动计划。综合来看,龙,钢,地是最强的口袋妖怪类型

数据集中有一些有趣的字段,如对抗 _ 电、对抗 _ 火和其他一些对抗 _ 字段,它们定义了口袋妖怪在战场上可以承受的伤害量,范围从 0 到 4。对于给定类型的口袋妖怪,我们可以找到每个战斗性质的耐力分数。****

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10:战力热图(图片由作者提供)

可以看出,飞行口袋妖怪对冰型口袋妖怪的伤害最高,其次是黑暗型口袋妖怪对仙女的伤害。因此,这张热图给出了在战场上使用的口袋妖怪的类型。

结论

忠太💫我们已经使用口袋妖怪原始数据集来获得一些基本问题的答案。同样,我们可以对这个基线故事进行更多的开发,具体地为每种类型的战斗找到最好的口袋妖怪名称,甚至更多。

希望这篇文章能让你了解如何从头开始。快乐学习:)

提升仪表板在 Dash 中的交互性

原文:https://towardsdatascience.com/elevate-your-dashboard-interactivity-in-dash-b655a0f45067?source=collection_archive---------20-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

海崖桥|君摄

在 dash 代码中实现“回调”的初学者协会

上周,我和大家分享了如何在 python 中使用 Dash 制作一个仪表盘来跟踪冠状病毒的传播,从中可以实时了解全球冠状病毒病例的数量,包括确诊、痊愈和死亡病例,以及它们在世界地图上的分布。

[## 使用 Dash 构建一个仪表板来跟踪冠状病毒的传播

全球冠状病毒(2019-nCoV)患者确诊、康复和死亡的实时视图

towardsdatascience.com](/build-a-dashboard-to-track-the-spread-of-coronavirus-using-dash-90364f016764)

对于第一个版本,我们实现了基本的 dash 功能,并获得了一个静态应用程序接口。换句话说,除了plotly ( 提供的本地交互,例如悬停文本显示和图形上的地图探索,一旦应用程序在用户的浏览器上启动,屏幕上显示的所有内容都将被设置。用户可能会在几秒钟内使用完仪表板,然后弹开。

一般来说,作为一个 web 应用程序,其整体健康状况的一个主要指标是参与度,它表示在一段定义的时间内,在您的应用程序中保持活跃的用户的百分比。这对于依赖稳定的用户流来产生收入的商业 web 应用程序来说尤其重要(当然这不是我的 dashboard 应用程序的目的,但是,为了引出本文的主题,让我们假设我们现在正在构建一个“商业应用程序”)。提高 web 应用程序参与度的一个简单而有效的方法是提高它与用户的交互性。

大家可以看到,相比于第一个版本( 标题图 在我上一篇中),在最新版本的 2019-nCoV 跟踪仪表盘中的地图旁边放置了一个数据表(图 1)。随着该数据表添加了两种类型的交互性,

  1. 用户可以根据四列中的任意一列(即国家/地区、确诊、康复和死亡)对行进行排序;
  2. 一旦用户选择表格中的一行,地图将自动更新为国家/地区的放大视图(例如,当选择“Mainland China”时,地图将放大到中国并显示每个省的病例数)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1 |仪表板中的两个函数和数据表

这两个功能可能有助于将用户保留在仪表板中更长时间,因为地图上显示的结果可以基于他们自己的兴趣和选择。

在本帖中,我将与你分享如何在 dash 中使用回调来实现前述的交互性。像往常一样,您可以从 my Github 访问所有需要的数据和 jupyter 笔记本。

dash 中的回调是什么

在 dash 中,callback可以理解为 python 函数和应用程序之间链接的双向隧道。

具体来说,InputOutput两种货物在一个callback内从两个方向交付,Input是从appfunction交付的货物,通过用户在app ( 中的动作生成,例如输入一个值,选择一行,从下拉菜单中选择一个项目,将光标悬停在一个分散点上等。)。相反,Output是从functionapp的货物,由function根据Input返回。

InputOutput都有两个自变量component_idcomponent_propertycomponent_id确保callback识别正确的发送者和接收者,而component_property是货物的实体。也就是说,基于这些component_id , callback知道哪个组件的component_property作为Input应该被监控,从function返回的component_property作为Output应该被分配给哪个组件。

在下面取自 Dash 教程的例子中,我们可以看到html.Div()容器中的两个组件( *dcc.Input()* *html.Div()*)各有一个id。这两个 id 分别分配给callback中的OutputInput。因此,用户输入的值( *dcc.Input* *value* 属性,其中 *id* 为‘my-id’)将被传递给update_output_div(input_value),返回的结果将被赋给html.Div()children属性,其中id为‘my-div’。

样本破折号

最重要的是,callback实时监控input的货物流量。如上例所示,一旦输入框内发生变化,就会触发update_output_div(input_value),并且children内容会立即更新(图 2)。这一特性使用户能够在采取行动后立即看到来自应用程序的反馈。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2 |示例应用程序

您可能会注意到component_property只是给定组件的属性,这意味着我们可以通过回调函数返回组件的任何属性。

一旦我们理解了 dash 中回调的概念,让我们深入到我的仪表板的代码中。

此仪表板中的回拨

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3 |地图容器的代码

这里我先在地图驻留的容器里面多加了一个html.Div() ( 图 3line523-line563 )子容器。在这个子容器中,有两个组件,html.H5()用于标题,dash_tale.DataTable()(Dash DataTable是一个交互式表格组件,设计用于查看、编辑和探索数据表的大型数据集

还记得第一种交互性,排序功能吗?这个很简单,可以直接用sort_action ( line536, )设置,可以让数据按列排序,接受 DataTable 组件中 *none* *native* *custom*的任意一个值。这里我们只使用了native,它根据数据的类型对数据进行排序(例如升序表示数字,字母表表示字符串)。

为了能够选择数据表中的行,我们还需要设置row_selectable='single' ( line534 ),选择界面将显示在每一行数据的前面。

对于地图组件( *dcc.graph()*),你可以发现它的id是‘数据表-交互-地图’(line 519),而带有‘数据表-交互-位置’id的数据表组件( line529 )。这是回调中使用的两个 id。我喜欢使用有意义的 id 名称,这有助于将来维护代码。

好了,app 端的一切都准备好了,现在让我们进入回调部分。在我们的例子中,作为Input cargo,我们有derived_virtual_selected_rows ( 这不是一个任意的名字,它是一个从数据表中选择 *dash_table.DataTable* 组件的行的索引列表),cargo 然后将传递给函数为dcc.graph()返回一个figure属性。

因此,如图 4 所示,我们给OutputInput分配了相应的‘id ’,同时,标记了正确的属性以便回调到 monitor。注意Input必须在列表中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4 |回调装饰器

最后,让我们完成功能方面。我们想要的交互性是让用户从数据表中选择一行,地图将自动移动到和放大所选的国家/地区。此外,在应用程序开始时,初始中心点和缩放级别应该显示整个世界地图。

第一次启动应用程序时,将调用回调及其函数。然而,当第一次呈现应用程序时,没有选择任何行,因此derived_virtual_selected_rows将是None。这可能会导致依赖于derived_virtual_selected_rows的函数出现问题。这就是为什么你可以从第 590 行到第 591 行(图 5)看到,在函数的开始,我们将derived_virtual_selected_rows设置为一个空列表。一旦用户做出选择,数据表中被选择行的索引将被存储在derived_virtual_selected_rows中。由于我们只启用了单一选择,它将只包含一个项目。为了访问这个索引,我们只需要调用derived_virtual_selected_rows[0]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5 |回调包装的函数

通过使用go.layout.mapbox.Center()(图 5,line 639–641,我们可以指定地图应该居中的坐标及其放大级别。因此,当应用程序第一次启动并且derived_virtual_selected_rows是一个空列表时,我们将使用预定义的坐标和放大级别。一旦derived_virtual_selected_rows内有选择,callback 会用相应的坐标和放大级别更新地图。

lat=3.684188 if len(derived_virtual_selected_rows)==0 else **dff['lat'][derived_virtual_selected_rows[0]]**
lon=148.374024 if len(derived_virtual_selected_rows)==0 else **dff['lon'][derived_virtual_selected_rows[0]]**zoom=1.2 if len(derived_virtual_selected_rows)==0 else **4**

万岁,我们现在使用回调为仪表板添加两种类型的交互性。我希望你觉得这篇文章很有用,并且现在对 dash 中的回调有了一个想法。

以下是我用来学习破折号回调的列表:

和往常一样,我欢迎反馈、建设性的批评以及听到关于您的数据科学项目的信息。你可以在 Linkedin 上找到我,也可以在我的网站上找到我。

提升您的 Jupyter 笔记本电脑环境体验

原文:https://towardsdatascience.com/elevate-your-jupyter-notebook-environment-experience-9bdd1101aa54?source=collection_archive---------14-----------------------

随后,让你喜欢在 Jupyter 环境中工作

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

香草形式的 Jupyter 笔记本

Jupyter 笔记本是我这个数据科学家不可分割的一部分。Jupyter 笔记本能够在不同的单元格中测试我的代码,立即显示我的数字,甚至以 markdown 的形式编写必要的解释;这是任何其他 IDE 都没有的体验(或者据我所知)。

虽然我说了这些,但我意识到 Jupyter Notebook 更多的是一种探索和测试工具,而不是生产工具。我也知道使用它们的默认形式会很痛苦,特别是对于来自其他 IDE 的人来说。然而,我想展示一些我喜欢的 Jupyter 笔记本的功能,以提升我们使用 Jupyter 笔记本的体验。只需注意一点,这里的大部分扩展只有在假设您的 jupyter 笔记本使用的语言是 Python 的情况下才能正常工作。

这里我创建了文章的第 2 部分,作为提升您的 Jupyter 笔记本体验的附加文章。

降价和乳胶

对于那些不知道 Markdown 是什么的人来说,它是一种在纯文本文档中添加格式化元素的语言。Markdown 让你可以灵活地将你的纯文本转换成更有趣的文本(例如嵌入链接、图像甚至视频)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

降价是如何运作的

要使用降价,我们需要将单元格切换到降价模式。我们只需要从下拉列表中选择降价选项。如果你想学习 markdown 格式中使用的所有命令,你可以在这里学习

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我喜欢 Jupyter Notebook 中的 Markdown 的地方不是格式部分,而是它如何在他们的单元中实现 LaTeX。LaTeX 也是一种纯文本格式语言。具体来说,它通常用于复杂的数学表达式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过 Markdown 单元中的 LatEx 得出的标准偏差方程

就像 Markdown 一样,LaTeX 也有自己的规则。如果你想了解更多,这里有 LaTeX 文档的开源指南。

编程语言扩展

当我们通过 Anaconda 安装 jupyter 笔记本时,我们只会使用 Python 作为我们的工作语言。这就产生了一个假设,jupyter 笔记本只能与 Python 语言一起工作,如果我们想与另一种语言如 R 或 Julia 一起工作,我们应该使用另一个 IDE。这完全不是事实;我们可以在 jupyter 环境中实现其他语言。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jupyter 笔记本内核选择

例如,我们可以为 jupyter 笔记本环境嵌入一个额外的扩展,以便与 R 或 Julia 编程语言一起工作。

1.r 笔记本

要在我们的 jupyter 笔记本中包含 R 笔记本,我们需要遵循以下步骤:

  1. 装 R;在 jupyter 笔记本环境中设置 R 之前,我们需要有自己的 R 编程语言。你可以在这里下载
  2. 安装伊尔克内尔;IRkernel 是一个 jupyter 内核,用于处理 jupyter 笔记本中的 R 语言。您可以通过 R 控制台输入install.packages('IRkernel')来安装 IRkernel。
  3. 启用 IRKernel;为了让 jupyter 看到新安装的 IRkernel,我们需要通过 R 控制台输入IRkernel::installspec(user = FALSE)来启用它。

现在,我们可以在 jupyter 笔记本中使用 R 编程语言,方法是在创建新笔记本时选择 R。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Jupyter 的 r

2.朱莉娅笔记本

朱莉娅怎么样?是的,我们还可以在 jupyter 笔记本中设置 Julia 语言。我们只需要遵循与上面类似的步骤。

  1. 安装茱莉亚;就像 R 一样,我们需要下载 Julia 语言作为第一步。你可以在这里下载朱莉娅的作品。
  2. 安装 I Julia;IJulia 相当于 r 中的 IRkernel。我们通过在 Julia 命令行中键入以下行来安装 IJulia:
using Pkg
Pkg.add("IJulia")

就这样,我们现在可以在 jupyter 笔记本上使用 Julia 语言了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

朱庇特的朱莉娅

Jupyter 笔记本扩展

Jupyter 笔记本包含一个附件或扩展,以提高我们的生产力。 Will Koehrsen 已经就如何启用这个扩展创建了一个很棒的帖子,但是在这里我将总结安装部分并展示一些我最喜欢的扩展。

要为 Python 环境启用我们的 Jupyter 笔记本扩展,我们只需在终端中运行以下命令:

pip install jupyter_contrib_nbextensions && jupyter contrib nbextension install

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jupyter 扩展选项卡

打开标签会给我们提供一个很好的扩展选项,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有许多扩展选项,其有用性取决于我们正在做什么样的工作,但我会展示一些我经常使用的。

1.执行时间

我发现执行时间扩展很有用,因为我经常测试各种代码组合,以找到运行速度最快的代码。每次我们运行当前单元格时,扩展都会出现,并且会一直保持在那里,直到我们再次运行单元格或重置笔记本。下面我用一个例子来展示一下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

执行时间延长

当我们运行完代码后,这个扩展将为我们提供代码执行的运行时间和结束时间。

2.变量检查器

人们不喜欢使用 Jupyter Notebook 的原因之一是他们缺少另一个 IDE 提供的变量检查器。对于这种情况,我们有另一个名为变量检查器的扩展。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

变量检查器如何工作

顾名思义,这些扩展将创建一个额外的实例来显示 jupyter 笔记本环境中的所有变量。

3.代码美化

只需按一下按钮,这个扩展就会重新构建我们的代码。我经常在漫长的编码过程后使用这个扩展来创建一行可读性更好、更整洁的代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个扩展并不总是像我们希望的那样工作。例如,如果我们已经有了一个简洁的线条,那么它根本不会改变我们的线条。

4.便条簿

这个扩展将创建一个独立的环境,就像我们书中的便签簿。如果我们需要一个地方进行代码实验,但又不想中断我们当前的 jupyter 环境,Scratchpad 是一个非常有用的扩展。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

草稿栏示例,红色圆圈表示弹出草稿栏的位置

支持包

已经开发了许多模块来创建一种更加无缝和交互式的方式来在 Jupyter 环境中处理我们的数据。随着时间的推移,我确信这个模块列表会被开发得更多,但是这里我将展示一些我日常使用的包。

1.进度条

大多数时候,我们花了很多时间做循环过程,但我们甚至不知道什么时候能完成。此外,我们想知道我们的循环是否运行正常。

在这种情况下,我们可以使用 tqdm 模块提供的进度条。下面是我们 jupyter 笔记本中进度条的一个例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

循环会话期间的进度条

我们在各自的环境中使用 pip 或 conda 来安装 tqdm 包。

pip install tqdm

下面是如何在我们的 jupyter 笔记本中添加进度条的代码示例。

#Import the module
from tqdm import tqdm#Creating the list for looping purposes
my_list = list(range(10000000))#Calling the progress bar in the jupyter noteboook
with tqdm(total=len(my_list)) as pbar:
    for x in my_list:
        pbar.update(1) #use this code to move our progress bar

我们通过调用带有 total 参数的 tqdm 来调用进度条,该参数接受迭代发生的次数。要移动进度条,在每次迭代中,我们需要使用 pbar.update()方法更新进度条,该方法接受增量(通常是 1)。

2.小工具

当我们努力探索我们的数据时,有时仅仅为了显示特定的情节而不断编辑我们的代码会是一件麻烦的事情。这肯定会阻碍我们的生产力,甚至浪费我们一些宝贵的时间。

在这里,我将展示一个交互式控件的示例,其目的是通过使用 ipywidgets 模块来更改我们的输入,而无需额外的代码编辑。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ipywidgets 如何在不编辑代码的情况下产生各种各样的情节

首先,我们通过使用通常的 pip 或 conda 来安装我们的模块。

pip install ipywidgets

然后,我们通过在命令提示符下键入以下代码来启用 ipywidget 模块:

jupyter nbextension enable --py widgetsnbextension

有了扩展,我们现在可以在 jupyter 笔记本中激活交互控制。这是我上面的 GIF 中的代码示例。

#Import all the important module
import matplotlib.pyplot as plt
import seaborn as sns
from ipywidgets import interact#load the example dataset
titanic = sns.load_dataset('titanic')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

泰坦尼克号数据集

假设我有兴趣查看按数据集中每个分类组分组的票价的平均值。我们可以一个一个地过滤数据,但是这将花费很多时间,并且降低我们的生产率。因此,我们可以使用交互控件。

#Creating the interactive control@interact
def create_fare_plot(col = titanic.drop(['fare', 'age'], axis =1).columns):
    sns.barplot(data = titanic, x = col, y ='fare')
    plt.title(f'Mean Bar Plot of the Fare grouped by the {col}')

我们用“@interact”代码行启动交互,然后用 def 语句创建我们想要的交互控件。在这里,interact 小部件将为我们提供下拉列表,其中包含我们在 def 参数中输入的选项。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

包含所有分类变量的下拉列表,用于在绘图之间切换

结论

以上是一些提升我们在 jupyter 笔记本上的体验的例子。我知道 jupyter 笔记本不会为了生产目的而改变 IDE,比如 Visual Studio 代码,但是我试图展示如何为了数据科学家的工作目的而改进 jupyter 环境。

如果您喜欢我的内容,并希望获得更多关于数据或数据科学家日常生活的深入知识,请考虑在此订阅我的简讯。

如果您没有订阅为中等会员,请考虑通过我的介绍订阅。

用 Splinter 提升你的网络抓取

原文:https://towardsdatascience.com/elevate-your-webscraping-with-splinter-a926eee7f7d9?source=collection_archive---------10-----------------------

按目标

通过自动化与网页的交互,从您的抓取中获得更好的数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像-像素

在以前的博客中,我使用两个包的组合探索了网络抓取的基础;请求,它获取一个网站的 HTML 和 BeautifulSoup4 ,它解释那个 HTML。

[## 今日汤

美汤网刮——初学者指南

towardsdatascience.com](/soup-of-the-day-97d71e6c07ec)

这些包很好地介绍了 webscraping,但是 Requests 有局限性,尤其是当您想要抓取的站点需要大量用户交互时。

提醒一下——Requests 是一个 Python 包,它将 URL 作为参数,并在第一次跟踪该 URL 时返回立即可用的 HTML。因此,如果你的目标网站只是在特定的交互之后才加载内容(比如滚动到页面底部或者点击一个按钮),那么请求就不是一个合适的解决方案。

对于我在我的目标系列中记录的 FPL 项目,我需要从英超联赛网站的结果页面中收集比赛数据。这些对请求提出了一些挑战。例如,我想要每个比赛页面上的“阵容”和“统计”标签中的数据,但是,这些数据不会加载新网页。相反,它们触发 JavaScript 事件,在同一个页面中加载新的 HTML。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我还想抓取比赛评论,可以通过向下滚动页面进入…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

…但只有当用户继续向下滚动时才会完全加载(类似于脸书和 Reddit 等“无限滚动”网站)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用碎片刮擦

我们可以使用一个名为 Splinter 的 Python 包来代替请求抓取。Splinter 是其他浏览器自动化工具之上的一个抽象层,比如 Selenium,它保持了良好的用户友好性。此外,一旦我们用 Splinter 抓取了 HTML,BeautifulSoup4 可以从其中提取数据,就像我们使用请求一样。

首先,我们需要为我们想要使用的浏览器下载合适的浏览器“驱动程序”。对于 Firefox 来说,这意味着使用 Mozilla 的 geckodriver (注意——Splinter 默认使用 Firefox)。如果你用的是 Chrome,那么你需要 chromedriver

您还需要使用您机器的终端 pip 安装 selenium(完整的细节包含在 Splinter 文档中):

$ [sudo] pip install selenium

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

同样值得注意的是,驱动文件本身(即 geckodriver 或 chromedriver)需要包含在您的 repo 的根目录中(这在 Splinter 或 Firefox 文档中都不是一个明显的要求!)

Splinter 的工作原理是实例化一个“浏览器”对象(如果你愿意,它会在你的桌面上启动一个新的浏览器窗口)。然后,我们可以在 Jupyter Notebook 上运行方法,与浏览器进行交互。

from splinter import Browser***#State the location of your driver***
executable_path = {"executable_path": "/Users/Callum/Downloads/geckodriver"}***#Instantiate a browser object as follows...
#Pass 'headless=False' to make Firefox launch a visible window*** browser = Browser("firefox", **executable_path, headless=False)

这将启动一个空的浏览器窗口。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意橙色条纹的地址栏,它告诉我们它是由我们的 Python 文件控制的

我们现在可以用一些 Python 命令来控制这个浏览器。首先,让它访问一个网页…

match_url = 'https://www.premierleague.com/match/46862'browser.visit(match_url)

查看我们桌面上的浏览器窗口,我们可以看到这已经工作了!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

与元素交互

现在我们已经加载了网站,让我们来解决请求无法处理的两个问题。首先,我们想点击“阵容”和“统计”标签。

为此,我们首先需要了解这些元素在 HTML 中是如何被引用的。右键单击按钮,并选择“检查元素”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以在检查器中找到合适的 HTML。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以按钮是一个有序列表元素

  • ,带有类“matchcentresquantlabelcontainer”。

Splinter 可以用。find_by_tag()方法。然后我们可以用点击这个按钮。点击()方法。

target = ‘li[class=”matchCentreSquadLabelContainer”]’browser.find_by_tag(target).click()

注意 Splinter 可以使用六种不同的东西来查找元素。这些在这里有完整的文档,但是包括通过元素 ID、CSS 或值进行查找的选项。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在浏览器已经‘点击’了我们想要的标签,我们可以使用 BeautifulSoup4 来获取和存储 HTML。

from bs4 import BeautifulSoup
html = BeautifulSoup(browser.html, 'html.parser')

然后,我们可以使用我之前的博客中记录的相同技术提取我们想要的文本信息。

当然,如果我们还想单击“Stats”选项卡,我们会执行相同的过程—使用 Inspect Element 工具检查该选项卡是如何被调用的,然后在使用 BeautifulSoup 提取 HTML 之前,在. find_by_tag()方法中传递它。

解决无限滚动问题

虽然 Splinter 的 Browser 类没有内置的“滚动”方法,但它有一个更强大的功能让我们可以做到这一点——也就是说,我们可以使用。execute_script()方法运行 JavaScript。

为了让浏览器滚动到当前加载页面的底部,我们使用了 JavaScript scrollTo()方法。它将 xy 位置作为它的参数,所以为了到达页面的末尾,我们将‘document . body . scroll height’作为 y 位置(我们不需要改变 x 位置,因为我们只是在垂直方向上滚动)。

因此,我们运行:

browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")

可能的情况是,在我们需要的所有东西加载之前,我们需要继续滚动。由于不同的比赛会有不同数量的事件,我们将需要在每一页上做不同数量的滚动。因此,我们需要某种条件来告诉我们的代码停止滚动。

令人高兴的是,英超网站上的评论总是以“阵容公布,球员热身”这句话开始。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,如果我们抓取的 HTML 包含这个短语,那么我们知道我们已经得到了我们需要的所有评论,我们可以停止滚动。这是一个适合“while 循环”的任务。

***#Declare the JavaScript that scrolls to the end of the page...***
scrollJS = "window.scrollTo(0, document.body.scrollHeight);"***#...and a variable that signifies the loop's end-condition***
condition = "Lineups are announced and players are warming up."***#Instantiate a 'first_content' variable (an empty string for now)*** first_content = ""***#Create a while loop that runs when 'first content'
#is not equal to our break condition***
while first_content != condition: ***#Scroll to the bottom of the page***
    browser.execute_script(scrollJS) ***#Use BS4 to get the HTML***
    soup = BeautifulSoup(browser.html, 'html.parser') ***#Store the first line of commentary displayed on the page as-is***
    first_content = soup.findAll('div',class_="innerContent")[-1].get_text() **#Scroll down again, and run the loop again if we
    #haven't reached the line "Lineups are announced..."**
    browser.execute_script(scrollJS)***#Store the soup that, thanks to the while loop, will
#definitely contain all of the commentary***
HTML = soup

然后,我们可以重构上面的所有内容,创建一个函数来接收匹配的 URL,并返回来自比赛评论的所有 HTML,以及来自阵容和统计选项卡的数据。因此,我们可以自动抓取本赛季到目前为止的所有比赛页面。当然,我们如何组织、存储和操纵这些数据完全是另一项任务(事实上,也是另一篇即将发表的博客的主题)。

值得一提的是,就 Splinter 的功能而言,这只是冰山一角。其他有趣的使用案例包括:

无论如何,Splinter 是一个很棒的小 Python 包,它将帮助你把你的网络抓取提升到一个新的水平!试一试,看看你有什么想法。

这是我的博客系列“目标”中的最新一篇文章,在这篇文章中,我将尝试构建一个“摇钱树”梦幻英超联赛的模型。我很乐意听到关于这个博客的任何评论,或者这篇文章涉及的任何概念。欢迎在下面留言,或者通过 LinkedIn 联系我。

ELMo:为什么这是 NLP 最大的进步之一

原文:https://towardsdatascience.com/elmo-why-its-one-of-the-biggest-advancements-in-nlp-7911161d44be?source=collection_archive---------19-----------------------

语言模型嵌入(ELMo)是一种先进的语言建模思想。是什么让它如此成功?

2018 年发布的“深度语境化单词嵌入”提出了从语言模型嵌入(ELMo)的想法,在许多热门任务上实现了最先进的性能,包括问答、情感分析和命名实体提取。ELMo 已经被证明可以提高近 5%的性能。但是是什么让这个想法如此具有革命性呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创作的《Elmo 近距离》在 2.0 的 CC 下授权

ELMo 是什么?ELMo 不仅是一个提线木偶,还是一个强大的计算模型,可以将单词转换成数字。这一重要过程允许机器学习模型(接受数字,而不是单词作为输入)根据文本数据进行训练。

**ELMo 为什么这么好?**当我通读原文时,有几个要点引起了我的注意:

  1. ELMo 说明了一个单词的上下文。
  2. ELMo 是在大型文本语料库上训练的。
  3. ELMo 是开源的。

让我们详细讨论这些要点,并谈谈它们为什么重要。

**#1: ELMo 可以唯一说明一个单词的上下文。**以前的语言模型,如 GloVe、Bag of Words 和 Word2Vec,只是基于单词的字面拼写生成嵌入。他们不考虑这个词的用法。例如,在以下示例中,这些语言模型将为“信任”返回相同的嵌入:

我不能信任你。

他们不再信任他们的朋友。

他有一个信托基金。

然而,ELMo 根据周围的单词为同一个单词返回不同的嵌入——它的嵌入是上下文相关的。在这些例子中,它实际上会为“信任”返回不同的答案,因为它会识别出这个词在不同的上下文中使用。这种独特的能力本质上意味着 ELMo 的嵌入有更多的可用信息,因此性能可能会提高。一个类似的考虑上下文的语言建模方法是 BERT

埃尔莫接受了大量数据的训练。无论你是一名资深的机器学习研究人员还是一名不经意的观察者,你可能都熟悉大数据的力量。最初的 ELMo 模型是在 55 亿词的语料库上训练的,即使是“小”版本也有 10 亿词的训练集。数据真多啊!在如此多的数据上接受训练意味着 ELMo 学到了大量的语言知识,并将在广泛的数据集上表现良好。

**#3:任何人都可以使用 ELMo!**推动机器学习作为一个领域发展的最重要因素之一是开源研究的文化。通过开源代码和数据集,研究人员可以让该领域的其他人轻松应用和建立现有的想法。按照这种文化,ELMo 是广泛开源的。它有一个网站,不仅包括它的基本信息,还包括该型号的小型、中型和原始版本的下载链接。希望使用 ELMo 的人绝对应该访问这个网站,快速获得该模型的副本。此外,代码发布在 GitHub 上,包括一个非常详尽的自述文件,让用户知道如何使用 ELMo。如果有人花了几个多小时才让一个 ELMo 模型运行起来,我会感到很惊讶。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ELMo 在著名的任务上实现了最先进的性能,例如 SQuAD、NER 和 SST。图片来源于 Peters 等人,ELMo 论文的原始作者。

ELMo 是上下文感知单词嵌入和大数据的强大组合,在 NLP 的大数据集上取得了一流的性能,包括 SQuAD、NER 和 SST。ELMo 彻底改变了我们处理计算语言学任务的方式,如问答和情感检测,这显然是该领域的一个关键进步,因为它被引用了 4500 多次。此外,向计算语言学协会(ACL)会议(最大的国际 NLP 会议)提交的论文在 ELMo 发表后翻了一番,从 2018 年的 1,544 篇论文增加到 2019 年的 2,905 篇论文(尽管这也可能归因于 2019 年初 BERT 的发表)。

我还想指出,埃尔默和伯特非常相似,因为他们都来自芝麻街!好吧,它们都是语言模型,说明了一个单词的上下文,在一个大的数据集上训练,并且正如我们所知,正在彻底改变自然语言处理领域。(我也写了一篇关于 BERT 的博文,如果你感兴趣,可以在这里找到)。

ELMo 是 NLP 中最大的进步之一,因为它本质上是第一个关注上下文的语言模型,允许在多个任务中实现更好的性能。

延伸阅读:

Elo 商家类别推荐——机器学习案例研究

原文:https://towardsdatascience.com/elo-merchant-category-recommendation-a-case-study-33e84b8465c7?source=collection_archive---------28-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由弗雷姆基·查马基拍摄

在当今的现代,机器学习几乎涉及到我们生活的方方面面。从提供电影和产品推荐这样简单的事情到利用商业数据为组织推断和做出商业决策这样复杂的事情,机器学习和人工智能在过去几十年里已经走过了漫长的道路。今天,我将讨论一个关于 Elo Merchant 类别推荐的例子——一个我喜欢从事的机器学习(ML)案例研究。

案例研究概述:

案例研究方法分为以下步骤:

  • 商业问题
  • ML 问题公式化
  • 探索性数据分析
  • 特征工程
  • 特征关联和特征选择
  • 回归模型
  • 结论和结果

商业问题

Elo 是巴西最大的支付品牌之一。它们为用户提供餐馆推荐以及基于用户的信用卡提供商和餐馆偏好的折扣。Elo 与各种品牌建立了合作关系,为用户提供来自不同商家的促销和折扣。

现在,我们手头的问题是找出这些促销对商家以及用户(顾客)有多大的用处和益处。我们需要弄清楚顾客是否真的使用了提供给他们的这些促销或折扣。这可以通过预测一个称为客户忠诚度得分的指标来实现,该指标是目标变量。因此每张卡都有相应的预测忠诚度得分。

客户忠诚度得分将让我们了解用户/客户使用这些促销和折扣的频率。有了预测数据,公司(Elo)现在可以专注于更忠诚的客户。这意味着他们可以将他们的营销努力指向这些忠诚的客户。这也将确保 Elo 减少针对预计客户忠诚度较低的客户的不必要的营销活动。这将最终提高客户保持率。

ML 问题公式化

尽管顾名思义,这是一个回归问题,因为对于给定的卡 ID,我们需要根据卡的交易和商户数据来预测它的忠诚度得分。因此,我们将根据这些数据生成的特征来训练我们的回归模型。然后,这些回归模型将根据生成的特征预测忠诚度得分。

数据概述

所有数据都是模拟的和虚构的,并不是真实的客户数据,原因很明显。所提供的数据包含每个卡 ID 长达 3 个月的交易数据。它还包含基于这些交易中涉及的商家的商家数据。还有一个附加文件,其中包含另外 2 个月的所有购买/交易数据,这些数据不包括在 3 个月的初始交易数据中。以下是每个文件的详细描述:

  1. Data_Dictionary.xlsx →该文件包含每个 csv 文件的数据字段描述。
  2. train.csv 和 test.csv →这些文件包含卡 id(Card _ id)和关于卡的信息。它们还包含需要预测的目标变量(忠诚度得分)。下面是对每个列的描述:
*card_id* → Unique card identifier
*first_active_month* → month of first purchase in 'YYYY-MM' format
*feature_1* → Anonymized card categorical feature
*feature_2*→ Anonymized card categorical feature
*feature_3* → Anonymized card categorical feature
*target* → Loyalty numerical score calculated 2 months after historical and evaluation period
  1. historical_transactions.csv 和 new _ merchant _ transactions . CSV→这些文件包含交易数据。它们包含每张卡的交易信息。下面是对每个列的描述:
*card_id* → Card identifier
*month_lag* → month lag to reference date
*purchase_date* → Purchase date
*authorized_flag* → 'Y' if approved, 'N' if denied
*category_3* → anonymized category
*installments* → number of installments of purchase
*category_1* → anonymized category
*merchant_category_id* → Merchant category identifier(anonymized)
*subsector_id* → Merchant category group identifier(anonymized)
*merchant_id* → Merchant identifier(anonymized)
*purchase_amount* → Normalized purchase amount
*city_id* → City identifier(anonymized)
*state_id* → State identifier (anonymized)
*category_2* → anonymized category
  1. merchants.csv →该文件包含交易中涉及的商家的附加信息。下面是对每个列的描述:
*merchant_id* → Unique merchant identifier
*merchant_group_id* → Merchant group(anonymized)
*merchant_category_id* → Unique identifier for merchant category (anonymized)
*subsector_id* → Merchant category group (anonymized)
*numerical_1* → anonymized measure
numerical_2 → anonymized measure
*category_1* → anonymized category
*category_2* → anonymized category
*category_4* → anonymized category
*city_id* → City identifier(anonymized)*most_recent_sales_range* → Range of revenue (monetary units) in last active month (A > B > C > D > E) *most_recent_sales_range* → Range of revenue (monetary units) in last active month (A > B > C > D > E)
*most_recent_purchases_range* → Range of quantity of transactions in last active month (A > B > C > D > E)*avg_sales_lag3* → Monthly average of revenue in last 3 months divided by revenue in last active month
*avg_purchases_lag3* → Monthly average of transactions in last 3 months divided by transactions in last active month
*active_months_lag3* → Quantity of active months within last 3 months*avg_sales_lag6* → Monthly average of revenue in last 6 months divided by revenue in last active month
*avg_purchases_lag6* → Monthly average of transactions in last 6 months divided by transactions in last active month
*active_months_lag6* → Quantity of active months within last 6 months*avg_sales_lag12* → Monthly average of revenue in last 12 months divided by revenue in last active month
*avg_purchases_lag12* → Monthly average of transactions in last 12 months divided by transactions in last active month
*active_months_lag12* → Quantity of active months within last 12 months

所有的数据文件都可以从这个链接下载。

绩效指标

为了根据实际忠诚度得分计算预测误差,我们将使用的性能指标是 RMSE(均方根误差)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

这里,ŷ是预测的忠诚度得分,y 是每个卡 ID 的实际忠诚度得分。

探索性数据分析

培训和测试文件

  1. 目标变量分布:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

目标变量分布(作者图片)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

大多数忠诚度得分在-1 和 1 之间。同样,这些都以零为中心。所以有可能这些已经标准化了。

正如我们所见,有些点离所有的点都很远。这些人的忠诚度低于-30。因为这些点构成了数据的 1%,所以它们不能被称为异常值。这完全取决于这些点是否出现在我们的测试数据中。正如我们在后面的阶段会知道的,我们在测试数据中确实有这些点。所以,我们称它们为稀有数据点。

2。分类特征

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

非稀有数据点的分类特征分布(图片由作者提供)

让我们来看看这些稀有数据点的分类特征…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

稀有数据点的分类特征分布(图片由作者提供)

对于稀有和非稀有数据点,分类特征 1、2 和 3 之间没有太大差异。从历史交易和商家交易中提取特征可能有助于更好地预测这些罕见的忠诚度得分

3。第一个活跃月份

因为这是以“YYYY-MM”格式给出的。让我们把它转换成一个简单的度量,比如从今天算起的月差。在 pandas 中使用 datetime 实现这一点非常简单。

现在,我们可以根据忠诚度得分(目标)来绘制这个图表…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

忠诚度得分与第一个活跃月的月差散点图(图片由作者提供)

  • 我们可以在这里观察到一种趋势,即最近的用户具有更高的忠诚度得分以及忠诚度得分的高方差。
  • 然而,这也表明,与长期用户相比,近期用户的数量更多。
  • 一个更重要的观察结果是忠诚度得分为<=-30(that might be outliers) are at the very bottom of the plot for each of the bin/value ranges of the active month difference from today. This shows that this feature might not be useful enough to separate our rare data points from the actual data.
  • This feature would definitely help in predicting the loyalty scores.

Historical Transactions

  1. 授权标志的用户数量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

正如应该的那样,大多数交易都是经过授权的。

2.分期付款

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分期付款分布(作者图片)

对于分期付款来说,值-1 和 999 似乎有些奇怪。如果需要的话,我们以后可能需要修剪这些。

3.购买金额

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

似乎这些也已经标准化了。

新商户交易

据观察,该文件不包含任何未经授权的交易。

分期付款和购买金额的分布与我们在历史交易中观察到的非常相似。

根据忠诚度得分绘制购买金额和分期付款

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分期付款与忠诚度得分(目标)的散点图(图片由作者提供)

由于目标分数似乎是标准化的(在某个范围内),观察到具有较高分期付款次数的人具有更接近于零的忠诚度分数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

  • 正如我们在这里看到的,忠诚度得分随着交易价值总额的增加而增加。
  • 对于每个 card_id 进行的交易数量也观察到相同的趋势。

商户数据

该文件的列中有许多缺失值和无穷大值。无穷大值被替换为空值。然后,相应地使用均值和众数插补来处理这些缺失值。

  1. 匿名类别

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

  • 因为这三个特征都是匿名的,所以我们不能从这些情节中说太多关于商人的事情,尽管从这些情节中可以解释很多。
  • 然而,可以进一步探索和检查,如果交易中涉及的商家属于多数类别,那么它可能导致什么样的忠诚度得分。

2.活跃月滞后

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

  • 正如我们在这里所观察到的,活动月数较少的卡忠诚度较低。
  • 我们可以在这里看到很多差异。这些活跃月份列可能是有用的特性。

3.平均购买量滞后

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

  • 对于我们的稀有数据点(忠诚度得分<=-30), the purchase range lag is less than 2000.
  • This can be a very useful feature as well for predicting the loyalty scores.

EDA Summary:

  1. The target variable(Loyalty score) has 1% of its points that seem like outliers. Dealing with these points depends on the number of these points in the test data. In that case, these aren’t actually outliers. They can be termed as Rare data points. However, the loyalty score would be difficult to predict for these points and they could have a major impact on our final scores.
  2. The first active month in train file could be very useful.
  3. Aggregated transaction features for each card would be very helpful in predicting the loyalty score using the regression models. Most of our features would be from the transaction files.
  4. Some features can be used from the merchants file as well. For example, we can have a categorical variable that states if the transaction involved a merchant that was active in the last 3/6 or 12 months. The average purchases and sales ranges might be useful as well.

Data Preprocessing

Data Preprocessing included imputing the missing values with either mean and mode for categorical and continuous variables respectively.

In case of Purchase amounts, this issue was handled by trimming the purchase amount values in a specific range that covered up to 99.9 percentile of its values.

As observed earlier, the installments values such as -1 and 999 were replaced with nan(null value) and imputed accordingly.

historical_transactions['purchase_amount'] = historical_transactions['purchase_amount'].apply(**lambda** x: min(x, 0.8))
historical_transactions['installments'].replace([-1, 999], np.nan, inplace=**True**)
historical_transactions['installments'].fillna(historical_transactions['installments'].mode()[0], inplace=**True**)

Feature Engineering

The Feature Engineering for this problem was done in two iterations. In the 第一次迭代,两个事务文件都被合并到一个包含所有事务的文件中,并且使用这个合并文件生成特征。从这些特征获得的 RMSE 分数不够好。

因此,我不得不回到特征工程,分别从这两个文件中提取特征。与此同时,在第二次迭代中也产生了额外的特性。这将 RMSE 分数提高了约 0.35。

处理日期功能

purchase_date 和 first_active_month 在生成额外的特性方面非常有用,例如一年中的季度和从今天开始的月份差异。在 Pandas 中使用 datetime 可以很容易地做到这一点。

类似地,对于 purchase_date,我们生成了指示购买是在周末、假日还是工作日进行的特性。这还包括诸如月、年、日、时间和购买时间等特征。

处理分类特征

两种方法用于分类特征:

  1. 一个热编码
  2. 平均编码

在第一次迭代中使用了一个热编码。然而,在特征工程的第二次迭代之后,均值编码提供了更好的结果。分类变量的均值编码是基于相应目标变量的值是否小于-30(是稀有数据点)来完成的。这样做是为了改进对稀有数据点的预测。

使用基于变量的平均值、最大值、最小值、var、偏斜和 n_unique/count,通过“card_id”聚合来自交易的特征,以最终与训练和测试文件结合。

使用现有的特性和可用的领域专业知识生成了更多的特性。其中一些如下所示:

最终总共生成了 226 个特征。

特征关联和特征选择:

由于我们已经生成了近 200 个特征,相关矩阵看起来像这样…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

相关矩阵(图片由作者提供)

呀!!这看起来不太好。

很难从中解读出什么,因为我们有大量的特征。因此,使用相关矩阵采取了一种稍微不同的方法。

阈值设置为 0.85,并且移除了共线性高于阈值的特征对中的一个特征。这种方法将特性的数量从 226 个减少到了 134 个。

回归模型

基线回归模型

初始模型使用 RandomizedSearchCV 和 GridSearchCV 进行调整,并根据训练测试拆分后获得的训练数据进行训练。

线性回归:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

线性回归(作者图片)

新币回归量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

新币回归(图片由作者提供)

随机森林回归量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机森林回归器(图片由作者提供)

LGBM 回归量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

LGBM 回归器(图片由作者提供)

由于 RandomizedSearchCV 没有提供最好的结果,贝叶斯优化被用于超调 LGBM 模型

LGBM 模型在全套 226 个特征上比 134 个特征表现得更好。对此的可能原因可能是树和基于树的集合模型受相关特征的影响不大。

带 LGBM Goss 的贝叶斯岭回归堆叠模型

预测是使用 LGBM Goss 模型进行的,该模型根据训练数据进行分层折叠和重复折叠训练。在这里,不需要进行训练测试分割,因为我们是根据训练数据进行折叠预测的。因此,可以使用整个训练数据来训练模型。

这两个预测叠加在一起,作为贝叶斯岭回归模型(元模型)的输入。

低概率和高概率模型方法

因为,稀有数据点严重影响了异常值。我尝试了下面的模型架构来处理这个问题…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由叶夫根尼·帕特卡

  1. **二元分类模型:**该分类模型识别稀有数据点。这是一个 LGBM 分类器模型。基于从模型获得的分类概率,使用阈值(超参数)将卡/用户分类为稀有数据点(异常值)。
  2. **回归(完整)😗*这是一个简单的回归模型,在整个数据集上进行训练。这是对整个数据进行训练的堆叠贝叶斯岭回归模型。
  3. **回归(低概率模型)😗*在稀有数据点(异常值)的低浓度(超参数)上训练的回归模型。这也是一个 LGBM 回归模型训练的所有功能。
  4. **回归(高概率模型)😗*用高浓度(超参数)异常值训练的回归模型。我们只有非常少的稀有数据点,所以模型很容易过度拟合。因此,这是一个简单的回归模型(贝叶斯岭回归)。基于从用于训练的完全回归模型获得的特征重要性,使用来自完全回归模型、二元分类的预测以及 10 个最重要的回归特征。
  5. **完全(混合)模型:**混合来自高概率模型、低概率模型和完全回归模型的预测,以给出最终的忠诚度得分预测。这里的最终元模型也是贝叶斯岭回归模型。

结果

下表总结了上述所有方法的性能:

基线回归模型:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

回归模型结果(图片由作者提供)

堆叠和 LGBM Goss 模型结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

RMSE 为提到的不同方法打分(图片由作者提供)

堆叠贝叶斯岭模型提供了最好的 Kaggle 评分。因此,这是用于最终提交。

Kaggle 提交

这是我提交的最好的 Kaggle 分数…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

总结

  1. 在这个问题上,特征工程至关重要。从交易和商家数据中生成的特征越好,RMSE 分数就越好。
  2. 处理异常值/稀有数据点是本案例研究的关键。这些分数极大地影响了最终的 RMSE 分数。
  3. 在尝试了不同的回归模型和堆叠架构后,LGBM Goss 模型堆叠在贝叶斯岭回归上作为元模型,提供了最佳结果。

未来的工作/改进

  1. 使用目标编码生成更多特征有助于提高分数。
  2. 高和低概率模型方法可以进一步优化,增加更多功能,并尝试高和低概率模型。正如这里的所解释的那样,Evgeny Patekha 在其第五名的解决方案中成功实现了这一点。
  3. 大多数分类和数字特征都是匿名的。因此,我们对这些特征没有明确的概念。这使得基于领域专业知识生成新特性变得困难。然而,这种限制/约束是为了避免泄露用户/公司数据。

结论

在这个案例研究中,我学到并改进了很多技巧。诸如特征工程和模型超调之类的技能对这个案例研究非常重要。这也是我第一次使用贝叶斯优化来超调模型。在 ML 领域,学习是一个持续的、永无止境的过程,因为每年都有新的技术和进步。因此,学习将继续…

希望你喜欢阅读这篇文章,就像我喜欢研究这个案例一样。感谢您的阅读!!

案例研究的完整代码可以在这里找到。

领英:www.linkedin.com/in/rjt5412

Github:https://github.com/Rjt5412

参考资料:

  1. https://www . ka ggle . com/sudalairajkumar/simple-exploration-notebook-elo
  2. https://www.kaggle.com/artgor/elo-eda-and-models
  3. Robin Denz 的《日期变量的进一步观察》
  4. https://www.kaggle.com/fabiendaniel/elo-world/notebook
  5. https://www.kaggle.com/fabiendaniel/hyperparameter-tuning
  6. https://www . ka ggle . com/mfjwr 1/simple-light GBM-without-blending
  7. https://www . ka ggle . com/roydata science/elo-stack-with-Goss-boosting
  8. https://www . ka ggle . com/c/elo-merchant-category-re commendation/discussion/82314
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值