端到端时间序列分析和预测:萨里马克斯、LSTM 和预言家三重奏(上)
预测赫尔辛基市选定地点的日总能耗
塔皮奥·哈贾在 Unsplash 上拍摄的照片
第二部分:端到端时间序列分析与预测:萨里马克斯、LSTM 和先知三人组(第二部分)|作者:孙乐| 2021 年 12 月|中
简介
时间序列,或按时间顺序排列的一系列数据点,是一种普遍存在的数据类型。经济学家通过观察经济在过去的表现来分析经济;天气预报部分基于历史数据等等。总的来说,任何涉及时间维度的定量问题都不可避免地涉及到处理时间序列数据。因此,时间序列分析和预测一直是一个活跃的研究领域,对学术界和商界都有切实的回报。
在本文中,我们将带您了解目前用于预测时间序列的 3 种最流行的技术/工具:经典的 SARIMAX 模型、LSTM 神经网络和 Prophet。所有的内容将基于我们上面提到的项目。其范围是分析和预测赫尔辛基市选定地点的总能耗水平。具体来说,我们将按照以下顺序浏览这篇文章:
- 数据
- 探索性数据分析
- 基线模型
- 萨里马克斯
- LSTM
- 先知
对于更多“铁杆”观众来说,你可以在项目网站上的我们的笔记本中直接看到代码(不幸的是,有注释:)。
数据
我们从 www.avoindata.fi 获取数据集,这是一个由芬兰数字和人口数据服务机构维护的开放式数据存储库。你可以在能源类别中找到,标题为‘赫尔辛基市公用事业和服务设施的能源消耗数据’在数据集的页面上,有一个链接将你带到 API 文档。
对于我们的项目,我们使用了 2017 年至 2020 年(不含)三年的每日和每小时电力消耗数据。这是因为我们发现这只是用于建模的适量数据——不需要旧数据,并且由于疫情,2020 年本身在能源消耗方面已经是一个显著的异常值——不需要新数据。获取的原始数据集大致如下:
马上,我们可以看到有很多冗余信息,甚至包括大楼地址。要获得总能耗,需要对沿locationName
特征的值求和。然而,这一过程因位置缺失值的常见程度而变得复杂,这意味着需要进行插值。然而,经过一些工作后,我们得到了想要的数据形式(当然,你可以在笔记本中看到我们所做的详细工作):
数据准备到此为止。现在我们进入可视化的有趣部分!
探索性数据分析
整个数据集的图
通过对数据的初步了解,我们已经可以识别出显著的年度(季节性)电力需求模式,在冬春季达到峰值,然后在夏季降至最低点。我们的假设可以通过查看每年的数据来进一步验证。
数据正如我们预期的那样,在 3 年中几乎呈现出相同的模式。也就是说,我们可以在 2018 年 9 月看到一些异常情况,能源需求意外大幅下降。对此进行了进一步的调查,但没有确定的结果。
更进一步,我们可以放大每年的数据,分析月、周、日的模式。
以 2017 年前两个月的数据为例。这里的重复模式非常引人注目。通过将它们与各自的日期进行比较,很明显数据具有 7 天的季节性,换句话说,是每周一次。从该图中,我们可以看到能源消耗在工作日达到高峰,然后在周末显著下降。这非常符合我们的预期,因为消耗大量能源的工厂和办公室都遵循这样的时间表。
时间序列分解
现在我们对数据有了更好的感觉,我们将通过使用一种叫做 时间序列分解 *的统计技术来获得更多的技术。*从概念上讲,它的工作原理是将时间序列分成 3 个部分:
- 趋势周期——长期或不固定频率的数据增加或减少。随着时间的推移,随着人口和商业的增加,能源需求也会增加。
- 季节性-当时间序列受季节性因素影响时会出现模式。例:我们刚刚在数据中看到的每周模式。
- 残差——去除上述两种成分后的余数。由于每个时间序列本质上都是一个随机过程,因此数据中总会存在随机噪声。一栋建筑可能有几个坏了的灯泡,或者一些员工生病了,能量水平因此波动。
特别是,我们使用了在包[statsmodels](https://www.statsmodels.org/stable/index.html)
内的类[STL](https://www.statsmodels.org/devel/generated/statsmodels.tsa.seasonal.STL.html)
中实现的黄土分解。为清楚起见,我们仅对一年的数据进行分解:
stl **=** STL(ts2017,seasonal**=**7,robust**=True**)
res **=** stl**.**fit()
fig **=** res**.**plot()
可见,剧情只是进一步印证了我们对数据的既有信念。分解完成后,探索阶段就结束了。现在,是建模时间了!
基线模型
在每个建模过程中,都需要一个基线模型,其结果可以用来评估我们的主要模型。在我们的案例中,我们选择使用线性回归模型,因为它简单高效。
关于建模过程本身,我们的测试集在 2019 年 12 月合成了所有数据。你可能已经猜到了,由于假期的原因,预测这个时间段是非常具有挑战性的。别担心,这正是我们的意图!我们想看看这些模型是如何处理非常时期的。而且,一点点挑战只会让事情变得更有趣,不是吗?
事不宜迟,我们用几行代码通过线性回归得到预测:
现在我们知道,我们的模型需要比大约 90,000 千瓦时的误差做得更好!
萨里马克斯
要使用这种技术,我们首先需要了解基本知识。
什么是 SARIMAX?
带外生因素的季节性自回归综合移动平均线,或 SARIMAX ,是 ARIMA 类模型的扩展。直观地说,ARIMA 模型由两部分组成:自回归项(AR)和移动平均项(MA)。前者将某一时间的值视为过去值的加权和。后者也将相同的值建模为加权和,但是是过去的残差(confer。时间序列分解。还有一个综合项(I)来区分时间序列(我们将在下面进一步讨论)。由于这是一个涉及大量数学知识的丰富话题,我们强烈建议你做进一步阅读以获得更好的理解。
总的来说,ARIMA 是一个非常正派的模特。然而,这种香草版本的问题是它不能处理季节性——这是一个很大的弱点。萨里玛来了——萨里玛的前身。SARIMA 模型的一个简写符号是:
其中 p =非季节性自回归(AR)订单, d =非季节性差异, q =非季节性移动平均(MA)订单, P =季节性 AR 订单, D =季节性差异, Q =季节性 MA 订单, S =重复季节性模式的长度。从现在起,我们将使用这种符号。通过添加季节性 AR 和季节性 MA 组件,SARIMA 解决了季节性问题。
SARIMAX 只是通过增加处理外部变量的能力来扩展这个框架。Holidays 是首选,但是如果需要的话,您也可以获得您自己的特定于领域的特性。在我们的例子中,我们从包[holidays](https://pypi.org/project/holidays/)
中获取了芬兰的假期列表:
我们在项目中使用的 SARIMAX 实现也来自于包statsmodels
。
落后
滞后只是一个系列中时间步长的延迟。考虑一个时间索引 *t,*相对于 t 的滞后 1 时间索引简单来说就是 *t-1,*滞后 2 就是 *t-2,*等等。
平稳性
平稳时间序列的均值、方差和自相关结构不随时间变化。换句话说,它没有任何周期/趋势或季节性。ARMA 模型系列实际上就是建立在这个概念上的。
来源:维基百科
自相关函数(ACF)和偏自相关函数(PACF)
这两个函数都测量时间 t 的数据与其过去的值 t-1,t-2,… 的相关程度,但是有一个关键的不同。ACF 还测量了相关滞后之前的间接相关性,而 PCAF 没有。在实践中,他们的图对许多任务至关重要,尤其是为 SARIMAX 模型选择参数。你可以在这里阅读更多关于如何解读这样的情节。
有了基础知识之后,我们继续使用 Box-Jenkins 程序对时间序列进行建模。
模型识别
差分以实现平稳性
我们首先确保我们的数据是稳定的。查看我们最初制作的图表,很明显,数据并不是稳定的,没有如此明显的趋势和季节性。然而,我们可以通过使用一个统计测试使我们的猜测更加科学:增强的 Dickey-Fuller 测试,也在statsmodels
包中实现。
**def** test_stationarity(timeseries,maxlag):
*# Perform Dickey-Fuller test:*
print('Results of Dickey-Fuller Test:')
dftest **=** adfuller(timeseries,maxlag**=maxlag,**
autolag**=**'AIC')
dfoutput **=** pd**.**Series(dftest[0:4], index**=**['Test Statistic','p-value','#Lags Used','Number of Observations Used'])
**for** key,value **in** dftest[4]**.**items():
dfoutput['Critical Value (%s)'**%key**] = value
print (round(dfoutput,3))
正如所料,p 值大于 0.05。因此,我们不能拒绝零假设,时间序列不是平稳的。现在的问题是,“我们如何做到这一点?”程序中的答案是差分——由积分项的 d 和 D 阶表示。对数据求差仅仅意味着取数据点与其滞后版本之间的差。直观上,这类似于对函数求导。
这看起来更像!我们再次求助于迪基-富勒试验进行验证。
完美!现在我们知道了 SARIMAX 中的综合术语的参数:季节性和非季节性术语的 1。建模过程的下一步是使用 ACF 和 PACF 图找到 AR 和 MA 项的顺序。
使用 ACF 和 PACF 图确定 p 和 q
在图中,我们可以发现错综复杂的自相关模式,没有明确的解释。因此,季节性和非季节性 AR 和 MA 条款的订单无法果断选择,使我们的流程变得复杂。这证明了现实生活中的统计数据可能比教科书上的例子要混乱得多,我们不得不面对自己选择的不确定性。幸运的是,我们可以通过使用一种叫做网格搜索的优化方法来避免任何不科学的猜测。
模型估计
网格搜索
该算法简单地对所有参数组合进行穷举搜索。将根据我们选择的损失函数来选择它们中最好的一个。在我们的案例中,我们按照 ARMA 建模过程中的标准使用流行的赤池信息标准(AIC) 。
**def** sarimax(ts,exo,all_param):
results **=** []
**for** param **in** all_param:
**try**:
mod **=** SARIMAX(ts,
exog **=** exo,
order**=**param[0],
seasonal_order**=**param[1])
res **=** mod**.**fit()
results**.**append((res,res**.**aic,param))
print('Tried out SARIMAX{}x{} - AIC:{}'**.**format(param[0], param[1], round(res**.**aic,2)))
**except** Exception **as** e:
print(e)
**continue**
**return** results*# set parameter range*
p,d,q **=** range(0,3),[1],range(0,3)
P,D,Q,s **=** range(0,3),[1],range(0,3),[7]
*# list of all parameter combos*
pdq **=** list(itertools**.**product(p, d, q))
seasonal_pdq **=** list(itertools**.**product(P, D, Q, s))
all_param **=** list(itertools**.**product(pdq,seasonal_pdq))
all_res **=** sarimax(train,exo_train,all_param)
搜索结束后,我们列出了前 5 名模特:
我们可以马上使用第一组参数。即使这个模型违反了简约原则(参数总和< 6),裕量足够小,我们可以从一些灵活性中获益。
模型验证
残留诊断
为了确定模型的拟合优度,我们可以使用标准假设来检查其残差:它们应该正态分布在 0 附近,或者换句话说,白噪声。
我们可以通过查看显示残差分布的各种图来验证这一点。这可以使用plot_diagnostics
方法方便地生成。此外,还可以使用 Ljung-Box 测试进行更精确的测试。
res**.**plot_diagnostics(figsize**=**(15, 12))
plt**.**show()
print("Ljung-box p-values:\n" **+** str(res**.**test_serial_correlation(method**=**'ljungbox')[0][1]))
res**.**summary()
在图中,残差似乎正态分布在 0 附近,这是我们需要的条件,尾部略重。然而,看一下 Ljung 盒统计,我们不能拒绝数据不是独立分布的假设,因为从 6 开始,对于某些滞后,p 值小于α=0.05。
尽管如此,让我们使用这个模型在测试集上进行预测,并自己进行判断。
fig, ax **=** plt**.**subplots(figsize**=**(12,7))
ax**.**set(title**=**'Energy consumption', ylabel**=**'kWh')ts**.**plot(ax**=**ax, style **=** 'o')
pred**.**predicted_mean**.**plot(ax**=**ax, style**=**'o')
ci **=** pred_ci**.**loc[demo_start:]
ax**.**fill_between(ci**.**index, ci**.**iloc[:,0], ci**.**iloc[:,1], color**=**'r', alpha**=**0.1)
总的来说,样本内预测似乎非常符合时间序列!没有明显的误差模式,除了该模型似乎比周末更好地预测了工作日的能量水平。现在,让我们看看它如何在测试集上预测:
样本外预测看起来也很令人满意!在该月的前 2 周,预测值与实际值吻合得很好,预测中没有系统误差。然而,可能有一个例外,12 月 6 日的数值是非常不正确的。幸运的是,我们知道原因很简单:那天是芬兰的独立日。
关于寒假季节,模型不幸地没有做得那么好。在此期间,尽管增加了外生的“假期”变量,但预测始终高估了用电量。显然,不仅圣诞假期能量下降,整个寒假期间也是如此。并且,该信息没有被合并到模型中。这解释了为什么错误率仍然很高——与基线模型相比没有太大的改进。
我们用一个不同的训练测试分割在幕后重做了这个过程,结果比预期的好得多。误差率下降到 30,000 千瓦时左右或低于 5%。对于一类 50 年前理论化的模型来说还不错,对吧?然而,所有这些都显示了在非常时期进行预测的挑战,强调了对更好技术的需求。除此之外,当数据表现可预测时,该模型仍然显示出预测的前景。
哎呀…这个帖子已经太长了!此外,这似乎是一个很好的地方停下来,所有的 SARIMAX 部署都包括在内。在下一篇文章中,我们将会谈到时间序列分析教科书中的“现代”部分。我们会发现用 Prophet 拟合时间序列的过程。也就是说,非常感谢你穿过文本墙走了这么远:)。下次见!
第二部分:端到端时间序列分析与预测:萨里马克斯、LSTM 和先知三人组(第二部分)|孙乐| 2021 年 12 月|中
同样,对于渴望更多未来预测的感兴趣的观众,你可以在项目网站上的我们的笔记本中看到所有的代码以及更多的技术评论。
数据科学工程基础
小窍门
你的代码周围的代码
“数据科学分析基础”帖子中的编程概念涵盖了当数据摆在你面前时如何处理数据。如果您的工作流看起来像是从 Google Drive 下载一个 CSV 文件到您的笔记本电脑上,分析数据,然后将 PDF 文件附加到报告中,那么这些概念就足够了。
然而,当您开始一个需要组合来自数百个 CSV 的数据的项目时,会发生什么呢?点击和拖动只能让你到此为止——即使你有耐心,你的经理可能没有!在你不得不通过一个没有好的用户界面的 API 来访问数据之前,这也只是一个时间问题。
类似地,也许你被分配到一个项目,该项目有一个现有的代码库,程序员希望在处理代码时有最佳实践。虽然一次性的脚本可能在学校期间就已经停止了,但是[1] 如果你不能以一种易于阅读、重用和被他人修改的方式来组织你的代码,你就活在借来的时间里。
这就是分析数据之外的编程技能的用武之地。在本帖中,我们将涵盖以下软件工程概念:
- 访问数据
- 版本控制
- 面向对象编程
- 虚拟环境
- 写作测试
访问数据
在这一节,我们将介绍 如何使用代码来访问数据。 这是一项跨越整个分析-工程领域的技能,但我认为与其说是一项分析技能,不如说是一项“工程”技能。
作为一名数据科学家,你很少会通过 Google Drive 或 Dropbox 的点击式图形用户界面来访问数据。相反,您将访问的大部分数据将驻留在 SQL (结构化查询语言)数据库或API(应用程序编程接口)之后。也有可能你需要使用网络抓取来访问不提供 API 的网站上的数据。本节将介绍这三种访问数据的方式。
结构化查询语言
除非你的公司很小,否则它的数据会比一两个硬盘所能容纳的还要多。随着数据量的增长,以最小化冗余和检索时间的方式组织数据至关重要;优化安全性和可靠性;正式陈述数据的不同部分如何相互关联;并让多个用户同时读取(和写入)数据。
实现这一点的主要方法是使用关系数据库,您可以使用 SQL 对其进行查询。[2]关系数据库本质上是一组表,表之间有定义的关系。
例如,如果你拥有一家网上商店,你不需要在顾客订购的每件商品旁边保存顾客的每一个细节;您可以将客户信息分离到一个表中(customers
),将订单信息分离到另一个表中(orders
),只需用orders
中的一个名为customer_id
的列将订单与客户相关联。使用 SQL,您可以轻松快速地从两个表中提取相关数据,即使表增长到数千万或数亿行。[3]
在您的角色中,您可能会非常频繁地使用 SQL,可能每天都在使用,所以我强烈建议您花时间来完善这项技能。幸运的是,SQL 不是一种庞大的语言,你可能只需要从数据库中查询数据,而不是创建数据库或表格,这更属于数据工程师的领域。在这篇文章中,我们将关注简单到中级的查询。
下面是一个用主流 SQL 语言之一的 Postgres 编写的简单查询。我们从表students
中选择name
和animal
列,使用AS
关键字为返回的表中的列创建别名或临时名称。最终结果经过过滤,因此返回的行中只有学生最喜欢的动物是海象的行。[4]
我们也可以为表格使用别名,下面我们将为users
、sql_pros
和transactions
使用别名。在这个例子中,我们以两种方式连接表;在第一个查询中,我们使用了一个LEFT JOIN
,它保留了users
中的所有行,但是删除了sql_pros
中没有 ID 的行。在第二个查询中,我们执行了一个FULL JOIN
,它保留了users
和transactions
中的所有行。
对于各种连接,要记住的主要事情是连接后要保留的行:只保留在两个表中匹配的行(INNER
)、全部在左(LEFT
)或右(RIGHT
)或全部在两个表中(FULL
)。
聚合数据是另一个关键的 SQL 技能。下面,我们创建一个包含学生姓名和平均成绩的表格。因为在这个例子中,name
列与grades
在不同的表中,所以我们在聚合后连接这些表。
对于更复杂的查询,我喜欢引入WITH {tab} AS
结构,它允许您编写基于其他查询输出的查询。在下面的查询中,我们执行以下步骤:
- 为每个用户创建一个带有平均值和标准偏差
price
的查找表 - 加入我们对原始
orders
表的查找 - 使用我们的查找来过滤掉不在每个用户的平均订单价格的三个标准差范围内的任何行
这个查询方便地返回离群值,我们可以更仔细地检查。注意,这是一个查询,但是由于使用了WITH
语法,我们可以在逻辑上将它视为两个查询。
最后,我们快速提一下写到一个数据库。写入数据库,尤其是生产环境中的数据库,很可能会受到软件工程团队的严格监督——一个好的团队会有适当的程序来验证写入的数据与表模式、防止 SQL 注入攻击,以及确保所有写入都被记录。但是如果您对您想要写入的数据库拥有完全控制权,下面是添加行的基本语法:
这是更新和删除的语法。非常确定你知道你在做什么,因为没有“撤销”命令![5]
与 API 交互
除了 SQL,你访问数据的另一个主要方式是通过 API 或者应用编程接口。[6]
API 就像银行的入口:这是(希望)访问银行内容的唯一方式,你必须遵循一定的规则才能进入:你不能携带武器,你必须步行进入,如果你没有穿衬衫,你会被拒之门外,等等。另一种方式认为它就像一个电源插座——除非你的和弦插头形状正确,否则你无法通电。
requests
库让我们直接从 Python 中查询 API。对于没有任何安全需求的 API 来说,这个过程很简单:你只需要 API 在互联网上的位置,也就是它们的 URL ,或者通用资源定位器。我们所做的就是向 URL 提出一个 HTTP GET
请求,然后解码从服务 API 的服务器返回的 JSON 。
但是有时我们需要一些额外的步骤来访问数据。在访问公司的专有数据时,有(或者应该有!)严格限制谁被授权与数据交互。在下面的例子中,我们使用boto3
来访问亚马逊网络服务 S3 、云存储市场领导者中的一个文件。注意,当我们与 S3 建立连接时,我们需要传递安全凭证(存储在os.environ
对象中)。
网页抓取
如果你想从一个没有提供方便的 API 的外部网站上收集数据怎么办?为此,我们求助于网页抓取。网页抓取的基本前提是编写代码,遍历网页的 HTML ,找到指定的标签(例如标题、表格、图片)并记录它们的信息。抓取对于自动化来说是理想的,因为 HTML 具有高度规则的、基于树的结构,所有元素都有清晰的标识符。
虽然抓取听起来很复杂,但实际上相当简单。我们首先通过用requests.get
从一个网站请求HTML 来模仿一个网络浏览器(例如 Firefox,Chrome)。(然后,我们的浏览器实际上呈现内容,但我们将坚持 HTML 作为一个很长的字符串。)
然后,我们使用 Python 的 Beautiful Soup 库将 HTML 组织成一个大型嵌套字典。然后,通过指定我们感兴趣的 HTML 标签,我们可以从这个对象中提取我们想要的信息。下面,我们打印出维基百科网页抓取页面的所有标题。
版本控制
当你做一个不能在几分钟内完成的项目时,你需要检查点来保存你的进度。即使项目已经完成,也许你会对一个额外的特性有一个很好的想法,或者你会找到一种方法来改进一段代码。
除非变化很小,你不会想要修改工作的版本,而是想要一个副本 ,在那里你可以进行修改并与原始版本进行比较。类似地,对于更大的项目来说,如果新的变更破坏了代码,能够回滚到旧的检查点是至关重要的。如果有多人在处理同一个文件,这一点尤其重要!
这就是像 Git 这样的版本控制系统发挥作用的地方。相比之下,有几十个文件称为my_project.py
、my_project_final.py
、my_project_final_REAL.py
等。相反,你有一个树状的项目时间表,漂浮在你电脑的一个文件夹里。代码有一个“主”分支,你只能修改副本。
每当您更新一个分支时,所有的变更都会被自动标记,并且对主分支的变更需要至少一个其他人的审核。(从技术上来说,他们不会,但实际上在任何职业环境中都是如此。)
随着时间的推移,您的项目结构可能看起来像这样。
来源:堆栈溢出
灰色的线是master
分支(现在称为main
【7】),蓝色和黄色的线是在不同点分支的副本(develop
和myfeature
),经过修改,然后合并回master
。在更大的公司,你可以有几十个分支并行运行,这对于让开发团队同时在同一个代码库上工作是必不可少的。然而,客户看到的唯一分支是main
。
使用 Git 的实际代码很简单。下面是 Mac 终端中 bash 中的一些命令,在这里我们:
git checkout main
git checkout -b DS-123-Add-outlier-check
git push --set-upstream origin DS-123-Add-outlier-check
现在,在我们的新分支上,我们可以自由地对代码进行我们想要的任何更改。假设我们通过添加一个移除异常值的步骤来修改preprocessor.py
。当我们想要保存我们的更改时,我们在终端中键入以下内容。
git add preprocessor.py
git commit -m "Add outlier check"
git push
这些步骤只体现在DS-123-Add-outlier-check
上,不体现在main
上。这让我们准备代码,直到它准备好被推送到main
。
如果我们破坏了一些东西,想恢复到以前的状态呢?我们使用提交的散列检查提交,告诉 Git 忽略有错误的提交,然后将我们的更改推送到分支。
git checkout abc123 # go to old checkpoint
git revert bad456 # "delete" the bad checkpoint
git push # update the branch
Neil 和 Zulma Scott 在 Unsplash 上的照片
面向对象编程
随着项目中代码数量的增长,它通常遵循这种增长组织的模式:
生产级 Python 最适合组织的第四级,在这一级可以轻松地跨上下文添加、修改和重用代码。一个团队的代码通常会根据公司产品(例如“数据质量警报”、“价格预测”、“客户流失预测”)组织成模块,这些模块又包含类,这些类包含协同工作的功能集合。下面是一个名为Student
的类的简单例子。
类别可以存储在具有相同名称的.py
文件中,分组到具有相似类别的文件夹中。模块是包含所有文件夹的目录。我们可以有一个data_processing
模块,例如,具有这样的目录结构[9]:
data_processing
| init.py
| cleaners
| | init.py
| | data_cleaner.py
| visualizers
| | init.py
| | error_logger.py
| | dashboarder.py
| services
| | init.py
| | data_loader.py
| | database_writer.py
在cleaners
子目录中,data_cleaner.py
包含了一个DataCleaner
类,其中包含了清理数据的方法。data_cleaner.py
的前 60 行可能如下所示:
这个代码块比其他代码块长很多,它甚至不包括帮助函数_find_outliers
或调用DataLoader
的代码。对于生产级编码,你需要围绕你的核心功能构建更多的架构来确保你的代码:
- 可以被其他人阅读和修改,而不仅仅是你
- 是否足够模块化以用于管道和多种环境
- 如果它收到一些意外的输入,不会使这些管道停止工作
上面的代码有一个详细的文档字符串、类型提示、参数默认值设置为文件顶部的全局变量,以及一个针对意外行为的警告日志。
这些附加组件有助于解释我们的代码对其他开发人员以及我们自己有什么作用!作为进一步的预防措施,我们可以加入错误处理来处理错误数据类型的参数,否则会导致脚本失败。
瑞安·赫顿在 Unsplash 上拍摄的照片
虚拟环境
除非我们的代码非常简单,否则我们需要导入外部库(比如pandas
和numpy
)。正如比尔·索鲁尔所说的,这些外部依赖“是魔鬼”代码是不断进化的,有时你一年前写的脚本在使用它的依赖项的最新版本时不再有效。 Python 3 与 Python 2 的向后不兼容是出了名的,比如pandas
v1.0 弃用或移除了几个Series
和DataFrame
操作。
防止改变依赖关系的一种方法是拍摄项目外部库的“快照”以及它们的确切版本号。然后我们可以创建一个虚拟环境,让我们重现当你创建你的项目并且一切正常运行时“外部代码世界”的样子。
在终端中,我们可以使用 Python 内置的virtualenv
模块来创建虚拟环境。在这里,我们创建一个名为venv
的。
python -m virtualenv venv
然后,我们可以通过键入以下代码进入这个虚拟环境:
source venv/bin/activate
我们的新环境没有我们的全球环境所拥有的外部库。例如,即使您在创建虚拟环境之前安装了scikit-learn
,在venv
中也不存在scikit-learn
。每当我们创造一个新的环境时,我们都是从一张白纸开始!
因此,我们需要在venv
内部安装我们项目所需的每个库。如果需要,我们可以使用<package>==<version>
语法指定版本号。
pip install pymongo
pip install scikit-learn==0.24
一旦我们的虚拟环境下载了所有软件包,并且您验证了您的应用程序的工作情况,我们就可以使用以下命令将所有软件包及其版本号保存到文件requirements.txt
中。pip freeze
返回所有下载的库,并且>
操作员将输出导入文件,而不是在屏幕上打印。
pip freeze > requirements.txt
将来,为了确保我们的项目如预期的那样工作,我们可以创建一个新的虚拟环境,并安装所有依赖关系的精确版本pip install -r requirements.txt
。这对于相对小规模的工作来说非常有用,但是如果您正在部署一个包,例如到 PyPI 本身以便其他人下载,您会想要深入更高级的 [setuptools](https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-dependencies)
方法。
写作测试
当我们编写一个函数时,代码应该按照我们希望它做的去做。但是更进一步,我们的代码也应该不做其他任何事情。虽然这看起来很明显,但是这个额外的步骤需要我们的代码中有很多额外的架构。这里有一个简单的例子:
当得到非数字输入时,我们的multiply
函数转向字符串和列表连接。这可能不是我们想要的!
人们很容易认为这些例子无关紧要,但如果multiply
在管道深处,接收前一个函数的输出,而前一个函数接收前一个函数的输出,这种情况很容易发生。例如,我们可以有一个find_anomalies
函数,如果没有异常就返回0
,如果有异常就返回一个值列表。(不要这样做——尝试总是从函数返回相同的数据类型。)如果multiply
接受find_anomalies
的输出,我们将根据find_anomalies
返回的结果得到截然不同的结果,这可能会破坏我们的管道。
捕捉这些问题并学习如何编写更好的代码的方法是编写测试。测试基本上就像它听起来的那样:对于一些输入有一个“正确的答案”输出,你检查你的代码是否产生正确的输出。测试在检查多少代码上有所不同:你可以为整个应用程序编写测试,为组件的子集编写测试,或者为单个功能编写测试。下面我将简要介绍最后一种类型,单元测试。
**一套好的功能单元测试将涵盖三种类型的输入:好的、特殊的和坏的。**一个好的输入就像它听起来的那样:这个函数打算接收的输入,就像我们的multiply
函数的整数或浮点数。一个特殊的输入是一种“边缘情况”,可能是触发内部逻辑产生不同输出的某种情况。最后,一个坏的输入是我们的函数无法处理的——我们的函数应该失败。
让我们重写multiply
[10],然后写一些测试。第 8-12 行,大约是我们函数的 83%,现在完全致力于确保multiply
不会中断。这些代码行真的做到了——我们最终不需要为错误的输入编写测试,因为我们不能向multiply
扔任何东西导致它中断。相信我,我试过了。
然后我们可以使用pytest
库来编写我们的测试。第一步是明确定义我们的输入类型及其预期输出。我喜欢使用嵌套字典,其中顶层是我们的测试用例(例如,int-int
,意味着a
和b
都是整数),内层是我们的参数名及其值的字典。然后我们可以使用 **kwarg 符号将字典解包到我们正在测试的函数中。[11]
实际的测试发生在assert
语句中,如果第一个参数返回True
,这些语句将保持沉默,如果第二个参数返回False
,这些语句将引发一个AssertionError
。如果测试失败,最好在断言字符串中详细说明,因为这将帮助您查明错误。
我建议将您的测试组织在一个目录中,该目录反映了您的存储库的结构,如下所示。
src
| custom_math
| | easy
| | | add.py
| | | subtract.py
| | hard
| | | multiply.py
| | | divide.py
| tests
| | test_easy
| | | test_add.py
| | | test_subtract.py
| | test_hard
| | | test_multiply.py
| | | test_divide.py
然后,您可以导航到您的项目的根文件夹(上面的src
,然后只需键入pytest tests
来运行所有的测试。请注意,函数、类和文件名需要以test
开头或结尾,以便pytest
将它们识别为测试。
一旦你做到了这一点,通过模仿依赖函数和断言异常,让你的测试更上一层楼。
总结想法
正如“数据科学统计要点”的帖子一样,当谈到数据科学的有用软件工程实践时,很难不写一本教科书。当我开始转向数据科学时,我最大的知识缺口是工程。在部署模型时,除了实际的分析之外,还有很多东西。你如何创建一个应用而不是一个一次性的脚本?你如何可持续地编写成千上万行代码,或者与其他人一起开发潜在的数百万行代码?
这篇文章中的概念,从管理依赖性到编写弹性代码,应该有助于填补这一知识空白。如果你渴望更多,看看 Python decorators ,创建自己的 API 端点, Docker 容器化,以及像 Heroku 这样的应用托管服务。如果你对这篇文章感兴趣,可以看看我写的关于数据科学工程的其他文章,告诉我你想知道更多的内容。
最好,
马特
进一步阅读
https://betterprogramming.pub/how-to-efficiently-validate-input-types-in-python-functions-1f662f45c24c 💔-levels-of-technical-abstraction-when-sharing-your-code-50ddc6e73946>
脚注
1.你的代码周围的代码
这一针既是针对我的,也是针对其他人的。在研究生院,我有数百行长的 R 脚本,重新运行它们以不同的方式处理数据总感觉像在做手术。不应该是那样的!回过头来看,如果有一些基本的软件工程和项目管理最佳实践,博士学位会变得容易得多。也许为了下一个博士学位…
2.结构化查询语言
关系数据库的一个流行的替代品是 NoSQL,或者非关系数据库(也称为“不仅仅是 SQL”))与具有严格定义的关系的表相比,像 MongoDB 这样的 NoSQL 数据库在如何存储数据方面允许更大的灵活性。查看我在这篇文章中对 SQL 和 NoSQL 数据库的深入研究。
3.结构化查询语言
在索引正确的数据库中搜索、插入和删除记录的规模预计为 O (日志 n ) 。这意味着对于每一个数量级,遍历数据只需要一个额外的步骤!我觉得这令人难以置信。
4.结构化查询语言
在这篇文章的研究中,我偶然发现了这个(有点争议的) SQL 风格指南。这是一篇有趣的文章,我决定采用这篇文章中 SQL 示例的一些布局技巧。我学到的主要东西是在代码中间有一条空白的“河”, SQL 关键字在左边,代码的其余部分在右边——这使得快速浏览查询正在做什么变得非常容易。但是,如果我找不到一种简单的方法来自动格式化代码,我怀疑我是否会坚持使用它来进行快速分析。
5.结构化查询语言
如果这真的发生在你身上,希望你公司的工程团队定期备份数据库,并且能够回滚到更早的版本。如果他们没有,而你不小心删除了你公司的所有数据,那么 Quora 建议你辞职,找一家工程实践更好的公司!
6.与 API 交互
事实上,API 和 SQL 是携手并进的。当您从 API 请求数据时,您的请求很可能被转换成 SQL 查询,然后在数据库上执行。
7.版本控制
2020 年 10 月, GitHub 将默认分支从master
重命名为main
,删除了对奴隶制不必要的引用。
8.版本控制
Git 分支的最佳命名惯例是通过它们的 JIRA 票据 ID 来引用它们。如果您的公司将 Git 与 JIRA 集成在一起,其他开发人员将会看到该分支是否正在开发中,是否有一个活跃的拉请求,或者是否已经被合并到main
。一个更好的(“最好的”-呃?)做法是在分支名称中包含是否是热修复,支持请求,部分路线图等。
9.面向对象编程
init.py
文件允许从目录外导入类。这使得下一个代码块中的from ..services import DataLoader
能够工作。类似地,如果你已经安装了 [data_processing](https://realpython.com/python-wheels/)
模块,你可以在任何脚本中通过输入:
# With init files
from data_processing import DataLoader
如果没有 init 文件,您需要键入:
# Without init files
from data_processing.services.data_loader import DataLoader
这不仅不太方便,而且如果您决定重新排列data_processing
模块中的目录,还会有外部脚本崩溃的风险。你可以在这里阅读更多关于 init.py 文件的内容。
10.写作测试
如果我们真的想彻底,我们应该创建一个助手函数来检查multiply
的输入。这使得我们的功能集中在尽可能少的任务上,使得编写测试和调试更加容易。这看起来是这样的:
11.写作测试
我发现解包元组和字典让编写测试变得有趣多了。这里是拆包和不拆包的符号。
利用喜力的全球规模,设计当地建议
(图片由喜力公司提供)
喜力公司正在利用数据和分析技术对其 157 年的历史进行创新。推荐系统越来越多地通过过滤信息来支持我们的业务流程。例如,我们的销售团队为酒吧的啤酒组合提供建议的系统。然而,我们的国际规模既是优势也是挑战。我们的目标是利用我们的国际影响力,协调各子公司的推荐基础设施。通过机器学习操作方法,我们在 Azure 平台上创建了标准化的推荐系统。自助服务、自动化和协作的文化使我们能够快速将推荐系统扩展到本地子公司。这项工作被 RecSys 会议接受。
喜力公司在 70 多个国家设有子公司,是最国际化的啤酒酿造商。在这些子公司中,对分析解决方案的需求增加,如过滤信息以预测偏好的推荐系统。这有助于我们的销售团队更好地建议商店(如酒吧)向顾客提供哪些啤酒。在开始的情况下,这些子公司独立开发他们的推荐系统。导致各种技术选择和数据来源的多样性。从全球角度来看,这种缺乏标准化是一个重大的工程挑战,因为它支持复杂,维护昂贵,并且没有利用喜力的规模。在期望的情况下,本地推荐系统在子公司之间被协调。确保没有技术债务的高交付速度。因此,这项工作集中在如何在企业内扩展标准化的推荐系统,同时保留分散的所有权并结合本地规范?
MLOps 方法
我们从使用机器学习操作(MLOps)方法应对这一挑战的经验中汲取了经验。MLOps 由关注效率、可靠性和再现性的系统启动实践组成。它是 ML 和 DevOps(开发运营)的结合。这是一次重要的合并,因为机器学习解决方案是由代码和数据形成的。与只覆盖代码的常规软件工程相比。我们的 MLOps 生命周期有四个阶段,因为我们相信数据管理是成功的推荐系统的基础部分。这个生命周期不是单向或单一的过程。推荐系统通过并行动作进化。
机器学习操作生命周期(图片由作者提供)
MLOps 方法有五种最佳实践:
- **管道自动化:**通过自动化操作和连接管道中的服务来消除手动操作和延迟。例如,通过发布具有持续集成和持续部署管道的解决方案。
- **数据可用性:**通过功能存储访问经验证的数据集,并在数据目录中进行索引,以确保可重复的机器学习。
- **可交换工件:**所有的机器学习模型、代码、配置都是版本控制的、描述性的、PEP8 一致的。带有文档的解决方案模式可用于常见的架构工件。在生产中,代码脚本比笔记本更受欢迎。
- **可观察性:**深入了解系统组件以确保性能并确定问题的根本原因。度量、事件、日志和跟踪都被收集起来,产品团队可以访问,而不仅仅是工程师。
- 基于策略的安全性:发布分为四个环境:开发、测试、验收和生产。对这些环境的授权是通过基于属性的访问控制(ABAC)来管理的,它具有控制平面和数据平面功能。机密存储在特定于环境的密钥库中。
推荐系统示例
根据这种 MLOps 方法,我们创建了一个可在企业内扩展的推荐系统。推荐系统是运行在 Azure 云上的内部解决方案模式的解耦架构。数据管理阶段从上游服务获取原始数据,并将其转换为集中可用的标准化数据集。在建模阶段,使用 Azure ML 工作空间对这些数据进行建模,该工作空间充当推荐引擎。开发阶段创建内部解决方案模式,确保资源之间的可靠性和互操作性。操作阶段使用 CI/CD 管道自动部署资源。这一阶段还为当地利益相关者提供了可访问的可观察性,以发现和检查可能的问题。这种标准化的解决方案减少了每个子公司的工程时间,同时保持了有效整合本地规范和分配所有权的灵活性。
推荐系统(作者图片)
通过资源和解决方案模式标准化跨子公司的推荐系统简化了同事之间的协作。他们可以轻松地交换知识、机器学习模型和其他工件。内部教程是一致的,如果需要,全球团队会提供额外的支持。从而受益于喜力啤酒的全球规模和经验。
结论
满足企业中推荐系统的本地需求需要具有最小技术债务的高效工程。我们解决了在企业内部扩展本地推荐系统的挑战。本文描述了一种 MLOps 方法和相应开发的推荐系统。从这个过程中,我们了解到喜力推荐系统的工程应该促进三个原则:自助服务、自动化和协作。
自助服务、自动化和协作
这些原则授权子公司开发在整个公司内统一的推荐系统。从而利用喜力的规模来实现高交付速度,同时仍然结合当地的规格。我们相信,这些工程原则对其他有类似数据驱动的雄心和挑战的企业也很有价值。此外,我们认为这些并不局限于推荐系统,而是适用于在全球范围内扩展各种类型的机器学习应用
会议文件和视频
这项关于推荐系统的工程工作被接受为第 15 届 ACM 推荐系统会议的行业海报。这是一个主要的国际论坛,由领先的研究人员和从业人员介绍该领域的研究和技术。今年,这是喜力和我自己的主场比赛,在阿姆斯特丹的 Beurs van Berlage 和全球在线举行混合会议。阅读短文(开放访问)中的完整作品或观看闪电谈话。
这篇文章站在(喜力公司)人才的肩膀上。他们的想法、代码和反馈塑造了我们的思想,并为这项工作做出了贡献。
英语专业转脸书数据科学家
办公时间
加速您的数据科学职业生涯的学习资源
信用:Pexels
我已经正式接受了脸书大学数据科学家的职位。大约四年前的今天,我写了第一行代码。同样,我在大学期间从未学过微积分或线性代数。所以我的工作是为我量身定做的…
扎实的基础超级重要,下面是对我帮助最大的资源。
第 1 部分:资源
Python(基础)
从开始,用 Python 将枯燥的东西自动化。这里的重点是自动化枯燥的东西。作为一个(以前的)非编码者,网络抓取有一种魔力。你能为自己做的最好的事情就是尽早迷上 python!
在你已经掌握了 5-10 章的 ATBS 之后,有必要更详细地了解一下 Python 作为一种编程语言的结构。科里·斯查费几乎在所有事情上都有资源——暂时放过弗拉斯克和姜戈吧!你现在需要的是面向对象编程(OOP)、生成器、装饰器等等。在构建我们自己的数据结构之前,我们需要彻底理解 python 的内置数据结构!
数据结构和算法
背靠背 SWE 从伪代码的角度讨论每一个算法,(读作而不是 python!)重点是理解数据结构和算法(DS & A)本身,而不是背代码。熟悉递归、树结构和动态编程是一个好主意——这些想法到处都有,比如强化学习、隐马尔可夫模型等。
为了测试你的理解,试着在 LeetCode 上解决问题。这将帮助你为编码面试做好准备。该网站面向软件工程师,而不是数据科学家;你不需要为“难”的问题而烦恼。但是处理几乎每一个“简单”的问题和适度抽样的“中等”问题将使数据科学家面试的编码部分易于管理。
概率论与统计
大多数人都是从既定的、事实上的方法开始他们的统计之旅的,即频率主义。这就是 p 值、置信区间和方差分析的来源。这些算法在计算上很简单,但分析师所需的假设非常微妙。作为一名概率统计新手,这有助于断章取义地使用给定的程序和/或曲解其结果。频繁主义在工作场所极其普遍,所以你必须在某个时候了解它。但是我相信这对早期**建立直觉很重要。为此,我建议从贝叶斯统计开始。算法在计算上更复杂,然而,结果的解释更简单。这是理想的,因为计算可以自动化,可以用软件抽象,但直觉不能。
为此,先从想贝氏说起。作者从零开始实现所有算法,从简单的直觉开始;这是理想的,因为它会帮助你用概率理论弄脏你的手,这是黄金!你不需要读完整本书,但是对术语、先验、可能性、证据和后验有个感觉是很重要的。
在建立了一些基本的直觉之后,是时候在现实世界的设置中获得一些更强大的实践经验了。《统计学再思考》在我看来是关于概率和(贝叶斯)统计学的最好的书。教科书不是免费的——但是讲座是免费的!代码最初是用 R 和 Stan 编写的。然而,随着我们对 python 的了解越来越多,最好留在 python 生态系统中,因此我链接了教科书代码的 PyMC3 端口。PyMC3 是一种“概率编程语言”——换句话说,你用各种观察到的和/或潜在的变量来指定概率分布的网络,并推断系统作为其参数的函数如何表现。
最后,你需要知道一些常客的统计数据。为此,我推荐stat quest——因为它将尽可能直观地涵盖主题,同时强调所做的假设。
微积分和线性代数
许多统计学是建立在微积分和线性代数的基础上的。例如,由多变量高斯分布包围的区域由其协方差矩阵的行列式来缩放。这是什么意思??和参数通常是由常客确定的!)使用最大似然估计——换句话说,导数。这里高层次直觉的最佳资源是 3Blue1Brown 。如果你想要一些微分和积分的实践经验,我推荐有机化学导师。他没有线性代数的播放列表,所以我联系了有列表的 PatrickJMT 。
数据争论
直接跳到熊猫身上可能很有诱惑力。我们快到了!但是首先,掌握好 SQL 是很重要的。Web Dev 简化版提供了一个很棒的 1 小时 SQL 速成班。重要的是您掌握了基础知识( Select、From、Where、Group by、Having 和 Joins ),然后掌握了相关子查询。这之后,真的是“熟能生巧!”参考 LeetCode(之前介绍过)来测试你的 SQL 精通程度。再次,针对“简单”和“中等”的问题。
在理解了处理数据争论的 SQL 方法之后,您就为熊猫做好了准备。再次,请参考科里斯查费寻求帮助。像 SQL 一样,真正的试金石是处理实践问题。我推荐 StrataScratch ,这是一种数据争论面试问题的 LeetCode。
机器学习
如果你遵循了我关于学习排序的建议,那么你一定已经注意到我把机器学习(ML)留到了最后。太多有抱负的数据科学家直接跳入 ML,而没有正确激励它所需的其他一切的坚实基础。StatQuest(前面介绍过)有关于 ML 的高级直觉的好内容。然而,掌握一个 ML 算法的最好方法是自己实现它。凭借你在 OOP、微积分、线性代数和概率论方面的新知识——信不信由你——你实际上已经准备好了!为此,我强烈推荐send ex。
产品感
产品意识是你处理模糊的产品问题、制定假设、识别结果性指标(也称为关键绩效指标——KPI)、设计实验和解释结果的能力。大多数数据科学家不会接受一个给定的 ML 模型,并将其准确性从 90%提高到 95%的任务。更有可能的情况是,数据科学家会被利益相关者提出一个问题,需要从头开始构建一个分析。为此,我强烈推荐数据面试 Pro 。
并非每份工作都以技术意义上的“产品”为中心,比如 Instagram Stories 或谷歌搜索。有些产品确实是有形的商品。为此,轻松的经济学观点会有所帮助。速成班有一个很棒的关于经济的播放列表。没有必要看微观经济学以外的东西,当然,除非你很好奇。
第 2 部分:一般提示
这里有一些关于你学习努力的一般性建议。
基础
打好坚实的基础是关键;科学家们期望知道的材料数据非常庞大。如果你直接跳到 ML,你会不断地回到基础,这是一个很差的构建搜索空间的方法。最好在基本原理上建立一个适当的基础,这样梯度下降的参考就不会把你带到一个意想不到的微分之旅。
同样,精通 SQL 和熊猫。如果您不清楚基本的 SQL 语法,没有人会对您掌握 PySpark 留下深刻印象…
集成是一个“美好的东西”
差异化是必须的!但是集成很繁琐,也很麻烦。底线是整合是逆向分化。通常,一个易处理的(可解的)解决方案并不存在。积分在概率论中会经常出现(一般是证明,比如从二项式 PMF 推导泊松 PMF。)然而,正如你从思考贝叶斯和统计反思中学到的,我们经常通过模拟来近似积分。最好能正确掌握蒙特卡罗方法(尤其是马尔可夫链蒙特卡罗方法)的工作原理。我概述的资源将带你到这里!
忘记深度学习
深度学习(DL)现在火了!但是一个鲜为人知的秘密是,能够使用 DL 的数据科学家的供应超过了对这类数据科学家的需求。另一个鲜为人知的秘密是——大多数数据科学家无论如何都不会用到 DL o 这个工作。第三个鲜为人知的秘密——领域知识可以弥补技术诀窍的缺乏。如果你知道一个系统是有效的,你对如何在其中构建你的实验和分析有很好的先验信念。如果你知道如何设计真正重要的特性,你就不需要使用高度灵活的 ML 模型。专注于使用更简单的统计和 ML 模型来回答问题,不要回避通过领域知识来缩小搜索空间。
自动化被高估了
每隔 2-3 年,谷歌、脸书和亚马逊都会以计算机视觉(CV)、自然语言处理(NLP)或强化学习(RL)的新进展登上头条。)这三者都有一个共同的主题——它们是没有具体解释的黑箱模型。简单地说——如果模型不主动消耗电力和使用计算资源,它就毫无用处。线性回归会吐出参数;无论您的机器是否被读入内存,您都可以使用这些来做出业务决策。CV、NL 和 RL 则不然。这些模型最适合自动化任务,人类通常可以完成这些任务(将英语翻译成意大利语、面部识别和驾驶汽车。)
会有一些 DS 角色会说,“设计一个算法,它会根据实时需求对产品进行动态定价。“但是这些工作很少。大多数工作会要求你“为所有消费者优化一个产品的固定价格假设你在一家杂货店工作,你已经使用逻辑回归来估计需求曲线微分,以确定一加仑牛奶的最优价格。现在假设你预计今天会有 1000 个顾客,而你有 500 加仑的油,需要卖掉或者扔掉。你应该把价格降低多少,才能卖出每加仑汽油?
没有理由设计一个强化学习模型来回答这个问题!通过逻辑回归(一个斜率和一个截距输入到一个 sigmoid 函数中),你已经有了需求曲线。)假设当前价格对应于 40%的顾客购买牛奶。一千个客户的 40%就是 400 个客户。如果 400 名顾客购买了 400 加仑牛奶,那么你预计你将不得不扔掉 100 单位。然而,如果您简单地在需求曲线上搜索对应于 50%购买机会的价格,并将牛奶的价格设置为这个值,那么您会期望所有 500 个单位都被售出!
自动化功能强大,但在对普通数据科学家工作的适用性方面被高估了。使用领域知识和参数模型被严重低估了——我希望这个例子能说服你!
祝你好运!
喜欢我的内容就订阅:)
英语拼写比你想象的还要糟糕
八万多字的拼写和发音分析
发音网络。稍后查看更多|作者图片
我们都知道单词片段‘ough’可以有很多不同的发音。
- C 到
- D 到
- Pl 到
- 尽管如此
- 穿过到
- +更多(不太常见)的
这是一个经常被引用的英语拼写错误的例子。学习不同地发音同一个字母集需要大量的经验,并且在学习新单词时是令人沮丧的。
另一个例子是拼写土豆的“正确”方法。
虽然这将它推向了极端,但它仍然是这种现象的一个有效例子。它让你思考。
如果我们发出的每个声音都只有一种拼写方式,那会好得多,不是吗?
输入 国际音标 。
这个字母表试图标准化“以书面形式表示语音”,这听起来很棒!最后,不含糊!
问题是,人类能够发出的不同声音数量巨大。
en和 th 中的 th 音有唯一字母真的值得吗?事实上,这些声音之间有一个连续的频谱;一个从哪里开始,另一个从哪里开始?
国际音标肺辅音图通过识别 方式 和 位置 音 |截图来自维基百科
音调符号(每个字母周围的小符号)试图代表甚至更微妙的发音差异。这样做的结果是超过一百个不同的字母来代表声音。
概括起来
英语的字母数量相对于单词发音的数量来说太少了,这迫使字母代表多种发音,使得学习这门语言变得困难。
国际音标有更多的字母来代表单词的发音,导致了令人难以置信的臃肿的书面文本(公平地说,这不是它存在的原因)。
想象这个问题
国际语音词典(ISLE)的一部分|作者截图
*https://github.com/ethanr-2000/bad-spelling-generator
作为开发错误拼写生成器的一部分,我想出了一种将拼写和发音分解成音节的方法——在书面文本和语音之间提供一致的链接。
比如劝有一个 p ə ɹ的发音。s w ei d ,所以我根据和 suade 将单词分解为**。**
这里的数据源是国际语音词典,取自 Python 包 Pysle 。它包含了成千上万的单词和它们的发音。
由于单一的数据来源,这些单词更倾向于美式发音。在数据分析过程中要注意这种偏差。
我运行了一些 Python 脚本,从 80,000 多个英语单词中创建了关于音节发音和拼写的数据库。让我们一起探索这些数据,看看我们会发现什么。*
最易发音的音节
同理到有很多发音,让我们看看哪组字母的发音最多*。*
一个音节有多少种发音方法?|作者图片
音节 su 和 cha 的 17 种独特发音!?我认为很糟糕!
看看苏的数据,我们就能明白这实际上意味着什么。这是每个发音的打印输出,也是它所在单词的一个例子:
*Pronunciations for the letters 'su':
0 | ʃə | cen**su**rable
1 | sə | cap**su**lar
2 | zə | re**su**rrect
3 | ʃu | cen**su**al
4 | ʒə | clau**su**la
5 | su | ba**su**to
6 | ʃʊ | as**su**rance
7 | ʒu | audiovi**su**al
8 | zu | je**su**
9 | sʌ | blood**su**cker
10 | ʒʊ | cae**su**ra
11 | sjʊ | con**su**lar
12 | zjə | cha**su**ble
13 | sju | di**su**nite
14 | sʊ | e**su**rient
15 | zʊ | u**su**fruct
16 | zju | unpre**su**ming*
我对这种多样性感到惊讶。作为一个以英语为母语的人,你在日常生活中不会想到这个词,但几乎所有这些词你都可以凭经验正确发音。
诚然有些非常相似,但是试着把苏中的每一个字都念成血* 苏 满满你就会意识到一个细微的变化会产生多么大的差别。*
拼写最易变的音节
把前面的图表翻过来,我们来看看的读音有哪些的拼法*。*
每种声音有多少种拼法?|作者图片
发音 si (发音见)有超过 40 种不同的拼法?这似乎不合理。我们来看一下数据。
这是音节拼法的列表和一个可以找到它的例句。
*Spellings for pronunciation 'si':
0 | c | c
1 | cae | caecilian
2 | ce | abecedarian
3 | cea | ceases
4 | cee | divorcee
5 | cei | ceiling
6 | ceip | receiptor
7 | cey | pricey
8 | ci | acierate
9 | cie | facie
10 | cis | precis
11 | coe | biocoenosis
12 | cy | abbacy
13 | pse | psephological
14 | sai | saiva
15 | scae | muscae
16 | sce | ascesis
17 | scei | transceiver
18 | sci | biosciences
19 | scie | bioscience
20 | se | antiserum
21 | sea | battersea
22 | see | endorsee
23 | sei | caseinogen
24 | seig | seigneur
25 | seu | transeunt
26 | sey | anglesey
27 | si | albigensian
28 | sie | besieging
29 | sig | monsignor
30 | ssae | fossae
31 | sse | colosseum
32 | ssee | addressee
33 | ssey | odyssey
34 | ssi | aglossia
35 | ssie | aussie
36 | ssis | chassis
37 | ssy | bessy
38 | sy | apostasy
39 | ze | yangtze
40 | zi | ritziest
41 | zy | chintzy*
除了 18 和 19(有点可疑),我要说都是有效的。
你认为用 40 种不同的方法来写一个声音合理吗?英语好像是这么认为的。
最常见的音节
作为一个题外话,我感兴趣的是在我使用的单词列表中最常出现的音节。
简单地计算每个音节的字数的问题是,它不能公平地代表最“常见”的声音。这是因为我们不知道每个词在日常生活中的相对使用频率。
考虑到这一点,请看下图。
前 20 个最常见的音节音是什么?|作者图片
,如 a bandon,是用词最多的,紧随其后的是 ri (ree)和 li (lee)。前三个之后,会有一个很大的下降。
让我们缩小来看看前 200 个音节。
最常见的前 200 个音节是什么?|作者图片
在第 100 个最常见的音节附近,出现该音节的单词不到 500 个(80,000+个),这似乎表明绝大多数单词使用相同的几个音。
这让我想起了帕累托法则,也就是 80/20 法则,20%的原因构成了 80%的结果。在这里,原因是声音,效果是使用它们的单词。
以下是该数据的“帕累托图”。
与不太常见的音节相比,语言中最常见的音节有多大分量?|作者图片
在这种情况下,效果更加不均衡。大约 20%的音节音构成了我们发出的所有声音的 90%。
言语的网络
我想要回答的下一个问题是这些音节是如何连接在一起的。假设已知一个单词中有另一个音节,那么每个音节出现在这个单词中的可能性有多大?
例如,如果我知道一个单词以 si、这个音开始,那么接下来最有可能是什么音呢?由此产生的网络图是我最喜欢的视觉效果之一。
我计算了前 20 个最常见的音节之间出现的频率。节点大小代表音节出现的总字数。
哪些音节经常出现在一起?|作者图片
的和的之间的紧密联系表明它们经常出现在单词中,而 di 和 tI 之间的微弱联系表明这些音节很少出现在单词中。**
总的来看,似乎两个音节越相似,它们之间的联系就越小。
元音的荒谬
最后,让我们记住我们如何开始这篇文章。
通过比较音节发音和拼写,我们能够识别发音差异很大的拼写,以及可能来自大量不同拼写的声音。
我们一直专注于音节,但在这个发音不一致的世界里,元音和是最糟糕的罪犯。**
让我们看看每个元音能发出的所有不同的音。在下面的热图中,深色代表字母/声音组合的大量单词。
五个元音分别发什么音?|作者图片
我们看到的是,有一些特定的声音,即使不是全部,也可以由大多数元音发出。 i 、 ʌ 、 ɑ 、 ə 、 I 这几个音都可以由 3 个以上的元音组成!
这还没有考虑元音组合(如 ai) ,以及不同的口音和方言,这些会让元音听起来更加混乱!
我想说的是,单词中使用的元音并不能明确地识别它所代表的声音,这可能会让任何试图学习这门语言的人感到沮丧。
关键要点
如果用一句话来概括这篇文章,那将是:书面语很少提供对口语的直观理解。
当然,它可以给你指明正确的方向,但这需要多年的经验。即使这样,结果也是模糊的。
如果你想看到这种现象的发生,可以看看 Bad Spelling Generator,它通过替换其他单词中发音相同的音节来重新拼写单词。结果可能是荒谬的。
***http://badspellinggenerator.com/
如果你喜欢这篇文章,可以考虑关注我,看看我的其他作品,比如这个关于从推文中提取情感的项目。
***
英语到印地语的神经机器翻译
实践教程
探索所有 Seq2Seq 任务中普遍存在的基本 NLP 预处理和编码器-解码器架构
图片经由 Adobe Stock 授权给 Maharshi Roy
自该领域的研究开始以来,机器翻译无疑是自然语言处理中最常遇到的问题之一。通过本教程,我们将探索一个编码器-解码器 NMT 模型采用 LSTM。为了简单起见,我将在后面的文章中讨论基于注意力机制的更好的性能架构。
资料组
我使用了 IIT 孟买英语-印地语语料库作为本教程的数据集,因为它是可用于执行英语-印地语翻译任务的最广泛的语料库之一。呈现的数据实际上是每种语言的两个独立文件中的句子列表,看起来像:
单独文件中的英语和印地语句子对(图片由作者提供)
预处理
作为基本的预处理,我们需要删除标点符号,数字,多余的空格,并将其转换为小写。封装在函数中的非常标准的东西:
预处理功能
值得注意的一件重要的事情是目前的北印度语句子的恶名。除了通过上述函数运行句子之外,还需要对句子进行净化,以删除其中包含的英文字符。这实际上导致了训练中的NaNbug,耗费了大量的调试时间。我们可以通过使用 regex 将任何英文字符替换为空字符串来避免这个问题,如下所示:
hindi_sentences = [re.sub(r'[a-zA-Z]','',hi) for hi in hindi_sentences
LSTMs 通常不会执行很长的序列(虽然比 RNNs 好,但仍然无法与 Transformers 相比)。因此,我们将数据集缩减为包含长度不超过 10 个单词的句子。同样出于演示的目的,我们考虑总共 25000 个句子来保持较低的训练时间。我们可以实现以下目标:
准备数据
在翻译过程中,我们的模型如何知道什么时候开始和结束?为此,我们专门使用了和标记,我们将它们添加到目标语言中。这可以通过如下所示的一行程序来实现:
hi_data = ['<START> ' + hi + ' <END>’ for hi in hi_data]
虽然这个巫毒步骤的确切用法目前可能还不清楚,但是一旦我们讨论了架构和事情是如何工作的,就很容易理解了。在这一点上,我们仍然有字符语句形式的数据,需要将其转换为数字表示,以便模型进行处理。具体来说,我们需要为英语和印地语句子创建一个单词索引表示。我们可以使用 tensorflow 的内置令牌化器来解除手动执行这一任务,如下所示:
这里需要注意的一个重要参数是 oov_token ,,它本质上代表词汇之外。这是专门用在我们想限制我们的词汇量,说只有前 5000 个单词。在这种情况下,任何不受欢迎的单词(基于实例计数> 5000 的单词排名)将被参数值替换,并被视为不包含在词汇表中。这样,我们可以放松我们的模型,忽略数据集中出现的模糊和罕见的单词所造成的任何影响。另一个需要注意的要点是,我们为英语和印地语单词的 vocab 大小加 1,以容纳填充数字(稍后讨论)。
准备解码器输入和输出
NMT 模式的运作方式可以理解为一个两阶段的过程。在第一阶段,编码器模块一次一个单词地接收输入序列。因为,我们使用的是 LSTM 层,随着每个单词的消耗,内部状态向量*【h,c】*会得到更新。在最后一个字之后,我们只关心要输入解码器的这些向量的最终值。然后,第二阶段通过摄取编码器的最终[h,c]向量并消耗目标句子的每个标记来工作,从< START >开始。为每个令牌产生的输出将与预期的相匹配,并且生成误差梯度来更新模型。在用尽目标输入后,我们期望我们的模型产生一个< END >令牌,表示这个数据样本的完成。从下图中可以更好地理解这一点。
训练阶段**“我会回来的”**的旅程(图片由作者提供)
我们从*《终结者》(1984)* ~《我会回来的》来演示阿诺德的著名对白的翻译。我们可以看到,解码器 LSTM 是用编码器的最终[h,c]初始化的。我们可以将这一步理解为将输入句子的思想/摘要传递给解码器 LSTM。灰色方框表示 LSTM 单位的输出,红色方框表示每个时间步长的目标令牌。当第一令牌< START >被馈送到解码器 LSTM 中时,我们期望它产生मैं,然而它产生तुम,说明了对误差梯度的贡献(用于随时间反向传播)。从上图中可以清楚地看到,我们应该按照下面的方式重新排列我们的目标句子,这可以使用下面的代码块来实现:
decoder-input: <START> मैं वापस आऊंगा
decoder-output: मैं वापस आऊंगा <END>
将解码器输入和输出以及填充序列对齐到固定长度
LSTMs 仅适用于固定长度的序列。既然我们选择了不超过 maxlen 、的句子长度,那么将所有实例填充到那个长度似乎是合乎逻辑的。
模型架构
为了保持较低的训练时间,同时具有足够的维度来捕捉数据中的变化,我们选择将我们的向量维度( d_model )设为 256。
编码器
如上面的代码片段所示,我们关心最终的内部状态*【h,c】*,而不是编码器 LSTM 的输出。
解码器
如上所述,解码器 LSTM 由编码器内部状态初始化,并且其输出被馈送到密集层,该密集层的单元数量等于具有 softmax 激活的目标 vocab 大小( hindi_vocab_size )。想法是在目标词汇表的所有单词上生成概率分布。选择具有最高值的一个,意味着这个特定的令牌是最有可能的,这正是我们在这里所做的。我们使用稀疏分类交叉熵作为要优化的损失函数。
培训和结果
我在 95%-5%的训练测试分割上训练了上述网络大约 100 个历元,在此期间,我观察到它在 80 个历元时遭遇 NaN loss。因此,我使用模型检查点来保存中间解决方案,如下所示:
检查点并保存最佳验证准确性
考虑到 google colab 中有限的电源和断开连接(如果保持空闲训练),我不得不在一个相当乏味的模型和有限的训练数据上妥协,用它我实现了 93%的训练准确率和 26%的验证准确率😢。
推理模型
加载保存的模型。我们需要使用 tensorflow 模型的 get_layer API 引入预训练层,以便在我们的推理工作流中重用。
从加载的预训练层准备推理模型:
推理模型
推理模型重用了经过训练的编码器和解码器模型。编码器部分将经过训练的嵌入层作为输入,并输出最终的*【h,c】(实质上也引入 LSTM 层),供解码器使用。解码器以类似的方式将第一令牌的训练目标嵌入层和编码器最终状态【h,c】*作为输入。对于每个后续令牌,我们提供先前的解码器内部状态(本质上给出当前单词的上下文,并在此基础上预测下一个单词)。从下面的推理算法可以更好地理解这一点:
翻译步骤
在翻译步骤中,我们将英语句子输入编码器,得到输出状态*【h,c】。主要的翻译发生在第 10–18 行,在这里我们获得当前单词的下一个单词,并更新内部状态以用于下一步。这个循环一直重复,直到我们得到一个<结束>令牌或者到达 maxlen 令牌。*
使用谷歌翻译进行推理和 BleU 评分
为了进行推理,我使用了一些句子,并使用了谷歌翻译的其他翻译版本来计算语料库 BleU 的分数。我从训练集中选择样本只是因为我们的验证分数很低,我预计我们的模型在测试集上的表现也同样糟糕(不适合演示)。
注 1: BleU 或双语评价 Understudy Score 是评价预测句子与参考语料库相似度的度量。它在候选数据和参考数据之间的 N 元匹配上使用精确召回概念。数学解释超出了本文的范围,因此,我建议读者仔细阅读 Jason Brownlee 的一篇受欢迎的博客文章。
注 2: 谷歌翻译 API 不是一个免费功能,使用说明可以在相应的 GCP 文档中找到。
下面显示了执行的一些实例。很明显,鉴于我们的模型在训练数据上的出色表现和大约 0.42 的 BleU 分数(一个令人惊讶的高值),我们的模型已经过度拟合了。一旦看到新的数据,这个模型就会彻底失败,这一点也不奇怪。
Input: accerciser accessibility explorer
Prediction: एक्सेर्साइसर पहुंचनीयता अन्वेषक
Dataset Reference: एक्सेर्साइसर पहुंचनीयता अन्वेषक
Google Translated Reference: accerciser अभिगम्यता एक्सप्लोररInput: the default plugin layout for the bottom panel
Prediction: ऊपरी पटल के लिए डिफोल्ट प्लगइन खाका
Dataset Reference: निचले पटल के लिए डिफोल्ट प्लगइन खाका
Google Translated Reference: निचले पैनल के लिए डिफ़ॉल्ट प्लगइन लेआउटInput: highlight duration
Prediction: अवधि को हाइलाइट रकें
Dataset Reference: अवधि को हाइलाइट रकें
Google Translated Reference: हाइलाइट अवधि
几十年来,神经机器翻译无疑一直是 NLP 的一个挑战性问题。虽然,接近完美的实现,如谷歌翻译等。今天存在,使我们的生活更容易,但我们在这个领域还有很长的路要走,特别是对于低资源语言。尝试探索这个问题对我来说是一个巨大的学习曲线,我希望这个社区的读者可以从我的分享经历中受益。完整的笔记本可在这里获得。
用这些小贴士提升你的 kedro 体验
从软件包安装、批量保存到子流水线和日志记录。愿这些建议能让你的 kedro 生活更轻松。
目录
什么是 kedro,我们为什么要使用它?
#1。设计结构采用模块化管道
#2。使用 Jupyter 笔记本建立流程
#3。对单独的管道使用日志记录
#4。批量保存结果
#5。
软件包版本控制 #6。常用功能存储位置
本文假设您知道如何独立开发 kedro 项目,或者至少在其官方页面上浏览过“宇宙飞船”示例。我将简单介绍一下这个工具,然后看看我的提示列表,这是我用***【kedro 】(适用于所有版本= < 0.17.0)*** 进行了几个月的试验(和努力)的结果
我已经写了一个关于地理人口分析的“ kedro 动手”系列,本文中的知识已经在那里实现了:
什么是 kedro,为什么要用它?
Kedro 是一个开源 Python 框架,用于创建可复制、可维护和模块化的数据科学代码。它从软件工程中借用概念,并将其应用于机器学习代码;应用的概念包括模块化、关注点分离和版本控制。[1]
使用 Kedro 的主要优势是以一种非常流畅的方式在数据科学领域应用软件和数据工程的概念。
Jo Stichbury 关于 kedro 的文章是一个很好的开始:
#1.模块化管道设计结构
在开始任何项目之前,坐下来规划整体结构是明智的。当旋转一个新的 kedro 项目时,它会创建一个标准的文件夹结构,如下所示:
作者图片
对于一个简单的数据科学项目,上面的结构已经足够好了。然而,当处理一个更复杂的项目时,虽然您可以添加更多的节点并使管道更长,但它会击败 kedro 的“干净组织”优势。所以我的经验法则是“如果管道有 5 个以上的节点,让我们看看我是否能把它们分成子管道”(如果不能,尽量把节点数保持在 5 到 10 之间)。为什么“5”—因为对我来说,在 Pycharm 窗口中,你不用向下滚动就能看到大约 5 个节点。
如果管道有 5 个以上的节点,让我们看看是否可以将它们分成子管道
例如:我的 CheapAtlas 项目中有 3 个节点的管道。作者图片
为了避免这些混乱,您可以创建一个子管道(或官方文档中的模块化管道):
kedro pipeline create <pipeline_name>
当运行上面的命令时,除了生成额外的文件夹,kedro 还将添加参数和目录 YAML 文件到:
conf/base/pipelines/<sub_pipeline_name>/
因此,如果您为数据准备流程名称preparation_1
和preparation_2
创建 2 个子管道,则文件夹结构将更新如下:
conf 和 src 文件夹中的更改。作者图片
在这里,设置*conf/base/pipelines*
中的 YAML 文件将拥有优先于根*conf/base*
文件夹中的 YAML 文件。换句话说,您可以单独为**preparation_1**
或**preparation_2**
指定目录中的哪些数据集和哪些参数,而无需在根目录. yml 和 parameters.yml 中列出所有数据集和参数
也可以在子管道下创建子管道。但这一次,你需要做一些手动复制文件和文件夹。
不仅如此,您可以在子管道中创建子管道,这使得组织任务更加清晰。但是,在这种情况下,您还不能使用 kedro CLI。所以我们必须自己创建文件夹和复制样板文件。继续上面的例子,如果我想在**preparation_1**
下创建一个子管道**preparation_1_a**
和**preparation_1_b**
会发生什么
亚-亚管道。作者图片
这样,*conf/base/pipelines/preparation_1*
下的 YAML 文件将同时应用于**preparation_1_a**
和**preparation_1_b**
。要连接它们,您需要在**preparation_1/pipelines.py**
中的 create_pipeline() 下指定您想要连接的所有子子管道,而不是像普通的 pipelines.py 文件那样列出所有节点(请将< name_of_package >改为您的项目包名称)
preparation_1 文件夹下的 pipelines.py。作者图片
#2.使用 Jupyter 笔记本建立流程
搬到 kedro 不代表放弃笔记本。我本人非常喜欢与 Jupyter Lab 合作(最近他们发布了 3.0 版本!)因为我可以轻松地快速测试和构建我的流。Kedro 还安装了 Jupyter Notebook,但是您应该升级软件包,因为它们使用的是旧版本的 0.4x。
pip uninstall jupyter
pip install jupyter
pip install jupyterlab
然后,您可以通过以下方式启动 Jupyter 集群(记住将终端光标放在项目文件夹中):
kedro jupyter lab
你可以阅读 kedro 关于与笔记本整合的官方文档。对我来说,使用笔记本进行 EDA 是一个很大的优势,因为你可以轻松地制作图表材料。此外,用多个笔记本建立一个完整的机器学习工作流也更容易理解:你可以从 0_EDA 到 1_data_preparation 等等开始,并将它们都放在**notebooks**
文件夹下。
【CheapAtlas 项目的 Jupiter 笔记本文件夹示例。作者图片
#3.对单独的管道使用日志记录
Kedro 官方文档有一些关于日志记录的指南,我认为这是调试的关键部分。通常,当不使用记录器时,您可以直接将结果打印到 CLI 终端。但是,如果您的 kedro 管道生成大量日志或打印出大量信息,那么就很难使用终端窗口进行追溯(而且您甚至没有在外部保存日志文件)。
要启用记录器,最好将其插入到**nodes.py**
中的函数中
# for logging
import logging
log = logging.getLogger(__name__)< codes here >
logging.info(abc)
logging.warn(abc)
logging.error(abc)
我通常会尽量多地记录*warnings*
和*errors*
,但尽量少记录*infos*
,因为讽刺的是,它们是你最不想存储的信息。要了解有关不同级别的日志记录的更多信息,请参考此处:
另一个技巧是指定处理程序来记录到管道的特定文件夹中——这意味着如果您正在运行子-子管道,日志应该保存在该管道的相应文件夹中。从上面的例子中可以看出,如果您只想保存**prepration_1_a**
子管道的日志,文件应该保存在这里:
为日志文件分而治之。作者图片
同样,结构看起来很清楚,但是如果您的管道不是那么复杂并且不生成长日志,我仍然建议坚持默认设置,只使用一个日志文件。
#4.批量保存结果
我同意 Lou Kratz 关于 kedro 的一个问题是,kedro 不支持像用增量数据集读取那样好的写入结果。这意味着,如果您正在处理一个运行了 6-7 个小时的大型数据集,突然内存不足或遇到一些错误…您的结果将不会被保存(哎哟!).问题已经在这里提出,让我们等待更新。
与此同时,我们可以通过将保存结果分成多个文件并使用日志记录来跟踪它们来实现一种变通方法。如果您想将结果保存到一个文件中,使用pandas.write_csv
的追加功能,如下所示。使用这种方法,当在*pipelines.py*
中声明节点时,您不必指定**Outputs**
(参见我在技巧#1 中的 3 个节点的例子)。记得直接在*nodes.py*
写日志跟踪进程就行了
import os
idx = (put the tracking index in the code section before this)# if file does not exist write header
if not os.path.isfile('filename.csv'):
df.to_csv('filename.csv', header='column_names')
else: # else it exists so append without writing the header
df.to_csv('filename.csv', mode='a', header=False)
# logging here for tracking with idx as the index
logging.info(f'Append result for element {idx} complete!')
将结果分成多个保存文件或使用 pandas.write_csv()的 append 函数,并使用日志记录来跟踪进度
#5.包版本控制
而 kedro 的功能是用kedro build-reqs
自动建立一个包需求列表,你可以用pip install -r requirements.txt
安装所有的需求。然而,我发现这给我的项目带来了很多麻烦(因为一些已知的原因,比如pip vs conda install
以及 python version = >,结果是使用 3.7 在这一点上是最稳定的——当然,还有一些未知的原因)。
过了一段时间,我发现保持事情一致性的最好方法是在requirements.in
中放入“硬锁”包版本,而不是依赖自动命令。一篇关于为什么requirements.txt
不够以及如何使用pip freeze
和pip-compile
的好文章,在这里。
#6.常用功能存储位置
在开发管道时,有时您希望重用已经在其他管道中开发的代码块。为了避免引用存储代码的nodes.py
,最好创建一个文件夹来存储src/common
中的那些函数,然后使用它作为引用的中心点。
在项目开始时,最好在 src/common 中为可重用函数创建一个文件夹,以便您可以在以后引用它们
继续上面的例子,当开发 preparation_1_b 时,您注意到有一个来自preparation_1_a/nodes.py
的函数,您希望重用。通常情况下,你会这样写preparation_1_b/nodes.py
:
from <name_of_package>.preparation_1.preparation_1_a.nodes import a,b,c
你应该做的是
from <name_of_package>.commons.helpers import a,b,c
在 src/commons 中存储可重用的函数。作者图片
感谢阅读!如果你能留下回复,我会很高兴,我们可以进一步讨论这个话题。
参考
[1] QuantumBlack。(2020).什么是 Kedro?— Kedro 0.17.0 文档。[在线]可从以下网址获取:https://kedro . readthedocs . io/en/stable/01 _ introduction/01 _ introduction . html【2021 年 1 月 24 日获取】。
用 SQLite 增强你的熊猫技能
python 内置数据库 SQLite 的一个简单技巧,使您的数据操作更加灵活和轻松。
Pandas 是一个强大的 Python 包,可以处理你的数据。但是,您是否遇到过一些任务,让您觉得‘如果我能在这里使用 SQL 查询就好了!’?我个人觉得在 pandas 中连接多个表并只提取那些您想要的列时特别烦人。例如,您想要连接 5 个表。您完全可以只用一个 SQL 查询就做到这一点。但是在熊猫身上,你要做 4 次归并,a+b,(a+b)+c,((a+b)+c)+d,…更糟糕的是,每次合并时,pandas 将保留所有列,尽管您可能只需要另一个表中的一两列。如果你和我有相同或相似的问题,你来对地方了。
这个问题让我想到了 SQLite,这是一个内置的轻量级 Python 数据库。内置意味着它是 Python 自带的,你不必再运行任何 pip/conda install 来安装这个包。有多神奇?!让我们看看这个魔术是如何运作的。有两种方法可以实现这一点,要么使用内置的 sqlite3 包,要么也可以使用 sqlalchemy。我将在下面说明这两个问题。
只需导入软件包,无需任何其他安装(您可能需要安装 sqlalchemy):
import sqlite3 as sl
import sqlalchemy as sa
from sqlalchemy import create_engine
不要担心驱动程序或连接字符串等。您可以像下面这样简单地创建连接:
# SQLite
sl_conn = sl.connect(‘{your db name}.db’)# SQLAlchemy
engine = create_engine('sqlite:///PATH/TO/DB/{your db name}.db',echo=True)
sa_conn = engine.connect()
如果数据库不存在,这将自动创建数据库,然后连接到它。否则,它将直接连接到数据库。多方便啊?
请注意,这将创建一个 db 文件。在 sqlalchemy 的create_engine
中,将echo
属性设置为 True 将会打印出日志。如果不想看到整个日志被打印出来,设置为 False。
一旦你创建了连接,通过熊猫的to_sql
和read_sql
功能与数据库进行交互就更加容易了。
假设您已经有了一些想要使用 SQL 查询的 pandas 数据帧。第一步是将数据帧导入到我们刚刚创建的数据库中。
# sa_conn and sl_conn can be used interchangeably
df_1.to_sql(‘table_1’, sa_conn, if_exists='replace')
df_2.to_sql(‘table_2’, sl_conn, if_exists='replace')
df_3.to_sql(‘table_3’, sa_conn, if_exists='replace')
您可以在数据库中指定表名,它不必与数据帧的名称相同。使用if_exists
来指定如果该表已经存在于数据库中,您想要做什么。选项包括“失败”、“替换”、“追加”。
- ’ fail ':引发 ValueError。(默认)
- ’ replace ':在插入新值之前删除该表。
- ’ append ':向现有表中插入新值。
一旦数据库中有了表,就可以开始使用 SQL 查询它们。
query = ```
SELECT
*
FROM table_1
INNER JOIN table_2 ON table_1_key=table_2_key
INNER JOIN table_3 ON table_1_key=table_3_key
```
joined_table = pd.read_sql(query, sa_conn)
首先将您的查询定义为一个字符串,如果查询很长并且有多行,使用`````,然后使用 pandas 的函数read_sql
从数据库中提取数据并将其转换为 pandas dataframe。
使用 SQL 客户端工具与数据库进行交互也是完全不需要动脑筋的。可以使用 DBeaver 或者 DB Browser for SQLite (DB4S)之类的工具。
迪贝弗
打开 DBeaver 并选择 SQLite 作为数据库驱动程序。
浏览到之前生成的 DB 文件,然后单击 finish。
DB4S
DB Browser for SQLite 也不例外。单击“打开数据库”,然后导航到数据库文件并打开它。
恭喜你!!现在你知道如何利用 Python 的内置数据库工具 SQLite,将你的熊猫技能带到另一个水平!通过集成 pandas 和 SQL 查询,您的数据处理技能变得更加灵活和强大。
用边缘图增强你的散点图
在您的 Plotly Express 散点图上显示额外信息
图示测井数据的散点图,在两个轴上显示边际图。图片由作者提供。
散点图是数据科学中常用的数据可视化工具。它们允许我们在二维图上画出两个数值变量,如点。从这些图中,我们可以了解这两个变量之间是否存在关系,以及这种关系的强度如何。
在这个简短的教程中,我们将使用优秀的 Plotly 库来可视化一个数据集,我们将了解如何在 y 轴和 x 轴的边缘添加边缘图,以增强我们对数据的可视化和理解。
我已经介绍了在 plotly 和 matplotlib 中创建散点图,您可以在下面找到:
本教程的一部分在我的散点图视频中有所涉及
普洛特利图书馆
Plotly 是一个基于网络的工具包,用于生成强大的交互式数据可视化。这是非常有效的,可以用很少的代码行生成图。这是一个受欢迎的库,包含各种各样的图表,包括统计、金融、地图、机器学习等等。
Plotly 库有两种主要用途:
- Plotly Graph Objects,这是一个用于创建图形、轨迹和布局的低级接口
- Plotly Express,它是 Plotly 图形对象的高级包装器。Plotly Express 允许用户键入更简单的语法来生成相同的绘图。
使用 Plotly Express 创建散点图
加载库&数据
第一步是加载 pandas ,它将用于加载我们的数据,以及 plotly.express 用于查看数据。
import pandas as pd
import plotly.express as px
一旦导入了库,我们就可以导入数据了。
我们将在本文中使用的数据集来自 Xeek 和 FORCE(https://xeek.ai/challenges/force-well-logs/overview)举办的岩性预测机器学习竞赛。竞赛的目的是从由 98 口训练井组成的数据集中预测岩性,每口井的测井完整性程度不同。目的是根据测井测量预测岩相。要下载该文件,请导航到上面链接的数据部分。原始数据源可在:https://github . com/bolgebrygg/Force-2020-机器学习-竞赛下载
df = pd.read_csv('xeek_subset_example.csv')
然后我们可以调用df
来查看数据帧的前五行和后五行。
包含测井数据的数据帧。图片由作者提供。
我们得到的是上面的数据帧。我们的数据集包含两个测井测量值(ρb-体积密度和 NPHI-中子孔隙度),一个深度曲线和一个地质解释的岩性。
创建散点图
用 Plotly Express 创建散点图非常简单,我们指定数据帧和我们想要绘制的列。
px.scatter(data_frame=df, x='NPHI', y='RHOB', range_x=[0, 1],range_y=[3, 1], color='LITH')
这将返回以下散点图。目前,它看起来有点乱,因为许多岩性有重叠的价值。这是因为解释的岩性是基于许多不同的测井测量和岩屑描述而产生的。
测井数据散点图的简单图示。图片由作者提供。
点击图例中的 LITH 名称,可以隐藏单个 LITH 组。
简单绘图表示滤波后的测井数据散点图。图片由作者提供。
向 Plotly Express 散点图添加边际图
边缘图是可以附加到 y 轴和 x 轴边缘的迷你图。Plotly Express 中有四种不同类型的边际图。
箱线图
箱线图是一种基于五个关键数字显示数据分布的图形和标准化方法:最小值、第一个四分位数(第 25 个百分位数)、中值(第二个四分位数)。/第 50 个百分位数)、第 3 个四分位数(第 75 个百分位数)和“最大值”。最小值和最大值分别定义为 Q1-1.5 * IQR 和 Q3 + 1.5 * IQR。任何超出这些限制的点都被称为异常值。
箱线图的图形描述,突出显示关键组成部分,包括中位数、四分位数、异常值和四分位数间距。作者创建的图像。
边缘箱线图可以添加到单个轴上,如下所示
px.scatter(data_frame=df, x='NPHI', y='RHOB', range_x=[0, 1],range_y=[3, 1], color='LITH',
marginal_y='box')
用 y 轴上的一系列箱线图来表示散点图。图片由作者提供。
或者通过指定关键字参数marginal_y
和marginal_x
的值来指定两个轴。
px.scatter(data_frame=df, x='NPHI', y='RHOB', range_x=[0, 1],range_y=[3, 1], color='LITH',
marginal_y='box', marginal_x='box')
用轴上的箱线图来表示散点图。图片由作者提供。
地毯图
Rug 图用于显示数据分布,可添加如下内容:
px.scatter(data_frame=df, x='NPHI', y='RHOB', range_x=[0, 1],range_y=[3, 1], color='LITH',
marginal_y='rug', marginal_x='rug')
用轴上的地毯图来表达散点图。图片由作者提供。
直方图
直方图是一种优秀的数据可视化工具,看起来类似于条形图。然而,直方图使我们能够深入了解一组数据中的值的分布,并使我们能够在一个简洁的图中显示大量数据。在岩石物理学和地球科学领域,我们可以使用直方图来识别异常值,也可以挑选关键的解释参数。例如,来自伽马射线的粘土体积或页岩体积端点。
要将边缘图更改为直方图,我们的操作如下:
px.scatter(data_frame=df, x='NPHI', y='RHOB', range_x=[0, 1],range_y=[3, 1], color='LITH',
marginal_y='histogram', marginal_x='histogram')
用轴上的直方图边缘图来表示散点图。图片由作者提供。
小提琴情节
Violin 图类似于 box 图,但它们也结合了核密度估计图的强大功能。除了说明箱线图显示的关键统计点之外,它还允许我们深入了解数据的分布。
px.scatter(data_frame=df, x='NPHI', y='RHOB', range_x=[0, 1],range_y=[3, 1], color='LITH',
marginal_y='violin', marginal_x='violin')
用轴上的小提琴边际图来表达散点图。图片由作者提供。
混合边际图
您不必在两个轴上绘制相同的图,您可以在 x 轴上使用直方图,在 y 轴上使用小提琴图。
px.scatter(data_frame=df, x='NPHI', y='RHOB', range_x=[0, 1],range_y=[3, 1], color='LITH',
marginal_y='violin', marginal_x='histogram')
用轴上的混合边际图来表示散点图。图片由作者提供。
摘要
在这个简短的教程中,我们看到了如何使用测井数据在 plotly express 散点图上显示各种边际图。这些图可以增强我们的数据可视化,并为我们提供有关数据分布的更多信息。
感谢阅读!
如果您觉得这篇文章有用,请随时查看我的其他文章,这些文章介绍了 Python 和测井数据的各个方面。你也可以在GitHub找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn或者我的 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
如果你喜欢阅读这些教程,并想支持我作为一名作家和创作者,那么请考虑报名成为一名媒体成员。一个月 5 美元,你就可以无限制地阅读数千篇各种主题的文章。如果您使用 我的链接 **,**注册,我将为您赚取一小笔佣金,无需额外费用!
https://andymcdonaldgeo.medium.com/membership
利用安讯士上的图像增强您的电力 BI 报告
你有没有想过在 axis 上使用图像而不是“常规”文本?或者,甚至在切片机内?检查这个简单的技术,你就可以开始了!
作者图片
如果你经常关注我的故事,你应该已经注意到我经常试图增强 Power BI 提供的内置可视化效果。在我看来,在某些情况下,作为一名 Power BI 开发人员,你可以超越自我,使用简单的技巧将用户体验提升到一个新的水平。
不久前,我正在为我们的客户支持部门创建一个报表解决方案。基本上,支持代理执行四种不同类型的与客户的交互—聊天、电子邮件、电话和调查。此外,管理人员需要一份报告来衡量每种特定类型的交互数量,以便他们可以确定高峰时间并为特定任务分配代理。
搭建舞台
对于这个例子,我将保持事情简单。有一个包含交互唯一 id、交互日期和交互类型的表。第一步是创建一个显式度量,它将计算交互的数量。
Total Interactions = COUNT(Sheet1[Interaction ID])
这是我此刻的视觉效果:
作者图片
如你所见,这里没什么特别的,“经典”柱形图。所以,让我们试着给它注入一些活力。我已经写过 DAX 中的 UNICHAR() 函数,以及如何在某些场景中利用这个函数。该函数返回 Unicode 字符,以表示您提供的数值。
现在,这个想法是用文字的视觉表现来代替文本值:聊天、电子邮件、调查和电话。有一系列不同的符号可以用 Unicode 表示。这里的是非常全面的符号列表,以及它们的 Unicode 值。
替换文本值
第一步是在 Power BI 数据模型中创建一个新表,它将保存关于 Unicode 值的数据。
作者图片
将表加载到数据模型后,我将切换到模型视图,并在该表和我的原始表之间建立“一对多”关系:
作者图片
之后,我将在我的交互 Unicodes 表中创建一个新的 DAX 列。神奇的事情发生了——我们将使用 UNICHAR() 函数,作为参数,我们将提供来自 Unicode 列的值:
Interaction Type Icon = UNICHAR('Interactions Unicodes'[Unicode])
瞧,现在看看这一列中的值!
作者图片
当我们建立了表之间的关系后,让我们抓取该列,并将其放在柱形图的 x 轴上:
作者图片
看起来很酷,对吧?您可以增加/减少 x 轴的字体大小,就像您对“正常”文本所做的一样——并且您的图标将被调整大小!
此外,我们还可以将这些图标用作切片器!让我向您展示如何使用它来增强您的用户体验。
作者图片
正如你在上面的插图中看到的,我已经很好地格式化了切片器,看起来像一组按钮(通过使用水平方向),所以我现在可以通过点击它来切片我的数据!我还创建了一个矩阵视觉,你可以看到我们的图标在那里,就像普通的文本列一样。最后,UNICHAR()函数将返回文本值,您可以像处理普通文本一样处理它。
结论
要记住的关键事情是:在创建 Power BI 报告时,不要让这个技巧成为主要的“工作方式”!当然,在大多数情况下,你应该坚持在你的视觉效果中显示价值的传统方式。但是,在某些情况下,应用这种技术可以增强用户体验。
在我看来,如果你决定使用这个技巧,有两件事非常重要:
- 处理有限数量的类别——正如你在我的例子中看到的,有固定数量的可能类别(聊天、电话、电子邮件和调查),所以没有轴/切片器被太多图标弄得混乱的危险
- 图标必须提供“常规”文本值 的上下文——用户必须明白每个图标代表什么。如果不是这样,你的报告将会导致混乱而不是清晰,这也是在使用这种技术之前要三思的另一个有效的理由
像往常一样,尝试在增强用户体验和使用这些非标准技术可能导致的额外开销之间找到适当的平衡。
感谢阅读!
使用 pycodestyle 增强 Python 代码的可读性
基于 PEP-8 风格约定自动检查 Python 脚本的可读性和质量
Emmanuel Ikwuegbu 在 Unsplash 上拍摄的照片
编程是数据从业者工具箱中不可或缺的技能,虽然创建一个脚本来执行基本功能很容易,但是大规模编写良好的可读代码需要更多的工作和思考。
鉴于 Python 在数据科学中的受欢迎程度,我将深入研究使用 pycodestyle 进行样式向导检查,以提高 Python 代码的质量和可读性。
内容
关于 PEP-8
pycodestyle 检查器根据 PEP-8 样式约定提供代码建议。那么 PEP-8 到底是什么?
PEP 代表 Python 增强提议,而 PEP-8 是概述编写 Python 代码最佳实践的指南。于 2001 年创作,其主要目标是通过标准化代码样式来提高整体代码的一致性和可读性。
需要注意的一点是,PEP-8 旨在作为指南,而不是旨在被解释为始终严格遵守的圣经指示。
动机
快速浏览一下 PEP-8 文档会立即发现有太多的最佳实践需要记住。
此外,已经花了这么多精力编写了这么多行代码,您肯定不希望浪费更多时间来手动检查脚本的可读性。
这就是 pycodestyle 发挥作用的地方,它可以自动分析您的 Python 脚本,并指出代码可以改进的具体领域。
格伦·卡斯滕斯-彼得斯在 Unsplash 上的照片
装置
这个包被命名为 pep8 ,但是为了减少混淆,被重新命名为 pycodestyle 。这是在 Python 的创造者(吉多·范·罗苏姆 ) 强调工具不应该以样式指南命名之后,因为人们可能会根据 pep8 (工具)的行为与 PEP-8(样式指南)进行“斗争”。
pip 是首选的安装程序,您可以通过在终端中运行以下命令来安装或升级 pycodestyle :
# *Install pycodestyle*
pip install pycodestyle# *Upgrade pycodestyle*
pip install --upgrade pycodestyle
基本用法
最直接的用法是在 Python 脚本上运行 pycodestyle 。py 文件)作为终端中的命令。让我们使用下面的示例脚本(名为*pycodestyle _ sample _ script . py)*进行演示:
我们通过运行这个简单的命令让 pycodestyle 开始工作:
pycodestyle pycodestyle_sample_script.py
输出指定了违反 PEP-8 样式约定的代码位置:
运行 pycodestyle | Image by Author 后的输出
每行中用冒号分隔的一对数字(如 3:19)分别指行号和字符号。
例如,6:64 E202 whitespace before ')'
的输出意味着在第 6 行中,在第64字符标记处有一个意外的空白。
您可以通过解析统计数据参数来查看错误发生的频率:
pycodestyle --statistics -qq pycodestyle_sample_script.py
pycodestyle 确定的错误频率|按作者分类的图片
在上面的输出中,我们看到在右括号“ ) ”前出现了 4 次意外的空白。
高级用法
我们也可以将 pycodestyle 直接导入到我们的 Python 代码中来执行**自动化测试。**这对于自动测试多个脚本的编码风格一致性非常有用。
例如,可以编写以下类来自动检查是否符合 PEP-8 约定:
**import** **unittest**
**import** **pycodestyle**
**class** **TestCodeFormat**(unittest.TestCase):
**def** test_conformance(self):
*"""Test that the scripts conform to PEP-8."""*
style = pycodestyle.StyleGuide(quiet=**True**)
result = style.check_files(['file1.py', 'file2.py'])
self.assertEqual(result.total_errors, 0, "Found style
errors")
该工具也可以被配置,以便基于我们定义的样式规则首选项来完成测试。例如,我们可以删除不希望在检查中检测到的特定错误:
style = pycodestyle.StyleGuide(ignore=['E201', 'E202', 'E501'])
或者,我们可以指导 pycodestyle 一起使用不同的配置文件(包含一组特定的样式规则)。
**import** **pycodestyle**
style = pycodestyle.StyleGuide(config_file='/path/to/tox.ini')
还有更多功能可以满足您的需求,请访问pycodestyle的文档页面了解更多详情。
结论
在本文中,我们看了如何使用pycodestyle工具来检查我们的 Python 脚本是否符合 PEP-8 代码样式约定。
代码被阅读的次数比被编写的次数多,所以我们的代码是一致的、可理解的和结构整洁的是至关重要的。相信我,你的合作者和你未来的自己会为此感谢你的。
欢迎您加入我的数据科学学习之旅!关注此媒体页面以了解更多数据科学内容,或者在 LinkedIn 上联系我。编码快乐!
参考
*
请随意查看远程 Python 职位,这是一个关于远程 Python 相关机会的职位公告板。*
用用于异常检测的存储器模块增强自动编码器
理解大数据
视频、异常和自动编码器
视频、异常和自动编码器
检测视频流中的异常是一项艰巨的任务。即使深度学习技术的兴起可以利用闭路电视摄像头生成的大量数据,这项任务仍然很难解决,因为顾名思义,异常是罕见的,几乎不可能为监督学习标注所有类型的异常。在本文中,我们将介绍如何检测视频馈送中的异常,以及当代作品如何使用内存模块来提高性能。
正如我们所提到的,一个注释良好的、全面的数据集对于异常检测来说是非常难以构建的。这就是为什么无监督学习被用于异常检测,即深度自动编码器。
自动编码器以无人监督的方式进行训练,也就是说,我们给神经网络分配表示学习的任务。本质上,神经网络具有编码器-解码器架构,其中编码器学习压缩的特征集(表示),解码器使用该表示来重构输入(通常,但不总是)。
自动编码器如何用于异常检测?
对于异常检测,自动编码器的任务是重建输入。仅在来自正常场景的图像上训练自动编码器,并且假设当模型遇到异常数据时,自动编码器将具有高重建误差。然而,这一假设在实践中并不成立。经验表明,卷积神经网络(CNN)的表示能力非常强大,足以以低重构误差重构甚至异常的帧。
已经观察到,有时自动编码器“概括”得很好,以至于它也可以很好地重建异常,导致异常的漏检。
内存模块在这一切中处于什么位置?
Gong 等人和 Park 等人的工作试图通过向网络架构添加存储模块来解决这个问题。这两部作品都试图记忆正常数据的原型元素,并使用这种记忆来扭曲异常数据的重构。更简单地说,他们不仅仅使用来自编码器的表示,还使用“存储项”来降低 CNN 的表示能力。在这些工作中,记忆模块的使用略有不同。让我们剖析一下这些实现。
龚等,MemAE
基于 MemAE 的异常检测。这个可视化显示了一个简化的版本,其中只有一个记忆项目被查询。MemAE 专门从存储器项目重建输出。图片由巩等人提供。
MemAE 本质上是一个编码器-解码器,在编码器和解码器之间添加了一个存储模块。编码器用于构建输入的表示,该表示用于查询最相关的存储项目,该存储项目用于解码器的重建。在训练阶段,记忆项目被更新以“记忆”正常数据的原型特征。当运行推理时,存储项目被冻结,并且由于存储模块仅识别正常数据,当异常实体出现在输入中时,它将挑选次优特征。异常帧的重构误差将比正常帧的重构误差高得多,这反过来用于异常检测。
Park 等人,异常检测的记忆引导常态(MNAD)
学习记忆引导的异常检测常态。我们将这个网络称为 MNAD。蓝色+红色特征块将被称为更新特征。注意红色的钥匙上有一个相应的(类似的)蓝色记忆物品。图片由朴等人提供。
继 MemAE 之后,Park 等人引入了类似的解决方案来处理 CNN 自动编码器的表示能力。他们建议使用编码器表示和存储项目作为解码器的输入来进行重建。他们将编码器表示(HxWxC)拆分为 HxW 键(1x1xC),而不是专门使用内存项目进行重建。这些键用于在训练阶段更新记忆项目和获取相关记忆项目。一旦存储器项目被聚集,它们就被堆叠到编码器表示,并被输入到解码器,用于输入的重建。
颜色意味着相似。编码器按键上堆叠有类似的存储项目。当输入是正常帧时,这将很好地耦合,但是密钥、存储器对对于异常帧将是次优的。
其思想是通过构建 2C 宽的表示(如图所示的 updated_features)将内存项用作某种噪声,其中编码器键上堆叠有类似的内存项。如果该帧是异常的,则存储项目不会像正常帧那样紧密匹配。
这些作品的核心区别是什么?
这两个作品的主要区别在于 MemAE 试图最佳地记忆项目,使得存储器模块可以单独地构造具有足够信息的表示,以帮助解码器重构输入;而 MNAD 试图将存储项目用作某种噪声过滤器。updated_features 将是编码器特征和相关存储器项目的串联。在异常输入的情况下,存储器项将仍然对应于正常场景特征,而编码器特征将是异常帧的特征,这在理论上应该增加重建误差。
MemAE 还建议使用 3D Convs 来处理视频中的时间信息,而 MNAD 则通过作为输入批次(4-5 个输入帧)提供的运动线索来处理时间信息。
MNAD 引入了一个测试时间内存更新方案,该方案允许在所有推理时间内对内存模块进行微调。
这些变化给 MNAD 带来了什么?
因为 MNAD 使用编码器表示和存储器模块来构建 updated_features,所以存储器模块没有必要具有大量的存储器项目(MemAE 为 2000 个,MNAD 为 10 个),并且因为存储器模块的大小很小,所以该模块必须具有在查询中很好分布的不同特征。MNAD 引入了特征分离性损失和紧凑性损失,这激励了用不同的存储项目来填充存储模块。
特征分离性损失促使模型减小每个特征与其最近特征之间的距离,同时增大该特征与第二最近特征之间的距离。这看起来就像三重损失。
三重损失是用于机器学习算法的损失函数,其中基线(锚)输入与正(真)输入和负(假)输入进行比较。从基线(锚)输入到正(真)输入的距离最小,从基线(锚)输入到负(假)输入的距离最大。⁹
紧凑性损失激励模型减少编码器构建的表示的类内变化。
总之,这两种损失有助于增加存储在存储模块中的存储项目的多样性,并增加存储模块的辨别能力。
t-SNE 图描绘了具有分离性损失、没有分离性损失和具有我们提出的对力分布的监督的特征(将在本文后面讨论)。
哪个好?
MNAD 始终优于大多数竞争对手,作者主要将性能归功于其内存模块的实现方式和测试时间内存更新方案。
这些结论具体而准确吗?
我们为 RC2020 challenge⁴重新实施了 MNAD,并写了一份关于其可重复性的 report⁵。
这项活动的主要目标是鼓励发表和分享可靠的和可复制的科学成果。为了支持这一点,本次挑战赛的目标是通过邀请社区成员选择一篇论文来调查顶级会议上发表的论文的可重复性,并通过复制计算实验(通过新的实施方案或使用作者提供的代码/数据或其他信息)来验证论文中的实证结果和主张。⁴
让我们更深入地挖掘一下这个理论在多大程度上符合预期。
这些任务被低估了吗?
MNAD 使用两个代理任务来测试他们的模型。
- 重建任务。
- 预测任务。
对于重建任务,模型被馈送 1 幅图像,并且模型的任务是重建该帧。
对于预测任务,该模型被馈送一批 5 个图像(用于时间信息),并且该模型的任务是预测第 6 帧。
这两个任务的模型架构非常类似于 U-Net。主要区别在于,跳过连接需要移除以用于重建任务。这是因为其中一个输入要素是目标要素,通过跳过连接,模型将基本学会将输入复制到输出。
这引发了两个问题。
- 预测任务允许在输入要素中嵌入时态信息,那么该任务是否在一定程度上提高了性能?
- 预测任务还允许模型跳过连接;跳过连接如何影响模型的行为?
从经验上来说,我们表明跳过连接会带来一些性能提升。为了绝对确定是这种情况,我们必须在重建任务中向模型引入 skip 连接。为了实现这一点,我们在输入中添加了不同概率(5%、25%、40%)的椒盐噪声,并让模型对输入进行降噪。由于输入要素不再包含精确的目标要素,因此模型无法通过跳过连接来复制输入。我们看到了一个原始的性能增益 ~4% ,但是它没有超过预测任务的基准。值得注意的是,预测任务确实将时间信息嵌入到输入特征中,这意味着在预测任务上训练的模型将能够对时间和时空异常进行分类,而在重建任务上训练的模型将完全错过时间异常。
因此,根据经验,这两个问题的答案是**是的。**预测任务允许嵌入时间信息和引入跳过连接,这两者都显示出改进的性能。
这个任务到底是如何影响模型行为的?
重建任务没有任何时间信息,因为它只接受一个输入并重建一个输出,因此只能识别空间异常。另一方面,预测任务允许将时间信息嵌入到输入特征中,这有助于模型识别空间、时间和时空异常。
为了显示任务如何影响模型的性能,我们综合生成了一个具有空间、时间和时空异常的数据集。正常情况包括半径为 10 个像素的圆以每帧 5 个像素的速度移动。空间异常包括以相同速度移动的正方形,而时间异常包括以每帧 10 个像素的速度移动的圆形。时空异常是以每帧 10 个像素移动的方块。
下图显示了在预测任务中训练的模型中,输出假象是如何更加突出的。
空间异常:数据以前从未有过正方形,两个模型都将正方形重建为填充正常数据的圆。
时间异常:圆圈移动得更快,这影响了预测任务的输入批次(由于输入中没有时间信息,因此重建时缺少该信息),只有在预测任务中训练的模型显示出伪影。
时空异常:输入中有移动速度比普通圆形更快的正方形。两种模型都将正方形转换成圆形,但是在预测任务上训练的模型也引入了时间假象,这使得分类任务更容易。
这些是唯一可能被低估的因素吗?
MNAD 在 UCSD Ped2⁶、中大 Avenue⁷和 Shanghaitech⁸数据集上进行了基准测试。上海理工大学的数据集比 UCSD 的 Ped2 数据集和 CUHK 大道要复杂得多。当在 Shanghaitech 数据集上重现 MNAD 的结果时,我们注意到在一个使用内存项的试探法中有很多 NaN 值。深入研究后,我们发现内存模块也没有按预期工作。这些特征只与一小部分记忆项目相关。这种偏斜的分布本质上会使内存模块的引入变得完全无用。对于 Shanghaitech 数据集,我们发现所有的特征都只与一个记忆项目相关。这意味着对于异常或正常帧,密钥、存储项目对将是相同的。
记忆张量将总是被映射到这个键,使得记忆模块完全过时。
我们引入了额外的监督,以强制统一分布,这有助于击败报告的分数。这不是一个理想的解决方案,因为我们正在强制一个可能不是最优的特定分布,但是这个黑客确实让管道按照预期工作。
强制统一内存分配。
其他观察结果
我们还观察到,skip 连接在解码输出时涉及的内容非常多,以至于内存模块和中间表示一点也不重要。我们用 0、1 和随机张量代替中间表示,输出在光学上没有变化。这让我们看到了重建和预测任务中测试时间记忆更新的性能差异。预测任务没有受到太大影响,但重建任务将受到巨大打击,这是由于没有跳跃连接来完成繁重的工作。
结论
我们的实验表明,内存模块的添加提高了 UCSD Ped2 和 CUHK 大道等简单数据集的性能。对于像上海理工大学数据集这样更复杂的数据集来说,结论还没有出来。与没有内存条的型号相比,内存条的添加降低了性能。我们发现,在如此复杂的数据集上进行训练,将导致记忆模块被搁置一旁,变得完全过时。我们提出的解决这一问题的额外监督并不理想,但确实让我们将模型评估为比 Park 等人报告的分数略高的分数。对于 ShanghaiTech 数据集,最大的问题仍然是在重建任务上训练的模型的分数略高于预测任务的分数。鉴于重建任务完全忽略了时间异常,这导致了对该数据集的存储模块的功效的质疑。有可能的是,即使在建议的监督之后,该模型也发现更容易严重依赖于跳跃连接,并试图找到表面特征来帮助解码这些特征。也有可能数据集对于模型来说噪音太大。
最后,我们可以通过这些拟议的修订达到报告的分数,但我们的结论是,为上海科技这样的复杂数据集建立稳定的管道仍有工作要做。我希望这篇文章对用于异常检测的内存模块有一个像样的介绍!
参考
[1]马克·克莱默(1991)。“使用自联想神经网络的非线性主成分分析” (PDF)。爱车日报。37(2):233–243。doi:10.1002/AIC . 690370209。
https://arxiv.org/pdf/1904.02639.pdf
[3]https://arxiv.org/pdf/2003.13228.pdf
https://paperswithcode.com/rc2020
https://arxiv.org/ftp/arxiv/papers/2101/2101.12382.pdf
[6]李维新,维杰·马哈德万,和努诺·赛洛斯。拥挤场景中的异常检测和定位。IEEE TPAMI,2013 年。
[7]陆,,和贾亚嘉。MATLAB 中 150 FPS 的异常事件检测。2013 年在 ICCV。
[8]罗伟新,,高胜华.堆叠 RNN 框架下基于稀疏编码的异常检测的再探讨。2017 年在 ICCV。
[9]https://en.wikipedia.org/wiki/Triplet_loss
【https://github.com/alchemi5t/MNADrc
利用 MIRNet 增强微光图像
在弱光下拍摄的照明图像
图像增强(来源:https://github . com/sou mik 12345/MIRNet/blob/master/assets/lol _ results . gif)
在我们的一生中,我们都点击过很多图片,但我特别面临的一个问题是,我相信你们都一定经历过在昏暗的光线下点击图片。如果你没有夜视功能或非常高端的相机,图像可能会非常模糊或光线很弱,这使得图像中的内容几乎看不见。
现在,如果你没有夜视相机,而你又喜欢拍照,那么这篇文章就是为你准备的,因为在这篇文章中,我将向你展示如何增强你所有的弱光图像,使它们清晰可见。所以让我们开始吧。
在本文中,我们将使用 MIRNet 模型进行图像增强。它用于增强低光图像,这是一个预先训练好的模型,可以很容易地从 Github repo 下载。对于这篇文章,我使用的是 Saumik Rakshit 的 GitHub repo。
克隆 Github 存储库
对于本文,我们将使用 Google Collab。对于初始设置,我们需要打开一个 collab 笔记本,并使用以下命令克隆所需的 Github 存储库:
!git clone [https://github.com/soumik12345/MIRNet](https://github.com/soumik12345/MIRNet)
#Setting up current directory
%cd MIRNet
安装 Wandb
Wandb 用于组织和分析机器学习实验。它与框架无关,比 TensorBoard 更轻。每当我们使用 wandb 运行一个脚本时,超参数和输出度量都会被保存下来,可以进一步可视化。我们将通过运行下面给出的命令来安装它。
!pip install -q wandb
导入所需的库
下一个重要的步骤是导入这个项目所需的所有库。
from glob import glob #
from PIL import Image #For image operations
from matplotlib import pyplot as plt #for visualization
# Importing required dependencies from MIRNet
from mirnet.inference import Inferer
from mirnet.utils import download_dataset, plot_resultimport tensorflow as tf # For modelling
import numpy as np # For mathematical computation
下载数据集和预先训练的权重
在这一步中,我们将下载模型和预定义权重的数据。这将为我们的图像增强项目创建具有所需预训练权重的模型。通过运行下面给出的命令,我们将完成这一步。
inferer = Inferer()
inferer.download_weights('1sUlRD5MTRKKGxtqyYDpTv7T3jOW6aVAL')
inferer.build_model(num_rrg=3, num_mrb=2, channels=64, weights_path='low_light_weights_best.h5')inferer.model.summary()
预训练模型(来源:作者)
接下来,我们将权重加载到模型中并保存模型。下面给出的命令将解决这个问题。
inferer.model.save('mirnet')
现在,我们需要将一张图片加载到 google collab 中,以便我们可以使用它进行增强。
IMAGE_LOC = '/content/123.JPG' # change the name of image to you desired image
最后一步
这是最后一步,我们将把输入图像传递给模型,模型将生成增强的输出图像。下面给出的代码适用于这种情况。
original_image, output_image = inferer.infer(IMAGE_LOC)
plot_result(original_image, output_image)
输入图像和增强图像(来源:作者)
在这里,你可以清楚地看到我们的模型是如何将一个低亮度的图像增强为清晰可见的图像的。
继续尝试不同的图片,如果你发现任何困难,你可以在回复部分张贴出来。
这篇文章是与皮尤什·英加尔合作的
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时在 hmix13@gmail.com 上联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。
PyTorch 中的混合增强神经网络
随机混合图像,效果更好?
随着深度学习的指数级改进,图像分类已经成为蓬勃发展的领域之一。传统的图像识别任务严重依赖于处理方法,例如扩展/腐蚀、核和频域变换,然而特征提取的困难最终限制了通过这些方法取得的进展。另一方面,神经网络专注于寻找输入图像和输出标签之间的关系,以为此目的“调整”架构。虽然准确性的提高非常显著,但网络通常需要大量数据来进行训练,因此许多研究现在都专注于执行数据扩充,即从预先存在的数据集增加数据量的过程。
本文介绍了一个简单却惊人有效的增强策略——mixup,通过 PyTorch 实现并比较结果。
在混淆之前——为什么要增加数据?
基于一组给定的训练数据来训练和更新神经网络架构内部的参数。然而,由于训练数据仅覆盖可能数据的整个分布的某一部分,网络可能在分布的“可见”部分上过度拟合。因此,我们用于训练的数据越多,理论上就能更好地描述整个分布。
虽然我们拥有的数据数量有限,但我们总是可以尝试稍微改变图像,并将它们用作“新”样本,输入网络进行训练。这个过程被称为数据扩充。
什么是 Mixup?
图一。图像混合的简单可视化。
假设我们正在对狗和猫的图像进行分类,给我们一组带有标签的图像(即,【1,0】->狗,【0,1】-->猫),混合过程就是简单地平均出两幅图像及其相应的标签作为新数据。
具体来说,我们可以用数学方法写出 mixup 的概念:
其中 x,y 是 xᵢ (标号 yᵢ )和 xⱼ (标号 y ⱼ )的混合图像和标号,λ是来自给定β分布的随机数。
这提供了不同类别之间的连续数据样本,直观地扩展了给定训练集的分布,从而使网络在测试阶段更加健壮。
在任何网络上使用 mixup
由于 mixup 仅仅是一种数据扩充方法,它与任何分类网络体系结构都是正交的,这意味着您可以在具有任何网络的数据集中实现这一点,以解决分类问题。
基于原论文mixup:Beyond experimental Risk Minimization,张等人对多个数据集和架构进行了实验,经验表明 mix up 的好处不仅仅是一次性的特例。
计算环境
图书馆
整个程序是通过 PyTorch 库(包括 torchvision)构建的。混音的概念需要从 beta 分布中生成样本,这可以从 NumPy 库中获得,我们还使用随机库来查找混音的随机图像。以下代码导入所有库:
资料组
为了演示,我们在传统的图像分类上应用了混合的概念,CIFAR-10 似乎是最可行的选择。CIFAR-10 包含 10 个类别的 60000 个彩色图像(每个类别 6000 个),以 5:1 的比例分成训练集和测试集。这些图像分类起来相当简单,但比最基本的数字识别数据集 MNIST 还要难。
有许多方法可以下载 CIFAR-10 数据集,包括从多伦多大学网站或使用 torchvision 数据集。值得一提的一个特定平台是 Graviti 开放数据集 平台,它包含数百个数据集及其对应的作者,以及每个数据集的指定训练任务(即分类、对象检测)的标签。您可以下载其他分类数据集,如 CompCars 或 SVHN,来测试 mixup 在不同场景中带来的改进。该公司目前正在开发他们的 SDK,尽管目前直接加载数据需要额外的时间,但在不久的将来会非常有用,因为他们正在快速改进批量下载。
硬件要求
最好在 GPU 上训练神经网络,因为它可以显著提高训练速度。但是,如果只有 CPU 可用,您仍然可以测试程序。要让您的程序自己决定硬件,只需使用以下代码:
履行
网络
目标是看到混合的结果,而不是网络本身。因此,为了演示的目的,实现了一个简单的 4 层卷积神经网络(CNN ),后面是 2 层全连接层。注意,对于混合和非混合训练过程,应用相同的网络来确保比较的公平性。
我们可以构建如下的简单网络:
混合
混合阶段是在数据集加载过程中完成的。因此,我们必须编写自己的数据集,而不是使用 torchvision.datasets 提供的默认数据集。
以下是通过合并 NumPy 的 beta 分布函数实现的简单混合:
请注意,我们并没有对所有的图像进行混音,而是对大约五分之一的图像进行混音。我们还使用了 0.2 的贝塔分布。您可以更改不同实验的混合图像的分布和数量。也许你会取得更好的成绩!
培训和评估
下面的代码显示了培训过程。我们将批量大小设置为 128,学习速率设置为 1e-3,总的历元数设置为 30。整个训练进行了两次——有和没有混淆。损失也必须由我们自己定义,因为目前 BCE 损失不允许带小数的标签:
为了评估混淆的影响,我们基于有和没有混淆的三次试验来计算最终精度。在没有混淆的情况下,网络在测试集上产生了大约 74.5%的准确率,而在有了混淆的的上,准确率提升到了大约 76.5% !
超越图像分类
虽然 mixup 推动了图像分类的最新精度,但研究表明,它的好处扩展到了其他计算机视觉任务,如生成和对对立示例的鲁棒性。研究文献也已经将该概念扩展到 3D 表示中,这也被证明是非常有效的(例如, 【点混合 )。
结论
所以你有它!希望这篇文章给你一个基本的概述和指导,告诉你如何将 mixup 应用到你的图像分类网络训练中。完整的实现可以在下面的 Github 资源库中找到:
https://github.com/ttchengab/mixup.git
感谢您坚持到现在🙏*!* 我会在计算机视觉/深度学习的不同领域发布更多内容。一定要看看我关于 VAE 的其他文章,一次学习,等等!
使用绘图填充增强测井曲线的可视化
使用 matplotlib 和 fill_betweenx() 将颜色填充应用于测井数据
使用 fill_betweenx 记录绘图阴影。[图片由作者创建]
Matplotlib 是 Python 中一个很棒的库,我总是一次又一次地使用它来处理测井记录。由于其高度的灵活性,开始使用它可能会很棘手,但是一旦您掌握了基础知识,它就可以成为数据可视化的强大工具。
处理测井数据时,通常会对数据应用颜色填充,以帮助快速识别感兴趣的区域。例如,识别岩性或含烃层段。当我在网上搜索实现颜色填充的方法时,大部分时间,文章都指向在一条线和图上的 x 轴之间填充。显示如何将阴影应用于测井曲线的结果明显较少,测井曲线的最长轴通常沿着 y 轴,或者通常沿着 y 轴。
这篇文章是我的 Python &岩石物理学系列的一部分。详情可以在这里找到。
在本文中,我将通过四个不同的例子来说明如何使用简单的填充来增强测井数据的外观。其中包括:
- 从曲线到图/轨迹边缘的简单颜色填充
- 从曲线到图/轨迹两边的颜色填充
- 从曲线到图/轨迹边缘的可变填充
- 两条曲线(密度和中子孔隙度)之间的填充,当它们相交时会发生变化
对于下面的例子,你可以在我的 GitHub 知识库中找到我的 Jupyter 笔记本和数据集,链接如下。
https://github.com/andymcdgeo/Petrophysics-Python-Series
我的 YouTube 频道上的以下视频演示了如何使用 fill_betweenx()应用一些填充。
使用 matplotlib 应用填充
设置库和加载数据
首先,在开始处理实际数据之前,我们将导入一些公共库。在本文中,我们将使用 pandas 、 matplotlib 和 numpy 。这三个库允许我们加载、处理和可视化我们的数据。此外,通常用于存储和传输数据的数据是。las 文件。为此,我们将使用优秀的 lasio 库来加载这些数据。你可以在我之前的文章中找到更多关于这个的信息。
导入 pandas,matplotlib,lasio 和 numpy 库。
导入和查看 LAS 数据
我们正在使用的数据集来自于 2018 年发布的公开发布的 Equinor Volve 场数据集。本教程中使用的文件来自 15/9- 19A 井,其中包含一组很好的测井数据。
要开始加载我们的 las 文件,我们可以使用 lasio 中的以下方法:
使用 lasio 库在 python 中加载 las 文件。
为了使绘图更容易,我们还将把 las 文件转换成 pandas 数据帧,并创建一个包含深度曲线的新列,该列基于数据帧索引。
然后,我们可以通过调用数据帧的.describe()
方法来找出数据集中包含的内容,如下所示。
返回关于数据帧内容的统计信息。
这将返回一个简单但非常有用的汇总表,详细列出所有曲线的统计数据。
由于我们已经将深度曲线作为一列添加到数据帧中,我们可以很容易地获得数据的最小值和最大值。请注意,这可能不一定是我在上一篇文章中使用 Matplotlib 可视化油井数据覆盖中看到的所有曲线的全部范围。
用简单的填充绘制我们的数据
现在我们已经加载了数据,并且确认了我们已经得到了想要的曲线,我们可以开始绘制数据了。在这篇文章中,我将使用.plot()
方法直接从数据帧中绘图。
在下面的代码中,您会看到我指定了许多参数:
- x 和 y 轴
c
指定线条的颜色lw
指定线条宽度legend
用于打开或关闭图例。适用于多条曲线/直线figsize
以英寸为单位指定图形的尺寸
代码的其余部分允许设置轴极限(ylim
和xlim
)。请注意,当我们在 y 轴上绘制深度时,我们必须翻转数字,以便最深的深度是第一个数字,最浅的深度是第二个数字。
当我们执行代码时,我们得到了我们的绘图:
使用 matplotlib 绘制的伽马射线数据。
我们可以通过添加一个从图的左边缘延伸到曲线值的简单填充来进一步增强这个图。这是通过使用.fill_betweenx()
实现的。
要使用这个函数,我们需要传递 y 值(深度),被着色的曲线(GR)和我们从 GR 曲线着色的值(0)。然后,我们可以通过使用facecolor
参数轻松指定填充的颜色。
使用 fill_betweenx 在曲线和 y 轴之间添加颜色。
当我们运行这段代码时,我们得到了一个稍微好看一点的图形:
y 轴带有颜色填充的伽马射线数据。
我们可以更进一步,通过复制线条并将“阴影”的值与颜色一起更改为“阴影”,以相反的方式绘制阴影:
使用 fill_betweenx 在曲线的左侧和右侧添加额外的阴影。
伽马射线图左侧为绿色阴影,右侧为黄色阴影,便于识别清洁层段和泥质层段。
我们的情节立刻变得更好。我们可以很快看出哪里有更清晰的音程,哪里有更清晰的音程。
用可变填充绘制数据
通过在伽马射线曲线和 y 轴之间应用可变填充,我们可以将我们的图带到下一个级别。您会注意到,与上面的代码相比,下面的代码扩展了很多。
我们首先要确定我们将把阴影分成多少种颜色。这是通过将我们的 x 轴值赋给一个变量并使用span = abs(left_col_value — right_col_value)
计算出它们之间的绝对差值来实现的。这给了我们一系列的价值观。
然后,我们使用cmap= plt.get_cmap('nipy_spectral')
从大量的彩色地图中抓取我们选择的彩色地图。完整的颜色列表可以在这里找到。对于这个例子,我选择了 nipy_spectral。
下一段代码看起来与上面类似,除了 x 限制现在由变量left_col_value
控制。还有right_col_value
。这允许我们只在一个地方而不是在多个地方改变极限值。
最后一部分是 for 循环,它遍历在第 14 行创建的数组中的每个颜色索引值,并从颜色图中获取一种颜色。然后(第 26 行)我们使用 fill_betweenx 方法来应用该颜色。注意,我们现在在参数中使用了where = curve >= index
。这允许我们在曲线值大于或等于索引值时使用合适的颜色。
运行代码后,我们会生成以下图形:
使用 fill_betweenx 使用可变渐变填充的伽马射线图。
乍一看,我们的绘图要好得多,并允许根据显示的颜色轻松识别相似值的区域。
在两条不同比例的曲线之间应用明暗处理
最后一个例子说明了如何将岩性阴影应用于密度和中子孔隙度曲线。这两条曲线通常根据交叉进行着色。当密度移到中子孔隙度的左边时,我们可能有一个多孔储集岩。当交叉以相反的方式发生时,密度在中子孔隙度的右边,我们可能有页岩。
请注意,这是非常简化的,这两条曲线相互交叉有许多不同的原因。
显示密度和中子孔隙度数据的第一步是将它们添加到图中。在这种情况下,我们必须创建一个图形并添加多个轴,而不是使用df.plot()
。为了让我们画出中子孔隙度,我们必须把它作为一个额外的轴加进去。使用ax1.twiny()
我们可以共享两条曲线之间的深度曲线。
matplotlib 图上的密度和中子孔隙度。
我们现在可以添加阴影。这比预期的要复杂一点,因为两条曲线的比例不同。通常,中子孔隙度从 45 到-15 孔隙度单位(p.u)(十进制为 0.45 到-0.15),密度从 1.95 到 2.95 g/cc。
我们必须添加额外的代码,将一条曲线缩放到另一条曲线的单位比例。第 21 行和第 30 行之间的代码部分是从下面的 stackoverflow 帖子中获得的:这里的和这里的。
当我们运行这段代码时,我们得到了下面的图。
以不同比例测量的中子孔隙度和密度数据的可变颜色填充。
这使我们能够很容易地识别潜在储油层的位置。为了确认这些部分是什么,需要进行进一步的分析。在进行解释时,您应该始终查看其他测井曲线,以帮助您理解和解释。
摘要
总之,在处理测井数据时,matplotlib 是一个强大的数据可视化工具。我们可以轻松地在轨迹上显示我们的日志,并在线条之间进行填充,以帮助可视化和解释我们的数据。在这篇文章中,我介绍了如何在曲线和绘图边缘之间应用固定颜色填充和可变渐变填充,以及如何在不同比例的两条曲线之间进行填充。
“作者创作的所有图片”
感谢阅读!
如果你觉得这篇文章很有用,可以看看我的其他文章,看看 Python 和测井数据的各个方面。你也可以在GitHub找到我和其他人在本文中使用的代码。
如果你想联系我,你可以在LinkedIn或者在我的 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
增强您的分析可读性——熊猫教程
改善数据框/图表格式的 3 个技巧
马库斯·斯皮斯克在 Unsplash 上的照片
你的日常生活数据分析
作为一名数据科学家/分析师,您的工作是生成一份包含许多商业决策见解的报告。报告可以通过一些有用的工具制作,如 Microsoft Excel、SAP,也可以用编程语言定制,如 SAS、R 或 Python。结果可以通过内部电子邮件发送给利益相关方,或者通过集中的仪表板发布。
和其他人一样,我是一名数据分析师,在日常生活中使用 python 制作报告或演示文稿。我通常的任务是在 2-3 小时内做一个特别的分析,然后提交给管理团队。
为了得到我想要的结果,我必须启动我的 Jupiter 笔记本内核,并快速编写代码来产生数字。在那之后,我可能会把结果放在微软的 PowerPoint 上,加上一些基本的脚注,然后把它们发给我的主管,让他在一天结束之前作出一个重要的决定。
一个棘手的问题是,由于时间限制,我必须消化信息,编写代码以产生结果,并将其放入 Microsoft PowerPoint 中,以一种漂亮的格式呈现出来。
不幸的是,我使用的编程语言可能不包含使您的报告对管理团队来说更好看的功能,例如,在数字中使用逗号,或者不使用科学符号来显示高数字。
如果你提交的报告没有考虑到这些方面,管理团队可能会对你的报告抱怨很多,有时,他们会看都不看就把它扔进垃圾桶。那会让你很恼火,因为你在这上面花费了时间和精力。
丹尼尔·帕斯夸在 Unsplash 上拍摄的照片
要解决这个问题,您可以将编程语言的结果放到 Microsoft Excel 中,并根据需要手动更改格式。Excel 是一个很好的工具。糟糕的是你必须手动完成。如果我们能在编程过程中实现自动化呢?那就太好了,不是吗?
原样
让我们看看我为这个例子制作的数据框。这是公司需要的收入额。如您所见,这是从pandas
数据帧返回的默认结果。没有任何配置,这就是你得到的。
作者图片
我总是从我的主管或首席执行官那里得到一个评价。
你能让更具可读性和更容易比较吗?”
解决方法可能是将该数字除以一百万,并将单位放在表格的上方。你必须记住的一件事是,它应该在你的演讲中保持一致。如果有 100 张表需要复制呢?很艰难,对吧。
我发现您可以通过编程来修复它。我花了很多时间从网上收集了以下代码片段。非常感谢堆栈溢出!我认为与你们分享它可以让任何发现我的这些问题的人受益。你应该减少花在修饰性评论上的时间,然后把注意力集中在内容的有效性上。
如何改善?你可能会问。
人类可读格式
我收到的最多的评论是,你能把数字四舍五入,并在末尾加上符号,比如 M 代表百万,K 代表千吗?这样会让你的表格更好看,减少读者眼中不必要的信息。很多时候,我们不需要这么精确来决定去哪里。
这是将您的pandas
数据框中的数字转换成您想要的格式的函数。
def human_readable_format(value, pos=None): '''
Convert number in dataframe to human readable format
`pos` argument is to used with the matplotlib ticker formatter.
''' assign_unit = 0
units = ['', 'K', 'M', 'B']
while value >= 1_000:
value /= 1_000
assign_unit += 1 return f"{value:.2f} {units[assign_unit]}"
作者图片
Tada!这是你将得到的结果。读起来容易多了,对吧?
这个函数的缺点是它将你的数字转换成一个字符串,这意味着你将失去从一个数据帧中排序的能力。这个问题可以通过先对您想要的值进行排序,然后再应用它们来解决。
您可以将结果保存为 excel 或 CSV 文件,并放入 PowerPoint 中。我的方法通常是截图并直接放入演示中。
这段代码节省了我大量复制多个表的时间,因为当您从主管那里得到评论时,您必须刷新所有的评论。假设演示中有 100 张表。对于手动逐表制作的人来说简直是噩梦。
同样,格式化后,我们也可以在matplotlib
图中使用它。我认为如果你使用pandas
库进行数据分析,那么matplotlib
将是你绘制图表的首选。
作者图片
您可以使用人类可读的格式(如您的表格)来设置此图表的 y 轴,方法是
import matplotlib.ticker as mticker
import matplotlib.pyplot as pltfig , ax = plt.subplots();
df['value_9'].plot(ax=ax);
ax.yaxis.set_major_formatter(
mticker.FuncFormatter(human_readable_format)
)
看起来更有说服力。
作者图片
突出显示单元格
有时你需要指出表外的重要数字、趋势或信息。您心中有一个逻辑规则,比如突出显示收款金额最大值的月份。该数量可以根据数据中的基础事务而变化。如果您想动态突出显示它,您必须通过编程来实现。
这是第二件我用得最多的东西,让我的桌子看起来更好。它帮助你传达信息,提高你讲故事的能力。强调其余部分中重要的部分。
def highlight_max_value(series): # get True, or False status of each value in series
boolean_mask = series == series.max() # return color is orange when the boolean mask is True
res = [f"color : orange" if max_val else '' for max_val in boolean_mask] return resdf.style.apply(highlight_max_value)
作者图片
有时,您会发现数据中的潜在趋势更容易理解。如果不进行适当的重新排列,你无法从一大堆数据中发现模式。
少即是多
最后一个不是给你的数据框/图添加有趣的东西,而是把它去掉。有时候少即是多。数据框或图中的组件越少,传达的信息就越好。读者或接受者只能吸收他们必须吸收的东西。
作者图片
你可以在这里改很多东西,然后就会是这个样子。
# Prepare data setrevenue = df[['value_9']].copy()
revenue['pct'] = revenue['value_9'] * 100 / revenue['value_9'].sum()
revenue = revenue.sort_values('pct', ascending=False).reset_index(drop=True)
revenue['cumsum_pct'] = revenue['pct'].cumsum()import matplotlib.ticker as mticker
import matplotlib.pyplot as plt
import seaborn as sns# enlarge font size for the graphsns.set_context('talk')# plot the bar chart to show the revenue amountfig , ax = plt.subplots(figsize=(9,6));
revenue['value_9'].plot.bar(ax=ax);
ax.yaxis.set_major_formatter(mticker.FuncFormatter(human_readable_format))
plt.title('Revenue generated');# plot cumulative revenue in percentage
# to show the impact of first 3 customersax2 = plt.twinx(ax)
revenue['cumsum_pct'].plot(ax=ax2, color='orange');
ax2.yaxis.set_major_formatter(mticker.PercentFormatter())
sns.despine();
作者图片
通过整理数据并添加一些信息,可以使用更直观的图表进行决策。例如,我们知道只有前 3 个客户占我们收入的 80 %以上。所以让他们保持良好的关系比什么都重要。
总结
在一个新的时代,数据分析员使用编程语言得出一份报告或演示。它减少了手动任务的大量时间,但如上所述,还有更复杂的事情要处理。这是一种权衡。
我想我今天与你们分享的技巧和诀窍在某种程度上会有所帮助。我花时间寻找代码片段并适应我的工作。如果它被整合在一个地方,我可以随时回头看,这将是方便的。另外,你们也可以看看这篇文章!
本文所有代码都可以在这里找到!
帕泰鲁什·西达
如果你喜欢这篇文章,并希望看到更多这样的东西。
用这些建议充实你的 Jupyter 笔记本
技术工具包
增强工作流程文档的实用技巧
Jupyter Notebook(从这里开始的笔记本)的一个漂亮的特性是能够在代码单元格旁边使用降价单元格。这些降价单元格使我们能够更清晰地表达文档,以便将来的用户更容易理解笔记本的工作流程。在这篇文章中,我分享了一些技巧来丰富 markdown 单元格中的文档,而不必安装任何扩展。
📝 0.使恢复活力的事物
如果你一直在使用笔记本电脑,你可能已经知道降价的基本知识。如果你需要复习,这里有一个一分钟的关于 Markdown 常用语法的介绍:
#### Headers
# Header 1
## Header 2#### Styles
*Italic*, **bold**, _underscore_, ~~strikethrough~~#### Hyperlink
[hyperlink](https://www.markdownguide.org/)#### Table
| Default | Left-aligned | Center-aligned | Right-aligned |
|---------|:-------------|:--------------:|--------------:|
| Default | Left | Center | Right |#### Others
* Bulleted item 1
* Bulleted subitem 1
* Bulleted item 2***
1\. Ordered item 1
1.1\. Ordered subitem 1
2\. Ordered item 2***
- [ ] Unchecked box
- [x] Checked box
其渲染输出:
更新了基础知识之后,是时候看看 Markdown 单元格更复杂的特性来丰富文档以提高可读性了。
🎨 1.颜色代码文本
大段的黑白文字读起来会令人沮丧。丰富黑白文本并提高文本可读性的一种方法是添加颜色来突出和突出关键部分。这里有三种不同的方法来给文本添加颜色:
🎨 1.1.彩色文本
我们可以使用 html <font>
标签来改变文本的颜色。我们可以使用颜色名称或十六进制颜色代码:
Example: <font color=green>green text</font>, <font color=blue>*blue italised text*</font> and <font color=#FF0000>**red bold text**</font>.
如果你想探索更多的颜色名称,这个可能会派上用场。如果颜色名称不能很好地抓住你想要的,你可以探索十六进制颜色来获得更广泛的选择。这里是我最喜欢的探索十六进制颜色的资源。
🎨 1.2.突出显示文本
我们还可以用 html <mark>
标签突出显示文本:
In addition, we can also <mark>highlight text</mark>.
现在更容易将注意力吸引到文本中突出显示的部分。
🎨 1.3.使用警报
最后,我们可以使用 bootstrap alert 设置背景和字体颜色的格式,使文本文档更容易阅读:
<div class="alert alert-info">Example text highlighted in blue background.</div>
<div class="alert alert-success">Example text highlighted in green background.</div>
<div class="alert alert-warning">Example text highlighted in yellow background.</div>
<div class="alert alert-danger">Example text highlighted in red background.</div>
这些格式都很好看!添加颜色可以突出重点,让文档不那么枯燥,从而立即提高笔记本文档的可读性。这样就变得更容易略读,快速获得要点。
📍 2.适当设置文本格式
丰富文档的另一种方法是使用合适的更丰富的文本格式。让我们看看三种不同的文本格式:
📍2.1 用 LaTeX 插入数学方程
在笔记本文档中经常需要引用数学方程。有了$
,我们可以使用 LaTeX 显示格式良好的数学公式:
$$logloss(\theta) = - {1 \over m} \sum_{i=1}^m (y_i \ln(\hat p(y_i=1)) + (1-y_i) \ln(1-\hat p(y_i=1)))$$
当等式用双$
包裹时,它将居中对齐。如果我们使用单个$
,它将是左对齐的。或者,我们也可以使用以下语法来编写公式:
\begin{equation} logloss(\theta) = - {1 \over m} \sum_{i=1}^m (y_i \ln(\hat p(y_i=1)) + (1-y_i) \ln(1-\hat p(y_i=1))) \end{equation}
如果你对 LaTeX 不熟悉,可以看看这个指南或者这个来入门。
📍 2.2.使用代码块
有时,在 markdown 单元格中显示代码引用而不是在代码单元格中运行它们是很有用的。我们可以使用单引号```来内联显示代码块:
If you haven't installed textblob, you can install it with: `pip install texblob`.
对于更大的代码块,我们可以使用三重反斜线`````:
If you haven't installed libraries, you can install them with the following command:
conda install pandas, numpy, sklearn -y
pip install textblob
如果我们在第一个三元组```后指定语言名称,代码块将在适用的地方进行彩色格式化:
```python
{"minimum": 10, "maximum": 50, "name": "optimiser"}

## 📍 2.3.使用引号缩进
缩进是设置文本格式以提高可读性的另一种方式。我们可以用`>`添加缩进:
Sample non-indented sentence here.
Note: Indented text.

# 📹 3.添加媒体
> 一幅画胜过千言万语。
文档不一定总是用文字。图像和其他媒体可以帮助我们传达用文字难以表达的思想。添加相关媒体是为未来用户提供必要信息来丰富文档的另一个好方法。
## 📷 3.1.嵌入包含 gif 的图像
我们可以使用 html `<img>`标签向 markdown 单元格添加图像:

在这里,example.jpeg
与笔记本保存在同一个文件夹中。我们可以使用width
或height
参数来改变图像的大小。例如,<img src=”example.jpeg” width=500>
会将显示的图像调整到所需的宽度,同时保持宽高比。
如果在静态图像中添加图表或其他数据可视化不能很好地捕捉我们想要传达的信息,我们可以嵌入 GIF,一个使用完全相同语法结构的动画图像:
<img src="example.gif"/>
文件的路径也可以是一个 web 链接:
<img src="[https://media.giphy.com/media/XtdLpKOzoxJCzlFY4n/giphy.gif](https://media.giphy.com/media/l46C93LNM33JJ1SMw/giphy.gif)">
📹 3.2.嵌入视频
如果 gif 不够,下一步就是用视频。我们可以使用 html <video>
标签来显示一段视频:
<video controls src="example.mp4" width=600/>
例如,屏幕记录如何完成一项任务,将其保存为视频文件,并将视频嵌入笔记本中,这对未来的用户可能很有用。
⚪ ️3.3.添加形状和表情符号
一段很长的纯文本可能会很无聊,读起来没有吸引力。优雅地添加形状和表情符号可以使文本阅读起来更有趣、更有吸引力:
➤ Bullet point one</br>
➤ Bullet point two</br>
➤ Bullet point three
✅ Sample text A</br>
✅ Sample text B</br>
✅ Sample text C
查看这个来探索更多的形状(和表情符号)。这个表情符号备忘单在按名字搜索表情符号时很有用。
瞧啊。这些是我丰富 Jupyter 笔记本文档的技巧。✨:我们可能不会一次使用所有这些功能。但是当合适的机会出现时,知道如何使用它们会让你有所准备。
克里斯·劳顿在 Unsplash 上拍摄的照片
您想要访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。
谢谢你看我的帖子。如果你想了解更多关于减价的信息,请查看本指南。如果你感兴趣,这里有我的一些帖子的链接:
◼️ 用这些技巧整理你的 Jupyter 笔记本
◼️ 有用的 IPython 魔法命令
◼️python 虚拟环境数据科学简介
◼️git 数据科学简介
◼️python 中的简单数据可视化,你会发现有用的
◼️ 6 个简单的技巧,让你在 Seaborn (Python)中的情节更漂亮、更定制
◼️️ 5
再见🏃💨