用 Vespa 从 python 构建一个基本的文本搜索应用程序
pyvespa 简化 API 简介。用几行代码从 python 构建 Vespa 应用程序。
这篇文章将向您介绍简化的pyvespa
API,允许我们用 python 的几行代码从头开始构建一个基本的文本搜索应用程序。后续的帖子将通过在这里描述的基础应用之上逐步构建来增加复杂性。
莎拉·多维勒在 Unsplash 上拍摄的照片
pyvespa
在 python 中公开了 Vespa API 的子集。该库的主要目标是允许更快的原型开发,并促进 Vespa 应用程序的机器学习实验。我曾经写过我们如何使用它来连接正在运行的 Vespa 应用并与之交互和评估 python 的 Vespa 排名函数。这一次,我们重点关注从零开始构建和部署应用程序。
安装
这里介绍的 pyvespa 简化 API 发布于版本0.2.0
pip3 install pyvespa>=0.2.0
定义应用程序
例如,我们将构建一个应用程序来搜索 CORD19 样本数据。
创建应用程序包
第一步是创建一个 Vespa 应用包:
from vespa.package import ApplicationPackage
app_package = ApplicationPackage(name="cord19")
向架构中添加字段
然后我们可以将字段添加到在app_package
中默认创建的应用程序的模式中。
from vespa.package import Field
app_package.schema.add_fields(
Field(
name = "cord_uid",
type = "string",
indexing = ["attribute", "summary"]
),
Field(
name = "title",
type = "string",
indexing = ["index", "summary"],
index = "enable-bm25"
),
Field(
name = "abstract",
type = "string",
indexing = ["index", "summary"],
index = "enable-bm25"
)
)
cord_uid
将存储 cord19 文档 id,而title
和abstract
不言自明。- 在这种情况下,所有字段都是类型
string
。 - 将
"index"
列入indexing
列表意味着 Vespa 将为title
和abstract
创建一个可搜索的索引。你可以在 Vespa 文档中了解更多关于indexing
可用选项的信息。 - 设置
index = "enable-bm25"
使 Vespa 预先计算数量,以便快速计算 bm25 分数。我们将使用 BM25 对检索到的文档进行排序。
查询时搜索多个字段
字段集将字段组合在一起进行搜索。例如,下面定义的default
字段集将title
和abstract
组合在一起。
from vespa.package import FieldSet
app_package.schema.add_field_set(
FieldSet(name = "default", fields = ["title", "abstract"])
)
定义如何对匹配的文档进行排序
我们可以通过定义一个 RankProfile 来指定如何对匹配的文档进行排序。在这种情况下,我们定义了bm25
等级配置文件,它结合了在title
和abstract
字段上计算的 BM25 分数。
from vespa.package import RankProfile
app_package.schema.add_rank_profile(
RankProfile(
name = "bm25",
first_phase = "bm25(title) + bm25(abstract)"
)
)
部署应用程序
我们现在已经定义了一个包含相关字段的基本文本搜索应用程序,一个将字段分组在一起的字段集,以及一个对匹配文档进行排名的排名配置文件。是时候部署我们的应用程序了。我们可以通过创建 VespaDocker 的实例,使用 Docker 在本地部署我们的app_package
,而无需离开笔记本,如下所示:
from vespa.package import VespaDocker
vespa_docker = VespaDocker(
disk_folder="/Users/username/cord19_app",
port=8080
)
app = vespa_docker.deploy(
application_package = app_package,
)Waiting for configuration server.
Waiting for configuration server.
Waiting for configuration server.
Waiting for configuration server.
Waiting for configuration server.
Waiting for configuration server.
Waiting for application status.
Finished deployment.
app
现在拥有一个 Vespa 实例,我们将使用它与我们的应用程序进行交互。恭喜,您现在已经有了一个可以运行的 Vespa 应用程序。
要知道pyvespa
提供了一个方便的 API 来定义来自 python 的 Vespa 应用包,这一点很重要。vespa_docker.deploy
将 Vespa 配置文件导出到上面定义的disk_folder
。浏览这些文件是开始学习 Vespa 语法的一个很好的方法。
输入一些数据
部署 Vespa 应用程序后,我们的第一个动作通常是向它提供一些数据。为了更容易理解,我们准备了一个包含 100 行的DataFrame
,以及模式定义所需的cord_uid
、title
和abstract
列。
from pandas import read_csv
parsed_feed = read_csv(
"https://thigm85.github.io/data/cord19/parsed_feed_100.csv"
)parsed_feed
然后我们可以遍历上面的DataFrame
并通过使用 app.feed_data_point 方法来填充每一行:
- 默认情况下,模式名被设置为等于应用程序名,在本例中为
cord19
。 - 当向 Vespa 提供数据时,我们必须为每个数据点提供一个唯一的 id。这里我们用
cord_uid
。
for idx, row in parsed_feed.iterrows():
fields = {
"cord_uid": str(row["cord_uid"]),
"title": str(row["title"]),
"abstract": str(row["abstract"])
}
response = app.feed_data_point(
schema = "cord19",
data_id = str(row["cord_uid"]),
fields = fields,
)
如果需要,您还可以检查对每个请求的响应。
response.json(){'pathId': '/document/v1/cord19/cord19/docid/qbldmef1',
'id': 'id:cord19:cord19::qbldmef1'}
查询您的应用程序
有了数据,我们可以开始查询我们的文本搜索应用程序。我们可以通过向 app.query 方法的主体参数发送所需的参数,直接使用 Vespa 查询语言。
query = {
'yql': 'select * from sources * where userQuery();',
'query': 'What is the role of endothelin-1',
'ranking': 'bm25',
'type': 'any',
'presentation.timing': True,
'hits': 3
}res = app.query(body=query)
res.hits[0]{'id': 'id:cord19:cord19::2b73a28n',
'relevance': 20.79338929607865,
'source': 'cord19_content',
'fields': {'sddocname': 'cord19',
'documentid': 'id:cord19:cord19::2b73a28n',
'cord_uid': '2b73a28n',
'title': 'Role of endothelin-1 in lung disease',
'abstract': 'Endothelin-1 (ET-1) is a 21 amino acid peptide with diverse biological activity that has been implicated in numerous diseases. ET-1 is a potent mitogen regulator of smooth muscle tone, and inflammatory mediator that may play a key role in diseases of the airways, pulmonary circulation, and inflammatory lung diseases, both acute and chronic. This review will focus on the biology of ET-1 and its role in lung disease.'}}
我们还可以通过使用 QueryModel 抽象来定义相同的查询,它允许我们指定我们希望如何匹配和排列我们的文档。在这种情况下,我们定义我们想要:
- 使用
OR
操作符匹配我们的文档,该操作符匹配与查询至少共享一个术语的所有文档。 - 使用我们的应用程序包中定义的
bm25
等级配置文件对匹配的文档进行分级。
from vespa.query import QueryModel, RankProfile as Ranking, OR
res = app.query(
query="What is the role of endothelin-1",
query_model=QueryModel(
match_phase = OR(),
rank_profile = Ranking(name="bm25")
)
)
res.hits[0]{'id': 'id:cord19:cord19::2b73a28n',
'relevance': 20.79338929607865,
'source': 'cord19_content',
'fields': {'sddocname': 'cord19',
'documentid': 'id:cord19:cord19::2b73a28n',
'cord_uid': '2b73a28n',
'title': 'Role of endothelin-1 in lung disease',
'abstract': 'Endothelin-1 (ET-1) is a 21 amino acid peptide with diverse biological activity that has been implicated in numerous diseases. ET-1 is a potent mitogen regulator of smooth muscle tone, and inflammatory mediator that may play a key role in diseases of the airways, pulmonary circulation, and inflammatory lung diseases, both acute and chronic. This review will focus on the biology of ET-1 and its role in lung disease.'}}
使用 Vespa 查询语言作为我们的第一个示例,可以为您提供 Vespa 所能提供的全部功能和灵活性。相比之下,QueryModel 抽象侧重于特定的用例,对于 ML 实验可能更有用,但这是以后的主题。
用 Facebook Messenger 在 60 分钟内创建一个聊天机器人
使用 Rasa 部署一个人工智能助手——从构思到脸书——只需一小时。
沃洛德梅尔·赫里先科在 Unsplash 上的照片
Rasa 是一个开源的对话式 AI 框架,使用机器学习来构建聊天机器人和 AI 助手。今天,我将向您展示如何使用 Rasa 构建您自己的简单聊天机器人,并将其作为机器人部署到脸书信使——所有这一切都在一个小时内完成。你所需要的只是一些简单的 Python 编程和一个有效的互联网连接。
完整代码可以在这里找到:GitHub Repo
代码是用 Python 3.7 开发和测试的。Rasa 目前只支持 Python 到 3.8(更新见此处)。
我们今天构建的机器人将非常简单,不会深入到任何高级 NLP 应用程序中。然而,该框架确实为更复杂的应用程序提供了充分的支持。
我推荐 DeepLearning 提供的这个 Coursera 课程。人工智能学习更多自然语言处理。
我们开始吧!
克隆我的回购的完整代码。
属国
确保安装了所有依赖项。我们的简单机器人只需要两个库:
rasa==2.2.0
spacy==2.2.4
如果您克隆了我的库,您可以运行pip install -r requirements.txt
从根目录安装这两个库。
spacy 语言模型需要在单独的步骤中安装。
python3 -m spacy download en_core_web_md
python3 -m spacy link en_core_web_md en
知识库演练
在我向您展示如何培训和部署我们的助手之前,让我们来看一下每个组件,了解它们是如何组合在一起的。
这些文件中的大部分可以通过运行rasa init
来生成,这将创建一个包含示例训练数据、动作和配置文件的新项目。
我们将从配置文件开始。
配置文件
endpoints.yml
这是助手的所有端点所在的位置。为了支持自定义动作(基本上是您可以编写的用于调用其他 API、查询数据库或访问您构建的其他服务的自定义代码),我们需要创建动作服务器。为此,将action_endpoint
包含在该文件中。
这里还可以定义其他端点,例如,使用 Kafka 发送事件,或者将对话存储在 Redis 中,而不是存储在内存中。
这是我们的端点文件的样子。
action_endpoint:
url: "http://localhost:5055/webhook"
config.yml
配置文件定义了模型将如何被训练——这是你可以真正发挥创造力的地方。第一行用于指定聊天机器人语言。在这里,它将是英语。
language: "en"
下一个组件配置 NLU 管道。我们正在使用 Rasa 推荐的“合理的”启动管道之一。
pipeline:
- name: SpacyNLP
- name: SpacyTokenizer
- name: SpacyFeaturizer
...
接下来是policies
。这些是 Rasa 回应用户信息时将遵循的政策。
policies:
- name: MemoizationPolicy
- name: RulePolicy
core_fallback_threshold: 0.3
core_fallback_action_name: action_default_fallback
...
MemoizationPolicy 会记住训练故事中的最后 X 个事件。可以通过将max_history
添加到策略配置中来指定 x。
RulePolicy 利用我们为机器人编写的硬规则(我们将在后面讨论)来形成响应。尽管 RulePolicy 是第二个指定的,但它将优先于其他策略。这是因为默认情况下,RulePolicy 具有最高优先级,这些优先级也可以配置。通过我们的策略配置,机器人将首先检查适用的规则,如果没有,则转向训练故事。
我们的策略还配置了默认的回退机制。当动作置信度低于我们的core_fallback_threshold
0.3 时,它将发送一个预定义的响应,称为utter_default
,并恢复到触发回退之前的状态。当用户说一些完全没有意义的事情时,这非常有用。
credentials.yml
如果你的代码托管在 GitHub 之类的地方,在发布这个文件之前你应该三思。该文件是身份验证所必需的。我们将利用它来连接到我们的 Facebook Messenger 机器人。我暂时隐藏了这些秘密,但是我们也将介绍如何从脸书生成它们。
**注意:**这个文件不包含在我的 GitHub repo 中。您需要创建这个文件,并用稍后生成的令牌填充它。
facebook: verify: "[this is your custom verification token]" secret: "[this is generated from facebook]" page-access-token: "[this is generated from facebook]"
domain.yml
来自官方 RASA 文档:
域定义了您的助手在其中工作的领域。它指定了你的机器人应该知道的意图、实体、插槽、响应、形式和动作。它还定义了对话会话的配置。
domain.yml 文件分为意图、实体、槽、响应、动作和会话配置。
- 这个机器人被训练成能识别你定义的意图,并根据检测到的意图做出不同的行为
- 如果您在管道中指定了实体提取器,它将提取您在该文件中定义的所有实体;实体可用于进一步调节对话流
- 插槽是机器人将信息存储到内存中的键值对;在对话过程中,可以随时设置、重置和检索插槽
- 响应是预定义的响应模板,供机器人发送消息;我们的默认回退将总是在到达那个状态时发送与
utter_default
相关的消息 - 操作包括已经定义的任何自定义操作
在会话配置中,我们可以定义会话到期时间,以及是否应该将时隙转移到新会话。
培训用数据
我们将训练数据分成三个文件夹:nlu、规则和故事。在 nlu 文件夹中,我们定义了我们的意图和实体。在 rules 文件夹中,我们定义了通过 RulePolicy 执行的规则。stories 文件夹包含模拟对话,用作如何管理对话的附加培训数据。
nlu.yml
在我们的 NLU 设置中,我们定义了一些非常基本的意图。以下是我们的“问候”意图示例,它使用了以下培训数据:
nlu:
- intent: greet
examples: |
- hey
- hello
- hiya
...
官方文档更加详细地介绍了如何改进 NLU 组件,例如通过包含实体、同义词和查找表。
rules.yml
规则定义了机器人应该始终遵循的小型对话模式。RulePolicy 利用这些规则管理对话流。
这里,我们定义了一个简单的规则“总是问候用户”。当机器人遇到“问候”意图时(它根据上面提供的数据学习识别问候意图),它将继续执行自定义操作action_greet
。
- rule: always greet the user
steps:
- intent: greet
- action: action_greet
stories.yml
故事是“真实”对话的例子,用于进一步训练机器人的对话管理模型。它们被写成用户和机器人之间的对话。
我们将包括一个非常简单的故事。
version: "2.0"stories: - story: generic happy pathsteps: - intent: greet - action: action_greet - intent: select_price - action: action_select_upper_price - intent: select_purpose - action: action_select_purpose - intent: select_brand - action: action_select_brand
自定义操作
自定义操作对于更复杂的机器人来说非常有用——它们可以用来调用其他内部 NLP 服务或公共 API 来改善对话体验。
自定义动作存储在actions/actions.py
这里有一个我们的自定义动作action_greet
的例子,它问候用户。
每个自定义操作都遵循非常标准的格式。有一个name
方法返回定制动作的名称——这个名称必须与 domain.yml 中提供的名称相匹配
run
方法需要接受一个分派器、跟踪器和域。
- 调度程序用于生成响应(这里它发出两条消息)
- 追踪器指的是会话追踪器,并且可以被利用来获取槽值、当前和过去的状态
返回值是在操作结束时执行的事件列表。下面是一些我们可以包括在内的常用事件:
- 插槽集-用于设置插槽值
- 后续操作—触发后续操作
- 重新启动—结束当前对话
培训和启动机器人
一旦你有了以上所有的文件,你就可以训练机器人了。在根目录下运行rasa train
开始这个过程。根据您拥有的数据量和管道的复杂程度,这可能需要一段时间。
如果您看到此消息,这意味着模型已成功训练并保存。
您需要运行两个命令来启动 bot——一个用于操作服务器,一个用于 bot 本身。我在这里把它合并成一行。
**注意:**这将在本地启动机器人。稍后,我们需要用脸书生成的令牌填充我们的凭证文件,以将其部署到 Messenger。
rasa run -m models --enable-api --cors "*" --debug & rasa run actions
这是我的终端在该命令运行完毕后的样子。每次服务器参与对话时,都会有额外的日志。
当上述程序运行时,操作服务器将占用端口 5055,而机器人将占用端口 5005。
您可以通过 pinghttp://localhost:5005来测试服务器是否正在运行。
与机器人互动
有几种方法可以开始与机器人互动。
REST API
您可以通过 REST API 与 bot 进行交互。当 RASA 服务器运行时,以用户身份发送此请求以发送消息:
curl --request POST \
--url [http://localhost:5005](http://localhost:5005)/webhooks/rest/webhook \
--header 'content-type: application/json' \
--data '{"sender": "sender_id", "message": "hi"}'
发件人字段是会话的唯一 ID。当您在同一个发件人字段下按顺序发送邮件时,服务器会识别出这些邮件都是一个会话的一部分。消息字段是您要发送的文本。
RASA shell
你可以通过命令行运行rasa shell
与机器人开始对话。
Facebook Messenger 设置
我倾向于通过 ngrok 来部署 Rasa 服务器,这将把您的本地端口暴露给公共互联网。或者,你可以使用 Heroku 之类的服务,但 Rasa 映像的大小使得应用程序很难在大多数空闲层上运行。
ngrok 设置
- 创建一个 ngrok 账户
- 按照以下说明在您的电脑上设置 ngrok:https://ngrok.com/download
- 通过在单独的终端中运行
./ngrok http 5005
,在端口 5005 上启动 HTTP 隧道
确保你保持终端运行——任何时候终端停止运行,你的隧道将停止,你的机器人将在 Messenger 上没有响应。
记下生成的 url。安全的网址(与 https)将需要提供给脸书。
在脸书上创建应用程序
脸书机器人需要链接到脸书网页。你需要一个脸书账户来创建一个页面。
一旦你的页面创建好了,去https://developers.facebook.com/apps。点击绿色的“创建应用程序”按钮创建一个新的应用程序。
选择第一个选项,并按照说明创建您的应用程序。
点击下方的“设置”按钮,将“Messenger”添加到您的应用程序中。
在脸书上设置应用程序
一旦创建了应用程序,它会引导你进入 Messenger 下的“设置”页面。如果您没有被自动带到那里,请导航到您的应用仪表板,然后进入“信使”>“设置”:
首先,将你的脸书页面链接到应用程序。
在链接页面旁边,添加订阅以允许邮件。
一旦你的页面被链接,点击页面旁边的“生成令牌”。跟踪这个令牌,因为它需要包含在您的 Rasa 项目中。我们将这个令牌称为“页面访问令牌”。
接下来,导航到仪表板上的设置>基本,找到您的应用程序密码。
然后,向下滚动到“webhooks”添加一个新的回调 URL。
在填写回调细节之前,在 Rasa 项目中创建 credentials.yml。该文件应该如下所示:
以下是填充字段的方法:
- **验证:**创建您选择的安全验证令牌
- **秘密:**这是从基本设置中获取的应用程序秘密
- **页面访问令牌:**使用您之前为页面生成的页面访问令牌
通过运行以下命令再次启动 Rasa 服务器。这与前面的命令略有不同,因为我们现在利用了凭据标志。
rasa run -m models --enable-api --cors "*" --debug --credentials credentials.yml & rasa run actions
获取作为 credentials.yml 的一部分创建的 verify 标记,并在 Webhooks 部分的回调 URL 旁边输入它。
**注意:**在你的 ngrok 隧道停止的任何时候,你都必须重启它,并在脸书上更新你的回拨 URL
验证您的应用程序已启动并正在运行
导航到您的页面并尝试向机器人发送消息。
太棒了,都准备好了!目前状态下的机器人实际上不会提供有意义的笔记本电脑推荐——也许有一天会的:)
感谢您的阅读!
通过 Medium 关注我的最新动态。😃
作为一个业余爱好项目,我还在 www.dscrashcourse.com建立了一套全面的免费数据科学课程和练习题。
再次感谢您的阅读!📕
为实际用例建立分类模型
具有核心技巧和松弛变量支持向量机深度挖掘
在 Unsplash 上由 Ameer Basheer 拍摄的照片
数据科学家你好!我希望你们都过得很好。上次我们讨论了如何用核心概念从零开始构建一个回归模型到一个实际用例。今天,我计划向您提供对分类模型的高级理解,并通过实践经验深入了解实际场景。
在本文中,您将了解到:
- 支持向量机
- 拉格朗日乘数
- 松弛变量
- 内核技巧
- 超参数调谐
- 类别不平衡处理
- 准确度测量
用例: 银行营销数据集:与一家葡萄牙银行机构的直接营销活动(电话)相关。目标是预测客户是否会认购定期存款。
我在这里使用的数据集来自 UCI 机器学习库,它可以公开用于研究。
数据集来源:https://archive.ics.uci.edu/ml/datasets/bank+marketing#
1.问题定义
在我们着手做任何事情之前,我们需要了解问题和影响决策的事实。所以根据给定的用例,这是一个分类问题。我们需要预测特定的客户是否愿意将他/她的钱存入银行。
作为数据科学家,我们的总体目标应该是,通过识别影响营销活动成功的主要因素并预测营销活动对特定客户是否会成功,来提高营销活动的效率。
因此,作为第一步,我们需要找出我们在数据集中有什么(+它的复杂性)以及可以直接或间接影响我们预测的特定事实。然后我们需要像往常一样做预处理。
我假设你已经熟悉了我之前的文章中的预处理步骤。我已经更深入地解释过了。因此,您将能够轻松地对该数据集进行预处理,直至添加 PCA。
此外,您可以参考我在资源部分下的 python 笔记本,一步一步地了解这个过程。
预处理完成后,我们需要拟合一个模型。所以根据给定的用例,这是一个分类问题。有如此多的分类技术可用。一些非常受欢迎的是:
- k-最近邻(KNN)-通过具有最小距离的多数表决原则确定数据点的类别。
- 朴素贝叶斯——基于贝叶斯定理的概率算法。
- 决策树—遵循一组决策规则(if-then-else 条件)来可视化数据并根据条件对其进行分类。
但是你不能盲目地将任何模型应用于给定的数据集。在应用模型之前,您需要了解问题环境、数据集复杂性、每个模型的特点和缺点,以及许多额外的东西。如果您用一些领域级别的理解(+您的经验)正确地分析它,您将能够为您的数据集找到一个更好的适合模型。
在这里,我选择了 SVM 模式。我将用一些数学概念在更深的层次上解释 SVM。如果你赶时间,跳过下面的解释,直接跳到实际的实现(第 6 节)。
2.支持向量机解释
支持向量机是机器学习中最流行的分类技术之一,它直接受到一个叫做统计学习理论的数学概念的启发。
它是一种监督学习技术,主要应用于线性可分的数据集(特征)。同样,目标(y)应该是明确的。当 y 是连续的,我们可以使用支持向量回归。
线性可分的意思是,我们应该能够找到一条线性直线或者一个线性超平面,能够把数据点完全分成两类。
关键思想是找到尽可能广泛地将一个类与另一个类分开的最优超平面。
注意——我们只能讨论两类的线性可分性。但是如何处理多类分类呢?等到最后!
如果数据集不是线性可分的,您应该使用非线性转换或松弛变量使它们可分(至少在某种程度上)。我将在几分钟后解释什么是松弛变量。
SVM 的主要直觉是识别边界向量。那么什么是边界呢?我用图表来解释一下。
对 SVM 的主要直觉(图片由作者提供)
在线性可分离的数据集中,我们可以找到分隔两个类的边界。(这里我取正和负)这些位于边界线上的向量称为支持向量。
SVM 总能找到线性可分模式的最佳超平面。这个边界被称为决策边界。(在神经网络中,这个判定边界是任意选择的。但是在 SVM,它是以一种最佳的方式被挑选出来的。)
在 SVM,我们讨论如何绘制这些线条,使它们具有最宽的宽度,将阳性样本与阴性样本分开。
通过最大化宽度来绘制决策边界(图片由作者提供)
所以这 3 条线可以用下面的等式来表示。
- wx+b = 0——用于决策边界
- Wx+b=-1 —适用于 1 级边界
- Wx+b=+1 —用于 2 级边界
阳性样本位于 Wx+b≥ +1 侧,阴性样本位于 Wx+b≤ -1 侧。
如果我们为正样本引入变量 y=+1,为负样本引入变量 y=-1,那么我们可以得到该条件的一般形式。
条件的一般形式(作者提供的图片)
如果满足这个条件,所有正负数据点都将在边界线后面。(完美区分两个类别)
当我们确定 w 和 b 时,我们应该考虑最大宽度。让我们找出宽度。
寻找宽度(作者图片)
我们想最大化宽度。为了最大化宽度,我们需要最小化|W|或 1/2|W|(这不会改变优化函数,但在计算梯度时只会导致更整洁的解决方案)
所以最终我们需要在y(Wx+b)-1=0
的约束下最小化 1/2|W|
如果根据给定的条件,我们需要最小化某个东西,我们拥有的唯一定理是拉格朗日定理。所以还是来应用一下吧。
拉格朗日定理的应用(图片由作者提供)
在解完方程并将数值代入前面的方程 A 后,我们可以找到最佳宽度。
最佳宽度(图片由作者提供)
最佳权重向量和偏差可以这样导出。
最佳权重向量和偏差(图片由作者提供)
现在我们知道了最佳权重向量和偏差。我们可以用它来预测给定的数据点。
给定数据点的预测(图片由作者提供)
所以到现在为止,我们一直在讨论线性可分数据。如果我们找不到数据之间的线性分离,我们要么将它们转换成线性可分离状态,要么我们需要使用松弛变量技术对这个非线性可分离数据应用 SVM 模型。
3.松弛变量的使用
实际上,大多数数据集不会是线性可分的。结果,将没有办法满足我们先前导出的约束。缓解这个问题的一个方法是通过引入松弛变量来放松一些约束。
因此,我们在这里做的是,计算与决策裕度不同的数据点之间的距离,并定义一个罚函数。该罚函数控制对误分类的支持。(由于违反了某些约束,这将导致多少数量被错误地分类。)
我们对落在边界之外的数据点施加惩罚。惩罚参数的实际作用是,它温和地惩罚位于决策边界错误一侧的点。这个损失(ξ)被称为松弛变量。
引入惩罚(图片由作者提供)
ξ = 0 表示数据点在正确的边界上或边界内。
对于决策边界上的数据点,ξ = 1。
落在错误一侧的数据点ξ ≥ 1。(j 和 k 被错误分类)
现在我们必须最小化:
最小化新目标(图片由作者提供)
参数 C 是惩罚强度,它指定了我们有多在乎错误。它也被称为正则化参数,我们用它来调整我们的模型。
如果 C 很小,SVM 就变得很松散,可能会牺牲一些点来获得一个简单的解。这将导致未命中分类的增加。如果 C 非常大,SVM 会变得非常严格(强),并试图获得超平面右侧的所有数据点。这将导致模型过拟合。所以在选择 C 的值时,你应该更加关注。
惩罚力量 C vs 分类行为(图片由作者提供)
4.用于多类分类的 SVM
二元对多类分类(图片由作者提供)
SVM 通常用于二元分类。它本身不支持多类分类。因此相同的二元分类原理被用于多类分类。可以看到有两种方法。
- 一对一方法— 将多类问题分解为多个二元分类案例。
- 一对多方法 — 细分设置为每个类一个二元分类器。连续地,某一类与所有其他类相区别。
让我们举一个简单的例子来进一步说明。这里,目标是四个可能的类中的一个:{红色、绿色、蓝色、黄色}。
在一对一方法中,我们创建了一组二元分类器,每个分类器代表一个对:
- OvO 二元分类器 1:红色对绿色
- OvO 二元分类器 2:红色对蓝色
- OvO 二元分类器 3:红色 vs 黄色
- OvO 二元分类器 4:绿色对蓝色
- OvO 二元分类器 5:绿色对黄色
- OvO 二元分类器 6:蓝色 vs 黄色
一对一多类分类所需的分类器数量可以用下面的公式(n 为类的数量)来检索:n*(n-1)/2
在一对一分类器中,仅涉及创建四个二元分类器:
- OvR 二元分类器 1:红色对{绿色、蓝色、黄色}
- OvR 二元分类器 2:绿色 vs {红色、蓝色、黄色}
- OvR 二元分类器 3:蓝色 vs {红色、绿色、黄色}
- OvR 二元分类器 4:黄色 vs {红色、绿色、蓝色}
因此所需的分类器等于类别的数量。这两者都可以通过使用 scikit-learn 库轻松实现。
from sklearn.svm import SVC
from sklearn.multiclass import OneVsOneClassifier
from sklearn.multiclass import OneVsRestClassifier# define model one-vs-one
clf1 = OneVsOneClassifier(SVC());# define model one-vs-rest
clf2 = OneVsRestClassifier(SVC());
最好的是什么?最好的方法是根据问题的具体情况而定。所以要明智地使用它。尽管如此,我还是会给出一些赞成和反对的意见。
一对一
- pro 不容易在数据集中造成不平衡。
- con 仅对每对类将主数据集分割成一个二元分类。
一比一休息
- pro 需要更少的分类器,使其成为更快的选项。
- con——由于大量的类实例,处理大型数据集具有挑战性。
5.内核技巧
内核是适用于所有 SVM 氏症的窍门。这是一种优化技术,使 SVM 更快,并导致高度的普遍性。这是一个非常简单但更强大的概念。
核函数的思想是使操作能够在输入空间中执行,而不是在潜在的高维特征空间中执行。因此,内积不需要在特征空间中计算。
迷茫?让我用图表来解释一下。
从输入空间到特征空间的数据转换(图片由作者提供)
我们使用函数(φ)将非线性可分数据转换到高维空间,在该空间中我们可以找到线性可分的决策状态。
对于线性可分数据,我们已经推导出宽度为 W =σαy x
,但是现在使用变换函数,它变成 W =σαyφ(x ),最终我们的 Q 最优宽度函数将是:
新的最佳宽度函数(图片由作者提供)
这里的问题是我们需要分别计算事物的数量。
- φ(xi)
- φ(xj)
- 两者的点积[φ(xi).φ(xj)]
为了降低计算复杂度,我们可以引入另一个函数为:
k(xi,xj) =φ(xi)*φ(xj)
该函数接受原始低维空间中的输入,并返回高维空间中已转换向量的点积。(直接进行转换,而不是显式地将转换应用于x i 和 xj 并获得点积)
这就是所谓的内核技巧。因此,该函数通过大幅降低计算复杂度,在 SVM 及其性能中起着关键作用。
对于大型数据集(如-10k、1M……),您不需要对所有数据点逐一进行转换并获得点积,但您可以简单地将此核函数应用于所选数据点,以获得相同的结果,这将有助于降低复杂性。
重要提示—核函数应该是对称的。
最常用的内核类型列表如下所示。
内核类型(图片由作者提供)
最流行的核函数类型是 **RBF。**因为它具有沿整个 x 轴的局部和有限响应。然而,需要记住的一个关键点是,当我们将数据映射到一个更高维度时,我们很有可能会过度拟合模型。因此,选择正确的核函数(+选择正确的参数)和正则化非常重要。
6.将 SVM 应用于预处理数据集
好吧!我希望我已经涵盖了 SVM 中的大部分核心概念。让我们回到我们的问题。现在我们需要将 SVM 模型应用到我们的数据集。编写代码比理解概念要容易得多。但是如果你盲目地应用它,那只是一种浪费。在实现任何模型之前,您需要更好地理解基本概念。我打算写更多关于不同模型的文章,并将它们应用于实际用例。希望它也有助于提高你的知识。
拿够了。来做实现吧。
我假设您已经在我上一篇文章的帮助下完成了 PCA 之前的所有预处理。
预处理数据集到 PCA(图片由作者提供)
scikit-learn 库提供了支持向量分类器来实现 SVM 模型。
from sklearn import svm
svc = svm.SVC(kernel='rbf', C=1.2, gamma=0.5)
你可以像往常一样得到预测。
predictions = svc.predict(X_test_pca)
y_hat = pd.DataFrame(predictions, columns=["predicted"])
7.准确性测量和评估
在回归分析中,我们有像均方误差、R 值这样的精度指标…等等。但是在分类问题中,分数在某些情况下可能是误导的(例如不平衡的分类问题)
我们用总预测中的正确预测来衡量准确性。可能会有这样的情况,一些类完全没有被识别(忽略),但由于其他类的影响,给出了超过 90%的准确率。它们在少数类中占主导地位,并使准确性成为衡量模型性能的不可靠指标。
因此,建议使用以下一些技术,因为它们为所有类别提供了良好的度量,并且是分类任务中最常用的指标。
- 混淆矩阵
- 精确召回和 F1 得分
- AUC - ROC 曲线
混淆矩阵
混淆矩阵提供了一个更有洞察力的图像,显示了哪些类被正确和错误地预测,最重要的是,显示了正在犯的错误的类型。它克服了单独使用分类精度的局限性。
困惑矩阵(图片由作者提供)
以下是需要记住的重要术语。(真更好,因为模型正确预测了它们)如果这两个类是正和负:
- 真正值(TP): 实际值是正值,它们被预测为正值
- 真负值(TN): 实际值是负值,它们被预测为负值
- 误报(FP): 实际值为负值,预测值为正值。(也称为 I 型错误)
- 假阴性(FN): 实际值为正,预测值为负。(也称为第二类错误)
解读困惑矩阵(图片由作者提供)
让我们看看实现。
混淆矩阵实现(图片由作者提供)
如您所见,一个类(0)得到了非常好的预测,而另一个却没有。这是因为阶级不平衡。将在几分钟后看到如何克服这一点。
F1 分数
F1 分数是精确度和召回率的调和平均值。(传达了精准和召回之间的平衡。)我们先来看看什么是精准和召回。
- 精确度—从预测的阳性中,有多少是实际阳性
- 回忆—从实际阳性中,有多少被正确分类
方程式(图片由作者提供)
F1 分数通常比准确性更有用,尤其是如果你的班级分布不均匀。如果假阳性和假阴性具有相似的成本,则准确性最好。如果误报和漏报的代价相差很大,最好同时看精度和召回率。这就是为什么我们使用 F1 的分数。
from sklearn.metrics import f1_scoref1_score(y_test,y_hat)
F1 的分数为 1 时被认为是完美的,而当分数为 0 时,这个模型就是一个彻底的失败。Scikit-learn 的分类报告精确地提供了所有的分数。
from sklearn.metrics import classification_reportprint(classification_report(y_test,y_hat))
分类报告(图片由作者提供)
对于 AUC-ROC 曲线,我将单独写一篇文章,因为它需要更深入地解释。(+带有成本函数)但是如果你好奇,你可以参考我的 Colab 笔记本,因为我把所有代码都放在里面了。
8.处理阶级不平衡
你可能会注意到有一个类在预测中占主导地位。那是因为阶级不平衡。我们将类不平衡定义为分类数据集类的比例不均衡。
目标中的类不平衡(图片由作者提供)
如果我们检查数据集,我们可以发现这两个类之间的巨大差异。这在大多数实际情况下都会发生,如垃圾邮件检测、欺诈分析等。为了避免这种情况,我们可以使用不同的技术。我将简单解释两种不同的方法。如果你想进一步了解它,请告诉我,我也会就此写一篇单独的文章。
请记住,我们需要将这些技术应用于预处理过的数据集,因为它们也被视为模型。另一件事是,我们只对训练数据进行采样。
过采样
通过这样做,它将按照多数类比例对少数类比例进行重新采样。
SMOTE 是一种非常著名的过采样技术。(合成少数过采样技术。)它通过利用k-最近邻算法来创建合成数据。这就是为什么我们需要在数据预处理之后使用它。
SMOTE 最初从少数类中选取随机数据开始,然后设置数据的 k 个最近邻。然后在随机数据和随机选择的 k-最近邻之间产生合成数据。
SMOTE 有不同的变体/形式,例如边界 SMOTE、SVM SMOTE(使用 SVM 算法)、自适应合成采样(ADASYN)…等等。让我们应用默认形式的 SMOTE。
from imblearn.over_sampling import SMOTEsm = SMOTE(random_state=101)
X_train2, y_train2 = sm.fit_resample(X_train_pca, y_train)
应用 SMOTE 后(图片由作者提供)
应用 SMOTE 后的分类报告(图片由作者提供)
它为少数民族班级带来了一点进步,但是我对结果有点担心,因为我期望得到更高的结果。没关系!总有改进的余地。如果你有时间,尝试一些不同的方法,如 SVM SMOTE,并把结果放在评论部分。
欠采样
通过这样做,它将减少多数阶级的比例,直到人数与少数阶级相似。
最流行的欠采样技术之一是侥幸成功。它也是基于 k-NN 算法。它所做的是当属于不同类别的两个点在分布中彼此非常接近时,该算法消除较大类别的数据点,从而试图平衡分布。
这项技术有三个版本。
- 接近失误 1 — 通过计算较大分布和三个最接近的较小分布之间的平均最小距离来平衡数据。
- 接近失误 2 — 通过计算较大分布和三个最远的较小分布之间的平均最小距离来选择示例。
- 接近未命中 3 — 考虑较小的类实例,并存储 m 个邻居。然后取该分布与较大分布之间的距离,并消除最大距离。
您可以尝试一些不同的方法/排序,并找出最适合您的数据集和模型的方法。这里我使用了版本 3 和两个最近的邻居。
# Undersample imbalanced dataset with NearMiss-3
from imblearn.under_sampling import NearMissundersample = NearMiss(version=3, n_neighbors_ver3=2)
# transform the datasetX_train_undermiss, y_train_undermiss = undersample.fit_resample(X_train_pca, y_train)
应用未遂事故后(图片由作者提供)
应用未遂事件后的分类报告(图片由作者提供)
虽然精确度下降了,但它为两个类别提供了相当好的分类。
另一种良好的欠采样技术是 Tomek 链接(T-Links ),它是压缩最近邻或简称 CNN 的一种改进。(不要与卷积神经网络混淆)它寻找样本集合的子集,这不会导致模型性能的损失。这种方法比 k-最近邻算法更有效,因为它减少了内存需求。
from imblearn.under_sampling import TomekLinks
undersample = TomekLinks()
X_train_under, y_train_under = undersample.fit_resample(X_train_pca, y_train)
9.超参数调谐
为了找到超参数的最佳配置,我们可以使用 GridSerch 。这是一种调整技术,用于确定给定模型的最佳值。如你所知,在我们的 SVM 模型中,我们使用了两个超参数 C 和γ。但是没有办法预先知道这些超参数的最佳值是什么。
所以,我们需要尝试所有可能的值,才能知道最优值。手动完成这项工作需要大量的时间和精力,因此我们使用网格搜索来自动完成这项工作,它会为我们找到最佳参数。
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC# defining parameter range
param_grid = {'C': [0.1, 1, 10, 100, 1000],
'gamma': [1, 0.1, 0.01, 0.001, 0.0001],
'kernel': ['rbf']}#grid search
grid = GridSearchCV(SVC(),param_grid,n_jobs=-1,verbose=3,refit = True)# fitting the model for grid search
grid.fit(X_train_pca, y_train.values.ravel())
n_jobs 参数允许 GridSearch 在幕后使用 Python 的多处理模块。设置 n_jobs = -1 会导致使用机器上所有可用的内核,这将提高网格搜索性能。
注意—在联合实验室中,我们只有 2 个内核。所以还需要一些时间。在我的情况下,它会持续 1 个半小时。
这里将测试不同的参数组合,并最终找到最佳参数。
不同网格的性能+时间分析(图片由作者提供)
# print best parameter after tuning
print(grid.best_params_)
# print how our model looks after hyper-parameter tuning
print(grid.best_estimator_)
微调 T-连锁 SVM 后的结果如下所示。它在所有实例中提供了最好的准确性,但是在识别第二(1)类时仍然有一些缺点。
T 型链接的网格搜索优化 SVM(图片由作者提供)
最后,我将向您展示这种情况下两种最佳技术的准确性比较。
最终对比(图片由作者提供)
10.资源
- 完整的合作实验室 Python 笔记本
https://colab.research.google.com/drive/1EcVY0jQsO6uKzTMlCPK1wt-T6eEYNDzA?usp=sharing
- 前一篇文章
[## 从零开始建立一个机器学习模型到实际使用案例
towardsdatascience.com](/build-a-machine-learning-model-to-a-practical-use-case-from-scratch-3a8dc65ab6f1)
结论
我们在这里讨论了支持向量机背后的原理的深层解释。我们学习了松弛变量和内核技巧,它们帮助我们高效地解决非线性问题。此外,我们还探索了如何使用这个 SVM 模型处理多类分类。
我们通过理解如何从零开始实现 SVM 和正确评估模型的准确性度量来掩盖实际场景。不仅如此,我们还通过修复类不平衡和使用网格搜索微调我们的模型来增加我们模型的准确性。我们已经看到了许多视觉表现,这有助于我们更好地理解所有的核心概念。
所以我希望你对 SVM 模式及其实施有一个清晰的了解。我会在另一篇有趣的文章中再见到你。在那之前,注意安全!快乐学习!❤️
使用 Rasa 构建强大的对话助手
用你的私人助理回复重复的信息
作者图片
动机
你曾经希望有一个私人助理来回答重复的信息吗?你可能会犹豫这样做,因为你不知道从哪里开始。
事实证明,创建一个对话助手并不困难。在本文中,我将向您展示如何使用 Rasa 为自己创建一个。
什么是 Rasa?
Rasa 是一个开源的机器学习框架,用于自动化对话。使用 Rasa,您可以创建一个健壮的模型,并使用几个命令行来部署它。
要安装 Rasa,请键入:
pip install rasa
现在,您可以通过键入以下命令来创建一个新项目:
rasa init
您可以训练一个初始模型,并在命令行上与您训练有素的助手聊天,如下所示。
作者图片
看到问题Do you want to speak to the trained assistant on the command line?
,输入Y
。你现在可以像下面这样和你的助手交谈。
作者图片
你与训练有素的助手的最初对话可能不太好。但是不用担心。你可以训练你的助手,让他明白你以后想要什么。
按 Ctrl + C 停止对话并完成项目目录的创建。项目的结构如下所示:
作者图片
现在你有了一个项目,让我们来学习如何提高你的助手。
基本概念
目的
首先,训练有素的助理如何理解你的消息?
作者图片
如果你打开data/nlu.yml
文件,你会看到如下内容:
因此,当我们键入Good morning
、hi
或hello there
时,该模型将检测这些句子的意图为greet
。而当我们键入bye
时,模型会检测到它的意图是goodbye
。
到目前为止一切顺利。但是你的助手如何知道如何响应检测到的意图,比如greet
?
故事
我们可以通过创建故事来指定我们的助手将如何响应每个意图。故事由用户和机器人之间的对话组成。
要查看故事,请打开data/stories.yml
文件。
intent
参数指定用户的意图,而action
参数指定机器人的响应。我们可以创造不同的故事来应对不同的场景。
让我们试着根据上面列出的sad path 1
故事与我们的助理聊天。
作者图片
酷!我们的助手对这个故事的反应类似。我们来分解一下为什么我们的助手会像上面这样回答我们:
Hi
:意图greet
Hey! How are you?
:动作utter_greet
I am sad
:意图mood_unhappy
Here is something to cheer you up
:动作utter_cheer_up
Did that help you?
:动作utter_did_that_help
Yes, it helps. Thank you!
:意图affirm
Great, carry on!
:动作utter_happy
厉害!但是对于utter_greet
这样的动作,我们怎么能指定具体答案?
领域
您可以通过更改domain.yml
中的text
来指定每个响应的答案。
请注意,这些文本与我们在之前的对话中看到的答案相似。
厉害!现在你知道一些基本概念了。让我们训练你的助手乐于助人。
创建列车数据
使用 Rasa 创建列车数据有多种方式。您可以使用交互模式、YAML 文件或 Rasa X 进行训练
相互作用的
如果你不知道从哪里开始,我建议你通过和你的助手聊天来创建新的训练数据。
要开始交互式会话,请键入:
rasa interactive
作者 GIF
正如你在上面看到的,在和你的助手交谈时,你可以标记你的问题的意图,并根据你的意图选择正确的回答!请注意,每个响应都应该以单词utter_
开头。
还可以在运行rasa interactive
的同时,在http://localhost:5006/visualization . html可视化交互对话。
作者 GIF
要停止交互式会话,请键入 Control + C。
现在你应该会在nlu.yml
文件中看到新的意图,在stories.yml
中看到新的故事,在domain.yml
文件中看到新的回应
YAML 文件
您也可以将数据直接添加到 YAML 文件中,或者更改任何您不喜欢的信息。您可以按照上面的格式添加新的故事、意图和回应。
不要害怕犯错误。要确保不同文件中的数据一致,请键入:
rasa data validate
如果有任何不一致,您应该会看到如下警告消息:
UserWarning: Issue found in 'data/stories.yml':
Found intent 'answer_question_about_health' in stories which is not part of the domain.
火车
添加更多训练数据后,您可以通过运行以下命令开始训练您的数据:
rasa train
和你的助手谈谈
现在,让我们通过运行来测试我们刚刚训练的模型:
rasa shell
作者图片
酷!现在问题"How are you?"
的答案更相关了。
想象你的故事
您还可以通过运行以下命令来可视化创建的故事:
rasa visualize
并且一个graph.html
文件将在您的当前目录中生成。在浏览器上查看该文件时,您应该会看到类似下面的内容。
作者图片
在浏览器上训练并与你的助手交谈
如果你喜欢在浏览器上训练和与你的助手交谈,使用 Rasa X。在这里找到安装 Rasa X 的说明。
安装 Rasa 后,您可以通过运行以下命令开始使用它:
rasa x
您应该能够在浏览器上与您的助手聊天,如下所示:
作者图片
您还可以通过单击对话右下角的铅笔图标,在与模型对话的同时训练模型。
作者图片
在与你的助手交谈时,你可以创造如下新的意图和行动:
作者图片
作者图片
对新的培训数据及其标签感到满意后,单击屏幕右下角的“保存故事”。
作者图片
您的新训练数据应该添加到您的 YAML 文件中,如下所示!
共享您的助手
您也可以通过点击左上角的共享按钮,然后点击生成链接,与其他测试人员共享您的助手。
作者图片
应该会为您生成一个链接!如果你在本地模式下使用 Rasa X,你可以使用 ngrok 让你的机器人对其他机器上的用户可用。
安装 ngrok 后,打开一个新的终端并键入:
ngrok http 5002
将生成如下所示的链接:
作者图片
现在,将此链接发送给你的同事,让他们在你的应用仍在运行时尝试一下。他们现在应该可以和你的助手互动了!
部署您的助手
如果您想要部署您的助手并使其对您的客户端可用,请遵循本说明。
结论
恭喜你!您刚刚了解了使用 Rasa 创建和部署聊天机器人是多么容易。我希望这篇文章能激励你创建自己的助手来帮助回答客户的问题。
这篇文章的源代码可以在这里找到。
本文只涉及了这个不可思议的库所能做的事情的表面。我建议你看看 Rasa 的文档或者他们的 Youtube 视频。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上和我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
用 Python 在 7 分钟内构建一个 Dash 应用程序
使用 Python 从头开始创建一个漂亮的可视化应用程序
卢克·切瑟在 Unsplash 上的照片
当我几个月前开始我的顶点项目时,我想为我的机器学习模型创建一个交互式仪表板。
我希望这个仪表板是 web 应用程序的形式,并在新数据进入系统时显示实时更新。
在探索了许多可视化工具之后,我决定使用 Dash。
Dash 是一个开源库,允许您用 Python 创建交互式 web 应用程序。
如果您经常使用 Python 来构建数据可视化,Dash 是您的理想工具。您可以围绕您的数据分析创建一个 GUI,并允许用户使用您的仪表板应用程序。
Dash 最棒的地方在于它允许你纯粹用 Python 来构建这些东西。您可以创建 web 组件,而无需编写任何 HTML 或 Javascript 代码。
在本教程中,我将展示如何用 Dash 创建一个简单的数据可视化应用程序。
最终产品将如下所示:
作者图片
步骤 1:先决条件
在我们开始构建应用程序之前,您需要具备:
- Python IDE:我使用 Visual Studio 代码进行分析,但是任何 Python IDE 都可以使用(除了 Jupyter 笔记本)。
- 库:安装 Dash、Pycountry、Plotly 和 Pandas
- 数据:我使用 Zomato Kaggle 数据集来构建 web 应用程序。我对数据集做了一些修改,使其适合这个分析,你可以在这里找到预处理版本。
步骤 2:构建仪表板
首先,让我们导入构建应用程序所需的所有库:
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
import plotly.express as px
import numpy as np
import pandas as pd
如果上面几行代码执行成功,干得好!所有的装置都工作正常。
就在导入的下面,添加这行代码以在 Dash 应用程序中包含 CSS 样式表:
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
然后,让我们初始化 Dash:
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
现在,我们可以用下面几行代码将数据帧读入 Pandas:
df = pd.read_csv('restaurants_zomato.csv',encoding="ISO-8859-1")
数据帧的头看起来像这样:
作者图片
该数据框由餐厅数据组成,即每家餐厅的名称、位置、评级和受欢迎程度。
我们将在 Dash 应用程序中可视化这些数据。
我们将创建三个图表显示在我们的 Dash 应用程序上——一个地图,一个条形图,以及一个饼图。
先说地图。
我们将按国家显示数据集中的餐馆数量。最终产品将如下所示:
作者图片
为此,我们将使用数据集中的’ country_iso’ '列。我们需要计算每个国家的餐馆数量,并创建一个新的“ count ”列,这可以用下面几行代码来完成:
# country iso with counts
col_label = "country_code"
col_values = "count"
v = df[col_label].value_counts()
new = pd.DataFrame({
col_label: v.index,
col_values: v.values
})
太好了!现在,我们可以创建包含*【国家/地区 _ iso】和计数*列的地图:
hexcode = 0
borders = [hexcode for x in range(len(new))],
map = dcc.Graph(
id='8',
figure = {
'data': [{
'locations':new['country_code'],
'z':new['count'],
'colorscale': 'Earth',
'reversescale':True,
'hover-name':new['final_country'],
'type': 'choropleth'
}],
'layout':{'title':dict(
text = 'Restaurant Frequency by Location',
font = dict(size=20,
color = 'white')),
"paper_bgcolor":"#111111",
"plot_bgcolor":"#111111",
"height": 800,
"geo":dict(bgcolor= 'rgba(0,0,0,0)') }
})
上面几行代码将创建地图,并根据列出的餐馆数量给每个区域着色。
现在,我们可以开始创建条形图了。
如果您查看上面显示的地图,印度在数据框中拥有最多的餐厅。数据框中列出的 8,155 家餐厅位于印度。
让我们用下面几行代码来看看印度最受欢迎的餐馆:
df2 = pd.DataFrame(df.groupby(by='Restaurant Name')['Votes'].mean())
df2 = df2.reset_index()
df2 = df2.sort_values(['Votes'],ascending=False)
df3 = df2.head(10)
bar1 = dcc.Graph(id='bar1',
figure={
'data': [go.Bar(x=df3['Restaurant Name'],
y=df3['Votes'])],
'layout': {'title':dict(
text = 'Top Restaurants in India',
font = dict(size=20,
color = 'white')),
"paper_bgcolor":"#111111",
"plot_bgcolor":"#111111",
'height':600,
"line":dict(
color="white",
width=4,
dash="dash",
),
'xaxis' : dict(tickfont=dict(
color='white'),showgrid=False,title='',color='white'),
'yaxis' : dict(tickfont=dict(
color='white'),showgrid=False,title='Number of Votes',color='white')
}})
上面的代码将呈现一个如下所示的条形图:
(注意,在完成所有组件的创建和显示之前,您还不能运行代码)
作者图片
让我们创建最终的图表。
我们将查看数据集中的总体评分分布:
col_label = "Rating text"
col_values = "Count"
v = df[col_label].value_counts()
new2 = pd.DataFrame({
col_label: v.index,
col_values: v.values
})
pie3 = dcc.Graph(
id = "pie3",
figure = {
"data": [
{
"labels":new2['Rating text'],
"values":new2['Count'],
"hoverinfo":"label+percent",
"hole": .7,
"type": "pie",
'marker': {'colors': [
'#0052cc',
'#3385ff',
'#99c2ff'
]
},
"showlegend": True
}],
"layout": {
"title" : dict(text ="Rating Distribution",
font =dict(
size=20,
color = 'white')),
"paper_bgcolor":"#111111",
"showlegend":True,
'height':600,
'marker': {'colors': [
'#0052cc',
'#3385ff',
'#99c2ff'
]
},
"annotations": [
{
"font": {
"size": 20
},
"showarrow": False,
"text": "",
"x": 0.2,
"y": 0.2
}
],
"showlegend": True,
"legend":dict(fontColor="white",tickfont={'color':'white' }),
"legenditem": {
"textfont": {
'color':'white'
}
}
} }
)
由上述代码创建的饼图将如下所示:
作者图片
现在我们已经创建了所有三个图表,让我们在网页上显示它们并运行 Dash 应用程序。
我们首先需要想出我们的 web 应用程序的页面布局和结构。
Dash 组件遵循网格结构(类似于 CSS 网格布局)。Dash 有三个核心组件——行、列和容器。
您可以以任何方式排列这些组件,以适合页面上的元素。
因为我们只有三张图表,所以我想出了这个简单的布局:
作者图片
对于这些图表,我们将只使用两行,第二行将有两列来容纳条形图和饼图。
下面是在 Dash 中创建这样的布局的一些代码:
graphRow1 = dbc.Row([dbc.Col(map,md=12)])
graphRow2 = dbc.Row([dbc.Col(bar1, md=6), dbc.Col(pie3, md=6)])
我们现在需要做的就是定义我们的应用程序布局并运行服务器:
app.layout = html.Div([navbar,html.Br(),graphRow1,html.Br(),graphRow2], style={'backgroundColor':'black'})
if __name__ == '__main__':
app.run_server(debug=True,port=8056)
默认情况下,Dash 在端口 8050 上运行。如果正在使用,您可以手动指定要运行它的端口。
我们已经完成了仪表板应用程序的编码。
只需运行 Python 应用程序并导航至仪表盘位置,您将看到您的可视化效果:
作者图片
下面是这个 Dash 项目的完整代码:
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import chart_studio.plotly as py
import plotly.graph_objs as go
import plotly.express as px
external_stylesheets =['https://codepen.io/chriddyp/pen/bWLwgP.css', dbc.themes.BOOTSTRAP, 'style.css']
import numpy as np
import pandas as pd
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
df = pd.read_csv('restaurants_zomato.csv',encoding="ISO-8859-1")
navbar = dbc.Nav()
# country iso with counts
col_label = "country_code"
col_values = "count"
v = df[col_label].value_counts()
new = pd.DataFrame({
col_label: v.index,
col_values: v.values
})
hexcode = 0
borders = [hexcode for x in range(len(new))],
map = dcc.Graph(
id='8',
figure = {
'data': [{
'locations':new['country_code'],
'z':new['count'],
'colorscale': 'Earth',
'reversescale':True,
'hover-name':new['country_code'],
'type': 'choropleth'
}],
'layout':{'title':dict(
text = 'Restaurant Frequency by Location',
font = dict(size=20,
color = 'white')),
"paper_bgcolor":"#111111",
"plot_bgcolor":"#111111",
"height": 800,
"geo":dict(bgcolor= 'rgba(0,0,0,0)') } })
# groupby country code/city and count rating
df2 = pd.DataFrame(df.groupby(by='Restaurant Name')['Votes'].mean())
df2 = df2.reset_index()
df2 = df2.sort_values(['Votes'],ascending=False)
df3 = df2.head(10)
bar1 = dcc.Graph(id='bar1',
figure={
'data': [go.Bar(x=df3['Restaurant Name'],
y=df3['Votes'])],
'layout': {'title':dict(
text = 'Top Restaurants in India',
font = dict(size=20,
color = 'white')),
"paper_bgcolor":"#111111",
"plot_bgcolor":"#111111",
'height':600,
"line":dict(
color="white",
width=4,
dash="dash",
),
'xaxis' : dict(tickfont=dict(
color='white'),showgrid=False,title='',color='white'),
'yaxis' : dict(tickfont=dict(
color='white'),showgrid=False,title='Number of Votes',color='white')
}})
# pie chart - rating
col_label = "Rating text"
col_values = "Count"
v = df[col_label].value_counts()
new2 = pd.DataFrame({
col_label: v.index,
col_values: v.values
})
pie3 = dcc.Graph(
id = "pie3",
figure = {
"data": [
{
"labels":new2['Rating text'],
"values":new2['Count'],
"hoverinfo":"label+percent",
"hole": .7,
"type": "pie",
'marker': {'colors': [
'#0052cc',
'#3385ff',
'#99c2ff'
]
},
"showlegend": True
}],
"layout": {
"title" : dict(text ="Rating Distribution",
font =dict(
size=20,
color = 'white')),
"paper_bgcolor":"#111111",
"showlegend":True,
'height':600,
'marker': {'colors': [
'#0052cc',
'#3385ff',
'#99c2ff'
]
},
"annotations": [
{
"font": {
"size": 20
},
"showarrow": False,
"text": "",
"x": 0.2,
"y": 0.2
}
],
"showlegend": True,
"legend":dict(fontColor="white",tickfont={'color':'white' }),
"legenditem": {
"textfont": {
'color':'white'
}
}
} }
)
graphRow1 = dbc.Row([dbc.Col(map,md=12)])
graphRow2 = dbc.Row([dbc.Col(bar1, md=6), dbc.Col(pie3, md=6)])
app.layout = html.Div([navbar,html.Br(),graphRow1,html.Br(),graphRow2], style={'backgroundColor':'black'})
if __name__ == '__main__':
app.run_server(debug=True,port=8056)
您可以摆弄代码,尝试改变图表的颜色和布局。您甚至可以尝试使用上面的数据集创建新行和添加更多图表。
Dash 有比我在本教程中提到的更多的功能。您可以创建过滤器、卡片,并添加实时更新数据。如果你喜欢这篇教程,我会写另一篇介绍 Dash 更多特性和功能的文章。
感谢您的阅读,祝您有美好的一天:)
本文原载此处。
构建群组分析的数据应用程序
进行群组分析的分步指南,关注技术而不是代码
作者图片
群组分析是一个强大的工具,它使公司能够更好地了解客户的需求和习惯。我们可以通过将具有相似属性(例如,注册日期、首次购买日期、潜在客户来源等)的用户或客户分组来收集有价值的见解。)然后观察关键绩效指标(KPI 例如,参与度、产生的收入、留存)。
下面是一个消费者应用留存的群组分析热图示例。该应用程序的用户被分组到注册月的队列中,他们的保留率是按周观察的。
作者图片
每一个水平行代表一个不同的用户群体,每一个方块代表该群体在注册后一周内的留存率。例如,我们可以看到,在注册 4 周后,该应用对 2018 年 2 月注册的用户有 49%的保留率,而对 2018 年 7 月注册的用户来说,同样的 4 周保留率只有 20%。
研究这种模式有助于发现与业务相关的系统问题,或者反过来,有助于验证随着时间的推移所做的更改。
虽然有许多文章谈到用 python 或 R 或 FORTRAN 执行群组分析,但没有一篇文章关注实际技术。
注意:在这里,虽然所有的分析都发生在 【交集】 (一个让你构建 数据应用 来自动化重复数据任务的平台),你可以用你喜欢的工具或语言来重复这些步骤。
数据描述
所用数据*😗https://archive.ics.uci.edu/ml/datasets/Online+Retail+II
该零售数据集包含一家英国在线零售店在 2009 年 12 月 1 日至 2012 年 12 月 9 日之间的所有交易。该公司主要销售独特的、适合各种场合的礼品。
数据集中的每一行都是客户订购的单独行项目。具有相同发票号(第一列)的行项目是同一订单的一部分。InvoiceDate 是购买的日期/时间,Customer ID 是每个客户的唯一标识符。
数据快照:
作者图片
我们主要感兴趣的是跟踪按月分组的客户保持率,换句话说,对于每个客户分组(按首次购买的月份分组),在首次购买后的每个月中重复下单的百分比是多少?
数据准备
缺少客户 id
数据集包含一些缺少客户 ID 的行。如果我们有办法知道它们代表什么,我们会估算值,但不幸的是,在不了解更多业务的情况下,我们所能做的就是删除这些行。
作者图片
负量
一些订单的数量为负;这样的订单最有可能是退货。返回并不是真正的约定,所以我们也删除了这些行。有人可能会说,我们还应该删除这些最终退货所附带的原始订单。然而:
- 我们不知道如何识别这些订单
- 即使我们这样做了,导致退货的订单仍然是客户的约定,因此对我们的目的有意义
为了减轻这种情况,我们筛选数量> 0 的行的数据。
作者图片
汇总成订单
如上所述,每张发票可以有多行,因为每一行对应一个单独的项目。但是,对于合约,我们感兴趣的是分析订单,而不是订单中的单个项目。我们汇总这些项目来管理不同订单的列表。
作者图片
断代分析
计算每个用户所属的群组
在这一部分中,我们确定了每位客户下第一笔订单的月份(即他们的群体)。
首先,我们按发票日期的升序排序,并计算第一个订单的日期:
作者图片
接下来,我们从该日期中提取月份名称和年份,并连接这些列以形成“群组名称”列。
作者图片
最后,我们将得到的数据集连接到上面的“Orders”数据集,以向其中添加“cohort name”列。
作者图片
生成群组分析表
接下来,对于每个订单,我们计算下订单的周期(订单月份—同期月份)。
作者图片
生成的输出如下所示(注意末尾的 period 列):
作者图片
最后,我们透视我们的表以生成群组分析报告,其中每行是一个单独的群组,每列显示随后几个月的保留情况。
作者图片
在对列和行进行快速排序后,我们得到了群组分析表
作者图片
这向我们展示了每个月每个群体中的回头客数量。但是,为了将这些值转换成百分比,我们将每一列除以第一列,然后乘以 100。
生成热图
最后,我们将表格转换成热图,以快速识别模式。右边的图例告诉我们,盒子越接近红色,保留值越高(越热),反之亦然,保留值越低的盒子越接近蓝色。
大约一半的方块是蓝色的,因为我们还没有这些群体的数据。
作者图片
有趣的见解
马上,我们注意到 2010 年 12 月的队列包含了第 11 个月的高回报(大概对应于第二年的假期)。我们在每个队列中都看到类似的峰值,但是没有一个像第一个队列那么高。这批人有什么特别之处吗?也许,这群人本质上由假日购物者组成,他们更倾向于在每个假日季节回来!
如果你想让用户/顾客再次光顾你的生意,你必须定期研究他们的群体和习惯。
为你的下一个时间序列数据科学项目在云上构建一个数据收集应用
使用 Python 和 PostgreSQL 在 Heroku 上构建和部署数据收集应用程序的分步指南
注:本帖代码可在 这里 找到
计划的数据收集应用程序工作流(图片由作者提供)
尽管近年来观察到数据激增,但我们经常发现自己被项目数据的缺乏所阻碍。在典型的一天网页浏览中,我们会遇到无数的信息(数据),这些信息可能会产生伟大的数据科学想法,如当前天气、交通、灾难预警、股票价格、投票计数、covid 计数、浏览量计数、社交媒体评论等等。我们知道数据得到维护并可供公众使用,但很少有方便的汇编。在本文中,我们将一步一步地介绍使用 Python 中的 RDMS ( 关系数据库管理系统)开发和部署数据收集应用程序。意识到根据收集的数据不同,各个脚本可能会有很大的不同,我们采用的方法是遍历一个简单的示例,同时为每个步骤提供充分的推理:
开发步骤
这个演示应用程序将由一个收集公共可用数据快照的脚本和一个定期调用收集脚本的调度程序组成。应用程序可以选择性地部署在云上,这提供了可靠性和可用性(以及其他好处)。我们选择了 Heroku 进行部署,这是一个平台即服务( PaaS ),使开发者能够完全在云端构建、运行和操作应用。
定义目标和目的
有许多不同的方式来获取数据:API 请求、web 抓取以及介于两者之间的任何方式。项目的第一步是确定可靠的数据源。我们的灵感来自于开放数据,这是一个想法,即一些数据应该免费提供给每个人,让他们随心所欲地使用。它的目标类似于其他“开放(源代码)”运动,如开源软件。有趣的是,美国联邦和地方层面的公共部门在数据透明方面取得了巨大进步。我们使用来自 511.orghttps://511.org/open-data/traffic的数据进行演示。它们为当前交通事件提供了一个方便的 API(参见文档)。 API 密钥需要注册,请求受发行方的费率限制。一旦接收到,响应就会以 JSON 格式出现,我们可以用它来提取有用的数据。我们感兴趣的是捕捉仅具有以下属性的单个交通事件。通过开发一个自动化脚本,我们希望收集所有交通事件的汇编。
one_record={'**id**': '511.org/721616'
'**headline**': 'Caltrans Bridge work...',
'**event_type**': 'CONSTRUCTION',
'**severity**': 'Unknown',
'**lat**': -122.241176,
'**lon**': 38.483586}
创建 GitHub 存储库并设置虚拟环境
Git 是一个免费开源的分布式版本控制系统。它跟踪对项目所在的存储库中的文件所做的所有更改。GitHub 是一个 Git 存储库托管平台,允许开发者为开源和私有项目构建软件。包括 Heroku 在内的许多平台都支持与 GitHub 的集成,从而简化了代码部署。
更多关于 Git 和基本用法的信息可以在 这里 找到。我们将在 GitHub 上为这个项目创建一个远程存储库,并使用$ **git clone "**[**https://github.com/[github_name]/[repo_name].git**](https://github.com/[github_user_name]/[repo_name].git)**”**
将其克隆到我们的本地机器上。使用$ **cd [repo_name]**
更改目录,以确保命令是针对作为根的新存储库执行的。
接下来,我们将为项目创建一个虚拟环境**。虚拟环境的主要目的是为项目创建隔离的环境。这意味着每个项目都可以有自己的依赖项,不管其他项目有什么依赖项。同样,在开发具有特定需求的应用程序时,虚拟环境也很重要。**
虚拟环境
用$ **conda create --name env_name python=3.6**
创建一个新环境,用$ **conda activate env_name**
激活。注意,我们用**python=3.6**
指定了新环境,以包括 pip (在 3.4 中添加了Python的包安装程序)和 f 字符串(在 3.6 中添加了格式化字符串文字**)。在此项目目录/存储库中安装应用程序和运行命令时,应该激活此环境。由于这是一个新环境,所有的依赖项都需要通过$ **pip install**
安装,然后才能导入。使用$ **conda deactivate**
停用虚拟环境。**
我们还需要为应用程序设置环境变量**。环境变量被认为是 操作系统 的一部分,存在于任何特定脚本之外。我们可以使用环境变量来隐藏敏感的凭证,比如 511 API 密钥。它们可以在我们部署时在主机上设置,但也需要在本地可用以进行开发。这些变量应该被限制在项目的范围内,所以我们真正需要的是“临时的”环境变量。幸运的是,我们可以使用python-dotenv从.env
文件中读入键值对,并在运行时将它们设置为环境变量。这允许我们开发我们的脚本,就像环境变量已经设置好了一样。应该将.env
文件添加到.gitignore
中,这样它就不会包含在本地或远程的 git 提交中。**
$ **echo "FIVEONEONE_TOKEN=[api_key]" > .env** $ **echo ".env" >> .gitignore**
环境变量比脚本更安全
开发数据收集脚本和数据库
在这一步中,我们将创建一个脚本来从数据源获取一次数据。在本演示中,我们使用分配的 api_key 向指定的端点发出请求。
fetch.py
我们实现了对请求成功与否的条件检查。一旦收到,响应包含 JSON 格式的数据。具体来说,我们对 JSON 的events
键中的活动交通事件的列表感兴趣。在安装必要的依赖项后,我们可以用$ **python fetch.py**
测试这个脚本:
$ **pip install requests**
$ **pip install python-dotenv**
接下来我们想把数据保存在数据库中,但是我们必须先做一点数据库工程。在这一步中,我们要充分考虑数据的大小以及如何使用数据。一般有两种类型的数据库: SQL 和 NoSQL 。相比之下,SQL 数据库需要更多的工作来建立,但当数据有一个定义的模式时是合适的。因为它与我们的数据相关——假设我们已经确定了想要收集的属性子集,那么使用 SQL 数据库是有意义的。我们将得到一个“事件”表,其中每个行代表一个事件,每个列代表一个感兴趣的属性。
SQL 数据库上的示例事件表
换句话说,从我们收到的 JSON 中的交通事件列表开始,我们将遍历每个元素并解析出我们感兴趣的属性。我们可以使用 SQLAlchemy 库在 Python 中使用 SQL。SQLAlchemy 是一个 Python SQL toolkit 和对象关系映射器,可以实现 SQL 的全部功能。****
在将记录存储到数据库之前,我们必须定义表模式。使用 声明性 将该模式映射到一个 Python 类,因此每个记录将被表示为其实例。
models.py
我们在一个单独的脚本models.py
中布置数据库表的模式,稍后我们可以调用这个脚本来创建表和记录。请注意,SQLAlchemy ORM 要求每个表都有一个主键。
将fetch.py
脚本修改为fetch_insert.py
到 1) 连接到数据库, 2) 基于models.py
创建表, 3) 遍历响应中的每个元素/解析出感兴趣的属性,并且 4) 创建要插入到数据库中的映射 Python 类的实例。
获取 _ 插入. py
我们使models.py
成为依赖**(如from models import *
)来加载模式以创建表和记录。**
使用 SQLAlchemy 从在数据库 url 建立连接开始。当没有DATABASE_URL
环境变量存在时,我们编写脚本来连接到单个文件 SQLite 数据库。我们将 SQLite 用于开发和备份目的,因为它是独立的、功能齐全的。像 Postgres 这样的 DB 服务器通常是首选,因为它支持可用性和可靠性**。一旦我们将生产数据库 url 作为一个环境变量添加,它将优先建立数据库连接。我们应该将数据库 url 视为环境变量,因为它包括用户名、密码、主机名和数据库名。**
[sqlalchemy.create_engine()](https://docs.sqlalchemy.org/en/14/core/engines.html#sqlalchemy.create_engine)
和Base.metadata.create_all()
的典型用法是每个特定的数据库 URL 使用一次,在单个应用程序进程的生命周期内全局保存。单个[Engine](https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.Engine)
代表流程管理许多单独的 DBAPI 连接,并且旨在以并发方式被调用。如果表已经存在,SQLAlchemy 不会尝试重新创建它们。
一旦请求返回了响应,我们就遍历事件列表来收集表记录的数据。在 SQLAlchemy 中,记录作为映射到 SQL 表的 Python 类的实例被添加和操作。**
操作符允许我们获取一个键值对字典,并将其解包为Events()
构造函数的关键字参数。
一旦记录被添加和提交,我们使用session.close()
将资源交还给引擎的连接池。这不应该与engine.dispose()
混淆,后者关闭连接池的所有连接。
至此,我们已经构建了一个脚本来收集数据并将记录存储到 SQL 数据库中。我们可以使用$ **python fetch_insert.py**
(在安装完依赖项之后)测试应用程序,这将创建一个新的 SQLite 文件作为数据库,如果它还不存在的话。
$ **pip install sqlalchemy**
$ **pip install psycopg2**
添加调度程序
我们可以安排数据获取过程按时间间隔运行,从而使我们能够持续收集数据。APS Scheduler(高级 Python 调度器)是一个 Python 库,它让用户调度 Python 代码稍后执行,要么只执行一次,要么定期执行。它易于使用,并与其他有用的框架集成。****
调度程序
很方便,Heroku 在他们的 自定义时钟进程 的实现中使用了 APScheduler。在他们的文档中,他们解释说主要有四个组件来促进作业的调度。在主程序中,用户选择一个调度器和一个作业存储器。调度器负责根据触发器将作业添加到作业库中,而执行器处理作业的运行。
clock.py
使用 APScheduler 调度作业很容易——我们在clock.py
中展示的基本实现从定义一个调度器和作业存储(默认为BlockingScheduler()
和MemoryJobStore()
)开始,它们充当运行应用程序的主要过程。接下来,使用包含调度逻辑的特定触发器添加作业。我们通过用scheduled_job()
和调用数据收集脚本的interval
触发器来装饰 函数来做到这一点。从技术上讲,该过程应该被描述为在计算的延迟之后重新执行。为了管理与数据库的连接,我们在主程序中创建引擎连接,并将其传递给收集脚本。我们必须再次将fetch.py
脚本重构为fetch_job.py
来适应这个实现。
fetch _ 作业. py
数据收集脚本被封装在函数fetch_data
中,因此可以很容易地调用它,并接受数据库连接作为参数。对于每个计划的“获取”作业,在关闭之前,会与数据库建立一个会话,直到所有插入操作完成。在安装新的依赖项后,我们可以用$ **python clock.py**
测试这个应用程序。
**$ **pip install apscheduler****
设置 Heroku 应用程序并添加配置文件
此时,数据收集应用程序以固定的时间间隔“自动”执行,但仅在应用程序处于本地活动状态时执行。我们可以将应用程序部署到 Heroku 来消除这种依赖性。
本次演示避免使用 Heroku 命令行界面 ( CLI )以保持部署简单。首先,在 Heroku 上创建一个新的应用程序。登录( #1 )后,转到“创建新应用”( #3 )并给应用一个唯一的名称以完成创建。Heroku 提供了与 GitHub 的便捷集成,以无缝加载代码进行部署。一旦通过验证( #5 ),开发人员就可以通过名称( #7 )进行搜索,并将应用程序的源代码连接到相应的存储库( #8 )。
设置 Heroku 应用程序
在部署之前,我们必须更新 GitHub 上的源代码,但我们必须首先配置几个设置: Postgres 数据库、配置变量、 requirements.txt 和 Procfile 。
Heroku 应用程序的 Postgres 数据库附件
Heroku 免费提供了一个 Postgres 插件(有限制)——一个单独的数据库比用于开发的 SQLite 本地数据库更好,因为它支持应用程序之外的访问。
在 Heroku 上,进入应用程序主页的资源部分( #1 )。在附加组件下,搜索“Heroku Postgres”(# 2&# 3)。接下来选择适当的计划(Hobby Dev-Free 计划提供多达 10k 行的数据)并进行配置。
Heroku-Postgres 附加软件
Heroku 构型
我们必须将 API 键和数据库 URL 设置为环境变量,以防止它们被暴露。从仪表板页面,导航至设置( #1 )并点击“显示配置变量”( #2 )。数据库的连接字符串自动添加到DATABASE_URL
( #3 )下。这可以用来连接到数据库(在这个项目之外)。我们必须手动添加 api_key ( #4 )作为环境变量( #5 )。
设置配置变量
要将应用程序部署到 Heroku,还需要两个文件:1) requirements.txt
和 2) [Procfile](https://devcenter.heroku.com/articles/procfile)
。这将有助于 Heroku 配置托管应用程序的服务器。我们必须在存储库中手动创建这些文件,它们必须位于根目录下,这样 Heroku 才能在部署过程中找到它们。
- requirements . txt—列出运行应用程序所需的所有依赖项和包。要创建 requirements.txt 文件,运行项目目录中的
$ **pip freeze > requirements.txt**
命令。这将把活动环境中安装的所有库的列表写入 requirements.txt 文件。这有助于 Heroku 为应用程序安装相同的依赖项。 - Procfile —告诉 Heroku 如何运行应用程序,并声明启动应用程序应该执行什么命令。要创建 Procfile 文件,运行项目目录中的
$ **echo "clock: python clock.py" > Procfile**
。请注意,Procfile 是一个没有文件扩展名的简单文本文件。
Procfile 在单独的行上声明应用程序进程类型,每一个都具有格式<process type>: <command>
,其中process type
是原型,从该原型实例化一个 dyno 。Dynos 是提供计算机、内存、操作系统和临时文件系统的容器,旨在根据用户指定的命令执行代码。
鉴于演示应用程序的简单性,一个时钟 dyno 就足够了。注意,对于我们的用例,在 Procfile 中,clock
或worker
流程类型声明都可以。选择使用clock
进程类型是为了表明clock.py
是一个调度应用程序,它是一个单独的进程,不应该扩展到多个进程,以防止任务重复。
另外,在我们的实现中,Dyno 负责调度和执行作业。根据数据收集脚本的复杂性和作业的频率,可能需要使用单独的 worker dynos(不在本项目范围内)。
对于简单的工作,一个时钟 Dyno 就足够了
正确的 Postgres 数据库 URL(3 月编辑)
由于 SQLAlchemy 在 2021 年 3 月弃用了postgres
方言,我们不得不手动更新 Heroku 的 DATABASE_URL 环境变量。不幸的是,它不能在 Heroku 操作系统级别更新,因为它是由附加组件生成的。相反,我们可以给clock.py
添加一个简单的String.replace()
语句来适应这种变化。
**SQLALCHEMY_DATABASE_URI=SQLALCHEMY_DATABASE_URI.replace('postgres://', 'postgresql://')**
部署到 Heroku
一旦创建了两个配置文件( Procfile 和 requirements.txt ),将所有工作文件保存在存储库中,然后将它们添加/提交/推送至远程存储库。
**$ **git add .**
$ **git commit -m 'ready to deploy'** $ **git push****
一旦代码被推送到 GitHub,我们就可以在 Heroku 部署它。在部署页面( #1 )中,选择要部署的分支(# 2)(# 3)。Heroku 将开始构建应用程序。
部署
我们要做的最后一件事是在 Heroku 对 dyno 进行缩放。回到资源页面( #1 ),现在应用程序已经部署完毕,已经识别出了clock
dyno。它需要扩大规模并保持扩大规模,以便保持运行并确定调度作业的合适时间( #2 、 #3 和 #4 )。
将时钟动态缩放至 1
摘要
在本文中,我们逐步介绍了数据收集应用程序的创建。我们还使用 Heroku 将它部署到云中。我希望这对你有帮助。我的希望是激励更多的人分享他们的想法(和数据),这些想法和数据是他们努力为其他人合作策划的。
接下来,我们将创建一个监控工具,并为我们的应用程序构建一个时序机器学习模型。如果你喜欢这篇文章,我邀请你跟我来,这样你就可以得到这个项目继续的通知。
如何用 PySpark & Faker 创建 JSON 数据流
在本教程中,您将学习如何生成 JSON 格式的实时数据流,将文件存储在 AWS S3 桶中,以及使用 Python (Spark)动态聚合数据。
Evgeny Tchebotarev 在像素上拍摄的照片
建议的点播课程
我的一些读者联系我,要求提供点播课程,以了解更多关于Apache Spark的信息。这是我推荐的 3 个很好的资源:
问题:生成流数据集
如果您熟悉 PySpark 及其结构化流 API,您就会知道将流作业表示为标准批处理作业是多么容易,不同之处在于数据流可以被视为一个不断追加的表。
尽管以简单明了的方式编写了一个流处理模型,但是寻找流数据源可能是一项具有挑战性的任务,尤其是在部署应用程序之前测试应用程序,或者在学习 PySpark 时设置第一个流作业。
为了让你的工作生活更轻松,学习过程更快,在本教程中,我将向你展示如何使用 Python 的faker
和random
包产生一个模拟数据流,以 JSON 格式序列化。
此外,您将学习如何将数据流保存在 S3 桶中(免费),然后使用 PySpark 动态读取和聚合数据流。所有这些步骤只需要 5 分钟。
先决条件和安装
为了快速完成这一过程,在您进一步阅读之前,我建议您:
- 在 AWS 上创建一个免费账户以及一个 IAM 用户。记得把
ACCESS KEY ID
和SECRET ACCESS KEY
都留着以后用。设置您的 IAM 用户,使其拥有一个AmazonS3FullAccess
。 - 安装
boto3
包并创建一个配置文件,以编程方式使用上面生成的两个密钥。 - 访问 Databricks 的社区版,然后配置一个标准集群并将其连接到您将运行 PySpark 代码的笔记本上。即使您以前没有使用该平台的经验,这项任务也应该相当直观。
- 安装剩余的软件包,其中有
faker
、random
、uuid
和json
。
如果是第一次在S3
创建 IAM 用户,以及config
文件,建议先看一下本教程的第一段。
一旦你完成了这些安装(大约需要 30-45 分钟),你就可以开始生成一个模拟数据流了!
创建您的数据生产者
第一步是使用以下命令创建一个带有boto3
(在本例中为data-stream-dump
)的S3
桶:
然后,我创建一个名为Transaction
的data class
( 如果数据类和装饰器对你来说是一个新概念,请看 本教程 ),它由 3 个字段组成:
username
是一个字符串,将通过调用faker.user_name
随机生成;currency
是一个字符串,在属于currencies
列表的字符串中取一个随机值。我将货币限制为 3 种,以使稍后在 PySpark 中执行的聚合更加明显,但是您也可以使用faker.currency
生成随机货币。amount
也是一个字符串,被定义为介于100
和200,000
之间的随机整数。
我现在可以使用Transaction
来定义一个serialize(self)
方法(是的,您可以将方法定义为数据类的一部分),它有效地用于将这 3 个字段格式化为一个字典:
现在每次我打电话:
print(Transaction().serialize())
我得到一个假的银行交易,由一个假的username
在一个假的currency
和一个假的amount
中创建:
{‘username’: ‘jjohnson’, ‘currency’: ‘GBP’, ‘amount’: 102884}
使用数据类和 faker 包很酷的一点是,您可以不断增加或改变字段的数量和类型,以满足您的特定需求。
使用数据类和 faker 包很酷的一点是,您可以不断增加或改变字段的数量和类型,以满足您的特定需求。
我终于准备好定义一个Producer()
函数,并将其传递给一个运行 100 次的循环:
每次调用Producer()
函数时,它都会以json
格式将单个事务写入一个文件(上传到 S3 ),该文件的名称采用标准根transaction_
加上一个uuid
代码,以使其唯一。
在这种情况下,循环将生成 100 个文件,每个文件之间的间隔为 3 秒,以模拟真实的数据流,其中流应用程序监听外部微服务。
完整的代码,直到这一点是可用的在这里。我将很快编译它,但在此之前,我需要先创建一个流作业。
在数据砖的 FS 中安装你的 S3 桶
我的流作业将写在 DataBrick CE 笔记本中,如下所示:
如果您希望您的流作业监听 S3 存储桶中发生的事情,您需要将您的亚马逊 S3 存储桶作为文件系统“挂载”。
挂载是指你可以使用 Databricks 的平台“ ”与亚马逊 S3 桶进行交互,对文件和文件夹 进行读写操作。
有一些关于如何在 Databricks 中挂载文件系统的文档,但是它们让您相信只有获得高级许可才有可能。原来,在这篇伟大的文章 中,Sneha Mehrin 找到了一个非常简单的方法来解决这个问题。
你需要做的一切,就是在你的笔记本上运行下面的代码(记住使用你在本教程开始时保存的 2 个键):
如果您没有得到任何错误,这意味着您的 S3 文件系统已经正确挂载,但是,因为您的 bucket 仍然是空的,所以您不会通过运行以下命令看到任何文件:
%fs ls /mnt/s3Data/ # --> where /s3Data is the folder name
相反,如果您很好奇并且已经运行了Producer()
函数(希望少于 100 次迭代:D ),您将会看到这种类型的文件结构:
一旦生成器运行,您将看到装载的 bucket 的文件结构。
最后,用 PySpark 写一些代码就都设置好了!
创建 Spark 会话并定义模式
首先,我定义了一个SparkSession
( 在 Databricks CE 中不是必需的,但是很好的实践),然后我手动声明了在使用readStream
读取文件时必须提供的数据流模式(JSONschema
):
请注意,Producer()
函数已经将字典转换为 JSON 文件,因此在 PySpark 中,金额类型现在被读取为LongType()
整数。
消耗数据流并在内存中输出
下面我要做的是读取挂载文件夹中以transactions
根目录开始的所有文件,然后将它们保存到ds
DataFrame:
如果ds.isStreaming
为True
,则表示流处理处于活动状态。
然后,我希望通过currency
计算总数amount
,因此我只从ds
中选择这两个字段,并执行简单的聚合(将其保存到trxs
最终,我只需要开始将trxs
写成一个流,并保存在memory
(对于本教程的范围来说已经足够了)。因为我正在执行聚合,所以我必须使用complete
作为outputMode
。注意,写入存储器的流被命名为transactions_stream_output:
现在,我要做的第一个操作是运行 PySpark 代码,开始读写一个流。这是因为它可能需要 30 秒,所以在此期间你可以跳到别的东西上。
我要执行的第二个操作是运行第一个生成数据的 Python 脚本。当您这样做时,您会看到屏幕上出现编号的迭代:
您将看到每 3 秒钟出现一次迭代(PyCharm)
此外,如果您检查您的 S3 用户界面,您会看到文件被写入桶:
用事务文件填充的 S3 时段。
现在回到数据砖笔记本。希望您应该看到流处于活动状态,并且随着新文件写入 S3 并被流作业读取,图形会不断变化:
Databricks 的数据流仪表板。
PySpark 的一个很酷的特性是,您可以使用 SQL 轻松查询由流作业创建的内存表。如您所见,每次在 S3 写入新文件并由流式作业处理时,表中保存的聚合都会更新:
查询 1:在执行了 10 次迭代之后。
查询 2:在执行了 20 次迭代之后。total_amount 持续增长!
结论
恭喜你!如果您已经做到了这一点,那么您已经成功地在 PySpark 中构建了一个工作数据流作业,该作业从一个 S3 桶中读取 JSON 文件(,每个文件包含一个模拟银行事务,并将它们写入内存表中。
既然您已经知道如何构建一个动态数据类(使用field()
和defaulty_factory
参数),那么您可以扩展这个概念并创建自己的流数据集。极限只是你的创意!
如果你觉得这个教程有用,请留下一些掌声,或者如果你想阅读更多类似主题的教程,请跟我来:D
使用 Arduino、Python 和 Streamlit 构建一个 DIY 迷你雷达
如何建立一个廉价的迷你雷达系统与现场仪表板
迷你雷达仪表板。作者视觉。
介绍
对于我们这些从事机器人行业的人来说,特别是像相扑和机器人足球机器人这样的竞争型机器人,我们知道检测物体以避免碰撞是不可能被夸大的。虽然你经常会用大量的红外传感器来探测周围的物体,但事实上,你可以用更少的资源建造自己的迷你雷达来达到同样的效果。在这里,我将向您展示如何使用红外测距传感器与伺服电机耦合来创建一个 DIY 迷你雷达,以及如何通过令人眼花缭乱的仪表盘实时显示结果。
1.阿尔杜伊诺
首先,我们将使用 Arduino Uno 板从锐距离测量传感器读取距离值。随后,我们将使用双面胶带将传感器安装到伺服电机的臂上,并如下所示连接电线。
Arduino,红外测距传感器和伺服电机—图片由作者提供。
一旦您连接了传感器和伺服系统,请继续将以下程序上传到 Arduino。
请注意,根据您使用的传感器和电源电压,您可能需要重新校准传感器。电流传感器可以读取 20-150 厘米之间的值,如有必要,通过读取已知距离的传感器测量值,并使用该测量值推导出一个新的公式,实验性地重新校准电机。
要验证 Arduino 和传感器是否按预期工作,请打开串行监视器(“工具”>“串行监视器”),确保记录值并通过串行端口发送,如下所示。
Arduino 串行监视器—图片由作者提供。
2.计算机编程语言
既然 Arduino、传感器和伺服系统正在工作并将值发送到串行端口,我们需要获取 Python 中的读数来生成我们的雷达仪表板。要通过串行 USB 连接将 Python 脚本与 Arduino 接口,我们需要下载并安装 Pyserial。继续启动 Anaconda 或您选择的任何其他 Python IDE,并键入以下命令:
pip install pyserial
为了生成一个图形用户界面,我们可以可视化我们的仪表板并与之交互,我们将使用 Streamlit 。这个高度通用的 web 框架允许您快速开发应用程序并将其部署到 web 服务器上,或者在您的浏览器上本地运行它们。为了显示传感器读数的雷达图,我们将使用 Plotly 。这是 JavaScript 的高度交互式数据可视化框架的 Python 绑定,允许您在指尖渲染令人眼花缭乱的视觉效果。
继续将以下源代码保存在本地目录中:
上述代码将启动到 Arduino 的连接,然后将从串行端口连续读取雷达位置和传感器值,直到被提示停止。它还会不断渲染和更新雷达图。
要运行上述脚本,请在 Anaconda 提示符下键入以下命令:
cd C:/Users/.../local_directory
streamlit run python_radar.py
结果
这就是你要的,一个自己动手做的迷你雷达!
如果您想了解更多关于数据可视化和 Python 的知识,请随时查看以下(附属链接)课程:
使用 Streamlit 开发 Web 应用程序:
使用 Python 实现数据可视化:
面向所有人的 Python 专业化:
物联网专业化编程简介:
GitHub 资源库:
https://github.com/mkhorasani/arduino_python_radar
新到中?您可以在此订阅并解锁无限文章。
建立假新闻检测器
是时候粉碎错误信息了
作者图片
如果冠状病毒教会了我一件事,那就是没有人能幸免于这种危险的病毒。它在世界的某些地方成倍增长,让我们担心和害怕我们所爱的人和我们自己的身体健康。但是你有没有花一点时间停下来想想错误的信息会如何影响你?你根据这些数据做出的决定?事实上,虚假新闻传播或“病毒式传播”的可能性高达 70%[1]。想象一下,有些决策是基于这种虚假的病毒式新闻做出的。令人欣慰的是,人工智能在对抗错误信息方面取得了一些重大进展。
在本文中,我们将使用 python 构建我们自己的假新闻检测器。我们将利用一些测试数据和代码,使用被动积极分类器(PAC)用我们的数据训练模型,该分类器将确定一段文本是真还是假。PAC 背后的主要概念是,它占用的内存较少,并且使用每个训练样本来重新评估训练算法的权重。预测正确,保持不变,预测错误,调整算法。它是一种流行的机器学习算法,特别是在做假新闻检测等工作时。其背后的数学并不简单,所以我们将把它留给另一篇文章。在本文中,让我们构建一个实际的应用程序。准备好了吗?我们开始吧!
To see a video demonstration of this exercise check out the youtube video with code walkthrough at [https://youtu.be/z_mNVoBcMjM](https://youtu.be/z_mNVoBcMjM)
为了开发这个应用程序,我们将利用两个流行的 python 包,一个是 pandas ,另一个是 sklearn 。完整的代码和数据可以在我的 g ithub 链接找到。所以让我们从导入我们的需求开始
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import PassiveAggressiveClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.model_selection import cross_val_score
一旦我们导入了需求,下一步就是导入数据集。我们从 kaggle 得到的数据和直接链接可以在上面链接的 github 链接上找到。
df=pd.read_csv('fake-news/train.csv')
conversion_dict = {0: 'Real', 1: 'Fake'}
df['label'] = df['label'].replace(conversion_dict)
df.label.value_counts()Fake 10413
Real 10387
Name: label, dtype: int64
一旦我们将数据导入 pandas 数据框架,我们将根据样本数据重新标记数据的真假。结果表明,我们有一个相当均匀和平衡的数据集,不需要在真实和虚假评论之间进行任何采样。所以我们不必担心不平衡的数据集。咻!少了一件需要担心的事情!
然后,我们获取数据集,创建一个训练测试分割,并保留一定百分比进行测试,在本例中为数据集的 25%。测试数据将由作为 X 变量的实际文本和作为 Y 值的标签组成。为了确保我们只使用关键字,我们还从算法中删除了所有停用词。
x_train,x_test,y_train,y_test=train_test_split(df['text'], df['label'], test_size=0.25, random_state=7, shuffle=True)
tfidf_vectorizer=TfidfVectorizer(stop_words='english', max_df=0.75)vec_train=tfidf_vectorizer.fit_transform(x_train.values.astype('U'))
vec_test=tfidf_vectorizer.transform(x_test.values.astype('U'))pac=PassiveAggressiveClassifier(max_iter=50)
pac.fit(vec_train,y_train)PassiveAggressiveClassifier(C=1.0, average=False, class_weight=None,
early_stopping=False, fit_intercept=True,
loss='hinge', max_iter=50, n_iter_no_change=5,
n_jobs=None, random_state=None, shuffle=True,
tol=0.001, validation_fraction=0.1, verbose=0,
warm_start=False)
然后,我们用数据训练模型,运行 50 次迭代,并使其符合 PAC。太好了,现在我们有一个训练有素的分类器。接下来让我们测试我们的训练模型的准确性。我们运行 y 测试和 y_pred 准确度分数,看到我们得到了一个不错的分数。96.29%.太好了!
y_pred=pac.predict(vec_test)
score=accuracy_score(y_test,y_pred)
print(f'PAC Accuracy: {round(score*100,2)}%')PAC Accuracy: 96.29%
为了进一步确保我们的模型预测正确的行为,我们查看混淆矩阵,以帮助我们更好地了解我们在结果集中看到多少真阴性和假阳性。
confusion_matrix(y_test,y_pred, labels=['Real','Fake'])array([[2488, 98],
[ 95, 2519]])
基于上面的混淆矩阵,结果看起来相当不错。在大多数情况下,它准确地预测了正确的结果。好吧,但是我们也不要就此打住。让我们针对这些结果运行黄金标准测试,这是一个 K 倍精度测试。
X=tfidf_vectorizer.transform(df['text'].values.astype('U'))
scores = cross_val_score(pac, X, df['label'].values, cv=5)
print(f'K Fold Accuracy: {round(scores.mean()*100,2)}%')K Fold Accuracy: 96.27%
这导致了 96.27%的 k 倍测试分数。好的,我对这个结果很满意。好了,现在让我们向这个模型展示一些它从未见过的数据,看看它在预测正确标签方面表现如何。
df_true=pd.read_csv('True.csv')
df_true['label']='Real'
df_true_rep=[df_true['text'][i].replace('WASHINGTON (Reuters) - ','').replace('LONDON (Reuters) - ','').replace('(Reuters) - ','') for i in range(len(df_true['text']))]
df_true['text']=df_true_rep
df_fake=pd.read_csv('Fake.csv')
df_fake['label']='Fake'
df_final=pd.concat([df_true,df_fake])
df_final=df_final.drop(['subject','date'], axis=1)
df_fake
在上面的代码块中我们引入了两个新的数据集,df_true 表明我们都是真实的新文档,df_fake 表明我们都是虚假的文章。我想把这些文章放入我们训练好的分类器,看看它对 df_true 数据集预测为真的次数,以及对 df_fake 数据集预测为假的次数的百分比。
为此,让我们构建一个快速函数,该函数将返回模型的一个不可见数据集的标签
def findlabel(newtext):
vec_newtest=tfidf_vectorizer.transform([newtext])
y_pred1=pac.predict(vec_newtest)
return y_pred1[0]findlabel((df_true['text'][0]))'Real'
在这个函数中,我们传递一些数据集,它将根据我们的 PAC 训练算法计算数据集是真是假。然后,我们运行 df_true 数据帧中的第一个文本文档,希望它能预测 real,在本例中就是这样。我想看到的是它准确预测正确数据集的时间百分比。
sum([1 if findlabel((df_true['text'][i]))=='Real' else 0 for i in range(len(df_true['text']))])/df_true['text'].size0.7151328383994023sum([1 if findlabel((df_fake['text'][i]))=='Fake' else 0 for i in range(len(df_fake['text']))])/df_fake['text'].size0.6975001064690601
好了,我们已经循环了 df_true 数据集中的数据,并注意到它在 71.5%的时间里准确地预测了“真实”。当我们查看 df_fake 数据集时,它准确预测 69.75%为“假”
对我来说,这实际上是一个非常有趣的项目,我也非常喜欢做这个项目。虽然训练结果看起来很惊人,但实际测试数据与看不见的数据相比并没有那么高,但是嘿,如果我能阻止来自互联网的错误信息大约 7/10 次,我认为这是一个胜利。
希望您喜欢这个代码演练。请务必登录 levers.ai 查看我们,了解更多关于我们服务的信息。
使用 Spotify API 构建一个 Flask-Heroku 情绪跟踪器 Web 应用程序
实践教程
A-Z 项目根据我每天听的歌曲生成情绪得分,并相应地给我的在线情绪跟踪器着色
下面先睹为快的 最终结果 !
子弹日记是我的主要爱好之一。浏览上百个 Instagram bujo 账号,每个月我最期待的一件事就是 bujo 艺术家的情绪追踪器模板。这些色彩缤纷的情绪拼贴画奠定了我这个项目的基础。当我在设计下个月的情绪跟踪器时,我的脑海中闪过一个想法,如果有一个系统可以自动检测我们的情绪,并帮助我们每天填充单元格,那将是非常棒的。
好的,数据呢?
音乐一直是我日常生活的重要组成部分。我的音乐和我的情绪之间的关系通常是线性的:当我开心的时候,我会听欢快的歌曲;当我悲伤的时候,我会听民谣;当我压力过大的时候,我会听治愈的节奏。因此,我决定使用我在 Spotify 上听的歌曲,特别是它们通过 API 提供的属性,如声音、舞蹈性等,作为歌曲快乐水平的指标,以及随后我的整体情绪。
头脑风暴出了一个总体的项目想法后,我开始起草一份管道组件的清单,确定任务应该完成的顺序以及我应该如何处理每一项任务。下图记录了我的思考过程和每个阶段的发展计划/进度:
帮助我跟踪项目进度的工作流程——按作者分类的图片
赫罗库
Heroku 是一个云平台,为 Webapp 的基本托管提供免费选项,因此它适用于小规模的个人项目。我们只需要一个账户!
注册后,导航到你的个人仪表盘,点击“新建”创建一个新的应用程序(我的应用程序叫做心情日志)。转到您的应用程序,在“部署”下,选择 Heroku Git 作为您的“部署方法”,并按照下面列出的步骤将您的项目(目前为空)克隆到您的本地工作环境。我们将在此文件夹中添加文件,然后推送更改。就这样,让我们开始这个项目吧!
决定模型
首先,我需要一个模型来根据我的音乐列表产生上述情绪得分。确定合适的模型需要后退一步,关注最终的结果。我相信好的模型来自于专注于你的最终目标;一个更复杂的模型不一定能为一个人的问题提供最好的解决方案。
如上所述,我的快乐程度是由一首歌听起来有多快乐来定义的。因此,我需要一种算法,给有快乐氛围的歌曲分配较高的分数,否则分配较低的分数。K-Means 是一种经常用于歌曲分析的无监督方法,它根据歌曲的相似性对歌曲进行分组,但这对我的任务没有帮助,因为我已经记住了两个类别。用于歌词分析的深度学习模型也是一种选择,但歌曲中的句子通常很短,重复且有隐藏的含义。另外,歌词不是我听歌的主要因素,节奏才是。
通过不同选项的循环,我得出结论,我可以用二元逻辑回归模型获得想要的结果。如果我使用两个非常不同的列表来拟合模型,一个是乐观快乐的歌曲,另一个是民谣式的忧郁旋律,那么我可以产生一个概率预测,任何给定的歌曲离快乐歌曲有多近。这个概率可以作为我的*情绪得分。*取一天中所有歌曲的所有情绪得分的中间值,我将准备好最终得分!
训练逻辑回归模型
从训练数据来看…
特别感谢 Sejal Dua 的详细指南使用 Spotify API 抓取歌曲,使这项任务变得前所未有的简单!Sejal 的程序接受 Spotify 的播放列表 id 列表,并返回所有歌曲属性的数据帧,准备用于训练模型。我将 Spotify 制作的快乐合辑(情绪助推器、快乐节拍等)和悲伤合辑(悲伤时刻、生活糟透了等)输入到这个功能中,这些合辑包含了这个类别中非常有代表性的歌曲。全套大约有 1000 首歌曲。
我用交叉验证训练了这个模型,它的准确率达到了 0.89!我还在包含悲伤和快乐歌曲的个人播放列表上使用了 predict_proba ,分配给歌曲的概率估计模型很好地反映了每首歌曲的快乐水平。然后,该模型被处理以用于我们的应用程序。
import picklepickle.dump(lr, open("deploy_model.sav", 'wb'))
…预测
为了进行预测,我需要收集一天中我听的所有歌曲。在 Spotify API 提供的所有功能中,current _ user _ recent _ played是最接近我们的目标结果的功能。然而,它仅限于在一次调用中返回最近的 50 首歌曲。因此,我不得不编写一个小程序来收集过去 24 小时内的所有歌曲(在我的手写笔记上也可以找到如何工作的说明)。
每当我调用这个函数的时候,在所有曲目旁边,它还会返回一个 光标 ,告诉我我听的第 50 首歌的时间戳(以毫秒为单位)。例如,如果我在过去 24 小时内听了 120 首歌曲,那么第 50 首最近的歌曲是,比如说,5 小时前。这个想法是一次获取一批 50 首歌曲,提取这个光标,将起点设置到这个光标,然后重新开始获取接下来的 50 首歌曲。一旦我得到的批次的光标超过 24 小时标记,该过程就停止了。在这一点上,我必须调用一个 get_right_limit() 函数,该函数确定从最后一个光标开始我听了多少首没有超过 24 小时的歌曲。代码片段如下:
get limit 函数如下:
一旦我得到了所有的歌曲,剩下的就是提取这些歌曲的特征,就像我在上一节中所做的那样。之后,我用 pickle.load() 加载 pickle 模型,并使用 predict_proba 生成分数,我将把它作为一个名为“prediction”的列添加到我们的数据帧中:
mixed = get_features_for_playlist2(mixed, curr_time, curr_time - 86400000) # 86400000 is 1 day in millislr = pickle.load(open('models/deploy_model.sav', 'rb'))vnpred = lr.predict_proba(mixed.drop(['name', 'artist','track_URI'], axis=1))[:,1]mixed.insert(3, 'prediction', vnpred)# take the median score
day_median = float(median(mixed['prediction']))
存储结果
我还需要一种存储方式,当上面的程序运行时,它会得到更新,Flask 应用程序可以很容易地从中检索和使用结果。这里我使用了一个 SQL 表。每天存储和更新结果的代码是:
将我的结果存储在 schema.sql 中
值得注意的是,我必须创建一个 31 行的表,因为 Flask 应用程序将检索所有 31 个值来查找图形的颜色。表中的所有值都被初始化为 0。每当我们转向一个新的月份,该表将被重置:所有旧的值将被清除并替换为 0。
去弗拉斯克和赫罗库!
烧瓶环境
如果你以前没有用过 Flask,这里的是一个快速而有用的安装和入门指南!我们的 flask 应用程序的所有必要文件的列表可以在上面的我的计划器中找到。
此外,为了将您的应用程序推送到 Heroku,我建议使用 pipenv——一种创建虚拟环境的工具——因为它很好地锁定了您的应用程序的所有必要的依赖库(并且比使用 requirements.txt 文件给我带来的麻烦更少!).为了使用它,使用 pip install pipenv ,然后键入 pipenv shell 来激活环境。在这种环境下,请执行*pipenv install[list of package _ name]*来安装您的程序所需的软件包。最后, pipenv lock 完成安装后,将所有这些需求锁定到您的应用程序。在将应用程序推送到 Heroku 之前,如果你安装了任何额外的包,你需要再次键入这个命令,否则部署会给你错误,说没有这样的模块。
SQL 烧瓶(app.py)
现在我已经将现成的结果存储在 schema.sql 中,我继续将它加载到我的 Flask 应用程序中。在 app.py 中,为了方便地从表中提取信息,我使用了一个名为 Flask-SQLAlchemy 的包。首先,我必须用下面的语法创建引擎和声明模型:
main.htmlJavaScript
“模板”文件夹中的 main.html 文件被我们的 Flask 应用程序用来创建网站的界面。但是为了绘制图形和定制颜色,我们需要 Javascript 和 CSS。烧瓶要求我们把。js 和。css 文件放在另一个名为 static 的文件夹中。我使用 Javascript 的 Canvas API 和 R ainbowVis 包来制作图纸。这一阶段需要大量的试验和错误,然后才能得到最终产品,但这也是最有趣的部分。 RainbowVis 包提供了一个工具来生成一个十六进制数字数组,表示从蓝色(=悲伤)到绿色(=中性)到黄色(=快乐)的 1000 种颜色的渐变。这个想法是将情绪分数四舍五入到小数点后 3 位,乘以 1000,并得到该指数处的十六进制颜色。
因此,我必须将分数从 app.py 传递到 html 文件,再传递到 javascript 文件。经过一轮谷歌搜索,我想到了解决方案:
- 为了将分数从 app.py 传递给 main.html,我使用 Flask-SQLAlchemy 函数来查询分数,或者“结果”列,并通过 render_template()将其作为数组传递给 html。我把这个函数放在默认路径下,因为我希望当我们访问首页时,这些图形能够正确地呈现。
[@app](http://twitter.com/app).route('/', methods=['GET'])
def main():
table = Mood.query.with_entities(Mood.results).all()
table = [r for r, in table]
# table is currently storing the mood scores
return render_template('main.html', results = table)
-
分数现在存储在 html 中一个名为 results 的数组中。为了获得数组中的信息,我们必须使用双花括号。我们可以使用***console . log({ { results } })***来确认数组成功传入。
-
为了将分数从 main.html 传递到 canvas.js ,我将 canvas.js 中的代码包装在一个名为 draw() 的函数中,该函数接受一个参数:值数组。在 HTML 内部,我调用 draw()并将情绪得分表传入。代码如下。注意,Flask 的 HTML 文件在语法上与普通的 HTML 文件略有不同,包括 src 和 href。
-
现在在 canvas.js 中,您可以使用普通的数组索引来访问结果中的值!
至此,我们已经完成了应用程序中不同组件之间的链接设置!如果你输入 flask run 我们可以看到我们的应用程序在本地主机上运行。现在剩下的,就是把它推到 Heroku 身上,让其他人也能看到!
烧瓶 Heroku
按照 Deploy 下的后续步骤,添加、提交和推送我们的文件到 Heroku(记住在执行这个步骤之前要 pipenv lock)。您的终端应该显示如下内容:
如果你访问这个网址,当你运行 *flask 时,它会显示出你在本地电脑上的 Flask 应用程序上看到的准确状态。*瞧!
计划每天运行:
最后一步是使程序自动化,以便它在每天结束时运行并相应地更新图表。为此,我编写了一个 crontab 脚本,它在每晚 11:59 运行 clock.py,用当天的新分数更新 SQL 表,然后将整个项目重新推送到 Heroku。
注意,为了让 crontab 在 macOS 上正常工作,您必须授权它使用您的本地文件夹。进入系统偏好设置->安全和隐私->全磁盘访问。单击下面的锁图标可以进行更改。单击“+”号,然后转到路径“/usr/sbin/cron”。选中 cron 的复选框,再次点击锁以锁定更改。我们都准备好了。
键入 crontab -e 开始编写 crontab 脚本:
59 23 * * * cd /Users/irenechang/Desktop/PORTFOLIO/Fall2021/mood-journaling && /opt/anaconda3/bin/python /Users/irenechang/Desktop/PORTFOLIO/Fall2021/mood-journaling/clock.py && /usr/local/bin/git add /Users/irenechang/Desktop/PORTFOLIO/Fall2021/mood-journaling/. && /usr/local/bin/git commit -m "commits" && /usr/local/bin/git push heroku HEAD:master
搞定了。我们的程序每天都会更新!
最终结果—作者提供的图片
结论:
这是一个有趣的从想法到部署的 ML 项目,我开始这个项目的目的是获得使用 Heroku 和 Flask 的实践经验,并让自己熟悉数据科学模型的 A-Z 管道。希望你会像我一样喜欢这篇在线子弹日志揭示的对你日常情绪的洞察。此外,我希望这能为您的个人项目提供有益的指导,尤其是在部署模型和找出多个不同元素之间的联系时。
非常感谢您的阅读!我的代码可以在这个 repo 中找到!如果您有任何进一步的想法或反馈,请随时通过我的 LinkedIn 联系我,我很乐意与您联系!
参考资料: