使用 Voila 创建交互式 Jupyter 笔记本并部署在 Heroku 上
使用 Python、Pandas 和 Plotly 分析 Covid 推文
作者图片(使用谷歌绘图创建),结果的折叠视图
最近,我被指派分析一个包含大约 44k 条目的 Covid tweets 数据集,这篇博客描述了我为创建一些漂亮的可视化效果以及最终部署到云平台 Heroku 所采取的所有步骤。部署了整个 Jupyter 笔记本,消除了任何前端编码的需要。
数据集
数据包含日期时间列,这是索引的最佳选择,因为我可以根据日期数据对数据及其子集进行分组。下面是 df.info()返回的内容,考虑到 df 是熊猫读取 CSV 文件的数据框对象:
数据集的所有功能
有 19 列(索引被设置为“created_at ”,它最初是数据集的一部分),除了 hashtags 和 user_description 列之外,大多数列都有非空值,这是有意义的,因为不是每个人都用 hashtags 发推文,也不是所有的推文都有个人资料描述。
经过一段时间的摸索,我发现 language 列只包含’ en ',因此我放弃了它。类似地,“id”、“tweet_url”、“user_screen_name.1”也被删除。“用户屏幕名称”和“用户屏幕名称. 1”是相同的列。
数据预处理
略述之后,是时候将数据转换成更好的形式了:
时区转换:我住在印度,我的时区是 GMT+5:30。数据集具有 UTC 时区,这使得执行日期时间分析变得困难。例如,如果不进行转换,推特的高峰时间是早上 6 点,但实际上是 11 点。我是这样做的:
UTC 到 IST 的转换
**清理推文:**推文通常包含标签、提及、链接和照片。从 Twitter API 获取数据时,照片被替换为一个链接。所有这些额外的材料都需要被移除,以获得良好的词云和高效的情感检测。我创建了以下函数来消除这种干扰,停止单词和标记化:
Tweets 清理功能
**获取地点的坐标:**有一个名为“地点”的栏目,告诉我们这条推文是从哪里发出的。这给了我一个在地图上绘制它们的绝佳机会,但唯一的问题是我需要可以传递给任何 Mapbox 库的这些地方的准确纬度和经度。经过一番搜索,我找到了一个网站,通过请求他们的端点来提供坐标。这是一个简单的函数:
函数来获取任意地址的坐标
制作交互式可视化
在最后一个笔记本里,我做了很多可视化,在这里描述每一个都要花很长时间。我会解释其中一些不同之处。所有的图表、数字都是用 Plotly 制作的。
**图表:**生成表示定性数据计数的静态条形图要容易得多。Plotly 为图表提供了广泛的定制。为了提高交互性,我使用 ipywidgets 来获取用户输入并控制图表的流程。例如,为了以条形图的形式显示 tweets 排名靠前的地方,使用了以下代码:
px 是 plotly express 的别名。
displayPlaces 函数采用参数“top ”,该参数控制绘制条形图时要考虑多少个前 X 位。interact 函数是 ipywidget,它自动创建用户界面控件。更多功能参见文档。下面的 gif 是为了更好的理解:
显示条形图交互性的 GIF
**地图绘制:**我个人喜欢这种可视化,因为它挑战我探索更多关于 Plotly 的东西。最初,我用 leav 绘制了这个图,结果令人满意,但是用 Plotly,你可以在底图上添加更多的图层,让它看起来更惊人。获取坐标是预处理时解决的一个大任务。以下是获取地理地图的步骤:
px.scatter_mapbox 获取位置坐标、可选的色标、缩放和高度参数。update _ layout map box _ style“carto-dark matter”使地图变暗。查看生成的地图:
表示地图绘图的 GIF
更多类型的绘图可参考 Plotly 文档。
**单词云:**这些是用一串单词描述文本信息/段落中整体趋势的图像。这些图片乍一看很酷,但大多数都是带有文字的矩形图像。为了使它更吸引人,我们可以把这些话放在图片上!没错,你可以用一个图像作为蒙版,这些文字将被强加于其上,它的实现更容易。将图像作为 numpy 数组加载,并将其作为遮罩参数传递给 word cloud 函数。词云函数来自库词云。
示例单词云
我加大了赌注,让它以用户为基础。用户可以选择屏蔽(可用选项:Modiji、Trump 和 India Map)和语料库(tweets 数据或用户描述),从中挑选单词。
部署 Jypter 笔记本
如果你读过我的“分析 WhatsApp 群聊&构建 Web 应用”的文章,那么你一定知道我必须为这个项目创建整个前端,如果知道就好了。
Voila 是一个 Python 包,它可以将我们的 Jupyter 笔记本变成一个独立的 web 应用程序。这意味着笔记本将作为网站呈现给用户,并且笔记本的单元格将被优先执行。它还提供了选择主题的功能,我选择了深色。下面是本地测试 voila 的过程:
pip install voila
pip install voila-material (optional)
安装后,只需在任何终端中运行该命令(假设您导航到了保存笔记本的文件夹,否则请输入笔记本名称的完整路径):
voila nameofnotebook.ipynb
然后嘣!你有一个网站运行在你的本地主机上,也就是你的笔记本上!您可以在笔记本中添加 HTML 代码作为 markdown,使其更具创造性。要更改网站的主题,只需在运行 voila 命令时传递 theme=dark:
voila --theme=dark nameofnotebook.ipynb
要将它部署在云上,以便其他人可以查看您的项目,有许多选择:
Heroku :只需在 Heroku 中创建一个新的应用程序,连接您笔记本所在的 GitHub repo。在 Procfile 中,可以使用此命令来避免任何错误:
web: voila --port=$PORT --no-browser --template=material --enable_nbextensions=True nameofnotebook.ipynb
活页夹:更简单的方法是使用名为活页夹的服务,它会为你的回购创建一个 docker 映像,每次你想运行你的笔记本时,你只需在活页夹上启动它。
截图来自 mybinder.org
这里要提到的一件重要事情是,在笔记本文件的路径中,从下拉列表中选择 URL,然后将 voila 端点粘贴为:
voila/render/path-to-notebook.ipynb
*“请注意,如果您的笔记本使用任何外部库,您的 Git 存储库中需要有一个 requirements.txt 文件,该文件可以通过 pip 安装。如果没有,将会出现诸如 404:笔记本找不到”*的错误
决赛成绩
由于 voila 支持,笔记本电脑的部署变得非常容易。我已经在这两个平台上部署了我的最终分析笔记本,我想在这里提到的一点是,由于我的笔记本是资源密集型的,它通常会溢出 Heroku 内存限制(512MB ),当它达到 200%时,笔记本无法正常呈现。我没有遇到很多活页夹的问题,但是加载图片需要更多的时间。在任何平台中,我的最终笔记本看起来都是这样的:
作者 GIF
整个预处理、仪表板笔记本文件和部署的笔记本链接都可以在我的 Github 存储库中找到:
https://github.com/kaustubhgupta/Covid-Tweets-Analysis-Dashboard
结论
有许多功能都是为分割工作负载而构建的。这种部署有助于更轻松、更交互地与外部世界共享结果。
这就是关于这篇文章的全部内容,我希望你能从这篇文章中学到一些新的东西。再见!
我的 Linkedin:
https://www.linkedin.com/in/kaustubh-gupta
其他热门文章:
用 Python 创建交互式雷达图
用 Plotly 实时绘制交互式雷达图
作者视觉
介绍
也许数据可视化的无名英雄之一是仁慈而优雅的雷达图。举几个例子,我们已经习惯了一大堆其他的可视化,choropleths,甜甜圈和热图,但是雷达图在我们眼花缭乱的仪表盘上基本上不见了。当然,这种图表只有特定的用例,即可视化风玫瑰图、地理数据和一些其他类型的多元数据。但是当您使用它时,它在可视化数值和有序数据集中的异常值或共性方面非常有效。
在本教程中,我们将生成一个带有交互式动态雷达图的仪表板,它将实时呈现。为此,我们将使用 Plotly 和 Streamlit 作为我们强大的 Python 堆栈。如果您还没有这样做,请启动 Anaconda 或您选择的任何其他 Python IDE,并安装以下软件包:
pip install plotlypip install streamlit
然后继续将以下包导入到脚本中:
Plotly
Plotly 是目前市面上最通用、最具交互性的数据可视化工具之一。它配备了 Python 和 R 的绑定,与仪表板的集成是无缝的。Plotly 能够生成交互式雷达图,对开发人员来说几乎没有任何开销。您需要做的就是输入一个与每个变量的值相对应的数字数组,剩下的就交给 Plotly 了,如下所示:
细流
对于我们的仪表板,我们将使用 Streamlit 。长话短说,Streamlit 是一个纯粹的 Python web 框架,它将仪表板实时呈现为 web 应用程序。一旦您的仪表板准备好了,您可以将它端口转发到您的本地 TCP 端口之一,并在您的浏览器上打开它。或者,如果你有兴趣,可以使用 Heroku、AWS 或 Streamlit 自己的一键式部署服务将仪表盘部署到云中。
对于 Streamlit 仪表板,我们将创建一个滑块,使我们能够向 radar_chart 函数中输入任何值。
脚本完成后,您可以通过打开 Anaconda 提示符并键入以下命令来呈现 dashboard/app。
最初,将目录更改为脚本的位置:
cd C:/Users/.../script_directory
然后通过键入以下命令运行您的应用程序:
streamlit run script_name.py
然后,Streamlit 将自动生成您的应用程序,并将其转发到您的本地主机,您可以在您选择的任何浏览器中打开它。
结果
现在你有了一个交互式雷达图,你可以通过修改滑块的位置来改变可视化显示的值。
作者图片
您还可以使用 Plotly 提供的所有交互式铃声和口哨声。
作者图片
如果您想了解更多关于数据可视化和 Python 的知识,请随时查看以下(附属链接)课程:
使用 Streamlit 开发 Web 应用程序:
使用 Python 实现数据可视化:
面向所有人的 Python 专业化:
GitHub 资源库:
https://github.com/mkhorasani/interactive_radar_chart
新到中?您可以在此订阅和解锁无限文章。
使用 Python Altair 创建交互式散点图
增强数据可视化的信息能力
照片由 IIONA VIRGIN 在 Unsplash 拍摄
数据可视化是数据科学不可或缺的一部分。我们创建数据可视化来获得手头数据的结构化概览。它们也是交付成果的有效工具。
因为这是一个如此重要的主题,所以有各种各样的软件工具和软件包来创建数据可视化。
其中之一是 Altair ,它是 Python 的一个声明性统计可视化库。
在本文中,我们将学习如何用 Altair 创建交互式散点图。交互式绘图能够提供比标准绘图更多的信息。它们还允许增加可视化的灵活性。
如果你想在我发表新文章时收到电子邮件,别忘了订阅。
当然,首要的要求是数据集。我们将在 2021 年 7 月 7 日使用来自西班牙巴塞罗那的 Airbnb 房源数据集。它是与知识共享许可共享的,所以你可以自由地使用和探索它们。
让我们从导入库和创建熊猫数据框开始。
import pandas as pd
import altair as altcol_list = ["accommodates", "instant_bookable", "room_type", "reviews_per_month", "price"]df = pd.read_csv(
"listings_2.csv",
usecols = col_list,
nrows = 1000
)df.dropna(inplace=True)print(df.shape)
(965, 5)df.head()
(图片由作者提供)
原始数据集中有 17k 行和 74 列。我们通过使用read_csv
函数的usecols
和nrows
参数创建了一个仅包含 5 列 1000 行的数据框。
值得注意的是,在默认设置下,Altair 最多可以使用 5000 个观测值(即行)。可以使用以下命令禁用此限制。感谢 Natalia Goloskokova 的提醒!
alt.data_transformers.disable_max_rows()
我们还使用了dropna
函数来删除缺少值的行。
在我们开始创建散点图之前,还有一个数据处理操作。价格列以对象数据类型存储,但需要转换为数字格式。
我们需要删除用作千位分隔符的“$”符号和“,”符号。然后,我们可以将其转换为数值数据类型。
df.loc[:, "price"] = \
df.loc[:, "price"].str[1:].str.replace(",","").astype("float")df = df[df["price"] < 1000]df.head()
(图片由作者提供)
散点图
散点图通常用于可视化两个连续变量之间的关系。它们概述了变量之间的相关性。
我们可以创建一个散点图来检查 accommodates 和 price 列之间的关系。容纳栏是一个地方容量的指标,所以我们预计它与价格正相关。
alt.Chart(df).mark_circle(size=50).encode(
alt.X("accommodates"),
alt.Y("price"),
alt.Color("room_type",
legend=alt.Legend(
title="Room Type",
orient='left',
titleFontSize=15,
labelFontSize=13)
)
).properties(
height=350, width=500
).configure_axis(
titleFontSize=20,
labelFontSize=15
)
第一步是将数据框传递给顶级图表对象,然后我们指定可视化的类型。mark_circle
功能创建一个散点图。
在encode
函数中,我们写下要绘制在 x 和 y 轴上的列名。color
参数用于区分图例上显示的不同类别。类似于 Seaborn 中的hue
参数。
最后,properties
和configure_axis
功能调整图形和标签大小等视觉属性。
该代码片段创建了以下散点图:
(图片由作者提供)
这是一个标准的散点图。我们现在将看到如何以几种不同的方式使它具有交互性。
互动功能
互动功能是使情节互动的最简单的方法。它允许在图上放大和缩小。
让我们通过添加 size 属性来增强上一个示例中的绘图。我们将分别在 x 轴和 y 轴上使用每月容纳量和每月评论数列。
颜色将指示房间类型,每个点的大小将与价格成比例。
通过在最后添加interactive
功能,我们将能够放大和缩小。
alt.Chart(df.sample(100)).mark_circle(size=50).encode(
alt.X("accommodates"),
alt.Y("reviews_per_month"),
alt.Color("room_type",
legend=alt.Legend(
title="Room Type",
orient='left',
titleFontSize=15,
labelFontSize=13)
),
alt.Size("price",
legend=alt.Legend(
title="Price",
orient='left',
titleFontSize=15,
labelFontSize=13))
).properties(
height=350, width=500
).configure_axis(
titleFontSize=20,
labelFontSize=15
).interactive()
我从我们的数据框中抽取了一个包含 100 个观察值(即行)的样本,以使该图看起来更有吸引力。这是我们的第一个互动情节。
(作者 GIF)
交互式图例
互动性也可以用来制作更具知识性和功能性的剧情。例如,我们可以通过使图例具有交互性来将其用作过滤器。
我们可以通过创建一个选择对象并将其绑定到图例来实现。
selection = alt.selection_multi(fields=['room_type'], bind='legend')alt.Chart(df).mark_circle(size=50).encode(
alt.X("accommodates"),
alt.Y("price"),
alt.Color("room_type",
legend=alt.Legend(
title="Room Type",
orient='left',
titleFontSize=15,
labelFontSize=13)
),
opacity=alt.condition(selection, alt.value(1), alt.value(0))
).properties(
height=350, width=500
).configure_axis(
titleFontSize=20,
labelFontSize=15
).add_selection(
selection
)
与之前的散点图相比,有 3 处变化。
room_type
列上的选择对象。它必然与传说有关。- 根据所选房间类型更改点的不透明度的“不透明度”参数。
- 用于将选择对象添加到我们的绘图中的
add_selection
功能。
结果如下:
(作者 GIF)
具有多个图的交互式图例
牛郎星允许连接一个传说到多个支线剧情。因此,我们可以同时看到我们的选择对不同关系的影响。
selection = alt.selection_multi(fields=['room_type'], bind='legend')chart = alt.Chart(df).mark_circle(size=50).encode(
y='price',
color='room_type',
opacity=alt.condition(selection, alt.value(1), alt.value(0))
).properties(
height=200, width=300
).add_selection(
selection
)chart.encode(x='reviews_per_month:Q') | chart.encode(x='accommodates:Q')
我们首先创建一个图表对象,但没有指定 x 轴的列。创建和绑定选择部分是相同的。在最后两行中,我们添加了两个不同的 x 轴,并用“or”(|)运算符将它们组合起来。
我已经删除了用于格式化图例和轴标题的部分,使代码看起来更容易理解。
(作者 GIF)
结论
我们已经介绍了如何使用交互性来增强数据的可视化。就交互性组件而言,Altair 的功能更多。一旦您理解了交互性的概念,如选择、绑定和条件,您就可以创建令人惊叹的数据可视化。
别忘了 订阅 如果你想在我发表新文章时收到电子邮件。
你可以成为 媒介会员 解锁我的全部写作权限,外加其余媒介。如果您使用以下链接,我将收取您的一部分会员费,无需您支付额外费用。
https://sonery.medium.com/membership
感谢您的阅读。如果您有任何反馈,请告诉我。
使用 JoyPy 创建快乐图
使用 JoyPy 创建一系列堆积直方图作为 Joy 图
来源:https://scrapbox.io/pycoaj/JoyPy
可视化是发现洞见的核心部分,可用于讲故事。在创建可视化时,我们需要考虑使用哪个情节,考虑哪些功能,将会出现什么故事,或者找到根本原因分析。你有没有被这些问题困住过?
有不同的 python 库可用于数据可视化。在本文中,将讨论一种罕见的情节类型,称为欢乐情节。它们是一系列直方图、密度图或时间序列数据,其中我们通过固定 X 轴上的数据点并在 Y 轴上创建堆栈来创建堆栈可视化。
在本文中,我们将探索 JoyPy,这是一个用于创建 Joy Plots 的开源 python 库。
让我们开始吧…
安装所需的库
我们将从使用 pip 安装 Joy Plots 库开始。下面给出的命令可以做到这一点。
!pip install joypy
导入所需的库
在这一步中,我们将导入加载数据集和可视化数据集所需的库。
import joypy
import seaborn as sns
正在加载数据集
对于本文,我们将使用著名的“Tips”数据集,它已经在 seaborn 库中定义。
df = sns.load_dataset('tips')
df
数据集(来源:作者)
创建快乐情节
现在,我们将从使用不同列的数据创建不同类型的快乐图开始。
fig, axes = joypy.joyplot(df)
来源:作者
该图显示了数据集中所有数字列的分布。
fig, axes = joypy.joyplot(df, by="smoker")
来源:作者
此图显示了数值列相对于吸烟者列的分布。
fig, axes = joypy.joyplot(df, by="size", ylim='day')
来源:作者
fig, axes = joypy.joyplot(df, by="day", overlap=10)
来源:作者
继续尝试不同的数据集,并使用 JoyPy 创建不同的可视化效果。如果您发现任何困难,请在回复部分告诉我。
本文是与皮尤什·英加尔合作完成的。
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。
使用 BigQuery 和 Dataflow 轻松创建 ML 数据集
放弃内存处理,支持并行执行和水平扩展
TL;DR:如果你正在处理大量数据,谷歌云平台(GCP)上的 BigQuery 和 Dataflow 可以提高你的效率,让你在为机器学习生成数据集时更加轻松。
最近,初创公司 reviewr.ai 找到我,让我完成一项数据工程任务,包括为机器学习(ML)准备数据和训练 ML 模型。大公司显然可以让他们的数据科学家花上几天或几周的时间来完成这样的任务,因为他们必须与本地基础设施或云提供商合作,选择云提供商主要是因为该公司多年来一直在许可其 office 套件,与大公司不同,初创公司希望快速完成任务——速度很重要。与许多公司不同,初创公司在选择特定任务的基础设施时通常非常灵活。因此,我们同意应该在 GCP 上完成这项工作,在我看来,它仍然是大数据和 ML 的最佳云平台。因此,我没有花几天时间,而是在一个上午做了所有的数据准备,并在训练 ML 模型的时候休息了一个下午。
本文不是关于任务的机器学习部分或 ML 模型的准确性。它是关于在机器学习开始之前必须发生的数据工程和预处理。大多数数据科学家可能会同意,这通常是花费大部分时间和精力的地方。
令人惊叹的任务
我被要求使用来自拥抱脸的亚马逊 _ 美国 _ 评论数据集来训练一个简单的 NLP 分类模型。它包含来自亚马逊网站的近 1.51 亿条顾客评论,总计约 76 GB 的数据。基于用户写的产品评论,该模型应该将评论分成特定的类别。类成员资格(标签)可以从数据集中包含的数据中导出。因为这个问题很标准,而且我们想要快速的结果,我在 GCP 上使用了 AutoML 。
为了能够使用 AutoML 中的数据,我必须以某种方式从 1 . 51 亿个样本中选择最多 100 万个合适的样本,并将它们转换为所需的格式,即每个样本一个文本文件,其中包含评论文本以及一个索引 CSV 文件,其中包含所有样本的 URIs 和类别。听起来令人畏惧或耗时?都不是。
BigQuery:全进
让我们从获取数据副本开始。这些文件存储在 AWS S3 存储桶中,可以通过 HTTP 请求公开访问。在我们进一步处理它们之前,谷歌云存储 (GCS)是放置它们的合适地方。具体来说,我将把数据吸收到 BigQuery 中,其原因稍后将变得显而易见。有几种方法可以将数据传输到 GCS——例如,使用 bash 脚本wget
s 或curl
s 文件,然后gsutil cp
s 循环地将它们传输到 GCS,或者使用小的 Python 脚本。
我在云外壳中运行这个程序,而不是在我的本地机器上,因为通过谷歌网络传输数据花费的时间要少得多。一旦所有文件都在 GCS 桶中,我只需使用bq
将它们一次性加载到 BigQuery 中,仍然来自云 Shell:
bq load --source_format CSV --autodetect --field_delimiter tab
--skip_leading_rows 1 --quote ' ' [DATASET_NAME].amazon_us_reviews gs://[BUCKET_NAME]/datasets/amazon-us-reviews/raw/*.tsv.gz
通过查看标志可以看出,我让 BigQuery 从数据中推断出模式,并将字段分隔符设置为制表符而不是逗号,将引号字符设置为 none 而不是双引号(“制表符分隔,没有引号和转义符”,如数据集描述所述)。BigQuery 本身可以处理 gzipped 文件,所以我们不必先解压缩文件。
几分钟后,数据存储在 BigQuery 中,并准备好接受检查:
附带说明:如果您有一个 AWS 帐户(因此可以创建一个访问密钥 ID 和一个秘密访问密钥,您可以使用存储传输服务下载文件,甚至可以使用 BigQuery 数据传输服务将数据直接导入 BigQuery。
接下来,让我们运行几个查询来感受一下数据。例如,我们可以询问每个产品组和星级有多少评论,以及星级在产品组中的百分比是多少:
或者:哪些产品类别平均获得的评论最长?
顺便说一下,最短的是礼品卡,这可能不会让人感到意外。
如您所见,在 1 . 51 亿行上运行这些查询只需要几秒钟。因此,你可以尽情发挥你的创造力,在你提出问题的时候尽快分析数据。(但是,请记住,你是按照处理的数据量向收费的,除非你选择统一价格模式。)如果你想绘制结果,只需点击一下鼠标,就可以在数据工作室中看到它们。
从数据集中选择训练数据
然而,我们不只是想分析数据。我们还希望为 AutoML 生成训练数据集,这意味着从总共 1.51 亿个样本中选择 100 万个样本并计算特征。我不想透露 reviewr.ai 到底想让我做什么,所以让我们假设我们想从评论机构推断评级(1-5 颗星)。理想情况下,在这种情况下,每个类有 200,000 个例子。
首先,我们将排除所有没有审核主体的审核(即WHERE review_body IS NOT NULL
)。此外,我们必须排除具有相同 review_body 的行,因为基本事实中的分类可能会变得模糊,而且 AutoML 不允许重复。事实证明,有很多这样的例子:
厉害!
为了进一步缩小结果范围,我们将关注大多数用户认为有帮助的评论。此外,我们将要求一个评论收到至少 x 票。在哪里设置阈值,x?让我们制作一个统计图,感受一下每个评分的投票数是如何分布的:
如果我们将阈值设置在 10 到 20 之间,我们似乎仍然有足够的样本用于每一类,所以我选择 15。这意味着我们只包括至少获得 15 票的评论,其中大多数被认为是有帮助的。当然,我们可以更进一步,要求每个产品类别都应该有平等的代表性。出于本文的目的,我们将它留在这里。因此,下面是最终定义数据集的查询:
运行一个快速的SELECT class, COUNT(*) FROM ... GROUP BY class
确认我们每个类确实有 200,000 个样本。
一旦我们对我们的查询感到满意,我们就可以运行它并将结果具体化为一个表,或者将查询本身保存为一个视图。加载表将比查询视图更快(这基本上与运行查询本身是一样的)。尽管如此,我还是选择创建一个视图,因为我们只需要查询一次或几次,所以速度并不重要。该视图的优点是记录了数据集是如何创建的,并且如果您想要为另一个模型迭代创建新的数据集,您可以很容易地修改它并以不同的名称保存它。
那么,现在我们通过 SQL 查询得到了训练数据集的定义——我们如何将它转换成我们需要的格式(每个示例一个包含评论正文的文本文件,加上一个包含所有示例的 URIs 和类的 CSV 文件)?将查询结果导出为 CSV 文件,并编写 Python 脚本来逐行处理该文件?这将花费一两个永恒的时间(处理,而不是写剧本)。相反,让我们转向另一个专门用于这种工作负载的伟大工具:GCP 上的 Apache Beam 和 Dataflow 。
无与伦比的并行执行速度
引用文档,“Apache Beam 是一个开源的统一模型,用于定义批处理和流数据并行处理管道”。Dataflow 是 GCP Apache Beam 的一个完全托管的跑步者。通常的工作流程是这样的:编写您的管道(用 Java 或 Python),用一些元素在本地测试它以验证它的工作,然后将其部署到数据流以在云中大规模处理整个数据集。
Apache Beam 有一组 I/O 连接器(“源”和“接收器”),其中一个用于 BigQuery,一个用于 GCS。这意味着我们可以直接从 BigQuery 查询结果中读取,并将输出写入 GCS bucket。从概念上讲,我们将要构建的管道如下所示:
管道从一个源(BigQuery 查询结果,包含 review_id、review_body 和类)读取数据,并生成两个输出:第一个分支使用 review_body 创建 100 万个文本文件,第二个分支创建 100 万个包含 URI ( gs://...
)和类的字符串,然后将它们连接起来并将结果写入一个 CSV 文件。管道以每行为基础进行操作。也就是说,每个元素都是查询结果中的一行。
以下是完整的管道代码:
使用 Apache Beam 的好处在于,因为查询结果的每一行都可以独立处理,所以我们实现了大规模并行处理。正如您在下面看到的,Dataflow 确实将工作线程的数量扩展到了 527 个,每秒处理大约 2,200 行:
工作所需的基础设施得到全面管理。在开始实际的管道执行之前,Dataflow 需要花几分钟来配置基础设施(计算引擎)。作业成功完成(或失败)后,它会降速并删除虚拟机。
由于并行执行,相当于 72 个 CPU 小时的工作仅用了大约 18 分钟就完成了。一个重要的注意事项:数据流扩展工作负载的程度可能会受到配额的限制。特别是,在新创建的 GCP 项目中,Compute Engine API in-use IP addresses配额(默认为每个区域 8 个)会限制为作业提供的计算实例数量(因为每个工作实例都需要一个 IP 地址)。因此,如果您发现现有配额过低,请务必提前请求增加配额。
现在我们有了:100 万个文本文件和一个 CSV,准备导入 AutoML。
工作证明
外卖食品
现在,准备这个数据集的麻烦已经消失在云中,让我们得出一些结论:
- **水平而不是垂直缩放。**我认识的许多数据科学家都专注于垂直扩展。不符合记忆?让我们得到一个有更多内存的更大的实例。然而,在坚持内存处理的同时,仅仅投入更多的内存来解决问题,会失去并行工作负载的优势,从而在更短的时间内完成更多的工作。对于 BigQuery 中的数据分析,我不必花一秒钟考虑它需要多少内存——我只需打开 BigQuery UI 并开始查询。我的数据流工作的工人都是 n1-standard-1 虚拟机,带有 1 个 vCPU 和 3.75 GB 的内存。唯一可能面临内存瓶颈的实例是在将一百万行写入文本文件之前收集并连接它们的工作人员。在这种情况下,产生的字符串使用了 123 MB,所以根本没有达到限制。如果我们这样做,我们只会创建分片文件,而不是一个单一的文件。
- 使用 GCP 的专业数据工程工具。为什么每次想使用 Jupyter 笔记本时都要麻烦地启动它并将数据加载到 pandas 中呢?相反,您可以将数据转储到 BigQuery 中,并在那里保存数据,使您可以随时查询数据。当 Jupyter 实例完成旋转,并且已经将所有数据加载到 pandas 数据帧中时,您可能已经完成了 BigQuery 中的数据分析。当然,你仍然可以使用 Jupyter 毕竟它对于数据科学任务有其优点。但是使用它来查询 BigQuery 和编写 Apache Beam 管道,而不是处理内存中的数据。顺便说一句:也有很多现成的数据流模板,所以你甚至可以不用写自己的管道。
- **精益和可重用流程。**使用 BigQuery 和 Apache Beam 的工作流是精简和灵活的。需要另一个具有不同功能的训练集吗?只需编写所需的查询,启动管道,20 分钟后,您就已经将新数据集导入到 AutoML 中了。如果我们想用相同的数据集在 TensorFlow 中训练自己的模型呢?稍微修改一下 Apache Beam 管道,输出 TFRecord 文件,就可以了。
- **高效利用基础设施。**使用这些工具不仅可以节省时间,还可以降低基础设施的使用成本。由于 BigQuery 和 Dataflow 都是无服务器的,因此不存在未充分利用或过度利用的基础设施,也没有固定成本(当然,除了存储成本之外,存储成本在大多数情况下可以忽略不计)。考虑到工作负载的短暂性和所需的有限财政承诺,您可能会考虑使用 GCP 来完成这种数据工程任务,即使它不是您的默认主力。
使用 Streamlit 创建多页面应用程序(高效!)
实践教程
使用面向对象编程,而不是条件编程。
你有多少次有一个伟大的 ML 项目想法,你认为你可以在你的简历上炫耀?你快速收集数据,分析数据,并在此基础上建立模型;所有这些都在一个 Jupyter 笔记本中,然后你面临你最糟糕的噩梦——有人要求你部署模型并用一个用户友好的界面显示推理。如果你独自工作,这有时真的会令人生畏,为什么不呢?你是一个数据科学家,而不是一个网络开发人员!这就是细流 的用武之地。这是一个非常棒的工具,它可以让你快速构建交互式 web 应用程序,并用图形展示你的模型,这可能会让许多开发人员感到羞愧。
Streamlit 最棒的一点是,你甚至不需要了解 web 开发的基础知识就可以开始工作,甚至不需要创建你的第一个 web 应用程序。因此,如果你对数据科学感兴趣,但因为不喜欢 web 开发而不愿意部署你的模型,那么这就是你只用 Python 构建漂亮的数据应用程序的机会!
有许多资源可以教你如何开始使用 Streamlit。我个人最喜欢的是这个。然而,一旦你开始,你很快就会意识到仅仅构建一两个页面是不够的,一个好的应用程序会有更多的页面,这就是构建多页面应用程序的需要。所以,我决定把它写下来。在本文中,我将带您构建您的第一个多页面 Streamlit 应用程序并部署它。先决条件是了解 Python 和 Streamlit 的基础知识。
**编辑:**Streamlit 社区成员非常友好地让我在 Streamlit 共享平台上展示我的应用程序,所以我在那里部署了它。现在任何人都可以实时测试。你可以在这里找到应用。
代码概述
在这一节中,我将带您浏览我做的一个项目,在这个项目中,我实现了 Streamlit 的多页面功能 。现在,这个不是使用一个if-else
方法完成的,在这个方法中,我一直检查用户选择了streamlit.selectbox()
的哪个条目,并相应地显示页面。这个方法看起来有点像这样。
import streamlit as st# Create a page dropdown
page = st.selectbox("Choose your page", ["Page 1", "Page 2", "Page 3"]) if page == "Page 1":
# Display details of page 1elif page == "Page 2":
# Display details of page 2elif page == "Page 3":
# Display details of page 3
虽然上面的方法没有错,但我发现它非常烦人,因为我必须管理这么多的if-else
和其中的代码。作为程序员,我们应该能够编写更整洁的应用程序,并在任何需要的地方使用面向对象编程。这就是我在这里要做的!
我制作的应用程序被称为“DataSpeare”(我知道这是一个糟糕的文字游戏),它是一个基于人工智能的工具,为非技术用户自动化数据分析和机器学习的过程。它可以获取数据集,识别数据中的模式,解释结果,然后基于问题的上下文生成业务用户可以理解的输出故事。该应用程序的功能如下
- 根据给定的数据将数据转化为交互式数据故事
- 产生深刻的见解,推断模式并帮助业务决策。
- 提供个性化配置文件;这些可以表示为描述给定用户感兴趣的内容的元数据。
- 使用交互式直观界面生成业务用户可以理解的报告。
你可以在这里阅读更多关于应用的内容。如果你想获得项目的源代码,那么你可以去 GitHub 仓库。在这篇文章中,我不会介绍整个应用程序,因为它有一个很大的代码库,但我会介绍它的基本结构。
首先,您需要将 Streamlit 安装到您的设备上。就个人而言,我会推荐使用虚拟环境,但这一步是可选的。
**pip install streamlit**
对于我的应用程序,如果你正在构建整个系统,你可以下载一堆其他需求。现在,最重要的部分是构建应用程序的结构。我使用了一个基于类的结构,它可以让你在应用程序中添加页面作为MultiPage
类的实例。这已经在我的项目报告中名为multipage.py
的文件中创建。
这是一个简单的类,有两个方法
add()
:用来给你的应用添加更多的页面。每个新页面都附加到实例变量pages
上。run()
:这个方法在应用程序的侧边栏中创建一个下拉菜单,这样你就可以从所有你需要的选项中进行选择。
它只需要你输入streamlit
,你就可以开始了!
这是一个处理应用程序结构的类,但是你实际上需要 file 来添加页面和管理代码。这已经在app.py
文件中完成。
这是我们的 Streamlit 应用程序中的主文件,它将运行以加载所有页面。在第 8 行,我创建了一个MultiPage
类的实例app
,它是我在第 4 行导入的。app
实例让我向应用程序添加页面,并运行应用程序。页面已经从第 14 行开始添加,用户可以以类似的方式添加他们的页面。最后,在第 21 行,我使用run
函数运行了应用程序。
注意:- 使用这种结构的人只需改变第 5 行的导入,从第 14 行添加的页面和其余的代码将保持不变。
下面是一个片段,展示了您的应用程序导航的外观,具体取决于您在应用程序中添加了哪些页面。
多页应用程序的简化输出(app.py 文件)
最后,您可以用下面给出的方式为您的应用程序创建一个页面。我只展示了一页,其余的可以在这里看到。我创建了一个名为pages
的新目录,并将所有页面文件放入其中,您也可以这样做。
我下面展示的页面叫做data_upload.py
。在这个页面中,我上传了数据集并制作了它的本地副本。这对于在页面之间传输数据很有用。使用会话变量有另一种方法可以做到这一点,但是我不推荐初学者使用(也许我会写另一篇关于它的文章)。上传的数据集以 csv 格式存储在data
文件夹中,并由其他页面读取。因为这是一个自动化的数据分析应用程序,所以它还会为更新的数据集中的每一列生成一个列数据。这是在第 44 行使用[utils.py](https://github.com/prakharrathi25/data-storyteller/blob/main/pages/utils.py)
文件中定义的genMetaData()
函数完成的。您可以用类似的方式创建其他页面并构建您的应用程序。
注意:-在 app()函数中编写代码很重要,因为它在多页面应用程序的结构中被调用。
一旦所有的页面都制作好、导入并添加到你的应用程序中,就该在你的命令提示符下使用下面的命令来运行你的应用程序了。
streamlit run app.py
一旦您运行应用程序,您的 cmd 应该看起来像这样,一旦您重定向到本地 URL,您将看到您的应用程序。
最终多页简化应用程序
您也可以使用这个方法,并在您定义的页面内建立的新执行个体,在 multipage 应用程式内建立 multipage 应用程式。
使用 Streamlit 和它的其他组件,如会话变量,您可以做很多令人惊奇的事情。我可能会写另一篇关于会话状态和变量的文章,但我希望这篇文章简短且内容丰富。我希望你喜欢它,这是有益的。祝你好运!
参考
- https://www.youtube.com/watch?v=nSw96qUbK9o&t = 78s
- https://streamlit.io/
- https://sap-code . hackere earth . com/challenges/hackathon/sap-code/custom-tab/Data-4-storying/# Data % 204% 20 storying
用 ML 创作音乐
变形金刚又来了。
技术设备日复一日地播放音乐。你的手机可以做到,我的电脑可以做到,智能设备根据命令播放歌曲。因此,我们为什么不让技术设备也创造音乐呢?不仅仅是回放我们的结果,而是拿出原创作品?
韦斯顿·麦金农在 Unsplash 上拍摄的照片
这个方向有一个正在进行的研究领域;DeepMind 凭借其能够生成原始音频的 WaveNet 架构做出了巨大贡献。生成原始音频(生成类似音频曲线的浮点值)的缺点是涉及巨大的计算能力。这是一个障碍,因为计算机可能并不总是可用。
另一个效果惊人的方法是使用不同的格式来表现音乐。想想活页乐谱:虽然它本身不会发出声音,但它可以在下一步中用来产生声音。并且通过简单地生成图像,可以容易地训练 ML 模型来生成活页乐谱。
另一种格式是使用 MIDI 文件。这种格式将音乐存储为符号表示。我们不再保留声音,而是保留产生声音的信息。乐谱将音符值放在五线谱上,而 MIDI 表示包含信息。这些信息包含音符开和音符关事件,指示乐器何时演奏。速度同样可以包括在内。
MIDI 文件可以包含从 C_{-1}到 C_{)}范围内的音符;相比之下,一架标准钢琴有 88 个键,那些漂亮的大钢琴有时更多。
在 MIDI 文件中,音符可以有任意的持续时间,并且必须在回放前量化。然后,为了可视化(量化的)MIDI 文件,我们使用钢琴卷帘窗表示。这本质上是一个矩阵,横轴是时标,纵轴是理论上可以同时演奏的音符数。下面描述了一个可视化示例:
钢琴卷帘窗的可视化示例。y 轴表示音高,x 轴表示时间。在时间 t,如果存在蓝色标记,则演奏一个音高(或音符)。图片由作者提供。
现在,用矩阵来表示音乐有什么好处呢?首先,你需要更少的存储空间,典型的 MIDI 文件最多只有几千字节。其次,实际的矩阵可以是布尔型或 int8 型。与原始音频相比,这大大减小了张量的大小。
简而言之:你把音乐表现为一种形象。
反过来也很容易做到。网络的输出也是一个矩阵(或图像),因此可以解释为钢琴曲。然后这个钢琴卷帘窗保存了事件信息,我们用它来(重新)构建一个 MIDI 文件。
现在的问题是:听起来怎么样?
训练越多越好。
但是你可以在这里自己听一听。默认情况下,我们最好的模型会被选中。换一个训练步数更少的,听听区别。
在一个项目中,我有机会成为一个使用 MIDI 文件的小组的一员,并尝试了各种 ML 模型。最初,我们训练自动编码器、变型自动编码器、生成对抗网络、它们的组合,以及不可避免的变形器。
最终,我们选定了由谷歌大脑团队开发的音乐转换器架构。然后我们在内部的 MIDI 文件数据集上训练这个模型。这个数据集包含了所有时代的音乐,但也包含了伟大的作曲家,如巴赫、柏辽兹、海顿和莫扎特。
由于我们为一个关于莫扎特的节日做了贡献,我们把重点放在了包括莫扎特在内的古典作曲家的作品上。
为了让访问者可以选择与我们的模型进行交互,我们编写了一个 web 应用程序。这是在 streamlit 的帮助下完成的(这在大多数情况下非常方便,但在一些重要的地方使用起来非常)令人沮丧。
要摆弄模型,从头开始创作新作品,请点击此链接。点击按钮,让人工智能做剩下的事情。
创建在线数据驱动的演示
使用 Reveal.js 和 D3.js 创建交互式在线数据科学演示
介绍
能够总结数据科学项目并展示其潜在的商业利益,在获得新客户方面可以发挥非常重要的作用,并使非技术受众更容易理解一些关键的设计概念。
在本文中,我将向您介绍两个免费的编程框架,它们可以用来创建交互式在线演示和基于数据的讲故事报告。
Reveal.js
Reveal.js 是一个完全基于开放网络技术的开源展示框架。使用 Reveal.js,可以轻松地创建基于 web 的演示文稿,并将其导出为 PDF 等其他格式。
Reveal.js 的一些最有趣的特性是 Latex 和 Markdown 支持、CSS 定制、演讲者注释功能和语法高亮代码工具。
设置
Reveal.js 可以通过克隆官方存储库轻松安装:
git clone [https://github.com/hakimel/reveal.js.git](https://github.com/hakimel/reveal.js.git)
我们的演示可以通过修改 reveal.js 目录中提供的index.html文件来开发。使用 Node.js,我们可以很容易地实时观察(在本地服务器上的端口: http://localhost:8000 )我们如何更新index.html文件。
cd reveal.js && npm install
npm start
下面是index.html演示文件的一个简单例子。从代码片段中我们可以看到,我们首先导入 Reveal.js 库,然后将 black 作为幻灯片的主题(更多主题选项列在 Reveal.js 文档页面上!)。每个不同的幻灯片,然后可以通过将它们封装在一个 section 标签中并在里面写入所有需要的内容来创建。嵌套不同的 section 标签,就有可能在演示文稿中创建不同的子部分。最后,在最后一个脚本标记中,Reveal.js 被初始化(传递不同的可选参数,可以添加 Latex 和 Math 格式等功能)。
<html>
<head>
<link rel="stylesheet" href="dist/reveal.css">
<link rel="stylesheet" href="dist/theme/black.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section>First Slide</section>
<section>Second Slide</section>
<section>
<section>Subsection Slide 1</section>
<section>Subsection Slide 2</section>
</section>
</div>
</div>
<script src="dist/reveal.js"></script>
<script>
Reveal.initialize();
</script>
</body>
</html>
此外,通过使用以下格式实例化幻灯片,还可以在 Markdown 中创建幻灯片,而不是使用 HTML 和 Javascript。
<section data-markdown>
<textarea data-template>
## Slide Title
Example **text**.
</textarea>
</section>
使用 Reveal.js 创建的完整工作示例演示可在此链接获得(图 1)。
图 1:视频示例
D3.js
D3.js 是一个开源的 Javascript 库,旨在使用 HTML、CSS 和 SVG 在 web 中创建数据驱动的可视化。D3.js 可以简单地通过在我们的工作文件中添加下面一行来加载。
# As new versions of the library might be released, updated versions # of this link might can be found at [this link.](https://d3js.org/)
<script src="https://d3js.org/d3.v5.min.js"></script>
使用 D3.js,可以通过元素名称或 CSS 选择器来选择元素。此外,D3.js 还提供了一些实用程序,用于加载数据集和预处理可视化数据。
下面是一个简单的示例代码,展示了如何设置 D3.js 来创建椭圆图表。
<html><head>
<script src="[https://d3js.org/d3.v5.min.js](https://d3js.org/d3.v5.min.js)"></script>
</head><body>
<h1>D3.js Template Example</h1>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", 270)
.attr("height", 270); svg.append("ellipse")
.attr("cx", 140)
.attr("cy", 100)
.attr("rx", 120)
.attr("ry", 70)
.attr("opacity", 0.3)
.attr("fill", "blue")
</script>
</body></html>
图 2: D3.js 示例代码输出
然后可以使用 D3.js 图表,通过将它们与 scroller 架构相集成来创建强大的演示。幸运的是,多亏了像吉姆·瓦兰丁汉和库斯伯特·周这样的作者,多用途滚动条(可用于任何类型的叙事)在过去几年已经被开发出来。下面的动画展示了我使用基于 D3.js 的滚动条创建的数据驱动叙事的完整工作示例。这也可以在以下链接中进行测试。
图 3: D3.js 滚动条演示
如果您有兴趣了解更多关于如何创建 D3.js 图表的信息,那么 D3 Graph Gallery 文档是一个很好的起点。
最后,如果你想让你的演示可以离线使用,可以使用 FPDF 和 python-pptx Python 库来分别自动生成 PDF 和 PowerPoints。
希望您喜欢这篇文章,感谢您的阅读!
联系人
如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:
从头开始创建 ONNX
我们将在本教程中构建 ONNX 管道;图片作者。
实践教程
ONNX 提供了一种非常灵活的格式来存储 AI/ML 模型和管道。要了解如何操作,手动构建 ONNX 图是有指导意义的。
ONNX 已经存在了一段时间,它正在成为一种成功的中间格式,将通常沉重的、经过训练的神经网络从一种训练工具转移到另一种工具(例如在 pyTorch 和 Tensorflow 之间转移),或者使用 ONNX 运行时在云中部署模型。在这些情况下,用户通常只是简单地将模型保存为 ONNX 格式,而不用担心生成的 ONNX 图形。
然而,ONNX 可以有更广泛的用途:ONNX 可以轻松地用于手动指定 AI/ML 处理管道,包括现实部署中经常需要的所有预处理和后处理。此外,由于其标准化和开放的结构,存储在 ONNX 中的管道可以很容易地部署,甚至在边缘设备上(例如,通过自动编译到 WebAssembly 以在各种目标上高效部署)。在本教程中,我们将展示如何使用 Python 中的onnx.helper
工具从头开始创建 ONNX 管道并有效地部署它。
本教程由以下部分组成:
- ONNX 的一些背景。在我们开始之前,从概念上理解 ONNX 是做什么的是有用的。
- “找房子”场景。在本教程中,我们将重点创建一个管道来预测广告房屋的价格,并随后判断该房屋是否符合我们的搜索约束条件(即我们的需求)。
- 模特培训。虽然不是真正的部署管道的一部分,我们将展示我们如何使用“sklearn”来训练预测模型。
- 创建 ONNX 管道。这是本教程的主体,我们将一步一步来:
— 预处理:我们将使用我们训练的结果来标准化输入。
— 推论:我们将使用训练期间拟合的模型来预测(对数)价格。
— 后处理:我们将检查结果是否符合我们的要求。
— 综合起来:我们将把预处理、推理和后处理管道合并成一个 ONNX 图。 - 部署模型:可以使用 ONNX 运行时部署 ONNX 模型,或者优化拟合图,使用 WebAssembly 部署。我们将简要探讨这两种选择。
ONNX 可轻松用于手动指定 AI/ML 处理管道,包括现实部署中经常需要的所有预处理和后处理。
1.ONNX 是什么?
根据 ONNX 官方网站:
“O NNX 是一种开放格式,用于表示机器学习模型。ONNX 定义了一组通用的运算符——机器学习和深度学习模型的构建块——和一种通用的文件格式,使 AI 开发人员能够使用各种框架、工具、运行时和编译器“”(参见)ONNX . AI)。
因此,ONNX 是一种开放文件格式,用于存储(已训练)包含足够细节(关于数据类型等)的机器学习模型/管道。)从一个平台移动到另一个平台。ONNX 的特性甚至允许将存储的操作自动编译成低级语言,以嵌入到各种设备中。实际上,当从一个平台转移到另一个平台时,一个onnx
文件将包含重新实例化一个完整的数据处理管道所需的所有信息。
从概念上讲,onnx 格式非常简单:ONNX 文件定义了一个有向图,其中每条边代表一个特定类型的张量,该张量从一个节点“移动”到另一个节点。节点本身被称为操作符,它们对它们的输入(即图中它们的父节点的结果)进行操作,并将它们的操作结果提交给它们的子节点。ONNX 指定了一个操作列表,它们共同允许你指定任何你可能想要执行的 AI/ML 操作(如果不是,操作符的集合很容易扩展)。
为了提供一个概念性的例子,假设我们想要从一个逻辑回归模型中生成推论。给定一个输入向量x
,我们将:
1.使用例如Gemm
算子将我们的输入向量x
乘以学习系数beta
来计算线性预测值ylin
。
2。使用Sigmoid
运算符将线性预测值转换为概率标度。
3。使用Less
操作符生成对或错的推断。
因此,通过有效地链接多个操作符,我们可以在给定一些特征向量的情况下生成推论。然而,不要被简单的例子所迷惑:由于 ONNXs 张量支持和大量的操作符列表,甚至用于视频处理的复杂 dnn 也可以在 ONNX 中表示。
不要被这个简单的例子所迷惑:由于 ONNXs 张量支持和广泛的操作符列表,甚至用于视频处理的复杂 dnn 也可以在 ONNX 中表示。
2.我们找房子的场景
为了演示如何从头开始创建 ONNX 图,我们引入一个简单的场景:
“假设你正在附近找一栋新房子。给定以前房屋销售的数据集,其中包含已出售房屋的庭院大小
yard
、居住面积area
和房间数量rooms
及其价格price
,构建一个模型来预测新广告房屋的价格(即,只给你庭院大小、面积和房间数量)。接下来,您感兴趣的是价格低于 400.000 欧元、带院子(即院子大小大于 0)的任何房子。”
在本教程中,我们将构建一个数据处理管道,包括一个用于推理的线性回归模型,该模型给出了一个示例特征向量(yard,area,rooms)
提供了一个布尔值,指示该房屋是否是感兴趣的。
3.模特培训
要创建管道,我们首先需要创建用于预测价格的模型。下面的代码打开数据文件houses.csv
(此处可用)并生成模型。请注意,我们执行了几个步骤:
- 我们对输入进行标准化(因此,为了在预处理流水线中进行去标准化,我们将需要每个输入变量的“平均值”和“标准值”)。
- 我们对价格进行对数转换(因此,在我们的后处理流程中,我们需要将我们的推断转换回正确的比例)。
- 我们拟合一个简单的线性回归模型(并且我们存储估计的系数用于我们的处理流水线)。
以下是注释代码(或点击此处查看完整笔记本):
import numpy as np
from sklearn import linear_model as lm# Open the training data:
data = np.loadtxt(open(“houses.csv”, “rb”), delimiter=”,”, skiprows=1)# Retreive feature vectors and outcomes:
datX = data[:, [1,2,3]] # Input features (yard, area, rooms)
datY = data[:, [0]] # Price# Standardize the inputs:
barX = np.mean(datX, 0) # Mean for each of the inputs
sdX = np.std(datX, 0) # Sd for each of the inputs
datZ = (datX — barX) / sdX# Log transform the output
logY = np.log(datY)# Fit a linear model
lin_mod = lm.LinearRegression()
lin_mod.fit(datZ, logY)# retrieve intercept and fitted coefficients:
intercept = lin_mod.intercept_
beta = lin_mod.coef_## Storing what we need for inference in our processing pipeline.
print(“ — — Values retrieved from training — — “)
print(“For input statdardization / pre-processing we need:”)
print(“ — The column means {}”.format(barX))
print(“ — The column sds {}”.format(sdX))print(“For the prediction we need:”)
print(“ — The estimated coefficients: {}”.format(beta))
print(“ — The intercept: {}”.format(intercept))# store the training results in an object to make the code more readable later on:
training_results = {
“barX” : barX.astype(np.float32),
“sdX” : sdX.astype(np.float32),
“beta” : beta.astype(np.float32),
“intercept” : intercept.astype(np.float32),
}# And, also creating the constraints (for usage in block 3):
constraints = {
“maxprice” : np.array([400000]),
“minyard” : np.array([1]),
}
虽然training_results
和constraints
对象是我们在这个训练阶段在 ONNX 中实现我们的处理管道所需要的,但是演示如何在不转换到 ONNX 的情况下生成推理是有启发性的。以下代码演示了我们旨在使用简单 python 代码构建的完整管道,并使用我们训练集中的第一个实例作为示例:
# Get the data from a single house
first_row_example = data[1,:]
input_example = first_row_example[[1,2,3]] # The features
output_example = first_row_example[0] # The observed price# 1\. Standardize input for input to the model:
standardized_input_example = (input_example - training_results['barX'])/ training_results['sdX']# 2\. Predict the *log* price (using a dot product and the intercept)
predicted_log_price_example = training_results['intercept'] + np.dot(standardized_input_example, training_results['beta'].flatten())# Compute the actual prediction on the original scale
predicted_price_example = np.exp(predicted_log_price_example)
print("Observed price: {}, predicted price: {}".format(output_example, predicted_price_example))# See if it is interesting according to our simple decision rules:
interesting = input_example[1] > 0 and predicted_price_example < 400000
print("Interesting? {}".format(interesting))
4.创建 ONNX 管道。
给定一个由长度为 3 (yard,area,rooms)
的输入向量描述的示例实例,我们的 ONNX 管道应该:
- [预处理]通过减去平均值(如在训练集中观察到的)并除以标准差来标准化输入
- [推论]在对数标度上预测示例实例的房价(使用来自上述训练好的
sklearn
模型的系数) - [后处理]将价格转换回原始规模,并检查 a)房子是否负担得起,b)是否有院子。
更准确地说,引入所使用的 ONNX 操作符,我们将生成以下管道:
图片作者。
请注意,在下面的代码块中,我们将使用此图片中介绍的命名约定。
4a。预处理
我们将使用 Python 中提供的onnx.helper
工具来构建我们的管道。我们首先创建常数,然后是操作节点(尽管常数也是操作符),接着是图形:
# The required constants:
c1 = h.make_node(‘Constant’, inputs=[], outputs=[‘c1’], name=”c1-node”,
value=h.make_tensor(name=”c1v”, data_type=tp.FLOAT,
dims=training_results[‘barX’].shape,
vals=training_results[‘barX’].flatten()))c2 = h.make_node(‘Constant’, inputs=[], outputs=[‘c2’], name=”c2-node”,
value=h.make_tensor(name=”c2v”, data_type=tp.FLOAT,
dims=training_results[‘sdX’].shape,
vals=training_results[‘sdX’].flatten()))# The functional nodes:
n1 = h.make_node(‘Sub’, inputs=[‘x’, ‘c1’], outputs=[‘xmin’], name=’n1')
n2 = h.make_node(‘Div’, inputs=[‘xmin’, ‘c2’], outputs=[‘zx’], name=”n2")# Create the graph
g1 = h.make_graph([c1, n1, c2, n2], ‘preprocessing’,
[h.make_tensor_value_info(‘x’, tp.FLOAT, [3])],
[h.make_tensor_value_info(‘zx’, tp.FLOAT, [3])])# Create the model and check
m1 = helper.make_model(g1, producer_name=’scailable-demo’)
checker.check_model(m1)# Save the model
save(m1, ‘pre-processing.onnx’)
上面的代码创建了预处理管道,并以onnx
格式存储。在 Python 中,我们可以使用onnxruntime
直接测试存储的模型:
# A few lines to evaluate the stored model, useful for debugging:
import onnxruntime as rt# test
sess = rt.InferenceSession(“pre-processing.onnx”) # Start the inference session and open the model
xin = input_example.astype(np.float32) # Use the input_example from block 0 as input
zx = sess.run([“zx”], {“x”: xin}) # Compute the standardized outputprint(“Check:”)
print(“The standardized input using onnx pipeline is: {}”.format(zx))
print(“ — Compare to standardized first row in block 0: {}”.format(datZ[1,:]))
评估表明,我们的预处理管道确实正确地标准化了示例实例。
预处理管道,作者图像。
4b。推理
创建预处理管道后,我们按照类似的方法创建推理管道:
# The constants:
c3 = h.make_node(‘Constant’, inputs=[], outputs=[‘c3’], name=”c3-node”,
value=h.make_tensor(name=”c3v”, data_type=tp.FLOAT,
dims=training_results[‘beta’].shape,
vals=training_results[‘beta’].flatten()))c4 = h.make_node(‘Constant’, inputs=[], outputs=[‘c4’], name=”c4-node”,
value=h.make_tensor(name=”c4v”, data_type=tp.FLOAT,
dims=training_results[‘intercept’].shape,
vals=training_results[‘intercept’].flatten()))# The operating nodes, Multiply, reduceSum, and Add
n3 = h.make_node(‘Mul’, inputs=[‘zx’, ‘c3’], outputs=[‘mulx’], name=”multiplyBeta”)
n4 = h.make_node(‘ReduceSum’, inputs=[‘mulx’], outputs=[‘sumx’], name=”reduceSum”, keepdims=0)
n5 = h.make_node(‘Add’, inputs=[‘sumx’, ‘c4’], outputs=[‘yhatlog’], name=’addIntercept’)# The graph
g2 = h.make_graph([c3, c4, n3, n4, n5], ‘linear_regression’,
[h.make_tensor_value_info(‘zx’, tp.FLOAT, [3])],
[h.make_tensor_value_info(‘yhatlog’, tp.FLOAT, [1])])# The model and check:
m2 = h.make_model(g2, producer_name=’scailable-demo’)
checker.check_model(m2)# Save the model
save(m2, ‘linear-regression.onnx’)
同样,很容易验证预测是否正确:
# test
sess = rt.InferenceSession(“linear-regression.onnx”) # Start the inference session and open the model
xin = standardized_input_example.astype(np.float32) # Use the input_example from block 0 as input
yhatlog = sess.run([“yhatlog”], {“zx”: xin}) # Compute the standardized outputprint(“Check:”)
print(“The log predicted price from ONNX is: {}”.format(yhatlog))
print(“ — Compare to analysis in block 0: {}”.format(predicted_log_price_example))
推理管道,作者图片。
4c。后加工
后处理流水线稍微复杂一点,因为它检查房子是否符合我们的需要(即predicted_price < 400000
和yard > 0
,因此使用各种输入源(注意“Slice”操作符有点复杂):
# Constants (note using the constraints object created in block 0 above)
c5 = h.make_node(‘Constant’, inputs=[], outputs=[‘c5’], name=”c5-node”,
value=h.make_tensor(name=”c5v”, data_type=tp.FLOAT,
dims=constraints[‘maxprice’].shape,
vals=constraints[‘maxprice’].flatten()))
c6 = h.make_node(‘Constant’, inputs=[], outputs=[‘c6’], name=”c6-node”,
value=h.make_tensor(name=”c6v”, data_type=tp.FLOAT,
dims=constraints[‘minyard’].shape,
vals=constraints[‘minyard’].flatten()))# Auxiliary constants for the slice operator:
caux1 = h.make_node(‘Constant’, inputs=[], outputs=[‘caux1’], name=”caux1-node”,
value=h.make_tensor(name=’caux1v’, data_type=tp.INT32,
dims=np.array([0]).shape, vals=np.array([0]).flatten()))
caux2 = h.make_node(‘Constant’, inputs=[], outputs=[‘caux2’], name=”caux2-node”,
value=h.make_tensor(name=’caux2v’, data_type=tp.INT32,
dims=np.array([1]).shape, vals=np.array([1]).flatten()))
caux3 = h.make_node(‘Constant’, inputs=[], outputs=[‘caux3’], name=”caux3-node”,
value=h.make_tensor(name=’caux3v’, data_type=tp.INT32,
dims=np.array([0]).shape, vals=np.array([0]).flatten()))
caux4 = h.make_node(‘Constant’, inputs=[], outputs=[‘caux4’], name=”caux4-node”,
value=h.make_tensor(name=’caux4v’, data_type=tp.INT32,
dims=np.array([1]).shape, vals=np.array([1]).flatten()))
# Nodes:
n6 = h.make_node(‘Exp’, inputs=[‘yhatlog’], outputs=[‘yhat’], name=’exponent’)
n7 = h.make_node(‘Less’, inputs=[‘yhat’, ‘c5’], outputs=[‘price_ok’], name=’priceLess’)n8 = h.make_node(‘Slice’, inputs=[‘x’, ‘caux1’, ‘caux2’, ‘caux3’, ‘caux4’], outputs=[‘yard’],)
n9 = h.make_node(‘Less’, inputs=[‘c6’, ‘yard’], outputs=[‘yard_ok’], name=”yardMore”) # note reversaln10 = h.make_node(‘And’, inputs=[‘price_ok’, ‘yard_ok’], outputs=[‘result’], name=’andBools’)# The graph
g3 = h.make_graph([c5, c6, caux1, caux2, caux3, caux4, n6, n7, n8, n9, n10], ‘postprocessing’,
[h.make_tensor_value_info(‘x’, tp.FLOAT, [3]), h.make_tensor_value_info(‘yhatlog’, tp.FLOAT, [1])],
[h.make_tensor_value_info(‘result’, tp.BOOL, [1])])# The model and check:
m3 = h.make_model(g3, producer_name=’scailable-demo’)
checker.check_model(m3)# Save the model
save(m3, ‘post-processing.onnx’)
同样,结果很容易验证:
# test
sess = rt.InferenceSession(“post-processing.onnx”) # Start the inference session and open the model
x = input_example.astype(np.float32) # Use the input_example from block 0 as inputyhatlog = np.array(yhatlog).flatten()
result = sess.run([“result”], {“x”: x, “yhatlog” : yhatlog}) # Compute the standardized outputprint(“Check:”)
print(“Predicted price {} and yardsize {} are appealing {}.”.format(np.exp(yhatlog), input_example[0], result))
后处理流水线。图片作者。
4d。把所有的放在一起
虽然将处理管道的每个部分都转换为 ONNX 以便于部署很好,但 ONNX 的强大之处之一是能够将一组操作链接到另一组操作。因此,我们可以轻松地在一个 ONNX 图中指定完整的管道:
g_full = h.make_graph([c1, n1, c2, n2, c3, c4, n3, n4, n5, c5, c6, caux1, caux2, caux3, caux4, n6, n7, n8, n9, n10],
‘fullpipeline’,
[h.make_tensor_value_info(‘x’, tp.FLOAT, [3])],
[h.make_tensor_value_info(‘result’, tp.BOOL, [1])])m_full = h.make_model(g_full, producer_name=’scailable-demo’)
checker.check_model(m_full)# Save the model
save(m_full, ‘full-pipeline.onnx’)
同样,很容易验证我们的结果:
# test
sess = rt.InferenceSession(“full-pipeline.onnx”) # Start the inference session and open the model
xin = input_example.astype(np.float32) # Use the input_example from block 0 as inputyhatlog = np.array(yhatlog).flatten()
result = sess.run([“result”], {“x”: xin}) # Compute the standardized outputprint(“Check:”)
print(“Example {} is appealing: {}.”.format(xin, result))
上面的代码演示了我们数据集中的第一所房子不符合我们的需求,因为预测价格超过 400.000E。
当使用 Netron 查看时,我们得到的 ONNX 管道如下所示:
ONNX 中创建的完整数据处理管道。图片作者。
请注意,只要输入和输出匹配,就可以将部分图形组合成其他图形,这使得在 ONNX 中存储常见的预处理和后处理操作并在各种项目中重用它们变得很容易。
5.部署。
上面我们希望展示 ONNX 不仅仅是各种复杂的人工智能训练工具使用的抽象文件格式。ONNX 还通过将手工制作的 ONNX 块链接在一起,使手动构建预处理和后处理流水线变得容易。因此,ONNX 是一个非常有效的工具,可以创建可以在任何地方使用(和重用)的数据分析管道。
一旦有了 ONNX 管道,它的部署就有了各种选项:
- 您可以使用 onnxruntime (这是我们在上面的一些快速测试中使用的)。如果您想在云上部署,一个选择是创建一个简单的 REST 端点(例如使用 Flask )来执行
onnxruntime
,并使用 Docker 来部署它。虽然这相对容易,但也经常效率很低(并且消耗内存)。 - 您可以使用 Scailable 提供的工具将 ONNX 模型传输到 WebAssembly,以实现极其高效的部署。实际上,由于其详细程度,ONNX 使得基于 ONNX 图自动生成低级语言的独立可执行文件成为可能。这提供了本机执行速度(当移植到
c
时),并且(当移植到像.wasm
这样的可移植目标时)允许您将完全相同的模型从云移动到边缘,甚至移动到小型(I)物联网设备。
相当酷。
最后,简单比较一下上面提到的两种部署选项的大小和速度:
- 我们在上面创建的完整 ONNX 管道消耗不到 1Kb 的内存。然而,为了运行,
onnxruntime
本身需要 200Mb 多一点。使用这个内存足迹,我可以在我的本地机器上,在 1.9 秒内执行我们的管道 1000 次(当每次重新启动会话时)。 - 使用到 WebAssembly 的转换(就像 Scailable 为任何 ONNX 图所做的那样)对于
.wasm
二进制文件来说,内存占用大约是 70Kb(这大于.onnx
规范,因为包括了必要操作符的功能规范),但是对于运行时来说只有 60Kb。这样,总共不到< 0.2 Mb 。使用这个内存足迹,我可以在同一台本地机器上,在 0.7 秒内生成 1000 个推理(类似地,每次重新加载;在没有重新加载的情况下,ONNX 和 WebAssembly 运行时之间的时间差实际上是 0,因为一旦初始化,两者都以接近本机的速度运行。
所以是的,结合 ONNX 和 WebAssembly 提供了跨所有目标的表现力(ONNX)和效率(WASM)。
所以是的,结合 ONNX 和 WebAssembly 提供了跨所有目标的表现力(ONNX)和效率(WASM)。
我希望你喜欢这个教程;如有任何与 ONNX / WebAssembly 部署相关的问题,请随时联系我们!
放弃
值得注意的是我自己的参与:我是 杰罗尼姆斯数据科学院 的数据科学教授,也是Scailable的联合创始人之一。因此,毫无疑问,我对 Scailable 有既得利益;我有兴趣让它成长,这样我们就可以最终将人工智能投入生产并兑现它的承诺。这里表达的观点是我自己的。
用赋格创造熊猫和火花兼容功能
用一行代码在熊猫和 Spark 之间无缝切换
动机
数据科学团队通常会投资一套通用的工具,并构建一个共享的 Python 库。这个库包括清理和转换数据的常用方法,以及用于机器学习的实用函数(如自定义指标或特定的训练测试拆分)。
拥有这样一个库为更快地启动新项目提供了基础。理想情况下,随着团队承担更多的项目,共享库会继续成长和成熟。
虽然拥有一个共享库在概念上很棒,但是在不同的设置中使用相同的代码会有一些问题。有不同的用例需要不同的工具。有些数据集大约有数万行,有些数据集可能有数亿行。大数据通常要求 Spark 利用分布式计算。然而,如果一个项目足够小,可以在 Pandas 上完成,那么就没有必要使用集群,也没有必要增加使用 Spark 的开销。正如所料,使用两个框架会导致维护共享库时出现问题。在一个框架中开发的功能不适合另一个框架。
熊猫和星火的区别
为了展示不同框架在语法上的不同,下面是一个用例示例。与任何数据科学工作一样,存在数据缺失的情况,我们会尝试填补它。在下表中,我们对使用某人的家庭状态感兴趣。如果家庭状态不可用,我们得到工作状态。如果工作状态也不可用,我们将尝试使用电话号码区号来推断状态。
| id | home_state | work _state | phone |
|-----|------------|-------------|----------------|
| "A" | "IL" | "IL" | "217-123-4567" |
| "B" | None | "WI" | "312-123-4567" |
| "C" | "FL" | "FL" | "352-234-5678" |
| "D" | "CA" | "CA" | "415-345-6789" |
| "E" | None | None | "217-123-4567" |
| "F" | "IL" | None | "312-234-5678" |
以上问题的 Pandas 和 Spark 实现可以在下面的 Github Gist 中看到。对于一个相对简单的转换,Pandas 和 Spark 之间的代码看起来已经非常不同。除了语法之外,更大的问题是我们有一个定制的业务逻辑不能在 Pandas 和 Spark 应用程序之间循环使用。
随着时间的推移,随着项目规模的增加,以及涉及到更复杂的逻辑来转换数据,被锁定到一个框架中变得更加复杂。最终,代码基础逻辑变得与框架紧密耦合,切换框架的开销逐渐增加。
如何才能跨熊猫和 Spark 回收自定义逻辑?我们是否需要实现每个功能的两个版本(一个用于 Spark,一个用于 Pandas)?或者有没有办法把逻辑和计算分开?我们能否专注于定义逻辑,然后选择运行它的底层引擎(Spark 或 Pandas)?
逻辑和执行的解耦允许以与规模和框架无关的方式编写代码。即使数据现在可以由熊猫处理,1 年后它仍然足够小吗?两年后呢?与规模和框架无关的代码允许数据科学家根据数据量在执行引擎之间无缝切换。为 Spark 回收代码不需要重写。
火花和熊猫兼容代码与赋格
神游标志
这就是赋格出现的地方。Fugue 是一个开源框架,作为一个适应不同计算框架的抽象层。它统一了分布式计算的核心概念,并将逻辑从用于处理数据的框架(如 Spark、Pandas、Dask)中分离出来。神游适应用户,而不是用户适应神游。
赋格建筑
它还旨在使分布式计算民主化,并使不熟悉 Spark 的人更容易使用它。通过使用 Fugue,用户可以用原生 Python 编写代码,然后轻松地通过逻辑将其移植到 Spark 或 Dask,而不必重写代码。
赋格例子
让我们重温一下上面的例子,用原生 Python 和赋格来求解。
赋格示例代码
代码示例中首先要注意的是,第 9–16 行定义的函数是用基本 Python 编写的,没有赋格依赖项。使用的所有数据类型都是原生 Python。函数是纯逻辑的,没有熊猫或者 Spark 的影响。
第二件事是第 9 行中的模式提示。模式在赋格中是强制的,它强制更明确和描述性的代码。如果用户不再神游,这个功能仍然可以被修改为用应用语句来处理熊猫或 Spark。模式提示只是一个注释,但仍然是有用的。
最后要注意的是第 18 行中定义的 FugueWorkflow 上下文管理器。默认的执行引擎是 Pandas,但是如果我们想要将整个工作流引入 Spark,我们可以通过一行代码的修改来实现。
with FugueWorkflow(SparkExecutionEngine) as dag:
现在,FugueWorkflow 上下文管理器中的所有内容都将在 Spark 中运行。
神游的好处
随着工作流复杂性的增加,神游的好处也随之增加。最明显的好处是,同样的逻辑将适用于熊猫和 Spark jobs。对于需求快速增长的团队来说,这给了我们灵活性和敏捷性来无缝地适应更大的数据量。即使数据科学团队可以处理熊猫当前的数据量,最终仍有可能需要 Spark。使用 Fugue,转换是毫不费力的,因为改变计算引擎只需要改变一行代码。
此外,Fugue 允许用户在更易管理的数据上构建原型,并在需要时可靠地扩展。这有可能提高开发人员的工作效率并降低集群成本。当准备好在完整数据集上测试代码时,可以交换执行引擎。Spark 作业每次运行的集群成本很容易达到数百美元。这节省了资金,因为集群不需要启动来测试代码。
最后,Fugue 允许用户为更小、更易测试的功能编写单元测试。逻辑是明确的,并且独立于框架。这增强了代码库的可维护性。
神游和考拉的区别
Databricks 有一个名为考拉的库,允许熊猫用户在 Spark 上使用相同的 DataFrame API。在某些情况下,更改 import 语句将允许用户从 Pandas 执行更改为 Spark 执行。
这里的一个问题是,不是所有的熊猫函数都在考拉中实现,这意味着一些熊猫编写的代码不会通过改变导入语句立即工作。
即使实现了这些功能,Pandas 和 Spark 之间也存在难以调和的差异。例如,Pandas 允许混合类型的列(包含整数和字符串),而 Spark DataFrames 不允许。使用混合类型的考拉会导致错误。赋格遵循火花,和阿帕奇箭头,在强有力的和明确的打字。
结论
我们已经看到了如何使用 Fugue 来创建 Python 函数,能够在 Pandas 和 Spark 之间使用而不做任何更改。这消除了维护每个函数的两个版本的需要,并且还将逻辑与底层执行引擎解耦。
用神游写一个代码库,让用户一行代码变化就能在熊猫和 Spark 之间变化。
联系我们
如果你对使用赋格感兴趣,想给我们反馈,或者有任何问题,我们很乐意在 Slack 上聊天!我们还将为对在数据工作流中应用 Fugue 感兴趣的数据团队举办更详细的研讨会。
创建 Python 虚拟环境
了解如何用 Python 创建虚拟环境,并使用 requirements.txt 安装依赖项
在 Python 中,虚拟环境是运行 Python 程序的隔离环境。使用虚拟环境允许你的程序拥有自己的依赖项(不同版本的包)。例如,程序 A 使用特定版本的 packageX,而程序 B 使用旧版本的 packageX。这是一个直观的问题:
作者图片
如果你把两个程序放在同一个默认环境中,你会遇到 packageX 的问题——如果你试图安装一个特定版本的 packageX,它会覆盖另一个版本。
更好的解决方案是创建两个隔离的环境——又名 虚拟环境 。每个虚拟环境将托管每个程序所需的特定版本的 packageX,如下所示:
作者图片
在本文中,我将向您展示如何使用 conda 包管理器创建虚拟环境。此外,我还将向您展示如何获得已经安装在您的环境中的包的列表(通过**pip**
命令),并使用**requirements.txt**
文件在另一个虚拟环境中执行相同的安装。
Conda是一个运行在 Windows、Mac OS 和 Linux 上的开源包和环境管理系统。
本文假设您的计算机上安装了 Anaconda(https://www.anaconda.com/products/individual-d)。
创建新的虚拟环境
要创建一个新的虚拟环境,使用带有以下选项的**conda**
命令:
**conda create --name Project1 python=3.9**
上面的命令创建了一个名为 Project1 的虚拟环境,Python 版本设置为 3.9:
查看虚拟环境列表
创建虚拟环境后,您可以使用以下命令查看您计算机上的虚拟环境列表:
**conda info --envs**
您应该会看到如下所示的内容:
(base) weimenglee@Wei-Mengs-Mini ~ % **conda info --envs**
# conda environments:
#
base * /Users/weimenglee/miniforge3
**Project1 /Users/weimenglee/miniforge3/envs/Project1**
激活虚拟环境
创建了新的虚拟环境后,是时候激活它了:
(base) weimenglee@Wei-Mengs-Mini ~ % **conda activate Project1****(Project1)** weimenglee@Wei-Mengs-Mini ~ %
正如您在上面看到的,一旦 Project1 虚拟环境被激活,您将在命令提示符前面看到虚拟环境的名称 (Project1) :
**(Project1)** weimenglee@Wei-Mengs-Mini ~ %
查看当前环境中安装的 Python 包列表
要了解您的虚拟环境中当前安装了哪些包,请使用**pip freeze**
命令:
(Project1) weimenglee@Wei-Mengs-Mini ~ % **pip freeze**
该命令将返回当前环境中安装的软件包列表,以及它们的版本号。以下是我的项目 1 虚拟环境的输出示例:
certifi==2021.5.30
有时,**pip freeze**
命令会包含从直接 URL 引用安装的发行版的直接引用,如下所示:
certifi==2021.5.30**chardet @ file:///Users/runner/miniforge3/conda-bld/chardet_1610093454858/work**
为了防止这种情况发生,对**pip freeze**
使用以下替代命令:
**pip list --format=freeze**
这将确保结果始终采用以下格式:
*<package_name>==<version_number>*
现在让我们尝试使用**pip install**
命令在我们的项目 1 虚拟环境中安装一些额外的包:
(Project1) weimenglee@Wei-Mengs-Mini ~ % **pip install pandas numpy matplotlib**
以上命令安装以下软件包:
- 熊猫
- NumPy
- Matplotlib
您现在可以使用**pip freeze**
命令(或**pip list —-format=freeze**
)来查看您拥有的软件包列表,包括您刚刚安装的软件包。
(Project1) weimenglee@Wei-Mengs-Mini ~ % **pip freeze**
certifi==2021.5.30
cycler==0.10.0
kiwisolver==1.3.2
matplotlib==3.4.3
numpy==1.21.2
pandas==1.3.2
Pillow==8.3.2
pyparsing==2.4.7
python-dateutil==2.8.2
pytz==2021.1
six==1.16.0
此外,您还将把列表保存到一个名为 requirements.txt 的文件中:
(Project1) weimenglee@Wei-Mengs-Mini ~ % **pip freeze > requirements.txt**
**requirements.txt**
文件包含当前环境中安装的软件包列表。稍后,您将利用它在另一个虚拟环境中重新安装所有软件包。
K ey takeaway —无论您在这个虚拟环境中安装了什么包,它都将留在这个虚拟环境中。它不会与您在另一个虚拟环境中安装的其他类似软件包冲突。
测试新环境
您现在可以测试环境是否正常工作。键入以下命令启动 Python 解释器:
(Project1) weimenglee@Wei-Mengs-Mini ~ % **python**
然后,导入熊猫包并打印其版本:
>>> **import pandas**
>>> **print(pandas.__version__)**
如果您能够看到上面的输出,那么软件包安装正确。
键入**exit()**
退出 Python 解释器。
停用环境
当您完成环境后,您可以使用**conda deactivate**
命令将您带回基本环境:
(Project1) weimenglee@Wei-Mengs-Mini ~ % **conda deactivate**
移除环境
完成项目后,如果不再需要该环境,可以使用以下命令删除该环境:
(base) weimenglee@Wei-Mengs-Mini ~ % **conda env remove --name Project1***Remove all packages in environment /Users/weimenglee/miniforge3/envs/Project1:*
使用 requirements.txt 文件创建另一个虚拟环境并安装软件包
现在让我们创建另一个虚拟环境,称之为项目 2 :
(base) weimenglee@Wei-Mengs-Mini ~ % **conda create --name Project2 python=3.9**
创建后,激活新的虚拟环境:
(base) weimenglee@Wei-Mengs-Mini ~ % **conda activate Project2
(Project2)** weimenglee@Wei-Mengs-Mini ~ %
让我们试试 Python 解释器,看看 Pandas 模块是否可用:
(Project2) weimenglee@Wei-Mengs-Mini ~ % python
Python 3.9.6 (default, Aug 16 2021, 12:43:27)
[Clang 12.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> **import pandas**
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
*ModuleNotFoundError: No module named 'pandas'*
>>> **exit()**
正如预测的那样,Pandas 是默认不安装的。如果您想要安装已经安装在项目 1 虚拟环境中的软件包,现在您可以使用您之前创建的**requirements.txt**
文件:
(Project2) weimenglee@Wei-Mengs-Mini ~ % **pip install -r requirements.txt** Requirement already satisfied: certifi==2021.5.30 in ./miniforge3/envs/Project2/lib/python3.9/site-packages (from -r requirements.txt (line 1)) (2021.5.30)
Collecting cycler==0.10.0...Collecting matplotlib==3.4.3
Using cached matplotlib-3.4.3-cp39-cp39-macosx_11_0_arm64.whl
Collecting numpy==1.21.2
Using cached numpy-1.21.2-cp39-cp39-macosx_11_0_arm64.whl (12.4 MB)
Collecting pandas==1.3.2
Using cached pandas-1.3.2-cp39-cp39-macosx_11_0_arm64.whl...Installing collected packages: six, pytz, python-dateutil, pyparsing, Pillow, numpy, kiwisolver, cycler, pandas, matplotlib
Successfully installed Pillow-8.3.2 cycler-0.10.0 kiwisolver-1.3.2 matplotlib-3.4.3 numpy-1.21.2 pandas-1.3.2 pyparsing-2.4.7 python-dateutil-2.8.2 pytz-2021.1 six-1.16.0
在**requirements.txt**
文件中指定的所有包现在将被安装在项目 2 虚拟环境中。
结论
使用**requirements.txt**
文件是指定 Python 程序依赖关系的好方法。在这个文件中,您放置了您的应用程序需要的所有包名(以及它们的特定版本号),这样无论谁运行您的程序,都只需要使用**requirements.txt**
文件执行一个**pip install**
。
用 Python 创建用于分析实时数据的可扩展机器学习系统—第 1 部分
本系列展示了如何实时输入数据、分析数据并将响应发送回感兴趣的受众。
最近,人们对用于分析实时数据并将结果实时发送给感兴趣的群体的可扩展机器学习(ML)系统产生了浓厚的兴趣。越来越多的企业热衷于为其业务部署实时预测或分析,这不仅是为了与众不同,也是为了在当今竞争激烈的和快节奏的市场中提高整体绩效、客户体验和保留率。
从股票交易到使用天气数据预测航班延误,动物和人类的实时行为分析,或者构建客户支持聊天机器人,有不同的实时问题和不同的解决方法-数据分类和聚类,实时训练 ML 模型和反馈循环以在发生时进行完善,实时决策树,以及许多这样的不断发展的算法。
几年前,跟踪这些算法、验证、实施、测试、改进和循环使用是一个非常困难的问题。但是,因为像 TensorFlow 、 PyTorch 、 NumPy 、 Scikit 这样的机器学习和科学计算工具的兴起,这些任务都变得‘琐碎’了。此外,所有这些工具都支持 Python 或基于 Python,因此 Python 是解决这些问题的最合适的选择。
虽然分析部分由这些工具负责,但添加实时通信和实现接近零的延迟也有其自身的挑战——排队、有时限的保证交付和确认、离线交付等。幸运的是,我们有另一个 python 库来拯救我们,mes ibo——它使解决方案变得完整,完全用 Python 编写。
在本文中,我们将学习如何用 Python 开发可扩展的机器学习系统来分析实时数据,这将带来无限的可能性;处理实时股票市场或地理传感器只是一些随机的例子。如果您是 Python 开发人员,您会发现这篇文章是一个很好的起点。
为了分析任何数据,我们首先需要访问数据。因此,在第一部分中,我们将描述如何向 Python 应用程序添加实时通信,如何实时输入源数据并发送输出。然后,我们将在第二部分描述将整个实时平台下载到您的服务器,与您的数据库进行交互,以及添加 Tensorflow 和其他工具。
实时与传统数据处理
在我们使用 mesibo 实时库之前,有必要快速了解一下实时和传统数据处理之间的区别。传统的和当今最主要的数据处理模型基于请求-响应模型,通常称为 REST,被超过 90%的应用程序使用。尽管 REST 对于静态或缓慢变化的数据非常优秀,但它不适合实时数据。实时数据处理应用程序保持实时连接,以零延迟方式发送和接收数据。如果您使用过传统的基于 REST 的 API,您可能会发现实时 API 略有不同,如下所述。
使用 mesibo 实时 Python 库
让我们从在 Python 应用程序中添加实时通信开始。首先,您需要使用pip
命令从 Python 包索引中下载 mesibo 库。或者,您也可以从 mesibo Python 库下载库源代码。Mesibo python 库支持所有 Linux 发行版、Mac OS,甚至 Raspberry Pi,因此您可以在几乎所有开发人员友好的平台上下载它。
**pip3 install mesibo**
一旦安装了 mesibo,就可以将 mesibo 导入到 Python 脚本中,如下所示
将 mesibo 导入 Python 脚本
mesibo 有一套定义非常好的 API。它有各种发送实时数据的函数和一个回调接口,一旦接收到实时传入数据就调用该接口。下面的脚本是一个简单的 mesibo 回调示例,当接收到一个实时消息(Mesibo_OnMessage
)或者当一个实时消息被发送、传递、读取等时,就会调用这个示例。(Mesibo_OnMessageStatus
)。还有更多回调函数,但为了简单起见,这里省略了。
但是,mesibo 不知道您的侦听器类,因此您应该首先向 mesibo 注册它,以便它可以在需要时调用您的侦听器类函数。您可以将它作为初始化的一部分,如下所示(细节将在后面解释)。
梅西博初始化
上面的代码不言自明。
- 它创建了一个 mesibo API 的实例。
- 通过调用函数
*addListener*()
,用我们的监听器初始化 mesibo。 - 通过调用函数
*setAccessToken*()
初始化 API 凭证,这将在下一节中解释。 - 通过调用函数
*setDatabase*()
设置数据库以存储收到的消息 - 启动 mesibo,通过调用函数
*start*()
开始发送和接收实时消息
一旦你启动了 mesibo,你不需要做任何事情就可以获得你的实时数据或消息。 您不会像使用 REST API时那样轮询或持续检查新消息。相反,一旦接收到新的实时数据,mesibo 将自动调用监听器函数,没有任何延迟。
但是,您的脚本如何知道谁发送了您刚刚收到的数据,或者如何将数据发送到特定的目的地或组?这就带来了实时端点的概念。
实时端点和地址
当你给你的朋友发电子邮件时,你使用他们的电子邮件地址。同样,当你的朋友收到一封电子邮件时,他们知道这是你发来的,因为邮件中包含了你的电子邮件地址。类似地,在实时系统中,每个端点都有一个地址。端点可以是任何东西,您的 Python 脚本、聊天用户或群组。在 mesibo 中,地址可以是任何唯一的字符串,无论是电子邮件地址、电话号码还是任何随机的唯一字符串。
创建端点
然后,端点使用这些地址相互进行实时通信。当您创建一个端点时,mesibo 会生成一个令牌,您可以在*setAccessToken*()
中初始化时使用这个令牌。
例如,您可以通过调用 mesibo 后端 API 创建一个地址为123
的端点,
curl 请求调用 mesibo 后端 API
上面请求中的token
是 mesibo API 密钥(也称为 App token)。强烈建议您阅读 mesibo 教程以了解如何将 mesibo 后端 API 与您自己的 App token 一起使用。
上面的 API 将返回一个 JSON 响应,
来自 mesibo 后端 API 的 JSON 响应
然后,您可以在 Python *setAccessToken*()
API 中使用上述响应中返回的用户令牌。
在初始化 mesibo 和设置 listener 之后,您现在已经设置好了。我们现在将学习如何发送实时数据
发送实时数据
向另一个端点发送实时数据很简单。你只需要指定地址,消息,并调用*api.sendMessage*()
函数,
只要发送一条消息,接收端就会立即调用*Mesibo_OnMessage*
功能。参考上面的清单,我们已经定义了监听器函数*Mesibo_OnMessage*
同样的,你可以给群发消息,参考梅斯博教程了解更多关于创建群的知识。一旦您向群组发送消息,所有群组成员将立即调用*Mesibo_OnMessage*
功能。
运行和测试您的脚本
我们现在准备运行我们的脚本。为了方便起见,完整的脚本在这里,它是从 mesibo Python 库下载的。
要使用您的脚本运行和测试实时通信,如上所述创建两个端点,比如说123
和456
,并为每个端点生成令牌。然后创建脚本的两个副本,并替换每个端点的令牌值。此外,替换另一个端点的目的地地址,例如,如果您使用端点的令牌123
,则在 sendMessage 中使用目的地456
,反之亦然。
现在运行脚本,您将看到两者都能够向对方发送实时数据。您还会看到连接状态。一旦成功执行了脚本,就可以探索更多的 mesibo APIs 来发送各种数据。
关于第二部分的结论和信息
在第一部分中,我们探索了如何发送和接收实时数据。对于第一部分,我们使用了 mesibo 托管的 API。在第二部分中,我们将学习如何将整个 mesibo 平台下载到您的服务器,将其与您的数据库连接,然后添加 Tensorflow。同时,我建议浏览一下 mesibo 教程,它更详细地描述了本文中显示的步骤。你也可以尝试 mesibo 支持的其他平台(Android、iOS、Javascript、C++),并把它们与你的 Python 脚本接口。
我希望您喜欢阅读第 1 部分,并且能够通过实时通信运行您的脚本。如果您有任何问题,或者您希望我在第二部分中解决一些具体的问题,请发表评论。
创建独立于比例的 SVG 图表
使用皮革优化探索性制图
艾萨克·史密斯在 Unsplash 上拍摄的照片
数据可视化在数据分析中起着重要的作用,因为人眼一看到一些图表或图形,就会试图在图形中找到模式。
数据可视化是使用不同的图/图形/图表直观地表示数据,以找出模式、异常值以及数据集不同属性之间的关系。它是数据的图形表示。
Leather 是一个开源 python 库,用于创建与比例无关的 SVG 图表。它可以绘制你传递给它的任何数据。它完全是用 python 开发的,没有其他依赖。
在这篇文章中,我们将探讨如何使用皮革创建不同的图表和情节。
让我们开始吧…
安装所需的库
我们将开始安装皮革像任何其他图书馆使用 pip 安装。
pip install leather
导入所需的库
在这一步中,我们将导入使用皮革创建图表和绘图所需的库。
import leather
使用皮革创建图表
接下来,我们将开始创建条形图和曲线图:
- 条形图
data = [
(3, 'Hello'),
(5, 'How'),
(9, 'Are'),
(4, 'You')
]chart = leather.Chart('Bars')
chart.add_bars(data)
chart.to_svg('bars.svg')
来源:作者
2。点阵图
data = [
(0, 3),
(4, 5),
(7, 9),
(8, 4)
]chart = leather.Chart('Dots')
chart.add_dots(data)
chart.to_svg('dots.svg')
来源:作者
3。折线图
data = [
(0, 3),
(4, 5),
(7, 9),
(8, 4)
]chart = leather.Chart('Line')
chart.add_line(data)
chart.to_svg('lines.svg')
来源:作者
4。使用 CSV 阅读器绘图
import csvimport leatherwith open('gii.csv') as f:
reader = csv.reader(f)
next(reader)
data = list(reader)[:10]for row in data:
row[1] = float(row[1]) if row[1] is not None else Nonechart = leather.Chart('Data from CSV reader')
chart.add_bars(data, x=1, y=0)
chart.to_svg('csv_reader.svg')
来源:作者
这就是你如何使用皮革创建不同类型的图表和情节。
继续尝试不同的数据集,并让我知道您在回复部分的评论。
本文是与皮尤什·英格尔合作完成的
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。
创建可共享的数据可视化报告
使用 Datapane 进行数据分析和可视化
来源:作者
可以使用不同的 Python 库创建用于数据分析的数据可视化。我们只需要创建不同的可视化,如条形图,直方图等。有各种各样的用于数据可视化的 python 库,可以创建 N 种可视化,但是这些可视化的问题是,您不能以报告格式共享它们,也不能将它们嵌入报告中。
创建数据可视化报告有助于进行演示,或者与客户或老板分享。当您创建一个包含可视化和降价的报表时,它会产生良好的影响。此外,您还可以共享这些报告。
Datapane 是一个开源的 python 库,用于创建数据可视化和分析报告,我们可以将所有的可视化和分析汇总到一个报告中,该报告可以在 Datapane 服务器上发布,并可以轻松共享。
让我们开始吧…
创建数据面板帐户
在创建您的报告之前,您首先需要在 Datapane 上创建一个帐户,以便我们可以在该帐户上发布我们的报告,并与任何人共享链接。要创建帐户,您可以通过下面的链接。一旦您创建了一个帐户,您就可以看到一个数据面板访问密钥,我们将使用它从我们的 Jupyter 笔记本上发布我们的报告。
https://datapane.com/accounts/signup/
安装所需的库
我们将从使用 pip 安装 Datapane 开始。下面给出的命令可以做到这一点。
pip install datapane
安装后,我们还将加载用于向我们的帐户发布报告的登录令牌(访问密钥)。
!datapane login --token=<YOUR TOKEN>
导入所需的库
在这一步中,我们将导入所需的库来创建可视化。
import datapane as dp
import pandas as pd
import seaborn as sns
导入数据集
我们将从导入所需的数据集开始。对于本文,我们使用 seaborn 库中已经定义的名为 Tips 的数据集。
df = sns.load_dataset('tips')
df.head()
数据集(来源:作者)
创建可视化
现在,我们将创建一些可视化效果,并添加到我们的报告中。
plot1 = sns.regplot(x="total_bill", y="tip", data=df)
plot2 = df.hist()
plot 3 = df.boxplot(column=['total_bill'])
创建并发布报告
这是最后一步,我们将创建并发布报告。
report = dp.Report(
dp.Text("Visualization Report"),
dp.Plot(plot1),
dp.Plot(plo2),
dp.Plot(plot3)
)report.publish(name='My First Report', open=True)
发布报告后,您可以在提供给您的链接上看到它,并且可以通过任何人的邮件 id 与他们分享。
继续尝试不同的数据集,为数据分析创建漂亮的可视化报告。如果您发现任何困难,请在回复部分告诉我。
本文是与 Piyush Ingale 合作完成的。
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。
使用 Matplotlib 中的线图创建测井数据的简单测井图
使用 Python 中的 matplotlib 库可视化测井数据与深度
使用 matplotlib Python 库创建的测井曲线。图片作者。
介绍
测井曲线是地球科学和岩石物理学中常用的可视化工具。它们使得沿井眼长度(深度)采集的数据(例如,伽马射线、中子孔隙度、体积密度等)易于可视化。在这些图上,我们在 x 轴上显示测井测量值,在 y 轴上显示测量深度或真实垂直深度。
在这篇短文中,我们将看到如何从一口 Volve 井创建一个简单的测井曲线可视化,该井是 Equinor 在 2018 年作为更大数据集的一部分发布的。
我之前已经在以下文章中介绍了制作这些情节的不同方面:
- 使用 Python 在测井图上显示岩性数据
- 用 Python 显示随钻测井(LWD)图像测井
- 使用绘图填充增强测井曲线的可视化
对于本教程,笔记本可以在这里找到,下面的视频也是附带的。
导入库和加载 LAS 数据
任何 python 项目或笔记本的第一步都是导入所需的库。在这种情况下,我们将使用lasio
来加载我们的 las 文件,pandas
来存储我们的测井记录数据,而matplotlib
用于可视化我们的数据。
import pandas as pd
import lasio
import matplotlib.pyplot as plt
为了读取数据,我们将使用 lasio 库,这是我们在之前的笔记本和视频中探索过的。
las = lasio.read("Data/15-9-19_SR_COMP.LAS")
一旦文件被加载,我们可以通过使用df.head()
来检查文件的内容。这将返回数据帧的前五行。
df = las.df()
df.head()
Equinor Volve 数据集 15/9–19-SR 井的前五行
我们可以从返回的结果中看到,我们有几列数据,每列代表测井工具沿井眼移动时进行的测量。
这些列表示以下内容:
- AC 代表声波压缩慢度
- 井径仪校准
- 容积密度的 DEN
- 伽马射线的 GR
- 中子孔隙度的 NEU
- RDEP 深电阻
- 中等电阻率 RMED
为了更容易使用我们的数据帧,我们可以将设置为深度的数据帧索引转换为数据帧中的列。我们可以通过像这样重置索引来实现这一点。
df.reset_index(inplace=True)
请注意,inplace=True
允许我们对原始 dataframe 对象进行更改。
我们可以再次调用df.head()
来确保我们的新列已经创建。
df.head()
Equinor Volve 数据集 15/9–19-SR 井的前五行
我们还需要对 DEPT 列稍加重命名,并将其改为 DEPTH
df.rename(columns={'DEPT':'DEPTH'}, inplace=True)df.head()
部门列重命名后,Equinor Volve 数据集 15/9–19-SR 井的前五行
现在我们的数据格式正确,列也标记正确,我们可以继续生成对数图了。
用 Matplotlib 创建测井曲线
创建简单的线形图
通过调用df.plot()
并传递我们的两个列,我们可以很容易地创建一个简单的情节
df.plot('GR', 'DEPTH')
伽马射线与深度的简单线图。
当我们运行这个单元格时,我们得到一个非常简单的图,很难阅读,也是颠倒的。
快速支线剧情
如果我们想查看数据框中的所有列,我们可以生成一个子图网格。
这是通过使用与之前相同的线(df.plot()
)来完成的,我们没有传入曲线名称,而是传入了subplots=True
。我们还可以指定一个图形大小(figsize()
),它控制图形将显示多大。
df.plot(subplots=True, figsize=(15, 15))
使用 df.plot()的测井数据的多个子图
现在我们可以看到一个网格图,数据框中的每一列都有一个网格图。这是检查我们哪里有数据以及哪里可能有差距的有用方法。
然而,我们对这个情节没有多少控制权。在接下来的章节中,我们将了解如何通过多次测量建立测井曲线。
在 Matplotlib 中使用子情节
在使用 matplotlib 中有许多方法可以生成支线剧情。对于这个特别的教程,我们将使用subplot2grid
。
fig = plt.subplots(figsize=(10,10))
#Set up the plot axis
ax1 = plt.subplot2grid((1,1), (0,0), rowspan=1, colspan = 1)
ax1.plot("GR", "DEPTH", data = df, color = "green") # Call the data from the df dataframe
ax1.set_xlabel("Gamma") # Assign a track title
ax1.set_xlim(0, 200) # Change the limits for the curve being plotted
ax1.set_ylim(4700, 3500) # Set the depth range
ax1.grid() # Display the grid
在上面的代码中,我们首先要定义一个图形(fig)并将其赋给plt.subplots
。在支线剧情参数中,我们可以传入图形大小,我们将它设置为 10 乘 10。
接下来,我们需要在我们的图形对象上创建一个轴。我们可以通过赋值ax1
等于plt.subplot2grid()
来做到这一点…我们首先传递子情节网格的形状,在本例中,我们将 1 对 1 设置。
接下来,我们使用索引位置来指定子情节的位置。由于我们只有 1 个子情节,我们将把位置设置为零,零(0,0)。Rowspan 和 colspan 都设置为 1,这意味着它们只有 1 列宽 1 行高。
然后,我们需要告诉 matplotlib 我们想要绘制的内容。在这种情况下,我们将使用 ax1.plot,并传入伽马射线和深度列。
使用 ax 符号,我们可以通过设置 x 和 y 限制以及显示网格来进一步定制绘图。
当我们运行这段代码时,我们生成了下面的图。
太好了!这里我们有了一个更好看的图,现在我们可以开始添加新的轨迹/支线剧情到我们的整体日志图中。
添加电阻率测井
为了给我们的图形添加一个新的轨迹/子图,我们可以重复上面所做的,添加一个新的轴,ax2
。
然后我们增加布局参数plt.subplot2grid()
的第二个数字,所以现在是 1 行,2 列。
对于ax2
,我们需要把它放在第二个子情节中,这是通过把位置参数从(0,0)改为(0,1)来完成的。
我们最后得到:
ax1 = plt.subplot2grid((1,2), (0,0), rowspan=1, colspan = 1)
ax2 = plt.subplot2grid((1,2), (0,1), rowspan=1, colspan = 1)
此外,由于电阻率通常是对数标度,我们需要添加行:ax2.semilogx()
。
fig = plt.subplots(figsize=(10,10))
#Set up the plot axes
ax1 = plt.subplot2grid((1,2), (0,0), rowspan=1, colspan = 1)
ax2 = plt.subplot2grid((1,2), (0,1), rowspan=1, colspan = 1)
ax1.plot("GR", "DEPTH", data = df, color = "green") # Call the data from the well dataframe
ax1.set_xlabel("Gamma") # Assign a track title
ax1.set_xlim(0, 200) # Change the limits for the curve being plotted
ax1.set_ylim(4700, 3500) # Set the depth range
ax1.grid() # Display the grid
ax2.plot("RDEP", "DEPTH", data = df, color = "red")
ax2.set_xlabel("Deep Res.")
ax2.set_xlim(0.2, 2000)
ax2.semilogx()
ax2.set_ylim(4700, 3500)
ax2.grid()
使用 matplotlib 创建的伽马射线和电阻率测井图。图片由作者提供。
添加密度日志
要添加第三个轨道/子情节,我们可以通过添加密度作为新的子情节来重复上面的操作。
fig = plt.subplots(figsize=(10,10))
#Set up the plot axes
ax1 = plt.subplot2grid((1,3), (0,0), rowspan=1, colspan = 1)
ax2 = plt.subplot2grid((1,3), (0,1), rowspan=1, colspan = 1)
ax3 = plt.subplot2grid((1,3), (0,2), rowspan=1, colspan = 1)
ax1.plot("GR", "DEPTH", data = df, color = "green") # Call the data from the well dataframe
ax1.set_xlabel("Gamma") # Assign a track title
ax1.set_xlim(0, 200) # Change the limits for the curve being plotted
ax1.set_ylim(4700, 3500) # Set the depth range
ax1.grid() # Display the grid
ax2.plot("RDEP", "DEPTH", data = df, color = "red")
ax2.set_xlabel("Deep Res.")
ax2.set_xlim(0.2, 2000)
ax2.semilogx()
ax2.set_ylim(4700, 3500)
ax2.grid()
ax3.plot("DEN", "DEPTH", data = df, color = "red")
ax3.set_xlabel("Density")
ax3.set_xlim(1.95, 2.95)
ax3.set_ylim(4700, 3500)
ax3.grid()
使用 matplotlib 创建的伽马射线、电阻率和体积密度测井图。图片由作者提供。
整理常见元素
我们可以从前面的代码片段中看到,我们有许多元素在每个轴调用中都重复出现,比如ax.set_ylim(4700, 3500)
。我们可以把它们分开,这样我们只需要调用这些函数一次。这节省了我们需要编写的行数,并使代码更具可读性。
为了实现这一点,我们添加了一个新的 for 循环,它将遍历 fig.axes 中的轴。
for i, ax in enumerate(fig.axes):
ax.set_ylim(4700, 3500) # Set the depth range
ax.grid()
ax.set_xlabel(curve_names[i])
在分离出公共元素后,我们的最终代码如下所示:
fig, axes = plt.subplots(figsize=(10,10))
curve_names = ['Gamma', 'Deep Res', 'Density']
#Set up the plot axes
ax1 = plt.subplot2grid((1,3), (0,0), rowspan=1, colspan = 1)
ax2 = plt.subplot2grid((1,3), (0,1), rowspan=1, colspan = 1)
ax3 = plt.subplot2grid((1,3), (0,2), rowspan=1, colspan = 1)
ax1.plot("GR", "DEPTH", data = df, color = "green")
ax1.set_xlim(0, 200)
ax2.plot("RDEP", "DEPTH", data = df, color = "red")
ax2.set_xlim(0.2, 2000)
ax2.semilogx()
ax3.plot("DEN", "DEPTH", data = df, color = "red")
ax3.set_xlim(1.95, 2.95)
for i, ax in enumerate(fig.axes):
ax.set_ylim(4700, 3500) # Set the depth range
ax.grid()
ax.set_xlabel(curve_names[i])
使用 matplotlib 创建的伽马射线、电阻率和体积密度测井图。图片由作者提供。
减少支线剧情之间的间隙
为了更好地整理剧情,我们可以移除每个子情节/轨道之间的深度标签,并减少它们之间的空间。这是通过仅在 ax2 和 ax3 上循环,以及通过调整图之间的填充宽度来实现的。
#Hide tick labels on the y-axis
for ax in [ax2, ax3]:
plt.setp(ax.get_yticklabels(), visible = False)
#Reduce the space between each subplot
fig.subplots_adjust(wspace = 0.05)
然后我们得出以下结论:
fig, axes = plt.subplots(figsize=(10,10))
curve_names = ['Gamma', 'Deep Res', 'Density']
#Set up the plot axes
ax1 = plt.subplot2grid((1,3), (0,0), rowspan=1, colspan = 1)
ax2 = plt.subplot2grid((1,3), (0,1), rowspan=1, colspan = 1)
ax3 = plt.subplot2grid((1,3), (0,2), rowspan=1, colspan = 1)
#Set up the individual log tracks / subplots
ax1.plot("GR", "DEPTH", data = df, color = "green")
ax1.set_xlim(0, 200)
ax2.plot("RDEP", "DEPTH", data = df, color = "red")
ax2.set_xlim(0.2, 2000)
ax2.semilogx()
ax3.plot("DEN", "DEPTH", data = df, color = "red")
ax3.set_xlim(1.95, 2.95)
#Set up the common elements between the subplots
for i, ax in enumerate(fig.axes):
ax.set_ylim(4700, 3500) # Set the depth range
ax.grid()
ax.set_xlabel(curve_names[i])
#Hide tick labels on the y-axis
for ax in [ax2, ax3]:
plt.setp(ax.get_yticklabels(), visible = False)
#Reduce the space between each subplot
fig.subplots_adjust(wspace = 0.05)
移除 y 标签并减少图之间的空白后,使用 matplotlib 创建伽马射线、电阻率和体积密度测井图。图片由作者提供。
为中子孔隙度的子图添加次轴
标准做法是在同一子图/轨迹上绘制体积密度和中子孔隙度。这两条曲线的相互作用使我们能够识别岩性变化和碳氢化合物的存在。
由于两次测量的单位和尺度不同(堆密度为 1.95 至 2.95 g/cc,中子孔隙度为-15 至 60),我们需要使用 twiny 函数在顶部添加另一个子图。这允许我们在图之间使用相同的 y 轴,但是 x 轴可以变化。
使用ax.xaxis.set_ticks_position('top')
和ax.xaxis.set_label_position('top').
将所有标签移动到图的顶部
然后,我们需要修改主 for 循环,以检查何时到达 ax4(当 i = 3 时,因为 Python 从 0 开始索引),然后调整脊椎,使其位于密度标签上方。
fig, axes = plt.subplots(figsize=(10,10))
curve_names = ['Gamma', 'Deep Res', 'Density', 'Neutron']
#Set up the plot axes
ax1 = plt.subplot2grid((1,3), (0,0), rowspan=1, colspan = 1)
ax2 = plt.subplot2grid((1,3), (0,1), rowspan=1, colspan = 1)
ax3 = plt.subplot2grid((1,3), (0,2), rowspan=1, colspan = 1)
ax4 = ax3.twiny()
#Set up the individual log tracks / subplots
ax1.plot("GR", "DEPTH", data = df, color = "green", lw = 0.5)
ax1.set_xlim(0, 200)
ax2.plot("RDEP", "DEPTH", data = df, color = "red", lw = 0.5)
ax2.set_xlim(0.2, 2000)
ax2.semilogx()
ax3.plot("DEN", "DEPTH", data = df, color = "red", lw = 0.5)
ax3.set_xlim(1.95, 2.95)
ax4.plot("NEU", "DEPTH", data = df, color = "blue", lw = 0.5)
ax4.set_xlim(45, -15)
#Set up the common elements between the subplots
for i, ax in enumerate(fig.axes):
ax.set_ylim(4700, 3500) # Set the depth range
ax.xaxis.set_ticks_position("top")
ax.xaxis.set_label_position("top")
ax.set_xlabel(curve_names[i])
if i == 3:
ax.spines["top"].set_position(("axes", 1.08))
else:
ax.grid()
#Hide tick labels on the y-axis
for ax in [ax2, ax3]:
plt.setp(ax.get_yticklabels(), visible = False)
#Reduce the space between each subplot
fig.subplots_adjust(wspace = 0.05)
最终测井曲线显示了轨迹 1 中的伽马射线、轨迹 2 中的电阻率(对数标度)和轨迹 3 中的密度/neturon(每种标度都不同)。图片作者。
摘要
在这个简短的教程中,我们已经讲述了如何使用 matplotlib 显示测井曲线,如何添加多个轨迹/子曲线,以及如何绘制两条曲线。Matplotlib 提供了一种从头构建简单日志图的好方法,是一个值得学习的好库。
感谢阅读!
如果你觉得这篇文章有用,请随时查看我的其他文章,这些文章从不同的角度研究了 Python 和测井数据。你也可以在 GitHub 找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn或者我的 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
为机器学习创建合成数据
本教程旨在探索如何创建合成数据来训练对象检测模型
训练本身基于 Jacob Solawetz 关于用 YOLOv5 训练自定义对象的教程
所以我将使用 Ultralytics 的 YOLOv5 库。
本教程将指导您完成创建合成数据所需的步骤,并展示如何使用 YOLOv5 对其进行训练,以便处理真实图像。
如果您想访问完整的脚本或下载数据集,您可以在这个 Git 资源库中找到所有内容。
例如,我将训练分类器来检测树上的橙子。
什么是合成数据,我们为什么需要它?
在一个完美的世界中,数据集将是丰富的,我们可以继续在真实图像上训练我们的模型,以便做出更好的预测。然而在现实中,ML 编码者的大部分时间将花费在收集数据和正确注释上。
像谷歌/脸书/亚马逊/苹果这样的大公司,甚至拥有资源的中型公司,都可以着手启动这样的项目。首先,因为他们有数据本身——在我们的例子中是图像。其次,因为他们有能力对数据进行注释,以创建无错误的数据集。但是,即使这些公司也不能确定数据是否被正确标注,所以正常的流程是对每张图片进行多次标注,然后寻找差异。
对于一个小公司或者像我这样试图从头开始构建一个 ML 项目的人来说,这是一个太大的任务。所以我发现自己在使用网上的一个数据集。有许多很好的数据集,在某些情况下,创建一个新的数据集真的没有必要。但是过了一段时间,我开始意识到我所有的项目都没有完全按照我的要求去做,因为它们是在一种不同的数据上被训练的。
对于这个项目,我想在树上数橘子,但找不到合适的数据集。
所以我开始下载图片,并试图给它们加注释。
举个例子,我从维基媒体的下面这张图片开始。
图片来自维基媒体
这不是一棵完整的树,但是很快我就明白了,我不能给这张图片做注释。
我想我可以试着选择简单的图像,但我不确定在训练网络时它们会如何站起来。
图片来自维基媒体
继续看更复杂的图片,比如这张(也来自维基媒体),我明白这不再是一个选项,并开始摆弄我可以在网上找到的不同数据集。
这不是我想做的,很快我就泄气了。
这是我发现自己在寻找一种不同的解决方案,并观看了亚当·凯利斯(我默塞里极限)的伟大教程,他在其中训练了一个网络,使用合成数据识别杂草。你可以在这里观看他的视频 AI 杂草探测器。
虽然我对结果没有太多印象(他似乎有),因为我需要更好的结果,我意识到如果我要继续这个项目,这是要走的路。
因此,为了适应我需要的新关键词,我开始在谷歌上搜索,找到了下面这篇由 Maryam Rahnemoonfar 和 Clay Sheppard 撰写的论文
在他们继续进行并根据无需起身开始拍摄图像就能生成的数据训练他们的网络之后,这篇论文似乎正是我想要的。更有甚者,他们试图做一些和我正在做的事情非常相似的事情。
他们没有提供数据集,看着他们的照片,我想我可以做得更好。
总的来说,他们建议采取以下步骤
图片来自文章
但在我看来,他们没有考虑到水果可能被树叶遮挡的事实,他们也没有计算边界框。
尽管如此,当我被这个成功的事实所鼓舞时,我开始创建我自己的合成数据。
步伐
我意识到,为了让网络能够统计真实数据,我需要做以下事情
- 收集关于我可能遇到的背景的信息
2.创建一个由这些颜色构成的背景图像
3.创建不同大小的圆圈来代替橙子
4.创建一个前景,从树叶的颜色,将阻碍一些橙子
为此,我编写了一个简单的 python 程序,它将为我创建图像—(代码被简化了,所以如果你想下载完整的代码,读者可以很容易地阅读它)
from PIL import Image, ImageDraw
from PIL import ImageFilter
from PIL import ImageColor
from pascal import PascalVOC, PascalObject, BndBox, size_block
from pathlib import Path
import cv2
import numpy as np
import random
我们从一些导入开始,我使用 PIL (pillow)来创建图像,使用 pascal (PascalVoc)来保存信息作为注释。
我从网上下载了一些橘子树的图片,并开始采样像素。他们的颜色被保存到三个数组中的一个
树叶,天空,地面
此时,我没有对橙子取样,因为我对它们使用了不同的方法
def prepare_colors():
txt_leaves = ['#608d2a', '#a8b146', '#ccf0bc']
txt_sky = ['#e9e3c3', '#99949e', '#9bb5cf']
txt_ground = ['#3d2c15', '#dfcba6'] bg_colors = []
fg_colors = [] for t in txt_leaves:
bg_colors.append(ImageColor.getrgb(t))
fg_colors.append(ImageColor.getrgb(t))
for t in txt_sky:
bg_colors.append(ImageColor.getrgb(t))
for t in txt_ground:
bg_colors.append(ImageColor.getrgb(t)) return bg_colors, fg_colors
这很简单,但值得一提的是,我采样的颜色比上面代码中的多(你可以在 Git 中找到我采样的所有颜色),但为了清晰起见,我减少了采样
下一步是编写一个函数,将颜色随机放置在图层上
def plot_random_color_blobs(draw, colors, count, mins, maxs):
for i in range(count):
x = random.randint(0,width)
y = random.randint(0,height)
w = random.randint(mins,maxs)
l = random.randint(mins,maxs)
c = bg_colors[random.randint(0,len(colors)-1)]
draw.ellipse((x, y, x+w, y+l), fill=c, outline=None)
这个函数接收一个 ImageDraw。从 PIL 绘制对象,并在随机点添加椭圆计数
假设我们使用了红色、绿色和蓝色,并且使用了大量的计数(在本例中为 1500 ),那么图层的结果可能看起来像这样
所以现在是时候构建背景层了
def create_bg(colors, width, height):
im_bg = Image.new('RGBA', (width, height),
ImageColor.getrgb('#7FCBFDFF'))
draw_bg = ImageDraw.Draw(im_bg)
plot_random_blobs(draw_bg, colors, 1500, 10, 25)
im_bg = im_bg.filter(ImageFilter.MedianFilter(size=9))
return im_bg
正如你所看到的,图像是用浅蓝色背景创建的,以消除随机椭圆没有瞄准的任何区域。
绘制完斑点后,我使用模糊滤镜来模糊图像,结果是这样的
这开始看起来好像我是在正确的方向。
但我担心网络会学会如何区分图像的模糊部分和非模糊部分,在我们的例子中是水果,所以我调低了它,并转移到一个 MedianFilter,它允许合并颜色,但仍保留背景的整体清晰度。
接下来,我创建了前景层——这一层将被放置在水果层,以掩盖一些水果
def create_fg(colors, width, height):
im_fg = Image.new('RGBA', (width, height), (0, 0, 0, 0))
draw_fg = ImageDraw.Draw(im_fg)
plot_random_el(draw_fg, colors, 40, 10, 25)
im_fg = im_fg.filter(ImageFilter.MedianFilter(size=9))
return im_fg
正如你所看到的,这个函数几乎是相同的,除了我将背景设置为透明,并使用了更少的斑点(40 个)来确保大部分水果都能被看到
最后,是时候创建水果层了
def plot_random_fruit(color_range, count, width, height, mins,
maxs):
im_fruit = Image.new('RGBA', (width, height), (0, 0, 0, 0))
draw_fruit = ImageDraw.Draw(im_fruit) fruit_info = []
for i in range(count):
x = random.randint(0,width-10)
y = random.randint(0,height-10)
w = random.randint(mins,maxs)
c = (random.randint(color_range[0][0],color_range[0][1]),
random.randint(color_range[1][0], color_range[1][1]),
random.randint(color_range[2][0], color_range[2][1]))
fruit_info.append([x, y, w, w, c])
draw_fruit.ellipse((x, y, x+w, y+w), fill=c, outline=None)
return im_fruit, fruit_info
与其他图层相似,这一层在图像周围随机地点绘制水果。然而,这一层有四点不同。
- 绘图总是一个圆形,因为这是水果更常见的形状。
- 它使用一系列颜色(在我的例子中是所有的橙色变体)来随机选择一种颜色。
- 没有给图像分配滤镜。
- 该图像将水果的边界框及其颜色存储在一个数组中,该数组作为水果信息返回。
def create_layered_image(im_bg, im_fruit, im_fg):
img = im_bg.copy()
img.paste(im_fruit, (0, 0), im_fruit)
img.paste(im_fg, (0, 0), im_fg)
return img
最后一个函数只是将图像一个接一个地粘贴起来
结果会像这样
显然,这看起来不像一棵果树,但它包括网络可能需要看到的正确颜色和情况,以便能够正确训练。
下一步是创建一个注释文件,我决定使用 PascalObject,因为我对它更熟悉,但是其他任何注释都可以。
def create_annotation(img, fruit_info, obj_name,
img_name ,ann_name):
pobjs = []
for i in range(len(fruit_info)):
pct = 0
circle = fruit_info[i]
color = circle[4]
for i in range(circle[2]):
if (circle[0]+i >= width):
continue;
for j in range(circle[3]):
if (circle[1]+j >= height):
continue;
r = img.getpixel((circle[0]+i, circle[1]+j))
if (r[0] == color[0]):
pct = pct +1
diffculty = pct/(circle[2]*circle[3]) if (diffculty > 0.1):
dif = True
if (diffculty > 0.4):
dif = False
pobjs.append(
PascalObject(obj_name, "", truncated=False,
difficult=dif,
bndbox=BndBox(circle[0], circle[1],
circle[0]+circle[2],
circle[1]+circle[3])))
pascal_ann = PascalVOC(img_name,
size=size_block(width, height, 3),
objects=pobjs)
pascal_ann.save(ann_name)
在将边界框添加到注释之前,该函数检查有多少水果没有被前景遮挡。这使得网络可以决定在计算这个水果时的错误是否严重。
然而,PascalObject 中的难度参数是一个布尔值,所以我选择以下 3 个截止点。如果看到的水果少于 10%,我就简单地忽略该信息,任何被遮挡超过 40%的水果都被认为是困难的。
把所有这些放在一起,我现在准备开始生成图像
def create_training_image(counter, bg_colors, fg_colors,
fruit_color_range):
fruit_count = random.randint(0, 20)
ext = '{}_{}'.format(counter, fruit_count)
img_name = '{}/fruit_{}.png'.format(img_path, ext)
ann_name = '{}/ann_{}.xml'.format(ann_path, ext) im_bg = create_bg(bg_colors, width, height)
im_fg = create_fg(fg_colors, width, height)
im_fruit, fruit_info = plot_random_fruit(fruit_color_range,
fruit_count, width,
height, 10, 25)
img = create_layered_image(im_bg, im_fruit, im_fg) #create the anootation File
create_annotation(img, fruit_info, 'oranges',
img_name, ann_name)
img.save(img_name)
return img, img_name, ann_name
这个函数现在应该是不言自明的,并且创建一个单独的图像及其注释文件。
现在为了制作更多的图像,我补充道
def create_training_set(num, start_at=0):
bg_colors, fg_colors = prepare_colors()
fruit_color_range = [[180,230],[50,130],[0,5]]
for i in range(num):
create_training_image(num+start_at, bg_colors,
fg_colors, fruit_color_range)
结果是
为测试而创建的批次中的 3 个样品
创建数据集
一旦我准备好了图像和注释,我就按照 Solawetz 教程进行操作,并使用 Roboflow 将其转换为 YOLOv5 的可读数据集——由于 Roboflow 免费使用的最大图像量是 1000 张图像,我确保不要创建太多图像,未来我将尝试通过简单地在代码中创建数据集来克服这一点,但现在应该可以了。
按照 Roboflow 设置中简单的 5 个步骤,我能够在几分钟内构建数据集。
我选择在这一点上不创建增强,但最终将它们用于缩放目的,以允许网络检测更大的对象。
训练网络
根据 Solawetz 的教程设置环境后,培训减少到一行代码
%%time
%cd /content/yolov5/!python train.py --img 416 --batch 16 --epochs 100 --data '../data.yaml' --cfg ./models/custom_yolov5s.yaml --weights '' --name yolov5s_results --cache
为了训练你自己的模型,我建议看看罗博弗洛的笔记本。
我最初写了我自己的笔记本,可以在 Git 资源库中找到,但在查看后,我发现上面的笔记本只是写得更好,所以向他们致敬:)。
由于我更习惯于使用 Tensorflow,而不太熟悉 PyTorch,并且由于培训的目的是测试合成数据,所以我选择不更改培训代码,也不尝试调整它。将来,我也打算探索这个问题。
仅仅过了几分钟(在使用 Collab 的 GPU 上运行)和仅仅运行 100 个 epochs,我就获得了精度接近 91%的训练结果。事实上,该网络能够在不到 40 个纪元的时间内收敛到这样的精度。惊艳。
但是当然,这都是基于合成数据。
是检验真实数据的时候了。
一如既往,图片胜过 1000 字。
对真实图像的推理结果
警告
并不是所有的都很完美。
具有占据屏幕大部分的大橙子的图像效果不好,我猜这是因为我的训练集中的所有橙子都相对较小。我使用带有 zoom 增强功能的 Roboflows 增强功能重新训练了数据集,并获得了更好的结果。但在进一步的测试中,我计划创建包括更大范围的斑点和橙色尺寸的图像。
为背景选择的颜色可能是至关重要的——在我的第一次测试中,我没有添加任何可能出现在图像中的通用颜色,如肤色,在某些情况下,它选择了橙色。但是在背景色中加入一些肤色之后,这个问题似乎就消失了。
结论
总之,使用合成数据被证明是有用和容易的。
我可以很容易地在视频上运行它,因为 Yolov5s 非常快,它实际上可以实时运行。
如果你想下载完整的代码和数据集,你可以查看我的 Git
我现在进入下一步,将通过许多不同的框架跟踪这些橙子,以允许在不同的视图中进行更强的验证,并最终进行作物估计。
诚挚的问候
大阿米佐拉赫